[pari-sage] 01/12: Imported Upstream version 2.7.2

Tobias Hansen thansen at moszumanska.debian.org
Sat May 23 14:30:44 UTC 2015


This is an automated email from the git hooks/post-receive script.

thansen pushed a commit to branch master
in repository pari-sage.

commit 732d3f005a74f64b150171b7d8afda124e0161a4
Author: Tobias Hansen <tobias.han at gmx.de>
Date:   Sun Mar 15 02:29:39 2015 +0000

    Imported Upstream version 2.7.2
---
 AUTHORS                                         |   133 +
 CHANGES                                         |    58 +
 CHANGES-2.2                                     |  3994 ++++++
 CHANGES-2.4                                     |  1537 ++
 CHANGES-2.6                                     |   929 ++
 COMPAT                                          |   652 +
 COPYING                                         |   339 +
 Configure                                       |   213 +
 INSTALL                                         |    20 +
 MACHINES                                        |   431 +
 NEW                                             |   733 +
 README                                          |    87 +
 README-git                                      |    96 +
 config/DOC_Make.SH                              |   162 +
 config/GEN_Make.SH                              |   101 +
 config/Imakefile                                |     2 +
 config/Makefile.SH                              |   799 ++
 config/TOP_Make.SH                              |   125 +
 config/ansi.c                                   |     1 +
 config/arch-osname                              |    79 +
 config/checkspaces                              |    25 +
 config/cygwin-gprc                              |     3 +
 config/cygwin-pari.nsi                          |   199 +
 config/cygwin-postinst                          |     6 +
 config/display                                  |    11 +
 config/endian.c                                 |    22 +
 config/extract_files                            |    22 +
 config/genfunclist                              |    11 +
 config/genkernel                                |    21 +
 config/get_MANIFEST                             |    12 +
 config/get_PATH                                 |    46 +
 config/get_Qt                                   |    44 +
 config/get_X11                                  |    58 +
 config/get_archos                               |    34 +
 config/get_cc                                   |   155 +
 config/get_config_options                       |   214 +
 config/get_dlcflags                             |    18 +
 config/get_dlld                                 |   125 +
 config/get_double_format                        |    51 +
 config/get_fltk                                 |    24 +
 config/get_gmp                                  |    79 +
 config/get_graphic_lib                          |    67 +
 config/get_head                                 |     5 +
 config/get_include_path                         |    17 +
 config/get_install                              |    57 +
 config/get_kernel                               |   122 +
 config/get_ld                                   |    78 +
 config/get_libc                                 |    56 +
 config/get_libpth                               |    54 +
 config/get_modld                                |    26 +
 config/get_mt                                   |    31 +
 config/get_nl                                   |    13 +
 config/get_objdir                               |    35 +
 config/get_perl                                 |    15 +
 config/get_pretty                               |    17 +
 config/get_readline                             |   102 +
 config/get_static                               |    28 +
 config/get_tests                                |    36 +
 config/gmp_version.c                            |    11 +
 config/gnu.c                                    |     5 +
 config/gprc.mingw                               |     6 +
 config/has_TIOCGWINSZ.c                         |    12 +
 config/has_X11.c                                |     4 +
 config/has_alarm.c                              |     3 +
 config/has_clock_gettime.c                      |     9 +
 config/has_dlopen.c                             |     3 +
 config/has_exp2.c                               |     3 +
 config/has_ftime.c                              |     5 +
 config/has_getenv.c                             |     2 +
 config/has_getrlimit.c                          |    10 +
 config/has_getrusage.c                          |     7 +
 config/has_isatty.c                             |     2 +
 config/has_log2.c                               |     3 +
 config/has_opendir.c                            |     3 +
 config/has_setsid.c                             |     4 +
 config/has_sigaction.c                          |    12 +
 config/has_stat.c                               |     9 +
 config/has_strftime.c                           |     2 +
 config/has_times.c                              |    13 +
 config/has_vsnprintf.c                          |     9 +
 config/has_wait.c                               |     4 +
 config/has_waitpid.c                            |     4 +
 config/install                                  |    27 +
 config/kernel-name                              |    33 +
 config/kernel.c                                 |    24 +
 config/ldflags                                  |    11 +
 config/locate                                   |    13 +
 config/locatedir                                |    10 +
 config/locatelib                                |    35 +
 config/log_cmd                                  |     2 +
 config/look                                     |    17 +
 config/make_tags                                |    90 +
 config/mingw-pari.nsi                           |   124 +
 config/mpi.c                                    |    13 +
 config/myread                                   |    26 +
 config/objdir                                   |     7 +
 config/pari.nsi.SH                              |     7 +
 config/paricfg.h.SH                             |   177 +
 config/pthread.c                                |    22 +
 config/rl_version.c                             |     3 +
 config/settar                                   |    28 +
 config/setversion                               |    14 +
 config/version                                  |    40 +
 doc/INSTALL.tex                                 |     4 +
 doc/appa.tex                                    |   619 +
 doc/appb.tex                                    |    36 +
 doc/appd.tex                                    |    87 +
 doc/develop.tex                                 |   851 ++
 doc/gp.1                                        |   336 +
 doc/gphelp.1                                    |   249 +
 doc/gphelp.in                                   |  1219 ++
 doc/index.tex                                   |   118 +
 doc/libpari.tex                                 |    41 +
 doc/parallel.tex                                |   393 +
 doc/paricfg.tex.in                              |    13 +
 doc/parimacro.tex                               |   471 +
 doc/pdfmacs.tex                                 |   138 +
 doc/refcard.tex                                 |  1059 ++
 doc/tex2mail.1                                  |    84 +
 doc/translations                                |   157 +
 doc/tutorial.tex                                |  3263 +++++
 doc/users.tex                                   |    42 +
 doc/usersFUNCS.tex                              |  1353 ++
 doc/usersch1.tex                                |   447 +
 doc/usersch2.tex                                |  3128 +++++
 doc/usersch3.tex                                | 16201 ++++++++++++++++++++++
 doc/usersch4.tex                                |  2236 +++
 doc/usersch5.tex                                | 11057 +++++++++++++++
 doc/usersch6.tex                                |  2100 +++
 doc/usersch7.tex                                |   626 +
 examples/EXPLAIN                                |   101 +
 examples/Inputrc                                |    14 +
 examples/bench.gp                               |    10 +
 examples/cl.gp                                  |   118 +
 examples/classno.gp                             |    34 +
 examples/contfrac.gp                            |    13 +
 examples/extgcd.c                               |    40 +
 examples/lucas.gp                               |     5 +
 examples/minigp.c                               |    80 +
 examples/openmp.c                               |    50 +
 examples/pari-mt.c                              |    51 +
 examples/rho.gp                                 |    53 +
 examples/squfof.gp                              |    51 +
 examples/taylor.gp                              |    58 +
 examples/thread.c                               |    57 +
 misc/README                                     |     9 +
 misc/color.dft                                  |    32 +
 misc/gpalias                                    |    31 +
 misc/gpflog                                     |     7 +
 misc/gprc.dft                                   |    92 +
 misc/gprc.dos                                   |    33 +
 misc/tex2mail.in                                |  2671 ++++
 misc/xgp                                        |    12 +
 src/basemath/F2x.c                              |  1672 +++
 src/basemath/F2xqE.c                            |   814 ++
 src/basemath/FF.c                               |  1776 +++
 src/basemath/Flx.c                              |  3777 +++++
 src/basemath/FlxqE.c                            |  1430 ++
 src/basemath/Flxq_log.c                         |   695 +
 src/basemath/FpE.c                              |  1904 +++
 src/basemath/FpV.c                              |  1299 ++
 src/basemath/FpX.c                              |  1911 +++
 src/basemath/FpXX.c                             |  1080 ++
 src/basemath/FpX_factor.c                       |  2904 ++++
 src/basemath/Hensel.c                           |   800 ++
 src/basemath/QX_factor.c                        |  1413 ++
 src/basemath/Qfb.c                              |  1474 ++
 src/basemath/RgV.c                              |   847 ++
 src/basemath/RgX.c                              |  2213 +++
 src/basemath/ZV.c                               |  1116 ++
 src/basemath/ZX.c                               |   773 ++
 src/basemath/alglin1.c                          |  4194 ++++++
 src/basemath/alglin2.c                          |  1556 +++
 src/basemath/alglin3.c                          |   892 ++
 src/basemath/arith1.c                           |  4404 ++++++
 src/basemath/arith2.c                           |  1440 ++
 src/basemath/base1.c                            |  2828 ++++
 src/basemath/base2.c                            |  3799 +++++
 src/basemath/base3.c                            |  2494 ++++
 src/basemath/base4.c                            |  3110 +++++
 src/basemath/base5.c                            |   992 ++
 src/basemath/bb_group.c                         |   909 ++
 src/basemath/bibli1.c                           |  1808 +++
 src/basemath/bibli2.c                           |  1901 +++
 src/basemath/bit.c                              |   496 +
 src/basemath/buch1.c                            |  1180 ++
 src/basemath/buch2.c                            |  4227 ++++++
 src/basemath/buch3.c                            |  2406 ++++
 src/basemath/buch4.c                            |   859 ++
 src/basemath/concat.c                           |   576 +
 src/basemath/ellanal.c                          |  1343 ++
 src/basemath/elliptic.c                         |  5594 ++++++++
 src/basemath/galconj.c                          |  2637 ++++
 src/basemath/gen1.c                             |  3398 +++++
 src/basemath/gen2.c                             |  2721 ++++
 src/basemath/gen3.c                             |  3939 ++++++
 src/basemath/hnf_snf.c                          |  2698 ++++
 src/basemath/ifactor1.c                         |  3831 +++++
 src/basemath/lll.c                              |   826 ++
 src/basemath/nffactor.c                         |  2220 +++
 src/basemath/perm.c                             |  1053 ++
 src/basemath/polarit1.c                         |   635 +
 src/basemath/polarit2.c                         |  3137 +++++
 src/basemath/polarit3.c                         |  2927 ++++
 src/basemath/prime.c                            |  1349 ++
 src/basemath/qfisom.c                           |  1796 +++
 src/basemath/random.c                           |   227 +
 src/basemath/rootpol.c                          |  2165 +++
 src/basemath/subcyclo.c                         |   993 ++
 src/basemath/subgroup.c                         |   648 +
 src/basemath/trans1.c                           |  3170 +++++
 src/basemath/trans2.c                           |  1742 +++
 src/basemath/trans3.c                           |  3312 +++++
 src/desc/PARI/822.pm                            |   141 +
 src/desc/doc_make                               |   125 +
 src/desc/gen_proto                              |    67 +
 src/desc/merge_822                              |    11 +
 src/desc/whatnow                                |    81 +
 src/funclist                                    |   825 ++
 src/functions/conversions/Col                   |    35 +
 src/functions/conversions/Colrev                |    12 +
 src/functions/conversions/List                  |    16 +
 src/functions/conversions/Mat                   |    42 +
 src/functions/conversions/Mod                   |    54 +
 src/functions/conversions/Pol                   |    46 +
 src/functions/conversions/Polrev                |    26 +
 src/functions/conversions/Qfb                   |    13 +
 src/functions/conversions/Ser                   |    43 +
 src/functions/conversions/Set                   |    23 +
 src/functions/conversions/Str                   |    28 +
 src/functions/conversions/Strchr                |    17 +
 src/functions/conversions/Strexpand             |    18 +
 src/functions/conversions/Strprintf             |    10 +
 src/functions/conversions/Strtex                |    13 +
 src/functions/conversions/Vec                   |    36 +
 src/functions/conversions/Vecrev                |    14 +
 src/functions/conversions/Vecsmall              |    17 +
 src/functions/conversions/binary                |    11 +
 src/functions/conversions/bitand                |    30 +
 src/functions/conversions/bitneg                |    15 +
 src/functions/conversions/bitnegimply           |    19 +
 src/functions/conversions/bitor                 |    18 +
 src/functions/conversions/bittest               |    25 +
 src/functions/conversions/bitxor                |    18 +
 src/functions/conversions/ceil                  |    16 +
 src/functions/conversions/centerlift            |    28 +
 src/functions/conversions/characteristic        |    15 +
 src/functions/conversions/component             |    40 +
 src/functions/conversions/conj                  |    11 +
 src/functions/conversions/conjvec               |    21 +
 src/functions/conversions/denominator           |    26 +
 src/functions/conversions/digits                |     8 +
 src/functions/conversions/floor                 |    16 +
 src/functions/conversions/frac                  |     8 +
 src/functions/conversions/hammingweight         |    20 +
 src/functions/conversions/imag                  |     7 +
 src/functions/conversions/length                |    39 +
 src/functions/conversions/lift                  |    52 +
 src/functions/conversions/liftall               |    23 +
 src/functions/conversions/liftint               |    22 +
 src/functions/conversions/liftpol               |    20 +
 src/functions/conversions/norm                  |    11 +
 src/functions/conversions/numerator             |    25 +
 src/functions/conversions/numtoperm             |    10 +
 src/functions/conversions/padicprec             |     9 +
 src/functions/conversions/permtonum             |     8 +
 src/functions/conversions/precision             |    72 +
 src/functions/conversions/random                |    76 +
 src/functions/conversions/real                  |     7 +
 src/functions/conversions/round                 |    35 +
 src/functions/conversions/simplify              |    32 +
 src/functions/conversions/sizebyte              |    10 +
 src/functions/conversions/sizedigit             |    10 +
 src/functions/conversions/truncate              |    36 +
 src/functions/conversions/valuation             |    21 +
 src/functions/conversions/variable              |    39 +
 src/functions/default/TeXstyle                  |    17 +
 src/functions/default/breakloop                 |    12 +
 src/functions/default/colors                    |    52 +
 src/functions/default/compatible                |    45 +
 src/functions/default/datadir                   |    13 +
 src/functions/default/debug                     |    10 +
 src/functions/default/debugfiles                |    11 +
 src/functions/default/debugmem                  |    16 +
 src/functions/default/echo                      |    13 +
 src/functions/default/factor_add_primes         |    17 +
 src/functions/default/factor_proven             |    16 +
 src/functions/default/format                    |    32 +
 src/functions/default/graphcolormap             |    23 +
 src/functions/default/graphcolors               |    16 +
 src/functions/default/help                      |    12 +
 src/functions/default/histfile                  |    15 +
 src/functions/default/histsize                  |    13 +
 src/functions/default/lines                     |    14 +
 src/functions/default/linewrap                  |    10 +
 src/functions/default/log                       |    23 +
 src/functions/default/logfile                   |    10 +
 src/functions/default/nbthreads                 |    16 +
 src/functions/default/new_galois_format         |    13 +
 src/functions/default/output                    |    33 +
 src/functions/default/parisize                  |    17 +
 src/functions/default/path                      |    16 +
 src/functions/default/prettyprinter             |    12 +
 src/functions/default/primelimit                |    36 +
 src/functions/default/prompt                    |    31 +
 src/functions/default/prompt_cont               |    12 +
 src/functions/default/psfile                    |    11 +
 src/functions/default/readline                  |    13 +
 src/functions/default/realprecision             |    29 +
 src/functions/default/recover                   |    11 +
 src/functions/default/secure                    |    13 +
 src/functions/default/seriesprecision           |    11 +
 src/functions/default/simplify                  |    19 +
 src/functions/default/sopath                    |    18 +
 src/functions/default/strictargs                |    28 +
 src/functions/default/strictmatch               |    14 +
 src/functions/default/threadsize                |    14 +
 src/functions/default/timer                     |    26 +
 src/functions/elliptic_curves/ellL1             |    37 +
 src/functions/elliptic_curves/elladd            |     8 +
 src/functions/elliptic_curves/ellak             |    37 +
 src/functions/elliptic_curves/ellan             |    12 +
 src/functions/elliptic_curves/ellanalyticrank   |    28 +
 src/functions/elliptic_curves/ellap             |    53 +
 src/functions/elliptic_curves/ellbil            |    13 +
 src/functions/elliptic_curves/ellcard           |    19 +
 src/functions/elliptic_curves/ellchangecurve    |    15 +
 src/functions/elliptic_curves/ellchangepoint    |    23 +
 src/functions/elliptic_curves/ellchangepointinv |    21 +
 src/functions/elliptic_curves/ellconvertname    |    18 +
 src/functions/elliptic_curves/elldivpol         |    17 +
 src/functions/elliptic_curves/elleisnum         |    33 +
 src/functions/elliptic_curves/elleta            |    15 +
 src/functions/elliptic_curves/ellfromj          |     8 +
 src/functions/elliptic_curves/ellgenerators     |    16 +
 src/functions/elliptic_curves/ellglobalred      |    29 +
 src/functions/elliptic_curves/ellgroup          |    67 +
 src/functions/elliptic_curves/ellheegner        |    24 +
 src/functions/elliptic_curves/ellheight         |    21 +
 src/functions/elliptic_curves/ellheightmatrix   |    15 +
 src/functions/elliptic_curves/ellidentify       |    14 +
 src/functions/elliptic_curves/ellinit           |   106 +
 src/functions/elliptic_curves/ellisoncurve      |    12 +
 src/functions/elliptic_curves/ellj              |     9 +
 src/functions/elliptic_curves/elllocalred       |    23 +
 src/functions/elliptic_curves/elllog            |    30 +
 src/functions/elliptic_curves/elllseries        |    15 +
 src/functions/elliptic_curves/ellminimalmodel   |    16 +
 src/functions/elliptic_curves/ellmodulareqn     |    63 +
 src/functions/elliptic_curves/ellmul            |    28 +
 src/functions/elliptic_curves/ellneg            |     7 +
 src/functions/elliptic_curves/ellorder          |    45 +
 src/functions/elliptic_curves/ellordinate       |    10 +
 src/functions/elliptic_curves/ellperiods        |    24 +
 src/functions/elliptic_curves/ellpointtoz       |    58 +
 src/functions/elliptic_curves/ellpow            |     6 +
 src/functions/elliptic_curves/ellrootno         |    14 +
 src/functions/elliptic_curves/ellsearch         |    47 +
 src/functions/elliptic_curves/ellsigma          |    24 +
 src/functions/elliptic_curves/ellsub            |     8 +
 src/functions/elliptic_curves/elltaniyama       |    22 +
 src/functions/elliptic_curves/elltatepairing    |     8 +
 src/functions/elliptic_curves/elltors           |    32 +
 src/functions/elliptic_curves/ellweilpairing    |     8 +
 src/functions/elliptic_curves/ellwp             |    34 +
 src/functions/elliptic_curves/ellzeta           |    33 +
 src/functions/elliptic_curves/ellztopoint       |    17 +
 src/functions/elliptic_curves/genus2red         |   116 +
 src/functions/gp2c/DEBUGLEVEL                   |     6 +
 src/functions/gp2c/clone                        |    11 +
 src/functions/gp2c/copy                         |    11 +
 src/functions/gp2c/unclone                      |     5 +
 src/functions/gp2c_internal/_avma               |     4 +
 src/functions/gp2c_internal/_badtype            |    17 +
 src/functions/gp2c_internal/_cast               |   141 +
 src/functions/gp2c_internal/_cgetg              |     5 +
 src/functions/gp2c_internal/_const              |    23 +
 src/functions/gp2c_internal/_formatcode         |     8 +
 src/functions/gp2c_internal/_gerepileall        |     5 +
 src/functions/gp2c_internal/_gerepileupto       |     8 +
 src/functions/gp2c_internal/_maxprime           |     4 +
 src/functions/gp2c_internal/_stack_lim          |     9 +
 src/functions/gp2c_internal/_strtoclosure       |     5 +
 src/functions/gp2c_internal/_tovec              |    20 +
 src/functions/gp2c_internal/_typedef            |   101 +
 src/functions/gp2c_internal/_wrap               |    20 +
 src/functions/graphic/plot                      |    12 +
 src/functions/graphic/plotbox                   |    11 +
 src/functions/graphic/plotclip                  |    11 +
 src/functions/graphic/plotcolor                 |    16 +
 src/functions/graphic/plotcopy                  |    16 +
 src/functions/graphic/plotcursor                |     8 +
 src/functions/graphic/plotdraw                  |    17 +
 src/functions/graphic/ploth                     |   114 +
 src/functions/graphic/plothraw                  |    15 +
 src/functions/graphic/plothsizes                |    16 +
 src/functions/graphic/plotinit                  |    28 +
 src/functions/graphic/plotkill                  |    10 +
 src/functions/graphic/plotlines                 |    25 +
 src/functions/graphic/plotlinetype              |    12 +
 src/functions/graphic/plotmove                  |     7 +
 src/functions/graphic/plotpoints                |    19 +
 src/functions/graphic/plotpointsize             |     9 +
 src/functions/graphic/plotpointtype             |    12 +
 src/functions/graphic/plotrbox                  |    11 +
 src/functions/graphic/plotrecth                 |    10 +
 src/functions/graphic/plotrecthraw              |    19 +
 src/functions/graphic/plotrline                 |    11 +
 src/functions/graphic/plotrmove                 |    10 +
 src/functions/graphic/plotrpoint                |    10 +
 src/functions/graphic/plotscale                 |    14 +
 src/functions/graphic/plotstring                |    18 +
 src/functions/graphic/psdraw                    |    12 +
 src/functions/graphic/psploth                   |     9 +
 src/functions/graphic/psplothraw                |     9 +
 src/functions/linear_algebra/algdep             |    49 +
 src/functions/linear_algebra/charpoly           |    68 +
 src/functions/linear_algebra/concat             |   101 +
 src/functions/linear_algebra/forqfvec           |    22 +
 src/functions/linear_algebra/lindep             |    62 +
 src/functions/linear_algebra/listcreate         |    12 +
 src/functions/linear_algebra/listinsert         |    13 +
 src/functions/linear_algebra/listkill           |     8 +
 src/functions/linear_algebra/listpop            |    13 +
 src/functions/linear_algebra/listput            |    18 +
 src/functions/linear_algebra/listsort           |    21 +
 src/functions/linear_algebra/matadjoint         |    32 +
 src/functions/linear_algebra/matcompanion       |     7 +
 src/functions/linear_algebra/matconcat          |    89 +
 src/functions/linear_algebra/matdet             |    33 +
 src/functions/linear_algebra/matdetint          |    26 +
 src/functions/linear_algebra/matdiagonal        |    32 +
 src/functions/linear_algebra/mateigen           |    41 +
 src/functions/linear_algebra/matfrobenius       |    14 +
 src/functions/linear_algebra/mathess            |     7 +
 src/functions/linear_algebra/mathilbert         |     8 +
 src/functions/linear_algebra/mathnf             |   127 +
 src/functions/linear_algebra/mathnfmod          |    16 +
 src/functions/linear_algebra/mathnfmodid        |    34 +
 src/functions/linear_algebra/mathouseholder     |     8 +
 src/functions/linear_algebra/matid              |     8 +
 src/functions/linear_algebra/matimage           |    17 +
 src/functions/linear_algebra/matimagecompl      |    13 +
 src/functions/linear_algebra/matindexrank       |    11 +
 src/functions/linear_algebra/matintersect       |    13 +
 src/functions/linear_algebra/matinverseimage    |    30 +
 src/functions/linear_algebra/matisdiagonal      |     7 +
 src/functions/linear_algebra/matker             |    20 +
 src/functions/linear_algebra/matkerint          |    22 +
 src/functions/linear_algebra/matmuldiagonal     |    10 +
 src/functions/linear_algebra/matmultodiagonal   |    10 +
 src/functions/linear_algebra/matpascal          |    12 +
 src/functions/linear_algebra/matqr              |    22 +
 src/functions/linear_algebra/matrank            |     6 +
 src/functions/linear_algebra/matrix             |    14 +
 src/functions/linear_algebra/matrixqz           |    53 +
 src/functions/linear_algebra/matsize            |     9 +
 src/functions/linear_algebra/matsnf             |    31 +
 src/functions/linear_algebra/matsolve           |    12 +
 src/functions/linear_algebra/matsolvemod        |    30 +
 src/functions/linear_algebra/matsupplement      |    26 +
 src/functions/linear_algebra/mattranspose       |     7 +
 src/functions/linear_algebra/minpoly            |     8 +
 src/functions/linear_algebra/norml2             |    24 +
 src/functions/linear_algebra/normlp             |    36 +
 src/functions/linear_algebra/qfauto             |    27 +
 src/functions/linear_algebra/qfautoexport       |    19 +
 src/functions/linear_algebra/qfbil              |    31 +
 src/functions/linear_algebra/qfgaussred         |    25 +
 src/functions/linear_algebra/qfisom             |    24 +
 src/functions/linear_algebra/qfisominit         |    28 +
 src/functions/linear_algebra/qfjacobi           |    34 +
 src/functions/linear_algebra/qflll              |    63 +
 src/functions/linear_algebra/qflllgram          |    36 +
 src/functions/linear_algebra/qfminim            |    97 +
 src/functions/linear_algebra/qfnorm             |    38 +
 src/functions/linear_algebra/qfperfection       |    15 +
 src/functions/linear_algebra/qfrep              |    28 +
 src/functions/linear_algebra/qfsign             |    10 +
 src/functions/linear_algebra/seralgdep          |    21 +
 src/functions/linear_algebra/setbinop           |    18 +
 src/functions/linear_algebra/setintersect       |     9 +
 src/functions/linear_algebra/setisset           |    19 +
 src/functions/linear_algebra/setminus           |    10 +
 src/functions/linear_algebra/setsearch          |    45 +
 src/functions/linear_algebra/setunion           |     9 +
 src/functions/linear_algebra/trace              |    11 +
 src/functions/linear_algebra/vecextract         |    71 +
 src/functions/linear_algebra/vecsearch          |    45 +
 src/functions/linear_algebra/vecsort            |    90 +
 src/functions/linear_algebra/vecsum             |     6 +
 src/functions/linear_algebra/vector             |    25 +
 src/functions/linear_algebra/vectorsmall        |    12 +
 src/functions/linear_algebra/vectorv            |     8 +
 src/functions/member_functions/a1               |     8 +
 src/functions/member_functions/a2               |     8 +
 src/functions/member_functions/a3               |     8 +
 src/functions/member_functions/a4               |     8 +
 src/functions/member_functions/a6               |     8 +
 src/functions/member_functions/area             |     5 +
 src/functions/member_functions/b2               |     8 +
 src/functions/member_functions/b4               |     8 +
 src/functions/member_functions/b6               |     8 +
 src/functions/member_functions/b8               |     8 +
 src/functions/member_functions/bid              |     8 +
 src/functions/member_functions/bnf              |     9 +
 src/functions/member_functions/c4               |     8 +
 src/functions/member_functions/c6               |     8 +
 src/functions/member_functions/clgp             |    10 +
 src/functions/member_functions/codiff           |     5 +
 src/functions/member_functions/cyc              |    10 +
 src/functions/member_functions/diff             |     8 +
 src/functions/member_functions/disc             |    10 +
 src/functions/member_functions/e                |     7 +
 src/functions/member_functions/eta              |     5 +
 src/functions/member_functions/f                |     7 +
 src/functions/member_functions/fu               |     9 +
 src/functions/member_functions/futu             |     5 +
 src/functions/member_functions/gen              |    12 +
 src/functions/member_functions/group            |     8 +
 src/functions/member_functions/index            |     8 +
 src/functions/member_functions/j                |     8 +
 src/functions/member_functions/mod              |     5 +
 src/functions/member_functions/nf               |     8 +
 src/functions/member_functions/no               |    10 +
 src/functions/member_functions/omega            |     5 +
 src/functions/member_functions/orders           |     7 +
 src/functions/member_functions/p                |     9 +
 src/functions/member_functions/pol              |     9 +
 src/functions/member_functions/polabs           |     5 +
 src/functions/member_functions/r1               |     8 +
 src/functions/member_functions/r2               |     8 +
 src/functions/member_functions/reg              |     9 +
 src/functions/member_functions/roots            |     9 +
 src/functions/member_functions/sign             |     8 +
 src/functions/member_functions/t2               |     7 +
 src/functions/member_functions/tate             |     5 +
 src/functions/member_functions/tu               |     7 +
 src/functions/member_functions/tufu             |     5 +
 src/functions/member_functions/zk               |     8 +
 src/functions/member_functions/zkst             |     7 +
 src/functions/number_fields/bnfcertify          |    32 +
 src/functions/number_fields/bnfcompress         |    32 +
 src/functions/number_fields/bnfdecodemodule     |    18 +
 src/functions/number_fields/bnfinit             |   101 +
 src/functions/number_fields/bnfisintnorm        |    20 +
 src/functions/number_fields/bnfisnorm           |    27 +
 src/functions/number_fields/bnfisprincipal      |    64 +
 src/functions/number_fields/bnfissunit          |    11 +
 src/functions/number_fields/bnfisunit           |    32 +
 src/functions/number_fields/bnfnarrow           |    15 +
 src/functions/number_fields/bnfsignunit         |    30 +
 src/functions/number_fields/bnfsunit            |    29 +
 src/functions/number_fields/bnrL1               |    56 +
 src/functions/number_fields/bnrclassno          |    28 +
 src/functions/number_fields/bnrclassnolist      |    30 +
 src/functions/number_fields/bnrconductor        |    26 +
 src/functions/number_fields/bnrconductorofchar  |    10 +
 src/functions/number_fields/bnrdisc             |    29 +
 src/functions/number_fields/bnrdisclist         |    51 +
 src/functions/number_fields/bnrinit             |    43 +
 src/functions/number_fields/bnrisconductor      |    12 +
 src/functions/number_fields/bnrisprincipal      |    27 +
 src/functions/number_fields/bnrrootnumber       |    33 +
 src/functions/number_fields/bnrstark            |    51 +
 src/functions/number_fields/dirzetak            |     9 +
 src/functions/number_fields/factornf            |    31 +
 src/functions/number_fields/galoisexport        |    35 +
 src/functions/number_fields/galoisfixedfield    |    38 +
 src/functions/number_fields/galoisgetpol        |    37 +
 src/functions/number_fields/galoisidentify      |    23 +
 src/functions/number_fields/galoisinit          |   101 +
 src/functions/number_fields/galoisisabelian     |    12 +
 src/functions/number_fields/galoisisnormal      |    12 +
 src/functions/number_fields/galoispermtopol     |    22 +
 src/functions/number_fields/galoissubcyclo      |    55 +
 src/functions/number_fields/galoissubfields     |     9 +
 src/functions/number_fields/galoissubgroups     |    18 +
 src/functions/number_fields/idealadd            |    44 +
 src/functions/number_fields/idealaddtoone       |    17 +
 src/functions/number_fields/idealappr           |    26 +
 src/functions/number_fields/idealchinese        |    14 +
 src/functions/number_fields/idealcoprime        |     9 +
 src/functions/number_fields/idealdiv            |    20 +
 src/functions/number_fields/idealfactor         |    10 +
 src/functions/number_fields/idealfactorback     |    53 +
 src/functions/number_fields/idealfrobenius      |    23 +
 src/functions/number_fields/idealhnf            |    73 +
 src/functions/number_fields/idealintersect      |    27 +
 src/functions/number_fields/idealinv            |    11 +
 src/functions/number_fields/ideallist           |    56 +
 src/functions/number_fields/ideallistarch       |    40 +
 src/functions/number_fields/ideallog            |    20 +
 src/functions/number_fields/idealmin            |     9 +
 src/functions/number_fields/idealmul            |    33 +
 src/functions/number_fields/idealnorm           |     6 +
 src/functions/number_fields/idealnumden         |    13 +
 src/functions/number_fields/idealpow            |    21 +
 src/functions/number_fields/idealprimedec       |    44 +
 src/functions/number_fields/idealprincipalunits |    20 +
 src/functions/number_fields/idealramgroups      |    45 +
 src/functions/number_fields/idealred            |    59 +
 src/functions/number_fields/idealstar           |    43 +
 src/functions/number_fields/idealtwoelt         |    26 +
 src/functions/number_fields/idealval            |     8 +
 src/functions/number_fields/matalgtobasis       |     9 +
 src/functions/number_fields/matbasistoalg       |     9 +
 src/functions/number_fields/modreverse          |    41 +
 src/functions/number_fields/newtonpoly          |    13 +
 src/functions/number_fields/nfalgtobasis        |    20 +
 src/functions/number_fields/nfbasis             |   116 +
 src/functions/number_fields/nfbasistoalg        |    20 +
 src/functions/number_fields/nfcertify           |    18 +
 src/functions/number_fields/nfdetint            |     9 +
 src/functions/number_fields/nfdisc              |    37 +
 src/functions/number_fields/nfeltadd            |     8 +
 src/functions/number_fields/nfeltdiv            |     7 +
 src/functions/number_fields/nfeltdiveuc         |     9 +
 src/functions/number_fields/nfeltdivmodpr       |    12 +
 src/functions/number_fields/nfeltdivrem         |     9 +
 src/functions/number_fields/nfeltmod            |    12 +
 src/functions/number_fields/nfeltmul            |     8 +
 src/functions/number_fields/nfeltmulmodpr       |    12 +
 src/functions/number_fields/nfeltnorm           |     6 +
 src/functions/number_fields/nfeltpow            |     9 +
 src/functions/number_fields/nfeltpowmodpr       |    11 +
 src/functions/number_fields/nfeltreduce         |    10 +
 src/functions/number_fields/nfeltreducemodpr    |    11 +
 src/functions/number_fields/nfelttrace          |     6 +
 src/functions/number_fields/nfeltval            |    12 +
 src/functions/number_fields/nffactor            |    32 +
 src/functions/number_fields/nffactorback        |    23 +
 src/functions/number_fields/nffactormod         |    28 +
 src/functions/number_fields/nfgaloisapply       |    49 +
 src/functions/number_fields/nfgaloisconj        |    54 +
 src/functions/number_fields/nfhilbert           |    17 +
 src/functions/number_fields/nfhnf               |    13 +
 src/functions/number_fields/nfhnfmod            |    11 +
 src/functions/number_fields/nfinit              |   175 +
 src/functions/number_fields/nfisideal           |     7 +
 src/functions/number_fields/nfisincl            |    19 +
 src/functions/number_fields/nfisisom            |     7 +
 src/functions/number_fields/nfkermodpr          |    10 +
 src/functions/number_fields/nfmodprinit         |     9 +
 src/functions/number_fields/nfnewprec           |    13 +
 src/functions/number_fields/nfroots             |    27 +
 src/functions/number_fields/nfrootsof1          |    31 +
 src/functions/number_fields/nfsnf               |    23 +
 src/functions/number_fields/nfsolvemodpr        |    21 +
 src/functions/number_fields/nfsubfields         |    17 +
 src/functions/number_fields/polcompositum       |    69 +
 src/functions/number_fields/polgalois           |    88 +
 src/functions/number_fields/polred              |    46 +
 src/functions/number_fields/polredabs           |    81 +
 src/functions/number_fields/polredbest          |    54 +
 src/functions/number_fields/polredord           |    11 +
 src/functions/number_fields/poltschirnhaus      |    11 +
 src/functions/number_fields/rnfalgtobasis       |    11 +
 src/functions/number_fields/rnfbasis            |    17 +
 src/functions/number_fields/rnfbasistoalg       |    10 +
 src/functions/number_fields/rnfcharpoly         |    16 +
 src/functions/number_fields/rnfconductor        |    19 +
 src/functions/number_fields/rnfdedekind         |    75 +
 src/functions/number_fields/rnfdet              |     7 +
 src/functions/number_fields/rnfdisc             |    15 +
 src/functions/number_fields/rnfeltabstorel      |    25 +
 src/functions/number_fields/rnfeltdown          |    26 +
 src/functions/number_fields/rnfeltnorm          |    18 +
 src/functions/number_fields/rnfeltreltoabs      |    22 +
 src/functions/number_fields/rnfelttrace         |    18 +
 src/functions/number_fields/rnfeltup            |    21 +
 src/functions/number_fields/rnfequation         |    56 +
 src/functions/number_fields/rnfhnfbasis         |    11 +
 src/functions/number_fields/rnfidealabstorel    |    40 +
 src/functions/number_fields/rnfidealdown        |    11 +
 src/functions/number_fields/rnfidealhnf         |    11 +
 src/functions/number_fields/rnfidealmul         |    10 +
 src/functions/number_fields/rnfidealnormabs     |    14 +
 src/functions/number_fields/rnfidealnormrel     |    10 +
 src/functions/number_fields/rnfidealreltoabs    |    22 +
 src/functions/number_fields/rnfidealtwoelt      |    11 +
 src/functions/number_fields/rnfidealup          |    21 +
 src/functions/number_fields/rnfinit             |    81 +
 src/functions/number_fields/rnfisabelian        |    13 +
 src/functions/number_fields/rnfisfree           |    12 +
 src/functions/number_fields/rnfisnorm           |    47 +
 src/functions/number_fields/rnfisnorminit       |    22 +
 src/functions/number_fields/rnfkummer           |    24 +
 src/functions/number_fields/rnflllgram          |    13 +
 src/functions/number_fields/rnfnormgroup        |    21 +
 src/functions/number_fields/rnfpolred           |    15 +
 src/functions/number_fields/rnfpolredabs        |    42 +
 src/functions/number_fields/rnfpolredbest       |    57 +
 src/functions/number_fields/rnfpseudobasis      |    16 +
 src/functions/number_fields/rnfsteinitz         |    17 +
 src/functions/number_fields/subgrouplist        |    41 +
 src/functions/number_fields/zetak               |    54 +
 src/functions/number_fields/zetakinit           |    28 +
 src/functions/number_theoretical/addprimes      |    17 +
 src/functions/number_theoretical/bestappr       |    50 +
 src/functions/number_theoretical/bestapprPade   |    43 +
 src/functions/number_theoretical/bezout         |     6 +
 src/functions/number_theoretical/bigomega       |    19 +
 src/functions/number_theoretical/binomial       |    13 +
 src/functions/number_theoretical/chinese        |    48 +
 src/functions/number_theoretical/content        |    27 +
 src/functions/number_theoretical/contfrac       |    72 +
 src/functions/number_theoretical/contfracpnqn   |    37 +
 src/functions/number_theoretical/core           |    15 +
 src/functions/number_theoretical/coredisc       |    22 +
 src/functions/number_theoretical/dirdiv         |     9 +
 src/functions/number_theoretical/direuler       |    27 +
 src/functions/number_theoretical/dirmul         |    20 +
 src/functions/number_theoretical/divisors       |    16 +
 src/functions/number_theoretical/eulerphi       |    16 +
 src/functions/number_theoretical/factor         |   168 +
 src/functions/number_theoretical/factorback     |    39 +
 src/functions/number_theoretical/factorcantor   |    15 +
 src/functions/number_theoretical/factorff       |    41 +
 src/functions/number_theoretical/factorial      |     8 +
 src/functions/number_theoretical/factorint      |    37 +
 src/functions/number_theoretical/factormod      |    14 +
 src/functions/number_theoretical/ffgen          |    41 +
 src/functions/number_theoretical/ffinit         |    16 +
 src/functions/number_theoretical/fflog          |    40 +
 src/functions/number_theoretical/ffnbirred      |    15 +
 src/functions/number_theoretical/fforder        |    21 +
 src/functions/number_theoretical/ffprimroot     |    44 +
 src/functions/number_theoretical/fibonacci      |     6 +
 src/functions/number_theoretical/gcd            |    53 +
 src/functions/number_theoretical/gcdext         |    34 +
 src/functions/number_theoretical/hilbert        |    12 +
 src/functions/number_theoretical/isfundamental  |    11 +
 src/functions/number_theoretical/ispolygonal    |    14 +
 src/functions/number_theoretical/ispower        |    30 +
 src/functions/number_theoretical/ispowerful     |    17 +
 src/functions/number_theoretical/isprime        |    55 +
 src/functions/number_theoretical/isprimepower   |    10 +
 src/functions/number_theoretical/ispseudoprime  |    32 +
 src/functions/number_theoretical/issquare       |    41 +
 src/functions/number_theoretical/issquarefree   |     9 +
 src/functions/number_theoretical/istotient      |    21 +
 src/functions/number_theoretical/kronecker      |    22 +
 src/functions/number_theoretical/lcm            |    36 +
 src/functions/number_theoretical/logint         |    34 +
 src/functions/number_theoretical/moebius        |     6 +
 src/functions/number_theoretical/nextprime      |    12 +
 src/functions/number_theoretical/numbpart       |    11 +
 src/functions/number_theoretical/numdiv         |     9 +
 src/functions/number_theoretical/omega          |    18 +
 src/functions/number_theoretical/partitions     |    52 +
 src/functions/number_theoretical/polrootsff     |    26 +
 src/functions/number_theoretical/precprime      |    12 +
 src/functions/number_theoretical/prime          |    10 +
 src/functions/number_theoretical/primepi        |    17 +
 src/functions/number_theoretical/primes         |    17 +
 src/functions/number_theoretical/qfbclassno     |    62 +
 src/functions/number_theoretical/qfbcompraw     |    10 +
 src/functions/number_theoretical/qfbhclassno    |     9 +
 src/functions/number_theoretical/qfbnucomp      |    18 +
 src/functions/number_theoretical/qfbnupow       |     9 +
 src/functions/number_theoretical/qfbpowraw      |     9 +
 src/functions/number_theoretical/qfbprimeform   |    14 +
 src/functions/number_theoretical/qfbred         |    36 +
 src/functions/number_theoretical/qfbsolve       |    18 +
 src/functions/number_theoretical/quadclassunit  |    57 +
 src/functions/number_theoretical/quaddisc       |     6 +
 src/functions/number_theoretical/quadgen        |    10 +
 src/functions/number_theoretical/quadhilbert    |    14 +
 src/functions/number_theoretical/quadpoly       |    10 +
 src/functions/number_theoretical/quadray        |    16 +
 src/functions/number_theoretical/quadregulator  |     9 +
 src/functions/number_theoretical/quadunit       |    12 +
 src/functions/number_theoretical/randomprime    |    10 +
 src/functions/number_theoretical/removeprimes   |    10 +
 src/functions/number_theoretical/sigma          |    12 +
 src/functions/number_theoretical/sqrtint        |    15 +
 src/functions/number_theoretical/sqrtnint       |    15 +
 src/functions/number_theoretical/stirling       |    31 +
 src/functions/number_theoretical/sumdedekind    |    10 +
 src/functions/number_theoretical/sumdigits      |    11 +
 src/functions/number_theoretical/zncoppersmith  |    55 +
 src/functions/number_theoretical/znlog          |    70 +
 src/functions/number_theoretical/znorder        |    18 +
 src/functions/number_theoretical/znprimroot     |    14 +
 src/functions/number_theoretical/znstar         |    26 +
 src/functions/operators/cmp                     |    30 +
 src/functions/operators/divrem                  |    30 +
 src/functions/operators/lex                     |    26 +
 src/functions/operators/max                     |    17 +
 src/functions/operators/min                     |    17 +
 src/functions/operators/shift                   |    12 +
 src/functions/operators/shiftmul                |    10 +
 src/functions/operators/sign                    |    10 +
 src/functions/operators/vecmax                  |    27 +
 src/functions/operators/vecmin                  |    28 +
 src/functions/polynomials/O                     |    23 +
 src/functions/polynomials/bezoutres             |     6 +
 src/functions/polynomials/deriv                 |    18 +
 src/functions/polynomials/diffop                |    48 +
 src/functions/polynomials/eval                  |    52 +
 src/functions/polynomials/factorpadic           |    37 +
 src/functions/polynomials/intformal             |    34 +
 src/functions/polynomials/padicappr             |    14 +
 src/functions/polynomials/padicfields           |    34 +
 src/functions/polynomials/polchebyshev          |    25 +
 src/functions/polynomials/polcoeff              |    23 +
 src/functions/polynomials/polcyclo              |    20 +
 src/functions/polynomials/polcyclofactors       |    28 +
 src/functions/polynomials/poldegree             |    18 +
 src/functions/polynomials/poldisc               |    13 +
 src/functions/polynomials/poldiscreduced        |    11 +
 src/functions/polynomials/polgraeffe            |     8 +
 src/functions/polynomials/polhensellift         |    24 +
 src/functions/polynomials/polhermite            |    14 +
 src/functions/polynomials/polinterpolate        |    15 +
 src/functions/polynomials/poliscyclo            |    16 +
 src/functions/polynomials/poliscycloprod        |    22 +
 src/functions/polynomials/polisirreducible      |    10 +
 src/functions/polynomials/pollead               |    14 +
 src/functions/polynomials/pollegendre           |    12 +
 src/functions/polynomials/polrecip              |     7 +
 src/functions/polynomials/polresultant          |    24 +
 src/functions/polynomials/polresultantext       |    25 +
 src/functions/polynomials/polroots              |    16 +
 src/functions/polynomials/polrootsmod           |    18 +
 src/functions/polynomials/polrootspadic         |    21 +
 src/functions/polynomials/polsturm              |    12 +
 src/functions/polynomials/polsubcyclo           |    16 +
 src/functions/polynomials/polsylvestermatrix    |    13 +
 src/functions/polynomials/polsym                |     7 +
 src/functions/polynomials/poltchebi             |     6 +
 src/functions/polynomials/polzagier             |    19 +
 src/functions/polynomials/serconvol             |     8 +
 src/functions/polynomials/serlaplace            |     8 +
 src/functions/polynomials/serreverse            |    14 +
 src/functions/polynomials/subst                 |    30 +
 src/functions/polynomials/substpol              |    27 +
 src/functions/polynomials/substvec              |    18 +
 src/functions/polynomials/sumformal             |    27 +
 src/functions/polynomials/taylor                |    18 +
 src/functions/polynomials/thue                  |    64 +
 src/functions/polynomials/thueinit              |    19 +
 src/functions/programming/_eval_mnemonic        |     5 +
 src/functions/programming/addhelp               |    34 +
 src/functions/programming/alarm                 |    49 +
 src/functions/programming/alias                 |    59 +
 src/functions/programming/allocatemem           |    49 +
 src/functions/programming/apply                 |    55 +
 src/functions/programming/break                 |    11 +
 src/functions/programming/breakpoint            |    22 +
 src/functions/programming/dbg_down              |     8 +
 src/functions/programming/dbg_err               |    19 +
 src/functions/programming/dbg_up                |     8 +
 src/functions/programming/dbg_x                 |    10 +
 src/functions/programming/default               |    32 +
 src/functions/programming/errname               |     8 +
 src/functions/programming/error                 |    25 +
 src/functions/programming/extern                |    10 +
 src/functions/programming/externstr             |    11 +
 src/functions/programming/for                   |     8 +
 src/functions/programming/forcomposite          |    43 +
 src/functions/programming/fordiv                |    35 +
 src/functions/programming/forell                |    25 +
 src/functions/programming/forpart               |    68 +
 src/functions/programming/forprime              |    69 +
 src/functions/programming/forstep               |    21 +
 src/functions/programming/forsubgroup           |    52 +
 src/functions/programming/forvec                |    31 +
 src/functions/programming/getabstime            |    12 +
 src/functions/programming/getenv                |     6 +
 src/functions/programming/getheap               |     9 +
 src/functions/programming/getrand               |    10 +
 src/functions/programming/getstack              |     7 +
 src/functions/programming/gettime               |    10 +
 src/functions/programming/global                |     5 +
 src/functions/programming/if                    |    72 +
 src/functions/programming/iferr                 |   236 +
 src/functions/programming/inline                |     8 +
 src/functions/programming/input                 |    15 +
 src/functions/programming/install               |    79 +
 src/functions/programming/kill                  |    45 +
 src/functions/programming/local                 |     3 +
 src/functions/programming/my                    |     3 +
 src/functions/programming/next                  |    11 +
 src/functions/programming/parapply              |    25 +
 src/functions/programming/pareval               |    16 +
 src/functions/programming/parfor                |    24 +
 src/functions/programming/parforprime           |    18 +
 src/functions/programming/parselect             |    11 +
 src/functions/programming/parsum                |    17 +
 src/functions/programming/parvector             |    22 +
 src/functions/programming/print                 |    10 +
 src/functions/programming/print1                |    12 +
 src/functions/programming/printf                |   185 +
 src/functions/programming/printsep              |    13 +
 src/functions/programming/printsep1             |    13 +
 src/functions/programming/printtex              |    20 +
 src/functions/programming/quit                  |    11 +
 src/functions/programming/read                  |    21 +
 src/functions/programming/readstr               |    11 +
 src/functions/programming/readvec               |    33 +
 src/functions/programming/return                |     8 +
 src/functions/programming/select                |    73 +
 src/functions/programming/setrand               |    10 +
 src/functions/programming/system                |    12 +
 src/functions/programming/trap                  |    66 +
 src/functions/programming/type                  |    13 +
 src/functions/programming/uninline              |     4 +
 src/functions/programming/until                 |     9 +
 src/functions/programming/version               |    49 +
 src/functions/programming/warning               |    16 +
 src/functions/programming/whatnow               |    12 +
 src/functions/programming/while                 |     9 +
 src/functions/programming/write                 |     9 +
 src/functions/programming/write1                |     9 +
 src/functions/programming/writebin              |    38 +
 src/functions/programming/writetex              |     8 +
 src/functions/sums/derivnum                     |    34 +
 src/functions/sums/intcirc                      |    24 +
 src/functions/sums/intfouriercos                |    18 +
 src/functions/sums/intfourierexp                |    19 +
 src/functions/sums/intfouriersin                |    18 +
 src/functions/sums/intfuncinit                  |    26 +
 src/functions/sums/intlaplaceinv                |    56 +
 src/functions/sums/intmellininv                 |    43 +
 src/functions/sums/intmellininvshort            |    43 +
 src/functions/sums/intnum                       |   287 +
 src/functions/sums/intnuminit                   |    31 +
 src/functions/sums/intnuminitgen                |    18 +
 src/functions/sums/intnumromb                   |    51 +
 src/functions/sums/intnumstep                   |     9 +
 src/functions/sums/prod                         |    39 +
 src/functions/sums/prodeuler                    |    12 +
 src/functions/sums/prodinf                      |    20 +
 src/functions/sums/solve                        |    17 +
 src/functions/sums/sum                          |    22 +
 src/functions/sums/sumalt                       |    53 +
 src/functions/sums/sumdiv                       |    14 +
 src/functions/sums/sumdivmult                   |    11 +
 src/functions/sums/suminf                       |    32 +
 src/functions/sums/sumnum                       |   126 +
 src/functions/sums/sumnumalt                    |    53 +
 src/functions/sums/sumnuminit                   |    12 +
 src/functions/sums/sumpos                       |    29 +
 src/functions/symbolic_operators/add            |    22 +
 src/functions/symbolic_operators/adde           |    19 +
 src/functions/symbolic_operators/and            |     7 +
 src/functions/symbolic_operators/call           |     8 +
 src/functions/symbolic_operators/coeff          |    29 +
 src/functions/symbolic_operators/compr          |    20 +
 src/functions/symbolic_operators/concat         |     8 +
 src/functions/symbolic_operators/deriv          |     7 +
 src/functions/symbolic_operators/div            |    18 +
 src/functions/symbolic_operators/dive           |    16 +
 src/functions/symbolic_operators/divent         |    13 +
 src/functions/symbolic_operators/divente        |    10 +
 src/functions/symbolic_operators/divround       |     8 +
 src/functions/symbolic_operators/divrounde      |     9 +
 src/functions/symbolic_operators/eq             |    29 +
 src/functions/symbolic_operators/fact           |     7 +
 src/functions/symbolic_operators/ge             |    17 +
 src/functions/symbolic_operators/gt             |    17 +
 src/functions/symbolic_operators/hist           |    11 +
 src/functions/symbolic_operators/id             |     5 +
 src/functions/symbolic_operators/le             |    18 +
 src/functions/symbolic_operators/lt             |    17 +
 src/functions/symbolic_operators/mm             |    14 +
 src/functions/symbolic_operators/mod            |    13 +
 src/functions/symbolic_operators/mode           |    12 +
 src/functions/symbolic_operators/mul            |    20 +
 src/functions/symbolic_operators/mule           |    18 +
 src/functions/symbolic_operators/ne             |    25 +
 src/functions/symbolic_operators/neg            |    11 +
 src/functions/symbolic_operators/not            |     8 +
 src/functions/symbolic_operators/or             |     7 +
 src/functions/symbolic_operators/pl             |     9 +
 src/functions/symbolic_operators/pound          |    10 +
 src/functions/symbolic_operators/pow            |    28 +
 src/functions/symbolic_operators/pp             |    14 +
 src/functions/symbolic_operators/range          |     8 +
 src/functions/symbolic_operators/shiftl         |     9 +
 src/functions/symbolic_operators/shiftle        |    10 +
 src/functions/symbolic_operators/shiftr         |    10 +
 src/functions/symbolic_operators/shiftre        |    10 +
 src/functions/symbolic_operators/slice          |    14 +
 src/functions/symbolic_operators/sub            |    20 +
 src/functions/symbolic_operators/sube           |    19 +
 src/functions/symbolic_operators/trans          |     8 +
 src/functions/transcendental/Catalan            |    10 +
 src/functions/transcendental/Euler              |    10 +
 src/functions/transcendental/I                  |     7 +
 src/functions/transcendental/Pi                 |     9 +
 src/functions/transcendental/abs                |    26 +
 src/functions/transcendental/acos               |    11 +
 src/functions/transcendental/acosh              |    10 +
 src/functions/transcendental/agm                |    11 +
 src/functions/transcendental/arg                |     6 +
 src/functions/transcendental/asin               |    11 +
 src/functions/transcendental/asinh              |    10 +
 src/functions/transcendental/atan               |    12 +
 src/functions/transcendental/atanh              |     8 +
 src/functions/transcendental/bernfrac           |     8 +
 src/functions/transcendental/bernpol            |    12 +
 src/functions/transcendental/bernreal           |     9 +
 src/functions/transcendental/bernvec            |    16 +
 src/functions/transcendental/besselh1           |     6 +
 src/functions/transcendental/besselh2           |     6 +
 src/functions/transcendental/besseli            |     9 +
 src/functions/transcendental/besselj            |     9 +
 src/functions/transcendental/besseljh           |    10 +
 src/functions/transcendental/besselk            |     6 +
 src/functions/transcendental/besseln            |     6 +
 src/functions/transcendental/cos                |     6 +
 src/functions/transcendental/cosh               |     6 +
 src/functions/transcendental/cotan              |     6 +
 src/functions/transcendental/dilog              |     7 +
 src/functions/transcendental/eint1              |    22 +
 src/functions/transcendental/erfc               |     9 +
 src/functions/transcendental/eta                |    26 +
 src/functions/transcendental/exp                |    14 +
 src/functions/transcendental/expm1              |    27 +
 src/functions/transcendental/gamma              |    22 +
 src/functions/transcendental/gammah             |     6 +
 src/functions/transcendental/hyperu             |     8 +
 src/functions/transcendental/incgam             |    12 +
 src/functions/transcendental/incgamc            |    10 +
 src/functions/transcendental/lambertw           |     7 +
 src/functions/transcendental/lngamma            |    25 +
 src/functions/transcendental/log                |    23 +
 src/functions/transcendental/polylog            |    35 +
 src/functions/transcendental/psi                |     7 +
 src/functions/transcendental/sin                |     6 +
 src/functions/transcendental/sinh               |     6 +
 src/functions/transcendental/sqr                |    30 +
 src/functions/transcendental/sqrt               |    19 +
 src/functions/transcendental/sqrtn              |    47 +
 src/functions/transcendental/tan                |     6 +
 src/functions/transcendental/tanh               |     6 +
 src/functions/transcendental/teichmuller        |     7 +
 src/functions/transcendental/theta              |     7 +
 src/functions/transcendental/thetanullk         |    12 +
 src/functions/transcendental/weber              |    23 +
 src/functions/transcendental/zeta               |    17 +
 src/gp/gp.c                                     |  2627 ++++
 src/gp/gp.h                                     |    84 +
 src/gp/gp_default.h                             |    20 +
 src/gp/gp_init.c                                |   117 +
 src/gp/gp_init.h                                |    20 +
 src/gp/gp_rl.c                                  |   890 ++
 src/gp/highlvl.c                                |    23 +
 src/gp/highlvl.h                                |    35 +
 src/gp/whatnow.c                                |   102 +
 src/gp/whatnow.h                                |   657 +
 src/graph/plotQt.c                              |   625 +
 src/graph/plotQt4.c                             |   612 +
 src/graph/plotWin32.c                           |   121 +
 src/graph/plotX.c                               |   298 +
 src/graph/plotfltk.c                            |   229 +
 src/graph/plotnull.c                            |    28 +
 src/graph/plotport.c                            |  2836 ++++
 src/graph/plotps.c                              |    61 +
 src/graph/rect.h                                |   287 +
 src/headers/pari.h                              |    64 +
 src/headers/paricast.h                          |    31 +
 src/headers/paricom.h                           |   121 +
 src/headers/paridecl.h                          |  4152 ++++++
 src/headers/parierr.h                           |    40 +
 src/headers/parigen.h                           |   171 +
 src/headers/pariinl.h                           |  2592 ++++
 src/headers/parinf.h                            |   202 +
 src/headers/pariold.h                           |   428 +
 src/headers/paripriv.h                          |   727 +
 src/headers/paristio.h                          |   246 +
 src/headers/parisys.h                           |    82 +
 src/headers/paritune.h                          |    81 +
 src/kernel/README                               |    33 +
 src/kernel/alpha/asm0.h                         |    69 +
 src/kernel/alpha/asm1.h                         |    97 +
 src/kernel/arm/asm0.h                           |    83 +
 src/kernel/gmp/MakeLVL1.SH                      |    12 +
 src/kernel/gmp/gcd.c                            |    73 +
 src/kernel/gmp/gcdext.c                         |   165 +
 src/kernel/gmp/int.h                            |    37 +
 src/kernel/gmp/mp.c                             |  1400 ++
 src/kernel/gmp/tune.h                           |    79 +
 src/kernel/hppa/asm0.h                          |    89 +
 src/kernel/hppa64/asm0.h                        |   121 +
 src/kernel/ia64/asm0.h                          |    55 +
 src/kernel/ia64/asm1.h                          |    42 +
 src/kernel/ix86/asm0.h                          |   135 +
 src/kernel/m68k/asm0.h                          |   101 +
 src/kernel/mips/asm0.h                          |    42 +
 src/kernel/mips64/asm0.h                        |    42 +
 src/kernel/none/MakeLVL1.SH                     |    12 +
 src/kernel/none/add.c                           |   374 +
 src/kernel/none/addll.h                         |   103 +
 src/kernel/none/asm0.h                          |    16 +
 src/kernel/none/bfffo.h                         |    64 +
 src/kernel/none/cmp.c                           |   137 +
 src/kernel/none/divll.h                         |   158 +
 src/kernel/none/gcd.c                           |   122 +
 src/kernel/none/gcdext.c                        |   211 +
 src/kernel/none/gcdll.c                         |  1084 ++
 src/kernel/none/int.h                           |    37 +
 src/kernel/none/invmod.c                        |   173 +
 src/kernel/none/level1.h                        |  1285 ++
 src/kernel/none/mp.c                            |  2155 +++
 src/kernel/none/mp_indep.c                      |  1072 ++
 src/kernel/none/mpinl.c                         |    19 +
 src/kernel/none/mulll.h                         |   140 +
 src/kernel/none/ratlift.c                       |   289 +
 src/kernel/none/tune-gen.h                      |    40 +
 src/kernel/none/tune.h                          |    79 +
 src/kernel/ppc/asm0.h                           |    74 +
 src/kernel/ppc64/asm0.h                         |    74 +
 src/kernel/sparcv8_micro/MakeLVL0.SH            |     4 +
 src/kernel/sparcv8_micro/asm0-common.h          |    80 +
 src/kernel/sparcv8_micro/asm0.h                 |    25 +
 src/kernel/sparcv8_super/MakeLVL0.SH            |     4 +
 src/kernel/sparcv8_super/asm0.h                 |     3 +
 src/kernel/x86_64/asm0.h                        |   126 +
 src/language/anal.c                             |  1174 ++
 src/language/anal.h                             |    65 +
 src/language/compat.c                           |   722 +
 src/language/compile.c                          |  2392 ++++
 src/language/default.c                          |   814 ++
 src/language/default.h                          |    34 +
 src/language/es.c                               |  4941 +++++++
 src/language/eval.c                             |  2236 +++
 src/language/hash.c                             |   244 +
 src/language/init.c                             |  2338 ++++
 src/language/init.h                             |   740 +
 src/language/intnum.c                           |  1785 +++
 src/language/members.c                          |   596 +
 src/language/opcode.h                           |    37 +
 src/language/paricfg.c                          |    21 +
 src/language/pariinl.c                          |    18 +
 src/language/parse.c                            |  2903 ++++
 src/language/parse.h                            |   100 +
 src/language/parse.y                            |   234 +
 src/language/parsec.h                           |   223 +
 src/language/sumiter.c                          |  1760 +++
 src/language/tree.h                             |    48 +
 src/modules/DedekZeta.c                         |   592 +
 src/modules/aprcl.c                             |  1028 ++
 src/modules/elldata.c                           |   263 +
 src/modules/ellsea.c                            |  1536 ++
 src/modules/galois.c                            |  2428 ++++
 src/modules/galpol.c                            |    63 +
 src/modules/genus2red.c                         |  2290 +++
 src/modules/groupid.c                           |   551 +
 src/modules/krasner.c                           |   957 ++
 src/modules/kummer.c                            |  1347 ++
 src/modules/mpqs.c                              |  3136 +++++
 src/modules/mpqs.h                              |   502 +
 src/modules/part.c                              |   413 +
 src/modules/stark.c                             |  3657 +++++
 src/modules/subfield.c                          |   932 ++
 src/modules/thue.c                              |  1384 ++
 src/mt/mpi.c                                    |   314 +
 src/mt/mpi.h                                    |    16 +
 src/mt/mt.c                                     |    78 +
 src/mt/mt.h                                     |    20 +
 src/mt/pthread.c                                |   305 +
 src/mt/pthread.h                                |    16 +
 src/mt/single.c                                 |    49 +
 src/mt/single.h                                 |    16 +
 src/systems/cygwin/cygwin.c                     |    28 +
 src/systems/darwin/darwin.c                     |   209 +
 src/systems/darwin/dlfcn.h                      |    68 +
 src/systems/mingw/mingw.c                       |   158 +
 src/systems/mingw/mingw.h                       |    19 +
 src/systems/os2/README                          |    51 +
 src/systems/os2/dlfcn.h                         |     5 +
 src/systems/os2/os2.c                           |   217 +
 src/systems/os2/pari.def.base                   |     7 +
 src/systems/win32/README.MSVC                   |    66 +
 src/systems/win32/pariinl.h                     |    11 +
 src/systems/winCE/pariCE.c                      |   151 +
 src/systems/winCE/pariCE.h                      |    14 +
 src/test/32/addprimes                           |    26 +
 src/test/32/agm                                 |     0
 src/test/32/analyz                              |     8 +
 src/test/32/apply                               |    15 +
 src/test/32/arith                               |     3 +
 src/test/32/aurifeuille                         |    13 +
 src/test/32/bern                                |    45 +
 src/test/32/bessel                              |   339 +
 src/test/32/bestappr                            |    17 +
 src/test/32/bit                                 |   185 +
 src/test/32/bnfinit                             |     0
 src/test/32/bnfisintnorm                        |   771 +
 src/test/32/bnr                                 |    44 +
 src/test/32/bnrL1                               |    33 +
 src/test/32/characteristic                      |    13 +
 src/test/32/charpoly                            |    73 +
 src/test/32/chinese                             |     5 +
 src/test/32/cmp                                 |    18 +
 src/test/32/combinat                            |    42 +
 src/test/32/compat                              |  2594 ++++
 src/test/32/concat                              |   104 +
 src/test/32/content                             |    46 +
 src/test/32/contfrac                            |    69 +
 src/test/32/cxtrigo                             |    18 +
 src/test/32/cyclo                               |    58 +
 src/test/32/debugger                            |    94 +
 src/test/32/deriv                               |    53 +
 src/test/32/det                                 |     9 +
 src/test/32/diffop                              |    11 +
 src/test/32/digits                              |    32 +
 src/test/32/dirmul                              |     7 +
 src/test/32/disc                                |     2 +
 src/test/32/div                                 |   910 ++
 src/test/32/ell                                 |   398 +
 src/test/32/ellanal                             |    14 +
 src/test/32/ellff                               |     6 +
 src/test/32/ellglobalred                        |    20 +
 src/test/32/elliptic                            |   129 +
 src/test/32/ellsea                              |    13 +
 src/test/32/ellweilpairing                      |   296 +
 src/test/32/env                                 |     6 +
 src/test/32/equal                               |    17 +
 src/test/32/err                                 |   590 +
 src/test/32/exact0                              |    24 +
 src/test/32/extract                             |   308 +
 src/test/32/factor                              |   149 +
 src/test/32/factorint                           |    29 +
 src/test/32/factormod                           |   251 +
 src/test/32/ff                                  |   280 +
 src/test/32/ffisom                              |    47 +
 src/test/32/for                                 |    17 +
 src/test/32/galois                              |   220 +
 src/test/32/galoisinit                          |   102 +
 src/test/32/galpol                              |    22 +
 src/test/32/gamma                               |    86 +
 src/test/32/gcdext                              |   113 +
 src/test/32/genus2red                           |   718 +
 src/test/32/graph                               |    65 +
 src/test/32/help                                |   149 +
 src/test/32/history                             |    18 +
 src/test/32/ideal                               |   147 +
 src/test/32/idealappr                           |     9 +
 src/test/32/idealramgroups                      |    21 +
 src/test/32/incgam                              |    73 +
 src/test/32/interpol                            |    11 +
 src/test/32/intnum                              |   117 +
 src/test/32/io                                  |     8 +
 src/test/32/ispower                             |  1194 ++
 src/test/32/isprime                             |    23 +
 src/test/32/iterator                            |    22 +
 src/test/32/kernel                              |    23 +
 src/test/32/krasner                             |   119 +
 src/test/32/lambert                             |    15 +
 src/test/32/lex                                 |    29 +
 src/test/32/lift                                |    65 +
 src/test/32/lindep                              |    19 +
 src/test/32/linear                              |   679 +
 src/test/32/list                                |    26 +
 src/test/32/lll                                 |    78 +
 src/test/32/log                                 |     4 +
 src/test/32/logint                              |     9 +
 src/test/32/mat                                 |   361 +
 src/test/32/mathnf                              |    59 +
 src/test/32/matsnf                              |   331 +
 src/test/32/member                              |   319 +
 src/test/32/minim                               |     5 +
 src/test/32/minmax                              |     5 +
 src/test/32/modfun                              |     9 +
 src/test/32/modpr                               |   363 +
 src/test/32/modular                             |    68 +
 src/test/32/multiif                             |    21 +
 src/test/32/multivar-mul                        |     0
 src/test/32/nf                                  |   362 +
 src/test/32/nffactor                            |   734 +
 src/test/32/nfhilbert                           |   107 +
 src/test/32/nfields                             |   751 +
 src/test/32/nfrootsof1                          |     8 +
 src/test/32/norm                                |    38 +
 src/test/32/number                              |   272 +
 src/test/32/objets                              |   125 +
 src/test/32/op                                  |     7 +
 src/test/32/orthopol                            |    16 +
 src/test/32/padic                               |    80 +
 src/test/32/parallel                            |    23 +
 src/test/32/partition                           |   194 +
 src/test/32/ploth                               |    83 +
 src/test/32/pol                                 |    36 +
 src/test/32/polmod                              |    61 +
 src/test/32/polred                              |    70 +
 src/test/32/polygonal                           |     0
 src/test/32/polylog                             |    19 +
 src/test/32/polyser                             |   160 +
 src/test/32/pow                                 |    55 +
 src/test/32/prec                                |    22 +
 src/test/32/prime                               |     9 +
 src/test/32/primes                              |    12 +
 src/test/32/print                               |     4 +
 src/test/32/printf                              |   187 +
 src/test/32/program                             |   107 +
 src/test/32/qf                                  |    69 +
 src/test/32/qfb                                 |    16 +
 src/test/32/qfbsolve                            |     0
 src/test/32/qfisom                              |    20 +
 src/test/32/quad                                |    12 +
 src/test/32/quadclassunit                       |   628 +
 src/test/32/quadray                             |  1126 ++
 src/test/32/random                              |    30 +
 src/test/32/ranges                              |   144 +
 src/test/32/real                                |     7 +
 src/test/32/resultant                           |   413 +
 src/test/32/rfrac                               |     8 +
 src/test/32/rnf                                 |   754 +
 src/test/32/rnfkummer                           |   174 +
 src/test/32/round                               |    86 +
 src/test/32/round4                              |   462 +
 src/test/32/select                              |    15 +
 src/test/32/ser                                 |    38 +
 src/test/32/set                                 |    25 +
 src/test/32/size                                |    13 +
 src/test/32/sort                                |    30 +
 src/test/32/sqrtn                               |    55 +
 src/test/32/stark                               |   117 +
 src/test/32/str                                 |     8 +
 src/test/32/subcyclo                            |     3 +
 src/test/32/subfields                           |  1701 +++
 src/test/32/subgroup                            |    11 +
 src/test/32/subst                               |    24 +
 src/test/32/sumdedekind                         |     5 +
 src/test/32/sumdiv                              |    92 +
 src/test/32/sumformal                           |    10 +
 src/test/32/sumiter                             |    43 +
 src/test/32/thue                                |    26 +
 src/test/32/time                                |     3 +
 src/test/32/trans                               |   438 +
 src/test/32/trans2                              |    90 +
 src/test/32/valuation                           |    27 +
 src/test/32/variable                            |    12 +
 src/test/32/whatnow                             |    22 +
 src/test/32/zetak                               |    52 +
 src/test/32/zn                                  |    45 +
 src/test/32/zncoppersmith                       |     9 +
 src/test/64/bnr                                 |    44 +
 src/test/64/bnrL1                               |    33 +
 src/test/64/compat                              |  2593 ++++
 src/test/64/ell                                 |   405 +
 src/test/64/ellanal                             |    14 +
 src/test/64/ff                                  |   283 +
 src/test/64/incgam                              |    73 +
 src/test/64/isprime                             |    25 +
 src/test/64/kernel                              |    23 +
 src/test/64/member                              |   319 +
 src/test/64/nf                                  |   361 +
 src/test/64/nfields                             |   751 +
 src/test/64/random                              |    30 +
 src/test/64/real                                |     3 +
 src/test/64/rnfkummer                           |   175 +
 src/test/dotest                                 |   167 +
 src/test/dummy.c                                |    23 +
 src/test/in/addprimes                           |    29 +
 src/test/in/agm                                 |    18 +
 src/test/in/analyz                              |     8 +
 src/test/in/apply                               |    12 +
 src/test/in/arith                               |     4 +
 src/test/in/aurifeuille                         |    21 +
 src/test/in/bern                                |     8 +
 src/test/in/bessel                              |    27 +
 src/test/in/bestappr                            |    18 +
 src/test/in/bit                                 |    32 +
 src/test/in/bnfisintnorm                        |    16 +
 src/test/in/bnr                                 |    50 +
 src/test/in/bnrL1                               |     6 +
 src/test/in/characteristic                      |     6 +
 src/test/in/charpoly                            |    37 +
 src/test/in/chinese                             |     4 +
 src/test/in/cmp                                 |    17 +
 src/test/in/combinat                            |     8 +
 src/test/in/compat                              |   621 +
 src/test/in/concat                              |    34 +
 src/test/in/content                             |     7 +
 src/test/in/contfrac                            |    18 +
 src/test/in/cxtrigo                             |    43 +
 src/test/in/cyclo                               |    39 +
 src/test/in/debugger                            |    38 +
 src/test/in/deriv                               |    24 +
 src/test/in/det                                 |     5 +
 src/test/in/diffop                              |     7 +
 src/test/in/digits                              |    18 +
 src/test/in/dirmul                              |     6 +
 src/test/in/disc                                |     1 +
 src/test/in/div                                 |    37 +
 src/test/in/ell                                 |   271 +
 src/test/in/ellanal                             |    18 +
 src/test/in/ellff                               |   133 +
 src/test/in/ellglobalred                        |    22 +
 src/test/in/elliptic                            |    51 +
 src/test/in/ellsea                              |    21 +
 src/test/in/ellweilpairing                      |    61 +
 src/test/in/env                                 |     5 +
 src/test/in/equal                               |    24 +
 src/test/in/err                                 |   252 +
 src/test/in/exact0                              |    28 +
 src/test/in/extract                             |    39 +
 src/test/in/factor                              |    30 +
 src/test/in/factorint                           |     2 +
 src/test/in/factormod                           |    29 +
 src/test/in/ff                                  |   108 +
 src/test/in/ffisom                              |    53 +
 src/test/in/for                                 |     6 +
 src/test/in/galois                              |   226 +
 src/test/in/galoisinit                          |    70 +
 src/test/in/galpol                              |    31 +
 src/test/in/gamma                               |    32 +
 src/test/in/gcdext                              |    62 +
 src/test/in/genus2red                           |   699 +
 src/test/in/graph                               |    30 +
 src/test/in/help                                |    25 +
 src/test/in/history                             |    17 +
 src/test/in/ideal                               |    72 +
 src/test/in/idealappr                           |     8 +
 src/test/in/idealramgroups                      |    16 +
 src/test/in/incgam                              |    52 +
 src/test/in/interpol                            |    11 +
 src/test/in/intnum                              |    75 +
 src/test/in/io                                  |    23 +
 src/test/in/ispower                             |   117 +
 src/test/in/isprime                             |    16 +
 src/test/in/iterator                            |     5 +
 src/test/in/krasner                             |    15 +
 src/test/in/lambert                             |     7 +
 src/test/in/lex                                 |    15 +
 src/test/in/lift                                |    12 +
 src/test/in/lindep                              |    17 +
 src/test/in/linear                              |    93 +
 src/test/in/list                                |    37 +
 src/test/in/lll                                 |    18 +
 src/test/in/log                                 |     4 +
 src/test/in/logint                              |     8 +
 src/test/in/mat                                 |    94 +
 src/test/in/mathnf                              |    21 +
 src/test/in/matsnf                              |    24 +
 src/test/in/member                              |    90 +
 src/test/in/minim                               |    14 +
 src/test/in/minmax                              |    12 +
 src/test/in/modfun                              |     8 +
 src/test/in/modpr                               |    41 +
 src/test/in/modular                             |    29 +
 src/test/in/multiif                             |    22 +
 src/test/in/multivar-mul                        |    26 +
 src/test/in/nf                                  |    82 +
 src/test/in/nffactor                            |   108 +
 src/test/in/nfhilbert                           |    58 +
 src/test/in/nfields                             |   147 +
 src/test/in/nfrootsof1                          |     9 +
 src/test/in/norm                                |    23 +
 src/test/in/number                              |    86 +
 src/test/in/objets                              |    62 +
 src/test/in/op                                  |     6 +
 src/test/in/orthopol                            |    26 +
 src/test/in/padic                               |    40 +
 src/test/in/parallel                            |    34 +
 src/test/in/partition                           |    39 +
 src/test/in/ploth                               |    36 +
 src/test/in/pol                                 |    22 +
 src/test/in/polmod                              |    35 +
 src/test/in/polred                              |    55 +
 src/test/in/polygonal                           |     5 +
 src/test/in/polylog                             |    11 +
 src/test/in/polyser                             |    52 +
 src/test/in/pow                                 |    36 +
 src/test/in/prec                                |    24 +
 src/test/in/prime                               |    11 +
 src/test/in/primes                              |     6 +
 src/test/in/print                               |     4 +
 src/test/in/printf                              |    75 +
 src/test/in/program                             |    47 +
 src/test/in/qf                                  |    36 +
 src/test/in/qfb                                 |    20 +
 src/test/in/qfbsolve                            |    22 +
 src/test/in/qfisom                              |    32 +
 src/test/in/quad                                |    11 +
 src/test/in/quadclassunit                       |    23 +
 src/test/in/quadray                             |    19 +
 src/test/in/random                              |    29 +
 src/test/in/ranges                              |    60 +
 src/test/in/real                                |     4 +
 src/test/in/resultant                           |    77 +
 src/test/in/rfrac                               |    46 +
 src/test/in/rnf                                 |   130 +
 src/test/in/rnfkummer                           |    45 +
 src/test/in/round                               |    13 +
 src/test/in/round4                              |   468 +
 src/test/in/select                              |     6 +
 src/test/in/ser                                 |    22 +
 src/test/in/set                                 |    23 +
 src/test/in/size                                |    11 +
 src/test/in/sort                                |    24 +
 src/test/in/sqrtn                               |    53 +
 src/test/in/stark                               |    46 +
 src/test/in/str                                 |     7 +
 src/test/in/subcyclo                            |     2 +
 src/test/in/subfields                           |    34 +
 src/test/in/subgroup                            |    10 +
 src/test/in/subst                               |    24 +
 src/test/in/sumdedekind                         |     4 +
 src/test/in/sumdiv                              |    41 +
 src/test/in/sumformal                           |     7 +
 src/test/in/sumiter                             |    23 +
 src/test/in/thue                                |    28 +
 src/test/in/time                                |     9 +
 src/test/in/trans                               |    68 +
 src/test/in/trans2                              |    53 +
 src/test/in/valuation                           |    26 +
 src/test/in/variable                            |     7 +
 src/test/in/whatnow                             |     5 +
 src/test/in/zetak                               |    28 +
 src/test/in/zn                                  |    50 +
 src/test/in/zncoppersmith                       |    15 +
 src/test/kerntest.c                             |    82 +
 src/test/tune.c                                 |   637 +
 src/whatnow                                     |   653 +
 1521 files changed, 318066 insertions(+)

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..5a21ab2
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,133 @@
+The PARI/GP project started around 1985 in the Laboratoire A2X (Universite
+Bordeaux 1, France)  and a first version was written by  Christian  Batut,
+Dominique Bernardi, Henri Cohen and Michel Olivier and maintained by Henri
+Cohen till 1995. Karim Belabas took over the maintainance between 1995 and
+2001,  the project has been jointly maintained by Bill Allombert and Karim
+Belabas since 2002.
+
+A great  number  of people  have contributed  code through  the successive
+stages which eventually resulted in the present version  of PARI/GP.  Even
+more people, too numerous to list, contributed by testing,  reporting bugs
+or suggesting improvements, Igor Schein foremost among them.
+
+We would like to thank them all here.
+
+Current lead developers:
+
+  Bill ALLOMBERT (Bordeaux)
+  Karim BELABAS (Bordeaux)
+
+Major contributors (large modules written or rewritten from scratch):
+
+  Bill ALLOMBERT: GP2C, the GP parser, extension of the GP language
+    (my, closures), GMP kernel, modular kernel, ffinit, galois* routines,
+    subcyclo, black-box groups, the elldata and galdata package,
+    multithread model, PARI description system, FLTK support, Bug tracking
+    system, administration of pari.math.u-bordeaux.fr, the
+    http://pari.math.u-bordeaux.fr website
+  Christian BATUT: 68k multiprecision kernel, Linear algebra, lattices
+  Karim BELABAS: current project leader, native multiprecision kernel, modular
+    kernel, polynomial arithmetic, polynomial factorization (finite fields,
+    number fields), LLL, primality and compositeness tests, number fields,
+    polgalois and the galdata package, transcendental functions, install(),
+    extension of the GP language (next/break/return, local, op=, default()),
+    gphelp, Configure, readline, man pages, documentation layout, reference card
+    and user's manuals, the FAQ, the http://pari.math.u-bordeaux.fr website
+    the PARI resource pages.
+  Dominique BERNARDI: the original gp interpreter, ECM, elliptic curves
+  Henri COHEN: original designer and project leader, native multiprecision
+    kernel, arithmetic functions, LLL, transcendental functions, number fields,
+    elliptic curves
+  Francisco DIAZ Y DIAZ: number fields (class groups, units)
+  Yves EICHENLAUB: original polgalois implementation
+  Xavier GOURDON: polroots, initial Karatsuba/Toom Cook/fft implementations
+  Louis GRANBOULAN: breakup of monolithic PARI into modules, first GMP
+    kernel, first Configure
+  Loic GRENIE: bnfinit() rewrite, openMP / MPI implementations
+  Bruno HAIBLE: micro assembly implementations, DOS/Windows support
+  Guillaume HANROT: thue, zncoppersmith
+  Pascal LETARD: basic number field (round2, initial round4)
+  Jean-Francois MESTRE: elliptic curves
+  Gerhard NIKLASCH: binary powering, integer extended gcd and rational
+    reconstruction, primality and compositeness test, integer factorization,
+    documentation layout, AIX and Solaris support, first PARI web site.
+  Michel OLIVIER: 68k multiprecision kernel, number fields, original polgalois
+    and nfsubfields implementation
+  Thomas PAPANIKOlAOU: MPQS integration, Pari-Lidia project
+  Xavier ROBLOT: MPQS integration, Stark, original nffactor
+  Denis SIMON: Norm equations, S-units, Hilbert symbols
+  Emmanuel TOLLIS: primedec, zetak
+  Ilya ZAKHAREVITCH: prime sieves, gphelp, tex2mail, major cleanup of the
+    graphics and GP parser code, gnuplot support, readline support,
+    OS/2 support, DOS/EMX support
+
+Other contributors: the 2 or 3 letter code refer to the CHANGES file. Please,
+kindly remind us if you have been forgotten!
+
+   = Karim Belabas (maintainer)
+AF = Aurimas Fiseras
+AM = Alex V. Myltsev
+AMe= Anton Mellit
+AW = Aleksander Wittlin
+AS = Andy Stubbs
+BA = Bill Allombert
+BD = Bill Daly
+BG = Brian Gladman
+BH = Bruno Haible
+BK = Bruce Kaskel
+CB = Cliff Bergman
+CG = Charles Greathouse
+CW = Carl Witty
+DB = Dominique Bernardi
+DCa= David Carlisle
+DC = Dan Christensen
+DE = Denis Excoffier
+DF = David Ford
+DS = Denis Simon
+EP = Esa Peuha
+GH = Guillaume Hanrot
+GN = Gerhard Niklasch
+GT = Glenn Thobe
+GTo= Gonzalo Tornaria
+HC = Henri Cohen
+HR = Harvey Rose
+IK = Iwao Kimura
+IM = Ivan Middleton
+IS = Igor Schein
+ISo= Ignat Soroko
+IZ = Ilya Zakharevich
+JD = Jeroen Demeyer
+JJ = John Jones
+JM = Jerome Milan
+JS = Juhana Sadeharju
+KO = Kiyoshi Ohgishi
+KPN= Klaus-Peter Nischke
+LG = Louis Granboulan
+LGr= Loic Grenie
+LM = Lorenz Minder
+MA = Michael Abshoff
+MD = Mark Dickinson
+MH = Marije Huizing
+MS = Michael Stoll
+MSo= Michael Somos
+MW = Mark Watkins
+NS = Nils Skoruppa
+OR = Olivier Ramare
+OV = Oliver Voigt
+PB = Peter Bruin
+PC = Phil Carmody
+PM = Peter Montgomery
+PMo= Pascal Molin
+PW = Paul van Wamelen
+RB = Remi Butel
+RM = Richard J. Mathar
+RS = Ralf Stephan
+RR = Randall Rathbun
+SC = Sylvain Chevillard
+SG = Scott Garee
+TH = Teluhiko Hilano
+TP = Thomas Papanikolaou
+VB = Vasili Burdo
+VL = Vincent Lefevre
+XR = Xavier Roblot
+YU = Yoshiaki Uchikawa
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..086ccdb
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,58 @@
+# $Id$
+Bug numbers refer to the BTS at http://pari.math.u-bordeaux.fr/Bugs/
+
+Done for version 2.7.2 (released 19/09/2014):
+[last column crossreferences current development release 2.8.0]
+
+  Fixed
+    1- gaffsg(0, t_PADIC): wrong valuation                                [F21]
+    2- (t_INTMOD with word-sized modulus)^(huge negative power) [#1584]   [F24]
+    3- (gp -p N) or (primelimit=N in gprc_ for N >= 436273290 resulted in an
+       incorrect primetable. N.B. Such commands are now useless: needed primes
+       are produced dynamically anyway.                                   [F25]
+    4- monomial(exact zero, d, v) returned an invalid t_POL / t_RFRAC     [F26]
+    5- contfracpnqn(v, n) returned partial quotients p[-1]/q[-1] ...
+       p[n-1]/q[n-1], instead of the documented p[0]/q[0] ... p[n]/q[n]   [F27]
+    6- factor((3+4*I)/25) -> factor 2+I had 0 exponent [#1586]            [F29]
+BA  7- iferr() could crash if some component of the t_ERROR were clones.  [F31]
+    8- nffactor() could overflow the stack when default accuracy too low  [F32]
+BA  9- obsolete use of E=[a1,a2,a3,a4,a6] in ellmul crashed  [#1589]      [F33]
+   10- incorrect rounding in mulrr/divrr for one-word precision reals     [F34]
+BA 11- multiif did not handle correctly return() in conditions [#1590]    [F35]
+   12- [0..5] -> [0,0,0,0,0] on some architectures                        [F36]
+   13- is_gener_Fp could return wrong results                             [F37]
+   14- Fq_sqrtn(t_INT,..,&zeta) could return a wrong root of 1            [F38]
+   15- bnfinit: SEGV due to precision issues [#1592]                      [F39]
+   16- zm_zc_mul only worked for square zm matrices                       [F40]
+   17- genus2red(0,27*x^5+97*x^4+118*x^3+60*x^2+13*x+1,3) -> bug [#1596]  [F41]
+   18- [gphelp] oo loop when $COLUMNS too small [#1594]                   [F42]
+   19- genus2red(x,-x^6-3*x^4-10*x^2-1,3) -> impossible inverse [#1597]   [F43]
+   20- factoru(1) returned a t_MAT instead of the expected "matsmall"     [F44]
+   21- FpM_charpoly wrong in small characteristic [#1602]                 [F45]
+   22- when compatible = 3; series() used a random precision              [F50]
+   23- genus2red(0,6*x^6+5*x^4+x^2+1,7) -> impossible inverse [#1597]     [F51]
+   24- isprime() could crash on large input [#1604]                       [F52]
+   25- genus2red(x^3+1,1) -> type error [#1597]                           [F53]
+   26- gphelp did not handle === correctly [#1603]                        [F54]
+   27- FpXY_evaly() wrong when evaluating at 0                            [F56]
+   28- [mingw] gp could crash at start up [#1607]                         [F57]
+
+Done for version 2.7.1 (released 16/05/2014):
+[last column crossreferences current development release 2.8.0]
+
+  Fixed
+    1- make install fails on OS/X: ln -s libpari.dylib libpari.dylib fails [F1]
+    2- Q_pvalrem(t_FRAC) => wrong result                                   [F2]
+    3- [] == 0 but []~ != 0  (now []~ == 0 as well) [#1560]                [F3]
+BA  4- test-kernel did not work when using --mt=pthread                    [F4]
+BA  5- ellheegner was using too much memory in some case                   [F5]
+BA  6- ellap can overflow on 32-bit machine [#1558] (minimal fix)          [F6]
+    7- nfhilbert(K,x,y, P above 2) could give wrong results [#1561]        [F7]
+    8- gcd(1/2, 1+I*1.) -> SEGV [#1563]                                   [F10]
+    9- mathnf(t_VEC) could corrupt input (change sign)                    [F11]
+   10- [libpari] RgM_transmul did not work                                [F12]
+   11- [libpari] Fq_issquare didn't support T=NULL                        [F13]
+   12- [libpari] nfpow_u didn't handle non-integral rational numbers      [F14]
+   13- eint1(0) -> stack overflow [#1568]                                 [F15]
+   14- nfroots(, t_POL with leading coeff -1) could miss solutions        [F19]
+   15- precprime(1) -> invalid t_INT [#1576]                              [F20]
diff --git a/CHANGES-2.2 b/CHANGES-2.2
new file mode 100644
index 0000000..b3e4fb2
--- /dev/null
+++ b/CHANGES-2.2
@@ -0,0 +1,3994 @@
+# $Id$
+Bug numbers refer to the BTS at http://pari.math.u-bordeaux.fr/Bugs/
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.3.0  (released 19/05/2006):
+  Fixed
+    1- ispower(HUGE t_INT, n) could give wrong results (rounding errors)
+    2- ellheightoo (internal) and RgXQ_norm (public) not declared
+BA  3- [m68k kernel] didn't compile + need -fPIC
+    4- libpari.so: soname was incorrect
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.13 (released 26/04/2006):
+  Fixed
+BA  1-[HPUX] 'program' bench failed for gp-sta [install did not work]
+    2- made BITS_IN_LONG and related constants 'signed long' [ avoid
+       problems like : exp(10^10) --> 10^10 + 1 (silent overflow) ]
+    3- --kernel=none : libpari ended up not containing the 'divll' symbol
+    4- polgalois(x^3-2) --> [,,,"A3"] when new_galois_format was unset.
+    5- problem with negative valuations in ggcd(t_PADIC) [#411]
+    6- Fp_pow for huge moduli could return a negative number [#417]
+    7- y;x/y/z/x --> error [#410]
+    8- polredabs(x^4-35048*x^2+1016392*x-7368842) --> same, instead of 
+       x^4 - 17524*x^2 + 69403802. Rounding error, due to setting the
+       relative precision wrt the wrong reference object.
+    9- nfisideal() could raise an error instead of returning 0
+   10- nfreducemodpr only accepted nf's (not bnf's or richer)
+   11- 1/a/x --> 1/a/x; 1/(a*x) --> 1/(a*x). Now 1/(a*x) in both cases
+       (unify t_RFRAC normalization routines)
+   12- content(1.*a*x) --> 1 [ now returns a ]
+   13- content(1./a*x) --> 1.00000/a, but content(1./a*x + 1./a) --> 1/a
+       [ now returns 1/a ]
+   14- t_SER ^ t_FRAC introduced floating point numbers even when
+       rational expression available, e.g (8 + x)^(1/3)
+   15- round(t_POL/t_SER, &e) with leading coeff rounded to zero
+   16- trace(Mod(y,x)) --> SEGV
+   17- gcd(1, 1/x) --> 1 [ gcd(Pol(1),1/x) correctly returns 1/x ]
+       content(x/y + 1) --> 1 [ content(x/y) correctly returns 1/y ]
+       content(x + 1/y) --> 1 [ also wrong in pari-2.1.7 ]
+       content(1/x/y) --> 1  [ should be 1/y ]
+   18- missing vectors in qfminim(x,b,,2) [ m omitted ]
+   19- 'make test-kernel' [ wouldn't compile ]
+   20- matsnf(matrix with t_POL entries, 1) ---> incorrect matrix V
+   21- O((-2)^3) --> invalid object
+   22- (0.*x)*(0.*x) --> 0.E-57 [ instead of 0.E-57*x^2 ]
+   23- qfminim(x,,m,2) [b omitted] -> [n,B,v]: output correct vectors (v),
+       but reported too many (n too large). 
+   24- qfminim(x,,,2) wrong when x has huge entries. Work out a sensible
+       default precision if x has exact entries.
+BA 25- HPPA 32bit level0 inline assembly addmul constraint was too weak.
+   26- factorback(x,y) --> error if x,y were valid t_VEC with exactly 6
+       components [ typo in checknf_i ]
+   27- [configure] shared libraries on 64bit sparc require -fPIC / -KPIC
+   28- (x^3/y^3)^(1/3) -> x + O(x^17)  [ leading coeff sometimes lost #433 ]
+   29- ??INT worked, but not ??t_INT
+   30- inconsistencies wrt variables in t_POLMOD, e.g
+       Mod(x*a,x^2)' ---> Mod(x, x^2)
+       deriv(Mod(x*a,x^2)) ---> 0
+   31- contfrac(sqrt(19),2^31-1) --> overflow [ signed overflow can't
+       be reliably tested, use unsigned computations ]
+BA 32- on x86_64 + gcc-4.0, CFLAGS was missing a -fno-gcse-after-reload
+   33- [output=3] give more time to external prettyprinter [#209]
+   34- rare SEGV in factor(t_INT) with low stack space [ #345 ]
+   35- ispower(1) --> error, ispower(-8) --> error [ allow negative numbers ]
+   36- Configure -a + kernel = $arch-gmp didn't work [missing -lgmp #438]
+   37- divisors([]) --> SEGV [#441]
+   38- missing GC in bernfrac
+   39- bnrrootnumber(bnrinit(bnfinit(x),1),[]) --> SEGV [#443]
+   40- f()= local(m = matrix(2,2)); m[1,1] = 1  f() --> m[1,2] also set to 1
+   41- make test-kernel would always fail with gmp kernel.
+   42- elllseries(e, 0.) --> error [ only elllseries(e,0) worked ] [#445]
+   43- 'ftime' was never detected by Configure
+   44- setrand(74);quadclassunit(-83138791008,,[0.2,6]) --> oo loop
+       [ large prime relation hashtable corrupted when changing subfactorbase ]
+   45- a(k)=if(k==0,0,a(k)=a(k-1))
+       a(1) --> SEGV [#447] (add refcounts to 'user function' structs)
+   46- getheap() did not report properly the "size" of user functions
+       (value too small)
+   47- add compatibility macro decomp -> Z_factor [ used by mwrank ]
+BA 48- minpoly(,,v) might return polynomials in x instead of v.
+   49- make test-kernel required inlining compiler
+   50- make test-kernel didn't work with C++ compilers
+   51- ia64 kernel assumed 64bit longs, whereas compilers can be
+       configured for 32bit
+
+  Changed
+    1- RgX_simple_gcd: make sure result has non-zero leading term [#412]
+    2- simplify(t_RFRAC): remove assumption that deg(denom) > 0 [#413]
+    3- split bfffo.h from level0.h
+    4- semantic of t_SER with inexact coefficients is now the same as
+       for t_POL: the sign is 0 iff all coefficients are zero. Either
+       there are no coefficients, or the leading coefficient is an
+       inexact zero.
+    5- removed all non-inline assembler kernels : they were complicated to
+       Configure, mostly untested, and inefficient ( function call overhead + 
+       must use global variable hiremainder/overlow when operating on limbs
+       instead of LOCAL_HIREMAINDER trick ). If this slows down your
+       application (it should not), install gcc or use the gmp kernel.
+    6- allow arbitrary n in divisors(n) and fordiv(n,), provided factor(n)
+       succeeds [ was restricted to t_INT or their factorization matrix ]
+    7- made LLL the default algorithm in algdep / lindep again [ replaces PSLQ ]
+       Our PSLQ implementation is slow and unstable, and LLL performs much
+       better, see the example in ??algdep.
+    8- addrr: extend accuracy much less frequently
+    9- make sure all kernel symbols are present in all versions of libpari
+       (addll & friends could be inline)
+   10- change the meaning of gcd(x) and lcm(x) when x has vector/matrix
+       components (a global recursive gcd/lcm is taken, instead of a
+       cartesian product of individual gcd/lcm)
+   11- disallow vecmin([]), vecmax([]) [ returning stoi(+/- BIGINT) is not
+       helpful ]
+   12- [libpari] renamed lellseries -> elllseries [ as in GP ]
+   13- semantics of stackdummy() [ make it consistent with gerepile ]
+   14- never assume that part of an object is "permanent" when it is out
+       of the stack (was used by INTMOD/POLMOD/PADIC). Always copy it.
+       As a result, 'gmodulo' and 'forcecopy' become obsolete.
+   15- use quadclassunit in qfbhclassno for large D, thereby ASSUMING GRH.
+   16- ellan was bypassing the check for CM
+   17- uniformize the generation of parilvl0.h [ genkernel ]
+
+  Added
+    1- [library] new function isinexact.
+BA  2- [elldata] function forell() to loop over elliptic curves.
+BA  3- [elldata] function ellconvertname() to parse curve name.
+    4- [library] new function RgX_shift, RgX_mulXn, RgX_shift_shallow, 
+       RgX_Rg_div
+BA  5- m68k level0 inline assembly kernel
+    6- [library] new function isint, issmall, mkrfrac
+    7- Configure --time=timing_fun
+    8- [Configure] genkernel script
+
+  Removed
+    1- kernel functions shiftl / shiftlr (inefficient, unused, untested)
+       [ backward compatibility version in src/kernel/level1.h ]
+    2- obsolete functions forcecopy (use gcopy) and gmodulcp (use gmodulo).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.12 (released 24/01/2006):
+  Fixed
+    1- incorrect detection of EditLine library
+    2- issquare(Mod(2,9)) --> 1  [ should be 0 ]. Implicitly assumed
+       v_p(N) odd for all primes divising the modulus N.
+BA  3- poltschirnhaus(ffinit(2,4)) could return 0.
+BA  4- Configure -a did not propose fltk and Qt as graphic engine.
+    5- g()=local(x;f) -- oo-loop [#352]
+    6- charpoly( Mod(a, b) ) --> wrong result when 'a' is not a
+       t_POL or t_INT.
+    7- \p2051 log(exp(-1)) --> printing bug [#357]
+    8- bad argument checks in qfrep() / qfminim() [#359]
+    9- fix mkintn to work as documented (even when leading word is 0) 
+BA 10- [Linux] gp only accepts one SIGINT in batch mode [#251, #370]
+       (initial patch JD).
+   11- contfrac(5/3.) was wrong: [1,2] instead of [1,1,2]. For a t_REAL x,
+       when the last partial quotients of a lower and upper bound differ 
+       by 1, choose the one associated to the digits of x (followed by 
+       infinitely many 0s) unless it was too small and the next partial
+       quotient was 1. [#371]
+   12- bnfisintnorm: results could have a wrong sign [#372]
+   13- isprime(N,2): oo loop if N is a perfect square
+   14- \p n was limited to a value ~ 10 times smaller than the actual limit
+BA 15- qfbsolve fix 2.2.11-F86 was not sufficient.
+   16- (1 + O(2))^2 was less precise than sqr(1+O(2))
+   17- in the extended help pager, typing ' ' then \n would skip
+       one page [#375]
+BA 18- config/has_stat.c didn't work with g++
+   19- charpoly/minpol(Mod(a,T)) was only monic if T was.
+BA 20- 'make install' did not work if 'prefix' contained spaces
+   21- has_dlopen always compiled in on Mac OS X, even if present in libc
+   22- unsafe handling of t_QFI/t_QFR (setsigne) [#384]
+   23- idealaddtoone didn't handle zero ideals [#386]
+   24- workaround a bug in g++ version 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)
+       infinite loop on factor(41093858855767145965571)
+   25- constlog2() was not restartable [ no way to set glog2 = NULL ]
+   26- sloppy arg check in ellchangecurve [#388]
+   27- [internal] gprec_w reduced the accuracy of real 0s [#396]
+   28- wrong arg check in matsolve [#400]
+   29- minpoly([;]) --> SEGV
+   30- y*x^3/( 1+x+x^2 +O(y)) -> gerepile error [#403]
+   31- poldivrem didn't handle properly multivariate polynomials [#402]
+   32- insufficient accuracy in ellheight --> singular curve, SEGV [#404]
+   33- [MacOS X + fltk] installed gp binary couldn't use hi-res graphics
+       (resource fork not copied)
+IZ 34- [2.2.9-A20] -fno-strict-aliasing not supported in older gcc
+   35- heapsize reported by getheap() were wrong (overestimate).
+   36- gerepile error in nfgaloisapply [#408]
+   37- wrong results in bessel functions at non-integer indices
+       (typo in isint(t_REAL) ) [#409] 
+   38- vector(3,n,n*=10) -> [10, 100, 1000] ( now [10, 20, 30] )
+   39- bnf.codiff didn't work
+   40- content([[2]]) --> error
+
+  Changed
+    1- speed up prime() by using checkpoints.
+    2- allow lists with vector entries in listsort (use lexsort)
+    3- allow vector of points in ellisoncurve()
+    4- tunings in quadclassunit (#298 / #355)
+    5- [Configure:] take CPPFLAGS into account
+    6- 'int' C type is now reserved for -1/0/1 values. Use 'long' otherwise.
+    7- Fl_inv and Fl_div now raise an error when divisor not invertible
+       [ used to return 0 ]
+    8- removed the threshold that prevented exponent overflow in
+       exp(-10^10) and returnd 0. instead [ either return correct digits
+       or raise an exception ].
+VL  9- honor C_INCLUDE_PATH in Configure (for get_readline)
+   10- [readline] remove old hack bypassing a bug in readline-2.0 [ did not
+       release SIGINT ]: useless nowadays, and used deprecated functions.
+   11- export combine_factors [ needed by giac ]
+   12- do not alias labs to abs in paricom.h
+   13- randomize polredabs() to improve bound on difficult fields, e.g.
+       f(k) = {
+         p = polcompositum(x^6-3*k^3,x^6-3*k^3)[4];
+         polredabs(poltschirnhaus(poltschirnhaus(p)),16)
+       }
+       for k = 2, 5, 6, 8, ...
+   14- try more names in 'pari_unique_filename' (26^2 instead of 26))
+   15- remove GCC_INLINE from CFLAGS. Define DISABLE_INLINE or
+       DISABLE_VOLATILE to prevent the compiler from inlining or using    
+       the 'volatile' keyword
+   16- let paripriv.h include parinf.h
+   17- rename 'prec' -> 'precreal' [ from paripriv.h ]
+BA 18- define INLINE to 'inline static' for C++ compilers also
+   19- rename polx -> pol_x, polun -> pol_1
+   20- make hell, hell2 static to elliptic.c
+   21- rename decomp -> Z_factor, decomp_limit -> Z_factor_limit
+   22- rename wf -> weberf, wf1 -> weberf1, wf2 -> weberf2
+   23- make incpos / incneg static
+   24- export setseriesprecision, setrealprecision
+BA 25- make pari restartable
+   26- [initialization of GP hashtables] remove the 'module' structure,
+       simplify pari_addfunctions, plug helpmessages in initializing
+       entree arrays.
+   27- rename err -> pari_err, pariputsf -> pariprintf
+   28- rename rnfhermitebasis -> rnfhnfbasis
+   29- forbid t_STR, t_VECSMALL and t_LIST in gvar.
+   30- cleanup mpqs use of temporary files
+   31- pari_unique_filename now MT-safe, returns a malloc'ed buffer.
+   32- default() now always returns the value of the (possibly changed) default.
+       No need for a flag anymore. The construction default(def,,1) is still
+       valid but deprecated (the flag is ignored).
+   33- increase default accuracy in ellinit() when curve has large coefficients
+       [ avoid large relative error on periods, #404 ]
+   34- qfminim(,,,0) often gives wrong results for matrices with large
+       entries. Test whether rounding errors occur and abort if so [#407]
+   35- qfminim(x,b,m,flag): b and m are now optional. Allow omitting b
+       (formerly b=0) if flag = 2 also.
+   36- eval("1a") is now '1' again (was "a" since 2.2.10)
+
+  Removed
+BA  1- gnuplot graphic engine (complicated, did not work from gnuplot-4 on)
+    2- has_sigrelse.c & has_sigsetmask.c [ deprecated, unused after C-10 ]
+    3- src/desc/Makefile
+    4- log2old ( = 2atanh(1/3) )
+    5- src/desc/gen_help
+BA  6- obsolete macros INIT_JMP/INIT_SIG
+
+  Added
+    1- library function ellisoncurve()
+BA  2- library functions related to sorting: gen_sort_aux, gen_search_aux,
+       vecsmall_indexsort
+    3- functions strtoi, strtor
+BA  4- function minpoly()
+    5- function zeromatcopy()
+BA  6- support for GNU/kFreeBSD and other GNU userlands.
+    7- document ZY_ZXY_rnfequation and ZY_ZXY_resultant
+    8- function pari_warn() use instead of pari_err() for warnings
+    9- function pari_add_function, pari_add_module
+   10- doc/develop.tex
+   11- library function uisprime, uissquarerem, Z_issquare, Z_issquarerem
+   12- library function pari_unique_dir
+   13- library function gp_default
+   14- library function rowcopy (GP2C)
+   15- library function traverseheap
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.11 (released 19/10/2005):
+  Fixed
+    1- a()=b(1,) --> error message
+    2- agm(-1.0000000004656,1) --> oo loop [Bug #214]
+    3- sdivss_rem(x,y,&rem) did not set rem, contrary to the documentation
+BA  4- ellzeta(ellinit([0,0,0,0,1]),1) --> gerepile error
+    5- random() was less random on 64 bit machines
+    6- typing 0.e-28 returned 0.E-38 (!)
+    7- ellminimalmodel(e).a2 was not always in {-1,0,1}
+    8- check arguments in elllocalred
+BA  9- [GP2C] type-checking codes generated for some types was wrong.
+   10- stray output: (a()= for(i=1,1, return);) a() --> 0
+   11- qfminim & qfrep didn't check their arguments
+   12- [library:] pariputsf() and GP print() used different output formats
+   13- nfdisc(x^4 - 2947*x^2 + 1553545) --> SEGV  [ incorrect test for gcd
+       in Round 4: gcmp1(d) --> degpol(d) == 0 ]
+   14- forvec(v=vector(4,k,[1,4]),,2) -> oo loop [bad initialization]
+   15- eta(x << 1) used far too much accuracy in intermediate computations
+GN 16- MPQS warning "factoring this number will take several/many hours" now
+       assume more current hardware.
+   17- off-by-1 error in initprimes0 allocation [Bug #237]
+       -> SEGV in default(primelimit,155100 + 41*7) 
+   18-[Cygwin] incorrect links created by 'make install'
+   19- a( <Return> --> oo loop in parser [Bug #240]
+   20- at \p366, the last digits of Pi were wrong [ not enough iterations, from
+       2.2.9, A-2]. [Bug #238]
+GTo21- wrong prototype for qfrep
+   22- factornf(P, T) did not accept non-monic T [Bug #241]
+   23- lngamma(1E+10) --> "Impossible assignment I --> S"
+   24- "couldn't deal with this field" errors in bnfinit/quadclassunit for
+       tiny discriminants, huge Bach constants (e.g. 12), and unlucky random
+       seeds. Remove arbitrary limits and let the loop run to completion.
+   25- neither freadseq() nor readGEN() were able to parse '{' / '}' [Bug #216]
+   26- accuracy problem in polroots (--> division by zero) [Bug #252]
+   27- acos(x < 0) --> result off by Pi [ introduced in 2.2.9-A3 ]
+   28- ellrootno(e,p) incorrect in the case of good reduction [#262]
+   29- (rare) "impossible assignment I-->I" in contfrac
+   30- typo in qfbclassno's hash function (when D < 0, function can't handle 0).
+       qfbclassno(-948) --> division by 0 [GMP kernel]
+   31- [Makefiles:] the behaviour of '//' as path leader is undefined. Make
+       sure path prefixes are not '/' [ e.g $prefix/lib --> //lib ]
+   32- pollead(u*v + (v+1)/v, v) --> 'x' [ should raise an error ]
+GN 33- problems in MPQS : some factors missed e.g factor(2^263-9) -->
+       expensive extra runs ( + possible stack corruptions when many factors
+       found simultaneously ).
+   34- tanh(x) was actually computing tanh(|x|) for real x ([#269] from 2.2.8)
+   35- [MacOS X] dlopen is now part of the system library. Don't redefine
+       dlopen in darwin.c if HAS_DLOPEN is defined.
+   36- fix Trager's trick in factorff (from 2.2.10)
+   37- rnfisnorminit(quadpoly(145,y),quadray(145,1)) --> stack overflow [#273]
+   38- matker([1,2,3;4,5,6]*1.) --> [;]  (missing 1 vector)
+   39- rnfconductor(bnfinit(y),x,1) --> error [#277]
+   40- gcc-4.0 miscompiles PARI on ix86 [#274]
+       ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23453 )
+   41- truncate(1/4/x+1+O(x)) -> (x + 1/4)/x instead of (4*x + 1)/(4*x) [#276]
+   42- bitneg(2^32-1,32) -> corrupt integer [#279]
+   43- typo in rnf_is_abelian [#277]
+GN 44- several minor fixes in mpqs.c
+BA 45- ellrootno(e,2) was wrong for curves of Kodaira type 3 and -7. 
+                                                           ([#212] from 2.2.9)
+BA 46- galoisfixedfield() was not robust enough ([#256, #228 from 2.2.9)
+   47- tanh(-10^20) --> error instead of -1  [tanh(10^20) was treated properly]
+BA 48- wrong rounding in wr_float: with default(format,"f0.10")
+        1.23456789049999999997 -> 1.234567891
+GH 49- typo in thue.c:MiddleSols() --> missing solutions [#264]
+   50- default(prettyprinter,,1) --> t_POL instead of t_STR [#296]
+BA 51- truncate(1/x+O(x)) --> SEGV
+   52- overflow in Flm_gauss (when solving triangular system) --> wrong result
+       [#284]
+   53- isprime(25, 2)
+         *** impossible inverse modulo: Mod(5, 25).
+       although it's useful to get a factor, it's not nice to raise an error,
+       since 'trap' then becomes mandatory. Removed this feature: the factor
+       is silently discarded (exceedingly rare anyway).
+   54- (-1)^(any integer >= 2^31) --> -1 [ from 2.2.9 ]
+GH 55- zncoppersmith(x,1002,1001) --> SEGV [#221]
+   56- subst(1+O(x),x,x^2+O(x^3)) --> SEGV [#287] (patch by MSo)
+       subst(1+O(x^2),x, x^2+O(x^3)) --> 1 + O(x^3)  [ should be O(x^4) ]
+   57- component() didn't work on lists [ SEGV ]
+   58- quadclassunit(D,, [c0 > 6]) --> weird error message
+   59- weird errors in bnrstark [#285]
+   60- (x^0)^2^31 --> length overflow
+   61- polroots(pollegendre(51),1) --> SEGV [#293]
+   62- print({}) --> oo loop
+BA 63- when choosing between (p-1) and APRCL, isprime() required a complete
+       factorization of p-1, whereas accumuluting factors up to sqrt(p-1) was
+       enough. (e.g 2^127 - 1)
+BA 64- function name not always properly reported on interrupt (e.g if, while)
+   65- content([-1]) --> -1 
+   66- memory leaks in quadclassunit (#318)
+BA 67- [Configure] check that 'readline' is not an EditLine wrapper
+       [ ==> compilation failure otherwise ]
+   68- Str() --> junk string (uninitialized data) (#326)
+   69- arg(I*O(7^5)+1) --> junk, exp(3*I + O(3^5)) --> junk. (#328)
+   70- powiu(2, ulong n) called int2n(long n), incorrect for huge n (#331)
+   71- Qfb(8,-4,1)*Qfb(5,-4,1) --> Floating Point Exception (BIB, #332)
+   72- t_QRF distance component was evaluated using an unstable algorithm,
+       possibly leading to catastrophic cancellation (#333)
+   73- dbltor did not recognize Infinity, NaN, or unnormalized numbers.
+   74- makerfrac(1,quadgen(24)*x^0)%b --> SEGV, where
+       GEN makerfrac(GEN p,GEN q) {
+          GEN z=cgetg(3,t_RFRAC);
+          z[1]=lcopy(p);
+          z[2]=lcopy(q); return z;
+        }
+BA 75- exp(x) gave imprecise results at huge accuracies for expo(x) >> 1.
+   76- valuation(x,p) using a divide & conquer algorithm would kill the
+       session if p = 1
+   77- (2 + O(2^8))^(-1/3) --> SEGV [#341]
+   78- exp(0.E100) --> invalid GEN
+   79- stack corruption in gsubst [#343]
+   80- Pol([Mat(0.1)],x)^3. --> crash [#346]
+   81- besseljh(0,2^65) --> crash [#340]
+BA 82- [plotQt:] ^C while hi-res window is present would crash the session
+   83- Strtex(Vecsmall) --> SEGV [#348]
+   84- oo loop in zeta() [catastrophic cancellation in get_xinf() ]
+   85- oo loop in nfdisc() [#350]
+   86- Mod(1,2)*x / (1/2) --> incorrect t_POL
+   87- qfbsolve forgot some solutions: qfbsolve(Qfb(2,1,3),3) --> 0
+   88- multiplying ideals could lead to factoring big integers (failsafe
+       algorithm in idealtwoelt, when random trials don't work): fix
+       idealtwoelt.
+
+  Changed
+BA  1- FpM_FpV_mul is renamed to FpM_FpC_mul, Flm_Flv_mul to Flm_Flc_mul and
+       FlxV_to_ZXC to FlxC_to_ZXC, Flv_to_ZC to Flc_to_ZC, FqV_to_FlxC to
+       FqC_to_FlxC, zv_to_ZC to zc_to_ZC
+BA  2- FpXV_FpV_innerprod renamed to FpXV_FpC_mul and FlxV_Flv_innerprod to
+       FlxV_Flc_mul
+    3- retain 'CHANGES' revision after 'make distrib'
+GN  4- extensive re-tuning in MPQS (all sizes) and avoid "sizing marginal,
+       index1 too large" warning
+    5- rename globalreduction -> ellglobalred, localreduction -> elllocalred
+    6- taniyama(e) is deprecated. Use elltaniyama(e, prec) instead of
+       old = precdl; precdl = prec; x = taniyama(e); precdl = old;
+    7- [bnfinit] retune compute_R to try and detect cheating with Bach's
+       constant.
+    8- rename lisexpr -> readexp, lisseq -> readseq, flisexpr -> freadexpr,
+       flisseq -> freadseq
+    9- rename Fp_gener -> gener_Fp, Fl_gener -> gener_Fl
+   10- change TeX output so that it's easier to handle:
+       - replace \over by \frac [ most people use AMS styles which forbids
+         \over, not plain TeX, which doesn't have \frac ]
+       - remove many extra braces { }  [ would prevent line breaks ]
+       - insert carriage returns
+   11- [GP handling of print1()] get rid of 'added_newline' hack, replace by
+       monitoring in pariputc/pariputs.[#243]
+   12- do not install libpari.a.xxx [ include files are not versionned either
+       and libpari.a is useless without the correct ones, contrary to .so ]
+   13- rename gtrans_i -> shallowtrans, concatsp -> shallowconcat.
+   14- add GC in parsing loop for huge input vectors [ needs roughly 2 or 3
+       times less memory ]
+   15- implement pari_is_dir() using stat() instead of opendir()
+   16- moved defaults from gp/gp.c to language/default.c [ -> into libpari ]
+   17- moved input_loop to language/es.c [-> into libpari ]
+   18- when called as 'gp --test', disregard actual terminal dimensions
+   19- allow ellinit over any ring (support only basic operations)
+   20- allow ellchangecurve on e = [a1,a2,a3,a4,a6].
+   21- typecast cleanup: move most of paricast.h to pariold.h
+   22- rename readGEN -> gp_read_stream, freadseq -> gp_read_str.
+       freadexpr/readexpr are deprecated, use gp_read_str/readseq
+   23- make sure GP_DATA is always defined --> default() becomes available
+       in libpari
+   24- make gnil public
+   25- change content() so that it always returns the gcd of all entries (as
+       documented). Used to return the gcd of all _contents_ of the entries
+       of a t_VEC/t_COL/t_MAT.
+   26- backport into quadclassunit some of the bnfinit improvements
+       [specifically, change subfactorbase, #298]
+BA 27- rewrite rectdraw in an object-oriented way [ reduce code duplication
+       in hi-res plot routines ]
+BA 28- change the semantic of includedir to not include the pari suffix.
+       Instead /pari is automatically added by make install.
+   29- improve plindep (use lllintpartial and floating point LLL)
+      + allow lindep with p-adic entries
+BA 30- allow relative paths in Configure --with-xxx directives
+BA 31- allow sinh, cosh, tanh with t_PADIC arguments
+XR 32- bnrstark(): try harder to find solutions [increase precision faster #255]
+BA 33- rename coefs_to_xxx() to mkxxxn() for xxx in {vec,col,pol,int}.
+   34- semantics of gcd with inexact zeros: now gcd(0., a) is 1 [used to be a
+       and led to problems: e.g 0./(1.*a)/(1.*a) not simplified ]
+   35- warn if 'perl' is found but doesn't seem to work
+   36- forbid the f'(x) construction when x is not a constant type [#327]
+
+  Added
+BA  1- polgalois(): 4th component: transitive group name following GAP4
+BA  2- new functions ZX_add, ZX_sub, ZX_neg, ZX_Z_add, ZX_Z_mul
+BA  3- new functions FpC_to_mod, FqV_to_FlxV, FpC_red
+    4- member functions r1, r2
+    5- functions factoru(), factoru_pow(), powiu(), powuu().
+    6- symbolic link $MANDIR/gp-$version.1 -> $MANDIR/gp.1
+    7- new functions shallowcopy, shallowconcat, shallowtrans, vecslice,
+       vecpermute, vecslicepermute, rowslice, rowpermute, rowslicepermute
+BA  8- support for gp2c-run on cygwin
+BA  9- new optional package 'elldata' and new GP functions ellsearch and
+       ellidentify to access it.
+BA 10- support for ellinit("<curve name>") through elldata.
+   11- new functions vec_ei / col_ei
+BA 12- new function  FpC_Fp_mul, FpV_FpC_mul
+BA 13- new functions const_col, const_vec, vec_is1to1, vec_isconst
+   14- new function gp_read_file
+BA 15- new function substvec
+BA 16- new divide_conquer_assoc (make divide_conquer_prod thread-safe)
+   17- p-adic zeta, cos, sin, tan, cotan
+BA 18- p-adic gamma
+BA 19- new routine readvec()
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.10 (released 17/04/2005):
+  Fixed
+BA  1- gp2c description of == operator was broken.
+    2- asin(I) --> SEGV, acos(1 + 0. * I) --> SEGV, atan(I/2) --> SEGV
+       sqrt(0*I),acosh(1+0*I) --> oo loop
+    3- make test-kernel didn't work [missing gen_m1 & int2n]
+    4- thetanullk(1/2,1) --> SEGV [from 2.2.9]
+    5- unit component in factor(t_INT + 0*I) was wrong
+    6- no typecheck for qfbhclassno input
+    7- diviuexact destroyed input and made further assumptions [=> wrong results
+       in ellglobalred]. Replaced it by a clean wrapper.
+GH  8- thue(thueinit(x^3 - 2), 2) --> SEGV
+    9- reorder([1]) --> SEGV (bad input, but sanity check was wrong)
+   10- rnfconductor(,1) [ = rnf_is_abelian() ] didn't accept polynomials with
+       POLMOD coeffs + used a wrong algorithm [ not modular + wrong result ]
+   11- broken code in hnfmodid() wrt GC (would add one 0 column, Bug #129)
+   12- sqrti(gen_0) --> SEGV [native kernel]
+BA 13- polrootsmod(,,1) could return a line vector instead of a column.
+   14- issquare(Pol(1)) --> SEGV
+BA 15- polsubcyclo() used too much memory for large conductors.
+   16- typo in quadtoc() --> quadgen(odd t_INT)*1. was wrong
+   17- subgrouplist([], [2]) --> [[;]]  (instead of [])
+   18- typo in FqX_split [  (!degpol(x))  --> degpol(x) <= 0 ] -> very rare SEGV
+   19- after alias(a, b); kill(b), invoking 'a' would access corrupted data
+   20- round(-0.5) --> -1 [ should be 0, as was output by round(-0.5, &e) ]
+RS 21- gcc-3.4 breaks on compatibility code to support old readline versions
+       [ pre-4.2 ]. Typecast parameters, not functions and dump
+       (approximate) support for g++ + old readlines in gp_rl.c
+   22- \p 8000
+       E=ellinit([0,0,0,0,1296]); P=[-8,28]; ellpointtoz(E,P) --> oo loop
+   23- idealaddtoone(nfinit(y),[0]) --> SEGV [ BIB ]
+   24- aprcl would wrongly report composites [ introduced aprcl.c:1.48,
+       before release-2-2-8 ]
+   25- ellchangecurve(e over Qp) --> e.w not updated
+   26- ellheight(e over Fp, pt) -->  SEGV
+   27- issquare(Mat(2), &z); z --> SEGV  [ don't allow matrices as input ]
+       issquare(1/x^2) --> SEGV [ typo in polcarrecomplet ]
+   28- rnfkummer(over Q) --> type error.
+   29- allow qfbprimeform(d > 0, p < 0)
+   30- Since exp(-1e100) --> 0. [ tolerable: alternative being underflow
+       error ], we shouldn't have exp(-1e100) --> truncation error. Fixed.
+   31- rnfkummer(bnrinit(bnfinit(y),nextprime(10^20),1),Mat(3)) --> module
+       too large in Fp_shanks [ conductor() was computing unnecessarily
+       tough discrete logs ]
+   32- [Configure:] add /lib64 and /usr/lib64 to library search path
+   33- SEGV in nfeltpowmodpr() for primes of degree 1 [see 2.2.7-F21]
+       + inputs containing t_INTMODs yield unpredictable [wrong] results
+   34- znlog(x,g): first check whether x == g [ quite frequent ]
+   35- rnfisnorminit(nfinit(y^2+y+1), x^3-y,2) --> "incompatible variables"
+   36- nffactormod() returned a factorization which was not a proper t_MAT
+   37- rnfequation(y^2+1, x + Mod(z*y - 1, y^2+1)) --> SEGV [ BIB ]
+   38- many problems in matfrobenius for _inexact_ matrices
+LGr39- very rare SEGV in idealprimedec() [ typo in init_norm(), using FpX_red
+       on a t_INT, which is no longer valid ]
+   40- [library:] precision() output "wrong" value for small non-zero t_REAL
+       (depended on exponent, not the bit accuracy of the input). Change so
+       that true bit accuracy is used unless input is 0 (then use exponent).
+   41- rare memory corruption in thue() [ SmallSols ]
+   42- gamma(z) for tiny z suffered from catastrophic cancellation
+   43- ??? sometimes output extra entries [ e.g. ???eigen output matfrobenius ]
+   44- bezout(0., 0.) --> division by 0
+   45- issquare(5, &n) --> SEGV
+   46- factor(x in Q[i] \ Z[i]) --> rubbish [ typo in factor_gauss() ]
+   47- ellrootno & elllseries didn't check their arguments
+   48- rnfisnorminit(non-monic polynomial) --> SEGV
+   49- prevent Ser & Pol from creating invalid objects (e.g Pol(x+y, y) -> y+y)
+   50- TODO item:  polrootspadic(x^2+8*x+4, 2, 2) --> 2 + O(2^2), whereas
+   there's no padic root. The documentation is not clear enough: should
+   polrootspadic(x,p,r) find roots in Z/p^r [current behaviour], or use the
+   precision of the supplied polynomial to compute roots in Qp, then return
+   them at precision p^r [ better, polroots() and factorpadic() behave this
+   way ] ?
+   51- padicappr(f, t_POLMOD) didn't work
+   52- rnfidealnormabs(rnfinit(nfinit(y),x^2+1),1) --> SEGV
+   53- no argument check in primepi()
+   54- ellminimalmodel(ellinit([1,2,3,4,5])) --> gerepile error
+   55- gcd([]) was 1 [ should be 0 ] + gcd([...]) used an inefficient algorithm
+   56- [readline] completion in extended help context ?? didn't include GP
+       defaults
+   57- [from 2.2.9] nfsubfields(x) had become unable to exploit
+       the trivial case when x is irreducible modulo some prime
+   58- [from 2.2.9] typo in krosi -> wrong result [ affected aprcl only ]
+   59- dirdiv([],[]) -> SEGV
+   60- using allocatemem() in files input with read() could corrupt stack
+       allocatemem() does not end by a longjmp anymore; it is still impossible
+       to use it in loops
+   61- typo in matrixqz: matrixqz(Mat([1,1]~)/2,0) --> "not a rational matrix"
+   62- [cf 2.2.7-F23-] use safer parameters in bernfrac(): bernfrac(166)
+       was wrong.
+   63- [Bug #201] gp --test: line split mode initialized too late
+       print(vector(1000)) would bypass it.
+   64- [Bug #200] gmul2n(t_POL, n) would not normalize the resulting polynomial
+       [ needed in characteristic 2 for instance ].
+   65- FpX_center and centermod didn't use the same normalizations. Fix
+       FpX_center
+   66- factormod(T,p,1): output was not sorted
+BA 67- [from 2.2.9] -fPIC missing for gp2c-run on platforms that require it.
+   68- if(1,print,print(no)) --> no was printed [a function expecting any
+       number of string objects, called without parentheses (no args), would
+       read the following arguments as its own ]
+   69- more stringent tests in ideal* functions: don't accept t_COL with
+       incorrect length as ideals ([]~ produced SEGV in many cases)
+   70- factorback([1,1; 0,1],nfinit(x^3+2)) --> SEGV
+   71- [Bug #156] kbessel was unstably evaluated
+       ? besselk(1,120)
+       %1 = 448600744132608.0000000000000
+   72- hyperu and kbessel(,1) inaccurate. E.g: hyperu(1,1,1) at \p28 -> last
+       3 digits wrong
+
+  Added
+    1- routines equalsi/equalis, equaliu/equalui, cmpui/cmpiu
+    2- exported FFTinit() / FFT() wrappers to rootpol.c:fft() [not under GP yet]
+    3- routines Rg_to_Fl, Rg_to_Fp, RgX_to_FpX, RgX_to_FpXQX, RgX_to_FqX,
+       RgV_to_FpV, RgXQ_mul, RgXQ_sqr, RgX_div_by_X_x, FpX_div_by_X_x,
+       RgXV_unscale
+    4- file Qfb.c [ stuff related to binary quad. forms moved out of arith1.c ]
+    5- routines truedivii, truedvmdis, truedivis
+BA  6- [GMP] faster divri routine.
+BA  7- support for real forms for qfbsolve.
+    8- routines RgX_gcd_simple, RgX_extgcd_simple [ when no coeff explosion
+       in base field ]
+BA  9- new algorithm for exp(t_REAL) [ Newton ]
+   10- new macro ndec2prec()
+   11- use Trager's trick in factorff()
+   12- member function .bid (from a bnr). Extend mod, clgp, no, cyc, gen to
+       bid and bnr structure.
+   13- new keywords for ?? : bid, CFT, ideal, idele, modulus, rnf.
+   14- new construction %#: number of history entries so far
+PC 15- an optional 2nd argument to znorder() to limit the search space
+
+  Changed
+    1- renamed gegal --> gequal, gegalgs --> gequalgs, gegalsg --> gequalsg,
+       egalii --> equalii
+    2- improve trial division in all basic arithmetic functions [ e.g try
+       for(i=1,10^5, factor(1009)), or moebius, or ... ]. Old logic was
+       broken for small inputs (tried far too many primes)
+    3- improve quadclassunit(D >> 1), about 10 times faster in the 40
+       digits range [forbid long reduction cycles (too costly to update arch.
+       info when a relation is found + streamline factorquad() ]. See
+       Z_lvalrem_stop() + remove extra_relations() and use large prime
+       variation all the way [almost all relations are found this way]
+    4- streamline/clean-up polroots [ complete rewrite, much faster for
+       small degrees ].
+    5- improved zetakinit() [save some multiplications, about half of them
+       for quadratic fields. Less so as the degree increases. ]
+    6- renamed RgX_RgX_compo -> RgX_RgXQ_compo
+    7- renamed binome -> binomial, chinois -> chinese [ added chinese1(x)
+       for chinese(x, NULL) ]
+    8- add GC in integer valuation functions + use recursive algorithm when
+       valuation looks large
+    9- no longer raise an exception when online help used on unknown id or
+       obsolete function [report 'unknown identifier' or 'obsolete function']
+   10- removed buggy support for narrow class group in quadclassunit
+       + rewrite binary quadratic forms [ qfr3 / qfr5 ]. TODO: move from
+       arith1.c to qfb.c
+   11- reinstate cornacchia + cornacchia2 with a different interface [ use
+       cornacchia in qfbsolve ]
+   12- allow non-real arguments in incgamc, incgam
+MW 13- ellap: if E has CM by a principal order, use Cornacchia instead
+       of Shanks/Mestre
+   14- more efficient algorithm for issquare(t_FRAC | t_RFRAC)
+   15- don't return t_INTMODs component in *modpr routines, but lifted
+       representatives.
+   16- FpX_roots, FpX_factor: normalize input first + direct support for
+       quadratic and linear polynomials [ no need to compute X^p !]
+   17- FpXQ_powers and similar routines: use multiplications when input has
+       small degree, squarings otherwise.
+   18- matsnf over polynomial rings: make sure elementary divisors are monic
+   19- ellpointtoz() now returns z such that 0 <= Im(z) < Im(w2),
+       0 <= Re(z) < Re(w1).
+   20- bnfinit(non-monic t_POL) --> now discard variable change
+   21- rename apprgen9 -> padicappr, factmod9 -> factorff
+   22- rewrite padicappr()
+   23- [COMPAT] bnrdisclist has lost its 4th argument 'flag', and omitting the
+       archimedean component now means that all 2^r1 possible values are
+       substituted (formerly: indicate no ramification at infinity).
+       The prototype of bnrdisclist0 has likewise changed.
+   24- renamed isprincipalrayall -> bnrisprincipal, rayclassno -> bnrclassno
+       rayclassnolist -> bnrclassnolist
+   25- Change the output of ideallist with technical (flag 2,3) [ with units ]:
+       instead of two vectors, output a vector of 2-component vectors.
+       Change the input of all list routines (bnrclassnolist, bnrdisclist)
+       accordingly.
+   26- %0 (undocumented) is no longer accepted as an alias for %
+   27- renamed realzero -> real_0, realun -> real_1, realmun -> real_m1.
+GN 28- MPQS rewrite [cleaner, faster]
+
+  Removed
+    1- obsolete implementation incgam1 [ use incgam. As far as private
+       functions go, incgam2 is better ]
+    2- removed ideallistarch0, ideallistarchgen, ideallistunitarch,
+       ideallistunitarchgen. Just use ideallistarch.
+    3- obsolete default 'buffersize'. Flag -b is a no-op.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.9 (released 23/12/2004):
+  Fixed
+    1- ploth(,256). splines had stopped working
+    2- gamma(-1.), psi(-1.) [ non-positive integers masquerading as t_REALs ]:
+       wrong results (oo) or 'division by 0' error messages
+    3- log(1. + 1e-20) --> print more decimals than are significant
+    4- [Mac OS X + Fltk]: remove the BROKEN_FORK assertion --> graphic
+       window becomes independant of gp as under other OSes.
+    5- elllseries did not accept complex arguments
+GN  6- [ix86 + Sun cc] build fails [ unsatisfied symbol reference to 'mulll' ]
+    7- [libpari] gprec_w didn't behave well for real zero
+    8- gp -p 51234567890 --> internal overflow and primelimit set to smaller
+       integer
+    9- [ GMP kernel ] incorrect resmod2n --> wrong 2-adic sqrt
+   10- [hi-res plot: fltk] fix compilation with g++-3.4
+BA 11- [hi-res plot: fltk] line plotting was broken
+   12- oo loops in prodinf [ e.g prodinf(x=2, zeta(x)) ]
+   13- [compatible=3] wrong prototype for isoncurve, lex, sign, thetanullk
+   14- quadhilbert(D > 0) spent an unreasonable time to find a relative
+       equation defined over Q [ use Galois theory instead of naive search ]
+   15- typo in resmod2n [ missing (ulong) typecast ] -> issquare((2^64+1)^2)=0
+BA 16- galoisinit() could fail to find a polynomial defining the fixed field.
+   17- elllseries had an apparent singularity at s = 2 [ loss of accuracy ]
+   18- gcoeff, gmael & their variants were not lvalues.
+   19- typo in element_val(t_FRAC) [ "non invertible" errors in rnfpseudobasis ]
+   20- added -fno-strict-aliasing to gcc flags [ PARI code doesn't follow
+       strict aliasing rules. In any case, gcc 3.3 can't handle new mael ]
+   21- 1.a --> 1.0000 [ should be error ].
+       (x.a = x+1); 1.a --> 1.0000 [ should be 2 ]
+   22- bnrdisclist(bnfinit(x,2),1) --> SEGV
+GN 23- [Solaris cc] use safer optimization flag [ -xalias_level=any, fix a
+       pb with 18 above ]
+
+  Changed
+    1- increased maximum binary exponent for t_REAL, and maximum GEN length
+       on 64 bit machines. Bumped BINARY_VERSION for writebin --> objects
+       saved in writebin format between 2.2.1 and 2.2.8 are incompatible.
+    2- use easier to differentiate colors for hi-res plots [ replace "sienna"
+       by "violetred" and "cornsilk" by "green" ]
+    3- GP interface of function intnum [ old flags not recognized, much more
+       elaborate interface ]
+    4- library interface of functions intnum, prodeuler, suminf, sumalt,
+       sumalt2, sumpos, sumpos2, prodinf, prodinf1  [ GEN (*eval)(GEN,void*)
+       everywhere instead of entree * ]
+BA  5- allow Configure -l <directory>  [ in addition to <directory>/pari.cfg ]
+    6- renamed gzero --> gen_0, gun --> gen_1, gdeux --> gen_2.
+    7- replaced kludgy matexp.c example by straightforward extgcd
+
+  Added
+    1- new algorithm for log( t_COMPLEX ), log(2) [ AGM ]
+    2- new algorithm for Pi [ AGM ]
+    3- new algorithm for atan, acos, asin [ AGM ]
+HC  4- "double exponential method" for numerical integration and sumation.
+       See ??intnum. New functions intcirc, intfouriercos, intfourierexp,
+       intfouriersin, intfuncinit, intlaplaceinv, intmellininv,
+       intmellininvshort, intnuminit, intnuminitgen, intnumromb, intnumstep,
+       sumnum, sumnumalt, sumnuminit
+    5- new universal constant gen_m1
+    6- [bench suite] test-intnum and test-stark toplevel targets
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.8 (released 25/11/2004):
+  Fixed
+    1- gp --version: stack overflow
+    2- overflow (in C long multiplication) in zeta(4.45+292532.0*I)
+    3- setrand(1);
+       bnr = bnrinit(bnfinit(quadpoly(1020,y)), 31,1);
+       rnfkummer(bnr, matdiagonal([5,1,1]));
+       --> SEGV ( typo in FpXQX_from_Kronecker )
+    4- make -j4 bench could start the bench before the binary was built
+    5- matkerint had stopped working [from 2.2.7]
+GN  6- [Solaris + cc:] fix linker warnings (missing object types in
+       src/kernel/*.S)
+    7- listsort(List([]), 1) --> SEGV
+    8- setrand(1582268146);
+       bnr=bnrinit(bnfinit(y^12+6*y^10+31*y^8+84*y^6+159*y^4+166*y^2+1),4,1);
+       rnfkummer(bnr,[1,0,0,0,0,0;0,1,0,0,0,0;0,0,1,0,0,0;0,0,0,1,0,0;0,0,0,0,2,1;0,0,0,0,0,1]);
+        --> bug in gadd [ typo in FqX_split ]
+       Smaller test-case:
+         setrand(1701015992)
+         factorff(x^2+y+2, 3, y^3+y^2+2)
+MSo 9- bug in print_version() --> SEGV on startup if cc version number too long
+   10- subst(O(x),x,x+O(x^2)) --> SEGV [ substituting in 0 t_SER ]
+MSo11- various bugs in anal.c: uninitialized reads in get_op_fun(), double_op()
+       memory possibly freed twice because ep->args was not reset to NULL
+       after being freed
+   12- (10^100 + 0.) / (10^100 + 1.)  --> 0.  [ display bug from 2.2.6 ]
+   13- rnfnormgroup(): restrict to primes of degree 1 [ much faster ]
+   14- gamma(351/2) --> 1.235874058265488750143951998 E740  [ = gamma(351) ]
+   15- gammah was much slower than gamma [ contrary to docs ]
+   16- problems with 0 t_SERs:
+       O(y^5)/(1+x) + O(x^3)  --> O(x^3)
+       O(y^5)/(1+x+O(x^3))    --> O(y^5) + O(y^5)*x + O(y^5)*x^2 + O(x^3)
+       O(y) / (1+x)           --> O(y)
+       O(y) * 1/(1+x)         --> O(y)
+       subst(x+O(b),x,a)      --> a
+   17- (0. * x) / x --> 0.E-28 * x
+   18- content(0.*x) --> 0
+   19- nfinit([x^2+1, y]) --> SEGV (bad input)
+   20- lngamma(10^50*I) returned many more digits than were significant
+   21- bnfinit(x^4-768*x^3+220032*x^2-27869184*x+1316749312) --> recent oo loop
+   22- elllseries(large t_REAL) --> 0 [ instead of ~ 1 ]
+   23- erfc(0) --> error  [ instead of 1 ]
+   24- bnfinit(,1) could return without giving fundamental units (if the
+       computation is unfeasible). Raise an error instead.
+IS 25- config/get_head would not work only if one invoked $PWD/Configure
+   26- obscure simplification bug:
+          x;s;t;u;
+          a = -9*u*x^3+3*u*s*x^2-3*u*s^2*x+u*s^3+u^4
+          b = 9*u*x^3+3*u*s*x^2+3*u*s^2*x+u*s^3+u^4
+          bezout(a,b) * (2*u^4*s^3+2*u^7)
+       --> involves terms like u^5/u^2
+   27- matrixqz(matrix(2,1,i,j,i-1),-2) --> [;] instead of [0;1]
+   28- possible SEGV in bnrinit [ when ray class group is trivial ]
+   29- negative definite forms allowed by Qfb() while most qfb* routines
+       can only cope with positive definite forms. Explicitly disallow
+       negative definite forms.
+   30- \p 38
+       ellinit([1,0,0,-19959260,-34323045317]) -> precision too low in initell
+   31- {p = x^36 - 252*x^34 + 27504*x^32 - 1723392*x^30 + 69300198*x^28 -
+      1894026456*x^2    6 + 36355251492*x^24 - 499350803616*x^22 +
+      4953373719489*x^20 - 355510821425 40*x^18 + 183790369965636*x^16 -
+      677399199594048*x^14 + 1751507793357696*x^1    2 - 3105079104411648*x^10
+      + 3651333353058816*x^8 - 2698458969378816*x^6 + 11 30370773667840*x^4 -
+      207898980728832*x^2 + 2879456034816; nfroots(nfinit(subst(p,x,y)), p); }
+      --> loop forever in lllintpartial()
+      Fix: abort lllintpartial if progress negligible (was: if _no_ progress)
+   32- some permanent structs not freed in freeall()
+   33- [--with-gmp] round(-4294967296.1) --> '-0' [ typo in mpent ]
+   34- polredabs(x^4-x^3-31*x^2-12*x+144) was not reduced [ typo in subfield
+       detection algorithm ]
+   35- nffactor(nfinit(a^2+1),x^2+[]) --> SEGV
+GH 36- lngamma was not even continuous : its argument was reduced mod 2Pi
+   37- contfrac(sqrt(2)/2,,2) --> trying to overwrite a universal object
+   38- nfroots(nfinit(a^2+a+1),x^2-a/4) --> []  (leading coeff not properly
+       taken into account in nf_DDF_roots)
+   39- Y=Mod(y,y+1);Mod(Y*x,x-1)^2 --> bug in FpX_divrem, p == NULL
+   40- return type for nffactormod was not a factorization (had become t_VEC)
+   41- Pol(Ser(1+x)) != 1+x  [ bitmask not cleared properly in gconvsp() ]
+   42- default(datadir,"...") --> SEGV [ attempt to free a static string ]
+   43- Configure was missing log2/exp2 on systems that had it
+   44- typo in base2.c:init_norm() [ wrong result in idealprimedec for huge
+       fields ]
+   45- matsnf([x,1; 0,x],6) --> incorrect type
+   46- qfgaussred(a) did not work if coefficients of a did not support the
+       "sign" operation [ signature was computed internally ]
+BA 47- bitneg(1,0) --> SEGV (caused by 2.2.5 A10).
+   48- factorback([;]) didn't work [ should be 1 ]
+   49- f = factor(n); divisors(f) destroyed the factorization stored in f
+   50- add missing GC in hess()
+   51- [Configure:] runpathprefix was computed only in terms of $osname
+       (assuming standard vendor supplied linker). At least check whether
+       it's GNU ld first.
+   52- misleading error message rtodber "overflow or underflow in R->dbl"
+       (in fact, only overflow)
+   53- quadgen(-8) * 0. --> SEGV
+   54- I + O(2^10) --> error
+   55- ((x + quadgen(-8))*Mod(1, i))/(x^2+1) --> SEGV [ from randomgen ]
+   56- Mod(a^0,i^0) --> Mod(1,1)  [ should be Mod(0,1) ]
+   57- factor(HUGE, 100000) --> "pointers lost in gerepile" in random GC in
+       ifac_realloc() [ missing copy() for (*partial)[2] ]
+   58- incorrect gerepile() behaviour on t_LIST [ would try to update
+       non-existing components if list not full ]
+BA 59- quoted strings were not displayed properly quoted.
+   60- bestappr(0.1, 8) --> 0 [ should be 1/8 ]
+   61- rare bugs in copying routines [ clone bit sometimes not unset ]
+   62- SEGV in qfminim when integer entries and precision error occured
+BA 63- galoisidentify() could fail on WSS group with S4 residue.
+   64- t_POLMOD + t_MAT was incorrect. E.g Mat(1) + Mod(1,x) --> Mod(Mat(1),x)
+       instead of Mat(Mod(1,x)) as for other scalars.
+   65- Mod(1,8) + O(2^2) --> Mod(1,8) [ should be error ]
+   66- lngamma(1.) returned a t_REAL of length 'realprecision' (instead of 3)
+BA 67- matsnf(Mat([])) was returning a matrix instead of a vector.
+   68- ellap(ellinit([0,1,0,1,0]), 100) --> FPE  [ BIB: 100 is not prime, but
+       make the routine more robust ]
+   69- give meaningful error messages (with context) for 1<<(1<<32), 1<<Pi
+       (1<<32)! allocatement(1<<32)  [ was: "impossible assignment I-->S" ]
+   70- memory possibly freed twice in pop_val_if_newer() [ race condition on
+       interrupt (<C-C>) during new_val_cell() ]
+   71- ellap(ellinit([0,1,0,1,0]),100) --> SEGV  [ 100 is not prime ! ]
+MS 72- recover from readline history corruption [ don't trust history_length
+       in history_is_new() ]
+   73- obscure bug in thue() [ unit of norm -1 not found due
+       to interface inconsistencies ]
+          t = thueinit(x^6 - 2); thue(t, 2638)  --> []
+JD 74- [Configure:] detection of times()
+   75- Forbid t_POL + t_VEC: useless and inconsistent [ we had: 1 + [] -->
+       error, x + [] --> OK but x * [] --> [] instead of '[]*x' , etc. ]
+   76- obscure bugs wrt polynomials with t_MAT coefficients created via Pol()
+       [ e.g content(x*[;]) --> [;], primitive_part(x*[;]) --> [;] ]
+   77- incorrect behaviour in scalarpol()/scalarser() for exact 0 argument
+   78- typo in ellap: e defined over Fp didn't work any more
+   79- t_SER ^ t_QUAD --> SEGV
+   80- nffactor(non-monic t_POL) --> SEGV due to FpX_red interface change
+       [ don't allow t_INT argument, t_POL is mandatory ]
+   81- ZX_incremental_CRT: wrong handling of degree increase (--> oo loop)
+   82- SEGV in Karatsuba multiplication for polynomials, when high product
+       was 0 (over a ring which is not an integral domain!)
+   83- 1 / Mod((a^2-a)*x^2-1,Mod(2,6)*x^3+Mod(1,2)*x^2+1) --> SEGV
+   84- ellap(e defined over F2, 2) gave wrong results [ assumed e defined
+       over Q and reduced relevant data modulo 8 ]
+   85- matcompanion(degree 0 polynomial) --> stack corruption
+   86- incorrect handling of inexact polynomials in matsnf(,2)  [ inexact
+       leading 0 coefficients ]
+   87- writebin(file, 0) saved a "corrupted" object (0 couldn't be read back)
+   88- wrong prototype used in rnfdet()  [ accepted 3 arguments instead of 2 ]
+   89- typo in to_Kronecker [ when at least 3 variables involved ]
+   90- typo in x = sqrt(t_PADIC)  [ returned either x or -x ]
+   91- rare stack corruption in RgX_mul(t_POL, t_POL) [ when product of
+       leading coefficient cancel, valuation is non-zero _and_ we use an
+       "unsafe" gerepile right afterwards ]
+   92- component(Ser(x),2^31-1) --> SEGV
+   93- typos in krosi(), kronecker() [ e.g krosi(-4,1), kronecker(0,2^32+1) ]
+   94- agm(-1,2) --> oo loop
+   95- tanh(10^10) --> exponent overflow [ should be 1 ! ]
+   96- gamma(exp(-373)) --> exponent overflow
+   97- readline: assume a file 'foo' exists, \rf<TAB> --> \foo [ r deleted! ]
+       [\r f<TAB> was OK]. Solution: just expand to '\r f'; hitting <TAB>
+       again completes properly --> \r foo.
+BA 98- Reduce stack consumption in ffinit and polsubcyclo.
+   99- (p/q)' wasn't simplified if q wasn't squarefree
+  100- polylog(n, x < 0) had a (small) non-zero imaginary part
+  101- 3 + O((-1)) --> oo loop
+  102- creating t_PADIC/t_SERs via x + O(...) eventually blew up the heap
+  103- fix memory leaks related to GP pointers and clones
+
+  Changed
+    1- lgef / setlgef / evallgef removed. One may safely use lg for t_POLs.
+       As a result maximal degree jumps to ~ 2^24 on 32bit machines
+    2- renamed *res(te) routines to *rem(ainder). Eg poldivres --> poldivrem,
+       nfdivres --> nfdivrem
+BA  3- Internal u_Fp* routines now are renamed Fl* and made public.
+BA  4- [GMP kernel] Library soname changed to libpari-gmp[-2.2].so.N.
+BA  5- Library .so link changed to libpari.so for all versions.
+       Static library changed to libpari.a for all versions.
+    6- COMPAT: ellheight now uses the standard normalization: twice the value
+    it used to return. The values returned by ellbil() and ellheightmatrix are
+    unaffected. In particular, ellheightmatrix() is the polar form of
+    elleight(), and ellbil now satisfies the proper identity for B(P, Q) =
+    (h(P+Q)-h(P)-h(Q)) / 2
+    7- renamed and declared gmul_mat_smallvec --> RM_zc_mul
+                            gmul_mati_smallvec--> ZM_zc_mul
+    8- move functions in highlvl.c to libpari, excluding install().
+    9- bnfinit: use approximate integral LLL reduction (much faster than fp)
+   10- bnfinit: cache multiplication table by prime ideal anti-uniformizers
+       (faster valuations)
+BA 11- improve binomial (use divide_conquer_prod)
+   12- improve bnrinit & idealstart when finite part of conductor is 1 (+
+       improve stability)
+   13- subgrouplist(bnr,...) does not require bnr to contain generators anymore
+       ( bnrinit(,,1) ). Also much faster.
+   14- allow zetakinit() to use a bnf argument [ would be recomputed before,
+       making it impossible to certify the result since bnfcertify could not be
+       applied ]
+BA 15- error messages now mention the GP function when the error occured.
+   16- intro message [ no point in displaying "realprecision",
+       "seriesprecision" and "format" on startup. One can query them
+       individually, or ask for all defaults ]
+BA 17- macro varncmp(vx,vy) should now be used to compare variable numbers.
+BA 18- split substpol from subst ( reverse [ 2.2.1 C24 ] ). Use substpol
+       for non-trivial algebraic substitution.
+   19- remove t_FRACN / t_RFRACN from \t output, add t_VECSMALL
+BA 20- bittest() now handle negative operand as 2-adic.
+BA 21- type() does not allow to change object types anymore.
+   22- internal routines setloop()/incloop() allocated 2 chunks of memory,
+       then assumed they were connected [ true for the current allocation
+       model ]. Remove that unecessary assumption.
+   23- more informative error messages in concat()
+   24- in affsi / affui (s, z) : do not check that lg(z) >= 3
+   25- replace gexpo(t_QUAD) by a rough aproximation (as t_COMPLEX): faster
+   26- made Mat(t_VEC of w t_COLs of the same length h) return a h x w matrix
+       Used to be a 1 x w matrix whose elements were t_COLs. Obsoletes such
+       hacks as: v = vector(...); v[1] = Mat(v[1]); concat(v). Now Mat(v) is
+       enough.
+   27- move BEGINEXTERN / ENDEXTERN pairs out of kernel headers into pari.h
+   28- split the User's manual in two: PARI/GP and libpari.
+   29- updated and completed the tutorial
+   30- faster basic transcendental functions on small inputs (sqrt, log, exp)
+   31- rewrote basic generic kernel (add,mul,div) [ faster, less obfuscated ]
+   32- macroified gop1z, gop2z, gops2gsz, gops2sgz, gops2ssz
+   33- diviiz(x,y,z), divisz, divsiz and divssz always assign the euclidean
+       quotient [ used to  depend on the type of z: if t_REAL computed exact
+       quotient ]. Use rdivii, rdivis, rdivsi, rdivss for analogous
+       functionality (no "z" variant);
+   34- ensure proper rounding in divrs
+   35- renamed padiczero --> zeropadic [ as in zero[pol|ser|vec|col|mat] ]
+   36- macroified gcosz, gsinz, gexpz, etc + cleanup transcendental functions
+   37- macroified mulssz, addssz
+   38- rename mpent --> mpfloor
+   39- rename divise --> dvdii, gdivise --> gdvd, mpdivis --> dvdiiz,
+       mpdivisis --> dvdisz
+   40- rename mpppcm --> lcmii, remove mppgcd [ use gcdii ]
+   41- rename resss --> remss, ressi --> remsi, resis --> remis,
+       resii --> remii, gres --> grem
+   42- rename krogs --> krois, krosg --> krosi
+   43- rename FpXQX_FpXQ_mul -> FqX_Fq_mul, FpXQX_normalize -> FqX_normalize
+   44- rename adduumod, subuumod, muluumod, divuumod --> Fl_[add,sub,mul,div]
+       invumod --> Fl_inv, invsmod --> Fl_inv_signed, powuumod --> Fl_pow
+       powiumod --> Fp_powu, mpsqrtmod --> Fp_sqrt, mpsqrtnmod --> Fp_sqrtn
+   45- rename mpsqrt --> sqrtr, mpsqrtn --> sqrtnr
+   46- don't copy arguments of user functions for types which have no
+       modifiable components (anything but VEC, COL, MAT, LIST, VECSMALL):
+       much faster.
+   47- ':' no longer allowed as a substitute for ';' if compatible = 0. Use
+       GP2C semantics [ x:int, v:vec ]. For the time being the type
+       information is discarded.
+   48- modulargcd() was very inefficient for non-monic t_POLs
+       [ e.g (poltchebi(x) - 1) / (x-1) ]
+JD 49- [timer: ] use sysconf(_SC_CLK_TCK) instead of CLK_TCK
+       if available, and make sure one of these is available before choosing
+       times() in Configure
+JD 50- check for 'exuberant-ctags' before 'ctags' in make_vi_tags [ and fail
+       gracefully if neither is found ]
+BA 51- support for gp2c-run on Darwin
+   52- remove blanks in "raw" outputs ( default(output,0) )
+   53- made .fu and .tu return t_POLMOD, not t_POL
+BA 54- [GMP kernel] use mpn_sqrtrem for sqrtr_abs
+   55- nfsubfields (use much less memory)
+GH 56- more robust thue(): faster enumeration of small solution, don't assume
+       that the full unit group is known, warn when conditional result is
+       obtained.
+   57- remove log() flag: decide alone whether to use AGM or not.
+       Rewrote logagm and mpexp1
+   58- make ??? index search ignore case [ ???bernoulli will find Bernoulli ]
+   59- renamed svaluation --> u_lvalrem, pvaluation --> Z_pvalrem
+   60- change semantics of sqrtn to catch easily non-residues from the
+       user's side
+   61- rewrote the 'tune' utility, add one tune.h file for each kernel,
+       support for user override still needs to be rethought (or documented).
+
+  Added
+    1- make test-all [ all available test suites (SLOW !)]
+BA  2- add support for GNU/Hurd.
+    3- conversion routines zv_to_ZC, zv_to_ZV, zm_to_ZM, and matrix
+    multiplication: RgM_zm_mul, ZM_zm_mul.
+    4- output support for zm / Flm matrices (t_MAT with t_VECSMALLs instead
+       of t_COLs)
+    5- FpM_rank
+    6- allow factor( an elt in Z[i] )
+    7- put back bruteall [ used by Math::Pari ]
+BA  8- Configure support for sparc64, x86_64 and ppc64
+BA  9- x86_64 level0 inline assembly kernel
+BA 10- add POSIX-style long options --fast, --quiet, --primelimit and
+       --stacksize.
+   11- new internal library routine itos_or_0
+   12- new member function .index
+   13- rdivii, rdivis, rdivsi, rdivss to replace diviiz & co.
+   14- new routine mpround, mpfloor, mpceil, mptrunc, roundr, floorr, ceilr,
+       truncr
+BA 15- galoisisabelian, galoisexport, galoisidentify and galoissubgroups now
+       also accept subgroups returned by galoissubgroups.
+   16- new routine Fl_sqrt
+   17- new routines FpX_factor, FqX_factor, FpX_degfact, FqX_red
+   18- private header file pari-priv.h
+   19- new function ispower
+   20- forvec iterator
+GH 21- new function zncoppersmith
+   22- public interface to forvec() [ forvec_start + forvec_data, for GP2C ]
+   23- routine sqrtremi() [ Karatsuba square root ]
+   24- internal routine int2n() [ = 2^n ]
+   25- new functions Z_pval, Z_lval, u_lval
+   26- new function primepi
+BA 27- new default factor_add_primes
+BA 28- HPPA 32bit and 64bit level0 inline assembly kernel
+
+  Removed
+    1- obsolete macro leadingcoeff [kept for backward compatibility but
+       removed from the documentation]. Use leading_term.
+    2- obsolete undocumented type t_SMALL
+BA  3- revert 2.2.1 A14 and A17 (broken with GMP kernel, unmaintainable).
+BA  4- cornacchia, in favour of qfbsolve.
+    5- useless inefficient types FRACN / RFRACN
+    6- routines gred [useless] / gredz [ useless did not work ]. Define
+       gred --> gcopy for backward compatibility
+    7- many obsolete error codes [ for err(...) ]
+    8- smodsi()  [ useless, not well defined ]
+    9- buggy macros mppiz, mpeulerz [ undocumented, useless, broke
+       compilation to use them ]
+   10- inconsistently named macros mpinv[sir]r [ were "z" functions ].
+   11- public macros refering to static transcendental routines (e.g mpatanz)
+   12- undocumented, inconsistently named, useless, mulsii, addsii, divisii
+   13- useless routine umuluu [ use mulll ]
+   14- undocumented [macro] constants pariC1, pariC2, pariC3, pariK,
+       pariK2, pariK4
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.7 (released 18/12/2003):
+  Fixed
+    1- bench input files: should all start by a 'gettime()' instruction
+    2- bnfisnorm(bnfinit(x^2-11),1) --> [Mod(1, #), 1]   [ return a result
+       involving MAXVARN on trivial input ]
+    3- [Mat(5)] * [[0]~]~ --> 0. Should be [0]~   [introduced in 2.2.6-C.25]
+    4- \p 1000
+E=ellinit([1,0,1,-120039822036992245303534619191166796374,5042249924849106700108
+01799168082726759443756222911415116]);
+x1=1e100; y1=ellordinate(E,x1)[1];
+ellpointtoz(E,[x1,y1]); -->  impossible addition t_VEC + t_VEC.
+       [ precision error ]
+    5- erfc(non scalar) --> type error
+    6- raising to t_FRAC power didn't work with p-adics: (-1+O(5))^(1/2) --> 1
+    7- [ isprime / APRCL: ] integer overflow, e.g isprime(2^32 + 1, 2)
+    8- sum(i=2^32-10,2^32+10,1) --> 11  [ kind of overflow ] + didn't use the
+       t_INT interface ==> broken with GMP kernel
+    9- polgalois(x^6-3*x^4+3) --> [24, -1, 1], should be [24, -1, 2]. From 2.2.4
+   10- nfisincl(x, x^2+1/2) --> SEGV   [ assumed integral inputs ]
+   11- numbpart(10^15+2) --> SEGV  [ check for x < 10^15 incorrect ]
+   12- polcompositum(x,x) --> SEGV [ problem with poldeflate(deg 0 pol) ]
+   13- zeta((1+I)/2^225) --> 7e28  [ precision error when using func. eq. ]
+   14- typos in qfsign/qfgaussred() [ aka sqred2 ]: wrong results: e.g
+       qfsign([0, -1, 1, 1; -1, 0, 0, 1; 1, 0, 0, -1; 1, 1, -1, 0]) --> [2,2]
+       ( should be [2,1] )
+   15- numbpart(0) --> 0 [ should be 1 ]
+GN 16- assembler section of Sparc Makefiles relied on GNU make specific $<
+MSo17- off-by-1 error in Vec( t_STR )  [ memory corruption ]
+   18- off-by-1 error in itostr(), e.g 10^11 [ memory corruption ]
+BA 19- [hi-res plot: X-Windows] rescaling bug
+MSo20- bittest(1,32) --> 1  [ off-by-1 error ]
+   21- nfeltpowmodpr(nf,x, prime of degree 1) --> SEGV [ FpXQ_pow couldn't
+       handle computation in prime fields ]
+   22- charpoly( Mod(mat, poly) ) --> SEGV [ out of stack components not
+       treated properly ]
+   23- memory corruption when parsing floating point constants [ constante() ]
+   24- too little GC in serreverse()
+   25- bnfinit(huge field) --> "precision loss in truncation" [ missing
+       term sqrt(disc(K)) in norm bound ]
+   26- allow conj(t_POLMOD) provided the modulus degree is <= 2.
+MSo27- ?0 with _many_ user functions defined --> SEGV [ typo in commands() ]
+   28- quadclassunit(5) --> PLEASE REPORT  [ can't build subfactorbase,
+       introduced in 2.2.6 ]
+   29- valuation(x^3, x^2) --> 3  [ valuation at a monomial assumed it
+       had degree 1, introduced in 2.2.2 ]
+   30- Set(matid(2)) --> [ "[1, 0;\n0, 1]" ]. Ensure "raw" format is used,
+       not "prettymat".
+   31- typo in rnfpolred(bnf, *). E.g rnfpolred(nfinit(polcyclo(3,a)),x^3-19)
+       --> oo loop  [ non-positive T_2 form ]
+   32- typo in ellap: rare wrong result. E.g
+       E=ellinit([0,0,1,-5115523309,-140826120488927]);
+       ellap(E, 1315717181)
+BA 33- qfbprimeform(-3, 1) --> data corruption [ gzero's signe set to 1 ]
+   34- qfbpowraw(x, n < 0) reduced its output
+   35- stack corruption when adding huge p-adics
+   36- idealintersect did not allow fractional ideals
+   37- Ser([1,2]) --> 1 + 2*x + O(x^2) [ OK ], but Ser([0,0]) --> O(x^16)
+   38- writebin() still produced incompatible output depending on the
+       multiprecision kernel.
+   39- [alg | lin]dep( t_PADIC ) had stopped working in 2.2.6
+   40- some precision problems in polgalois(deg(p) > 7) [ changed slightly
+       some heuristic settings: the algorithm is simply not rigorous... ]
+   41- typo in polredabs: polynomial of minimal T_2 norm might be missed
+       [ actually found, then deleted due to a typo when "Sorting" small
+       vectors, which would assess them the norm of a larger element ]
+   42- polredabs(,4): some polynomials of minimal norm could be eliminated
+       due to fixed buffer size for the small vectors. Made that dynamic.
+   43- in certain situations (when using Allombert's algo.), subfields did
+       not output subfields sorted by degree
+   44- [Windows installer:] online help did not work on most installations
+   45- lines continuation with \ did not work with DOS fileformat
+BG 46- random(2^31) returned integers in [0, 2^32-1]  [ long/ulong problem ]
+   47- isprime(156499227435744375600531968861048687296374896432841731) --> SEGV
+       [ missing case in aprcl:sqrmod5() ]
+BA 48- Configure: using gcc-3.3 + LANG=french, Configure failed to detect gcc
+       [ messages translated ]
+   49- alpha kernel would not compile with a C++ compiler (missing extern "C")
+   50- rare stack corruption in gmul(t_COMPLEX, t_COMPLEX) [ when result is
+       real ]
+   51- valuation(Mod(x,x^2), y) --> oo loop  [ now 0 ]
+   52- optimization problems in new zeta() and gamma() [2.2.0 C13]
+       ( e.g zeta(0.5 + 10000*I) was _much_ slower than before )
+   53- precision problem in nffactor [ in get_R() ], could yield to a SEGV, e.g
+       nffactor(nfinit(y^4+7^2),x^28-14*x^24+20321*x^20+166992*x^16+1171296*x^12+1342208*x^8-5005056*x^4+3211264);
+   54- contfrac(Pi/2,2) --> [1, 1]  (doc says a_n != 1, so should be [2])
+   55- factorint(
+3280696195200006885547973357173182411881462602934802054749349683516314460050
+2548596646024265171468102088952756084355402608216473040677346490348936595334
+0299389230010814564530645152250241760175805765084544728012391149823776145022
+4913178048870032606672750626773595019035786804361581343222144074318522768947
+2061349830773562073895748494534362180537529975336881525935996152181576667869
+0625865166662910961888106910345518707973056625912728422391238723575276042635
+9727112469435556047168454738353621102783718736137552743946577993495054202848
+9372915917197045423574773583244796785665263319216397122018435444897 ,11)
+      --> impossible assignment I-->S  [ stack corruption ].
+   56- typo in kummer.c:invimsubgroup [ hnfmod --> hnfmodid ]. Very rare
+       weird error message in rnfkummer(): "wrong subgroup in conductor".
+   57- typo in alglin2.c:hnfspec() [ same loop index used twice ]
+   58- zeta(x) very slow for large integer |x| (use new bernfrac() code)
+   59- removed -mimpure-text from DLLDFLAGS on non-sparc architecture
+XR 60- Configure/bench: use head -n # instead of head -# (deprecated and
+       unsupported by GNU coreutils)
+   61- not enough GC in mpbern() [ Bernoulli numbers ] and psi()
+   62- \p50000, then polroots(x) --> exponent overflow
+   63- ellj( t_QUAD ) / eta( t_QUAD ) was forbidden.
+   64- polredabs(x,1) did not work when x was already reduced
+   65- polroots(x^3 + 0e-20) --> oo loop [ didn't handle nicely non-exact 0
+       as a constant coeff ]
+   66- 'make doc' always rebuilt the documentation, even if it was up to date
+
+  Changed
+    1- bnfinit: [small_norm phase] speed up norm computations.
+    2- [make etags:] use $ETAGS instead of 'etags' if defined
+    3- [libpari:] renamed incgam4 --> incgam0, incgam3 --> incgamc
+BA  4- [Description system] Store GP functions in a database in
+       src/functions/ instead of hard-coding them in C files.
+    5- [libpari:] modified zbrent interface to allow library programming
+    6- ellpointtoz(E, P): remove the test that P be on E [ useless and
+       often wrong when P has inexact entries]
+    7- allow serconvol() with 0 series.
+MSo 8- allow numtoperm(0,k)  [ --> [] ]
+    9- simplify result of basic op. involving complex numbers,
+       e.g. I*I --> -1 as t_INT  (was t_COMPLEX)
+   10- embedings of number fields: use x + iy --> (x + y, x - y) instead of
+       (sqrt(2)x, sqrt(2)y) to map complex embeddings to R^2.
+   11- disallow Ser( t_MAT )  [ resulted in a series with t_COL coefficients ]
+   12- nf format to improve idealinv() [ nf[5][6] and nf[5][7] ]
+   13- disallow sqrtint( x < 0 )
+   14- meaning of the GP_DATA_DIR global variable: now points to the
+       directory _containing_ galdata, not to the galdata files
+   15- qfminim(,,2) + polredabs: look for points by (roughly) increasing
+       norms (was: full enumeration and roughly decreasing norms)
+   16- exp(-10^10) --> 0.E2525223  [ was 'underflow' ]
+   17- polredabs: improve the search for subfields (no polynomial
+       factorization required now, only linear algebra)
+   18- ellglobalred(): use gcd(e.c4, e.c6) to help factor e.disc
+   19- removed obsolete components ("checks", "bits of accuracy left in
+       computation") from quadclassunit / bnfinit / bnfisprincipal /
+       bnrisprincipal / bnfunit outputs.
+   20- 'make test-graphic' is gone. Use 'make test-ploth'
+HC 21- new implementation of gamma()
+   22- renamed Oxxx/dft.Config.in --> pari.cfg
+   23- new implementation of bernfrac() [ use zeta(2k), initial GP code by HC]
+   24- bernvec always calls bernfrac()  [ hence has become useless ]
+   25- [sparcv8_super kernel:] replace asm divll with portable one
+       (slightly slower but fixes many compilation problems, and easier to
+       maintain on an architecture which has become hard to test)
+   26- buchall() [ bnfinit, etc ]: complete rewrite to avoid restarting from
+       scratch after a precision increase. All relations stored in algebraic
+       form, so that data is easily recomputed to higher accuracy if necessary.
+       Use lower default precision.
+   27- remove special handling of t_STR by printtex / writetex [ 2-2-6-C19:
+       a \ would be inserted in front of $, %,  etc. ]. This broke things like
+       printtex("$", x, "$"), and is better done by the user herself.
+   28- bnfinit(): remove useless components from the "technical parameter".
+
+  Added
+    1- [Configure:] flag --with-ncurses-lib=*
+BA  2- support for FLTK graphical library [ hi-res plot ]
+BA  3- galoisidentify for order 96 to 127.
+    4- default 'datadir' [ to allow relocating 'galdata' for polgalois ]
+    5- hi-res plotting under Windows
+GTo 6- routine qfrep()
+BA  7- routine qfbsolve(Q,p) for Q imaginary and p prime
+    8- new testsuite: make test-galois (~ 1 minute)
+    9- new options to Configure: --with-fltk, --emacsdir, --datadir,
+       --sysdatadir
+SG 10- updated micro-kernel for HPPA
+BA 11- Add code 'i' for functions returning C int (not long)
+
+  Removed
+    1- bruteall [ useless, undocumented variant of 'brute' ]
+    2- option to Configure: --miscdir
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.6 (released 19/06/2003):
+  Fixed
+    1- rnfidealtwoelt, rnfidealmult --> SEGV
+    2- agm(x+1, x+2) --> oo loop [2.2.5 F-61 was undone...]
+    3- setrand(1); elltors(ellinit(vector(5,k,random/random))) --> SEGV
+       [ incorrect use of partial factorization in ellintegralmodel -->
+       surviving denominator ]
+    4- old typo in cauchy_bound --> factor((x - 155)*(x^2 + 160*x + 12864))
+       returned a single irreducible factor [ missing factor 2: initial upper
+       bound for roots was 125 instead of 250 ]
+    5- zeta(x) very slow near large negative integers
+    6- algdep(2^46,1) ---> 1 [from 2.2.5]
+    7- bnrclassnolist(bnfinit(y),[2,2]) --> SEGV
+IS  8- typo in paricom.h [extra , in enum] --> compilation failure
+IS  9- typo in part.c [ // comments ] --> compilation failure
+   10- y/(x/y) --> error [ wrong assumption that y is a genuine GEN in
+       mulscalrfrac ]
+   11- example/Makefile wasn't up to date [ link with -lpari ]
+   12- zetak(zetakinit(y), 3-1e-28) --> -7.2e28 [ catastrophic precision
+       loss triggered by anything non-integral but very close to an integer ]
+IS 13- gp-dyn build on AIX
+   14- subst(y*x,y^2,x) --> 0
+   15- rnfisnorminit(x,x^3-2) --> SEGV [ bad input. Missing argument checks ]
+   16- Compiling with g++ + gmp, missing gmp version number [missing stdio.h]
+IS 17- [AIX:] various compilation problems + select better CFLAGS
+       (-qtune=auto -qmaxmem=8192)
+IS 18- [HPUX:] remove -E from DLCFLAGS
+   19- bnrconductor(bnfinit(x)) --> SEGV [ missing check ]
+   20- ellpow(E,...,-1) --> stack error [ from 2.2.5 ]
+   21- a:b:c; c/a/b == c/b/a --> 0
+   22- sparcv8 inline assembler kernel used global registers for hiremainder /
+       overflow, which was dangerous, inefficient, and complicated. Now none
+       of the inline kernels use any global variable. The non-inline
+       assembler kernels do define and access globals hiremainder and overflow.
+BA 23- cos(2^2^22) --> precision too low [ idem sin, tan ]
+   24- setrand(1504969109; quadclassunit(-403195) --> incorrect. Check against
+       L-value too liberal; now allow quotient in [ 0.8, 1.3 ], not [0.75,1.5]
+   25- TODO item: quadclassunit not reliable when fed non fundamental
+       discriminant (oo loop [e.g quadclassunit(-352)], wrong result).
+       [ fix: increase factor base ]
+   26- sign of elleisnum(E, 6, 1) was wrong
+BA 27- [GMP kernel] factor(18295370635792208009) --> error
+   28- idealprimedec(nfinit(x^16+16),256) --> SEGV [ bad input ]
+   29- setrand(1642014180);quadclassunit(1642014180) --> PLEASE REPORT [ bug
+       in large prime relation code ]
+   30- TODO item: rnfpolred(nfinit(quadpoly(904,y)),quadray(904,1))
+       ***   division by zero in gdiv, gdivgs or ginv
+BA 31- uninitialized memory read in divrr(x,y), when lg(y) < lg(x), leading
+       to random rounding of result.
+   32- [Cygwin + bash >= 2.05a]: HOSTTYPE no longer auto-exported by bash -->
+       Cygwin compilation broken. Define it to i386 by default.
+   33- TODO items related to polgalois
+       + polgalois(x^8-1864259299553450972214799899167226732549697977945716*x^6+331143259018657601105207922631212331088735421305543663274125986698777318014979969*x^4-2225286541902342283500014249183311190477390*x^2+5); --> degree too large
+       + setrand(7); polgalois(x^8+162644002617632464507038884216211529274267271168000002) --> wrong result
+       + polgalois(x^11 - 2) took a looong time.
+   34- systematic warning in bnfisnorm()  [ "useless flag" ]
+   35- charpoly could not handle t_POL in variable MAXVARN
+   36- \p400 + sin(2*Pi) boasted far too many significant digits
+   37- [Configure:] --with-readline=path did not work
+   38- [Configure:] $TOP/readline was added to search path for ncurses,
+       termcap, etc
+   39- memory leak:
+         x = vector(2); y = vectorv(2);
+         while(1, m = matrix(2,2); m[2, ] = x; m[, 2] = y) --> out of memory
+   40- factor(12345)[,1][3] --> error
+   41- overflow in some GP interface routines: e.g default(realprecision,10^125)
+   42- polhensellift((x^2+1)*Mod(1,5),...) --> SEGV [ bad input ]
+   43-  lindep([x,y]) --> SEGV [ bad input ]
+   44-  polredabs(x^4+9670527181567158504671*x^2+4840282594390812607599424249,16);
+        --> impossible concatenation in concat [ from 2.2.5 ]
+   45- [Cygwin compilation:] incorrect cygtop [ --> readline not found ]
+   46- [Cygwin:] DLL build. libpari.dll and gp-dyn build OK. Install does
+       not work in gp-sta (SEGV)
+   47- rare SEGV in Round 4 when using non-primes in addprimes()
+   48- overflow when computing Euler's constant to 20000+ decimal digits
+MSo49- error message for unrecognized GP metacommand: off-by-1 error
+GN 50- Configure breaks if CFLAGS contains '/'  (invoke sed with '!' separator)
+IZ 51- [tex2mail:] line couldn't start with {...\over...} (required indent)
+IZ 52- [tex2mail:] \over wasn't allowed within \left( \right)
+   53- ZX_QX_resultant (modular resultant with integer result): possible
+       oo loop when the result is 0, and non trivial denominator.
+   54- elleta(E.omega) gave incorrect results [ conflicting normalizations ]
+   55- typo in charpoly(t_MAT) [ stack overflow after garbage collection ]
+   56- subst(y,x+y-x,y) --> 0  [ missing simplification ]
+   57- typo in nfroots [ returned inverse of roots when polynomial had small
+       degree (using Trager's method) ]
+   58- 'path' default incorrectly set on Cygwin
+   59- [internal:] isprincipalfact() incorrect if flag contained
+       nf_GEN_IF_PRINCIPAL (was unused).
+   60- bnfisintnorm(bnfinit(y^4+y+1), -1) --> SEGV [ could occur for any unit ]
+   61- stack corruption in matsnf() [ when GC occurs ]
+   62- vecsort(t_VECSMALL) --> SEGV
+   63-  v = vector(2); j = 0; v[j++] = 1 --> j = 2 [ side effect of
+        2.2.4-F21: LHS for matrix assignment was evaluated twice ]
+        Rem: v = vector(2); v[j++] = v = 0 --> SEGV. Don't cry.
+BA 64- [ix86 + gcc compilation]: fix "invalid preprocessing token" Warning
+BA 65- galoisinit could very rarely trigger a 'impossible inverse modulo' error.
+
+  Changed
+    1- rnf structure (removed useless components)
+    2- rnfidealreltoabs, rnfidealup: now return a Z-basis as a vector of
+       elements in the relative extension, instead of a meaningless HNF matrix
+       wrt an unknown basis. Was especially dangerous if the extension also
+       existed in nfinit form, since the HNF matrices were _not_ ideals wrt
+       this nf structure. [ also consistent with rnfeltreltoabs ]
+    3- rnfidealabstorel now requires input in the above form, so use
+       rnfidealabstorel(rnf, nf.zk * A), if A is a standard ideal in HNF
+       form. Used to require matrices in HNF form, which was confusing since
+       ideals such as A gave wrong results [ HNF wrt different implicit bases ].
+    4- idealadd(x,y): multiply by lcm(denom(x), denom(y)), not their product
+    5- rewrote nfhnf/nfhnfmod/nfbezout [ many small improvements ]
+    6- rnf structure now filled incrementally [ absolute nf, data for norm
+       computations ]. Use build_and_check_obj() mechanism already used for
+       bnf: cycgen ( bnf.gen[i]^bnf.cyc[i] ) + matal (relations in algebraic
+       form)
+    7- rewrote rnfpseudobasis, rnfordmax [+ new routine rnfallbase ]
+    8- always define checkmemory() in libpari [ was included only if MEMSTEP
+       was defined ]. Could cause compilation failure when compiling with
+       different flags.
+    9- upgrade pariemacs, see pariemacs.txt
+   10- faster divll for portable kernel [ inspired by GMP ]
+   11- allow system() under Windows (95 and higher)
+   12- use cmprr() in gegal(t_REAL, t_REAL)
+   13- have Configure check explicitly that the C compiler is ANSI
+   14- faster modular algorithm for characteristic polynomial of algebraic
+       integers Mod(x, y)  [ ZX_caract(): old would replace x by its integral
+       part and correct at the end; new is fully modular ]
+   15- separated mpadd functions from [none|gmp]/mp.c --> kernel/add.c
+   16- use 'lgeflist' for t_LIST: remove the 65536 limit on list size
+   17- subst(1 + x^2 + O(x^5), x^2, y) --> 1 + y + O(y^2)
+   18- nffactor: allow modular factorization over primes of degree > 1
+IZ 19- try to output usable TeX (avoid braces, let TeX split lines)
+   20- [libpari internals:] jmp_buf 'environnement' no longer global
+MSo21- allow poltchebi(n < 0) [ := poltchebi(-n), still satisfies the 3-term
+       recursion ]
+IZ 22- prime sieve optimization [ primelimit ]
+   23- try to reduce the likelihood of an artefact relation in PSLQ [ use
+       "confidence level" 2^(-20) ]
+IZ 24- [tex2mail:] add a "cut here" sign when expression does not fit in the
+       linelength:
+       \o3
+       \p240
+       [Pi, Pi]~
+   25- faster multiplication [t_VEC | t_MAT] * t_COL for sparse t_COL
+       [ induces faster charpoly for sparse t_MAT ]
+   26- matsnf: allow rectangular matrices + improvements for singular matrices
+   27- made startup errors ( GPRC ) non fatal.
+   28- allow vecsort(t_VECSMALL)
+   29- renamed mulssmod/divssmod --> muluumod/divuumod and changed their
+       prototype (use ulong arguments, return ulong)  [ was already the case,
+       with various casts ]
+   30- renamed u_invmod --> invumod. Documented all level0 modular routines.
+       [ also added invsmod ]
+   31- separated "negative or zero argument" (arither2) and "zero argument"
+       (arither3) errors.
+
+  Added
+    1- member function rnf.pol (polynomial generating absolute extension)
+    2- src/kernel/sparcv8_[micro|super]
+    3- in emacs/ : pari-completion.el pari-conf.el.in pari-fontification.el
+       pari-help.el pari-messages.el sli-tools.el
+    4- Configure --builddir : find a decent name for build directory
+       depending on kernel options (with/without gmp, exotic assembler kernel)
+GH  5- PowerPC level0 inline assembly kernel
+    6- support for install() on Mac OS X
+BA  7- GP function galoisexport
+IZ  8- logstyle and TeXstyle defaults
+    9- [GP defaults:] support 'G' suffix (for Giga) (already supported k and M)
+   10- extend GP command line: once flags (-xxx) are processed, remaining
+       args are taken to be filenames, read upon startup [ _after_ the gprc
+       read statements have been processed ]
+GH 11- IA64 level0 inline assembly kernel
+BA 12- GP function galoisidentify and groups identification facility
+   13- public function GENtoGENstr [ for GP2C ]. (was private: gtostr())
+   14- function coefs_to_vec()
+NS 15- support for Qt graphical library [ hi-res plot ]
+
+  Remove
+    1- remove emacs/compile [ integrated in Makefile ]
+    2- src/kernel/sparcv[89]
+    3- config/MANIFEST [ now built dynamically ]
+    4- config/Makefile.DLLs [ use recent libtool instead of dirty hacks ]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.5 (released 11/02/2003):
+  Fixed
+    1- [bnfinit:] typo in be_honest() --> SEGV when cbach2 > cbach [from 2.2.4]
+    2- apprpadic() when p = 2 (could not work) + typos
+    3- typo in gphelp [detex mode]: ubiquitous "var" in translation [from 2.2.4]
+    4- typo in var_make_safe() [ protecting user variables after trapped
+       "stack overflow" ] --> possible SEGV  [from 2.2.4]
+BA  5- [Configure:] add -lm -lc to EXTRADLLDFLAGS, not DLLDFLAGS
+    6- typo in gp_main_loop: avma not restored properly after allocatemem.
+       [from 2.2.4]
+    7- typo in [imag|real]_be_honest: SEGV in quadclassunit(D,,[c,c2]), c2 > c
+       [from 2.2.4]
+    8- typo in rnfisnorminit (pol_up)
+    9- at \p28: x = (1. + 10^-28) - 1; for (j=1, 100, x = (x-10^-28) + 10^-28)
+       now x has 200 words of precision
+   10- stack corruption in addition (t_INT + t_PADIC)
+   11- factorpadic(z+y,2,4) --> SEGV
+   12- wrong result in idealchinese when the y_i had denominator
+BA 13- sigma(n,-1) output a wrong result.
+BA 14- inline alpha assembler broken (registers clobbered)
+GH 15- polsturm(1, 0, 1) ---> error  [ return 0! ]
+   16- "break loop" did not react correctly to allocatemem() read from a file
+       with \r (stopped reading file right away)
+   17- matker(x, 1): internal loop forgot a pointer when garbage collecting
+       --> SEGV (or weird error, e.g division by 0)
+   18- ideallog(nfinit(y),2,idealstar(nfinit(y),4,1)) --> SEGV [bad input]
+   19- ellrootno(ellinit([0,-25,0,-1250,0])) --> SEGV [from 2.2.3]
+   20- typo in elllocalred (wrong Tamagawa number, case I0). [from 2.2.4]
+   21- idealtwoelt(nfinit(y),2/3,1) --> SEGV [bad input]
+   22- for transcendental function f and polynomial p, f(p) only gave
+       seriesprecision significant terms when val(p) = 0
+   23- Ser(a/b) --> 1 + O(x^16) (!!)
+   24- (x^2)^(1/2) --> error
+   25- idealnorm(nfinit(y),matdiagonal([1+I])) --> SEGV [bad input]
+   26- polylog(10,x^10) --> O(x^6) at \ps16 [ 16 significant terms ?]
+                        --> x^10 + O(x^21) at \ps 21 [missing x^20 term]
+   27- nffactor/nfroots: SEGV over Q
+   28- missing sanity checks in install() [ did not check the parser code ]
+   29- typo in nilord() [lg-->lgefint]: nfdisc() didn't work on 64bit HP
+   30- ellisoncurve(ellinit([0,0,0,0,1]),[1/2-sqrt(3)/2*I,0]) --> 0
+   31- type 'gphelp' from command line: carriage return is missing
+   32- typo in LLL_cmbf() [ rare oo loop in factor() over Z[X] ]
+IS 33- [HPUX:] should compile with cc -Ae, not -Aa
+   34- excessive memory usage in bnrstark() [ InitPrimes ]
+   35- bessel[jk](t_SER) not implemented around a!=0, but gave (bogus) results
+       [output error message for now]
+IZ 36- [OS/2:] rename static functions _core[2] (conflict with stdlib.h)
+IZ 37- [OS/2:] ^C would only work once
+   38- algdep(.1^5,1,10) --> 1
+   39- { "a
+            b" }  \\ with an explicit \n
+       produced "ab", whereas whitespace in strings is to be retained.
+   40- inconsistencies in return type from lindep [t_VEC/t_COL].
+   41- possible oo loop in ellap [ typo in appell1 ]
+   42- made PSLQ (algdep/lindep) insensitive to 'realprecision' [use
+       precision of the input]
+   43- nfinit(x^2-4*3,4) --> SEGV
+   44- Compilation failure on ORIGIN + Irix: rename 'sgi' --> 'SG' in
+       basemath/galconj.c
+   45- '\r a' where file 'a' is empty --> "a is not a GP binary file".
+   46- make sure qflll[gram](x) never fails when x has exact entries [ much
+       faster than qflll(x,1) when the entries are large ]
+   47- increase subFB sooner in buchall() + add GC in Q_denom { bnfinit(x^8 -
+4*x^7 + 462*x^6 - 1372*x^5 + 85789*x^4 - 169296*x^3 + 7540560*x^2 - 7456140*x
++ 263038707) } took a long time, then overflowed the stack
+   48- make test-kernel did not work anymore (prototype error in kerntest.c)
+   49- overflow in u_FpM_gauss (oo loop in ZM_inv for huge matrices)
+   50- polcoeff(1+O(x^2),1,y) --> SEGV
+   51- content(y/x) --> 1   [ should be y ]
+   52- divrem(x,2) --> x/2 instead of [x/2, 0]~
+   53- divrem([1.,2],x) --> stack corruption
+   54- ff(n)=local(v=[],w=[]);n
+       f(n)=n
+       ff(f(n))=n  --> error (OK) + SEGV on Linux (same data freed twice)
+   55- qflll(matrix(2,3,x,y,x+y)) --> SEGV
+IZ 56- default(primelimit, 2156858852) --> SEGV and other signed/unsigned
+       conversion problems.
+IZ 57- printtex("x1") and printtex("x_1") gave same output
+IZ 58- printtex(t_STR) [ protect special characters, eg ~ or \ ]
+IZ 59- [readline:] support old versions (1.0), try to recover from
+       mismatched headers wrt. library, report version of loaded library in
+       gp header [as opposed to library version at Configure time]
+BA 60- add "const" keyword to allow building with g++
+   61- agm(1+x,2+x) --> oo loop
+   62- lindep([1,0]) --> error [ problem with trivial cases in PSLQ ]
+   63- wrong reconstruction bounds in nffactor
+   64- short help message for isprime/ispseudoprime
+   65- issquare(t_FRAC or t_RFRAC, &x) did not work [ worked without &x ]
+   66- nfgaloisconj(x^2+1,4, 0) --> SEGV [ bad input ]
+   67- typo in quadhilbert(-D): required O(D) memory instead of O(sqrt(D))
+   68- all signed/unsigned compiler warnings
+   69- \p29, tan(Pi/2) --> "division by zero". Made error more explicit
+   70- nfnewprec(nfinit(x,3)) --> SEGV
+   71- bnrconductorofchar(0,0)--> SEGV [bad input]
+   72- qfjacobi( non-square matrix )  --> SEGV [bad input]
+   73- matmultodiagonal([;],matrix(0,1)); --> SEGV [bad input]
+   74- [GP, trap:] prevent oo recursion if default exception handler
+       itself raises an exception
+   75- allow qfbred(,2) [don't update Shanks's distance] also for definite
+       forms [ignore flag].
+   76- lex(x,"y") --> error [ should be -1 ]
+   77- algdep(0,1) --> 0 [ should be x ]
+   78- log(1, AGM) --> oo loop
+   79- [Configure+gcc] remove useless -m* arguments from CFLAGS [ obsolete ]
+   80- improper rounding when printing floats: 1.2 --> 1.199999999
+   81- [default:] realprecision was allowed to be 0, and then treated as 9.
+   82- Precision loss in inputs of large exponent: 1e100000 -->9.99999824 E99999
+   83- quadgen / quadpoly accepted square inputs --> weird bugs later.
+   84- poldegree(x^2 / y^4, y) --> -1
+   85- setrand(178);quadclassunit(82421) --> 2 instead of 1 [from 2.2.4:]
+   86- polroots( Pol(subst([1,0,4*x,0,2*x^2+24,0,16*x,0,16],x,10^2589) )
+       --> division by zero [ double overflow ]
+   87- LLL-reduction over polynomial rings (almost always failed)
+   88- (x/y)*(y/x) --> y/y
+   89- fix pari_init_stackcheck to avoid bogus "deep recursion" messages on
+       broken machines.
+   90- reused invalid (gerepile'd) pointer in mat_ideal_two_elt.
+
+  Changed:
+    1- "not enough precomputed primes": output largest needed p if available
+XR  2- factorpadic so that factorback gives back the original polynomial, up
+       to a power of p
+    3- type of avma, bot, top to 'pari_sp' (pari stack pointer) [does not
+       break existing code]
+    4- [library:] make floating point assignments round the inputs (used to
+       truncate)
+    5- ensure proper rounding in divrr/mulrr() [ important for numbers input
+       in scientific format, e.g. 1e20 ]
+BA  6- moved internals of mppgcd to mp.c/gcdii
+BA  7- moved internals of genrand to mp.c/randomi
+BA  8- renamed mymyrand --> pari_rand31
+    9- bnrstark: use partial factorization of discriminant when (pol)reducing
+       the polynomial [could embark into hopeless factorizations]
+   10- primedec: improved search for uniformizers when p | index
+IZ 11- [OS/2:] improve dlopen(NULL,...)
+IZ 12- [OS/2:] enable dynamic-linking build
+IZ 13- [OS/2:] use same CFLAGS as under Linux
+IZ 14- when using external prettyprinter, write in raw format to logfile
+       [not in TeX format sent to prettyprinter]
+IZ 15- don't output pseudo-TeX sequences to logfile when using external
+       prettyprinter (raw format)
+IZ 16- make X11.builtin-gnuplot-dynamic the default graphic library.
+IZ 17- allow changing the gnuplot DLL name for gnuplot-dynamic at Configure-time
+       e.g. Configure --graphic=gnuplot-dynamic,gnpltdrw
+   18- allow Vecsmall(t_STR)
+   19- polredabs heuristic (try harder to detect subfields).
+       Ex: try it on polcompositum(x^2 - d, polcyclo(11));
+MSo20- simplified polzagier()
+DS 21- improvements in elltors() [ faster torsion bound, check bound > 1
+       before checking precision ]
+   22- minor improvements in plindep() [p-adic lindep/algdep]
+   23- 'make all' now builds the documentation [ that way, 'make install' need
+       not run a bunch of TeX commands as root ]
+   24- improved idealaddtoone/idealchinese/idealapprfact [use dedicated HNF
+       variant + simpler uniformizers in idealapprfact]
+   25- rnfkummer: do not return rational coeffs as POLMODs: -1, not Mod(-1,pol)
+   26- improved idealval()
+   27- simplified element_reduce, idealmodidele (redideal), compute_raygen
+       (create bnr.gen): faster, smaller elements
+   28- new algorithm for conductor() and bnrdisc() [much faster, does not
+       need bnr.gen]: find minimal f such that P_{1,f}(K) \subset H,
+       computations are done for a fixed modulus F instead of computing the
+       image of H in Cl_f(K) for many f | F.
+IZ 29- improve portability of make_vi_tags (cf 'make ctags')
+   30- using install() twice on the same symbol now updates the prototype code
+   31- re-enable ranlib support (disabled in 2.0.13)
+   32- declare first argument in pariputs[f] as 'const char*'
+   33- enabled Karatsuba multiplication for t_REAL
+   34- broken Configure into many smaller files (config/get_*)
+   35- let gphelp write all cross-references as [label:LABELNAME] (was [??])
+IZ 36- cache optimization in initprimes() [ default(primelimit,.) ]
+   37- Str() now takes multiple arguments as print(). Str(,1) replaced by
+       Strexpand()
+   38- prototype of strtoGENstr() [remove flag]
+   39- remove -Wno-implicit from CFLAGS
+   40- prototype code 's*' now produces a t_VEC of GENs [callee's business
+       to call GENtostr] (used to be a NULL-terminated list)
+   41- [gphelp:] if perl not available, don't pretend extended help is.
+   42- try harder to detect precision problems in floating point lllgram
+   43- [ix86 kernel:] macroified bfffo()
+   44- made all GEN macros return signed types [ had inadvertently switched
+       to ulong as a side effect of some other change, long ago ]
+BA 45- split kernel Makefiles in MakeLVL0 and MakeLVL1.
+BA 46- --disable-kernel Configure option replaced by --kernel=none
+   47- compute_polrel() [rnfkummer] was very slow for [L:K] > 5 (compute
+       Newton sums directly)
+   48- simplified fincke_pohst() [don't recompute gram_matrix]
+   49- faster trueeta(), faster quadhilbert(D < 0)
+   50- faster isprime (trial divide by small primes, recognize 1-word moduli)
+   51- ellheight: use Mestre's AGM to compute Archimedean height
+   52- poldegree(0) is now VERYBIGINT
+   53- alpha + gcc: compile with -O3 [ since F14 fixed the optimizer "bugs" ]
+
+  Added:
+    1- [TeXmacs:] contextual completion (<TAB>)
+BA  2- routines ishiftr, gfloor2n, isqrti, randomi
+    3- routine random_bits
+BA  4- [Configure:] --builddir flag
+IZ  5- File README.os2
+    6- Allow nfroots(, P) [ roots in Q ]
+    7- routines Strexpand(), Strtex()
+IZ  8- detect oo recursion on OS/2 [as on Unix systems] (cf STACK_CHECK)
+RS  9- routine numbpart()
+BA 10- GMP kernel
+BA 11- --kernel Configure option
+IZ 12- allow an arbitrary polynomial as a 2nd argument to subst()
+   13- many files in config/  [ from Configure ]
+
+  Removed
+    1- macro definition BITS_IN_RANDOM (useless)
+    2- appr_reduce() [static base4.c]: lllreducemodmatrix does the same. Faster.
+    3- [useless,undocumented]: idealoplll, idealmullll, idealdivlll
+    4- 68k assembler kernel [ unmaintained, obsoleted by the GMP kernel ]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.4 (released 12/09/2002):
+  Fixed
+    1- add default 'new_galois_format' to make 2.2.3 C-22 optional (by default,
+       old format will be used, preserving compatibility)
+ BA 2- FreeBSD: DL_DFLT_NAME value was incorrect
+    3- Configure: TIOCGWINSZ not detected on Linux
+ BA 4- FpV_roots_to_pol not reduced when applied to a singleton
+    5- (a.x = [a]); "a".x ---> [a]  \\ should be ["a"]
+    6- (a.x = [a]); print("a".x) ---> a0.E-28x  \\ should be ["a"]
+    7- (a.x = [a]); 1.x ---> error \\ should be [1]
+    8- wrong rnfidealnorm[rel | abs] (wrong result if O_L not free O_K-module)
+    9- gcd(x + 1, x + 1/2) --> 1 \\ should be 1/2  [introduced in 2.2.1]
+   10- gcd for multivariate polynomials over finite fields slower than
+       in characteristic 0
+   11- [library:] typo in vpariputs: pariputsf("%Z%Z",x,y) did not work
+       (prints x and address of y)
+IZ 12- [gnuplot + dynamic link:] allow building even when dlopen() not available
+   13- [CVS:] warnings about unknown files (added .cvsignore files)
+   14- f()=return \\ return "void"
+       g()=return(f()) \\ return eval("void") = 0
+       now g() returns "void" also
+   15- f(x=11,y=x)=local(t=ff(),u=t);1 --> parse error
+   16- conjvec([[],[],[]]) --> SEGV
+   17- galoisisabelian(x) --> SEGV
+   18- nfeltreducemodpr(nfinit(x),1,1) --> SEGV
+   19- idealmul(nf, principal ideal, prime ideal) returned wrong result
+       [introduced sometime in 2.2]
+   20- elltors(ellinit([...], 1)) --> SEGV
+   21- try to make sure life of GP variables is not too short. Was:
+         v = [0,0]; v + [v=0,v=0] --> SEGV
+         u = Mod(x*Mod(1,2),polcyclo(25)*Mod(1,2)); sum(i=1,4,u=u^32) --> SEGV
+   22- typo in to_Fp_simple [ bnfcertify(bnfinit(x^2-40!)) --> type error ]
+   23- memory leak in gp when handling '&' arguments
+   24- removed hack in gcopy [ did not reset the isclone() flag because
+       gunclone checks isonstack(). But other routines may want to use it ].
+   25- apparent oo loop in bnfcertify [when computing lower bound for
+       regulator]
+   26- ideallistarch(nfinit(x),[1,1],0); --> SEGV [bad input]
+   27- factor(x-I) --> x - #<16382>
+   28- ellsigma(...,matid(1)) --> SEGV [bad input]
+   29- ideleprincipal([],1) --> SEGV [bad input]
+   30- factorback(matid(1),nfinit(x)) --> SEGV [bad input]
+   31- incomplete help message for vecsort
+   32- polredabs fails to reduce
+         x^8-2*x^7-34*x^6+78*x^5+265*x^4-628*x^3-389*x^2+1237*x-449
+       [typo in chk_gen_init: skipfirst not initialized properly]
+MW 33- (recent) typo in localred (char 2)
+   34- 2.2.3-C20 had broken backward compatibility: restore
+       [inefficient, useless] previous output of nfelt*modpr routines
+   35- idealappr(nfinit(y),matid(2),1) --> SEGV  [bad input]
+   36- sqrtn(0,...) ---> error
+   37- galoisinit(x^4 + 5264*x^3 + 8034856*x^2 + 4205424384*x + 504485485632)
+       --> weird error
+   38- qfsign([;]) --> SEGV
+MW 39- torsion group of [0,0,0,-6648,208633] reported as C2 instead of C6
+   40- bnfinit: very rare stack corruption
+   41- bnfinit: used too much memory when needing huge number of relations
+       [when computing fundamental units]
+   42- bnrL1(bnr with conductor 1) --> SEGV
+   43- "impossible inverse modulo ..." when using addprimes() + ROUND 2
+MSo44- [gp: \x] missing 'break' statement in escape()
+   45- wrong bound in nf_LLL_cmbf (nffactor) [no counter example to
+       the old bound, but proof was wrong anyway...]
+   46- [gp:] memory leak when assigning to multidimensional arrays (x[i][j]=1)
+   47- added user-friendly error message if Configure not run properly + fix
+       INSTALL.tex about make gp.dbg / gp.prf
+   48- matcompanion(x*y) --> weird error
+   49- typos in hilbert(), e.g
+         hilbert(Mod(1,2), y) --> SEGV,
+         hilbert(-1+O(2^3), 12 + O(2^3)) accepted wheras 2-adic precision too
+         low to decide
+BA 50- [FreeBSD:] PORTOBJFORMAT undefined by the system --> Configure fails
+   51- rnfisnorm() [errors, SEGV]. Had to change the prototype.
+BA 52- Oxxx/Makefile was not compatible with BSD make.
+   53- errors in files read from .gprc, containing trap() --> SEGV
+   54- bezoutres(Pol(sin(x)+Pi),Pol(cos(x))) --> "bug in subresext"
+   55- nfsubfields had a different output format when using galoissubfieds
+   56- polcoeff(1/x, -3) --> SEGV
+   57- (-2/x)/(-1/x) --> -2/-1
+BA 58- [GP internals:] trap() had an invalid prototype [DI,DI]
+   59- M[,2][1]=1 --> error; M[1,][2]=1 --> no effect  [ now <==> M[1,2]=1 ]
+   60- [GP:] newline in multiline comments /* */ was not ignored
+   61- [GP:] when using default 'colors' for input at \gn>1, first debugging
+       msg could be colored
+   62- [GP:] in break loop, trailing ; was ignored (all results were printed)
+XR 63- smarter precision increase in update_alpha() (ROUND 4) [stick to
+       padics don't go over Z]
+       Ex: factorpadic(polzagier(18,3), 2, 30) much faster than before.
+   64- E=ellinit([0,0,0,-10301051460877581926458079712219,-12725370882271967125361344545020920373899020890]); ellap(E,1167254453) --> wrong result
+   65- factor(x^2 + I*1.) --> SEGV
+   66- when printing x < 0 t_REAL, was rounded in the wrong direction
+   67- [WINCE port:] small and SID are already defined in windows.h
+
+  Changed
+    1- remove most global variables from gp.c (put them in struct gp_data).
+    2- use better bounds for size of factors in nffactor()
+    3- tuning for van Hoeij's factorizer (factor + nffactor)
+       nffactor: call factornf when deg(pol) << deg(nf) + remove a priori
+       overlift for d-1/d-2 test [major overkill]
+    4- automatic concatenation for strings: use longest match for expression.
+       print("a"[1]) is not valid since "a" is not a vector
+       print("a", [1])  prints  'a[1]'
+IZ  5- [gphelp:] allow uninstalled operation from $TOPDIR or $TOPDIR/Oarch
+IZ  6- [gphelp:] better error messages in case TeX compilation fails
+    7- [development version:] add version number for this file to the gp
+       header when using CVS
+IZ  8- [library:] unified access to diffptr (NEXT_PRIME_VIADIFF macro)
+    9- buchall: re-use the same random seed when doubling prec (for units)
+   10- major cleanup in thue / thueinit
+   11- major improvements in rnfkummer (use elements in factored form):
+       much faster, give smaller elements, allow arbitrary prime degree
+   12- improved quadray(D < 0) when relative degree is huge
+   13- improved bnfisprincipal when class group large: use factorisation
+       (+idealred) instead of arch. components (require much less precision)
+   14- [library:] allow trapping invmoder and recovering the offending INTMOD
+   15- improved factor over Z[X] for _huge_ degrees (factorizations mod p)
+   16- change in ordering for primedec output [use cmp_prime_over_p]
+   17- [library:] moved (formerly) gp-specific write* and print* to libpari
+   18- input format to rnfisnorm() [use rnfisnorminit]
+   19- [GP:] break loop prompt from '>' to 'break>'
+   20- [GP:] don't get out of break loop after \r
+   21- type f(x=1)=; twice ---> x = 1
+   22- [trap:] the way default error handlers operate, how to get out of
+       break loops (see section 2.7 of manual)
+
+  Added
+ BA 1- Add PARI_VERSION and PARI_VERSION_CODE to paricfg.h
+ BA 2- Add pari_release at top of dft.Config.in
+    3- rnfpolredabs: flag to use partial factorization
+    4- [gprc:] test against VERSION number in .gprc
+    5- [gprc:] multiline constructs
+ IZ 6- mnemonics for flags
+    7- internal flag nf_GEN_IF_PRINCIPAL to bnfisprincipal [isprincipalall]
+    8- routine rnfisnorminit()
+ BA 9- [Makefile:] bzdist target
+   10- [library:] CATCH / TRY mechanism [ encapsulate err_catch ]
+
+  Removed
+    1- (useless, undocumented) macros buchgen*, buchinit*
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.3 (released 10/06/2002):
+  Fixed
+    1- bnrinit(bnfinit(subst(polsubcyclo(89,8),x,-x)),4) --> SEGV [make sure
+    isprincipalfact is used in makecygen, even in case of precision problems]
+    2- bnrisprincipal --> warning + "not an element in ..." [missing nf_FORCE]
+    3- oo loop in isprincipalfact when increasing bnf precision
+    4- stack corruption in ellap (LARGE p)
+    5- zeta(s) destroyed the Bernoulli cache when using the functional equation
+    6- oo loop in random_relation (called from bnfinit)
+    7- fixed lower bound for p in nfsubfields (could get oo loop: pol never
+       squarefree mod p)
+    8- [Vecsmall([1, 2])][1][2][1] --> SEGV
+    9- typo in t_FRAC + t_PADIC  [only numerator of t_FRAC used]
+   10- factorback(t,nfinit(x)) --> SEGV
+IZ 11- shift(x > 0,, flag!=0) didn't act as the docs said [flag now ignored]
+   12- lindep(Vec(x^48)) --> oo loop
+   13- could get NaN in max_modulus (polroots) --> havoc later
+   14- missing normalizepol() in centermod(t_POL,)
+   15- [Follow-up to 2.2.2-F10] even more recent pdftex failed again
+   16- not enough GC when printing a huge t_PADIC
+   17- factorpadic(x*(x+1),3,10) would corrupt polx --> SEGV later
+   18- delayed carry treated improperly in red_montgomery
+   19- F() = 0; local(x)  ==> confusing error message
+IZ 20- OS/2: make bench didn't work [env wouldn't start shell scripts]
+BA 21- sqrtn(Mod(1,7),2,&z)-->z=1 should be -1
+HC 22- Standard transcental functions exp, sin, cos, ^, *, are now orders of
+       magnitude faster for t_POL arguments of small degree.
+HC 23- fixed a severe bug in p-adic/integer addition (1+3^4+O(3^10))+3
+       didn't work.
+HC 24- Corrected p-adic initell.
+   25- ellj / eta (t_SER) truncated result to seriesprecision
+   26- possible overflow in u_FpM_gauss (from ZM_inv)
+   27- various problems with C++ compilers
+   28- polredabs could fail to reduce "obvious" input, e.g x^2 + n*x + n^2
+   29- gcd(x,y,1) didn't check its arguments
+   30- typo in elltors: could forget a point of order 2
+   31- gdivgs(t_RFRAC,x) incorrect (if numerator had denominator)
+   32- serreverse(O(x) or x + O(x^2)) --> SEGV
+   33- dbltor(0) returned 2^-308, instead of 10^-308
+   34- precision problems in thue/thueinit
+       setrand(5);tnf=thueinit(x^3 + x^2 - 43690*x - 3529208) --> error in mplog
+   35- typo in bnfnarrow ("impossible inverse")
+   36- subst(x,x^0,x) --> floating point exception
+ISo37- many typos in the documentation
+   38- [DOS/Windows]: incorrect conversion between 'double' and t_REAL
+   39- polrootspadic(4*x^2-1,2,2) --> impossible inverse: Mod(2, 16).
+       [specific to 2, and non-monic equations]
+   40- discrepancy between bnfsunit output and docs [ bnfS[5][2] was t_MAT ]
+   41- matdet: pivoting strategy incorrectly chosen ("incorrect type in gexpo")
+   42- polcompositum output ordering depended on random seed
+   43- plotrecthraw(0,[0]) --> SEGV
+   44- plothraw([],[]) --> SEGV
+IZ 45- ix86 inline assembler compilation problem [divll]
+XR 46- forgotten case in Round4 [ nfdisc(x^12-10*x^11-57*x^10+740*x^9+353*x^8-16130*x^7+17749*x^6+100120*x^5-108466*x^4-292200*x^3+128380*x^2+380800*x+133112)
+--> impossible inverse: Mod(2, ...) ]
+   47- sin(1e-100) ---> 9.999999999999999999735998397 E-101 [ precision loss ]
+   48- idealpow(nf,x, n < 0, 1) did not reduce the result
+   49- gcd(0, -1) --> -1, content([0, Pi]) --> 3.14, content([0,Pi,Pi]) --> 1
+BA 50- compilation problem on OSF (RTLD_GLOBAL undefined)
+BA 51- [X-Windows] hi-res plot: window not redrawn properly (BackingStore pb)
+   52- problems with tex2mail output (wrong alignment, wrong colors)
+   53- contfrac(sin(Pi/4),,2) --> impossible assignment
+   54- oo recursion in gaffect(t_POL, scalar)
+   55- content([-1]) was -1 [ should be 1 ]
+   56- stack corruption in u_FpV_polint (used by modular bivariate resultant)
+   57- polroots() used too high a precision when checking errors a posteriori
+       (--> slow)
+   58- contfrac(1/x) --> SEGV
+   59- typo in sinh(0) --> wrong zero exponent
+   60- exp(O(x^-1)) = O(x^(-1))  [ouch...]
+   61- dilog(O(x^10)) = O(1)
+   62- cosh(O(1)) --> division by 0
+   63- sqrt(4*x^2) --> not an integer exponent for non invertible series in gpow
+   64- besselj(0,O(1)) [or besseli] --> precision<=0 in gprec.
+   65- O(1)' --> O(x^-1)
+   66- gcd(O(2^1), O(2^10)) --> O(2^10)
+   67- polrootspadic(x^2+8*x+4,2,2) -->  "impossible assignment"
+   68- one could write() an object to a binary file, corrupting it
+MW 69- ellap(ellinit([0,0,1,-7077,235516]), 1075060289) --> "zero argument in
+       an arithmetic function"
+MW 70- ellap(ellinit([0,0,1,-7077,235516]),1135392007) --> SEGV
+BA 71- user's manual index truncated if LANG=fr_FR
+   72- lex(string1, string2) could return something not in {-1,0,1}
+   73- bnfisprincipal(bnf,x, 0) was not instantaneous when bnf.no = 1
+   74- no GC in poleval
+   75- make clean did not remove libpari-2.2.*
+   76- incorrect result in gaffsg(s < 0, t_PADIC)  [ wrong valuation ]
+
+  Changed
+BA  1- improved Fp_isom
+IZ  2- remove all dependance on __OPTIMIZE__ for inlining [cf 2.0.14 F-24]
+    3- implementation of psi()  [very slow + wrong results at low accuracy]
+    4- cleaned up forvec() [no more global variables]
+    5- improvements in bnrstark (precompute common data)
+BA  6- improved ffinit()
+    7- rewrote time-critical parts of bnrstark
+    8- all 'input filter' code removed from GP. es.c:filtre() now handles the
+       full filtering [and has become reentrant]
+    9- isprime() now guarantees primality
+BA 10- improved polsubcyclo()
+BA 11- new interface for galoissubcyclo
+HC 12- gamma function for integral/rational arguments
+   13- faster factornf / nfgcd
+   14- removed readline-specific code from gp.c
+   15- allow bnrstark over Q
+   16- 'subgroup' argument made optional in bnrL1, bnrstark, rnfkummer
+   17- use relative van Hoeij algorithm in nffactor [can handle huge number
+       of modular factors]
+   18- rewrote factorff() to use new modular functions
+   19- rewrote primedec() to use new modular functions
+   20- rewrote most *modpr functions
+   21- modified diagnostics for integral LLL to match floating point version
+   22- INCOMPATIBILITY: polgalois(); changed 3rd component of result so that
+       it gives the numbering among all transitive subgroups of S_n [ was ad
+       hoc up to 7, then as described above for n >= 8 ]
+   23- INCOMPATIBILITY: nf.zk is now T2-LLL-reduced
+   24- idealtwoelt was very slow when a small prime with many divisors
+       divided the index (use approximation theorem)
+   25- content(scalar) = abs(scalar) [when it makes sense]
+   26- changed the interface to ispseudoprime and isprime
+   27- [internal] element_muli: check input is consistent
+   28- rewrote all LLL algorithms (use Householder, not Gram-Schmidt; do not
+       use Gram matrix; unified code).
+   29- INCOMPATIBILITY: the internal components of nf[5] have changed (MC and
+       T2 not needed anymore)
+   30- INCOMPATIBILITY: [library] polred & polredabs do not take a 'prec'
+       argument anymore
+   31- unified nfinit, polred* [use dedicated structs internally]
+   32- rnfconductor(..., 1) doesn't need GRH anymore
+   33- extracted FpXQ_gener from idealstar
+   34- allow more types in gdivround
+   35- improved root_bound (first step in factor() over Z[X]) for huge pols.
+   36- faster bernfrac / bernvec
+   37- INCOMPATIBILITY: removed gentimer() / genmsgtimer(). Use TIMER/msgTIMER
+   38- let nfsubfields call galoissubfields (much faster when field is Galois)
+   39- Configure (locatelib): don't look for lib*.so.x [ pb on Linux with
+       [readline | ncurses]-devel.rpm.
+
+  Added
+    1- mplog2() function --> faster mplog()
+    2- gcd for Gaussian integers
+HC  3- APRCL primality test
+    4- (strong) Lucas primality test + Baillie-Pomerance-Selfridge-Wagstaff test
+BA  5- Facilities for permutation groups (perm.c).
+BA  6- New functions galoissubgroups, galoisubfields.
+HC  7- Implemented all Bessel functions J, I, N, K, H1, H2, and Bessel functions
+       for power series and polynomials.
+HC  8- Implemented PSLQ [preliminary]
+GTo 9- Implemented Cipolla's algorithm for sqrt(Mod(x, p))
+   10- library functions FpM_ker, FpM_deplin, diviiround, centermodii
+   11- library function gerepileall
+   12- library functions corepartial, core2partial
+   13- library functions affui, itou, stor, itor
+   14- library function lllint_ip (in place)
+   15- library functions Q_primitive_part, Q_primpart, Q_remove_denom, Q_denom
+   16- allow polredabs to use a partial factorisation of disc(nf) [as polred,
+       but inconsistent values for flag. Backward compatibility problem here...]
+   17- routine ellminimalmodel()
+IZ 18- systems/ directory (currently for OS/2 only)
+IZ 19- [OS/2:] install() for OS/2 system  (using builtin dlopen)
+IZ 20- gnuplot and X11 are now simultaneously supported
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.2 (released 10/01/2002):
+  Fixed
+    1- qfbred(Qfb(4, 17, 18)) --> SIGFPE
+    2- broken compilation in arith.c when CC != gcc
+    3- nfhilbert (local case pr | 2) expected POLMOD argument without check
+    4- poldegree(P, t) slow when t != varn(P), and may not work in library mode
+    5- polrootspadic only worked when leading coeff was a unit
+    6- paddicappr was very slow (esp. when p was large)
+    7- Configure: check for 'double endianness' [used for double --> t_REAL
+       conversion]. Previous check was failing on ARM architecture.
+    8- forsubgroup state not properly restored after ^C
+    9- libpari.so included symbols from libc without linking it explicitly
+       (Debian requirement, and cleaner anyway)
+   10- recent pdftex failed to compile users.tex  (\pdfannotlink undefined)
+   11- !nf.sign[1] was parsed as (!nf.sign)[1]
+   12- matrix(0,1) --> "identical index variables in matrix" [from 2.2.1 F48]
+   13- bnfinit could miss some relations in the "small norm" phase
+   14- x % y incorrect when y < 0 inexact (2 % -3. --> -1.)
+   15- typo in gcdreal (result < 0) [introduced in 2.2.1]
+   16- some obscure problems in rnfkummer
+   17- not enough GC in hnflll
+   18- sqrt(25 + O(2^5))^2 - 25 = O(2^4)
+   19- not enough GC when writing a t_PADIC
+   20- user member functions were very slow (unnecessary copy)
+   21- cd Oxxx; make -j4 bench ran things in the wrong order
+   22- poltchebi, pollegendre gave bogus output for negative degrees
+   23- possible SEGV or oo loop in polrootsmod when p not a prime
+   24- nfnewprec(bnf) could change bnf.gen (due to round-off errors)
+   25- SEGV in gcd(x,y) when operands have coeffs of the form Mod(t_INT,t_POL)
+   26- not enough GC in poldivres (= t_POL % t_POL)
+   27- issquare(Mod(3,27)) = 1  [from 2.2.1 C 10]
+   28- [BUG] message in 'elliptic' bench [made polroots more canonical]
+
+  Changed
+    1- allow compressing *.dvi files for the online help system
+    2- index bound was restricted to MAXLONG in subgrouplist. Made it a GEN
+    3- try to guess correct precision earlier in polgalois/polroots
+    4- allow GP pointers to 'matrix components', e.g issquare(25,&x[i][j])
+    5- changed assignment semantics to make it closer to C: x = y understood as
+       (evaluate Y:=y, then set x:=Y), e.g i=0; i += (i=2) sets i to 4  (was 2)
+    6- output VECSMALLs as 'Vecsmall([...])'
+    7- more efficient polcoeff [also: made it independent of MAXVARN]
+    8- retuned bnfinit (let subfactorbase increase further):
+       setrand(1);bnfinit(x^4 + 1159*x^2 + 335241) would never finish.
+    9- specified precisely lex() [see manual]. Now:
+       lex([0,0],[0]) = 1 (was -1), lex(0,[0]) = -1 (was 0)
+   10- allow factorback(x, e) for prod x[i]^e[i]
+   11- don't store empty lines in history [cf 2.2.1 F57]
+   12- extend x \ y, x \/ y and divrem(x,y)
+XR 13- improvements in bnrstark (try harder to find modulus, need less memory)
+   14- global(x): ignore if x already global (used to raise a warning)
+BA 15- install: add RTLD_GLOBAL to dlopen flags [so that symbols can be used
+       in other .so]
+   16- removed hack in gp_init.c [used "constant default args" to call print0]
+
+  Added
+BA  1- New function 'vectorsmall'.
+    2- allow setting variable in divrem
+    3- GP operator #l for length(l)
+    4- a warning in zsign ("increase precision?") when dubious result
+    5- Montgomery reduction (only used in powmodulo() for now)
+    6- [experimental] basic tuning utilities (src/test/tune.c) [make tune]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.1 (released 18/10/2001):
+
+  Fixed
+    1- typo in factornf (factor of degree 3 --> error message)
+    2- rnflllgram(), idealinv() didn't check their arguments
+    3- [readline] \r<TAB> would complete using files matching r*
+    4- wrong result in ZY_ZXY_resultant when degree dropped in ERS
+    5- factor(x^2 + I*1.) --> stack corruption
+    6- \r directory was silently accepted
+    7- check arguments in polcompositum()
+TH  8- [Cygwin:] fixed paths for readline on cygwin 1.*
+XR  9- possible overflow in ComputeCoeff [ bnrstark() ]
+   10- numtoperm(2,x) --> SEGV [check arguments now]
+   11- rnfkummer could fail with "missing units"
+   12- factornf(P, non-monic t) --> error
+   13- forsubgroup/subgrouplist didn't check their arguments nor accepted SNF
+       matrices
+XR 14- typo in nilord4 --> SEGV, e.g.
+       nfdisc(x^16-363*x^14+53550*x^12-4091823*x^10+170172414*x^8 \
+         -3663509067*x^6+33703350345*x^4-63300912912*x^2+32451860736)
+   15- some messages didn't distinguish between MBytes and millions of Bytes
+   16- typo in BuildTree [hensel lift]: SEGV
+   17- (rare) oo loop in polroots, e.g. x^5-2*x^4-32*x^3+72*x^2+218*x-491
+   18- oo loop in ellpointtoz on certain rare inputs
+   19- stack corruption in factorff
+BA 20- better interface for galoissubcyclo
+BA 21- galoisfixedfield(,,2) now works with non Galois subfields
+BA 22- nfgaloisconj now use `d-1'-test + better strategy
+BA 23- fix accuracy problems in vandermondeinverse
+   24- missed some simplifications when handling multivariate t_RFRAC
+   25- [Cygwin:] incorrect default 'path' (used : instead of ;)
+   26- element_sqr didn't check its arguments (SEGV if bad input)
+   27- possible stack corruption in polroots (+ improved GC there)
+   28- typo in nfsubfields (incorrect bound: could miss a subfield)
+   29- typo in zsimpjoin (concat error in bnrdisclist, intr. in 2.2.0)
+IZ 30- typo in Configure [rl_save_prompt and _eprintf not found in target libs]
+MD 31- oo loop in ellap [uninitialized array length in apell1]
+   32- stack corruption in addfrac [wrong result for large denominators]
+MD 33- resmod2n [called from powmodulo] could return unnormalized integers
+   34- not enough GC in nfinit for huge degrees [get_mul_table]
+   35- intformal(1/2/t^2) --> apparent oo loop
+   36- polredabs(degree 1 polynomial) --> x  [could be very slow]
+   37- polredabs() could use a basis which was not LLL reduced
+BA 38- pariformats (e.g %Z) incorrect on 64bit machines
+   39- polhensellift(x,[x,1],2,2) --> "bug in multiplication"
+   40- glitches in readline completion (random() * ne<TAB) --> inserted args)
+IZ 41- CLK_TCK may reside in time.h
+   42- misleading definition for ?omega / ?bigomega
+   43- on \q, "Good bye" --> Goodbye
+   44- install() accepted gpnames which were not valid identifiers
+   45- bnfinit(x^4+65,,[.1]) --> SEGV   (whenever c < c2)
+   46- when host badily configured (~ not expanded), gp would die on startup
+XR 47- factorpadic(, not a prime, ) --> SEGV
+   48- matrix(2,2,i,i, ...) allowed, with weird result
+BA 49- better arguments checking for galoisfixedfield
+   50- psi(I) --> incorrect type in rtodbl
+XR 51- increase precision automatically when needed in quadhilbertreal
+   52- idealstar(bnfinit(polcyclo(5)),11).clgp --> stack error
+   53- default(format,"g0.4"); 3./10 --> 0.300004577  [now: 0.3000]
+   54- subst() didn't check its arguments (e.g SEGV on t_STR)
+   55- qfbclassno(x < 0): wrong algo when x non fundamental
+   56- obscure bugs with types FRACN/RFRACN [when simplifies to INT/POL]
+IZ 57- readline history: while inputing continuation lines, the partial
+       command was not put into history until full command was run
+   58- not enough GC in polroots (dft) for huge degrees
+   59- Cygwin: don't try to build gp-dyn.exe [crashes]
+   60- use vsnprintf to fix a long-standing bug in PARI output (using formats)
+       used a fixed-length buffer that could overflow. If vsnprintf is not
+       found by Configure, the bug remains.
+
+  Changed
+    1- removed useless parameter prec in many bnr* and rnf* functions
+    2- major update of bnr* functions [use elements in factored form]
+    3- use /tmp instead of /var/tmp as default tmp directory [faster on Solaris]
+BA  4- Fp_PHlog (Pohlig-Hellman) can use a factorization of the subgroup order
+    5- major update of buchall() [bnfinit]: accurate precision increase, use
+       multiplicative archimedean components (fewer logs), cleanup
+    6- [libpari:] rename permute/permuteInv to GP names numtoperm/permtonum
+    7- scalar + [;] --> error  [made it [;]]
+BA  8- factorback, chinese, lcm and gcd now accept a single vector
+    9- Use I \cap Z instead of NI in hnfideal_inv
+   10- improved issquare(t_INTMOD)
+   11- improved subresultant routines (new function pseudodiv)
+   12- command-line options to Configure (installation directories)
+   13- rnfequation: use modular (bivariate) resultant
+   14- polynomial gcd (srgcd): uses modular algo (modulargcd,nfgcd) if possible
+   15- unified internal hnf* functions
+   16- improved nfsubfields [Hensel lift, allow nfsubfields(pol) instead of nf]
+   17- merged all "integer logarithms" + "safe ceil" functions (mylogint,
+       get_e, floor_bound, myceil)
+   18- readline: TAB on empty parentheses following a function name has the
+       same effect as M-h (used to insert args).
+IZ 19- faster computation of prime number table [better cache use]
+IS 20- obscure compiler bug in rootpol.c:max_modulus() [don't modify tau]
+   21- make bench ('elliptic'): explained [BUG] message
+   22- allow lift(t_PADIC)
+   23- faster polroots for Q of the form P(x^k)
+   24- allow subst(t, x^n, x)
+   25- ?user_fun: do not include function text if help available from addhelp
+       [always include it for ??user_fun]
+   26- default values for used defined function arguments are evaluated when
+       the function is called [used to be "when it's defined"]
+   27- allow subgrouplist(znstar(5)) [didn't accept groups in general form]
+   28- taught GP about GP2C-style type declaration [ignored]
+   29- 0. [realzero()] is now coded on 2 words (was 3, with third one ignored)
+IZ 30- readline history: lines are remembered as they were input (whitespace
+       was deleted)
+
+  Added
+BA  1- function pith() [= pi(x), naive implementation]
+    2- new target for Makefile: gp.dbg
+BA  3- library functions FpX_roots_to_pol, FqX_roots_to_pol
+BA  4- library function FpX_FpXQ_compo (Brent & Kung)
+    5- library function FpM_inv, FpM_gauss, ZM_inv, QM_inv
+    6- library function primitive_part
+IZ  7- readline: allow switching readline editing on/off [for commandtools]
+IZ  8- readline: F1 has same effect as M-h, and F1F1 as M-H (short/long help)
+    9- function writebin
+XR 10- new error message "precer" (precision too low in...)
+   11- allow minimal handling of t_VECSMALL under GP
+IZ 12- add error messages (as in err(shier2)) to emacs tag file
+   13- add error messages (as in err(shier2)) to vi tag file
+IZ 14- add flag to shift() to enable consistency with 2-complement semantic
+IZ 15- default 'prompt_cont' for continuation lines
+IZ 16- default 'readline' to switch readline on/off in readline-able binaries
+IZ 17- capabilities to 'bittest' (return bitmap, 2-complement arithmetic)
+GH 18- inline assembler micro-kernel for alpha + recent gcc (at least 2.95.3)
+
+  Removed
+    1- obsolete functions twototwo, threetotwo, threetotwo2
+XR  2- error message truer2 (superseded by precer)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.2.0 (released 30/04/2001):
+
+  Fixed
+BA  1- gpflog could print some lines twice
+    2- return() could forget objects on heap
+    3- polhensellift didn't accept factors which were not squarefree
+    4- Configure missed some shared libraries (when only .so.version was there)
+    5- possible symlink attacks against mpqs()
+    6- internal requests for precision of bnfinit(x) [ = Q ] could yield SEGV
+    7- compatible = 3 not taken into account when reading a file
+    8- cbezout(0,0) = 1 (should be 0)
+    9- when lines > 0, output driver didn't reset properly after user output
+   10- nfdisc(x^5+2*x^4+3*x^3-3*x^2+122*x-1) --> impossible inverse
+   11- bug in gcc-2.95 -O3 -fomit-frame-pointer [Linux]: SEGV in quicksqri
+   12- addrfrac: could return a t_RFRAC whose denominator wasn't a t_POL
+   13- bnfcertify (zimmertbound): off by two error when reading bound
+   14- very inefficient Hensel lift (used ideas from Shoup's NTL)
+   15- incorrect assumption on factor degrees in polcompositum (SEGV in rare
+       cases)
+   16- Vec(VECSMALL) --> incorrect object
+   17- galoispermtopol didn't check permutation length (--> SEGV on bad input)
+XR 18- rnfinit(nf, T) -> bug in multiplication if nf = Q
+   19- Pol(cotan(x)) -> SEGV
+   20- factor(tough polynomial over Z[X]) --> possible SEGV when padic
+       precision is increased [e.g factor(polzagier(60,0))]
+XR 21- bug in ideal bound computation in rnfnormgroup
+   22- bnrisprincipal(...,y+z) --> SEGV
+   23- GP could forget about a function whose redefinition was stopped by a
+       syntax error (sometimes with a delay!)
+   24- bnfisnorm result would include pols in MAXVARN: e.g. Mod(1, #)
+BA 25- pbs in Fp_factor_irred (factoring over Fq a pol. defined over Fp)
+   26- missing macros for gphelp (tex-mode) in chapter 5
+   27- nfsubfields(nfinit(polcyclo(13))) --> impossible inverse Mod(0, 29)
+   28- argument checks for element_mulid, nf_get_r1 (access to nf.sign)
+   29- typo in quadhilbertimag (SEGV for very large discriminants)
+   30- wrong precision used in numerical derivation
+BA 31- isprime(n < 2, 1 or 2) gave wrong result
+IZ 32- \r C:\a.gp wouldn't work     [ : and \ forbidden... ]
+BA 33- bad PARI prototypes in init.c [were confusing GP2C]
+XR 34- nfdisc: fixed cache system in nilord (esp. precision handling)
+BA 35- nfgaloisconj(polcyclo(40)): accuracy lost
+       nfgaloisconj(polcyclo(11)+1): oo loop
+IS 36- typo in squfof tuning on 64bit machines
+   37- -lm doesn't exist on MacOS X
+BD 38- typo in src/kernel/l0asm.c: ulong not recognized by MSVC
+IZ 39- TeX quasi-parsing in gphelp
+   40- ^C in gp would kill an xdvi launched by gphelp via ?? [detach from tty]
+   41- factor(P in Z[X]): wrong bound used in LLL_cmbf --> "no factor"
+   42- one extra blank line printed with some error messages [errcontext]
+   43- no typechecks in subgrouplist and forsubgroup
+   44- round4 [dbasis]: make sure polmodi gets a polynomial, not an int (SEGV)
+   45- various typos in rnfkummer (SEGV or 'non-maximal rank in nfhermite')
+   46- Configure -l (no argument) didn't work anymore
+BA 47- incorrect quoting in src/make_vi_tags (make ctags)
+   48- 1/[;] --> error, whereas [;]^-1 --> [;]  [ now, allow 1/[;] ]
+   49- ??real  only gave the help on t_REALs, not on real()
+   50- eigen: "missing eigenspace" [roundoff pb in ker() compared to exact 0]
+   51- error messages on GP metacommands (\...) indicated wrong context
+   52- bnr functions might fail with "indefinite matrix in lllgram" [prec pb]
+   53- nfhilbert(nf,a,b, pr | 2) would give bogus result if nf in variable 0 (x)
+MD 54- real(1 / (a+quadgen(...)*b)) was a / (a^2 + b^2)  [assumed quadgen(-4) !]
+   55- wrong HNF (reduction not finished) when nblines >> nbcol [hnf(,0) only]
+   56- rare problem in isprincipal (large non Galois base field)--> wrong
+       result (generators not required) or oo loop.
+   57- compatibility problems with readline-4.2 (build would fail)
+BA 58- pari format %Z (pariputsf) treated incorrectly on 64bit machines
+XR 59- typo in smallvectors (polredabs): possible oo loop
+
+  Changed
+    1- DOS distribution archives (GPM removed)
+XR  2- round 4 algorithm: compute characteristic polynomials via Newton sums
+XR  3- nffactormod now calls factmod / factmod9 when possible
+XR  4- don't compute discriminant in nffactor/nfroots + better bounds in nfsqff
+    5- input loops rewritten (filtre more flexible + unified common code)
+    6- better modular arithmetic for polynomials (+ uniformized names)
+    7- bnfinit: "looking hard" part overdone in random relations. Tuned down.
+XR  8- added flag in rnfconductor to check extension is abelian (under GRH)
+    9- made Vec("pari") return ["p","a","r","i"] instead of ["pari"]
+   10- renamed library function gsize() to sizedigit()  [conflict with gtk]
+XR 11- bnrstark: check if N0 is too large (=> computation impossible)
+IK 12- let readGEN() return NULL when EOF is met (was oo loop)
+   13- algorithm for zeta(), gamma() [initial GP code by HC]
+   14- improved rnfnormgroup() [reduce number of calls to isprincipal]
+IZ 15- flags for OS/2 build + use generic [pre|suf]fix for object files
+IZ 16- readline: hit_return() would not work after Esc-H
+                 extend online help recognition capabilities (Esc h/H, F1)
+   17- install the whole distribution (see ?12)
+   18- clean up in the bnrdisclist ray class group internal functions
+   19- naming scheme for development versions library: libpari-2.2.so.0.0.0
+   20- new Configure flags --share-prefix, --host
+   21- allow library functions to return NULL to the GP interpreter
+   22- let A^-1 and 1/A return a left inverse of A if it exists [A had to be
+       square]
+   23- retuned factorization over Z[X] (+ "d-1 test" in naive recombination)
+   24- sort factorpadic() output
+
+  Added
+    1- files README.WIN, config/[arch-osname|locatesymbol], doc/tex2mail.1
+GN  2- Jebelean extended gcd + rational number reconstruction
+XR  3- new flag to rnfconductor (check extension is abelian)
+IZ  4- set of default colors (boldfg)
+    5- modular polynomial arithmetic ([uni|bi]variate resultant over Z,
+       characteristic polynomial of algebraic numbers, nfgcd).
+       [undocumented, experimental]
+    6- Pohlig-Hellman discrete log over Fp and nf.zk / pr
+BA  7- flag to galoissubcyclo (also output conductor)
+
+  Changed
+    1- start using the new modular functions [polcompositum]
+BA  2- sqrtn, aka mpsqrtnmod (modular n-th root) uses Fp_shanks now
+
+  Removed
+    1- obsolete undocumented functions oldidealinv, idealinv0
+    2- buggy function hnfhavas (hnflll is a better alternative)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.1.0 (released 17/11/2000):
+
+  Fixed
+    1- non portable \e in es.c (pb with Sun cc)
+    2- ^C while prettyprinter in action --> gp crash
+    3- polresultant(a,b,x,2) --> b
+    4- exp(log(1.+x+y)) --> division by 0
+TH  5- support cygwin version 1
+    6- TeXmacs interface
+    7- GP parser codes (sqrtint, idealprimedec)
+    8- trap (break loop) + NO readline + ^C at input time --> exit.
+    9- qfminim(,,,1) --> nonsensical result (typo)
+   10- SPARC + gcc-2.97 --> bug in addsmulsi (register reset too soon)
+   11- trapping allocatemem() could crash GP
+
+  Changed
+    1- release PARI under the GNU GPL. Remove COPYRIGHT file, add COPYING
+
+===========================================================================
+Done for version 2.0.21 (released 27/10/2000):
+  Fixed
+    1- trap(gdiver2,a,1/0) <C-D> trap(,a,1/0); trap(gdiver2,a,1/0) --> crash
+    2- lllgramintern could reduce a wrong lattice (after precision problems)
+XR  3- not enough GC in bezout()
+    4- use hnfmodid in ideallllred  (work mod I \cap Z, not mod N(I))
+IS  5- C++ compilation problem (compl is a C++ operator + casts)
+    6- factor(x^2 - 16810110*x + 62994937599000) --> division by 0
+BA  7- Pocklington-Lehmer didn't stop factorisation after sqrt(N)
+    8- exp(log(Pi+x+y)*1.) --> SEGV (still gives an error, but a decent one)
+    9- factorff needed a prime field F_p where p was single precision
+   10- more digits than were significant could be printed (ex: precision(Pi,1))
+   11- rnfisnorm didn't accept vectors on Zk basis as argument
+   12- [cygwin] fixed timer (always returned 0)
+IS 13- function prototypes (missing 'extern')
+   14- quadray(bnf, ...) didn't work when bnf.disc < 0. Could also return
+       a relative equation over an intermediate field
+   15- deficient argument checks: asinh(2*x-1), agm(1,1-x), zetakinit(2*x-1),
+       idealinv( , wrongtype), nfnewprec([2*x-1]), thueinit(non-monic or
+       degree 1), lcm(x, 0) rnfidealmul(2*x-1,0), galoisfixedfield(2*x-1),
+       qfbnupow(2*x-1, 0), polred([x,x]), bezout(Mod(x,x^2+1),0),
+       numtoperm(-1,0), polred(nf), idealhnf(nf,y+z), factorpadic(,y,),
+       nfelftreduce, rnfsteinitz, idealappr(not an nf, ...): SEGV
+   16- check variable numbers in algtobasis: nfalgtobasis(nfinit(P(y), x))
+   17- factorcantor(x^4 - x^3 - 2*x - 1, 3) --> SEGV [typo!]
+   18- matsupplement(non-exact entries): fixed zero test (--> |x| < eps)
+   19- precision(I*1.) --> +oo (should be realprecision)
+   20- global() could check uninitialized memory
+   21- default(prettyprinter,"non-existent-file") ---> SIGPIPE and GP crash
+   22- gamma(1+O(3^2)+x) --> SIGFPE
+   23- typo in cxgamma (gamma(-4.1+Pi*I/2) --> exponent overflow)
+   24- Odos/paricfg.h defined PARIINFO incorrectly
+   25- inconsistenty t_POLMOD specification: Mod(x,y)+16 --> invalid object
+   26- bnfsunit error (impossible I-->S) when large class group
+   27- qfminim([;],...) / qfperfection([;]) --> SEGV
+   28- idealpow(,,,1) only reduced at the end, not after each multiplication
+   29- typo in zbrent (always used bisection)
+BA 30- cotan(x) wasn't accepted
+   31- wrong internal prototype for thetanullk (prec not taken into account)
+   32- various bugs in gphelp -detex (??\a)
+   33- trap + ^C + next would interrupt \r or read()
+IZ 34- external prettyprinter incompatible with colors
+   35- idealval used much larger numbers than necessary
+   36- sqrtint very inefficient (computed with full precision all along)
+   37- bnfisprincipal couldn't deal with some non-Galois fields
+   38- listcreate allowed creating longer lists than GP could later handle
+   39- INPUT colors was "leaking out" (affected status messages)
+   40- idealpowred(, power > 2^32) --> wrong result
+   41- when lines > 0, output driver didn't reset properly after overflow
+   42- huge precision losses in bnfinit computations (--> truncation error)
+   43- mateigen([1,2;3,4]) wouldn't work at default precision
+   44- leftover debugging statements in gphelp -to_pod
+   45- zeta(x) didn't always use the precision of x (contrary to the other
+       trans. functions)
+   46- some Warnings from MIPSPro 7.2.1 compiler (contributed by PM)
+BA 47- gdiv(SER,POL) --> SEGV when POL has lgef < lg
+   48- check relative pol is monic before calling a rnf* function
+XR 49- cleaned up bnrL1 character computations
+XR 50- round 4: possible problems when increasing p-adic precision
+   51- stack corruption in mppgcd(huge t_FRAC, huge t_FRAC)
+BA 52- abs(t_SER) not allowed
+   53- nfdisc(non monic polynomial,, disc factorization) returned wrong answer
+   54- rnfidealnormrel(rnfinit(nfinit(y^2-2),x),2) --> SIGBUS
+   55- hardcoded paths in a few scripts (tex2mail, gpflog, make_vi_tags)
+
+  Changed
+BA  1- more efficient quadratic Hensel lift
+    2- made ya optional in polinterpolate
+BA  3- more efficient factorff when pol belongs to Fp[X] (based on Fp_isom)
+    4- catch SIGFPE
+    5- made poltchebi efficient
+    6- write all real zeroes in exponential format (0e28) in format 'g'
+    7- bnfsunit: try to compute S-units even when the class group is large
+                 reduce class group generators
+    8- 0.e N now inputs a real 0 of decimal exponent N (was N-defaultprecision)
+    9- try to avoid errors due to precision loss (while computing archimedian
+       components) in bnfnewprec/bnfmake
+   10- restore \o2 to previous meaning, use \o3 for alternate prettyprinter
+   11- normalized output of idealfactor (sort factors)
+   12- better TeXization (\left / \right)
+   13- allow color changes in error messages (when sample input is given)
+   14- format for bnf[9] (new isprincipal)
+   15- cleaned up isprincipal + bnfinit (small_norm, getfu, class_group_gen)
+XR 16- added flag for D>0 in quadhilbert: if non-zero, try more modulii
+   17- idealred: reduce huge ideals as Z-module first (using lllintpartial)
+   18- TeXmacs interface
+
+  Added
+    1- library function zerovec
+    2- van Hoeij's algo. for modular factors recombination (factor over Z[X])
+    3- algdep for p-adic numbers
+IZ  4- default values for 'colors' (light/dark) and 'prettyprinter' (tex2mail)
+    5- hnflll implementation (old one was preliminary and didn't work at all)
+GN  6- squfof implementation
+    7- numerical derivation
+XR  8- GP interface to hensel_lift functions (polhensellift)
+
+  Removed
+    1- quadrayimagwei, preliminary implementation to quadray(D < 0)
+
+===========================================================================
+Done for version 2.0.20.beta (released 07/06/2000):
+  Fixed
+    1- gp -p1e -->SEGV
+    2- nfinit([x,1]) --> SEGV
+    3- ensure stack large enough [allocatemem(1) painted user in a corner]
+    4- make sure mpinl.o is linked (binaries may fail to build otherwise)
+    5- make sure sqrt(Mod(a,b)) terminates
+    6- memory leak with PARI pointers (issquare(4,&x))
+    7- 'make clean' didn't affect doc
+    8- Configure didn't detect some SuperSparc + Linux combination
+    9- forprime (and friends) could crash when loop index was tampered with
+   10- trap(, -1, (1/0)) == -1 ---> -1 (analyseur not reset)
+XR 11- bnrstark initializations (incorrect when field not totally real)
+   12- member functions not very robust [e.g. ellinit(e,1).roots --> SEGV]
+   13- compiler warnings (gcc -O -Wall)
+   14- trap() not robust enough in library mode (nested handlers)
+   15- (x^2+3)/(2*x)*x --> invalid RFRAC
+   16- factorpadic result used too high a precision (+improved handling of
+       nonmonic polynomials)
+   17- sqrt(Mod(0,1)) --> oo loop (+ improved a bit mpsqrtmod)
+   18- no GC in norml2
+IZ 19- tex2mail had difficulties with rational functions
+   20- (rare) SIGBUS in quadclassunit(non-fundamental discriminant)
+   21- g()=f()==0 --> syntax error
+   22- } as last char of an input file (missing a final \n) caused problems
+   23- ??keyword@ didn't work properly (pattern modified between chapters)
+BA 24- bug in galconj.c:corediscpartial (could return half integer)
+   25- factorff and ffinit "not random enough"
+TH 26- Configure doesn't handle cygwin new directory structure
+CW 27- polsturm(x,0,1) = 1 (should be 0)
+   28- [library:] gentimer(3) didn't work
+PW 29- various bugs in qfminim(,,2) [bound was rounded up + stack corruption]
+   30- ellap(e,2) gave wrong result when 2 | e.disc
+   31- factorint: isprime() used on factors even within smallfact()
+GN 32- SEGV in mpqs main sieving routine (unsigned comparison)
+   33- prevent polredabs from keeping small vectors accumulating in a subfield
+
+  Changed
+    1- renamed .DOC files to .txt (Explorer was getting confused...)
+    2- moved MANIFEST to config/MANIFEST
+IZ  3- meaning of flags > 1 in plothraw
+IZ  4- output of \x (a bit more verbose)
+    5- unified basic t_QFR routines (use exponential distance internally)
+
+  Added
+BA  1- Pocklington-Lehmer primality prover
+BA  2- Fp_isom (library)
+BA  3- Mod(a,b)^(c/d) for a,b,c,d integers
+    4- flag to vecsort (decreasing order)
+BA  5- function sqrtn
+
+===========================================================================
+Done for version 2.0.19.beta (released 17/03/2000):
+  Fixed
+    1- buffer overflow in ploth (when extrema too large)
+    2- trap(,"") didn't work
+IS+BA 3- various fixes necessary for g++
+    3- prototype inconsistency in level1.h (evalexpo/evalvalp)
+    4- trouble with pgcc -mk6 + variable k6 in elliptic.c
+HC  5- nfnewprec(bnf) could contain objects of low accuracy
+XR  6- check subgroup argument in bnrL1
+    7- typo in ideal_two_elt (oo loop)
+    8- pb with GNU as + preprocessor on Sparc
+    9- incorrect result in matdet over Z[1/n] (Gauss-Bareiss) [gdivexact]
+   10- solve(x=0,1,deriv(x)) --> SEGV
+   11- ??' --> weird error
+   12- generic SNF (polynomial entries) couldn't handle 0s on diagonal
+   13- incorrect handling of 0 ideals in some functions
+XR 14- typos in bnrL1
+   15- bad input not handled in rnfcharpoly()
+IZ 16- delay rounding in rectplot structures until plotting device
+   17- Mod(1,x) / matid(2) --> incorrect object
+   18- avoid getrusage on alpha (pb with gp-dyn)
+   19- incorrect file inclusions when using gas on Sparc (-->undefined symbols)
+   20- massive cancellations in zetak(nfz,) when current prec lower than nfz's
+XR 21- bnrstark(): fixed inconsistency between 32bit & 64bit versions +
+       check maxprime() before starting the computation
+PW 22- hyperu bad input bug
+PW 23- intnum(x=1,2,0) --> oo loop
+   24- sin(x + y) --> error
+   25- next(i > 1) didn't work (part of the last loop could be executed)
+   26- typo in polrootspadic(p, 2, *) [singular case]
+
+  Changed
+XR  1- bnrL1 now takes a congruence subgroup as second argument
+    2- allow some functions to modify bnf/bnr structures (matal + cycgen)
+CB  3- modified TeX macros for pdftex
+    4- have abs(t_COMPLEX) give an exact result if possible [abs(0*I) was 0.0]
+IS  5- improved generic gcc compilation flags
+    6- in qfbclassno(), compute in G^2 to get rid of cycli factors
+    7- improved bnrdisc/bnrconductor
+
+  Added
+BA  1- function galoissubcyclo()
+BA  2- (for now internal) function inverseimage_mod_p()
+BA  3- optional arguments (vertical range) to plot()
+IZ  4- `ticks' on hi-res plots
+IZ  5- prettyprinter default and tex2mail file
+BA  6- new function Fq_ker
+
+===========================================================================
+Done for version 2.0.18.beta (released 20/12/1999):
+  Fixed
+    1- wrong method chosen in isinexactfield for 0 polynomials
+    2- stack corruption in sylvestermatrix(0,...): Mod(x,x)^-1 --> SEGV
+    3- garbage left on stack in ginv(1 / n)
+    4- typo: carreparfait --> gcarreparfait in nfiso0()
+    5- for (A=1, 2, A = -A) --> SEGV
+IZ  6- [gnuplot] couldn't set terminal as the first graphing operation
+DE  7- typo in kernel/sparcv7/level0.S: err --> pari_err
+    8- TeXmacs interface (use new protocol)
+    9- call simplify() before applying GP '==' and '!=' operators
+      Ex: x;n;k;m; (n+m)==(k+1-k)*(n+m) was false.
+   10- sumdiv(N,...) required single precision N
+BA 11- bound problems in nfgaloisconj()
+   12- oo loop in do_agm()
+   13- "Warning: in Gauss lg(a)=1..." from isprincipalall0
+   14- specific error for precision problems with p-adic ell. curves
+IZ 15- ploth() plots are 1/2-pixel off
+BA 16- result was not always complete in nfgaloisconj()
+   17- type(1/y, RFRACN) --> SEGV + check compatibility before applying type()
+   18- made 'secure' safer. Confirm before write, prevent from changing 'help'
+   19- gettime() called the wrong timer(): was reset in debug mode
+   20- missing case (doubling) in apell1
+   21- gmod(x, mod) replaced by gmul(x, gmodulcp(gun,mod)) in poldivres
+       [obscure bugs when dividing complicated polynomials with mixed
+        polmods/intmods].
+   22- Make sure result is a t_POL before doing a setvarn in caract2_i()
+   23- precision problems in ClxModulus (bnrstark()) [check leading term!=0]
+   24- elltors() could miss some points: determine needed precision first
+   25- clean up files after an error (not only under GP)
+   26-  divisors(highly composite integer) --> "cryptic" error message
+   27- 'make clean' wasn't thorough
+   28- ellpointtoz(e, [0,0]) could correspond to the inverse point
+   29- "ideals not coprime" in rnfsteinitz
+BA 30- typo in Configure [CPP] (cc --> CC)
+BA 31- member functions for galoisinit
+   32- allowed power series in Mod(). Fixed cryptic error messages.
+   33- precision problems in zsigne
+   34- value of 'gzip' found by Configure overwritten in TOP_Make.SH
+XR 35- precision loss in Round 4
+XR 36- ensure that fundamental unit is computed in quadray (if D > 0)
+XR 37- precision fixes in stark.c (inconsistencies 32/64 bit + early abort)
+   38- bug in P*Q for polynomials over (non prime) finite fields if Q is
+       defined over the prime field
+XR 39- [stark.c]: bugs in RecCoeff3, reduced memory use in ComputeArtinNumber
+   40- core dump on oo recursion under GP
+   41- use appropriate precision in torselldoud (could be much too large)
+   42- removeprimes(addprimes) didn't work
+   43- (at least part of) numerical instability of LLL over R
+   44- f() = bug(x, &y) ---> syntax error
+XR 45- inefficiencies in ComputeArtinNumber for large modulus
+HC 46- no GC in dirmul
+   47- meaningless heap count for user functions
+   48- setrand(16);quadclassunit(48893) --> oo loop
+   49- rnfpolred(bnfinit(y^2+1),x) --> SEGV
+   50- float overflow in polroots (e.g polroots(x^3 + 2^1024*x + 1))
+   51- charpoly(Mod(1,x^2+1)) --> SEGV
+   52- added GC in polsturm
+BA 53- bugs involving rarely used type combinations in generic operations
+   54- check x >= 0 in lllgramint(x)
+   55- in rnfdedekind: use rnfhermitemod, not rnfhermite (slooow)
+XR 56- updated factorpadic to use new Round 4
+BA 57- nfgaloisconj: discrepancies between 32/64 bits architectures
+   58- random(1 << 32) could have 32 bits
+   59- missing newline in error messages on startup (reading .gprc)
+   60- silent codeword overflows (e.g. O(x^100000))
+IZ 61- updated gnuplot support
+   62- x="a"; eval(x) --> error
+   63- general flakiness with buffer handling under GP
+       (Ex: input() + allocatemem --> SEGV)
+   64- [1] == 2 --> error (instead of 0)
+   65- lngamma(-0.106) --> SEGV
+   66- quadray(-11,3) --> x^2
+XR 67- typo in nilord2 (wrong value for modular reduction)
+   68- unsafe division t_SER / t_SER with clonable components
+   69- polroots(1E-28*I*x^2+1) --> bug in roots (conjugates)
+   70- typo in cxlngamma [ lngamma(-7.4927-0.418564*I) --> SEGV ]
+   71- polroots(polynomial of very small Norm wrt prec) behaved badly (SEGV)
+   72- polylog(1, Mod(1,2)) --> SEGV
+   73- zeta(22!/23) --> SEGV
+   74- not enough GC in rnformax
+   75- over-reactive 'secure' default (+ moved it later in default .gprc)
+   76- sqrt(Mod(15,y^2+1) + O(x^5)) --> SEGV
+BA 77- typo in isinexactfield
+   78- sum(x=1,10, expr1; seq) : seq silently ignored
+   79- ellan and elltaniyama didn't check their arguments
+   80- simplify(t_POLMOD) could create invalid objects (mod not a t_POL)
+   81- primes(-1) --> SEGV
+   82- addprimes(0) wasn't rejected
+   83- various inconsistant error bessages (e.g. factor("a"))
+   84- estimate for the precision of unit embeddings in isprincipalall
+   85- global("b") --> polvar corrupt
+   86- hnfmod --> hnfmodid whenever possible
+   87- check arguments in incgamc
+   88- non-rectangular matrices could trigger a SEGV (in error msg!)
+   89- qfminim(,,2) didn't like mixed t_REAL/ t_FRAC entries (typo)
+   90- inconsistant spacing after GP error messages
+   91- regrouped code between idealred and idealmin
+   92- prevent 'install' in secure mode
+DS 93- typo in bnfisnorm(,,flag > 1) [extraneous gtrans]
+   94- don't reset pariErr in err_recover()
+   95- highlevel.c had become dependent from gp.c
+XR 96- incorrect output of quadray(D,,1) when D>0 and rayclass is trivial
+   97- typo in lllall_trivial --> matkerint([0;0]) didn't work
+BA 98- incorrect object for gerepileupto in gscalcol
+   99- try to detect bad input (e.g quad. form not > 0) in (integral) LLL
+XR100- typo in nffactor ("keep the value of i")
+IS101- work around an obscure gcc bug (gcc-2.96, alpha-linux) in stark.c
+  102- rnfcharpoly(nfinit(y^2+1),1,1) --> SEGV
+BA103- tan(t_COMPLEX) gave bogus result
+BA104- trace([;]) --> SEGV
+
+  Added
+IZ  1- [gnuplot] look up directory tree for gnuplot related files
+    2- improved on-line help wrt to defaults and ambiguities
+       (? default/some_default, ? some_default)
+    3- library functions gentimer, genmsgtimer, get_timer
+HC  4- optional argument to direuler (length of result)
+    5- low-level kernel function for integer squarings (twice faster)
+    6- error trapping in library mode and under GP
+    7- break loop to investigate errors under GP
+    8- "sprintf-rounding" under GP
+IZ  9- string justification in high-res plot
+IZ 10- bit operations ( bit[ and | or | neg | negimply | xor ] )
+XR 11- add new flag value in bnrstark/quadray: try to find a better modulus
+   12- Configure flags to indicate where gmp/readline library/headers are to
+       be found
+
+  Changed
+    1- default binary produced by Configure -g is now static
+    2- improved the heuristics in ellisoncurve
+HC  3- extend the range of direuler (maxp was 2^16)
+    4- trial divide by the "private primes" before the primality tests
+    5- don't double stack automatically
+    6- disable ff_poltype correction (for lack of decent finite fields) in gmul
+    7- slight optimization to hnf / hnfmod (skip zeroes)
+XR  8- nfsqff: choose a prime ideal with few factors + better heuristic bound
+IS  9- default CFLAGS on linux-alpha
+   10- improved factorcantor / factorff
+   11- default multiprecision kernel is GMP if the library is installed
+
+  Removed
+    1- sunview "support" (didn't work, hard to test, X11 is a better standard)
+
+===========================================================================
+Done for version 2.0.17.beta (released 24/09/1999):
+
+  Fixed
+XR  1- typo in zarchstar (result possibly incorrect when more than two places)
+    2- check_unit not severe enough ([;] --> session could die !)
+    3- wrong error message when using global var as argument to user function
+    4- typo in boundfact(t_FRAC) --> SEGV
+    5- buchall: don't compute xarch if not needed
+    6- remove limit on string size + silent overflow, e.g Str(10^5000)
+    7- "break status" not checked often enough: [break, 1] --> SEGV
+    8- rewrote errcontext (use print_text to fit messages on terminal)
+BD  9- added workarounds against some MSVC annoyances (Windows version)
+IZ 10- typo in Gnuplot.h
+   11- typo in Configure ("2>&1 >/dev/null" replaced by ">/dev/null 2>&1")
+   12- simplified incgam[23] + typo (mulrr --> gmul)
+IZ 13- typo in LD_LIBRARY_PATH setting (benches)
+   14- inconsistency in gadd(0., FRAC) (should give 0. if FRAC small enough)
+   15- libraries/headers ordering inconsistent in Configure (report: IZ and KO)
+   16- typo in ff_poltype [symptom: (Mod(1, y^2 + 1)*x + 1)*x --> garbage]
+   17- qfbprimeform(4, [2]) --> SEGV
+   18- overflow in  factorcantor(2*x^3+3*x^2+x, 2^31-1)
+   19- memory corruption in apell1 + no garbage collection,
+       rewrote the function to parallel apell0, replaced hashing by sorting,
+       use Montgomery's trick, removed the p < 10^25 limit
+IS 20- cc -64 warning in mp.c:vals()
+   21- bad free in gp_expand_path
+   22- extract([;], "..") --> SEGV
+   23- not enough GC in hnfall
+   24- recover gracefully when precision too low in lindep/algdep
+IS 25- cleaned up unused variables, fixed some missing casts
+   26- algdep(x) --> undefined behavior when x exact. Replace by decimal
+       approximation
+IS 27- C++ keywords (new, class) occurred as variable names
+   28- arguments of ideallistarch not checked
+   29- Mod(t_FRAC or t_PADIC, t_INT) --> garbage
+   30- gcmp1(t_REAL) always false: polylog(2, 1) --> error
+   31- (spurious) compiler warnings about variables used before initialization
+   32- memory (possibly) freed twice in buchall + unsafe allocation of matcopy
+   33- online help for matsnf not updated
+   34- rnfequation() didn't check its arguments correctly
+   35- removed spurious special case in compute_class_number()
+   36- typo in the compatibility macros mpabsz / mpnegz (missing ;)
+   37- "lost pointers in gerepile" in bnfisprincipal(Q, ...)
+   38- bnfisprincipal(..., Pol(0)) accepted
+   39- [Configure] Makefile name didn't necessarily match object directory
+   40- zetak(nfz, integer + 0.) --> stack corruption
+   41- for(i=1, ..., zetak(nfz, any complex number)) went slower and slower
+   42- poltchebi(.,y), pollegendre(.,y) didn't work
+   43- confusing "array index out of allowed range" message (e.g [1-0])
+IS 44- fixed obscure compiler optimization bugs in smithall() and eint1()
+GN 45- weird results in ellisoncurve() due to misguided precision heuristics
+   46- typo in forvec([],...): readexpr() --> readseq()
+   47- check arguments in ploth()
+XR 48- rare bug in bnrL1 (wrong value!)
+   49- precision problems in polredabs
+   50- division by 0 in rnfordmax [typo in mymod()]
+   51- removed all dangerous occurences of constpi/consteuler (cf 41-)
+   52- typo in det() [forgot to divide by a pivot]
+   53- divide by 0 error in lllgramintern (precision problem)
+   54- suminf(k=1,suminf(j=1,.067^(k+j)/k^4/(k+j))) ran forever
+   55- factor(2*x + 2) --> [2*x + 2, 1] (instead of stripping the content)
+   56- (ultra-rare) stack corruption in mpqs_solve_linear_system()
+   57- rl_refresh_line() prototype changed across readline's versions
+   58- incorrect handling of INTMODs modulo integers of different magnitude
+XR 59- matsnf(3 x 3 matrix, 4) --> SEGV
+   60- some pathological bnfinit() computations (strive to get maximal rank)
+XR 61- precision problems in nfsqff() [increase precision for T2-norm bound]
+   62- time wasted checking generators of the form [x,0,...,0] in polredabs
+   63- variables deleted too late in freeall()
+
+  Changed
+XR  1- modulus choice in bnrstark
+    2- make sure that addii(x,y) returns gzero, not icopy(gzero)
+    3- cutoff in ellap to use Jacobi sums (457 --> 100)
+    4- pariputsf("%Z",(long)g): removed the casts (not needed)
+    5- cleaned up hnfspec
+    6- look for at least MIN_EXTRA extra relations in buchall
+    7- simplified calling interface to hnfspec/add
+    8  Used hnfspec in bnfsunit, changed the way S-units are found
+    8- output of bnfsunit modified (removed res[3], made res[2] suitable
+       for hnfadd)
+    9- listput and listinsert now return the inserted element, not the list
+   10- check_break_status rewritten: was very inefficient
+   11- Used parser code DG whenever it was possible (moved reorder to init.c)
+   12- faster isunit()
+   13- added GC in expr()
+   14- buchall(): increase subfactorbase without starting over
+GN 15- minor tuning in pollardbrent() for huge integers (call ECM sooner)
+   16- renamed directories dos -> Odos, o.xxx.xxx --> Oxxx.
+       Moved win32/* --> Odos
+OR 17- new version of pari.el
+XR 18- new modular round 4 implementation (nilord2)
+BA 19- improved algorithms in galconj.c
+
+  Added
+    1- an optional argument to next()
+    2- MANIFEST
+YU  3- support for FreeBSD ELF binary format
+    4- file CVS.DOC
+    5- support for Windows CE (Nigel Smart + coworkers)
+BA  6- functions related to Galois theory: galoisinit, galoisfixedfield, etc
+    7- [library] trivial function realun()
+
+  Removed
+    1- #define HIGHBITM1 (useless)
+    2- files config/tar_[include | exclude] (obsoleted by MANIFEST)
+
+===========================================================================
+Done for version 2.0.16.beta (released 29/06/1999):
+
+  Fixed
+    1- paricfg.tex (needed to compile INSTALL.tex) missing if Configure is
+       not run. Added a test in parimacro.tex
+    2- non-portable casts in galconj.c
+    3- 64-bit graph benches (extra spaces)
+IZ  4- typos in plotgnuplot.c
+    5- unnecessary plothsizes in "graph" bench (fails if no X server)
+    6- typo in ff_poltype
+IZ  7- check rectwindow in rectcopy and rectclip
+IZ  8- /opt/local/lib missing in Configure's library path
+IZ  9- [Math::Pari] needs pariErr->die _before_ pariErr->flush
+IZ 10- wrong valence code for plotcolor, plotclip
+IS 11- obscure inlining bug (pgcc 1.1.3 -O3) in stark.c:ComputeKernel0()
+   12- gcc -E doesn't define __GNUC__. Add it explicitly to KERNELCPPFLAGS
+   13- whatnow(sigmak) didn't tell the arguments have been swapped
+   14- [1]~ * [[1]] --> SEGV
+   15- lim=(av+ x * bot) >> y can overflow if large addresses are available
+       (e.g Linux...). Use lim_stack() instead
+   16- algdep(I,1), lindep([I,1]) entered an oo loop
+   17- removed the maxHastad (= 50) limit in algdep/lindep
+   18- typo in polrootspadic(,,1) --> SEGV
+   19- GC not frequent enough in idealval
+   20- in split_ideal (isprincipal), LLL-reduce first if ideal is big
+   21- factorpadic(,,,1) didn't work anymore
+   22- round 2 (= nfbasis(,2)) used too much memory
+   23- idealval did not accept all types of ideals
+GN 24- unsafe stack handling in auxdecomp
+   25- polroots[mod|padic] returned a t_VEC, not a t_COL (as polroots)
+   26- check coeffs of polynomials with coeffs in nf (rnf* functions)
+   27- typos in polredabs0 (get_Bnf + nf_RAW in storeallpols)
+BA 28- nfgaloisapply could forget to clean up the stack
+   29- typo in gp_rl.c (rl_save_prompt <--> rl_restore_prompt)
+DE 30- arch="sun4" non reconnu par Configure
+   31- uninitialized variable (prec) in quadhilbertimag
+IS 32- pari.el.in was not updated when gphelp was moved from miscdir to bindir
+   33- rnfkummer(bnrinit(bnfinit(y^2-y-1),101,1),[1,0;0,2]) --> gerepile error
+IZ 34- update gnuplot interface
+      a) Strings were put too low in gnuplot terminal;
+      b) Allow setting of output file sizes, as in plotterm("gif=300,200");
+      c) Allow querying of possible output terminals, via plotterm("?")
+      d) Update to newer Gnuplot-interface-layer (Gnuplot.h)
+         allows compilation on Linux (stdout was bad as an initializer),
+         corrects bugs in processing of terminal options
+   35- in buchall, allow minfsb to increase before doubling cbach
+       (bnfinit(x^4+5*239*x^2+5*239^2) couldn't be computed)
+   36- bnfnewprec can't handle bnf = bnfinit(,2) --> SEGV (now, error)
+   37- typo in pseudorem (didn't recognize 0 properly)
+GH 38- weird bugs in thue() on alpha: typo int <--> long in thue.c
+   39- bnrisconductor assumed moduli had small norm (< VERYBIGINT)
+   40- bnfnewprec didn't accept imaginary quadratic fields ("missing units")
+   41- inefficiency in zarchstar (VERYBIGINT --> BIGINT)
+       (rnfconductor(bnfinit(y^3+972*y-12),x^2+x+1) ran forever)
+   42- too much memory used in rnfordmax (+ removed some inefficiencies)
+   43- try to recover in nffactormod if input not prime (SEGV)
+   44- SEGV in nffactormod if degree(pol) > 100
+   45- factornf(p(x), q(x)) was accepted (and produced incorrect objects)
+   46- nfisincl / nfisiso made use of the bug above
+   47- subgrouplist(bnr) with trivial ray class group --> SEGV
+   48- background color was assumed to be "white" (by gphelp and gp)
+       Made it "transparent" by default
+   49- pages shifted by 1 in User's Manual's table of contents
+   50- Mod(x, x^2+1) + x return Mod(2*x, x^2+1), not x + Mod(x,x^2+1)
+       (fixed gadd, gmul, gdiv)
+   51- for certain flag combinations buchrayall unsuitable for gerepileupto
+       (+ cleaned up the code)
+   52- ^C in smithclean corrupt existing objects
+   53- silent overflow in qfbhclasso
+   54- nfreducemodpr didn't check its arguments
+   55- confusing error messages when precision too low in initell
+   56- polx[0] modified in nfsubfields(P(y))
+   57- various memory optimizations in bnrstark
+   58- incorrect debugging output in calc_bloc (at \g6)
+   59- rare memory corruption when garbage collecting in mppgcd
+   60- type t_STR not treated correctly in changevar
+   61- C-long overflow in ellan
+   62- memory use in the incgam* functions
+   63- bad input bug: qfperfection(indefinite matrix) --> SEGV
+   64- mateigen dropped some eigenvectors when precision was too low
+
+  Changed
+    1- err() --> parierr() [conflict with system library in Redhat 6.0]
+    2- use Doud's algorithm in elltors [initial patch: HC]
+    3- new (internal) function get_mul_table (for nfinit, padicff)
+    4- disable LD_LIBRARY_PATH before running benches
+    5- strtoGEN() --> strtoGENstr() + added flag
+    6- optimized stack usage in pollardbrent (in place)
+    7- don't try to compute units in buchrayall if bnf doesn't contain them
+    8- simplified misc/gprc.* (esp. colors)
+    9- use C long to keep track of exponent in regula
+   10- src/kernel/sparcv7/level0.s --> level0.S + include preprocessing stuff
+   11- extended matsnf flags: immediate cleanup (backward compatible)
+   12- arguments swapped in veceint1
+   13- allow @ markers (??keyword@) in extended help (was apropos only)
+   14- do all computations in mppgcd in place (as in Changed-6)
+OR 15- new pari.el (cf emacs/pariemacs.txt)
+   16- search multiple lines in apropos extended help (???key)
+
+  Removed
+    1- make test duplicated the "make bench" computations. Removed the
+       test and dotest targets
+    2 -veceint1() function. Can be reached in library mode or using a flag to
+       eint1
+
+  Added
+    1- default 'secure'
+    2- optional flag to Str()
+    3- expand environment variables in filenames
+    4- TODO file
+    5- man page for gphelp
+    6- Trivia section in the pari/gp man page
+
+===========================================================================
+Done for version 2.0.15.beta (released 21/05/1999):
+
+  Fixed
+    1- ?? (TeX mode) didn't work anymore
+    2- rl_save_prompt incorrectly detected on a.out systems
+    3- allow extra_relation() (bnfclassunit) to abort
+    4- allow cbach to double once more before triggering PLEASE REPORT
+       (introduced in 2.0.14)
+    5- check for non-monic pol in smallbuchinit
+    6- vecex<TAB> --> SEGV on some Linux systems (typo in match_concat)
+    7- exceedingly rare but possible overflow in forvec(, flag == 1)
+    8- factorff(x^7-3,3,y^3+2) --> SEGV
+DC  9- ghpelp doesn't exit when problems arise in TeX processing
+   10- made idealprimedec random again (also use det_mod_P_n to compute norm)
+   11- inefficiencies in nfeltval (computing norm is a waste of time)
+   12- did same prime twice when collecting garbage in modulargcd (=> error)
+   13- ffinit(huge prime,) output a weird error message
+   14- highly inefficient memory use in nfsubfields
+IS 15- [cygwin] typo in Makefile generation
+   16- file leak in mpqs (COMB)
+   17- allow prime_to_ideal to be called with a t_INT argument (for quadray)
+   18- typo in nf_shanks (calling Fp_shanks with wrong parameters)
+   19- polun (possibly) destroyed in bnrstark
+IS 20- keep logfile in synch with screen output
+   21- reset DEBUGLEVEL correctly on ^C (was sometimes set to 0)
+   22- bnrstark does not require anymore the modulus to be the conductor
+   23- check bnrstark's arguments in the right order (bnrstark(1,0) --> SEGV)
+   24- inefficiencies in idealval (removed element_mulh)
+   25- idealadd unsuitable for gerepileupto
+HC 26- quadray should work in all cases now and give better polynomials
+XR 27- bnrstark should return smaller polynomials
+   28- very inefficient pseudo-remainder routine (psres)
+   29- forstep(x=a,b, 1/2, ...) --> SEGV (signe -> gsigne)
+IS 30- [Cygwin] Configure fix (use $HOSTTYPE instead of uname -m)
+   31- polcoeff(a*x^-1*y^-1+O(x^2)+O(y^2),-1,y) --> 0
+HC 32- (false) accuracy problem in mpsc1 ("truncation error")
+   33- don't use mpqs_diffptr in mpqs_find_k, set it in mpqs_create_FB
+   34- (very rare) "precision loss in truncation" in buchall
+   35- C-long overflow in zarchstar
+   36- C-long overflow in zprimestar
+   37- (old versions of) GNU as not recognized correctly
+BA 38- ?? in TeX mode could not process some sections (missing macros)
+   39- factor(polynomial with t_PADIC coeffs) didn't work (factorpadic
+       assumed coeffs were integers)
+   40- T=[1,x; x,1]; charpoly(T,Z) produced object with wrong variable
+       ordering
+   41- polrootsmod(x^n,p) could corrupt the stack
+   42- Warnings/errors from HPUX and AIX native compilers
+OR 43- many problems in the emacs interface pari.el (introduced in 2.0.14)
+   44- factorpadic treated only monic polynomials
+   45- gphelp TeX mode (use general macros instead of a specific file)
+   46- under readline, input lines of length > 2048 could trigger SEGV
+   47- more thorough check in checkbid (accepted prime ideals->SEGV)
+GH 48- possible stack corruption in thueinit(totally real field)
+XR 49- various problems in bnrstark (oo loop in fincke_pohst)
+   50- in doc/Makefile: removed GNU-style make macros + put a missing TAB
+   51- sloppy garbage collecting in hnfperm
+   52- HPUX+cc: work around a compiler bug (wr_float)
+   53- warnings in make_emacs_tags (perl5.005)
+   54- Configure --static should not disable install()
+   55- in rare cases reduction not complete in lllgramall (off-by-1 error)
+   56- addshiftw (polynomial case) could produce non-normalized polynomials
+   57- take content into account in modulargcd as in srgcd (not normalized)
+GH 58- uninitialized variable in poldisc0 (purify warning)
+   59- inefficiencies in round 4: modular computations [more needed!]
+       (starting from a patch by DF+XR)
+GN 60- lllintpartial did not output the right matrix
+   61- huge memory leaks in bnrstark
+   62- signed int overflow in allocatemem
+   63- parisize (local to gp/gp.c) not updated properly
+   64- *sol not properly initialized in subresall
+   65- in idealpowred, check whether |n| < 16, not n < 16 (would slow down
+       bnfinit a lot when class number is large)
+   66- polrootsmod(x^6-10,25) --> SEGV (now error message)
+   67- SEGV when renormalizing zero series in gdiv
+XR 68- unify precision choice in bnrstark (4 different formulae used...)
+GN 69- oo loop in mpqs (64bits machines + harsh compiler) when more
+       factors were found than were hoped for (1 <--> 1L)
+GN 70- SIGFPE in buchall (double didn't fit in 32-bit integer)
+   71- also check sign in real0 (internal: called by gauss_pivot)
+   72- use current realprecision in plot() (was fixed: 28 digits)
+   73- memory use in subgrouplist(bnr)
+   74- in bnfcertify: "Too many iterations in isprincipal"
+   75- auto detect precision in quadhilbertimag ("overflow in I+R")
+   76- error message numbers in src/kernel/m68k/mp.s
+   77- polroots(x^3-x-422!) --> "impossible R-> dbl conversion"
+   78- not enough modular reductions in buchrayall (bnrinit)
+   79- technical argument in quadclassunit not read correctly
+   80- factor(x^3-1 + 0.*I) --> gerepile error
+GN 81- gp -p (close to a p^2) --> p possibly missing in the prime table
+   82- until narrow class group in quadclassunit is implemented, non-zero
+       flag raises an error
+IZ 83- some code numbers in functions_basic (for Math::Pari)
+   84- precision problem in initell (AGM)
+   85- "impossible assignment I-->S" in ellrootno
+   86- missing break in poltype (factor)
+
+  Changed
+    1- ?? (no arguments) opens the users'manual in xdvi
+    2- print readline version in header on startup
+    3- compute multiplication table first in element_mulvec[row]
+    4- nf[5][7] is now stored in two-element form (faster ideal inversion)
+    5- install gphelp in BINDIR, not MISCDIR
+    6- simplified output of trivial matrices
+    7- replaced square_free_factorization by mysquare_free_factorization
+    8- call LLL before using idealtwoelt
+GH  9- choose random elements in idealtwoelt, and allow bigger ones in two_elt
+   10- cleaned up lllgramall/lllgramintern + give quality ratio as argument
+   11- in rnflllgram, try to survive lllgram errors (findmin)
+   12- simplified rnfpolredabs (simple interface to polredabs). Should be
+       much more efficient
+   13- replace many gdiv by 1 ginv + many gmul in sqred1intern
+   14- optimized polredabs (always do the initial polred now: it is for free)
+   15- more efficient ideal multiplication in random_relation (bnfinit)
+   16- library names nfhermite[mod|basis] <--> nfhnf[mod|basis]
+   17- subgrouplist function (use Birkhoff algorithm)
+   18- improve recovery in lllgramintern
+   19- improved checkgenerator in polredabs (look directly for double roots)
+   20- for consistency, return S-units in t_POL format (as fundamental units)
+IZ 21- revamp gnuplot autodetection by Configure
+BA 22- nfgaloisconj(nf, 4) uses Allombert's algorithm instead of Kluners's
+
+  Added
+    1- install-doc target
+    2- make install makes a symlink pari.1 --> gp.1
+    3- ??tutorial/refcard opens tutorial/refcard.dvi in xdvi
+    4- posibility to extract the complement in vecextract
+HC  5- Ducos's subresultant algorithm (polresultant(,,2))
+HC  6- accept a "vector of steps" in forstep
+    7- examples/classno.gp
+XR  8- new function bnrL1
+    9- new function gdivexact (used in subresultant, Gauss-Bareiss...)
+   10- new function bnfnewprec
+   11- optional argument to \l and \e shortcut
+   12- nfinit,polred[abs] accept input of the form [pol, HNF basis for Z_K]
+   13- q-Pascal triangle (matpascal(n,q))
+   14- file src/basemath/subgroup.c, new function forsubgroup()
+IZ 15- function plotclip
+IZ 16- new file examples/taylor.gp (nice example for plotclip)
+BA 17- new file galconj.c
+
+===========================================================================
+Done for version 2.0.14.alpha (released 05/03/1999):
+
+  Fixed
+    1- factormod(2*x+1, 2) --> "factor for general pol. not implemented"
+IK  2- gphelp could leak temporary files
+BD  3- for native Win32 build: buffer overflow in get_home, incomplete
+       paricfg.h, typo in try_pipe (#endif misplaced)
+    4- nfsubfields: could miss subfields if index > 1
+    5- removed 3 useless setrand(1) in subfields.c
+    6- rare and obscure memory bug in inverseimage (lost pointers in gerepile)
+    7- memory bug in factorpadic4 (prime not copied before result)
+    8- memory consumption in round2 (allbase)
+GT  9- m68k version: duplicate symbols from mp.c
+IS 10- Cygwin: in mpqs.c, open files in binary mode (otherwise fseek goes crazy)
+   11- bnfisprincipal().gen gives the expected answer
+IZ 12- signatures for Math::Pari in highlvl.c / init.c + GNUPLOT
+       set-output-file problem
+   13- if we think readline needs libiberty, check whether it's installed first
+DB 14- PowerMac: header inclusion in highlvl.c, lround def'd in system headers
+XR 15- rnfkummer makes sure to return an integer
+XR 16- rnfconductor accepts polynomials with rational coeffs
+   17- polredabs + internal precision change --> nfnewprec forgets nf[5][1]
+   18- don't log lines twice when pariecho is set
+   19- glitches in content() (e.g content("a") --> SEGV)
+   20- get_sep(2) reacted incorrectly to empty strings
+   21- rootmod could destroy its argument
+   22- Fp_pow_mod_pol(x, 1,...) should return gcopy(x), not x
+   23- galoisconj1 computed wrongly the precision needed (+ prototype change)
+IZ 24- remove __OPTIMIZE__ dependence (define GCC_INLINE instead)
+   25- off-by-1 error in apell1
+   26- misleading error message in minimalexponent()
+XR 27- in nffactor, forgot to update precision when increasing exponent
+       (+ various typos)
+   28- sparc + non-gcc --> kernel2 possibly not included: compilation failure
+   29- when echo is set don't print prompt if line is empty
+   30- ellpointtoz sometimes return -x instead of x
+   31- gp -b 1 --> hangs gp
+DE 32- many glitches in manual
+   33- mathnf(x, 3) when x hasn't maximal rank (SEGV)
+   34- differences in buchall 32bit/64bit (PRECREG too high. bnfinit(x^13-6))
+   35- various inefficiencies in nfshanks ("module too large in nfshanks")
+   36- memory leak in buchall (= bnf*)
+   37- "not a definite matrix in lllgram" after a call to bnf*
+   38- typo in factorff(x^3+2,3,y^2+1) --> SEGV
+   39- [internal] missing tags (e.g zprimestar) after make ctags
+   40- idealstar(*, big prime) --> cryptic error msg (more tolerant now)
+   41- have Mod(0,1)^-1 return Mod(0,1) (fixes znstar(prime))
+   42- ginv(-1/2) --> 2
+   43- heap leak when using local()
+   44- overflow in addssmod
+   45- changed the syntax of round()
+   46- sqrt(1. + O(x)) --> oo recursion
+IZ 47- don't create GP variables when expanding strings
+   48- internal variable 'parisize' not properly updated after allocatemem()
+IS 49- config/display didn't work properly with ActivePerl
+IS 50- Configure didn't handle most recent Cygwin
+   51- polinterpolate([],[]) --> SEGV
+   52- adapted gp_rl.c to readline 4.0
+   53- glitches in whatnow database (new file src/gp/whatnow.c)
+   54- all warnings from gcc 2.8.1
+   55- uninitialized tetpil in gscal() (bnfreg(x^2+1))
+   56- precision problems in polgalois (degree > 7)
+   57- config/locatelib prompted a lot of useless work (returning too many
+       libraries) and could pick up the wrong library
+   58- very rare bug in nfbasis (discriminant ok, but wrong basis)
+   59- quadclassunit: make sure sub factor base is big enough (oo loop)
+   60- more flexible "colors" default (initial work IZ)
+
+  Changed
+    1- x.fu outputs polynomials (as all bnf* functions), not polmods
+    2- improved mppgcd (including vali)
+    3- improved division vector/scalar
+    4- \x to print a * in front of out-of-stack moduli (t_[INT|POL]MOD)
+IZ  5- better commandline usage message
+    6- don't buffer log messages going to pari.log
+    7- accept [nf, t_POLMOD] where nf is expected
+    8- poldisc() and quadgen() accept an optional variable name
+    9- when defining user function check for duplicate variable names
+   10- [internal] is_entry always uses function_hash (use is_entry_intern)
+OR 11- pari.el updated (see emacs/pariemacs.txt)
+   12- removed shiftl from lgcdii
+   13- use roots_to_pol in polgalois
+   14- precision heuristic in gauss_get_prec
+   15- cleaned up lllall and lllgramall
+   16- sort the output of nffactor, nfroots, factorff and factornf
+   17- nfgaloisconj now guaranteed to find all conjugates (use nffactor)
+   18- Removed flag 1 in nfisisom/nfisincl: function checks its arguments,
+       and uses the best algorithm (both guaranteed complete). Changed the
+       names (nfiso/nfincl) in library mode to match GP usage
+   19- gerepile(ltop,lbot,0) no longer returns ltop - lbot
+   20- improved floor and round(t_FRAC)
+   21- removed some duplicate code from nffactor
+   22- passing a pointer to GEN in a GP funciton now explicitly requires an &
+IZ 23- more verbose error messages for online help
+   24- increased a bit poltschirnaus's period
+   25- DOS version: don't use more for external help
+   26- cleaned up buchall code
+   27- removed unnecessary gres from polarit1.c:to_fq()
+
+  Added
+    1- apropos command in gphelp (-k switch), ??? under GP
+    2- function global()
+    3- function gp_variable to use sums, etc in library mode
+    4- [internal] new functions mulmat_real
+    5- new functions gerepileupto[leaf|int]
+    6- function znlog()
+IZ  7- added default(color, "yes" / "no")
+    8- --static flag to Configure
+GH  9- inline alpha assembler (gcc specific)
+
+  Removed
+    1- isinclfast/isisomfast
+    2- rounderror
+
+===========================================================================
+Done for version 2.0.13 alpha (released 14/12/98):
+  Fixed
+IK  1- Configure hangs on FreeBSD systems
+RD  2- Roland's patch (2.0.11: Fixed- 42) had been incorrectly applied
+    3- stack corruption in glcm (if operands > 10^155)
+    4- index wrap-around in polsubcyclo (n > sqrt(2^31))
+    5- incorrect placement of strings in psdraw
+    6- memory corruption in rhoimag0
+    7- fix up memory debug mode (\gm) when switching to alternate stack
+    8- memory corruption (new_chunk + mulii) in gmul/gdiv (t_FRAC)
+    9- escape chars in GP strings sometimes parsed twice (e.g Str("\\") --> "")
+   10- nfinit(x^18+16) --> impossible inverse mod(0,2) (bug in eltppm()
+       introduced in 2.0.12)
+   11- weird SEGVs due to variable handling (changed the 'bloc' structure)
+   12- moved highlvl.c to src/gp (libpari was missing symbols from plotport)
+   13- polcyclo ignored its second argument (introduced in 2.0.12)
+TP 14- minor fixes in paridecl.h (poldivres, gredsp: C++ compiler exits)
+HC 15- still sign problems in resultant (see 2.0.12: Fixed-58)
+   16- (Solaris):GNU ld doesn't like empty object files: remove kernel2.o
+IZ 17- make bench forgot to treat the install() BUG in a special way
+   18- factor(x^2 + I) --> SEGV (also with t_QUADs)
+   19- factornf(x^3+1,y^2-1) --> stack doubling (now output error msg)
+IZ 20- for Math::Pari fix valence of factormod()
+IZ 21- outfile rename pari_outfile (conflict with gnuplot headers)
+   22- DEBUGLEVEL could be modified and not reset upon interrupt
+IK 23- make perl -wc gphelp happy
+   24- incorrect use of gettime() in the library (nffactor) --> wrong timings
+       (in particular for the 'nfield' bench)
+   25- polred/polredabs used different types for output. Made both t_VEC
+   26- remove duplicate polynomials in polredabs(x,4) and polred
+XR 27- precision fixes in modules/[nffactor|stark].c
+   28- in compatible mode, \precision didn't work anymore
+   29- typo in Round4 (case "p small" and "p huge" were swapped)
+   30- incorrect prototype for conductor in compatible mode
+   31- factor((x^2-1)/2) --> SEGV (factpol assumed integer entries)
+   32- memory usage in Round2: nfbasis(*, 2) (also cleared some inefficiencies)
+IK 33- typo in primitive_pol_to_monic
+   34- deplin did not check its arguments
+   35- quadray() assumed variable "y" was not in use
+
+  Changed
+    1- glength() returns a C-long integer
+    2- in hnfmodid(x,d) reduce mod d immediately
+    3- allow vector(n), and matrix(n,m)  (filled with 0s)
+    4- under GP, wait for input if line ends with '=' (cf \)
+    5- wait for input when a line ends with an '=' sign
+    6- modified extended help to (potentially) search the whole manual
+    7- in nfdisc, specific error message if discriminant is 0
+    8- use divide_conquer_prod() in factorback()
+IZ  9- in test suite, add setrand(1) in front of poltschirnaus
+IZ 10- M-( bound by default
+IZ 11- improved gnuplot support
+   12- improved gcmp[1|_1](t_REAL)
+OR 13- new pari.el (see emacs/pariemacs.txt)
+
+  Added
+    1- members e, f, p, gen for prime ideals
+    2- mathnf([M, M2]) computes mathnf(M), updating M2 (equivalent to
+       v=mathnf(M,1) then M2*v[2], but much faster if M2 is small or over a
+       finite field)
+    3- local() keyword for user function definitions in GP
+IZ  4- new functions plotfile(), plotpointsize()
+IZ  5- splines for ploth (flag 256)
+
+  Removed
+    1- \k metacommand
+
+===========================================================================
+Done for version 2.0.12 alpha (released 06/11/98):
+
+  Fixed
+    1- GNU as incorrectly treated by Configure
+GN  2- various fixes in mpqs.c (incl. file descriptor leak)
+GN  3- [From 2.0.11- Fixed 14] floating point exponents: 1E1 --> possibly 0.1
+    4- 1 % Pol(2) still wasn't right [cf 2.0.11- Fixed 31]
+    5- src/test/dotest for DOS boxes (running sh)
+    6- removed unreachable err() in factor()
+    7- system() is defined under EMX, so make it available for DOS
+    8- possibly use / as path separator under EMX, check COMSPEC and EMXSHELL
+    9- compatible = 3 downcased all the following lines in gprc and caused
+       incorrect behaviour of preprocessing statements
+   10- unified default commands + better checks (e.g: default(log,0)->SEGV)
+   11- memory leak in gp_main_loop: bufferlisit wasn't reset on error
+   12- gptimer() not initialized properly if ^C was first command
+   13- extra space (sometimes) output by command line completion in DOS
+       version
+   14- modifying histsize could corrupt the history stack
+   15- incorrect error messages in gsqr
+   16- not enough garbage collection in rootmod (polgcdnun)
+TP 17- compilation using cc -64 on 64-bit SGI
+IS+GN 18- "(hit return to continue)" message did not flush stdin
+GN 18- ECM rewrite
+   19- incorect absi_cmp in buch3.c
+   20- useless garbage collecting in sqred2
+   21- O(1)^(1/2) --> SEGV
+   22- forvec(i=[],...) --> SEGV
+GN 23- deriv(x*y, y) --> 0
+GN 24- inefficient GC in ispseudoprime()
+   25- fixed some (not all) compiler warnings (char * --> unsigned char *)
+   26- exp(too large number) now gives a meaningful error message
+AW 27- getrusage still not detected on FreeBSD 2.2.5
+GN 28- off by 1 error in initprimes() (--> not enough calculated primes)
+   29- inefficiencies in factor(t_POL)
+HC 30- bug in rnfelementabstorel for Mod(scalar, t_POL)
+HC 31- typo in gaussmoduloall
+   32- idealred(principal ideal) always returned an archimedean part
+HC 33- idealpowprime wrong for negative powers
+   34- typo in binomial(n,k) (wrong answer if n<=k)
+   35- online help for polinterpolate
+   36- incorrect garbage collecting in quickmulii/quicksqri
+   37- reformatted the output in test mode (gp -test)
+   38- polroots((x-7)*(x-8)*(x+16)) took far too much time
+   39- ellap assumed ell was given in characteristic 0
+IZ 40- outlook of plot function (better labels, better choice of chars)
+RD 41- when factoring over a non prime finite field and found a p-th power,
+       forgot about Frobenius
+GN 42- rare memory bug in ellfacteur
+   43- gscalmat was not suitable for gerepileupto
+   44- polredabs(,2) didn't handle non-monic polynomials
+   45- check for various overflows (x ^ 1000000, etc.)
+   46- printtex(I) --> missing closing brace
+   47- setisset() did not check that elements were strings
+   48- typo in gdiv(t_POLMOD, t_POLMOD) with different variables
+KO 49- missing ; in level1.h (Windows specific code)
+   50- forprime(p=0,10,) indeed started at p=0
+   51- (cf 2.0.11 Fixed-1) put back 3 cgeti in galois.c (were necessary)
+   52- factorpadic could overstate the precision of the result
+   53- (very) rare memory corruption in allhnfmod (when cleaning up)
+PM 54- prototypes in paridecl.h (fussy IRIX compiler), cc -64 in MACHINES
+   55- factormod implemented for all primes (p = 2 and p > 2^31 called
+       factorcantor which was much slower)
+   56- check for unsuitable input in [factor|roots] (SEGV for multivar. pol)
+   57- polresultant sometimes gave the wrong sign
+   58- extraneous space in err(impl,"") (= "sorry,... not implemented")
+   59- aliases treated incorrectly during error recovery (--> obscure bugs)
+   60- obscure bug when normalizing rational functions with real coeffs
+       (corrected content())
+
+  Changed
+    1- paricfg.h in dos and win32 extracted by Configure before the release
+       (to get version number right)
+    2- GPRC logic: try $GPRC, then look in $HOME, /etc (/ and C:/ under EMX)
+    3- make sure the output of pari_unique_filename() doesn't exist already
+    4- use a stack of files to gracefully handle errors/interrupts without
+       leaking file descriptors
+    5- replaced fixed-size buffers by dynamically allocated ones (es.c/gp.c)
+    6- rename INSTALL.QUICK --> INSTALL.DOC (updated)
+              Changelog --> CHANGES (looks better under DOS)
+    7- mention ?12 in the header
+    8- use ; (instead of :) as PATH separator under DOS, OS/2 or Windows (for
+       drive letter)
+GN  9- ECM tunings
+   10- check for overflow in cget* (instead of silent wraparound)
+   11- gp_main_loop cut into (improved) pieces
+   12- simplified normalizepol
+   13- Warn when trying to replace an existing function with install
+       (previously error)
+   14- new function mpcopy. replaced some inlined function (rcopy, absi,
+       absr, negi, negr) by compatibility macros
+   15- gphelp now uses GPDOCDIR and GPTMPDIR
+   16- improved rational arithmetic by computing smaller gcds (gredsp removed)
+GN 17- improvements in MPQS (use less memory, count relations precisely)
+   18- text-mode (non-TeX) extended help printed screen by screen
+   19- retuned integer multiplication, and made polkaramul the default
+       polynomial multiplication
+XR 20- stark.c rewritten
+IZ 21- updated the pariperl interface
+   22- improved handling of t_INTMODs (less GC)
+   23- improved (a lot) factoring/root finding for intmod polynomials
+   24- modified poldivres to avoid computing remainder when useless
+   25- removed inefficient shiftl/shiftlr from the kernel
+   26- from the same sources, Configure can now simultaneously run on
+       different architectures
+   27- polynomial factorizer now sorts the factors (increasing degree)
+   28- ?an_obsolete_function now calls whatnow
+   29- .pol operates also on t_POLMOD
+   30- random() argument can have arbitrary length
+IZ 31- with gnuplot, pick a sensible terminal when X11 is not around
+IZ 32- change valences for use with Math::PARI
+IZ 33- various interface patches (new file highlvl.c, different prototype
+       for foreignAutoload...)
+   34- optimized permute()
+   35- setrand, getrand, getstack, gettime return a C long, and not a GEN
+   36- pari_randseed no longer global. Don't reset random number generator
+       when entering certain functions
+   37- improved smallvectors() (correcting the "not enough storage" bug)
+   38- improved computation of special polynomials ([sub]cyclo,tchebi,legendre)
+   39- read() and extern() are timed as a whole now
+   40- check if LONG_IS_64BIT is correctly defined in pari_init (in case we
+       include the wrong pari.h)
+GN 41- use Lehmer-Jebelean to compute inverse mod p (TODO: extended gcd)
+   42- moved subcyclo() to bibli2.c
+   43- improved polredabs, suppressed flag 8
+   44- improved ground(), case t_REAL
+   45- modified the internal SMALL nf structure (add matrix M, for polredabs)
+
+  Removed
+    1- doc/Makefile.SOS, since make should succeed even if Configure failed
+    2- many error messages from the analyzer (referer*, trucer1,
+       matvecter...), better handled by talker2
+    3- obsolete test %_ in bench
+    4- polkaramul(), which is now the default multiplication
+    5- factmoder error message
+    6- obsolete function polredabsfast
+
+  Added
+    1- `pipes' for DOS running EMX, i.e extern() and extended help are
+       available (perl needed for the latter)
+    2- file handling functions pari_fopen, pari_fclose, pari_unlink
+    3- new default `debugfiles'
+    4- file README.DOS
+IZ  5- target etags in top Makefile
+    6- target ctags
+IZ  7- gnuplot support
+HC  8- elliptic functions package (ellzeta, ellwp, ellsigma)
+HC  9- quadray function, extending quadhilbert
+   10- files src/basemath/polarit3.c and src/gp/highlvl.c
+   11- user-defined member functions
+   12- possibility to choose sizeof(long) at Configure time when the
+       hardware suports it (eg. MIPS)
+
+===========================================================================
+Done for version 2.0.11 beta (released 30/07/98):
+  Fixed
+    1- removed all dummy cgeti (--> new_chunk)
+    2- stack corruption in gcarreparfait (t_INTMOD)
+    3- incorrect Fq-loop in apprgen9
+    4- removed useless normalize in gdivgs, gdiv
+    5- some {} Warnings from gcc -Wall (unjustified, but doesn't hurt)
+    6- incorrect zero series return by deriv
+    7- gaffsg(, t_PADIC) misused the valuation (symptom: deriv((1+O(2^2))*x^2)
+    8- online help for ellinit
+GN  9- default gp built without X11 in presence of some versions of xmkmf
+   10- ggval: zero series + simplified the code in there
+   11- subst(O(q),q,x) --> O(q)
+   12- newtonpoly did not treat correctly zero coefficients
+IK 13- getrusage incorrectly detected (at least on Linux/FreeBSD machines)
+   14- constante() used far too much memory (+ an int should have been a long
+       + an lg should have been an lgefint). Reading in a huge bnf needs much
+       less memory now
+   15- y[2] checked in divri instead of is_bigint (see 2.0.10, Changed 2)
+   16- powgi, default case: missing gcopy + incorrect gerepilemany if y==NULL
+       Also, uniformized random GC with gpowgs
+   17- stack corruption in hil(x,y,p) when typ(x) > typ(y)
+   18- SEGV if DISPLAY was unset and hi-res routine under X11 is called
+GN 19- ispseudoprime(negative integer)
+   20- removed -static from the CFLAGS of profiling version (didn't build)
+   21- linear algebra routines involving polynomials with real coeffs
+   22- compiles properly under DOS + EMX
+GN 23- Warnings from C++ compiler (include unistd.h and sys/ioctl.h where
+       needed)
+   24- znprimroot(0) --> oo loop
+GN 25- various fixes in mpqs
+GN 26- add safety parentheses to macros in paricom.h
+   27- polroots needed too much precision (two extra words)
+   28- factor(1. * x + I) ---> rubbish or SEGV (bug in polynomialtype automat)
+   29- possible address wrapparound in gerepile* (cast to ulong)
+   30- in gerepile: useless special case for t_SER
+   31- Pol(1) % 1 returned 1, not 0
+   32- matdet([x1,1,1/x1; x2,1,1/x2 ; x3,1,1/x3]) returned wrong result
+       (call to gdeuc should have been gdiv in all cases in det())
+   33- SEGV in powmodulo (access garbage pointer just before exiting)
+GN 34- various problems in rho and mpqs
+
+  Changed
+    1- simplified detint, fibo
+    2- simplified GC and optimized gmul: t_SERxt_SER, t_POLxt_POL, and gsqr
+       (same types)
+    3- streamlined binomial
+    4- replaced all abusive cmpsi by the relevant egalii
+    5- prototype for error types [gmuler|gadder|gdiver][fi], assign[ri]
+       using new function type_name
+    6- uniformized the test suites (make test*,bench,...)
+GN  7- add random GC in mppgcd. Use modified plus-minus algorithm (new cgcd)
+    8- format of bench files (to reduce size)
+    9- cleaned up factor and polynomialtype
+   10- matdet tries to return a significant 0 when called with a
+       non-invertible argument, e.g
+         matdet([1+O(3),1+O(3);O(3),O(3)]) --> O(3), not 0
+   11- made the output of make bench/test slightly more informative
+
+  Removed
+    1- mpkaramul
+    2- error messages expter1 and gaffer13
+
+  Added
+    1- misc/gprc.dos a sample gprc for DOS boxes
+    2- Configure --prefix=dir is now recognized (in addition to -p)
+===========================================================================
+
+Done for version 2.0.10 beta (released 09/07/98):
+  Fixed
+    1- integer factoring engine (ECM): sisprime killed N
+XR  2- file closed twice in MPQS
+GN  3- bad argument checking in [next|prec]prime
+BD  4- warnings from MSVC compiler
+    5- warnings from purify (vpariputs + puissii)
+    6- sample program and Makefile in examples/
+    7- v=...; forvec(a=v, v=...) ==> SEGV
+LG  8- support for HP running NextStep
+GN  9- have checkmemory (in cget*) check for overflows
+LG 10- avoid a bug in cc compiler (version 4.2) under Solaris (in factmod())
+LG 11- some missing prototypes and typecasts (for C++)
+GN 12- add LOCAL_HIREMAINDER in mulssmod (factor(17!+1) => SEGV on some PCs)
+   13- non portable pari_is_rwxdir
+GN 14- lots of typos in the documentation
+
+  Changed
+GN  1- add debugging output to ECM
+    2- forvec implementation (+ new flags)
+GN  3- rewrote the integer factorizer (use Pollard-Brent + improved ECM +
+       new MPQS code from 2.0.9)
+    4- disabled pari-matched-insert under Emacs
+    5- reorganized vecsort & co
+    6- in library mode, classno3 --> hclassno
+XR  7- improved the nffactor module
+HC  8- elllseries (use ellglobalred + ellrootno)
+
+  Added
+    1- new function in library mode readGEN()
+HC  2- new GP function ellrootno
+
+  Removed
+    1- error message vecsorter2
+    2- functions vecindexsort, veclexsort (use vecsort with flag)
+
+===========================================================================
+Done for version 2.0.9 alpha (released 16/06/98):
+
+  Fixed
+    1- typo in qfbhclassno (SEGV when result in (1/3)Z)
+    2- too much memory allocated in factmod /factcantor (use clones)
+    3- removed the -DREADLINE_LIBRARY hack, use <readline/readline.h> and
+       not <readline.h>
+    4- version 2.0.8 did not compile with readline 1.*
+    5- after gaffect(0, padic), padic was unsuitable for further gaffect
+    6- length(a string) gave number of non code words, not string length
+    7- factorpadic(polynomial of degree one) did not convert coeffs to padics
+    8- reduction mod p^r forgotten in gaffsg(s,t_PADIC)
+    9- default(realprecision,,1) did not return # significant digits
+   10- typo in nfmodprinit (return x instead of 1-x)
+   11- matinverseimage did not check its arguments
+   12- mathess([;]) --> SEGV
+   13- matid(-100) --> SEGV
+   14- mateigen([;]) --> SEGV
+   15- matmultodiagonal([;],Mat(1)) --> SEGV
+   16- vecextract([;],"..") --> SEGV
+   17- introduced in 2.0.8 (Added 3-): aliases/user function + <TAB> ==> SEGV
+GN 18- (t_RFRAC) ^ t_INT took an unreasonable amount of time
+   19- prevent quick succession of ^C from corrupting memory in recover()
+   20- hyperu could enter an oo loop due to round-off errors
+XR 21- matadjoint(Mat(n)) returned Mat(n), not Mat(1)
+   22- matadjoint([;]) returned [[;]] (???)
+   23- wrap some long error messages
+XR 24- inefficiencies in rnfpolredabs
+XR 25- nffactor (wrong format for discriminant computation + problems with
+       unseparable polynomials + compute disc only once)
+HC 26- typos in kummer.c
+GN 27- check environment variable LINES, not ROWS
+   28- introduced in 2.0.7 (Fixed 7-): lift(Mod(O(2^0)*x, x^3 - 2)^4) was a
+       zero polynomial with non-zero sign (=> pb when normalizing in poldivres)
+   29- timer always returned 0 if times() was used (e.g linux-alpha)
+   30- kill'ing the argument of a user function corrupted the function
+   31- make clean did not remove pariinline.h
+   32- incorrect memcopy in identifier, case 's' (removed * sizeof(long))
+   33- online help for ?. (nf.nf does not exist)
+   34- lisseq0: gnil not respected after break/next. avma=av too brutal
+       after return
+   35- ellwp assumed precdl > 3
+   36- under emacs, \c + hit return froze emacs (Emmanuel Kowalski)
+   37- (f()= f()=x); f; didn't set f properly
+GN 38- comments in anal.c
+   39- qflllgram([;]) --> SEGV
+   40- no online help for bernfrac
+   41- from 2.0.6 (Fixed-8): in padicff2, forgot to raise ideal to power e
+
+  Changed
+    1- improved (trivially) gneg (case t_INTMOD), gtopoly
+    2- use macro is_bigint() instead of tests (ulong)x[2] < VERYBIGINT
+       (led to typos as in Fixed-4 in 2.0.8)
+    3- improved poldivres by replacing many gsub by 1 gneg + many gadd
+    4- in pvaluation check for small integer
+    5- internal function rnfelement_*mod (prhall=NULL instead of gzero)
+    6- improved mathess
+    7- added error message "inconsistent data in" in parierr.h
+    8- gpui[gs] renamed to gpow[gs]
+GN  9- improved probable-primality tests ('end matching')
+   10- moved pseudo primality and ECM stuff into ifactor1.c
+GN 11- raise to an integer power using left-shift binary (new functions
+       powi and powgi)
+   12- add some details in the online help headers
+GN 13- nextprime, precprime now accept real arguments
+TP 14- improved the alpha micro kernel (addllx and subllx)
+   15- simplified poltschirnaus
+   16- cleaned up identifier(): #ifdef __hpux__ + call_fun() modified
+   17- taylor() improved (one gerepile removed)
+   18- unified the treatment of zero series
+   19- gerepilemany faster and more efficient memory-wise (copy to heap first)
+GN 20- improved stack checking in lllgramall
+
+  Removed
+    1- global variable defaultpadicprecision
+    2- function compute_prhall (nfmodprinit is better)
+    3- function [g]pseudopremier (miller[rabin] better)
+
+  Added
+    1- function egalii
+    2- support for cygwin32 in Configure (Andy Stubbs)
+    3- new function gunclone to delete a clone (killbloc should be used by
+       the analyser only)
+    4- comments in anal.c
+TP/XR 5- new function factorint(), using MPQS (_experimental_)
+
+===========================================================================
+Done for version 2.0.8.alpha (released 07/05/98):
+
+  Fixed
+    1- improved garbage collecting in polroots
+    2- zetak did not check its nfz argument properly
+    3- warnings from MSVC
+GN  4- isprime() did not work for numbers in [2^(BIL-1), 2^BIL-1]
+GN  5- precprime sometimes missed a prime
+    6- quaddisc(x in Q\Z) did not always work
+    7- powering of zero series
+    8- factorization of null matrices (SEGV)
+    9- mateigen for non-diagonalizable matrices (SEGV)
+GN 10- oo loops in some arithmetical functions when arg = big prime
+GN 11- cleaned up paridecl.h
+   12- gimag/greal for type t_RFRAC/t_RFRACN (fix: Terje Sparre Olsen)
+GN 13- ordinal numbers to number components seen through \x
+HC 14- lllgram: incorrect gabage collecting in first "warnmem"
+   15- theta(q exact, z) entered oo loop
+   16- typo in gdiventres (x<-->y in last line)
+MS 17- gtrunc for p-adics when valp(x) < 0
+MS 18- polresultant(u+v,u-v,v) => x + u;  polresultant(u+v,u-v) => 2*u
+MS 19- issquare(Pol), where Pol(0) = 0
+IZ 20- use 15 points (by default) for recursive plotting as documented
+   21- matsnf for singular matrices (supersedes a patch by GN)
+LG 22- micro kernel support for HPUX
+LG 23- compilation with Sun's C++ compiler (version 4.2)
+   24- SEGV when factoring polynomials of huge degree (removed expos[100],etc)
+   25- garbage collection in gpuigs
+GN/BH 26- micro kernel support for ix86 running SunOS
+HC 27- handling of 0x0 matrix in some hnf* functions
+HC 28- bug in discrayabslist* (try bnrdisclist(bnfinit(y^2-2),200,,1) in 2.0.7)
+
+  Changed:
+    1- moved hnf and snf from base1.c to alglin2.c (base1.c too big)
+    2- use gexpo in linear algebra functions when entries contain real
+       numbers (work for inexact polynomial entries now). Still does not
+       work properly for p-adics
+GN  3- in sigma() fall back to numbdiv() or sumdiv() when k < 2
+    4- made comments started in file through read("file") local to file
+       (not so \r file)
+    5- valuation(0) now returns VERYBIGINT instead of raising an error
+MS  6- simplified gegal in case t_FRAC
+IZ/GN 7- faster initprimes, using less memory
+LG  8- improved Configure
+
+  Added:
+    1- new function write1
+MS  2- new Lisp-like quote operator 'a
+IZ  3- readline: electric parentheses, move across balanced expressions,
+         add formal arguments to completion of GP command (when unique)
+    4- micro-kernel for hppa
+    5- quiet mode (gp -q) to suppress headers
+
+===========================================================================
+Done for version 2.0.7.alpha (released 21/03/98):
+
+  Fixed
+XR  1- SEGV in get_regulator for imag. quad. fields
+    2- "beautified" output (still ugly, less buggy, ok for simple objects)
+    3- error during "print()" could change output default
+    4- SEGV when syntax errors in gprc
+    5- make install failed when libpari.$sodest had been removed
+    6- moved term_width and term_heigth to es.c
+HC  7- inefficiency in mulii (Karatsuba used too easily)
+XR  8- many problems in stark.c
+XR  9- bugs in rnfpolredabs (+ new flag)
+GN 10- _many_ typos in tutorial and user's manual. New, much nicer, layout
+   11- whatnow not robust enough + faulty call by err_new_fun ==> SEGV
+   12- idealadd treated incorrectly the 0 ideal
+   13- default(realprecision) gave wrong value when format had been changed
+   14- bnfs structure was inefficient for applications (inverted 2nd comp.)
+   15- buffersize was incorrectly updated during complicated read()
+   16- one-line comments "ignored" if buffersize too small
+   17- using eval on object containing killed variables caused a SEGV
+   18- contfrac lost last term when first parameter was rational and
+       numerators were supplied
+   19- problems when dividing with polynomials/series whose leading coeff is
+       non-exact 0
+BD 20- missing #ifdef ZCAT in es.c
+BD 21- check in paricom.h whether min / max are already defined
+BD 22- universal_constants freed early in freeall() (problems on Windows NT)
+BH 23- update Makefile.dos (nf.h --> parinf.h)
+BH 24- changed kernel/ix86/level0asm.c (FUNBEGIN/FUNEND + ALIGN)
+   25- cleaned the gauss_pivot functions + garbage collecting in gauss()
+   26- mathnfmod did not check its second argument
+   27- since 2.0.4 (item 24) install did not work anymore on FreeBSD + gp-dyn
+   28- cleaner malloc in plotX.c (to remove Warnings when debugmem > 0)
+   29- slightly optimized matdet (gsub --> gadd(,gneg))
+   30- ? x=1; Pol(1)
+         ***   variable name expected: x,n,
+                                       ^---
+       is fixed everwhere (wherever an optional variable name is expected)
+       setting "x" to some value is now safe
+IZ 31- nicer looking plot() function
+   32- warnings while building for m68k arch
+   33- sqrt(Mod(1,2)) went into an oo loop
+   34- ?? did not resolve aliases
+   35- besselk near integers entered an oo loop
+   36- p-adic sqrt (bad valp)
+   37- hnfmodid could output wrong results (wrong diagonal) and wreck the
+       powering of prime ideals
+   38- added garbage collecting in izeta
+   39- ??a_number now works as gphelp
+   40- in prettymatrix format, matrices 0xn and nx0 are always printed as [;]
+   41- 1 - "a" ==> SEGV
+
+  Changed
+    1- reorganized output functions (es.c)
+    2- have mulir check if integer is small
+    3- part of GENtostr inlined (check_output_length())
+HC  4- functions where it makes sense now admit an optional argument for
+       "variable number" (intformal, deriv, things having to do with
+       polynomials, etc)
+    5- lines of any length can be input interactively (previously 1k at most)
+    6- remove {} and \ from readline history
+    7- improve treatment of sample programs in gphelp -d
+    8- uniformized debugmem messages
+MSo 9- simplify the coinit function
+   10- changing the function set through default(compatible,) no longer
+       resets installed functions
+   11- renamed nfker-->nfkermodpr, nfgauss-->nfsolvemodpr
+   12- simplified/extended dummycopy
+   13- listput gives more informative error messages
+
+  Added
+    1- default: lines
+HC  2- (made known to GP) functions nfmodprinit, nfkermodpr & nfsolvemodpr
+XR  3- function bnrstark
+    4- install code D& (optional pointer)
+    5- function name_var (to use after fetch_var)
+    6- concatenation of lists (or row vectors) of objects (overloaded concat)
+    7- ranges for vecextract (eg. vecextract(x, "1..3"))
+
+  Removed
+    1- useless code 'F' in analyzer
+    2- perl directory (moved the files in ./perl to ./doc)
+===========================================================================
+Done for version 2.0.6.alpha (released 22/02/98):
+
+  Fixed
+    1- \x (voir2) did not always print the correct number of words
+    2- in changevar, type POLMOD, modulus and polynomial were interchanged
+    3- all occurences of former header file names in the documentation
+    4- (from 2.0.5. item C5) using allocatemem in a script aborted file reading
+    5- expanded the documentation for bnrrootnumber
+    6- factor(pol. with rational non integer coeff) could corrupt the stack
+    7- Euler gave wrong results when prec > 80502 digits (also cleaned up Pi)
+    8- cleaned up ggcd, grando0, tayl and factorpadic2
+    9- is_scalar_t --> is_const_t in gvar/gvar2
+IZ 10- Configure and example Makefile for OS/2
+   11- idealprincipal did not accept n x 1 matrices
+   12- idealhnf(nf,a,b) did not work for quadratic fields
+   13- matsolve[mod]([;],.) could corrupt the stack or accept incorrect input
+   14- modules/galois.c unnecessarily included <sys/stat.h>
+BH 15- symbol name problem in level0asm.c (cancels patch by IZ)
+XR 16- fixes in stark.c
+   17- cleaned all occurences of HIGHVALPBIT and HIGHEXPOBIT
+   18- serconvol assumed main variable was "x"
+   19- (x + O(x^2))^(3/2) gave a stupid error message
+   20- types not checked correctly in gtoser
+   21- check more seriously arguments to default()
+   22- in Makefile: added some missing $(RM), changed an "ln -s" in $(LN)
+   23- extra '\n' after print1 + sequence of warnings
+   24- simplified a statement in addii()
+   25- typo in classno() (classno(-200183): "division by 0") + cleaned classno
+   26- all compiler warnings + most of lint's
+   27- make install would not work anymore if emacs was not found
+   28- HNF reduction not always complete when rank is small compared to dim
+   29- gphelp -d did not handle ~ properly (+ cosmetic changes)
+
+  Removed
+    1- unused files src/kernel/ix86/{asmi386.h,asmi386inline.h}
+    2- useless macro gcopyifstack and global variable RAVYZARC
+    3- useless error message gcder1
+    4- useless (undocumented) function fasthnf
+
+  Changed
+    1- simplified isonstack
+    2- extended valid inputs for matsolvemod
+    3- don't output a '\n' before an empty matrix
+    4- mpsincos no longer static (so that it can be installed)
+    5- reorganized gp_initrc() to cater for for non-UNIX arch
+    6- the "obsolete function" message now launches whatnow() directly
+       (and caters for the special cases "i" and "o" now)
+    7- improved stack management in hnf/allhnfmod/fasthnf
+    8- moved powering functions to trans1.c
+    9- disable logfile while reading .gprc
+   10- (slightly) the output of whatnow()
+
+  Added
+BH  1- support for DOS build using EMX (dos directory + fixes)
+    2- function cotan() and bernfrac()
+    3- a code for pointers (&) for analyzer and install()
+    4- ??readline now includes info about completion and online help
+
+===========================================================================
+Done for version 2.0.5.alpha (released 07/02/98):
+
+  Fixed
+BH  1- LOCAL_HIREMAINDER added twice more in mp.c
+LG  2- some symbols declared extern in gp_rl.c to avoid compiler warnings
+LG  3- isprime could corrupt the stack (isprime_proto removed)
+LG  4- possible redeclaration of macro from system header (MAX in bibli1.c)
+    5- typos in the user's manual
+    6- removed unused error numbers and fixed their ordering in mp.s
+    7- a ; or : after read/extern was not always correctly taken into account
+    8- a bug in polroots (possible SEGV in very rare cases) (Paul Zimmermann)
+
+  Changed
+LG  1- (huge) reorganization of PARI kernel and headers
+    2- logfile example in gprc.dft to take advantage of "time expansion"
+XR  3- stark units module rewritten
+HC  4- rnfpolredabs improved
+    5- errors now cause GP to close any file it was reading instead of going on
+    6- increased the static limit for the number of files opened simultaneously
+
+  Added
+XR  1- function bnrrootnumber
+
+===========================================================================
+Done for version 2.0.4.alpha (released 26/01/98):
+
+  Fixed
+    1- recovery on startup was not correctly disabled
+GN  2- pari.el (see emacs/pari.el-changes)
+    3- "" missing around a DLLD caused Configure to fail if shared library not
+       available
+    4- component of GEN could be created before its root in gadd(t_SER, t_SER)
+    5- nffactormod did not like big primes
+    6- removed the `.' binary concat operator introduced in last update
+       (broke semantics). Instead Str() argument evaluated in string context
+    7- solve did not find some obvious zeroes (solve(x=-2,1,x) for instance)
+    8- Configure -pg did not work
+    9- 68k version didn't work: corrected mp.s, moved some code & defines
+   10- subst(x,variable(x),1) did not work
+   11- flag acted contrary to doc in matsolvemod
+   12- in rare cases the prompt could still start at column > 1
+   13- bnfissunit much faster now
+   14- idealval could make mistakes with non-integers
+   15- Mat([1])[1,] gave a stack error
+   16- zetakinit(x-1) as well
+   17- zetakinit(K, even integer) gave a wrong result whenever r1(K)>0
+   18- typo in whatnow(hermite) and ?bnfsunit
+   19- missing newline in user error after print1
+   20- various typos and omissions in chapters 1, 2 and 5 of manual
+   21- (x-x)==(y-y) returned FALSE
+   22- polfactormod(f,0) gave a SIGFPE
+   23- some missing #ifdef UNIX
+BH 24- install() now works for gp-sta under Linux and OSF
+BH 25- gcc Warnings in gp.c + es.c
+BH 26- problems when installing from a different non-priviledged account
+BH 27- inefficiencies in mpinline.h (replace memory access by a constant)
+
+  Changed
+    1- when logging mode is on, record command line as well as result, and
+       flush buffer often
+    2- subdirectory lib now called misc. Changed some filenames in it
+    3- defaults psfile and logfile are now run through strftime
+    4- for benches total time now taken into account ([BUG] was excluded)
+    5- noerr is now the LAST error message (for CLISP interface)
+    6- cleaned path expansion (default(path,...))
+    7- renamed types.h and cast.h (prefixed by "pari")
+    8- renamed type Rect to PariRect
+DB  9- kernel/kerPPC.s replaced by kernel/kerPPC.c
+   10- made static the arrays in check_isin() (for the Mac port)
+BH 11- overflow/hiremainder use local variables as much as possible
+   12- prompt not printed during a batch job, unless echo is set
+       commands not echoed twice in interactive mode
+
+  Added
+    1- gplogfilter script in misc
+    2- check for strftime in Configure
+    3- support for alpha running linux (include portability fix on keralpha.s)
+    4- file MACHINES
+    5- made message for "array index out of range" error more precise
+    6- more frequent garbage collecting in mathnf
+BH  7- inline asm for i386
+GN  8- support for native compiler on AIX
+
+  Removed
+    1- support for dynamic linking on AIX (did not work)
+
+===========================================================================
+Done for version 2.0.3.alpha (released 13/01/98):
+
+  Fixed:
+    1- rare bug in gadd (PADIC + PADIC) which caused one of the arguments
+       to be overwritten
+    2- typos in refcard
+    3- galois.c still couldn't compile on the HP (_INCLUDE_POSIX_SOURCE)
+    4- introduced in 2.0.2: item 14 used gexpo incorrectly (bnfinit sometimes
+       did not give units it could have computed)
+    5- replaced INFINITY by pariINFINITY in rootpol.c (cancels Warning on OS/2)
+    6- on OS/2, target ../gp-$dft in o.xxx/Makefile could not be built
+       (extraneous $exe_suff)
+    7- command line switches so that one can enter them with or without white
+       space (new function read_arg in gp.c)
+    8- setdcolors so that it can't unset disable_colors if we are under emacs
+    9- random did not check its argument correctly (random(2^32) was accepted)
+   10- b=10; for(a=1,b, b=2) exited immediately, whereas the upper bound is
+       supposed to be evaluated only once
+   11- reorganized the error recovery system (initially because errpile could
+       cause SEGV on Linux systems)
+   12- typo. problems in doc and refcard (interletter spacing in $nfz$...)
+
+  Changed:
+    1- Reorganized Makefile.SH: extraction twice as fast
+    2- pari.menu and pariemacs.txt rewritten, pari.el updated
+    3- expanded the man gp.1 to mention command line switches
+    4- the implied input from non-interactive input command (like extern and
+       read) does not go into the GP history (%x) anymore (it never went into
+       readline's). The final output (value of last expression evaluated) of
+       course still does!
+    5- updated chapter 5 of the User's Manual (removed obsolete information)
+
+  Added:
+    1- better settings for handling the Meta key under readline in
+       examples/Inputrc
+YU  2- support for shared libraries under FreeBSD
+    3- colors under Emacs
+       1) emulate exactly the "colors" default after a M-x gp
+       2) .gp files edited get a special highlighting
+    4- a flag to default() to get the result under GP
+    5- overloaded the "." (member) operator to concatenate as strings if LHS is
+       a string
+    6- .gprc accepts some limited preprocessing directive (#if READL and
+       #if EMACS (and #ifnot as well)). Updated lib/gprc.default to reflect the
+       changes
+
+  Removed:
+    1- some unused, undocumented functions (allocatemem(), checksqid())
+       made static some other (op_ReIm)
+
+===========================================================================
+Done for version 2.0.2.alpha (released 15/12/1997):
+
+  Fixed:
+    1- typos in the documentation for the random() function
+    2- removed an extra -emacs flag in pari.el
+    3- decodefactor was incorrectly remembered by whatnow (it's factorback now)
+    4- test mode did not prevent all prompt expansion (==> bug in make test)
+    5- gphelp stopped abruptly when meeting a cross-referencing macro
+    6- zetainit now aborts cleanly when disc. too big (caused memory fault)
+    7- exceedingly rare bug in the printing of real numbers (missing decimals)
+    8- too early rounding in polroots which in rare cases made GP think some
+       error had happened
+    9- text overflowed the manual pages (and tutorial) on non-A4 paper
+   10- polred incorrectly assumed that nf arguments were totally real (in a non
+       critical part: that just led to some unnecessary computations)
+   11- polred(f,2) could try to overwrite universal integer gzero
+   12- check if we are using GNU as or GNU ld in Configure
+   13- empty -R argument to $CC when building GP without graphics (==> link
+       failed)
+   14- bnfinit acts sensibly when fundamental units are too large (before:
+       "overflow in R*R")
+   15- cleaned up buch2.c (removed ideallllredpart1, removed gerepile in
+       class_group_generators(), not_given now called from getfu)
+   16- nfgaloisconj(...,2) was unusable (tried an illegal multiplication)
+   17- is_totally_split was very inefficient (==> nfgaloisconj was very slow)
+   18- some unimportant typos (gexpo) in polgalois
+   19- compiling without readline gave a Warning in gp.c (already_hist)
+   20- some make programs don't like $< (suppressed from doc/Makefile)
+   21- ellap sometimes assumed wrongly that coeffs of the curve were integers
+   22- flag 0 and 1 in ellap had been mixed up
+   23- print an extra \n before an error message if last output did not
+       include it
+   24- component(any non recursive type) gave a SEGV
+   25- suminf / prodinf / prodeuler assumed they treated a real expression
+   26- lindep / algep had problems with numbers having a rational component
+   27- last significant digits of bessel* and hyperu were wrong (now only the
+       last one is)
+   28- expi now returns a long as documented (expi(gzero) returned 0 on 64-bit
+       machines!)
+   29- g++ could not compile libpari.a (casts missing, extraneous extern "C",
+       faulty inline, etc.)
+   30- cleaned up the enums in gp.h
+   31- incorrect target veryclean in doc/Makefile
+
+  Changed:
+    1- '_' is now valid in GP identifiers
+    2- removed subsections from table of contents. pages in the manual are now
+       numbered consecutively
+    3- gexpo now accepts exact 0 arguments (return -HIGHEXPOBIT)
+       gexpo for complex numbers now return max(gexpo(Re), gexpo(Im))
+    4- parts of lib/gprc.default
+    5- the second argument of subgrouplist is now optional
+
+  Removed:
+    1- buggy label/goto functions
+    2- (now unused) error messages: labeler, gexpoer2
+    3- _ as shorthand for conj()
+    4- (useless, undocumented) function gnormalize
+    5- some files in the lib directory (functions, gp)
+
+  Added:
+    1- some files in the lib directory (README, pari.xbm, xgp)
+
+==============================================================================
+Done for version 2.0.1.alpha (released 29/11/1997):
+
+  Fixed:
+    1- multiple factors forgotten when factoring univariate pols over Z
+    2- extraneous modifications of the random seed (period of random
+       generator was ridiculously small for some buchxxx functions). Change
+       the bench results (in a non essential way)
+    3- bad terminal size determination
+    4- aliases incorrectly killed (possible SEGV)
+    5- incrementing/decrementing array elements with the (valid) syntax
+      v[i]++ / v[i]-- caused a weird error message
+    6- subgrouplist() could end up by a SEGV on Linux systems
+LG  7- on HP-UX, flag -Aa not taken into account in Configure (caused it to
+       fail on has_TIOCGWINSZ.c)
+LG  8- added a missing #define _INCLUDE_POSIX_SOURCE in galois.c
+       (7 & 8 independently fixed by OV)
+GN  9- the script examples/cl.gp called vecconcat() instead of concat()
+GN 10- make install-sta rebuilt gp-sta unnecessarily
+GN 11- many, many typos in the tutorial
+   12- tu / fu applied incorrectly to a bnfclassunit
+   13- bnfclassunit did not output a valid object (matrix whose elements were
+       rows instead of columns)
+   14- conversion bug from t_QUAD to t_REAL/t_COMPLEX
+   15- off-by-1 error in the history recovery after an error
+   16- in fprintferr() (debug messages), embedded %Z did not work correctly
+   17- rnfequation over Q yielded a SEGV
+   18- string() in compatibility mode corrupted the stack
+   19- it is now safe to have colours in prompt and input line under readline
+   20- default colours restored upon exiting
+   21- default colors in lib/gprc.default used 0 instead of -1 for "no color"
+   22- psi and lngamma could give wrong results when the argument was not real
+   23- the make test-graphic bench was missing a newline
+   24- Pol(break) gave a SEGV
+   25- x=1; Pol(1) gave a stupid error message
+IZ 26- tutorial.tex was unnecessarily rebuilt
+   27- typos in ggcd (cases nobody will ever access: gcd of a non-reduced
+LG     fraction with an intmod, etc.)
+LG 28- incorrect handling of integrals of vectors
+   29- gcd of polynomials with non-exact coeffs gave stupid results (they are
+       still often wrong, since the mathematical notion is rather imprecise)
+   30- typos in rnfkummer (incorrect flag handling)
+   31- typo in idealpowprime (negative exponent gave wrong denominator)
+   32- multiplication t_COMPLEX x t_COMPLEX used 4 mult. instead of 3
+   33- some modular functions (j, f, f2) rewritten to take advantage of new eta
+       function (trueta)
+
+   Changed:
+    1- ?? (gphelp) starts in detex mode (-d) from a console window. tmp files
+       now placed according to the $TMPDIR environment variable (in /tmp by
+       default)
+    2- /usr/local/lib/pari/data is a better place for the Galois resolvents
+       (which are not included yet in the standard distribution)
+    3- The example for prompt in gprc.default to discuss escape sequences under
+       readline
+    4- Configure now starts by searching the toplevel directory for a temporary
+       readline installation
+    5- The output of default(colors) was confusing. It is a string now
+    6- third argument of polinterpolate can be arbitrary and is now optional
+       ("x" by default) (it had to be numeric)
+    7- directory configure renamed config, some of the Makefiles in there as
+       well (to avoid confusion)
+    8- the low_stack macro to facilitate dynamic stack expansion
+    9- if, back to GP main loop, the last command was a print1(), output an
+       extra newline. This way the prompt is guaranteed to be anchored on
+       column 1 (suppresses a readline display bug as well)
+   10- Due to 9, pari.el now supposes the prompt starts in col. 1
+   11- Better handling of version numbers (LG)
+   12- decodefactor() renamed to factorback()
+
+   Removed:
+    1- The (unused, undocumented) Malloc_Procs functions and macros
+    2- The (now unused) error message numvarer
+    3- some (useless, undocumented) targets in the top Makefile
+    4- (useless, undocumented) function polgcd()
+
+   Added:
+    1- This file !
+    2- new flags -ch, -cb, -cu (colour support) to gphelp (see its header)
+IZ  3- OS/2 (+ enough tools...) supported by Configure
+    4- file examples/Inputrc (example of .inputrc for readline)
+    5- Weber f1 function implemented
+    6- Karatsuba multiplication t_REAL x t_REAL (development code, not used by
+       PARI yet). Test it with install if you wish
diff --git a/CHANGES-2.4 b/CHANGES-2.4
new file mode 100644
index 0000000..86bee0c
--- /dev/null
+++ b/CHANGES-2.4
@@ -0,0 +1,1537 @@
+# $Id$
+Bug numbers refer to the BTS at http://pari.math.u-bordeaux.fr/Bugs/
+
+Done for version 2.5.0 (released 31/05/2011):
+
+  Fixed
+BA  1- DESTDIR did not work. [#1194]
+BA  2- Improve Darwin shared library support.
+LGr 3- bnfinit -> oo-loop if class number is divisible by 27449 [#1197]
+BA  4- [gp2c] error("foo") did not work.
+    5- wrong generators in quadclassunit(D > 0) [ fix qfr3_pow / qfr5_pow ] [#1195]
+    6- setrand(45);quadclassunit(185477) -> SEGV [#1200]
+    7- fix derivnum for large arguments [#1201]
+    8- "#if READL" always evaluated to "FALSE" in gprc [#1202]
+    9- matsnf(non-square matrix, 4) --> SEGV [#1208]
+HC 10- fix p-adic Gamma inefficiency for small p: gamma(O(2^20))->no answer
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.4.4 (released 29/04/2011):
+
+  Fixed
+BA  1- FpX_gcd did not garbage-collect for large input.
+BA  2- 'make all' failed to build the documentation.
+    3- typo in polgalois() when new_galois_format is 0 : returned [3,1,2]
+       instead of [3,1,1] for C_3
+    4- rnfkummer was using too much stack.
+    5- nfgaloisapply did not work on extended ideals.
+    6- Strprintf and printf0 prototypes were wrong.
+    7- Mod(4*x+1,Mod(1,2)*x) -> SEGV [#1116]
+    8- gcd(t_INTMOD, t_FRAC) -> SEGV
+    9- qfjacobi failed on a matrix of zeros (or in presence of 0s of large
+       exponent) [#990]
+   10- t_SER / t_SER where denominator has 0 leading term -> SEGV [#1120]
+   11- (t_COMPLEX of t_INTMOD) + t_PADIC -> SEGV [#1121]
+   12- (t_QUAD of t_INTMOD) + t_PADIC -> SEGV [#1122]
+LGr13- bnfinit: various performance improvement.
+BA 14- several instance of random garbage collection were broken.
+BA 15- [install]: support for D0,G, and D"str",s, in prototype code.
+BA 16- [gp2c]: foo(x:mp)=... did not work.
+   17- when the divisor's leading term was 0, RgX_divrem(,,&rem) created rem
+       before the quotient (invalidating the use of cgiv() suggested in the
+       documentation) [#1129]
+   18- gadd(t_COMPLEX of t_REAL, t_QUAD = 0) -> SEGV [#1131]
+   19- log(11+11^2+O(11^3)) -> bug in log_p [#1136]
+BA 20- (ffgen(ffinit(2,10))*0)^-1 -> oo-loop
+BA 21- ispseudoprime(-3,0) != ispseudoprime(-3,1)
+BA 22- text form of a closure now include the closure context.
+BA 23- reading compressed files could corrupt the stack.
+BA 24- t_CLOSURE==t_CLOSURE and t_CLOSURE===t_CLOSURE did not work.
+   25- gequal1(0.*x + 1) returned 0
+   26- ispower((100!)^2) -> 0 [#1155] Affected pure powers divisible by more
+       than 22 of the 26 primes <= 101. (introduced in 2.4.3)
+BA 27- LLL could fail (rarely) due to insufficient precision [#1154]
+   28- rnfisnorminit(y,x^2-Mod(2+y,y)): polynomial not in Z[X] in nfinit [#1157]
+   29- factorff failed to merge identical irreducible factors [#1159]
+BA 30- ploth(x=0,1,0,"Complex|Recursive") -> SEGV [#1163]
+   31- mathnf(dim > 8) could return a non-HNF upper triangular matrix [#1153]
+BA 32- forsubgroup() did not handle break/next/return.
+   33- modular resultant, e.g. rnfequation(polcyclo(17,y), x+y^6,1) [#1151]
+   34- gequal1(x) / gequalm1(x) did not implement gequal(x, 1) for t_COL/t_MAT
+   35- matsize(vecextract(M,0,y)) -> [;] instead of matrix with 0 and the
+       requested number of columns [#1170]
+   36- readline completion : allow commas ',' in directory names [#105]
+   37- List() == List() returned 0.
+   38- [Configure] C_INCLUDE_PATH was not used when looking for libgmp
+BA 39- polroots(1) returned [] instead of []~.
+   40- a succession of <C-C> in breakloop context would reinitialize timer
+   41- matsnf(matdiagonal([x,0])) --> [x,0] instead of [0,x]
+BA 42- qfbred(large discriminant, small prec): precision too low in truncr [#1173]
+PMo43- erfc(large x) was slow and inaccurate [#364]
+   44- erfc() : allow t_COMPLEX inputs
+   45- stack corruption in ifac_decomp_break() [#1115]
+   46- factor(0) returned 0^1, but factorint(0) raised an error. Allow
+       factorint(0), core(0), coredisc(0), etc.
+   47- ellwp, ellzeta, ellsigma only used realprecision, instead of the input
+       precision [#1184]
+BA 48- tuning for 32bit and 64bit, with or without GMP5.
+   49- idealintersect(K, 1/2,1/2) -> 1/4 instead of 1/2 [#1192]
+
+  Changed
+LGr1- universal constants (gen_0,gen_1, etc.) are now read-only
+   2- qfjacobi : sort eigenvalues by increasing order
+   3- [libpari] gtoser : add a third precdl argument.
+   4- [libpari] swap the last 2 arguments of ellwp0 (prec and precdl)
+   5- ellwp now outputs a number of terms equal to the default seriesprecision,
+      instead of *twice* that number.
+   6- Ser, ellwp, taylor, elltaniyama: add an optional 'precdl' argument (by
+      default equal to 'seriesprecision'). One no longer has to rely on (and
+      change locally) a global variable to handle conversion to power series.
+LGr7- add information to the version() output [backward compatible if version()
+      used as recommended in the documentation] [#1130]
+   8- O(x^0) is now printed as is, not as O(1). t_SER and t_PADIC are now
+      treated in the same way.
+   9- inverse trigonometric functions (acos,asin,atan,acosh,asinh,atanh):
+      values on the branch cuts changed to conform to standards, e.g.
+      "implementations shall map a cut so the function is continuous as the cut
+      is approached coming around the finite endpoint of the cut in a counter
+      clockwise direction." (ISO C99) [initial patch Richard Kreckel, #1084]
+BA 10-[libpari] nf_get_TrInv renamed to nf_get_diff
+   11- Qfb() : forbid square discriminants (-> corrupted objects [#1145])
+   12- allow gequal1 / gequalm1 to return 'true' for t_SER
+   13- follow-up to (2.4.3, C104) [#1156]:
+       - stop using the undocumented feature Z_factor_limit(x,1) = Z_factor(x).
+       - abide by the "lim" parameter in all cases :
+          factor(100,1 or 2) -> [10; 2] (was [2,2; 5,2])
+   14- include GP defaults in the function description system + use standard
+       hashtables instead of ad hoc types
+   15- cleanup and document term_color() and term_get_color()
+BA 16- prototype of summations functions now is fun(void *E, GEN call(void*, GEN),...)
+BA 17- traversesubgroups renamed to forsubgroup.
+   18- t_QFR with the same coefficients but different distance component are
+       now tested equal by == (they are still different according to ===).
+   19- bnfinit no longer outputs a warning when fundamental units couldn't be
+       computed (annoying and rather useless) [#1166]
+   20-[Configure] no longer support a link to an uninstalled readline library
+      in PARI toplevel
+   21- thue() no longer outputs a Warning when the result is conditional on
+       the GRH.
+   22- [libpari] rename TIMER -> timer_delay, TIMERread -> timer_get,
+       TIMERstart -> timer_start, msgTIMER -> timer_printf
+BA 23- polrootsff now returns a t_COL
+   24- default(a, b) used to return "b". Now returns gnil
+   25- [libpari] remove allocatemem0 [make it private to GP], write allocatemem
+   26- matdet() : allow Guass-Bareiss to develop somewhat wrt. rows/columns
+       [ cf test O1 in Lewis-Wester's bench ]
+   27- addprimes() now includes its argument as-is (used to take gcds,
+       making an insertion linear in the table size instead of O(1); filling
+       an inially empty table was quadratic in the final table size). They
+       must be true primes, otherwise number theoretic routines may return
+       wrong values. [#322]
+   28- rename fprintferr -> err_printf, flusherr -> err_flush
+   29- ellpow (CM case). Try to determine the discriminant of the
+       endomorphism ring first [#1186]
+
+  Added
+BA  1- trap keyword "syntaxer" to trap syntax error from eval.
+BA  2- Flx_shift now support negative shift
+BA  3- [gp2c] add description _(_) for closure evaluation
+BA  4- PARI function FpX_halfgcd() and subquadratic FpX_gcd(), FpX_extgcd()
+BA  5- PARI function Flx_halfgcd() and subquadratic Flx_gcd(), Flx_extgcd()
+BA  6- random() now allow to draw random polynomials
+BA  7- PARI functions pol_0, pol0_Flx, pol0_F2x
+VB  8- [mingw] --datadir=@ option to Configure
+BA  9- PARI functions FlxqX_div
+   10- PARI function ismpzero
+BA 11- PARI function bnr_get_clgp
+   12- new default 'histfile' (to save readline history in between sessions)
+   13- PARI function cmp_universal
+BA 14- [gp2c] add descriptions to support intnum/suminf type functions.
+BA 15- function diffop
+   16- PARI function zero_Flm_copy
+   17- PARI function mantissa_real
+BA 18- PARI functions F2v_clear, F2m_clear, Z_to_F2x, F2x_set, F2x_clear, F2x_flip, F2x_coeff
+BA 19- PARI functions F2xq_sqrt, F2x_deriv
+BA 20- PARI functions FpXQ_trace, Flxq_trace, F2xq_trace
+BA 21- PARI functions FpM_det, Flm_det, F2m_det
+
+  Removed
+BA  1- PARI function CM_CardEFp (ellsea script deprecated).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.4.3 (released 08/10/2010):
+
+  Fixed
+    1- \h m-n no longer worked
+BA  2- some error messages displayed internal token names (e.g for '1)
+    3- named colors in 'graphcolormap' were not portable across graphic drivers
+    4- setunion(Set(),Set()) -> SEGV [#714]
+DE  5- [Solaris] pari.desc won't compile [#715]
+    6- 822.pm broke the formatting of inline program examples
+    7- memory leaks in pari_close_opts [ freeep(), parser stacks, some
+       defaults... ]. FIXME: calls to getpwuid introduce (small) leaks
+    8- ZV_Z_mul might not reset clonebit.
+    9- nfeltreducemodpr --> gerepile errors [#716]. Also SEGV for residue
+       degre > 1 (when p does not divide the index)
+JD 10- write1("/dev/null", foo);1 --> extra newline
+JD 11- is_dir_stat() gave incorrect results on special files (pipes,...)
+JD 12- read() would block on pipes
+   13- nfeltreducemodpr used different canonical representatives than all other
+       modpr routines [#719]
+   14- rnfconductor(,,1) did not recognize some abelian extensions [#718]
+       (when the discriminant of a defining polynomial for the absolute number
+       field could not be factored). Related to Changed-4.
+   15- polgalois could confuse S_11 and F_110(11) if a certain polynomial
+       discriminant could not be fully factored. Related to Changed-4.
+   16- a = 0./x; a+a --> 0.
+BA 17- [from 2.4.2] use of pointers in recursive expressions [#717]
+BA 18- [from 2.4.2] trap() did not restore evaluator states [#722]
+   19- simplifications missed when adding t_RFRAC [#721]
+   20- gtofp(1 + 0*I, prec) should not return 1. + 0.*I but 1.
+       gtofp(0 + (tiny real) * I, prec) should make sure the real part is
+       converted to a zero of sufficiently small exponent (otherwise the
+       modulus of the result is zero, because 1E-100 + 0E-28 = 0E-28)
+   21- factor(500501^36): impossible assignment I-->S
+   22- quadray(-7,6) [or other "impossible conductors"] --> oo loop [#725]
+   23- acosh([Pol(1)]) -> gerepile error [#728] (same asin, asinh)
+   24- rnf functions sometimes do not reduce their output when relative
+       extension has degree 1 [#727]
+   25- zetakinit(bnfinit(...)) -> gerepile error
+   26- zeta(1-2^31 or 1-2^63) -> SEGV [#730]
+BA 27- priority of << had become lower than priority of * (should be =)
+   28- last digit of polylogs was wrong
+BA 29- missing GC in the leftrightpow functions
+   30- bnr=bnrinit(bnfinit(y^2-229,1),5,1); rnfkummer(bnr,,3) --> oo loop
+   31- mathnf(a,1) could have negative entries if non-trivial kernel [#741]
+   32- [Configure -pg] some variables not written to pari.cfg
+   33- missing subfields in nfsubfields [#744]
+   34- [readline] <F1> or <M-h> would fail on 'fun()'. Delete trailing
+       garbage in help routine.
+   35- modular matrix inversion routines used O(n^2) inversions instead of O(n)
+   36- print1() + read() or extern() + error/warning ==> missing \n
+   37- infinite loop in nffactor [#751]
+   38- ellordinate couldn't handle t_FFELTs
+   39- eint1(10,10) completely wrong
+   40- ispower broken due to 1) uninitialized variable, 2) insufficient
+       precision [#916]
+   41- compilation fails with yacc unable to handle parse.y [ add explicit
+       rule in Makefile to prevent from generating a wrong one ]
+   42- duplicate factors in nffactor (easy cases) [#761]
+   43- ellisoncurve(e, P) didn't check that "point" P had the correct format
+   44- znlog(...,znprimroot(p)) --> SEGV [#765]
+   45- wrong signs in bnfisintnorm e.g. [bnfinit(x^3+5), 5] -> x [#767]
+   46- x/(x+2) * Mod(1,2) -> Mod(1, 2)*x/(x + 2)  [ should be Mod(1,2) ]
+   47- substpol(x/(y^3*x+1),y^2,1) -> x/(y^3*x + 1) [ should be x/(y*x+1) ]
+   48- missing sanity checks in rnfbasistoalg()
+   49- setrand(1); E=ellinit([0,0,0,1,T^4]*Mod(1,ffinit(2,7,T))) --> "weird
+       base ring; can't divide"
+   50- nffactor(nfinit(y),x*y)
+       nffactor(nfinit(y),x^2*y+1)
+       nffactor(nfinit(y),x^2*y)  --> errors or SEGV. [#769]
+   51- hilbert(1,1,1) --> oo loop [#770]
+   52- nffactor(y^2+1, x+1/2) --> x+1/2 [ not integral ]
+   53- nffactor(y^2+1,x*y+1) --> x*y + 1 [ not normalized, missing POLMOD ]
+   54- nffactor(y^2+1,x^2+2*y+1) --> x^2+2*y+1 [ missing POLMOD ]
+   55- ellpow() with CM raised a "division by 0" instead of returning [0] [#777]
+   56- ellpow(e,z,2^20*I) --> norm too large in CM.
+   57- p=x^4-x^3+x^2-x+1; tnf = thueinit(p); thue(tnf,1) -> missing solutions
+       (could occur whenever p has no real root) [#764]
+   58- missing GC in numtoperm()
+   59- harmless uninitialized memory read in mulrrz_i()
+   60- sigma(..., 2) --> SEGV [ introduced in 2.4.1 ] [#783]
+   61- tuning problems in bnfinit
+       bnfinit(polredabs(quadhilbert(-2939))) --> oo loop
+   62- minor inaccuracy in GMP kernel (affir)
+   63- missing GC in gamma / lngamma
+   64- obscure SEGV when setting 'log' in a file read from command-line
+       as gp foo.gp [#789]
+   65- check that moduli are compatible in Rg_to_Fp(t_INTMOD, t_INT)
+   66- matsolve([1;2;3],[1,2,4]~) --> [1]~  (should raise an error)
+   67- allow polredabs(x in Q[X])
+BA 68- [galoisinit] Documentation of "weakly" supersolvable was incorrect.
+   69- binomial(t_REAL or t_COMPLEX, k) was very slow for large k
+   70- quadclassunit(-3 or -4) --> one extra (obsolete) component.
+BA 71- the evaluator did not garbage collect huge expressions [#713]
+BA 72- changing a function in use caused a crash [#784]
+   73- vecsort() would not check the type of element in 1-dimensional vector
+       [ e.g. vecsort([I] now raises an error ]
+   74- RgX_val would raise a SEGV on t_POLs with exact *non-rational* 0
+       leading coeff, e.g. Mod(0,2)*x
+   75- SEGV (stack corruption) in idealval(K, rational number, pr) [#808]
+   76- All the charpoly(x,v) variants failed when v > variable(x)
+BA 77- conjvec did not support FFELTs.
+   78- memory leak in t_LIST handling
+   79- polrootsmod(,1) could return an incomplete result [when 0 was a root
+       or there was a double root], e.g. polrootsmod(x^3 + x^2 + 21*x,23,1)
+IM 80- lngamma(x) gave incorrect results (off by 2*Pi*I) when real(x) was a
+       negative half-integer.
+   81- trying to write to a binary file and failing leaked a file descriptor
+   82- long-standing stack corruption when <C-C> pressed during gclone [#458]
+   83- idealaddtoone had problems with 0 ideals [#829]
+   84- wrong results in qfgaussred() [only totally real case was correct]
+   85- uninitialized memory read in divrr (both kernels), would only affect
+       the last bit of the result, but could cause SEGV on empty stack.
+   86- After A=1/(y + 0.); B=1. + O(y); all of A*B, A/B, B/A caused an error
+   87- wrong result in ellap(E, 2) if bad reduction
+   88- 0.*x+1 == 1 --> 1 but 0.*x^2+x == x  --> 0 [ latter is now 1 ]
+   89- ffinit(2,30) --> division by 0 [typo in FlxY_Flx_div], [#859]
+   90- when unexpected exception/signal raised (^C, stack overflow, out of
+       memory), == would cancel it and silently return wrong value [#329]
+   91- reading recently freed memory in popinfile()
+   92- contfrac(1,[],-1) ==> SEGV [#865]
+   93- missing type checks in bnrconductorofchar [#867], nfhilbert [#868]
+   94- unstable evaluation of tan(t_COMPLEX); at \p9, try
+       ploth(t=-0.1,0.1,abs(tan(10*I+t)))
+BA 95- Vecsmall([1,1])^-1 returned garbage
+   96- was not handling nffactor(*huge degree* nf, *non-monic* t_POL) properly
+       --> wrong result and possible memory corruption [#870]
+   97- polredabs: excessive stack usage [#872]
+   98- besselj(0., 0) --> error [#880]
+   99- polred didn't accept non-monic inputs
+  100- factor(..., 10^100) ==> "overflow in t_INT-->long conversion" [#855]
+IZ101- missing `const's in groupid.c cause massive copying to stack.
+  102- [native kernel only] int_normalize() didn't treat properly inputs
+       of lgefint 2
+  103- gerepile errors in quadclassunit(13).gen or .cyc
+  104- from_Kronecker & FpXQX_from_Kronecker returned polynomials with invalid
+       first codeword [ unused bits not reset to 0 ]
+  105- gred_rfrac_simple(n,d) [ basic constructor for t_RFRAC n/d ] did not
+       cater for d = constant polynomial
+  106- RgX_extgcd created invalid objects in trivial cases [ gen_0 used by
+       RgX-only routines ]
+BA107- Fix detection for ppc64 running in 32bit mode.
+  108- Fp_div used remii instead of modii: Mod(3,5)-1/3 -> Mod(0, 5) [#893]
+  109- trying to prevent accuracy problems in thue() caused oo loop
+  110- wrong results in matsnf with t_POL entries [#901]
+       minpoly(matrix(4,4,i,j,i/j)) -> x^3 - 4*x^2 + x
+  111- thue(thueinit(x^3-100,1), 25) -> "Short continued fraction"
+  112- thue(thueinit(x^3-48, 320) -> missing solutions (similar problem
+       when LHS is a polynomial of degree d and RHS includes a d-th power).
+  113- completely wrong results in nfsnf
+  114- intformal(A,Y) -> 1/2*A^2
+  115- uninitialized memory read in sin / cos / exp (possibly fed random
+       final digits to the algorithm, normally truncated away before returning)
+  116- nffactor(): SEGV if "relifting" was necessary (very rare)
+  117- nffactor(): obscure "no factor" bug [when trial division found and
+       extracted *non monic* factors], #930
+  118- incorrect output type in FpX_factor [#933]
+  119- possible stack corruption in polylog() + missing imaginary part.
+  120- incorrect final simplification in idealtwoelt(nf, x, y).
+       Eg: idealtwoelt(nfinit(x^2+23), 3, 6) -> [0, 0]~
+BA121- listsort() could return a pointer referencing freed memory: make it
+       return void
+BA122- src/kernel/ix86/asm0.h had an incorrect #line entry.
+  123- rnfkummer could miss some fields ( bug in rnfnormgroup() ), e.g.
+       bnrM = bnrinit(bnfinit(X^4 - 34*X^2 + 1189,1), 5, 1);
+       rnfkummer(bnrM, matdiagonal([3,1,1]))
+  124- nfgaloisconj(y,1) -> invalid object
+  125- weird error message in ellpointtoz on loss of accuracy [#962]
+  126- ^C before inital prompt appears ==> SEGV [#965]
+BA127- ellsea(,,1) did not work
+BA128- COMPAT file was missing some functions renamed in 2.3:
+       flisexpr, flisseq -> gp_read_str; lisGEN  -> gp_read_stream
+AF129- fix for documentation of polcoeff, extern and readvec
+BA130- 1/matrix(2,2,i,j,Mod(0, 2)) -> SEGV
+  131- wrong results for qfminim(A,,,2) if minimal norm < 1. E.g. A = matid(2)/2
+BA132- besselk(0.001,1) -> SEGV
+BA133- galoisinit(x).gen was not a t_VEC
+  134- lngamma(10^1000) -> precision too low in truncr. Same for psi()
+  135- nffactor could segfault. [#979]
+  136- Mod(10,37)^10000000000 -> SEGV
+  137- missing Flx_renormalize at the end of Flx_mulspec_basecase() &
+       Flx_sqrspec_basecase() [#984]
+  138- after \y0, [1,x]*[0,0]~ returned 0 t_INT, which should be a t_POL [#983]
+  139- isprime(x) possibly incorrect on 64 bit machines when 10^15 < x < 2^64
+LM140- fix for the documentation of deriv, intformal, poldisc and taylor.
+JD141- fix for the documentation of install, matsnf and polsturm.
+LM142- general documentation spelling fixes.
+BA143- detection of exp2 and log2 was broken with C++
+BA144- support for shared library on netbsd.
+IZ145- [readline] 'foo)' + M-C-b -> infinite loop
+  146- factorff / polrootsff : wrong result if t_FFELT coefficients
+       in variable 'x
+BA147- nfisideal did not reject invalid hnf ideal matrices [#999]
+  148- v=vectorv(1);concat([matrix(0,0),v,v,v,v,v,v,v,v,v,v,v,v,v,v,v,v])
+       -> SEGV [#1002]
+BA149- minpoly([-5,0,-1,1;0,-5,-1,-1;1,1,-5,0;-1,1,0,-5]/3)
+       -> wrong result[#994]
+BA150- 2/O(5) -> O(5^-1) (should raise an error) [#1009]
+  151-  factor(13533236897) -> I/O Warning [#1012]
+BA152- usqrtsafe result could be off by one.
+BA153- faster algorithm for nfgaloisapply.
+  154- bnfisnorm: output could have constant t_POL(MAXVARN) components
+       instead of t_INT. E.g. bnfisnorm( bnfinit(x^2+10), 1690 )  [#1026]
+IZ155- [OS/2] Fix warning about string signedness.
+IZ156- [OS/2] Fix symbol checking in Configure.
+BA157- upowuu(2,31) returned a wrong result [#1031]
+BA158- [from 2.4.2] issquare(x^2+y^2) -> SEGV [#1027]
+BA159- [from 2.4.2] pointers to Vecsmall components were broken.
+BA160- [from 2.4.2] ellap could return a wrong result. [#1030]
+  161- thue() could return wrong results (ZX_Z_normalize() didn't follow its
+       documentation) [#1032]
+  162- qfminim([1,2;2,1]*1.,,,2) -> SEGV [#1033]
+  163- [doc] const_col, const_vec: typo in prototype [#1040]
+BA164- precision(0.E100) returned an invalid object.
+  165- nbits2nlong, nbits2prec, nbits2nchar : avoid overflow [#1037]
+  166- normalization problems in RgX_Rg_add & friends, leading to t_POL
+       with sign incorrectly set to 0, e.g. O(2)*x + 1 [#1042]
+  167- t_FFELT + t_FRAC -> SEGV (missing case in gadd)
+  168- polred(x) -> stack overflow [#1044]
+  169- sign of t_POL whose coefficients were all equal to 0 was not always
+       set to 0 (RgX_add / RgX_sub) [#1053]
+  170- problems with p-adic gamma function :
+       - remove extra digit in Morita's algorithm [ e.g. gamma(1/2+O(3^10)) ]
+       - better threshold between Morita and Dwork's algorithm
+BA171- qfbhclassno(x) was wrong for x<0
+  172- subst(t_LIST,x,y) returned a copy without substituting [#969]
+       substvec & substpol didn't work on t_LISTs
+BA173- RETRY macro was incorrect and could cause crash [#1064]
+BA174- factorpadic(,,,1) was actually using ROUND4 [#1064 again]
+  175- nfbasis(x^2+1,,factor(-4)) --> oo loop [#1072]
+  176- factorpadic: incorrect conversion from ZX to ZpX [#1073]
+  177- bnfinit() : possible overflow in double -> long conversion [#1099]
+  178- various inconsistencies in polinterpolate() [#1096]
+  179- rnfequation() broken when called over the prime field Q [#1079]
+BA180- F2x_divrem could call bfffo(0) which is not defined.
+  181- remove restriction on forsubgroup / subgrouplist that cyclic factors
+       of all p-Sylows should have less than 2^32 / 2^64 elements
+
+  Changed
+    1- [libpari] prototype of gpmalloc, gprealloc [ return void * ]
+    2- contfrac(x, b): no longer allow (undocumented) type t_MAT for b
+BA  3- builtin-in GP functions can now be used as closures.
+    4- allow nffactor(t_POL, t_POL), nfroots(t_POL, t_POL) [ necessary when
+       nf.disc could not be factored and addprimes() would have to be used ]
+    5- removed undocumented interface FpM_mul(x,y,NULL), and analogously for
+       FpV_dotproduct, FpV_dotsquare, FpM_FpC_mul
+    6- [library] major names cleanup; rename *lots* of functions:
+       BSW_* -> BPSW_*
+       Z_factor_limit -> Z_factor_until, auxdecomp -> Z_factor_limit
+       assmat -> matcompanion
+       certifybuchall -> bnfcertify
+       derivpol -> RgX_deriv
+       discf -> nfdisc, nfdiscf0 -> nfdisc0
+       element_div -> nfdiv, element_divmodpr -> nfdivmodpr,
+       element_mul -> nfmul, element_mulmodpr -> nfmulmodpr,
+       element_pow -> nfpow, element_powmodpr -> nfpowmodpr,
+       element_reduce -> nfreduce, element_val -> nfval
+       factorback0 -> factorback2
+       factorpadic4 -> factorpadic, factorpadic2 now static
+       fundunit -> quadunit
+       gcmp0 -> gequal0, gcmp1 -> gequal1, gcmp_1 -> gequalm1
+       Flx_cmp1->Flx_equal1, F2x_cmp1->F2x_equal1, FF_cmp1->FF_equal1
+       FF_cmp0->FF_equal0, FF_cmp_1->FF_equalm1
+       ZV_cmp0 -> ZV_equal0
+       gener -> znprimroot, ggener -> znprimroot0
+       greffe -> RgX_to_ser (+ change prototype)
+       hil0 -> hilbert, hilii -> hilbertii [ delete useless hil() ]
+       ideal_two_elt* -> idealtwoelt*,
+       idealhermite -> idealhnf
+       initalg -> nfinit, initalgred -> nfinitred, initalgred2 -> nfinitred2
+       initell -> ellinit, smallinitell -> smallellinit
+       isfundamental -> Z_isfundamental
+       ismonome -> RgX_is_monomial
+       isunit -> bnfisunit
+       matrixqz -> QM_minors_coprime
+       matrixqz2 -> QM_ImZ_hnf
+       matrixqz3 -> QM_ImQ_hnf
+       minideal -> idealmin and change prototype
+       mu -> moebius, gmu -> gmoebius,
+       nfhermite -> nfhnf, nfhermitemod -> nfhnfmod, nfsmith -> nfsnf
+       phi -> eulerphi, gphi -> geulerphi, phiu -> eulerphiu,
+       poldeflate -> RgX_deflate_max
+       polymodrecip -> modreverse
+       powraw -> qfbpowraw, compraw -> qfbcompraw
+       primedec -> idealprimedec
+       racine -> sqrtint,
+       regula -> quadregulator
+       rename cmp_ZV -> ZV_cmp
+       smith2 -> smithall, gsmith2 -> gsmithall
+       srgcd -> RgX_gcd
+       subfields0 -> nfsubfields,
+       subres -> resultant, subresall -> resultant_all.
+       torsell -> elltors and document it
+       zideallog -> ideallog
+       RgXQ_caract -> RgXQ_charpoly
+       ZX_caract -> ZXQ_charpoly
+       ZX_isirreducible -> ZX_is_irred
+       pariputc -> pari_putc, pariputs -> pari_puts, pariflush -> pari_flush,
+       gpmalloc -> pari_malloc, gprealloc -> pari_realloc, gpfree -> pari_free
+       ordell -> ellordinate
+    7- polvaluation -> RgX_valrem(x, pz) and no longer accept pz = NULL
+       (use RgX_val). Same for ZX_valuation -> ZX_valrem [ & ZX_val ]
+    8- rename u2toi -> uu32toi [ paste two 32-bit unsigned into a t_INT ]
+       Write uutoi for the more natural operation of pasting two ulongs into
+       a t_INT.
+    9- prototype of FpX_center [ add one argument ]
+BA 10- [svn/CVS] number development snapshots according to repository version
+   11- delete CVS.txt and replace it with README-subversion
+   12- syntax errors, SIGINT and "PARI bugs" are no longer trapped
+   13- quadray no longer allows an (optional) 3rd argument
+   14- 'format' was updated after each 'realprecision' change. Don't update
+       if 'format' is Xm.-1 (show all significant digits).
+   15- the algorithm used to round t_REAL for output
+   16- the "minimum field width" component of the 'format' default is now
+       ignored (was used only for integers in 'prettyprint' output mode and
+       defaulted to 0 [no effect]). Use printf !
+   17- %Z is no longer a valid conversion specification for PARI formats,
+       since this is now handled as a length modifier. Use %Ps instead
+       (P stands for PARI).
+   18- remove the (ugly) prettyprint engine ('output' = 2), render as matbrute.
+       printp / printp1 now act as print/print1
+   19- pariprintf renamed to pari_printf, %Z conversion disappeared (use %Ps)
+   20- check for small prime divisors in ispower [ large speedup ]
+   21- the conversion style 'g' of the 'format' default now printfs in style
+       'f' if the decimal exponent is < -4 ( was: if the binary exponent
+       is < -32 ), in order to conform to standard printf specifications.
+   22- [GMP kernel] enable GMP exact division
+   23- matdetint: use less memory + faster finish
+   24- change the default algorithm for mathnf(a) [ much faster ], flag 0 is
+       no longer deprecated
+   25- library/GP function galoisinit() now returns NULL/0 on failure
+       (used to raise an exception)
+   26- allow nfgaloisconj(t_POL, 1) [ cf C-4 ], never resort to nfgaloisconj(,2)
+       [ slow, unreliable ] with default flag = 0
+   27- [library]: gsub now implemented directly when typ(x) = typ(y)
+BA 28- all LLL variants now use an implementation of NGuyen & Stehle's algorithm
+   29- [library]: the "prec" parameter of floating point LLLs has disappeared
+   30- Vecrev(x) now equivalent to extract(Vec(x), "-1..1") [was only for t_POL]
+   31- allow t_COMPLEX as well as t_QUAD in ellpow
+   32- we used to have Set(1) == Set("1") ( = ["1"] ). Now the latter returns
+       ["\"1\""] (the Set whose element evaluate to the character string "1")
+   33- plotinit: change the way default arguments are evaluated (omit, instead
+       of interpreting 0 in a special way)
+   34- nfbasistoalg / nfalgtobasis no longer accept t_VEC/t_COL/t_MAT,
+       use matbasistoalg / matalgtobasis, which now also accept t_VEC/t_COL
+   35- uniformize factornf and nffactor outputs (+ speed them up for
+       non-squarefree inputs: compute valuations mod a suitable prime)
+   36- treat Mat(t_LIST) as Mat(t_VEC)
+   37- t_POL^0 always returned t_INT 1 [ try to return 1 in coefficient ring ]
+   38- polresultant(x,y,,0) now chooses the best algorithm by itself
+       [Ducos, modular, Sylvester], polresultant(x,y,,2) is now a synonym
+       for the default. The change also improves poldisc().
+   39- regression tests in src/test/in/ no longer need to end with
+       'print("Total time spent: ",gettime)'
+   40- arguments of stirling1 / stirling2 are now ulongs [ were longs ]
+BA 41- user variables can now be aliased [#790]
+   42- conjvec(POLMOD of INTMODs) was slow
+   43- bnfcertify can now handle Zimmert/Minkowski bounds > 2^31
+   44- moved misc/dico.new to src/whatnow + integrate it in the
+       description system [ new script desc/whatnow ]
+   45- bittest(x, n) no longer accepts t_VEC arguments n
+   46- split library function disable_dbg() -> dbg_block() / dbg_release()
+   47- make the semantics of substpol more precise and less surprising:
+       substpol((x^2+x+1)/(x^2+x-1),x^2,x^3) -> (x^3 + x + 1)/(x^3 + x - 1)
+       [ was: (x^6 - x^3 - 2*x - 1)/(x^6 - 3*x^3 + 1)  ]
+   48- deprecated flag '2' for bnfinit [ don't compute units ] is now ignored
+       ==> bnfunit becomes totally useless.
+   49- the last component in prime ideals "primedec" format is a number field
+       element, but no longer necessarily in t_COL form
+   50- no longer allow ideals in *non-square* t_MAT form
+   51- idealmin now returns a number field element, instead of the associated
+       "principal idele"
+   52- cleared up the confusion caused by PARI "ideles", which were remotely
+       related to the mathematical notion, and covered two distinct uses. The
+       first one (ideal + list of real places) is now called "divisor"; the
+       second one (ideal + factorization matrix, which used to be a vector
+       of logarithms of complex embeddings) is now called "extended ideal".
+   53- Allow Scalar + t_COL: x + y returns [ y[1]+x, y[2],... ]. Consistent
+       with RgC_Rg_add and RgV_isscalar, and allows linear algebra over number
+       fields with less rigid data types (can mix freely t_COL, t_INT
+       and t_FRAC, instead of converting everything to t_COL).
+   54- Functions quadunit(), quadregulator(), and factor() no longer apply
+       componentwise to vector / matrix arguments. Use apply()
+   55- [libpari] removed obsolete 'prec' argument from prototypes of idealmul0,
+       idealmulred, idealpow0, idealpowred, ideallllred, algdep, algdep0,
+       lindep, lindep0
+   56- factorback() no longer accepts an optional 3rd argument (nf). Use
+       idealfactorback(). See also nffactorback().
+   57- Extend polhensellift to work over unramified extensions of Q_p
+       [ was Q_p only ]
+   58- [libpari] changed prototypes of bnrdisc, bnrconductor, bnrisconductor
+   59- slightly change the matrices used in idealmin (round them): the elements
+       returned are a little different.
+   60- no longer export private library function incgam2. Remove it also
+       under GP when compatible = 3. Like incgam1 (suppressed), 1.39.15 doc
+       stated it was provided "for debugging only".
+   61- addhelp: TABs are now treated properly (would overflow line without
+       wrapping) and \n are no longer ignored. No longer add a trailing '.'
+       when missing.
+   62- Useless nfinit flags 4, 5 (partial polred) removed.
+   63- Split off inline functions unrelated to the kernel from
+       src/kernel/level1.h
+   64- Swap the order of the 2 arguments to select(): selection function
+       now comes first [ as in apply() ]
+   65- error() messages now prefixed with the customary *** [ used to be ### ]
+   66- dvmdiiz(x, y, z) now expects z to be a t_INT [ to be consistent with
+       all other xxxz functions where xxx includes only i,s,u arguments ]
+   67- improved charpoly(,,2) [ Hessenberg form ]
+   68- allow Flxq_mul and Flxq_sqr to call Flx_rem_montgomery
+   69- t_POLMOD * t_POLMOD with same modulus: if all coefficients are in Z/NZ,
+       call specialized functions FpXQ_mul / Flxq_mul [ analog with gsqr, gdiv,
+       ginv, powgi ]
+   70- bezout(): make sure the leading term of the gcd of 2 polynomials is positive
+       (when it makes sense), as was already the case for gcd()
+   71- mpcopy, Flv_copy, Flx_copy, vecsmall_copy have all become
+       aliases for leafcopy.
+   72- trap(,,foo) was actually equivalent to trap(,foo) changing the global
+       exception handler, which was awkward an unintended
+   73- norml2(t_POL) now returns the expected (square of) the standard L^2 norm.
+       Use x * conj(x) to get back the old behaviour. norml2 now raises an error
+       on t_POLMOD and t_FFELT components; used to add relative norms as in
+        ? norml2([Mod(Mod(1,3)*x,x^2-2), 2])
+        %1 = Mod(2, 3)
+   74- [library] kernel functions involving a t_INT and a t_REAL now return a
+       t_REAL, e.g. divsr(0, x) or mulir(gen_0,x) return real_0(...) [ used
+       to return gen_0 ] ==> much better control of object types when writing
+       kernel code. Generic functions (gmul, gdiv), as called from gp still
+       return a result as precise as possible given the input: 0 * 1. --> gen_0
+   75- [library] cleanup t_COMPLEX arithmetic to take advantage of real parts
+       equal to a t_INT 0. Imaginary parts equal to 0 should never be created by
+       our generic functions (so no need to cater for them specially: they
+       will be very rare, produced by ad hoc routines).
+       Special case trigonometric functions on pure imaginary arguments.
+   76- kill(z) no longer destroys user variable, only resets them to their
+       "undefined" value (as z = 'z would).
+   77- [library] prototype of galoisconj [ add one argument ], and made it
+       correspond to the default nfgaloisconj flag
+   78- allow ordinary sorted vectors in set* functions [ sets are still t_VECs
+       of t_STR, but if components can be directly compared, ordinary vectors
+       are simpler and faster ]
+   79- allow arbitrary nf entries in relative matrices; nfhnf and nfhnfmod
+       return matrices whose entries may be t_INT or t_FRAC besides the
+       traditionnal nfalgtobasis form. Allow ideals in arbitrary form [was:
+       necessarily HNF]
+   80- linear algebra routines: determines first whether we are over Z/NZ
+       then call specialized FpM_* routines if possible [fixes #376]
+   81- [library] commented out a large section of pariold.h. Define
+       PARI_OLD_NAMES to recover compatibility macros for old friends like
+       'un' and 'lstoi'
+   82- Mat(t_QFI or t_QFR) now returns the associated 2x2 symmetric matrix
+   83- [library] rename Buchall -> Buchall_param and export a new Buchall
+       with a simplified interface.
+   84- trap() no longer allows installing default error handlers.
+   85- GP's break loop is now *enabled* by default [ set breakloop = 0 in your
+       gprc to disable it ], and no longer controlled by trap(). To get out
+       of a break loop, hit Return three times (next, return, break are no
+       longer accepted)
+BA 86- factormod and matker: faster linear algebra over F2.
+   87- remove duplicate polynomials in polred output [#874]
+   88- let forvec(X = v,...) respect the type of v (t_VEC or t_COL)
+   89- polred: compute characteristic polynomials as in polredabs [ using
+       complex embeddings, not ZX_charpoly ]
+PMo90- algorithm for nfrootsof1 now defaults to nffactor: generically a little
+       slower than qfminim, but safer and sometimes much faster (e.g. 1mn
+       against a few hours)
+   91- added a new component nf_get_roundG to the nf structure [ to speed up
+       ideal reductions ]
+BA 92- make test-xxx: trap SIGINT in benching script to exit on <C-C>
+   93- removed optional flag to rnfconductor, use rnfisabelian.
+   94- improved dirzetak() by an order of magnitude: e.g. K=nfinit(x^3-10*x+8),
+       dirzetak(K, 10^6) from 4min (2.4.2) to 2.5s
+   95- renormalize rnfkummer output so that it cannot be written as
+       P(C * x)*C^(-deg P) for any integer C > 1  [ saves large powers of C
+       in disc P ]
+   96- added a flag to the (mostly useless) function rnfdedekind(), the
+       version with the flag set is a little more useful. Allow list of prime
+       ideals instead of single prime ideal, and make this list optional
+       (omitted = test for maximality at ALL primes)
+   97- the "zetakinit" format: bnf no longer included
+   98- thueinit / thue: allow reducible and non-monic polynomials
+   99- rnfdedekind: allow non-monic polynomials
+  100- bnrclassno(bnr,id) : allow bid as well as modules for the 'id' argument.
+  101- GP 'path' now also applies for file paths containing a '/' provided
+       they do not *start* with a '/' (i.e. not an absolute path)
+  102- [library mode] the function znlog now takes an extra "order" argument
+  103- added an optional "order" argument to znlog, fflog, elllog, znorder,
+       fforder, ellorder, whose preferred format is [ord, factor(ord)] and
+       ord is the order of the group
+  104- factor(t_INT/t_FRAC, lim) used to trial divide by primes up to
+       min(lim, primelimit). Now we trial divide up to lim and raise an error
+       if lim > primelimit. [ Having the routine return an obviously "wrong"
+       result depending on an invisible parameter was not a good idea. ]
+  105- factor(t_INT/t_FRAC, 1) was the same as factor(t_INT/t_FRAC, 0). Now
+       abide by the input value and leave (..., 0) as the single special case
+       (shortcut for "the largest precomputed prime")
+BA106- [native kernel] Schoenhage-Strassen multiplication/squaring
+  107- factorff(A, p, T): make p & T optional if A has t_FFELT coefficients
+BA108- [TLS] Change pari_thread calling convention
+  109- allow x.a1, ..., x.a6 for an "ell5" x (5-component vector)
+  110- removed 'primelimit' grom gp_data struct: we now print the actual limit
+       of the primetable, not the value input by the user (which may be a
+       little less)
+BA111- listsort(L) no longer returns the list L: it now returns nothing.
+       (No point in sorting in place if we must immediately copy the result.)
+  112- allow component(t_VECSMALL,...) [#957]
+  113- allow bestappr(x)  [2nd argument made optional]
+BA114- GMP kernel chosen by default when GMP is available
+BA115- Rename RgXQ_u_pow to RgXQ_powu
+  116- Darwin: add -search_paths_first to LDFLAGS (try to solve the Editline
+       / Readline conflict in a user-friendly way)
+BA117- [enable-tls] Library soname changed to libpari[-gmp]-tls[-2.4].so.N.
+  118- integrate a final polredabs in quadhilbertreal()
+BA119- Rename leftright_pow to gen_pow, leftright_pow_u to gen_pow_u and
+       implement sliding window powering.
+BA120- galoisapply now accept automorphisms in t_COL format.
+  121- allow moduli equal to 0 in matsolvemod (was SEGV) [#947] + rigorously
+       size-reduce the output in all cases.
+  122- the bnr structure in a minor way (bnr[6] has 3 components now)
+  123- cleaned up the code used by the GP evaluator to call PARI (and
+       installed) functions. Functions can now have 20 arguments [#795]
+BA124- galoisfixedfield now accept subgroups.
+  125- ellap(E, p) second argument now optional if E has t_INTMOD or t_PADIC
+       coefficients.
+  126- generalize the "generic pivot" strategy to all linear algebra (support
+       RgM_solve and det() in addition to existing functions); implement
+       maximal pivot strategy for p-adics [#1054]
+BA127- padic_sqrt -> Qp_sqrt, padic_sqrtn -> Qp_sqrtn, gammap -> Qp_gamma
+  128- rename rnfinitalg -> rnfinit
+  129- [readline + vi-mode] h/H no longer clobbered by online help. 'h' will
+       now act as expected (= 'move left one character')
+  130- any of break / next / return now get out of the break loop debugger
+       (as they used to in 2.3)
+  131- typing <Return> thrice no longer gets out of the break loop : use break
+  132- faster eta(,1), weber() and quadhilbert() [ rewrite using eta's
+       functional equation explicitely ]
+  133- checked that no 2-strong pseudoprime passes BSPW primality test up to
+       2^64 (use Feitsma's list, extending Galway's)
+
+  Added
+    1- many new benches (make test-all)
+    2- split up some large monolithic files: new files
+       src/basemath/ RgV.c, FpV.c, ZX.c, ZV.c, concat.c, lll.c, hnf_snf.c
+         prime.c, bit.c
+       src/modules/ QX_factor.c, Hensel.c, DedekZeta.c
+    3- split off module of general inline functions: src/headers/pariinl.h.
+    4- Write or rename / document many convenience library functions:
+       pari_calloc, cgetalloc
+       mkfraccopy, mkquad, mkvecsmall4, absfrac, sqrfrac, mul_content, mul_denom
+       roundr_safe, trunc_safe
+       shallowextract, shallowconcat1, concat1,
+       BPSW_psp & BPSW_isprime
+       Z_smoothen, Z_ispower, Z_ispowerall
+       Z_isanypower, chinese_coprime_Z, Z_chinese, Z_chinese_all,
+       Z_chinese_coprime, Z_chinese_post, Z_chinese_pre, Z_incremental_CRT,
+       Z_init_CRT, ZM_incremental_CRT, ZM_init_CRT, ZX_incremental_CRT,
+       ZX_init_CRT,
+       ZX_to_monic, ZX_primitive_to_monic, ZX_squff,
+       ZX_gcd_all, nfgcd, nfgcd_all,
+       RgX_disc, quad_disc, qfb_disc,
+       ZpX_liftfact, ZpX_gcd, ZpX_reduced_resultant, ZpX_reduced_resultant_fast
+       Fp_center, FpM_center, FpV_center
+       Fp_ratlift, FpM_ratlift, FpX_ratlift
+       FpM_FpC_mul_FpX
+       Fp_red, FpXQ_red
+       Flxq_add, Flxq_sub, FpXQ_add, FpXQ_sub, FqXQ_add, FqXQ_sub
+       FqX_add, FqX_sub
+       FqX_roots, FpX_factorff, FpX_rootsff
+       FpXQX_div, FpXQX_rem, FpXQXQ_mul, FpXQXQ_sqr, FpXQXQ_pow
+       FpXQXQ_inv, FpXQXQ_invsafe, FpXQXQ_div
+       FqXQ_mul, FqXQ_sqr, FqXQ_pow, FqXQ_inv, FqXQ_invsafe, FqXQ_div
+       FqX_extgcd
+       ZC_ZV_mul, ZM_is_identity, ZM_copy, ZM_neg, ZM_add, ZM_mul, ZM_pow
+       ZM_max_lg, ZM_sub, ZM_ZC_mul, ZXV_Z_mul, ZM_Z_mul, ZV_Z_mul, ZV_isscalar,
+       ZV_copy, ZV_neg, ZV_neg_inplace ZV_togglesign, ZV_indexsort
+       ZMrow_ZC_mul, ZV_ZM_mul, ZV_cmp0, ZV_content, ZV_equal,
+       ZC_lincomb, ZC_lincomb1_inplace, ZC_z_mul,
+       ZV_prod, ZV_pval, ZV_pvalrem zv_prod
+       ZM_Z_divexact, ZC_Z_divexact, ZX_Z_divexact,
+       ZM_charpoly, Flm_charpoly, Flm_hess
+       Z_ZX_sub, ZX_Z_sub, Fp_FpX_sub,
+       Flc_Fl_div, Flc_Fl_div_inplace, Flc_Fl_mul, Flc_Fl_mul_inplace,
+       Flm_Fl_mul, Flm_Fl_mul_inplace, Flm_copy, Flm_mul, Flm_inage,
+       Flv_Fl_mul_inplace, Flv_add, Flv_add_inplace, Flv_copy, Flv_dotproduct,
+       Flv_sub, Flv_sub_inplace,
+       Fl_order, Flx_nbfact_by_degree, Flx_roots_naive,
+       FF_sub,
+       Fp_mulu,
+       FpV_inv, FpXQ_inv, FqV_inv,
+       FpXY_eval, FpXY_evalx, FpXY_evaly,
+       Flx_Fl_mul_to_monic, FlxqX_Flxq_mul_to_monic, FlxqX_rem,
+       FlxqX_gcd, FlxqX_extgcd,
+       FlxqXQ_mul, FlxqXQ_sqr, FlxqXQ_inv, FlxqXQ_invsafe,
+       pol1_FlxX,
+       FpX_Fp_sub, FpX_Fp_sub_shallow,
+       FpX_Fp_mul_to_monic, FqX_Fq_mul_to_monic,
+       FpXX_Fp_mul,
+       RgM_check_ZM, RgM_det_trianguar, RgM_isdiagonal,
+       RgV_check_ZV, RgV_neg, RgV_add, RgV_sub, RgV_Rg_mul,
+       RgC_neg, RgC_add, RgC_sub, RgC_Rg_div, RgC_Rg_mul,
+       RgM_neg, RgM_add, RgM_sub, RgM_Rg_div, RgM_Rg_mul,
+       RgC_RgM_mul, RgC_RgV_mul, RgM_RgC_mul, RgM_RgV_mul, RgM_mul, RgM_sqr,
+       RgV_RgM_mul, RgV_RgC_mul, RgV_add, RgV_neg, RgV_sub, RgC_Rg_add
+       RgM_diagonal, RgM_diagonal_shallow, RgM_inv, RgM_solve,
+       RgM_mulreal, mulreal, RgX_RgM_eval
+       RgV_sum, RgV_sumpart, RgV_sumpart2
+       RgX_neg, RgX_add, RgX_add_shallow, RgX_sub, RgX_Rg_add, RgX_Rg_sub,
+       Rg_RgX_sub, RgX_equal, RgX_equal_var, RgX_translate,
+       RgX_to_nfX, RgM_to_nfM, RgC_to_nfC,
+       RgX_type, RgX_type_decode, RgX_type_is_composite, RgX_Rg_divexact,
+       RgXQ_norm, RgXQ_pow, RgXQ_caract, RgXQ_reverse
+       RgXQX_translate, RgXQV_to_mod, RgXQXV_to_mod
+       RgXQX_pseudorem, RgXQX_pseudodivrem,
+       RgX_check_ZX, RgX_check_ZXY, RgX_pseudodivrem, RgX_pseudorem,
+       RgX_recip, RgX_recip_shallow,
+       ZC_hnfrem, ZC_hnfremdiv, ZM_det_triangular, ZM_detmult, ZM_equal, ZM_hnf,
+       ZM_hnfall, ZM_hnfcenter, ZM_hnflll, ZM_hnfmod, ZM_hnfmodid,
+       ZM_hnfmodall, ZM_hnfrem, ZM_hnfremdiv, ZM_hnfperm, ZM_snf, ZM_snfall,
+       ZM_snfall_i, ZM_snf_group, ZM_snfclean
+       ZM_lll, ZM_lll_norms, hnfmerge_get_1,
+       ZXY_max_lg, ZX_copy, ZX_equal, ZX_max_lg, row_Flm, row_zm,
+       scalar_ZX, scalar_ZX_shallow, deg1pol, deg1pol_shallow
+       togglesign_safe, setabssign,
+       zero_Flm, zero_Flv, zero_zm, zero_zv, zm_copy, zv_cmp0, zv_copy,
+       zv_equal, zv_content, zx_renormalize,
+       identity_perm, cyclic_perm, perm_mul, perm_commute, perm_inv, perm_pow,
+       cyc_pow_perm, cyc_pow, perm_cycles, perm_order, vecperm_orbits
+       bitvec_test_set,
+       RgX_get_0, RgX_get_1,
+       resultant, resultant_all, QX_disc, QX_resultant,
+       lllfp, lllintpartial, lllintpartial_inplace, reducemodlll,
+       reducemodinvertible, closemodinvertible,
+       init_primepointer,
+       galoisinit0, get_nfpol, get_bnfpol, get_prid,
+       bnf_get_nf, bnr_get_bnf, bnr_get_bid, bnr_get_mod, bnr_get_nf
+       nf_get_r1, nf_get_r2, nf_get_roots, nf_get_sign, nf_get_roots,
+       nf_get_M, nf_get_G, nf_get_roundG,
+       nf_get_Tr, nf_get_TrInv, nf_get_disc, nf_get_index, nf_get_pol, nf_get_zk
+       nf_get_prec (was nfgetprec), nf_to_scalar_or_basis, nf_to_scalar_or_alg
+       nf_to_Fq_init, nf_to_Fq, Fq_to_nf, zkmodprinit, zk_to_Fq_init, zk_to_Fq,
+       nfM_to_FqM, FqM_to_nfM, nfX_to_FqX, FqX_to_nfX
+       zk_multable, zk_scalar_or_multable, multable, tablemul, tablemul_ei,
+       tablemul_ei_ej, tablemulvec, tablesqr, ei_multable.
+       nfordmax, nfarchstar (was zarchstar), nfadd, nfsign, nfnorm, nftrace
+       nfsign_arch, nfsign_units, nfsign_from_logarch, Buchquad, Buchall,
+       Idealstar, idealaddtoone_i, idealcoprimefact,
+       nfinvmodideal, nfpowmodideal, idealsqr, nfpow_u
+       numberofconjugates,
+       bnrisconductor0, bnrdisc0, bnrconductor0,
+       bnrsurjection, ABC_to_bnr, idealred, idealred0 (was ideallllred),
+       idealred_elt, idealred_elt0
+       ellinit_padic, ellinit_real, ellinf, ell_is_inf, ell_is_padic,
+       ell_is_real, checkerr_real, checkell_padic, checksmallell, checkell5,
+       checkellpt
+       trueeta, exp_Ir, roots_from_deg1, deg1_from_roots
+    5- [libpari] public generic hashtables
+    6- library functions sqrr and invr [ Newton inversion, called by ginv() ]
+BA  7- library functions checkgroup, galois_group, cyclicgroup, dicyclicgroup,
+       abelian_group, group_domain, group_elts, group_order, group_isabelian,
+       group_abelianHNF, group_abelianSNF, group_isA4S4, group_leftcoset,
+       group_rightcoset, group_perm_normalize, group_quotient, quotient_perm,
+       quotient_group, quotient_subgroup_lift, group_subgroups,
+       groupelts_abelian_group, groupelts_center, group_export, group_ident
+    8- library functions pr_get_p, pr_get_gen, pr_get_e, pr_get_f, pr_get_tau
+       to access components of prime ideals
+BA  9- handle numerical derivation as an operation on closure.
+RB 10- GP functions printf, Strprintf
+   11- pari_printf, pari_fprintf, pari_sprintf, pari_vfprintf, pari_vprintf,
+       pari_vsprintf
+BA 12- docpdf and install-docpdf targets (toplevel Makefile)
+JD 13- add an optional argument to quit()
+   14- universal constant gen_m2, for symmetry
+   15- GP function version() [ current version number ]
+BA 16- [ellap] Port of SEA algorithm from the ellsea GP package
+BA 17- new alarm GP function and alarmer error code
+RM 18- partitions()
+   19- New chapters in Libpari Guide: usersch6.tex, usersch7.tex
+   20- library function dbg_pari_heap
+   21- function bnfcompress() [ to create a "small bnf" from a true
+       bnf, to be recoved using bnfinit ]. More consistent than old
+       bnfnit(,3) / bnfmake.
+   22- a concept of "extended ideal" [I, t], where t is an algebraic number
+       (possibly in factored form). The pair represents the ideal I x (t)
+       In applications the norm of I stays bounded for a given base field,
+       and t keeps track of the "principal part" of the ideal (usually in
+       factored form to avoid coefficient explosion).
+   23- public interface to forsubgroup: traversesubgroups()
+   24- library function bnfisprincipal0 (with new public flags, see nf_GENMAT)
+   25- GP functions nffactorback / idealfactorback
+JD 26- GP function warning()
+   27- new flag to charpoly [ integral matrix, modular algorithm ]
+BA 28- GP function galoisisnormal()
+BA 29- F2x functions family for polynomials over GF(2) and
+BA 30- t_FFELT subtype_FF_F2xq for GF(2^n)
+BA 31- F2v/F2m functions family and F2m_ker for linear algebra over GF(2)
+BA 32- extra debugging data in t_CLOSURE and debugging facility.
+BA 33- compile-time copy optimizer
+BA 34- elliptic discrete logarithm function elllog()
+BA 35- direct implementation of ZX_mul, ZX_sqr using Kronecker's trick + mulii
+   36- rnfisabelian GP function
+   37- default 'recover'
+   38- more possibilities to draw "random" elements in natural sets using
+       random(), e.g. finite fields, elliptic curve over finite field.
+   39- GP routines nfeltadd, nfelttrace, nfeltnorm
+BA 40- Pollard rho discrete logarithm algorithm (function gen_Pollard_log)
+   41- GP routine polrootsff
+   42- matadjoint: implement division-free algorithm (flag = 1) [#937]
+BA 43- GP routines and PARI functions ellweilpairing, elltatepairing and
+       ellgroup
+BA 44- FpE functions family for points on E(F_p)
+BA 45- GP routine galoisgetpol and PARI function galoisgetpol, galoisnbpol
+BA 46- GP routine and PARI function elldivpol
+BA 47- GP routine and PARI function ellmodulareqn
+BA 48- function RgXQ_ratlift for reconstruction of rational functions.
+   49- optional flag to bnfcertify : only check that the correct class group
+       is a quotient of the computed one.
+VB 50- mingw support
+VB 51- plotwin32 graphic engine
+   52- plotQt4 graphic engine
+BA 53- low-level accessors functions gal_get_* for galoisinit objects
+   54- libpari functions cvstop2, z_lval, z_lvalrem, z_pvalrem
+   55- GP function externstr()
+   56- library function ZC_reducemodmatrix, ZM_reducemodmatrix,
+       ZC_reducemodlll, ZM_reducemodlll
+BA 57- libpari functions vecsmall_max and vecsmall_min
+BA 58- GP functions idealfrobenius and idealramgroups
+BA 59- library functions Zn_sqrt, Zn_issquare
+   60- library function gidentical
+BA 61- GP operator === that use gidentical
+   62- GP function sumdedekind()
+JD 63- [gphelp] support for bzip2 compressed documentation.
+
+  Removed
+    1- obsolete GP functions printp, printp1. Either replace them by
+       print / print1 [ or the new printf ], or define aliases
+       alias(printp, print); alias(printp1, print1)
+    2- obsolete GP functions bnfclassunit, bnfreg, bnfclgp, bnfunit.
+       Use bnfinit.
+    3- obsolete GP functions bnrclass. Use bnrinit or bnrclassno.
+    4- obsolete GP function idealprincipal. Use number field elements "as is"
+    5- obsolete GP function ideleprincipal. No routine remains that would
+       use the auxiliary "archimedean information"
+    6- obsolete library functions extract and matextract
+    7- obsolete optional argument to quadhilbert(D < 0)
+    8- unused undocumented library functions os_open/os_read/os_close
+    9- obsolete README.DOS and README.WIN
+   10- cant_deflate, elliper1, lllger3, varer1, obsoler, infprecer, errlg,
+       errexpo, errvalp, rtodber, affer2, primer2, siginter error codes
+   11- obsolete library function bruteall
+   12- useless undocumented library function gkrogs (use krois)
+   13- obsolete library functions roots2 and rootsold are no longer public
+   14- useless library function isnfscalar
+   15- obsolete undocumented library function factpol(), lift_to_pol()
+   16- library function combine_factors() is no longer public
+   17- obsolete undocumented library function rnfdet0, rnfdet2
+   18- obsolete library function discsr(). Use RgX_disc (or quad_disc, qfb_disc)
+   19- obsolete library function element_powmodidele() [use element_powmodideal
+       + set_sign_mod_idele], ideleaddone, reducemodmatrix, nfreducemodidele,
+       nfreducemodideal, nfreducemodideal_i, elementinv_modpr
+   20- useless macros gaddgsz, gaddsgz, gdiventgsz, gdiventsgz, gdivgsz,
+       gdivsgz, gmaxgsz, gmaxsgz, gmingsz, gminsgz, gmodgsz, gmodsgz, gmulgsz,
+       gmulsgz, gsubgsz, gsubsgz, gachz, gacosz, gashz, gasinz, gatanz, gathz,
+       gchz, gcosz, gcotanz, gexpz, ggamdz, ggammaz, glngammaz, glogz, gpsiz,
+       gshz, gsinz, gsqrtz, gtanz, gthz, gzetaz, gmaxz, gminz, TRgopgz,
+       mpfloorz, mptruncz, mpdvmdz
+   21- obsolete library function gmillerrabin, gpolcomp, caradj0
+   22- obsolete library functions allbase, base, base2, factoredbase, smallbase,
+       discf2, factoreddiscf, smalldiscf. Use nfbasis0 / nfdisc0 / nfmaxord
+   23- obsolete library function pari_rand31  [ use pari_rand ]
+   24- obsolete undocumented library function bruterr
+   25- obsolete library function smallbuchinit
+   26- awkward flag '3' for bnfinit. Use bnfcompress().
+   27- badly named bnfmake. Use bnfinit(sbnf).
+   28- useless component v[6] in "small bnfs" (was 'different', is 0)
+   29- obsolete library functions buchray, buchrayinit, buchrayinitgen:
+       use Buchray.
+   30- obsolete library function smallfact [ use boundfact or Z_factor_limit ]
+   31- obsolete library function gracine [ use sqrtint ]
+   32- obsolete library function ispsp, gispsp. Use ispseudoprime or BPSW_psp.
+   33- obsolete library function gregula, gfundunit, gboundfact.
+       Use quadregulator, quadunit, boundfact
+   34- obsolete library functions ideallistunit, ideallistunitgen,
+       ideallistzstar, ideallistzstargen. Use ideallist0.
+   35- obsolete library function algdep2. Use algdep0 (same arguments).
+   36- obsolete library function Mod0. Use gmodulo.
+   37- obsolete undocumented library function poldvd. Use
+       RgX_divrem(x,y, ONLY_DIVIDES)
+   38- obsolete library function kerint1. Use kerint or ZM_lll(,0.99,LLL_KER)
+   39- header file paritype.h [ split between parigen.h and pariinl.h ]
+   40- obsolete library functions sor, outbrute, outbeaut
+   41- dangerous macros max & min [ use maxss/minss, maxuu/minuu, maxdd/mindd ]
+   42- deprecated global constants gi, geuler, gpi. Use
+       mulcxI/mkcomplex/gen_I(), mpeuler() and mppi().
+BA 43- bitvec family function. Use F2v instead.
+BA 44- useless prototype code 'S'. Use 'r' instead.
+BA 45- useless function delete_named_var.
+   46- obsolete and dangerous switch_stack() [#1013]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.4.2 (released 22/12/2007):
+
+  Fixed
+    1- divrs(0., negative integer) had the wrong exponent
+    2- vecsort(,,4) was broken in 2.4.1
+    3- allocatemem(z) didn't check that z >= 0 [#556]
+    4- remove hack in internal function 'readbin' that used the clone bit as
+    a special marker hence returned an invalid object, which could be
+    propagated to higher level public functions.
+BA  5- sqrtn(Mod(a,p),..) and factorff() were broken in 2.4.1
+BA  6- bitnegimply(1,2^65) returned a wrong result [#560]
+    7- nfeltreduce(nf,t_POLMOD,id) didn't work [#558]
+    8- [library] missing function intfourierexp()
+    9- segv in polrootspadic / repeated roots in FpX_root (only in deg 2) [#562]
+   10- log(2+O(4^4)) --> oo loop
+   11- log(exp(1e-100)) -> 1.000000000149525401279188592 E-100
+       [ for some accuracies, log & exp inacurate near 1 and 0 resp. ]
+   12- numbpart inaccurate (and slow). E.g numbpart(52602) off by 1 [#557].
+BA 13- &, |, && and || were right-associative instead of left-associative
+   14- remove type assumptions in mulcxI, mulcxmI: unsafe [#516]]
+   15- too much memory allocated to print real numbers
+   16- (y/x)/(x^0/y) not correctly simplified
+   17- \s reported a misleading number of available user variables
+   18- 1/(quadgen(-4)*x)+0. -> exponent overflow [#591]
+   19- sumpos(x=1,1/x^2,1) had the wrong sign [#587]
+   20- matrixqz(x,0) would not work if first two determinants were 0
+       + might lose pointers in gerepile
+   21- sumpos(x=1,0) -> oo loop [#585]
+   22- qfminim([[;]],,,2) --> SEGV [#598]
+   23- intformal(1) --> incorrect object
+   24- intformal(y,x) --> y*y
+   25- matadjoint(0) --> gerepile error
+   26- prodinf(x=0,0), prodinf(x=0,-1,1) --> oo loop
+   27- agm(-1, 1+1e-55) --> oo loop, agm(-1,1) --> wrong result
+LGr28- killing hi-res graphic window created zombie process (missing wait())
+   29- missing type check in eint1(x,n)
+   30- ploth(...) + Ctrl-C --> hi-res graphic window killed [use pari_daemon()]
+   31- quadgen([]) --> incorrect object [#606]
+   32- hyperu(0,1,1) --> oo loop [#608]
+   33- fix headers so that ulong is always defined
+   34- read("a b.gz")   \\ filename contains a space
+       gzip: ./a.gz: No such file or directory
+   35- logagmcx inaccurate [ used for log(t_COMPLEX), large precision ]
+   36- RgX_divrem: prevent "impossible" situations for weird base rings
+       1/ Mod((y-1)*x-1, Mod(2,6)*x^3-Mod(1,2)*x^2-1) --> SEGV
+   37- gclone didn't work on t_INTs with lg > lgefint
+   38- GC error in add_rfrac() [#612]
+   39- missing type check in subgrouplist [#616]
+   40- inconsistent type checking in nfeltdivmodpr [#617]
+   41- nfdisc(x^32+24888960*x^30+227077545803904*x^28+887225199431341086720*x^26+1145458263702669503266741248*x^24-459489127319699704284694158213120*x^22+73430099675988831740428200872826863616*x^20-6599321778961370851005469933592282336329728*x^18+369563540304176984501448638025309170375722401792*x^16-13214618078037183940422584396181416089308059714715648*x^14+291247424536170563676138246427605494527806096141868597248*x^12-3407227562250271661213064368167141844394234574629997803208704*x^10+86 [...]
+       --> "not a prime" error (help Round4 to recover when using non-primes,
+       instead of raising immediate errors) [#624]
+   42- vecextract: C14 in 2.4.0 didn't work for large masks [#623]
+   43- clean up version handling: move version setting code to config/version
+       and always set PARIVERSION from CHANGES if 'CVS' [ used to require
+       a 'Configure' ]
+   44- zeta(0e1) --> SEGV [#627], exp(0e1*I) --> SEGV [#630]
+   45- exp(2^200*I) --> catastrophic cancellation [cos/sin were OK] [#631]
+DE 46- on NFS filesystems, make install would rebuild all [#503]
+   47- default(compatible,3); default(compatible,1) --> case no longer taken
+       into account [#629]
+   48- missing GC in 'sigma'
+   49- eta(x): valuation would overflow if seriesprecision is large
+   50- typo in src/kernel/none/mp.c:convi --> inefficiency
+   51- concat(v) used too much memory (quadratic in #v, make it linear) [#634]
+BA 52- gp -q -f < eval('y) --> SEGV
+   53- "significant pointers lost" for objects involving 0 t_SERs [#635]
+   54- for trivial x, isanypower(x,&p) would not set p
+MSo55- [Configure] don't rely on $CC exit status, check whether a non-0 size
+       executable is produced [ problems with tcc ]
+BA 56- wrong Prototype for subgrouplist [ extra 'p' ]
+   57- hole in 'secure' mode: don't allow changing psfile / logfile [#645]
+   58- nf.codiff was only correct up to multiplication by some rational number
+       [cf #510 & 2.4.1-F7]
+   59- cgiv failed to delete the last object on stack [ if recursive ]
+LGr60- first default(parisize,*) would segfault [due to C-1, #569]
+   61- pariputs("") --> invalid read of size 1 [ valgrind ]
+   62- ell.omega, ell.eta, pr.gen could lead to gerepile error [#641]
+   63- y=[x];eval('y) --> error [#652]
+   64- incorrect use of gerepileupto in inittestlift [ galoisconj ]
+   65- extra multiplication in the innermost loop of Cholesky decomposition
+       (qfgaussred, etc).
+   66- made FpX_add, FpX_sub, Fq_add, Fq_sub stack-clean. FpX_neg, Fq_neg
+   returned an incorrect result if input was not reduced mod p
+   67- setrand(4);polgalois(x^8-264*x^6+25410*x^4-1054152*x^2+15856203)
+       --> wrong result [#660]
+   68- build fails with "env: parameter list too long" [#661]
+   69- factorff did not accept inputs with t_FFELT coeffs
+   70- could use PARI stack while reading gprc [ before pari_init ]
+   71- Mod(Mod(1,v),v) --> invalid object.
+   72- a = Mod(y, v); y = Mod(1,v); eval(a) --> invalid object
+   73- for some complicated t_RFRAC z: z' --> impossible assignment I-->S
+   74- typo in bnfisintnorm(): missed some solutions  [ couldn't find a unit
+       of norm -1 even though one exists ]
+   75- ffprimroot(ffgen( t_POL of degree 1)) --> oo loop
+   76- wrong result in theta(q, z) if sin(nz) was small for some small n [#667]
+   77- 1/Mod(0,2) --> impossible inverse modulo: Mod(2, 2)
+   78- alias(a,b), then ?a --> 'a is aliased to a'
+   79- -Mod(0,2) --> Mod(2,2)
+MA 80- [Linux-PPC] missing -fPIC in DLCFLAGS
+   81- possible oo loop in _isprincipal [ precision was supposed to increase,
+       but could in fact remain the same ]
+   82- quadregulator(y) -> SEGV [#674]
+   83- acos(x^0) -> division by 0 [ instead of O(x^8), at \ps 16 ]
+       Analogously, acosh(x^0), asin(x^0), asinh(I+O(x)) --> division by 0
+   84- dilog(-1) [ more generally polylog of < 0 t_REALs ] should have
+       0 imaginary part
+   85- problems with  [ build/install ] directory names containing spaces
+   86- avoid catastrophic cancellation in 3M [ Karatsuba ] formula for
+       t_COMPLEX * t_COMPLEX
+   87- ix86, x86_64: missing earlyclobber constraint for addllx, subllx, divll
+   88- ploth(,4) --> huge memory use for large plots
+   89- stirling(0,0,2) --> 0  [ should be 1, #690 ]
+   90- deriv(x/(x+1),y) --> invalid t_RFRAC with exact 0 numerator [#687]
+   91- issquare(t_POL) assumed characteristic 0 [#549]
+   92- sqrt(Mod(4,5) + x) --> error [ e.g. stack overflow ]
+   93- hyperu(0*I,1,1) --> forbidden assignment t_COMPLEX --> t_REAL.
+BA 94- fix compilation problem with g++-4.2 and GMP.
+   95- ??factor_proven, ??factor_add_primes did not work
+   96- typo in znprimroot: wrong result for large moduli
+       znprimroot(5*2^127+1) --> 2 [#696]
+   97- ffgen(x*Mod(1,2)) --> x [ should be 0 ]
+   98- ffprimroot(ffgen((x+1)*Mod(1,2))) --> oo loop
+   99- nffactor(nfinit(y),x^2+Mod(1,y)) --> SEGV [#699]
+  100- "precision error in minim0" on qfminim(G, norml2(M[,1]), 100)
+       from tutorial
+  101- nffactor(nfinit(y^2+1),(2*x+1)*x*(x+1)) --> SEGV [#702]
+  102- isprime((6^2176+1)/(6^128+1)) --> length (lg) overflow [#697]
+       [ analogous problems for any large integer ]
+  103- various problems related to allocatemem() [ + document quirks ]
+       Remove the br_ALOCATEMEM construct and end allocatemem0() by a longjmp
+  104- missing GC in det_simple_gauss() [ matdet for inexact inputs ]
+  105- rare stack corruption in add_rfrac [#700]
+  106- add missing GC in gsubst
+  107- polred([pol, b]) computed unnecessary but possibly expensive
+       invariants of pol [ e.g. disc, index ]
+  108- compilation failed on AIX [ YYSIZE_T, pow ]
+  109- ? Mod(1,2)*x + Mod(1,2)*y
+       %1 = Mod(1, 2)*x + (Mod(1, 2)*y) \\ extra parentheses in constant term
+       ? % + 0*z
+       %2 = Mod(1, 2)*x + Mod(1, 2)*y
+  110- factornf(x^5+(-a+2)*x^4-a*x^3+(3*a-6)*x^2+(5*a-10)*x+(2*a-5), a^2-5)
+       --> SEGV  [ not squarefree -> denominators creeping in ] [#708]
+  111- problems with isexactzero and t_INTMODs. Mod(0,2)*x*1. -> 0,
+       Pol(Mod(0,2))+2 -> 2, (2+0*I)+I*Mod(0,4)->Mod(2,4), Mod(0,2)/x -> 0
+       Use isrationalzero instead.
+  112- substvec(x^2, [x^2], [y]) --> y^2  [ should be an error ]
+  113- typo in FpM_gauss_pivot: FpM_rank, FpM_image, FpM_suppl, FpM_indexrank
+       much slower than they should be. Analogous problem in FqM_gauss_pivot.
+BA114- missing GP2C descriptions for Pol and Polrev.
+BA115- zero FFELTs were not considered as exact zeros [#710]
+  116- rare SEGV in gp when recovering from error (dereferencing
+       global_err_data equal to BREAK_LOOP)
+  117- vecsort(t_LIST) returned a t_VEC
+  118- gp "huge file" + stack overflow --> stack overflow in next
+       interactive command (+ minor memoryleak) [#712]
+
+  Changed
+BA  1- The combined GP parser/evaluator has been replaced by a bytecode
+       compiler and a bytecode evaluator
+BA  2- install(): parser code 'E' and 'I' now refer to closures, not strings:
+       'I': closure whose value is ignored, like in for() loop
+       'E': closure whose value is used, like in sum() loop
+    3- Fl_pow renamed to Fl_powu [ exponent may not be negative ]
+    4- split usersch3.tex moving function documentation to src/functions/*
+    5- simplify table of contents for users.dvi
+    6- rename Flx_rand -> random_Flx, FpX_rand -> random_FpX
+BA  7- use factor_pn_1 to compute various orders in FF.c
+    8- file usersch3.tex is now generated from functions/*
+    9- rewrote logr_abs [ inaccurate + slow for x = 2^k * (1 - epsilon) ]
+   10- rewrote exp1r_abs [ inaccurate + slow ]  and mpexp_basecase [ reduce
+       input mod log(2) ]
+   11- rewrote mpsc1 [ slow ] -> faster sin and cos
+   12- [library] rename pointch ->  ellchangepoint, coordch -> ellchangecurve
+   13- prototype of constpi, consteuler.
+   14- use a little less memory to compute pi, euler, log(2)
+   15- qfminim(x,b,m,flag) made 'm' parameter optionnal also when flag=0
+   16- made second argument to matrixqz optional, allow non-prime values
+   17- matpascal(n < -1), mathilbert(n < 0) now raise an error
+   18- add optional extra argument to ffprimroot, fforder, fflog
+   19- allow znlog(x,g) where g a t_PADIC or an t_INTMOD modulo any N
+       that znprimroot(N) would accept
+   20- log(x t_PADIC): check whether x = 1 (mod p) before replacing x <- x^(p-1)
+   21- znprimroot(p^k): use isanypower() instead of factor().
+       E.g. znprimroot(nextprime(10^20)^1000): 8mn --> 12ms
+       znprimroot(N) no longer checks reliably whether (Z/N)^* is cyclic.
+       Result undefined if it is not.
+   22- padic sqrt much faster [ small and large accuracies ]
+   23- let primes() indicate a value of primelimit if unable to answer [#625]
+   24- remove variable names from ?0
+   25- exp(0e10) returned 1.000, made it 0exxx [ no significant digit ]
+MSo26- define polchebyshev(n, 1 or 2), pollegendre(n) for n < 0
+   27- znorder faster for non-prime modulus [ try it for Mod(2,5^10*7^10) ]
+       (compute lcm of local p-adic orders)
+   28- changed icopyifstack / copyifstack macros so that their arguments
+       are GENs, not GENs typecast to long.
+   29- add -funroll-loops to gcc flags when compiling arithmetic kernel
+   30- improve ellap (Shanks) by computing #E(F_p) mod 2 [ idea stolen from
+       Pierrick Gaudry ]
+   31- nfreducemodpr was exceedingly slow for large t_POL inputs, e.g.
+        w=x^48 + 158*x^46 + 4*x^45 + 12103*x^44 + 448*x^43 + 597874*x^42 + 23928*x^41 + 21373779*x^40 + 802424*x^39 + 588314524*x^38 + 18516794*x^37 + 12951694530*x^36 + 294992428*x^35 + 233870773964*x^34 + 2752210590*x^33 + 3524535272389*x^32 - 5797649292*x^31 + 44873186922754*x^30 - 798816466566*x^29 + 486736157075707*x^28 - 18082470992066*x^27 + 4523171646555185*x^26 - 271968456240780*x^25 + 36127625049532658*x^24 - 3144283847234232*x^23 + 248308835345289047*x^22 - 29271322082172250*x [...]
+        nf=nfinit([w, nfbasis(w,1)]);
+        modpr=nfmodprinit(nf, idealprimedec(nf,5)[1]);
+        L = nfgaloisconj(w); vector(#L,i, nfeltreducemodpr(nf,L[i],modpr));
+      (in this example, the last reduction is down from ~ 1minute to ~ 2s)
+   32- make sure nfmodprininit chooses a monic T to represent
+      Fq ~ Fp[X]/(T)  [#646]
+   33- remove obsolete undocumented functions outerr, outbeauterr, outsor,
+    outtex. Rename voir -> dbgGEN. Functions brute, outbrute, matbrute,
+    outmat, sor, outbeaut are obsoleted and no longer documented.
+   34- rename errfile -> pari_errfile, infile -> pari_infile, logfile ->
+    pari_logfile
+   35- extra test infrastructure [ drop file in src/test/in and possibly
+    src/test/[32|64], then run Configure, no need to edit config/get_tests ]
+   36- inline gerepileupto/gerepileuptoleaf + improve gerepileupto
+   37- [libpari] cleanup of user / temp variable handling. manage_var
+   obsoleted (kept for backward compatibility, to be removed), see Section 5.9
+   for new equivalents. Fixes #633, #650
+   38- fix t_LIST as components: v = [List()]; listput(v[1],) didn't work [#468]
+   39- listcreate() and listkill() are obsolete, don't use them. L = List()
+    should be enough in all cases. All lists now grow as needed, without
+    requiring an awkward maximal length (from listcreate).
+   40- rename sqred -> qfgaussred, signat -> qfsign,
+    sqred1 -> qfgaussred_positive
+   41- rename gaddmat -> RgM_Rg_add and swap arguments. Add RgM_Rg_add_shallow
+   42- document library functions zv_neg, zm_transpose, fix typo in
+    documentation for RgX_neg
+   43- document library functions gmaxgs, gmaxsg, gmings, gminsg.
+   44- document library function gfloor2n
+   45- document library function zx_shift
+   46- cleaned up splines handling [ ploth(,,,256) ] : remove quark_gen & QUARK
+BA 47- implicitly local variables are lexically-scoped
+BA 48- local and my can now appear anywhere in a program.
+   49- [library] rename apell -> ellap
+   50- Removed the OK_ULONG macro and renamed u_OK_ULONG -> SMALL_ULONG
+   51- Rename BIGINT -> NO_VARIABLE [used by gvar()], VERYBIGINT -> LONG_MAX,
+       MAXULONG -> LONG_UMAX. Remove BYTES_IN_LONG, TWOPOTBYTES_IN_LONG
+   52- implement directly gsubsg [ was a macro calling a trivial wrapper ]
+   53- optimize multiplication for quadratic t_POLMOD [ t_QUAD remains faster ]
+   54- theta(q,z) very inefficient for large accuracies
+   55- remove support for nf of the form [nf, change of variable] in
+       nfnewprec() [#672]
+   56- global() now obsolete and scheduled for removal
+LGr57- 'make ctags' gctags-specific flags [ add #defines, typedefs ]
+   58- prototype of nfsubfields [ 2nd argument GEN -> long ]
+   59- allow t_FFELT in issquare / issquarerem / ispower
+   60- sqrt(t_SER) now uses Newton iteration
+   61- rename gissquarerem -> gissquareall, uissquarerem -> uissquareall,
+       Z_issquarerem -> Z_issquareall (analogy with sqrtrem was faulty: we do
+       not store a remainder but the square root)
+BA 62- User functions are now regular variables holding values of type t_CLOSURE
+   63- Output of \u is now sorted
+   64- More explicit error messages in gp_history, e.g.
+       ***   History result %10 not available [%1-%6]
+BA 65- vecsmall_uniq(v) no longer assumes that v is sorted
+   66- allow ellorder to handle curves over Fp, add an optional parameter to
+       indicate a multiple of the order as in znorder.
+   67- allow polcyclo(n, a) for an arbitrary a [ had to be a variable ] +
+       major speedups: polcyclo(10^6) 5min -> 16ms. Similarly for polchebyshev,
+       pollegendre and polhermite
+   68- ?0 listed all "user-defined identifiers". Restrict to "functions"
+   69- use simplify in setsearch() and Set() [#707]
+   70- change gcd(t_POL, t_POL) so that inexact inputs have scalar gcd
+       [ used to compute a "sensible" approximate gcd ]
+
+  Added
+    1- library functions expu, adduu, subuu
+    2- library functions divisorsu, factor_pn_1 (using cyclotomic factors and
+       Aurifeuille), merge_factor
+    3- library function divru, dbllog2r
+    4- library function ZX_gcd, ZX_isirreducible
+    5- library function gtos
+    6- library function pari_daemon
+    7- library function Fp_sqr, padicsqrtlift
+    8- library function RgXQ_inv
+    9- bench 'extract'
+   10- charpoly: Berkowitz division-free algorithm (new default) [#541]
+   11- library function phiu
+   12- library function mkvecsmalln
+   13- library function chk_gerepileupto, dbg_gerepile, dbg_gerepileupto
+   14- library function gen_sort_uniq, gen_indexsort_uniq+add flag 8 in vecsort
+   15- library function remi2n, cmp_RgX, gen_cmp_RgX
+   16- library function RgV_dotproduct, ZV_dotproduct, RgV_dotsquare, ZV_dotsquare, FpV_dotproduct, FpV_dotsquare
+   17- library function FpX_Fp_add_shallow
+   18- library function Q_pval
+BA 19- new keyword "my" to declare lexically-scopped local variables.
+   20- new GP function listpop()
+   21- library function RgX_inflate, RgX_deflate, Flx_inflate, Flx_deflate, poldeflate
+   22- library function Fp_neg, Fp_add, Fp_sub
+   23- library function ugcd
+   24- library function vecinv, vecmul, vecpow, vecdiv, vecmodii
+   25- library function RgM_isidentity, RgV_isin
+   26- library function nfnewprec_shallow, bnfnewprec_shallow, bnrnewprec_shallow
+   27- library function Zp_issquare
+   28- library function dvmdsBIL, divsBIL, remsBIL
+   29- library functions gassoc_proto, map_proto_G, map_proto_GG, map_proto_GL,
+       map_proto_lG, map_proto_lGG, map_proto_lGL [ were private with
+       other names ]
+   30- default factor_proven
+   31- experimental -balloon option to gphelp
+LGr32- defaults graphcolormap, graphcolors [runtime-defined colormap for plots]
+LGr33- bit 4096 [ complex ] to ploth
+   34- member function .p / .mod for t_PADIC
+   35- same mnemonic flags to plotrecth as in ploth
+BA 36- member function .pol, .mod and .p for t_FFELT
+BA 37- New type t_CLOSURE to store GP functions
+   38- Ability to use arbitrary comparison functions in vecsort()
+   39- library functions closure_callgen1, closure_callgen2, closure_callgen,
+       closure_callgenvec.
+   40- GP function apply(), select()
+   41- library functions FF_issquare, FpXQ_issquare, Flxq_issquare
+BA 42- library function vecsmall_uniq_sorted
+   43- bench 'ff', 'exact0'
+   44- has_getenv.c
+   45- library function FpX_oneroot
+   46- library function gcmpX
+   47- library functions ZV_sort, ZV_sort_uniq, ZV_search
+
+  Removed
+    1- obsolete function kbessel2 (now static). Removed optional flag in besselk
+    2- Removed obsolete optional flag in ellap(), Mod() and gcd()
+    3- Rename gen_search_aux -> gen_search and document it [ old gen_search
+       disappears: use tablesearch() ]
+    4- undocumented library functions gpolylogz, polylog, polylogd, polylogp,
+       polylogdold. Use polylog0 with appropriate flags.
+    5- undocumented library function ghell2. Use ellheight0 with appropriate
+       flag.
+    6- obsolete function log0; use glog
+    7- obsolete undocumented library function mpdivz, polzagreel, RgX_powers
+       (use RgXQ_powers)
+    8- obsolete undocumented library function sqred3
+    9- the emacs/ directory: PariEmacs is now distributed separately.
+   10- obsolete functions ellap0, apell2
+   11- obsolete header file pariport.h
+   12- obsolete error codes paramer1, valencer1, accurer, caracer1
+   13- removed obsolete system-specific #ifdefs (macintosh, alliant)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.4.1 (released 28/03/2007):
+  Fixed
+    1- qflll / qflllgram (t_MAT with t_FRAC entries) would not reduce to the
+       integer case (--> insufficient precision, SEGV) [#505]
+    2- [Cygwin] missing -L... -lgmp when compiling with gmp.
+    3- ispower(522^3) -> 0 [ looked like a 7th power to is_357_power(), which
+       then forgot to test for cubes ] [#506]
+LGr 4- [gphelp] race condition --> incomplete cleanup (improved patch BA)
+    5- Cleanup library linking: do not link libpari with -ld [only gp], do
+    not link gp with -lgmp [only libpari]. Side effect: libgmp.so no longer
+    needed for modules compiled by gp2c-run
+    6- when nf.disc < 0, nf.diff was an incorrect PARI ideal [#510]
+    7- nf.codiff was only correct up to multiplication by some rational number
+       (a divisor of nf.disc) [#510]
+    8- inaccuracy (>= 2ulp) in [cached] log(2) [#498]
+    9- exp, sinh, asinh, tanh, atanh were inaccurate near 0
+   10- [GMP kernel] forvec(x=[[-1,0]],print(x)) --> error [#509]
+       [ 'resetloop' failed when passing through '0' ]
+   11- nfbasistoalg(nfinit(y),x) created an invalid t_POLMOD
+   12- incorrect result in ZX_resultant (accuracy loss in bound computation)
+   13- bnfinit(): avoid further precision problems for large degree fields
+   14- [Configure] gcc-specific flags were used on linux/freebsd/cygwin, even
+       when __gnuc__ was unset
+   15- factor( pure power FqX ) --> SEGV
+   16- [GMP kernel] polrootsmod(f, 4) --> wrong result [ low level t_INT
+       manipulation not using the int_* macros ]
+   17- polrootspadic(f, 2, r) --> some roots would be found twice [ due to
+       FpX_roots(f, 4) called ]  [#521]
+   18- ??sumalt doesn't compile: in GPHELP, treat \ref in verbatim mode [#518]
+   19- matinverseimage returned [;] when no pre-image exists. Conform to
+       the docs: "an empty vector or matrix", depending on the input types.
+   20- [Configure] abort when $CFLAGS is not supported by $CC
+   21- 3.5 % 2 --> error [ should be 0.5 ]
+   22- sin(1/10^100) --> 0e-28 [ also affected cos,tan,cotan ]
+   23- fix e.eta and elleta such that e.eta = 2 ellzeta(e,e.omega/2)
+       [ was ellzeta(e,e.omega/2) ]. COMPAT.
+   24- elleta(e) was different from elleta(e.omega). Analogous problems
+       in all quasi-elliptic functions. COMPAT: change e.omega so that
+       e.omega[1] / e.omega[2] belongs to the Poincare half plane [ used
+       to be the inverse ]. Together with 24: the Legendre relation now reads
+       w1 e2 - w2 e1 = 2 I Pi
+       Rationale: 1) the action of Sl_2(R) becomes the standard one, not a
+       twisted one 2) fixes quite a few normalization problems in our code.
+   25- check that k >= 0 in thetanullk [#531]
+   26- isprime(-2,1) returned 1
+   27- Fix 'Not enough precision in thue' error
+BA 28- [OS X] Fix kernel detection on x86_64-darwin
+   29- Remove "VERY long time" Warning in bnfcertify (few minutes nowadays)
+BA 30- missing prototype for documented function ZY_ZXY_rnfequation
+   31- sqrt(x^2/y^2) --> SEGV [#536]
+   32- \r foo no longer worked if foo was a directory and foo.gp a valid
+       input file [#540]
+BA 33- [Configure] spectacular failure to recognize gcc under some locales.
+   34- polredabs(x^8+2*x^6-5*x^4+78*x^2+9) was incorrect [ missed
+       x^8+6*x^6-x^4+54*x^2+25 due to incorrect "skipfirst" ]   [#542]
+   35- typo in resmod2n (both kernels) [#546]
+   36- At \p28, 0.1 - 0.1 would return 0.E-30 instead of 0.E-29
+BA 37- missing prototype for documented function FpX_div_by_X_x
+   38- isprime(,0) very slow when primelimit is large [#546]
+   39- nfmodprinit could create FpX's which were not reduced mod p
+   40- O(x^3)^(1/2) was O(x^2) instead of O(x)
+RB 41- the following TODO item:
+       v = vector(2); v[1] = v = 0  --> SEGV. Occurs with high probability if
+       any variable is "deleted", while it (or part of it) is still in use
+       Reference count could be helpful here.
+   42- substpol(x^-2+O(x^-1),x^2,x) --> error [#555]
+BA 43- [TLS] addss, addsr and subsr were not reentrant.
+
+  Changed
+    1- concat(t_VECSMALL, t_VECSMALL) to return the concatenated vector
+       [was: a vector with two t_VECSMALL entries]
+    2- pariprintf() so that it handles t_STR as print() [ don't include quotes ]
+LGr 3- [Makefile] make generated src/funclist independent of locale
+    4- Extend Pocklington-Lehmer to the case N-1 = FU, F > N^(1/3)
+BA  5- Much faster base-2 to base-10 conversion.
+BA  6- FpX_Fp_add() is now clean.
+BA  7- rename ZY_ZXY_resultant -> ZX_ZXY_resultant, ZY_ZXY_rnfequation ->
+       ZX_ZXY_rnfequation and FpY_FpXY_resultant -> FpX_FpXY_resultant.
+BA  8- FpV_polint() now take a variable number as last parameter.
+    9- use Miller-Rabin-like improvement in znprimroot and FpXQ_gener
+   10- indexrank, indexsort and indexlexsort now return t_VECSMALLs
+   11- API for gen_sort, vecsort
+
+  Added
+BA  1- derivnum(x=a,expr) for numerical derivations
+BA  2- library function strntoGENstr
+    3- function Vecrev
+BA  4- ppc64 level0 inline assembly kernel
+    5- library function floor_safe()
+    6- library function itostr()
+BA  7- library function Fp_div(), Fp_mul()
+BA  8- library function FpXQ_norm()
+BA  9- library functions FlxX_resultant() and Flx_FlxY_resultant()
+BA 10- library function FlxY_Flx_div()
+BA 11- library function Flm_transpose()
+BA 12- library function Flx_Fl_add()
+BA 13- library function Flxq_div()
+BA 14- function stirling (Stirling numbers of 1st and 2nd kind)
+BA 15- library function FpX_valrem()
+   16- library function Flxq_gener
+BA 17- library function Flxq_norm, Flxq_minpoly, Flxq_charpoly
+BA 18- [toplevel benchmark] ffisom
+BA 19- library functions Fp_order, FpXQ_order, FpXQ_log, FpXQ_sqrtn,
+       Flxq_order, Flxq_log, Flxq_sqrtn
+   20- library functions gen_sort_inplace, gen_indexsort, sort_factor,
+       indexvecsort
+BA 21- New PARI type t_FFELT and support functions (FF_*) for finite field
+       elements.
+BA 22- functions ffgen, fforder, fflog, ffprimroot for finite field elements.
+
+  Removed
+    1- obsolete functions readexpr(), readexpr_nobreak()
+    2- pariemacs support from Configure
+    3- obsolete functions sindexsort, sindexlexsort, sindexrank
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+Done for version 2.4.0 (released 06/10/2006):
+  Fixed
+    1- typo in remiimul: wrong result in a "failsafe" branch (hardly ever
+       taken). May affect modular powering /Fp if p >> 10^1300 [#457]
+       Also affects the gmp kernel, for p >> 10^5800.
+    2- rare accuracy problem in bnfinit
+       P=x^8-787856*x^6+232721637848*x^4-30546112988506688*x^2+1503204734505922286224;
+       setrand(1974190693); bnfinit(P) --> non invertible matrix in gauss
+    3- inconsistent return type in nffactormod [#460]
+    4- the following TODO item
+        bnfinit may give wrong answers because we cheat on the value of
+        Bach's bound, using B := 0.3 log^2 D by default, where 0.3 should
+        really be 12 (under GRH). If the prime ideals of norm <= B do not
+        generate the classgroup, we may not detect it, and compute junk. Ex:
+        * setrand(3); bnfinit(y^4 + 1190*y^2 + 1416100).cyc
+          --> [8,2,2,2]. The correct structure is [8,4,2].
+        * setrand(1414185642); bnfinit(y^4 + 635*y^2 + 403225).reg
+          [twice the correct value]
+        * setrand(867341586); bnfinit(y^4-y^3+6122*y^2+6121*y+37466641).gen[2]
+          [is principal]. Group structure and regulator are correct!
+       Retune check_bach()
+IZ  5- remove bogus test for "external help" (= ??)
+IZ  6- [OS/2]: stack check [prevent oo recursion] present but not enabled
+IZ  7- [OS/2]: bogus [BUG] in 'program' bench (install pb not detected)
+    8- gammah(1+O(3^5)) --> incorrect type in ggamd.
+    9- invalid read in copy_leaf [ SEGV in some libc ]
+BA 10- ellheight short help was accidentally truncated.
+   11- substpol(1+O(x^(2*n)),x^2,x) --> 1+O(x) instead of 1+O(x^n) [#470]
+   12- [OS X] only use -no-cpp-precomp with Apple cc
+   13- divrem(x,x,y) --> [1/y*x, 0]
+   14- (1+x)/(1-x)/(1+x)^2 not simplified [#472]
+   15- typo in qflll: in rare cases (exact input+floating point computation+
+       precision increase in last-but-1 step), the returned base change is not
+       properly updated in last iteration --> basis not LLL-reduced
+   16- simplification missed in div_scal_rfrac() [ #473 ]
+   17- ispower(x^k, k) would answer 0 for some x and k in {3,5,7} [#476]
+   18- content(t_MAT with exactly 1 col) gave a wrong result
+   19- random(N) was not uniformly distributed in [0,N-1] (use a reject
+       strategy instead of moding out) [#210]
+   20- rare bug in red_montgomery (returning 0 with + sign, an incorrect object)
+       [ polrootspadic(x^11+x,11,10) --> corrupts gen_0 ]
+   21- qfbsolve(Qfb(1,2,10),5) --> [0,0; 0,0] instead of 0 [#479]
+   22- ispower(0, n, &z) would not set z
+   23- wrong result in conversion t_QUAD -> t_PADIC whenever disc  = 1 (4)
+JJ 24- gaddgsz macro was wrong [#481]
+   25- gener_Fl(p^k) can't handle k > 1, use gener_Zl instead [#480]
+SC 26- [GMP] mp_set_memory_functions was called with an incompatible realloc
+       function. [#484]
+   27- raising a t_QFI or t_QFR to the power n would return a reduced form (OK)
+       except when n = 1. Make it systematic.
+   28- in rare cases (return 0, native kernel) int_normalize was not suitable
+       for gerepileupto
+   29- idealnorm(nf, t_POL) didn't work
+AM 30- [TeXmacs] typo in texmacs_output: x --> <bluex> [#491]
+   31- accuracy errors in bnfinit: setrand(3); bnfinit(x^2+999999999999971)
+       [ use a failsafe version of gmod (modr_safe), and increase accuracy
+         if necessary ]
+   32- possible corruption of gen_2 in mynegi() [ hnflll ]
+   33- factor(4/x) --> [2, 2; x -1]  (content not removed)
+BA 34- bnrstark prototype code was non-standard.
+   35- rnfkummer(,,degree) often found too many fields [#482]
+   36- loss of accuracy in p-adic ellinit: wrong digits and spurious errors:
+       E.g i = 5; ellinit([1, -1, 1, -1, -14]*(1+O(17^i)))
+   37- missing GC in forvec(,,2)
+BA 38- Interrupting GP could lead to a freeze. [#488]
+   39- possible stack corruption in charpoly(,1)
+   40- Ser(x) raised an error [#499]
+
+  Changed
+    1- moved cgiv, gtofp to inline kernel
+    2- reduce amount of memory needed by APRCL
+    3- remove CPP from Configure tests (not needed)
+    4- allow arbitrary sequences (not only expressions) in sum, prod, etc.
+       sum(i = 1, 2, 1;2) --> 4  [ was: error ]
+    5- checked that no 2-strong pseudoprime passes BSPW primality test up to
+       10^15 (use Galway's list, extending Pinch's 10^13)
+    6- random() now uses Brent's XORGEN (replaces congruential linear generator)
+       getrand() returns the FSR internal state array, used by setrand(). The
+       latter no longer returns the input seed. (Initial patch by RR)
+    7- for install(): parser code 'E' is now obsolete, use 'I'
+    8- if issquare(x, &n) == 1, always set n to a square root of x
+    9- if ispower(x, k, &n) == 1, always set n to a k-root of x
+   10- issquare(t_QFI or t_QFR) now is an error. Use explicitly
+       issquare(component(x,1)) if you relied(?) on the old behaviour
+   11- [library] rename gener_Fp -> pgener_Fp, gener_Fl -> pgener_Fp
+       [ contrary to 'gener', these assume that their argument is prime ]
+   12- [library] rename cyclo -> polcyclo, subcyclo -> polsubcyclo,
+       tchebi -> polchebyshev1, legendre -> pollegendre
+   13- restrict the types allowed in gaffect and gaffsg to scalar types and
+       vectors of such.
+   14- vecextract(x, bitmask) much faster [ read bits, don't use shifts ]
+   15- improve hyperu
+   16- remove gp-specific signal handler. Use the one in libpari
+   17- [library] pol_1 and pol_x are no longer global arrays but functions
+   18- [GMP] was inefficient for small sizes (e.g. 1-word operands)
+   19- optimized rectplothrawin & plot (remove gaffect, use gtodouble
+       whenever possible). Coordinate computations should be faster and more
+       accurate.
+
+  Added
+BA  1- function Flm_rank
+BA  2- Add experimental --enable-tls Configure option and thread-local stack
+       support. Warning: using this option break the ABI.
+    3- centerlift(t_PADIC)
+    4- pgener_Zp, pgener_Zl
+    5- dvdiu, dvdis, dvdiu, dvdsi, affgr
+    6- cgetipos, cgetineg, togglesign
+RM  7- polhermite, polchebyshev2
+    8- allow rnf.zk and rnf.nf
+    9- [toplevel benchmarks] bnfisintnorm, quadclassunit, rnfkummer
+   10- ZX_factor, QX_factor
+   11- allow gzip'ped elldata & galdata files
+   12- allow t_VECSMALL in vecmin / vecmax
+BA 13- add pari_stackcheck_init() function to control deep recursion detection
+   14- Configure --tune flag
+
+  Removed
+    1- Odos/Makefile (no longer functional, obsolete)
+    2- old CodeWarrior-specific hack (malloc)
+    3- error code: intger2, affer3, overwriter
+    4- useless static function gp_handle_SIGINT
+    5- functions changevar() [ use substvec ] and reorder() [ use variable()
+       to get the list of user variables ], global arrays pol_1[], pol_x[]
+       ordvar[] and polvar[]
+
diff --git a/CHANGES-2.6 b/CHANGES-2.6
new file mode 100644
index 0000000..c277219
--- /dev/null
+++ b/CHANGES-2.6
@@ -0,0 +1,929 @@
+# $Id$
+Bug numbers refer to the BTS at http://pari.math.u-bordeaux.fr/Bugs/
+
+Done for version 2.7.0 (released 21/03/2014):
+
+  Fixed
+    1- e = ellinit(E over Qp); ellztopoint(e, 3) --> SEGV
+    2- thue((x^2+1)^2*(x^2-2),-4) --> missing solutions [+-1, +-1]
+BA  3- spaces in directory names were not supported
+    4- silent overflow when inputing t_REAL of huge exponent [#1526]
+    5- zeta(2+10^-101*I) -> overflow
+    6- RgX_mullow(f,g,n) could return a result of degree n (instead of < n)
+       Same with RgX_sqrlow
+    7- ellrootno(E) could lead to a SEGV [#1527]
+    8- issquare(Mod(3,22)) -> 0 [#1528]
+JD  9- polcyclo(5, Mod(-1,3)) was the negative of correct value
+JD 10- elltors(ellinit("90c3")) returned Z/12 but a generator of order 6
+   11- thue(thueinit(x^3-x-1),1578191) => oo loop [#1532]
+   12- catastrophic cancellation in eint1 (x moderately large)
+   13- SEGV in ellweilpairing [#1535]
+   14- factor(Mod(1,2)*Pol(1)) -> [Mod(0, 2) 1]
+   15- Mod(Pol(0), 2) -> 0, instead of Mod(0,2)
+   16- QXQ_to_mod was called with t_POLMOD arguments
+   17- ellwp(E, 'z) had stopped working for E not defined over C
+   18- nfdisc(T, t_MAT=partial discriminant factorization) => corruption [#1542]
+   19- norm(Mod(z,T)) where z is not a t_POL or in a different variable than T
+       => wrong result [#1539]
+   20- polresultant(Pol(Mod(0,2)), x) -> 0 instead of Mod(0,2)
+   21- nffactor(T, pol) possibly wrong results when nfinit(T) is not rigorously
+       computed [#1545]
+   22- gmodgs(t_POLMOD,): incorrect implementation
+BA 23- library functions Fp_pow/Fp_pow mishandled negative basis
+   24- gcd(1/(2^64*y),Mod(x^2,2^64*x^3)) --> inconsistent gcd [#1541]
+   25- matadjoint([x,0,0;0,0,0;0,0,0]) => wrong variables [#1547]
+   26- ellordinate(e, exact t_COMPLEX) => either a wrong result (no solution)
+       or a solution computed at an incorrect accuracy (DEFAULTPREC) [#1548]
+   27- weber(1.0*I,1) => SEGV [#1549]
+   28- primepi(2^32) => oo loop [on 32-bit machine]
+   29- primepi(N >= 2^32 or 2^64) off by 1
+JD 30- [libpari] initprimetable(2^20): infinite recursion
+   31- very inefficient div_rfrac_pol() [#1537]
+   32- polresultantext(x+1,x^2) -> [-# + 1, 1, 1]
+   33- for(i=a, b, ) where a < 0, b < 0 : go through wrong indices if #a != #b
+   34- printf("%1.2f",Mat([1.,2.])) -> SEGV [#1550]
+
+  Added
+BA  1- library functions FlxX_to_FlxC, FlxXV_to_FlxM, polx_FlxX
+BA  2- library function Flx_lead
+    3- library function RgV_to_RgM
+
+Done for version 2.6.2 (released 30/01/2014):
+
+  Fixed
+    1- eulerphi(t_INT factorization) corrupted the input
+BA  2- 8bit chars were interpreted as end of input
+    3- Bernoulli numbers were no longer cached [2.6.0]
+    4- typo in Brillhart, Lehmer, Selfridge primality test [#1467]
+    5- [Configure] confusing error messages when C compiler doesn't work
+    6- scalar + t_SER = 0 => wrong valuation (1+O(x)-1 => O(x^2))
+BA  7- squaring of t_REAL was not faster than multiply
+    8- 1+O(2^(2^18)) => O(2^0) (silent overflow)
+PB  9- Mod(1, 2) - Mod(2, 4) => Mod(-1,2) [#1469]
+   10- memory corruption in subgrouplist: e.g. subgrouplist([53835600, 29], 5)
+   11- SEGV in zncoppersmith (when auxilliary polynomial was non-monic)
+   12- division by 0 in Fl_ellcard_Shanks [2.6.1, #1474]
+   13- ellwp([1,I]) -> stack overflow [2.6.0]
+   14- sumdedekind(h < 0, k < 0) wrong result; if gcd(h,k) > 0 as well
+   15- galoisexport(G, 1) -> SEGV
+   16- ??default(log) no longer worked
+   17- rnfalgtobasis, rnfbasistoalg, rnfeltup, rnfeltdown, rnfeltabstorel,
+       rnfeltreltoabs were not treating their inputs consistently, and
+       accepted (or crashed on) too many incorrect inputs.
+   18- rnfideal* did not handle correctly the 0 ideal
+   19- rnfidealtwoelt(non integral ideal) => crash
+   20- nfeltreducemodpr(K, non invertible elt mod pr,...) => wrong result
+       (now an e_INV error)
+   21- rnfbasis: wrong result due to a mistake in gen_if_principal
+       [2.6.0, revert d62f0de01]
+   22- nfsolvemodpr(nf,a,b,P): a must be invertible, as in matsolve
+       (was not documented, used to SEGV). Raise an error if not.
+   23- various bnrdisclist crashes
+   24- bnrclassno(bnf, prime ideal) -> SEGV
+   25- forpart(x=k,) raised an exception if k < 0 [ now empty loop ]
+   26- deriv(constant) now returns 0 in the base ring (not gen_0).
+   27- inconsistencies and mistakes in deriv / intformal of t_POLMOD: forbid
+       integration wrt the main variable of x.mod, deriv(Mod(x, x^2),x) was
+       Mod(1,x^2) and deriv(Mod(y,x^2),y) was 0 instead of respectively 0 and
+       Mod(1,x^2)
+   28- idealchinese with t_POLMOD inputs -> input error
+   29- idealchinese with denominators -> SEGV
+   30- qflll(Mat(0)) returned Mat(1)
+   31- infinite loop in sumpos: sumpos(n=1,-log(cos(1/n))) [#1481]
+BA 32- doing allocatemem in the break loop would corrupt the new break loop
+   33- relative error in eint1 was larger than expected
+   34- select(t_LIST,,1) [ indirect select ] didn't work
+   35- vecsort(List(),,4) -> SEGV
+   36- factorpadic / polrootspadic lost accuracy when converting output from
+       integers to t_PADICs (factorpadic(t^2 + 3^5, 3, 5) -> irreducible factor
+       (1 + O(3^5))*t^2 + O(3^5)*t + O(3^0)
+   37- Mod(0,0) is no longer valid => division by 0 (as Mod(x,0) for x != 0)
+   38- RgX_to_RgV no longer accepts other types than t_POL. Use Rg_to_RgV for
+       a more permissive function
+   39- broken Fq_sqrt when T != NULL and t_INT input
+   40- ispower(1009^11) -> 0 [2.6.0]
+   41- ellinit([0,1,1,0,1],2).group -> SEGV
+   42- nfroots(nf, deg 1 polynomial) -> wrong result
+   43- Configure --time=ftime: wrong reported timings
+   44- mateigen(rational matrix): inconsistent concatenation [#1489,2.6.0]
+   45- polroots(): wrong accuracy of (real) 0 components
+   46- randomprime(2) -> SEGV [#1493, 2.6.0]
+   47- forqfvec(v,[;],0,) -> SEGV [#1495]
+   48- poliscyclo(x^0) -> 2
+   49- truncation (precision) errors in bnfnarrow [#1491]
+   50- matadjoint assumed that n! is invertible. If this is not the case, now
+       use a safe algorithm
+   51- leading coefficient of elldivpol ignored the characteristic,e.g.
+       E=ellinit([a1,a2,a3,a4,a6*Mod(1,2)]); elldivpol(E,2)
+   52- gcd involving a t_FFELT -> SEGV
+   53- oo loop in ispower for huge arguments / oo loop in prime iterator
+       in congruence classes [#1498, 2.6.0]
+   54- L=[1,2,3]; apply(x->L=x,L) -> SEGV  ( idem select and [|x<-] operators )
+   55- (k+1.)/k - (2*k+1.)/k -> incorrect object
+   56- gcd(x,0.) -> 1 but gcd(x,Pol(0.)) -> x [ the former is correct, since 0.
+       is inexact and may represent an arbitrary small non-zero real number ]
+   57- eint1 misbehaved at large precision [#1499]
+   58- polredbest(T,1) incorrect if T non-monic or degree 1
+   59- nfcertify might miss a factor to be certified, e.g.
+       P=polcompositum(x^4+437*x+19,x^5-571*x+27)[1];
+       nfcertify(nfinit(P, [2,3,5])) was empty
+   60- polresultantext(t_POL in y, t_POL in y,y) -> U/V as t_POL in x [#1509]
+   61- inconsistent handling of t_POL "divided by" scalar among the variants
+       of Euclidean division (%, divrem, \, ...). E.g. x % 2 is OK, but
+       x % Mod(2,3) was not. We now follow the semantic of
+       grem/gdeuc/poldivrem: a scalar is understood as a t_POL of degree 0
+       in the proper variable [#1510]
+   62- incorrect rounding in mulur
+BA 63- Configure: --includedir was ignored
+   64- add GC in RgV_dotproduct / RgV_dotsquare
+   65- ZX equal was checking variables, contrary to the spirit of the
+       specification (and contrary to RgX_equal). Specify behaviour properly:
+       none of the internal .*[xX]_equal function check variable numbers.
+   66- arithmetic functions did not accept factorizations of negative integers
+       (whereas most of them are defined for negetive integers)
+BA 67- Configure --time=ftime actually tested times, not ftime. Remove ftime
+       from the default list of timer functions (reports wallclock time, not
+       CPU time) [#1523]
+   68- polredabs() could (find then) forget some polynomials [#1511]
+   69- poldisc(x^3 + Mod(2,3)*x^2) --> e_INV
+   70- (Mod(4.,y^2+1)*(1 + O(x)))^(1/2) -> junk (= [2.0+O(x)),2.0+O(x)]~).
+       Worse if other t_POLMOD coefficients. Now exception.
+   71- sin(O(p)) returned 1+O(p) instead of O(p)
+
+  Changed
+    1- default help text for a user function is now as in \u:
+       fun =
+         (args)->body
+    2- after addhelp(f,...), ?f no longer include default help text for a
+       user function (function code). Type 'f' to see the function code.
+    3- primes([a,b]) now returns the primes in the interval [a,b]
+    4- install: add symbol name in library and GP prototype to default help
+    5- the rnf structure format
+    6- library functions: rename rnfelementxxx -> rnfeltxxx,
+       rnfidealhermite -> rnfidealhnf
+    7- allow mathnf(t_VEC) [ apply Mat() automatically ], gives acces to the
+       ZV_gcdext() routine.
+    8- mathnf(,2) (generic vs integral t_MAT) is deprecated: we always test
+       the matrix entries and choose the appropriate routine
+    9- bnrclassno: allow all standard ways to define class fiels (A,B,C),
+       not only bnf + module
+   10- bnrinit(bnf,f): allow using idealstar(bnf,f) in place of f
+   11- Let Set(t_VECSMALL v) behave as Set(Vec(v))
+   12- ffgen(T) now checks whether T is squarefree (testing for
+       irreducibility is too expensive)
+   13- variable() no longer raises exceptions: if no variable can be
+       associated to the object, return 0. Thus "if (!variable(x), )" may be
+       used to test wether x has a "variable".
+JD 14- polcyclo(n, x) now works for all roots of 1
+   15- quadpoly no longer accepts vector/matrix arguments, see 2.6.0,C94
+   16- [libpari] renamed ZM_hnfremdiv -> ZM_hnfdivrem
+   17- x t_REAL * y t_INT: either convert y to t_REAL (as before) or use
+       integer multiplication (if lg(x) >> lg(y))
+   18- GP: all results are now stored in GP history, together with the time
+       to obtain them [ we used to only store non-gnil results, and no timings ]
+   19- chinese(t_POL,t_POL): allow polynomials of different degrees
+   20- deplin(): use FpM/Flm/F2m routines if possible (like all other generic
+       linear algebra routines)
+   21- FpM_gauss(a,b,) from modular kernel no longer allow all types of input:
+       split into FpM_FpC_gauss (b t_COL) and FpM_gauss (b t_MAT). Same for
+       all modular xxx_gauss functions
+   22- O(1/x^2) --> error [ now equivalent to O(x^-2) ]
+   23- faster evaluated polhermite/pollegendre
+   24- polhensellift / ZpX_liftfact: use quasi-linear ZXX_mul_Kronecker instead
+       of RgX_mul (~ Karatsuba)
+   25- [libpari] renamed gcmpX -> gequalX
+   26- [libpari] renamed ordred -> polredord
+   27- the prid structure returned by idealprimedec: the anti-uniformizer
+       tau (pr_get_tau) is now stored via its multiplication table
+   28- rnfpolredabs(x,3) did not conform to the documentation (which did not
+       make sense). Fix documentation and function, for rnfpolredbest as well.
+   29- [libpari] renamed recip -> serreverse
+   30- lift(x,'v) / centerlift(x,'v) now only lift t_POLMODs in variable v,
+       no longer (most) t_INTMOD / t_PADICs met along the way
+   31- lift / centerlift no longer raise an exception when they encounter
+       a non-arithmetic type (e.g. a t_REAL or even a t_STR) in a structure.
+       They just copy the offending sub-object as-is.
+   32- generic polynomial Euclidean division (grem, gmod, poldivrem): when
+       result is the zero polynomial, use RgX_get_0, not gen_0 / pol_0
+   33- rnf.pol (absolute defining polynomial / Q) is now called rnf.polabs,
+       rnf.pol is now the relative polynomial, defining the relative extension
+       over the base.
+BA 34- FpXYQQ_pow: change order of moduli to be consstent with FpXQXQ_pow.
+BA 35- FlxYqQ_pow: renamed to FlxYqq_pow, and moduli order changed.
+   36- idealstar(K,id), allow to input 'id' as a factorization into prime
+       ideals, as produced by idealfactor
+   37- define eulerphi(0) = 2 [ now eulerphi(n) = znstar(n).no for all n ]
+   38- allow subst(t_SER,x,exact 0): subst(2+O(x),x,Mod(0,3))->Mod(2,3) [#1513]
+   39- [libpari] rename gsh -> gsinh, gch -> gcosh, gth -> gtanh,
+       gash -> gasinh, gach -> gacosh, gath -> gatanh, ggamd -> ggammah
+       (follow GP names)
+   40- no longer naively use the Karatsuba/3M formula to multiply t_COMPLEX
+       of t_REALs: loss of accuracy is too important and unpredictable from the
+       user's point of view [ e.g. (1+ 1e-90*I)*(1e90+I) -> 1. E90 + 0.E52*I,
+       when the definition directly yields 1. E90 + 2*I ]. Could use it when
+       the exponents of real/imaginary parts are close, increasing the
+       precision by the exponent difference (increasing overhead...): not done
+       yet.
+   41- renamed mpexp1 -> mpexpm1, cxexp1 -> cxexpm1
+   42- change prototye of mpsincos1 and rename -> mpsincosm1
+
+  Added
+EP  1- Configure option for gcov/lcov support
+    2- libpari function FF_f, member function g.f for t_FFELT
+       (definition field has p^f elements)
+    3- libpari functions gprimepi_upper_bound, gprimepi_lower_bound,
+       primepi_lower_bound, primes_interval, primes_interval_zv,
+       primes_upto_zv, primes0
+    4- libpari functions rnf_get_absdegree, rnf_get_invzk, rnf_get_map,
+       rnf_get_nf, rnf_get_nfdegree, rnf_get_nfpol, rnf_get_nfvarn, rnf_get_pol,
+       rnf_get_polabs, rnf_get_zk, rnf_get_nfzk, rnf_get_varn, rnf_get_disc,
+       rnf_get_index, RgV_is_QV, RgX_equal_var
+    5- allow rnf.disc, rnf.index
+    6- functions rnfelttrace, rnfeltnorm
+    7- function ZV_gcdext, based on ZM_hnflll (simplified)
+    8- allow ffgen(p^f) instead of ffgen(ffinit(p,f))
+    9- [libpari] Fq_sqrtn
+   10- mathnfmodid(m, d): allow d an arbitrary vector of positive integers.
+       The old syntax (d an integer, representing a vector of equal integers)
+       is still allowed
+LGr11- GP function Colrev
+   12- GP operator %#n to recover time used to compute history result %n
+   13- GP function getabstime()
+   14- [libpari] Rg_to_F2, RgV_to_F2v, RgM_to_F2m, F2c_to_mod, F2m_to_mod,
+       Flc_to_mod, Flm_to_mod, F2m_F2c_invimage, F2m_invimage, F2m_suppl,
+       Flm_suppl, F2m_rank, Flm_rank, F2m_rowslice, F2v_slice, F2v_ei
+BA 15- Parallel GP support (parapply, pareval, parfor, parforprime, parselect,
+       parsum, parvector)
+   16- [libpari] residual_characteristic
+   17- GP function characteristic()
+   18- [libpari] muluui, diviuuexact
+   19- [libpari] Kronecker_to_ZXX, ZXX_mul_Kronecker
+BA 20- conversion from matrices of FFELTs to low level (Fq/Flxq/F2xq) kernels
+       for faster treatment.
+   21- FqM_det, FlxqM_det, FlxqM_inv, F2xqM_det, F2xqM_inv, F2xqM_image,
+       F2xqM_image, F2xqM_rank, FFM_det, FFM_image, FFM_ker, FFM_rank, FFM_inv
+   22- ZM_rank, ZM_indexrank, ZM_indeximage
+   23- GP function normlp
+BA 24- GP function vecsum
+   25- [libpari] pr_equal, ZC_nfval, ZC_nfvalrem, ZC_prdvd, ZV_Z_dvd
+   26- allow .p / .e / .f / .gen for modpr structures
+   27- function rnfpolredbest
+   28- functions qfnorm, qfbil
+   29- [libpari] ZM_multosym, RgM_multosym, RgMrow_RgC_mul, RgM_transmul,
+       RgM_transmultosym, ZM_transmultosym
+   30- [libpari] nf_rnfeq, nf_rnfeqsimple, eltreltoabs, eltabstorel,
+       eltabstorel_lift, nf_nfzk, nfeltup, QXV_QXQ_eval, QXX_QXQ_eval
+   31- [libpari] factor_pn_1_limit
+   32- [libpari] get_FpXQ_star, gener_FpXQ_local
+BA 33- move gpinstall to libpari
+   34- liftall() lifts recursively all t_INTMOD/t_PADIC/t_POLMOD components
+   35- liftint() lifts recursively all t_INTMOD/t_PADIC components
+   36- liftpol() lifts recursively all t_POLMOD components
+BA 37- GP function logint
+PB 38- [libpari] gen_matmul, gen_matcolmul, FFM_mul, FqM_FqC_mul, FqM_mul,
+       F2xqM_mul, F2xqM_F2xqC_mul, FlxqM_FlxqC_mul, FlxqM_mul.
+   39- [libpari] Flx_equal, F2x_equal
+BA 40- [libpari] FpM_powu/Flm_powu/F2m_powu
+   41- new GP function idealprincipalunits
+   42- [libpari] checkabgrp, abgrp_get_no, abgrp_get_cyc, abgrp_get_gen,
+       bid_get_grp, bid_get_no
+   43- [libpari] FpX_disc
+   44- [libpari] retmkcomplex
+   45- GP function expm1 (= exp - 1, avoiding cancellation)
+
+  Removed
+    1- optional flag to factorpadic() [ enabling Buchman-Lenstra + round2 ]
+       Use the default = round4
+    2- useless wrappers map_proto_GG, map_proto_GL, map_proto_lGG
+    3- [libpari] useless functions gand, gor
+    4- [libpari] useless function ratlift [ use Fp_ratlift ]
+
+Done for version 2.6.1 (released 20/09/2013):
+
+  Fixed
+    1- Allow compounding 1-letter flags to gp, e.g. qp -qf [ used to silently
+       ignore the 'f' ]
+    2- ellminimalmodel, followed by ellchangecurve ==> structure incorrectly
+       updated [wrong Q_MINIMALMODEL component] [2.6.0, #1416]
+    3- lift(1/2+O(2))=2/1 --> incorrect result + corrupt object [2.6.0, #1424]
+    4- local() could corrupt polynomial variable of the same name
+    5- write did not fclose() the output file handle [2.6.0, #1425]
+PB  6- issquare(Mod(1,2)) -> domain error [2.6.0, #1429]
+PB  7- issquare(Mod(0,2),&s); s -> Mod(2,0) error [2.6.0, #1430]
+    8- ellheight(E not given by minimal model,P) -> domain error [2.6.0, #1432]
+    9- ellchangecurve(E, [1,0,0,0]) lost Q_MINIMALMODEL ->SEGV later [2.6.0]
+   10- warning() would print "warning: user warning:"-> "user warning:" [2.6.0]
+   11- matinverseimage(A, t_MAT B) would treat individual columns B[,i]
+       independently and successively. Now use a single Gauss reduction.
+   12- Flm_Fl_mul returned a wrong result
+   13- ellinit(ellfromj(Mod(0,17))) -> curve defined over Z [2.6.0, #1436]
+   14- allocatemem: make sure fix_size() is called to avoid alignment
+       problems [#1437]
+   15- ellmodulareqn(2) did not clean the stack properly [2.6.0, #1442]
+   16- issquare(Mod(13,121)) --> not an n-th power residue in Qp_sqrt [2.6.0]
+   17- stack corruption in carberkowitz / charpoly(,,3) [ session crash ]
+   18- ellinit(E over Fp, t_FFELT) => error [2.6.0]
+   19- mateigen([5/3,7/45;0,21/10]) => precision error (missing eigenspace)
+   20- subst(O(x^2),x,0*x) => SEGV [#1447]
+   21- memory corruption in aprcl [2.6.0]
+   22- factormod(,2) => wrong result [2.6.0, #1451]
+   23- dirmul/dirdiv: incorrect result length when valuation > 1
+   24- x,y a t_PADIC, x === y always returned 0 [2.6.0]
+   25- bernpol(0) => memory corruption [2.6.0, #1458]
+   26- round((1e-40+x) / (1e-39+x)) would create the invalid object x/x
+   27- polgalois(x^11 + 627*x^4 - 584) -> F_110 instead of S_11 [#1460]
+   28- input lines with more than 2^31 characters (without \n) resulted in a
+       truncated read [#1461]
+   29- znlog(1,Mod(8,9)) -> division by 0 [#1463]
+   30- plot(x=1,2,x) gave a graph with 0<=y<=2, instead of ymin<=y<=ymax
+   31- ?= or even = would apparently hang GP (waiting for further input)
+
+  Added
+    1- genus2red: an implementation of Liu's algorithm to determine the
+       reduction of a genus 2 curve (at p > 2). Based on genus2reduction-0.3,
+         http://www.math.u-bordeaux1.fr/~liu/G2R/ (Cohen & Liu, 1994)
+       mostly rewritten from scratch:
+       - adapted to pari-2.*
+       - somewhat modularized
+       - fixes all known instances of 'bug27' and 'bug28' (at p = 3, reported
+         through Sage users).
+       - bench 'genus2red' contains a check of at least one instance of each
+         of Namikawa-Ueno's types + all cases on which the original
+         genus2reduction was known to fail. [CAVEAT: the interface will change]
+    2- allow to startup gp with flags --default key=val (or -D key=val):
+       execute default(key,val) on startup
+BA  3- functions Flm_center, Flv_center, zv_to_Flv, zm_to_Flm, zm_mul,
+       zm_zc_mul, scalar_Flm
+BA  4- function minim_raw
+    5- function QX_complex_roots
+PMo 6- function forpart for looping over partitions
+    7- [libpari] forcomposite_init / forcomposite_next, reimplement
+       forcomposite() using this iterator
+    8- Configure now generates a file 'config.log' to help debugging when it
+       makes a mistake (contains all messages from compilers)
+    9- [libpari] RgM_invimage, RgM_RgC_invimage, FpM_invimage,
+       FpM_FpC_invimage, Flm_invimage, Flm_Flc_invimage, Flm_neg
+   10- [libpari] serchop0, integser functions
+   11- psi(t_SER) using Luke's recursion, then lngamma, gamma, gammah
+       for t_SER arguments around an arbitrary complex z0 (was implemented
+       for z0 = 0,1 only)
+PB 12- PARI functions FlxqM_gauss, FlxqM_inv and FqM_gauss
+   13- PARI functions FpM_hess, FpM_charpoly, charpoly, RgM_Rg_sub,
+       RgM_Rg_sub_shallow
+   14- generic driver for GP's charpoly(): select appropriate algorith (flag)
+       depending on input type.
+BA 15- allow ellsearch(t_VEC) : search for curve with given name, in given
+       isogeny class or with given conductor; alias for the existing
+       ellsearch(t_STR) as per the ellconvertname correspondance
+   16- option flag to mateigen: also return the eigenvalues
+   17- [libpari] Z_lvalrem_stop, u_lvalrem_stop
+   18- [libpari] ZX_rescale_lt
+BA 19- Port of the program ISOM by Bernt Souvignier for computation of
+       automorphisms and isomorphisms of lattices.
+       New GP functions qfauto, qfisom, qfisominit, qfautoexport
+   20- [libpari] RgX_mullow, RgX_sqrlow
+   21- [libpari] embed_T2, embednorm_T2, embed_roots, embed_disc, embed_norm
+   22- arithmetic functions now accept factorization matrices, you can use any
+       of f(N), f(factor(N)) or f([N, factor(N)])
+   23- GP function readstr
+   24- allow lists of elements in chinese()
+
+  Changed
+    1- gp --primelimit lim (gp -p lim) is deprecated. Use the generic form
+       'gp -D primelimit=lim' (setting primelimit is now mostly useless, anyway)
+    2- gp --stacksize lim is deprecated. Use gp -s lim or the generic form
+       'gp -D parisize=lim'
+    3- partitions() interface to match forpart()
+    4- improve qfbred(t_QFI) for "small" inputs
+    5- bnfnewprec: ensure we recompute bnf from scratch at most once (could
+       happen many times in makematal())
+    6- [make bench] properly align results if 'printf' is available
+    7- [libpari] the *_invimage function no longer accept a t_COL second
+       argument: use the RgM_RgC_invimage variant. They now return NULL when
+       no solution exist (used to return a t_COL / t_MAT of length 1)
+    8- [libpari] replace the forvec_start() function by a standard
+       iterator: forvec_init() / forvec_next()
+    9- [libpari] Z_lvalrem would destroy its argument. No longer.
+   10- znprimroot(p^k) now always returns the smallest primitive root (was
+       only true for k = 1)
+   11- gmul / gsqr for t_SER with t_INTMOD coefficients: reduce to Z[X] +
+       Kronecker substititution (quasi-linear vs. Karatsuba)
+   12- Mulders/Hanrot-Zimmerman short products for power series
+HC 13- new mpveceint1 implementation: faster and more precise
+   14- version string for development versions: added number of reachable
+       commits (as per git rev-list). Affects version() [#1346]
+
+  Removed
+    1- drop support for "gp -b buffersize" (obsolete since 2.2.10, 04/2005)
+    2- drop backward compatibility 'gp -emacs', 'gp -test'. Use the '--emacs'
+       and '--test' forms.
+
+Done for version 2.6.0 (released 15/05/2013):
+
+  Fixed
+    1- Euclidean chains were confused by t_POLs with t_INTMOD/t_POLMOD coeffs
+       [ because a 0 polynomial could have lg() == 3 ]
+    2- numerical instability in ellheightoo [#1204]
+    3- Flm_gauss(m, smallish p): fix and enable the OK_ulong switch [ faster ]
+    4- remi2n didn't allow negative arguments [native kernel] [#1215]
+    5- ellrootno(e, p) started by computing a global minimal model for e
+       instead of a local one at p.
+    6- qfbred(t_QFR) wrong when frac( sqrt(D) ) > 0.5
+    7- add GC in sumalt()
+    8- problems with diagnostic messages when changing a 'default', e.g. \o3
+    9- divru() could call bfffo(0) => overflow in expo()
+AMe10- (t_FRAC<0) * t_INTMOD => wrong result
+   11- trace(t_POL or t_SER) could yield an unormalized object [0 leading term]
+LGr12- off-by-1 error in primepi() when argument is = primelimit
+   13- bestappr(0.*x+1) -> incorrect object
+   14- znlog(3, Mod(3,8), 2) -> error
+   15- crash when default(prettyprinter,...) points to a non-existent command
+   16- qfperfection(): wrong result in unlucky cases [#1223]
+   17- allocatemem() did not reset parse error [#1226]
+BA 18- (x->vector(1,j,x))(1) --> significant pointers lost [#1211]
+VL 19- [makefile] Incorrect -I and -L flags order [#1212]
+   20- nfbasis / nfdisc(f,, partial factorization) would sometimes spend a
+       lot of time trying to factorize a discriminant [#1227]
+   21- numerical instability in qfgaussred / qfsign [#1232]
+   22- missing consistency checks in conjvec [#1231]
+   23- numerical instability in polredabs [#1228, #1229]
+   24- wrong result in bezoutres [#1233]
+   25- wrong sign in polresultant() [#1234]
+   26- change default(realprecision) so that it returns the internal precision
+       [ as precision(1.) ], not the number of printed digits. [#1235]
+   27- subst(Y/X,X,x) -> 0
+PB 28- polrootsff(x^2-x-ffgen((v^2+1) * Mod(1,3))) -> SEGV [#1241]
+   29- intnum(t=[0,0],[1],(sin(t)/t)^2) -> "precision too low in mpsc1"
+BA 30- functions ending by the x[y]=z construct could corrupt the stack.
+   31- nfbasis(pol, 0, factorization containing (-1)^1) -> wrong [#1244]
+   32- qfminim(): better type checks for matrix entries
+   33- qfminim(): incorrect GC in smallvectors()
+   34- padicappr(x^2+1+O(3), -1+O(5^10)) --> no error
+   35- tests test-kernel and test-ploth were broken
+   36- lots of missing sanity checks in hilbert(x,y,p)
+       [ e.g. hilbert(O(3), Pi, 5) -> 0 ]
+BA 37- for(i=1,10^7,) + SIGINT -> SEGV [#1249]
+   38- ellwp: take flag into account also for t_SER output [ was: numerical
+       case only ]
+   39- factor(p) was much slower than isprime(p) for p a "small" prime
+BA 40- mateigen precision error was not trappable.
+   41- accuracy problems in bnfisunit [#1253]
+   42- broken rnfeltup [#1255]
+   43- x===y was always wrong for x,y t_SER
+BA 44- gamma(t_SER) with positive valuation did not work.
+   45- ispower(x < 0) could return an even value ! [#1259]
+   46- ispower(1 / n) return a wrong result [#1259]
+BA 47- [breakloop] initial context could be lost.
+BA 48- Ser([1+O(x)],x) returned an invalid object.
+BA 49- ispseudoprime used too much stack for huge operands.
+   50- [rnfidealup doc] idealgentoHNF() was incorrect.
+DS 51- elldivpol returned wrong result for degree n >=8.
+   52- overflow in mpbern(n) for huge n [#1256]
+   53- idealfactor(nf, non integral ideal) would remove a rational content
+       instead of factoring A/B with A,B coprime integer ideal. Which led
+       to costly factorizations [#1158]
+LGr54- the global variables associated to the primetable _maxprime / diffptr
+       could become inconsistent
+   55- psdraw() ignored plot colors
+BA 56- stack_base() was not C89 standard compliant.
+BA 57- my(f(x)=x+1);f --> SEGV                                       [#1273]
+   58- ellheight([0, 0, 1, -1, 0], [0., 0.]) -> BUG
+   59- allow bnrL1 over Q [#1279]
+   60- factorpadic(,,,1) => SEGV when linear factors/Q [#1278]
+   61- mathnf(..., 4) didn't remove 0 columns from HNF [#1271]
+JD 62- possible SEGV in pari_init_opts [#1264]
+BA 63- ellorder(E,P*Mod(1,p)) did not work if E was defined over Z
+   64- allow writebin to save user functions (t_CLOSURE) [#1171]
+   65- sqrtn(1+O(5),5) -> error, ispower(1+O(5),5) -> error [#1261]
+   66- parimacro.tex: pdftex --output-format dvi  would produce a PDF
+BA 67- pari_close failed to free all allocated memory [#1297]
+BA 68- pari_close did not restore GMP memory functions
+   69- avoid outputing ANSI escape sequences to reset terminal state when
+       unnecessary [#1289]
+BA 70- ellpointtoz was numerically unstable
+   71- issquarefree(0) => error [#1304]
+   72- sizebyte(t_LIST) returned the size of the wrapper type, not the list
+       itself [#1307]
+   73- ellgroup(E over Q, p) now returns the structure of the group of
+       non-singular points, when the curve has bad reduction at p.
+   74- factor_proven not taken into account in factor(n), where n is a
+       BPSW-pseudoprime
+   75- core(5*(huge composite)^2) was very slow [#1305]
+   76- sqrt(0.E-97-1.12-97*I) -> div by 0 [#1309]
+BA 77- FpX_gcd(0,x) -> div by 0
+   78- x; y; p; q; subst(Mod(q,p), q, x + y) --> Mod(1, p)*x [#1321]
+   79- off-by-1 error when placing prime table sentinel after e.g.
+       default(primelimit, 2);
+   80- t_REAL with huge exponents print incorrectly [#1322]
+   81- a1;a2;a3;a4;a6;ellinit([a1,a2,a3,a4,a6],1) was very slow
+BA 82- FpX_factorff and FpX_rootsff were not using standard argument order
+PB 83- printf("%.6f", 5E-5) -> SEGV [#1328]
+BA 84- diffop(Mod(y,x^2-y),[y],[1]) -> wrong result
+BA 85- calling a GP function with a lot of omitted arguments could cause a crash
+GH 86- missing solutions in thue(), e.g. thue(thueinit(x^3+92*x+1),3^3)
+   87- t_RFRAC == t_RFRAC could return a wrong result [#1339]
+   88- obscure bug in polroots() (sage #13314)
+   89- polinterpolate([1,2] * Mod(1,7), [2,3], 0) -> incorrect type in gabs
+BA 90- printf() did not flush output
+   91- logint() used too much memory and could return a wrong result:
+       logint(3^64, 3) --> 33
+   92- eint1(x < 0) normalization. Now eint1(x) = incgam(0, x) [#418]
+   93- polrootsff(2*x+1,2,y) -> SEGV [#1350]
+   94- resultant(x,x,y) -> 0 and related problems
+   95- thue((x^4+1)^2, 4) -> no solution
+BA 96- idealramgroups() and idealfrobenius() did not accept a bnf
+   97- using new operators with default(compatible,3) caused a crash
+   98- rare corruption in ECM [#1240] factorint(,1) => SEGV
+   99- add missing call to normalize() in lift(t_POL/t_SER) [#1359]
+BA 100- listput was not SIGINT safe
+BA 101- znorder(Mod(5,16),200) -> 5 instead of 4
+   102- e.tate lost accuracy
+   103- 2-adic ellinit
+   104- allow ellinit over C
+   105- ellpointtoz(E / Qp, ...)  [ wrong result / error messages ]
+JD 106- galoisinit(x^3+x^2-2*x-1) -> uninitialized read [#1389]
+BA 107- polhermite(66) -> corrupted result [#1393]
+   108- nfhnf() would only accept matrices of maximal rank
+BA 109- galoisfixedfield() could return a wrong result for polynomials with
+        large roots [#1406]
+   110- bnrdisc could return a wrong result if the modulus had more than
+        2 prime divisors [#1399]
+   111- a = Mod(1,2)*x; gcd(a,a) return x instead of Mod(1,2)*x
+   112- changing primelimit from within forprime loop yielded unpredictable
+        results (potential SEGV)
+   113- y;z;(x^2+y)*w/z/x -> significant pointers lost [#1248]
+   114- log(t_FRAC close to 1) => junk (idem lngamma) [#1238]
+   115- thue(thueinit(x^3-1493,1),3) -> error
+LGr116- typo in modr_safe -> precision error [#1413]
+   117- nfhnfmod(non-integral module) => wrong result
+        nf=nfinit(y); A = [[1,1/2;0,1],[1,1]];
+        nfhnfmod(nf, A, nfdetint(nf,A))
+BA 118- [native kernel] FFT mulii relied on unspecified order of evaluation
+
+  Changed
+    1- mathnf: swapped flag 3 and 5.
+    2- allow ellinit([a4,a6]) [#1213]
+    3- ellinit(... singular curve ...]): return [] instead of raising an error
+    4- GP set elements are no longer converted to t_STR (requiring "eval" to
+       recover the underlying object). Arbitrary GENs can be stored in a set.
+    5- moved Odos/* to relevant src/systems subdirectory
+    6- removed Odos directory
+    7- no longer allow Vec()/Vecrev(), Col(), Vecsmall(),  without argument.
+       Use [], []~, Vecsmall([]), respectively.
+    8- allow specifying an optional dimension in Vec/Vecrev, Col, Vecsmall.
+    9- allow Vecsmall(t_POL or t_SER)
+LGr10- nicer printout of small matrices: justify columns
+   11- improve gmul(t_REAL, t_FRAC) when numerator of t_FRAC is 1
+BA 12- [darwin] do not use dylib_ prefix to ld options [#1210]
+   13- allow idealhnf(nf, t_QFI / t_QFR) for quadratic fields
+   14- no longer allow 3 arguments in bestappr() [useless], no longer
+       mix Pade approximants and continued fractions [too confusing: must a
+       t_SER be converted to a t_RFRAC or be treated coefficientwise?]
+   15- if znlog() has no solution, return [] instead of error
+   16- znlog(x, g): no longer assume that g is a primitive root, nor that the
+       underlying (Z/N)^* is cyclic
+   17- renamed gen_eltorder -> gen_order
+   18- logfile: strip properly color escape sequences [#1225]
+   19- change nfbasis(T, flag, fa) to nfbasis(T, listP). flag was used to invoke
+       round2 instead of round4 (inefficient=> useless) OR to only
+       partially factor poldisc(T), up to primelimit (very dangerous since
+       primelimit is a global variable). Now listP describes a list of primes,
+       and we return a basis of an order which is p-maximal at all those primes:
+       either a vector of primes, a factorisation (as fa before) or an integer
+       B to indicated {p <= B} (a safe and flexible version of nfbasis(T, 1)).
+       nfdisc() was changed similarly.
+   20- first call isanypower() in BPSP_psp_nosmalldiv() when input is > 2^512
+       [ < 1% of required time, 1 order of magnitude faster when it succeeds ]
+       [ #1074 ]
+   21- sort polroots() output so that it no longer depends on current precision
+   22- delete README-subversion, replace with README-git
+   23- move README.os2 to src/system/os2/README
+   24- change compiler version printout in gp header
+   25- much faster final divisibility test in nfgcd() [#1214]
+   26- error type sqrter5 "not a quadratic residue in sqrt" now generalized
+       to denote "not an n-th power residue in sqrtn"
+BA 27- global_err_data is now a GEN.
+BA 28- more usable form of alarm(s, code): evaluate code, aborting after s
+       seconds. Return the result or a t_ERROR object.
+   29- [non-Unix systems] the name of the preferences file is now "gprc.txt"
+       (it remains ".gprc" on Unix systems)
+   30- also look for the preferences file in PARI's "datadir"
+   31- removed rootsold() code : polroots(x, 1) is no longer accepted
+   32- rewrite ellan using C-longs + new function anellsmall()
+   33- renamed all libpari error codes [ pari_err() arguments ]
+   34- allow t_VECSMALL in vecextract
+   35- look for a few more short vectors in polred(), only return subfields
+       and the best primitive polynomial [wrt discriminant]
+   36- [library] remove precdl argument in ellwp0: use a t_SER argument like
+       in all other transcendental functions.
+   37- ellsigma / ellzeta: allow t_SER arguments, like ellwp.
+   38- polcoeff(x+2*y,1,y) -> 2  [ was 2*x^0 ]
+   39- quadhilbert / quadray: replace final polredabs call by polredbest [#1025]
+   40- listsort() now uses the cmp() comparison function, and becomes fully
+       compatible with setsearch() [#1167]
+   41- vecsort(,,2) [lexicographic order] is now always on. The flag is
+       now deprecated and ignored.
+   42- allow t_SER with integral coefficients to use asymptotically fast
+       (ZX_mul / ZX_sqr) polynomial multiplication
+LGr43- let initprimes0 use an existing prime table
+   44- rename stackmalloc -> stack_malloc. Add stack_calloc
+   45- matimagecompl() now returns a permutation (t_VECMALL) no longer a t_VEC
+   46- remove config/gitversion from 'make snapshot' tarball
+JM 47- Better implementation of ellweilpairing/elltatepairing
+BA 48- agm now returns the optimal AGM
+   49- unify make_emacs_tags / make_vi_tags => make_tags + fix problems for
+       (exuberant-ctags)-based etags. Both tag files (emacs / vi) contain
+       the same tags now.
+   50- ellglobalred(E) now also returns the conductor factorization [4th compo]
+   51- library functions *_incremental_CRT no longer need the product of the
+       moduli, instead they compute it and update the running modulus.
+   52- factor_proven now affects all the factoring machinery, as documented, i.e
+       also multiplicative function (moebius, sumdiv,...)
+   53- allow t_VECSMALL in lex()
+   54- nfrootsof1(K) check whether K.pol is a translate of a cyclotomic pol.
+       [initial patch LGr, #1175]
+   55- format of cached Bernoulli table: now a t_VEC of t_FRAC / t_REALs.
+       Removed bern() macro. The new data must be accessed using bernfrac /
+       bernreal.
+   56- [libpari] simplify init_primepointer(n, p, &pd) interface
+       => init_primepointer(n, &pd). Remove argument 'p', ignore the previous
+       value of pd [ remove assumption that it pointed into a prime table ]
+       and always set pd to a pointer into the private prime table.
+   57- forprime loop: no longer allow to modify the loop index
+       ? forprime(p = 2, 10, p = [])
+        ***   at top-level: forprime(p=2,10,p=[])
+        ***                                   ^---
+        ***   prime index read-only: was changed to [].
+   58- faster forparii() [ for() with t_INT lower bound ]
+   59- forprime(p = a, b, ...) now iterates over arbitrary ranges of primes,
+       independently of 'primelimit'. Parameter 'b' can be omitted (no upper
+       limit). More generally primelimit is no longer a true limit to iterate
+       over primes: all libpari functions use the forprime_t interface
+   60- rename ggval -> gvaluation
+BA 70- GP function ellpow is renamed to ellmul
+BA 71- rename powell->ellmul, addell->elladd, subell->ellsub
+   72- is_pth_power interface [ pass a forprime_t iterator ]
+   73- polrootsmod(, 4) is no longer accepted
+   74- revert to Ramanujan's formula to compute Pi [ + binary splitting ]
+   75- polinterpolate(,, 'x) use divide & conquer algorithm
+BA 76- binary(0) now return []
+   77- polisirreducible() now avoids factoring in most cases
+HC 78- reimplement incgam()
+   79- allow eint1(t_COMPLEX)
+   80- when 'echo = 1', no longer echo commands if they were entered
+       interactively [commands ended up printed twice]
+   81- unless 'echo = 1', no longer log commands entered non-interactively
+       when 'log = 1'. '\r file' used to log the entire content of 'file'.
+   82- allow thue(t_POL, rhs) for thue(thueinit(t_POL, rhs))
+   83- elltors now uses division polynomials by default
+   84- modified "Hit Return to Continue" message so that it becomes a comment
+       when copy-pasted
+   85- rnf_fix_pol() takes an extra argument, the calling function's name
+   86- fast gerepilecopy() for leaves
+   87- rename leftright_pow_fold -> gen_pow_fold, leftright_pow_fold_i
+       -> gen_pow_fold_i
+   88- upowuu now returns 0 on overflow
+   89- primes(n) no longer needs precomputed primes
+   90- prime(n) no longer needs precomputed primes
+   91- primepi(n) no longer needs precomputed primes
+   92- removed 3s delay when recompiling PARI after modifying a header
+       [ Was there to avoid problems on slightly out-of-synch NFS fileserver
+       host. Had become an annoyance on fast multicore servers, esp. when
+       bisecting to find a broken commit. ]
+   93- improve Configure --tune + let tune -t/-tt/-ttt print more and more
+       verbose messages [ old 'tune -t' corresponds to current 'tune -tt' ]
+   94- arithmetic functions no longer accept vector / matrix arguments [ to
+    later allow passing factorization matrices ]: use apply()
+BA 95- rename RgX_check_ZXY -> RgX_check_ZXX, ZXY_max_lg -> to ZXX_max_lg
+   96- elleta() and elleisnum(e, 2) to use a theta series formula in
+       O~(prec^(3/2)) instead of O~(prec^2).
+BA 97- zv_cmp0 renamed to zv_equal0
+   98- allow ellinit / Qp for arbitrary reduction type
+   99- ellpointtoz(E / Qp, ...), now return phi(P) [ used to return the same
+       result for phi(P) and phi(-P) [ split multiplicative reduction ],
+       resp. phi(P) + 1/phi(P) [ non-split reduction ]
+  100- ellinit(E / Qp).tate : the u component is now always a square root
+       of u2, also in the non-split case (in which case it lives in a
+       quadratic extension of Qp)
+  101- renamed library function divsum() => sumdivexpr()
+  102- listpop(empty list) => no-op [ was "domain error" ]
+  103- ellap, ellak, ellan: allow non-minimal model
+  104- when timer = 1, no longer print timing if the time is negligible;
+       in particular, no timing should be printed when defining a user function
+       or and alias.
+  105- The proper way to initialize an nf structure when the polynomial
+       discriminant is hard to factor is nfinit([T, listP]), where listP
+       specifies a list of primes (see ??nfinit). nfdisc, nfbasis, all the
+       polred functions allow analogous arguments (see Changed-19). This is
+       cleaner and more flexible than old flags relying on the value
+       primelimit (e.g. nfinit([T, nfbasis(T, 1)]), now deprecated). Also,
+       the nfinit function now sees the local specifications and can
+       take steps to avoid problems (instead of taking for granted a basis,
+       without knowing whether it is correct or not). The result can
+       also be certified (nfcertify)
+  106- polredabs() with (deprecated) nf_PARTIALFACT flag (or new [T,listP]
+       argument) now returns 0 if the resulting order cannot be proven to be
+       maximal.
+  107- rename bezout() -> gcdext(), polresultant() -> polresultantext()
+  108- the prime table is now computed once and for all on startup and can
+       no longer be increased by modifying primelimit: the dynamic forprime
+       machinery allows fast primes up to primelimit^2 which is more than
+       enough even with a small table. The default value of 500.000 is already
+       larger than necessary
+  109- removed the mechanism allowing prime gaps larger than 255 in the
+       prime table: as a result it now limited to 436273290, allowing fast
+       primes up to 1.9 10^17
+BA110- permtonum/numtoperm now use the standard lexicographic numbering
+  111- issquare(t_INTMOD), ispower(t_INTMOD): factor modulus incrementally,
+       in case we hit a local non-residue -> early abort [#1376]
+  112- gphelp + OS/X : make "open" the default PDF viewer (was "acroread")
+  113- renamed exp_Ir -> expIr
+
+  Added
+    1- mathnf for matrices over K[X] [#41]
+BA  2- GP function ellheegner
+BA  3- asm inline macro addllx8 for faster addition
+BA  4- Library function FpXQ_autpowers
+    5- GP default 'linewrap'
+    6- functions Fp_issquare, Fq_issquare.
+    7- GP function cmp [ universal comparison ]
+    8- library functions Fp_addmul, addmulii, addmulii_inplace, addmuliu,
+       addmuliu_inplace, lincombii, mulsubii, submulii, submuliu,
+       submuliu_inplace
+LGr 9- Catalan's constant [ Catalan() ]
+BA 10- library functions F2x_issquare/F2x_sqrt
+BA 11- [INSTALL] Documentation of RUNTEST
+   12- library function bestapprPade
+   13- library function gen_factored_order, Fp_factored_order
+   14- macros retmkvec, retmkvec2, retmkvec3, retmkvec4, retmkcol, retmkcol2,
+       retmkmat, retmkmat2, retmkintmod, retmkpolmod
+       retmkintmod, retmkpolmod, retconst_col, retconst_vec
+   15- allow Ser(t_VECSMALL)
+BA 16- library function gsprintf/gvsprintf
+BA 17- new PARI type t_ERROR (error messages)
+BA 18- new error trapping system and GP functions iferr,iferrname
+BA 19- implement lngamma(t_PADIC)
+LGr20- new PARI functions F2m_gauss, F2m_inv, F2m_rank, F2m_image, matid_F2m,
+       F2m_mul, F2m_F2c_mul
+BA 21- GP function getenv
+   22- new error class e_PRIME [ pari_err() ]
+BA 23- low-level function int_bit
+   24- library function stack_strcat
+   25- function polredbest [ adapting a preliminary patch by BA ]
+BA 26- library functions Fl_invsafe, F2x_halfgcd, Flx_Flxq_eval and Flx_FlxqV_eval.
+BA 27- support for sparse matrix and Wiedemann algorithm
+   28- GP function vecsearch() [ use with vecsort() ]
+BA 29- library function Z_issmooth
+BA 30- linear sieve algorithm for Fp_log/znlog
+BA 31- library functions Flx_to_FlxX, F2m_to_Flm, F2c_to_Flc, and Flxq_powu
+   32- GP function idealnumden()
+LGr33- library function uprecprime()
+   34- library function ZM_pivots()
+   35- library functions nm_Z_mul, ZM_togglesign, ZM_nm_mul [ to improve Zlm_gauss ]
+BA 36- [breakloop] GP functions dbg_up/dbg_down (like gdb up/down)
+   37- library functions rootsof1_Fp, rootsof1u_Fp, rootsof1_Fl
+BA 38- GP functions dbg_x (like \x) and dbg_err (current error data)
+   39- matconcat()
+BA 40- library functions Flm_Fl_add, Flm_invimage, FlxY_evalx
+BA 41- library functions Flx_ffisom, Flx_ffintersect, Flxq_ffisom_inv
+BA 42- library functions Flx_is_irred, Flx_is_smooth, F2x_is_irred
+BA 43- accessors functions for t_CLOSURE: closure_arity,closure_codestr,closure_get_*
+   44- library functions ZMs_ZC_mul, ZpMs_ZpCs_solve, gen_ZpM_Dixon
+BA 45- [breakloop] GP function breakpoint
+BA 46- GP function ffnbirred
+BA 47- cubic sieve algorithm for Flxq_log
+BA 48- library functions F2x_F2xqV_eval, F2x_F2xq_eval
+BA 49- forqfvec()
+BA 50- library functions FqM_image, FqM_rank, FpXQXQ_powers, FpXQXQ_matrix_pow
+BA 51- ellgroup(,,1): also return the generators
+BA 52- library functions FpVV_to_mod, FpE_changepoint, FpE_changepointinv
+BA 53- GP syntax [a..b] : [a,a+1,...,b]
+BA 54- GP syntax [a(x)|x<-b,c(x)] : apply(a,select(c,b))
+BA 55- GP syntax M[a..b,^c] : vecextract(M,"a..b","^c")
+BA 56- library function FpE_log
+   57- select(f, v, 1) for indirect selection
+   58- hamming() function [initial implementation CG]
+   59- ispowerful() function
+   60- polgraeffe() function
+   61- functions poliscyclo(), poliscycloprod(), polcyclofactors()
+   62- function setbinop(f,X,Y) = { f(x,y), x in X, y in Y }
+   63- libpari function moebiusu()
+   64- sumdigits() function
+   65- libpari functions addiu, addui, subiu, subui, uissquare
+   66- randomprime() function. Allow random([a,b])  (return n, a <= n <= b)
+   67- ispolygonal() function
+   68- libpari functions uissquarefree, uposisfundamental, unegisfundamental
+   69- istotient() function
+   70- implement Haible/Papanikolaou binary splitting
+BA 71- PARI functions FlxqX_nbroots, FpXQX_nbroots, FpXQX_nbfact.
+BA 72- PARI function zv_search
+BA 73- GP syntax: multiif if(a==1,b,a==2,c,default).
+BA 74- GP syntax: multi assignement: [a,b,c]=V -> a=V[1];b=V[2];c=V[3]
+BA 75- PARI functions gen_gener, gen_ellgroup, gen_ellgens, gen_powers
+BA 76- FlxqE functions family (for elliptic curves over field of small characteristic>3)
+BA 77- PARI functions Flxq_sqrt, FpXQ_sqrt
+   78- bernpol() function
+   79- sumformal() function
+BA 80- PARI functions ZX_equal1, zvV_equal, ZXV_equal, FpXX_neg, FqX_neg
+BA 81- FpXQE functions family (for elliptic curves over field of large characteristic)
+BA 82- added GP function ellcard
+BA 83- PARI functions FpXQ_powu, Fq_powu, FpXX_mulu, Fq_mulu, Fq_div
+BA 84- PARI functions FqXQ_powers, FqXQ_matrix_pow, FqX_mulu, FqX_Fq_add
+BA 85- PARI functions FqXY_eval, FqXY_evalx, FpXY_Fq_evaly
+BA 86- SEA over non-prime finite field (for char p > 1000)
+LGr87- Add clock_gettime timer option
+   88- add new error type e_DOMAIN
+BA 89- Add black box finite fields for generic linear algebra
+BA 90- PARI functions FlxqM_image, FlxqM_ker, FqM_deplin
+BA 91- GP function ellneg
+BA 92- PARI functions Fp_ellcard, FpXQ_ellcard, Flxq_ellcard
+LGr93- vecmax / vecmin: add optional pointer argument (to hold index of a
+       largest/smallest entry)
+   94- printsep() function
+CG 95- isprimepower() function
+   96- PARI functions F2v_to_F2x, F2x_valrem, F2x_deflate, F2x_shift
+   97- PARI function RgV_polint
+BA 98- GP function digits
+   99- GP default 'sopath' [ rewritten from initial patch by GTo ]
+LGr100- allow polylog(n, t_SER) around a != 0
+BA 101- PARI functions ZX_shifti, ZX_remi2n, ZXV_remi2n
+   102- PARI functions cxexp1, mpsincos1
+BA 103- GP function ellfromj
+   103- GP function forcomposite
+   104- new error class e_PRIORITY [ pari_err() ]
+BA 105- Add black box algebra for Brent and Kung algorithm
+   106- PARI function RgM_dimensions()
+BA 107- PARI functions RgX_splitting(), Flx_splitting()
+   108- made public the CATCH / TRY interface, renamed pari_CATCH / pari_TRY
+        PARI functions err_get_num(), err_get_compo(), pari_err_last()
+   109- PARI function stack_sprintf()
+   110- PARI function RgX_is_QX()
+BA 111- PARI functions retmkmat2,retmkcol2,mkmat2,mkcol2,mkcols,mkcol2s et al.
+BA 112- PARI functions ZXV_dotproduct(), ZXX_Z_divexact()
+BA 113- PARI function gen_ZpX_Newton()
+   114- optional argument to contfracpnqn: return all convergents up to
+        p_n/q_n, not only the last 2
+BA 115- PARI functions Flxq_autpow, F2xq_autpow
+BA 116- PARI functions FpX_divrem_Barrett and unconditional FpX_rem_Barrett
+BA 117- PARI functions F2xq_sqrt_fast, Flxq_lroot, Flxq_lroot_fast
+BA 118- PARI functions FlxqV_dotproduct, FlxV_red
+BA 119- PARI functions ZpXQ_inv, ZpXQ_invlift, ZpXQ_log
+   120- PARI functions absi_shallow, mpabs_shallow, absfrac_shallow,
+        Q_abs_shallow
+BA 121- PARI functions FlxX_Flx_add, FlxX_Fl_mul, FlxX_Flx_mul, FlxX_neg
+BA 122- PARI functions Fp_ellj, FpXQ_ellj, Flxq_ellj
+BA 123- PARI functions FpX_mulu, Flx_mulu, ZX_mulu
+BA 124- PARI functions FlxqXQV_autpow, FlxqXQV_autsum
+BA 125- PARI functions FpXQXQV_autpow, FpXQXQV_autsum
+BA 126- PARI functions FpXT_red, FlxT_red, ZXT_to_FlxT, ZXT_remi2n
+BA 127- Support for preconditionned reduction in FpXQ/Flxq
+   128- PARI functions padic_to_Q, padic_to_Q_shallow, QpV_to_QV, Q_pvalrem,
+        ZX_Zp_root, Zp_appr, Fp_muls, retmkfrac
+BA 129- Add safegel et al. for GP2C -C option
+HC 130- Function sqrtnint
+   131- sumdivmult() to sum multiplicative functions
+   132- ?? online help: allow searching labels, e.g. ??"se:priority"@
+   133- PARI function ZpM_echelon, zlm_echelon
+   134- GP functions matqr and mathouseholder, PARI functions QR_init,
+        QgM_QR_init, gaussred_from_QR, R_from_QR, gtomp, RgC_gtomp, RgM_gtomp
+   135- PARI functions trivial_fact, prime_fact
+   136- PARI function rfrac_to_ser
+   137- PARI functions padic_lindep, Xadic_lindep
+   138- GP function seralgdep
+BA 139- arm, mips and mips64 level0 inline assembly kernel
+   140- new error class e_COMPONENT
+   141- PARI functions init_primepointer_geq, init_primepointer_gt,
+        init_primepointer_leq, init_primepointer_lt
+BA 142- new default strictargs for mandatory arguments
+   143- GP function nfcertify
+   144- GP function ellchangepointinv
+   145- optional 'variable' argument to polresultantext()
+   146- export part of the ifac_* interface (ifact_start, ifact_next,
+        ifac_read, ifac_skip, ifac_isprime)
+   147- PARI function expIxy
+   148- poor man's graphic engine 'plotps' (Configure --graphic=ps) when
+        no graphic library is available; dumps the hi-res plot to a temporary
+        PostScript file, then opens a PostScript viewer ('open -W' by default,
+        $GP_POSTSCRIPT_VIEWER otherwise). Works around #1354 on OS/X.
+PMo149- GP function lambertw / library functions mplambertW, glambertW
+
+  Removed
+    1- dropped DOS support
+    2- qfrep(): bit 2 of flag is now meaningless, we now always return a
+       t_VECSMALL
+    3- file language/errmsg.c and global errmessage[] array: all error
+       messages are now part of pari_err_display())
+    4- error types arither1,mattype1,notpoler (merged with typeer), matinv1
+       (merged with gdiver)
+    5- legacy lindep and PSLQ implementations [algdep/lindep with
+       negative flags]: now use LLL in all cases.
+BA  6- [libpari] removed unusable functions dbg_close/dbg_release.
+    7- [libpari] gisfundamental, gkronecker, gbigomega, geulerphi,
+       gissquarefree, gmoebius, gnextprime, gnumbdiv, gomega, gprecprime,
+       gsumdiv, gdumdivk, znprimroot0
+    8- ellsigma: flags 3 and 4 [ inefficient algorithm using the product
+       formula ]
+    9- Member function 'w' (this is technical, and no longer needed:-)
+   10- obsolete function weipell(). Use ellwpseries()
+   11- [libpari] obsolete function Polred. Use polredbest
+   12- old logo misc/pari.xpm, see http://pari.math.u-bordeaux1.fr/logo.html
diff --git a/COMPAT b/COMPAT
new file mode 100644
index 0000000..a32a8f0
--- /dev/null
+++ b/COMPAT
@@ -0,0 +1,652 @@
+This file lists the incompatible changes between Version 2.x and older versions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.6 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+  - eval() no longer evaluates its arguments in "permissive" mode: eval("1a")
+    now produces a syntax error.
+  - '|' is no longer a synonym for '||' (boolean 'or'); '&' for '&&' is still
+    supported but deprecated.
+  - GP set elements are no longer converted to t_STR (requiring "eval" to
+    recover the underlying object). Arbitrary GENs can be stored in a set.
+  - no longer allow 3 arguments in bestappr() [useless], no longer
+    mix Pade approximants and continued fractions [too confusing: must a
+    t_SER be converted to a t_RFRAC or be treated coefficientwise?]. Use
+    bestapprPade() to obtain rational functions, and bestappr() to obtain
+    rational numbers.
+  - [non-Unix systems] the name of the preferences file is now "gprc.txt"
+    (it remains ".gprc" on Unix systems)
+  - removed rootsold() code : polroots(x, 1) is no longer accepted.
+  - format of cached Bernoulli table: now a t_VEC of t_FRAC / t_REALs.
+    Removed bern() macro. The new data must be accessed using bernfrac /
+    bernreal.
+  - forprime loop: no longer allow to modify the loop index
+    ? forprime(p = 2, 10, p = [])
+     ***   at top-level: forprime(p=2,10,p=[])
+     ***                                   ^---
+     ***   prime index read-only: was changed to [].
+  - rename ellpow -> ellmul
+  - polrootsmod(, 4) is no longer accepted. The second argument must be prime.
+  - binary(0) now return []
+  - arithmetic functions no longer accept vector / matrix arguments [ to
+    later allow passing factorization matrices ]: use apply()
+  - permtonum/numtoperm now use the standadr lexicographic numbering.
+  - ellsigma: removed flags 3 and 4 [ inefficient algorithm using the product
+    formula ]
+  - removed member function 'w' (this is technical, and no longer needed:-)
+  - ellpointtoz(E / Qp, ...), now return phi(P) [ used to return the same
+    result for phi(P) and phi(-P) [ split multiplicative reduction ],
+    resp. phi(P) + 1/phi(P) [ non-split reduction ]
+  - ellinit(E / Qp).tate : the u component is now always a square root
+    of u2, also in the non-split case (in which case it lives in a
+    quadratic extension of Qp)
+  - when timer = 1, no longer print timing if the time is negligible;
+    in particular, no timing should be printed when defining a user function
+    or and alias.
+  - change nfbasis(T, flag, fa) to nfbasis(T, listP). flag was used to invoke
+    round2 instead of round4 (inefficient=> useless) OR to only
+    partially factor poldisc(T), up to primelimit (very dangerous since
+    primelimit is a global variable). Now listP describes a list of primes,
+    and we return a basis of an order which is p-maximal at all those primes:
+    either a vector of primes, a factorisation (as fa before) or an integer
+    B to indicated {p <= B} (a safe and flexible version of nfbasis(T, 1)).
+    nfdisc() was changed similarly.
+  - The proper way to initialize an nf structure when the polynomial
+    discriminant is hard to factor is nfinit([T, listP]), where listP
+    specifies a list of primes (see ??nfinit). nfdisc, nfbasis, all the
+    polred functions allow analogous arguments. This is cleaner and more
+    flexible than optionnal flags relying on the value primelimit
+    (e.g. nfinit([T, nfbasis(T, 1)]), now deprecated). Also, the nfinit
+    function now sees the local specifications directly and can take steps
+    to fix problems. The result can also be certified (nfcertify)
+  - renamed bezout() -> gcdext(), polresultant() -> polresultantext()
+  - the prime table is now computed once and for all on startup and can
+    no longer be increased by modifying primelimit: the dynamic forprime
+    machinery allows fast primes up to primelimit^2 which is more than
+    enough even with a small table. The default value of 500.000 is already
+    larger than necessary
+  - removed optional flag to factorpadic() / factorpadic0() [ enabling
+    Buchman-Lenstra + round2 ] Use the default factorpadic(T, p, r), both
+    under GP and in library mode.
+  - default help text for a user function is now as in \u: ?fun produces
+      fun =
+        (args)->body
+  - after addhelp(f,...), ?f no longer include default help text for a
+    user function (function code). Type 'f' to see the function code.
+  - rnf structure: added new components
+  - lift(x,'v) / centerlift(x,'v) now only lift t_POLMODs in variable v,
+    no longer (most) t_INTMOD / t_PADICs met along the way
+  - rnf.pol (absolute defining polynomial / Q) is now called rnf.polabs,
+    rnf.pol is now the relative polynomial, defining the relative extension
+    over the base.
+  - the prid structure returned by idealprimedec: the anti-uniformizer
+    tau (pr_get_tau / pr[5]) is now stored via its multiplication table
+
+* Specific to the PARI library:
+===============================
+  - renamed all libpari error codes [ pari_err() arguments ]
+  - ellwp0(): remove precdl argument
+  - rename stackmalloc -> stack_malloc
+  - rename ggval -> gvaluation
+  - simplify init_primepointer(n, p, &pd) interface
+    => init_primepointer(n, &pd). Remove argument 'p', ignore the previous
+    value of pd [ remove assumption that it pointed into a prime table ]
+    and always set pd to a pointer into the private prime table.
+  - powell, addell, subell renamed to ellpow, elladd, ellsub.
+  - last tow arguments of FpX_factorff and FpX_rootsff have been swapped.
+  - rnf_fix_pol() takes an extra argument, the calling function's name
+  - rename leftright_pow_fold -> gen_pow_fold, leftright_pow_fold_i
+       -> gen_pow_fold_i
+  - upowuu(x,n) now returns 0 on overflow. Used to return x^n mod 2^BITS_IN_LONG
+  - rename RgX_check_ZXY -> RgX_check_ZXX, ZXY_max_lg -> to ZXX_max_lg
+  - removed gisfundamental, gkronecker, gbigomega, geulerphi, gissquarefree,
+    gmoebius, gnextprime, gnumbdiv, gomega, gprecprime, gsumdiv, gdumdivk,
+    znprimroot0
+  - rename zv_cmp0 -> to zv_equal0
+  - removed obsolete function weipell(). Use ellwpseries()
+  - the prototype of nfbasis() changed [ remove 'flag'; if you needed it, use
+    nfmaxord() ]. The old nfbasis0 / nfdisc0 are deprecated: don't use them.
+  - removed obsolete Polred(): use polred() or polredbest()
+  - rename exp_Ir -> expIr
+  - the *_invimage function no longer accept a t_COL second argument. Use the
+    RgM_RgC_invimage variant. They now return NULL when no solution exist
+    (used to return a t_COL / t_MAT of length 1)
+  - the forvec_start() function has been replaced by a standard
+    iterator: forvec_init() / forvec_next()
+  - renamed rnfelementxxx -> rnfeltxxx, rnfidealhermite -> rnfidealhnf
+  - removed useless wrappers map_proto_GG, map_proto_GL, map_proto_lGG
+  - renamed ZM_hnfremdiv -> ZM_hnfdivrem
+  - removed useless functions gand, gor
+  - removed useless function ratlift [ use Fp_ratlift ]
+  - renamed gcmpX -> gequalX
+  - renamed ordred -> polredord: useless function, use polredbest!
+  - renamed recip -> serreverse
+  - renamed FlxYqQ_pow -> FlxYqq_pow
+  - FpXYQQ_pow/FlxYqq_pow: the order of the moduli S,T have been swapped to
+    match FpXQXQ_pow
+  - renamed gsh -> gsinh, gch -> gcosh, gth -> gtanh,
+    gash -> gasinh, gach -> gacosh, gath -> gatanh, ggamd -> ggammah
+    (follow GP names)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.4 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+  - The "break loop" is now *on* by default, and no longer controlled by
+    trap(), but by the default 'breakloop'. To get out of a break loop, hit
+    <C-D> or type 'next', 'return', or 'break'
+  - trap() no longer allows installing default error handlers.
+  - GP's break loop is now *enabled* by default [ set breakloop = 0 in your
+    gprc to disable it ]
+  - random() now uses Brent's XORGEN (replaces congruential linear generator)
+    As a consequence, getrand() returns the FSR internal state array,
+    used by setrand() and the prototypes of both functions changed.
+    To adapt simple library code, replace
+      long seed = getrand(); ...; (void)setrand(seed);
+    by
+      GEN seed = getrand(); ...; setrand(seed);
+    The sequence of generated pseudo-random numbers are completely different.
+  - install(): parser code 'E' and 'I' now refer to closure, not strings:
+    'I': closure whose value is ignored, like in for() loop
+    'E': closure whose value is used, like in sum() loop
+  - changevar()  [ use substvec ] and reorder() [ use variable() for the list
+    of user variables ] have been removed
+  - The quasi-periods e.eta (and elleta(e)) are now twice the old value,
+    so that e.eta = 2 ellzeta(e,e.omega/2)
+  - Change e.omega so that e.omega[1] / e.omega[2] belongs to the Poincare
+    half plane [ used to be the inverse ].
+  - indexrank, indexsort and indexlexsort now return t_VECSMALLs
+  - besselk no longer admits a flag, the default implementation caters for
+    all cases
+  - removed (obsolete) optional flag in ellap(), Mod() and gcd()
+  - the weird extra argument to listcreate() [ maximal length ] has
+    disappeared. In fact, listcreate() is now obsolete: juste use List() to
+    create an empty list. It will grow as needed.
+  - listkill() is also obsolete: no need for a specific function
+    to kill old lists floating around. L = List() reclaims memory just as
+    efficiently as listkill(L) would. In most cases, you won't even need
+    that, e.g. local variables are automatically cleared when a user function
+    returns; no need to kill the ones which are lists specifically.
+  - local variables declared implicitly (user function parameters, loop
+    indices) are now lexically-scoped. The keyword 'my' allows to declare
+    lexically-scoped variables explicitly.
+  - global(), being awkward to implement in the new parser model and
+    essentially useless is now a no-op, scheduled for removal.
+  - PariEmacs is now distributed as a separate package, and is no longer
+    included in the PARI distribution
+  - distinction between user functions and user variables has been removed.
+    As a consequence, calling a user function without parentheses no more
+    evaluate it.
+  - syntax errors are no longer trap-ped, only runtime errors are. First,
+    error trapping and compile-time errors are best left unrelated. Second,
+    since the code is first byte-compiled, then evaluated, it made no sense
+    trapping a syntax error that would necessarily occur in the first
+    milliseconds after inputing the command to the interpreter. (Not so
+    in the old evaluator which could notice syntax errors very late, and one
+    was happy to be able to still salvage something when it bombed.)
+  - quadray no longer allows an (optional) 3rd argument: was not properly
+    documented, broke the symmetry between real / imaginary case (was ignored
+    if D > 0), complicated the code, and was rather useless in any case.
+  - the "minimum field width" component of the 'format' default is now
+    ignored (was used only for integers in 'prettyprint' output mode and
+    defaulted to 0 [no effect]). Use printf !
+  - removed the fieldw "minimum field width" field from the undocumented
+    pariout_t struct
+  - remove obsolete "prettyprint" support: printp / printp1 now act as
+    print/print1
+  - the conversion style 'g' of the 'format' default now printfs in style 'f'
+    if the decimal exponent is < -4 ( was: if the binary exponent is < -32 ),
+    in order to conform to standard printf specifications.
+  - all LLL variants now use an implementation of NGuyen & Stehle's algorithm
+    The LLL-reduced bases will be different from previous versions.
+  - plotinit: change the way default arguments are evaluated (omit dimension,
+    instead of interpreting 0 in a special way)
+  - nfbasistoalg / nfalgtobasis no longer accept t_VEC/t_COL/t_MAT
+    (not being able to apply nfbasistoalg to nfalgtobasis(nf,x) was awkward
+    and inconsistent; the doc stated that those were inverse functions,
+    but they were not). Use matbasistoalg / matalgtobasis, which now also
+    accept t_VEC/t_COL.
+  - removed obsolete optional argument to quadhilbert(D < 0)
+  - removed obsolete GP functions printp, printp1. Use print.
+  - bittest(x, n) no longer accepts t_VEC arguments n
+  - remove obsolete GP functions bnfclassunit, bnfreg, bnfclgp, bnfunit.
+    Use bnfinit.
+  - remove obsolete GP functions bnrclass. Use bnrinit or bnrclassno.
+  - remove awkward flag '3' for bnfinit. Use bnfcompress().
+  - remove badly named bnfmake. Use bnfinit(sbnf).
+  - idealmin now returns a number field element, instead of the associated
+    "principal idele"
+  - removed obsolete GP functions idealprincipal. Use number field elements
+    "as is".
+  - Functions quadunit(), quadregulator(), and factor() no longer apply
+    componentwise to vector / matrix arguments. Use apply()
+  - factorback() no longer accepts an optional 3rd argument (nf). Use
+    idealfactorback(). See also nffactorback().
+  - no longer export private library function incgam2. Remove it also under
+    GP when compatible = 3. Situation is the same as incgam1 (removed long
+    ago), 1.39.15 doc stated both were provided "for debugging only".
+  - Change of behaviour for Set(t_STR): Set("1") used to return ["1"], now
+    it returns ["\"1\""], i.e returns the set whose element evaluates to the
+    character string "1".
+  - Flags 4, 5, 6 (useless) have disappeared in nfinit()
+  - norml2(t_POL) now returns the expected (square of) the polynomial L^2 norm.
+    Use x * conj(x) to get back the old behaviour. norml2 now raises an error
+    on t_POLMOD and t_FFELT components; used to add relative norms as in
+      ? norml2([Mod(Mod(1,3)*x,x^2-2), 2])
+      %1 = Mod(2, 3)
+  - kill(z) has essentially the same effect as z = 'z [ it also kills addhelp
+    messages attached to z, and works for aliases and installed functions too ]
+  - removed optional flag to rnfconductor, use rnfisabelian.
+  - changed the "zetakinit" format: bnf no longer included
+  - factor(t_INT/t_FRAC, lim) used to trial divide by primes up to
+    min(lim, primelimit). Now we trial divide up to lim and raise an error
+    if lim > primelimit. [ Having the routine return an obviously "wrong"
+    result depending on an invisible parameter was not a good idea. ]
+  - factor(t_INT/t_FRAC, 1) was the same as factor(t_INT/t_FRAC, 0). Now
+    abide by the input value and leave (..., 0) as the single special case
+    (shortcut for "the largest precomputed prime")
+  - listsort(L) no longer returns the list L: it now returns nothing.
+    (No point in sorting in place if we must immediately copy the result.)
+  - ellwp now outputs a number of terms equal to the default seriesprecision,
+    instead of *twice* that number.
+  - inverse trigonometric functions (acos,asin,atan,acosh,asinh,atanh):
+    values on the branch cuts changed to conform to standards, e.g.
+    "implementations shall map a cut so the function is continuous as the cut
+    is approached coming around the finite endpoint of the cut in a counter
+    clockwise direction." (ISO C99)
+  - bnfinit no longer outputs a warning when fundamental units couldn't be
+    computed (annoying and rather useless) [#1166]
+  -[Configure] no longer support a link to an uninstalled readline library
+   in PARI toplevel
+  - thue() no longer outputs a Warning when the result is conditional on
+    the GRH.
+  - addprimes() now includes its argument as-is (used to take gcds,
+    making an insertion linear in the table size instead of O(1); filling
+    an inially empty table was quadratic in the final table size). They
+    must be true primes, otherwise number theoretic routines may return
+    wrong values. [#322]
+
+* Specific to the PARI library:
+===============================
+  - allocatemoremem() is gone. Use allocatemem().
+  - rename fprintferr() -> err_printf().
+  - rename TIMER -> timer_delay, TIMERread -> timer_get, TIMERstart ->
+    timer_start, msgTIMER -> timer_printf
+  - rename gener_Fp -> pgener_Fp, gener_Fl -> pgener_Fp [ contrary to
+    'gener', these assume that their argument is prime ]
+  - rename Flx_rand -> random_Flx, FpX_rand -> random_FpX
+  - rename cyclo -> polcyclo (GP name), subcyclo -> polsubcyclo (GP name),
+    tchebi -> polchebyshev (GP name), legendre -> pollegendre (GP name)
+  - only scalar types (and vectors/matrices of such) are allowed in gaffect
+    and gaffsg.
+  - pol_x and pol_1 are now functions, not global arrays. Use pol_x(v)
+    instead of pol_x[v]. polvar[] and ordvar[] have disappeared
+  - rename ZY_ZXY_resultant   -> ZX_ZXY_resultant,
+           ZY_ZXY_rnfequation -> ZX_ZXY_rnfequation and
+           FpY_FpXY_resultant -> FpX_FpXY_resultant
+  - FpX_ functions no longer accept p==NULL.
+  - Fl_pow renamed to Fl_powu [ exponent may not be negative ]
+  - rename pointch ->  ellchangepoint, coordch -> ellchangecurve,
+    ordell -> ellordinate
+  - remove obsolete undocumented functions outerr, outbeauterr, outsor,
+    outtex. Rename voir -> dbgGEN. Functions brute, outbrute, matbrute,
+    outmat, sor, outbeaut are obsoleted and no longer documented.
+  - rename errfile -> pari_errfile, infile -> pari_infile, logfile ->
+    pari_logfile
+  - manage_var obsoleted (kept for backward compatibility, to be removed),
+    use pari_var_init, pari_var_next, pari_var_max_avil, pari_var_create
+    instead.
+  - rename sqred -> qfgaussred, signat -> qfsign
+  - rename gscalmat -> scalarmat, gscalsmat -> scalarmat_s,
+    gscalcol -> scalarcol, gscalcol_i -> scalarcol_shallow
+  - removed obsolete apell2, ellap0. Rename apell -> ellap.
+  - typedef for type col_counter [last argument of unused, undocumented
+    plot_count()] changed (must be malloced since MAX_COLORS is no longer
+    a constant)
+  - rename gissquarerem -> gissquareall, uissquarerem -> uissquareall,
+    Z_issquarerem -> Z_issquareall (analogy with sqrtrem was faulty: we do
+    not store a remainder but the square root)
+  - rename assmat -> matcompanion, polymodrecip -> modreverse
+  - removed obsolete undocumented bruteall()
+  - %Z is no longer a valid conversion specification for PARI formats,
+    since this is now handled as a length modifier. Use %Ps instead.
+  - rename pariprintf -> pari_printf (%Z conversion disappeared, use %Ps)
+  - rename pariputc -> pari_putc, pariputs -> pari_puts, pariflush -> pari_flush
+  - rename gpmalloc -> pari_malloc, gprealloc -> pari_realloc,
+    gpfree -> pari_free
+  - rename derivpol -> RgX_deriv
+  - obsolete library functions roots2 and rootsold are no longer public
+  - rename factorpadic4 -> factorpadic, factorpadic2 now static
+  - the "prec" parameter has disappeared in all floating point LLL various
+  - remove obsolete function factpol()
+  - rename library functions nfhermite -> nfhnf, nfhermitemod -> nfhnfmod,
+    nfsmith -> nfsnf
+  - rename subres -> resultant, subresall -> resultant_all.
+  - remove obsolete discsr. Use RgX_disc (or quad_disc, qfb_disc...)
+  - remove obsolete allbase, base, base2, factoredbase, smallbase,
+    discf2, factoreddiscf, smalldiscf. Use nfbasis0 / nfdisc0 / nfmaxord
+  - rename library functions matrixqz2 -> QM_ImZ_hnf, matrixqz3 ->
+    QM_ImQ_hnf, matrixqz -> QM_minors_coprime
+  - rename library functions hil0->hilbert, hilii->hilbertii
+  - rename library functions srgcd -> RgX_gcd
+  - rename library functions ismonome -> RgX_is_monomial
+  - remove obsolete buchray, buchrayinit, buchrayinitgen: use Buchray.
+  - rename library functions minideal -> idealmin and change prototype
+  - rename library functions element_mulmodpr -> nfmulmodpr,
+    element_powmodpr -> nfpowmodpr, element_val -> nfval,
+    element_add -> nfadd, element_divmodpr -> nfdivmodpr,
+    element_mul -> nfmul, element_pow -> nfpow, element_div -> nfdiv.
+  - remove obsolete library function smallfact; use boundfact or Z_factor_limit
+  - rename mu -> moebius, gmu -> gmoebius, phi -> eulerphi, gphi ->
+    geulerphi, phiu -> eulerphiu, gener -> znprimroot, ggener -> znprimroot0,
+    racine -> sqrtint [ NO COMPATIBILITY #define PROVIDED: too dangerous ]
+  - rename regula -> quadregulator, fundunit -> quadunit
+  - remove obsolete library function gracine
+  - remove obsolete library function ispsp, gispsp. Use ispseudoprime
+    or BPSW_psp.
+  - remove obsolete library function gregula, gfundunit. Use quadregulator and
+    quadunit
+  - removed obsolete 'prec' argument from prototypes of idealmul0,
+    idealmulred, idealpow0, idealpowred, ideallllred, algdep, algdep0,
+    lindep, lindep0
+  - removed obsolete library functions ideallistunit, ideallistunitgen,
+    ideallistzstar, ideallistzstargen. Use ideallist0.
+  - removed obsolete library function algdep2. Use algdep0 (same arguments).
+  - removed obsolete library function Mod0. Use gmodulo.
+  - factorback() now accepts a single argument.
+  - rename library functions factorback0 -> factorback2,
+    subfields0 -> nfsubfields, ideallllred -> idealred0, zideallog -> ideallog,
+    isunit -> bnfisunit, ideal_two_elt* -> idealtwoelt*
+  - changed prototypes of bnrdisc, bnrconductor, bnrisconductor [ actually
+    renamed bnrdisc0, bnrconductor0, bnrisconductor0 ]. Just use the new
+    prototype, it's more convenient.
+  - removed obsolete library function kerin1. Use kerint or [simpler if
+    LLL reduction of the returned basis is not deisred] ZM_lll(,0.99,LLL_KER)
+  - rename library functions initell -> ellinit, smallinitell -> smallellinit
+    initalg -> nfinit, initalgred -> nfinitred -> initalgred2 -> nfinitred2
+    primedec -> idealprimedec, powraw -> qfbpowraw, compraw -> qfbcompraw
+  - rename library function u2toi -> uu32toi.
+  - remove obsolete library functions sor, outbrute, outbeaut
+  - basic kernel functions involving a t_INT and a t_REAL now return a
+    t_REAL, e.g. divsr(0, x) or mulir(gen_0,x) return real_0(...) [ used
+    to return gen_0 ] ==> much better control of object types when writing
+    kernel code. Generic functions (gmul, gdiv), as called from gp still
+    return a result as precise as possible given the input: 0 * 1. --> gen_0
+  - deprecated global constants gi; use mulcxI, gen_I() or mkcomplex().
+  - deprecated global constants geuler & gpi; use mpeuler() & mppi().
+  - changed prototype of galoisconj: galoisconj(x) -> galoisconj(x,NULL)
+  - commented out a large section of pariold.h. Define PARI_OLD_NAMES to
+    recover compatibility macros for old friends like 'un' and 'lstoi'
+  - rename greffe -> RgX_to_ser and remove the "use_pari_stack" flag
+  - rename Buchall -> Buchall_param and export a new Buchall with a
+    simplified interface.
+  - rename certifybuchall -> bnfcertify
+  - the bitvec family function was replaced by the new F2v family functions.
+  - prototype change: added a flag to the (mostly useless) function
+    rnfdedekind(), the version with the flag set is more useful.
+  - rename gcmp0 -> gequal0, gcmp1 -> gequal1, gcmp_1 -> gequalm1
+  - rename ZX_caract -> ZXQ_charpoly and swap arguments.
+  - rename RgXQ_u_pow -> RgXQ_powu
+  - remove function delete_named_var (using kill0 instead).
+  - remove RgM_ishnf (use ZM_ishnf instead).
+  - rename leftright_pow to gen_pow, leftright_pow_u to gen_powu.
+  - remove unused 'prec' argument in rnfinitalg(). Rename rnfinitalg -> rnfinit
+  - remove unused 'prec' argument in bnfisnorm()
+  - add a third precdl argument to gtoser()
+  - the last 2 arguments of ellwp0 (prec and precdl) have been swapped
+  - prototype of "summation" functions (suminf,intnum,prodinf,etc.) now is
+    fun(void *E, GEN call(void*, GEN),...)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+* General:  [ use GP function names in description ]
+==========
+  - Default args for user defined functions now evaluated when function is
+    called (used to be: at definition time)
+    E.g f(x) = local(z = x); z.
+    Before: f(2) -> x
+    Now:    f(2) -> 2
+
+  - rnfisnorm() input format has changed. Older version was complicated and
+    only worked if rnfequation(bnf, p, 1)[3] == 0  [otherwise, crash].
+    Use rnfisnorminit() now.
+
+  - rnfidealreltoabs, rnfidealup: now return a Z-basis as a vector of
+    elements in the relative extension, instead of a meaningless HNF matrix
+    wrt an unknown basis. Was especially dangerous if the extension also existed
+    in nfinit form, since the HNF matrices were _not_ ideals wrt this nf
+    structure. [ also consistent with rnfeltreltoabs ]. Use the following
+    construction instead, referring to an explicit NF structure:
+
+      \\ return y = rnfidealup(rnf,...) as an ideal in HNF form associated to
+      \\ nf = nfinit(rnf.pol);
+      idealgentoHNF(nf, y) = mathnf( Mat( nfalgtobasis(nf, y) ) );
+
+  - the "bit accuracy component" for computations of principal ideal generators
+    (bnfisprincipal), units (bnfunit), and some of the bnf structures has
+    been removed.
+
+  - the "technical parameter" to bnfinit() has been shortened to 3 components
+    (the others were deprecated / useless): [c, c2, nrpid]. The output format
+    has changed (technical components).
+
+  - poldegree(0) now returns -VERYBIGINT, not -1. Before using d = poldegree(x),
+    always check for 0 first: either (x == 0) or (d >= 0) as you prefer. Never
+    check for  d == -1 or -2^31-1 which is not portable.
+
+  - ellheight now uses the standard normalization: twice the value it used to
+    return. The values returned by ellbil() and ellheightmatrix are
+    unaffected. In particular, ellheightmatrix() is the polar form of
+    elleight(), and ellbil now satisfies the proper identity
+    B(P, Q) = (h(P+Q)-h(P)-h(Q)) / 2
+
+  - ':' no longer allowed as a substitute for ';' if compatible = 0. Use GP2C
+    semantics [ x:int, v:vec ]. For the time being the type information is
+    discarded.
+
+  - log(x, {flag}): optional flag removed. Decide alone whether to use AGM.
+
+  - valuation(x, 1) used to return 1, valuation(x, -1) returned 0 if (x >= 0)
+    and 1 otherwise. Not anymore: error message.
+
+  - bnrdisclist has lost its 4th argument 'flag', and omitting the
+    archimedean component now means that all 2^r1 possible values are
+    substituted (formerly: indicate no ramification at infinity).
+
+  - 'buffersize' default has disappeared: it is adjusted internally whenever
+    needed. The -b xxx flag to gp is a no-op.
+
+  - the member function bnr.zkst is deprecated, use bnr.bid.clgp
+
+  - the member function bnr.futu is deprecated. Please don't use bnr.tufu
+    either.
+  - use \frac instead of \over in TeX output. Define
+      \def\frac#1#2{{#1\over#2}}
+    if you insist on using plain TeX and run into problems.
+  - automatic concatenation for strings: use longest match for expression.
+    print("a"[1]) is not valid since "a" is not a vector print("a", [1])
+    prints 'a[1]'.
+  - isprime() now guarantees primality, use ispseudoprime() for fast
+    pseudo-primality tests.
+  - default() now always returns the value of the (possibly changed) default.
+    No need for a flag anymore.
+  - semantic of t_SER with inexact coefficients is now the same as for
+    t_POL: the sign is 0 iff all coefficients are zero. Either there are
+    no coefficients, or the leading coefficient is an inexact zero.
+  - No longer assume that part of an object is "permanent" when it is out
+    of the stack (was used by INTMOD/POLMOD/PADIC). Always copy it.
+    As a result, 'gmodulo' and 'forcecopy' become obsolete.
+    Rename gmodulcp -> gmodulo.
+
+* Specific to the PARI library:
+===============================
+Incompatible changes:
+---------------------
+  - gsize() -> gsizeword()  [conflict with gtk]
+    taille2 -> gsizebyte()
+  - hnfhavas removed (didn't work properly, hnflll provides an alternative)
+  - real zeroes are now coded on 2 words. Beware of constructs like
+    t = cgetg(lg(x), t_REAL); gaffect(y, t). If x = 0, so will be t.
+  - polgalois(): old format deprecated. New preferred format for result
+    has 3rd component giving numbering among all transitive subgroups of S_n
+    [ was ad hoc up to 7, as described above for n >= 8 ]. Old format is
+    still the default, but will eventually change. Use
+    default(new_galois_format, 1) or, in library mode, set global variable
+    new_galois_format to 1 to enable the new format.
+  - The nf structure output by nfinit has changed:
+    *) nf.zk is now T2-LLL-reduced, not in HNF wrt the power basis
+    *) the internal components of nf[5] have changed (MC and T2 not needed
+       anymore)
+  - polred & polredabs do not take a 'prec' argument anymore [was unused]
+  - gentimer / genmsgtimer / get_timer have been removed. They are superseded
+    by TIMER and msgTIMER which are fully reentrant and easier to use.
+  - (undocumented) macros buch[gen | genfu | init | iniftu]
+  - rnfisnorm() prototype has changed [ + need to call rnfisnorminit first ]
+  - incgam4 renamed to incgam0, incgam3 renamed to incgamc
+  - prototypes of buchall and smallbuchinit have changed (much simpler)
+  - co8() renamed to quadtoc()
+  - ker_mod_p() renamed to FpM_ker [ and supplemented with many analogous
+    modular routines ]
+  - nfreducemodpr2() removed, use an nfmodpr structure from nfmodprinit() or
+    zkmodprinit() instead
+  - smodsi() removed [ not well defined ]. Use either modsi() or umodui()
+  - Many error codes were removed. It is usually better to use only talker
+    outside libpari.
+  - Macro BITS_IN_RANDOM has been removed. Used to be 32.
+  - mymyrand() has been removed, use pari_rand31() or pari_rand() instead
+  - diviiz(x,y,z), divisz, divsiz and divssz always assign the euclidean
+    quotient [ used to  depend on the type of z: if t_REAL computed exact
+    quotient ]. Use rdivii, rdivis, rdivsi, rdivss for analogous
+    functionality (no "z" variant);
+  - mpdivz(x,y,z) assigns the euclidean quotient when x,y are t_INT (used
+    to depend on the type of z)
+  - removed inconsistently named macros mpinv[sir]r [ were "z" functions ]
+  - removed useless routine shifts [ use shifti( stoi() ) ]
+  - library interface of functions intnum, prodeuler, suminf, sumalt,
+       sumalt2, sumpos, sumpos2, prodinf, prodinf1  [ use GEN
+       (*eval)(GEN,void*) everywhere instead of entree * ]
+  - changed the prototype of bnrdisclist0
+  - removed ideallistarch0, ideallistarchgen, ideallistunitarch,
+    ideallistunitarchgen. Just use ideallistarch.
+  - change the output of ideallist with technical (flag 2,3) [ with units ]:
+    instead of two vectors, output a vector of 2-component vectors.
+    change the input of all list routines (bnrclassnolist, bnrdisclist)
+    accordingly.
+  - rename gtrans_i -> shallowtrans, concatsp -> shallowconcat.
+  - pari_err(warner | warnmem | warnfile | warnprec,) no longer accepted. Use
+    pari_warn.
+
+Partially compatible changes:
+-----------------------------
+[ not mandatory for your program to work with 2.3, since compatibility macros
+support the old names but are likely to become mandatory in 2.4, so we
+advise you to update soon ]
+
+  - typecasting macros (e.g. ladd(), lmul() ...) are obsolete and
+    shouldn't be used in new programs. Use accessors gel() and friends.
+  - stack locations used to be of type long or ulong, now they have a
+    dedicated type pari_sp [ pari stack pointer ]
+  - in order to compare variable numbers, use the macro varncmp() instead of
+    < or > operators.
+  - access to the prime numbers table is done via the macros
+    NEXT_PRIME_VIADIFF or NEXT_PRIME_VIADIFF_CHECK.
+  - direct access to the mantissa of t_INT is deprecated. Instead you should
+    use the t_INT API (macros int_MSW, int_LSW, int_precW, int_nextW, int_W).
+    This will ensure your code is compatible with both native and GMP kernels.
+  - the macros lgef / setlgef / evallgef have been removed. t_POLs no longer
+    include an "effective length". One should use lg() for t_POLs as for
+    most other types. Don't use lgef() in new code.
+
+    lgef() and setlgef() are aliased to lg() and setlg(). They will break on
+    hackish code accessing directly the first component of t_POL objets, e.g
+      x = cgetg(20, t_POL);
+      x[1] = evalsigne(1) | evalvarn(0) | evallgef( 10 )  /* 10, not 20 */
+      ...
+      setlgef(x, 20); /* now create/use further coefficients */
+  - types t_FRACN, t_RFRACN and gred() have been removed (complicated, unused,
+    and very inefficient). There are now aliases to t_FRAC, t_RFRAC and gcopy
+    respectively for backward compatibility.
+  - renamed nfdivres -> nfdivrem, poldivres -> poldivrem
+  - renamed permute -> numtoperm, permuteInv -> permtonum
+  - renamed gzero -> gen_0, gun -> gen_1, gdeux -> gen_2, polx -> pol_x,
+    polun -> pol_1
+  - renamed gegal -> gequal, gegalgs -> gequalgs, gegalsg -> gequalsg,
+    egalii -> equalii
+  - renamed binome -> binomial, chinois -> chinese
+  - renamed apprgen9, apprgen -> padicappr, factmod9 -> factorff
+  - rename resss -> remss, ressi -> remsi, resis -> remis, resii -> remii,
+    gres -> grem
+  - rename divise -> dvdii, gdivise -> gdvd, mpdivis -> dvdiiz, mpdivisis
+    -> dvdisz
+  - rename mpent -> mpfloor
+  - rename isprincipalrayall -> bnrisprincipal, rayclassno -> bnrclassno
+    rayclassnolist -> bnrclassnolist
+  - rename globalreduction -> ellglobalred, localreduction -> elllocalred
+  - taniyama(e) is deprecated. Use elltaniyama(e, prec) instead of
+      old = precdl; precdl = prec; x = taniyama(e); precdl = old;
+  - rename lisexpr -> readexp, lisseq -> readseq
+  - rename flisexpr, flisseq -> gp_read_str
+  - rename lisGEN  -> gp_read_stream
+  - rename idmat -> matid
+  - rename coefs_to_col -> mkcoln, coefs_to_int -> mkintn
+           coefs_to_pol -> mkpoln, coefs_to_vec -> mkvecn
+  - rename wf -> weberf, wf1 -> weberf1, wf2 -> weberf2
+  - rename err -> pari_err, pariputsf -> pariprintf
+  - rename rnfhermitebasis -> rnfhnfbasis
+  - rename gcarreparfait -> gissquare, gcarrecomplet -> gissquarerem
+  - rename lseriesell -> elllseries
+  - gmodulo and forcecopy are obsolete. Use gmodulcp and gcopy.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+* The calculator GP:
+====================
+ - most function names have changed (see misc/new.dic or use whatnow under GP)
+ - lowercase / uppercase letters distinguished.
+ - the syntax \var = value is not recognized anymore. Use standard
+   metacommands instead.
+(setting the compatibility level "compatible" to 3, with "default" will give
+you those three back).
+
+ - strict parenthesis matching before executing the command
+(set strictmatch to 0 to get old behaviour)
+
+ - functions label / goto have been removed (use break/next/return instead).
+ - conjugation x_ has been removed. '_' can be freely used in identifiers.
+ - \k metacommand removed
+
+ - result history is now cyclic (older results are erased when the maximal
+   size "histsize" is reached).
+ - the Set() function turns objects into strings (so that set functions are
+   able to treat arbitrary objects). Use eval to turn them back to GENs.
+ - the type() function returns a string and not a number.
+ - sum(X=a,b,...,x) yields x if b<a. Analogous statement for prod.
+
+* The library PARI:
+===================
+ - use symbolic type names (t_INT, etc.). Don't use any explicit type number
+   in your programs since these are liable to change (they have not, but will).
+ - the codewords of GEN objects have a different internal structure. The
+   components should only be accessed through the documented macros. When
+   creating codeword x[1] for the GEN x, use the eval* macros (the set* ones
+   don't fill the codeword completely).
+ - the following constant names have changed ("pari" prepended): C1, C2, C3, K,
+   K1, K2, K4. The constant C31 has disappeared.
+ - prec and defaultpadicprecision are no longer global. The latter has
+   disapeared.
+ - char *pariversion not set anymore (use the macros PARIVERSION/PARIINFO)
+ - LONG_IS_32BIT is never defined (test whether LONG_IS_64BIT is defined
+   or not).
+ - gen2str() renamed to GENtostr().
+ - gitoascii() suppressed, use GENtostr().
+ - init() renamed to pari_init().
+ - the macro mant() has been suppressed.
+ - imprimer() suppressed. output() is now a function and can be used under
+   all debuggers.
+ - the prototypes and behaviour of gredsp() and normalize() have changed.
+   They are not documented anymore.
+ - the install() function has been modified (use codes instead of valence).
+ - gpuigs only has two arguments.
+ - integ now takes two arguments, like deriv.
+ - setrand, getrand, getstack, gettime, glength return a C long, and not a GEN.
+ - pari_randseed no longer global, use setrand/getrand
+ - gerepile(ltop,lbot,0) no longer returns ltop - lbot
+ - nf[5][7] stored in two element form (was Z-basis) for faster inversion
+ - t_POL and t_POLMOD can be freely mixed as long as main variables are the
+   same (result is the expected POLMOD)
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Configure b/Configure
new file mode 100755
index 0000000..ecedc55
--- /dev/null
+++ b/Configure
@@ -0,0 +1,213 @@
+#! /bin/sh
+#
+# This file is part of the PARI/GP package.
+#
+# PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY WHATSOEVER.
+#
+# Check the License for details. You should have received a copy of it, along
+# with the package; see the file 'COPYING'. If not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Configuration file for GP/PARI.  Run Configure --help for Usage.
+#
+# Perl's Configure and GNU autoconfig were of much help in writing these files.
+# $Id$
+
+TOP=`pwd`
+MAKE=${MAKE:-make}
+config_dir=config
+data_dir=data
+doc_dir=doc
+examples_dir=examples
+misc_dir=misc
+src_dir=src
+desc_dir=src/desc
+
+case "x$RUNTEST" in x);; x/*);; *) RUNTEST="$TOP/$RUNTEST";; esac
+
+cd $config_dir
+# Process options, initialize
+. ./get_head # do we need head -n # or head -# ?
+. ./version
+echo "Configuring pari-$pari_release_verbose $patchlevel_verbose"
+. ./get_config_options
+####################### CONFIGURE - SHELL ###################################
+. ./get_nl # how to get echo without \n ? (for config questions)
+. ./get_PATH
+#  We might need the following :
+echo Looking for some tools first ...
+_tools_list='gzip cc gcc ld perl zcat'
+pathspace=`echo $PATH | sed -e "s/$dir_sep/ /g" | sed -e 's,\\\\,/,g'`
+
+for file in $_tools_list; do
+  x=`./locate $file '' $pathspace`
+  eval $file=$x
+  case $x in
+# support also DOS filesystems (hard drive prepended)
+   ?:/*|/*) echo ..."$file is $x";;
+      *) echo ..."I could not find $file." >&2;;
+  esac
+done
+if test -z "$zcat" -a -n "$gzip"; then zcat="$gzip -dc"; fi
+
+####################### CONFIGURE - ARCHITECTURE ############################
+. ./get_archos # arch, osname
+config_log="$TOP/config-$arch-$osname$$.log"
+cat > $config_log<< EOT
+This file contains messages produced while configuring
+  pari-$pari_release $patchlevel_verbose
+to aid debugging if Configure makes a mistake. Command line was
+  > $0 $@
+
+EOT
+exec 5>> $config_log
+
+####################### CONFIGURE - COMPILATION #############################
+# $_cc_list (includes 'optimization'), extraflag
+. ./get_cc
+#
+. ./get_mt
+# doubleformat, sizeof_long
+. ./get_double_format
+# asmarch, pretty
+. ./get_kernel
+# _dl_list, DLCFLAGS, update CFLAGS
+. ./get_dlcflags
+# $_ld_list
+. ./get_ld
+# $_dlld_list
+. ./get_dlld
+# $_perl_list
+. ./get_perl
+####################### CONFIGURE - LIBC ####################################
+. ./get_libc # $_has_list, RT_LIBS, DL_LIBS
+####################### CONFIGURE - LIBRARIES ###############################
+# Looking for libraries: gmp, X11, fltk, Qt, readline
+echo Checking for optional libraries and headers...
+. ./get_include_path
+. ./get_libpth
+# $_gmp_list
+if test "$kernlvl1" = "gmp"; then
+  . ./get_gmp
+fi
+# $_graphic_list
+. ./get_graphic_lib
+# $_readline_list (includes 'readline')
+case "$without_readline" in
+  yes);;
+  *) . ./get_readline
+esac
+#############################################################################
+case $kernlvl1 in
+gmp) libpari_base=pari-gmp;;
+none) libpari_base=pari;;
+esac
+case $enable_tls in
+yes) libpari_base="${libpari_base}-tls"
+esac
+
+if test `expr $VersionMinor % 2` = 0; then
+  libpari_base=$libpari_base-$version
+fi
+
+####################### CONFIGURE - MAKE ####################################
+. ./get_install # $_install_list
+. ./get_objdir  # objdir, cdobjdir
+. ./get_static  # static
+# For dynamic linking, before and after installing
+runpath=\"$libdir\"
+LDDYN="-lpari"
+# get_modld needs $includedir from get_install, static, and LDDYN
+. ./get_modld   # $_modld_list
+
+# Which copy, SHELL ?
+case "$osname" in
+  os2) ln_s=cp;      make_sh=sh;;
+  *)   ln_s="ln -s"; make_sh="/bin/sh";;
+esac
+####################### CONFIGURE - CLEANUP #################################
+rm -f gmon.out # created by Configure -pg
+rm -f *.gcno *.gcda # created by Configure -gcov
+####################### CONFIGURE - SPIT ####################################
+. ./get_tests #_test_list
+# Now spit out the results
+cat << EOT
+==========================================================================
+EOT
+cd "$TOP"
+if test ! -d $objdir; then mkdir -p $objdir; fi
+rm -f $objdir/config.log; mv $config_log $objdir/config.log
+dflt_conf_file=$objdir/$dflt_conf_file
+
+cat > $dflt_conf_file << EOT
+# Config file for Pari $release -- $pretty
+
+EOT
+case "$osname" in
+  os2|mingw) shell_q='"'; echo "shell_q='\"'"  >> $dflt_conf_file;;
+    *) shell_q="'"; echo "shell_q=\"'\"" >> $dflt_conf_file;;
+esac
+
+for variable in\
+  pari_release pari_release_verbose version libpari_base static TOP objdir\
+  arch asmarch osname pretty\
+  kernlvl0 kernlvl1 RT_LIBS DL_LIBS MT_LIBS LIBS\
+  dir_sep runpath runpathprefix LDDYN RUNTEST\
+  ln_s make_sh\
+  sizeof_long doubleformat\
+  thread_engine enable_tls\
+  $_tools_list\
+  $_test_list\
+  $_install_list\
+  $_perl_list\
+  $_cc_list\
+  $_ld_list\
+  $_dl_list\
+  $_dlld_list\
+  $_graphic_list\
+  $_modld_list\
+  $_readline_list\
+  $_gmp_list\
+  $_has_list; do
+  eval "echo $variable=\'"'$'"$variable\'" \>\> $dflt_conf_file
+done
+
+. $config_dir/extract_files
+
+#  Building...
+cat << EOT
+==========================================================================
+EOT
+
+if test -n "$tune"; then
+  echo "Building and tuning PARI (this may take a while)"
+  echo
+  (cd $objdir; rm -f parilvl1.h pariinl.h;\
+  $MAKE tune; tune -t > tune.h.new && mv tune.h.new tune.h && cat tune.h;\
+  rm -f parilvl1.h pariinl.h; $MAKE gp)
+else
+  echo $n "Shall we try to build pari $version.$patch ($status) now (y/n)? $c"
+  dflt=n; rep='y n'; . $config_dir/myread
+fi
+
+mkobjdir=`$config_dir/objdir`
+cdobjdir=
+if test "$objdir" != "$mkobjdir"; then
+  cdobjdir="cd $objdir; "
+fi
+
+case $ans in
+y) if (cd $objdir; $MAKE gp); then
+     echo $n "Shall we install the files where they belong (y/n)? $c"
+     dflt=n; rep='y n'; . $config_dir/myread
+     case $ans in
+       y) $MAKE install;;
+       n) echo "Ok. Type \"${cdobjdir}make install\" when you are ready";;
+     esac
+   fi;;
+n) echo "Ok. Type \"${cdobjdir}make install\" when you are ready";;
+esac
+echo 'Bye !'
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..b100175
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,20 @@
+Assuming your system is a fairly standard Unix, you can quickly build/test
+GP in the following way:
+
+a) ./Configure  ( --prefix=/exotic/dir/name if desired. Default is /usr/local. )
+b) make all, make bench
+c) make install, if desired
+d) copy misc/gprc.dft to /etc/gprc [sitewide] or $HOME/.gprc [personal]
+
+Documentation can be found in directory doc.  Compile and read doc/INSTALL.tex
+(e.g cd doc; tex INSTALL; xdvi INSTALL) for detailed installation instructions.
+
+P.S: Useful optional packages can be downloaded separately
+
+  http://pari.math.u-bordeaux1.fr/packages.html
+
+in particular
+
+  elldata: Cremona's Elliptic Curve Data, needed by ellsearch and ellidentify; 
+  galdata: needed by polgalois to compute Galois group in degrees 8 through 11; 
+  seadata: allow ellcard(E) for large finite fields.
diff --git a/MACHINES b/MACHINES
new file mode 100644
index 0000000..c598f7f
--- /dev/null
+++ b/MACHINES
@@ -0,0 +1,431 @@
+NOTE : Due to the widespread availability of free and reliable operating
+systems (e.g. GNU/Linux) and C compilers (e.g. gcc), and the gradual
+extinction of "exotic" systems, this file is no longer actively maintained.
+See
+
+  http://pari.math.u-bordeaux1.fr/buildlog.html
+
+for an overview of the development platforms we have access to and actively
+maintain.
+==============================================================================
+This file gives information about architectures/operating systems GP has been
+compiled on. If your configuration is not listed, it means that nobody sent us
+information about it. If indeed GP builds successfuly, we'd be grateful if you
+could notify us at the address
+
+  pari at math.u-bordeaux.fr
+
+Please include a line analogous to the ones below, so that we can
+accordingly expand the list.
+==============================================================================
+General notes: binaries should be compiled with native kernel (--without-gmp)
+- Arch
+ a star (*) in the first column means GP was built using portable kernel.
+
+- Bench, as output by `make bench'
+ 1) can vary with load, available memory, compiler version ...
+ 2) is not the actual time spent: bench "nfields" is weighted by 1/5.
+ 3) An ! present next to the timing means that install() did not work with
+ that configuration ([BUG] in 'program' bench)
+
+NOTE: bench timings in version 2.0.11 and 2.0.12 were not correct (given
+between parentheses). Add about 25% to running times for comparison with
+other versions.
+
+Clock frequency in MHz when known, in BM (BogoMIPS) otherwise.
+                                                       Bench
+Arch / proc:         OS:          Compiler:         sta:    dyn:  GP version:
+==============================================================================
+DEC/Alpha
+alpha21264c 1000MHz  OSF1-V5.1        cc             990    1165  2.2.3     IS
+alpha21264b  833MHz  OSF1-V5.1        cc            1867    2099  2.1.1     JC
+alpha21264   667Mhz  FreeBSD-4.0     gcc            2626!   2656  2.0.20    IS
+alpha21264   500MHz  OSF1-V4.0       gcc-2.8.1      3178    3436  2.1.0     JC
+alpha21264   500MHz  OSF1-V4.0       gcc-2.95.3     1885    1980  2.2.1     GH
+alpha21164a  600MHz  Linux-2.4.9     gcc.2.96       3746    3771  2.2.6
+alpha21164a  600MHz  Linux-2.2.13   egcs            4270    4350  2.0.19(26)IS
+alpha21164   600MHz  OSF1-V4.0D      cc             3996    4384  2.0.10    LG
+alpha21164   533Mhz  Linux 2.0.34    gcc           (4294)  (4385) 2.0.12    JC
+alpha21164   500Mhz  Linux-2.2.13   egcs-2.91.66    5167    5217  2.1.1
+alpha21164   500Mhz  Linux-2.2.13    gcc-2.95.3     4319    4253  2.2.2 (27)
+alpha21064   172Mhz  OSF1-3.0        cc            34104   35564  2.1.1
+------------------------------------------------------------------------------
+Sun/SPARC
+Sun-Fire V440 1593MHz*4 Solaris-10   gcc-4.0.3       655     658  2.3.0     SHo
+UltraSPARCIII+ 900MHz Solaris-9      gcc-3.4.1      1434    1442  2.2.8     IS
+UltraSPARCIII 750MHz Solaris-8       gcc-3.4.1      1685    1708  2.2.8     IS
+UltraSPARC-IIe 502MHz Solaris-8      gcc            3608    3646  2.1.4     HR
+UltraSPARCII 450MHz  Solaris-7       gcc-3.4.1      3007    3007  2.2.8     IS
+UltraSPARC60 450MHz  Solaris-7       gcc            3970    4010  2.0.19    IS
+UltraSPARC60 450MHz  Solaris-7        cc            5070    5210  2.0.19    IS
+UltraSPARC2i 440MHz  Solaris-8       gcc-2.95.2     2802    3028  2.3.0
+Ultra250     400MHz  Solaris-7       gcc            4510    4560  2.0.19    IS
+Ultra250     400MHz  Solaris-7        cc            5950    6090  2.0.19    IS
+UltraSPARC60 360MHz  Solaris-2.6     gcc            5034    5209  2.0.16    IS
+UltraSPARC60 360MHz  Solaris-2.6      cc            6329    6453  2.0.16    IS
+UltraSPARC2i 360MHz  Solaris-7      egcs            5816    6286  2.0.16(17)HS
+UltraSPARC2i 333MHz  Solaris-7       gcc            5666    6818  2.0.20(18)
+UltraSparcII 300MHz  Linux-2.4.18    gcc-3.2.3      6102    6184  2.1.5     BA
+UltraSPARC10 300MHz  Solaris-2.6     gcc           (5932)  (6102) 2.0.12    GN
+UltraSPARC   300MHz  Solaris-2.5     gcc            7643    7566  2.0.9     IS
+UltraSPARC2  296MHz  Solaris-2.5.1   gcc            6098    6277  2.0.16    IS
+UltraSPARC2  296MHz  Solaris-2.5.1    cc            7676    7869  2.0.16    IS
+UltraSPARC2i 270MHz  Solaris-7       gcc            9008    7860  2.0.16    IZ
+UltraSPARC2i 270MHz  Solaris-2.6      cc            9200    9970  2.0.10    LG
+UltraSPARC2  248MHz  Solaris-7        cc-5.0        9420    9692  2.0.18(13)GN
+UltraSPARC2  248MHz  Solaris-7      egcs            7218    7262  2.0.16    HS
+UltraSPARC   200MHz  Solaris-2.5.1   gcc            9226    9268  2.0.16    IS
+UltraSPARC   200MHz  Solaris-2.5.1    cc           11226   11755  2.0.16    IS
+UltraSPARC   167MHz  Solaris-2.6     gcc           10767   11218  2.0.16    IS
+UltraSPARC   167MHz  Solaris-2.6      cc           13677   13967  2.0.16    IS
+UltraSPARC   167MHz  Solaris-7       gcc-2.8.1      9106    9144  2.2.4
+UltraSPARC   167MHz  Solaris-2.5     g++           13474   13418  2.0.9 (7) LG
+UltraSPARC   167MHz  Solaris-2.5     CC            15778!  16400! 2.0.9     LG
+UltraSPARC   167MHz  Solaris-2.5     cc            16241   17048  2.0.9     LG
+UltraSPARC   143MHz  Solaris-2.5     gcc-2.95.3    11905   11723  2.2.1
+
+SuperSPARC2   75MHz  Solaris-2.5     gcc           16969   17013  2.0.10    LG
+SuperSPARC    50Mhz  Linux-2.2.14    gcc           41126   40854  2.0.18(20)BA
+SuperSPARC    40MHz  SunOS-4.1.3     gcc           31178   30456  2.0.19
+SuperSPARC    40Mhz  Solaris-2.5     gcc           31528   31576  2.0.16
+TurboSPARC   170MHz  Solaris-2.5     gcc           15907   16529  2.0.9     LG
+MicroSPARC2  110MHz  NextStep-v3     cc            37794!      -  2.0.9     LG
+MicroSPARC2  110MHz  SunOS-4.1.4     gcc           31476   32123  2.0.9     LG
+MicroSPARC2  110MHz  Solaris-2.5     gcc-2.7.2     24197   24107  2.2.2
+SPARCv7(SS2)  40MHz  SunOS-4.1.3     gcc          115212! 115364  2.0.9     LG
+SPARCv7(ELC)  33MHz  SunOS-4.1.1     gcc          139406!      -  2.0.8     GN
+SPARCv7(SS400)  ???  SunOS-4.1.4     gcc          107074  106494  2.0.16(19)DE
+SPARCv7(HWS210) ???  Linux-2.2.5     gcc           98488!      -  2.0.16(14)DE
+------------------------------------------------------------------------------
+Intel/x86_64 in 64bit mode
+Core2Duo    3400MHz  Linux-2.6.20-16 gcc-4.1.2       134     136  2.3.2     RN
+AMD Opteron 2400MHz  Linux-2.6.12-1  gcc-4.0.1       198     204  2.2.11    PZ
+AMD Opteron 2200MHz  Linux-2.6.11-6  gcc-3.4.3       206     221  2.3.0
+AMD Opteron 2000MHz  Linux-2.4.22    gcc-3.3.2       290     290  2.2.8
+AMD Athlon 64 3000+  Linux-2.6.15    gcc-4.1.0       300     287  2.3.0     JB
+AMD Opteron 1333MHz  Linux-2.6.5     gcc-3.3         413     419  2.2.8     BA
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Intel/x86
+AMD Opteron 1333MHz  Linux-2.6.5     gcc-3.3         536     539  2.2.8     BA
+AMD Athlon  XP2400+  Linux-2.4.21    gcc-3.2         428     398  2.2.5 (41)MH
+AMD Athlon  XP1800+  Linux-2.4.17    gcc-3.0.4       616     616  2.2.3 (30)PE
+AMD Athlon  MP1800+  Linux-2.4.9     gcc-2.96        696     688  2.2.3 (29)HC
+AMD Athlon  1250MHz  Linux-2.4.2     gcc-2.96        597     591  2.2.10a   HV
+AMD Athlon  1200MHz  Linux-2.4.7     gcc-2.96       1002     992  2.1.2     CL
+AMD Athlon  1100MHz  Linux-2.4.2-2   gcc            1078    1076  2.1.1     DP
+AMD Duron   1200MHz  Linux-2.4.19    gcc-3.2         789     800  2.2.4     QL
+AMD Duron   1200MHz  Linux-2.4.19    gcc-2.95.3      834     851  2.2.4     QL
+AMD Duron   1000MHz  Linux-2.4.0    egcs            1364    1428  2.0.20    QL
+AMD Athlon   850MHz  Linux-2.2.17    gcc            1364    1342  2.1.0     JC
+AMD Duron    700MHz  Linux-2.4.0    egcs            2066    2138  2.0.20    QL
+AMD Athlon   500MHz  Linux-2.2.13    gcc            2736    2724  2.0.18    MS
+AMD K6-2     350Mhz  Linux-2.0.34   egcs           (4856)  (4756) 2.0.11    SH
+AMD K6-2     350Mhz  Linux-2.0.36    gcc            5154    4850  2.0.17
+AMD K6       266MHz  Linux-2.2.5    pgcc            5861    6001  2.0.16(16)IS
+AMD K6       233Mhz  Linux-2.2.14    gcc            6968    7158  2.0.18    QL
+IBM/Cyrix    200MHz  Linux-2.0.35    gcc           13190   13729  2.0.15    AP
+
+CoreDuo     2160MHz  MacOS X 10.4    gcc-4.0.1       376       -  2.3.0     LG
+PentiumIV   3000MHz  Linux-2.6.22-1  gcc-4.1.2       376     370  2.3.3     RM
+PentiumIV   3000MHz  Linux-2.6.11-1  gcc-3.4.3       291     296  2.2.11    PZ
+PentiumM    1600MHz  Linux-2.6.8     gcc-3.3.4       461     460  2.2.8     JD
+PentiumIV   2800MHz  Linux-2.6.11    gcc-3.4.1       402     398  2.3.0
+PentiumIV   2000MHz  Linux-2.4.20    gcc-3.3         794     962  2.2.6 (42)ID
+PentiumIV   2000MHz  Linux-2.4.18    gcc-3.2         849     835  2.1.5 (25)FU
+PentiumIV   1800MHz  WinXP + Cygwin  gcc-3.2        1384!      -  2.2.5 (38)DC
+PentiumIV   1695MHz  FreeBSD 4.6.2   gcc-2.95.3      904     918  2.2.5     MW
+PentiumIV   1600MHz  Linux-2.4.18-3  gcc-2.96        930     926  2.2.5
+PentiumIV   1400MHz  Linux-2.4.4-4   gcc-2.95.3     1318    1354  2.1.3     HG
+PentiumIII  1400MHz  FreeBSD-4.6.2   gcc-2.95.3      752     757  2.2.4(35) IS
+PentiumIII  1133MHz  Linux-2.4.18    gcc-2.96       1028    1055  2.2.4(36) IS
+PentiumIII  1000MHz  Linux-2.4.7-10  gcc-2.96       1128    1180  2.2.4
+PentiumIII   933MHz  Linux-2.2.15   egcs            1450    1485  2.0.20    IS
+PentiumIII   800MHz  Linux-2.2.14   egcs            1900    1882  2.0.20    CL
+PentiumIII   800MHz  Linux-2.2.19    gcc-2.96       1638    1624  2.1.2     OT
+PentiumIII   733MHz  Linux-2.2.14   egcs            2080    2090  2.0.19    IS
+PentiumIII   550MHz  Linux-2.5.56    gcc.3.2.1      1918    1984  2.2.5     MH
+PentiumIII   533MHz  Linux-2.2.14    gcc            2710    2730  2.0.19    IS
+PentiumIII   500MHz  Linux-2.2.12   egcs            3154    3176  2.0.18    HC
+PentiumII    450MHz  Linux-2.4.18    gcc-3.0.4      2348    2426  2.2.3 (31)PE
+PentiumII    450MHz  Linux-2.0.35    gcc            3220    3230  2.1.1     TT
+PentiumII    400MHz  Linux-2.3.99    gcc            3392    3410  2.0.20(25)GH
+PentiumII    400MHz  Linux-2.2.14    gcc            3886    3902  2.0.20    GH
+PentiumII    366MHz  Linux-2.2.16    gcc-2.95.2     4018    4118  2.1.3     HG
+PentiumII    350MHz  Linux-2.2.16    gcc-2.95.2     3760    3784  2.2.1
+PentiumII    350MHz  Win98         mingw-2.95.2     4412!      -  2.1.0 (22)
+PentiumII    350MHz  Win98 + Cygwin1 gcc-2.95.2-5   4026!      -  2.2.1
+PentiumII    350MHz  Win98 + RSX     gcc            9436!      -  2.0.20(24)
+PentiumII    350Mhz  Linux 2.0.36   pgcc-2.92.21   (3310)  (3340) 2.0.12(11)PE
+PentiumII    350Mhz  Linux 2.0.36   egcs-2.92.21   (3352)  (3402) 2.0.12(11)PE
+PentiumII    350Mhz  Linux 2.0.36    gcc           (3600)  (3610) 2.0.12    PE
+PentiumII    333Mhz  Linux-2.2.19    gcc-2.7        4446    4464  2.1.1     BA
+PentiumII    333Mhz  Linux-2.2.19    gcc-3.0.2      4104    4218  2.1.1     BA
+PentiumII    300MHz  NextStep-3.3    cc             5325!      -  2.0.10
+PentiumII    233MHz  FreeBSD-2.2.5   gcc           (6251)  (6114) 2.0.11
+PentiumII    233MHz  Linux-2.0.35    gcc           (7460)  (7586) 2.0.12    HG
+PentiumPro   200MHz  Linux-2.2.12   egcs 2.91.66    6036    6044  2.2.6
+Pentium      266Mhz  Linux-2.0.29    gcc           (7256)  (7044) 2.0.11
+Pentium      200MHz  Linux-2.2.10    gcc           12062   12340  2.0.18    MS
+Pentium      200Mhz  NextStep-3.3    cc            16035!      -  2.0.06    MS
+Pentium      166MHz  FreeBSD-2.2.5   gcc          (15159) (14481) 2.0.11
+Pentium      166MHz  Solaris-2.6     gcc          (13550) (13746) 2.0.12    GN
+Pentium      133MHz  Linux-1.2.8     gcc-2.7.2p   (15448!)     -  2.0.12    GN
+Pentium      133MHz  FreeBSD-2.2.5   gcc           15231   17259  2.0.10    KK
+486DX (Cyrix) 66MHz  Linux 2.2.18    gcc           59078   60600  2.1.1     CC
+486DX2        50MHz  Linux-2.0.33    gcc          213710  216224  2.0.9     LG
+486DX         50MHz  DOS-5 + EMX     gcc           77126!      -  2.0.20
+*i860         40MHz  Concentrix      cc           147989!      -  2.0.9     LG
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Intel/ARM
+*StrongARM   110MHz  Linux-2.4.5     gcc-2.95.2    17162!      -  2.1.1     BA
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Intel/ia64
+*Itanium II 1000MHz  Linux-2.4.19    gcc-3.0.4      1318    1478  2.1.5     BA
+*Itanium     800MHz  Linux-2.4.9     gcc-3.0.1      2557    2474  2.1.2 (28)BA
+ Itanium     733MHz  Linux-2.4.21    gcc-4.1.0      1405    1494  2.2.13    BA
+------------------------------------------------------------------------------
+Motorola
+ MC68060      50MHz  Linux-2.4.31    gcc-4.0.3     30010   30584  2.2.13(40)BA
+*MC68060      50MHz  Linux-2.4.26    gcc-3.3.3     49586   50296  2.2.8 (40)BA
+*MC68060      50MHz  Linux-2.2.20    gcc-2.95.4            74998  2.1.3 (37)BA
+*MC68040      25MHz  NextStep-v2     cc           250542!      -  2.0.9     LG
+ MC68030      25MHz  SunOS-4.0.3     gcc          683736! 685248  2.0.9 (4)
+*MC68030      25MHz  SunOS-4.0.3     gcc         1034136!1038368  2.0.9
+------------------------------------------------------------------------------
+IBM/Cell
+Sony PS3    3192MHZ  Linux 2.6.18    gcc-4.3.2       620     643  2.4.3     BA
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+IBM/POWER,PowerPC
+PPC G5      2000MHz  MacOS X 10.4    gcc-4.0.1       461       -  2.3.0     LG
+PPC G4      1667MHz  MacOS X 10.4    gcc-3.3         543       -  2.3.0     LG
+PPC G4      1000MHz  MacOS X 10.3    gcc-3.3         940       -  2.3.2     BM
+*PPC G4      400MHz  MacOS X 10.2    gcc-3.1        5174!      -  2.1.4     MB
+*PowerMac3,3 500MHz  Linux-2.4.2     gcc            3742    3732  2.1.1     BA
+*PPC800(G3)  400MHz  Linux-2.4.3     gcc            4640    4624  2.2.0     GH
+*PPC750(G3)  400MHZ  Linux-2.4.24    gcc-2.95.4     3840    3872  2.1.5     SC
+*PPC???(G3)  350MHz  MacOS X 10.1    gcc-2.95.2     5722!      -  2.1.2     NI
+*PPC750(G3)  266MHz  Linux-2.2.6     gcc            6944    6924  2.0.18    DE
+*PPC604e     233MHz  AIX-4.2         cc            (8616!)     -  2.0.12(3) GN
+*PPC604      133MHz  AIX-4.2         cc            21528!      -  2.0.4 (3) GN
+*PPC601      100MHz  AIX-4.1         cc            21692!      -  2.0.4 (3) GN
+*PPC601       75MHz  Linux-2.1.24    gcc           51810   52702  2.0.9     EK
+*PPC601       66MHz  AIX-3.2.5       cc            66280!      -  2.0.4 (3) GN
+*POWER2       66MHz  AIX-4.2         cc            37592!      -  2.0.4 (3) GN
+------------------------------------------------------------------------------
+IBM/S390
+*S390         773BM  Linux-2.4.19    gcc-3.2.3      5272    5646  2.1.5 (39)BA
+*S390         630BM  Linux-2.4.17    gcc-2.95.4     6764    6574  2.1.3 (33)BA
+------------------------------------------------------------------------------
+SGI/MIPS
+*R5000       250MHz  Linux-2.4.20    gcc-3.2.3     10778   10664  2.2.6CVS  BA
+*R10000      196MHz  IRIX-6.4        cc             8461    9856  2.0.10(8) TP
+*R10000      196Mhz  IRIX-6.2        cc            15719   15581  2.0.13(12)BN
+*R4000        75MHz  Linux-2.4.19    gcc-2.95.4    37324   36098  2.1.5     BA
+*R4000     50/70MHz  IRIX-5.2        gcc           70764   70114  2.0.9 (1) TP
+*R4000     50/70MHz  IRIX-5.2        cc            81467   80176  2.0.9 (2) TP
+------------------------------------------------------------------------------
+HP/PA-RISC
+HPPA9000/785  552MHz HPUX-11.00     gcc-2.9         8284    8460  2.2.4(34) IS
+*HPPA8500     440MHz Linux-2.4.16   gcc-3.0.3       7558    7814  2.1.2     BA
+HPPA9000/778  134MHz HPUX-10.20      cc            18898!  29138! 2.0.19(9)
+*HPPA9000/778 134MHz HPUX-10.20      cc           (23666!)     -  2.0.12(10)
+HPPA9000/735  100MHz HPUX-10.20      cc           (25106!)(38898!)2.0.12(9)
+HPPA9000/735  100MHz HPUX-09-07      cc            25714!      -  2.0.16(15)IS
+HPPA9000/720     ??? HPUX-10.20      cc            66138!      -  2.0.9
+*HPPA9000/720    ??? HPUX-10.20      cc           101114! 119626  2.0.9
+HPPA9000/712   90MHz HPUX-10.20      cc           (26756!)(45196!) 2.0.12(9)
+*HPPA9000/712    ??? HPUX-10.10      gcc           66328!  71250! 2.0.5 (6)
+*HPPA7100LC    60MHz NextStep-v3     cc            62866!      -  2.0.10    LG
+*HPPA7000      50MHz HPUX-09.05      cc           113406! 152778! 2.0.9     LG
+------------------------------------------------------------------------------
+
+Notes:
+======
+(1) uncomment the last PLOTLIBS= in Makefile
+(2) same as above, compiled with cc -O2
+(3) AIX/POWER versions built with cc options including
+      -qarch=com -qtune=601 (any other tuning makes them slower GN)
+Addendum (IK): gcc 2.8.1 -O2 -DGCC_INLINE -mtune=604e produces a faster binary
+
+(4) Slightly different results in make bench (last decimal different in
+    3 tests, due to a different mulsr())
+(6) gcc -fPIC (otherwise /usr/bin/ld will not link the shared library)
+(7) the `program' bench fails on "install(addii)". But if the symbol addii
+    is declared within extern "C" {} in paridecl.h, it works.
+
+(8) use
+CC         = cc -64 -DLONG_IS_64BIT
+CFLAGS     = -O -OPT:Olimit=2170
+LD         = cc -64
+LDFLAGS    = -O -L/usr/lib64
+DLLD       = cc -64
+DLLDFLAGS  = -shared -elf -no_unresolved -all -L/usr/lib64
+CPP        = cc -64 -E -I.
+
+(9) * for gp-dyn: couldn't find a way to produce PIC code with either
+  -- HP as, version HP92453-03 UX.10.20.05 (DAVIS) PA-RISC 2.0 Assembler
+     (+z, +Z ignored)
+  -- gcc-2.8.1 + gas-2.6 (hppa1.1-hp-hpux9.01), using BFD version 2.6
+     (-fPIC -shared ignored)
+
+(bad) FIX: in o-hpux.hppa/Makefile, move kernel.o from OBJS to OBJSGP.
+Hence kernel.o has to be linked explicitly with all programs compiled with
+libpari.sl
+
+    * for gp-sta: remove +z from CFLAGS and LDFLAGS, final link only worked
+with the following command line (there has to be a better way...):
+
+cc dummy.c -c
+cc -o gp-sta -Wl,-a,archive -O dummy.o [... rest as produced by Configure]
+
+where dummy.c defines three dummy symbols (referenced in static libc):
+
+shl_findsym(){}
+ shl_unload(){}
+   shl_load(){}
+
+Whatever else I tried, linking failed:
+
+/usr/ccs/bin/ld: Data address is out of range for short load or store
+   Reference from:  kernel.o(0x8) [for all symbols defined in level0.s]
+
+(10) compiled without +z flags
+
+(11)
+CFLAGS (for egcs)= -O9 -malign-double -fschedule-insns2  -mpentium\
+-march=pentium -fomit-frame-pointer -fno-strength-reduce\
+-fno-inline-functions -fexpensive-optimizations
+CFLAGS (for pgcc)= -O9 -malign-double -mk6   -fomit-frame-pointer\
+-fno-strength-reduce -fno-inline-functions
+
+Libc: 5.4.46. egcs/pgcc compiled with Haifa scheduler
+Binutils: 2.9.1.0.4 or 2.9.1.0.15
+
+(12) used LD = cc -Wl,-rpath,my_exotic_DLL_path
+
+(13) use /opt/SUNWspro/bin/fpversion to get explicit recommended CFLAGS.
+     use -fsimple=1;  the Sun FD6U2 cc 5.3 defaults to -fsimple=2 at higher
+optimization levels, which is deadly to PARI.
+(these options must be given _after_ the -fast macro if -fast is used, or the
+-fast expansion will override them.)
+
+(14) use asmarch=sparcv7
+(15) Configure defaults to hpux-none. Use Configure -a and specify the
+hppa kernel. Remove +z from compiler flags
+(16) CFLAGS = -mk6 -march=i586 -O6
+(17) 256kb L2 Cache - Ultra U5
+(18) 2  MB L2 Cache - Ultra U10
+(19) replace err by pari_err in src/kernel/sparcv7/level0.S [in 2.0.16 or 17]
+(20) asmarch incorrectly defaulted to MicroSPARC (bypassed using Configure -a)
+(22) same machine as above, native binary compiled with mingw32-gcc-2.95.2
+     had to modify Odos/Makefile (to link with readline-4.0).
+     PROBLEMS:
+      * when started from the shell, on ^C GP quits! OK when started from GUI
+      * stdin redirection from the shell (gp < file) doesn't work (SEGV in gp).
+      * ANSI escape sequences are printed "as is" (so don't enable colors)
+(24) same machine as above, using the RSX compatibility package
+(25) Xeon
+(26) RedHat-7.0 + alpha has a broken glibc-2.1.x [issquare(8388609^2) = 0 !]
+     Updating to glibc-2.2 fixes the problem.
+(27) gcc-2.95.3 -O + new inline assembler [gcc-2.95.2 and gcc-2.95.3 -O3 both
+produce a broken binary]
+(28) gp-dyn: add -fPIC to CFLAGS [or linking fails].
+     gcc-2.96 fails
+(29) added -mcpu=athlon -march=athlon to CFLAGS (very minor speedup).
+     clock frequency for Athlon 1800+ = 1530MHz
+(30) cpu=Athlon XP1800+ Mandrake 8.1, Linux-2.4.17
+CFLAGS= -fbranch-probabilities -fstrict-aliasing  -falign-jumps
+-foptimize-sibling-calls -fssa -maccumulate-outgoing-args
+-minline-all-stringops
+-march=athlon   -O3 -DGCC_INLINE -Wall -Wno-implicit  -fomit-frame-pointer
+
+(31) cpu=Pentium II 450 at 463 (FSB a 103mhz),  Bi-Pro
+Linux-2.4.18-rc2 + patches
+CFLAGS= -fbranch-probabilities  -O3 -DGCC_INLINE -Wall -Wno-implicit
+-fstrict-aliasing -fssa   -foptimize-sibling-calls       -falign-jumps
+-march=pentiumpro -maccumulate-outgoing-args -minline-all-stringops
+-fomit-frame-pointer
+
+(32) gp-dyn: add -fPIC to CFLAGS [or linking fails].
+     Most gcc 2.95 ARM support lib have buggy (unsigned)%(unsigned)
+
+(33) LCDS System: 9672 G6 Model ZX7 (10 way processor, 32GB RAM)
+     DASD: Shark 2105-F20 (2.1 terabytes)
+
+(34) 64-bit executable
+
+(35) SMP, 512KB L2 cache
+
+(36) UP, 256KB L2 cache
+
+(37) http://db.debian.org/machines.cgi?host=crest
+     Build with gcc -fPIC.
+
+(38) Cygwin 1.3.20-1
+
+(39) http://db.debian.org/machines.cgi?host=raptor
+
+(40) same machine as (37) but without -fPIC.
+
+(41) 1 GB RAM, NForce2 chipset, 2.4.21pre7 kernel (Red Hat 8.0)
+CFLAGS = -O2 -s -DGCC_INLINE -Wall -fbranch-probabilities -mmmx -msse -m3dnow
+-ffast-math -fno-unsafe-math-optimizations -D__USE_ISOC99
+-D__USE_EXTERN_INLINES -fomit-frame-pointer -DBOTH_GNUPLOT_AND_X11
+
+-fno-unsafe-math-optimizations is mandatory with -ffast-math, in order to avoid
+bugs in nfields test.
+
+Used -fprofile-arcs for the first compilation parse, then -fbranch-probabilities
+to generate optimized executables. For this machine, many benchmarks are too
+short to provide accurate results (TIME=0 or 10).
+
+(42) gcc 3.3 version 20030226  -O3 failed to compile buch2.c with "internal
+compiler error". Works fine after compiling manually this file at -O2.
+
+(43) marie.medicis.polytechnique.fr
+
+=============================================================================
+Contributors:
+  AW = Aleksander Wittlin
+  AP = Andreas Piotrowski
+  BA = Bill Allombert
+  BM = Brian Mach Inneirghthe
+  BN = Balasubramanian Narasimhan
+  CC = Christian Cornelssen
+  CL = Christian Labesse
+  DC = David Cleaver
+  DE = Denis Excoffier
+  DP = David Purdy
+  EK = Emmanuel Kowalski
+  FU = Frederic Udina
+  GH = Guillaume Hanrot
+  GN = Gerhard Niklasch
+  HC = Henri Cohen
+  HG = Herbert Gangl
+  HR = Herman te Riele
+  HS = Hans Schaechl
+  HV = Hugo van der Sanden
+  ID = Ingo Dittmer
+  IS = Igor Schein
+  JB = Jonathan Baker
+  JC = John Cremona
+  JD = Jeroen Demeyer
+  KK = Kimitoshi Kono
+  LG = Louis Granboulan
+  MB = Marco Bucci
+  MH = Mohammad Bahathir Hashim
+  MS = Michael Stoll
+  MW = Mariusz Wodzicki
+  NI = Nathaniel Irons
+  PE = Philippe Elbaz-Vincent
+  OT = Olivier Thibault
+  PZ = Paul Zimmermann
+  QL = Qing Liu
+  RM = Richard J. Mathar
+  RN = Reed Nessler
+  SC = Scott Chadde
+  SH = Steve Harding
+  SHo= Steve Holmes
+  TP = Thomas Papanikolaou
+  TT = Tibor I. Toth
+  XR = Xavier Roblot
diff --git a/NEW b/NEW
new file mode 100644
index 0000000..cee6675
--- /dev/null
+++ b/NEW
@@ -0,0 +1,733 @@
+This file highlights some of the novelties in PARI/GP 2.x compared to 1.39.15
+(last release in the 1.x series). The detailed Changelog is in CHANGES.
+Incompatible changes are described in COMPAT. See TODO for possible future
+improvements.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.7 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+[Configure]
+  - Configure --mt=pthread or --mt=mpi: GP now supports POSIX threads and MPI.
+    See doc/parallel.dvi for an introduction, and have a look at the new
+    parxxx GP functions.
+
+  - Configure -gcov: support for gcov/lcov. As a result, the regression suite
+    (make test-all) has been much expanded and lots of obscure bugs fixed. See
+        http://pari.math.u-bordeaux1.fr/lcov-report/
+    for current coverage test reports.
+
+  - Configure now generates a file 'config.log' to help diagnose problems
+    (contains all messages from compilers)
+
+[The GP language]
+
+  - Ranges and slices: [a..b], x[a..b], x[^a]
+    ? v = [2..8]
+    %1 = [2, 3, 4, 5, 6, 7, 8]
+    ? v[2..4]
+    %2 = [3, 4, 5]
+    ? v[^2]     \\ remove 2nd element
+    %3 = [2, 4, 5, 6, 7, 8]
+    ? M = matid(3); M[1..2, ^2]  \\ first two rows, remove 2nd col
+    %4 =
+    [1 0]
+
+    [0 0]
+
+  - Set notations:
+    ? [ p | p <- primes(10), isprime(p+2) ]
+    %1 = [3, 5, 11, 17, 29]
+
+  - Multiple assignments: [a,b,c] = V, for a = V[1], b = V[2], c = V[3]
+    ? [D,U,V] = matsnf(A, 1)    \\ returns SNF and transformation matrices
+
+  - Error trapping: iferr() + a new data type 't_ERROR' to represent error
+    contexts. See ??iferr
+
+     \\ Compute [B]P on a "curve over Z/NZ". If an exception occurs,
+     \\ we found a zero divisor in Z/NZ, thereby factoring N.
+     ECM(N, B = 1000!, nb = 100)=
+     {
+       for(a = 1, nb,
+         iferr(ellmul(ellinit([a,1], N), [0,1], B),
+           E, return(gcd(lift(component(E,2)),N)),
+           errname(E) == "e_INV"));
+     }
+     ? ECM(2^101-1)
+     %1 = 7432339208719
+
+  - Timeouts: alarm(delay, expr) spends 'delay' seconds trying to evaluate
+    'expr', then aborts returning a t_ERROR object.
+
+  - Multi-if: to simplify successive 'else' clauses
+      ? if (a == 1,     print("1"),          \
+            a < 0,      print("negative"),   \
+            isprime(a), print("prime"),      \
+            print("generic"))
+
+  - new function cmp() to "compare" arbitrary objects (transitive order
+    relation, returns 0 iff x === y). Useful for sets.
+
+  - new function getenv()
+
+[The GP calculator]
+
+  - parallel GP support: parapply, pareval, parfor, parforprime, parselect,
+    parsum, parvector. E.g.
+      ? parapply(factor, [2^256 + 1, 2^193 - 1])
+    will factor these two integers in parallel.
+
+  - forprime(p = a, b, ...) now iterates over arbitrary ranges of primes,
+    independently of 'primelimit'. Parameter 'b' can be omitted (no upper
+    limit). More generally, primelimit is now deprecated: libpari functions
+    can quickly produce their own primes without relying on (enough)
+    precomputed primes.
+
+  - new iterators:  forcomposite(), forpart() forqfvec(), to loop
+    over composite integers, (possibly restricted) partitions and integer
+    points in ellipsoids.
+
+  - GP debugger, new functions expanding the 'break loop' mechanism: dbg_up(),
+    dbg_down(), dbg_x(), breakpoint()
+
+  - liftall(), liftint(), liftpol() give more flexibility than lift() in
+    complicated situations
+
+  - characteristic(x) returns the "characteristic" of the base ring over which
+    x is defined.
+
+  - ffgen(p^f), as an alias for ffgen(ffinit(p,f))
+
+  - vecsum(v), as an alias for sum(i=1,#v,v[i])
+
+  - variable() no longer raise exceptions, but returns 0 if the object
+    has no main variable.
+
+  - getabstime() returns the CPU time elapsed since gp startup, providing a
+    reentrant version of gettime()
+
+  - %#n returns the time it took to compute history result %n
+
+  - new default 'linewrap'
+
+  - arbitrary GP 'defaults' can now be set via the command-line:
+      gp -D default=value
+    gp --primelimit lim (gp -p lim) and gp --stacksize=lim are deprecated.
+    Use the generic form  (-D parisize=lim or -D primelimit=lim)
+
+[Multiprecision Kernel & Transcendental functions]
+
+  - binary splitting: Catalan's constant, Pi, log(2) (to be expanded)
+
+  - logint(x,b) for floor(log(x) / log(b)), avoiding rounding problems
+
+  - sqrtnint(x,b) for floor(x^(1/b))
+
+  - expm1(x) for exp(x) - 1, but also accurate for x ~ 0
+
+[Polynomial Arithmetic & Power series]
+
+  - Mulders/Hanrot-Zimmerman short products for power series
+
+  - Allow t_SER arguments for gamma, lngamma, and psi around arbitrary
+    complex numbers (was either forbidden or limited to z = 0 or 1)
+
+  - seralgdep: to find linear relations with polynomial coefficients
+    ? s = 1+1/2*y+3/8*y^2-3/16*y^3+3/128*y^4+15/256*y^5-57/1024*y^6 + O(y^7);
+    ? seralgdep(s,2,2) \\ relation of degree <= 2, degree(coeffs) <= 2
+    %2 = -x^2 + (y^2 + y + 1)
+
+  - polgraeffe(f): returns g such that g(x^2) = f(x)f(-x)
+
+  - poliscyclo(), poliscycloprod(), polcyclofactors(): cyclotomic factors
+    of rational polynomials
+
+[Linear Algebra]
+  - port of the program ISOM by Bernd Souvignier for computation of
+    automorphisms and isomorphisms of lattices.
+    New GP functions: qfauto, qfisom, qfisominit, qfautoexport
+
+  - linear algebra routines now try to convert generic GP constructions
+    involving t_INTMODs or t_FFELTs to appropriate (faster, more memory
+    efficient) representations, then call routines in the libpari modular
+    kernel (FpM, Flm, F2m, FqM, FlxqM, F2xqM).
+
+  - add optional flag to mateigen to also return the eigenvalues
+
+  - charpoly() now selects an appropriate algorithm by itself, depending
+    on the input. Using a flag should no longer be necessary and is
+    deprecated.
+
+  - mathnf for matrices over K[X]
+
+  - mathnfmodid(x,D), where D = [d1,...,dn] compute the HNF of
+    concat(x,matdiagonal(D)); in a more efficient way
+
+  - matqr() to compute the QR-decomposition of a real square matrix;
+    mathouseholder() to apply a sequence of Householder transforms
+
+  - internal support for sparse matrices and Wiedemann algorithm; currently
+    only used by the discrete log algorithms.
+
+  - matinverseimage(A, t_MAT B) would treat individual columns B[,i]
+    independently and successively. Now use a single Gauss reduction.
+
+  - normlp(): true L^p norm [ N.B. the old norml2() is still available,
+    and returns the *square* of the L^2 norm ].
+
+  - clean generalizations of current norml2: qfnorm(), qfbil()
+
+[Elementary Number Theory]
+
+  - arithmetic functions now accept factorization matrices as input, you can
+    use any of f(N), f(factor(N)) or f([N, factor(N)]).
+
+  - arithmetic functions no longer apply componentwise to vector / matrix
+    arguments [ to allow passing factorization matrices ]: use apply()
+
+  - new convenience functions: hamming(), ispowerful(), digits() /
+    sumdigits(), ispolygonal(), istotient(), isprimepower()
+
+  - randomprime(), random([a,b])
+
+  - Bernoulli polynomials: bernpol() and sumformal()
+    ? sumformal(n^2)     \\ F such that F(b) = \sum_{n <= b} n^2
+    %1 = 1/3*n^3 + 1/2*n^2 + 1/6*n
+
+  - sumdivmult: to sum multiplicative expressions
+
+  - sieve algorithms for znlog() and fflog(), computing discrete logs in F_q^*
+
+[Elliptic curves & Arithmetic geometry]
+
+  - new dynamic implementation of the 'ell' data structure: ellinit is now
+    used to record the coefficients of the curve and the domain over which it
+    is defined. Further data is added to the structure on demand, if and when
+    it is needed, e.g. cardinality and group structure. See ??ellinit.
+
+  - elliptic curves functions no longer assume that a curve over Q is given by
+    a minimal model. A non-miminal model used to silently produce wrong
+    answers; no longer!
+
+  - allow ellinit(E / Qp) for arbitrary p (also p = 2) and reduction type
+    (no longer restricted to Tate curves)
+
+  - allow ellinit(E / Fq) for non-prime finite fields, incl. point counting
+    (SEA, Harley)
+
+  - allow ellinit(E / C)
+
+  - new function ellheegner() to find a non-torsion rational point on
+    E / Q of rank 1.
+
+  - new implementation of ellweilpairing / elltatepairing
+
+  - ellsearch now accepts both syntaxes allowed by ellconvertname(),
+    e.g. "11a3" / "11a" and [11,0,3] / [11,0]
+
+  - extend ellinit inputs: ellinit([a4, a6]). On singular curve, return []
+    instead of raising an error. New function ellfromj().
+
+  - genus2red: an implementation of Liu's algorithm to determine the
+     reduction of a genus 2 curve (at p > 2). Based on genus2reduction-0.3,
+       http://www.math.u-bordeaux1.fr/~liu/G2R/ (Cohen & Liu, 1994)
+    mostly rewritten from scratch, and fixing known problems in the original
+    implementation (so-called bug27, bug28). The regression bench contains a
+    check of at least one instance of each of Namikawa-Ueno's types + all
+    cases on which the original genus2reduction was known to fail.
+    CAVEAT: the interface will probably change & reduction at p = 2 not handled
+
+[Number Fields]
+
+  - maximal orders (when the discriminant is hard to factor): allow to specify
+    a list of primes at which the order nf.zk must be maximal. This [T, listP]
+    format supersedes the old addprimes() hack as well as rigid optional
+    flags for nfbasis, nfdisc, polredabs. (And no longer depends on the
+    global 'primelimit'...) See ??nfinit
+    ? T = polcompositum(x^5 - 101, polcyclo(7))[1];
+    ? nf = nfinit( [T, 10^3] );
+    ? nfcertify(nf)
+    %3 = []
+    A priori, nf.zk defines an order which is known to be maximal at all
+    p <= 10^3. The final certification step proves it is in fact
+    globally maximal.
+
+  - polredbest / rnfpolredbest: "best-effort" variants of polredabs /
+    rnfpolredabs. Not canonical but often smaller, and run in poly-time !
+
+  - idealprincipalunits: structure of the multiplicative group
+    (1 + pr) / (1 + pr^k), for a prime ideal pr
+    [ special case of idealstar, faster ]
+
+[COMPATIBILITY WARNING]
+
+  - lift(x,'v) / centerlift(x,'v) now only lift t_POLMODs in variable v,
+    no longer (most) t_INTMOD / t_PADICs met along the way
+
+  - rnf.pol (absolute defining polynomial / Q) has been renamed rnf.polabs.
+    rnf.pol is now the relative polynomial, defining the relative extension
+    over the base.
+
+  - as a side effect of the new %#n construction, all GP results are now stored
+    as history entries,  including the "void" object returned by functions
+    such as print() or for().
+
+  - renamed bezout() -> gcdext()
+
+  - renamed ellpow() -> ellmul()
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+[Libpari Build & Configuration]
+  - 'Configure --tune' fine-tunes gp and the PARI library for a given host.
+    On some machines this leads to noticeable performance improvements,
+    esp. when using the GMP multiprecision kernel.
+
+  - 'Configure --enable-tls' makes libpari thread-safe, for multi-threaded
+    applications. See Appendix B in the "User's Guide to the PARI Library"
+
+[Multiprecision Kernel]
+  - The GMP library is now used by default if Configure can find it.
+
+  - Schoenhage-Strassen big integers multiplication to native kernel
+    (very useful if GMP not available)
+
+[Polynomial Arithmetic]
+  - faster multiplication of integer polynomials (Kronecker's trick)
+
+  - subquadratic gcd over prime finite fields
+
+  - special polynomials (polcyclo, polchebyshev, pollegendre...) are orders
+  of magnitude faster ( polcyclo(10^6): 1min 30s (2.3) -> 4ms (2.5) ) and
+  directly allow evaluation at a given point, e.g. polcyclo(n, 2) for Phi_n(2).
+
+  - issquare(t_POL) now works reliably over prime finite fields ( we used
+  to have  issquare(Mod(1,2)*(x^2+1)) -> 0, or error messages in more
+  complicated cases ).
+
+  - charpoly no longer assumes that the characteristic is 0 or large enough
+    (Berkowitz division-free algorithm).
+
+[Linear Algebra]
+  - all LLL variants use an implementation of NGuyen & Stehle's L^2
+    algorithm : stabler, much faster
+
+  - better resultants
+
+[Elliptic curves]
+  - ellap() now uses the SEA algorithm (port of GP's ellsea package).
+  - discrete logarithm [ elllog() ], group structure of E(Fp) [ ellgroup() ],
+  - division polynomials [ elldivpol() ]
+  - Tate and Weil pairings [ elltatepairing() / ellweilpairing() ]
+
+[Number Fields]
+  - Class-field theoretic functions (e.g. bnfinit) no longer cheat on Bach's
+    constant. They now use safe bounds by default, correct under GRH, and no
+    slowdown has been observed.
+  - bnfinit: huge improvements for fields of large degree or admitting
+    non-trivial automorphisms (series of patches by Loic Grenie).
+  - faster quadhilbert(D < 0) [ Hilbert class field via CM ]
+  - Frobenius elements [ idealfrobenius() ]
+  - ramification groups [ idealramgroups() ]
+
+[GP defaults]
+  - new default "factor_proven" to guarantee that all integer factorizations
+    outputs proven primes (the default is to be happy with strong pseudoprimes).
+
+  - new defaults "graphcolormap" and "graphcolors" to allow arbitrary
+    colormaps in hi-res plots.
+
+  - new default 'histfile', to save your typing history in between sessions !
+
+[GP data structures]
+  - Lists now grow as needed, without imposing an awkward maximal length.
+    v = List()  is now sufficient to initialize an empty list, instead of
+    v = listcreate(100) to initialize a list which wouldn't grow past 100
+    elements.
+
+  - New GP type to handle non-prime finite fields in a reasonably efficient
+    way. E.g:
+    ? T = ffinit(7,5); \\ irreducible of degree 5 in F_7[x]
+    ? t = ffgen(T); \\ The element x mod (T,p) in Fp[x] / (T) ~ F_{7^5}
+    %2 = x  \\ this has type t_FFELT
+    ? t^10 \\ handled like Mod(x, T) but faster, and less cumbersome
+    %3 = 5*x^4 + 5*x^2 + 5*x + 6
+    ? fforder(t)
+    %4 = 5602  \\ multiplicative order
+    ? g = ffprimroot(t); \\ primitive element
+    ? fflog(g^1000,g)
+    %6 = 1000
+
+  - In GP-2.3, it was not possible to use the same identifier for
+  variables and functions; in GP-2.5 there is nothing wrong with defining
+    f(x,y)=x^2+y^2
+  then setting f = 1 (thereby deleting the user function). In fact, the
+  distinction between variables and functions has been abolished: anonymous
+  functions (closures) may be defined and assigned to variables, e.g.
+  the old f(x,y) = x^2+y^2 is now an alias for  f = (x,y) -> x^2+y^2
+
+[GP]
+  - the debugger, or "break loop", is now enabled by default
+    [ set breakloop = 0 in your gprc to disable it ], and no longer
+    controlled by trap(). The debugger is more verbose:
+    ? f(x) = g(x);
+    ? g(y) = 1/y;
+    ? f(0)
+      ***   at top-level: f(0)
+      ***                 ^----
+      ***   in function f: g(x)
+      ***                  ^----
+      ***   in function g: 1/y
+      ***                   ^--
+      *** _/_: division by zero
+      ***   Break loop: type 'break' to go back to GP
+    break> y
+    0
+
+  - all GP functions are now understood by GP2C
+
+  - formatted printing : printf(), Strprintf()
+
+  - alarm(n) to abort a lengthy computation after n seconds.
+
+  - === "isidentical" operator, much stricter than ==
+
+  - The introducion of anonymous functions had a number of useful side effects;
+  for instance, it made possible two new functions select() and apply(), as
+  well as arbitrary comparisons in vecsort():
+
+   \\ primes in { i^2+1 : i <= 50 }
+   ? select(x->isprime(x), vector(50,i,i^2+1))
+   %1 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+
+   ? apply(x->x^2, [1,2,3,4])
+   %2 = [1, 4, 9, 16]
+
+   \\ sorts a vector of polynomials by increasing discriminant
+   ? vecsort( v, (x,y) -> sign(poldisc(x) - poldisc(y)) )
+
+[Main Backward Compatibility issues] see the 'COMPAT' file in the distribution
+for the full list.
+  - The main issue with existing GP scripts has to do with the scope of
+    private variables (my vs. local), see section 2.6 in User's Manual.
+    Indeed, variables implicitly scoped to loop or function bodies are now
+    lexically scoped. From GP-2.5 on, in constructs like
+      for(i = 1, 10, g())
+      f(i) = g()
+    the index i is truly local to the loop/function body. It is no longer seen
+    by the function g(), as used to be the case in GP-2.3.
+
+    One can declare lexicaly-scoped variable anywhere using the construct
+    my(x, y, z, t), possibly with initializations: my(x = 1, y = x).
+    The old "local" keyword keeps the same semantic (dynamic scoping) and is
+    mostly obsolete, outside of very specific situations beyond the scope of
+    these release notes.
+
+  - function calls *must* include parentheses. I.e. typing 'f()' calls the
+    function f without arguments as expected, typing 'f' returns an anonymous
+    function with the same definition as f; for instance, v[1] = f is valid,
+    assigning the closure f to the first entry of vector v.
+
+  - private "prime table" (addprimes) must now contain primes only: its
+    entries are now used in all arithmetic functions
+
+  - The pseudo-random number generator has been changed. The old linear
+    congruential generator has been replaced by Brent's XORGEN, which uses
+    a Linear Feedback Shift Register: pseudo-random sequences are much
+    better behaved, e.g. matdet(matrix(5,5,i,j, random())) is no longer
+    guaranteed to be divisible by 2^90 or so. There is no simple way to
+    emulate GP-2.3 pseudo-random sequences in GP-2.5.
+
+  - PariEmacs is no longer distributed with PARI/GP. The "PARI Emacs shell"
+    is available as a separate package, to be downloaded once if at all.
+
+  - | and & were accepted as aliases for || and && respectively. This
+    construction still works in GP-2.5, but is scheduled to disappear. We
+    strongly advise to update scripts to use the proper '||' and '&&'
+    constructions.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.3 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+   * The GP2C compiler is available at
+
+         http://pari.math.u-bordeaux.fr/download.html#gp2c
+
+    GP2C compiles GP scripts to the C language, easing the task of writing
+    PARI programs. It can transparently compile them to object code and
+    load the resulting functions in gp. Low-level gp2c-compiled scripts
+    typically run 3 or 4 times faster. Minor hand editing (specifying types)
+    typically gains a further factor 2.
+
+   * Cremona's database of elliptic curves is available through the 'elldata'
+     package [ to be downloaded separately ]
+     ? E = ellinit([1,1,0,10,10]);
+     ? id = ellidentify(E)[1]   \\ [1] = discard change of variables
+     %2 = ["69950b1", [1, 1, 0, 10, 10], [[-1, 1], [-1/4, 23/8]]]
+     \\ gives name and generators
+
+     ? E = ellinit("11a1"); E.disc
+     %3 = -161051
+
+     ? ellsearch(11); \\ all curves of conductor 11
+
+     ? forell(E, 10,20, print(E))   \\ iterate over curves of conductor 10-20
+     ["11a1", [0, -1, 1, -10, -20], []]
+     ...
+
+Kernel:
+
+   * Use 'Configure --with-gmp' to replace the native multiprecision kernel by
+     the GNU MP library (featuring asymptotically fast arithmetic).
+
+   * Cleanup of all architecture specific kernels (all macroized); added
+     support for x86_64, ppc64, hppa and hppa64, ia64, sparc64, m68k.
+
+   * Faster algorithms for "transcendental" functions (divide/conquer square
+     root, AGM for log and Pi, Newton for exp and most trigonometric functions,
+     AGM for inverse trigonometric functions, rewrite for gamma and zeta =>
+     faster Bernoulli, Mestre's AGM for ellheight)
+
+   * Faster and cleaner kernel for modular arithmetic. Try e.g.
+     factormod/factorff or polcompositum.
+
+   * Major internal cleanups: separate lgef for t_POLs is gone, zero t_SER
+     and t_POL now handled in a uniform way, heuristic soft copies in
+     t_INTMOD, t_POLMOD, t_PADIC are gone [ led to fatal errors in complex
+     scripts, no performance penalty ]
+
+   * The "syntax" of GP routines and operators are no longer hard-coded in
+     the sources, but maintained in a separate database (pari.desc). This
+     way, external tools like GP2C need not be modified when the GP language
+     is changed.
+
+Algebraic number Theory:
+   * Faster integral LLL (still not super fast, but getting better), and
+     polynomial factorization routines (over finite fields [ new modular
+     kernel ], Q or general number fields [ van Hoeij's algorithm ])
+
+   * Faster maximal order (round4 rewrite) and polredabs (esp. with flag
+     16: don't factor the discriminant; yields a canonical equation for a
+     field), faster ideal arithmetic (prime decomposition, approximation,
+     multiplication).
+
+   * Faster and more reliable class-field theoretic functions quadclassunit,
+     bnfinit, bnfisprincipal, bnrinit (and related functions, e.g. bnrconductor
+     or bnrdisc), rnfkummer, thue (fast enumeration of small solutions, don't
+     assume the full unit group is known).
+
+   * A set of fast routines for Galois theory (galoisininit, nfgaloisconj,
+     galoisisabelian, galoisfixedfield, galoissubfields, galoissubcyclo for
+     abelian fields, galoisidentify to identify large Galois fields up to
+     degree 127).
+
+     'galdata' package [ to be downloaded separately ]: polgalois is safer
+     and orders of magnitudes faster in tough cases, output is now
+     human readable
+     ? polgalois(x^11-2)
+     time = 1,759 ms.   \\ used to be ~ 1 hour
+     %1 = [110, -1, 4, "F_110(11)=11:10"]
+
+Miscellaneous:
+   * For convenience, the manual was split in two parts: the GP user's manual
+     and the libpari user's manual, the latter being substantially expanded.
+     Many formerly private functions have been renamed, specified,
+     cleaned up and documented.
+
+   * Initial implementation of the APR-CL primality prover, faster
+     compositeness tests (BPSW)
+
+   * A new set of fast numerical summation and integration routine,
+     variations on the Ooura-Mori "double exponential" method. See ??intnum
+     Library interface to all these functions and standard iterators (e.g.
+     forvec)
+
+   * Error messages now mention the GP function where the error occured.
+
+   * Input/output and convenience functions: Str(a,1,c) --> "a1c",
+     Strexpand("~") --> "/home/a2x/belabas", substvec (parallel substitutions),
+     substpol(expr, x^2, y), writebin (write objets in binary format for
+     fast retrieval), readvec (load a file's content into a vector)
+
+   * Support for new graphic libraries (Qt, FLTK) [ ==> hi-res plots now
+     also available under Mac OS X and Windows ]
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+   * PARI/GP is now released under the GNU General Public License.
+
+   * PARI now has a CVS server which is intended for PARI lovers who want
+     the very latest bleeding edge release (see the CVS.txt file).
+
+   * Argument checks have been added to prevent unpredictable results when
+     the input is incorrect.
+
+   * Errors can be trapped to avoid abort and recover computations.
+
+   * extended on-line help:
+     ?? (no arguments)      opens the users'manual in xdvi,
+     ?? tutorial / refcard  opens tutorial / refcard in xdvi,
+     ??? keyword            searches for topic in the manual.
+
+   * Arithmetic: much faster integer factorization with several factoring
+     engines including Pollard Rho, SQUFOF, improved ECM, and an MPQS/PMPQS
+     implementation derived from LiDIA's, with kind permission from the LiDIA
+     team
+
+   * Polynomials:
+     - much faster factorization over Z[X] (van Hoeij's algorithm) or Fq[X]
+       (more efficient modular kernel), esp. when the polynomial is defined
+       over Fp.
+     - Ducos' subresultant algorithm for resultants
+
+   * Number field:
+     - improved ROUND 4 for computations of integral basis/discriminant
+     - faster polredabs / rnfpolredabs polynomial reductions functions
+     - Galois extensions of Q: Fixed fields, Galois conjugates using
+       Allombert's algorithm.
+
+   * Class group, ray-class group:
+     - improved bnf/bnr functions (faster, numerically stabler), in
+       particular bnfisprincipal
+     - computations of explicit defining equations of abelian extensions of
+       imaginery quadratic fields (using complex multiplication) of totally
+       real abelian extensions (using Stark units).
+
+   * Elliptic functions: Weierstrass and Weber functions.
+
+   * Plotting: support of gnuplot, new functions (possiblity to plot directly
+     in a file).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%% VERSION 2.0 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+The GP/PARI structure has been cleaned up.
+
+   * The whole configuration process has been automated, and a Configure
+     file is provided. Just typing `./Configure' should see you home in most
+     cases.
+
+   * PARI is now available as a dynamic library, thanks to Louis Granboulan.
+     (you can link GP with it, if you wish to). This saves a tremendous
+     amount of disk space, and is generally more convenient as you don't need
+     to re-link your files when updating the library (or when debugging.
+     or profiling, or...).
+
+   * types now have a symbolic mnemonic name (e.g t_INT for an integer,
+     t_VEC for a vector, and so on).
+
+   * General speed-up (depends on your applications, about 40% for our
+     generic testing file).
+
+   * Experimental module loading structure (the actual function tree
+     has not yet been cut into modules, but for the GP specific functions).
+
+==========================================================================
+Many new or improved functions in the PARI library.
+
+   * MANY class-field related functions. In particular:
+     - is it now possible to try and remove the GRH assumption on class group
+     computations.
+     - ray class groups computations (including discrete log).
+     - explicit defining equations in simple cases (Kummer extensions of prime
+       degree, quadratic base field).
+
+   * computation of Galois groups up to degree 11
+
+   * roots is now entirely reliable, thanks to Xavier Gourdon.
+
+   * some core routines have been optimized: Karatsuba fast multiplication,
+     a specific function gsqr() for squarings,...
+
+   * input/output is much more flexible now:
+     - a function GENtostring has been added, generalizing gitoascii to any
+     PARI object (with a simpler syntax: GENtostring(g) returns a malloc'ed
+     string containing g as gp would print it).
+
+     - readexpr has a relative freadexpr (for filtered readexpr), which enables
+     you to use input containing whitespaces.
+
+     - you can use GENs in formatted output, a la printf.
+
+   * improved garbage collecting.
+
+   * private variables can be created without an explicit readexpr(), using
+     fetch_var() and delete_var().
+
+==========================================================================
+GP has been completely re-written:
+
+   * lowercase/uppercase are now significant. All predefined constants
+     (Euler, I, Pi) have been renamed (as well, the o() notation for series
+     and padics has been superseded by O()).
+
+     for (i=1,10, print(i)) will not yield an error anymore.
+
+   * human-readable error messages, including a caret to indicate where
+     a GP syntax error occurred.
+
+   * function names were renamed according to a more logical scheme. The
+     file new.dico provides a translation (available under GP using "whatnow")
+
+   * You can retrieve basic information from complicated objects using member
+     functions. For instance x.disc will yield the discriminant of x, whether
+     it was created by nfinit (aka initalg), bnfinit (aka buchinit), ellinit
+     (aka initell).
+
+   * A `gprc' file is available to set "permanent" defaults (such as
+     global variables, aliases, custom user functions, etc...).  For instance,
+     you can put all your scripts in some special directories, and
+     point them out to GP using "path". See misc/gprc.dft for examples.
+
+     The function "default" enables to change most defaults under gp.
+     For instance: default(compatible, 2) will give you back the former gp
+     function names and helpmessages. [default(compatible, 3) undoes the
+     lowercaps/uppercaps changes as well]. Try "default".
+
+   * basic C idiosyncrasies such as for instance i++ (for i=i+1), i<<1
+     (for left shift) or i+=j (for i=i+j) are now allowed within GP scripts.
+     /* */ multi-line comments are understood.
+
+   * lists and (primitive) string support have been added. Characters can be
+     quoted with the usual meaning. As a result, set functions can now be
+     used with arbitrary elements.
+
+   * if your terminal supports color (variants of color_xterm for instance),
+     you can tell GP to highlight its output in different (user configurable)
+     colors. This is done by fiddling with the default "colors".
+
+   * The familiar functions "break", "next" and "return" are now available.
+     These should supersede the buggy label/goto provided in older versions.
+
+   * Enhanced on-line help. If you have perl on your system, try
+     ?? function-name (e.g ?? bnfinit)
+     This is provided by external scripts which can be used independently,
+     outside of the GP session.
+
+   * If readline is installed on your system, a context-dependent completion
+     (not yet user-programmable) is now available (try hitting <TAB> here and
+     there). Try ?? readline.
+
+   * many functions now have default arguments (shown between braces {} in
+     the on-line description). gp first reads user-supplied arguments, and
+     then fills in the arg list with these default values. Optional args can
+     be entirely omitted, comma included (for a function with no mandatory
+     arguments, even parentheses are optional !). For instance:
+
+       Mat = Mat()
+       bnfclassunit(x^2+1,0) = bnfclassunit(x^2+1)
+       bnfclassunit(x^2+1,,[0.2,0.2]) = bnfclassunit(x^2+1,0,[0.2,0.2])
+
+       The "else" part of the "if" function can be entirely omitted.
+       if (a,1) is now correct; of course, the former syntax if (a,1,) is
+       still valid.
+
+   * functions "extern" and "system" have been added to interface with
+     external programs (UNIX only). You can do for instance
+     extern("myprog"), or system("ls -l *.gp").
+
+   * even better, "install" enables you to load any function provided by
+     a dynamically linked library, and have the GP interpreter use it. This
+     makes it easy to have your own customized version of GP with your own set
+     of functions on startup (you can document them using "addhelp").
+
+   * On 32-bit machines, maximum number of variables has been increased from
+     254 to 16382. Arrays can have up to 16777214 elements (instead of 65534).
+     In addition vector/matrix operations in GP now perform orders of
+     magnitudes faster than in version 1.39
diff --git a/README b/README
new file mode 100644
index 0000000..886427e
--- /dev/null
+++ b/README
@@ -0,0 +1,87 @@
+This is PARI/GP, version 2.7.x (*).
+
+PARI/GP Number Theory-oriented Computer Algebra System
+        Copyright (C) 2000-2014 The PARI Group, Bordeaux.
+
+==========================================================================
+
+To get started, run
+
+  tex doc/INSTALL.tex
+
+and have a look at the file doc/INSTALL.dvi, extracted verbatim from Appendix A
+of the User Manual. If you are in a hurry, look at INSTALL (ascii file). But
+a lot of information is missing there.
+
+Once 'Configure' has been run, 'make doc' typesets separate user's manual for
+GP and libpari, a tutorial and reference card in directory doc.
+
+See the files NEW and COMPAT for new features and incompatible changes
+respectively between 2.7 and older versions. See CHANGES-* for a terse
+description of subsequent patches.
+
+For the adventurous: to check out bleeding-edge development versions of
+PARI/GP, see the file README-git.
+
+Afterwards, you're on your own.
+
+==========================================================================
+
+There are three mailing lists devoted to the PARI/GP package, and most
+feedback should be directed to those. They are:
+
+* pari-announce: to announce major version changes. You can't write to this
+one, but you should probably subscribe.
+
+* pari-dev: for everything related to the development of PARI, including
+suggestions, technical questions, bug reports or patch submissions.
+
+* pari-users: for everything else.
+
+To subscribe, send empty messages respectively to
+
+   pari-announce-request at pari.math.u-bordeaux.fr
+   pari-users-request at pari.math.u-bordeaux.fr
+   pari-dev-request at pari.math.u-bordeaux.fr
+
+with a Subject: containing the word "subscribe".
+
+If you are not a member of any of those lists and do not want to become
+one, you can write to us at
+
+  pari at math.u-bordeaux.fr
+
+We may forward your mail to the lists above and will definitely try to
+correct faulty behaviour, if necessary. But we cannot promise that you
+will get an individual answer.
+
+Last but not least, PARI home page can be found at
+
+  http://pari.math.u-bordeaux.fr/
+
+  Thanks for your support, and have fun !
+
+========================================================================
+
+PARI/GP 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 2 of the License, or (at your option) any later
+version. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+(*) Version numbers have the shape: MAJOR.MINOR.patchlevel.status. The status
+increases through {alpha, beta, release}. New features are introduced during
+the `alpha' phase, tested during `beta', and declared stable when `release'
+is reached.
+The MAJOR version number will not change for a while.
+The MINOR version number increases by 2 when a new status cycle starts (as
+soon as a 'release' version is out): odd numbers are reserved for 'released'
+versions, even ones for alpha/beta phase.
+The patchlevel goes up each time we feel an upgrade is necessary (improvement
+or bugfix). 'Release' versions may be updated with further patchlevels if
+important bugs need to be fixed before the next cycle reaches completion.
diff --git a/README-git b/README-git
new file mode 100644
index 0000000..22fb7a6
--- /dev/null
+++ b/README-git
@@ -0,0 +1,96 @@
+We use the GIT open-source revision control system. For us developers, it
+provides network-transparent source control. For ordinary users it provides a
+convenient way to obtain patched versions in between releases, or follow
+development branches. GIT clients are available for all major platforms:
+Unix, MacOS, Windows, see
+
+  http://git-scm.com/download
+
+In particular, the git command-line client is readily available in all Linux
+distributions.
+
+Note: We worked happily for many years with CVS, then Subversion, and
+provided anonymous read-only CVS / Subversion servers. The transition period
+is over: fetching PARI using this method is no longer possible; please
+upgrade to GIT.
+
+This file documents access to the PARI GIT server, which is intended for PARI
+lovers who want the very latest bleeding edge release and development
+branches. These sources may contain severe bugs, they may not even compile,
+benches may fail and so on. Stable releases are made available on a regular
+basis using the customary method: a message to pari-announce.
+
+Note that in order to use the sources fetched via GIT, you will need a
+working bison and perl installation, as well as the regular build toolchain.
+
+1) First connection to the GIT repository:
+==========================================
+To get a working copy, type the following command from the shell
+
+    git clone http://pari.math.u-bordeaux.fr/git/pari.git
+
+This creates a local copy of the distribution from the distant repository in
+local directory pari, which you may move or rename as you wish. From now on,
+you can cd to this pari directory and use any git command directly, as long as
+you remain there, or in a subdirectory.
+
+2) What can I do now ?
+======================
+
+* You can build pari in the usual way (see INSTALL) as if this 'pari' directory
+had been created by fetching, then extracting, an archive on an FTP server.
+
+* You can update your local copy at any time using git pull, which puts you in
+synch with the repository.
+
+* You can list all available development branches using git branch -a. 
+To checkout a specific branch, type git checkout branchname. The two main
+branches are master (testing branch) and pari-2-5 (updates to old stable
+branch). Other branches are customarily named after the developer who sent 
+the initial patch and the proposed feature, and eventually merged into master.
+
+* You can create your own private branches: for instance, the following creates
+a local branch my-branch, starting from a copy of some-branch you found on our
+server.
+
+    git checkout origin/some-branch -b my-branch
+
+Of course, you can modify files in your copy and commit changes to your local
+branches. You can send the output of the relevant git diff command, to the
+pari-dev mailing list with a short description of what you have done. (No need
+to subscribe to the mailing list to post, but it will allow you to follow the
+discussion!)
+
+* On the other hand, you will not be able to commit your changes to our GIT
+repository using anonymous access. For this, you will need read-write access,
+which requires an account on our development machine.
+
+3) Version tags and branches:
+=============================
+
+Official releases (starting from version 2.0.17) are 'tagged' so that all files
+pertaining to a given release can be simultaneously accessed without tracking
+version numbers. Tag names are pari-version with dots replaced by dashes, 
+e.g. pari-2-0-20 for 2.0.20.
+
+To fetch a specific version of pari (2.0.17 or more recent), type for instance
+
+    git checkout pari-2-0-20
+
+The branch pari-2-5 denotes the stable branch 2.5.* as a whole, and can be
+used to checkout up to date sources from that branch in between releases.
+For instance:
+
+    git checkout pari-2-5
+
+produces the latest stable distribution with all relevant patches (the ones not affecting stability) backported.
+
+Tips and Caveats:
+
+* git diff gives you the difference between your local copy and the sources
+they were based on, not with the current state of the testing branch on the
+PARI repository. Use 'git diff master' for that.
+
+* To see the log message associated to the last commit leading to the current
+state of your local repository, type 'git show'. You may add a file or directory
+name to get the log message for the last commit which modified it.
diff --git a/config/DOC_Make.SH b/config/DOC_Make.SH
new file mode 100644
index 0000000..528e54f
--- /dev/null
+++ b/config/DOC_Make.SH
@@ -0,0 +1,162 @@
+# Path to directories
+cfg=../$config_dir
+data=../$data_dir
+doc=../$doc_dir
+desc=../$desc_dir
+ex=../$examples_dir
+misc=../$misc_dir
+src=../$src_dir
+
+cat > doc/Makefile << EOT
+#  This file was created by Configure. Any change made to it will be lost
+#  next time configure is run.
+#
+SHELL = $make_sh
+PERL  = $perl
+TEX   = tex
+PDFTEX= pdftex
+
+DESC          = pari.desc
+MACROS= paricfg.tex parimacro.tex
+USERS_TEX=\$(MACROS) appa.tex users.tex usersch1.tex usersch2.tex usersch3.tex
+PARI_TEX=\$(MACROS) libpari.tex appb.tex appd.tex usersch4.tex usersch5.tex usersch6.tex usersch7.tex
+
+doc all: develop.dvi libpari.dvi parallel.dvi users.dvi refcard.ps tutorial.dvi
+docps: develop.ps libpari.ps parallel.ps refcard.ps tutorial.ps users.ps INSTALL.ps
+docpdf: develop.pdf libpari.pdf parallel.pdf users.pdf tutorial.pdf refcard.pdf INSTALL.pdf
+
+.SUFFIXES: .tex .ipf .pod .inf .dumbpod .3 .html
+
+paricfg.tex: paricfg.tex.in
+	sed -e 's/@version@/$version.$patch/'\\
+	    -e 's!@includedir@!$includedir!'\\
+	    -e 's!@libdir@!$libdir!'\\
+	    -e 's!@datadir@!$datadir!' paricfg.tex.in > \$@
+
+%.pdf: %.tex \$(MACROS)
+	\$(PDFTEX) \$<
+
+%.dvi: %.tex \$(MACROS)
+	\$(TEX) \$<
+
+libpari.pdf: \$(PARI_TEX) \$(MACROS)
+	-rm -f libpari.std
+	\$(PDFTEX) libpari
+	\$(PDFTEX) libpari
+	make libpari.std; \$(PDFTEX) libpari; rm -f libpari.std
+libpari.dvi: \$(PARI_TEX) \$(MACROS)
+	-rm -f libpari.std
+	\$(TEX) libpari
+	\$(TEX) libpari
+	make libpari.std; \$(TEX) libpari; rm -f libpari.std
+
+develop.pdf: develop.tex \$(MACROS)
+	-rm -f develop.std
+	\$(PDFTEX) develop
+	\$(PDFTEX) develop
+	make develop.std; \$(PDFTEX) develop; rm -f develop.std
+develop.dvi: develop.tex \$(MACROS)
+	-rm -f develop.std
+	\$(TEX) develop
+	\$(TEX) develop
+	make develop.std; \$(TEX) develop; rm -f develop.std
+
+parallel.pdf: parallel.tex \$(MACROS)
+	-rm -f parallel.std
+	\$(PDFTEX) parallel
+	\$(PDFTEX) parallel
+	make parallel.std; \$(PDFTEX) parallel; rm -f parallel.std
+parallel.dvi: parallel.tex \$(MACROS)
+	-rm -f parallel.std
+	\$(TEX) parallel
+	\$(TEX) parallel
+	make parallel.std; \$(TEX) parallel; rm -f parallel.std
+
+tutorial.pdf: tutorial.tex \$(MACROS)
+	-rm -f tutorial.std
+	\$(PDFTEX) tutorial
+	\$(PDFTEX) tutorial
+tutorial.dvi: tutorial.tex \$(MACROS)
+	-rm -f tutorial.std
+	\$(TEX) tutorial
+	\$(TEX) tutorial
+
+%.ps: %.dvi
+	dvips \$< -o \$@
+
+refcard.ps: refcard.dvi
+	dvips refcard.dvi -o \$@
+
+%.std: %.idx
+	sed -e 's/!\([1-9]\)!/!00\1!/'\\
+	    -e 's/!\([1-9][0-9]\)!/!0\1!/'\\
+	    -e 's/\\\\EFF {}/f/g'\\
+	  \$< | env LANG=C sort -f |\\
+	sed -e 's/!00*\([0-9]*\)!/!\1!/' > \$@;
+
+users.dvi: \$(USERS_TEX)
+	-rm -f users.std
+	\$(TEX) users
+	\$(TEX) users
+	make users.std; \$(TEX) users; rm -f users.std
+
+users.pdf: \$(USERS_TEX)
+	-rm -f users.std
+	\$(PDFTEX) users
+	\$(PDFTEX) users
+	make users.std; \$(PDFTEX) users; rm -f users.std
+
+gpman: gp.1
+	nroff -man gp.1 | unix2dos -ascii > gp.man
+
+clean:
+	rm -f *.log *.dvi *.idx *.ps *.pdf *.aux *.toc *.std *.dumbpod *.3 *.tmp *.html
+
+cleandoc: clean
+
+veryclean: clean
+	rm -f paricfg.tex gp.man
+
+.tex.pod:
+	\$(PERL) gphelp -to_pod \$*.tex > tmp_pod && mv tmp_pod \$*.pod
+
+.tex.dumbpod:
+	\$(PERL) gphelp -to_dumb_pod \$*.tex > tmp_pod && mv tmp_pod \$*.dumbpod
+
+.ipf.inf:
+	ipfc /INF \$*.ipf
+
+# This is for testing:
+.pod.ipf:
+	pod2ipf \$*.pod > tmp_ipf && mv tmp_ipf \$*.ipf
+
+pari.ipf:  refcard.pod tutorial.pod usersch1.pod usersch1.pod usersch2.pod usersch3.pod appa.pod
+	pod2ipf --title="PARI/GP Manual" --by-files --section-name="Getting started" --file=refcard.pod --file=tutorial.pod --section-name="User guide" --file=usersch1.pod --file=usersch2.pod --file=usersch3.pod --section-name=Appendices --file=appa.pod > tmp_ipf && mv tmp_ipf pari.ipf
+
+dumbpod: appa.dumbpod usersch1.dumbpod usersch2.dumbpod usersch3.dumbpod
+
+.dumbpod.html:
+	pod2html --title="PARI/GP Manual, part \$*" --infile=\$*.dumbpod --outfile=tmp_html && mv tmp_html \$*.html
+
+manpages: dumbpod appa.3 usersch1.3 usersch2.3 usersch3.3
+
+html: dumbpod appa.html appb.html appd.html usersch1.html usersch2.html usersch3.html usersch4.html usersch5.html usersch6.html usersch7.html
+
+.dumbpod.3:
+	pod2man --section=3 --center="PARI/GP Manual, part \$*" --release=$version.$patch --lax \$*.dumbpod > tmp_man && mv tmp_man \$*.3
+EOT
+
+if test -n "$add_funclist"; then
+  cat >> doc/Makefile << EOT
+
+$src/funclist::
+	@-$cfg/genfunclist $desc
+
+$desc/\$(DESC): $src/funclist $desc/merge_822 $desc/PARI/822.pm
+	cd $desc && \$(PERL) merge_822 ../funclist > def\$(TMPSUF)
+	mv $desc/def\$(TMPSUF) \$@
+
+usersch3.tex: $desc/\$(DESC) ../src/desc/doc_make usersFUNCS.tex
+	cd ../src/desc && \$(PERL) doc_make > ../../doc/usersch3.tex
+EOT
+fi
diff --git a/config/GEN_Make.SH b/config/GEN_Make.SH
new file mode 100644
index 0000000..a70ce22
--- /dev/null
+++ b/config/GEN_Make.SH
@@ -0,0 +1,101 @@
+dir=examples
+name=Makefile.$osname-$arch
+file=$dir/$name
+lnfile=Makefile
+
+echo Extracting $file
+rm -f $file $dir/$lnfile
+
+if test -z "$DLLD"; then static=y; fi
+case "$static" in
+  n) dft=dyn ;;
+  y) dft=sta ;;
+esac
+
+RUNPTH=
+if test -n "$runpathprefix"; then
+  RUNPTH=`config/ldflags "$LDneedsWl" "$runpathprefix $runpath"`
+fi
+
+cat > $file << EOT
+# Generic Makefile for PARI programs -- $pretty
+#
+#  This file was created by Configure. Any change made to it will be
+#  lost when Configure is run.
+#
+# make all will create
+#  extgcd-dyn (linked dynamically with libpari)
+#  extgcd-sta (linked statically)
+#  libextgcd.so (to be used by "install" under GP)
+#
+# Under GP: install("extgcd", "GG&&", "gcdex", "./libextgcd.so") enables
+# you to subsequently use gcdex to call extgcd (see the reference manual).
+#
+
+# change this TARGET to compile your own programs
+TARGET = extgcd
+SHELL  = $make_sh
+
+DBGFLAGS   = $DBGFLAGS
+CFLAGS     = $OPTFLAGS
+EXTRACFLAGS=
+#CFLAGS    = \$(DBGFLAGS)
+
+# Various linkers use different flags to force static compilation. Choose
+# the one which is relevant for your installation.
+#
+# Solaris ld (global)
+#STATIC    = -dn
+
+# Solaris ld (toggle: no shared object accepted until -B dynamic is seen
+#STATIC    = -B static
+
+# gcc
+#STATIC    = -static
+
+CC         = $CC
+CPPFLAGS   = -I. -I$includedir
+LD         = $LD
+LDFLAGS    = $LDFLAGS
+MODLD      = $MODLD
+MODLDFLAGS = $MODLDFLAGS
+EXTRAMODLDFLAGS = $EXTRAMODLDFLAGS
+EXTRALIBS  =
+
+RUNPTH     = $RUNPTH
+DLCFLAGS   = $DLCFLAGS
+LIBS       = $LIBS -L$libdir -lpari
+
+RM = rm -f
+
+
+OBJS = \$(TARGET).o
+DYN = lib\$(TARGET).$DLSUFFIX
+ALL = \$(TARGET)-sta \$(TARGET)-dyn \$(DYN)
+
+dft: \$(TARGET)-$dft
+
+all: \$(ALL)
+
+sta: \$(TARGET)-sta
+
+dyn: \$(TARGET)-dyn
+
+dynlib: \$(DYN)
+
+\$(DYN): \$(OBJS)
+	\$(MODLD) -o \$@ \$(MODLDFLAGS) \$(EXTRACFLAGS) \$(OBJS) \$(EXTRAMODLDFLAGS)
+
+\$(TARGET)-sta: \$(OBJS)
+	\$(LD) -o \$@ \$(LDFLAGS) \$(EXTRACFLAGS) \$< \$(EXTRALIBS) \$(STATIC) \$(LIBS)
+
+\$(TARGET)-dyn: \$(OBJS)
+	\$(LD) -o \$@ \$(LDFLAGS) \$(EXTRACFLAGS) \$< \$(EXTRALIBS) \$(RUNPTH) \$(LIBS)
+
+%.o: %.c
+	\$(CC) -c \$(CFLAGS) \$(EXTRACFLAGS) \$(CPPFLAGS) \$(DLCFLAGS) \$<
+clean:
+	-\$(RM) *.o \$(ALL)
+EOT
+
+( cd $dir ; $ln_s $name $lnfile )
diff --git a/config/Imakefile b/config/Imakefile
new file mode 100644
index 0000000..1fdd682
--- /dev/null
+++ b/config/Imakefile
@@ -0,0 +1,2 @@
+gp-X11:
+	@echo 'Xincroot="${INCROOT}"; usrlibdir="${USRLIBDIR}"; libdir="${LIBDIR}";extralib="${EXTRA_LIBRARIES}"'
diff --git a/config/Makefile.SH b/config/Makefile.SH
new file mode 100644
index 0000000..d2193d0
--- /dev/null
+++ b/config/Makefile.SH
@@ -0,0 +1,799 @@
+file=$objdir/Makefile
+
+echo Extracting $file
+rm -f $file
+
+# Path to directories
+cfg=../$config_dir
+data=../$data_dir
+doc=../$doc_dir
+desc=../$desc_dir
+ex=../$examples_dir
+misc=../$misc_dir
+src=../$src_dir
+
+knone=$src/kernel/none
+kern0=$src/kernel/$kernlvl0
+kern1=$src/kernel/$kernlvl1
+
+#
+# File lists
+#
+kernel="mpker mpinl"
+gp=`ls $src_dir/gp/*.c | sed 's,.*/\(.*\)\.c,\1,'`
+language=`ls $src_dir/language/*.c | sed 's,.*/\(.*\)\.c,\1,'`
+basemath=`ls $src_dir/basemath/*.c | sed 's,.*/\(.*\)\.c,\1,'`
+modules=`ls $src_dir/modules/*.c   | sed 's,.*/\(.*\)\.c,\1,'`
+mt="mt $thread_engine"
+
+systems=
+if test -d "$src_dir/systems/$osname"; then
+  systems=`ls $src_dir/systems/$osname/*.c | sed 's,.*/\(.*\)\.c,\1,'`
+fi
+# remove paridecl: no general recompilation when only changing a prototype
+hlist=`ls $src_dir/headers/*.h | grep -v paridecl | grep -v paripriv |\
+       sed 's,.*/\(.*\)\.h,\1,'`
+# for installation put paridecl back. Remove \n otherwise make will choke.
+headerlist="paridecl paripriv `echo $hlist| tr '\n' ' '`"
+
+#Add src/language/parse.c if it is not yet generated
+case $language in *parse*);; *) language="$language parse";; esac
+
+# special systems (OS/2 for now)
+shortlib_prefix=lib
+lib_prefix=lib
+dllib_prefix=lib
+case "$osname" in
+  os2)  shortlib_prefix= # 8.3 filenames
+        dllib_prefix= # != lib_prefix to allow gp-dyn link with DLL
+	export_lib_create=emximp; export_lib='$(DLLIBPARI)$(_A)'
+	export_file=pari.def; export_create="emxexp -u"
+	# Actually, the build will fail until the switch to -Zomf
+	dlld_ignore=- ;;
+  mingw)
+    export_file='$(LIBPARI).def';;
+  darwin)
+    ld_install_name="-Wl,-install_name -Wl,\"$libdir\"/\$(LIBPARI_DYN)";;
+esac
+case "$osname" in
+  mingw|cygwin)
+    install_implib="\$(INSTALL_DATA) \$(LIBPARI_SO)\$(_A) \$(LIBDIR)/\$(LIBPARI_SO)\$(_A)";;
+  *)
+    install_implib="";;
+esac
+
+PLOTCFLAGS=
+PLOTLIBS=
+postconfig=:
+case "$which_graphic_lib" in
+none)
+  graph=plotnull;;
+ps)
+  graph=plotps;;
+Qt)
+  PLOTCFLAGS='-D__FANCY_WIN__ -I$(QTDIR)/include'
+  PLOTLIBS="-L\$(QTDIR)/lib $QTLIB"
+  graph=plotQt;;
+Qt4)
+  PLOTCFLAGS='-D__FANCY_WIN__ -I$(QTDIR)/include'
+  PLOTLIBS="-L\$(QTDIR)/lib $QTLIB"
+  graph=plotQt4;;
+fltk)
+  PLOTCFLAGS="-I\$(FLTKDIR)/include $X11_INC"
+  PLOTLIBS="$FLTK_LIBS"
+  postconfig='fltk-config --post '
+  graph=plotfltk;;
+win32)
+  PLOTLIBS="-lgdi32"
+  graph=plotWin32;;
+esac
+graph="plotport $graph"
+
+plotrunpath=
+case "$which_graphic_lib" in
+  *X11*)
+    PLOTCFLAGS="$PLOTCFLAGS $X11_INC"
+    PLOTLIBS="$PLOTLIBS $X11_LIBS"
+    plotrunpath=$X11
+    graph="plotX $graph"
+  ;;
+esac
+
+KERNOBJS=
+for f in $kernel; do
+  KERNOBJS="$KERNOBJS $f\$(_O)"
+done
+OBJS=$KERNOBJS
+for f in $basemath $language $modules $systems $mt; do
+  OBJS="$OBJS $f\$(_O)"
+done
+OBJSGP=
+for f in $gp $graph; do
+  OBJSGP="$OBJSGP $f\$(_O)"
+done
+HEADERS="mpinl.h parimt.h"
+for f in $hlist; do
+  HEADERS="$HEADERS $src/headers/$f.h";
+done
+
+# runpath
+tmp=$runpath
+for d in "$plotrunpath" "$gmp" "$readline" "/usr/lib"; do
+  case "$d" in
+    ""|yes) ;;
+    *) case "$tmp" in
+        $d|*$dir_sep$d|*$dir_sep$d$dir_sep*);;
+        *) tmp="$tmp$dir_sep$d";;
+       esac ;;
+  esac
+done
+RUNPTH_FINAL=
+RUNPTH=
+if test -n "$runpathprefix"; then
+  RUNPTH_FINAL=`$config_dir/ldflags "$LDneedsWl" $runpathprefix"$tmp"`
+  RUNPTH=`$config_dir/ldflags "$LDneedsWl" $runpathprefix\\\$\(TOPDIR\)/$objdir$dir_sep"$tmp"`
+fi
+
+if test -z "$DLLD"; then
+  exec="gp-sta"; static=y
+else
+  exec="gp-sta gp-dyn"
+fi
+case "$sizeof_long" in
+  4) numbits=32;;
+  8) numbits=64;;
+esac
+dotest="env \"RUNTEST=\$(RUNTEST)\" \"LD_LIBRARY_PATH=.$dir_sep\$\$LD_LIBRARY_PATH\" \$(SHELL) $src/test/dotest $numbits"
+case "$static" in
+  n) dft=dyn; libdft=lib-dyn;;
+  y) dft=sta; libdft= ;;
+esac
+
+CPPFLAGS="-I. -I$src/headers"
+if test "$has_dlopen" = builtin; then
+  CPPFLAGS="$CPPFLAGS -I$src/systems/$osname"
+fi
+
+case "$ln_s" in
+  *cp*) ln_objdir=".";;
+  *)    ln_objdir="$objdir"
+esac
+
+cat > $file << EOT
+# Makefile for Pari/GP -- $pretty
+#
+#  This file was created by Configure. Any change made to it will be
+#  lost when Configure is run.
+#
+TOPDIR="$TOP"
+
+SHELL      = $make_sh
+PERL       = $perl
+BISON      = bison
+AR         = ar
+RANLIB     = ranlib
+
+SIZEOF_LONG= $sizeof_long
+
+CC_FLAVOR  =
+CC         = $CC \$(CC_FLAVOR)
+CPPFLAGS   = $CPPFLAGS
+CFLAGS     = $CFLAGS
+DLCFLAGS   = $DLCFLAGS
+KERNELCFLAGS  = $KERNELCFLAGS
+LD_FLAVOR  = \$(CC_FLAVOR)
+LD         = $LD \$(LD_FLAVOR)
+LDFLAGS    = $LDFLAGS
+DLLD_FLAVOR  = \$(LD_FLAVOR)
+DLLD       = $DLLD \$(DLLD_FLAVOR)
+DLLDFLAGS  = $DLLDFLAGS
+EXTRADLLDFLAGS = $EXTRADLLDFLAGS
+RUNTEST    = $RUNTEST
+
+# HIGHLY EXPERIMENTAL (only tested with gmp-4.0 on ix86 and Ultra).
+# If you've configured and compiled GMP and would like to tune PARI using
+# the nice cycle counting functions in GMP, uncomment the 4 lines below
+# (correct the first one to the path to your gmp source tree).
+#
+#GMP     = /some/directory/gmp-4.0/tune
+#GMPFLAGS= -DGMP_TIMER
+#GMPO1 = \$(GMP)/time.o \$(GMP)/freq.o
+#GMPOBJS=\$(GMPO1) \$(GMPO2)
+#
+#You may need to add a few object files to GMPOBJS. On UltraSparc, uncomment
+#the following line
+#GMPO2 = \$(GMP)/sparcv9.o
+
+_O	   = .o
+_A	   = .a
+LIB_PREFIX = $lib_prefix
+DLLIB_PREFIX = $dllib_prefix
+LIBPARI_BASE = $libpari_base
+LIBPARI      = \$(LIB_PREFIX)pari
+DLLIBPARI    = \$(DLLIB_PREFIX)pari
+LIBPARI_STA  = \$(LIBPARI)\$(_A)
+LIBPARI_SO   = \$(DLLIBPARI).$DLSUFFIX
+SOLIBPARI    = \$(DLLIB_PREFIX)\$(LIBPARI_BASE).$DLSUFFIX
+LIBPARI_DYN   = \$(SOLIBPARI)$sodest
+LIBPARI_SONAME= \$(SOLIBPARI)$soname
+DL_DFLT_NAME = $DL_DFLT_NAME
+
+LD_INSTALL_NAME  = $ld_install_name
+
+EXPORT_FILE   = $export_file
+EXPORT_CREATE = $export_create
+EXPORT_LIB    = $export_lib
+EXPORT_LIB_CREATE = $export_lib_create
+DLLD_IGNORE   = $dlld_ignore
+DLLTOOL = $DLLTOOL
+
+RUNPTH       = $RUNPTH
+RUNPTH_FINAL = $RUNPTH_FINAL
+LDDYN        = $LDDYN
+LIBS         = $LIBS
+GMPLIBS      = $GMPLIBS
+MT_LIBS      = $MT_LIBS
+RT_LIBS      = $RT_LIBS
+DL_LIBS      = $DL_LIBS
+DYN_LIBS     = \$(GMPLIBS) \$(DL_LIBS) \$(RT_LIBS) \$(MT_LIBS) \$(LIBS)
+STA_LIBS     = \$(GMPLIBS) \$(DL_LIBS) \$(RT_LIBS) \$(MT_LIBS) \$(LIBS)
+
+RM = rm -f
+MV = mv -f
+LN = $ln_s
+CP_F = cp -f
+STRIP      = strip
+STRIPFLAGS =
+
+# Change these installation directories to suit your needs.
+# DESTDIR is used to install to a false hierachy (to build a Debian package)
+INCLUDEDIR= "\$(DESTDIR)$includedir"
+LIBDIR    = "\$(DESTDIR)$libdir"
+BINDIR    = "\$(DESTDIR)$bindir"
+MANDIR    = "\$(DESTDIR)$mandir"
+DATADIR   = "\$(DESTDIR)$datadir"
+SYSDATADIR= "\$(DESTDIR)$sysdatadir"
+
+EXDIR     = \$(DATADIR)/examples
+MISCDIR   = \$(DATADIR)/misc
+DOCDIR    = \$(DATADIR)/doc
+
+INSTALL = $cfg/install
+INSTALL_PROGRAM = \$(INSTALL)
+INSTALL_DATA = \$(INSTALL) -m 644
+
+# Readline
+RLINCLUDE = $RLINCLUDE
+RLLIBS    = $RLLIBS
+# GMP
+GMPINCLUDE = $GMPINCLUDE
+# Graphic library.
+QTDIR      = "$QTDIR"
+MOC        = \$(QTDIR)/bin/moc
+PLOTCFLAGS = $PLOTCFLAGS
+PLOTLIBS   = $PLOTLIBS
+CPLUSPLUS  = g++
+
+FLTKDIR    = "$FLTKDIR"
+
+TOPLDDYN   = "$TOP/$objdir"
+# Description system
+DESC          = pari.desc
+DESC_HELP_GEN = $desc/gen_proto $desc/PARI/822.pm
+TMPSUF        = $osname-$arch.tmp
+
+DOTEST=$dotest
+
+OBJS   = $OBJS
+OBJSGP = $OBJSGP
+
+.PHONY: gp dft clean
+
+gp: gp-$dft ../gp$suffix$exe_suff $libdft
+
+../gp$suffix$exe_suff: gp-$dft
+	-\$(RM) \$@
+	-\$(LN) $ln_objdir/gp-$dft \$@
+
+all: $exec lib-sta
+
+lib-sta: \$(LIBPARI_STA)
+lib-dyn: \$(LIBPARI_DYN)
+
+\$(LIBPARI_STA): $add_funclist \$(OBJS)
+	-\$(RM) \$@
+	\$(AR) r \$@ \$(OBJS)
+	-\$(RANLIB) \$@
+
+kerntest\$(_O): $src/test/kerntest.c
+	\$(CC) -c -I$src/language \$(CPPFLAGS) \$(CFLAGS) -o \$@ $src/test/kerntest.c
+
+dummy\$(_O): $src/test/dummy.c
+	\$(CC) -c \$(CPPFLAGS) \$(CFLAGS) -o \$@ $src/test/dummy.c
+
+kerntest: $KERNOBJS dummy\$(_O) kerntest\$(_O)
+	\$(CC) \$(CPPFLAGS) \$(CFLAGS) -o \$@ $KERNOBJS dummy\$(_O) kerntest\$(_O) \$(STA_LIBS)
+
+mpinl\$(_O): .headers parilvl0.h parilvl1.h $knone/mpinl.c
+	\$(CC) -c \$(CPPFLAGS) \$(CFLAGS) \$(DLCFLAGS) -o mpinl\$(_O) $knone/mpinl.c
+
+test-kernel:: kerntest
+	@./kerntest > gp.out;\
+	diff -c gp.out $src/test/$numbits/kernel > kern.dif;\
+	if test -s kern.dif; then echo "KERNEL BUG"; else echo OK; fi
+
+tune.o: $src/test/tune.c
+	\$(CC) \$(GMPFLAGS) \$(CPPFLAGS) \$(CFLAGS) -o \$@ \$< -c
+
+tune: mpinl.h tune-sta
+	-\$(RM) tune ../tune
+	-\$(LN) tune-sta tune
+	-\$(LN) $ln_objdir/tune ../tune
+
+tune-dyn: tune.o \$(LIBPARI_DYN)
+	\$(LD) -L. \$(LDFLAGS) \$(RUNPTH) -o \$@ \$< \$(GMPOBJS) \$(LDDYN) \$(STA_LIBS)
+tune-sta: tune.o \$(LIBPARI_STA)
+	\$(LD) \$(LDFLAGS) \$(RUNPTH) -o \$@ \$< \$(GMPOBJS) ./\$(LIBPARI_STA) \$(STA_LIBS)
+
+gp-sta: $add_funclist \$(OBJS) \$(OBJSGP)
+	\$(RM) \$@
+	\$(LD) -o \$@ \$(LDFLAGS) \$(OBJS) \$(OBJSGP) \$(RUNPTH) \$(RLLIBS) \$(PLOTLIBS) \$(STA_LIBS)
+	$postconfig gp-sta
+
+cleantest:
+	\$(RM) *.dif gp.out io-testfile pari.ps
+cleanobj:
+	-\$(RM) *\$(_O) mpker.c *.s parimt.h mpinl.h parilvl0.h parilvl1.h libpari* $exec kerntest
+	-\$(RM) gmon.out
+	-\$(RM) *.gcno *.gcda
+cleandesc:
+	-\$(RM) $desc/\$(DESC) *\$(TMPSUF)
+cleantune:
+	-\$(RM) tune tune-sta tune.o
+
+cleanall: cleanobj cleantune cleantest cleandesc
+
+clean: cleanall
+
+
+# Use this version to avoid problems with NFS and slightly out of synch
+# fileserver/host. We are recompiling everything anyway. Not on by default:
+# 3s is slower than the whole compilation on our development server :-)
+# .headers: $HEADERS
+#	@sleep 3; touch \$@
+.headers: $HEADERS
+	@touch \$@
+
+install-nodata: install-lib-$dft install-include install-bin install-man install-misc install-doc install-examples install-cfg
+
+install: install-nodata install-data
+
+install-include:
+	-mkdir -p \$(INCLUDEDIR)/pari
+	-for i in paricfg.h mpinl.h parimt.h; do \\
+	  \$(INSTALL_DATA) \$\$i \$(INCLUDEDIR)/pari; done
+	-for i in $headerlist; do \\
+	   \$(INSTALL_DATA) $src/headers/\$\$i.h  \$(INCLUDEDIR)/pari; done
+	-\$(RM) \$(INCLUDEDIR)/pari/genpari.h
+	-\$(LN) pari.h \$(INCLUDEDIR)/pari/genpari.h
+
+install-bin: install-bin-$dft
+
+install-bin-dyn: gp-dyn install-lib-dyn
+	-mkdir -p \$(BINDIR)
+	-\$(RM) \$(BINDIR)/gp-$version$exe_suff \$(BINDIR)/gp$exe_suff
+	\$(LD) -o \$(BINDIR)/gp-$version$exe_suff -L\$(LIBDIR) \$(LDFLAGS) \$(OBJSGP) \$(RUNPTH_FINAL) \$(LDDYN) \$(RLLIBS) \$(PLOTLIBS) \$(LIBS)
+	-\$(STRIP) \$(STRIPFLAGS) \$(BINDIR)/gp-$version$exe_suff
+	-cd \$(BINDIR); $postconfig gp-$version$exe_suff
+	-\$(LN) gp-$version$exe_suff \$(BINDIR)/gp$exe_suff
+
+install-bin-sta: gp-sta
+	-mkdir -p \$(BINDIR)
+	-\$(RM) \$(BINDIR)/gp-$version$exe_suff \$(BINDIR)/gp$exe_suff
+	\$(INSTALL_PROGRAM) gp-sta \$(BINDIR)/gp-$version$exe_suff
+	-\$(LN) gp-$version$exe_suff \$(BINDIR)/gp$exe_suff
+# Can't strip it if we want install() to work on OSF.
+#	-\$(STRIP) \$(STRIPFLAGS)  \$(BINDIR)/gp-$version
+	-cd \$(BINDIR); $postconfig gp-$version$exe_suff
+
+install-man::
+	-mkdir -p \$(MANDIR)
+	-\$(RM) \$(MANDIR)/pari.1 \$(MANDIR)/gp.1 \$(MANDIR)/gp-$version.1
+	\$(INSTALL_DATA) $doc/gphelp.1 \$(MANDIR)
+	\$(INSTALL_DATA) $doc/gp.1 \$(MANDIR)/gp-$version.1
+	-\$(LN) gp.1 \$(MANDIR)/pari.1
+	-\$(LN) gp-$version.1 \$(MANDIR)/gp.1
+
+install-misc:
+	-mkdir -p \$(MISCDIR) \$(BINDIR)
+	\$(INSTALL_PROGRAM) $misc/tex2mail \$(BINDIR)
+	\$(INSTALL_DATA) $doc/tex2mail.1 \$(MANDIR)
+	\$(INSTALL_DATA) $misc/README    \$(MISCDIR)
+	\$(INSTALL_DATA) $misc/color.dft \$(MISCDIR)
+	\$(INSTALL_DATA) $misc/gpalias   \$(MISCDIR)
+	\$(INSTALL_PROGRAM) $misc/gpflog \$(MISCDIR)
+	\$(INSTALL_DATA) $misc/gprc.dft  \$(MISCDIR)
+	\$(INSTALL_PROGRAM) $misc/xgp    \$(MISCDIR)
+
+install-cfg::
+	-mkdir -p \$(SYSDATADIR)
+	-\$(INSTALL_DATA) pari.cfg \$(SYSDATADIR)
+	-if test -n "$add_funclist"; then\
+	   mkdir -p \$(DATADIR)/PARI;\
+	   \$(INSTALL_DATA) $desc/PARI/822.pm \$(DATADIR)/PARI;\
+	   \$(INSTALL_DATA) $desc/\$(DESC) \$(DATADIR); fi
+
+install-doc::
+	-mkdir -p \$(BINDIR) \$(DOCDIR)
+	-cd $doc; \$(MAKE) all
+	-\$(INSTALL_PROGRAM) $doc/gphelp    \$(BINDIR)
+	-\$(INSTALL_DATA) $doc/translations \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/appa.tex     \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/appb.tex     \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/appd.tex     \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/parimacro.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/pdfmacs.tex  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/develop.tex  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/refcard.tex  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/tutorial.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/users.tex    \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/usersch1.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/usersch2.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/usersch3.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/usersch4.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/usersch5.tex \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/paricfg.tex  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/develop.dvi  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/libpari.dvi  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/users.dvi    \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/tutorial.dvi \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/refcard.dvi  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/refcard.ps   \$(DOCDIR)
+
+install-docpdf::
+	-mkdir -p \$(BINDIR) \$(DOCDIR)
+	-cd $doc; \$(MAKE) docpdf
+	-\$(INSTALL_DATA) $doc/libpari.pdf  \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/users.pdf    \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/tutorial.pdf \$(DOCDIR)
+	-\$(INSTALL_DATA) $doc/refcard.pdf  \$(DOCDIR)
+
+install-examples:
+	-mkdir -p \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/EXPLAIN     \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/Inputrc     \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/Makefile    \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/bench.gp    \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/cl.gp       \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/classno.gp  \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/contfrac.gp \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/lucas.gp    \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/extgcd.c    \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/rho.gp      \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/squfof.gp   \$(EXDIR)
+	-\$(INSTALL_DATA) $ex/taylor.gp   \$(EXDIR)
+
+install-data:
+	-if test -d $data; then cd $data; \
+	   for d in \`ls\`; do \
+	     mkdir -p \$(DATADIR)/\$\$d && \
+	     for f in \`ls \$\$d\`; do \
+	       \$(INSTALL_DATA) \$\$d/\$\$f \$(DATADIR)/\$\$d; \
+	     done >/dev/null;\
+	   done; \
+	 fi
+
+install-lib-sta: \$(LIBPARI_STA)
+	-mkdir -p \$(LIBDIR)
+	-\$(RM) \$(LIBDIR)/\$(LIBPARI_STA)
+	\$(INSTALL_DATA) \$(LIBPARI_STA) \$(LIBDIR)/\$(LIBPARI_STA)
+
+install-lib-dyn-base:
+	-mkdir -p \$(LIBDIR)
+	-\$(RM) \$(LIBDIR)/\$(LIBPARI_DYN) \$(LIBDIR)/\$(LIBPARI_SONAME) \$(LIBDIR)/\$(LIBPARI_SO)
+	\$(DLLD_IGNORE)\$(DLLD) -o \$(LIBDIR)/\$(LIBPARI_DYN) \$(DLLDFLAGS) \$(OBJS) \$(EXTRADLLDFLAGS) \$(DYN_LIBS) \$(EXPORT_FILE) \$(LD_INSTALL_NAME)
+	$install_implib
+
+install-lib-dyn-link: install-lib-dyn-base
+	-if test "\$(LIBPARI_DYN)" != "\$(LIBDIR)/\$(LIBPARI_SO)"; then \
+          \$(LN) \$(LIBPARI_DYN) \$(LIBDIR)/\$(LIBPARI_SO); fi
+	-if test "\$(LIBPARI_SONAME)" != "\$(LIBPARI_SO)"; then \
+	  \$(LN) \$(LIBPARI_DYN) \$(LIBDIR)/\$(LIBPARI_SONAME); fi
+install-lib-dyn: \$(LIBPARI_DYN) install-lib-dyn-link
+
+nsis: gp
+	-cd $doc; \$(MAKE) docpdf
+	makensis pari.nsi
+
+parimt.h: $src/mt/$thread_engine.h
+	cat $src/mt/$thread_engine.h > parimt.h
+
+mpinl.h: parilvl0.h parilvl1.h
+	cat parilvl0.h parilvl1.h > mpinl.h
+
+bench: $exec
+	@\$(DOTEST) $test_basic
+dobench::
+	@\$(DOTEST) $test_basic
+test-all: $exec
+	@\$(DOTEST) $test_extra
+dotest-all::
+	@\$(DOTEST) $test_extra
+dyntest-all: gp-dyn
+	@env dotestSUF=dyn make test-all
+statest-all: gp-sta
+	@env dotestSUF=sta make test-all
+
+dotest-env::
+	@export AAA=XXX BBB=YYY; \$(DOTEST) env
+EOT
+for i in $test_extra $test_extra_out $test_extra_OUT; do
+  echo "test-$i: $exec dotest-$i" >>$file
+done
+for i in $test_extra $test_extra_out; do
+  cat >> $file << EOT
+dotest-$i::
+	@\$(DOTEST) $i
+EOT
+done
+
+if test "$optimization" = gcov; then
+  cat >> $file << EOT
+
+.PHONY: lcov-report lcov-reset
+LCOV_TRACE = lcov.info
+LCOV_REPORT= lcov-report
+LCOV_FLAGS=
+GENHTML_FLAGS=
+LCOV_TITLE="PARI/GP v$version.$patch lcov report ($status)"
+
+lcov-report:
+	\$(RM) \$(LCOV_TRACE)
+	rm -rf \$(LCOV_REPORT)
+	lcov -c \$(LCOV_FLAGS) -d . -b . -o \$(LCOV_TRACE)
+	genhtml \$(GENHTML_FLAGS) --legend -t \$(LCOV_TITLE) -o \$(LCOV_REPORT) \$(LCOV_TRACE)
+
+lcov-reset:
+	\$(RM) *.gcda
+EOT
+fi
+
+if test -z "$DLLD"; then
+  cat >> $file << EOT
+
+\$(LIBPARI_DYN)::
+	@echo "Configure could not find a way to build a shared library on this machine"
+
+EOT
+else
+  if test -n "$export_file"; then
+    case "$osname" in
+    os2)
+    cat >> $file << EOT
+
+EXPORT_FILE_BASE = $src/systems/os2/pari.def.base
+VERSION_VERBOSE = $pari_release_verbose
+
+\$(EXPORT_FILE): \$(OBJS) \$(EXPORT_FILE_BASE)
+	cat \$(EXPORT_FILE_BASE) | sed 's/<DLL_BASE>/\$(DLLIBPARI)/' | sed 's/<VENDOR>/pari.math.u-bordeaux.fr\//' | sed 's/<VERSION>/\$(VERSION_VERBOSE)/' | sed 's/<DESCR>/GP\/PARI compiled with \$(CFLAGS)/' > \$@
+	\$(EXPORT_CREATE) \$(OBJS) >> \$@
+
+\$(DLLIBPARI)\$(_A): \$(EXPORT_FILE)
+	\$(EXPORT_LIB_CREATE) -o \$@ \$(EXPORT_FILE)
+
+EOT
+    ;;
+    mingw)
+    cat >> $file << EOT
+
+\$(EXPORT_FILE): \$(OBJS)
+	\$(DLLTOOL) --export-all-symbols -k -z \$@.tmp \$(OBJS)
+	echo "LIBRARY \$(LIBPARI)" > \$@ && cat \$@.tmp >> \$@ && rm \$@.tmp
+EOT
+    ;;
+    esac
+  fi
+  cat >> $file << EOT
+
+gp-dyn: $add_funclist \$(OBJSGP) \$(LIBPARI_DYN) \$(EXPORT_LIB)
+	\$(RM) \$@
+	\$(LD) -o \$@ -L\$(TOPLDDYN) \$(LDFLAGS) \$(OBJSGP) \$(RUNPTH) \$(RLLIBS) \$(LDDYN) \$(PLOTLIBS) \$(LIBS)
+	$postconfig gp-dyn
+
+\$(LIBPARI_DYN): $add_funclist \$(OBJS) \$(EXPORT_FILE)
+	-\$(RM) \$(LIBPARI_DYN)
+	\$(DLLD_IGNORE)\$(DLLD) -o \$(TOPLDDYN)/\$(LIBPARI_DYN) \$(DLLDFLAGS) \$(OBJS) \$(EXTRADLLDFLAGS) \$(DYN_LIBS) \$(EXPORT_FILE)
+	-if test "\$(LIBPARI_DYN)" != "\$(LIBPARI_SO)"; then \
+	  \$(RM) \$(LIBPARI_SO);\
+	  \$(LN) \$(LIBPARI_DYN) \$(LIBPARI_SO); fi
+	-if test "\$(LIBPARI_DYN)" != "\$(LIBPARI_SONAME)"; then \
+	  \$(RM) \$(LIBPARI_SONAME);\
+	  \$(LN) \$(LIBPARI_DYN) \$(LIBPARI_SONAME); fi
+EOT
+fi
+
+cat >> $file << EOT
+$src/language/parse.h: $src/language/parse.y
+	\$(BISON) -d $src/language/parse.y -o $src/language/parse.c
+$src/language/parse.c: $src/language/parse.h
+	@:
+EOT
+
+if test -n "$add_funclist"; then
+# files generated using external scripts
+HUGELINE="
+$src/funclist::
+	@-$cfg/genfunclist $desc"
+suffix='$$$$-$(TMPSUF)'
+list="funclist whatnow init default gp_default gp_init highlvl"
+for name in $list; do
+  case $name in
+  funclist)
+    target="$desc/\$(DESC)"
+    depend="$src/funclist $desc/merge_822 $desc/PARI/822.pm"
+    script="merge_822 ../funclist"
+  ;;
+  whatnow)
+    target=$src/gp/whatnow.h
+    depend="$desc/whatnow $src/whatnow"
+    script=whatnow
+  ;;
+  init)
+    target=$src/language/init.h
+    depend="$desc/\$(DESC) \$(DESC_HELP_GEN)"
+    script="gen_proto basic \$(DESC)"
+  ;;
+  default)
+    target=$src/language/default.h
+    depend="$desc/\$(DESC) \$(DESC_HELP_GEN)"
+    script="gen_proto default \$(DESC)"
+  ;;
+  gp_default)
+    target=$src/gp/gp_default.h
+    depend="$desc/\$(DESC) \$(DESC_HELP_GEN)"
+    script="gen_proto gp_default \$(DESC)"
+  ;;
+  gp_init)
+    target=$src/gp/gp_init.h
+    depend="$desc/\$(DESC) \$(DESC_HELP_GEN)"
+    script="gen_proto gp \$(DESC)"
+  ;;
+  highlvl)
+    target=$src/gp/highlvl.h
+    depend="$desc/\$(DESC) \$(DESC_HELP_GEN)"
+    script="gen_proto highlevel \$(DESC)"
+  ;;
+  esac
+HUGELINE="$HUGELINE
+$target: $depend
+	f=$name-$suffix; (cd $desc && \$(PERL) $script > \$\$f) && mv $desc/\$\$f \$@"
+done
+echo "$HUGELINE" >> $file
+
+fi
+
+# Level 0
+f=$src_dir/kernel/$kernlvl0/MakeLVL0.SH
+if test -s $f; then
+  . $f
+else
+  cat >> $file <<EOT
+L0MODS=$kern0/asm0.h $knone/addll.h $knone/mulll.h $knone/bfffo.h $knone/divll.h
+parilvl0.h: \$(L0MODS)
+	$cfg/genkernel $kern0/asm0.h > parilvl0.h
+EOT
+fi
+
+if test -f "$TOP/.git/index"; then
+  vcfile="../.git/index"
+  cat >> $file <<EOT
+paricfg.h: $vcfile $cfg/version
+	-sh $cfg/setversion
+EOT
+fi
+
+# Level 1
+f=$src_dir/kernel/$kernlvl1/MakeLVL1.SH
+if test -s $f; then . $f; fi
+
+ANAL_H=$src/language/anal.h
+RECT_H=$src/graph/rect.h
+HUGELINE=
+for dir in basemath modules language gp graph systems mt; do
+  eval list='$'$dir
+  case "$dir" in
+    systems)  SRC=$src/$dir/$osname;;
+    *)        SRC=$src/$dir;;
+  esac
+  for f in $list; do
+
+  source="$SRC/$f.c"
+  f=`basename $f`
+  depend=
+  cflags="\$(CPPFLAGS)"
+  compile='$(CC)'
+  case "$f" in
+  gp)
+    cflags="$cflags -I$src/language -DDL_DFLT_NAME=\$(DL_DFLT_NAME)"
+    depend="$src/gp/gp.h"
+    ;;
+  gp_rl)
+    cflags="$cflags -I$src/language \$(RLINCLUDE)"
+    depend="$src/gp/gp.h"
+    ;;
+  gp_init)
+    cflags="$cflags -I$src/graph"
+    depend="$RECT_H $src/gp/gp_init.h $src/gp/gp_default.h"
+    ;;
+  default)
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  paricfg)
+    depend="./paricfg.h"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  plotport)
+    cflags="$cflags -I$src/graph"
+    depend="$RECT_H"
+    ;;
+  highlvl)
+    depend="$RECT_H $src/gp/highlvl.h"
+    ;;
+  plotQt)
+    cflags="$cflags \$(PLOTCFLAGS)"
+    depend="$RECT_H"
+    compile="\$(MOC) -o plotQt.moc.cpp $src/graph/plotQt.c && \$(CPLUSPLUS)"
+    ;;
+  plotQt4)
+    cflags="$cflags \$(PLOTCFLAGS)"
+    depend="$RECT_H"
+    compile="\$(MOC) \$(PLOTCFLAGS) -o plotQt4.moc.cpp $src/graph/plotQt4.c && \$(CPLUSPLUS)"
+    ;;
+  plotfltk)
+    cflags="$cflags \$(PLOTCFLAGS)"
+    depend="$RECT_H"
+    compile="\$(CPLUSPLUS)"
+    ;;
+  plot*)
+    cflags="$cflags \$(PLOTCFLAGS)"
+    depend="$RECT_H"
+    ;;
+  es|sumiter|intnum)
+    depend="$ANAL_H"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  whatnow)
+    depend="$src/gp/whatnow.h"
+    ;;
+  init)
+    depend="$ANAL_H $src/language/init.h $src/language/default.h"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  anal)
+    depend="$ANAL_H $src/language/parse.h"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  parse)
+    depend="$ANAL_H $src/language/parse.h $src/language/parsec.h $src/language/tree.h"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  compile)
+    depend="$ANAL_H $src/language/tree.h $src/language/opcode.h"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  eval)
+    depend="$ANAL_H $src/language/opcode.h"
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  *)
+    cflags="$cflags \$(DLCFLAGS)"
+    ;;
+  esac
+cflags="$cflags \$(CFLAGS)"
+HUGELINE="$HUGELINE
+$f\$(_O): .headers $depend $source
+	$compile -c $cflags -o $f\$(_O) $source"
+  done
+done
+# avoid execing too many "cat". MS-DOS can't handle it
+echo "$HUGELINE" >> $file
diff --git a/config/TOP_Make.SH b/config/TOP_Make.SH
new file mode 100644
index 0000000..11533b4
--- /dev/null
+++ b/config/TOP_Make.SH
@@ -0,0 +1,125 @@
+file=Makefile
+echo "Extracting $file"
+rm -f $file
+
+dosversion=`echo $version|sed -e 's/\.//g'`
+dosversion="_$dosversion$patch"
+__status__=$status
+case "$status" in
+  development*) __status__='snapshot';;
+esac
+
+cat > $file << EOT
+# This file was created by Configure. All changes made will be lost
+# next time Configure is run.
+#
+SHELL = $make_sh
+VERS = pari-$version.$patch $__status__
+TAG=release-$VersionMajor-$VersionMinor-$patch
+
+dft target::
+	@echo "Main targets: we suggest 'make all', then 'make install' as root"
+	@echo "    all                    Compilation + Documentation"
+	@echo "    gp                     Compilation"
+	@echo "    bench                  Compilation + Quick test"
+	@echo "    dobench                Quick test only"
+	@echo "    doc                    Documentation only"
+	@echo "    install                Installation"
+	@echo "    clean, cleantest       Clean up"
+	@echo "For pari maintainers:"
+	@echo "    dbg                    Compile gp binary suitable for debugging"
+	@echo "    prf                    Compile gp binary suitable for profiling"
+	@echo "    gcov                   Compile gp binary for test coverage reporting"
+	@echo "    alpha, beta, release   Tarfile for official source distribution"
+	@echo "    snapshot, distrib      Tarfile for source snapshot"
+	@echo "    nsis                   Create a NSIS installer for win32"
+	@echo "    ctags                  Generate VI/VIM tags file in ./src"
+	@echo "    etags                  Generate Emacs  tags file in ./src"
+	@echo "    tune                   Generate tuning utility"
+	@echo "    test-all               Thorough regression tests (slow)"
+
+all::
+	@\$(MAKE) gp
+	@-cd doc && \$(MAKE) doc
+
+gp bench test-kernel test-all install cleanall cleanobj cleantest nsis install-bin install-doc install-docpdf install-nodata install-data install-lib-sta install-bin-sta dobench dyntest-all statest-all tune $top_test_extra $top_dotest_extra::
+	@dir=\`config/objdir\`; echo "Making \$@ in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) \$@
+
+dbg gp.dbg::
+	@dir=\`config/objdir\`.dbg; echo "Making gp in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure -g first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) gp
+
+prf gp.prf::
+	@dir=\`config/objdir\`.prf; echo "Making gp in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure -pg first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) gp
+
+gcov gp.gcov::
+	@dir=\`config/objdir\`.gcov; echo "Making gp in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure -gcov first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) gp
+
+doc docps docpdf gpman cleandoc::
+	cd doc && \$(MAKE) \$@
+
+clean:: cleandoc cleanall
+
+clean.dbg::
+	@dir=\`config/objdir\`.dbg; echo "Making clean in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Nothing to be done"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) clean
+
+clean.prf::
+	@dir=\`config/objdir\`.prf; echo "Making clean in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Nothing to be done"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) clean
+
+clean.gcov::
+	@dir=\`config/objdir\`.gcov; echo "Making clean in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Nothing to be done"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) clean
+
+bench.dbg::
+	@dir=\`config/objdir\`.dbg; echo "Making bench in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure -g first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) bench
+
+bench.prf::
+	@dir=\`config/objdir\`.prf; echo "Making bench in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure -pg first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) bench
+
+bench.gcov::
+	@dir=\`config/objdir\`.gcov; echo "Making bench in \$\$dir";\\
+	 if test ! -d \$\$dir; then echo "Please run Configure -gcov first!"; exit 1; fi;\\
+	cd \$\$dir && \$(MAKE) bench
+
+distrib:
+	$config_dir/settar \$(VERS) $__status__
+
+alpha:
+	$config_dir/settar \$(VERS) alpha
+beta:
+	$config_dir/settar \$(VERS) beta
+release:
+	$config_dir/settar \$(VERS) released
+snapshot:
+	$config_dir/settar \$(VERS) snapshot
+
+dosdistrib: gpman
+	-zip -kr GPB${dosversion}.ZIP GP.EXE README README.DOS ../EMX/README.DOC ../RSX/README.TXT
+	-zip -k GPD${dosversion}.ZIP doc/*.tex doc/gphelp doc/gp.man examples misc/gprc* misc/gpalias
+
+etags:
+	config/make_tags --emacs "$TOP/src"
+
+ctags:
+	config/make_tags --vi "$TOP/src"
+
+checkspaces:
+	config/checkspaces
+
+EOT
diff --git a/config/ansi.c b/config/ansi.c
new file mode 100644
index 0000000..8668330
--- /dev/null
+++ b/config/ansi.c
@@ -0,0 +1 @@
+int main(int argc, char **argv){ return 0; }
diff --git a/config/arch-osname b/config/arch-osname
new file mode 100755
index 0000000..5da1bc8
--- /dev/null
+++ b/config/arch-osname
@@ -0,0 +1,79 @@
+#! /bin/sh
+arch=none; osname=unknown
+myuname=`(uname -a) 2>/dev/null || arch 2>&1`
+if test -d /NextApps; then myuname=nextstep; fi
+if test -n "$myuname"; then
+  myuname=`echo $myuname | sed -e 's/^[^=]*=//' -e 's,/,,g' | \
+	  tr '[A-Z]' '[a-z]' | tr '\012' ' '`
+  set X $myuname; shift; osname=$1
+  case "$osname" in
+  irix*)    osname=irix;;
+  fx2800)   arch=fx2800; osname=concentrix;;
+  hp*)      osname=hpux; arch=`uname -m`
+            case $arch in
+		ia64) 		arch=ia64;;
+		9000/[34]*)	arch=m68k;;
+		9000/[678]*)	arch=hppa;;
+		*)		arch=hppa;;
+	    esac;;
+  os2)
+            arch=`uname -m`
+            if test -z "$arch"; then arch=ix86; fi
+            ;;
+  freebsd|netbsd|openbsd)
+            arch=`uname -m`
+            if test -z "$arch"; then arch=ix86; fi
+            case $arch in
+              amd64) arch=x86_64;;
+            esac;;
+  cygwin*)  arch=`uname -m`
+            if test -z "$arch"; then arch=ix86; fi
+            osname=cygwin;;
+  mingw*)   arch=`uname -m`
+            if test -z "$arch"; then arch=ix86; fi
+            osname=mingw;;
+  ultrix)   arch=mips;;
+  nextstep) arch=`file /bin/sh | sed 's/.*(for architecture \(.*\))/\1/'`;;
+  darwin*)  arch=`uname -p`
+            if test "$arch" = powerpc; then arch=ppc; fi ;;
+  osf1)     case "$5" in alpha) arch=alpha;; esac;;
+  linux)    arch=`uname -m`
+	    case $arch in
+		sparc64) arch=sparcv9;;
+		parisc*) arch=hppa;;
+		sparc) case "`cat /proc/cpuinfo`" in
+	    *SuperSparc*)   arch=sparcv8_super;;
+	    *TMS390Z5[05]*) arch=sparcv8_super;; # SuperSparc I or II
+	    *TMS390S1[05]*) arch=sparcv8_micro;; # MicroSparc I
+	    *MB86904*)      arch=sparcv8_micro;; # MicroSparc II
+	    *MB86907*)      arch=sparcv8_micro;; # TurboSparc
+	    *MB86934*)      arch=sparcv8_super;; # SparcLite
+	    *RT625*)        arch=sparcv8_super;; # HyperSparc
+	    *CY605*)        arch=sparcv8_super;;
+	    	       esac;;
+	    esac;;
+  sunos)    case "$3" in 5*) osname=solaris;; esac
+	    case "$5" in
+	    sun4|sun4[ce]) ;; #arch=sparcv7;;
+	    sun4[dm]) cpu="TI,|FMI,|Cypress,|Ross,"
+	      case "`(prtconf||devinfo)2>&- |egrep $cpu`" in
+	      *TI,TMS390Z5[05]*) arch=sparcv8_super;; # SuperSparc I or II
+	      *TI,TMS390S1[05]*) arch=sparcv8_micro;; # MicroSparc I
+	      *FMI,MB86904*)     arch=sparcv8_micro;; # MicroSparc II
+	      *FMI,MB86907*)     arch=sparcv8_micro;; # TurboSparc
+	      *FMI,MB86934*)     arch=sparcv8_super;; # SparcLite
+	      *Ross,RT625*)      arch=sparcv8_super;; # HyperSparc
+	      *Cypress,CY605*)   arch=sparcv8_super;;
+	      *)                 arch=sparcv8_super;; # ???
+	      esac;;
+	    sun4[uv])    arch=sparcv9;;
+            i*pc) arch=ix86;;
+	    esac;;
+  gnu*)     # Cover GNU/Hurd, GNU/kFreeBSD and other GNU userland
+            arch=`uname -m`;
+            case $arch in i386-*) arch=i386;;esac;;
+  aix)      arch=`uname -p`;
+            case $arch in powerpc) arch=ppc;;esac;;
+  esac
+fi
+echo $arch-$osname
diff --git a/config/checkspaces b/config/checkspaces
new file mode 100755
index 0000000..413c45c
--- /dev/null
+++ b/config/checkspaces
@@ -0,0 +1,25 @@
+#! /bin/sh
+srcbase="`echo src/*/*.[chy] | sed -e 's,src/language/parse\.[ch],,g'`"
+CFILES="$srcbase src/*/*/*.[ch] examples/*.c examples/*.gp"
+docbase="`echo doc/*.tex | sed -e 's,doc/usersch3.tex,,'`"
+OFILES="$docbase AUTHORS COMPAT NEW TODO CHANGES MACHINES src/test/in/* src/functions/*/*"
+SCRIPTS="config/* src/test/dotest"
+ALLFILES="$CFILES $OFILES $SCRIPTS"
+err=0;
+if grep -P '[\x80-\xff]' $ALLFILES; then
+  echo "BUG: high bit found."
+  err=1;
+fi
+if grep ' $' $ALLFILES; then
+  echo "BUG: trailing spaces found."
+  err=1;
+fi
+if grep '	' $CFILES $OFILES; then
+  echo "BUG: TAB found."
+  err=1;
+fi
+if grep '	$' $SCRIPTS; then
+  echo "BUG: TAB found in scripts."
+  err=1;
+fi
+exit $err
diff --git a/config/cygwin-gprc b/config/cygwin-gprc
new file mode 100644
index 0000000..901d393
--- /dev/null
+++ b/config/cygwin-gprc
@@ -0,0 +1,3 @@
+help = "perl.exe gphelp -cu 6"
+prettyprinter = "perl.exe tex2mail -TeX -noindent -ragged -by_par"
+prompt = "(%H:%M) gp > "
diff --git a/config/cygwin-pari.nsi b/config/cygwin-pari.nsi
new file mode 100755
index 0000000..909aaf5
--- /dev/null
+++ b/config/cygwin-pari.nsi
@@ -0,0 +1,199 @@
+#! /bin/sh
+. config/version
+release=`echo "$pari_release"|sed  's/\./-/g'`
+cat << EOT
+;--- PARI/GP: NullSoft Installer configuration file
+!include "MUI.nsh"
+Name "PARI $pari_release_verbose"
+!define dll "libpari.dll"
+!define PARIver "Pari-$release"
+EOT
+cat << 'EOT'
+;--No need to modify things below --
+!define top ".."
+!define cfgdir "${top}\config"
+AutoCloseWindow false
+
+OutFile "Pari.exe"
+InstallDir "$PROGRAMFILES\${PARIver}"
+InstallDirRegKey HKLM "Software\${PARIver}" ""
+
+!define MUI_ABORTWARNING
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_LICENSE "${top}\COPYING"
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+!insertmacro MUI_LANGUAGE "English"
+;--------------------------------
+;Installer Sections
+
+!define uninst "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PARIver}"
+
+Section "pari (required)" SecCopy
+  SetOutPath "$INSTDIR"
+  File /oname=gp.exe "gp-dyn.exe"
+  File /oname=.gprc "${cfgdir}\cygwin-gprc"
+  File /oname=postinst "${cfgdir}\cygwin-postinst"
+  File "${top}\misc\tex2mail"
+  File "${dll}"
+  FILE "\cygwin\bin\cygcrypt-0.dll"
+  FILE "\cygwin\bin\cygiconv-2.dll"
+  FILE "\cygwin\bin\cygintl-8.dll"
+  File "\cygwin\bin\cyggmp-3.dll"
+  File "\cygwin\bin\cygncursesw-10.dll"
+  File "\cygwin\bin\cygreadline7.dll"
+  File "\cygwin\bin\cygperl5_10.dll"
+  File "\cygwin\bin\cyggcc_s-1.dll"
+  File "\cygwin\bin\cygssp-0.dll"
+  File "\cygwin\bin\cygwin1.dll"
+  File "\cygwin\bin\perl.exe"
+  File "\cygwin\bin\sh.exe"
+  File "\cygwin\bin\ln.exe"
+  SetOutPath "$INSTDIR\terminfo\c"
+  File /nonfatal "\cygwin\usr\share\terminfo\c\cygwin"
+  SetOutPath "$INSTDIR\terminfo\63"
+  File /nonfatal "\cygwin\usr\share\terminfo\63\cygwin"
+  SetOutPath "$INSTDIR"
+  CreateDirectory "$INSTDIR\..\bin"
+  ExecWait 'sh ./postinst'
+  Delete "ln.exe"
+  Delete "postinst"
+
+  WriteRegStr HKCU "Software\${PARIver}" "" $INSTDIR
+  WriteRegStr HKLM ${uninst} "DisplayName" "${PARIver} (remove only)"
+  WriteRegStr HKLM ${uninst} "UninstallString" '"$INSTDIR\uninstall.exe"'
+
+  WriteUninstaller "$INSTDIR\Uninstall.exe"
+SectionEnd
+
+SectionGroup /e "Data files" SecDATA
+Section "Elliptic curves files" SecELL
+  SetOutPath "$INSTDIR\data\elldata"
+  File "${top}\data\elldata\*"
+SectionEnd
+
+Section "Galois files" SecGAL
+  SetOutPath "$INSTDIR\data\galdata"
+  File "${top}\data\galdata\*"
+SectionEnd
+
+Section "Frobenius of elliptic curves files" SecSEA
+  SetOutPath "$INSTDIR\data\seadata"
+  File "${top}\data\seadata\*"
+SectionEnd
+
+Section "Galois polynomial files" SecGPL
+  SetOutPath "$INSTDIR\data\galpol"
+  File "${top}\data\galpol\*"
+SectionEnd
+SectionGroupEnd
+
+Section "documentation" SecDOC
+  SetOutPath "$INSTDIR"
+  File "${top}\doc\gphelp"
+  SetOutPath $INSTDIR\doc
+  File "${top}\doc\translations"
+  File "${top}\doc\*.tex"
+  File "${top}\doc\*.pdf"
+SectionEnd
+
+Section "examples" SecEX
+  SetOutPath "$INSTDIR"
+  File "${top}\doc\gphelp"
+  SetOutPath $INSTDIR\examples
+  File "${top}\examples\EXPLAIN"
+  File "${top}\examples\Inputrc"
+  File "${top}\examples\*.gp"
+  File "${top}\examples\*.c"
+  File "${top}\examples\Makefile.cygwin-i686"
+SectionEnd
+
+Function .onInstSuccess
+  MessageBox MB_OK "Thank you for using PARI/GP! Double-click on 'gp' to start the calculator.$\r$\nTweak $INSTDIR\.gprc to customize GP: colors, script search path, etc."
+  ExecShell "open" "$INSTDIR"
+FunctionEnd
+
+!define short "$SMPROGRAMS\${PARIver}"
+
+Section "shortcuts" SecSM
+  CreateDirectory "${short}"
+  CreateShortCut "${short}\gp.lnk" "$INSTDIR\gp.exe" "" "$INSTDIR\gp.exe" 0
+  CreateShortCut "${short}\users.lnk" "$INSTDIR\doc\users.pdf" "" "$INSTDIR\doc\users.pdf" 0
+  CreateShortCut "${short}\libpari.lnk" "$INSTDIR\doc\libpari.pdf" "" "$INSTDIR\doc\libpari.pdf" 0
+  CreateShortCut "${short}\tutorial.lnk" "$INSTDIR\doc\tutorial.pdf" "" "$INSTDIR\doc\tutorial.pdf" 0
+  CreateShortCut "${short}\refcard.lnk" "$INSTDIR\doc\refcard.pdf" "" "$INSTDIR\doc\refcard.pdf" 0
+  WriteINIStr "${short}\PARI pages.url" "InternetShortcut" "URL" "http://pari.math.u-bordeaux.fr"
+  CreateShortCut "${short}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+  CreateShortCut "$DESKTOP\PARI.lnk" "$INSTDIR\gp.exe"
+SectionEnd
+
+;--------------------------------
+;Descriptions
+
+LangString DESC_SecCopy ${LANG_ENGLISH} "Copy pari files to application folder."
+LangString DESC_DOC ${LANG_ENGLISH} "Install documentation and online help."
+LangString DESC_EX ${LANG_ENGLISH} "Install sample GP scripts."
+LangString DESC_DATA ${LANG_ENGLISH} "Data files pertaining to pari"
+LangString DESC_ELL ${LANG_ENGLISH} "Install elliptic curves data files (for ellsearch and ellidentify)."
+LangString DESC_GAL ${LANG_ENGLISH} "Install Galois data files (for polgalois in degree > 7)."
+LangString DESC_SEA ${LANG_ENGLISH} "Install Modular polynomials (for ellap'SEA implementation)."
+LangString DESC_GPL ${LANG_ENGLISH} "Install Galois polynomials data files (for galoisgetpol)."
+LangString DESC_SM ${LANG_ENGLISH} "Add PARI shortcuts to Start Menu and desktop."
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecCopy} $(DESC_SecCopy)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecDATA} $(DESC_DATA)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecELL} $(DESC_ELL)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecGAL} $(DESC_GAL)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecSEA} $(DESC_SEA)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecGPL} $(DESC_GPL)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecSM} $(DESC_SM)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecDOC} $(DESC_DOC)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecEX} $(DESC_EX)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+Section "Uninstall"
+  Delete "$INSTDIR\gp.exe"
+  Delete "$INSTDIR\.gprc"
+  Delete "$INSTDIR\gphelp"
+  Delete "$INSTDIR\tex2mail"
+  Delete "$INSTDIR\${dll}"
+  Delete "$INSTDIR\cygcrypt-0.dll"
+  Delete "$INSTDIR\cygiconv-2.dll"
+  Delete "$INSTDIR\cygintl-8.dll"
+  Delete "$INSTDIR\cyggmp-3.dll"
+  Delete "$INSTDIR\cygncursesw-10.dll"
+  Delete "$INSTDIR\cygreadline7.dll"
+  Delete "$INSTDIR\cygperl5_10.dll"
+  Delete "$INSTDIR\cyggcc_s-1.dll"
+  Delete "$INSTDIR\cygssp-0.dll"
+  Delete "$INSTDIR\cygwin1.dll"
+  Delete "$INSTDIR\perl.exe"
+  Delete "$INSTDIR\sh.exe"
+
+  Delete "$INSTDIR\Uninstall.exe"
+  RMDir /r "$INSTDIR\doc"
+  RMDir /r "$INSTDIR\examples"
+  RMDir /r "$INSTDIR\data"
+  RMDir /r "$INSTDIR\terminfo"
+
+  DeleteRegKey HKLM ${uninst}
+  DeleteRegKey /ifempty HKLM "Software\${PARIver}"
+
+  RMDir /r "$SMPROGRAMS\${PARIver}"
+  Delete "$DESKTOP\PARI.lnk"
+  Delete "$INSTDIR\..\bin\sh"
+  RMDir "$INSTDIR\..\bin"
+  RMDir "$INSTDIR"
+SectionEnd
+EOT
diff --git a/config/cygwin-postinst b/config/cygwin-postinst
new file mode 100644
index 0000000..45271a0
--- /dev/null
+++ b/config/cygwin-postinst
@@ -0,0 +1,6 @@
+pwd="$(pwd)"
+echo 'colors = "boldfg"' >> .gprc
+echo 'help = "'"$pwd"'/perl.exe '"$pwd"'/gphelp -cu 6"' >> .gprc
+echo 'prettyprinter = "'"$pwd"'/perl.exe tex2mail -TeX -noindent -ragged -by_par"' >> .gprc
+pwd=${pwd##*/}
+./ln -s ../${pwd}/sh.exe ../bin/sh
diff --git a/config/display b/config/display
new file mode 100755
index 0000000..7034165
--- /dev/null
+++ b/config/display
@@ -0,0 +1,11 @@
+if test -n "$perl"; then
+  echo $rep | $perl -e "
+     @input=split(/\s/,<>); \$len = \$#input + 3;
+     \$len = ( \$len - \$len % 3) / 3;
+     for(\$i=0;\$i<\$len;\$i++) {
+       printf qq(      %-18s%-18s%-18s\n),
+         \$input[\$i], \$input[\$i+\$len], \$input[\$i+2*\$len];
+     }"
+else
+  echo $rep
+fi
diff --git a/config/endian.c b/config/endian.c
new file mode 100644
index 0000000..f4d9fb6
--- /dev/null
+++ b/config/endian.c
@@ -0,0 +1,22 @@
+#include <stdio.h>
+int main()
+{
+  if (sizeof(long) == 4)
+  {
+    union {double d; unsigned long l[2];} x;
+    x.d = 2.;
+    if      (x.l[0]==0 && x.l[1]==(1UL<<30)) printf("1\n");
+    else if (x.l[1]==0 && x.l[0]==(1UL<<30)) printf("0\n");
+    else
+      printf("NOT IEEE (32 bit)\n");
+  }
+  else
+  {
+    union {double d; unsigned long l;} x;
+    x.d = 2.;
+    if (x.l==(1UL<<62)) printf("-\n");
+    else
+      printf("NOT IEEE (64 bit)\n");
+  }
+  return 0;
+}
diff --git a/config/extract_files b/config/extract_files
new file mode 100755
index 0000000..e8eae1a
--- /dev/null
+++ b/config/extract_files
@@ -0,0 +1,22 @@
+flist=`ls $config_dir/*.SH`
+for file in $flist; do
+  . ./$file
+done
+
+echo "Extracting scripts and macros"
+for dir in "$doc_dir" "$misc_dir"; do
+  if test -d $dir; then
+    echo "...in $dir"
+    flist=`ls $dir/*.in`
+    for file in $flist; do
+      sed -e "s%@perl@%$perl%g"\
+          -e "s%@datadir@%$datadir%g"\
+          -e "s%@bindir@%$bindir%g"\
+          -e "s%@includedir@%$includedir%g"\
+          -e "s%@libdir@%$libdir%g"\
+          -e "s%@version@%$pari_release%g"\
+      $file > $dir/`basename $file .in`
+    done
+  fi
+done
+chmod +x $doc_dir/gphelp $misc_dir/tex2mail
diff --git a/config/genfunclist b/config/genfunclist
new file mode 100755
index 0000000..b2570d7
--- /dev/null
+++ b/config/genfunclist
@@ -0,0 +1,11 @@
+#! /bin/sh
+cd "$1"
+FL=../funclist
+TMPFL=$FL.tmp$$
+find ../functions -name CVS -prune -o -name '.*' -prune -o -name '*~' -prune -o -type f -print | env LANG= LC_COLLATE= LC_ALL= sort | xargs cksum > $TMPFL
+if cmp $FL $TMPFL >/dev/null 2>&1; then
+        rm -f $TMPFL
+else
+        echo "File $FL updated."
+        mv $TMPFL $FL
+fi
diff --git a/config/genkernel b/config/genkernel
new file mode 100755
index 0000000..2a924b8
--- /dev/null
+++ b/config/genkernel
@@ -0,0 +1,21 @@
+#! /bin/sh
+K=../src/kernel
+knone=$K/none
+for file in "$@"; do
+  echo "#ifndef ASMINLINE"
+  for i in `grep '^ASM' $file`; do
+    case $i in
+      ASM);;
+      *) cat $knone/$i.h;;
+    esac;
+  done
+  echo "#endif"
+  cat $file
+  for i in `grep '^NOASM' $file`; do
+    case $i in
+      NOASM);;
+      *) cat $knone/$i.h;;
+    esac;
+  done
+done
+exit 0
diff --git a/config/get_MANIFEST b/config/get_MANIFEST
new file mode 100755
index 0000000..68774c6
--- /dev/null
+++ b/config/get_MANIFEST
@@ -0,0 +1,12 @@
+git ls-files | grep -v '/$' | grep -v '\.gitignore' | grep -v TODO
+# generated files
+echo doc/usersch3.tex
+echo src/language/default.h
+echo src/language/init.h
+echo src/language/parse.c
+echo src/language/parse.h
+echo src/gp/gp_default.h
+echo src/gp/gp_init.h
+echo src/gp/highlvl.h
+echo src/gp/whatnow.h
+echo src/funclist
diff --git a/config/get_PATH b/config/get_PATH
new file mode 100644
index 0000000..24cbd8f
--- /dev/null
+++ b/config/get_PATH
@@ -0,0 +1,46 @@
+if test -z "$OS2_SHELL"; then dir_sep=':' ; else dir_sep=';' ; fi
+
+# Proper PATH setting
+pth="\
+  /bin\
+  /usr/bin\
+  /usr/locateal/bin\
+  /usr/ucb\
+  /usr/locateal\
+  /usr/lbin\
+  /usr/5bin\
+  /etc\
+  /usr/etc\
+  /usr/gnu/bin\
+  /usr/new\
+  /usr/new/bin\
+  /usr/nbin\
+  /sys5.3/bin\
+  /sys5.3/usr/bin\
+  /bsd4.3/bin\
+  /bsd4.3/usr/ucb\
+  /bsd4.3/usr/bin\
+  /usr/bsd\
+  /bsd43/bin\
+  /opt/ansic/bin\
+  /usr/ccs/bin\
+  /usr/lib\
+  /usr/ucblib\
+  /lib\
+  /usr/ccs/lib\
+  /sbin\
+  /usr/sbin\
+  /usr/libexec\
+  /usr/openwin/bin\
+  /usr/local/bin\
+"
+# /usr/openwin/bin added for xmkmf
+
+for p in $pth
+do
+  case "$dir_sep$PATH$dir_sep" in
+    *$dir_sep$p$dir_sep*) ;;
+    *) test -d $p && PATH=$PATH$dir_sep$p ;;
+  esac
+done
+PATH=.$dir_sep$PATH; export PATH
diff --git a/config/get_Qt b/config/get_Qt
new file mode 100644
index 0000000..4325a02
--- /dev/null
+++ b/config/get_Qt
@@ -0,0 +1,44 @@
+if test -z "$with_qt"; then
+  with_qt=yes
+fi
+QTDIR=
+case "$with_qt" in
+yes)
+   pth="/usr/local/lib /usr/local/share /usr/lib /usr/share"
+   QTDIR=`locatedir qt4/bin $pth`
+   if test -n "$QTDIR"; then
+     QTLIB="-lQtCore -lQtGui"
+     which_graphic_lib=Qt4
+   else
+     QTDIR=`locatedir qt/bin $pth`
+     if test -n "$QTDIR"; then
+       QTLIB="-lQtCore -lQtGui"
+       which_graphic_lib=Qt4
+     else
+       QTDIR=`locatedir qt3/bin $pth`
+       if test -n "$QTDIR"; then
+         QTLIB="-lqt-mt"
+         which_graphic_lib=Qt
+       else
+         QTDIR=`locatedir qt2/bin $pth`
+         if test -n "$QTDIR"; then
+           QTLIB="-lqt"
+           which_graphic_lib=Qt
+         fi
+       fi
+     fi
+   fi;
+   if test -n "$QTDIR"; then
+     QTDIR=`dirname $QTDIR`
+   fi;;
+*) if test ! -d "$with_qt"; then
+    echo "### Qt directory '$with_qt' not found"
+  else
+    QTDIR=$with_qt
+  fi;;
+esac
+if test -n "$QTDIR"; then
+  echo "Using Qt library, QTDIR = $QTDIR, QTLIB = $QTLIB"
+else
+  echo "### Qt not found. Building without Qt support"
+fi
diff --git a/config/get_X11 b/config/get_X11
new file mode 100644
index 0000000..6f5a174
--- /dev/null
+++ b/config/get_X11
@@ -0,0 +1,58 @@
+tdir=$osname-$arch-X11$$
+mkdir $tdir; cp Imakefile $tdir; cd $tdir
+cmd="xmkmf"; . ../log_cmd
+if test -f Makefile; then
+  eval `make gp-X11 >&5 2>&1 | grep -v make`
+  x11pth="$usrlibdir $libdir";
+fi
+cd ..; rm -rf $tdir
+
+# Check xmkmf answer
+# X11 -- Headers
+if test ! -f $Xincroot/X11/Xos.h; then
+  x11pth="$addlib64\
+    /usr/openwin/share/lib\
+    /usr/openwin/lib\
+    /usr/X11R6/lib       /usr/X11R5/lib       /usr/X11R4/lib\
+    /usr/lib/X11R6       /usr/lib/X11R5       /usr/lib/X11R4\
+    /usr/local/X11R6/lib /usr/local/X11R5/lib /usr/local/X11R4/lib\
+    /usr/local/lib/X11R6 /usr/local/lib/X11R5 /usr/local/lib/X11R4\
+    /usr/X11/lib\
+    /usr/lib/X11\
+    /usr/local/X11/lib\
+    /usr/local/lib/X11\
+    /usr/XFree86/lib/X11\
+    /usr/lib\
+    /usr/local/lib\
+    /usr/athena/lib\
+  ";
+  pth=`echo $x11pth | sed 's,/lib,/include,g'`
+  x=`./locate X11/Xos.h '' $pth`
+  case $x in
+   /*) Xincroot=`echo $x | sed 's,/X11/Xos.h,,'`;;
+   *)  Xincroot=;;
+  esac
+fi
+if test -f $Xincroot/X11/Xos.h; then
+  echo ..."Found X11 header files in $Xincroot/X11"
+  X11_INC="-I$Xincroot"
+fi
+# X11 -- Lib
+pth=$x11pth
+lib=X11; . ./locatelib
+if test -n "$X11"; then
+  X11_LIBS="-L$X11 -lX11 $extralib"
+else
+  exe=$osname-$arch-X11$$
+  cmd="$CC $CFLAGS $extraflag $X11_INC -o $exe has_X11.c -lX11 $extralib"
+  . log_cmd
+  if test -r $exe; then
+    X11_LIBS="-lX11 $extralib"
+  else
+    echo "### X11 not found"
+    X11_LIBS=
+    X11_INC=
+  fi
+fi
+rm -f $exe $exe$exe_suff;
+echo ..."X11 libraries: $X11_LIBS"
diff --git a/config/get_archos b/config/get_archos
new file mode 100644
index 0000000..d2165e4
--- /dev/null
+++ b/config/get_archos
@@ -0,0 +1,34 @@
+# Testing Architectures. Try uname to provide a default, then ask user.
+#
+if test -z "$target_host"; then
+  target_host=`./arch-osname`
+fi
+arch=`echo "$target_host" | sed -e 's/\(.*\)-.*/\1/'`
+osname=`echo "$target_host" | sed -e 's/.*-\(.*\)/\1/'`
+
+if test "$fastread" != yes; then
+  cat << EOM
+==========================================================================
+Currently supported architectures:
+EOM
+  rep='none sparcv8_super sparcv8_micro sparcv9 ix86 i386 i486 i586 i686
+       alpha x86_64 arm fx2800 hppa ia64 mips m68k ppc s390'
+  . ./display
+  echo $n ..."Which of these apply, if any ? $c"
+  dflt=$arch; . ./myread; arch=$ans
+fi
+
+#
+#   Test OS, using the info uname provided.
+#
+if test "$fastread" != yes; then
+  cat << EOM
+==========================================================================
+I know of the following Operating Systems
+EOM
+  rep='os2 freebsd netbsd cygwin linux mingw gnu gnukfreebsd hpux aix osf1 solaris sunos nextstep concentrix irix';
+  . ./display
+  echo $n ..."Any of these apply ? $c"
+  dflt=$osname; . ./myread
+  osname=$ans
+fi
diff --git a/config/get_cc b/config/get_cc
new file mode 100644
index 0000000..9fe9179
--- /dev/null
+++ b/config/get_cc
@@ -0,0 +1,155 @@
+# Exported variables
+_cc_list="__gnuc__ CC CFLAGS optimization DBGFLAGS OPTFLAGS exe_suff suffix ASMINLINE KERNELCFLAGS"
+
+# Which optimization ?
+if test "$fastread" != yes; then
+  cat << EOT
+==========================================================================
+The default is to fully optimize the compilation. You may choose to build
+  an executable for debugging or profiling instead. Choose among :
+       full       debugging       profiling         gcov
+EOT
+  echo $n ..."Which optimization do you prefer ? $c"
+  dflt=$optimization; rep='full debugging profiling gcov'; . ./myread
+  optimization=$ans
+fi
+
+case "$osname" in
+  os2)   exe_suff=.exe; extraflag="-Zexe";;
+  cygwin|mingw) exe_suff=.exe; extraflag="";;
+# On Darwin, by default, the full library search path is searched for a .dylib
+# before a .a can be considered, preventing users to install their libraries
+# in a simple way (e.g. the readline / Editline conflict). Override this.
+  darwin)  exe_suff=; extraflag=-Wl,-search_paths_first;;
+  *)       exe_suff=; extraflag="";;
+esac
+
+if test -z "$CC"; then
+  echo Choosing C compiler ...
+  if test -n "$gcc"; then CC=$gcc; else CC=$cc; fi
+fi
+
+if test "$fastread" != yes; then
+  cat << EOT
+==========================================================================
+Only ANSI C and C++ compilers are supported.  Choosing the GNU compiler
+gcc/g++ enables the inlining of kernel routines (about 20% speedup; if you
+use g++, include the -fpermissive flag). We strongly recommand using gcc all
+the way through.
+EOT
+  echo $n ..."Which C compiler shall I use ? $c"
+  dflt=$CC; rep=; . ./myread
+  CC=$ans
+fi
+if test -z "$CC"; then cat <<EOT
+###
+### Could not find a C compiler. Please install cc/gcc or set \$CC
+###
+EOT
+  exit 1;
+fi
+
+exe=$osname-$arch-ansi$$
+cmd="$CC $CFLAGS $extraflag -o $exe ansi.c";
+ . log_cmd
+if test -s $exe; then
+  $RUNTEST $exe
+fi
+if test $? != 0 -o ! -s $exe; then cat << EOT
+###
+### C compiler does not work. PARI/GP requires an ANSI C compiler! Aborting.
+###
+### Compiler was: $CC $CFLAGS $extraflag
+EOT
+  exit 1;
+fi
+rm -f $exe $exe$exe_suff
+
+if test "$CC" != "$gcc"; then __gnuc__=; fi
+if test -z "$__gnuc__"; then
+  exe=$osname-$arch-gnu$$
+  cmd="$CC $extraflag -o $exe gnu.c"
+  . log_cmd
+  if $RUNTEST $exe; then
+    # avoid internationalization trouble by setting LANG=C
+    __gnuc__=`env LANG=C LC_ALL=C LC_MESSAGES=C $CC -v 2>&1 |\
+      grep ' version ' | tr '\n' , | sed -e 's/,$//'`
+    echo GNU compatible compiler: $__gnuc__
+  fi
+  rm -f $exe $exe$exe_suff
+fi
+
+# Which Flags for Compiler ?
+cflags=
+ASMINLINE=
+if test -n "$__gnuc__"; then
+  __GNUC__="-D__GNUC__"
+  warn="-Wall"
+  OPTFLAGS=-O3
+  ASMINLINE=yes
+  OPTFLAGS="$OPTFLAGS $warn"
+  cmd="$CC $CFLAGS $extraflag -fno-strict-aliasing -o $exe gnu.c"
+  . log_cmd
+  if test -s $exe; then
+    OPTFLAGS="$OPTFLAGS -fno-strict-aliasing"
+  fi
+  rm -f $exe $exe$exe_suff
+  KERNELCFLAGS=-funroll-loops
+
+  DBGFLAGS=${DBGFLAGS:-"-g $warn"}
+  # Specific optimisations for some architectures
+  case "$arch" in
+    sparcv8*) cflags=-mv8;;
+    i?86|x86_64)
+      case "$__gnuc__" in
+        gcc*4.0.*) cflags=-fno-gcse-after-reload
+      esac
+  esac
+  # problems on some architectures
+  case "$osname" in
+    os2)      cflags="$cflags -Zmt -Zsysv-signals";;
+    nextstep) cflags="$cflags -traditional-cpp";;
+  esac
+
+  # omit-frame-pointer incompatible with -pg
+  PRFFLAGS="-pg $OPTFLAGS"
+  GCOVFLAGS="-fprofile-arcs -ftest-coverage"
+  case "$optimization" in
+    full) case "$osname" in
+           mingw) ;;
+           *) OPTFLAGS="$OPTFLAGS -fomit-frame-pointer";;
+          esac;;
+  esac
+else
+  DBGFLAGS=${DBGFLAGS:-'-g'}
+  PRFFLAGS='-pg'
+  case "$osname-$arch" in
+    hpux-*) # -Ae is for ANSI C + defines HPUX_SOURCE
+                  OPTFLAGS=-O; cflags=-Ae;;
+    aix-*)        OPTFLAGS='-O2 -qtune=auto -qmaxmem=8192'
+                  cflags='-qlanglvl=ansi';;
+    osf1-*)       OPTFLAGS='-O4 -migrate -ifo -Olimit 9999';;
+    sunos-*)      OPTFLAGS=-fast; PRFFLAGS='-pg -Bstatic';;
+    solaris-*)    OPTFLAGS='-fast -fsimple=1'; PRFFLAGS=-xpg;
+                case "$arch" in
+                  sparc*) OPTFLAGS="$OPTFLAGS -xalias_level=any";;
+                esac;;
+    concentrix-*) OPTFLAGS=-Ogi;;
+    *)            OPTFLAGS=-O;;
+  esac
+  PRFFLAGS="$PRFFLAGS $OPTFLAGS"
+fi
+
+case "$optimization" in
+  full)      suffix=;     cflags="$OPTFLAGS $cflags";;
+  profiling) suffix=.prf; cflags="$PRFFLAGS $cflags";;
+  debugging) suffix=.dbg; cflags="-DMEMSTEP=1048576 $DBGFLAGS $cflags";;
+  gcov)      suffix=.gcov; cflags="$GCOVFLAGS $cflags";;
+esac
+
+CFLAGS="$cflags $CFLAGS $CPPFLAGS"
+if test "$fastread" != yes; then
+  echo $n ..."With which flags ? $c"
+  dflt=$CFLAGS; rep=; . ./myread
+  CFLAGS=$ans
+fi
diff --git a/config/get_config_options b/config/get_config_options
new file mode 100644
index 0000000..82b39dd
--- /dev/null
+++ b/config/get_config_options
@@ -0,0 +1,214 @@
+# Processing Options
+dflt_conf_file=pari.cfg
+fastread=yes
+config_file=
+optimization=full
+target_host=
+which_graphic_lib=auto
+thread_engine=single
+share_prefix=
+prefix=/usr/local
+test -n "$GP_INSTALL_PREFIX" && prefix="$GP_INSTALL_PREFIX"
+
+while test $# -gt 0; do
+  case "$1" in
+  -l|--load) shift; initfile=$1;
+     cd "$TOP"
+     PATH=.:$PATH; export PATH
+     if test -z "$1";then
+       tmp_host=`$config_dir/arch-osname`
+       arch=`echo "$tmp_host" | sed -e 's/\(.*\)-.*/\1/'`
+       osname=`echo "$tmp_host" | sed -e 's/.*-\(.*\)/\1/'`
+       objdir=O$osname-$arch;
+       initfile=$objdir/$dflt_conf_file;
+     fi
+     if test -d "$initfile"; then
+       initfile="$initfile/pari.cfg"
+     fi
+     if test -r "$initfile"; then
+       . $initfile
+       if test ! -d $objdir; then mkdir $objdir; fi
+       . $config_dir/get_tests # in case the test set have been changed
+       . $config_dir/extract_files
+       exit 0
+     else
+       echo "Cannot read config file \"$initfile\"." >&2
+       exit 1
+     fi;;
+  -p|-prefix)   shift; prefix=$1;;
+  --prefix=*|--prefi=*|--pref=*|--pre=*|--pr=*|--p=*)
+               prefix=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --share-prefix=*)
+               share_prefix=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --bindir=*) dfltbindir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --datadir=*) dfltdatadir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --includedir=*) dfltincludedir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --libdir=*) dfltlibdir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --mandir=*) dfltmandir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --sysdatadir=*) dfltsysdatadir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --time=*) timing_fun=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --host=*) target_host=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --kernel=*) kernel=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --mt=*) thread_engine=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  --tune) tune=yes;;
+  --builddir) dfltobjdir=auto;;
+  --builddir=*) dfltobjdir=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+  -a|-ask|--ask)     fastread=no;;
+  -g)          optimization=debugging;;
+  -pg)         optimization=profiling;;
+  -gcov)       optimization=gcov;;
+  -h|-help|--help|-\?)error=true;;
+  -v|-verbhelp|--verbhelp)error=verb;;
+  -s|--static) static=y;;
+  -graphic|--graphic) shift; which_graphic_lib=$1;;
+  --graphic=*|--graphi=*|--graph=*|--grap=*|--gra=*|--gr=*|--g=*)
+	       which_graphic_lib=`echo "$1" | sed -e 's/[-a-z]*=//'`;;
+
+  --without-readline|--with-readline=no) without_readline=yes ;;
+  --with-readline) with_readline=yes ;;
+  --with-readline=*)
+      with_readline=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+  --with-readline-lib=*)
+      with_readline_lib=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+  --with-readline-include=*)
+      with_readline_include=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+
+  --without-gmp|--with-gmp=no) without_gmp=yes ;;
+  --with-gmp) with_gmp=yes ;;
+  --with-gmp=*)
+      with_gmp=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+  --with-gmp-lib=*)
+      with_gmp_lib=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+  --with-gmp-include=*)
+      with_gmp_include=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+
+  --with-ncurses-lib=*|--with-ncurses=*)
+      with_ncurses_lib=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+
+  --with-qt) with_qt=yes ;;
+  --with-qt=*)
+      with_qt=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+
+  --with-fltk) with_fltk=yes ;;
+  --with-fltk=*)
+      with_fltk=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+  --enable-tls) enable_tls=yes;;
+  --enable-tls=*) enable_tls=`echo "$1" | sed -e 's/[-a-z]*=//'` ;;
+  --disable-tls) enable_tls=no;;
+  *) echo "*** Unrecognized option $1." >&2; error=true;;
+  esac
+  shift
+done
+# Convert relative paths to absolute.
+# Variables that can be set interactively should not be listed here,
+# see get_install.
+for i in with_readline with_readline_lib with_readline_include \
+         with_gmp with_gmp_lib with_gmp_include \
+         with_ncurses_lib with_qt with_fltk ; do
+  eval "dflt=\"\$$i\""
+  case $dflt in
+    ''|yes|/*) ;;
+            *) eval "$i='$TOP/$dflt'";;
+  esac
+done
+# The behaviour of $prefix/lib --> '//lib' is implementation-dependent
+case "$prefix" in /) prefix=;; esac
+case "$share_prefix" in /) prefix=;; esac
+
+case "$error" in
+true) cat >&2 <<EOT
+Usage: Configure [-ask|-help|-g|-pg] [ --load <filename> ] [ --prefix=<dir> ]
+
+Options: some names can be abbreviated to one character (e.g -h = -help)
+-a, --ask        interactive configuration
+-h, --help       this message
+-l, --load       skip Configure and specify a default config file
+-s, --static     build static GP binary only
+-v, --verbhelp   a longer help message
+Build Options:
+  --host=<arch-osname>  target achitecture
+  --kernel=<kern>       kernel used
+  --graphic=<gr>        graphic library used (none, X11, Qt, fltk, ps, win32)
+  --time=<fun>          timing function to use (getrusage, clock_gettime,
+                          times, ftime)
+  --builddir=<dir>      directory where the object files will be created
+  --tune                tune the binary for compiling host (slow)
+Additional developer options:
+  -g              creates debugging version (in Oxxx.dbg)
+  -pg             creates profiling version (in Oxxx.prf)
+  -gcov           creates gcov version (in Oxxx.gcov)
+  --enable-tls	  (*experimental*) enable thread-local stack
+  --mt=pthread	  (*experimental*) enable pthread thread engine
+  --mt=mpi	  (*experimental*) enable MPI thread engine
+
+Installation directories:
+  --prefix=<dir>        install files in <dir> (default $prefix)
+  --share-prefix=<dir>  as 'prefix', for architecture independent files
+  --bindir=<dir>        for binaries
+  --includedir=<dir>    for C header files
+  --libdir=<dir>        for libraries
+  --mandir=<dir>        for manual pages
+  --sysdatadir=<dir>    for architecture-dependent data
+  --datadir=<dir>       for architecture-independent data
+
+Optional libraries:
+  --without-readline          do not link with GNU readline
+  --with-readline[=DIR]       use GNU readline [prefix for readline files]
+  --with-readline-include=DIR specify location of readline headers
+  --with-readline-lib=DIR     specify location of readline libs
+  --with-ncurses-lib=DIR      specify location of ncurses lib (for readline)
+
+  --without-gmp               use the native kernel instead of GNU MP
+  --with-gmp[=DIR]            use the GMP kernel [prefix for gmp files]
+  --with-gmp-include=DIR      specify location of gmp headers
+  --with-gmp-lib=DIR          specify location of gmp libs
+
+  --with-qt[=DIR]        use the Qt graphical library [prefix for Qt dir.]
+  --with-fltk[=DIR]      use the FLTK graphical library [prefix for FLTK dir.]
+
+Environment variables affecting the build:
+  CC                     C compiler
+  CFLAGS                 additional flags to the C compiler
+  LD                     linker
+  LDFLAGS                additional linker flags
+  DLLDFLAGS              additional linker flags for linking the shared lib
+  C_INCLUDE_PATH         directories to search for include files (separate by :)
+  LIBRARY_PATH           directories to search for libraries (separate by :)
+
+EOT
+exit 1
+;;
+verb) cat >&2 <<EOT
+Architecture, operating system and asm kernel.
+  Configure tries to detect what is the architecture of the machine (CPU type)
+  and what operating system it is running. Then, it decides whether an asm
+  kernel exists and should be used. You can override this with Configure -ask.
+Which compiler will be used ?
+  Depending on the machine (architecture and operating system) and on what is
+  found in the path, a compiler is automatically chosen. If you set the
+  environment variable CC before typing Configure, it will be used instead.
+  Typeset the installation help ('tex doc/INSTALL.tex') for more details.
+EOT
+exit 1
+;;
+esac
+
+test "$fastread" = yes || cat <<EOT
+==========================================================================
+             Beginning configuration questions for GP/PARI.
+
+You will be asked some questions about your system. Most of the time, a list
+of acceptable answers will be supplied as well as a default between brackets.
+Type a carriage return ('Enter') to accept these defaults.
+
+Though the present script strives to prevent any fatal mistake on your part,
+there is currently no easy way out if you make one. Your best bet is to press
+Ctrl-C, then start again.  Another possibility is to wait till the questions
+end, edit the file "Oxxx/$dflt_conf_file", then run
+
+      Configure --load Oxxx/$dflt_conf_file
+
+(which can be abbreviated to simply "Configure -l", provided your architecture
+is correctly detected)
+==========================================================================
+EOT
diff --git a/config/get_dlcflags b/config/get_dlcflags
new file mode 100644
index 0000000..3d9c97f
--- /dev/null
+++ b/config/get_dlcflags
@@ -0,0 +1,18 @@
+_dl_list="DLCFLAGS"
+if test -n "$__gnuc__"; then
+  case $osname in
+    cygwin|mingw) DLCFLAGS=;;
+    darwin) DLCFLAGS=-fPIC
+      case $arch in
+        ppc|ppc64) DLCFLAGS="$DLCFLAGS -fno-common"
+      esac;;
+    *) DLCFLAGS=-fPIC;;
+  esac
+else #assume native compiler
+  case "$osname" in
+    hpux) DLCFLAGS=+z;;
+    solaris) DLCFLAGS=-KPIC;;
+  esac
+fi
+
+echo "C compiler is          $CC $CFLAGS $DLCFLAGS"
diff --git a/config/get_dlld b/config/get_dlld
new file mode 100644
index 0000000..2272aac
--- /dev/null
+++ b/config/get_dlld
@@ -0,0 +1,125 @@
+# Exported variables
+_dlld_list='DL_DFLT_NAME DLLD DLLDFLAGS EXTRADLLDFLAGS DLSUFFIX soname sodest DLLTOOL'
+
+# Which suffix for Dynamic Lib?
+# Some linkers (SunOS 4) need minor and major lib version numbers.
+# Some others (SunOS 5) need a link from a .so
+# Some others (HPUX 09) do not want version numbers.
+DLSUFFIX=so
+soname=.$soname_num
+do_dll=yes
+case "$osname" in
+  gnu*|aix|osf1|solaris|linux|freebsd|netbsd)
+    case $pari_release_verbose in
+      *STABLE*)      sodest=.$version.$patch;; # released version
+      *DEVELOPMENT*) sodest=.$patch.0.0;;      # unstable version
+    esac ;;
+  sunos) sodest=.$VersionMajor$VersionMinor.$patch
+         soname=$sodest;;
+  hpux) soname= ; sodest= ; DLSUFFIX=sl;;
+  irix) soname= ; sodest= ;;
+  os2|cygwin|mingw)soname= ; sodest= ; DLSUFFIX=dll
+    if test "x$DLLTOOL" = x; then
+      DLLTOOL=`locate dlltool`;
+      if test "x$DLLTOOL" = x; then
+        DLLTOOL=`$CC -dumpmachine`-dlltool
+      fi
+    fi;;
+  darwin)soname= ; sodest= ; DLSUFFIX=dylib;
+         compat_ver=$VersionMajor.$VersionMinor.0; num_ver=$VersionMajor.$VersionMinor.$patch;;
+  *) do_dll=no ;;
+esac
+
+# dlopen(NULL) should return a handle to the running process.
+# On FreeBSD 2.2.5 (Y. Uchikawa) and Cygwin, this does not work.
+case "$osname" in
+  freebsd|cygwin) DL_DFLT_NAME="\\\"\$(LIBPARI_DYN)\\\"" ;;
+  mingw) DL_DFLT_NAME="\\\"\$(LIBPARI_SO)\\\"" ;;
+  *) DL_DFLT_NAME=NULL ;;
+esac
+
+# if DLLD is defined at this point, respect it, even if do_dll=no
+if test $do_dll = yes -a -z "$DLLD"; then
+  if test -n "$__gnuc__" -o "$osname" = "solaris"; then
+    DLLD="$CC"
+  else
+    DLLD=$ld # don't take risks
+  fi
+fi
+
+GNUdlld=
+DLLDisGCC=
+if test -n "$DLLD"; then
+# Which Dynamic Lib Linker?
+  if test "$fastread" != yes; then
+    echo $n ..."Which linker for building dynamic libs? $c"
+    dflt="$DLLD"; rep=; . ./myread
+    DLLD=$ans
+  fi
+
+  if test "$DLLD" = "$CC" -a -n "$__gnuc__"; then
+    DLLDisGCC=yes;
+    GNUdlld=$GNULDused
+  else
+    case "$DLLD" in
+      *ld) if ($DLLD -v 2>&1 | grep GNU > /dev/null); then GNUdlld=yes; fi;;
+    esac
+  fi
+
+# Which Flags for Dynamic Lib Linker ?
+  dlldflags="$DLLDFLAGS"
+  DLLDFLAGS=
+  if test -n "$GNUdlld"; then
+    DLLDFLAGS="-shared -soname=\$(LIBPARI_SONAME)"
+  else # DLLD != GNU ld
+    case "$osname" in
+      aix)     DLLDFLAGS='-r' ;;
+      darwin)  DLLDFLAGS="-flat_namespace -undefined suppress -compatibility_version $compat_ver -current_version $num_ver" ;;
+      freebsd) DLLDFLAGS='-Bshareable -x' ;;
+      hpux)    DLLDFLAGS='-b' ;;
+      irix)    DLLDFLAGS='-shared -elf -no_unresolved -all' ;;
+      osf1)    DLLDFLAGS='-shared' ;;
+      solaris) DLLDFLAGS="-G -h \$(LIBPARI_SONAME)" ;;
+      sunos)   DLLDFLAGS='-assert nodefinitions' ;;
+      os2)     ;; # see below
+      linux)   ;; # for e.g. the Portland Group cc (pgcc)
+      *)         DLLD=;;
+    esac
+  fi
+  if test -n "$DLLDFLAGS"; then
+    DLLDFLAGS=`./ldflags "$DLLDisGCC" $DLLDFLAGS`
+  fi
+  case "$osname" in
+    os2) DLLDFLAGS="$CFLAGS -Zdll" ;; # assume DLLD = gcc
+    cygwin) DLLDFLAGS="-Wl,--out-implib=\$(LIBPARI_SO)\$(_A),--export-all-symbols";;
+    mingw) DLLDFLAGS="-Wl,--out-implib=\$(LIBPARI_SO)\$(_A)";;
+  esac
+  if test -n "$DLLDisGCC"; then
+    case "$arch-$osname" in
+      sparc-solaris) extra='-mimpure-text';;
+      *) extra=;;
+    esac
+    case "$osname" in
+      darwin) shared=-dynamiclib;;
+      *) shared=-shared;;
+    esac
+    DLLDFLAGS="$shared $extra \$(CFLAGS) \$(DLCFLAGS) $DLLDFLAGS"
+  fi
+  case "$osname" in
+# Beware: will run through 'eval' [ hence ${...} instead of \$(...) ]
+    gnu*|cygwin|osf1|freebsd|linux|sunos|solaris) EXTRADLLDFLAGS='-lc ${LIBS}';;
+  esac
+
+  if test "$fastread" != yes; then
+    echo $n ..."Which flags for linker? $c"
+    dflt=$DLLDFLAGS; rep=; . ./myread
+    DLLDFLAGS=$ans
+  fi
+  DLLDFLAGS="$DLLDFLAGS $dlldflags"
+fi
+
+if test -z "$DLLD"; then
+  echo "No Dynamic Lib"
+else
+  echo "Dynamic Lib linker is  $DLLD  $DLLDFLAGS"
+fi
diff --git a/config/get_double_format b/config/get_double_format
new file mode 100644
index 0000000..bf80390
--- /dev/null
+++ b/config/get_double_format
@@ -0,0 +1,51 @@
+exe=$osname-$arch-endian$$
+cmd="$CC $CFLAGS $extraflag endian.c -o $exe"; . log_cmd
+if test -r $exe; then
+  doubleformat=`$RUNTEST $exe`;
+else
+  echo "***************************************************************"
+  echo "Cannot compile endian.c. Aborting. PLEASE REPORT!"
+  exit 1
+fi
+rm -f $exe $exe$exe_suff
+case "$doubleformat" in
+  *IEEE*)
+    echo "***************************************************************"
+    echo "Your 'double' type does not follow the IEEE754 format. Aborting"
+    echo "PLEASE REPORT! (dbltor/rtodbl need to be fixed)"; exit 1;;
+  -) sizeof_long=8;;
+  *) sizeof_long=4;;
+esac
+echo "Given the previous choices, sizeof(long) is $sizeof_long chars."
+
+if test "$fastread" != yes; then
+cat << EOT
+If your hardware supports different size of longs (e.g SGI/MIPS), and you
+want to use a different word size than the above. You should probably have
+specified some exotic compilation flag CFLAG (e.g -o32,-n32).
+
+EOT
+
+  if test $doubleformat != "-"; then
+cat << EOT
+For 32-bit architecture, PARI needs to know the format of your 'double' type.
+PARI assumes doubles are stored in IEEE754 format [ (sign, exponent, mantissa
+high) on one word, (mantissa low) on another ]; assuming a
+  union { double d; ulong l[2]; } x;
+are the double exponent and sign stored on x.i[0] (0) or on x.i[1] (1) ?
+
+Using \$CC \$CFLAGS with
+  CC    =$CC
+  CFLAGS=$CFLAGS
+the answer is: $doubleformat.
+EOT
+  fi
+fi
+case $doubleformat in
+  0) _format='l[0], l[1]';;
+  1) _format='l[1], l[0]';;
+  -) _format='not needed (64bit)';;
+esac
+cat <<EOT
+The internal word representation of a double is $_format.
+EOT
diff --git a/config/get_fltk b/config/get_fltk
new file mode 100644
index 0000000..cfe16c3
--- /dev/null
+++ b/config/get_fltk
@@ -0,0 +1,24 @@
+if test -z "$with_fltk"; then
+  with_fltk=yes
+fi
+FLTKDIR=
+case "$with_fltk" in
+yes)
+   pth=$libpth; lib=fltk; . ./locatelib
+   if test -n "$fltk"; then
+     FLTKDIR=`dirname $fltk | sed -e 's/lib\/$//'`
+   fi
+   ;;
+*) if test ! -d "$with_fltk"; then
+    echo "### FLTK directory '$with_fltk' not found"
+  else
+    FLTKDIR=$with_fltk
+  fi;;
+esac
+if test -n "$FLTKDIR"; then
+  echo "Using FLTK library, FLTKDIR = $FLTKDIR"
+  FLTK_LIBS="`fltk-config --ldflags` -lstdc++"
+else
+  echo "### FLTK not found. Building without FLTK support"
+  FLTK_LIBS=
+fi
diff --git a/config/get_gmp b/config/get_gmp
new file mode 100644
index 0000000..f2a11c0
--- /dev/null
+++ b/config/get_gmp
@@ -0,0 +1,79 @@
+_gmp_list="gmp GMPLIBS GMPINCLUDE"
+gmp=
+if test -n "$with_gmp"; then
+  with_gmp_lib="$with_gmp_lib $with_gmp/lib"
+  with_gmp_include="$with_gmp_include $with_gmp/include"
+fi
+pth="$with_gmp_lib $libpth"
+lib=gmp; . ./locatelib
+
+pth="$with_gmp_include $basic_include_path"
+x=`./locate 'gmp.h' '' $pth`
+case $x in
+ ?:/*|/*) gmp_include=`dirname $x`
+   echo ..."Found gmp header in $gmp_include"
+   GMPINCLUDE="-I$gmp_include"
+   ;;
+  *) echo ..."gmp header file not found by Configure, trying to proceed"
+     gmp=;;
+esac
+
+exe=$osname-$arch-gmpv$$
+if test -n "$gmp"; then
+  GMPLIBS="-L$gmp -lgmp"
+else
+  GMPLIBS="-lgmp"
+fi
+
+cmd="$CC $CFLAGS $extraflag $GMPINCLUDE -o $exe gmp_version.c $GMPLIBS"
+. log_cmd
+if test -r $exe; then
+  gmp_version=`env LD_LIBRARY_PATH="$LD_LIBRARY_PATH$dir_sep$gmp" $RUNTEST $exe`;
+fi
+case "$gmp_version" in
+  unsupported) gmp=
+    echo "### Your GMP library ABI is unsupported.";;
+  "") gmp=
+    cmd="$CC $CFLAGS $extraflag $GMPINCLUDE -o $exe ansi.c $GMPLIBS"
+    . log_cmd
+    if test -r $exe; then
+      echo "### Your version of GMP is too old for PARI. Please upgrade"
+    else
+      echo "### Your GMP library is incompatible with the compiler settings."
+    fi;;
+  *) if test -z "$gmp"; then gmp=yes; fi;;
+esac
+rm -f $exe $exe$exe_suff
+
+if test -z "$gmp"; then
+  echo "### Building without GNU MP support"
+else
+  if test "$fastread" = yes; then
+    echo "Using GNU MP, version $gmp_version"
+  else
+    cat << EOM
+==========================================================================
+GNU MP library can be used as an alternate multiprecision kernel, which
+is faster than PARI's native one as soon as integers larger than 10^100
+are considered. Unfortunately, with GNU MP, libpari is binary incompatible
+with the native one. Despite this, you should only answer 'no' to the
+following question if you plan to use libpari (not only the gp shell)
+and have stringent backward compatibility requirements.
+EOM
+    echo $n "Do you want to use GNU MP library instead of the native kernel? $c"
+    if test "$with_gmp" = yes; then dflt=y; else dflt=n; fi
+    rep='y n'; . ./myread
+    case $ans in
+      n) gmp=;;
+    esac
+  fi
+fi
+
+if test -n "$gmp"; then
+  kernlvl1=gmp
+else
+  kernlvl1=none
+  GMPINCLUDE=
+  GMPLIBS=
+fi
+. get_pretty
diff --git a/config/get_graphic_lib b/config/get_graphic_lib
new file mode 100644
index 0000000..8a8aa97
--- /dev/null
+++ b/config/get_graphic_lib
@@ -0,0 +1,67 @@
+if test "$optimization" = profiling; then
+  which_graphic_lib=none
+fi
+
+_graphic_list="which_graphic_lib X11 X11_INC X11_LIBS \
+FLTKDIR FLTK_LIBS QTDIR QTLIB"
+
+if test -n "$with_fltk"; then which_graphic_lib=fltk; fi
+if test -n "$with_qt";   then which_graphic_lib=Qt; fi
+if test "$fastread" != yes; then
+  cat << EOT
+==========================================================================
+GP contains high resolution plotting functions. Choose among
+      none      X11      fltk      Qt      win32      ps
+EOT
+  echo $n ..."Use which graphic library (\"none\" means no hi-res plot) ? $c"
+  rep="none X11 fltk Qt win32 ps";
+  dflt=$which_graphic_lib; . ./myread
+  which_graphic_lib=$ans
+fi
+
+case $osname in
+  mingw|cygwin) case $which_graphic_lib in
+                  auto) which_graphic_lib=win32;;
+                esac;;
+esac
+case $which_graphic_lib in
+  auto|X11|x11)
+  . ./get_X11  # X11,X11_INC,X11_LIBS.
+    if test -z "$X11_LIBS"; then
+      case $which_graphic_lib in X11|x11) which_graphic_lib=none;; esac
+    else
+      which_graphic_lib=X11
+    fi;;
+esac
+case $which_graphic_lib in
+  auto|fltk)
+    case $osname in
+      darwin) ;; # fltk brings in CoreFoundation, incompatible with pari_daemon
+      *). ./get_fltk # FLTKDIR, FLTK_LIBS ;;
+        if test -z "$FLTKDIR"; then
+          case $which_graphic_lib in fltk) which_graphic_lib=none;; esac
+        else
+          which_graphic_lib=fltk
+        fi;;
+    esac;;
+esac
+# TODO: Check whether Qt + pari_daemon() work on darwin [ probably not ]
+case $which_graphic_lib in
+  auto|Qt|qt)
+    . ./get_Qt   # QTDIR, QTLIB
+    if test -z "$QTDIR"; then
+      case $which_graphic_lib in qt|Qt) which_graphic_lib=none;; esac
+    # Never automatically pick Qt
+    fi;;
+esac
+case $which_graphic_lib in
+  auto|PS|ps) which_graphic_lib=ps;;
+esac
+case $which_graphic_lib in
+  X11|fltk|Qt|Qt4|ps)
+    if test "$has_waitpid" = no -o "$has_setsid" = no; then
+      echo "### Missing waitpid() or setsid(), no Hi-Res graphing window"
+      which_graphic_lib=none
+    fi;;
+esac
+echo "Hi-Res Graphics: $which_graphic_lib"
diff --git a/config/get_head b/config/get_head
new file mode 100644
index 0000000..2920953
--- /dev/null
+++ b/config/get_head
@@ -0,0 +1,5 @@
+if (head -n 1 < /dev/null >/dev/null 2>&1); then
+  head='head -n'
+else
+  head='head -'
+fi
diff --git a/config/get_include_path b/config/get_include_path
new file mode 100644
index 0000000..da1d9a8
--- /dev/null
+++ b/config/get_include_path
@@ -0,0 +1,17 @@
+incpth="`echo $C_INCLUDE_PATH | sed -e \"s%\([^$dir_sep]*\)$dir_sep*%\1 \1/readline %g\"`"
+case "$osname" in
+  os2);;
+  cygwin*) incpth="$incpth\
+    $cygtop/usr/local/include\
+    $cygtop/usr/include\
+    $cygtop/H-${arch}-cygwin32/${arch}-cygwin32/include\
+    ";;
+  *) incpth="$incpth\
+    /usr/local/include\
+    /usr/include\
+    /opt/include\
+    /opt/local/include\
+    /opt/gnu/include\
+    ";;
+esac
+basic_include_path=$incpth
diff --git a/config/get_install b/config/get_install
new file mode 100644
index 0000000..7e16541
--- /dev/null
+++ b/config/get_install
@@ -0,0 +1,57 @@
+# Exported variables
+_install_list="prefix share_prefix bindir datadir includedir libdir mandir sysdatadir "
+
+dflt=$prefix; rep=
+test "$fastread" = yes || cat <<EOT
+==========================================================================
+By default, gp will be installed in $dflt/bin, manual pages under
+  $dflt/man, etc..., with $dflt as prefix for all installation directories.
+  If you wish to have binaries under /bin but manual pages under
+  /usr/local/man, that's ok: you will be prompted separately for each of the
+  installation directories, the prefix being only used to set the defaults.
+  (You will be prompted before the actual installation is done.)
+The names of executables and libraries contain their version number $version.
+  A symbolic link to gp or libpari.[a/so] will point to the most recent
+  installation of GP/PARI.
+EOT
+echo $n "Installation prefix ? $c"
+. ./myread; prefix=$ans
+
+if test -z "$share_prefix"; then
+  share_prefix=$prefix/share
+fi
+echo $n "...for architecture-independent files (share-prefix) ? $c"
+dflt=$share_prefix; . ./myread; share_prefix=$ans
+
+dfltman=$share_prefix/man/man1
+dfltdata=$share_prefix/pari
+
+echo "Installation directories for:"
+echo $n ..."executables (gp, gphelp) ? $c"
+dflt=${dfltbindir:-$prefix/bin}; . ./myread; bindir=$ans
+
+echo $n ..."libraries (libpari) ? $c"
+case $osname in
+mingw) dflt=${dfltlibdir:-$prefix/bin};;
+*) dflt=${dfltlibdir:-$prefix/lib};;
+esac; . ./myread; libdir=$ans
+
+echo $n ..."include files ? $c"
+dflt=${dfltincludedir:-$prefix/include}; . ./myread; includedir=$ans
+
+echo $n ..."manual pages ? $c"
+dflt=${dfltmandir:-$dfltman}; . ./myread; mandir=$ans
+
+echo $n ..."other system-dependent data ? $c"
+dflt=${dfltsysdatadir:-$prefix/lib/pari}; . ./myread; sysdatadir=$ans
+
+echo $n ..."other system-independent data ? $c"
+dflt=${dfltdatadir:-$dfltdata}; . ./myread; datadir=$ans
+
+for i in prefix share_prefix bindir libdir includedir mandir sysdatadir datadir ; do
+  eval "dflt=\"\$$i\""
+  case $dflt in
+       @|/*) ;;
+       *) eval "$i='$TOP/$dflt'";;
+  esac
+done
diff --git a/config/get_kernel b/config/get_kernel
new file mode 100644
index 0000000..7717f35
--- /dev/null
+++ b/config/get_kernel
@@ -0,0 +1,122 @@
+# Testing Architectures. Try uname to provide a default, then ask user.
+#
+case "$arch" in
+  sparc)         asmarch=sparcv8_micro; prettya=Sparc ;;
+  sparcv8_micro) asmarch=$arch;         prettya=MicroSparc ;;
+  sparcv8_super) asmarch=$arch;         prettya=SuperSparc ;;
+  sparcv9) case "$sizeof_long" in
+           4) asmarch=sparcv8_micro;;
+           8) asmarch=none;;
+           esac;                        prettya=UltraSparc ;;
+  i?86)    case "$sizeof_long" in
+           4) asmarch=ix86;;
+           8) asmarch=x86_64;;
+           esac;                        prettya=$arch ;;
+  x86_64)  case "$sizeof_long" in
+           4) asmarch=ix86;;
+           8) asmarch=x86_64;;
+           esac;                        prettya='amd64';;
+  ia64)    case "$sizeof_long" in
+           4) asmarch=none;;
+           8) asmarch=ia64;;
+           esac;                        prettya=Itanium;;
+  hppa) case "$sizeof_long" in
+           4) asmarch=hppa;             prettya='PA-RISC1.1';;
+           8) asmarch=hppa64;           prettya='PA-RISC2.0';;
+           esac;;
+  mips|mips64) case "$sizeof_long" in
+           4) asmarch=mips;             prettya='MIPS';;
+           8) asmarch=mips64;           prettya='MIPS64';;
+           esac;;
+  alpha)         asmarch=$arch;         prettya=Alpha ;;
+  ppc|ppc64)  case "$sizeof_long" in
+        4) asmarch=ppc;;
+        8) asmarch=ppc64;;
+        esac;                           prettya='PowerPC' ;;
+  arm*)  case "$sizeof_long" in
+    4)
+      exe=$osname-$arch-endian$$
+      echo $n "Checking supported ARM kernel: $c"
+      cmd="$CC $CFLAGS -I../src/kernel/arm kernel.c -o $exe"; . log_cmd
+      if test -r $exe; then
+        asmarch=arm;
+      else
+        asmarch=none;
+      fi;
+      echo "$asmarch"
+      rm -f $exe;;
+    8) asmarch=none;;
+    esac; prettya=$arch;;
+  m68k)          asmarch=m68k;          prettya='Motorola 68k';;
+  sh3)           asmarch=none;          prettya=SH-3 ;;
+  sh4)           asmarch=none;          prettya=SH-4 ;;
+  sh5)           asmarch=none;          prettya=SH-5 ;;
+  vax)           asmarch=none;          prettya=VAX ;;
+  fx2800)        asmarch=none;          prettya='Alliant FX/2800' ;;
+  s390)          asmarch=none;          prettya='S/390' ;;
+  none)          asmarch=none;          prettya=unknown ;;
+  *)             asmarch=none;          prettya=$arch
+                 echo "        Warning ! architecture $arch not tested";;
+esac
+
+#
+#   Modifications for pretty name and asm file
+#
+
+cat << EOM
+==========================================================================
+EOM
+
+tmp_kern=auto-auto
+if test  -n "$kernel"; then
+  tmp_kern=$kernel
+else
+  if test "$fastread" != yes; then
+  cat << EOM
+An optimized Pari kernel is available for these architectures
+("none" means that we will use the portable C version of GP/PARI)
+("-gmp" means we will use the GMP library (that needs to be installed))
+EOM
+  rep='none sparcv8_super sparcv8_micro ix86 alpha hppa m68k ppc ppc64 x86_64
+  none-gmp sparcv8_super-gmp sparcv8_micro-gmp ix86-gmp alpha-gmp hppa-gmp m68k-gmp ppc-gmp ppc64-gmp x86_64-gmp'
+  . ./display
+  echo $n ..."Which of these apply, if any ? $c"
+  dflt=$asmarch; . ./myread;
+  kernel=$ans # explicit kernel, needed when checking for gmp in Configure
+  tmp_kern=$ans
+  cat << EOM
+==========================================================================
+EOM
+  fi
+fi
+if test -z "$without_gmp" ; then
+  lvl1=gmp
+else
+  lvl1=none
+fi
+tmp_kern=`./kernel-name $tmp_kern $asmarch $lvl1`
+kernlvl0=`echo "$tmp_kern" | sed -e 's/\(.*\)-.*/\1/'`
+kernlvl1=`echo "$tmp_kern" | sed -e 's/.*-\(.*\)/\1/'`
+
+case "$kernlvl0" in
+  none)          prettyk0="portable C";;
+  m68k)          prettyk0="m68k";;
+  sparcv8_super) prettyk0=SuperSparc;;
+  sparcv8_micro) prettyk0=MicroSparc;;
+  ix86)          prettyk0=ix86;;
+  ia64)          prettyk0=ia64;;
+  hppa)          prettyk0=HPPA;;
+  hppa64)        prettyk0=HPPA64;;
+  alpha)         prettyk0=Alpha;;
+  ppc)           prettyk0=PPC;;
+  ppc64)         prettyk0=PPC64;;
+  x86_64)        prettyk0="x86-64";;
+  *)             prettyk0="$kernlvl0";;
+esac
+
+. get_pretty
+
+echo "Building for: $pretty"
+cat << EOM
+==========================================================================
+EOM
diff --git a/config/get_ld b/config/get_ld
new file mode 100644
index 0000000..9a8482d
--- /dev/null
+++ b/config/get_ld
@@ -0,0 +1,78 @@
+# Which Executable Linker ?
+#
+_ld_list='LD LDFLAGS LIBS runpathprexix LDneedsWl LDused GNULDused'
+case "$osname" in
+  darwin) LIBS= ;;
+  osf1)   LIBS='-lm -lots';;
+  *)      LIBS=-lm;;
+esac
+
+if test -z "$LD"; then LD=$CC; fi
+
+if test "$fastread" != yes; then
+  echo $n ..."Which linker for building executables ? $c"
+  dflt=$LD; rep=; . ./myread
+  LD=$ans
+fi
+LDused=$LD
+
+# Which Flags for Executable Linker?
+ldflags=
+GNULDused=
+if test "$LD" = "$CC"; then
+  ldflags=$CFLAGS
+  if test -n "$__gnuc__"; then
+    LDused=`$CC -print-prog-name=ld`
+    LDneedsWl=yes
+  else
+    if test "$osname" = hpux; then LDneedsWl=yes; fi
+  fi
+fi
+if ($LDused -v 2>&1 | grep GNU > /dev/null); then GNULDused=yes; fi
+
+tmp=
+if test "$GNULDused" = "yes"; then
+  case "$osname" in
+    cygwin|mingw) tmp=--enable-auto-import ;; # PE does not support --export-dynamic
+    *) tmp=--export-dynamic ;;
+  esac
+else
+  case "$osname-$arch" in
+    aix-*)  tmp=-brtl ;; # in case we link against a shared library
+    hpux-*) tmp=-E ;;
+  esac
+fi
+case "$osname" in
+  darwin) tmp="$tmp -search_paths_first";;
+esac
+
+if test -n "$tmp"; then
+  tmp=`./ldflags "$LDneedsWl" $tmp`
+  ldflags="$ldflags $tmp"
+fi
+
+case "$osname-$arch" in
+  os2-*)  ldflags="$ldflags -Zexe"
+          if test "$optimization" = "full"; then ldflags="$ldflags -s"; fi
+esac
+
+LDFLAGS="$ldflags $LDFLAGS"
+
+if test "$fastread" != yes; then
+  echo $n ..."With which flags ? $c"
+  dflt=$LDFLAGS; rep=; . ./myread
+  LDFLAGS=$ans
+fi
+
+echo "Executable linker is   $LD  $LDFLAGS"
+
+if test "$GNULDused" = yes; then
+  runpathprefix='-rpath '
+else # guess...
+  case "$osname" in
+    gnu|osf1|linux|cygwin*|freebsd|netbsd) runpathprefix='-rpath ' ;;
+    solaris) runpathprefix='-R ' ;;
+    hpux) runpathprefix='+b ' ;;
+    aix) runpathprefix='-blibpath:' ;;
+  esac
+fi
diff --git a/config/get_libc b/config/get_libc
new file mode 100644
index 0000000..513a983
--- /dev/null
+++ b/config/get_libc
@@ -0,0 +1,56 @@
+# Looking in libc for some functions.
+exe=$osname-$arch-tmp$$
+_has_list=
+echo Looking in C lib for some symbols...
+extra_flags=-lm
+list=exp2; . ./look
+list=log2; . ./look
+extra_flags=
+list=strftime; . ./look
+if test "$timing_fun" = "clock_gettime"; then
+  extra_flags=-lrt
+  list=clock_gettime; . ./look
+  extra_flags=
+  if test "$has_clock_gettime" = yes; then
+    RT_LIBS=-lrt
+  fi
+else
+  if test -n "$timing_fun"; then
+    list=$timing_fun
+  else
+    case "$osname" in
+      *cygwin*) list='times';; # getrusage based timer always returns 0
+      *) list='getrusage times';;
+    esac;
+  fi; . ./look
+fi
+list=sigaction; . ./look
+list=TIOCGWINSZ; . ./look
+list=getrlimit; . ./look
+list='stat opendir'; . ./look
+list=vsnprintf; . ./look
+list=waitpid; . ./look
+list=setsid; . ./look
+list=getenv; . ./look
+list=isatty; . ./look
+list=alarm; . ./look
+
+# For install(). Do we need libdl.so?
+# on irix and osf1 -ldl not needed
+extra_flags=
+DL_LIBS=
+list=dlopen; . ./look
+if test "$has_dlopen" = no; then
+  echo "Try again, with -ldl this time..."
+  extra_flags=-ldl; . ./look
+  if test "$has_dlopen" = yes; then
+    DL_LIBS=-ldl
+  fi
+fi
+if test "$has_dlopen" = no; then
+  case "$osname" in
+    os2|darwin)
+      echo "Will use builtin dlopen() support for $osname..."
+      has_dlopen=builtin
+  esac
+fi
diff --git a/config/get_libpth b/config/get_libpth
new file mode 100644
index 0000000..6e3fb72
--- /dev/null
+++ b/config/get_libpth
@@ -0,0 +1,54 @@
+case "$sizeof_long" in
+  8) addlib64="/usr/local/lib64 /lib64 /usr/lib64"
+esac
+
+libpth="`echo $LIBRARY_PATH | sed -e \"s%\([^$dir_sep]*\)$dir_sep*%\1 %g\"`"
+case "$osname" in
+  cygwin*)
+    cygtop=/cygdrive/c
+    if test ! -d $cygtop; then
+      ver=`uname -r | cut -d. -f1`
+      cygtop=/Cygnus/cygwin/B$ver
+      if test ! -d $cygtop; then
+        cygtop=/Cygnus/cygwin-B$ver
+        if test ! -d $cygtop; then
+          cygtop=/usr/${arch}-pc-cygwin
+          if test ! -d $cygtop; then
+            echo ..."I could not find Cygwin top directory" >&2
+          fi
+        fi
+      fi
+    fi
+    if test "$cygtop" = /cygdrive/c; then
+# cygnus for version 1.*
+      libpth="$libpth\
+          /usr/local/lib\
+          /lib\
+      "
+      cygtop=
+    else
+# cygnus for version 0.*
+      libpth="$libpth\
+          $cygtop/H-${arch}-cygwin32/lib\
+          $cygtop/H-${arch}-cygwin32/${arch}-cygwin32/lib\
+      "
+    fi;;
+  os2) libpth=`echo $libpth | sed 's,\\\\,/,g'`;;
+  *) libpth="$libpth $addlib64\
+      /usr/local/lib\
+      /lib\
+      /usr/lib\
+      /opt/lib\
+      /opt/local/lib\
+      /opt/gnu/lib\
+      /lib/pa1.1\
+      /usr/lib/large\
+      /lib/large\
+      /usr/lib/small\
+      /lib/small\
+      /usr/ccs/lib\
+      /usc/ucblib\
+      /usr/shlib\
+      .\
+   ";;
+esac
diff --git a/config/get_modld b/config/get_modld
new file mode 100644
index 0000000..3a77363
--- /dev/null
+++ b/config/get_modld
@@ -0,0 +1,26 @@
+# Exported variables
+_modld_list='EXTRAMODLDFLAGS MODLD MODLDFLAGS modules_build'
+
+# EXTRADLLDFLAGS might refer to $LIBS
+__LIBS=$LIBS
+if test "$static" = n; then LIBS="$LIBS -L$libdir $LDDYN"; fi
+case $osname in
+cygwin|mingw) EXTRAMODLDFLAGS="-L$libdir $LDDYN -Wl,--enable-auto-import";;
+*)      EXTRAMODLDFLAGS=`eval echo $EXTRADLLDFLAGS`;;
+esac;
+LIBS=$__LIBS
+
+case $osname in
+darwin) MODLD=$CC;
+        MODLDFLAGS="-bundle -flat_namespace -undefined suppress \
+\$(CFLAGS) \$(DLCFLAGS)";;
+*)      MODLD="$DLLD";
+        MODLDFLAGS=`echo "$DLLDFLAGS" | \
+   sed -e 's/,*-[^ \t-]*[ \t,=]*\\$(LIBPARI_SONAME)//' \
+       -e 's/-Wl,--out-implib=\$(LIBPARI_SO)\$(_A)//'`;;
+esac;
+
+modules_build=`echo "$CC -c -o %s.o $CFLAGS $DLCFLAGS -I\"$includedir\" %s.c \
+&& $MODLD -o %s.so $MODLDFLAGS %s.o $EXTRAMODLDFLAGS" | \
+               sed -e 's!\$(CFLAGS)'"!$CFLAGS!g" \
+                   -e 's!\$(DLCFLAGS)'"!$DLCFLAGS!g"`
diff --git a/config/get_mt b/config/get_mt
new file mode 100644
index 0000000..daf2fa7
--- /dev/null
+++ b/config/get_mt
@@ -0,0 +1,31 @@
+file=$thread_engine.c
+MT_LIBS=
+case $thread_engine in
+single);;
+pthread)
+  MT_LIBS="-lpthread"
+  cmd="$CC $CFLAGS $extraflag -o $exe $file $MT_LIBS"
+  . log_cmd
+  if test -r $exe; then
+    rm -f $exe $exe$exe_suff
+    enable_tls="yes"
+  else
+    echo "### --mt=pthread requires the library pthread"
+    echo "### Please install it"
+    thread_engine=single;
+  fi;;
+mpi)
+  cmd="$CC $CFLAGS $extraflag -o $exe $file"
+  . log_cmd
+  if test -r $exe; then
+    rm -f $exe $exe$exe_suff
+  else
+    echo "### --mt=mpi requires to use mpicc"
+    echo "### Please use 'env CC=mpicc ./Configure --mt=mpi'"
+    exit 1;
+  fi;;
+*) echo "### invalid mt engine $thread_engine"
+   thread_engine=single;;
+esac
+
+echo "Using mt engine $thread_engine"
diff --git a/config/get_nl b/config/get_nl
new file mode 100644
index 0000000..3867ac0
--- /dev/null
+++ b/config/get_nl
@@ -0,0 +1,13 @@
+echo "Checking echo to see how to suppress newlines..."
+if (echo "hi\c"; echo " ") | grep c >/dev/null 2>&1 ; then
+  echo "...using -n."; n=-n; c=
+else
+  cat <<EOM
+...using \c
+EOM
+  n=; c='\c'
+fi
+if test "$fastread" != yes; then
+  echo $n ..."The star should be here-->$c"; echo '*'
+fi
+
diff --git a/config/get_objdir b/config/get_objdir
new file mode 100644
index 0000000..603f650
--- /dev/null
+++ b/config/get_objdir
@@ -0,0 +1,35 @@
+# Target directory for object files
+pre=O
+objdir=$pre$osname-$arch;
+if test -n "$dfltobjdir"; then
+  if test "$dfltobjdir" = auto; then
+    case "$kernlvl0" in
+      $asmarch);;
+      sparcv8_micro)
+         if test "$arch" != sparcv9; then objdir=$objdir-$kernlvl0; fi ;;
+      *) objdir=$objdir-$kernlvl0 ;;
+    esac
+    if test -n "$with_gmp"; then objdir=$objdir-gmp; fi
+    dfltobjdir=
+  else
+    objdir="$dfltobjdir"
+  fi
+fi
+if test -z "$dfltobjdir"; then
+  case "$optimization" in
+    full)      objdir=$objdir;;
+    debugging) objdir=$objdir.dbg ;;
+    profiling) objdir=$objdir.prf ;;
+    gcov)      objdir=$objdir.gcov ;;
+  esac
+fi
+
+if test "$fastread" != yes; then
+  cat << EOT
+==========================================================================
+This is the name of the directory where all the object files will be:
+EOT
+  echo $n ..."Enter dir name : $c"
+  dflt=$objdir; rep=; . ./myread
+  objdir=$ans
+fi
diff --git a/config/get_perl b/config/get_perl
new file mode 100644
index 0000000..99613f9
--- /dev/null
+++ b/config/get_perl
@@ -0,0 +1,15 @@
+# set variables depending on perl's version
+_perl_list="add_funclist"
+
+add_funclist=
+if test -n "$perl"; then
+  res=`$perl -e 'print "OK" if ($] >= 5.005);'`;
+  if test $? != 0; then
+    echo "###"
+    echo "### $perl seems to be broken"
+    echo "###"
+    perl=
+  fi
+  if test "$res" = OK; then add_funclist=../src/funclist; fi
+fi
+
diff --git a/config/get_pretty b/config/get_pretty
new file mode 100644
index 0000000..05fddd3
--- /dev/null
+++ b/config/get_pretty
@@ -0,0 +1,17 @@
+pretty="$prettya running $osname"
+
+case "$kernlvl1" in
+  gmp) if test -n "$gmp_version"; then
+         prettyk="$prettyk0/GMP-${gmp_version}"
+       else
+         prettyk="$prettyk0/GMP"
+       fi;;
+  none) prettyk="$prettyk0";;
+  *) prettyk="$prettyk0/$kernlvl1";;
+esac
+
+case "$sizeof_long" in
+  4) pretty="$pretty ($prettyk kernel) 32-bit version";;
+  8) pretty="$pretty ($prettyk kernel) 64-bit version";;
+esac;
+
diff --git a/config/get_readline b/config/get_readline
new file mode 100644
index 0000000..a7ba691
--- /dev/null
+++ b/config/get_readline
@@ -0,0 +1,102 @@
+#exported variables
+_readline_list="readline readline_version RLINCLUDE RLLIBS"
+
+readline=
+if test -n "$with_readline"; then
+  with_readline_lib="$with_readline_lib $with_readline/lib"
+  with_readline_include="$with_readline_include $with_readline/include"
+fi
+pth="$with_readline_lib $libpth"
+lib=readline; . ./locatelib
+
+# Readline -- Headers
+pth="$with_readline_include $basic_include_path"
+x=`./locate 'readline/readline.h' '' $pth`
+case $x in
+ ?:/*|/*) rl_include=`dirname $x`
+    echo ..."Found readline header in $rl_include"
+    if (echo $rl_include | grep "readline$" > /dev/null); then
+      rl_include=`dirname $rl_include`
+      RLINCLUDE="-I$rl_include"
+    fi
+     ;;
+  *) echo ..."readline header file not found by Configure, trying to proceed"
+     readline=;;
+esac
+
+exe=$osname-$arch-rlv$$
+if test -n "$readline"; then
+  RLLIBS="-L$readline -lreadline"
+else
+  RLLIBS="-lreadline"
+fi
+rllibs="$RLLIBS";
+cmd="$CC $CFLAGS $extraflag $RLINCLUDE -o $exe rl_version.c $RLLIBS"
+. log_cmd
+if test ! -r $exe; then # need ncurses ?
+  echo ..."Linking failed. Trying with libncurses"
+  pth="$with_ncurses_lib $libpth"
+  lib=ncurses; . ./locatelib
+  if test -n "$ncurses"; then
+    RLLIBS="$rllibs -L$ncurses -lncurses"
+  else
+    RLLIBS="$rllibs -lncurses"
+  fi
+  cmd="$CC $CFLAGS $extraflag $RLINCLUDE -o $exe rl_version.c $RLLIBS"
+  . log_cmd
+fi
+if test ! -r $exe; then # need termcap ?
+  echo ..."Linking failed. Trying with libtermcap"
+  pth="$with_ncurses_lib $libpth"
+  lib=termcap; . ./locatelib
+  if test -n "$termcap"; then
+    RLLIBS="$rllibs -L$termcap -ltermcap"
+  else
+    RLLIBS="$rllibs -ltermcap"
+  fi
+  cmd="$CC $CFLAGS $extraflag $RLINCLUDE -o $exe rl_version.c $RLLIBS"
+  . log_cmd
+fi
+
+readline_version=
+if test -r $exe; then
+  readline_version=`env LD_LIBRARY_PATH="$LD_LIBRARY_PATH$dir_sep$readline" $RUNTEST $exe`;
+fi
+rm -f $exe $exe$exe_suff;
+
+case "$readline_version" in
+*Editline*|*EditLine*) readline=
+  echo "###"
+  echo "### Editline wrapper detected, building without readline support"
+  echo "###";;
+"") readline=
+  echo "###"
+  echo "### Readline library does not seem to work. Maybe install libncurses?"
+  echo "###";;
+*) if test -z "$readline"; then readline=yes; fi;;
+esac
+
+if test -n "$readline"; then
+  if test "$fastread" != yes; then
+    cat << EOM
+==========================================================================
+GNU readline provides line editing in the gp shell, with history and
+context-dependent completions. You should really answer 'yes' to the
+following question, unless you are trying to overcome a problem in
+the default build.
+EOM
+    echo $n "Do you want to use GNU readline library within GP ? $c"
+    rep='y n'; dflt=y; . ./myread
+    case $ans in
+      n) readline=;;
+    esac
+  fi
+fi
+
+if test -z "$readline"; then
+  echo "### Building without GNU readline support"
+  RLLIBS=
+  RLINCLUDE=
+else
+  echo "Using GNU readline, version $readline_version"
+fi
diff --git a/config/get_static b/config/get_static
new file mode 100644
index 0000000..b647aba
--- /dev/null
+++ b/config/get_static
@@ -0,0 +1,28 @@
+if test -z "$DLLD" -o "$optimization" != full; then
+  static=y
+else
+  static=${static-n}
+fi
+if test "$fastread" != yes; then
+  if test -z "$DLLD"; then
+    cat <<EOT
+==========================================================================
+We cannot build a dynamic executable. We will build the static version.
+EOT
+  else
+    cat <<EOT
+==========================================================================
+By default, we try to build the shared library and gp is an executable
+  dynamically linked with it. Do you prefer to have the static archive
+  libpari.a and a statically linked executable (which is a bit faster,
+  but takes more disk place) ?
+You can always type "make all" in case you want both later.
+EOT
+  echo $n "Do you want static executable and library ? $c"
+  dflt=$static; rep='y n'; . ./myread; static=$ans
+  fi
+fi
+case "$static" in
+  y) echo "Default is static executable and archive library";;
+  n) echo "Default is dynamic executable and shared library";;
+esac
diff --git a/config/get_tests b/config/get_tests
new file mode 100644
index 0000000..84484cd
--- /dev/null
+++ b/config/get_tests
@@ -0,0 +1,36 @@
+# Format: filename_weight (weight = 1000 if omitted)
+#   individual times are printed as is, but accumulated time is weighed
+#   by (weight / 1000)
+_test_list='test_extra_OUT test_extra_out test_extra test_basic top_test_extra top_dotest_extra'
+
+# _not_ included in 'make test-all' (annoying)
+test_extra_out="ploth io parallel time"
+
+# _not_ included automatically in Oxxx/Makefile (special cased there)
+test_extra_OUT="env"
+
+test_extra_all_out="$test_extra_out $test_extra_OUT"
+pattern_out=`echo $test_extra_all_out | sed -e 's/\b/\\\b/g; s/ /|/g'`
+
+# included in 'make bench'
+test_basic="\
+  objets\
+  analyz\
+  number\
+  polyser\
+  linear\
+  elliptic\
+  sumiter\
+  graph\
+  program\
+  trans\
+  nfields_200\
+"
+
+# included in 'make test-all' in addition to regular components of 'make bench'
+test_extra=`ls "$TOP"/src/test/in | egrep -v $pattern_out`
+test_extra=`echo $test_extra | sed -e 's/\n/ /g'`
+
+all_tests="$test_extra $test_extra_out $test_extra_OUT"
+top_test_extra="test-`echo $all_tests | sed -e 's/ \\([^ ]\\)/ test-\\1/g'`"
+top_dotest_extra="dotest-`echo $all_tests | sed -e 's/ \\([^ ]\\)/ dotest-\\1/g'`"
diff --git a/config/gmp_version.c b/config/gmp_version.c
new file mode 100644
index 0000000..001df8b
--- /dev/null
+++ b/config/gmp_version.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <gmp.h>
+void f(void) { mpn_gcdext(NULL,NULL, NULL, NULL, 0, NULL, 0); }
+int main()
+{
+  if (sizeof(mp_limb_t) == sizeof(long))
+    printf("%s", gmp_version);
+  else
+    printf("unsupported");
+  return 0;
+}
diff --git a/config/gnu.c b/config/gnu.c
new file mode 100644
index 0000000..d1eba99
--- /dev/null
+++ b/config/gnu.c
@@ -0,0 +1,5 @@
+#ifdef __GNUC__
+int main(){return 0;}
+#else
+int main(){return 1;}
+#endif
diff --git a/config/gprc.mingw b/config/gprc.mingw
new file mode 100644
index 0000000..d87cbcd
--- /dev/null
+++ b/config/gprc.mingw
@@ -0,0 +1,6 @@
+lines  = 25
+colors = "brightfg"
+prompt = "(%H:%M) gp > "
+histfile = "gp_history.txt"
+breakloop = 0
+help = "@ perl\\perl gphelp.pl -detex -ch 10 -cb 11 -cu 12"
diff --git a/config/has_TIOCGWINSZ.c b/config/has_TIOCGWINSZ.c
new file mode 100644
index 0000000..afa3e10
--- /dev/null
+++ b/config/has_TIOCGWINSZ.c
@@ -0,0 +1,12 @@
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef __sun
+#  include <sys/termios.h>
+#endif
+#include <sys/ioctl.h>
+int main()
+{
+  struct winsize s;
+  int status = ioctl(0, TIOCGWINSZ, &s);
+  return s.ws_col;
+}
diff --git a/config/has_X11.c b/config/has_X11.c
new file mode 100644
index 0000000..45ccfa5
--- /dev/null
+++ b/config/has_X11.c
@@ -0,0 +1,4 @@
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+int main(){ (void)XOpenDisplay(NULL); return 0; }
diff --git a/config/has_alarm.c b/config/has_alarm.c
new file mode 100644
index 0000000..555a89c
--- /dev/null
+++ b/config/has_alarm.c
@@ -0,0 +1,3 @@
+#include <unistd.h>
+unsigned int (*f)(unsigned int) = alarm;
+int main(){ return f != alarm; }
diff --git a/config/has_clock_gettime.c b/config/has_clock_gettime.c
new file mode 100644
index 0000000..e7f64c3
--- /dev/null
+++ b/config/has_clock_gettime.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <time.h>
+int
+main()
+{
+  struct timespec t;
+  printf("%d",clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t));
+  return 0;
+}
diff --git a/config/has_dlopen.c b/config/has_dlopen.c
new file mode 100644
index 0000000..148ddab
--- /dev/null
+++ b/config/has_dlopen.c
@@ -0,0 +1,3 @@
+#include <stdio.h>
+#include <dlfcn.h>
+int main() {dlopen("a",RTLD_LAZY); return 0;}
diff --git a/config/has_exp2.c b/config/has_exp2.c
new file mode 100644
index 0000000..f97cbe6
--- /dev/null
+++ b/config/has_exp2.c
@@ -0,0 +1,3 @@
+#include <math.h>
+double (*f)(double) = exp2;
+int main(){ return f != exp2; }
diff --git a/config/has_ftime.c b/config/has_ftime.c
new file mode 100644
index 0000000..410f97a
--- /dev/null
+++ b/config/has_ftime.c
@@ -0,0 +1,5 @@
+# include <sys/timeb.h>
+int main() {
+  struct timeb t; ftime(&t);
+  return t.time*1000+t.millitm;
+}
diff --git a/config/has_getenv.c b/config/has_getenv.c
new file mode 100644
index 0000000..17ab790
--- /dev/null
+++ b/config/has_getenv.c
@@ -0,0 +1,2 @@
+#include <stdlib.h>
+int main(){ (void)getenv(""); return 0; }
diff --git a/config/has_getrlimit.c b/config/has_getrlimit.c
new file mode 100644
index 0000000..20d07bf
--- /dev/null
+++ b/config/has_getrlimit.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+int main() {
+  struct rlimit rip;
+  getrlimit(RLIMIT_STACK, &rip);
+  setrlimit(RLIMIT_STACK, &rip);
+  return 0;
+}
+
diff --git a/config/has_getrusage.c b/config/has_getrusage.c
new file mode 100644
index 0000000..f22b188
--- /dev/null
+++ b/config/has_getrusage.c
@@ -0,0 +1,7 @@
+#define _INCLUDE_POSIX_SOURCE
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+int main(){ struct rusage a; printf("%d",getrusage(RUSAGE_SELF,&a)); return 0; }
diff --git a/config/has_isatty.c b/config/has_isatty.c
new file mode 100644
index 0000000..c0df7c3
--- /dev/null
+++ b/config/has_isatty.c
@@ -0,0 +1,2 @@
+#include <unistd.h>
+int main(){ isatty(0); return 0; }
diff --git a/config/has_log2.c b/config/has_log2.c
new file mode 100644
index 0000000..6d23834
--- /dev/null
+++ b/config/has_log2.c
@@ -0,0 +1,3 @@
+#include <math.h>
+double (*f)(double) = log2;
+int main(){ return f != log2; }
diff --git a/config/has_opendir.c b/config/has_opendir.c
new file mode 100644
index 0000000..022fb20
--- /dev/null
+++ b/config/has_opendir.c
@@ -0,0 +1,3 @@
+#include <sys/types.h>
+#include <dirent.h>
+main() { DIR *d = opendir("."); }
diff --git a/config/has_setsid.c b/config/has_setsid.c
new file mode 100644
index 0000000..d0b6875
--- /dev/null
+++ b/config/has_setsid.c
@@ -0,0 +1,4 @@
+#include <sys/types.h>
+#include <unistd.h>
+pid_t (*f)() = setsid;
+int main(){ return f != setsid; }
diff --git a/config/has_sigaction.c b/config/has_sigaction.c
new file mode 100644
index 0000000..dcf6711
--- /dev/null
+++ b/config/has_sigaction.c
@@ -0,0 +1,12 @@
+#include <signal.h>
+int main()
+{
+  struct sigaction sa, oldsa;
+
+  sa.sa_handler = SIG_DFL;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_NODEFER;
+
+  (void)(sigaction(SIGINT, &sa, &oldsa));
+  return 0;
+}
diff --git a/config/has_stat.c b/config/has_stat.c
new file mode 100644
index 0000000..48f22d0
--- /dev/null
+++ b/config/has_stat.c
@@ -0,0 +1,9 @@
+#include <sys/stat.h>
+#if (!defined(_MSC_VER) && !defined(_WIN32))
+#  include <sys/types.h>
+#  include <unistd.h>
+#endif
+int main(void) { struct stat buf;
+  if (stat (".", &buf) || !S_ISDIR(buf.st_mode)) return 1;
+  return 0;
+}
diff --git a/config/has_strftime.c b/config/has_strftime.c
new file mode 100644
index 0000000..5fc3ead
--- /dev/null
+++ b/config/has_strftime.c
@@ -0,0 +1,2 @@
+#include <time.h>
+int main(){ struct tm *x = NULL; strftime("",1," ",x); return 0; }
diff --git a/config/has_times.c b/config/has_times.c
new file mode 100644
index 0000000..cfe49da
--- /dev/null
+++ b/config/has_times.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <sys/times.h>
+#include <unistd.h>
+main(){
+  struct tms t;
+  printf("%d%d", times(&t),
+#ifdef _SC_CLK_TCK
+		 sysconf(_SC_CLK_TCK)
+#else
+		 CLK_TCK
+#endif
+  );
+}
diff --git a/config/has_vsnprintf.c b/config/has_vsnprintf.c
new file mode 100644
index 0000000..f5c2042
--- /dev/null
+++ b/config/has_vsnprintf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <stdarg.h>
+
+int main() { return 0; }
+int f(int i,...) { char s[1]; va_list ap; va_start(ap,i);
+  vsnprintf(s,1," ",ap); return 0;
+}
+
+
diff --git a/config/has_wait.c b/config/has_wait.c
new file mode 100644
index 0000000..68849b7
--- /dev/null
+++ b/config/has_wait.c
@@ -0,0 +1,4 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+main(){ wait(NULL); }
diff --git a/config/has_waitpid.c b/config/has_waitpid.c
new file mode 100644
index 0000000..a5a0a79
--- /dev/null
+++ b/config/has_waitpid.c
@@ -0,0 +1,4 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+int main(){ waitpid(-1,NULL,0); return 0; }
diff --git a/config/install b/config/install
new file mode 100755
index 0000000..b03723a
--- /dev/null
+++ b/config/install
@@ -0,0 +1,27 @@
+#! /bin/sh
+
+mode=755
+while test $# -gt 0; do
+  case "$1" in
+  -c);;
+  -m) shift; mode="$1";;
+  *) break;;
+  esac
+  shift
+done
+
+if test -d "$2"; then
+  file="$2/`basename "$1"`"
+else
+  file="$2"
+fi
+
+if test -d "$1"; then
+  mkdir -p "$file"
+  for f in `ls "$1"`; do
+    "$0" -m "$mode" "$1/$f" "$file"
+  done
+else
+  cp "$1" "$2";
+  if test -f "$file"; then chmod "$mode" "$file"; fi
+fi
diff --git a/config/kernel-name b/config/kernel-name
new file mode 100755
index 0000000..2310c6b
--- /dev/null
+++ b/config/kernel-name
@@ -0,0 +1,33 @@
+#! /bin/sh
+name=$1
+arch=$2
+lvl1=$3
+
+case "$name" in
+  *-*)
+  kernlvl0=`echo "$name" | sed -e 's/\(.*\)-.*/\1/'`
+  kernlvl1=`echo "$name" | sed -e 's/.*-\(.*\)/\1/'` ;;
+  gmp) #Alias for auto-gmp
+  kernlvl0="$arch"; kernlvl1=gmp ;;
+  none) #Alias for none-none
+  kernlvl0=none; kernlvl1=none;;
+  *)
+  kernlvl0="$name"; kernlvl1=auto ;;
+esac
+
+if [ "$kernlvl0" = "auto" ]; then
+  kernlvl0="$arch";
+fi
+
+case "$kernlvl1" in
+  gmp|none) ;;
+  auto) kernlvl1=$lvl1;;
+  *) cat << EOM >& 2
+###
+### Level1 kernel = '$kernlvl1' unknown, using 'none'
+###
+EOM
+     kernlvl1=none ;;
+esac
+
+echo "$kernlvl0-$kernlvl1"
diff --git a/config/kernel.c b/config/kernel.c
new file mode 100644
index 0000000..e230fb2
--- /dev/null
+++ b/config/kernel.c
@@ -0,0 +1,24 @@
+#define ulong unsigned long
+#define ASMINLINE
+#include "asm0.h"
+
+#define __asm__ __asm__ volatile
+
+void fun(ulong a, ulong b)
+{
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+  addll(a,b);
+  addllx(a,b);
+  mulll(a,b);
+  addmul(a,b);
+#if 0
+  bfffo(a);
+#endif
+}
+
+int main(void)
+{
+  fun(0xb9f3dcdcUL,0xfbdc740b);
+  return 0;
+}
diff --git a/config/ldflags b/config/ldflags
new file mode 100755
index 0000000..b6c9100
--- /dev/null
+++ b/config/ldflags
@@ -0,0 +1,11 @@
+#! /bin/sh
+t=$1; shift
+if test -n "$t"; then
+  L=-Wl
+  for c in "$@"; do
+    L=$L,"$c"
+  done;
+  echo $L
+else
+  echo "$@"
+fi
diff --git a/config/locate b/config/locate
new file mode 100755
index 0000000..489c12b
--- /dev/null
+++ b/config/locate
@@ -0,0 +1,13 @@
+#!/bin/sh
+t=$1; shift; dflt=$1; shift
+
+for dir in $*; do
+  file=$dir/$t
+  if test -f $file; then
+    echo $file; exit 0
+  fi
+  if test -f $file.exe; then
+    echo $file.exe; exit 0
+  fi
+done
+echo $dflt; exit 1
diff --git a/config/locatedir b/config/locatedir
new file mode 100755
index 0000000..2f0c32a
--- /dev/null
+++ b/config/locatedir
@@ -0,0 +1,10 @@
+#!/bin/sh
+t=$1; shift;
+
+for dir in $*; do
+  file=$dir/$t
+  if test -d $file; then
+    echo $file; exit 0
+  fi
+done
+exit 1
diff --git a/config/locatelib b/config/locatelib
new file mode 100755
index 0000000..eec671c
--- /dev/null
+++ b/config/locatelib
@@ -0,0 +1,35 @@
+_pb= # to detect missing .so when .so.x is found
+for dir in $pth; do
+  base=$dir/lib$lib
+  case "$osname" in
+    os2)
+      try=`ls $base.a 2> /dev/null`;;
+    *)
+      ok=`(ls $base.* | ${head}1) 2> /dev/null`
+      if test -n "$ok"; then
+        _pb="$ok"
+        # so, sl, dylib, dll.a
+        try=`ls $base.s? $base.a $base.dylib $base.dll.a 2> /dev/null`
+      else
+        try=
+      fi;;
+  esac
+  if test -n "$try"; then
+    echo ..."Found lib$lib in $dir";
+    eval $lib=$dir;
+    break
+  fi
+done
+# not found? detect missing lib*.so (missing *-devel package)
+if test -z "$try"; then
+  if test "$osname" = linux -a  -n "$_pb"; then
+    case "$lib" in
+      X11) rpmlib="[XFree86|xorg-x11|libx11]"; devlib=libx11 ;;
+      *) rpmlib=$lib; devlib=lib$lib ;;
+    esac
+    echo "###"
+    echo "### lib$lib.so not found. Maybe install $lib development files?"
+    echo "### E.g.$rpmlib-devel (RPM) or $devlib-dev (Debian) packages"
+    echo "###"
+  fi
+fi
diff --git a/config/log_cmd b/config/log_cmd
new file mode 100644
index 0000000..b2d12ce
--- /dev/null
+++ b/config/log_cmd
@@ -0,0 +1,2 @@
+echo $cmd >&5
+eval $cmd >&5 2>&1
diff --git a/config/look b/config/look
new file mode 100755
index 0000000..35ca620
--- /dev/null
+++ b/config/look
@@ -0,0 +1,17 @@
+# Look for functions in $list. Return as soon as the first function is found,
+# defining has_$fun
+for fun in $list; do
+  cmd="$CC $CFLAGS has_$fun.c -o $exe $extra_flags"; . log_cmd
+  if test -s $exe ; then
+    if test -x $exe -o "$osname" = os2 ; then
+      eval "has_$fun=yes"; echo ..."Found $fun."
+      case "$_has_list" in
+        *has_$fun*);;
+        *) _has_list="$_has_list has_$fun";;
+      esac
+      break
+    fi
+  fi
+  eval "has_$fun=no"; echo ..."I did not find $fun."
+done
+rm -f $exe
diff --git a/config/make_tags b/config/make_tags
new file mode 100755
index 0000000..9a269cd
--- /dev/null
+++ b/config/make_tags
@@ -0,0 +1,90 @@
+eval 'exec perl "$0" "$1" "$2"'
+  if $running_under_some_shell;
+use File::Find 'find';
+
+# Build a (sorted) EMACS / VI(M) tags file including GP functions
+#
+$emacs_mode = ($ARGV[0] eq "--emacs");
+$src = $ARGV[1];
+
+if ($emacs_mode)
+{ $tags = "$src/TAGS"; }
+else
+# Case sensitive?
+{ $tags = $^O eq 'os2' ? "$src/ctags" : "$src/tags"; }
+$tmptags = "$tags.tmp";
+
+my (%gp);
+getnames("$src/gp/gp_init.h");
+getnames("$src/language/init.h");
+
+my (@files) = (); find \&filter_c, $src;
+ at tags = "";
+if ($emacs_mode) {
+  system('exuberant-ctags', '-e', '-f', $tmptags, @files)
+    && system('ctags-exuberant', '-e', '-f', $tmptags, @files)
+    && system('etags', '-f', $tmptags, @files)
+    && die("etags failed");
+  open(T,"$tmptags");
+
+  while(<T>) {
+    my ($a,$b);
+    $a = $_;
+    if (/^(\w+)\(/ && ($b = $gp{$1}) && $b ne $1)
+    {
+      $a =~ s/\x7F.*\x01/\x7F$b\x01/;
+      push(@tags,$a);
+    }
+    push(@tags,$_);
+  }
+} else {
+  system('exuberant-ctags', '-f', $tmptags, @files)
+    && system('ctags-exuberant', '-f', $tmptags, @files)
+    && system('ctags', '-dT', '-o', $tmptags, @files) # gctags
+    && system('ctags', '-f', $tmptags, @files)
+    && die("ctags failed");
+  open(T,"$tmptags");
+
+  # Assume ctags outputs sorted tags (e.g Exuberant Ctags)
+  my ($old) = "";
+  for (sort(keys %gp)) {
+    my ($a) = $_;
+    my ($b) = $gp{$a};
+    if ($a eq $old) { push(@tags,"$b$rest\n"); next; }
+    $old = $a;
+    while(<T>)
+    {
+      push(@tags,$_);
+      if (/^$a(.*)/) { $rest="$1"; push(@tags,"$b$rest\n"); last; }
+    }
+  }
+  while(<T>) { push(@tags,$_); }
+  @tags = sort(@tags);
+}
+close(T);
+open(OUT,">$tags");
+print OUT @tags;
+unlink $tmptags;
+
+# $gp{GP_function} = C_function
+sub getnames
+{
+  open(A,$_[0]);
+  while (<A>) {
+    if (/^entree functions_/../^$/) {
+      if (/[^"]*"([^"]*)".*\(void\*\) *([^,]*)/)
+      {
+        my ($gpfun, $cfun) = ($1,$2);
+        $gpfun =~ s/_\.//g; # member functions
+        $gp{$cfun} = $gpfun;
+      }
+    }
+  }
+  close(A);
+}
+
+sub filter_c {
+  return unless /\.[chy]\Z/ && -f;
+  return if (/(dummy|tune|kerntest|parse)\.c/);
+  push @files, "$File::Find::name";
+}
diff --git a/config/mingw-pari.nsi b/config/mingw-pari.nsi
new file mode 100755
index 0000000..104be31
--- /dev/null
+++ b/config/mingw-pari.nsi
@@ -0,0 +1,124 @@
+#! /bin/sh
+. config/version
+release=`echo "$pari_release"|sed  's/\./-/g'`
+cat << EOT
+;--- PARI/GP: NullSoft Installer configuration file
+!include "MUI.nsh"
+Name "PARI $pari_release_verbose"
+!define dll "libpari.dll"
+!define PARIver "Pari-$release"
+EOT
+cat << 'EOT'
+!define top ".."
+!define tree "..\mingw"
+AutoCloseWindow false
+
+OutFile "..\${PARIver}.exe"
+InstallDir "$PROGRAMFILES\${PARIver}"
+InstallDirRegKey HKLM "Software\${PARIver}" ""
+
+!define MUI_ABORTWARNING
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_LICENSE "${top}\COPYING"
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+!insertmacro MUI_LANGUAGE "English"
+;--------------------------------
+;Installer Sections
+
+!define uninst "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PARIver}"
+
+Section "pari (required)" SecCopy
+  SetOutPath "$INSTDIR"
+  File "${tree}\bin\gp.exe"
+  File "${tree}\bin\${dll}"
+  File /oname=gphelp.pl "${tree}\bin\gphelp"
+  File /oname=gprc.txt "${tree}\gprc.mingw"
+
+  WriteRegStr HKCU "Software\${PARIver}" "" $INSTDIR
+  WriteRegStr HKLM ${uninst} "DisplayName" "${PARIver} (remove only)"
+  WriteRegStr HKLM ${uninst} "UninstallString" '"$INSTDIR\uninstall.exe"'
+
+  WriteUninstaller "$INSTDIR\Uninstall.exe"
+SectionEnd
+
+Section "Data files" SecGAL
+  SetOutPath "$INSTDIR"
+  File /r "${tree}\data"
+SectionEnd
+
+Section "documentation" SecDOC
+  CreateDirectory "$INSTDIR\etc"
+  SetOutPath "$INSTDIR"
+  File /r "${tree}\share\pari\doc"
+  File /r "${tree}\perl"
+  File /r "${tree}\share\pari\examples"
+  File /oname=gprc_dft.txt "${tree}\gprc.dft"
+SectionEnd
+
+Function .onInstSuccess
+  MessageBox MB_OK "Thank you for using PARI/GP! Double-click on 'gp' to start the calculator.$\r$\n"
+  ExecShell "open" "$INSTDIR"
+FunctionEnd
+
+!define short "$SMPROGRAMS\${PARIver}"
+
+Section "shortcuts" SecSM
+  CreateDirectory "${short}"
+  CreateShortCut "${short}\gp.lnk" "$INSTDIR\gp.exe" "" "$INSTDIR\gp.exe" 0
+  CreateShortCut "${short}\users.lnk" "$INSTDIR\doc\users.pdf" "" "$INSTDIR\doc\users.pdf" 0
+  CreateShortCut "${short}\libpari.lnk" "$INSTDIR\doc\libpari.pdf" "" "$INSTDIR\doc\libpari.pdf" 0
+  CreateShortCut "${short}\tutorial.lnk" "$INSTDIR\doc\tutorial.pdf" "" "$INSTDIR\doc\tutorial.pdf" 0
+  CreateShortCut "${short}\refcard.lnk" "$INSTDIR\doc\refcard.pdf" "" "$INSTDIR\doc\refcard.pdf" 0
+  WriteINIStr "${short}\PARI pages.url" "InternetShortcut" "URL" "http://pari.math.u-bordeaux.fr"
+  CreateShortCut "${short}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+  CreateShortCut "$DESKTOP\PARI.lnk" "$INSTDIR\gp.exe"
+SectionEnd
+
+;--------------------------------
+;Descriptions
+
+LangString DESC_SecCopy ${LANG_ENGLISH} "Copy pari files to application folder."
+LangString DESC_DOC ${LANG_ENGLISH} "Install documentation and online help."
+LangString DESC_EX ${LANG_ENGLISH} "Install sample GP scripts."
+LangString DESC_GAL ${LANG_ENGLISH} "Install Pari package files."
+LangString DESC_SM ${LANG_ENGLISH} "Add PARI shortcuts to Start Menu and desktop."
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecCopy} $(DESC_SecCopy)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecGAL} $(DESC_GAL)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecSM} $(DESC_SM)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecDOC} $(DESC_DOC)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+Section "Uninstall"
+  Delete "$INSTDIR\gp.exe"
+  Delete "$INSTDIR\gphelp.pl"
+  Delete "$INSTDIR\gprc_dft.txt"
+  Delete "$INSTDIR\${dll}"
+
+  RMDir /r "$INSTDIR\perl"
+  RMDir /r "$INSTDIR\etc"
+  RMDir /r "$INSTDIR\doc"
+  RMDir /r "$INSTDIR\examples"
+  RMDir /r "$INSTDIR\data"
+  Delete "$INSTDIR\Uninstall.exe"
+
+  DeleteRegKey HKLM ${uninst}
+  DeleteRegKey /ifempty HKLM "Software\${PARIver}"
+
+  RMDir /r "$SMPROGRAMS\${PARIver}"
+  Delete "$DESKTOP\PARI.lnk"
+  RMDir "$INSTDIR"
+SectionEnd
+EOT
diff --git a/config/mpi.c b/config/mpi.c
new file mode 100644
index 0000000..f68d27f
--- /dev/null
+++ b/config/mpi.c
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+#include <mpi.h>
+
+int main()
+{
+  int pari_MPI_size, pari_MPI_rank;
+  int res = MPI_Init(0, NULL);
+  if (res == MPI_SUCCESS)
+  {
+    MPI_Comm_size(MPI_COMM_WORLD, &pari_MPI_size);
+    MPI_Comm_rank(MPI_COMM_WORLD, &pari_MPI_rank);
+  }
+}
diff --git a/config/myread b/config/myread
new file mode 100755
index 0000000..02245e3
--- /dev/null
+++ b/config/myread
@@ -0,0 +1,26 @@
+if test "$fastread" = yes; then
+  echo "[$dflt]"
+  ans=$dflt
+else
+  if test -n "$dflt"; then echo $n "[$dflt] $c"; fi
+  while :; do
+    read ans
+    case "$ans" in
+    '')
+      ans="$dflt"
+      break
+      ;;
+    !*)
+      ans=`echo "$ans"|cut -c2-`
+      break
+      ;;
+    *)
+      if test -z "$rep"; then break; fi
+      for i in $rep; do
+        if test "$i" = "$ans"; then break 2; fi
+      done
+      echo $n "***  Please try something else : [$rep] $c"
+      ;;
+    esac
+  done
+fi
diff --git a/config/objdir b/config/objdir
new file mode 100755
index 0000000..c9606e2
--- /dev/null
+++ b/config/objdir
@@ -0,0 +1,7 @@
+#! /bin/sh
+
+tmp_host=`config/arch-osname`
+arch=`echo "$tmp_host" | sed -e 's/\(.*\)-.*/\1/'`
+osname=`echo "$tmp_host" | sed -e 's/.*-\(.*\)/\1/'`
+objdir=O$osname-$arch;
+echo $objdir
diff --git a/config/pari.nsi.SH b/config/pari.nsi.SH
new file mode 100644
index 0000000..869ca36
--- /dev/null
+++ b/config/pari.nsi.SH
@@ -0,0 +1,7 @@
+cfg=$config_dir
+obj=$objdir
+file="$obj/pari.nsi"
+if [ -f "$cfg/$osname-pari.nsi" ]; then
+  echo "Extracting $file"
+  "$cfg/$osname-pari.nsi" > "$file"
+fi
diff --git a/config/paricfg.h.SH b/config/paricfg.h.SH
new file mode 100644
index 0000000..9389814
--- /dev/null
+++ b/config/paricfg.h.SH
@@ -0,0 +1,177 @@
+file="$objdir/paricfg.h"
+
+echo Extracting $file
+rm -f $file
+
+case "$optimization" in
+  full) ;;
+  *) debuginfo=" -- $optimization";;
+esac
+
+if test -n "$perl"; then
+  case "$osname" in
+    os2|mingw) gphelp="perl -S gphelp -detex" ;;
+    *) gphelp="\\\"$bindir/gphelp\\\"" ;;
+  esac
+fi
+
+has_stack_check=
+if test "$osname" = "os2" -o "$has_getrlimit" = "yes"; then
+  has_stack_check=yes;
+fi
+
+cat > $file << EOT
+/*  This file was created by Configure. Any change made to it will be lost
+ *  next time Configure is run. */
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+EOT
+
+cat >> $file << EOT
+#define UNIX
+#define GPHELP "$gphelp"
+#define GPDATADIR "$datadir"
+#define SHELL_Q '\\$shell_q'
+
+#define PARIVERSION "GP/PARI CALCULATOR Version ${version}.${patch} (${status})"
+#define PARIINFO "${pretty}${debuginfo}"
+#define PARI_VERSION_CODE ${version_code}
+#define PARI_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#define PARI_VERSION_SHIFT 8
+#define PARI_VCSVERSION "${vcsversion}"
+#define PARI_MT_ENGINE "${thread_engine}"
+
+#define PARI_DOUBLE_FORMAT ${doubleformat}
+EOT
+
+if test -n "$__gnuc__"; then
+  echo "#define GCC_VERSION \"$__gnuc__\"" >> $file
+fi
+
+case $asmarch in
+  none) echo '#define __HAS_NO_ASM__' >> $file;;
+  hppa) echo '#define __HPPA__' >> $file;;
+esac
+
+if test -n "$ASMINLINE"; then
+  echo '#define ASMINLINE' >> $file
+fi
+
+if test -n "$gzip"; then
+  cat >> $file << EOT
+
+/*  Location of GNU gzip program (enables reading of .Z and .gz files). */
+#define GNUZCAT
+#define ZCAT "$gzip -dc"
+
+EOT
+else if test -n "$zcat"; then
+  cat >> $file << EOT
+
+/*  Location of zcat program (enables reading of .Z files). */
+#define ZCAT "$zcat"
+
+EOT
+  fi
+fi
+
+if test "$osname" = "mingw"; then
+cat >> $file << EOT
+
+#undef UNIX
+#define GNUZCAT
+#undef ZCAT
+#define ZCAT "gzip.exe -dc"
+
+EOT
+fi
+
+if test -n "$readline"; then
+  cat >> $file <<EOT
+#define READLINE "$readline_version"
+EOT
+fi
+
+case "$sizeof_long" in
+8) echo '#define LONG_IS_64BIT' >> $file;;
+esac
+
+case "$has_exp2" in
+yes) echo '#define HAS_EXP2' >> $file;;
+esac
+case "$has_log2" in
+yes) echo '#define HAS_LOG2' >> $file;;
+esac
+case "$has_isatty" in
+yes) echo '#define HAS_ISATTY' >> $file;;
+esac
+case "$has_alarm" in
+yes) echo '#define HAS_ALARM' >> $file;;
+esac
+
+
+case "$has_clock_gettime" in
+yes) echo '#define USE_CLOCK_GETTIME 1' >> $file;;
+  *) case "$has_getrusage" in
+     yes) echo '#define USE_GETRUSAGE 1' >> $file;;
+       *) case "$has_times" in
+          yes) echo '#define USE_TIMES 1' >> $file;;
+            *) case "$has_ftime" in
+               yes) echo '#define USE_FTIME 1' >> $file;;
+               esac;;
+          esac;;
+     esac;;
+esac
+
+case $has_sigaction in
+yes) echo '#define HAS_SIGACTION' >> $file;;
+esac
+
+case $has_waitpid in
+yes) echo '#define HAS_WAITPID' >> $file;;
+esac
+
+case $has_getenv in
+yes) echo '#define HAS_GETENV' >> $file;;
+esac
+
+case $has_setsid in
+yes) echo '#define HAS_SETSID' >> $file;;
+esac
+
+case $has_dlopen in
+yes|builtin) cat >> $file << EOT
+#define HAS_DLOPEN
+EOT
+;;
+esac
+
+case $has_stack_check in
+yes) echo '#define STACK_CHECK' >> $file;;
+esac
+
+case $has_vsnprintf in
+yes) echo '#define HAS_VSNPRINTF' >> $file;;
+esac
+
+case $has_TIOCGWINSZ in
+yes) echo '#define HAS_TIOCGWINSZ' >> $file;;
+esac
+
+case $has_strftime in
+yes) echo '#define HAS_STRFTIME' >> $file;;
+esac
+
+case $has_opendir in
+yes) echo '#define HAS_OPENDIR' >> $file;;
+esac
+
+case $has_stat in
+yes) echo '#define HAS_STAT' >> $file;;
+esac
+
+case $enable_tls in
+yes) echo '#define ENABLE_TLS' >> $file;;
+esac
+
+echo '#endif' >> $file
diff --git a/config/pthread.c b/config/pthread.c
new file mode 100644
index 0000000..4026f4d
--- /dev/null
+++ b/config/pthread.c
@@ -0,0 +1,22 @@
+#include <stdlib.h>
+#include <pthread.h>
+
+static __thread long counter;
+
+void *start_routine(void *pt_val)
+{
+  long val = *(long *)pt_val;
+  counter = val+1;
+  return NULL;
+}
+
+int main(void)
+{
+  pthread_t thread;
+  counter = 0;
+  if (pthread_create(&thread, NULL, start_routine, &counter))
+    exit(1);
+  if (pthread_join(thread, NULL))
+    exit(1);
+  return 0;
+}
diff --git a/config/rl_version.c b/config/rl_version.c
new file mode 100644
index 0000000..256d26f
--- /dev/null
+++ b/config/rl_version.c
@@ -0,0 +1,3 @@
+#include <stdio.h>
+#include <readline/readline.h>
+int main(){ printf("%s", rl_library_version); return 0; }
diff --git a/config/settar b/config/settar
new file mode 100755
index 0000000..f0724e1
--- /dev/null
+++ b/config/settar
@@ -0,0 +1,28 @@
+#!/bin/sh
+dir=$1;
+status=$2; # true status (probably 'snapshot')
+STATUS=$3; # status we're being coerced into
+
+case "$STATUS" in
+  alpha|beta) dir="$dir.$STATUS";;
+  snapshot) STATUS="development git-"`git log -1 --pretty=format:%h`;
+            dir=`git describe`;;
+esac
+if test -d $dir; then
+  echo "Remove $dir before building a new release"; exit 1
+fi
+
+tarfile=$dir.tar
+tar cf $tarfile `config/get_MANIFEST`
+mkdir $dir && mv $tarfile $dir
+cd $dir && tar xf $tarfile && rm -f $tarfile && cd ..
+if test "$status" != "$STATUS"; then
+  v=$dir/config/version
+  mv $v $v.old
+  sed -e "s/^stat=.*/stat=\'$STATUS\'/" $v.old > $v
+  rm -f $v.old
+fi
+tar cf $tarfile $dir
+rm -rf $dir
+rm -f $tarfile.gz
+gzip $tarfile
diff --git a/config/setversion b/config/setversion
new file mode 100755
index 0000000..8004125
--- /dev/null
+++ b/config/setversion
@@ -0,0 +1,14 @@
+#!/bin/sh
+# to be run from the Oxxx directory. Called iff $TOP/.git/index exists
+TOP=..
+cfg=$TOP/config
+f=paricfg.h
+rm -f $f.old
+cp $f $f.old
+. $cfg/get_head
+. $cfg/version # need $TOP
+v="GP/PARI CALCULATOR Version ${version}.${patch} (${status})"
+sed -e "s,define PARIVERSION.*,define PARIVERSION \"$v\",;
+        s,define PARI_VCSVERSION.*,define PARI_VCSVERSION \"${vcsversion}\",;
+        s,define PARI_VERSION_CODE.*,define PARI_VERSION_CODE ${version_code}," $f.old > $f
+
diff --git a/config/version b/config/version
new file mode 100644
index 0000000..fba211d
--- /dev/null
+++ b/config/version
@@ -0,0 +1,40 @@
+# Major version number
+VersionMajor='2'
+
+# minor version number
+VersionMinor='7'
+
+# Patch level
+patch='2'
+
+# Version code
+version_code=`expr $VersionMajor \\* 65536 + $VersionMinor \\* 256 + $patch`
+
+# Status: alpha, beta, released, development. Rewritten by config/settar !
+stat='released'
+
+# soname of stable libpari.so is libpari.so.$soname_num
+status="$stat"
+patchlevel_verbose=
+
+case "$stat" in # $stat rewritten by config/settar ?
+  *git-*) patchlevel_verbose="[ $stat ]";;
+  *) if test -d "$TOP/.git"; then
+       t=`git rev-list HEAD 2>/dev/null | wc -l` # ~ svn revision number
+       t=`echo $t | sed -e 's/ //g'` # some broken wc prepend spaces
+       T=`git log -1 --pretty=format:%h` # commit hash
+       if test -z "$t"; then t=0; fi
+       vcsversion=$t-$T
+       status="$stat $vcsversion"
+       patchlevel_verbose="[ $status ]"
+     fi
+esac
+version=$VersionMajor.$VersionMinor
+pari_release="$version.$patch"
+if test `expr $VersionMinor % 2` = 1; then
+  pari_release_verbose="$pari_release (STABLE)"
+  soname_num=`expr '(' $VersionMinor '+' 1 ')' / 2`
+else
+  pari_release_verbose="$pari_release (DEVELOPMENT VERSION)"
+  soname_num=$patch
+fi
diff --git a/doc/INSTALL.tex b/doc/INSTALL.tex
new file mode 100644
index 0000000..db6e53b
--- /dev/null
+++ b/doc/INSTALL.tex
@@ -0,0 +1,4 @@
+\def\TITLE{Installing Pari/GP}
+\input parimacro.tex
+\input appa.tex
+\end
diff --git a/doc/appa.tex b/doc/appa.tex
new file mode 100644
index 0000000..59a69c0
--- /dev/null
+++ b/doc/appa.tex
@@ -0,0 +1,619 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\appendix{Installation Guide for the UNIX Versions}
+
+\def\tocwrite#1{}
+\section{Required tools}
+
+Compiling PARI requires an \kbd{ANSI C} or a \kbd{C++} compiler. If you do
+not have one, we suggest that you obtain the \kbd{gcc/g++} compiler. As for
+all GNU software mentioned afterwards, you can find the most convenient site
+to fetch \kbd{gcc} at the address
+
+\kbd{http://www.gnu.org/order/ftp.html}
+
+\noindent (On Mac OS X, this is also provided in the \kbd{Xcode} tool
+suite; or the lightweight ``Command-line tools for \kbd{Xcode}''.) You can
+certainly compile PARI with a different compiler, but the PARI kernel takes
+advantage of optimizations provided by \kbd{gcc}. This results in at least
+20\% speedup on most architectures.
+
+\misctitle{Optional libraries and programs} The following programs and libraries are useful
+in conjunction with \kbd{gp}, but not mandatory. In any case, get them before
+proceeding if you want the functionalities they provide. All of them are free.
+The download page on our website
+\kbd{http://pari.math.u-bordeaux.fr/download.html} contains pointers on how
+to get these.
+
+  \item GNU \kbd{MP} library. This provides an alternative multiprecision
+kernel, which is faster than PARI's native one, but unfortunately binary
+incompatible, so the resulting PARI library SONAME is libpari-gmp.
+
+  \item GNU \kbd{readline} library. This provides line editing under
+\kbd{gp}, an automatic context-dependent completion, and an editable history
+of commands.
+
+  \item GNU \kbd{emacs} and the \tet{PariEmacs} package. The \kbd{gp}
+calculator can be run in an Emacs buffer, with all the obvious advantages if
+you are familiar with this editor. Note that \kbd{readline} is still useful
+in this case since it provides a better automatic completion than is provided
+by Emacs's GP-mode.
+
+  \item GNU \kbd{gzip/gunzip/gzcat} package enables \kbd{gp} to read
+compressed data.
+
+  \item \kbd{perl} provides extended online help (full text from the
+manual) about functions and concepts. The script handling this online help
+can be used under \kbd{gp} or independently.
+
+\section{Compiling the library and the \kbd{gp} calculator}
+
+\subsec{Basic configuration} Type
+
+\kbd{./Configure}
+
+\noindent in the toplevel directory. This attempts to configure PARI/GP
+without outside help. Note that if you want to install the end product in
+some nonstandard place, you can use the \kbd{--prefix} option, as in
+
+\kbd{./Configure --prefix=}\var{/an/exotic/directory}
+
+\noindent (the default prefix is \kbd{/usr/local}). For example, to build a
+package for a Linux distribution, you may want to use
+
+\kbd{./Configure --prefix=/usr}
+
+This phase extracts some files and creates a \var{build directory}, names
+\kbd{O}\var{osname}\kbd{-}\var{arch}, where the
+object files and executables will be built. The
+\var{osname} and \var{arch} components depends on your architecture and
+operating system, thus you can build PARI/GP for several different machines
+from the same source tree (the builds are independent and can be done
+simultaneously).
+
+Decide whether you agree with what \kbd{Configure} printed on your screen, in
+particular the architecture, compiler and optimization flags. Look for
+messages prepended by \kbd{\#\#\#}, which report genuine problems.
+Look especially for the \kbd{gmp}, \kbd{readline} and \kbd{X11} libraries,
+and the \kbd{perl} and \kbd{gunzip} (or \kbd{zcat}) binaries.
+If anything should have been found and was not, consider that \kbd{Configure}
+failed and follow the instructions in section~3.
+
+The \kbd{Configure} run creates a file \kbd{config.log} in the build
+directory, which contains debugging information --- in particular, all
+messages from compilers ---  that may help diagnose problems. This file
+is erased and recreated from scratch each time \kbd{Configure} is run.
+
+\subsec{Advanced configuration}
+\kbd{Configure} accepts many other flags, and you may use any number of them
+to build quite a complicated configuration command. See \kbd{Configure
+--help} for a complete list. In particular, there are sets of flags related
+to GNU MP (\kbd{--with-gmp*}) and GNU readline library
+(\kbd{--with-readline*}).
+
+Here, we focus on the non-obvious ones:
+
+\kbd{--tune}: fine tunes the library for the host used for compilation. This
+adjusts thresholds by running a large number of comparative tests and creates
+a file \kbd{tune.h} in the build directory, that will be used from now on,
+overriding the ones in \kbd{src/kernel/none/} and \kbd{src/kernel/gmp/}. It
+will take a while: about 30 minutes. Expect a small
+performance boost, perhaps a 10\% speed increase
+compared to default settings.
+
+If you are using GMP, tune it first, then PARI. Make sure you tune PARI on
+the machine that will actually run your computations. Do not use a heavily
+loaded machine for tunings.
+
+You may speed up the compilation by using a parallel make:
+\bprog
+  env MAKE="make -j4" Configure --tune
+ at eprog
+
+\kbd{--graphic=}\var{lib}: enables a particular graphic library.
+The default is \kbd{X11} on most platforms, but PARI can use
+\kbd{Qt}, \kbd{fltk}, \kbd{ps}, or \kbd{win32} (GDI).
+
+\kbd{--time=}\var{function}: chooses a timing function. The default usually
+works fine, however you can use a different one that better fits your needs.
+PARI can use \kbd{getrusage}, \kbd{clock\_gettime}, \kbd{times} or
+\kbd{ftime} as timing functions. (Not all timing functions are available on
+all platforms.) The three first functions give timings in terms of CPU usage
+of the current task, approximating the complexity of the algorithm. The last
+one, \kbd{ftime}, gives timings in terms of absolute (wall-clock) time.
+Moreover, the \kbd{clock\_gettime} function is more precise, but much slower
+(at the time of this writing), than \kbd{getrusage} or \kbd{times}.
+
+
+The remaining options are specific to parallel programming. We provide an
+\emph{Introduction to parallel GP programming} in the file
+\kbd{doc/parallel.dvi}, and to multi-threaded \kbd{libpari} programs
+in Appendix~D. Beware that these options change the library ABI:
+
+\kbd{--mt=}\var{engine}: specify the engine used for parallel computations.
+Supported value are
+
+\item single: (default) no parallellism.
+
+\item pthread: use POSIX threads. This is well-suited for multi-core systems.
+Setting this option also set \kbd{--enable-tls}, see below. This option
+requires the pthread library.
+For benchmarking, it is often useful to set \kbd{--time=ftime} so that GP
+report wall-clock instead of the sum of the time spent by each thread.
+
+\item mpi: use the MPI interface to parallelism.  This allows to take
+advantage of clusters using MPI. This option requires a MPI library.
+It is usually necessary to set the environment variable \kbd{CC} to
+\kbd{mpicc}.
+
+\kbd{--enable-tls}: build the thread-safe version of the library. Implied by
+\kbd{--mt=pthread}. This tends to slow down the \emph{shared} library
+\kbd{libpari.so} by about $15\%$, so you probably want to use the static
+library \kbd{libpari.a} instead.
+
+\subsec{Compilation} To compile the \kbd{gp} binary and build the
+documentation, type
+
+\kbd{make all}
+
+\noindent To only compile the \kbd{gp} binary, type
+
+\kbd{make gp}
+
+\noindent in the toplevel directory. If your \kbd{make} program supports
+parallel make, you can speed up the process by going to the build
+directory that \kbd{Configure} created and doing a parallel make here, for
+instance \kbd{make -j4} with GNU make. It should even work from the toplevel
+directory.
+
+\subsec{Basic tests}
+
+To test the binary, type \kbd{make bench}. This runs a quick series of
+tests, for a few seconds on modern machines.
+
+In many cases, this will also build a different binary (named \kbd{gp-sta} or
+\kbd{gp-dyn}) linked in a slightly different way and run the tests with both.
+(In exotic configurations, one may pass all the tests while the other fails
+and we want to check for this.) To test only the default binary, use
+\kbd{make dobench} which starts the bench immediately.
+
+If a \kbd{[BUG]} message shows up, something went wrong. The testing utility
+directs you to files containing the differences between the test output and
+the expected results. Have a look and decide for yourself if something is
+amiss. If it looks like a bug in the Pari system, we would appreciate a
+report, see the last section.
+
+\subsec{Cross-compiling}
+
+When cross-compiling, you can set the environment variable \kbd{RUNTEST} to a
+program that is able to run the target binaries, e.g. an emulator. It will be
+used for both the \kbd{Configure} tests and \kbd{make bench}.
+
+\section{Troubleshooting and fine tuning}
+In case the default \kbd{Configure} run fails miserably, try
+
+\kbd{./Configure -a}
+
+\noindent (interactive mode) and answer all the questions: there are about 30
+of them, and default answers are provided. If you accept all default answers,
+\kbd{Configure} will fail just the same, so be wary. In any case, we would
+appreciate a bug report (see the last section).
+
+\subsec{Installation directories} The precise default destinations are as
+follows: the \kbd{gp} binary, the scripts \kbd{gphelp} and \kbd{tex2mail} go
+to \kbd{\$prefix/bin}. The pari library goes to \kbd{\$prefix/lib} and
+include files to \kbd{\$prefix/include/pari}. Other system-dependent data go
+to \kbd{\$prefix/lib/pari}.
+
+Architecture independent files go to various subdirectories of
+\kbd{\$share\_prefix}, which defaults to \kbd{\$prefix/share}, and can be
+specified via the \kbd{--share-prefix} argument. Man pages go into
+\kbd{\$share\_prefix/man}, and other system-independent data
+under \kbd{\$share\_prefix/pari}: documentation,
+sample GP scripts and C code, extra packages like \kbd{elldata} or
+\kbd{galdata}.
+
+\noindent You can also set directly \kbd{--bindir} (executables),
+\kbd{--libdir} (library), \kbd{--includedir} (include files), \kbd{--mandir}
+(manual pages), \kbd{--datadir} (other architecture-independent data), and
+finally \kbd{--sysdatadir} (other architecture-dependent data).
+
+\subsec{Environment variables} \kbd{Configure} lets the following environment
+variable override the defaults if set:
+
+\kbd{CC}: C compiler.
+
+\kbd{DLLD}: Dynamic library linker.
+
+\kbd{LD}: Static linker.
+
+\noindent For instance, \kbd{Configure} may avoid \kbd{/bin/cc} on some
+architectures due to various problems which may have been fixed in your
+version of the compiler. You can try
+
+\kbd{env CC=cc Configure}
+
+\noindent and compare the benches. Also, if you insist on using a \kbd{C++}
+compiler and run into trouble with a fussy \kbd{g++}, try to use
+\kbd{g++ -fpermissive}.
+
+
+\noindent The contents of the following variables are \emph{appended} to the
+values computed by \kbd{Configure}:
+
+\kbd{CFLAGS}: Flags for \kbd{CC}.
+
+\kbd{CPPFLAGS}: Flags for \kbd{CC} (preprocessor).
+
+\kbd{LDFLAGS}: Flags for \kbd{LD}.
+
+\noindent The contents of the following variables are \emph{prepended} to
+the values computed by \kbd{Configure}:
+
+\kbd{C\_INCLUDE\_PATH} is prepended to the list of directories
+searched for include files. Note that adding \kbd{-I} flags to
+\kbd{CFLAGS} is not enough since \kbd{Configure} sometimes
+relies on finding the include files and parsing them, and it does not
+parse \kbd{CFLAGS} at this time.
+
+\kbd{LIBRARY\_PATH} is prepended to the list of directories
+searched for libraries.
+
+\noindent You may disable inlining by adding \kbd{-DDISABLE\_INLINE} to
+\kbd{CFLAGS}, and prevent the use of the \kbd{volatile} keyword with
+\kbd{-DDISABLE\_VOLATILE}.
+
+\subsec{Debugging/profiling}: If you also want to debug the PARI library,
+
+\kbd{Configure -g}
+
+\noindent creates a directory \kbd{O$xxx$.dbg} containing a special
+\kbd{Makefile} ensuring that the \kbd{gp} and PARI library built there is
+suitable for debugging. If you want to
+profile \kbd{gp} or the library, using \kbd{gprof} for instance,
+
+\kbd{Configure -pg}
+
+\noindent will create an \kbd{O$xxx$.prf} directory where a suitable version
+of PARI can be built.
+
+The \kbd{gp} binary built above with \kbd{make all} or \kbd{make gp} is
+optimized. If you have run \kbd{Configure -g} or \kbd{-pg} and want to build
+a special purpose binary, you can \kbd{cd} to the \kbd{.dbg} or \kbd{.prf}
+directory and type \kbd{make gp} there. You can also invoke \kbd{make gp.dbg}
+or \kbd{make gp.prf} directly from the toplevel.
+
+\subsec{Multiprecision kernel} The kernel can be specified via the
+
+\kbd{--kernel=\emph{fully\_qualified\_kernel\_name}}
+
+\noindent switch. The PARI kernel consists of two levels: Level 0 (operation
+on words) and Level 1 (operation on multi-precision integers and reals),
+which can take the following values.
+
+Level 0: \kbd{auto} (as detected), \kbd{none} (portable C) or
+one of the assembler micro-kernels
+\bprog
+  alpha
+  hppa hppa64
+  ia64
+  ix86 x86_64
+  m68k
+  ppc ppc64
+  sparcv7 sparcv8_micro sparcv8_super
+ at eprog
+
+Level 1: \kbd{auto} (as detected), \kbd{none} (native code only), or \kbd{gmp}
+
+\noindent\item A fully qualified kernel name is of the form
+\kbd{\var{Level0}-\var{Level1}}, the default value being \kbd{auto-auto}.
+
+\noindent\item A \emph{name} not containing a dash '\kbd{-}' is an alias
+for a fully qualified kernel name. An alias stands for
+\kbd{\emph{name}-none}, but \kbd{gmp} stands for \kbd{auto-gmp}.
+
+\subsec{Problems related to readline}
+\kbd{Configure} does not try very hard to find the \kbd{readline} library and
+include files. If they are not in a standard place, it will not find them.
+You can invoke \kbd{Configure} with one of the following arguments:
+
+   \kbd{--with-readline[=\emph{prefix to \kbd{lib/libreadline}.xx and
+\kbd{include/readline.h}}]}
+
+   \kbd{--with-readline-lib=\emph{path to \kbd{libreadline}.xx}}
+
+   \kbd{--with-readline-include=\emph{path to \kbd{readline.h}}}
+
+\misctitle{Known problems}
+
+\item on Linux: Linux distributions have separate \kbd{readline} and
+\kbd{readline-devel} packages. You need both of them installed to
+compile gp with readline support. If only \kbd{readline} is installed,
+\kbd{Configure} will complain. \kbd{Configure} may also complain about a
+missing libncurses.so, in which case, you have to install the
+\kbd{ncurses-devel} package (some distributions let you install
+\kbd{readline-devel} without \kbd{ncurses-devel}, which is a bug in
+their package dependency handling).
+
+\item on OS X.4 or higher: these systems comes equipped with a fake
+\kbd{readline}, which is not sufficient for our purpose. As a result, gp is
+built without readline support. Since \kbd{readline} is not trivial to
+install in this environment, a step by step solution can be found in the PARI
+FAQ, see
+\bprog
+  http://pari.math.u-bordeaux.fr/
+ at eprog
+
+\subsec{Testing}
+
+\subsubsec{Known problems} if \kbd{BUG} shows up in \kbd{make bench}
+
+\item \kbd{program}: the GP function \kbd{install} may not be available on
+your platform, triggering an error message (``not yet available for this
+architecture'').
+
+\item If when running \kbd{gp-dyn}, you get a message of the form
+
+\kbd{ld.so: warning: libpari.so.$xxx$ has older revision than expected $xxx$}
+
+\noindent (possibly followed by more errors), you already have a dynamic PARI
+library installed \emph{and} a broken local configuration. Either remove the
+old library or unset the \kbd{LD\_LIBRARY\_PATH} environment variable. Try to
+disable this variable in any case if anything \emph{very} wrong occurs with
+the \kbd{gp-dyn} binary, like an Illegal Instruction on startup. It does not
+affect \kbd{gp-sta}.
+
+\item Some implementations of the \kbd{diff} utility (on HPUX for
+instance) output \kbd{No differences encountered} or some similar
+message instead of the expected empty input, thus producing a spurious
+\kbd{[BUG]} message.
+
+\subsubsec{Some more testing} [{\sl Optional\/}]
+
+You can test \kbd{gp} in compatibility mode with \kbd{make test-compat}. If
+you want to test the graphic routines, use \kbd{make test-ploth}. You will
+have to click on the mouse button after seeing each image. There will be
+eight of them, probably shown twice (try to resize at least one of them as a
+further test).
+
+The \kbd{make bench}, \kbd{make test-compat} and \kbd{make test-ploth} runs
+all produce a Postscript file \kbd{pari.ps} in \kbd{O$xxx$} which you can
+send to a Postscript printer. The output should bear some similarity to the
+screen images.
+
+\subsubsec{Heavy-duty testing} [{\sl Optional\/}]
+There are a few extra tests which should be useful only for developers.
+
+\kbd{make test-kernel} checks whether the low-level kernel seems to work,
+and provides simple diagnostics if it does not. Only useful if \kbd{make
+bench} fails horribly, e.g.~things like \kbd{1+1} do not work.
+
+\kbd{make test-all} runs all available test suites. Thorough, but slow. Some
+of the tests require extra packages (\kbd{elldata}, \kbd{galdata}, etc.)
+to be available. If you want to test such an extra package \emph{before}
+\kbd{make install} (which would install it to its final location, where
+\kbd{gp} expects to find it), run
+\bprog
+  env GP_DATA_DIR=$PWD/data make test-all
+ at eprog\noindent from the PARI toplevel directory, otherwise the test will
+fail.
+
+\kbd{make test-io} tests writing to and reading from  files. It requires
+a working \kbd{system()} command (fails on Windows + MingW).
+
+\kbd{make test-time} tests absolute and relative timers. This test has a
+tendency to fail when the machine is heavily loaded or if the granularity
+of the chosen system timer is bigger than 2ms. Try it a few times before
+reporting a problem.
+
+\section{Installation} When everything looks fine, type
+
+\kbd{make install}
+
+\noindent You may have to do this with superuser privileges, depending on the
+target directories. (Tip for MacOS X beginners: use \kbd{sudo make install}.)
+In this case, it is advised to type \kbd{make all} first to avoid running
+unnecessary commands as \kbd{root}.
+
+\misctitle{Caveat} Install directories are created honouring your \kbd{umask}
+settings: if your umask is too restrictive, e.g.~\kbd{077}, the installed
+files will not be world-readable. (Beware that running \kbd{sudo} may change
+your user umask.)
+
+This installs in the directories chosen at \kbd{Configure} time the default
+\kbd{gp} executable (probably \kbd{gp-dyn}) under the name \kbd{gp}, the
+default PARI library (probably \kbd{libpari.so}), the necessary include
+files, the manual pages, the documentation and help scripts.
+
+To save on disk space, you can manually \kbd{gzip} some of the documentation
+files if you wish: \kbd{usersch*.tex} and all \kbd{dvi} files (assuming your
+\kbd{xdvi} knows how to deal with compressed files); the online-help system
+can handle it.
+
+\subsec{Static binaries and libraries}
+By default, if a dynamic library \kbd{libpari.so} can be built, the \kbd{gp}
+binary we install is \kbd{gp-dyn}, pointing to \kbd{libpari.so}. On the other
+hand, we can build a \kbd{gp} binary into which the \kbd{libpari} is
+statically linked (the library code is copied into the binary); that binary
+is not independent of the machine it was compiled on, and may still refer to
+other dynamic libraries than \kbd{libpari}.
+
+You may want to compile your own programs in the same way, using the static
+\kbd{libpari.a} instead of \kbd{libpari.so}. By default this static library
+\kbd{libpari.a} is not created. If you want it as well, use the target
+\kbd{make install-lib-sta}. You can install a statically linked \kbd{gp} with
+the target \kbd{make install-bin-sta}. As a rule, programs linked statically
+(with \kbd{libpari.a}) may be slightly faster (about 5\% gain, possibly
+up to 20\% when using \kbd{pthreads}), but use more disk space and take more
+time to compile. They are also harder to upgrade: you will have to recompile
+them all instead of just installing the new dynamic library. On the other
+hand, there is no risk of breaking them by installing a new pari library.
+
+\subsec{Extra packages} The following optional packages endow PARI with some
+extra capabilities:
+
+\item \kbd{elldata}: This package contains the elliptic curves in
+John Cremona's database. It is needed by the functions \kbd{ellidentify},
+\kbd{ellsearch}, \kbd{forell} and can be used by \kbd{ellinit} to initialize a curve given by its standard code.
+
+\item \kbd{galdata}: The default \kbd{polgalois} function can only
+compute Galois groups of polynomials of degree less or equal to 7. Install
+this package if you want to handle polynomials of degree bigger than 7 (and
+less than 11).
+
+\item \kbd{seadata}: This package contains the database of modular
+polynomials extracted from the ECHIDNA databases and computed by David R.
+Kohel. It is needed by the functions \kbd{ellap} and \kbd{ellgroup} for
+primes larger than $10^{20}$.
+
+\item \kbd{galpol}: This package contains the GALPOL database of polynomials
+defining Galois extensions of the rationals, accessed by \kbd{galoisgetpol}.
+
+\medskip
+
+To install package \emph{pack}, you need to fetch the separate archive:
+\emph{pack}\kbd{.tgz} which you can download from the \kbd{pari} server.
+Copy the archive in the PARI toplevel directory, then extract its
+contents; these will go to \kbd{data/\emph{pack}/}. Typing \kbd{make
+install} installs all such packages.
+
+\subsec{The \kbd{GPRC} file} Copy the file \kbd{misc/gprc.dft} (or
+\kbd{gprc.dos} if you are using \kbd{GP.EXE}) to \kbd{\$HOME/.gprc}. Modify
+it to your liking. For instance, if you are not using an ANSI terminal,
+remove control characters from the \kbd{prompt} variable. You can also
+enable colors.
+
+If desired, read \kbd{\$datadir/misc/gpalias}  from the \kbd{gprc}
+file, which provides some common shortcuts to lengthy names; fix the path in
+gprc first. (Unless you tampered with this via Configure, \kbd{datadir} is
+\kbd{\$prefix/share/pari}.) If you have superuser privileges and want to
+provide system-wide defaults, copy your customized \kbd{.gprc} file to
+\kbd{/etc/gprc}.
+
+In older versions, \kbd{gphelp} was hidden in pari lib directory and was not
+meant to be used from the shell prompt, but not anymore. If gp complains it
+cannot find \kbd{gphelp}, check whether your \kbd{.gprc} (or the system-wide
+\kbd{gprc}) does contain explicit paths. If so, correct them according to the
+current \kbd{misc/gprc.dft}.
+
+\section{Getting Started}
+
+\subsec{Printable Documentation} Building gp with \kbd{make all} also builds
+its documentation. You can also type directly \kbd{make doc}. In any case,
+you need a working (plain) \TeX\ installation.
+
+After that, the \kbd{doc} directory contains various \kbd{dvi} files:
+\kbd{libpari.dvi} (manual for the PARI library), \kbd{users.dvi} (manual
+for the \kbd{gp} calculator), \kbd{tutorial.dvi} (a tutorial), and
+\kbd{refcard.dvi} (a reference card for GP). You can send these files to your
+favorite printer in the usual way, probably via \kbd{dvips}. The reference
+card is also provided as a \kbd{PostScript} document, which may be easier to
+print than its \kbd{dvi} equivalent (it is in Landscape orientation and
+assumes A4 paper size).
+
+\noindent If \kbd{pdftex} is part of your \TeX\ setup, you can produce these
+documents in PDF format, which may be more convenient for online browsing
+(the manual is complete with hyperlinks); type
+
+\kbd{make docpdf}
+
+\noindent All these documents are available online from PARI home page
+(see the last section).
+
+\subsec{C programming} Once all libraries and include files are installed,
+you can link your C programs to the PARI library. A sample makefile
+\kbd{examples/Makefile} is provided to illustrate the use of the various
+libraries. Type \kbd{make all} in the \kbd{examples} directory to see how
+they perform on the \kbd{extgcd.c} program, which is commented in the
+manual.
+
+This should produce a statically linked binary \kbd{extgcd-sta}
+(standalone), a dynamically linked binary \kbd{extgcd-dyn} (loads libpari
+at runtime) and a shared library \kbd{libextgcd}, which can be used from
+\kbd{gp} to \kbd{install} your new \kbd{extgcd} command.
+
+The standalone binary should be bulletproof, but the other two may fail
+for various reasons. If when running \kbd{extgcd-dyn}, you get a message
+of the form ``DLL not found'', then stick to statically linked binaries
+or look at your system documentation to see how to indicate at linking
+time where the required DLLs may be found! (E.g.~on Windows, you will
+need to move \kbd{libpari.dll} somewhere in your \kbd{PATH}.)
+
+\subsec{GP scripts} Several complete sample GP programs are also given in
+the \kbd{examples} directory, for example Shanks's SQUFOF factoring method,
+the Pollard rho factoring method, the Lucas-Lehmer primality test for
+Mersenne numbers and a simple general class group and fundamental unit
+algorithm. See the file \kbd{examples/EXPLAIN} for some explanations.
+
+\subsec{The PARI Community} PARI's home page at the address
+\bprog
+  http://pari.math.u-bordeaux.fr/
+ at eprog\noindent
+maintains an archive of mailing lists dedicated to PARI, documentation
+(including Frequently Asked Questions), a download area and our Bug Tracking
+System (BTS). Bug reports should be submitted online to the BTS, which may be
+accessed from the navigation bar on the home page or directly at
+\bprog
+  http://pari.math.u-bordeaux.fr/Bugs/
+ at eprog\noindent
+Further information can be found at that address but, to report a
+configuration problem, make sure to include the relevant \kbd{*.dif} files in
+the \kbd{O$xxx$} directory and the file \kbd{pari.cfg}.
+\smallskip
+
+There are a number of mailing lists devoted to PARI/GP, and most feedback
+should be directed there. Instructions and archives can be consulted at
+\bprog
+  http://pari.math.u-bordeaux1.fr/lists-index.html
+ at eprog\noindent The most important are:
+
+\item \kbd{pari-announce} (\emph{read-only}): to announce major version
+changes. You cannot write to this one, but you should probably subscribe.
+
+\item \kbd{pari-dev}: for everything related to the development of PARI,
+including suggestions, technical questions or patch submissions. Bug reports
+can be discussed here, but as a rule it is better to submit them directly
+to the BTS.
+
+\item \kbd{pari-users}: for everything else.
+
+\noindent You may send an email to the last two without being subscribed.
+To subscribe, send an message respectively to
+\def\@{@}
+\bprog
+  pari-announce-request@@pari.math.u-bordeaux.fr
+     pari-users-request@@pari.math.u-bordeaux.fr
+       pari-dev-request@@pari.math.u-bordeaux.fr
+ at eprog\noindent with the word \kbd{subscribe} in the \kbd{Subject:}.
+You can also write to us at the address
+\bprog
+  pari@@math.u-bordeaux.fr
+ at eprog\noindent but we cannot promise you will get an individual answer.
+\smallskip
+
+If you have used PARI in the preparation of a paper, please cite it in the
+following form (BibTeX format):
+
+\bprog
+@@preamble{\usepackage{url}}
+@@manual{PARI2,
+    organization = "{The PARI~Group}",
+    title        = "{PARI/GP version @vers}",
+    year         = 2014,
+    address      = "Bordeaux",
+    note         = "available from \url{http://pari.math.u-bordeaux.fr/}"
+}
+ at eprog
+\smallskip
+
+\noindent In any case, if you like this software, we would be indebted if you
+could send us an email message giving us some information about yourself and
+what you use PARI for.
+
+\medskip
+{\bf Good luck and enjoy!}
+\vfill\eject
diff --git a/doc/appb.tex b/doc/appb.tex
new file mode 100644
index 0000000..251fa5e
--- /dev/null
+++ b/doc/appb.tex
@@ -0,0 +1,36 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\appendix{A Sample program and Makefile}
+
+We assume that you have installed the PARI library and include files as
+explained in Appendix A or in the installation guide. If you chose
+differently any of the directory names, change them accordingly in the
+Makefiles.
+
+If the program example that we have given is in the file \kbd{extgcd.c}, then
+a sample Makefile might look as follows. Note that the actual file {\tt
+examples/Makefile} is more elaborate and you should have a look at it if you
+intend to use {\tt install()} on custom made functions, see
+\secref{se:install}.
+
+\bprog
+CC = cc
+INCDIR = @includedir
+LIBDIR = @libdir
+CFLAGS = -O -I$(INCDIR) -L$(LIBDIR)
+
+all:  extgcd
+
+extgcd:  extgcd.c
+      $(CC) $(CFLAGS) -o extgcd extgcd.c -lpari -lm
+ at eprog
+
+\noindent We then give the listing of the program \kbd{examples/extgcd.c}
+seen in detail in \secref{se:prog}.
+
+\bprogfile{../examples/extgcd.c}
+\vfill\eject
diff --git a/doc/appd.tex b/doc/appd.tex
new file mode 100644
index 0000000..f594373
--- /dev/null
+++ b/doc/appd.tex
@@ -0,0 +1,87 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\appendix{PARI and threads}
+
+To use PARI in multi-threaded programs, you must configure it using
+\kbd{Configure --enable-tls}. Your system must implement the \kbd{\_\_thread}
+storage class. As a major side effect, this breaks the \kbd{libpari} ABI: the
+resulting library is not compatible with the old one, and \kbd{-tls} is
+appended to the PARI library \kbd{soname}. On the other hand, this library is
+now thread-safe.
+
+PARI provides some functions to set up PARI subthreads\sidx{threads}. In our
+model, each concurrent thread needs its own PARI stack. The following scheme
+is used:
+
+\noindent Child thread:
+\bprog
+void *child_thread(void *arg)
+{
+  GEN data = pari_thread_start((struct pari_thread*)arg);
+  GEN result = ...; /* Compute result from data */
+  pari_thread_close();
+  return (void*)result;
+}
+ at eprog
+\noindent Parent thread:
+\bprog
+  pthread_t th;
+  struct pari_thread pth;
+  GEN data, result;
+
+  pari_thread_alloc(&pth, s, data);
+  pthread_create(&th, NULL, &child_thread, (void*)&pth); /* start child */
+  ... /* do stuff in parent */
+  pthread_join(th, (void*)&result); /* wait until child terminates */
+  result = gcopy(result); /* copy result from thread stack to main stack */
+  pari_thread_free(&pth); /* ... and clean up */
+ at eprog
+
+\fun{void}{pari_thread_alloc}{struct pari_thread *pth, size_t s, GEN arg}
+Allocate a PARI stack of size \kbd{s} and associate it, together with the
+argument \kbd{arg}, with the PARI thread data \kbd{pth}.
+
+\fun{void}{pari_thread_free}{struct pari_thread *pth}
+Free the PARI stack associated with the PARI thread data \kbd{pth}. This
+is called after the child thread terminates, i.e.~after
+\tet{pthread_join} in the parent. Any \kbd{GEN} objects returned by the
+child in the thread stack need to be saved before running this command.
+
+\fun{void}{pari_thread_init}{void}
+Initialize the thread-local PARI data structures. This function is called by
+\kbd{pari\_thread\_start}.
+
+\fun{GEN}{pari_thread_start}{struct pari_thread *t}
+Initialize the thread-local PARI data structures and set up the thread stack
+using the PARI thread data \kbd{pth}. This function returns the thread
+argument \kbd{arg} that was given to \kbd{pari\_thread\_alloc}.
+
+\fun{void}{pari_thread_close}{void}
+Free the thread-local PARI data structures, but keeping the thread stack, so
+that a \kbd{GEN} returned by the thread remains valid.
+
+\noindent Under this model, some PARI states are reset in new threads. In
+particular
+
+\item the random number generator is reset to the starting seed;
+
+\item the system stack exhaustion checking code, meant to catch infinite
+recursions, is disabled (use \kbd{pari\_stackcheck\_init()} to reenable it);
+
+\item cached real constants (returned by \kbd{mppi}, \kbd{mpeuler} and
+\kbd{mplog2}) are not shared between threads and will be recomputed as
+needed;
+
+\noindent The following sample program can be compiled using
+\bprog
+    cc thread.c -o thread.o -lpari -lpthread
+ at eprog\noindent
+(Add \kbd{-I/-L} paths as necessary.)
+
+\noindent\bprogfile{../examples/thread.c}
+
+\vfill\eject
diff --git a/doc/develop.tex b/doc/develop.tex
new file mode 100644
index 0000000..03e7151
--- /dev/null
+++ b/doc/develop.tex
@@ -0,0 +1,851 @@
+\def\TITLE{Developer's Guide to the PARI library}
+\input parimacro.tex
+
+% START TYPESET
+\begintitle
+\vskip 2.5truecm
+\centerline{\mine Developer's Guide}
+\vskip 1.truecm
+\centerline{\mine to}
+\vskip 1.truecm
+\centerline{\mine the PARI library}
+\vskip 1.truecm
+\centerline{\sectiontitlebf (version \vers)}
+\vskip 1.truecm
+\authors
+\endtitle
+
+\copyrightpage
+\tableofcontents
+\openin\std=develop.aux
+\ifeof\std
+\else
+  \input develop.aux
+\fi
+\chapno=0
+
+\chapter{Work in progress}
+
+This draft documents private internal functions and structures for hard-core
+PARI developers. Anything in here is liable to change on short notice. Don't
+use anything in the present document, unless you are implementing new
+features for the PARI library. Try to fix the interfaces before using them,
+or document them in a better way.
+If you find an undocumented hack somewhere, add it here.
+
+Hopefully, this will eventually document everything that we buried in
+\kbd{paripriv.h} or even more private header files like \kbd{anal.h}.
+Possibly, even implementation choices! Way to go.
+
+\section{The type \typ{CLOSURE}}\kbdsidx{t_CLOSURE}\sidx{closure}
+This type holds closures and functions in compiled form, so is deeply
+linked to the internals of the GP compiler and evaluator.
+The length of this type can be $6$, $7$ or $8$ depending whether the
+object is an ``inline closure'', a ``function'' or a ``true closure''.
+
+A function is a regular GP function. The GP input line is treated as a
+function of arity $0$.
+
+A true closure is a GP function defined in a non-empty lexical context.
+
+An inline closure is a closure that appears in the code without
+the preceding \kbd{->} token. They are generally associated to the prototype
+code 'E' and 'I'. Inline closures can only exist as data of other closures,
+see below.
+
+In the following example,
+\bprog
+f(a=Euler)=x->sin(x+a);
+g=f(Pi/2);
+plot(x=0,2*Pi,g(x))
+ at eprog\noindent
+\kbd{f} is a function, \kbd{g} is a true closure and both \kbd{Euler} and
+\kbd{g(x)} are inline closures.
+
+This type has a second codeword \kbd{z[1]}, which is the arity of the
+function or closure. This is zero for inline closures. To access it, use
+
+\fun{long}{closure_arity}{GEN C}
+
+\item \kbd{z[2]} points to a \typ{STR} which holds the opcodes. To access it, use
+
+\fun{GEN}{closure_get_code}{GEN C}.
+
+\fun{const char *}{closure_codestr}{GEN C} returns as an array of \kbd{char}
+starting at $1$.
+
+\item \kbd{z[3]} points to a \typ{VECSMALL} which holds the operands of the opcodes.
+To access it, use
+
+\fun{GEN}{closure_get_oper}{GEN C}
+
+\item \kbd{z[4]} points to a \typ{VEC} which hold the data referenced by the
+\kbd{pushgen} opcodes, which can be \typ{CLOSURE}, and in particular
+inline closures. To access it, use
+
+\fun{GEN}{closure_get_data}{GEN C}
+
+\item \kbd{z[5]} points to a \typ{VEC} which hold extra data needed for
+error-reporting and debugging. See \secref{se:dbgclosure} for details.
+To access it, use
+
+\fun{GEN}{closure_get_dbg}{GEN C}
+
+Additionally, for functions and true closures,
+
+\item \kbd{z[6]} usually points to a \typ{VEC} with two components which are \typ{STR}.
+The first one displays the list of arguments of the closure without the
+enclosing parentheses, the second one the GP code of the function at the
+right of the \kbd{->} token. They are used to display the closure, either in
+implicit or explicit form. However for closures that were not generated from GP
+code, \kbd{z[6]} can point to a \typ{STR} instead. To access it, use
+
+\fun{GEN}{closure_get_text}{GEN C}
+
+Additionally, for true closure,
+
+\item \kbd{z[7]} points to a \typ{VEC} which holds the values of all lexical
+variables defined in the scope the closure was defined. To access it, use
+
+\fun{GEN}{closure_get_frame}{GEN C}
+
+\subsec{Debugging information in closure}\label{se:dbgclosure}
+
+Every \typ{CLOSURE} object \kbd{z} has a component \kbd{dbg=z[5]}
+which which hold extra data needed for error-reporting and debugging.
+The object \kbd{dbg} is a \typ{VEC} with $3$ components:
+
+\kbd{dbg[1]} is a \typ{VECSMALL} of the same length than \kbd{z[3]}. For each
+opcode, it holds the position of the corresponding GP source code in the
+strings stored in \kbd{z[6]} for function or true closures, positive indices
+referring to the second strings, and negative indices referring to the first
+strings, the last element being indexed as $-1$. For inline closures, the
+string of the parent function or true closure is used instead.
+
+\kbd{dbg[2]} is a \typ{VECSMALL} that lists opcodes index where new lexical
+local variables are created. The value $0$ denotes the position before the
+first offset and variables created by the prototype code 'V'.
+
+\kbd{dbg[3]} is a \typ{VEC} of \typ{VECSMALL}s that give the list of
+\kbd{entree*} of the lexical local variables created at a given index in
+\kbd{dbg[2]}.
+
+\section{The type \typ{LIST}}\kbdsidx{t_LIST}\sidx{list} This type needs to go
+through various hoops to support GP's inconvenient memory model. Don't
+use \typ{LIST}s in pure library mode, reimplement ordinary lists! This
+dynamic type is implemented by a \kbd{GEN} of length 3: two codewords and a
+vector containing the actual entries. In a normal setup (a finished list,
+ready to be used),
+
+\item the vector is malloc'ed, so that it can be realloc'ated without moving
+the parent \kbd{GEN}.
+
+\item all the entries are clones, possibly with cloned subcomponents; they
+must be deleted with \tet{gunclone_deep}, not \tet{gunclone}.
+
+The following macros are proper lvalues and access the components
+
+\fun{long}{list_nmax}{GEN L}: current maximal number of elements. This grows
+as needed.
+
+\fun{GEN}{list_data}{GEN L}: the elements. If \kbd{v = list\_data(L)}, then
+either \kbd{v} is \kbd{NULL} (empty list) or \kbd{l = lg(v)} is defined, and
+the elements are \kbd{v[1]}, \dots, \kbd{v[l-1]}.
+
+In most \kbd{gerepile} scenarios, the list components are not inspected
+and a shallow copy of the malloc'ed vector is made. The functions
+\kbd{gclone}, \kbd{copy\_bin\_canon} are exceptions, and make a full copy of
+the list.
+
+The main problem with lists is to avoid memory leaks; in the above setup,
+a statement like \kbd{a = List(1)} would already leak memory, since
+\kbd{List(1)} allocates memory, which is cloned (second allocation) when
+assigned to \kbd{a}; and the original list is lost. The solution we
+implemented is
+
+\item to create anonymous lists (from \kbd{List}, \kbd{gtolist},
+\kbd{concat} or \kbd{vecsort}) entirely on the stack, \emph{not} as described
+above, and to set \kbd{list\_nmax} to $0$. Such a list is not yet proper and
+trying to append elements to it fails:
+\bprog
+? listput(List(),1)
+  ***   variable name expected: listput(List(),1)
+  ***                                   ^----------------
+ at eprog\noindent
+If we had been malloc'ing memory for the
+\kbd{List([1,2,3])}, it would have leaked already.
+
+\item as soon as a list is assigned to a variable (or a component thereof)
+by the GP evaluator, the assigned list is converted to the proper format
+(with \kbd{list\_nmax} set) previously described.
+
+\fun{GEN}{listcopy}{GEN L} return a full copy of the \typ{LIST}~\kbd{L},
+allocated on the \emph{stack} (hence \kbd{list\_nmax} is $0$). Shortcut for
+\kbd{gcopy}.
+
+\fun{GEN}{mklistcopy}{GEN x} returns a list with a single element $x$,
+allocated on the stack. Used to implement most cases of \kbd{gtolist}
+(except vectors and lists).
+
+A typical low-level construct:
+\bprog
+  long l;
+  /* assume L is a t_LIST */
+  L = list_data(L); /* discard t_LIST wrapper */
+  l = L? lg(L): 1;
+  for (i = 1; i < l; i++) output( gel(L, i) );
+  for (i = 1; i < l; i++) gel(L, i) = gclone( ... );
+ at eprog\noindent
+
+\section{Protection of non-interruptible code}
+
+GP allows the user to interrupt a computation by issuing SIGINT
+(usually by entering control-C) or SIGALRM (usually using alarm()).
+To avoid such interruption to occurs in section of code which are not
+reentrant (in particular \kbd{malloc} and \kbd{free})
+the following mechanism is provided:
+
+\fun{}{BLOCK_SIGINT_START}{}
+  Start a non-interruptible block code. Block both \kbd{SIGINT} and \kbd{SIGARLM}.
+
+\fun{}{BLOCK_SIGALRM_START}{}
+  Start a non-interruptible block code. Block only \kbd{SIGARLM}.
+This is used in the \kbd{SIGINT} handler itself to delay an eventual pending
+alarm.
+
+\fun{}{BLOCK_SIGINT_END}{}
+  End a non-interruptible block code
+
+The above macros make use of the following global variables:
+
+\tet{PARI_SIGINT_block}: set to $1$ (resp. $2$) by \kbd{BLOCK\_SIGINT\_START}
+(resp. \kbd{BLOCK\_SIGALRM\_START}).
+
+\tet{PARI_SIGINT_pending}: Either $0$ (no signal was blocked), \kbd{SIGINT}
+(\kbd{SIGINT} was blocked) or \kbd{SIGALRM} (\kbd{SIGALRM} was blocked).
+This need to be set by the signal handler.
+
+Inside a block, a auto variable \kbd{int block} is defined which holds the
+value of \kbd{PARI\_SIGINT\_block} when entering the block.
+
+\subsec{Multithread interruptions}
+
+To support multithread, \kbd{BLOCK\_SIGINT\_START} and
+\kbd{BLOCK\_SIGALRM\_START} calls \kbd{MT\_SIGINT\_BLOCK(block);}, and
+\kbd{BLOCK\_SIGINT\_END} calls \kbd{MT\_SIGINT\_UNBLOCK(block);}.
+
+\tet{MT_SIGINT_BLOCK} and \tet{MT_SIGINT_UNBLOCK} are defined by the
+multithread engine. They can calls the following public functions defined by
+the multithread engine.
+
+\fun{void}{mt_sigint_block}{void}
+
+\fun{void}{mt_sigint_unblock}{void}
+
+In practice this mechanism is used by the POSIX thread engine to protect against
+asychronous cancellation.
+
+\section{Black box groups}
+
+A black box group is defined by a \tet{bb_group} struct, describing methods
+available to handle group elements:
+\bprog
+    struct bb_group
+    {
+      GEN (*mul)(void*, GEN, GEN);
+      GEN (*pow)(void*, GEN, GEN);
+      ulong (*hash)(GEN);
+      GEN (*rand)(void*);
+      int (*equal)(GEN, GEN);
+      int (*equal1)(GEN);
+      GEN (*easylog)(void *E, GEN, GEN, GEN);
+    };
+ at eprog
+\kbd{mul(E,x,y)} returns the product $x\*y$.
+
+\kbd{pow(E,x,n)} returns $x^n$ ($n$ integer, possibly negative or zero).
+
+\kbd{hash(x)} returns a hash value for $x$ (\kbd{hash\_GEN} is suitable for this field).
+
+\kbd{rand(E)} returns a random element in the group.
+
+\kbd{equal(x,y)} returns one if $x=y$ and zero otherwise.
+
+\kbd{equal1(x)} returns one if $x$ is the neutral element in the group,
+and zero otherwise.
+
+\kbd{easylog(E,a,g,o)} (optional) returns either NULL or the discrete logarithm
+$n$ such that $g^n=a$, the element $g$ being of order $o$. This provides a
+short-cut in situation where a better algorithm than the generic one is known.
+
+A group is thus described by a \kbd{struct bb\_group} as above and auxiliary
+data typecast to \kbd{void*}. The following functions operate on black box
+groups:
+
+\fun{GEN}{gen_Shanks_log}{GEN x, GEN g, GEN N, void *E, const struct bb_group
+*grp} \hbadness 10000\break
+Generic baby-step/giant-step algorithm (Shanks's method). Assuming
+that $g$ has order $N$, compute an integer $k$ such that $g^k = x$.
+Return \kbd{cgetg(1, t\_VEC)} if there are no solutions. This requires
+$O(\sqrt{N})$ group operations and uses an auxiliary table containing
+$O(\sqrt{N})$ group elements.
+
+\fun{GEN}{gen_Pollard_log}{GEN x, GEN g, GEN N, void *E, const struct bb_group
+*grp} \hbadness 10000\break
+Generic Pollard rho algorithm. Assuming that $g$ has order $N$, compute an
+integer $k$ such that $g^k = x$. This requires $O(\sqrt{N})$ group operations
+in average and $O(1)$ storage. Will enter an infinite loop if there are no
+solutions.
+
+\fun{GEN}{gen_plog}{GEN x, GEN g, GEN N, void *E, const struct bb_group}
+Assuming that $g$ has prime order $N$, compute an integer $k$ such that
+$g^k = x$, using either \kbd{gen\_Shanks\_log} or \kbd{gen\_Pollard\_log}.
+Return \kbd{cgetg(1, t\_VEC)} if there are no solutions.
+
+If \kbd{easy} is not \kbd{NULL}, call \kbd{easy(E,a,g,N)} first and if the
+return value is not \kbd{NULL}, return it. For instance this is used over
+$\F_q^*$ to compute the discrete log of elements belonging to the prime
+field.
+
+\fun{GEN}{gen_Shanks_sqrtn}{GEN a, GEN n, GEN N, GEN *zetan, void *E, const
+struct bb_group *grp} \hbadness 10000 returns one solution of $x^n = a$ in a
+black box cyclic group of order $N$. Return \kbd{NULL} if no solution exists.
+If \kbd{zetan} is not \kbd{NULL} it is set to an element of exact order $n$.
+
+This function uses \kbd{gen\_plog} for all prime divisors of $\gcd(n,N)$.
+
+\fun{GEN}{gen_PH_log}{GEN a, GEN g, GEN N, void *E, const struct bb_group
+*grp}
+Generic Pohlig-Hellman algorithm. Assuming that $g$ has order $N$, compute
+an integer $k$ such that $g^k = x$. Return \kbd{cgetg(1, t\_VEC)} if there
+are no solutions. This calls \tet{gen_plog} repeatedly for all prime divisors
+$p$ of $N$.
+
+\kbd{easy} is as in \kbd{gen\_plog}.
+
+\fun{GEN}{gen_order}{GEN x, GEN N, void *E, const struct bb_group *grp}
+computes the order of $x$. If $N$ is not \kbd{NULL} it is a multiple of the
+order, as a \typ{INT} or a factorization matrix.
+
+\fun{GEN}{gen_factored_order}{GEN x, GEN N, void *E, const struct bb_group *grp}
+returns $[o,F]$, where $o$ is the order of $x$ and $F$ is the factorization
+of $o$. If $N$ is not \kbd{NULL} it is a multiple of the order, as a
+\typ{INT} or a factorization matrix.
+
+\fun{GEN}{gen_select_order}{GEN v, GEN N, void *E, const struct bb_group *grp}
+$v$ being a vector of possible order of the group, try to find the true order
+by checking orders of random points. This will not terminate if there is an
+ambiguity.
+
+\fun{GEN}{gen_gener}{GEN o, void *E, const struct bb_group *grp}
+returns a random generator of the group, assuming it is of order exactly
+$o$ (which can be given by a factorization matrix).
+
+\subsec{Black box groups with pairing}
+
+Theses functions handle groups of rank at most $2$ equipped with a family of
+bilinear pairings which behave like the Weil pairing on elliptic curves over
+finite field.
+
+The function \kbd{pairorder(E, P, Q, m, F)} must return the order of of the $m$-pairing
+of $P$ and $Q$, both of order dividing $m$, where $F$ is the factorisation matrix
+of a multiple of $m$.
+
+\fun{GEN}{gen_ellgroup}{GEN o, GEN d, GEN *pt_m, void *E, const struct bb_group *grp,
+             GEN pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)}
+
+returns the elementary divisors $[d_1, d_2]$ of the group, assuming it is of order exactly
+$o>1$ (which can be given by a factorization matrix), and that $d_2$ divides $d$.
+If $d_2=1$ then $[o]$ is returned, otherwise \kbd{m=*pt\_m} is set to the order of the
+pairing required to verify a generating set which is to be used with \kbd{gen\_ellgens}.
+
+\fun{GEN}{gen_ellgens}{GEN d1, GEN d2, GEN m, void *E, const struct bb_group *grp,
+             GEN pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)}
+the parameters $d_1$, $d_2$, $m$ being as returned by \kbd{gen\_ellgroup}, returns a pair
+of generators $[P,Q]$ such that $P$ is of order $d_1$ and the $m$-pairing of $P$ and
+$Q$ is of order $m$. (Note: $Q$ needs not be of order $d_2$).
+
+\subsec{Functions returning black box groups}
+
+\fun{const struct bb_group *}{get_FpXQ_star}{void **E, GEN T, GEN p}
+returns a pointer to the black box group $(\F_p[x]/(T))^*$.
+
+\fun{const struct bb_group *}{get_FpE_group}{void **pt_E, GEN a4, GEN a6, GEN p}
+returns a pointer to a black box group and set \kbd{*pt\_E} to the necessary data for
+computing in the group $E(\F_p)$ where $E$ is the elliptic curve $E:y^2=x^3+a_4\*x+a_6$,
+with $a_4$ and $a_6$ in $\F_p$.
+
+\fun{const struct bb_group *}{get_FpXQE_group}{void **pt_E, GEN a4, GEN a6, GEN T, GEN p}
+returns a pointer to a black box group and set \kbd{*pt\_E} to the necessary data for
+computing in the group $E(\F_p[X]/(T))$ where $E$ is the elliptic curve $E:y^2=x^3+a_4\*x+a_6$,
+with $a_4$ and $a_6$ in $\F_p[X]/(T)$.
+
+\fun{const struct bb_group *}{get_FlxqE_group}{void **pt_E, GEN a4, GEN a6, GEN T, ulong p}
+idem for small $p$.
+
+\fun{const struct bb_group *}{get_F2xqE_group}{void **pt_E, GEN a2, GEN a6, GEN T}
+idem for $p=2$.
+
+\section{Black box finite fields}
+
+A black box finite field is defined by a \tet{bb_field} struct, describing methods
+available to handle field elements:
+\bprog
+  struct bb_field
+  {
+    GEN (*red)(void *E ,GEN);
+    GEN (*add)(void *E ,GEN, GEN);
+    GEN (*mul)(void *E ,GEN, GEN);
+    GEN (*neg)(void *E ,GEN);
+    GEN (*inv)(void *E ,GEN);
+    int (*equal0)(GEN);
+    GEN (*s)(void *E, long);
+  };
+ at eprog
+
+Note that, in contrast of black box group, elements can have non canonical forms, and
+only \kbd{red} is required to return a canonical form.
+
+\kbd{red(E,x)} returns the canonical form of $x$.
+
+\kbd{add(E,x,y)} returns the sum $x+y$.
+
+\kbd{mul(E,x,y)} returns the product $x\*y$.
+
+\kbd{neg(E,x)} returns $-x$.
+
+\kbd{inv(E,x)} returns the inverse of $x$.
+
+\kbd{equal0(x)} $x$ being in canonical form, returns one if $x=0$ and zero otherwise.
+
+\kbd{s(n)} $n$ being a small signed integer, returns $n$ times the unit element.
+
+A finite field is thus described by a \kbd{struct bb\_field} as above and auxiliary
+data typecast to \kbd{void*}. The following functions operate on black box
+fields:
+
+\fun{GEN}{gen_Gauss}{GEN a, GEN b, void *E, const struct bb_field *ff}
+
+\fun{GEN}{gen_Gauss_pivot}{GEN x, long *rr, void *E, const struct bb_field *ff}
+
+\fun{GEN}{gen_det}{GEN a, void *E, const struct bb_field *ff}
+
+\fun{GEN}{gen_ker}{GEN x, long deplin, void *E, const struct bb_field *ff}
+
+\fun{GEN}{gen_matcolmul}{GEN a, GEN b, void *E, const struct bb_field *ff}
+
+\fun{GEN}{gen_matid}{long n, void *E, const struct bb_field *ff}
+
+\fun{GEN}{gen_matmul}{GEN a, GEN b, void *E, const struct bb_field *ff}
+
+\subsec{Functions returning black box fields}
+
+\fun{const struct bb_field *}{get_Fp_field}{void **pt_E, GEN p}
+
+\fun{const struct bb_field *}{get_Fq_field}{void **pt_E, GEN T, GEN p}
+
+\fun{const struct bb_field *}{get_Flxq_field}{void **pt_E, GEN T, ulong p}
+
+\fun{const struct bb_field *}{get_F2xq_field}{void **pt_E, GEN T}
+
+\section{Black box algebra}
+
+A black box algebra is defined by a \tet{bb_algebra} struct, describing methods
+available to handle algebra elements:
+\bprog
+struct bb_algebra
+{
+  GEN (*red)(void *E, GEN x);
+  GEN (*add)(void *E, GEN x, GEN y);
+  GEN (*mul)(void *E, GEN x, GEN y);
+  GEN (*sqr)(void *E, GEN x);
+  GEN (*one)(void *E);
+  GEN (*zero)(void *E);
+};
+ at eprog
+
+Note that, in contrast with black box groups, elements can have non canonical
+forms, but only \kbd{add} is allowed to return a non canonical form.
+
+\kbd{red(E,x)} returns the canonical form of $x$.
+
+\kbd{add(E,x,y)} returns the sum $x+y$.
+
+\kbd{mul(E,x,y)} returns the product $x\*y$.
+
+\kbd{sqr(E,x)} returns the square $x^2$.
+
+\kbd{one(E)} returns the unit element.
+
+\kbd{zero(E)} returns the zero element.
+
+An algebra is thus described by a \kbd{struct bb\_algebra} as above and
+auxiliary data typecast to \kbd{void*}. The following functions operate on
+black box algebra:
+
+\fun{GEN}{gen_bkeval}{GEN P, long d, GEN x, int use_sqr, void *E,
+          const struct bb_algebra *ff, GEN cmul(void *E, GEN P, long a, GEN x)}
+$x$ being an element of the black box algebra, and $P$ some black box
+polynomial of degree $d$ over the base field,  returns $P(x)$. The function
+\kbd{cmul(E,P,a,y)} must return the coefficient of degree $a$ of $P$
+multiplied by $y$. \kbd{cmul} is allowed to return a non canonical form.
+
+The flag \kbd{use\_sqr} has the same meaning as for \kbd{gen\_powers}. This
+implements an algorithm of Brent and Kung (1978).
+
+\fun{GEN}{gen_bkeval_powers}{GEN P, long d, GEN V, void *E,
+ const struct bb_algebra *ff, GEN cmul(void *E, GEN P, long a, GEN x)}
+as \tet{gen_RgX_bkeval} assuming $V$ was output by
+\tet{gen_powers}$(x, l, E, \var{ff})$ for some $l\geq 1$. For optimal
+performance, $l$ should be computed by \tet{brent_kung_optpow}.
+
+\fun{long}{brent_kung_optpow}{long d, long n, long m} returns the optimal
+parameter $l$ for the evaluation of $n/m$ polynomials of degree $d$.
+Fractional values can be used if the evaluations are done with different
+accuracies, and thus have different weights.
+
+\section{Black box free $\Z_p$-modules}
+
+(Very experimental)
+
+\fun{GEN}{gen_ZpX_Dixon}{GEN F, GEN V, GEN q, GEN p, long N, void *E,
+                            GEN lin(void *E, GEN F, GEN z, GEN q),
+                            GEN invl(void *E, GEN z)}
+
+Let $F$ be a \kbd{ZpXT} representing the coefficients of some abstract
+linear mapping $f$ over $\Z_p[X]$ seen as a free $\Z_p$-module, let $V$ be
+an element of $\Z_p[X]$ and let $q = p^N$.  Return $y\in\Z_p[X]$ such that
+$f(y)=V\pmod{p^N}$ assuming the following holds for $n\leq N$:
+
+\item $\kbd{lin}(E, \kbd{FpX\_red}(F, p^n), z, p^n) \equiv f(z) \pmod{p^n}$
+
+\item $f(\kbd{invl}(E, z)) \equiv z \pmod{p}$
+
+The rationale for the argument $F$ being that it allows \kbd{gen\_ZpX\_Dixon}
+to reduce it to the required $p$-adic precision.
+
+\fun{GEN}{gen_ZpX_Newton}{GEN x, GEN p, long n, void *E,
+                          GEN eval(void *E, GEN a, GEN q),
+                          GEN invd(void *E, GEN b, GEN v, GEN q, long N)}
+
+Let $x$ be an element of $\Z_p[X]$ seen as a free  $\Z_p$-module, and $f$
+some differentiable function over $\Z_p[X]$ such that $f(x) \equiv 0
+\pmod{p}$. Return $y$ such that $f(y) \equiv 0\pmod{p^n}$, assuming the
+following holds for all $a, b\in \Z_p[X]$ and $M\leq N$:
+
+\item $v = \kbd{eval}(E,a,p^N)$ is a vector of elements of $\Z_p[X]$,
+
+\item $w = \kbd{invd}(E,b,v,p^M,M)$ is an element in $\Z_p[X]$,
+
+\item $v[1] \equiv f(a) \pmod{p^N\Z_p[X]}$,
+
+\item $df_a(w) \equiv b \pmod{p^M\Z_p[X]}$
+
+\noindent and $df_a$ denotes the differential of $f$ at $a$. Motivation:
+\kbd{eval} allows to evaluate $f$ and \kbd{invd} allows to invert its
+differential. Frequently, data useful to compute the differential appear as a
+subproduct of computing the function. The vector $v$ allows \kbd{eval} to
+provide these to \kbd{invd}. The implementation of \kbd{invd} will generally
+involves the use of the function \kbd{gen\_ZpX\_Dixon}.
+
+\section{Public functions useless outside of GP context}
+
+These functions implement GP functionality for which the C language or
+other libpari routines provide a better equivalent; or which are so tied
+to the \kbd{gp} interpreter as to be virtually useless in \kbd{libpari}. Some
+may be generated by \kbd{gp2c}. We document them here for completeness.
+
+\subsec{Conversions}
+
+\fun{GEN}{toser_i}{GEN x} internal shallow function, used to implement
+automatic conversions to power series in GP (as in \kbd{cos(x)}).
+Converts a \typ{POL} or a \typ{RFRAC} to a \typ{SER} in the same variable and
+precision \kbd{precdl} (the global variable corresponding to
+\kbd{seriesprecision}). Returns $x$ itself for a \typ{SER}, and \kbd{NULL}
+for other argument types. The fact that it uses a global variable makes it
+awkward whenever you're not implementing a new transcendental function in GP.
+Use \tet{RgX_to_ser} or \tet{rfrac_to_ser} for a fast clean alternative to
+\kbd{gtoser}.
+
+\subsec{Output}
+
+\fun{void}{print0}{GEN g, long flag} internal function underlying the
+\kbd{print} GP function. Prints the entries of the \typ{VEC} $g$, one by one,
+without any separator; entries of type \typ{STR} are printed without enclosing
+quotes. \fl is one of \tet{f_RAW}, \tet{f_PRETTYMAT} or \tet{f_TEX}, using the
+current default output context.
+
+\fun{void}{out_print0}{PariOUT *out, const char *sep, GEN g, long flag} as
+\tet{print0}, using output context \kbd{out} and separator \kbd{sep} between
+successive entries (no separator if \kbd{NULL}).
+
+\fun{void}{printsep}{const char *s, GEN g, long flag} \tet{out_print0} on
+\tet{pariOut} followed by a newline.
+
+\fun{void}{printsep1}{const char *s, GEN g, long flag} \tet{out_print0} on
+\tet{pariOut}.
+
+\fun{char*}{pari_sprint0}{const char *s, GEN g, long flag} displays $s$,
+then \kbd{print0(g, flag)}.
+
+\fun{void}{print}{GEN g} equivalent to \kbd{print0(g, f\_RAW)}, followed
+by a \kbd{\bs n} then an \kbd{fflush}.
+
+\fun{void}{print1}{GEN g} as above, without the \kbd{\bs n}. Use
+\tet{pari_printf} or \tet{output} instead.
+
+\fun{void}{printtex}{GEN g} equivalent to \kbd{print0(g, t\_TEX)}, followed
+by a \kbd{\bs n} then an \kbd{fflush}. Use \tet{GENtoTeXstr} and
+\tet{pari_printf} instead.
+
+\fun{void}{write0}{const char *s, GEN g}
+
+\fun{void}{write1}{const char *s, GEN g} use \kbd{fprintf}
+
+\fun{void}{writetex}{const char *s, GEN g} use \tet{GENtoTeXstr} and
+\kbd{fprintf}.
+
+\fun{void}{printf0}{GEN fmt, GEN args} use \tet{pari_printf}.
+
+\fun{GEN}{Strprintf}{GEN fmt, GEN args} use \tet{pari_sprintf}.
+
+\subsec{Input}
+
+\kbd{gp}'s input is read from the stream \tet{pari_infile}, which is changed
+using
+
+\fun{FILE*}{switchin}{const char *name}
+
+Note that this function is quite complicated, maintaining stacks of files
+to allow smooth error recovery and \kbd{gp} interaction. You will be better
+off using \tet{gp_read_file}.
+
+\subsec{Control flow statements}
+
+\fun{GEN}{break0}{long n}. Use the C control statement \kbd{break}. Since
+\kbd{break(2)} is invalid in C, either rework your code or use \kbd{goto}.
+
+\fun{GEN}{next0}{long n}. Use the C control statement \kbd{continue}. Since
+\kbd{continue(2)} is invalid in C, either rework your code or use \kbd{goto}.
+
+\fun{GEN}{return0}{GEN x}. Use \kbd{return}!
+
+\fun{void}{error0}{GEN g}. Use \kbd{pari\_err(e\_USER,)}
+
+\fun{void}{warning0}{GEN g}. Use \kbd{pari\_warn(e\_USER,)}
+
+\subsec{Accessors}
+
+\fun{GEN}{vecslice0}{GEN A, long y1, long y2} used to implement $A[y_1..y_2]$.
+
+\fun{GEN}{matslice0}{GEN A, long x1, long x2, long y1, long y2}
+used to implement $A[x_1..x_2,y_1..y_2]$.
+
+\subsec{Iterators}
+
+\fun{GEN}{apply0}{GEN f, GEN A} gp wrapper calling \tet{genapply}, where $f$
+is a \typ{CLOSURE}, applied to $A$. Use \kbd{genapply} or a standard C loop.
+
+\fun{GEN}{select0}{GEN f, GEN A} gp wrapper calling \tet{genselect}, where $f$
+is a \typ{CLOSURE} selecting from $A$. Use \kbd{genselect} or a standard C loop.
+
+\fun{GEN}{vecapply}{void *E, GEN (*f)(void* E, GEN x), GEN x} used to implement
+\kbd{[a(x)|x<-b]}.
+
+\fun{GEN}{vecselect}{void *E, long (*f)(void* E, GEN x), GEN A}
+used to implement \kbd{[x<-b,c(x)]}.
+
+\fun{GEN}{vecselapply}{void *Epred, long (*pred)(void* E, GEN x), void *Efun, GEN (*fun)(void* E, GEN x), GEN A}
+used to implement \kbd{[a(x)|x<-b,c(x)]}.
+
+\subsec{Function related to the GP parser}
+
+The GP parser can generate an opcode saving the current lexical context
+(pairs made of a lexical variable name and its value) in a \kbd{GEN}, called
+\kbd{pack} in the sequel. These can be used from debuggers (e.g. gp's break
+loop) to track values of lexical variable. Indeed, lexical variables have
+disappeared from the compiled code, only their values in a given scope exist
+(on some value stack). Provided the parser generated the proper opcode, there
+remains a trace of lexical variable names and everything can still be
+unravelled.
+
+\fun{GEN}{localvars_read_str}{const char *s, GEN pack} evaluate the string $s$
+in the lexical context given by \kbd{pack}.  Used by \tet{geval_gp} in GP.
+
+\fun{long}{localvars_find}{GEN pack, entree *ep} does \kbd{pack} contain
+a pair whose variable corresponds to \kbd{ep}? If so, where is the
+corresponding value? (returns an offset on the value stack).
+
+\subsec{Miscellaneous}
+
+\fun{char*}{os_getenv}{const char *s} either calls \kbd{getenv}, or directly
+return \kbd{NULL} if the \kbd{libc} does not provide it. Use \tet{getenv}.
+
+\fun{sighandler_t}{os_signal}{int sig, pari_sighandler_t fun} after a
+\bprog
+  typedef void (*pari_sighandler_t)(int);
+ at eprog\noindent
+(private type, not exported). Installs signal handler \kbd{fun} for
+signal \kbd{sig}, using \tet{sigaction} with flag \tet{SA_NODEFER}. If
+\kbd{sigaction} is not available use \tet{signal}. If even the latter is not
+available, just return \tet{SIG_IGN}. Use \tet{sigaction}.
+
+\chapter{Regression tests, benches}
+
+This chapter documents how to write an automated test module, say \kbd{fun},
+so that \kbd{make test-fun} executes the statements in the \kbd{fun} module
+and times them, compares the output to a template, and prints an error
+message if they do not match.
+
+\item Pick a \emph{new} name for your test, say \kbd{fun}, and write down a
+GP script named \kbd{fun}. Make sure it produces some useful output and tests
+adequately a set of routines.
+
+\item The script should not be too long: one minute runs should be enough.
+Try to break your script into independent easily reproducible tests, this way
+regressions are easier to debug; e.g. include \kbd{setrand(1)} statement before
+a randomized computation. The expected output may be different on 32-bit and
+64-bit machines but should otherwise be platform-independent. If possible, the
+output shouldn't even depend on \kbd{sizeof(long)}; using a \kbd{realprecision}
+that exists on both 32-bit and 64-bit architectures, e.g. \kbd{\bs p 38} is a
+good first step.
+
+\item Dump your script into \kbd{src/test/in/} and run \kbd{Configure}.
+
+\item \kbd{make test-fun} now runs the new test, producing a \kbd{[BUG]} error
+message and a \kbd{.dif} file in the relevant object directory \kbd{Oxxx}.
+In fact, we compared the output to a non-existing template, so this must fail.
+
+\item Now
+\bprog
+  patch -p1 < Oxxx/fun.dif
+ at eprog\noindent
+generates a template output in the right place \kbd{src/test/32/fun}, for
+instance on a 32-bit machine.
+
+\item If different output is expected on 32-bit and 64-bit machines, run the
+test on a 64-bit machine and patch again, thereby
+producing \kbd{src/test/64/fun}. If, on the contrary, the output must be the
+same, make sure the output template land in the \kbd{src/test/32/} directory
+(which provides a default template when the 64-bit output file is missing);
+in particular move the file from \kbd{src/test/64/} to \kbd{src/test/32/}
+if the test was run on a 64-bit machine.
+
+\item You can now re-run the test to check for regressions: no \kbd{[BUG]}
+is expected this time! Of course you can at any time add some checks, and
+iterate the test / patch phases. In particular, each time a bug in the
+\kbd{fun} module is fixed, it is a good idea to add a minimal test case to
+the test suite.
+
+\item By default, your new test is now included in \kbd{make test-all}. If
+it is particularly annoying, e.g. opens tons of graphical windows as
+\kbd{make test-ploth} or just much longer than the recommended minute, you
+may edit \kbd{config/get\_tests} and add the \kbd{fun} test to the list of
+excluded tests, in the \kbd{test\_extra\_out} variable.
+
+\item The \kbd{get\_tests} script also defines the recipe for
+\kbd{make bench} timings, via the variable \kbd{test\_basic}. A test is
+included as \kbd{fun} or \kbd{fun\_$n$}, where $n$ is an integer $\leq 1000$;
+the latter means that the timing is weighted by a factor $n/1000$. (This was
+introduced a long time ago, when the \kbd{nfields} bench was so much slower
+than the others that it hid slowdowns elsewhere.)
+
+\section{Functions for GP2C}
+
+\subsec{Functions for safe access to components}
+
+Theses function returns the adress of the requested component after checking
+it is actually valid. This is used by GP2C -C.
+
+\fun{GEN*}{safegel}{GEN x, long l}, safe version of \kbd{gel(x,l)} for \typ{VEC},
+\typ{COL} and \typ{MAT}.
+
+\fun{long*}{safeel}{GEN x, long l}, safe version of \kbd{x[l]} for \typ{VECSMALL}.
+
+\fun{GEN*}{safelistel}{GEN x, long l} safe access to \typ{LIST} component.
+
+\fun{GEN*}{safegcoeff}{GEN x, long a, long b} safe version of
+\kbd{gcoeff(x,a, b)} for \typ{MAT}.
+
+\chapter{Parallelism}
+
+\section{The PARI MT interface}
+
+PARI provides an abstraction for doing parallel computations.
+
+\fun{void}{mt_queue_start}{struct pari\_mt *pt, GEN worker} Let \kbd{worker}
+be a \typ{CLOSURE} object of arity $1$.  Initialize the structure \kbd{pt}
+to evaluate \kbd{worker} in parallel.
+
+\fun{void}{mt_queue_submit}{struct pari\_mt *pt, long taskid, GEN task} Submit
+\kbd{task} to be evaluated by \kbd{worker}, or \kbd{NULL} if no further task
+is left to be submitted. The value \kbd{taskid} is user-specified and allows
+to later match up results and submitted tasks.
+
+\fun{GEN}{mt_queue_get}{struct pari\_mt *pt, long *taskid, long *pending}
+Return the result of the evaluation by \kbd{worker} of one of the previously
+submitted tasks. Set \kbd{pending} to the number of remaining pending tasks.
+Set \kbd{taskid} to the value associate to this task by
+\kbd{mt\_queue\_submit}.  Returns \kbd{NULL} if more tasks need to be
+submitted.
+
+\fun{void}{mt_queue_end}{struct pari\_mt *pt} End the parallel execution.
+
+Calls to \tet{mt_queue_submit} and \tet{mt_queue_get} must alternate: each
+call to \tet{mt_queue_submit} must be followed by a call to
+\tet{mt_queue_get} before any other call to \tet{mt_queue_submit},
+and conversely.
+
+The first call to \tet{mt_queue_get} will return \kbd{NULL} until a
+sufficient number of tasks have been submitted. If no more tasks are left
+to be submitted, use
+\bprog
+  mt_queue_submit(handle, id, NULL)
+ at eprog\noindent
+to allow further calls to \tet{mt_queue_get}.  If \tet{mt_queue_get} sets
+\kbd{pending} to $0$, then no more tasks are pending and it is safe to call
+\tet{mt_queue_end}.
+
+The parameter \kbd{taskid} can be chosen arbitrarily. It is associated to a
+task but is not available to \kbd{worker}.  It provides an efficient way to
+match a tasks and results. It is ignored when the parameter \kbd{task} is
+\kbd{NULL}.
+
+\subsec{Miscellaneous}
+
+\fun{void}{mt_broadcast}{GEN code}: do nothing unless the MPI threading engine
+is in use. In that case, it evaluates the closure  \kbd{code} on all secondary
+nodes. This can be sued to change the states of the MPI child nodes.
+This is used by \tet{install}.
+
+\section{Initialization}
+
+This section is technical.
+
+\fun{void}{pari_mt_init}{void} \label{pari_mt_init}
+When using MPI, it is sometimes necessary to run initialization code on the
+child nodes after PARI is initialized. This can be done as follow:
+
+\item call \tet{pari_init_opts} with the flag \tet{INIT_noIMTm}.
+This initializes PARI, but not the MT engine.
+
+\item call the required initialization code.
+
+\item call \tet{pari_mt_init} to initialize the MT engine.
+Note that under MPI, this function only returns on the master node. On the
+child nodes, it enters slave mode. Thus it is no longer possible to run
+initialization code on the child nodes.
+
+See the file \kbd{examples/pari-mt.c} for an example.
+
+\fun{void}{pari_mt_close}{void} \label{pari_mt_close}
+When using MPI, calling \tet{pari_close} will terminate the MPI execution
+environment. If this is undesirable, you should call \tet{pari_close_opts} with
+the flag \tet{INIT_noIMTm}.  This closes PARI without terminating the MPI
+execution environment It is allowed to call \kbd{pari\_mt\_close} later to
+terminate it.  Note that the once MPI is terminated it cannot be restarted, and
+that it is considered an error for a program to end without having terminated
+the MPI execution environment.
+
+\vfill\eject
+\input index\end
diff --git a/doc/gp.1 b/doc/gp.1
new file mode 100644
index 0000000..0035b36
--- /dev/null
+++ b/doc/gp.1
@@ -0,0 +1,336 @@
+.TH GP 1 "25 September 2013"
+.SH NAME
+gp \- The PARI calculator
+.SH SYNOPSIS
+.B gp
+.RB [ -s
+.IR stacksize ]
+.RB [ -p
+.IR primelimit ]
+.RB [ --emacs ]
+.RB [ -f | --fast ]
+.RB [ -q | --quiet ]
+.RB [ -D | --default
+.IR key=val ]
+.RB [ --help ]
+.RB [ --test ]
+.RB [ --texmacs ]
+.RB [ --version ]
+.RB [ --version-short ]
+[ file1 file2 ...]
+
+.SH DESCRIPTION
+Invokes the PARI-GP calculator
+\&\fBgp\fR; gp is an advanced programmable calculator, specializing in number
+theory, which computes symbolically as long as possible, numerically where
+needed, and contains a wealth of arithmetic functions: factorizations,
+elliptic curves, Galois theory, class field theory... Commands, written in
+the GP scripting language, are input interactively or loaded from files.
+
+If present at the end of the command line, files 'file1', 'file2', ...
+are loaded on startup; they must be written in the GP language.
+
+.SH OPTIONS
+Command line options are available in both short form (-f) and POSIX-like
+(--fast). Numeric arguments can be followed by a modifier
+.B k
+,
+.B M
+or
+.B G
+at the user's convenience; in that case the argument is multiplied by 10^3,
+10^6, or 10^9 respectively.
+
+.TP
+.B \-f, \--fast
+Fast start (or factory settings). Do not read
+.B .gprc
+(see below) upon startup.
+.TP
+.B \-p limit
+[DEPRECATED]
+Upon startup, gp computes a table of small primes used in
+number-theoretic applications. If
+.I primelimit
+is set, the table include primes up to that bound instead of the default
+(= 500000). It is now mostly useless to change this value.
+.TP
+.B \-q, \--quiet
+Quiet mode. Do not print headers or history numbers and do not say goodbye.
+
+.TP
+.B \-D, \--default key=val
+performs
+.BR default(key,
+.BR val) ";"
+on startup, overriding values from the
+.B gprc
+preferences file. 'val' must be a constant value and is not allowed to
+involve any computation (e.g. 1+1 is forbidden). Any number of such
+default-setting statements may appear on the command line.
+
+.TP
+.B \-s limit
+Size of gp internal stack allocated on startup. When gp runs out of space, it
+interrupts the current computation and raises a
+.BI "stack overflow"
+exception. If this occurs frequently, start with a bigger stack. The stack
+size can also be
+increased from within gp, using
+.BR default(parisize, limit) ";"
+it may be convenient to set
+.B stacksize
+from your
+.B .gprc.
+Note that computations with a
+.B smaller
+stack may be more efficient due to better data locality. Most computations
+should need less than 20MB.
+
+.TP
+.B \--emacs
+gp can be run in an
+.I Emacs
+shell (see GP User's manual for details). This flag is then required for
+smooth interaction with the 
+.I PariEmacs
+package (pari.el). It is set automatically by the pari.el package, and will
+produce nice display oddities if you set it outside of an
+.I Emacs
+session.
+.TP
+.B \--help
+print a summary of available command-line options.
+.TP
+.B \--test
+run gp in test mode: suppress printing of history numbers and wrap long
+output lines (to get readable diff output). For benches only.
+.TP
+.B \--texmacs
+gp can be run from a
+.I TeXmacs
+frontend. This flag is set by TeXmacs, to enable special purpose
+communication channels. Do not set it yourself.
+
+.TP
+.B \--version
+output version info (banner) then exit.
+
+.TP
+.B \--version-short
+output version number then exit.
+
+.SH USE
+.TP
+.B ?
+to get online help.
+.TP
+.B ??
+to get extended online help (more precisely, to call the external help
+program,
+.B gphelp
+by default)
+.TP
+.B quit
+(or \\q), or
+.B EOF
+(Ctrl-D) to quit
+.BR gp .
+.PP
+The following works only when gp was linked with GNU
+.IR readline
+library:
+.TP
+arrow keys
+for editing and viewing the input history.
+.TP
+.B TAB
+ for automatic completion
+
+.SH MANUALS
+The following material is included in the standard distribution (originally
+in TeX format):
+.TP
+.I The User's Guide to PARI/GP
+(users.dvi)
+.TP
+.I The User's Guide to the PARI library
+(library.dvi)
+.TP
+.I The Developer's Guide to the PARI library
+(develop.dvi)
+.TP
+.I PARI/GP, a tutorial
+(tutorial.dvi)
+.TP
+.I PARI/GP reference card
+(refcard.ps): 4 pages, based on an earlier version by Joseph H. Silverman.
+
+.SH FILES
+.TP
+.I gp
+main executable
+.TP
+.I $HOME/.gprc
+(or $GPRC if set) user preference file, read at beginning of execution by
+each
+.B gp
+shell. A default gprc
+.I gprc.dft
+is provided with the distribution. If this file cannot be found,
+.I /etc/gprc
+is checked instead.
+.TP
+.I pari.log
+default logfile (can be changed in the gprc file or interactively using
+.B default()
+)
+.TP
+.I pari.ps
+default psfile used for postscript output (as above)
+.TP
+.I gphelp
+default external help program (as above)
+.TP
+.I *.gp
+GP programs
+
+.SH ENVIRONMENT
+.TP
+.I $GPRC
+place to look for the user's preference file (gprc); if the file does not exist,
+we then check in $HOME/.gprc, /etc/gprc, and finally for a file named 'gprc'
+in PARI's
+.B datadir.
+
+.TP
+.I $GP_DATA_DIR
+directory containing data installed by optional PARI packages.
+For example, the Galois resolvents files in directory
+.I galdata/
+needed by the
+.B polgalois
+function, in degrees 8 to 11; or the modular polynomials in
+.I seadata/
+used by the
+.B ellap
+function for large base fields. This environment variable
+overrides PARI's 'datadir', defined at Configure time.
+
+.TP
+.I $GP_POSTSCRIPT_VIEWER
+an application able to display PostScript files, used by the
+.I plotps
+graphic engine. This engine is a fallback used to output hi-res plots even
+when no compatible graphical library was available on your platform at
+Configure time. (Dumps the graph to a temporary file, then open the file.)
+
+.TP
+.I $GPHELP
+name of the external help program invoked by ?? and ??? shortcuts.
+
+.TP
+.I $GPTMPDIR
+name of the directory where temporary files will be generated.
+
+.SH HOME PAGE
+PARI's home page resides at
+.RS
+.I http://pari.math.u-bordeaux.fr/
+.RE
+
+.SH MAILING LISTS
+There are a number of mailing lists devoted to the PARI/GP package, and most
+feedback should be directed to those. See
+.RS
+.I http://pari.math.u-bordeaux1.fr/lists-index.html
+.RE
+for details. The most important ones are:
+
+.PP
+-
+.B pari-announce
+(moderated): for us to announce major version changes.
+.PP
+-
+.B pari-dev:
+for everything related to the development of PARI, including
+suggestions, technical questions, bug reports or patch submissions.
+
+.PP
+-
+.B pari-users:
+for discuss about everything else, in particular ask for help.
+
+To subscribe, send empty messages with a Subject: containing the word
+"subscribe" respectively to
+
+.PP
+   pari-announce-request at pari.math.u-bordeaux.fr
+.PP
+   pari-users-request at pari.math.u-bordeaux.fr
+.PP
+   pari-dev-request at pari.math.u-bordeaux.fr
+
+.SH BUG REPORTS
+Bugs should be submitted online to our Bug Tracking System, available from
+PARI's home page, or directly from the URL
+.RS
+.I http://pari.math.u-bordeaux.fr/Bugs/
+.RE
+Further instructions can be found on that page.
+
+.SH TRIVIA
+Despite the leading G, GP has nothing to do with GNU. The first version was
+originally called GPC, for Great Programmable Calculator. For some reason,
+the trailing C was eventually dropped.
+
+PARI has nothing to do with the French capital. The name is a pun about the
+project's early stages when the authors started to implement a library for
+"Pascal ARIthmetic" in the PASCAL programming language. They quickly
+switched to C.
+
+For the benefit of non-native French speakers, here's a slightly expanded
+explanation:
+.B Blaise Pascal
+(1623-1662) was a famous French mathematician and philosopher who was one
+of the founders of probability and devised one of the first "arithmetic
+machines". He once proposed the following "proof" of the existence of God
+for the unbelievers: whether He exists or not I lose nothing by believing
+in Him, whereas if He does and I misbehave... This is the so-called "pari
+de Pascal" (Pascal's Wager).
+
+Note that PARI also means "fairy" in Persian.
+
+.SH AUTHORS
+PARI was originally written by Christian Batut, Dominique Bernardi, Henri
+Cohen, and Michel Olivier in Laboratoire A2X (Universite Bordeaux I, France),
+and was maintained by Henri Cohen up to version 1.39.15 (1995), and by Karim
+Belabas since then.
+
+A great number of people have contributed to the successive improvements
+which eventually resulted in the present version. See the AUTHORS file in
+the distribution.
+
+.SH SEE ALSO
+.IR gap (1),
+.IR gphelp (1),
+.IR perl (1),
+.IR readline (3),
+.IR sage (1),
+.IR tex (1),
+.IR texmacs (1),
+
+.SH COPYING
+
+This program 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.
+
+This program 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
+this program; if not, write to the Free Software Foundation, Inc., 675 Mass
+Ave, Cambridge, MA 02139, USA.
diff --git a/doc/gphelp.1 b/doc/gphelp.1
new file mode 100644
index 0000000..4136fd9
--- /dev/null
+++ b/doc/gphelp.1
@@ -0,0 +1,249 @@
+.TH GPHELP 1 "02 February June 2012"
+.SH NAME
+gphelp \- GP-PARI online help script
+.SH SYNOPSIS
+.BR gphelp
+[-to_pod
+.IR file ]
+[-raw]
+[-detex]
+[-k]
+[-ch
+.IR c1 ]
+[-cb
+.IR c2 ]
+[-cu
+.IR c3 ]
+[keyword [ @{1,2,3,4,5} ]]
+[tutorial]
+[refcard]...
+
+.SH DESCRIPTION
+Invokes the PARI-GP online help script. By default,
+.I keyword
+is understood as a section heading (a GP function name), and
+.B gphelp
+looks for it in Chapter 3 of PARI User's Manual. If it is found, the
+corresponding section is copied to a temporary file in /tmp (or
+.BR $GPTMPDIR ),
+.B TeX
+is run then the corresponding section is displayed in a separate window, using
+.B xdvi
+(or
+.BR $GPXDVI ).
+
+
+If more than one keyword is given on a single command line, the outputs are
+concatenated. If
+.I keyword
+is omitted, open the whole manual
+.IR users.dvi .
+The keywords
+.I tutorial
+and
+.I refcard
+open the GP tutorial and reference card respectively.
+
+GP defaults, help sections (1 to 11) as well as some keywords (readline,
+bnf, ell, all operators...) are recognized and treated in a special way to
+make sure they match a section heading. For instance
+.B &&
+is translated to
+.I "Comparison and boolean operators"
+which isn't that obvious to guess.
+Make sure to enclose
+.I keyword
+between quotes if it contains dangerous characters (e.g spaces). For
+instance, you need to type
+
+.RS
+gphelp -k "elliptic curves"
+.RE
+
+and not
+
+.RS
+gphelp -k elliptic curves
+.RE
+
+which would look for
+"elliptic"
+then for
+"curves"
+and output e.g.
+.B ploth
+among the relevant sections.
+
+.SH FULL SEARCH: @ MARKERS
+
+The pattern
+.BR @ x
+(where x is a chapter number between 1 and 5) at the end of
+.I keyword
+conducts the search in the corresponding chapter instead of the default
+Chapter 3. If the number is omitted, search the whole manual. For instance
+.RS
+gphelp -k gerepile at 4
+.RE
+will look for
+.B gerepile
+in Chapter 4 of users manual.
+.RS
+gphelp -k gerepile@
+.RE
+will search the whole manual. All chapters of the manual (usersch[1-5].tex)
+are assumed to be in
+.B $GPDOCDIR
+.RI ( default value set by
+Configure). You are allowed to gzip (or compress) them, this won't affect
+the search.
+
+.SH OPTIONS
+The following command line options are available:
+.TP
+.BI -cb,\ -color_bold\  c2
+color used for bold. See
+.BR -color_help .
+
+.TP
+.BI -ch,\ -color_help\  c1
+color (between 1 and 16) used for general printing. Only
+significant in
+.B detex
+mode.
+
+.TP
+.BI -cu,\ -color_underline\  c3
+color used for underline. See
+.BR -color_help .
+
+.TP
+.BI \-detex
+Use
+.B detex
+mode. The TeX file is converted to readable screen output that is directly
+written to the terminal; don't use tex or xdvi.
+
+.TP
+.BI \-k
+Switch to
+.B apropos
+mode. Only write to standard output the list of section headings in manual
+which contain
+.IR keyword .
+
+You can then choose an element in the list and use it as argument for
+.B gphelp
+(surround it by quotes if it includes spaces), possibly followed by a
+trailing
+.B @
+marker (see above).
+
+.TP
+.BI \-raw
+use internal format for output (using @x markers). For debugging only.
+
+.TP
+.BI \-to_pod\  file
+try to convert
+.I file
+from TeX to POD format. Will only work on some subset of PARI User's Manual
+(Chapter 3, reference card). This should be the only arguments on the
+command line.
+
+.SH ENVIRONMENT
+.TP
+COLUMNS
+number of columns available on screen (for
+.B detex
+mode)
+
+.TP
+DISPLAY
+where to launch the dvi previewer. If unset,
+.B detex
+mode is assumed automatically.
+
+.TP
+GPDOCDIR
+directory where PARI help files are located. Default value set at Configure
+time.
+
+.TP
+GPTEX
+.RS
+TeX typesetting program to use
+.RE
+
+.TP
+GPTMPDIR
+directory where temporary files are written
+
+.TP
+GPXDVI
+.RS
+dvi previewer to use
+.RE
+
+.SH FILES
+.TP
+.I gp
+the gp calculator
+
+.TP
+.I gphelp
+the gphelp script
+
+.TP
+.I users.dvi
+PARI/GP User's Manual
+
+.TP
+.I tutorial.dvi
+PARI/GP tutorial
+
+.TP
+.I refcard.dvi
+GP reference card
+
+.TP
+.I refcard.ps
+GP reference card, printer ready
+
+.SH BUGS
+
+The search algorithm is rather crude and the data searched rather
+unstructured. Hence, searching outside of Chapter 3 may not yield useful
+results, except in
+.B apropos
+mode (sections may be truncated too soon for instance).
+
+Multiword search patterns have a tendency to fail due to various TeX
+constructs in the source.
+
+.SH AUTHORS
+Originally written by Ilya Zakharevitch for the Math::Pari perl package.
+Rewritten and expanded by Karim Belabas for the main PARI distribution.
+
+.SH SEE ALSO
+.IR gp (1),
+.IR gzip (1),
+.IR readline (1),
+.IR tex (1),
+.IR xdvi (1).
+
+.SH COPYING
+
+This program 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.
+
+This program 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 this program; if not, write to the Free
+Software Foundation,  Inc.,  675  Mass  Ave,  Cambridge,  MA
+02139, USA.
diff --git a/doc/gphelp.in b/doc/gphelp.in
new file mode 100755
index 0000000..79d3d11
--- /dev/null
+++ b/doc/gphelp.in
@@ -0,0 +1,1219 @@
+#!@perl@
+#
+# $Id$
+#
+# Copyright (C) 2000  The PARI group.
+#
+# This file is part of the PARI/GP package.
+#
+# PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY WHATSOEVER.
+#
+# Check the License for details. You should have received a copy of it, along
+# with the package; see the file 'COPYING'. If not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Output extended help corresponding to a given GP command. By default,
+# extract relevant information from  from the PARI manual, run TeX, then open
+# an xdvi display.
+#
+# The manual can be compressed.
+#
+# Usage: gphelp keyword
+#
+# Command line options:
+#  -k: apropos (list of relevant GP functions)
+#  -detex      (-d): don't use TeX + xdvi (implicit when DISPLAY is not set).
+#  -color_help (-ch) <number>: use color "number" (same scheme as in GP)
+#  -color_bold (-cb) <number>: display bold in color "number"
+#  -color_underline (-cu) <number>: display underlined text in color "number"
+#
+#  -raw   use internal format for output with @x markers, -detex is implicit
+#         (for the TeX-to-pod converter)
+#
+#  -to_pod file		convert file to POD (should be the only args)
+#
+#  -to_dumbpod file	same, but without nested formating
+#
+# Granted environment variables (override):
+#  GPTMPDIR: where temporary files will go (/tmp by default).
+#  GPDOCDIR: where is manual (by default, where make install will put it).
+#  GPXDVI: which 'xdvi' program to call (xdvi by default)
+#
+$version= "@version@";
+$datadir= "@datadir@";
+# no expanded material (@key@) below
+$wwwsite= "http://pari.math.u-bordeaux.fr/";
+
+$xdvi = $ENV{GPXDVI} || "xdvi";
+$xdviref = $ENV{GPXDVIREF} || "$xdvi -paper 29.7x21cm";
+$gzip = "gzip";
+$zcat = "$gzip -dc";
+$bzip = "bzip2";
+$bzcat = "$bzip -dc";
+$docdir = &get_docdir();
+$tex = $ENV{GPTEX} || "tex";
+
+$refcard = (@ARGV and $ARGV[-1] =~ /refcard/i);
+
+$dumb_pod=1, $ARGV[0] = '-to_pod' if @ARGV && $ARGV[0] eq '-to_dumb_pod';
+&to_pod() if @ARGV == 2 && $ARGV[0] eq '-to_pod';
+
+&options(); &init();
+if ($#ARGV < 0) { &treat(""); cleanexit(); }
+
+&pretex() if (!$detex);
+for (@ARGV) { &treat($_); }
+if ($apropos) { &apropos_final_print(); cleanexit(); }
+&posttex() if (!$detex);
+print "ugly_kludge_done\n" if (!$detex && $fromgp);
+cleanexit();
+
+#
+# Procedures
+#
+sub cleanexit {
+  print "\e[0m" unless $to_pod;
+  exit 0;
+}
+
+sub help {
+  print "Usage: $0 [-k] [-detex] [-ch c1] [-cb c2] [-cu c3] keyword\n";
+  print "where c1,c2,c3 denote background, bold and underline color\n";
+  exit(1);
+}
+
+sub options {
+  $raw = $detex = $fromgp = $apropos = 0;
+  $ch = $cb = $cu = '';
+  while ($_ = $ARGV[0])
+  {
+    last if (! /^-[a-z]/);
+    shift(@ARGV);
+    if ($_ eq "-fromgp")
+      { $fromgp = 1; }
+    elsif ($_ eq "-k")
+      { $apropos = $detex = 1; }
+    elsif ($_ eq "-balloon")
+      { $balloon = 1; }
+    elsif ($_ eq "-detex" || $_ eq "-d")
+      { $detex = 1; }
+    elsif ($_ eq "-raw")
+      { $raw = $detex = 1; }
+    elsif ($_ eq "-color_help" || $_ eq "-ch")
+      { $ch = &color(shift(@ARGV)); }
+    elsif ($_ eq "-color_bold" || $_ eq "-cb")
+      { $cb = &color(shift(@ARGV)); }
+    elsif ($_ eq "-color_underline" || $_ eq "-cu")
+      { $cu = &color(shift(@ARGV)); }
+    else
+      { &help(); }
+  }
+  $ch = "\e[m$ch";
+  $cu .= $cu ? "\e[1m": "\e[4m";
+  $cb .= "\e[1m";
+  $detex = 1 if (!$ENV{DISPLAY});
+}
+
+sub get_docdir {
+  my $d = $ENV{GPDOCDIR} || $ENV{GPHELP_DOCDIR};
+  if (!defined $d) {
+    # work from TOPDIR/Oarch or TOPDIR too: may be uninstalled yet;
+    $d = $0; $d =~ s,/gphelp,,;
+    for ("$datadir", '.', '..', $d) {
+      my $t = "$_/doc";
+      if (-f "$t/translations") { $d = $t; last; }
+    }
+    $d ||= "$datadir/doc";	# Last resort
+  }
+  if ($d =~ /^\./) {
+    eval { require Cwd;
+      $d = Cwd::cwd() . "/$d";
+      $d =~ s,doc/\.\./doc,doc,;
+    }
+  }
+  return $d;
+}
+
+sub init {
+  &inittr();
+
+  $indent = "   ";
+  # avoid Glob.pm! (for minimal Windows install)
+  opendir(DIR, $docdir) || die "$docdir not found";
+  @file_list = grep { /^usersch.*tex/ } readdir(DIR);
+  closedir(DIR);
+
+  chdir($docdir);
+  $docfile = "usersch3.tex";
+  open(IN,"translations") || die("Could not find translation file, docdir='$docdir'");
+  while(<IN>)
+  {
+    chomp; @_ = split(/ *\@ */);
+    $key = shift(@_);
+    $transl{$key} = join('@', at _);
+  }
+  close(IN);
+}
+
+sub not_found {
+  my($help) = shift;
+  $help =~ s/\\\\/_B#K#S_/g;
+  $help =~ s/\\(.)/$1/g;
+  $help =~ s/_B#K#S_/\\/g;
+  print "'$help' not found !\n";
+}
+
+sub choose_chap {
+  while (s/\@([0-9])$//) { $docfile = "usersch$1.tex"; }
+  if (-f $docfile) { $pipe = ""; }
+  else
+  {
+    die "Cannot find $docfile"
+      if (! -f "$docfile.z" &&
+          ! -f "$docfile.gz" &&
+          ! -f "$docfile.Z" &&
+          ! -f "$docfile.bz2");
+    if (-f "$docfile.bz2") {
+        $pipe = $bzcat;
+        $docfile = "$docfile.bz2";
+    } else {
+        $pipe = $zcat;
+    }
+  }
+}
+
+sub safe_setsid {
+  eval {
+    require POSIX;
+    POSIX::setsid(); # detach from terminal (^C will not kill xdvi)
+  };
+}
+
+# assume we're in $docdir
+sub open_viewer_then_quit {
+  my $F = shift;
+  my ($f, $viewer, $redirect);
+  my $cygwin = ($^O =~ /cygwin/);
+  my $win32 = ($^O =~ /ms(win|ys)/);
+  my $osx = ($^O =~ /darwin/);
+
+  $f = "$F.dvi";
+  $f = "$F.dvi.gz" if (! -f "$f");
+  $f = "$F.pdf"    if (! -f "$f");
+  die "could not find \'$F\'" if (! -f "$f");
+  $F = $f;
+  $redirect = ' 2>/dev/null >/dev/null &';
+  if ($f =~ /\.dvi/)
+  { # DVI
+    $viewer = ($f =~ /refcard/)? $xdviref: $xdvi;
+  }
+  elsif ($cygwin)
+  { # PDF Win32
+    @_ = split(/"/, `acro.exe`);
+    ($viewer = $_[1]) =~ s,\\,/,g;
+
+    $redirect = "";
+    $F =~ s,/cygdrive/(.),$1:, ; # Reader can't cope with Cygwin paths
+    $F      = "\"$F\"";
+    $viewer = "\"$viewer\"";
+  }
+  elsif ($win32)
+  { # PDF Win32
+    @_ = split(/"/, "$ENV{GP_PDF_VIEWER}");
+    $viewer = $_[1];
+    print "using \'$viewer\', ";
+  }
+  elsif ($osx)
+  {
+    $viewer = "open";
+  }
+  else
+  { # PDF generic
+    $viewer = "acroread";
+  }
+
+  print "displaying \'$F\'.";
+  print "\n" if (!$fromgp);
+  safe_setsid();
+  if ($win32)
+  {
+    system($viewer,$F);
+  }
+  else
+  {
+    system("$viewer $F$redirect");
+  }
+  cleanexit();
+}
+
+sub treat {
+  my($help);
+  $_ = $_[0];
+  s/_QUOTE/'/g;
+  s/_BACKQUOTE/`/g;
+  s/_DOUBQUOTE/"/g;
+  s/^ *"(.*)"([^"]*) *$/$1$2/;
+  if (s/\@$//)
+  {
+    $found = 0;
+    $searchall = 1;
+    $help = $_;
+    for (@file_list)
+    {
+      next if (!/^usersch(.*)\.tex/);
+
+      &treat("$help\@$1");
+      if ($apropos && $#list > 0 || $#sentence_list > 0)
+      {
+        print "\nChapter $1:\n";
+        print "==========\n";
+        &apropos_final_print();
+      }
+    }
+    return not_found($help) if (!$found && !$apropos);
+    $searchall = 0;
+    $apropos = 0; return;
+  }
+  &choose_chap;
+
+  if (!$apropos)
+  {
+    $_ = "users" if (/^$/);
+    open_viewer_then_quit($_) if (/^(users|tutorial|refcard|libpari)$/);
+    if ($transl{$_}) { $_ = $transl{$_}; &choose_chap; }
+  }
+
+  s/(\W)/\\$1/g;
+  s/_/\\\\_/g;
+      ($pipe && open(DOC,"$pipe $docfile |"))
+  || (!$pipe && open(DOC,"$docfile")) || die "Cannot open $docfile: $!";
+  return &apropos($_) if ($apropos);
+
+  if (/^\\[<>=!](\\=)?$/ || /^\\\|\\\||\\&\\&$/ || /\\\||&/ || /===/)
+    { $_ = 'Comparison and Boolean operators'; }
+  $help = $_;
+
+  my ($first);
+  my ($pat) = $help;
+  if ($pat =~ /[a-zA-Z]$/) { $pat .= '\b'; } else { $pat .= '}'; }
+  while (<DOC>)
+  {
+    if (/\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter|label){$pat/)
+      { $first = $_; last; }
+  }
+  if (eof(DOC))
+  {
+    &not_found($help) if (!$searchall);
+    return;
+  }
+  $found = 1;
+  if (!$detex) { tex($first); }
+  else
+  {
+    &detex(); print "\n" if (!$fromgp);
+    # Avoid broken pipe from zcat
+    do {local $/; <DOC>} if $^O eq 'os2' and $pipe;
+  }
+  close(DOC);
+}
+
+#
+#  A propos
+#
+
+sub apropos_print_list {
+  $current = "";
+  @_ = sort(@_);
+  for (@_)
+  {
+    next if ($_ eq $current);
+    $current = $_; print "$indent$_\n";
+  }
+}
+
+sub apropos_raw_print {
+  $indent = "";
+  &apropos_print_list(@sentence_list);
+  &apropos_print_list(@list);
+}
+
+sub apropos_final_print {
+  my($maxlen) = 0;
+  my($i,$nbcol,$current);
+  my($cols) = ($ENV{'COLUMNS'} || 80) - 1;
+
+  if ($raw) { &apropos_raw_print(); return; }
+  @list = sort(@list);
+  for (@list)
+  {
+    $i= length($_);
+    $maxlen = $i if ($i > $maxlen);
+  }
+  $maxlen++; $nbcol = $cols / $maxlen;
+  $nbcol =~ s/\..*//;
+  $nbcol-- if  ($nbcol * $maxlen == $cols);
+  $nbcol = 1 if (!$nbcol);
+
+  $current = ""; $i = 0;
+  for (@list)
+  {
+    next if ($_ eq $current);
+    $current = $_; print($_); $i++;
+    if ($i >= $nbcol)
+    {
+      $i=0; print "\n"; next;
+    }
+    print " " x ($maxlen - length($_));
+  }
+  print "\n" if ($i);
+  if ($#sentence_list > 0)
+  {
+    print "\nSee also:\n" if ($#list > 0);
+    $indent = "  ";
+    apropos_print_list(@sentence_list);
+  }
+}
+
+sub apropos_check {
+  my($line, $current) = @_;
+  $line =~ s/\n/ /g;
+  return if ($line !~ /$help/i);
+
+  local($_) = $current;
+  s/\\b{(.)}/\\$1/;
+  s/\{\}//g;
+  s/\\pow/^/;
+  s/\\%/%/;
+  s/\\bs/\\/;
+  s/\\\#/\#/g;
+  s,\+\$/\$-,+/-,;
+  s/\$\(.*//; # remove argument lists
+  if (/ /) { push(@sentence_list,$_); } else { push(@list,$_); }
+}
+
+sub apropos {
+  my($line,$current,$new);
+  $help = $_[0];
+  $help='\\\\pow' if ($help eq '\^');
+  $help='\\\\til' if ($help eq '\~');
+  @sentence_list = @list = "";
+  while (<DOC>)
+  {
+    if (/^\\(subsubsec[a-z]*|subsec[a-z]*|section|chapter){/)
+    {
+      $new = &get_match($_,'{','}');
+      &apropos_check($line, $current);
+      $current = $new; $line = "";
+    }
+    $line .= $_;
+  }
+  &apropos_check($line, $current);
+}
+
+##
+##  Tex Part
+##
+# Actual text is in file TEX. Parimacro + Geometry info goes to WRAP
+sub pretex {
+  my ($basedir) = $ENV{GPHELP_TMPDIR} || $ENV{GPTMPDIR} || $ENV{TMPDIR} || "/tmp";
+  $tmpdir = "$basedir/gp.help$$";
+  mkdir $tmpdir, 0755 || die "Cannot create temporary directory";
+  $texfile = "$tmpdir/gp.help";
+
+  open(TEX,">$texfile.tex") || die "Couldn't open $texfile.tex";
+}
+
+sub tex { my ($first) = @_;
+  print TEX $first;
+  while (<DOC>)
+  {
+    last if /^\\(section|sub[sub]*sec)/i;
+    print TEX;
+  }
+}
+
+sub posttex {
+  my ($wrap) = "$tmpdir/gpwrapper.help";
+  my (@goners) = ("$texfile.tex",
+                  "$wrap.tex", "$wrap.dvi", "$wrap.log", "$wrap.aux");
+  if (!$found) { unlink @goners; rmdir("$tmpdir"); cleanexit(); }
+
+  open(WRAP, ">$wrap.tex") || die "Couldn't open $wrap.tex";
+  if ($balloon) {
+    print WRAP '\nopagenumbers\def\fromgphelp{}'
+    . "\\input $docdir/parimacro.tex"
+    . '\setbox0\vbox{'
+    . "\\input $texfile.tex"
+    . ' }
+\dimen0=\the\ht0 \advance\dimen0 by \dp0
+\advance\dimen0 by 60pt
+\dimen1=\the\wd0
+\advance\dimen1 by 60pt
+\vsize \dimen0
+\hsize \dimen1
+\advance\voffset 30pt\advance\hoffset 30pt
+\advance\hoffset-1in \advance\voffset-1in
+\special{papersize=\the\dimen1,\the\dimen0}
+\noindent\box0
+\end';
+  } else {
+    print WRAP '\nopagenumbers\def\fromgphelp{}'
+    . "\\input $docdir/parimacro.tex"
+    . "\\input $texfile.tex"
+    . '\end';
+  }
+  close(WRAP) || die "Error closing '$wrap.tex': $!";
+  close(TEX) || die "Error closing '$texfile.tex': $!";
+
+  chdir($tmpdir);
+  $out = `$tex $wrap.tex 2>&1 < /dev/null`;
+  -f "$wrap.dvi"
+    || die "could not create '$wrap.dvi': status=$?, $out";
+  safe_setsid();
+  my ($goners) = join(" ", @goners);
+  system("($xdvi $wrap.dvi 2>/dev/null >/dev/null; rm -f $goners; rmdir $tmpdir)&");
+}
+
+#
+#  Detex Part
+#
+sub fit_loop {
+  my($i);
+  return if ($miss > 9 || $#_ <= 0);
+  while ($miss > 0)
+  {
+#  print "1:$miss ";print @_;print "\n";
+    for (@_) { $miss-- if (s/([?!\.;])$/$1 /);
+       return if ($miss == 0);
+    }
+#  print "2:$miss ";print @_;print "\n";
+    for (@_) { $miss-- if (s/([?!\.;]) $/$1  /);
+       return if ($miss == 0);
+    }
+#  print "3:$miss ";print @_;print "\n";
+    for (@_) { $miss-- if (s/([\),])$/$1 /);
+       return if ($miss == 0);
+    }
+#  print "4:$miss ";print @_;print "\n";
+    $i = 0;
+    for (@_)
+    {
+      if (!$i) { $i = 1; next; }
+      $miss-- if (s/^\(/ (/);
+       return if ($miss == 0);
+    }
+#  print "5:$miss "; print @_;print "\n";
+    for (@_)
+    {
+       next if (/^ *$/);
+       $miss--; s/$/ /;
+       return if ($miss == 0);
+    }
+  }
+}
+
+sub fit_line {
+  my($wi, @a);
+  my($l) = -1;
+  my($rem) = $_[0];
+  for (@l)
+  {
+     my ($l2) = $l; $l += ($_ + 1);
+     if ($l > $rem) { $l = $l2; last; }
+     $wi++;
+  }
+  $miss = $rem - $l;
+  splice(@l, 0, $wi);
+  @a = splice(@w, 0, $wi-1); &fit_loop(@a);
+  push(@a, shift(@w)); return join(' ', @a);
+}
+
+# empty output line
+sub is_void {
+  my($in) = shift;
+  $in =~ s/\@\[\w+\]//g;
+  $in =~ s/\@[012]//g;
+  ($in =~ /^\s*$/)? 1: 0;
+}
+
+sub nl { push(@f_text, shift); }
+
+sub split_words { my ($txt) = @_;
+  $txt =~ s/^ +//;
+  for ( split(/\s+/, $txt) )
+  {
+    s/\Q$tr{nbrk}/ /g;
+    my ($w) = $_;
+    # these codes will be replaced by 1 character
+    s/\@\[(obr|cbr|ouml|uuml|agrav|eacute|ldollar|lt|gt|\{|\})]/\@/g;
+    s/\@\[pm]/+\/-/g;
+    # the rest will be replaced by zero-width characters
+    s/\@\[\w+\]//g;
+    my ($l) = length($_);
+    # zero-width word
+    if (!$l && $#w >= 0) { $w[$#w] .= $w; next; }
+    push(@l, $l);
+    push(@w, $w);
+  }
+  # first word might still be zero-width
+  if ($#w >= 1 && !$l[0])
+  {
+    splice(@w, 0,2, "$w[0]$w[1]");
+    splice(@l, 0,1);
+  }
+}
+
+sub format_text {
+  my($last_void) = 0;
+  my($noindent) = 0;
+  my($init) = 1;
+  my($cols) = ($ENV{'COLUMNS'} || 80) - 1;
+  my($first) = $cols - length($indent);
+
+  for (@text)
+  {
+    if (s/^\@1//)       # start verbatim
+    {
+      nl(&fit_line($first)) if (@w);
+      nl("") if (!$last_void && !is_void($_)); # add empty line
+      nl("$indent$_"); next;
+    }
+    if (s/^\@0//)    # verbatim lines
+    {
+      nl("$indent$_"); next;
+    }
+
+    if (s/^\@2//)       # end verbatim, add indent
+    {
+      nl("") if (!$last_void);
+      $last_void = 1;
+      split_words($_); next;
+    }
+
+    if (s/^\@3//)       # end verbatim + no indent
+    {
+      nl("") if (!$last_void);
+      $noindent = 1;
+      $last_void = 1;
+      split_words($_); next;
+    }
+
+    if (!is_void($_)) { split_words($_); next; }
+
+    # line is empty, split out previous paragraph
+    next if (!@l || !$l[0]); # nothing
+    if ($init) {
+      nl(&fit_line($first));
+    } else {
+      nl("") if (!$last_void);
+      nl("\@[endbold]" . ($noindent? "": $indent) 
+                       . &fit_line($noindent? $cols: $first));
+    }
+    while (@w) { nl(&fit_line($cols)); }
+    $noindent = $init = $last_void = 0;
+  }
+}
+
+# argument has the form s1${open}s2${close}s3
+# Return 's2'. Set $remainder to 's3'.
+sub get_match {
+  local ($_, $open, $close) = @_;
+  my (@tmp, $arg,$parity,$ok);
+  my ($obr) = 1;
+  $parity = ($open eq $close);
+  /$open/; $_ = $'; # remove everything before (and including) first $open
+
+  while ($_) {
+    @tmp = split(/($open|$close)/);
+    while ($#tmp >= 0) {
+      $_ = shift(@tmp);
+      $obr++ if (/^$open$/);
+      if ($parity && $obr == 2) { $ok = 1; last }
+      $obr-- if (/^$close$/);
+      if (!$obr) { $ok = 1; last }
+      $arg .= $_;
+    }
+    last if ($ok);
+    $_ = <DOC>;
+  }
+  $remainder = join('', at tmp);
+  return $arg;
+}
+
+sub detex {
+  my($fun);
+# 1: get the function "prototype"
+  $fun = &get_match($_,'{','}');
+  $fun = &basic_subst($fun);
+  $_ = $remainder;
+  $_ = <DOC> if (!&basic_subst($_));
+  push(@text, "\@[startbold]$fun:\@[endbold]");
+  push(@text, "");
+# 2: parse the function description
+  if ($_) { s/^ *://; &presubst(); }
+  while (<DOC>)
+  {
+    last if /^\\(section|sub[sub]*sec)/i;
+    &presubst();
+  }
+  if ($raw) { print join("\n", @text); return; }
+#  for (@text) { print("AA{$_}BB\n"); } # DEBUG
+  &format_text();
+  for (@f_text) { &TeXprint($_); }
+}
+
+# We use the special char @ to transmit special sequences
+sub inittr {
+  @ou = qw( dollar nbrk startbold endbold startcode endcode
+	    obr cbr uuml ouml agrave eacute
+	    startpodcode endpodcode startlink endlink
+	    startbcode endbcode startbi endbi startit endit
+	    startword endword startlword endlword pm empty gt lt podleader );
+
+  @tr{@ou} = map "\@[$_]", @ou;
+  $tr{dollar} = '$' if $to_pod;
+
+  %pr = ( dollar => '',
+	  ldollar => '$',	# literal dollar
+	  nbrk => 'S< >',
+	  startbold => 'B<',
+	  endbold => '>',
+	  startcode => 'C<',
+	  startlink => 'L<',
+	  endlink => '>',
+	  endcode => '>',
+	  obr => '{',
+	  cbr => '}',
+	  startpodcode => 'C<',
+	  endpodcode => '>',
+	  ( $dumb_pod
+	    ? (startbcode => 'B<',
+	       endbcode => '>',
+	       startbi => 'B<',
+	       endbi => '>',)
+	    : (startbcode => 'B<C<',
+	       endbcode => '>>',
+	       startbi => 'B<I<',
+	       endbi => '>>')),
+	  startit => 'I<',
+	  endit => '>',
+	  startword => 'F<',
+	  endword => '>',
+	  startlword => ' F<',
+	  endlword => '> ',
+	  pm => 'F<+->',
+	  "gt" => 'E<gt>',
+	  "lt" => 'E<lt>',
+	  ouml => 'E<ouml>',
+	  uuml => 'E<uuml>',
+	  eacute => 'E<eacute>',
+	  agrave => 'E<agrave>',
+	  empty => 'Z<>',
+	  podleader => '=',
+	);
+}
+
+sub indent_equally { my $in = shift; $in =~ s/^[ \t]*/    /mg; $in}
+
+sub basic_subst {
+  local($_) = shift;
+
+  s/(\S)[ \t]*\n[ \t]+/$1\n/gm;
+  s/([^\\])\\\{/$1$tr{obr}/g;
+  s/([^\\])\\\}/$1$tr{cbr}/g;
+  s/([^\\])\\-/$1/g;
+  s/\A\\q?quad(?![a-zA-Z])\s*/$tr{nbrk}$tr{nbrk}/;
+  s|\\wwwsite|$wwwsite|g;
+  s/^\\def\\.*\{\n.*\n\}//gm;
+  s/\\def\\.*//g;
+  s(\\footnote\s*\{?\*+\}?\s*\{\s*((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\})
+    {$tr{startbold}FOOTNOTE$tr{endbold}$tr{lt}$tr{lt}$tr{lt} $1 $tr{gt}$tr{gt}$tr{gt}}g;
+  s/(\{[\w\s]+)\{\}([\s\w]+\})/$1$2/g;	# {nf{}init}
+  s(\\op(?![a-zA-Z])\s*)({\\it op\\/})g;	# {nf{}init}
+  s/\\emacs\b//;
+  s/\\unix\b//;
+  s/\\(leavevmode|strut)(?![a-zA-Z])\s*//g;
+  s/ \\funno \s*
+     { \s* ((?:[^{}]|\{[^{}]*\})*) } \s*
+     { \s* ((?:[^{}]|\{[^{}]*\})*) } \s*
+     { \s* ((?:[^{}]|\{[^{}]*\})*) }
+   /\\noindent{\\tt $1 \$\\key{$2}\$($3)}/gx;
+  s/\\fun\s*\{([^{}]*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}\s*\{((?:[^{}]|\{[^{}]*\})*)\}/\\kbd{$1 \\key{$2}($3)}\\sidx{$2}/g;
+
+  s/\\\\(?=[a-zA-Z])/\\bs /g;
+  s/\\b{}\\b{}/\\bs\\bs /g;
+  s/\\\\/\\bs/g;
+  s/(\'\'|\`\`)/"/g unless $to_pod;     # (english) double quotes
+  # asymptotic or isomorphic (~) [beware of ties]
+  s/(^|[^\\]) +~/$1~/;
+  s/~ */~/;
+  s/(^|[^\\])~/$1$tr{nbrk}/g;           # ties
+  s/\\(simeq|sim|approx)(?![a-zA-Z])/ ~ /g;
+  s/\\til(?![a-zA-Z])/~/g;		# ~ (transpose)
+  s/\\(~|tilde)/~/g;
+
+  s/\\(equiv)(?![a-zA-Z])/ = /g;
+  s/\\`a/$tr{agrave}/; s/\\`{a}/$tr{agrave}/;
+  s/\\"o/$tr{ouml}/;   s/\\"{o}/$tr{ouml}/;
+  s/\\"u/$tr{uuml}/;   s/\\"{u}/$tr{uuml}/;
+  s/\\'e/$tr{eacute}/; s/\\'{e}/$tr{eacute}/;
+
+  s/(^|[^\\])%.*/$1/g;		        # comments
+  s/\\vadjust\s*\{\s*\\penalty\s*\d+\s*\}//g;
+
+  # We do not strip %\n, thus:
+  s/\\kbd{\n\s*/\\kbd{/g;
+  s/\$\\bf(\b|(?=[\d_]))\s*([^\$]+)\$/\$$tr{startbcode}$1$tr{endbcode}\$/g;
+  s/\$/$tr{dollar}/g;		        # math mode
+  s/\t/ /g; s/\\,//g; s/\\[ ;]/ /g;     # various spaces
+  s/\\\///g;			# italic correction
+  s/^&+//g;			# tab marks
+  s/([^\\])&+/$1 /g;		# tab marks
+  s/\\TeX\{\}/TeX/g;
+  s/\\TeX(\W)/TeX$1/g;
+  s/ *\\circ\b */ o /g;
+  s/\\d?frac{\s*((?:[^{}]|\{[^{}]*\})*)}{\s*((?:[^{}]|\{[^{}]*\})*)}/($1)\/($2)/g;
+  s(\\d?frac\s*(\d)\s*(\d))(($1/$2))g;
+  s[{\s*(\w)\s*\\over(?![a-zA-Z])\s*(\w)\s*}]{($1/$2)}g;
+  s[{\s*((?:[^{}]|\{[^{}]*\})*)\\over(?![a-zA-Z])\s*((?:[^{}]|\{[^{}]*\})*)}][($1)/($2)]g;
+
+  # \def\synt#1#2{\syn{#1}{\tt #2}}
+  # \def\syn#1#2{\synx{#1}{#2}{#1}}
+  s/\\synt?\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\synx{$1}{$2}{$1}/g;
+  # \def\synx#1#2#3{\sidx{#3}The library syntax is $\key{#1}({#2})$}
+  # Often used with embedded {}.
+  s/\\synx\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{((?:[^{}]|\{[^{}]*\})*)\}/\\sidx{$3}The library syntax is $tr{startcode}$tr{startbold}$1$tr{endbold}($2)$tr{endcode}/;
+
+  # May be used with an empty arg
+  s/\\typ\{([^\}]*)\}/$tr{startcode}t_$1$tr{endcode}/g;
+
+  s/(\\string)?\\_/_/g;
+  s/\\([#\$&%|])/$1/g;
+  s/\\(hat(?![a-zA-Z])|\^)({\\?\s*})?/^/g;
+  s/^(\@\[podleader\]head\d *)\\pow(?![a-zA-z])( *)/$1^$2/gm;
+  s/ *\\pow(?![a-zA-z]) */^/g;
+
+  s/\\neq?(?![a-zA-Z])/ != /g;
+  s/\\enspace(?![a-zA-Z])/ /g;
+  s/\\\*/ /g;
+  s/\\times(?![a-zA-Z]) */ x /g;
+  s/\\infty(?![a-zA-Z]) */ oo /g;
+  s/ *\\(bmod|mod) */ mod /g;
+  s/ *\\pmod(?![a-zA-Z]) *\{\s*((?:[^{}]|\{[^{}]*\})*)\}/ (mod $1)/g;
+  s/ *\\cdot(?![a-zA-Z]) */./g;		# Maybe " . "?
+  s/ *(\\|\@)[lc]?dots(?![a-zA-Z]) */.../g;
+  s/\\(Id|Norm|disc|Cl|log|sin|cos|lim(proj)?|tan|mod|sqrt|exp|ln|det|Re|Im|deg|wp|cap|oplus)(?![a-zA-Z])/$tr{startlword}$1$tr{endlword}/g;
+  s/\\pi(?![a-zA-Z])/$tr{startword}Pi$tr{endword}/g;
+  s/\\(Alpha | Beta | Chi | Delta | Epsilon | Phi | Gamma | Eta | Iota
+      | vartheta | Kappa | Lambda | Mu | Nu | Omicron | Pi | Theta | Rho
+      | Sigma | Tau | Ypsilon | varsigma | Omega | Xi | Psi | Zeta
+      | alpha | beta | chi | delta | epsilon | varepsilon | phi | gamma | eta
+      | iota | varphi | kappa | lambda | mu | nu | omicron | pi | theta | rho
+      | sigma | tau | ypsilon | varpi | omega | xi | psi | zeta
+      | int | expr | seq | args | gcd | lcm | sum | prod | Re | infty )
+      (?![a-zA-Z])/$tr{startword}$1$tr{endword}/xg;
+  s/ *\\in(?![a-zA-Z]) */ belongs to /g;
+  s/\\pm(?![a-zA-Z])/$tr{pm}/g;
+  s/ *\\mid(?![a-zA-Z]) */ | /g;
+
+  s/\\idxtyp\{([^{}]*)\}/\\sidx{t_$1}/g;
+  s/\\ref\{([^\}]*)\}/[$tr{startbold}Label: $1$tr{endbold}]/g unless $to_pod;
+  s/\\secref\{([^\}]*)\}/Section [$tr{startbold}Label: $1$tr{endbold}]/g unless $to_pod;
+  s/\\label\{[^\}]*\}//g unless $to_pod;
+
+  s/ *\\noindent\b */\@3/;
+  s/\\(medskip|bigskip|smallskip|left|right)(?![a-zA-Z])[ \t]*//g;
+  s/\\vfill *(\\eject)?//g;
+  s/\\(q|quad)(?![a-zA-Z])\s*/  /g;
+  s/\\qquad(?![a-zA-Z])\s*/    /g;
+
+  s/\\centerline\s*\{\s*(?:\\tt\b\s*)?(.*(\n[ \t].*)*)\}(?=\s*$)/indent_equally($1)/ge;
+  s/\\centerline\s*\{\s*(?:\\tt\b\s*)?((?:[^{}]|\{[^{}]*\})*)\}/ indent_equally($1)/ge;
+
+  s/\\big\b//g;
+
+  s/\\settabs.*//;
+  s/^\\\+/\n$tr{nbrk}/gm;
+  s/\\\+//g;
+  s/\\cr(?![a-zA-Z])//g;
+  s/\\B(?![a-zA-Z])/\\kbd{BIL}/g;
+
+  s/ *([=><]) */ $1 /g;
+  s/ *<  *([=<]) */ <$1 /g;
+  s/ *>  *([=>]) */ >$1 /g;
+  s/ *([*+-\/^&=|:]) += */ $1= /g;
+  s/ *! *= */ != /g;
+  s/ == = / === /g;
+
+  s/ *\\Rightarrow */ ==$tr{gt} /g;
+  s/\\rangle(?![a-zA-Z])\s*/$tr{startcode}$tr{gt}$tr{endcode}/g;
+  s/\\langle(?![a-zA-Z])\s*/$tr{startcode}$tr{lt}$tr{endcode}/g;
+  s/\\rightarrow(?![a-zA-Z])\s*/$tr{startcode}--$tr{gt}$tr{endcode}/g;
+  s/\\longleftrightarrow(?![a-zA-Z])\s*/$tr{startcode}$tr{lt}-----$tr{gt}$tr{endcode}/g;
+  s/\\mapsto(?![a-zA-Z])\s*/$tr{startcode}|---$tr{gt}$tr{endcode}/g;
+  s/ *\\geq?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{gt}=$tr{endcode} $1/g;
+  s/ *\\leq?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{lt}=$tr{endcode} $1/g;
+  s/ *\\gg?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{gt}$tr{gt}$tr{endcode} $1/g;
+  s/ *\\ll?(?![a-zA-Z]) *([^ ])/ $tr{startcode}$tr{lt}$tr{ll}$tr{endcode} $1/g;
+
+  s/\\(vers|PARIversion)(?![a-zA-Z])/$tr{startbold}$version$tr{endbold}/;
+  s/\\([QRCFZNapdf])(?![a-zA-Z])/$tr{startbi}$1$tr{endbi}$2/g;
+  s/\\([QRCFZN])\1(?![a-zA-Z])/$tr{startbi}$1$tr{endbi}$2/g;
+  s/\\Bbb\b\s*(\w)/$tr{startbi}$1$tr{endbi}/g;
+
+  s/\\([oc]br)/$tr{$1}/g;
+  s/\\quo(?![a-zA-Z])/\"/g;
+  s/(^|\s)\{(\w+)\}/$1$2/g;
+
+  s/\\p(?![a-zA-Z])/$tr{startbold}p$tr{endbold}$1/g;
+  s/^ *\\point\{([^\}]*)\}/\\item $1/g;
+  s/\@\[dollar]\\bullet\@\[dollar]/\\item /g;
+  s/\\bullet/\\item/g;
+  s/^ *\\item/\@3$tr{startbold}*$tr{endbold}/g;
+  s/\\item/$tr{startbold}*$tr{endbold}/g;
+  s/^ *\\misctitle\{([^\}]*)\}/\@3$tr{startbold}$1.$tr{endbold}/g;
+  s/\\subsec\{([^\}]*)\}/\@3$tr{startbold}$1.$tr{endbold}/g unless $to_pod;
+  s/\\teb\{([^\}]*)\}/\\sidx{$1}$tr{startbold}$1$tr{endbold}/g;
+  s/\\tet\{([^\}]*)\}/\\sidx{$1}$tr{startcode}$1$tr{endcode}/g;
+  s/\\tev\{([^\}]*)\}/\\sidx{$1}$tr{startit}$1$tr{endit}/g;
+  s/\\\$/$tr{ldollar}/g;
+  s/\\kbd\s*\{\s*</\\kbd{$tr{lt}/g if $to_pod;
+  s/\\kbd\s*\{\s*>/\\kbd{$tr{gt}/g if $to_pod;
+  s/\\kbd\s*\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startcode}$1$tr{endcode}/g;
+
+  s/\\key\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g unless $refcard;
+  s/\\goth\{((?:[^{}]|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g;
+
+  if ($refcard) {
+    s/\\(?:key|li)\{((?:[^{}]+(?=[{}])|\{[^{}]*\})*)\}\s*\{\}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/g;
+    s/\\(?:key|li)\{((?:[^{}]+(?=[{}])|\{[^{}]*\})*)\}\s*\{(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/\n=item $tr{startcode}$2$tr{endcode}\n\n$1\n/g;
+  }
+
+  s/\\(floor|ceil|round|binom)\{/$1\{/g;
+  s/\\(var|emph)\{([^\}]*)\}/$tr{startit}$2$tr{endit}/g;
+  s/\\fl(?![a-zA-Z])/$tr{startit}flag$tr{endit}/g;
+  s/\\b{([^}]*)}/$tr{startcode}\\$1$tr{endcode}/g;
+  s/\\kbdsidx/\\sidx/g;
+  s/\\sidx\{[^\}]*\}//g unless $to_pod;
+  s/\\[a-zA-Z]*idx\{([^\}]*)\}/$1/g unless $to_pod;
+  s/{\\text{(st|nd|th)}}/\\text{$1}/g;
+  s/\^\\text{th}/-th/g;
+  s/1\^\\text{st}/1st/g;
+  s/2\^\\text{nd}/2nd/g;
+
+  s/\\(text|hbox|Big)//g;
+  s/^([ \t]+)\{ *\\(it|sl|bf|tt)\b/S<$1>{\\$2/gm;
+  s/\{ *\\(it|sl) *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startit}$2$tr{endit}/g;
+  s/\{ *\\bf *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startbold}$1$tr{endbold}/g;
+  s/\{ *\\tt *(([^{}]+(?=[{}])|\{[^{}]*\})*)\}/$tr{startpodcode}$1$tr{endpodcode}/g;
+  $seek=1 if (s/\\emph{ */$tr{startit}/g);
+  if ($seek) { $seek=0 if (s/\}/$tr{endit}/) }
+  s/\\(backslash|bs)\{(\w)\}/\\$2/g;
+  s/\\(backslash|bs)(?![a-zA-Z]) */\\/g;
+  s/ *\\setminus */ \\ /g;
+
+  s/\@com(.*)$/$tr{startcode}$1$tr{endcode}/g;
+
+  # Last resort:
+  s/\\kbd\s*\{(.*?)\}/$tr{startcode}$1$tr{endcode}/g;
+  s/^([ \t]{3,})\Q$tr{startcode}\E(.*)\Q$tr{endcode}\E/$1$2/gmo if $to_pod;
+  # Last resort:
+  s/^([ \t]{3,})\Q$tr{startcode}\E(.*?)\Q$tr{endcode}\E/$1$2/gmso if $to_pod;
+  # Remove leading spaces unless have embedded wrapped code:
+  s/^[ \t]+//gm if $to_pod and /^\S/ and not /^[ \t]*\n[ \t]/m;
+  s/\{ *\}//g;			# empty args
+
+  s{\Q$tr{startcode}\E((ftp|http)://.*?)\Q$tr{endcode}\E}{$tr{startlink}$1$tr{endlink}}go if $to_pod;
+  $_;
+}
+
+sub presubst {
+  chomp;
+  if ($in_prog && /\@eprog *(\\noindent)? */)
+  {
+    my ($eprog) = $1? '@3': '@2';
+    $in_prog = 0;
+    $_ = $eprog . &code_subst($`) . $tr{endcode};
+    push(@text, $_);
+    $_ = &basic_subst($');
+  }
+  elsif ($in_prog || s/\\bprog(tabs.*)?//g)
+  {
+    $in_prog++;  # = 1 on the \bprog line
+                 # code should start on the next line
+    $_ = &code_subst($_);
+    s/^/\@1$tr{startcode}/ if ($in_prog == 2);
+    s/^/\@0/ if ($in_prog > 2);
+  }
+  else { $_ = &basic_subst($_); }
+  if (/^ *$/)
+  {
+    push(@text,"\n");
+  }
+  else
+  {
+    for (split(/\n/, $_)) { push(@text, $_); }
+  }
+}
+
+sub code_subst {
+  my $in = shift;
+  $in =~ s/\@dots\b/.../g;
+  if ($in =~ /\@com(.*)/)
+  {
+    if ($to_pod) {
+      $in = $` . &basic_subst($1) . code_subst($');
+    } else {
+      $in = $` . $tr{endcode} . &basic_subst($1) . $tr{startcode} . code_subst($');
+    }
+  }
+  if ($in =~ /\@Ccom(.*)\*\//)
+  {
+    if ($to_pod) {
+      $in = $` .		&basic_subst($1) . "*/" . &code_subst($');
+    } else {
+      $in = $` . $tr{endcode} . &basic_subst($1) . $tr{startcode}
+                                                 . "*/" . &code_subst($');
+    }
+  }
+  $in;
+}
+
+sub wrap_code {
+  my $in = shift;
+  $in =~ s/^[ \t]+$//mg;
+  $in = &code_subst($in);
+  $in =~ s/^(.)/  $1/mg;
+  $in =~ s/\s*\Z//;
+#  $in =~ s/\\kbd\{((?:[^{}]|\{[^{}]*\})*)\}/$1/g if $to_pod;
+  $in =~ s/\$([^\$\n]*)\$/$1/g if $to_pod;
+  "\n\n$in\n\n";
+}
+
+sub indexify {
+  my $in = shift;
+  $in =~ s/(^|and\s+)(\w+)(\$?\()/$1\\idx{$2}$3/g;
+  $in =~ s/^(\\b\{\w+\})(?!\S)/\\idx{$1}/g;
+  $in;
+}
+
+sub for_index {
+  my $in = shift;
+  1 while $in =~ s/\Q$tr{startcode}\E(.*?)\Q$tr{endcode}\E/$1/go;
+  $in;
+}
+
+sub strip_trail { my $in = shift; $in =~ s/\s+\Z//; $in }
+
+# This subroutine works in paragraph mode
+sub TeXprint_topod {
+  s/\A\s+//;
+  s/^\\def\\.*\{\n.*\n\}//gm;
+  s/\\def\\.*//g;		# Repeated in basic_subst, as the next one
+  s/(\{[\w\s]+)\{\}([\s\w]+\})/$1$2/g;	# {rnf{}llgram}
+
+  s/\\vbox\s*\{\s*\\bprog/\\bprog/g;
+  s/([\\\@])eprog\s*\}/$1eprog/g;
+
+  #  \n is below to prevent splitting on ' '
+  #  We also remove ':'
+  s/\\sectype\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\subsec{Type \\typ{$1} (${2}s)}\n\\sidx{$2}/g;
+  s/\\sectypeindex\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\subsec{Type \\typ{$1} (${2}s)}\n\\sidx{$3}/g;
+  s/\\sectypes\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}\{\s*((?:[^{}]|\{[^{}]*\})*)\}/\\subsec{Type \\typ{$1} and \\typ{$1} (${3}s)}\n\\sidx{$3}/g;
+
+  # Try to guard \label/\sidx (removing possible '.')
+#  This somehow breaks index...
+#  s/(\\(?:section|subsec(?:ref|idx|op)?(unix)?)\s*{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)})\.?\s*\\(label|sidx)/$1\n\\$2/;
+  s/(\\(?:section|subsec(?:ref|idx|op)?)\s*{(?:(?:[^{}]+(?=[{}])|{[^{}]+})+)})\.?\s*\\(label|sidx)/$1\n\\$2/;
+
+  # last if /\\subsec[\\{}ref]*[\\\${]$help[}\\\$]/o;
+  s/\\chapter\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/\n\n$tr{podleader}head1 NAME\n\nlibPARI - $1\n\n/;
+  s/\\appendix\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/\n\n$tr{podleader}head1 NAME\n\nAppendix - $1\n\n/;
+  s/\\section\s*{((?:[^{}]|\{[^{}]*\})*)}\s*/"\n\n$tr{podleader}head1 " . indexify($1) . "\n\n"/e;
+
+  # Try to delimit by :
+  s/\\subsec(?:ref)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^\n]*):[\n ]/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
+  s/\\subsubsec(?:ref)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^:]*):\s*/"\n\n$tr{podleader}head3 " . indexify("$1$3") . "\n\n"/e;
+  s/\\subsubsec\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}(.*)$/"\n\n$tr{podleader}head3 " . indexify("$1") . "$3\n\n"/me;
+  s/\\subseckbd\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}([^:]*):\s*/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
+  # Try to delimit by ' '
+  s/\\subsec(?:ref)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]+})+)}(\S*)\s+/"\n\n$tr{podleader}head2 " . indexify("$1$3") . "\n\n"/e;
+  s/\\subsec(?:title)?(?:unix)?\s*{(([^{}]+(?=[{}])|{[^{}]*})+)}:?\s*/"\n\n$tr{podleader}head2 " . indexify("$1") . "\n\n"/e;
+
+  # This is to skip preface in refcard:
+  /\Q$tr{podleader}\Ehead1|\\title(?![a-zA-Z])\s*\{/o and $seen_start = 1
+    or $seen_start or return;	# Skip now!
+
+  s/\\title\s*\{([^{}\s]*)(\s+([^{}]*))?\}(\s*\\centerline\s*\{([^{}]*)\})?\s*/$tr{podleader}head1 NAME\n\n$1 - $3.  $5\n\n/ and $seen_title++
+    unless $seen_title;
+  s/\\title\s*\{([^{}\s]*)(\s+([^{}]*))?\}(\s*\\centerline\s*\{([^{}]*)\})?\s*/\n\n/;
+  s/\\parskip.*/\n/g;		# Up to end of the line
+  #s/([A-Z])\</$1 < /g;		# Disambiguate with POD...
+
+  # Duplicate removal of settabs, since they may contain \hskip
+  s/\\settabs.*//;
+  s/^[ \t]*\\hskip\s*\w+/$tr{nbrk}/g;
+  s/[ \t]*\\hskip\s*\w+/\n$tr{nbrk}/g;
+  1 while s/ \\
+	     ( (small|big)skip | newcolumn | noindent
+	     | (short)?copyrightnotice | hfill | break | par
+	     | leavevmode | strut | endgroup | bye
+             )
+	     (?![a-zA-Z])[ \t]*(\n\s*)
+	     /\n\n/gx;
+
+  s/'(\W)'/{\\tt '$1'}/g;
+  s/(\\\w+)(\\hbox)/$1 $2/g;
+  s/\\hbox\s*\{(?:\\it\b\s*)?((?:\\[\{\}]|[^{}]|\{[^{}]*\})*)\}/$1/g;
+
+  # substitute non-verbatim code
+  $acc = '';
+  pos = 0;
+  while ( s/\A(.*?)\\bprog//s ) {
+    $acc .= basic_subst(strip_trail($1));
+    $_ .= <DOC> until /(\\|@)eprog\b/ or eof(DOC);
+    $acc .= wrap_code($1) if s/\A(?:tabs[^\n]*)?(?![a-zA-Z])[ \t]*\n?(.*?)(\\|@)eprog\s*//s;
+  }
+  $_ = $acc . basic_subst($_);
+
+#  s/\\kbd\{/\{\\tt /g;		# startcode
+#  s/\\typ\{/\{\\tt t_/g;	# startcode
+
+  s/\$\s*(\@\[startbi\][A-Z]\@\[endbi\])\s*\$/$1/g;
+#  s/\\p(\b|(?=[\d_]))/B<p>/g;
+  #s/\$\\bf\b\s*([^\$]+)\$/C<B<$1>>/g;
+
+  @lines = split /^$/m, $_;
+  for (@lines) {
+    s/>/\@[gt]/g unless /^\n*[ \t]/;
+    s/</\@[lt]/g unless /^\n*[ \t]/;
+  }
+  $_ = join '', @lines;
+
+  s/\$\$(.*?)\$\$\s*/\n\nS<  >$tr{startcode}$1$tr{endcode}\n\n/gs;
+  s/\$([^\$]+)\$/$tr{startcode}$1$tr{endcode}/g;
+
+  s/\\s(?:ref|idx){\s*([^{}]*)}/"X<" . for_index($1) . ">"/ge; #
+  s/\\(?:ref|idx){\s*([^{}]*)}/"X<" . for_index($1) . ">$1"/ge;
+
+# Conflict between different versions of PARI and refcard:
+# s/\\(?:key|li)\s*{(.*)}\s*{(.+)}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/msg;
+# s/\\(?:key|li)\s*{(.*)}\s*{}[ \t]*\n/\n\n=back\n\n$1\n\n=over\n\n/mgs;
+# s/\\(key|var)(?![a-zA-Z])\s*{(\w+)}/C<$2>/mg;
+  s/\\var\s*{X<(\w+)>(\w+)}/X<$1>$tr{startcode}$2$tr{endcode}/mg;
+  s/\\var\s*{f{}lag}/$tr{startcode}flag$tr{endcode}/mg;
+
+  s/\\metax(?![a-zA-Z])\s*{(.*)}\s*{\s*(\w+)(?=C\<)(.*)}[ \t]*\n/\n\n=item C<L<$2>$3>\n\n$1\n\n/mg;
+  s/\\metax(?![a-zA-Z])\s*{(.*)}\s*{(.*)}[ \t]*\n/\n\n=item C<$2>\n\n$1\n\n/mg;
+  s/C\<\{\}=/C\<=/g;
+  s/\\fl(?![a-zA-Z])/I<flag>/g;
+  s/\\file(?![a-zA-Z])/F<file>/g;
+  s/\\(unix|emacs)\b\s*(\{?)(\s*\\(no)?indent)?\s*/X<\U$1>$2/g;
+  s/\A\\label\s*\{([\w:.-]*)\}([ \t]*\n\s*(?=[^\s=]))?/X<Label $1>/g;
+  s/\\label\s*\{([\w:.-]*)\}/X<Label $1>/g;
+  s/\\secref\s*\{([\w:.-]*)\}/L<Label $1>/g;
+  s/\\begin(double)?indentedkeys\s*/\n\n=over\n\n/g;
+  s/\\end(double)?indentedkeys\s*/\n\n=back\n\n/g;
+  # begin/end group appear in very special context only
+  s/\\begingroup\W.*//s;		# Eat to the end
+  s/\n{3,}/\n\n/g;
+  s/\\subsec\{((?:[^{}]|\{[^{}]*\})+)\}\s*/\n\n=back\n\nB<$1>\n\n=over\n\n/g; # In refcard
+  # for refcard:
+  s/{\\rm(?![a-zA-Z])\s*([^{}]*)}/$1/g;
+  s/\\Z<>/\\/g;			# Optimize for readability
+
+  # Now replace the POD stuff
+  # Start with cosmetic stuff:
+  $in_code = 0;
+  s/(\@\[((start)|end)code\])/ ($3 ? $in_code++ : --$in_code) ? "" : $1 /ge;
+
+  if ($dumb_pod) {
+    my @stack;
+    s /(\@\[((start)|end)(\w+)\])/
+      if ($3) {			# Start
+	push @stack, $4;
+	(@stack > 1 ? "\@[end$stack[-2]]" : '') . $1
+      } else {			# end
+	pop @stack;
+	$1 . (@stack ? "\@[start$stack[-1]]" : '')
+      }
+      /ge
+  }
+  1 while s/\@\[start(\w+)\](\s*)\@\[end\1\]/$2/g;
+
+  s/\@\[(\w+)\]/\@!$pr{$1}/g;
+  s/(\\\w+)\@!(\w)/$1 $2/g;
+  s/\@!//g;
+  s/\\([\{\}])/$1/g;
+
+  # Normalize the spacing
+  s/\n{3,}/\n\n/;
+  s/\A([ \t]*\n)+//;
+  # Single label is not healthy...
+  print "\n" if $last_glued and /\A=/; # POD markup needs a new paragraph
+  $last_glued = s/((\A|\n\n)(X<[^<>]+>)+)[ \t]*\n\n/$1\n/;
+
+  print;
+}
+
+sub color {
+  my($a);
+  $_ = $_[0];
+  if (/[^0-9]/ || $_ < 0 || $_ > 17)
+    { print "bad color in gphelp: $_\n"; return ""; }
+  if ($_ < 8) { $a = $_ + 30; } else { $a = $_ + 82; }
+  return "\e[0;${a}m";
+}
+
+sub TeXprint {
+  local($_) = $_[0];
+  s/\@\[obr\]/{/g;
+  s/\@\[cbr\]/}/g;
+  s/\@\[ouml\]/"o/g;
+  s/\@\[uuml\]/"u/g;
+  s/\@\[agrave\]/`a/g;
+  s/\@\[eacute\]/'e/g;
+  s/\@\[ldollar\]/\$/g;
+  s/\@\[end(bold|code|bcode|bi|it)\]/$ch/g;
+  s/\@\[start(bold|code|bcode|bi)\]/$cb/g;
+  s/\@\[startit\]/$cu/g;
+  s/\@\[(dollar|empty|endl?word|endpodcode|startl?word|startpodcode)\]//g;
+  s/\@\[pm\]/+\/-/g;
+  s/\@\[lt\]/</g;
+  s/\@\[gt\]/>/g;
+  s/\\([\{\}])/$1/g;
+  s/\@\[nbrk\]/ /g; print "$_\n";
+}
+
+sub to_pod {
+  $to_pod = $ARGV[1];
+  inittr();
+  $parifile = $to_pod;
+  %compress = ('.gz', 'gzip -cd',
+	       '.z', 'gzip -cd',
+	       '.Z', 'zcat',
+	      );
+  foreach $suffix (keys %compress) {
+    ($patt = $suffix) =~ s/(\W)/\\$1/;
+    if ($to_pod =~ /$patt$/) { $pipe = $compress{$suffix}; last; }
+  }
+  if ($pipe) {
+    open(DOC,"$pipe $parifile |") ||
+      die "Cannot open pipe $pipe from $parifile: $!, stopped";
+  } else {
+    open(DOC,$parifile) || die "Cannot find file $parifile: $!, stopped";
+  }
+  $/='';			# Paragraph mode
+  while (<DOC>) {
+    &TeXprint_topod();
+  }
+  if ($pipe) {
+    close(DOC) || die "Cannot close pipe `$pipe $parifile': $!, stopped";
+  } else {
+    close(DOC) || die "Cannot close file $parifile: $!, stopped";
+  }
+  cleanexit();
+}
diff --git a/doc/index.tex b/doc/index.tex
new file mode 100644
index 0000000..c969094
--- /dev/null
+++ b/doc/index.tex
@@ -0,0 +1,118 @@
+%%
+%% INDEX (Macros)
+%%
+\ifsecondpass\else
+  \condwrite\index{The End}
+  \immediate\condwrite\toc{Index\string\dotfill\the\pageno}
+  \ifPDF \writesecnumbers \fi
+  \expandafter\end % stop here the first time (don't process index)
+\fi
+
+\ifPDF
+% Add a bookmark entry for the index.   CHB
+  \putchapdest
+  \pdfoutline goto name {pdfchap@\the\pdfchapcntr} {Index}
+\fi
+
+\newdimen\fullhsize
+\fullhsize=\hsize
+\advance\hsize by -20pt
+\divide\hsize by 2
+
+\def\fullline{\hbox to\fullhsize}
+\let\lr=L\newbox\leftcolumn
+
+\headline={\hfil\bf Index\hfil\global\headline={\hfil}}
+
+\def\makeheadline{\vbox to 0pt{\vskip-22.5pt
+  \fullline{\vbox to8.5pt{}\the\headline}\vss}
+  \nointerlineskip}
+
+\def\makefootline{\baselineskip=24pt\fullline{\the\footline}}
+
+\output={\if L\lr   %cf. The TeXbook, p257
+  \global\setbox\leftcolumn=\columnbox\global\let\lr=R
+  \else\doubleformat\global\let\lr=L\fi
+  \ifnum\outputpenalty>-20000\else\dosupereject\fi}
+\def\doubleformat{\shipout\vbox
+  {\makeheadline
+  \fullline{\box\leftcolumn\hfil\columnbox}
+  \makefootline}
+  \advancepageno}
+\def\columnbox{\leftline{\pagebody}}
+
+\def\parse!#1#2!#3!#4!#5 {%
+  \uppercase{\def\theletter{#1}}%
+  \def\theword{#1#2}%
+  \def\thefont{#3}%
+  \def\thepage{#4}%
+  \def\thedest{#5}}
+
+\ifPDF
+%% This puts the hyperlink command in the index, linked to the page
+%% number. #1 is the usual page number, #2 the pdfcounter.  CHB
+  \def\indxjump#1#2{\pdfstartlink attr {/Border [ 0 0 0 ] /H /O}
+    goto name {pdf@#2}\pushcolor{\linkcolor}#1\popcolor\pdfendlink}
+\else
+  \def\indxjump#1#2{#1}
+\fi
+
+\def\theoldword{}
+\def\theoldletter{}
+\def\theoldpage{}
+\def\theend{The End }
+
+% more efficient to parse the glue specs once and keep them in registers
+% for later use.  These govern index lines with too many page numbers to
+% fit in one line
+%  b: indentation for 2nd and further lines / a: compensation for same,
+% and shrinkability for the normal word space
+\newbox\dbox \setbox\dbox=\hbox to 3truemm{\hss.\hss}
+\newskip\dfillskip \dfillskip=.5em plus .98\hsize
+\def\dotfill{\leaders\copy\dbox\hskip\dfillskip\relax}
+\newskip\interskipa \interskipa=-.4\hsize plus -1.5\hsize minus .11em
+\newskip\interskipb \interskipb= .4\hsize plus  1.5\hsize
+
+% cf. The TeXbook, p393:
+\def\interpage{,\penalty100\kern0.33em%normal space
+  \hskip\interskipa\vadjust{}\penalty10000 \hskip\interskipb\relax}
+
+\def\newword{\relax\endgraf%
+  {\csname\thefont\endcsname\theword}\dotfill\indxjump{\thepage}{\thedest}%
+  \let\theoldfont\thefont%
+  \let\theoldword\theword}
+
+%%
+%% INDEX
+%%
+\parskip=0pt plus 1pt
+\parindent=0pt
+\parfillskip=0pt
+
+\catcode`\_=11 % make _ an ordinary char (frequent in function names)
+
+\def\li#1{\hbox to\hsize{#1\hfil}}
+\li{\var{SomeWord} refers to PARI-GP concepts.}
+\li{\kbd{SomeWord} is a PARI-GP keyword.}
+\li{SomeWord is a generic index entry.}
+
+\checkfile{\jobname.std}
+\newif\ifmore
+\loop
+  \read\std to\theline
+  \ifx\theline\theend\morefalse\else\moretrue\fi
+\ifmore
+  \expandafter\parse\theline
+  \ifx \theletter \theoldletter \else \endgraf
+    \vskip 10pt plus 10pt\centerline{\bf\theletter}
+    \vskip  6pt plus  7pt
+  \fi
+  \ifx \theword \theoldword
+    \ifx \thefont \theoldfont
+      \ifx \thepage \theoldpage
+      \else \interpage \indxjump{\thepage}{\thedest}\fi
+    \else \newword \fi
+  \else \newword \fi
+  \let\theoldletter\theletter
+  \let\theoldpage\thepage
+\repeat%
diff --git a/doc/libpari.tex b/doc/libpari.tex
new file mode 100644
index 0000000..7a8b3bd
--- /dev/null
+++ b/doc/libpari.tex
@@ -0,0 +1,41 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+
+% Compile with plain TeX
+%
+\def\TITLE{User's Guide to the PARI library}
+\input parimacro.tex
+
+% START TYPESET
+\begintitle
+\vskip 2.5truecm
+\centerline{\mine User's Guide}
+\vskip 1.truecm
+\centerline{\mine to}
+\vskip 1.truecm
+\centerline{\mine the PARI library}
+\vskip 1.truecm
+\centerline{\sectiontitlebf (version \vers)}
+\vskip 1.truecm
+\authors
+\endtitle
+
+\copyrightpage
+\tableofcontents
+\openin\std=users.aux
+\ifeof\std
+\else
+  \input users.aux
+\fi
+\chapno=3
+{ \input usersch4 }
+{ \input usersch5 }
+{ \input usersch6 }
+{ \input usersch7 }
+{ \input appb }
+{ \input appd }
+\input index\end
diff --git a/doc/parallel.tex b/doc/parallel.tex
new file mode 100644
index 0000000..461a83e
--- /dev/null
+++ b/doc/parallel.tex
@@ -0,0 +1,393 @@
+\def\TITLE{Introduction to parallel GP programming}
+\input parimacro.tex
+
+% START TYPESET
+\begintitle
+\vskip 2.5truecm
+\centerline{\mine Introduction}
+\vskip 1.truecm
+\centerline{\mine to}
+\vskip 1.truecm
+\centerline{\mine parallel GP}
+\vskip 1.truecm
+\centerline{\sectiontitlebf (version \vers)}
+\vskip 1.truecm
+\authors
+\endtitle
+
+\copyrightpage
+\tableofcontents
+\openin\std=parallel.aux
+\ifeof\std
+\else
+  \input parallel.aux
+\fi
+\chapno=0
+
+\chapter{Parallel GP interface}
+
+\section{Configuration}
+
+This draft documents the (experimental) parallel GP interface.
+Two multithread interfaces are supported:
+
+\item POSIX threads
+
+\item Message passing interface (MPI)
+
+As a rule, POSIX threads are well-suited for single systems, while MPI is
+used by most clusters. However the parallel GP interface does not depend on
+the multithread interface: a properly written GP program will work
+identically with both.
+
+\subsec{POSIX threads}
+
+POSIX threads are selected by passing the flag \kbd{--mt=pthread} to
+\kbd{Configure}. The required library and header files are installed by
+default on most Linux system. Unfortunately this option implies
+\kbd{--enable-tls} which makes the dynamically linked \kbd{gp-dyn} binary
+about $25\%$ slower. Since \kbd{gp-sta} is only $5\%$ slower, you will
+definitely want to use the latter binary.
+
+It is sometimes useful to pass the flag \kbd{--time=ftime} to Configure
+so that \kbd{gettime} and the GP timer report real time instead of cumulated
+CPU time.
+
+You can test parallel GP support with
+
+\bprog
+make test-parallel
+ at eprog
+
+\subsec{Message Passing Interface}
+
+Configuring MPI is somewhat more difficult, but your MPI installation should
+include a script \kbd{mpicc} that takes care of the necessary configuration.
+If you have a choice between several MPI implementation, choose OpenMPI.
+
+To configure for MPI, use
+\bprog
+env CC=mpicc ./Configure --mt=mpi
+ at eprog
+
+To run the program \kbd{fun.gp} on $10$ nodes, you can then use
+\bprog
+  mpirun -np 10 gp fun.gp
+ at eprog\noindent (or \kbd{mpiexec} instead of \kbd{mpirun} if such is
+the name used in your MPI implementation).
+
+PARI requires at least $3$ MPI nodes to work properly.
+
+Note that \kbd{mpirun} is not suited for interactive use because it
+does not provide tty emulation. Also currently it is not possible to
+interrupt parallel tasks.
+
+You can test parallel GP support (here using 3 nodes) with
+
+\bprog
+make test-parallel RUNTEST="mpirun -np 3"
+ at eprog
+
+\section{Concept}
+
+GP provides functions that allows parallel execution of GP code, subject to
+the following limitations: the parallel code
+
+\item must not access global variables or local variables declared with
+  \kbd{local()},
+
+\item must be free of side effect.
+
+Due to the overhead of parallelism, we recommend to split the computation so
+that each parallel computation requires at least a few seconds. On the other
+hand, it is generally more efficient to split the computation in small chunks
+rather than large chunks.
+
+\subsec{Resources}
+
+The number of secondary threads to use is controlled by
+\kbd{default(nbthreads)}. The default value of nbthreads is:
+
+\item POSIX threads: the number of CPU threads (i.e. the number of CPU cores
+multiplied by the hyperthreading factor).
+The default can be freely modified.
+
+\item MPI: the number of available process slots minus $1$ (one slot is used by
+the master thread), as configured with \kbd{mpirun} (or \kbd{mpiexec}). E.g
+\kbd{nbthreads} is $9$ after \kbd{mpirun -np 10 gp}.
+It is possible to change the default to a lower value, but increasing it will
+not work (MPI does not allow to span new threads at run time).
+
+PARI requires at least $3$ nodes to work properly.
+
+
+The PARI stack size in secondary threads is controlled by
+\kbd{default(threadsize)}, so the total memory allocated is equal to
+$\kbd{parisize}+\kbd{nbthreads}\times\kbd{threadsize}$.  By default,
+$\kbd{threadsize}=\kbd{parisize}$.
+
+\subsec{GP functions}
+
+GP provides the following functions for parallel operations:
+
+\item \tet{parvector}: parallel version of \kbd{vector}
+
+\item \tet{parapply}:  parallel version of \kbd{apply}
+
+\item \tet{parsum}:    parallel version of \kbd{sum}
+
+\item \tet{pareval}:   evaluate a vector of closures in parallel
+
+\item \tet{parfor}:    parallel version of \kbd{for}
+
+\item \tet{parforprime}:   parallel version of \kbd{forprime}
+
+Please see the documentation of each function for details.
+
+\subsec{PARI functions}
+The low-level \kbd{libpari} interface for parallelism is documented
+in the \emph{Developer's guide to the PARI library}.
+
+\chapter{Writing code suitable for parallel execution}
+
+\section{Avoiding global variables}
+
+When parallel execution encounters a global variable \kbd{var},
+the following error is reported:
+\bprog
+  *** parapply: mt: global variable not supported: var.
+ at eprog
+
+From a user perspective, global variables fall in three categories:
+data, functions, and polynomial variables. We give some examples.
+
+\subsec{Example 1: data}
+\bprog
+? V=[2^256 + 1, 2^193 - 1];
+? parvector(#V,i,factor(V[i]))
+  *** parvector: mt: global variable not supported: V.
+ at eprog\noindent
+fails because \kbd{V} is a global variable in the expression
+\kbd{factor(V[i])}; the variable \kbd{V} must first be converted to a local
+variable. There are several ways to achieve this:
+
+\item use \kbd{parapply} instead of \kbd{parvector}:
+\bprog
+? V=[2^256 + 1, 2^193 - 1];
+? parapply(factor,V)
+ at eprog
+
+Here \kbd{V} is not part of the parallel section of the code.
+
+\item use a wrapper function, and pass data as function arguments:
+\bprog
+? V=[2^256 + 1, 2^193 - 1];
+? fun(z)=parvector(#z,i,factor(z[i]));
+? fun(V)
+ at eprog
+
+\item define \kbd{V} as a local variable:
+\bprog
+? my(V=[2^256 + 1, 2^193 - 1]); parvector(#V,i,factor(V[i]))
+ at eprog
+
+\item redefine \kbd{V} as a local variable:
+\bprog
+? V=[2^256 + 1, 2^193 - 1];
+? my(V=V); parvector(#V,i,factor(V[i]))
+ at eprog
+
+\item use \kbd{inline}, that replaces later occurences of that variable name
+  by the variable content (inlining):
+\bprog
+? inline(V);
+? V=[2^256 + 1, 2^193 - 1];
+? parvector(#V,i,factor(V[i]))
+ at eprog
+
+Here \kbd{V} is inlined inside the function \kbd{i->factor(V[i])}.
+
+\subsec{Example 2: polynomial variable}
+
+\bprog
+? fun(n)=bnfinit(x^n-2).no;
+? parapply(fun,[1..50])
+  *** parapply: mt: global variable not supported: x.
+ at eprog
+
+The function \kbd{fun} should use the polynomial indeterminate \kbd{'x}
+instead the global variable \kbd{x} (whose value is \kbd{'x} on startup, but
+may or may no longer be \kbd{'x} at this point):
+
+\bprog
+? fun(n)=bnfinit('x^n-2).no;
+ at eprog
+
+or alternatively
+
+\bprog
+? fun(n)=my(x='x);bnfinit(x^n-2).no;
+ at eprog
+which is more readable if the same polynomial variable is used several times.
+
+\subsec{Example 3: function}
+\bprog
+f(a) = bnfinit('x^8-a).no;
+g(a,b) = parsum(i=a,b,f(i));
+? g(37,48)
+  *** parsum: mt: global variable not supported: f.
+ at eprog\noindent
+fails because the function \kbd{f} is a global variable (whose value is a
+function).  This is identical to the first example, and all solutions given
+there apply here:
+
+\item use \kbd{inline}
+\bprog
+inline(f);
+f(a) = bnfinit('x^8-a).no;
+g(a,b) = parsum(i=a,b,f(i));
+? g(37,48)
+ at eprog
+
+\item make \kbd{f} a parameter of \kbd{g}:
+\bprog
+f(a) = bnfinit('x^8-a).no;
+g(a,b,f) = parsum(i=a,b,f(i));
+? g(37,48,f)
+ at eprog
+
+\item define \kbd{f} as a local variable:
+
+\bprog
+{
+  my(f(a) = bnfinit('x^8-a).no);
+  g(a,b) = parsum(i=a,b,f(i));
+}
+? g(37,48)
+ at eprog
+
+\item redefine \kbd{f} as a local variable:
+\bprog
+? f(a) = bnfinit('x^8-a).no;
+? my(f=f); g(a,b) = parsum(i=a,b,f(i));
+? g(37,48)
+ at eprog
+
+\section{Input and output}
+If your parallel code needs to write data to files, we recommend to split
+the output in as many files as the number of parallel computations, to avoid
+concurrent writes to the same file.
+
+For example a parallel version of
+\bprog
+? f(a) = write("bnf",bnfinit('x^8-a));
+? apply(f,[37..48])
+ at eprog\noindent could be
+\bprog
+? f(a) = write(Str("bnf/bnf-",a), bnfinit('x^8-a).no); 0
+? parapply(f,[37..48])
+ at eprog\noindent which creates the files \kbd{bnf/bnf-37} to \kbd{bnf/bnf-48}.
+
+\section{Using \kbd{parfor} and \kbd{parforprime}}
+\kbd{parfor} and \kbd{parforprime} are the most powerful of all parallel GP
+functions but since they have a different interface than \kbd{for} and
+\kbd{forprime}, the code needs to be adapted. Consider the example
+\bprog
+for(i=a,b,
+  my(c = f(i));
+  g(i,c));
+ at eprog\noindent
+
+where \kbd{f} is a function without side-effects.  This can be run in parallel
+as follows:
+\bprog
+parfor(i=a, b,
+  f(i),
+  c,     /*@Ccom the value of \kbd{f(i)} is assigned to \kbd{c} */
+  g(i,c));
+ at eprog\noindent For each $i$, $a \leq i \leq b$, in random order,
+this construction assigns \kbd{f(i)} to (local, as per \kbd{my}) variable
+$c$, then calls \kbd{g}$(i,c)$.
+
+The following function finds the index of the first component of a vector
+satisfying a predicate, and $0$ if none satisfies:
+\bprog
+parfirst(pred,V)=
+{
+  parfor(i=1, #V,
+    pred(V[i]),
+    cond,
+    if(cond, return(i));
+  0
+}
+ at eprog
+
+The following function is similar to \kbd{parsum}:
+\bprog
+myparsum(a,b,expr)=
+{
+  my(s = 0);
+  parfor(i=a, b,
+    expr(i),
+    val,
+    s += val);
+  val
+}
+ at eprog
+
+\section{Sizing parallel tasks}
+
+Dispatching tasks to parallel threads takes time. To limit overhead, we
+recommend to split the computation in tasks so that each parallel task
+requires at least a few seconds. Consider the following example:
+\bprog
+  thuemorse(n)= my(V=binary(n)); (-1)^n*sum(i=1,#V,V[i]);
+  sum(n=1,2*10^6, thuemorse(n)/n*1.)
+ at eprog\noindent
+It is natural to try
+\bprog
+  inline(thuemorse);
+  thuemorse(n)= my(V=binary(n)); (-1)^n*sum(i=1,#V,V[i]);
+  parsum(n=1,2*10^6, thuemorse(n)/n*1.)
+ at eprog\noindent
+However, due to the overhead, this will not be much faster than the
+sequential version. To limit overhead, we group the summation by blocks:
+\bprog
+  parsum(N=1,20, sum(n=1+(N-1)*10^5, N*10^5, thuemorse(n)/n*1.))
+ at eprog\noindent
+Try to create at least as many groups as the number of available threads,
+to take full advantage of parallelism.
+
+\section{Load balancing}
+
+If the parallel tasks require varying time to complete, it is preferable to
+perform the slower ones first, when there are more tasks than available
+parallel threads. Instead of
+\bprog
+  parvector(36,i,bnfinit('x^i-2).no)
+ at eprog\noindent doing
+\bprog
+  parvector(36,i,bnfinit('x^(37-i)-2).no)
+ at eprog\noindent will be faster if you have fewer than $36$ threads.
+
+Indeed, \kbd{parvector} schedules tasks by increasing $i$ values, and the
+computation time increases steeply with $i$. With $18$ threads, say:
+
+\item in the first form, thread~1 handles both $i = 1$ and $i = 19$,
+  while thread~18 will likely handle $i = 18$ and $i = 36$. In fact, it is
+  likely that the first batch of tasks $i\leq 18$ runs relatively quickly,
+  but that none of the threads  handling a value $i > 18$ (second task) will
+  have time to complete before $i = 18$. When that thread finishes
+  $i = 18$, it will pick the remaining task $i = 36$.
+
+\item in the second form, thread~1 will likely handle
+only $i = 36$: tasks $i = 36, 35, \dots 19$ go to the available
+18 threads, and $i = 36$ is likely to finish last, when
+$i = 18,\dots, 2$ are already assigned to the other 17 threads.
+Since the small values of $i$ will finish almost instantly, $i = 1$ will have
+been allocated before the initial thread handling $i = 36$ becomes ready again.
+
+Load distribution is clearly more favorable in the second form.
+
+\vfill\eject
+\input index\end
diff --git a/doc/paricfg.tex.in b/doc/paricfg.tex.in
new file mode 100644
index 0000000..e07f342
--- /dev/null
+++ b/doc/paricfg.tex.in
@@ -0,0 +1,13 @@
+\begingroup
+\catcode`\&=12
+\catcode`\_=12
+\catcode`\$=12
+\catcode`\#=12
+\catcode`\%=12
+\catcode`\^=13
+\catcode`\~=13
+\gdef\vers{@version@}
+\gdef\includedir{@includedir@}
+\gdef\libdir{@libdir@}
+\gdef\datadir{@datadir@}
+\endgroup
diff --git a/doc/parimacro.tex b/doc/parimacro.tex
new file mode 100644
index 0000000..d5a5817
--- /dev/null
+++ b/doc/parimacro.tex
@@ -0,0 +1,471 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+
+\catcode`\@=11
+%
+% GENERAL FORMATTING
+%
+\newif\ifSUBSECTOC\SUBSECTOCtrue
+\newif\ifSUBSECDOT\SUBSECDOTtrue
+\newif\ifGPHELP
+\newif\ifPDF
+\newread\std
+\def\checkfile#1{\def\@stdfile{#1}\openin\std=#1\relax}
+\long\def\@ifundef#1#2#3{\expandafter\ifx\csname
+  #1\endcsname\relax#2\else#3\fi}
+
+% do we come from gphelp ?
+\@ifundef{fromgphelp}{\GPHELPfalse}{\GPHELPtrue}
+\ifGPHELP %YES
+  \overfullrule=0pt
+\else     %NO
+  \magnification=\magstephalf
+  \PDFfalse
+  \ifx\pdfoutput\undefined
+  \else
+     \ifnum\pdfoutput<1
+     \else
+       \PDFtrue
+     \fi
+  \fi
+\fi
+
+% if paricfg.tex is there (Configure succeeded), input it, otherwise use
+% default values
+\checkfile{paricfg.tex}
+\ifeof\std
+  \checkfile{doc/paricfg.tex}
+\fi
+\ifeof\std
+  \ifGPHELP\else% OK for gphelp to use default values
+    \message{paricfg.tex not found. You should run Configure.}
+  \fi
+  \def\vers{2.0.x}
+  \def\includedir{/usr/local/include/pari}
+  \def\libdir{/usr/local/lib}
+\else
+  \input\@stdfile
+\fi
+
+\font\chaptertitlefont=cmr12 scaled \magstep1
+\font\chaptertitlebf=cmbx10 scaled \magstep2
+\font\sectiontitlebf=cmbx12
+\font\seventt=cmtt8 scaled 875
+\scriptfont\ttfam=\seventt % we should really set the \hyphenchar etc first
+\parskip=6pt plus 3pt minus 1.5pt
+%\overfullrule=0pt
+
+%%
+%% TABLE OF CONTENTS
+%%
+\newwrite\toc
+\def\tableofcontents{\begintitle
+\openin\std=\jobname.toc
+\ifeof\std
+\else
+  \begingroup
+  \centerline{\bf Table of Contents}\medskip
+  \parskip=0pt plus 1pt
+  \parindent=0pt
+  \catcode`\_=11 % make _ an ordinary char (frequent in function names)
+  \catcode`\@=11 % make @ an ordinary char (appears in \_ expansion)
+  \obeylines\input\jobname.toc
+  \endgroup
+\fi
+\openout\toc=\jobname.toc
+\endtitle}
+
+%%
+%% CROSS REFERENCING & INDEX
+%%
+\newif\ifsecondpass
+\newwrite\out
+\newwrite\aux
+\newwrite\index
+\ifGPHELP % disable crossreferences
+  \def\condwrite#1#2{}
+  \def\idx#1{#1}
+  \def\toindex#1{}
+  \def\tocwrite#1{}
+  \def\label#1{}
+  \def\gphelpref#1{[Label: {\tt #1}]}
+  \def\gphelpsecref#1{Section~\gphelpref{#1}}
+  \def\ref{\let\do=\gphelpref\doverb}
+  \def\secref{\let\do=\gphelpsecref\doverb}
+\else % none of the following is needed by gphelp
+  \def\typeout#1{\immediate\write\out{#1}}
+  \def\@namedef#1{\expandafter\def\csname#1\endcsname}
+  \def\newlabel#1#2{\@ifundef{r@#1}{}{\message{Label `#1' multiply
+    defined}}\global\@namedef{r@#1}{#2}}
+
+  \openin\std=\jobname.std
+  \ifeof\std
+    \secondpassfalse
+    \typeout{FIRST PASS}
+    \openout\index=\jobname.idx
+    \let\condwrite=\write
+  \else
+    \secondpasstrue
+    \typeout{SECOND PASS}
+    \let\immediate\relax
+    \def\condwrite#1#2{}
+  \fi
+
+%default font for index entry
+  \def\f at nt{rm}
+
+%% \toindex{#1} = put #1 in index; use font \f at nt, indicate \pageno.
+%% If PDF, associate the page to a unique integer (\pdfdestcntr).
+  \ifPDF
+    \def\toindex#1{\putdest
+      \immediate\condwrite\index{!#1!\f at nt!\the\pageno!\number\pdfdestcntr}}
+  \else
+    \def\toindex#1{%
+      \immediate\condwrite\index{!#1!\f at nt!\the\pageno!}}
+  \fi
+
+  \checkfile{\jobname.aux}
+  \ifeof\std
+    \message{No aux file.}
+  \else
+    \input\@stdfile% input aux file if present
+  \fi
+
+% \ref, \label. We need an auxiliary file written during first pass
+  \openout\aux=\jobname.aux
+
+  \ifx\inputlineno\undefined
+    \let\on at line\empty
+  \else
+    \def\on at line{ on input line \the\inputlineno}
+  \fi
+  \def\@errundef#1{\typeout{Reference `#1' on page \the\pageno \space
+    undefined\on at line}}
+
+  \def\@car#1#2\@nil{#1}
+  \def\@cdr#1#2\@nil{#2}
+
+  \def\@ref {\expandafter\@cdr\@temp \@nil\null}
+  \def\@cref{\expandafter\@car\@temp \@nil\null}
+
+  \def\label#1{\immediate\write\aux{\string
+    \newlabel{#1}{{\the\chapno}{\currentlabel}}}}
+  \def\ref#1{\@ifundef{r@#1}
+    {{\bf ??}\@errundef{#1}}
+    {\edef\@temp{\csname r@#1\endcsname}%
+      \def\lbl{\@ref}\def\chp{\@cref}%
+      \ifx\chp{\the\chapno}\lbl\else\chp.\lbl\fi}}
+  \def\secref#1{Section~\ref{#1}}
+\fi % end of non-gphelp section
+
+%%
+%% VERBATIM MODE
+%%
+% \doverb: setup verbatim mode for the first argument, and execute \do{\arg}
+\def\setupverb{\def\do##1{\catcode`##1=12}\dospecials
+  \catcode`\ =10% standard space
+  \catcode`\f=13% to break ugly ligatures as in nf{}init, use \EFF instead
+}
+% f won't produce any ligature if catcode 13, e.g in verbatim mode (cf above).
+{ \let\GDEF=\gdef \global\let\EFF=f \catcode`\f=13
+  \GDEFf{\EFF{}}}
+
+\begingroup
+\catcode`<=1\catcode`\{=12
+\catcode`>=2\catcode`\}=12
+\gdef\d at verb<%
+  \def\next{##1}<\gdef\@va<##1>\endgroup%
+  \do<\@va>>% \@va is the verbatim argument
+  \next>
+%
+\gdef\d at verbb<%
+  \def\next{##1}{##2}<\gdef\@va<##1>\gdef\@vb<##2>\endgroup%
+  \do<\@va><\@vb>>% \@vxxx are the verbatim arguments
+  \next>
+%
+\gdef\d at verbbb<%
+  \def\next{##1}{##2}{##3}<\gdef\@va<##1>\gdef\@vb<##2>\gdef\@vc<##3>\endgroup%
+  \do<\@va><\@vb><\@vc>>% \@vxxx are the verbatim arguments
+  \next>
+%
+\endgroup
+\def\doverb {\begingroup\setupverb\d at verb}
+\def\doverbb{\begingroup\setupverb\d at verbb}
+\def\doverbbb{\begingroup\setupverb\d at verbbb}
+
+% argument (silently) goes to index
+\def\sidx{\gdef\f at nt{rm}\let\do=\toindex\doverb}   % \rm
+\def\kbdsidx{\gdef\f at nt{tt}\let\do=\toindex\doverb}% \tt
+\def\varsidx{\gdef\f at nt{it}\let\do=\toindex\doverb}% \tt
+
+% argument printed + sent to index
+\def\@idx#1{#1\toindex{#1}}
+\def\idx{\gdef\f at nt{rm}\let\do=\@idx\doverb}
+
+% to index + set up as key (keyword)
+\def\@keyidx#1{{\bf\@idx{#1}}}
+\def\teb{\gdef\f at nt{tt}\let\do=\@keyidx\doverb}
+
+% to index + set up as kbd (verbatim)
+\def\@kbdidx#1{{\tt\@idx{#1}}}
+\def\tet{\gdef\f at nt{tt}\let\do=\@kbdidx\doverb}
+
+% to index + set up as var (variable)
+\def\@kbdvar#1{{\it\@idx{#1}\/}}
+\def\tev{\gdef\f at nt{it}\let\do=\@kbdvar\doverb}
+
+\def\@synt#1#2{\gdef\f at nt{tt}\toindex{#1}
+  The library syntax is \key{#1}({\tt #2})}
+\def\synt{\let\do=\@synt\doverb}
+
+% function prototypes
+\def\funno#1#2#3{\tolerance 1200\emergencystretch 3em\hbadness 4000\noindent{\tt#1 #2(#3)}} % no index
+\def\@fun#1#2#3{\gdef\f at nt{tt}\toindex{#2}\funno{#1}{#2}{#3}}
+\def\fun{\let\do=\@fun\doverbbb}
+
+\def\@doc#1#2{\gdef\f at nt{tt}\toindex{#1}\noindent{\tt#2}}
+\def\doc{\let\do=\@doc\doverbb}
+
+%%
+%% SECTIONS
+%%
+\newcount\appno
+\newcount\chapno
+\newcount\secno
+\newcount\subsecno
+\newcount\subsubsecno
+
+\def\newpage{\hbox{}\vfill\eject}
+%Table of contents. cf TeXBook Exercise 21.10
+\def\tocwrite#1{{\let\the=0\edef\next{\condwrite\toc{#1}}\next}}
+\let\putchapdest\relax
+\let\sectionhook\relax
+
+\def\title#1#2{%
+  \ifodd\pageno\else\newpage\fi
+  \tocwrite{{\bf #1 #2\string\dotfill\the\pageno}}
+  \ifGPHELP\else
+     \putchapdest
+     \centerline{\chaptertitlefont #1}\medskip
+  \fi
+  \centerline{\let\bf\chaptertitlebf \chaptertitlefont #2}\vskip1cm}
+\def\sectitle#1{%
+  \ifGPHELP\else%
+    \vskip 0pt plus 54pt\penalty-600% good break
+    \vskip 24pt plus -45pt minus 9pt\fi%
+  \putchapdest%
+  \tocwrite{{\hskip0.5cm#1\string\dotfill\the\pageno}}%
+  \leftline{\sectionhook{\sectiontitlebf #1}.}
+  \penalty10000 % impossible break
+  \smallskip}% whatever follows will add a \parskip
+
+\def\subsectitle#1{%
+  \ifGPHELP\else%
+    \vskip 0pt plus 45pt\penalty-300
+    \vskip 6pt plus -42pt minus 3pt\fi%
+  \ifSUBSECTOC\tocwrite{{\sevenrm \hskip1cm#1\string\dotfill\the\pageno}}\fi%
+  \sectionhook\noindent{\bf#1}.}
+
+\def\subsubsectitle#1{% a \parskip is being added anyway by \noindent
+  \ifGPHELP\else%
+    \par\vskip 0pt plus 39pt\penalty-200
+    \vskip 0pt plus -37pt minus 1.5pt\fi%
+  \sectionhook\noindent{\bf#1}.}
+
+\ifGPHELP
+  \let\maketitle\relax
+\else
+  \def\maketitle{%
+  \ifnum\chapno=0
+    \currentlabel.
+  \else
+    \number\chapno.\currentlabel\
+  \fi}
+\fi
+
+\def\misctitle#1{\par\vskip 0pt plus 66pt\penalty-400
+  \vskip 3pt plus -64pt minus 1.5pt\noindent{\bf #1.}}
+
+% Chapter headings occupy two lines in the manual (only one in
+% INSTALL.tex and gphelp-extracted bits).  Syntax requirement: After calling
+% \chapter{...} and possibly \label{...}  etc, an empty line _must_ follow
+% before the first paragraph of text or section heading begins. [GN]
+\def\chapter#1#2\par{
+  \secno=0\global\advance\chapno by 1
+  \title{Chapter \number\chapno:}{#1}#2\noindent\ignorespaces}
+\def\appendix#1\par{
+  \chapno=0
+  \secno=0\global\advance\appno by 1
+  \def\applet{\ifcase\appno\or A\or B\or C\or D\or E\or F\or G\fi}
+  \title{Appendix \applet:}{#1}\noindent\ignorespaces}
+\def\section#1{%
+  \subsecno=0\global\advance\secno by 1%
+  \gdef\currentlabel{\number\secno}%
+  \sectitle{\maketitle#1}}
+\def\subsec#1{
+  \subsubsecno=0\global\advance\subsecno by 1
+  \gdef\currentlabel{\number\secno.\number\subsecno}
+  \subsectitle{\maketitle#1}}
+\def\subsubsec#1{
+  \global\advance\subsubsecno by 1
+  \gdef\currentlabel{\number\secno.\number\subsecno.\number\subsubsecno}
+  \subsubsectitle{\maketitle#1}}
+
+\def\annotepar#1{\noindent\llap{#1:\ \ }}
+\def\emacs{\annotepar{EMACS}}
+
+\def\subseckbd#1{\subsec{\kbd{#1}}}
+
+%
+% General purpose
+%
+\def\begintitle{
+  \begingroup\nopagenumbers
+  \font\mine=cmb10 scaled 1893
+  \hbox{}
+}
+\def\authors{
+  \centerline{The PARI Group}
+  \vskip 1.truecm
+  \centerline{Institut de Math\'ematiques de Bordeaux, UMR 5251 du CNRS.}
+  \centerline{Universit\'e Bordeaux 1, 351 Cours de la Lib\'eration}
+  \centerline{F-33405 TALENCE Cedex, FRANCE}
+  \centerline{\tt e-mail: pari at math.u-bordeaux.fr}
+  \vskip 1.5truecm
+  \centerline{\sectiontitlebf Home Page:}
+  \centerline{\kbd{http://pari.math.u-bordeaux.fr/}}
+  \vskip 2.truecm
+}
+
+\def\copyrightpage{
+\begintitle
+\vskip 14cm
+\noindent Copyright \copyright\ 2000--2014 The PARI Group
+\medskip\par
+\noindent Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice are
+preserved on all copies.
+\medskip\par
+\noindent Permission is granted to copy and distribute modified versions, or
+translations, of this manual under the conditions for verbatim copying,
+provided also that the entire resulting derived work is distributed under the
+terms of a permission notice identical to this one.
+\bigskip\par
+\noindent PARI/GP is Copyright \copyright\ 2000--2014 The PARI Group
+\medskip\par
+\noindent PARI/GP 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. It is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY WHATSOEVER.
+\endtitle
+}
+
+\def\endtitle{\newpage\endgroup}
+
+\newfam\euffam
+\font\teneuf=eufm10
+\font\eighteuf=eufm8
+\textfont\euffam=\teneuf
+\scriptfont\euffam=\eighteuf
+\def\goth#1{{\fam\euffam#1}}
+\def\kbd#1{{\tt #1}}
+\def\key#1{{\bf #1}}
+\def\emph#1{{\it #1\/}}
+\def\var#1{\hbox{\it #1\/}}
+\def\floor#1{\left\lfloor #1 \right\rfloor}
+\def\ceil#1{\left\lceil #1 \right\rceil}
+\def\round#1{\left\lfloor #1 \right\rceil}
+\def\fl{\var{f\kern0pt lag}}
+\def\Cl{\text{Cl}}
+\def\gcd{\text{gcd}}
+\def\lcm{\text{lcm}}
+\def\Norm{\text{Norm}}
+\def\Id{\text{Id}}
+\def\disc{\text{disc}}
+\def\item{$\bullet$~}
+
+\def\bs{{\char'134}}
+\def\obr{{\char'173}}
+\def\cbr{{\char'175}}
+\def\pow{\^{}\hskip0pt}
+\def\til{\raise-0.3em\hbox{\~{}}}
+\def\b#1{{\tt \bs#1}}
+\def\mod{\,{\rm mod}\,}
+\def\text#1{{\rm#1}}
+\def\dfrac#1#2{{{#1}\over{#2}}}
+\def\binom#1#2{\pmatrix{{#1}\cr{#2}}}
+\def\Bbb#1{{\bf #1}}
+{\catcode`\_=11
+\gdef\typ#1{\kbd{t_#1}}}
+\def\Z{\Bbb Z}
+\def\Q{\Bbb Q}
+\def\F{\Bbb F}
+\def\P{\Bbb P}
+\def\R{\Bbb R}
+\def\C{\Bbb C}
+\def\dotfill{\leaders\hbox to 3truemm{\hfil.\hfil}\hfill}
+\def\B{\kbd{BITS\_IN\_LONG}}
+\def\op{{\it op\/}}
+
+% verbatim mode, leave alone $ and _
+% @ made active: assume verbatim text doesn't contain it
+\newif\ifnopar
+{\catcode`\^=13\global\let^=\pow\obeyspaces\global\let \ }
+\def\ttverb{%
+  \nopartrue
+  \catcode`\\=12%
+  \catcode`\{=12%
+  \catcode`\}=12%
+  \catcode`\&=12%
+  \catcode`\#=12%
+  \catcode`\%=12%
+  \catcode`\^=13%
+  \catcode`\~=13\def~{{\til}}%
+  \catcode`\@=0%
+  \def\par{\futurelet\next\dopars}%
+  \def\dopars{%
+    \ifnopar \noparfalse%
+    \else% treat two consecutive \par specialy
+      \ifx\next\par \vskip4pt plus 1pt\nopartrue%
+      \else \leavevmode\endgraf\fi\fi}%
+  \obeyspaces\obeylines\tt}
+
+% back to normalcy
+\def\unverb{%
+  \catcode`\\=0%
+  \catcode`\{=1%
+  \catcode`\}=2%
+  \catcode`\$=3%
+  \catcode`\&=4%
+  \catcode`\#=6%
+  \catcode`\^=7%
+  \catcode`\_=8%
+  \catcode`\^^I=10}
+
+\def\bprogpart{\begingroup%
+  \vskip 0pt plus 1pt%
+  \leavevmode\parskip=0pt plus 1pt%
+  \interlinepenalty2000\clubpenalty9000\widowpenalty9000%
+  \ttverb}
+
+% complete verbatim (including _ and $)
+\def\bprogfile#1{\bprog\input#1\eprog}
+\def\bprog{\bprogpart\catcode`\_=12\catcode`\$=12}
+\def\eprog{\endgroup\par}
+{\obeylines
+  \gdef\com{\begingroup\unverb\comstart}
+  \gdef\comstart#1^^M{\it#1\endgroup
+} % newline after @endgroup is important
+}
+% comments
+\def\Ccom{\begingroup\unverb\Ccomstart}
+\def\Ccomstart#1*/{\rm#1\endgroup*/}
+
+\ifPDF
+  \input pdfmacs.tex
+\fi
+\catcode`\@=12
diff --git a/doc/pdfmacs.tex b/doc/pdfmacs.tex
new file mode 100644
index 0000000..1788543
--- /dev/null
+++ b/doc/pdfmacs.tex
@@ -0,0 +1,138 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+
+%% Modifications to parimacro.tex to be run through pdftex instead of
+%% tex. Code now includes some pdf-specific code for hyperlinks.
+%%
+%% Cliff Bergman (cbergman at iastate.edu) Jan. 2000.
+%%
+%
+%% Set the document info
+\pdfoutput = 1
+\pdfinfo {
+  /Title    (\TITLE)
+  /Creator  (pdfTeX)
+  /Producer (PARI, pari at math.u-bordeaux.fr)
+  /Author   (C. Batut, K. Belabas, D. Bernardi, H. Cohen, M. Olivier)
+  /Subject  (Number Theory) }
+%
+\pdfcatalog {/PageMode /UseOutlines}
+%
+\catcode`\@=11
+%% Now we redefine several of the macros so as to provide hyperlinks.
+%
+%  Colors
+%
+%% We use TeX's grouping mechanism to make \currentcolor into a  stack.
+%
+ \def\pushcolor#1{\bgroup\pdfsetcolor{#1}}
+ \def\popcolor{\egroup\pdfsetcolor{\currentcolor}}
+ \def\pdfsetcolor#1{\let\currentcolor=#1\pdfliteral{#1 k}}
+% %
+% %  Maybe somebody with a better eye would like to pick nicer ones. See
+% %  the file plain/misc/pdfcolor.tex in the pdftex distribution.
+% %
+ \def\Red{0 1 1 0}
+ \def\Blue{1 1 0 0}
+ \def\Green{1 0 1 0}
+ \def\Black{0 0 0 1}
+ \def\textcolor{\Black}
+ \def\linkcolor{\Red}
+ \def\emacscolor{\Green}
+ \def\unixcolor{\Blue}
+ \let\currentcolor=\textcolor
+ \pdfsetcolor{\textcolor}
+%
+%% Bookmarks.  These turned out to be a pain.  In order to get nested
+%% bookmarks, Acrobat requires that each entry declare the number of
+%% subentries in advance.  We do this by counting the subentries (the
+%% main entries are the chapters, subentries are the sections) during
+%% the first pass and writing them to the aux file as a macro. Then the
+%% bookmark entry is created on the second pass.
+
+% These keep track of the number of sections in each chapter and appendix.
+\newtoks\numsectok    \numsectok={\or}
+\newtoks\numsecapptok \numsecapptok={\or}
+
+% Append #1 to the token list given in #2, separated by \or.  #1 is
+%    expanded first (needed by \numsecs)
+\def\append#1#2{\toks0=\expandafter{#1 \or }%
+  \edef\act{\global\noexpand#2={\the#2 \the\toks0}}\act}
+
+\def\writesecnumbers{
+% Append the number of sections to the last appendix to the toks reg.
+  \append{\the\secno}\numsecapptok
+% Write the definitions of (\numsecs and \numsecapp) to the aux file.
+  \write\aux{
+    \def\string\numsecs\string##1{%
+      \string\ifcase \string##1 \the\numsectok 0 \string\else 0 \string\fi}
+    \def\string\numsecsapp\string##1{%
+      \string\ifcase \string##1 \the\numsecapptok 0 \string\else 0 \string\fi}}}
+%
+% Hyperlink destinations will simply be of the form: pdf at nnn, where nnn
+% is obtained from a new counter.
+\newcount\pdfdestcntr \pdfdestcntr=0
+% \putdest creates a pdf destination. Currently, the destination view
+% is 'xyz' which means no change from the existing zoom factor.
+\def\putdest{\global\advance\pdfdestcntr by 1%
+ \pdfdest name {pdf@\number\pdfdestcntr} xyz }
+
+%% It turns out that I also need a different counter for these
+%% destinations since they are only computed on the second pass.
+\newcount\pdfchapcntr \pdfchapcntr=0
+\def\putchapdest{\global\advance\pdfchapcntr by1%
+ \pdfdest name {pdfchap@\number\pdfchapcntr} fitbh }
+
+\def\chapter#1#2\par{
+  \ifnum\chapno=0 \else \append{\the\secno}\numsectok \fi
+  \secno=0\global\advance\chapno by 1
+  \title{Chapter \number\chapno:}{#1}#2\noindent\ignorespaces
+  \ifsecondpass
+   \pdfoutline goto name {pdfchap@\number\pdfchapcntr} count -\numsecs\chapno
+     {\number\chapno\ #1}
+   \fi
+}
+\def\appendix#1{
+  \ifnum\appno=0 \append{\the\secno}\numsectok
+   \else \append{\the\secno}\numsecapptok \fi
+  \chapno=0
+  \global\secno=0\global\advance\appno by 1
+  \def\applet{\ifcase\appno\or A\or B\or C\or D\or E\or F\or G\fi}
+  \title{Appendix \applet:}{#1}\noindent\ignorespaces
+  \ifsecondpass
+   \pdfoutline goto name {pdfchap@\number\pdfchapcntr}
+    count -\numsecsapp\appno {\applet\ #1}
+   \fi}
+\def\section#1{
+  \subsecno=0\global\advance\secno by 1
+  \gdef\currentlabel{\number\secno}
+  \sectitle{\maketitle{#1}}
+  \ifsecondpass
+   \pdfoutline goto name {pdfchap@\number\pdfchapcntr}
+    {\number\chapno.\number\secno\ #1}
+  \fi
+}
+%
+%% FIXME: should use \[push|pop]color and not use explicitly \textcolor
+%% (cf install() in Chapter 3)
+\def\@restore{\endgraf \global\let\par\endgraf \pdfsetcolor{\textcolor}}
+\def\unix{\global\let\par\@restore\pdfsetcolor{\unixcolor}\annotepar{UNIX}}
+\def\emacs{\global\let\par\@restore\pdfsetcolor{\emacscolor}\annotepar{EMACS}}
+
+%% labels and symbolic cross-refs.  For this we use the parameter to
+%% build the symbolic pdf destination.
+\@ifundef{pdfstartlink}{\global\let\pdfstartlink\pdfannotlink}{}
+\def\label#1{\immediate\write\aux{\string
+   \newlabel{#1}{{\the\chapno}{\currentlabel}}}
+   \pdfdest name {pdf at lab#1} xyz}
+\def\ref#1{\@ifundef{r@#1}
+  {{\bf ??}\@errundef{#1}}
+  {\edef\@temp{\csname r@#1\endcsname}%
+    \def\lbl{\@ref}\def\chp{\@cref}%
+    \pdfjumpref{#1}{\ifx\chp{\the\chapno}\lbl\else\chp.\lbl\fi}}}
+\def\pdfjumpref#1#2{\pdfstartlink attr {/Border [ 0 0 0 ] /H /O}
+  goto name {pdf at lab#1}\pushcolor{\linkcolor}#2\popcolor\pdfendlink}
diff --git a/doc/refcard.tex b/doc/refcard.tex
new file mode 100644
index 0000000..27a7163
--- /dev/null
+++ b/doc/refcard.tex
@@ -0,0 +1,1059 @@
+% This file is intended to be processed by plain TeX (TeX82).
+% Reference Card for PARI-GP.
+
+% Copyright (c) 1997-2008 Karim Belabas.
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+
+% Based on an earlier version by Joseph H. Silverman who kindly let me
+% use his original file.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% The original copyright notice read:
+%
+%% Copyright (c) 1993,1994 Joseph H. Silverman. May be freely distributed.
+%% Created Tuesday, July 27, 1993
+%% Thanks to Stephen Gildea for the multicolumn macro package
+%% which I modified from his GNU emacs reference card
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\def\TITLE{Pari/GP reference card}
+\def\versionnumber{2.27}% Version of this reference card
+\def\PARIversion{2.6.1}% Version of PARI described on this reference card
+\def\year{2013}
+\def\month{September}
+
+\special{papersize=29.7cm,21cm}
+
+% ignore parimacro.tex's \magnification setting
+\let\oldmagnification\magnification
+\catcode`@=11
+\def\magnification{\count@}%
+\catcode`@=12
+\input parimacro.tex
+\let\magnification\oldmagnification
+\ifPDF
+  \pdfpagewidth=11.69in
+  \pdfpageheight=8.26in
+\fi
+%**start of header
+\newcount\columnsperpage
+% The final reference card has six columns, three on each side.
+% This file can be used to produce it in any of three ways:
+% 1 column per page
+%    produces six separate pages, each of which needs to be reduced to 80%.
+%    This gives the best resolution.
+% 2 columns per page
+%    produces three already-reduced pages.
+%    You will still need to cut and paste.
+% 3 columns per page
+%    produces two pages which must be printed sideways to make a
+%    ready-to-use 8.5 x 11 inch reference card.
+%    For this you need a dvi device driver that can print sideways.
+% [For 2 or 3 columns, you'll need 6 and 8 point fonts.]
+% Which mode to use is controlled by setting \columnsperpage above.
+%
+% Specify how many columns per page you want here:
+\columnsperpage=3
+
+% You shouldn't need to modify anything below this line.
+%
+% Author:
+%  Karim Belabas
+%  Universite Bordeaux 1, 351 avenue de la Liberation, F-33405 Talence
+%  email: Karim.Belabas at math.u-bordeaux.fr
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% (original reference card by Joseph H. Silverman)
+% (original reference card macros due to Stephen Gildea)
+%
+%% Original Thanks:
+%%  I would like to thank Jim Delaney, Kevin Buzzard, Dan Lieman,
+%%  and Jaap Top for sending me corrections.
+%%
+% Thanks to Bill Allombert, Henri Cohen, Gerhard Niklasch, and Joe Silverman
+% for many comments and corrections.
+
+\def\version{\month\ \year\ v\versionnumber}
+
+\def\shortcopyrightnotice{\vskip .5ex plus 2 fill
+  \centerline{\small \copyright\ \year\ Karim Belabas.
+  Permissions on back.  v\versionnumber}}
+
+\def\<#1>{$\langle${#1}$\rangle$}
+\def\copyrightnotice{\vskip 1ex plus 2 fill
+\begingroup\small
+\centerline{Based on an earlier version by Joseph H. Silverman}
+\centerline{\version. Copyright \copyright\ \year\ K. Belabas}
+%\centerline{GP copyright by The PARI Group}
+
+Permission is granted to make and distribute copies of this card provided the
+copyright and this permission notice are preserved on all copies.
+
+Send comments and corrections to \<Karim.Belabas at math.u-bordeaux.fr>
+\endgroup}
+
+% make \bye not \outer so that the \def\bye in the \else clause below
+% can be scanned without complaint.
+\def\bye{\par\vfill\supereject\end}
+
+\newdimen\intercolumnskip
+\newbox\columna
+\newbox\columnb
+
+\def\ncolumns{\the\columnsperpage}
+
+\message{[\ncolumns\space
+  column\if 1\ncolumns\else s\fi\space per page]}
+
+\def\scaledmag#1{ scaled \magstep #1}
+
+% This multi-way format was designed by Stephen Gildea
+% October 1986.
+\if 1\ncolumns
+  \hsize 4in
+  \vsize 10in
+  \voffset -.7in
+  \font\titlefont=\fontname\tenbf \scaledmag3
+  \font\headingfont=\fontname\tenbf \scaledmag2
+  \font\smallfont=\fontname\sevenrm
+  \font\smallsy=\fontname\sevensy
+
+  \footline{\hss\folio}
+  \def\makefootline{\baselineskip10pt\hsize6.5in\line{\the\footline}}
+\else
+  \hsize 3.2in
+%  \vsize 7.95in
+  \vsize 7.90in
+  \hoffset -.75in
+%  \voffset -.745in
+  \voffset -.815in
+  \font\titlefont=cmbx10 \scaledmag2
+  \font\headingfont=cmbx10 %\scaledmag1
+  \font\smallfont=cmr6
+  \font\smallsy=cmsy6
+  \font\eightrm=cmr8
+  \font\eightbf=cmbx8
+  \font\eightit=cmti8
+  \font\eighttt=cmtt8
+  \font\eightsy=cmsy8
+  \font\eightsl=cmsl8
+  \font\eighti=cmmi8
+  \font\eightex=cmex10 at 8pt
+  \textfont0=\eightrm
+  \textfont1=\eighti
+  \textfont2=\eightsy
+  \textfont3=\eightex
+  \def\rm{\fam0 \eightrm}
+  \def\bf{\eightbf}
+  \def\it{\eightit}
+  \def\tt{\eighttt}
+  \normalbaselineskip=.8\normalbaselineskip
+  \normallineskip=.8\normallineskip
+  \normallineskiplimit=.8\normallineskiplimit
+  \normalbaselines\rm %make definitions take effect
+
+  \if 2\ncolumns
+    \let\maxcolumn=b
+    \footline{\hss\rm\folio\hss}
+    \def\makefootline{\vskip 2in \hsize=6.86in\line{\the\footline}}
+  \else \if 3\ncolumns
+    \let\maxcolumn=c
+    \nopagenumbers
+  \else
+    \errhelp{You must set \columnsperpage equal to 1, 2, or 3.}
+    \errmessage{Illegal number of columns per page}
+  \fi\fi
+
+  \intercolumnskip=.46in
+  \def\abc{a}
+  \output={%
+      % This next line is useful when designing the layout.
+      %\immediate\write16{Column \folio\abc\space starts with \firstmark}
+      \if \maxcolumn\abc \multicolumnformat \global\def\abc{a}
+      \else\if a\abc
+        \global\setbox\columna\columnbox \global\def\abc{b}
+        %% in case we never use \columnb (two-column mode)
+        \global\setbox\columnb\hbox to -\intercolumnskip{}
+      \else
+        \global\setbox\columnb\columnbox \global\def\abc{c}\fi\fi}
+  \def\multicolumnformat{\shipout\vbox{\makeheadline
+      \hbox{\box\columna\hskip\intercolumnskip
+        \box\columnb\hskip\intercolumnskip\columnbox}
+      \makefootline}\advancepageno}
+  \def\columnbox{\leftline{\pagebody}}
+
+  \def\bye{\par\vfill\supereject
+    \if a\abc \else\null\vfill\eject\fi
+    \if a\abc \else\null\vfill\eject\fi
+    \end}
+\fi
+
+% we won't be using math mode much, so redefine some of the characters
+% we might want to talk about
+%\catcode`\^=12
+%\catcode`\_=12
+%\catcode`\~=12
+
+\chardef\\=`\\
+\chardef\{=`\{
+\chardef\}=`\}
+
+\hyphenation{}
+
+\parindent 0pt
+\parskip 0pt
+
+\def\small{\smallfont\textfont2=\smallsy\baselineskip=.8\baselineskip}
+
+\outer\def\newcolumn{\vfill\eject}
+
+\outer\def\title#1{{\titlefont\centerline{#1}}}
+
+\outer\def\section#1{\par\filbreak
+  \vskip 1ex plus .4ex minus .5ex
+  {\headingfont #1}\mark{#1}%
+  \vskip .5ex plus .3ex minus .5ex
+}
+
+\outer\def\subsec#1{\filbreak
+  \vskip 0.07ex plus 0.05ex
+  {\bf #1}
+  \vskip 0.03ex plus 0.05ex
+}
+
+\newdimen\keyindent
+\def\beginindentedkeys{\keyindent=1em}
+\def\endindentedkeys{\keyindent=0em}
+\endindentedkeys
+
+\def\kbd#1{{\tt#1}\null} %\null so not an abbrev even if period follows
+
+\newbox\libox
+\setbox\libox\hbox{\kbd{M-x }}
+\newdimen\liwidth
+\liwidth=\wd\libox
+
+\def\li#1#2{\leavevmode\hbox to \hsize{\hbox to .75\hsize
+  {\hskip\keyindent\relax#1\hfil}%
+  \hskip -\liwidth minus 1fil
+  \kbd{#2}\hfil}}
+
+\def\threecol#1#2#3{\hskip\keyindent\relax#1\hfil&\kbd{#2}\quad
+  &\kbd{#3}\quad\cr}
+
+\def\mod{\;\hbox{\rm mod}\;}
+\def\expr{\hbox{\it expr}}
+\def\seq{\hbox{\it seq}}
+\def\args{\hbox{\it args}}
+\def\file{\hbox{\it file}}
+\def\QQ{\hbox{\bf Q}}
+\def\ZZ{\hbox{\bf Z}}
+\def\RR{\hbox{\bf R}}
+\def\FF{\hbox{\bf F}}
+\def\CC{\hbox{\bf C}}
+\def\deg{\mathop{\rm deg}}
+\def\bs{\char'134}
+\def\pow{\^{}\hskip0pt}
+\def\til{\raise-0.3em\hbox{\~{}}}
+\def\typ#1{\kbd{t\_#1}}
+%**end of header
+
+\title{PARI-GP Reference Card}
+\centerline{(PARI-GP version \PARIversion)}
+Note: optional arguments are surrounded by braces $\{\}$.\hfill\break
+To start the calculator, type its name in the terminal: \kbd{gp}\hfill\break
+To exit \kbd{gp}, type \kbd{quit}, \kbd{\\q}, or \kbd{<C-D>} at prompt.\hfill
+\section{Help}
+\li{describe function}{?{\it function}}
+\li{extended description}{??{\it keyword}}
+\li{list of relevant help topics}{???{\it pattern}}
+
+\section{Input/Output}
+\li{previous result, the result before}
+  {\%{\rm, }\%`{\rm, }\%``{\rm, etc.}}
+\li{$n$-th result since startup}{\%$n$}
+\li{separate multiple statements on line}{;}
+\li{extend statement on additional lines}{\\}
+\li{extend statements on several lines}{\{$\seq_1$; $\seq_2$;\}}
+\li{comment}{/* $\dots$ */}
+\li{one-line comment, rest of line ignored}{\\\\ \dots}
+
+\section{Metacommands \& Defaults}
+\li{set default $d$ to \var{val}} {default$(\{d\},\{\var{val}\},\{\fl\})$}
+\li{toggle timer on/off}{\#}
+\li{print time for last result}{\#\#}
+\li{print defaults}{\\d}
+\li{set debug level to $n$}{\\g $n$}
+\li{set memory debug level to $n$}{\\gm $n$}
+\li{set output mode (raw=0, default=1)}{\\o $n$}
+\li{set $n$ significant digits}{\\p $n$}
+\li{set $n$ terms in series}{\\ps $n$}
+\li{quit GP}{\\q}
+\li{print the list of PARI types}{\\t}
+\li{print the list of user-defined functions}{\\u}
+\li{read file into GP}{\\r {\it filename}}
+
+\section{Debugger / break loop}
+\li{get out of break loop}{break {\rm or} <C-D>}
+\li{go up $n$ frames}{dbg\_up$(\{n\})$}
+\li{examine object $o$}{dbg\_x$(o)$}
+
+\section{PARI Types \& Input Formats}
+\li{\typ{INT}/\typ{REAL}. Integers, Reals}{$\pm n$, $\pm n.ddd$}
+\li{\typ{INTMOD}. Integers modulo $m$}{Mod$(n,m)$}
+\li{\typ{FRAC}. Rational Numbers}{$n/m$}
+\li{\typ{FFELT}. Elt in finite field $\F_q$}{ffgen(q)}
+\li{\typ{COMPLEX}. Complex Numbers}{$x +y\,*\;$I}
+\li{\typ{PADIC}. $p$-adic Numbers}{$x\;+\;$O$(p$\pow$k)$}
+\li{\typ{QUAD}. Quadratic Numbers}{$x + y\,*\;$quadgen$(D)$}
+\li{\typ{POLMOD}. Polynomials modulo $g$}{Mod$(f,g)$}
+\li{\typ{POL}. Polynomials}{$a*x$\pow$n+\cdots+b$}
+\li{\typ{SER}. Power Series}{$f\;+\;$O$(x$\pow$k)$}
+\li{\typ{QFI}/\typ{QFR}. Imag/Real bin.\ quad.\ forms}
+  {Qfb$(a,b,c,\{d\})$}
+\li{\typ{RFRAC}. Rational Functions}{$f/g$}
+\li{\typ{VEC}/\typ{COL}. Row/Column Vectors}
+  {[$x,y,z$]{\rm,} [$x,y,z$]\til}
+\li{\typ{MAT}. Matrices}{[$x,y$;$z,t$;$u,v$]}
+\li{\typ{LIST}. Lists}{List$($[$x,y,z$]$)$}
+\li{\typ{STR}. Strings}{"abc"}
+
+\section{Reserved Variable Names}
+\li{$\pi=3.14\dots$, $\gamma=0.57\dots$, $C=0.91\dots$}{Pi{\rm, }Euler{\rm, }Catalan}
+\li{square root of $-1$}{I}
+\li{big-oh notation}{O}
+
+% ****************************************
+% This goes at the bottom of page 1
+\shortcopyrightnotice
+\newcolumn
+
+\section{Information about an Object}
+\li{PARI type of object $x$}{type$(x)$}
+\li{length of $x$ / size of $x$ in memory}{\#$x${\rm, }sizebyte$(x)$}
+\li{real or $p$-adic precision of $x$}{precision$(x)${\rm, }padicprec}
+
+\section{Operators}
+\li{basic operations}{+{\rm,} - {\rm,} *{\rm,} /{\rm,} \pow}
+\li{\kbd{i=i+1}, \kbd{i=i-1}, \kbd{i=i*j}, \dots}
+  {i++{\rm,} i--{\rm,} i*=j{\rm,}\dots}
+\li{euclidean quotient, remainder}{$x$\bs/$y${\rm,} $x$\bs$y${\rm,}
+$x$\%$y${\rm,} divrem$(x,y)$}
+\li{shift $x$ left or right $n$ bits}{ $x$<<$n$, $x$>>$n$
+  {\rm or} shift$(x,\pm n)$}
+  \li{comparison operators}{<={\rm, }<{\rm, }>={\rm, }>{\rm, }=={\rm,
+  }!={\rm, }==={\rm, }lex{\rm, }cmp}
+\li{boolean operators (or, and, not)}{||{\rm, } \&\&{\rm ,} !}
+\li{bit operations}{bitand{\rm, }bitneg{\rm, }bitor{\rm, }bitxor}
+\li{sign of $x=-1,0,1$}{sign$(x)$}
+\li{maximum/minimum of $x$ and $y$}{max{\rm,} min$(x,y)$}
+\li{integer or real factorial of $x$}{$x$!~{\rm or} factorial$(x)$}
+\li{derivative of $f$ w.r.t. $x$}{$f$'}
+\li{apply differential operator}{diffop}
+\li{restore $x$ as a formal variable}{$x$='$x$}
+\li{simultaneous assignment $x\leftarrow v_1$, $y\leftarrow v_2$}{[x,y] = v}
+\section{Select Components}
+\li{$n$-th component of $x$}{component$(x,n)$}
+\li{$n$-th component of vector/list $x$}{$x$[$n$]}
+\li{components $a,a+1,\dots,b$ of vector $x$}{$x$[$a$..$b$]}
+\li{$(m,n)$-th component of matrix $x$}{$x$[$m,n$]}
+\li{row $m$ or column $n$ of matrix $x$}{$x$[$m,$]{\rm,} $x$[$,n$]}
+\li{numerator/denominator of $x$}{numerator$(x)${\rm, }denominator}
+%
+\section{Conversions}
+\li{to vector, matrix, set, list, string}
+  {Col{\rm/}Vec{\rm,}Mat{\rm,}Set{\rm,}List{\rm,}Str}
+\li{create PARI object $(x\mod y)$}{Mod$(x,y)$}
+\li{make $x$ a polynomial of $v$}{Pol$(x,\{v\})$}
+\li{as \kbd{Pol}/\kbd{Vec}, starting with constant term}{Polrev{\rm, }Vecrev}
+\li{make $x$ a power series of $v$}{Ser$(x,\{v\})$}
+\li{string from bytes / from format+args}{Strchr{\rm, }Strprintf}
+\li{convert $x$ to simplest possible type}{simplify$(x)$}
+\li{object $x$ with precision $n$}{precision$(x,n)$}
+%
+\subsec{Conjugates and Lifts}
+\li{conjugate of a number $x$}{conj$(x)$}
+\li{conjugate vector of algebraic number $x$}{conjvec$(x)$}
+\li{norm of $x$, product with conjugate}{norm$(x)$}
+\li{square of $L^2$ norm of vector $x$}{norml2$(x)$}
+\li{lift of $x$ from Mods}{lift{\rm,} centerlift$(x)$}
+
+\begingroup
+\outer\def\subsec#1{\filbreak
+  \vskip 0.05ex plus 0.05ex
+  {\bf #1}
+  \vskip 0.05ex plus 0.05ex
+}
+
+\section{Lists, Sets \& Sorting}
+\li{sort $x$ by $k$-th component}{vecsort$(x,\{k\},\{fl=0\})$}
+\li{min.~$m$ of $x$ ($m=x[i]$), max.}{vecmin$(x,\{\&i\})${\rm, }vecmax}
+\li{does $y$ belong to $x$, sorted wrt. $f$}{vecsearch$(x,y,\{f\})$}
+  {\bf Sets} (= row vector of strings with strictly increasing entries)\hfill\break
+%
+\li{intersection of sets $x$ and $y$}{setintersect$(x,y)$}
+\li{set of elements in $x$ not belonging to $y$}{setminus$(x,y)$}
+\li{union of sets $x$ and $y$}{setunion$(x,y)$}
+\li{does $y$ belong to the set $x$}{setsearch$(x,y,\{\fl\})$}
+\li{is $x$ a set ?}{setisset$(x)$}
+%
+\subsec{Lists. {\rm create empty list: $L$ = List$()$}}
+\li{append $x$ to list $L$}{listput$(L,x,\{i\})$}
+\li{remove $i$-th component from list $L$}{listpop$(L,\{i\})$}
+\li{insert $x$ in list $L$ at position $i$}{listinsert$(L,x,i)$}
+\li{sort the list $L$ in place}{listsort$(L,\{\fl\})$}
+
+\newpage
+\section{Programming}
+\subsec{Functions and closures}
+\leavevmode
+ {\tt fun(vars) = my(local vars); \var{seq}\hfill\break}
+ {\tt fun = (vars) -> my(local vars); \var{seq}\hfill}
+\subsec{Control Statements {\rm ($X$: formal parameter in expression \seq)}}
+\li{eval.\ \seq\ for $a\le X\le b$}{for$(X\,$=$\;a,b,\seq)$}
+\li{eval.\ \seq\ for $X$ dividing $n$}{fordiv$(n,X,\seq)$}
+\li{eval.\ \seq\ for primes $a\le X\le b$}{forprime$(X\,$=$\;a,b,\seq)$}
+\li{eval.\ \seq\ for $a\le X\le b$ stepping
+$s$}{forstep$(X\,$=$\;a,b,s,\seq)$}
+\li{multivariable {\tt for}}{forvec$(X\,$=$\;v,\seq)$}
+\li{loop over partitions of $n$}{forpart$(p\,$=$n\;\seq)$}
+\li{loop over vectors $v$, $q(v)\leq B$, $q > 0$}{forqfvec$(v, q, b, \seq)$}
+\li{loop over subgrps $H$ of abelian grp $G$}{forsubgroup$(H=G)$}
+\li{evaluate \seq\ until $a\ne0$}{until$(a,\seq)$}
+\li{while $a\ne0$, evaluate \seq}{while$(a,\seq)$}
+\li{exit $n$ innermost enclosing loops}{break$(\{n\})$}
+\li{start new iteration of $n$-th enclosing loop}{next$(\{n\})$}
+\li{return $x$ from current subroutine}{return$(\{x\})$}
+\li{raise an exception}{error$()$}
+\li{if $a\ne0$, evaluate $\seq_1$, else $\seq_2$}{if$(a,\{\seq_1\},\{\seq_2\})$}
+\li{try $\seq_1$, evaluate $\seq_2$ on error}{iferr$(\seq_1, E, \seq_2)$}
+%\li{type of error message $E$}{errname$(E)$}
+\li{select from $v$ according to $f$}{select$(f, v)$}
+\li{apply $f$ to all entries in $v$}{apply$(f, v)$}
+%
+\subsec{Input/Output}
+\li{print with/without \kbd{\bs n}, \TeX\ format}{print{\rm, }print1{\rm,
+}printtex}
+\li{formatted printing}{printf$()$}
+\li{write \args\ to file}{write{\rm,} write1{\rm,} writetex$(\file,\args)$}
+\li{write $x$ in binary format}{writebin$(\file,x)$}
+\li{read file into GP}{read($\{\file\}$)}
+\li{read file, return as vector of lines}{readvec($\{\file\}$)}
+\li{read a string from keyboard}{input$()$}
+%
+\subsec{Interface with User and System}
+\li{allocates a new stack of $s$ bytes}{allocatemem$(\{s\})$}
+\li{alias \var{old}\ to \var{new}}{alias$(\var{new},\var{old})$}
+\li{install function from library}{install$(f,code,\{\var{gpf\/}\},\{\var{lib}\})$}
+\li{execute system command $a$}{system$(a)$}
+\li{as above, feed result to GP}{extern$(a)$}
+\li{as above, return GP string}{externstr$(a)$}
+%\li{new name of function $f$ in GP 2.0}{whatnow$(f)$}
+\li{get \kbd{\$VAR} from environment}{getenv$($\kbd{"VAR"}$)$}
+\li{measure time in ms.}{gettime$()$}
+\li{timeout command after $s$ seconds}{alarm$(s, \expr)$}
+%
+\section{Iterations, Sums \& Products}
+\li{numerical integration}{intnum$(X\,$=$\;a,b,\expr,\{\fl\})$}
+\li{sum \expr\ over divisors of $n$}{sumdiv$(n,X,\expr)$}
+\li{\kbd{sumdiv}, with \expr\ multiplicative}{sumdivmult$(n,X,\expr)$}
+\li{sum $X=a$ to $X=b$, initialized at $x$}{sum$(X\,$=$\;a,b,\expr,\{x\})$}
+\li{sum of series \expr}{suminf$(X\,$=$\;a,\expr)$}
+\li{sum of alternating/positive series}{sumalt{\rm,} sumpos}
+\li{sum of series using \kbd{intnum}}{sumnum}
+\li{product $a\le X\le b$, initialized at $x$}{prod$(X\,$=$\;a,b,\expr,\{x\})$}
+\li{product over primes $a\le X\le b$}{prodeuler$(X\,$=$\;a,b,\expr)$}
+\li{infinite product $a\le X\le\infty$}{prodinf$(X\,$=$\;a,\expr)$}
+\li{real root of \expr\ between $a$ and $b$}{solve$(X\,$=$\;a,b,\expr)$}
+\endgroup
+
+\section{Random Numbers}
+\li{random integer/prime in $[0,N[$}{random$(N)${\rm, }randomprime}
+\li{get/set random seed}{getrand{\rm, }setrand$(s)$}
+
+% This goes at the top of page 4 (=1st column on back of reference card)
+
+\section{Vectors \& Matrices}
+%
+\li{dimensions of matrix $x$}{matsize$(x)$}
+\li{concatenation of $x$ and $y$}{concat$(x,\{y\})$}
+\li{extract components of $x$}{vecextract$(x,y,\{z\})$}
+\li{transpose of vector or matrix $x$}{mattranspose$(x)$ {\rm or} $x$\til}
+\li{adjoint of the matrix $x$}{matadjoint$(x)$}
+\li{eigenvectors/values of matrix $x$}{mateigen$(x)$}
+\li{characteristic/minimal polynomial of $x$}{charpoly$(x)${\rm, }minpoly}
+\li{trace/determinant of matrix $x$}{trace$(x)${\rm, }matdet}
+\li{Frobenius form of $x$}{matfrobenius$(x)$}
+\li{QR decomposition}{matqr$(x)$}
+%
+\subsec{Constructors \& Special Matrices}
+\li{row vec.\ of \expr\ eval'ed at $1\le i\le n$}{vector$(n,\{i\},\{\expr\})$}
+\li{col.\ vec.\ of \expr\ eval'ed at $1\le i\le n$}{vectorv$(n,\{i\},\{\expr\})$}
+\li{matrix $1\le i\le m$, $1\le j\le n$}{matrix$(m,n,\{i\},\{j\},\{\expr\})$}
+\li{define matrix by blocks}{matconcat$(B)$}
+\li{diagonal matrix with diagonal $x$}{matdiagonal$(x)$}
+\li{$n\times n$ identity matrix}{matid$(n)$}
+\li{Hessenberg form of square matrix $x$}{mathess$(x)$}
+\li{$n\times n$ Hilbert matrix $H_{ij}=(i+j-1)^{-1}$}{mathilbert$(n)$}
+%\li{$n\times n$ Pascal triangle}{matpascal$(n-1)$}
+\li{companion matrix to polynomial $x$}{matcompanion$(x)$}
+\li{Sylvester matrix of $x$}{polsylvestermatrix$(x)$}
+%
+\subsec{Gaussian elimination}
+\li{kernel of matrix $x$}{matker$(x,\{\fl\})$}
+\li{intersection of column spaces of $x$ and $y$}{matintersect$(x,y)$}
+\li{solve $M*X = B$ ($M$ invertible)}{matsolve$(M,B)$}
+\li{as solve, modulo $D$ (col. vector)}{matsolvemod$(M,D,B)$}
+\li{one sol of $M*X = B$}{matinverseimage$(M,B)$}
+\li{basis for image of matrix $x$}{matimage$(x)$}
+\li{supplement columns of $x$ to get basis}{matsupplement$(x)$}
+\li{rows, cols to extract invertible matrix}{matindexrank$(x)$}
+\li{rank of the matrix $x$}{matrank$(x)$}
+
+\section{Lattices \& Quadratic Forms}
+\li{upper triangular Hermite Normal Form}{mathnf$(x)$}
+\li{HNF of $x$ where $d$ is a multiple of det$(x)$}{mathnfmod$(x,d)$}
+\li{elementary divisors of $x$}{matsnf$(x)$}
+\li{LLL-algorithm applied to columns of $x$}{qflll$(x,\{\fl\})$}
+\li{like \kbd{qflll}, $x$ is Gram matrix of lattice}
+  {qflllgram$(x,\{\fl\})$}
+\li{LLL-reduced basis for kernel of $x$}{matkerint$(x)$}
+\li{$\ZZ$-lattice $\longleftrightarrow$ $\QQ$-vector space}{matrixqz$(x,p)$}
+%
+\li{signature of quad form $^ty*x*y$}{qfsign$(x)$}
+\li{decomp into squares of $^ty*x*y$}{qfgaussred$(x)$}
+\li{eigenvals/eigenvecs for real symmetric $x$}{qfjacobi$(x)$}
+\li{find up to $m$ sols of $^ty*x*y\le b$}{qfminim$(x,b,m)$}
+\li{perfection rank of $x$}{qfperfection$(x)$}
+\li{$v$, $v[i]:=$number of sols of $^ty*x*y = i$}{qfrep$(x,B,\{\fl\})$}
+\li{automorphism group of $q$}{qfauto$(q)$}
+\li{find isomorphism between $q$ and $Q$}{qfisom$(q,Q)$}
+
+\section{Formal \& p-adic Series}
+\li{truncate power series or $p$-adic number}{truncate$(x)$}
+\li{valuation of $x$ at $p$}{valuation$(x,p)$}
+\subsec{Dirichlet and Power Series}
+\li{Taylor expansion around $0$ of $f$ w.r.t. $x$}{taylor$(f,x)$}
+\li{$\sum a_kb_k t^k$ from $\sum a_kt^k$ and $\sum b_kt^k$}{serconvol$(a,b)$}
+\li{$f=\sum a_k t^k$ from $\sum (a_k/k!)t^k$}{serlaplace$(f)$}
+\li{reverse power series $F$ so $F(f(x))=x$}{serreverse$(f)$}
+\li{Dirichlet series multiplication / division}{dirmul{\rm,} dirdiv$(x,y)$}
+\li{Dirichlet Euler product ($b$ terms)}{direuler$(p\,$=$\;a,b,\expr)$}
+
+\newcolumn
+\title{PARI-GP Reference Card}
+\centerline{(PARI-GP version \PARIversion)}
+
+\section{Polynomials \& Rational Functions}
+%
+\li{degree of $f$}{poldegree$(f)$}
+\li{coeff. of degree $n$ of $f$, leading coeff.}{polcoeff$(f,n)${\rm, }pollead}
+\li{gcd of coefficients of $f$}{content$(f)$}
+\li{replace $x$ by $y$}{subst$(f,x,y)$}
+\li{evaluate $f$ replacing vars by their value}{eval$(f)$}
+\li{replace polynomial expr.~$T(x)$ by $y$ in $f$}{substpol$(f,T,y)$}
+\li{replace $x_1,\dots,x_n$ by $y_1,\dots,y_n$ in $f$}{substvec$(f,x,y)$}
+\li{discriminant of polynomial $f$}{poldisc$(f)$}
+%\li{elementary divisors of Z[a]/f'(a)Z[a]}{poldiscreduced$(f)$}
+\li{resultant $R = \text{Res}_v(f,g)$}{polresultant$(f,g,\{v\})$}
+\li{$[u,v,R]$, $xu + yv = \text{Res}_v(f,g)$}{polresultantext$(x,y,\{v\})$}
+\li{derivative of $f$ w.r.t. $x$}{deriv$(f,\{x\})$}
+\li{formal integral of $f$ w.r.t. $x$}{intformal$(f,\{x\})$}
+\li{formal sum of $f$ w.r.t. $x$}{sumformal$(f,\{x\})$}
+\li{reciprocal poly $x^{\deg f}f(1/x)$}{polrecip$(f)$}
+\li{interpol.~pol.~eval.~at $a$}{polinterpolate$(X,\{Y\},\{a\},\{$\&$e\})$}
+\li{initialize $t$ for Thue equation solver}{thueinit$(f)$}
+\li{solve Thue equation $f(x,y)=a$}{thue$(t,a,\{sol\})$}
+%
+\subsec{Roots and Factorization}
+\li{number of real roots of $f$, $a < x\le b$}{polsturm$(f,\{a\},\{b\})$}
+\li{complex roots of $f$}{polroots$(f)$}
+\li{symmetric powers of roots of $f$ up to $n$}{polsym$(f,n)$}
+\li{factor $f$}{factor$(f,\{lim\})$}
+\li{factor $f\mod p$ / roots}{factormod$(f,p)${\rm, }polrootsmod}
+\li{factor $f$ over $\FF_{p^a}$ / roots}{factorff$(f,p,a)${\rm, }polrootsff}
+\li{factor $f$ over $\QQ_p$ / roots}{factorpadic$(f,p,r)${\rm, }polrootspadic}
+\li{find irreducible $T\in \FF_p[x]$, $\deg T = n$}{ffinit$(p,n,\{x\})$}
+\li{$\#\{{\rm monic\ irred.}\ T\in \FF_q[x], \deg T = n\}$}{ffnbirred$(q,n)$}
+\li{$p$-adic root of $f$ cong. to $a\mod p$}{padicappr$(f,a)$}
+\li{Newton polygon of $f$ for prime $p$}{newtonpoly$(f,p)$}
+\li{extensions of $\QQ_p$ of degree $N$}{padicfields$(p,N)$}
+%
+\subsec{Special Polynomials}
+\li{$n$-th cyclotomic polynomial in var. $v$}{polcyclo$(n,\{v\})$}
+\li{$d$-th degree subfield of $\QQ(\zeta_n)$} {polsubcyclo$(n,d,\{v\})$}
+\li{$P_n$, $T_n/U_n$, $H_n$}{pollegendre{\rm, }polchebyshev{\rm, }polhermite}
+%\li{Zagier's polynomial of index $n$,$m$}{polzagier$(n,m)$}
+
+\section{Transcendental and $p$-adic Functions}
+\li{real, imaginary part of $x$}{real$(x)$, imag$(x)$}
+\li{absolute value, argument of $x$}{abs$(x)$, arg$(x)$}
+\li{square/nth root of $x$}{sqrt$(x)$, sqrtn$(x,n,\{$\&$z\})$}
+\li{trig functions}{sin, cos, tan, cotan}
+\li{inverse trig functions}{asin, acos, atan}
+\li{hyperbolic functions}{sinh, cosh, tanh}
+\li{inverse hyperbolic functions}{asinh, acosh, atanh}
+\li{exponential / natural log of $x$}{exp{\rm, }log}
+%
+\li{Euler $\Gamma$ function, $\log \Gamma$, $\Gamma'/\Gamma$}
+   {gamma{\rm, }lngamma{\rm, }psi}
+%\li{half-integer gamma function $\Gamma(n+1/2)$}{gammah$(n)$}
+\li{incomplete gamma function ($y=\Gamma(s)$)}{incgam$(s,x,\{y\})$}
+\li{exponential integral $\int_x^\infty e^{-t}/t\,dt$}{eint1$(x)$}
+\li{error function $2/\sqrt\pi\int_x^\infty e^{-t^2}dt$}{erfc$(x)$}
+\li{dilogarithm of $x$}{dilog$(x)$}
+\li{$m$-th polylogarithm of $x$}{polylog$(m,x,\{\fl\})$}
+\li{$U$-confluent hypergeometric function}{hyperu$(a,b,u)$}
+\li{Bessel $J_n(x)$, $J_{n+1/2}(x)$}{besselj$(n,x)$, besseljh$(n,x)$}
+\li{Bessel $I_\nu$, $K_\nu$, $H^1_\nu$, $H^2_\nu$, $N_\nu$}
+{(bessel)i{\rm, }k{\rm, }h1{\rm, }h2{\rm, }n}
+\li{Lambert $W$: $x$ s.t. $xe^x =y$}{lambertw$(y)$}
+\li{Teichmuller character of $p$-adic $x$}{teichmuller$(x)$}
+
+\section{Elementary Arithmetic Functions}
+\li{vector of binary digits of $|x|$}{binary$(x)$}
+\li{bit number $n$ of integer $x$}{bittest$(x,n)$}
+\li{Hamming weight of integer $x$}{hammingweight$(x)$}
+%\li{Sum of decimal digits of integer $x$}{sumdigits$(x)$}
+\li{ceiling/floor/fractional part}{ceil{\rm, }floor{\rm, }frac}
+\li{round $x$ to nearest integer}{round$(x,\{$\&$e\})$}
+\li{truncate $x$}{truncate$(x,\{$\&$e\})$}
+\li{gcd/LCM of $x$ and $y$}{gcd$(x,y)$, lcm$(x,y)$}
+\li{gcd of entries of a vector/matrix}{content$(x)$}
+\par
+\subsec{Primes and Factorization}
+\li{add primes in $v$ to prime table}{addprimes$(v)$}
+\li{Chebyshev $\pi(x)$, $n$-th prime $p_n$}{primepi$(x)$, prime$(n)$}
+\li{vector of first $n$ primes}{primes$(n)$}
+\li{smallest prime $\ge x$}{nextprime$(x)$}
+\li{largest prime $\le x$}{precprime$(x)$}
+\li{factorization of $x$}{factor$(x,\{lim\})$}
+\li{$n=df^2$, $d$ squarefree/fundamental}{core$(n,\{fl\})${\rm, }coredisc}
+\li{recover $x$ from its factorization}{factorback$(f,\{e\})$}
+\par
+\subsec{Divisors}
+\li{number of prime divisors $\omega(n)$ / $\Omega(n)$}
+   {omega$(n)${\rm, }bigomega}
+\li{divisors of $n$ / number of divisors $\tau(n)$}{divisors$(n)${\rm, }numdiv}
+\li{sum of ($k$-th powers of) divisors of $n$}{sigma$(n,\{k\})$}
+\par
+\subsec{Special Functions and Numbers}
+\li{binomial coefficient $x\choose y$}{binomial$(x,y)$}
+\li{Bernoulli number $B_n$ as real/rational}{bernreal$(n)${\rm, }bernfrac}
+\li{Bernoulli polynomial $B_n(x)$}{bernpol$(n,\{x\})$}
+\li{$n$-th Fibonacci number}{fibonacci$(n)$}
+\li{Stirling numbers $s(n,k)$ and $S(n,k)$}{stirling$(n,k,\{\fl\})$}
+\li{number of partitions of $n$}{numbpart$(n)$}
+\li{M\"obius $\mu$-function}{moebius$(x)$}
+\li{Hilbert symbol of $x$ and $y$ (at $p$)}{hilbert$(x,y,\{p\})$}
+\li{Kronecker-Legendre symbol $({x\over y})$}{kronecker$(x,y)$}
+\li{Dedekind sum $s(h,k)$}{sumdedekind$(h,k)$}
+\par
+\subsec{Multiplicative groups $(\ZZ/N\ZZ)^*$, $\FF_q^*$}
+\li{Euler $\phi$-function}{eulerphi$(x)$}
+\li{multiplicative order of $x$ (divides $o$)}{znorder$(x,\{o\})${\rm, }fforder}
+\li{primitive root mod $q$ / $x$\kbd{.mod}}{znprimroot$(q)${\rm, }ffprimroot$(x)$}
+\li{structure of $(\ZZ/n\ZZ)^*$}{znstar$(n)$}
+\li{discrete logarithm of $x$ in base $g$}{znlog$(x,g,\{o\})${\rm, }fflog}
+\subsec{Miscellaneous}
+\li{integer square / $n$-th root of $x$}{sqrtint$(x)$, sqrtnint$(x,n)$}
+\li{solve $z\equiv x$ and $z\equiv y$}{chinese$(x,y)$}
+\li{minimal $u,v$ so $xu+yv=\gcd(x,y)$}{gcdext$(x,y)$}
+\li{continued fraction of $x$}{contfrac$(x,\{b\},\{lmax\})$}
+\li{last convergent of continued fraction $x$}{contfracpnqn$(x)$}
+\li{rational approximation to $x$}{bestappr$(x,k)${\rm, }bestapprPade}
+
+\section{True-False Tests}
+\li{is $x$ the disc. of a quadratic field?}{isfundamental$(x)$}
+\li{is $x$ a prime?}{isprime$(x)$}
+\li{is $x$ a strong pseudo-prime?}{ispseudoprime$(x)$}
+\li{is $x$ square-free?}{issquarefree$(x)$}
+\li{is $x$ a square?}{issquare$(x,\{$\&$n\})$}
+\li{is $x$ a perfect power?}{ispower$(x,\{k\},\{$\&$n\})$}
+\li{is \var{pol}\ irreducible?}{polisirreducible$(\var{pol})$}
+
+% This goes at the bottom of the second page (column 6)
+\copyrightnotice
+%
+
+%%%%%%%%%%% Extra Material (part II)
+%
+\newcolumn
+\title{PARI-GP Reference Card (2)}
+\centerline{(PARI-GP version \PARIversion)}
+
+\section{Elliptic Curves}
+%
+Elliptic curve initially given by $5$-tuple $v=$\kbd{[$a_1,a_2,a_3,a_4,a_6$]}.
+\li{Initialize \var{ell} struct}{E = ellinit$(v,\{Domain\})$}
+\leavevmode
+Points are \kbd{[x,y]}, the origin is \kbd{[0]}.
+Struct members accessed as \kbd{E.}\var{member}:\hfill\break
+$\bullet$ All domains:
+\kbd{E.a1},\kbd{a2},\kbd{a3},\kbd{a4},\kbd{a6},
+\kbd{b2},\kbd{b4},\kbd{b6},\kbd{b8},
+\kbd{c4},\kbd{c6}, \kbd{disc}, \kbd{j}\hfill\break
+\li{$\bullet$ $E$ defined over $\RR$ or $\CC$}{}
+\beginindentedkeys
+\li{$x$-coords. of points of order $2$}{E.roots}
+\li{periods / quasi-periods}{E.omega{,\rm }E.eta}
+\li{volume of complex lattice}{E.area}
+\endindentedkeys
+\li{$\bullet$ $E$ defined over $\QQ_p$}{}
+\beginindentedkeys
+\li{residual characteristic}{E.p}
+\li{If $|j|_p>1$: Tate's $[u^2, u, q, [a,b]]$}{E.tate}
+\endindentedkeys
+\li{$\bullet$ $E$ defined over $\FF_q$}{}
+\beginindentedkeys
+\li{characteristic}{E.p}
+\li{$\#E(\FF_q)$/cyclic structure/generators}{E.no{\rm, }E.cyc{\rm, }E.gen}
+\endindentedkeys
+\li{$\bullet$ $E$ defined over $\QQ$}{}
+\beginindentedkeys
+\li{generators of $E(\QQ)$ (require \kbd{elldata})}{E.gen}
+\endindentedkeys
+\li{$[a_1,a_2,a_3,a_4,a_6]$ from $j$-invariant}{ellfromj$(j)$}
+\li{change curve $E$ using $v=$\kbd{[}$u,r,s,t$\kbd{]}}{ellchangecurve$(E,v)$}
+\li{change point $z$ using $v=$\kbd{[}$u,r,s,t$\kbd{]}}{ellchangepoint$(z,v)$}
+\li{add points $P+Q$ / $P-Q$}{elladd$(E,P,Q)${\rm, }ellsub}
+\li{negate point}{ellneg$(E,P)$}
+\li{compute $n\cdot z$}{ellmul$(E,z,n)$}
+\li{$n$-division polynomial $f_n(x)$}{elldivpol$(E,n,\{x\})$}
+\li{check if $z$ is on $E$}{ellisoncurve$(E,z)$}
+\li{order of torsion point $z$}{ellorder$(E,z)$}
+\li{$y$-coordinates of point(s) for $x$}{ellordinate$(E,x)$}
+\li{point $[\wp(z),\wp'(z)]$ corresp. to $z$}{ellztopoint$(E,z)$}
+\li{complex $z$ such that $p=[\wp(z),\wp'(z)]$}{ellpointtoz$(E,p)$}
+
+\subsec{Curves over finite fields, Pairings}
+\li{random point on $E$}{random$(E)$}
+\li{$\#E(\FF_q)$}{ellcard$(E)$}
+\li{structure $\ZZ/d_1\ZZ\times \ZZ/d_2\ZZ$ of $E(\FF_q)$}{ellgroup$(E)$}
+\li{Weil pairing of $m$-torsion pts $x,y$}{ellweilpairing$(E,x,y, m)$}
+\li{Tate pairing of $x,y$; $x$ $m$-torsion}{elltatepairing$(E,x,y, m)$}
+\li{Discrete log, find $n$ s.t. $P=[n]Q$}{elllog$(E,P,Q,\{ord\})$}
+
+\subsec{Curves over $\QQ$ and the $L$-function}
+\li{canonical bilinear form taken at $z_1$, $z_2$}{ellbil$(E,z_1,z_2)$}
+\li{canonical height of $z$}{ellheight$(E,z,\{\fl\})$}
+\li{height regulator matrix for pts in $x$}{ellheightmatrix$(E,x)$}
+\li{cond, min mod, Tamagawa num \kbd{[}$N,v,c$\kbd{]}}{ellglobalred$(E)$}
+\li{reduction of $y^2+Qy = P$ (genus $2$)}{genus2red$(Q,P,\{p\})$}
+\li{Kodaira type of $p$-fiber of $E$}{elllocalred$(E,p)$}
+\li{minimal model of $E/\QQ$} {ellminimalmodel$(E,\{$\&$v\})$}
+\li{$p$-th coeff $a_p$ of $L$-function, $p$ prime}{ellap$(E,p)$}
+\li{$k$-th coeff $a_k$ of $L$-function}{ellak$(E,k)$}
+\li{vector of first $n$ $a_k$'s in $L$-function}{ellan$(E,n)$}
+\li{$L(E,s)$}{elllseries$(E,s)$}
+\li{$L^{(r)}(E,1)$}{ellL1$(E,r)$}
+\li{return a Heegner point on $E$ of rank $1$}{ellheegner$(E)$}
+\li{order of vanishing at $1$}{ellanalyticrank$(E,\{\var{eps}\})$}
+\li{root number for $L(E,.)$ at $p$}{ellrootno$(E,\{p\})$}
+\li{torsion subgroup with generators}{elltors$(E)$}
+\li{modular parametrization of $E$}{elltaniyama$(E)$}
+\shortcopyrightnotice
+
+\subsec{Elldata package, Cremona's database:}
+\li{db code $\leftrightarrow$ $[\var{conductor}, \var{class}, \var{index}]$}{ellconvertname$(s)$}
+\li{generators of Mordell-Weil group}{ellgenerators$(E)$}
+\li{look up $E$ in database}{ellidentify$(E)$}
+\li{all curves matching criterion}{ellsearch$(N)$}
+\li{loop over curves with cond.~from $a$ to $b$}{forell$(E, a,b,\seq)$}
+
+\section{Elliptic \& Modular Functions}
+$w = [\omega_1,\omega_2]$ or \var{ell} struct (\kbd{E.omega}), $\tau=\omega_1/\omega_2$.\hfill\break
+%
+\li{arithmetic-geometric mean}{agm$(x,y)$}
+\li{elliptic $j$-function $1/q+744+\cdots$}{ellj$(x)$}
+\li{Weierstrass $\sigma$/$\wp$/$\zeta$ function}
+   {ellsigma$(w,z)${\rm, }ellwp{\rm, }ellzeta}
+\li{periods/quasi-periods}{ellperiods$(E,\{\fl\})${\rm, }elleta$(w)$}
+\li{$(2i\pi/\omega_2)^k E_k(\tau)$}{elleisnum$(w,k,\{\fl\})$}
+\li{modified Dedekind $\eta$ func. $\prod(1-q^n)$}{eta$(x,\{\fl\})$}
+\li{Jacobi sine theta function}{theta$(q,z)$}
+\li{k-th derivative at z=0 of \kbd{theta}$(q,z)$}{thetanullk$(q,k)$}
+\li{Weber's $f$ functions}{weber$(x,\{\fl\})$}
+\li{Riemann's zeta $\zeta(s)=\sum n^{-s}$}{zeta$(s)$}
+%
+\bigskip
+\section{Binary Quadratic Forms}
+%
+\li{create $ax^2+bxy+cy^2$ (distance $d$) }{Qfb$(a,b,c,\{d\})$}
+\li{reduce $x$ ($s =\sqrt{D}$, $l=\floor{s}$)}
+  {qfbred$(x,\{\fl\},\{D\},\{l\},\{s\})$}
+\li{composition of forms}{$x$*$y$ {\rm or }qfbnucomp$(x,y,l)$}
+\li{$n$-th power of form}{$x$\pow$n$ {\rm or }qfbnupow$(x,n)$}
+\li{composition without reduction}{qfbcompraw$(x,y)$}
+\li{$n$-th power without reduction}{qfbpowraw$(x,n)$}
+\li{prime form of disc. $x$ above prime $p$}{qfbprimeform$(x,p)$}
+\li{class number of disc. $x$}{qfbclassno$(x)$}
+\li{Hurwitz class number of disc. $x$}{qfbhclassno$(x)$}
+\li{Solve $Q(x,y) = p$ in integers, $p$ prime}{qfbsolve$(Q,p)$}
+
+\section{Quadratic Fields}
+%
+\li{quadratic number $\omega=\sqrt x$ or $(1+\sqrt x)/2$}{quadgen$(x)$}
+\li{minimal polynomial of $\omega$}{quadpoly$(x)$}
+\li{discriminant of $\QQ(\sqrt{D})$}{quaddisc$(x)$}
+\li{regulator of real quadratic field}{quadregulator$(x)$}
+\li{fundamental unit in real $\QQ(x)$}{quadunit$(x)$}
+\li{class group of $\QQ(\sqrt{D})$}{quadclassunit$(D,\{\fl\},\{t\})$}
+\li{Hilbert class field of $\QQ(\sqrt{D})$}{quadhilbert$(D,\{\fl\})$}
+\li{ray class field modulo $f$ of $\QQ(\sqrt{D})$}{quadray$(D,f,\{\fl\})$}
+\bigskip
+
+\section{General Number Fields: Initializations}
+A number field $K$ is given by a monic irreducible $f\in\ZZ[X]$.\hfill\break
+\li{init number field structure \var{nf}}{nfinit$(f,\{\fl\})$}
+\subsec{nf members:}
+\beginindentedkeys
+\li{polynomial defining \var{nf}, $f(\theta)=0$}{\var{nf}.pol}
+\li{number of real/complex places}{\var{nf}.r1/r2/sign}
+\li{discriminant of \var{nf}}{\var{nf}.disc}
+\li{$T_2$ matrix}{\var{nf}.t2}
+\li{vector of roots of $f$}{\var{nf}.roots}
+\li{integral basis of $\ZZ_K$ as powers of $\theta$}{\var{nf}.zk}
+\li{different}{\var{nf}.diff}
+\li{codifferent}{\var{nf}.codiff}
+\li{index}{\var{nf}.index}
+\endindentedkeys
+\li{recompute \var{nf}\ using current precision}{nfnewprec$(nf)$}
+\li{init relative \var{rnf}\ given by $g=0$ over $K$}{rnfinit$(\var{nf},g)$}
+%
+\li{init\var{bnf} structure}{bnfinit$(f,\{\fl\})$}
+\subsec{bnf members: {\rm same as \var{nf}, plus}}
+\beginindentedkeys
+\li{underlying \var{nf}}{\var{bnf}.nf}
+\li{classgroup}{\var{bnf}.clgp}
+\li{regulator}{\var{bnf}.reg}
+\li{fundamental units}{\var{bnf}.fu}
+\li{torsion units}{\var{bnf}.tu}
+\endindentedkeys
+\li{compute a \var{bnf}\ from small \var{bnf}}{bnfinit$(\var{sbnf})$}
+%
+\li{add $S$-class group and units, yield \var{bnf} s}{bnfsunit$(\var{nf},S)$}
+\li{init class field structure \var{bnr}}{bnrinit$(\var{bnf},m,\{\fl\})$}
+%
+\subsec{bnr members: {\rm same as \var{bnf}, plus}}
+\beginindentedkeys
+\li{underlying \var{bnf}}{\var{bnr}.bnf}
+\li{big ideal structure}{\var{bnr}.bid}
+\li{modulus}{\var{bnr}.mod}
+\li{structure of $(\ZZ_K/m)^*$}{\var{bnr}.zkst}
+\endindentedkeys
+\medskip
+
+\section{Basic Number Field Arithmetic (nf)}
+Elements are \typ{INT}, \typ{FRAC}, \typ{POL}, \typ{POLMOD}, or \typ{COL} (on
+integral basis \kbd{\var{nf}.zk}). Basic operations (prefix \kbd{nfelt}):
+$($\kbd{nfelt}$)$\kbd{add}, \kbd{mul}, \kbd{pow}, \kbd{div}, \kbd{diveuc},
+\kbd{mod}, \kbd{divrem}, \kbd{val}, \kbd{trace}, \kbd{norm} \hfill\break
+%
+\li{express $x$ on integer basis}{nfalgtobasis$(\var{nf},x)$}
+\li{express element\ $x$ as a polmod}{nfbasistoalg$(\var{nf},x)$}
+\li{reverse polmod $a=A(X)\mod T(X)$}{modreverse$(a)$}
+
+\li{integral basis of field def. by $f=0$}{nfbasis$(f)$}
+\li{field discriminant of field $f=0$}{nfdisc$(f)$}
+\li{smallest poly defining $f=0$ (slow)}{polredabs$(f,\{\fl\})$}
+\li{small poly defining $f=0$ (fast)}{polredbest$(f,\{\fl\})$}
+\li{are fields $f=0$ and $g=0$ isomorphic?}{nfisisom$(f,g)$}
+\li{is field $f=0$ a subfield of $g=0$?}{nfisincl$(f,g)$}
+\li{compositum of $f=0$, $g=0$}{polcompositum$(f,g,\{\fl\})$}
+\li{subfields (of degree $d$) of \var{nf}}{nfsubfields$(\var{nf},\{d\})$}
+%
+\li{roots of unity in \var{nf}}{nfrootsof1$(\var{nf}\,)$}
+\li{roots of $g$ belonging to \var{nf}}{nfroots$(\{\var{nf}\},g)$}
+\li{factor $g$ in \var{nf}}{nffactor$(\var{nf},g)$}
+\li{factor $g$ mod prime $pr$ in \var{nf}}{nffactormod$(\var{nf},g,pr)$}
+\li{conjugates of a root $\theta$ of \var{nf}}{nfgaloisconj$(\var{nf},\{\fl\})$}
+\li{apply Galois automorphism $s$ to $x$}{nfgaloisapply$(\var{nf},s,x)$}
+\li{quadratic Hilbert symbol (at $p$)}{nfhilbert$(\var{nf},a,b,\{p\})$}
+%
+\subsec{Linear and algebraic relations}
+\li{poly of degree $\le k$ with root $x\in\CC$}{algdep$(x,k)$}
+\li{alg. dep. with pol.~coeffs for series $s$}{seralgdep$(s,x,y)$}
+\li{small linear rel.\ on coords of vector $x$}{lindep$(x)$}
+\subsec{Dedekind Zeta Function $\zeta_K$, Hecke $L$ series}
+\li{$\zeta_K$ as Dirichlet series, $N(I)<b$}{dirzetak$(\var{nf},b)$}
+\li{init \var{nfz}\ for field $f=0$}{zetakinit$(f)$}
+\li{compute $\zeta_K(s)$}{zetak$(\var{nfz},s,\{\fl\})$}
+\li{Artin root number of $K$}{bnrrootnumber$(\var{bnr},\var{chi},\{\fl\})$}
+\li{$L(1,\chi)$, for all $\chi$ trivial on $H$}{bnrL1$(\var{bnr},\{H\},\{\fl\})$}
+
+\section{Class Groups \& Units (bnf, bnr)}
+\leavevmode
+$a_1,\{a_2\},\{a_3\}$ usually $bnr,subgp$ or $\var{bnf},module,\{subgp\}$
+\hfill\break
+%
+\li{remove GRH assumption from \var{bnf}}{bnfcertify$(\var{bnf})$}
+\li{expo.~of ideal $x$ on class gp}{bnfisprincipal$(\var{bnf},x,\{\fl\})$}
+\li{expo.~of ideal $x$ on ray class gp}{bnrisprincipal$(\var{bnr},x,\{\fl\})$}
+\li{expo.~of $x$ on fund.~units}{bnfisunit$(\var{bnf},x)$}
+\li{as above for $S$-units}{bnfissunit$(\var{bnfs},x)$}
+\li{signs of real embeddings of \kbd{\var{bnf}.fu}}{bnfsignunit$(\var{bnf})$}
+\li{narrow class group}{bnfnarrow$(\var{bnf})$}
+%
+\subsec{Class Field Theory}
+\li{ray class number for mod.~$m$}{bnrclassno$(\var{bnf},m)$}
+\li{discriminant of class field ext}{bnrdisc$(a_1,\{a_2\},\{a_3\})$}
+\li{ray class numbers, $l$ list of mods}{bnrclassnolist$(\var{bnf},l)$}
+\li{discriminants of class fields}{bnrdisclist$(\var{bnf},l,\{arch\},\{\fl\})$}
+\li{decode output from \kbd{bnrdisclist}}{bnfdecodemodule$(\var{nf},fa)$}
+\li{is modulus the conductor?}{bnrisconductor$(a_1,\{a_2\},\{a_3\})$}
+\li{conductor of character $chi$}{bnrconductorofchar$(\var{bnr},chi)$}
+\li{conductor of extension}{bnrconductor$(a_1,\{a_2\},\{a_3\},\{\fl\})$}
+\li{conductor of extension def.\ by $g$}{rnfconductor$(\var{bnf},g)$}
+\li{Artin group of ext.\ def'd by $g$}{rnfnormgroup$(\var{bnr},g)$}
+\li{subgroups of \var{bnr}, index $<=b$}{subgrouplist$(\var{bnr},b,\{\fl\})$}
+\li{rel.\ eq.\ for class field def'd by $sub$}{rnfkummer$(\var{bnr},sub,\{d\})$}
+\li{same, using Stark units (real field)}{bnrstark$(\var{bnr},sub,\{\fl\})$}
+
+\section{Ideals: {\rm elements, primes, or matrix of generators in HNF}}
+\li{is $id$ an ideal in \var{nf} ?}{nfisideal$(\var{nf},id)$}
+\li{is $x$ principal in \var{bnf} ?}{bnfisprincipal$(\var{bnf},x)$}
+\li{give {\tt [}$a,b${\tt ]}, s.t.~ $a\ZZ_K+b\ZZ_K = x$}{idealtwoelt$(\var{nf},x,\{a\})$}
+\li{put ideal $a$ ($a\ZZ_K+b\ZZ_K$) in HNF form}{idealhnf$(\var{nf},a,\{b\})$}
+\li{norm of ideal $x$}{idealnorm$(\var{nf},x)$}
+\li{minimum of ideal $x$ (direction $v$)}{idealmin$(\var{nf},x,v)$}
+\li{LLL-reduce the ideal $x$ (direction $v$)}{idealred$(\var{nf},x,\{v\})$}
+%
+\subsec{Ideal Operations}
+\li{add ideals $x$ and $y$}{idealadd$(\var{nf},x,y)$}
+\li{multiply ideals $x$ and $y$}{idealmul$(\var{nf},x,y,\{\fl\})$}
+\li{intersection of ideals $x$ and $y$}{idealintersect$(\var{nf},x,y,\{\fl\})$}
+\li{$n$-th power of ideal $x$}{idealpow$(\var{nf},x,n,\{\fl\})$}
+\li{inverse of ideal $x$}{idealinv$(\var{nf},x)$}
+\li{divide ideal $x$ by $y$}{idealdiv$(\var{nf},x,y,\{\fl\})$}
+\li{Find $(a,b)\in x\times y$, $a+b=1$}{idealaddtoone$(\var{nf},x,\{y\})$}
+\li{coprime integral $A,B$ such that $x=A/B$}{idealnumden$(\var{nf},x)$}
+%
+\subsec{Primes and Multiplicative Structure}
+\li{factor ideal $x$ in \var{nf}}{idealfactor$(\var{nf},x)$}
+\li{expand ideal factorization in \var{nf}}{idealfactorback$(nf,f,{e})$}
+\li{decomposition of prime $p$ in \var{nf}}{idealprimedec$(\var{nf},p)$}
+\li{valuation of $x$ at prime ideal $pr$}{idealval$(\var{nf},x,pr)$}
+\li{weak approximation theorem in \var{nf}}{idealchinese$(\var{nf},x,y)$}
+\li{give $bid=$structure of $(\ZZ_K/id)^*$}{idealstar$(\var{nf},id,\{\fl\})$}
+\li{discrete log of $x$ in $(\ZZ_K/bid)^*$}{ideallog$(\var{nf},x,bid)$}
+\li{\kbd{idealstar} of all ideals of norm $\le b$}{ideallist$(\var{nf},b,\{\fl\})$}
+\li{add Archimedean places}{ideallistarch$(\var{nf},b,\{ar\},\{\fl\})$}
+\li{init \kbd{prmod} structure}{nfmodprinit$(\var{nf},pr)$}
+\li{kernel of matrix $M$ in $(\ZZ_K/pr)^*$}{nfkermodpr$(\var{nf},M,prmod)$}
+\li{solve $M x = B$ in $(\ZZ_K/pr)^*$}{nfsolvemodpr$(\var{nf},M,B,prmod)$}
+
+\section{Galois theory over $\QQ$}
+\li{Galois group of field $\QQ[x]/(f)$}{polgalois$(f)$}
+\li{initializes a Galois group structure $G$}{galoisinit$(\var{pol},\{den\})$}
+\li{action of $p$ in nfgaloisconj form}{galoispermtopol$(G,\{p\})$}
+\li{identify as abstract group}{galoisidentify$(G)$}
+\li{export a group for GAP/MAGMA}{galoisexport$(G,\{\fl\})$}
+\li{subgroups of the Galois group $G$}{galoissubgroups$(G)$}
+\li{is subgroup $H$ normal?}{galoisisnormal$(G,H)$}
+\li{subfields from subgroups}{galoissubfields$(G,\{\fl\},\{v\})$}
+\li{fixed field}{galoisfixedfield$(G,\var{perm},\{\fl\},\{v\})$}
+\li{Frobenius at maximal ideal $P$}{idealfrobenius$(\var{nf},G,P)$}
+\li{ramification groups at $P$}{idealramgroups$(\var{nf},G,P)$}
+
+\newcolumn
+\title{PARI-GP Reference Card (2)}
+\centerline{(PARI-GP version \PARIversion)}
+\medskip
+
+\li{is $G$ abelian?}{galoisisabelian$(G,\{\fl\})$}
+\li{abelian number fields/$\QQ$}{galoissubcyclo(N,H,\{\fl\},\{v\})}
+\li{query the \kbd{galpol} package}{galoisgetpol(a,b,\{s\})}
+
+\section{Relative Number Fields (rnf)}
+Extension $L/K$ is defined by $T\in K[x]$.
+\hfill\break
+%
+\li{absolute equation of $L$}{rnfequation$(\var{nf},T,\{\fl\})$}
+\li{is $L/K$ abelian?}{rnfisabelian$(\var{nf},T)$}
+\li{relative {\tt nfalgtobasis}}{rnfalgtobasis$(\var{rnf},x)$}
+\li{relative {\tt nfbasistoalg}}{rnfbasistoalg$(\var{rnf},x)$}
+\li{relative {\tt idealhnf}}{rnfidealhnf$(\var{rnf},x)$}
+\li{relative {\tt idealmul}}{rnfidealmul$(\var{rnf},x,y)$}
+\li{relative {\tt idealtwoelt}}{rnfidealtwoelt$(\var{rnf},x)$}
+%
+\subsec{Lifts and Push-downs}
+\li{absolute $\rightarrow$ relative repres.\ for $x$}
+  {rnfeltabstorel$(\var{rnf},x)$}
+\li{relative $\rightarrow$ absolute repres.\ for $x$}
+  {rnfeltreltoabs$(\var{rnf},x)$}
+\li{lift $x$ to the relative field}{rnfeltup$(\var{rnf},x)$}
+\li{push $x$ down to the base field}{rnfeltdown$(\var{rnf},x)$}
+\leavevmode idem for $x$ ideal:
+\kbd{$($rnfideal$)$reltoabs}, \kbd{abstorel}, \kbd{up}, \kbd{down}\hfill
+%
+\subsec{Norms}
+\li{absolute norm of ideal $x$}{rnfidealnormabs$(\var{rnf},x)$}
+\li{relative norm of ideal $x$}{rnfidealnormrel$(\var{rnf},x)$}
+\li{solutions of $N_{K/\QQ}(y)=x\in \ZZ$}{bnfisintnorm$(\var{bnf},x)$}
+\li{is $x\in\QQ$ a norm from $K$?}{bnfisnorm$(\var{bnf},x,\{\fl\})$}
+\li{initialize $T$ for norm eq.~solver}{rnfisnorminit$(K,pol,\{\fl\})$}
+\li{is $a\in K$ a norm from $L$?}{rnfisnorm$(T,a,\{\fl\})$}
+%
+\subsec{Maximal order $\ZZ_L$ as a $\ZZ_K$-module}
+\li{relative {\tt polred}}{rnfpolred$(\var{nf},T)$}
+\li{relative {\tt polredabs}}{rnfpolredabs$(\var{nf},T)$}
+\li{characteristic poly.\ of $a$ mod $T$}{rnfcharpoly$(\var{nf},T,a,\{v\})$}
+\li{relative Dedekind criterion, prime $pr$}{rnfdedekind$(\var{nf},T,pr)$}
+\li{discriminant of relative extension}{rnfdisc$(\var{nf},T)$}
+\li{pseudo-basis of $\ZZ_L$}{rnfpseudobasis$(\var{nf},T)$}
+%
+\subsec{General $\ZZ_K$-modules:
+  {\rm $M = [{\rm matrix}, {\rm vec.~of~ideals}] \subset L$}}
+\li{relative HNF / SNF}{nfhnf$(\var{nf},M)${\rm, }nfsnf}
+\li{reduced basis for $M$}{rnflllgram$(\var{nf},T,M)$}
+\li{determinant of pseudo-matrix $M$}{rnfdet$(\var{nf},M)$}
+\li{Steinitz class of $M$}{rnfsteinitz$(\var{nf},M)$}
+\li{$\ZZ_K$-basis of $M$ if $\ZZ_K$-free, or $0$}{rnfhnfbasis$(\var{bnf},M)$}
+\li{$n$-basis of $M$, or $(n+1)$-generating set}{rnfbasis$(\var{bnf},M)$}
+\li{is $M$ a free $\ZZ_K$-module?}{rnfisfree$(\var{bnf},M)$}
+\newpage
+
+\section{Graphic Functions}
+\li{crude graph of \expr\ between $a$ and $b$}{plot$(X\,$=$\;a,b,expr)$}
+\subsec{High-resolution plot {\rm (immediate plot)}}
+\li{plot \expr\ between $a$ and $b$}{ploth$(X\,$=$\;a,b,expr,\{\fl\},\{n\})$}
+\li{plot points given by lists $lx$, $ly$}{plothraw$(lx,ly,\{\fl\})$}
+\li{terminal dimensions}{plothsizes$()$}
+%
+\subsec{Rectwindow functions}
+\li{init window $w$, with size $x$,$y$}{plotinit$(w,x,y)$}
+\li{erase window $w$}{plotkill$(w)$}
+\li{copy $w$ to $w_2$ with offset $(dx,dy)$}{plotcopy$(w,w_2,dx,dy)$}
+\li{clips contents of $w$}{plotclip$(w)$}
+\li{scale coordinates in $w$}{plotscale$(w,x_1,x_2,y_1,y_2)$}
+\li{\kbd{ploth} in $w$}{plotrecth$(w,X\,$=$\;a,b,expr,\{\fl\},\{n\})$}
+\li{\kbd{plothraw} in $w$}{plotrecthraw$(w,data,\{\fl\})$}
+\li{draw window $w_1$ at $(x_1,y_1)$, \dots} {plotdraw$($[[$w_1,x_1,y_1$]$,\dots$]$)$}
+%
+\subsec{Low-level Rectwindow Functions}
+%\li{}{plotlinetype$(w,)$}
+%\li{}{plotpointtype$(w,)$}
+%\li{}{plotterm$(w,)$}
+\li{set current drawing color in $w$ to $c$}{plotcolor$(w,c)$}
+\li{current position of cursor in $w$}{plotcursor$(w)$}
+%
+\li{write $s$ at cursor's position}{plotstring$(w,s)$}
+\li{move cursor to $(x,y)$}{plotmove$(w,x,y)$}
+\li{move cursor to $(x+dx,y+dy)$}{plotrmove$(w,dx,dy)$}
+\li{draw a box to $(x_2,y_2)$}{plotbox$(w,x_2,y_2)$}
+\li{draw a box to $(x+dx,y+dy)$}{plotrbox$(w,dx,dy)$}
+\li{draw polygon}{plotlines$(w,lx,ly,\{\fl\})$}
+\li{draw points}{plotpoints$(w,lx,ly)$}
+\li{draw line to $(x+dx,y+dy)$}{plotrline$(w,dx,dy)$}
+\li{draw point $(x+dx,y+dy)$}{plotrpoint$(w,dx,dy)$}
+\li{draw point $(x+dx,y+dy)$}{plotrpoint$(w,dx,dy)$}
+%
+\subsec{Postscript Functions}
+\li{as {\tt ploth}}{psploth$(X\,$=$\;a,b,expr,\{\fl\},\{n\})$}
+\li{as {\tt plothraw}}{psplothraw$(lx,ly,\{\fl\})$}
+\li{as {\tt plotdraw}}{psdraw$($[[$w_1,x_1,y_1$]$,\dots$]$)$}
+\vfill
+\copyrightnotice
+\bye
+% Local variables:
+% compile-command: "tex PARIRefCard"
+% End:
diff --git a/doc/tex2mail.1 b/doc/tex2mail.1
new file mode 100644
index 0000000..e237510
--- /dev/null
+++ b/doc/tex2mail.1
@@ -0,0 +1,84 @@
+.TH TEX2MAIL 1 "26 March 2001"
+.SH NAME
+tex2mail \- TeX to ascii math prettyprinter
+.SH SYNOPSIS
+.BR tex2mail
+[-linelength=
+.IR length ]
+[-maxdef=
+.IR number ]
+[-debug=
+.IR number ]
+[-by_par=
+.IR number ]
+[-TeX]
+[-ragged]
+[-noindent]
+
+.SH DESCRIPTION
+
+Filter converting TeX or LaTeX math formulae to ASCII art. tex2mail is
+used by the PARI-GP calculator for output method
+.I external prettyprint .
+Under gp, prettyprint mode can be set by
+.B default(output, 3)
+(or \\o3). By default, this switches gp main output to TeX format, filtered
+by
+
+.RS
+tex2mail \-TeX \-noindent \-ragged \-by_par
+.RE
+
+Flags passed to tex2mail can be modified via the
+.IR prettyprinter
+default in gp.
+
+.SH OPTIONS
+The following command line options are available:
+.TP
+.BI \-linelength=length
+Wrap text at this column.
+.TP
+.BI \-maxdef=number
+definition loops: croak if more than maxdef substitutions in a given
+paragraph.
+.TP
+.BI \-debug=debuglevel
+output debugging messages.
+.TP
+.BI \-by_par
+Expect each paragraph to be terminated by *exactly* 2 "\\n", and do not
+print an extra "\\n" between paragraphs.
+.TP
+.BI \-TeX
+Assume input formulae are in plain TeX format.
+.TP
+.BI \-ragged
+leave right ragged.
+.TP
+.BI \-noindent
+assume \\noindent everywhere.
+.SH BUGS
+
+ % at the end of a line followed by \\n\\n is recognized as end of paragraph
+.SH AUTHOR
+Ilya Zakharevitch
+
+.SH SEE ALSO
+.IR gp (1),
+.IR tex (1),
+
+.SH COPYING
+
+This program 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.
+
+This program 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 this program; if not, write to the Free Software Foundation,  Inc.,
+675  Mass  Ave,  Cambridge,  MA 02139, USA.
diff --git a/doc/translations b/doc/translations
new file mode 100644
index 0000000..a564e5b
--- /dev/null
+++ b/doc/translations
@@ -0,0 +1,157 @@
+readline        @se:readline at 2
+edit            @se:readline at 2
+?               @?@2
+??              @?@2
+\\              @\bs\bs at 2
+\a              @\b{a}@2
+\b              @\b{b}@2
+\c              @\b{c}@2
+\d              @\b{d}@2
+\e              @\b{e}@2
+\g              @\b{g}@2
+\gf             @\b{gf}@2
+\gm             @\b{gm}@2
+\h              @\b{h}@2
+\k              @\b{k}@2
+\l              @\b{l}@2
+\m              @\b{m}@2
+\p              @\b{p}@2
+\o              @\b{o}@2
+\ps             @\b{ps}@2
+\q              @\b{q}@2
+\r              @\b{r}@2
+\s              @\b{s}@2
+\t              @\b{t}@2
+\u              @\b{u}@2
+\um             @\b{um}@2
+\v              @\b{v}@2
+\w              @\b{w}@2
+\x              @\b{x}@2
+\y              @\b{y}@2
+#               @\#@2
+##              @\#\#@2
+operator        @GP operators at 2
+and             @&&@3
+or              @&&@3
+not             @&&@3
+INT             @Integers at 2
+integer         @Integers at 2
+REAL            @Real numbers at 2
+real number     @Real numbers at 2
+INTMOD          @Integermods at 2
+intmod          @Integermods at 2
+FRAC            @Rational numbers at 2
+fraction        @Rational numbers at 2
+rational        @Rational numbers at 2
+FRACN           @Rational numbers at 2
+COMPLEX         @Complex numbers at 2
+complex         @Complex numbers at 2
+PADIC           @$p$-adic numbers at 2
+padic           @$p$-adic numbers at 2
+QUAD            @Quadratic numbers at 2
+quadratic       @Quadratic numbers at 2
+POLMOD          @Polmods at 2
+polmod          @Polmods at 2
+POL             @Polynomials at 2
+polynomial      @Polynomials at 2
+SER             @Power series at 2
+RFRAC           @Rational functions at 2
+RFRACN          @Rational functions at 2
+RFRACN          @Rational functions at 2
+QFR             @Binary quadratic forms of positive or negative discriminant at 2
+QFI             @Binary quadratic forms of positive or negative discriminant at 2
+VEC             @Row and column vectors at 2
+COL             @Row and column vectors at 2
+MAT             @Matrices at 2
+LIST            @Lists at 2
+STR             @Strings at 2
+VECSMALL        @Small vectors at 2
+ERROR           @Error contexts at 2
+e_SYNTAX        @iferr@
+e_BUG           @iferr@
+e_ALARM         @iferr@
+e_FILE          @iferr@
+e_MISC          @iferr@
+e_FLAG          @iferr@
+e_IMPL          @iferr@
+e_ARCH          @iferr@
+e_PACKAGE       @iferr@
+e_NOTFUNC       @iferr@
+e_PREC          @iferr@
+e_TYPE          @iferr@
+e_DIM           @iferr@
+e_VAR           @iferr@
+e_PRIORITY      @iferr@
+e_USER          @iferr@
+e_STACK         @iferr@
+e_OVERFLOW      @iferr@
+e_DOMAIN        @iferr@
+e_COMPONENT     @iferr@
+e_MAXPRIME      @iferr@
+e_CONSTPOL      @iferr@
+e_IRREDPOL      @iferr@
+e_COPRIME       @iferr@
+e_PRIME         @iferr@
+e_MODULUS       @iferr@
+e_ROOTS0        @iferr@
+e_OP            @iferr@
+e_TYPE2         @iferr@
+e_INV           @iferr@
+e_MEM           @iferr@
+e_SQRTN         @iferr@
+local           @Variables and Scope at 2
+my              @Variables and Scope at 2
+user defined    @User defined functions at 2
+user defined function @User defined functions at 2
+member function @Member functions at 2
+member          @Member functions at 2
+'               @deriv
+\               @\bs
+\/              @\bs/
+^               @\pow
+!               @factorial
+~               @mattranspose
+-               @+
+%               @\%
+++              @GP operators at 2
+--              @GP operators at 2
+-=              @GP operators at 2
++=              @GP operators at 2
+*=              @GP operators at 2
+/=              @GP operators at 2
+%=              @GP operators at 2
+\/=             @GP operators at 2
+>>=             @GP operators at 2
+<<=             @GP operators at 2
+<<              @shift
+>>              @shift
+<>              @Comparison and Boolean operators
+===             @Comparison and Boolean operators
++/-             @+$/$-
+min             @max
+emacs           @Using GP under GNU Emacs at 2
+Emacs           @Using GP under GNU Emacs at 2
+0               @Functions and Operations Available in PARI and GP
+1               @Standard monadic or dyadic operators
+2               @Conversions and similar elementary functions or commands
+3               @Transcendental functions
+4               @Arithmetic functions
+5               @Functions related to elliptic curves
+6               @Functions related to general number fields
+7               @Polynomials and power series
+8               @Vectors, matrices, linear algebra and sets
+9               @Sums, products, integrals and similar functions
+10              @Plotting functions
+11              @Programming in GP: control statements
+13              @GP operators at 2
+ell             @Functions related to elliptic curves
+nf              @Number field structures
+bnf             @Number field structures
+bnr             @Number field structures
+rnf             @Relative extensions
+ideal           @Algebraic numbers and ideals
+idele           @Algebraic numbers and ideals
+modulus         @Class field theory
+CFT             @Class field theory
+bid             @Class field theory
+prototype       @GP prototypes, parser codes at 5
diff --git a/doc/tutorial.tex b/doc/tutorial.tex
new file mode 100644
index 0000000..4593a53
--- /dev/null
+++ b/doc/tutorial.tex
@@ -0,0 +1,3263 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/\kbd{gp} documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+
+% This should be compiled with plain TeX
+\def\TITLE{A Tutorial for Pari/GP}
+\input parimacro.tex
+
+\chapno=0
+\begintitle
+\vskip2.5truecm
+\centerline{\mine A Tutorial}
+\vskip1.truecm
+\centerline{\mine for}
+\vskip1.truecm
+\centerline{\mine PARI / GP}
+\vskip1.truecm
+\centerline{\sectiontitlebf (version \vers)}
+\vskip1.truecm
+\authors
+\endtitle
+
+\copyrightpage
+\tableofcontents
+
+\noindent This booklet is a guided tour and a tutorial to the \kbd{gp}
+calculator. Many examples will be given, but each time a new function is
+used, the reader should look at the appropriate section in the \emph{User's
+Manual to PARI/GP} for detailed explanations. This chapter can be read
+independently, for example to get acquainted with the possibilities of
+\kbd{gp} without having to read the whole manual. At this point.
+
+\section{Greetings!}
+
+So you are sitting in front of your workstation (or terminal, or PC\dots),
+and you type \kbd{gp} to get the program started (or click on the relevant
+icon, or select some menu item). It says hello in its particular manner, and
+then waits for you after its \kbd{prompt}, initially \kbd{?} (or something
+like {\bf gp}~\kbd{>}). Type
+\bprog
+  2 + 2
+ at eprog\noindent What happens? Maybe not what you expect. First of all, of
+course, you should tell \kbd{gp} that your input is finished, and this is
+done by hitting the \kbd{Return} (or \kbd{Newline}, or \kbd{Enter}) key. If
+you do exactly this, you will get the expected answer. However some of you
+may be used to other systems like Gap, Macsyma, Magma or Maple. In this case,
+you will have subconsciously ended the line with a semicolon ``\kbd{;}''
+before hitting \kbd{Return}, since this is how it is done on those systems.
+In that case, you will simply see \kbd{gp} answering you with a smug
+expression, i.e.~a new prompt and no answer!  This is because a semicolon at
+the end of a line tells \kbd{gp} not to print the result (it is still stored
+in the result history). You will certainly want to use this feature if the
+output is several pages long. Try
+\bprog
+  27 * 37
+ at eprog\noindent Wow! even multiplication works. Actually, maybe those
+spaces are not necessary after all. Let's try \kbd{27*37}. Seems to be ok. We
+will still insert them in this document since it makes things easier to read,
+but as \kbd{gp} does not care about them, you don't have to type them all.
+
+Now this session is getting lengthy, so the second thing one needs to learn
+is to quit. Each system has its quit signal. In \kbd{gp}, you can use
+\kbd{quit} or \b{q} (backslash q), the \kbd{q} being of course for quit.
+Try it.
+
+Now you've done it! You're out of \kbd{gp}, so how do you want to continue
+studying this tutorial? Get back in please.
+
+Let's get to more serious stuff. I seem to remember that the decimal
+expansion of $1/7$ has some interesting properties. Let's see what \kbd{gp}
+has to say about this. Type
+\bprog
+  1 / 7
+ at eprog\noindent
+What? This computer is making fun of me, it just spits back to me my own
+input, that's not what I want!
+
+Now stop complaining, and think a little. Mathematically, $1/7$ is an element
+of the field $\Q$ of rational numbers, so how else but $1/7$ can the computer
+give the answer to you? Well maybe $2/14$ or $7^{-1}$, but why complicate
+matters? Seriously, the basic point here is that PARI, hence \kbd{gp}, will
+almost always try to give you a result which is as precise as possible (we
+will see why ``almost'' later). Hence since here the result can be
+represented exactly, that's what it gives you.
+
+But I still want the decimal expansion of $1/7$. No problem. Type one of
+the following:
+\bprog
+  1./ 7
+  1 / 7.
+  1./ 7.
+  1 / 7 + 0.
+ at eprog\noindent
+Immediately a number of decimals of this fraction appear, 38 on most systems,
+28 on the others, and the repeating pattern is $142857$. The reason is that
+you have included in the operations numbers like \kbd{0.}, \kbd{1.} or \kbd{7.}
+which are \emph{imprecise} real numbers, hence \kbd{gp} cannot give you an
+exact result.
+
+Why 28 / 38 decimals by the way? Well, it is the default initial precision.
+This has been chosen so that the computations are very fast, and gives
+already 12 decimals more accuracy than conventional double precision floating
+point operations. The precise value depends on a technical reason: if your
+machine supports 64-bit integers (the standard C library can handle integers
+up to $2^{64}$), the default precision is 38 decimals, and 28 otherwise.
+For definiteness, we will assume the former henceforth. Of course, you can
+extend the precision (almost) as much as you like as we will see in a moment.
+
+I'm getting bored, why don't we get on with some more exciting stuff?  Well,
+try \kbd{exp(1)}. Presto, comes out the value of $e$ to 38 digits. Try
+\kbd{log(exp(1))}. Well, we get a floating point number and not an exact $1$,
+but pretty close! That's what you lose by working numerically.
+
+What could we try now? Hum, \kbd{pi}? The answer is not that
+enlightening. \kbd{Pi}? Ah. This works better. But let's remember that
+\kbd{gp} distinguishes between uppercase and lowercase letters. \kbd{pi} was
+as meaningless to it as \kbd{stupid garbage} would have been: in both cases
+\kbd{gp} will just create a variable with that funny unknown name you just
+used. Try it! Note that it is actually equivalent to type
+\kbd{stupidgarbage}: all spaces are suppressed from the input. In the
+\kbd{27~*~37} example  it was not so conspicuous as we had an operator to
+separate the two operands. This has important consequences for the writing of
+\kbd{gp} scripts. More about this later.
+
+By the way, you can ask \kbd{gp} about any identifier you think it might know
+about: just type it, prepending a question mark ``\kbd{?}''. Try \kbd{?Pi}
+and \kbd{?pi} for instance. On most systems, an extended online help should
+be available: try doubling the question mark to check whether it's the case
+on yours: \kbd{??Pi}. In fact the \kbd{gp} header already gave you that
+information if it was the case, just before the copyright message. As well,
+if it says something like ``\kbd{readline enabled}'' then you should have a
+look at the \kbd{readline} introduction in the User's Manual before you go
+on: it will be much easier to type in examples and correct typos after you've
+done that.
+
+Now try \kbd{exp(Pi * sqrt(163))}. Hmmm, we suspect that the last digit may
+be wrong, can this really be an integer? This is the time to change
+precision. Type \kbd{\b{p} 50}, then try \kbd{exp(Pi * sqrt(163))} again. We
+were right to suspect that the last decimal was incorrect, since we get quite
+a few nines in its place, but it is now convincingly clear that this is not
+an integer. Maybe it's a bug in PARI, and the result is really an integer?
+Type
+\bprog
+  (log(%) / Pi)^2
+ at eprog\noindent
+immediately after the preceding computation; \kbd{\%} means the result of the
+last computed expression. More generally, the results are numbered \kbd{\%1,
+\%2, \dots} \emph{including} the results
+that you do not want to see printed by putting a semicolon at the end of the
+line, and you can evidently use all these quantities in any further
+computations. The result seems to be indistinguishable from $163$, hence it
+does not seem to be a bug.
+
+In fact, it is known that $\exp(\pi*\sqrt{n})$ not only is not an integer or
+a rational number, but is even a transcendental number when $n$ is a non-zero
+rational number.
+
+So \kbd{gp} is just a fancy calculator, able to give me more decimals than I
+will ever need? Not so, \kbd{gp} is incredibly more powerful than an ordinary
+calculator, independently of its arbitrary precision possibilities.
+
+\misctitle{Additional comments} (you are supposed to skip this at first,
+and come back later)
+
+1) If you are a PARI old timer, say the last version of PARI you used was
+released around 1996, you have certainly noticed already that many many
+things changed between the older 1.39.xx versions and this one.
+Conspicuously, most function names have been changed.
+
+Of course, this is going to break all your nice old scripts. Well, you can
+either change the compatibility level (typing \kbd{default(compatible, 3)}
+will send you back to the stone-age behavior of good ol' version 1.39.15),
+or rewrite the scripts. We really advise you to do the latter if they are not
+too long, since they can now be written much more cleanly than before,
+especially with lexical scoping (\kbd{my}) and the new control statements
+(\kbd{break}, \kbd{next}, \kbd{return}). Besides it'll be as good a way as any
+to get used to the new names.
+
+To know how a specific function was changed, just type \kbd{whatnow({\rm
+function})}.
+
+2) It seems that the text implicitly says that as soon as an imprecise number
+is entered, the result will be imprecise. Is this always true? There is a
+unique exception: when you multiply an imprecise number by the exact number
+0, you will get the exact 0. Compare \kbd{0 * 1.4} and \kbd{0.~*~1.4}.
+\smallskip
+%
+3) Not only can the number of decimal places of real numbers be large, but
+the number of digits of integers also. Try \kbd{1000!}. It is never necessary
+to tell \kbd{gp} in advance the size of the integers that it will encounter.
+The same is true for real numbers, although most computations with floating
+point assume a default precision and truncate their results to this accuracy;
+initially 38 decimal digits, but we may change that with \b{p} of course.
+\smallskip
+%
+4) Come back to 38 digits of precision (\kbd{\b{p} 38}), and type
+\kbd{exp(100)}. As you can see the result is printed in exponential format.
+This is because \kbd{gp} never wants you to believe that a result is correct
+when it is not. We are working with 38 digits of precision, but the integer
+part of $\exp(100)$ has 44 decimal digits. Hence if \kbd{gp} had dutifully
+printed out 44 digits, the last few digits would have been wrong. Hence
+\kbd{gp} wants to print only 38 significant digits, but to do so it has to
+print in exponential format. \smallskip
+%
+5) There are two ways to avoid this. One is of course to increase the
+precision. Let's try it. To give it a wide margin, we set the precision to 50
+decimals. Then we recall our last result (\kbd{\%}
+or \kbd{\%n} where \kbd{n} is the number of the result). What? We still have
+an exponential format! Do you understand why?
+
+Again let's try to see what's happening. The number you recalled had been
+computed only to 38 decimals, and even if you set the precision to 1000
+decimals, \kbd{gp} knows that your number has only 38 digits of accuracy but
+an integral part with 44 digits. So you haven't improved things by increasing
+the precision. Or have you? What if we retype \kbd{exp(100)} now that we
+have 50 digits? Try it. Now we no longer have an exponential format.
+\medskip
+%
+6) What if I forget what the current precision is and I don't feel like
+counting all the decimals? Well, you can type \b{p} by itself. You may also
+learn about \kbd{gp} internal variables (and change them!) using
+\kbd{default}. Type \kbd{default(realprecision)}, then
+\kbd{default(realprecision, 38)}. Huh? In fact this last command is strictly
+equivalent to \kbd{\b{p} 38}! (Admittedly more cumbersome to type.) There are
+more ``defaults'' than just \kbd{format} and \kbd{realprecision}: type
+\kbd{default} by itself now, they are all there. \smallskip
+%
+7) Note that the \kbd{default} command reacts differently according to the
+number of input arguments. This is not an uncommon behavior for \kbd{gp}
+functions. You can see this from the online help, or the complete description
+in Chapter~3: any argument surrounded by braces \kbd{\obr\cbr} in the
+function prototype is optional, which really means that a \emph{default}
+argument will be supplied by \kbd{gp}. You can then check out from the text
+what effect a given value will have, and in particular the default one.
+\smallskip
+%
+8) Try the following: starting in precision 38, type first
+\kbd{default(format, "e0.100")}, then \kbd{exp(1)}. Where are my 100
+significant digits? Well, \kbd{default(format,)} only changes the output
+format, but \emph{not} the default precision. On the other hand, the \b{p}
+command changes both the precision and the output format.
+
+\section{Warming up}
+
+Another thing you better get used to pretty fast is error messages. Try
+typing \kbd{1/0}. Could not be clearer. But why has the prompt
+become funny, turning from \kbd{?} to \kbd{break>} ? When an error occurs, we
+enter a so-called \emph{break loop}, where you get a chance, e.g to inspect
+(and save!) values of variables before the prompt returns and all
+computations so far are lost. In fact you can run an arbitrary command at
+this point, and this mechanism is a tremendous help in debugging. To get out
+of the break loop, type \kbd{break}, as instructed in the error message
+last line.
+
+\misctitle{Comment} You can enter the break loop at any time using
+\kbd{Control-C}: this freezes the current computation and gets you a new
+prompt so that you may e.g., increase debugging level, inspect or modify
+variables (again, run arbitrary commands), before letting the program go
+on.
+\medskip
+
+Now, back to our favorite example, in precision 38, type
+\bprog
+  floor(exp(100))
+ at eprog\noindent
+\kbd{floor} is the mathematician's integer part, not to be confused with
+\kbd{truncate}, which is the computer scientist's: \kbd{floor(-3.4)} is equal
+to $-4$ whereas \kbd{truncate(-3.4)} is equal to $-3$.  You get a more
+cryptic error message, which you would immediately understand if you had read
+the additional comments of the preceding section. Since you were told not to
+read them, here's the explanation: \kbd{gp} is unable to compute the
+integer part of \kbd{exp(100)} given only 38 decimals of accuracy, since
+it has 44 digits.
+
+Some error messages are more cryptic and sometimes not so easy to understand.
+For instance, try \kbd{log(x)}. It simply tells you that \kbd{gp} does not
+understand what \kbd{log(x)} is, although it does know the \kbd{log}
+function, as \kbd{?log} will readily tell us.
+
+Now let's try \kbd{sqrt(-1)} to see what error message we get now. Haha!
+\kbd{gp} even knows about complex numbers, so impossible to trick it that
+way. Similarly, try typing \kbd{log(-2)}, \kbd{exp(I*Pi)}, \kbd{I\pow
+I}\dots\ So we have a lot of real and complex analysis at our disposal.
+There always is a specific branch of multivalued complex transcendental
+functions which is taken, specified in the manual. Again, beware that
+\kbd{I} and \kbd{i} are not the same thing. Compare \kbd{I\pow2} with
+\kbd{i\pow2} for instance.
+
+Just for fun, let's try \kbd{6*zeta(2) / Pi\pow2}. Pretty close, no?
+
+\medskip
+Now \kbd{gp} didn't seem to know what \kbd{log(x)} was, although it did know
+how to compute numerical values of \kbd{log}. This is annoying. Maybe it
+knows the exponential function? Let's give it a try. Type \kbd{exp(x)}.
+What's this? If you had any experience with other computer algebra systems,
+the answer should have simply been \kbd{exp(x)} again. But here the answer is
+the Taylor expansion of the function around $\kbd{x}=0$, to 16 terms. 16 is
+the default \kbd{seriesprecision}, which can be changed by typing \kbd{\b{ps}
+$n$} or \kbd{default(seriesprecision, $n$)} where $n$ is the number of terms
+that you want in your power series. Note the \kbd{O(x\pow16)} which ends the
+series, and which is trademark of this type of object in \kbd{gp}. It is the
+familiar ``big--oh'' notation of analysis.
+
+You thus automatically get the Taylor expansion of any function that can be
+expanded around $0$, and incidentally this explains why we weren't able to do
+anything with \kbd{log(x)} which is not defined at $0$. (In fact \kbd{gp}
+knows about Laurent series, but \kbd{log(x)} is not meromorphic either at
+$0$.) If we try \kbd{log(1+x)}, then it works. But what if we wanted the
+expansion around a point different from 0? Well, you're able to change $x$
+into $x-a$, aren't you? So for instance you can type \kbd{log(x+2)} to have
+the expansion of \kbd{log} around $\kbd{x}=2$. As exercises you can try
+\bprog
+  cos(x)
+  cos(x)^2 + sin(x)^2
+  exp(cos(x))
+  gamma(1 + x)
+  exp(exp(x) - 1)
+  1 / tan(x)
+ at eprog\noindent
+for different values of \kbd{serieslength} (change it using \b{ps}
+\var{newvalue}).
+
+Let's try something else: type \kbd{(1 + x)\pow 3}. No \kbd{O(x)} here, since
+the result is a polynomial.  Haha, but I have learnt that if you do not take
+exponents which are integers greater or equal to 0, you obtain a power series
+with an infinite number of non-zero terms. Let's try.  Type
+\kbd{(1 + x)\pow (-3)} (the parentheses around \kbd{-3} are not necessary but
+make things easier to read). Surprise! Contrary to what we expected, we don't
+get a power series but a rational function. Again this is for the same reason
+that \kbd{1 / 7} just gave you $1/7$: the result being exact, PARI doesn't see
+any reason to make it non-exact.
+
+But I still want that power series. To obtain it, you can do as in the $1/7$
+example and type
+\bprog
+  (1 + x)^(-3) + O(x^16)
+  (1 + x)^(-3) * (1 + O(x^16))
+  (1 + x + O(x^16))^(-3)
+ at eprog\noindent
+(Not on this example, but there is a difference between the first $2$
+methods. Do you spot it?) Better yet, use the series constructor which
+transforms any object into a power series, using the current
+\kbd{seriesprecision}, and simply type
+\bprog
+  Ser( (1 + x)^(-3) )
+ at eprog
+
+Now try \kbd{(1 + x)\pow (1/2)}: we obtain a power series, since the
+result is an object which PARI does not know how to represent exactly. (We
+could teach PARI about algebraic functions, but then take \kbd{(1 + x)\pow Pi}
+as another example.) This gives us still another solution to our preceding
+exercise: we can type \kbd{(1 + x)\pow (-3.)}. Since \kbd{-3.} is not an exact
+quantity, PARI has no means to know that we are dealing with a rational
+function, and will instead give you the power series, this time with real
+instead of integer coefficients.
+\smallskip
+
+To summarize, in this section we have seen that in addition to integers, real
+numbers and rational numbers, PARI can handle complex numbers, polynomials,
+rational functions and power series. A large number of functions exist which
+handle these types, but in this tutorial we will only look at a few.
+
+\misctitle{Additional comments} (as before, you are supposed to skip this
+at first reading)
+
+1) In almost all cases, there is no loss of information in PARI output: what
+you see is all that PARI knows about the object, and you can happily
+copy-paste it into another session. There are exceptions, though. Type
+\kbd{n = 3 + 0*x}, then \kbd{n} is not the integer 3 but a constant polynomial
+equal to $3 x^0$. Check it with \kbd{type(n)}.
+
+However, it \emph{looks} like an integer without being one, and this may
+cause some confusion in programs which actually expect integers. Hence if you
+try to \kbd{factor(n)}, you obtain an empty factorization ! (Because, once
+considered as a polynomial, \kbd{n} is a unit in $\Q[x]$.)
+
+If you try to apply more general arithmetic functions, say the Euler totient
+function (known as \kbd{eulerphi} to \kbd{gp}), you get an error message
+worrying about integer arguments. You would have guessed yourself, but the
+message is difficult to understand since 3 looks like a genuine integer!
+Please make sure you understand the above, it is a common source of
+incomprehension.
+
+2) If you want the final expression to be in the simplest form possible (for
+example before applying an arithmetic function, or simply because things will
+go faster afterwards), apply the function \kbd{simplify} to the result.
+This is done automatically at the end of a \kbd{gp} command, but
+\emph{not} in intermediate expressions. Hence \kbd{n} above is not an
+integer, but the final result stored in the output history is! So
+if you type \kbd{type(\%)} instead of \kbd{type(n)} the answer is
+\typ{INT}, adding to the confusion.
+
+3) As already stated, power series expansions are always implicitly around
+$\kbd{x} = 0$. When we wanted them around $\kbd{x} = \kbd{a}$, we replaced
+\kbd{x} by \kbd{z + a} in the function we wanted to expand. For complicated
+functions, it may be simpler to use the substitution function \kbd{subst}.
+For example, if \kbd{p~= 1 / (x\pow 4 + 3*x\pow 3 + 5*x\pow 2 - 6*x + 7)},
+you may not want to retype this, replacing \kbd{x} by \kbd{z~+ a}, so you can
+write \kbd{subst(p, x, z+a)} (look up the exact description of the
+\kbd{subst} function).
+
+Now type \kbd{subst(1 + O(x), x, z+1)}. Do you understand the error message?
+
+4) The valuation at $\kbd{x} = 0$ for a power series \kbd{p} is obtained
+as \kbd{valuation(p, x)}.
+
+\section{The Remaining PARI Types}
+Let's talk some more about the basic PARI types.
+
+Type \kbd{p = x * exp(-x)}. As expected, you get the power series expansion
+to 16 terms (if you have not changed the default). Now type
+\kbd{pr = serreverse(p)}. You are asking here for the \emph{reversion} of the
+power series \kbd{p}, in other words the inverse function. This is possible
+only for power series whose first non-zero coefficient is that of $x^1$.  To
+check the correctness of the result, you can type \kbd{subst(p, x, pr)} or
+\kbd{ subst(pr, x, p)} and you should get back \kbd{x + O(x\pow 17)}.
+
+Now the coefficients of \kbd{pr} obey a very simple formula. First, we would
+like to multiply the coefficient of \kbd{x\pow n} by \kbd{n!} (in the case of
+the exponential function, this would simplify things considerably!). The PARI
+function \kbd{serlaplace} does just that. So type \kbd{ps = serlaplace(pr)}.
+The coefficients now become integers, which can be immediately recognized by
+inspection. The coefficient of $x^n$ is now equal to
+$n^{n-1}$. In other words, we have
+%
+$$\kbd{pr} = \sum_{n\ge1}\dfrac{n^{n-1}}{n!} X^{n}.$$
+%
+Do you know how to prove this? (The proof is difficult.)
+\smallskip
+%
+Of course PARI knows about vectors (rows and columns are distinguished, even
+though mathematically there is no difference) and matrices. Type for example
+\kbd{[1,2,3,4]}. This gives the row vector whose coordinates are 1, 2, 3 and
+4.  If you want a column vector, type \kbd{[1,2,3,4]\til}, the tilde meaning
+of course transpose. You don't see much difference in the output, except for
+the tilde at the end. However, now type \b{b}: lo and behold, the column
+vector appears as a proper vertical thingy now. The \b{b} command is used
+mainly for this purpose. The length of a vector is given by, well
+\kbd{length} of course. The shorthand ``cardinal'' notation \kbd{\#v} for
+\kbd{length(v)} is also available, for instance \kbd{v[\#v]} is the last
+element of \kbd{v}.
+
+Type \kbd{m = [a,b,c; d,e,f]}. You have just entered a matrix with 2 rows and
+3 columns. Note that the matrix is entered by \emph{rows} and the rows are
+separated by semicolons ``\kbd{;}''. The matrix is printed naturally in a
+rectangle shape. If you want it printed horizontally just as you typed it,
+type \b{a}, or if you want this type of printing to be the permanent default
+type \kbd{default(output, 0)}. Type \kbd{default(output, 1)} if you want to
+come back to the original output mode.
+
+Now type \kbd{m[1,2]}, \kbd{m[1,]}, \kbd{m[,2]}. Are explanations necessary?
+(In an expression such as \kbd{m[j,k]}, the \kbd{j} always refers to the
+row number, and the \kbd{k} to the column number, and the first index is
+always 1, never 0. This default cannot be changed.)
+
+Even better, type \kbd{m[1,2] = 5; m}. The semicolon also allows us to put
+several instructions on the same line; the final result is the output of
+the last statement on the line. Now type \kbd{m[1,] = [15,-17,8]}. No
+problem. Finally type \kbd{m[,2] = [j,k]}. You have an error message since you
+have typed a row vector, while \kbd{m[,2]} is a column vector. If you type
+instead \kbd{m[,2] = [j,k]\til} it works. \smallskip
+%
+\label{se:types}
+Type now \kbd{h = mathilbert(20)}. You get the so-called ``Hilbert matrix''
+whose coefficient of row $i$ and column $j$ is equal to $(i+j-1)^{-1}$.
+Incidentally, the matrix \kbd{h} takes too much room. If you don't want to
+see it, simply type a semi-colon ``\kbd{;}'' at the end of the line
+(\kbd{h = mathilbert(20);}). This is an example of a ``precomputed'' matrix,
+built into PARI. We will see a more general construction later.
+
+What is interesting about Hilbert matrices is that first their inverses and
+determinants can be computed explicitly (and the inverse has integer
+coefficients), and second they are numerically very unstable, which make them
+a severe test for linear algebra packages in numerical analysis.  Of course
+with PARI, no such problem can occur: since the coefficients are given as
+rational numbers, the computation will be done exactly, so there cannot be
+any numerical error. Try it. Type \kbd{d~=~matdet(h)}. The result is a
+rational number (of course) of numerator equal to 1 and denominator having
+226 digits. How do I know, by the way? Well, type \kbd{sizedigit(1/d)}. Or
+\kbd{\#Str(1/d)}. (The length of the character string representing the
+result.)
+
+Now type \kbd{hr = 1.* h;} (do not forget the semicolon, we don't want to see
+the result!), then \kbd{dr = matdet(hr)}. You notice two things. First the
+computation, is much faster than in the rational case. (If your computer is
+too fast for you to notice, try again with \kbd{h = mathilbert(40)}, or
+even some larger value.) The reason for this is that PARI is handling real
+numbers with 38 digits of accuracy, while in the rational case it is
+handling integers having up to 226 decimal digits.
+
+The second, more important, fact is that the result is terribly wrong. If you
+compare with \kbd{1.$*$d} computed earlier, which is the correct answer, you
+will see that few decimals agree! (None agree if you replaced 20 by 40 as
+suggested above.) This catastrophic instability is as already mentioned one
+of the characteristics of Hilbert matrices. In fact, the situation is
+worse than that. Type \kbd{norml2(1/h - 1/hr)} (the function \kbd{norml2}
+gives the square of the $L^2$ norm, i.e.~the sum of the squares of the
+coefficients). The result is larger than $10^{32}$, showing that some
+coefficients of \kbd{1/hr} are wrong by as much as $10^{16}$. To obtain the
+correct result after rounding for the inverse, we have to use a default
+precision of 57 digits (try it).
+
+Although vectors and matrices can be entered manually, by typing explicitly
+their elements, very often the elements satisfy a simple law and one uses a
+different syntax. For example, assume that you want a vector whose $i$-th
+coordinate is equal to $i^2$. No problem, type for example
+\kbd{vector(10,i, i\pow 2)} if you want a vector of length 10. Similarly, if
+you type
+\bprog
+  matrix(5,5, i,j, 1 / (i+j-1))
+ at eprog\noindent
+you will get the Hilbert matrix of order 5, hence the \kbd{mathilbert}
+function is in fact redundant.  The \kbd{i} and \kbd{j} represent dummy
+variables which are used to number the rows and columns respectively (in
+the case of a vector only one is present of course). You must not forget,
+in addition to the dimensions of the vector or matrix, to indicate
+explicitly the names of these variables. You may omit the variables and
+the final expression to get zero entries, as in \kbd{matrix(10,20)}.
+
+\misctitle{Warning} The letter \kbd{I} is reserved for the complex number
+equal to the square root of $-1$. Hence it is forbidden to use it as a
+variable. Try typing \kbd{vector(10,I, I\pow 2)}, the error message that you
+get clearly indicates that \kbd{gp} does not consider \kbd{I} as a variable.
+There are other reserved variable names: \kbd{Pi}, \kbd{Euler} and
+\kbd{Catalan}. All function names are forbidden as well. On the other hand
+there is nothing special about \kbd{i}, \kbd{pi}, \kbd{euler} or \kbd{catalan}.
+
+When creating vectors or matrices, it is often useful to use Boolean
+operators and the \kbd{if()} statement. Indeed, an \kbd{if} expression has a
+value, which is of course equal to the evaluated part of the \kbd{if}. So for
+example you can type
+\bprog
+  matrix(8,8, i,j, if ((i-j)%2, 1, 0))
+ at eprog\noindent
+to get a checkerboard matrix of \kbd{1} and \kbd{0}. Note however
+that a vector or matrix must be \emph{created} first before being used. For
+example, it is possible to write
+\bprog
+  v = vector(5);
+  for (i = 1, 5, v[i] = 1/i)
+ at eprog\noindent
+but this would fail if the vector \kbd{v} had not been created beforehand.
+Of course, the above example is better written as
+\bprog
+  v = vector(5, i, 1/i);
+ at eprog
+
+Another useful way to create vectors and matrices is to extract them from
+larger ones. For instance, if \kbd{h} is the $20\times 20$ Hilbert matrix as above,
+\bprog
+  h = mathilbert(20);
+  h[11..20, 11..20]
+ at eprog\noindent is its lower right quadrant.
+
+\medskip The last PARI types which we have not yet played with are closely
+linked to number theory. People not interested in number theory can skip
+ahead.
+
+The first is the type ``integer--modulo''. Let us see an example. Type
+\bprog
+  n = 10^15 + 3
+ at eprog
+We want to know whether this number is prime or not. Of course we could make
+use of the built-in facilities of PARI, but let us do otherwise. We first
+trial divide by the built-in table of primes. We slightly cheat here and use
+a variant of the function \kbd{factor} which does exactly this. So type
+\kbd{factor(n, 200000)}. The last argument tells \kbd{factor} to trial divide
+up to the given bound and stop at this point. Set it to 0 to trial divide by
+the full set of built-in primes, which goes up to $500000$ by default.
+
+As for all factoring functions, the result is a 2 column matrix: the first
+column gives the primes and the second their exponents. Here we get a single
+row, telling us that if primes stopped at $200000$ as we made \kbd{factor}
+believe, \kbd{n} would be prime. (Or is that a contradiction?) More
+seriously, \kbd{n} is not divisible by any prime up to $200000$.
+
+We could now trial divide further, or cheat and call the PARI function
+\kbd{factor} without the optional second argument, but before we do this let
+us see how to get an answer ourselves.
+
+By Fermat's little theorem, if $n$ is prime we must have $a^{n-1}\equiv 1
+\pmod{n}$ for all $a$ not divisible by $n$. Hence we could try this with $a=2$
+for example. But $2^{n-1}$ is a number with approximately $3\cdot10^{14}$
+digits, hence impossible to write down, let alone to compute. But instead type
+\kbd{a = Mod(2,n)}. This creates the number $2$ considered now as an element
+of the ring $R = \Z/\kbd{n}\Z$. The elements of $R$, called intmods, can
+always be represented by numbers smaller than \kbd{n}, hence small. Fermat's
+theorem can be rewritten
+%
+$\kbd{a}^{n-1} = \kbd{Mod(1,n)}$
+%
+in the ring $R$, and this can be computed very efficiently. Elements of $R$
+may be lifted back to $\Z$ with either \kbd{lift} or \kbd{centerlift}. Type
+\kbd{a\pow (n-1)}. The result is definitely \emph{not} equal to
+\kbd{Mod(1,n)}, thus \emph{proving} that \kbd{n} is not a prime. If we had
+obtained \kbd{Mod(1,n)} on the other hand, it would have given us a hint that
+\kbd{n} is maybe prime, but not a proof.
+
+To find the factors is another story. In this case, the integer $n$ is small
+ enough to let trial division run to completion. Type \kbd{\#} to turn on the
+\kbd{gp} timer, then
+\bprog
+  for (i = 2, ceil(sqrt(n)), if (n%i==0, print(i); break))
+ at eprog\noindent
+This should take less than 5 seconds. In general, one must use less naive
+techniques than trial division, or be very patient. Type \kbd{fa = factor(n)}
+to let the factoring engine find all prime factors. You may stop the timer by
+typing \kbd{\#} again.
+
+Note that, as is the case with most ``prime''-producing functions, the
+``prime'' factors given by \kbd{factor} are only strong pseudoprimes, and not
+\emph{proven} primes.  Use \kbd{isprime( fa[,1] )} to rigorously prove
+primality of the factors. The latter command applies \kbd{isprime} to all
+entries in the first column of \kbd{fa}, i.e to all pseudoprimes, and returns
+the column vector of results: all equal to 1, so our pseudoprimes were
+true primes. All arithmetic functions can be applied in this way to the entries
+of a vector or matrix. In fact, it has been checked that the strong
+pseudoprimes output by \kbd{factor} (Baillie-Pomerance-Selfridge-Wagstaff
+pseudoprimes, without small divisors) are true primes at least up to
+$2^{64}$, and no explicit counter-example is known.\smallskip
+
+The second specifically number-theoretic type is the $p$-adic numbers. I have
+no room for definitions, so please skip ahead if you have no use for such
+beasts. A $p$-adic number is entered as a rational or integer valued
+expression to which is added \kbd{O(p\pow n)}, or simply \kbd{O(p)} if
+$\kbd{n}=1$, where \kbd{p} is the prime and \kbd{n} the $p$-adic precision.
+Note that you have to explicitly type in \kbd{3\pow 2} for instance, \kbd{9}
+will not do. Unless you want to cheat \kbd{gp} into believing that \kbd{9}
+is prime, but you had better know what you are doing in this case: most
+computations will yield a wrong result.
+
+Apart from the usual arithmetic operations, you can apply a number of
+transcendental functions. For example, type \kbd{n = 569 + O(7\pow 8)}, then
+\kbd{s~=~sqrt(n)}, you obtain one of the square roots of \kbd{n}; to check
+this, type \kbd{s\pow 2 - n}). Type now \kbd{s = log(n)}, then \kbd{e =
+exp(s)}. If you know about $p$-adic logarithms, you will not be surprised
+that \kbd{e} is not equal to \kbd{n}. Type \kbd{(n/e)\pow 6}: \kbd{e} is in
+fact equal to \kbd{n} times the $(p-1)$-st root of unity \kbd{teichmuller(n)}.
+
+Incidentally, if you want to get back the integer 569 from the $p$-adic
+number \kbd{n}, type \kbd{lift(n)} or \kbd{truncate(n)}.
+\smallskip
+
+The third number-theoretic type is the type ``quadratic number''. This type
+is specially tailored so that we can easily work in a quadratic extension of
+a base field, usually $\Q$. It is a generalization of the type
+``complex''. To start, we must specify which quadratic field we want to work
+in. For this, we use the function \kbd{quadgen} applied to the
+\emph{discriminant} \kbd{d} (as opposed to the radicand) of the quadratic
+field. This returns a number (always printed as \kbd{w}) equal to
+$(\kbd{d}+a) / 2$ where $a$ is equal to 0 or 1 according to whether \kbd{d} is
+even or odd. The behavior of \kbd{quadgen} is a little special: although its
+result is always printed as \kbd{w}, the variable \kbd{w} itself is not set
+to that value. Hence it is necessary to write systematically
+\kbd{w = quadgen(d)} using the variable name \kbd{w} (or \kbd{w1} etc. if you
+have several quadratic fields), otherwise things will get confusing.
+
+So type \kbd{w = quadgen(-163)}, then \kbd{charpoly(w)} which asks for the
+characteristic polynomial of \kbd{w}. The result shows what \kbd{w} will
+represent. You may ask for \kbd{1.*w} to see which root of the quadratic has
+been taken, but this is rarely necessary. We can now play in the field
+$\Q(\sqrt{-163})$. Type for example \kbd{w\pow 10}, \kbd{norm(3 + 4*w)},
+\kbd{1 / (4+w)}. More interesting, type \kbd{a = Mod(1,23) * w} then \kbd{b =
+a\pow 264}. This is a generalization of Fermat's theorem to quadratic fields.
+If you do not want to see the modulus 23 all the time, type \kbd{lift(b)}.
+
+Another example: type \kbd{p = x\pow 2 + w*x + 5*w + 7}, then \kbd{norm(p)}. We
+thus obtain the quartic equation over $\Q$ corresponding to the relative
+quadratic extension over $\Q(\kbd{w})$ defined by \kbd{p}.
+
+On the other hand, if you type \kbd{wr  = sqrt(w\pow 2)}, do not expect to get
+back \kbd{w}. Instead, you get the numerical value, the function \kbd{sqrt}
+being considered as a ``transcendental'' function, even though it is
+algebraic. Type \kbd{algdep(wr,2)}: this looks for algebraic relations
+involving the powers of \kbd{w} up to degree 2. This is one way to get
+\kbd{w} back. Similarly, type \kbd{algdep(sqrt(3*w + 5), 4)}. See the user's
+manual for the function \kbd{algdep}.\smallskip
+
+The fourth number-theoretic type is the type ``polynomial--modulo'', i.e.
+polynomial modulo another polynomial. This type is used to work in general
+algebraic extensions, for example elements of number fields (if the base
+field is $\Q$), or elements of finite fields (if the base field is
+$\Z/p\Z$ for a prime $p$). In a sense it is a generalization of the type
+quadratic number. The syntax used is the same as for intmods. For example,
+instead of typing \kbd{w = quadgen(-163)}, you can type
+\bprog
+  w = Mod(x, quadpoly(-163))
+ at eprog\noindent
+Then, exactly as in the quadratic case, you can type \kbd{w\pow 10},
+\kbd{norm(3 + 4*w)}, \kbd{1 / (4+w)}, \kbd{a = Mod(1,23)*w}, \kbd{b = a\pow
+264}, obtaining of course the same results. (Type \kbd{lift(\dots)} if you
+don't want to see the polynomial \kbd{x\pow 2 - x + 41} repeated all the
+time.) Of course, you can work in any degree, not only quadratic. For the
+latter, the corresponding elementary operations will be slower than
+with quadratic numbers. Start the timer, then compare
+\bprog
+  w = quadgen(-163); W = Mod(x, quadpoly(-163));
+  a = 2 + w;         A = 2 + W;
+  b = 3 + w;         B = 3 + W;
+  for (i=1,10^5, a+b)
+  for (i=1,10^5, A+B)
+  for (i=1,10^5, a*b)
+  for (i=1,10^5, A*B)
+  for (i=1,10^5, a/b)
+  for (i=1,10^5, A/B)
+ at eprog\noindent
+Don't retype everything, use the arrow keys!
+
+There is however a slight difference in behavior. Keeping our polmod \kbd{w},
+type \kbd{1.*w}. As you can see, the result is not the same. Type
+\kbd{sqrt(w)}. Here, we obtain a vector with 2 components, the two components
+being the principal branch of the square root of all the possible embeddings
+of \kbd{w} in $\C$. More generally, if
+\kbd{w} was of degree $n$, we would get an $n$-component vector, and similarly
+for all transcendental functions.
+
+We have at our disposal the usual arithmetic functions, plus a few others.
+Type \kbd{a = Mod(x, x\pow 3 - x - 1)} defining a cubic extension. We can for
+example ask for \kbd{b = a\pow 5}. Now assume we want to express \kbd{a}
+as a polynomial in \kbd{b}. This is possible since \kbd{b} is also a
+generator of the same field. No problem, type \kbd{modreverse(b)}. This gives
+a new defining polynomial for the same field, i.e.~the characteristic
+polynomial of \kbd{b}, and expresses \kbd{a} in terms of this new polmod,
+i.e.~in terms of \kbd{a}. We will see this in more detail in the number
+field section.
+
+An important special case of the above construction allows to work in finite
+fields, by choosing an irreductible polynomial $T$ of degree $f$ over $\F_p$
+and considering $\F_p[t]/(T)$. As in
+\bprog
+  T = ffinit(5, 6, 't); \\ @com degree 6, irreducible over $\F_5$
+  g = Mod(t, T)
+ at eprog\noindent Try a few elementary operations involving $g$, such as
+$g^{100}$. This special case of \typ{POLMOD}s is in fact so important that we
+now introduce a final dedicated number theoretical type \typ{FFELT}, for
+``finite field element'', to simplify work with finite fields: \kbd{g =
+ffgen(5\pow6, 't)} computes a suitable polynomial $T$ as above and returns
+the generator $t \mod T(t)$. This has major advantages over the generic
+\typ{POLMOD} solution: elements are printed in a simplified way (in lifted
+form), and functions can assume that $T$ is indeed irreducible. A few dedicated
+functions  \kbd{ffprimroot} (analog of \kbd{znprimroot}), \kbd{fforder}
+(analog of \kbd{znorder}), \kbd{fflog} (analog of \kbd{znlog}) are available.
+Rational expressions in the variable $t$ can be mapped to such a finite
+field by substituting $t$ by $g$, for instance
+\bprog
+  ? g = ffgen(5^6, 't);
+  ? g.mod  \\ @com irreducible over $\F_5$, defines $\F_{5^6}$
+  %2 = t^6 + t^5 + t^4 + t^3 + t^2 + t + 1
+  ? Q = x^2 + t*x + 1
+  ? factor(subst(Q,t,g))
+  %3 =
+  [    x + (t^5 + 3*t^4 + t^3 + 4*t + 1) 1]
+
+  [x + (4*t^5 + 2*t^4 + 4*t^3 + 2*t + 4) 1]
+ at eprog\noindent factors the polynomial $Q \in \F_{5^6}[x]$, where
+$\F_{5^6} = \F_5[t]/(\kbd{g.mod})$.
+
+\section{Elementary Arithmetic Functions}
+
+Since PARI is aimed at number theorists, it is not surprising that there
+exists a large number of arithmetic functions; see the list by typing
+\kbd{?4}. We have already seen several, such as \kbd{factor}. Note that
+\kbd{factor} handles not only integers, but also univariate polynomials.
+Type for example \kbd{factor(x\pow 200 - 1)}. You can also ask to factor a
+polynomial modulo a prime $p$ (\kbd{factormod}) and even in a finite field
+which is not a prime field (\kbd{factorff}).
+
+Evidently, you have functions for computing GCD's (\kbd{gcd}), extended GCD's
+(\kbd{bezout}), solving the Chinese remainder theorem (\kbd{chinese}) and so
+on.
+
+In addition to the factoring facilities, you have a few functions related to
+primality testing such as \kbd{isprime}, \kbd{ispseudoprime},
+\kbd{precprime}, and \kbd{nextprime}. As previously mentioned, only strong
+pseudoprimes are produced by the latter two (they pass the
+\kbd{ispseudoprime} test); the more sophisticated primality tests in
+\kbd{isprime}, being so much slower, are not applied by default.
+
+We also have the usual multiplicative arithmetic functions: the M\"obius $\mu$
+function (\kbd{moebius}), the Euler $\phi$ function (\kbd{eulerphi}), the
+$\omega$ and $\Omega$ functions (\kbd{omega} and \kbd{bigomega}), the
+$\sigma_k$ functions (\kbd{sigma}), which compute sums of $k$-th powers of the
+positive divisors of a given integer, etc\dots
+
+You can compute continued fractions. For example, type \kbd{\b{p} 1000}, then
+\kbd{contfrac(exp(1))}: you obtain the continued fraction of the base of
+natural logarithms, which as you can see obeys a very simple pattern. Can
+you prove it?
+
+In many cases, one wants to perform some task only when an arithmetic
+condition is satisfied. \kbd{gp} gives you the following functions: \kbd{isprime}
+as mentioned above, \kbd{issquare}, \kbd{isfundamental} to test whether an
+integer is a fundamental discriminant (i.e.~$1$ or the discriminant of a
+quadratic field), and the \kbd{forprime}, \kbd{fordiv} and \kbd{sumdiv}
+loops. Assume for example that we want to compute the product of all the
+divisors of a positive integer \kbd{n}. The easiest way is to write
+\bprog
+  p = 1; fordiv(n,d, p *= d); p
+ at eprog\noindent
+(There is a simple formula for this product in terms of $n$ and the number of
+its divisors: find and prove it!) The notation \kbd{p *= d} is just a
+shorthand for \kbd{p = p * d}.
+
+If we want to know the list of primes $p$ less than 1000 such that 2 is a
+primitive root modulo $p$, one way would be to write:
+\bprog
+  forprime(p=3,1000, if (znprimroot(p) == 2, print(p)))
+ at eprog\noindent
+%
+Note that this assumes that \kbd{znprimroot} returns the smallest primitive
+root, and this is indeed the case. Had we not known about this, we could
+have written
+\bprog
+  forprime(p=3,1000, if (znorder(Mod(2,p)) == p-1, print(p)))
+ at eprog\noindent
+%
+(which is actually faster since we only compute the order of $2$ in $\Z/p\Z$,
+instead of looking for a generator by trying successive elements whose orders
+have to be computed as well.) Once we know a primitive root $g$, we can write
+any non-zero element of $\Z/p\Z$ as $g^x$ for some unique $x$ in $\Z/(p-1)\Z$.
+Computing such a discrete logarithm is a hard problem in general, performed
+by the function \kbd{znlog}.
+
+Arithmetic functions related to quadratic fields, binary quadratic forms and
+general number fields will be seen in the next sections.
+
+\section{Performing Linear Algebra}
+The standard linear algebra routines are available: \kbd{matdet},
+\kbd{mateigen} (eigenvectors), \kbd{matker}, \kbd{matimage}, \kbd{matrank},
+\kbd{matsolve} (to solve a linear system), \kbd{charpoly} (characteristic
+polynomial), to name a few. Bilinear algebra over $\R$ is also there:
+\kbd{qfgaussred} (Gauss reduction), \kbd{qfsign} (signature). You may also
+type \kbd{?8}. Can you guess what each of these do?
+
+Let us see how this works. First, a vector space (or module) is given by a
+generating set of vectors (often a basis) which are represented as
+\emph{column} vectors. This set of vectors is in turn represented by the
+columns of a matrix. Quadratic forms are represented by their Gram matrix.
+The base field (or ring) can be any ring type PARI supports. However, certain
+operations are specifically written for a real or complex base field, while
+others are written for $\Z$ as the base ring.
+
+We had some fun with Hilbert matrices and numerical instability a while back,
+but most of the linear algebra routines are generic. If as before \kbd{h =
+mathilbert(20)}, we may compute
+\bprog
+  matdet(h * Mod(1,101))
+  matdet(h * (1 + O(101^100)))
+ at eprog\noindent
+in $\Z/101\Z$ and the $p$-adic ring $\Z_{101}$ (to $100$ words of accuracy)
+respectively. Let \kbd{H = 1/h} the inverse of \kbd{h}:
+\bprog
+  H = 1/h;  \\ @com integral
+  L = primes([10^5, 10^5 + 1000]);  \\ @com pick a few primes
+  v = vector(#L, i, matdet(H * Mod(1,L[i])));
+  centerlift( chinese(v) )
+ at eprog\noindent
+returns the determinant of \kbd{H}. (Assuming it is an integer
+less than half the product of elements of \kbd{L} in absolute value, which
+it is.)
+In fact, we computed an homomorphic image of the determinant in a few small
+finite fields, which admits a single integer representative given the size
+constraints. We could also have made a single determinant computation modulo
+a big prime (or pseudoprime) number, e.g \kbd{nextprime(2 * B)} if we know
+that the determinant is less than \kbd{B} in absolute value.
+(Why is that $2$ necessary?)
+
+By the way, this is how you insert comments in a script: everything
+following a double backslash, up to the first newline character, is ignored.
+If you want comments which span many lines, you can brace them between
+\kbd{/* ... */} pairs. Everything in between will be ignored as well. For
+instance as a header for the script above you could insert the
+following:
+\bprog
+  /* Homomorphic imaging scheme to compute the determinant of a classical
+   * integral matrix.
+   * TODO: Look up the explicit formula
+   */
+ at eprog\noindent
+(I hope you did not waste your time copying this nonsense, did you?)
+\medskip
+
+In addition, linear algebra over $\Z$, i.e.~work on lattices, can also be
+performed. Let us now consider the lattice $\Lambda$ generated by the columns
+of \kbd{H} in $\Z^{20}\subset\R^{20}$. Since the determinant is non-zero, we
+have in fact a basis. What is the structure of the finite abelian group
+$\Z^{20}/\Lambda$? Type \kbd{matsnf(H)}. Wow, 20 cyclic factors.
+
+
+There is a triangular basis for $\Lambda$ (triangular when expressed in
+the canonical basis), perhaps it looks better than our initial one? Type
+\kbd{mathnf(H)}. Hum, what if I also want the unimodular transformation
+matrix? Simple : \kbd{z = mathnf(H, 1);} \kbd{z[1]} is the triangular HNF
+basis, and \kbd{z[2]} is the base change matrix from the canonical basis to
+the new one, with determinant $\pm 1$. Try \kbd{matdet(z[2])},
+then \kbd{H * z[2] == z[1]}. Fine, it works. And \kbd{z[1]} indeed looks
+better than \kbd{H}.
+
+Can we do better? Perhaps, but then we'd better drop the requirement that
+the basis be triangular, since the latter is essentially canonical. Type
+\bprog
+  M = H * qflll(H)
+ at eprog
+Its columns give an LLL-reduced basis for $\Lambda$ (\kbd{qflll(H)} itself
+gives the base change matrix). The LLL algorithm outputs a nice basis for a
+lattice given by an arbitrary basis, where nice means the basis vectors are
+almost orthogonal and short, with precise guarantees on their relations to
+the shortest vectors. Not really spectacular on this example, though.
+
+Let us try something else, there should be an integer relation between
+$\log 3$, $\log 5$ and $\log 15$. How to detect it?
+\bprog
+  u = [log(15), log(5), log(3)];
+  m = matid(3); m[3,] = round(u * 10^25);
+  v = qflll(m)[,1] \\@com first vector of the LLL-reduced basis
+  u * v
+ at eprog\noindent
+Pretty close. In fact, \kbd{lindep} automates this kind of search for integer
+relations; try \kbd{lindep(u)}.
+
+Let us come back to $\Lambda$ above, and our LLL basis in \kbd{M}. Type
+\bprog
+  G = M~*M  \\@com Gram matrix
+  m = qfminim(G, norml2(M[,1]), 100, 2);
+ at eprog\noindent
+This enumerates the vectors in $\Lambda$ which are shorter than the first LLL
+basis vector, at most 100 of them. The final argument $2$ instructs the
+function to use a safe (slower) algorithm, since the matrix entries are
+rather large; trying to remove it should produce an error, in this case.
+There are $\kbd{m[1]} = 6$ such vectors, and \kbd{m[3]} gives half of them
+(\kbd{-m[3]} would complete the lot): they are the first 3 basis vectors! So
+these are optimally short, at least with respect to the Euclidean length. Let
+us try
+\bprog
+  m = qfminim(G, norml2(M[,4]), 100, 2);
+ at eprog\noindent
+(The flag $2$ instructs \kbd{qfminim} to use a different enumeration
+strategy, which is much faster when we expect more short vectors than we want
+to store. Without the flag, this example requires several hours. This is an
+exponential time algorithm, after all!) This time, we find a slew of short
+vectors; \kbd{matrank(m[3])} says the 100 we have are all included in a
+2-dimensional space. Let us try
+\bprog
+  m = qfminim(G, norml2(M[,4]) - 1, 100000, 2);
+ at eprog\noindent
+This time we find 50886 vectors of the requested length, spanning a
+$4$-dimensional space, which is actually generated by \kbd{M[,1]},
+\kbd{M[,2]} \kbd{M[,3]} and \kbd{M[,5]}.
+
+\section{Using Transcendental Functions}
+
+All the elementary transcendental functions and several higher transcendental
+functions are provided: $\Gamma$ function, incomplete $\Gamma$ function, error
+function, exponential integral, Bessel functions ($H^1$, $H^2$, $I$, $J$,
+$K$, $N$), confluent hypergeometric functions, Riemann $\zeta$ function,
+polylogarithms, Weber functions, theta functions. More will be written if the
+need arises.
+
+In this type of functions, the default precision plays an essential role.
+In almost all cases transcendental functions work in the following way.
+If the argument is exact, the result is computed using the current
+default precision. If the argument is not exact, the precision of the
+argument is used for the computation. A note of warning however: even in this
+case the \emph{printed} value is the current real format, usually the
+same as the default precision. In the present chapter we assume that your
+machine works with 64-bit long integers. If it is not the case, we leave it
+to you as a good exercise to make the necessary modifications.
+
+Let's assume that we have 38 decimals of default precision (this is what we
+get automatically at the start of a \kbd{gp} session on 64-bit machines). Type
+\kbd{e = exp(1)}. We get the number $e=2.718\dots$ to 38 decimals. Let us check
+how many correct decimals we really have. Change the precision to a
+substantially higher value, for example by typing \kbd{\b{p} 100}. Then type
+\kbd{e}, then \kbd{exp(1)} once again. This last value is the correct value
+of the mathematical constant $e$ to 100 decimals, while the variable \kbd{e}
+shows the value that was computed to 38 decimals. Clearly they coincide to
+exactly 29 significant digits.
+
+So 38 digits are printed, but how many significant digits are actually
+contained in the variable \kbd{e}? Type \kbd{\#e} which indicates we have
+exactly $2$ mantissa words. Since $2\ln(2^{64}) / \ln(10)\approx38.5$ we see
+that we have 38 or 39 significant digits (on 64-bit machines).
+
+\smallskip
+Come back to 38 decimals (\kbd{\b{p} 38}). If we type \kbd{exp(1.)}
+you can check that we also obtain 38 decimals. However, type
+\kbd{f = exp(1 + 1E-40)}. Although the default precision is still 38,
+you can check using the method above that we have in fact 96 significant
+digits! The reason is that \kbd{1 + 1E-40} is computed according to the PARI
+philosophy, i.e.~to the best possible precision. Since \kbd{1E-40} has 39
+significant digits and 1 has ``infinite'' precision, the number \kbd{1 +
+1E-30} will have $79=39+40$ significant digits, hence \kbd{f} also.
+
+Now type \kbd{cos(1E-19)}. The result is printed as $1.0000\dots$, but
+is of course not exactly equal to 1. Using \kbd{\#\%}, we see that the
+result has 4 mantissa words, giving us the possibility of having 77
+correct significant digits. PARI gives you as much as it can, and since 3
+mantissa words would have given you only 57 digits, it uses 4. But why does
+it give so precise a result? Well, it is the same reason as before. When $x$
+is close to 1, $\cos(x)$ is close to $1-x^2/2$, hence the precision is going
+to be approximately the same as when computing this quantity, here
+$1-0.5*10^{-38}$ where $0.5*10^{-38}$ is considered with 38 significant digit
+accuracy. Hence the result will have approximately $38+38=76$ significant
+digits.
+
+This philosophy cannot go too far. For example, when you type \kbd{cos(0)},
+\kbd{gp} should give you exactly 1. Since it is reasonable for a program to
+assume that a transcendental function never gives you an exact result,
+\kbd{gp} gives you $1.000\dots$ with as many mantissa word as the current
+precision.
+\medskip
+Let's see some more transcendental functions at work. Type
+\kbd{gamma(10)}. No problem (type \kbd{9!} to check). Type \kbd{gamma(100)}.
+The number is now written in exponential format because the default accuracy
+is too small to give the correct result. To get all digits, the most natural
+solution is to increase the precision; since \kbd{gamma(100)} has 156 decimal
+digits, type \kbd{\b{p} 170} to be on the safe side, then \kbd{gamma(100)}
+once again. Another one is to compute \kbd{99!} directly.
+
+Try \kbd{gamma(1/2 + 10*I)}. No problem, we have the complex $\Gamma$ function.
+Now type
+\bprog
+  t = 1000;
+  z = gamma(1 + I*t) * t^(-1/2) * exp(Pi/2*t) / sqrt(2*Pi)
+  norm(z)
+ at eprog\noindent The latter is very close to 1, in accordance with the complex
+Stirling formula.
+\smallskip
+
+Let's play now with the Riemann zeta function. First turn on the timer (type
+\kbd{\#}). Type \kbd{zeta(2)}, then \kbd{Pi\pow 2/6}. This seems correct. Type
+\kbd{zeta(3)}. All this takes essentially no time at all. However, type
+\kbd{zeta(3.1)}. You will notice that the time is substantially larger; if
+your machine is too fast to see the difference, increase the precision to
+\kbd{\b{p}1000}. This is because PARI uses special formulas to compute
+\kbd{zeta(n)} when \kbd{n} is an integer.
+
+Type \kbd{zeta(1 + I)}. This also works. Now for fun, let us compute in a
+naive way the first complex zero of \kbd{zeta}. We know that it is
+of the form $1/2 + i*t$ with $t$ between 14 and 15. Thus, we can use the
+following series of instructions. But instead of typing them directly, write
+them into a file, say \kbd{zeta.gp}, then type \kbd{\b{r} zeta.gp} under
+\kbd{gp} to read it in:
+\bprog
+  {
+    t1 = 1/2 + 14*I;
+    t2 = 1/2 + 15*I; eps = 1E-50;
+    z1 = zeta(t1);
+    until (norm(z2) < eps,
+      z2 = zeta(t2);
+      if (norm(z2) < norm(z1),
+        t3 = t1; t1 = t2; t2 = t3; z1 = z2
+      );
+      t2 = (t1+t2) / 2.;
+      print(t1 ": " z1)
+    )
+  }
+ at eprog\noindent
+Don't forget the braces: they tell \kbd{gp} that a sequence of instructions
+is going to span many lines. We thus obtain the first zero to 25 significant
+digits.
+
+By the way, you don't need to type in the suffix~\kbd{.gp} in the \b{r}
+command: it is supplied by \kbd{gp} if you forget it. The suffix is not
+mandatory either, but it is convenient to have all GP scripts labeled in the
+same distinctive way. Also, some text editors, e.g. Emacs or Vim, will
+recognize GP scripts as such by their suffix and load special colourful
+modes. \medskip
+%
+As mentioned at the beginning of this tutorial, some transcendental functions
+can also be applied to $p$-adic numbers. This is as good a time as any to
+familiarize yourself with them. Type
+\bprog
+  a = exp(7 + O(7^10))
+  log(a)
+ at eprog\noindent All seems in order.
+\bprog
+  b = log(5 + O(7^10))
+  exp(b)
+ at eprog\noindent Is something wrong? We don't recover the number we started
+with? This is normal: type
+\bprog
+  exp(b) * teichmuller(5 + O(7^10))
+ at eprog\noindent
+and we indeed recover our initial number. The Teichm\"uller
+character \kbd{teichmuller(x)} on $\Z_p^*$ is the unique \hbox{$(p-1)$-st}
+root of unity which is congruent to \kbd{x} modulo $p$, assuming that \kbd{x}
+is a $p$-adic unit.\smallskip
+%
+Let us come back to real numbers for the moment. Type \kbd{agm(1,sqrt(2))}.
+This gives the arithmetic-geometric mean of 1 and $\sqrt2$, and is the basic
+method for computing complete elliptic integrals. In fact, type
+
+\kbd{Pi/2 / intnum(t=0,Pi/2, 1 / sqrt(1 + sin(t)\pow 2))},
+
+\noindent and the result is the same. The elementary transformation
+\kbd{x = sin(t)} gives the mathematical equality
+$$\int_0^1 \dfrac{dx}{\sqrt{1-x^4}} = \dfrac{\pi}{2\text{AGM}(1,\sqrt2)}
+\enspace,$$
+which was one of Gauss's remarkable discoveries in his youth.
+
+Now type \kbd{2 * agm(1,I) / (1+I)}. As you see, the complex AGM also works,
+although one must be careful with its definition. The result found is
+almost identical to the previous one. Do you see why?
+
+Finally, type \kbd{agm(1, 1 + 7 + O(7\pow 10))}. So we also have $p$-adic
+AGM. Note however that since the square root of a $p$-adic number is not
+in general an element of the same $p$-adic field,
+only certain $p$-adic AGMs can be computed. In addition,
+when $p=2$, the congruence restriction is that \kbd{agm(a,b)} can be computed
+only when \kbd{a/b} is congruent to 1 modulo $16$, and not 8 as could be
+expected.\smallskip
+%
+Now type \kbd{?3}. This gives you the list of all transcendental functions.
+Instead of continuing with more examples, we suggest that you experiment
+yourself with this list. Try integer, real, complex and $p$-adic arguments.
+You will notice that some have not been implemented (or do not have a
+reasonable definition).
+
+\section{Using Numerical Tools}
+
+ Although not written to be a numerical analysis package, PARI can
+nonetheless perform some numerical computations. Since linear algebra and
+polynomial computations are treated somewhere else, this section focuses on
+solving equations and various methods of summation.
+
+You of course know the formula $\pi = 4(1-\dfrac13+\dfrac15-\dfrac17+\cdots)$
+which is deduced from the power series expansion of \kbd{atan(x)}. You also
+know that $\pi$ cannot be computed from this formula, since the convergence
+is so slow. Right? Wrong! Type
+\bprog
+  \p 100
+  4 * sumalt(k=0, (-1)^k/(2*k + 1))
+ at eprog\noindent In a split second, we get $\pi$ to 100 significant digits
+(type \kbd{Pi} to check).
+
+Similarly, try
+\bprog
+  sumpos(k=1, k^-2)
+ at eprog\noindent Although once again the convergence is slow, the summation is
+rather fast; compare with the exact result \kbd{Pi\pow 2/6}. This is less
+impressive because a bit slower than for alternating sums, but still useful.
+
+Even better, \kbd{sumalt} can be used to sum divergent series! Type
+\bprog
+  zet(s) = sumalt(k=1, (-1)^(k-1) / k^s) / (1 - 2^(1-s))
+ at eprog\noindent
+Then for positive values of \kbd{s} different from 1, \kbd{zet(s)} is equal
+to \kbd{zeta(s)} and the series converges, albeit slowly; \kbd{sumalt}
+doesn't care however. For negative \kbd{s}, the series diverges, but
+\kbd{zet(s)} still gives the correct result! (Namely, the value of a suitable
+analytic continuation.) Try \kbd{zet(-1)}, \kbd{zet(-2)}, \kbd{zet(-1.5)},
+and compare with the corresponding values of \kbd{zeta}. You should not push
+the game too far: \kbd{zet(-100)}, for example, gives a completely wrong
+answer.
+
+Try \kbd{zet(I)}, and compare with \kbd{zeta(I)}. Even (some) complex values
+work, although the sum is not alternating any more! Similarly, try
+\bprog
+  sumalt(n=1, (-1)^n / (n+I))
+ at eprog
+\medskip
+More traditional functions are the numerical integration functions. Try
+\kbd{intnum(t=1,2, 1/t)} and presto! you get 100 decimals of $\log(2)$. Look
+at Chapter 3 to see the available integration functions.
+
+With PARI, however, you can go further since complex types are allowed.
+For example, assume that we want to know the location of the zeros of the
+function $h(z)=e^z-z$. We use Cauchy's theorem, which tells us that the
+number of zeros in a disk of radius $r$ centered around the origin is
+equal to
+$$\dfrac{1}{2i\pi}\int_{C_r}\dfrac{h'(z)}{h(z)}\,dz\enspace,$$
+where $C_r$ is the circle of radius $r$ centered at the origin.
+The function we want to integrate is
+\bprog
+  fun(z) = my(u = exp(z)); (u-1) / (u-z)
+ at eprog\noindent
+(Here \kbd{u} is a local variable to the function \kbd{f}: whenever
+a function is called, \kbd{gp} fills its argument list with the actual arguments
+given, and initializes the other declared parameters and local variables to
+0. It will then restore their former values upon exit. If we had not declared
+\kbd{u} in the function prototype, it would be considered as a global
+variable, whose value would be permanently changed. It is not mandatory to
+declare in this way all parameters, but beware of side effects!)
+
+Type now:
+\bprog
+  zero(r) = r/(2*Pi) * intnum(t=0, 2*Pi, real( fun(r*exp(I*t)) * exp(I*t) ))
+ at eprog
+The function \kbd{zero(r)} will count the number of zeros of \kbd{fun}
+whose modulus is less than \kbd{r}: we simply made the change of variable
+$z = r*\exp(i*t)$, and took the real part to avoid integrating the
+imaginary part. Actually, there is a built-in function \kbd{intcirc}
+to integrate over a circle, yielding the much simpler:
+\bprog
+  zero2(r) = intcirc(z=0, r, fun(z))
+ at eprog
+(This is a little faster than the previous implementation, and no less
+accurate.)
+
+We may type \kbd{\b{p} 9} since we know that the result is a small integer
+(but the computations should be instantaneous even at \b{p} 100 or so),
+then \kbd{zero(1)}, \kbd{zero(1.5)}. The result tells us that there are
+no zeros inside the unit disk, but that there are two (necessarily
+complex conjugate) in the annulus $1<|z|<1.5$. For the sake of
+completeness, let us compute them. Let $z = x+iy$ be such a zero, with
+$x$ and $y$ real. Then the equation $e^z-z=0$ implies, after elementary
+transformations, that $e^{2x}=x^2+y^2$ and that $e^x\cos(y)=x$. Hence
+$y=\pm\sqrt{e^{2x}-x^2}$ and hence $e^x\cos(\sqrt{e^{2x}-x^2})=x$.
+Therefore, type
+\bprog
+  fun(x) = my(u = exp(x)); u * cos(sqrt(u^2 - x^2)) - x
+ at eprog\noindent
+Then \kbd{fun(0)} is positive while \kbd{fun(1)} is negative. Come
+back to precision 38 and type
+\bprog
+  x0 = solve(x=0,1, fun(x))
+  z = x0 + I*sqrt(exp(2*x0) - x0^2)
+ at eprog\noindent
+which is the required zero. As a check, type \kbd{exp(z) - z}.
+
+Of course you can integrate over contours which are more complicated than
+circles, but you must perform yourself the variable changes, as we have done
+above to reduce the integral to a number of integrals on line segments.
+\smallskip
+%
+The example above also shows the use of the \kbd{solve} function. To use
+\kbd{solve} on functions of a complex variable, it is necessary to reduce the
+problem to a real one. For example, to find the first complex zero of the
+Riemann zeta function as above, we could try typing
+
+\kbd{solve(t=14,15, real( zeta(1/2 + I*t) ))},
+
+\noindent but this does not work because the real part is positive for
+$\kbd{t}=14$ and $15$. As it happens, the imaginary part works. Type
+
+\kbd{solve(t=14,15, imag( zeta(1/2 + I*t) ))},
+
+\noindent and this now works. We could also narrow the search interval and
+type for instance
+
+\kbd{solve(t=14,14.2, real( zeta(1/2 + I*t) ))}
+
+\noindent which would also work.
+
+\section{Polynomials}
+
+First a word of warning: it is essential to understand the difference between
+exact and inexact objects. Try
+\bprog
+  gcd(x - Pi, x^2 - 6*zeta(2))
+ at eprog\noindent
+We return a trivial GCD because the notion of GCD for non-exact polynomials
+doesn't make much sense. A better quantitative approach is to use
+\bprog
+  polresultant(x - Pi, x^2 - 6*zeta(2))
+ at eprog\noindent
+A result close to zero shows that the GCD is non-trivial for small
+deformations of the inputs. Without telling us what it is, of course. This
+being said, we will mostly use polynomials (and power series) with exact
+coefficients in our examples.\smallskip
+
+The simplest way to input a polynomial, is to simply write it down,
+or use an explicit formula for the coefficients and the function \kbd{sum}:
+\bprog
+  T = 1 + x^2 + 27*x^10;
+  T = sum(i = 1, 100, (i+1) * x^i);
+ at eprog\noindent but it is in much more efficient to create a vector of
+coefficients then convert it to a polynomial using \kbd{Pol} or \kbd{Polrev}
+(\kbd{Pol([1,2])} is $x+2$, \kbd{Polrev([1,2]) is $2x + 1$}) :
+\bprog
+  T = Polrev( vector(100, i, i) );
+
+  for (i=1, 10^4, Polrev( vector(100, i, i) ) )   \\@com time: 60ms
+  for (i=1, 10^4, sum(i = 1, 100, (i+1) * x^i) ) \\@com time: 1,74ms
+ at eprog\noindent The reason for the discrepancy is that the explicit summation
+(of densely encoded polynomials) is quadratic in the degree, whereas creating
+a vector of coefficients then converting it to a polynomial type is linear.
+
+We also have a few built-in classical polynomial families. Consider the
+$15$-th cyclotomic polynomial,
+\bprog
+  pol = polcyclo(15)
+ at eprog\noindent
+which is of degree $\varphi(15)=8$. Now, type
+\bprog
+  r = polroots(pol)
+ at eprog\noindent We obtain the 8 complex roots of \kbd{pol}, given to 38
+significant digits. To see them better, type \b{b}: they are given as pairs
+of complex conjugate roots, in a random order. The only ordering done by the
+function \kbd{polroots} concerns the real roots, which are given first, and
+in increasing order.
+
+The roots of \kbd{pol} are by definition the primitive $15$-th roots of unity.
+To check this, simply type \kbd{rc = r\pow 15}. Why, we get an error message!
+Fair enough, vectors cannot be multiplied, even less raised to a power.
+However, type
+\bprog
+  rc = r^15.
+ at eprog\noindent without forgetting the `\kbd{.}' at the end. Now it works,
+because powering to a non-integer exponent is a transcendental function and
+hence is applied termwise. Note that the fact that $15.$ is a real number
+which is representable exactly as an integer has nothing to do with the
+problem.
+
+We see that the components of the result are very close to 1. It is however
+tedious to look at all these real and imaginary parts. It would be impossible
+if we had many more. Let's do it automatically. Type
+\bprog
+  rr = round(rc)
+  sqrt( norml2(rc - rr) )
+ at eprog\noindent We see that \kbd{rr} is indeed all 1's, and that the
+$L^2$-norm of \kbd{rc - rr} is around $2.10^{-37}$, reasonable enough when we
+work with 38 significant digits! Note that the function \kbd{norml2},
+contrary to what its name implies, does not give the $L^2$ norm but its
+square, hence we must take the square root. Well, this is not absolutely
+necessary in the present case! In fact, \kbd{round} itself already provides
+a built-in rough approximation of the error:
+\bprog
+  rr = round(rc, &e)
+ at eprog\noindent Now \kbd{e} contains the number of error bits when rounding
+\kbd{rc} to \kbd{rr}; in other words the sup norm of $\kbd{rc} - \kbd{rr}$
+is bounded by $2^{-\kbd{e}}$.
+%
+\smallskip
+Now type
+\bprog
+  pol = x^5 + x^4 + 2*x^3 - 2*x^2 - 4*x - 3
+  factor(pol)
+  factor( poldisc(pol) )
+  fun(p) = factorpadic(pol,p,10);
+ at eprog\noindent The polynomial \kbd{pol} factors over $\Q$ (or $\Z$) as a
+product of two factors, and the primes dividing its discriminant are
+$11$, $23$ and $37$. We also created a function \kbd{fun(p)} which factors
+\kbd{pol} over $\Q_p$ to $p$-adic precision 10. Type
+\bprog
+  fun(5)
+  fun(11)
+  fun(23)
+  fun(37)
+ at eprog\noindent to see different splittings.
+
+Similarly, type
+\bprog
+  lf(p) = lift( factormod(pol,p) );
+  lf(2)
+  lf(11)
+  lf(23)
+  lf(37)
+ at eprog\noindent which show the different factorizations, this time over
+$\F_p$. In fact, even better: type successively
+\bprog
+  t = 't;   \\@com we want \kbd{t} to be a free variable
+  T = ffinit(3,3, t)
+  pol2 = subst(T, t, x)
+  fq = factorff(pol2, 3, T)
+  centerlift( lift(fq) )
+ at eprog\noindent
+\kbd{T}, which is actually \kbd{t\pow 3 + t\pow 2 + t + 2} (with intmod
+coefficients), is defined above to be an irreducible polynomial of degree $3$
+over $\F_3$. This code snippet factors the polynomial \kbd{pol2} over the
+finite field $\F_3[t]/(T)$. This is of course a form of the field $\F_{27}$.
+We know that Gal$(\F_{27}/\F_3)$ is cyclic of order 3 generated by the
+Frobenius homomorphism $u\mapsto u^3$, and the roots that we have found give
+the action of the powers of the Frobenius on \kbd{t}. (If you do not know
+what I am talking about, please try some examples, it's not so hard to figure
+out.) We took pain above to factor a polynomial in the variable $x$ over a
+finite field defined by a polynomial in $t$, even though they were apparently
+one and the same. There is a crucial rule in all routines involving relative
+extensions: the variable associated to the base field is required to have
+lower priority than the variables of polynomials whose coefficients are taken
+in that base field. Have a look at the section on \emph{Variable priorities}
+in the user's manual (see ``The GP programming language''). A simpler way
+to accomplish the above, using \typ{FFELT}s is
+\bprog
+  g = ffgen(3^3, 't);
+  T = subst(g.mod, t, x);
+  factor(T * g^0)
+ at eprog\noindent Multiplying by $g^0 = 1$ seems to do ``nothing'', but it has
+the interesting effect of mapping all coefficients of $T$ to $\F_{27}$. The
+generic function \kbd{factor} then does the right thing. (Typing
+\kbd{factor(T)} directly would factor it over $\Q$, not what we wanted.)
+
+Similarly, type
+\bprog
+  pol3 = x^4 - 4*x^2 + 16
+  fn = lift( factornf(pol3, t^2 + 1) )
+ at eprog\noindent and we get the factorization of the polynomial \kbd{pol3}
+over the number field defined by $t^2+1$, i.e.~over $\Q(i)$. Without the
+\kbd{lift}, the result would involve number field elements as \typ{POLMOD}s
+of the form \kbd{Mod(1+t, t\pow2+1)}, which are more explicit but much less
+readable.
+\smallskip
+
+To summarize, in addition to being able to factor integers, you can
+factor polynomials over $\C$ and $\R$ using \kbd{polroots},
+over $\F_p$ using \kbd{factormod}, over $\F_{p^k}$ using \kbd{factorff},
+over $\Q_p$ using \kbd{factorpadic}, over $\Q$ using \kbd{factor}, and over
+number fields using \kbd{factornf} or \kbd{nffactor}. Note however
+that \kbd{factor} itself will try to guess intelligently over which ring you
+want to factor: try
+\bprog
+  pol = x^2 + 1;
+  factor(pol)
+  factor(pol *1.)
+  factor(pol * (1 + 0*I))
+  factor(pol * (1 + 0.*I))
+  factor(pol * Mod(1,2))
+  factor(pol * Mod(1, Mod(1,3)*(t^2+1)))
+ at eprog
+
+In the present version \vers{}, it is not possible to factor over other
+rings than the ones mentioned above. For example \kbd{gp} cannot directly
+factor multivariate polynomials, although it is not too hard to write a
+simple-minded Kronecker's substitution to reduce to the univariate case.
+(Exercise!) Other functions related to factoring are \kbd{padicappr},
+\kbd{polrootsmod}, \kbd{polrootspadic}, \kbd{polsturm}. Play with them a
+little.
+
+Finally, type
+\bprog
+  polsym(pol3, 20)
+ at eprog\noindent where \kbd{pol3} was defined above. This gives the sum of the
+$k$-th powers of the roots of \kbd{pol3} up to $k=20$, of course computed
+using Newton's formula and not using \kbd{polroots}. You notice that every
+odd sum is zero (expected, since the polynomial is even), but also that
+the signs follow a regular pattern and that the (non-zero) absolute values
+are powers of 2. This is true: prove it, and more precisely find an explicit
+formula for the $k$-th symmetric power not involving (non-rational) algebraic
+numbers.
+
+\section{Power series}
+
+Now let's play with power series as we have done at the beginning.  Type
+\bprog
+  N = 39;
+  8*x + prod(n=1,N, if(n%4, 1 - x^n, 1), 1 + O(x^(N+1)))^8
+ at eprog\noindent
+Apparently, only even powers of \kbd{x} appear. This is surprising, but can
+be proved using the theory of modular forms. Note that we initialize
+the product to \kbd{1 + O(x\pow (N+1))}, otherwise the whole computation would
+be done with polynomials; this would first have been slightly slower and also
+totally useless since the coefficients of \kbd{x\pow (N+1)} and above are
+irrelevant anyhow if we stop the product at $\kbd{n} = \kbd{N}$.
+
+While we are on the subject of modular forms (which, together with Taylor
+series expansions are another great source of power series), type
+\bprog
+  \ps 122     \\@com shortcut for \kbd{default(seriesprecision, 122)}
+  d = x * eta(x)^24
+ at eprog\noindent This gives the first 122 terms of the Fourier series
+expansion of the modular discriminant function $\Delta$ of Ramanujan. Its
+coefficients give by definition the Ramanujan $\tau$ function, which has a
+number of marvelous properties (look at any book on modular forms for
+explanations). We would like to see its properties modulo 2. Type \kbd{d\%2}.
+Hmm, apparently PARI doesn't like to reduce coefficients of power series, or
+polynomials for that matter, directly. Can we do it without writing a little
+program? No problem. Type instead
+\bprog
+  lift(Mod(1,2) * d)
+  centerlift(Mod(1,3) * d)
+ at eprog\noindent and now this works like a charm. The pattern in the first
+result is clear; the pattern is less clear in the second result, but
+nonetheless there is one. Of course, it now remains to prove it (see Antwerp
+III or your resident modular forms guru).
+
+\section{Working with Elliptic Curves}
+
+Now we are getting to more complicated objects. Just as with number fields
+which we will meet later on, the first thing to do is to initialize them.
+That's because a lot of data will be needed repeatedly, and it's much more
+convenient to have it ready once and for all. Here, this is done with the
+function \kbd{ellinit}.
+
+So type
+\bprog
+  e0 = ellinit([6,-3,9,-16,-14])
+ at eprog
+This computes a number of things
+about the elliptic curve defined by the affine equation
+%
+$$ y^2+6xy+9y = x^3-3x^2-16x-14\enspace. $$
+%
+It is not that clear what all these funny numbers mean, except that we
+recognize the first few of them as the coefficients we just input. To
+retrieve meaningful information from such complicated objects (and number
+fields will be much worse), one uses so-called \emph{member
+functions}. Type \kbd{?.} to get a complete list. Whenever \kbd{ell} appears
+in the right hand side, we can apply the corresponding function to an object
+output by \kbd{ellinit}. (I'm sure you know how the other \kbd{init} functions
+will be called now, don't you? Oh, by the way, neither \kbd{clgpinit} nor
+\kbd{pridinit} exist.)
+
+  Let's try it. The discriminant \kbd{e0.disc} is equal to 37, hence
+the conductor of the curve is 37. Of course in general it is not so
+trivial. In fact, although the equation of the curve is clearly
+minimal (since the discriminant is $12$th-power-free), it is not in
+standard reduced form, so type
+\bprog
+  e = ellminimalmodel(e0)
+ at eprog\noindent which
+gives the \kbd{ell} structure associated with the standard model,
+exactly as if we had used \kbd{ellinit} on a reduced equation. For
+some related data, type
+\bprog
+  gr = ellglobalred(e0)
+ at eprog\noindent The first
+component \kbd{gr[1]} tells us that the conductor is 37 as we already
+knew.  The second component is a 4-component vector which allows us to
+get the minimal equation: in fact \kbd{e} is \kbd{ellchangecurve(e0, gr[2])}.
+You can for the moment ignore the third component \kbd{gr[3]}. (For the
+impatient reader, this is the product of the local Tamagawa numbers, $c_p$.)
+Type
+\bprog
+  q0 = [-2,2]
+  ellisoncurve(e0, q0)
+  q = ellchangepoint(q0,gr[2])
+  ellisoncurve(e, q)
+ at eprog\noindent
+The point \kbd{q0} is on the curve, as checked by \kbd{ellisoncurve}, and we
+transferred it onto the minimal model \kbd{e}, using \kbd{ellchangepoint} and
+the change of variable computed above. Note that \kbd{ellchangepoint()} is
+unusual among the elliptic curve functions in that it does not take an
+\kbd{ell} structure as its first argument: in \kbd{gp}, points do not
+``know'' which curve they are on, but to move a point from one model to
+another we only need to know the coordinates of the point and the
+transformation data here stored in \kbd{gr[2]}.  Also, the point at infinity
+is represented as \kbd{[0]} on all elliptic curves; this is the identity for
+the group law.
+
+Here, \kbd{q=[0,0]} obviously lies on \kbd{e}, which has equation
+$y^2+y = x^3-x$.  Let us now play a little with points on \kbd{e}.
+The group law on an elliptic curve is implemented with the functions
+\kbd{elladd} for addition, \kbd{ellsub} for subtraction and
+\kbd{ellmul} for multiplication by an integer.  For
+example, the negative of \kbd{q} is \kbd{ellsub(e,[0],q)}, and the
+double is obtained either as \kbd{ellmul(e,q,2)} or as
+\kbd{elladd(e,q,q)}.
+
+Now \kbd{q} may be a torsion point. Type \kbd{ellheight(e, q)}, which
+computes the canonical Neron-Tate height of \kbd{q}. Note that
+\kbd{ellheight} assumes that \kbd{e} is \emph{minimal}! (Which it is.)
+This is non-zero, hence \kbd{q} is not torsion. To see this even better,
+type
+\bprog
+  for(k = 1, 20, print(ellmul(e, q, k)))
+ at eprog\noindent and we see the characteristic parabolic explosion of the size
+of the points. (And another proof that \kbd{q} is not torsion, assuming
+Mazur's bound on the size of the rational torsion.) We could can also type
+\kbd{ellorder(e, q)} which returns 0, telling us yet again that \kbd{q} is
+non-torsion. As a consistency check, type
+\bprog
+  ellheight(e, ellmul(e, q,20)) / ellheight(e, q)
+ at eprog\noindent
+We indeed find $400=20^2$ as it should be.
+
+Notice how (almost) all those \kbd{ell}--prefixed functions take our
+elliptic curve as a first argument? This will be true with number
+fields as well: whatever object was initialized by an $ob$--\kbd{init}
+function will have to be used as a first argument of all the
+$ob$--prefixed functions. Conversely, you won't be able to use any
+such high-level function before you correctly initialize the relevant
+object. \smallskip
+
+Ok, let's try another curve. Type
+\bprog
+  E = ellinit([0,-1,1,0,0])
+  q = [0,0]; ellheight(E, q)
+ at eprog\noindent This corresponds to the equation $y^2+y = x^3-x^2$ and an
+obvious rational point on it. Again from the discriminant we see that the
+conductor is equal to 11, and if you type \kbd{ellminimalmodel(E)} you will
+see that the equation  for \kbd{E} is minimal. This time the height is very
+close to zero, hence \kbd{q} must be a torsion point. Indeed, typing
+\bprog
+  for(k=1, 5, print(ellmul(E, q,k)))
+  ellorder(E, q)   \\@com simpler
+ at eprog\noindent
+we see in two different ways that \kbd{q} is a point of order 5. Moreover,
+typing
+\bprog
+  elltors(E)
+ at eprog\noindent shows that \kbd{q} generates all the torsion of \kbd{E},
+which is cyclic of order~$5$. \smallskip
+
+Let's try still another curve, $y^2+y = x^3-7x+6$:
+\bprog
+  e = ellinit([0,0,1,-7,6])
+  ellglobalred(e)
+ at eprog\noindent As before, this is a minimal equation; now the conductor is
+5077. There are some trivial integral points on this curve, but let's try to
+be more systematic. Typing
+\bprog
+  elltors(e)
+ at eprog\noindent
+shows that the torsion subgroup is trivial, so we don't have to worry about
+torsion points. Next, the member \kbd{e.roots} gives us the 3 roots of the
+minimal equation over $\C$, i.e.~$Y^2=X^3-7X+25/4$ (set $(X,Y)=(x,y+1/2)$) so
+if $(x,y)$ is a real point on the curve, $x$ must be at least equal to the
+smallest root, i.e.~$x\ge-3$. Finally, if $(x,y)$ is on the curve, its
+opposite is clearly $(x,-y-1)$. Type
+\bprog
+  {
+    v = List();
+    for (x = -3, 1000,
+      s = ellordinate(e,x);
+      if (#s, listput(v, [x,s[1]]))  \\@com or \kbd{if (\#s != 0,}
+    ); v
+  }
+ at eprog\noindent
+(If cardinality of \kbd{s} is non-zero, insert the first point with given
+\kbd{x}.) We thus get a large number (18) of integral points. Together with
+their opposites and the point at infinity, this makes a total of 37 integral
+points, which is large for a curve having such a small conductor. So we
+suspect (if we do not know already, since this curve is quite famous!) that
+the rank of this curve must be large. Let's try and put some order into this.
+Note that we work only with the integral points, but in general rational
+points should also be considered. Type
+\bprog
+  v = Vec(v);  \\@com convert to a vector
+  hv = ellheight(e, v)
+ at eprog\noindent (We convert the list to a vector because \typ{LIST}s are not
+accepted as input by most mathematical functions. They must be converted to a
+standard vector type first.) This gives the vector of canonical heights. Let
+us order the points according to their height. For this, type
+\bprog
+  perm = vecsort(vector(#v,i,i), (a,b) -> hv[a]>hv[b]);
+  v = vecextract(v, perm)
+ at eprog\noindent
+Note how we input a non-obvious comparison function to \kbd{vecsort}, using
+an inline anonymous function: we sort the integers between $1$ and the number
+of points we have declaring that $a > b$ if and only if the height of the
+$a$-th point is larger than the height of the $b$-th. The result is a
+permutation which we apply to \kbd{v} using \kbd{vecextract}.
+
+It seems reasonable to take the numbers with smallest height as
+possible generators of the Mordell-Weil group. Let's try the first
+four: type
+\bprog
+  m = ellheightmatrix(e, v[1..4]); matdet(m)
+ at eprog\noindent
+Since the curve has no torsion, the determinant being close to zero
+implies that the first four points are dependent. To find the
+dependency, it is enough to find the kernel of the matrix \kbd{m}. So
+type \kbd{matker(m)}: we indeed get a non-trivial kernel, and the
+coefficients are close to integers. Typing \kbd{elladd(e, v[1],v[3])} does
+indeed show that it is equal to \kbd{v[4]}.
+
+Taking any other four points, we would in fact always find a
+dependency.  Let's find all dependencies. Type
+\bprog
+  vp = [v[1],v[2],v[3]]~;
+  m = ellheightmatrix(e,vp);
+  matdet(m)
+ at eprog\noindent This is now clearly non-zero so the first 3 points are
+linearly independent, showing that the rank of the curve is at least equal to
+3. (It is in fact equal to 3, and \kbd{e} is the curve of smallest conductor
+having rank 3.) We would like to see whether the other points are dependent.
+For this, we use the function \kbd{ellbil}. Indeed, if \kbd{Q} is some point
+which is dependent on \kbd{v[1],v[2]} and \kbd{v[3]}, then \kbd{matsolve(m,
+ellbil(e, vp,Q))} will by definition give the coefficients of the dependence
+relation. If these coefficients are close to integers, then there is a
+dependency, otherwise not.  This is much safer than using the \kbd{matker}
+function. Thus, type
+\bprog
+  w = vector(#v, k, matsolve(m, ellbil(e, vp,v[k])))
+  wr = round(w, &err)
+ at eprog\noindent
+We ``see'' that the coefficients are all very close to integers, and we
+quantified it with the last instruction: \kbd{wr} is the vector expressing
+all the components of \kbd{v} on its first 3, and \kbd{err} gives an upper
+bound on the maximum distance to an integer. We are thus led to strongly
+believe that the curve has rank exactly 3, with generators \kbd{v[1]},
+\kbd{v[2]} and \kbd{v[3]}, and this can be proved to be the case.
+
+Two remarks: (1) Using the height pairing to find dependence relations
+as we have done only in fact finds relations modulo torsion; but in
+this example, the curve has trivial torsion, as we checked
+earlier. (2) In the above calculation we were lucky that all the
+\kbd{v[j]} were $\Z$-linear combinations of \kbd{v[1]}, \kbd{v[2]} and
+\kbd{v[3]} and not just $\Q$-linear combinations;  in general the
+result of \kbd{matsolve(m, ellbil(e, vp,Q))} might have given a vector
+of rationals: if $k\ge2$ is minimal such that $kQ$ is in the subgroup
+generated by \kbd{v[1]}, \kbd{v[2]} and \kbd{v[3]}, then the entries of
+\kbd{matsolve(m, ellbil(e, vp,Q))} will be rationals with common
+denominator~$k$.
+
+\smallskip
+
+Let us explore a few more elliptic curve related functions. Keep our
+curve \kbd{e} of rank 3, and type
+\bprog
+  v1 = [1,0]; z1 = ellpointtoz(e, v1)
+  v2 = [2,0]; z2 = ellpointtoz(e, v2)
+ at eprog\noindent
+We thus get the complex parametrization of the curve. To add the points
+\kbd{v1} and \kbd{v2}, we should of course type \kbd{elladd(e, v1,v2)},
+but we can also type \kbd{ellztopoint(e, z1 + z2)} which has the disadvantage
+of giving complex numbers, but illustrates how the group law on \kbd{e} is
+obtained from the addition law on $\C$.
+
+Type
+\bprog
+  f = x * Ser(ellan(e, 30))
+ at eprog\noindent This gives a power series which is the Fourier expansion of a
+modular form of weight 2 for $\Gamma_0(5077)$. (This has been proved
+directly, before Wiles's general result.) In fact, to find the modular
+parametrization of the curve, type
+\bprog
+  modul = elltaniyama(e)
+  u = modul[1];
+  v = modul[2];
+ at eprog\noindent
+We can check in various ways that this indeed parametrizes the curve:
+\bprog
+  (v^2 + v) - (u^3 - 7*u + 6)
+ at eprog\noindent
+is close to $0$ for instance, or simply that \kbd{ellisoncurve(e,modul)}
+returns~$1$. Now type
+\bprog
+  x * u' / (2*v + 1)
+ at eprog\noindent and we see that this is equal to the modular form \kbd{f}
+found above; the quote \kbd{'} tells \kbd{gp} to take the derivative of the
+expression with respect to its main variable. The functions \kbd{u} and
+\kbd{v}, considered on the upper half plane with $x=e^{2i\pi\tau}$, are in
+fact modular \emph{functions} for $\Gamma_0(5077)$. \smallskip
+
+The function \kbd{ellan(e, 30)} gives the first~$30$ coefficients
+$a_n$ of the $L$-series of \kbd{e}.  One can also ask for a single
+coefficient: the millionth is \kbd{ellak(e, 10\pow 6)}.  Note however
+that calling \kbd{ellan(e,100000)} is much faster than
+the equivalent \kbd{vector(100000,k,ellak(e,k))}.  For a
+prime~\kbd{p},
+\kbd{ellap(e,p)} is equivalent to \kbd{ellak(e,p)};  this is the
+integer $a_p$ such that the number of points on \kbd{e} over $\F_p$ is
+$1+p-a_p$. (With the standard PARI distribution, \kbd{ellap} is the only way
+to obtain the order of an elliptic curve over $\F_p$ in \kbd{gp}. The
+external package \kbd{ellsea} provides much more efficient routines.)
+
+Finally, let us come back to the curve \kbd{E} defined above by
+\kbd{E = ellinit([0,-1,1,0,0])}, which had an obvious rational $5$-torsion
+point. The sign of the functional equation, given by \kbd{ellrootno(E)}, is
+$+1$. Assuming the rank parity conjecture, it follows that the Mordell-Weil
+group of $E$ has even rank. The value of the L-function of \kbd{E} at 1
+\bprog
+  ls = elllseries(E, 1)
+ at eprog\noindent is definitely non-zero, so \kbd{E} has rank $0$. According to
+the Birch and Swinnerton-Dyer conjecture, which is proved for this curve,
+\kbd{ls} is given by the following formula (in this case):
+%
+\def\sha{\hbox{III}}
+$$L(E,1)=\dfrac{\Omega\cdot c\cdot|\sha|}{|E_{\text{tors}}|^2}\enspace,$$
+%
+where $\Omega$ is the real period of $E$, $c$ is the global Tamagawa number,
+product of the local $c_p$ for primes $p$ dividing the conductor, $|\sha|$ is
+the order of the Tate-Shafarevich group, and $E_{\text{tors}}$ is the
+torsion group of $E$.
+
+Now we know many of these quantities: $\Omega$ is equal to \kbd{E.omega[1]}
+(if there had been 3 real roots instead of 1 in \kbd{E.roots}, $\Omega$ would
+be equal to \kbd{2 * E.omega[1]}). The Tamagawa number $c$ is given as the
+last component of \kbd{ellglobalred(E)}, and here is equal to 1. We already
+know that the torsion subgroup of $E$ contains a point of order 5, and typing
+\kbd{elltors(E)} shows that it is of order exactly 5. So type
+\bprog
+  ls * 25/E.omega[1]
+ at eprog\noindent This shows that $\sha$ must be the trivial group.
+
+For more detailed information on the local reduction of an elliptic curve at
+a specific prime~\kbd{p}, use the function \kbd{elllocalred(E,p)}; the second
+component gives the Kodaira symbol in an encoded form.  See the manual or
+online help for details.
+
+\section{Working in Quadratic Number Fields}
+
+The simplest of all number fields outside $\Q$ are quadratic fields and are
+the subject of the present section. We shall deal in the next one with
+general number fields (including $\Q$ and quadratic fields!), and one should
+be aware that all we will see now has a more powerful, in general easier to
+use, equivalent in the general context. But possibly much slower.
+
+Such fields are characterized by their discriminant. Even better, any
+non-square integer $D$ congruent to 0 or 1 modulo 4 is the discriminant of a
+specific order in a quadratic field. We can check whether this order is
+maximal with \kbd{isfundamental(D)}. Elements of a quadratic field are of the
+form $a+b\omega$, where $\omega$ is chosen as $\sqrt{D}/2$ if $D$ is even and
+$(1+\sqrt{D})/2$ if $D$ is odd, and are represented in PARI by quadratic
+numbers. To initialize working in a quadratic order, one starts by the
+command \kbd{w = quadgen($D$)}.
+
+This sets \kbd{w} equal to $\omega$ as above, and is printed \kbd{w}. Note
+however that if several different quadratic orders are used, a printed \kbd{w}
+may have several different meanings. For example if you type
+\bprog
+  w1 = quadgen(-23)
+  w2 = quadgen(-15)
+ at eprog\noindent
+both will be printed as \kbd{w}, but of course they are not equal. Hence
+beware when dealing with several quadratic orders at once. \smallskip
+%
+In addition to elements of a quadratic order, we also want to be able to
+handle ideals of such orders. In the quadratic case, it is equivalent to
+handling binary quadratic forms. For negative discriminants, quadratic forms
+are triples $(a,b,c)$ representing the form $ax^2+bxy+cy^2$. Such a form will
+be printed as, and can be created by, \kbd{Qfb($a$,$b$,$c$)}.
+
+Such forms can be multiplied, divided, powered as many PARI objects using
+the usual operations, and they can also be reduced using the function
+\kbd{qfbred} (it is not the purpose of this tutorial to explain what all
+these things mean). In addition, Shanks's NUCOMP algorithm has been
+implemented (functions \kbd{qfbnucomp} and \kbd{qfbnupow}), and this is
+usually a little faster.
+
+Finally, you have at your disposal the functions \kbd{qfbclassno} which
+(\emph{usually}) gives the class number, the function \kbd{qfbhclassno}
+which gives the Hurwitz class number, and the much more sophisticated
+\kbd{quadclassunit} function which gives the class number and class group
+structure.
+
+Let us see examples of all this at work.
+
+Type \kbd{qfbclassno(-10007)}. \kbd{gp} tells us that the result is 77. However,
+you may have noticed in the explanation above that the result is only
+\emph{usually} correct. This is because the implementers of the algorithm
+have been lazy and have not put the complete Shanks algorithm into PARI,
+causing it to fail in certain very rare cases. In practice, it is almost
+always correct, and the much more powerful \kbd{quadclassunit} program, which
+\emph{is} complete (at least for fundamental discriminants) can give
+confirmation; but now, under the Generalized Riemann Hypothesis!
+
+So we may be a little suspicious of this class number. Let us check it.
+First, we need to find a quadratic form of discriminant $-10007$. Since this
+discriminant is congruent to 1 modulo 8, we know that there is an ideal of
+norm equal to 2, i.e.~a binary quadratic form $(a,b,c)$ with $a=2$. To
+compute it we type \kbd{f = qfbprimeform(-10007, 2)}. OK, now we have a form.
+If the class number is correct, the very least is that this form raised to
+the power 77 should equal the identity. Type \kbd{f\pow 77}. We get a form
+starting with 1, i.e.~the identity. Raising \kbd{f} to the powers 11 and 7
+does not give the identity, thus we now know that the order of \kbd{f} is
+exactly 77, hence the class number is a multiple of 77. But how can we be
+sure that it is exactly 77 and not a proper multiple? Well, type
+\bprog
+  sqrt(10007)/Pi * prodeuler(p=2,500, 1./(1 - kronecker(-10007,p)/p))
+ at eprog\noindent
+This is nothing else than an approximation to the Dirichlet class number
+formula. The function \kbd{kronecker} is the Kronecker symbol, in this case
+simply the Legendre symbol. Note also that we have written \kbd{1./(1 - \dots)}
+with a dot after the first 1. Otherwise, PARI may want to compute the whole
+thing as a rational number, which would be terribly long and useless. In fact
+PARI does no such thing in this particular case (\kbd{prodeuler} is always
+computed as a real number), but you never know. Better safe than sorry!
+
+We find 77.77, pretty close to 77, so things seem in order. Explicit bounds
+on the prime limit to be used in the Euler product can be given which make
+the above reasoning rigorous.
+
+Let us try the same thing with $D=-3299$. \kbd{qfbclassno} and the Euler
+product convince us that the class number must be 27. However, we get stuck
+when we try to prove this in the simple-minded way above. Indeed, we type
+\kbd{f = qfbprimeform(-3299, 3)} (2 is not the norm of a prime ideal but
+3 is), and we see that \kbd{f} raised to the power 9 is equal to the identity.
+This is the case for any other quadratic form we choose. So we suspect that the
+class group is not cyclic. Indeed, if we list all 9 distinct powers of \kbd{f},
+we see that \kbd{qfbprimeform(-3299, 5)} is not on the list, although its cube
+is as it must. This implies that the class group is probably equal to a
+product of a cyclic group of order 9 by a cyclic group of order 3. The Euler
+product plus explicit bounds prove this.
+
+Another way to check it is to use the \kbd{quadclassunit} function by typing
+for example
+\bprog
+  quadclassunit(-3299)
+ at eprog\noindent
+Note that this function cheats a little and could still give a wrong answer,
+even assuming GRH: the forms given could generate a strict subgroup of the
+class group. If we want to use proven bounds under GRH, we have to type
+\bprog
+  quadclassunit(-3299,,[1,6])
+ at eprog\noindent
+The double comma \kbd{,,} is not a typo, it means we omit an optional second
+argument. As we want to use the optional \emph{third} argument, we have to
+indicate to \kbd{gp} we skipped this one.
+
+Now, if we believe in GRH, the class group is as we thought (see Chapter 3
+for a complete description of this function).
+
+  Note that using the even more general function \kbd{bnfinit} (which handles
+general number fields and gives more complicated results), we could
+\emph{certify} this result, i.e~remove the GRH assumption. Let's do it, type
+\bprog
+  bnf = bnfinit(x^2 + 3299); bnfcertify(bnf)
+ at eprog
+
+  A non-zero result (here 1) means that everything is ok. Good, but what did
+we certify after all? Let's have a look at this \kbd{bnf} (just type it!).
+Enlightening, isn't it? Recall that the \kbd{init} functions (we've already
+seen \kbd{ellinit}) store all kind of technical information which you
+certainly don't care about, but which will be put to good use by higher level
+functions. That's why \kbd{bnfcertify} could not be used on the output of
+\kbd{quadclassunit}: it needs much more data.
+
+  To extract sensible information from such complicated objects, you must use
+one of the many \emph{member functions} (remember: \kbd{?.} to get a complete
+list). In this case \kbd{bnf.clgp} which extracts the class group structure.
+This is much better. Type \kbd{\%.no} to check that this leading 27 is indeed
+what we think it is and not some stupid technical parameter. Note that
+\kbd{bnf.clgp.no} would work just as well, or even \kbd{bnf.no}!
+
+As a last check, we can request a relative equation for the Hilbert class
+field of $\Q(\sqrt{-3299})$: type \kbd{quadhilbert(-3299)}. It is indeed of
+degree 27 so everything fits together.
+
+\medskip
+%
+Working in real quadratic fields instead of complex ones, i.e.~with $D>0$, is
+not very different.
+
+The same \kbd{quadgen} function is used to create elements. Ideals are again
+represented by binary quadratic forms $(a,b,c)$, this time indefinite. However,
+the Archimedean valuations of the number field start to come into play, hence
+in fact quadratic forms with positive discriminant will be represented as a
+quadruplet $(a,b,c,d)$ where the quadratic form itself is $ax^2+bxy+cy^2$
+with $a$, $b$ and $c$ integral, and $d\in \R$ is a ``distance''
+component, as defined by Shanks and Lenstra.
+
+To create such forms, one uses the same function as for definite ones, but
+you can add a fourth (optional) argument to initialize the distance:
+\kbd{q = Qfb($a$, $b$, $c$, $d$)}.
+If the discriminant of \kbd{poldisc(q)} is negative, $d$ is silently
+discarded. If you omit it, this component is set to \kbd{0.} (i.e.~a real
+zero to the current precision).
+
+Again these forms can be multiplied, divided, powered, and they can be
+reduced using \kbd{qfbred}. This function is in fact a succession of
+elementary reduction steps corresponding essentially to a continued fraction
+expansion, and a single one of these steps can be achieved by adding an
+(optional) flag to the arguments of \kbd{qfbred}. Since handling the
+fourth component $d$ usually involves computing expensive logarithms, the
+same flag may be used to ignore the fourth component. Finally, it is
+sometimes useful to operate on forms of positive discriminant without
+performing any reduction (this is useless in the negative case), the
+functions \kbd{qfbcompraw} and \kbd{qfbpowraw} do exactly that.
+
+Again, the function \kbd{qfbprimeform} gives a prime form, but the form which
+is given corresponds to an ideal of prime norm which is usually not reduced.
+If desired, it can be reduced using \kbd{qfbred}.
+
+Finally, you still have at your disposal the function \kbd{qfbclassno} which
+gives the class number (this time \emph{guaranteed} correct),
+\kbd{quadregulator} which gives the regulator, and the much more sophisticated
+\kbd{quadclassunit} giving the class group's structure and its generators,
+as well as the regulator. The \kbd{qfbclassno} and \kbd{quadregulator}
+functions use an algorithm which is $O(\sqrt D)$, hence become very slow for
+discriminants of more than 10 digits. \kbd{quadclassunit} can be used on a
+much larger range.
+
+Let us see examples of all this at work and learn some little known number
+theory at the same time. First of all, type
+\bprog
+  d = 3 * 3299; qfbclassno(d)
+ at eprog\noindent We see that the class number is 3. (We know
+in advance that it must be divisible by 3 from the \kbd{d = -3299} case above
+and Scholz's mirror theorem.) Let us create a form by typing
+\bprog
+  f = qfbred(qfbprimeform(d,2), 2)
+ at eprog\noindent
+(the last 2 tells \kbd{qfbred} to ignore the Archimedean component). This
+gives us a prime ideal of norm equal to 2. Is this ideal principal? Well, one
+way to check this, which is not the most efficient but will suffice for now,
+is to look at the complete cycle of reduced forms equivalent to \kbd{f}. Type
+\bprog
+ g = f; for(i=1,20, g = qfbred(g, 3); print(g))
+ at eprog\noindent
+(this time the 3 means to do a single reduction step, still not using
+Shanks's distance). We see that we come back to the form \kbd{f} without
+having the principal form (starting with $\pm1$) in the cycle, so the ideal
+corresponding to \kbd{f} is not principal.
+
+Since the class number is equal to 3, we know however that \kbd{f\pow 3} will
+be a principal ideal $\alpha\Z_K$. How do we find $\alpha$? For this, type
+\bprog
+  f3 = qfbpowraw(f, 3)
+ at eprog
+This computes the cube of \kbd{f}, without
+reducing it. Hence it corresponds to an ideal of norm equal to $8=2^3$, so we
+already know that the norm of $\alpha$ is equal to $\pm8$. We need more
+information, and this will be given by the fourth component of the form.
+Reduce your form until you reach the unit form (you will have to type
+\kbd{qfbred(\%,~1)} exactly 6 times), then extract the Archimedean component,
+say $c$. By definition of this distance, we know that
+$${\alpha\over{\sigma(\alpha)}}=\pm e^{2c},$$
+where $\sigma$ denotes real conjugation in our quadratic field. This can be
+automated:
+\bprog
+  q = f3;
+  while(abs(component(q,1)) != 1, print(q); q = qfbred(q, 1))
+  c = component(q,4);
+ at eprog\noindent
+Thus, if we type
+\bprog
+  a = sqrt(8 * exp(2*c))
+  sa = 8 / a
+ at eprog\noindent
+we know that up to sign, \kbd{a} and \kbd{sa} are
+numerical approximations of $\alpha$ and $\sigma(\alpha)$. Of course,
+$\alpha$ can always be chosen to be positive, and a quick numerical check
+shows that the difference of \kbd{a} and \kbd{sa} is close to an integer, and
+not the sum, so that in fact the norm of $\alpha$ is equal to $-8$ and the
+numerical approximation to $\sigma(\alpha)$ is \kbd{$-$sa}. Thus we type
+\bprog
+  p = x^2 - round(a-sa)*x - 8
+ at eprog\noindent
+and this is the characteristic polynomial of $\alpha$. We can check that the
+discriminant of this polynomial is a square multiple of \kbd{d}, so $\alpha$
+is indeed in our field. More precisely, solving for $\alpha$ and using the
+numerical approximation that we have to resolve the sign ambiguity in the
+square root, we get explicitly $\alpha=(15221+153\sqrt d)/2$. Note that this
+can also be done automatically using the functions \kbd{polred} and
+\kbd{modreverse}, as we will see later in the general number field case, or
+by solving a system of 2 linear equations in 2 variables. (Exercise: now that
+we have $\alpha$, check that it is indeed a generator of the ideal
+corresponding to the form \kbd{f3}.)
+
+\medskip Let us now play a little with cycles. Type
+\bprog
+  D = 10^7 + 1
+  quadclassunit(D)
+ at eprog\noindent
+We get as a result a 5-component vector, which tells us that (under GRH) the
+class number is equal to 1, and the regulator is approximately
+equal to $2641.5$. You may certify this with
+\bprog
+  bnf = bnfinit(x^2 - D, 1);  \\ @com insist on finding fundamental unit
+  bnfcertify(bnf);
+ at eprog\noindent
+although it's a little inefficient. Indeed \kbd{bnfcertify} needs the
+fundamental unit which is so large that \kbd{bnfinit} will have a hard time
+computing it: it needs about $R/\log(10)\approx 1147$ digits of precision!
+(So that it would have given up had we not inserted the flag $1$.)
+See \kbd{bnf.fu}. On the other hand, you can try \kbd{quadunit(D)}.
+Impressive, isn't it? (You can check that its logarithm is indeed equal to
+the regulator.)
+
+Now just as an example, let's assume that we want the regulator to 500
+decimals, say. (Without cheating and computing the fundamental unit exactly
+first!) I claim that simply from the crude approximation above, this can be
+computed with no effort.
+
+This time, we want to start with the unit form. Type:
+\bprog
+  u = qfbred(qfbprimeform(D, 1), 2)
+ at eprog\noindent
+We use the function \kbd{qfbred} with no distance since we want the initial
+distance to be equal to~0. Now type  \kbd{f = qfbred(u, 1)}. This is the
+first form encountered along the principal cycle. For the moment, keep the
+precision low, for example the initial default precision. The distance from
+the identity of \kbd{f} is around 4.253. Very crudely, since we want a
+distance of $2641.5$, this should be encountered approximately at
+$2641.5/4.253=621$ times the distance of \kbd{f}. Hence, as a first try, we
+type \kbd{f\pow 621}. Oops, we overshot, since the distance is now $3173.02$.
+Now we can refine our initial estimate and believe that we should be close to
+the correct distance if we raise \kbd{f} to the power $621*2641.5/3173$ which
+is close to $517$. Now if we compute \kbd{f\pow 517} we hit the principal
+form right on the dot. Note that this is not a lucky accident: we will always
+land extremely close to the correct target using this method, and usually at
+most one reduction correction step is necessary. Of course, only the distance
+component can tell us where we are along the cycle.
+
+Up to now, we have only worked to low precision. The goal was to obtain this
+unknown integer $517$. Note that this number has absolutely no mathematical
+significance: indeed the notion of reduction of a form with positive
+discriminant is not well defined since there are usually many reduced forms
+equivalent to a given form. However, when PARI makes its computations, the
+specific order and reductions that it performs are dictated entirely by the
+coefficients of the quadratic form itself, and not by the distance component,
+hence the precision used has no effect.
+
+Hence we now start again by setting the precision to (for example) 500,
+we retype the definition of \kbd{u} (why is this necessary?), and then
+\kbd{qfbred(u, 1)\pow 517}. Of course we know in advance that we land on the
+unit form, and the fourth component gives us the regulator to 500 decimal
+places with no effort at all.
+
+In a similar way, we could obtain the so-called \emph{compact representation}
+of the fundamental unit itself, or $p$-adic regulators. I leave this as
+exercises for the interested reader.
+
+You can try the \kbd{quadhilbert} function on that field but, since the class
+number is $1$, the result won't be that exciting. If you try it on our
+preceding example ($3*3299$) it should take about 2 seconds.
+\medskip
+
+Time for a coffee break?
+
+\section{Working in General Number Fields}
+\subsec{Elements}
+
+The situation here is of course more difficult. First of all, remembering
+what we did with elliptic curves, we need to initialize data linked to our
+base number field, with something more serious than \kbd{quadgen}. For
+example assume that we want to work in the number field $K$ defined by one of
+the roots of the equation $x^4+24x^2+585x+1791=0$. This is done by typing
+\bprog
+  T = x^4 + 24*x^2 + 585*x + 1791
+  nf = nfinit(T)
+ at eprog\noindent
+We get an \kbd{nf} structure but, thanks to member functions, we do not need
+to know anything about it. If you type \kbd{nf.pol}, you will get the
+polynomial \kbd{T} which you just input. \kbd{nf.sign} yields the signature
+$(r_1,r_2)$ of the field, \kbd{nf.disc} the field discriminant, \kbd{nf.zk}
+an integral basis, etc\dots.
+
+The integral basis is expressed in terms of a generic root \kbd{x} of \kbd{T}
+and we notice it's very far from being a power integral basis, which is a
+little strange for such a small field. Hum, let's check that: \kbd{poldisc(T)}?
+Ooops, small wonder we had such denominators, the index of the power order
+$\Z[x]/(T)$ in the maximal order $\Z_K$ is, well,
+\kbd{nf.index}. Note that this is also given by
+\bprog
+  sqrtint(poldisc(nf.pol) / nf.disc)
+ at eprog\noindent
+Anyway, that's $3087$, we don't want to work with such a badly skewed
+polynomial! So, we type
+\bprog
+  P = polred(T)
+ at eprog\noindent
+We see from the third component that the
+polynomial $x^4-x^3-21x^2+17x+133$ defines the same field with much smaller
+coefficients, so type
+\bprog
+  A = P[3]
+ at eprog\noindent
+The \kbd{polred} function usually gives a simpler polynomial, and also
+sometimes some information on the existence of subfields. For example in this
+case, the second component of \kbd{polred} tells us that the field defined by
+$x^2-x+1=0$, i.e.~the field generated by the cube roots of unity, is a subfield
+of~$K$. Note this is incidental information and that the list of subfields
+found in this way is usually far from complete. To get the complete list, one
+uses \kbd{nfsubfields} (we shall do that later on).
+
+  Type \kbd{poldisc(A)}, this is much better, but maybe not optimal yet
+(the index is still $7$). Type \kbd{polredabs(A)} (the \kbd{abs} stands for
+absolute). Since it seems that we won't get anything better, we'll stick with
+\kbd{A} (note however that \kbd{polredabs} finds a smallest generating
+polynomial with respect to a bizarre norm which ensures that the index will
+be small, but not necessarily minimal). In fact, had you typed
+\kbd{nfinit(T, 3)}, \kbd{nfinit} would first have tried to find a good
+polynomial defining the same field (i.e.~one with small index) before
+proceeding.
+
+  It's not too late, let's redefine our number field:
+\bprog
+  NF = nfinit(nf, 3)
+ at eprog\noindent
+The output is a two-component vector. The first component is the new
+\kbd{nf}: type
+\bprog
+  nf = NF[1];
+ at eprog\noindent
+If you type \kbd{nf.pol}, you notice that \kbd{gp} indeed replaced your bad
+polynomial \kbd{T} by a much better one, which happens to be \kbd{A}. (Small
+wonder, \kbd{nfinit} internally called \kbd{polredabs}.) The second component
+enables you to switch conveniently to our new polynomial.
+
+Namely, call $\theta$ a root of our initial polynomial \kbd{T}, and $\alpha$
+a root of the one that \kbd{polred} has found, namely \kbd{A}. These are
+algebraic numbers, and as already mentioned are represented as polmods. For
+example, in our special case $\theta$ and $\alpha$ are equal to the polmods
+\bprog
+  THETA = Mod(x, x^4 + 24*x^2 + 585*x + 1791)
+  ALPHA = Mod(x, x^4 - x^3 - 21*x^2 + 17*x + 133)
+ at eprog\noindent respectively. Here we are considering only the algebraic
+aspect, and hence ignore completely \emph{which} root $\theta$ or $\alpha$ is
+chosen.
+
+Now you may have a number of elements of your number field which are
+expressed as polmods with respect to your old polynomial, i.e.~as polynomials
+in $\theta$. Since we are now going to work with $\alpha$ instead, it is
+necessary to convert these numbers to a representation using $\alpha$. This
+is what the second component of \kbd{NF} is for: type \kbd{C = NF[2]}, you get
+\bprog
+  Mod(-10/7*x^3 + 43/7*x^2 + 73/7*x - 64, x^4 - x^3 - 21*x^2 + 17*x + 133)
+ at eprog\noindent
+meaning that $\theta =
+-\dfrac{10}{7}\alpha^3+\dfrac{43}{7}\alpha^2+\dfrac{73}{7}\alpha-64$, and hence the conversion from a
+polynomial in $\theta$ to one in $\alpha$ is easy, using \kbd{subst}. (We
+could get this polynomial from \kbd{polred} as well, try \kbd{polred(T, 2)}.)
+If we want the reverse, i.e.~to go back from a representation in $\alpha$ to
+a representation in $\theta$, we use the function \kbd{modreverse} on this
+polmod \kbd{C}. Try it. The result has a big denominator (1029) essentially
+because our initial polynomial \kbd{T} was so bad. By the way, to get that
+1029,
+you should type \kbd{denominator(content(C))}. Trying \kbd{denominator} by
+itself would not work since the denominator of a polynomial is defined to be
+1 (and its numerator is itself). The reason for this is that we think of a
+polynomial as a special case of a rational function.\smallskip
+
+From now on, we forget about \kbd{T}, and use only the polynomial
+\kbd{A} defining $\alpha$, and the components of the vector \kbd{nf} which
+gives information on our number field $K$. Type
+\bprog
+  u = Mod(x^3 - 5*x^2 - 8*x + 56, A) / 7
+ at eprog\noindent
+This is an element in $K$. There are three equivalent
+representations for number field elements: polmod, polynomial, and column
+vector giving a decomposition in the integral basis \kbd{nf.zk} (\emph{not} on
+the power basis $(1,x,x^2,\dots)$). All three are equally valid when the
+number field is understood (is given as first argument to the function).
+You will be able to use any one of them as long as the function you call
+requires an \kbd{nf} argument as well. However, most PARI functions will
+return elements as column vectors. It's an important feature of number
+theoretic functions that, although they may have a preferred format for
+input, they will accept a wealth of other different formats. We already saw
+this for \kbd{nfinit} which accepts either a polynomial or an \kbd{nf}. It
+will be true for ideals, congruence subgroups, etc.
+
+  Let's stick with elements for the time being. How does one go from one
+representation to the other? Between polynomials and polmods, it's easy:
+\kbd{lift} and \kbd{Mod} will do the job. Next, from polmods/polynomials to
+column vectors: type \kbd{v = nfalgtobasis(nf, u)}. So $\kbd{u} = \alpha^3-
+\alpha^2 - \alpha + 8$, right? Wrong! The coordinates of \kbd{u} are given
+with respect to the \emph{integral basis}, not the power basis
+$(1,\alpha,\alpha^2,\alpha^3)$ (and they don't coincide, type \kbd{nf.zk} if
+you forgot what the integral basis looked like). As a polynomial in $\alpha$,
+we simply have $\kbd{u} = {1\over7}(\alpha^3 - 5\alpha^2-8\alpha+56)$, which
+is trivially deduced from the original polmod representation!
+
+Of course \kbd{v = nfalgtobasis(nf, lift(u))} would work equally well. Indeed
+we don't need the polmod information since \kbd{nf} already provides the
+defining polynomial. To go back to polmod representation, use
+\kbd{nfbasistoalg(nf, v)}. Notice that \kbd{u} is an algebraic integer since
+\kbd{v} has integer coordinates (try \kbd{denominator(v) == 1}, which is of
+course overkill here, but not so in a program).
+
+Let's try this out. We may for instance compute \kbd{u\pow 3}. Try it. Or, we
+can type \kbd{1/u}. Better yet, if we want to know the norm from $K$ to $\Q$
+of \kbd{u}, we type \kbd{norm(u)} (what else?); \kbd{trace(u)} works as well.
+Notice that none of this would work on polynomials or column vectors since
+you don't have the opportunity to supply \kbd{nf}! But we could use
+\kbd{nfeltpow(nf,u,3)}, \kbd{nfeltdiv(nf,1,u)} (or \kbd{nfeltpow(nf,u,-1)})
+which would work whatever representation was chosen. Of course,
+there is also an \kbd{nfeltnorm} function (and \kbd{nfelttrace} as well).
+You can also consider $(u)$ as a principal ideal, and just type
+\bprog
+  idealnorm(nf,u)
+ at eprog\noindent
+Of course, in this way, we lose the \emph{sign} information. We will talk
+about ideals later on.
+
+  If we want all the symmetric functions of \kbd{u} and not only the norm, we
+type \kbd{charpoly(u)}. Note that this gives the characteristic polynomial of
+\kbd{u}, and not in general the minimal polynomial.
+\misctitle{Exercise} How does one get the minimal polynomial from this?
+Find a simpler expression for \kbd{u}. \smallskip
+
+  Now let's work on the field itself. The \kbd{nfinit} command already
+gave us some information. The field is totally complex (its signature
+\kbd{nf.sign} is $[0,2]$), its discriminant \kbd{nf.disc} is $18981$ and
+\kbd{nf.zk} is an integral basis. The Galois group of its Galois closure
+can be obtained by typing \kbd{polgalois(A)}. The answer (\kbd{[8,-1,1]}, or
+\kbd{[8,-1,1,"D(4)"]} if the \kbd{galdata} package is installed) shows that
+it is equal to $D_4$, the dihedral group with 8 elements, i.e.~the group of
+symmetries of a square.
+
+This implies that the field is ``partially Galois'', i.e.~that there exists
+at least one non-trivial field isomorphism which fixes $K$, exactly one in
+this case. Type \kbd{nfgaloisconj(nf)}. The result tells us that, apart from
+the trivial automorphism, the map
+$$\alpha \mapsto {1\over7}(-\alpha^3+5\alpha^2+\alpha-49)$$
+is the only field automorphism.
+\bprog
+  nfgaloisconj(nf);
+  s = Mod(%[2], A)
+  charpoly(s)
+ at eprog\noindent
+and we obtain \kbd{A} once again.
+
+Let us check that \kbd{s} is of order 2: \kbd{subst(lift(s), x, s)}. It is. We
+may express it as a matrix:
+\bprog
+  w = Vec( matid(4) ) \\@com canonical basis
+  v = vector(#w, i, nfgaloisapply(nf, s, w[i]))
+  M = Mat(v)
+ at eprog\noindent
+The vector \kbd{v} contains the images of the integral basis elements (as
+column vectors). The last statement concatenates them into a square matrix.
+So, \kbd{M} gives the action of \kbd{s} on the integral basis. Let's check
+\kbd{M\pow2}. That's the identity all right.
+
+The fixed field of this automorphism is going to be the only non-trivial
+subfield of $K$. I seem to recall that \kbd{polred} told us this was the
+third cyclotomic field. Let's check this: type \kbd{nfsubfields(nf)}. Indeed,
+there's a quadratic subfield, but it's given by \kbd{T = x\pow 2 + 22*x + 133
+} and I don't recognize it. But \kbd{nfisisom(T, polcyclo(3))} indeed tells
+us that the fields $\Q[x]/(T)$ and $\Q[x]/(x^2+x+1)$ are isomorphic.
+(In fact, \kbd{polred(T)} would tell us the same, but does not correspond to
+a foolproof test: \kbd{polred} could have returned some other polynomials.)
+
+We may also check that \kbd{k = matker(M-1)} is two-dimensional, then \kbd{z
+= nfbasistoalg(nf, k[,2])} generates the quadratic subfield. Notice that 1,
+\kbd{z} and \kbd{u} are $\Q$-linearly dependent, and in fact $\Z$-linearly as
+well. Exercise: how would you check these two assertions in general? (Answer:
+\kbd{concat}, then respectively \kbd{matrank} or \kbd{matkerint} (or
+\kbd{qflll})). \kbd{z = charpoly(z)}, \kbd{z = gcd(z,z')} and \kbd{polred(z)}
+tell us that we found back the same subfield again (as we ought to!).
+
+Final check: type \kbd{nfrootsof1(nf)}. Again we find that $K$ contains
+a cube root of unity, since the torsion subgroup of its unit group
+has order 6. The given generator happens to be equal to \kbd{u}.
+
+\misctitle{Additional comment} (you are no longer supposed to skip this,
+but do as you wish):
+
+Before working with ideals, let us note one more thing. The main part of the
+work of \kbd{polred} or \kbd{nfinit}$(T)$ is to compute an integral basis,
+i.e.~a $\Z$-basis of the maximal order $\Z_K$ of $K$. This implies factoring
+the discriminant of the polynomial $T$, which is often not feasible. The
+situation may be improved in many ways:
+
+1) First, it is often the case that our number field is of quite a special
+type. For example, one may know in advance some prime divisors of the
+discriminant. Hence we can ``help'' PARI by giving it that information. More
+precisely, we can use the function \kbd{addprimes} to inform PARI to keep on
+eye for these prime numbers. Do it only for big primes ! (Say, larger than
+\kbd{primelimit}.)
+
+2) The second way in which the situation may be improved is that often we do
+not need the complete information on the maximal order, but only require that
+the order be $p$-maximal for a certain number of primes $p$ --- but then, we
+may not be able to use functions which require a genuine \kbd{nf}. The
+function \kbd{nfbasis} specifically computes the integral basis and is not
+much quicker than \kbd{nfinit} so is not very useful in its standard use. But
+we can optionnally provide a list of primes: this returns a basis of an order
+which is $p$-maximal at the given primes. For example coming back to our
+initial polynomial $T$, the discriminant of the polynomial is
+$3^7\cdot7^6\cdot19\cdot37$. If we only want a $7$-maximal order, we simply
+type
+\bprog
+  nfbasis([T, [7]])
+ at eprog\noindent Of course, \kbd{nfbasis([T, [2,3,5,7]])} would return an
+order which is maximal at $2,3,5,7$. A variant offers a nice generalization:
+\bprog
+  nfbasis([T, 10^5])
+ at eprog\noindent will return an order which is maximal at all primes less than
+$10^5$.
+
+3) Building on the previous points, \emph{if} the field discriminant is
+$y$-smooth (never mind the polynomial discriminant), up to a few big primes
+known to \kbd{addprimes}, then \kbd{bas = nfbasis(T, y)} returns a basis for
+the maximal order! We can then input the resulting basis to \kbd{nfinit}, as
+\kbd{nfinit([T, bas])}. Better: the $[T, \var{listP}]$ format can be
+directly used with nfinit, where \var{listP} specifies a finite list of
+primes in one of the above ways (explicit list or primes up to some bound),
+and the result can be unconditionnally certified, independently of the
+\var{listP} parameter:
+\bprog
+  T = polcompositum(x^7-2, polcyclo(5))[1];
+  K = nfinit( [T, [2,5,7]] );
+  nfcertify(K)
+ at eprog\noindent The output is a list of composite integers whose complete
+factorization must be computed in order to certify the result (which may be
+very hard, hence is not done on the spot). When the list is empty, as here,
+the result is unconditionnal
+
+\kbd{nfcertify(nf)}
+
+\subsec{Ideals}
+
+We now want to work with ideals and not only
+with elements. An ideal can be represented in many different ways. First, an
+element of the field (in any of the various guises seen above) will be
+considered as a principal ideal. Then the standard representation is a
+square matrix giving the Hermite Normal Form (HNF) of a $\Z$-basis of the
+ideal expressed on the integral basis \kbd{nf.zk}. Standard means that most
+ideal related functions will use this representation for their output.
+
+Prime ideals can be represented in a special form as well (see
+\kbd{idealprimedec}) and all ideal-related functions will accept them. On the
+other hand, the function \kbd{idealtwoelt} can be used to find a two-element
+$\Z_K$-basis of a given ideal (as $a\Z_K + b\Z_K$, where $a$ and $b$ belong
+to $K$), but this is \emph{not} a valid representation for an ideal under
+\kbd{gp}, and most functions will choke on it (or worse, take it for
+something else and output a meaningless result). To be able to use such an
+ideal, you will first have to convert it to HNF form.
+
+Whereas it's very easy to go to HNF form (use \kbd{idealhnf(nf,id)} for valid
+ideals, or \kbd{idealhnf(nf,a,b)} for a two-element representation as above),
+it's a much more complicated problem to check whether an ideal is principal
+and find a generator. In fact an \kbd{nf} does not contain enough data for
+this particular task. We'll need a Buchmann Number Field, or \kbd{bnf}, for
+that. In particular, we need the class group and fundamental units, at least
+in some approximate form. More on this later (which will trivialize the end
+of the present section).\smallskip
+
+Let us keep our number field $K$ as above and its \kbd{nf} structure. Type
+\bprog
+  P = idealprimedec(nf,7)
+ at eprog\noindent
+This gives the decomposition of the prime number 7 into prime ideals. We have
+chosen 7 because it divides \kbd{nf.index} (in fact, is equal to it), hence
+is the most difficult case to treat.
+
+The result is a vector with 4 components, showing that 7 is totally split in
+the field $K$ into prime ideals of norm 7 (you can check:
+\kbd{idealnorm(nf,P[1])}). Let us take one of these ideals, say the first, so
+type
+\bprog
+  pr = P[1]
+ at eprog
+We obtain its inertia and residue degree as \kbd{pr.e}
+and \kbd{pr.f}, and its two generators as \kbd{pr.gen}. One of them is
+$\kbd{pr.p} = 7$, and the other is guaranteed to have valuation $1$ at
+\kbd{pr}. What is the Hermite Normal Form of this ideal? No problem:
+\bprog
+  idealhnf(nf,pr)
+ at eprog\noindent and we have the desired HNF. Let's now perform ideal
+operations. For example type
+\bprog
+  idealmul(nf, pr, idealmul(nf, pr,pr))
+ at eprog\noindent or more simply
+\bprog
+  pr3 = idealpow(nf, pr,3)
+ at eprog\noindent
+to get the cube of the ideal \kbd{pr}. Since the norm of this ideal is equal
+to $343=7^3$, to check that it is really the cube of \kbd{pr} and not of
+other ideals above 7, we can type
+\bprog
+  for(i=1, #P, print( idealval(nf, pr3, P[i]) ))
+ at eprog\noindent
+and we see that the valuation at \kbd{pr} is equal to 3, while the others are
+equal to zero. We could see this as well from \kbd{idealfactor(nf, pr3)}.
+
+Let us now work in the class group ``by hand'' (we shall see simpler ways
+later). We shall work with \emph{extended ideals}: an extended ideal is a pair
+\kbd{[A, t]}, where $A$ is an ordinary ideal as above, and $t$ a number field
+element; this pair represents the ideal $(t) A$.
+\bprog
+  id3 = [pr3, 1]
+  r0 = idealred(nf, id3)
+ at eprog\noindent
+The input \kbd{id3} is an extended ideal: pr3 together with 1 (trivial
+factorization). The new extended ideal \kbd{r0} is equal to the old one,
+in the sense that the products $(t)A$ are the same. It contains a ``reduced''
+ideal equivalent to \kbd{pr3} (modulo the principal ideals), and a generator
+of the principal ideal that was factored out.
+
+Now, just for fun type
+\bprog
+  r = r0; for(i=1,3, r = idealred(nf,r, [1,5]); print(r))
+ at eprog\noindent
+The ideals in the third \kbd{r} and initial \kbd{r0} are equal, say
+$(t) A = (t_0) A$: this means we
+have found a unit $(t_0/t)$ in our field, and it is easy to extract this unit
+given the extended component:
+\bprog
+  t0 = r0[2]; t = r[2];
+  u = nfeltdiv(nf, t0, t)
+  u = nfbasistoalg(nf, u)
+ at eprog\noindent The last line recovers the unit as an algebraic number.
+Type
+\bprog
+  ch = charpoly(u)
+ at eprog\noindent
+and we obtain the characteristic polynomial \kbd{ch} of $u$ again. (Whose
+constant coefficient is $1$, hence $u$ is indeed a unit.)
+
+There is of course no reason for $u$ to be a fundamental unit. Let us see if
+it is a square. Type
+\bprog
+  F = factor(subst(ch, x, x^2))
+ at eprog\noindent
+We see that \kbd{ch(x\pow2)} is a product of 2 polynomials of degree 4, hence
+$u$ is a square. (Why?) We now want to find its square root. A simple method
+is as follows:
+\bprog
+  NF = subst(nf,x,y);
+  r = F[1,1] % (x^2 - nfbasistoalg(NF, u))
+ at eprog\noindent
+to find the remainder of the characteristic polynomial of \kbd{u2} divided by
+\kbd{x\pow 2 - $u$}. This is a polynomial of degree 1 in \kbd{x}, with polmod
+coefficients, and we know that \kbd{u2}, being a root of both polynomials,
+is the root of \kbd{r}, hence can be obtained by typing
+\bprog
+  u2 = -polcoeff(r,0) / polcoeff(r,1)
+ at eprog\noindent There is an important technicality in the above: why did we
+need to substitute \kbd{NF} to \kbd{nf}? The reason is that data related to
+\kbd{nf} is given in terms of the variable \kbd{x}, seen modulo \kbd{nf.pol};
+but we need \kbd{x} as a free variable for our polynomial divisions. Hence
+the substitution of \kbd{x} by \kbd{y} in our \kbd{nf} data.
+
+The most natural method is to try directly
+\bprog
+  nffactor(nf, y^2 - u)
+ at eprog\noindent
+Except that this won't work for the same technical reason as above: the main
+variable of the polynomial to be factored must have \emph{higher} priority
+than the number field variable. This won't be possible here since \kbd{nf}
+was defined using the variable \kbd{x} which has the highest possible
+priority. So we need to substitute variables around:
+\bprog
+  nffactor(NF, x^2 - nfbasistoalg(NF, subst(lift(u),x,y)))
+ at eprog\noindent
+(Of course, with better planning, we would just have defined \kbd{nf} in
+terms of the \kbd{'y} variable, to avoid all these substitutions.)
+\smallskip
+
+A much simpler approach is to consider the above as instances of a
+\emph{discrete logarithm} problem, where we want to express some elements an
+abelian group (of finite type) in terms of explicitly given generators, and
+transfer all computations from abstract groups like $\text{Cl}(K)$ and
+$\Z_K^*$ to products of simpler groups like $\Z^n$ or $\Z/d\Z$. We shall do
+exactly that in the next section.
+
+Before that, let us mention another famous (but in fact, simpler)
+\emph{discrete logarithm} problem, namely the one associated to the
+invertible elements modulo an ideal: $(\Z_K / I)^*$. Just use \kbd{idealstar}
+(this is an \kbd{init} function) and \kbd{ideallog}.
+
+Many more functions on ideals are available. We mention here the complete
+list, referring to Chapter 3 for detailed explanations:
+
+\kbd{idealadd}, \kbd{idealaddtoone}, \kbd{idealappr}, \kbd{idealchinese},
+\kbd{idealcoprime}, \kbd{idealdiv}, \kbd{idealfactor}, \kbd{idealhnf},
+\kbd{idealintersect}, \kbd{idealinv}, \kbd{ideallist}, \kbd{ideallog},
+\kbd{idealmin}, \kbd{idealmul}, \kbd{idealnorm}, \kbd{idealpow},
+\kbd{idealprimedec}, \kbd{idealred},
+\kbd{idealstar}, \kbd{idealtwoelt}, \kbd{idealval},
+\kbd{nfisideal}.
+
+We suggest you play with these to get a feel for the algebraic number theory
+package. Remember that when a matrix (usually in HNF) is output, it is always
+a $\Z$-basis of the result expressed on the \emph{integral basis} \kbd{nf.zk}
+of the number field, which is usually \emph{not} a power basis.
+
+\subsec{Class groups and units, \kbd{bnf}}
+
+Apart from the above functions you have at your disposal the powerful
+function \kbd{bnfinit}, which initializes a \kbd{bnf} structure, i.e.~a
+number field with all its invariants (including class group and units), and
+enough technical data to solve discrete logarithm problems in the class and
+unit groups.
+
+First type \kbd{setrand(1)}: this resets the random seed (to make sure we
+and you get the exact same results). Now type
+\bprog
+  bnf = bnfinit(NF);
+ at eprog\noindent
+where \kbd{NF} is the same number field as before. You do not want to see the
+output clutter a number of screens so don't forget the semi-colon. (Well if
+you insist, it is about three screenful in this case, but may require several
+Megabytes for larger degrees.) Note that \kbd{NF} is now expressed in terms
+of the variable \kbd{y}, to avoid later problems with variable priorities.
+
+A word of warning: both the \kbd{bnf} and all results obtained from it are
+\emph{conditional} on a Riemann Hypothesis at this point; the \kbd{bnf} must
+be certified before the following statements become actual theorems.
+\smallskip
+
+Member functions are still available for \kbd{bnf} structures. So, let's try
+them: \kbd{bnf.pol} gives \kbd{A}, \kbd{bnf.sign}, \kbd{bnf.disc},
+\kbd{bnf.zk}, ok nothing really exciting. In fact, an \kbd{nf} is included
+in the \kbd{bnf} structure: \kbd{bnf.nf} should be identical to
+\kbd{NF}. Thus, all functions which took an \kbd{nf} as first argument, will
+equally accept a \kbd{bnf} (and a \kbd{bnr} as well which contains even more
+data).
+
+Anyway, new members are available now: \kbd{bnf.no} tells us the class number
+is 4, \kbd{bnf.cyc} that it is cyclic (of order 4 but that we already knew),
+\kbd{bnf.gen} that it is generated by the ideal \kbd{g = bnf.gen[1]}. If you
+\kbd{idealfactor(bnf, g)}, you recognize \kbd{P[2]}. (You may also play in
+the other direction with \kbd{idealhnf}.) The regulator \kbd{bnf.reg} is
+equal to $3.794\dots$. \kbd{bnf.tu} tells us that the roots of unity in $K$
+are exactly the sixth roots of 1 and gives a primitive root $\zeta =
+{1\over7}(\alpha^3 - 5\alpha^2 - 8\alpha + 56)$, which we have seen already.
+Finally \kbd{bnf.fu} gives us a fundamental unit $\epsilon =
+{1\over7}(\alpha^3 - 5\alpha^2 - \alpha + 28)$, which must be linked to the
+units \kbd{u} and \kbd{u2} found above since the unit rank is~1. To find
+these relations, type
+\bprog
+  bnfisunit(bnf, u)
+  bnfisunit(bnf, u2)
+ at eprog\noindent
+Lo and behold, \kbd{u = $\zeta^2\epsilon^2$} and \kbd{u2 =
+$\zeta^{4}\epsilon^1$}.
+
+\misctitle{Note} Since the fundamental unit obtained depends on the random
+seed, you could have obtained another unit than $\epsilon$, had you not reset
+the random seed before the computation. This was the purpose of the initial
+\kbd{setrand} instruction, which was otherwise unnecessary.\medskip
+
+We are now ready to perform operations in the class group. First and
+foremost, let us certify the result: type \kbd{bnfcertify(bnf)}. The
+output is \kbd{1} if all went well; in fact no other output is possible,
+whether the input is correct or not, but you can get an error message (or in
+exceedingly rare cases an infinite loop) if it is incorrect.
+
+It means that we now know the class group and fundamental units
+unconditionally (no more GRH then!). In this case, the certification process
+takes a very short time, and you might wonder why it is not built in as a
+final check in the \kbd{bnfinit} function. The answer is that as the
+regulator gets bigger this process gets increasingly difficult, and becomes
+soon impractical, while \kbd{bnfinit} still happily spits out results. So it
+makes sense to dissociate the two: you can always check afterwards, if the
+result is interesting enough. Looking at the tentative regulator, you know in
+advance whether the certification can possibly succeed: if \kbd{bnf.reg} is
+large, don't waste your time.
+
+
+Now that we feel safe about the \kbd{bnf} output, let's do some real work.
+For example, let us take again our prime ideal \kbd{pr} above 7. Since we
+know that the class group is of order 4, we deduce that \kbd{pr} raised to
+the fourth power must be principal. Type
+\bprog
+  pr4 = idealpow(nf, pr, 4)
+  v = bnfisprincipal(bnf, pr4)
+ at eprog\noindent
+The first component gives the factorization of the ideal in the class group.
+Here, \kbd{[0]} means that it is up to equivalence equal to the 0-th power of
+the generator \kbd{g} given in \kbd{bnf.gen}, in other words that it is a
+principal ideal. The second component gives us the algebraic number $\alpha$
+such that $\kbd{pr4}=\alpha\Z_K$, $\alpha$ being as usual expressed on the
+integral basis. Type \kbd{alpha = v[2]}. Let us check that the result is
+correct: first, type \kbd{idealnorm(bnf, alpha)}. (Note that we can use a
+\kbd{bnf} with all the \kbd{nf} functions; but not the other way round, of
+course.) It is indeed equal to $7^4 = 2401$, which is the norm of \kbd{pr4}.
+This is only a first check. The complete check is obtained by computing the
+HNF of the principal ideal generated by \kbd{alpha}. To do this, type
+\kbd{idealhnf(bnf, alpha) == pr4}.
+
+Since the equality is true, \kbd{alpha} is correct (not that there was any
+doubt!). But \kbd{bnfisprincipal} also gives us information for non-principal
+ideals. For example, type
+\bprog
+  v = bnfisprincipal(bnf, pr)
+ at eprog\noindent
+The component \kbd{v[1]} is now equal to \kbd{[3]}, and tells us that \kbd{pr}
+is ideal-equivalent to the cube of the generator \kbd{g}. Of course we
+already knew this since the product of \kbd{P[3]} and \kbd{P[4]} was
+principal (generated by \kbd{al}), as well as the product of all the
+\kbd{P[$i$]} (generated by 7), and we noticed that \kbd{P[2]} was equal
+to \kbd{g}, which has order 4. The second component \kbd{v[2]} gives us
+$\alpha$ on the integral basis such that $\kbd{pr}=\alpha \kbd{g}^3$. Note
+that if you \emph{don't} want this $\alpha$, which may be large and whose
+computation may take some time, you can just add the flag $1$ (see the online
+help) to the arguments of \kbd{bnfisprincipal}, so that it only returns the
+position of \kbd{pr} in the class group. \smallskip
+
+\subsec{Class field theory, \kbd{bnr}}
+
+We now survey quickly some class field theoretic routines. We must first
+initialize a Buchmann Number Ray, or \kbd{bnr}, structure, associated to a
+\kbd{bnf} base field and a modulus. Let's keep $K$, and try a finite modulus
+${\goth f} = 7\Z_K$. (See the manual for how to include infinite places in
+the modulus.) Since $K$ will now become a base field over which we want to
+build relative extensions, the associated \kbd{bnf} needs to have variables
+of lower priority than the polynomials defining the extensions. Fortunately,
+we already took care that, but it would have been easy to deal with the
+problem now (as easy as \kbd{bnf = subst(bnf, x, y)}). Then type
+\bprog
+  bnr = bnrinit(bnf, 7, 1);
+  bnr.cyc
+ at eprog\noindent
+tells us the ray class group modulo ${\goth f}$ is isomorphic to
+$\Z/24\Z \times \Z/6\Z \times \Z/2\Z $. The associated generators are
+\kbd{bnr.gen}.  Just as a \kbd{bnf} contained an \kbd{nf}, a \kbd{bnr}
+contains a \kbd{bnf} (hence an \kbd{nf}), namely \kbd{bnr.bnf}. Here
+\kbd{bnr.clgp} refers to the ray class group, while \kbd{bnr.bnf.clgp} refers
+to the class group.
+\bprog
+  rnfkummer(bnr,, 2)
+  rnfkummer(bnr,, 3)
+ at eprog\noindent
+outputs defining polynomials for the $2$ abelian extensions of $K$ of degree
+$2$ (resp.~$3$), whose conductor is exactly equal to ${\goth f}$ (the modulus
+used to define \kbd{bnr}). (In the current implementation of \kbd{rnfkummer},
+these degrees must be \emph{prime}.) What about other extensions of degree
+$2$ for instance?
+\bprog
+  L0= subgrouplist(bnr, [2])
+  L = subgrouplist(bnr, [2], 1)
+ at eprog\noindent
+\kbd{L0}, resp.~\kbd{L} is the list of those subgroups of the full ray class
+group mod $7$, whose index is $2$, and whose conductor is $7$,
+resp.~arbitrary. (Subgroups are given by a matrix of generators, in terms of
+\kbd{bnr.gen}.) \kbd{L0} has $2$ elements, associated to the $2$ extensions
+we already know. \kbd{L} has $7$ elements, the $2$ from \kbd{L0}, and
+$5$ new ones:
+\bprog
+  L1 = setminus(Set(L), Set(L0))
+ at eprog\noindent
+The conductors are
+\bprog
+  vector(#L1, i, bnrconductor(bnr, L1[i]))
+ at eprog\noindent
+among which one sees the identity matrix, i.e. the trivial ideal. (It is
+\kbd{L1[3]} in my session, maybe not in yours. Take the right one!) Indeed,
+the class group was cyclic of order $4$ and there exists a unique unramified
+quadratic extension. We could find it directly by recomputing a \kbd{bnr}
+with trivial conductor, but we can also use
+\bprog
+  rnfkummer(bnr, L1[3]) \\ @com pick the subgroup with trivial conductor!
+ at eprog\noindent
+directly which outputs the (unique by Takagi's theorem) class field
+associated to the subgroup \kbd{L1[3]}. In fact, it is of the form
+$K(\sqrt{-\epsilon})$. We can check this directly:
+\bprog
+  rnfconductor(bnf, x^2 + bnf.fu[1])
+ at eprog\noindent
+
+\subsec{Galois theory over $\Q$}
+
+PARI includes a nearly complete set of routines to compute with Galois
+extensions of $\Q$. We start with a very simple example.
+Let $\zeta$ a $8$th-root of unity and $K=\Q(\zeta)$. The minimal
+polynomial of $\zeta$ is the 8$th$ cyclotomic polynomial, namely
+\kbd{polcyclo(8)} (=$x^4+1$).
+
+We issue the command
+\bprog
+G = galoisinit(x^4 + 1);
+ at eprog\noindent
+to compute $G=\text{Gal}(K/\Q)$.  The command \kbd{galoisisabelian(G)}
+returns \kbd{[2,0;0,2]} so $G$ is an abelian group, isomorphic to $(\Z/2\Z)^2$, generated by
+$\sigma$=\kbd{G.gen[1]} and $\tau$=\kbd{G.gen[2]}. These automorphisms are
+given by their actions on the roots of $x^4+1$ in a suitable $p$-adic
+extension. To get the explicit action on $\zeta$, we use
+\kbd{galoispermtopol(G,G.gen[i])} for $i=1,2$ and get $\sigma(\zeta)=-\zeta$
+and $\tau(\zeta)=\zeta^3$. The last non-trivial automorphism is
+$\sigma\tau$=\kbd{G.gen[1]*G.gen[2]} and we have
+$\sigma\tau(\zeta)=-\zeta^3$. (At least in my version, yours may return a
+different set of generators, rename accordingly.)
+
+We compute the fixed field of $K$ by the subgroup generated by $\tau$ with
+\bprog
+galoisfixedfield(G, G.gen[2], 1)
+ at eprog\noindent
+and get $x^2 + 2$. Now we want the factorisation of $x^4+1$ over that
+subfield. Of course, we could use \kbd{nffactor}, but here we have a much
+simpler option: \kbd{galoisfixedfield(G, G.gen[1], 2)} outputs
+\bprog
+[x^2 + 2, Mod(x^3 + x, x^4 + 1), [x^2 - y*x - 1, x^2 + y*x - 1]]
+ at eprog\noindent
+which means that
+$x^4+1=(x^2-\alpha\*x-1)(x^2+\alpha\*x-1)$ where $\alpha$ is a root of $x^2+2$,
+and more precisely, $\alpha=\zeta^3+\zeta$. So we recover the well-known
+factorisation:
+
+$$x^4+1=(x^2-\sqrt{-2}\*x-1)(x^2+\sqrt{-2}\*x-1)$$
+
+For our second example, let us take the field $K$ defined by the polynomial
+\bprog
+  P = x^18 - 3*x^15 + 115*x^12 + 104*x^9 + 511*x^6 + 196*x^3 + 343;
+  G = galoisinit(P);
+ at eprog\noindent Since \kbd{galoisinit} succeeds,
+the extension $K/\Q$ is Galois. This time \kbd{galoisisabelian(G)} return
+$0$, so the extension is not abelian, however we can still put a name on the
+underlying abstract group. Use \kbd{galoisidentify(G)}, which return $[18,
+3]$. By looking at the GAP4 classification we find that $[18, 3]$ is
+$S_3\times\Z/3\Z$. This time, the subgroups of $G$ are not obvious,
+fortunately we can ask PARI : \kbd{galoissubgroups(G)}.
+
+Let us look for a polynomial $Q$ with the property that $K$ is the splitting
+field of $Q(x^2)$. For that purpose, let us take $\sigma$=\kbd{G.gen[3]}.  We
+check that \kbd{G.gen[3]\^{}2} is the identity, so $\sigma$ is of order $2$. We now compute the fixed field $K^\sigma$ and the relative factorisation of $P$ over
+$K^\sigma$:
+\bprog
+F = galoisfixedfield(G, G.gen[3], 2);
+ at eprog\noindent
+So $K$ is a quadratic extension of $K^\sigma$ defined by the polynomial
+\kbd{R=F[3][1]}. It is well-known that $K$ is also defined by $x^2-D$
+where $D$ is the discriminant of $R$ (over $K^\sigma$).
+To compute $D$ we issue:
+\bprog
+D = poldisc(F[3][1]) * Mod(1,subst(F[1],x,y));
+ at eprog\noindent
+Note that since \kbd{y} in \kbd{F[3][1]} denotes a root of \kbd{F[1]}, we
+have to use \kbd{subst(,x,y)}.  Now we hope that $D$ generate $K^\sigma$ and
+compute \kbd{Q=charpoly(D)}. We check that $Q=x^9+270\*x^6+12393\*x^3+19683$ is
+irreducible with \kbd{polisirreducible(Q)}. (Were it not the case, we would
+multiply $D$ by a random square.) So $D$ is a generator of $K^\sigma$ and
+$\sqrt{D}$ is a generator of $K$. The result is that $K$ is the splitting
+field of $Q(x^2)$.  We can check that with
+\kbd{nfisisom(P,subst(Q,x,x\^{}2))}.
+
+\section{Plotting}
+
+PARI supports high and low-level graphing functions, on a variety of output
+devices: a special purpose window under standard graphical environments (the
+\kbd{X Windows} system, Mac OS X, Microsoft Windows), or a \kbd{PostScript}
+file ready for the printer. These functions use a multitude of flags, which
+are mostly power-of-2. To simplify understanding we first give these flags
+symbolic names.
+\bprog
+  /* Relative positioning of graphic objects: */
+  nw       = 0;  se       = 4;  relative = 1;
+  sw       = 2;  ne       = 6;
+
+  /* String positioning: */
+  /* V */ bottom  =  0;   /* H */  left   = 0;   /* Fine tuning */ hgap = 16;
+          vcenter =  4;            center = 1;                     vgap = 32;
+          top     =  8;            right  = 2;
+ at eprog\noindent
+We also decrease drastically the default precision.
+\bprog
+  \p 9
+ at eprog\noindent
+This is very important, since plotting involves calculation of functions at
+a huge number of points, and a relative precision of 38 significant digits
+is an obvious overkill: the output device resolution certainly won't reach
+$10^{38} \times 10^{38}$ pixels!
+
+Start with a simple plot:
+\bprog
+  ploth(X = -2, 2, sin(X^7))
+ at eprog\noindent
+You can see the limitations of the ``straightforward'' mode of plotting:
+while the first several cycles of \kbd{sin} reach $-1$ and $1$, the cycles
+which are closer to the left and right border do not. This is understandable,
+since PARI is calculating $\sin(X^7)$ at many (evenly spaced) points, but
+these points have no direct relationship to the ``interesting'' points on
+the graph of this function.  No value close enough to the maxima and minima
+are calculated, which leads to wrong turning points in the graph. To fix
+this, one may use variable steps which are smaller where the function varies
+rapidly:
+\bprog
+  ploth(X = -2, 2, sin(X^7), "Recursive")
+ at eprog\noindent
+The precision near the edges of the graph is much better now.
+However, the recursive plotting (named so since PARI subdivides intervals
+until the graph becomes almost straight) has its own pitfalls.  Try
+\bprog
+  ploth(X = -2, 2, sin(X*7), "Recursive")
+ at eprog\noindent The graph looks correct far away, but it has a straight
+interval near the origin, and some sharp corners as well.  This happens
+because the graph is symmetric with respect to the origin, thus the middle 3
+points calculated during the initial subdivision of $[-2,2]$ are exactly on
+the same line.  To PARI this indicates that no further subdivision is needed,
+and it plots the graph on this subinterval as a straight line.
+
+There are many ways to circumvent this.  Say, one can make the right limit
+2.1.  Or one can ask PARI for an initial subdivision into 16 points instead
+of default 15:
+\bprog
+  ploth(X = -2, 2, sin(X*7), "Recursive", 16)
+ at eprog\noindent
+All these arrangements break the symmetry of the initial subdivision, thus
+make the problem go away.  Eventually PARI will be able to better detect such
+pathological cases, but currently some manual intervention may be required.
+
+The function \kbd{ploth} has some additional enhancements which allow
+graphing in situations when the calculation of the function takes a lot of
+time.  Let us plot $\zeta({1\over 2} + it)$:
+\bprog
+  ploth(t = 100, 110, real(zeta(0.5+I*t)), /*empty*/, 1000)
+ at eprog\noindent
+This can take quite some time.  (1000 is close to the default for many
+plotting devices, we want to specify it explicitly so that the result does
+not depend on the output device.)  Try the recursive plot:
+\bprog
+  ploth(t = 100, 110, real(zeta(0.5+I*t)), "Recursive")
+ at eprog\noindent
+It takes approximately the same time.  Now try specifying fewer points,
+but make PARI approximate the data by a smooth curve:
+\bprog
+  ploth(t = 100, 110, real(zeta(0.5+I*t)), "Splines", 100)
+ at eprog\noindent
+This takes much less time, and the output is practically the same.  How to
+compare these two outputs?  We will see it shortly.  Right now let us plot
+both real and complex parts of $\zeta$ on the same graph:
+\bprog
+  f(t) = my(z = zeta(0.5+I*t)); [real(z),imag(z)];
+  ploth(t = 100, 110, f(t), , 1000)
+ at eprog\noindent (Note the use of the temporary variable \kbd{z}; \kbd{my}
+declares it local to the function's body.)
+
+Note how one half of the roots of the real and imaginary parts coincide.
+Why did we define a function \kbd{f(t)}?  To avoid calculation of
+$\zeta({1\over2} + it)$ twice for the same value of t.  Similarly, we can
+plot parametric graphs:
+\bprog
+  ploth(t = 100, 110, f(t), "Parametric", 1000)
+ at eprog\noindent In that case (parametric plot of the real and imaginary parts
+of a complex function), we can also use directly
+\bprog
+  ploth(t = 100, 110, zeta(0.5+I*t), "Complex", 1000)
+  ploth(t = 100, 110, zeta(0.5+I*t), "Complex|Splines", 100)
+ at eprog
+
+If your plotting device supports it, you may ask PARI to show the points
+in which it calculated your function:
+\bprog
+  ploth(t = 100, 110, f(t), "Parametric|Splines|Points_too", 100)
+ at eprog
+
+As you can see, the points are very dense on the graph.  To see some crude
+graph, one can even decrease the number of points to 30.  However, if you
+decrease the number of points to 20, you can see that the approximation to
+the graph now misses zero.  Using splines, one can create reasonable graphs
+for larger values of t, say with
+\bprog
+  ploth(t = 10000, 10004, f(t), "Parametric|Splines|Points_too", 50)
+ at eprog
+
+How can we compare two graphs of the same function plotted by different
+methods?  Documentation shows that \kbd{ploth} does not provide any direct
+method to do so.  However, it is possible, and even not very complicated.
+
+The solution comes from the other direction.  PARI has a power mix of high
+level plotting function with low level plotting functions, and these functions
+can be combined together to obtain many different effects.  Return back
+to the graph of $\sin(X^7)$.  Suppose we want to create an additional
+rectangular frame around our graph.  No problem!
+
+First, all low-level graphing work takes place in some virtual drawing
+boards (numbered from 0 to 15), called ``rectangles'' (or ``rectwindows'').
+So we create an empty ``rectangle'' and name it rectangle 2 (any
+number between 0 and 15 would do):
+\bprog
+  plotinit(2)
+  plotscale(2, 0,1, 0,1)
+ at eprog
+This creates a rectwindow whose size exactly fits the size of the output
+device, and makes the coordinate system inside it go from 0 to 1 (for both
+$x$ and $y$). Create a rectangular frame along the boundary of this rectangle:
+\bprog
+  plotmove(2, 0,0)
+  plotbox(2, 1,1)
+ at eprog
+Suppose we want to draw the graph inside a subrectangle of this with upper
+and left margins of $0.10$ (so 10\% of the full rectwindow width), and
+lower and top margins of $0.02$, just to make it more interesting. That
+makes it an $0.88 \times 0.88$ subrectangle; so we create another rectangle
+(call it 3) of linear size 0.88 of the size of the initial rectangle and
+graph the function in there:
+\bprog
+  plotinit(3, 0.88, 0.88, relative)
+  plotrecth(3, X = -2, 2, sin(X^7), "Recursive")
+ at eprog
+(nothing is output yet, these commands only fills the virtual drawing
+boards with PARI graphic objects). Finally, output rectangles 2 and 3 on
+the same plot, with the required offsets (counted from upper-left corner):
+\bprog
+  plotdraw([2, 0,0,  3, 0.1,0.02], relative)
+ at eprog
+\noindent The output misses something comparing to the output of
+\kbd{ploth}: there are no coordinates of the corners of the internal
+rectangle.  If your output device supports mouse operations (only
+\kbd{gnuplot} does), you can find coordinates of particular points of the
+graph, but it is nice to have something printed on a hard copy too.
+
+However, it is easy to put $x$- and $y$-limits on the graph.  In the
+coordinate system of the rectangle 2 the corners are $(0.1,0.1)$,
+$(0.1,0.98)$, $(0.98,0.1)$, $(0.98,0.98)$.  We can mark lower $x$-limit by
+doing
+\bprog
+  plotmove(2, 0.1,0.1)
+  plotstring(2, "-2.000", left+top+vgap)
+ at eprog\noindent
+Computing the minimal and maximal $y$-coordinates might be trickier, since
+in principle we do not know the range in advance (though for $\sin(X^7)$ it
+is easy to guess!). Fortunately, \kbd{plotrecth} returns the $x$- and
+$y$-limits.
+
+Here is the complete program:
+\bprog
+  plotinit(3, 0.88, 0.88, relative)
+  lims = plotrecth(3, X = -2, 2, sin(X^7), "Recursive")
+  \p 3          \\ @com $3$ significant digits for the bounding box are enough
+  plotinit(2);      plotscale(2, 0,1, 0,1)
+  plotmove(2, 0,0); plotbox(2, 1,1)
+  plotmove(2, 0.1,0.1);
+  plotstring(2, lims[1], left+top+vgap)
+  plotstring(2, lims[3], bottom+vgap+right+hgap)
+  plotmove(2, 0.98,0.1); plotstring(2, lims[2], right+top+vgap)
+  plotmove(2, 0.1,0.98); plotstring(2, lims[4], right+hgap+top)
+  plotdraw([2, 0,0,  3, 0.1,0.02], relative)
+ at eprog
+
+We started with a trivial requirement: have an additional frame around
+the graph, and it took some effort to do so.  But at least it was possible,
+and PARI did the hardest part: creating the actual graph.
+Now do a different thing: plot together the ``exact'' graph of
+$\zeta({1/2}+it)$ together with one obtained from splines approximation.
+We can emit these graphs into two rectangles, say 0 and 1, then put these
+two rectangles together on one plot.  Or we can emit these graphs into one
+rectangle 0.
+
+However, a problem arises: note how we
+introduced a coordinate system in rectangle 2 of the above example, but we
+did not introduce a coordinate system in rectangle 3.  Plotting a
+graph into rectangle 3 automatically created a coordinate system
+inside this rectangle (you could see this coordinate system in action
+if your output device supports mouse operations).  If we use two different
+methods of graphing, the bounding boxes of the graphs will not be exactly
+the same, thus outputting the rectangles may be tricky.  Thus during
+the second plotting we ask \kbd{plotrecth} to use the coordinate system of
+the first plotting.  Let us add another plotting with fewer
+points too:
+\bprog
+plotinit(0, 0.9,0.9, relative)
+plotrecth(0, t=100,110, f(t), "Parametric",300)
+plotrecth(0, t=100,110, f(t), "Parametric|Splines|Points_too|no_Rescale",30);
+plotrecth(0, t=100,110, f(t), "Parametric|Splines|Points_too|no_Rescale",20);
+plotdraw([0, 0.05,0.05], relative)
+ at eprog
+
+This achieves what we wanted: we may compare different ways to plot a graph,
+but the picture is confusing: which graph is what, and why there are multiple
+boxes around the graph?  At least with some output devices one can control
+how the output curves look like, so we can use this to distinguish different
+graphs.  And the mystery of multiple boxes is also not that hard to solve:
+they are bounding boxes for calculated points on each graph.  We can disable
+output of bounding boxes with appropriate options for \kbd{plotrect}.
+With these frills the script becomes:
+\bprog
+plotinit(0, 0.9,0.9, relative)
+plotpointtype(-1, 0)                \\@com set color of graph points
+plotpointsize(0, 0.4)               \\@com use tiny markers (if available)
+plotrecth(0, t=100,110, f(t), "Parametric|no_Lines", 300)
+plotpointsize(0, 1)                 \\@com normal-size markers
+plotlinetype(-1, 1)                 \\@com set color of graph lines
+plotpointtype(-1, 1)                \\@com set color of graph points
+opts="Parametric|Splines|Points_too|no_Rescale|no_Frame|no_X_axis|no_Y_axis";
+plotrecth(0, t=100,110,f(t), opts, 30);
+plotlinetype(-1, 2)                 \\@com set color of graph lines
+plotpointtype(-1, 2)                \\@com set color of graph points
+plotrecth(0, t=100,110,f(t), opts, 20);
+plotdraw([0, 0.05,0.05], relative)
+ at eprog
+
+\noindent Plotting axes on the second and third graph would not hurt, but
+is not needed either, so we omit them.  One can see that the discrepancies
+between the exact graph and one based on 30 points exist, but are pretty
+small.  On the other hand, decreasing the number of points to 20 makes
+quite a noticeable difference.
+
+Keep in mind that \kbd{plotlinetype}, \kbd{plotpointtype},
+\kbd{plotpointsize} may do nothing on some terminals.
+
+Additionally, one can ask PARI to output a plot into a PS file: just
+use the command \kbd{psdraw} instead of \kbd{plotdraw} in the above examples
+(or \kbd{psploth} instead of \kbd{ploth}).  See \kbd{psfile} argument
+to \kbd{default} for how to change the output file for this operation.  Keep
+in mind that the precision of PARI PS output will be the same as for the
+current output device.
+
+Now suppose we want to join many different small graphs into one picture.
+We cannot use one rectangle for all the output as we did in the example
+with $\zeta({1/2}+it)$, since the graphs should go into different places.
+Rectangles are a scarce commodity in PARI, since only 16 of them are
+user-accessible.  Does it mean that we cannot have more than 16 graphs on
+one picture?  Thanks to an additional operation of PARI plotting engine,
+there is no such restrictions.  This operation is \kbd{plotcopy}.
+
+The following script puts 4 different graphs on one plot using 2 rectangles
+only, \kbd{A} and \kbd{T}:
+\bprog
+  A = 2;   \\@com accumulator
+  T = 3;   \\@com temporary target
+  plotinit(A);         plotscale(A, 0, 1, 0, 1)
+
+  plotinit(T, 0.42, 0.42, relative);
+  plotrecth(T, x= -5, 5, sin(x), "Recursive")
+  plotcopy(T, 2, 0.05, 0.05, relative + nw)
+
+  plotmove(A, 0.05 + 0.42/2, 1 - 0.05/2)
+  plotstring(A,"Graph", center + vcenter)
+
+  plotinit(T, 0.42, 0.42, relative);
+  plotrecth(T, x= -5, 5, [sin(x),cos(2*x)], 0)
+  plotcopy(T, 2, 0.05, 0.05, relative + ne)
+
+  plotmove(A, 1 - 0.05 - 0.42/2, 1 - 0.05/2)
+  plotstring(A,"Multigraph", center + vcenter)
+
+  plotinit(T, 0.42, 0.42, relative);
+  plotrecth(T, x= -5, 5, [sin(3*x), cos(2*x)], "Parametric")
+  plotcopy(T, 2, 0.05, 0.05, relative + sw)
+
+  plotmove(A, 0.05 + 0.42/2, 0.5)
+  plotstring(A,"Parametric", center + vcenter)
+
+  plotinit(T, 0.42, 0.42, relative);
+  plotrecth(T, x= -5, 5, [sin(x), cos(x), sin(3*x),cos(2*x)], "Parametric")
+  plotcopy(T, 2, 0.05, 0.05, relative + se)
+
+  plotmove(A, 1 - 0.05 - 0.42/2, 0.5)
+  plotstring(A,"Multiparametric", center + vcenter)
+
+  plotlinetype(A, 3)
+  plotmove(A, 0, 0)
+  plotbox(A, 1, 1)
+
+  plotdraw([A, 0, 0])
+  \\ psdraw([A, 0, 0], relative)          \\ @com if hard copy is needed
+ at eprog
+
+The rectangle \kbd{A} plays the role of accumulator, rectangle \kbd{T} is
+used as a target to \kbd{plotrecth} only.  Immediately after plotting into
+rectangle \kbd{T} the contents is copied to accumulator.  Let us explain
+numbers which appear in this example: we want to create 4 internal rectangles
+with a gap 0.06 between them, and the outside gap 0.05 (of the size of the
+plot).  This leads to the size 0.42 for each rectangle.  We also
+put captions above each graph, centered in the middle of each gap.  There
+is no need to have a special rectangle for captions: they go into the
+accumulator too.
+
+To simplify positioning of the rectangles, the above example uses relative
+``geographic'' notation: the last argument of \kbd{plotcopy} specifies the
+corner of the graph (say, northwest) one counts offset from. (Positive
+offsets go into the rectangle.)
+
+To demonstrate yet another useful plotting function, design a program to
+plot Taylor polynomials for a $\sin x$ about 0.  For simplicity, make the
+program good for any function, but assume that a function is odd, so only
+odd-numbered Taylor series about 0 should be plotted.  Start with defining
+some useful shortcuts
+\bprog
+  xlim = 13;  ordlim = 25;  f(x) = sin(x);
+  default(seriesprecision,ordlim)
+  farray(t) = vector((ordlim+1)/2, k, truncate( f(1.*t + O(t^(2*k+1)) )));
+  FARRAY = farray('t);  \\@com\kbd{'t} to make sure \kbd{t} is a free variable
+ at eprog\noindent
+\kbd{farray(x)} returns a vector of Taylor polynomials for $f(x)$, which we
+store in \kbd{FARRAY}.  We want to plot $f(x)$ into a rectangle, then make
+the rectangle which is 1.2 times higher, and plot Taylor polynomials into the
+larger rectangle.  Assume that the larger rectangle takes 0.9 of the final
+plot.
+
+First of all, we need to measure the height of the smaller rectangle:
+\bprog
+  plotinit(3, 0.9, 0.9/1.2, 1);
+  opts = "Recursive | no_X_axis|no_Y_axis|no_Frame";
+  lims = plotrecth(3, x= -xlim, xlim, f(x), opts,16);
+  h = lims[4] - lims[3];
+ at eprog\noindent
+Next step is to create a larger rectangle, and plot the Taylor polynomials
+into the larger rectangle:
+\bprog
+  plotinit(4, 0.9,0.9, relative);
+  plotscale(4, lims[1], lims[2], lims[3] - h/10, lims[4] + h/10)
+  plotrecth(4, x = -xlim, xlim, subst(FARRAY,t,x), "no_Rescale");
+ at eprog
+
+Here comes the central command of this example:
+\bprog
+  plotclip(4)
+ at eprog\noindent
+What does it do?  The command \kbd{plotrecth(\dots, "no\_Rescale")} scales the
+graphs according to coordinate system in the rectangle, but it does not pay
+any other attention to the size of the rectangle.  Since \kbd{xlim} is 13,
+the Taylor polynomials take very large values in the interval
+\kbd{-xlim...xlim}.  In particular, significant part of the graphs is going
+to be \emph{outside} of the rectangle. \kbd{plotclip} removes the parts of
+the plottings which fall off the rectangle boundary
+\bprog
+  plotinit(2)
+  plotscale(2, 0.0, 1.0, 0.0, 1.0)
+  plotmove(2,0.5,0.975)
+  plotstring(2,"Multiple Taylor Approximations",center+vcenter)
+  plotdraw([2, 0, 0,  3, 0.05, 0.05 + 0.9/12,  4, 0.05, 0.05], relative)
+ at eprog\noindent
+These commands draw a caption, and combine 3 rectangles (one with the
+caption, one with the graph of the function, and one with graph of Taylor
+polynomials) together. The plots are not very beautiful with the default
+colors. See \kbd{examples/taylor.gp} for a user function encapsulating the
+above example, and a colormap generator.
+
+This finishes our survey of PARI plotting functions, but let us add
+some remarks.  First of all, for a typical output device the picture is
+composed of small colored squares (pixels), as a very large checkerboard.
+Each output rectangle is a disjoint union of such squares.  Each drop
+of paint in the rectangle will color a whole square in it.  Since the
+rectangle has a coordinate system, it is important to know how this
+coordinate system is positioned with respect to the boundaries of these
+squares.
+
+The command \kbd{plotscale} describes a range of $x$ and $y$ in the
+rectangle.  The limit values of $x$ and $y$ in the coordinate system are
+coordinates \emph{of the centers} of corner squares.  In particular,
+if ranges of $x$ and $y$ are $[0,1]$, then the segment which connects (0,0)
+with (0,1) goes along the \emph{middle} of the left column of the rectangle.
+In particular, if we made tiny errors in calculation of endpoints of this
+segment, this will not change which squares the segment intersects, thus
+the resulting picture will be the same.  (It is important to know such details
+since many calculations are approximate.)
+
+Another consideration is that all examples we did in this section were
+using relative sizes and positions for the rectangles.  This is nice, since
+different output devices will have very similar pictures, while we
+did not need to care about particular resolution of the output device.
+On the other hand,
+using relative positions does not guarantee that the pictures will be
+similar.  Why?  Even if two output devices have the same resolution,
+the picture may be different.  The devices may use fonts of different
+size, or may have a different ``unit of length''.
+
+The information about the device in PARI is encoded in 6 numbers: resolution,
+size of a character cell of the font, and unit of length, all separately
+for horizontal and vertical direction.  These sizes are expressed as
+numbers of pixels.  To inspect these numbers one may use the function
+\kbd{plothsizes}.  The ``units of length'' are currently used to calculate
+right and top gaps near graph rectangle of \kbd{ploth}, and gaps for
+\kbd{plotstring}.  Left and bottom gaps near graph rectangle are calculate
+using both units of length, and sizes of character boxes (so that there
+is enough place to print limits of the graphs).
+
+What does it show?  Using relative sizes during plotting produces
+\var{approximately} the same plotting on different devices, but does not
+ensure that the plottings ``look the same''.  Moreover, ``looking the
+same'' is not a desirable target, ``looking tuned for the environment''
+will be much better.  If you want to produce such fine-tuned plottings,
+you need to abandon a relative-size model, and do your plottings in
+pixel units.  To do this one removes flag \kbd{relative} from the above
+examples, which will make size and offset arguments interpreted this way.
+After querying sizes with \kbd{plothsizes} one can fine-tune sizes and
+locations of subrectangles to the details of an arbitrary plotting
+device.
+
+To check how good your fine-tuning is, you may test your graphs with a
+medium-resolution plotting (as many display output devices are), and
+with a low-resolution plotting (say, with \kbd{plotterm("dumb")} of gnuplot).
+
+\section{GP Programming}
+
+Do we really need such a section after all we have learnt so far? We now
+know how to write scripts and feed them to \kbd{gp}, in particular how to
+define functions. It's possible to define \emph{member} function as well, but
+we trust you to find them in the manual. We have seen most control
+statements: the missing ones (\kbd{while}, \kbd{break}, \kbd{next},
+\kbd{return} and various \kbd{for} loops) should be straightforward. (You
+won't forget to look them up in the manual, will you?)
+
+Output is done via variants of the familiar \kbd{print} (to screen),
+\kbd{write} (to a file). Input via \kbd{read} (from file), \kbd{input}
+(querying user), or \kbd{extern} (from an external auxiliary program).
+Perhaps the most useful simple command we haven't seen yet is
+\kbd{allocatemem} which increase the size of \kbd{gp}'s ``scratch space''. If
+you regularly see \kbd{PARI stack overflows!} messages, think about this
+one.
+
+To customize \kbd{gp}, e.g.~increase the default stack space or load your
+private script libraries on startup, look up \kbd{The preferences file}
+section in the User's manual. For clarity, it is advisable  to declare local
+variables in user functions (and in fact, with the smallest possible scope),
+as we have done so far with the keyword \kbd{my}. One is usually better off
+avoiding global variables altogether, but you are the sole master on board at
+this point.
+
+\emph{Break loops} are more powerful than we saw: look up \kbd{dbg\_down} /
+\kbd{dbg\_up} (to get a chance to inspect local variables in various scopes)
+and \kbd{dbg\_err} (to access all components of an error context).
+
+To reach grandwizard status, you may need to understand the all powerful
+\kbd{install} function, which imports into \kbd{gp} an (almost) arbitrary
+function from the PARI library (and elsewhere too!), or how to use the
+\kbd{gp2c} compiler and its extended types. But both are beyond the scope of
+the present document.
+
+Have fun!
+\bye
diff --git a/doc/users.tex b/doc/users.tex
new file mode 100644
index 0000000..453ebf2
--- /dev/null
+++ b/doc/users.tex
@@ -0,0 +1,42 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+
+% Compile with plain TeX
+%
+\def\TITLE{User's Guide to Pari/GP}
+\input parimacro.tex
+
+% remove TOC for this level
+\SUBSECTOCfalse
+
+% START TYPESET
+\begintitle
+\vskip 2.5truecm
+\centerline{\mine User's Guide}
+\vskip 1.truecm
+\centerline{\mine to}
+\vskip 1.truecm
+\centerline{\mine PARI / GP}
+\vskip 1.truecm
+\centerline{\sectiontitlebf (version \vers)}
+\vskip 1.truecm
+\authors
+\endtitle
+
+\copyrightpage
+\tableofcontents
+\openin\std=libpari.aux
+\ifeof\std
+\else
+  \input libpari.aux
+\fi
+\chapno=0
+{ \input usersch1 }
+{ \input usersch2 }
+{ \input usersch3 }
+{ \input appa }
+\input index\end
diff --git a/doc/usersFUNCS.tex b/doc/usersFUNCS.tex
new file mode 100644
index 0000000..f3dc1c2
--- /dev/null
+++ b/doc/usersFUNCS.tex
@@ -0,0 +1,1353 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Functions and Operations Available in PARI and GP}
+\label{se:functions}
+
+The functions and operators available in PARI and in the GP/PARI calculator
+are numerous and ever-expanding. Here is a description of the ones available
+in version \vers. It should be noted that many of these functions accept
+quite different types as arguments, but others are more restricted. The list
+of acceptable types will be given for each function or class of functions.
+Except when stated otherwise, it is understood that a function or operation
+which should make natural sense is legal. In this chapter, we will describe
+the functions according to a rough classification. The general entry looks
+something like:
+
+\key{foo}$(x,\{\fl=0\})$: short description.
+
+The library syntax is \kbd{GEN foo(GEN x, long fl = 0)}.
+
+\noindent
+This means that the GP function \kbd{foo} has one mandatory argument $x$, and
+an optional one, $\fl$, whose default value is 0. (The $\{\}$ should not be
+typed, it is just a convenient notation we will use throughout to denote
+optional arguments.) That is, you can type \kbd{foo(x,2)}, or \kbd{foo(x)},
+which is then understood to mean \kbd{foo(x,0)}. As well, a comma or closing
+parenthesis, where an optional argument should have been, signals to GP it
+should use the default. Thus, the syntax \kbd{foo(x,)} is also accepted as a
+synonym for our last expression. When a function has more than one optional
+argument, the argument list is filled with user supplied values, in order.
+When none are left, the defaults are used instead. Thus, assuming that
+\kbd{foo}'s prototype had been
+$$\hbox{%
+\key{foo}$(\{x=1\},\{y=2\},\{z=3\})$,%
+}$$
+typing in \kbd{foo(6,4)} would give
+you \kbd{foo(6,4,3)}. In the rare case when you want to set some far away
+argument, and leave the defaults in between as they stand, you can use the
+``empty arg'' trick alluded to above: \kbd{foo(6,,1)} would yield
+\kbd{foo(6,2,1)}. By the way, \kbd{foo()} by itself yields
+\kbd{foo(1,2,3)} as was to be expected.
+
+In this rather special case of a function having no mandatory argument, you
+can even omit the $()$: a standalone \kbd{foo} would be enough (though we
+do not recommend it for your scripts, for the sake of clarity). In defining
+GP syntax, we strove to put optional arguments at the end of the argument
+list (of course, since they would not make sense otherwise), and in order of
+decreasing usefulness so that, most of the time, you will be able to ignore
+them.
+
+Finally, an optional argument (between braces) followed by a star, like
+$\{\var{x}\}*$, means that any number of such arguments (possibly none) can
+be given. This is in particular used by the various \kbd{print} routines.
+
+\misctitle{Flags} A \tev{flag} is an argument which, rather than conveying
+actual information to the routine, instructs it to change its default
+behavior, e.g.~return more or less information. All such
+flags are optional, and will be called \fl\ in the function descriptions to
+follow. There are two different kind of flags
+
+\item generic: all valid values for the flag are individually
+described (``If \fl\ is equal to $1$, then\dots'').
+
+\item binary:\sidx{binary flag} use customary binary notation as a
+compact way to represent many toggles with just one integer. Let
+$(p_0,\dots,p_n)$ be a list of switches (i.e.~of properties which take either
+the value $0$ or~$1$), the number $2^3 + 2^5 = 40$ means that $p_3$ and $p_5$
+are set (that is, set to $1$), and none of the others are (that is, they
+are set to $0$). This is announced as ``The binary digits of $\fl$ mean 1:
+$p_0$, 2: $p_1$, 4: $p_2$'', and so on, using the available consecutive
+powers of~$2$.
+
+\misctitle{Mnemonics for flags} Numeric flags as mentioned above are
+obscure, error-prone, and quite rigid: should the authors
+want to adopt a new flag numbering scheme (for instance when noticing
+flags with the same meaning but different numeric values across a set of
+routines), it would break backward compatibility. The only advantage of
+explicit numeric values is that they are fast to type, so their use is only
+advised when using the calculator \kbd{gp}.
+
+As an alternative, one can replace a numeric flag by a character string
+containing symbolic identifiers. For a generic flag, the mnemonic
+corresponding to the numeric identifier is given after it as in
+
+\bprog
+fun(x, {flag = 0} ):
+
+  If flag is equal to 1 = AGM, use an agm formula ...
+ at eprog\noindent
+which means that one can use indifferently \kbd{fun($x$, 1)} or
+\kbd{fun($x$, "AGM")}.
+
+For a binary flag, mnemonics corresponding to the various toggles are given
+after each of them. They can be negated by prepending \kbd{no\_} to the
+mnemonic, or by removing such a prefix. These toggles are grouped together
+using any punctuation character (such as ',' or ';'). For instance (taken
+from description of $\tet{ploth}(X=a,b,\var{expr},\{\fl=0\},\{n=0\})$)
+
+\centerline{Binary digits of flags mean: $1=\kbd{Parametric}$,
+$2=\kbd{Recursive}$, \dots}
+
+\noindent so that, instead of $1$, one could use the mnemonic
+\kbd{"Parametric; no\_Recursive"}, or simply \kbd{"Parametric"} since
+\kbd{Recursive} is unset by default (default value of $\fl$ is $0$,
+i.e.~everything unset). People used to the bit-or notation in languages like
+C may also use the form \kbd{"Parametric | no\_Recursive"}.
+
+\misctitle{Pointers} \varsidx{pointer} If a parameter in the function
+prototype is prefixed with a \& sign, as in
+
+\key{foo}$(x,\&e)$
+
+\noindent it means that, besides the normal return value, the function may
+assign a value to $e$ as a side effect. When passing the argument, the \&
+sign has to be typed in explicitly. As of version \vers, this \tev{pointer}
+argument is optional for all documented functions, hence the \& will always
+appear between brackets as in \kbd{Z\_issquare}$(x,\{\&e\})$.
+
+\misctitle{About library programming}
+The \var{library} function \kbd{foo}, as defined at the beginning of this
+section, is seen to have two mandatory arguments, $x$ and \fl: no function
+seen in the present chapter has been implemented so as to accept a variable
+number of arguments, so all arguments are mandatory when programming with the
+library (usually, variants are provided corresponding to the various flag values).
+We include an \kbd{= default value} token in the prototype to signal how a missing
+argument should be encoded. Most of the time, it will be a \kbd{NULL} pointer, or
+-1 for a variable number. Refer to the \emph{User's Guide to the PARI library}
+for general background and details.
+
+\section{Standard monadic or dyadic operators}
+
+\subseckbd{+$/$-} The expressions \kbd{+}$x$ and \kbd{-}$x$ refer
+to monadic operators (the first does nothing, the second negates $x$).
+
+The library syntax is \fun{GEN}{gneg}{GEN x} for \kbd{-}$x$.
+
+\subseckbd{+} The expression $x$ \kbd{+} $y$ is the \idx{sum} of $x$ and $y$.
+Addition between a scalar type $x$ and a \typ{COL} or \typ{MAT} $y$ returns
+respectively $[y[1] + x, y[2],\dots]$ and $y + x \text{Id}$. Other additions
+between a scalar type and a vector or a matrix, or between vector/matrices of
+incompatible sizes are forbidden.
+
+The library syntax is \fun{GEN}{gadd}{GEN x, GEN y}.
+
+\subseckbd{-} The expression $x$ \kbd{-} $y$ is the \idx{difference} of $x$
+and $y$. Subtraction between a scalar type $x$ and a \typ{COL} or \typ{MAT}
+$y$ returns respectively $[y[1] - x, y[2],\dots]$ and $y - x \text{Id}$.
+Other subtractions between a scalar type and a vector or a matrix, or
+between vector/matrices of incompatible sizes are forbidden.
+
+The library syntax is \fun{GEN}{gsub}{GEN x, GEN y} for $x$ \kbd{-} $y$.
+
+\subseckbd{*} The expression $x$ \kbd{*} $y$ is the \idx{product} of $x$
+and $y$. Among the prominent impossibilities are multiplication between
+vector/matrices of incompatible sizes, between a \typ{INTMOD} or \typ{PADIC}
+Restricted to scalars, \kbd{*} is commutative; because of vector and matrix
+operations, it is not commutative in general.
+
+Multiplication between two \typ{VEC}s or two \typ{COL}s is not
+allowed; to take the \idx{scalar product} of two vectors of the same length,
+transpose one of the vectors (using the operator \kbd{\til} or the function
+\kbd{mattranspose}, see \secref{se:linear_algebra}) and multiply a line vector
+by a column vector:
+\bprog
+? a = [1,2,3];
+? a * a
+  ***   at top-level: a*a
+  ***                  ^--
+  *** _*_: forbidden multiplication t_VEC * t_VEC.
+? a * a~
+%2 = 14
+ at eprog
+
+If $x,y$ are binary quadratic forms, compose them; see also
+\kbd{qfbnucomp} and \kbd{qfbnupow}. If $x,y$ are \typ{VECSMALL} of the same
+length, understand them as permutations and compose them.
+
+The library syntax is \fun{GEN}{gmul}{GEN x, GEN y} for $x$ \kbd{*} $y$.
+Also available is \fun{GEN}{gsqr}{GEN x} for $x$ \kbd{*} $x$.
+
+\subseckbd{/} The expression $x$ \kbd{/} $y$ is the \idx{quotient} of $x$
+and $y$. In addition to the impossibilities for multiplication, note that if
+the divisor is a matrix, it must be an invertible square matrix, and in that
+case the result is $x*y^{-1}$. Furthermore note that the result is as exact
+as possible: in particular, division of two integers always gives a rational
+number (which may be an integer if the quotient is exact) and \emph{not} the
+Euclidean quotient (see $x$ \kbd{\bs} $y$ for that), and similarly the
+quotient of two polynomials is a rational function in general. To obtain the
+approximate real value of the quotient of two integers, add \kbd{0.} to the
+result; to obtain the approximate $p$-adic value of the quotient of two
+integers, add \kbd{O(p\pow k)} to the result; finally, to obtain the
+\idx{Taylor series} expansion of the quotient of two polynomials, add
+\kbd{O(X\pow k)} to the result or use the \kbd{taylor} function
+(see \secref{se:taylor}). \label{se:gdiv}
+
+The library syntax is \fun{GEN}{gdiv}{GEN x, GEN y} for $x$ \kbd{/} $y$.
+
+\subseckbd{\bs} The expression \kbd{$x$ \bs\ $y$} is the \idx{Euclidean
+quotient} of $x$ and $y$. If $y$ is a real scalar, this is defined as
+\kbd{floor($x$/$y$)} if $y > 0$, and \kbd{ceil($x$/$y$)} if $y < 0$ and
+the division is not exact. Hence the remainder \kbd{$x$ - ($x$\bs$y$)*$y$}
+is in $[0, |y|[$.
+
+Note that when $y$ is an integer and $x$ a polynomial, $y$ is first promoted
+to a polynomial of degree $0$. When $x$ is a vector or matrix, the operator
+is applied componentwise.
+
+The library syntax is \fun{GEN}{gdivent}{GEN x, GEN y}
+for $x$ \kbd{\bs} $y$.
+
+\subseckbd{\bs/} The expression $x$ \b{/} $y$ evaluates to the rounded
+\idx{Euclidean quotient} of $x$ and $y$. This is the same as \kbd{$x$ \bs\ $y$}
+except for scalar division: the quotient is such that the corresponding
+remainder is smallest in absolute value and in case of a tie the quotient
+closest to $+\infty$ is chosen (hence the remainder would belong to
+$]{-}|y|/2, |y|/2]$).
+
+When $x$ is a vector or matrix, the operator is applied componentwise.
+
+The library syntax is \fun{GEN}{gdivround}{GEN x, GEN y}
+for $x$ \b{/} $y$.
+
+\subseckbd{\%} The expression \kbd{$x$ \% $y$} evaluates to the modular
+\idx{Euclidean remainder} of $x$ and $y$, which we now define. When $x$ or $y$
+is a non-integral real number, \kbd{$x$\%$y$} is defined as \kbd{$x$ -
+($x$\bs$y$)*$y$}. Otherwise, if $y$ is an integer, this is the smallest
+non-negative integer congruent to $x$ modulo $y$. (This actually coincides with
+the previous definition if and only if $x$ is an integer.) If $y$ is a
+polynomial, this is the polynomial of smallest degree congruent to $x$ modulo
+$y$. For instance:
+\bprog
+? (1/2) % 3
+%1 = 2
+? 0.5 % 3
+%2 = 0.5000000000000000000000000000
+? (1/2) % 3.0
+%3 = 1/2
+ at eprog
+Note that when $y$ is an integer and $x$ a polynomial, $y$ is first promoted
+to a polynomial of degree $0$. When $x$ is a vector or matrix, the operator
+is applied componentwise.
+
+The library syntax is \fun{GEN}{gmod}{GEN x, GEN y}
+for $x$ \kbd{\%} $y$.
+
+\subseckbd{\pow} The expression $x\hbox{\kbd{\pow}}n$ is \idx{powering}.
+If the exponent is an integer, then exact operations are performed using
+binary (left-shift) powering techniques. In particular, in this case $x$
+cannot be a vector or matrix unless it is a square matrix (invertible
+if the exponent is negative). If $x$ is a $p$-adic number, its
+precision will increase if $v_p(n) > 0$. Powering a binary quadratic form
+(types \typ{QFI} and \typ{QFR}) returns a reduced representative of the
+class, provided the input is reduced. In particular, $x\hbox{\kbd{\pow}}1$ is
+identical to $x$.
+
+PARI is able to rewrite the multiplication $x * x$ of two \emph{identical}
+objects as $x^2$, or $\kbd{sqr}(x)$. Here, identical means the operands are
+two different labels referencing the same chunk of memory; no equality test
+is performed. This is no longer true when more than two arguments are
+involved.
+
+If the exponent is not of type integer, this is treated as a transcendental
+function (see \secref{se:trans}), and in particular has the effect of
+componentwise powering on vector or matrices.
+
+As an exception, if the exponent is a rational number $p/q$ and $x$ an
+integer modulo a prime or a $p$-adic number, return a solution $y$ of
+$y^q=x^p$ if it exists. Currently, $q$ must not have large prime factors.
+Beware that
+\bprog
+? Mod(7,19)^(1/2)
+%1 = Mod(11, 19) /* is any square root */
+? sqrt(Mod(7,19))
+%2 = Mod(8, 19)  /* is the smallest square root */
+? Mod(7,19)^(3/5)
+%3 = Mod(1, 19)
+? %3^(5/3)
+%4 = Mod(1, 19)  /* Mod(7,19) is just another cubic root */
+ at eprog
+
+If the exponent is a negative integer, an \idx{inverse} must be computed.
+For non-invertible \typ{INTMOD}, this will fail and implicitly exhibit a
+non trivial factor of the modulus:
+\bprog
+? Mod(4,6)^(-1)
+  ***   at top-level: Mod(4,6)^(-1)
+  ***                         ^-----
+  *** _^_: impossible inverse modulo: Mod(2, 6).
+ at eprog\noindent
+(Here, a factor 2 is obtained directly. In general, take the gcd of the
+representative and the modulus.) This is most useful when performing
+complicated operations modulo an integer $N$ whose factorization is
+unknown. Either the computation succeeds and all is well, or a factor $d$
+is discovered and the computation may be restarted modulo $d$ or $N/d$.
+
+For non-invertible \typ{POLMOD}, this will fail without exhibiting a
+factor.
+\bprog
+? Mod(x^2, x^3-x)^(-1)
+  ***   at top-level: Mod(x^2,x^3-x)^(-1)
+  ***                               ^-----
+  *** _^_: non-invertible polynomial in RgXQ_inv.
+? a = Mod(3,4)*y^3 + Mod(1,4); b = y^6+y^5+y^4+y^3+y^2+y+1;
+? Mod(a, b)^(-1);
+  ***   at top-level: Mod(a,b)^(-1)
+  ***                         ^-----
+  *** _^_: impossible inverse modulo: Mod(0, 4).
+ at eprog\noindent
+In fact the latter polynomial is invertible, but the algorithm used
+(subresultant) assumes the base ring is a domain. If it is not the case,
+as here for $\Z/4\Z$, a result will be correct but chances are an error
+will occur first. In this specific case, one should work with $2$-adics.
+In general, one can try the following approach
+\bprog
+? inversemod(a, b) =
+{ my(m);
+  m = polsylvestermatrix(polrecip(a), polrecip(b));
+  m = matinverseimage(m, matid(#m)[,1]);
+  Polrev( vecextract(m, Str("..", poldegree(b))), variable(b) )
+}
+? inversemod(a,b)
+%2 = Mod(2,4)*y^5 + Mod(3,4)*y^3 + Mod(1,4)*y^2 + Mod(3,4)*y + Mod(2,4)
+ at eprog\noindent
+This is not guaranteed to work either since it must invert pivots. See
+\secref{se:linear_algebra}.
+
+The library syntax is \fun{GEN}{gpow}{GEN x, GEN n, long prec}
+for $x\hbox{\kbd{\pow}}n$.
+
+%SECTION: operators
+
+\subsec{Comparison and Boolean operators}\sidx{Boolean operators} The six
+standard \idx{comparison operators} \kbd{<=}, \kbd{<}, \kbd{>=}, \kbd{>},
+\kbd{==}, \kbd{!=} are available in GP. The result is 1 if the comparison is
+true, 0 if it is false. The operator \kbd{==} is quite liberal : for
+instance, the integer 0, a 0 polynomial, and a vector with 0 entries are all
+tested equal.
+
+The extra operator \kbd{===} tests whether two objects are identical and is
+much stricter than \kbd{==} : objects of different type or length are never
+identical.
+
+For the purpose of comparison, \typ{STR} objects are strictly larger than any
+other non-string type; two \typ{STR} objects are compared using the standard
+lexicographic order.
+
+GP accepts \kbd{<>} as a synonym for \kbd{!=}. On the other hand, \kbd{=} is
+definitely \emph{not} a synonym for \kbd{==}: it is the assignment statement.
+
+The standard boolean operators \kbd{||} (\idx{inclusive or}), \kbd{\&\&}
+(\idx{and})\sidx{or} and \kbd{!} (\idx{not}) are also available.
+
+\section{Conversions and similar elementary functions or commands}
+\label{se:conversion}
+
+\noindent
+Many of the conversion functions are rounding or truncating operations. In
+this case, if the argument is a rational function, the result is the
+Euclidean quotient of the numerator by the denominator, and if the argument
+is a vector or a matrix, the operation is done componentwise. This will not
+be restated for every function.
+
+%SECTION: conversions
+
+\section{Transcendental functions}\label{se:trans}
+
+Since the values of transcendental functions cannot be exactly represented,
+these functions will always return an inexact object: a real number,
+a complex number, a $p$-adic number or a power series.  All these objects
+have a certain finite precision.
+
+As a general rule, which of course in some cases may have exceptions,
+transcendental functions operate in the following way:
+
+\item If the argument is either a real number or an inexact complex number
+(like \kbd{1.0 + I} or \kbd{Pi*I} but not \kbd{2 - 3*I}), then the
+computation is done with the precision of the argument.
+In the example below, we see that changing the precision to $50$ digits does
+not matter, because $x$ only had a precision of $19$ digits.
+\bprog
+? \p 15
+   realprecision = 19 significant digits (15 digits displayed)
+? x = Pi/4
+%1 = 0.785398163397448
+? \p 50
+   realprecision = 57 significant digits (50 digits displayed)
+? sin(x)
+%2 = 0.7071067811865475244
+ at eprog
+
+Note that even if the argument is real, the result may be complex
+(e.g.~$\text{acos}(2.0)$ or $\text{acosh}(0.0)$). See each individual
+function help for the definition of the branch cuts and choice of principal
+value.
+
+\item If the argument is either an integer, a rational, an exact complex
+number or a quadratic number, it is first converted to a real
+or complex number using the current \idx{precision} held in the default
+\tet{realprecision}.  This precision (the number of decimal digits) can be
+changed using \b{p} or \kbd{default(realprecision,...)}).
+After this conversion, the computation proceeds as above for real or complex
+arguments.
+
+In library mode, the \kbd{realprecision} does not matter; instead the
+precision is taken from the \kbd{prec} parameter which every transcendental
+function has.  As in \kbd{gp}, this \kbd{prec} is not used when the
+argument to a function is already inexact.
+Note that the argument \var{prec} stands for the length in words of a real
+number, including codewords. Hence we must have $\var{prec} \geq 3$.
+
+Some accuracies attainable on 32-bit machines cannot be attained
+on 64-bit machines for parity reasons. For example the default \kbd{gp} accuracy
+is 28 decimal digits on 32-bit machines, corresponding to \var{prec} having
+the value 5, but this cannot be attained on 64-bit machines.
+
+\item If the argument is a polmod (representing an algebraic number),
+then the function is evaluated for every possible complex embedding of that
+algebraic number.  A column vector of results is returned, with one component
+for each complex embedding.  Therefore, the number of components equals
+the degree of the \typ{POLMOD} modulus.
+
+\item If the argument is an intmod or a $p$-adic, at present only a
+few functions like \kbd{sqrt} (square root), \kbd{sqr} (square), \kbd{log},
+\kbd{exp}, powering, \kbd{teichmuller} (Teichm\"uller character) and
+\kbd{agm} (arithmetic-geometric mean) are implemented.
+
+Note that in the case of a $2$-adic number, $\kbd{sqr}(x)$ may not be
+identical to $x*x$: for example if $x = 1+O(2^5)$ and $y = 1+O(2^5)$ then
+$x*y = 1+O(2^5)$ while $\kbd{sqr}(x) = 1+O(2^6)$. Here, $x * x$ yields the
+same result as $\kbd{sqr}(x)$ since the two operands are known to be
+\emph{identical}. The same statement holds true for $p$-adics raised to the
+power $n$, where $v_p(n) > 0$.
+
+\misctitle{Remark} If we wanted to be strictly consistent with
+the PARI philosophy, we should have $x*y = (4 \mod 8)$ and $\kbd{sqr}(x) =
+(4 \mod 32)$ when both $x$ and $y$ are congruent to $2$ modulo $4$.
+However, since intmod is an exact object, PARI assumes that the modulus
+must not change, and the result is hence $(0\, \mod\, 4)$ in both cases. On
+the other hand, $p$-adics are not exact objects, hence are treated
+differently.
+
+\item If the argument is a polynomial, a power series or a rational function,
+it is, if necessary, first converted to a power series using the current
+series precision, held in the default \tet{seriesprecision}. This precision
+(the number of significant terms) can be changed using \b{ps} or
+\kbd{default(seriesprecision,...)}. Then the Taylor series expansion of the
+function around $X=0$ (where $X$ is the main variable) is computed to a
+number of terms depending on the number of terms of the argument and the
+function being computed.
+
+Under \kbd{gp} this again is transparent to the user. When programming in
+library mode, however, it is \emph{strongly} advised to perform an explicit
+conversion to a power series first, as in \kbd{x = gtoser(x, seriesprec)},
+where the number of significant terms \kbd{seriesprec} can be specified
+explicitly. If you do not do this, a global variable \kbd{precdl} is used
+instead, to convert polynomials and rational functions to a power series with
+a reasonable number of terms; tampering with the value of this global
+variable is \emph{deprecated} and strongly discouraged.
+
+
+\item If the argument is a vector or a matrix, the result is the
+componentwise evaluation of the function. In particular, transcendental
+functions on square matrices, which are not implemented in the present
+version \vers, will have a different name if they are implemented some day.
+
+\subseckbd{\pow} If $y$ is not of type integer, \kbd{x\pow y} has the same
+effect as \kbd{exp(y*log(x))}. It can be applied to $p$-adic numbers as well
+as to the more usual types.\sidx{powering}
+
+The library syntax is \fun{GEN}{gpow}{GEN x, GEN n, long prec}
+for $x\hbox{\kbd{\pow}}n$.
+
+%SECTION: transcendental
+
+\section{Arithmetic functions}\label{se:arithmetic}
+
+These functions are by definition functions whose natural domain of
+definition is either $\Z$ (or $\Z_{>0}$). The way these functions are used is
+completely different from transcendental functions in that there are no
+automatic type conversions: in general only integers are accepted as
+arguments. An integer argument $N$ can be given in the following alternate
+formats:
+
+\item \typ{MAT}: its factorization \kbd{fa = factor($N$)},
+
+\item \typ{VEC}: a pair \kbd{[$N$, fa]} giving both the integer and
+  its factorization.
+
+This allows to compute different arithmetic functions at a given $N$
+while factoring the latter only once.
+
+\bprog
+  ? N = 10!; faN = factor(N);
+  ? eulerphi(N)
+  %2 = 829440
+  ? eulerphi(faN)
+  %3 = 829440
+  ? eulerphi(S = [N, faN])
+  %4 = 829440
+  ? sigma(S)
+  %5 = 15334088
+ at eprog
+
+\subsec{Arithmetic functions and the factoring engine}
+All arithmetic functions in the narrow sense of the word~--- Euler's
+totient\sidx{Euler totient function} function, the \idx{Moebius} function,
+the sums over divisors or powers of divisors etc.--- call, after trial
+division by small primes, the same versatile factoring machinery described
+under \kbd{factorint}. It includes \idx{Shanks SQUFOF}, \idx{Pollard Rho},
+\idx{ECM} and \idx{MPQS} stages, and has an early exit option for the
+functions \teb{moebius} and (the integer function underlying)
+\teb{issquarefree}. This machinery relies on a fairly strong
+probabilistic primality test, see \kbd{ispseudoprime}, but you may also set
+\bprog
+  default(factor_proven, 1)
+ at eprog\noindent to ensure that all tentative factorizations are fully proven.
+This should not slow down PARI too much, unless prime numbers with
+hundreds of decimal digits occur frequently in your application.
+
+\subsec{Orders in finite groups and Discrete Logarithm functions}
+\label{se:DLfun}
+
+The following functions compute the order of an element in a finite group:
+\kbd{ellorder} (the rational points on an elliptic curve defined over a
+finite field), \kbd{fforder} (the multiplicative group of a finite field),
+\kbd{znorder} (the invertible elements in $\Z/n\Z$). The following functions
+compute discrete logarithms in the same groups (whenever this is meaningful)
+\kbd{elllog}, \kbd{fflog}, \kbd{znlog}.
+
+All such functions allow an optional argument specifying an integer
+$N$, representing the order of the group. (The \emph{order} functions also
+allows any non-zero multiple of the order, with a minor loss of efficiency.)
+That optional argument follows the same format as given above:
+
+\item \typ{INT}: the integer $N$,
+
+\item \typ{MAT}: the factorization \kbd{fa = factor($N$)},
+
+\item \typ{VEC}: this is the preferred format and provides both the
+integer $N$ and its factorization in a two-component vector
+\kbd{[$N$, fa]}.
+
+When the group is fixed and many orders or discrete logarithms will be
+computed, it is much more efficient to initialize this data once and for all
+and pass it to the relevant functions, as in
+\bprog
+? p = nextprime(10^40);
+? v = [p-1, factor(p-1)]; \\ data for discrete log & order computations
+? znorder(Mod(2,p), v)
+%3 = 500000000000000000000000000028
+? g = znprimroot(p);
+? znlog(2, g, v)
+%5 = 543038070904014908801878611374
+ at eprog
+\bigskip
+
+%SECTION: number_theoretical
+
+\section{Functions related to elliptic curves}
+
+\subsec{Elliptic curve structures}
+An elliptic curve is given by a Weierstrass model\sidx{Weierstrass equation}
+$$
+  y^2+a_1xy+a_3y=x^3+a_2x^2+a_4x+a_6,
+$$
+whose discriminant is non-zero. Affine points on \kbd{E} are represented as
+two-component vectors \kbd{[x,y]}; the point at infinity, i.e.~the identity
+element of the group law, is represented by the one-component vector
+\kbd{[0]}.
+
+Given a vector of coefficients $[a_1,a_2,a_3,a_4,a_6]$, the function
+\tet{ellinit} initializes and returns an \tev{ell} structure. (An additional
+optional argument allows to specify the base field in case it cannot be
+inferred from the curve coefficients.) This structure contains data needed by
+elliptic curve related functions, and is generally passed as a first argument.
+Expensive data are skipped on initialization: they will be dynamically
+computed when (and if) needed, and then inserted in the structure. The
+precise layout of the \tev{ell} structure is left undefined and should never
+be used directly. The following \idx{member functions} are available,
+depending on the underlying domain.
+
+\subsubsec{All domains}
+
+\item \tet{a1}, \tet{a2}, \tet{a3}, \tet{a4}, \tet{a6}: coefficients of the
+elliptic curve.
+
+\item \tet{b2}, \tet{b4}, \tet{b6}, \tet{b8}: $b$-invariants of the curve; in
+characteristic $\neq 2$, for $Y = 2y + a_1x+a3$, the curve equation becomes
+$$ Y^2 = 4 x^3 + b_2 x^2 + 2b_4 x + b_6 =: g(x). $$
+
+\item \tet{c4}, \tet{c6}: $c$-invariants of the curve; in characteristic $\neq
+2,3$, for $X = x + b_2/12$ and $Y = 2y + a_1x+a3$, the curve equation becomes
+$$ Y^2 = 4 X^3 - (c_4/12) X - (c_6/216). $$
+
+\item \tet{disc}: discriminant of the curve. This is only required to be
+non-zero, not necessarily a unit.
+
+\item \tet{j}: $j$-invariant of the curve.
+
+\noindent These are used as follows:
+\bprog
+? E = ellinit([0,0,0, a4,a6]);
+? E.b4
+%2 = 2*a4
+? E.disc
+%3 = -64*a4^3 - 432*a6^2
+ at eprog
+
+\subsubsec{Curves over $\R$}
+
+This in particular includes curves defined over $\Q$. All member functions in
+this section return data, as it is currently stored in the structure, if
+present; and otherwise compute it to the default accuracy, that was fixed
+\emph{at the time of ellinit} (via a \typ{REAL} $D$ domain argument, or
+\kbd{realprecision} by default). The function \tet{ellperiods} allows to
+recompute (and cache) the following data to \emph{current}
+\kbd{realprecision}.
+
+\item \tet{area}: volume of the complex lattice defining $E$.
+
+\item \tet{roots} is a vector whose three components contain the complex
+roots of the right hand side $g(x)$ of the associated $b$-model $Y^2 = g(x)$.
+If the roots are all real, they are ordered by decreasing value. If only one
+is real, it is the first component.
+
+\item \tet{omega}: $[\omega_1,\omega_2]$, periods forming a basis of the
+complex lattice defining $E$. The first component $\omega_1$ is the
+(positive) real period, in other words the integral of $dx/(2y+a_1x+a_3)$
+over the connected component of the identity component of $E(\R)$.
+The second component $\omega_2$ is a complex period, such that
+$\tau=\dfrac{\omega_1}{\omega_2}$ belongs to Poincar\'e's
+half-plane (positive imaginary part); not necessarily to the standard
+fundamental domain.
+
+\item \tet{eta} is a row vector containing the quasi-periods $\eta_1$ and
+$\eta_2$ such that $\eta_i = 2\zeta(\omega_i/2)$, where $\zeta$ is the
+Weierstrass zeta function associated to the period lattice; see
+\tet{ellzeta}. In particular, the Legendre relation holds: $\eta_2\omega_1 -
+\eta_1\omega_2 = 2i\pi$.
+
+\misctitle{Warning} As for the orientation of the basis of the period lattice,
+beware that many sources use the inverse convention where $\omega_2/\omega_1$
+has positive imaginary part and our $\omega_2$ is the negative of theirs. Our
+convention $\tau = \omega_1/\omega_2$  ensures that the action of $\text{PSL}_2$ is the natural
+one:
+$$[a,b;c,d]\cdot\tau = (a\tau+b)/(c\tau+d)
+  = (a \omega_1 + b\omega_2)/(c\omega_1 + d\omega_2),$$
+instead of a twisted one. (Our $tau$ is $-1/\tau$ in the above inverse
+convention.)
+
+\subsubsec{Curves over $\Q_p$}
+
+We advise to input a model defined over $\Q$ for such curves. In any case,
+if you input an approximate model with \typ{PADIC} coefficients, it will be
+replaced by a lift to $\Q$ (an exact model ``close'' to the one that was
+input) and all quantities will then be computed in terms of this lifted
+model.
+
+For the time being only curves with multiplicative reduction (split or
+non-split), i.e. $v_p(j) < 0$, are supported by non-trivial functions. In
+this case the curve is analytically isomorphic to $\bar{\Q}_p^*/q^\Z :=
+E_q(\bar{\Q}_p)$, for some $p$-adic integer $q$ (the Tate period). In
+particular, we have $j(q) = j(E)$.
+
+\item \tet{p} is the residual characteristic
+
+\item \tet{roots} is a vector with a single component, equal to the $p$-adic
+root $e_1$ of the right hand side $g(x)$ of the associated $b$-model $Y^2
+= g(x)$. The point $(e_1,0)$ corresponds to $-1 \in \bar{\Q}_p^*/q^\Z$
+under the Tate parametrization.
+
+\item \tet{tate} returns $[u^2,u,q,[a,b]]$ in the notation of Henniart-Mestre
+(CRAS t. 308, p.~391--395, 1989): $q$ is as above, $u\in \Q_p(\sqrt{-c_6})$
+is such that $\phi^* dx/(2y + a_1x+a3) = u dt/t$, where $\phi: E_q\to E$
+is an isomorphism (well defined up to sign) and $dt/t$ is the canonical
+invariant differential on the Tate curve; $u^2\in\Q_p$ does not depend on
+$\phi$. (Technicality: if $u\not\in\Q_p$, it is stored as a quadratic
+\typ{POLMOD}.)
+Finally, $[a,b]$ satisfy $4u^2 b \cdot \text{agm}(\sqrt{a/b},1)^2 = 1$
+as in Theorem~2 (\emph{loc.~cit.}).
+
+\subsubsec{Curves over $\F_q$}
+
+\item \tet{p} is the characteristic of $\F_q$.
+
+\item \tet{no} is $\#E(\F_q)$.
+
+\item \tet{cyc} gives the cycle structure of $E(\F_q)$.
+
+\item \tet{gen} returns the generators of $E(\F_q)$.
+
+\item \tet{group} returns $[\kbd{no},\kbd{cyc},\kbd{gen}]$, i.e. $E(\F_q)$
+as an abelian group structure.
+
+\subsubsec{Curves over $\Q$}
+
+All functions should return a correct result, whether the model is minimal or
+not, but it is a good idea to stick to minimal models whenever
+$\gcd(c_4,c_6)$ is easy to factor (minor speed-up). The construction
+\bprog
+  E = ellminimalmodel(E0, &v)
+ at eprog\noindent replaces the original model $E_0$ by a minimal model $E$,
+and the variable change $v$ allows to go between the two models:
+\bprog
+  ellchangepoint(P0, v)
+  ellchangepointinv(P, v)
+ at eprog\noindent respectively map the point $P_0$ on $E_0$ to its image on
+$E$, and the point $P$ on $E$ to its pre-image on $E_0$.
+
+A few routines --- namely \tet{ellgenerators}, \tet{ellidentify},
+\tet{ellsearch}, \tet{forell} --- require the optional package \tet{elldata}
+(John Cremona's database) to be installed. In that case, the function
+\tet{ellinit} will allow alternative inputs, e.g.~\kbd{ellinit("11a1")}.
+Functions using this package need to load chunks of a large database in
+memory and require at least 2MB stack to avoid stack overflows.
+
+\item \tet{gen} returns the generators of $E(\Q)$, if known (from John
+  Cremona's database)
+
+%SECTION: elliptic_curves
+
+\section{Functions related to general number fields}
+
+In this section can be found functions which are used almost exclusively for
+working in general number fields. Other less specific functions can be found
+in the next section on polynomials. Functions related to quadratic number
+fields are found in section \secref{se:arithmetic} (Arithmetic functions).
+
+\subsec{Number field structures}
+
+Let $K = \Q[X] / (T)$ a number field, $\Z_K$ its ring of integers, $T\in\Z[X]$
+is monic. Three basic number field structures can be associated to $K$ in
+GP:
+
+\item $\tev{nf}$ denotes a number field, i.e.~a data structure output by
+\tet{nfinit}. This contains the basic arithmetic data associated to the
+number field: signature, maximal order (given by a basis \kbd{nf.zk}),
+discriminant, defining polynomial $T$, etc.
+
+\item $\tev{bnf}$ denotes a ``Buchmann's number field'', i.e.~a
+data structure output by \tet{bnfinit}. This contains
+$\var{nf}$ and the deeper invariants of the field: units $U(K)$, class group
+$\Cl(K)$, as well as technical data required to solve the two associated
+discrete logarithm problems.
+
+\item $\tev{bnr}$ denotes a ``ray number field'', i.e.~a data structure
+output by \kbd{bnrinit}, corresponding to the ray class group structure of
+the field, for some modulus $f$. It contains a \var{bnf}, the modulus
+$f$, the ray class group $\Cl_f(K)$ and data associated to
+the discrete logarithm problem therein.
+
+\subsec{Algebraic numbers and ideals}
+
+\noindent An \tev{algebraic number} belonging to $K = \Q[X]/(T)$ is given as
+
+\item a \typ{INT}, \typ{FRAC} or \typ{POL} (implicitly modulo $T$), or
+
+\item a \typ{POLMOD} (modulo $T$), or
+
+\item a \typ{COL}~\kbd{v} of dimension $N = [K:\Q]$, representing
+the element in terms of the computed integral basis, as
+\kbd{sum(i = 1, N,~v[i] * nf.zk[i])}. Note that a \typ{VEC}
+will not be recognized.
+\medskip
+
+\noindent An \tev{ideal} is given in any of the following ways:
+
+\item an algebraic number in one of the above forms, defining a principal ideal.
+
+\item a prime ideal, i.e.~a 5-component vector in the format output by
+\kbd{idealprimedec} or \kbd{idealfactor}.
+
+\item a \typ{MAT}, square and in Hermite Normal Form (or at least
+upper triangular with non-negative coefficients), whose columns represent a
+$\Z$-basis of the ideal.
+
+One may use \kbd{idealhnf} to convert any ideal to the last (preferred) format.
+
+\item an \emph{extended ideal} \sidx{ideal (extended)} is a 2-component
+vector $[I, t]$, where $I$ is an ideal as above and $t$ is an algebraic
+number, representing the ideal $(t)I$. This is useful whenever \tet{idealred}
+is involved, implicitly working in the ideal class group, while keeping track
+of principal ideals. Ideal operations suitably update the principal part
+when it makes sense (in a multiplicative context), e.g.~using \kbd{idealmul}
+on $[I,t]$, $[J,u]$, we obtain $[IJ, tu]$. When it does not make sense, the
+extended part is silently discarded, e.g.~using \kbd{idealadd} with the above
+input produces $I+J$.
+
+The ``principal part'' $t$ in an extended ideal may be
+represented in any of the above forms, and \emph{also} as a factorization
+matrix (in terms of number field elements, not ideals!), possibly the empty
+matrix \kbd{[;]} representing $1$. In the latter case, elements stay in
+factored form, or \tev{famat} for \emph{fa}ctorization \emph{mat}rix, which
+is a convenient way to avoid coefficient explosion. To recover the
+conventional expanded form, try \tet{nffactorback}; but many functions
+already accept \var{famat}s as input, for instance \tet{ideallog}, so
+expanding huge elements should never be necessary.
+
+\subsec{Finite abelian groups}
+
+A finite abelian group $G$ in user-readable format is given by its Smith
+Normal Form as a pair $[h,d]$ or triple $[h,d,g]$.
+Here $h$ is the cardinality of $G$, $(d_i)$ is the vector of elementary
+divisors, and $(g_i)$ is a vector of generators. In short,
+$G = \oplus_{i\leq n} (\Z/d_i\Z) g_i$, with $d_n \mid \dots \mid d_2 \mid d_1$
+and $\prod d_i = h$. This information can also be retrieved as
+$G.\kbd{no}$, $G.\kbd{cyc}$ and $G.\kbd{gen}$.
+
+\item a \tev{character} on the abelian group
+$\oplus (\Z/d_i\Z) g_i$
+is given by a row vector $\chi = [a_1,\ldots,a_n]$ such that
+$\chi(\prod g_i^{n_i}) = \exp(2i\pi\sum a_i n_i / d_i)$.
+
+\item given such a structure, a \tev{subgroup} $H$ is input as a square
+matrix in HNF, whose columns express generators of $H$ on the given generators
+$g_i$. Note that the determinant of that matrix is equal to the index $(G:H)$.
+
+\subsec{Relative extensions}
+
+We now have a look at data structures associated to relative extensions
+of number fields $L/K$, and to projective $\Z_K$-modules. When defining a
+relative extension $L/K$, the $\var{nf}$ associated to the base field $K$
+must be defined by a variable having a lower priority (see
+\secref{se:priority}) than the variable defining the extension. For example,
+you may use the variable name $y$ to define the base field $K$, and $x$ to
+define the relative extension $L/K$.
+
+\subsubsec{Basic definitions}\label{se:ZKmodules}
+
+\item $\tev{rnf}$ denotes a relative number field, i.e.~a data structure
+output by \kbd{rnfinit}, associated to the extension $L/K$. The \var{nf}
+associated to be base field $K$ is \kbd{rnf.nf}.
+
+\item A \emph{relative matrix} is an $m\times n$ matrix whose entries are
+elements of $K$, in any form. Its $m$ columns $A_j$ represent elements
+in $K^n$.
+
+\item An \tev{ideal list} is a row vector of fractional ideals of the number
+field $\var{nf}$.
+
+\item A \tev{pseudo-matrix} is a 2-component row vector $(A,I)$ where $A$
+is a relative $m\times n$ matrix and $I$ an ideal list of length $n$. If $I =
+\{{\Bbb a}_1,\dots, {\Bbb a}_n\}$ and the columns of $A$ are $(A_1,\dots,
+A_n)$, this data defines the torsion-free (projective) $\Z_K$-module ${\Bbb
+a}_1 A_1\oplus {\Bbb a}_n A_n$.
+
+\item An \tev{integral pseudo-matrix} is a 3-component row vector w$(A,I,J)$
+where $A = (a_{i,j})$ is an $m\times n$ relative matrix and $I = ({\Bbb
+b}_1,\dots, {\Bbb b}_m)$, $J = ({\Bbb a}_1,\dots, {\Bbb a}_n)$ are ideal
+lists, such that $a_{i,j} \in {\Bbb b}_i {\Bbb a}_j^{-1}$ for all $i,j$. This
+data defines two abstract projective $\Z_K$-modules $N = {\Bbb
+a_1}\omega_1\oplus \cdots\oplus {\Bbb a_n}\omega_n $ in $K^n$, $P = {\Bbb
+b_1}\eta_1\oplus \cdots\oplus {\Bbb b_m}\eta_m$ in $K^m$, and a $\Z_K$-linear
+map $f:N\to P$ given by
+$$ f(\sum \alpha_j\omega_j) = \sum_i \Big(a_{i,j}\alpha_j\Big) \eta_i.$$
+This data defines the $\Z_K$-module $M = P/f(N)$.
+
+\item Any \emph{projective} $\Z_K$-module\varsidx{projective module} $M$
+of finite type in $K^m$ can be given by a pseudo matrix $(A,I)$.
+
+\item An arbitrary $\Z_K$ modules of finite type in $K^m$, with non-trivial
+torsion, is given by an integral pseudo-matrix $(A,I,J)$
+
+\subsubsec{Pseudo-bases, determinant}
+
+\item The pair $(A,I)$ is a \tev{pseudo-basis} of the module it
+generates if the ${\Bbb a_j}$ are non-zero, and the $A_j$ are $K$-linearly
+independent. We call $n$ the \emph{size} of the pseudo-basis. If $A$ is a
+relative matrix, the latter condition means it is square with non-zero
+determinant; we say that it is in Hermite Normal Form\sidx{Hermite normal
+form} (HNF) if it is upper triangular and all the elements of the diagonal
+are equal to 1.
+
+\item For instance, the relative integer basis \kbd{rnf.zk} is a pseudo-basis
+$(A,I)$ of $\Z_L$, where $A = \kbd{rnf.zk[1]}$ is a vector of elements of $L$,
+which are $K$-linearly independent. Most \var{rnf} routines return and handle
+$\Z_K$-modules contained in $L$ (e.g.~$\Z_L$-ideals) via a pseudo-basis
+$(A',I')$, where $A'$ is a relative matrix representing a vector of elements of
+$L$ in terms of the fixed basis \kbd{rnf.zk[1]}
+
+\item The \emph{determinant} of a pseudo-basis $(A,I)$ is the ideal
+equal to the product of the determinant of $A$ by all the ideals of $I$. The
+determinant of a pseudo-matrix is the determinant of any pseudo-basis of the
+module it generates.
+
+\subsec{Class field theory}\label{se:CFT}
+
+A $\tev{modulus}$, in the sense of class field theory, is a divisor supported
+on the non-complex places of $K$. In PARI terms, this means either an
+ordinary ideal $I$ as above (no Archimedean component), or a pair $[I,a]$,
+where $a$ is a vector with $r_1$ $\{0,1\}$-components, corresponding to the
+infinite part of the divisor. More precisely, the $i$-th component of $a$
+corresponds to the real embedding associated to the $i$-th real root of
+\kbd{K.roots}. (That ordering is not canonical, but well defined once a
+defining polynomial for $K$ is chosen.) For instance, \kbd{[1, [1,1]]} is a
+modulus for a real quadratic field, allowing ramification at any of the two
+places at infinity, and nowhere else.
+
+A \tev{bid} or ``big ideal'' is a structure output by \kbd{idealstar}
+needed to compute in $(\Z_K/I)^*$, where $I$ is a modulus in the above sense.
+It is a finite abelian group as described above, supplemented by
+technical data needed to solve discrete log problems.
+
+Finally we explain how to input ray number fields (or \var{bnr}), using class
+field theory. These are defined by a triple $A$, $B$, $C$, where the
+defining set $[A,B,C]$ can have any of the following forms: $[\var{bnr}]$,
+$[\var{bnr},\var{subgroup}]$, $[\var{bnf},\var{mod}]$,
+$[\var{bnf},\var{mod},\var{subgroup}]$. The last two forms are kept for
+backward compatibility, but no longer serve any real purpose (see example
+below); no newly written function will accept them.
+
+\item $\var{bnf}$ is as output by \kbd{bnfinit}, where units are mandatory
+unless the modulus is trivial; \var{bnr} is as output by \kbd{bnrinit}. This
+is the ground field $K$.
+
+\item \emph{mod} is a modulus $\goth{f}$, as described above.
+
+\item \emph{subgroup} a subgroup of the ray class group modulo $\goth{f}$ of
+$K$. As described above, this is input as a square matrix expressing
+generators of a subgroup of the ray class group \kbd{\var{bnr}.clgp} on the
+given generators.
+
+The corresponding \var{bnr} is the subfield of the ray class field of $K$
+modulo $\goth{f}$, fixed by the given subgroup.
+
+\bprog
+  ? K = bnfinit(y^2+1);
+  ? bnr = bnrinit(K, 13)
+  ? %.clgp
+  %3 = [36, [12, 3]]
+  ? bnrdisc(bnr); \\ discriminant of the full ray class field
+  ? bnrdisc(bnr, [3,1;0,1]); \\ discriminant of cyclic cubic extension of K
+ at eprog\noindent
+We could have written directly
+\bprog
+  ? bnrdisc(K, 13);
+  ? bnrdisc(K, 13, [3,1;0,1]);
+ at eprog\noindent
+avoiding one \tet{bnrinit}, but this would actually be slower since the
+\kbd{bnrinit} is called internally anyway. And now twice!
+
+\subsec{General use}
+
+All the functions which are specific to relative extensions, number fields,
+Buchmann's number fields, Buchmann's number rays, share the prefix \kbd{rnf},
+\kbd{nf}, \kbd{bnf}, \kbd{bnr} respectively. They take as first argument a
+number field of that precise type, respectively output by \kbd{rnfinit},
+\kbd{nfinit}, \kbd{bnfinit}, and \kbd{bnrinit}.
+
+However, and even though it may not be specified in the descriptions of the
+functions below, it is permissible, if the function expects a $\var{nf}$, to
+use a $\var{bnf}$ instead, which contains much more information. On the other
+hand, if the function requires a \kbd{bnf}, it will \emph{not} launch
+\kbd{bnfinit} for you, which is a costly operation. Instead, it will give you
+a specific error message. In short, the types
+$$ \kbd{nf} \leq \kbd{bnf} \leq \kbd{bnr}$$
+are ordered, each function requires a minimal type to work properly, but you
+may always substitute a larger type.
+
+The data types corresponding to the structures described above are rather
+complicated. Thus, as we already have seen it with elliptic curves, GP
+provides ``member functions'' to retrieve data from these structures (once
+they have been initialized of course). The relevant types of number fields
+are indicated between parentheses: \smallskip
+
+\sidx{member functions}
+\settabs\+xxxxxxx&(\var{bnr},x&\var{bnf},x&nf\hskip2pt&)x&: &\cr
+\+\tet{bid}    &(\var{bnr}&&&)&: & bid ideal structure.\cr
+
+\+\tet{bnf}    &(\var{bnr},& \var{bnf}&&)&: & Buchmann's number field.\cr
+
+\+\tet{clgp}  &(\var{bnr},& \var{bnf}&&)&: & classgroup. This one admits the
+following three subclasses:\cr
+
+\+      \quad \tet{cyc} &&&&&: & \quad cyclic decomposition
+ (SNF)\sidx{Smith normal form}.\cr
+
+\+      \quad \kbd{gen}\sidx{gen (member function)} &&&&&: &
+ \quad generators.\cr
+
+\+      \quad \tet{no}  &&&&&: & \quad number of elements.\cr
+
+\+\tet{diff}  &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the different ideal.\cr
+
+\+\tet{codiff}&(\var{bnr},& \var{bnf},& \var{nf}&)&: & the codifferent
+(inverse of the different in the ideal group).\cr
+
+\+\tet{disc} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & discriminant.\cr
+
+\+\tet{fu}   &(\var{bnr},& \var{bnf}&&)&: & \idx{fundamental units}.\cr
+
+\+\tet{index}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: &
+ \idx{index} of the power order in the ring of integers.\cr
+
+\+\tet{mod}   &(\var{bnr}&&&)&: & modulus.\cr
+
+\+\tet{nf}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & number field.\cr
+
+\+\tet{pol}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & defining polynomial.\cr
+
+\+\tet{r1} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the number
+of real embeddings.\cr
+
+\+\tet{r2} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the number
+of pairs of complex embeddings.\cr
+
+\+\tet{reg}  &(\var{bnr},& \var{bnf}&&)&: & regulator.\cr
+
+\+\tet{roots}&(\var{bnr},& \var{bnf},& \var{nf}&)&: & roots of the
+polynomial generating the field.\cr
+
+\+\tet{sign} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & signature $[r1,r2]$.\cr
+
+\+\tet{t2}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the $T_2$ matrix (see
+\kbd{nfinit}).\cr
+
+\+\tet{tu}   &(\var{bnr},& \var{bnf}&&)&: & a generator for the torsion
+units.\cr
+
+\+\tet{zk}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & integral basis, i.e.~a
+$\Z$-basis of the maximal order.\cr
+
+\+\tet{zkst}   &(\var{bnr}&&&)&: & structure of $(\Z_K/m)^*$.\cr
+
+\misctitle{Deprecated} The following member functions are still available, but deprecated and should not be used in new scripts :
+\+\tet{futu} &(\var{bnr},& \var{bnf},&&)&: &
+ $[u_1,...,u_r,w]$, $(u_i)$ is a vector of fundamental units,\cr
+\+&&&&&& $w$ generates the torsion units.\cr
+
+\+\tet{tufu} &(\var{bnr},& \var{bnf},&&)&: &
+ $[w,u_1,...,u_r]$, $(u_i)$ is a vector of fundamental units,\cr
+\+&&&&&& $w$ generates the torsion units.\cr
+
+
+  For instance, assume that $\var{bnf} = \kbd{bnfinit}(\var{pol})$, for some
+polynomial. Then \kbd{\var{bnf}.clgp} retrieves the class group, and
+\kbd{\var{bnf}.clgp.no} the class number. If we had set $\var{bnf} =
+\kbd{nfinit}(\var{pol})$, both would have output an error message. All these
+functions are completely recursive, thus for instance
+\kbd{\var{bnr}.bnf.nf.zk} will yield the maximal order of \var{bnr}, which
+you could get directly with a simple \kbd{\var{bnr}.zk}.
+
+\subsec{Class group, units, and the GRH}\label{se:GRHbnf}
+
+Some of the functions starting with \kbd{bnf} are implementations of the
+sub-exponential algorithms for finding class and unit groups under \idx{GRH},
+due to Hafner-McCurley, \idx{Buchmann} and Cohen-Diaz-Olivier. The general
+call to the functions concerning class groups of general number fields
+(i.e.~excluding \kbd{quadclassunit}) involves a polynomial $P$ and a
+technical vector
+$$\var{tech} = [c_1, c_2, \var{nrpid} ],$$
+where the parameters are to be understood as follows:
+
+$P$ is the defining polynomial for the number field, which must be in
+$\Z[X]$, irreducible and monic. In fact, if you supply a non-monic polynomial
+at this point, \kbd{gp} issues a warning, then \emph{transforms your
+polynomial} so that it becomes monic. The \kbd{nfinit} routine
+will return a different result in this case: instead of \kbd{res}, you get a
+vector \kbd{[res,Mod(a,Q)]}, where \kbd{Mod(a,Q) = Mod(X,P)} gives the change
+of variables. In all other routines, the variable change is simply lost.
+
+The \var{tech} interface is obsolete and you should not tamper with
+these parameters. Indeed, from version 2.4.0 on,
+
+\item the results are always rigorous under \idx{GRH} (before that version,
+they relied on a heuristic strengthening, hence the need for overrides).
+
+\item the influence of these parameters on execution time and stack size is
+marginal. They \emph{can} be useful to fine-tune and experiment with the
+\kbd{bnfinit} code, but you will be better off modifying all tuning
+parameters in the C code (there are many more than just those three).
+We nevertheless describe it for completeness.
+
+The numbers $c_1 \leq c_2$ are non-negative real numbers. By default they are
+chosen so that the result is correct under GRH. For $i = 1,2$, let
+$B_i = c_i(\log |d_K|)^2$, and denote by $S(B)$ the set of maximal ideals of
+$K$ whose norm is less than $B$. We want $S(B_1)$ to generate $\Cl(K)$ and hope
+that $S(B_2)$ can be \emph{proven} to generate $\Cl(K)$.
+
+More precisely, $S(B_1)$ is a factorbase used to compute a tentative
+$\Cl(K)$ by generators and relations. We then check explicitly, using
+essentially \kbd{bnfisprincipal}, that the elements of $S(B_2)$ belong to the
+span of $S(B_1)$. Under the assumption that $S(B_2)$ generates $\Cl(K)$, we
+are done. User-supplied $c_i$ are only used to compute initial guesses for
+the bounds $B_i$, and the algorithm increases them until one can \emph{prove}
+under GRH that $S(B_2)$ generates $\Cl(K)$. A uniform result of Bach says
+that $c_2 = 12$ is always suitable, but this bound is very pessimistic and a
+direct algorithm due to Belabas-Diaz-Friedman is used to check the condition,
+assuming GRH. The default values are $c_1 = c_2 = 0$. When $c_1$ is equal to
+$0$ the algorithm takes it equal to $c_2$.
+
+$\var{nrpid}$ is the maximal number of small norm relations associated to each
+ideal in the factor base. Set it to $0$ to disable the search for small norm
+relations. Otherwise, reasonable values are between 4 and 20. The default is
+4.
+
+\misctitle{Warning} Make sure you understand the above! By default, most of
+the \kbd{bnf} routines depend on the correctness of the GRH. In particular,
+any of the class number, class group structure, class group generators,
+regulator and fundamental units may be wrong, independently of each other.
+Any result computed from such a \kbd{bnf} may be wrong. The only guarantee is
+that the units given generate a subgroup of finite index in the full unit
+group. You must use \kbd{bnfcertify} to certify the computations
+unconditionally.
+
+\misctitle{Remarks}
+
+You do not need to supply the technical parameters (under the library you
+still need to send at least an empty vector, coded as \kbd{NULL}). However,
+should you choose to set some of them, they \emph{must} be given in the
+requested order. For example, if you want to specify a given value of
+\var{nrpid}, you must give some values as well for $c_1$ and $c_2$, and provide
+a vector $[c_1,c_2,\var{nrpid}]$.
+
+Note also that you can use an $\var{nf}$ instead of $P$, which avoids
+recomputing the integral basis and analogous quantities.
+
+\smallskip
+
+%SECTION: number_fields
+
+\section{Polynomials and power series}
+
+We group here all functions which are specific to polynomials or power
+series. Many other functions which can be applied on these objects are
+described in the other sections. Also, some of the functions described here
+can be applied to other types.
+
+%SECTION: polynomials
+
+\section{Vectors, matrices, linear algebra and sets}
+\label{se:linear_algebra}
+
+Note that most linear algebra functions operating on subspaces defined by
+generating sets (such as \tet{mathnf}, \tet{qflll}, etc.) take matrices as
+arguments. As usual, the generating vectors are taken to be the
+\emph{columns} of the given matrix.
+
+Since PARI does not have a strong typing system, scalars live in
+unspecified commutative base rings. It is very difficult to write
+robust linear algebra routines in such a general setting. We thus
+assume that the base ring is a domain and work over its field of
+fractions. If the base ring is \emph{not} a domain, one gets an error as soon
+as a non-zero pivot turns out to be non-invertible. Some functions,
+e.g.~\kbd{mathnf} or \kbd{mathnfmod}, specifically assume that the base ring is
+$\Z$.
+
+%SECTION: linear_algebra
+
+\section{Sums, products, integrals and similar functions}
+\label{se:sums}
+
+Although the \kbd{gp} calculator is programmable, it is useful to have
+a number of preprogrammed loops, including sums, products, and a certain
+number of recursions. Also, a number of functions from numerical analysis
+like numerical integration and summation of series will be described here.
+
+One of the parameters in these loops must be the control variable, hence a
+simple variable name. In the descriptions, the letter $X$ will always denote
+any simple variable name, and represents the formal parameter used in the
+function. The expression to be summed, integrated, etc. is any legal PARI
+expression, including of course expressions using loops.
+
+\misctitle{Library mode}
+Since it is easier to program directly the loops in library mode, these
+functions are mainly useful for GP programming. On the other hand, numerical
+routines code a function (to be integrated, summed, etc.) with two parameters
+named
+\bprog
+  GEN (*eval)(void*,GEN)
+  void *E;  \\ context: eval(E, x) must evaluate your function at x.
+ at eprog\noindent
+see the Libpari manual for details.
+
+\misctitle{Numerical integration}\sidx{numerical integration}
+Starting with version 2.2.9 the ``double exponential'' univariate
+integration method is implemented in \tet{intnum} and its variants. Romberg
+integration is still available under the name \kbd{intnumromb}, but
+superseded. It is possible to compute numerically integrals to thousands of
+decimal places in reasonable time, as long as the integrand is regular. It is
+also reasonable to compute numerically integrals in several variables,
+although more than two becomes lengthy. The integration domain may be
+non-compact, and the integrand may have reasonable singularities at
+endpoints. To use \kbd{intnum}, you must split the integral into a sum
+of subintegrals where the function has no singularities except at the
+endpoints. Polynomials in logarithms are not considered singular, and
+neglecting these logs, singularities are assumed to be algebraic (asymptotic
+to $C(x-a)^{-\alpha}$ for some $\alpha > -1$ when $x$ is
+close to $a$), or to correspond to simple discontinuities of some (higher)
+derivative of the function. For instance, the point $0$ is a singularity of
+$\text{abs}(x)$.
+
+See also the discrete summation methods below, sharing the prefix \kbd{sum}.
+
+%SECTION: sums
+
+\section{Plotting functions}
+
+  Although plotting is not even a side purpose of PARI, a number of plotting
+functions are provided. Moreover, a lot of people
+\footnote{*}{Among these, special thanks go to Klaus-Peter Nischke who
+suggested the recursive plotting and forking/resizing stuff the graphical
+window, and Ilya Zakharevich who rewrote the graphic code from scratch
+implementing many new primitives (splines, clipping). Nils Skoruppa and Bill
+Allombert wrote the \tet{Qt} and \tet{fltk} graphic drivers respectively.}
+suggested ideas or submitted patches for this section of the code. There are
+three types of graphic functions.
+
+\subsec{High-level plotting functions} (all the functions starting with
+\kbd{ploth}) in which the user has little to do but explain what type of plot
+he wants, and whose syntax is similar to the one used in the preceding
+section.
+
+\subsec{Low-level plotting functions} (called \var{rectplot} functions,
+sharing the prefix \kbd{plot}), where every drawing primitive (point, line,
+box, etc.) is specified by the user. These low-level functions work as
+follows. You have at your disposal 16 virtual windows which are filled
+independently, and can then be physically ORed on a single window at
+user-defined positions. These windows are numbered from 0 to 15, and must be
+initialized before being used by the function \kbd{plotinit}, which specifies
+the height and width of the virtual window (called a \var{rectwindow} in the
+sequel). At all times, a virtual cursor (initialized at $[0,0]$) is associated
+to the window, and its current value can be obtained using the function
+\kbd{plotcursor}.
+
+A number of primitive graphic objects (called \var{rect} objects) can then
+be drawn in these windows, using a default color associated to that window
+(which can be changed using the \kbd{plotcolor} function) and only the part
+of the object which is inside the window will be drawn, with the exception of
+polygons and strings which are drawn entirely. The ones sharing the prefix
+\kbd{plotr} draw relatively to the current position of the virtual cursor,
+the others use absolute coordinates. Those having the prefix \kbd{plotrecth}
+put in the rectwindow a large batch of rect objects corresponding to the
+output of the related \kbd{ploth} function.
+
+   Finally, the actual physical drawing is done using \kbd{plotdraw}. The
+rectwindows are preserved so that further drawings using the same windows at
+different positions or different windows can be done without extra work. To
+erase a window, use \kbd{plotkill}. It is not possible to partially erase a
+window: erase it completely, initialize it again, then fill it with the
+graphic objects that you want to keep.
+
+   In addition to initializing the window, you may use a scaled window to
+avoid unnecessary conversions. For this, use \kbd{plotscale}. As long as this
+function is not called, the scaling is simply the number of pixels, the
+origin being at the upper left and the $y$-coordinates going downwards.
+
+   Plotting functions are platform independent, but a number of graphical
+drivers are available for screen output: X11-windows (hence also for GUI's
+based on X11 such as Openwindows and Motif), and the Qt and FLTK graphical
+libraries. The physical window opened by \kbd{plotdraw} or any of the
+\kbd{ploth*} functions is completely separated from \kbd{gp} (technically, a
+\kbd{fork} is done, and the non-graphical memory is immediately freed in the
+child process), which means you can go on working in the current \kbd{gp}
+session, without having to kill the window first. This window can be closed,
+enlarged or reduced using the standard window manager functions. No zooming
+procedure is implemented though (yet).
+
+\subsec{Functions for PostScript output} in the same way that \kbd{printtex} allows you to have a \TeX\ output
+corresponding to printed results, the functions starting with \kbd{ps} allow
+you to have \tet{PostScript} output of the plots. This will not be identical
+with the screen output, but sufficiently close. Note that you can use
+PostScript output even if you do not have the plotting routines enabled. The
+PostScript output is written in a file whose name is derived from the
+\tet{psfile} default (\kbd{./pari.ps} if you did not tamper with it). Each
+time a new PostScript output is asked for, the PostScript output is appended
+to that file. Hence you probably want to remove this file, or change the
+value of \kbd{psfile}, in between plots. On the other hand, in this manner,
+as many plots as desired can be kept in a single file. \smallskip
+
+\subsec{Library mode} \emph{None of the graphic functions are available
+within the PARI library, you must be under \kbd{gp} to use them}. The reason
+for that is that you really should not use PARI for heavy-duty graphical work,
+there are better specialized alternatives around. This whole set of routines
+was only meant as a convenient, but simple-minded, visual aid. If you really
+insist on using these in your program (we warned you), the source
+(\kbd{plot*.c}) should be readable enough for you to achieve something.
+
+%SECTION: graphic
+
+\section{Programming in GP: control statements}
+\sidx{programming}\label{se:programming}
+
+  A number of control statements are available in GP. They are simpler and
+have a syntax slightly different from their C counterparts, but are quite
+powerful enough to write any kind of program. Some of them are specific to
+GP, since they are made for number theorists. As usual, $X$ will denote any
+simple variable name, and \var{seq} will always denote a sequence of
+expressions, including the empty sequence.
+
+\misctitle{Caveat} In constructs like
+\bprog
+    for (X = a,b, seq)
+ at eprog\noindent
+the variable \kbd{X} is lexically scoped to the loop, leading to possibly
+unexpected behavior:
+\bprog
+    n = 5;
+    for (n = 1, 10,
+      if (something_nice(), break);
+    );
+    \\ @com at this point \kbd{n} is 5 !
+ at eprog\noindent
+If the sequence \kbd{seq} modifies the loop index, then the loop
+is modified accordingly:
+\bprog
+    ? for (n = 1, 10, n += 2; print(n))
+    3
+    6
+    9
+    12
+ at eprog
+
+%SECTION: programming/control
+
+\section{Programming in GP: other specific functions}
+\label{se:gp_program}
+
+  In addition to the general PARI functions, it is necessary to have some
+functions which will be of use specifically for \kbd{gp}, though a few of these can
+be accessed under library mode. Before we start describing these, we recall
+the difference between \emph{strings} and \emph{keywords} (see
+\secref{se:strings}): the latter don't get expanded at all, and you can type
+them without any enclosing quotes. The former are dynamic objects, where
+everything outside quotes gets immediately expanded.
+
+%SECTION: programming/specific
+
+\section{Parallel programming}
+
+These function are only available if PARI was configured using \kbd{Configure
+--mt=\dots}. Two multithread interfaces are supported:
+
+\item POSIX threads
+
+\item Message passing interface (MPI)
+
+As a rule, POSIX threads are well-suited for single systems, while MPI is used
+by most clusters. However the parallel GP interface does not depend on the
+chosen multithread interface: a properly written GP program will work
+identically with both.
+
+%SECTION: programming/parallel
+
+\section{GP defaults}
+\label{se:gp_defaults} This section documents the GP defaults
+
+%SECTION: default
+
+\vfill\eject
diff --git a/doc/usersch1.tex b/doc/usersch1.tex
new file mode 100644
index 0000000..12a057d
--- /dev/null
+++ b/doc/usersch1.tex
@@ -0,0 +1,447 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Overview of the PARI system}
+
+\section{Introduction}
+
+\noindent
+PARI/GP is a specialized computer algebra system, primarily aimed at number
+theorists, but has been put to good use in many other different fields, from
+topology or numerical analysis to physics.
+
+Although quite an amount of symbolic manipulation is possible, PARI does
+badly compared to systems like Axiom, Magma, Maple, Mathematica, Maxima, or
+Reduce on such tasks, e.g.~multivariate polynomials, formal integration,
+etc. On the other hand, the three main advantages of the system are its
+speed, the possibility of using directly data types which are familiar to
+mathematicians, and its extensive algebraic number theory module (from
+the above-mentioned systems, only Magma provides similar features).
+
+Non-mathematical strong points include the possibility to program either
+in high-level scripting languages or with the PARI library, a mature system
+(development started in the mid eighties) that was used to conduct and
+disseminate original mathematical research, while building a large user
+community, linked by helpful mailing lists and a tradition of great user
+support from the developers. And, of course, PARI/GP is Free Software,
+covered by the GNU General Public License, either version 2 of the License or
+(at your option) any later version.
+
+PARI is used in three different ways:
+
+\quad 1) as a library \tet{libpari}, which can be called from an upper-level
+language application, for instance written in ANSI C or C$++$;
+
+\quad 2) as a sophisticated programmable calculator, named \tet{gp}, whose
+language \tet{GP} contains most of the control instructions of a standard
+language like C;
+
+\quad 3) the compiler \tet{gp2c} translates GP code to C, and loads it into
+the \kbd{gp} interpreter. A typical script compiled by \kbd{gp2c} runs 3 to 10
+times faster. The generated C code can be edited and optimized by hand. It
+may also be used as a tutorial to \kbd{libpari} programming.
+
+The present Chapter 1 gives an overview of the PARI/GP system; \kbd{gp2c} is
+distributed separately and comes with its own manual. Chapter 2 describes the
+\kbd{GP} programming language and the \kbd{gp} calculator. Chapter 3
+describes all routines available in the calculator. Programming in library
+mode is explained in Chapters 4 and 5 in a separate booklet: \emph{User's
+Guide to the PARI library} (\kbd{libpari.dvi}.
+
+A tutorial for \kbd{gp} is provided in the standard distribution: \emph{A
+tutorial for PARI/GP} (\kbd{tutorial.dvi}) and you should read this first.
+You can then start over and read the more boring stuff which lies ahead. You
+can have a quick idea of what is available by looking at the \kbd{gp}
+reference card (\kbd{refcard.dvi} or \kbd{refcard.ps}). In case of need, you
+can refer to the complete function description in Chapter 3.
+
+\subsectitle{How to get the latest version} Everything can be found on
+PARI's home page:
+\bprog
+  http://pari.math.u-bordeaux.fr/
+ at eprog\noindent From that point you may access all sources, some binaries,
+version information, the complete mailing list archives, frequently asked
+questions and various tips. All threaded and fully searchable.
+
+\subsectitle{How to report bugs} Bugs are submitted online to our Bug
+Tracking System, available from PARI's home page, or directly from the URL
+\bprog
+  http://pari.math.u-bordeaux.fr/Bugs/
+ at eprog\noindent Further instructions can be found on that page.
+
+\section{Multiprecision kernels / Portability}
+
+The PARI multiprecision kernel comes in three non exclusive flavors. See
+Appendix~A for how to set up these on your system; various compilers are
+supported, but the GNU \kbd{gcc} compiler is the definite favorite.
+
+A first version is written entirely in ANSI C, with a C++-compatible syntax,
+and should be portable without trouble to any 32 or 64-bit computer having no
+drastic memory constraints. We do not know any example of a computer where a
+port was attempted and failed.
+
+In a second version, time-critical parts of the kernel are written in
+inlined assembler. At present this includes
+
+\item the whole ix86 family (Intel, AMD, Cyrix) starting at the 386, up to
+the Xbox gaming console, including the Opteron 64 bit processor.
+
+\item three versions for the Sparc architecture: version 7, version 8 with
+SuperSparc processors, and version 8 with MicroSparc I or II processors.
+UltraSparcs use the MicroSparc II version;
+
+\item the DEC Alpha 64-bit processor;
+
+\item the Intel Itanium 64-bit processor;
+
+\item the PowerPC equipping old macintoshs (G3, G4, etc.);
+
+\item the HPPA processors (both 32 and 64 bit);
+
+A third version uses the GNU MP library to implement most of its
+multiprecision kernel. It improves significantly on the native one for large
+operands, say 100 decimal digits of accuracy or more. You \emph{should}
+enable it if GMP is present on your system. Parts of the first version are
+still in use within the GMP kernel, but are scheduled to disappear.
+
+A historical version of the PARI/GP kernel, written in 1985, was specific to
+680x0 based computers, and was entirely written in MC68020 assembly language.
+It ran on SUN-3/xx, Sony News, NeXT cubes and on 680x0 based Macs. It is no
+longer part of the PARI distribution; to run PARI with a 68k assembler
+micro-kernel, use the GMP kernel!
+
+\section{The PARI types} \label{se:start}
+
+\noindent The GP language is not typed in the traditional sense; in
+particular, variables have no type. In library mode, the type of all PARI
+objects is \kbd{GEN}, a generic type. On the other hand, it is dynamically
+typed: each object has a specific internal type, depending on the
+mathematical object it represents.
+
+The crucial word is recursiveness: most of the PARI types are recursive. For
+example, the basic internal type \typ{COMPLEX} exists. However, the
+components (i.e.~the real and imaginary part) of such a ``complex number''
+can be of any type. The only sensible ones are integers (we are then in
+$\Z[i]$), rational numbers ($\Q[i]$), real numbers ($\R[i]=\C$), or even
+elements of $\Z/n\Z$ (in $(\Z/n\Z)[t]/(t^2+1)$), or $p$-adic numbers when
+$p\equiv 3 \mod 4$ ($\Q_{p}[i]$). This feature must not be used too rashly in
+library mode: for example you are in principle allowed to create objects
+which are ``complex numbers of complex numbers''. (This is not possible under
+\kbd{gp}.) But do not expect PARI to make sensible use of such objects: you
+will mainly get nonsense.
+
+On the other hand, it \emph{is} allowed to have components of different, but
+compatible, types, which can be freely mixed in basic ring operations $+$ or
+$\times$. For example, taking again complex numbers, the real part could be
+an integer, and the imaginary part a rational number. On the other hand, if
+the real part is a real number, the imaginary part cannot be an integer
+modulo $n$ !
+
+Let us now describe the types. As explained above, they are built recursively
+from basic types which are as follows. We use the letter $T$ to designate any
+type; the symbolic names \typ{xxx} correspond to the internal representations
+of the types.\medskip
+\settabs\+xxxxxx&typexxxxxxxxxxxxx&xxxxxxxxxxxxx&xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\cr
+%
+\+&type \tet{t_INT}& $\Z$& Integers (with arbitrary
+precision)\sidx{integer}\cr
+%
+\+&type \tet{t_REAL}& $\R$& Real numbers (with arbitrary precision)\sidx{real
+number}\cr
+%
+\+&type \tet{t_INTMOD}& $\Z/n\Z$& Intmods (integers modulo
+$n$)\varsidx{intmod}\cr
+%
+\+&type \tet{t_FRAC}& $\Q$& Rational numbers (in irreducible
+form)\sidx{rational number}\cr
+%
+\+&type \tet{t_FFELT}& $\F_q$& Finite field element\sidx{finite field
+element}\cr
+%
+%
+\+&type \tet{t_COMPLEX}& $T[i]$& Complex numbers\sidx{complex number}\cr
+%
+\+&type \tet{t_PADIC}& $\Q_p$& $p$-adic\sidx{p-adic number} numbers\cr
+%
+\+&type \tet{t_QUAD}& $\Q[w]$& Quadratic Numbers (where
+$[\Z[w]:\Z]=2$)\sidx{quadratic number}\cr
+%
+\+&type \tet{t_POLMOD}& $T[X]/(P)$& Polmods (polynomials modulo
+$P\in T[X]$)\varsidx{polmod}\cr
+%
+\+&type \tet{t_POL}& $T[X]$& Polynomials \sidx{polynomial}\cr
+%
+\+&type \tet{t_SER}& $T((X))$& Power series (finite Laurent
+series)\sidx{power series}\cr
+%
+\+&type \tet{t_RFRAC}& $T(X)$& Rational functions (in irreducible
+form)\sidx{rational function}\cr
+%
+\+&type \tet{t_VEC}& $T^n$& Row (i.e.~horizontal) vectors\sidx{row vector}\cr
+%
+\+&type \tet{t_COL}& $T^n$& Column (i.e.~vertical) vectors\sidx{column
+vector}\cr
+%
+\+&type \tet{t_MAT}& ${\cal M}_{m,n}(T)$& Matrices\sidx{matrix}\cr
+%
+\+&type \tet{t_LIST}& $T^n$& Lists\sidx{list}\cr
+%
+\+&type \tet{t_STR}&    & Character strings\sidx{string}\cr
+%
+\+&type \tet{t_CLOSURE}&    & Functions\cr
+%
+\+&type \tet{t_ERROR}&    & Error messages\cr
+
+\noindent and where the types $T$ in recursive types can be different in each
+component. \sidx{scalar type} The first nine basic types, from \typ{INT} to
+\typ{POLMOD}, are called scalar types because they essentially occur as
+coefficients of other more complicated objects. Type \typ{POLMOD} is used to
+define algebraic extensions of a base ring, and as such is a scalar type.
+
+In addition, there exist types \tet{t_QFR} and \tet{t_QFI} for integral
+binary quadratic forms, and the internal type \tet{t_VECSMALL}. The latter
+holds vectors of small integers\sidx{vecsmall}, whose absolute value is
+bounded by $2^{31}$ (resp.~$2^{63}$) on 32-bit, resp.~64-bit, machines. They
+are used internally to represent permutations, polynomials or matrices over a
+small finite field, etc.
+
+Every PARI object (called \tet{GEN} in the sequel) belongs to one of these
+basic types. Let us have a closer look.
+
+\subsec{Integers and reals} They are of
+arbitrary and varying length (each number carrying in its internal
+\sidx{integer}\sidx{real number}
+representation its own length or precision) with the following mild
+restrictions (given for 32-bit machines, the restrictions for 64-bit machines
+being so weak as to be considered nonexistent): integers must be in absolute
+value less than $2^{536870815}$ (i.e.~roughly 161614219 decimal digits). The
+precision of real numbers is also at most 161614219 significant decimal
+digits, and the binary exponent must be in absolute value less than
+$2^{29}$, resp. $2^{61}$, on 32-bit, resp.~64-bit machines.
+
+Integers and real numbers are non-recursive types.
+
+\subsec{Intmods, rational numbers, $p$-adic numbers, polmods, and rational
+functions} These are recursive, but in a restricted way.
+\sidx{intmod}\sidx{rational number}\sidx{p-adic number}\sidx{polmod}
+
+For intmods or polmods, there are two components: the modulus, which must
+be of type integer (resp.\ polynomial), and the representative number (resp.\
+polynomial).
+
+For rational numbers or rational functions, there are also only two
+components: the numerator and the denominator, which must both be of type
+integer (resp.\ polynomial).
+
+\def\limproj{{\displaystyle\lim_{\textstyle\longleftarrow}}}
+
+Finally, $p$-adic numbers have three components: the prime $p$, the
+``modulus'' $p^k$, and an approximation to the $p$-adic number. Here $\Z_p$
+is considered as the projective limit $\limproj \Z/p^k\Z$ via its finite
+quotients, and $\Q_p$ as its field of fractions. Like real numbers, the
+codewords contain an exponent, giving the $p$-adic valuation of the number,
+and also the information on the precision of the number, which is
+redundant with $p^k$, but is included for the sake of efficiency.
+
+\subsec{Finite field elements}\sidx{finite field element}
+The exact internal format depends of the finite field size, but it includes
+the field characteristic $p$, an irreducible polynomial $T\in\F_p[X]$
+defining the finite field $\F_p[X]/(T)$ and the element expressed as
+a polynomial in (the class of) $X$.
+
+\subsec{Complex numbers and quadratic numbers}\sidx{complex
+number}\sidx{quadratic number} Quadratic numbers are numbers of the form
+$a+bw$, where $w$ is such that $[\Z[w]:\Z]=2$, and more precisely $w=\sqrt
+d/2$ when $d\equiv 0 \mod 4$, and $w=(1+\sqrt d)/2$ when $d\equiv 1 \mod 4$,
+where $d$ is the discriminant of a quadratic order. Complex numbers
+correspond to the important special case $w=\sqrt{-1}$.\label{se:compquad}
+
+Complex numbers are partially recursive: the two components $a$
+and $b$ can be of type \typ{INT}, \typ{REAL}, \typ{INTMOD}, \typ{FRAC}, or
+\typ{PADIC}, and can be mixed, subject to the limitations mentioned above.
+For example, $a+bi$ with $a$ and $b$ $p$-adic is in $\Q_p[i]$, but this is
+equal to $\Q_p$ when $p\equiv 1 \mod 4$, hence we must exclude these $p$ when
+one explicitly uses a complex $p$-adic type. Quadratic numbers are more
+restricted: their components may be as above, except that \typ{REAL} is not
+allowed.
+
+\subsec{Polynomials, power series, vectors, matrices and lists}
+\sidx{polynomial}\sidx{power series}\sidx{vector}\sidx{matrix}
+They are completely recursive: their components can be of any type, and types
+can be mixed (however beware when doing operations). Note in particular that
+a polynomial in two variables is simply a polynomial with polynomial
+coefficients.
+
+In the present version \vers{} of PARI, it is not possible to handle
+conveniently power series of power series, i.e.~power series in several
+variables. However power series of polynomials (which are power series in
+several variables of a special type) are OK. This is a difficult design
+problem: the mathematical problem itself contains some amount of imprecision,
+and it is not easy to design an intuitive generic interface for such beasts.
+
+\subsec{Strings} These contain objects just as they would be printed by the
+\kbd{gp} calculator.
+
+\subsec{Zero} What is zero? This is a crucial question in all computer
+systems. The answer we give in PARI is the following. For exact types, all
+zeros are equivalent and are exact, and thus are usually represented as an
+integer \idx{zero}. The problem becomes non-trivial for imprecise types:
+there are infinitely many distinct zeros of each of these types! For
+$p$-adics and power series the answer is as follows: every such object,
+including 0, has an exponent $e$. This $p$-adic or $X$-adic zero is
+understood to be equal to $O(p^e)$ or $O(X^e)$ respectively.
+\label{se:whatzero}
+
+Real numbers also have exponents and a real zero is in fact $O(2^e)$ where
+$e$ is now usually a negative binary exponent. This of course is printed as
+usual for a floating point number ($0.00\cdots$ or $0.Exx$ depending on the
+output format) and not with a $O$ symbol as with $p$-adics or power series.
+With respect to the natural ordering on the reals we make the following
+convention: whatever its exponent a real zero is smaller than any positive
+number, and any two real zeroes are equal.
+
+\section{The PARI philosophy}
+The basic principles which govern PARI is that operations and functions
+should, firstly, give as exact a result as possible, and secondly, be
+permitted if they make any kind of sense.
+
+In this respect, we make an important distinction between exact and inexact
+objects: by definition, types \typ{REAL}, \typ{PADIC} or \typ{SER} are
+imprecise. A PARI object having one of these imprecise types anywhere in
+its tree is \emph{inexact}, and \emph{exact} otherwise. No loss of accuracy
+(rounding error) is involved when dealing with exact objects. Specifically,
+an exact operation between exact objects will yield an exact object. For
+example, dividing 1 by 3 does not give $0.333\cdots$, but the rational number
+$(1/3)$. To get the result as a floating point real number, evaluate
+\kbd{1./3} or \kbd{0.+1/3}.
+
+Conversely, the result of operations between imprecise objects, although
+inexact by nature, will be as precise as possible. Consider for example the
+addition of two real numbers $x$ and $y$. The \idx{accuracy} of the result is
+\emph{a priori} unpredictable; it depends on the precisions of $x$ and $y$,
+on their sizes, and also on the size of $x+y$. From this data, PARI works out
+the right precision for the result. Even if it is working in calculator mode
+\kbd{gp}, where there is a notion of \idx{default precision}, its value is
+only used to convert exact types to inexact ones.
+
+In particular, if an operation involves objects of different accuracies, some
+digits will be disregarded by PARI. It is a common source of errors to
+forget, for instance, that a real number is given as $r + 2^e \varepsilon$
+where $r$ is a rational approximation, $e$ a binary exponent and
+$\varepsilon$ is a nondescript real number less than 1 in absolute value.
+Hence, any number less than $2^e$ may be treated as an exact zero:
+\bprog
+? 0.E-28 + 1.E-100
+%1 = 0.E-28
+? 0.E100 + 1
+%2 = 0.E100
+ at eprog
+\noindent As an exercise, if \kbd{a = 2\pow (-100)}, why do \kbd{a + 0.} and
+\kbd{a * 1.} differ?
+
+The second principle is that PARI operations are in general quite permissive.
+For instance taking the exponential of a vector should not make sense.
+However, it frequently happens that one wants to apply a given function
+to all elements in a vector. This is easily done using a loop,
+or using the \tet{apply} built-in function, but in fact PARI assumes that
+this is exactly what you want to do when you apply a scalar function to a
+vector. Taking the exponential of a vector will do just that, so no work is
+necessary. Most transcendental functions work in the same way\footnote{*}{An
+ambiguity arises with square matrices. PARI always considers that you want to
+do componentwise function evaluation in this context, hence to get for
+example the standard exponential of a square matrix you would need to
+implement a different function.}.
+
+In the same spirit, when objects of different types are combined they
+are first automatically mapped to a suitable ring, where the computation
+becomes meaningful:
+\bprog
+    ? 1/3 + Mod(1,5)
+    %1 = Mod(3, 5)
+    ? I + O(5^9)
+    %2 = 2 + 5 + 2*5^2 + 5^3 + 3*5^4 + 4*5^5 + 2*5^6 + 3*5^7 + O(5^9)
+    ? Mod(1,15) + Mod(1,10)
+    %3 = Mod(2, 5)
+ at eprog
+The first example is straightforward: since $3$ is invertible mod $5$, $(1/3)$
+is easily mapped to $\Z/5\Z$. In the second example, \kbd{I} stands for the
+customary square root of $-1$; we obtain a $5$-adic number, $5$-adically
+close to a square root of $-1$. The final example is more problematic, but
+there are natural maps from $\Z/15\Z$ and $\Z/10\Z$ to $\Z/5\Z$, and the
+computation takes place there.
+
+\section{Operations and functions}
+
+The available operations and functions in PARI are described in detail in
+Chapter 3. Here is a brief summary:
+
+\subsec{Standard arithmetic operations}
+
+\noindent
+Of course, the four standard operators \kbd{+}, \kbd{-}, \kbd{*}, \kbd{/}
+exist. We emphasize once more that division is, as far as possible,
+an exact operation: $4$ divided by $3$ gives \kbd{(4/3)}. In addition to
+this, operations on integers or polynomials, like \b{} (Euclidean
+division), \kbd{\%} (Euclidean remainder) exist; for integers, {\b{/}}
+computes the quotient such that the remainder has smallest possible absolute
+value. There is also the exponentiation operator \kbd{\pow }, when the
+exponent is of type integer; otherwise, it is considered as a transcendental
+function. Finally, the logical operators \kbd{!} (\kbd{not} prefix operator),
+\kbd{\&\&} (\kbd{and} operator), \kbd{||} (\kbd{or} operator) exist, giving
+as results \kbd{1} (true) or \kbd{0} (false).
+
+\subsec{Conversions and similar functions}
+
+\noindent
+Many conversion functions are available to convert between different types.
+For example floor, ceiling, rounding, truncation, etc\dots. Other simple
+functions are included like real and imaginary part, conjugation, norm,
+absolute value, changing precision or creating an intmod or a polmod.
+
+\subsec{Transcendental functions}
+
+\noindent
+They usually operate on any complex number, power series, and some also on
+$p$-adics. The list is ever-expanding and of course contains all the
+elementary functions (exp/log, trigonometric functions), plus many others
+(modular functions, Bessel functions, polylogarithms\dots). Recall that by
+extension, PARI usually allows a transcendental function to operate
+componentwise on vectors or matrices.
+
+\subsec{Arithmetic functions}
+
+\noindent
+Apart from a few like the factorial function or the Fibonacci numbers, these
+are functions which explicitly use the prime factor decomposition of
+integers. The standard functions are included. A number of factoring methods
+are used by a rather sophisticated factoring engine (to name a few, Shanks's
+SQUFOF, Pollard's rho, Lenstra's ECM, the MPQS quadratic sieve). These
+routines output strong pseudoprimes, which may be certified by the APRCL
+test.
+
+There is also a large package to work with algebraic number fields. All the
+usual operations on elements, ideals, prime ideals, etc.~are available.
+More sophisticated functions are also implemented, like solving Thue
+equations, finding integral bases and discriminants of number fields,
+computing class groups and fundamental units, computing in relative number
+field extensions, Galois and class field theory, and also many functions
+dealing with elliptic curves over $\Q$ or over local fields.
+
+\subsec{Other functions}
+
+\noindent
+Quite a number of other functions dealing with polynomials (e.g.~finding
+complex or $p$-adic roots, factoring, etc), power series (e.g.~substitution,
+reversion), linear algebra (e.g.~determinant, characteristic polynomial,
+linear systems), and different kinds of recursions are also included. In
+addition, standard numerical analysis routines like univariate integration
+(using the double exponential method), real root finding (when the root is
+bracketed), polynomial interpolation, infinite series evaluation, and
+plotting are included.
+\medskip
+
+And now, you should really have a look at the tutorial before proceeding.
+\newpage
diff --git a/doc/usersch2.tex b/doc/usersch2.tex
new file mode 100644
index 0000000..0389ad5
--- /dev/null
+++ b/doc/usersch2.tex
@@ -0,0 +1,3128 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{The gp Calculator}
+
+\section{Introduction}
+
+Originally, \tet{gp} was designed as a debugging device for the PARI system
+library. Over the years, it has become a powerful user-friendly stand-alone
+calculator. The mathematical functions available in PARI and \kbd{gp} are
+described in the next chapter. In the present one, we describe the specific
+use of the \kbd{gp} programmable calculator.
+
+\emacs If you have GNU Emacs and use the PariEmacs package, you can work in a
+special Emacs shell, described in \secref{se:emacs}. Specific features of
+this Emacs shell are indicated by an EMACS sign in the left margin.
+
+\subsec{Startup}
+
+To start the calculator, the general command line syntax is:
+
+\kbd{gp [-D \var{key}=\var{val}] [\var{files}]}
+
+\noindent
+where items within brackets are optional. The [\var{files}] argument is a
+list of files written in the GP scripting language, which will be loaded on
+startup. There can be any number of arguments of the form
+\kbd{-D \var{key}=\var{val}}, setting some internal parameters of \kbd{gp},
+or \var{defaults}: each sets the default \var{key} to the value \var{val}. See
+\secref{se:defaults} below for a list and explanation of all defaults. These
+defaults can be changed by adding parameters to the input line as above, or
+interactively during a \kbd{gp} session, or in a preferences file also known
+as \tet{gprc}.
+
+If a \idx{preferences file} (to be discussed in \secref{se:gprc}) is
+found, \kbd{gp} then reads it and executes the commands it contains. This
+provides an easy way to customize \kbd{gp}. The \var{files} argument is
+processed right after the \kbd{gprc}.
+
+A copyright banner then appears which includes the version number, and a lot
+of useful technical information. After the copyright, the computer writes the
+top-level help information, some initial defaults, and then waits after
+printing its prompt, which is '\kbd{?~}' by default . Whether extended
+on-line help and line editing are available or not is indicated in this
+\kbd{gp} banner, between the version number and the copyright message.
+Consider investigating the matter with the person who installed \kbd{gp} if
+they are not. Do this as well if there is no mention of the GMP kernel.
+
+\subsec{Getting help}
+
+To get help, type a \kbd{?} and hit return. A menu appears, describing the
+main categories of available functions and how to get more detailed
+help. If you now type \kbd{?$n$} with $1\le n\le11$, you get the list of
+commands corresponding to category $n$ and simultaneously to Section $3.n$ of
+this manual. If you type \kbd{?}\var{functionname} where \var{functionname}
+is the name of a PARI function, you will get a short explanation of this
+function.
+
+If extended help (see \secref{se:exthelp}) is available on your system,
+you can double or triple the \kbd{?} sign to get much more: respectively the
+complete description of the function (e.g.~\kbd{??sqrt}), or a list of
+\kbd{gp} functions relevant to your query (e.g.~ \kbd{???"elliptic curve"}
+or \kbd{???"quadratic field"}).
+
+If \kbd{gp} was properly installed (see Appendix~A), a line editor is
+available to correct the command line, get automatic completions, and so on.
+See \secref{se:readline} or \kbd{??readline} for a short summary of the line
+editor's commands.
+
+If you type \kbd{?\bs} you will get a short description of the metacommands
+(keyboard shortcuts).
+
+Finally, typing \kbd{?.} will return the list of available (pre-defined)
+member functions. These are functions attached to specific kind of objects,
+used to retrieve easily some information from complicated structures (you can
+define your own but they won't be shown here). We will soon describe these
+commands in more detail.
+
+More generally, commands starting with the symbols \b\ or \kbd{?}, are not
+computing commands, but are metacommands which allow you to exchange
+information with \kbd{gp}. The available metacommands can be divided into
+default setting commands (explained below) and simple commands (or keyboard
+shortcuts, to be dealt with in \secref{se:meta}).
+
+\subsec{Input}
+
+Just type in an instruction, e.g. \kbd{1 + 1}, or \kbd{Pi}. No action is
+undertaken until you hit the \kbd{<Return>} key. Then computation starts, and
+a result is eventually printed. To suppress printing of the result, end the
+expression with a \kbd{;} sign. Note that many systems use \kbd{;} to
+indicate end of input. Not so in \kbd{gp}: a final semicolon means the
+result should not be printed. (Which is certainly useful if it occupies
+several screens.)
+
+\subsec{Interrupt, Quit}
+
+Typing \kbd{quit} at the prompt ends the session and exits \kbd{gp}. At any
+point you can type \kbd{Ctrl-C} (that is press simultaneously the
+\kbd{Control} and \kbd{C} keys): the current computation is interrupted and
+control given back to you at the \kbd{gp} prompt, together with a message
+like
+\bprog
+  ***   at top-level: gcd(a,b)
+  ***                 ^--------
+  *** gcd: user interrupt after 236 ms.
+ at eprog\noindent
+telling you how much time elapsed since the last command was typed in and
+in which GP function the computation was aborted. It does not mean that that
+much time was spent in the function, only that the evaluator was busy
+processing that specific function when you stopped it.
+
+\section{The general gp input line}
+
+The \kbd{gp} calculator uses a purely interpreted language GP. The structure
+of this language is reminiscent of LISP with a functional notation,
+\kbd{f(x,y)} rather than \kbd{(f x y)}: all programming constructs,
+such as \kbd{if}, \kbd{while,} etc\dots are functions\footnote{*}{Not exactly,
+since not all their arguments need be evaluated. For instance it would be
+stupid to evaluate both branches of an \kbd{if} statement: since only one
+will apply, only this one is evaluated.}, and the main loop does not really
+execute, but rather evaluates (sequences of) expressions. Of course, it is by
+no means a true LISP, and has been strongly influenced by C and Perl since
+then.
+
+\subsec{Introduction} User interaction with a \kbd{gp} session proceeds as
+follows. First, one types a sequence of characters at the \kbd{gp} prompt;
+see \secref{se:readline} for a description of the line editor. When you hit
+the \kbd{<Return>} key, \kbd{gp} gets your input, evaluates it, then prints
+the result and assigns it to an ``history'' array.
+
+More precisely, the input is case-sensitive and, outside of character
+strings, blanks are completely ignored. Inputs are either metacommands or
+sequences of expressions. Metacommands are shortcuts designed to alter gp's
+internal state, such as the working precision or general verbosity level; we
+shall describe them in \secref{se:meta}, and ignore them for the time being.
+
+The evaluation of a sequence of instructions proceeds in two phases: your
+input is first digested (byte-compiled) to a bytecode suitable for fast
+evaluation, in particular loop bodies are compiled only once but a priori
+evaluated many times; then the bytecode is evaluated.
+
+An \idx{expression}\sidx{expression sequence} is formed by combining
+constants, variables, operator symbols, functions and control statements.
+It is evaluated using the conventions about operator priorities and left to
+right associativity. An expression always has a value, which can be any PARI
+object:
+\bprog
+? 1 + 1
+%1 = 2          \\@com an ordinary integer
+? x
+%2 = x          \\@com a polynomial of degree 1 in the unknown \kbd{x}
+? print("Hello")
+Hello           \\@com \kbd{void} return value
+? f(x) = x^2
+%3 = (x)->x^2   \\@com a user function
+ at eprog
+\noindent In the third example, \kbd{Hello} is printed as a side effect, but
+is not the return value. The \kbd{print} command is a \emph{procedure},
+which conceptually returns nothing. But in fact procedures return a special
+\kbd{void} object, meant to be ignored (but which evaluates
+to $0$ in a numeric context, and stored as $0$ in the history or results).
+The final example assigns to the variable \kbd{f} the function $x\mapsto
+x^2$, the alternative form \kbd{f = x->x\pow2} achieving the same effect; the
+return value of a function definition is, unsurprisingly, a function object
+(of type \typ{CLOSURE}).
+
+Several expressions are combined on a single line by separating them with
+semicolons ('\kbd{;}'). Such an expression sequence will be called a
+\var{seq}. A \var{seq} also has a value, which is the value of the last
+expression in the sequence. Under \kbd{gp}, the value of the \var{seq}, and
+only this last value, becomes an history entry. The values of the other
+expressions in the \var{seq} are discarded after the execution of the
+\var{seq} is complete, except of course if they were assigned into variables.
+In addition, the value of the \var{seq} is printed if the line does not end
+with a semicolon \kbd{;}.
+
+\subsec{The gp history of results}
+
+This is not to be confused with the history of your \emph{commands},
+maintained by \kbd{readline}. The \kbd{gp} history contains the \emph{results}
+they produced, in sequence.
+
+The successive elements of the history array are called \kbd{\%1}, \kbd{\%2},
+\dots As a shortcut, the latest computed expression can also be
+called \kbd{\%}, the previous one \kbd{\%`},
+the one before that \kbd{\%``} and so on.
+
+When you suppress the printing of the result with a semicolon, it is still
+stored in the history, but its history number will not appear either. It is a
+better idea to assign it to a variable for later use than to mentally
+recompute what its number is. Of course, on the next line, you may just use
+\kbd{\%}.
+
+The time used to compute that history entry is also stored as part of the entry
+and can be recovered using the \kbd{\%\#} operator: \kbd{\%\#1}, \kbd{\%\#2},
+\kbd{\%\#`}; \kbd{\%\#} by itself returns the time needed to compute the last
+result (the one returned by \kbd{\%}).
+
+\misctitle{Remark}
+The history ``array'' is in fact better thought of as a queue: its size is
+limited to 5000 entries by default, after which \kbd{gp} starts forgetting
+the initial entries. So \kbd{\%1} becomes unavailable as \kbd{gp} prints
+\kbd{\%5001}. You can modify the history size using \tet{histsize}.
+
+\subsec{Special editing characters}\sidx{editing characters} A GP program
+can of course have more than one line. Since your commands are executed as
+soon as you have finished typing them, there must be a way to tell \kbd{gp}
+to wait for the next line or lines of input before doing anything. There are
+three ways of doing this.
+
+The first one is to use the \idx{backslash character} \kbd{\bs} at the end of
+the line that you are typing, just before hitting \kbd{<Return>}. This tells
+\kbd{gp} that what you will write on the next line is the physical
+continuation of what you have just written. In other words, it makes \kbd{gp}
+forget your newline character. You can type a \kbd{\bs} anywhere. It is
+interpreted as above only if (apart from ignored whitespace characters) it is
+immediately followed by a newline. For example, you can type
+\bprog
+? 3 + \
+4
+ at eprog
+\noindent instead of typing \kbd{3 + 4}.
+
+The second one is a variation on the first, and is mostly useful when
+defining a user function (see \secref{se:user_defined}): since an equal sign
+can never end a valid expression, \kbd{gp} disregards a newline immediately
+following an \kbd{=}.
+\bprog
+? a =
+123
+%1 = 123
+ at eprog
+
+The third one is in general much more useful, and uses braces \kbd{\obr} and
+\kbd{\cbr}.\sidx{brace characters} An opening brace \kbd{\obr} signals that
+you are typing a multi-line command, and newlines are ignored until you type
+a closing brace \kbd{\cbr}. There are two important, but easily obeyed,
+restrictions: first, braces do not nest; second, inside an open brace-close
+brace pair, all input lines are concatenated, suppressing any newlines. Thus,
+all newlines should occur after a semicolon (\kbd{;}), a comma (\kbd{,}) or
+an operator (for clarity's sake, never split an identifier over two lines in
+this way). For instance, the following program
+\bprog
+{
+  a = b
+  b = c
+}
+ at eprog
+
+\noindent would silently produce garbage, since this is interpreted as
+\kbd{a=bb=c} which assigns the value of \kbd{c} to both \kbd{bb} and
+\kbd{a}. It should have been written
+\bprog
+{
+  a = b;
+  b = c;
+}
+ at eprog
+
+\section{The PARI types}
+
+\noindent
+We see here how to input values of the different data types known to PARI.
+Recall that blanks are ignored in any expression which is not a string (see
+below).
+
+\misctitle{A note on efficiency}
+The following types are provided for convenience, not for speed:
+\typ{INTMOD}, \typ{FRAC}, \typ{PADIC}, \typ{QUAD}, \typ{POLMOD},
+\typ{RFRAC}. Indeed, they always perform a reduction of some kind after
+each basic operation, even though it is usually more efficient to perform
+a single reduction at the end of some complex computation. For instance,
+in a convolution product $\sum_{i+j = n} x_i y_j$ in $\Z/N\Z$ --- common
+when multiplying polynomials! ---, it is quite wasteful to perform $n$
+reductions modulo $N$. In short, basic individual operations on these types
+are fast, but recursive objects with such components could be handled more
+efficiently: programming with libpari will save large constant factors here,
+compared to GP.
+
+\subsec{Integers (\typ{INT})}%
+\sidx{integer}\kbdsidx{t_INT}
+After an (optional) leading \kbd{+} or \kbd{-}, type in the
+decimal digits of your integer. No decimal point!
+\bprog
+? 1234567
+%1 = 1234567
+? -3
+%2 = -3
+? 1.         \\@com oops, not an integer
+%3 = 1.000000000000000000000000000
+ at eprog
+
+\subsec{Real numbers (\typ{REAL})}%
+\sidx{real number}\kbdsidx{t_REAL}
+
+Real numbers are represented (approximately) in a floating point system,
+internally in base 2, but converted to base 10 for input / output purposes.
+A \typ{REAL} object has a given \emph{accuracy} (or \emph{precision}) $\ell
+\geq 0$; it comprises
+
+\item a sign $s$: $+1$, $-1$ or $0$;
+
+\item a mantissa $m$: a multiprecision integer, $0\leq m < 10^\ell$;
+
+\item an exponent $e$: a small integer in $[-E,E]$, where $E \approx 2^B
+  \log_{10} 2$, and $B = 32$ on a 32-bit machine and 64 otherwise.
+
+This data may represent any real number $x$ such that
+$$|x - s m 10^e| < 10^{e-\ell}.$$
+We consider that a \typ{REAL} with sign $s = 0$ has accuracy $\ell = 0$, so
+that its mantissa is useless, but it still has an exponent $e$ and acts like
+a machine epsilon for all accuracies $< e$.
+
+After an (optional) leading \kbd{+} or \kbd{-}, type a number with a decimal
+point. Leading zeroes may be omitted, up to the decimal point, but trailing
+zeroes are important: your \typ{REAL} is assigned an internal precision,
+which is the supremum of the input precision, one more than the number of
+decimal digits input, and the default precision. For example, if the default
+precision is 28 digits, typing \kbd{2.} yields a precision of 28 digits, but
+\kbd{2.0\dots0} with 45 zeros gives a number with internal precision at least
+45, although less may be printed.
+
+You can also use scientific notation with the letter \kbd{E} or
+\kbd{e}. As usual, \kbd{e$n$} is interpreted as $\times 10^n$ for all
+integers $n$. Since the result is converted to a \typ{REAL}, you may often
+omit the decimal point in this case: \kbd{6.02 E 23} or \kbd{1e-5} are fine,
+but \kbd{e10} is not.
+
+By definition, \kbd{0.E $n$} returns a real $0$ of exponent $n$, whereas
+\kbd{0.} returns a real 0 ``of default precision'' (of exponent
+$-\tet{realprecision}$), see \secref{se:whatzero}, behaving like the machine
+epsilon for the current default accuracy: any float of smaller absolute value
+is indistinguishable from $0$.
+
+\misctitle{Note on output formats} A zero real number is printed in \kbd{e}
+format as $0.Exx$ where $xx$ is the (usually negative) \emph{decimal}
+exponent of the number (cf.~\secref{se:whatzero}). This allows the user to
+check the accuracy of that particular zero.
+
+When the integer part of a real number $x$ is not known exactly because the
+exponent of $x$ is greater than the internal precision, the real number is
+printed in \kbd{e} format.
+
+\subsec{Intmods (\typ{INTMOD})}%
+\sidx{intmod}\kbdsidx{t_INTMOD}
+To create the image of the integer $a$ in $\Z/b\Z$ (for some non-zero
+integer $b$), type \kbd{Mod(a,b)}; \emph{not} \kbd{a\%b}.
+Internally, all operations are done on integer representatives belonging to
+$[0,b-1]$.
+
+Note that this type is available for convenience, not for speed: each
+elementary operation involves a reduction modulo $b$.
+
+If $x$ is a \typ{INTMOD} \kbd{Mod(a,b)}, the following member function is
+defined:
+
+\kbd{x.mod}: return the modulus \kbd{b}.
+
+\subsec{Rational numbers (\typ{FRAC})}%
+\sidx{rational number}\kbdsidx{t_FRAC}
+All fractions are automatically reduced to lowest
+terms, so it is impossible to work with reducible fractions. To enter $n/m$
+just type it as written. As explained in \secref{se:gdiv}, floating point
+division is \emph{not} performed, only reduction to lowest
+terms.\label{se:FRAC}
+
+Note that rational computation are almost never the fastest method to proceed:
+in the PARI implementation, each elementary operation involves computing a gcd.
+It is generally a little more efficient to cancel denominators and work with
+integers only:
+\bprog
+? P = Pol( vector(10^3,i, 1/i) ); \\@com big polynomial with small rational coeffs
+? P^2
+time = 1,392 ms.
+? c = content(P); c^2 * (P/c)^2;  \\@com same computation in integers
+time = 1,116 ms.
+ at eprog\noindent
+And much more efficient (but harder to setup) to use homomorphic imaging
+schemes and modular computations. As the simple example below indicates, if you
+only need modular information, it is very worthwhile to work with
+\typ{INTMOD}s directly, rather than deal with \typ{FRAC}s all the way through:
+\bprog
+? p = nextprime(10^7);
+? sum(i=1, 10^5, 1/i) % p
+time = 13,288 ms.
+%1 = 2759492
+? sum(i=1, 10^5, Mod(1/i, p))
+time = 60 ms.
+%2 = Mod(2759492, 10000019)
+ at eprog\noindent
+
+\subsec{Finite field elements (\typ{FFELT})}%
+\sidx{finite field element}\kbdsidx{t_FFELT}
+Let $T\in\F_p[X]$ be a monic irreducible polynomial defining your
+finite field over $\F_p$, for instance obtained using \tet{ffinit}. Then the
+\tet{ffgen} function creates a generator of the finite field as an
+$\F_p$-algebra, namely the class of $X$ in $\F_p[X]/(T)$, from which you can
+build all other elements. For instance, to create the field $\F_{2^8}$, we
+write
+\bprog
+? T = ffinit(2, 8);
+? y = ffgen(T, 'y);
+? y^0    \\ the unit element in the field
+%3 = 1
+? y^8
+%4 = y^6 + y^5 + y^4 + y^3 + y + 1
+ at eprog\noindent
+The second (optional) parameter to \tet{ffgen} is only used to display
+the result; it is customary to use the name of the variable we assign the
+generator to. If \kbd{g} is a \typ{FFELT}, the following member functions are
+defined:
+
+\kbd{g.pol}: the polynomial (with reduced integer coefficients)
+expressing \kbd{g} in term of the field generator.
+
+\kbd{g.p}: the characteristic of the finite field.
+
+\kbd{g.f}: the dimension of the definition field over its prime field; the
+cardinality of the definition field is thus $p^f$.
+
+\kbd{g.mod}: the minimal polynomial (with reduced integer
+coefficients) of the field generator.
+
+\subsec{Complex numbers (\typ{COMPLEX})}%
+\sidx{complex number}\kbdsidx{t_COMPLEX}
+To enter $x+iy$, type \kbd{x + I*y}. (That's \kbd{I}, \emph{not} \kbd{i}!)
+The letter \tet{I} stands for $\sqrt{-1}$. The ``real'' and ``imaginary''
+parts $x$ and $y$ can be of type \typ{INT}, \typ{REAL}, \typ{INTMOD},
+\typ{FRAC}, or \typ{PADIC}.
+
+\subsec{$p$-adic numbers (\typ{PADIC}):}%
+\sidx{p-adic number}\label{se:padic}\kbdsidx{t_PADIC}
+Typing \kbd{O($p$\pow $k$)}, where $p$ and $k$ are integers, yields a
+$p$-adic $0$ of accuracy~$k$, representing any $p$-adic number whose
+valuation is $\geq k$. To input a general non-0 $p$-adic number, write
+a suitably precise rational or integer approximation and add \kbd{O($p$\pow
+$k$)} to it.
+
+Note that it is not checked whether $p$ is indeed prime but results are
+undefined if this is not the case: you can work on $10$-adics if you want,
+but disasters will happen as soon as you do something non-trivial like
+taking a square root. Note that \kbd{O(25)} is not the same as
+\kbd{O(5\pow 2)}; you want the latter!
+
+For example, you can type in the $7$-adic number
+
+\kbd{2*7\pow(-1) + 3 + 4*7 + 2*7\pow 2 + O(7\pow3)}
+
+\noindent exactly as shown, or equivalently as \kbd{905/7 + O(7\pow3)}.
+
+If $a$ is a \typ{PADIC}, the following member functions are defined:
+
+\kbd{a.mod}: returns the modulus $p^k$.
+
+\kbd{a.p}: returns $p$.
+
+Note that this type is available for convenience, not for speed:
+internally, \typ{PADIC}s are stored as $p$-adic units modulo some $p^k$.
+Each elementary operation involves updating $p^k$ (multiplying or
+dividing by powers of $p$) and a reduction mod $p^k$. In particular,
+additions are slow.
+\bprog
+    ? n = 1+O(2^20);   for (i=1,10^6, n++)
+    time = 841 ms.
+    ? n = Mod(1,2^20); for (i=1,10^6, n++)
+    time = 441 ms.
+    ? n = 1;           for (i=1,10^6, n++)
+    time = 328 ms.
+ at eprog\noindent The penalty associated with maintaining $p^k$ decreases
+steeply as $p$ increases (and updates become very rare). But \typ{INTMOD}s
+remain at least 25\% more efficient. (But they do not have denominators!)
+% n = 1+O(1009^2);   for (i=1,10^6, n++)
+% n = Mod(1,1009^2); for (i=1,10^6, n++)
+
+\subsec{Quadratic numbers (\typ{QUAD})}%
+\sidx{quadratic number}\kbdsidx{t_QUAD}
+This type is used to work in the quadratic order of \emph{discriminant}
+\kbd{d}, where \kbd{d} is a non-square integer congruent to $0$ or $1$
+(modulo $4$). The command
+\bprog
+    w = quadgen(d)
+ at eprog\noindent
+assigns to \kbd{w} the ``canonical'' generator for the integer basis
+of the order of discriminant $d$, i.e.~$w=\sqrt{d}/2$ if $d\equiv 0 \mod 4$,
+and $w=(1+\sqrt{d})/2$ if $d\equiv 1 \mod 4$. The name \kbd{w} is of course
+just a suggestion, but corresponds to traditional usage. You can use any
+variable name that you like, but \kbd{quadgen(d)} is always printed as
+\kbd{w}, regardless of the discriminant. So beware, two \typ{QUAD}s can be
+printed in the same way and not be equal; however, \kbd{gp} will refuse to add
+or multiply them for example.
+
+Since the order is $\Z + \kbd{w}\Z$, any other element can be input
+as \kbd{$x$+$y$*w} for some integers $x$ and $y$. In fact, you may work in
+its fraction field $\Q(\sqrt{d})$ and use \typ{FRAC} values for $x$ and $y$.
+
+\subsec{Polmods (\typ{POLMOD})}%
+\sidx{polmod}\kbdsidx{t_POLMOD}
+Exactly as for intmods, to enter $x \mod y$ (where $x$ and $y$ are
+polynomials), type \kbd{Mod(x,y)}, not \kbd{x\%y}. Note that when $y$ is an
+irreducible polynomial in one variable, polmods whose modulus is $y$ are simply
+algebraic numbers in the finite extension defined by the polynomial $y$.
+This allows us to work easily in \idx{number field}s, finite extensions of
+the $p$-adic field $\Q_p$, or \idx{finite field}s.
+
+Note that this type is available for convenience, not for speed: each
+elementary operation involves a reduction modulo $y$.
+If $p$ is a \typ{POLMOD}, the following member functions are defined:
+
+\kbd{p.pol}: return a representative of the polynomial class of minimal degree.
+
+\kbd{p.mod}: return the modulus.
+
+\label{se:rempolmod}
+\misctitle{Important remark}\sidx{variable (priority)}
+Mathematically, the variables\sidx{variable} occurring in a polmod are not
+free variables. But internally, a congruence class in $R[t]/(y)$ is
+represented by its representative  of lowest degree, which is a \typ{POL} in
+$R[t]$, and computations occur with polynomials in the variable $t$. PARI
+will not recognize that \kbd{Mod(y, y\pow2 + 1)} is ``the same'' as
+\kbd{Mod(x, x\pow2 + 1)}, since \kbd{x} and \kbd{y} are different variables.
+
+To avoid inconsistencies, polmods must use the same variable in internal
+operations (i.e.~between polmods) and variables of lower priority for
+external operations, typically between a polynomial and a polmod. See
+\secref{se:priority} for a definition of ``priority'' and a discussion of
+(PARI's idea of) multivariate polynomial arithmetic.
+For instance:
+\bprog
+    ? Mod(x, x^2+ 1) + Mod(x, x^2 + 1)
+    %1 = Mod(2*x, x^2 + 1)    \\@com $2i$ (or $-2i$), with $i^2=-1$
+    ? x + Mod(y, y^2 + 1)
+    %2 = x + Mod(y, y^2 + 1)  \\@com in $\Q(i)[x]$
+    ? y + Mod(x, x^2 + 1)
+    %3 = Mod(x + y, x^2 + 1)  \\@com in $\Q(y)[i]$
+ at eprog\noindent
+The first two are straightforward, but the last one may not be what you
+want: \kbd{y} is treated here as a numerical parameter, not as a polynomial
+variable.
+
+If the main variables are the same, it is allowed to mix \typ{POL} and
+\typ{POLMOD}s. The result is the expected \typ{POLMOD}. For instance
+\bprog
+    ? x + Mod(x, x^2 + 1)
+    %1 = Mod(2*x, x^2 + 1)
+ at eprog
+
+\subsec{Polynomials (\typ{POL})}%
+\sidx{polynomial}\label{se:pol}\kbdsidx{t_POL}
+Type the polynomial in a natural way, not forgetting to put a ``$*$'' between
+a coefficient and a formal variable;
+\bprog
+? 1 + 2*x + 3*x^2
+%1 = 3*x^2 + 2*x + 1
+ at eprog\noindent
+This assumes that \kbd{x} is still a ''free variable''.
+\bprog
+? x = 1; 1 + 2*x + 3*x^2
+%2 = 6
+ at eprog\noindent
+generates an integer, not a polynomial! It is good practice to never assign
+values to polynomial variables to avoid the above problem, but a foolproof
+construction is available using \kbd{'x} instead of~\kbd{x}: \kbd{'x}
+is a constant evaluating to the free variable with name \kbd{x},
+independently of the current value of~\kbd{x}.
+\bprog
+? x = 1; 1 + 2*'x + 3*'x^2
+%3 = 1 + 2*x + 3*x^2
+? x = 'x; 1 + 2*x + 3*x^2
+%4 = 1 + 2*x + 3*x^2
+ at eprog\noindent
+You may also use the functions \kbd{Pol} or \kbd{Polrev}:
+\bprog
+? Pol([1,2,3])       \\@com \kbd{Pol} creates a polynomial in \kbd{x} by default
+%1 = x^2 + 2*x + 3
+? Polrev([1,2,3])
+%2 = 3*x^2 + 2*x + 1
+? Pol([1,2,3], 'y)   \\@com we use \kbd{'y}, safer than \kbd{y}
+%3 = y^2 + 2*y + 3
+ at eprog\noindent The latter two are much more efficient constructors than an
+explicit summation (the latter is quadratic in the degree, the former linear):
+\bprog
+? for (i=1, 10^4, Polrev( vector(100, i,i) ) )
+time = 124ms
+
+? for (i=1, 10^4, sum(i = 1, 100, (i+1) * 'x^i) )
+time = 3,985ms
+ at eprog
+
+Polynomials are always printed as \emph{univariate} polynomials, with
+monomials sorted by decreasing degree:
+\bprog
+? (x+y+1)^2
+%1 = x^2 + (2*y + 2)*x + (y^2 + 2*y + 1)
+ at eprog\noindent
+(Univariate polynomial in \kbd{x} whose coefficients are polynomials in
+\kbd{y}.) See \secref{se:varsymb} for valid variable names, and a discussion
+of multivariate polynomial rings.
+
+\subsec{Power series (\typ{SER})}%
+\sidx{power series}\kbdsidx{t_SER}\label{se:series}
+Typing \kbd{O(X\pow $k$)}, where $k$ is an integer, yields an $X$-adic $0$ of
+accuracy~$k$, representing any power series in \kbd{X} whose valuation is
+$\geq k$. Of course, \kbd{X} can be replaced by any other variable name! To
+input a general non-0 power series, type in a polynomial or rational
+function (in \kbd{X}, say), and add \kbd{O(X\pow $k$)} to it. The discussion
+in the \typ{POL} section about variables remains valid; a constructor
+\tet{Ser} replaces \tet{Pol} and \tet{Polrev}.
+
+\misctitle{Caveat} Power series with inexact coefficients sometimes have a
+non-intuitive behavior: if $k$ significant terms are requested, an inexact
+zero is counted as significant, even if it is the coefficient of lowest
+degree. This means that useful higher order terms may be disregarded.
+
+If a series with a zero leading coefficient must be inverted, then as a
+desperation measure that coefficient is discarded, and a warning is issued:
+\bprog
+? C = 0. + y + O(y^2);
+? 1/C
+  *** _/_: Warning: normalizing a series with 0 leading term.
+%2 = y^-1 + O(1)
+ at eprog\noindent
+The last output could be construed as a bug since it is a priori impossible
+to deduce such a result from the input ($0.$ represents any sufficiently
+small real number). But it was thought more useful to try and go on with an
+approximate computation than to raise an early exception.
+
+If the series precision is insufficient, errors may occur (mostly division
+by $0$), which could have been avoided by a better global understanding of
+the computation:
+\bprog
+? A = 1/(y + 0.); B = 1. + O(y);
+? B * denominator(A)
+%2 = 0.E-28 + O(y)
+? A/B
+  *** _/_: Warning: normalizing a series with 0 leading term.
+%3 = 1.000000000000000000000000000*y^-1 + O(1)
+? A*B
+  *** _*_: Warning: normalizing a series with 0 leading term.
+%4 = 1.000000000000000000000000000*y^-1 + O(1)
+ at eprog
+
+\subsec{Rational functions (\typ{RFRAC})}%
+\sidx{rational function}\kbdsidx{t_RFRAC}
+As for fractions, all rational functions are automatically reduced to lowest
+terms. All that was said about fractions in \secref{se:FRAC} remains valid
+here.
+
+\subsec{Binary quadratic forms of positive or negative discriminant
+(\typ{QFR} and \typ{QFI})}%
+\sidx{binary quadratic form}\kbdsidx{t_QFR}\kbdsidx{t_QFI}
+These are input using the function \kbd{Qfb}. For example \kbd{Qfb(1,2,3)}
+creates the binary form $x^2+2xy+3y^2$. It is imaginary (of internal type
+\typ{QFI}) since its discriminant $2^2 - 4\times 3 = -8$ is negative.
+Although imaginary forms could be positive or negative definite, only
+positive definite forms are implemented.
+
+In the case of forms with positive discriminant (\typ{QFR}), you may add an
+optional fourth component (related to the regulator, more precisely to Shanks
+and Lenstra's ``distance''), which must be a real number. See also the
+function \kbd{qfbprimeform} which directly creates a prime form of given
+discriminant.
+
+\subsec{Row and column vectors (\typ{VEC} and \typ{COL})}%
+\sidx{row vector}\sidx{column vector}\kbdsidx{t_VEC}\kbdsidx{t_COL})
+To enter a row vector, type the components separated by commas ``\kbd{,}'',
+and enclosed between brackets ``\kbd{[}$\,$'' and ``$\,$\kbd{]}'',
+e.g.~\kbd{[1,2,3]}. To enter a column vector, type the vector horizontally,
+and add a tilde ``\til'' to transpose. \kbd{[ ]} yields the empty (row)
+vector. The function \tet{Vec} can be used to transform any object into a
+vector (see Chapter~3). The construction $[i..j]$, where $i \leq j$ are two
+integers returns the vector $[i, i+1,\dots, j-1, j]$
+\bprog
+? [1,2,3]
+%1 = [1, 2, 3]
+? [-2..3]
+%2 = [-2, -1, 0, 1, 2, 3]
+ at eprog
+
+Let the variable $v$ contain a (row or column) vector:
+
+\item \kbd{v[m]} refers to its $m$-th entry; you can assign any value to
+\kbd{v[m]},  i.e.~write something like $v[m]=\var{expr}$.
+
+\item \kbd{v[i..j]}, where $i \leq j$, returns the vector slice containing
+elements $v[i],\dots, v[j]$; you can \emph{not} assign a result to
+\kbd{v[i..j]}.
+
+\item \kbd{v[\pow i]} returns the vector whose $i$-th entry has been removed;
+you can \emph{not} assign a result to \kbd{v[\pow i]}.
+
+\noindent In the last two constructions \kbd{v[i..j]} and \kbd{v[\pow i]},
+$i$ and $j$ are allowed to be negative integers, in which case, we start
+counting from the end of the vector: e.g., $-1$ is the index of the last
+element.
+\bprog
+? v = [1,2,3,4];
+? v[2..4]
+%2 = [2, 3, 4]
+? v[^3]
+%3 = [1, 2, 4]
+? v[^-1]
+%3 = [1, 2, 3]
+? v[-3..-1]
+%4 = [2, 3, 4]
+ at eprog
+
+\misctitle{Remark} \tet{vector} is the standard constructor for row vectors
+ whose $i$-th entry is given by a simple function of $i$; \tet{vectorv}
+ is similar for column vectors:
+ \bprog
+ ? vector(10, i, i^2+1)
+ %1 = [2, 5, 10, 17, 26, 37, 50, 65, 82, 101]
+ @eprog
+ The functions \tet{Vec} and \tet{Col} convert objects to row and column
+vectors respectively (as well as \tet{Vecrev} and \tet{Colrev}, which revert
+the indexing):
+ \bprog
+ ? T = poltchebi(5)   \\ 5-th Chebyshev polynomial
+ %1 = 16*x^5 - 20*x^3 + 5*x
+ ? Vec(T)
+ %2 = [16, 0, -20, 0, 5, 0]  \\ coefficients of T
+ ? Vecrev(T)
+ %3 = [0, 5, 0, -20, 0, 16]  \\ ... in reverse order
+ @eprog
+
+\misctitle{Remark} For $v$ a \typ{VEC}, \typ{COL}, \typ{LIST} or \typ{MAT},
+ the alternative set-notations
+ \bprog
+ [g(x) | x <- v, f(x)]
+ [x | x <- v, f(x)]
+ [g(x) | x <- v]
+ @eprog\noindent
+ are available as shortcuts for
+ \bprog
+ apply(g, select(f, Vec(v)))
+ select(f, Vec(v))
+ apply(g, Vec(v))
+ @eprog\noindent respectively, and may serve as \typ{VEC} constructors:
+ \bprog
+ ? [ p | p <- primes(10), isprime(p+2) ]
+ %2 = [3, 5, 11, 17, 29]
+ @eprog\noindent returns the primes $p$ (among the first 10 primes) such
+ that $(p, p+2)$ is a twin pair;
+ \bprog
+ ? [ p^2 | p <- primes(10), p % 4 == 1 ]
+ %1 = [25, 169, 289, 841]
+ @eprog\noindent returns the squares of the primes congruent to $1$ modulo $4$,
+ where $p$ runs among the first 10 primes.
+
+\subsec{Matrices (\typ{MAT})}%
+\sidx{matrix}\kbdsidx{t_MAT}
+To enter a matrix, type the components row by row, the components being
+separated by commas ``\kbd{,}'', the rows by semicolons ``\kbd{;}'', and
+everything enclosed in brackets ``\kbd{[}$\,$'' and ``$\,$\kbd{]}'', e.g.
+\kbd{[x,y; z,t; u,v]}. \kbd{[;]} yields an empty ($0 \times 0$) matrix. The
+function \tet{Mat} transforms any object into a matrix, and \tet{matrix}
+creates matrices whose $(i,j)$-th entry is described by a function $f(i,j)$:
+\bprog
+? Mat(1)
+%1 =
+[1]
+? matrix(2,2, i,j, 2*i+j)
+%2 =
+[3 4]
+
+[5 6]
+ at eprog
+
+\noindent Let the variable $M$ contain a matrix, and let $i,j,k,l$ denote
+four
+integers:
+
+\item \kbd{M[i,j]} refers to its $(i,j)$-th entry; you can assign any result
+to \kbd{M[i,j]}.
+
+\item \kbd{M[i,]} refers to its $i$-th row; you can assign a \typ{VEC}
+of the right dimension to \kbd{M[i,]}.
+
+\item \kbd{M[,j]} refers to its $j$-th column; you can assign a \typ{COL}
+of the right dimension to \kbd{M[,j]}.
+
+\noindent \emph{But} \kbd{M[i]} is meaningless and triggers an error. The
+``range'' $i..j$ and ``caret'' \kbd{\pow}$c$ notations are available as for
+vectors; you can not \emph{assign} to any of these:
+
+\item \kbd{M[i..j, k..l]}, $i\leq j$, $k\leq l$, returns the submatrix built
+from the rows $i$ to $j$ and columns $k$ to $l$ of $M$.
+assign to \kbd{M[i..j, j..l]}.
+
+\item \kbd{M[i..j,]} returns the submatrix built from the rows $i$ to
+$j$ of $M$.
+
+\item \kbd{M[,i..j]} returns the submatrix built from the columns $i$ to
+$j$ of $M$.
+
+\item \kbd{M[i..j, \pow k]}, $i\leq j$, returns the submatrix built
+from the rows $i$ to $j$ and column $k$ removed.
+
+\item \kbd{M[\pow k,]} returns the submatrix with row $k$ removed.
+
+\item \kbd{M[,\pow k]} returns the submatrix with column $k$ removed.
+
+\noindent Finally,
+
+\item \kbd{M[i..j, k]} returns the \typ{COL} built from the $k$-th column
+  (entries $i$ to $j$).
+
+\item \kbd{M[\pow i, k]} returns the \typ{COL} built from the $k$-th column
+  (entry $i$ removed).
+
+\item \kbd{M[k, i..j]} returns the \typ{VEC} built from the $k$-th row
+  (entries $i$ to $j$).
+
+\item \kbd{M[k, \pow i]} returns the \typ{VEC} built from the $k$-th row
+  (entry $i$ removed).
+
+\bprog
+? M = [1,2,3;4,5,6;7,8,9];
+? M[1..2, 2..3]
+%2 =
+[2 3]
+
+[5 6]
+? M[1..2,]
+%3 =
+[1 2 3]
+
+[4 5 6]
+? M[,2..3]
+%4 =
+[2 3]
+
+[5 6]
+
+[8 9]
+ at eprog
+
+All this is recursive, so if \kbd{M} is a matrix of matrices of \dots, an
+expression such as \kbd{M[1,1][,3][4] = 1} is perfectly valid (and actually
+identical to \kbd{M[1,1][4,3] = 1}), assuming that all matrices along the way
+have compatible dimensions.
+
+\misctitle{Technical note (design flaw)} Matrices are internally represented
+as a vector of columns. All matrices with $0$ columns are thus represented
+by the same object (internally, an empty vector), and there is no way to
+distinguish between them. Thus it is not possible to create or represent
+matrices with zero columns and an actual nonzero number of rows.
+The empty matrix \kbd{[;]} is handled as though it had an arbitrary number of
+rows, exactly as many as needed for the current computation to make sense:
+\bprog
+? [1,2,3; 4,5,6] * [;]
+%1 = [;]
+ at eprog\noindent
+The empty matrix on the first line is understood as a $3\times 0$ matrix, and
+the result as a $2\times 0$ matrix. On the other hand, it is possible to
+create matrices with a given positive number of columns, each of which has
+zero rows, e.g.~using \kbd{Mat} as above or using the \kbd{matrix} function.
+
+Note that although the internal representation is essentially the same, a row
+vector of column vectors is \emph{not} a matrix; for example, multiplication
+will not work in the same way. It is easy to go from one representation to
+the other using \tet{Vec} / \tet{Mat}, though:
+\bprog
+? [1,2,3;4,5,6]
+%1 =
+[1 2 3]
+
+[4 5 6]
+
+? Vec(%)
+%2 = [[1, 4]~, [2, 5]~, [3, 6]~]
+? Mat(%)
+%3 =
+[1 2 3]
+
+[4 5 6]
+ at eprog
+
+\subsec{Lists (\typ{LIST})}%
+\sidx{list}\kbdsidx{t_LIST}
+Lists can be input directly, as in \kbd{List([1,2,3,4])}; but in most cases,
+one creates an empty list, then appends elements using \kbd{listput}:
+\bprog
+  ? a = List(); listput(a,1); listput(a,2);
+  ? a
+  %2 = List([1, 2])
+ at eprog\noindent
+Elements can be accessed directly as with the vector types described above.
+
+\subsec{Strings (\typ{STR})}%
+\sidx{string}\sidx{character string}\kbdsidx{t_STR}
+To enter a string, enclose it between double quotes \kbd{"}, like this:
+\kbd{"this is a string"}. The function \kbd{Str} can be used to transform any
+object into a string.
+
+\subsec{Small vectors (\typ{VECSMALL})}%
+\kbdsidx{t_VECSMALL}
+This is an internal type, used to code in an efficient way vectors containing
+only small integers, such as permutations. Most \kbd{gp} functions will
+refuse to operate on these objects.
+
+\subsec{Functions (\typ{CLOSURE})}%
+\kbdsidx{t_CLOSURE}
+We will explain this at length in \secref{se:user_defined}. For the time
+being, suffice it to say that functions can be assigned to variables, as any
+other object, and the following equivalent basic forms are available to
+create new ones
+\bprog
+  f = (x,y) -> x^2 + y^2
+
+  f(x,y) = x^2 + y^2
+ at eprog
+
+\subsec{Error contexts (\typ{ERROR})}%
+\kbdsidx{t_ERROR}
+An object of this type is created whenever an error occurs: it contains some
+information about the error and the error context. Usually, an appropriate
+error is printed immediately, the computation is aborted, and GP enters the
+``break loop'':
+\bprog
+  ? 1/0; 1 + 1
+    ***   at top-level: 1/0;1+1
+    ***                  ^------
+    *** _/_: division by a non-invertible object
+    ***   Break loop: type 'break' to go back to the GP prompt
+
+ at eprog\noindent Here the computation is aborted as soon as we try to evaluate
+$1/0$, and $1 + 1$ is never executed. Exceptions can be trapped
+using \tet{iferr}, however: we can evaluate some expression and either recover
+an ordinary result (no error occurred), or an exception (an error did occur).
+\bprog
+  ? i = Mod(6,12); iferr(1/i, E, print(E)); 1 + 1
+  error("impossible inverse modulo: Mod(6, 12).")
+  %1 = 2
+ at eprog\noindent One can ignore the exception, print it as above, or extract
+non trivial information from the error context:
+\bprog
+  ? i = Mod(6,12); iferr(1/i, E, print(component(E,1)));
+  Mod(6, 12)
+ at eprog\noindent We can also rethrow the exception: \kbd{error(E)}.
+
+\section{GP operators}\label{se:operators}
+
+\noindent Loosely speaking, an \idx{operator} is a function, usually
+associated to basic arithmetic operations, whose name contains only
+non-alphanumeric characters. For instance \kbd{+} or \kbd{-}, but also
+\kbd{=} or \kbd{+=}, or even \kbd{[ ]} (the selection operator). As all
+functions, operators take arguments, and return a value; \emph{assignment}
+operators also have side effects: besides returning a value, they change the
+value of some variable.
+
+Each operator has a fixed and unchangeable priority, which means that, in
+a given expression, the operations with the highest priority is performed
+first. Unless mentioned otherwise, operators at the same priority level are
+left-associative (performed from left to right), unless they are assignments,
+in which case they are right-associative. Anything enclosed between
+parenthesis is considered a complete subexpression, and is resolved
+recursively, independently of the surrounding context. For instance,
+\bprog
+  a + b + c    -->   (a + b) + c     \\@com left-associative
+  a = b = c    -->   a = (b = c)     \\@com right-associative
+ at eprog\noindent
+Assuming that \var{op}$_1$, \var{op}$_2$, \var{op}$_3$ are
+binary operators with increasing priorities (think of \kbd{+},
+\kbd{*}, \kbd{\pow}),
+$$ x~\var{op}_1~y~\var{op}_2~z~\var{op}_2~x~\var{op}_3~y $$ is
+equivalent to $$ x~\var{op}_1~((y~\var{op}_2~z)~\var{op}_2~
+(x~\var{op}_3~y)).$$
+
+GP contains many different operators, either unary (having only
+one argument) or binary, plus a few special selection operators. Unary
+operators are defined as either \emph{prefix}  or \emph{postfix}, meaning
+that they respectively precede (\var{op}~$x$) and follow ($x$~\var{op}) their
+single argument. Some symbols are syntactically correct in both positions,
+like \kbd{!}, but then represent different operators: the \kbd{!} symbol
+represents the negation and factorial operators when in prefix and postfix
+position respectively. Binary operators all use the (infix) syntax
+$x$~\var{op}~$y$.
+
+Most operators are standard (\kbd{+}, \kbd{\%}, \kbd{=}), some are
+borrowed from the C language (\kbd{++}, \kbd{<<}), and a few are
+specific to GP (\kbd{\bs}, \kbd{\#}). Beware that some GP operators differ
+slightly from their C counterparts. For instance, GP's postfix \kbd{++}
+returns the \emph{new} value, like the prefix \kbd{++} of~C, and the binary
+shifts \kbd{<<}, \kbd{>>} have a priority which is different from (higher
+than) that of their C counterparts. When in doubt, just surround everything
+by parentheses; besides, your code will be more legible.
+
+\noindent Here is the list of available operators, ordered by decreasing
+\idx{priority}, binary and left-associative unless mentioned otherwise. An
+expression is an \tev{lvalue} if something can be assigned to it. (The name
+comes from left-value, to the left of a \kbd{=} operator; e.g.
+\kbd{x}, or \kbd{v[1]} are lvalues, but \kbd{x + 1} is not.)
+
+\def\point#1{\noindent\item #1\hfill\break\indent\strut}
+\point{Priority 14}
+%
+\kbd{:} as in \kbd{x:small}, is used to indicate to the GP2C compiler that the
+variable on the left-hand side always contains objects of the type specified
+on the right hand-side (here, a small integer) in order to produce more
+efficient or more readable C code. This is ignored by GP.
+
+%
+\point{Priority 13}
+\kbd{( )} is the function call operator. If $f$ is a closure and \var{args}
+is a comma-separated list of arguments (possibly empty),
+$f\kbd{(\var{args})}$ evaluates $f$ on those arguments.
+
+\point{Priority 12}
+%
+\kbd{++} and \kbd{--} (unary, postfix): if $x$ is an \tet{lvalue},
+\kbd{$x$++} assigns the value $x+1$ to $x$, then returns the new value of
+$x$. This corresponds to the C statement \kbd{++$x$}: there is no prefix
+\kbd{++} operator in GP. \kbd{$x$--} does the same with $x-1$. These
+operators are not associative, i.e. \kbd{x++++} is invalid, since
+\kbd{x++} is not an lvalue.
+
+\point{Priority 11}
+%
+\kbd{.}\var{member} (unary, postfix): \kbd{$x$.\var{member}} extracts
+\var{member} from structure $x$ (see~\secref{se:member}).
+
+\kbd{[ ]} is the selection operator. \kbd{$x$[$i$]} returns the $i$-th
+component of vector $x$; \kbd{$x$[$i$,$j$]}, \kbd{$x$[,$j$]} and
+\kbd{$x$[$i$,]} respectively return the entry of coordinates $(i,j)$, the
+$j$-th column, and the $i$-th row of matrix $x$. If the assignment operator
+(\kbd{=}) immediately follows a sequence of selections, it assigns its right
+hand side to the selected component. E.g \kbd{x[1][1] = 0} is valid; but
+beware that \kbd{(x[1])[1] = 0} is not (because the parentheses force the
+complete evaluation of \kbd{x[1]}, and the result is not modifiable).
+
+\point{Priority 10}
+%
+\kbd{'} (unary, postfix): derivative with respect to the main variable.
+If $f$ is a function (\typ{CLOSURE}), $f'$ is allowed and defines a new
+function, which will perform \idx{numerical derivation} when evaluated
+at a scalar $x$; this is defined as $(f(x+\varepsilon) - f(x-\varepsilon)) /
+2\varepsilon$ for a suitably small epsilon depending on current precision.
+\bprog
+? (x^2 + y*x + y^2)'  \\@com derive with respect to main variable \kbd{x}
+%1 = 2*x + y
+? SIN = cos'
+%2 = cos'
+? SIN(Pi/6)         \\@com numerical derivation
+%3 = -0.5000000000000000000000000000
+? cos'(Pi/6)        \\@com works directly: no need for intermediate \kbd{SIN}
+%4 = -0.5000000000000000000000000000
+ at eprog
+
+\strut\kbd{\til} (unary, postfix): vector/matrix transpose.
+
+\kbd{!} (unary, postfix): factorial. $x\kbd{!}=x(x-1)\cdots 1$.
+
+\kbd{!} (unary, prefix): logical \var{not}. \kbd{!$x$} returns $1$ if $x$ is
+equal to $0$ (specifically, if \kbd{gequal0($x$)==1}), and $0$ otherwise.
+
+\point{Priority 9}
+%
+\kbd{\#} (unary, prefix): cardinality; \kbd{\#$x$} returns \kbd{length($x$)}.
+
+\point{Priority 8}
+%
+\kbd{\pow}: powering. This operator is right associative:
+\kbd{2 \pow 3\pow 4} is understood as \kbd{2 \pow (3\pow 4)}.
+
+\point{Priority 7}
+%
+\kbd{+}, \kbd{-} (unary, prefix): \kbd{-} toggles the sign of its argument,
+\kbd{+} has no effect whatsoever.
+
+\point{Priority 6}
+%
+\kbd{*}: multiplication.
+
+\kbd{/}: exact division (\kbd{3/2} yields $3/2$, not $1.5$).
+
+\kbd{\bs}, \kbd{\%}: Euclidean quotient and remainder, i.e.~if $x =
+qy + r$, then $\kbd{x \b{ } y} = q$, $\kbd{x\%y} = r$. If $x$ and $y$
+are scalars, then $q$ is an integer and $r$ satisfies $0\le r < y$; if $x$
+and $y$ are polynomials, then $q$ and $r$ are polynomials such that $\deg r<
+\deg y$ and the leading terms of $r$ and $x$ have the same sign.
+
+\kbd{\bs/}: rounded Euclidean quotient for integers (rounded towards
+$+\infty$ when the exact quotient would be a half-integer).
+
+\kbd{<<}, \kbd{>>}: left and right binary shift. By definition,
+\kbd{x<<n}$~=~x * 2^n$ if $n>0$, and $\kbd{truncate}(x 2^{-n})$ otherwise.
+Right shift is defined by \kbd{x>>n}$~=~$\kbd{x<<(-n)}.
+
+\point{Priority 5}
+%
+\kbd{+}, \kbd{-}: addition/subtraction.
+
+\point{Priority 4}
+%
+\kbd{<}, \kbd{>}, \kbd{<=}, \kbd{>=}: the usual comparison operators,
+returning 1 for \kbd{true} and 0 for \kbd{false}. For instance,
+\kbd{x<=1} returns $1$ if $x\le 1$ and $0$ otherwise.
+
+\kbd{<>}, \kbd{!=}: test for (exact) inequality.
+
+\kbd{==}: test for (exact) equality. \typ{QFR} having the same coefficients
+but a different distance component are tested as equal.
+
+\kbd{===}: test whether two objects are identical component-wise. This is
+stricter than \kbd{==}: for instance, the integer 0, a 0 polynomial or a
+vector with 0 entries, are all tested equal by \kbd{==}, but they are not
+identical.
+
+\point{Priority 3}
+%
+\kbd{\&\&}: logical \var{and}.
+
+\kbd{||}: logical (inclusive) \var{or}. Any sequence of logical
+\var{or} and \var{and} operations is evaluated from left to right,
+and aborted as soon as the final truth value is known. Thus, for instance,
+\bprog
+  x == 0 || test(1/x)
+ at eprog\noindent
+will never produce an error since \kbd{test(1/x)} is not even evaluated
+when the first test is true (hence the final truth value is true). Similarly
+\bprog
+  type(p) == "t_INT" && isprime(p)
+ at eprog\noindent
+does not evaluate \kbd{isprime(p)} if \kbd{p} is not an integer.
+
+\point{Priority 2}
+%
+\kbd{=} (assignment, \var{lvalue} \kbd{=} \var{expr}). The result of
+\kbd{x~=~$y$} is the value of the expression~$y$, which is also assigned to
+the variable~\kbd{x}. This assignment operator is right-associative. This is
+\emph{not} the equality test operator; a statement like \kbd{x~=~1} is always
+true (i.e.~non-zero), and sets \kbd{x} to~1; the equality test would be
+\kbd{x == 1}. The right hand side of the assignment operator is evaluated
+before the left hand side.
+
+It is crucial that the left hand-side be an \var{lvalue} there, it avoids
+ambiguities in expressions like \kbd{1 + x = 1}. The latter evaluates as
+\kbd{1 + (x = 1)}, not as \kbd{(1 + x) = 1}, even though the priority of
+\kbd{=} is lower than the priority of \kbd{+}: \kbd{1 + x} is not an lvalue.
+
+If the expression cannot be parsed in a way where the left hand side is an
+lvalue, raise an error.
+\bprog
+? x + 1 = 1
+  ***   unused characters: x+1=1
+  ***                         ^--
+ at eprog
+\leavevmode
+\kbd{\var{op}=}, where \var{op} is any binary operator
+among \kbd{+}, \kbd{-}, \kbd{*}, \kbd{\%}, \kbd{/}, \kbd{\bs}, \kbd{\bs/},
+\kbd{<<}, or
+\kbd{>>} (composed assignment \var{lvalue} \var{op}\kbd{=} \var{expr}).
+The expression \kbd{x~\var{op}=~$y$} assigns $(\kbd{x}~\var{op}~y)$
+to~\kbd{x}, and returns the new value of~\kbd{x}. The result is \emph{not}
+an \tev{lvalue}; thus
+\bprog
+  (x += 2) = 3
+ at eprog\noindent
+is invalid. These assignment operators are right-associative:
+\bprog
+  ? x = 'x; x += x *= 2
+  %1 = 3*x
+ at eprog
+
+\point{Priority 1}
+\kbd{->} (function definition): \kbd{(\var{vars})->\var{expr}} returns a
+function object, of type \typ{CLOSURE}.
+
+\misctitle{Remark} Use the \var{op}\kbd{=} operators as often as possible
+since they make complex assignments more legible: one needs not parse
+complicated expressions twice to make sure they are indeed identical. Compare
+\bprog
+v[i+j-1] = v[i+j-1] + 1    -->    v[i+j-1]++
+
+M[i,i+j] = M[i,i+j] * 2    -->    M[i,i+j] *= 2
+ at eprog
+
+\misctitle{Remark} Less important but still interesting. The
+\kbd{++}, \kbd{--} and \var{op}\kbd{=} operators are slightly more efficient:
+\bprog
+? a = 10^6;
+? i = 0; while(i<a, i=i+1)
+time = 365 ms.
+? i = 0; while(i<a, i++)
+time = 352ms.
+ at eprog
+\noindent For the same reason, the shift operators should be preferred to
+multiplication:
+\bprog
+? a = 1<<(10^5);
+? i = 1; while(i<a, i=i*2);
+time = 1,052 ms.
+? i = 1; while(i<a, i<<=1);
+time = 617 ms.
+ at eprog
+
+\section{Variables and symbolic expressions}\sidx{variable}\label{se:varsymb}
+In this section we use \emph{variable} in the standard mathematical
+sense, symbols representing algebraically independent elements used to build
+rings of polynomials and power series, and explain the all-important concept
+of \emph{variable priority}. In the next \secref{se:scope}, we shall no
+longer consider only free variables, but adopt the viewpoint of computer
+programming and assign values to these symbols: (bound) variables are names
+associated to values in a given scope.
+
+\subsec{Variable names}\label{se:varname} A valid name starts with a letter,
+followed by any number of keyword characters: \kbd{\_} or alphanumeric
+characters ([\kbd{A-Za-z0-9}]). The built-in function names are reserved and
+cannot be used; see the list with \b{c}, including the constants \kbd{Pi},
+\kbd{Euler}, \kbd{Catalan} and $\kbd{I}=\sqrt{-1}$.
+
+GP names are case sensitive. For instance, the symbol \kbd{i} is perfectly
+safe to use, and will not be mistaken for $\kbd{I} = \sqrt{-1}$; analogously,
+\kbd{o} is not synonymous to \kbd{O}.
+
+In GP you can use up to 16383 variable names (up to 65535 on 64-bit
+machines). If you ever need thousands of variables and this becomes a serious
+limitation, you should probably be using vectors instead: e.g. instead of
+variables \kbd{X1}, \kbd{X2}, \kbd{X3}, \dots, you might equally well store
+their values in \kbd{X[1]}, \kbd{X[2]}, \kbd{X[3]}, \dots
+
+\subsec{Variables and polynomials}\sidx{free variable}
+What happens when you use a valid variable name,\kbd{t} say, for the first
+time \emph{before} assigning a value into it? This registers a new
+\emph{free variable} with the interpreter (which will be written as \kbd{t}),
+and evaluates to a monomial of degree $1$ in the said variable \kbd{t}. It is
+important to understand that PARI/GP is \emph{not} a symbolic manipulation
+package: even free variables already have default values%
+\footnote{*}{More generally, any expression has a value, and is
+\emph{replaced} by its value as soon as it is read; it never stays in an
+abstract form.}%
+, there is no such thing as an ``unbound'' variable in GP.
+You have access to this default value using the quote operator: \kbd{'t}
+always evaluates to the above monomial of degree~$1$, independently of
+assignments made since then (e.g. \kbd{t = 1}).
+%
+\bprog
+? t^2 + 1
+%1 = t^2 + 1
+? t = 2; t^2 + 1
+%2 = 5
+? %1
+%3 = t^2 + 1
+? eval(%1)
+%4 = 5
+ at eprog\noindent
+In the above, \kbd{t} is initially a free variable, later bound to $2$. We
+see that assigning a value to a variable does not affect previous expressions
+involving it; to take into account the new variable's value, one must force a
+new evaluation, using the function \kbd{eval} (see \secref{se:eval}). It is
+preferable to leave alone your ``polynomial variables'', never assigning
+values to them, and to use \kbd{subst} and its more powerful variants rather
+than \kbd{eval}. You will avoid the following kind of problems:
+\bprog
+? p = t^2 + 1; subst(p, t, 2)
+%1 = 5
+? t = 2;
+? subst(p, t, 3)    \\@com \kbd{t} is no longer free: it evaluates to 2
+  ***   at top-level: subst(p,t,3)
+  ***                         ^----
+  ***   variable name expected.
+? subst(p, 't, 3)   \\ OK
+%3 = 10
+ at eprog\noindent
+A statement like \kbd{x = 'x} in effect restores \kbd{x} as a free variable.
+
+\subsec{Variable priorities, multivariate objects}\sidx{variable (priority)}\label{se:priority}
+A multivariate polynomial in PARI is just a polynomial (in one variable),
+whose coefficients are themselves polynomials, arbitrary but for the fact
+that they do not involve the main variable. (PARI currently has no sparse
+representation for polynomials, listing only non-zero monomials.) All
+computations are then done formally on the coefficients as if the
+polynomial was univariate.
+
+This is not symmetrical. So if I enter \kbd{x + y} in a clean session,
+what happens? This is understood as
+$$ x^1 + (y^1 + 0*y^0)*x^0 \in (\Z[y])[x] $$
+but how do we know that $x$ is ``more important'' than $y$ ? Why not $y^1 +
+x*y^0$, which is the same mathematical entity after all?
+
+The answer is that variables are ordered implicitly by the interpreter:
+when a new identifier (e.g~$x$, or $y$ as above) is input, the corresponding
+variable is registered as having a strictly lower priority than any variable in
+use at this point\footnote{*}{This is not strictly true:
+the variable $x$ is predefined and always has the highest possible priority.}%
+. To see the ordering used by \kbd{gp} at any given time, type
+\kbd{variable()}.\kbdsidx{variable}
+
+Given such an ordering, multivariate polynomials are stored so that the
+variable with the highest priority is the main variable. And so on,
+recursively, until all variables are exhausted. A different storage pattern
+(which could only be obtained via \kbd{libpari} programming and low-level
+constructors) would produce an invalid object, and eventually a disaster.
+
+In any case, if you are working with expressions involving several variables
+and want to have them ordered in a specific manner in the internal
+representation just described, the simplest is just to write down the
+variables one after the other under \kbd{gp} before starting any real
+computations. You could also define variables from your \tet{gprc} to have a
+consistent ordering of common variable names in all your \kbd{gp} sessions,
+e.g read in a file \kbd{variables.gp} containing
+\bprog
+x;y;z;t;a;b;c;d;
+ at eprog
+
+\misctitle{Important note} PARI allows Euclidean division of multivariate
+polynomials, but assumes that the computation takes place in the fraction
+field of the coefficient ring (if it is not an integral domain, the result
+will a priori not make sense). This can become tricky; for instance
+assume $x$ has highest priority (which is always the case), then
+$y$:
+\bprog
+? x % y
+%1 = 0
+? y % x
+%2 = y             \\@com these two take place in $\Q(y)[x]$
+? x * Mod(1,y)
+%3 = Mod(1, y)*x   \\@com in $(\Q(y)/y\Q(y))[x] \sim \Q[x]$
+? Mod(x,y)
+%4 = 0
+ at eprog
+\noindent In the last example, the division by $y$ takes place in
+$\Q(y)[x]$,
+hence the \kbd{Mod} object is a coset in $(\Q(y)[x]) / (y\Q(y)[x])$, which
+is the null ring since $y$ is invertible! So be very wary of variable
+ordering when your computations involve implicit divisions and many
+variables. This also affects functions like \tet{numerator}/\tet{denominator}
+or \tet{content}:
+\bprog
+? denominator(x / y)
+%1 = 1
+? denominator(y / x)
+%2 = x
+? content(x / y)
+%3 = 1/y
+? content(y / x)
+%4 = y
+? content(2 / x)
+%5 = 2
+ at eprog
+\noindent Can you see why? Hint: $x/y = (1/y) * x$ is in $\Q(y)[x]$ and
+denominator is taken with respect to $\Q(y)(x)$; $y/x = (y*x^0) / x$ is in
+$\Q(y)(x)$ so $y$ is invertible in the coefficient ring. On the other hand,
+$2/x$ involves a single variable and the coefficient ring is simply $\Z$.
+
+These problems arise because the variable ordering defines an \emph{implicit}
+variable with respect to which division takes place. This is
+the price to pay to allow \kbd{\%} and \kbd{/} operators on polynomials
+instead of requiring a more cumbersome \kbd{divrem($x$, $y$, \var{var})}
+(which also exists). Unfortunately, in some functions like \tet{content} and
+\tet{denominator}, there is no way to set explicitly a main variable like in
+\tet{divrem} and remove the dependence on implicit orderings. This will
+hopefully be corrected in future versions.
+
+\subsec{Multivariate power series}
+Just like multivariate polynomials, power series are fundamentally
+single-variable objects. It is awkward to handle many variables at once,
+since PARI's implementation cannot handle multivariate error terms like
+$O(x^i y^j)$. (It can handle the polynomial $O(y^j) \times x^i$ which is
+a very different thing, see below.)
+
+The basic assumption in our model is that if variable $x$ has higher
+priority than $y$, then $y$ does not depend on $x$: setting $y$ to a
+function of $x$ after some computations with bivariate power series does
+not make sense a priori. This is because implicit constants in
+expressions like $O(x^i)$ depend on $y$ (whereas in $O(y^j)$ they can not
+depend on $x$). For instance
+\bprog
+  ? O(x) * y
+  %1 = O(x)
+  ? O(y) * x
+  %2 = O(y)*x
+ at eprog\noindent
+Here is a more involved example:
+\bprog
+  ? A = 1/x^2 + 1 + O(x); B = 1/x + 1 + O(x^3);
+  ? subst(z*A, z, B)
+  %2 = x^-3 + x^-2 + x^-1 + 1 + O(x)
+  ? B * A
+  %3 = x^-3 + x^-2 + x^-1 + O(1)
+  ? z * A
+  %4 = z*x^-2 + z + O(x)
+ at eprog\noindent
+The discrepancy between \kbd{\%2} and \kbd{\%3} is surprising. Why does
+\kbd{\%2} contain a spurious constant term, which cannot be
+deduced from the input? Well, we ignored the rule that forbids to
+substitute an expression involving high-priority variables
+to a low-priority variable. The result \kbd{\%4} is correct according to
+our rules since the implicit constant in $O(x)$ may depend on $z$. It is
+obviously wrong if $z$ is allowed to have negative valuation in $x$. Of
+course, the correct error term should be $O(xz)$, but this is not
+possible in PARI.
+
+\section{Variables and Scope}\sidx{variable scope}\label{se:scope}
+This section is rather technical, and strives to explain potentially
+confusing concepts. Skip to the last subsection for practical advice, if the
+next discussion does not make sense to you. After learning about user
+functions, study the example in \secref{se:bewarescope} then come back.
+
+\misctitle{Definitions} % rather not make it a section to allow for ??my
+
+A \emph{scope} is an enclosing context where names and values are associated.
+A user's function body, the body of a loop, an individual command line, all
+define scopes; the whole program defines the \emph{global} scope. The
+argument of \tet{eval} is evaluated in the enclosing scope.
+
+Variables are bound to values within a given scope. This is traditionally
+implemented in two different ways:
+
+\item\sidx{lexical scoping} lexical (or static) scoping: the binding makes
+sense within a given block of program text. The value is private to the block
+and may not be accessed from outside. Where to find the value is determined
+at compile time.
+
+\item\sidx{dynamic scoping} dynamic scoping: introducing a local variable,
+say \kbd{x}, pushes a new value on a stack associated to the name \kbd{x}
+(possibly empty at this point), which is popped out when the control flow
+leaves the scope. Evaluating \kbd{x} in any context, possibly outside of the
+given block, always yields the top value on this dynamic stack.
+
+GP implements both lexical and dynamic scoping, using the keywords%
+\footnote{*}{The names are borrowed from the \tet{Perl} scripting language.}
+\tet{my} (lexical) and \tet{local} (dynamic):
+\bprog
+  x = 0;
+  f() = x
+  g() =    my(x = 1); f()
+  h() = local(x = 1); f()
+ at eprog\noindent
+The function \kbd{g} returns 0 since the global \kbd{x} binding
+is unaffected by the introduction of a private variable of the same name in
+\kbd{g}. On the other hand, \kbd{h} returns 1; when it calls \kbd{f()}, the
+binding stack for the \kbd{x} identifier contains two items: the global
+binding to 0, and the binding to 1 introduced in \kbd{h}, which is still
+present on the stack since the control flow has not left \kbd{h} yet.
+
+\subsec{Scoping rules}
+
+Named parameters in a function definition, as well as all loop
+indices\footnote{*}{
+More generally, in all iterative constructs which use a variable name
+(\kbd{for}, \kbd{prod}, \kbd{sum}, \kbd{vector}, \kbd{matrix},
+\kbd{plot}, etc.) the given variable is lexically scoped to the construct's
+body.},
+have lexical scope within the function body and the loop body respectively.
+\bprog
+p = 0;
+forprime (p = 2, 11, print(p)); p   \\ prints 0 at the end
+
+x = 0;
+f(x) = x++;
+f(1)  \\ returns 2, and leave global x unaffected (= 0)
+ at eprog\noindent
+If you exit the loop prematurely, e.g.~using the \kbd{break} statement, you
+must save the loop index in another variable since its value prior the loop
+will be restored upon exit. For instance
+\bprog
+  for(i = 1, n,
+    if (ok(i), break);
+  );
+  if (i > n, return(failure));
+ at eprog\noindent
+is incorrect, since the value of $i$ tested by the $(i > n)$ is quite
+unrelated to the loop index. One ugly workaround is
+\bprog
+  for(i = 1, n,
+    if (ok(i), isave = i; break);
+  );
+  if (isave > n, return(failure));
+ at eprog\noindent
+But it is usually more natural to wrap the loop in a user function
+and use \kbd{return} instead of \kbd{break}:
+\bprog
+try() =
+{
+  for(i = 1, n,
+    if (ok(i), return (i));
+  );
+  0 \\ failure
+}
+ at eprog
+
+A list of variables can be lexically or dynamically scoped (to the block
+between the declaration and the end of the innermost enclosing scope) using a
+\kbd{my} or \kbd{local} declaration:
+\bprog
+for (i = 1, 10,
+  my(x, y, z, i2 = i^2); \\ temps needed within the loop body
+  ...
+)
+ at eprog\noindent
+Note how the declaration can include (optional) initial values, \kbd{i2 =
+i\pow 2} in the above. Variables for which no explicit default value is given
+in the declaration are initialized to $0$. It would be more natural to
+initialize them to free variables, but this would break backward
+compatibility. To obtain this behavior, you may explicitly use the quoting
+operator:
+\bprog
+my(x = 'x, y = 'y, z = 'z);
+ at eprog\noindent
+A more complicated example:
+\bprog
+for (i = 1, 3,
+  print("main loop");
+  my(x = i);          \\ local to the outermost loop
+  for (j = 1, 3,
+    my (y = x^2);     \\ local to the innermost loop
+    print (y + y^2);
+    x++;
+  )
+)
+ at eprog\noindent
+When we leave the loops, the values of \kbd{x}, \kbd{y}, \kbd{i}, \kbd{j}
+are the same as before they were started.
+
+Note that \tet{eval} is evaluated in the given scope, and can access values
+of lexical variables:
+\bprog
+? x = 1;
+? my(x = 0); eval("x")
+%2 = 0    \\@com we see the local \kbd{x} scoped to this command line, not the global one
+ at eprog
+
+Variables dynamically scoped using \kbd{local} should more appropriately be
+called \emph{temporary values} since they are in fact local to the function
+declaring them \emph{and} any subroutine called from within. In practice, you
+almost certainly want true private variables, hence should use almost
+exclusively \kbd{my}.
+
+We strongly recommended to explicitly scope (lexically) all variables to the
+smallest possible block. Should you forget this, in expressions involving such
+``rogue'' variables, the value used will be the one which happens to be on
+top of the value stack at the time of the call; which depends on the whole
+calling context in a non-trivial way. This is in general \emph{not} what you
+want.
+
+\section{User defined functions}\sidx{user defined functions}
+\label{se:user_defined}
+
+The most important thing to understand about user-defined functions is
+that they are ordinary GP objects, bound to variables just like any
+other object. Those variables are subject to scoping rules as any other:
+while you can define all your functions in global scope, it is usually
+possible and cleaner to lexically scope your private helper functions to the
+block of text where they will be needed.
+
+Whenever gp meets a construction of the form \kbd{expr(\var{argument list})}
+and the expression \kbd{expr} evaluates to a function (an object of type
+\typ{CLOSURE}), the function is called with the proper arguments. For
+instance, constructions like \kbd{funcs[i](x)} are perfectly valid,
+assuming \kbd{funcs} is an array of functions.
+
+\subsec{Defining a function}\label{se:userfundef}
+
+A user function is defined as follows:
+
+  \kbd{(\var{list of formal variables}) -> \var{seq}}.
+
+\noindent The list of formal variables is a comma-separated list of
+\emph{distinct} variable names and allowed to be empty. It there is a single
+formal variable, the parentheses are optional. This list corresponds to the
+list of parameters you will supply to your function when calling it.
+
+In most cases you want to assign a function to a variable immediately, as in
+\bprog
+R = (x,y) -> sqrt( x^2+y^2 );
+sq = x -> x^2;  \\@com or equivalently \kbd{(x) -> x\pow2}
+ at eprog\noindent
+but it is quite possible to define (a priori short-lived) anonymous functions.
+The trailing semicolon is not part of the definition, but as usual prevents
+\kbd{gp} from printing the result of the evaluation, i.e. the function
+object. The construction
+
+  \kbd{f(\var{list of formal variables}) = \var{seq}}
+
+\noindent is available as an alias for
+
+  \kbd{f = (\var{list of formal variables}) -> \var{seq}}
+
+\noindent Using that syntax, it is not possible to define anonymous functions
+(obviously), and the above two examples become:
+\bprog
+R(x,y) = sqrt( x^2+y^2 );
+sq(x) = x^2;
+ at eprog\noindent
+The semicolon serves the same purpose as above: preventing the printing
+of the resulting function object; compare
+\bprog
+? sq(x) = x^2;  \\@com no output
+? sq(x) = x^2   \\@com print the result: a function object
+%2 = (x)->x^2
+ at eprog\noindent Of course, the sequence \var{seq} can be arbitrarily
+complicated, in which case it will look better written on consecutive lines,
+with properly scoped variables:
+\bprogpart
+{
+f($x_0$, $x_1$, @dots) =
+  my($t_0$, $t_1$, @dots); \\@com variables lexically scoped to the function body
+  @dots
+}
+ at eprog \noindent Note that the following variant would also work:
+\bprogpart
+f($x_0$, $x_1$, @dots) =
+{
+  my($t_0$, $t_1$, @dots); \\@com variables lexically scoped to the function body
+  @dots
+}
+ at eprog \noindent
+(the first newline is disregarded due to the preceding \kbd{=} sign, and the
+others because of the enclosing braces). The \tet{my} statements can actually
+occur anywhere within the function body, scoping the variables to more
+restricted blocks than the whole function body.
+
+Arguments are passed by value, not as variables: modifying a function's
+argument in the function body is allowed, but does not modify its value in the
+calling scope. In fact, a \emph{copy} of the actual parameter is assigned to
+the formal parameter when the function is called. Formal parameters are
+lexically scoped to the function body. It is not allowed to use the same
+variable name for different parameters of your function:
+\bprog
+? f(x,x) = 1
+  ***   variable declared twice: f(x,x)=1
+  ***                                ^----
+ at eprog
+
+\misctitle{Finishing touch}
+You can add a specific help message for your function using \kbd{addhelp},
+but the online help system already handles it. By default \kbd{?\var{name}}
+will print the definition of the function \var{name}: the list of arguments,
+as well as their default values, the text of \var{seq} as you input it.
+Just as \b{c} prints the list of all built-in commands, \b{u} outputs the
+list of all user-defined functions.
+
+\misctitle{Backward compatibility (lexical scope)} Lexically scoped
+variables were introduced in version~2.4.2. Before that, the formal
+parameters were dynamically scoped. If your script depends on this behavior,
+you may use the following trick: replace the initial \kbd{f(x) =} \ by
+\bprog
+f(x_orig) = local(x = x_orig)
+ at eprog
+\misctitle{Backward compatibility (disjoint namespaces)} Before version
+2.4.2, variables and functions lived in disjoint namespaces and it was not
+possible to have a variable and a function share the same name. Hence the
+need for a \kbd{kill} function allowing to reuse symbols. This is no longer
+the case.
+
+There is now no distinction between variable and function names: we
+have PARI objects (functions of type \typ{CLOSURE}, or more mundane
+mathematical entities, like \typ{INT}, etc.) and variables bound to them.
+There is nothing wrong with the following sequence of assignments:
+\bprog
+? f = 1       \\@com assigns the integer 1 to \kbd{f}
+%1 = 1;
+? f() = 1     \\@com a function with a constant value
+%2 = ()->1
+? f = x^2     \\@com \kbd{f} now holds a polynomial
+%3 = x^2
+? f(x) = x^2  \\@com \dots and now a polynomial function
+%4 = (x)->x^2
+? g(fun) = fun(Pi);\\@com a function taking a function as argument
+? g(cos)
+%6 = -1.000000000000000000000000000
+
+ at eprog\noindent
+Previously used names can be recycled as above: you are just redefining the
+variable. The previous definition is lost of course.
+
+\misctitle{Important technical note} Built-in functions are a special case
+since they are read-only (you cannot overwrite their default meaning),
+and they use features not available to user functions, in particular pointer
+arguments. In the present version \vers{}, it is possible to assign a built-in
+function to a variable, or to use a built-in function name to create an
+anonymous function, but some special argument combinations may not be
+available:
+\bprog
+? issquare(9, &e)
+%1 = 1
+? e
+%2 = 3
+? g = issquare;
+? g(9)
+%4 = 1
+? g(9, &e)  \\@com pointers are not implemented for user functions
+  ***   unexpected &: g(9,&e)
+  ***                     ^---
+ at eprog
+
+\subsec{Function call, Default arguments}
+
+You may now call your function, as in \kbd{f(1,2)}, supplying values
+for the formal variables. The number of parameters actually supplied may be
+\emph{less} than the number of formal variables in the function definition.
+An uninitialized formal variable is given an implicit default value of (the
+integer)~0, i.e. after the definition
+\bprog
+f(x, y) = ...
+ at eprog\noindent
+you may call \kbd{f(1, 2)}, supplying values for the two formal
+parameters, or for example
+\settabs\+\indent&xxxxxxxxx& equivalent to xxxx &\cr
+\+& \kbd{f(2)}  & equivalent to &\kbd{f(2,0)},\cr
+\+& \kbd{f()}   & & \kbd{f(0,0)},\cr
+\+& \kbd{f(,3)} & &\kbd{f(0,3)}.  (``Empty argument'' trick)\cr
+\noindent This \emph{implicit} default value of $0$, is actually deprecated
+and setting
+\bprog
+  default(strictargs, 1)
+ at eprog\noindent allows to disable it (see \secref{se:def,strictargs}).\kbdsidx{strictargs}
+
+The recommended practice is to \emph{explicitly} set a default value:
+in the function definition, you can append \kbd{=}\var{expr} to a formal
+parameter, to give that variable a default value. The expression
+gets evaluated the moment the function is called, and may involve the
+preceding function parameters: a default value for $x_i$ may involve $x_j$
+for $j < i$. For instance, after
+\bprog
+f(x = 1, y = 2, z = y+1) = ....
+ at eprog\noindent
+typing in \kbd{f(3,4)} would give you \kbd{f(3,4,5)}. In the rare case when
+you want to set some far away argument, and leave the defaults in between as
+they stand, use the ``empty argument'' trick: \kbd{f(6,,1)} would yield
+\kbd{f(6,2,1)}. Of course, \kbd{f()} by itself yields \kbd{f(1,2,3)} as was
+to be expected.
+
+In short, the argument list is filled with user supplied values, in
+order. A comma or closing parenthesis, where a value should have been,
+signals we must use a default value. When no input arguments are left, the
+defaults are used instead to fill in remaining formal parameters.
+A final example:
+\bprog
+f(x, y=2, z=3) = print(x, ":", y, ":", z);
+ at eprog
+\noindent defines a function which prints its arguments (at most three of
+them), separated by colons.
+\bprog
+? f(6,7)
+6:7:3
+? f(,5)
+0:5:3
+? f()
+0:2:3
+ at eprog\noindent If \kbd{strictargs} is set (recommended), $x$ is now a
+mandatory argument, and the above becomes:
+\bprog
+? default(strictargs,1)
+? f(6,7)
+6:7:3
+? f(,5)
+  ***   at top-level: f(,5)
+  ***                 ^-----
+  ***   in function f: x,y=2,z=3
+  ***                  ^---------
+  ***   missing mandatory argument 'x' in user function.
+ at eprog
+
+\misctitle{Example} We conclude with an amusing example, intended to
+illustrate both user-defined functions and the power of the \kbd{sumalt}
+function. Although the \idx{Riemann zeta-function} is included (as
+\kbd{zeta}) among the standard functions, let us assume that we want to check
+other implementations. Since we are highly interested in the critical strip,
+we use the classical formula
+$$ (2^{1-s} - 1)\zeta(s) = \sum_{n\geq 1} (-1)^n n^{-s},
+  \qquad\Re s > 0.$$
+The implementation is obvious:\sidx{zeta function}
+\bprog
+ZETA(s) = sumalt(n=1, (-1)^n*n^(-s)) / (2^(1-s) - 1)
+ at eprog
+\noindent
+Note that \kbd{n} is automatically lexically scoped to the \kbd{sumalt}
+``loop'', so that it is unnecessary to add a \kbd{my(n)} declaration to the
+function body. Surprisingly, this gives very good accuracy in a larger region
+than expected:
+\bprog
+? check = z -> ZETA(z) / zeta(z);
+? check(2)
+%1 = 1.000000000000000000000000000
+? check(200)
+%2 = 1.000000000000000000000000000
+? check(0)
+%3 = 0.9999999999999999999999999994
+? check(-5)
+%4 = 1.00000000000000007549266557
+? check(-11)
+%5 = 0.9999752641047824902660847745
+? check(1/2+14.134*I)  \\@com very close to a non-trivial zero
+%6 = 1.000000000000000000003747432 + 7.62329066 E-21*I
+? check(-1+10*I)
+%7 = 1.000000000000000000000002511 + 2.989950968 E-24*I
+ at eprog\noindent Now wait a minute; not only are we summing a series which is
+certainly no longer alternating (it has complex coefficients), but we are
+also way outside of the region of convergence, and still get decent results! No
+programming mistake this time: \kbd{sumalt} is a
+``magic'' function\footnote{*}{\kbd{sumalt} is heuristic, but its use can be
+rigorously justified for a given function, in particular our $\zeta(s)$
+formula. Indeed, Peter Borwein (\emph{An efficient algorithm for the Riemann
+zeta function}, CMS Conf.~Proc.~{\bf 27} (2000), pp.~29--34) proved that the
+formula used in \kbd{sumalt} with $n$ terms computes $(1-2^{1-s})\zeta(s)$
+with a relative error of the order of $(3+\sqrt{8})^{-n}|\Gamma(s)|^{-1}$.},
+providing very good convergence acceleration; in effect, we are computing
+the analytic continuation of our original function. To convince ourselves
+that \kbd{sumalt} is a non-trivial implementation, let us try a simpler
+example:
+\bprog
+? sum(n=1, 10^7, (-1)^n/n, 0.) / (-log(2)) \\@com approximates the well-known formula
+time = 7,417 ms.
+%1 = 0.9999999278652515622893405457
+? sumalt(n=1, (-1)^n/n) / (-log(2))        \\@com accurate and fast
+time = 0 ms.
+%2 = 1.000000000000000000000000000
+ at eprog\noindent No, we are not using a powerful simplification tool here,
+only numerical computations. Remember, PARI is not a computer algebra system!
+
+
+\subsec{Beware scopes}\label{se:bewarescope}
+Be extra careful with the scopes of variables. What is wrong with the
+following definition?
+\bprog
+FirstPrimeDiv(x) =
+{ my(p);
+  forprime(p=2, x, if (x%p == 0, break));
+  p
+}
+? FirstPrimeDiv(10)
+%1 = 0
+ at eprog\noindent \misctitle{Hint} The function body is equivalent to
+\bprog
+{ my(newp = 0);
+  forprime(p=2, x, if (x%p == 0, break));
+  newp
+}
+ at eprog\noindent
+\misctitle{Detailed explanation} The index \kbd{p} in the \kbd{forprime}
+loop is lexically scoped to the loop and is not visible to the outside world.
+Hence, it will not survive the \kbd{break} statement. More precisely, at this
+point the loop index is restored to its preceding value. The initial
+\kbd{my(p)}, although well-meant, adds to the confusion: it indeed scopes
+\kbd{p} to the function body, with initial value $0$, but the \kbd{forprime}
+loop introduces \emph{another} variable, unfortunately also called \kbd{p},
+scoped to the loop body, which shadows the one we wanted. So we always return
+$0$, since the value of the \kbd{p} scoped to the function body never changes
+and is initially $0$.
+
+To sum up, the routine returns the \kbd{p} declared local to
+it, not the one which was local to \kbd{forprime} and ran through consecutive
+prime numbers. Here is a corrected version:
+\bprog
+? FirstPrimeDiv(x) = forprime(p=2, x, if (x%p == 0, return(p)))
+ at eprog
+
+\subsec{Recursive functions} Recursive functions\sidx{recursion} can easily
+be written as long as one pays proper attention to variable scope. Here is an
+example, used to retrieve the coefficient array of a multivariate polynomial
+(a non-trivial task due to PARI's unsophisticated representation for those
+objects): \sidx{multivariate polynomial}
+\bprog
+coeffs(P, nbvar) =
+{
+  if (type(P) != "t_POL",
+    for (i=1, nbvar, P = [P]);
+    return (P)
+  );
+  vector(poldegree(P)+1, i, coeffs(polcoeff(P, i-1), nbvar-1))
+}
+ at eprog
+
+\noindent If $P$ is a polynomial in $k$ variables, show that after the
+assignment {\tt v = coeffs(P,k)}, the coefficient of $x_1^{n_1}\dots
+x_k^{n_k}$ in P is given by {\tt v[$n_1$+1][\dots][$n_k$+1]}.
+
+The operating system automatically limits the \idx{recursion depth}:
+\sidx{deep recursion}
+\bprog
+? dive(n) = dive(n+1)
+? dive(0);
+  ***   [...] at: dive(n+1)
+  ***             ^---------
+  ***   in function dive: dive(n+1)
+  ***                     ^---------
+  \\@com (last 2 lines repeated 19 times)
+  ***   deep recursion.
+ at eprog\noindent
+There is no way to increase the recursion limit (which may be different on
+your machine) from within \kbd{gp}. To increase it before launching \kbd{gp},
+you can use \tet{ulimit} or \tet{limit}, depending on your shell, and raise
+the process available stack space (increase \tet{stacksize}).
+
+\subsec{Function which take functions as parameters} This is done as follows:
+\bprog
+? calc(f, x) = f(x)
+? calc(sin, Pi)
+%2 = -5.04870979 E-29
+? g(x) = x^2;
+? calc(g, 3)
+%4 = 9
+ at eprog
+\noindent If we do not need \kbd{g} elsewhere, we should use an anonymous
+function here, \kbd{calc(x->x\pow 2, 3)}. Here is a variation:
+\bprog
+? funs = [cos, sin, tan, x->x^3+1]; \\@com an array of functions
+? call(i, x) = funs[i](x)
+ at eprog\noindent
+evaluates the appropriate function on argument \kbd{x},
+provided $1\leq i\leq 4$. Finally, a more useful example:
+\bprog
+APPLY(f, v) = vector(#v, i, f(v[i]))
+ at eprog\noindent
+applies the function \kbd{f} to every element in the vector \kbd{v}.
+(The built-in function \kbd{apply} is more powerful since it also applies to
+lists and matrices.)
+
+\subsec{Defining functions within a function}
+Defining a single function is easy:
+\bprog
+init(x) = (add = y -> x+y);
+ at eprog\noindent Basically, we are defining a global variable \kbd{add}
+whose value is the function \kbd{y->x+y}. The parentheses were added for
+clarity and are not mandatory.
+\bprog
+? init(5);
+? add(2)
+%2 = 7
+ at eprog\noindent
+A more refined approach is to
+avoid global variables and \emph{return} the function:
+\bprog
+init(x) = y -> x+y
+add = init(5)
+ at eprog\noindent Then \kbd{add(2)} still returns 7, as expected! Of course,
+if \kbd{add} is in global scope, there is no gain, but we can
+lexically scope it to the place where it is useful:
+\bprog
+  my ( add = init(5) );
+ at eprog
+
+How about multiple functions then? We can use the last idea and return a
+vector of functions, but if we insist on global variables?
+The first idea
+\bprog
+init(x) = add(y) = x+y; mul(y) = x*y;
+ at eprog
+\noindent does not work since in the construction \kbd{f() = }\var{seq}, the
+function body contains everything until the end of the expression. Hence
+executing \kbd{init} defines the wrong function \kbd{add} (itself defining
+a function \kbd{mul}). The way out is to
+use parentheses for grouping, so that enclosed subexpressions will be
+evaluated independently:
+\bprog
+? init(x) = ( add(y) = x+y ); ( mul(y) = x*y );
+? init(5);
+? add(2)
+%3 = 7
+? mul(3)
+%4 = 15
+ at eprog\noindent This defines two global functions which have access to the
+lexical variables private to \kbd{init}! The following would work in exactly
+the same way:
+\bprog
+? init5() = my(x = 5); ( add(y) = x+y ); ( mul(y) = x*y );
+ at eprog
+
+\subsec{Closures as Objects} Contrary to what you might think after the
+preceding examples, GP's closures may not be used to simulate true
+``objects'', with private and public parts and methods to access and
+manipulate them. In fact, closures indeed incorporate an existing context
+(they may access lexical variables that existed at the time of their
+definition), but then may not change it. More precisely, they access a copy,
+which they are welcome to change, but a further function call still accesses
+the original context, as it existed at the time the function was defined:
+\bprog
+init() =
+{ my(count = 0);
+  inc()=count++;
+  dec()=count--;
+}
+? inc()
+%1 = 1
+? inc()
+%2 = 1
+? inc()
+%3 = 1
+ at eprog
+
+\section{Member functions}\sidx{member functions} \label{se:member}
+
+Member functions use the `dot' notation to retrieve information from
+complicated structures. The built-in structures are \tev{bid}, \tev{ell},
+\tev{galois}, \tev{ff}, \tev{nf}, \tev{bnf}, \tev{bnr} and \tev{prid}, which
+will be described at length in Chapter~3. The syntax \kbd{structure.member}
+is taken to mean: retrieve \kbd{member} from \kbd{structure},
+e.g.~\kbd{E.j} returns the $j$-invariant of the elliptic curve \kbd{E},
+or outputs an error message if \kbd{E} is not a proper \tev{ell} structure.
+To define your own member functions, use the syntax
+
+\ \kbd{\var{var}.\var{member} = \var{seq}},
+
+\noindent where the formal variable \var{var} is scoped to the function
+body \var{seq}. This is of course reminiscent of a user function with a
+single formal variable \var{var}. For instance, the current implementation of
+the \kbd{ell} type is a vector, the $j$-invariant being the thirteenth
+component. It could be implemented as
+
+\bprog
+x.j =
+{
+  if (type(x) != "t_VEC" || #x < 14, error("not an elliptic curve: " x));
+  x[13]
+}
+ at eprog\noindent As for user functions, you can redefine your member functions
+simply by typing new definitions. On the other hand, as a safety measure, you
+cannot redefine the built-in member functions, so attempting to redefine
+\kbd{x.j} as above would in fact produce an error; you would have to call it
+e.g.~\kbd{x.myj} in order for \kbd{gp} to accept it.
+
+\misctitle{Rationale} In most cases, member functions are simple accessors
+of the form
+\bprog
+  x.a = x[1];
+  x.b = x[2];
+  x.c = x[3];
+ at eprog\noindent
+where \kbd{x} is a vector containing relevant data. There are at least
+three alternative approaches to the above member functions: 1) hardcode
+\kbd{x[1]}, etc. in the program text, 2) define constant global variables
+\kbd{AINDEX = 1}, \kbd{BINDEX = 2}  and hardcode \kbd{x[AINDEX]}, 3)
+user functions \kbd{a(x) = x[1]} and so on.
+
+Even if 2) improves on 1), these solutions are neither elegant nor flexible,
+and they scale badly. 3) is a genuine possibility, but the main advantage of
+member functions is that their namespace is independent from the variables
+(and functions) namespace, hence we can use very short identifiers without
+risk. The $j$-invariant is a good example: it would clearly not be a good
+idea to define \kbd{j(E) = E[13]}, because clashes with loop indices are
+likely.
+
+\misctitle{Note} Typing \b{um} will output all user-defined member functions.
+
+\misctitle{Member function names} A valid name starts with a letter followed
+by any number of keyword characters: \kbd{\_} or alphanumeric characters
+([\kbd{A-Za-z0-9}]). The built-in member function names are reserved and
+cannot be used (see the list with \kbd{?.}). Finally, names starting with
+\kbd{e} or \kbd{E} followed by a digit are forbidden, due to a clash with
+the floating point exponent notation: we understand \kbd{1.e2} as
+$100.000\dots$, not as extracting member \kbd{e2} of object \kbd{1}.
+
+\section{Strings and Keywords}\sidx{string}\sidx{keyword}
+\label{se:strings}
+
+\subsec{Strings} GP variables can hold values of type character string
+(internal type \typ{STR}). This section describes how they are actually used,
+as well as some convenient tricks (automatic concatenation and expansion,
+keywords) valid in string context.
+
+As explained above, the general way to input a string is to enclose
+characters between quotes~\kbd{"}. This is the only input construct where
+whitespace characters are significant: the string will contain the exact
+number of spaces you typed in. Besides, you can ``escape'' characters by
+putting a \kbd{\bs} just before them; the translation is as follows
+\bprog
+   \e: <Escape>
+   \n: <Newline>
+   \t: <Tab>
+ at eprog
+For any other character $x$, \b{$x$} is expanded to $x$. In particular, the
+only way to put a \kbd{"} into a string is to escape it. Thus, for
+instance, \kbd{"\bs"a\bs""} would produce the string whose content is
+``a''. This is definitely \emph{not} the same thing as typing \kbd{"a"},
+whose content is merely the one-letter string a.
+
+You can concatenate two strings using the \tet{concat} function. If either
+argument is a string, the other is automatically converted to a string if
+necessary (it will be evaluated first).
+
+\bprog
+? concat("ex", 1+1)
+%1 = "ex2"
+? a = 2; b = "ex"; concat(b, a)
+%2 = "ex2"
+? concat(a, b)
+%3 = "2ex"
+ at eprog
+
+Some functions expect strings for some of their arguments: \tet{print} would
+be an obvious example, \tet{Str} is a less obvious but useful one (see the
+end of this section for a complete list). While typing in such an argument,
+you will be said to be in \tev{string context}. The rest of this section is
+devoted to special syntactical tricks which can be used with such arguments
+(and only here; you will get an error message if you try these outside of
+string context):
+
+\item Writing two strings alongside one another will just concatenate
+them, producing a longer string. Thus it is equivalent to type in
+\kbd{"a " "b"} or \kbd{"a b"}. A little tricky point in the first expression:
+the first whitespace is enclosed between quotes, and so is part of a string;
+while the second (before the \kbd{"b"}) is completely optional and \kbd{gp}
+actually suppresses it, as it would with any number of whitespace characters
+at this point (i.e.~outside of any string).
+
+\item If you insert any expression when a string is expected, it gets
+``expanded'': it is evaluated as a standard GP expression, and the final
+result (as would have been printed if you had typed it by itself) is then
+converted to a string, as if you had typed it directly. For instance \kbd{"a"
+1+1 "b"} is equivalent to \kbd{"a2b"}: three strings get created, the middle
+one being the expansion of \kbd{1+1}, and these are then concatenated
+according to the rule described above. Another tricky point here: assume you
+did not assign a value to \kbd{aaa} in a GP expression before. Then typing
+\kbd{aaa} by itself in a string context will actually produce the correct
+output (i.e.~the string whose content is aaa), but in a fortuitous way. This
+\kbd{aaa} gets expanded to the monomial of degree one in the variable
+\kbd{aaa}, which is of course printed as \kbd{aaa}, and thus will expand to
+the three letters you were expecting.
+
+\misctitle{Warning} Expression involving strings are not handled in a
+special way; even in string context, the largest possible expression is
+evaluated, hence \kbd{print("a"[1])} is incorrect since \kbd{"a"} is not an
+object whose first component can be extracted. On the other hand
+\kbd{print("a", [1])} is correct (two distinct argument, each converted to a
+string), and so is \kbd{print("a" 1)} (since \kbd{"a"1} is not a valid
+expression, only \kbd{"a"} gets expanded, then \kbd{1}, and the result is
+concatenated as explained above).
+
+\subsec{Keywords} Since there are cases where expansion is not desirable, we
+now distinguish between ``Keywords'' and ``Strings''. String is what has been
+described so far. Keywords are special relatives of Strings which are
+automatically assumed to be quoted, whether you actually type in the quotes
+or not. Thus expansion is never performed on them. They get concatenated,
+though. The analyzer supplies automatically the quotes you have ``forgotten''
+and treats Keywords just as normal strings otherwise. For instance, if you
+type \kbd{"a"b+b} in Keyword context, you will get the string whose contents
+are ab+b. In String context, on the other hand, you would get a2\kbd{*}b.
+
+All GP functions have prototypes (described in Chapter~3 below) which
+specify the types of arguments they expect: either generic PARI objects
+(GEN), or strings, or keywords, or unevaluated expression sequences. In the
+keyword case, only a very small set of words will actually be meaningful
+(the \kbd{default} function is a prominent example).
+
+\misctitle{Reference} The arguments of the following functions are processed
+in string context:
+
+\settabs\+\indent&\cr
+\+&\tet{Str}\cr
+\+&\tet{addhelp} (second argument)\cr
+\+&\tet{default} (second argument)\cr
+\+&\tet{error}\cr
+\+&\tet{extern}\cr
+\+&\tet{plotstring} (second argument)\cr
+\+&\tet{plotterm} (first argument)\cr
+\+&\tet{read} and \tet{readvec}\cr
+\+&\tet{system}\cr
+\+&all the \tet{print}\var{xxx} functions\cr
+\+&all the \tet{write}\var{xxx} functions\cr
+
+\noindent The arguments of the following functions are processed as keywords:
+
+\+&\tet{alias}\cr
+\+&\tet{default} (first argument)\cr
+\+&\tet{install} (all arguments but the last)\cr
+\+&\tet{trap} (first argument)\cr
+\+&\tet{whatnow}\cr
+
+\subsec{Useful examples} The function \kbd{Str} converts its arguments into
+strings and concatenate them. Coupled with \tet{eval}, it is very powerful.
+The following example creates generic matrices\sidx{generic
+matrix}\sidx{matrix}:
+\bprog
+? genmat(u,v,s="x") = matrix(u,v,i,j, eval( Str(s,i,j) ))
+? genmat(2,3) + genmat(2,3,"m")
+%1 =
+[x11 + m11 x12 + m12 x13 + m13]
+[x21 + m21 x22 + m22 x23 + m23]
+ at eprog\noindent
+
+Two last examples: \kbd{hist(10,20)} returns all \idx{history} entries from
+\kbd{\%10} to \kbd{\%20} neatly packed into a single
+vector; \kbd{histlast(10)} returns the last $10$ history entries:
+\bprog
+  hist(a,b) = vector(b-a+1, i, eval(Str("%", a-1+i)))
+  histlast(n) = vector(n, i, eval(Str("%", %#-i+1)))
+ at eprog
+
+\section{Errors and error recovery}
+
+\subsec{Errors} Your input program is first compiled to a more efficient
+bytecode; then the latter is evaluated, calling appropriate functions from
+the PARI library. Accordingly, there are two kind of errors: syntax errors
+produced by the compiler, and runtime errors produced by the PARI library
+either by the evaluator itself, or in a mathematical function.
+Both kinds are fatal to your computation: \kbd{gp} will report the error,
+perform some cleanup (restore variables modified while evaluating the
+erroneous command, close open files, reclaim unused memory, etc.), and output
+its usual prompt.
+
+When reporting a \emph{syntax error}, \kbd{gp} gives meaningful
+context by copying (part of) the expression it was trying to compile,
+indicating where the error occurred with a caret \kbd{\pow-}, as in
+\bprog
+? factor()
+  ***   too few arguments: factor()
+  ***                             ^-
+? 1+
+  ***   syntax error, unexpected $end: 1+
+  ***                                   ^-
+ at eprog\noindent
+possibly enlarged to a full arrow given enough trailing context
+\bprog
+? if (isprime(1+, do_something())
+  ***   syntax error, unexpected ',': if(isprime(1+,do_something()))
+  ***                                              ^----------------
+ at eprog\noindent
+These error messages may be mysterious, because \kbd{gp} cannot guess what
+you were trying to do, and the error may occur once \kbd{gp} has been
+sidetracked. The first error is straightforward: \kbd{factor} has one
+mandatory argument, which is missing.
+
+The other two are simple typos involving an ill-formed addition
+\kbd{1 + } missing its second operand. The error messages differ
+because the parsing context is slightly different: in the first
+case we reach the end of input (\kbd{\$end}) while still expecting a token,
+and in the second one, we received an unexpected token (the comma).
+
+Here is a more complicated one:
+\bprog
+? factor(x
+  ***   syntax error, unexpected $end, expecting )-> or ',' or ')': factor(x
+  ***                                                                      ^-
+ at eprog\noindent
+The error is a missing parenthesis, but from \kbd{gp}'s point of view,
+you might as well have intended to give further arguments to \kbd{factor}
+(this is possible and useful, see the description of the function). In fact
+\kbd{gp} expected either a closing parenthesis, or a second argument
+separated from the first by a comma. And this is essentially what the error
+message says: we reached the end  of the input (\kbd{\$end}) while expecting
+a \kbd{')'} or a \kbd{','}.
+
+Actually, a third possibility is mentioned in the error message \kbd{)->},
+which could never be valid in the above context, but a subexpression like
+\kbd{(x)->sin(x)}, defining an inline closure would be valid, and the parser is
+not clever enough to rule that out, so we get the same message as in
+\bprog
+? (x
+  ***   syntax error, unexpected $end, expecting )-> or ',' or ')': (x
+  ***                                                                ^-
+ at eprog\noindent
+where all three proposed continuations would be valid.
+
+\emph{Runtime errors} from the evaluator are nicer because they answer a
+correctly worded query, otherwise the bytecode compiler would have protested
+first; here is a slightly pathological case:
+\bprog
+? if (siN(x) < eps, do_something())
+  ***   at top-level: if(siN(x)<eps,do_someth
+  ***                    ^--------------------
+  ***   not a function in function call
+ at eprog\noindent (no arrow!) The code is syntactically correct and compiled
+correctly, even though the \kbd{siN} function, a typo for \kbd{sin}, was not
+defined at this point. When trying to evaluate the bytecode, however, it
+turned out that \kbd{siN} is still undefined so we cannot evaluate the
+function call \kbd{siN(x)}.
+
+\emph{Library runtime errors} are even nicer because they have more
+mathematical content, which is easier to grasp than a parser's logic:
+\bprog
+? 1/Mod(2,4)
+  ***   at top-level: 1/Mod(2,4)
+  ***                  ^---------
+  *** _/_: impossible inverse in Fp_inv: Mod(2, 4).
+ at eprog\noindent telling us that a runtime error occurred while evaluating the
+binary \kbd{/} operator (the \kbd{\_} surrounding the operator are
+placeholders), more precisely the \kbd{Fp\_inv} library function was fed
+the argument \kbd{Mod(2,4)} and could not invert it. More context is provided
+if the error occurs deep in the call chain:
+\bprog
+? f(x) = 1/x;
+? g(N) = for(i = -N, N, f(i + O(5)));
+? g(10)
+  ***   at top-level: g(10)
+  ***                 ^-----
+  ***   in function g: for(i=-N,N,f(i))
+  ***                             ^-----
+  ***   in function f: 1/x
+  ***                   ^--
+  *** _/_: impossible inverse in ginv: O(5).
+ at eprog\noindent In this example, the debugger reports (at least) 3 enclosed
+frames: last (innermost) is the body of user function $f$, the body of $g$,
+and the top-level (global scope). In fact, the \kbd{for} loop in $g$'s body
+defines an extra frame, since there exist variables scoped to the loop body.
+
+\subsec{Error recovery}\sidx{error recovery}\label{se:errorrec}
+
+It is annoying to wait for a program to finish and find out the hard
+way that there was a mistake in it (like the division by 0 above), sending
+you back to the prompt. First you may lose some valuable intermediate data.
+Also, correcting the error may not be obvious; you might have to change your
+program, adding a number of extra statements and tests to narrow down
+the problem.
+
+A different situation, still related to error recovery, is when you
+actually foresee that some error may occur, are unable to prevent it, but
+quite capable of recovering from it, given the chance. Examples include lazy
+factorization, where you knowingly use a pseudo prime $N$ as if it were
+prime; you may then encounter an ``impossible'' situation, but this would
+usually exhibit a factor of $N$, enabling you to refine the factorization and
+go on. Or you might run an expensive computation at low precision to guess
+the size of the output, hence the right precision to use. You can then
+encounter errors like ``precision loss in truncation'', e.g when trying to
+convert \kbd{1E1000}, known to $28$ digits of accuracy, to an integer; or
+``division by 0'', e.g inverting \kbd{0E1000} when all accuracy has been
+lost, and no significant digit remains. It would be enough to restart part of
+the computation at a slightly higher precision.
+
+We now describe \tev{error trapping}, a useful mechanism which alleviates
+much of the pain in the first situation (the break loop debugger), and
+provides satisfactory ways out of the second one (the \tet{iferr} exception
+handler).
+
+\subsec{Break loop}\label{se:break_loop}
+
+A \tev{break loop} is a special debugging mode that you enter whenever a
+user interrupt (\kbd{Control-C}) or runtime error occurs, freezing the
+\kbd{gp} state, and preventing cleanup until you get out of the loop. By
+runtime error, we mean an error from the evaluator, the library or a user error
+(from \tet{error}), \emph{not} syntax errors. When a break loop starts, a
+prompt is issued (\kbd{break>}). You can type in a \kbd{gp} command, which is
+evaluated when you hit the \kbd{<Return>} key, and the result is printed as
+during the main \kbd{gp} loop, except that no history of results is kept. Then
+the break loop prompt reappears and you can type further commands as long as
+you do not exit the loop. If you are using readline, the history of commands is
+kept, and line editing is available as usual. If you type in a command that
+results in an error, you are sent back to the break loop prompt: errors do
+\var{not} terminate the loop.
+
+To get out of a break loop, you can use \tet{next}, \tet{break}, \tet{return},
+or type \kbd{C-d} (\kbd{EOF}), any of which will let \kbd{gp} perform its
+usual cleanup, and send you back to the \kbd{gp} prompt. Note that \kbd{C-d}
+is slightly dangerous, since typing it \emph{twice} will not only send you
+back to the \kbd{gp} prompt, but to your shell prompt! (Since \kbd{C-d} at
+the \kbd{gp} prompt exits the gp session.)
+
+If the break loop was started by a user interrupt \kbd{Control-C}, and not by
+an error, inputting an empty line, i.e hitting the \kbd{<Return>} key at the
+\kbd{break>} prompt, resumes the temporarily interrupted computation. A single
+empty line has no effect in case of a fatal error, to avoid getting get out of
+the loop prematurely, thereby losing valuable debugging data. Any of
+\tet{next}, \tet{break}, \tet{return}, or \kbd{C-d} will abort the computation
+and send you back to  the \kbd{gp} prompt as above.
+
+Break loops are useful as a debugging tool. You may inspect the values of
+\kbd{gp} variables to understand why an error occurred, or change
+\kbd{gp}'s state in the middle of a computation (increase debugging level,
+start storing results in a log file, set variables to different values\dots):
+hit \kbd{C-c}, type in your modifications, then let the computation go on as
+explained above. A break loop looks like this:
+\bprog
+? v = 0; 1/v
+  ***   at top-level: v=0;1/v
+  ***                      ^--
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop (type 'break' to go back to the GP prompt)
+break>
+ at eprog
+\noindent So the standard error message is printed first. The
+\kbd{break>} at the bottom is a prompt, and hitting \kbd{v} then
+\kbd{<Return>}, we see:
+\bprog
+break> v
+0
+ at eprog\noindent explaining the problem. We could have typed any \kbd{gp}
+command, not only the name of a variable, of course. Lexically-scoped
+variables are accessible to the evaluator during the break loop:
+\bprog
+? for(v = -2, 2, print(1/v))
+-1/2
+-1
+  ***   at top-level: for(v=-2,2,print(1/v))
+  ***                                   ^----
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop (type 'break' to go back to the GP prompt)
+break> v
+0
+ at eprog\noindent
+Even though loop indices are automatically lexically scoped and no longer
+exist when the break loop is run, enough debugging information is retained in
+the bytecode to reconstruct the evaluation context. Of course, when the error
+occurs in a nested chain of user function calls, lexically scoped variables are
+available only in the corresponding frame:
+\bprog
+? f(x) = 1/x;
+? g(x) = for(i = 1, 10, f(x+i));
+? for(j = -5,5, g(j))
+  ***   at top-level: for(j=-5,5,g(j))
+  ***                            ^-----
+  ***   in function g: for(i=1,10,f(x+i))
+  ***                             ^-------
+  ***   in function f: 1/x
+  ***                   ^--
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop: type 'break' to go back to GP prompt
+break> [i,j,x]     \\ @com the $x$ in $f$'s body.
+[i, j, 0]
+break> dbg_up      \\ @com go up one frame
+  ***   at top-level: for(j=-5,5,g(j))
+  ***                            ^-----
+  ***   in function g: for(i=1,10,f(x+i))
+  ***                             ^-------
+break> [i,j,x]      \\ @com the $x$ in $g$'s body, $i$ in the for loop.
+[5, j, -5]
+ at eprog
+The following GP commands are available during a break loop to help debugging:
+
+\tet{dbg_up}$(n)$: go up $n$ frames, as seen above.
+
+\tet{dbg_down}$(n)$: go down $n$ frames, cancelling previous \kbd{dbg\_up}'s.
+
+\tet{dbg_x}$(t)$: examine $t$, as \kbd{\bs x} but more flexible.
+
+\tet{dbg_err}$()$: returns the current error context \typ{ERROR}. The error
+ components often provide useful additional information:
+\bprog
+  ? O(2) + O(3)
+    ***   at top-level: O(2)+O(3)
+    ***                     ^-----
+    *** _+_: inconsistent addition t_PADIC + t_PADIC.
+    ***   Break loop: type 'break' to go back to GP prompt
+  break> E = dbg_err()
+  error("inconsistent addition t_PADIC + t_PADIC.")
+  break> Vec(E)
+  ["e_OP", "+", O(2), O(3)]
+ at eprog
+
+\misctitle{Note} The debugger is enabled by default, and fires up as soon as
+a runtime error occurs. If you do not like this behavior, you may disable it by
+setting the default \tet{breakloop} to 0 in for \kbd{gprc}. A runtime error
+will send you back to the prompt. Note that the break loop is automatically
+disabled when running \kbd{gp} in non interactive mode, i.e.~when the program's
+standard input is not attached to a terminal.
+
+\misctitle{Technical Note} When you enter a break loop due to a PARI stack
+overflow, the PARI stack is reset so that you can run commands. Otherwise the
+stack would immediately overflow again! Still, as explained above, you do not
+lose the value of any \kbd{gp} variable in the process.
+
+\subsec{Protecting code}
+The expression
+
+  \kbd{iferr(\var{statements}, ERR, \var{recovery})}
+
+\noindent evaluates and returns the value of \var{statements}, unless an
+error occurs during the evaluation in which case the value of \var{recovery}
+is returned. As in an if/else clause, with the difference that
+\var{statements} has been partially evaluated, with possible side effects.
+We shall give a lot more details about the \kbd{ERR} argument shortly; it is
+the name of a variable, lexically scoped to the \var{recovery} expression
+sequence, whose value is set by the exception handler to help the recovery
+code decide what to do about the error.
+
+For instance one can define a fault tolerant inversion function as follows:
+\bprog
+? inv(x) = iferr(1/x, ERR, "oo")    \\ ERR is unused...
+? for (i=-1,1, print(inv(i)))
+-1
+oo
+1
+ at eprog\noindent Protected codes can be nested without adverse effect.
+Let's now see how \kbd{ERR} can be used; as written, \kbd{inv} is too
+tolerant:
+\bprog
+? inv("blah")
+%2 = "oo"
+ at eprog Let's improve it by checking that we caught a ``division by 0''
+exception, and not an unrelated one like the type error \kbd{1 / "blah"}.
+\bprog
+? inv2(x) = {
+  iferr(1/x,
+        ERR, if (errname(ERR) != "e_INV", error(ERR)); "oo")
+}
+? inv2(0)
+%3 = "oo"  \\ as before
+? inv2("blah")
+  ***   at top-level: inv2("blah")
+  ***                 ^------------
+  ***   in function inv2: ...f(errname(ERR)!="e_INV",error(ERR));"oo")
+  ***                                                 ^-----------------
+  *** error: forbidden division t_INT / t_STR.
+  @eprog\noindent In the \kbd{inv2("blah")} example, the error type was not
+expected, so we rethrow the exception: \kbd{error(ERR)} triggers the original
+error that we mistakenly trapped. Since the recovery code should always check
+whether the error is the one expected, this construction is very common and
+can be simplified to
+\bprog
+? inv3(x) = iferr(1/x,
+                  ERR, "oo",
+                  errname(ERR) == "e_INV")
+ at eprog\noindent More generally
+
+  \kbd{iferr(\var{statements}, ERR, \var{recovery}, \var{predicate})}
+
+\noindent only catches the exception if \var{predicate} (allowed to check
+various things about \kbd{ERR}, not only its name) is non-zero.
+
+Rather than trapping everything, then rethrowing whatever we do not like, we
+advise to only trap errors of a specific kind, as above. Of course,
+sometimes, one just want to trap \emph{everything} because we do not know
+what to expect. The following function check whether \tet{install} works
+correctly in your \kbd{gp}:
+\bprog
+broken_install() =
+{ \\ can we install?
+  iferr(install(addii,GG),
+        ERR, return ("OS"));
+  \\ can we use the installed function?
+  iferr(if (addii(1,1) != 2, return("BROKEN")),
+        ERR, return("USE"));
+  return (0);
+}
+ at eprog
+\noindent The function returns
+\kbd{OS} if the operating system does not support \kbd{install},
+\kbd{USE} if using an installed function triggers an error,
+\kbd{BROKEN} if the installed function did not behave as expected,
+and 0 if everything works.
+
+The \kbd{ERR} formal parameter contains more useful data than just the error
+name, which we recovered using \kbd{errname(ERR)}. In fact, a \typ{ERROR}
+object usually has extra components, which can be accessed as
+\kbd{component(ERR,1)}, \kbd{component(ERR,2)}, and so on. Or globally by
+casting the error to a \typ{VEC}: \kbd{Vec(ERR)} returns the vector
+of all components at once. See \secref{se:iferr} for the list of all
+exception types, and the corresponding contents of \kbd{ERR}.
+
+\section{Interfacing GP with other languages}
+\noindent
+The PARI library was meant to be interfaced with C programs. This specific
+use is dealt with extensively in the \emph{User's guide to the PARI library}.
+Of course, \kbd{gp} itself provides a convenient  interpreter to execute
+rather intricate scripts (see \secref{se:programming}).
+
+Scripts, when properly written, tend to be shorter and clearer than C
+programs, and are certainly easier to write, maintain or debug. You don't
+need to deal with memory management, garbage collection, pointers,
+declarations, and so on. Because of their intrinsic simplicity, they are more
+robust as well. They are unfortunately somewhat slower. Thus their use will
+remain complementary: it is suggested that you test and debug your algorithms
+using scripts, before actually coding them in C if speed is paramount.
+The GP2C compiler often eases this part.
+
+The \kbd{install} command (see~\secref{se:install}) efficiently imports
+foreign functions for use under \kbd{gp}, which can of course be written
+using other libraries than PARI. Thus you may code only critical parts
+of your program in C, and still maintain most of the program as a GP script.
+
+We are aware of three PARI-related Free Software packages to embed PARI in
+other languages. We \emph{neither endorse nor support} any of them, but you
+may want to give them a try if you are familiar with the languages they are
+based on. The first is William Stein's Python-based SAGE\footnote{*}{see
+\kbd{http://sagemath.org/}} system. The second is the \tet{Math::Pari} Perl
+module (see any CPAN mirror), written by Ilya Zakharevich.
+Finally, Michael Stoll has integrated PARI into \tet{CLISP}\footnote{***}{see
+\kbd{http://clisp.cons.org/}}, which is a Common Lisp implementation by Bruno
+Haible, Marcus Daniels and others; this interface has been updated for pari-2
+by Sam Steingold.
+
+These provide interfaces to \kbd{gp} functions for use in
+\kbd{python}, \kbd{perl}, or \kbd{Lisp}\sidx{Perl}\sidx{Python}\sidx{Lisp}
+programs, respectively.
+
+\section{Defaults}\sidx{defaults}
+\label{se:defaults}
+
+\noindent There are many internal variables in \kbd{gp}, defining how the
+system will behave in certain situations, unless a specific override has been
+given. Most of them are a matter of basic customization (colors, prompt) and
+will be set once and for all in your \idx{preferences file} (see
+\secref{se:gprc}), but some of them are useful interactively (set timer on,
+increase precision, etc.).
+
+The function used to manipulate these values is called \kbd{default}, which
+is described in \secref{se:default}. The basic syntax is
+
+\kbd{default(\var{def}, \var{value})},
+
+\noindent which sets the default \var{def} to \var{value}. In interactive
+use, most of these can be abbreviated using \kbd{gp} metacommands
+(mostly, starting with \b), which we shall describe in the next section.
+
+Here we will only describe the available defaults and how they are used. Just
+be aware that typing \kbd{default} by itself will list all of them, as well
+as their current values (see \b{d}). Just after the default name, we give
+between parentheses the initial value when \kbd{gp} starts, assuming you did
+not tamper with factory settings using command-line switches or a~\tet{gprc}.
+
+\misctitle{Note} The suffixes \kbd{k}, \kbd{M} or \kbd{G} can be appended to
+a \var{value} which is a numeric argument, with the effect of multiplying it
+by $10^3$, $10^6$ and $10^9$ respectively. Case is not taken into account
+there, so for instance \kbd{30k} and \kbd{30K} both stand for $30000$. This
+is mostly useful to modify or set the default \kbd{parisize} which typically
+involve a lot of trailing zeroes.
+
+\misctitle{(somewhat technical) Note} As we saw in \secref{se:strings},
+the second argument to \kbd{default} is subject to string context
+expansion, which means you can use run-time values. In other words, something
+like
+\bprog
+  a = 3;
+  default(logfile, "file" a ".log")
+ at eprog
+logs the output in \kbd{file3.log}.
+
+Some special defaults, corresponding to file names and prompts, expand further
+the resulting value at the time they are set. Two kinds of expansions may be
+performed:
+
+\item \teb{time expansion}: the string is sent through the library
+function \tet{strftime}. This means that \kbd{\%}\var{char} combinations have
+a special meaning, usually related to the time and date. For instance,
+\kbd{\%H} = hour (24-hour clock) and \kbd{\%M} = minute [00,59] (on a Unix
+system, you can try \kbd{man strftime} at your shell prompt to get a complete
+list). This is applied to \kbd{prompt}, \kbd{psfile}, and \kbd{logfile}. For
+instance,
+
+\kbd{default(prompt,"(\%H:\%M) ? ")}
+
+\noindent
+will prepend the time of day, in the form \kbd{(\var{hh}:\var{mm})}
+to \kbd{gp}'s usual prompt.
+
+\item \teb{environment expansion}: When the string contains a sequence of
+the form \kbd{\$\var{SOMEVAR}}, e.g.~\kbd{\$HOME}, the environment is
+searched and if \var{SOMEVAR} is defined, the sequence is replaced by the
+corresponding value. Also the \kbd{\til} symbol has the same meaning as in
+many shells~--- \kbd{\til} by itself stands for your home directory, and
+\kbd{\til{}user} is expanded to \kbd{user}'s home directory. This is applied
+to all file names\sidx{filename}. \label{se:envir}
+
+Available defaults are described in the reference guide,
+\secref{se:gp_defaults}.
+
+\section{Simple metacommands}\label{se:meta}
+
+\noindent
+Simple metacommands are meant as shortcuts and should not be used in GP
+scripts (see \secref{se:programming}). Beware that these, as all of \kbd{gp}
+input, are \emph{case sensitive}. For example, \b{Q} is not identical to
+\b{q}. In the following list, braces are used to denote optional arguments,
+with their default values when applicable, e.g.~$\{n=0\}$ means that if $n$
+is not there, it is assumed to be~$0$. Whitespace (or spaces) between the
+metacommand and its arguments and within arguments is optional. (This can
+cause problems only with \b{w}, when you insist on having a file name whose
+first character is a digit, and with \b{r} or \b{w}, if the file name itself
+contains a space. In such cases, just use the underlying \tet{read} or
+\tet{write} function; see~\secref{se:write}).
+
+\subseckbd{?$\{\var{command}\}$} The \kbd{gp} on-line help interface.
+If you type \kbd{?$n$} where $n$ is a number from 1 to 11, you will get the
+list of functions in Section $3.n$ of the manual (the list of sections being
+obtained by simply typing \kbd{?}). \label{se:exthelp}
+
+These names are in general not informative enough. More details can be
+obtained by typing \kbd{?\var{function}}, which gives a short explanation of
+the function's calling convention and effects. Of course, to have complete
+information, read Chapter 3 of this manual (the source code is at your
+disposal as well, though a trifle less readable).
+
+If the line before the copyright message indicates that extended help is
+available (this means \kbd{perl} is present on your system and the PARI
+distribution was correctly installed), you can add more \kbd{?} signs for
+extended functionality:
+
+\kbd{??~\var{keyword}} yields the function description as it stands in this
+manual, usually in Chapter~2 or~3. If you're not satisfied with the default
+chapter chosen, you can impose a given chapter by ending the keyword with
+\kbd{@} followed by the chapter number, e.g.~\kbd{??~Hello at 2} will look in
+Chapter~2 for section heading \kbd{Hello} (which doesn't exist, by the way).
+
+All operators (e.g.~\kbd{+}, \kbd{\&\&}, etc.) are accepted by this
+extended help, as well as a few other keywords describing key \kbd{gp} concepts,
+e.g.~\kbd{readline} (the line editor), \kbd{integer}, \kbd{nf} (``number
+field'' as used in most algebraic number theory computations), \kbd{ell}
+(elliptic curves), etc.
+
+In case of conflicts between \emph{function} and \emph{default} names
+(e.g \tet{log},
+\tet{simplify}), the function has higher priority. To get the \emph{default}
+help, use
+\bprog
+  ?? default(log)
+  ?? default(simplify)
+ at eprog
+
+\kbd{???~\var{pattern}} produces a list of sections in Chapter~3 of the
+manual related to your query. As before, if \var{pattern} ends by \kbd{@}
+followed by a chapter number, that chapter is searched instead; you also
+have the option to append a simple \kbd{@} (without a chapter number) to
+browse through the whole manual.
+
+If your query contains dangerous characters (e.g \kbd{?} or blanks) it is
+advisable to enclose it within double quotes, as for GP strings (e.g
+\kbd{???~"elliptic curve"}).
+
+Note that extended help is much more powerful than the short help, since
+it knows about operators as well: you can type \kbd{??~*} or
+\kbd{??~\&\&}, whereas a single \kbd{?} would just yield a not too helpful
+\bprog
+&&: unknown identifier.}
+ at eprog\noindent message. Also, you can ask for extended help on section
+number~$n$ in Chapter~3, just by typing \kbd{??~$n$} (where \kbd{?$n$} would
+yield merely a list of functions). Finally, a few key concepts in \kbd{gp} are
+documented in this way: metacommands (e.g \kbd{??~"??"}), defaults (e.g
+\kbd{??~psfile}) and type names (e.g \typ{INT} or \kbd{integer}), as well as
+various miscellaneous keywords such as \kbd{edit} (short summary of line
+editor commands), \kbd{operator}, \kbd{member}, \kbd{"user defined"},
+\kbd{nf}, \kbd{ell}, \dots
+
+Last but not least: \kbd{??} without argument will open a \kbd{dvi}
+previewer (\kbd{xdvi} by default, \kbd{\$GPXDVI} if it is defined in your
+environment) containing the full user's manual. \kbd{??tutorial} and
+\kbd{??refcard} do the same with the \idx{tutorial} and \idx{reference card}
+respectively.
+
+\misctitle{Technical note} This functionality is provided by an
+external \kbd{perl} script that you are free to use outside any \kbd{gp} session
+(and modify to your liking, if you are perl-knowledgeable). It is called
+\tet{gphelp}, lies in the \kbd{doc} subdirectory of your distribution
+(just make sure you run \kbd{Configure} first, see Appendix~A) and is
+really two programs in one. The one which is used from within \kbd{gp} is
+\kbd{gphelp} which runs \TeX\ on a selected part of this manual, then opens
+a previewer. \kbd{gphelp -detex} is a text mode equivalent, which looks
+often nicer especially on a colour-capable terminal (see
+\kbd{misc/gprc.dft} for examples). The default \kbd{help} selects which
+help program will be used from within \kbd{gp}. You are welcome to improve this
+help script, or write new ones (and we would like to know about it
+so that we may include them in future distributions). By the way, outside
+of \kbd{gp} you can give more than one keyword as argument to \kbd{gphelp}.
+
+\subseckbd{/*...*/} A comment. Everything between the stars is ignored by
+\kbd{gp}. These comments can span any number of lines.
+
+\subseckbd{\bs\bs} A one-line comment. The rest of the line
+is ignored by \kbd{gp}.
+
+\subsec{\b{a} $\{n\}$} Prints the object number $n$ ($\%n$)
+in raw format. If the number $n$ is omitted, print the latest computed object
+($\%$). \label{se:history}
+
+\subsec{\b{c}}\sidx{available commands} Prints the list of all available
+hardcoded functions under \kbd{gp}, not including operators written as special
+symbols (see \secref{se:operators}). More information can be obtained using
+the \kbd{?} metacommand (see above). For user-defined functions / member
+functions, see \b{u} and \b{um}.
+
+\subsec{\b{d}} Prints the \idx{defaults} as described in the
+previous section (shortcut for \kbd{default()}, see \secref{se:default}).
+
+\subsec{\b{e} $\{n\}$} Switches the \tet{echo} mode on (1) or off (0). If
+$n$ is explicitly given, set echo to $n$.
+
+\subsec{\b{g} $\{n\}$} Sets the debugging level \tet{debug} to the
+non-negative integer $n$.
+
+\subsec{\b{gf} $\{n\}$} Sets the file usage debugging level \tet{debugfiles}
+to the non-negative integer $n$.
+
+\subsec{\b{gm} $\{n\}$} Sets the memory debugging level \tet{debugmem}
+to the non-negative integer $n$.
+
+\subsec{\b{h} $\{m$\kbd{-}$n\}$} Outputs some debugging info about the
+hashtable. If the argument is a number $n$, outputs the contents of cell
+$n$. Ranges can be given in the form $m$\kbd{-}$n$ (from cell $m$ to cell
+$n$, \$ = last cell). If a function name is given instead of a number or
+range, outputs info on the internal structure of the hash cell this
+function occupies (a \kbd{struct entree} in C). If the range is reduced to
+a dash ('\kbd{-}'), outputs statistics about hash cell usage.
+
+\subsec{\b{l} $\{$\var{logfile}$\}$} Switches \tet{log} mode on and off.
+If a \var{logfile} argument is given, change the default logfile name to
+\var{logfile} and switch log mode on.
+
+\subsec{\b{m}} As \b{a}, but using prettymatrix format.
+
+\subsec{\b{o} $\{n\}$} Sets \tet{output} mode to $n$ ($0$: raw, $1$:
+prettymatrix, $3$: external prettyprint).
+
+\subsec{\b{p} $\{n\}$} Sets \tet{realprecision} to $n$ decimal
+digits. Prints its current value if $n$ is omitted.
+
+\subsec{\b{ps} $\{n\}$} Sets \tet{seriesprecision} to $n$ significant terms.
+Prints its current value if $n$ is omitted.
+
+\subsec{\b{q}} Quits the \kbd{gp} session and returns to the system.
+Shortcut for \tet{quit}\kbd{()} (see \secref{se:quit}).
+
+\subsec{\b{r} $\{$\var{filename}$\}$} Reads into \kbd{gp} all the
+commands contained in the named file as if they had been typed from the
+keyboard, one line after the other. Can be used in combination with the \b{w}
+command (see below). Related but not equivalent to the function \kbd{read}
+(see \secref{se:read}); in particular, if the file contains more than one
+line of input, there will be one history entry for each of them, whereas
+\kbd{read} would only record the last one. If \var{filename} is omitted,
+re-read the previously used input file (fails if no file has ever been
+successfully read in the current session). If a \kbd{gp} \tet{binary file}
+(see \secref{se:writebin}) is read using this command, it is silently loaded,
+without cluttering the history.
+
+Assuming \kbd{gp} figures how to decompress files on your machine, this
+command accepts compressed files in \tet{compress}ed (\kbd{.Z}) or
+\tet{gzip}ped (\kbd{.gz} or \kbd{.z}) format. They will be uncompressed on
+the fly as \kbd{gp} reads them, without changing the files themselves.
+
+\subsec{\b{s}} Prints the state of the PARI \tev{stack} and \tev{heap}.
+This is used primarily as a debugging device for PARI.
+
+\subsec{\b{t}} Prints the \idx{internal longword format} of all the PARI
+types. The detailed bit or byte format of the initial codeword(s) is
+explained in Chapter~4, but its knowledge is not necessary for a \kbd{gp}
+user.
+
+\subsec{\b{u}} Prints the definitions of all user-defined functions.
+
+\subsec{\b{um}} Prints the definitions of all user-defined member functions.
+
+\subsec{\b{v}} Prints the \idx{version number} and implementation architecture
+(680x0, Sparc, Alpha, other) of the \kbd{gp} executable you are using.
+
+\subsec{\b{w} $\{n\}$ $\{$\var{filename}$\}$} Writes the object number
+$n$ ( $\%n$ ) into the named file, in raw format. If the number $n$ is
+omitted, writes the latest computed object ( $\%$ ). If \var{filename} is
+omitted, appends to \kbd{logfile} (the GP function \tet{write} is a trifle more
+powerful, as you can have arbitrary file names).
+
+\subsec{\b{x} $\{n\}$} Prints the complete tree with addresses and contents
+(in hexadecimal) of the \idx{internal representation} of the object number
+$n$ ( $\%n$ ). If the number $n$ is omitted, uses the latest computed object
+in \kbd{gp}. As for \b{s}, this is used primarily as a debugging device for
+PARI, and the format should be self-explanatory. The underlying GP
+function \tet{dbg_x} is more versatile, since it can be applied to other
+objects than history entries.
+
+\subsec{\b{y} $\{n\}$} Switches \kbd{simplify} on (1) or off (0). If $n$
+is explicitly given, set simplify to $n$.
+
+\subseckbd{\#} Switches the \kbd{timer} on or off.
+
+\subseckbd{\#\#} Prints the time taken by the latest computation.
+Useful when you forgot to turn on the \kbd{timer}.
+
+\section{The preferences file}\sidx{startup}\sidx{preferences file}
+\label{se:gprc}
+
+This file, called \tet{gprc} in the sequel, is used to modify or extend
+\kbd{gp} default behavior, in all \kbd{gp} sessions: e.g customize
+\kbd{default} values or load common user functions and aliases. \kbd{gp}
+opens the \kbd{gprc} file and processes the commands in there, \emph{before}
+doing anything else, e.g.~creating the PARI stack. If the file does not exist
+or cannot be read, \kbd{gp} will proceed to the initialization phase at once,
+eventually emitting a prompt. If any explicit command line switches are
+given, they override the values read from the preferences file.
+
+\subsec{Syntax} The syntax in the \kbd{gprc} file (and valid in this file
+only) is simple-minded, but should be sufficient for most purposes. The file
+is read line by line; as usual, white space is ignored unless surrounded by
+quotes and the standard multiline constructions using braces, \kbd{\bs}, or
+\kbd{=} are available (multiline comments between \kbd{/*~\dots~*/} are also
+recognized).
+
+\subsubsec{Preprocessor:}
+Two types of lines are first dealt with by a preprocessor:
+
+\item comments are removed. This applies to all text surrounded by
+\kbd{/*~\dots~*/} as well as to everything following \kbd{\bs\bs} on a given
+line.
+
+\item lines starting with \kbd{\#if} \var{boolean} are treated as
+comments if \var{boolean} evaluates to \kbd{false}, and read normally
+otherwise. The condition can be negated using either \kbd{\#if not} (or
+\kbd{\#if !}). If the rest of the current line is empty, the test applies to
+the next line (same behavior as \kbd{=} under \kbd{gp}). Only three tests can be
+performed:
+
+\kbd{EMACS}: \kbd{true} if \kbd{gp} is running in an Emacs or TeXmacs shell (see
+\secref{se:emacs}).
+
+\kbd{READL}: \kbd{true} if \kbd{gp} is compiled with \kbd{readline} support (see
+\secref{se:readline}).
+
+\kbd{VERSION} \var{op} \var{number}: where \var{op} is in the set
+$\{ \kbd{>}, \kbd{<}, \kbd{<=}, \kbd{>=} \}$, and \var{number} is a PARI
+version number of the form \var{Major}.\var{Minor}.\var{patch}, where the
+last two components can be omitted (i.e.~$1$ is understood as version $1.0.0$).
+This is \kbd{true} if \kbd{gp}'s version number satisfies the required
+inequality.
+
+\subsubsec{Commands:}
+After preprocessing, the remaining lines are executed as
+sequence of expressions (as usual, separated by \kbd{;} if necessary). Only
+two kinds of expressions are recognized:
+
+\item \var{default} \kbd{=} \var{value}, where \var{default} is one of
+the available defaults (see \secref{se:defaults}), which will be set to
+\var{value} on actual startup. Don't forget the quotes around strings
+(e.g.~for \kbd{prompt} or \kbd{help}).
+
+\item \kbd{read "\var{some\_GP\_file}"} where \kbd{\var{some\_GP\_file}}
+is a regular GP script this time, which will be read just before \kbd{gp}
+prompts you for commands, but after initializing the defaults. In particular,
+file input is delayed until the \kbd{gprc} has been fully loaded. This is the
+right place to input files containing \kbd{alias} commands, or your favorite
+macros.
+
+\noindent For instance you could set your prompt in the following portable way:
+\bprog
+\\ self modifying prompt looking like @com\hbox{\rm(18:03) \key{gp}\kbd{ >}}
+prompt   = "(%H:%M) \e[1mgp\e[m > "
+
+\\ readline wants non-printing characters to be braced between ^A/^B pairs
+#if READL prompt = "(%H:%M) ^A\e[1m^Bgp^A\e[m^B > "
+
+\\ escape sequences not supported under emacs
+#if EMACS prompt = "(%H:%M) gp > "
+ at eprog
+
+\noindent Note that any of the last two lines could be broken in the
+following way
+\bprog
+#if EMACS
+  prompt = "(%H:%M) gp > "
+ at eprog
+\noindent since the preprocessor directive applies to the next line if the
+current one is empty.
+
+A sample \kbd{gprc} file called \kbd{misc/gprc.dft} is provided in the
+standard distribution. It is a good idea to have a look at it and customize
+it to your needs. Since this file does not use multiline constructs, here is
+one (note the terminating \kbd{;} to separate the expressions):
+\bprog
+#if VERSION > 2.2.3
+{
+  read "my_scripts";     \\ syntax errors in older versions
+  new_galois_format = 1; \\ default introduced in 2.2.4
+}
+#if ! EMACS
+{
+  colors = "9, 5, no, no, 4, 1, 2";
+  help   = "gphelp -detex -ch 4 -cb 0 -cu 2";
+}
+ at eprog
+
+\subsec{The gprc location}
+When \kbd{gp} is started, it looks for a customization file, or \kbd{gprc} in
+the following places (in this order, only the first one found will be
+loaded):
+
+\noindent\item \kbd{gp} checks whether the environment variable
+\tet{GPRC} is set. On Unix, this can be done with something like: \smallskip
+
+\settabs\+\indent&\kbd{GPRC=/my/dir/anyname; export GPRC}\quad&\cr
+
+\+&\kbd{GPRC=/my/dir/anyname; export GPRC}\quad&in \kbd{sh} syntax
+(for instance in your \kbd{.profile}),\cr
+
+\+&\kbd{setenv GPRC /my/dir/anyname} &in \kbd{csh} syntax
+(in your \kbd{.login} or \kbd{.cshrc} file).\cr
+
+\+&\kbd{env GPRC=/my/dir/anyname gp} &on the command line launching \kbd{gp}.\cr
+
+\noindent If so, the file named by \kbd{\$GPRC} is the \kbd{gprc}.
+
+\noindent\item If \kbd{GPRC} is not set, and if the environment variable
+\kbd{HOME} is defined, \kbd{gp} then tries
+
+\kbd{\$HOME/.gprc} on a Unix system
+
+\kbd{\$HOME\bs gprc.txt} on a DOS, OS/2, or Windows system.
+
+\noindent\item If no gprc was found among the user files mentioned above
+we look for \kbd{/etc/gprc} for a system-wide gprc file (you will need root
+privileges to set up such a file yourself).
+
+\noindent\item Finally, we look in pari's \kbd{datadir} for a file named
+
+\kbd{.gprc} on a Unix system
+
+\kbd{gprc.txt} on a DOS, OS/2, or Windows system. If you are using our
+Windows installer, this is where the default preferences file is written.
+
+\noindent Note that on Unix systems, the \kbd{gprc}'s default name starts
+with a '.' and thus is hidden to regular \kbd{ls} commands; you need to type
+\kbd{ls -a} to list it.
+
+
+\section{Using readline} \sidx{line editor}\sidx{completion}
+
+This very useful library provides line editing and contextual completion
+to \kbd{gp}. You are encouraged to read the \kbd{readline} user manual,
+but we describe basic usage here.
+
+\misctitle{A (too) short introduction to readline}\label{se:readline}
+In the following, \kbd{C-} stands for ``the \kbd{Control} key combined with
+another'' and the same for \kbd{M-} with the \kbd{Meta} key; generally
+\kbd{C-} combinations act on characters, while the \kbd{M-} ones operate on
+words. The \kbd{Meta} key might be called \kbd{Alt} on some keyboards, will
+display a black diamond on most others, and can safely be replaced by
+\kbd{Esc} in any case.
+
+Typing any ordinary key inserts text where the cursor stands, the arrow keys
+enabling you to move in the line. There are many more movement commands,
+which will be familiar to the Emacs user, for instance \kbd{C-a}/\kbd{C-e}
+will take you to the start/end of the line, \kbd{M-b}/\kbd{M-f} move the
+cursor backward/forward by a word, etc. Just press the \kbd{<Return>} key at
+any point to send your command to \kbd{gp}.
+
+  All the commands you type at the \kbd{gp} prompt are stored in a history,
+a multiline command being saved as a single concatenated line. The Up and Down
+arrows (or \kbd{C-p}/\kbd{C-n}) will move you through the history,
+\kbd{M-<}/\kbd{M->} sending you to the start/end of the history.
+\kbd{C-r}/\kbd{C-s} will start an incremental backward/forward search. You
+can kill text (\kbd{C-k} kills till the end of line, \kbd{M-d} to the end of
+current word) which you can then yank back using the \kbd{C-y} key (\kbd{M-y}
+will rotate the kill-ring). \kbd{C-\_} will undo your last changes
+incrementally (\kbd{M-r} undoes all changes made to the current line).
+\kbd{C-t} and \kbd{M-t} will transpose the character (word) preceding the
+cursor and the one under the cursor.
+
+  Keeping the \kbd{M-} key down while you enter an integer (a minus sign
+meaning reverse behavior) gives an argument to your next readline command
+(for instance \kbd{M-- C-k} will kill text back to the start of line). If you
+prefer \idx{Vi}--style editing, \kbd{M-C-j} will toggle you to Vi mode.
+
+  Of course you can change all these default bindings. For that you need to
+create a file named \kbd{.inputrc} in your home directory. For instance
+(notice the embedding conditional in case you would want specific bindings
+for \kbd{gp}):
+%
+\bprog
+$if Pari-GP
+  set show-all-if-ambiguous
+  "\C-h": backward-delete-char
+  "\e\C-h": backward-kill-word
+  "\C-xd": dump-functions
+  (: "\C-v()\C-b"       #@com can be annoying when copy-pasting!
+  [: "\C-v[]\C-b"
+$endif
+ at eprog
+\noindent\kbd{C-x C-r} will re-read this init file, incorporating any
+changes made to it during the current session.
+
+\misctitle{Note} By default, \kbd{(} and \kbd{[} are bound to the function
+\kbd{pari-matched-insert} which, if ``electric parentheses'' are enabled
+(default: off) will automatically insert the matching closure (respectively
+\kbd{)} and \kbd{]}). This behavior can be toggled on and off by giving
+the numeric argument $-2$ to \kbd{(} (\kbd{M--2(}), which is useful if you
+want, e.g to copy-paste some text into the calculator. If you do not want a
+toggle, you can use \kbd{M--0} / \kbd{M--1} to specifically switch it on or
+off).
+
+\misctitle{Note} In some versions of readline (2.1 for instance), the
+\kbd{Alt} or \kbd{Meta} key can give funny results (output 8-bit accented
+characters for instance). If you do not want to fall back to the \kbd{Esc}
+combination, put the following two lines in your \kbd{.inputrc}:
+%
+\bprog
+  set convert-meta on
+  set output-meta off
+ at eprog
+
+\misctitle{Command completion and online help} Hitting
+\kbd{<TAB>} will complete words for you. This mechanism is context-dependent:
+\kbd{gp} will strive to only give you meaningful completions in a given
+context (it will fail sometimes, but only under rare and restricted
+conditions).
+
+  For instance, shortly after a \kbd{\til}, we expect a user name, then a
+path to some file. Directly after \kbd{default(} has been typed, we would
+expect one of the \kbd{default} keywords. After \kbd{whatnow(} , we expect
+the name of an old function, which may well have disappeared from this
+version. After a '.', we expect a member keyword. And generally of course, we
+expect any GP symbol which may be found in the hashing lists: functions (both
+yours and GP's), and variables.
+
+  If, at any time, only one completion is meaningful, \kbd{gp} will provide it
+together with
+
+\item an ending comma if we are completing a default,
+
+\item a pair of parentheses if we are completing a function name. In
+that case hitting \kbd{<TAB>} again will provide the argument list as given
+by the online help\footnote{*}{recall that you can always undo the effect
+of the preceding keys by hitting \kbd{C-\_}}.
+
+Otherwise, hitting \kbd{<TAB>} once more will give you the list of possible
+completions. Just experiment with this mechanism as often as possible,
+you will probably find it very convenient. For instance, you can obtain
+\kbd{default(seriesprecision,10)}, just by hitting \kbd{def<TAB>se<TAB>10},
+which saves 18 keystrokes (out of 27).
+
+  Hitting \kbd{M-h} will give you the usual short online help concerning the
+word directly beneath the cursor, \kbd{M-H} will yield the extended help
+corresponding to the \kbd{help} default program (usually opens a \idx{dvi}
+previewer, or runs a primitive tex-to-ASCII program). None of these disturb
+the line you were editing.
+
+\section{GNU Emacs and PariEmacs}
+\label{se:emacs}
+
+If you install the PariEmacs package (see Appendix A), you may use \kbd{gp}
+as a subprocess in \idx{Emacs}. You then need to include in your \kbd{.emacs}
+file the following lines:
+\bprog
+  (autoload 'gp-mode "pari" nil t)
+  (autoload 'gp-script-mode "pari" nil t)
+  (autoload 'gp "pari" nil t)
+  (autoload 'gpman "pari" nil t)
+
+  (setq auto-mode-alist
+    (cons '("\\.gp$" . gp-script-mode) auto-mode-alist))
+ at eprog
+\noindent which autoloads functions from the PariEmacs package and ensures
+that file with the \kbd{.gp} suffix are edited in gp-script mode.
+
+Once this is done, under GNU Emacs if you type \kbd{M-x gp} (where as usual
+\kbd{M} is the \kbd{Meta} key), a special shell will be started launching
+\kbd{gp} with the default stack size and prime limit. You can then work as
+usual under \kbd{gp}, but with all the facilities of an advanced text editor.
+See the PariEmacs documentation for customizations, menus, etc.
+
+\newpage
diff --git a/doc/usersch3.tex b/doc/usersch3.tex
new file mode 100644
index 0000000..d1b391c
--- /dev/null
+++ b/doc/usersch3.tex
@@ -0,0 +1,16201 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Functions and Operations Available in PARI and GP}
+\label{se:functions}
+
+The functions and operators available in PARI and in the GP/PARI calculator
+are numerous and ever-expanding. Here is a description of the ones available
+in version \vers. It should be noted that many of these functions accept
+quite different types as arguments, but others are more restricted. The list
+of acceptable types will be given for each function or class of functions.
+Except when stated otherwise, it is understood that a function or operation
+which should make natural sense is legal. In this chapter, we will describe
+the functions according to a rough classification. The general entry looks
+something like:
+
+\key{foo}$(x,\{\fl=0\})$: short description.
+
+The library syntax is \kbd{GEN foo(GEN x, long fl = 0)}.
+
+\noindent
+This means that the GP function \kbd{foo} has one mandatory argument $x$, and
+an optional one, $\fl$, whose default value is 0. (The $\{\}$ should not be
+typed, it is just a convenient notation we will use throughout to denote
+optional arguments.) That is, you can type \kbd{foo(x,2)}, or \kbd{foo(x)},
+which is then understood to mean \kbd{foo(x,0)}. As well, a comma or closing
+parenthesis, where an optional argument should have been, signals to GP it
+should use the default. Thus, the syntax \kbd{foo(x,)} is also accepted as a
+synonym for our last expression. When a function has more than one optional
+argument, the argument list is filled with user supplied values, in order.
+When none are left, the defaults are used instead. Thus, assuming that
+\kbd{foo}'s prototype had been
+$$\hbox{%
+\key{foo}$(\{x=1\},\{y=2\},\{z=3\})$,%
+}$$
+typing in \kbd{foo(6,4)} would give
+you \kbd{foo(6,4,3)}. In the rare case when you want to set some far away
+argument, and leave the defaults in between as they stand, you can use the
+``empty arg'' trick alluded to above: \kbd{foo(6,,1)} would yield
+\kbd{foo(6,2,1)}. By the way, \kbd{foo()} by itself yields
+\kbd{foo(1,2,3)} as was to be expected.
+
+In this rather special case of a function having no mandatory argument, you
+can even omit the $()$: a standalone \kbd{foo} would be enough (though we
+do not recommend it for your scripts, for the sake of clarity). In defining
+GP syntax, we strove to put optional arguments at the end of the argument
+list (of course, since they would not make sense otherwise), and in order of
+decreasing usefulness so that, most of the time, you will be able to ignore
+them.
+
+Finally, an optional argument (between braces) followed by a star, like
+$\{\var{x}\}*$, means that any number of such arguments (possibly none) can
+be given. This is in particular used by the various \kbd{print} routines.
+
+\misctitle{Flags} A \tev{flag} is an argument which, rather than conveying
+actual information to the routine, instructs it to change its default
+behavior, e.g.~return more or less information. All such
+flags are optional, and will be called \fl\ in the function descriptions to
+follow. There are two different kind of flags
+
+\item generic: all valid values for the flag are individually
+described (``If \fl\ is equal to $1$, then\dots'').
+
+\item binary:\sidx{binary flag} use customary binary notation as a
+compact way to represent many toggles with just one integer. Let
+$(p_0,\dots,p_n)$ be a list of switches (i.e.~of properties which take either
+the value $0$ or~$1$), the number $2^3 + 2^5 = 40$ means that $p_3$ and $p_5$
+are set (that is, set to $1$), and none of the others are (that is, they
+are set to $0$). This is announced as ``The binary digits of $\fl$ mean 1:
+$p_0$, 2: $p_1$, 4: $p_2$'', and so on, using the available consecutive
+powers of~$2$.
+
+\misctitle{Mnemonics for flags} Numeric flags as mentioned above are
+obscure, error-prone, and quite rigid: should the authors
+want to adopt a new flag numbering scheme (for instance when noticing
+flags with the same meaning but different numeric values across a set of
+routines), it would break backward compatibility. The only advantage of
+explicit numeric values is that they are fast to type, so their use is only
+advised when using the calculator \kbd{gp}.
+
+As an alternative, one can replace a numeric flag by a character string
+containing symbolic identifiers. For a generic flag, the mnemonic
+corresponding to the numeric identifier is given after it as in
+
+\bprog
+fun(x, {flag = 0} ):
+
+  If flag is equal to 1 = AGM, use an agm formula ...
+ at eprog\noindent
+which means that one can use indifferently \kbd{fun($x$, 1)} or
+\kbd{fun($x$, "AGM")}.
+
+For a binary flag, mnemonics corresponding to the various toggles are given
+after each of them. They can be negated by prepending \kbd{no\_} to the
+mnemonic, or by removing such a prefix. These toggles are grouped together
+using any punctuation character (such as ',' or ';'). For instance (taken
+from description of $\tet{ploth}(X=a,b,\var{expr},\{\fl=0\},\{n=0\})$)
+
+\centerline{Binary digits of flags mean: $1=\kbd{Parametric}$,
+$2=\kbd{Recursive}$, \dots}
+
+\noindent so that, instead of $1$, one could use the mnemonic
+\kbd{"Parametric; no\_Recursive"}, or simply \kbd{"Parametric"} since
+\kbd{Recursive} is unset by default (default value of $\fl$ is $0$,
+i.e.~everything unset). People used to the bit-or notation in languages like
+C may also use the form \kbd{"Parametric | no\_Recursive"}.
+
+\misctitle{Pointers} \varsidx{pointer} If a parameter in the function
+prototype is prefixed with a \& sign, as in
+
+\key{foo}$(x,\&e)$
+
+\noindent it means that, besides the normal return value, the function may
+assign a value to $e$ as a side effect. When passing the argument, the \&
+sign has to be typed in explicitly. As of version \vers, this \tev{pointer}
+argument is optional for all documented functions, hence the \& will always
+appear between brackets as in \kbd{Z\_issquare}$(x,\{\&e\})$.
+
+\misctitle{About library programming}
+The \var{library} function \kbd{foo}, as defined at the beginning of this
+section, is seen to have two mandatory arguments, $x$ and \fl: no function
+seen in the present chapter has been implemented so as to accept a variable
+number of arguments, so all arguments are mandatory when programming with the
+library (usually, variants are provided corresponding to the various flag values).
+We include an \kbd{= default value} token in the prototype to signal how a missing
+argument should be encoded. Most of the time, it will be a \kbd{NULL} pointer, or
+-1 for a variable number. Refer to the \emph{User's Guide to the PARI library}
+for general background and details.
+
+\section{Standard monadic or dyadic operators}
+
+\subseckbd{+$/$-} The expressions \kbd{+}$x$ and \kbd{-}$x$ refer
+to monadic operators (the first does nothing, the second negates $x$).
+
+The library syntax is \fun{GEN}{gneg}{GEN x} for \kbd{-}$x$.
+
+\subseckbd{+} The expression $x$ \kbd{+} $y$ is the \idx{sum} of $x$ and $y$.
+Addition between a scalar type $x$ and a \typ{COL} or \typ{MAT} $y$ returns
+respectively $[y[1] + x, y[2],\dots]$ and $y + x \text{Id}$. Other additions
+between a scalar type and a vector or a matrix, or between vector/matrices of
+incompatible sizes are forbidden.
+
+The library syntax is \fun{GEN}{gadd}{GEN x, GEN y}.
+
+\subseckbd{-} The expression $x$ \kbd{-} $y$ is the \idx{difference} of $x$
+and $y$. Subtraction between a scalar type $x$ and a \typ{COL} or \typ{MAT}
+$y$ returns respectively $[y[1] - x, y[2],\dots]$ and $y - x \text{Id}$.
+Other subtractions between a scalar type and a vector or a matrix, or
+between vector/matrices of incompatible sizes are forbidden.
+
+The library syntax is \fun{GEN}{gsub}{GEN x, GEN y} for $x$ \kbd{-} $y$.
+
+\subseckbd{*} The expression $x$ \kbd{*} $y$ is the \idx{product} of $x$
+and $y$. Among the prominent impossibilities are multiplication between
+vector/matrices of incompatible sizes, between a \typ{INTMOD} or \typ{PADIC}
+Restricted to scalars, \kbd{*} is commutative; because of vector and matrix
+operations, it is not commutative in general.
+
+Multiplication between two \typ{VEC}s or two \typ{COL}s is not
+allowed; to take the \idx{scalar product} of two vectors of the same length,
+transpose one of the vectors (using the operator \kbd{\til} or the function
+\kbd{mattranspose}, see \secref{se:linear_algebra}) and multiply a line vector
+by a column vector:
+\bprog
+? a = [1,2,3];
+? a * a
+  ***   at top-level: a*a
+  ***                  ^--
+  *** _*_: forbidden multiplication t_VEC * t_VEC.
+? a * a~
+%2 = 14
+ at eprog
+
+If $x,y$ are binary quadratic forms, compose them; see also
+\kbd{qfbnucomp} and \kbd{qfbnupow}. If $x,y$ are \typ{VECSMALL} of the same
+length, understand them as permutations and compose them.
+
+The library syntax is \fun{GEN}{gmul}{GEN x, GEN y} for $x$ \kbd{*} $y$.
+Also available is \fun{GEN}{gsqr}{GEN x} for $x$ \kbd{*} $x$.
+
+\subseckbd{/} The expression $x$ \kbd{/} $y$ is the \idx{quotient} of $x$
+and $y$. In addition to the impossibilities for multiplication, note that if
+the divisor is a matrix, it must be an invertible square matrix, and in that
+case the result is $x*y^{-1}$. Furthermore note that the result is as exact
+as possible: in particular, division of two integers always gives a rational
+number (which may be an integer if the quotient is exact) and \emph{not} the
+Euclidean quotient (see $x$ \kbd{\bs} $y$ for that), and similarly the
+quotient of two polynomials is a rational function in general. To obtain the
+approximate real value of the quotient of two integers, add \kbd{0.} to the
+result; to obtain the approximate $p$-adic value of the quotient of two
+integers, add \kbd{O(p\pow k)} to the result; finally, to obtain the
+\idx{Taylor series} expansion of the quotient of two polynomials, add
+\kbd{O(X\pow k)} to the result or use the \kbd{taylor} function
+(see \secref{se:taylor}). \label{se:gdiv}
+
+The library syntax is \fun{GEN}{gdiv}{GEN x, GEN y} for $x$ \kbd{/} $y$.
+
+\subseckbd{\bs} The expression \kbd{$x$ \bs\ $y$} is the \idx{Euclidean
+quotient} of $x$ and $y$. If $y$ is a real scalar, this is defined as
+\kbd{floor($x$/$y$)} if $y > 0$, and \kbd{ceil($x$/$y$)} if $y < 0$ and
+the division is not exact. Hence the remainder \kbd{$x$ - ($x$\bs$y$)*$y$}
+is in $[0, |y|[$.
+
+Note that when $y$ is an integer and $x$ a polynomial, $y$ is first promoted
+to a polynomial of degree $0$. When $x$ is a vector or matrix, the operator
+is applied componentwise.
+
+The library syntax is \fun{GEN}{gdivent}{GEN x, GEN y}
+for $x$ \kbd{\bs} $y$.
+
+\subseckbd{\bs/} The expression $x$ \b{/} $y$ evaluates to the rounded
+\idx{Euclidean quotient} of $x$ and $y$. This is the same as \kbd{$x$ \bs\ $y$}
+except for scalar division: the quotient is such that the corresponding
+remainder is smallest in absolute value and in case of a tie the quotient
+closest to $+\infty$ is chosen (hence the remainder would belong to
+$]{-}|y|/2, |y|/2]$).
+
+When $x$ is a vector or matrix, the operator is applied componentwise.
+
+The library syntax is \fun{GEN}{gdivround}{GEN x, GEN y}
+for $x$ \b{/} $y$.
+
+\subseckbd{\%} The expression \kbd{$x$ \% $y$} evaluates to the modular
+\idx{Euclidean remainder} of $x$ and $y$, which we now define. When $x$ or $y$
+is a non-integral real number, \kbd{$x$\%$y$} is defined as \kbd{$x$ -
+($x$\bs$y$)*$y$}. Otherwise, if $y$ is an integer, this is the smallest
+non-negative integer congruent to $x$ modulo $y$. (This actually coincides with
+the previous definition if and only if $x$ is an integer.) If $y$ is a
+polynomial, this is the polynomial of smallest degree congruent to $x$ modulo
+$y$. For instance:
+\bprog
+? (1/2) % 3
+%1 = 2
+? 0.5 % 3
+%2 = 0.5000000000000000000000000000
+? (1/2) % 3.0
+%3 = 1/2
+ at eprog
+Note that when $y$ is an integer and $x$ a polynomial, $y$ is first promoted
+to a polynomial of degree $0$. When $x$ is a vector or matrix, the operator
+is applied componentwise.
+
+The library syntax is \fun{GEN}{gmod}{GEN x, GEN y}
+for $x$ \kbd{\%} $y$.
+
+\subseckbd{\pow} The expression $x\hbox{\kbd{\pow}}n$ is \idx{powering}.
+If the exponent is an integer, then exact operations are performed using
+binary (left-shift) powering techniques. In particular, in this case $x$
+cannot be a vector or matrix unless it is a square matrix (invertible
+if the exponent is negative). If $x$ is a $p$-adic number, its
+precision will increase if $v_p(n) > 0$. Powering a binary quadratic form
+(types \typ{QFI} and \typ{QFR}) returns a reduced representative of the
+class, provided the input is reduced. In particular, $x\hbox{\kbd{\pow}}1$ is
+identical to $x$.
+
+PARI is able to rewrite the multiplication $x * x$ of two \emph{identical}
+objects as $x^2$, or $\kbd{sqr}(x)$. Here, identical means the operands are
+two different labels referencing the same chunk of memory; no equality test
+is performed. This is no longer true when more than two arguments are
+involved.
+
+If the exponent is not of type integer, this is treated as a transcendental
+function (see \secref{se:trans}), and in particular has the effect of
+componentwise powering on vector or matrices.
+
+As an exception, if the exponent is a rational number $p/q$ and $x$ an
+integer modulo a prime or a $p$-adic number, return a solution $y$ of
+$y^q=x^p$ if it exists. Currently, $q$ must not have large prime factors.
+Beware that
+\bprog
+? Mod(7,19)^(1/2)
+%1 = Mod(11, 19) /* is any square root */
+? sqrt(Mod(7,19))
+%2 = Mod(8, 19)  /* is the smallest square root */
+? Mod(7,19)^(3/5)
+%3 = Mod(1, 19)
+? %3^(5/3)
+%4 = Mod(1, 19)  /* Mod(7,19) is just another cubic root */
+ at eprog
+
+If the exponent is a negative integer, an \idx{inverse} must be computed.
+For non-invertible \typ{INTMOD}, this will fail and implicitly exhibit a
+non trivial factor of the modulus:
+\bprog
+? Mod(4,6)^(-1)
+  ***   at top-level: Mod(4,6)^(-1)
+  ***                         ^-----
+  *** _^_: impossible inverse modulo: Mod(2, 6).
+ at eprog\noindent
+(Here, a factor 2 is obtained directly. In general, take the gcd of the
+representative and the modulus.) This is most useful when performing
+complicated operations modulo an integer $N$ whose factorization is
+unknown. Either the computation succeeds and all is well, or a factor $d$
+is discovered and the computation may be restarted modulo $d$ or $N/d$.
+
+For non-invertible \typ{POLMOD}, this will fail without exhibiting a
+factor.
+\bprog
+? Mod(x^2, x^3-x)^(-1)
+  ***   at top-level: Mod(x^2,x^3-x)^(-1)
+  ***                               ^-----
+  *** _^_: non-invertible polynomial in RgXQ_inv.
+? a = Mod(3,4)*y^3 + Mod(1,4); b = y^6+y^5+y^4+y^3+y^2+y+1;
+? Mod(a, b)^(-1);
+  ***   at top-level: Mod(a,b)^(-1)
+  ***                         ^-----
+  *** _^_: impossible inverse modulo: Mod(0, 4).
+ at eprog\noindent
+In fact the latter polynomial is invertible, but the algorithm used
+(subresultant) assumes the base ring is a domain. If it is not the case,
+as here for $\Z/4\Z$, a result will be correct but chances are an error
+will occur first. In this specific case, one should work with $2$-adics.
+In general, one can try the following approach
+\bprog
+? inversemod(a, b) =
+{ my(m);
+  m = polsylvestermatrix(polrecip(a), polrecip(b));
+  m = matinverseimage(m, matid(#m)[,1]);
+  Polrev( vecextract(m, Str("..", poldegree(b))), variable(b) )
+}
+? inversemod(a,b)
+%2 = Mod(2,4)*y^5 + Mod(3,4)*y^3 + Mod(1,4)*y^2 + Mod(3,4)*y + Mod(2,4)
+ at eprog\noindent
+This is not guaranteed to work either since it must invert pivots. See
+\secref{se:linear_algebra}.
+
+The library syntax is \fun{GEN}{gpow}{GEN x, GEN n, long prec}
+for $x\hbox{\kbd{\pow}}n$.
+
+
+\subsec{cmp$(x,y)$}\kbdsidx{cmp}\label{se:cmp}
+Gives the result of a comparison between arbitrary objects $x$ and $y$
+(as $-1$, $0$ or $1$). The underlying order relation is transitive,
+the function returns $0$ if and only if $x~\kbd{===}~y$, and its
+restriction to integers coincides with the customary one. Besides that,
+it has no useful mathematical meaning.
+
+In case all components are equal up to the smallest length of the operands,
+the more complex is considered to be larger. More precisely, the longest is
+the largest; when lengths are equal, we have matrix $>$ vector $>$ scalar.
+For example:
+\bprog
+? cmp(1, 2)
+%1 = -1
+? cmp(2, 1)
+%2 = 1
+? cmp(1, 1.0)   \\ note that 1 == 1.0, but (1===1.0) is false.
+%3 = -1
+? cmp(x + Pi, [])
+%4 = -1
+ at eprog\noindent This function is mostly useful to handle sorted lists or
+vectors of arbitrary objects. For instance, if $v$ is a vector, the
+construction \kbd{vecsort(v, cmp)} is equivalent to \kbd{Set(v)}.
+
+The library syntax is \fun{GEN}{cmp_universal}{GEN x, GEN y}.
+
+\subsec{divrem$(x,y,\{v\})$}\kbdsidx{divrem}\label{se:divrem}
+Creates a column vector with two components, the first being the Euclidean
+quotient (\kbd{$x$ \bs\ $y$}), the second the Euclidean remainder
+(\kbd{$x$ - ($x$\bs$y$)*$y$}), of the division of $x$ by $y$. This avoids the
+need to do two divisions if one needs both the quotient and the remainder.
+If $v$ is present, and $x$, $y$ are multivariate
+polynomials, divide with respect to the variable $v$.
+
+Beware that \kbd{divrem($x$,$y$)[2]} is in general not the same as
+\kbd{$x$ \% $y$}; no GP operator corresponds to it:
+\bprog
+? divrem(1/2, 3)[2]
+%1 = 1/2
+? (1/2) % 3
+%2 = 2
+? divrem(Mod(2,9), 3)[2]
+ ***   at top-level: divrem(Mod(2,9),3)[2
+ ***                 ^--------------------
+ ***   forbidden division t_INTMOD \ t_INT.
+? Mod(2,9) % 6
+%3 = Mod(2,3)
+ at eprog
+
+The library syntax is \fun{GEN}{divrem}{GEN x, GEN y, long v = -1}, where \kbd{v} is a variable number.
+Also available is \fun{GEN}{gdiventres}{GEN x, GEN y} when $v$ is
+not needed.
+
+\subsec{lex$(x,y)$}\kbdsidx{lex}\label{se:lex}
+Gives the result of a lexicographic comparison
+between $x$ and $y$ (as $-1$, $0$ or $1$). This is to be interpreted in quite
+a wide sense: It is admissible to compare objects of different types
+(scalars, vectors, matrices), provided the scalars can be compared, as well
+as vectors/matrices of different lengths. The comparison is recursive.
+
+In case all components are equal up to the smallest length of the operands,
+the more complex is considered to be larger. More precisely, the longest is
+the largest; when lengths are equal, we have matrix $>$ vector $>$ scalar.
+For example:
+\bprog
+? lex([1,3], [1,2,5])
+%1 = 1
+? lex([1,3], [1,3,-1])
+%2 = -1
+? lex([1], [[1]])
+%3 = -1
+? lex([1], [1]~)
+%4 = 0
+ at eprog
+
+The library syntax is \fun{GEN}{lexcmp}{GEN x, GEN y}.
+
+\subsec{max$(x,y)$}\kbdsidx{max}\label{se:max}
+Creates the maximum of $x$ and $y$ when they can be compared.
+
+The library syntax is \fun{GEN}{gmax}{GEN x, GEN y}.
+
+\subsec{min$(x,y)$}\kbdsidx{min}\label{se:min}
+Creates the minimum of $x$ and $y$ when they can be compared.
+
+The library syntax is \fun{GEN}{gmin}{GEN x, GEN y}.
+
+\subsec{shift$(x,n)$}\kbdsidx{shift}\label{se:shift}
+Shifts $x$ componentwise left by $n$ bits if $n\ge0$ and right by $|n|$
+bits if $n<0$. May be abbreviated as $x$ \kbd{<<} $n$ or $x$ \kbd{>>} $(-n)$.
+A left shift by $n$ corresponds to multiplication by $2^n$. A right shift of an
+integer $x$ by $|n|$ corresponds to a Euclidean division of $x$ by $2^{|n|}$
+with a remainder of the same sign as $x$, hence is not the same (in general) as
+$x \kbd{\bs} 2^n$.
+
+The library syntax is \fun{GEN}{gshift}{GEN x, long n}.
+
+\subsec{shiftmul$(x,n)$}\kbdsidx{shiftmul}\label{se:shiftmul}
+Multiplies $x$ by $2^n$. The difference with
+\kbd{shift} is that when $n<0$, ordinary division takes place, hence for
+example if $x$ is an integer the result may be a fraction, while for shifts
+Euclidean division takes place when $n<0$ hence if $x$ is an integer the result
+is still an integer.
+
+The library syntax is \fun{GEN}{gmul2n}{GEN x, long n}.
+
+\subsec{sign$(x)$}\kbdsidx{sign}\label{se:sign}
+\idx{sign} ($0$, $1$ or $-1$) of $x$, which must be of
+type integer, real or fraction.
+
+The library syntax is \fun{GEN}{gsigne}{GEN x}.
+
+\subsec{vecmax$(x,\{\&v\})$}\kbdsidx{vecmax}\label{se:vecmax}
+If $x$ is a vector or a matrix, returns the largest entry of $x$,
+otherwise returns a copy of $x$. Error if $x$ is empty.
+
+If $v$ is given, set it to the index of a largest entry (indirect maximum),
+when $x$ is a vector. If $x$ is a matrix, set $v$ to coordinates $[i,j]$
+such that $x[i,j]$ is a largest entry. This flag is ignored if $x$ is not a
+vector or matrix.
+
+\bprog
+? vecmax([10, 20, -30, 40])
+%1 = 40
+? vecmax([10, 20, -30, 40], &v); v
+%2 = 4
+? vecmax([10, 20; -30, 40], &v); v
+%3 = [2, 2]
+ at eprog
+
+The library syntax is \fun{GEN}{vecmax0}{GEN x, GEN *v = NULL}.
+Also available is \fun{GEN}{vecmax}{GEN x}.
+
+\subsec{vecmin$(x,\{\&v\})$}\kbdsidx{vecmin}\label{se:vecmin}
+If $x$ is a vector or a matrix, returns the smallest entry of $x$,
+otherwise returns a copy of $x$. Error if $x$ is empty.
+
+If $v$ is given, set it to the index of a smallest entry (indirect minimum),
+when $x$ is a vector. If $x$ is a matrix, set $v$ to coordinates $[i,j]$ such
+that $x[i,j]$ is a smallest entry. This is ignored if $x$ is not a vector or
+matrix.
+
+\bprog
+? vecmin([10, 20, -30, 40])
+%1 = -30
+? vecmin([10, 20, -30, 40], &v); v
+%2 = 3
+? vecmin([10, 20; -30, 40], &v); v
+%3 = [2, 1]
+ at eprog
+
+The library syntax is \fun{GEN}{vecmin0}{GEN x, GEN *v = NULL}.
+Also available is \fun{GEN}{vecmin}{GEN x}.
+%SECTION: operators
+
+\subsec{Comparison and Boolean operators}\sidx{Boolean operators} The six
+standard \idx{comparison operators} \kbd{<=}, \kbd{<}, \kbd{>=}, \kbd{>},
+\kbd{==}, \kbd{!=} are available in GP. The result is 1 if the comparison is
+true, 0 if it is false. The operator \kbd{==} is quite liberal : for
+instance, the integer 0, a 0 polynomial, and a vector with 0 entries are all
+tested equal.
+
+The extra operator \kbd{===} tests whether two objects are identical and is
+much stricter than \kbd{==} : objects of different type or length are never
+identical.
+
+For the purpose of comparison, \typ{STR} objects are strictly larger than any
+other non-string type; two \typ{STR} objects are compared using the standard
+lexicographic order.
+
+GP accepts \kbd{<>} as a synonym for \kbd{!=}. On the other hand, \kbd{=} is
+definitely \emph{not} a synonym for \kbd{==}: it is the assignment statement.
+
+The standard boolean operators \kbd{||} (\idx{inclusive or}), \kbd{\&\&}
+(\idx{and})\sidx{or} and \kbd{!} (\idx{not}) are also available.
+
+\section{Conversions and similar elementary functions or commands}
+\label{se:conversion}
+
+\noindent
+Many of the conversion functions are rounding or truncating operations. In
+this case, if the argument is a rational function, the result is the
+Euclidean quotient of the numerator by the denominator, and if the argument
+is a vector or a matrix, the operation is done componentwise. This will not
+be restated for every function.
+
+
+\subsec{Col$(x, \{n\})$}\kbdsidx{Col}\label{se:Col}
+Transforms the object $x$ into a column vector. The dimension of the
+resulting vector can be optionally specified via the extra parameter $n$.
+
+If $n$ is omitted or $0$, the dimension depends on the type of $x$; the
+vector has a single component, except when $x$ is
+
+\item a vector or a quadratic form (in which case the resulting vector
+is simply the initial object considered as a row vector),
+
+\item a polynomial or a power series. In the case of a polynomial, the
+coefficients of the vector start with the leading coefficient of the
+polynomial, while for power series only the significant coefficients are
+taken into account, but this time by increasing order of degree.
+In this last case, \kbd{Vec} is the reciprocal function of \kbd{Pol} and
+\kbd{Ser} respectively,
+
+\item a matrix (the column of row vector comprising the matrix is returned),
+
+\item a character string (a vector of individual characters is returned).
+
+In the last two cases (matrix and character string), $n$ is meaningless and
+must be omitted or an error is raised. Otherwise, if $n$ is given, $0$
+entries are appended at the end of the vector if $n > 0$, and prepended at
+the beginning if $n < 0$. The dimension of the resulting vector is $|n|$.
+
+Note that the function \kbd{Colrev} does not exist, use \kbd{Vecrev}.
+
+The library syntax is \fun{GEN}{gtocol0}{GEN x, long n}.
+\fun{GEN}{gtocol}{GEN x} is also available.
+
+\subsec{Colrev$(x, \{n\})$}\kbdsidx{Colrev}\label{se:Colrev}
+As $\kbd{Col}(x, n)$, then reverse the result. In particular
+
+The library syntax is \fun{GEN}{gtocolrev0}{GEN x, long n}.
+\fun{GEN}{gtocolrev}{GEN x} is also available.
+
+\subsec{List$(\{x=[\,]\})$}\kbdsidx{List}\label{se:List}
+Transforms a (row or column) vector $x$ into a list, whose components are
+the entries of $x$. Similarly for a list, but rather useless in this case.
+For other types, creates a list with the single element $x$. Note that,
+except when $x$ is omitted, this function creates a small memory leak; so,
+either initialize all lists to the empty list, or use them sparingly.
+
+The library syntax is \fun{GEN}{gtolist}{GEN x = NULL}.
+The variant \fun{GEN}{listcreate}{void} creates an empty list.
+
+\subsec{Mat$(\{x=[\,]\})$}\kbdsidx{Mat}\label{se:Mat}
+Transforms the object $x$ into a matrix.
+If $x$ is already a matrix, a copy of $x$ is created.
+If $x$ is a row (resp. column) vector, this creates a 1-row (resp.
+1-column) matrix, \emph{unless} all elements are column (resp.~row) vectors
+of the same length, in which case the vectors are concatenated sideways
+and the associated big matrix is returned.
+If $x$ is a binary quadratic form, creates the associated $2\times 2$
+matrix. Otherwise, this creates a $1\times 1$ matrix containing $x$.
+
+\bprog
+? Mat(x + 1)
+%1 =
+[x + 1]
+? Vec( matid(3) )
+%2 = [[1, 0, 0]~, [0, 1, 0]~, [0, 0, 1]~]
+? Mat(%)
+%3 =
+[1 0 0]
+
+[0 1 0]
+
+[0 0 1]
+? Col( [1,2; 3,4] )
+%4 = [[1, 2], [3, 4]]~
+? Mat(%)
+%5 =
+[1 2]
+
+[3 4]
+? Mat(Qfb(1,2,3))
+%6 =
+[1 1]
+
+[1 3]
+ at eprog
+
+The library syntax is \fun{GEN}{gtomat}{GEN x = NULL}.
+
+\subsec{Mod$(a,b)$}\kbdsidx{Mod}\label{se:Mod}
+In its basic form, creates an intmod or a polmod $(a \mod b)$; $b$ must
+be an integer or a polynomial. We then obtain a \typ{INTMOD} and a
+\typ{POLMOD} respectively:
+\bprog
+? t = Mod(2,17); t^8
+%1 = Mod(1, 17)
+? t = Mod(x,x^2+1); t^2
+%2 = Mod(-1, x^2+1)
+ at eprog\noindent If $a \% b$ makes sense and yields a result of the
+appropriate type (\typ{INT} or scalar/\typ{POL}), the operation succeeds as
+well:
+\bprog
+? Mod(1/2, 5)
+%3 = Mod(3, 5)
+? Mod(7 + O(3^6), 3)
+%4 = Mod(1, 3)
+? Mod(Mod(1,12), 9)
+%5 = Mod(1, 3)
+? Mod(1/x, x^2+1)
+%6 = Mod(-1, x^2+1)
+? Mod(exp(x), x^4)
+%7 = Mod(1/6*x^3 + 1/2*x^2 + x + 1, x^4)
+ at eprog
+If $a$ is a complex object, ``base change'' it to $\Z/b\Z$ or $K[x]/(b)$,
+which is equivalent to, but faster than, multiplying it by \kbd{Mod(1,b)}:
+\bprog
+? Mod([1,2;3,4], 2)
+%8 =
+[Mod(1, 2) Mod(0, 2)]
+
+[Mod(1, 2) Mod(0, 2)]
+? Mod(3*x+5, 2)
+%9 = Mod(1, 2)*x + Mod(1, 2)
+? Mod(x^2 + y*x + y^3, y^2+1)
+%10 = Mod(1, y^2 + 1)*x^2 + Mod(y, y^2 + 1)*x + Mod(-y, y^2 + 1)
+ at eprog
+
+This function is not the same as $x$ \kbd{\%} $y$, the result of which
+has no knowledge of the intended modulus $y$. Compare
+\bprog
+? x = 4 % 5; x + 1
+%1 = 5
+? x = Mod(4,5); x + 1
+%2 = Mod(0,5)
+ at eprog
+
+The library syntax is \fun{GEN}{gmodulo}{GEN a, GEN b}.
+
+\subsec{Pol$(t,\{v='x\})$}\kbdsidx{Pol}\label{se:Pol}
+Transforms the object $t$ into a polynomial with main variable $v$. If $t$
+is a scalar, this gives a constant polynomial. If $t$ is a power series with
+non-negative valuation or a rational function, the effect is similar to
+\kbd{truncate}, i.e.~we chop off the $O(X^k)$ or compute the Euclidean
+quotient of the numerator by the denominator, then change the main variable
+of the result to $v$.
+
+The main use of this function is when $t$ is a vector: it creates the
+polynomial whose coefficients are given by $t$, with $t[1]$ being the leading
+coefficient (which can be zero). It is much faster to evaluate
+\kbd{Pol} on a vector of coefficients in this way, than the corresponding
+formal expression $a_n X^n + \dots + a_0$, which is evaluated naively exactly
+as written (linear versus quadratic time in $n$). \tet{Polrev} can be used if
+one wants $x[1]$ to be the constant coefficient:
+\bprog
+? Pol([1,2,3])
+%1 = x^2 + 2*x + 3
+? Polrev([1,2,3])
+%2 = 3*x^2 + 2*x + 1
+ at eprog\noindent
+The reciprocal function of \kbd{Pol} (resp.~\kbd{Polrev}) is \kbd{Vec} (resp.~
+\kbd{Vecrev}).
+\bprog
+? Vec(Pol([1,2,3]))
+%1 = [1, 2, 3]
+? Vecrev( Polrev([1,2,3]) )
+%2 = [1, 2, 3]
+ at eprog\noindent
+
+\misctitle{Warning} This is \emph{not} a substitution function. It will not
+transform an object containing variables of higher priority than~$v$.
+\bprog
+? Pol(x + y, y)
+  ***   at top-level: Pol(x+y,y)
+  ***                 ^----------
+  *** Pol: variable must have higher priority in gtopoly.
+ at eprog
+
+The library syntax is \fun{GEN}{gtopoly}{GEN t, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{Polrev$(t,\{v='x\})$}\kbdsidx{Polrev}\label{se:Polrev}
+Transform the object $t$ into a polynomial
+with main variable $v$. If $t$ is a scalar, this gives a constant polynomial.
+If $t$ is a power series, the effect is identical to \kbd{truncate}, i.e.~it
+chops off the $O(X^k)$.
+
+The main use of this function is when $t$ is a vector: it creates the
+polynomial whose coefficients are given by $t$, with $t[1]$ being the
+constant term. \tet{Pol} can be used if one wants $t[1]$ to be the leading
+coefficient:
+\bprog
+? Polrev([1,2,3])
+%1 = 3*x^2 + 2*x + 1
+? Pol([1,2,3])
+%2 = x^2 + 2*x + 3
+ at eprog
+The reciprocal function of \kbd{Pol} (resp.~\kbd{Polrev}) is \kbd{Vec} (resp.~
+\kbd{Vecrev}).
+
+The library syntax is \fun{GEN}{gtopolyrev}{GEN t, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{Qfb$(a,b,c,\{D=0.\})$}\kbdsidx{Qfb}\label{se:Qfb}
+Creates the binary quadratic form\sidx{binary quadratic form}
+$ax^2+bxy+cy^2$. If $b^2-4ac>0$, initialize \idx{Shanks}' distance
+function to $D$. Negative definite forms are not implemented,
+use their positive definite counterpart instead.
+
+The library syntax is \fun{GEN}{Qfb0}{GEN a, GEN b, GEN c, GEN D = NULL, long prec}.
+Also available are
+\fun{GEN}{qfi}{GEN a, GEN b, GEN c} (assumes $b^2-4ac<0$) and
+\fun{GEN}{qfr}{GEN a, GEN b, GEN c, GEN D} (assumes $b^2-4ac>0$).
+
+\subsec{Ser$(s,\{v='x\},\{d=\var{seriesprecision}\})$}\kbdsidx{Ser}\label{se:Ser}
+Transforms the object $s$ into a power series with main variable $v$
+($x$ by default) and precision (number of significant terms) equal to
+$d$ (= the default \kbd{seriesprecision} by default). If $s$ is a
+scalar, this gives a constant power series in $v$ with precision \kbd{d}.
+If $s$ is a polynomial, the polynomial is truncated to $d$ terms if needed
+\bprog
+? Ser(1, 'y, 5)
+%1 = 1 + O(y^5)
+? Ser(x^2,, 5)
+%2 = x^2 + O(x^7)
+? T = polcyclo(100)
+%3 = x^40 - x^30 + x^20 - x^10 + 1
+? Ser(T, 'x, 11)
+%4 = 1 - x^10 + O(x^11)
+ at eprog\noindent The function is more or less equivalent with multiplication by
+$1 + O(v^d)$ in theses cases, only faster.
+
+If $s$ is a vector, on the other hand, the coefficients of the vector are
+understood to be the coefficients of the power series starting from the
+constant term (as in \tet{Polrev}$(x)$), and the precision $d$ is ignored:
+in other words, in this case, we convert \typ{VEC} / \typ{COL} to the power
+series whose significant terms are exactly given by the vector entries.
+Finally, if $s$ is already a power series in $v$, we return it verbatim,
+ignoring $d$ again. If $d$ significant terms are desired in the last two
+cases, convert/truncate to \typ{POL} first.
+\bprog
+? v = [1,2,3]; Ser(v, t, 7)
+%5 = 1 + 2*t + 3*t^2 + O(t^3)  \\ 3 terms: 7 is ignored!
+? Ser(Polrev(v,t), t, 7)
+%6 = 1 + 2*t + 3*t^2 + O(t^7)
+? s = 1+x+O(x^2); Ser(s, x, 7)
+%7 = 1 + x + O(x^2)  \\ 2 terms: 7 ignored
+? Ser(truncate(s), x, 7)
+%8 = 1 + x + O(x^7)
+ at eprog\noindent
+The warning given for \kbd{Pol} also applies here: this is not a substitution
+function.
+
+The library syntax is \fun{GEN}{gtoser}{GEN s, long v = -1, long precdl}, where \kbd{v} is a variable number.
+
+\subsec{Set$(\{x=[\,]\})$}\kbdsidx{Set}\label{se:Set}
+Converts $x$ into a set, i.e.~into a row vector, with strictly increasing
+entries with respect to the (somewhat arbitrary) universal comparison function
+\tet{cmp}. Standard container types \typ{VEC}, \typ{COL}, \typ{LIST} and
+\typ{VECSMALL} are converted to the set with corresponding elements. All
+others are converted to a set with one element.
+\bprog
+? Set([1,2,4,2,1,3])
+%1 = [1, 2, 3, 4]
+? Set(x)
+%2 = [x]
+? Set(Vecsmall([1,3,2,1,3]))
+%3 = [1, 2, 3]
+ at eprog
+
+The library syntax is \fun{GEN}{gtoset}{GEN x = NULL}.
+
+\subsec{Str$(\{x\}*)$}\kbdsidx{Str}\label{se:Str}
+Converts its argument list into a
+single character string (type \typ{STR}, the empty string if $x$ is omitted).
+To recover an ordinary \kbd{GEN} from a string, apply \kbd{eval} to it. The
+arguments of \kbd{Str} are evaluated in string context, see \secref{se:strings}.
+
+\bprog
+? x2 = 0; i = 2; Str(x, i)
+%1 = "x2"
+? eval(%)
+%2 = 0
+ at eprog\noindent
+This function is mostly useless in library mode. Use the pair
+\tet{strtoGEN}/\tet{GENtostr} to convert between \kbd{GEN} and \kbd{char*}.
+The latter returns a malloced string, which should be freed after usage.
+%\syn{NO}
+
+\subsec{Strchr$(x)$}\kbdsidx{Strchr}\label{se:Strchr}
+Converts $x$ to a string, translating each integer
+into a character.
+\bprog
+? Strchr(97)
+%1 = "a"
+? Vecsmall("hello world")
+%2 = Vecsmall([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])
+? Strchr(%)
+%3 = "hello world"
+ at eprog
+
+The library syntax is \fun{GEN}{Strchr}{GEN x}.
+
+\subsec{Strexpand$(\{x\}*)$}\kbdsidx{Strexpand}\label{se:Strexpand}
+Converts its argument list into a
+single character string (type \typ{STR}, the empty string if $x$ is omitted).
+Then perform \idx{environment expansion}, see \secref{se:envir}.
+This feature can be used to read \idx{environment variable} values.
+\bprog
+? Strexpand("$HOME/doc")
+%1 = "/home/pari/doc"
+ at eprog
+
+The individual arguments are read in string context, see \secref{se:strings}.
+%\syn{NO}
+
+\subsec{Strtex$(\{x\}*)$}\kbdsidx{Strtex}\label{se:Strtex}
+Translates its arguments to TeX
+format, and concatenates the results into a single character string (type
+\typ{STR}, the empty string if $x$ is omitted).
+
+The individual arguments are read in string context, see \secref{se:strings}.
+%\syn{NO}
+
+\subsec{Vec$(x, \{n\})$}\kbdsidx{Vec}\label{se:Vec}
+Transforms the object $x$ into a row vector. The dimension of the
+resulting vector can be optionally specified via the extra parameter $n$.
+
+If $n$ is omitted or $0$, the dimension depends on the type of $x$; the
+vector has a single component, except when $x$ is
+
+\item a vector or a quadratic form (in which case the resulting vector
+is simply the initial object considered as a row vector),
+
+\item a polynomial or a power series. In the case of a polynomial, the
+coefficients of the vector start with the leading coefficient of the
+polynomial, while for power series only the significant coefficients are
+taken into account, but this time by increasing order of degree.
+In this last case, \kbd{Vec} is the reciprocal function of \kbd{Pol} and
+\kbd{Ser} respectively,
+
+\item a matrix: return the vector of columns comprising the matrix.
+
+\item a character string: return the vector of individual characters.
+
+\item an error context (\typ{ERROR}): return the error components, see
+\tet{iferr}.
+
+In the last three cases (matrix, character string, error), $n$ is
+meaningless and must be omitted or an error is raised. Otherwise, if $n$ is
+given, $0$ entries are appended at the end of the vector if $n > 0$, and
+prepended at the beginning if $n < 0$. The dimension of the resulting vector
+is $|n|$. Variant: \fun{GEN}{gtovec}{GEN x} is also available.
+
+The library syntax is \fun{GEN}{gtovec0}{GEN x, long n}.
+
+\subsec{Vecrev$(x, \{n\})$}\kbdsidx{Vecrev}\label{se:Vecrev}
+As $\kbd{Vec}(x, n)$, then reverse the result. In particular
+In this case, \kbd{Vecrev} is the reciprocal function of \kbd{Polrev}: the
+coefficients of the vector start with the constant coefficient of the
+polynomial and the others follow by increasing degree.
+
+The library syntax is \fun{GEN}{gtovecrev0}{GEN x, long n}.
+\fun{GEN}{gtovecrev}{GEN x} is also available.
+
+\subsec{Vecsmall$(x, \{n\})$}\kbdsidx{Vecsmall}\label{se:Vecsmall}
+Transforms the object $x$ into a row vector of type \typ{VECSMALL}. The
+dimension of the resulting vector can be optionally specified via the extra
+parameter $n$.
+
+This acts as \kbd{Vec}$(x,n)$, but only on a limited set of objects:
+the result must be representable as a vector of small integers.
+If $x$ is a character string, a vector of individual characters in ASCII
+encoding is returned (\tet{Strchr} yields back the character string).
+
+The library syntax is \fun{GEN}{gtovecsmall0}{GEN x, long n}.
+\fun{GEN}{gtovecsmall}{GEN x} is also available.
+
+\subsec{binary$(x)$}\kbdsidx{binary}\label{se:binary}
+Outputs the vector of the binary digits of $|x|$.
+Here $x$ can be an integer, a real number (in which case the result has two
+components, one for the integer part, one for the fractional part) or a
+vector/matrix.
+
+The library syntax is \fun{GEN}{binaire}{GEN x}.
+
+\subsec{bitand$(x,y)$}\kbdsidx{bitand}\label{se:bitand}
+Bitwise \tet{and}
+\sidx{bitwise and}of two integers $x$ and $y$, that is the integer
+$$\sum_i (x_i~\kbd{and}~y_i) 2^i$$
+
+Negative numbers behave $2$-adically, i.e.~the result is the $2$-adic limit
+of \kbd{bitand}$(x_n,y_n)$, where $x_n$ and $y_n$ are non-negative integers
+tending to $x$ and $y$ respectively. (The result is an ordinary integer,
+possibly negative.)
+
+\bprog
+? bitand(5, 3)
+%1 = 1
+? bitand(-5, 3)
+%2 = 3
+? bitand(-5, -3)
+%3 = -7
+ at eprog
+
+The library syntax is \fun{GEN}{gbitand}{GEN x, GEN y}.
+Also available is
+\fun{GEN}{ibitand}{GEN x, GEN y}, which returns the bitwise \emph{and}
+of $|x|$ and $|y|$, two integers.
+
+\subsec{bitneg$(x,\{n=-1\})$}\kbdsidx{bitneg}\label{se:bitneg}
+\idx{bitwise negation} of an integer $x$,
+truncated to $n$ bits, $n\geq 0$, that is the integer
+$$\sum_{i=0}^{n-1} \kbd{not}(x_i) 2^i.$$
+The special case $n=-1$ means no truncation: an infinite sequence of
+leading $1$ is then represented as a negative number.
+
+See \secref{se:bitand} for the behavior for negative arguments.
+
+The library syntax is \fun{GEN}{gbitneg}{GEN x, long n}.
+
+\subsec{bitnegimply$(x,y)$}\kbdsidx{bitnegimply}\label{se:bitnegimply}
+Bitwise negated imply of two integers $x$ and
+$y$ (or \kbd{not} $(x \Rightarrow y)$), that is the integer $$\sum
+(x_i~\kbd{and not}(y_i)) 2^i$$
+
+See \secref{se:bitand} for the behavior for negative arguments.
+
+The library syntax is \fun{GEN}{gbitnegimply}{GEN x, GEN y}.
+Also available is
+\fun{GEN}{ibitnegimply}{GEN x, GEN y}, which returns the bitwise negated
+imply of $|x|$ and $|y|$, two integers.
+
+\subsec{bitor$(x,y)$}\kbdsidx{bitor}\label{se:bitor}
+\sidx{bitwise inclusive or}bitwise (inclusive)
+\tet{or} of two integers $x$ and $y$, that is the integer $$\sum
+(x_i~\kbd{or}~y_i) 2^i$$
+
+See \secref{se:bitand} for the behavior for negative arguments.
+
+The library syntax is \fun{GEN}{gbitor}{GEN x, GEN y}.
+Also available is
+\fun{GEN}{ibitor}{GEN x, GEN y}, which returns the bitwise \emph{ir}
+of $|x|$ and $|y|$, two integers.
+
+\subsec{bittest$(x,n)$}\kbdsidx{bittest}\label{se:bittest}
+Outputs the $n^{\text{th}}$ bit of $x$ starting
+from the right (i.e.~the coefficient of $2^n$ in the binary expansion of $x$).
+The result is 0 or 1.
+\bprog
+? bittest(7, 3)
+%1 = 1 \\ the 3rd bit is 1
+? bittest(7, 4)
+%2 = 0 \\ the 4th bit is 0
+ at eprog\noindent
+See \secref{se:bitand} for the behavior at negative arguments.
+
+The library syntax is \fun{GEN}{gbittest}{GEN x, long n}.
+For a \typ{INT} $x$, the variant \fun{long}{bittest}{GEN x, long n} is
+generally easier to use, and if furthermore $n\ge 0$ the low-level function
+\fun{ulong}{int_bit}{GEN x, long n} returns \kbd{bittest(abs(x),n)}.
+
+\subsec{bitxor$(x,y)$}\kbdsidx{bitxor}\label{se:bitxor}
+Bitwise (exclusive) \tet{or}
+\sidx{bitwise exclusive or}of two integers $x$ and $y$, that is the integer
+$$\sum (x_i~\kbd{xor}~y_i) 2^i$$
+
+See \secref{se:bitand} for the behavior for negative arguments.
+
+The library syntax is \fun{GEN}{gbitxor}{GEN x, GEN y}.
+Also available is
+\fun{GEN}{ibitxor}{GEN x, GEN y}, which returns the bitwise \emph{xor}
+of $|x|$ and $|y|$, two integers.
+
+\subsec{ceil$(x)$}\kbdsidx{ceil}\label{se:ceil}
+Ceiling of $x$. When $x$ is in $\R$, the result is the
+smallest integer greater than or equal to $x$. Applied to a rational
+function, $\kbd{ceil}(x)$ returns the Euclidean quotient of the numerator by
+the denominator.
+
+The library syntax is \fun{GEN}{gceil}{GEN x}.
+
+\subsec{centerlift$(x,\{v\})$}\kbdsidx{centerlift}\label{se:centerlift}
+Same as \tet{lift}, except that \typ{INTMOD} and \typ{PADIC} components
+are lifted using centered residues:
+
+\item for a \typ{INTMOD} $x\in \Z/n\Z$, the lift $y$ is such that
+$-n/2<y\le n/2$.
+
+\item  a \typ{PADIC} $x$ is lifted in the same way as above (modulo
+$p^\kbd{padicprec(x)}$) if its valuation $v$ is non-negative; if not, returns
+the fraction $p^v$ \kbd{centerlift}$(x p^{-v})$; in particular, rational
+reconstruction is not attempted. Use \tet{bestappr} for this.
+
+For backward compatibility, \kbd{centerlift(x,'v)} is allowed as an alias
+for \kbd{lift(x,'v)}.
+
+\synt{centerlift}{GEN x}.
+
+\subsec{characteristic$(x)$}\kbdsidx{characteristic}\label{se:characteristic}
+Returns the characteristic of the base ring over which $x$ is defined (as
+defined by \typ{INTMOD} and \typ{FFELT} components). The function raises an
+exception if incompatible primes arise from \typ{FFELT} and \typ{PADIC}
+components.
+\bprog
+? characteristic(Mod(1,24)*x + Mod(1,18)*y)
+%1 = 6
+ at eprog
+
+The library syntax is \fun{GEN}{characteristic}{GEN x}.
+
+\subsec{component$(x,n)$}\kbdsidx{component}\label{se:component}
+Extracts the $n^{\text{th}}$-component of $x$. This is to be understood
+as follows: every PARI type has one or two initial \idx{code words}. The
+components are counted, starting at 1, after these code words. In particular
+if $x$ is a vector, this is indeed the $n^{\text{th}}$-component of $x$, if
+$x$ is a matrix, the $n^{\text{th}}$ column, if $x$ is a polynomial, the
+$n^{\text{th}}$ coefficient (i.e.~of degree $n-1$), and for power series,
+the $n^{\text{th}}$ significant coefficient.
+
+For polynomials and power series, one should rather use \tet{polcoeff}, and
+for vectors and matrices, the \kbd{[$\,$]} operator. Namely, if $x$ is a
+vector, then \tet{x[n]} represents the $n^{\text{th}}$ component of $x$. If
+$x$ is a matrix, \tet{x[m,n]} represents the coefficient of row \kbd{m} and
+column \kbd{n} of the matrix, \tet{x[m,]} represents the $m^{\text{th}}$
+\emph{row} of $x$, and \tet{x[,n]} represents the $n^{\text{th}}$
+\emph{column} of $x$.
+
+Using of this function requires detailed knowledge of the structure of the
+different PARI types, and thus it should almost never be used directly.
+Some useful exceptions:
+\bprog
+    ? x = 3 + O(3^5);
+    ? component(x, 2)
+    %2 = 81   \\ p^(p-adic accuracy)
+    ? component(x, 1)
+    %3 = 3    \\ p
+    ? q = Qfb(1,2,3);
+    ? component(q, 1)
+    %5 = 1
+ at eprog
+
+The library syntax is \fun{GEN}{compo}{GEN x, long n}.
+
+\subsec{conj$(x)$}\kbdsidx{conj}\label{se:conj}
+Conjugate of $x$. The meaning of this
+is clear, except that for real quadratic numbers, it means conjugation in the
+real quadratic field. This function has no effect on integers, reals,
+intmods, fractions or $p$-adics. The only forbidden type is polmod
+(see \kbd{conjvec} for this).
+
+The library syntax is \fun{GEN}{gconj}{GEN x}.
+
+\subsec{conjvec$(z)$}\kbdsidx{conjvec}\label{se:conjvec}
+Conjugate vector representation of $z$. If $z$ is a
+polmod, equal to \kbd{Mod}$(a,T)$, this gives a vector of length
+$\text{degree}(T)$ containing:
+
+\item the complex embeddings of $z$ if $T$ has rational coefficients,
+i.e.~the $a(r[i])$ where $r = \kbd{polroots}(T)$;
+
+\item the conjugates of $z$ if $T$ has some intmod coefficients;
+
+\noindent if $z$ is a finite field element, the result is the vector of
+conjugates $[z,z^p,z^{p^2},\ldots,z^{p^{n-1}}]$ where $n=\text{degree}(T)$.
+
+\noindent If $z$ is an integer or a rational number, the result is~$z$. If
+$z$ is a (row or column) vector, the result is a matrix whose columns are
+the conjugate vectors of the individual elements of $z$.
+
+The library syntax is \fun{GEN}{conjvec}{GEN z, long prec}.
+
+\subsec{denominator$(x)$}\kbdsidx{denominator}\label{se:denominator}
+Denominator of $x$. The meaning of this
+is clear when $x$ is a rational number or function. If $x$ is an integer
+or a polynomial, it is treated as a rational number or function,
+respectively, and the result is equal to $1$. For polynomials, you
+probably want to use
+\bprog
+denominator( content(x) )
+ at eprog\noindent
+instead. As for modular objects, \typ{INTMOD} and \typ{PADIC} have
+denominator $1$, and the denominator of a \typ{POLMOD} is the denominator
+of its (minimal degree) polynomial representative.
+
+If $x$ is a recursive structure, for instance a vector or matrix, the lcm
+of the denominators of its components (a common denominator) is computed.
+This also applies for \typ{COMPLEX}s and \typ{QUAD}s.
+
+\misctitle{Warning} Multivariate objects are created according to variable
+priorities, with possibly surprising side effects ($x/y$ is a polynomial, but
+$y/x$ is a rational function). See \secref{se:priority}.
+
+The library syntax is \fun{GEN}{denom}{GEN x}.
+
+\subsec{digits$(x,\{b={10}\})$}\kbdsidx{digits}\label{se:digits}
+Outputs the vector of the digits of $|x|$ in base $b$, where $x$ and $b$ are integers.
+
+The library syntax is \fun{GEN}{digits}{GEN x, GEN b = NULL}.
+
+\subsec{floor$(x)$}\kbdsidx{floor}\label{se:floor}
+Floor of $x$. When $x$ is in $\R$, the result is the
+largest integer smaller than or equal to $x$. Applied to a rational function,
+$\kbd{floor}(x)$ returns the Euclidean quotient of the numerator by the
+denominator.
+
+The library syntax is \fun{GEN}{gfloor}{GEN x}.
+
+\subsec{frac$(x)$}\kbdsidx{frac}\label{se:frac}
+Fractional part of $x$. Identical to
+$x-\text{floor}(x)$. If $x$ is real, the result is in $[0,1[$.
+
+The library syntax is \fun{GEN}{gfrac}{GEN x}.
+
+\subsec{hammingweight$(x)$}\kbdsidx{hammingweight}\label{se:hammingweight}
+If $x$ is a \typ{INT}, return the binary Hamming weight of $|x|$. Otherwise
+$x$ must be of type \typ{POL}, \typ{VEC}, \typ{COL}, \typ{VECSMALL}, or
+\typ{MAT} and the function returns the number of non-zero coefficients of
+$x$.
+\bprog
+? hammingweight(15)
+%1 = 4
+? hammingweight(x^100 + 2*x + 1)
+%2 = 3
+? hammingweight([Mod(1,2), 2, Mod(0,3)])
+%3 = 2
+? hammingweight(matid(100))
+%4 = 100
+ at eprog
+
+The library syntax is \fun{long}{hammingweight}{GEN x}.
+
+\subsec{imag$(x)$}\kbdsidx{imag}\label{se:imag}
+Imaginary part of $x$. When $x$ is a quadratic number, this is the
+coefficient of $\omega$ in the ``canonical'' integral basis $(1,\omega)$.
+
+The library syntax is \fun{GEN}{gimag}{GEN x}.
+
+\subsec{length$(x)$}\kbdsidx{length}\label{se:length}
+Length of $x$; \kbd{\#}$x$ is a shortcut for \kbd{length}$(x)$.
+This is mostly useful for
+
+\item vectors: dimension (0 for empty vectors),
+
+\item lists: number of entries (0 for empty lists),
+
+\item matrices: number of columns,
+
+\item character strings: number of actual characters (without
+trailing \kbd{\bs 0}, should you expect it from $C$ \kbd{char*}).
+\bprog
+ ? #"a string"
+ %1 = 8
+ ? #[3,2,1]
+ %2 = 3
+ ? #[]
+ %3 = 0
+ ? #matrix(2,5)
+ %4 = 5
+ ? L = List([1,2,3,4]); #L
+ %5 = 4
+ at eprog
+
+The routine is in fact defined for arbitrary GP types, but is awkward and
+useless in other cases: it returns the number of non-code words in $x$, e.g.
+the effective length minus 2 for integers since the \typ{INT} type has two code
+words.
+
+The library syntax is \fun{long}{glength}{GEN x}.
+
+\subsec{lift$(x,\{v\})$}\kbdsidx{lift}\label{se:lift}
+If $v$ is omitted, lifts intmods from $\Z/n\Z$ in $\Z$,
+$p$-adics from $\Q_p$ to $\Q$ (as \tet{truncate}), and polmods to
+polynomials. Otherwise, lifts only polmods whose modulus has main
+variable~$v$. \typ{FFELT} are not lifted, nor are List elements: you may
+convert the latter to vectors first, or use \kbd{apply(lift,L)}. More
+generally, components for which such lifts are meaningless (e.g. character
+strings) are copied verbatim.
+\bprog
+? lift(Mod(5,3))
+%1 = 2
+? lift(3 + O(3^9))
+%2 = 3
+? lift(Mod(x,x^2+1))
+%3 = x
+? lift(Mod(x,x^2+1))
+%4 = x
+ at eprog
+Lifts are performed recursively on an object components, but only
+by \emph{one level}: once a \typ{POLMOD} is lifted, the components of
+the result are \emph{not} lifted further.
+\bprog
+? lift(x * Mod(1,3) + Mod(2,3))
+%4 = x + 2
+? lift(x * Mod(y,y^2+1) + Mod(2,3))
+%5 = y*x + Mod(2, 3)   \\@com do you understand this one?
+? lift(x * Mod(y,y^2+1) + Mod(2,3), 'x)
+%6 = Mod(y, y^2 + 1)*x + Mod(Mod(2, 3), y^2 + 1)
+? lift(%, y)
+%7 = y*x + Mod(2, 3)
+ at eprog\noindent To recursively lift all components not only by one level,
+but as long as possible, use \kbd{liftall}. To lift only \typ{INTMOD}s and
+\typ{PADIC}s components, use \tet{liftint}. To lift only \typ{POLMOD}s
+components, use \tet{liftpol}. Finally, \tet{centerlift} allows to lift
+\typ{INTMOD}s and \typ{PADIC}s using centered residues (lift of smallest
+absolute value).
+
+The library syntax is \fun{GEN}{lift0}{GEN x, long v = -1}, where \kbd{v} is a variable number.
+Also available is \fun{GEN}{lift}{GEN x} corresponding to
+\kbd{lift0(x,-1)}.
+
+\subsec{liftall$(x)$}\kbdsidx{liftall}\label{se:liftall}
+Recursively lift all components of $x$ from $\Z/n\Z$ to $\Z$,
+from $\Q_p$ to $\Q$ (as \tet{truncate}), and polmods to
+polynomials. \typ{FFELT} are not lifted, nor are List elements: you may
+convert the latter to vectors first, or use \kbd{apply(liftall,L)}. More
+generally, components for which such lifts are meaningless (e.g. character
+strings) are copied verbatim.
+\bprog
+? liftall(x * (1 + O(3)) + Mod(2,3))
+%1 = x + 2
+? liftall(x * Mod(y,y^2+1) + Mod(2,3)*Mod(z,z^2))
+%2 = y*x + 2*z
+ at eprog
+
+The library syntax is \fun{GEN}{liftall}{GEN x}.
+
+\subsec{liftint$(x)$}\kbdsidx{liftint}\label{se:liftint}
+Recursively lift all components of $x$ from $\Z/n\Z$ to $\Z$ and
+from $\Q_p$ to $\Q$ (as \tet{truncate}).
+\typ{FFELT} are not lifted, nor are List elements: you may
+convert the latter to vectors first, or use \kbd{apply(liftint,L)}. More
+generally, components for which such lifts are meaningless (e.g. character
+strings) are copied verbatim.
+\bprog
+? liftint(x * (1 + O(3)) + Mod(2,3))
+%1 = x + 2
+? liftint(x * Mod(y,y^2+1) + Mod(2,3)*Mod(z,z^2))
+%2 = Mod(y, y^2 + 1)*x + Mod(Mod(2*z, z^2), y^2 + 1)
+ at eprog
+
+The library syntax is \fun{GEN}{liftint}{GEN x}.
+
+\subsec{liftpol$(x)$}\kbdsidx{liftpol}\label{se:liftpol}
+Recursively lift all components of $x$ which are polmods to
+polynomials. \typ{FFELT} are not lifted, nor are List elements: you may
+convert the latter to vectors first, or use \kbd{apply(liftpol,L)}. More
+generally, components for which such lifts are meaningless (e.g. character
+strings) are copied verbatim.
+\bprog
+? liftpol(x * (1 + O(3)) + Mod(2,3))
+%1 = (1 + O(3))*x + Mod(2, 3)
+? liftpol(x * Mod(y,y^2+1) + Mod(2,3)*Mod(z,z^2))
+%2 = y*x + Mod(2, 3)*z
+ at eprog
+
+The library syntax is \fun{GEN}{liftpol}{GEN x}.
+
+\subsec{norm$(x)$}\kbdsidx{norm}\label{se:norm}
+Algebraic norm of $x$, i.e.~the product of $x$ with
+its conjugate (no square roots are taken), or conjugates for polmods. For
+vectors and matrices, the norm is taken componentwise and hence is not the
+$L^2$-norm (see \kbd{norml2}). Note that the norm of an element of
+$\R$ is its square, so as to be compatible with the complex norm.
+
+The library syntax is \fun{GEN}{gnorm}{GEN x}.
+
+\subsec{numerator$(x)$}\kbdsidx{numerator}\label{se:numerator}
+Numerator of $x$. The meaning of this
+is clear when $x$ is a rational number or function. If $x$ is an integer
+or a polynomial, it is treated as a rational number or function,
+respectively, and the result is $x$ itself. For polynomials, you
+probably want to use
+\bprog
+numerator( content(x) )
+ at eprog\noindent
+instead.
+
+In other cases, \kbd{numerator(x)} is defined to be
+\kbd{denominator(x)*x}. This is the case when $x$ is a vector or a
+matrix, but also for \typ{COMPLEX} or \typ{QUAD}. In particular since a
+\typ{PADIC} or \typ{INTMOD} has  denominator $1$, its numerator is
+itself.
+
+\misctitle{Warning} Multivariate objects are created according to variable
+priorities, with possibly surprising side effects ($x/y$ is a polynomial, but
+$y/x$ is a rational function). See \secref{se:priority}.
+
+The library syntax is \fun{GEN}{numer}{GEN x}.
+
+\subsec{numtoperm$(n,k)$}\kbdsidx{numtoperm}\label{se:numtoperm}
+Generates the $k$-th permutation (as a row vector of length $n$) of the
+numbers $1$ to $n$. The number $k$ is taken modulo $n!\,$, i.e.~inverse
+function of \tet{permtonum}. The numbering used is the standard lexicographic
+ordering, starting at $0$.
+
+The library syntax is \fun{GEN}{numtoperm}{long n, GEN k}.
+
+\subsec{padicprec$(x,p)$}\kbdsidx{padicprec}\label{se:padicprec}
+Absolute $p$-adic precision of the object $x$. This is the minimum
+precision of the components of $x$. The result is \tet{LONG_MAX}
+($2^{31}-1$ for 32-bit machines or $2^{63}-1$ for 64-bit machines) if $x$ is
+an exact object.
+
+The library syntax is \fun{long}{padicprec}{GEN x, GEN p}.
+
+\subsec{permtonum$(x)$}\kbdsidx{permtonum}\label{se:permtonum}
+Given a permutation $x$ on $n$ elements, gives the number $k$ such that
+$x=\kbd{numtoperm(n,k)}$, i.e.~inverse function of \tet{numtoperm}.
+The numbering used is the standard lexicographic ordering, starting at $0$.
+
+The library syntax is \fun{GEN}{permtonum}{GEN x}.
+
+\subsec{precision$(x,\{n\})$}\kbdsidx{precision}\label{se:precision}
+The function has two different behaviors according to whether $n$ is present or not.
+
+If $n$ is missing, the function returns the precision in decimal digits of the
+PARI object $x$. If $x$ is
+an exact object, the largest single precision integer is returned.
+\bprog
+? precision(exp(1e-100))
+%1 = 134                \\ 134 significant decimal digits
+? precision(2 + x)
+%2 = 2147483647         \\ exact object
+? precision(0.5 + O(x))
+%3 = 28                 \\ floating point accuracy, NOT series precision
+? precision( [ exp(1e-100), 0.5 ] )
+%4 = 28                 \\ minimal accuracy among components
+ at eprog\noindent
+The return value for exact objects is meaningless since it is not even the
+same on 32 and 64-bit machines. The proper way to test whether an object is
+exact is
+\bprog
+? isexact(x) = precision(x) == precision(0)
+ at eprog
+
+If $n$ is present, the function creates a new object equal to $x$ with a new
+``precision'' $n$. (This never changes the type of the result. In particular
+it is not possible to use it to obtain a polynomial from a power series; for
+that, see \tet{truncate}.) Now the meaning of precision is different from the
+above (floating point accuracy), and depends on the type of $x$:
+
+For exact types, no change. For $x$ a vector or a matrix, the operation is
+done componentwise.
+
+For real $x$, $n$ is the number of desired significant \emph{decimal}
+digits. If $n$ is smaller than the precision of $x$, $x$ is truncated,
+otherwise $x$ is extended with zeros.
+
+For $x$ a $p$-adic or a power series, $n$ is the desired number of
+\emph{significant} $p$-adic or $X$-adic digits, where $X$ is the main
+variable of $x$. (Note: yes, this is inconsistent.)
+Note that the precision is a priori distinct from the exponent $k$ appearing
+in $O(*^k)$; it is indeed equal to $k$ if and only if $x$ is a $p$-adic
+or $X$-adic \emph{unit}.
+\bprog
+? precision(1 + O(x), 10)
+%1 = 1 + O(x^10)
+? precision(x^2 + O(x^10), 3)
+%2 = x^2 + O(x^5)
+? precision(7^2 + O(7^10), 3)
+%3 = 7^2 + O(7^5)
+ at eprog\noindent
+For the last two examples, note that $x^2 + O(x^5) = x^2(1 + O(x^3))$
+indeed has 3 significant coefficients
+
+The library syntax is \fun{GEN}{precision0}{GEN x, long n}.
+Also available are \fun{GEN}{gprec}{GEN x, long n} and
+\fun{long}{precision}{GEN x}. In both, the accuracy is expressed in
+\emph{words} (32-bit or 64-bit depending on the architecture).
+
+\subsec{random$(\{N=2^{{31}}\})$}\kbdsidx{random}\label{se:random}
+Returns a random element in various natural sets depending on the
+argument $N$.
+
+\item \typ{INT}: returns an integer
+uniformly distributed between $0$ and $N-1$. Omitting the argument
+is equivalent to \kbd{random(2\pow31)}.
+
+\item \typ{REAL}: returns a real number in $[0,1[$ with the same accuracy as
+$N$ (whose mantissa has the same number of significant words).
+
+\item \typ{INTMOD}: returns a random intmod for the same modulus.
+
+\item \typ{FFELT}: returns a random element in the same finite field.
+
+\item \typ{VEC} of length $2$, $N = [a,b]$: returns an integer uniformly
+distributed between $a$ and $b$.
+
+\item \typ{VEC} generated by \kbd{ellinit} over a finite field $k$
+(coefficients are \typ{INTMOD}s modulo a prime or \typ{FFELT}s): returns a
+``random'' $k$-rational \emph{affine} point on the curve. More precisely
+if the curve has a single point (at infinity!) we return it; otherwise
+we return an affine point by drawing an abscissa uniformly at
+random until \tet{ellordinate} succeeds. Note that this is definitely not a
+uniform distribution over $E(k)$, but it should be good enough for
+applications.
+
+\item \typ{POL} return a random polynomial of degree at most the degree of $N$.
+The coefficients are drawn by applying \kbd{random} to the leading
+coefficient of $N$.
+
+\bprog
+? random(10)
+%1 = 9
+? random(Mod(0,7))
+%2 = Mod(1, 7)
+? a = ffgen(ffinit(3,7), 'a); random(a)
+%3 = a^6 + 2*a^5 + a^4 + a^3 + a^2 + 2*a
+? E = ellinit([3,7]*Mod(1,109)); random(E)
+%4 = [Mod(103, 109), Mod(10, 109)]
+? E = ellinit([1,7]*a^0); random(E)
+%5 = [a^6 + a^5 + 2*a^4 + 2*a^2, 2*a^6 + 2*a^4 + 2*a^3 + a^2 + 2*a]
+? random(Mod(1,7)*x^4)
+%6 = Mod(5, 7)*x^4 + Mod(6, 7)*x^3 + Mod(2, 7)*x^2 + Mod(2, 7)*x + Mod(5, 7)
+
+ at eprog
+These variants all depend on a single internal generator, and are
+independent from your operating system's random number generators.
+A random seed may be obtained via \tet{getrand}, and reset
+using \tet{setrand}: from a given seed, and given sequence of \kbd{random}s,
+the exact same values will be generated. The same seed is used at each
+startup, reseed the generator yourself if this is a problem. Note that
+internal functions also call the random number generator; adding such a
+function call in the middle of your code will change the numbers produced.
+
+\misctitle{Technical note}
+Up to
+version 2.4 included, the internal generator produced pseudo-random numbers
+by means of linear congruences, which were not well distributed in arithmetic
+progressions. We now
+use Brent's XORGEN algorithm, based on Feedback Shift Registers, see
+\kbd{http://wwwmaths.anu.edu.au/\til{}brent/random.html}. The generator has period
+$2^{4096}-1$, passes the Crush battery of statistical tests of L'Ecuyer and
+Simard, but is not suitable for cryptographic purposes: one can reconstruct
+the state vector from a small sample of consecutive values, thus predicting
+the entire sequence.
+
+The library syntax is \fun{GEN}{genrand}{GEN N = NULL}.
+
+ Also available: \fun{GEN}{ellrandom}{GEN E} and \fun{GEN}{ffrandom}{GEN a}.
+
+\subsec{real$(x)$}\kbdsidx{real}\label{se:real}
+Real part of $x$. In the case where $x$ is a quadratic number, this is the
+coefficient of $1$ in the ``canonical'' integral basis $(1,\omega)$.
+
+The library syntax is \fun{GEN}{greal}{GEN x}.
+
+\subsec{round$(x,\{\&e\})$}\kbdsidx{round}\label{se:round}
+If $x$ is in $\R$, rounds $x$ to the nearest integer (rounding to
+$+\infty$ in case of ties), then and sets $e$ to the number of error bits,
+that is the binary exponent of the difference between the original and the
+rounded value (the ``fractional part''). If the exponent of $x$ is too large
+compared to its precision (i.e.~$e>0$), the result is undefined and an error
+occurs if $e$ was not given.
+
+\misctitle{Important remark} Contrary to the other truncation functions,
+this function operates on every coefficient at every level of a PARI object.
+For example
+$$\text{truncate}\left(\dfrac{2.4*X^2-1.7}{X}\right)=2.4*X,$$
+whereas
+$$\text{round}\left(\dfrac{2.4*X^2-1.7}{X}\right)=\dfrac{2*X^2-2}{X}.$$
+An important use of \kbd{round} is to get exact results after an approximate
+computation, when theory tells you that the coefficients must be integers.
+
+The library syntax is \fun{GEN}{round0}{GEN x, GEN *e = NULL}.
+Also available are \fun{GEN}{grndtoi}{GEN x, long *e} and
+\fun{GEN}{ground}{GEN x}.
+
+\subsec{simplify$(x)$}\kbdsidx{simplify}\label{se:simplify}
+This function simplifies $x$ as much as it can. Specifically, a complex or
+quadratic number whose imaginary part is the integer 0 (i.e.~not \kbd{Mod(0,2)}
+or \kbd{0.E-28}) is converted to its real part, and a polynomial of degree $0$
+is converted to its constant term. Simplifications occur recursively.
+
+This function is especially useful before using arithmetic functions,
+which expect integer arguments:
+\bprog
+? x = 2 + y - y
+%1 = 2
+? isprime(x)
+  ***   at top-level: isprime(x)
+  ***                 ^----------
+  *** isprime: not an integer argument in an arithmetic function
+? type(x)
+%2 = "t_POL"
+? type(simplify(x))
+%3 = "t_INT"
+ at eprog
+Note that GP results are simplified as above before they are stored in the
+history. (Unless you disable automatic simplification with \b{y}, that is.)
+In particular
+\bprog
+? type(%1)
+%4 = "t_INT"
+ at eprog
+
+The library syntax is \fun{GEN}{simplify}{GEN x}.
+
+\subsec{sizebyte$(x)$}\kbdsidx{sizebyte}\label{se:sizebyte}
+Outputs the total number of bytes occupied by the tree representing the
+PARI object $x$.
+
+The library syntax is \fun{long}{gsizebyte}{GEN x}.
+Also available is \fun{long}{gsizeword}{GEN x} returning a
+number of \emph{words}.
+
+\subsec{sizedigit$(x)$}\kbdsidx{sizedigit}\label{se:sizedigit}
+Outputs a quick bound for the number of decimal
+digits of (the components of) $x$, off by at most $1$. If you want the
+exact value, you can use \kbd{\#Str(x)}, which is slower.
+
+The library syntax is \fun{long}{sizedigit}{GEN x}.
+
+\subsec{truncate$(x,\{\&e\})$}\kbdsidx{truncate}\label{se:truncate}
+Truncates $x$ and sets $e$ to the number of
+error bits. When $x$ is in $\R$, this means that the part after the decimal
+point is chopped away, $e$ is the binary exponent of the difference between
+the original and the truncated value (the ``fractional part''). If the
+exponent of $x$ is too large compared to its precision (i.e.~$e>0$), the
+result is undefined and an error occurs if $e$ was not given. The function
+applies componentwise on vector / matrices; $e$ is then the maximal number of
+error bits. If $x$ is a rational function, the result is the ``integer part''
+(Euclidean quotient of numerator by denominator) and $e$ is not set.
+
+Note a very special use of \kbd{truncate}: when applied to a power series, it
+transforms it into a polynomial or a rational function with denominator
+a power of $X$, by chopping away the $O(X^k)$. Similarly, when applied to
+a $p$-adic number, it transforms it into an integer or a rational number
+by chopping away the $O(p^k)$.
+
+The library syntax is \fun{GEN}{trunc0}{GEN x, GEN *e = NULL}.
+The following functions are also available: \fun{GEN}{gtrunc}{GEN x}
+and \fun{GEN}{gcvtoi}{GEN x, long *e}.
+
+\subsec{valuation$(x,p)$}\kbdsidx{valuation}\label{se:valuation}
+Computes the highest
+exponent of $p$ dividing $x$. If $p$ is of type integer, $x$ must be an
+integer, an intmod whose modulus is divisible by $p$, a fraction, a
+$q$-adic number with $q=p$, or a polynomial or power series in which case the
+valuation is the minimum of the valuation of the coefficients.
+
+If $p$ is of type polynomial, $x$ must be of type polynomial or rational
+function, and also a power series if $x$ is a monomial. Finally, the
+valuation of a vector, complex or quadratic number is the minimum of the
+component valuations.
+
+If $x=0$, the result is \tet{LONG_MAX} ($2^{31}-1$ for 32-bit machines or
+$2^{63}-1$ for 64-bit machines) if $x$ is an exact object. If $x$ is a
+$p$-adic numbers or power series, the result is the exponent of the zero.
+Any other type combinations gives an error.
+
+The library syntax is \fun{long}{gvaluation}{GEN x, GEN p}.
+
+\subsec{variable$(\{x\})$}\kbdsidx{variable}\label{se:variable}
+Gives the main variable of the object $x$ (the variable with the highest
+priority used in $x$), and $p$ if $x$ is a $p$-adic number. Return $0$ if
+$x$ has no variable associated to it.
+\bprog
+? variable(x^2 + y)
+%1 = x
+? variable(1 + O(5^2))
+%2 = 5
+? variable([x,y,z,t])
+%3 = x
+? variable(1)
+%4 = 0
+ at eprog\noindent The construction
+\bprog
+   if (!variable(x),...)
+ at eprog\noindent can be used to test whether a variable is attached to $x$.
+
+If $x$ is omitted, returns the list of user variables known to the
+interpreter, by order of decreasing priority. (Highest priority is $x$,
+which always come first.)
+
+The library syntax is \fun{GEN}{gpolvar}{GEN x = NULL}.
+However, in library mode, this function should not be used for $x$
+non-\kbd{NULL}, since \tet{gvar} is more appropriate. Instead, for
+$x$ a $p$-adic (type \typ{PADIC}), $p$ is $gel(x,2)$; otherwise, use
+\fun{long}{gvar}{GEN x} which returns the variable number of $x$ if
+it exists, \kbd{NO\_VARIABLE} otherwise, which satisfies the property
+$\kbd{varncmp}(\kbd{NO\_VARIABLE}, v) > 0$ for all valid variable number
+$v$, i.e. it has lower priority than any variable.
+%SECTION: conversions
+
+\section{Transcendental functions}\label{se:trans}
+
+Since the values of transcendental functions cannot be exactly represented,
+these functions will always return an inexact object: a real number,
+a complex number, a $p$-adic number or a power series.  All these objects
+have a certain finite precision.
+
+As a general rule, which of course in some cases may have exceptions,
+transcendental functions operate in the following way:
+
+\item If the argument is either a real number or an inexact complex number
+(like \kbd{1.0 + I} or \kbd{Pi*I} but not \kbd{2 - 3*I}), then the
+computation is done with the precision of the argument.
+In the example below, we see that changing the precision to $50$ digits does
+not matter, because $x$ only had a precision of $19$ digits.
+\bprog
+? \p 15
+   realprecision = 19 significant digits (15 digits displayed)
+? x = Pi/4
+%1 = 0.785398163397448
+? \p 50
+   realprecision = 57 significant digits (50 digits displayed)
+? sin(x)
+%2 = 0.7071067811865475244
+ at eprog
+
+Note that even if the argument is real, the result may be complex
+(e.g.~$\text{acos}(2.0)$ or $\text{acosh}(0.0)$). See each individual
+function help for the definition of the branch cuts and choice of principal
+value.
+
+\item If the argument is either an integer, a rational, an exact complex
+number or a quadratic number, it is first converted to a real
+or complex number using the current \idx{precision} held in the default
+\tet{realprecision}.  This precision (the number of decimal digits) can be
+changed using \b{p} or \kbd{default(realprecision,...)}).
+After this conversion, the computation proceeds as above for real or complex
+arguments.
+
+In library mode, the \kbd{realprecision} does not matter; instead the
+precision is taken from the \kbd{prec} parameter which every transcendental
+function has.  As in \kbd{gp}, this \kbd{prec} is not used when the
+argument to a function is already inexact.
+Note that the argument \var{prec} stands for the length in words of a real
+number, including codewords. Hence we must have $\var{prec} \geq 3$.
+
+Some accuracies attainable on 32-bit machines cannot be attained
+on 64-bit machines for parity reasons. For example the default \kbd{gp} accuracy
+is 28 decimal digits on 32-bit machines, corresponding to \var{prec} having
+the value 5, but this cannot be attained on 64-bit machines.
+
+\item If the argument is a polmod (representing an algebraic number),
+then the function is evaluated for every possible complex embedding of that
+algebraic number.  A column vector of results is returned, with one component
+for each complex embedding.  Therefore, the number of components equals
+the degree of the \typ{POLMOD} modulus.
+
+\item If the argument is an intmod or a $p$-adic, at present only a
+few functions like \kbd{sqrt} (square root), \kbd{sqr} (square), \kbd{log},
+\kbd{exp}, powering, \kbd{teichmuller} (Teichm\"uller character) and
+\kbd{agm} (arithmetic-geometric mean) are implemented.
+
+Note that in the case of a $2$-adic number, $\kbd{sqr}(x)$ may not be
+identical to $x*x$: for example if $x = 1+O(2^5)$ and $y = 1+O(2^5)$ then
+$x*y = 1+O(2^5)$ while $\kbd{sqr}(x) = 1+O(2^6)$. Here, $x * x$ yields the
+same result as $\kbd{sqr}(x)$ since the two operands are known to be
+\emph{identical}. The same statement holds true for $p$-adics raised to the
+power $n$, where $v_p(n) > 0$.
+
+\misctitle{Remark} If we wanted to be strictly consistent with
+the PARI philosophy, we should have $x*y = (4 \mod 8)$ and $\kbd{sqr}(x) =
+(4 \mod 32)$ when both $x$ and $y$ are congruent to $2$ modulo $4$.
+However, since intmod is an exact object, PARI assumes that the modulus
+must not change, and the result is hence $(0\, \mod\, 4)$ in both cases. On
+the other hand, $p$-adics are not exact objects, hence are treated
+differently.
+
+\item If the argument is a polynomial, a power series or a rational function,
+it is, if necessary, first converted to a power series using the current
+series precision, held in the default \tet{seriesprecision}. This precision
+(the number of significant terms) can be changed using \b{ps} or
+\kbd{default(seriesprecision,...)}. Then the Taylor series expansion of the
+function around $X=0$ (where $X$ is the main variable) is computed to a
+number of terms depending on the number of terms of the argument and the
+function being computed.
+
+Under \kbd{gp} this again is transparent to the user. When programming in
+library mode, however, it is \emph{strongly} advised to perform an explicit
+conversion to a power series first, as in \kbd{x = gtoser(x, seriesprec)},
+where the number of significant terms \kbd{seriesprec} can be specified
+explicitly. If you do not do this, a global variable \kbd{precdl} is used
+instead, to convert polynomials and rational functions to a power series with
+a reasonable number of terms; tampering with the value of this global
+variable is \emph{deprecated} and strongly discouraged.
+
+
+\item If the argument is a vector or a matrix, the result is the
+componentwise evaluation of the function. In particular, transcendental
+functions on square matrices, which are not implemented in the present
+version \vers, will have a different name if they are implemented some day.
+
+\subseckbd{\pow} If $y$ is not of type integer, \kbd{x\pow y} has the same
+effect as \kbd{exp(y*log(x))}. It can be applied to $p$-adic numbers as well
+as to the more usual types.\sidx{powering}
+
+The library syntax is \fun{GEN}{gpow}{GEN x, GEN n, long prec}
+for $x\hbox{\kbd{\pow}}n$.
+
+
+\subsec{Catalan}\kbdsidx{Catalan}\label{se:Catalan}
+Catalan's constant $G = \sum_{n>=0}\dfrac{(-1)^n}{(2n+1)^2}=0.91596\cdots$.
+Note that \kbd{Catalan} is one of the few reserved names which cannot be
+used for user variables.
+
+The library syntax is \fun{GEN}{mpcatalan}{long prec}.
+
+\subsec{Euler}\kbdsidx{Euler}\label{se:Euler}
+Euler's constant $\gamma=0.57721\cdots$. Note that
+\kbd{Euler} is one of the few reserved names which cannot be used for
+user variables.
+
+The library syntax is \fun{GEN}{mpeuler}{long prec}.
+
+\subsec{I}\kbdsidx{I}\label{se:I}
+The complex number $\sqrt{-1}$.
+
+The library syntax is \fun{GEN}{gen_I}{}.
+
+\subsec{Pi}\kbdsidx{Pi}\label{se:Pi}
+The constant $\pi$ ($3.14159\cdots$). Note that \kbd{Pi} is one of the few
+reserved names which cannot be used for user variables.
+
+The library syntax is \fun{GEN}{mppi}{long prec}.
+
+\subsec{abs$(x)$}\kbdsidx{abs}\label{se:abs}
+Absolute value of $x$ (modulus if $x$ is complex).
+Rational functions are not allowed. Contrary to most transcendental
+functions, an exact argument is \emph{not} converted to a real number before
+applying \kbd{abs} and an exact result is returned if possible.
+\bprog
+? abs(-1)
+%1 = 1
+? abs(3/7 + 4/7*I)
+%2 = 5/7
+? abs(1 + I)
+%3 = 1.414213562373095048801688724
+ at eprog\noindent
+If $x$ is a polynomial, returns $-x$ if the leading coefficient is
+real and negative else returns $x$. For a power series, the constant
+coefficient is considered instead.
+
+The library syntax is \fun{GEN}{gabs}{GEN x, long prec}.
+
+\subsec{acos$(x)$}\kbdsidx{acos}\label{se:acos}
+Principal branch of $\text{cos}^{-1}(x) = -i \log (x + i\sqrt{1-x^2})$.
+In particular, $\text{Re(acos}(x))\in [0,\pi]$ and if $x\in \R$ and $|x|>1$,
+then $\text{acos}(x)$ is complex. The branch cut is in two pieces:
+$]-\infty,-1]$ , continuous with quadrant II, and $[1,+\infty[$, continuous
+with quadrant IV. We have $\text{acos}(x) = \pi/2 - \text{asin}(x)$ for all
+$x$.
+
+The library syntax is \fun{GEN}{gacos}{GEN x, long prec}.
+
+\subsec{acosh$(x)$}\kbdsidx{acosh}\label{se:acosh}
+Principal branch of $\text{cosh}^{-1}(x) = 2
+ \log(\sqrt{(x+1)/2} + \sqrt{(x-1)/2})$. In particular,
+$\text{Re}(\text{acosh}(x))\geq 0$ and
+$\text{In}(\text{acosh}(x))\in ]-\pi,\pi]0$; if $x\in \R$ and $x<1$, then
+$\text{acosh}(x)$ is complex.
+
+The library syntax is \fun{GEN}{gacosh}{GEN x, long prec}.
+
+\subsec{agm$(x,y)$}\kbdsidx{agm}\label{se:agm}
+Arithmetic-geometric mean of $x$ and $y$. In the
+case of complex or negative numbers, the optimal AGM is returned
+(the largest in absolute value over all choices of the signs of the square
+roots).  $p$-adic or power series arguments are also allowed. Note that
+a $p$-adic agm exists only if $x/y$ is congruent to 1 modulo $p$ (modulo
+16 for $p=2$). $x$ and $y$ cannot both be vectors or matrices.
+
+The library syntax is \fun{GEN}{agm}{GEN x, GEN y, long prec}.
+
+\subsec{arg$(x)$}\kbdsidx{arg}\label{se:arg}
+Argument of the complex number $x$, such that $-\pi<\text{arg}(x)\le\pi$.
+
+The library syntax is \fun{GEN}{garg}{GEN x, long prec}.
+
+\subsec{asin$(x)$}\kbdsidx{asin}\label{se:asin}
+Principal branch of $\text{sin}^{-1}(x) = -i \log(ix + \sqrt{1 - x^2})$.
+In particular, $\text{Re(asin}(x))\in [-\pi/2,\pi/2]$ and if $x\in \R$ and
+$|x|>1$ then $\text{asin}(x)$ is complex. The branch cut is in two pieces:
+$]-\infty,-1]$, continuous with quadrant II, and $[1,+\infty[$ continuous
+with quadrant IV. The function satisfies $i \text{asin}(x) =
+\text{asinh}(ix)$.
+
+The library syntax is \fun{GEN}{gasin}{GEN x, long prec}.
+
+\subsec{asinh$(x)$}\kbdsidx{asinh}\label{se:asinh}
+Principal branch of $\text{sinh}^{-1}(x) = \log(x + \sqrt{1+x^2})$. In
+particular $\text{Im(asinh}(x))\in [-\pi/2,\pi/2]$.
+The branch cut is in two pieces: [-i oo ,-i],  continuous with quadrant III
+and [i,+i oo [ continuous with quadrant I.
+
+The library syntax is \fun{GEN}{gasinh}{GEN x, long prec}.
+
+\subsec{atan$(x)$}\kbdsidx{atan}\label{se:atan}
+Principal branch of $\text{tan}^{-1}(x) = \log ((1+ix)/(1-ix)) /
+2i$. In particular the real part of $\text{atan}(x))$ belongs to
+$]-\pi/2,\pi/2[$.
+The branch cut is in two pieces:
+$]-i\infty,-i[$, continuous with quadrant IV, and $]i,+i \infty[$ continuous
+with quadrant II. The function satisfies $i \text{atan}(x) =
+-i\text{atanh}(ix)$ for all $x\neq \pm i$.
+
+The library syntax is \fun{GEN}{gatan}{GEN x, long prec}.
+
+\subsec{atanh$(x)$}\kbdsidx{atanh}\label{se:atanh}
+Principal branch of $\text{tanh}^{-1}(x) = log ((1+x)/(1-x)) / 2$. In
+particular the imaginary part of $\text{atanh}(x)$ belongs to
+$[-\pi/2,\pi/2]$; if $x\in \R$ and $|x|>1$ then $\text{atanh}(x)$ is complex.
+
+The library syntax is \fun{GEN}{gatanh}{GEN x, long prec}.
+
+\subsec{bernfrac$(x)$}\kbdsidx{bernfrac}\label{se:bernfrac}
+Bernoulli number\sidx{Bernoulli numbers} $B_x$,
+where $B_0=1$, $B_1=-1/2$, $B_2=1/6$,\dots, expressed as a rational number.
+The argument $x$ should be of type integer.
+
+The library syntax is \fun{GEN}{bernfrac}{long x}.
+
+\subsec{bernpol$(n, \{v = 'x\})$}\kbdsidx{bernpol}\label{se:bernpol}
+\idx{Bernoulli polynomial} $B_n$ in variable $v$.
+\bprog
+? bernpol(1)
+%1 = x - 1/2
+? bernpol(3)
+%2 = x^3 - 3/2*x^2 + 1/2*x
+ at eprog
+
+The library syntax is \fun{GEN}{bernpol}{long n, long v  = -1}, where \kbd{v } is a variable number.
+
+\subsec{bernreal$(x)$}\kbdsidx{bernreal}\label{se:bernreal}
+Bernoulli number\sidx{Bernoulli numbers}
+$B_x$, as \kbd{bernfrac}, but $B_x$ is returned as a real number
+(with the current precision).
+
+The library syntax is \fun{GEN}{bernreal}{long x, long prec}.
+
+\subsec{bernvec$(x)$}\kbdsidx{bernvec}\label{se:bernvec}
+Creates a vector containing, as rational numbers,
+the \idx{Bernoulli numbers} $B_0$, $B_2$,\dots, $B_{2x}$.
+This routine is obsolete. Use \kbd{bernfrac} instead each time you need a
+Bernoulli number in exact form.
+
+\misctitle{Note} This routine is implemented using repeated independent
+calls to \kbd{bernfrac}, which is faster than the standard recursion in exact
+arithmetic. It is only kept for backward compatibility: it is not faster than
+individual calls to \kbd{bernfrac}, its output uses a lot of memory space,
+and coping with the index shift is awkward.
+
+The library syntax is \fun{GEN}{bernvec}{long x}.
+
+\subsec{besselh1$(\var{nu},x)$}\kbdsidx{besselh1}\label{se:besselh1}
+$H^1$-Bessel function of index \var{nu} and argument $x$.
+
+The library syntax is \fun{GEN}{hbessel1}{GEN nu, GEN x, long prec}.
+
+\subsec{besselh2$(\var{nu},x)$}\kbdsidx{besselh2}\label{se:besselh2}
+$H^2$-Bessel function of index \var{nu} and argument $x$.
+
+The library syntax is \fun{GEN}{hbessel2}{GEN nu, GEN x, long prec}.
+
+\subsec{besseli$(\var{nu},x)$}\kbdsidx{besseli}\label{se:besseli}
+$I$-Bessel function of index \var{nu} and
+argument $x$. If $x$ converts to a power series, the initial factor
+$(x/2)^\nu/\Gamma(\nu+1)$ is omitted (since it cannot be represented in PARI
+when $\nu$ is not integral).
+
+The library syntax is \fun{GEN}{ibessel}{GEN nu, GEN x, long prec}.
+
+\subsec{besselj$(\var{nu},x)$}\kbdsidx{besselj}\label{se:besselj}
+$J$-Bessel function of index \var{nu} and
+argument $x$. If $x$ converts to a power series, the initial factor
+$(x/2)^\nu/\Gamma(\nu+1)$ is omitted (since it cannot be represented in PARI
+when $\nu$ is not integral).
+
+The library syntax is \fun{GEN}{jbessel}{GEN nu, GEN x, long prec}.
+
+\subsec{besseljh$(n,x)$}\kbdsidx{besseljh}\label{se:besseljh}
+$J$-Bessel function of half integral index.
+More precisely, $\kbd{besseljh}(n,x)$ computes $J_{n+1/2}(x)$ where $n$
+must be of type integer, and $x$ is any element of $\C$. In the
+present version \vers, this function is not very accurate when $x$ is small.
+
+The library syntax is \fun{GEN}{jbesselh}{GEN n, GEN x, long prec}.
+
+\subsec{besselk$(\var{nu},x)$}\kbdsidx{besselk}\label{se:besselk}
+$K$-Bessel function of index \var{nu} and argument $x$.
+
+The library syntax is \fun{GEN}{kbessel}{GEN nu, GEN x, long prec}.
+
+\subsec{besseln$(\var{nu},x)$}\kbdsidx{besseln}\label{se:besseln}
+$N$-Bessel function of index \var{nu} and argument $x$.
+
+The library syntax is \fun{GEN}{nbessel}{GEN nu, GEN x, long prec}.
+
+\subsec{cos$(x)$}\kbdsidx{cos}\label{se:cos}
+Cosine of $x$.
+
+The library syntax is \fun{GEN}{gcos}{GEN x, long prec}.
+
+\subsec{cosh$(x)$}\kbdsidx{cosh}\label{se:cosh}
+Hyperbolic cosine of $x$.
+
+The library syntax is \fun{GEN}{gcosh}{GEN x, long prec}.
+
+\subsec{cotan$(x)$}\kbdsidx{cotan}\label{se:cotan}
+Cotangent of $x$.
+
+The library syntax is \fun{GEN}{gcotan}{GEN x, long prec}.
+
+\subsec{dilog$(x)$}\kbdsidx{dilog}\label{se:dilog}
+Principal branch of the dilogarithm of $x$,
+i.e.~analytic continuation of the power series $\log_2(x)=\sum_{n\ge1}x^n/n^2$.
+
+The library syntax is \fun{GEN}{dilog}{GEN x, long prec}.
+
+\subsec{eint1$(x,\{n\})$}\kbdsidx{eint1}\label{se:eint1}
+Exponential integral $\int_x^\infty \dfrac{e^{-t}}{t}\,dt =
+\kbd{incgam}(0, x)$, where the latter expression extends the function
+definition from real $x > 0$ to all complex $x \neq 0$.
+
+If $n$ is present, we must have $x > 0$; the function returns the
+$n$-dimensional vector $[\kbd{eint1}(x),\dots,\kbd{eint1}(nx)]$. Contrary to
+other transcendental functions, and to the default case ($n$ omitted), the
+values are correct up to a bounded \emph{absolute}, rather than relative,
+error $10^-n$, where $n$ is \kbd{precision}$(x)$ if $x$ is a \typ{REAL}
+and defaults to \kbd{realprecision} otherwise. (In the most important
+application, to the computation of $L$-functions via approximate functional
+equations, those values appear as weights in long sums and small individual
+relative errors are less useful than controlling the absolute error.) This is
+faster than repeatedly calling \kbd{eint1($i$ * x)}, but less precise.
+
+The library syntax is \fun{GEN}{veceint1}{GEN x, GEN n = NULL, long prec}.
+Also available is \fun{GEN}{eint1}{GEN x, long prec}.
+
+\subsec{erfc$(x)$}\kbdsidx{erfc}\label{se:erfc}
+Complementary error function, analytic continuation of
+$(2/\sqrt\pi)\int_x^\infty e^{-t^2}\,dt = \kbd{incgam}(1/2,x^2)/\sqrt\pi$,
+where the latter expression extends the function definition from real $x$ to
+all complex $x \neq 0$.
+
+The library syntax is \fun{GEN}{gerfc}{GEN x, long prec}.
+
+\subsec{eta$(z,\{\fl=0\})$}\kbdsidx{eta}\label{se:eta}
+Variants of \idx{Dedekind}'s $\eta$ function.
+If $\fl = 0$, return $\prod_{n=1}^\infty(1-q^n)$, where $q$ depends on $x$
+in the following way:
+
+\item $q = e^{2i\pi x}$ if $x$ is a \emph{complex number} (which must then
+have positive imaginary part); notice that the factor $q^{1/24}$ is
+missing!
+
+\item $q = x$ if $x$ is a \typ{PADIC}, or can be converted to a
+\emph{power series} (which must then have positive valuation).
+
+If $\fl$ is non-zero, $x$ is converted to a complex number and we return the
+true $\eta$ function, $q^{1/24}\prod_{n=1}^\infty(1-q^n)$,
+where $q = e^{2i\pi x}$.
+
+The library syntax is \fun{GEN}{eta0}{GEN z, long flag, long prec}.
+
+Also available is \fun{GEN}{trueeta}{GEN x, long prec} ($\fl=1$).
+
+\subsec{exp$(x)$}\kbdsidx{exp}\label{se:exp}
+Exponential of $x$.
+$p$-adic arguments with positive valuation are accepted.
+
+The library syntax is \fun{GEN}{gexp}{GEN x, long prec}.
+For a \typ{PADIC} $x$, the function
+\fun{GEN}{Qp_exp}{GEN x} is also available.
+
+\subsec{expm1$(x)$}\kbdsidx{expm1}\label{se:expm1}
+Return $\exp(x)-1$, computed in a way that is also accurate
+when the real part of $x$ is near $0$. Only accept real or complex arguments.
+A naive direct computation would suffer from catastrophic cancellation;
+PARI's direct computation of $\exp(x)$ alleviates this well known problem at
+the expense of computing $\exp(x)$ to a higher accuracy when $x$ is small.
+Using \kbd{expm1} is recommended instead:
+\bprog
+? default(realprecision, 10000); x = 1e-100;
+? a = expm1(x);
+time = 4 ms.
+? b = exp(x)-1;
+time = 28 ms.
+? default(realprecision, 10040); x = 1e-100;
+? c = expm1(x);  \\ reference point
+? abs(a-c)/c  \\ relative error in expm1(x)
+%7 = 0.E-10017
+? abs(b-c)/c  \\ relative error in exp(x)-1
+%8 = 1.7907031188259675794 E-9919
+ at eprog\noindent As the example above shows, when $x$ is near $0$,
+\kbd{expm1} is both faster and more accurate than \kbd{exp(x)-1}.
+
+The library syntax is \fun{GEN}{gexpm1}{GEN x, long prec}.
+
+\subsec{gamma$(s)$}\kbdsidx{gamma}\label{se:gamma}
+For $s$ a complex number, evaluates Euler's gamma
+function \sidx{gamma-function}
+$$\Gamma(s)=\int_0^\infty t^{s-1}\exp(-t)\,dt.$$
+Error if $s$ is a non-positive integer, where $\Gamma$ has a pole.
+
+For $s$ a \typ{PADIC}, evaluates the Morita gamma function at $s$, that
+is the unique continuous $p$-adic function on the $p$-adic integers
+extending $\Gamma_p(k)=(-1)^k \prod_{j<k}'j$, where the prime means that $p$
+does not divide $j$.
+\bprog
+? gamma(1/4 + O(5^10))
+%1= 1 + 4*5 + 3*5^4 + 5^6 + 5^7 + 4*5^9 + O(5^10)
+? algdep(%,4)
+%2 = x^4 + 4*x^2 + 5
+ at eprog
+
+The library syntax is \fun{GEN}{ggamma}{GEN s, long prec}.
+For a \typ{PADIC} $x$, the function \fun{GEN}{Qp_gamma}{GEN x} is
+also available.
+
+\subsec{gammah$(x)$}\kbdsidx{gammah}\label{se:gammah}
+Gamma function evaluated at the argument $x+1/2$.
+
+The library syntax is \fun{GEN}{ggammah}{GEN x, long prec}.
+
+\subsec{hyperu$(a,b,x)$}\kbdsidx{hyperu}\label{se:hyperu}
+$U$-confluent hypergeometric function with
+parameters $a$ and $b$. The parameters $a$ and $b$ can be complex but
+the present implementation requires $x$ to be positive.
+
+The library syntax is \fun{GEN}{hyperu}{GEN a, GEN b, GEN x, long prec}.
+
+\subsec{incgam$(s,x,\{g\})$}\kbdsidx{incgam}\label{se:incgam}
+Incomplete gamma function $\int_x^\infty e^{-t}t^{s-1}\,dt$, extended by
+analytic continuation to all complex $x, s$ not both $0$. The relative error
+is bounded in terms of the precision of $s$ (the accuracy of $x$ is ignored
+when determining the output precision). When $g$ is given, assume that
+$g=\Gamma(s)$. For small $|x|$, this will speed up the computation.
+
+The library syntax is \fun{GEN}{incgam0}{GEN s, GEN x, GEN g = NULL, long prec}.
+Also available is \fun{GEN}{incgam}{GEN s, GEN x, long prec}.
+
+\subsec{incgamc$(s,x)$}\kbdsidx{incgamc}\label{se:incgamc}
+Complementary incomplete gamma function.
+The arguments $x$ and $s$ are complex numbers such that $s$ is not a pole of
+$\Gamma$ and $|x|/(|s|+1)$ is not much larger than 1 (otherwise the
+convergence is very slow). The result returned is $\int_0^x
+e^{-t}t^{s-1}\,dt$.
+
+The library syntax is \fun{GEN}{incgamc}{GEN s, GEN x, long prec}.
+
+\subsec{lambertw$(y)$}\kbdsidx{lambertw}\label{se:lambertw}
+Lambert $W$ function, solution of the implicit equation $xe^x=y$,
+for $y > 0$.
+
+The library syntax is \fun{GEN}{glambertW}{GEN y, long prec}.
+
+\subsec{lngamma$(x)$}\kbdsidx{lngamma}\label{se:lngamma}
+Principal branch of the logarithm of the gamma function of $x$. This
+function is analytic on the complex plane with non-positive integers
+removed, and can have much larger arguments than \kbd{gamma} itself.
+
+For $x$ a power series such that $x(0)$ is not a pole of \kbd{gamma},
+compute the Taylor expansion. (PARI only knows about regular power series
+and can't include logarithmic terms.)
+\bprog
+? lngamma(1+x+O(x^2))
+%1 = -0.57721566490153286060651209008240243104*x + O(x^2)
+? lngamma(x+O(x^2))
+ ***   at top-level: lngamma(x+O(x^2))
+ ***                 ^-----------------
+ *** lngamma: domain error in lngamma: valuation != 0
+? lngamma(-1+x+O(x^2))
+ *** lngamma: Warning: normalizing a series with 0 leading term.
+ ***   at top-level: lngamma(-1+x+O(x^2))
+ ***                 ^--------------------
+ *** lngamma: domain error in intformal: residue(series, pole) != 0
+ at eprog
+
+The library syntax is \fun{GEN}{glngamma}{GEN x, long prec}.
+
+\subsec{log$(x)$}\kbdsidx{log}\label{se:log}
+Principal branch of the natural logarithm of
+$x \in \C^*$, i.e.~such that $\text{Im(log}(x))\in{} ]-\pi,\pi]$.
+The branch cut lies
+along the negative real axis, continuous with quadrant 2, i.e.~such that
+$\lim_{b\to 0^+} \log (a+bi) = \log a$ for $a \in\R^*$. The result is complex
+(with imaginary part equal to $\pi$) if $x\in \R$ and $x < 0$. In general,
+the algorithm uses the formula
+$$\log(x) \approx {\pi\over 2\text{agm}(1, 4/s)} - m \log 2, $$
+if $s = x 2^m$ is large enough. (The result is exact to $B$ bits provided
+$s > 2^{B/2}$.) At low accuracies, the series expansion near $1$ is used.
+
+$p$-adic arguments are also accepted for $x$, with the convention that
+$\log(p)=0$. Hence in particular $\exp(\log(x))/x$ is not in general equal to
+1 but to a $(p-1)$-th root of unity (or $\pm1$ if $p=2$) times a power of $p$.
+
+The library syntax is \fun{GEN}{glog}{GEN x, long prec}.
+For a \typ{PADIC} $x$, the function
+\fun{GEN}{Qp_log}{GEN x} is also available.
+
+\subsec{polylog$(m,x,\{\fl=0\})$}\kbdsidx{polylog}\label{se:polylog}
+One of the different polylogarithms, depending on \fl:
+
+If $\fl=0$ or is omitted: $m^\text{th}$ polylogarithm of $x$, i.e.~analytic
+continuation of the power series $\text{Li}_m(x)=\sum_{n\ge1}x^n/n^m$
+($x < 1$). Uses the functional equation linking the values at $x$ and $1/x$
+to restrict to the case $|x|\leq 1$, then the power series when
+$|x|^2\le1/2$, and the power series expansion in $\log(x)$ otherwise.
+
+Using $\fl$, computes a modified $m^\text{th}$ polylogarithm of $x$.
+We use Zagier's notations; let $\Re_m$ denote $\Re$ or $\Im$ depending
+on whether $m$ is odd or even:
+
+If $\fl=1$: compute $\tilde D_m(x)$, defined for $|x|\le1$ by
+$$\Re_m\left(\sum_{k=0}^{m-1} \dfrac{(-\log|x|)^k}{k!}\text{Li}_{m-k}(x)
++\dfrac{(-\log|x|)^{m-1}}{m!}\log|1-x|\right).$$
+
+If $\fl=2$: compute $D_m(x)$, defined for $|x|\le1$ by
+$$\Re_m\left(\sum_{k=0}^{m-1}\dfrac{(-\log|x|)^k}{k!}\text{Li}_{m-k}(x)
+-\dfrac{1}{2}\dfrac{(-\log|x|)^m}{m!}\right).$$
+
+If $\fl=3$: compute $P_m(x)$, defined for $|x|\le1$ by
+$$\Re_m\left(\sum_{k=0}^{m-1}\dfrac{2^kB_k}{k!}(\log|x|)^k\text{Li}_{m-k}(x)
+-\dfrac{2^{m-1}B_m}{m!}(\log|x|)^m\right).$$
+
+These three functions satisfy the functional equation
+$f_m(1/x) = (-1)^{m-1}f_m(x)$.
+
+The library syntax is \fun{GEN}{polylog0}{long m, GEN x, long flag, long prec}.
+Also available is
+\fun{GEN}{gpolylog}{long m, GEN x, long prec} (\fl = 0).
+
+\subsec{psi$(x)$}\kbdsidx{psi}\label{se:psi}
+The $\psi$-function of $x$, i.e.~the logarithmic derivative
+$\Gamma'(x)/\Gamma(x)$.
+
+The library syntax is \fun{GEN}{gpsi}{GEN x, long prec}.
+
+\subsec{sin$(x)$}\kbdsidx{sin}\label{se:sin}
+Sine of $x$.
+
+The library syntax is \fun{GEN}{gsin}{GEN x, long prec}.
+
+\subsec{sinh$(x)$}\kbdsidx{sinh}\label{se:sinh}
+Hyperbolic sine of $x$.
+
+The library syntax is \fun{GEN}{gsinh}{GEN x, long prec}.
+
+\subsec{sqr$(x)$}\kbdsidx{sqr}\label{se:sqr}
+Square of $x$. This operation is not completely
+straightforward, i.e.~identical to $x * x$, since it can usually be
+computed more efficiently (roughly one-half of the elementary
+multiplications can be saved). Also, squaring a $2$-adic number increases
+its precision. For example,
+\bprog
+? (1 + O(2^4))^2
+%1 = 1 + O(2^5)
+? (1 + O(2^4)) * (1 + O(2^4))
+%2 = 1 + O(2^4)
+ at eprog\noindent
+Note that this function is also called whenever one multiplies two objects
+which are known to be \emph{identical}, e.g.~they are the value of the same
+variable, or we are computing a power.
+\bprog
+? x = (1 + O(2^4)); x * x
+%3 = 1 + O(2^5)
+? (1 + O(2^4))^4
+%4 = 1 + O(2^6)
+ at eprog\noindent
+(note the difference between \kbd{\%2} and \kbd{\%3} above).
+
+The library syntax is \fun{GEN}{gsqr}{GEN x}.
+
+\subsec{sqrt$(x)$}\kbdsidx{sqrt}\label{se:sqrt}
+Principal branch of the square root of $x$, defined as $\sqrt{x} =
+\exp(\log x / 2)$. In particular, we have
+$\text{Arg}(\text{sqrt}(x))\in{} ]-\pi/2, \pi/2]$, and if $x\in \R$ and $x<0$,
+then the result is complex with positive imaginary part.
+
+Intmod a prime $p$, \typ{PADIC} and \typ{FFELT} are allowed as arguments. In
+the first 2 cases (\typ{INTMOD}, \typ{PADIC}), the square root (if it
+exists) which is returned is the one whose first $p$-adic digit is in the
+interval $[0,p/2]$. For other arguments, the result is undefined.
+
+The library syntax is \fun{GEN}{gsqrt}{GEN x, long prec}.
+For a \typ{PADIC} $x$, the function
+\fun{GEN}{Qp_sqrt}{GEN x} is also available.
+
+\subsec{sqrtn$(x,n,\{\&z\})$}\kbdsidx{sqrtn}\label{se:sqrtn}
+Principal branch of the $n$th root of $x$,
+i.e.~such that $\text{Arg}(\text{sqrt}(x))\in{} ]-\pi/n, \pi/n]$. Intmod
+a prime and $p$-adics are allowed as arguments.
+
+If $z$ is present, it is set to a suitable root of unity allowing to
+recover all the other roots. If it was not possible, z is
+set to zero. In the case this argument is present and no square root exist,
+$0$ is returned instead or raising an error.
+\bprog
+? sqrtn(Mod(2,7), 2)
+%1 = Mod(4, 7)
+? sqrtn(Mod(2,7), 2, &z); z
+%2 = Mod(6, 7)
+? sqrtn(Mod(2,7), 3)
+  ***   at top-level: sqrtn(Mod(2,7),3)
+  ***                 ^-----------------
+  *** sqrtn: nth-root does not exist in gsqrtn.
+? sqrtn(Mod(2,7), 3,  &z)
+%2 = 0
+? z
+%3 = 0
+ at eprog
+
+The following script computes all roots in all possible cases:
+\bprog
+sqrtnall(x,n)=
+{ my(V,r,z,r2);
+  r = sqrtn(x,n, &z);
+  if (!z, error("Impossible case in sqrtn"));
+  if (type(x) == "t_INTMOD" || type(x)=="t_PADIC",
+    r2 = r*z; n = 1;
+    while (r2!=r, r2*=z;n++));
+  V = vector(n); V[1] = r;
+  for(i=2, n, V[i] = V[i-1]*z);
+  V
+}
+addhelp(sqrtnall,"sqrtnall(x,n):compute the vector of nth-roots of x");
+ at eprog\noindent
+
+The library syntax is \fun{GEN}{gsqrtn}{GEN x, GEN n, GEN *z = NULL, long prec}.
+If $x$ is a \typ{PADIC}, the function
+\fun{GEN}{Qp_sqrt}{GEN x, GEN n, GEN *z} is also available.
+
+\subsec{tan$(x)$}\kbdsidx{tan}\label{se:tan}
+Tangent of $x$.
+
+The library syntax is \fun{GEN}{gtan}{GEN x, long prec}.
+
+\subsec{tanh$(x)$}\kbdsidx{tanh}\label{se:tanh}
+Hyperbolic tangent of $x$.
+
+The library syntax is \fun{GEN}{gtanh}{GEN x, long prec}.
+
+\subsec{teichmuller$(x)$}\kbdsidx{teichmuller}\label{se:teichmuller}
+Teichm\"uller character of the $p$-adic number $x$, i.e. the unique
+$(p-1)$-th root of unity congruent to $x / p^{v_p(x)}$ modulo $p$.
+
+The library syntax is \fun{GEN}{teich}{GEN x}.
+
+\subsec{theta$(q,z)$}\kbdsidx{theta}\label{se:theta}
+Jacobi sine theta-function
+$$ \theta_1(z, q) = 2q^{1/4} \sum_{n\geq 0} (-1)^n q^{n(n+1)} \sin((2n+1)z).$$
+
+The library syntax is \fun{GEN}{theta}{GEN q, GEN z, long prec}.
+
+\subsec{thetanullk$(q,k)$}\kbdsidx{thetanullk}\label{se:thetanullk}
+$k$-th derivative at $z=0$ of $\kbd{theta}(q,z)$.
+
+The library syntax is \fun{GEN}{thetanullk}{GEN q, long k, long prec}.
+
+\fun{GEN}{vecthetanullk}{GEN q, long k, long prec} returns the vector
+of all $\dfrac{d^i\theta}{dz^i}(q,0)$ for all odd $i = 1, 3, \dots, 2k-1$.
+\fun{GEN}{vecthetanullk_tau}{GEN tau, long k, long prec} returns
+\kbd{vecthetanullk\_tau} at $q = \exp(2i\pi \kbd{tau})$.
+
+\subsec{weber$(x,\{\fl=0\})$}\kbdsidx{weber}\label{se:weber}
+One of Weber's three $f$ functions.
+If $\fl=0$, returns
+$$f(x)=\exp(-i\pi/24)\cdot\eta((x+1)/2)\,/\,\eta(x) \quad\hbox{such that}\quad
+j=(f^{24}-16)^3/f^{24}\,,$$
+where $j$ is the elliptic $j$-invariant  (see the function \kbd{ellj}).
+If $\fl=1$, returns
+$$f_1(x)=\eta(x/2)\,/\,\eta(x)\quad\hbox{such that}\quad
+j=(f_1^{24}+16)^3/f_1^{24}\,.$$
+Finally, if $\fl=2$, returns
+$$f_2(x)=\sqrt{2}\eta(2x)\,/\,\eta(x)\quad\hbox{such that}\quad
+j=(f_2^{24}+16)^3/f_2^{24}.$$
+Note the identities $f^8=f_1^8+f_2^8$ and $ff_1f_2=\sqrt2$.
+
+The library syntax is \fun{GEN}{weber0}{GEN x, long flag, long prec}.
+Also available are \fun{GEN}{weberf}{GEN x, long prec},
+\fun{GEN}{weberf1}{GEN x, long prec} and \fun{GEN}{weberf2}{GEN x, long prec}.
+
+\subsec{zeta$(s)$}\kbdsidx{zeta}\label{se:zeta}
+For $s$ a complex number, Riemann's zeta
+function \sidx{Riemann zeta-function} $\zeta(s)=\sum_{n\ge1}n^{-s}$,
+computed using the \idx{Euler-Maclaurin} summation formula, except
+when $s$ is of type integer, in which case it is computed using
+Bernoulli numbers\sidx{Bernoulli numbers} for $s\le0$ or $s>0$ and
+even, and using modular forms for $s>0$ and odd.
+
+For $s$ a $p$-adic number, Kubota-Leopoldt zeta function at $s$, that
+is the unique continuous $p$-adic function on the $p$-adic integers
+that interpolates the values of $(1 - p^{-k}) \zeta(k)$ at negative
+integers $k$ such that $k \equiv 1 \pmod{p-1}$ (resp. $k$ is odd) if
+$p$ is odd (resp. $p = 2$).
+
+The library syntax is \fun{GEN}{gzeta}{GEN s, long prec}.
+%SECTION: transcendental
+
+\section{Arithmetic functions}\label{se:arithmetic}
+
+These functions are by definition functions whose natural domain of
+definition is either $\Z$ (or $\Z_{>0}$). The way these functions are used is
+completely different from transcendental functions in that there are no
+automatic type conversions: in general only integers are accepted as
+arguments. An integer argument $N$ can be given in the following alternate
+formats:
+
+\item \typ{MAT}: its factorization \kbd{fa = factor($N$)},
+
+\item \typ{VEC}: a pair \kbd{[$N$, fa]} giving both the integer and
+  its factorization.
+
+This allows to compute different arithmetic functions at a given $N$
+while factoring the latter only once.
+
+\bprog
+  ? N = 10!; faN = factor(N);
+  ? eulerphi(N)
+  %2 = 829440
+  ? eulerphi(faN)
+  %3 = 829440
+  ? eulerphi(S = [N, faN])
+  %4 = 829440
+  ? sigma(S)
+  %5 = 15334088
+ at eprog
+
+\subsec{Arithmetic functions and the factoring engine}
+All arithmetic functions in the narrow sense of the word~--- Euler's
+totient\sidx{Euler totient function} function, the \idx{Moebius} function,
+the sums over divisors or powers of divisors etc.--- call, after trial
+division by small primes, the same versatile factoring machinery described
+under \kbd{factorint}. It includes \idx{Shanks SQUFOF}, \idx{Pollard Rho},
+\idx{ECM} and \idx{MPQS} stages, and has an early exit option for the
+functions \teb{moebius} and (the integer function underlying)
+\teb{issquarefree}. This machinery relies on a fairly strong
+probabilistic primality test, see \kbd{ispseudoprime}, but you may also set
+\bprog
+  default(factor_proven, 1)
+ at eprog\noindent to ensure that all tentative factorizations are fully proven.
+This should not slow down PARI too much, unless prime numbers with
+hundreds of decimal digits occur frequently in your application.
+
+\subsec{Orders in finite groups and Discrete Logarithm functions}
+\label{se:DLfun}
+
+The following functions compute the order of an element in a finite group:
+\kbd{ellorder} (the rational points on an elliptic curve defined over a
+finite field), \kbd{fforder} (the multiplicative group of a finite field),
+\kbd{znorder} (the invertible elements in $\Z/n\Z$). The following functions
+compute discrete logarithms in the same groups (whenever this is meaningful)
+\kbd{elllog}, \kbd{fflog}, \kbd{znlog}.
+
+All such functions allow an optional argument specifying an integer
+$N$, representing the order of the group. (The \emph{order} functions also
+allows any non-zero multiple of the order, with a minor loss of efficiency.)
+That optional argument follows the same format as given above:
+
+\item \typ{INT}: the integer $N$,
+
+\item \typ{MAT}: the factorization \kbd{fa = factor($N$)},
+
+\item \typ{VEC}: this is the preferred format and provides both the
+integer $N$ and its factorization in a two-component vector
+\kbd{[$N$, fa]}.
+
+When the group is fixed and many orders or discrete logarithms will be
+computed, it is much more efficient to initialize this data once and for all
+and pass it to the relevant functions, as in
+\bprog
+? p = nextprime(10^40);
+? v = [p-1, factor(p-1)]; \\ data for discrete log & order computations
+? znorder(Mod(2,p), v)
+%3 = 500000000000000000000000000028
+? g = znprimroot(p);
+? znlog(2, g, v)
+%5 = 543038070904014908801878611374
+ at eprog
+\bigskip
+
+
+\subsec{addprimes$(\{x=[\,]\})$}\kbdsidx{addprimes}\label{se:addprimes}
+Adds the integers contained in the
+vector $x$ (or the single integer $x$) to a special table of
+``user-defined primes'', and returns that table. Whenever \kbd{factor} is
+subsequently called, it will trial divide by the elements in this table.
+If $x$ is empty or omitted, just returns the current list of extra
+primes.
+
+The entries in $x$ must be primes: there is no internal check, even if
+the \tet{factor_proven} default is set. To remove primes from the list use
+\kbd{removeprimes}.
+
+The library syntax is \fun{GEN}{addprimes}{GEN x = NULL}.
+
+\subsec{bestappr$(x, \{B\})$}\kbdsidx{bestappr}\label{se:bestappr}
+Using variants of the extended Euclidean algorithm, returns a rational
+approximation $a/b$ to $x$, whose denominator is limited
+by $B$, if present. If $B$ is omitted, return the best approximation
+affordable given the input accuracy; if you are looking for true rational
+numbers, presumably approximated to sufficient accuracy, you should first
+try that option. Otherwise, $B$ must be a positive real scalar (impose
+$0 < b \leq B$).
+
+\item If $x$ is a \typ{REAL} or a \typ{FRAC}, this function uses continued
+fractions.
+\bprog
+? bestappr(Pi, 100)
+%1 = 22/7
+? bestappr(0.1428571428571428571428571429)
+%2 = 1/7
+? bestappr([Pi, sqrt(2) + 'x], 10^3)
+%3 = [355/113, x + 1393/985]
+ at eprog
+By definition, $a/b$ is the best rational approximation to $x$ if
+$|b x - a| < |v x - u|$ for all integers $(u,v)$ with $0 < v \leq B$.
+(Which implies that $n/d$ is a convergent of the continued fraction of $x$.)
+
+\item If $x$ is a \typ{INTMOD} modulo $N$ or a \typ{PADIC} of precision $N =
+p^k$, this function performs rational modular reconstruction modulo $N$. The
+routine then returns the unique rational number $a/b$ in coprime integers
+$|a| < N/2B$ and $b\leq B$ which is congruent to $x$ modulo $N$. Omitting
+$B$ amounts to choosing it of the order of $\sqrt{N/2}$. If rational
+reconstruction is not possible (no suitable $a/b$ exists), returns $[]$.
+\bprog
+? bestappr(Mod(18526731858, 11^10))
+%1 = 1/7
+? bestappr(Mod(18526731858, 11^20))
+%2 = []
+? bestappr(3 + 5 + 3*5^2 + 5^3 + 3*5^4 + 5^5 + 3*5^6 + O(5^7))
+%2 = -1/3
+ at eprog\noindent In most concrete uses, $B$ is a prime power and we performed
+Hensel lifting to obtain $x$.
+
+The function applies recursively to components of complex objects
+(polynomials, vectors, \dots). If rational reconstruction fails for even a
+single entry, return $[]$.
+
+The library syntax is \fun{GEN}{bestappr}{GEN x, GEN B = NULL}.
+
+\subsec{bestapprPade$(x, \{B\})$}\kbdsidx{bestapprPade}\label{se:bestapprPade}
+Using variants of the extended Euclidean algorithm, returns a rational
+function approximation $a/b$ to $x$, whose denominator is limited
+by $B$, if present. If $B$ is omitted, return the best approximation
+affordable given the input accuracy; if you are looking for true rational
+functions, presumably approximated to sufficient accuracy, you should first
+try that option. Otherwise, $B$ must be a non-negative real (impose
+$0 \leq \text{degree}(b) \leq B$).
+
+\item If $x$ is a \typ{RFRAC} or \typ{SER}, this function uses continued
+fractions.
+\bprog
+? bestapprPade((1-x^11)/(1-x)+O(x^11))
+%1 = 1/(-x + 1)
+? bestapprPade([1/(1+x+O(x^10)), (x^3-2)/(x^3+1)], 1)
+%2 =  [1/(x + 1), -2]
+ at eprog
+
+\item If $x$ is a \typ{POLMOD} modulo $N$ or a \typ{SER} of precision $N =
+t^k$, this function performs rational modular reconstruction modulo $N$. The
+routine then returns the unique rational function $a/b$ in coprime
+polynomials, with $\text{degree}(b)\leq B$ which is congruent to $x$ modulo
+$N$. Omitting $B$ amounts to choosing it of the order of $N/2$. If rational
+reconstruction is not possible (no suitable $a/b$ exists), returns $[]$.
+\bprog
+? bestapprPade(Mod(1+x+x^2+x^3+x^4, x^4-2))
+%1 = (2*x - 1)/(x - 1)
+? % * Mod(1,x^4-2)
+%2 = Mod(x^3 + x^2 + x + 3, x^4 - 2)
+? bestapprPade(Mod(1+x+x^2+x^3+x^5, x^9))
+%2 = []
+? bestapprPade(Mod(1+x+x^2+x^3+x^5, x^10))
+%3 = (2*x^4 + x^3 - x - 1)/(-x^5 + x^3 + x^2 - 1)
+ at eprog\noindent
+The function applies recursively to components of complex objects
+(polynomials, vectors, \dots). If rational reconstruction fails for even a
+single entry, return $[]$.
+
+The library syntax is \fun{GEN}{bestapprPade}{GEN x, long B}.
+
+\subsec{bezout$(x,y)$}\kbdsidx{bezout}\label{se:bezout}
+Deprecated alias for \kbd{gcdext}
+
+The library syntax is \fun{GEN}{gcdext0}{GEN x, GEN y}.
+
+\subsec{bigomega$(x)$}\kbdsidx{bigomega}\label{se:bigomega}
+Number of prime divisors of the integer $|x|$ counted with
+multiplicity:
+\bprog
+? factor(392)
+%1 =
+[2 3]
+
+[7 2]
+
+? bigomega(392)
+%2 = 5;  \\ = 3+2
+? omega(392)
+%3 = 2;  \\ without multiplicity
+ at eprog
+
+The library syntax is \fun{long}{bigomega}{GEN x}.
+
+\subsec{binomial$(x,y)$}\kbdsidx{binomial}\label{se:binomial}
+\idx{binomial coefficient} $\binom{x}{y}$.
+Here $y$ must be an integer, but $x$ can be any PARI object.
+
+The library syntax is \fun{GEN}{binomial}{GEN x, long y}.
+The function
+\fun{GEN}{binomialuu}{ulong n, ulong k} is also available, and so is
+\fun{GEN}{vecbinome}{long n}, which returns a vector $v$
+with $n+1$ components such that $v[k+1] = \kbd{binomial}(n,k)$ for $k$ from
+$0$ up to $n$.
+
+\subsec{chinese$(x,\{y\})$}\kbdsidx{chinese}\label{se:chinese}
+If $x$ and $y$ are both intmods or both polmods, creates (with the same
+type) a $z$ in the same residue class as $x$ and in the same residue class as
+$y$, if it is possible.
+\bprog
+? chinese(Mod(1,2), Mod(2,3))
+%1 = Mod(5, 6)
+? chinese(Mod(x,x^2-1), Mod(x+1,x^2+1))
+%2 = Mod(-1/2*x^2 + x + 1/2, x^4 - 1)
+ at eprog\noindent
+This function also allows vector and matrix arguments, in which case the
+operation is recursively applied to each component of the vector or matrix.
+\bprog
+? chinese([Mod(1,2),Mod(1,3)], [Mod(1,5),Mod(2,7)])
+%3 = [Mod(1, 10), Mod(16, 21)]
+ at eprog\noindent
+For polynomial arguments in the same variable, the function is applied to each
+coefficient; if the polynomials have different degrees, the high degree terms
+are copied verbatim in the result, as if the missing high degree terms in the
+polynomial of lowest degree had been \kbd{Mod(0,1)}. Since the latter
+behavior is usually \emph{not} the desired one, we propose to convert the
+polynomials to vectors of the same length first:
+\bprog
+ ? P = x+1; Q = x^2+2*x+1;
+ ? chinese(P*Mod(1,2), Q*Mod(1,3))
+ %4 = Mod(1, 3)*x^2 + Mod(5, 6)*x + Mod(3, 6)
+ ? chinese(Vec(P,3)*Mod(1,2), Vec(Q,3)*Mod(1,3))
+ %5 = [Mod(1, 6), Mod(5, 6), Mod(4, 6)]
+ ? Pol(%)
+ %6 = Mod(1, 6)*x^2 + Mod(5, 6)*x + Mod(4, 6)
+ at eprog
+
+If $y$ is omitted, and $x$ is a vector, \kbd{chinese} is applied recursively
+to the components of $x$, yielding a residue belonging to the same class as all
+components of $x$.
+
+Finally $\kbd{chinese}(x,x) = x$ regardless of the type of $x$; this allows
+vector arguments to contain other data, so long as they are identical in both
+vectors.
+
+The library syntax is \fun{GEN}{chinese}{GEN x, GEN y = NULL}.
+\fun{GEN}{chinese1}{GEN x} is also available.
+
+\subsec{content$(x)$}\kbdsidx{content}\label{se:content}
+Computes the gcd of all the coefficients of $x$,
+when this gcd makes sense. This is the natural definition
+if $x$ is a polynomial (and by extension a power series) or a
+vector/matrix. This is in general a weaker notion than the \emph{ideal}
+generated by the coefficients:
+\bprog
+? content(2*x+y)
+%1 = 1            \\ = gcd(2,y) over Q[y]
+ at eprog
+
+If $x$ is a scalar, this simply returns the absolute value of $x$ if $x$ is
+rational (\typ{INT} or \typ{FRAC}), and either $1$ (inexact input) or $x$
+(exact input) otherwise; the result should be identical to \kbd{gcd(x, 0)}.
+
+The content of a rational function is the ratio of the contents of the
+numerator and the denominator. In recursive structures, if a
+matrix or vector \emph{coefficient} $x$ appears, the gcd is taken
+not with $x$, but with its content:
+\bprog
+? content([ [2], 4*matid(3) ])
+%1 = 2
+ at eprog
+
+The library syntax is \fun{GEN}{content}{GEN x}.
+
+\subsec{contfrac$(x,\{b\},\{\var{nmax}\})$}\kbdsidx{contfrac}\label{se:contfrac}
+Returns the row vector whose components are the partial quotients of the
+\idx{continued fraction} expansion of $x$. In other words, a result
+$[a_0,\dots,a_n]$ means that $x \approx a_0+1/(a_1+\dots+1/a_n)$. The
+output is normalized so that $a_n \neq 1$ (unless we also have $n = 0$).
+
+The number of partial quotients $n+1$ is limited by \kbd{nmax}. If
+\kbd{nmax} is omitted, the expansion stops at the last significant partial
+quotient.
+\bprog
+? \p19
+  realprecision = 19 significant digits
+? contfrac(Pi)
+%1 = [3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2]
+? contfrac(Pi,, 3)  \\ n = 2
+%2 = [3, 7, 15]
+ at eprog\noindent
+$x$ can also be a rational function or a power series.
+
+If a vector $b$ is supplied, the numerators are equal to the coefficients
+of $b$, instead of all equal to $1$ as above; more precisely, $x \approx
+(1/b_0)(a_0+b_1/(a_1+\dots+b_n/a_n))$; for a numerical continued fraction
+($x$ real), the $a_i$ are integers, as large as possible; if $x$ is a
+rational function, they are polynomials with $\deg a_i = \deg b_i + 1$.
+The length of the result is then equal to the length of $b$, unless the next
+partial quotient cannot be reliably computed, in which case the expansion
+stops. This happens when a partial remainder is equal to zero (or too small
+compared to the available significant digits for $x$ a \typ{REAL}).
+
+A direct implementation of the numerical continued fraction
+\kbd{contfrac(x,b)} described above would be
+\bprog
+\\ "greedy" generalized continued fraction
+cf(x, b) =
+{ my( a= vector(#b), t );
+
+  x *= b[1];
+  for (i = 1, #b,
+    a[i] = floor(x);
+    t = x - a[i]; if (!t || i == #b, break);
+    x = b[i+1] / t;
+  ); a;
+}
+ at eprog\noindent There is some degree of freedom when choosing the $a_i$; the
+program above can easily be modified to derive variants of the standard
+algorithm. In the same vein, although no builtin
+function implements the related \idx{Engel expansion} (a special kind of
+\idx{Egyptian fraction} decomposition: $x = 1/a_1 + 1/(a_1a_2) + \dots$ ),
+it can be obtained as follows:
+\bprog
+\\ n terms of the Engel expansion of x
+engel(x, n = 10) =
+{ my( u = x, a = vector(n) );
+  for (k = 1, n,
+    a[k] = ceil(1/u);
+    u = u*a[k] - 1;
+    if (!u, break);
+  ); a
+}
+ at eprog
+
+\misctitle{Obsolete hack} (don't use this): If $b$ is an integer, \var{nmax}
+is ignored and the command is understood as \kbd{contfrac($x,, b$)}.
+
+The library syntax is \fun{GEN}{contfrac0}{GEN x, GEN b = NULL, long nmax}.
+Also available are \fun{GEN}{gboundcf}{GEN x, long nmax},
+\fun{GEN}{gcf}{GEN x} and \fun{GEN}{gcf2}{GEN b, GEN x}.
+
+\subsec{contfracpnqn$(x, \{n=-1\})$}\kbdsidx{contfracpnqn}\label{se:contfracpnqn}
+When $x$ is a vector or a one-row matrix, $x$
+is considered as the list of partial quotients $[a_0,a_1,\dots,a_n]$ of a
+rational number, and the result is the 2 by 2 matrix
+$[p_n,p_{n-1};q_n,q_{n-1}]$ in the standard notation of continued fractions,
+so $p_n/q_n=a_0+1/(a_1+\dots+1/a_n)$. If $x$ is a matrix with two rows
+$[b_0,b_1,\dots,b_n]$ and $[a_0,a_1,\dots,a_n]$, this is then considered as a
+generalized continued fraction and we have similarly
+$p_n/q_n=(1/b_0)(a_0+b_1/(a_1+\dots+b_n/a_n))$. Note that in this case one
+usually has $b_0=1$.
+
+If $n \geq 0$ is present, returns all convergents from $p_0/q_0$ up to
+$p_n/q_n$. (All convergents if $x$ is too small to compute the $n+1$
+requested convergents.)
+\bprog
+? a=contfrac(Pi,20)
+%1 = [3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2]
+? contfracpnqn(a,3)
+%2 =
+[3 22 333 355]
+
+[1  7 106 113]
+
+? contfracpnqn(a,7)
+%3 =
+[3 22 333 355 103993 104348 208341 312689]
+
+[1  7 106 113  33102  33215  66317  99532]
+ at eprog
+
+The library syntax is \fun{GEN}{contfracpnqn}{GEN x, long n}.
+also available is \fun{GEN}{pnqn}{GEN x} for $n = -1$.
+
+\subsec{core$(n,\{\fl=0\})$}\kbdsidx{core}\label{se:core}
+If $n$ is an integer written as
+$n=df^2$ with $d$ squarefree, returns $d$. If $\fl$ is non-zero,
+returns the two-element row vector $[d,f]$. By convention, we write $0 = 0
+\times 1^2$, so \kbd{core(0, 1)} returns $[0,1]$.
+
+The library syntax is \fun{GEN}{core0}{GEN n, long flag}.
+Also available are \fun{GEN}{core}{GEN n} ($\fl = 0$) and
+\fun{GEN}{core2}{GEN n} ($\fl = 1$)
+
+\subsec{coredisc$(n,\{\fl=0\})$}\kbdsidx{coredisc}\label{se:coredisc}
+A \emph{fundamental discriminant} is an integer of the form $t\equiv 1
+\mod 4$ or $4t \equiv 8,12 \mod 16$, with $t$ squarefree (i.e.~$1$ or the
+discriminant of a quadratic number field). Given a non-zero integer
+$n$, this routine returns the (unique) fundamental discriminant $d$
+such that $n=df^2$, $f$ a positive rational number. If $\fl$ is non-zero,
+returns the two-element row vector $[d,f]$. If $n$ is congruent to
+0 or 1 modulo 4, $f$ is an integer, and a half-integer otherwise.
+
+By convention, \kbd{coredisc(0, 1))} returns $[0,1]$.
+
+Note that \tet{quaddisc}$(n)$ returns the same value as \kbd{coredisc}$(n)$,
+and also works with rational inputs $n\in\Q^*$.
+
+The library syntax is \fun{GEN}{coredisc0}{GEN n, long flag}.
+Also available are \fun{GEN}{coredisc}{GEN n} ($\fl = 0$) and
+\fun{GEN}{coredisc2}{GEN n} ($\fl = 1$)
+
+\subsec{dirdiv$(x,y)$}\kbdsidx{dirdiv}\label{se:dirdiv}
+$x$ and $y$ being vectors of perhaps different
+lengths but with $y[1]\neq 0$ considered as \idx{Dirichlet series}, computes
+the quotient of $x$ by $y$, again as a vector.
+
+The library syntax is \fun{GEN}{dirdiv}{GEN x, GEN y}.
+
+\subsec{direuler$(p=a,b,\var{expr},\{c\})$}\kbdsidx{direuler}\label{se:direuler}
+Computes the \idx{Dirichlet series} associated to the
+\idx{Euler product} of expression \var{expr} as $p$ ranges through the primes
+from $a$
+to $b$. \var{expr} must be a polynomial or rational function in another
+variable than $p$ (say $X$) and $\var{expr}(X)$ is understood as the local
+factor $\var{expr}(p^{-s})$.
+
+The series is output as a vector of coefficients. If $c$ is present, output
+only the first $c$ coefficients in the series. The following command computes
+the \teb{sigma} function, associated to $\zeta(s)\zeta(s-1)$:
+\bprog
+? direuler(p=2, 10, 1/((1-X)*(1-p*X)))
+%1 = [1, 3, 4, 7, 6, 12, 8, 15, 13, 18]
+ at eprog
+
+\synt{direuler}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b}
+
+\subsec{dirmul$(x,y)$}\kbdsidx{dirmul}\label{se:dirmul}
+$x$ and $y$ being vectors of perhaps different lengths representing
+the \idx{Dirichlet series} $\sum_n x_n n^{-s}$ and $\sum_n y_n n^{-s}$,
+computes the product of $x$ by $y$, again as a vector.
+\bprog
+? dirmul(vector(10,n,1), vector(10,n,moebius(n)))
+%1 = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+ at eprog\noindent
+The product
+length is the minimum of $\kbd{\#}x\kbd{*}v(y)$ and $\kbd{\#}y\kbd{*}v(x)$,
+where $v(x)$ is the index of the first non-zero coefficient.
+\bprog
+? dirmul([0,1], [0,1]);
+%2 = [0, 0, 0, 1]
+ at eprog
+
+The library syntax is \fun{GEN}{dirmul}{GEN x, GEN y}.
+
+\subsec{divisors$(x)$}\kbdsidx{divisors}\label{se:divisors}
+Creates a row vector whose components are the
+divisors of $x$. The factorization of $x$ (as output by \tet{factor}) can
+be used instead.
+
+By definition, these divisors are the products of the irreducible
+factors of $n$, as produced by \kbd{factor(n)}, raised to appropriate
+powers (no negative exponent may occur in the factorization). If $n$ is
+an integer, they are the positive divisors, in increasing order.
+
+The library syntax is \fun{GEN}{divisors}{GEN x}.
+
+\subsec{eulerphi$(x)$}\kbdsidx{eulerphi}\label{se:eulerphi}
+Euler's $\phi$ (totient)\sidx{Euler totient function} function of the
+integer $|x|$, in other words $|(\Z/x\Z)^*|$.
+\bprog
+? eulerphi(40)
+%1 = 16
+ at eprog\noindent
+According to this definition we let $\phi(0) := 2$, since $\Z^* = \{-1,1\}$;
+this is consistent with \kbd{znstar(0)}: we have \kbd{znstar$(n)$.no =
+eulerphi(n)} for all $n\in\Z$.
+
+The library syntax is \fun{GEN}{eulerphi}{GEN x}.
+
+\subsec{factor$(x,\{\var{lim}\})$}\kbdsidx{factor}\label{se:factor}
+General factorization function, where $x$ is a
+rational (including integers), a complex number with rational
+real and imaginary parts, or a rational function (including polynomials).
+The result is a two-column matrix: the first contains the irreducibles
+dividing $x$ (rational or Gaussian primes, irreducible polynomials),
+and the second the exponents. By convention, $0$ is factored as $0^1$.
+
+\misctitle{$\Q$ and $\Q(i)$}
+See \tet{factorint} for more information about the algorithms used.
+The rational or Gaussian primes are in fact \var{pseudoprimes}
+(see \kbd{ispseudoprime}), a priori not rigorously proven primes. In fact,
+any factor which is $\leq 2^{64}$ (whose norm is $\leq 2^{64}$ for an
+irrational Gaussian prime) is a genuine prime. Use \kbd{isprime} to prove
+primality of other factors, as in
+\bprog
+? fa = factor(2^2^7 + 1)
+%1 =
+[59649589127497217 1]
+
+[5704689200685129054721 1]
+
+? isprime( fa[,1] )
+%2 = [1, 1]~   \\ both entries are proven primes
+ at eprog\noindent
+Another possibility is to set the global default \tet{factor_proven}, which
+will perform a rigorous primality proof for each pseudoprime factor.
+
+A \typ{INT} argument \var{lim} can be added, meaning that we look only for
+prime factors $p < \var{lim}$. The limit \var{lim} must be non-negative.
+In this case, all but the last factor are proven primes, but the remaining
+factor may actually be a proven composite! If the remaining factor is less
+than $\var{lim}^2$, then it is prime.
+\bprog
+? factor(2^2^7 +1, 10^5)
+%3 =
+[340282366920938463463374607431768211457 1]
+ at eprog\noindent
+\misctitle{Deprecated feature} Setting $\var{lim}=0$ is the same
+as setting it to $\kbd{primelimit} + 1$. Don't use this: it is unwise to
+rely on global variables when you can specify an explicit argument.
+\smallskip
+
+This routine uses trial division and perfect power tests, and should not be
+used for huge values of \var{lim} (at most $10^9$, say):
+\kbd{factorint(, 1 + 8)} will in general be faster. The latter does not
+guarantee that all small
+prime factors are found, but it also finds larger factors, and in a much more
+efficient way.
+\bprog
+? F = (2^2^7 + 1) * 1009 * 100003; factor(F, 10^5)  \\ fast, incomplete
+time = 0 ms.
+%4 =
+[1009 1]
+
+[34029257539194609161727850866999116450334371 1]
+
+? factor(F, 10^9)    \\ very slow
+time = 6,892 ms.
+%6 =
+[1009 1]
+
+[100003 1]
+
+[340282366920938463463374607431768211457 1]
+
+? factorint(F, 1+8)  \\ much faster, all small primes were found
+time = 12 ms.
+%7 =
+[1009 1]
+
+[100003 1]
+
+[340282366920938463463374607431768211457 1]
+
+? factor(F)   \\ complete factorisation
+time = 112 ms.
+%8 =
+[1009 1]
+
+[100003 1]
+
+[59649589127497217 1]
+
+[5704689200685129054721 1]
+ at eprog\noindent Over $\Q$, the prime factors are sorted in increasing order.
+
+\misctitle{Rational functions}
+The polynomials or rational functions to be factored must have scalar
+coefficients. In particular PARI does not know how to factor
+\emph{multivariate} polynomials. The following domains are currently
+supported: $\Q$, $\R$, $\C$, $\Q_p$, finite fields and number fields.
+See \tet{factormod} and \tet{factorff} for
+the algorithms used over finite fields, \tet{factornf} for the algorithms
+over number fields. Over $\Q$, \idx{van Hoeij}'s method is used, which is
+able to cope with hundreds of modular factors.
+
+The routine guesses a sensible ring over which to factor: the
+smallest ring containing all coefficients, taking into account quotient
+structures induced by \typ{INTMOD}s and \typ{POLMOD}s (e.g.~if a coefficient
+in $\Z/n\Z$ is known, all rational numbers encountered are first mapped to
+$\Z/n\Z$; different moduli will produce an error). Factoring modulo a
+non-prime number is not supported; to factor in $\Q_p$, use \typ{PADIC}
+coefficients not \typ{INTMOD} modulo $p^n$.
+\bprog
+? T = x^2+1;
+? factor(T);                         \\ over Q
+? factor(T*Mod(1,3))                 \\ over F_3
+? factor(T*ffgen(ffinit(3,2,'t))^0)  \\ over F_{3^2}
+? factor(T*Mod(Mod(1,3), t^2+t+2))   \\ over F_{3^2}, again
+? factor(T*(1 + O(3^6))              \\ over Q_3, precision 6
+? factor(T*1.)                       \\ over R, current precision
+? factor(T*(1.+0.*I))                \\ over C
+? factor(T*Mod(1, y^3-2))            \\ over Q(2^{1/3})
+ at eprog\noindent In most cases, it is clearer and simpler to call an
+explicit variant than to rely on the generic \kbd{factor} function and
+the above detection mechanism:
+\bprog
+? factormod(T, 3)           \\ over F_3
+? factorff(T, 3, t^2+t+2))  \\ over F_{3^2}
+? factorpadic(T, 3,6)       \\ over Q_3, precision 6
+? nffactor(y^3-2, T)        \\ over Q(2^{1/3})
+? polroots(T)               \\ over C
+ at eprog
+
+Note that factorization of polynomials is done up to
+multiplication by a constant. In particular, the factors of rational
+polynomials will have integer coefficients, and the content of a polynomial
+or rational function is discarded and not included in the factorization. If
+needed, you can always ask for the content explicitly:
+\bprog
+? factor(t^2 + 5/2*t + 1)
+%1 =
+[2*t + 1 1]
+
+[t + 2 1]
+
+? content(t^2 + 5/2*t + 1)
+%2 = 1/2
+ at eprog\noindent
+The irreducible factors are sorted by increasing degree.
+See also \tet{nffactor}.
+
+The library syntax is \fun{GEN}{gp_factor0}{GEN x, GEN lim = NULL}.
+This function should only be used by the \kbd{gp} interface. Use
+directly \fun{GEN}{factor}{GEN x} or \fun{GEN}{boundfact}{GEN x, ulong lim}.
+The obsolete function \fun{GEN}{factor0}{GEN x, long lim} is kept for
+backward compatibility.
+
+\subsec{factorback$(f,\{e\})$}\kbdsidx{factorback}\label{se:factorback}
+Gives back the factored object
+corresponding to a factorization. The integer $1$ corresponds to the empty
+factorization.
+
+If $e$ is present, $e$ and $f$ must be vectors of the same length ($e$ being
+integral), and the corresponding factorization is the product of the
+$f[i]^{e[i]}$.
+
+If not, and $f$ is vector, it is understood as in the preceding case with $e$
+a vector of 1s: we return the product of the $f[i]$. Finally, $f$ can be a
+regular factorization, as produced with any \kbd{factor} command. A few
+examples:
+\bprog
+? factor(12)
+%1 =
+[2 2]
+
+[3 1]
+
+? factorback(%)
+%2 = 12
+? factorback([2,3], [2,1])   \\ 2^3 * 3^1
+%3 = 12
+? factorback([5,2,3])
+%4 = 30
+ at eprog
+
+The library syntax is \fun{GEN}{factorback2}{GEN f, GEN e = NULL}.
+Also available is \fun{GEN}{factorback}{GEN f} (case $e = \kbd{NULL}$).
+
+\subsec{factorcantor$(x,p)$}\kbdsidx{factorcantor}\label{se:factorcantor}
+Factors the polynomial $x$ modulo the
+prime $p$, using distinct degree plus
+\idx{Cantor-Zassenhaus}\sidx{Zassenhaus}. The coefficients of $x$ must be
+operation-compatible with $\Z/p\Z$. The result is a two-column matrix, the
+first column being the irreducible polynomials dividing $x$, and the second
+the exponents. If you want only the \emph{degrees} of the irreducible
+polynomials (for example for computing an $L$-function), use
+$\kbd{factormod}(x,p,1)$. Note that the \kbd{factormod} algorithm is
+usually faster than \kbd{factorcantor}.
+
+The library syntax is \fun{GEN}{factcantor}{GEN x, GEN p}.
+
+\subsec{factorff$(x,\{p\},\{a\})$}\kbdsidx{factorff}\label{se:factorff}
+Factors the polynomial $x$ in the field
+$\F_q$ defined by the irreducible polynomial $a$ over $\F_p$. The
+coefficients of $x$ must be operation-compatible with $\Z/p\Z$. The result
+is a two-column matrix: the first column contains the irreducible factors of
+$x$, and the second their exponents. If all the coefficients of $x$ are in
+$\F_p$, a much faster algorithm is applied, using the computation of
+isomorphisms between finite fields.
+
+Either $a$ or $p$ can omitted (in which case both are ignored) if x has
+\typ{FFELT} coefficients; the function then becomes identical to \kbd{factor}:
+\bprog
+? factorff(x^2 + 1, 5, y^2+3)  \\ over F_5[y]/(y^2+3) ~ F_25
+%1 =
+[Mod(Mod(1, 5), Mod(1, 5)*y^2 + Mod(3, 5))*x
+ + Mod(Mod(2, 5), Mod(1, 5)*y^2 + Mod(3, 5)) 1]
+
+[Mod(Mod(1, 5), Mod(1, 5)*y^2 + Mod(3, 5))*x
+ + Mod(Mod(3, 5), Mod(1, 5)*y^2 + Mod(3, 5)) 1]
+? t = ffgen(y^2 + Mod(3,5), 't); \\ a generator for F_25 as a t_FFELT
+? factorff(x^2 + 1)   \\ not enough information to determine the base field
+ ***   at top-level: factorff(x^2+1)
+ ***                 ^---------------
+ *** factorff: incorrect type in factorff.
+? factorff(x^2 + t^0) \\ make sure a coeff. is a t_FFELT
+%3 =
+[x + 2 1]
+
+[x + 3 1]
+? factorff(x^2 + t + 1)
+%11 =
+[x + (2*t + 1) 1]
+
+[x + (3*t + 4) 1]
+ at eprog\noindent
+Notice that the second syntax is easier to use and much more readable.
+
+The library syntax is \fun{GEN}{factorff}{GEN x, GEN p = NULL, GEN a = NULL}.
+
+\subsec{factorial$(x)$}\kbdsidx{factorial}\label{se:factorial}
+Factorial of $x$. The expression $x!$ gives a result which is an integer,
+while $\kbd{factorial}(x)$ gives a real number.
+
+The library syntax is \fun{GEN}{mpfactr}{long x, long prec}.
+\fun{GEN}{mpfact}{long x} returns $x!$ as a \typ{INT}.
+
+\subsec{factorint$(x,\{\fl=0\})$}\kbdsidx{factorint}\label{se:factorint}
+Factors the integer $n$ into a product of
+pseudoprimes (see \kbd{ispseudoprime}), using a combination of the
+\idx{Shanks SQUFOF} and \idx{Pollard Rho} method (with modifications due to
+Brent), \idx{Lenstra}'s \idx{ECM} (with modifications by Montgomery), and
+\idx{MPQS} (the latter adapted from the \idx{LiDIA} code with the kind
+permission of the LiDIA maintainers), as well as a search for pure powers.
+The output is a two-column matrix as for \kbd{factor}: the first column
+contains the ``prime'' divisors of $n$, the second one contains the
+(positive) exponents.
+
+By convention $0$ is factored as $0^1$, and $1$ as the empty factorization;
+also the divisors are by default not proven primes is they are larger than
+$2^{64}$, they only failed the BPSW compositeness test (see
+\tet{ispseudoprime}). Use \kbd{isprime} on the result if you want to
+guarantee primality or set the \tet{factor_proven} default to $1$.
+Entries of the private prime tables (see \tet{addprimes}) are also included
+as is.
+
+This gives direct access to the integer factoring engine called by most
+arithmetical functions. \fl\ is optional; its binary digits mean 1: avoid
+MPQS, 2: skip first stage ECM (we may still fall back to it later), 4: avoid
+Rho and SQUFOF, 8: don't run final ECM (as a result, a huge composite may be
+declared to be prime). Note that a (strong) probabilistic primality test is
+used; thus composites might not be detected, although no example is known.
+
+You are invited to play with the flag settings and watch the internals at
+work by using \kbd{gp}'s \tet{debug} default parameter (level 3 shows
+just the outline, 4 turns on time keeping, 5 and above show an increasing
+amount of internal details).
+
+The library syntax is \fun{GEN}{factorint}{GEN x, long flag}.
+
+\subsec{factormod$(x,p,\{\fl=0\})$}\kbdsidx{factormod}\label{se:factormod}
+Factors the polynomial $x$ modulo the prime integer $p$, using
+\idx{Berlekamp}. The coefficients of $x$ must be operation-compatible with
+$\Z/p\Z$. The result is a two-column matrix, the first column being the
+irreducible polynomials dividing $x$, and the second the exponents. If $\fl$
+is non-zero, outputs only the \emph{degrees} of the irreducible polynomials
+(for example, for computing an $L$-function). A different algorithm for
+computing the mod $p$ factorization is \kbd{factorcantor} which is sometimes
+faster.
+
+The library syntax is \fun{GEN}{factormod0}{GEN x, GEN p, long flag}.
+
+\subsec{ffgen$(q,\{v\})$}\kbdsidx{ffgen}\label{se:ffgen}
+Return a \typ{FFELT} generator for the finite field with $q$ elements;
+$q = p^f$ must be a prime power. This functions computes an irreducible
+monic polynomial $P\in\F_p[X]$ of degree~$f$ (via \tet{ffinit}) and
+returns $g = X \pmod{P(X)}$. If \kbd{v} is given, the variable name is used
+to display $g$, else the variable $x$ is used.
+\bprog
+? g = ffgen(8, 't);
+? g.mod
+%2 = t^3 + t^2 + 1
+? g.p
+%3 = 2
+? g.f
+%4 = 3
+? ffgen(6)
+ ***   at top-level: ffgen(6)
+ ***                 ^--------
+ *** ffgen: not a prime number in ffgen: 6.
+ at eprog\noindent Alternative syntax: instead of a prime power $q$, one may
+input directly the polynomial $P$ (monic, irreducible, with \typ{INTMOD}
+coefficients), and the function returns the generator $g = X \pmod{P(X)}$,
+inferring $p$ from the coefficients of $P$. If \kbd{v} is given, the
+variable name is used to display $g$, else the variable of the polynomial
+$P$ is used. If $P$ is not irreducible, we create an invalid object and
+behaviour of functions dealing with the resulting \typ{FFELT}
+is undefined; in fact, it is much more costly to test $P$ for
+irreducibility than it would be to produce it via \kbd{ffinit}.
+
+The library syntax is \fun{GEN}{ffgen}{GEN q, long v = -1}, where \kbd{v} is a variable number.
+
+To create a generator for a prime finite field, the function
+\fun{GEN}{p_to_GEN}{GEN p, long v} returns \kbd{1+ffgen(x*Mod(1,p),v)}.
+
+\subsec{ffinit$(p,n,\{v='x\})$}\kbdsidx{ffinit}\label{se:ffinit}
+Computes a monic polynomial of degree $n$ which is irreducible over
+ $\F_p$, where $p$ is assumed to be prime. This function uses a fast variant
+ of Adleman and Lenstra's algorithm.
+
+It is useful in conjunction with \tet{ffgen}; for instance if
+\kbd{P = ffinit(3,2)}, you can represent elements in $\F_{3^2}$ in term of
+\kbd{g = ffgen(P,'t)}. This can be abbreviated as
+\kbd{g = ffgen(3\pow2, 't)}, where the defining polynomial $P$ can be later
+recovered as \kbd{g.mod}.
+
+The library syntax is \fun{GEN}{ffinit}{GEN p, long n, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{fflog$(x,g,\{o\})$}\kbdsidx{fflog}\label{se:fflog}
+Discrete logarithm of the finite field element $x$ in base $g$, i.e.~
+an $e$ in $\Z$ such that $g^e = o$. If
+present, $o$ represents the multiplicative order of $g$, see
+\secref{se:DLfun}; the preferred format for
+this parameter is \kbd{[ord, factor(ord)]}, where \kbd{ord} is the
+order of $g$. It may be set as a side effect of calling \tet{ffprimroot}.
+
+If no $o$ is given, assume that $g$ is a primitive root. The result is
+undefined if $e$ does not exist. This function uses
+
+\item a combination of generic discrete log algorithms (see \tet{znlog})
+
+\item a cubic sieve index calculus algorithm for large fields of degree at
+least $5$.
+
+\item Coppersmith's algorithm for fields of characteristic at most $5$.
+
+\bprog
+? t = ffgen(ffinit(7,5));
+? o = fforder(t)
+%2 = 5602   \\@com \emph{not} a primitive root.
+? fflog(t^10,t)
+%3 = 10
+? fflog(t^10,t, o)
+%4 = 10
+? g = ffprimroot(t, &o);
+? o   \\ order is 16806, bundled with its factorization matrix
+%6 = [16806, [2, 1; 3, 1; 2801, 1]]
+? fforder(g, o)
+%7 = 16806
+? fflog(g^10000, g, o)
+%8 = 10000
+ at eprog
+
+The library syntax is \fun{GEN}{fflog}{GEN x, GEN g, GEN o = NULL}.
+
+\subsec{ffnbirred$(q,n\{,\var{fl}=0\})$}\kbdsidx{ffnbirred}\label{se:ffnbirred}
+Computes the number of monic irreducible polynomials over $\F_q$ of degree exactly $n$,
+($\fl=0$ or omitted) or at most $n$ ($\fl=1$).
+
+The library syntax is \fun{GEN}{ffnbirred0}{GEN q, long n, long fl}.
+Also available are
+ \fun{GEN}{ffnbirred}{GEN q, long n} (for $\fl=0$)
+ and \fun{GEN}{ffsumnbirred}{GEN q, long n} (for $\fl=1$).
+
+\subsec{fforder$(x,\{o\})$}\kbdsidx{fforder}\label{se:fforder}
+Multiplicative order of the finite field element $x$.  If $o$ is
+present, it represents a multiple of the order of the element,
+see \secref{se:DLfun}; the preferred format for
+this parameter is \kbd{[N, factor(N)]}, where \kbd{N} is the cardinality
+of the multiplicative group of the underlying finite field.
+\bprog
+? t = ffgen(ffinit(nextprime(10^8), 5));
+? g = ffprimroot(t, &o);  \\@com o will be useful!
+? fforder(g^1000000, o)
+time = 0 ms.
+%5 = 5000001750000245000017150000600250008403
+? fforder(g^1000000)
+time = 16 ms. \\@com noticeably slower, same result of course
+%6 = 5000001750000245000017150000600250008403
+ at eprog
+
+The library syntax is \fun{GEN}{fforder}{GEN x, GEN o = NULL}.
+
+\subsec{ffprimroot$(x, \{\&o\})$}\kbdsidx{ffprimroot}\label{se:ffprimroot}
+Return a primitive root of the multiplicative
+group of the definition field of the finite field element $x$ (not necessarily
+the same as the field generated by $x$). If present, $o$ is set to
+a vector \kbd{[ord, fa]}, where \kbd{ord} is the order of the group
+and \kbd{fa} its factorisation \kbd{factor(ord)}. This last parameter is
+useful in \tet{fflog} and \tet{fforder}, see \secref{se:DLfun}.
+\bprog
+? t = ffgen(ffinit(nextprime(10^7), 5));
+? g = ffprimroot(t, &o);
+? o[1]
+%3 = 100000950003610006859006516052476098
+? o[2]
+%4 =
+[2 1]
+
+[7 2]
+
+[31 1]
+
+[41 1]
+
+[67 1]
+
+[1523 1]
+
+[10498781 1]
+
+[15992881 1]
+
+[46858913131 1]
+
+? fflog(g^1000000, g, o)
+time = 1,312 ms.
+%5 = 1000000
+ at eprog
+
+The library syntax is \fun{GEN}{ffprimroot}{GEN x, GEN *o = NULL}.
+
+\subsec{fibonacci$(x)$}\kbdsidx{fibonacci}\label{se:fibonacci}
+$x^{\text{th}}$ Fibonacci number.
+
+The library syntax is \fun{GEN}{fibo}{long x}.
+
+\subsec{gcd$(x,\{y\})$}\kbdsidx{gcd}\label{se:gcd}
+Creates the greatest common divisor of $x$ and $y$.
+If you also need the $u$ and $v$ such that $x*u + y*v = \gcd(x,y)$,
+use the \tet{bezout} function. $x$ and $y$ can have rather quite general
+types, for instance both rational numbers. If $y$ is omitted and $x$ is a
+vector, returns the $\text{gcd}$ of all components of $x$, i.e.~this is
+equivalent to \kbd{content(x)}.
+
+When $x$ and $y$ are both given and one of them is a vector/matrix type,
+the GCD is again taken recursively on each component, but in a different way.
+If $y$ is a vector, resp.~matrix, then the result has the same type as $y$,
+and components equal to \kbd{gcd(x, y[i])}, resp.~\kbd{gcd(x, y[,i])}. Else
+if $x$ is a vector/matrix the result has the same type as $x$ and an
+analogous definition. Note that for these types, \kbd{gcd} is not
+commutative.
+
+The algorithm used is a naive \idx{Euclid} except for the following inputs:
+
+\item integers: use modified right-shift binary (``plus-minus''
+variant).
+
+\item univariate polynomials with coefficients in the same number
+field (in particular rational): use modular gcd algorithm.
+
+\item general polynomials: use the \idx{subresultant algorithm} if
+coefficient explosion is likely (non modular coefficients).
+
+If $u$ and $v$ are polynomials in the same variable with \emph{inexact}
+coefficients, their gcd is defined to be scalar, so that
+\bprog
+? a = x + 0.0; gcd(a,a)
+%1 = 1
+? b = y*x + O(y); gcd(b,b)
+%2 = y
+? c = 4*x + O(2^3); gcd(c,c)
+%3 = 4
+ at eprog\noindent A good quantitative check to decide whether such a
+gcd ``should be'' non-trivial, is to use \tet{polresultant}: a value
+close to $0$ means that a small deformation of the inputs has non-trivial gcd.
+You may also use \tet{gcdext}, which does try to compute an approximate gcd
+$d$ and provides $u$, $v$ to check whether $u x + v y$ is close to $d$.
+
+The library syntax is \fun{GEN}{ggcd0}{GEN x, GEN y = NULL}.
+Also available are \fun{GEN}{ggcd}{GEN x, GEN y}, if \kbd{y} is not
+\kbd{NULL}, and \fun{GEN}{content}{GEN x}, if $\kbd{y} = \kbd{NULL}$.
+
+\subsec{gcdext$(x,y)$}\kbdsidx{gcdext}\label{se:gcdext}
+Returns $[u,v,d]$ such that $d$ is the gcd of $x,y$,
+$x*u+y*v=\gcd(x,y)$, and $u$ and $v$ minimal in a natural sense.
+The arguments must be integers or polynomials. \sidx{extended gcd}
+\sidx{Bezout relation}
+\bprog
+? [u, v, d] = gcdext(32,102)
+%1 = [16, -5, 2]
+? d
+%2 = 2
+? gcdext(x^2-x, x^2+x-2)
+%3 = [-1/2, 1/2, x - 1]
+ at eprog
+
+If $x,y$ are polynomials in the same variable and \emph{inexact}
+coefficients, then compute $u,v,d$ such that $x*u+y*v = d$, where $d$
+approximately divides both and $x$ and $y$; in particular, we do not obtain
+\kbd{gcd(x,y)} which is \emph{defined} to be a scalar in this case:
+\bprog
+? a = x + 0.0; gcd(a,a)
+%1 = 1
+
+? gcdext(a,a)
+%2 = [0, 1, x + 0.E-28]
+
+? gcdext(x-Pi, 6*x^2-zeta(2))
+%3 = [-6*x - 18.8495559, 1, 57.5726923]
+ at eprog\noindent For inexact inputs, the output is thus not well defined
+mathematically, but you obtain explicit polynomials to check whether the
+approximation is close enough for your needs.
+
+The library syntax is \fun{GEN}{gcdext0}{GEN x, GEN y}.
+
+\subsec{hilbert$(x,y,\{p\})$}\kbdsidx{hilbert}\label{se:hilbert}
+\idx{Hilbert symbol} of $x$ and $y$ modulo the prime $p$, $p=0$ meaning
+the place at infinity (the result is undefined if $p\neq 0$ is not prime).
+
+It is possible to omit $p$, in which case we take $p = 0$ if both $x$
+and $y$ are rational, or one of them is a real number. And take $p = q$
+if one of $x$, $y$ is a \typ{INTMOD} modulo $q$ or a $q$-adic. (Incompatible
+types will raise an error.)
+
+The library syntax is \fun{long}{hilbert}{GEN x, GEN y, GEN p = NULL}.
+
+\subsec{isfundamental$(x)$}\kbdsidx{isfundamental}\label{se:isfundamental}
+True (1) if $x$ is equal to 1 or to the discriminant of a quadratic
+field, false (0) otherwise.
+
+The library syntax is \fun{long}{isfundamental}{GEN x}.
+
+\subsec{ispolygonal$(x,s,\{\&N\})$}\kbdsidx{ispolygonal}\label{se:ispolygonal}
+True (1) if the integer $x$ is an s-gonal number, false (0) if not.
+The parameter $s > 2$ must be a \typ{INT}. If $N$ is given, set it to $n$
+if $x$ is the $n$-th $s$-gonal number.
+\bprog
+? ispolygonal(36, 3, &N)
+%1 = 1
+? N
+ at eprog
+
+The library syntax is \fun{long}{ispolygonal}{GEN x, GEN s, GEN *N = NULL}.
+
+\subsec{ispower$(x,\{k\},\{\&n\})$}\kbdsidx{ispower}\label{se:ispower}
+If $k$ is given, returns true (1) if $x$ is a $k$-th power, false
+(0) if not.
+
+If $k$ is omitted, only integers and fractions are allowed for $x$ and the
+function returns the maximal $k \geq 2$ such that $x = n^k$ is a perfect
+power, or 0 if no such $k$ exist; in particular \kbd{ispower(-1)},
+\kbd{ispower(0)}, and \kbd{ispower(1)} all return $0$.
+
+If a third argument $\&n$ is given and $x$ is indeed a $k$-th power, sets
+$n$ to a $k$-th root of $x$.
+
+\noindent For a \typ{FFELT} \kbd{x}, instead of omitting \kbd{k} (which is
+not allowed for this type), it may be natural to set
+\bprog
+k = (x.p ^ poldegree(x.pol) - 1) / fforder(x)
+ at eprog
+
+The library syntax is \fun{long}{ispower}{GEN x, GEN k = NULL, GEN *n = NULL}.
+Also available is
+\fun{long}{gisanypower}{GEN x, GEN *pty} ($k$ omitted).
+
+\subsec{ispowerful$(x)$}\kbdsidx{ispowerful}\label{se:ispowerful}
+True (1) if $x$ is a powerful integer, false (0) if not;
+an integer is powerful if and only if its valuation at all primes is
+greater than 1.
+\bprog
+? ispowerful(50)
+%1 = 0
+? ispowerful(100)
+%2 = 1
+? ispowerful(5^3*(10^1000+1)^2)
+%3 = 1
+ at eprog
+
+The library syntax is \fun{long}{ispowerful}{GEN x}.
+
+\subsec{isprime$(x,\{\fl=0\})$}\kbdsidx{isprime}\label{se:isprime}
+True (1) if $x$ is a prime
+number, false (0) otherwise. A prime number is a positive integer having
+exactly two distinct divisors among the natural numbers, namely 1 and
+itself.
+
+This routine proves or disproves rigorously that a number is prime, which can
+be very slow when $x$ is indeed prime and has more than $1000$ digits, say.
+Use \tet{ispseudoprime} to quickly check for compositeness. See also
+\kbd{factor}. It accepts vector/matrices arguments, and is then applied
+componentwise.
+
+If $\fl=0$, use a combination of Baillie-PSW pseudo primality test (see
+\tet{ispseudoprime}), Selfridge ``$p-1$'' test if $x-1$ is smooth enough, and
+Adleman-Pomerance-Rumely-Cohen-Lenstra (APRCL) for general $x$.
+
+If $\fl=1$, use Selfridge-Pocklington-Lehmer ``$p-1$'' test and output a
+primality certificate as follows: return
+
+\item 0 if $x$ is composite,
+
+\item 1 if $x$ is small enough that passing Baillie-PSW test guarantees
+its primality (currently $x < 2^{64}$, as checked by Jan Feitsma),
+
+\item $2$ if $x$ is a large prime whose primality could only sensibly be
+proven (given the algorithms implemented in PARI) using the APRCL test.
+
+\item Otherwise ($x$ is large and $x-1$ is smooth) output a three column
+matrix as a primality certificate. The first column contains prime
+divisors $p$ of $x-1$ (such that $\prod p^{v_p(x-1)} > x^{1/3}$), the second
+the corresponding elements $a_p$ as in Proposition~8.3.1 in GTM~138 , and the
+third the output of isprime(p,1).
+
+The algorithm fails if one of the pseudo-prime factors is not prime, which is
+exceedingly unlikely and well worth a bug report. Note that if you monitor
+\kbd{isprime} at a high enough debug level, you may see warnings about
+untested integers being declared primes. This is normal: we ask for partial
+factorisations (sufficient to prove primality if the unfactored part is not
+too large), and \kbd{factor} warns us that the cofactor hasn't been tested.
+It may or may not be tested later, and may or may not be prime. This does
+not affect the validity of the whole \kbd{isprime} procedure.
+
+If $\fl=2$, use APRCL.
+
+The library syntax is \fun{GEN}{gisprime}{GEN x, long flag}.
+
+\subsec{isprimepower$(x,\{\&n\})$}\kbdsidx{isprimepower}\label{se:isprimepower}
+If $x = p^k$ is a prime power ($p$ prime, $k > 0$), return $k$, else
+return 0. If a second argument $\&n$ is given and $x$ is indeed
+the $k$-th power of a prime $p$, sets $n$ to $p$.
+
+The library syntax is \fun{long}{isprimepower}{GEN x, GEN *n = NULL}.
+
+\subsec{ispseudoprime$(x,\{\fl\})$}\kbdsidx{ispseudoprime}\label{se:ispseudoprime}
+True (1) if $x$ is a strong pseudo
+prime (see below), false (0) otherwise. If this function returns false, $x$
+is not prime; if, on the other hand it returns true, it is only highly likely
+that $x$ is a prime number. Use \tet{isprime} (which is of course much
+slower) to prove that $x$ is indeed prime.
+The function accepts vector/matrices arguments, and is then applied
+componentwise.
+
+If $\fl = 0$, checks whether $x$ is a Baillie-Pomerance-Selfridge-Wagstaff
+pseudo prime (strong Rabin-Miller pseudo prime for base $2$, followed by
+strong Lucas test for the sequence $(P,-1)$, $P$ smallest positive integer
+such that $P^2 - 4$ is not a square mod $x$).
+
+There are no known composite numbers passing this test, although it is
+expected that infinitely many such numbers exist. In particular, all
+composites $\leq 2^{64}$ are correctly detected (checked using
+\kbd{http://www.cecm.sfu.ca/Pseudoprimes/index-2-to-64.html}).
+
+If $\fl > 0$, checks whether $x$ is a strong Miller-Rabin pseudo prime  for
+$\fl$ randomly chosen bases (with end-matching to catch square roots of $-1$).
+
+The library syntax is \fun{GEN}{gispseudoprime}{GEN x, long flag}.
+
+\subsec{issquare$(x,\{\&n\})$}\kbdsidx{issquare}\label{se:issquare}
+True (1) if $x$ is a square, false (0)
+if not. What ``being a square'' means depends on the type of $x$: all
+\typ{COMPLEX} are squares, as well as all non-negative \typ{REAL}; for
+exact types such as \typ{INT}, \typ{FRAC} and \typ{INTMOD}, squares are
+numbers of the form $s^2$ with $s$ in $\Z$, $\Q$ and $\Z/N\Z$ respectively.
+\bprog
+? issquare(3)          \\ as an integer
+%1 = 0
+? issquare(3.)         \\ as a real number
+%2 = 1
+? issquare(Mod(7, 8))  \\ in Z/8Z
+%3 = 0
+? issquare( 5 + O(13^4) )  \\ in Q_13
+%4 = 0
+ at eprog
+If $n$ is given, a square root of $x$ is put into $n$.
+\bprog
+? issquare(4, &n)
+%1 = 1
+? n
+%2 = 2
+ at eprog
+For polynomials, either we detect that the characteristic is 2 (and check
+directly odd and even-power monomials) or we assume that $2$ is invertible
+and check whether squaring the truncated power series for the square root
+yields the original input.
+
+The library syntax is \fun{long}{issquareall}{GEN x, GEN *n = NULL}.
+Also available is \fun{long}{issquare}{GEN x}. Deprecated
+GP-specific functions \fun{GEN}{gissquare}{GEN x} and
+\fun{GEN}{gissquareall}{GEN x, GEN *pt} return \kbd{gen\_0} and \kbd{gen\_1}
+instead of a boolean value.
+
+\subsec{issquarefree$(x)$}\kbdsidx{issquarefree}\label{se:issquarefree}
+True (1) if $x$ is squarefree, false (0) if not. Here $x$ can be an
+integer or a polynomial.
+
+The library syntax is \fun{long}{issquarefree}{GEN x}.
+
+\subsec{istotient$(x,\{\&N\})$}\kbdsidx{istotient}\label{se:istotient}
+True (1) if $x = \phi(n)$ for some integer $n$, false (0)
+if not.
+\bprog
+? istotient(14)
+%1 = 0
+? istotient(100)
+%2 = 0
+ at eprog
+If $N$ is given, set $N = n$ as well.
+\bprog
+? istotient(4, &n)
+%1 = 1
+? n
+%2 = 10
+ at eprog
+
+The library syntax is \fun{long}{istotient}{GEN x, GEN *N = NULL}.
+
+\subsec{kronecker$(x,y)$}\kbdsidx{kronecker}\label{se:kronecker}
+\idx{Kronecker symbol} $(x|y)$, where $x$ and $y$ must be of type integer. By
+definition, this is the extension of \idx{Legendre symbol} to $\Z \times \Z$
+by total multiplicativity in both arguments with the following special rules
+for $y = 0, -1$ or $2$:
+
+\item $(x|0) = 1$ if $|x| = 1$ and $0$ otherwise.
+
+\item $(x|-1) = 1$ if $x \geq 0$ and $-1$ otherwise.
+
+\item $(x|2) = 0$ if $x$ is even and $1$ if $x = 1,-1 \mod 8$ and $-1$
+if $x=3,-3 \mod 8$.
+
+The library syntax is \fun{long}{kronecker}{GEN x, GEN y}.
+
+\subsec{lcm$(x,\{y\})$}\kbdsidx{lcm}\label{se:lcm}
+Least common multiple of $x$ and $y$, i.e.~such
+that $\lcm(x,y)*\gcd(x,y) = \text{abs}(x*y)$. If $y$ is omitted and $x$
+is a vector, returns the $\text{lcm}$ of all components of $x$.
+
+When $x$ and $y$ are both given and one of them is a vector/matrix type,
+the LCM is again taken recursively on each component, but in a different way.
+If $y$ is a vector, resp.~matrix, then the result has the same type as $y$,
+and components equal to \kbd{lcm(x, y[i])}, resp.~\kbd{lcm(x, y[,i])}. Else
+if $x$ is a vector/matrix the result has the same type as $x$ and an
+analogous definition. Note that for these types, \kbd{lcm} is not
+commutative.
+
+Note that \kbd{lcm(v)} is quite different from
+\bprog
+l = v[1]; for (i = 1, #v, l = lcm(l, v[i]))
+ at eprog\noindent
+Indeed, \kbd{lcm(v)} is a scalar, but \kbd{l} may not be (if one of
+the \kbd{v[i]} is a vector/matrix). The computation uses a divide-conquer tree
+and should be much more efficient, especially when using the GMP
+multiprecision kernel (and more subquadratic algorithms become available):
+\bprog
+? v = vector(10^4, i, random);
+? lcm(v);
+time = 323 ms.
+? l = v[1]; for (i = 1, #v, l = lcm(l, v[i]))
+time = 833 ms.
+ at eprog
+
+The library syntax is \fun{GEN}{glcm0}{GEN x, GEN y = NULL}.
+
+\subsec{logint$(x,b,\&z)$}\kbdsidx{logint}\label{se:logint}
+Return the largest integer $e$ so that $b^e \leq x$, where the
+parameters $b > 1$ and $x > 0$ are both integers. If the parameter $z$ is
+present, set it to $b^e$.
+\bprog
+? logint(1000, 2)
+%1 = 9
+? 2^9
+%2 = 512
+? logint(1000, 2, &z)
+%3 = 9
+? z
+%4 = 512
+ at eprog\noindent The number of digits used to write $b$ in base $x$ is
+\kbd{1 + logint(x,b)}:
+\bprog
+? #digits(1000!, 10)
+%5 = 2568
+? logint(1000!, 10)
+%6 = 2567
+ at eprog\noindent This function may conveniently replace
+\bprog
+  floor( log(x) / log(b) )
+ at eprog\noindent which may not give the correct answer since PARI
+does not guarantee exact rounding.
+
+The library syntax is \fun{long}{logint0}{GEN x, GEN b, GEN *z = NULL}.
+
+\subsec{moebius$(x)$}\kbdsidx{moebius}\label{se:moebius}
+\idx{Moebius} $\mu$-function of $|x|$. $x$ must be of type integer.
+
+The library syntax is \fun{long}{moebius}{GEN x}.
+
+\subsec{nextprime$(x)$}\kbdsidx{nextprime}\label{se:nextprime}
+Finds the smallest pseudoprime (see
+\tet{ispseudoprime}) greater than or equal to $x$. $x$ can be of any real
+type. Note that if $x$ is a pseudoprime, this function returns $x$ and not
+the smallest pseudoprime strictly larger than $x$. To rigorously prove that
+the result is prime, use \kbd{isprime}.
+
+The library syntax is \fun{GEN}{nextprime}{GEN x}.
+
+\subsec{numbpart$(n)$}\kbdsidx{numbpart}\label{se:numbpart}
+Gives the number of unrestricted partitions of
+$n$, usually called $p(n)$ in the literature; in other words the number of
+nonnegative integer solutions to $a+2b+3c+\cdots=n$. $n$ must be of type
+integer and $n<10^{15}$ (with trivial values $p(n) = 0$ for $n < 0$ and
+$p(0) = 1$). The algorithm uses the Hardy-Ramanujan-Rademacher formula.
+To explicitly enumerate them, see \tet{partitions}.
+
+The library syntax is \fun{GEN}{numbpart}{GEN n}.
+
+\subsec{numdiv$(x)$}\kbdsidx{numdiv}\label{se:numdiv}
+Number of divisors of $|x|$. $x$ must be of type integer.
+
+The library syntax is \fun{GEN}{numdiv}{GEN x}.
+
+\subsec{omega$(x)$}\kbdsidx{omega}\label{se:omega}
+Number of distinct prime divisors of $|x|$. $x$ must be of type integer.
+\bprog
+? factor(392)
+%1 =
+[2 3]
+
+[7 2]
+
+? omega(392)
+%2 = 2;  \\ without multiplicity
+? bigomega(392)
+%3 = 5;  \\ = 3+2, with multiplicity
+ at eprog
+
+The library syntax is \fun{long}{omega}{GEN x}.
+
+\subsec{partitions$(k,\{a=k\},\{n=k\}))$}\kbdsidx{partitions}\label{se:partitions}
+Returns the vector of partitions of the integer $k$ as a sum of positive
+integers (parts); for $k < 0$, it returns the empty set \kbd{[]}, and for $k
+= 0$ the trivial partition (no parts). A partition is given by a
+\typ{VECSMALL}, where parts are sorted in nondecreasing order:
+\bprog
+? partitions(3)
+%1 = [Vecsmall([3]), Vecsmall([1, 2]), Vecsmall([1, 1, 1])]
+ at eprog\noindent correspond to $3$, $1+2$ and $1+1+1$. The number
+of (unrestricted) partitions of $k$ is given
+by \tet{numbpart}:
+\bprog
+? #partitions(50)
+%1 = 204226
+? numbpart(50)
+%2 = 204226
+ at eprog
+
+\noindent Optional parameters $n$ and $a$ are as follows:
+
+\item $n=\var{nmax}$ (resp. $n=[\var{nmin},\var{nmax}]$) restricts
+partitions to length less than $\var{nmax}$ (resp. length between
+$\var{nmin}$ and $nmax$), where the \emph{length} is the number of nonzero
+entries.
+
+\item $a=\var{amax}$ (resp. $a=[\var{amin},\var{amax}]$) restricts the parts
+to integers less than $\var{amax}$ (resp. between $\var{amin}$ and
+$\var{amax}$).
+\bprog
+? partitions(4, 2)  \\ parts bounded by 2
+%1 = [Vecsmall([2, 2]), Vecsmall([1, 1, 2]), Vecsmall([1, 1, 1, 1])]
+? partitions(4,, 2) \\ at most 2 parts
+%2 = [Vecsmall([4]), Vecsmall([1, 3]), Vecsmall([2, 2])]
+? partitions(4,[0,3], 2) \\ at most 2 parts
+%3 = [Vecsmall([4]), Vecsmall([1, 3]), Vecsmall([2, 2])]
+ at eprog\noindent
+By default, parts are positive and we remove zero entries unless
+$amin\leq0$, in which case $nmin$ is ignored and $X$ is of constant length
+$\var{nmax}$:
+\bprog
+? partitions(4, [0,3])  \\ parts between 0 and 3
+%1 = [Vecsmall([0, 0, 1, 3]), Vecsmall([0, 0, 2, 2]),\
+      Vecsmall([0, 1, 1, 2]), Vecsmall([1, 1, 1, 1])]
+ at eprog
+
+The library syntax is \fun{GEN}{partitions}{long k, GEN a = NULL, GEN n) = NULL}.
+
+\subsec{polrootsff$(x,\{p\},\{a\})$}\kbdsidx{polrootsff}\label{se:polrootsff}
+Returns the vector of distinct roots of the polynomial $x$ in the field
+$\F_q$ defined by the irreducible polynomial $a$ over $\F_p$. The
+coefficients of $x$ must be operation-compatible with $\Z/p\Z$.
+Either $a$ or $p$ can omitted (in which case both are ignored) if x has
+\typ{FFELT} coefficients:
+\bprog
+? polrootsff(x^2 + 1, 5, y^2+3)  \\ over F_5[y]/(y^2+3) ~ F_25
+%1 = [Mod(Mod(3, 5), Mod(1, 5)*y^2 + Mod(3, 5)),
+      Mod(Mod(2, 5), Mod(1, 5)*y^2 + Mod(3, 5))]
+? t = ffgen(y^2 + Mod(3,5), 't); \\ a generator for F_25 as a t_FFELT
+? polrootsff(x^2 + 1)   \\ not enough information to determine the base field
+ ***   at top-level: polrootsff(x^2+1)
+ ***                 ^-----------------
+ *** polrootsff: incorrect type in factorff.
+? polrootsff(x^2 + t^0) \\ make sure one coeff. is a t_FFELT
+%3 = [3, 2]
+? polrootsff(x^2 + t + 1)
+%4 = [2*t + 1, 3*t + 4]
+ at eprog\noindent
+Notice that the second syntax is easier to use and much more readable.
+
+The library syntax is \fun{GEN}{polrootsff}{GEN x, GEN p = NULL, GEN a = NULL}.
+
+\subsec{precprime$(x)$}\kbdsidx{precprime}\label{se:precprime}
+Finds the largest pseudoprime (see
+\tet{ispseudoprime}) less than or equal to $x$. $x$ can be of any real type.
+Returns 0 if $x\le1$. Note that if $x$ is a prime, this function returns $x$
+and not the largest prime strictly smaller than $x$. To rigorously prove that
+the result is prime, use \kbd{isprime}.
+
+The library syntax is \fun{GEN}{precprime}{GEN x}.
+
+\subsec{prime$(n)$}\kbdsidx{prime}\label{se:prime}
+The $n^{\text{th}}$ prime number
+\bprog
+? prime(10^9)
+%1 = 22801763489
+ at eprog\noindent Uses checkpointing and a naive $O(n)$ algorithm.
+
+The library syntax is \fun{GEN}{prime}{long n}.
+
+\subsec{primepi$(x)$}\kbdsidx{primepi}\label{se:primepi}
+The prime counting function. Returns the number of
+primes $p$, $p \leq x$.
+\bprog
+? primepi(10)
+%1 = 4;
+? primes(5)
+%2 = [2, 3, 5, 7, 11]
+? primepi(10^11)
+%3 = 4118054813
+ at eprog\noindent Uses checkpointing and a naive $O(x)$ algorithm.
+
+The library syntax is \fun{GEN}{primepi}{GEN x}.
+
+\subsec{primes$(n)$}\kbdsidx{primes}\label{se:primes}
+Creates a row vector whose components are the first $n$ prime numbers.
+(Returns the empty vector for $n \leq 0$.) A \typ{VEC} $n = [a,b]$ is also
+allowed, in which case the primes in $[a,b]$ are returned
+\bprog
+? primes(10)     \\ the first 10 primes
+%1 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+? primes([0,29])  \\ the primes up to 29
+%2 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+? primes([15,30])
+%3 = [17, 19, 23, 29]
+ at eprog
+
+The library syntax is \fun{GEN}{primes0}{GEN n}.
+
+\subsec{qfbclassno$(D,\{\fl=0\})$}\kbdsidx{qfbclassno}\label{se:qfbclassno}
+Ordinary class number of the quadratic
+order of discriminant $D$. In the present version \vers, a $O(D^{1/2})$
+algorithm is used for $D > 0$ (using Euler product and the functional
+equation) so $D$ should not be too large, say $D < 10^8$, for the time to be
+reasonable. On the other hand, for $D < 0$ one can reasonably compute
+\kbd{qfbclassno($D$)} for $|D|<10^{25}$, since the routine uses
+\idx{Shanks}'s method which is in $O(|D|^{1/4})$. For larger values of $|D|$,
+see \kbd{quadclassunit}.
+
+If $\fl=1$, compute the class number using \idx{Euler product}s and the
+functional equation. However, it is in $O(|D|^{1/2})$.
+
+\misctitle{Important warning} For $D < 0$, this function may give incorrect
+results when the class group has many cyclic factors,
+because implementing \idx{Shanks}'s method in full generality slows it down
+immensely. It is therefore strongly recommended to double-check results using
+either the version with $\fl = 1$ or the function \kbd{quadclassunit}.
+
+\misctitle{Warning} Contrary to what its name implies, this routine does not
+compute the number of classes of binary primitive forms of discriminant $D$,
+which is equal to the \emph{narrow} class number. The two notions are the same
+when $D < 0$ or the fundamental unit $\varepsilon$ has negative norm; when $D
+> 0$ and $N\varepsilon > 0$, the number of classes of forms is twice the
+ordinary class number. This is a problem which we cannot fix for backward
+compatibility reasons. Use the following routine if you are only interested
+in the number of classes of forms:
+\bprog
+QFBclassno(D) =
+qfbclassno(D) * if (D < 0 || norm(quadunit(D)) < 0, 1, 2)
+ at eprog\noindent
+Here are a few examples:
+\bprog
+? qfbclassno(400000028)
+time = 3,140 ms.
+%1 = 1
+? quadclassunit(400000028).no
+time = 20 ms. \\@com{ much faster}
+%2 = 1
+? qfbclassno(-400000028)
+time = 0 ms.
+%3 = 7253 \\@com{ correct, and fast enough}
+? quadclassunit(-400000028).no
+time = 0 ms.
+%4 = 7253
+ at eprog\noindent
+See also \kbd{qfbhclassno}.
+
+The library syntax is \fun{GEN}{qfbclassno0}{GEN D, long flag}.
+The following functions are also available:
+
+\fun{GEN}{classno}{GEN D} ($\fl = 0$)
+
+\fun{GEN}{classno2}{GEN D} ($\fl = 1$).
+
+\noindent Finally
+
+\fun{GEN}{hclassno}{GEN D} computes the class number of an imaginary
+quadratic field by counting reduced forms, an $O(|D|)$ algorithm.
+
+\subsec{qfbcompraw$(x,y)$}\kbdsidx{qfbcompraw}\label{se:qfbcompraw}
+\idx{composition} of the binary quadratic forms $x$ and $y$, without
+\idx{reduction} of the result. This is useful e.g.~to compute a generating
+element of an ideal. The result is undefined if $x$ and $y$ do not have the
+same discriminant.
+
+The library syntax is \fun{GEN}{qfbcompraw}{GEN x, GEN y}.
+
+\subsec{qfbhclassno$(x)$}\kbdsidx{qfbhclassno}\label{se:qfbhclassno}
+\idx{Hurwitz class number} of $x$, where
+$x$ is non-negative and congruent to 0 or 3 modulo 4. For $x > 5\cdot
+10^5$, we assume the GRH, and use \kbd{quadclassunit} with default
+parameters.
+
+The library syntax is \fun{GEN}{hclassno}{GEN x}.
+
+\subsec{qfbnucomp$(x,y,L)$}\kbdsidx{qfbnucomp}\label{se:qfbnucomp}
+\idx{composition} of the primitive positive
+definite binary quadratic forms $x$ and $y$ (type \typ{QFI}) using the NUCOMP
+and NUDUPL algorithms of \idx{Shanks}, \`a la Atkin. $L$ is any positive
+constant, but for optimal speed, one should take $L=|D|^{1/4}$, where $D$ is
+the common discriminant of $x$ and $y$. When $x$ and $y$ do not have the same
+discriminant, the result is undefined.
+
+The current implementation is straightforward and in general \emph{slower}
+than the generic routine (since the latter takes advantage of asymptotically
+fast operations and careful optimizations).
+
+The library syntax is \fun{GEN}{nucomp}{GEN x, GEN y, GEN L}.
+Also available is \fun{GEN}{nudupl}{GEN x, GEN L} when $x=y$.
+
+\subsec{qfbnupow$(x,n)$}\kbdsidx{qfbnupow}\label{se:qfbnupow}
+$n$-th power of the primitive positive definite
+binary quadratic form $x$ using \idx{Shanks}'s NUCOMP and NUDUPL algorithms
+(see \kbd{qfbnucomp}, in particular the final warning).
+
+The library syntax is \fun{GEN}{nupow}{GEN x, GEN n}.
+
+\subsec{qfbpowraw$(x,n)$}\kbdsidx{qfbpowraw}\label{se:qfbpowraw}
+$n$-th power of the binary quadratic form
+$x$, computed without doing any \idx{reduction} (i.e.~using \kbd{qfbcompraw}).
+Here $n$ must be non-negative and $n<2^{31}$.
+
+The library syntax is \fun{GEN}{qfbpowraw}{GEN x, long n}.
+
+\subsec{qfbprimeform$(x,p)$}\kbdsidx{qfbprimeform}\label{se:qfbprimeform}
+Prime binary quadratic form of discriminant
+$x$ whose first coefficient is $p$, where $|p|$ is a prime number.
+By abuse of notation,
+$p = \pm 1$ is also valid and returns the unit form. Returns an
+error if $x$ is not a quadratic residue mod $p$, or if $x < 0$ and $p < 0$.
+(Negative definite \typ{QFI} are not implemented.) In the case where $x>0$,
+the ``distance'' component of the form is set equal to zero according to the
+current precision.
+
+The library syntax is \fun{GEN}{primeform}{GEN x, GEN p, long prec}.
+
+\subsec{qfbred$(x,\{\fl=0\},\{d\},\{\var{isd}\},\{\var{sd}\})$}\kbdsidx{qfbred}\label{se:qfbred}
+Reduces the binary quadratic form $x$ (updating Shanks's distance function
+if $x$ is indefinite). The binary digits of $\fl$ are toggles meaning
+
+\quad 1: perform a single \idx{reduction} step
+
+\quad 2: don't update \idx{Shanks}'s distance
+
+The arguments $d$, \var{isd}, \var{sd}, if present, supply the values of the
+discriminant, $\floor{\sqrt{d}}$, and $\sqrt{d}$ respectively
+(no checking is done of these facts). If $d<0$ these values are useless,
+and all references to Shanks's distance are irrelevant.
+
+The library syntax is \fun{GEN}{qfbred0}{GEN x, long flag, GEN d = NULL, GEN isd = NULL, GEN sd = NULL}.
+Also available are
+
+\fun{GEN}{redimag}{GEN x} (for definite $x$),
+
+\noindent and for indefinite forms:
+
+\fun{GEN}{redreal}{GEN x}
+
+\fun{GEN}{rhoreal}{GEN x} (= \kbd{qfbred(x,1)}),
+
+\fun{GEN}{redrealnod}{GEN x, GEN isd} (= \kbd{qfbred(x,2,,isd)}),
+
+\fun{GEN}{rhorealnod}{GEN x, GEN isd} (= \kbd{qfbred(x,3,,isd)}).
+
+\subsec{qfbsolve$(Q,p)$}\kbdsidx{qfbsolve}\label{se:qfbsolve}
+Solve the equation $Q(x,y)=p$ over the integers,
+where $Q$ is a binary quadratic form and $p$ a prime number.
+
+Return $[x,y]$ as a two-components vector, or zero if there is no solution.
+Note that this function returns only one solution and not all the solutions.
+
+Let $D = \disc Q$. The algorithm used runs in probabilistic polynomial time
+in $p$ (through the computation of a square root of $D$ modulo $p$); it is
+polynomial time in $D$ if $Q$ is imaginary, but exponential time if $Q$ is
+real (through the computation of a full cycle of reduced forms). In the
+latter case, note that \tet{bnfisprincipal} provides a solution in heuristic
+subexponential time in $D$ assuming the GRH.
+
+The library syntax is \fun{GEN}{qfbsolve}{GEN Q, GEN p}.
+
+\subsec{quadclassunit$(D,\{\fl=0\},\{\var{tech}=[\,]\})$}\kbdsidx{quadclassunit}\label{se:quadclassunit}
+\idx{Buchmann-McCurley}'s sub-exponential algorithm for computing the
+class group of a quadratic order of discriminant $D$.
+
+This function should be used instead of \tet{qfbclassno} or \tet{quadregula}
+when $D<-10^{25}$, $D>10^{10}$, or when the \emph{structure} is wanted. It
+is a special case of \tet{bnfinit}, which is slower, but more robust.
+
+The result is a vector $v$ whose components should be accessed using member
+functions:
+
+\item \kbd{$v$.no}: the class number
+
+\item \kbd{$v$.cyc}: a vector giving the structure of the class group as a
+product of cyclic groups;
+
+\item \kbd{$v$.gen}: a vector giving generators of those cyclic groups (as
+binary quadratic forms).
+
+\item \kbd{$v$.reg}: the regulator, computed to an accuracy which is the
+maximum of an internal accuracy determined by the program and the current
+default (note that once the regulator is known to a small accuracy it is
+trivial to compute it to very high accuracy, see the tutorial).
+
+The $\fl$ is obsolete and should be left alone. In older versions,
+it supposedly computed the narrow class group when $D>0$, but this did not
+work at all; use the general function \tet{bnfnarrow}.
+
+Optional parameter \var{tech} is a row vector of the form $[c_1, c_2]$,
+where $c_1 \leq c_2$ are non-negative real numbers which control the execution
+time and the stack size, see \ref{se:GRHbnf}. The parameter is used as a
+threshold to balance the relation finding phase against the final linear
+algebra. Increasing the default $c_1$ means that relations are easier
+to find, but more relations are needed and the linear algebra will be
+harder. The default value for $c_1$ is $0$ and means that it is taken equal
+to $c_2$. The parameter $c_2$ is mostly obsolete and should not be changed,
+but we still document it for completeness: we compute a tentative class
+group by generators and relations using a factorbase of prime ideals
+$\leq c_1 (\log |D|)^2$, then prove that ideals of norm
+$\leq c_2 (\log |D|)^2$ do
+not generate a larger group. By default an optimal $c_2$ is chosen, so that
+the result is provably correct under the GRH --- a famous result of Bach
+states that $c_2 = 6$ is fine, but it is possible to improve on this
+algorithmically. You may provide a smaller $c_2$, it will be ignored
+(we use the provably correct
+one); you may provide a larger $c_2$ than the default value, which results
+in longer computing times for equally correct outputs (under GRH).
+
+The library syntax is \fun{GEN}{quadclassunit0}{GEN D, long flag, GEN tech = NULL, long prec}.
+If you really need to experiment with the \var{tech} parameter, it is
+usually more convenient to use
+\fun{GEN}{Buchquad}{GEN D, double c1, double c2, long prec}
+
+\subsec{quaddisc$(x)$}\kbdsidx{quaddisc}\label{se:quaddisc}
+Discriminant of the quadratic field $\Q(\sqrt{x})$, where $x\in\Q$.
+
+The library syntax is \fun{GEN}{quaddisc}{GEN x}.
+
+\subsec{quadgen$(D)$}\kbdsidx{quadgen}\label{se:quadgen}
+Creates the quadratic
+number\sidx{omega} $\omega=(a+\sqrt{D})/2$ where $a=0$ if $D\equiv0\mod4$,
+$a=1$ if $D\equiv1\mod4$, so that $(1,\omega)$ is an integral basis for the
+quadratic order of discriminant $D$. $D$ must be an integer congruent to 0 or
+1 modulo 4, which is not a square.
+
+The library syntax is \fun{GEN}{quadgen}{GEN D}.
+
+\subsec{quadhilbert$(D)$}\kbdsidx{quadhilbert}\label{se:quadhilbert}
+Relative equation defining the
+\idx{Hilbert class field} of the quadratic field of discriminant $D$.
+
+If $D < 0$, uses complex multiplication (\idx{Schertz}'s variant).
+
+If $D > 0$ \idx{Stark units} are used and (in rare cases) a
+vector of extensions may be returned whose compositum is the requested class
+field. See \kbd{bnrstark} for details.
+
+The library syntax is \fun{GEN}{quadhilbert}{GEN D, long prec}.
+
+\subsec{quadpoly$(D,\{v='x\})$}\kbdsidx{quadpoly}\label{se:quadpoly}
+Creates the ``canonical'' quadratic
+polynomial (in the variable $v$) corresponding to the discriminant $D$,
+i.e.~the minimal polynomial of $\kbd{quadgen}(D)$. $D$ must be an integer
+congruent to 0 or 1 modulo 4, which is not a square.
+
+The library syntax is \fun{GEN}{quadpoly0}{GEN D, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{quadray$(D,f)$}\kbdsidx{quadray}\label{se:quadray}
+Relative equation for the ray
+class field of conductor $f$ for the quadratic field of discriminant $D$
+using analytic methods. A \kbd{bnf} for $x^2 - D$ is also accepted in place
+of $D$.
+
+For $D < 0$, uses the $\sigma$ function and Schertz's method.
+
+For $D>0$, uses Stark's conjecture, and a vector of relative equations may be
+returned. See \tet{bnrstark} for more details.
+
+The library syntax is \fun{GEN}{quadray}{GEN D, GEN f, long prec}.
+
+\subsec{quadregulator$(x)$}\kbdsidx{quadregulator}\label{se:quadregulator}
+Regulator of the quadratic field of positive discriminant $x$. Returns
+an error if $x$ is not a discriminant (fundamental or not) or if $x$ is a
+square. See also \kbd{quadclassunit} if $x$ is large.
+
+The library syntax is \fun{GEN}{quadregulator}{GEN x, long prec}.
+
+\subsec{quadunit$(D)$}\kbdsidx{quadunit}\label{se:quadunit}
+Fundamental unit\sidx{fundamental units} of the
+real quadratic field $\Q(\sqrt D)$ where  $D$ is the positive discriminant
+of the field. If $D$ is not a fundamental discriminant, this probably gives
+the fundamental unit of the corresponding order. $D$ must be an integer
+congruent to 0 or 1 modulo 4, which is not a square; the result is a
+quadratic number (see \secref{se:quadgen}).
+
+The library syntax is \fun{GEN}{quadunit}{GEN D}.
+
+\subsec{randomprime$(\{N = 2^{{31}}\})$}\kbdsidx{randomprime}\label{se:randomprime}
+Returns a strong pseudo prime (see \tet{ispseudoprime}) in $[2,N-1]$.
+A \typ{VEC} $N = [a,b]$ is also allowed, with $a \leq b$ in which case a
+pseudo prime $a \leq p \leq b$ is returned; if no prime exists in the
+interval, the function will run into an infinite loop. If the upper bound
+is less than $2^{64}$ the pseudo prime returned is a proven prime.
+
+The library syntax is \fun{GEN}{randomprime}{GEN N  = NULL}.
+
+\subsec{removeprimes$(\{x=[\,]\})$}\kbdsidx{removeprimes}\label{se:removeprimes}
+Removes the primes listed in $x$ from
+the prime number table. In particular \kbd{removeprimes(addprimes())} empties
+the extra prime table. $x$ can also be a single integer. List the current
+extra primes if $x$ is omitted.
+
+The library syntax is \fun{GEN}{removeprimes}{GEN x = NULL}.
+
+\subsec{sigma$(x,\{k=1\})$}\kbdsidx{sigma}\label{se:sigma}
+Sum of the $k^{\text{th}}$ powers of the positive divisors of $|x|$. $x$
+and $k$ must be of type integer.
+
+The library syntax is \fun{GEN}{sumdivk}{GEN x, long k}.
+Also available is \fun{GEN}{sumdiv}{GEN n}, for $k = 1$.
+
+\subsec{sqrtint$(x)$}\kbdsidx{sqrtint}\label{se:sqrtint}
+Returns the integer square root of $x$, i.e. the largest integer $y$
+such that $y^2 \leq x$, where $x$ a non-negative integer.
+\bprog
+? N = 120938191237; sqrtint(N)
+%1 = 347761
+? sqrt(N)
+%2 = 347761.68741970412747602130964414095216
+ at eprog
+
+The library syntax is \fun{GEN}{sqrtint}{GEN x}.
+
+\subsec{sqrtnint$(x,n)$}\kbdsidx{sqrtnint}\label{se:sqrtnint}
+Returns the integer $n$-th root of $x$, i.e. the largest integer $y$ such
+that $y^n \leq x$, where $x$ is a non-negative integer.
+\bprog
+? N = 120938191237; sqrtnint(N, 5)
+%1 = 164
+? N^(1/5)
+%2 = 164.63140849829660842958614676939677391
+ at eprog\noindent The special case $n = 2$ is \tet{sqrtint}
+
+The library syntax is \fun{GEN}{sqrtnint}{GEN x, long n}.
+
+\subsec{stirling$(n,k,\{\fl=1\})$}\kbdsidx{stirling}\label{se:stirling}
+\idx{Stirling number} of the first kind $s(n,k)$ ($\fl=1$, default) or
+of the second kind $S(n,k)$ (\fl=2), where $n$, $k$ are non-negative
+integers. The former is $(-1)^{n-k}$ times the
+number of permutations of $n$ symbols with exactly $k$ cycles; the latter is
+the number of ways of partitioning a set of $n$ elements into $k$ non-empty
+subsets. Note that if all $s(n,k)$ are needed, it is much faster to compute
+$$\sum_k s(n,k) x^k = x(x-1)\dots(x-n+1).$$
+Similarly, if a large number of $S(n,k)$ are needed for the same $k$,
+one should use
+$$\sum_n S(n,k) x^n = \dfrac{x^k}{(1-x)\dots(1-kx)}.$$
+(Should be implemented using a divide and conquer product.) Here are
+simple variants for $n$ fixed:
+\bprog
+/* list of s(n,k), k = 1..n */
+vecstirling(n) = Vec( factorback(vector(n-1,i,1-i*'x)) )
+
+/* list of S(n,k), k = 1..n */
+vecstirling2(n) =
+{ my(Q = x^(n-1), t);
+  vector(n, i, t = divrem(Q, x-i); Q=t[1]; t[2]);
+}
+ at eprog
+
+The library syntax is \fun{GEN}{stirling}{long n, long k, long flag}.
+Also available are \fun{GEN}{stirling1}{ulong n, ulong k}
+($\fl=1$) and \fun{GEN}{stirling2}{ulong n, ulong k} ($\fl=2$).
+
+\subsec{sumdedekind$(h,k)$}\kbdsidx{sumdedekind}\label{se:sumdedekind}
+Returns the \idx{Dedekind sum} associated to the integers $h$ and $k$,
+ corresponding to a fast implementation of
+ \bprog
+  s(h,k) = sum(n = 1, k-1, (n/k)*(frac(h*n/k) - 1/2))
+ @eprog
+
+The library syntax is \fun{GEN}{sumdedekind}{GEN h, GEN k}.
+
+\subsec{sumdigits$(n)$}\kbdsidx{sumdigits}\label{se:sumdigits}
+Sum of (decimal) digits in the integer $n$.
+\bprog
+? sumdigits(123456789)
+%1 = 45
+ at eprog\noindent Other bases that 10 are not supported. Note that the sum of
+bits in $n$ is returned by \tet{hammingweight}.
+
+The library syntax is \fun{GEN}{sumdigits}{GEN n}.
+
+\subsec{zncoppersmith$(P, N, X, \{B=N\})$}\kbdsidx{zncoppersmith}\label{se:zncoppersmith}
+$N$ being an integer and $P\in \Z[X]$, finds all integers $x$ with
+$|x| \leq X$ such that
+$$\gcd(N, P(x)) \geq B,$$
+using \idx{Coppersmith}'s algorithm (a famous application of the \idx{LLL}
+algorithm). $X$ must be smaller than $\exp(\log^2 B / (\deg(P) \log N))$:
+for $B = N$, this means $X < N^{1/\deg(P)}$. Some $x$ larger than $X$ may
+be returned if you are very lucky. The smaller $B$ (or the larger $X$), the
+slower the routine will be. The strength of Coppersmith method is the
+ability to find roots modulo a general \emph{composite} $N$: if $N$ is a prime
+or a prime power, \tet{polrootsmod} or \tet{polrootspadic} will be much
+faster.
+
+We shall now present two simple applications. The first one is
+finding non-trivial factors of $N$, given some partial information on the
+factors; in that case $B$ must obviously be smaller than the largest
+non-trivial divisor of $N$.
+\bprog
+setrand(1); \\ to make the example reproducible
+p = nextprime(random(10^30));
+q = nextprime(random(10^30)); N = p*q;
+p0 = p % 10^20; \\ assume we know 1) p > 10^29, 2) the last 19 digits of p
+p1 = zncoppersmith(10^19*x + p0, N, 10^12, 10^29)
+
+\\ result in 10ms.
+%1 = [35023733690]
+? gcd(p1[1] * 10^19 + p0, N) == p
+%2 = 1
+ at eprog\noindent and we recovered $p$, faster than by trying all
+possibilities $ < 10^{12}$.
+
+The second application is an attack on RSA with low exponent, when the
+message $x$ is short and the padding $P$ is known to the attacker. We use
+the same RSA modulus $N$ as in the first example:
+\bprog
+setrand(1);
+P = random(N);    \\ known padding
+e = 3;            \\ small public encryption exponent
+X = floor(N^0.3); \\ N^(1/e - epsilon)
+x0 = random(X);   \\ unknown short message
+C = lift( (Mod(x0,N) + P)^e ); \\ known ciphertext, with padding P
+zncoppersmith((P + x)^3 - C, N, X)
+
+\\ result in 244ms.
+%3 = [265174753892462432]
+? %[1] == x0
+%4 = 1
+ at eprog\noindent
+We guessed an integer of the order of $10^{18}$, almost instantly.
+
+The library syntax is \fun{GEN}{zncoppersmith}{GEN P, GEN N, GEN X, GEN B = NULL}.
+
+\subsec{znlog$(x,g,\{o\})$}\kbdsidx{znlog}\label{se:znlog}
+Discrete logarithm of $x$ in $(\Z/N\Z)^*$ in base $g$.
+The result is $[]$ when $x$ is not a power of $g$.
+If present, $o$ represents the multiplicative order of $g$, see
+\secref{se:DLfun}; the preferred format for this parameter is
+\kbd{[ord, factor(ord)]}, where \kbd{ord} is the order of $g$.
+This provides a definite speedup when the discrete log problem is simple:
+\bprog
+? p = nextprime(10^4); g = znprimroot(p); o = [p-1, factor(p-1)];
+? for(i=1,10^4, znlog(i, g, o))
+time = 205 ms.
+? for(i=1,10^4, znlog(i, g))
+time = 244 ms. \\ a little slower
+ at eprog
+
+The result is undefined if $g$ is not invertible mod $N$ or if the supplied
+order is incorrect.
+
+This function uses
+
+\item a combination of generic discrete log algorithms (see below).
+
+\item in $(\Z/N\Z)^*$ when $N$ is prime: a linear sieve index calculus
+method, suitable for $N < 10^{50}$, say, is used for large prime divisors of
+the order.
+
+The generic discrete log algorithms are:
+
+\item Pohlig-Hellman algorithm, to reduce to groups of prime order $q$,
+where $q | p-1$ and $p$ is an odd prime divisor of $N$,
+
+\item Shanks baby-step/giant-step ($q < 2^{32}$ is small),
+
+\item Pollard rho method ($q > 2^{32}$).
+
+The latter two algorithms require $O(\sqrt{q})$ operations in the group on
+average, hence will not be able to treat cases where $q > 10^{30}$, say.
+In addition, Pollard rho is not able to handle the case where there are no
+solutions: it will enter an infinite loop.
+\bprog
+? g = znprimroot(101)
+%1 = Mod(2,101)
+? znlog(5, g)
+%2 = 24
+? g^24
+%3 = Mod(5, 101)
+
+? G = znprimroot(2 * 101^10)
+%4 = Mod(110462212541120451003, 220924425082240902002)
+? znlog(5, G)
+%5 = 76210072736547066624
+? G^% == 5
+%6 = 1
+? N = 2^4*3^2*5^3*7^4*11; g = Mod(13, N); znlog(g^110, g)
+%7 = 110
+? znlog(6, Mod(2,3))  \\ no solution
+%8 = []
+ at eprog\noindent For convenience, $g$ is also allowed to be a $p$-adic number:
+\bprog
+? g = 3+O(5^10); znlog(2, g)
+%1 = 1015243
+? g^%
+%2 = 2 + O(5^10)
+ at eprog
+
+The library syntax is \fun{GEN}{znlog}{GEN x, GEN g, GEN o = NULL}.
+
+\subsec{znorder$(x,\{o\})$}\kbdsidx{znorder}\label{se:znorder}
+$x$ must be an integer mod $n$, and the
+result is the order of $x$ in the multiplicative group $(\Z/n\Z)^*$. Returns
+an error if $x$ is not invertible.
+The parameter o, if present, represents a non-zero
+multiple of the order of $x$, see \secref{se:DLfun}; the preferred format for
+this parameter is \kbd{[ord, factor(ord)]}, where \kbd{ord = eulerphi(n)}
+is the cardinality of the group.
+
+The library syntax is \fun{GEN}{znorder}{GEN x, GEN o = NULL}.
+Also available is \fun{GEN}{order}{GEN x}.
+
+\subsec{znprimroot$(n)$}\kbdsidx{znprimroot}\label{se:znprimroot}
+Returns a primitive root (generator) of $(\Z/n\Z)^*$, whenever this
+latter group is cyclic ($n = 4$ or $n = 2p^k$ or $n = p^k$, where $p$ is an
+odd prime and $k \geq 0$). If the group is not cyclic, the result is
+undefined. If $n$ is a prime power, then the smallest positive primitive
+root is returned. This may not be true for $n = 2p^k$, $p$ odd.
+
+Note that this function requires factoring $p-1$ for $p$ as above,
+in order to determine the exact order of elements in
+$(\Z/n\Z)^*$: this is likely to be costly if $p$ is large.
+
+The library syntax is \fun{GEN}{znprimroot}{GEN n}.
+
+\subsec{znstar$(n)$}\kbdsidx{znstar}\label{se:znstar}
+Gives the structure of the multiplicative group
+$(\Z/n\Z)^*$ as a 3-component row vector $v$, where $v[1]=\phi(n)$ is the
+order of that group, $v[2]$ is a $k$-component row-vector $d$ of integers
+$d[i]$ such that $d[i]>1$ and $d[i]\mid d[i-1]$ for $i \ge 2$ and
+$(\Z/n\Z)^* \simeq \prod_{i=1}^k(\Z/d[i]\Z)$, and $v[3]$ is a $k$-component row
+vector giving generators of the image of the cyclic groups $\Z/d[i]\Z$.
+\bprog
+? G = znstar(40)
+%1 = [16, [4, 2, 2], [Mod(17, 40), Mod(21, 40), Mod(11, 40)]]
+? G.no   \\ eulerphi(40)
+%2 = 16
+? G.cyc  \\ cycle structure
+%3 = [4, 2, 2]
+? G.gen  \\ generators for the cyclic components
+%4 = [Mod(17, 40), Mod(21, 40), Mod(11, 40)]
+? apply(znorder, G.gen)
+%5 = [4, 2, 2]
+ at eprog\noindent According to the above definitions, \kbd{znstar(0)} is
+\kbd{[2, [2], [-1]]}, corresponding to $\Z^*$.
+
+The library syntax is \fun{GEN}{znstar}{GEN n}.
+%SECTION: number_theoretical
+
+\section{Functions related to elliptic curves}
+
+\subsec{Elliptic curve structures}
+An elliptic curve is given by a Weierstrass model\sidx{Weierstrass equation}
+$$
+  y^2+a_1xy+a_3y=x^3+a_2x^2+a_4x+a_6,
+$$
+whose discriminant is non-zero. Affine points on \kbd{E} are represented as
+two-component vectors \kbd{[x,y]}; the point at infinity, i.e.~the identity
+element of the group law, is represented by the one-component vector
+\kbd{[0]}.
+
+Given a vector of coefficients $[a_1,a_2,a_3,a_4,a_6]$, the function
+\tet{ellinit} initializes and returns an \tev{ell} structure. (An additional
+optional argument allows to specify the base field in case it cannot be
+inferred from the curve coefficients.) This structure contains data needed by
+elliptic curve related functions, and is generally passed as a first argument.
+Expensive data are skipped on initialization: they will be dynamically
+computed when (and if) needed, and then inserted in the structure. The
+precise layout of the \tev{ell} structure is left undefined and should never
+be used directly. The following \idx{member functions} are available,
+depending on the underlying domain.
+
+\subsubsec{All domains}
+
+\item \tet{a1}, \tet{a2}, \tet{a3}, \tet{a4}, \tet{a6}: coefficients of the
+elliptic curve.
+
+\item \tet{b2}, \tet{b4}, \tet{b6}, \tet{b8}: $b$-invariants of the curve; in
+characteristic $\neq 2$, for $Y = 2y + a_1x+a3$, the curve equation becomes
+$$ Y^2 = 4 x^3 + b_2 x^2 + 2b_4 x + b_6 =: g(x). $$
+
+\item \tet{c4}, \tet{c6}: $c$-invariants of the curve; in characteristic $\neq
+2,3$, for $X = x + b_2/12$ and $Y = 2y + a_1x+a3$, the curve equation becomes
+$$ Y^2 = 4 X^3 - (c_4/12) X - (c_6/216). $$
+
+\item \tet{disc}: discriminant of the curve. This is only required to be
+non-zero, not necessarily a unit.
+
+\item \tet{j}: $j$-invariant of the curve.
+
+\noindent These are used as follows:
+\bprog
+? E = ellinit([0,0,0, a4,a6]);
+? E.b4
+%2 = 2*a4
+? E.disc
+%3 = -64*a4^3 - 432*a6^2
+ at eprog
+
+\subsubsec{Curves over $\R$}
+
+This in particular includes curves defined over $\Q$. All member functions in
+this section return data, as it is currently stored in the structure, if
+present; and otherwise compute it to the default accuracy, that was fixed
+\emph{at the time of ellinit} (via a \typ{REAL} $D$ domain argument, or
+\kbd{realprecision} by default). The function \tet{ellperiods} allows to
+recompute (and cache) the following data to \emph{current}
+\kbd{realprecision}.
+
+\item \tet{area}: volume of the complex lattice defining $E$.
+
+\item \tet{roots} is a vector whose three components contain the complex
+roots of the right hand side $g(x)$ of the associated $b$-model $Y^2 = g(x)$.
+If the roots are all real, they are ordered by decreasing value. If only one
+is real, it is the first component.
+
+\item \tet{omega}: $[\omega_1,\omega_2]$, periods forming a basis of the
+complex lattice defining $E$. The first component $\omega_1$ is the
+(positive) real period, in other words the integral of $dx/(2y+a_1x+a_3)$
+over the connected component of the identity component of $E(\R)$.
+The second component $\omega_2$ is a complex period, such that
+$\tau=\dfrac{\omega_1}{\omega_2}$ belongs to Poincar\'e's
+half-plane (positive imaginary part); not necessarily to the standard
+fundamental domain.
+
+\item \tet{eta} is a row vector containing the quasi-periods $\eta_1$ and
+$\eta_2$ such that $\eta_i = 2\zeta(\omega_i/2)$, where $\zeta$ is the
+Weierstrass zeta function associated to the period lattice; see
+\tet{ellzeta}. In particular, the Legendre relation holds: $\eta_2\omega_1 -
+\eta_1\omega_2 = 2i\pi$.
+
+\misctitle{Warning} As for the orientation of the basis of the period lattice,
+beware that many sources use the inverse convention where $\omega_2/\omega_1$
+has positive imaginary part and our $\omega_2$ is the negative of theirs. Our
+convention $\tau = \omega_1/\omega_2$  ensures that the action of $\text{PSL}_2$ is the natural
+one:
+$$[a,b;c,d]\cdot\tau = (a\tau+b)/(c\tau+d)
+  = (a \omega_1 + b\omega_2)/(c\omega_1 + d\omega_2),$$
+instead of a twisted one. (Our $tau$ is $-1/\tau$ in the above inverse
+convention.)
+
+\subsubsec{Curves over $\Q_p$}
+
+We advise to input a model defined over $\Q$ for such curves. In any case,
+if you input an approximate model with \typ{PADIC} coefficients, it will be
+replaced by a lift to $\Q$ (an exact model ``close'' to the one that was
+input) and all quantities will then be computed in terms of this lifted
+model.
+
+For the time being only curves with multiplicative reduction (split or
+non-split), i.e. $v_p(j) < 0$, are supported by non-trivial functions. In
+this case the curve is analytically isomorphic to $\bar{\Q}_p^*/q^\Z :=
+E_q(\bar{\Q}_p)$, for some $p$-adic integer $q$ (the Tate period). In
+particular, we have $j(q) = j(E)$.
+
+\item \tet{p} is the residual characteristic
+
+\item \tet{roots} is a vector with a single component, equal to the $p$-adic
+root $e_1$ of the right hand side $g(x)$ of the associated $b$-model $Y^2
+= g(x)$. The point $(e_1,0)$ corresponds to $-1 \in \bar{\Q}_p^*/q^\Z$
+under the Tate parametrization.
+
+\item \tet{tate} returns $[u^2,u,q,[a,b]]$ in the notation of Henniart-Mestre
+(CRAS t. 308, p.~391--395, 1989): $q$ is as above, $u\in \Q_p(\sqrt{-c_6})$
+is such that $\phi^* dx/(2y + a_1x+a3) = u dt/t$, where $\phi: E_q\to E$
+is an isomorphism (well defined up to sign) and $dt/t$ is the canonical
+invariant differential on the Tate curve; $u^2\in\Q_p$ does not depend on
+$\phi$. (Technicality: if $u\not\in\Q_p$, it is stored as a quadratic
+\typ{POLMOD}.)
+Finally, $[a,b]$ satisfy $4u^2 b \cdot \text{agm}(\sqrt{a/b},1)^2 = 1$
+as in Theorem~2 (\emph{loc.~cit.}).
+
+\subsubsec{Curves over $\F_q$}
+
+\item \tet{p} is the characteristic of $\F_q$.
+
+\item \tet{no} is $\#E(\F_q)$.
+
+\item \tet{cyc} gives the cycle structure of $E(\F_q)$.
+
+\item \tet{gen} returns the generators of $E(\F_q)$.
+
+\item \tet{group} returns $[\kbd{no},\kbd{cyc},\kbd{gen}]$, i.e. $E(\F_q)$
+as an abelian group structure.
+
+\subsubsec{Curves over $\Q$}
+
+All functions should return a correct result, whether the model is minimal or
+not, but it is a good idea to stick to minimal models whenever
+$\gcd(c_4,c_6)$ is easy to factor (minor speed-up). The construction
+\bprog
+  E = ellminimalmodel(E0, &v)
+ at eprog\noindent replaces the original model $E_0$ by a minimal model $E$,
+and the variable change $v$ allows to go between the two models:
+\bprog
+  ellchangepoint(P0, v)
+  ellchangepointinv(P, v)
+ at eprog\noindent respectively map the point $P_0$ on $E_0$ to its image on
+$E$, and the point $P$ on $E$ to its pre-image on $E_0$.
+
+A few routines --- namely \tet{ellgenerators}, \tet{ellidentify},
+\tet{ellsearch}, \tet{forell} --- require the optional package \tet{elldata}
+(John Cremona's database) to be installed. In that case, the function
+\tet{ellinit} will allow alternative inputs, e.g.~\kbd{ellinit("11a1")}.
+Functions using this package need to load chunks of a large database in
+memory and require at least 2MB stack to avoid stack overflows.
+
+\item \tet{gen} returns the generators of $E(\Q)$, if known (from John
+  Cremona's database)
+
+
+\subsec{ellL1$(e, r)$}\kbdsidx{ellL1}\label{se:ellL1}
+Returns the value at $s=1$ of the derivative of order $r$ of the
+$L$-function of the elliptic curve $e$ assuming that $r$ is at most the order
+of vanishing of the $L$-function at $s=1$. (The result is wrong if $r$ is
+strictly larger than the order of vanishing at 1.)
+\bprog
+? e = ellinit("11a1"); \\ order of vanishing is 0
+? ellL1(e, 0)
+%2 = 0.2538418608559106843377589233
+? e = ellinit("389a1");  \\ order of vanishing is 2
+? ellL1(e, 0)
+%4 = -5.384067311837218089235032414 E-29
+? ellL1(e, 1)
+%5 = 0
+? ellL1(e, 2)
+%6 = 1.518633000576853540460385214
+ at eprog\noindent
+The main use of this function, after computing at \emph{low} accuracy the
+order of vanishing using \tet{ellanalyticrank}, is to compute the
+leading term at \emph{high} accuracy to check (or use) the Birch and
+Swinnerton-Dyer conjecture:
+\bprog
+? \p18
+  realprecision = 18 significant digits
+? ellanalyticrank(ellinit([0, 0, 1, -7, 6]))
+time = 32 ms.
+%1 = [3, 10.3910994007158041]
+? \p200
+  realprecision = 202 significant digits (200 digits displayed)
+? ellL1(e, 3)
+time = 23,113 ms.
+%3 = 10.3910994007158041387518505103609170697263563756570092797 at com$[\dots]$
+ at eprog
+
+The library syntax is \fun{GEN}{ellL1}{GEN e, long r, long prec}.
+
+\subsec{elladd$(E,\var{z1},\var{z2})$}\kbdsidx{elladd}\label{se:elladd}
+Sum of the points $z1$ and $z2$ on the
+elliptic curve corresponding to $E$.
+
+The library syntax is \fun{GEN}{elladd}{GEN E, GEN z1, GEN z2}.
+
+\subsec{ellak$(E,n)$}\kbdsidx{ellak}\label{se:ellak}
+Computes the coefficient $a_n$ of the $L$-function of the elliptic curve
+$E/\Q$, i.e.~coefficients of a newform of weight 2 by the modularity theorem
+(\idx{Taniyama-Shimura-Weil conjecture}). $E$ must be an \var{ell} structure
+over $\Q$ as output by \kbd{ellinit}. $E$ must be given by an integral model,
+not necessarily minimal, although a minimal model will make the function
+faster.
+\bprog
+? E = ellinit([0,1]);
+? ellak(E, 10)
+%2 = 0
+? e = ellinit([5^4,5^6]); \\ not minimal at 5
+? ellak(e, 5) \\ wasteful but works
+%3 = -3
+? E = ellminimalmodel(e); \\ now minimal
+? ellak(E, 5)
+%5 = -3
+ at eprog\noindent If the model is not minimal at a number of bad primes, then
+the function will be slower on those $n$ divisible by the bad primes.
+The speed should be comparable for other $n$:
+\bprog
+? for(i=1,10^6, ellak(E,5))
+time = 820 ms.
+? for(i=1,10^6, ellak(e,5)) \\ 5 is bad, markedly slower
+time = 1,249 ms.
+
+? for(i=1,10^5,ellak(E,5*i))
+time = 977 ms.
+? for(i=1,10^5,ellak(e,5*i)) \\ still slower but not so much on average
+time = 1,008 ms.
+ at eprog
+
+The library syntax is \fun{GEN}{akell}{GEN E, GEN n}.
+
+\subsec{ellan$(E,n)$}\kbdsidx{ellan}\label{se:ellan}
+Computes the vector of the first $n$ Fourier coefficients $a_k$
+corresponding to the elliptic curve $E$. The curve must be given by an
+integral model, not necessarily minimal, although a minimal model will make
+the function faster.
+
+The library syntax is \fun{GEN}{anell}{GEN E, long n}.
+Also available is \fun{GEN}{anellsmall}{GEN e, long n}, which
+returns a \typ{VECSMALL} instead of a \typ{VEC}, saving on memory.
+
+\subsec{ellanalyticrank$(e, \{\var{eps}\})$}\kbdsidx{ellanalyticrank}\label{se:ellanalyticrank}
+Returns the order of vanishing at $s=1$ of the $L$-function of the
+elliptic curve $e$ and the value of the first non-zero derivative. To
+determine this order, it is assumed that any value less than \kbd{eps} is
+zero. If no value of \kbd{eps} is given, a value of half the current
+precision is used.
+\bprog
+? e = ellinit("11a1"); \\ rank 0
+? ellanalyticrank(e)
+%2 = [0, 0.2538418608559106843377589233]
+? e = ellinit("37a1"); \\ rank 1
+? ellanalyticrank(e)
+%4 = [1, 0.3059997738340523018204836835]
+? e = ellinit("389a1"); \\ rank 2
+? ellanalyticrank(e)
+%6 = [2, 1.518633000576853540460385214]
+? e = ellinit("5077a1"); \\ rank 3
+? ellanalyticrank(e)
+%8 = [3, 10.39109940071580413875185035]
+ at eprog
+
+The library syntax is \fun{GEN}{ellanalyticrank}{GEN e, GEN eps = NULL, long prec}.
+
+\subsec{ellap$(E,\{p\})$}\kbdsidx{ellap}\label{se:ellap}
+Let $E$ be an \var{ell} structure as output by \kbd{ellinit}, defined over
+$\Q$ or a finite field $\F_q$. The argument $p$ is best left omitted if the
+curve is defined over a finite field, and must be a prime number otherwise.
+This function computes the trace of Frobenius $t$ for the elliptic curve $E$,
+defined by the equation $\#E(\F_q) = q+1 - t$.
+
+If the curve is defined over $\Q$, $p$ must be explicitly given and the
+function computes the trace of the reduction over $\F_p$.
+The trace of Frobenius is also the $a_p$ coefficient in the curve $L$-series
+$L(E,s) = \sum_n a_n n^{-s}$, whence the function name. The equation must be
+integral at $p$ but need not be minimal at $p$; of course, a minimal model
+will be more efficient.
+\bprog
+? E = ellinit([0,1]);  \\ y^2 = x^3 + 0.x + 1, defined over Q
+? ellap(E, 7) \\ 7 necessary here
+%2 = -4       \\ #E(F_7) = 7+1-(-4) = 12
+? ellcard(E, 7)
+%3 = 12       \\ OK
+
+? E = ellinit([0,1], 11);  \\ defined over F_11
+? ellap(E)       \\ no need to repeat 11
+%4 = 0
+? ellap(E, 11)   \\ ... but it also works
+%5 = 0
+? ellgroup(E, 13) \\ ouch, inconsistent input!
+   ***   at top-level: ellap(E,13)
+   ***                 ^-----------
+   *** ellap: inconsistent moduli in Rg_to_Fp:
+     11
+     13
+
+? Fq = ffgen(ffinit(11,3), 'a); \\ defines F_q := F_{11^3}
+? E = ellinit([a+1,a], Fq);  \\ y^2 = x^3 + (a+1)x + a, defined over F_q
+? ellap(E)
+%8 = -3
+ at eprog
+
+\misctitle{Algorithms used} If $E/\F_q$ has CM by a principal imaginary
+quadratic order we use a fast explicit formula (involving essentially Kronecker
+symbols and Cornacchia's algorithm), in $O(\log q)^2$.
+Otherwise, we use Shanks-Mestre's baby-step/giant-step method, which runs in
+time $q(p^{1/4})$ using $O(q^{1/4})$ storage, hence becomes unreasonable when
+$q$ has about 30~digits. If the \tet{seadata} package is installed, the
+\tet{SEA} algorithm becomes available, heuristically in $\tilde{O}(\log
+q)^4$, and primes of the order of 200~digits become feasible. In very small
+characteristic (2,3,5,7 or $13$), we use Harley's algorithm.
+
+The library syntax is \fun{GEN}{ellap}{GEN E, GEN p = NULL}.
+
+\subsec{ellbil$(E,\var{z1},\var{z2})$}\kbdsidx{ellbil}\label{se:ellbil}
+If $z1$ and $z2$ are points on the elliptic
+curve $E$ this function
+computes the value of the canonical bilinear form on $z1$, $z2$:
+$$ ( h(E,z1\kbd{+}z2) - h(E,z1) - h(E,z2) ) / 2 $$
+where \kbd{+} denotes of course addition on $E$. In addition, $z1$ or $z2$
+(but not both) can be vectors or matrices.
+
+The library syntax is \fun{GEN}{bilhell}{GEN E, GEN z1, GEN z2, long prec}.
+
+\subsec{ellcard$(E,\{p\})$}\kbdsidx{ellcard}\label{se:ellcard}
+Let $E$ be an \var{ell} structure as output by \kbd{ellinit}, defined over
+$\Q$ or a finite field $\F_q$. The argument $p$ is best left omitted if the
+curve is defined over a finite field, and must be a prime number otherwise.
+This function computes the order of the group $E(\F_q)$ (as would be
+computed by \tet{ellgroup}).
+
+If the curve is defined over $\Q$, $p$ must be explicitly given and the
+function computes the cardinal of the reduction over $\F_p$; the
+equation need not be minimal at $p$, but a minimal model will be more
+efficient. The reduction is allowed to be singular, and we return the order
+of the group of non-singular points in this case.
+
+The library syntax is \fun{GEN}{ellcard}{GEN E, GEN p = NULL}.
+Also available is \fun{GEN}{ellcard}{GEN E, GEN p} where $p$ is not
+\kbd{NULL}.
+
+\subsec{ellchangecurve$(E,v)$}\kbdsidx{ellchangecurve}\label{se:ellchangecurve}
+Changes the data for the elliptic curve $E$
+by changing the coordinates using the vector \kbd{v=[u,r,s,t]}, i.e.~if $x'$
+and $y'$ are the new coordinates, then $x=u^2x'+r$, $y=u^3y'+su^2x'+t$.
+$E$ must be an \var{ell} structure as output by \kbd{ellinit}. The special
+case $v = 1$ is also used instead of $[1,0,0,0]$ to denote the
+trivial coordinate change.
+
+The library syntax is \fun{GEN}{ellchangecurve}{GEN E, GEN v}.
+
+\subsec{ellchangepoint$(x,v)$}\kbdsidx{ellchangepoint}\label{se:ellchangepoint}
+Changes the coordinates of the point or
+vector of points $x$ using the vector \kbd{v=[u,r,s,t]}, i.e.~if $x'$ and
+$y'$ are the new coordinates, then $x=u^2x'+r$, $y=u^3y'+su^2x'+t$ (see also
+\kbd{ellchangecurve}).
+\bprog
+? E0 = ellinit([1,1]); P0 = [0,1]; v = [1,2,3,4];
+? E = ellchangecurve(E0, v);
+? P = ellchangepoint(P0,v)
+%3 = [-2, 3]
+? ellisoncurve(E, P)
+%4 = 1
+? ellchangepointinv(P,v)
+%5 = [0, 1]
+ at eprog
+
+The library syntax is \fun{GEN}{ellchangepoint}{GEN x, GEN v}.
+The reciprocal function \fun{GEN}{ellchangepointinv}{GEN x, GEN ch}
+inverts the coordinate change.
+
+\subsec{ellchangepointinv$(x,v)$}\kbdsidx{ellchangepointinv}\label{se:ellchangepointinv}
+Changes the coordinates of the point or vector of points $x$ using
+the inverse of the isomorphism associated to \kbd{v=[u,r,s,t]},
+i.e.~if $x'$ and $y'$ are the old coordinates, then $x=u^2x'+r$,
+$y=u^3y'+su^2x'+t$ (inverse of \kbd{ellchangepoint}).
+\bprog
+? E0 = ellinit([1,1]); P0 = [0,1]; v = [1,2,3,4];
+? E = ellchangecurve(E0, v);
+? P = ellchangepoint(P0,v)
+%3 = [-2, 3]
+? ellisoncurve(E, P)
+%4 = 1
+? ellchangepointinv(P,v)
+%5 = [0, 1]  \\ we get back P0
+ at eprog
+
+The library syntax is \fun{GEN}{ellchangepointinv}{GEN x, GEN v}.
+
+\subsec{ellconvertname$(\var{name})$}\kbdsidx{ellconvertname}\label{se:ellconvertname}
+Converts an elliptic curve name, as found in the \tet{elldata} database,
+from a string to a triplet $[\var{conductor}, \var{isogeny class},
+\var{index}]$. It will also convert a triplet back to a curve name.
+Examples:
+\bprog
+? ellconvertname("123b1")
+%1 = [123, 1, 1]
+? ellconvertname(%)
+%2 = "123b1"
+ at eprog
+
+The library syntax is \fun{GEN}{ellconvertname}{GEN name}.
+
+\subsec{elldivpol$(E,n,\{v='x\})$}\kbdsidx{elldivpol}\label{se:elldivpol}
+$n$-division polynomial $f_n$ for the curve $E$ in the
+variable $v$. In standard notation, for any affine point $P = (X,Y)$ on the
+curve, we have
+$$[n]P = (\phi_n(P)\psi_n(P) : \omega_n(P) : \psi_n(P)^3)$$
+for some polynomials $\phi_n,\omega_n,\psi_n$ in
+$\Z[a_1,a_2,a_3,a_4,a_6][X,Y]$. We have $f_n(X) = \psi_n(X)$ for $n$ odd, and
+$f_n(X) = \psi_n(X,Y) (2Y + a_1X+a_3)$ for $n$ even. We have
+$$ f_1  = 1,\quad f_2 = 4X^3 + b_2X^2 + 2b_4 X + b_6, \quad f_3 = 3 X^4 + b_2 X^3 + 3b_4 X^2 + 3 b_6 X + b8, $$
+$$ f_4 = f_2(2X^6 + b_2 X^5 + 5b_4 X^4 + 10 b_6 X^3 + 10 b_8 X^2 +
+(b_2b_8-b_4b_6)X + (b_8b_4 - b_6^2)), \dots $$
+For $n \geq 2$, the roots of $f_n$ are the $X$-coordinates of points in $E[n]$.
+
+The library syntax is \fun{GEN}{elldivpol}{GEN E, long n, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{elleisnum$(w,k,\{\fl=0\})$}\kbdsidx{elleisnum}\label{se:elleisnum}
+$k$ being an even positive integer, computes the numerical value of the
+Eisenstein series of weight $k$ at the lattice $w$, as given by
+\tet{ellperiods}, namely
+$$
+(2i \pi/\omega_2)^k
+\Big(1 + 2/\zeta(1-k) \sum_{n\geq 0} n^{k-1}q^n / (1-q^n)\Big),
+$$
+where $q = \exp(2i\pi \tau)$ and $\tau:=\omega_1/\omega_2$ belongs to the
+complex upper half-plane. It is also possible to directly input $w =
+[\omega_1,\omega_2]$, or an elliptic curve $E$ as given by \kbd{ellinit}.
+\bprog
+? w = ellperiods([1,I]);
+? elleisnum(w, 4)
+%2 = 2268.8726415508062275167367584190557607
+? elleisnum(w, 6)
+%3 = -3.977978632282564763 E-33
+? E = ellinit([1, 0]);
+? elleisnum(E, 4, 1)
+%5 = -47.999999999999999999999999999999999998
+ at eprog
+
+When \fl\ is non-zero and $k=4$ or 6, returns the elliptic invariants $g_2$
+or $g_3$, such that
+$$y^2 = 4x^3 - g_2 x - g_3$$
+is a Weierstrass equation for $E$.
+
+The library syntax is \fun{GEN}{elleisnum}{GEN w, long k, long flag, long prec}.
+
+\subsec{elleta$(w)$}\kbdsidx{elleta}\label{se:elleta}
+Returns the quasi-periods $[\eta_1,\eta_2]$
+associated to the lattice basis $\var{w} = [\omega_1, \omega_2]$.
+Alternatively, \var{w} can be an elliptic curve $E$ as output by
+\kbd{ellinit}, in which case, the quasi periods associated to the period
+lattice basis \kbd{$E$.omega} (namely, \kbd{$E$.eta}) are returned.
+\bprog
+? elleta([1, I])
+%1 = [3.141592653589793238462643383, 9.424777960769379715387930149*I]
+ at eprog
+
+The library syntax is \fun{GEN}{elleta}{GEN w, long prec}.
+
+\subsec{ellfromj$(j)$}\kbdsidx{ellfromj}\label{se:ellfromj}
+Returns the coefficients $[a_1,a_2,a_3,a_4,a_6]$ of a fixed elliptic curve
+with $j$-invariant $j$.
+
+The library syntax is \fun{GEN}{ellfromj}{GEN j}.
+
+\subsec{ellgenerators$(E)$}\kbdsidx{ellgenerators}\label{se:ellgenerators}
+If $E$ is an elliptic curve over the rationals, return a $\Z$-basis of the
+free part of the \idx{Mordell-Weil group} associated to $E$.  This relies on
+the \tet{elldata} database being installed and referencing the curve, and so
+is only available for curves over $\Z$ of small conductors.
+If $E$ is an elliptic curve over a finite field $\F_q$ as output by
+\tet{ellinit}, return a minimal set of generators for the group $E(\F_q)$.
+
+The library syntax is \fun{GEN}{ellgenerators}{GEN E}.
+
+\subsec{ellglobalred$(E)$}\kbdsidx{ellglobalred}\label{se:ellglobalred}
+Calculates the arithmetic conductor, the global
+minimal model of $E$ and the global \idx{Tamagawa number} $c$.
+$E$ must be an \var{ell} structure as output by \kbd{ellinit}, defined over
+$\Q$. The result is a vector $[N,v,c,F,L]$, where
+
+\item $N$ is the arithmetic conductor of the curve,
+
+\item $v$ gives the coordinate change for $E$ over $\Q$ to the minimal
+integral model (see \tet{ellminimalmodel}),
+
+\item $c$ is the product of the local Tamagawa numbers $c_p$, a quantity
+which enters in the \idx{Birch and Swinnerton-Dyer conjecture},\sidx{minimal model}
+
+\item $F$ is the factorization of $N$ over $\Z$.
+
+\item $L$ is a vector, whose $i$-th entry contains the local data
+at the $i$-th prime divisor of $N$, i.e. \kbd{L[i] = elllocalred(E,F[i,1])},
+where the local coordinate change has been deleted, and replaced by a $0$.
+
+The library syntax is \fun{GEN}{ellglobalred}{GEN E}.
+
+\subsec{ellgroup$(E,\{p\},\{\fl\})$}\kbdsidx{ellgroup}\label{se:ellgroup}
+Let $E$ be an \var{ell} structure as output by \kbd{ellinit}, defined over
+$\Q$ or a finite field $\F_q$. The argument $p$ is best left omitted if the
+curve is defined over a finite field, and must be a prime number otherwise.
+This function computes the structure of the group $E(\F_q) \sim \Z/d_1\Z
+\times \Z/d_2\Z$, with $d_2\mid d_1$.
+
+If the curve is defined over $\Q$, $p$ must be explicitly given and the
+function computes the structure of the reduction over $\F_p$; the
+equation need not be minimal at $p$, but a minimal model will be more
+efficient. The reduction is allowed to be singular, and we return the
+structure of the (cyclic) group of non-singular points in this case.
+
+If the flag is $0$ (default), return $[d_1]$ or $[d_1, d_2]$, if $d_2>1$.
+If the flag is $1$, return a triple $[h,\var{cyc},\var{gen}]$, where
+$h$ is the curve cardinality, \var{cyc} gives the group structure as a
+product of cyclic groups (as per $\fl = 0$). More precisely, if $d_2 > 1$,
+the output is $[d_1d_2, [d_1,d_2],[P,Q]]$ where $P$ is
+of order $d_1$ and $[P,Q]$ generates the curve.
+\misctitle{Caution} It is not guaranteed that $Q$ has order $d_2$, which in
+the worst case requires an expensive discrete log computation. Only that
+\kbd{ellweilpairing(E, P, Q, d1)} has order $d_2$.
+\bprog
+? E = ellinit([0,1]);  \\ y^2 = x^3 + 0.x + 1, defined over Q
+? ellgroup(E, 7)
+%2 = [6, 2] \\ Z/6 x Z/2, non-cyclic
+? E = ellinit([0,1] * Mod(1,11));  \\ defined over F_11
+? ellgroup(E)   \\ no need to repeat 11
+%4 = [12]
+? ellgroup(E, 11)   \\ ... but it also works
+%5 = [12]
+? ellgroup(E, 13) \\ ouch, inconsistent input!
+   ***   at top-level: ellgroup(E,13)
+   ***                 ^--------------
+   *** ellgroup: inconsistent moduli in Rg_to_Fp:
+     11
+     13
+? ellgroup(E, 7, 1)
+%6 = [12, [6, 2], [[Mod(2, 7), Mod(4, 7)], [Mod(4, 7), Mod(4, 7)]]]
+ at eprog\noindent
+If $E$ is defined over $\Q$, we allow singular reduction and in this case we
+return the structure of the group of non-singular points, satisfying
+$\#E_{ns}(\F_p) = p - a_p$.
+\bprog
+? E = ellinit([0,5]);
+? ellgroup(E, 5, 1)
+%2 = [5, [5], [[Mod(4, 5), Mod(2, 5)]]]
+? ellap(E, 5)
+%3 = 0 \\ additive reduction at 5
+? E = ellinit([0,-1,0,35,0]);
+? ellgroup(E, 5, 1)
+%5 = [4, [4], [[Mod(2, 5), Mod(2, 5)]]]
+? ellap(E, 5)
+%6 = 1 \\ split multiplicative reduction at 5
+? ellgroup(E, 7, 1)
+%7 = [8, [8], [[Mod(3, 7), Mod(5, 7)]]]
+? ellap(E, 7)
+%8 = -1 \\ non-split multiplicative reduction at 7
+ at eprog
+
+The library syntax is \fun{GEN}{ellgroup0}{GEN E, GEN p = NULL, long flag}.
+Also available is \fun{GEN}{ellgroup}{GEN E, GEN p}, corresponding
+to \fl = 0.
+
+\subsec{ellheegner$(E)$}\kbdsidx{ellheegner}\label{se:ellheegner}
+Let $E$ be an elliptic curve over the rationals, assumed to be of
+(analytic) rank $1$. This returns a non-torsion rational point on the curve,
+whose canonical height is equal to the product of the elliptic regulator by the
+analytic Sha.
+
+This uses the Heegner point method, described in Cohen GTM 239; the complexity
+is proportional to the product of the square root of the conductor and the
+height of the point (thus, it is preferable to apply it to strong Weil curves).
+\bprog
+? E = ellinit([-157^2,0]);
+? u = ellheegner(E); print(u[1], "\n", u[2])
+69648970982596494254458225/166136231668185267540804
+538962435089604615078004307258785218335/67716816556077455999228495435742408
+? ellheegner(ellinit([0,1]))         \\ E has rank 0 !
+ ***   at top-level: ellheegner(E=ellinit
+ ***                 ^--------------------
+ *** ellheegner: The curve has even analytic rank.
+ at eprog
+
+The library syntax is \fun{GEN}{ellheegner}{GEN E}.
+
+\subsec{ellheight$(E,x,\{\fl=2\})$}\kbdsidx{ellheight}\label{se:ellheight}
+Global N\'eron-Tate height of the point $z$ on the elliptic curve
+$E$ (defined over $\Q$), using the normalization in Cremona's
+\emph{Algorithms for modular elliptic curves}. $E$
+must be an \kbd{ell} as output by \kbd{ellinit}; it needs not be given by a
+minimal model although the computation will be faster if it is. \fl\ selects
+the algorithm used to compute the Archimedean local height. If $\fl=0$,
+we use sigma and theta-functions and Silverman's trick (Computing
+heights on elliptic curves, \emph{Math.~Comp.} {\bf 51}; note that
+our height is twice Silverman's height). If
+$\fl=1$, use Tate's $4^n$ algorithm. If $\fl=2$, use Mestre's AGM algorithm.
+The latter converges quadratically and is much faster than the other two.
+
+The library syntax is \fun{GEN}{ellheight0}{GEN E, GEN x, long flag, long prec}.
+Also available is \fun{GEN}{ghell}{GEN E, GEN x, long prec}
+($\fl=2$).
+
+\subsec{ellheightmatrix$(E,x)$}\kbdsidx{ellheightmatrix}\label{se:ellheightmatrix}
+$x$ being a vector of points, this
+function outputs the Gram matrix of $x$ with respect to the N\'eron-Tate
+height, in other words, the $(i,j)$ component of the matrix is equal to
+\kbd{ellbil($E$,x[$i$],x[$j$])}. The rank of this matrix, at least in some
+approximate sense, gives the rank of the set of points, and if $x$ is a
+basis of the \idx{Mordell-Weil group} of $E$, its determinant is equal to
+the regulator of $E$. Note our height normalization follows Cremona's
+\emph{Algorithms for modular elliptic curves}: this matrix should be divided
+by 2 to be in accordance with, e.g., Silverman's normalizations.
+
+The library syntax is \fun{GEN}{mathell}{GEN E, GEN x, long prec}.
+
+\subsec{ellidentify$(E)$}\kbdsidx{ellidentify}\label{se:ellidentify}
+Look up the elliptic curve $E$, defined by an arbitrary model over $\Q$,
+in the \tet{elldata} database.
+Return \kbd{[[N, M, G], C]}  where $N$ is the curve name in Cremona's
+elliptic curve database, $M$ is the minimal model, $G$ is a $\Z$-basis of
+the free part of the \idx{Mordell-Weil group} $E(\Q)$ and $C$ is the
+change of coordinates change, suitable for \kbd{ellchangecurve}.
+
+The library syntax is \fun{GEN}{ellidentify}{GEN E}.
+
+\subsec{ellinit$(x,\{D=1\})$}\kbdsidx{ellinit}\label{se:ellinit}
+Initialize an \tet{ell} structure, associated to the elliptic curve $E$.
+$E$ is either
+
+\item a $5$-component vector $[a_1,a_2,a_3,a_4,a_6]$ defining the elliptic
+curve with Weierstrass equation
+$$ Y^2 + a_1 XY + a_3 Y = X^3 + a_2 X^2 + a_4 X + a_6, $$
+
+\item a $2$-component vector $[a_4,a_6]$ defining the elliptic
+curve with short Weierstrass equation
+$$ Y^2 = X^3 + a_4 X + a_6, $$
+
+\item a character string in Cremona's notation, e.g. \kbd{"11a1"}, in which
+case the curve is retrieved from the \tet{elldata} database if available.
+
+The optional argument $D$ describes the domain over which the curve is
+defined:
+
+\item the \typ{INT} $1$ (default): the field of rational numbers $\Q$.
+
+\item a \typ{INT} $p$, where $p$ is a prime number: the prime finite field
+$\F_p$.
+
+\item an \typ{INTMOD} \kbd{Mod(a, p)}, where $p$ is a prime number: the
+prime finite field $\F_p$.
+
+\item a \typ{FFELT}, as returned by \tet{ffgen}: the corresponding finite
+field $\F_q$.
+
+\item a \typ{PADIC}, $O(p^n)$: the field $\Q_p$, where $p$-adic quantities
+will be computed to a relative accuracy of $n$ digits. We advise to input a
+model defined over $\Q$ for such curves. In any case, if you input an
+approximate model with \typ{PADIC} coefficients, it will be replaced by a lift
+to $\Q$ (an exact model ``close'' to the one that was input) and all quantities
+will then be computed in terms of this lifted model, at the given accuracy.
+
+\item a \typ{REAL} $x$: the field $\C$ of complex numbers, where floating
+point quantities are by default computed to a relative accuracy of
+\kbd{precision}$(x)$. If no such argument is given, the value of
+\kbd{realprecision} at the time \kbd{ellinit} is called will be used.
+
+This argument $D$ is indicative: the curve coefficients are checked for
+compatibility, possibly changing $D$; for instance if $D = 1$ and
+an \typ{INTMOD} is found. If inconsistencies are detected, an error is
+raised:
+\bprog
+? ellinit([1 + O(5), 1], O(7));
+ ***   at top-level: ellinit([1+O(5),1],O
+ ***                 ^--------------------
+ *** ellinit: inconsistent moduli in ellinit: 7 != 5
+ at eprog\noindent If the curve coefficients are too general to fit any of the
+above domain categories, only basic operations, such as point addition, will
+be supported later.
+
+If the curve (seen over the domain $D$) is singular, fail and return an
+empty vector $[]$.
+\bprog
+? E = ellinit([0,0,0,0,1]); \\ y^2 = x^3 + 1, over Q
+? E = ellinit([0,1]);       \\ the same curve, short form
+? E = ellinit("36a1");      \\ sill the same curve, Cremona's notations
+? E = ellinit([0,1], 2)     \\ over F2: singular curve
+%4 = []
+? E = ellinit(['a4,'a6] * Mod(1,5));  \\ over F_5[a4,a6], basic support !
+ at eprog\noindent
+
+The result of \tet{ellinit} is an \tev{ell} structure. It contains at least
+the following information in its components:
+%
+$$ a_1,a_2,a_3,a_4,a_6,b_2,b_4,b_6,b_8,c_4,c_6,\Delta,j.$$
+%
+All are accessible via member functions. In particular, the discriminant is
+\kbd{$E$.disc}, and the $j$-invariant is \kbd{$E$.j}.
+\bprog
+? E = ellinit([a4, a6]);
+? E.disc
+%2 = -64*a4^3 - 432*a6^2
+? E.j
+%3 = -6912*a4^3/(-4*a4^3 - 27*a6^2)
+ at eprog
+Further components contain domain-specific data, which are in general dynamic:
+only computed when needed, and then cached in the structure.
+\bprog
+? E = ellinit([2,3], 10^60+7);  \\ E over F_p, p large
+? ellap(E)
+time = 4,440 ms.
+%2 = -1376268269510579884904540406082
+? ellcard(E);  \\ now instantaneous !
+time = 0 ms.
+? ellgenerators(E);
+time = 5,965 ms.
+? ellgenerators(E); \\ second time instantaneous
+time = 0 ms.
+ at eprog
+See the description of member functions related to elliptic curves at the
+beginning of this section.
+
+The library syntax is \fun{GEN}{ellinit}{GEN x, GEN D = NULL, long prec}.
+
+\subsec{ellisoncurve$(E,z)$}\kbdsidx{ellisoncurve}\label{se:ellisoncurve}
+Gives 1 (i.e.~true) if the point $z$ is on the elliptic curve $E$, 0
+otherwise. If $E$ or $z$ have imprecise coefficients, an attempt is made to
+take this into account, i.e.~an imprecise equality is checked, not a precise
+one. It is allowed for $z$ to be a vector of points in which case a vector
+(of the same type) is returned.
+
+The library syntax is \fun{GEN}{ellisoncurve}{GEN E, GEN z}.
+Also available is \fun{int}{oncurve}{GEN E, GEN z} which does not
+accept vectors of points.
+
+\subsec{ellj$(x)$}\kbdsidx{ellj}\label{se:ellj}
+Elliptic $j$-invariant. $x$ must be a complex number
+with positive imaginary part, or convertible into a power series or a
+$p$-adic number with positive valuation.
+
+The library syntax is \fun{GEN}{jell}{GEN x, long prec}.
+
+\subsec{elllocalred$(E,p)$}\kbdsidx{elllocalred}\label{se:elllocalred}
+Calculates the \idx{Kodaira} type of the local fiber of the elliptic curve
+$E$ at the prime $p$. $E$ must be an \var{ell} structure as output by
+\kbd{ellinit}, and is assumed to have all its coefficients $a_i$ in $\Z$.
+The result is a 4-component vector $[f,kod,v,c]$. Here $f$ is the exponent of
+$p$ in the arithmetic conductor of $E$, and $kod$ is the Kodaira type which
+is coded as follows:
+
+1 means good reduction (type I$_0$), 2, 3 and 4 mean types II, III and IV
+respectively, $4+\nu$ with $\nu>0$ means type I$_\nu$;
+finally the opposite values $-1$, $-2$, etc.~refer to the starred types
+I$_0^*$, II$^*$, etc. The third component $v$ is itself a vector $[u,r,s,t]$
+giving the coordinate changes done during the local reduction;
+$u = 1$ if and only if the given equation was already minimal at $p$.
+Finally, the last component $c$ is the local \idx{Tamagawa number} $c_p$.
+
+The library syntax is \fun{GEN}{elllocalred}{GEN E, GEN p}.
+
+\subsec{elllog$(E,P,G,\{o\})$}\kbdsidx{elllog}\label{se:elllog}
+Given two points $P$ and $G$ on the elliptic curve $E/\F_q$, returns the
+discrete logarithm of $P$ in base $G$, i.e. the smallest non-negative
+integer $n$ such that $P = [n]G$.
+See \tet{znlog} for the limitations of the underlying discrete log algorithms.
+If present, $o$ represents the order of $G$, see \secref{se:DLfun};
+the preferred format for this parameter is \kbd{[N, factor(N)]}, where $N$
+is  the order of $G$.
+
+If no $o$ is given, assume that $G$ generates the curve.
+The function also assumes that $P$ is a multiple of $G$.
+\bprog
+? a = ffgen(ffinit(2,8),'a);
+? E = ellinit([a,1,0,0,1]);  \\ over F_{2^8}
+? x = a^3; y = ellordinate(E,x)[1];
+? P = [x,y]; G = ellmul(E, P, 113);
+? ord = [242, factor(242)]; \\ P generates a group of order 242. Initialize.
+? ellorder(E, G, ord)
+%4 = 242
+? e = elllog(E, P, G, ord)
+%5 = 15
+? ellmul(E,G,e) == P
+%6 = 1
+ at eprog
+
+The library syntax is \fun{GEN}{elllog}{GEN E, GEN P, GEN G, GEN o = NULL}.
+
+\subsec{elllseries$(E,s,\{A=1\})$}\kbdsidx{elllseries}\label{se:elllseries}
+$E$ being an elliptic curve, given by an arbitrary model over $\Q$ as output
+by \kbd{ellinit}, this function computes the value of the $L$-series of $E$ at
+the (complex) point $s$. This function uses an $O(N^{1/2})$ algorithm, where
+$N$ is the conductor.
+
+The optional parameter $A$ fixes a cutoff point for the integral and is best
+left omitted; the result must be independent of $A$, up to
+\kbd{realprecision}, so this allows to check the function's accuracy.
+
+The library syntax is \fun{GEN}{elllseries}{GEN E, GEN s, GEN A = NULL, long prec}.
+
+\subsec{ellminimalmodel$(E,\{\&v\})$}\kbdsidx{ellminimalmodel}\label{se:ellminimalmodel}
+Return the standard minimal integral model of the rational elliptic
+curve $E$. If present, sets $v$ to the corresponding change of variables,
+which is a vector $[u,r,s,t]$ with rational components. The return value is
+identical to that of \kbd{ellchangecurve(E, v)}.
+
+The resulting model has integral coefficients, is everywhere minimal, $a_1$
+is 0 or 1, $a_2$ is 0, 1 or $-1$ and $a_3$ is 0 or 1. Such a model is
+unique, and the vector $v$ is unique if we specify that $u$ is positive,
+which we do. \sidx{minimal model}
+
+The library syntax is \fun{GEN}{ellminimalmodel}{GEN E, GEN *v = NULL}.
+
+\subsec{ellmodulareqn$(N,\{x\},\{y\})$}\kbdsidx{ellmodulareqn}\label{se:ellmodulareqn}
+Return a vector [\kbd{eqn},$t$] where \kbd{eqn} is a modular equation of
+level $N$, i.e.~a bivariate polynomial with integer coefficients; $t$
+indicates the type of this equation: either \emph{canonical} ($t = 0$) or
+\emph{Atkin} ($t = 1$). This function currently requires the package
+\kbd{seadata} to be installed and is limited to $N<500$, $N$ prime.
+
+Let $j$ be the $j$-invariant function. The polynomial \kbd{eqn} satisfies
+the following functional equation, which allows to compute the values of the
+classical modular polynomial $\Phi_N$ of prime level $N$, such that
+$\Phi_N(j(\tau), j(N\tau)) = 0$, while being much smaller than the latter:
+
+\item for canonical type:
+ $P(f(\tau),j(\tau)) = P(N^s/f(\tau),j(N\*\tau)) = 0$,
+ where $s = 12/\gcd(12,N-1)$;
+
+\item for Atkin type:
+ $P(f(\tau),j(\tau)) = P(f(\tau),j(N\*\tau)) = 0$.
+
+\noindent In both cases, $f$ is a suitable modular function (see below).
+
+The following GP function returns values of the classical modular polynomial
+by eliminating $f(\tau)$ in the above two equations, for $N\leq 31$ or
+$N\in\{41,47,59,71\}$.
+
+\bprog
+classicaleqn(N, X='X, Y='Y)=
+{
+  my(E=ellmodulareqn(N), P=E[1], t=E[2], Q, d);
+  if(poldegree(P,'y)>2,error("level unavailable in classicaleqn"));
+  if (t == 0,
+    my(s = 12/gcd(12,N-1));
+    Q = 'x^(N+1) * substvec(P,['x,'y],[N^s/'x,Y]);
+    d = N^(s*(2*N+1)) * (-1)^(N+1);
+  ,
+    Q = subst(P,'y,Y);
+    d = (X-Y)^(N+1));
+  polresultant(subst(P,'y,X), Q) / d;
+}
+ at eprog
+
+More precisely, let $W_N(\tau)={{-1}\over{N\*\tau}}$ be the Atkin-Lehner
+involution; we have $j(W_N(\tau)) = j(N\*\tau)$ and the function $f$ also
+satisfies:
+
+\item for canonical type:
+   $f(W_N(\tau)) = N^s/f(\tau)$;
+
+\item for Atkin type:
+   $f(W_N(\tau)) = f(\tau)$.
+
+\noindent Furthermore, for an equation of canonical type, $f$ is the standard
+$\eta$-quotient
+$$f(\tau) = N^s \* \big(\eta(N\*\tau) / \eta(\tau) \big)^{2\*s},$$
+where $\eta$ is Dedekind's eta function, which is invariant under
+$\Gamma_0(N)$.
+
+The library syntax is \fun{GEN}{ellmodulareqn}{long N, long x = -1, long y = -1}, where \kbd{x}, \kbd{y} are variable numbers.
+
+\subsec{ellmul$(E,z,n)$}\kbdsidx{ellmul}\label{se:ellmul}
+Computes $[n]z$, where $z$ is a point on the elliptic curve $E$. The
+exponent $n$ is in $\Z$, or may be a complex quadratic integer if the curve $E$
+has complex multiplication by $n$ (if not, an error message is issued).
+\bprog
+? Ei = ellinit([1,0]); z = [0,0];
+? ellmul(Ei, z, 10)
+%2 = [0]     \\ unsurprising: z has order 2
+? ellmul(Ei, z, I)
+%3 = [0, 0]  \\ Ei has complex multiplication by Z[i]
+? ellmul(Ei, z, quadgen(-4))
+%4 = [0, 0]  \\ an alternative syntax for the same query
+? Ej  = ellinit([0,1]); z = [-1,0];
+? ellmul(Ej, z, I)
+  ***   at top-level: ellmul(Ej,z,I)
+  ***                 ^--------------
+  *** ellmul: not a complex multiplication in ellmul.
+? ellmul(Ej, z, 1+quadgen(-3))
+%6 = [1 - w, 0]
+ at eprog
+The simple-minded algorithm for the CM case assumes that we are in
+characteristic $0$, and that the quadratic order to which $n$ belongs has
+small discriminant.
+
+The library syntax is \fun{GEN}{ellmul}{GEN E, GEN z, GEN n}.
+
+\subsec{ellneg$(E,z)$}\kbdsidx{ellneg}\label{se:ellneg}
+Opposite of the point $z$ on elliptic curve $E$.
+
+The library syntax is \fun{GEN}{ellneg}{GEN E, GEN z}.
+
+\subsec{ellorder$(E,z,\{o\})$}\kbdsidx{ellorder}\label{se:ellorder}
+Gives the order of the point $z$ on the elliptic
+curve $E$, defined over $\Q$ or a finite field.
+If the curve is defined over $\Q$, return (the impossible value) zero if the
+point has infinite order.
+\bprog
+? E = ellinit([-157^2,0]);  \\ the "157-is-congruent" curve
+? P = [2,2]; ellorder(E, P)
+%2 = 2
+? P = ellheegner(E); ellorder(E, P) \\ infinite order
+%3 = 0
+? E = ellinit(ellfromj(ffgen(5^10)));
+? ellcard(E)
+%5 = 9762580
+? P = random(E); ellorder(E, P)
+%6 = 4881290
+? p = 2^160+7; E = ellinit([1,2], p);
+? N = ellcard(E)
+%8 = 1461501637330902918203686560289225285992592471152
+? o = [N, factor(N)];
+? for(i=1,100, ellorder(E,random(E)))
+time = 260 ms.
+ at eprog
+The parameter $o$, is now mostly useless, and kept for backward
+compatibility. If present, it represents a non-zero multiple of the order
+of $z$, see \secref{se:DLfun}; the preferred format for this parameter is
+\kbd{[ord, factor(ord)]}, where \kbd{ord} is the cardinality of the curve.
+It is no longer needed since PARI is now able to compute it over large
+finite fields (was restricted to small prime fields at the time this feature
+was introduced), \emph{and} caches the result in $E$ so that it is computed
+and factored only once. Modifying the last example, we see that including
+this extra parameter provides no improvement:
+\bprog
+? o = [N, factor(N)];
+? for(i=1,100, ellorder(E,random(E),o))
+time = 260 ms.
+ at eprog
+
+The library syntax is \fun{GEN}{ellorder}{GEN E, GEN z, GEN o = NULL}.
+The obsolete form \fun{GEN}{orderell}{GEN e, GEN z} should no longer be
+used.
+
+\subsec{ellordinate$(E,x)$}\kbdsidx{ellordinate}\label{se:ellordinate}
+Gives a 0, 1 or 2-component vector containing
+the $y$-coordinates of the points of the curve $E$ having $x$ as
+$x$-coordinate.
+
+The library syntax is \fun{GEN}{ellordinate}{GEN E, GEN x, long prec}.
+
+\subsec{ellperiods$(w, \{\fl = 0\})$}\kbdsidx{ellperiods}\label{se:ellperiods}
+Let $w$ describe a complex period lattice ($w = [w_1,w_2]$
+or an ellinit structure). Returns normalized periods $[W_1,W_2]$ generating
+the same lattice such that $\tau := W_1/W_2$ has positive imaginary part
+and lies in the standard fundamental domain for $\text{SL}_2(\Z)$.
+
+If $\fl = 1$, the function returns $[[W_1,W_2], [\eta_1,\eta_2]]$, where
+$\eta_1$ and $\eta_2$ are the quasi-periods associated to
+$[W_1,W_2]$, satisfying $\eta_1 W_2 - \eta_2 W_1 = 2 i \pi$.
+
+The output of this function is meant to be used as the first argument
+given to ellwp, ellzeta, ellsigma or elleisnum. Quasi-periods are
+needed by ellzeta and ellsigma only.
+
+The library syntax is \fun{GEN}{ellperiods}{GEN w, long flag , long prec}.
+
+\subsec{ellpointtoz$(E,P)$}\kbdsidx{ellpointtoz}\label{se:ellpointtoz}
+If $E/\C \simeq \C/\Lambda$ is a complex elliptic curve ($\Lambda =
+\kbd{E.omega}$),
+computes a complex number $z$, well-defined modulo the lattice $\Lambda$,
+corresponding to the point $P$; i.e.~such that
+ $P = [\wp_\Lambda(z),\wp'_\Lambda(z)]$
+satisfies the equation
+$$y^2 = 4x^3 - g_2 x - g_3,$$
+where $g_2$, $g_3$ are the elliptic invariants.
+
+If $E$ is defined over $\R$ and $P\in E(\R)$, we have more precisely, $0 \leq
+\Re(t) < w1$ and $0 \leq \Im(t) < \Im(w2)$, where $(w1,w2)$ are the real and
+complex periods of $E$.
+\bprog
+? E = ellinit([0,1]); P = [2,3];
+? z = ellpointtoz(E, P)
+%2 = 3.5054552633136356529375476976257353387
+? ellwp(E, z)
+%3 = 2.0000000000000000000000000000000000000
+? ellztopoint(E, z) - P
+%4 = [6.372367644529809109 E-58, 7.646841173435770930 E-57]
+? ellpointtoz(E, [0]) \\ the point at infinity
+%5 = 0
+ at eprog
+
+If $E/\Q_p$ has multiplicative reduction, then $E/\bar{\Q_p}$ is analytically
+isomorphic to $\bar{\Q}_p^*/q^\Z$ (Tate curve) for some $p$-adic integer $q$.
+The behaviour is then as follows:
+
+\item If the reduction is split ($E.\kbd{tate[2]}$ is a \typ{PADIC}), we have
+an isomorphism $\phi: E(\Q_p) \simeq \Q_p^*/q^\Z$ and the function returns
+$\phi(P)\in \Q_p$.
+
+\item If the reduction is \emph{not} split ($E.\kbd{tate[2]}$ is a
+\typ{POLMOD}), we only have an isomorphism $\phi: E(K) \simeq K^*/q^\Z$ over
+the unramified quadratic extension $K/\Q_p$. In this case, the output
+$\phi(P)\in K$ is a \typ{POLMOD}.
+\bprog
+? E = ellinit([0,-1,1,0,0], O(11^5)); P = [0,0];
+? [u2,u,q] = E.tate; type(u) \\ split multiplicative reduction
+%2 = "t_PADIC"
+? ellmul(E, P, 5)  \\ P has order 5
+%3 = [0]
+? z = ellpointtoz(E, [0,0])
+%4 = 3 + 11^2 + 2*11^3 + 3*11^4 + O(11^5)
+? z^5
+%5 = 1 + O(11^5)
+? E = ellinit(ellfromj(1/4), O(2^6)); x=1/2; y=ellordinate(E,x)[1];
+? z = ellpointtoz(E,[x,y]); \\ t_POLMOD of t_POL with t_PADIC coeffs
+? liftint(z) \\ lift all p-adics
+%8 = Mod(8*u + 7, u^2 + 437)
+ at eprog
+
+The library syntax is \fun{GEN}{zell}{GEN E, GEN P, long prec}.
+
+\subsec{ellpow$(E,z,n)$}\kbdsidx{ellpow}\label{se:ellpow}
+Deprecated alias for \kbd{ellmul}.
+
+The library syntax is \fun{GEN}{ellmul}{GEN E, GEN z, GEN n}.
+
+\subsec{ellrootno$(E,\{p\})$}\kbdsidx{ellrootno}\label{se:ellrootno}
+$E$ being an \var{ell} structure over $\Q$ as output by \kbd{ellinit},
+this function computes the local root number of its $L$-series at the place
+$p$ (at the infinite place if $p = 0$). If $p$ is omitted, return the global
+root number. Note that the global root number is the sign of the functional
+equation and conjecturally is the parity of the rank of the \idx{Mordell-Weil
+group}. The equation for $E$ needs not be minimal at $p$, but if the model
+is already minimal the function will run faster.
+
+The library syntax is \fun{long}{ellrootno}{GEN E, GEN p = NULL}.
+
+\subsec{ellsearch$(N)$}\kbdsidx{ellsearch}\label{se:ellsearch}
+This function finds all curves in the \tet{elldata} database satisfying
+the constraint defined by the argument $N$:
+
+\item if $N$ is a character string, it selects a given curve, e.g.
+\kbd{"11a1"}, or curves in the given isogeny class, e.g. \kbd{"11a"}, or
+curves with given conductor, e.g. \kbd{"11"};
+
+\item if $N$ is a vector of integers, it encodes the same constraints
+as the character string above, according to the \tet{ellconvertname}
+correspondance, e.g. \kbd{[11,0,1]} for \kbd{"11a1"}, \kbd{[11,0]} for
+\kbd{"11a"} and \kbd{[11]} for \kbd{"11"};
+
+\item if $N$ is an integer, curves with conductor $N$ are selected.
+
+If $N$ is a full curve name, e.g. \kbd{"11a1"} or \kbd{[11,0,1]},
+the output format is $[N, [a_1,a_2,a_3,a_4,a_6], G]$ where
+$[a_1,a_2,a_3,a_4,a_6]$ are the coefficients of the Weierstrass equation of
+the curve and $G$ is a $\Z$-basis of the free part of the \idx{Mordell-Weil
+group} associated to the curve.
+\bprog
+? ellsearch("11a3")
+%1 = ["11a3", [0, -1, 1, 0, 0], []]
+? ellsearch([11,0,3])
+%2 = ["11a3", [0, -1, 1, 0, 0], []]
+ at eprog\noindent
+
+If $N$ is not a full curve name, then the output is a vector of all matching
+curves in the above format:
+\bprog
+? ellsearch("11a")
+%1 = [["11a1", [0, -1, 1, -10, -20], []],
+      ["11a2", [0, -1, 1, -7820, -263580], []],
+      ["11a3", [0, -1, 1, 0, 0], []]]
+? ellsearch("11b")
+%2 = []
+ at eprog
+
+The library syntax is \fun{GEN}{ellsearch}{GEN N}.
+Also available is \fun{GEN}{ellsearchcurve}{GEN N} that only
+accepts complete curve names (as \typ{STR}).
+
+\subsec{ellsigma$(L,\{z='x\},\{\fl=0\})$}\kbdsidx{ellsigma}\label{se:ellsigma}
+Computes the value at $z$ of the Weierstrass $\sigma$ function attached to
+the lattice $L$ as given by \tet{ellperiods}$(,1)$: including quasi-periods
+is useful, otherwise there are recomputed from scratch for each new $z$.
+$$ \sigma(z, L) = z \prod_{\omega\in L^*} \left(1 -
+\dfrac{z}{\omega}\right)e^{\dfrac{z}{\omega} + \dfrac{z^2}{2\omega^2}}.$$
+It is also possible to directly input $L = [\omega_1,\omega_2]$,
+or an elliptic curve $E$ as given by \kbd{ellinit} ($L = \kbd{E.omega}$).
+\bprog
+? w = ellperiods([1,I], 1);
+? ellsigma(w, 1/2)
+%2 = 0.47494937998792065033250463632798296855
+? E = ellinit([1,0]);
+? ellsigma(E) \\ at 'x, implicitly at default seriesprecision
+%4 = x + 1/60*x^5 - 1/10080*x^9 - 23/259459200*x^13 + O(x^17)
+ at eprog
+
+If $\fl=1$, computes an arbitrary determination of $\log(\sigma(z))$.
+
+The library syntax is \fun{GEN}{ellsigma}{GEN L, GEN z = NULL, long flag, long prec}.
+
+\subsec{ellsub$(E,\var{z1},\var{z2})$}\kbdsidx{ellsub}\label{se:ellsub}
+Difference of the points $z1$ and $z2$ on the
+elliptic curve corresponding to $E$.
+
+The library syntax is \fun{GEN}{ellsub}{GEN E, GEN z1, GEN z2}.
+
+\subsec{elltaniyama$(E, \{d = \var{seriesprecision}\})$}\kbdsidx{elltaniyama}\label{se:elltaniyama}
+Computes the modular parametrization of the elliptic curve $E/\Q$,
+where $E$ is an \var{ell} structure as output by \kbd{ellinit}. This returns
+a two-component vector $[u,v]$ of power series, given to $d$ significant
+terms (\tet{seriesprecision} by default), characterized by the following two
+properties. First the point $(u,v)$ satisfies the equation of the elliptic
+curve. Second, let $N$ be the conductor of $E$ and $\Phi: X_0(N)\to E$
+be a modular parametrization; the pullback by $\Phi$ of the
+N\'eron differential $du/(2v+a_1u+a_3)$ is equal to $2i\pi
+f(z)dz$, a holomorphic differential form. The variable used in the power
+series for $u$ and $v$ is $x$, which is implicitly understood to be equal to
+$\exp(2i\pi z)$.
+
+The algorithm assumes that $E$ is a \emph{strong} \idx{Weil curve}
+and that the Manin constant is equal to 1: in fact, $f(x) = \sum_{n > 0}
+\kbd{ellan}(E, n) x^n$.
+
+The library syntax is \fun{GEN}{elltaniyama}{GEN E, long precdl}.
+
+\subsec{elltatepairing$(E, P, Q, m)$}\kbdsidx{elltatepairing}\label{se:elltatepairing}
+Computes the Tate pairing of the two points $P$ and $Q$ on the elliptic
+curve $E$. The point $P$ must be of $m$-torsion.
+
+The library syntax is \fun{GEN}{elltatepairing}{GEN E, GEN P, GEN Q, GEN m}.
+
+\subsec{elltors$(E,\{\fl=0\})$}\kbdsidx{elltors}\label{se:elltors}
+If $E$ is an elliptic curve \emph{defined over $\Q$}, outputs the torsion
+subgroup of $E$ as a 3-component vector \kbd{[t,v1,v2]}, where \kbd{t} is the
+order of the torsion group, \kbd{v1} gives the structure of the torsion group
+as a product of cyclic groups (sorted by decreasing order), and \kbd{v2}
+gives generators for these cyclic groups. $E$ must be an \var{ell} structure
+as output by \kbd{ellinit}, defined over $\Q$.
+
+\bprog
+?  E = ellinit([-1,0]);
+?  elltors(E)
+%1 = [4, [2, 2], [[0, 0], [1, 0]]]
+ at eprog
+Here, the torsion subgroup is isomorphic to $\Z/2\Z \times \Z/2\Z$, with
+generators $[0,0]$ and $[1,0]$.
+
+If $\fl = 0$, find rational roots of division polynomials.
+
+If $\fl = 1$, use Lutz-Nagell (\emph{much} slower).
+
+If $\fl = 2$, use Doud's algorithm: bound torsion by computing $\#E(\F_p)$
+for small primes of good reduction, then look for torsion points using
+Weierstrass $\wp$ function (and Mazur's classification). For this variant,
+$E$ must be an \var{ell}.
+
+The library syntax is \fun{GEN}{elltors0}{GEN E, long flag}.
+Also available is \fun{GEN}{elltors}{GEN E} for \kbd{elltors(E, 0)}.
+
+\subsec{ellweilpairing$(E, P, Q, m)$}\kbdsidx{ellweilpairing}\label{se:ellweilpairing}
+Computes the Weil pairing of the two points of $m$-torsion $P$ and $Q$
+on the elliptic curve $E$.
+
+The library syntax is \fun{GEN}{ellweilpairing}{GEN E, GEN P, GEN Q, GEN m}.
+
+\subsec{ellwp$(w,\{z='x\},\{\fl=0\})$}\kbdsidx{ellwp}\label{se:ellwp}
+Computes the value at $z$ of the Weierstrass $\wp$ function attached to
+the lattice $w$ as given by \tet{ellperiods}. It is also possible to
+directly input $w = [\omega_1,\omega_2]$, or an elliptic curve $E$ as given
+by \kbd{ellinit} ($w = \kbd{E.omega}$).
+\bprog
+? w = ellperiods([1,I]);
+? ellwp(w, 1/2)
+%2 = 6.8751858180203728274900957798105571978
+? E = ellinit([1,1]);
+? ellwp(E, 1/2)
+%4 = 3.9413112427016474646048282462709151389
+ at eprog\noindent One can also compute the series expansion around $z = 0$:
+\bprog
+? E = ellinit([1,0]);
+? ellwp(E)              \\ 'x implicitly at default seriesprecision
+%5 = x^-2 - 1/5*x^2 + 1/75*x^6 - 2/4875*x^10 + O(x^14)
+? ellwp(E, x + O(x^12)) \\ explicit precision
+%6 = x^-2 - 1/5*x^2 + 1/75*x^6 + O(x^9)
+ at eprog
+
+Optional \fl\ means 0 (default): compute only $\wp(z)$, 1: compute
+$[\wp(z),\wp'(z)]$.
+
+The library syntax is \fun{GEN}{ellwp0}{GEN w, GEN z = NULL, long flag, long prec}.
+For $\fl = 0$, we also have
+\fun{GEN}{ellwp}{GEN w, GEN z, long prec}, and
+\fun{GEN}{ellwpseries}{GEN E, long v, long precdl} for the power series in
+variable $v$.
+
+\subsec{ellzeta$(w,\{z='x\})$}\kbdsidx{ellzeta}\label{se:ellzeta}
+Computes the value at $z$ of the Weierstrass $\zeta$ function attached to
+the lattice $w$ as given by \tet{ellperiods}$(,1)$: including quasi-periods
+is useful, otherwise there are recomputed from scratch for each new $z$.
+$$ \zeta(z, L) = \dfrac{1}{z} + z^2\sum_{\omega\in L^*}
+\dfrac{1}{\omega^2(z-\omega)}.$$
+It is also possible to directly input $w = [\omega_1,\omega_2]$,
+or an elliptic curve $E$ as given by \kbd{ellinit} ($w = \kbd{E.omega}$).
+The quasi-periods of $\zeta$, such that
+$$\zeta(z + a\omega_1 + b\omega_2) = \zeta(z) + a\eta_1 + b\eta_2 $$
+for integers $a$ and $b$ are obtained as $\eta_i = 2\zeta(\omega_i/2)$.
+Or using directly \tet{elleta}.
+\bprog
+? w = ellperiods([1,I],1);
+? ellzeta(w, 1/2)
+%2 = 1.5707963267948966192313216916397514421
+? E = ellinit([1,0]);
+? ellzeta(E, E.omega[1]/2)
+%4 = 0.84721308479397908660649912348219163647
+ at eprog\noindent One can also compute the series expansion around $z = 0$
+(the quasi-periods are useless in this case):
+\bprog
+? E = ellinit([0,1]);
+? ellzeta(E) \\ at 'x, implicitly at default seriesprecision
+%4 = x^-1 + 1/35*x^5 - 1/7007*x^11 + O(x^15)
+? ellzeta(E, x + O(x^20)) \\ explicit precision
+%5 = x^-1 + 1/35*x^5 - 1/7007*x^11 + 1/1440257*x^17 + O(x^18)
+ at eprog\noindent
+
+The library syntax is \fun{GEN}{ellzeta}{GEN w, GEN z = NULL, long prec}.
+
+\subsec{ellztopoint$(E,z)$}\kbdsidx{ellztopoint}\label{se:ellztopoint}
+$E$ being an \var{ell} as output by
+\kbd{ellinit}, computes the coordinates $[x,y]$ on the curve $E$
+corresponding to the complex number $z$. Hence this is the inverse function
+of \kbd{ellpointtoz}. In other words, if the curve is put in Weierstrass
+form $y^2 = 4x^3 - g_2x - g_3$, $[x,y]$ represents the Weierstrass
+$\wp$-function\sidx{Weierstrass $\wp$-function} and its derivative. More
+precisely, we have
+$$x = \wp(z) - b_2/12,\quad y = \wp'(z) - (a_1 x + a_3)/2.$$
+If $z$ is in the lattice defining $E$ over $\C$, the result is the point at
+infinity $[0]$.
+
+The library syntax is \fun{GEN}{pointell}{GEN E, GEN z, long prec}.
+
+\subsec{genus2red$(Q,P,\{p\})$}\kbdsidx{genus2red}\label{se:genus2red}
+Let $Q,P$ be polynomials with integer coefficients.
+Determines the reduction at $p > 2$ of the (proper, smooth) genus~2
+curve $C/\Q$, defined by the hyperelliptic equation $y^2+Qy = P$. (The
+special fiber $X_p$ of the minimal regular model $X$ of $C$ over $\Z$.)
+If $p$ is omitted, determines the reduction type for all (odd) prime
+divisors of the discriminant.
+
+\noindent This function rewritten from an implementation of Liu's algorithm by
+Cohen and Liu (1994), \kbd{genus2reduction-0.3}, see
+\kbd{http://www.math.u-bordeaux1.fr/\til liu/G2R/}.
+
+\misctitle{CAVEAT} The function interface may change: for the
+time being, it returns $[N,\var{FaN}, T, V]$
+where $N$ is either the local conductor at $p$ or the
+global conductor, \var{FaN} is its factorization, $y^2 = T$ defines a
+minimal model over $\Z[1/2]$ and $V$ describes the reduction type at the
+various considered~$p$. Unfortunately, the program is not complete for
+$p = 2$, and we may return the odd part of the conductor only: this is the
+case if the factorization includes the (impossible) term $2^{-1}$; if the
+factorization contains another power of $2$, then this is the exact local
+conductor at $2$ and $N$ is the global conductor.
+
+\bprog
+? default(debuglevel, 1);
+? genus2red(0,x^6 + 3*x^3 + 63, 3)
+(potential) stable reduction: [1, []]
+reduction at p: [III{9}] page 184, [3, 3], f = 10
+%1 = [59049, Mat([3, 10]), x^6 + 3*x^3 + 63, [3, [1, []],
+       ["[III{9}] page 184", [3, 3]]]]
+? [N, FaN, T, V] = genus2red(x^3-x^2-1, x^2-x);  \\ X_1(13), global reduction
+p = 13
+(potential) stable reduction: [5, [Mod(0, 13), Mod(0, 13)]]
+reduction at p: [I{0}-II-0] page 159, [], f = 2
+? N
+%3 = 169
+? FaN
+%4 = Mat([13, 2])   \\ in particular, good reduction at 2 !
+? T
+%5 = x^6 + 58*x^5 + 1401*x^4 + 18038*x^3 + 130546*x^2 + 503516*x + 808561
+? V
+%6 = [[13, [5, [Mod(0, 13), Mod(0, 13)]], ["[I{0}-II-0] page 159", []]]]
+ at eprog\noindent
+We now first describe the format of the vector $V = V_p$ in the case where
+$p$ was specified (local reduction at~$p$): it is a triple $[p, \var{stable},
+\var{red}]$. The component $\var{stable} = [\var{type}, \var{vecj}]$ contains
+information about the stable reduction after a field extension;
+depending on \var{type}s, the stable reduction is
+
+\item 1: smooth (i.e. the curve has potentially good reduction). The
+      Jacobian $J(C)$ has potentially good reduction.
+
+\item 2: an elliptic curve $E$ with an ordinary double point; \var{vecj}
+contains $j$ mod $p$, the modular invariant of $E$. The (potential)
+semi-abelian reduction of $J(C)$ is the extension of an elliptic curve (with
+modular invariant $j$ mod $p$) by a torus.
+
+\item 3: a projective line with two ordinary double points. The Jacobian
+$J(C)$ has potentially multiplicative reduction.
+
+\item 4: the union of two projective lines crossing transversally at three
+points. The Jacobian $J(C)$ has potentially multiplicative reduction.
+
+\item 5: the union of two elliptic curves $E_1$ and $E_2$ intersecting
+transversally at one point; \var{vecj} contains their modular invariants
+$j_1$ and $j_2$, which may live in a quadratic extension of $\F_p$ are need
+not be distinct. The Jacobian $J(C)$ has potentially good reduction,
+isomorphic to the product of the reductions of $E_1$ and $E_2$.
+
+\item 6: the union of an elliptic curve $E$ and a projective line which has
+an ordinary double point, and these two components intersect transversally
+at one point; \var{vecj} contains $j$ mod $p$, the modular invariant of $E$.
+The (potential) semi-abelian reduction of $J(C)$ is the extension of an
+elliptic curve (with modular invariant $j$ mod $p$) by a torus.
+
+\item 7: as in type 6, but the two components are both singular. The
+Jacobian $J(C)$ has potentially multiplicative reduction.
+
+The component $\var{red} = [\var{NUtype}, \var{neron}]$ contains two data
+concerning the reduction at $p$ without any ramified field extension.
+
+The \var{NUtype} is a \typ{STR} describing the reduction at $p$ of $C$,
+following Namikawa-Ueno, \emph{The complete classification of fibers in
+pencils of curves of genus two}, Manuscripta Math., vol. 9, (1973), pages
+143-186. The reduction symbol is followed by the corresponding page number in
+this article.
+
+The second datum \var{neron} is the group of connected components (over an
+algebraic closure of $\F_p$) of the N\'eron model of $J(C)$, given as a
+finite abelian group (vector of elementary divisors).
+\smallskip
+If $p = 2$, the \var{red} component may be omitted altogether (and
+replaced by \kbd{[]}, in the case where the program could not compute it.
+When $p$ was not specified, $V$ is the vector of all $V_p$, for all
+considered $p$.
+
+\misctitle{Notes about Namikawa-Ueno types}
+
+\item A lower index is denoted between braces: for instance, \kbd{[I\obr
+ 2\cbr-II-5]} means \kbd{[I\_2-II-5]}.
+
+\item If $K$ and $K'$ are Kodaira symbols for singular fibers of elliptic
+curves, \kbd{[$K$-$K'$-m]} and \kbd{[$K'$-$K$-m]} are the same.
+
+\item \kbd{[$K$-$K'$-$-1$]}  is \kbd{[$K'$-$K$-$\alpha$]} in the notation of
+Namikawa-Ueno.
+
+\item The figure \kbd{[2I\_0-m]} in Namikawa-Ueno, page 159, must be denoted
+by \kbd{[2I\_0-(m+1)]}.
+
+The library syntax is \fun{GEN}{genus2red}{GEN Q, GEN P, GEN p = NULL}.
+%SECTION: elliptic_curves
+
+\section{Functions related to general number fields}
+
+In this section can be found functions which are used almost exclusively for
+working in general number fields. Other less specific functions can be found
+in the next section on polynomials. Functions related to quadratic number
+fields are found in section \secref{se:arithmetic} (Arithmetic functions).
+
+\subsec{Number field structures}
+
+Let $K = \Q[X] / (T)$ a number field, $\Z_K$ its ring of integers, $T\in\Z[X]$
+is monic. Three basic number field structures can be associated to $K$ in
+GP:
+
+\item $\tev{nf}$ denotes a number field, i.e.~a data structure output by
+\tet{nfinit}. This contains the basic arithmetic data associated to the
+number field: signature, maximal order (given by a basis \kbd{nf.zk}),
+discriminant, defining polynomial $T$, etc.
+
+\item $\tev{bnf}$ denotes a ``Buchmann's number field'', i.e.~a
+data structure output by \tet{bnfinit}. This contains
+$\var{nf}$ and the deeper invariants of the field: units $U(K)$, class group
+$\Cl(K)$, as well as technical data required to solve the two associated
+discrete logarithm problems.
+
+\item $\tev{bnr}$ denotes a ``ray number field'', i.e.~a data structure
+output by \kbd{bnrinit}, corresponding to the ray class group structure of
+the field, for some modulus $f$. It contains a \var{bnf}, the modulus
+$f$, the ray class group $\Cl_f(K)$ and data associated to
+the discrete logarithm problem therein.
+
+\subsec{Algebraic numbers and ideals}
+
+\noindent An \tev{algebraic number} belonging to $K = \Q[X]/(T)$ is given as
+
+\item a \typ{INT}, \typ{FRAC} or \typ{POL} (implicitly modulo $T$), or
+
+\item a \typ{POLMOD} (modulo $T$), or
+
+\item a \typ{COL}~\kbd{v} of dimension $N = [K:\Q]$, representing
+the element in terms of the computed integral basis, as
+\kbd{sum(i = 1, N,~v[i] * nf.zk[i])}. Note that a \typ{VEC}
+will not be recognized.
+\medskip
+
+\noindent An \tev{ideal} is given in any of the following ways:
+
+\item an algebraic number in one of the above forms, defining a principal ideal.
+
+\item a prime ideal, i.e.~a 5-component vector in the format output by
+\kbd{idealprimedec} or \kbd{idealfactor}.
+
+\item a \typ{MAT}, square and in Hermite Normal Form (or at least
+upper triangular with non-negative coefficients), whose columns represent a
+$\Z$-basis of the ideal.
+
+One may use \kbd{idealhnf} to convert any ideal to the last (preferred) format.
+
+\item an \emph{extended ideal} \sidx{ideal (extended)} is a 2-component
+vector $[I, t]$, where $I$ is an ideal as above and $t$ is an algebraic
+number, representing the ideal $(t)I$. This is useful whenever \tet{idealred}
+is involved, implicitly working in the ideal class group, while keeping track
+of principal ideals. Ideal operations suitably update the principal part
+when it makes sense (in a multiplicative context), e.g.~using \kbd{idealmul}
+on $[I,t]$, $[J,u]$, we obtain $[IJ, tu]$. When it does not make sense, the
+extended part is silently discarded, e.g.~using \kbd{idealadd} with the above
+input produces $I+J$.
+
+The ``principal part'' $t$ in an extended ideal may be
+represented in any of the above forms, and \emph{also} as a factorization
+matrix (in terms of number field elements, not ideals!), possibly the empty
+matrix \kbd{[;]} representing $1$. In the latter case, elements stay in
+factored form, or \tev{famat} for \emph{fa}ctorization \emph{mat}rix, which
+is a convenient way to avoid coefficient explosion. To recover the
+conventional expanded form, try \tet{nffactorback}; but many functions
+already accept \var{famat}s as input, for instance \tet{ideallog}, so
+expanding huge elements should never be necessary.
+
+\subsec{Finite abelian groups}
+
+A finite abelian group $G$ in user-readable format is given by its Smith
+Normal Form as a pair $[h,d]$ or triple $[h,d,g]$.
+Here $h$ is the cardinality of $G$, $(d_i)$ is the vector of elementary
+divisors, and $(g_i)$ is a vector of generators. In short,
+$G = \oplus_{i\leq n} (\Z/d_i\Z) g_i$, with $d_n \mid \dots \mid d_2 \mid d_1$
+and $\prod d_i = h$. This information can also be retrieved as
+$G.\kbd{no}$, $G.\kbd{cyc}$ and $G.\kbd{gen}$.
+
+\item a \tev{character} on the abelian group
+$\oplus (\Z/d_i\Z) g_i$
+is given by a row vector $\chi = [a_1,\ldots,a_n]$ such that
+$\chi(\prod g_i^{n_i}) = \exp(2i\pi\sum a_i n_i / d_i)$.
+
+\item given such a structure, a \tev{subgroup} $H$ is input as a square
+matrix in HNF, whose columns express generators of $H$ on the given generators
+$g_i$. Note that the determinant of that matrix is equal to the index $(G:H)$.
+
+\subsec{Relative extensions}
+
+We now have a look at data structures associated to relative extensions
+of number fields $L/K$, and to projective $\Z_K$-modules. When defining a
+relative extension $L/K$, the $\var{nf}$ associated to the base field $K$
+must be defined by a variable having a lower priority (see
+\secref{se:priority}) than the variable defining the extension. For example,
+you may use the variable name $y$ to define the base field $K$, and $x$ to
+define the relative extension $L/K$.
+
+\subsubsec{Basic definitions}\label{se:ZKmodules}
+
+\item $\tev{rnf}$ denotes a relative number field, i.e.~a data structure
+output by \kbd{rnfinit}, associated to the extension $L/K$. The \var{nf}
+associated to be base field $K$ is \kbd{rnf.nf}.
+
+\item A \emph{relative matrix} is an $m\times n$ matrix whose entries are
+elements of $K$, in any form. Its $m$ columns $A_j$ represent elements
+in $K^n$.
+
+\item An \tev{ideal list} is a row vector of fractional ideals of the number
+field $\var{nf}$.
+
+\item A \tev{pseudo-matrix} is a 2-component row vector $(A,I)$ where $A$
+is a relative $m\times n$ matrix and $I$ an ideal list of length $n$. If $I =
+\{{\Bbb a}_1,\dots, {\Bbb a}_n\}$ and the columns of $A$ are $(A_1,\dots,
+A_n)$, this data defines the torsion-free (projective) $\Z_K$-module ${\Bbb
+a}_1 A_1\oplus {\Bbb a}_n A_n$.
+
+\item An \tev{integral pseudo-matrix} is a 3-component row vector w$(A,I,J)$
+where $A = (a_{i,j})$ is an $m\times n$ relative matrix and $I = ({\Bbb
+b}_1,\dots, {\Bbb b}_m)$, $J = ({\Bbb a}_1,\dots, {\Bbb a}_n)$ are ideal
+lists, such that $a_{i,j} \in {\Bbb b}_i {\Bbb a}_j^{-1}$ for all $i,j$. This
+data defines two abstract projective $\Z_K$-modules $N = {\Bbb
+a_1}\omega_1\oplus \cdots\oplus {\Bbb a_n}\omega_n $ in $K^n$, $P = {\Bbb
+b_1}\eta_1\oplus \cdots\oplus {\Bbb b_m}\eta_m$ in $K^m$, and a $\Z_K$-linear
+map $f:N\to P$ given by
+$$ f(\sum \alpha_j\omega_j) = \sum_i \Big(a_{i,j}\alpha_j\Big) \eta_i.$$
+This data defines the $\Z_K$-module $M = P/f(N)$.
+
+\item Any \emph{projective} $\Z_K$-module\varsidx{projective module} $M$
+of finite type in $K^m$ can be given by a pseudo matrix $(A,I)$.
+
+\item An arbitrary $\Z_K$ modules of finite type in $K^m$, with non-trivial
+torsion, is given by an integral pseudo-matrix $(A,I,J)$
+
+\subsubsec{Pseudo-bases, determinant}
+
+\item The pair $(A,I)$ is a \tev{pseudo-basis} of the module it
+generates if the ${\Bbb a_j}$ are non-zero, and the $A_j$ are $K$-linearly
+independent. We call $n$ the \emph{size} of the pseudo-basis. If $A$ is a
+relative matrix, the latter condition means it is square with non-zero
+determinant; we say that it is in Hermite Normal Form\sidx{Hermite normal
+form} (HNF) if it is upper triangular and all the elements of the diagonal
+are equal to 1.
+
+\item For instance, the relative integer basis \kbd{rnf.zk} is a pseudo-basis
+$(A,I)$ of $\Z_L$, where $A = \kbd{rnf.zk[1]}$ is a vector of elements of $L$,
+which are $K$-linearly independent. Most \var{rnf} routines return and handle
+$\Z_K$-modules contained in $L$ (e.g.~$\Z_L$-ideals) via a pseudo-basis
+$(A',I')$, where $A'$ is a relative matrix representing a vector of elements of
+$L$ in terms of the fixed basis \kbd{rnf.zk[1]}
+
+\item The \emph{determinant} of a pseudo-basis $(A,I)$ is the ideal
+equal to the product of the determinant of $A$ by all the ideals of $I$. The
+determinant of a pseudo-matrix is the determinant of any pseudo-basis of the
+module it generates.
+
+\subsec{Class field theory}\label{se:CFT}
+
+A $\tev{modulus}$, in the sense of class field theory, is a divisor supported
+on the non-complex places of $K$. In PARI terms, this means either an
+ordinary ideal $I$ as above (no Archimedean component), or a pair $[I,a]$,
+where $a$ is a vector with $r_1$ $\{0,1\}$-components, corresponding to the
+infinite part of the divisor. More precisely, the $i$-th component of $a$
+corresponds to the real embedding associated to the $i$-th real root of
+\kbd{K.roots}. (That ordering is not canonical, but well defined once a
+defining polynomial for $K$ is chosen.) For instance, \kbd{[1, [1,1]]} is a
+modulus for a real quadratic field, allowing ramification at any of the two
+places at infinity, and nowhere else.
+
+A \tev{bid} or ``big ideal'' is a structure output by \kbd{idealstar}
+needed to compute in $(\Z_K/I)^*$, where $I$ is a modulus in the above sense.
+It is a finite abelian group as described above, supplemented by
+technical data needed to solve discrete log problems.
+
+Finally we explain how to input ray number fields (or \var{bnr}), using class
+field theory. These are defined by a triple $A$, $B$, $C$, where the
+defining set $[A,B,C]$ can have any of the following forms: $[\var{bnr}]$,
+$[\var{bnr},\var{subgroup}]$, $[\var{bnf},\var{mod}]$,
+$[\var{bnf},\var{mod},\var{subgroup}]$. The last two forms are kept for
+backward compatibility, but no longer serve any real purpose (see example
+below); no newly written function will accept them.
+
+\item $\var{bnf}$ is as output by \kbd{bnfinit}, where units are mandatory
+unless the modulus is trivial; \var{bnr} is as output by \kbd{bnrinit}. This
+is the ground field $K$.
+
+\item \emph{mod} is a modulus $\goth{f}$, as described above.
+
+\item \emph{subgroup} a subgroup of the ray class group modulo $\goth{f}$ of
+$K$. As described above, this is input as a square matrix expressing
+generators of a subgroup of the ray class group \kbd{\var{bnr}.clgp} on the
+given generators.
+
+The corresponding \var{bnr} is the subfield of the ray class field of $K$
+modulo $\goth{f}$, fixed by the given subgroup.
+
+\bprog
+  ? K = bnfinit(y^2+1);
+  ? bnr = bnrinit(K, 13)
+  ? %.clgp
+  %3 = [36, [12, 3]]
+  ? bnrdisc(bnr); \\ discriminant of the full ray class field
+  ? bnrdisc(bnr, [3,1;0,1]); \\ discriminant of cyclic cubic extension of K
+ at eprog\noindent
+We could have written directly
+\bprog
+  ? bnrdisc(K, 13);
+  ? bnrdisc(K, 13, [3,1;0,1]);
+ at eprog\noindent
+avoiding one \tet{bnrinit}, but this would actually be slower since the
+\kbd{bnrinit} is called internally anyway. And now twice!
+
+\subsec{General use}
+
+All the functions which are specific to relative extensions, number fields,
+Buchmann's number fields, Buchmann's number rays, share the prefix \kbd{rnf},
+\kbd{nf}, \kbd{bnf}, \kbd{bnr} respectively. They take as first argument a
+number field of that precise type, respectively output by \kbd{rnfinit},
+\kbd{nfinit}, \kbd{bnfinit}, and \kbd{bnrinit}.
+
+However, and even though it may not be specified in the descriptions of the
+functions below, it is permissible, if the function expects a $\var{nf}$, to
+use a $\var{bnf}$ instead, which contains much more information. On the other
+hand, if the function requires a \kbd{bnf}, it will \emph{not} launch
+\kbd{bnfinit} for you, which is a costly operation. Instead, it will give you
+a specific error message. In short, the types
+$$ \kbd{nf} \leq \kbd{bnf} \leq \kbd{bnr}$$
+are ordered, each function requires a minimal type to work properly, but you
+may always substitute a larger type.
+
+The data types corresponding to the structures described above are rather
+complicated. Thus, as we already have seen it with elliptic curves, GP
+provides ``member functions'' to retrieve data from these structures (once
+they have been initialized of course). The relevant types of number fields
+are indicated between parentheses: \smallskip
+
+\sidx{member functions}
+\settabs\+xxxxxxx&(\var{bnr},x&\var{bnf},x&nf\hskip2pt&)x&: &\cr
+\+\tet{bid}    &(\var{bnr}&&&)&: & bid ideal structure.\cr
+
+\+\tet{bnf}    &(\var{bnr},& \var{bnf}&&)&: & Buchmann's number field.\cr
+
+\+\tet{clgp}  &(\var{bnr},& \var{bnf}&&)&: & classgroup. This one admits the
+following three subclasses:\cr
+
+\+      \quad \tet{cyc} &&&&&: & \quad cyclic decomposition
+ (SNF)\sidx{Smith normal form}.\cr
+
+\+      \quad \kbd{gen}\sidx{gen (member function)} &&&&&: &
+ \quad generators.\cr
+
+\+      \quad \tet{no}  &&&&&: & \quad number of elements.\cr
+
+\+\tet{diff}  &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the different ideal.\cr
+
+\+\tet{codiff}&(\var{bnr},& \var{bnf},& \var{nf}&)&: & the codifferent
+(inverse of the different in the ideal group).\cr
+
+\+\tet{disc} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & discriminant.\cr
+
+\+\tet{fu}   &(\var{bnr},& \var{bnf}&&)&: & \idx{fundamental units}.\cr
+
+\+\tet{index}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: &
+ \idx{index} of the power order in the ring of integers.\cr
+
+\+\tet{mod}   &(\var{bnr}&&&)&: & modulus.\cr
+
+\+\tet{nf}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & number field.\cr
+
+\+\tet{pol}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & defining polynomial.\cr
+
+\+\tet{r1} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the number
+of real embeddings.\cr
+
+\+\tet{r2} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the number
+of pairs of complex embeddings.\cr
+
+\+\tet{reg}  &(\var{bnr},& \var{bnf}&&)&: & regulator.\cr
+
+\+\tet{roots}&(\var{bnr},& \var{bnf},& \var{nf}&)&: & roots of the
+polynomial generating the field.\cr
+
+\+\tet{sign} &(\var{bnr},& \var{bnf},& \var{nf}&)&: & signature $[r1,r2]$.\cr
+
+\+\tet{t2}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & the $T_2$ matrix (see
+\kbd{nfinit}).\cr
+
+\+\tet{tu}   &(\var{bnr},& \var{bnf}&&)&: & a generator for the torsion
+units.\cr
+
+\+\tet{zk}   &(\var{bnr},& \var{bnf},& \var{nf}&)&: & integral basis, i.e.~a
+$\Z$-basis of the maximal order.\cr
+
+\+\tet{zkst}   &(\var{bnr}&&&)&: & structure of $(\Z_K/m)^*$.\cr
+
+\misctitle{Deprecated} The following member functions are still available, but deprecated and should not be used in new scripts :
+\+\tet{futu} &(\var{bnr},& \var{bnf},&&)&: &
+ $[u_1,...,u_r,w]$, $(u_i)$ is a vector of fundamental units,\cr
+\+&&&&&& $w$ generates the torsion units.\cr
+
+\+\tet{tufu} &(\var{bnr},& \var{bnf},&&)&: &
+ $[w,u_1,...,u_r]$, $(u_i)$ is a vector of fundamental units,\cr
+\+&&&&&& $w$ generates the torsion units.\cr
+
+
+  For instance, assume that $\var{bnf} = \kbd{bnfinit}(\var{pol})$, for some
+polynomial. Then \kbd{\var{bnf}.clgp} retrieves the class group, and
+\kbd{\var{bnf}.clgp.no} the class number. If we had set $\var{bnf} =
+\kbd{nfinit}(\var{pol})$, both would have output an error message. All these
+functions are completely recursive, thus for instance
+\kbd{\var{bnr}.bnf.nf.zk} will yield the maximal order of \var{bnr}, which
+you could get directly with a simple \kbd{\var{bnr}.zk}.
+
+\subsec{Class group, units, and the GRH}\label{se:GRHbnf}
+
+Some of the functions starting with \kbd{bnf} are implementations of the
+sub-exponential algorithms for finding class and unit groups under \idx{GRH},
+due to Hafner-McCurley, \idx{Buchmann} and Cohen-Diaz-Olivier. The general
+call to the functions concerning class groups of general number fields
+(i.e.~excluding \kbd{quadclassunit}) involves a polynomial $P$ and a
+technical vector
+$$\var{tech} = [c_1, c_2, \var{nrpid} ],$$
+where the parameters are to be understood as follows:
+
+$P$ is the defining polynomial for the number field, which must be in
+$\Z[X]$, irreducible and monic. In fact, if you supply a non-monic polynomial
+at this point, \kbd{gp} issues a warning, then \emph{transforms your
+polynomial} so that it becomes monic. The \kbd{nfinit} routine
+will return a different result in this case: instead of \kbd{res}, you get a
+vector \kbd{[res,Mod(a,Q)]}, where \kbd{Mod(a,Q) = Mod(X,P)} gives the change
+of variables. In all other routines, the variable change is simply lost.
+
+The \var{tech} interface is obsolete and you should not tamper with
+these parameters. Indeed, from version 2.4.0 on,
+
+\item the results are always rigorous under \idx{GRH} (before that version,
+they relied on a heuristic strengthening, hence the need for overrides).
+
+\item the influence of these parameters on execution time and stack size is
+marginal. They \emph{can} be useful to fine-tune and experiment with the
+\kbd{bnfinit} code, but you will be better off modifying all tuning
+parameters in the C code (there are many more than just those three).
+We nevertheless describe it for completeness.
+
+The numbers $c_1 \leq c_2$ are non-negative real numbers. By default they are
+chosen so that the result is correct under GRH. For $i = 1,2$, let
+$B_i = c_i(\log |d_K|)^2$, and denote by $S(B)$ the set of maximal ideals of
+$K$ whose norm is less than $B$. We want $S(B_1)$ to generate $\Cl(K)$ and hope
+that $S(B_2)$ can be \emph{proven} to generate $\Cl(K)$.
+
+More precisely, $S(B_1)$ is a factorbase used to compute a tentative
+$\Cl(K)$ by generators and relations. We then check explicitly, using
+essentially \kbd{bnfisprincipal}, that the elements of $S(B_2)$ belong to the
+span of $S(B_1)$. Under the assumption that $S(B_2)$ generates $\Cl(K)$, we
+are done. User-supplied $c_i$ are only used to compute initial guesses for
+the bounds $B_i$, and the algorithm increases them until one can \emph{prove}
+under GRH that $S(B_2)$ generates $\Cl(K)$. A uniform result of Bach says
+that $c_2 = 12$ is always suitable, but this bound is very pessimistic and a
+direct algorithm due to Belabas-Diaz-Friedman is used to check the condition,
+assuming GRH. The default values are $c_1 = c_2 = 0$. When $c_1$ is equal to
+$0$ the algorithm takes it equal to $c_2$.
+
+$\var{nrpid}$ is the maximal number of small norm relations associated to each
+ideal in the factor base. Set it to $0$ to disable the search for small norm
+relations. Otherwise, reasonable values are between 4 and 20. The default is
+4.
+
+\misctitle{Warning} Make sure you understand the above! By default, most of
+the \kbd{bnf} routines depend on the correctness of the GRH. In particular,
+any of the class number, class group structure, class group generators,
+regulator and fundamental units may be wrong, independently of each other.
+Any result computed from such a \kbd{bnf} may be wrong. The only guarantee is
+that the units given generate a subgroup of finite index in the full unit
+group. You must use \kbd{bnfcertify} to certify the computations
+unconditionally.
+
+\misctitle{Remarks}
+
+You do not need to supply the technical parameters (under the library you
+still need to send at least an empty vector, coded as \kbd{NULL}). However,
+should you choose to set some of them, they \emph{must} be given in the
+requested order. For example, if you want to specify a given value of
+\var{nrpid}, you must give some values as well for $c_1$ and $c_2$, and provide
+a vector $[c_1,c_2,\var{nrpid}]$.
+
+Note also that you can use an $\var{nf}$ instead of $P$, which avoids
+recomputing the integral basis and analogous quantities.
+
+\smallskip
+
+
+\subsec{bnfcertify$(\var{bnf},\{\fl = 0\})$}\kbdsidx{bnfcertify}\label{se:bnfcertify}
+$\var{bnf}$ being as output by
+\kbd{bnfinit}, checks whether the result is correct, i.e.~whether it is
+possible to remove the assumption of the Generalized Riemann
+Hypothesis\sidx{GRH}. It is correct if and only if the answer is 1. If it is
+incorrect, the program may output some error message, or loop indefinitely.
+You can check its progress by increasing the debug level. The \var{bnf}
+structure must contain the fundamental units:
+\bprog
+? K = bnfinit(x^3+2^2^3+1); bnfcertify(K)
+  ***   at top-level: K=bnfinit(x^3+2^2^3+1);bnfcertify(K)
+  ***                                        ^-------------
+  *** bnfcertify: missing units in bnf.
+? K = bnfinit(x^3+2^2^3+1, 1); \\ include units
+? bnfcertify(K)
+%3 = 1
+ at eprog
+
+If flag is present, only certify that the class group is a quotient of the
+one computed in bnf (much simpler in general); likewise, the computed units
+may form a subgroup of the full unit group. In this variant, the units are
+no longer needed:
+\bprog
+? K = bnfinit(x^3+2^2^3+1); bnfcertify(K, 1)
+%4 = 1
+ at eprog
+
+The library syntax is \fun{long}{bnfcertify0}{GEN bnf, long flag }.
+Also available is  \fun{GEN}{bnfcertify}{GEN bnf} ($\fl=0$).
+
+\subsec{bnfcompress$(\var{bnf})$}\kbdsidx{bnfcompress}\label{se:bnfcompress}
+Computes a compressed version of \var{bnf} (from \tet{bnfinit}), a
+``small Buchmann's number field'' (or \var{sbnf} for short) which contains
+enough information to recover a full $\var{bnf}$ vector very rapidly, but
+which is much smaller and hence easy to store and print. Calling
+\kbd{bnfinit} on the result recovers a true \kbd{bnf}, in general different
+from the original. Note that an \tev{snbf} is useless for almost all
+purposes besides storage, and must be converted back to \tev{bnf} form
+before use; for instance, no \kbd{nf*}, \kbd{bnf*} or member function
+accepts them.
+
+An \var{sbnf} is a 12 component vector $v$, as follows. Let \kbd{bnf} be
+the result of a full \kbd{bnfinit}, complete with units. Then $v[1]$ is
+\kbd{bnf.pol}, $v[2]$ is the number of real embeddings \kbd{bnf.sign[1]},
+$v[3]$ is \kbd{bnf.disc}, $v[4]$ is \kbd{bnf.zk}, $v[5]$ is the list of roots
+\kbd{bnf.roots}, $v[7]$ is the matrix $\kbd{W} = \kbd{bnf[1]}$,
+$v[8]$ is the matrix $\kbd{matalpha}=\kbd{bnf[2]}$,
+$v[9]$ is the prime ideal factor base \kbd{bnf[5]} coded in a compact way,
+and ordered according to the permutation \kbd{bnf[6]}, $v[10]$ is the
+2-component vector giving the number of roots of unity and a generator,
+expressed on the integral basis, $v[11]$ is the list of fundamental units,
+expressed on the integral basis, $v[12]$ is a vector containing the algebraic
+numbers alpha corresponding to the columns of the matrix \kbd{matalpha},
+expressed on the integral basis.
+
+All the components are exact (integral or rational), except for the roots in
+$v[5]$.
+
+The library syntax is \fun{GEN}{bnfcompress}{GEN bnf}.
+
+\subsec{bnfdecodemodule$(\var{nf},m)$}\kbdsidx{bnfdecodemodule}\label{se:bnfdecodemodule}
+If $m$ is a module as output in the
+first component of an extension given by \kbd{bnrdisclist}, outputs the
+true module.
+\bprog
+? K = bnfinit(x^2+23); L = bnrdisclist(K, 10); s = L[1][2]
+%1 = [[Mat([8, 1]), [[0, 0, 0]]], [Mat([9, 1]), [[0, 0, 0]]]]
+? bnfdecodemodule(K, s[1][1])
+%2 =
+[2 0]
+
+[0 1]
+ at eprog
+
+The library syntax is \fun{GEN}{decodemodule}{GEN nf, GEN m}.
+
+\subsec{bnfinit$(P,\{\fl=0\},\{\var{tech}=[\,]\})$}\kbdsidx{bnfinit}\label{se:bnfinit}
+Initializes a
+\var{bnf} structure. Used in programs such as \kbd{bnfisprincipal},
+\kbd{bnfisunit} or \kbd{bnfnarrow}. By default, the results are conditional
+on the GRH, see \ref{se:GRHbnf}. The result is a
+10-component vector \var{bnf}.
+
+This implements \idx{Buchmann}'s sub-exponential algorithm for computing the
+class group, the regulator and a system of \idx{fundamental units} of the
+general algebraic number field $K$ defined by the irreducible polynomial $P$
+with integer coefficients.
+
+If the precision becomes insufficient, \kbd{gp} does not strive to compute
+the units by default ($\fl=0$).
+
+When $\fl=1$, we insist on finding the fundamental units exactly. Be
+warned that this can take a very long time when the coefficients of the
+fundamental units on the integral basis are very large. If the fundamental
+units are simply too large to be represented in this form, an error message
+is issued. They could be obtained using the so-called compact representation
+of algebraic numbers as a formal product of algebraic integers. The latter is
+implemented internally but not publicly accessible yet.
+
+$\var{tech}$ is a technical vector (empty by default, see \ref{se:GRHbnf}).
+Careful use of this parameter may speed up your computations,
+but it is mostly obsolete and you should leave it alone.
+
+\smallskip
+
+The components of a \var{bnf} or \var{sbnf} are technical and never used by
+the casual user. In fact: \emph{never access a component directly, always use
+a proper member function.} However, for the sake of completeness and internal
+documentation, their description is as follows. We use the notations
+explained in the book by H. Cohen, \emph{A Course in Computational Algebraic
+Number Theory}, Graduate Texts in Maths \key{138}, Springer-Verlag, 1993,
+Section 6.5, and subsection 6.5.5 in particular.
+
+$\var{bnf}[1]$ contains the matrix $W$, i.e.~the matrix in Hermite normal
+form giving relations for the class group on prime ideal generators
+$(\goth{p}_i)_{1\le i\le r}$.
+
+$\var{bnf}[2]$ contains the matrix $B$, i.e.~the matrix containing the
+expressions of the prime ideal factorbase in terms of the $\goth{p}_i$.
+It is an $r\times c$ matrix.
+
+$\var{bnf}[3]$ contains the complex logarithmic embeddings of the system of
+fundamental units which has been found. It is an $(r_1+r_2)\times(r_1+r_2-1)$
+matrix.
+
+$\var{bnf}[4]$ contains the matrix $M''_C$ of Archimedean components of the
+relations of the matrix $(W|B)$.
+
+$\var{bnf}[5]$ contains the prime factor base, i.e.~the list of prime
+ideals used in finding the relations.
+
+$\var{bnf}[6]$ used to contain a permutation of the prime factor base, but
+has been obsoleted. It contains a dummy $0$.
+
+$\var{bnf}[7]$ or \kbd{\var{bnf}.nf} is equal to the number field data
+$\var{nf}$ as would be given by \kbd{nfinit}.
+
+$\var{bnf}[8]$ is a vector containing the classgroup \kbd{\var{bnf}.clgp}
+as a finite abelian group, the regulator \kbd{\var{bnf}.reg}, a $1$ (used to
+contain an obsolete ``check number''), the number of roots of unity and a
+generator \kbd{\var{bnf}.tu}, the fundamental units \kbd{\var{bnf}.fu}.
+
+$\var{bnf}[9]$ is a 3-element row vector used in \tet{bnfisprincipal} only
+and obtained as follows. Let $D = U W V$ obtained by applying the
+\idx{Smith normal form} algorithm to the matrix $W$ (= $\var{bnf}[1]$) and
+let $U_r$ be the reduction of $U$ modulo $D$. The first elements of the
+factorbase are given (in terms of \kbd{bnf.gen}) by the columns of $U_r$,
+with Archimedean component $g_a$; let also $GD_a$ be the Archimedean
+components of the generators of the (principal) ideals defined by the
+\kbd{bnf.gen[i]\pow bnf.cyc[i]}. Then $\var{bnf}[9]=[U_r, g_a, GD_a]$.
+
+$\var{bnf}[10]$ is by default unused and set equal to 0. This field is used
+to store further information about the field as it becomes available, which
+is rarely needed, hence would be too expensive to compute during the initial
+\kbd{bnfinit} call. For instance, the generators of the principal ideals
+\kbd{bnf.gen[i]\pow bnf.cyc[i]} (during a call to \tet{bnrisprincipal}), or
+those corresponding to the relations in $W$ and $B$ (when the \kbd{bnf}
+internal precision needs to be increased).
+
+The library syntax is \fun{GEN}{bnfinit0}{GEN P, long flag, GEN tech = NULL, long prec}.
+
+Also available is \fun{GEN}{Buchall}{GEN P, long flag, long prec},
+corresponding to \kbd{tech = NULL}, where
+\kbd{flag} is either $0$ (default) or \tet{nf_FORCE} (insist on finding
+fundamental units). The function
+\fun{GEN}{Buchall_param}{GEN P, double c1, double c2, long nrpid, long flag, long prec} gives direct access to the technical parameters.
+
+\subsec{bnfisintnorm$(\var{bnf},x)$}\kbdsidx{bnfisintnorm}\label{se:bnfisintnorm}
+Computes a complete system of
+solutions (modulo units of positive norm) of the absolute norm equation
+$\Norm(a)=x$,
+where $a$ is an integer in $\var{bnf}$. If $\var{bnf}$ has not been certified,
+the correctness of the result depends on the validity of \idx{GRH}.
+
+See also \tet{bnfisnorm}.
+
+The library syntax is \fun{GEN}{bnfisintnorm}{GEN bnf, GEN x}.
+The function \fun{GEN}{bnfisintnormabs}{GEN bnf, GEN a}
+returns a complete system of solutions modulo units of the absolute norm
+equation $|\Norm(x)| = |a|$. As fast as \kbd{bnfisintnorm}, but solves
+the two equations $\Norm(x) = \pm a$ simultaneously.
+
+\subsec{bnfisnorm$(\var{bnf},x,\{\fl=1\})$}\kbdsidx{bnfisnorm}\label{se:bnfisnorm}
+Tries to tell whether the
+rational number $x$ is the norm of some element y in $\var{bnf}$. Returns a
+vector $[a,b]$ where $x=Norm(a)*b$. Looks for a solution which is an $S$-unit,
+with $S$ a certain set of prime ideals containing (among others) all primes
+dividing $x$. If $\var{bnf}$ is known to be \idx{Galois}, set $\fl=0$ (in
+this case, $x$ is a norm iff $b=1$). If $\fl$ is non zero the program adds to
+$S$ the following prime ideals, depending on the sign of $\fl$. If $\fl>0$,
+the ideals of norm less than $\fl$. And if $\fl<0$ the ideals dividing $\fl$.
+
+Assuming \idx{GRH}, the answer is guaranteed (i.e.~$x$ is a norm iff $b=1$),
+if $S$ contains all primes less than $12\log(\disc(\var{Bnf}))^2$, where
+$\var{Bnf}$ is the Galois closure of $\var{bnf}$.
+
+See also \tet{bnfisintnorm}.
+
+The library syntax is \fun{GEN}{bnfisnorm}{GEN bnf, GEN x, long flag}.
+
+\subsec{bnfisprincipal$(\var{bnf},x,\{\fl=1\})$}\kbdsidx{bnfisprincipal}\label{se:bnfisprincipal}
+$\var{bnf}$ being the \sidx{principal ideal}
+number field data output by \kbd{bnfinit}, and $x$ being an ideal, this
+function tests whether the ideal is principal or not. The result is more
+complete than a simple true/false answer and solves general discrete
+logarithm problem. Assume the class group is $\oplus (\Z/d_i\Z)g_i$
+(where the generators $g_i$ and their orders $d_i$ are respectively given by
+\kbd{bnf.gen} and \kbd{bnf.cyc}). The routine returns a row vector $[e,t]$,
+where $e$ is a vector of exponents $0 \leq e_i < d_i$, and $t$ is a number
+field element such that
+$$ x = (t) \prod_i  g_i^{e_i}.$$
+For \emph{given} $g_i$ (i.e. for a given \kbd{bnf}), the $e_i$ are unique,
+and $t$ is unique modulo units.
+
+In particular, $x$ is principal if and only if $e$ is the zero vector. Note
+that the empty vector, which is returned when the class number is $1$, is
+considered to be a zero vector (of dimension $0$).
+\bprog
+? K = bnfinit(y^2+23);
+? K.cyc
+%2 = [3]
+? K.gen
+%3 = [[2, 0; 0, 1]]          \\ a prime ideal above 2
+? P = idealprimedec(K,3)[1]; \\ a prime ideal above 3
+? v = bnfisprincipal(K, P)
+%5 = [[2]~, [3/4, 1/4]~]
+? idealmul(K, v[2], idealfactorback(K, K.gen, v[1]))
+%6 =
+[3 0]
+
+[0 1]
+? % == idealhnf(K, P)
+%7 = 1
+ at eprog
+
+\noindent The binary digits of \fl mean:
+
+\item $1$: If set, outputs $[e,t]$ as explained above, otherwise returns
+only $e$, which is much easier to compute. The following idiom only tests
+whether an ideal is principal:
+\bprog
+  is_principal(bnf, x) = !bnfisprincipal(bnf,x,0);
+ at eprog
+
+\item $2$: It may not be possible to recover $t$, given the initial accuracy
+to which \kbd{bnf} was computed. In that case, a warning is printed and $t$ is
+set equal to the empty vector \kbd{[]\til}. If this bit is set,
+increase the precision and recompute needed quantities until $t$ can be
+computed. Warning: setting this may induce \emph{very} lengthy computations.
+
+The library syntax is \fun{GEN}{bnfisprincipal0}{GEN bnf, GEN x, long flag}.
+Instead of the above hardcoded numerical flags, one should
+rather use an or-ed combination of the symbolic flags \tet{nf_GEN} (include
+generators, possibly a place holder if too difficult) and \tet{nf_FORCE}
+(insist on finding the generators).
+
+\subsec{bnfissunit$(\var{bnf},\var{sfu},x)$}\kbdsidx{bnfissunit}\label{se:bnfissunit}
+$\var{bnf}$ being output by
+\kbd{bnfinit}, \var{sfu} by \kbd{bnfsunit}, gives the column vector of
+exponents of $x$ on the fundamental $S$-units and the roots of unity.
+If $x$ is not a unit, outputs an empty vector.
+
+The library syntax is \fun{GEN}{bnfissunit}{GEN bnf, GEN sfu, GEN x}.
+
+\subsec{bnfisunit$(\var{bnf},x)$}\kbdsidx{bnfisunit}\label{se:bnfisunit}
+\var{bnf} being the number field data
+output by \kbd{bnfinit} and $x$ being an algebraic number (type integer,
+rational or polmod), this outputs the decomposition of $x$ on the fundamental
+units and the roots of unity if $x$ is a unit, the empty vector otherwise.
+More precisely, if $u_1$,\dots,$u_r$ are the fundamental units, and $\zeta$
+is the generator of the group of roots of unity (\kbd{bnf.tu}), the output is
+a vector $[x_1,\dots,x_r,x_{r+1}]$ such that $x=u_1^{x_1}\cdots
+u_r^{x_r}\cdot\zeta^{x_{r+1}}$. The $x_i$ are integers for $i\le r$ and is an
+integer modulo the order of $\zeta$ for $i=r+1$.
+
+Note that \var{bnf} need not contain the fundamental unit explicitly:
+\bprog
+? setrand(1); bnf = bnfinit(x^2-x-100000);
+? bnf.fu
+  ***   at top-level: bnf.fu
+  ***                     ^--
+  *** _.fu: missing units in .fu.
+? u = [119836165644250789990462835950022871665178127611316131167, \
+       379554884019013781006303254896369154068336082609238336]~;
+? bnfisunit(bnf, u)
+%3 = [-1, Mod(0, 2)]~
+ at eprog\noindent The given $u$ is the inverse of the fundamental unit
+implicitly stored in \var{bnf}. In this case, the fundamental unit was not
+computed and stored in algebraic form since the default accuracy was too
+low. (Re-run the command at \bs g1 or higher to see such diagnostics.)
+
+The library syntax is \fun{GEN}{bnfisunit}{GEN bnf, GEN x}.
+
+\subsec{bnfnarrow$(\var{bnf})$}\kbdsidx{bnfnarrow}\label{se:bnfnarrow}
+$\var{bnf}$ being as output by
+\kbd{bnfinit}, computes the narrow class group of $\var{bnf}$. The output is
+a 3-component row vector $v$ analogous to the corresponding class group
+component \kbd{\var{bnf}.clgp} (\kbd{\var{bnf}[8][1]}): the first component
+is the narrow class number \kbd{$v$.no}, the second component is a vector
+containing the SNF\sidx{Smith normal form} cyclic components \kbd{$v$.cyc} of
+the narrow class group, and the third is a vector giving the generators of
+the corresponding \kbd{$v$.gen} cyclic groups. Note that this function is a
+special case of \kbd{bnrinit}.
+
+The library syntax is \fun{GEN}{buchnarrow}{GEN bnf}.
+
+\subsec{bnfsignunit$(\var{bnf})$}\kbdsidx{bnfsignunit}\label{se:bnfsignunit}
+$\var{bnf}$ being as output by
+\kbd{bnfinit}, this computes an $r_1\times(r_1+r_2-1)$ matrix having $\pm1$
+components, giving the signs of the real embeddings of the fundamental units.
+The following functions compute generators for the totally positive units:
+
+\bprog
+/* exponents of totally positive units generators on bnf.tufu */
+tpuexpo(bnf)=
+{ my(S,d,K);
+
+  S = bnfsignunit(bnf); d = matsize(S);
+  S = matrix(d[1],d[2], i,j, if (S[i,j] < 0, 1,0));
+  S = concat(vectorv(d[1],i,1), S);   \\ add sign(-1)
+  K = lift(matker(S * Mod(1,2)));
+  if (K, mathnfmodid(K, 2), 2*matid(d[1]))
+}
+
+/* totally positive units */
+tpu(bnf)=
+{ my(vu = bnf.tufu, ex = tpuexpo(bnf));
+
+  vector(#ex-1, i, factorback(vu, ex[,i+1]))  \\ ex[,1] is 1
+}
+ at eprog
+
+The library syntax is \fun{GEN}{signunits}{GEN bnf}.
+
+\subsec{bnfsunit$(\var{bnf},S)$}\kbdsidx{bnfsunit}\label{se:bnfsunit}
+Computes the fundamental $S$-units of the
+number field $\var{bnf}$ (output by \kbd{bnfinit}), where $S$ is a list of
+prime ideals (output by \kbd{idealprimedec}). The output is a vector $v$ with
+6 components.
+
+$v[1]$ gives a minimal system of (integral) generators of the $S$-unit group
+modulo the unit group.
+
+$v[2]$ contains technical data needed by \kbd{bnfissunit}.
+
+$v[3]$ is an empty vector (used to give the logarithmic embeddings of the
+generators in $v[1]$ in version 2.0.16).
+
+$v[4]$ is the $S$-regulator (this is the product of the regulator, the
+determinant of $v[2]$ and the natural logarithms of the norms of the ideals
+in $S$).
+
+$v[5]$ gives the $S$-class group structure, in the usual format
+(a row vector whose three components give in order the $S$-class number,
+the cyclic components and the generators).
+
+$v[6]$ is a copy of $S$.
+
+The library syntax is \fun{GEN}{bnfsunit}{GEN bnf, GEN S, long prec}.
+
+\subsec{bnrL1$(\var{bnr}, \{H\}, \{\fl=0\})$}\kbdsidx{bnrL1}\label{se:bnrL1}
+Let \var{bnr} be the number field data output by \kbd{bnrinit(,,1)} and
+\var{H} be a square matrix defining a congruence subgroup of the
+ray class group corresponding to \var{bnr} (the trivial congruence subgroup
+if omitted). This function returns, for each \idx{character} $\chi$ of the ray
+class group which is trivial on $H$, the value at $s = 1$ (or $s = 0$) of the
+abelian $L$-function associated to $\chi$. For the value at $s = 0$, the
+function returns in fact for each $\chi$ a vector $[r_\chi, c_\chi]$ where
+$$L(s, \chi) = c \cdot s^r + O(s^{r + 1})$$
+\noindent near $0$.
+
+The argument \fl\ is optional, its binary digits
+mean 1: compute at $s = 0$ if unset or $s = 1$ if set, 2: compute the
+primitive $L$-function associated to $\chi$ if unset or the $L$-function
+with Euler factors at prime ideals dividing the modulus of \var{bnr} removed
+if set (that is $L_S(s, \chi)$, where $S$ is the
+set of infinite places of the number field together with the finite prime
+ideals dividing the modulus of \var{bnr}), 3: return also the character if
+set.
+\bprog
+K = bnfinit(x^2-229);
+bnr = bnrinit(K,1,1);
+bnrL1(bnr)
+ at eprog\noindent
+returns the order and the first non-zero term of $L(s, \chi)$ at $s = 0$
+where $\chi$ runs through the characters of the class group of
+$K = \Q(\sqrt{229})$. Then
+\bprog
+bnr2 = bnrinit(K,2,1);
+bnrL1(bnr2,,2)
+ at eprog\noindent
+returns the order and the first non-zero terms of $L_S(s, \chi)$ at $s = 0$
+where $\chi$ runs through the characters of the class group of $K$ and $S$ is
+the set of infinite places of $K$ together with the finite prime $2$. Note
+that the ray class group modulo $2$ is in fact the class group, so
+\kbd{bnrL1(bnr2,0)} returns the same answer as \kbd{bnrL1(bnr,0)}.
+
+This function will fail with the message
+\bprog
+ *** bnrL1: overflow in zeta_get_N0 [need too many primes].
+ at eprog\noindent if the approximate functional equation requires us to sum
+too many terms (if the discriminant of $K$ is too large).
+
+The library syntax is \fun{GEN}{bnrL1}{GEN bnr, GEN H = NULL, long flag, long prec}.
+
+\subsec{bnrclassno$(A,\{B\},\{C\})$}\kbdsidx{bnrclassno}\label{se:bnrclassno}
+Let $A$, $B$, $C$ define a class field $L$ over a ground field $K$
+(of type \kbd{[\var{bnr}]},
+\kbd{[\var{bnr}, \var{subgroup}]},
+or \kbd{[\var{bnf}, \var{modulus}]},
+or \kbd{[\var{bnf}, \var{modulus},\var{subgroup}]},
+\secref{se:CFT}); this function returns the relative degree $[L:K]$.
+
+In particular if $A$ is a \var{bnf} (with units), and $B$ a modulus,
+this function returns the corresponding ray class number modulo $B$.
+One can input the associated \var{bid} (with generators if the subgroup
+$C$ is non trivial) for $B$ instead of the module itself, saving some time.
+
+This function is faster than \kbd{bnrinit} and should be used if only the
+ray class number is desired. See \tet{bnrclassnolist} if you need ray class
+numbers for all moduli less than some bound.
+
+The library syntax is \fun{GEN}{bnrclassno0}{GEN A, GEN B = NULL, GEN C = NULL}.
+Also available is
+\fun{GEN}{bnrclassno}{GEN bnf,GEN f} to compute the ray class number
+modulo~$f$.
+
+\subsec{bnrclassnolist$(\var{bnf},\var{list})$}\kbdsidx{bnrclassnolist}\label{se:bnrclassnolist}
+$\var{bnf}$ being as
+output by \kbd{bnfinit}, and \var{list} being a list of moduli (with units) as
+output by \kbd{ideallist} or \kbd{ideallistarch}, outputs the list of the
+class numbers of the corresponding ray class groups. To compute a single
+class number, \tet{bnrclassno} is more efficient.
+
+\bprog
+? bnf = bnfinit(x^2 - 2);
+? L = ideallist(bnf, 100, 2);
+? H = bnrclassnolist(bnf, L);
+? H[98]
+%4 = [1, 3, 1]
+? l = L[1][98]; ids = vector(#l, i, l[i].mod[1])
+%5 = [[98, 88; 0, 1], [14, 0; 0, 7], [98, 10; 0, 1]]
+ at eprog
+The weird \kbd{l[i].mod[1]}, is the first component of \kbd{l[i].mod}, i.e.
+the finite part of the conductor. (This is cosmetic: since by construction
+the Archimedean part is trivial, I do not want to see it). This tells us that
+the ray class groups modulo the ideals of norm 98 (printed as \kbd{\%5}) have
+respectively order $1$, $3$ and $1$. Indeed, we may check directly:
+\bprog
+? bnrclassno(bnf, ids[2])
+%6 = 3
+ at eprog
+
+The library syntax is \fun{GEN}{bnrclassnolist}{GEN bnf, GEN list}.
+
+\subsec{bnrconductor$(A,\{B\},\{C\},\{\fl=0\})$}\kbdsidx{bnrconductor}\label{se:bnrconductor}
+Conductor $f$ of the subfield of a ray class field as defined by $[A,B,C]$
+(of type \kbd{[\var{bnr}]},
+\kbd{[\var{bnr}, \var{subgroup}]},
+\kbd{[\var{bnf}, \var{modulus}]} or
+\kbd{[\var{bnf}, \var{modulus}, \var{subgroup}]},
+\secref{se:CFT})
+
+If $\fl = 0$, returns $f$.
+
+If $\fl = 1$, returns $[f, Cl_f, H]$, where $Cl_f$ is the ray class group
+modulo $f$, as a finite abelian group; finally $H$ is the subgroup of $Cl_f$
+defining the extension.
+
+If $\fl = 2$, returns $[f, \var{bnr}(f), H]$, as above except $Cl_f$ is
+replaced by a \kbd{bnr} structure, as output by $\tet{bnrinit}(,f,1)$.
+
+The library syntax is \fun{GEN}{bnrconductor0}{GEN A, GEN B = NULL, GEN C = NULL, long flag}.
+
+Also available is \fun{GEN}{bnrconductor}{GEN bnr, GEN H, long flag}
+
+\subsec{bnrconductorofchar$(\var{bnr},\var{chi})$}\kbdsidx{bnrconductorofchar}\label{se:bnrconductorofchar}
+\var{bnr} being a big
+ray number field as output by \kbd{bnrinit}, and \var{chi} being a row vector
+representing a \idx{character} as expressed on the generators of the ray
+class group, gives the conductor of this character as a modulus.
+
+The library syntax is \fun{GEN}{bnrconductorofchar}{GEN bnr, GEN chi}.
+
+\subsec{bnrdisc$(A,\{B\},\{C\},\{\fl=0\})$}\kbdsidx{bnrdisc}\label{se:bnrdisc}
+$A$, $B$, $C$ defining a class field $L$ over a ground field $K$
+(of type \kbd{[\var{bnr}]},
+\kbd{[\var{bnr}, \var{subgroup}]},
+\kbd{[\var{bnf}, \var{modulus}]} or
+\kbd{[\var{bnf}, \var{modulus}, \var{subgroup}]},
+\secref{se:CFT}), outputs data $[N,r_1,D]$ giving the discriminant and
+signature of $L$, depending on the binary digits of \fl:
+
+\item 1: if this bit is unset, output absolute data related to $L/\Q$:
+$N$ is the absolute degree $[L:\Q]$, $r_1$ the number of real places of $L$,
+and $D$ the discriminant of $L/\Q$. Otherwise, output relative data for $L/K$:
+$N$ is the relative degree $[L:K]$, $r_1$ is the number of real places of $K$
+unramified in $L$ (so that the number of real places of $L$ is equal to $r_1$
+times $N$), and $D$ is the relative discriminant ideal of $L/K$.
+
+\item 2: if this bit is set and if the modulus is not the conductor of $L$,
+only return 0.
+
+The library syntax is \fun{GEN}{bnrdisc0}{GEN A, GEN B = NULL, GEN C = NULL, long flag}.
+
+\subsec{bnrdisclist$(\var{bnf},\var{bound},\{\var{arch}\})$}\kbdsidx{bnrdisclist}\label{se:bnrdisclist}
+$\var{bnf}$ being as output by \kbd{bnfinit} (with units), computes a
+list of discriminants of Abelian extensions of the number field by increasing
+modulus norm up to bound \var{bound}. The ramified Archimedean places are
+given by \var{arch}; all possible values are taken if \var{arch} is omitted.
+
+The alternative syntax $\kbd{bnrdisclist}(\var{bnf},\var{list})$ is
+supported, where \var{list} is as output by \kbd{ideallist} or
+\kbd{ideallistarch} (with units), in which case \var{arch} is disregarded.
+
+The output $v$ is a vector of vectors, where $v[i][j]$ is understood to be in
+fact $V[2^{15}(i-1)+j]$ of a unique big vector $V$. (This awkward scheme
+allows for larger vectors than could be otherwise represented.)
+
+$V[k]$ is itself a vector $W$, whose length is the number of ideals of norm
+$k$. We consider first the case where \var{arch} was specified. Each
+component of $W$ corresponds to an ideal $m$ of norm $k$, and
+gives invariants associated to the ray class field $L$ of $\var{bnf}$ of
+conductor $[m, \var{arch}]$. Namely, each contains a vector $[m,d,r,D]$ with
+the following meaning: $m$ is the prime ideal factorization of the modulus,
+$d = [L:\Q]$ is the absolute degree of $L$, $r$ is the number of real places
+of $L$, and $D$ is the factorization of its absolute discriminant. We set $d
+= r = D = 0$ if $m$ is not the finite part of a conductor.
+
+If \var{arch} was omitted, all $t = 2^{r_1}$ possible values are taken and a
+component of $W$ has the form $[m, [[d_1,r_1,D_1], \dots, [d_t,r_t,D_t]]]$,
+where $m$ is the finite part of the conductor as above, and
+$[d_i,r_i,D_i]$ are the invariants of the ray class field of conductor
+$[m,v_i]$, where $v_i$ is the $i$-th Archimedean component, ordered by
+inverse lexicographic order; so $v_1 = [0,\dots,0]$, $v_2 = [1,0\dots,0]$,
+etc. Again, we set $d_i = r_i = D_i = 0$ if $[m,v_i]$ is not a conductor.
+
+Finally, each prime ideal $pr = [p,\alpha,e,f,\beta]$ in the prime
+factorization $m$ is coded as the integer $p\cdot n^2+(f-1)\cdot n+(j-1)$,
+where $n$ is the degree of the base field and $j$ is such that
+
+\kbd{pr = idealprimedec(\var{nf},p)[j]}.
+
+\noindent $m$ can be decoded using \tet{bnfdecodemodule}.
+
+Note that to compute such data for a single field, either \tet{bnrclassno}
+or \tet{bnrdisc} is more efficient.
+
+The library syntax is \fun{GEN}{bnrdisclist0}{GEN bnf, GEN bound, GEN arch = NULL}.
+
+\subsec{bnrinit$(\var{bnf},f,\{\fl=0\})$}\kbdsidx{bnrinit}\label{se:bnrinit}
+$\var{bnf}$ is as
+output by \kbd{bnfinit}, $f$ is a modulus, initializes data linked to
+the ray class group structure corresponding to this module, a so-called
+\var{bnr} structure. One can input the associated \var{bid} with generators
+for $f$ instead of the module itself, saving some time.
+(As in \tet{idealstar}, the finite part of the conductor may be given
+by a factorization into prime ideals, as produced by \tet{idealfactor}.)
+
+The following member functions are available
+on the result: \kbd{.bnf} is the underlying \var{bnf},
+\kbd{.mod} the modulus, \kbd{.bid} the \var{bid} structure associated to the
+modulus; finally, \kbd{.clgp}, \kbd{.no}, \kbd{.cyc}, \kbd{.gen} refer to the
+ray class group (as a finite abelian group), its cardinality, its elementary
+divisors, its generators (only computed if $\fl = 1$).
+
+The last group of functions are different from the members of the underlying
+\var{bnf}, which refer to the class group; use \kbd{\var{bnr}.bnf.\var{xxx}}
+to access these, e.g.~\kbd{\var{bnr}.bnf.cyc} to get the cyclic decomposition
+of the class group.
+
+They are also different from the members of the underlying \var{bid}, which
+refer to $(\Z_K/f)^*$; use \kbd{\var{bnr}.bid.\var{xxx}} to access these,
+e.g.~\kbd{\var{bnr}.bid.no} to get $\phi(f)$.
+
+If $\fl=0$ (default), the generators of the ray class group are not computed,
+which saves time. Hence \kbd{\var{bnr}.gen} would produce an error.
+
+If $\fl=1$, as the default, except that generators are computed.
+
+The library syntax is \fun{GEN}{bnrinit0}{GEN bnf, GEN f, long flag}.
+Instead the above  hardcoded  numerical flags,  one should rather use
+\fun{GEN}{Buchray}{GEN bnf, GEN module, long flag}
+where flag is an or-ed combination of \kbd{nf\_GEN} (include generators)
+and \kbd{nf\_INIT} (if omitted, return just the cardinal of the ray class group
+and its structure), possibly 0.
+
+\subsec{bnrisconductor$(A,\{B\},\{C\})$}\kbdsidx{bnrisconductor}\label{se:bnrisconductor}
+$A$, $B$, $C$ represent
+an extension of the base field, given by class field theory
+(see~\secref{se:CFT}). Outputs 1 if this modulus is the conductor, and 0
+otherwise. This is slightly faster than \kbd{bnrconductor}.
+
+The library syntax is \fun{long}{bnrisconductor0}{GEN A, GEN B = NULL, GEN C = NULL}.
+
+\subsec{bnrisprincipal$(\var{bnr},x,\{\fl=1\})$}\kbdsidx{bnrisprincipal}\label{se:bnrisprincipal}
+\var{bnr} being the
+number field data which is output by \kbd{bnrinit}$(,,1)$ and $x$ being an
+ideal in any form, outputs the components of $x$ on the ray class group
+generators in a way similar to \kbd{bnfisprincipal}. That is a 2-component
+vector $v$ where $v[1]$ is the vector of components of $x$ on the ray class
+group generators, $v[2]$ gives on the integral basis an element $\alpha$ such
+that $x=\alpha\prod_ig_i^{x_i}$.
+
+If $\fl=0$, outputs only $v_1$. In that case, \var{bnr} need not contain the
+ray class group generators, i.e.~it may be created with \kbd{bnrinit}$(,,0)$
+If $x$ is not coprime to the modulus of \var{bnr} the result is undefined.
+
+The library syntax is \fun{GEN}{bnrisprincipal}{GEN bnr, GEN x, long flag}.
+Instead of hardcoded  numerical flags,  one should rather
+use
+\fun{GEN}{isprincipalray}{GEN bnr, GEN x} for $\kbd{flag} = 0$, and if you
+want generators:
+\bprog
+  bnrisprincipal(bnr, x, nf_GEN)
+ at eprog
+
+\subsec{bnrrootnumber$(\var{bnr},\var{chi},\{\fl=0\})$}\kbdsidx{bnrrootnumber}\label{se:bnrrootnumber}
+If $\chi=\var{chi}$ is a
+\idx{character} over \var{bnr}, not necessarily primitive, let
+$L(s,\chi) = \sum_{id} \chi(id) N(id)^{-s}$ be the associated
+\idx{Artin L-function}. Returns the so-called \idx{Artin root number}, i.e.~the
+complex number $W(\chi)$ of modulus 1 such that
+%
+$$\Lambda(1-s,\chi) = W(\chi) \Lambda(s,\overline{\chi})$$
+%
+\noindent where $\Lambda(s,\chi) = A(\chi)^{s/2}\gamma_\chi(s) L(s,\chi)$ is
+the enlarged L-function associated to $L$.
+
+The generators of the ray class group are needed, and you can set $\fl=1$ if
+the character is known to be primitive. Example:
+
+\bprog
+bnf = bnfinit(x^2 - x - 57);
+bnr = bnrinit(bnf, [7,[1,1]], 1);
+bnrrootnumber(bnr, [2,1])
+ at eprog\noindent
+returns the root number of the character $\chi$ of
+$\Cl_{7\infty_1\infty_2}(\Q(\sqrt{229}))$ defined by $\chi(g_1^ag_2^b)
+= \zeta_1^{2a}\zeta_2^b$. Here $g_1, g_2$ are the generators of the
+ray-class group given by \kbd{bnr.gen} and $\zeta_1 = e^{2i\pi/N_1},
+\zeta_2 = e^{2i\pi/N_2}$ where $N_1, N_2$ are the orders of $g_1$ and
+$g_2$ respectively ($N_1=6$ and $N_2=3$ as \kbd{bnr.cyc} readily tells us).
+
+The library syntax is \fun{GEN}{bnrrootnumber}{GEN bnr, GEN chi, long flag, long prec}.
+
+\subsec{bnrstark$(\var{bnr},\{\var{subgroup}\})$}\kbdsidx{bnrstark}\label{se:bnrstark}
+\var{bnr} being as output by \kbd{bnrinit(,,1)}, finds a relative equation
+for the class field corresponding to the modulus in \var{bnr} and the given
+congruence subgroup (as usual, omit $\var{subgroup}$ if you want the whole ray
+class group).
+
+The main variable of \var{bnr} must not be $x$, and the ground field and the
+class field must be totally real. When the base field is $\Q$, the vastly
+simpler \tet{galoissubcyclo} is used instead. Here is an example:
+\bprog
+bnf = bnfinit(y^2 - 3);
+bnr = bnrinit(bnf, 5, 1);
+bnrstark(bnr)
+ at eprog\noindent
+returns the ray class field of $\Q(\sqrt{3})$ modulo $5$. Usually, one wants
+to apply to the result one of
+\bprog
+rnfpolredabs(bnf, pol, 16)     \\@com compute a reduced relative polynomial
+rnfpolredabs(bnf, pol, 16 + 2) \\@com compute a reduced absolute polynomial
+ at eprog
+
+The routine uses \idx{Stark units} and needs to find a suitable auxiliary
+conductor, which may not exist when the class field is not cyclic over the
+base. In this case \kbd{bnrstark} is allowed to return a vector of
+polynomials defining \emph{independent} relative extensions, whose compositum
+is the requested class field. It was decided that it was more useful
+to keep the extra information thus made available, hence the user has to take
+the compositum herself.
+
+Even if it exists, the auxiliary conductor may be so large that later
+computations become unfeasible. (And of course, Stark's conjecture may simply
+be wrong.) In case of difficulties, try \tet{rnfkummer}:
+\bprog
+? bnr = bnrinit(bnfinit(y^8-12*y^6+36*y^4-36*y^2+9,1), 2, 1);
+? bnrstark(bnr)
+  ***   at top-level: bnrstark(bnr)
+  ***                 ^-------------
+  *** bnrstark: need 3919350809720744 coefficients in initzeta.
+  *** Computation impossible.
+? lift( rnfkummer(bnr) )
+time = 24 ms.
+%2 = x^2 + (1/3*y^6 - 11/3*y^4 + 8*y^2 - 5)
+ at eprog
+
+The library syntax is \fun{GEN}{bnrstark}{GEN bnr, GEN subgroup = NULL, long prec}.
+
+\subsec{dirzetak$(\var{nf},b)$}\kbdsidx{dirzetak}\label{se:dirzetak}
+Gives as a vector the first $b$
+coefficients of the \idx{Dedekind} zeta function of the number field $\var{nf}$
+considered as a \idx{Dirichlet series}.
+
+The library syntax is \fun{GEN}{dirzetak}{GEN nf, GEN b}.
+
+\subsec{factornf$(x,t)$}\kbdsidx{factornf}\label{se:factornf}
+Factorization of the univariate polynomial $x$
+over the number field defined by the (univariate) polynomial $t$. $x$ may
+have coefficients in $\Q$ or in the number field. The algorithm reduces to
+factorization over $\Q$ (\idx{Trager}'s trick). The direct approach of
+\tet{nffactor}, which uses \idx{van Hoeij}'s method in a relative setting, is
+in general faster.
+
+The main variable of $t$ must be of \emph{lower} priority than that of $x$
+(see \secref{se:priority}). However if non-rational number field elements
+occur (as polmods or polynomials) as coefficients of $x$, the variable of
+these polmods \emph{must} be the same as the main variable of $t$. For
+example
+
+\bprog
+? factornf(x^2 + Mod(y, y^2+1), y^2+1);
+? factornf(x^2 + y, y^2+1); \\@com these two are OK
+? factornf(x^2 + Mod(z,z^2+1), y^2+1)
+  ***   at top-level: factornf(x^2+Mod(z,z
+  ***                 ^--------------------
+  *** factornf: inconsistent data in rnf function.
+? factornf(x^2 + z, y^2+1)
+  ***   at top-level: factornf(x^2+z,y^2+1
+  ***                 ^--------------------
+  *** factornf: incorrect variable in rnf function.
+ at eprog
+
+The library syntax is \fun{GEN}{polfnf}{GEN x, GEN t}.
+
+\subsec{galoisexport$(\var{gal},\{\fl\})$}\kbdsidx{galoisexport}\label{se:galoisexport}
+\var{gal} being be a Galois group as output by \tet{galoisinit},
+export the underlying permutation group as a string suitable
+for (no flags or $\fl=0$) GAP or ($\fl=1$) Magma. The following example
+compute the index of the underlying abstract group in the GAP library:
+\bprog
+? G = galoisinit(x^6+108);
+? s = galoisexport(G)
+%2 = "Group((1, 2, 3)(4, 5, 6), (1, 4)(2, 6)(3, 5))"
+? extern("echo \"IdGroup("s");\" | gap -q")
+%3 = [6, 1]
+? galoisidentify(G)
+%4 = [6, 1]
+ at eprog\noindent
+This command also accepts subgroups returned by \kbd{galoissubgroups}.
+
+To \emph{import} a GAP permutation into gp (for \tet{galoissubfields} for
+instance), the following GAP function may be useful:
+\bprog
+PermToGP := function(p, n)
+  return Permuted([1..n],p);
+end;
+
+gap> p:= (1,26)(2,5)(3,17)(4,32)(6,9)(7,11)(8,24)(10,13)(12,15)(14,27)
+  (16,22)(18,28)(19,20)(21,29)(23,31)(25,30)
+gap> PermToGP(p,32);
+[ 26, 5, 17, 32, 2, 9, 11, 24, 6, 13, 7, 15, 10, 27, 12, 22, 3, 28, 20, 19,
+  29, 16, 31, 8, 30, 1, 14, 18, 21, 25, 23, 4 ]
+ at eprog
+
+The library syntax is \fun{GEN}{galoisexport}{GEN gal, long flag}.
+
+\subsec{galoisfixedfield$(\var{gal},\var{perm},\{\fl\},\{v=y\})$}\kbdsidx{galoisfixedfield}\label{se:galoisfixedfield}
+\var{gal} being be a Galois group as output by \tet{galoisinit} and
+\var{perm} an element of $\var{gal}.group$, a vector of such elements
+or a subgroup of \var{gal} as returned by galoissubgroups,
+computes the fixed field of \var{gal} by the automorphism defined by the
+permutations \var{perm} of the roots $\var{gal}.roots$. $P$ is guaranteed to
+be squarefree modulo $\var{gal}.p$.
+
+If no flags or $\fl=0$, output format is the same as for \tet{nfsubfield},
+returning $[P,x]$ such that $P$ is a polynomial defining the fixed field, and
+$x$ is a root of $P$ expressed as a polmod in $\var{gal}.pol$.
+
+If $\fl=1$ return only the polynomial $P$.
+
+If $\fl=2$ return $[P,x,F]$ where $P$ and $x$ are as above and $F$ is the
+factorization of $\var{gal}.pol$ over the field defined by $P$, where
+variable $v$ ($y$ by default) stands for a root of $P$. The priority of $v$
+must be less than the priority of the variable of $\var{gal}.pol$ (see
+\secref{se:priority}). Example:
+
+\bprog
+? G = galoisinit(x^4+1);
+? galoisfixedfield(G,G.group[2],2)
+%2 = [x^2 + 2, Mod(x^3 + x, x^4 + 1), [x^2 - y*x - 1, x^2 + y*x - 1]]
+ at eprog\noindent
+computes the factorization  $x^4+1=(x^2-\sqrt{-2}x-1)(x^2+\sqrt{-2}x-1)$
+
+The library syntax is \fun{GEN}{galoisfixedfield}{GEN gal, GEN perm, long flag, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{galoisgetpol$(a,\{b\},\{s\})$}\kbdsidx{galoisgetpol}\label{se:galoisgetpol}
+Query the galpol package for a polynomial with Galois group isomorphic to
+GAP4(a,b), totally real if $s=1$ (default) and totally complex if $s=2$. The
+output is a vector [\kbd{pol}, \kbd{den}] where
+
+\item  \kbd{pol} is the polynomial of degree $a$
+
+\item \kbd{den} is the denominator of \kbd{nfgaloisconj(pol)}.
+Pass it as an optional argument to \tet{galoisinit} or \tet{nfgaloisconj} to
+speed them up:
+\bprog
+? [pol,den] = galoisgetpol(64,4,1);
+? G = galoisinit(pol);
+time = 352ms
+? galoisinit(pol, den);  \\ passing 'den' speeds up the computation
+time = 264ms
+? % == %`
+%4 = 1  \\ same answer
+ at eprog
+If $b$ and $s$ are omitted, return the number of isomorphism classes of
+groups of order $a$.
+
+The library syntax is \fun{GEN}{galoisgetpol}{long a, long b, long s}.
+Also available is \fun{GEN}{galoisnbpol}{long a} when $b$ and $s$
+are omitted.
+
+\subsec{galoisidentify$(\var{gal})$}\kbdsidx{galoisidentify}\label{se:galoisidentify}
+\var{gal} being be a Galois group as output by \tet{galoisinit},
+output the isomorphism class of the underlying abstract group as a
+two-components vector $[o,i]$, where $o$ is the group order, and $i$ is the
+group index in the GAP4 Small Group library, by Hans Ulrich Besche, Bettina
+Eick and Eamonn O'Brien.
+
+This command also accepts subgroups returned by \kbd{galoissubgroups}.
+
+The current implementation is limited to degree less or equal to $127$.
+Some larger ``easy'' orders are also supported.
+
+The output is similar to the output of the function \kbd{IdGroup} in GAP4.
+Note that GAP4 \kbd{IdGroup} handles all groups of order less than $2000$
+except $1024$, so you can use \tet{galoisexport} and GAP4 to identify large
+Galois groups.
+
+The library syntax is \fun{GEN}{galoisidentify}{GEN gal}.
+
+\subsec{galoisinit$(\var{pol},\{\var{den}\})$}\kbdsidx{galoisinit}\label{se:galoisinit}
+Computes the Galois group
+and all necessary information for computing the fixed fields of the
+Galois extension $K/\Q$ where $K$ is the number field defined by
+$\var{pol}$ (monic irreducible polynomial in $\Z[X]$ or
+a number field as output by \tet{nfinit}). The extension $K/\Q$ must be
+Galois with Galois group ``weakly'' super-solvable, see below;
+returns 0 otherwise. Hence this permits to quickly check whether a polynomial
+of order strictly less than $36$ is Galois or not.
+
+The algorithm used is an improved version of the paper
+``An efficient algorithm for the computation of Galois automorphisms'',
+Bill Allombert, Math.~Comp, vol.~73, 245, 2001, pp.~359--375.
+
+A group $G$ is said to be ``weakly'' super-solvable if there exists a
+normal series
+
+$\{1\} = H_0 \triangleleft H_1 \triangleleft \cdots \triangleleft H_{n-1}
+\triangleleft H_n$
+
+such that each $H_i$ is normal in $G$ and for $i<n$, each quotient group
+$H_{i+1}/H_i$ is cyclic, and either $H_n=G$ (then $G$ is super-solvable) or
+$G/H_n$ is isomorphic to either $A_4$ or $S_4$.
+
+In practice, almost all small groups are WKSS, the exceptions having order
+36(1 exception), 48(2), 56(1), 60(1), 72(5), 75(1), 80(1), 96(10) and $\geq
+108$.
+
+This function is a prerequisite for most of the \kbd{galois}$xxx$ routines.
+For instance:
+
+\bprog
+P = x^6 + 108;
+G = galoisinit(P);
+L = galoissubgroups(G);
+vector(#L, i, galoisisabelian(L[i],1))
+vector(#L, i, galoisidentify(L[i]))
+ at eprog
+
+The output is an 8-component vector \var{gal}.
+
+$\var{gal}[1]$ contains the polynomial \var{pol}
+(\kbd{\var{gal}.pol}).
+
+$\var{gal}[2]$ is a three-components vector $[p,e,q]$ where $p$ is a
+prime number (\kbd{\var{gal}.p}) such that \var{pol} totally split
+modulo $p$ , $e$ is an integer and $q=p^e$ (\kbd{\var{gal}.mod}) is the
+modulus of the roots in \kbd{\var{gal}.roots}.
+
+$\var{gal}[3]$ is a vector $L$ containing the $p$-adic roots of
+\var{pol} as integers implicitly modulo \kbd{\var{gal}.mod}.
+(\kbd{\var{gal}.roots}).
+
+$\var{gal}[4]$ is the inverse of the Vandermonde matrix of the
+$p$-adic roots of \var{pol}, multiplied by $\var{gal}[5]$.
+
+$\var{gal}[5]$ is a multiple of the least common denominator of the
+automorphisms expressed as polynomial in a root of \var{pol}.
+
+$\var{gal}[6]$ is the Galois group $G$ expressed as a vector of
+permutations of $L$ (\kbd{\var{gal}.group}).
+
+$\var{gal}[7]$ is a generating subset $S=[s_1,\ldots,s_g]$ of $G$
+expressed as a vector of permutations of $L$ (\kbd{\var{gal}.gen}).
+
+$\var{gal}[8]$ contains the relative orders $[o_1,\ldots,o_g]$ of
+the generators of $S$ (\kbd{\var{gal}.orders}).
+
+Let $H_n$ be as above, we have the following properties:
+
+\quad\item if $G/H_n\simeq A_4$ then $[o_1,\ldots,o_g]$ ends by
+$[2,2,3]$.
+
+\quad\item if $G/H_n\simeq S_4$ then $[o_1,\ldots,o_g]$ ends by
+$[2,2,3,2]$.
+
+\quad\item for $1\leq i \leq g$ the subgroup of $G$ generated by
+$[s_1,\ldots,s_g]$ is normal, with the exception of $i=g-2$ in the
+$A_4$ case and of $i=g-3$ in the $S_A$ case.
+
+\quad\item the relative order $o_i$ of $s_i$ is its order in the
+quotient group $G/\langle s_1,\ldots,s_{i-1}\rangle$, with the same
+exceptions.
+
+\quad\item for any $x\in G$ there exists a unique family
+$[e_1,\ldots,e_g]$ such that (no exceptions):
+
+-- for $1\leq i \leq g$ we have $0\leq e_i<o_i$
+
+-- $x=g_1^{e_1}g_2^{e_2}\ldots g_n^{e_n}$
+
+If present $den$ must be a suitable value for $\var{gal}[5]$.
+
+The library syntax is \fun{GEN}{galoisinit}{GEN pol, GEN den = NULL}.
+
+\subsec{galoisisabelian$(\var{gal},\{\fl=0\})$}\kbdsidx{galoisisabelian}\label{se:galoisisabelian}
+\var{gal} being as output by \kbd{galoisinit}, return $0$ if
+\var{gal} is not an abelian group, and the HNF matrix of \var{gal} over
+\kbd{gal.gen} if $fl=0$, $1$ if $fl=1$.
+
+This command also accepts subgroups returned by \kbd{galoissubgroups}.
+
+The library syntax is \fun{GEN}{galoisisabelian}{GEN gal, long flag}.
+
+\subsec{galoisisnormal$(\var{gal},\var{subgrp})$}\kbdsidx{galoisisnormal}\label{se:galoisisnormal}
+\var{gal} being as output by \kbd{galoisinit}, and \var{subgrp} a subgroup
+of \var{gal} as output by \kbd{galoissubgroups},return $1$ if \var{subgrp} is a
+normal subgroup of \var{gal}, else return 0.
+
+This command also accepts subgroups returned by \kbd{galoissubgroups}.
+
+The library syntax is \fun{long}{galoisisnormal}{GEN gal, GEN subgrp}.
+
+\subsec{galoispermtopol$(\var{gal},\var{perm})$}\kbdsidx{galoispermtopol}\label{se:galoispermtopol}
+\var{gal} being a
+Galois group as output by \kbd{galoisinit} and \var{perm} a element of
+$\var{gal}.group$, return the polynomial defining the Galois
+automorphism, as output by \kbd{nfgaloisconj}, associated with the
+permutation \var{perm} of the roots $\var{gal}.roots$. \var{perm} can
+also be a vector or matrix, in this case, \kbd{galoispermtopol} is
+applied to all components recursively.
+
+\noindent Note that
+\bprog
+G = galoisinit(pol);
+galoispermtopol(G, G[6])~
+ at eprog\noindent
+is equivalent to \kbd{nfgaloisconj(pol)}, if degree of \var{pol} is greater
+or equal to $2$.
+
+The library syntax is \fun{GEN}{galoispermtopol}{GEN gal, GEN perm}.
+
+\subsec{galoissubcyclo$(N,H,\{\var{fl}=0\},\{v\})$}\kbdsidx{galoissubcyclo}\label{se:galoissubcyclo}
+Computes the subextension
+of $\Q(\zeta_n)$ fixed by the subgroup $H \subset (\Z/n\Z)^*$. By the
+Kronecker-Weber theorem, all abelian number fields can be generated in this
+way (uniquely if $n$ is taken to be minimal).
+
+\noindent The pair $(n, H)$ is deduced from the parameters $(N, H)$ as follows
+
+\item $N$ an integer: then $n = N$; $H$ is a generator, i.e. an
+integer or an integer modulo $n$; or a vector of generators.
+
+\item $N$ the output of \kbd{znstar($n$)}. $H$ as in the first case
+above, or a matrix, taken to be a HNF left divisor of the SNF for $(\Z/n\Z)^*$
+(of type \kbd{$N$.cyc}), giving the generators of $H$ in terms of \kbd{$N$.gen}.
+
+\item $N$ the output of \kbd{bnrinit(bnfinit(y), $m$, 1)} where $m$ is a
+module. $H$ as in the first case, or a matrix taken to be a HNF left
+divisor of the SNF for the ray class group modulo $m$
+(of type \kbd{$N$.cyc}), giving the generators of $H$ in terms of \kbd{$N$.gen}.
+
+In this last case, beware that $H$ is understood relatively to $N$; in
+particular, if the infinite place does not divide the module, e.g if $m$ is
+an integer, then it is not a subgroup of $(\Z/n\Z)^*$, but of its quotient by
+$\{\pm 1\}$.
+
+If $fl=0$, compute a polynomial (in the variable \var{v}) defining the
+the subfield of $\Q(\zeta_n)$ fixed by the subgroup \var{H} of $(\Z/n\Z)^*$.
+
+If $fl=1$, compute only the conductor of the abelian extension, as a module.
+
+If $fl=2$, output $[pol, N]$, where $pol$ is the polynomial as output when
+$fl=0$ and $N$ the conductor as output when $fl=1$.
+
+The following function can be used to compute all subfields of
+$\Q(\zeta_n)$ (of exact degree \kbd{d}, if \kbd{d} is set):
+\bprog
+polsubcyclo(n, d = -1)=
+{ my(bnr,L,IndexBound);
+  IndexBound = if (d < 0, n, [d]);
+  bnr = bnrinit(bnfinit(y), [n,[1]], 1);
+  L = subgrouplist(bnr, IndexBound, 1);
+  vector(#L,i, galoissubcyclo(bnr,L[i]));
+}
+ at eprog\noindent
+Setting \kbd{L = subgrouplist(bnr, IndexBound)} would produce subfields of exact
+conductor $n\infty$.
+
+The library syntax is \fun{GEN}{galoissubcyclo}{GEN N, GEN H = NULL, long fl, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{galoissubfields$(G,\{\var{flags}=0\},\{v\})$}\kbdsidx{galoissubfields}\label{se:galoissubfields}
+Outputs all the subfields of the Galois group \var{G}, as a vector.
+This works by applying \kbd{galoisfixedfield} to all subgroups. The meaning of
+the flag \var{fl} is the same as for \kbd{galoisfixedfield}.
+
+The library syntax is \fun{GEN}{galoissubfields}{GEN G, long flags, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{galoissubgroups$(G)$}\kbdsidx{galoissubgroups}\label{se:galoissubgroups}
+Outputs all the subgroups of the Galois group \kbd{gal}. A subgroup is a
+vector [\var{gen}, \var{orders}], with the same meaning
+as for $\var{gal}.gen$ and $\var{gal}.orders$. Hence \var{gen} is a vector of
+permutations generating the subgroup, and \var{orders} is the relatives
+orders of the generators. The cardinal of a subgroup is the product of the
+relative orders. Such subgroup can be used instead of a Galois group in the
+following command: \kbd{galoisisabelian}, \kbd{galoissubgroups},
+\kbd{galoisexport} and \kbd{galoisidentify}.
+
+To get the subfield fixed by a subgroup \var{sub} of \var{gal}, use
+\bprog
+galoisfixedfield(gal,sub[1])
+ at eprog
+
+The library syntax is \fun{GEN}{galoissubgroups}{GEN G}.
+
+\subsec{idealadd$(\var{nf},x,y)$}\kbdsidx{idealadd}\label{se:idealadd}
+Sum of the two ideals $x$ and $y$ in the number field $\var{nf}$. The
+result is given in HNF.
+\bprog
+ ? K = nfinit(x^2 + 1);
+ ? a = idealadd(K, 2, x + 1)  \\ ideal generated by 2 and 1+I
+ %2 =
+ [2 1]
+
+ [0 1]
+ ? pr = idealprimedec(K, 5)[1];  \\ a prime ideal above 5
+ ? idealadd(K, a, pr)     \\ coprime, as expected
+ %4 =
+ [1 0]
+
+ [0 1]
+ at eprog\noindent
+This function cannot be used to add arbitrary $\Z$-modules, since it assumes
+that its arguments are ideals:
+\bprog
+  ? b = Mat([1,0]~);
+  ? idealadd(K, b, b)     \\ only square t_MATs represent ideals
+  *** idealadd: non-square t_MAT in idealtyp.
+  ? c = [2, 0; 2, 0]; idealadd(K, c, c)   \\ non-sense
+  %6 =
+  [2 0]
+
+  [0 2]
+  ? d = [1, 0; 0, 2]; idealadd(K, d, d)   \\ non-sense
+  %7 =
+  [1 0]
+
+  [0 1]
+
+ at eprog\noindent In the last two examples, we get wrong results since the
+matrices $c$ and $d$ do not correspond to an ideal: the $\Z$-span of their
+columns (as usual interpreted as coordinates with respect to the integer basis
+\kbd{K.zk}) is not an $O_K$-module. To add arbitrary $\Z$-modules generated
+by the columns of matrices $A$ and $B$, use \kbd{mathnf(concat(A,B))}.
+
+The library syntax is \fun{GEN}{idealadd}{GEN nf, GEN x, GEN y}.
+
+\subsec{idealaddtoone$(\var{nf},x,\{y\})$}\kbdsidx{idealaddtoone}\label{se:idealaddtoone}
+$x$ and $y$ being two co-prime
+integral ideals (given in any form), this gives a two-component row vector
+$[a,b]$ such that $a\in x$, $b\in y$ and $a+b=1$.
+
+The alternative syntax $\kbd{idealaddtoone}(\var{nf},v)$, is supported, where
+$v$ is a $k$-component vector of ideals (given in any form) which sum to
+$\Z_K$. This outputs a $k$-component vector $e$ such that $e[i]\in x[i]$ for
+$1\le i\le k$ and $\sum_{1\le i\le k}e[i]=1$.
+
+The library syntax is \fun{GEN}{idealaddtoone0}{GEN nf, GEN x, GEN y = NULL}.
+
+\subsec{idealappr$(\var{nf},x,\{\fl=0\})$}\kbdsidx{idealappr}\label{se:idealappr}
+If $x$ is a fractional ideal
+(given in any form), gives an element $\alpha$ in $\var{nf}$ such that for
+all prime ideals $\goth{p}$ such that the valuation of $x$ at $\goth{p}$ is
+non-zero, we have $v_{\goth{p}}(\alpha)=v_{\goth{p}}(x)$, and
+$v_{\goth{p}}(\alpha)\ge0$ for all other $\goth{p}$.
+
+If $\fl$ is non-zero, $x$ must be given as a prime ideal factorization, as
+output by \kbd{idealfactor}, but possibly with zero or negative exponents.
+This yields an element $\alpha$ such that for all prime ideals $\goth{p}$
+occurring in $x$, $v_{\goth{p}}(\alpha)$ is equal to the exponent of
+$\goth{p}$ in $x$, and for all other prime ideals,
+$v_{\goth{p}}(\alpha)\ge0$. This generalizes $\kbd{idealappr}(\var{nf},x,0)$
+since zero exponents are allowed. Note that the algorithm used is slightly
+different, so that
+\bprog
+  idealappr(nf, idealfactor(nf,x))
+ at eprog\noindent
+may not be the same as \kbd{idealappr(nf,x,1)}.
+
+The library syntax is \fun{GEN}{idealappr0}{GEN nf, GEN x, long flag}.
+
+\subsec{idealchinese$(\var{nf},x,y)$}\kbdsidx{idealchinese}\label{se:idealchinese}
+$x$ being a prime ideal factorization
+(i.e.~a 2 by 2 matrix whose first column contains prime ideals, and the second
+column integral exponents), $y$ a vector of elements in $\var{nf}$ indexed by
+the ideals in $x$, computes an element $b$ such that
+
+$v_{\goth{p}}(b - y_{\goth{p}}) \geq v_{\goth{p}}(x)$ for all prime ideals
+in $x$ and $v_{\goth{p}}(b)\geq 0$ for all other $\goth{p}$.
+
+The library syntax is \fun{GEN}{idealchinese}{GEN nf, GEN x, GEN y}.
+
+\subsec{idealcoprime$(\var{nf},x,y)$}\kbdsidx{idealcoprime}\label{se:idealcoprime}
+Given two integral ideals $x$ and $y$
+in the number field $\var{nf}$, returns a $\beta$ in the field,
+such that $\beta\cdot x$ is an integral ideal coprime to $y$.
+
+The library syntax is \fun{GEN}{idealcoprime}{GEN nf, GEN x, GEN y}.
+
+\subsec{idealdiv$(\var{nf},x,y,\{\fl=0\})$}\kbdsidx{idealdiv}\label{se:idealdiv}
+Quotient $x\cdot y^{-1}$ of the two ideals $x$ and $y$ in the number
+field $\var{nf}$. The result is given in HNF.
+
+If $\fl$ is non-zero, the quotient $x \cdot y^{-1}$ is assumed to be an
+integral ideal. This can be much faster when the norm of the quotient is
+small even though the norms of $x$ and $y$ are large.
+
+The library syntax is \fun{GEN}{idealdiv0}{GEN nf, GEN x, GEN y, long flag}.
+Also available are \fun{GEN}{idealdiv}{GEN nf, GEN x, GEN y}
+($\fl=0$) and \fun{GEN}{idealdivexact}{GEN nf, GEN x, GEN y} ($\fl=1$).
+
+\subsec{idealfactor$(\var{nf},x)$}\kbdsidx{idealfactor}\label{se:idealfactor}
+Factors into prime ideal powers the
+ideal $x$ in the number field $\var{nf}$. The output format is similar to the
+\kbd{factor} function, and the prime ideals are represented in the form
+output by the \kbd{idealprimedec} function, i.e.~as 5-element vectors.
+
+The library syntax is \fun{GEN}{idealfactor}{GEN nf, GEN x}.
+
+\subsec{idealfactorback$(\var{nf},f,\{e\},\{\fl = 0\})$}\kbdsidx{idealfactorback}\label{se:idealfactorback}
+Gives back the ideal corresponding to a factorization. The integer $1$
+corresponds to the empty factorization.
+If $e$ is present, $e$ and $f$ must be vectors of the same length ($e$ being
+integral), and the corresponding factorization is the product of the
+$f[i]^{e[i]}$.
+
+If not, and $f$ is vector, it is understood as in the preceding case with $e$
+a vector of 1s: we return the product of the $f[i]$. Finally, $f$ can be a
+regular factorization, as produced by \kbd{idealfactor}.
+\bprog
+? nf = nfinit(y^2+1); idealfactor(nf, 4 + 2*y)
+%1 =
+[[2, [1, 1]~, 2, 1, [1, 1]~] 2]
+
+[[5, [2, 1]~, 1, 1, [-2, 1]~] 1]
+
+? idealfactorback(nf, %)
+%2 =
+[10 4]
+
+[0  2]
+
+? f = %1[,1]; e = %1[,2]; idealfactorback(nf, f, e)
+%3 =
+[10 4]
+
+[0  2]
+
+? % == idealhnf(nf, 4 + 2*y)
+%4 = 1
+ at eprog
+If \kbd{flag} is non-zero, perform ideal reductions (\tet{idealred}) along the
+way. This is most useful if the ideals involved are all \emph{extended}
+ideals (for instance with trivial principal part), so that the principal parts
+extracted by \kbd{idealred} are not lost. Here is an example:
+\bprog
+? f = vector(#f, i, [f[i], [;]]);  \\ transform to extended ideals
+? idealfactorback(nf, f, e, 1)
+%6 = [[1, 0; 0, 1], [2, 1; [2, 1]~, 1]]
+? nffactorback(nf, %[2])
+%7 = [4, 2]~
+ at eprog
+The extended ideal returned in \kbd{\%6} is the trivial ideal $1$, extended
+with a principal generator given in factored form. We use \tet{nffactorback}
+to recover it in standard form.
+
+The library syntax is \fun{GEN}{idealfactorback}{GEN nf, GEN f, GEN e = NULL, long flag }.
+
+\subsec{idealfrobenius$(\var{nf},\var{gal},\var{pr})$}\kbdsidx{idealfrobenius}\label{se:idealfrobenius}
+Let $K$ be the number field defined by $nf$ and assume $K/\Q$ be a
+Galois extension with Galois group given \kbd{gal=galoisinit(nf)},
+and that $pr$ is the prime ideal $\goth{P}$ in prid format, and that
+$\goth{P}$ is unramified.
+This function returns a permutation of \kbd{gal.group} which defines the
+automorphism $\sigma=\left(\goth{P}\over K/\Q \right)$, i.e the Frobenius
+element associated to $\goth{P}$. If $p$ is the unique prime number
+in $\goth{P}$, then $\sigma(x)\equiv x^p\mod\P$ for all $x\in\Z_K$.
+\bprog
+? nf = nfinit(polcyclo(31));
+? gal = galoisinit(nf);
+? pr = idealprimedec(nf,101)[1];
+? g = idealfrobenius(nf,gal,pr);
+? galoispermtopol(gal,g)
+%5 = x^8
+ at eprog\noindent This is correct since $101\equiv 8\mod{31}$.
+
+The library syntax is \fun{GEN}{idealfrobenius}{GEN nf, GEN gal, GEN pr}.
+
+\subsec{idealhnf$(\var{nf},u,\{v\})$}\kbdsidx{idealhnf}\label{se:idealhnf}
+Gives the \idx{Hermite normal form} of the ideal $u\Z_K+v\Z_K$, where $u$
+and $v$ are elements of the number field $K$ defined by \kbd{nf}.
+\bprog
+? nf = nfinit(y^3 - 2);
+? idealhnf(nf, 2, y+1)
+%2 =
+[1 0 0]
+
+[0 1 0]
+
+[0 0 1]
+? idealhnf(nf, y/2, [0,0,1/3]~)
+%3 =
+[1/3 0 0]
+
+[0 1/6 0]
+
+[0 0 1/6]
+ at eprog
+
+If $b$ is omitted, returns the HNF of the ideal defined by $u$: $u$ may be an
+algebraic number (defining a principal ideal), a maximal ideal (as given by
+\kbd{idealprimedec} or \kbd{idealfactor}), or a matrix whose columns give
+generators for the ideal. This last format is a little complicated, but
+useful to reduce general modules to the canonical form once in a while:
+
+\item if strictly less than $N = [K:\Q]$ generators are given, $u$
+is the $\Z_K$-module they generate,
+
+\item if $N$ or more are given, it is \emph{assumed} that they form a
+$\Z$-basis of the ideal, in particular that the matrix has maximal rank $N$.
+This acts as \kbd{mathnf} since the $\Z_K$-module structure is (taken for
+granted hence) not taken into account in this case.
+\bprog
+? idealhnf(nf, idealprimedec(nf,2)[1])
+%4 =
+[2 0 0]
+
+[0 1 0]
+
+[0 0 1]
+? idealhnf(nf, [1,2;2,3;3,4])
+%5 =
+[1 0 0]
+
+[0 1 0]
+
+[0 0 1]
+ at eprog\noindent Finally, when $K$ is quadratic with discriminant $D_K$, we
+allow $u =$ \kbd{Qfb(a,b,c)}, provided $b^2 - 4ac = D_K$. As usual,
+this represents the ideal $a \Z + (1/2)(-b + \sqrt{D_K}) \Z$.
+\bprog
+? K = nfinit(x^2 - 60); K.disc
+%1 = 60
+? idealhnf(K, qfbprimeform(60,2))
+%2 =
+[2 1]
+
+[0 1]
+? idealhnf(K, Qfb(1,2,3))
+  ***   at top-level: idealhnf(K,Qfb(1,2,3
+  ***                 ^--------------------
+  *** idealhnf: Qfb(1, 2, 3) has discriminant != 60 in idealhnf.
+ at eprog
+
+The library syntax is \fun{GEN}{idealhnf0}{GEN nf, GEN u, GEN v = NULL}.
+Also available is \fun{GEN}{idealhnf}{GEN nf, GEN a}.
+
+\subsec{idealintersect$(\var{nf},A,B)$}\kbdsidx{idealintersect}\label{se:idealintersect}
+Intersection of the two ideals
+$A$ and $B$ in the number field $\var{nf}$. The result is given in HNF.
+\bprog
+? nf = nfinit(x^2+1);
+? idealintersect(nf, 2, x+1)
+%2 =
+[2 0]
+
+[0 2]
+ at eprog
+
+This function does not apply to general $\Z$-modules, e.g.~orders, since its
+arguments are replaced by the ideals they generate. The following script
+intersects $\Z$-modules $A$ and $B$ given by matrices of compatible
+dimensions with integer coefficients:
+\bprog
+ZM_intersect(A,B) =
+{ my(Ker = matkerint(concat(A,B)));
+  mathnf( A * Ker[1..#A,] )
+}
+ at eprog
+
+The library syntax is \fun{GEN}{idealintersect}{GEN nf, GEN A, GEN B}.
+
+\subsec{idealinv$(\var{nf},x)$}\kbdsidx{idealinv}\label{se:idealinv}
+Inverse of the ideal $x$ in the
+number field $\var{nf}$, given in HNF. If $x$ is an extended
+ideal\sidx{ideal (extended)}, its principal part is suitably
+updated: i.e. inverting $[I,t]$, yields $[I^{-1}, 1/t]$.
+
+The library syntax is \fun{GEN}{idealinv}{GEN nf, GEN x}.
+
+\subsec{ideallist$(\var{nf},\var{bound},\{\fl=4\})$}\kbdsidx{ideallist}\label{se:ideallist}
+Computes the list
+of all ideals of norm less or equal to \var{bound} in the number field
+\var{nf}. The result is a row vector with exactly \var{bound} components.
+Each component is itself a row vector containing the information about
+ideals of a given norm, in no specific order, depending on the value of
+$\fl$:
+
+The possible values of $\fl$ are:
+
+\quad 0: give the \var{bid} associated to the ideals, without generators.
+
+\quad 1: as 0, but include the generators in the \var{bid}.
+
+\quad 2: in this case, \var{nf} must be a \var{bnf} with units. Each
+component is of the form $[\var{bid},U]$, where \var{bid} is as case 0
+and $U$ is a vector of discrete logarithms of the units. More precisely, it
+gives the \kbd{ideallog}s with respect to \var{bid} of \kbd{bnf.tufu}.
+This structure is technical, and only meant to be used in conjunction with
+\tet{bnrclassnolist} or \tet{bnrdisclist}.
+
+\quad 3: as 2, but include the generators in the \var{bid}.
+
+\quad 4: give only the HNF of the ideal.
+
+\bprog
+? nf = nfinit(x^2+1);
+? L = ideallist(nf, 100);
+? L[1]
+%3 = [[1, 0; 0, 1]]  \\@com A single ideal of norm 1
+? #L[65]
+%4 = 4               \\@com There are 4 ideals of norm 4 in $\Z[i]$
+ at eprog
+If one wants more information, one could do instead:
+\bprog
+? nf = nfinit(x^2+1);
+? L = ideallist(nf, 100, 0);
+? l = L[25]; vector(#l, i, l[i].clgp)
+%3 = [[20, [20]], [16, [4, 4]], [20, [20]]]
+? l[1].mod
+%4 = [[25, 18; 0, 1], []]
+? l[2].mod
+%5 = [[5, 0; 0, 5], []]
+? l[3].mod
+%6 = [[25, 7; 0, 1], []]
+ at eprog\noindent where we ask for the structures of the $(\Z[i]/I)^*$ for all
+three ideals of norm $25$. In fact, for all moduli with finite part of norm
+$25$ and trivial Archimedean part, as the last 3 commands show. See
+\tet{ideallistarch} to treat general moduli.
+
+The library syntax is \fun{GEN}{ideallist0}{GEN nf, long bound, long flag}.
+
+\subsec{ideallistarch$(\var{nf},\var{list},\var{arch})$}\kbdsidx{ideallistarch}\label{se:ideallistarch}
+\var{list} is a vector of vectors of bid's, as output by \tet{ideallist} with
+flag $0$ to $3$. Return a vector of vectors with the same number of
+components as the original \var{list}. The leaves give information about
+moduli whose finite part is as in original list, in the same order, and
+Archimedean part is now \var{arch} (it was originally trivial). The
+information contained is of the same kind as was present in the input; see
+\tet{ideallist}, in particular the meaning of \fl.
+
+\bprog
+? bnf = bnfinit(x^2-2);
+? bnf.sign
+%2 = [2, 0]                         \\@com two places at infinity
+? L = ideallist(bnf, 100, 0);
+? l = L[98]; vector(#l, i, l[i].clgp)
+%4 = [[42, [42]], [36, [6, 6]], [42, [42]]]
+? La = ideallistarch(bnf, L, [1,1]); \\@com add them to the modulus
+? l = La[98]; vector(#l, i, l[i].clgp)
+%6 = [[168, [42, 2, 2]], [144, [6, 6, 2, 2]], [168, [42, 2, 2]]]
+ at eprog
+Of course, the results above are obvious: adding $t$ places at infinity will
+add $t$ copies of $\Z/2\Z$ to the ray class group. The following application
+is more typical:
+\bprog
+? L = ideallist(bnf, 100, 2);        \\@com units are required now
+? La = ideallistarch(bnf, L, [1,1]);
+? H = bnrclassnolist(bnf, La);
+? H[98];
+%6 = [2, 12, 2]
+ at eprog
+
+The library syntax is \fun{GEN}{ideallistarch}{GEN nf, GEN list, GEN arch}.
+
+\subsec{ideallog$(\var{nf},x,\var{bid})$}\kbdsidx{ideallog}\label{se:ideallog}
+$\var{nf}$ is a number field,
+\var{bid} is as output by \kbd{idealstar(nf, D, \dots)} and $x$ a
+non-necessarily integral element of \var{nf} which must have valuation
+equal to 0 at all prime ideals in the support of $\kbd{D}$. This function
+computes the discrete logarithm of $x$ on the generators given in
+\kbd{\var{bid}.gen}. In other words, if $g_i$ are these generators, of orders
+$d_i$ respectively, the result is a column vector of integers $(x_i)$ such
+that $0\le x_i<d_i$ and
+$$x \equiv \prod_i g_i^{x_i} \pmod{\ ^*D}\enspace.$$
+Note that when the support of \kbd{D} contains places at infinity, this
+congruence implies also sign conditions on the associated real embeddings.
+See \tet{znlog} for the limitations of the underlying discrete log algorithms.
+
+The library syntax is \fun{GEN}{ideallog}{GEN nf, GEN x, GEN bid}.
+
+\subsec{idealmin$(\var{nf},\var{ix},\{\var{vdir}\})$}\kbdsidx{idealmin}\label{se:idealmin}
+\emph{This function is useless and kept for backward compatibility only,
+use \kbd{idealred}}. Computes a pseudo-minimum of the ideal $x$ in the
+direction \var{vdir} in the number field \var{nf}.
+
+The library syntax is \fun{GEN}{idealmin}{GEN nf, GEN ix, GEN vdir = NULL}.
+
+\subsec{idealmul$(\var{nf},x,y,\{\fl=0\})$}\kbdsidx{idealmul}\label{se:idealmul}
+Ideal multiplication of the ideals $x$ and $y$ in the number field
+\var{nf}; the result is the ideal product in HNF. If either $x$ or $y$
+are extended ideals\sidx{ideal (extended)}, their principal part is suitably
+updated: i.e. multiplying $[I,t]$, $[J,u]$ yields $[IJ, tu]$; multiplying
+$I$ and $[J, u]$ yields $[IJ, u]$.
+\bprog
+? nf = nfinit(x^2 + 1);
+? idealmul(nf, 2, x+1)
+%2 =
+[4 2]
+
+[0 2]
+? idealmul(nf, [2, x], x+1)        \\ extended ideal * ideal
+%4 = [[4, 2; 0, 2], x]
+? idealmul(nf, [2, x], [x+1, x])   \\ two extended ideals
+%5 = [[4, 2; 0, 2], [-1, 0]~]
+ at eprog\noindent
+If $\fl$ is non-zero, reduce the result using \kbd{idealred}.
+
+The library syntax is \fun{GEN}{idealmul0}{GEN nf, GEN x, GEN y, long flag}.
+
+\noindent See also
+\fun{GEN}{idealmul}{GEN nf, GEN x, GEN y} ($\fl=0$) and
+\fun{GEN}{idealmulred}{GEN nf, GEN x, GEN y} ($\fl\neq0$).
+
+\subsec{idealnorm$(\var{nf},x)$}\kbdsidx{idealnorm}\label{se:idealnorm}
+Computes the norm of the ideal~$x$ in the number field~$\var{nf}$.
+
+The library syntax is \fun{GEN}{idealnorm}{GEN nf, GEN x}.
+
+\subsec{idealnumden$(\var{nf},x)$}\kbdsidx{idealnumden}\label{se:idealnumden}
+Returns $[A,B]$, where $A,B$ are coprime integer ideals
+such that $x = A/B$, in the number field $\var{nf}$.
+\bprog
+? nf = nfinit(x^2+1);
+? idealnumden(nf, (x+1)/2)
+%2 = [[1, 0; 0, 1], [2, 1; 0, 1]]
+ at eprog
+
+The library syntax is \fun{GEN}{idealnumden}{GEN nf, GEN x}.
+
+\subsec{idealpow$(\var{nf},x,k,\{\fl=0\})$}\kbdsidx{idealpow}\label{se:idealpow}
+Computes the $k$-th power of
+the ideal $x$ in the number field $\var{nf}$; $k\in\Z$.
+If $x$ is an extended
+ideal\sidx{ideal (extended)}, its principal part is suitably
+updated: i.e. raising $[I,t]$ to the $k$-th power, yields $[I^k, t^k]$.
+
+If $\fl$ is non-zero, reduce the result using \kbd{idealred}, \emph{throughout
+the (binary) powering process}; in particular, this is \emph{not} the same as
+as $\kbd{idealpow}(\var{nf},x,k)$ followed by reduction.
+
+The library syntax is \fun{GEN}{idealpow0}{GEN nf, GEN x, GEN k, long flag}.
+
+\noindent See also
+\fun{GEN}{idealpow}{GEN nf, GEN x, GEN k} and
+\fun{GEN}{idealpows}{GEN nf, GEN x, long k} ($\fl = 0$).
+Corresponding to $\fl=1$ is \fun{GEN}{idealpowred}{GEN nf, GEN vp, GEN k}.
+
+\subsec{idealprimedec$(\var{nf},p)$}\kbdsidx{idealprimedec}\label{se:idealprimedec}
+Computes the prime ideal
+decomposition of the (positive) prime number $p$ in the number field $K$
+represented by \var{nf}. If a non-prime $p$ is given the result is undefined.
+
+The result is a vector of \tev{prid} structures, each representing one of the
+prime ideals above $p$ in the number field $\var{nf}$. The representation
+$\kbd{pr}=[p,a,e,f,\var{mb}]$ of a prime ideal means the following: $a$ and
+is an algebraic integer in the maximal order $\Z_K$ and the prime ideal is
+equal to $\goth{p} = p\Z_K + a\Z_K$;
+$e$ is the ramification index; $f$ is the residual index;
+finally, \var{mb} is the multiplication table associated to the algebraic
+integer $b$ is such that $\goth{p}^{-1}=\Z_K+ b/ p\Z_K$, which is used
+internally to compute valuations. In other words if $p$ is inert,
+then \var{mb} is the integer $1$, and otherwise it's a square \typ{MAT}
+whose $j$-th column is $b \cdot \kbd{nf.zk[j]}$.
+
+The algebraic number $a$ is guaranteed to have a
+valuation equal to 1 at the prime ideal (this is automatic if $e>1$).
+
+The components of \kbd{pr} should be accessed by member functions: \kbd{pr.p},
+\kbd{pr.e}, \kbd{pr.f}, and \kbd{pr.gen} (returns the vector $[p,a]$):
+\bprog
+? K = nfinit(x^3-2);
+? L = idealprimedec(K, 5);
+? #L       \\ 2 primes above 5 in Q(2^(1/3))
+%3 = 2
+? p1 = L[1]; p2 = L[2];
+? [p1.e, p1.f]    \\ the first is unramified of degree 1
+%4 = [1, 1]
+? [p2.e, p2.f]    \\ the second is unramified of degree 2
+%5 = [1, 2]
+? p1.gen
+%6 = [5, [2, 1, 0]~]
+? nfbasistoalg(K, %[2])  \\ a uniformizer for p1
+%7 = Mod(x + 2, x^3 - 2)
+ at eprog
+
+The library syntax is \fun{GEN}{idealprimedec}{GEN nf, GEN p}.
+
+\subsec{idealprincipalunits$(\var{nf},\var{pr},k)$}\kbdsidx{idealprincipalunits}\label{se:idealprincipalunits}
+Given a prime ideal in \tet{idealprimedec} format,
+returns the multiplicative group $(1 + \var{pr}) / (1 + \var{pr}^k)$ as an
+abelian group. This function is much faster than \tet{idealstar} when the
+norm of \var{pr} is large, since it avoids (useless) work in the
+multiplicative group of the residue field.
+\bprog
+? K = nfinit(y^2+1);
+? P = idealprimedec(K,2)[1];
+? G = idealprincipalunits(K, P, 20);
+? G.cyc
+[512, 256, 4]   \\ Z/512 x Z/256 x Z/4
+? G.gen
+%5 = [[-1, -2]~, 1021, [0, -1]~] \\ minimal generators of given order
+ at eprog
+
+The library syntax is \fun{GEN}{idealprincipalunits}{GEN nf, GEN pr, long k}.
+
+\subsec{idealramgroups$(\var{nf},\var{gal},\var{pr})$}\kbdsidx{idealramgroups}\label{se:idealramgroups}
+Let $K$ be the number field defined by \var{nf} and assume that $K/\Q$ is
+Galois with Galois group $G$ given by \kbd{gal=galoisinit(nf)}.
+Let \var{pr} be the prime ideal $\goth{P}$ in prid format.
+This function returns a vector $g$ of subgroups of \kbd{gal}
+as follow:
+
+\item \kbd{g[1]} is the decomposition group of $\goth{P}$,
+
+\item \kbd{g[2]} is $G_0(\goth{P})$, the inertia group of $\goth{P}$,
+
+and for $i\geq 2$,
+
+\item \kbd{g[i]} is $G_{i-2}(\goth{P})$, the $i-2$-th \idx{ramification
+group} of $\goth{P}$.
+
+\noindent The length of $g$ is the number of non-trivial groups in the
+sequence, thus is $0$ if $e=1$ and $f=1$, and $1$ if $f>1$ and $e=1$.
+The following function computes the cardinality of a subgroup of $G$,
+as given by the components of $g$:
+\bprog
+card(H) =my(o=H[2]); prod(i=1,#o,o[i]);
+ at eprog
+\bprog
+? nf=nfinit(x^6+3); gal=galoisinit(nf); pr=idealprimedec(nf,3)[1];
+? g = idealramgroups(nf, gal, pr);
+? apply(card,g)
+%4 = [6, 6, 3, 3, 3] \\ cardinalities of the G_i
+ at eprog
+
+\bprog
+? nf=nfinit(x^6+108); gal=galoisinit(nf); pr=idealprimedec(nf,2)[1];
+? iso=idealramgroups(nf,gal,pr)[2]
+%4 = [[Vecsmall([2, 3, 1, 5, 6, 4])], Vecsmall([3])]
+? nfdisc(galoisfixedfield(gal,iso,1))
+%5 = -3
+ at eprog\noindent The field fixed by the inertia group of $2$ is not ramified at
+$2$.
+
+The library syntax is \fun{GEN}{idealramgroups}{GEN nf, GEN gal, GEN pr}.
+
+\subsec{idealred$(\var{nf},I,\{v=0\})$}\kbdsidx{idealred}\label{se:idealred}
+\idx{LLL} reduction of
+the ideal $I$ in the number field \var{nf}, along the direction $v$.
+The $v$ parameter is best left omitted, but if it is present, it must
+be an $\kbd{nf.r1} + \kbd{nf.r2}$-component vector of \emph{non-negative}
+integers. (What counts is the relative magnitude of the entries: if all
+entries are equal, the effect is the same as if the vector had been omitted.)
+
+This function finds a ``small'' $a$ in $I$ (see the end for technical details).
+The result is the Hermite normal form of
+the ``reduced'' ideal $J = r I/a$, where $r$ is the unique rational number such
+that $J$ is integral and primitive. (This is usually not a reduced ideal in
+the sense of \idx{Buchmann}.)
+\bprog
+? K = nfinit(y^2+1);
+? P = idealprimedec(K,5)[1];
+? idealred(K, P)
+%3 =
+[1 0]
+
+[0 1]
+ at eprog\noindent More often than not, a \idx{principal ideal} yields the unit
+ideal as above. This is a quick and dirty way to check if ideals are principal,
+but it is not a necessary condition: a non-trivial result does not prove that
+the ideal is non-principal. For guaranteed results, see \kbd{bnfisprincipal},
+which requires the computation of a full \kbd{bnf} structure.
+
+If the input is an extended ideal $[I,s]$, the output is $[J,sa/r]$; this way,
+one can keep track of the principal ideal part:
+\bprog
+? idealred(K, [P, 1])
+%5 = [[1, 0; 0, 1], [-2, 1]~]
+ at eprog\noindent
+meaning that $P$ is generated by $[-2, 1]~$. The number field element in the
+extended part is an algebraic number in any form \emph{or} a factorization
+matrix (in terms of number field elements, not ideals!). In the latter case,
+elements stay in factored form, which is a convenient way to avoid
+coefficient explosion; see also \tet{idealpow}.
+
+\misctitle{Technical note} The routine computes an LLL-reduced
+basis for the lattice $I$ equipped with the quadratic form
+$$|| x ||_v^2 = \sum_{i=1}^{r_1+r_2} 2^{v_i}\varepsilon_i|\sigma_i(x)|^2,$$
+where as usual the $\sigma_i$ are the (real and) complex embeddings and
+$\varepsilon_i = 1$, resp.~$2$, for a real, resp.~complex place. The element
+$a$ is simply the first vector in the LLL basis. The only reason you may want
+to try to change some directions and set some $v_i\neq 0$ is to randomize
+the elements found for a fixed ideal, which is heuristically useful in index
+calculus algorithms like \tet{bnfinit} and \tet{bnfisprincipal}.
+
+\misctitle{Even more technical note} In fact, the above is a white lie.
+We do not use $||\cdot||_v$ exactly but a rescaled rounded variant which
+gets us faster and simpler LLLs. There's no harm since we are not using any
+theoretical property of $a$ after all, except that it belongs to $I$ and is
+``expected to be small''.
+
+The library syntax is \fun{GEN}{idealred0}{GEN nf, GEN I, GEN v = NULL}.
+
+\subsec{idealstar$(\var{nf},I,\{\fl=1\})$}\kbdsidx{idealstar}\label{se:idealstar}
+Outputs a \var{bid} structure,
+necessary for computing in the finite abelian group $G = (\Z_K/I)^*$. Here,
+\var{nf} is a number field and $I$ is a \var{modulus}: either an ideal in any
+form, or a row vector whose first component is an ideal and whose second
+component is a row vector of $r_1$ 0 or 1. Ideals can also be given
+by a factorization into prime ideals, as produced by \tet{idealfactor}.
+
+This \var{bid} is used in \tet{ideallog} to compute discrete logarithms. It
+also contains useful information which can be conveniently retrieved as
+\kbd{\var{bid}.mod} (the modulus),
+\kbd{\var{bid}.clgp} ($G$ as a finite abelian group),
+\kbd{\var{bid}.no} (the cardinality of $G$),
+\kbd{\var{bid}.cyc} (elementary divisors) and
+\kbd{\var{bid}.gen} (generators).
+
+If $\fl=1$ (default), the result is a \var{bid} structure without
+generators.
+
+If $\fl=2$, as $\fl=1$, but including generators, which wastes some time.
+
+If $\fl=0$, only outputs $(\Z_K/I)^*$ as an abelian group,
+i.e as a 3-component vector $[h,d,g]$: $h$ is the order, $d$ is the vector of
+SNF\sidx{Smith normal form} cyclic components and $g$ the corresponding
+generators.
+
+The library syntax is \fun{GEN}{idealstar0}{GEN nf, GEN I, long flag}.
+Instead the above hardcoded numerical flags, one should rather use
+\fun{GEN}{Idealstar}{GEN nf, GEN ideal, long flag}, where \kbd{flag} is
+an or-ed combination of \tet{nf_GEN} (include generators) and \tet{nf_INIT}
+(return a full \kbd{bid}, not a group), possibly $0$. This offers
+one more combination: gen, but no init.
+
+\subsec{idealtwoelt$(\var{nf},x,\{a\})$}\kbdsidx{idealtwoelt}\label{se:idealtwoelt}
+Computes a two-element
+representation of the ideal $x$ in the number field $\var{nf}$, combining a
+random search and an approximation theorem; $x$ is an ideal
+in any form (possibly an extended ideal, whose principal part is ignored)
+
+\item When called as \kbd{idealtwoelt(nf,x)}, the result is a row vector
+$[a,\alpha]$ with two components such that $x=a\Z_K+\alpha\Z_K$ and $a$ is
+chosen to be the positive generator of $x\cap\Z$, unless $x$ was given as a
+principal ideal (in which case we may choose $a = 0$). The algorithm
+uses a fast lazy factorization of $x\cap \Z$ and runs in randomized
+polynomial time.
+
+\item When called as \kbd{idealtwoelt(nf,x,a)} with an explicit non-zero $a$
+supplied as third argument, the function assumes that $a \in x$ and returns
+$\alpha\in x$ such that $x = a\Z_K + \alpha\Z_K$. Note that we must factor
+$a$ in this case, and the algorithm is generally much slower than the
+default variant.
+
+The library syntax is \fun{GEN}{idealtwoelt0}{GEN nf, GEN x, GEN a = NULL}.
+Also available are
+\fun{GEN}{idealtwoelt}{GEN nf, GEN x} and
+\fun{GEN}{idealtwoelt2}{GEN nf, GEN x, GEN a}.
+
+\subsec{idealval$(\var{nf},x,\var{pr})$}\kbdsidx{idealval}\label{se:idealval}
+Gives the valuation of the ideal $x$ at the prime ideal \var{pr} in the
+number field $\var{nf}$, where \var{pr} is in \kbd{idealprimedec} format.
+
+The library syntax is \fun{long}{idealval}{GEN nf, GEN x, GEN pr}.
+
+\subsec{matalgtobasis$(\var{nf},x)$}\kbdsidx{matalgtobasis}\label{se:matalgtobasis}
+$\var{nf}$ being a number field in \kbd{nfinit} format, and $x$ a
+(row or column) vector or matrix, apply \tet{nfalgtobasis} to each entry
+of $x$.
+
+The library syntax is \fun{GEN}{matalgtobasis}{GEN nf, GEN x}.
+
+\subsec{matbasistoalg$(\var{nf},x)$}\kbdsidx{matbasistoalg}\label{se:matbasistoalg}
+$\var{nf}$ being a number field in \kbd{nfinit} format, and $x$ a
+(row or column) vector or matrix, apply \tet{nfbasistoalg} to each entry
+of $x$.
+
+The library syntax is \fun{GEN}{matbasistoalg}{GEN nf, GEN x}.
+
+\subsec{modreverse$(z)$}\kbdsidx{modreverse}\label{se:modreverse}
+Let $z = \kbd{Mod(A, T)}$ be a polmod, and $Q$ be its minimal
+polynomial, which must satisfy $\text{deg}(Q) = \text{deg}(T)$.
+Returns a ``reverse polmod'' \kbd{Mod(B, Q)}, which is a root of $T$.
+
+This is quite useful when one changes the generating element in algebraic
+extensions:
+\bprog
+? u = Mod(x, x^3 - x -1); v = u^5;
+? w = modreverse(v)
+%2 = Mod(x^2 - 4*x + 1, x^3 - 5*x^2 + 4*x - 1)
+ at eprog\noindent
+which means that $x^3 - 5x^2 + 4x -1$ is another defining polynomial for the
+cubic field
+$$\Q(u) = \Q[x]/(x^3 - x - 1) = \Q[x]/(x^3 - 5x^2 + 4x - 1) = \Q(v),$$
+and that $u \to v^2 - 4v + 1$ gives an explicit isomorphism. From this, it is
+easy to convert elements between the $A(u)\in \Q(u)$ and $B(v)\in \Q(v)$
+representations:
+\bprog
+? A = u^2 + 2*u + 3; subst(lift(A), 'x, w)
+%3 = Mod(x^2 - 3*x + 3, x^3 - 5*x^2 + 4*x - 1)
+? B = v^2 + v + 1;   subst(lift(B), 'x, v)
+%4 = Mod(26*x^2 + 31*x + 26, x^3 - x - 1)
+ at eprog
+If the minimal polynomial of $z$ has lower degree than expected, the routine
+fails
+\bprog
+? u = Mod(-x^3 + 9*x, x^4 - 10*x^2 + 1)
+? modreverse(u)
+ *** modreverse: domain error in modreverse: deg(minpoly(z)) < 4
+ ***   Break loop: type 'break' to go back to GP prompt
+break> Vec( dbg_err() ) \\ ask for more info
+["e_DOMAIN", "modreverse", "deg(minpoly(z))", "<", 4,
+  Mod(-x^3 + 9*x, x^4 - 10*x^2 + 1)]
+break> minpoly(u)
+x^2 - 8
+ at eprog
+
+The library syntax is \fun{GEN}{modreverse}{GEN z}.
+
+\subsec{newtonpoly$(x,p)$}\kbdsidx{newtonpoly}\label{se:newtonpoly}
+Gives the vector of the slopes of the Newton
+polygon of the polynomial $x$ with respect to the prime number $p$. The $n$
+components of the vector are in decreasing order, where $n$ is equal to the
+degree of $x$. Vertical slopes occur iff the constant coefficient of $x$ is
+zero and are denoted by \tet{LONG_MAX}, the biggest single precision
+integer representable on the machine ($2^{31}-1$ (resp.~$2^{63}-1$) on 32-bit
+(resp.~64-bit) machines), see \secref{se:valuation}.
+
+The library syntax is \fun{GEN}{newtonpoly}{GEN x, GEN p}.
+
+\subsec{nfalgtobasis$(\var{nf},x)$}\kbdsidx{nfalgtobasis}\label{se:nfalgtobasis}
+Given an algebraic number $x$ in the number field $\var{nf}$,
+transforms it to a column vector on the integral basis \kbd{\var{nf}.zk}.
+\bprog
+? nf = nfinit(y^2 + 4);
+? nf.zk
+%2 = [1, 1/2*y]
+? nfalgtobasis(nf, [1,1]~)
+%3 = [1, 1]~
+? nfalgtobasis(nf, y)
+%4 = [0, 2]~
+? nfalgtobasis(nf, Mod(y, y^2+4))
+%4 = [0, 2]~
+ at eprog
+This is the inverse function of \kbd{nfbasistoalg}.
+
+The library syntax is \fun{GEN}{algtobasis}{GEN nf, GEN x}.
+
+\subsec{nfbasis$(T)$}\kbdsidx{nfbasis}\label{se:nfbasis}
+Let $T(X)$ be an irreducible polynomial with integral coefficients. This
+function returns an \idx{integral basis} of the number field defined by $T$,
+that is a $\Z$-basis of its maximal order. The basis elements are given as
+elements in $\Q[X]/(T)$:
+\bprog
+? nfbasis(x^2 + 1)
+%1 = [1, x]
+ at eprog
+This function uses a modified version of the \idx{round 4} algorithm,
+due to David \idx{Ford}, Sebastian \idx{Pauli} and Xavier \idx{Roblot}.
+
+\misctitle{Local basis, orders maximal at certain primes}
+
+Obtaining the maximal order is hard: it requires factoring the discriminant
+$D$ of $T$. Obtaining an order which is maximal at a finite explicit set of
+primes is easy, but if may then be a strict suborder of the maximal order. To
+specify that we are interested in a given set of places only, we can replace
+the argument $T$ by an argument $[T,\var{listP}]$, where \var{listP} encodes
+the primes we are interested in: it must be a factorization matrix, a vector
+of integers or a single integer.
+
+\item Vector: we assume that it contains distinct \emph{prime} numbers.
+
+\item Matrix: we assume that it is a two-column matrix of a
+(partial) factorization of $D$; namely the first column contains
+\emph{primes} and the second one the valuation of $D$ at each of these
+primes.
+
+\item Integer $B$: this is replaced by the vector of primes up to $B$. Note
+that the function will use at least $O(B)$ time: a small value, about
+$10^5$, should be enough for most applications. Values larger than $2^{32}$
+are not supported.
+
+In all these cases, the primes may or may not divide the discriminant $D$
+of $T$. The function then returns a $\Z$-basis of an order whose index is
+not divisible by any of these prime numbers. The result is actually a global
+integral basis if all prime divisors of the \emph{field} discriminant are
+included! Note that \kbd{nfinit} has built-in support for such
+a check:
+\bprog
+? K = nfinit([T, listP]);
+? nfcertify(K)   \\ we computed an actual maximal order
+%2 = [];
+ at eprog\noindent The first line initializes a number field structure
+incorporating \kbd{nfbasis([T, listP]} in place of a proven integral basis.
+The second line certifies that the resulting structure is correct. This
+allows to create an \kbd{nf} structure associated to the number field $K =
+\Q[X]/(T)$, when the discriminant of $T$ cannot be factored completely,
+whereas the prime divisors of $\disc K$ are known.
+
+Of course, if \var{listP} contains a single prime number $p$,
+the function returns a local integral basis for $\Z_p[X]/(T)$:
+\bprog
+? nfbasis(x^2+x-1001)
+%1 = [1, 1/3*x - 1/3]
+? nfbasis( [x^2+x-1001, [2]] )
+%2 = [1, x]
+ at eprog
+
+\misctitle{The Buchmann-Lenstra algorithm}
+
+We now complicate the picture: it is in fact allowed to include
+\emph{composite} numbers instead of primes
+in \kbd{listP} (Vector or Matrix case), provided they are pairwise coprime.
+The result will still be a correct integral basis \emph{if}
+the field discriminant factors completely over the actual primes in the list.
+Adding a composite $C$ such that $C^2$ \emph{divides} $D$ may help because
+when we consider $C$ as a prime and run the algorithm, two good things can
+happen: either we
+succeed in proving that no prime dividing $C$ can divide the index
+(without actually needing to find those primes), or the computation
+exhibits a non-trivial zero divisor, thereby factoring $C$ and
+we go on with the refined factorization. (Note that including a $C$
+such that $C^2$ does not divide $D$ is useless.) If neither happen, then the
+computed basis need not generate the maximal order. Here is an example:
+\bprog
+? B = 10^5;
+? P = factor(poldisc(T), B)[,1]; \\ primes <= B dividing D + cofactor
+? basis = nfbasis([T, listP])
+? disc = nfdisc([T, listP])
+ at eprog\noindent We obtain the maximal order and its discriminant if the
+field discriminant factors
+completely over the primes less than $B$ (together with the primes
+contained in the \tet{addprimes} table). This can be tested as follows:
+\bprog
+  check = factor(disc, B);
+  lastp = check[-1..-1,1];
+  if (lastp > B && !setsearch(addprimes(), lastp),
+    warning("nf may be incorrect!"))
+ at eprog\noindent
+This is a sufficient but not a necessary condition, hence the warning,
+instead of an error. N.B. \kbd{lastp} is the last entry
+in the first column of the \kbd{check} matrix, i.e. the largest prime
+dividing \kbd{nf.disc} if $\leq B$ or if it belongs to the prime table.
+
+The function \tet{nfcertify} speeds up and automates the above process:
+\bprog
+? B = 10^5;
+? nf = nfinit([T, B]);
+? nfcertify(nf)
+%3 = []      \\ nf is unconditionally correct
+? basis = nf.zk;
+? disc = nf.disc;
+ at eprog
+
+\synt{nfbasis}{GEN T, GEN *d, GEN listP = NULL}, which returns the order
+basis, and where \kbd{*d} receives the order discriminant.
+
+\subsec{nfbasistoalg$(\var{nf},x)$}\kbdsidx{nfbasistoalg}\label{se:nfbasistoalg}
+Given an algebraic number $x$ in the number field \kbd{nf}, transforms it
+into \typ{POLMOD} form.
+\bprog
+? nf = nfinit(y^2 + 4);
+? nf.zk
+%2 = [1, 1/2*y]
+? nfbasistoalg(nf, [1,1]~)
+%3 = Mod(1/2*y + 1, y^2 + 4)
+? nfbasistoalg(nf, y)
+%4 = Mod(y, y^2 + 4)
+? nfbasistoalg(nf, Mod(y, y^2+4))
+%4 = Mod(y, y^2 + 4)
+ at eprog
+This is the inverse function of \kbd{nfalgtobasis}.
+
+The library syntax is \fun{GEN}{basistoalg}{GEN nf, GEN x}.
+
+\subsec{nfcertify$(\var{nf})$}\kbdsidx{nfcertify}\label{se:nfcertify}
+$\var{nf}$ being as output by
+\kbd{nfinit}, checks whether the integer basis is known unconditionally.
+This is in particular useful when the argument to \kbd{nfinit} was of the
+form $[T, \kbd{listP}]$, specifying a finite list of primes when
+$p$-maximality had to be proven.
+
+The function returns a vector of composite integers. If this vector is
+empty, then \kbd{nf.zk} and \kbd{nf.disc} are correct. Otherwise, the
+result is dubious. In order to obtain a certified result, one must
+completely factor each of the given integers, then \kbd{addprime} each of
+them, then check whether \kbd{nfdisc(nf.pol)} is equal to \kbd{nf.disc}.
+
+The library syntax is \fun{GEN}{nfcertify}{GEN nf}.
+
+\subsec{nfdetint$(\var{nf},x)$}\kbdsidx{nfdetint}\label{se:nfdetint}
+Given a pseudo-matrix $x$, computes a
+non-zero ideal contained in (i.e.~multiple of) the determinant of $x$. This
+is particularly useful in conjunction with \kbd{nfhnfmod}.
+
+The library syntax is \fun{GEN}{nfdetint}{GEN nf, GEN x}.
+
+\subsec{nfdisc$(T)$}\kbdsidx{nfdisc}\label{se:nfdisc}
+\idx{field discriminant} of the number field defined by the integral,
+preferably monic, irreducible polynomial $T(X)$. Returns the discriminant of
+the number field $\Q[X]/(T)$, using the Round $4$ algorithm.
+
+\misctitle{Local discriminants, valuations at certain primes}
+
+As in \kbd{nfbasis}, the argument $T$ can be replaced by $[T,\var{listP}]$,
+where \kbd{listP} is as in \kbd{nfbasis}: a vector of
+pairwise coprime integers (usually distinct primes), a factorization matrix,
+or a single integer. In that case, the function returns the discriminant of
+an order whose basis is given by \kbd{nfbasis(T,listP)}, which need not be
+the maximal order, and whose valuation at a prime entry in \kbd{listP} is the
+same as the valuation of the field discriminant.
+
+In particular, if \kbd{listP} is $[p]$ for a prime $p$, we can
+return the $p$-adic discriminant of the maximal order of $\Z_p[X]/(T)$,
+as a power of $p$, as follows:
+\bprog
+? padicdisc(T,p) = p^valuation(nfdisc(T,[p]), p);
+? nfdisc(x^2 + 6)
+%1 = -24
+? padicdisc(x^2 + 6, 2)
+%2 = 8
+? padicdisc(x^2 + 6, 3)
+%3 = 3
+ at eprog
+
+\synt{nfdisc}{GEN T} (\kbd{listP = NULL}). Also available is
+\fun{GEN}{nfbasis}{GEN T, GEN *d, GEN listP = NULL}, which returns the order
+basis, and where \kbd{*d} receives the order discriminant.
+
+\subsec{nfeltadd$(\var{nf},x,y)$}\kbdsidx{nfeltadd}\label{se:nfeltadd}
+Given two elements $x$ and $y$ in
+\var{nf}, computes their sum $x+y$ in the number field $\var{nf}$.
+
+The library syntax is \fun{GEN}{nfadd}{GEN nf, GEN x, GEN y}.
+
+\subsec{nfeltdiv$(\var{nf},x,y)$}\kbdsidx{nfeltdiv}\label{se:nfeltdiv}
+Given two elements $x$ and $y$ in
+\var{nf}, computes their quotient $x/y$ in the number field $\var{nf}$.
+
+The library syntax is \fun{GEN}{nfdiv}{GEN nf, GEN x, GEN y}.
+
+\subsec{nfeltdiveuc$(\var{nf},x,y)$}\kbdsidx{nfeltdiveuc}\label{se:nfeltdiveuc}
+Given two elements $x$ and $y$ in
+\var{nf}, computes an algebraic integer $q$ in the number field $\var{nf}$
+such that the components of $x-qy$ are reasonably small. In fact, this is
+functionally identical to \kbd{round(nfdiv(\var{nf},x,y))}.
+
+The library syntax is \fun{GEN}{nfdiveuc}{GEN nf, GEN x, GEN y}.
+
+\subsec{nfeltdivmodpr$(\var{nf},x,y,\var{pr})$}\kbdsidx{nfeltdivmodpr}\label{se:nfeltdivmodpr}
+Given two elements $x$
+and $y$ in \var{nf} and \var{pr} a prime ideal in \kbd{modpr} format (see
+\tet{nfmodprinit}), computes their quotient $x / y$ modulo the prime ideal
+\var{pr}.
+
+The library syntax is \fun{GEN}{nfdivmodpr}{GEN nf, GEN x, GEN y, GEN pr}.
+This function is normally useless in library mode. Project your
+inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
+
+\subsec{nfeltdivrem$(\var{nf},x,y)$}\kbdsidx{nfeltdivrem}\label{se:nfeltdivrem}
+Given two elements $x$ and $y$ in
+\var{nf}, gives a two-element row vector $[q,r]$ such that $x=qy+r$, $q$ is
+an algebraic integer in $\var{nf}$, and the components of $r$ are
+reasonably small.
+
+The library syntax is \fun{GEN}{nfdivrem}{GEN nf, GEN x, GEN y}.
+
+\subsec{nfeltmod$(\var{nf},x,y)$}\kbdsidx{nfeltmod}\label{se:nfeltmod}
+Given two elements $x$ and $y$ in
+\var{nf}, computes an element $r$ of $\var{nf}$ of the form $r=x-qy$ with
+$q$ and algebraic integer, and such that $r$ is small. This is functionally
+identical to
+$$\kbd{x - nfmul(\var{nf},round(nfdiv(\var{nf},x,y)),y)}.$$
+
+The library syntax is \fun{GEN}{nfmod}{GEN nf, GEN x, GEN y}.
+
+\subsec{nfeltmul$(\var{nf},x,y)$}\kbdsidx{nfeltmul}\label{se:nfeltmul}
+Given two elements $x$ and $y$ in
+\var{nf}, computes their product $x*y$ in the number field $\var{nf}$.
+
+The library syntax is \fun{GEN}{nfmul}{GEN nf, GEN x, GEN y}.
+
+\subsec{nfeltmulmodpr$(\var{nf},x,y,\var{pr})$}\kbdsidx{nfeltmulmodpr}\label{se:nfeltmulmodpr}
+Given two elements $x$ and
+$y$ in \var{nf} and \var{pr} a prime ideal in \kbd{modpr} format (see
+\tet{nfmodprinit}), computes their product $x*y$ modulo the prime ideal
+\var{pr}.
+
+The library syntax is \fun{GEN}{nfmulmodpr}{GEN nf, GEN x, GEN y, GEN pr}.
+This function is normally useless in library mode. Project your
+inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
+
+\subsec{nfeltnorm$(\var{nf},x)$}\kbdsidx{nfeltnorm}\label{se:nfeltnorm}
+Returns the absolute norm of $x$.
+
+The library syntax is \fun{GEN}{nfnorm}{GEN nf, GEN x}.
+
+\subsec{nfeltpow$(\var{nf},x,k)$}\kbdsidx{nfeltpow}\label{se:nfeltpow}
+Given an element $x$ in \var{nf}, and a positive or negative integer $k$,
+computes $x^k$ in the number field $\var{nf}$.
+
+The library syntax is \fun{GEN}{nfpow}{GEN nf, GEN x, GEN k}.
+\fun{GEN}{nfinv}{GEN nf, GEN x} correspond to $k = -1$, and
+\fun{GEN}{nfsqr}{GEN nf,GEN x} to $k = 2$.
+
+\subsec{nfeltpowmodpr$(\var{nf},x,k,\var{pr})$}\kbdsidx{nfeltpowmodpr}\label{se:nfeltpowmodpr}
+Given an element $x$ in \var{nf}, an integer $k$ and a prime ideal
+\var{pr} in \kbd{modpr} format
+(see \tet{nfmodprinit}), computes $x^k$ modulo the prime ideal \var{pr}.
+
+The library syntax is \fun{GEN}{nfpowmodpr}{GEN nf, GEN x, GEN k, GEN pr}.
+This function is normally useless in library mode. Project your
+inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
+
+\subsec{nfeltreduce$(\var{nf},a,\var{id})$}\kbdsidx{nfeltreduce}\label{se:nfeltreduce}
+Given an ideal \var{id} in
+Hermite normal form and an element $a$ of the number field $\var{nf}$,
+finds an element $r$ in $\var{nf}$ such that $a-r$ belongs to the ideal
+and $r$ is small.
+
+The library syntax is \fun{GEN}{nfreduce}{GEN nf, GEN a, GEN id}.
+
+\subsec{nfeltreducemodpr$(\var{nf},x,\var{pr})$}\kbdsidx{nfeltreducemodpr}\label{se:nfeltreducemodpr}
+Given an element $x$ of the number field $\var{nf}$ and a prime ideal
+\var{pr} in \kbd{modpr} format compute a canonical representative for the
+class of $x$ modulo \var{pr}.
+
+The library syntax is \fun{GEN}{nfreducemodpr}{GEN nf, GEN x, GEN pr}.
+This function is normally useless in library mode. Project your
+inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
+
+\subsec{nfelttrace$(\var{nf},x)$}\kbdsidx{nfelttrace}\label{se:nfelttrace}
+Returns the absolute trace of $x$.
+
+The library syntax is \fun{GEN}{nftrace}{GEN nf, GEN x}.
+
+\subsec{nfeltval$(\var{nf},x,\var{pr})$}\kbdsidx{nfeltval}\label{se:nfeltval}
+Given an element $x$ in
+\var{nf} and a prime ideal \var{pr} in the format output by
+\kbd{idealprimedec}, computes the valuation at \var{pr} of the
+element $x$. The same result can be obtained using
+\kbd{idealval(\var{nf},x,\var{pr})}, since $x$ is then converted to a
+principal ideal.
+
+The library syntax is \fun{long}{nfval}{GEN nf, GEN x, GEN pr}.
+
+\subsec{nffactor$(\var{nf},T)$}\kbdsidx{nffactor}\label{se:nffactor}
+Factorization of the univariate
+polynomial $T$ over the number field $\var{nf}$ given by \kbd{nfinit}; $T$
+has coefficients in $\var{nf}$ (i.e.~either scalar, polmod, polynomial or
+column vector). The factors are sorted by increasing degree.
+
+The main variable of $\var{nf}$ must be of \emph{lower}
+priority than that of $T$, see \secref{se:priority}. However if
+the polynomial defining the number field occurs explicitly  in the
+coefficients of $T$ as modulus of a \typ{POLMOD} or as a \typ{POL}
+coefficient, its main variable must be \emph{the same} as the main variable
+of $T$. For example,
+\bprog
+? nf = nfinit(y^2 + 1);
+? nffactor(nf, x^2 + y); \\@com OK
+? nffactor(nf, x^2 + Mod(y, y^2+1)); \\ @com OK
+? nffactor(nf, x^2 + Mod(z, z^2+1)); \\ @com WRONG
+ at eprog
+
+It is possible to input a defining polynomial for \var{nf}
+instead, but this is in general less efficient since parts of an \kbd{nf}
+structure will then be computed internally. This is useful in two
+situations: when you do not need the \kbd{nf} elsewhere, or when you cannot
+compute the field discriminant due to integer factorization difficulties. In
+the latter case, if you must use a partial discriminant factorization (as
+allowed by both \tet{nfdisc} or \tet{nfbasis}) to build a partially correct
+\var{nf} structure, always input \kbd{nf.pol} to \kbd{nffactor}, and not your
+makeshift \var{nf}: otherwise factors could be missed.
+
+The library syntax is \fun{GEN}{nffactor}{GEN nf, GEN T}.
+
+\subsec{nffactorback$(\var{nf},f,\{e\})$}\kbdsidx{nffactorback}\label{se:nffactorback}
+Gives back the \kbd{nf} element corresponding to a factorization.
+The integer $1$ corresponds to the empty factorization.
+
+If $e$ is present, $e$ and $f$ must be vectors of the same length ($e$ being
+integral), and the corresponding factorization is the product of the
+$f[i]^{e[i]}$.
+
+If not, and $f$ is vector, it is understood as in the preceding case with $e$
+a vector of 1s: we return the product of the $f[i]$. Finally, $f$ can be a
+regular factorization matrix.
+\bprog
+? nf = nfinit(y^2+1);
+? nffactorback(nf, [3, y+1, [1,2]~], [1, 2, 3])
+%2 = [12, -66]~
+? 3 * (I+1)^2 * (1+2*I)^3
+%3 = 12 - 66*I
+ at eprog
+
+The library syntax is \fun{GEN}{nffactorback}{GEN nf, GEN f, GEN e = NULL}.
+
+\subsec{nffactormod$(\var{nf},Q,\var{pr})$}\kbdsidx{nffactormod}\label{se:nffactormod}
+Factors the univariate polynomial $Q$ modulo the prime ideal \var{pr} in
+the number field $\var{nf}$. The coefficients of $Q$ belong to the number
+field (scalar, polmod, polynomial, even column vector) and the main variable
+of $\var{nf}$ must be of lower priority than that of $Q$ (see
+\secref{se:priority}). The prime ideal \var{pr} is either in
+\tet{idealprimedec} or (preferred) \tet{modprinit} format. The coefficients
+of the polynomial factors are lifted to elements of \var{nf}:
+\bprog
+? K = nfinit(y^2+1);
+? P = idealprimedec(K, 3)[1];
+? nffactormod(K, x^2 + y*x + 18*y+1, P)
+%3 =
+[x + (2*y + 1) 1]
+
+[x + (2*y + 2) 1]
+? P = nfmodprinit(K, P);  \\ convert to nfmodprinit format
+? nffactormod(K, x^2 + y*x + 18*y+1)
+[x + (2*y + 1) 1]
+
+[x + (2*y + 2) 1]
+ at eprog\noindent Same result, of course, here about 10\% faster due to the
+precomputation.
+
+The library syntax is \fun{GEN}{nffactormod}{GEN nf, GEN Q, GEN pr}.
+
+\subsec{nfgaloisapply$(\var{nf},\var{aut},x)$}\kbdsidx{nfgaloisapply}\label{se:nfgaloisapply}
+Let $\var{nf}$ be a
+number field as output by \kbd{nfinit}, and let \var{aut} be a \idx{Galois}
+automorphism of $\var{nf}$ expressed by its image on the field generator
+(such automorphisms can be found using \kbd{nfgaloisconj}). The function
+computes the action of the automorphism \var{aut} on the object $x$ in the
+number field; $x$ can be a number field element, or an ideal (possibly
+extended). Because of possible confusion with elements and ideals, other
+vector or matrix arguments are forbidden.
+ \bprog
+ ? nf = nfinit(x^2+1);
+ ? L = nfgaloisconj(nf)
+ %2 = [-x, x]~
+ ? aut = L[1]; /* the non-trivial automorphism */
+ ? nfgaloisapply(nf, aut, x)
+ %4 = Mod(-x, x^2 + 1)
+ ? P = idealprimedec(nf,5); /* prime ideals above 5 */
+ ? nfgaloisapply(nf, aut, P[2]) == P[1]
+ %7 = 0 \\ !!!!
+ ? idealval(nf, nfgaloisapply(nf, aut, P[2]), P[1])
+ %8 = 1
+ at eprog\noindent The surprising failure of the equality test (\kbd{\%7}) is
+due to the fact that although the corresponding prime ideals are equal, their
+representations are not. (A prime ideal is specified by a uniformizer, and
+there is no guarantee that applying automorphisms yields the same elements
+as a direct \kbd{idealprimedec} call.)
+
+The automorphism can also be given as a column vector, representing the
+image of \kbd{Mod(x, nf.pol)} as an algebraic number. This last
+representation is more efficient and should be preferred if a given
+automorphism must be used in many such calls.
+\bprog
+ ? nf = nfinit(x^3 - 37*x^2 + 74*x - 37);
+ ? l = nfgaloisconj(nf); aut = l[2] \\ @com automorphisms in basistoalg form
+ %2 = -31/11*x^2 + 1109/11*x - 925/11
+ ? L = matalgtobasis(nf, l); AUT = L[2] \\ @com same in algtobasis form
+ %3 = [16, -6, 5]~
+ ? v = [1, 2, 3]~; nfgaloisapply(nf, aut, v) == nfgaloisapply(nf, AUT, v)
+ %4 = 1 \\ @com same result...
+ ? for (i=1,10^5, nfgaloisapply(nf, aut, v))
+ time = 1,451 ms.
+ ? for (i=1,10^5, nfgaloisapply(nf, AUT, v))
+ time = 1,045 ms.  \\ @com but the latter is faster
+ at eprog
+
+The library syntax is \fun{GEN}{galoisapply}{GEN nf, GEN aut, GEN x}.
+
+\subsec{nfgaloisconj$(\var{nf},\{\fl=0\},\{d\})$}\kbdsidx{nfgaloisconj}\label{se:nfgaloisconj}
+$\var{nf}$ being a number field as output by \kbd{nfinit}, computes the
+conjugates of a root $r$ of the non-constant polynomial $x=\var{nf}[1]$
+expressed as polynomials in $r$. This also makes sense when the number field
+is not \idx{Galois} since some conjugates may lie in the field.
+$\var{nf}$ can simply be a polynomial.
+
+If no flags or $\fl=0$, use a combination of flag $4$ and $1$ and the result
+is always complete. There is no point whatsoever in using the other flags.
+
+If $\fl=1$, use \kbd{nfroots}: a little slow, but guaranteed to work in
+polynomial time.
+
+If $\fl=2$ (OBSOLETE), use complex approximations to the roots and an integral
+\idx{LLL}. The result is not guaranteed to be complete: some
+conjugates may be missing (a warning is issued if the result is not proved
+complete), especially so if the corresponding polynomial has a huge index,
+and increasing the default precision may help. This variant is slow and
+unreliable: don't use it.
+
+If $\fl=4$, use \kbd{galoisinit}: very fast, but only applies to (most) Galois
+fields. If the field is Galois with weakly
+super-solvable Galois group (see \tet{galoisinit}), return the complete list
+of automorphisms, else only the identity element. If present, $d$ is assumed to
+be a multiple of the least common denominator of the conjugates expressed as
+polynomial in a root of \var{pol}.
+
+This routine can only compute $\Q$-automorphisms, but it may be used to get
+$K$-automorphism for any base field $K$ as follows:
+\bprog
+rnfgaloisconj(nfK, R) = \\ K-automorphisms of L = K[X] / (R)
+{ my(polabs, N);
+  R *= Mod(1, nfK.pol);             \\ convert coeffs to polmod elts of K
+  polabs = rnfequation(nfK, R);
+  N = nfgaloisconj(polabs) % R;     \\ Q-automorphisms of L
+  \\ select the ones that fix K
+  select(s->subst(R, variable(R), Mod(s,R)) == 0, N);
+}
+K  = nfinit(y^2 + 7);
+rnfgaloisconj(K, x^4 - y*x^3 - 3*x^2 + y*x + 1)  \\ K-automorphisms of L
+ at eprog
+
+The library syntax is \fun{GEN}{galoisconj0}{GEN nf, long flag, GEN d = NULL, long prec}.
+Use directly
+\fun{GEN}{galoisconj}{GEN nf, GEN d}, corresponding to $\fl = 0$, the others
+only have historical interest.
+
+\subsec{nfhilbert$(\var{nf},a,b,\{\var{pr}\})$}\kbdsidx{nfhilbert}\label{se:nfhilbert}
+If \var{pr} is omitted,
+compute the global quadratic \idx{Hilbert symbol} $(a,b)$ in $\var{nf}$, that
+is $1$ if $x^2 - a y^2 - b z^2$ has a non trivial solution $(x,y,z)$ in
+$\var{nf}$, and $-1$ otherwise. Otherwise compute the local symbol modulo
+the prime ideal \var{pr}, as output by \kbd{idealprimedec}.
+
+The library syntax is \fun{long}{nfhilbert0}{GEN nf, GEN a, GEN b, GEN pr = NULL}.
+
+Also available is \fun{long}{nfhilbert}{GEN bnf,GEN a,GEN b} (global
+quadratic Hilbert symbol).
+
+\subsec{nfhnf$(\var{nf},x)$}\kbdsidx{nfhnf}\label{se:nfhnf}
+Given a pseudo-matrix $(A,I)$, finds a
+pseudo-basis in \idx{Hermite normal form} of the module it generates.
+
+The library syntax is \fun{GEN}{nfhnf}{GEN nf, GEN x}.
+Also available:
+
+\fun{GEN}{rnfsimplifybasis}{GEN bnf, GEN x} simplifies the pseudo-basis
+given by $x = (A,I)$. The ideals in the list $I$ are integral, primitive and
+either trivial (equal to the full ring of integer) or non-principal.
+
+\subsec{nfhnfmod$(\var{nf},x,\var{detx})$}\kbdsidx{nfhnfmod}\label{se:nfhnfmod}
+Given a pseudo-matrix $(A,I)$
+and an ideal \var{detx} which is contained in (read integral multiple of) the
+determinant of $(A,I)$, finds a pseudo-basis in \idx{Hermite normal form}
+of the module generated by $(A,I)$. This avoids coefficient explosion.
+\var{detx} can be computed using the function \kbd{nfdetint}.
+
+The library syntax is \fun{GEN}{nfhnfmod}{GEN nf, GEN x, GEN detx}.
+
+\subsec{nfinit$(\var{pol},\{\fl=0\})$}\kbdsidx{nfinit}\label{se:nfinit}
+\var{pol} being a non-constant,
+preferably monic, irreducible polynomial in $\Z[X]$, initializes a
+\emph{number field} structure (\kbd{nf}) associated to the field $K$ defined
+by \var{pol}. As such, it's a technical object passed as the first argument
+to most \kbd{nf}\var{xxx} functions, but it contains some information which
+may be directly useful. Access to this information via \emph{member
+functions} is preferred since the specific data organization specified below
+may change in the future. Currently, \kbd{nf} is a row vector with 9
+components:
+
+$\var{nf}[1]$ contains the polynomial \var{pol} (\kbd{\var{nf}.pol}).
+
+$\var{nf}[2]$ contains $[r1,r2]$ (\kbd{\var{nf}.sign}, \kbd{\var{nf}.r1},
+\kbd{\var{nf}.r2}), the number of real and complex places of $K$.
+
+$\var{nf}[3]$ contains the discriminant $d(K)$ (\kbd{\var{nf}.disc}) of $K$.
+
+$\var{nf}[4]$ contains the index of $\var{nf}[1]$ (\kbd{\var{nf}.index}),
+i.e.~$[\Z_K : \Z[\theta]]$, where $\theta$ is any root of $\var{nf}[1]$.
+
+$\var{nf}[5]$ is a vector containing 7 matrices $M$, $G$, \var{roundG}, $T$,
+$MD$, $TI$, $MDI$ useful for certain computations in the number field $K$.
+
+\quad\item $M$ is the $(r1+r2)\times n$ matrix whose columns represent
+the numerical values of the conjugates of the elements of the integral
+basis.
+
+\quad\item $G$ is an $n\times n$ matrix such that $T2 = {}^t G G$,
+where $T2$ is the quadratic form $T_2(x) = \sum |\sigma(x)|^2$, $\sigma$
+running over the embeddings of $K$ into $\C$.
+
+\quad\item \var{roundG} is a rescaled copy of $G$, rounded to nearest
+integers.
+
+\quad\item $T$ is the $n\times n$ matrix whose coefficients are
+$\text{Tr}(\omega_i\omega_j)$ where the $\omega_i$ are the elements of the
+integral basis. Note also that $\det(T)$ is equal to the discriminant of the
+field $K$. Also, when understood as an ideal, the matrix $T^{-1}$
+generates the codifferent ideal.
+
+\quad\item The columns of $MD$ (\kbd{\var{nf}.diff}) express a $\Z$-basis
+of the different of $K$ on the integral basis.
+
+\quad\item $TI$ is equal to the primitive part of $T^{-1}$, which has integral
+coefficients.
+
+\quad\item Finally, $MDI$ is a two-element representation (for faster
+ideal product) of $d(K)$ times the codifferent ideal
+(\kbd{\var{nf}.disc$*$\var{nf}.codiff}, which is an integral ideal). $MDI$
+is only used in \tet{idealinv}.
+
+$\var{nf}[6]$ is the vector containing the $r1+r2$ roots
+(\kbd{\var{nf}.roots}) of $\var{nf}[1]$ corresponding to the $r1+r2$
+embeddings of the number field into $\C$ (the first $r1$ components are real,
+the next $r2$ have positive imaginary part).
+
+$\var{nf}[7]$ is an integral basis for $\Z_K$ (\kbd{\var{nf}.zk}) expressed
+on the powers of~$\theta$. Its first element is guaranteed to be $1$. This
+basis is LLL-reduced with respect to $T_2$ (strictly speaking, it is a
+permutation of such a basis, due to the condition that the first element be
+$1$).
+
+$\var{nf}[8]$ is the $n\times n$ integral matrix expressing the power
+basis in terms of the integral basis, and finally
+
+$\var{nf}[9]$ is the $n\times n^2$ matrix giving the multiplication table
+of the integral basis.
+
+If a non monic polynomial is input, \kbd{nfinit} will transform it into a
+monic one, then reduce it (see $\fl=3$). It is allowed, though not very
+useful given the existence of \tet{nfnewprec}, to input a \kbd{nf} or a
+\kbd{bnf} instead of a polynomial.
+
+\bprog
+? nf = nfinit(x^3 - 12); \\ initialize number field Q[X] / (X^3 - 12)
+? nf.pol   \\ defining polynomial
+%2 = x^3 - 12
+? nf.disc  \\ field discriminant
+%3 = -972
+? nf.index \\ index of power basis order in maximal order
+%4 = 2
+? nf.zk    \\ integer basis, lifted to Q[X]
+%5 = [1, x, 1/2*x^2]
+? nf.sign  \\ signature
+%6 = [1, 1]
+? factor(abs(nf.disc ))  \\ determines ramified primes
+%7 =
+[2 2]
+
+[3 5]
+? idealfactor(nf, 2)
+%8 =
+[[2, [0, 0, -1]~, 3, 1, [0, 1, 0]~] 3]  \\ @com $\goth{p}_2^3$
+ at eprog
+
+\misctitle{Huge discriminants, helping nfdisc}
+
+In case \var{pol} has a huge discriminant which is difficult to factor,
+it is hard to compute from scratch the maximal order. The special input
+format $[\var{pol}, B]$ is also accepted where \var{pol} is a polynomial as
+above and $B$ has one of the following forms
+
+\item an integer basis, as would be computed by \tet{nfbasis}: a vector of
+polynomials with first element $1$. This is useful if the maximal order is
+known in advance.
+
+\item an argument \kbd{listP} which specifies a list of primes (see
+\tet{nfbasis}). Instead of the maximal order, \kbd{nfinit} then computes an
+order which is maximal at these particular primes as well as the primes
+contained in the private prime table (see \tet{addprimes}). The result is
+unconditionaly correct when the discriminant \kbd{nf.disc} factors
+completely over this set of primes. The function \tet{nfcertify} automates
+this:
+\bprog
+? pol = polcompositum(x^5 - 101, polcyclo(7))[1];
+? nf = nfinit( [pol, 10^3] );
+? nfcertify(nf)
+%3 = []
+ at eprog\noindent A priori, \kbd{nf.zk} defines an order which is only known
+to be maximal at all primes $\leq 10^3$ (no prime $\leq 10^3$ divides
+\kbd{nf.index}). The certification step proves the correctness of the
+computation.
+\medskip
+
+If $\fl=2$: \var{pol} is changed into another polynomial $P$ defining the same
+number field, which is as simple as can easily be found using the
+\tet{polredbest} algorithm, and all the subsequent computations are done
+using this new polynomial. In particular, the first component of the result
+is the modified polynomial.
+
+If $\fl=3$, apply \kbd{polredbest} as in case 2, but outputs
+$[\var{nf},\kbd{Mod}(a,P)]$, where $\var{nf}$ is as before and
+$\kbd{Mod}(a,P)=\kbd{Mod}(x,\var{pol})$ gives the change of
+variables. This is implicit when \var{pol} is not monic: first a linear change
+of variables is performed, to get a monic polynomial, then \kbd{polredbest}.
+
+The library syntax is \fun{GEN}{nfinit0}{GEN pol, long flag, long prec}.
+Also available are
+\fun{GEN}{nfinit}{GEN x, long prec} ($\fl = 0$),
+\fun{GEN}{nfinitred}{GEN x, long prec} ($\fl = 2$),
+\fun{GEN}{nfinitred2}{GEN x, long prec} ($\fl = 3$).
+Instead of the above hardcoded numerical flags in \kbd{nfinit0}, one should
+rather use
+
+\fun{GEN}{nfinitall}{GEN x, long flag, long prec}, where \fl\ is an
+or-ed combination of
+
+\item \tet{nf_RED}: find a simpler defining polynomial,
+
+\item \tet{nf_ORIG}: if \tet{nf_RED} set, also return the change of variable,
+
+\item \tet{nf_ROUND2}: \emph{Deprecated}. Slow down the routine by using an
+obsolete normalization algorithm (do not use this one!),
+
+\item \tet{nf_PARTIALFACT}: \emph{Deprecated}. Lazy factorization of the
+polynomial discriminant. Result is conditional unless \kbd{nfcertify}
+can certify it.
+
+\subsec{nfisideal$(\var{nf},x)$}\kbdsidx{nfisideal}\label{se:nfisideal}
+Returns 1 if $x$ is an ideal in the number field $\var{nf}$, 0 otherwise.
+
+The library syntax is \fun{long}{isideal}{GEN nf, GEN x}.
+
+\subsec{nfisincl$(x,y)$}\kbdsidx{nfisincl}\label{se:nfisincl}
+Tests whether the number field $K$ defined
+by the polynomial $x$ is conjugate to a subfield of the field $L$ defined
+by $y$ (where $x$ and $y$ must be in $\Q[X]$). If they are not, the output
+is the number 0. If they are, the output is a vector of polynomials, each
+polynomial $a$ representing an embedding of $K$ into $L$, i.e.~being such
+that $y\mid x\circ a$.
+
+If $y$ is a number field (\var{nf}), a much faster algorithm is used
+(factoring $x$ over $y$ using \tet{nffactor}). Before version 2.0.14, this
+wasn't guaranteed to return all the embeddings, hence was triggered by a
+special flag. This is no more the case.
+
+The library syntax is \fun{GEN}{nfisincl}{GEN x, GEN y}.
+
+\subsec{nfisisom$(x,y)$}\kbdsidx{nfisisom}\label{se:nfisisom}
+As \tet{nfisincl}, but tests for isomorphism. If either $x$ or $y$ is a
+number field, a much faster algorithm will be used.
+
+The library syntax is \fun{GEN}{nfisisom}{GEN x, GEN y}.
+
+\subsec{nfkermodpr$(\var{nf},x,\var{pr})$}\kbdsidx{nfkermodpr}\label{se:nfkermodpr}
+Kernel of the matrix $a$ in $\Z_K/\var{pr}$, where \var{pr} is in
+\key{modpr} format (see \kbd{nfmodprinit}).
+
+The library syntax is \fun{GEN}{nfkermodpr}{GEN nf, GEN x, GEN pr}.
+This function is normally useless in library mode. Project your
+inputs to the residue field using \kbd{nfM\_to\_FqM}, then work there.
+
+\subsec{nfmodprinit$(\var{nf},\var{pr})$}\kbdsidx{nfmodprinit}\label{se:nfmodprinit}
+Transforms the prime ideal \var{pr} into \tet{modpr} format necessary
+for all operations modulo \var{pr} in the number field \var{nf}.
+
+The library syntax is \fun{GEN}{nfmodprinit}{GEN nf, GEN pr}.
+
+\subsec{nfnewprec$(\var{nf})$}\kbdsidx{nfnewprec}\label{se:nfnewprec}
+Transforms the number field $\var{nf}$
+into the corresponding data using current (usually larger) precision. This
+function works as expected if $\var{nf}$ is in fact a $\var{bnf}$ (update
+$\var{bnf}$ to current precision) but may be quite slow (many generators of
+principal ideals have to be computed).
+
+The library syntax is \fun{GEN}{nfnewprec}{GEN nf, long prec}.
+See also \fun{GEN}{bnfnewprec}{GEN bnf, long prec}
+and \fun{GEN}{bnrnewprec}{GEN bnr, long prec}.
+
+\subsec{nfroots$(\{\var{nf}\},x)$}\kbdsidx{nfroots}\label{se:nfroots}
+Roots of the polynomial $x$ in the
+number field $\var{nf}$ given by \kbd{nfinit} without multiplicity (in $\Q$
+if $\var{nf}$ is omitted). $x$ has coefficients in the number field (scalar,
+polmod, polynomial, column vector). The main variable of $\var{nf}$ must be
+of lower priority than that of $x$ (see \secref{se:priority}). However if the
+coefficients of the number field occur explicitly (as polmods) as
+coefficients of $x$, the variable of these polmods \emph{must} be the same as
+the main variable of $t$ (see \kbd{nffactor}).
+
+It is possible to input a defining polynomial for \var{nf}
+instead, but this is in general less efficient since parts of an \kbd{nf}
+structure will be computed internally. This is useful in two situations: when
+you don't need the \kbd{nf}, or when you can't compute its discriminant due
+to integer factorization difficulties. In the latter case, \tet{addprimes} is
+a possibility but a dangerous one: roots will probably be missed if the
+(true) field discriminant and an \kbd{addprimes} entry are strictly divisible
+by some prime. If you have such an unsafe \var{nf}, it is safer to input
+\kbd{nf.pol}.
+
+The library syntax is \fun{GEN}{nfroots}{GEN nf = NULL, GEN x}.
+See also \fun{GEN}{nfrootsQ}{GEN x},
+corresponding to $\kbd{nf} = \kbd{NULL}$.
+
+\subsec{nfrootsof1$(\var{nf})$}\kbdsidx{nfrootsof1}\label{se:nfrootsof1}
+Returns a two-component vector $[w,z]$ where $w$ is the number of roots of
+unity in the number field \var{nf}, and $z$ is a primitive $w$-th root
+of unity.
+\bprog
+? K = nfinit(polcyclo(11));
+? nfrootsof1(K)
+%2 = [22, [0, 0, 0, 0, 0, -1, 0, 0, 0, 0]~]
+? z = nfbasistoalg(K, %[2])   \\ in algebraic form
+%3 = Mod(-x^5, x^10 + x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)
+? [lift(z^11), lift(z^2)]     \\ proves that the order of z is 22
+%4 = [-1, -x^9 - x^8 - x^7 - x^6 - x^5 - x^4 - x^3 - x^2 - x - 1]
+ at eprog
+This function guesses the number $w$ as the gcd of the $\#k(v)^*$ for
+unramified $v$ above odd primes, then computes the roots in \var{nf}
+of the $w$-th cyclotomic polynomial: the algorithm is polynomial time with
+respect to the field degree and the bitsize of the multiplication table in
+\var{nf} (both of them polynomially bounded in terms of the size of the
+discriminant). Fields of degree up to $100$ or so should require less than
+one minute.
+
+The library syntax is \fun{GEN}{rootsof1}{GEN nf}.
+Also available is \fun{GEN}{rootsof1_kannan}{GEN nf}, that computes
+all algebraic integers of $T_2$ norm equal to the field degree
+(all roots of $1$, by Kronecker's theorem). This is in general a little
+faster than the default when there \emph{are} roots of $1$ in the field
+(say twice faster), but can be much slower (say, \emph{days} slower), since
+the algorithm is a priori exponential in the field degree.
+
+\subsec{nfsnf$(\var{nf},x)$}\kbdsidx{nfsnf}\label{se:nfsnf}
+Given a $\Z_K$-module $x$ associated to the integral pseudo-matrix
+$(A,I,J)$, returns an ideal list $d_1,\dots,d_n$ which is the \idx{Smith
+normal form} of $x$. In other words, $x$ is isomorphic to
+$\Z_K/d_1\oplus\cdots\oplus\Z_K/d_n$ and $d_i$ divides $d_{i-1}$ for $i\ge2$.
+
+See \secref{se:ZKmodules} for the definition of integral pseudo-matrix;
+briefly, it is input as a 3-component row vector $[A,I,J]$ where
+$I = [b_1,\dots,b_n]$ and $J = [a_1,\dots,a_n]$ are two ideal lists,
+and $A$ is a square $n\times n$ matrix with columns $(A_1,\dots,A_n)$,
+seen as elements in $K^n$ (with canonical basis $(e_1,\dots,e_n)$).
+This data defines the $\Z_K$ module $x$ given by
+$$ (b_1e_1\oplus\cdots\oplus b_ne_n) / (a_1A_1\oplus\cdots\oplus a_nA_n)
+\enspace, $$
+The integrality condition is $a_{i,j} \in b_i a_j^{-1}$ for all $i,j$. If it
+is not satisfied, then the $d_i$ will not be integral. Note that every
+finitely generated torsion module is isomorphic to a module of this form and
+even with $b_i=Z_K$ for all $i$.
+
+The library syntax is \fun{GEN}{nfsnf}{GEN nf, GEN x}.
+
+\subsec{nfsolvemodpr$(\var{nf},a,b,P)$}\kbdsidx{nfsolvemodpr}\label{se:nfsolvemodpr}
+Let $P$ be a prime ideal in \key{modpr} format (see \kbd{nfmodprinit}),
+let $a$ be a matrix, invertible over the residue field, and let $b$ be
+a column vector or matrix. This function returns a solution of $a\cdot x =
+b$; the coefficients of $x$ are lifted to \var{nf} elements.
+\bprog
+? K = nfinit(y^2+1);
+? P = idealprimedec(K, 3)[1];
+? P = nfmodprinit(K, P);
+? a = [y+1, y; y, 0]; b = [1, y]~
+? nfsolvemodpr(K, a,b, P)
+%5 = [1, 2]~
+ at eprog
+
+The library syntax is \fun{GEN}{nfsolvemodpr}{GEN nf, GEN a, GEN b, GEN P}.
+This function is normally useless in library mode. Project your
+inputs to the residue field using \kbd{nfM\_to\_FqM}, then work there.
+
+\subsec{nfsubfields$(\var{pol},\{d=0\})$}\kbdsidx{nfsubfields}\label{se:nfsubfields}
+Finds all subfields of degree
+$d$ of the number field defined by the (monic, integral) polynomial
+\var{pol} (all subfields if $d$ is null or omitted). The result is a vector
+of subfields, each being given by $[g,h]$, where $g$ is an absolute equation
+and $h$ expresses one of the roots of $g$ in terms of the root $x$ of the
+polynomial defining $\var{nf}$. This routine uses J.~Kl\"uners's algorithm
+in the general case, and B.~Allombert's \tet{galoissubfields} when \var{nf}
+is Galois (with weakly supersolvable Galois group).\sidx{Galois}\sidx{subfield}
+
+The library syntax is \fun{GEN}{nfsubfields}{GEN pol, long d}.
+
+\subsec{polcompositum$(P,Q,\{\fl=0\})$}\kbdsidx{polcompositum}\label{se:polcompositum}
+\sidx{compositum} $P$ and $Q$
+being squarefree polynomials in $\Z[X]$ in the same variable, outputs
+the simple factors of the \'etale $\Q$-algebra $A = \Q(X, Y) / (P(X), Q(Y))$.
+The factors are given by a list of polynomials $R$ in $\Z[X]$, associated to
+the number field $\Q(X)/ (R)$, and sorted by increasing degree (with respect
+to lexicographic ordering for factors of equal degrees). Returns an error if
+one of the polynomials is not squarefree.
+
+Note that it is more efficient to reduce to the case where $P$ and $Q$ are
+irreducible first. The routine will not perform this for you, since it may be
+expensive, and the inputs are irreducible in most applications anyway. In
+this case, there will be a single factor $R$ if and only if the number
+fields defined by $P$ and $Q$ are disjoint.
+
+Assuming $P$ is irreducible (of smaller degree than $Q$ for efficiency), it
+is in general much faster to proceed as follows
+\bprog
+nf = nfinit(P); L = nffactor(nf, Q)[,1];
+vector(#L, i, rnfequation(nf, L[i]))
+ at eprog\noindent
+to obtain the same result. If you are only interested in the degrees of the
+simple factors, the \kbd{rnfequation} instruction can be replaced by a
+trivial \kbd{poldegree(P) * poldegree(L[i])}.
+
+If $\fl=1$, outputs a vector of 4-component vectors $[R,a,b,k]$, where $R$
+ranges through the list of all possible compositums as above, and $a$
+(resp. $b$) expresses the root of $P$ (resp. $Q$) as an element of
+$\Q(X)/(R)$. Finally, $k$ is a small integer such that $b + ka = X$ modulo
+$R$.
+
+A compositum is often defined by a complicated polynomial, which it is
+advisable to reduce before further work. Here is an example involving
+the field $\Q(\zeta_5, 5^{1/5})$:
+\bprog
+? L = polcompositum(x^5 - 5, polcyclo(5), 1); \\@com list of $[R,a,b,k]$
+? [R, a] = L[1];  \\@com pick the single factor, extract $R,a$ (ignore $b,k$)
+? R               \\@com defines the compositum
+%3 = x^20 + 5*x^19 + 15*x^18 + 35*x^17 + 70*x^16 + 141*x^15 + 260*x^14\
++ 355*x^13 + 95*x^12 - 1460*x^11 - 3279*x^10 - 3660*x^9 - 2005*x^8    \
++ 705*x^7 + 9210*x^6 + 13506*x^5 + 7145*x^4 - 2740*x^3 + 1040*x^2     \
+- 320*x + 256
+? a^5 - 5         \\@com a fifth root of $5$
+%4 = 0
+? [T, X] = polredbest(R, 1);
+? T     \\@com simpler defining polynomial for $\Q[x]/(R)$
+%6 = x^20 + 25*x^10 + 5
+? X     \\ @com root of $R$ in $\Q[y]/(T(y))$
+%7 = Mod(-1/11*x^15 - 1/11*x^14 + 1/22*x^10 - 47/22*x^5 - 29/11*x^4 + 7/22,\
+x^20 + 25*x^10 + 5)
+? a = subst(a.pol, 'x, X)  \\@com \kbd{a} in the new coordinates
+%8 = Mod(1/11*x^14 + 29/11*x^4, x^20 + 25*x^10 + 5)
+? a^5 - 5
+%9 = 0
+ at eprog
+
+The library syntax is \fun{GEN}{polcompositum0}{GEN P, GEN Q, long flag}.
+Also available are
+\fun{GEN}{compositum}{GEN P, GEN Q} ($\fl = 0$) and
+\fun{GEN}{compositum2}{GEN P, GEN Q} ($\fl = 1$).
+
+\subsec{polgalois$(T)$}\kbdsidx{polgalois}\label{se:polgalois}
+\idx{Galois} group of the non-constant
+polynomial $T\in\Q[X]$. In the present version \vers, $T$ must be irreducible
+and the degree $d$ of $T$ must be less than or equal to 7. If the
+\tet{galdata} package has been installed, degrees 8, 9, 10 and 11 are also
+implemented. By definition, if $K = \Q[x]/(T)$, this computes the action of
+the Galois group of the Galois closure of $K$ on the $d$ distinct roots of
+$T$, up to conjugacy (corresponding to different root orderings).
+
+The output is a 4-component vector $[n,s,k,name]$ with the
+following meaning: $n$ is the cardinality of the group, $s$ is its signature
+($s=1$ if the group is a subgroup of the alternating group $A_d$, $s=-1$
+otherwise) and name is a character string containing name of the transitive
+group according to the GAP 4 transitive groups library by Alexander Hulpke.
+
+$k$ is more arbitrary and the choice made up to version~2.2.3 of PARI is rather
+unfortunate: for $d > 7$, $k$ is the numbering of the group among all
+transitive subgroups of $S_d$, as given in ``The transitive groups of degree up
+to eleven'', G.~Butler and J.~McKay, \emph{Communications in Algebra}, vol.~11,
+1983,
+pp.~863--911 (group $k$ is denoted $T_k$ there). And for $d \leq 7$, it was ad
+hoc, so as to ensure that a given triple would denote a unique group.
+Specifically, for polynomials of degree $d\leq 7$, the groups are coded as
+follows, using standard notations
+\smallskip
+In degree 1: $S_1=[1,1,1]$.
+\smallskip
+In degree 2: $S_2=[2,-1,1]$.
+\smallskip
+In degree 3: $A_3=C_3=[3,1,1]$, $S_3=[6,-1,1]$.
+\smallskip
+In degree 4: $C_4=[4,-1,1]$, $V_4=[4,1,1]$, $D_4=[8,-1,1]$, $A_4=[12,1,1]$,
+$S_4=[24,-1,1]$.
+\smallskip
+In degree 5: $C_5=[5,1,1]$, $D_5=[10,1,1]$, $M_{20}=[20,-1,1]$,
+$A_5=[60,1,1]$, $S_5=[120,-1,1]$.
+\smallskip
+In degree 6: $C_6=[6,-1,1]$, $S_3=[6,-1,2]$, $D_6=[12,-1,1]$, $A_4=[12,1,1]$,
+$G_{18}=[18,-1,1]$, $S_4^-=[24,-1,1]$, $A_4\times C_2=[24,-1,2]$,
+$S_4^+=[24,1,1]$, $G_{36}^-=[36,-1,1]$, $G_{36}^+=[36,1,1]$,
+$S_4\times C_2=[48,-1,1]$, $A_5=PSL_2(5)=[60,1,1]$, $G_{72}=[72,-1,1]$,
+$S_5=PGL_2(5)=[120,-1,1]$, $A_6=[360,1,1]$, $S_6=[720,-1,1]$.
+\smallskip
+In degree 7: $C_7=[7,1,1]$, $D_7=[14,-1,1]$, $M_{21}=[21,1,1]$,
+$M_{42}=[42,-1,1]$, $PSL_2(7)=PSL_3(2)=[168,1,1]$, $A_7=[2520,1,1]$,
+$S_7=[5040,-1,1]$.
+\smallskip
+This is deprecated and obsolete, but for reasons of backward compatibility,
+we cannot change this behavior yet. So you can use the default
+\tet{new_galois_format} to switch to a consistent naming scheme, namely $k$ is
+always the standard numbering of the group among all transitive subgroups of
+$S_n$. If this default is in effect, the above groups will be coded as:
+\smallskip
+In degree 1: $S_1=[1,1,1]$.
+\smallskip
+In degree 2: $S_2=[2,-1,1]$.
+\smallskip
+In degree 3: $A_3=C_3=[3,1,1]$, $S_3=[6,-1,2]$.
+\smallskip
+In degree 4: $C_4=[4,-1,1]$, $V_4=[4,1,2]$, $D_4=[8,-1,3]$, $A_4=[12,1,4]$,
+$S_4=[24,-1,5]$.
+\smallskip
+In degree 5: $C_5=[5,1,1]$, $D_5=[10,1,2]$, $M_{20}=[20,-1,3]$,
+$A_5=[60,1,4]$, $S_5=[120,-1,5]$.
+\smallskip
+In degree 6: $C_6=[6,-1,1]$, $S_3=[6,-1,2]$, $D_6=[12,-1,3]$, $A_4=[12,1,4]$,
+$G_{18}=[18,-1,5]$, $A_4\times C_2=[24,-1,6]$, $S_4^+=[24,1,7]$,
+$S_4^-=[24,-1,8]$, $G_{36}^-=[36,-1,9]$, $G_{36}^+=[36,1,10]$,
+$S_4\times C_2=[48,-1,11]$, $A_5=PSL_2(5)=[60,1,12]$, $G_{72}=[72,-1,13]$,
+$S_5=PGL_2(5)=[120,-1,14]$, $A_6=[360,1,15]$, $S_6=[720,-1,16]$.
+\smallskip
+In degree 7: $C_7=[7,1,1]$, $D_7=[14,-1,2]$, $M_{21}=[21,1,3]$,
+$M_{42}=[42,-1,4]$, $PSL_2(7)=PSL_3(2)=[168,1,5]$, $A_7=[2520,1,6]$,
+$S_7=[5040,-1,7]$.
+\smallskip
+
+\misctitle{Warning} The method used is that of resolvent polynomials and is
+sensitive to the current precision. The precision is updated internally but,
+in very rare cases, a wrong result may be returned if the initial precision
+was not sufficient.
+
+The library syntax is \fun{GEN}{polgalois}{GEN T, long prec}.
+To enable the new format in library mode,
+set the global variable \tet{new_galois_format} to $1$.
+
+\subsec{polred$(T,\{\fl=0\})$}\kbdsidx{polred}\label{se:polred}
+This function is \emph{deprecated}, use \tet{polredbest} instead.
+Finds polynomials with reasonably small coefficients defining subfields of
+the number field defined by $T$. One of the polynomials always defines $\Q$
+(hence is equal to $x-1$), and another always defines the same number field
+as $T$ if $T$ is irreducible.
+
+All $T$ accepted by \tet{nfinit} are also allowed here;
+in particular, the format \kbd{[T, listP]} is recommended, e.g. with
+$\kbd{listP} = 10^5$ or a vector containing all ramified primes. Otherwise,
+the maximal order of $\Q[x]/(T)$ must be computed.
+
+The following binary digits of $\fl$ are significant:
+
+1: Possibly use a suborder of the maximal order. The
+primes dividing the index of the order chosen are larger than
+\tet{primelimit} or divide integers stored in the \tet{addprimes} table.
+This flag is \emph{deprecated}, the \kbd{[T, listP]} format is more
+flexible.
+
+2: gives also elements. The result is a two-column matrix, the first column
+giving primitive elements defining these subfields, the second giving the
+corresponding minimal polynomials.
+\bprog
+? M = polred(x^4 + 8, 2)
+%1 =
+[1 x - 1]
+
+[1/2*x^2 x^2 + 2]
+
+[1/4*x^3 x^4 + 2]
+
+[x x^4 + 8]
+? minpoly(Mod(M[2,1], x^4+8))
+%2 = x^2 + 2
+ at eprog
+
+\synt{polred}{GEN T} ($\fl = 0$). Also available is
+\fun{GEN}{polred2}{GEN T} ($\fl = 2$). The function \kbd{polred0} is
+deprecated, provided for backward compatibility.
+
+\subsec{polredabs$(T,\{\fl=0\})$}\kbdsidx{polredabs}\label{se:polredabs}
+Returns a canonical defining polynomial $P$ for the number field
+$\Q[X]/(T)$ defined by $T$, such that the sum of the squares of the modulus
+of the roots (i.e.~the $T_2$-norm) is minimal. Different $T$ defining
+isomorphic number fields will yield the same $P$. All $T$ accepted by
+\tet{nfinit} are also allowed here, e.g. non-monic polynomials, or pairs
+\kbd{[T, listP]} specifying that a non-maximal order may be used.
+
+\misctitle{Warning 1} Using a \typ{POL} $T$ requires fully factoring the
+discriminant of $T$, which may be very hard. The format \kbd{[T, listP]}
+computes only a suborder of the maximal order and replaces this part of the
+algorithm by a polynomial time computation. In that case the polynomial $P$
+is a priori no longer canonical, and it may happen that it does not have
+minimal $T_2$ norm. The routine attempts to certify the result independently
+of this order computation (as per \tet{nfcertify}: we try to prove that the
+order is maximal); if it fails, the routine returns $0$ instead of $P$.
+In order to force an output in that case as well, you may either use
+\tet{polredbest}, or \kbd{polredabs(,16)}, or
+\bprog
+  polredabs([T, nfbasis([T, listP])])
+ at eprog\noindent (In all three cases, the result is no longer canonical.)
+
+\misctitle{Warning 2} Apart from the factorization of the discriminant of
+$T$, this routine runs in polynomial time for a \emph{fixed} degree.
+But the complexity is exponential in the degree: this routine
+may be exceedingly slow when the number field has many subfields, hence a
+lot of elements of small $T_2$-norm. If you do not need a canonical
+polynomial, the function \tet{polredbest} is in general much faster (it runs
+in polynomial time), and tends to return polynomials with smaller
+discriminants.
+
+The binary digits of $\fl$ mean
+
+1: outputs a two-component row vector $[P,a]$, where $P$ is the default
+output and \kbd{Mod(a, P)} is a root of the original $T$.
+
+4: gives \emph{all} polynomials of minimal $T_2$ norm; of the two polynomials
+$P(x)$ and $\pm P(-x)$, only one is given.
+
+16: Possibly use a suborder of the maximal order, \emph{without} attempting to
+certify the result as in Warning 1: we always return a polynomial and never
+$0$. The result is a priori not canonical.
+
+\bprog
+? T = x^16 - 136*x^14 + 6476*x^12 - 141912*x^10 + 1513334*x^8 \
+      - 7453176*x^6 + 13950764*x^4 - 5596840*x^2 + 46225
+? T1 = polredabs(T); T2 = polredbest(T);
+? [ norml2(polroots(T1)), norml2(polroots(T2)) ]
+%3 = [88.0000000, 120.000000]
+? [ sizedigit(poldisc(T1)), sizedigit(poldisc(T2)) ]
+%4 = [75, 67]
+ at eprog
+
+The library syntax is \fun{GEN}{polredabs0}{GEN T, long flag}.
+Instead of the above hardcoded numerical flags, one should use an
+or-ed combination of
+
+\item \tet{nf_PARTIALFACT}: possibly use a suborder of the maximal order,
+\emph{without} attempting to certify the result.
+
+\item \tet{nf_ORIG}: return $[P, a]$, where \kbd{Mod(a, P)} is a root of $T$.
+
+\item \tet{nf_RAW}: return $[P, b]$, where \kbd{Mod(b, T)} is a root of $P$.
+The algebraic integer $b$ is the raw result produced by the small vectors
+enumeration in the maximal order; $P$ was computed as the characteristic
+polynomial of \kbd{Mod(b, T)}. \kbd{Mod(a, P)} as in \tet{nf_ORIG}
+is obtained with \tet{modreverse}.
+
+\item \tet{nf_ADDZK}: if $r$ is the result produced with some of the above
+flags (of the form $P$ or $[P,c]$), return \kbd{[r,zk]}, where \kbd{zk} is a
+$\Z$-basis for the maximal order of $\Q[X]/(P)$.
+
+\item \tet{nf_ALL}: return a vector of results of the above form, for all
+polynomials of minimal $T_2$-norm.
+
+\subsec{polredbest$(T,\{\fl=0\})$}\kbdsidx{polredbest}\label{se:polredbest}
+Finds a polynomial with reasonably
+small coefficients defining the same number field as $T$.
+All $T$ accepted by \tet{nfinit} are also allowed here (e.g. non-monic
+polynomials, \kbd{nf}, \kbd{bnf}, \kbd{[T,Z\_K\_basis]}). Contrary to
+\tet{polredabs}, this routine runs in polynomial time, but it offers no
+guarantee as to the minimality of its result.
+
+This routine computes an LLL-reduced basis for the ring of integers of
+$\Q[X]/(T)$, then examines small linear combinations of the basis vectors,
+computing their characteristic polynomials. It returns the \emph{separable}
+$P$ polynomial of smallest discriminant (the one with lexicographically
+smallest \kbd{abs(Vec(P))} in case of ties). This is a good candidate
+for subsequent number field computations, since it guarantees that
+the denominators of algebraic integers, when expressed in the power basis,
+are reasonably small. With no claim of minimality, though.
+
+It can happen that iterating this functions yields better and better
+polynomials, until it stabilizes:
+\bprog
+? \p5
+? P = X^12+8*X^8-50*X^6+16*X^4-3069*X^2+625;
+? poldisc(P)*1.
+%2 = 1.2622 E55
+? P = polredbest(P);
+? poldisc(P)*1.
+%4 = 2.9012 E51
+? P = polredbest(P);
+? poldisc(P)*1.
+%6 = 8.8704 E44
+ at eprog\noindent In this example, the initial polynomial $P$ is the one
+returned by \tet{polredabs}, and the last one is stable.
+
+If $\fl = 1$: outputs a two-component row vector $[P,a]$,  where $P$ is the
+default output and \kbd{Mod(a, P)} is a root of the original $T$.
+\bprog
+? [P,a] = polredbest(x^4 + 8, 1)
+%1 = [x^4 + 2, Mod(x^3, x^4 + 2)]
+? charpoly(a)
+%2 = x^4 + 8
+ at eprog\noindent In particular, the map $\Q[x]/(T) \to \Q[x]/(P)$,
+$x\mapsto \kbd{Mod(a,P)}$ defines an isomorphism of number fields, which can
+be computed as
+\bprog
+  subst(lift(Q), 'x, a)
+ at eprog\noindent if $Q$ is a \typ{POLMOD} modulo $T$; \kbd{b = modreverse(a)}
+returns a \typ{POLMOD} giving the inverse of the above map (which should be
+useless since $\Q[x]/(P)$ is a priori a better representation for the number
+field and its elements).
+
+The library syntax is \fun{GEN}{polredbest}{GEN T, long flag}.
+
+\subsec{polredord$(x)$}\kbdsidx{polredord}\label{se:polredord}
+Finds polynomials with reasonably small
+coefficients and of the same degree as that of $x$ defining suborders of the
+order defined by $x$. One of the polynomials always defines $\Q$ (hence
+is equal to $(x-1)^n$, where $n$ is the degree), and another always defines
+the same order as $x$ if $x$ is irreducible. Useless function: try
+\kbd{polredbest}.
+
+The library syntax is \fun{GEN}{polredord}{GEN x}.
+
+\subsec{poltschirnhaus$(x)$}\kbdsidx{poltschirnhaus}\label{se:poltschirnhaus}
+Applies a random Tschirnhausen
+transformation to the polynomial $x$, which is assumed to be non-constant
+and separable, so as to obtain a new equation for the \'etale algebra
+defined by $x$. This is for instance useful when computing resolvents,
+hence is used by the \kbd{polgalois} function.
+
+The library syntax is \fun{GEN}{tschirnhaus}{GEN x}.
+
+\subsec{rnfalgtobasis$(\var{rnf},x)$}\kbdsidx{rnfalgtobasis}\label{se:rnfalgtobasis}
+Expresses $x$ on the relative
+integral basis. Here, $\var{rnf}$ is a relative number field extension $L/K$
+as output by \kbd{rnfinit}, and $x$ an element of $L$ in absolute form, i.e.
+expressed as a polynomial or polmod with polmod coefficients, \emph{not} on
+the relative integral basis.
+
+The library syntax is \fun{GEN}{rnfalgtobasis}{GEN rnf, GEN x}.
+
+\subsec{rnfbasis$(\var{bnf},M)$}\kbdsidx{rnfbasis}\label{se:rnfbasis}
+Let $K$ the field represented by
+\var{bnf}, as output by \kbd{bnfinit}. $M$ is a projective $\Z_K$-module
+of rank $n$ ($M\otimes K$ is an $n$-dimensional $K$-vector space), given by a
+pseudo-basis of size $n$. The routine returns either a true $\Z_K$-basis of
+$M$ (of size $n$) if it exists, or an $n+1$-element generating set of $M$ if
+not.
+
+It is allowed to use an irreducible polynomial $P$ in $K[X]$ instead of $M$,
+in which case, $M$ is defined as the ring of integers of $K[X]/(P)$, viewed
+as a $\Z_K$-module.
+
+The library syntax is \fun{GEN}{rnfbasis}{GEN bnf, GEN M}.
+
+\subsec{rnfbasistoalg$(\var{rnf},x)$}\kbdsidx{rnfbasistoalg}\label{se:rnfbasistoalg}
+Computes the representation of $x$
+as a polmod with polmods coefficients. Here, $\var{rnf}$ is a relative number
+field extension $L/K$ as output by \kbd{rnfinit}, and $x$ an element of
+$L$ expressed on the relative integral basis.
+
+The library syntax is \fun{GEN}{rnfbasistoalg}{GEN rnf, GEN x}.
+
+\subsec{rnfcharpoly$(\var{nf},T,a,\{\var{var}='x\})$}\kbdsidx{rnfcharpoly}\label{se:rnfcharpoly}
+Characteristic polynomial of
+$a$ over $\var{nf}$, where $a$ belongs to the algebra defined by $T$ over
+$\var{nf}$, i.e.~$\var{nf}[X]/(T)$. Returns a polynomial in variable $v$
+($x$ by default).
+\bprog
+? nf = nfinit(y^2+1);
+? rnfcharpoly(nf, x^2+y*x+1, x+y)
+%2 = x^2 + Mod(-y, y^2 + 1)*x + 1
+ at eprog
+
+The library syntax is \fun{GEN}{rnfcharpoly}{GEN nf, GEN T, GEN a, long var = -1}, where \kbd{var} is a variable number.
+
+\subsec{rnfconductor$(\var{bnf},\var{pol})$}\kbdsidx{rnfconductor}\label{se:rnfconductor}
+Given $\var{bnf}$
+as output by \kbd{bnfinit}, and \var{pol} a relative polynomial defining an
+\idx{Abelian extension}, computes the class field theory conductor of this
+Abelian extension. The result is a 3-component vector
+$[\var{conductor},\var{rayclgp},\var{subgroup}]$, where \var{conductor} is
+the conductor of the extension given as a 2-component row vector
+$[f_0,f_\infty]$, \var{rayclgp} is the full ray class group corresponding to
+the conductor given as a 3-component vector [h,cyc,gen] as usual for a group,
+and \var{subgroup} is a matrix in HNF defining the subgroup of the ray class
+group on the given generators gen.
+
+The library syntax is \fun{GEN}{rnfconductor}{GEN bnf, GEN pol}.
+
+\subsec{rnfdedekind$(\var{nf},\var{pol},\{\var{pr}\},\{\fl=0\})$}\kbdsidx{rnfdedekind}\label{se:rnfdedekind}
+Given a number field $K$ coded by $\var{nf}$ and a monic
+polynomial $P\in \Z_K[X]$, irreducible over $K$ and thus defining a relative
+extension $L$ of $K$, applies \idx{Dedekind}'s criterion to the order
+$\Z_K[X]/(P)$, at the prime ideal \var{pr}. It is possible to set \var{pr}
+to a vector of prime ideals (test maximality at all primes in the vector),
+or to omit altogether, in which case maximality at \emph{all} primes is tested;
+in this situation \fl\ is automatically set to $1$.
+
+The default historic behavior (\fl\ is 0 or omitted and \var{pr} is a
+single prime ideal) is not so useful since
+\kbd{rnfpseudobasis} gives more information and is generally not that
+much slower. It returns a 3-component vector $[\var{max}, \var{basis}, v]$:
+
+\item \var{basis} is a pseudo-basis of an enlarged order $O$ produced by
+Dedekind's criterion, containing the original order $\Z_K[X]/(P)$
+with index a power of \var{pr}. Possibly equal to the original order.
+
+\item \var{max} is a flag equal to 1 if the enlarged order $O$
+could be proven to be \var{pr}-maximal and to 0 otherwise; it may still be
+maximal in the latter case if \var{pr} is ramified in $L$,
+
+\item $v$ is the valuation at \var{pr} of the order discriminant.
+
+If \fl\ is non-zero, on the other hand, we just return $1$ if the order
+$\Z_K[X]/(P)$ is \var{pr}-maximal (resp.~maximal at all relevant primes, as
+described above), and $0$ if not. This is much faster than the default,
+since the enlarged order is not computed.
+\bprog
+? nf = nfinit(y^2-3); P = x^3 - 2*y;
+? pr3 = idealprimedec(nf,3)[1];
+? rnfdedekind(nf, P, pr3)
+%2 = [1, [[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, 1]], 8]
+? rnfdedekind(nf, P, pr3, 1)
+%3 = 1
+ at eprog\noindent In this example, \kbd{pr3} is the ramified ideal above $3$,
+and the order generated by the cube roots of $y$ is already
+\kbd{pr3}-maximal. The order-discriminant has valuation $8$. On the other
+hand, the order is not maximal at the prime above 2:
+\bprog
+? pr2 = idealprimedec(nf,2)[1];
+? rnfdedekind(nf, P, pr2, 1)
+%5 = 0
+? rnfdedekind(nf, P, pr2)
+%6 = [0, [[2, 0, 0; 0, 1, 0; 0, 0, 1], [[1, 0; 0, 1], [1, 0; 0, 1],
+     [1, 1/2; 0, 1/2]]], 2]
+ at eprog
+The enlarged order is not proven to be \kbd{pr2}-maximal yet. In fact, it
+is; it is in fact the maximal order:
+\bprog
+? B = rnfpseudobasis(nf, P)
+%7 = [[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, [1, 1/2; 0, 1/2]],
+     [162, 0; 0, 162], -1]
+? idealval(nf,B[3], pr2)
+%4 = 2
+ at eprog\noindent
+It is possible to use this routine with non-monic
+$P = \sum_{i\leq n} a_i X^i \in \Z_K[X]$ if $\fl = 1$;
+in this case, we test maximality of Dedekind's order generated by
+$$1, a_n \alpha, a_n\alpha^2 + a_{n-1}\alpha, \dots,
+a_n\alpha^{n-1} + a_{n-1}\alpha^{n-2} + \cdots + a_1\alpha.$$
+The routine will fail if $P$ is $0$ on the projective line over the residue
+field $\Z_K/\kbd{pr}$ (FIXME).
+
+The library syntax is \fun{GEN}{rnfdedekind}{GEN nf, GEN pol, GEN pr = NULL, long flag}.
+
+\subsec{rnfdet$(\var{nf},M)$}\kbdsidx{rnfdet}\label{se:rnfdet}
+Given a pseudo-matrix $M$ over the maximal
+order of $\var{nf}$, computes its determinant.
+
+The library syntax is \fun{GEN}{rnfdet}{GEN nf, GEN M}.
+
+\subsec{rnfdisc$(\var{nf},\var{pol})$}\kbdsidx{rnfdisc}\label{se:rnfdisc}
+Given a number field $\var{nf}$ as
+output by \kbd{nfinit} and a polynomial \var{pol} with coefficients in
+$\var{nf}$ defining a relative extension $L$ of $\var{nf}$, computes the
+relative discriminant of $L$. This is a two-element row vector $[D,d]$, where
+$D$ is the relative ideal discriminant and $d$ is the relative discriminant
+considered as an element of $\var{nf}^*/{\var{nf}^*}^2$. The main variable of
+$\var{nf}$ \emph{must} be of lower priority than that of \var{pol}, see
+\secref{se:priority}.
+
+The library syntax is \fun{GEN}{rnfdiscf}{GEN nf, GEN pol}.
+
+\subsec{rnfeltabstorel$(\var{rnf},x)$}\kbdsidx{rnfeltabstorel}\label{se:rnfeltabstorel}
+$\var{rnf}$ being a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an
+element of $L$ expressed as a polynomial modulo the absolute equation
+\kbd{\var{rnf}.pol}, computes $x$ as an element of the relative extension
+$L/K$ as a polmod with polmod coefficients.
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+? L.pol
+%2 = x^4 + 1
+? rnfeltabstorel(L, Mod(x, L.pol))
+%3 = Mod(x, x^2 + Mod(-y, y^2 + 1))
+? rnfeltabstorel(L, Mod(2, L.pol))
+%4 = 2
+? rnfeltabstorel(L, Mod(x, x^2-y))
+ ***   at top-level: rnfeltabstorel(L,Mod
+ ***                 ^--------------------
+ *** rnfeltabstorel: inconsistent moduli in rnfeltabstorel: x^2-y != x^4+1
+ at eprog
+
+The library syntax is \fun{GEN}{rnfeltabstorel}{GEN rnf, GEN x}.
+
+\subsec{rnfeltdown$(\var{rnf},x)$}\kbdsidx{rnfeltdown}\label{se:rnfeltdown}
+$\var{rnf}$ being a relative number
+field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an element of
+$L$ expressed as a polynomial or polmod with polmod coefficients, computes
+$x$ as an element of $K$ as a polmod, assuming $x$ is in $K$ (otherwise a
+domain error occurs).
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+? L.pol
+%2 = x^4 + 1
+? rnfeltdown(L, Mod(x^2, L.pol))
+%3 = Mod(y, y^2 + 1)
+? rnfeltdown(L, Mod(y, x^2-y))
+%4 = Mod(y, y^2 + 1)
+? rnfeltdown(L, Mod(y,K.pol))
+%5 = Mod(y, y^2 + 1)
+? rnfeltdown(L, Mod(x, L.pol))
+ ***   at top-level: rnfeltdown(L,Mod(x,x
+ ***                 ^--------------------
+ *** rnfeltdown: domain error in rnfeltdown: element not in the base field
+ at eprog
+
+The library syntax is \fun{GEN}{rnfeltdown}{GEN rnf, GEN x}.
+
+\subsec{rnfeltnorm$(\var{rnf},x)$}\kbdsidx{rnfeltnorm}\label{se:rnfeltnorm}
+$\var{rnf}$ being a relative number field extension $L/K$ as output by
+\kbd{rnfinit} and $x$ being an element of $L$, returns the relative norm
+$N_{L/K}(x)$ as an element of $K$.
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+? rnfeltnorm(L, Mod(x, L.pol))
+%2 = Mod(x, x^2 + Mod(-y, y^2 + 1))
+? rnfeltnorm(L, 2)
+%3 = 4
+? rnfeltnorm(L, Mod(x, x^2-y))
+ at eprog
+
+The library syntax is \fun{GEN}{rnfeltnorm}{GEN rnf, GEN x}.
+
+\subsec{rnfeltreltoabs$(\var{rnf},x)$}\kbdsidx{rnfeltreltoabs}\label{se:rnfeltreltoabs}
+$\var{rnf}$ being a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an
+element of $L$ expressed as a polynomial or polmod with polmod
+coefficients, computes $x$ as an element of the absolute extension $L/\Q$ as
+a polynomial modulo the absolute equation \kbd{\var{rnf}.pol}.
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+? L.pol
+%2 = x^4 + 1
+? rnfeltreltoabs(L, Mod(x, L.pol))
+%3 = Mod(x, x^4 + 1)
+? rnfeltreltoabs(L, Mod(y, x^2-y))
+%4 = Mod(x^2, x^4 + 1)
+? rnfeltreltoabs(L, Mod(y,K.pol))
+%5 = Mod(x^2, x^4 + 1)
+ at eprog
+
+The library syntax is \fun{GEN}{rnfeltreltoabs}{GEN rnf, GEN x}.
+
+\subsec{rnfelttrace$(\var{rnf},x)$}\kbdsidx{rnfelttrace}\label{se:rnfelttrace}
+$\var{rnf}$ being a relative number field extension $L/K$ as output by
+\kbd{rnfinit} and $x$ being an element of $L$, returns the relative trace
+$N_{L/K}(x)$ as an element of $K$.
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+? rnfelttrace(L, Mod(x, L.pol))
+%2 = 0
+? rnfelttrace(L, 2)
+%3 = 4
+? rnfelttrace(L, Mod(x, x^2-y))
+ at eprog
+
+The library syntax is \fun{GEN}{rnfelttrace}{GEN rnf, GEN x}.
+
+\subsec{rnfeltup$(\var{rnf},x)$}\kbdsidx{rnfeltup}\label{se:rnfeltup}
+$\var{rnf}$ being a relative number field extension $L/K$ as output by
+\kbd{rnfinit} and $x$ being an element of $K$, computes $x$ as an element of
+the absolute extension $L/\Q$ as a polynomial modulo the absolute equation
+\kbd{\var{rnf}.pol}.
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+? L.pol
+%2 = x^4 + 1
+? rnfeltup(L, Mod(y, K.pol))
+%4 = Mod(x^2, x^4 + 1)
+? rnfeltup(L, y)
+%5 = Mod(x^2, x^4 + 1)
+? rnfeltup(L, [1,2]~) \\ in terms of K.zk
+%6 = Mod(2*x^2 + 1, x^4 + 1)
+ at eprog
+
+The library syntax is \fun{GEN}{rnfeltup}{GEN rnf, GEN x}.
+
+\subsec{rnfequation$(\var{nf},\var{pol},\{\fl=0\})$}\kbdsidx{rnfequation}\label{se:rnfequation}
+Given a number field
+$\var{nf}$ as output by \kbd{nfinit} (or simply a polynomial) and a
+polynomial \var{pol} with coefficients in $\var{nf}$ defining a relative
+extension $L$ of $\var{nf}$, computes an absolute equation of $L$ over
+$\Q$.
+
+The main variable of $\var{nf}$ \emph{must} be of lower priority than that
+of \var{pol} (see \secref{se:priority}). Note that for efficiency, this does
+not check whether the relative equation is irreducible over $\var{nf}$, but
+only if it is squarefree. If it is reducible but squarefree, the result will
+be the absolute equation of the \'etale algebra defined by \var{pol}. If
+\var{pol} is not squarefree, raise an \kbd{e\_DOMAIN} exception.
+\bprog
+? rnfequation(y^2+1, x^2 - y)
+%1 = x^4 + 1
+? T = y^3-2; rnfequation(nfinit(T), (x^3-2)/(x-Mod(y,T)))
+%2 = x^6 + 108  \\ Galois closure of Q(2^(1/3))
+ at eprog
+
+If $\fl$ is non-zero, outputs a 3-component row vector $[z,a,k]$, where
+
+\item $z$ is the absolute equation of $L$ over $\Q$, as in the default
+behavior,
+
+\item $a$ expresses as a \typ{POLMOD} modulo $z$ a root $\alpha$ of the
+polynomial defining the base field $\var{nf}$,
+
+\item $k$ is a small integer such that $\theta = \beta+k\alpha$
+is a root of $z$, where $\beta$ is a root of $\var{pol}$.
+\bprog
+? T = y^3-2; pol = x^2 +x*y + y^2;
+? [z,a,k] = rnfequation(T, pol, 1);
+? z
+%4 = x^6 + 108
+? subst(T, y, a)
+%5 = 0
+? alpha= Mod(y, T);
+? beta = Mod(x*Mod(1,T), pol);
+? subst(z, x, beta + k*alpha)
+%8 = 0
+ at eprog
+
+The library syntax is \fun{GEN}{rnfequation0}{GEN nf, GEN pol, long flag}.
+Also available are
+\fun{GEN}{rnfequation}{GEN nf, GEN pol} ($\fl = 0$) and
+\fun{GEN}{rnfequation2}{GEN nf, GEN pol} ($\fl = 1$).
+
+\subsec{rnfhnfbasis$(\var{bnf},x)$}\kbdsidx{rnfhnfbasis}\label{se:rnfhnfbasis}
+Given $\var{bnf}$ as output by
+\kbd{bnfinit}, and either a polynomial $x$ with coefficients in $\var{bnf}$
+defining a relative extension $L$ of $\var{bnf}$, or a pseudo-basis $x$ of
+such an extension, gives either a true $\var{bnf}$-basis of $L$ in upper
+triangular Hermite normal form, if it exists, and returns $0$ otherwise.
+
+The library syntax is \fun{GEN}{rnfhnfbasis}{GEN bnf, GEN x}.
+
+\subsec{rnfidealabstorel$(\var{rnf},x)$}\kbdsidx{rnfidealabstorel}\label{se:rnfidealabstorel}
+Let $\var{rnf}$ be a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and $x$ be an ideal of
+the absolute extension $L/\Q$ given by a $\Z$-basis of elements of $L$.
+Returns the relative pseudo-matrix in HNF giving the ideal $x$ considered as
+an ideal of the relative extension $L/K$, i.e.~as a $\Z_K$-module.
+
+The reason why the input does not use the customary HNF in terms of a fixed
+$\Z$-basis for $\Z_L$ is precisely that no such basis has been explicitly
+specified. On the other hand, if you already computed an (absolute) \var{nf}
+structure \kbd{Labs} associated to $L$, and $m$ is in HNF, defining
+an (absolute) ideal with respect to the $\Z$-basis \kbd{Labs.zk}, then
+\kbd{Labs.zk * m} is a suitable $\Z$-basis for the ideal, and
+\bprog
+  rnfidealabstorel(rnf, Labs.zk * m)
+ at eprog\noindent converts $m$ to a relative ideal.
+\bprog
+? K = nfinit(y^2+1); L = rnfinit(K, x^2-y); Labs = nfinit(L.pol);
+? m = idealhnf(Labs, 17, x^3+2);
+? B = rnfidealabstorel(L, Labs.zk * m)
+%3 = [[1, 8; 0, 1], [[17, 4; 0, 1], 1]]  \\ pseudo-basis for m as Z_K-module
+? A = rnfidealreltoabs(L, B)
+%4 = [17, x^2 + 4, x + 8, x^3 + 8*x^2]   \\ Z-basis for m in Q[x]/(L.pol)
+? mathnf(matalgtobasis(Labs, A))
+%5 =
+[17 8 4 2]
+
+[ 0 1 0 0]
+
+[ 0 0 1 0]
+
+[ 0 0 0 1]
+? % == m
+%6 = 1
+ at eprog
+
+The library syntax is \fun{GEN}{rnfidealabstorel}{GEN rnf, GEN x}.
+
+\subsec{rnfidealdown$(\var{rnf},x)$}\kbdsidx{rnfidealdown}\label{se:rnfidealdown}
+Let $\var{rnf}$ be a relative number
+field extension $L/K$ as output by \kbd{rnfinit}, and $x$ an ideal of
+$L$, given either in relative form or by a $\Z$-basis of elements of $L$
+(see \secref{se:rnfidealabstorel}). This function returns the ideal of $K$
+below $x$, i.e.~the intersection of $x$ with $K$.
+
+The library syntax is \fun{GEN}{rnfidealdown}{GEN rnf, GEN x}.
+
+\subsec{rnfidealhnf$(\var{rnf},x)$}\kbdsidx{rnfidealhnf}\label{se:rnfidealhnf}
+$\var{rnf}$ being a relative number
+field extension $L/K$ as output by \kbd{rnfinit} and $x$ being a relative
+ideal (which can be, as in the absolute case, of many different types,
+including of course elements), computes the HNF pseudo-matrix associated to
+$x$, viewed as a $\Z_K$-module.
+
+The library syntax is \fun{GEN}{rnfidealhnf}{GEN rnf, GEN x}.
+
+\subsec{rnfidealmul$(\var{rnf},x,y)$}\kbdsidx{rnfidealmul}\label{se:rnfidealmul}
+$\var{rnf}$ being a relative number
+field extension $L/K$ as output by \kbd{rnfinit} and $x$ and $y$ being ideals
+of the relative extension $L/K$ given by pseudo-matrices, outputs the ideal
+product, again as a relative ideal.
+
+The library syntax is \fun{GEN}{rnfidealmul}{GEN rnf, GEN x, GEN y}.
+
+\subsec{rnfidealnormabs$(\var{rnf},x)$}\kbdsidx{rnfidealnormabs}\label{se:rnfidealnormabs}
+Let $\var{rnf}$ be a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be a
+relative ideal (which can be, as in the absolute case, of many different
+types, including of course elements). This function computes the norm of the
+$x$ considered as an ideal of the absolute extension $L/\Q$. This is
+identical to
+\bprog
+   idealnorm(rnf, rnfidealnormrel(rnf,x))
+ at eprog\noindent but faster.
+
+The library syntax is \fun{GEN}{rnfidealnormabs}{GEN rnf, GEN x}.
+
+\subsec{rnfidealnormrel$(\var{rnf},x)$}\kbdsidx{rnfidealnormrel}\label{se:rnfidealnormrel}
+Let $\var{rnf}$ be a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be a
+relative ideal (which can be, as in the absolute case, of many different
+types, including of course elements). This function computes the relative
+norm of $x$ as an ideal of $K$ in HNF.
+
+The library syntax is \fun{GEN}{rnfidealnormrel}{GEN rnf, GEN x}.
+
+\subsec{rnfidealreltoabs$(\var{rnf},x)$}\kbdsidx{rnfidealreltoabs}\label{se:rnfidealreltoabs}
+Let $\var{rnf}$ be a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be a
+relative ideal, given as a $\Z_K$-module by a pseudo matrix $[A,I]$.
+This function returns the ideal $x$ as an absolute ideal of $L/\Q$ in
+the form of a $\Z$-basis, given by a vector of polynomials (modulo
+\kbd{rnf.pol}).
+
+The reason why we do not return the customary HNF in terms of a fixed
+$\Z$-basis for $\Z_L$ is precisely that no such basis has been explicitly
+specified. On the other hand, if you already computed an (absolute) \var{nf}
+structure \kbd{Labs} associated to $L$, then
+\bprog
+  xabs = rnfidealreltoabs(L, x);
+  xLabs = mathnf(matalgtobasis(Labs, xabs));
+ at eprog\noindent computes a traditional HNF \kbd{xLabs} for $x$ in terms of
+the fixed $\Z$-basis \kbd{Labs.zk}.
+
+The library syntax is \fun{GEN}{rnfidealreltoabs}{GEN rnf, GEN x}.
+
+\subsec{rnfidealtwoelt$(\var{rnf},x)$}\kbdsidx{rnfidealtwoelt}\label{se:rnfidealtwoelt}
+$\var{rnf}$ being a relative
+number field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an
+ideal of the relative extension $L/K$ given by a pseudo-matrix, gives a
+vector of two generators of $x$ over $\Z_L$ expressed as polmods with polmod
+coefficients.
+
+The library syntax is \fun{GEN}{rnfidealtwoelement}{GEN rnf, GEN x}.
+
+\subsec{rnfidealup$(\var{rnf},x)$}\kbdsidx{rnfidealup}\label{se:rnfidealup}
+Let $\var{rnf}$ be a relative number
+field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be an ideal of
+$K$. This function returns the ideal $x\Z_L$ as an absolute ideal of $L/\Q$,
+in the form of a $\Z$-basis, given by a vector of polynomials (modulo
+\kbd{rnf.pol}).
+
+The reason why we do not return the customary HNF in terms of a fixed
+$\Z$-basis for $\Z_L$ is precisely that no such basis has been explicitly
+specified. On the other hand, if you already computed an (absolute) \var{nf}
+structure \kbd{Labs} associated to $L$, then
+\bprog
+  xabs = rnfidealup(L, x);
+  xLabs = mathnf(matalgtobasis(Labs, xabs));
+ at eprog\noindent computes a traditional HNF \kbd{xLabs} for $x$ in terms of
+the fixed $\Z$-basis \kbd{Labs.zk}.
+
+The library syntax is \fun{GEN}{rnfidealup}{GEN rnf, GEN x}.
+
+\subsec{rnfinit$(\var{nf},\var{pol})$}\kbdsidx{rnfinit}\label{se:rnfinit}
+$\var{nf}$ being a number field in \kbd{nfinit}
+format considered as base field, and \var{pol} a polynomial defining a relative
+extension over $\var{nf}$, this computes data to work in the
+relative extension. The main variable of \var{pol} must be of higher priority
+(see \secref{se:priority}) than that of $\var{nf}$, and the coefficients of
+\var{pol} must be in $\var{nf}$.
+
+The result is a row vector, whose components are technical. In the following
+description, we let $K$ be the base field defined by $\var{nf}$ and $L/K$
+the large field associated to the \var{rnf}. Furthermore, we let
+$m = [K:\Q]$ the degree of the base field, $n = [L:K]$ the relative degree,
+$r_1$ and $r_2$ the number of real and complex places of $K$. Access to this
+information via \emph{member functions} is preferred since the specific
+data organization specified below will change in the future.
+
+$\var{rnf}[1]$(\kbd{rnf.pol}) contains the relative polynomial \var{pol}.
+
+$\var{rnf}[2]$ contains the integer basis $[A,d]$ of $K$, as
+(integral) elements of $L/\Q$. More precisely, $A$ is a vector of
+polynomial with integer coefficients, $d$ is a denominator, and the integer
+basis is given by $A/d$.
+
+$\var{rnf}[3]$ (\kbd{rnf.disc}) is a two-component row vector
+$[\goth{d}(L/K),s]$ where $\goth{d}(L/K)$ is the relative ideal discriminant
+of $L/K$ and $s$ is the discriminant of $L/K$ viewed as an element of
+$K^*/(K^*)^2$, in other words it is the output of \kbd{rnfdisc}.
+
+$\var{rnf}[4]$(\kbd{rnf.index}) is the ideal index $\goth{f}$, i.e.~such
+that $d(pol)\Z_K=\goth{f}^2\goth{d}(L/K)$.
+
+$\var{rnf}[5]$ is currently unused.
+
+$\var{rnf}[6]$ is currently unused.
+
+$\var{rnf}[7]$ (\kbd{rnf.zk}) is the pseudo-basis $(A,I)$ for the maximal
+order $\Z_L$ as a $\Z_K$-module: $A$ is the relative integral pseudo basis
+expressed as polynomials (in the variable of $pol$) with polmod coefficients
+in $\var{nf}$, and the second component $I$ is the ideal list of the
+pseudobasis in HNF.
+
+$\var{rnf}[8]$ is the inverse matrix of the integral basis matrix, with
+coefficients polmods in $\var{nf}$.
+
+$\var{rnf}[9]$ is currently unused.
+
+$\var{rnf}[10]$ (\kbd{rnf.nf}) is $\var{nf}$.
+
+$\var{rnf}[11]$ is the output of \kbd{rnfequation(K, pol, 1)}. Namely, a
+vector $[P, a, k]$ describing the \emph{absolute} extension
+$L/\Q$: $P$ is an absolute equation, more conveniently obtained
+as \kbd{rnf.polabs}; $a$ expresses the generator $\alpha = y \mod \kbd{K.pol}$
+of the number field $K$ as an element of $L$, i.e.~a polynomial modulo the
+absolute equation $P$;
+
+$k$ is a small integer such that, if $\beta$ is an abstract root of \var{pol}
+and $\alpha$ the generator of $K$ given above, then $P(\beta + k\alpha) = 0$.
+
+\misctitle{Caveat.} Be careful if $k\neq0$ when dealing simultaneously with
+absolute and relative quantities since $L = \Q(\beta + k\alpha) =
+K(\alpha)$, and the generator chosen for the absolute extension is not the
+same as for the relative one. If this happens, one can of course go on
+working, but we advise to change the relative polynomial so that its root
+becomes $\beta + k \alpha$. Typical GP instructions would be
+\bprog
+  [P,a,k] = rnfequation(K, pol, 1);
+  if (k, pol = subst(pol, x, x - k*Mod(y, K.pol)));
+  L = rnfinit(K, pol);
+ at eprog
+
+$\var{rnf}[12]$ is by default unused and set equal to 0. This field is used
+to store further information about the field as it becomes available (which
+is rarely needed, hence would be too expensive to compute during the initial
+\kbd{rnfinit} call).
+
+The library syntax is \fun{GEN}{rnfinit}{GEN nf, GEN pol}.
+
+\subsec{rnfisabelian$(\var{nf},T)$}\kbdsidx{rnfisabelian}\label{se:rnfisabelian}
+$T$ being a relative polynomial with coefficients
+in \var{nf}, return 1 if it defines an abelian extension, and 0 otherwise.
+\bprog
+? K = nfinit(y^2 + 23);
+? rnfisabelian(K, x^3 - 3*x - y)
+%2 = 1
+ at eprog
+
+The library syntax is \fun{long}{rnfisabelian}{GEN nf, GEN T}.
+
+\subsec{rnfisfree$(\var{bnf},x)$}\kbdsidx{rnfisfree}\label{se:rnfisfree}
+Given $\var{bnf}$ as output by
+\kbd{bnfinit}, and either a polynomial $x$ with coefficients in $\var{bnf}$
+defining a relative extension $L$ of $\var{bnf}$, or a pseudo-basis $x$ of
+such an extension, returns true (1) if $L/\var{bnf}$ is free, false (0) if
+not.
+
+The library syntax is \fun{long}{rnfisfree}{GEN bnf, GEN x}.
+
+\subsec{rnfisnorm$(T,a,\{\fl=0\})$}\kbdsidx{rnfisnorm}\label{se:rnfisnorm}
+Similar to
+\kbd{bnfisnorm} but in the relative case. $T$ is as output by
+\tet{rnfisnorminit} applied to the extension $L/K$. This tries to decide
+whether the element $a$ in $K$ is the norm of some $x$ in the extension
+$L/K$.
+
+The output is a vector $[x,q]$, where $a = \Norm(x)*q$. The
+algorithm looks for a solution $x$ which is an $S$-integer, with $S$ a list
+of places of $K$ containing at least the ramified primes, the generators of
+the class group of $L$, as well as those primes dividing $a$. If $L/K$ is
+Galois, then this is enough; otherwise, $\fl$ is used to add more primes to
+$S$: all the places above the primes $p \leq \fl$ (resp.~$p|\fl$) if $\fl>0$
+(resp.~$\fl<0$).
+
+The answer is guaranteed (i.e.~$a$ is a norm iff $q = 1$) if the field is
+Galois, or, under \idx{GRH}, if $S$ contains all primes less than
+$12\log^2\left|\disc(M)\right|$, where $M$ is the normal
+closure of $L/K$.
+
+If \tet{rnfisnorminit} has determined (or was told) that $L/K$ is
+\idx{Galois}, and $\fl \neq 0$, a Warning is issued (so that you can set
+$\fl = 1$ to check whether $L/K$ is known to be Galois, according to $T$).
+Example:
+
+\bprog
+bnf = bnfinit(y^3 + y^2 - 2*y - 1);
+p = x^2 + Mod(y^2 + 2*y + 1, bnf.pol);
+T = rnfisnorminit(bnf, p);
+rnfisnorm(T, 17)
+ at eprog\noindent
+checks whether $17$ is a norm in the Galois extension $\Q(\beta) /
+\Q(\alpha)$, where $\alpha^3 + \alpha^2 - 2\alpha - 1 = 0$ and $\beta^2 +
+\alpha^2 + 2\alpha + 1 = 0$ (it is).
+
+The library syntax is \fun{GEN}{rnfisnorm}{GEN T, GEN a, long flag}.
+
+\subsec{rnfisnorminit$(\var{pol},\var{polrel},\{\fl=2\})$}\kbdsidx{rnfisnorminit}\label{se:rnfisnorminit}
+Let $K$ be defined by a root of \var{pol}, and $L/K$ the extension defined
+by the polynomial \var{polrel}. As usual, \var{pol} can in fact be an \var{nf},
+or \var{bnf}, etc; if \var{pol} has degree $1$ (the base field is $\Q$),
+polrel is also allowed to be an \var{nf}, etc. Computes technical data needed
+by \tet{rnfisnorm} to solve norm equations $Nx = a$, for $x$ in $L$, and $a$
+in $K$.
+
+If $\fl = 0$, do not care whether $L/K$ is Galois or not.
+
+If $\fl = 1$, $L/K$ is assumed to be Galois (unchecked), which speeds up
+\tet{rnfisnorm}.
+
+If $\fl = 2$, let the routine determine whether $L/K$ is Galois.
+
+The library syntax is \fun{GEN}{rnfisnorminit}{GEN pol, GEN polrel, long flag}.
+
+\subsec{rnfkummer$(\var{bnr},\{\var{subgp}\},\{d=0\})$}\kbdsidx{rnfkummer}\label{se:rnfkummer}
+\var{bnr}
+being as output by \kbd{bnrinit}, finds a relative equation for the
+class field corresponding to the module in \var{bnr} and the given
+congruence subgroup (the full ray class field if \var{subgp} is omitted).
+If $d$ is positive, outputs the list of all relative equations of
+degree $d$ contained in the ray class field defined by \var{bnr}, with
+the \emph{same} conductor as $(\var{bnr}, \var{subgp})$.
+
+\misctitle{Warning} This routine only works for subgroups of prime index. It
+uses Kummer theory, adjoining necessary roots of unity (it needs to compute a
+tough \kbd{bnfinit} here), and finds a generator via Hecke's characterization
+of ramification in Kummer extensions of prime degree. If your extension does
+not have prime degree, for the time being, you have to split it by hand as a
+tower / compositum of such extensions.
+
+The library syntax is \fun{GEN}{rnfkummer}{GEN bnr, GEN subgp = NULL, long d, long prec}.
+
+\subsec{rnflllgram$(\var{nf},\var{pol},\var{order})$}\kbdsidx{rnflllgram}\label{se:rnflllgram}
+Given a polynomial
+\var{pol} with coefficients in \var{nf} defining a relative extension $L$ and
+a suborder \var{order} of $L$ (of maximal rank), as output by
+\kbd{rnfpseudobasis}$(\var{nf},\var{pol})$ or similar, gives
+$[[\var{neworder}],U]$, where \var{neworder} is a reduced order and $U$ is
+the unimodular transformation matrix.
+
+The library syntax is \fun{GEN}{rnflllgram}{GEN nf, GEN pol, GEN order, long prec}.
+
+\subsec{rnfnormgroup$(\var{bnr},\var{pol})$}\kbdsidx{rnfnormgroup}\label{se:rnfnormgroup}
+\var{bnr} being a big ray
+class field as output by \kbd{bnrinit} and \var{pol} a relative polynomial
+defining an \idx{Abelian extension}, computes the norm group (alias Artin
+or Takagi group) corresponding to the Abelian extension of
+$\var{bnf}=$\kbd{bnr.bnf}
+defined by \var{pol}, where the module corresponding to \var{bnr} is assumed
+to be a multiple of the conductor (i.e.~\var{pol} defines a subextension of
+bnr). The result is the HNF defining the norm group on the given generators
+of \kbd{bnr.gen}. Note that neither the fact that \var{pol} defines an
+Abelian extension nor the fact that the module is a multiple of the conductor
+is checked. The result is undefined if the assumption is not correct.
+
+The library syntax is \fun{GEN}{rnfnormgroup}{GEN bnr, GEN pol}.
+
+\subsec{rnfpolred$(\var{nf},\var{pol})$}\kbdsidx{rnfpolred}\label{se:rnfpolred}
+THIS FUNCTION IS OBSOLETE: use \tet{rnfpolredbest} instead.
+Relative version of \kbd{polred}. Given a monic polynomial \var{pol} with
+coefficients in $\var{nf}$, finds a list of relative polynomials defining some
+subfields, hopefully simpler and containing the original field. In the present
+version \vers, this is slower and less efficient than \kbd{rnfpolredbest}.
+
+\misctitle{Remark} this function is based on an incomplete reduction
+theory of lattices over number fields, implemented by \kbd{rnflllgram}, which
+deserves to be improved.
+
+The library syntax is \fun{GEN}{rnfpolred}{GEN nf, GEN pol, long prec}.
+
+\subsec{rnfpolredabs$(\var{nf},\var{pol},\{\fl=0\})$}\kbdsidx{rnfpolredabs}\label{se:rnfpolredabs}
+THIS FUNCTION IS OBSOLETE: use \tet{rnfpolredbest} instead.
+Relative version of \kbd{polredabs}. Given a monic polynomial \var{pol}
+with coefficients in $\var{nf}$, finds a simpler relative polynomial defining
+the same field. The binary digits of $\fl$ mean
+
+The binary digits of $\fl$ correspond to $1$: add information to convert
+elements to the new representation, $2$: absolute polynomial, instead of
+relative, $16$: possibly use a suborder of the maximal order. More precisely:
+
+0: default, return $P$
+
+1: returns $[P,a]$ where $P$ is the default output and $a$,
+a \typ{POLMOD} modulo $P$, is a root of \var{pol}.
+
+2: returns \var{Pabs}, an absolute, instead of a relative, polynomial.
+Same as but faster than
+\bprog
+  rnfequation(nf, rnfpolredabs(nf,pol))
+ at eprog
+
+3: returns $[\var{Pabs},a,b]$, where \var{Pabs} is an absolute polynomial
+as above, $a$, $b$ are \typ{POLMOD} modulo \var{Pabs}, roots of \kbd{nf.pol}
+and \var{pol} respectively.
+
+16: possibly use a suborder of the maximal order. This is slower than the
+default when the relative discriminant is smooth, and much faster otherwise.
+See \secref{se:polredabs}.
+
+\misctitle{Warning} In the present implementation, \kbd{rnfpolredabs}
+produces smaller polynomials than \kbd{rnfpolred} and is usually
+faster, but its complexity is still exponential in the absolute degree.
+The function \tet{rnfpolredbest} runs in polynomial time, and  tends  to
+return polynomials with smaller discriminants.
+
+The library syntax is \fun{GEN}{rnfpolredabs}{GEN nf, GEN pol, long flag}.
+
+\subsec{rnfpolredbest$(\var{nf},\var{pol},\{\fl=0\})$}\kbdsidx{rnfpolredbest}\label{se:rnfpolredbest}
+Relative version of \kbd{polredbest}. Given a monic polynomial \var{pol}
+with coefficients in $\var{nf}$, finds a simpler relative polynomial $P$
+defining the same field. As opposed to \tet{rnfpolredabs} this function does
+not return a \emph{smallest} (canonical) polynomial with respect to some
+measure, but it does run in polynomial time.
+
+The binary digits of $\fl$ correspond to $1$: add information to convert
+elements to the new representation, $2$: absolute polynomial, instead of
+relative. More precisely:
+
+0: default, return $P$
+
+1: returns $[P,a]$ where $P$ is the default output and $a$,
+a \typ{POLMOD} modulo $P$, is a root of \var{pol}.
+
+2: returns \var{Pabs}, an absolute, instead of a relative, polynomial.
+Same as but faster than
+\bprog
+  rnfequation(nf, rnfpolredbest(nf,pol))
+ at eprog
+
+3: returns $[\var{Pabs},a,b]$, where \var{Pabs} is an absolute polynomial
+as above, $a$, $b$ are \typ{POLMOD} modulo \var{Pabs}, roots of \kbd{nf.pol}
+and \var{pol} respectively.
+
+\bprog
+? K = nfinit(y^3-2); pol = x^2 +x*y + y^2;
+? [P, a] = rnfpolredbest(K,pol,1);
+? P
+%3 = x^2 - x + Mod(y - 1, y^3 - 2)
+? a
+%4 = Mod(Mod(2*y^2+3*y+4,y^3-2)*x + Mod(-y^2-2*y-2,y^3-2),
+         x^2 - x + Mod(y-1,y^3-2))
+? subst(K.pol,y,a)
+%5 = 0
+? [Pabs, a, b] = rnfpolredbest(K,pol,3);
+? Pabs
+%7 = x^6 - 3*x^5 + 5*x^3 - 3*x + 1
+? a
+%8 = Mod(-x^2+x+1, x^6-3*x^5+5*x^3-3*x+1)
+? b
+%9 = Mod(2*x^5-5*x^4-3*x^3+10*x^2+5*x-5, x^6-3*x^5+5*x^3-3*x+1)
+? subst(K.pol,y,a)
+%10 = 0
+? substvec(pol,[x,y],[a,b])
+%11 = 0
+ at eprog
+
+The library syntax is \fun{GEN}{rnfpolredbest}{GEN nf, GEN pol, long flag}.
+
+\subsec{rnfpseudobasis$(\var{nf},\var{pol})$}\kbdsidx{rnfpseudobasis}\label{se:rnfpseudobasis}
+Given a number field
+$\var{nf}$ as output by \kbd{nfinit} and a polynomial \var{pol} with
+coefficients in $\var{nf}$ defining a relative extension $L$ of $\var{nf}$,
+computes a pseudo-basis $(A,I)$ for the maximal order $\Z_L$ viewed as a
+$\Z_K$-module, and the relative discriminant of $L$. This is output as a
+four-element row vector $[A,I,D,d]$, where $D$ is the relative ideal
+discriminant and $d$ is the relative discriminant considered as an element of
+$\var{nf}^*/{\var{nf}^*}^2$.
+
+The library syntax is \fun{GEN}{rnfpseudobasis}{GEN nf, GEN pol}.
+
+\subsec{rnfsteinitz$(\var{nf},x)$}\kbdsidx{rnfsteinitz}\label{se:rnfsteinitz}
+Given a number field $\var{nf}$ as
+output by \kbd{nfinit} and either a polynomial $x$ with coefficients in
+$\var{nf}$ defining a relative extension $L$ of $\var{nf}$, or a pseudo-basis
+$x$ of such an extension as output for example by \kbd{rnfpseudobasis},
+computes another pseudo-basis $(A,I)$ (not in HNF in general) such that all
+the ideals of $I$ except perhaps the last one are equal to the ring of
+integers of $\var{nf}$, and outputs the four-component row vector $[A,I,D,d]$
+as in \kbd{rnfpseudobasis}. The name of this function comes from the fact
+that the ideal class of the last ideal of $I$, which is well defined, is the
+\idx{Steinitz class} of the $\Z_K$-module $\Z_L$ (its image in $SK_0(\Z_K)$).
+
+The library syntax is \fun{GEN}{rnfsteinitz}{GEN nf, GEN x}.
+
+\subsec{subgrouplist$(\var{bnr},\{\var{bound}\},\{\fl=0\})$}\kbdsidx{subgrouplist}\label{se:subgrouplist}
+\var{bnr} being as output by \kbd{bnrinit} or a list of cyclic components
+of a finite Abelian group $G$, outputs the list of subgroups of $G$. Subgroups
+are given as HNF left divisors of the SNF matrix corresponding to $G$.
+
+If $\fl=0$ (default) and \var{bnr} is as output by \kbd{bnrinit}, gives
+only the subgroups whose modulus is the conductor. Otherwise, the modulus is
+not taken into account.
+
+If \var{bound} is present, and is a positive integer, restrict the output to
+subgroups of index less than \var{bound}. If \var{bound} is a vector
+containing a single positive integer $B$, then only subgroups of index
+exactly equal to $B$ are computed. For instance
+\bprog
+? subgrouplist([6,2])
+%1 = [[6, 0; 0, 2], [2, 0; 0, 2], [6, 3; 0, 1], [2, 1; 0, 1], [3, 0; 0, 2],
+[1, 0; 0, 2], [6, 0; 0, 1], [2, 0; 0, 1], [3, 0; 0, 1], [1, 0; 0, 1]]
+? subgrouplist([6,2],3)    \\@com index less than 3
+%2 = [[2, 1; 0, 1], [1, 0; 0, 2], [2, 0; 0, 1], [3, 0; 0, 1], [1, 0; 0, 1]]
+? subgrouplist([6,2],[3])  \\@com index 3
+%3 = [[3, 0; 0, 1]]
+? bnr = bnrinit(bnfinit(x), [120,[1]], 1);
+? L = subgrouplist(bnr, [8]);
+ at eprog\noindent
+In the last example, $L$ corresponds to the 24 subfields of
+$\Q(\zeta_{120})$, of degree $8$ and conductor $120\infty$ (by setting \fl,
+we see there are a total of $43$ subgroups of degree $8$).
+\bprog
+? vector(#L, i, galoissubcyclo(bnr, L[i]))
+ at eprog\noindent
+will produce their equations. (For a general base field, you would
+have to rely on \tet{bnrstark}, or \tet{rnfkummer}.)
+
+The library syntax is \fun{GEN}{subgrouplist0}{GEN bnr, GEN bound = NULL, long flag}.
+
+\subsec{zetak$(\var{nfz},x,\{\fl=0\})$}\kbdsidx{zetak}\label{se:zetak}
+\var{znf} being a number
+field initialized by \kbd{zetakinit} (\emph{not} by \kbd{nfinit}),
+computes the value of the \idx{Dedekind} zeta function of the number
+field at the complex number $x$. If $\fl=1$ computes Dedekind $\Lambda$
+function instead (i.e.~the product of the Dedekind zeta function by its gamma
+and exponential factors).
+
+\misctitle{CAVEAT} This implementation is not satisfactory and must be
+rewritten. In particular
+
+\item The accuracy of the result depends in an essential way on the
+accuracy of both the \kbd{zetakinit} program and the current accuracy.
+Be wary in particular that $x$ of large imaginary part or, on the
+contrary, very close to an ordinary integer will suffer from precision
+loss, yielding fewer significant digits than expected. Computing with 28
+digits of relative accuracy, we have
+\bprog
+? zeta(3)
+%1 = 1.202056903159594285399738161
+? zeta(3-1e-20)
+%2 = 1.202056903159594285401719424
+? zetak(zetakinit(x), 3-1e-20)
+%3 = 1.2020569031595952919  \\ 5 digits are wrong
+? zetak(zetakinit(x), 3-1e-28)
+%4 = -25.33411749           \\ junk
+ at eprog
+
+\item As the precision increases, results become unexpectedly
+completely wrong:
+\bprog
+? \p100
+? zetak(zetakinit(x^2-5), -1) - 1/30
+%1 = 7.26691813 E-108    \\ perfect
+? \p150
+? zetak(zetakinit(x^2-5), -1) - 1/30
+%2 = -2.486113578 E-156  \\ perfect
+? \p200
+? zetak(zetakinit(x^2-5), -1) - 1/30
+%3 = 4.47... E-75        \\ more than half of the digits are wrong
+? \p250
+? zetak(zetakinit(x^2-5), -1) - 1/30
+%4 = 1.6 E43             \\ junk
+ at eprog
+
+The library syntax is \fun{GEN}{gzetakall}{GEN nfz, GEN x, long flag, long prec}.
+See also \fun{GEN}{glambdak}{GEN znf, GEN x, long prec} or
+\fun{GEN}{gzetak}{GEN znf, GEN x, long prec}.
+
+\subsec{zetakinit$(\var{bnf})$}\kbdsidx{zetakinit}\label{se:zetakinit}
+Computes a number of initialization data
+concerning the number field associated to \kbd{bnf} so as to be able
+to compute the \idx{Dedekind} zeta and lambda functions, respectively
+$\kbd{zetak}(x)$ and $\kbd{zetak}(x,1)$, at the current real precision. If
+you do not need the \kbd{bnfinit} data somewhere else, you may call it
+with an irreducible polynomial instead of a \var{bnf}: it will call
+\kbd{bnfinit} itself.
+
+The result is a 9-component vector $v$ whose components are very technical
+and cannot really be used except through the \kbd{zetak} function.
+
+This function is very inefficient and should be rewritten. It needs to
+computes millions of coefficients of the corresponding Dirichlet series if
+the precision is big. Unless the discriminant is small it will not be able
+to handle more than 9 digits of relative precision. For instance,
+\kbd{zetakinit(x\pow 8 - 2)} needs 440MB of memory at default precision.
+
+This function will fail with the message
+\bprog
+ *** bnrL1: overflow in zeta_get_N0 [need too many primes].
+ at eprog\noindent if the approximate functional equation requires us to sum
+too many terms (if the discriminant of the number field is too large).
+
+The library syntax is \fun{GEN}{initzeta}{GEN bnf, long prec}.
+%SECTION: number_fields
+
+\section{Polynomials and power series}
+
+We group here all functions which are specific to polynomials or power
+series. Many other functions which can be applied on these objects are
+described in the other sections. Also, some of the functions described here
+can be applied to other types.
+
+
+\subsec{O$(p\hbox{\kbd{\pow}}e)$}\kbdsidx{O}\label{se:O}
+If $p$ is an integer
+greater than $2$, returns a $p$-adic $0$ of precision $e$. In all other
+cases, returns a power series zero with precision given by $e v$, where $v$
+is the $X$-adic valuation of $p$ with respect to its main variable.
+
+The library syntax is \fun{GEN}{ggrando}{}.
+\fun{GEN}{zeropadic}{GEN p, long e} for a $p$-adic and
+\fun{GEN}{zeroser}{long v, long e} for a power series zero in variable $v$.
+
+\subsec{bezoutres$(A,B,\{v\})$}\kbdsidx{bezoutres}\label{se:bezoutres}
+Deprecated alias for \kbd{polresultantext}
+
+The library syntax is \fun{GEN}{polresultantext0}{GEN A, GEN B, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{deriv$(x,\{v\})$}\kbdsidx{deriv}\label{se:deriv}
+Derivative of $x$ with respect to the main
+variable if $v$ is omitted, and with respect to $v$ otherwise. The derivative
+of a scalar type is zero, and the derivative of a vector or matrix is done
+componentwise. One can use $x'$ as a shortcut if the derivative is with
+respect to the main variable of $x$.
+
+By definition, the main variable of a \typ{POLMOD} is the main variable among
+the coefficients from its two polynomial components (representative and
+modulus); in other words, assuming a polmod represents an element of
+$R[X]/(T(X))$, the variable $X$ is a mute variable and the derivative is
+taken with respect to the main variable used in the base ring $R$.
+
+The library syntax is \fun{GEN}{deriv}{GEN x, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{diffop$(x,v,d,\{n=1\})$}\kbdsidx{diffop}\label{se:diffop}
+Let $v$ be a vector of variables, and $d$ a vector of the same length,
+return the image of $x$ by the $n$-power ($1$ if n is not given) of the differential
+operator $D$ that assumes the value \kbd{d[i]} on the variable \kbd{v[i]}.
+The value of $D$ on a scalar type is zero, and $D$ applies componentwise to a vector
+or matrix. When applied to a \typ{POLMOD}, if no value is provided for the variable
+of the modulus, such value is derived using the implicit function theorem.
+
+Some examples:
+This function can be used to differentiate formal expressions:
+If $E=\exp(X^2)$ then we have $E'=2*X*E$. We can derivate $X*exp(X^2)$ as follow:
+\bprog
+? diffop(E*X,[X,E],[1,2*X*E])
+%1 = (2*X^2 + 1)*E
+ at eprog
+Let \kbd{Sin} and \kbd{Cos} be two function such that $\kbd{Sin}^2+\kbd{Cos}^2=1$
+and $\kbd{Cos}'=-\kbd{Sin}$. We can differentiate $\kbd{Sin}/\kbd{Cos}$ as follow,
+PARI inferring the value of $\kbd{Sin}'$ from the equation:
+\bprog
+? diffop(Mod('Sin/'Cos,'Sin^2+'Cos^2-1),['Cos],[-'Sin])
+%1 = Mod(1/Cos^2, Sin^2 + (Cos^2 - 1))
+
+ at eprog
+Compute the Bell polynomials (both complete and partial) via the Faa di Bruno formula:
+\bprog
+Bell(k,n=-1)=
+{
+  my(var(i)=eval(Str("X",i)));
+  my(x,v,dv);
+  v=vector(k,i,if(i==1,'E,var(i-1)));
+  dv=vector(k,i,if(i==1,'X*var(1)*'E,var(i)));
+  x=diffop('E,v,dv,k)/'E;
+  if(n<0,subst(x,'X,1),polcoeff(x,n,'X))
+}
+ at eprog
+
+The library syntax is \fun{GEN}{diffop0}{GEN x, GEN v, GEN d, long n}.
+
+For $n=1$, the function \fun{GEN}{diffop}{GEN x, GEN v, GEN d} is also available.
+
+\subsec{eval$(x)$}\kbdsidx{eval}\label{se:eval}
+Replaces in $x$ the formal variables by the values that
+have been assigned to them after the creation of $x$. This is mainly useful
+in GP, and not in library mode. Do not confuse this with substitution (see
+\kbd{subst}).
+
+If $x$ is a character string, \kbd{eval($x$)} executes $x$ as a GP
+command, as if directly input from the keyboard, and returns its
+output.
+\bprog
+? x1 = "one"; x2 = "two";
+? n = 1; eval(Str("x", n))
+%2 = "one"
+? f = "exp"; v = 1;
+? eval(Str(f, "(", v, ")"))
+%4 = 2.7182818284590452353602874713526624978
+ at eprog\noindent Note that the first construct could be implemented in a
+simpler way by using a vector \kbd{x = ["one","two"]; x[n]}, and the second
+by using a closure \kbd{f = exp; f(v)}. The final example is more interesting:
+\bprog
+? genmat(u,v) = matrix(u,v,i,j, eval( Str("x",i,j) ));
+? genmat(2,3)   \\ generic 2 x 3 matrix
+%2 =
+[x11 x12 x13]
+
+[x21 x22 x23]
+ at eprog
+
+A syntax error in the evaluation expression raises an \kbd{e\_SYNTAX}
+exception, which can be trapped as usual:
+\bprog
+? 1a
+ ***   unused characters: 1a
+ ***                       ^-
+? E(expr) =
+  {
+    iferr(eval(expr),
+          e, print("syntax error"),
+          errname(e) == "e_SYNTAX");
+  }
+? E("1+1")
+%1 = 2
+? E("1a")
+syntax error
+ at eprog
+\synt{geval}{GEN x}.
+
+\subsec{factorpadic$(\var{pol},p,r)$}\kbdsidx{factorpadic}\label{se:factorpadic}
+$p$-adic factorization
+of the polynomial \var{pol} to precision $r$, the result being a
+two-column matrix as in \kbd{factor}. Note that this is not the same
+as a factorization over $\Z/p^r\Z$ (polynomials over that ring do not form a
+unique factorization domain, anyway), but approximations in $\Q/p^r\Z$ of
+the true factorization in $\Q_p[X]$.
+\bprog
+? factorpadic(x^2 + 9, 3,5)
+%1 =
+[(1 + O(3^5))*x^2 + O(3^5)*x + (3^2 + O(3^5)) 1]
+? factorpadic(x^2 + 1, 5,3)
+%2 =
+[  (1 + O(5^3))*x + (2 + 5 + 2*5^2 + O(5^3)) 1]
+
+[(1 + O(5^3))*x + (3 + 3*5 + 2*5^2 + O(5^3)) 1]
+ at eprog\noindent
+The factors are normalized so that their leading coefficient is a power of
+$p$. The method used is a modified version of the \idx{round 4} algorithm of
+\idx{Zassenhaus}.
+
+If \var{pol} has inexact \typ{PADIC} coefficients, this is not always
+well-defined; in this case, the polynomial is first made integral by dividing
+out the $p$-adic content,  then lifted to $\Z$ using \tet{truncate}
+coefficientwise.
+Hence we actually factor exactly a polynomial which is only $p$-adically
+close to the input. To avoid pitfalls, we advise to only factor polynomials
+with exact rational coefficients.
+
+\synt{factorpadic}{GEN f,GEN p, long r} . The function \kbd{factorpadic0} is
+deprecated, provided for backward compatibility.
+
+\subsec{intformal$(x,\{v\})$}\kbdsidx{intformal}\label{se:intformal}
+\idx{formal integration} of $x$ with respect to the variable $v$ (wrt.
+the main variable if $v$ is omitted). Since PARI cannot represent
+logarithmic or arctangent terms, any such term in the result will yield an
+error:
+\bprog
+ ? intformal(x^2)
+ %1 = 1/3*x^3
+ ? intformal(x^2, y)
+ %2 = y*x^2
+ ? intformal(1/x)
+   ***   at top-level: intformal(1/x)
+   ***                 ^--------------
+   *** intformal: domain error in intformal: residue(series, pole) != 0
+ at eprog
+The argument $x$ can be of any type. When $x$ is a rational function, we
+assume that the base ring is an integral domain of characteristic zero.
+
+  By  definition,   the main variable of a \typ{POLMOD} is the main variable
+among the  coefficients  from  its  two  polynomial  components
+(representative and modulus); in other words, assuming a polmod represents an
+element of $R[X]/(T(X))$, the variable $X$ is a mute variable and the
+integral is taken with respect to the main variable used in the base ring $R$.
+In particular, it is meaningless to integrate with respect to the main
+variable of \kbd{x.mod}:
+\bprog
+? intformal(Mod(1,x^2+1), 'x)
+*** intformal: incorrect priority in intformal: variable x = x
+ at eprog
+
+The library syntax is \fun{GEN}{integ}{GEN x, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{padicappr$(\var{pol},a)$}\kbdsidx{padicappr}\label{se:padicappr}
+Vector of $p$-adic roots of the
+polynomial $pol$ congruent to the $p$-adic number $a$ modulo $p$, and with
+the same $p$-adic precision as $a$. The number $a$ can be an ordinary
+$p$-adic number (type \typ{PADIC}, i.e.~an element of $\Z_p$) or can be an
+integral element of a finite extension of $\Q_p$, given as a \typ{POLMOD}
+at least one of whose coefficients is a \typ{PADIC}. In this case, the result
+is the vector of roots belonging to the same extension of $\Q_p$ as $a$.
+
+The library syntax is \fun{GEN}{padicappr}{GEN pol, GEN a}.
+Also available is \fun{GEN}{Zp_appr}{GEN f, GEN a} when $a$ is a
+\typ{PADIC}.
+
+\subsec{padicfields$(p, N, \{\fl=0\})$}\kbdsidx{padicfields}\label{se:padicfields}
+Returns a vector of polynomials generating all the extensions of degree
+$N$ of the field $\Q_p$ of $p$-adic rational numbers; $N$ is
+allowed to be a 2-component vector $[n,d]$, in which case we return the
+extensions of degree $n$ and discriminant $p^d$.
+
+The list is minimal in the sense that two different polynomials generate
+non-isomorphic extensions; in particular, the number of polynomials is the
+number of classes of non-isomorphic extensions. If $P$ is a polynomial in this
+list, $\alpha$ is any root of $P$ and $K = \Q_p(\alpha)$, then $\alpha$
+is the sum of a uniformizer and a (lift of a) generator of the residue field
+of $K$; in particular, the powers of $\alpha$ generate the ring of $p$-adic
+integers of $K$.
+
+If $\fl = 1$, replace each polynomial $P$ by a vector $[P, e, f, d, c]$
+where $e$ is the ramification index, $f$ the residual degree, $d$ the
+valuation of the discriminant, and $c$ the number of conjugate fields.
+If $\fl = 2$, only return the \emph{number} of extensions in a fixed
+algebraic closure (Krasner's formula), which is much faster.
+
+The library syntax is \fun{GEN}{padicfields0}{GEN p, GEN N, long flag}.
+Also available is
+\fun{GEN}{padicfields}{GEN p, long n, long d, long flag}, which computes
+extensions of $\Q_p$ of degree $n$ and discriminant $p^d$.
+
+\subsec{polchebyshev$(n,\{\fl=1\},\{a='x\})$}\kbdsidx{polchebyshev}\label{se:polchebyshev}
+Returns the $n^{\text{th}}$
+\idx{Chebyshev} polynomial of the first kind $T_n$ ($\fl=1$) or the second
+kind $U_n$ ($\fl=2$), evaluated at $a$ (\kbd{'x} by default). Both series of
+polynomials satisfy the 3-term relation
+$$ P_{n+1} = 2xP_n - P_{n-1}, $$
+and are determined by the initial conditions $U_0 = T_0 = 1$, $T_1 = x$,
+$U_1 = 2x$. In fact $T_n' = n U_{n-1}$ and, for all complex numbers $z$, we
+have $T_n(\cos z) = \cos (nz)$ and $U_{n-1}(\cos z) = \sin(nz)/\sin z$.
+If $n \geq 0$, then these polynomials have degree $n$.  For $n < 0$,
+$T_n$ is equal to $T_{-n}$ and $U_n$ is equal to $-U_{-2-n}$.
+In particular, $U_{-1} = 0$.
+
+The library syntax is \fun{GEN}{polchebyshev_eval}{long n, long flag, GEN a = NULL}.
+Also available are
+\fun{GEN}{polchebyshev}{long n, long \fl, long v},
+\fun{GEN}{polchebyshev1}{long n, long v} and
+\fun{GEN}{polchebyshev2}{long n, long v} for $T_n$ and $U_n$ respectively.
+
+\subsec{polcoeff$(x,n,\{v\})$}\kbdsidx{polcoeff}\label{se:polcoeff}
+Coefficient of degree $n$ of the polynomial $x$, with respect to the
+main variable if $v$ is omitted, with respect to $v$ otherwise.  If $n$
+is greater than the degree, the result is zero.
+
+Naturally applies to scalars (polynomial of degree $0$), as well as to
+rational functions whose denominator is a monomial.
+It also applies to power series: if $n$ is less than the valuation, the result
+is zero. If it is greater than the largest significant degree, then an error
+message is issued.
+
+ For greater flexibility, $x$ can be a vector or matrix type and the
+ function then returns \kbd{component(x,n)}.
+
+The library syntax is \fun{GEN}{polcoeff0}{GEN x, long n, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{polcyclo$(n,\{a = 'x\})$}\kbdsidx{polcyclo}\label{se:polcyclo}
+$n$-th cyclotomic polynomial, evaluated at $a$ (\kbd{'x} by default). The
+integer $n$ must be positive.
+
+Algorithm used: reduce to the case where $n$ is squarefree; to compute the
+cyclotomic polynomial, use $\Phi_{np}(x)=\Phi_n(x^p)/\Phi(x)$; to compute
+it evaluated, use $\Phi_n(x) = \prod_{d\mid n} (x^d-1)^{\mu(n/d)}$. In the
+evaluated case, the algorithm assumes that $a^d - 1$ is either $0$ or
+invertible, for all $d\mid n$. If this is not the case (the base ring has
+zero divisors), use \kbd{subst(polcyclo(n),x,a)}.
+
+The library syntax is \fun{GEN}{polcyclo_eval}{long n, GEN a  = NULL}.
+The variant \fun{GEN}{polcyclo}{long n, long v} returns the $n$-th
+cyclotomic polynomial in variable $v$.
+
+\subsec{polcyclofactors$(f)$}\kbdsidx{polcyclofactors}\label{se:polcyclofactors}
+Returns a vector of polynomials, whose product is the product of
+distinct cyclotomic polynomials dividing $f$.
+\bprog
+? f = x^10+5*x^8-x^7+8*x^6-4*x^5+8*x^4-3*x^3+7*x^2+3;
+? v = polcyclofactors(f)
+%2 = [x^2 + 1, x^2 + x + 1, x^4 - x^3 + x^2 - x + 1]
+? apply(poliscycloprod, v)
+%3 = [1, 1, 1]
+? apply(poliscyclo, v)
+%4 = [4, 3, 10]
+ at eprog\noindent In general, the polynomials are products of cyclotomic
+polynomials and not themselves irreducible:
+\bprog
+? g = x^8+2*x^7+6*x^6+9*x^5+12*x^4+11*x^3+10*x^2+6*x+3;
+? polcyclofactors(g)
+%2 = [x^6 + 2*x^5 + 3*x^4 + 3*x^3 + 3*x^2 + 2*x + 1]
+? factor(%[1])
+%3 =
+[            x^2 + x + 1 1]
+
+[x^4 + x^3 + x^2 + x + 1 1]
+ at eprog
+
+The library syntax is \fun{GEN}{polcyclofactors}{GEN f}.
+
+\subsec{poldegree$(x,\{v\})$}\kbdsidx{poldegree}\label{se:poldegree}
+Degree of the polynomial $x$ in the main variable if $v$ is omitted, in
+the variable $v$ otherwise.
+
+The degree of $0$ is a fixed negative number, whose exact value should not
+be used. The degree of a non-zero scalar is $0$. Finally, when $x$ is a
+non-zero polynomial or rational function, returns the ordinary degree of
+$x$. Raise an error otherwise.
+
+The library syntax is \fun{long}{poldegree}{GEN x, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{poldisc$(\var{pol},\{v\})$}\kbdsidx{poldisc}\label{se:poldisc}
+Discriminant of the polynomial
+\var{pol} in the main variable if $v$ is omitted, in $v$ otherwise. The
+algorithm used is the \idx{subresultant algorithm}.
+
+The library syntax is \fun{GEN}{poldisc0}{GEN pol, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{poldiscreduced$(f)$}\kbdsidx{poldiscreduced}\label{se:poldiscreduced}
+Reduced discriminant vector of the
+(integral, monic) polynomial $f$. This is the vector of elementary divisors
+of $\Z[\alpha]/f'(\alpha)\Z[\alpha]$, where $\alpha$ is a root of the
+polynomial $f$. The components of the result are all positive, and their
+product is equal to the absolute value of the discriminant of~$f$.
+
+The library syntax is \fun{GEN}{reduceddiscsmith}{GEN f}.
+
+\subsec{polgraeffe$(f)$}\kbdsidx{polgraeffe}\label{se:polgraeffe}
+Returns the \idx{Graeffe} transform $g$ of $f$, such that $g(x^2) = f(x)
+f(-x)$.
+
+The library syntax is \fun{GEN}{polgraeffe}{GEN f}.
+
+\subsec{polhensellift$(A, B, p, e)$}\kbdsidx{polhensellift}\label{se:polhensellift}
+Given a prime $p$, an integral polynomial $A$ whose leading coefficient
+is a $p$-unit, a vector $B$ of integral polynomials that are monic and
+pairwise relatively prime modulo $p$, and whose product is congruent to
+$A/\text{lc}(A)$ modulo $p$, lift the elements of $B$ to polynomials whose
+product is congruent to $A$ modulo $p^e$.
+
+More generally, if $T$ is an integral polynomial irreducible mod $p$, and
+$B$ is a factorization of $A$ over the finite field $\F_p[t]/(T)$, you can
+lift it to $\Z_p[t]/(T, p^e)$ by replacing the $p$ argument with $[p,T]$:
+\bprog
+? { T = t^3 - 2; p = 7; A = x^2 + t + 1;
+    B = [x + (3*t^2 + t + 1), x + (4*t^2 + 6*t + 6)];
+    r = polhensellift(A, B, [p, T], 6) }
+%1 = [x + (20191*t^2 + 50604*t + 75783), x + (97458*t^2 + 67045*t + 41866)]
+? liftall( r[1] * r[2] * Mod(Mod(1,p^6),T) )
+%2 = x^2 + (t + 1)
+ at eprog
+
+The library syntax is \fun{GEN}{polhensellift}{GEN A, GEN B, GEN p, long e}.
+
+\subsec{polhermite$(n,\{a='x\})$}\kbdsidx{polhermite}\label{se:polhermite}
+$n^{\text{th}}$ \idx{Hermite} polynomial $H_n$ evaluated at $a$
+(\kbd{'x} by default), i.e.
+$$ H_n(x) = (-1)^n\*e^{x^2} \dfrac{d^n}{dx^n}e^{-x^2}.$$
+
+The library syntax is \fun{GEN}{polhermite_eval}{long n, GEN a = NULL}.
+The variant \fun{GEN}{polhermite}{long n, long v} returns the $n$-th
+Hermite polynomial in variable $v$.
+
+\subsec{polinterpolate$(X,\{Y\},\{x\},\{\&e\})$}\kbdsidx{polinterpolate}\label{se:polinterpolate}
+Given the data vectors
+$X$ and $Y$ of the same length $n$ ($X$ containing the $x$-coordinates,
+and $Y$ the corresponding $y$-coordinates), this function finds the
+\idx{interpolating polynomial} passing through these points and evaluates it
+at~$x$. If $Y$ is omitted, return the polynomial interpolating the
+$(i,X[i])$. If present, $e$ will contain an error estimate on the returned
+value.
+
+The library syntax is \fun{GEN}{polint}{GEN X, GEN Y = NULL, GEN x = NULL, GEN *e = NULL}.
+
+\subsec{poliscyclo$(f)$}\kbdsidx{poliscyclo}\label{se:poliscyclo}
+Returns 0 if $f$ is not a cyclotomic polynomial, and $n > 0$ if $f =
+\Phi_n$, the $n$-th cyclotomic polynomial.
+\bprog
+? poliscyclo(x^4-x^2+1)
+%1 = 12
+? polcyclo(12)
+%2 = x^4 - x^2 + 1
+? poliscyclo(x^4-x^2-1)
+%3 = 0
+ at eprog
+
+The library syntax is \fun{long}{poliscyclo}{GEN f}.
+
+\subsec{poliscycloprod$(f)$}\kbdsidx{poliscycloprod}\label{se:poliscycloprod}
+Returns 1 if $f$ is a product of cyclotomic polynomial, and $0$
+otherwise.
+\bprog
+? f = x^6+x^5-x^3+x+1;
+? poliscycloprod(f)
+%2 = 1
+? factor(f)
+%3 =
+[  x^2 + x + 1 1]
+
+[x^4 - x^2 + 1 1]
+? [ poliscyclo(T) | T <- %[,1] ]
+%4 = [3, 12]
+? polcyclo(3) * polcyclo(12)
+%5 = x^6 + x^5 - x^3 + x + 1
+ at eprog
+
+The library syntax is \fun{long}{poliscycloprod}{GEN f}.
+
+\subsec{polisirreducible$(\var{pol})$}\kbdsidx{polisirreducible}\label{se:polisirreducible}
+\var{pol} being a polynomial (univariate in the present version \vers),
+returns 1 if \var{pol} is non-constant and irreducible, 0 otherwise.
+Irreducibility is checked over the smallest base field over which \var{pol}
+seems to be defined.
+
+The library syntax is \fun{long}{isirreducible}{GEN pol}.
+
+\subsec{pollead$(x,\{v\})$}\kbdsidx{pollead}\label{se:pollead}
+Leading coefficient of the polynomial or power series $x$. This is
+ computed with respect to the main variable of $x$ if $v$ is omitted, with
+ respect to the variable $v$ otherwise.
+
+The library syntax is \fun{GEN}{pollead}{GEN x, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{pollegendre$(n,\{a='x\})$}\kbdsidx{pollegendre}\label{se:pollegendre}
+$n^{\text{th}}$ \idx{Legendre polynomial} evaluated at $a$ (\kbd{'x} by
+default).
+
+The library syntax is \fun{GEN}{pollegendre_eval}{long n, GEN a = NULL}.
+To obtain the $n$-th Legendre polynomial in variable $v$,
+use \fun{GEN}{pollegendre}{long n, long v}.
+
+\subsec{polrecip$(\var{pol})$}\kbdsidx{polrecip}\label{se:polrecip}
+Reciprocal polynomial of \var{pol}, i.e.~the coefficients are in
+reverse order. \var{pol} must be a polynomial.
+
+The library syntax is \fun{GEN}{polrecip}{GEN pol}.
+
+\subsec{polresultant$(x,y,\{v\},\{\fl=0\})$}\kbdsidx{polresultant}\label{se:polresultant}
+Resultant of the two
+polynomials $x$ and $y$ with exact entries, with respect to the main
+variables of $x$ and $y$ if $v$ is omitted, with respect to the variable $v$
+otherwise. The algorithm assumes the base ring is a domain. If you also need
+the $u$ and $v$ such that $x*u + y*v = \text{Res}(x,y)$, use the
+\tet{polresultantext} function.
+
+If $\fl=0$ (default), uses the the algorithm best suited to the inputs,
+either the \idx{subresultant algorithm} (Lazard/Ducos variant, generic case),
+a modular algorithm (inputs in $\Q[X]$) or Sylvester's matrix (inexact
+inputs).
+
+If $\fl=1$, uses the determinant of Sylvester's matrix instead; this should
+always be slower than the default.
+
+The library syntax is \fun{GEN}{polresultant0}{GEN x, GEN y, long v = -1, long flag}, where \kbd{v} is a variable number.
+
+\subsec{polresultantext$(A,B,\{v\})$}\kbdsidx{polresultantext}\label{se:polresultantext}
+Finds polynomials $U$ and $V$ such that $A*U + B*V = R$, where $R$ is
+the resultant of $U$ and $V$ with respect to the main variables of $A$ and
+$B$ if $v$ is omitted, and with respect to $v$ otherwise. Returns the row
+vector $[U,V,R]$. The algorithm used (subresultant) assumes that the base
+ring is a domain.
+\bprog
+? A = x*y; B = (x+y)^2;
+? [U,V,R] = polresultantext(A, B)
+%2 = [-y*x - 2*y^2, y^2, y^4]
+? A*U + B*V
+%3 = y^4
+? [U,V,R] = polresultantext(A, B, y)
+%4 = [-2*x^2 - y*x, x^2, x^4]
+? A*U+B*V
+%5 = x^4
+ at eprog
+
+The library syntax is \fun{GEN}{polresultantext0}{GEN A, GEN B, long v = -1}, where \kbd{v} is a variable number.
+Also available is
+\fun{GEN}{polresultantext}{GEN x, GEN y}.
+
+\subsec{polroots$(x)$}\kbdsidx{polroots}\label{se:polroots}
+Complex roots of the polynomial
+\var{x}, given as a column vector where each root is repeated according to
+its multiplicity. The precision is given as for transcendental functions: in
+GP it is kept in the variable \kbd{realprecision} and is transparent to the
+user, but it must be explicitly given as a second argument in library mode.
+
+The algorithm used is a modification of A.~Sch\"onhage\sidx{Sch\"onage}'s
+root-finding algorithm, due to and originally implemented by X.~Gourdon.
+Barring bugs, it is guaranteed to converge and to give the roots to the
+required accuracy.
+
+The library syntax is \fun{GEN}{roots}{GEN x, long prec}.
+
+\subsec{polrootsmod$(\var{pol},p,\{\fl=0\})$}\kbdsidx{polrootsmod}\label{se:polrootsmod}
+Row vector of roots modulo $p$ of the polynomial \var{pol}.
+Multiple roots are \emph{not} repeated.
+\bprog
+? polrootsmod(x^2-1,2)
+%1 = [Mod(1, 2)]~
+ at eprog\noindent
+If $p$ is very small, you may set $\fl=1$, which uses a naive search.
+
+The library syntax is \fun{GEN}{rootmod0}{GEN pol, GEN p, long flag}.
+
+\subsec{polrootspadic$(x,p,r)$}\kbdsidx{polrootspadic}\label{se:polrootspadic}
+Vector of $p$-adic roots of the polynomial \var{pol}, given to
+$p$-adic precision $r$ $p$ is assumed to be a prime. Multiple roots are
+\emph{not} repeated. Note that this is not the same as the roots in
+$\Z/p^r\Z$, rather it gives approximations in $\Z/p^r\Z$ of the true roots
+living in $\Q_p$.
+\bprog
+? polrootspadic(x^3 - x^2 + 64, 2, 5)
+%1 = [2^3 + O(2^5), 2^3 + 2^4 + O(2^5), 1 + O(2^5)]~
+ at eprog
+If \var{pol} has inexact \typ{PADIC} coefficients, this is not always
+well-defined; in this case, the polynomial is first made integral by dividing
+out the $p$-adic content, then lifted
+to $\Z$ using \tet{truncate} coefficientwise. Hence the roots given are
+approximations of the roots of an exact polynomial which is $p$-adically
+close to the input. To avoid pitfalls, we advise to only factor polynomials
+with eact rational coefficients.
+
+The library syntax is \fun{GEN}{rootpadic}{GEN x, GEN p, long r}.
+
+\subsec{polsturm$(\var{pol},\{a\},\{b\})$}\kbdsidx{polsturm}\label{se:polsturm}
+Number of real roots of the real squarefree polynomial \var{pol} in the
+interval $]a,b]$, using Sturm's algorithm. $a$ (resp.~$b$) is taken to be
+$-\infty$ (resp.~$+\infty$) if omitted.
+
+The library syntax is \fun{long}{sturmpart}{GEN pol, GEN a = NULL, GEN b = NULL}.
+Also available is \fun{long}{sturm}{GEN pol} (total number of real
+roots).
+
+\subsec{polsubcyclo$(n,d,\{v='x\})$}\kbdsidx{polsubcyclo}\label{se:polsubcyclo}
+Gives polynomials (in variable $v$) defining the sub-Abelian extensions
+of degree $d$ of the cyclotomic field $\Q(\zeta_n)$, where $d\mid \phi(n)$.
+
+If there is exactly one such extension the output is a polynomial, else it is
+a vector of polynomials, possibly empty. To get a vector in all cases,
+use \kbd{concat([], polsubcyclo(n,d))}.
+
+The function \tet{galoissubcyclo} allows to specify exactly which
+sub-Abelian extension should be computed.
+
+The library syntax is \fun{GEN}{polsubcyclo}{long n, long d, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{polsylvestermatrix$(x,y)$}\kbdsidx{polsylvestermatrix}\label{se:polsylvestermatrix}
+Forms the Sylvester matrix
+corresponding to the two polynomials $x$ and $y$, where the coefficients of
+the polynomials are put in the columns of the matrix (which is the natural
+direction for solving equations afterwards). The use of this matrix can be
+essential when dealing with polynomials with inexact entries, since
+polynomial Euclidean division doesn't make much sense in this case.
+
+The library syntax is \fun{GEN}{sylvestermatrix}{GEN x, GEN y}.
+
+\subsec{polsym$(x,n)$}\kbdsidx{polsym}\label{se:polsym}
+Creates the column vector of the \idx{symmetric powers} of the roots of the
+polynomial $x$ up to power $n$, using Newton's formula.
+
+The library syntax is \fun{GEN}{polsym}{GEN x, long n}.
+
+\subsec{poltchebi$(n,\{v='x\})$}\kbdsidx{poltchebi}\label{se:poltchebi}
+Deprecated alias for \kbd{polchebyshev}
+
+The library syntax is \fun{GEN}{polchebyshev1}{long n, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{polzagier$(n,m)$}\kbdsidx{polzagier}\label{se:polzagier}
+Creates Zagier's polynomial $P_n^{(m)}$ used in
+the functions \kbd{sumalt} and \kbd{sumpos} (with $\fl=1$). One must have $m\le
+n$. The exact definition can be found in ``Convergence acceleration of
+alternating series'', Cohen et al., Experiment.~Math., vol.~9, 2000, pp.~3--12.
+
+%@article {MR2001m:11222,
+%    AUTHOR = {Cohen, Henri and Rodriguez Villegas, Fernando and Zagier, Don},
+%     TITLE = {Convergence acceleration of alternating series},
+%   JOURNAL = {Experiment. Math.},
+%    VOLUME = {9},
+%      YEAR = {2000},
+%    NUMBER = {1},
+%     PAGES = {3--12},
+%}
+
+The library syntax is \fun{GEN}{polzag}{long n, long m}.
+
+\subsec{serconvol$(x,y)$}\kbdsidx{serconvol}\label{se:serconvol}
+Convolution (or \idx{Hadamard product}) of the
+two power series $x$ and $y$; in other words if $x=\sum a_k*X^k$ and $y=\sum
+b_k*X^k$ then $\kbd{serconvol}(x,y)=\sum a_k*b_k*X^k$.
+
+The library syntax is \fun{GEN}{convol}{GEN x, GEN y}.
+
+\subsec{serlaplace$(x)$}\kbdsidx{serlaplace}\label{se:serlaplace}
+$x$ must be a power series with non-negative
+exponents. If $x=\sum (a_k/k!)*X^k$ then the result is $\sum a_k*X^k$.
+
+The library syntax is \fun{GEN}{laplace}{GEN x}.
+
+\subsec{serreverse$(s)$}\kbdsidx{serreverse}\label{se:serreverse}
+Reverse power series of $s$, i.e. the series $t$ such that $t(s) = x$;
+$s$ must be a power series whose valuation is exactly equal to one.
+\bprog
+? \ps 8
+? t = serreverse(tan(x))
+%2 = x - 1/3*x^3 + 1/5*x^5 - 1/7*x^7 + O(x^8)
+? tan(t)
+%3 = x + O(x^8)
+ at eprog
+
+The library syntax is \fun{GEN}{serreverse}{GEN s}.
+
+\subsec{subst$(x,y,z)$}\kbdsidx{subst}\label{se:subst}
+Replace the simple variable $y$ by the argument $z$ in the ``polynomial''
+expression $x$. Every type is allowed for $x$, but if it is not a genuine
+polynomial (or power series, or rational function), the substitution will be
+done as if the scalar components were polynomials of degree zero. In
+particular, beware that:
+
+\bprog
+? subst(1, x, [1,2; 3,4])
+%1 =
+[1 0]
+
+[0 1]
+
+? subst(1, x, Mat([0,1]))
+  ***   at top-level: subst(1,x,Mat([0,1])
+  ***                 ^--------------------
+  *** subst: forbidden substitution by a non square matrix.
+ at eprog\noindent
+If $x$ is a power series, $z$ must be either a polynomial, a power
+series, or a rational function. Finally, if $x$ is a vector,
+matrix or list, the substitution is applied to each individual entry.
+
+Use the function \kbd{substvec} to replace several variables at once,
+or the function \kbd{substpol} to replace a polynomial expression.
+
+The library syntax is \fun{GEN}{gsubst}{GEN x, long y, GEN z}, where \kbd{y} is a variable number.
+
+\subsec{substpol$(x,y,z)$}\kbdsidx{substpol}\label{se:substpol}
+Replace the ``variable'' $y$ by the argument $z$ in the ``polynomial''
+expression $x$. Every type is allowed for $x$, but the same behavior
+as \kbd{subst} above apply.
+
+The difference with \kbd{subst} is that $y$ is allowed to be any polynomial
+here. The substitution is done moding out all components of $x$
+(recursively) by $y - t$, where $t$ is a new free variable of lowest
+priority. Then substituting $t$ by $z$ in the resulting expression. For
+instance
+\bprog
+? substpol(x^4 + x^2 + 1, x^2, y)
+%1 = y^2 + y + 1
+? substpol(x^4 + x^2 + 1, x^3, y)
+%2 = x^2 + y*x + 1
+? substpol(x^4 + x^2 + 1, (x+1)^2, y)
+%3 = (-4*y - 6)*x + (y^2 + 3*y - 3)
+ at eprog
+
+The library syntax is \fun{GEN}{gsubstpol}{GEN x, GEN y, GEN z}.
+Further, \fun{GEN}{gdeflate}{GEN T, long v, long d} attempts to
+write $T(x)$ in the form $t(x^d)$, where $x=$\kbd{pol\_x}$(v)$, and returns
+\kbd{NULL} if the substitution fails (for instance in the example \kbd{\%2}
+above).
+
+\subsec{substvec$(x,v,w)$}\kbdsidx{substvec}\label{se:substvec}
+$v$ being a vector of monomials of degree 1 (variables),
+$w$ a vector of expressions of the same length, replace in the expression
+$x$ all occurrences of $v_i$ by $w_i$. The substitutions are done
+simultaneously; more precisely, the $v_i$ are first replaced by new
+variables in $x$, then these are replaced by the $w_i$:
+
+\bprog
+? substvec([x,y], [x,y], [y,x])
+%1 = [y, x]
+? substvec([x,y], [x,y], [y,x+y])
+%2 = [y, x + y]     \\ not [y, 2*y]
+ at eprog
+
+The library syntax is \fun{GEN}{gsubstvec}{GEN x, GEN v, GEN w}.
+
+\subsec{sumformal$(f,\{v\})$}\kbdsidx{sumformal}\label{se:sumformal}
+\idx{formal sum} of the polynomial expression $f$ with respect to the
+main variable if $v$ is omitted, with respect to the variable $v$ otherwise;
+it is assumed that the base ring has characteristic zero. In other words,
+considering $f$ as a polynomial function in the variable $v$,
+returns $F$, a polynomial in $v$ vanishing at $0$, such that $F(b) - F(a)
+= sum_{v = a+1}^b f(v)$:
+\bprog
+? sumformal(n)  \\ 1 + ... + n
+%1 = 1/2*n^2 + 1/2*n
+? f(n) = n^3+n^2+1;
+? F = sumformal(f(n))  \\ f(1) + ... + f(n)
+%3 = 1/4*n^4 + 5/6*n^3 + 3/4*n^2 + 7/6*n
+? sum(n = 1, 2000, f(n)) == subst(F, n, 2000)
+%4 = 1
+? sum(n = 1001, 2000, f(n)) == subst(F, n, 2000) - subst(F, n, 1000)
+%5 = 1
+? sumformal(x^2 + x*y + y^2, y)
+%6 = y*x^2 + (1/2*y^2 + 1/2*y)*x + (1/3*y^3 + 1/2*y^2 + 1/6*y)
+? x^2 * y + x * sumformal(y) + sumformal(y^2) == %
+%7 = 1
+ at eprog
+
+The library syntax is \fun{GEN}{sumformal}{GEN f, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{taylor$(x,t,\{d=\var{seriesprecision}\})$}\kbdsidx{taylor}\label{se:taylor}
+Taylor expansion around $0$ of $x$ with respect to
+the simple variable $t$. $x$ can be of any reasonable type, for example a
+rational function. Contrary to \tet{Ser}, which takes the valuation into
+account, this function adds $O(t^d)$ to all components of $x$.
+\bprog
+? taylor(x/(1+y), y, 5)
+%1 = (y^4 - y^3 + y^2 - y + 1)*x + O(y^5)
+? Ser(x/(1+y), y, 5)
+ ***   at top-level: Ser(x/(1+y),y,5)
+ ***                 ^----------------
+ *** Ser: main variable must have higher priority in gtoser.
+ at eprog
+
+The library syntax is \fun{GEN}{tayl}{GEN x, long t, long precdl}, where \kbd{t} is a variable number.
+
+\subsec{thue$(\var{tnf},a,\{\var{sol}\})$}\kbdsidx{thue}\label{se:thue}
+Returns all solutions of the equation
+$P(x,y)=a$ in integers $x$ and $y$, where \var{tnf} was created with
+$\kbd{thueinit}(P)$. If present, \var{sol} must contain the solutions of
+$\Norm(x)=a$ modulo units of positive norm in the number field
+defined by $P$ (as computed by \kbd{bnfisintnorm}). If there are infinitely
+many solutions, an error will be issued.
+
+It is allowed to input directly the polynomial $P$ instead of a \var{tnf},
+in which case, the function first performs \kbd{thueinit(P,0)}. This is
+very wasteful if more than one value of $a$ is required.
+
+If \var{tnf} was computed without assuming GRH (flag $1$ in \tet{thueinit}),
+then the result is unconditional. Otherwise, it depends in principle of the
+truth of the GRH, but may still be unconditionally correct in some
+favorable cases. The result is conditional on the GRH if
+$a\neq \pm 1$ and, $P$ has a single irreducible rational factor, whose
+associated tentative class number $h$ and regulator $R$ (as computed
+assuming the GRH) satisfy
+
+\item $h > 1$,
+
+\item $R/0.2 > 1.5$.
+
+Here's how to solve the Thue equation $x^{13} - 5y^{13} = - 4$:
+\bprog
+? tnf = thueinit(x^13 - 5);
+? thue(tnf, -4)
+%1 = [[1, 1]]
+ at eprog\noindent In this case, one checks that \kbd{bnfinit(x\pow13 -5).no}
+is $1$. Hence, the only solution is $(x,y) = (1,1)$, and the result is
+unconditional. On the other hand:
+\bprog
+? P = x^3-2*x^2+3*x-17; tnf = thueinit(P);
+? thue(tnf, -15)
+%2 = [[1, 1]]  \\ a priori conditional on the GRH.
+? K = bnfinit(P); K.no
+%3 = 3
+? K.reg
+%4 = 2.8682185139262873674706034475498755834
+ at eprog
+This time the result is conditional. All results computed using this
+particular \var{tnf} are likewise conditional, \emph{except} for a right-hand
+side of $\pm 1$.
+The above result is in fact correct, so we did not just disprove the GRH:
+\bprog
+? tnf = thueinit(x^3-2*x^2+3*x-17, 1 /*unconditional*/);
+? thue(tnf, -15)
+%4 = [[1, 1]]
+ at eprog
+Note that reducible or non-monic polynomials are allowed:
+\bprog
+? tnf = thueinit((2*x+1)^5 * (4*x^3-2*x^2+3*x-17), 1);
+? thue(tnf, 128)
+%2 = [[-1, 0], [1, 0]]
+ at eprog\noindent Reducible polynomials are in fact much easier to handle.
+
+The library syntax is \fun{GEN}{thue}{GEN tnf, GEN a, GEN sol = NULL}.
+
+\subsec{thueinit$(P,\{\fl=0\})$}\kbdsidx{thueinit}\label{se:thueinit}
+Initializes the \var{tnf} corresponding to $P$, a univariate polynomial
+with integer coefficients. The result is meant to be used in conjunction with
+\tet{thue} to solve Thue equations $P(X / Y)Y^{\deg P} = a$, where $a$ is an
+integer.
+
+If $\fl$ is non-zero, certify results unconditionally. Otherwise, assume
+\idx{GRH}, this being much faster of course. In the latter case, the result
+may still be unconditionally correct, see \tet{thue}. For instance in most
+cases where $P$ is reducible (not a pure power of an irreducible), \emph{or}
+conditional computed class groups are trivial \emph{or} the right hand side
+is $\pm1$, then results are always unconditional.
+
+The library syntax is \fun{GEN}{thueinit}{GEN P, long flag, long prec}.
+%SECTION: polynomials
+
+\section{Vectors, matrices, linear algebra and sets}
+\label{se:linear_algebra}
+
+Note that most linear algebra functions operating on subspaces defined by
+generating sets (such as \tet{mathnf}, \tet{qflll}, etc.) take matrices as
+arguments. As usual, the generating vectors are taken to be the
+\emph{columns} of the given matrix.
+
+Since PARI does not have a strong typing system, scalars live in
+unspecified commutative base rings. It is very difficult to write
+robust linear algebra routines in such a general setting. We thus
+assume that the base ring is a domain and work over its field of
+fractions. If the base ring is \emph{not} a domain, one gets an error as soon
+as a non-zero pivot turns out to be non-invertible. Some functions,
+e.g.~\kbd{mathnf} or \kbd{mathnfmod}, specifically assume that the base ring is
+$\Z$.
+
+
+\subsec{algdep$(x,k,\{\fl=0\})$}\kbdsidx{algdep}\label{se:algdep}
+\sidx{algebraic dependence}
+$x$ being real/complex, or $p$-adic, finds a polynomial of degree at most
+$k$ with integer coefficients having $x$ as approximate root. Note that the
+polynomial which is obtained is not necessarily the ``correct'' one. In fact
+it is not even guaranteed to be irreducible. One can check the closeness
+either by a polynomial evaluation (use \tet{subst}), or by computing the
+roots of the polynomial given by \kbd{algdep} (use \tet{polroots}).
+
+Internally, \tet{lindep}$([1,x,\ldots,x^k], \fl)$ is used.
+A non-zero value of $\fl$ may improve on the default behavior
+if the input number is known to a \emph{huge} accuracy, and you suspect the
+last bits are incorrect  (this truncates the number, throwing away the least
+significant bits), but default values are usually sufficient:
+\bprog
+? \p200
+? algdep(2^(1/6)+3^(1/5), 30);      \\ wrong in 0.8s
+? algdep(2^(1/6)+3^(1/5), 30, 100); \\ wrong in 0.4s
+? algdep(2^(1/6)+3^(1/5), 30, 170); \\ right in 0.8s
+? algdep(2^(1/6)+3^(1/5), 30, 200); \\ wrong in 1.0s
+? \p250
+? algdep(2^(1/6)+3^(1/5), 30);      \\ right in 1.0s
+? algdep(2^(1/6)+3^(1/5), 30, 200); \\ right in 1.0s
+? \p500
+? algdep(2^(1/6)+3^(1/5), 30);      \\ right in 2.9s
+? \p1000
+? algdep(2^(1/6)+3^(1/5), 30);      \\ right in 10.6s
+ at eprog\noindent
+The changes in \kbd{defaultprecision} only affect the quality of the
+initial approximation to $2^{1/6} + 3^{1/5}$, \kbd{algdep} itself uses
+exact operations (the size of its operands depend on the accuracy of the
+input of course: more accurate input means slower operations).
+
+Proceeding by increments of 5 digits of accuracy, \kbd{algdep} with default
+flag produces its first correct result at 205 digits, and from then on a
+steady stream of correct results.
+
+The above example is the test case studied in a 2000 paper by Borwein and
+Lisonek: Applications of integer relation algorithms, \emph{Discrete Math.},
+{\bf 217}, p.~65--82. The version of PARI tested there was 1.39, which
+succeeded reliably from precision 265 on, in about 200 as much time as the
+current version.
+
+The library syntax is \fun{GEN}{algdep0}{GEN x, long k, long flag}.
+Also available is \fun{GEN}{algdep}{GEN x, long k} ($\fl=0$).
+
+\subsec{charpoly$(A,\{v='x\},\{\fl=5\})$}\kbdsidx{charpoly}\label{se:charpoly}
+\idx{characteristic polynomial}
+of $A$ with respect to the variable $v$, i.e.~determinant of $v*I-A$ if $A$
+is a square matrix.
+\bprog
+? charpoly([1,2;3,4]);
+%1 = x^2 - 5*x - 2
+? charpoly([1,2;3,4],, 't)
+%2 = t^2 - 5*t - 2
+ at eprog\noindent
+If $A$ is not a square matrix, the function returns the characteristic
+polynomial of the map ``multiplication by $A$'' if $A$ is a scalar:
+\bprog
+? charpoly(Mod(x+2, x^3-2))
+%1 = x^3 - 6*x^2 + 12*x - 10
+? charpoly(I)
+%2 = x^2 + 1
+? charpoly(quadgen(5))
+%3 = x^2 - x - 1
+? charpoly(ffgen(ffinit(2,4)))
+%4 = Mod(1, 2)*x^4 + Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(1, 2)*x + Mod(1, 2)
+ at eprog
+
+The value of $\fl$ is only significant for matrices, and we advise to stick
+to the default value. Let $n$ be the dimension of $A$.
+
+If $\fl=0$, same method (Le Verrier's) as for computing the adjoint matrix,
+i.e.~using the traces of the powers of $A$. Assumes that $n!$ is
+invertible; uses $O(n^4)$ scalar operations.
+
+If $\fl=1$, uses Lagrange interpolation which is usually the slowest method.
+Assumes that $n!$ is invertible; uses $O(n^4)$ scalar operations.
+
+If $\fl=2$, uses the Hessenberg form. Assumes that the base ring is a field.
+Uses $O(n^3)$ scalar operations, but suffers from coefficient explosion
+unless the base field is finite or $\R$.
+
+If $\fl=3$, uses Berkowitz's division free algorithm, valid over any
+ring (commutative, with unit). Uses $O(n^4)$ scalar operations.
+
+If $\fl=4$, $x$ must be integral. Uses a modular algorithm: Hessenberg form
+for various small primes, then Chinese remainders.
+
+If $\fl=5$ (default), uses the ``best'' method given $x$.
+This means we use Berkowitz unless the base ring is $\Z$ (use $\fl=4$)
+or a field where coefficient explosion does not occur,
+e.g.~a finite field or the reals (use $\fl=2$).
+
+The library syntax is \fun{GEN}{charpoly0}{GEN A, long v = -1, long flag}, where \kbd{v} is a variable number.
+Also available are
+\fun{GEN}{charpoly}{GEN x, long v} ($\fl=5$),
+\fun{GEN}{caract}{GEN A, long v} ($\fl=1$),
+\fun{GEN}{carhess}{GEN A, long v} ($\fl=2$),
+\fun{GEN}{carberkowitz}{GEN A, long v} ($\fl=3$) and
+\fun{GEN}{caradj}{GEN A, long v, GEN *pt}. In this
+last case, if \var{pt} is not \kbd{NULL}, \kbd{*pt} receives the address of
+the adjoint matrix of $A$ (see \tet{matadjoint}), so both can be obtained at
+once.
+
+\subsec{concat$(x,\{y\})$}\kbdsidx{concat}\label{se:concat}
+Concatenation of $x$ and $y$. If $x$ or $y$ is
+not a vector or matrix, it is considered as a one-dimensional vector. All
+types are allowed for $x$ and $y$, but the sizes must be compatible. Note
+that matrices are concatenated horizontally, i.e.~the number of rows stays
+the same. Using transpositions, one can concatenate them vertically,
+but it is often simpler to use \tet{matconcat}.
+\bprog
+? x = matid(2); y = 2*matid(2);
+? concat(x,y)
+%2 =
+[1 0 2 0]
+
+[0 1 0 2]
+? concat(x~,y~)~
+%3 =
+[1 0]
+
+[0 1]
+
+[2 0]
+
+[0 2]
+? matconcat([x;y])
+%4 =
+[1 0]
+
+[0 1]
+
+[2 0]
+
+[0 2]
+ at eprog\noindent
+To concatenate vectors sideways (i.e.~to obtain a two-row or two-column
+matrix), use \tet{Mat} instead, or \tet{matconcat}:
+\bprog
+? x = [1,2];
+? y = [3,4];
+? concat(x,y)
+%3 = [1, 2, 3, 4]
+
+? Mat([x,y]~)
+%4 =
+[1 2]
+
+[3 4]
+? matconcat([x;y])
+%5 =
+[1 2]
+
+[3 4]
+ at eprog
+Concatenating a row vector to a matrix having the same number of columns will
+add the row to the matrix (top row if the vector is $x$, i.e.~comes first, and
+bottom row otherwise).
+
+The empty matrix \kbd{[;]} is considered to have a number of rows compatible
+with any operation, in particular concatenation. (Note that this is
+\emph{not} the case for empty vectors \kbd{[~]} or \kbd{[~]\til}.)
+
+If $y$ is omitted, $x$ has to be a row vector or a list, in which case its
+elements are concatenated, from left to right, using the above rules.
+\bprog
+? concat([1,2], [3,4])
+%1 = [1, 2, 3, 4]
+? a = [[1,2]~, [3,4]~]; concat(a)
+%2 =
+[1 3]
+
+[2 4]
+
+? concat([1,2; 3,4], [5,6]~)
+%3 =
+[1 2 5]
+
+[3 4 6]
+? concat([%, [7,8]~, [1,2,3,4]])
+%5 =
+[1 2 5 7]
+
+[3 4 6 8]
+
+[1 2 3 4]
+ at eprog
+
+The library syntax is \fun{GEN}{concat}{GEN x, GEN y = NULL}.
+\fun{GEN}{concat1}{GEN x} is a shortcut for \kbd{concat(x,NULL)}.
+
+\subsec{forqfvec$(v,q,b,\var{expr})$}\kbdsidx{forqfvec}\label{se:forqfvec}
+$q$ being a square and symmetric matrix representing a positive definite
+quadratic form, evaluate \kbd{expr} for all vector $v$ such that $q(v)\leq b$.
+The formal variable $v$ runs through all such vectors in turn.
+\bprog
+? forqfvec(v, [3,2;2,3], 3, print(v))
+[0, 1]~
+[1, 0]~
+[-1, 1]~
+ at eprog
+
+The library syntax is \fun{void}{forqfvec0}{GEN v, GEN q = NULL, GEN b}.
+The following function is also available:
+\fun{void}{forqfvec}{void *E, long (*fun)(void *, GEN, double), GEN q, GEN b}:
+Evaluate \kbd{fun(E,v,m)} on all $v$ such that $q(v)<b$, where $v$ is a
+\typ{VECSMALL} and $m=q(v)$ is a C double. The function \kbd{fun} must
+return $0$, unless \kbd{forqfvec} should stop, in which case, it should
+return $1$.
+
+\subsec{lindep$(v,\{\fl=0\})$}\kbdsidx{lindep}\label{se:lindep}
+\sidx{linear dependence} finds a small non-trivial integral linear
+combination between components of $v$. If none can be found return an empty
+vector.
+
+If $v$ is a vector with real/complex entries we use a floating point
+(variable precision) LLL algorithm. If $\fl = 0$ the accuracy is chosen
+internally using a crude heuristic. If $\fl > 0$ the computation is done with
+an accuracy of $\fl$ decimal digits. To get meaningful results in the latter
+case, the parameter $\fl$ should be smaller than the number of correct
+decimal digits in the input.
+
+\bprog
+? lindep([sqrt(2), sqrt(3), sqrt(2)+sqrt(3)])
+%1 = [-1, -1, 1]~
+ at eprog
+
+If $v$ is $p$-adic, $\fl$ is ignored and the algorithm LLL-reduces a
+suitable (dual) lattice.
+\bprog
+? lindep([1, 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5)])
+%2 = [1, -2]~
+ at eprog
+
+If $v$ is a matrix, $\fl$ is ignored and the function returns a non trivial
+kernel vector (combination of the columns).
+\bprog
+? lindep([1,2,3;4,5,6;7,8,9])
+%3 = [1, -2, 1]~
+ at eprog
+
+If $v$ contains polynomials or power series over some base field, finds a
+linear relation with coefficients in the field.
+\bprog
+? lindep([x*y, x^2 + y, x^2*y + x*y^2, 1])
+%4 = [y, y, -1, -y^2]~
+ at eprog\noindent For better control, it is preferable to use \typ{POL} rather
+than \typ{SER} in the input, otherwise one gets a linear combination which is
+$t$-adically small, but not necessarily $0$. Indeed, power series are first
+converted to the minimal absolute accuracy occurring among the entries of $v$
+(which can cause some coefficients to be ignored), then truncated to
+polynomials:
+\bprog
+? v = [t^2+O(t^4), 1+O(t^2)]; L=lindep(v)
+%1 = [1, 0]~
+? v*L
+%2 = t^2+O(t^4)  \\ small but not 0
+ at eprog
+
+The library syntax is \fun{GEN}{lindep0}{GEN v, long flag}.
+Also available are \fun{GEN}{lindep}{GEN v} (real/complex entries,
+$\fl=0$), \fun{GEN}{lindep2}{GEN v, long flag} (real/complex entries)
+\fun{GEN}{padic_lindep}{GEN v} ($p$-adic entries) and
+\fun{GEN}{Xadic_lindep}{GEN v} (polynomial entries).
+Finally \fun{GEN}{deplin}{GEN v} returns a non-zero kernel vector for a
+\typ{MAT} input.
+
+\subsec{listcreate$()$}\kbdsidx{listcreate}\label{se:listcreate}
+Creates an empty list. This routine used to have a mandatory argument,
+which is now ignored (for backward compatibility). In fact, this function
+has become redundant and obsolete; it will disappear in future versions of
+PARI: just use \kbd{List()}
+% \syn{NO}
+
+\subsec{listinsert$(L,x,n)$}\kbdsidx{listinsert}\label{se:listinsert}
+Inserts the object $x$ at
+position $n$ in $L$ (which must be of type \typ{LIST}). This has
+complexity $O(\#L - n + 1)$: all the
+remaining elements of \var{list} (from position $n+1$ onwards) are shifted
+to the right.
+
+The library syntax is \fun{GEN}{listinsert}{GEN L, GEN x, long n}.
+
+\subsec{listkill$(L)$}\kbdsidx{listkill}\label{se:listkill}
+Obsolete, retained for backward compatibility. Just use \kbd{L = List()}
+instead of \kbd{listkill(L)}. In most cases, you won't even need that, e.g.
+local variables are automatically cleared when a user function returns.
+
+The library syntax is \fun{void}{listkill}{GEN L}.
+
+\subsec{listpop$(\var{list},\{n\})$}\kbdsidx{listpop}\label{se:listpop}
+Removes the $n$-th element of the list
+\var{list} (which must be of type \typ{LIST}). If $n$ is omitted,
+or greater than the list current length, removes the last element.
+If the list is already empty, do nothing. This runs in time $O(\#L - n + 1)$.
+
+The library syntax is \fun{void}{listpop}{GEN list, long n}.
+
+\subsec{listput$(\var{list},x,\{n\})$}\kbdsidx{listput}\label{se:listput}
+Sets the $n$-th element of the list
+\var{list} (which must be of type \typ{LIST}) equal to $x$. If $n$ is omitted,
+or greater than the list length, appends $x$.
+You may put an element into an occupied cell (not changing the
+list length), but it is easier to use the standard \kbd{list[n] = x}
+construct. This runs in time $O(\#L)$ in the worst case (when the list must
+be reallocated), but in time $O(1)$ on average: any number of successive
+\kbd{listput}s run in time $O(\#L)$, where $\#L$ denotes the list
+\emph{final} length.
+
+The library syntax is \fun{GEN}{listput}{GEN list, GEN x, long n}.
+
+\subsec{listsort$(L,\{\fl=0\})$}\kbdsidx{listsort}\label{se:listsort}
+Sorts the \typ{LIST} \var{list} in place, with respect to the (somewhat
+arbitrary) universal comparison function \tet{cmp}. In particular, the
+ordering is the same as for sets and \tet{setsearch} can be used on a sorted
+list.
+\bprog
+? L = List([1,2,4,1,3,-1]); listsort(L); L
+%1 = List([-1, 1, 1, 2, 3, 4])
+? setsearch(L, 4)
+%2 = 6
+? setsearch(L, -2)
+%3 = 0
+ at eprog\noindent This is faster than the \kbd{vecsort} command since the list
+is sorted in place: no copy is made. No value returned.
+
+If $\fl$ is non-zero, suppresses all repeated coefficients.
+
+The library syntax is \fun{void}{listsort}{GEN L, long flag}.
+
+\subsec{matadjoint$(M,\{\fl=0\})$}\kbdsidx{matadjoint}\label{se:matadjoint}
+\idx{adjoint matrix} of $M$, i.e.~a matrix $N$
+of cofactors of $M$, satisfying $M*N=\det(M)*\Id$. $M$ must be a
+(non-necessarily invertible) square matrix of dimension $n$.
+If $\fl$ is 0 or omitted, we try to use Leverrier-Faddeev's algorithm,
+which assumes that $n!$ invertible. If it fails or $\fl = 1$,
+compute $T = \kbd{charpoly}(M)$ independently first and return
+$(-1)^{n-1} (T(x)-T(0))/x$ evaluated at $M$.
+\bprog
+? a = [1,2,3;3,4,5;6,7,8] * Mod(1,4);
+%2 =
+[Mod(1, 4) Mod(2, 4) Mod(3, 4)]
+
+[Mod(3, 4) Mod(0, 4) Mod(1, 4)]
+
+[Mod(2, 4) Mod(3, 4) Mod(0, 4)]
+ at eprog\noindent
+Both algorithms use $O(n^4)$ operations in the base ring, and are usually
+slower than computing the characteristic polynomial or the inverse of $M$
+directly.
+
+The library syntax is \fun{GEN}{matadjoint0}{GEN M, long flag}.
+Also available are
+\fun{GEN}{adj}{GEN x} (\fl=0) and
+\fun{GEN}{adjsafe}{GEN x} (\fl=1).
+
+\subsec{matcompanion$(x)$}\kbdsidx{matcompanion}\label{se:matcompanion}
+The left companion matrix to the non-zero polynomial $x$.
+
+The library syntax is \fun{GEN}{matcompanion}{GEN x}.
+
+\subsec{matconcat$(v)$}\kbdsidx{matconcat}\label{se:matconcat}
+Returns a \typ{MAT} built from the entries of $v$, which may
+be a \typ{VEC} (concatenate horizontally), a \typ{COL} (concatenate
+vertically), or a \typ{MAT} (concatenate vertically each column, and
+concatenate vertically the resulting matrices). The entries of $v$ are always
+considered as matrices: they can themselves be \typ{VEC} (seen as a row
+matrix), a \typ{COL} seen as a column matrix), a \typ{MAT}, or a scalar (seen
+as an $1 \times 1$ matrix).
+\bprog
+? A=[1,2;3,4]; B=[5,6]~; C=[7,8]; D=9;
+? matconcat([A, B]) \\ horizontal
+%1 =
+[1 2 5]
+
+[3 4 6]
+? matconcat([A, C]~) \\ vertical
+%2 =
+[1 2]
+
+[3 4]
+
+[7 8]
+? matconcat([A, B; C, D]) \\ block matrix
+%3 =
+[1 2 5]
+
+[3 4 6]
+
+[7 8 9]
+ at eprog\noindent
+If the dimensions of the entries to concatenate do not match up, the above
+rules are extended as follows:
+
+\item each entry $v_{i,j}$ of $v$ has a natural length and height: $1 \times
+1$ for a scalar, $1 \times n$ for a \typ{VEC} of length $n$, $n \times 1$
+for a \typ{COL}, $m \times n$ for an $m\times n$ \typ{MAT}
+
+\item let $H_i$ be the maximum over $j$ of the lengths of the $v_{i,j}$,
+let $L_j$ be the maximum over $i$ of the heights of the $v_{i,j}$.
+The dimensions of the $(i,j)$-th block in the concatenated matrix are
+$H_i \times L_j$.
+
+\item a scalar $s = v_{i,j}$ is considered as $s$ times an identity matrix
+of the block dimension $\min (H_i,L_j)$
+
+\item blocks are extended by 0 columns on the right and 0 rows at the
+bottom, as needed.
+
+\bprog
+? matconcat([1, [2,3]~, [4,5,6]~]) \\ horizontal
+%4 =
+[1 2 4]
+
+[0 3 5]
+
+[0 0 6]
+? matconcat([1, [2,3], [4,5,6]]~) \\ vertical
+%5 =
+[1 0 0]
+
+[2 3 0]
+
+[4 5 6]
+? matconcat([B, C; A, D]) \\ block matrix
+%6 =
+[5 0 7 8]
+
+[6 0 0 0]
+
+[1 2 9 0]
+
+[3 4 0 9]
+? U=[1,2;3,4]; V=[1,2,3;4,5,6;7,8,9];
+? matconcat(matdiagonal([U, V])) \\ block diagonal
+%7 =
+[1 2 0 0 0]
+
+[3 4 0 0 0]
+
+[0 0 1 2 3]
+
+[0 0 4 5 6]
+
+[0 0 7 8 9]
+ at eprog
+
+The library syntax is \fun{GEN}{matconcat}{GEN v}.
+
+\subsec{matdet$(x,\{\fl=0\})$}\kbdsidx{matdet}\label{se:matdet}
+Determinant of the square matrix $x$.
+
+If $\fl=0$, uses an appropriate algorithm depending on the coefficients:
+
+\item integer entries: modular method due to Dixon, Pernet and Stein.
+
+\item real or $p$-adic entries: classical Gaussian elimination using maximal
+pivot.
+
+\item intmod entries: classical Gaussian elimination using first non-zero
+pivot.
+
+\item other cases: Gauss-Bareiss.
+
+If $\fl=1$, uses classical Gaussian elimination with appropriate pivoting
+strategy (maximal pivot for real or $p$-adic coefficients). This is usually
+worse than the default.
+
+The library syntax is \fun{GEN}{det0}{GEN x, long flag}.
+Also available are \fun{GEN}{det}{GEN x} ($\fl=0$),
+\fun{GEN}{det2}{GEN x} ($\fl=1$) and \fun{GEN}{ZM_det}{GEN x} for integer
+entries.
+
+\subsec{matdetint$(B)$}\kbdsidx{matdetint}\label{se:matdetint}
+Let $B$ be an $m\times n$ matrix with integer coefficients. The
+\emph{determinant} $D$ of the lattice generated by the columns of $B$ is
+the square root of $\det(B^T B)$ if $B$ has maximal rank $m$, and $0$
+otherwise.
+
+This function uses the Gauss-Bareiss algorithm to compute a positive
+\emph{multiple} of $D$. When $B$ is square, the function actually returns
+$D = |\det B|$.
+
+This function is useful in conjunction with \kbd{mathnfmod}, which needs to
+know such a multiple. If the rank is maximal and the matrix non-square,
+you can obtain $D$ exactly using
+\bprog
+  matdet( mathnfmod(B, matdetint(B)) )
+ at eprog\noindent
+Note that as soon as one of the dimensions gets large ($m$ or $n$ is larger
+than 20, say), it will often be much faster to use \kbd{mathnf(B, 1)} or
+\kbd{mathnf(B, 4)} directly.
+
+The library syntax is \fun{GEN}{detint}{GEN B}.
+
+\subsec{matdiagonal$(x)$}\kbdsidx{matdiagonal}\label{se:matdiagonal}
+$x$ being a vector, creates the diagonal matrix
+whose diagonal entries are those of $x$.
+\bprog
+? matdiagonal([1,2,3]);
+%1 =
+[1 0 0]
+
+[0 2 0]
+
+[0 0 3]
+ at eprog\noindent Block diagonal matrices are easily created using
+\tet{matconcat}:
+\bprog
+? U=[1,2;3,4]; V=[1,2,3;4,5,6;7,8,9];
+? matconcat(matdiagonal([U, V]))
+%1 =
+[1 2 0 0 0]
+
+[3 4 0 0 0]
+
+[0 0 1 2 3]
+
+[0 0 4 5 6]
+
+[0 0 7 8 9]
+ at eprog
+
+The library syntax is \fun{GEN}{diagonal}{GEN x}.
+
+\subsec{mateigen$(x,\{\fl=0\})$}\kbdsidx{mateigen}\label{se:mateigen}
+Returns the (complex) eigenvectors of $x$ as columns of a matrix.
+If $\fl=1$, return $[L,H]$, where $L$ contains the
+eigenvalues and $H$ the corresponding eigenvectors; multiple eigenvalues are
+repeated according to the eigenspace dimension (which may be less
+than the eigenvalue multiplicity in the characteristic polynomial).
+
+This function first computes the characteristic polynomial of $x$ and
+approximates its complex roots $(\lambda_i)$, then tries to compute the
+eigenspaces as kernels of the $x - \lambda_i$. This algorithm is
+ill-conditioned and is likely to miss kernel vectors if some roots of the
+characteristic polynomial are close, in particular if it has multiple roots.
+\bprog
+? A = [13,2; 10,14]; mateigen(A)
+%1 =
+[-1/2 2/5]
+
+[   1   1]
+? [L,H] = mateigen(A, 1);
+? L
+%3 = [9, 18]
+? H
+%4 =
+[-1/2 2/5]
+
+[   1   1]
+ at eprog\noindent
+For symmetric matrices, use \tet{qfjacobi} instead; for Hermitian matrices,
+compute
+\bprog
+ A = real(x);
+ B = imag(x);
+ y = matconcat([A, -B; B, A]);
+ at eprog\noindent and apply \kbd{qfjacobi} to $y$.
+
+The library syntax is \fun{GEN}{mateigen}{GEN x, long flag, long prec}.
+Also available is \fun{GEN}{eigen}{GEN x, long prec} ($\fl = 0$)
+
+\subsec{matfrobenius$(M,\{\fl\},\{v='x\})$}\kbdsidx{matfrobenius}\label{se:matfrobenius}
+Returns the Frobenius form of
+the square matrix \kbd{M}. If $\fl=1$, returns only the elementary divisors as
+a vector of polynomials in the variable \kbd{v}.  If $\fl=2$, returns a
+two-components vector [F,B] where \kbd{F} is the Frobenius form and \kbd{B} is
+the basis change so that $M=B^{-1}FB$.
+
+The library syntax is \fun{GEN}{matfrobenius}{GEN M, long flag, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{mathess$(x)$}\kbdsidx{mathess}\label{se:mathess}
+Returns a matrix similar to the square matrix $x$, which is in upper Hessenberg
+form (zero entries below the first subdiagonal).
+
+The library syntax is \fun{GEN}{hess}{GEN x}.
+
+\subsec{mathilbert$(n)$}\kbdsidx{mathilbert}\label{se:mathilbert}
+$x$ being a \kbd{long}, creates the
+\idx{Hilbert matrix}of order $x$, i.e.~the matrix whose coefficient
+($i$,$j$) is $1/ (i+j-1)$.
+
+The library syntax is \fun{GEN}{mathilbert}{long n}.
+
+\subsec{mathnf$(M,\{\fl=0\})$}\kbdsidx{mathnf}\label{se:mathnf}
+Let $R$ be a Euclidean ring, equal to $\Z$ or to $K[X]$ for some field
+$K$. If $M$ is a (not necessarily square) matrix with entries in $R$, this
+routine finds the \emph{upper triangular} \idx{Hermite normal form} of $M$.
+If the rank of $M$ is equal to its number of rows, this is a square
+matrix. In general, the columns of the result form a basis of the $R$-module
+spanned by the columns of $M$.
+
+The values $0,1,2,3$ of $\fl$ have a binary meaning, analogous to the one
+in \tet{matsnf}; in this case, binary digits of $\fl$ mean:
+
+\item 1 (complete output): if set, outputs $[H,U]$, where $H$ is the Hermite
+normal form of $M$, and $U$ is a transformation matrix such that $MU=[0|H]$.
+The matrix $U$ belongs to $\text{GL}(R)$. When $M$ has a large kernel, the
+entries of $U$ are in general huge.
+
+\item 2 (generic input): \emph{Deprecated}. If set, assume that $R = K[X]$ is
+a polynomial ring; otherwise, assume that $R = \Z$. This flag is now useless
+since the routine always checks whether the matrix has integral entries.
+
+\noindent For these 4 values, we use a naive algorithm, which behaves well
+in small dimension only. Larger values correspond to different algorithms,
+are restricted to \emph{integer} matrices, and all output the unimodular
+matrix $U$. From now on all matrices have integral entries.
+
+\item $\fl=4$, returns $[H,U]$ as in ``complete output'' above, using a
+variant of \idx{LLL} reduction along the way. The matrix $U$ is provably
+small in the $L_2$ sense, and in general close to optimal; but the
+reduction is in general slow, although provably polynomial-time.
+
+If $\fl=5$, uses Batut's algorithm and output $[H,U,P]$, such that $H$ and
+$U$ are as before and $P$ is a permutation of the rows such that $P$ applied
+to $MU$ gives $H$. This is in general faster than $\fl=4$ but the matrix $U$
+is usually worse; it is heuristically smaller than with the default algorithm.
+
+When the matrix is dense and the dimension is large (bigger than 100, say),
+$\fl = 4$ will be fastest. When $M$ has maximal rank, then
+\bprog
+  H = mathnfmod(M, matdetint(M))
+ at eprog\noindent will be even faster. You can then recover $U$ as $M^{-1}H$.
+
+\bprog
+? M = matrix(3,4,i,j,random([-5,5]))
+%1 =
+[ 0 2  3  0]
+
+[-5 3 -5 -5]
+
+[ 4 3 -5  4]
+
+? [H,U] = mathnf(M, 1);
+? U
+%3 =
+[-1 0 -1 0]
+
+[ 0 5  3 2]
+
+[ 0 3  1 1]
+
+[ 1 0  0 0]
+
+? H
+%5 =
+[19 9 7]
+
+[ 0 9 1]
+
+[ 0 0 1]
+
+? M*U
+%6 =
+[0 19 9 7]
+
+[0  0 9 1]
+
+[0  0 0 1]
+ at eprog
+
+For convenience, $M$ is allowed to be a \typ{VEC}, which is then
+automatically converted to a \typ{MAT}, as per the \tet{Mat} function.
+For instance to solve the generalized extended gcd problem, one may use
+\bprog
+? v = [116085838, 181081878, 314252913,10346840];
+? [H,U] = mathnf(v, 1);
+? U
+%2 =
+[ 103 -603    15  -88]
+
+[-146   13 -1208  352]
+
+[  58  220   678 -167]
+
+[-362 -144   381 -101]
+? v*U
+%3 = [0, 0, 0, 1]
+ at eprog\noindent This also allows to input a matrix as a \typ{VEC} of
+\typ{COL}s of the same length (which \kbd{Mat} would concatenate to
+the \typ{MAT} having those columns):
+\bprog
+? v = [[1,0,4]~, [3,3,4]~, [0,-4,-5]~]; mathnf(v)
+%1 =
+[47 32 12]
+
+[ 0  1  0]
+
+[ 0  0  1]
+ at eprog
+
+The library syntax is \fun{GEN}{mathnf0}{GEN M, long flag}.
+Also available are \fun{GEN}{hnf}{GEN M} ($\fl=0$) and
+\fun{GEN}{hnfall}{GEN M} ($\fl=1$). To reduce \emph{huge} relation matrices
+(sparse with small entries, say dimension $400$ or more), you can use the
+pair \kbd{hnfspec} / \kbd{hnfadd}. Since this is quite technical and the
+calling interface may change, they are not documented yet. Look at the code
+in \kbd{basemath/hnf\_snf.c}.
+
+\subsec{mathnfmod$(x,d)$}\kbdsidx{mathnfmod}\label{se:mathnfmod}
+If $x$ is a (not necessarily square) matrix of
+maximal rank with integer entries, and $d$ is a multiple of the (non-zero)
+determinant of the lattice spanned by the columns of $x$, finds the
+\emph{upper triangular} \idx{Hermite normal form} of $x$.
+
+If the rank of $x$ is equal to its number of rows, the result is a square
+matrix. In general, the columns of the result form a basis of the lattice
+spanned by the columns of $x$. Even when $d$ is known, this is in general
+slower than \kbd{mathnf} but uses much less memory.
+
+The library syntax is \fun{GEN}{hnfmod}{GEN x, GEN d}.
+
+\subsec{mathnfmodid$(x,d)$}\kbdsidx{mathnfmodid}\label{se:mathnfmodid}
+Outputs the (upper triangular)
+\idx{Hermite normal form} of $x$ concatenated with the diagonal
+matrix with diagonal $d$. Assumes that $x$ has integer entries.
+Variant: if $d$ is an integer instead of a vector, concatenate $d$ times the
+identity matrix.
+\bprog
+? m=[0,7;-1,0;-1,-1]
+%1 =
+[ 0  7]
+
+[-1  0]
+
+[-1 -1]
+? mathnfmodid(m, [6,2,2])
+%2 =
+[2 1 1]
+
+[0 1 0]
+
+[0 0 1]
+? mathnfmodid(m, 10)
+%3 =
+[10 7 3]
+
+[ 0 1 0]
+
+[ 0 0 1]
+ at eprog
+
+The library syntax is \fun{GEN}{hnfmodid}{GEN x, GEN d}.
+
+\subsec{mathouseholder$(Q,v)$}\kbdsidx{mathouseholder}\label{se:mathouseholder}
+\sidx{Householder transform}applies a sequence $Q$ of Householder
+transforms, as returned by \kbd{matqr}$(M,1)$ to the vector or matrix $v$.
+
+The library syntax is \fun{GEN}{mathouseholder}{GEN Q, GEN v}.
+
+\subsec{matid$(n)$}\kbdsidx{matid}\label{se:matid}
+Creates the $n\times n$ identity matrix.
+
+The library syntax is \fun{GEN}{matid}{long n}.
+
+\subsec{matimage$(x,\{\fl=0\})$}\kbdsidx{matimage}\label{se:matimage}
+Gives a basis for the image of the
+matrix $x$ as columns of a matrix. A priori the matrix can have entries of
+any type. If $\fl=0$, use standard Gauss pivot. If $\fl=1$, use
+\kbd{matsupplement} (much slower: keep the default flag!).
+
+The library syntax is \fun{GEN}{matimage0}{GEN x, long flag}.
+Also available is \fun{GEN}{image}{GEN x} ($\fl=0$).
+
+\subsec{matimagecompl$(x)$}\kbdsidx{matimagecompl}\label{se:matimagecompl}
+Gives the vector of the column indices which
+are not extracted by the function \kbd{matimage}, as a permutation
+(\typ{VECSMALL}). Hence the number of
+components of \kbd{matimagecompl(x)} plus the number of columns of
+\kbd{matimage(x)} is equal to the number of columns of the matrix $x$.
+
+The library syntax is \fun{GEN}{imagecompl}{GEN x}.
+
+\subsec{matindexrank$(x)$}\kbdsidx{matindexrank}\label{se:matindexrank}
+$x$ being a matrix of rank $r$, returns a vector with two
+\typ{VECSMALL} components $y$ and $z$ of length $r$ giving a list of rows
+and columns respectively (starting from 1) such that the extracted matrix
+obtained from these two vectors using $\tet{vecextract}(x,y,z)$ is
+invertible.
+
+The library syntax is \fun{GEN}{indexrank}{GEN x}.
+
+\subsec{matintersect$(x,y)$}\kbdsidx{matintersect}\label{se:matintersect}
+$x$ and $y$ being two matrices with the same
+number of rows each of whose columns are independent, finds a basis of the
+$\Q$-vector space equal to the intersection of the spaces spanned by the
+columns of $x$ and $y$ respectively. The faster function
+\tet{idealintersect} can be used to intersect fractional ideals (projective
+$\Z_K$ modules of rank $1$); the slower but much more general function
+\tet{nfhnf} can be used to intersect general $\Z_K$-modules.
+
+The library syntax is \fun{GEN}{intersect}{GEN x, GEN y}.
+
+\subsec{matinverseimage$(x,y)$}\kbdsidx{matinverseimage}\label{se:matinverseimage}
+Given a matrix $x$ and
+a column vector or matrix $y$, returns a preimage $z$ of $y$ by $x$ if one
+exists (i.e such that $x z = y$), an empty vector or matrix otherwise. The
+complete inverse image is $z + \text{Ker} x$, where a basis of the kernel of
+$x$ may be obtained by \kbd{matker}.
+\bprog
+? M = [1,2;2,4];
+? matinverseimage(M, [1,2]~)
+%2 = [1, 0]~
+? matinverseimage(M, [3,4]~)
+%3 = []~    \\@com no solution
+? matinverseimage(M, [1,3,6;2,6,12])
+%4 =
+[1 3 6]
+
+[0 0 0]
+? matinverseimage(M, [1,2;3,4])
+%5 = [;]    \\@com no solution
+? K = matker(M)
+%6 =
+[-2]
+
+[1]
+ at eprog
+
+The library syntax is \fun{GEN}{inverseimage}{GEN x, GEN y}.
+
+\subsec{matisdiagonal$(x)$}\kbdsidx{matisdiagonal}\label{se:matisdiagonal}
+Returns true (1) if $x$ is a diagonal matrix, false (0) if not.
+
+The library syntax is \fun{GEN}{isdiagonal}{GEN x}.
+
+\subsec{matker$(x,\{\fl=0\})$}\kbdsidx{matker}\label{se:matker}
+Gives a basis for the kernel of the matrix $x$ as columns of a matrix.
+The matrix can have entries of any type, provided they are compatible with
+the generic arithmetic operations ($+$, $\times$ and $/$).
+
+If $x$ is known to have integral entries, set $\fl=1$.
+
+The library syntax is \fun{GEN}{matker0}{GEN x, long flag}.
+Also available are \fun{GEN}{ker}{GEN x} ($\fl=0$),
+\fun{GEN}{keri}{GEN x} ($\fl=1$).
+
+\subsec{matkerint$(x,\{\fl=0\})$}\kbdsidx{matkerint}\label{se:matkerint}
+Gives an \idx{LLL}-reduced $\Z$-basis
+for the lattice equal to the kernel of the matrix $x$ as columns of the
+matrix $x$ with integer entries (rational entries are not permitted).
+
+If $\fl=0$, uses an integer LLL algorithm.
+
+If $\fl=1$, uses $\kbd{matrixqz}(x,-2)$. Many orders of magnitude slower
+than the default: never use this.
+
+The library syntax is \fun{GEN}{matkerint0}{GEN x, long flag}.
+See also \fun{GEN}{kerint}{GEN x} ($\fl=0$), which is a trivial
+wrapper around
+\bprog
+ZM_lll(ZM_lll(x, 0.99, LLL_KER), 0.99, LLL_INPLACE);
+ at eprog\noindent Remove the outermost \kbd{ZM\_lll} if LLL-reduction is not
+desired (saves time).
+
+\subsec{matmuldiagonal$(x,d)$}\kbdsidx{matmuldiagonal}\label{se:matmuldiagonal}
+Product of the matrix $x$ by the diagonal
+matrix whose diagonal entries are those of the vector $d$. Equivalent to,
+but much faster than $x*\kbd{matdiagonal}(d)$.
+
+The library syntax is \fun{GEN}{matmuldiagonal}{GEN x, GEN d}.
+
+\subsec{matmultodiagonal$(x,y)$}\kbdsidx{matmultodiagonal}\label{se:matmultodiagonal}
+Product of the matrices $x$ and $y$ assuming that the result is a
+diagonal matrix. Much faster than $x*y$ in that case. The result is
+undefined if $x*y$ is not diagonal.
+
+The library syntax is \fun{GEN}{matmultodiagonal}{GEN x, GEN y}.
+
+\subsec{matpascal$(n,\{q\})$}\kbdsidx{matpascal}\label{se:matpascal}
+Creates as a matrix the lower triangular
+\idx{Pascal triangle} of order $x+1$ (i.e.~with binomial coefficients
+up to $x$). If $q$ is given, compute the $q$-Pascal triangle (i.e.~using
+$q$-binomial coefficients).
+
+The library syntax is \fun{GEN}{matqpascal}{long n, GEN q = NULL}.
+Also available is \fun{GEN}{matpascal}{GEN x}.
+
+\subsec{matqr$(M,\{\fl=0\})$}\kbdsidx{matqr}\label{se:matqr}
+Returns $[Q,R]$, the \idx{QR-decomposition} of the square invertible
+matrix $M$ with real entries: $Q$ is orthogonal and $R$ upper triangular. If
+$\fl=1$, the orthogonal matrix is returned as a sequence of Householder
+transforms: applying such a sequence is stabler and faster than
+multiplication by the corresponding $Q$ matrix.\sidx{Householder transform}
+More precisely, if
+\bprog
+  [Q,R] = matqr(M);
+  [q,r] = matqr(M, 1);
+ at eprog\noindent then $r = R$ and \kbd{mathouseholder}$(q, M)$ is $R$;
+furthermore
+\bprog
+  mathouseholder(q, matid(#M)) == Q~
+ at eprog\noindent the inverse of $Q$. This function raises an error if the
+precision is too low or $x$ is singular.
+
+The library syntax is \fun{GEN}{matqr}{GEN M, long flag, long prec}.
+
+\subsec{matrank$(x)$}\kbdsidx{matrank}\label{se:matrank}
+Rank of the matrix $x$.
+
+The library syntax is \fun{long}{rank}{GEN x}.
+
+\subsec{matrix$(m,n,\{X\},\{Y\},\{\var{expr}=0\})$}\kbdsidx{matrix}\label{se:matrix}
+Creation of the
+$m\times n$ matrix whose coefficients are given by the expression
+\var{expr}. There are two formal parameters in \var{expr}, the first one
+($X$) corresponding to the rows, the second ($Y$) to the columns, and $X$
+goes from 1 to $m$, $Y$ goes from 1 to $n$. If one of the last 3 parameters
+is omitted, fill the matrix with zeroes.
+%\syn{NO}
+
+\subsec{matrixqz$(A,\{p=0\})$}\kbdsidx{matrixqz}\label{se:matrixqz}
+$A$ being an $m\times n$ matrix in $M_{m,n}(\Q)$, let
+$\text{Im}_\Q A$ (resp.~$\text{Im}_\Z A$) the $\Q$-vector space
+(resp.~the $\Z$-module) spanned by the columns of $A$. This function has
+varying behavior depending on the sign of $p$:
+
+If $p \geq 0$, $A$ is assumed to have maximal rank $n\leq m$. The function
+returns a matrix $B\in M_{m,n}(\Z)$, with $\text{Im}_\Q B = \text{Im}_\Q A$,
+such that the GCD of all its $n\times n$ minors is coprime to
+$p$; in particular, if $p = 0$ (default), this GCD is $1$.
+\bprog
+? minors(x) = vector(#x[,1], i, matdet(x[^i,]));
+? A = [3,1/7; 5,3/7; 7,5/7]; minors(A)
+%1 = [4/7, 8/7, 4/7]   \\ determinants of all 2x2 minors
+? B = matrixqz(A)
+%2 =
+[3 1]
+
+[5 2]
+
+[7 3]
+? minors(%)
+%3 = [1, 2, 1]   \\ B integral with coprime minors
+ at eprog
+
+If $p=-1$, returns the HNF basis of the lattice $\Z^n \cap \text{Im}_\Z A$.
+
+If $p=-2$, returns the HNF basis of the lattice $\Z^n \cap \text{Im}_\Q A$.
+\bprog
+? matrixqz(A,-1)
+%4 =
+[8 5]
+
+[4 3]
+
+[0 1]
+
+? matrixqz(A,-2)
+%5 =
+[2 -1]
+
+[1 0]
+
+[0 1]
+ at eprog
+
+The library syntax is \fun{GEN}{matrixqz0}{GEN A, GEN p = NULL}.
+
+\subsec{matsize$(x)$}\kbdsidx{matsize}\label{se:matsize}
+$x$ being a vector or matrix, returns a row vector
+with two components, the first being the number of rows (1 for a row vector),
+the second the number of columns (1 for a column vector).
+
+The library syntax is \fun{GEN}{matsize}{GEN x}.
+
+\subsec{matsnf$(X,\{\fl=0\})$}\kbdsidx{matsnf}\label{se:matsnf}
+If $X$ is a (singular or non-singular) matrix outputs the vector of
+\idx{elementary divisors} of $X$, i.e.~the diagonal of the
+\idx{Smith normal form} of $X$, normalized so that $d_n \mid d_{n-1} \mid
+\ldots \mid d_1$.
+
+The binary digits of \fl\ mean:
+
+1 (complete output): if set, outputs $[U,V,D]$, where $U$ and $V$ are two
+unimodular matrices such that $UXV$ is the diagonal matrix $D$. Otherwise
+output only the diagonal of $D$. If $X$ is not a square matrix, then $D$
+will be a square diagonal matrix padded with zeros on the left or the top.
+
+2 (generic input): if set, allows polynomial entries, in which case the
+input matrix must be square. Otherwise, assume that $X$ has integer
+coefficients with arbitrary shape.
+
+4 (cleanup): if set, cleans up the output. This means that elementary
+divisors equal to $1$ will be deleted, i.e.~outputs a shortened vector $D'$
+instead of $D$. If complete output was required, returns $[U',V',D']$ so
+that $U'XV' = D'$ holds. If this flag is set, $X$ is allowed to be of the
+form `vector of elementary divisors' or $[U,V,D]$ as would normally be output with the cleanup flag
+unset.
+
+The library syntax is \fun{GEN}{matsnf0}{GEN X, long flag}.
+
+\subsec{matsolve$(M,B)$}\kbdsidx{matsolve}\label{se:matsolve}
+$M$ being an invertible matrix and $B$ a column
+vector, finds the solution $X$ of $MX=B$, using Dixon $p$-adic lifting method
+if $M$ and $B$ are integral and Gaussian elimination otherwise. This
+has the same effect as, but is faster, than $M^{-1}*B$.
+
+The library syntax is \fun{GEN}{gauss}{GEN M, GEN B}.
+For integral input, the function
+\fun{GEN}{ZM_gauss}{GEN M,GEN B} is also available.
+
+\subsec{matsolvemod$(M,D,B,\{\fl=0\})$}\kbdsidx{matsolvemod}\label{se:matsolvemod}
+$M$ being any integral matrix,
+$D$ a column vector of non-negative integer moduli, and $B$ an integral
+column vector, gives a small integer solution to the system of congruences
+$\sum_i m_{i,j}x_j\equiv b_i\pmod{d_i}$ if one exists, otherwise returns
+zero. Shorthand notation: $B$ (resp.~$D$) can be given as a single integer,
+in which case all the $b_i$ (resp.~$d_i$) above are taken to be equal to $B$
+(resp.~$D$).
+\bprog
+? M = [1,2;3,4];
+? matsolvemod(M, [3,4]~, [1,2]~)
+%2 = [-2, 0]~
+? matsolvemod(M, 3, 1) \\ M X = [1,1]~ over F_3
+%3 = [-1, 1]~
+? matsolvemod(M, [3,0]~, [1,2]~) \\ x + 2y = 1 (mod 3), 3x + 4y = 2 (in Z)
+%4 = [6, -4]~
+ at eprog
+If $\fl=1$, all solutions are returned in the form of a two-component row
+vector $[x,u]$, where $x$ is a small integer solution to the system of
+congruences and $u$ is a matrix whose columns give a basis of the homogeneous
+system (so that all solutions can be obtained by adding $x$ to any linear
+combination of columns of $u$). If no solution exists, returns zero.
+
+The library syntax is \fun{GEN}{matsolvemod0}{GEN M, GEN D, GEN B, long flag}.
+Also available are \fun{GEN}{gaussmodulo}{GEN M, GEN D, GEN B}
+($\fl=0$) and \fun{GEN}{gaussmodulo2}{GEN M, GEN D, GEN B} ($\fl=1$).
+
+\subsec{matsupplement$(x)$}\kbdsidx{matsupplement}\label{se:matsupplement}
+Assuming that the columns of the matrix $x$
+are linearly independent (if they are not, an error message is issued), finds
+a square invertible matrix whose first columns are the columns of $x$,
+i.e.~supplement the columns of $x$ to a basis of the whole space.
+\bprog
+? matsupplement([1;2])
+%1 =
+[1 0]
+
+[2 1]
+ at eprog
+Raises an error if $x$ has 0 columns, since (due to a long standing design
+bug), the dimension of the ambient space (the number of rows) is unknown in
+this case:
+\bprog
+? matsupplement(matrix(2,0))
+  ***   at top-level: matsupplement(matrix
+  ***                 ^--------------------
+  *** matsupplement: sorry, suppl [empty matrix] is not yet implemented.
+ at eprog
+
+The library syntax is \fun{GEN}{suppl}{GEN x}.
+
+\subsec{mattranspose$(x)$}\kbdsidx{mattranspose}\label{se:mattranspose}
+Transpose of $x$ (also $x\til$).
+This has an effect only on vectors and matrices.
+
+The library syntax is \fun{GEN}{gtrans}{GEN x}.
+
+\subsec{minpoly$(A,\{v='x\})$}\kbdsidx{minpoly}\label{se:minpoly}
+\idx{minimal polynomial}
+of $A$ with respect to the variable $v$., i.e. the monic polynomial $P$
+of minimal degree (in the variable $v$) such that $P(A) = 0$.
+
+The library syntax is \fun{GEN}{minpoly}{GEN A, long v = -1}, where \kbd{v} is a variable number.
+
+\subsec{norml2$(x)$}\kbdsidx{norml2}\label{se:norml2}
+Square of the $L^2$-norm of $x$. More precisely,
+if $x$ is a scalar, $\kbd{norml2}(x)$ is defined to be the square
+of the complex modulus of $x$ (real \typ{QUAD}s are not supported).
+If $x$ is a polynomial, a (row or column) vector or a matrix, \kbd{norml2($x$)} is
+defined recursively as $\sum_i \kbd{norml2}(x_i)$, where $(x_i)$ run through
+the components of $x$. In particular, this yields the usual $\sum |x_i|^2$
+(resp.~$\sum |x_{i,j}|^2$) if $x$ is a polynomial or vector (resp.~matrix) with
+complex components.
+
+\bprog
+? norml2( [ 1, 2, 3 ] )      \\ vector
+%1 = 14
+? norml2( [ 1, 2; 3, 4] )   \\ matrix
+%2 = 30
+? norml2( 2*I + x )
+%3 = 5
+? norml2( [ [1,2], [3,4], 5, 6 ] )   \\ recursively defined
+%4 = 91
+ at eprog
+
+The library syntax is \fun{GEN}{gnorml2}{GEN x}.
+
+\subsec{normlp$(x,\{p\})$}\kbdsidx{normlp}\label{se:normlp}
+$L^p$-norm of $x$; sup norm if $p$ is omitted. More precisely,
+if $x$ is a scalar, \kbd{normlp}$(x, p)$ is defined to be \kbd{abs}$(x)$.
+If $x$ is a polynomial, a (row or column) vector or a matrix:
+
+\item  if $p$ is omitted, \kbd{normlp($x$)} is defined recursively as
+$\max_i \kbd{normlp}(x_i))$, where $(x_i)$ run through the components of~$x$.
+In particular, this yields the usual sup norm if $x$ is a polynomial or
+vector with complex components.
+
+\item otherwise, \kbd{normlp($x$, $p$)} is defined recursively as $(\sum_i
+\kbd{normlp}^p(x_i,p))^{1/p}$. In particular, this yields the usual $(\sum
+|x_i|^p)^{1/p}$ if $x$ is a polynomial or vector with complex components.
+
+\bprog
+? v = [1,-2,3]; normlp(v)      \\ vector
+%1 = 3
+? M = [1,-2;-3,4]; normlp(M)   \\ matrix
+%2 = 4
+? T = (1+I) + I*x^2; normlp(T)
+%3 = 1.4142135623730950488016887242096980786
+? normlp([[1,2], [3,4], 5, 6])   \\ recursively defined
+%4 = 6
+
+? normlp(v, 1)
+%5 = 6
+? normlp(M, 1)
+%6 = 10
+? normlp(T, 1)
+%7 = 2.4142135623730950488016887242096980786
+ at eprog
+
+The library syntax is \fun{GEN}{gnormlp}{GEN x, GEN p = NULL, long prec}.
+
+\subsec{qfauto$(G,\{\var{fl}\})$}\kbdsidx{qfauto}\label{se:qfauto}
+$G$ being a square and symmetric matrix with integer entries representing a
+positive definite quadratic form, outputs the automorphism group of the
+associate lattice.
+Since this requires computing the minimal vectors, the computations can
+become very lengthy as the dimension grows. $G$ can also be given by an
+\kbd{qfisominit} structure.
+See \kbd{qfisominit} for the meaning of \var{fl}.
+
+The output is a two-components vector $[o,g]$ where $o$ is the group order
+and $g$ is the list of generators (as a vector). For each generator $H$,
+the equality $G={^t}H\*G\*H$ holds.
+
+The interface of this function is experimental and will likely change in the
+future.
+
+This function implements an algorithm of Plesken and Souvignier, following
+Souvignier's implementation.
+
+The library syntax is \fun{GEN}{qfauto0}{GEN G, GEN fl = NULL}.
+Also available is \fun{GEN}{qfauto}{GEN G, GEN fl}
+where $G$ is a vector of \kbd{zm}.
+
+\subsec{qfautoexport$(\var{qfa},\{\fl\})$}\kbdsidx{qfautoexport}\label{se:qfautoexport}
+\var{qfa} being an automorphism group as output by
+\tet{qfauto}, export the underlying matrix group as a string suitable
+for (no flags or $\fl=0$) GAP or ($\fl=1$) Magma. The following example
+computes the size of the matrix group using GAP:
+\bprog
+? G = qfauto([2,1;1,2])
+%1 = [12, [[-1, 0; 0, -1], [0, -1; 1, 1], [1, 1; 0, -1]]]
+? s = qfautoexport(G)
+%2 = "Group([[-1, 0], [0, -1]], [[0, -1], [1, 1]], [[1, 1], [0, -1]])"
+? extern("echo \"Order("s");\" | gap -q")
+%3 = 12
+ at eprog
+
+The library syntax is \fun{GEN}{qfautoexport}{GEN qfa, long flag}.
+
+\subsec{qfbil$(x,y,\{q\})$}\kbdsidx{qfbil}\label{se:qfbil}
+Evaluate the bilinear form $q$ (symmetric matrix)
+at the vectors $(x,y)$; if $q$ omitted, use the standard Euclidean scalar
+product, corresponding to the identity matrix.
+
+Roughly equivalent to \kbd{x\til * q * y}, but a little faster and
+more convenient (does not distinguish between column and row vectors):
+\bprog
+? x = [1,2,3]~; y = [-1,0,1]~; qfbil(x,y)
+%1 = 2
+? q = [1,2,3;2,2,-1;3,-1,0]; qfbil(x,y, q)
+%2 = -13
+? for(i=1,10^6, qfbil(x,y,q))
+%3 = 568ms
+? for(i=1,10^6, x~*q*y)
+%4 = 717ms
+ at eprog\noindent The associated quadratic form is also available, as
+\tet{qfnorm}, slightly faster:
+\bprog
+? for(i=1,10^6, qfnorm(x,q))
+time = 444ms
+? for(i=1,10^6, qfnorm(x))
+time = 176 ms.
+? for(i=1,10^6, qfbil(x,y))
+time = 208 ms.
+ at eprog
+
+The library syntax is \fun{GEN}{qfbil}{GEN x, GEN y, GEN q = NULL}.
+
+\subsec{qfgaussred$(q)$}\kbdsidx{qfgaussred}\label{se:qfgaussred}
+\idx{decomposition into squares} of the
+quadratic form represented by the symmetric matrix $q$. The result is a
+matrix whose diagonal entries are the coefficients of the squares, and the
+off-diagonal entries on each line represent the bilinear forms. More
+precisely, if $(a_{ij})$ denotes the output, one has
+$$ q(x) = \sum_i a_{ii} (x_i + \sum_{j \neq i} a_{ij} x_j)^2 $$
+\bprog
+? qfgaussred([0,1;1,0])
+%1 =
+[1/2 1]
+
+[-1 -1/2]
+ at eprog\noindent This means that $2xy = (1/2)(x+y)^2 - (1/2)(x-y)^2$.
+
+The library syntax is \fun{GEN}{qfgaussred}{GEN q}.
+\fun{GEN}{qfgaussred_positive}{GEN q} assumes that $q$ is
+ positive definite and is a little faster; returns \kbd{NULL} if a vector
+ with negative norm occurs (non positive matrix or too many rounding errors).
+
+\subsec{qfisom$(G,H,\{\var{fl}\})$}\kbdsidx{qfisom}\label{se:qfisom}
+$G$, $H$ being square and symmetric matrices with integer entries representing
+positive definite quadratic forms, return an invertible matrix $S$ such that
+$G={^t}S\*H\*S$. This defines a isomorphism between the corresponding lattices.
+Since this requires computing the minimal vectors, the computations can
+become very lengthy as the dimension grows.
+See \kbd{qfisominit} for the meaning of \var{fl}.
+
+$G$ can also be given by an \kbd{qfisominit} structure which is preferable if
+several forms $H$ need to be compared to $G$.
+
+This function implements an algorithm of Plesken and Souvignier, following
+Souvignier's implementation.
+
+The library syntax is \fun{GEN}{qfisom0}{GEN G, GEN H, GEN fl = NULL}.
+Also available is \fun{GEN}{qfisom}{GEN G, GEN H, GEN fl}
+where $G$ is a vector of \kbd{zm}, and $H$ is a \kbd{zm}.
+
+\subsec{qfisominit$(G,\{\var{fl}\})$}\kbdsidx{qfisominit}\label{se:qfisominit}
+$G$ being a square and symmetric matrix with integer entries representing a
+positive definite quadratic form, return an \kbd{isom} structure allowing to
+compute isomorphisms between $G$ and other quadratic forms faster.
+
+The interface of this function is experimental and will likely change in future
+release.
+
+If present, the optional parameter \var{fl} must be a \typ{VEC} with two
+components. It allows to specify the invariants used, which can make the
+computation faster or slower. The components are
+
+\item \kbd{fl[1]} Depth of scalar product combination to use.
+
+\item \kbd{fl[2]} Maximum level of Bacher polynomials to use.
+
+Since this function computes the minimal vectors, it can become very lengthy
+as the dimension of $G$ grows.
+
+The library syntax is \fun{GEN}{qfisominit0}{GEN G, GEN fl = NULL}.
+Also available is
+\fun{GEN}{qfisominit}{GEN F, GEN fl}
+where $F$ is a vector of \kbd{zm}.
+
+\subsec{qfjacobi$(A)$}\kbdsidx{qfjacobi}\label{se:qfjacobi}
+Apply Jacobi's eigenvalue algorithm to the real symmetric matrix $A$.
+This returns $[L, V]$, where
+
+\item $L$ is the vector of (real) eigenvalues of $A$, sorted in increasing
+order,
+
+\item $V$ is the corresponding orthogonal matrix of eigenvectors of $A$.
+
+\bprog
+? \p19
+? A = [1,2;2,1]; mateigen(A)
+%1 =
+[-1 1]
+
+[ 1 1]
+? [L, H] = qfjacobi(A);
+? L
+%3 = [-1.000000000000000000, 3.000000000000000000]~
+? H
+%4 =
+[ 0.7071067811865475245 0.7071067811865475244]
+
+[-0.7071067811865475244 0.7071067811865475245]
+? norml2( (A-L[1])*H[,1] )       \\ approximate eigenvector
+%5 = 9.403954806578300064 E-38
+? norml2(H*H~ - 1)
+%6 = 2.350988701644575016 E-38   \\ close to orthogonal
+ at eprog
+
+The library syntax is \fun{GEN}{jacobi}{GEN A, long prec}.
+
+\subsec{qflll$(x,\{\fl=0\})$}\kbdsidx{qflll}\label{se:qflll}
+\idx{LLL} algorithm applied to the
+\emph{columns} of the matrix $x$. The columns of $x$ may be linearly
+dependent. The result is a unimodular transformation matrix $T$ such that $x
+\cdot T$ is an LLL-reduced basis of the lattice generated by the column
+vectors of $x$. Note that if $x$ is not of maximal rank $T$ will not be
+square. The LLL parameters are $(0.51,0.99)$, meaning that the Gram-Schmidt
+coefficients for the final basis satisfy $\mu_{i,j} \leq |0.51|$, and the
+Lov\'{a}sz's constant is $0.99$.
+
+If $\fl=0$ (default), assume that $x$ has either exact (integral or
+rational) or real floating point entries. The matrix is rescaled, converted
+to integers and the behavior is then as in $\fl = 1$.
+
+If $\fl=1$, assume that $x$ is integral. Computations involving Gram-Schmidt
+vectors are approximate, with precision varying as needed (Lehmer's trick,
+as generalized by Schnorr). Adapted from Nguyen and Stehl\'e's algorithm
+and Stehl\'e's code (\kbd{fplll-1.3}).
+
+If $\fl=2$, $x$ should be an integer matrix whose columns are linearly
+independent. Returns a partially reduced basis for $x$, using an unpublished
+algorithm by Peter Montgomery: a basis is said to be \emph{partially reduced}
+if $|v_i \pm v_j| \geq |v_i|$ for any two distinct basis vectors $v_i, \,
+v_j$.
+
+This is faster than $\fl=1$, esp. when one row is huge compared
+to the other rows (knapsack-style), and should quickly produce relatively
+short vectors. The resulting basis is \emph{not} LLL-reduced in general.
+If LLL reduction is eventually desired, avoid this partial reduction:
+applying LLL to the partially reduced matrix is significantly \emph{slower}
+than starting from a knapsack-type lattice.
+
+If $\fl=4$, as $\fl=1$, returning a vector $[K, T]$ of matrices: the
+columns of $K$ represent a basis of the integer kernel of $x$
+(not LLL-reduced in general) and $T$ is the transformation
+matrix such that $x\cdot T$ is an LLL-reduced $\Z$-basis of the image
+of the matrix $x$.
+
+If $\fl=5$, case as case $4$, but $x$ may have polynomial coefficients.
+
+If $\fl=8$, same as case $0$, but $x$ may have polynomial coefficients.
+
+The library syntax is \fun{GEN}{qflll0}{GEN x, long flag}.
+Also available are \fun{GEN}{lll}{GEN x} ($\fl=0$),
+\fun{GEN}{lllint}{GEN x} ($\fl=1$), and \fun{GEN}{lllkerim}{GEN x} ($\fl=4$).
+
+\subsec{qflllgram$(G,\{\fl=0\})$}\kbdsidx{qflllgram}\label{se:qflllgram}
+Same as \kbd{qflll}, except that the
+matrix $G = \kbd{x\til * x}$ is the Gram matrix of some lattice vectors $x$,
+and not the coordinates of the vectors themselves. In particular, $G$ must
+now be a square symmetric real matrix, corresponding to a positive
+quadratic form (not necessarily definite: $x$ needs not have maximal rank).
+The result is a unimodular
+transformation matrix $T$ such that $x \cdot T$ is an LLL-reduced basis of
+the lattice generated by the column vectors of $x$. See \tet{qflll} for
+further details about the LLL implementation.
+
+If $\fl=0$ (default), assume that $G$ has either exact (integral or
+rational) or real floating point entries. The matrix is rescaled, converted
+to integers and the behavior is then as in $\fl = 1$.
+
+If $\fl=1$, assume that $G$ is integral. Computations involving Gram-Schmidt
+vectors are approximate, with precision varying as needed (Lehmer's trick,
+as generalized by Schnorr). Adapted from Nguyen and Stehl\'e's algorithm
+and Stehl\'e's code (\kbd{fplll-1.3}).
+
+$\fl=4$: $G$ has integer entries, gives the kernel and reduced image of $x$.
+
+$\fl=5$: same as $4$, but $G$ may have polynomial coefficients.
+
+The library syntax is \fun{GEN}{qflllgram0}{GEN G, long flag}.
+Also available are \fun{GEN}{lllgram}{GEN G} ($\fl=0$),
+\fun{GEN}{lllgramint}{GEN G} ($\fl=1$), and \fun{GEN}{lllgramkerim}{GEN G}
+($\fl=4$).
+
+\subsec{qfminim$(x,\{b\},\{m\},\{\fl=0\})$}\kbdsidx{qfminim}\label{se:qfminim}
+$x$ being a square and symmetric matrix representing a positive definite
+quadratic form, this function deals with the vectors of $x$ whose norm is
+less than or equal to $b$, enumerated using the Fincke-Pohst algorithm,
+storing at most $m$ vectors (no limit if $m$ is omitted). The function
+searches for the minimal non-zero vectors if $b$ is omitted. The behavior is
+undefined if $x$ is not positive definite (a ``precision too low'' error is
+most likely, although more precise error messages are possible). The precise
+behavior depends on $\fl$.
+
+If $\fl=0$ (default), seeks at most $2m$ vectors. The result is a
+three-component vector, the first component being the number of vectors
+found, the second being the maximum norm found, and the last vector is a
+matrix whose columns are the vectors found, only one being given for each
+pair $\pm v$ (at most $m$ such pairs, unless $m$ was omitted). The vectors
+are returned in no particular order.
+
+If $\fl=1$, ignores $m$ and returns $[N,v]$, where $v$ is a non-zero vector
+of length $N \leq b$, or $[]$ if no non-zero vector has length $\leq b$.
+If no explicit $b$ is provided, return a vector of smallish norm
+(smallest vector in an LLL-reduced basis).
+
+In these two cases, $x$ must have \emph{integral} entries. The
+implementation uses low precision floating point computations for maximal
+speed, which gives incorrect result when $x$ has large entries. (The
+condition is checked in the code and the routine raises an error if
+large rounding errors occur.) A more robust, but much slower,
+implementation is chosen if the following flag is used:
+
+If $\fl=2$, $x$ can have non integral real entries. In this case, if $b$
+is omitted, the ``minimal'' vectors only have approximately the same norm.
+If $b$ is omitted, $m$ is an upper bound for the number of vectors that
+will be stored and returned, but all minimal vectors are nevertheless
+enumerated. If $m$ is omitted, all vectors found are stored and returned;
+note that this may be a huge vector!
+
+\bprog
+? x = matid(2);
+? qfminim(x)  \\@com 4 minimal vectors of norm 1: $\pm[0,1]$, $\pm[1,0]$
+%2 = [4, 1, [0, 1; 1, 0]]
+? { x =
+[4, 2, 0, 0, 0,-2, 0, 0, 0, 0, 0, 0, 1,-1, 0, 0, 0, 1, 0,-1, 0, 0, 0,-2;
+ 2, 4,-2,-2, 0,-2, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0,-1, 0, 1,-1,-1;
+ 0,-2, 4, 0,-2, 0, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1, 0, 0, 1,-1,-1, 0, 0;
+ 0,-2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1,-1, 0, 0, 0, 1,-1, 0, 1,-1, 1, 0;
+ 0, 0,-2, 0, 4, 0, 0, 0, 1,-1, 0, 0, 1, 0, 0, 0,-2, 0, 0,-1, 1, 1, 0, 0;
+-2, -2,0, 0, 0, 4,-2, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,-1, 1, 1;
+ 0, 0, 0, 0, 0,-2, 4,-2, 0, 0, 0, 0, 0, 1, 0, 0, 0,-1, 0, 0, 0, 1,-1, 0;
+ 0, 0, 0, 0, 0, 0,-2, 4, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0,-1,-1,-1, 0, 1, 0;
+ 0, 0, 0, 0, 1,-1, 0, 0, 4, 0,-2, 0, 1, 1, 0,-1, 0, 1, 0, 0, 0, 0, 0, 0;
+ 0, 0, 0, 0,-1, 0, 0, 0, 0, 4, 0, 0, 1, 1,-1, 1, 0, 0, 0, 1, 0, 0, 1, 0;
+ 0, 0, 0, 0, 0, 0, 0, 0,-2, 0, 4,-2, 0,-1, 0, 0, 0,-1, 0,-1, 0, 0, 0, 0;
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 4,-1, 1, 0, 0,-1, 1, 0, 1, 1, 1,-1, 0;
+ 1, 0,-1, 1, 1, 0, 0,-1, 1, 1, 0,-1, 4, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1,-1;
+-1,-1, 1,-1, 0, 0, 1, 0, 1, 1,-1, 1, 0, 4, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1;
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 1, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0;
+ 0, 0, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1, 1, 0, 4, 0, 0, 0, 0, 1, 1, 0, 0;
+ 0, 0, 1, 0,-2, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 4, 1, 1, 1, 0, 0, 1, 1;
+ 1, 0, 0, 1, 0, 0,-1, 0, 1, 0,-1, 1, 1, 0, 0, 0, 1, 4, 0, 1, 1, 0, 1, 0;
+ 0, 0, 0,-1, 0, 1, 0,-1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 4, 0, 1, 1, 0, 1;
+-1, -1,1, 0,-1, 1, 0,-1, 0, 1,-1, 1, 0, 1, 0, 0, 1, 1, 0, 4, 0, 0, 1, 1;
+ 0, 0,-1, 1, 1, 0, 0,-1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 4, 1, 0, 1;
+ 0, 1,-1,-1, 1,-1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 4, 0, 1;
+ 0,-1, 0, 1, 0, 1,-1, 1, 0, 1, 0,-1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 4, 1;
+-2,-1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 4]; }
+? qfminim(x,,0)  \\ the Leech lattice has 196560 minimal vectors of norm 4
+time = 648 ms.
+%4 = [196560, 4, [;]]
+? qfminim(x,,0,2); \\ safe algorithm. Slower and unnecessary here.
+time = 18,161 ms.
+%5 = [196560, 4.000061035156250000, [;]]
+ at eprog\noindent\sidx{Leech lattice}\sidx{minimal vector}
+In the last example, we store 0 vectors to limit memory use. All minimal
+vectors are nevertheless enumerated. Provided \kbd{parisize} is about 50MB,
+\kbd{qfminim(x)} succeeds in 2.5 seconds.
+
+The library syntax is \fun{GEN}{qfminim0}{GEN x, GEN b = NULL, GEN m = NULL, long flag, long prec}.
+Also available are
+\fun{GEN}{minim}{GEN x, GEN b = NULL, GEN m = NULL} ($\fl=0$),
+\fun{GEN}{minim2}{GEN x, GEN b = NULL, GEN m = NULL} ($\fl=1$).
+\fun{GEN}{minim_raw}{GEN x, GEN b = NULL, GEN m = NULL} (do not perform LLL
+reduction on x).
+
+\subsec{qfnorm$(x,\{q\})$}\kbdsidx{qfnorm}\label{se:qfnorm}
+Evaluate the binary quadratic form $q$ (symmetric matrix)
+at the vector $x$. If $q$ omitted, use the standard Euclidean form,
+corresponding to the identity matrix.
+
+Equivalent to \kbd{x\til * q * x}, but about twice faster and
+more convenient (does not distinguish between column and row vectors):
+\bprog
+? x = [1,2,3]~; qfnorm(x)
+%1 = 14
+? q = [1,2,3;2,2,-1;3,-1,0]; qfnorm(x, q)
+%2 = 23
+? for(i=1,10^6, qfnorm(x,q))
+time = 384ms.
+? for(i=1,10^6, x~*q*x)
+time = 729ms.
+ at eprog\noindent We also allow \typ{MAT}s of compatible dimensions for $x$,
+and return \kbd{x\til * q * x} in this case as well:
+\bprog
+? M = [1,2,3;4,5,6;7,8,9]; qfnorm(M) \\ Gram matrix
+%5 =
+[66  78  90]
+
+[78  93 108]
+
+[90 108 126]
+
+? for(i=1,10^6, qfnorm(M,q))
+time = 2,144 ms.
+? for(i=1,10^6, M~*q*M)
+time = 2,793 ms.
+ at eprog
+\noindent The polar form is also available, as \tet{qfbil}.
+
+The library syntax is \fun{GEN}{qfnorm}{GEN x, GEN q = NULL}.
+
+\subsec{qfperfection$(G)$}\kbdsidx{qfperfection}\label{se:qfperfection}
+$G$ being a square and symmetric matrix with
+integer entries representing a positive definite quadratic form, outputs the
+perfection rank of the form. That is, gives the rank of the family of the $s$
+symmetric matrices $v_iv_i^t$, where $s$ is half the number of minimal
+vectors and the $v_i$ ($1\le i\le s$) are the minimal vectors.
+
+Since this requires computing the minimal vectors, the computations can
+become very lengthy as the dimension of $x$ grows.
+
+The library syntax is \fun{GEN}{perf}{GEN G}.
+
+\subsec{qfrep$(q,B,\{\fl=0\})$}\kbdsidx{qfrep}\label{se:qfrep}
+$q$ being a square and symmetric matrix with integer entries representing a
+positive definite quadratic form, count the vectors representing successive
+integers.
+
+\item If $\fl = 0$, count all vectors. Outputs the vector whose $i$-th
+entry, $1 \leq i \leq B$ is half the number of vectors $v$ such that $q(v)=i$.
+
+\item If $\fl = 1$, count vectors of even norm. Outputs the vector
+whose $i$-th entry, $1 \leq i \leq B$ is half the number of vectors such
+that $q(v) = 2i$.
+
+\bprog
+? q = [2, 1; 1, 3];
+? qfrep(q, 5)
+%2 = Vecsmall([0, 1, 2, 0, 0]) \\ 1 vector of norm 2, 2 of norm 3, etc.
+? qfrep(q, 5, 1)
+%3 = Vecsmall([1, 0, 0, 1, 0]) \\ 1 vector of norm 2, 0 of norm 4, etc.
+ at eprog\noindent
+This routine uses a naive algorithm based on \tet{qfminim}, and
+will fail if any entry becomes larger than $2^{31}$ (or $2^{63}$).
+
+The library syntax is \fun{GEN}{qfrep0}{GEN q, GEN B, long flag}.
+
+\subsec{qfsign$(x)$}\kbdsidx{qfsign}\label{se:qfsign}
+Returns $[p,m]$ the signature of the quadratic form represented by the
+symmetric matrix $x$. Namely, $p$ (resp.~$m$) is the number of positive
+(resp.~negative) eigenvalues of $x$.The result is computed using Gaussian
+reduction.
+
+The library syntax is \fun{GEN}{qfsign}{GEN x}.
+
+\subsec{seralgdep$(s,p,r)$}\kbdsidx{seralgdep}\label{se:seralgdep}
+\sidx{algebraic dependence} finds a linear relation between powers $(1,s,
+\dots, s^p)$ of the series $s$, with polynomial coefficients of degree
+$\leq r$. In case no relation is found, return $0$.
+\bprog
+? s = 1 + 10*y - 46*y^2 + 460*y^3 - 5658*y^4 + 77740*y^5 + O(y^6);
+? seralgdep(s, 2, 2)
+%2 = -x^2 + (8*y^2 + 20*y + 1)
+? subst(%, x, s)
+%3 = O(y^6)
+? seralgdep(s, 1, 3)
+%4 = (-77*y^2 - 20*y - 1)*x + (310*y^3 + 231*y^2 + 30*y + 1)
+? seralgdep(s, 1, 2)
+%5 = 0
+ at eprog\noindent The series main variable must not be $x$, so as to be able
+to express the result as a polynomial in $x$.
+
+The library syntax is \fun{GEN}{seralgdep}{GEN s, long p, long r}.
+
+\subsec{setbinop$(f,X,\{Y\})$}\kbdsidx{setbinop}\label{se:setbinop}
+The set whose elements are the f(x,y), where x,y run through X,Y.
+respectively. If $Y$ is omitted, assume that $X = Y$ and that $f$ is symmetric:
+$f(x,y) = f(y,x)$ for all $x,y$ in $X$.
+\bprog
+? X = [1,2,3]; Y = [2,3,4];
+? setbinop((x,y)->x+y, X,Y) \\ set X + Y
+%2 = [3, 4, 5, 6, 7]
+? setbinop((x,y)->x-y, X,Y) \\ set X - Y
+%3 = [-3, -2, -1, 0, 1]
+? setbinop((x,y)->x+y, X)   \\ set 2X = X + X
+%2 = [2, 3, 4, 5, 6]
+ at eprog
+
+The library syntax is \fun{GEN}{setbinop}{GEN f, GEN X, GEN Y = NULL}.
+
+\subsec{setintersect$(x,y)$}\kbdsidx{setintersect}\label{se:setintersect}
+Intersection of the two sets $x$ and $y$ (see \kbd{setisset}).
+If $x$ or $y$ is not a set, the result is undefined.
+
+The library syntax is \fun{GEN}{setintersect}{GEN x, GEN y}.
+
+\subsec{setisset$(x)$}\kbdsidx{setisset}\label{se:setisset}
+Returns true (1) if $x$ is a set, false (0) if
+not. In PARI, a set is a row vector whose entries are strictly
+increasing with respect to a (somewhat arbitrary) universal comparison
+function. To convert any object into a set (this is most useful for
+vectors, of course), use the function \kbd{Set}.
+\bprog
+? a = [3, 1, 1, 2];
+? setisset(a)
+%2 = 0
+? Set(a)
+%3 = [1, 2, 3]
+ at eprog
+
+The library syntax is \fun{long}{setisset}{GEN x}.
+
+\subsec{setminus$(x,y)$}\kbdsidx{setminus}\label{se:setminus}
+Difference of the two sets $x$ and $y$ (see \kbd{setisset}),
+i.e.~set of elements of $x$ which do not belong to $y$.
+If $x$ or $y$ is not a set, the result is undefined.
+
+The library syntax is \fun{GEN}{setminus}{GEN x, GEN y}.
+
+\subsec{setsearch$(S,x,\{\fl=0\})$}\kbdsidx{setsearch}\label{se:setsearch}
+Determines whether $x$ belongs to the set $S$ (see \kbd{setisset}).
+
+We first describe the default behaviour, when $\fl$ is zero or omitted. If $x$
+belongs to the set $S$, returns the index $j$ such that $S[j]=x$, otherwise
+returns 0.
+\bprog
+? T = [7,2,3,5]; S = Set(T);
+? setsearch(S, 2)
+%2 = 1
+? setsearch(S, 4)      \\ not found
+%3 = 0
+? setsearch(T, 7)      \\ search in a randomly sorted vector
+%4 = 0 \\ WRONG !
+ at eprog\noindent
+If $S$ is not a set, we also allow sorted lists with
+respect to the \tet{cmp} sorting function, without repeated entries,
+as per \tet{listsort}$(L,1)$; otherwise the result is undefined.
+\bprog
+? L = List([1,4,2,3,2]); setsearch(L, 4)
+%1 = 0 \\ WRONG !
+? listsort(L, 1); L    \\ sort L first
+%2 = List([1, 2, 3, 4])
+? setsearch(L, 4)
+%3 = 4                 \\ now correct
+ at eprog\noindent
+If $\fl$ is non-zero, this function returns the index $j$ where $x$ should be
+inserted, and $0$ if it already belongs to $S$. This is meant to be used for
+dynamically growing (sorted) lists, in conjunction with \kbd{listinsert}.
+\bprog
+? L = List([1,5,2,3,2]); listsort(L,1); L
+%1 = List([1,2,3,5])
+? j = setsearch(L, 4, 1)  \\ 4 should have been inserted at index j
+%2 = 4
+? listinsert(L, 4, j); L
+%3 = List([1, 2, 3, 4, 5])
+ at eprog
+
+The library syntax is \fun{long}{setsearch}{GEN S, GEN x, long flag}.
+
+\subsec{setunion$(x,y)$}\kbdsidx{setunion}\label{se:setunion}
+Union of the two sets $x$ and $y$ (see \kbd{setisset}).
+If $x$ or $y$ is not a set, the result is undefined.
+
+The library syntax is \fun{GEN}{setunion}{GEN x, GEN y}.
+
+\subsec{trace$(x)$}\kbdsidx{trace}\label{se:trace}
+This applies to quite general $x$. If $x$ is not a
+matrix, it is equal to the sum of $x$ and its conjugate, except for polmods
+where it is the trace as an algebraic number.
+
+For $x$ a square matrix, it is the ordinary trace. If $x$ is a
+non-square matrix (but not a vector), an error occurs.
+
+The library syntax is \fun{GEN}{gtrace}{GEN x}.
+
+\subsec{vecextract$(x,y,\{z\})$}\kbdsidx{vecextract}\label{se:vecextract}
+Extraction of components of the vector or matrix $x$ according to $y$.
+In case $x$ is a matrix, its components are the \emph{columns} of $x$. The
+parameter $y$ is a component specifier, which is either an integer, a string
+describing a range, or a vector.
+
+If $y$ is an integer, it is considered as a mask: the binary bits of $y$ are
+read from right to left, but correspond to taking the components from left to
+right. For example, if $y=13=(1101)_2$ then the components 1,3 and 4 are
+extracted.
+
+If $y$ is a vector (\typ{VEC}, \typ{COL} or \typ{VECSMALL}), which must have
+integer entries, these entries correspond to the component numbers to be
+extracted, in the order specified.
+
+If $y$ is a string, it can be
+
+\item a single (non-zero) index giving a component number (a negative
+index means we start counting from the end).
+
+\item a range of the form \kbd{"$a$..$b$"}, where $a$ and $b$ are
+indexes as above. Any of $a$ and $b$ can be omitted; in this case, we take
+as default values $a = 1$ and $b = -1$, i.e.~ the first and last components
+respectively. We then extract all components in the interval $[a,b]$, in
+reverse order if $b < a$.
+
+In addition, if the first character in the string is \kbd{\pow}, the
+complement of the given set of indices is taken.
+
+If $z$ is not omitted, $x$ must be a matrix. $y$ is then the \emph{row}
+specifier, and $z$ the \emph{column} specifier, where the component specifier
+is as explained above.
+
+\bprog
+? v = [a, b, c, d, e];
+? vecextract(v, 5)         \\@com mask
+%1 = [a, c]
+? vecextract(v, [4, 2, 1]) \\@com component list
+%2 = [d, b, a]
+? vecextract(v, "2..4")    \\@com interval
+%3 = [b, c, d]
+? vecextract(v, "-1..-3")  \\@com interval + reverse order
+%4 = [e, d, c]
+? vecextract(v, "^2")      \\@com complement
+%5 = [a, c, d, e]
+? vecextract(matid(3), "2..", "..")
+%6 =
+[0 1 0]
+
+[0 0 1]
+ at eprog
+The range notations \kbd{v[i..j]} and \kbd{v[\pow i]} (for \typ{VEC} or
+\typ{COL}) and \kbd{M[i..j, k..l]} and friends (for \typ{MAT}) implement a
+subset of the above, in a simpler and \emph{faster} way, hence should be
+preferred in most common situations. The following features are not
+implemented in the range notation:
+
+\item reverse order,
+
+\item omitting either $a$ or $b$ in \kbd{$a$..$b$}.
+
+The library syntax is \fun{GEN}{extract0}{GEN x, GEN y, GEN z = NULL}.
+
+\subsec{vecsearch$(v,x,\{\var{cmpf}\})$}\kbdsidx{vecsearch}\label{se:vecsearch}
+Determines whether $x$ belongs to the sorted vector or list $v$: return
+the (positive) index where $x$ was found, or $0$ if it does not belong to
+$v$.
+
+If the comparison function cmpf is omitted, we assume that $v$ is sorted in
+increasing order, according to the standard comparison function $<$, thereby
+restricting the possible types for $x$ and the elements of $v$ (integers,
+fractions or reals).
+
+If \kbd{cmpf} is present, it is understood as a comparison function and we
+assume that $v$ is sorted according to it, see \tet{vecsort} for how to
+encode comparison functions.
+\bprog
+? v = [1,3,4,5,7];
+? vecsearch(v, 3)
+%2 = 2
+? vecsearch(v, 6)
+%3 = 0 \\ not in the list
+? vecsearch([7,6,5], 5) \\ unsorted vector: result undefined
+%4 = 0
+ at eprog
+
+By abuse of notation, $x$ is also allowed to be a matrix, seen as a vector
+of its columns; again by abuse of notation, a \typ{VEC} is considered
+as part of the matrix, if its transpose is one of the matrix columns.
+\bprog
+? v = vecsort([3,0,2; 1,0,2]) \\ sort matrix columns according to lex order
+%1 =
+[0 2 3]
+
+[0 2 1]
+? vecsearch(v, [3,1]~)
+%2 = 3
+? vecsearch(v, [3,1])  \\ can search for x or x~
+%3 = 3
+? vecsearch(v, [1,2])
+%4 = 0 \\ not in the list
+ at eprog\noindent
+
+The library syntax is \fun{long}{vecsearch}{GEN v, GEN x, GEN cmpf = NULL}.
+
+\subsec{vecsort$(x,\{\var{cmpf}\},\{\fl=0\})$}\kbdsidx{vecsort}\label{se:vecsort}
+Sorts the vector $x$ in ascending order, using a mergesort method.
+$x$ must be a list, vector or matrix (seen as a vector of its columns).
+Note that mergesort is stable, hence the initial ordering of ``equal''
+entries (with respect to the sorting criterion) is not changed.
+
+If \kbd{cmpf} is omitted, we use the standard comparison function
+\kbd{lex}, thereby restricting the possible types for the elements of $x$
+(integers, fractions or reals and vectors of those). If \kbd{cmpf} is
+present, it is understood as a comparison function and we sort according to
+it. The following possibilities exist:
+
+\item an integer $k$: sort according to the value of the $k$-th
+subcomponents of the components of~$x$.
+
+\item a vector: sort lexicographically according to the components listed in
+the vector. For example, if $\kbd{cmpf}=\kbd{[2,1,3]}$, sort with respect to
+the second component, and when these are equal, with respect to the first,
+and when these are equal, with respect to the third.
+
+\item a comparison function (\typ{CLOSURE}), with two arguments $x$ and $y$,
+and returning an integer which is $<0$, $>0$ or $=0$ if $x<y$, $x>y$ or
+$x=y$ respectively. The \tet{sign} function is very useful in this context:
+\bprog
+? vecsort([3,0,2; 1,0,2]) \\ sort columns according to lex order
+%1 =
+[0 2 3]
+
+[0 2 1]
+? vecsort(v, (x,y)->sign(y-x))            \\@com reverse sort
+? vecsort(v, (x,y)->sign(abs(x)-abs(y)))  \\@com sort by increasing absolute value
+? cmpf(x,y) = my(dx = poldisc(x), dy = poldisc(y)); sign(abs(dx) - abs(dy))
+? vecsort([x^2+1, x^3-2, x^4+5*x+1], cmpf)
+ at eprog\noindent
+The last example used the named \kbd{cmpf} instead of an anonymous function,
+and sorts polynomials with respect to the absolute value of their
+discriminant. A more efficient approach would use precomputations to ensure
+a given discriminant is computed only once:
+\bprog
+? DISC = vector(#v, i, abs(poldisc(v[i])));
+? perm = vecsort(vector(#v,i,i), (x,y)->sign(DISC[x]-DISC[y]))
+? vecextract(v, perm)
+ at eprog\noindent Similar ideas apply whenever we sort according to the values
+of a function which is expensive to compute.
+
+\noindent The binary digits of \fl\ mean:
+
+\item 1: indirect sorting of the vector $x$, i.e.~if $x$ is an
+$n$-component vector, returns a permutation of $[1,2,\dots,n]$ which
+applied to the components of $x$ sorts $x$ in increasing order.
+For example, \kbd{vecextract(x, vecsort(x,,1))} is equivalent to
+\kbd{vecsort(x)}.
+
+\item 4: use descending instead of ascending order.
+
+\item 8: remove ``duplicate'' entries with respect to the sorting function
+(keep the first occurring entry).  For example:
+\bprog
+  ? vecsort([Pi,Mod(1,2),z], (x,y)->0, 8)   \\@com make everything compare equal
+  %1 = [3.141592653589793238462643383]
+  ? vecsort([[2,3],[0,1],[0,3]], 2, 8)
+  %2 = [[0, 1], [2, 3]]
+ at eprog
+
+The library syntax is \fun{GEN}{vecsort0}{GEN x, GEN cmpf = NULL, long flag}.
+
+\subsec{vecsum$(v)$}\kbdsidx{vecsum}\label{se:vecsum}
+Return the sum of the component of the vector $v$
+
+The library syntax is \fun{GEN}{vecsum}{GEN v}.
+
+\subsec{vector$(n,\{X\},\{\var{expr}=0\})$}\kbdsidx{vector}\label{se:vector}
+Creates a row vector (type
+\typ{VEC}) with $n$ components whose components are the expression
+\var{expr} evaluated at the integer points between 1 and $n$. If one of the
+last two arguments is omitted, fill the vector with zeroes.
+
+Avoid modifying $X$ within \var{expr}; if you do, the formal variable
+still runs from $1$ to $n$. In particular, \kbd{vector(n,i,expr)} is not
+equivalent to
+\bprog
+v = vector(n)
+for (i = 1, n, v[i] = expr)
+ at eprog\noindent
+as the following example shows:
+\bprog
+n = 3
+v = vector(n); vector(n, i, i++)            ----> [2, 3, 4]
+v = vector(n); for (i = 1, n, v[i] = i++)   ----> [2, 0, 4]
+ at eprog\noindent
+%\syn{NO}
+
+\subsec{vectorsmall$(n,\{X\},\{\var{expr}=0\})$}\kbdsidx{vectorsmall}\label{se:vectorsmall}
+Creates a row vector of small integers (type
+\typ{VECSMALL}) with $n$ components whose components are the expression
+\var{expr} evaluated at the integer points between 1 and $n$. If one of the
+last two arguments is omitted, fill the vector with zeroes.
+%\syn{NO}
+
+\subsec{vectorv$(n,\{X\},\{\var{expr}=0\})$}\kbdsidx{vectorv}\label{se:vectorv}
+As \tet{vector}, but returns a column vector (type \typ{COL}).
+%\syn{NO}
+%SECTION: linear_algebra
+
+\section{Sums, products, integrals and similar functions}
+\label{se:sums}
+
+Although the \kbd{gp} calculator is programmable, it is useful to have
+a number of preprogrammed loops, including sums, products, and a certain
+number of recursions. Also, a number of functions from numerical analysis
+like numerical integration and summation of series will be described here.
+
+One of the parameters in these loops must be the control variable, hence a
+simple variable name. In the descriptions, the letter $X$ will always denote
+any simple variable name, and represents the formal parameter used in the
+function. The expression to be summed, integrated, etc. is any legal PARI
+expression, including of course expressions using loops.
+
+\misctitle{Library mode}
+Since it is easier to program directly the loops in library mode, these
+functions are mainly useful for GP programming. On the other hand, numerical
+routines code a function (to be integrated, summed, etc.) with two parameters
+named
+\bprog
+  GEN (*eval)(void*,GEN)
+  void *E;  \\ context: eval(E, x) must evaluate your function at x.
+ at eprog\noindent
+see the Libpari manual for details.
+
+\misctitle{Numerical integration}\sidx{numerical integration}
+Starting with version 2.2.9 the ``double exponential'' univariate
+integration method is implemented in \tet{intnum} and its variants. Romberg
+integration is still available under the name \kbd{intnumromb}, but
+superseded. It is possible to compute numerically integrals to thousands of
+decimal places in reasonable time, as long as the integrand is regular. It is
+also reasonable to compute numerically integrals in several variables,
+although more than two becomes lengthy. The integration domain may be
+non-compact, and the integrand may have reasonable singularities at
+endpoints. To use \kbd{intnum}, you must split the integral into a sum
+of subintegrals where the function has no singularities except at the
+endpoints. Polynomials in logarithms are not considered singular, and
+neglecting these logs, singularities are assumed to be algebraic (asymptotic
+to $C(x-a)^{-\alpha}$ for some $\alpha > -1$ when $x$ is
+close to $a$), or to correspond to simple discontinuities of some (higher)
+derivative of the function. For instance, the point $0$ is a singularity of
+$\text{abs}(x)$.
+
+See also the discrete summation methods below, sharing the prefix \kbd{sum}.
+
+
+\subsec{derivnum$(X=a,\var{expr})$}\kbdsidx{derivnum}\label{se:derivnum}
+Numerical derivation of \var{expr} with respect to $X$ at $X=a$.
+
+\bprog
+? derivnum(x=0,sin(exp(x))) - cos(1)
+%1 = -1.262177448 E-29
+ at eprog
+A clumsier approach, which would not work in library mode, is
+\bprog
+? f(x) = sin(exp(x))
+? f'(0) - cos(1)
+%1 = -1.262177448 E-29
+ at eprog
+When $a$ is a power series, compute \kbd{derivnum(t=a,f)} as $f'(a) =
+(f(a))'/a'$.
+
+\synt{derivnum}{void *E, GEN (*eval)(void*,GEN), GEN a, long prec}. Also
+available is \fun{GEN}{derivfun}{void *E, GEN (*eval)(void *, GEN), GEN a, long prec}, which also allows power series for $a$.
+
+\subsec{intcirc$(X=a,R,\var{expr},\{\var{tab}\})$}\kbdsidx{intcirc}\label{se:intcirc}
+Numerical
+integration of $(2i\pi)^{-1}\var{expr}$ with respect to $X$ on the circle
+$|X-a| = R$.
+In other words, when \var{expr} is a meromorphic
+function, sum of the residues in the corresponding disk. \var{tab} is as in
+\kbd{intnum}, except that if computed with \kbd{intnuminit} it should be with
+the endpoints \kbd{[-1, 1]}.
+
+\bprog
+? \p105
+? intcirc(s=1, 0.5, zeta(s)) - 1
+%1 = -2.398082982 E-104 - 7.94487211 E-107*I
+ at eprog
+
+\synt{intcirc}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN R,GEN tab, long prec}.
+
+\subsec{intfouriercos$(X=a,b,z,\var{expr},\{\var{tab}\})$}\kbdsidx{intfouriercos}\label{se:intfouriercos}
+Numerical
+integration of $\var{expr}(X)\cos(2\pi zX)$ from $a$ to $b$, in other words
+Fourier cosine transform (from $a$ to $b$) of the function represented by
+\var{expr}. Endpoints $a$ and $b$ are coded as in \kbd{intnum}, and are not
+necessarily at infinity, but if they are, oscillations (i.e. $[[\pm1],\alpha
+I]$) are forbidden.
+
+\synt{intfouriercos}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, GEN z, GEN tab, long prec}.
+
+\subsec{intfourierexp$(X=a,b,z,\var{expr},\{\var{tab}\})$}\kbdsidx{intfourierexp}\label{se:intfourierexp}
+Numerical
+integration of $\var{expr}(X)\exp(-2i\pi zX)$ from $a$ to $b$, in other words
+Fourier transform (from $a$ to $b$) of the function represented by
+\var{expr}. Note the minus sign. Endpoints $a$ and $b$ are coded as in
+\kbd{intnum}, and are not necessarily at infinity but if they are,
+oscillations (i.e. $[[\pm1],\alpha I]$) are forbidden.
+
+\synt{intfourierexp}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, GEN z, GEN tab, long prec}.
+
+\subsec{intfouriersin$(X=a,b,z,\var{expr},\{\var{tab}\})$}\kbdsidx{intfouriersin}\label{se:intfouriersin}
+Numerical
+integration of $\var{expr}(X)\sin(2\pi zX)$ from $a$ to $b$, in other words
+Fourier sine transform (from $a$ to $b$) of the function represented by
+\var{expr}. Endpoints $a$ and $b$ are coded as in \kbd{intnum}, and are not
+necessarily at infinity but if they are, oscillations (i.e. $[[\pm1],\alpha
+I]$) are forbidden.
+
+\synt{intfouriersin}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, GEN z, GEN tab, long prec}.
+
+\subsec{intfuncinit$(X=a,b,\var{expr},\{\fl=0\},\{m=0\})$}\kbdsidx{intfuncinit}\label{se:intfuncinit}
+Initialize tables for use with integral transforms such as \kbd{intmellininv},
+etc., where $a$ and $b$ are coded as in \kbd{intnum}, $\var{expr}$ is the
+function $s(X)$ to which the integral transform is to be applied (which will
+multiply the weights of integration) and $m$ is as in \kbd{intnuminit}. If
+$\fl$ is nonzero, assumes that $s(-X)=\overline{s(X)}$, which makes the
+computation twice as fast. See \kbd{intmellininvshort} for examples of the
+use of this function, which is particularly useful when the function $s(X)$
+is lengthy to compute, such as a gamma product.
+
+\synt{intfuncinit}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN b,long m, long flag, long prec}. Note that the order of $m$ and $\fl$ are reversed compared
+to the \kbd{GP} syntax.
+
+\subsec{intlaplaceinv$(X=\var{sig},z,\var{expr},\{\var{tab}\})$}\kbdsidx{intlaplaceinv}\label{se:intlaplaceinv}
+Numerical integration of $(2i\pi)^{-1}\var{expr}(X)e^{Xz}$ with respect
+to $X$ on the line $\Re(X)=sig$. In other words, inverse Laplace transform
+of the function corresponding to \var{expr} at the value $z$.
+
+$sig$ is coded as follows. Either it is a real number $\sigma$, equal to the
+abscissa of integration, and then the integrand is assumed to
+be slowly decreasing when the imaginary part of the variable tends to
+$\pm\infty$. Or it is a two component vector $[\sigma,\alpha]$, where
+$\sigma$ is as before, and either $\alpha=0$ for slowly decreasing functions,
+or $\alpha>0$ for functions decreasing like $\exp(-\alpha t)$. Note that it
+is not necessary to choose the exact value of $\alpha$. \var{tab} is as in
+\kbd{intnum}.
+
+It is often a good idea to use this function with a value of $m$ one or two
+higher than the one chosen by default (which can be viewed thanks to the
+function \kbd{intnumstep}), or to increase the abscissa of integration
+$\sigma$. For example:
+
+\bprog
+? \p 105
+? intlaplaceinv(x=2, 1, 1/x) - 1
+time = 350 ms.
+%1 = 7.37... E-55 + 1.72... E-54*I \\@com not so good
+? m = intnumstep()
+%2 = 7
+? intlaplaceinv(x=2, 1, 1/x, m+1) - 1
+time = 700 ms.
+%3 = 3.95... E-97 + 4.76... E-98*I \\@com better
+? intlaplaceinv(x=2, 1, 1/x, m+2) - 1
+time = 1400 ms.
+%4 = 0.E-105 + 0.E-106*I \\@com perfect but slow.
+? intlaplaceinv(x=5, 1, 1/x) - 1
+time = 340 ms.
+%5 = -5.98... E-85 + 8.08... E-85*I \\@com better than \%1
+? intlaplaceinv(x=5, 1, 1/x, m+1) - 1
+time = 680 ms.
+%6 = -1.09... E-106 + 0.E-104*I \\@com perfect, fast.
+? intlaplaceinv(x=10, 1, 1/x) - 1
+time = 340 ms.
+%7 = -4.36... E-106 + 0.E-102*I \\@com perfect, fastest, but why $sig=10$?
+? intlaplaceinv(x=100, 1, 1/x) - 1
+time = 330 ms.
+%7 = 1.07... E-72 + 3.2... E-72*I \\@com too far now...
+ at eprog
+
+\synt{intlaplaceinv}{void *E, GEN (*eval)(void*,GEN), GEN sig,GEN z, GEN tab, long prec}.
+
+\subsec{intmellininv$(X=\var{sig},z,\var{expr},\{\var{tab}\})$}\kbdsidx{intmellininv}\label{se:intmellininv}
+Numerical
+integration of $(2i\pi)^{-1}\var{expr}(X)z^{-X}$ with respect to $X$ on the
+line $\Re(X)=sig$,  in other words, inverse Mellin transform of
+the function corresponding to \var{expr} at the value $z$.
+
+$sig$ is coded as follows. Either it is a real number $\sigma$, equal to the
+abscissa of integration, and then the integrated is assumed to decrease
+exponentially fast, of the order of $\exp(-t)$ when the imaginary part of the
+variable tends to $\pm\infty$. Or it is a two component vector
+$[\sigma,\alpha]$, where $\sigma$ is as before, and either $\alpha=0$ for
+slowly decreasing functions, or $\alpha>0$ for functions decreasing like
+$\exp(-\alpha t)$, such as gamma products. Note that it is not necessary to
+choose the exact value of $\alpha$, and that $\alpha=1$ (equivalent to $sig$
+alone) is usually sufficient. \var{tab} is as in \kbd{intnum}.
+
+As all similar functions, this function is provided for the convenience of
+the user, who could use \kbd{intnum} directly. However it is in general
+better to use \kbd{intmellininvshort}.
+
+\bprog
+? \p 105
+? intmellininv(s=2,4, gamma(s)^3);
+time = 1,190 ms. \\@com reasonable.
+? \p 308
+? intmellininv(s=2,4, gamma(s)^3);
+time = 51,300 ms. \\@com slow because of $\Gamma(s)^3$.
+ at eprog\noindent
+
+\synt{intmellininv}{void *E, GEN (*eval)(void*,GEN), GEN sig, GEN z, GEN tab, long prec}.
+
+\subsec{intmellininvshort$(\var{sig},z,\var{tab})$}\kbdsidx{intmellininvshort}\label{se:intmellininvshort}
+Numerical integration
+of $(2i\pi)^{-1}s(X)z^{-X}$ with respect to $X$ on the line $\Re(X)=sig$.
+In other words, inverse Mellin transform of $s(X)$ at the value $z$.
+Here $s(X)$ is implicitly contained in \var{tab} in \kbd{intfuncinit} format,
+typically
+\bprog
+tab = intfuncinit(T = [-1], [1], s(sig + I*T))
+ at eprog\noindent
+or similar commands. Take the example of the inverse Mellin transform of
+$\Gamma(s)^3$ given in \kbd{intmellininv}:
+
+\bprog
+? \p 105
+? oo = [1]; \\@com for clarity
+? A = intmellininv(s=2,4, gamma(s)^3);
+time = 2,500 ms. \\@com not too fast because of $\Gamma(s)^3$.
+\\ @com function of real type, decreasing as $\exp(-3\pi/2\cdot |t|)$
+? tab = intfuncinit(t=[-oo, 3*Pi/2],[oo, 3*Pi/2], gamma(2+I*t)^3, 1);
+time = 1,370 ms.
+? intmellininvshort(2,4, tab) - A
+time = 50 ms.
+%4 = -1.26... - 3.25...E-109*I \\@com 50 times faster than \kbd{A} and perfect.
+? tab2 = intfuncinit(t=-oo, oo, gamma(2+I*t)^3, 1);
+? intmellininvshort(2,4, tab2)
+%6 = -1.2...E-42 - 3.2...E-109*I  \\@com 63 digits lost
+ at eprog\noindent
+In the computation of \var{tab}, it was not essential to include the
+\emph{exact} exponential decrease of $\Gamma(2+it)^3$. But as the last
+example shows, a rough indication \emph{must} be given, otherwise slow
+decrease is assumed, resulting in catastrophic loss of accuracy.
+
+The library syntax is \fun{GEN}{intmellininvshort}{GEN sig, GEN z, GEN tab, long prec}.
+
+\subsec{intnum$(X=a,b,\var{expr},\{\var{tab}\})$}\kbdsidx{intnum}\label{se:intnum}
+Numerical integration
+of \var{expr} on $]a,b[$ with respect to $X$. The integrand may have values
+belonging to a vector space over the real numbers; in particular, it can be
+complex-valued or vector-valued. But it is assumed that the function is regular
+on $]a,b[$. If the endpoints $a$ and $b$ are finite and the function is regular
+there, the situation is simple:
+\bprog
+? intnum(x = 0,1, x^2)
+%1 = 0.3333333333333333333333333333
+? intnum(x = 0,Pi/2, [cos(x), sin(x)])
+%2 = [1.000000000000000000000000000, 1.000000000000000000000000000]
+ at eprog\noindent
+An endpoint equal to $\pm\infty$ is coded as the single-component vector
+$[\pm1]$. You are welcome to set, e.g \kbd{oo = [1]} or \kbd{INFINITY = [1]},
+then using \kbd{+oo}, \kbd{-oo}, \kbd{-INFINITY}, etc. will have the expected
+behavior.
+\bprog
+? oo = [1];  \\@com for clarity
+? intnum(x = 1,+oo, 1/x^2)
+%2 = 1.000000000000000000000000000
+ at eprog\noindent
+In basic usage, it is assumed that the function does not decrease
+exponentially fast at infinity:
+\bprog
+? intnum(x=0,+oo, exp(-x))
+  ***   at top-level: intnum(x=0,+oo,exp(-
+  ***                 ^--------------------
+  *** exp: exponent (expo) overflow
+ at eprog\noindent
+We shall see in a moment how to avoid the last problem, after describing
+the last argument \var{tab}, which is both optional and technical. The
+routine uses weights, which are mostly independent of the function being
+integrated, evaluated at many sampling points. If \var{tab} is
+
+\item a positive integer $m$, we use $2^m$ sampling points, hopefully
+increasing accuracy. But note that the running time is roughly proportional
+to $2^m$. One may try consecutive values of $m$ until they give the same
+value up to an accepted error. If \var{tab} is omitted, the algorithm guesses
+a reasonable value for $m$ depending on the current precision only, which
+should be sufficient for regular functions. That value may be obtained from
+\tet{intnumstep}, and increased in case of difficulties.
+
+\item a set of integration tables as output by \tet{intnuminit},
+they are used directly. This is useful if several integrations of the same
+type are performed (on the same kind of interval and functions, for a given
+accuracy), in particular for multivariate integrals, since we then skip
+expensive precomputations.
+
+\misctitle{Specifying the behavior at endpoints}
+This is done as follows. An endpoint $a$ is either given as such (a scalar,
+real or complex, or $[\pm1]$ for $\pm\infty$), or as a two component vector
+$[a,\alpha]$, to indicate the behavior of the integrand in a neighborhood
+of $a$.
+
+If $a$ is finite, the code $[a,\alpha]$ means the function has a
+singularity of the form $(x-a)^{\alpha}$, up to logarithms. (If $\alpha \ge
+0$, we only assume the function is regular, which is the default assumption.)
+If a wrong singularity exponent is used, the result will lose a catastrophic
+number of decimals:
+\bprog
+? intnum(x=0, 1, x^(-1/2))         \\@com assume $x^{-1/2}$ is regular at 0
+%1 = 1.999999999999999999990291881
+? intnum(x=[0,-1/2], 1, x^(-1/2))  \\@com no, it's not
+%2 = 2.000000000000000000000000000
+? intnum(x=[0,-1/10], 1, x^(-1/2))
+%3 = 1.999999999999999999999946438 \\@com using a wrong exponent is bad
+ at eprog
+
+If $a$ is $\pm\infty$, which is coded as $[\pm 1]$, the situation is more
+complicated, and $[[\pm1],\alpha]$ means:
+
+\item $\alpha=0$ (or no $\alpha$ at all, i.e. simply $[\pm1]$) assumes that the
+integrand tends to zero, but not exponentially fast, and not
+oscillating such as $\sin(x)/x$.
+
+\item $\alpha>0$ assumes that the function tends to zero exponentially fast
+approximately as $\exp(-\alpha x)$. This includes oscillating but quickly
+decreasing functions such as $\exp(-x)\sin(x)$.
+\bprog
+? oo = [1];
+? intnum(x=0, +oo, exp(-2*x))
+  ***   at top-level: intnum(x=0,+oo,exp(-
+  ***                 ^--------------------
+  *** exp: exponent (expo) overflow
+? intnum(x=0, [+oo, 2], exp(-2*x))
+%1 = 0.5000000000000000000000000000 \\@com OK!
+? intnum(x=0, [+oo, 4], exp(-2*x))
+%2 = 0.4999999999999999999961990984 \\@com wrong exponent $\Rightarrow$ imprecise result
+? intnum(x=0, [+oo, 20], exp(-2*x))
+%2 = 0.4999524997739071283804510227 \\@com disaster
+ at eprog
+
+\item $\alpha<-1$ assumes that the function tends to $0$ slowly, like
+$x^{\alpha}$. Here it is essential to give the correct $\alpha$, if possible,
+but on the other hand $\alpha\le -2$ is equivalent to $\alpha=0$, in other
+words to no $\alpha$ at all.
+
+\smallskip The last two codes are reserved for oscillating functions.
+Let $k > 0$ real, and $g(x)$ a non-oscillating function tending slowly to $0$
+(e.g. like a negative power of $x$), then
+
+\item $\alpha=k * I$ assumes that the function behaves like $\cos(kx)g(x)$.
+
+\item $\alpha=-k* I$ assumes that the function behaves like $\sin(kx)g(x)$.
+
+\noindent Here it is critical to give the exact value of $k$. If the
+oscillating part is not a pure sine or cosine, one must expand it into a
+Fourier series, use the above codings, and sum the resulting contributions.
+Otherwise you will get nonsense. Note that $\cos(kx)$, and similarly
+$\sin(kx)$, means that very function, and not a translated version such as
+$\cos(kx+a)$.
+
+\misctitle{Note} If $f(x)=\cos(kx)g(x)$ where $g(x)$ tends to zero
+exponentially fast as $\exp(-\alpha x)$, it is up to the user to choose
+between $[[\pm1],\alpha]$ and $[[\pm1],k* I]$, but a good rule of thumb is that
+if the oscillations are much weaker than the exponential decrease, choose
+$[[\pm1],\alpha]$, otherwise choose $[[\pm1],k* I]$, although the latter can
+reasonably be used in all cases, while the former cannot. To take a specific
+example, in the inverse Mellin transform, the integrand is almost always a
+product of an exponentially decreasing and an oscillating factor. If we
+choose the oscillating type of integral we perhaps obtain the best results,
+at the expense of having to recompute our functions for a different value of
+the variable $z$ giving the transform, preventing us to use a function such
+as \kbd{intmellininvshort}. On the other hand using the exponential type of
+integral, we obtain less accurate results, but we skip expensive
+recomputations. See \kbd{intmellininvshort} and \kbd{intfuncinit} for more
+explanations.
+
+\smallskip
+
+We shall now see many examples to get a feeling for what the various
+parameters achieve. All examples below assume precision is set to $105$
+decimal digits. We first type
+\bprog
+? \p 105
+? oo = [1]  \\@com for clarity
+ at eprog
+
+\misctitle{Apparent singularities} Even if the function $f(x)$ represented
+by \var{expr} has no singularities, it may be important to define the
+function differently near special points. For instance, if $f(x) = 1
+/(\exp(x)-1) - \exp(-x)/x$, then $\int_0^\infty f(x)\,dx=\gamma$, Euler's
+constant \kbd{Euler}. But
+
+\bprog
+? f(x) = 1/(exp(x)-1) - exp(-x)/x
+? intnum(x = 0, [oo,1],  f(x)) - Euler
+%1 = 6.00... E-67
+ at eprog\noindent
+thus only correct to $67$ decimal digits. This is because close to $0$ the
+function $f$ is computed with an enormous loss of accuracy.
+A better solution is
+
+\bprog
+? f(x) = 1/(exp(x)-1)-exp(-x)/x
+? F = truncate( f(t + O(t^7)) ); \\@com expansion around t = 0
+? g(x) = if (x > 1e-18, f(x), subst(F,t,x))  \\@com note that $6 \cdot 18 > 105$
+? intnum(x = 0, [oo,1],  g(x)) - Euler
+%2 = 0.E-106 \\@com perfect
+ at eprog\noindent
+It is up to the user to determine constants such as the $10^{-18}$ and $7$
+used above.
+
+\misctitle{True singularities} With true singularities the result is worse.
+For instance
+
+\bprog
+? intnum(x = 0, 1,  1/sqrt(x)) - 2
+%1 = -1.92... E-59 \\@com only $59$ correct decimals
+
+? intnum(x = [0,-1/2], 1,  1/sqrt(x)) - 2
+%2 = 0.E-105 \\@com better
+ at eprog
+
+\misctitle{Oscillating functions}
+
+\bprog
+? intnum(x = 0, oo, sin(x) / x) - Pi/2
+%1 = 20.78.. \\@com nonsense
+? intnum(x = 0, [oo,1], sin(x)/x) - Pi/2
+%2 = 0.004.. \\@com bad
+? intnum(x = 0, [oo,-I], sin(x)/x) - Pi/2
+%3 = 0.E-105 \\@com perfect
+? intnum(x = 0, [oo,-I], sin(2*x)/x) - Pi/2  \\@com oops, wrong $k$
+%4 = 0.07...
+? intnum(x = 0, [oo,-2*I], sin(2*x)/x) - Pi/2
+%5 = 0.E-105 \\@com perfect
+
+? intnum(x = 0, [oo,-I], sin(x)^3/x) - Pi/4
+%6 = 0.0092... \\@com bad
+? sin(x)^3 - (3*sin(x)-sin(3*x))/4
+%7 = O(x^17)
+ at eprog\noindent
+We may use the above linearization and compute two oscillating integrals with
+``infinite endpoints'' \kbd{[oo, -I]} and \kbd{[oo, -3*I]} respectively, or
+notice the obvious change of variable, and reduce to the single integral
+${1\over 2}\int_0^\infty \sin(x)/x\,dx$. We finish with some more complicated
+examples:
+
+\bprog
+? intnum(x = 0, [oo,-I], (1-cos(x))/x^2) - Pi/2
+%1 = -0.0004... \\@com bad
+? intnum(x = 0, 1, (1-cos(x))/x^2) \
++ intnum(x = 1, oo, 1/x^2) - intnum(x = 1, [oo,I], cos(x)/x^2) - Pi/2
+%2 = -2.18... E-106 \\@com OK
+
+? intnum(x = 0, [oo, 1], sin(x)^3*exp(-x)) - 0.3
+%3 = 5.45... E-107 \\@com OK
+? intnum(x = 0, [oo,-I], sin(x)^3*exp(-x)) - 0.3
+%4 = -1.33... E-89 \\@com lost 16 decimals. Try higher $m$:
+? m = intnumstep()
+%5 = 7 \\@com the value of $m$ actually used above.
+? tab = intnuminit(0,[oo,-I], m+1); \\@com try $m$ one higher.
+? intnum(x = 0, oo, sin(x)^3*exp(-x), tab) - 0.3
+%6 = 5.45... E-107 \\@com OK this time.
+ at eprog
+
+\misctitle{Warning} Like \tet{sumalt}, \kbd{intnum} often assigns a
+reasonable value to diverging integrals. Use these values at your own risk!
+For example:
+
+\bprog
+? intnum(x = 0, [oo, -I], x^2*sin(x))
+%1 = -2.0000000000...
+ at eprog\noindent
+Note the formula
+$$ \int_0^\infty \sin(x)/x^s\,dx = \cos(\pi s/2) \Gamma(1-s)\;, $$
+a priori valid only for $0 < \Re(s) < 2$, but the right hand side provides an
+analytic continuation which may be evaluated at $s = -2$\dots
+
+\misctitle{Multivariate integration}
+Using successive univariate integration with respect to different formal
+parameters, it is immediate to do naive multivariate integration. But it is
+important to use a suitable \kbd{intnuminit} to precompute data for the
+\emph{internal} integrations at least!
+
+For example, to compute the double integral on the unit disc $x^2+y^2\le1$
+of the function $x^2+y^2$, we can write
+\bprog
+? tab = intnuminit(-1,1);
+? intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2, tab), tab)
+ at eprog\noindent
+The first \var{tab} is essential, the second optional. Compare:
+
+\bprog
+? tab = intnuminit(-1,1);
+time = 30 ms.
+? intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2));
+time = 54,410 ms. \\@com slow
+? intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2, tab), tab);
+time = 7,210 ms.  \\@com faster
+ at eprog\noindent
+However, the \kbd{intnuminit} program is usually pessimistic when it comes to
+choosing the integration step $2^{-m}$. It is often possible to improve the
+speed by trial and error. Continuing the above example:
+\bprog
+? test(M) =
+{
+tab = intnuminit(-1,1, M);
+intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2,tab), tab) - Pi/2
+}
+? m = intnumstep() \\@com what value of $m$ did it take?
+%1 = 7
+? test(m - 1)
+time = 1,790 ms.
+%2 = -2.05... E-104 \\@com $4 = 2^2$ times faster and still OK.
+? test(m - 2)
+time = 430 ms.
+%3 = -1.11... E-104 \\@com $16 = 2^4$ times faster and still OK.
+? test(m - 3)
+time = 120 ms.
+%3 = -7.23... E-60 \\@com $64 = 2^6$ times faster, lost $45$ decimals.
+ at eprog
+
+\synt{intnum}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN b,GEN tab, long prec},
+where an omitted \var{tab} is coded as \kbd{NULL}.
+
+\subsec{intnuminit$(a,b,\{m=0\})$}\kbdsidx{intnuminit}\label{se:intnuminit}
+Initialize tables for integration from
+$a$ to $b$, where $a$ and $b$ are coded as in \kbd{intnum}. Only the
+compactness, the possible existence of singularities, the speed of decrease
+or the oscillations at infinity are taken into account, and not the values.
+For instance {\tt intnuminit(-1,1)} is equivalent to {\tt intnuminit(0,Pi)},
+and {\tt intnuminit([0,-1/2],[1])} is equivalent to {\tt
+intnuminit([-1],[-1,-1/2])}. If $m$ is not given, it is computed according to
+the current precision. Otherwise the integration step is $1/2^m$. Reasonable
+values of $m$ are $m=6$ or $m=7$ for $100$ decimal digits, and $m=9$ for
+$1000$ decimal digits.
+
+The result is technical, but in some cases it is useful to know the output.
+Let $x=\phi(t)$ be the change of variable which is used. \var{tab}[1] contains
+the integer $m$ as above, either given by the user or computed from the default
+precision, and can be recomputed directly using the function \kbd{intnumstep}.
+\var{tab}[2] and \var{tab}[3] contain respectively the abscissa and weight
+corresponding to $t=0$ ($\phi(0)$ and $\phi'(0)$). \var{tab}[4] and
+\var{tab}[5] contain the abscissas and weights corresponding to positive
+$t=nh$ for $1\le n\le N$ and $h=1/2^m$ ($\phi(nh)$ and $\phi'(nh)$). Finally
+\var{tab}[6] and \var{tab}[7] contain either the abscissas and weights
+corresponding to negative $t=nh$ for $-N\le n\le -1$, or may be empty (but
+not always) if $\phi(t)$ is an odd function (implicitly we would have
+$\var{tab}[6]=-\var{tab}[4]$ and $\var{tab}[7]=\var{tab}[5]$).
+
+The library syntax is \fun{GEN}{intnuminit}{GEN a, GEN b, long m, long prec}.
+
+\subsec{intnuminitgen$(t,a,b,\var{ph},\{m=0\},\{\fl=0\})$}\kbdsidx{intnuminitgen}\label{se:intnuminitgen}
+Initialize tables for integrations from $a$ to $b$ using abscissas
+$ph(t)$ and weights $ph'(t)$. Note that there is no equal sign after the
+variable name $t$ since $t$ always goes from $-\infty$ to $+\infty$, but it
+is $ph(t)$ which goes from $a$ to $b$, and this is not checked. If \fl = 1
+or 2, multiply the reserved table length by $4^{\fl}$, to avoid corresponding
+error.
+
+\synt{intnuminitgen}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, long m, long flag, long prec}
+
+\subsec{intnumromb$(X=a,b,\var{expr},\{\fl=0\})$}\kbdsidx{intnumromb}\label{se:intnumromb}
+Numerical integration of \var{expr} (smooth in $]a,b[$), with respect to
+$X$. Suitable for low accuracy; if \var{expr} is very regular (e.g. analytic
+in a large region) and high accuracy is desired, try \tet{intnum} first.
+
+Set $\fl=0$ (or omit it altogether) when $a$ and $b$ are not too large, the
+function is smooth, and can be evaluated exactly everywhere on the interval
+$[a,b]$.
+
+If $\fl=1$, uses a general driver routine for doing numerical integration,
+making no particular assumption (slow).
+
+$\fl=2$ is tailored for being used when $a$ or $b$ are infinite. One
+\emph{must} have $ab>0$, and in fact if for example $b=+\infty$, then it is
+preferable to have $a$ as large as possible, at least $a\ge1$.
+
+If $\fl=3$, the function is allowed to be undefined (but continuous) at $a$
+or $b$, for example the function $\sin(x)/x$ at $x=0$.
+
+The user should not require too much accuracy: 18 or 28 decimal digits is OK,
+but not much more. In addition, analytical cleanup of the integral must have
+been done: there must be no singularities in the interval or at the
+boundaries. In practice this can be accomplished with a simple change of
+variable. Furthermore, for improper integrals, where one or both of the
+limits of integration are plus or minus infinity, the function must decrease
+sufficiently rapidly at infinity. This can often be accomplished through
+integration by parts. Finally, the function to be integrated should not be
+very small (compared to the current precision) on the entire interval. This
+can of course be accomplished by just multiplying by an appropriate constant.
+
+Note that \idx{infinity} can be represented with essentially no loss of
+accuracy by an appropriate huge number. However beware of real underflow
+when dealing with rapidly decreasing functions. For example, in order to
+compute the $\int_0^\infty e^{-x^2}\,dx$ to 28 decimal digits, then one can
+set infinity equal to 10 for example, and certainly not to \kbd{1e1000}.
+
+\synt{intnumromb}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, long flag, long prec},
+where $\kbd{eval}(x, E)$ returns the value of the function at $x$.
+You may store any additional information required by \kbd{eval} in $E$, or set
+it to \kbd{NULL}.
+
+\subsec{intnumstep$()$}\kbdsidx{intnumstep}\label{se:intnumstep}
+Give the value of $m$ used in all the
+\kbd{intnum} and \kbd{sumnum} programs, hence such that the integration
+step is equal to $1/2^m$.
+
+The library syntax is \fun{long}{intnumstep}{long prec}.
+
+\subsec{prod$(X=a,b,\var{expr},\{x=1\})$}\kbdsidx{prod}\label{se:prod}
+Product of expression
+\var{expr}, initialized at $x$, the formal parameter $X$ going from $a$ to
+$b$. As for \kbd{sum}, the main purpose of the initialization parameter $x$
+is to force the type of the operations being performed. For example if it is
+set equal to the integer 1, operations will start being done exactly. If it
+is set equal to the real $1.$, they will be done using real numbers having
+the default precision. If it is set equal to the power series $1+O(X^k)$ for
+a certain $k$, they will be done using power series of precision at most $k$.
+These are the three most common initializations.
+
+\noindent As an extreme example, compare
+
+\bprog
+? prod(i=1, 100, 1 - X^i);  \\@com this has degree $5050$ !!
+time = 128 ms.
+? prod(i=1, 100, 1 - X^i, 1 + O(X^101))
+time = 8 ms.
+%2 = 1 - X - X^2 + X^5 + X^7 - X^12 - X^15 + X^22 + X^26 - X^35 - X^40 + \
+X^51 + X^57 - X^70 - X^77 + X^92 + X^100 + O(X^101)
+ at eprog\noindent
+Of course, in  this specific case, it is faster to use \tet{eta},
+which is computed using Euler's formula.
+\bprog
+? prod(i=1, 1000, 1 - X^i, 1 + O(X^1001));
+time = 589 ms.
+? \ps1000
+seriesprecision = 1000 significant terms
+? eta(X) - %
+time = 8ms.
+%4 = O(X^1001)
+ at eprog
+
+\synt{produit}{GEN a, GEN b, char *expr, GEN x}.
+
+\subsec{prodeuler$(X=a,b,\var{expr})$}\kbdsidx{prodeuler}\label{se:prodeuler}
+Product of expression \var{expr},
+initialized at 1. (i.e.~to a \emph{real} number equal to 1 to the current
+\kbd{realprecision}), the formal parameter $X$ ranging over the prime numbers
+between $a$ and $b$.\sidx{Euler product}
+
+\synt{prodeuler}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN b, long prec}.
+
+\subsec{prodinf$(X=a,\var{expr},\{\fl=0\})$}\kbdsidx{prodinf}\label{se:prodinf}
+\idx{infinite product} of
+expression \var{expr}, the formal parameter $X$ starting at $a$. The evaluation
+stops when the relative error of the expression minus 1 is less than the
+default precision. In particular, non-convergent products result in infinite
+loops. The expressions must always evaluate to an element of $\C$.
+
+If $\fl=1$, do the product of the ($1+\var{expr}$) instead.
+
+\synt{prodinf}{void *E, GEN (*eval)(void*,GEN), GEN a, long prec}
+($\fl=0$), or \tet{prodinf1} with the same arguments ($\fl=1$).
+
+\subsec{solve$(X=a,b,\var{expr})$}\kbdsidx{solve}\label{se:solve}
+Find a real root of expression
+\var{expr} between $a$ and $b$, under the condition
+$\var{expr}(X=a) * \var{expr}(X=b) \le 0$. (You will get an error message
+\kbd{roots must be bracketed in solve} if this does not hold.)
+This routine uses Brent's method and can fail miserably if \var{expr} is
+not defined in the whole of $[a,b]$ (try \kbd{solve(x=1, 2, tan(x))}).
+
+\synt{zbrent}{void *E,GEN (*eval)(void*,GEN),GEN a,GEN b,long prec}.
+
+\subsec{sum$(X=a,b,\var{expr},\{x=0\})$}\kbdsidx{sum}\label{se:sum}
+Sum of expression \var{expr},
+initialized at $x$, the formal parameter going from $a$ to $b$. As for
+\kbd{prod}, the initialization parameter $x$ may be given to force the type
+of the operations being performed.
+
+\noindent As an extreme example, compare
+
+\bprog
+? sum(i=1, 10^4, 1/i); \\@com rational number: denominator has $4345$ digits.
+time = 236 ms.
+? sum(i=1, 5000, 1/i, 0.)
+time = 8 ms.
+%2 = 9.787606036044382264178477904
+ at eprog
+
+\synt{somme}{GEN a, GEN b, char *expr, GEN x}.
+
+\subsec{sumalt$(X=a,\var{expr},\{\fl=0\})$}\kbdsidx{sumalt}\label{se:sumalt}
+Numerical summation of the series \var{expr}, which should be an
+\idx{alternating series}, the formal variable $X$ starting at $a$. Use an
+algorithm of Cohen, Villegas and Zagier (\emph{Experiment. Math.} {\bf 9}
+(2000), no.~1, 3--12).
+
+If $\fl=1$, use a variant with slightly different polynomials. Sometimes
+faster.
+
+The routine is heuristic and a rigorous proof assumes that the values of
+\var{expr} are the moments of a positive measure on $[0,1]$. Divergent
+alternating series can sometimes be summed by this method, as well as series
+which are not exactly alternating (see for example
+\secref{se:user_defined}). It should be used to try and guess the value of
+an infinite sum. (However, see the example at the end of
+\secref{se:userfundef}.)
+
+If the series already converges geometrically,
+\tet{suminf} is often a better choice:
+\bprog
+? \p28
+? sumalt(i = 1, -(-1)^i / i)  - log(2)
+time = 0 ms.
+%1 = -2.524354897 E-29
+? suminf(i = 1, -(-1)^i / i)   \\@com Had to hit <C-C>
+  ***   at top-level: suminf(i=1,-(-1)^i/i)
+  ***                                ^------
+  *** suminf: user interrupt after 10min, 20,100 ms.
+? \p1000
+? sumalt(i = 1, -(-1)^i / i)  - log(2)
+time = 90 ms.
+%2 = 4.459597722 E-1002
+
+? sumalt(i = 0, (-1)^i / i!) - exp(-1)
+time = 670 ms.
+%3 = -4.03698781490633483156497361352190615794353338591897830587 E-944
+? suminf(i = 0, (-1)^i / i!) - exp(-1)
+time = 110 ms.
+%4 = -8.39147638 E-1000   \\ @com faster and more accurate
+ at eprog
+
+\synt{sumalt}{void *E, GEN (*eval)(void*,GEN),GEN a,long prec}. Also
+available is \tet{sumalt2} with the same arguments ($\fl = 1$).
+
+\subsec{sumdiv$(n,X,\var{expr})$}\kbdsidx{sumdiv}\label{se:sumdiv}
+Sum of expression \var{expr} over the positive divisors of $n$.
+This function is a trivial wrapper essentially equivalent to
+\bprog
+  D = divisors(n);
+  for (i = 1, #D, X = D[i]; eval(expr))
+ at eprog\noindent (except that \kbd{X} is lexically scoped to the \kbd{sumdiv}
+loop). If \var{expr} is a multiplicative function, use \tet{sumdivmult}.
+%\syn{NO}
+
+\subsec{sumdivmult$(n,d,\var{expr})$}\kbdsidx{sumdivmult}\label{se:sumdivmult}
+Sum of \emph{multiplicative} expression \var{expr} over the positive
+divisors $d$ of $n$. Assume that \var{expr} evaluates to $f(d)$
+where $f$ is multiplicative: $f(1) = 1$ and $f(ab) = f(a)f(b)$ for coprime
+$a$ and $b$.
+%\syn{NO}
+
+\subsec{suminf$(X=a,\var{expr})$}\kbdsidx{suminf}\label{se:suminf}
+\idx{infinite sum} of expression
+\var{expr}, the formal parameter $X$ starting at $a$. The evaluation stops
+when the relative error of the expression is less than the default precision
+for 3 consecutive evaluations. The expressions must always evaluate to a
+complex number.
+
+If the series converges slowly, make sure \kbd{realprecision} is low (even 28
+digits may be too much). In this case, if the series is alternating or the
+terms have a constant sign, \tet{sumalt} and \tet{sumpos} should be used
+instead.
+
+\bprog
+? \p28
+? suminf(i = 1, -(-1)^i / i)   \\@com Had to hit <C-C>
+  ***   at top-level: suminf(i=1,-(-1)^i/i)
+  ***                                ^------
+  *** suminf: user interrupt after 10min, 20,100 ms.
+? sumalt(i = 1, -(-1)^i / i) - log(2)
+time = 0 ms.
+%1 = -2.524354897 E-29
+ at eprog
+
+\synt{suminf}{void *E, GEN (*eval)(void*,GEN), GEN a, long prec}.
+
+\subsec{sumnum$(X=a,\var{sig},\var{expr},\{\var{tab}\},\{\fl=0\})$}\kbdsidx{sumnum}\label{se:sumnum}
+Numerical summation of \var{expr}, the variable $X$ taking integer values
+from ceiling of $a$ to $+\infty$, where \var{expr} is assumed to be a
+holomorphic function $f(X)$ for $\Re(X)\ge \sigma$.
+
+The parameter $\sigma\in\R$ is coded in the argument \kbd{sig} as follows: it
+is either
+
+\item a real number $\sigma$. Then the function $f$ is assumed to
+decrease at least as $1/X^2$ at infinity, but not exponentially;
+
+\item a two-component vector $[\sigma,\alpha]$, where $\sigma$ is as
+before, $\alpha < -1$. The function $f$ is assumed to decrease like
+$X^{\alpha}$. In particular, $\alpha\le-2$ is equivalent to no $\alpha$ at all.
+
+\item a two-component vector $[\sigma,\alpha]$, where $\sigma$ is as
+before, $\alpha > 0$. The function $f$ is assumed to decrease like
+$\exp(-\alpha X)$. In this case it is essential that $\alpha$ be exactly the
+rate of exponential decrease, and it is usually a good idea to increase
+the default value of $m$ used for the integration step. In practice, if
+the function is exponentially decreasing \kbd{sumnum} is slower and less
+accurate than \kbd{sumpos} or \kbd{suminf}, so should not be used.
+
+The function uses the \tet{intnum} routines and integration on the line
+$\Re(s) = \sigma$. The optional argument \var{tab} is as in intnum, except it
+must be initialized with \kbd{sumnuminit} instead of \kbd{intnuminit}.
+
+When \var{tab} is not precomputed, \kbd{sumnum} can be slower than
+\kbd{sumpos}, when the latter is applicable. It is in general faster for
+slowly decreasing functions.
+
+Finally, if $\fl$ is nonzero, we assume that the function $f$ to be summed is
+of real type, i.e. satisfies $\overline{f(z)}=f(\overline{z})$, which
+speeds up the computation.
+
+\bprog
+? \p 308
+? a = sumpos(n=1, 1/(n^3+n+1));
+time = 1,410 ms.
+? tab = sumnuminit(2);
+time = 1,620 ms. \\@com slower but done once and for all.
+? b = sumnum(n=1, 2, 1/(n^3+n+1), tab);
+time = 460 ms. \\@com 3 times as fast as \kbd{sumpos}
+? a - b
+%4 = -1.0... E-306 + 0.E-320*I \\@com perfect.
+? sumnum(n=1, 2, 1/(n^3+n+1), tab, 1) - a; \\@com function of real type
+time = 240 ms.
+%2 = -1.0... E-306 \\@com twice as fast, no imaginary part.
+? c = sumnum(n=1, 2, 1/(n^2+1), tab, 1);
+time = 170 ms. \\@com fast
+? d = sumpos(n=1, 1 / (n^2+1));
+time = 2,700 ms. \\@com slow.
+? d - c
+time = 0 ms.
+%5 = 1.97... E-306 \\@com perfect.
+ at eprog
+
+For slowly decreasing function, we must indicate singularities:
+\bprog
+? \p 308
+? a = sumnum(n=1, 2, n^(-4/3));
+time = 9,930 ms. \\@com slow because of the computation of $n^{-4/3}$.
+? a - zeta(4/3)
+time = 110 ms.
+%1 = -2.42... E-107 \\@com lost 200 decimals because of singularity at $\infty$
+? b = sumnum(n=1, [2,-4/3], n^(-4/3), /*omitted*/, 1); \\@com of real type
+time = 12,210 ms.
+? b - zeta(4/3)
+%3 = 1.05... E-300 \\@com better
+ at eprog
+
+Since the \emph{complex} values of the function are used, beware of
+determination problems. For instance:
+\bprog
+? \p 308
+? tab = sumnuminit([2,-3/2]);
+time = 1,870 ms.
+? sumnum(n=1,[2,-3/2], 1/(n*sqrt(n)), tab,1) - zeta(3/2)
+time = 690 ms.
+%1 = -1.19... E-305 \\@com fast and correct
+? sumnum(n=1,[2,-3/2], 1/sqrt(n^3), tab,1) - zeta(3/2)
+time = 730 ms.
+%2 = -1.55... \\@com nonsense. However
+? sumnum(n=1,[2,-3/2], 1/n^(3/2), tab,1) - zeta(3/2)
+time = 8,990 ms.
+%3 = -1.19... E-305 \\@com perfect, as $1/(n*\sqrt{n})$ above but much slower
+ at eprog
+
+For exponentially decreasing functions, \kbd{sumnum} is given for
+completeness, but one of \tet{suminf} or \tet{sumpos} should always be
+preferred. If you experiment with such functions and \kbd{sumnum} anyway,
+indicate the exact rate of decrease and increase $m$ by $1$ or $2$:
+
+\bprog
+? suminf(n=1, 2^(-n)) - 1
+time = 10 ms.
+%1 = -1.11... E-308 \\@com fast and perfect
+? sumpos(n=1, 2^(-n)) - 1
+time = 10 ms.
+%2 = -2.78... E-308 \\@com also fast and perfect
+? sumnum(n=1,2, 2^(-n)) - 1
+%3 = -1.321115060 E320 + 0.E311*I \\@com nonsense
+? sumnum(n=1, [2,log(2)], 2^(-n), /*omitted*/, 1) - 1 \\@com of real type
+time = 5,860 ms.
+%4 = -1.5... E-236 \\@com slow and lost $70$ decimals
+? m = intnumstep()
+%5 = 9
+? sumnum(n=1,[2,log(2)], 2^(-n), m+1, 1) - 1
+time = 11,770 ms.
+%6 = -1.9... E-305 \\@com now perfect, but slow.
+ at eprog
+
+\synt{sumnum}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN sig,GEN tab,long flag, long prec}.
+
+\subsec{sumnumalt$(X=a,\var{sig},\var{expr},\{\var{tab}\},\{\fl=0\})$}\kbdsidx{sumnumalt}\label{se:sumnumalt}
+Numerical
+summation of $(-1)^X\var{expr}(X)$, the variable $X$ taking integer values from
+ceiling of $a$ to $+\infty$, where \var{expr} is assumed to be a holomorphic
+function for $\Re(X)\ge sig$ (or $sig[1]$).
+
+\misctitle{Warning} This function uses the \kbd{intnum} routines and is
+orders of magnitude slower than \kbd{sumalt}. It is only given for
+completeness and should not be used in practice.
+
+\misctitle{Warning 2} The expression \var{expr} must \emph{not} include the
+$(-1)^X$ coefficient. Thus $\kbd{sumalt}(n=a,(-1)^nf(n))$ is (approximately)
+equal to $\kbd{sumnumalt}(n=a,sig,f(n))$.
+
+$sig$ is coded as in \kbd{sumnum}. However for slowly decreasing functions
+(where $sig$ is coded as $[\sigma,\alpha]$ with $\alpha<-1$), it is not
+really important to indicate $\alpha$. In fact, as for \kbd{sumalt}, the
+program will often give meaningful results (usually analytic continuations)
+even for divergent series. On the other hand the exponential decrease must be
+indicated.
+
+\var{tab} is as in \kbd{intnum}, but if used must be initialized with
+\kbd{sumnuminit}. If $\fl$ is nonzero, assumes that the function $f$ to be
+summed is of real type, i.e. satisfies $\overline{f(z)}=f(\overline{z})$, and
+then twice faster when \var{tab} is precomputed.
+
+\bprog
+? \p 308
+? tab = sumnuminit(2, /*omitted*/, -1); \\@com abscissa $\sigma=2$, alternating sums.
+time = 1,620 ms. \\@com slow, but done once and for all.
+? a = sumnumalt(n=1, 2, 1/(n^3+n+1), tab, 1);
+time = 230 ms. \\@com similar speed to \kbd{sumnum}
+? b = sumalt(n=1, (-1)^n/(n^3+n+1));
+time = 0 ms. \\@com infinitely faster!
+? a - b
+time = 0 ms.
+%1 = -1.66... E-308 \\@com perfect
+ at eprog
+
+\synt{sumnumalt}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN sig, GEN tab, long flag, long prec}.
+
+\subsec{sumnuminit$(\var{sig}, \{m=0\}, \{\var{sgn}=1\})$}\kbdsidx{sumnuminit}\label{se:sumnuminit}
+Initialize tables for numerical summation using \kbd{sumnum} (with
+$\var{sgn}=1$) or \kbd{sumnumalt} (with $\var{sgn}=-1$), $sig$ is the
+abscissa of integration coded as in \kbd{sumnum}, and $m$ is as in
+\kbd{intnuminit}.
+
+The library syntax is \fun{GEN}{sumnuminit}{GEN sig, long m, long sgn, long prec}.
+
+\subsec{sumpos$(X=a,\var{expr},\{\fl=0\})$}\kbdsidx{sumpos}\label{se:sumpos}
+Numerical summation of the series \var{expr}, which must be a series of
+terms having the same sign, the formal variable $X$ starting at $a$. The
+algorithm used is Van Wijngaarden's trick for converting such a series into
+an alternating one, then we use \tet{sumalt}. For regular functions, the
+function \kbd{sumnum} is in general much faster once the initializations
+have been made using \kbd{sumnuminit}.
+
+The routine is heuristic and assumes that \var{expr} is more or less a
+decreasing function of $X$. In particular, the result will be completely
+wrong if \var{expr} is 0 too often. We do not check either that all terms
+have the same sign. As \tet{sumalt}, this function should be used to
+try and guess the value of an infinite sum.
+
+If $\fl=1$, use slightly different polynomials. Sometimes faster.
+
+\synt{sumpos}{void *E, GEN (*eval)(void*,GEN),GEN a,long prec}. Also
+available is \tet{sumpos2} with the same arguments ($\fl = 1$).
+%SECTION: sums
+
+\section{Plotting functions}
+
+  Although plotting is not even a side purpose of PARI, a number of plotting
+functions are provided. Moreover, a lot of people
+\footnote{*}{Among these, special thanks go to Klaus-Peter Nischke who
+suggested the recursive plotting and forking/resizing stuff the graphical
+window, and Ilya Zakharevich who rewrote the graphic code from scratch
+implementing many new primitives (splines, clipping). Nils Skoruppa and Bill
+Allombert wrote the \tet{Qt} and \tet{fltk} graphic drivers respectively.}
+suggested ideas or submitted patches for this section of the code. There are
+three types of graphic functions.
+
+\subsec{High-level plotting functions} (all the functions starting with
+\kbd{ploth}) in which the user has little to do but explain what type of plot
+he wants, and whose syntax is similar to the one used in the preceding
+section.
+
+\subsec{Low-level plotting functions} (called \var{rectplot} functions,
+sharing the prefix \kbd{plot}), where every drawing primitive (point, line,
+box, etc.) is specified by the user. These low-level functions work as
+follows. You have at your disposal 16 virtual windows which are filled
+independently, and can then be physically ORed on a single window at
+user-defined positions. These windows are numbered from 0 to 15, and must be
+initialized before being used by the function \kbd{plotinit}, which specifies
+the height and width of the virtual window (called a \var{rectwindow} in the
+sequel). At all times, a virtual cursor (initialized at $[0,0]$) is associated
+to the window, and its current value can be obtained using the function
+\kbd{plotcursor}.
+
+A number of primitive graphic objects (called \var{rect} objects) can then
+be drawn in these windows, using a default color associated to that window
+(which can be changed using the \kbd{plotcolor} function) and only the part
+of the object which is inside the window will be drawn, with the exception of
+polygons and strings which are drawn entirely. The ones sharing the prefix
+\kbd{plotr} draw relatively to the current position of the virtual cursor,
+the others use absolute coordinates. Those having the prefix \kbd{plotrecth}
+put in the rectwindow a large batch of rect objects corresponding to the
+output of the related \kbd{ploth} function.
+
+   Finally, the actual physical drawing is done using \kbd{plotdraw}. The
+rectwindows are preserved so that further drawings using the same windows at
+different positions or different windows can be done without extra work. To
+erase a window, use \kbd{plotkill}. It is not possible to partially erase a
+window: erase it completely, initialize it again, then fill it with the
+graphic objects that you want to keep.
+
+   In addition to initializing the window, you may use a scaled window to
+avoid unnecessary conversions. For this, use \kbd{plotscale}. As long as this
+function is not called, the scaling is simply the number of pixels, the
+origin being at the upper left and the $y$-coordinates going downwards.
+
+   Plotting functions are platform independent, but a number of graphical
+drivers are available for screen output: X11-windows (hence also for GUI's
+based on X11 such as Openwindows and Motif), and the Qt and FLTK graphical
+libraries. The physical window opened by \kbd{plotdraw} or any of the
+\kbd{ploth*} functions is completely separated from \kbd{gp} (technically, a
+\kbd{fork} is done, and the non-graphical memory is immediately freed in the
+child process), which means you can go on working in the current \kbd{gp}
+session, without having to kill the window first. This window can be closed,
+enlarged or reduced using the standard window manager functions. No zooming
+procedure is implemented though (yet).
+
+\subsec{Functions for PostScript output} in the same way that \kbd{printtex} allows you to have a \TeX\ output
+corresponding to printed results, the functions starting with \kbd{ps} allow
+you to have \tet{PostScript} output of the plots. This will not be identical
+with the screen output, but sufficiently close. Note that you can use
+PostScript output even if you do not have the plotting routines enabled. The
+PostScript output is written in a file whose name is derived from the
+\tet{psfile} default (\kbd{./pari.ps} if you did not tamper with it). Each
+time a new PostScript output is asked for, the PostScript output is appended
+to that file. Hence you probably want to remove this file, or change the
+value of \kbd{psfile}, in between plots. On the other hand, in this manner,
+as many plots as desired can be kept in a single file. \smallskip
+
+\subsec{Library mode} \emph{None of the graphic functions are available
+within the PARI library, you must be under \kbd{gp} to use them}. The reason
+for that is that you really should not use PARI for heavy-duty graphical work,
+there are better specialized alternatives around. This whole set of routines
+was only meant as a convenient, but simple-minded, visual aid. If you really
+insist on using these in your program (we warned you), the source
+(\kbd{plot*.c}) should be readable enough for you to achieve something.
+
+
+\subsec{plot$(X=a,b,\var{expr},\{\var{Ymin}\},\{\var{Ymax}\})$}\kbdsidx{plot}\label{se:plot}
+Crude ASCII plot of the function represented by expression \var{expr}
+from $a$ to $b$, with \var{Y} ranging from \var{Ymin} to \var{Ymax}. If
+\var{Ymin} (resp. \var{Ymax}) is not given, the minimum (resp. the maximum)
+of the computed values of the expression is used instead.
+
+\subsec{plotbox$(w,\var{x2},\var{y2})$}\kbdsidx{plotbox}\label{se:plotbox}
+Let $(x1,y1)$ be the current position of the virtual cursor. Draw in the
+rectwindow $w$ the outline of the rectangle which is such that the points
+$(x1,y1)$ and $(x2,y2)$ are opposite corners. Only the part of the rectangle
+which is in $w$ is drawn. The virtual cursor does \emph{not} move.
+
+\subsec{plotclip$(w)$}\kbdsidx{plotclip}\label{se:plotclip}
+`clips' the content of rectwindow $w$, i.e remove all parts of the
+drawing that would not be visible on the screen. Together with
+\tet{plotcopy} this function enables you to draw on a scratchpad before
+committing the part you're interested in to the final picture.
+
+\subsec{plotcolor$(w,c)$}\kbdsidx{plotcolor}\label{se:plotcolor}
+Set default color to $c$ in rectwindow $w$.
+This is only implemented for the X-windows, fltk and Qt graphing engines.
+Possible values for $c$ are given by the \tet{graphcolormap} default,
+factory setting are
+
+1=black, 2=blue, 3=violetred, 4=red, 5=green, 6=grey, 7=gainsborough.
+
+but this can be considerably extended.
+
+\subsec{plotcopy$(\var{sourcew},\var{destw},\var{dx},\var{dy},\{\fl=0\})$}\kbdsidx{plotcopy}\label{se:plotcopy}
+Copy the contents of rectwindow \var{sourcew} to rectwindow \var{destw}
+with offset (dx,dy). If flag's bit 1 is set, dx and dy express fractions of
+the size of the current output device, otherwise dx and dy are in pixels. dx
+and dy are relative positions of northwest corners if other bits of flag
+vanish, otherwise of: 2: southwest, 4: southeast, 6: northeast corners
+
+\subsec{plotcursor$(w)$}\kbdsidx{plotcursor}\label{se:plotcursor}
+Give as a 2-component vector the current
+(scaled) position of the virtual cursor corresponding to the rectwindow $w$.
+
+\subsec{plotdraw$(\var{list}, \{\fl=0\})$}\kbdsidx{plotdraw}\label{se:plotdraw}
+Physically draw the rectwindows given in $list$
+which must be a vector whose number of components is divisible by 3. If
+$list=[w1,x1,y1,w2,x2,y2,\dots]$, the windows $w1$, $w2$, etc.~are
+physically placed with their upper left corner at physical position
+$(x1,y1)$, $(x2,y2)$,\dots\ respectively, and are then drawn together.
+Overlapping regions will thus be drawn twice, and the windows are considered
+transparent. Then display the whole drawing in a special window on your
+screen. If $\fl \neq 0$, x1, y1 etc. express fractions of the size of the
+current output device
+
+\subsec{ploth$(X=a,b,\var{expr},\{\var{flags}=0\},\{n=0\})$}\kbdsidx{ploth}\label{se:ploth}
+High precision plot of the function $y=f(x)$ represented by the expression
+\var{expr}, $x$ going from $a$ to $b$. This opens a specific window (which is
+killed whenever you click on it), and returns a four-component vector giving
+the coordinates of the bounding box in the form
+$[\var{xmin},\var{xmax},\var{ymin},\var{ymax}]$.
+
+\misctitle{Important note} \kbd{ploth} may evaluate \kbd{expr} thousands of
+times; given the relatively low resolution of plotting devices, few
+significant digits of the result will be meaningful. Hence you should keep
+the current precision to a minimum (e.g.~9) before calling this function.
+
+$n$ specifies the number of reference point on the graph, where a value of 0
+means we use the hardwired default values (1000 for general plot, 1500 for
+parametric plot, and 8 for recursive plot).
+
+If no $\fl$ is given, \var{expr} is either a scalar expression $f(X)$, in which
+case the plane curve $y=f(X)$ will be drawn, or a vector
+$[f_1(X),\dots,f_k(X)]$, and then all the curves $y=f_i(X)$ will be drawn in
+the same window.
+
+\noindent The binary digits of $\fl$ mean:
+
+\item $1 = \kbd{Parametric}$: \tev{parametric plot}. Here \var{expr} must
+be a vector with an even number of components. Successive pairs are then
+understood as the parametric coordinates of a plane curve. Each of these are
+then drawn.
+
+For instance:
+\bprog
+ploth(X=0,2*Pi,[sin(X),cos(X)], "Parametric")
+ploth(X=0,2*Pi,[sin(X),cos(X)])
+ploth(X=0,2*Pi,[X,X,sin(X),cos(X)], "Parametric")
+ at eprog\noindent draw successively a circle, two entwined sinusoidal curves
+and a circle cut by the line $y=x$.
+
+\item $2 = \kbd{Recursive}$: \tev{recursive plot}. If this flag is set,
+only \emph{one} curve can be drawn at a time, i.e.~\var{expr} must be either a
+two-component vector (for a single parametric curve, and the parametric flag
+\emph{has} to be set), or a scalar function. The idea is to choose pairs of
+successive reference points, and if their middle point is not too far away
+from the segment joining them, draw this as a local approximation to the
+curve. Otherwise, add the middle point to the reference points. This is
+fast, and usually more precise than usual plot. Compare the results of
+\bprog
+ploth(X=-1,1, sin(1/X), "Recursive")
+ploth(X=-1,1, sin(1/X))
+ at eprog\noindent
+for instance. But beware that if you are extremely unlucky, or choose too few
+reference points, you may draw some nice polygon bearing little resemblance
+to the original curve. For instance you should \emph{never} plot recursively
+an odd function in a symmetric interval around 0. Try
+\bprog
+ploth(x = -20, 20, sin(x), "Recursive")
+ at eprog\noindent
+to see why. Hence, it's usually a good idea to try and plot the same curve
+with slightly different parameters.
+
+The other values toggle various display options:
+
+\item $4 = \kbd{no\_Rescale}$: do not rescale plot according to the
+computed extrema. This is used in conjunction with \tet{plotscale} when
+graphing multiple functions on a rectwindow (as a \tet{plotrecth} call):
+\bprog
+  s = plothsizes();
+  plotinit(0, s[2]-1, s[2]-1);
+  plotscale(0, -1,1, -1,1);
+  plotrecth(0, t=0,2*Pi, [cos(t),sin(t)], "Parametric|no_Rescale")
+  plotdraw([0, -1,1]);
+ at eprog\noindent
+This way we get a proper circle instead of the distorted ellipse produced by
+\bprog
+  ploth(t=0,2*Pi, [cos(t),sin(t)], "Parametric")
+ at eprog
+
+\item $8 = \kbd{no\_X\_axis}$: do not print the $x$-axis.
+
+\item $16 = \kbd{no\_Y\_axis}$: do not print the $y$-axis.
+
+\item $32 = \kbd{no\_Frame}$: do not print frame.
+
+\item $64 = \kbd{no\_Lines}$: only plot reference points, do not join them.
+
+\item $128 = \kbd{Points\_too}$: plot both lines and points.
+
+\item $256 = \kbd{Splines}$: use splines to interpolate the points.
+
+\item $512 = \kbd{no\_X\_ticks}$: plot no $x$-ticks.
+
+\item $1024 = \kbd{no\_Y\_ticks}$: plot no $y$-ticks.
+
+\item $2048 = \kbd{Same\_ticks}$: plot all ticks with the same length.
+
+\item $4096 = \kbd{Complex}$: is a parametric plot but where each member of
+\kbd{expr} is considered a complex number encoding the two coordinates of a
+point. For instance:
+\bprog
+ploth(X=0,2*Pi,exp(I*X), "Complex")
+ploth(X=0,2*Pi,[(1+I)*X,exp(I*X)], "Complex")
+ at eprog\noindent will draw respectively a circle and a circle cut by the line
+$y=x$.
+
+\subsec{plothraw$(\var{listx},\var{listy},\{\fl=0\})$}\kbdsidx{plothraw}\label{se:plothraw}
+Given \var{listx} and \var{listy} two vectors of equal length, plots (in
+high precision) the points whose $(x,y)$-coordinates are given in
+\var{listx} and \var{listy}. Automatic positioning and scaling is done, but
+with the same scaling factor on $x$ and $y$. If $\fl$ is 1, join points,
+other non-0 flags toggle display options and should be combinations of bits
+$2^k$, $k \geq 3$ as in \kbd{ploth}.
+
+\subsec{plothsizes$(\{\fl=0\})$}\kbdsidx{plothsizes}\label{se:plothsizes}
+Return data corresponding to the output window
+in the form of a 6-component vector: window width and height, sizes for ticks
+in horizontal and vertical directions (this is intended for the \kbd{gnuplot}
+interface and is currently not significant), width and height of characters.
+
+If $\fl = 0$, sizes of ticks and characters are in
+pixels, otherwise are fractions of the screen size
+
+\subsec{plotinit$(w,\{x\},\{y\},\{\fl=0\})$}\kbdsidx{plotinit}\label{se:plotinit}
+Initialize the rectwindow $w$,
+destroying any rect objects you may have already drawn in $w$. The virtual
+cursor is set to $(0,0)$. The rectwindow size is set to width $x$ and height
+$y$; omitting either $x$ or $y$ means we use the full size of the device
+in that direction.
+If $\fl=0$, $x$ and $y$ represent pixel units. Otherwise, $x$ and $y$
+are understood as fractions of the size of the current output device (hence
+must be between $0$ and $1$) and internally converted to pixels.
+
+The plotting device imposes an upper bound for $x$ and $y$, for instance the
+number of pixels for screen output. These bounds are available through the
+\tet{plothsizes} function. The following sequence initializes in a portable
+way (i.e independent of the output device) a window of maximal size, accessed
+through coordinates in the $[0,1000] \times [0,1000]$ range:
+
+\bprog
+s = plothsizes();
+plotinit(0, s[1]-1, s[2]-1);
+plotscale(0, 0,1000, 0,1000);
+ at eprog
+
+\subsec{plotkill$(w)$}\kbdsidx{plotkill}\label{se:plotkill}
+Erase rectwindow $w$ and free the corresponding memory. Note that if you
+want to use the rectwindow $w$ again, you have to use \kbd{plotinit} first
+to specify the new size. So it's better in this case to use \kbd{plotinit}
+directly as this throws away any previous work in the given rectwindow.
+
+\subsec{plotlines$(w,X,Y,\{\fl=0\})$}\kbdsidx{plotlines}\label{se:plotlines}
+Draw on the rectwindow $w$
+the polygon such that the (x,y)-coordinates of the vertices are in the
+vectors of equal length $X$ and $Y$. For simplicity, the whole
+polygon is drawn, not only the part of the polygon which is inside the
+rectwindow. If $\fl$ is non-zero, close the polygon. In any case, the
+virtual cursor does not move.
+
+$X$ and $Y$ are allowed to be scalars (in this case, both have to).
+There, a single segment will be drawn, between the virtual cursor current
+position and the point $(X,Y)$. And only the part thereof which
+actually lies within the boundary of $w$. Then \emph{move} the virtual cursor
+to $(X,Y)$, even if it is outside the window. If you want to draw a
+line from $(x1,y1)$ to $(x2,y2)$ where $(x1,y1)$ is not necessarily the
+position of the virtual cursor, use \kbd{plotmove(w,x1,y1)} before using this
+function.
+
+\subsec{plotlinetype$(w,\var{type})$}\kbdsidx{plotlinetype}\label{se:plotlinetype}
+Change the type of lines subsequently plotted in rectwindow $w$.
+\var{type} $-2$ corresponds to frames, $-1$ to axes, larger values may
+correspond to something else. $w = -1$ changes highlevel plotting. This is
+only taken into account by the \kbd{gnuplot} interface.
+
+\subsec{plotmove$(w,x,y)$}\kbdsidx{plotmove}\label{se:plotmove}
+Move the virtual cursor of the rectwindow $w$ to position $(x,y)$.
+
+\subsec{plotpoints$(w,X,Y)$}\kbdsidx{plotpoints}\label{se:plotpoints}
+Draw on the rectwindow $w$ the
+points whose $(x,y)$-coordinates are in the vectors of equal length $X$ and
+$Y$ and which are inside $w$. The virtual cursor does \emph{not} move. This
+is basically the same function as \kbd{plothraw}, but either with no scaling
+factor or with a scale chosen using the function \kbd{plotscale}.
+
+As was the case with the \kbd{plotlines} function, $X$ and $Y$ are allowed to
+be (simultaneously) scalar. In this case, draw the single point $(X,Y)$ on
+the rectwindow $w$ (if it is actually inside $w$), and in any case
+\emph{move} the virtual cursor to position $(x,y)$.
+
+\subsec{plotpointsize$(w,\var{size})$}\kbdsidx{plotpointsize}\label{se:plotpointsize}
+Changes the ``size'' of following points in rectwindow $w$. If $w = -1$,
+change it in all rectwindows. This only works in the \kbd{gnuplot} interface.
+
+\subsec{plotpointtype$(w,\var{type})$}\kbdsidx{plotpointtype}\label{se:plotpointtype}
+Change the type of points subsequently plotted in rectwindow $w$.
+$\var{type} = -1$ corresponds to a dot, larger values may correspond to
+something else. $w = -1$ changes highlevel plotting. This is only taken into
+account by the \kbd{gnuplot} interface.
+
+\subsec{plotrbox$(w,\var{dx},\var{dy})$}\kbdsidx{plotrbox}\label{se:plotrbox}
+Draw in the rectwindow $w$ the outline of the rectangle which is such
+that the points $(x1,y1)$ and $(x1+dx,y1+dy)$ are opposite corners, where
+$(x1,y1)$ is the current position of the cursor. Only the part of the
+rectangle which is in $w$ is drawn. The virtual cursor does \emph{not} move.
+
+\subsec{plotrecth$(w,X=a,b,\var{expr},\{\fl=0\},\{n=0\})$}\kbdsidx{plotrecth}\label{se:plotrecth}
+Writes to rectwindow $w$ the curve output of
+\kbd{ploth}$(w,X=a,b,\var{expr},\fl,n)$. Returns a vector for the bounding box.
+
+\subsec{plotrecthraw$(w,\var{data},\{\var{flags}=0\})$}\kbdsidx{plotrecthraw}\label{se:plotrecthraw}
+Plot graph(s) for
+\var{data} in rectwindow $w$. $\fl$ has the same significance here as in
+\kbd{ploth}, though recursive plot is no more significant.
+
+\var{data} is a vector of vectors, each corresponding to a list a coordinates.
+If parametric plot is set, there must be an even number of vectors, each
+successive pair corresponding to a curve. Otherwise, the first one contains
+the $x$ coordinates, and the other ones contain the $y$-coordinates
+of curves to plot.
+
+\subsec{plotrline$(w,\var{dx},\var{dy})$}\kbdsidx{plotrline}\label{se:plotrline}
+Draw in the rectwindow $w$ the part of the segment
+$(x1,y1)-(x1+dx,y1+dy)$ which is inside $w$, where $(x1,y1)$ is the current
+position of the virtual cursor, and move the virtual cursor to
+$(x1+dx,y1+dy)$ (even if it is outside the window).
+
+\subsec{plotrmove$(w,\var{dx},\var{dy})$}\kbdsidx{plotrmove}\label{se:plotrmove}
+Move the virtual cursor of the rectwindow $w$ to position
+$(x1+dx,y1+dy)$, where $(x1,y1)$ is the initial position of the cursor
+(i.e.~to position $(dx,dy)$ relative to the initial cursor).
+
+\subsec{plotrpoint$(w,\var{dx},\var{dy})$}\kbdsidx{plotrpoint}\label{se:plotrpoint}
+Draw the point $(x1+dx,y1+dy)$ on the rectwindow $w$ (if it is inside
+$w$), where $(x1,y1)$ is the current position of the cursor, and in any case
+move the virtual cursor to position $(x1+dx,y1+dy)$.
+
+\subsec{plotscale$(w,\var{x1},\var{x2},\var{y1},\var{y2})$}\kbdsidx{plotscale}\label{se:plotscale}
+Scale the local coordinates of the rectwindow $w$ so that $x$ goes from
+$x1$ to $x2$ and $y$ goes from $y1$ to $y2$ ($x2<x1$ and $y2<y1$ being
+allowed). Initially, after the initialization of the rectwindow $w$ using
+the function \kbd{plotinit}, the default scaling is the graphic pixel count,
+and in particular the $y$ axis is oriented downwards since the origin is at
+the upper left. The function \kbd{plotscale} allows to change all these
+defaults and should be used whenever functions are graphed.
+
+\subsec{plotstring$(w,x,\{\var{flags}=0\})$}\kbdsidx{plotstring}\label{se:plotstring}
+Draw on the rectwindow $w$ the String $x$ (see \secref{se:strings}), at
+the current position of the cursor.
+
+\fl\ is used for justification: bits 1 and 2 regulate horizontal alignment:
+left if 0, right if 2, center if 1. Bits 4 and 8 regulate vertical
+alignment: bottom if 0, top if 8, v-center if 4. Can insert additional small
+gap between point and string: horizontal if bit 16 is set, vertical if bit
+32 is set (see the tutorial for an example).
+
+\subsec{psdraw$(\var{list}, \{\fl=0\})$}\kbdsidx{psdraw}\label{se:psdraw}
+Same as \kbd{plotdraw}, except that the output is a PostScript program
+appended to the \kbd{psfile}, and flag!=0 scales the plot from size of the
+current output device to the standard PostScript plotting size
+
+\subsec{psploth$(X=a,b,\var{expr},\{\var{flags}=0\},\{n=0\})$}\kbdsidx{psploth}\label{se:psploth}
+Same as \kbd{ploth}, except that the output is a PostScript program
+appended to the \kbd{psfile}.
+
+\subsec{psplothraw$(\var{listx},\var{listy},\{\fl=0\})$}\kbdsidx{psplothraw}\label{se:psplothraw}
+Same as \kbd{plothraw}, except that the output is a PostScript program
+appended to the \kbd{psfile}.
+%SECTION: graphic
+
+\section{Programming in GP: control statements}
+\sidx{programming}\label{se:programming}
+
+  A number of control statements are available in GP. They are simpler and
+have a syntax slightly different from their C counterparts, but are quite
+powerful enough to write any kind of program. Some of them are specific to
+GP, since they are made for number theorists. As usual, $X$ will denote any
+simple variable name, and \var{seq} will always denote a sequence of
+expressions, including the empty sequence.
+
+\misctitle{Caveat} In constructs like
+\bprog
+    for (X = a,b, seq)
+ at eprog\noindent
+the variable \kbd{X} is lexically scoped to the loop, leading to possibly
+unexpected behavior:
+\bprog
+    n = 5;
+    for (n = 1, 10,
+      if (something_nice(), break);
+    );
+    \\ @com at this point \kbd{n} is 5 !
+ at eprog\noindent
+If the sequence \kbd{seq} modifies the loop index, then the loop
+is modified accordingly:
+\bprog
+    ? for (n = 1, 10, n += 2; print(n))
+    3
+    6
+    9
+    12
+ at eprog
+
+
+\subsec{break$(\{n=1\})$}\kbdsidx{break}\label{se:break}
+Interrupts execution of current \var{seq}, and
+immediately exits from the $n$ innermost enclosing loops, within the
+current function call (or the top level loop); the integer $n$ must be
+positive. If $n$ is greater than the number of enclosing loops, all
+enclosing loops are exited.
+
+\subsec{breakpoint$()$}\kbdsidx{breakpoint}\label{se:breakpoint}
+Interrupt the program and enter the breakloop. The program continues when
+the breakloop is exited.
+\bprog
+? f(N,x)=my(z=x^2+1);breakpoint();gcd(N,z^2+1-z);
+? f(221,3)
+  ***   at top-level: f(221,3)
+  ***                 ^--------
+  ***   in function f: my(z=x^2+1);breakpoint();gcd(N,z
+  ***                              ^--------------------
+  ***   Break loop: type <Return> to continue; 'break' to go back to GP
+break> z
+10
+break>
+%2 = 13
+ at eprog
+
+\subsec{dbg\_down$(\{n=1\})$}\kbdsidx{dbg_down}\label{se:dbg_down}
+(In the break loop) go down n frames. This allows to cancel a previous call to
+\kbd{dbg\_up}.
+
+\subsec{dbg\_err$()$}\kbdsidx{dbg_err}\label{se:dbg_err}
+In the break loop, return the error data of the current error, if any.
+See \tet{iferr} for details about error data.  Compare:
+\bprog
+? iferr(1/(Mod(2,12019)^(6!)-1),E,Vec(E))
+%1 = ["e_INV", "Fp_inv", Mod(119, 12019)]
+? 1/(Mod(2,12019)^(6!)-1)
+  ***   at top-level: 1/(Mod(2,12019)^(6!)-
+  ***                  ^--------------------
+  *** _/_: impossible inverse in Fp_inv: Mod(119, 12019).
+  ***   Break loop: type 'break' to go back to GP prompt
+break> Vec(dbg_err())
+["e_INV", "Fp_inv", Mod(119, 12019)]
+ at eprog
+
+\subsec{dbg\_up$(\{n=1\})$}\kbdsidx{dbg_up}\label{se:dbg_up}
+(In the break loop) go up n frames. This allows to inspect data of the
+parent function. To cancel a \tet{dbg_up} call, use \tet{dbg_down}
+
+\subsec{dbg\_x$(A\{,n\})$}\kbdsidx{dbg_x}\label{se:dbg_x}
+Print the inner structure of \kbd{A}, complete if \kbd{n} is omitted, up
+to level \kbd{n} otherwise. This is useful for debugging. This is similar to
+\b{x} but does not require \kbd{A} to be an history entry. In particular,
+it can be used in the break loop.
+
+\subsec{for$(X=a,b,\var{seq})$}\kbdsidx{for}\label{se:for}
+Evaluates \var{seq}, where
+the formal variable $X$ goes from $a$ to $b$. Nothing is done if $a>b$.
+$a$ and $b$ must be in $\R$.
+
+\subsec{forcomposite$(n=a,\{b\},\var{seq})$}\kbdsidx{forcomposite}\label{se:forcomposite}
+Evaluates \var{seq},
+where the formal variable $n$ ranges over the composite numbers between the
+non-negative real numbers $a$ to $b$, including $a$ and $b$ if they are
+composite. Nothing is done if $a>b$.
+\bprog
+? forcomposite(n = 0, 10, print(n))
+4
+6
+8
+9
+10
+ at eprog\noindent Omitting $b$ means we will run through all composites $\geq a$,
+starting an infinite loop; it is expected that the user will break out of
+the loop himself at some point, using \kbd{break} or \kbd{return}.
+
+Note that the value of $n$ cannot be modified within \var{seq}:
+\bprog
+? forcomposite(n = 2, 10, n = [])
+ ***   at top-level: forcomposite(n=2,10,n=[])
+ ***                                      ^---
+ ***   index read-only: was changed to [].
+ at eprog
+
+\subsec{fordiv$(n,X,\var{seq})$}\kbdsidx{fordiv}\label{se:fordiv}
+Evaluates \var{seq}, where
+the formal variable $X$ ranges through the divisors of $n$
+(see \tet{divisors}, which is used as a subroutine). It is assumed that
+\kbd{factor} can handle $n$, without negative exponents. Instead of $n$,
+it is possible to input a factorization matrix, i.e. the output of
+\kbd{factor(n)}.
+
+This routine uses \kbd{divisors} as a subroutine, then loops over the
+divisors. In particular, if $n$ is an integer, divisors are sorted by
+increasing size.
+
+To avoid storing all divisors, possibly using a lot of memory, the following
+(much slower) routine loops over the divisors using essentially constant
+space:
+\bprog
+FORDIV(N)=
+{ my(P, E);
+
+  P = factor(N); E = P[,2]; P = P[,1];
+  forvec( v = vector(#E, i, [0,E[i]]),
+  X = factorback(P, v)
+  \\ ...
+);
+}
+? for(i=1,10^5, FORDIV(i))
+time = 3,445 ms.
+? for(i=1,10^5, fordiv(i, d, ))
+time = 490 ms.
+ at eprog
+
+\subsec{forell$(E,a,b,\var{seq})$}\kbdsidx{forell}\label{se:forell}
+Evaluates \var{seq}, where the formal variable $E = [\var{name}, M, G]$
+ranges through all elliptic curves of conductors from $a$ to $b$. In this
+notation \var{name} is the curve name in Cremona's elliptic  curve  database,
+$M$ is the minimal model, $G$ is a $\Z$-basis of the free part of the
+Mordell-Weil group $E(\Q)$.
+\bprog
+? forell(E, 1, 500, my([name,M,G] = E); \
+    if (#G > 1, print(name)))
+389a1
+433a1
+446d1
+ at eprog\noindent
+The \tet{elldata} database must be installed and contain data for the
+specified conductors.
+
+\synt{forell}{void *data, long (*call)(void*,GEN), long a, long b}.
+
+\subsec{forpart$(X=k,\var{seq},\{a=k\},\{n=k\})$}\kbdsidx{forpart}\label{se:forpart}
+Evaluate \var{seq} over the partitions $X=[x_1,\dots x_n]$ of the
+integer $k$, i.e.~increasing sequences $x_1\leq x_2\dots \leq x_n$ of sum
+$x_1+\dots + x_n=k$. By convention, $0$ admits only the empty partition and
+negative numbers have no partitions. A partition is given by a
+\typ{VECSMALL}, where parts are sorted in nondecreasing order:
+\bprog
+? forpart(X=3, print(X))
+Vecsmall([3])
+Vecsmall([1, 2])
+Vecsmall([1, 1, 1])
+ at eprog\noindent Optional parameters $n$ and $a$ are as follows:
+
+\item $n=\var{nmax}$ (resp. $n=[\var{nmin},\var{nmax}]$) restricts
+partitions to length less than $\var{nmax}$ (resp. length between
+$\var{nmin}$ and $nmax$), where the \emph{length} is the number of nonzero
+entries.
+
+\item $a=\var{amax}$ (resp. $a=[\var{amin},\var{amax}]$) restricts the parts
+to integers less than $\var{amax}$ (resp. between $\var{amin}$ and
+$\var{amax}$).
+
+By default, parts are positive and we remove zero entries unless $amin\leq0$,
+in which case $X$ is of constant length $\var{nmax}$.
+\bprog
+\\ at most 3 non-zero parts, all <= 4
+? forpart(v=5,print(Vec(v)),4,3)
+[1, 4]
+[2, 3]
+[1, 1, 3]
+[1, 2, 2]
+
+\\ between 2 and 4 parts less than 5, fill with zeros
+? forpart(v=5,print(Vec(v)),[0,5],[2,4])
+[0, 0, 1, 4]
+[0, 0, 2, 3]
+[0, 1, 1, 3]
+[0, 1, 2, 2]
+[1, 1, 1, 2]
+ at eprog\noindent
+The behavior is unspecified if $X$ is modified inside the loop.
+
+\synt{forpart}{void *data, long (*call)(void*,GEN), long k, GEN a, GEN n}.
+
+\subsec{forprime$(p=a,\{b\},\var{seq})$}\kbdsidx{forprime}\label{se:forprime}
+Evaluates \var{seq},
+where the formal variable $p$ ranges over the prime numbers between the real
+numbers $a$ to $b$, including $a$ and $b$ if they are prime. More precisely,
+the value of
+$p$ is incremented to \kbd{nextprime($p$ + 1)}, the smallest prime strictly
+larger than $p$, at the end of each iteration. Nothing is done if $a>b$.
+\bprog
+? forprime(p = 4, 10, print(p))
+5
+7
+ at eprog\noindent Omitting $b$ means we will run through all primes $\geq a$,
+starting an infinite loop; it is expected that the user will break out of
+the loop himself at some point, using \kbd{break} or \kbd{return}.
+
+Note that the value of $p$ cannot be modified within \var{seq}:
+\bprog
+? forprime(p = 2, 10, p = [])
+ ***   at top-level: forprime(p=2,10,p=[])
+ ***                                   ^---
+ ***   prime index read-only: was changed to [].
+ at eprog
+
+\subsec{forstep$(X=a,b,s,\var{seq})$}\kbdsidx{forstep}\label{se:forstep}
+Evaluates \var{seq},
+where the formal variable $X$ goes from $a$ to $b$, in increments of $s$.
+Nothing is done if $s>0$ and $a>b$ or if $s<0$ and $a<b$. $s$ must be in
+$\R^*$ or a vector of steps $[s_1,\dots,s_n]$. In the latter case, the
+successive steps are used in the order they appear in $s$.
+
+\bprog
+? forstep(x=5, 20, [2,4], print(x))
+5
+7
+11
+13
+17
+19
+ at eprog
+
+\subsec{forsubgroup$(H=G,\{\var{bound}\},\var{seq})$}\kbdsidx{forsubgroup}\label{se:forsubgroup}
+Evaluates \var{seq} for
+each subgroup $H$ of the \emph{abelian} group $G$ (given in
+SNF\sidx{Smith normal form} form or as a vector of elementary divisors).
+
+If \var{bound} is present, and is a positive integer, restrict the output to
+subgroups of index less than \var{bound}. If \var{bound} is a vector
+containing a single positive integer $B$, then only subgroups of index
+exactly equal to $B$ are computed
+
+The subgroups are not ordered in any
+obvious way, unless $G$ is a $p$-group in which case Birkhoff's algorithm
+produces them by decreasing index. A \idx{subgroup} is given as a matrix
+whose columns give its generators on the implicit generators of $G$. For
+example, the following prints all subgroups of index less than 2 in $G =
+\Z/2\Z g_1 \times \Z/2\Z g_2$:
+
+\bprog
+? G = [2,2]; forsubgroup(H=G, 2, print(H))
+[1; 1]
+[1; 2]
+[2; 1]
+[1, 0; 1, 1]
+ at eprog\noindent
+The last one, for instance is generated by $(g_1, g_1 + g_2)$. This
+routine is intended to treat huge groups, when \tet{subgrouplist} is not an
+option due to the sheer size of the output.
+
+For maximal speed the subgroups have been left as produced by the algorithm.
+To print them in canonical form (as left divisors of $G$ in HNF form), one
+can for instance use
+\bprog
+? G = matdiagonal([2,2]); forsubgroup(H=G, 2, print(mathnf(concat(G,H))))
+[2, 1; 0, 1]
+[1, 0; 0, 2]
+[2, 0; 0, 1]
+[1, 0; 0, 1]
+ at eprog\noindent
+Note that in this last representation, the index $[G:H]$ is given by the
+determinant. See \tet{galoissubcyclo} and \tet{galoisfixedfield} for
+applications to \idx{Galois} theory.
+
+\synt{forsubgroup}{void *data, long (*call)(void*,GEN), GEN G, GEN bound}.
+
+\subsec{forvec$(X=v,\var{seq},\{\fl=0\})$}\kbdsidx{forvec}\label{se:forvec}
+Let $v$ be an $n$-component
+vector (where $n$ is arbitrary) of two-component vectors $[a_i,b_i]$
+for $1\le i\le n$. This routine evaluates \var{seq}, where the formal
+variables $X[1],\dots, X[n]$ go from $a_1$ to $b_1$,\dots, from $a_n$ to
+$b_n$, i.e.~$X$ goes from $[a_1,\dots,a_n]$ to $[b_1,\dots,b_n]$ with respect
+to the lexicographic ordering. (The formal variable with the highest index
+moves the fastest.) If $\fl=1$, generate only nondecreasing vectors $X$, and
+if $\fl=2$, generate only strictly increasing vectors $X$.
+
+The type of $X$ is the same as the type of $v$: \typ{VEC} or \typ{COL}.
+
+\subsec{if$(a,\{\var{seq1}\},\{\var{seq2}\})$}\kbdsidx{if}\label{se:if}
+Evaluates the expression sequence \var{seq1} if $a$ is non-zero, otherwise
+the expression \var{seq2}. Of course, \var{seq1} or \var{seq2} may be empty:
+
+\kbd{if ($a$,\var{seq})} evaluates \var{seq} if $a$ is not equal to zero
+(you don't have to write the second comma), and does nothing otherwise,
+
+\kbd{if ($a$,,\var{seq})} evaluates \var{seq} if $a$ is equal to zero, and
+does nothing otherwise. You could get the same result using the \kbd{!}
+(\kbd{not}) operator: \kbd{if (!$a$,\var{seq})}.
+
+The value of an \kbd{if} statement is the value of the branch that gets
+evaluated: for instance
+\bprog
+x = if(n % 4 == 1, y, z);
+ at eprog\noindent sets $x$ to $y$ if $n$ is $1$ modulo $4$, and to $z$
+otherwise.
+
+Successive 'else' blocks can be abbreviated in a single compound \kbd{if}
+as follows:
+\bprog
+if (test1, seq1,
+    test2, seq2,
+    ...
+    testn, seqn,
+    seqdefault);
+ at eprog\noindent is equivalent to
+\bprog
+if (test1, seq1
+         , if (test2, seq2
+                    , ...
+                      if (testn, seqn, seqdefault)...));
+ at eprog For instance, this allows to write traditional switch / case
+constructions:
+\bprog
+if (x == 0, do0(),
+    x == 1, do1(),
+    x == 2, do2(),
+    dodefault());
+ at eprog
+
+\misctitle{Remark}
+The boolean operators \kbd{\&\&} and \kbd{||} are evaluated
+according to operator precedence as explained in \secref{se:operators}, but,
+contrary to other operators, the evaluation of the arguments is stopped
+as soon as the final truth value has been determined. For instance
+\bprog
+if (x != 0 && f(1/x), ...)
+ at eprog
+\noindent is a perfectly safe statement.
+
+\misctitle{Remark} Functions such as \kbd{break} and \kbd{next} operate on
+\emph{loops}, such as \kbd{for$xxx$}, \kbd{while}, \kbd{until}. The \kbd{if}
+statement is \emph{not} a loop. (Obviously!)
+
+\subsec{iferr$(\var{seq1},E,\var{seq2}\{,\var{pred}\})$}\kbdsidx{iferr}\label{se:iferr}
+Evaluates the expression sequence \var{seq1}. If an error occurs,
+set the formal parameter \var{E} set to the error data.
+If \var{pred} is not present or evaluates to true, catch the error
+and evaluate \var{seq2}. Both \var{pred} and \var{seq2} can reference \var{E}.
+The error type is given by \kbd{errname(E)}, and other data can be
+accessed using the \tet{component} function. The code \var{seq2} should check
+whether the error is the one expected. In the negative the error can be
+rethrown using \tet{error(E)} (and possibly caught by an higher \kbd{iferr}
+instance). The following uses \kbd{iferr} to implement Lenstra's ECM factoring
+ method
+\bprog
+? ecm(N, B = 1000!, nb = 100)=
+  {
+    for(a = 1, nb,
+      iferr(ellmul(ellinit([a,1]*Mod(1,N)), [0,1]*Mod(1,N), B),
+        E, return(gcd(lift(component(E,2)),N)),
+        errname(E)=="e_INV" && type(component(E,2)) == "t_INTMOD"))
+  }
+? ecm(2^101-1)
+%2 = 7432339208719
+ at eprog
+The return value of \kbd{iferr} itself is the value of \var{seq2} if an
+error occurs, and the value of \var{seq1} otherwise. We now describe the
+list of valid error types, and the associated error data \var{E}; in each
+case, we list in order the components of \var{E}, accessed via
+\kbd{component(E,1)}, \kbd{component(E,2)}, etc.
+
+ \misctitle{Internal errors, ``system'' errors}
+
+ \item \kbd{"e\_ARCH"}. A requested feature $s$ is not available on this
+ architecture or operating system.
+ \var{E} has one component (\typ{STR}): the missing feature name $s$.
+
+ \item \kbd{"e\_BUG"}. A bug in the PARI library, in function $s$.
+ \var{E} has one component (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_FILE"}. Error while trying to open a file.
+ \var{E} has two components, 1 (\typ{STR}): the file type (input, output,
+ etc.), 2 (\typ{STR}): the file name.
+
+ \item \kbd{"e\_IMPL"}. A requested feature $s$ is not implemented.
+ \var{E} has one component, 1 (\typ{STR}): the feature name $s$.
+
+ \item \kbd{"e\_PACKAGE"}. Missing optional package $s$.
+ \var{E} has one component, 1 (\typ{STR}): the package name $s$.
+
+ \misctitle{Syntax errors, type errors}
+
+ \item \kbd{"e\_DIM"}. The dimensions of arguments $x$ and $y$ submitted
+ to function $s$ does not match up.
+ E.g., multiplying matrices of inconsistent dimension, adding vectors of
+ different lengths,\dots
+ \var{E} has three component, 1 (\typ{STR}): the function name $s$, 2: the
+ argument $x$, 3: the argument $y$.
+
+ \item \kbd{"e\_FLAG"}. A flag argument is out of bounds in function $s$.
+ \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_NOTFUNC"}. Generated by the PARI evaluator; tried to use a
+\kbd{GEN} $x$ which is not a \typ{CLOSURE} in a function call syntax (as in
+\kbd{f = 1; f(2);}).
+ \var{E} has one component, 1: the offending \kbd{GEN} $x$.
+
+ \item \kbd{"e\_OP"}. Impossible operation between two objects than cannot
+ be typecast to a sensible common domain for deeper reasons than a type
+ mismatch, usually for arithmetic reasons. As in \kbd{O(2) + O(3)}: it is
+ valid to add two \typ{PADIC}s, provided the underlying prime is the same; so
+ the addition is not forbidden a priori for type reasons, it only becomes so
+ when inspecting the objects and trying to perform the operation.
+ \var{E} has three components, 1 (\typ{STR}): the operator name \var{op},
+ 2: first argument, 3: second argument.
+
+ \item \kbd{"e\_TYPE"}. An argument $x$ of function $s$ had an unexpected type.
+ (As in \kbd{factor("blah")}.)
+ \var{E} has two components, 1 (\typ{STR}): the function name $s$,
+ 2: the offending argument $x$.
+
+ \item \kbd{"e\_TYPE2"}. Forbidden operation between two objects than cannot be
+ typecast to a sensible common domain, because their types do not match up.
+ (As in \kbd{Mod(1,2) + Pi}.)
+ \var{E} has three components, 1 (\typ{STR}): the operator name \var{op},
+ 2: first argument, 3: second argument.
+
+ \item \kbd{"e\_PRIORITY"}. Object $o$ in function $s$ contains
+ variables whose priority is incompatible with the expected operation.
+ E.g.~\kbd{Pol([x,1], 'y)}: this raises an error because it's not possible to
+ create a polynomial whose coefficients involve variables with higher priority
+ than the main variable. $E$ has four components: 1 (\typ{STR}): the function
+ name $s$, 2: the offending argument $o$, 3 (\typ{STR}): an operator
+ $\var{op}$ describing the priority error, 4 (\typ{POL}):
+ the variable $v$ describing the priority error. The argument
+ satisfies $\kbd{variable}(x)~\var{op} \kbd{variable}(v)$.
+
+ \item \kbd{"e\_VAR"}. The variables of arguments $x$ and $y$ submitted
+ to function $s$ does not match up. E.g., considering the algebraic number
+ \kbd{Mod(t,t\pow2+1)} in \kbd{nfinit(x\pow2+1)}.
+ \var{E} has three component, 1 (\typ{STR}): the function name $s$, 2
+ (\typ{POL}): the argument $x$, 3 (\typ{POL}): the argument $y$.
+
+ \misctitle{Overflows}
+
+ \item \kbd{"e\_COMPONENT"}. Trying to access an inexistent component in a
+ vector/matrix/list in a function: the index is less than $1$ or greater
+ than the allowed length.
+ \var{E} has four components,
+ 1 (\typ{STR}): the function name
+ 2 (\typ{STR}): an operator $\var{op}$ ($<$ or $>$),
+ 2 (\typ{GEN}): a numerical limit $l$ bounding the allowed range,
+ 3 (\kbd{GEN}): the index $x$. It satisfies $x$ \var{op} $l$.
+
+ \item \kbd{"e\_DOMAIN"}. An argument is not in the function's domain.
+ \var{E} has five components, 1 (\typ{STR}): the function name,
+ 2 (\typ{STR}): the mathematical name of the out-of-domain argument
+ 3 (\typ{STR}): an operator $\var{op}$ describing the domain error,
+ 4 (\typ{GEN}): the numerical limit $l$ describing the domain error,
+ 5 (\kbd{GEN}): the out-of-domain argument $x$. The argument satisfies $x$
+ \var{op} $l$, which prevents it from belonging to the function's domain.
+
+ \item \kbd{"e\_MAXPRIME"}. A function using the precomputed list of prime
+ numbers ran out of primes.
+ \var{E} has one component, 1 (\typ{INT}): the requested prime bound, which
+ overflowed \kbd{primelimit} or $0$ (bound is unknown).
+
+ \item \kbd{"e\_MEM"}. A call to \tet{pari_malloc} or \tet{pari_realloc}
+ failed. \var{E} has no component.
+
+ \item \kbd{"e\_OVERFLOW"}. An object in function $s$ becomes too large to be
+ represented within PARI's hardcoded limits. (As in \kbd{2\pow2\pow2\pow10} or
+ \kbd{exp(1e100)}, which overflow in \kbd{lg} and \kbd{expo}.)
+ \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_PREC"}. Function $s$ fails because input accuracy is too low.
+ (As in \kbd{floor(1e100)} at default accuracy.)
+ \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_STACK"}. The PARI stack overflows.
+ \var{E} has no component.
+
+ \misctitle{Errors triggered intentionally}
+
+ \item \kbd{"e\_ALARM"}. A timeout, generated by the \tet{alarm} function.
+ \var{E} has one component (\typ{STR}): the error message to print.
+
+ \item \kbd{"e\_USER"}. A user error, as triggered by
+ \tet{error}($g_1,\dots,g_n)$.
+ \var{E} has one component, 1 (\typ{VEC}): the vector of $n$ arguments given
+ to \kbd{error}.
+
+ \misctitle{Mathematical errors}
+
+ \item \kbd{"e\_CONSTPOL"}. An argument of function $s$ is a constant
+ polynomial, which does not make sense. (As in \kbd{galoisinit(Pol(1))}.)
+ \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_COPRIME"}. Function $s$ expected coprime arguments,
+ and did receive $x,y$, which were not.
+ \var{E} has three component, 1 (\typ{STR}): the function name $s$,
+ 2: the argument $x$, 3: the argument $y$.
+
+ \item \kbd{"e\_INV"}. Tried to invert a non-invertible object $x$ in
+ function $s$.
+ \var{E} has two components, 1 (\typ{STR}): the function name $s$,
+ 2: the non-invertible $x$. If $x = \kbd{Mod}(a,b)$
+ is a \typ{INTMOD} and $a$ is not $0$ mod $b$, this allows to factor
+ the modulus, as \kbd{gcd}$(a,b)$ is a non-trivial divisor of $b$.
+
+ \item \kbd{"e\_IRREDPOL"}. Function $s$ expected an irreducible polynomial,
+ and did receive $T$, which was not. (As in \kbd{nfinit(x\pow2-1)}.)
+ \var{E} has two component, 1 (\typ{STR}): the function name $s$,
+ 2 (\typ{POL}): the polynomial $x$.
+
+ \item \kbd{"e\_MISC"}. Generic uncategorized error.
+ \var{E} has one component (\typ{STR}): the error message to print.
+
+ \item \kbd{"e\_MODULUS"}. moduli $x$ and $y$ submitted to function $s$ are
+ inconsistent. As in
+ \bprog
+   nfalgtobasis(nfinit(t^3-2), Mod(t,t^2+1)
+ @eprog\noindent
+ \var{E} has three component, 1 (\typ{STR}): the function $s$,
+ 2: the argument $x$, 3: the argument $x$.
+
+ \item \kbd{"e\_NEGVAL"}. An argument of function $s$ is a power series with
+ negative valuation, which does not make sense. (As in \kbd{cos(1/x)}.)
+ \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_PRIME"}. Function $s$ expected a prime number,
+ and did receive $p$, which was not. (As in \kbd{idealprimedec(nf, 4)}.)
+ \var{E} has two component, 1 (\typ{STR}): the function name $s$,
+ 2: the argument $p$.
+
+ \item \kbd{"e\_ROOTS0"}. An argument of function $s$ is a zero polynomial,
+ and we need to consider its roots. (As in \kbd{polroots(0)}.) \var{E} has
+ one component, 1 (\typ{STR}): the function name $s$.
+
+ \item \kbd{"e\_SQRTN"}. Trying to compute an $n$-th root of $x$, which does
+ not exist, in function $s$. (As in \kbd{sqrt(Mod(-1,3))}.)
+ \var{E} has two components, 1 (\typ{STR}): the function name $s$,
+ 2: the argument $x$.
+
+\subsec{next$(\{n=1\})$}\kbdsidx{next}\label{se:next}
+Interrupts execution of current $seq$,
+resume the next iteration of the innermost enclosing loop, within the
+current function call (or top level loop). If $n$ is specified, resume at
+the $n$-th enclosing loop. If $n$ is bigger than the number of enclosing
+loops, all enclosing loops are exited.
+
+\subsec{return$(\{x=0\})$}\kbdsidx{return}\label{se:return}
+Returns from current subroutine, with
+result $x$. If $x$ is omitted, return the \kbd{(void)} value (return no
+result, like \kbd{print}).
+
+\subsec{until$(a,\var{seq})$}\kbdsidx{until}\label{se:until}
+Evaluates \var{seq} until $a$ is not
+equal to 0 (i.e.~until $a$ is true). If $a$ is initially not equal to 0,
+\var{seq} is evaluated once (more generally, the condition on $a$ is tested
+\emph{after} execution of the \var{seq}, not before as in \kbd{while}).
+
+\subsec{while$(a,\var{seq})$}\kbdsidx{while}\label{se:while}
+While $a$ is non-zero, evaluates the expression sequence \var{seq}. The
+test is made \emph{before} evaluating the $seq$, hence in particular if $a$
+is initially equal to zero the \var{seq} will not be evaluated at all.
+%SECTION: programming/control
+
+\section{Programming in GP: other specific functions}
+\label{se:gp_program}
+
+  In addition to the general PARI functions, it is necessary to have some
+functions which will be of use specifically for \kbd{gp}, though a few of these can
+be accessed under library mode. Before we start describing these, we recall
+the difference between \emph{strings} and \emph{keywords} (see
+\secref{se:strings}): the latter don't get expanded at all, and you can type
+them without any enclosing quotes. The former are dynamic objects, where
+everything outside quotes gets immediately expanded.
+
+
+\subsec{Strprintf$(\var{fmt},\{x\}*)$}\kbdsidx{Strprintf}\label{se:Strprintf}
+Returns a string built from the remaining arguments according to the
+format fmt. The format consists of ordinary characters (not \%), printed
+unchanged, and conversions specifications. See \kbd{printf}.
+%\syn{NO}
+
+\subsec{addhelp$(\var{sym},\var{str})$}\kbdsidx{addhelp}\label{se:addhelp}
+Changes the help message for the symbol \kbd{sym}. The string \var{str}
+is expanded on the spot and stored as the online help for \kbd{sym}. It is
+recommended to document global variables and user functions in this way,
+although \kbd{gp} will not protest if you don't.
+
+You can attach a help text to an alias, but it will never be
+shown: aliases are expanded by the \kbd{?} help operator and we get the help
+of the symbol the alias points to. Nothing prevents you from modifying the
+help of built-in PARI functions. But if you do, we would like to hear why you
+needed it!
+
+Without \tet{addhelp}, the standard help for user functions consists of its
+name and definition.
+\bprog
+gp> f(x) = x^2;
+gp> ?f
+f =
+  (x)->x^2
+
+ at eprog\noindent Once addhelp is applied to $f$, the function code is no
+longer included. It can still be consulted by typing the function name:
+\bprog
+gp> addhelp(f, "Square")
+gp> ?f
+Square
+
+gp> f
+%2 = (x)->x^2
+ at eprog
+
+The library syntax is \fun{void}{addhelp}{const char *sym, const char *str}.
+
+\subsec{alarm$(\{s = 0\},\{\var{code}\})$}\kbdsidx{alarm}\label{se:alarm}
+If \var{code} is omitted, trigger an \var{e\_ALARM} exception after $s$
+seconds, cancelling any previously set alarm; stop a pending alarm if $s =
+0$ or is omitted.
+
+Otherwise, if $s$ is positive, the function evaluates \var{code},
+aborting after $s$ seconds. The return value is the value of \var{code} if
+it ran to completion before the alarm timeout, and a \typ{ERROR} object
+otherwise.
+\bprog
+  ? p = nextprime(10^25); q = nextprime(10^26); N = p*q;
+  ? E = alarm(1, factor(N));
+  ? type(E)
+  %3 = "t_ERROR"
+  ? print(E)
+  %4 = error("alarm interrupt after 964 ms.")
+  ? alarm(10, factor(N));   \\ enough time
+  %5 =
+  [ 10000000000000000000000013 1]
+
+  [100000000000000000000000067 1]
+ at eprog\noindent Here is a more involved example: the function
+\kbd{timefact(N,sec)} below tries to factor $N$ and gives up after \var{sec}
+seconds, returning a partial factorisation.
+\bprog
+\\ Time-bounded partial factorization
+default(factor_add_primes,1);
+timefact(N,sec)=
+{
+  F = alarm(sec, factor(N));
+  if (type(F) == "t_ERROR", factor(N, 2^24), F);
+}
+ at eprog\noindent We either return the factorization directly, or replace the
+\typ{ERROR} result by a simple bounded factorization \kbd{factor(N, 2\pow 24)}.
+Note the \tet{factor_add_primes} trick: any prime larger than $2^{24}$
+discovered while attempting the initial factorization is stored and
+remembered. When the alarm rings, the subsequent bounded factorization finds
+it right away.
+
+\misctitle{Caveat} It is not possible to set a new alarm \emph{within}
+another \kbd{alarm} code: the new timer erases the parent one.
+
+\subsec{alias$(\var{newsym},\var{sym})$}\kbdsidx{alias}\label{se:alias}
+Defines the symbol \var{newsym} as an alias for the the symbol \var{sym}:
+\bprog
+? alias("det", "matdet");
+? det([1,2;3,4])
+%1 = -2
+ at eprog\noindent
+You are not restricted to ordinary functions, as in the above example:
+to alias (from/to) member functions, prefix them with `\kbd{\_.}';
+to alias operators, use their internal name, obtained by writing
+\kbd{\_} in lieu of the operators argument: for instance, \kbd{\_!} and
+\kbd{!\_} are the internal names of the factorial and the
+logical negation, respectively.
+\bprog
+? alias("mod", "_.mod");
+? alias("add", "_+_");
+? alias("_.sin", "sin");
+? mod(Mod(x,x^4+1))
+%2 = x^4 + 1
+? add(4,6)
+%3 = 10
+? Pi.sin
+%4 = 0.E-37
+ at eprog
+Alias expansion is performed directly by the internal GP compiler.
+Note that since alias is performed at compilation-time, it does not
+require any run-time processing, however it only affects GP code
+compiled \emph{after} the alias command is evaluated. A slower but more
+flexible alternative is to use variables. Compare
+\bprog
+? fun = sin;
+? g(a,b) = intnum(t=a,b,fun(t));
+? g(0, Pi)
+%3 = 2.0000000000000000000000000000000000000
+? fun = cos;
+? g(0, Pi)
+%5 = 1.8830410776607851098 E-39
+ at eprog\noindent
+with
+\bprog
+? alias(fun, sin);
+? g(a,b) = intnum(t=a,b,fun(t));
+? g(0,Pi)
+%2 = 2.0000000000000000000000000000000000000
+? alias(fun, cos);  \\ Oops. Does not affect *previous* definition!
+? g(0,Pi)
+%3 = 2.0000000000000000000000000000000000000
+? g(a,b) = intnum(t=a,b,fun(t)); \\ Redefine, taking new alias into account
+? g(0,Pi)
+%5 = 1.8830410776607851098 E-39
+ at eprog
+
+A sample alias file \kbd{misc/gpalias} is provided with
+the standard distribution.
+
+The library syntax is \fun{void}{alias0}{const char *newsym, const char *sym}.
+
+\subsec{allocatemem$(\{s=0\})$}\kbdsidx{allocatemem}\label{se:allocatemem}
+This special operation changes the stack size \emph{after}
+initialization. $x$ must be a non-negative integer. If $x > 0$, a new stack
+of at least $x$ bytes is allocated. We may allocate more than $x$ bytes if
+$x$ is way too small, or for alignment reasons: the current formula is
+$\max(16*\ceil{x/16}, 500032)$ bytes.
+
+If $x=0$, the size of the new stack is twice the size of the old one. The
+old stack is discarded.
+
+\misctitle{Warning} This function should be typed at the \kbd{gp} prompt in
+interactive usage, or left by itself at the start of batch files.
+It cannot be used meaningfully in loop-like constructs, or as part of a
+larger expression sequence, e.g
+\bprog
+   allocatemem(); x = 1;   \\@com This will not set \kbd{x}!
+ at eprog\noindent
+In fact, all loops are immediately exited, user functions terminated, and
+the rest of the sequence following \kbd{allocatemem()} is silently
+discarded, as well as all pending sequences of instructions. We just go on
+reading the next instruction sequence from the file we're in (or from the
+user). In particular, we have the following possibly unexpected behavior: in
+\bprog
+   read("file.gp"); x = 1
+ at eprog\noindent were \kbd{file.gp} contains an \kbd{allocatemem} statement,
+the \kbd{x = 1} is never executed, since all pending instructions in the
+current sequence are discarded.
+
+The technical reason is that this routine moves the stack, so temporary
+objects created during the current expression evaluation are not correct
+anymore. (In particular byte-compiled expressions, which are allocated on
+the stack.) To avoid accessing obsolete pointers to the old stack, this
+routine ends by a \kbd{longjmp}.
+
+\misctitle{Remark} If the operating system cannot allocate the desired
+$x$ bytes, a loop halves the allocation size until it succeeds:
+\bprog
+? allocatemem(5*10^10)
+ ***   Warning: not enough memory, new stack 50000000000.
+ ***   Warning: not enough memory, new stack 25000000000.
+ ***   Warning: not enough memory, new stack 12500000000.
+ ***   Warning: new stack size = 6250000000 (5960.464 Mbytes).
+ at eprog
+
+\subsec{apply$(f, A)$}\kbdsidx{apply}\label{se:apply}
+Apply the \typ{CLOSURE} \kbd{f} to the entries of \kbd{A}. If \kbd{A}
+is a scalar, return \kbd{f(A)}. If \kbd{A} is a polynomial or power series,
+apply \kbd{f} on all coefficients. If \kbd{A} is a vector or list, return
+the elements $f(x)$ where $x$ runs through \kbd{A}. If \kbd{A} is a matrix,
+return the matrix whose entries are the $f(\kbd{A[i,j]})$.
+\bprog
+? apply(x->x^2, [1,2,3,4])
+%1 = [1, 4, 9, 16]
+? apply(x->x^2, [1,2;3,4])
+%2 =
+[1 4]
+
+[9 16]
+? apply(x->x^2, 4*x^2 + 3*x+ 2)
+%3 = 16*x^2 + 9*x + 4
+ at eprog\noindent Note that many functions already act componentwise on
+vectors or matrices, but they almost never act on lists; in this
+case, \kbd{apply} is a good solution:
+\bprog
+? L = List([Mod(1,3), Mod(2,4)]);
+? lift(L)
+  ***   at top-level: lift(L)
+  ***                 ^-------
+  *** lift: incorrect type in lift.
+? apply(lift, L);
+%2 = List([1, 2])
+ at eprog
+\misctitle{Remark} For $v$ a \typ{VEC}, \typ{COL}, \typ{LIST} or \typ{MAT},
+the alternative set-notations
+\bprog
+[g(x) | x <- v, f(x)]
+[x | x <- v, f(x)]
+[g(x) | x <- v]
+ at eprog\noindent
+are available as shortcuts for
+\bprog
+apply(g, select(f, Vec(v)))
+select(f, Vec(v))
+apply(g, Vec(v))
+ at eprog\noindent respectively:
+\bprog
+? L = List([Mod(1,3), Mod(2,4)]);
+? [ lift(x) | x<-L ]
+%2 = [1, 2]
+ at eprog
+
+\synt{genapply}{void *E, GEN (*fun)(void*,GEN), GEN a}.
+
+\subsec{default$(\{\var{key}\},\{\var{val}\})$}\kbdsidx{default}\label{se:default}
+Returns the default corresponding to keyword \var{key}. If \var{val} is
+present, sets the default to \var{val} first (which is subject to string
+expansion first). Typing \kbd{default()} (or \b{d}) yields the complete
+default list as well as their current values. See \secref{se:defaults} for an
+introduction to GP defaults, \secref{se:gp_defaults} for a
+list of available defaults, and \secref{se:meta} for some shortcut
+alternatives. Note that the shortcuts are meant for interactive use and
+usually display more information than \kbd{default}.
+
+The library syntax is \fun{GEN}{default0}{const char *key = NULL, const char *val = NULL}.
+
+\subsec{errname$(E)$}\kbdsidx{errname}\label{se:errname}
+Returns the type of the error message \kbd{E} as a string.
+
+The library syntax is \fun{GEN}{errname}{GEN E}.
+
+\subsec{error$(\{\var{str}\}*)$}\kbdsidx{error}\label{se:error}
+Outputs its argument list (each of
+them interpreted as a string), then interrupts the running \kbd{gp} program,
+returning to the input prompt. For instance
+\bprog
+error("n = ", n, " is not squarefree!")
+ at eprog\noindent
+ % \syn{NO}
+
+\subsec{extern$(\var{str})$}\kbdsidx{extern}\label{se:extern}
+The string \var{str} is the name of an external command (i.e.~one you
+would type from your UNIX shell prompt). This command is immediately run and
+its output fed into \kbd{gp}, just as if read from a file.
+
+\subsec{externstr$(\var{str})$}\kbdsidx{externstr}\label{se:externstr}
+The string \var{str} is the name of an external command (i.e.~one you
+would type from your UNIX shell prompt). This command is immediately run and
+its output is returned as a vector of GP strings, one component per output
+line.
+
+\subsec{getabstime$()$}\kbdsidx{getabstime}\label{se:getabstime}
+Returns the time (in milliseconds) elapsed since \kbd{gp} startup. This
+provides a reentrant version of \kbd{gettime}:
+\bprog
+my (t = getabstime());
+...
+print("Time: ", getabstime() - t);
+ at eprog
+
+The library syntax is \fun{long}{getabstime}{}.
+
+\subsec{getenv$(s)$}\kbdsidx{getenv}\label{se:getenv}
+Return the value of the environment variable \kbd{s} if it is defined, otherwise return 0.
+
+The library syntax is \fun{GEN}{gp_getenv}{const char *s}.
+
+\subsec{getheap$()$}\kbdsidx{getheap}\label{se:getheap}
+Returns a two-component row vector giving the
+number of objects on the heap and the amount of memory they occupy in long
+words. Useful mainly for debugging purposes.
+
+The library syntax is \fun{GEN}{getheap}{}.
+
+\subsec{getrand$()$}\kbdsidx{getrand}\label{se:getrand}
+Returns the current value of the seed used by the
+pseudo-random number generator \tet{random}. Useful mainly for debugging
+purposes, to reproduce a specific chain of computations. The returned value
+is technical (reproduces an internal state array), and can only be used as an
+argument to \tet{setrand}.
+
+The library syntax is \fun{GEN}{getrand}{}.
+
+\subsec{getstack$()$}\kbdsidx{getstack}\label{se:getstack}
+Returns the current value of $\kbd{top}-\kbd{avma}$, i.e.~the number of
+bytes used up to now on the stack. Useful mainly for debugging purposes.
+
+The library syntax is \fun{long}{getstack}{}.
+
+\subsec{gettime$()$}\kbdsidx{gettime}\label{se:gettime}
+Returns the time (in milliseconds) elapsed since either the last call to
+\kbd{gettime}, or to the beginning of the containing GP instruction (if
+inside \kbd{gp}), whichever came last.
+
+For a reentrant version, see \tet{getabstime}.
+
+The library syntax is \fun{long}{gettime}{}.
+
+\subsec{global$(\var{list} \var{of} \var{variables})$}\kbdsidx{global}\label{se:global}
+Obsolete. Scheduled for deletion.
+% \syn{NO}
+
+\subsec{inline$(x,...,z)$}\kbdsidx{inline}\label{se:inline}
+(Experimental) declare $x,\ldots, z$ as inline variables. Such variables
+behave like lexically scoped variable (see my()) but with unlimited scope.
+It is however possible to exit the scope by using \kbd{uninline()}.
+When used in a GP script, it is recommended to call \kbd{uninline()} before
+the script's end to avoid inline variables leaking outside the script.
+
+\subsec{input$()$}\kbdsidx{input}\label{se:input}
+Reads a string, interpreted as a GP expression,
+from the input file, usually standard input (i.e.~the keyboard). If a
+sequence of expressions is given, the result is the result of the last
+expression of the sequence. When using this instruction, it is useful to
+prompt for the string by using the \kbd{print1} function. Note that in the
+present version 2.19 of \kbd{pari.el}, when using \kbd{gp} under GNU Emacs (see
+\secref{se:emacs}) one \emph{must} prompt for the string, with a string
+which ends with the same prompt as any of the previous ones (a \kbd{"? "}
+will do for instance).
+
+\subsec{install$(\var{name},\var{code},\{\var{gpname}\},\{\var{lib}\})$}\kbdsidx{install}\label{se:install}
+Loads from dynamic library \var{lib} the function \var{name}. Assigns to it
+the name \var{gpname} in this \kbd{gp} session, with \emph{prototype}
+\var{code} (see below). If \var{gpname} is omitted, uses \var{name}.
+If \var{lib} is omitted, all symbols known to \kbd{gp} are available: this
+includes the whole of \kbd{libpari.so} and possibly others (such as
+\kbd{libc.so}).
+
+Most importantly, \kbd{install} gives you access to all non-static functions
+defined in the PARI library. For instance, the function \kbd{GEN addii(GEN
+x, GEN y)} adds two PARI integers, and is not directly accessible under
+\kbd{gp} (it is eventually called by the \kbd{+} operator of course):
+\bprog
+? install("addii", "GG")
+? addii(1, 2)
+%1 = 3
+ at eprog\noindent
+It also allows to add external functions to the \kbd{gp} interpreter.
+For instance, it makes the function \tet{system} obsolete:
+\bprog
+? install(system, vs, sys,/*omitted*/)
+? sys("ls gp*")
+gp.c            gp.h            gp_rl.c
+ at eprog\noindent This works because \kbd{system} is part of \kbd{libc.so},
+which is linked to \kbd{gp}. It is also possible to compile a shared library
+yourself and provide it to gp in this way: use \kbd{gp2c}, or do it manually
+(see the \kbd{modules\_build} variable in \kbd{pari.cfg} for hints).
+
+Re-installing a function will print a warning and update the prototype code
+if needed. However, it will not reload a symbol from the library, even if the
+latter has been recompiled.
+
+\misctitle{Prototype} We only give a simplified description here, covering
+most functions, but there are many more possibilities. The full documentation
+is available in \kbd{libpari.dvi}, see
+\bprog
+  ??prototype
+ at eprog
+
+\item First character \kbd{i}, \kbd{l}, \kbd{v} : return type int / long /
+void. (Default: \kbd{GEN})
+
+\item One letter for each mandatory argument, in the same order as they appear
+in the argument list: \kbd{G} (\kbd{GEN}), \kbd{\&}
+(\kbd{GEN*}), \kbd{L} (\kbd{long}), \kbd{s} (\kbd{char *}), \kbd{n}
+(variable).
+
+ \item \kbd{p} to supply \kbd{realprecision} (usually \kbd{long prec} in the
+ argument list), \kbd{P} to supply \kbd{seriesprecision} (usually \kbd{long
+ precdl}).
+
+ \noindent We also have special constructs for optional arguments and default
+ values:
+
+ \item \kbd{DG} (optional \kbd{GEN}, \kbd{NULL} if omitted),
+
+ \item \kbd{D\&} (optional \kbd{GEN*}, \kbd{NULL} if omitted),
+
+ \item \kbd{Dn} (optional variable, $-1$ if omitted),
+
+For instance the prototype corresponding to
+\bprog
+  long issquareall(GEN x, GEN *n = NULL)
+ at eprog\noindent is \kbd{lGD\&}.
+
+\misctitle{Caution} This function may not work on all systems, especially
+when \kbd{gp} has been compiled statically. In that case, the first use of an
+installed function will provoke a Segmentation Fault (this should never
+happen with a dynamically linked executable). If you intend to use this
+function, please check first on some harmless example such as the one above
+that it works properly on your machine.
+
+The library syntax is \fun{void}{gpinstall}{const char *name, const char *code, const char *gpname, const char *lib}.
+
+\subsec{kill$(\var{sym})$}\kbdsidx{kill}\label{se:kill}
+Restores the symbol \kbd{sym} to its ``undefined'' status, and deletes any
+help messages associated to \kbd{sym} using \kbd{addhelp}. Variable names
+remain known to the interpreter and keep their former priority: you cannot
+make a variable ``less important" by killing it!
+\bprog
+? z = y = 1; y
+%1 = 1
+? kill(y)
+? y            \\ restored to ``undefined'' status
+%2 = y
+? variable()
+%3 = [x, y, z] \\ but the variable name y is still known, with y > z !
+ at eprog\noindent
+For the same reason, killing a user function (which is an ordinary
+variable holding a \typ{CLOSURE}) does not remove its name from the list of
+variable names.
+
+If the symbol is associated to a variable --- user functions being an
+important special case ---, one may use the \idx{quote} operator
+\kbd{a = 'a} to reset variables to their starting values. However, this
+will not delete a help message associated to \kbd{a}, and is also slightly
+slower than \kbd{kill(a)}.
+\bprog
+? x = 1; addhelp(x, "foo"); x
+%1 = 1
+? x = 'x; x   \\ same as 'kill', except we don't delete help.
+%2 = x
+? ?x
+foo
+ at eprog\noindent
+On the other hand, \kbd{kill} is the only way to remove aliases and installed
+functions.
+\bprog
+? alias(fun, sin);
+? kill(fun);
+
+? install(addii, GG);
+? kill(addii);
+ at eprog
+
+The library syntax is \fun{void}{kill0}{const char *sym}.
+
+\subsec{print$(\{\var{str}\}*)$}\kbdsidx{print}\label{se:print}
+Outputs its (string) arguments in raw format, ending with a newline.
+%\syn{NO}
+
+\subsec{print1$(\{\var{str}\}*)$}\kbdsidx{print1}\label{se:print1}
+Outputs its (string) arguments in raw
+format, without ending with a newline. Note that you can still embed newlines
+within your strings, using the \b{n} notation~!
+%\syn{NO}
+
+\subsec{printf$(\var{fmt},\{x\}*)$}\kbdsidx{printf}\label{se:printf}
+This function is based on the C library command of the same name.
+It prints its arguments according to the format \var{fmt}, which specifies how
+subsequent arguments are converted for output. The format is a
+character string composed of zero or more directives:
+
+\item ordinary characters (not \kbd{\%}), printed unchanged,
+
+\item conversions specifications (\kbd{\%} followed by some characters)
+which fetch one argument from the list and prints it according to the
+specification.
+
+More precisely, a conversion specification consists in a \kbd{\%}, one or more
+optional flags (among \kbd{\#}, \kbd{0}, \kbd{-}, \kbd{+}, ` '), an optional
+decimal digit string specifying a minimal field width, an optional precision
+in the form of a period (`\kbd{.}') followed by a decimal digit string, and
+the conversion specifier (among \kbd{d},\kbd{i}, \kbd{o}, \kbd{u},
+\kbd{x},\kbd{X}, \kbd{p}, \kbd{e},\kbd{E}, \kbd{f}, \kbd{g},\kbd{G}, \kbd{s}).
+
+\misctitle{The flag characters} The character \kbd{\%} is followed by zero or
+more of the following flags:
+
+\item \kbd{\#}: The value is converted to an ``alternate form''. For
+\kbd{o} conversion (octal), a \kbd{0} is prefixed to the string. For \kbd{x}
+and \kbd{X} conversions (hexa), respectively \kbd{0x} and \kbd{0X} are
+prepended. For other conversions, the flag is ignored.
+
+\item \kbd{0}: The value should be zero padded. For
+\kbd{d},
+\kbd{i},
+\kbd{o},
+\kbd{u},
+\kbd{x},
+\kbd{X}
+\kbd{e},
+\kbd{E},
+\kbd{f},
+\kbd{F},
+\kbd{g}, and
+\kbd{G} conversions, the value is padded on the left with zeros rather than
+blanks. (If the \kbd{0} and \kbd{-} flags both appear, the \kbd{0} flag is
+ignored.)
+
+\item \kbd{-}: The value is left adjusted on the field boundary. (The
+default is right justification.) The value is padded on the right with
+blanks, rather than on the left with blanks or zeros. A \kbd{-} overrides a
+\kbd{0} if both are given.
+
+\item \kbd{` '} (a space): A blank is left before a positive number
+produced by a signed conversion.
+
+\item \kbd{+}: A sign (+ or -) is placed before a number produced by a
+signed conversion. A \kbd{+} overrides a space if both are used.
+
+\misctitle{The field width} An optional decimal digit string (whose first
+digit is non-zero) specifying a \emph{minimum} field width. If the value has
+fewer characters than the field width, it is padded with spaces on the left
+(or right, if the left-adjustment flag has been given). In no case does a
+small field width cause truncation of a field; if the value is wider than
+the field width, the field is expanded to contain the conversion result.
+Instead of a decimal digit string, one may write \kbd{*} to specify that the
+field width is given in the next argument.
+
+\misctitle{The precision} An optional precision in the form of a period
+(`\kbd{.}') followed by a decimal digit string. This gives
+the number of digits to appear after the radix character for \kbd{e},
+\kbd{E}, \kbd{f}, and \kbd{F} conversions, the maximum number of significant
+digits for \kbd{g} and \kbd{G} conversions, and the maximum number of
+characters to be printed from an \kbd{s} conversion.
+Instead of a decimal digit string, one may write \kbd{*} to specify that the
+field width is given in the next argument.
+
+\misctitle{The length modifier} This is ignored under \kbd{gp}, but
+necessary for \kbd{libpari} programming. Description given here for
+completeness:
+
+\item \kbd{l}: argument is a \kbd{long} integer.
+
+\item \kbd{P}: argument is a \kbd{GEN}.
+
+\misctitle{The conversion specifier} A character that specifies the type of
+conversion to be applied.
+
+\item \kbd{d}, \kbd{i}: A signed integer.
+
+\item \kbd{o}, \kbd{u}, \kbd{x}, \kbd{X}: An unsigned integer, converted
+to unsigned octal (\kbd{o}), decimal (\kbd{u}) or hexadecimal (\kbd{x} or
+\kbd{X}) notation. The letters \kbd{abcdef} are used for \kbd{x}
+conversions;  the letters \kbd{ABCDEF} are used for \kbd{X} conversions.
+
+\item \kbd{e}, \kbd{E}: The (real) argument is converted in the style
+\kbd{[ -]d.ddd e[ -]dd}, where there is one digit before the decimal point,
+and the number of digits after it is equal to the precision; if the
+precision is missing, use the current \kbd{realprecision} for the total
+number of printed digits. If the precision is explicitly 0, no decimal-point
+character appears. An \kbd{E} conversion uses the letter \kbd{E} rather
+than \kbd{e} to introduce the exponent.
+
+\item \kbd{f}, \kbd{F}: The (real) argument is converted in the style
+\kbd{[ -]ddd.ddd}, where the number of digits after the decimal point
+is equal to the precision; if the precision is missing, use the current
+\kbd{realprecision} for the total number of printed digits. If the precision
+is explicitly 0, no decimal-point character appears. If a decimal point
+appears, at least one digit appears before it.
+
+\item \kbd{g}, \kbd{G}: The (real) argument is converted in style
+\kbd{e} or \kbd{f} (or \kbd{E} or \kbd{F} for \kbd{G} conversions)
+\kbd{[ -]ddd.ddd}, where the total number of digits printed
+is equal to the precision; if the precision is missing, use the current
+\kbd{realprecision}. If the precision is explicitly 0, it is treated as 1.
+Style \kbd{e} is used when
+the decimal exponent is $< -4$, to print \kbd{0.}, or when the integer
+part cannot be decided given the known significant digits, and the \kbd{f}
+format otherwise.
+
+\item \kbd{c}: The integer argument is converted to an unsigned char, and the
+resulting character is written.
+
+\item \kbd{s}: Convert to a character string. If a precision is given, no
+more than the specified number of characters are written.
+
+\item \kbd{p}: Print the address of the argument in hexadecimal (as if by
+\kbd{\%\#x}).
+
+\item \kbd{\%}: A \kbd{\%} is written. No argument is converted. The complete
+conversion specification is \kbd{\%\%}.
+
+\noindent Examples:
+
+\bprog
+? printf("floor: %d, field width 3: %3d, with sign: %+3d\n", Pi, 1, 2);
+floor: 3, field width 3:   1, with sign:  +2
+
+? printf("%.5g %.5g %.5g\n",123,123/456,123456789);
+123.00 0.26974 1.2346 e8
+
+? printf("%-2.5s:%2.5s:%2.5s\n", "P", "PARI", "PARIGP");
+P :PARI:PARIG
+
+\\ min field width and precision given by arguments
+? x = 23; y=-1/x; printf("x=%+06.2f y=%+0*.*f\n", x, 6, 2, y);
+x=+23.00 y=-00.04
+
+\\ minimum fields width 5, pad left with zeroes
+? for (i = 2, 5, printf("%05d\n", 10^i))
+00100
+01000
+10000
+100000  \\@com don't truncate fields whose length is larger than the minimum width
+? printf("%.2f  |%06.2f|", Pi,Pi)
+3.14  |  3.14|
+ at eprog\noindent All numerical conversions apply recursively to the entries
+of vectors and matrices:
+\bprog
+? printf("%4d", [1,2,3]);
+[   1,   2,   3]
+? printf("%5.2f", mathilbert(3));
+[ 1.00  0.50  0.33]
+
+[ 0.50  0.33  0.25]
+
+[ 0.33  0.25  0.20]
+ at eprog
+\misctitle{Technical note} Our implementation of \tet{printf}
+deviates from the C89 and C99 standards in a few places:
+
+\item whenever a precision is missing, the current \kbd{realprecision} is
+used to determine the number of printed digits (C89: use 6 decimals after
+the radix character).
+
+\item in conversion style \kbd{e}, we do not impose that the
+exponent has at least two digits; we never write a \kbd{+} sign in the
+exponent; 0 is printed in a special way, always as \kbd{0.E\var{exp}}.
+
+\item in conversion style \kbd{f}, we switch to style \kbd{e} if the
+exponent is greater or equal to the precision.
+
+\item in conversion \kbd{g} and \kbd{G}, we do not remove trailing zeros
+ from the fractional part of the result; nor a trailing decimal point;
+ 0 is printed in a special way, always as \kbd{0.E\var{exp}}.
+%\syn{NO}
+
+\subsec{printsep$(\var{sep},\{\var{str}\}*)$}\kbdsidx{printsep}\label{se:printsep}
+Outputs its (string) arguments in raw format, ending with a newline.
+Successive entries are separated by \var{sep}:
+\bprog
+? printsep(":", 1,2,3,4)
+1:2:3:4
+ at eprog
+%\syn{NO}
+
+\subsec{printsep1$(\var{sep},\{\var{str}\}*)$}\kbdsidx{printsep1}\label{se:printsep1}
+Outputs its (string) arguments in raw format, without ending with a
+newline.  Successive entries are separated by \var{sep}:
+\bprog
+? printsep1(":", 1,2,3,4);print("|")
+1:2:3:4
+ at eprog
+%\syn{NO}
+
+\subsec{printtex$(\{\var{str}\}*)$}\kbdsidx{printtex}\label{se:printtex}
+Outputs its (string) arguments in \TeX\ format. This output can then be
+used in a \TeX\ manuscript.
+The printing is done on the standard output. If you want to print it to a
+file you should use \kbd{writetex} (see there).
+
+Another possibility is to enable the \tet{log} default
+(see~\secref{se:defaults}).
+You could for instance do:\sidx{logfile}
+%
+\bprog
+default(logfile, "new.tex");
+default(log, 1);
+printtex(result);
+ at eprog
+%\syn{NO}
+
+\subsec{quit$(\{\var{status} = 0\})$}\kbdsidx{quit}\label{se:quit}
+Exits \kbd{gp} and return to the system with exit status
+\kbd{status}, a small integer. A non-zero exit status normally indicates
+abnormal termination. (Note: the system actually sees only
+\kbd{status} mod $256$, see your man pages for \kbd{exit(3)} or \kbd{wait(2)}).
+
+\subsec{read$(\{\var{filename}\})$}\kbdsidx{read}\label{se:read}
+Reads in the file
+\var{filename} (subject to string expansion). If \var{filename} is
+omitted, re-reads the last file that was fed into \kbd{gp}. The return
+value is the result of the last expression evaluated.
+
+If a GP \tet{binary file} is read using this command (see
+\secref{se:writebin}), the file is loaded and the last object in the file
+is returned.
+
+In case the file you read in contains an \tet{allocatemem} statement (to be
+generally avoided), you should leave \kbd{read} instructions by themselves,
+and not part of larger instruction sequences.
+
+\subsec{readstr$(\{\var{filename}\})$}\kbdsidx{readstr}\label{se:readstr}
+Reads in the file \var{filename} and return a vector of GP strings,
+each component containing one line from the file. If \var{filename} is
+omitted, re-reads the last file that was fed into \kbd{gp}.
+
+\subsec{readvec$(\{\var{filename}\})$}\kbdsidx{readvec}\label{se:readvec}
+Reads in the file
+\var{filename} (subject to string expansion). If \var{filename} is
+omitted, re-reads the last file that was fed into \kbd{gp}. The return
+value is a vector whose components are the evaluation of all sequences
+of instructions contained in the file. For instance, if \var{file} contains
+\bprog
+1
+2
+3
+ at eprog\noindent
+then we will get:
+\bprog
+? \r a
+%1 = 1
+%2 = 2
+%3 = 3
+? read(a)
+%4 = 3
+? readvec(a)
+%5 = [1, 2, 3]
+ at eprog
+In general a sequence is just a single line, but as usual braces and
+\kbd{\bs} may be used to enter multiline sequences.
+
+The library syntax is \fun{GEN}{gp_readvec_file}{const char *filename}.
+The underlying library function
+\fun{GEN}{gp_readvec_stream}{FILE *f} is usually more flexible.
+
+\subsec{select$(f, A, \{\fl = 0\})$}\kbdsidx{select}\label{se:select}
+We first describe the default behavior, when $\fl$ is 0 or omitted.
+Given a vector or list \kbd{A} and a \typ{CLOSURE} \kbd{f}, \kbd{select}
+returns the elements $x$ of \kbd{A} such that $f(x)$ is non-zero. In other
+words, \kbd{f} is seen as a selection function returning a boolean value.
+\bprog
+? select(x->isprime(x), vector(50,i,i^2+1))
+%1 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+? select(x->(x<100), %)
+%2 = [2, 5, 17, 37]
+ at eprog\noindent returns the primes of the form $i^2+1$ for some $i\leq 50$,
+then the elements less than 100 in the preceding result. The \kbd{select}
+function also applies to a matrix \kbd{A}, seen as a vector of columns, i.e. it
+selects columns instead of entries, and returns the matrix whose columns are
+the selected ones.
+
+\misctitle{Remark} For $v$ a \typ{VEC}, \typ{COL}, \typ{LIST} or \typ{MAT},
+the alternative set-notations
+\bprog
+[g(x) | x <- v, f(x)]
+[x | x <- v, f(x)]
+[g(x) | x <- v]
+ at eprog\noindent
+are available as shortcuts for
+\bprog
+apply(g, select(f, Vec(v)))
+select(f, Vec(v))
+apply(g, Vec(v))
+ at eprog\noindent respectively:
+\bprog
+? [ x | x <- vector(50,i,i^2+1), isprime(x) ]
+%1 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+ at eprog
+
+\noindent If $\fl = 1$, this function returns instead the \emph{indices} of
+the selected elements, and not the elements themselves (indirect selection):
+\bprog
+? V = vector(50,i,i^2+1);
+? select(x->isprime(x), V, 1)
+%2 = Vecsmall([1, 2, 4, 6, 10, 14, 16, 20, 24, 26, 36, 40])
+? vecextract(V, %)
+%3 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+ at eprog\noindent
+The following function lists the elements in $(\Z/N\Z)^*$:
+\bprog
+? invertibles(N) = select(x->gcd(x,N) == 1, [1..N])
+ at eprog
+
+\noindent Finally
+\bprog
+? select(x->x, M)
+ at eprog\noindent selects the non-0 entries in \kbd{M}. If the latter is a
+\typ{MAT}, we extract the matrix of non-0 columns. Note that \emph{removing}
+entries instead of selecting them just involves replacing the selection
+function \kbd{f} with its negation:
+\bprog
+? select(x->!isprime(x), vector(50,i,i^2+1))
+ at eprog
+
+\synt{genselect}{void *E, long (*fun)(void*,GEN), GEN a}. Also available
+is \fun{GEN}{genindexselect}{void *E, long (*fun)(void*, GEN), GEN a},
+corresponding to $\fl = 1$.
+
+\subsec{setrand$(n)$}\kbdsidx{setrand}\label{se:setrand}
+Reseeds the random number generator using the seed $n$. No value is
+returned. The seed is either a technical array output by \kbd{getrand}, or a
+small positive integer, used to generate deterministically a suitable state
+array. For instance, running a randomized computation starting by
+\kbd{setrand(1)} twice will generate the exact same output.
+
+The library syntax is \fun{void}{setrand}{GEN n}.
+
+\subsec{system$(\var{str})$}\kbdsidx{system}\label{se:system}
+\var{str} is a string representing a system command. This command is
+executed, its output written to the standard output (this won't get into your
+logfile), and control returns to the PARI system. This simply calls the C
+\kbd{system} command.
+
+\subsec{trap$(\{e\}, \{\var{rec}\}, \var{seq})$}\kbdsidx{trap}\label{se:trap}
+THIS FUNCTION IS OBSOLETE: use \tet{iferr}, which has a nicer and much
+more powerful interface. For compatibility's sake we now describe the
+\emph{obsolete} function \tet{trap}.
+
+This function tries to
+evaluate \var{seq}, trapping runtime error $e$, that is effectively preventing
+it from aborting computations in the usual way; the recovery sequence
+\var{rec} is executed if the error occurs and the evaluation of \var{rec}
+becomes the result of the command. If $e$ is omitted, all exceptions are
+trapped. See \secref{se:errorrec} for an introduction to error recovery
+under \kbd{gp}.
+
+\bprog
+? \\@com trap division by 0
+? inv(x) = trap (e_INV, INFINITY, 1/x)
+? inv(2)
+%1 = 1/2
+? inv(0)
+%2 = INFINITY
+ at eprog\noindent
+Note that \var{seq} is effectively evaluated up to the point that produced
+the error, and the recovery sequence is evaluated starting from that same
+context, it does not "undo" whatever happened in the other branch (restore
+the evaluation context):
+\bprog
+? x = 1; trap (, /* recover: */ x, /* try: */ x = 0; 1/x)
+%1 = 0
+ at eprog
+
+\misctitle{Note} The interface is currently not adequate for trapping
+individual exceptions. In the current version \vers, the following keywords
+are recognized, but the name list will be expanded and changed in the
+future (all library mode errors can be trapped: it's a matter of defining
+the keywords to \kbd{gp}):
+
+\kbd{e\_ALARM}: alarm time-out
+
+\kbd{e\_ARCH}: not available on this architecture or operating system
+
+\kbd{e\_STACK}: the PARI stack overflows
+
+\kbd{e\_INV}: impossible inverse
+
+\kbd{e\_IMPL}: not yet implemented
+
+\kbd{e\_OVERFLOW}: all forms of arithmetic overflow, including length
+or exponent overflow (when a larger value is supplied than the
+implementation can handle).
+
+\kbd{e\_SYNTAX}: syntax error
+
+\kbd{e\_MISC}: miscellaneous error
+
+\kbd{e\_TYPE}: wrong type
+
+\kbd{e\_USER}: user error (from the \kbd{error} function)
+
+The library syntax is \fun{GEN}{trap0}{const char *e = NULL, GEN rec = NULL, GEN seq = NULL}.
+
+\subsec{type$(x)$}\kbdsidx{type}\label{se:type}
+This is useful only under \kbd{gp}. Returns the internal type name of
+the PARI object $x$ as a  string. Check out existing type names with the
+metacommand \b{t}. For example \kbd{type(1)} will return "\typ{INT}".
+
+The library syntax is \fun{GEN}{type0}{GEN x}.
+The macro \kbd{typ} is usually simpler to use since it returns a
+\kbd{long} that can easily be matched with the symbols \typ{*}. The name
+\kbd{type} was avoided since it is a reserved identifier for some compilers.
+
+\subsec{uninline$()$}\kbdsidx{uninline}\label{se:uninline}
+(Experimental) Exit the scope of all current \kbd{inline} variables.
+
+\subsec{version$()$}\kbdsidx{version}\label{se:version}
+Returns the current version number as a \typ{VEC} with three integer
+components (major version number, minor version number and patchlevel);
+if your sources were obtained through our version control system, this will
+be followed by further more precise arguments, including
+e.g.~a~\kbd{git} \emph{commit hash}.
+
+This function is present in all versions of PARI following releases 2.3.4
+(stable) and 2.4.3 (testing).
+
+Unless you are working with multiple development versions, you probably only
+care about the 3 first numeric components. In any case, the \kbd{lex} function
+offers a clever way to check against a particular version number, since it will
+compare each successive vector entry, numerically or as strings, and will not
+mind if the vectors it compares have different lengths:
+\bprog
+   if (lex(version(), [2,3,5]) >= 0,
+     \\ code to be executed if we are running 2.3.5 or more recent.
+   ,
+     \\ compatibility code
+   );
+ at eprog\noindent On a number of different machines, \kbd{version()} could return either of
+\bprog
+ %1 = [2, 3, 4]    \\ released version, stable branch
+ %1 = [2, 4, 3]    \\ released version, testing branch
+ %1 = [2, 6, 1, 15174, ""505ab9b"] \\ development
+ at eprog
+
+In particular, if you are only working with released versions, the first
+line of the gp introductory message can be emulated by
+\bprog
+   [M,m,p] = version();
+   printf("GP/PARI CALCULATOR Version %s.%s.%s", M,m,p);
+ @eprog\noindent If you \emph{are} working with many development versions of
+ PARI/GP, the 4th and/or 5th components can be profitably included in the
+ name of your logfiles, for instance.
+
+ \misctitle{Technical note} For development versions obtained via \kbd{git},
+ the 4th and 5th components are liable to change eventually, but we document
+ their current meaning for completeness. The 4th component counts the number
+ of reachable commits in the branch (analogous to \kbd{svn}'s revision
+ number), and the 5th is the \kbd{git} commit hash. In particular, \kbd{lex}
+ comparison still orders correctly development versions with respect to each
+ others or to released versions (provided we stay within a given branch,
+ e.g. \kbd{master})!
+
+The library syntax is \fun{GEN}{pari_version}{}.
+
+\subsec{warning$(\{\var{str}\}*)$}\kbdsidx{warning}\label{se:warning}
+Outputs the message ``user warning''
+and the argument list (each of them interpreted as a string).
+If colors are enabled, this warning will be in a different color,
+making it easy to distinguish.
+\bprog
+warning(n, " is very large, this might take a while.")
+ at eprog
+% \syn{NO}
+
+\subsec{whatnow$(\var{key})$}\kbdsidx{whatnow}\label{se:whatnow}
+If keyword \var{key} is the name of a function that was present in GP
+version 1.39.15 or lower, outputs the new function name and syntax, if it
+changed at all ($387$ out of $560$ did).
+
+\subsec{write$(\var{filename},\{\var{str}\}*)$}\kbdsidx{write}\label{se:write}
+Writes (appends) to \var{filename} the remaining arguments, and appends a
+newline (same output as \kbd{print}).
+%\syn{NO}
+
+\subsec{write1$(\var{filename},\{\var{str}\}*)$}\kbdsidx{write1}\label{se:write1}
+Writes (appends) to \var{filename} the remaining arguments without a
+trailing newline (same output as \kbd{print1}).
+%\syn{NO}
+
+\subsec{writebin$(\var{filename},\{x\})$}\kbdsidx{writebin}\label{se:writebin}
+Writes (appends) to
+\var{filename} the object $x$ in binary format. This format is not human
+readable, but contains the exact internal structure of $x$, and is much
+faster to save/load than a string expression, as would be produced by
+\tet{write}. The binary file format includes a magic number, so that such a
+file can be recognized and correctly input by the regular \tet{read} or \b{r}
+function. If saved objects refer to (polynomial) variables that are not
+defined in the new session, they will be displayed in a funny way (see
+\secref{se:kill}). Installed functions and history objects can not be saved
+via this function.
+
+If $x$ is omitted, saves all user variables from the session, together with
+their names. Reading such a ``named object'' back in a \kbd{gp} session will set
+the corresponding user variable to the saved value. E.g after
+\bprog
+x = 1; writebin("log")
+ at eprog\noindent
+reading \kbd{log} into a clean session will set \kbd{x} to $1$.
+The relative variables priorities (see \secref{se:priority}) of new variables
+set in this way remain the same (preset variables retain their former
+priority, but are set to the new value). In particular, reading such a
+session log into a clean session will restore all variables exactly as they
+were in the original one.
+
+Just as a regular input file, a binary file can be compressed
+using \tet{gzip}, provided the file name has the standard \kbd{.gz}
+extension.\sidx{binary file}
+
+In the present implementation, the binary files are architecture dependent
+and compatibility with future versions of \kbd{gp} is not guaranteed. Hence
+binary files should not be used for long term storage (also, they are
+larger and harder to compress than text files).
+
+The library syntax is \fun{void}{gpwritebin}{const char *filename, GEN x = NULL}.
+
+\subsec{writetex$(\var{filename},\{\var{str}\}*)$}\kbdsidx{writetex}\label{se:writetex}
+As \kbd{write}, in \TeX\ format.
+%\syn{NO}
+%SECTION: programming/specific
+
+\section{Parallel programming}
+
+These function are only available if PARI was configured using \kbd{Configure
+--mt=\dots}. Two multithread interfaces are supported:
+
+\item POSIX threads
+
+\item Message passing interface (MPI)
+
+As a rule, POSIX threads are well-suited for single systems, while MPI is used
+by most clusters. However the parallel GP interface does not depend on the
+chosen multithread interface: a properly written GP program will work
+identically with both.
+
+
+\subsec{parapply$(f, x)$}\kbdsidx{parapply}\label{se:parapply}
+Parallel evaluation of \kbd{f} on the elements of \kbd{x}.
+The function \kbd{f} must not access global variables or variables
+declared with local(), and must be free of side effects.
+\bprog
+parapply(factor,[2^256 + 1, 2^193 - 1])
+ at eprog
+factors $2^{256} + 1$ and $2^{193} - 1$ in parallel.
+\bprog
+{
+  my(E = ellinit([1,3]), V = vector(12,i,randomprime(2^200)));
+  parapply(p->ellcard(E,p), V)
+}
+ at eprog
+computes the order of $E(\F_p)$ for $12$ random primes of $200$ bits.
+
+The library syntax is \fun{GEN}{parapply}{GEN f, GEN x}.
+
+\subsec{pareval$(x)$}\kbdsidx{pareval}\label{se:pareval}
+Parallel evaluation of the elements of \kbd{x}, where \kbd{x} is a
+vector of closures. The closures must be of arity $0$, must not access
+global variables or variables declared with \kbd{local} and must be
+free of side effects.
+
+The library syntax is \fun{GEN}{pareval}{GEN x}.
+
+\subsec{parfor$(i=a,\{b\},\var{expr1},\{j\},\{\var{expr2}\})$}\kbdsidx{parfor}\label{se:parfor}
+Evaluates the sequence \kbd{expr2} (dependent on $i$ and $j$) for $i$
+between $a$ and $b$, in random order, computed in parallel; in this sequence
+\kbd{expr2}, substitute the variable $j$ by the value of \kbd{expr1}
+(dependent on $i$). If $b$ is omitted, the loop will not stop.
+
+It is allowed for \kbd{expr2} to exit the loop using
+\kbd{break}/\kbd{next}/\kbd{return}; however in that case, \kbd{expr2} will
+still be evaluated for all remaining value of $i$ less than the current one,
+unless a subsequent \kbd{break}/\kbd{next}/\kbd{return} happens.
+%\syn{NO}
+
+\subsec{parforprime$(p=a,\{b\},\var{expr1},\{j\},\{\var{expr2}\})$}\kbdsidx{parforprime}\label{se:parforprime}
+Evaluates the sequence \kbd{expr2} (dependent on $p$ and $j$) for $p$
+prime between $a$ and $b$, in random order, computed in parallel. Substitute
+for $j$ the value of \kbd{expr1} (dependent on $p$).
+If $b$ is omitted, the loop will not stop.
+
+It is allowed fo \kbd{expr2} to exit the loop using
+\kbd{break}/\kbd{next}/\kbd{return}, however in that case, \kbd{expr2} will
+still be evaluated for all remaining value of $p$ less than the current one,
+unless a subsequent \kbd{break}/\kbd{next}/\kbd{return} happens.
+%\syn{NO}
+
+\subsec{parselect$(f, A, \{\fl = 0\})$}\kbdsidx{parselect}\label{se:parselect}
+Selects elements of $A$ according to the selection function $f$, done in
+parallel.  If \fl is $1$, return the indices of those elements (indirect
+selection) The function \kbd{f} must not access global variables or
+variables declared with local(), and must be free of side effects.
+
+The library syntax is \fun{GEN}{parselect}{GEN f, GEN A, long flag }.
+
+\subsec{parsum$(i=a,b,\var{expr},\{x\})$}\kbdsidx{parsum}\label{se:parsum}
+Sum of expression \var{expr}, initialized at $x$, the formal parameter
+going from $a$ to $b$, evaluated in parallel in random order.
+The expression \kbd{expr} must not access global variables or
+variables declared with \kbd{local()}, and must be free of side effects.
+\bprog
+parsum(i=1,1000,ispseudoprime(2^prime(i)-1))
+ at eprog
+returns the numbers of prime numbers among the first $1000$ Mersenne numbers.
+%\syn{NO}
+
+\subsec{parvector$(N,i,\var{expr})$}\kbdsidx{parvector}\label{se:parvector}
+As \kbd{vector(N,i,expr)} but the evaluations of \kbd{expr} are done in
+parallel. The expression \kbd{expr} must not access global variables or
+variables declared with \kbd{local()}, and must be free of side effects.
+\bprog
+parvector(10,i,quadclassunit(2^(100+i)+1).no)
+ at eprog\noindent
+computes the class numbers in parallel.
+%\syn{NO}
+%SECTION: programming/parallel
+
+\section{GP defaults}
+\label{se:gp_defaults} This section documents the GP defaults
+
+
+\subsec{TeXstyle}\kbdsidx{TeXstyle}\label{se:def,TeXstyle}
+The bits of this default allow
+\kbd{gp} to use less rigid TeX formatting commands in the logfile. This
+default is only taken into account when $\kbd{log} = 3$. The bits of
+\kbd{TeXstyle} have the following meaning
+
+2: insert \kbd{\bs right} / \kbd{\bs left} pairs where appropriate.
+
+4: insert discretionary breaks in polynomials, to enhance the probability of
+a good line break.
+
+The default value is \kbd{0}.
+
+\subsec{breakloop}\kbdsidx{breakloop}\label{se:def,breakloop}
+If true, enables the ``break loop'' debugging mode, see
+\secref{se:break_loop}.
+
+The default value is \kbd{1} if we are running an interactive \kbd{gp}
+session, and \kbd{0} otherwise.
+
+\subsec{colors}\kbdsidx{colors}\label{se:def,colors}
+This default is only usable if \kbd{gp}
+is running within certain color-capable terminals. For instance \kbd{rxvt},
+\kbd{color\_xterm} and modern versions of \kbd{xterm} under X Windows, or
+standard Linux/DOS text consoles. It causes \kbd{gp} to use a small palette of
+colors for its output. With xterms, the colormap used corresponds to the
+resources \kbd{Xterm*color$n$} where $n$ ranges from $0$ to $15$ (see the
+file \kbd{misc/color.dft} for an example). Accepted values for this
+default are strings \kbd{"$a_1$,\dots,$a_k$"} where $k\le7$ and each
+$a_i$ is either
+
+\noindent\item the keyword \kbd{no} (use the default color, usually
+black on transparent background)
+
+\noindent\item an integer between 0 and 15 corresponding to the
+aforementioned colormap
+
+\noindent\item a triple $[c_0,c_1,c_2]$ where $c_0$ stands for foreground
+color, $c_1$ for background color, and $c_2$ for attributes (0 is default, 1
+is bold, 4 is underline).
+
+The output objects thus affected are respectively error messages,
+history numbers, prompt, input line, output, help messages, timer (that's
+seven of them). If $k < 7$, the remaining $a_i$ are assumed to be $no$. For
+instance
+%
+\bprog
+default(colors, "9, 5, no, no, 4")
+ at eprog
+\noindent
+typesets error messages in color $9$, history numbers in color $5$, output in
+color $4$, and does not affect the rest.
+
+A set of default colors for dark (reverse video or PC console) and light
+backgrounds respectively is activated when \kbd{colors} is set to
+\kbd{darkbg}, resp.~\kbd{lightbg} (or any proper prefix: \kbd{d} is
+recognized as an abbreviation for \kbd{darkbg}). A bold variant of
+\kbd{darkbg}, called \kbd{boldfg}, is provided if you find the former too
+pale.
+
+\emacs In the present version, this default is incompatible with PariEmacs.
+Changing it will just fail silently (the alternative would be to display
+escape sequences as is, since Emacs will refuse to interpret them).
+You must customize color highlighting from the PariEmacs side, see its
+documentation.
+
+The default value is \kbd{""} (no colors).
+
+\subsec{compatible}\kbdsidx{compatible}\label{se:def,compatible}
+The GP function names and syntax
+have changed tremendously between versions 1.xx and 2.00. To help you cope
+with this we provide some kind of backward compatibility, depending on the
+value of this default:
+
+\quad \kbd{compatible} = 0: no backward compatibility. In this mode, a very
+handy function, to be described in \secref{se:whatnow}, is \kbd{whatnow},
+which tells you what has become of your favorite functions, which \kbd{gp}
+suddenly can't seem to remember.
+
+\quad \kbd{compatible} = 1: warn when using obsolete functions, but
+otherwise accept them. The output uses the new conventions though, and
+there may be subtle incompatibilities between the behavior of former and
+current functions, even when they share the same name (the current function
+is used in such cases, of course!). We thought of this one as a transitory
+help for \kbd{gp} old-timers. Thus, to encourage switching to \kbd{compatible}=0,
+it is not possible to disable the warning.
+
+\quad \kbd{compatible} = 2: use only the old function naming scheme (as
+used up to version 1.39.15), but \emph{taking case into account}. Thus
+\kbd{I} (${}=\sqrt{-1}$) is not the same as \kbd{i} (user variable, unbound
+by default), and you won't get an error message using \kbd{i} as a loop
+index as used to be the case.
+
+\quad \kbd{compatible} = 3: try to mimic exactly the former behavior. This
+is not always possible when functions have changed in a fundamental way.
+But these differences are usually for the better (they were meant to,
+anyway), and will probably not be discovered by the casual user.
+
+One adverse side effect is that any user functions and aliases that have
+been defined \emph{before} changing \kbd{compatible} will get erased if this
+change modifies the function list, i.e.~if you move between groups
+$\{0,1\}$ and $\{2,3\}$ (variables are unaffected). We of course strongly
+encourage you to try and get used to the setting \kbd{compatible}=0.
+
+Note that the default \tet{new_galois_format} is another compatibility setting,
+which is completely independent of \kbd{compatible}.
+
+The default value is \kbd{0}.
+
+\subsec{datadir}\kbdsidx{datadir}\label{se:def,datadir}
+The name of directory containing the optional data files. For now,
+this includes the \kbd{elldata}, \kbd{galdata}, \kbd{galpol}, \kbd{seadata}
+packages.
+
+The default value is \datadir (the location of installed precomputed data,
+can be specified via \kbd{Configure --datadir=}).
+
+\subsec{debug}\kbdsidx{debug}\label{se:def,debug}
+Debugging level. If it is non-zero, some extra messages may be printed,
+according to what is going on (see~\b{g}).
+
+The default value is \kbd{0} (no debugging messages).
+
+\subsec{debugfiles}\kbdsidx{debugfiles}\label{se:def,debugfiles}
+File usage debugging level. If it is non-zero, \kbd{gp} will print
+information on file descriptors in use, from PARI's point of view
+(see~\b{gf}).
+
+The default value is \kbd{0} (no debugging messages).
+
+\subsec{debugmem}\kbdsidx{debugmem}\label{se:def,debugmem}
+Memory debugging level. If it is non-zero, \kbd{gp} will regularly print
+information on memory usage. If it's greater than 2, it will indicate any
+important garbage collecting and the function it is taking place in
+(see~\b{gm}).
+
+\noindent {\bf Important Note:} As it noticeably slows down the performance,
+the first functionality (memory usage) is disabled if you're not running a
+version compiled for debugging (see Appendix~A).
+
+The default value is \kbd{0} (no debugging messages).
+
+\subsec{echo}\kbdsidx{echo}\label{se:def,echo}
+This toggle is either 1 (on) or 0 (off). When \kbd{echo}
+mode is on, each command is reprinted before being executed. This can be
+useful when reading a file with the \b{r} or \kbd{read} commands. For
+example, it is turned on at the beginning of the test files used to check
+whether \kbd{gp} has been built correctly (see \b{e}).
+
+The default value is \kbd{0} (no echo).
+
+\subsec{factor\_add\_primes}\kbdsidx{def,factor_add_primes}\label{se:def,factor_add_primes}
+This toggle is either 1 (on) or 0 (off). If on,
+the integer factorization machinery calls \tet{addprimes} on primes
+factor that were difficult to find (larger than $2^24$), so they are
+automatically tried first in other factorizations. If a routine is performing
+(or has performed) a factorization and is interrupted by an error or via
+Control-C, this lets you recover the prime factors already found. The
+downside is that a huge \kbd{addprimes} table unrelated to the current
+computations will slow down arithmetic functions relying on integer
+factorization; one should then empty the table using \tet{removeprimes}.
+
+The default value is \kbd{0}.
+
+\subsec{factor\_proven}\kbdsidx{def,factor_proven}\label{se:def,factor_proven}
+This toggle is either 1 (on) or 0 (off). By
+default, the factors output by the integer factorization machinery are
+only pseudo-primes, not proven primes. If this toggle is
+set, a primality proof is done for each factor and all results depending on
+integer factorization are fully proven. This flag does not affect partial
+factorization when it is explicitly requested. It also does not affect the
+private table managed by \tet{addprimes}: its entries are included as is in
+factorizations, without being tested for primality.
+
+The default value is \kbd{0}.
+
+\subsec{format}\kbdsidx{format}\label{se:def,format}
+Of the form x$.n$, where x (conversion style)
+is a letter in $\{\kbd{e},\kbd{f},\kbd{g}\}$, and $n$ (precision) is an
+integer; this affects the way real numbers are printed:
+
+\item If the conversion style is \kbd{e}, real numbers are printed in
+\idx{scientific format}, always with an explicit exponent,
+e.g.~\kbd{3.3 E-5}.
+
+\item In style \kbd{f}, real numbers are generally printed in \idx{fixed
+floating point format} without exponent, e.g.~\kbd{0.000033}. A large
+real number, whose integer part is not well defined (not enough significant
+digits), is printed in style~\kbd{e}. For instance \kbd{10.\pow 100} known to
+ten significant digits is always printed in style \kbd{e}.
+
+\item In style \kbd{g}, non-zero real numbers are printed in \kbd{f} format,
+except when their decimal exponent is $< -4$, in which case they are printed in
+\kbd{e} format. Real zeroes (of arbitrary exponent) are printed in \kbd{e}
+format.
+
+The precision $n$ is the number of significant digits printed for real
+numbers, except if $n<0$ where all the significant digits will be printed
+(initial default 28, or 38 for 64-bit machines). For more powerful formatting
+possibilities, see \tet{printf} and \tet{Strprintf}.
+
+The default value is \kbd{"g.28"} and \kbd{"g.38"} on 32-bit and
+64-bit machines, respectively.
+
+\subsec{graphcolormap}\kbdsidx{graphcolormap}\label{se:def,graphcolormap}
+A vector of colors, to be
+used by hi-res graphing routines. Its length is arbitrary, but it must
+contain at least 3 entries: the first 3 colors are used for background,
+frame/ticks and axes respectively. All colors in the colormap may be freely
+used in \tet{plotcolor} calls.
+
+A color is either given as in the default by character strings or by an RGB
+code. For valid character strings, see the standard \kbd{rgb.txt} file in X11
+distributions, where we restrict to lowercase letters and remove all
+whitespace from color names. An RGB code is a vector with 3 integer entries
+between 0 and 255. For instance \kbd{[250, 235, 215]} and
+\kbd{"antiquewhite"} represent the same color. RGB codes are cryptic but
+often easier to generate.
+
+The default value is [\kbd{"white"}, \kbd{"black"}, \kbd{"blue"},
+\kbd{"violetred"}, \kbd{"red"}, \kbd{"green"}, \kbd{"grey"},
+\kbd{"gainsboro"}].
+
+\subsec{graphcolors}\kbdsidx{graphcolors}\label{se:def,graphcolors}
+Entries in the
+\tet{graphcolormap} that will be used to plot multi-curves. The successive
+curves are drawn in colors
+
+\kbd{graphcolormap[graphcolors[1]]}, \kbd{graphcolormap[graphcolors[2]]},
+  \dots
+
+cycling when the \kbd{graphcolors} list is exhausted.
+
+The default value is \kbd{[4,5]}.
+
+\subsec{help}\kbdsidx{help}\label{se:def,help}
+Name of the external help program to use from within \kbd{gp} when
+extended help is invoked, usually through a \kbd{??} or \kbd{???} request
+(see \secref{se:exthelp}), or \kbd{M-H} under readline (see
+\secref{se:readline}).
+
+The default value is the path to the \kbd{gphelp} script we install.
+
+\subsec{histfile}\kbdsidx{histfile}\label{se:def,histfile}
+Name of a file where
+\kbd{gp} will keep a history of all \emph{input} commands (results are
+omitted). If this file exists when the value of \kbd{histfile} changes,
+it is read in and becomes part of the session history. Thus, setting this
+default in your gprc saves your readline history between sessions. Setting
+this default to the empty string \kbd{""} changes it to
+\kbd{$<$undefined$>$}
+
+The default value is \kbd{$<$undefined$>$} (no history file).
+
+\subsec{histsize}\kbdsidx{histsize}\label{se:def,histsize}
+\kbd{gp} keeps a history of the last
+\kbd{histsize} results computed so far, which you can recover using the
+\kbd{\%} notation (see \secref{se:history}). When this number is exceeded,
+the oldest values are erased. Tampering with this default is the only way to
+get rid of the ones you do not need anymore.
+
+The default value is \kbd{5000}.
+
+\subsec{lines}\kbdsidx{lines}\label{se:def,lines}
+If set to a positive value, \kbd{gp} prints at
+most that many lines from each result, terminating the last line shown with
+\kbd{[+++]} if further material has been suppressed. The various \kbd{print}
+commands (see \secref{se:gp_program}) are unaffected, so you can always type
+\kbd{print(\%)} or \b{a} to view the full result. If the actual screen width
+cannot be determined, a ``line'' is assumed to be 80 characters long.
+
+The default value is \kbd{0}.
+
+\subsec{linewrap}\kbdsidx{linewrap}\label{se:def,linewrap}
+If set to a positive value, \kbd{gp} wraps every single line after
+printing that many characters.
+
+The default value is \kbd{0} (unset).
+
+\subsec{log}\kbdsidx{log}\label{se:def,log}
+This can be either 0 (off) or 1, 2, 3
+(on, see below for the various modes). When logging mode is turned on, \kbd{gp}
+opens a log file, whose exact name is determined by the \kbd{logfile}
+default. Subsequently, all the commands and results will be written to that
+file (see \b{l}). In case a file with this precise name already existed, it
+will not be erased: your data will be \emph{appended} at the end.
+
+The specific positive values of \kbd{log} have the following meaning
+
+1: plain logfile
+
+2: emit color codes to the logfile (if \kbd{colors} is set).
+
+3: write LaTeX output to the logfile (can be further customized using
+\tet{TeXstyle}).
+
+The default value is \kbd{0}.
+
+\subsec{logfile}\kbdsidx{logfile}\label{se:def,logfile}
+Name of the log file to be used when the \kbd{log} toggle is on.
+Environment and time expansion are performed.
+
+The default value is \kbd{"pari.log"}.
+
+\subsec{nbthreads}\kbdsidx{nbthreads}\label{se:def,nbthreads}
+Number of threads to use for parallel computing.
+The exact meaning an default depend on the \kbd{mt} engine used:
+
+\item \kbd{single}: not used (always one thread).
+
+\item \kbd{pthread}: number of threads (unlimited, default: number of core)
+
+\item \kbd{mpi}: number of MPI process to use (limited to the number allocated by \kbd{mpirun},
+default: use all allocated process).
+
+\subsec{new\_galois\_format}\kbdsidx{def,new_galois_format}\label{se:def,new_galois_format}
+This toggle is either 1 (on) or 0 (off). If on,
+the \tet{polgalois} command will use a different, more
+consistent, naming scheme for Galois groups. This default is provided to
+ensure that scripts can control this behavior and do not break unexpectedly.
+
+The default value is \kbd{0}. This value will change to $1$ (set) in the next
+major version.
+
+\subsec{output}\kbdsidx{output}\label{se:def,output}
+There are three possible values: 0
+(=~\var{raw}), 1 (=~\var{prettymatrix}), or 3
+(=~\var{external} \var{prettyprint}). This
+means that, independently of the default \kbd{format} for reals which we
+explained above, you can print results in three ways:
+
+\item \tev{raw format}, i.e.~a format which is equivalent to what you
+input, including explicit multiplication signs, and everything typed on a
+line instead of two dimensional boxes. This can have several advantages, for
+instance it allows you to pick the result with a mouse or an editor, and to
+paste it somewhere else.
+
+\item \tev{prettymatrix format}: this is identical to raw format, except
+that matrices are printed as boxes instead of horizontally. This is
+prettier, but takes more space and cannot be used for input. Column vectors
+are still printed horizontally.
+
+\item \tev{external prettyprint}: pipes all \kbd{gp}
+output in TeX format to an external prettyprinter, according to the value of
+\tet{prettyprinter}. The default script (\tet{tex2mail}) converts its input
+to readable two-dimensional text.
+
+Independently of the setting of this default, an object can be printed
+in any of the three formats at any time using the commands \b{a} and \b{m}
+and \b{B} respectively.
+
+The default value is \kbd{1} (\var{prettymatrix}).
+
+\subsec{parisize}\kbdsidx{parisize}\label{se:def,parisize}
+\kbd{gp}, and in fact any program using the PARI
+library, needs a \tev{stack} in which to do its computations. \kbd{parisize}
+is the stack size, in bytes. It is strongly recommended you increase this
+default (using the \kbd{-s} command-line switch, or a \tet{gprc}) if you can
+afford it. Don't increase it beyond the actual amount of RAM installed on
+your computer or \kbd{gp} will spend most of its time paging.
+
+In case of emergency, you can use the \tet{allocatemem} function to
+increase \kbd{parisize}, once the session is started.
+
+The default value is 4M, resp.~8M on a 32-bit, resp.~64-bit machine.
+
+\subsec{path}\kbdsidx{path}\label{se:def,path}
+This is a list of directories, separated by colons ':'
+(semicolons ';' in the DOS world, since colons are preempted for drive names).
+When asked to read a file whose name is not given by an absolute path
+(does not start with \kbd{/}, \kbd{./} or \kbd{../}), \kbd{gp} will look for
+it in these directories, in the order they were written in \kbd{path}. Here,
+as usual, \kbd{.} means the current directory, and \kbd{..} its immediate
+parent. Environment expansion is performed.
+
+The default value is \kbd{".:\til:\til/gp"} on UNIX systems,
+\kbd{".;C:\bs;C:\bs GP"} on DOS, OS/2 and Windows, and \kbd{"."} otherwise.
+
+\subsec{prettyprinter}\kbdsidx{prettyprinter}\label{se:def,prettyprinter}
+The name of an external prettyprinter to use when
+\kbd{output} is~3 (alternate prettyprinter). Note that the default
+\tet{tex2mail} looks much nicer than the built-in ``beautified
+format'' ($\kbd{output} = 2$).
+
+The default value is \kbd{"tex2mail -TeX -noindent -ragged -by\_par"}.
+
+\subsec{primelimit}\kbdsidx{primelimit}\label{se:def,primelimit}
+\kbd{gp} precomputes a list of
+all primes less than \kbd{primelimit} at initialization time, and can build
+fast sieves on demand to quickly iterate over primes up to the \emph{square}
+of \kbd{primelimit}. These are used by many arithmetic functions, usually for
+trial division purposes. The maximal value is $2^{32} - 2049$ (resp $2^{64} -
+2049$) on a 32-bit (resp.~64-bit) machine, but values beyond $10^8$,
+allowing to iterate over primes up to $10^{16}$, do not seem useful.
+
+Since almost all arithmetic functions eventually require some table of prime
+numbers, PARI guarantees that the first 6547 primes, up to and
+including 65557, are precomputed, even if \kbd{primelimit} is $1$.
+
+This default is only used on startup: changing it will not recompute a new
+table.
+
+\misctitle{Deprecated feature} \kbd{primelimit} was used in some
+situations by algebraic number theory functions using the
+\tet{nf_PARTIALFACT} flag (\tet{nfbasis}, \tet{nfdisc}, \tet{nfinit}, \dots):
+this assumes that all primes $p > \kbd{primelimit}$ have a certain
+property (the equation order is $p$-maximal). This is never done by default,
+and must be explicitly set by the user of such functions. Nevertheless,
+these functions now provide a more flexible interface, and their use
+of the global default \kbd{primelimit} is deprecated.
+
+\misctitle{Deprecated feature} \kbd{factor(N, 0)} was used to partially
+factor integers by removing all prime factors $\leq$ \kbd{primelimit}.
+Don't use this, supply an explicit bound: \kbd{factor(N, bound)},
+which avoids relying on an unpredictable global variable.
+
+The default value is \kbd{500k}.
+
+\subsec{prompt}\kbdsidx{prompt}\label{se:def,prompt}
+A string that will be printed as
+prompt. Note that most usual escape sequences are available there: \b{e} for
+Esc, \b{n} for Newline, \dots, \kbd{\bs\bs} for \kbd{\bs}. Time expansion is
+performed.
+
+This string is sent through the library function \tet{strftime} (on a
+Unix system, you can try \kbd{man strftime} at your shell prompt). This means
+that \kbd{\%} constructs have a special meaning, usually related to the time
+and date. For instance, \kbd{\%H} = hour (24-hour clock) and \kbd{\%M} =
+minute [00,59] (use \kbd{\%\%} to get a real \kbd{\%}).
+
+If you use \kbd{readline}, escape sequences in your prompt will result in
+display bugs. If you have a relatively recent \kbd{readline} (see the comment
+at the end of \secref{se:def,colors}), you can brace them with special sequences
+(\kbd{\bs[} and \kbd{\bs]}), and you will be safe. If these just result in
+extra spaces in your prompt, then you'll have to get a more recent
+\kbd{readline}. See the file \kbd{misc/gprc.dft} for an example.
+
+\emacs {\bf Caution}: PariEmacs needs to know about the prompt pattern to
+separate your input from previous \kbd{gp} results, without ambiguity. It is
+not a trivial problem to adapt automatically this regular expression to an
+arbitrary prompt (which can be self-modifying!). See PariEmacs's
+documentation.
+
+The default value is \kbd{"? "}.
+
+\subsec{prompt\_cont}\kbdsidx{def,prompt_cont}\label{se:def,prompt_cont}
+A string that will be printed
+to prompt for continuation lines (e.g. in between braces, or after a
+line-terminating backslash). Everything that applies to \kbd{prompt}
+applies to \kbd{prompt\_cont} as well.
+
+The default value is \kbd{""}.
+
+\subsec{psfile}\kbdsidx{psfile}\label{se:def,psfile}
+Name of the default file where
+\kbd{gp} is to dump its PostScript drawings (these are appended, so that no
+previous data are lost). Environment and time expansion are performed.
+
+The default value is \kbd{"pari.ps"}.
+
+\subsec{readline}\kbdsidx{readline}\label{se:def,readline}
+Switches readline line-editing
+facilities on and off. This may be useful if you are running \kbd{gp} in a Sun
+\tet{cmdtool}, which interacts badly with readline. Of course, until readline
+is switched on again, advanced editing features like automatic completion
+and editing history are not available.
+
+The default value is \kbd{1}.
+
+\subsec{realprecision}\kbdsidx{realprecision}\label{se:def,realprecision}
+The number of significant digits used to convert exact inputs given to
+transcendental functions (see \secref{se:trans}), or to create
+absolute floating point constants (input as \kbd{1.0} or \kbd{Pi} for
+instance). Unless you tamper with the \tet{format} default, this is also
+the number of significant digits used to print a \typ{REAL} number;
+\kbd{format} will override this latter behaviour, and allow you to have a
+large internal precision while outputting few digits for instance.
+
+Note that PARI's internal precision works on a word basis (by increments of
+32 or 64 bits), hence may be a little larger than the number of decimal
+digits you expected. For instance to get 2 decimal digits you need one word
+of precision which, on a 64-bit machine, actually gives you 19 digits ($19 <
+\log_{10}(2^{64}) < 20$). The value returned when typing
+\kbd{default(realprecision)} is the internal number of significant digits,
+not the number of printed digits:
+\bprog
+? default(realprecision, 2)
+      realprecision = 19 significant digits (2 digits displayed)
+? default(realprecision)
+%1 = 19
+ at eprog
+The default value is \kbd{38}, resp.~\kbd{28}, on a 64-bit, resp~.32-bit,
+machine.
+
+\subsec{recover}\kbdsidx{recover}\label{se:def,recover}
+This toggle is either 1 (on) or 0 (off). If you change this to $0$, any
+error becomes fatal and causes the gp interpreter to exit immediately. Can be
+useful in batch job scripts.
+
+The default value is \kbd{1}.
+
+\subsec{secure}\kbdsidx{secure}\label{se:def,secure}
+This toggle is either 1 (on) or 0 (off). If on, the \tet{system} and
+\tet{extern} command are disabled. These two commands are potentially
+dangerous when you execute foreign scripts since they let \kbd{gp} execute
+arbitrary UNIX commands. \kbd{gp} will ask for confirmation before letting
+you (or a script) unset this toggle.
+
+The default value is \kbd{0}.
+
+\subsec{seriesprecision}\kbdsidx{seriesprecision}\label{se:def,seriesprecision}
+Number of significant terms
+when converting a polynomial or rational function to a power series
+(see~\b{ps}).
+
+The default value is \kbd{16}.
+
+\subsec{simplify}\kbdsidx{simplify}\label{se:def,simplify}
+This toggle is either 1 (on) or 0 (off). When the PARI library computes
+something, the type of the
+result is not always the simplest possible. The only type conversions which
+the PARI library does automatically are rational numbers to integers (when
+they are of type \typ{FRAC} and equal to integers), and similarly rational
+functions to polynomials (when they are of type \typ{RFRAC} and equal to
+polynomials). This feature is useful in many cases, and saves time, but can
+be annoying at times. Hence you can disable this and, whenever you feel like
+it, use the function \kbd{simplify} (see Chapter 3) which allows you to
+simplify objects to the simplest possible types recursively (see~\b{y}).
+\sidx{automatic simplification}
+
+The default value is \kbd{1}.
+
+\subsec{sopath}\kbdsidx{sopath}\label{se:def,sopath}
+This is a list of directories, separated by colons ':'
+(semicolons ';' in the DOS world, since colons are preempted for drive names).
+When asked to \tet{install} an external symbol from a shared library whose
+name is not given by an absolute path (does not start with \kbd{/}, \kbd{./}
+or \kbd{../}), \kbd{gp} will look for it in these directories, in the order
+they were written in \kbd{sopath}. Here, as usual, \kbd{.} means the current
+directory, and \kbd{..} its immediate parent. Environment expansion is
+performed.
+
+The default value is \kbd{""}, corresponding to an empty list of
+directories: \tet{install} will use the library name as input (and look in
+the current directory if the name is not an absolute path).
+
+\subsec{strictargs}\kbdsidx{strictargs}\label{se:def,strictargs}
+This toggle is either 1 (on) or 0 (off). If on, all arguments to \emph{new}
+user functions are mandatory unless the function supplies an explicit default
+value.
+Otherwise arguments have the default value $0$.
+
+In this example,
+\bprog
+  fun(a,b=2)=a+b
+ at eprog
+\kbd{a} is mandatory, while \kbd{b} is optional. If \kbd{strictargs} is on:
+\bprog
+? fun()
+ ***   at top-level: fun()
+ ***                 ^-----
+ ***   in function fun: a,b=2
+ ***                    ^-----
+ ***   missing mandatory argument 'a' in user function.
+ at eprog
+This applies to functions defined while \kbd{strictargs} is on. Changing \kbd{strictargs}
+does not affect the behavior of previously defined functions.
+
+The default value is \kbd{0}.
+
+\subsec{strictmatch}\kbdsidx{strictmatch}\label{se:def,strictmatch}
+This toggle is either 1 (on) or 0 (off). If on, unused characters after a
+sequence has been
+processed will produce an error. Otherwise just a warning is printed. This
+can be useful when you are unsure how many parentheses you have to close
+after complicated nested loops. Please do not use this; find a decent
+text-editor instead.
+
+The default value is \kbd{1}.
+
+\subsec{threadsize}\kbdsidx{threadsize}\label{se:def,threadsize}
+In parallel mode, each thread needs its own private \tev{stack} in which
+to do its computations, see \kbd{parisize}. This value determines the size
+in bytes of the stacks of each thread, so the total memory allocated will be
+$\kbd{parisize}+\kbd{nbthreads}\times\kbd{threadsize}$.
+
+If set to $0$, the value used is the same as \kbd{parisize}.
+
+The default value is $0$.
+
+\subsec{timer}\kbdsidx{timer}\label{se:def,timer}
+This toggle is either 1 (on) or 0 (off). Every instruction sequence
+in the gp calculator (anything ended by a newline in your input) is timed,
+to some accuracy depending on the hardware and operating system. When
+\tet{timer} is on, each such timing is printed immediately before the
+output as follows:
+\bprog
+? factor(2^2^7+1)
+time = 108 ms.     \\ this line omitted if 'timer' is 0
+%1 =
+[     59649589127497217 1]
+
+[5704689200685129054721 1]
+ at eprog\noindent (See also \kbd{\#} and \kbd{\#\#}.)
+
+The time measured is the user \idx{CPU time}, \emph{not} including the time
+for printing the results. If the time is negligible ($< 1$ ms.), nothing is
+printed: in particular, no timing should be printed when defining a user
+function or an alias, or installing a symbol from the library.
+
+The default value is \kbd{0} (off).
+%SECTION: default
+
+\vfill\eject
diff --git a/doc/usersch4.tex b/doc/usersch4.tex
new file mode 100644
index 0000000..907adb0
--- /dev/null
+++ b/doc/usersch4.tex
@@ -0,0 +1,2236 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Programming PARI in Library Mode}
+
+\noindent The \emph{User's Guide to Pari/GP} gives in three chapters a
+general presentation of the system, of the \kbd{gp} calculator, and detailed
+explanation of high level PARI routines available through the calculator. The
+present manual assumes general familiarity with the contents of these
+chapters and the basics of ANSI C programming, and focuses on the usage of
+the PARI library. In this chapter, we introduce the general concepts of PARI
+programming and describe useful general purpose functions; the following
+chapters describes all public low or high-level functions, underlying or
+extending the GP functions seen in Chapter 3 of the User's guide.
+
+\section{Introduction: initializations, universal objects}
+\label{se:intro4}
+
+\noindent
+To use PARI in \idx{library mode}, you must write a C program and link it to
+the PARI library. See the installation guide or the Appendix to the
+\emph{User's Guide to Pari/GP} on how to create and install the library and
+include files. A sample Makefile is presented in Appendix~A, and a more
+elaborate one in \kbd{examples/Makefile}. The best way to understand how
+programming is done is to work through a complete example. We will write such
+a program in~\secref{se:prog}. Before doing this, a few explanations are in
+order.
+
+First, one must explain to the outside world what kind of objects and
+routines we are going to use. This is done\footnote{*}{This assumes that PARI
+headers are installed in a directory which belongs to your compiler's search
+path for header files. You might need to add flags like
+\kbd{-I/usr/local/include} or modify \tet{C_INCLUDE_PATH}.}
+with the directive
+
+\bprog
+#include <pari/pari.h>
+ at eprog
+\noindent
+In particular, this defines the fundamental type for all PARI objects: the
+type \teb{GEN}, which is simply a pointer to \kbd{long}.
+
+Before any PARI routine is called, one must initialize the system, and in
+particular the PARI stack which is both a scratchboard and a repository for
+computed objects. This is done with a call to the function
+
+\fun{void}{pari_init}{size_t size, ulong maxprime}
+
+\noindent The first argument is the number of bytes given to PARI to work
+with, and the second is the upper limit on a precomputed prime number table;
+\kbd{size} should not reasonably be taken below $500000$ but you may set
+$\tet{maxprime} = 0$, although the system still needs to precompute all
+primes up to about $2^{16}$. For lower-level variants allowing finer
+control, e.g.~preventing PARI from installing its own error or signal
+handlers, see~\secref{se:pari_init_tech}.
+
+\noindent We have now at our disposal:
+
+\item a PARI \tev{stack} containing nothing. This is a big
+connected chunk of \kbd{size} bytes of memory, where all computations
+take place. In large computations, intermediate results quickly
+clutter up memory so some kind of garbage collecting is needed. Most
+systems do garbage collecting when the memory is getting scarce, and this
+slows down the performance. PARI takes a different approach, admittedly more
+demanding on the programmer: you must do your own cleaning up when the
+intermediate results are not needed anymore. We will see later how (and when)
+this is done.
+
+\item the following \emph{universal objects} (by definition, objects
+which do not belong to the stack): the integers $0$, $1$, $-1$, $2$ and
+$-2$ (respectively called \tet{gen_0}, \tet{gen_1}, \tet{gen_m1},
+\tet{gen_2} and \tet{gen_m2}), the fraction $\dfrac{1}{2}$ (\tet{ghalf}).
+All of these are of type \kbd{GEN}.
+
+\item a \tev{heap} which is just a linked list of permanent
+universal objects. For now, it contains exactly the ones listed above. You
+will probably very rarely use the heap yourself; and if so, only as a
+collection of copies of objects taken from the stack (called \idx{clone}s in
+the sequel). Thus you need not bother with its internal structure, which may
+change as PARI evolves. Some complex PARI functions create clones for special
+garbage collecting purposes, usually destroying them when returning.
+
+\item a table of primes (in fact of \emph{differences} between
+consecutive primes), called \teb{diffptr}, of type \kbd{byteptr}
+(pointer to \kbd{unsigned char}). Its use is described in
+\secref{se:primetable} later. Using it directly is deprecated,
+high-level iterators provide a cleaner and more flexible interface, see
+\secref{se:primeiter} (such iterators use the private prime table, but extend
+it dynamically).
+
+\item access to all the built-in functions of the PARI library.
+These are declared to the outside world when you include \kbd{pari.h}, but
+need the above things to function properly. So if you forget the call to
+\tet{pari_init}, you will get a fatal error when running your program.
+
+\section{Important technical notes}
+
+\subsec{Backward compatibility} The PARI function names evolved over time,
+and deprecated functions are eventually deleted.  The file \kbd{pariold.h}
+contains macros implementing a weak form of backward compatibility.
+In particular, whenever the name of a documented function changes, a
+\kbd{\#define} is added to this file so that the old name expands to the new
+one (provided the prototype didn't change also).
+
+This file is included by \kbd{pari.h}, but a large section is commented out
+by default. Define \tet{PARI_OLD_NAMES} before including \kbd{pari.h} to
+pollute your namespace with lots of obsolete names like
+\kbd{un}\footnote{*}{For \kbd{(long)gen\_1}. Since 2004 and version 2.2.9,
+typecasts are completely unnecessary in PARI programs.}: that might enable
+you to compile old programs without having to modify them. The preferred way
+to do that is to add \kbd{-DPARI\_OLD\_NAMES} to your compiler \kbd{CFLAGS},
+so that you don't need to modify the program files themselves.
+
+Of course, it's better to fix the program if you can!
+
+\subsec{Types}
+
+\noindent
+Although PARI objects all have the C type \kbd{GEN}, we will freely use
+the word \teb{type} to refer to PARI dynamic subtypes: \typ{INT}, \typ{REAL},
+etc. The declaration
+\bprog
+  GEN x;
+ at eprog\noindent
+declares a C variable of type \kbd{GEN}, but its ``value'' will be said to
+have type \typ{INT}, \typ{REAL}, etc. The meaning should always be clear from
+the context.
+
+\subsec{Type recursivity}
+
+\noindent
+Conceptually, most PARI types are recursive. But the \kbd{GEN} type is a
+pointer to \kbd{long}, not to \kbd{GEN}. So special macros must be used to
+access \kbd{GEN}'s components. The simplest one is \tet{gel}$(V, i)$, where
+\key{el} stands for \key{el}ement, to access component number $i$ of the
+\kbd{GEN} $V$. This is a valid \kbd{lvalue} (may be put on the left side of
+an assignment), and the following two constructions are exceedingly frequent
+%
+\bprog
+  gel(V, i) = x;
+  x = gel(V, i);
+ at eprog\noindent
+where \kbd{x} and \kbd{V} are \kbd{GEN}s. This macro accesses and modifies
+directly the components of $V$ and do not create a copy of the coefficient,
+contrary to all the library \emph{functions}.
+
+More generally, to retrieve the values of elements of lists of \dots\ of
+lists of vectors we have the \tet{gmael} macros (for {\bf m}ultidimensional
+{\bf a}rray {\bf el}ement). The syntax is $\key{gmael}n(V,a_1,\dots,a_n)$,
+where $V$ is a \kbd{GEN}, the $a_i$ are indexes, and $n$ is an integer
+between $1$ and $5$. This stands for $x[a_1][a_2]\dots[a_n]$, and returns a
+\kbd{GEN}. The macros \tet{gel} (resp.~\tet{gmael}) are synonyms for
+\tet{gmael1} (resp.~\kbd{gmael2}).
+
+Finally, the macro $\tet{gcoeff}(M, i, j)$ has exactly the meaning of
+\kbd{M[i,j]} in GP when \kbd{M} is a matrix. Note that due to the
+implementation of \typ{MAT}s as horizontal lists of vertical vectors,
+\kbd{gcoeff(x,y)} is actually equivalent to \kbd{gmael(y,x)}. One should use
+\kbd{gcoeff} in matrix context, and \kbd{gmael} otherwise.
+
+\subsec{Variations on basic functions}\label{se:low_level} In the library
+syntax descriptions in Chapter~3, we have only given the basic names of the
+functions. For example \kbd{gadd}$(x,y)$ assumes that $x$ and $y$ are
+\kbd{GEN}s, and \emph{creates} the result $x+y$ on the PARI stack. For most
+of the basic operators and functions, many other variants are available. We
+give some examples for \kbd{gadd}, but the same is true for all the basic
+operators, as well as for some simple common functions (a complete list
+is given in Chapter~6):
+
+\fun{GEN}{gaddgs}{GEN x, long y}
+
+\fun{GEN}{gaddsg}{long x, GEN y}
+
+\noindent In the following one, \kbd{z} is a preexisting \kbd{GEN} and the
+result of the corresponding operation is put into~\kbd{z}. The size of the PARI
+stack does not change:
+
+\fun{void}{gaddz}{GEN x, GEN y, GEN z}
+
+\noindent (This last form is inefficient in general and deprecated outside of
+PARI kernel programming.) Low level kernel functions implement these
+operators for specialized arguments and are also available: Level 0 deals
+with operations at the word level (\kbd{long}s and \kbd{ulong}s), Level 1
+with \typ{INT} and \typ{REAL} and Level 2 with the rest (modular arithmetic,
+polynomial arithmetic and linear algebra). Here are some examples of Level
+$1$ functions:
+
+\fun{GEN}{addii}{GEN x, GEN y}: here $x$ and $y$ are \kbd{GEN}s of type
+\typ{INT} (this is not checked).
+
+\fun{GEN}{addrr}{GEN x, GEN y}: here $x$ and $y$ are \kbd{GEN}s of
+type \typ{REAL} (this is not checked).
+
+\noindent
+There also exist functions \teb{addir}, \teb{addri}, \teb{mpadd} (whose
+two arguments can be of type \typ{INT} or \typ{REAL}), \teb{addis} (to add a
+\typ{INT} and a \kbd{long}) and so on.
+
+The Level $1$ names are self-explanatory once you know that {\bf i} stands for a
+\typ{INT}, {\bf r} for a \typ{REAL}, {\bf mp} for i or r, {\bf s} for a signed C
+long integer, {\bf u} for an unsigned C long integer; finally the suffix {\bf z}
+means that the result is not created on the PARI stack but assigned to a
+preexisting GEN object passed as an extra argument. Chapter 6 gives a
+description of these low-level functions.
+
+Level $2$ names are more complicated, see \secref{se:level2names} for all the
+gory details, and we content ourselves with a simple example used to implement
+\typ{INTMOD} arithmetic:
+
+\fun{GEN}{Fp_add}{GEN x, GEN y, GEN m}: returns the sum of $x$ and $y$ modulo
+$m$. Here $x, y, m$ are \typ{INT}s (this is not checked). The operation is
+more efficient if the inputs $x$, $y$ are reduced modulo $m$, but this is not
+a necessary condition.
+
+\misctitle{Important Note} These specialized functions are of course more
+efficient than the generic ones, but note the hidden danger here: the types
+of the objects involved (which is not checked) must be severely controlled,
+e.g.~using \kbd{addii} on a \typ{FRAC} argument will cause disasters. Type
+mismatches may corrupt the PARI stack, though in most cases they will just
+immediately overflow the stack. Because of this, the PARI philosophy of
+giving a result which is as exact as possible, enforced for generic functions
+like \kbd{gadd} or \kbd{gmul}, is dropped in kernel routines of Level $1$,
+where it is replaced by the much simpler rule: the result is a \typ{INT} if
+and only if all arguments are integer types (\typ{INT} but also C \kbd{long}
+and \kbd{ulong}) and a \typ{REAL} otherwise. For instance, multiplying a
+\typ{REAL} by a \typ{INT} always yields a \typ{REAL} if you use \kbd{mulir},
+where \kbd{gmul} returns the \typ{INT} \kbd{gen\_0} if the integer is $0$.
+
+\subsec{Portability: 32-bit / 64-bit architectures}
+
+\noindent
+PARI supports both 32-bit and 64-bit based machines, but not simultaneously!
+The library is compiled assuming a given architecture, and some
+of the header files you include (through \kbd{pari.h}) will have been
+modified to match the library.
+
+Portable macros are defined to bypass most machine dependencies. If you want
+your programs to run identically on 32-bit and 64-bit machines, you have to
+use these, and not the corresponding numeric values, whenever the precise
+size of your \kbd{long} integers might matter. Here are the most important
+ones:
+
+\settabs\+ -----------------------------&---------------&------------&\cr \+
+& 64-bit  & 32-bit \cr\+ \tet{BITS_IN_LONG}  & 64      & 32 \cr\+
+\tet{LONG_IS_64BIT} & defined & undefined \cr\+
+\tet{DEFAULTPREC}   & 3       & 4 & ($\approx$ 19 decimal digits, %
+see formula below) \cr\+
+\tet{MEDDEFAULTPREC}& 4       & 6 & ($\approx$ 38 decimal digits) \cr\+
+\tet{BIGDEFAULTPREC}& 5       & 8 & ($\approx$ 57 decimal digits) \cr
+\noindent For instance, suppose you call a transcendental function, such as
+
+\kbd{GEN \key{gexp}(GEN x, long prec)}.
+
+\noindent The last argument \kbd{prec} is an integer $\geq 3$, corresponding
+to the default floating point precision required. It is \emph{only} used if
+\kbd{x} is an exact object, otherwise the relative precision is determined by
+the precision of~\kbd{x}. Since the parameter \kbd{prec} sets the size of the
+inexact result counted in (\kbd{long}) \emph{words} (including codewords),
+the same value of \kbd{prec} will yield different results on 32-bit and
+64-bit machines. Real numbers have two codewords (see~\secref{se:impl}), so
+the formula for computing the bit accuracy is
+$$ \tet{bit_accuracy}(\kbd{prec}) = (\kbd{prec} - 2) * \tet{BITS_IN_LONG}$$
+(this is actually the definition of an inline function). The corresponding
+accuracy expressed in decimal digits would be
+%
+$$ \kbd{bit\_accuracy(prec)} * \log(2) / \log(10).$$
+%
+For example if the value of \kbd{prec} is 5, the corresponding accuracy for
+32-bit machines is $(5-2)*\log(2^{32})/\log(10)\approx 28$ decimal digits,
+while for 64-bit machines it is $(5-2)*\log(2^{64})/\log(10)\approx 57$
+decimal digits.
+
+Thus, you must take care to change the \kbd{prec} parameter you are supplying
+according to the bit size, either using the default precisions given by the
+various \kbd{DEFAULTPREC}s, or by using conditional constructs of the form:
+%
+\bprog
+#ifndef LONG_IS_64BIT
+  prec = 4;
+#else
+  prec = 6;
+#endif
+ at eprog
+\noindent which is in this case equivalent to the statement
+\kbd{prec = MEDDEFAULTPREC;}.
+
+Note that for parity reasons, half the accuracies available on 32-bit
+architectures (the odd ones) have no precise equivalents on 64-bit machines.
+
+\subsec{Using \kbd{malloc} / \kbd{free}}
+You should make use of the PARI stack as much as possible, and avoid
+allocating objects using the customary functions. If you do, you should
+use, or at least have a very close look at, the following wrappers:
+
+\fun{void*}{pari_malloc}{size_t size} calls \kbd{malloc} to allocate
+\kbd{size} bytes and returns a pointer to the allocated memory. If the
+request fails, an error is raised. The \kbd{SIGINT} signal is blocked until
+\kbd{malloc} returns, to avoid leaving the system stack in an inconsistent
+state.
+
+\fun{void*}{pari_realloc}{void* ptr, size_t size} as \tet{pari_malloc} but
+calls \kbd{realloc} instead of \kbd{malloc}.
+
+\fun{void*}{pari_calloc}{size_t size} as \tet{pari_malloc}, setting the
+memory to zero.
+
+\fun{void}{pari_free}{void* ptr} calls \kbd{free} to liberate the memory
+space pointed to by \kbd{ptr}, which must have been allocated by \kbd{malloc}
+(\tet{pari_malloc}) or \kbd{realloc} (\tet{pari_realloc}). The \kbd{SIGINT}
+signal is blocked until \kbd{free} returns.
+
+If you use the standard \kbd{libc} functions instead of our wrappers, then
+your functions will be subtly incompatible with the \kbd{gp} calculator: when
+the user tries to interrupt a computation, the calculator may crash
+(if a system call is interrupted at the wrong time).
+
+\section{Garbage collection}\label{se:garbage}\sidx{garbage collecting}
+
+\subsec{Why and how}
+
+\noindent
+As we have seen, \kbd{pari\_init} allocates a big range of
+addresses, the \tev{stack}, that are going to be used throughout. Recall
+that all PARI objects are pointers. Except for a few universal objects,
+they all point at some part of the stack.
+
+The stack starts at the address \kbd{bot} and ends just before \kbd{top}. This
+means that the quantity
+%
+$$ (\kbd{top} - \kbd{bot})\,/\,\kbd{sizeof(long)} $$
+%
+is (roughly) equal to the \kbd{size} argument of \kbd{pari\_init}. The PARI
+stack also has a ``current stack pointer'' called \teb{avma}, which stands
+for {\bf av}ailable {\bf m}emory {\bf a}ddress. These three variables are
+global (declared by \kbd{pari.h}). They are of type \tet{pari_sp}, which
+means \emph{pari stack pointer}.
+
+The stack is oriented upside-down: the more recent an object, the closer to
+\kbd{bot}. Accordingly, initially \kbd{avma} = \kbd{top}, and \kbd{avma} gets
+\emph{decremented} as new objects are created. As its name indicates,
+\kbd{avma} always points just \emph{after} the first free address on the
+stack, and \kbd{(GEN)avma} is always (a pointer to) the latest created object.
+When \kbd{avma} reaches \kbd{bot}, the stack overflows, aborting all
+computations, and an error message is issued. To avoid this \emph{you}
+need to clean up the stack from time to time, when intermediate objects are
+not needed anymore. This is called ``\emph{garbage collecting}.''
+
+We are now going to describe briefly how this is done. We will see many
+concrete examples in the next subsection.
+
+\noindent\item
+First, PARI routines do their own garbage collecting, which means that
+whenever a documented function from the library returns, only its result(s)
+have been added to the stack, possibly up to a very small overhead
+(non-documented ones may not do this). In
+particular, a PARI function that does not return a \kbd{GEN} does not clutter
+the stack. Thus, if your computation is small enough (e.g.~you call few PARI
+routines, or most of them return \kbd{long} integers), then you do not need
+to do any garbage collecting. This is probably the case in many of your
+subroutines. Of course the objects that were on the stack \emph{before} the
+function call are left alone. Except for the ones listed below, PARI
+functions only collect their own garbage.
+
+\noindent\item
+It may happen that all objects that were created after a certain point can
+be deleted~--- for instance, if the final result you need is not a
+\kbd{GEN}, or if some search proved futile. Then, it is enough to record
+the value of \kbd{avma} just \emph{before} the first garbage is created,
+and restore it upon exit:
+
+\bprog
+pari_sp av = avma; /*@Ccom record initial avma */
+
+garbage ...
+avma = av; /*@Ccom restore it */
+ at eprog
+\noindent All objects created in the \kbd{garbage} zone will eventually
+be overwritten: they should no longer be accessed after \kbd{avma} has been
+restored.
+
+\noindent\item
+If you want to destroy (i.e.~give back the memory occupied by) the
+\emph{latest} PARI object on the stack (e.g.~the latest one obtained from a
+function call), you can use the function\sidx{destruction}%
+\vadjust{\penalty500}%discourage page break
+
+\fun{void}{cgiv}{GEN z}
+
+\noindent where \kbd{z} is the object you want to give back. This is
+equivalent to the above where the initial \kbd{av} is computed from \kbd{z}.
+
+\noindent\item
+Unfortunately life is not so simple, and sometimes you will want
+to give back accumulated garbage \emph{during} a computation without losing
+recent data. We shall start with the lowest level function to get a feel for
+the underlying mechanisms, we shall describe simpler variants later:
+
+\fun{GEN}{gerepile}{pari_sp ltop, pari_sp lbot, GEN q}. This function cleans
+up the stack between \kbd{ltop} and \kbd{lbot}, where $\kbd{lbot} <
+\kbd{ltop}$, and returns the updated object \kbd{q}. This means:
+
+1) we translate (copy) all the objects in the interval
+$[\kbd{avma}, \kbd{lbot}[$, so that its right extremity abuts the address
+\kbd{ltop}. Graphically
+
+\vbox{\bprog
+             bot           avma   lbot          ltop     top
+End of stack  |-------------[++++++[-/-/-/-/-/-/-|++++++++|  Start
+                free memory            garbage
+ at eprog
+\noindent becomes:
+\bprog
+             bot                         avma   ltop     top
+End of stack  |---------------------------[++++++[++++++++|  Start
+                       free memory
+ at eprog
+}
+\noindent where \kbd{++} denote significant objects, \kbd{--} the unused part
+of the stack, and \kbd{-/-} the garbage we remove.
+
+2) The function then inspects all the PARI objects between \kbd{avma} and
+\kbd{lbot} (i.e.~the ones that we want to keep and that have been translated)
+and looks at every component of such an object which is not a codeword. Each
+such component is a pointer to an object whose address is either
+
+--- between \kbd{avma} and \kbd{lbot}, in which case it is suitably updated,
+
+--- larger than or equal to \kbd{ltop}, in which case it does not change, or
+
+--- between \kbd{lbot} and \kbd{ltop} in which case \kbd{gerepile}
+raises an error (``significant pointers lost in gerepile'').
+
+3) \key{avma} is updated (we add $\kbd{ltop} - \kbd{lbot}$ to the old value).
+
+4) We return the (possibly updated) object \kbd{q}: if \kbd{q} initially
+pointed between \kbd{avma} and \kbd{lbot}, we return the updated address, as
+in~2). If not, the original address is still valid, and is returned!
+
+As stated above, no component of the remaining objects (in particular
+\kbd{q}) should belong to the erased segment [\kbd{lbot}, \kbd{ltop}[, and
+this is checked within \kbd{gerepile}. But beware as well that the addresses
+of the objects in the translated zone change after a call to \kbd{gerepile},
+so you must not access any pointer which previously pointed into the zone
+below \kbd{ltop}. If you need to recover more than one object, use the
+\kbd{gerepileall} function below.
+
+\misctitle{Remark}
+As a consequence of the preceding explanation, if a PARI object is to be
+relocated by \hbox{gerepile} then, apart from universal objects, the chunks
+of memory used by its components should be in consecutive memory locations.
+All \kbd{GEN}s created by documented PARI functions are guaranteed to satisfy
+this. This is because the \kbd{gerepile} function knows only about \emph{two
+connected zones}: the garbage that is erased (between \kbd{lbot} and
+\kbd{ltop}) and the significant pointers that are copied and updated. If
+there is garbage interspersed with your objects, disaster occurs when we try
+to update them and consider the corresponding ``pointers''. In most cases of
+course the said garbage is in fact a bunch of other \kbd{GEN}s, in which case
+we simply waste time copying and updating them for nothing. But be wary when
+you allow objects to become disconnected.
+
+\noindent In practice this is achieved by the following programming idiom:
+\bprog
+  ltop = avma; garbage(); lbot = avma; q = anything();
+  return gerepile(ltop, lbot, q); /*@Ccom returns the updated q */
+ at eprog\noindent or directly
+\bprog
+  ltop = avma; garbage(); lbot = avma;
+  return gerepile(ltop, lbot, anything());
+ at eprog\noindent
+Beware that
+\bprog
+  ltop = avma; garbage();
+  return gerepile(ltop, avma, anything())
+ at eprog
+
+\noindent might work, but should be frowned upon. We cannot predict whether
+\kbd{avma} is evaluated after or before the call to \kbd{anything()}: it
+depends on the compiler. If we are out of luck, it is \emph{after} the
+call, so the result belongs to the garbage zone and the \kbd{gerepile}
+statement becomes equivalent to \kbd{avma = ltop}. Thus we return a
+pointer to random garbage.
+
+\subsec{Variants}
+
+\fun{GEN}{gerepileupto}{pari_sp ltop, GEN q}. Cleans the stack between
+\kbd{ltop} and the \emph{connected} object \kbd{q} and returns \kbd{q}
+updated. For this to work, \kbd{q} must have been created \emph{before} all
+its components, otherwise they would belong to the garbage zone! Unless
+mentioned otherwise, documented PARI functions guarantee this.
+
+\fun{GEN}{gerepilecopy}{pari_sp ltop, GEN x}. Functionally equivalent to,
+but more efficient than
+\bprog
+  gerepileupto(ltop, gcopy(x))
+ at eprog\noindent In this case, the \kbd{GEN} parameter \kbd{x} need not
+satisfy any property before the garbage collection: it may be disconnected,
+components created before the root, and so on. Of course, this is about
+twice slower than either \tet{gerepileupto} or \tet{gerepile}, because
+\kbd{x} has to be copied to a clean stack zone first. This function is a
+special case of \tet{gerepileall} below, where $n=1$.
+
+\fun{void}{gerepileall}{pari_sp ltop, int n, ...}.
+To cope with complicated cases where many objects have to be preserved. The
+routine expects $n$ further arguments, which are the \emph{addresses} of
+the \kbd{GEN}s you want to preserve:
+\bprog
+  pari_sp ltop = avma;
+  ...; y = ...; ... x = ...; ...;
+  gerepileall(ltop, 2, &x, &y);
+ at eprog\noindent
+It cleans up the most recent part of the
+stack (between \kbd{ltop} and \kbd{avma}), updating all the \kbd{GEN}s added
+to the argument list. A copy is done just before the cleaning to preserve
+them, so they do not need to be connected before the call. With
+\kbd{gerepilecopy}, this is the most robust of the \kbd{gerepile} functions
+(the less prone to user error), hence the slowest.
+
+\fun{void}{gerepileallsp}{pari_sp ltop, pari_sp lbot, int n, ...}.
+More efficient, but trickier than \kbd{gerepileall}. Cleans the stack between
+\kbd{lbot} and \kbd{ltop} and updates the \kbd{GEN}s pointed at by the
+elements of \kbd{gptr} without any further copying. This is subject to the
+same restrictions as \kbd{gerepile}, the only difference being that more than
+one address gets updated.
+
+\subsec{Examples}
+
+\subsubsec{gerepile}
+
+Let \kbd{x} and \kbd{y} be two preexisting PARI objects and suppose that we
+want to compute $\kbd{x}^2 + \kbd{y}^2$. This is done using the following
+program:
+\bprog
+  GEN x2 = gsqr(x);
+  GEN y2 = gsqr(y), z = gadd(x2,y2);
+ at eprog\noindent
+The \kbd{GEN} \kbd{z} indeed points at the desired quantity. However,
+consider the stack: it contains as unnecessary garbage \kbd{x2} and \kbd{y2}.
+More precisely it contains (in this order) \kbd{z}, \kbd{y2}, \kbd{x2}.
+(Recall that, since the stack grows downward from the top, the most recent
+object comes first.)
+
+It is not possible to get rid of \kbd{x2}, \kbd{y2} before \kbd{z} is
+computed, since they are used in the final operation. We cannot record
+\kbd{avma} before \kbd{x2} is computed and restore it later, since this would
+destroy \kbd{z} as well. It is not possible either to use the function
+\kbd{cgiv} since \kbd{x2} and \kbd{y2} are not at the bottom of the stack and
+we do not want to give back~\kbd{z}.
+
+But using \kbd{gerepile}, we can give back the memory locations corresponding
+to \kbd{x2}, \kbd{y2}, and move the object \kbd{z} upwards so that no
+space is lost. Specifically:
+\bprog
+  pari_sp ltop = avma;  /*@Ccom remember the current top of the stack */
+  GEN x2 = gsqr(x);
+  GEN y2 = gsqr(y);
+  pari_sp lbot = avma;  /*@Ccom the bottom of the garbage pile */
+  GEN z = gadd(x2, y2); /*@Ccom z is now the last object on the stack */
+  z = gerepile(ltop, lbot, z);
+ at eprog
+\noindent Of course, the last two instructions could also have been
+written more simply:
+\bprog
+  z = gerepile(ltop, lbot, gadd(x2,y2));
+ at eprog\noindent In fact \kbd{gerepileupto} is even simpler to use, because
+the result of \kbd{gadd} is the last object on the stack and \kbd{gadd}
+is guaranteed to return an object suitable for \kbd{gerepileupto}:
+\bprog
+  ltop = avma;
+  z = gerepileupto(ltop, gadd(gsqr(x), gsqr(y)));
+ at eprog\noindent
+Make sure you understand exactly what has happened before you go on!
+
+\misctitle{Remark on assignments and gerepile} When the tree structure and
+the size of the PARI objects which will appear in a computation are under
+control, one may allocate sufficiently large objects at the beginning,
+use assignment statements, then simply restore \kbd{avma}. Coming back to the
+above example, note that \emph{if} we know that x and y are of type real
+fitting into \kbd{DEFAULTPREC} words, we can program without using
+\kbd{gerepile} at all:
+\bprog
+  z = cgetr(DEFAULTPREC); ltop = avma;
+  gaffect(gadd(gsqr(x), gsqr(y)), z);
+  avma = ltop;
+ at eprog\noindent This is often \emph{slower} than a craftily used
+\kbd{gerepile} though, and certainly more cumbersome to use. As a rule,
+assignment statements should generally be avoided.
+
+\misctitle{Variations on a theme} it is often necessary to do several
+\kbd{gerepile}s during a computation. However, the fewer the better. The only
+condition for \kbd{gerepile} to work is that the garbage be connected. If the
+computation can be arranged so that there is a minimal number of connected
+pieces of garbage, then it should be done that way.
+
+For example suppose we want to write a function of two \kbd{GEN} variables
+\kbd{x} and \kbd{y} which creates the vector $\kbd{[x}^2+\kbd{y},
+\kbd{y}^2+\kbd{x]}$. Without garbage collecting, one would write:
+%
+\bprog
+  p1 = gsqr(x); p2 = gadd(p1, y);
+  p3 = gsqr(y); p4 = gadd(p3, x);
+  z = mkvec2(p2, p4);  /* not suitable for gerepileupto! */
+ at eprog\noindent
+This leaves a dirty stack containing (in this order) \kbd{z}, \kbd{p4},
+\kbd{p3}, \kbd{p2}, \kbd{p1}. The garbage here consists of \kbd{p1} and
+\kbd{p3}, which are separated by \kbd{p2}. But if we compute \kbd{p3}
+\emph{before} \kbd{p2} then the garbage becomes connected, and we get the
+following program with garbage collecting:
+%
+\bprog
+  ltop = avma; p1 = gsqr(x); p3 = gsqr(y);
+  lbot = avma; z = cgetg(3, t_VEC);
+  gel(z, 1) = gadd(p1,y);
+  gel(z, 2) = gadd(p3,x); z = gerepile(ltop,lbot,z);
+ at eprog\noindent Finishing by \kbd{z = gerepileupto(ltop, z)} would be ok as
+well. Beware that
+\bprog
+  ltop = avma; p1 = gadd(gsqr(x), y); p3 = gadd(gsqr(y), x);
+  z = cgetg(3, t_VEC);
+  gel(z, 1) = p1;
+  gel(z, 2) = p3; z = gerepileupto(ltop,z); /*@Ccom WRONG */
+ at eprog\noindent
+is a disaster since \kbd{p1} and \kbd{p3} are created before
+\kbd{z}, so the call to \kbd{gerepileupto} overwrites them, leaving
+\kbd{gel(z, 1)} and \kbd{gel(z, 2)} pointing at random data! The following
+does work:
+\bprog
+  ltop = avma; p1 = gsqr(x); p3 = gsqr(y);
+  lbot = avma; z = mkvec2(gadd(p1,y), gadd(p3,x));
+  z = gerepile(ltop,lbot,z);
+ at eprog\noindent but is very subtly wrong in the sense that
+\kbd{z = gerepileupto(ltop, z)} would \emph{not} work. The reason being
+that \kbd{mkvec2} creates the root \kbd{z} of the vector \emph{after}
+its arguments have been evaluated, creating the components of \kbd{z}
+too early; \kbd{gerepile} does not care, but the created \kbd{z} is a time
+bomb which will explode on any later \kbd{gerepileupto}.
+On the other hand
+\bprog
+  ltop = avma; z = cgetg(3, t_VEC);
+  gel(z, 1) = gadd(gsqr(x), y);
+  gel(z, 2) = gadd(gsqr(y), x); z = gerepileupto(ltop,z); /*@Ccom INEFFICIENT */
+ at eprog\noindent
+leaves the results of \kbd{gsqr(x)} and \kbd{gsqr(y)} on the stack (and
+lets \kbd{gerepileupto} update them for naught). Finally, the most elegant
+and efficient version (with respect to time and memory use) is as follows
+\bprog
+  z = cgetg(3, t_VEC);
+  ltop = avma; gel(z, 1) = gerepileupto(ltop, gadd(gsqr(x), y));
+  ltop = avma; gel(z, 2) = gerepileupto(ltop, gadd(gsqr(y), x));
+ at eprog\noindent
+which avoids updating the container \kbd{z} and cleans up its components
+individually, as soon as they are computed.
+
+\misctitle{One last example} Let us compute the product of two complex
+numbers $x$ and $y$, using the $3M$ method which requires 3 multiplications
+instead of the obvious 4. Let $z = x*y$, and set $x = x_r + i*x_i$ and
+similarly for $y$ and $z$. We compute $p_1 = x_r*y_r$, $p_2=x_i*y_i$,
+$p_3=(x_r+x_i)*(y_r+y_i)$, and then we have $z_r=p_1-p_2$,
+$z_i=p_3-(p_1+p_2)$. The program is as follows:
+%
+\bprog
+ltop = avma;
+p1 = gmul(gel(x,1), gel(y,1));
+p2 = gmul(gel(x,2), gel(y,2));
+p3 = gmul(gadd(gel(x,1), gel(x,2)), gadd(gel(y,1), gel(y,2)));
+p4 = gadd(p1,p2);
+lbot = avma; z = cgetg(3, t_COMPLEX);
+gel(z, 1) = gsub(p1,p2);
+gel(z, 2) = gsub(p3,p4); z = gerepile(ltop,lbot,z);
+ at eprog
+
+\misctitle{Exercise} Write a function which multiplies a matrix by a column
+vector. Hint: start with a \kbd{cgetg} of the result, and use \kbd{gerepile}
+whenever a coefficient of the result vector is computed. You can look at the
+answer in \kbd{src/basemath/RgV.c:RgM\_RgC\_mul()}.
+
+\subsubsec{gerepileall}
+
+Let us now see why we may need the \kbd{gerepileall} variants. Although it
+is not an infrequent occurrence, we do not give a specific example but a
+general one: suppose that we want to do a computation (usually inside a
+larger function) producing more than one PARI object as a result, say two for
+instance. Then even if we set up the work properly, before cleaning up we
+have a stack which has the desired results \kbd{z1}, \kbd{z2} (say), and
+then connected garbage from lbot to ltop. If we write
+\bprog
+  z1 = gerepile(ltop, lbot, z1);
+ at eprog\noindent
+then the stack is cleaned, the pointers fixed up, but we have lost the
+address of \kbd{z2}. This is where we need the \idx{gerepileall}
+function:
+\bprog
+  gerepileall(ltop, 2, &z1, &z2)
+ at eprog
+\noindent copies \kbd{z1} and \kbd{z2} to new locations, cleans the stack
+from \kbd{ltop} to the old \kbd{avma}, and updates the pointers \kbd{z1} and
+\kbd{z2}. Here we do not assume anything about the stack: the garbage can be
+disconnected and \kbd{z1}, \kbd{z2} need not be at the bottom of the stack.
+If all of these assumptions are in fact satisfied, then we can call
+\kbd{gerepilemanysp} instead, which is usually faster since we do not need
+the initial copy (on the other hand, it is less cache friendly).
+
+A most important usage is ``random'' garbage collection during loops
+whose size requirements we cannot (or do not bother to) control in advance:
+\bprog
+  pari_sp ltop = avma, limit = stack_lim(avma, 1);
+  GEN x, y;
+  while (...)
+  {
+    garbage(); x = anything();
+    garbage(); y = anything(); garbage();
+    if (avma < limit) /*@Ccom memory is running low (half spent since entry) */
+      gerepileall(ltop, 2, &x, &y);
+  }
+ at eprog
+\noindent Here we assume that only \kbd{x} and \kbd{y} are needed from one
+iteration to the next. As it would be costly to call gerepile once for each
+iteration, we only do it when it seems to have become necessary. The macro
+\tet{stack_lim}\kbd{(avma,$n$)} denotes an address where $2^{n-1} /
+(2^{n-1}+1)$ of the remaining stack space is exhausted ($1/2$ for $n=1$,
+$2/3$ for $n=2$).
+
+\subsec{Comments}
+
+First, \kbd{gerepile} has turned out to be a flexible and fast garbage
+collector for number-theoretic computations, which compares favorably with
+more sophisticated methods used in other systems. Our benchmarks indicate
+that the price paid for using \kbd{gerepile} and \kbd{gerepile}-related
+copies, when properly used, is usually less than 1\% of the total
+running time, which is quite acceptable!
+
+Second, it is of course harder on the programmer, and quite error-prone
+if you do not stick to a consistent PARI programming style. If all seems
+lost, just use \tet{gerepilecopy} (or \tet{gerepileall}) to fix up the stack
+for you. You can always optimize later when you have sorted out exactly which
+routines are crucial and what objects need to be preserved and their usual
+sizes.
+
+\smallskip If you followed us this far, congratulations, and rejoice: the
+rest is much easier.
+
+\section{Creation of PARI objects, assignments, conversions}
+
+\subsec{Creation of PARI objects}\sidx{creation}
+The basic function which creates a PARI object is
+
+\fun{GEN}{cgetg}{long l, long t}
+$l$ specifies the number of longwords to be allocated to the
+object, and $t$ is the type of the object, in symbolic
+form (see \secref{se:impl} for the list of these). The precise effect of
+this function is as follows: it first creates on the PARI \emph{stack} a
+chunk of memory of size \kbd{length} longwords, and saves the address of the
+chunk which it will in the end return. If the stack has been used up, a
+message to the effect that ``the PARI stack overflows'' is printed,
+and an error raised. Otherwise, it sets the type and length of the PARI object.
+In effect, it fills its first codeword (\kbd{z[0]}). Many PARI
+objects also have a second codeword (types \typ{INT}, \typ{REAL},
+\typ{PADIC}, \typ{POL}, and \typ{SER}). In case you want to produce one of
+those from scratch, which should be exceedingly rare, \emph{it is your
+responsibility to fill this second codeword}, either explicitly (using the
+macros described in \secref{se:impl}), or implicitly using an assignment
+statement (using \kbd{gaffect}).
+
+Note that the length argument $l$ is predetermined for a number of types:
+3 for types \typ{INTMOD}, \typ{FRAC}, \typ{COMPLEX}, \typ{POLMOD},
+\typ{RFRAC}, 4 for type \typ{QUAD} and \typ{QFI}, and 5 for type \typ{PADIC}
+and \typ{QFR}. However for the sake of efficiency,
+\kbd{cgetg} does not check this: disasters will occur if you give an incorrect
+length for those types.
+
+\misctitle{Notes} 1)  The main use of this function is create efficiently
+a constant object, or to prepare for later assignments (see
+\secref{se:assign}). Most of the time you will use \kbd{GEN} objects as they
+are created and returned by PARI functions. In this case you do not need to
+use \kbd{cgetg} to create space to hold them.
+
+\noindent 2) For the creation of leaves, i.e.~\typ{INT} or \typ{REAL},
+
+\fun{GEN}{cgeti}{long length}
+
+\fun{GEN}{cgetr}{long length}
+
+\noindent should be used instead of \kbd{cgetg(length, t\_INT)} and
+\kbd{cgetg(length, t\_REAL)} respectively. Finally
+
+\fun{GEN}{cgetc}{long prec}
+
+\noindent creates a \typ{COMPLEX} whose real and imaginary part are
+\typ{REAL}s allocated by \kbd{cgetr(prec)}.
+
+\misctitle{Examples} 1) Both \kbd{z = cgeti(DEFAULTPREC)} and
+\kbd{cgetg(DEFAULTPREC, t\_INT)} create a \typ{INT} whose ``precision'' is
+\kbd{bit\_accuracy(DEFAULTPREC)} = 64. This means \kbd{z} can hold rational
+integers of absolute value less than $2^{64}$. Note that in both cases, the
+second codeword is \emph{not} filled. Of course we could use numerical
+values, e.g.~\kbd{cgeti(4)}, but this would have different meanings on
+different machines as \kbd{bit\_accuracy(4)} equals 64 on 32-bit machines,
+but 128 on 64-bit machines.
+
+\noindent 2) The following creates a \emph{complex number} whose real and
+imaginary parts can hold real numbers of precision
+$\kbd{bit\_accuracy(MEDDEFAULTPREC)} = 96\hbox{ bits:}$
+%
+\bprog
+  z = cgetg(3, t_COMPLEX);
+  gel(z, 1) = cgetr(MEDDEFAULTPREC);
+  gel(z, 2) = cgetr(MEDDEFAULTPREC);
+ at eprog\noindent
+or simply \kbd{z = cgetc(MEDDEFAULTPREC)}.
+
+\noindent 3) To create a matrix object for $4\times 3$ matrices:
+%
+\bprog
+  z = cgetg(4, t_MAT);
+  for(i=1; i<4; i++) gel(z, i) = cgetg(5, t_COL);
+ at eprog\noindent
+or simply \kbd{z = zeromatcopy(4, 3)}, which further initializes all entries
+to \kbd{gen\_0}.
+
+These last two examples illustrate the fact that since PARI types are
+recursive, all the branches of the tree must be created. The function
+\teb{cgetg} creates only the ``root'', and other calls to \kbd{cgetg} must be
+made to produce the whole tree. For matrices, a common mistake is to think
+that \kbd{z = cgetg(4, t\_MAT)} (for example) creates the root of the
+matrix: one needs also to create the column vectors of the matrix (obviously,
+since we specified only one dimension in the first \kbd{cgetg}!). This is
+because a matrix is really just a row vector of column vectors (hence a
+priori not a basic type), but it has been given a special type number so that
+operations with matrices become possible.
+
+Finally, to facilitate input of constant objects when speed is not paramount,
+there are four \tet{varargs} functions:
+
+\fun{GEN}{mkintn}{long n, ...}
+returns the non-negative \typ{INT} whose development in base $2^{32}$
+is given by the following $n$ words (\kbd{unsigned long}). It is assumed that
+all such arguments are less than $2^{32}$ (the actual \kbd{sizeof(long)} is
+irrelevant, the behavior is also as above on $64$-bit machines).
+\bprog
+  mkintn(3, a2, a1, a0);
+ at eprog
+\noindent returns $a_2 2^{64} + a_1 2^{32} + a_0$.
+
+\fun{GEN}{mkpoln}{long n, ...}
+Returns the \typ{POL} whose $n$ coefficients (\kbd{GEN}) follow, in order of
+decreasing degree.
+\bprog
+  mkpoln(3, gen_1, gen_2, gen_0);
+ at eprog
+\noindent returns the polynomial $X^2 + 2X$ (in variable $0$, use
+\tet{setvarn} if you want other variable numbers). Beware that $n$ is the
+number of coefficients, hence \emph{one more} than the degree.
+
+\fun{GEN}{mkvecn}{long n, ...}
+returns the \typ{VEC} whose $n$ coefficients (\kbd{GEN}) follow.
+
+\fun{GEN}{mkcoln}{long n, ...}
+returns the \typ{COL} whose $n$ coefficients (\kbd{GEN}) follow.
+
+\misctitle{Warning} Contrary to the policy of general PARI functions, the
+latter three functions do \emph{not} copy their arguments, nor do they produce
+an object a priori suitable for \tet{gerepileupto}. For instance
+\bprog
+  /*@Ccom gerepile-safe: components are universal objects */
+  z = mkvecn(3, gen_1, gen_0, gen_2);
+
+  /*@Ccom not OK for gerepileupto: stoi(3) creates component before root */
+  z = mkvecn(3, stoi(3), gen_0, gen_2);
+
+  /*@Ccom NO! First vector component \kbd{x} is destroyed */
+  x = gclone(gen_1);
+  z = mkvecn(3, x, gen_0, gen_2);
+  gunclone(x);
+ at eprog
+
+\noindent The following function is also available as a special case of
+\tet{mkintn}:
+
+\fun{GEN}{uu32toi}{ulong a, ulong b}
+
+Returns the \kbd{GEN} equal to $2^{32} a + b$, \emph{assuming} that
+$a,b < 2^{32}$. This does not depend on \kbd{sizeof(long)}: the behavior is
+as above on both $32$ and $64$-bit machines.
+
+\subsec{Sizes}
+
+\fun{long}{gsizeword}{GEN x} returns the total number of \B-bit words occupied
+by the tree representing~\kbd{x}.
+
+\fun{long}{gsizebyte}{GEN x} returns the total number of bytes occupied
+by the tree representing~\kbd{x}, i.e.~\kbd{gsizeword(x)} multiplied by
+\kbd{sizeof(long)}. This is normally useless since PARI functions use
+a number of \emph{words} as input for lengths and precisions.
+
+\subsec{Assignments}
+Firstly, if \kbd{x} and \kbd{y} are both declared as \kbd{GEN} (i.e.~pointers
+to something), the ordinary C assignment \kbd{y = x} makes perfect sense: we
+are just moving a pointer around. However, physically modifying either
+\kbd{x} or \kbd{y} (for instance, \kbd{x[1] = 0}) also changes the other
+one, which is usually not desirable. \label{se:assign}
+
+\misctitle{Very important note} Using the functions described in this
+paragraph is inefficient and often awkward: one of the \tet{gerepile}
+functions (see~\secref{se:garbage}) should be preferred. See the paragraph
+end for one exception to this rule.
+
+\noindent
+The general PARI \idx{assignment} function is the function \teb{gaffect} with
+the following syntax:
+
+\fun{void}{gaffect}{GEN x, GEN y}
+
+\noindent
+Its effect is to assign the PARI object \kbd{x} into the \emph{preexisting}
+object \kbd{y}. Both \kbd{x} and \kbd{y} must be \emph{scalar} types. For
+convenience, vector or matrices of scalar types are also allowed.
+
+This copies the whole structure of \kbd{x} into \kbd{y} so many conditions
+must be met for the assignment to be possible. For instance it is allowed to
+assign a \typ{INT} into a \typ{REAL}, but the converse is forbidden. For
+that, you must use the truncation or rounding function of your choice,
+e.g.\kbd{mpfloor}.
+
+It can also happen that \kbd{y} is not large enough or does not have the proper
+tree structure to receive the object \kbd{x}. For instance, let \kbd{y} the zero
+integer with length equal to 2; then \kbd{y} is too small to accommodate any
+non-zero \typ{INT}. In general common sense tells you what is possible,
+keeping in mind the PARI philosophy which says that if it makes sense it is
+valid. For instance, the assignment of an imprecise object into a precise one
+does \emph{not} make sense. However, a change in precision of imprecise
+objects is allowed, even if it \emph{increases} its accuracy: we complement
+the ``mantissa'' with infinitely many $0$ digits in this case. (Mantissa
+between quotes, because this is not restricted to \typ{REAL}s, it also
+applies for $p$-adics for instance.)
+
+All functions ending in ``\kbd{z}'' such as \teb{gaddz}
+(see~\secref{se:low_level}) implicitly use this function. In fact what they
+exactly do is record {\teb{avma}} (see~\secref{se:garbage}), perform the
+required operation, \teb{gaffect} the result to the last operand, then
+restore the initial \kbd{avma}.
+
+You can assign ordinary C long integers into a PARI object (not necessarily
+of type \typ{INT}) using
+
+\fun{void}{gaffsg}{long s, GEN y}
+
+\misctitle{Note} Due to the requirements mentioned above, it is usually
+a bad idea to use \tet{gaffect} statements. There is one exception: for simple
+objects (e.g.~leaves) whose size is controlled, they can be easier to use than
+\kbd{gerepile}, and about as efficient.
+
+\misctitle{Coercion} It is often useful to coerce an inexact object to a
+given precision. For instance at the beginning of a routine where precision
+can be kept to a minimum; otherwise the precision of the input is used in all
+subsequent computations, which is inefficient if the latter is known to
+thousands of digits. One may use the \kbd{gaffect} function for this, but it
+is easier and more efficient to call
+
+\fun{GEN}{gtofp}{GEN x, long prec} converts the complex number~\kbd{x}
+(\typ{INT}, \typ{REAL}, \typ{FRAC}, \typ{QUAD} or \typ{COMPLEX}) to either
+a \typ{REAL} or \typ{COMPLEX} whose components are \typ{REAL} of length
+\kbd{prec}.
+
+\subsec{Copy} It is also very useful to \idx{copy} a PARI object, not
+just by moving around a pointer as in the \kbd{y = x} example, but by
+creating a copy of the whole tree structure, without pre-allocating a
+possibly complicated \kbd{y} to use with \kbd{gaffect}. The function which
+does this is called \teb{gcopy}. Its syntax is:
+
+\fun{GEN}{gcopy}{GEN x}
+
+\noindent and the effect is to create a new copy of x on the PARI stack.
+
+Sometimes, on the contrary, a quick copy of the skeleton of \kbd{x} is
+enough, leaving pointers to the original data in \kbd{x} for the sake of
+speed instead of making a full recursive copy. Use
+\fun{GEN}{shallowcopy}{GEN x} for this. Note that the result is not suitable
+for \tet{gerepileupto} !
+
+Make sure at this point that you understand the difference between \kbd{y =
+x}, \kbd{y = gcopy(x)}, \kbd{y = shallowcopy(x)} and \kbd{gaffect(x,y)}.
+
+\subsec{Clones}\sidx{clone}\label{se:clone}
+Sometimes, it is more efficient to create a \emph{persistent} copy of a PARI
+object. This is not created on the stack but on the heap, hence unaffected by
+\tet{gerepile} and friends. The function which does this is called
+\teb{gclone}. Its syntax is:
+
+\fun{GEN}{gclone}{GEN x}
+
+A clone can be removed from the heap (thus destroyed) using
+
+\fun{void}{gunclone}{GEN x}
+
+\noindent No PARI object should keep references to a clone which has been
+destroyed!
+
+\subsec{Conversions}\sidx{conversions}
+The following functions convert C objects to PARI objects (creating them on
+the stack as usual):
+
+\fun{GEN}{stoi}{long s}: C \kbd{long} integer  (``small'') to \typ{INT}.
+
+\fun{GEN}{dbltor}{double s}: C \kbd{double} to \typ{REAL}. The accuracy of
+the result is 19 decimal digits, i.e.~a type \typ{REAL} of length
+\kbd{DEFAULTPREC}, although on 32-bit machines only 16 of them are
+significant.
+
+\noindent We also have the converse functions:
+
+\fun{long}{itos}{GEN x}: \kbd{x} must be of type \typ{INT},
+
+\fun{double}{rtodbl}{GEN x}: \kbd{x} must be of type \typ{REAL},
+
+\noindent as well as the more general ones:
+
+\fun{long}{gtolong}{GEN x},
+
+\fun{double}{gtodouble}{GEN x}.
+
+\section{Implementation of the PARI types}
+\label{se:impl}
+
+\noindent
+We now go through each type and explain its implementation. Let \kbd{z} be a
+\kbd{GEN}, pointing at a PARI object. In the following paragraphs, we will
+constantly mix two points of view: on the one hand, \kbd{z} is treated as the
+C pointer it is, on the other, as PARI's handle on some mathematical entity,
+so we will shamelessly write $\kbd{z} \ne 0$ to indicate that the
+\emph{value} thus represented is nonzero (in which case the
+\emph{pointer}~\kbd{z} is certainly non-\kbd{NULL}). We offer no apologies
+for this style. In fact, you had better feel comfortable juggling both views
+simultaneously in your mind if you want to write correct PARI programs.
+
+Common to all the types is the first codeword \kbd{z[0]}, which we do not
+have to worry about since this is taken care of by \kbd{cgetg}. Its precise
+structure depends on the machine you are using, but it always contains the
+following data: the \emph{internal type number}\sidx{type number} associated
+to the symbolic type name, the \emph{length} of the root in longwords, and a
+technical bit which indicates whether the object is a clone or not (see
+\secref{se:clone}). This last one is used by \kbd{gp} for internal garbage
+collecting, you will not have to worry about it.
+
+Some types have a second codeword, different for each type, which we will
+soon describe as we will shortly consider each of them in turn.
+
+\noindent The first codeword is handled through the following \emph{macros}:
+
+\fun{long}{typ}{GEN z} returns the type number of \kbd{z}.
+
+\fun{void}{settyp}{GEN z, long n} sets the type number of \kbd{z} to
+\kbd{n} (you should not have to use this function if you use \kbd{cgetg}).
+
+\fun{long}{lg}{GEN z} returns the length (in longwords) of the root of \kbd{z}.
+
+\fun{long}{setlg}{GEN z, long l} sets the length of \kbd{z} to \kbd{l} (you
+should not have to use this function if you use \kbd{cgetg}; however, see
+an advanced example in \secref{se:prog}).
+
+\fun{long}{isclone}{GEN z} is \kbd{z} a clone?
+
+\fun{void}{setisclone}{GEN z} sets the \emph{clone} bit.
+
+\fun{void}{unsetisclone}{GEN z} clears the \emph{clone} bit.
+
+\misctitle{Important remark} For the sake of efficiency, none of the
+codeword-handling macros check the types of their arguments even when there
+are stringent restrictions on their use. It is trivial to create invalid
+objects, or corrupt one of the ``universal constants'' (e.g. setting the sign
+of \kbd{gen\_0} to $1$), and they usually provide negligible savings.
+Use higher level functions whenever possible.
+
+\misctitle{Remark} The clone bit is there so that \kbd{gunclone} can check
+it is deleting an object which was allocated by \kbd{gclone}. Miscellaneous
+vector entries are often cloned by \kbd{gp} so that a GP statement like
+\kbd{v[1] = x} does not involve copying the whole of \kbd{v}: the component
+\kbd{v[1]} is deleted if its clone bit is set, and is replaced by a clone of
+\kbd{x}. Don't set/unset yourself the clone bit unless you know what you are
+doing: in particular \emph{never} set the clone bit of a vector component
+when the said vector is scheduled to be uncloned. Hackish code may abuse the
+clone bit to tag objects for reasons unrelated to the above instead of using
+proper data structures. Don't do that.
+
+\subsec{Type \typ{INT} (integer)}
+\sidx{integer}\kbdsidx{t_INT}this type has a second codeword \kbd{z[1]} which
+contains the following information:
+
+the sign of \kbd{z}: coded as $1$, $0$ or $-1$ if $\kbd{z} > 0$, $\kbd{z} = 0$,
+$\kbd{z} < 0$ respectively.
+
+the \emph{effective length} of \kbd{z}, i.e.~the total number of significant
+longwords. This means the following: apart from the integer 0, every integer
+is ``normalized'', meaning that the most significant mantissa longword is
+non-zero. However, the integer may have been created with a longer length.
+Hence the ``length'' which is in \kbd{z[0]} can be larger than the
+``effective length'' which is in \kbd{z[1]}.
+
+\noindent This information is handled using the following macros:
+
+\fun{long}{signe}{GEN z} returns the sign of \kbd{z}.
+
+\fun{void}{setsigne}{GEN z, long s} sets the sign of \kbd{z} to \kbd{s}.
+
+\fun{long}{lgefint}{GEN z} returns the \idx{effective length} of \kbd{z}.
+
+\fun{void}{setlgefint}{GEN z, long l} sets the effective length
+of \kbd{z} to \kbd{l}.
+
+The integer 0 can be recognized either by its sign being~0, or by its
+effective length being equal to~2. Now assume that $\kbd{z} \ne 0$, and let
+$$ |z| = \sum_{i = 0}^n z_i B^i,
+  \quad\text{where}~z_n\ne 0~\text{and}~B = 2^{\kbd{BITS\_IN\_LONG}}.
+$$
+With these notations, $n$ is \kbd{lgefint(z) - 3}, and the mantissa of
+$\kbd{z}$ may be manipulated via the following interface:
+
+\fun{GEN}{int_MSW}{GEN z} returns a pointer to the most significant word of
+\kbd{z}, $z_n$.
+
+\fun{GEN}{int_LSW}{GEN z} returns a pointer to the least significant word of
+\kbd{z}, $z_0$.
+
+\fun{GEN}{int_W}{GEN z, long i} returns the $i$-th significant word of
+\kbd{z}, $z_i$. Accessing the $i$-th significant word for $i > n$
+yields unpredictable results.
+
+\fun{GEN}{int_W_lg}{GEN z, long i, long lz} returns the $i$-th significant
+word of \kbd{z}, $z_i$, assuming \kbd{lgefint(z)} is \kbd{lz} ($= n + 3$).
+Accessing the $i$-th significant word for $i > n$ yields unpredictable
+results.
+
+\fun{GEN}{int_precW}{GEN z} returns the previous (less significant) word of
+\kbd{z}, $z_{i-1}$ assuming \kbd{z} points to $z_i$.
+
+\fun{GEN}{int_nextW}{GEN z} returns the next (more significant) word of \kbd{z},
+$z_{i+1}$ assuming \kbd{z} points to $z_i$.
+
+Unnormalized integers, such that $z_n$ is possibly $0$, are explicitly
+forbidden. To enforce this, one may write an arbitrary mantissa then call
+
+\fun{void}{int_normalize}{GEN z, long known0}
+
+\noindent normalizes in place a non-negative integer (such that $z_n$ is
+possibly $0$), assuming at least the first \kbd{known0} words are zero.
+
+\noindent For instance a binary \kbd{and} could be implemented in the
+following way:
+\bprog
+GEN AND(GEN x, GEN y) {
+  long i, lx, ly, lout;
+  long *xp, *yp, *outp; /* mantissa pointers */
+  GEN out;
+
+  if (!signe(x) || !signe(y)) return gen_0;
+  lx = lgefint(x); xp = int_LSW(x);
+  ly = lgefint(y); yp = int_LSW(y); lout = min(lx,ly); /* > 2 */
+
+  out = cgeti(lout); out[1] = evalsigne(1) | evallgefint(lout);
+  outp = int_LSW(out);
+  for (i=2; i < lout; i++)
+  {
+    *outp = (*xp) & (*yp);
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+    yp    = int_nextW(yp);
+  }
+  if ( !*int_MSW(out) ) out = int_normalize(out, 1);
+  return out;
+}
+ at eprog
+
+\noindent This low-level interface is mandatory in order to write portable
+code since PARI can be compiled using various multiprecision kernels, for
+instance the native one or GNU MP, with incompatible internal structures
+(for one thing, the mantissa is oriented in different directions).
+
+\noindent The following further functions are available:
+
+\fun{int}{mpodd}{GEN x} which is 1 if \kbd{x} is odd, and 0 otherwise.
+
+\fun{long}{mod2}{GEN x}
+
+\fun{long}{mod4}{GEN x}
+
+\fun{long}{mod8}{GEN x}
+
+\fun{long}{mod16}{GEN x}
+
+\fun{long}{mod32}{GEN x}
+
+\fun{long}{mod64}{GEN x} give the residue class of \kbd{x} modulo the
+corresponding power of 2, for \emph{positive}~\kbd{x}. By definition,
+$\kbd{mod}n(x) := \kbd{mod}n(|x|)$ for $x < 0$ (the functions disregard the
+sign), and the result is undefined if $x = 0$. As well,
+
+\fun{ulong}{mod2BIL}{GEN x} returns the least significant word of $|x|$, still
+assuming that $x\neq 0$.
+
+These functions directly access the binary data and are thus much faster than
+the generic modulo functions. Besides, they return long integers instead of
+\kbd{GEN}s, so they do not clutter up the stack.
+
+\subsec{Type \typ{REAL} (real number)}
+\kbdsidx{t_REAL}\sidx{real number}this type has a second codeword z[1] which
+also encodes its sign, obtained or set using the same functions as for a
+\typ{INT}, and a binary exponent. This exponent is handled using the
+following macros:
+
+\fun{long}{expo}{GEN z} returns the exponent of \kbd{z}.
+This is defined even when \kbd{z} is equal to zero, see
+\secref{se:whatzero}.
+
+\fun{void}{setexpo}{GEN z, long e} sets the exponent of \kbd{z} to \kbd{e}.
+
+\noindent Note the functions:
+
+\fun{long}{gexpo}{GEN z} which tries to return an exponent for \kbd{z},
+even if \kbd{z} is not a real number.
+
+\fun{long}{gsigne}{GEN z} which returns a sign for \kbd{z}, even when
+\kbd{z} is neither real nor integer (a rational number for instance).
+
+The real zero is characterized by having its sign equal to 0. If \kbd{z} is
+not equal to~0, then is is represented as $2^e M$, where $e$ is the exponent,
+and $M\in [1, 2[$ is the mantissa of $z$, whose digits are stored in
+$\kbd{z[2]},\dots, \kbd{z[lg(z)-1]}$.
+
+More precisely, let $m$ be the integer (\kbd{z[2]},\dots, \kbd{z[lg(z)-1]})
+in base \kbd{2\pow BITS\_IN\_LONG}; here, \kbd{z[2]} is the most significant
+longword and is normalized, i.e.~its most significant bit is~1. Then we have
+$M := m / 2^{\kbd{bit\_accuracy(lg(z))} - 1 - \kbd{expo}(z)}$.
+
+\fun{GEN}{mantissa_real}{GEN z, long *e} returns the mantissa $m$ of $z$, and
+sets \kbd{*e} to the exponent $\kbd{bit\_accuracy(lg(z))}-1-\kbd{expo}(z)$,
+so that $z = m / 2^e$.
+
+Thus, the real number $3.5$ to accuracy \kbd{bit\_accuracy(lg(z))} is
+represented as \kbd{z[0]} (encoding $\kbd{type} = \typ{REAL}$, \kbd{lg(z)}),
+\kbd{z[1]} (encoding $\kbd{sign} = 1$, $\kbd{expo} = 1$), $\kbd{z[2]} =
+\kbd{0xe0000000}$, $\kbd{z[3]} =\dots = \kbd{z[lg(z)-1]} = \kbd{0x0}$.
+
+\subsec{Type \typ{INTMOD}}\kbdsidx{t_INTMOD}
+\kbd{z[1]} points to the modulus, and \kbd{z[2]} at the number representing
+the class \kbd{z}. Both are separate \kbd{GEN} objects, and both must be
+\typ{INT}s, satisfying the inequality $0 \le \kbd{z[2]} < \kbd{z[1]}$.
+
+\subsec{Type \typ{FRAC} (rational number)}%
+\kbdsidx{t_FRAC}\sidx{rational number}
+\kbd{z[1]} points to the numerator $n$, and \kbd{z[2]} to the denominator
+$d$. Both must be of type \typ{INT} such that $n\neq 0$, $d > 0$ and
+$(n,d) = 1$.
+
+\subsec{Type \typ{FFELT} (finite field element)}%
+\kbdsidx{t_FFELT}\sidx{finite field element} (Experimental)
+
+Components of this type should normally not be accessed directly. Instead,
+finite field elements should be created using \kbd{ffgen}.
+
+\noindent The second codeword \kbd{z[1]} determines the storage format of the
+element, among
+
+\item \tet{t_FF_FpXQ}: \kbd{A=z[2]} and \kbd{T=z[3]} are \kbd{FpX},
+\kbd{p=z[4]} is a \typ{INT}, where $p$ is a prime number, $T$ is irreducible
+modulo $p$, and $\deg A < \deg T$.
+This represents the element $A\pmod{T}$ in $\F_p[X]/T$.
+
+\item \tet{t_FF_Flxq}: \kbd{A=z[2]} and \kbd{T=z[3]} are \kbd{Flx},
+\kbd{l=z[4]} is a \typ{INT}, where $l$ is a prime number, $T$ is irreducible
+modulo $l$, and $\deg A < \deg T$ This represents the element $A\pmod{T}$ in
+$\F_l[X]/T$.
+
+\item \tet{t_FF_F2xq}: \kbd{A=z[2]} and \kbd{T=z[3]} are \kbd{F2x},
+\kbd{l=z[4]} is the \typ{INT} $2$, $T$ is irreducible modulo $2$, and
+$\deg A < \deg T$. This represents the element $A\pmod{T}$ in $\F_2[X]/T$.
+
+\subsec{Type \typ{COMPLEX} (complex number)}%
+\kbdsidx{t_COMPLEX}\sidx{complex number}
+\kbd{z[1]} points to the real part, and \kbd{z[2]} to the imaginary part.
+The components \kbd{z[1]} and \kbd{z[2]} must be of type
+\typ{INT}, \typ{REAL} or \typ{FRAC}. For historical reasons \typ{INTMOD}
+and \typ{PADIC} are also allowed (the latter for $p = 2$ or
+congruent to 3 mod 4 only), but one should rather use the more general
+\typ{POLMOD} construction.
+
+\subsec{Type \typ{PADIC} ($p$-adic numbers)}%
+\sidx{p-adic number}\kbdsidx{t_PADIC} this type has a second codeword
+\kbd{z[1]} which contains the following information: the $p$-adic precision
+(the exponent of $p$ modulo which the $p$-adic unit corresponding to
+\kbd{z} is defined if \kbd{z} is not~0), i.e.~one less than the number of
+significant $p$-adic digits, and the exponent of \kbd{z}. This information
+can be handled using the following functions:
+
+\fun{long}{precp}{GEN z} returns the $p$-adic precision of \kbd{z}. This is
+$0$ if $\kbd{z} = 0$.
+
+\fun{void}{setprecp}{GEN z, long l} sets the $p$-adic precision of \kbd{z}
+to \kbd{l}.
+
+\fun{long}{valp}{GEN z} returns the $p$-adic valuation of \kbd{z} (i.e. the
+exponent). This is defined even if \kbd{z} is equal to~0, see
+\secref{se:whatzero}.
+
+\fun{void}{setvalp}{GEN z, long e} sets the $p$-adic valuation of \kbd{z}
+to \kbd{e}.
+
+In addition to this codeword, \kbd{z[2]} points to the prime $p$,
+\kbd{z[3]} points to $p^{\text{precp(z)}}$, and \kbd{z[4]} points to
+a\typ{INT} representing the $p$-adic unit associated to \kbd{z} modulo
+\kbd{z[3]} (and to zero if \kbd{z} is zero). To summarize, if $z\neq
+0$, we have the equality:
+$$ \kbd{z} = p^{\text{valp(z)}} * (\kbd{z[4]} + O(\kbd{z[3]})),\quad
+\text{where}\quad \kbd{z[3]} = O(p^{\text{precp(z)}}). $$
+
+\subsec{Type \typ{QUAD} (quadratic number)}
+\sidx{quadratic number}\kbdsidx{t_QUAD}\kbd{z[1]} points to the canonical
+polynomial $P$ defining the quadratic field (as output by \tet{quadpoly}),
+\kbd{z[2]} to the ``real part'' and \kbd{z[3]} to the ``imaginary part''. The
+latter are of type \typ{INT}, \typ{FRAC}, \typ{INTMOD}, or \typ{PADIC} and
+are to be taken as the coefficients of \kbd{z} with respect to the canonical
+basis $(1,X)$ or $\Q[X]/(P(X))$, see~\secref{se:compquad}. Exact complex
+numbers may be implemented as quadratics, but \typ{COMPLEX} is in general
+more versatile (\typ{REAL} components are allowed) and more efficient.
+
+Operations involving a \typ{QUAD} and \typ{COMPLEX} are implemented by
+converting the \typ{QUAD} to a \typ{REAL} (or \typ{COMPLEX} with \typ{REAL}
+components) to the accuracy of the \typ{COMPLEX}. As a consequence,
+operations between \typ{QUAD} and \emph{exact} \typ{COMPLEX}s are not allowed.
+
+\subsec{Type \typ{POLMOD} (polmod)}\kbdsidx{t_POLMOD}\sidx{polmod}
+as for \typ{INTMOD}s, \kbd{z[1]} points to the modulus, and \kbd{z[2]}
+to a polynomial representing the class of~\kbd{z}. Both must be of type
+\typ{POL} in the same variable, satisfying the inequality $\deg \kbd{z[2]}
+< \deg \kbd{z[1]}$. However, \kbd{z[2]} is allowed to be a simplification
+of such a polynomial, e.g.~a scalar. This is tricky considering the
+hierarchical structure of the variables; in particular, a polynomial in
+variable of \emph{lesser} priority (see \secref{se:priority}) than the
+modulus variable is valid, since it is considered as the constant term of
+a polynomial of degree 0 in the correct variable. On the other hand a
+variable of \emph{greater} priority is not acceptable; see
+\secref{se:priority} for the problems which may arise.
+
+\subsec{Type \typ{POL} (polynomial)}\kbdsidx{t_POL}\sidx{polynomial} this
+type has a second codeword. It contains a ``\emph{sign}'': 0 if the
+polynomial is equal to~0, and 1 if not (see however the important remark
+below) and a \emph{variable number} (e.g.~0 for $x$, 1 for $y$, etc\dots).
+
+\noindent These data can be handled with the following macros: \teb{signe}
+and \teb{setsigne} as for \typ{INT} and \typ{REAL},
+
+\fun{long}{varn}{GEN z} returns the variable number of the object \kbd{z},
+
+\fun{void}{setvarn}{GEN z, long v} sets the variable number of \kbd{z} to
+\kbd{v}.
+
+The variable numbers encode the relative priorities of variables as discussed
+in \secref{se:priority}. We will give more details in \secref{se:vars}. Note
+also the function \fun{long}{gvar}{GEN z} which tries to return a
+\idx{variable number} for \kbd{z}, even if \kbd{z} is not a polynomial or
+power series. The variable number of a scalar type is set by definition equal
+to \tet{NO_VARIABLE}, which has lower priority than any other variable number.
+
+The components \kbd{z[2]}, \kbd{z[3]},\dots \kbd{z[lg(z)-1]} point to the
+coefficients of the polynomial \emph{in ascending order}, with \kbd{z[2]}
+being the constant term and so on.
+
+For a \typ{POL} of non-zero sign, \tet{degpol}, \tet{leading_term},
+\tet{constant_term}, return its degree, and a pointer to the leading,
+resp. constant, coefficient with respect to the main variable. Note that no
+copy is made on the PARI stack so the returned value is not safe for a basic
+\kbd{gerepile} call. Applied to any other type than \typ{POL}, the result is
+unspecified. Those three functions are still defined when the sign is $0$,
+see \secref{se:accessors} and \secref{se:polynomials}.
+
+\fun{long}{degree}{GEN x} returns the degree of \kbd{x} with respect to its
+main variable even when \kbd{x} is not a polynomial (a rational function for
+instance). By convention, the degree of a zero polynomial is~$-1$.
+
+\misctitle{Important remark}
+The leading coefficient of a \typ{POL} may be equal to zero:
+
+\item it is not allowed to be an exact rational $0$, such as \tet{gen_0};
+
+\item an exact non-rational $0$, like \kbd{Mod(0,2)}, is possible for
+constant polynomials, i.e. of length $3$ and no other coefficient: this
+carries information about the base ring for the polynomial;
+
+\item an inexact $0$, like \kbd{0.E-38} or \kbd{O(3\pow 5)}, is always
+possible. Inexact zeroes do not correspond to an actual $0$, but to a
+very small coefficient according to some metric; we keep them to give
+information on how much cancellation occurred in previous computations.
+
+A polynomial disobeying any of these rules is an invalid \emph{unnormalized}
+object. We advise \emph{not} to use low-level constructions to build a
+\typ{POL} coefficient by coefficient, such as
+\bprog
+  GEN T = cgetg(4, t_POL);
+  T[1] = evalvarn(0);
+  gel(T, 2) = x;
+  gel(T, 3) = y;
+ at eprog\noindent But if you do and it is not clear whether the result will be
+normalized, call
+
+\fun{GEN}{normalizepol}{GEN x} applied to an unnormalized \typ{POL}~\kbd{x}
+(with all coefficients correctly set except that \kbd{leading\_term(x)} might
+be zero), normalizes \kbd{x} correctly in place and returns~\kbd{x}. This
+functions sets \kbd{signe} (to $0$ or $1$) properly.
+
+\misctitle{Caveat} A consequence of the remark above is that zero polynomials
+are characterized by the fact that their sign is~0. It is in general
+incorrect to check whether \kbd{lg(x)} is $2$ or \kbd{degpol(x)} $< 0$,
+although both tests are valid when the coefficient types are under control:
+for instance, when they are guaranteed to be \typ{INT}s or \typ{FRAC}s.
+The same remark applies to \typ{SER}s.
+
+\subsec{Type \typ{SER} (power series)}
+\kbdsidx{t_SER}\sidx{power series}This type also has a second codeword, which
+encodes a ``\emph{sign}'', i.e.~0 if the power series is 0, and 1 if not, a
+\emph{variable number} as for polynomials, and an \emph{exponent}. This
+information can be handled with the following functions: \teb{signe},
+\teb{setsigne}, \teb{varn}, \teb{setvarn} as for polynomials, and \teb{valp},
+\teb{setvalp} for the exponent as for $p$-adic numbers. Beware: do \emph{not}
+use \teb{expo} and \teb{setexpo} on power series.
+
+The coefficients \kbd{z[2]}, \kbd{z[3]},\dots \kbd{z[lg(z)-1]} point to
+the coefficients of \kbd{z} in ascending order. As for polynomials
+(see remark there), the sign of a \typ{SER} is $0$ if and only all
+its coefficients are equal to $0$. (The leading coefficient cannot be an
+integer $0$.)
+
+Note that the exponent of a power series can be negative, i.e.~we are
+then dealing with a Laurent series (with a finite number of negative
+terms).
+
+\subsec{Type \typ{RFRAC} (rational function)}%
+\kbdsidx{t_RFRAC}\sidx{rational function} \kbd{z[1]} points to the
+numerator $n$, and \kbd{z[2]} on the denominator $d$. The denominator must be
+of type \typ{POL}, with variable of higher priority than the numerator. The
+numerator $n$ is not an exact $0$ and $(n,d) = 1$ (see \tet{gred_rfac2}).
+
+\subsec{Type \typ{QFR} (indefinite binary quadratic form)}%
+\kbdsidx{t_QFR}\sidx{indefinite binary quadratic form} \kbd{z[1]},
+\kbd{z[2]}, \kbd{z[3]} point to the three coefficients of the form and are of
+type \typ{INT}. \kbd{z[4]} is Shanks's distance function, and must be of type
+\typ{REAL}.
+
+\subsec{Type \typ{QFI} (definite binary quadratic form)}%
+\kbdsidx{t_QFI}\sidx{definite binary quadratic form} \kbd{z[1]}, \kbd{z[2]},
+\kbd{z[3]} point to the three coefficients of the form. All three are of type
+\typ{INT}.
+
+\subsec{Type \typ{VEC} and \typ{COL} (vector)}%
+\kbdsidx{t_VEC}\kbdsidx{t_COL}\sidx{row vector}\sidx{column vector}
+\kbd{z[1]}, \kbd{z[2]},\dots \kbd{z[lg(z)-1]} point to the components of the
+vector.
+
+\subsec{Type \typ{MAT} (matrix)}\kbdsidx{t_MAT}\sidx{matrix} \kbd{z[1]},
+\kbd{z[2]},\dots \kbd{z[lg(z)-1]} point to the column vectors of \kbd{z},
+i.e.~they must be of type \typ{COL} and of the same length.
+
+\subsec{Type \typ{VECSMALL} (vector of small integers)}\kbdsidx{t_VECSMALL}
+\kbd{z[1]}, \kbd{z[2]},\dots \kbd{z[lg(z)-1]} are ordinary signed long
+integers. This type is used instead of a \typ{VEC} of \typ{INT}s for
+efficiency reasons, for instance to implement efficiently permutations,
+polynomial arithmetic and linear algebra over small finite fields, etc.
+
+\subsec{Type \typ{STR} (character string)}%
+\kbdsidx{t_STR}\sidx{character string}
+
+\fun{char *}{GSTR}{z} (= \kbd{(z+1)}) points to the first character of the
+(\kbd{NULL}-terminated) string.
+
+\subsec{Type \typ{ERROR} (error context)}\kbdsidx{t_ERROR}\sidx{error context}
+This type holds error messages, as well as details about the error, as
+returned by the exception handling system. The second codeword \kbd{z[1]}
+contains the error type (an \kbd{int}, as passed to \tet{pari_err}). The
+subsequent words \kbd{z[2]},\dots \kbd{z[lg(z)-1]} are \kbd{GEN}s containing
+additional data, depending on the error type.
+
+\subsec{Type \typ{CLOSURE} (closure)}\kbdsidx{t_CLOSURE}\sidx{closure}
+This type holds GP functions and closures, in compiled form. The internal detail
+of this type is subject to change each time the GP language evolves. Hence we
+do not describe it here and refer to the Developer's Guide.  However
+functions to create or to evaluate \typ{CLOSURE}s are documented in
+\secref{se:closure}.
+
+\fun{long}{closure_arity}{GEN C} returns the arity of the \typ{CLOSURE}.
+
+\subsec{Type \typ{LIST} (list)}\kbdsidx{t_LIST}\sidx{list}
+this type was introduced for specific \kbd{gp} use and is rather inefficient
+compared to a straightforward linked list implementation (it requires more
+memory, as well as many unnecessary copies). Hence we do not describe it
+here and refer to the Developer's Guide.
+
+\misctitle{Implementation note} For the types including an exponent (or a
+valuation), we actually store a biased non-negative exponent (bit-ORing the
+biased exponent to the codeword), obtained by adding a constant to the true
+exponent: either \kbd{HIGHEXPOBIT} (for \typ{REAL}) or \kbd{HIGHVALPBIT} (for
+\typ{PADIC} and \typ{SER}). Of course, this is encapsulated by the
+exponent/valuation-handling macros and needs not concern the library user.
+
+\section{PARI variables}\label{se:vars}
+\subsec{Multivariate objects}
+\sidx{variable (priority)}
+
+\noindent We now consider variables and formal computations, and give the
+technical details corresponding to the general discussion in
+\secref{se:priority}. As we have seen in \secref{se:impl}, the codewords for
+types \typ{POL} and \typ{SER} encode a ``variable number''. This is an
+integer, ranging from $0$ to \kbd{MAXVARN}. Relative priorities may be
+ascertained using
+
+\fun{int}{varncmp}{long v, long w}
+
+\noindent which is $>0$, $=0$, $<0$ whenever $v$ has lower, resp.~same,
+resp.~higher priority than $w$.
+
+The way an object is considered in formal computations depends entirely on
+its ``principal variable number'' which is given by the function
+
+\fun{long}{gvar}{GEN z}
+
+\noindent which returns a \idx{variable number} for \kbd{z}, even if \kbd{z}
+is not a polynomial or power series. The variable number of a scalar type is
+set by definition equal to \tet{NO_VARIABLE} which has lower priority than any
+valid variable number. The variable number of a recursive type which is not a
+polynomial or power series is the variable number with highest priority among
+its components. But for polynomials and power series only the ``outermost''
+number counts (we directly access $\tet{varn}(x)$ in the codewords): the
+representation is not symmetrical at all.
+
+Under \kbd{gp}, one needs not worry too much since the interpreter defines
+the variables as it sees them\footnote{*}{ The first time a given identifier
+is read by the GP parser a new variable is created, and it is assigned a
+strictly lower priority than any variable in use at this point. On startup,
+before any user input has taken place, 'x' is defined in this way and has
+initially maximal priority (and variable number $0$).}
+%
+and do the right thing with the polynomials produced (however, have a look at
+the remark in \secref{se:rempolmod}).
+
+But in library mode, they are tricky objects if you intend to build
+polynomials yourself (and not just let PARI functions produce them, which is
+less efficient). For instance, it does not make sense to have a variable
+number occur in the components of a polynomial whose main variable has a
+lower priority, even though PARI cannot prevent you from doing it; see
+\secref{se:priority} for a discussion of possible problems in a similar
+situation.
+
+\subsec{Creating variables} A basic difficulty is to ``create'' a variable.
+Some initializations are needed before you can use a given integer $v$ as a
+variable number.
+
+Initially, this is done for $0$ (the variable \kbd{x} under \kbd{gp}), and
+\tet{MAXVARN}, which is there to address the need for a ``temporary'' new
+variable in library mode and cannot be input under \kbd{gp}. No documented
+library function can create from scratch an object involving \tet{MAXVARN}
+(of course, if the operands originally involve \kbd{MAXVARN}, the function
+abides). We call the latter type a ``temporary variable''. The regular
+variables meant to be used in regular objects, are called ``user
+variables\sidx{variable (user)}''.
+
+\subsubsec{User variables}\sidx{variable (user)} When the program starts,
+\kbd{x} is the only user variable (number~$0$). To define new ones, use
+
+\fun{long}{fetch_user_var}{char *s}: inspects the user variable whose name is
+the string pointed to by \kbd{s}, creating it if needed, and returns its
+variable number.
+\bprog
+long v = fetch_user_var("y");
+GEN gy = pol_x(v);
+ at eprog\noindent
+The function raises an exception if the name is already in use for an
+\tet{install}ed or built-in function, or an alias.
+
+\misctitle{Caveat} You can use \tet{gp_read_str}
+(see~\secref{se:gp_read_str}) to execute a GP command and create GP
+variables on the fly as needed:
+\bprog
+GEN gy = gp_read_str("'y"); /*@Ccom returns \kbd{pol\_x}($v$), for some $v$ */
+long v = varn(gy);
+ at eprog\noindent
+But please note the quote \kbd{'y} in the above. Using \kbd{gp\_read\_str("y")}
+might work, but is dangerous, especially when programming functions to
+be used under \kbd{gp}. The latter reads the value of \kbd{y}, as
+\emph{currently} known by the \kbd{gp} interpreter, possibly creating it
+in the process. But if \kbd{y} has been modified by previous \kbd{gp}
+commands (e.g.~\kbd {y = 1}), then the value of \kbd{gy} is not what you
+expected it to be and corresponds instead to the current value of the
+\kbd{gp} variable (e.g.~\kbd{gen\_1}).
+
+\fun{GEN}{fetch_var_value}{long v} returns a shallow copy of the current
+value of the variable numbered $v$. Returns \kbd{NULL} if that variable
+number is unknown to the interpreter,  e.g. it is a user variable. Note
+that this may not be the same as \kbd{pol\_x(v)} if assignments have been
+performed in the interpreter.
+
+\subsubsec{Temporary variables}\sidx{variable (temporary)}
+\kbd{MAXVARN} is available, but is better left to PARI internal functions
+(some of which do not check that \kbd{MAXVARN} is free for them to use,
+which can be considered a bug). You can create more temporary variables
+using
+
+\fun{long}{fetch_var}{}\label{se:fetch_var}
+
+\noindent
+This returns a variable number which is guaranteed to be unused by the
+library at the time you get it and as long as you do not delete it (we will
+see how to do that shortly). This has \emph{higher} priority than any
+temporary variable produced so far (\kbd{MAXVARN} is assumed to be the first
+such). After the statement \kbd{v = fetch\_var()}, you can use
+\kbd{pol\_1(v)} and \kbd{pol\_x(v)}. The variables created in this way have no
+identifier assigned to them though, and are printed as
+\kbd{\#<\text{number}>}, except for \kbd{MAXVARN} which is printed
+as~\kbd{\#}. You can assign a name to a temporary variable, after creating
+it, by calling the function
+
+\fun{void}{name_var}{long n, char *s}
+
+\noindent after which the output machinery will use the name \kbd{s} to
+represent the variable number~\kbd{n}. The GP parser will \emph{not}
+recognize it by that name, however, and calling this on a variable known
+to~\kbd{gp} raises an error. Temporary variables are meant to be used as free
+variables, and you should never assign values or functions to them as you
+would do with variables under~\kbd{gp}. For that, you need a user variable.
+
+All objects created by \kbd{fetch\_var} are on the heap and not on the stack,
+thus they are not subject to standard garbage collecting (they are not
+destroyed by a \kbd{gerepile} or \kbd{avma = ltop} statement). When you do
+not need a variable number anymore, you can delete it using
+
+\fun{long}{delete_var}{}
+
+\noindent which deletes the \emph{latest} temporary variable created and
+returns the variable number of the previous one (or simply returns 0 if you
+try, in vain, to delete \kbd{MAXVARN}). Of course you should make sure that
+the deleted variable does not appear anywhere in the objects you use later
+on. Here is an example:
+
+\bprog
+  long first = fetch_var();
+  long n1 = fetch_var();
+  long n2 = fetch_var(); /*@Ccom prepare three variables for internal use */
+  ...
+  /*@Ccom delete all variables before leaving */
+  do { num = delete_var(); } while (num && num <= first);
+ at eprog\noindent
+The (dangerous) statement
+
+\bprog
+  while (delete_var()) /*@Ccom empty */;
+ at eprog\noindent
+removes all temporary variables in use, except \kbd{MAXVARN} which cannot be
+deleted.
+
+\section{Input and output}
+
+\noindent
+Two important aspects have not yet been explained which are specific to
+library mode: input and output of PARI objects.
+
+\subsec{Input}
+
+\noindent
+For \idx{input}, PARI provides a powerful high level function
+which enables you to input your objects as if you were under \kbd{gp}. In fact,
+it \emph{is} essentially the GP syntactical parser, hence you can use it not
+only for input but for (most) computations that you can do under \kbd{gp}.
+It has the following syntax:\label{se:gp_read_str}
+
+\fun{GEN}{gp_read_str}{const char *s}
+
+\noindent
+Note that \kbd{gp}'s metacommands are not recognized.
+
+\misctitle{Note} The obsolete form
+
+\fun{GEN}{readseq}{char *t}
+
+still exists for backward compatibility (assumes filtered input, without
+spaces or comments). Don't use it.
+
+To read a \kbd{GEN} from a file, you can use the simpler interface
+
+\fun{GEN}{gp_read_stream}{FILE *file}
+
+\noindent which reads a character string of arbitrary length from the stream
+\kbd{file} (up to the first complete expression sequence), applies
+\kbd{gp\_read\_str} to it, and returns the resulting \kbd{GEN}. This way, you
+do not have to worry about allocating buffers to hold the string. To
+interactively input an expression, use \kbd{gp\_read\_stream(stdin)}.
+
+Finally, you can read in a whole file, as in GP's \tet{read} statement
+
+\fun{GEN}{gp_read_file}{char *name}
+
+\noindent As usual, the return value is that of the last non-empty expression
+evaluated. There is one technical exception: if \kbd{name} is a \emph{binary}
+file (from \tet{writebin}) containing more than one object, a \typ{VEC}
+containing them all is returned. This is because binary objects bypass the
+parser, hence reading them has no useful side effect.
+
+\subsec{Output to screen or file, output to string}\sidx{output}
+
+General output functions return nothing but print a character string as a
+side effect. Low level routines are available to write on PARI output stream
+\tet{pari_outfile} (\tet{stdout} by default):
+
+\fun{void}{pari_putc}{char c}: write character \kbd{c} to the output stream.
+
+\fun{void}{pari_puts}{char *s}: write \kbd{s} to the output stream.
+
+\fun{void}{pari_flush}{}: flush output stream; most streams are buffered by
+default, this command makes sure that all characters output so are actually
+written.
+
+\fun{void}{pari_printf}{const char *fmt, ...}: the most versatile such
+function. \kbd{fmt} is a character string similar to the one
+\tet{printf} uses. In there, \kbd{\%} characters have a special meaning, and
+describe how to print the remaining operands. In addition to the standard
+format types (see the GP function \tet{printf}), you can use the \emph{length
+modifier}~\kbd{P} (for PARI of course!) to specify that an argument is a
+\kbd{GEN}. For instance, the following are valid conversions for a \kbd{GEN}
+argument
+\bprog
+    %Ps     @com convert to \kbd{char*} (will print an arbitrary \kbd{GEN})
+    %P.10s  @com convert to \kbd{char*}, truncated to 10 chars
+    %P.2f   @com convert to floating point format with 2 decimals
+    %P4d    @com convert to integer, field width at least 4
+
+    pari_printf("x[%d] = %Ps is not invertible!\n", i, gel(x,i));
+ at eprog\noindent
+Here \kbd{i} is an \kbd{int}, \kbd{x} a \kbd{GEN} which is not a leaf
+(presumably a vector, or a polynomial) and this would insert the value of its
+$i$-th \kbd{GEN} component: \kbd{gel(x,i)}.
+
+\noindent Simple but useful variants to \kbd{pari\_printf} are
+
+\fun{void}{output}{GEN x} prints \kbd{x} in raw format, followed by a
+newline and a buffer flush. This is more or less equivalent to
+\bprog
+    pari_printf("%Ps\n", x);
+    pari_flush();
+ at eprog
+
+\fun{void}{outmat}{GEN x} as above except if $x$ is a \typ{MAT}, in which
+case a multi-line display is used to display the matrix. This is prettier for
+small dimensions, but quickly becomes unreadable and cannot be pasted and
+reused for input. If all entries of $x$ are small integers, you may use the
+recursive features of \kbd{\%Pd} and obtain the same (or better) effect with
+\bprog
+    pari_printf("%Pd\n", x);
+    pari_flush();
+ at eprog\noindent
+A variant like \kbd{"\%5Pd"} would improve alignment by imposing
+5 chars for each coefficient. Similarly if all entries are to be converted to
+floats, a format like \kbd{"\%5.1Pf"} could be useful.
+
+
+These functions write on (PARI's idea of) standard output, and must be used
+if you want your functions to interact nicely with \kbd{gp}. In most
+programs, this is not a concern and it is more flexible to write to an
+explicit \kbd{FILE*}, or to recover a character string:
+
+\fun{void}{pari_fprintf}{FILE *file, const char *fmt, ...} writes the
+remaining arguments to stream \kbd{file} according to the format
+specification \kbd{fmt}.
+
+\fun{char*}{pari_sprintf}{const char *fmt, ...} produces a string from the
+remaining arguments, according to the PARI format \kbd{fmt} (see \tet{printf}).
+This is the \kbd{libpari} equivalent of \tet{Strprintf}, and returns a
+\kbd{malloc}'ed string, which must be freed by the caller. Note that contrary
+to the analogous \tet{sprintf} in the \kbd{libc} you do not provide a buffer
+(leading to all kinds of buffer overflow concerns); the function provided is
+actually closer to the GNU extension \kbd{asprintf}, although the latter has
+a different interface.
+
+Simple variants of \tet{pari_sprintf} convert a \kbd{GEN} to a
+\kbd{malloc}'ed ASCII string, which you must still \kbd{free} after use:
+
+\fun{char*}{GENtostr}{GEN x}, using the current default output format
+(\kbd{prettymat} by default).
+
+\fun{char*}{GENtoTeXstr}{GEN x}, suitable for inclusion in a \TeX\ file.
+
+
+Note that we have \tet{va_list} analogs of the functions of \kbd{printf} type
+seen so far:
+
+\fun{void}{pari_vprintf}{const char *fmt, va_list ap}
+
+\fun{void}{pari_vfprintf}{FILE *file, const char *fmt, va_list ap}
+
+\fun{char*}{pari_vsprintf}{const char *fmt, va_list ap}
+
+\subsec{Errors}\sidx{error}\kbdsidx{e_MISC}
+
+\noindent
+If you want your functions to issue error messages, you can use the general
+error handling routine \tet{pari_err}. The basic syntax is
+%
+\bprog
+  pari_err(e_MISC, "error message");
+ at eprog\noindent
+This prints the corresponding error message and exit the program (in
+library mode; go back to the \kbd{gp} prompt otherwise).\label{se:err} You can
+also use it in the more versatile guise
+\bprog
+  pari_err(e_MISC, format, ...);
+ at eprog\noindent
+where \kbd{format} describes the format to use to write the remaining
+operands, as in the \tet{pari_printf} function. For instance:
+\bprog
+  pari_err(e_MISC, "x[%d] = %Ps is not invertible!", i, gel(x,i));
+ at eprog\noindent
+The simple syntax seen above is just a special case with a constant format
+and no remaining arguments. The general syntax is
+
+\fun{void}{pari_err}{numerr,...}
+
+\noindent where \kbd{numerr} is a codeword which specifies the error class
+and what to do with the remaining arguments and what message to print.
+For instance, if $x$ is a \kbd{GEN} with internal type \typ{STR}, say,
+\kbd{pari\_err(e\_TYPE,"extgcd", $x$)} prints the message:
+\bprog
+    ***   incorrect type in extgcd (t_STR),
+ at eprog\noindent See \secref{se:errors} for details. In the libpari code
+itself, the general-purpose \kbd{e\_MISC} is used sparingly: it is so
+flexible that the corresponding error contexts (\typ{ERROR}) become hard to
+use reliably. Other more rigid error types are generally more useful: for
+instance the error context associated to the \kbd{e\_TYPE} exception above is
+precisely documented and contains \kbd{"extgcd"} and $x$ (not only its type)
+as readily available components.
+
+\subsec{Warnings}
+
+\noindent To issue a warning, use
+
+\fun{void}{pari_warn}{warnerr,...}
+In that case, of course, we do \emph{not} abort the computation, just print
+the requested message and go on. The basic example is
+%
+\bprog
+    pari_warn(warner, "Strategy 1 failed. Trying strategy 2")
+ at eprog\noindent
+which is the exact equivalent of \kbd{pari\_err(e\_MISC,...)} except that
+you certainly do not want to stop the program at this point, just inform the
+user that something important has occurred; in particular, this output would be
+suitably highlighted under \kbd{gp}, whereas a simple \kbd{printf} would not.
+
+The valid \emph{warning} keywords are \tet{warner} (general), \tet{warnprec}
+(increasing precision), \tet{warnmem} (garbage collecting) and \tet{warnfile}
+(error in file operation), used as follows:
+\bprog
+    pari_warn(warnprec, "bnfinit", newprec);
+    pari_warn(warnmem,  "bnfinit");
+    pari_warn(warnfile, "close", "afile");  /* error when closing "afile" */
+ at eprog
+
+\subsec{Debugging output}\sidx{debugging}\sidx{format}\label{se:dbg_output}
+
+For debugging output, you can use the standard output
+functions, \tet{output} and \tet{pari_printf} mainly. Corresponding to the
+\kbd{gp} metacommand \kbd{\b x}, you can also output the \idx{hexadecimal
+tree} associated to an object:
+
+\fun{void}{dbgGEN}{GEN x, long nb = -1}, displays the recursive structure of
+\kbd{x}. If $\kbd{nb} = -1$, the full structure is printed, otherwise
+the leaves (non-recursive components) are truncated to \kbd{nb} words.
+
+\noindent The function \tet{output} is vital under debuggers, since none of
+them knows how to print PARI objects by default. Seasoned PARI developers
+add the following \kbd{gdb} macro to their \kbd{.gdbinit}:
+\bprog
+  define i
+    call output((GEN)$arg0)
+  end
+ at eprog\noindent
+Typing \kbd{i x} at a breakpoint in \kbd{gdb} then prints the value of the
+\kbd{GEN} \kbd{x} (provided the optimizer has not put it into a register, but
+it is rarely a good idea to debug optimized code).
+
+\noindent
+The global variables \teb{DEBUGLEVEL} and \teb{DEBUGMEM} (corresponding
+to the default \teb{debug} and \teb{debugmem}, see \secref{se:defaults})
+are used throughout the PARI code to govern the amount of diagnostic and
+debugging output, depending on their values. You can use them to debug your
+own functions, especially if you \tet{install} the latter under \kbd{gp}
+(see \secref{se:install}).
+
+\fun{void}{dbg_pari_heap}{void} print debugging statements about the PARI
+stack, heap, and number of variables used. Corresponds to \kbd{\bs s}
+under gp.
+
+\subsec{Timers and timing output}
+
+\noindent To handle timings in a reentrant way, PARI defines a dedicated data
+type, \tet{pari_timer}, together with the following methods:
+
+\fun{void}{timer_start}{pari_timer *T} start (or reset) a timer.
+
+\fun{long}{timer_delay}{pari_timer *T} returns the number of milliseconds
+elapsed since the timer was last reset. Resets the timer as a side effect.
+
+\fun{long}{timer_get}{pari_timer *T} returns the number of milliseconds
+elapsed since the timer was last reset. Does \emph{not} reset the timer.
+
+\fun{long}{timer_printf}{pari_timer *T, char *format,...} This diagnostics
+function is equivalent to the following code
+\bprog
+  err_printf("Time ")
+  ... prints remaining arguments according to format ...
+  err_printf(": %ld", timer_delay(T));
+ at eprog\noindent Resets the timer as a side effect.
+
+\noindent They are used as follows:
+\bprog
+  pari_timer T;
+  timer_start(&T); /* initialize timer */
+  ...
+  printf("Total time: %ldms\n", timer_delay(&T));
+ at eprog\noindent
+or
+\bprog
+  pari_timer T;
+  timer_start(&T);
+  for (i = 1; i < 10; i++) {
+    ...
+    timer_printf(&T, "for i = %ld (L[i] = %Ps)", i, gel(L,i));
+  }
+ at eprog
+
+The following functions provided the same functionality, in a
+non-reentrant way, and are now deprecated.
+
+\fun{long}{timer}{void}
+
+\fun{long}{timer2}{void}
+
+\fun{void}{msgtimer}{const char *format, ...}
+
+The following function implements \kbd{gp}'s timer and should not be used in
+libpari programs:
+\fun{long}{gettime}{void} equivalent to \tet{timer_delay}$(T)$ associated to a
+private timer $T$.
+
+\section{Iterators, Numerical integration, Sums, Products}
+\subsec{Iterators}
+Since it is easier to program directly simple loops in library mode, some GP
+iterators are mainly useful for GP programming. Here are the others:
+
+\item \tet{fordiv} is a trivial iteration over a list produced by
+\tet{divisors}.
+
+\item \tet{forell} and \tet{forsubgroup} are currently not implemented as an
+iterator but as a procedure with callbacks.
+
+\fun{void}{forell}{void *E, long fun(void*, GEN), GEN a, GEN b}
+goes through the same curves as \tet{forell(ell,a,b,)}, calling
+\tet{fun(E, ell)} for each curve \kbd{ell}, stopping if \kbd{fun} returns a
+non-zero value.
+
+\fun{void}{forsubgroup}{void *E, long fun(void*, GEN), GEN G, GEN B}
+goes through the same subgroups as \tet{forsubgroup(H = G, B,)}, calling
+\tet{fun(E, H)} for each subgroup $H$, stopping if \kbd{fun} returns a
+non-zero value.
+
+\item \tet{forprime}, for which we refer you to the next subsection.
+
+\item \tet{forcomposite}, we provide an iterator over composite integers:
+
+\fun{int}{forcomposite}{forcomposite_t *T, GEN a, GEN b} initialize an
+iterator $T$ over composite integers in $[a,b]$; over composites $\geq a$ if
+$b = \kbd{NULL}$. Return $0$ if the range is known to be empty from the start
+(as if $b < a$ or $b < 0$), and return $1$ otherwise.
+
+\fun{GEN}{forcomposite_next}{forcomposite_t *T} returns the next composite in
+the range, assuming that $T$ was initialized by \tet{forcomposite_init}.
+
+\item \tet{forvec}, for which we provide a convenient iterator. To
+initialize the analog of \kbd{forvec(X = v, ..., flag)}, call
+
+\fun{int}{forvec_init}{forvec_t *T, GEN v, long flag}
+initialize an iterator $T$ over the vectors generated by
+\kbd{forvec(X = $v$,..., flag)}. This returns $0$ if this vector list is
+empty, and $1$ otherwise.
+
+\fun{GEN}{forvec_next}{forvec_t *T} returns the next element in the
+\kbd{forvec} sequence, or \kbd{NULL} if we are done. The return value must be
+used immediately or copied since the next call to the iterator destroys it:
+the relevant vector is updated in place. The iterator works hard to not
+use up PARI stack, and is more efficient when all lower bounds in the
+initialization vector $v$ are integers. In that case, the cost is linear in
+the number of tuples enumerated, and you can expect to run over more than
+$10^9$ tuples per minute. If speed is critical and all integers involved
+would fit in $C$ \kbd{long}s, write a simple direct backtracking algorithm
+yourself.
+
+\item \tet{forpart} is a variant of \kbd{forvec} which iterates over
+  partitions. See the documentation of the \kbd{forpart} GP function for
+  details. This function is available as a loop with callbacks:
+
+  \fun{void}{forpart}{void *data, long (*call)(void*,GEN), long k, GEN a, GEN n}
+
+  \noindent It is also available as an iterator:
+
+  \fun{void}{forpart_init}{forpart_t *T, long k, GEN a, GEN n} initializes an
+  iterator over the partitions of $k$, with length restricted by $n$,
+  and components restricted by $a$, either of which can be set to \kbd{NULL}
+  to run without restriction.
+
+  \fun{GEN}{forpart_next}{forpart_t *T} returns the next partition, or
+  \kbd{NULL} when all partitions have been exhausted.
+
+  \fun{GEN}{forpart_prev}{forpart_t *T}returns the previous partition, or
+  \kbd{NULL} when all partitions have been exhausted.
+
+  You may \emph{not} mix calls to \tet{forpart_next} and \tet{forpart_prev}:
+  the first one called determines the ordering used to iterate over the
+  partitions; you can not go back since the \tet{forpart_t} structure is used
+  in incompatible ways.
+
+\subsec{Iterating over primes}\label{se:primeiter}
+
+The library provides a high-level iterator, which stores its (private) data
+in a \kbd{struct} \tet{forprime_t} and runs over arbitrary ranges of primes,
+without ever overflowing.
+
+The iterator has two flavors, one providing the successive primes as
+\kbd{ulong}s, the other as \kbd{GEN}. They are initialized as follows,
+where we expect to run over primes $\geq a$ and $\leq b$:
+
+\fun{int}{forprime_init}{forprime_t *T, GEN a, GEN b} for the \kbd{GEN}
+variant, where $b = \kbd{NULL}$ means $+\infty$.
+
+\fun{int}{u_forprime_init}{forprime_t *T, ulong a, ulong b} for the
+\kbd{ulong} variant, where $b = \kbd{ULONG\_MAX}$ means we will run through
+all primes representable in a \kbd{ulong} type.
+
+Both variant return $1$ on success, and $0$ if the iterator would run over an
+empty interval (if $a > b$, for instance). They allocate the \tet{forprime_t}
+data structure on the PARI stack.
+
+\noindent The successive primes are then obtained using
+
+\fun{GEN}{forprime_next}{forprime_t *T}, returns \kbd{NULL} if no more primes
+are available in the interval.
+
+\fun{ulong}{u_forprime_next}{forprime_t *T}, returns $0$ if no more primes
+are available in the interval.
+
+These two functions leave alone the PARI stack, and write their state
+information in the preallocated \tet{forprime_t} struct. The typical usage is
+thus:
+\bprog
+  forprime_t T;
+  GEN p;
+  pari_sp av = avma, av2;
+
+  forprime_init(&T, gen_2, stoi(1000));
+  av2 = avma;
+  while ( (p = forprime_next(&T)) )
+  {
+    ...
+    if ( prime_is_OK(p) ) break;
+    avma = av2; /* delete garbage accumulated in this iteration */
+  }
+  avma = av; /* delete all */
+ at eprog\noindent Of course, the final \kbd{avma = av} could be replaced
+by a \kbd{gerepile} call. Beware that swapping the
+\kbd{av2 = avma} and \tet{forprime_init} call would be incorrect: the
+first \kbd{avma = av2} would delete the \tet{forprime_t} structure!
+
+\subsec{Numerical analysis}
+
+Numerical routines code a function (to be integrated, summed, zeroed, etc.)
+with two parameters named
+\bprog
+  void *E;
+  GEN (*eval)(void*, GEN)
+ at eprog\noindent
+The second is meant to contain all auxiliary data needed by your function.
+The first is such that \kbd{eval(x, E)} returns your function evaluated at
+\kbd{x}. For instance, one may code the family of functions
+$f_t: x \to (x+t)^2$ via
+\bprog
+GEN fun(void *t, GEN x) { return gsqr(gadd(x, (GEN)t)); }
+ at eprog\noindent
+One can then integrate $f_1$ between $a$ and $b$ with the call
+\bprog
+intnum((void*)stoi(1), &fun, a, b, NULL, prec);
+ at eprog\noindent
+Since you can set \kbd{E} to a pointer to any \kbd{struct} (typecast to
+\kbd{void*}) the above mechanism handles arbitrary functions. For simple
+functions without extra parameters, you may set \kbd{E = NULL} and ignore
+that argument in your function definition.
+
+\section{Catching exceptions}
+
+\subsec{Basic use}
+
+PARI provides a mechanism to trap exceptions generated via \kbd{pari\_err}
+using the \tet{pari_CATCH} construction. The basic usage is as follows
+\bprog
+ pari_CATCH(err_code) {
+   @com recovery branch
+ }
+ pari_TRY {
+   @com main branch
+ }
+ pari_ENDCATCH
+ at eprog\noindent This fragment executes the main branch, then the recovery
+branch \emph{if} exception \kbd{err\_code} is thrown, e.g. \kbd{e\_TYPE}.
+See \secref{se:errors} for the description of all error classes.
+The special error code \tet{CATCH_ALL} is available to catch all errors.
+
+One can replace the \tet{pari_TRY} keyword by \tet{pari_RETRY}, in which case
+once the recovery branch is run, we run the main branch again, still catching
+the same exceptions.
+
+\misctitle{Restrictions}
+
+\item Such constructs can be nested without adverse effect, the innermost
+handler catching the exception.
+
+\item It is \emph{valid} to leave either branch using \tet{pari_err}.
+
+\item It is \emph{invalid} to use C flow control instructions (\kbd{break},
+\kbd{continue}, \kbd{return}) to directly leave either branch without seeing
+the \tet{pari_ENDCATCH} keyword. This would leave an invalid structure in the
+exception handler stack, and the next exception would crash.
+
+\item In order to leave using \kbd{break}, \kbd{continue} or \kbd{return},
+one must precede the keyword by a call to
+
+\fun{void}{pari_CATCH_reset}{} disable the current handler, allowing to leave
+without adverse effect.
+
+\subsec{Advanced use}
+
+In the recovery branch, the exception context can be examined via the
+following helper routines:
+
+\fun{GEN}{pari_err_last}{} returns the exception context, as a \typ{ERROR}.
+The exception $E$ returned by \tet{pari_err_last} can be rethrown, using
+\bprog
+  pari_err(0, E);
+ at eprog
+
+\fun{long}{err_get_num}{GEN E} returns the error symbolic name. E.g
+\kbd{e\_TYPE}.
+
+\fun{GEN}{err_get_compo}{GEN E, long i} error $i$-th component, as documented
+in \secref{se:errors}.
+
+\noindent For instance
+\bprog
+ pari_CATCH(CATCH_ALL) { /* catch everything */
+    GEN x, E = pari_err_last();
+    long code = err_get_num(E);
+    if (code != e_INV) pari_err(0, E); /* unexpected error, rethrow */
+    x = err_get_compo(E, 2);
+    /* e_INV has two components, 1: function name 2: non-invertible x */
+    if (typ(x) != t_INTMOD) pari_err(0, E); /* unexpected type, rethrow */
+    pari_CATCH_reset();
+    return x; /* leave ! */
+    @com @dots
+ } pari_TRY {
+   @com main branch
+ }
+ pari_ENDCATCH
+ at eprog
+
+\section{A complete program}
+\label{se:prog}
+
+\noindent
+Now that the preliminaries are out of the way, the best way to learn how to
+use the library mode is to study a detailed example. We want to write a
+program which computes the gcd of two integers, together with the Bezout
+coefficients. We shall use the standard quadratic algorithm which is not
+optimal but is not too far from the one used in the PARI function
+\teb{bezout}.
+
+Let $x,y$ two integers and initially
+$ \pmatrix{s_x & s_y \cr t_x & t_y } =
+  \pmatrix{1 & 0 \cr 0 & 1}$, so that
+$$ \pmatrix{s_x & s_y \cr
+            t_x & t_y }
+   \pmatrix{x \cr y } =
+   \pmatrix{x \cr y }.
+$$
+To apply the ordinary Euclidean algorithm to the right hand side,
+multiply the system from the left by
+$ \pmatrix{0 & 1 \cr 1 & -q }$,
+with $q = \kbd{floor}(x / y)$. Iterate until $y = 0$ in the right hand side,
+then the first line of the system reads
+$$ s_x x + s_y y = \gcd(x,y).$$
+In practice, there is no need to update $s_y$ and $t_y$ since
+$\gcd(x,y)$ and $s_x$ are enough to recover $s_y$. The following program
+is now straightforward. A couple of new functions appear in there, whose
+description can be found in the technical reference manual in Chapter 5,
+but whose meaning should be clear from their name and the context.
+
+This program can be found in \kbd{examples/extgcd.c} together with a
+proper \kbd{Makefile}. You may ignore the first comment
+\bprog
+/*
+GP;install("extgcd", "GG&&", "gcdex", "./libextgcd.so");
+*/
+ at eprog\noindent which instruments the program so that \kbd{gp2c-run extgcd.c}
+can import the \kbd{extgcd()} routine into an instance of the \kbd{gp}
+interpreter (under the name \kbd{gcdex}). See the \kbd{gp2c} manual for
+details.
+\newpage
+
+\bprogfile{../examples/extgcd.c}
+
+\noindent For simplicity, the inner loop does not include any garbage
+collection, hence memory use is quadratic in the size of the inputs instead
+of linear. Here is a better version of that loop:
+\bprog
+  pari_sp av = avma, lim = stack_lim(av,1);
+  ...
+  while (!gequal0(b))
+  {
+    GEN r, q = dvmdii(a, b, &r), v = vx;
+
+    vx = subii(ux, mulii(q, vx));
+    ux = v; a = b; b = r;
+    if (low_stack(lim, stack_lim(av,1)))
+      gerepileall(av, 4, &a, &b, &ux, &vx);
+  }
+ at eprog
+\newpage
diff --git a/doc/usersch5.tex b/doc/usersch5.tex
new file mode 100644
index 0000000..34b4acb
--- /dev/null
+++ b/doc/usersch5.tex
@@ -0,0 +1,11057 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Technical Reference Guide: the basics}
+
+In the following chapters, we describe all public low-level functions of the
+PARI library. These include specialized functions for handling all the PARI
+types. Simple higher level functions, such as arithmetic or transcendental
+functions, are described in Chapter~3 of the GP user's manual; we will
+eventually see more general or flexible versions in the chapters to come. A
+general introduction to the major concepts of PARI programming can be found
+in Chapter~4, which you should really read first.
+
+We shall now study specialized functions, more efficient than the library
+wrappers, but sloppier on argument checking and damage control; besides
+speed, their main advantage is to give finer control about the inner
+workings of generic routines, offering more options to the programmer.
+
+\misctitle{Important advice} Generic routines eventually call lower level
+functions. Optimize your algorithms first, not overhead and conversion costs
+between PARI routines. For generic operations, use generic routines first;
+do not waste time looking for the most specialized one available unless you
+identify a genuine bottleneck, or you need some special behavior the generic
+routine does not offer. The PARI source code is part of the documentation;
+look for inspiration there.\smallskip
+
+The type \kbd{long} denotes a \tet{BITS_IN_LONG}-bit signed long integer (32
+or 64 bits). The type \tet{ulong} is defined as \kbd{unsigned long}. The word
+\emph{stack} always refer to the PARI stack, allocated through an initial
+\kbd{pari\_init} call. Refer to Chapters 1--2 and~4 for general background.
+\kbdsidx{BIL}
+
+We shall often refer to the notion of \tev{shallow} function, which means that
+some components of the result may point to components of the input, which is
+more efficient than a \emph{deep} copy (full recursive copy of the object
+tree). Such outputs are not suitable for \kbd{gerepileupto} and particular
+care must be taken when garbage collecting objects which have been input to
+shallow functions: corresponding outputs also become invalid and should no
+longer be accessed.
+
+A function is \emph{not stack clean} if it leaves intermediate data on the
+stack besides its output, for efficiency reasons.
+
+\section{Initializing the library}
+
+The following functions enable you to start using the PARI functions
+in a program, and cleanup without exiting the whole program.
+
+\subsec{General purpose}
+
+\fun{void}{pari_init}{size_t size, ulong maxprime} initialize the
+library, with a stack of \kbd{size} bytes and a prime table
+up to the maximum of \kbd{maxprime} and $2^{16}$. Unless otherwise
+mentioned, no PARI function will function properly before such an
+initialization.
+
+\fun{void}{pari_close}{void} stop using the library (assuming it was
+initialized with \kbd{pari\_init}) and frees all allocated objects.
+
+\subsec{Technical functions}\label{se:pari_init_tech}
+
+\fun{void}{pari_init_opts}{size_t size, ulong maxprime, ulong opts} as
+\kbd{pari\_init}, more flexible. \kbd{opts} is a mask of flags
+among the following:
+
+  \kbd{INIT\_JMPm}: install PARI error handler. When an exception is
+raised, the program is terminated with \kbd{exit(1)}.
+
+  \kbd{INIT\_SIGm}: install PARI signal handler.
+
+  \kbd{INIT\_DFTm}: initialize the \kbd{GP\_DATA} environment structure.
+This one \emph{must} be enabled once. If you close pari, then restart it,
+you need not reinitialize \kbd{GP\_DATA}; if you do not, then old values are
+restored.
+
+  \kbd{INIT\_noPRIMEm}: do not compute the prime table (ignore the
+  \kbd{maxprime} argument). The user \emph{must} call
+  \tet{initprimetable} later.
+
+  \kbd{INIT\_noIMTm}: (technical, see \kbd{pari\_mt\_init} in the Developer's
+Guide for detail). Do not call \tet{pari_mt_init} to initialize the
+multi-thread engine. If this flag is set, \kbd{pari\_mt\_init()} will need to
+be called manually. See \kbd{examples/pari-mt.c} for an example.
+
+\fun{void}{pari_close_opts}{ulong init_opts} as \kbd{pari\_close},
+for a library initialized with a mask of options using
+\kbd{pari\_init\_opts}. \kbd{opts} is a mask of flags among
+
+  \kbd{INIT\_SIGm}: restore \kbd{SIG\_DFL} default action for signals
+tampered with by PARI signal handler.
+
+  \kbd{INIT\_DFTm}: frees the \kbd{GP\_DATA} environment structure.
+
+  \kbd{INIT\_noIMTm}: (technical, see \kbd{pari\_mt\_init} in the Developer's
+Guide for detail). Do not call \tet{pari_mt_close} to close the multi-thread
+engine.
+
+\fun{void}{pari_sig_init}{void (*f)(int)} install the signal handler \kbd{f}
+(see \kbd{signal(2)}): the signals \kbd{SIGBUS}, \kbd{SIGFPE}, \kbd{SIGINT},
+\kbd{SIGBREAK}, \kbd{SIGPIPE} and \kbd{SIGSEGV} are concerned.
+
+\fun{void}{pari_stackcheck_init}{void *stackbase} controls the system stack
+exhaustion checking code in the GP interpreter. This should be used when the
+system stack base address change or when the address seen by \kbd{pari\_init}
+is too far from the base address. If \kbd{stackbase} is \kbd{NULL}, disable the
+check, else set the base address to \kbd{stackbase}. It is normally used this
+way
+\bprog
+int thread_start (...)
+{
+  long first_item_on_the_stack;
+  ...
+  pari_stackcheck_init(&first_item_on_the_stack);
+}
+ at eprog
+
+\fun{int}{pari_daemon}{void} fork a PARI daemon, detaching from the main
+process group. The function returns 1 in the parent, and 0 in the
+forked son.
+
+\subsec{Notions specific to the GP interpreter}
+
+An \kbd{entree} is the generic object associated to an identifier (a name)
+in GP's interpreter, be it a built-in or user function, or a variable. For
+a function, it has at least the following fields:
+
+  \kbd{char *name}: the name under which the interpreter knows us.
+
+  \kbd{void *value}:  a pointer to the C function to call.
+
+  \kbd{long menu}: an integer from 1 to 11 (to which group of function
+                    help do we belong).
+
+  \kbd{char *code}: the prototype code.
+
+  \kbd{char *help}: the help text for the function.
+
+A routine in GP is described to the analyzer by an \kbd{entree}
+structure. Built-in PARI routines are grouped in \emph{modules}, which
+are arrays of \kbd{entree} structs, the last of which satisfy
+\kbd{name = NULL} (sentinel).
+
+There are currently five modules in PARI/GP: general functions
+(\tet{functions_basic}, known to \kbd{libpari}), gp-specific functions
+(\tet{functions_gp}), gp-specific highlevel functions
+(\tet{functions_highlevel}), and two modules of obsolete functions. The
+function \kbd{pari\_init} initializes the interpreter and declares all
+symbols in \kbd{functions\_basic}. You may declare further functions on a
+case by case basis or as a whole module using
+
+\fun{void}{pari_add_function}{entree *ep} adds a single routine to the
+table of symbols in the interpreter. It assumes \kbd{pari\_init} has been
+called.
+
+\fun{void}{pari_add_module}{entree *mod} adds all the routines in module
+\kbd{mod} to the table of symbols in the interpreter. It assumes
+\kbd{pari\_init} has been called.
+
+\noindent For instance, gp implements a number of private routines, which
+it adds to the default set via the calls
+\bprog
+  pari_add_module(functions_gp);
+  pari_add_module(functions_highlevel);
+ at eprog
+
+\fun{void}{pari_add_oldmodule}{entree *mod} adds all the routines in module
+\kbd{mod} to the table of symbols in the interpreter when running in
+"PARI 1.xx compatible" mode (see \kbd{default(compatible)}) . It assumes
+that
+\kbd{pari\_init} has been called.
+
+A GP \kbd{default} is likewise associated to a helper routine, that is run
+when the value is consulted, or changed by \tet{default0} or \tet{setdefault}.
+Such routines are grouped into modules: \tet{functions_default} containing all
+defaults that make sense in libpari context, \tet{functions_gp_rl_default}
+containing defaults that are \kbd{gp}-specific and do not make sense unless
+we use \kbd{libreadline}, and \tet{functions_gp_default} containing all other
+\kbd{gp}-specific defaults.
+
+\fun{void}{pari_add_defaults_module}{entree *mod} adds all the defaults in
+module \kbd{mod} to the interpreter. It assumes that \kbd{pari\_init} has
+been called. From this point on, all defaults in module \kbd{mod} are known
+to \tet{setdefault} and friends.
+
+\subsec{Public callbacks}
+
+The \kbd{gp} calculator associates elaborate functions (for instance the
+break loop handler) to the following callbacks, and so can you:
+
+\doc{cb_pari_ask_confirm}{void (*cb_pari_ask_confirm)(const char *s)}
+initialized to \kbd{NULL}. Called with argument $s$ whenever PARI wants
+confirmation for action $s$, for instance in \tet{secure} mode.
+
+\doc{cb_pari_handle_exception}{extern int (*cb_pari_handle_exception)(long)}
+initialized to \kbd{NULL}. If not \kbd{NULL}, this routine is called with
+argument $-1$ on \kbd{SIGINT}, and argument \kbd{err} on error \kbd{err}. If
+it returns a non-zero value, the error or signal handler returns, in effect
+further ignoring the error or signal, otherwise it raises a fatal error.
+
+\doc{cb_pari_sigint}{void (*cb_pari_sigint)(void)}.
+Function called when we receive \kbd{SIGINT}. By default, raises
+\bprog
+  pari_err(e_MISC, "user interrupt");
+ at eprog
+
+\doc{cb_pari_pre_recover}{extern void (*cb_pari_err_recover)(long)}
+initialized to \kbd{NULL}. If not \kbd{NULL}, this routine is called just
+before PARI cleans up from an error. It is not required to return.  The error
+number is passed as argument, unless the PARI stack has been destroyed
+(\kbd{allocatemem}), in which case $-1$ is passed.
+
+\doc{cb_pari_err_recover}{extern void (*cb_pari_err_recover)(long)}
+initialized to \kbd{pari\_exit()}. This callback must not return.
+It is called after PARI has cleaned-up from an error. The error number is
+passed as argument, unless the PARI stack has been destroyed, in which case
+it is called with argument $-1$.
+
+\doc{cb_pari_whatnow}{int (*cb_pari_whatnow)(PariOUT *out, const char *s, int
+flag)} initialized to \kbd{NULL}. If not \kbd{NULL}, must check whether $s$
+existed in older versions of \kbd{pari} (the \kbd{gp} callback checks against
+\kbd{pari-1.39.15}). All output must be done via \kbd{out} methods.
+
+\item $\fl = 0$: should print verbosely the answer, including help text if
+available.
+
+\item $\fl = 1$: must return $0$ if the function did not change, and a
+non-$0$ result otherwise. May print a help message.
+
+\subsec{Configuration variables}
+
+\tet{pari_library_path}: If set, It should be a path to the libpari library.
+It is used by the function \tet{gpinstall} to locate the PARI library when
+searching for symbols.  This should only be useful on Windows.
+
+\misctitle{Utility function}
+
+\fun{void}{pari_ask_confirm}{const char *s} raise an error if the
+callback \tet{cb_pari_ask_confirm} is \kbd{NULL}. Otherwise
+calls
+\bprog
+  cb_pari_ask_confirm(s);
+ at eprog
+\subsec{Saving and restoring the GP context}
+
+\fun{void}{gp_context_save}{struct gp_context* rec} save the current GP
+context.
+
+\fun{void}{gp_context_restore}{struct gp_context* rec} restore a GP context.
+The new context must be an ancestor of the current context.
+
+\subsec{GP history}
+
+These functions allow to control the GP history (the \kbd{\%} operator).
+
+\fun{void}{pari_add_hist}{GEN x, long t} adds \kbd{x} as the last history
+entry; $t$ is the time we used to compute it.
+
+\fun{GEN}{pari_get_hist}{long p}, if $p>0$ returns entry of index $p$
+(i.e. \kbd{\%p}), else returns entry of index $n+p$ where $n$ is the
+index of the last entry (used for \kbd{\%}, \kbd{\%`}, \kbd{\%``}, etc.).
+
+\fun{long}{pari_get_histtime}{long p} as \tet{pari_get_hist},
+returning the time used to compute the history entry, instead of the entry
+itself.
+
+\fun{ulong}{pari_nb_hist}{void} return the index of the last entry.
+
+\section{Handling \kbd{GEN}s}
+\noindent Almost all these functions are either macros or inlined. Unless
+mentioned otherwise, they do not evaluate their arguments twice. Most of them
+are specific to a set of types, although no consistency checks are made:
+e.g.~one may access the \kbd{sign} of a \typ{PADIC}, but the result is
+meaningless.
+
+\subsec{Allocation}
+
+\fun{GEN}{cgetg}{long l, long t} allocates (the root of) a \kbd{GEN}
+of type $t$ and length $l$. Sets $z[0]$.
+
+\fun{GEN}{cgeti}{long l} allocates a \typ{INT} of length $l$ (including the
+2 codewords). Sets $z[0]$ only.
+
+\fun{GEN}{cgetr}{long l} allocates a \typ{REAL} of length $l$ (including the
+2 codewords). Sets $z[0]$ only.
+
+\fun{GEN}{cgetc}{long prec} allocates a \typ{COMPLEX} whose real and
+imaginary parts are \typ{REAL}s of length \kbd{prec}.
+
+\fun{GEN}{cgetg_copy}{GEN x, long *lx} fast version of \kbd{cgetg}:
+allocate a \kbd{GEN} with the same type and length as $x$, setting \kbd{*lx}
+to \kbd{lg(x)} as a side-effect. (Only sets the first codeword.) This is
+a little faster than \kbd{cgetg} since we may reuse the bitmask in
+$x[0]$ instead of recomputing it, and we do not need to check that the
+length does not overflow the possibilities of the
+implementation (since an object with that length already exists). Note that
+\kbd{cgetg} with arguments known at compile time, as in
+\bprog
+  cgetg(3, t_INTMOD)
+ at eprog\noindent will be even faster since the compiler will directly perform
+all computations and checks.
+
+\fun{GEN}{vectrunc_init}{long l} perform \kbd{cgetg(l,t\_VEC)}, then
+set the length to $1$ and return the result. This is used to  implement
+vectors whose final length is easily bounded at creation time, that we intend
+to fill gradually using:
+
+\fun{void}{vectrunc_append}{GEN x, GEN y} assuming $x$ was allocated using
+\tet{vectrunc_init}, appends $y$ as the last element of $x$, which
+grows in the process. The function is shallow: we append $y$, not a copy;
+it is equivalent to
+\bprog
+  long lx = lg(x); gel(x,lx) = y; setlg(x, lx+1);
+ at eprog\noindent
+Beware that the maximal size of $x$ (the $l$ argument to \tet{vectrunc_init})
+is unknown, hence unchecked, and stack corruption will occur if we append
+more than $l-1$ elements to $x$. Use the safer (but slower)
+\kbd{shallowconcat} when $l$ is not easy to bound in advance.
+
+An other possibility is simply to allocate using \kbd{cgetg(l, t)} then fill
+the components as they become available: this time the downside is that we do
+not obtain a correct \kbd{GEN} until the vector is complete. Almost no PARI
+function will be able to operate on it.
+
+\fun{void}{vectrunc_append_batch}{GEN x, GEN y} successively apply
+\bprog
+  vectrunc_append(x, gel(y, i))
+ at eprog
+for all elements of the vector $y$.
+
+\fun{GEN}{vecsmalltrunc_init}{long l}
+
+\fun{void}{vecsmalltrunc_append}{GEN x, long t} analog to the above for a
+\typ{VECSMALL} container.
+
+\subsec{Length conversions}
+
+These routines convert a non-negative length to different units. Their
+behavior is undefined at negative integers.
+
+\fun{long}{ndec2nlong}{long x} converts a number of decimal digits to a number
+of words. Returns $ 1 + \kbd{floor}(x \times \B \log_2 10)$.
+
+\fun{long}{ndec2prec}{long x} converts a number of decimal digits to a number
+of codewords. This is equal to 2 + \kbd{ndec2nlong(x)}.
+
+\fun{long}{prec2ndec}{long x} converts a number of of codewords to a
+number of decimal digits.
+
+\fun{long}{nbits2nlong}{long x} converts a number of bits to a number of
+words. Returns the smallest word count containing $x$ bits, i.e $
+\kbd{ceil}(x / \B)$.
+
+\fun{long}{nbits2prec}{long x} converts a number of bits to a number of
+codewords. This is equal to 2 + \kbd{nbits2nlong(x)}.
+
+\fun{long}{nchar2nlong}{long x} converts a number of bytes to number of
+words. Returns the smallest word count containing $x$ bytes, i.e
+$\kbd{ceil}(x / \kbd{sizeof(long)})$.
+
+\fun{long}{bit_accuracy}{long x} converts a \typ{REAL} length into a number
+of significant bits. Returns $(x - 2)\B$.
+
+\fun{double}{bit_accuracy_mul}{long x, double y} returns $(x - 2)\B \times y$.
+
+\subsec{Read type-dependent information}
+
+\fun{long}{typ}{GEN x} returns the type number of~\kbd{x}. The header files
+included through \kbd{pari.h} define symbolic constants for the \kbd{GEN}
+types: \typ{INT} etc. Never use their actual numerical values. E.g to determine
+whether \kbd{x} is a \typ{INT}, simply check
+\bprog
+  if (typ(x) == t_INT) { }
+ at eprog\noindent
+The types are internally ordered and this simplifies the implementation of
+commutative binary operations (e.g addition, gcd). Avoid using the ordering
+directly, as it may change in the future; use type grouping functions
+instead (\secref{se:typegroup}).
+
+\fun{const char*}{type_name}{long t} given a type number \kbd{t} this routine
+returns a string containing its symbolic name. E.g \kbd{type\_name(\typ{INT})}
+returns \kbd{"\typ{INT}"}. The return value is read-only.
+
+\fun{long}{lg}{GEN x} returns the length of~\kbd{x} in \B-bit words.
+
+\fun{long}{lgefint}{GEN x} returns the effective length of the \typ{INT}
+\kbd{x} in \B-bit words.
+
+\fun{long}{signe}{GEN x} returns the sign ($-1$, 0 or 1) of~\kbd{x}. Can be
+used for \typ{INT}, \typ{REAL}, \typ{POL} and \typ{SER} (for the last two
+types, only 0 or 1 are possible).
+
+\fun{long}{gsigne}{GEN x} returns the sign of a real number $x$,
+valid for \typ{INT}, \typ{REAL} as \kbd{signe}, but also for \typ{FRAC}.
+Raise a type error if \kbd{typ(x)} is not among those three.
+
+\fun{long}{expi}{GEN x} returns the binary exponent of the real number equal
+to the \typ{INT}~\kbd{x}. This is a special case of \kbd{gexpo}.
+
+\fun{long}{expo}{GEN x} returns the binary exponent of the
+\typ{REAL}~\kbd{x}.
+
+\fun{long}{mpexpo}{GEN x} returns the binary exponent of the \typ{INT}
+or \typ{REAL}~\kbd{x}.
+
+\fun{long}{gexpo}{GEN x} same as \kbd{expo}, but also valid when \kbd{x}
+is not a \typ{REAL} (returns the largest exponent found among the components
+of \kbd{x}). When \kbd{x} is an exact~0, this returns
+\hbox{\kbd{-HIGHEXPOBIT}}, which is lower than any valid exponent.
+
+\fun{long}{valp}{GEN x} returns the $p$-adic valuation (for
+a \typ{PADIC}) or $X$-adic valuation (for a \typ{SER}, taken with respect to
+the main variable) of~\kbd{x}.
+
+\fun{long}{precp}{GEN x} returns the precision of the \typ{PADIC}~\kbd{x}.
+
+\fun{long}{varn}{GEN x} returns the variable number of the
+\typ{POL} or \typ{SER}~\kbd{x} (between 0 and \kbd{MAXVARN}).
+
+\fun{long}{gvar}{GEN x} returns the main variable number when any variable
+at all occurs in the composite object~\kbd{x} (the smallest variable number
+which occurs), and \tet{NO_VARIABLE} otherwise.
+
+\fun{long}{gvar2}{GEN x} returns the variable number for the ring over which
+$x$ is defined, e.g. if $x\in \Z[a][b]$ return (the variable number for)
+$a$. Return \tet{NO_VARIABLE} if $x$ has no variable or is not defined over a
+polynomial ring.
+
+\fun{long}{degpol}{GEN x} is a simple macro returning \kbd{lg(x) - 3}.
+This is the degree of the \typ{POL}~\kbd{x} with respect to its main
+variable, \emph{if} its leading coefficient is non-zero (a rational $0$ is
+impossible, but an inexact $0$ is allowed, as well as an exact modular $0$,
+e.g. \kbd{Mod(0,2)}). If $x$ has no coefficients (rational $0$ polynomial),
+its length is $2$ and we return the expected $-1$.
+
+\fun{long}{lgpol}{GEN x} is equal to \kbd{degpol(x) + 1}. Used to loop over
+the coefficients of a \typ{POL} in the following situation:
+\bprog
+    GEN xd = x + 2;
+    long i, l = lgpol(x);
+    for (i = 0; i < l; i++) foo( xd[i] ).
+ at eprog
+
+\fun{long}{precision}{GEN x} If \kbd{x} is of type \typ{REAL}, returns the
+precision of~\kbd{x}, namely the length of \kbd{x} in \B-bit words if \kbd{x}
+is not zero, and a reasonable quantity obtained from the exponent of \kbd{x}
+if \kbd{x} is numerically equal to zero. If \kbd{x} is of type
+\typ{COMPLEX}, returns the minimum of the precisions of the real and
+imaginary part. Otherwise, returns~0 (which stands for infinite precision).
+
+\fun{long}{lgcols}{GEN x} is equal to \kbd{lg(gel(x,1))}. This is the length
+of the columns of a \typ{MAT} with at least one column.
+
+\fun{long}{nbrows}{GEN x} is equal to \kbd{lg(gel(x,1))-1}. This is the number
+of rows of a \typ{MAT} with at least one column.
+
+\fun{long}{gprecision}{GEN x} as \kbd{precision} for scalars. Returns the
+lowest precision encountered among the components otherwise.
+
+\fun{long}{sizedigit}{GEN x} returns 0 if \kbd{x} is exactly~0. Otherwise,
+returns \kbd{\key{gexpo}(x)} multiplied by $\log_{10}(2)$. This gives a crude
+estimate for the maximal number of decimal digits of the components
+of~\kbd{x}.
+
+\subsec{Eval type-dependent information}
+These routines convert type-dependent information to bitmask to fill the
+codewords of \kbd{GEN} objects (see \secref{se:impl}). E.g for a
+\typ{REAL}~\kbd{z}:
+\bprog
+  z[1] = evalsigne(-1) | evalexpo(2)
+ at eprog
+Compatible components of a codeword for a given type can be OR-ed as above.
+
+\fun{ulong}{evaltyp}{long x} convert type~\kbd{x} to bitmask (first
+codeword of all \kbd{GEN}s)
+
+\fun{long}{evallg}{long x} convert length~\kbd{x} to bitmask (first
+codeword of all \kbd{GEN}s). Raise overflow error if \kbd{x} is so large that
+the corresponding length cannot be represented
+
+\fun{long}{_evallg}{long x} as \kbd{evallg} \emph{without} the overflow
+check.
+
+\fun{ulong}{evalvarn}{long x} convert variable number~\kbd{x} to bitmask
+(second codeword of \typ{POL} and \typ{SER})
+
+\fun{long}{evalsigne}{long x} convert sign~\kbd{x} (in $-1,0,1$) to bitmask
+(second codeword of \typ{INT}, \typ{REAL}, \typ{POL}, \typ{SER})
+
+\fun{long}{evalprecp}{long x} convert $p$-adic ($X$-adic) precision~\kbd{x}
+to bitmask (second codeword of \typ{PADIC}, \typ{SER}). Raise overflow error
+if \kbd{x} is so large that the corresponding precision cannot be
+represented.
+
+\fun{long}{_evalprecp}{long x} same as \kbd{evalprecp} \emph{without} the
+overflow check.
+
+\fun{long}{evalvalp}{long x} convert $p$-adic ($X$-adic) valuation~\kbd{x} to
+bitmask (second codeword of \typ{PADIC}, \typ{SER}). Raise overflow error if
+\kbd{x} is so large that the corresponding valuation cannot be represented.
+
+\fun{long}{_evalvalp}{long x} same as \kbd{evalvalp} \emph{without} the
+overflow check.
+
+\fun{long}{evalexpo}{long x} convert exponent~\kbd{x} to bitmask (second
+codeword of \typ{REAL}). Raise overflow error if \kbd{x} is so
+large that the corresponding exponent cannot be represented
+
+\fun{long}{_evalexpo}{long x} same as \kbd{evalexpo} \emph{without} the
+overflow check.
+
+\fun{long}{evallgefint}{long x} convert effective length~\kbd{x} to bitmask
+(second codeword \typ{INT}). This should be less or equal than the length
+of the \typ{INT}, hence there is no overflow check for the effective length.
+
+\subsec{Set type-dependent information}
+Use these functions and macros with extreme care since usually the
+corresponding information is set otherwise, and the components and further
+codeword fields (which are left unchanged) may not be compatible with the new
+information.
+
+\fun{void}{settyp}{GEN x, long s} sets the type number of~\kbd{x} to~\kbd{s}.
+
+\fun{void}{setlg}{GEN x, long s} sets the length of~\kbd{x} to~\kbd{s}. This
+is an efficient way of truncating vectors, matrices or polynomials.
+
+\fun{void}{setlgefint}{GEN x, long s} sets the effective length
+of the \typ{INT} \kbd{x} to~\kbd{s}. The number \kbd{s} must be less than or
+equal to the length of~\kbd{x}.
+
+\fun{void}{setsigne}{GEN x, long s} sets the sign of~\kbd{x} to~\kbd{s}.
+If \kbd{x} is a \typ{INT} or \typ{REAL}, \kbd{s} must be equal to $-1$, 0
+or~1, and if \kbd{x} is a \typ{POL} or \typ{SER}, \kbd{s} must be equal to 0
+or~1. No sanity check is made; in particular, setting the sign of a
+$0$ \typ{INT} to $\pm1$ creates an invalid object.
+
+\fun{void}{togglesign}{GEN x} sets the sign $s$ of~\kbd{x} to $-s$, in place.
+
+\fun{void}{togglesign_safe}{GEN *x} sets the $s$ sign of~\kbd{*x} to $-s$, in
+place, unless \kbd{*x} is one of the integer universal constants in which case
+replace \kbd{*x} by its negation (e.g.~replace \kbd{gen\_1} by \kbd{gen\_m1}).
+
+\fun{void}{setabssign}{GEN x} sets the sign $s$ of~\kbd{x} to $|s|$, in place.
+
+\fun{void}{affectsign}{GEN x, GEN y} shortcut for \kbd{setsigne(y, signe(x))}.
+No sanity check is made; in particular, setting the sign of a
+$0$ \typ{INT} to $\pm1$ creates an invalid object.
+
+\fun{void}{affectsign_safe}{GEN x, GEN *y} sets the sign of~\kbd{*y} to that
+of~\kbd{x}, in place, unless \kbd{*y} is one of the integer universal
+constants in which case replace \kbd{*y} by its negation if needed
+(e.g.~replace \kbd{gen\_1} by \kbd{gen\_m1} if \kbd{x} is negative). No other
+sanity check is made; in particular, setting the sign of a $0$
+\typ{INT} to $\pm1$ creates an invalid object.
+
+\fun{void}{normalize_frac}{GEN z} assuming $z$ is of the form \kbd{mkfrac(a,b)}
+with $b\neq 0$, make sure that $b > 0$ by changing the sign of $a$ in place if
+needed (use \kbd{togglesign}).
+
+\fun{void}{setexpo}{GEN x, long s} sets the binary exponent of the
+\typ{REAL}~\kbd{x} to \kbd{s}. The value \kbd{s} must be a 24-bit signed
+number.
+
+\fun{void}{setvalp}{GEN x, long s} sets the $p$-adic or $X$-adic valuation
+of~\kbd{x} to~\kbd{s}, if \kbd{x} is a \typ{PADIC} or a \typ{SER},
+respectively.
+
+\fun{void}{setprecp}{GEN x, long s} sets the $p$-adic precision of the
+\typ{PADIC}~\kbd{x} to~\kbd{s}.
+
+\fun{void}{setvarn}{GEN x, long s} sets the variable number of the \typ{POL}
+or \typ{SER}~\kbd{x} to~\kbd{s} (where $0\le \kbd{s}\le\kbd{MAXVARN}$).
+
+\subsec{Type groups}\label{se:typegroup}
+In the following functions, \kbd{t} denotes the type of a \kbd{GEN}.
+They used to be implemented as macros, which could evaluate their argument
+twice; \emph{no longer}: it is not inefficient to write
+\bprog
+  is_intreal_t(typ(x))
+ at eprog
+
+\fun{int}{is_recursive_t}{long t} \kbd{true} iff \kbd{t} is a recursive
+type (the non-recursive types are \typ{INT}, \typ{REAL},
+\typ{STR}, \typ{VECSMALL}). Somewhat contrary to intuition, \typ{LIST} is
+also non-recursive, ; see the Developer's guide for details.
+
+\fun{int}{is_intreal_t}{long t} \kbd{true} iff \kbd{t} is \typ{INT}
+or \typ{REAL}.
+
+\fun{int}{is_rational_t}{long t} \kbd{true} iff \kbd{t} is \typ{INT}
+or \typ{FRAC}.
+
+\fun{int}{is_vec_t}{long t} \kbd{true} iff \kbd{t} is \typ{VEC}
+or \typ{COL}.
+
+\fun{int}{is_matvec_t}{long t} \kbd{true} iff \kbd{t} is \typ{MAT}, \typ{VEC}
+or \typ{COL}.
+
+\fun{int}{is_scalar_t}{long t} \kbd{true} iff \kbd{t} is a scalar, i.e
+a \typ{INT},
+a \typ{REAL},
+a \typ{INTMOD},
+a \typ{FRAC},
+a \typ{COMPLEX},
+a \typ{PADIC},
+a \typ{QUAD},
+or
+a \typ{POLMOD}.
+
+\fun{int}{is_extscalar_t}{long t} \kbd{true} iff \kbd{t} is a scalar (see
+\kbd{is\_scalar\_t}) or \kbd{t} is \typ{POL}.
+
+\fun{int}{is_const_t}{long t} \kbd{true} iff \kbd{t} is a scalar which is not
+\typ{POLMOD}.
+
+\fun{int}{is_noncalc_t}{long t} true if generic operations (\kbd{gadd},
+\kbd{gmul}) do not make sense for $t$: corresponds to types
+\typ{LIST}, \typ{STR}, \typ{VECSMALL}, \typ{CLOSURE}
+
+\subsec{Accessors and components}\label{se:accessors}
+The first two functions return \kbd{GEN} components as copies on the stack:
+
+\fun{GEN}{compo}{GEN x, long n} creates a copy of the \kbd{n}-th true
+component (i.e.\ not counting the codewords) of the object~\kbd{x}.
+
+\fun{GEN}{truecoeff}{GEN x, long n} creates a copy of the coefficient of
+degree~\kbd{n} of~\kbd{x} if \kbd{x} is a scalar, \typ{POL} or \typ{SER},
+and otherwise of the \kbd{n}-th component of~\kbd{x}.
+\smallskip
+
+\noindent On the contrary, the following routines return the address of a
+\kbd{GEN} component. No copy is made on the stack:
+
+\fun{GEN}{constant_term}{GEN x} returns the address of the constant
+coefficient of \typ{POL}~\kbd{x}. By convention, a $0$ polynomial (whose
+\kbd{sign} is $0$) has \kbd{gen\_0} constant term.
+
+\fun{GEN}{leading_term}{GEN x} returns the address of the leading coefficient
+of \typ{POL}~\kbd{x}, i.e. the coefficient of largest index stored in the
+array representing $x$. This may be an inexact $0$. By convention, return
+\kbd{gen\_0} if the coefficient array is empty.
+
+\fun{GEN}{gel}{GEN x, long i} returns the address of the
+\kbd{x[i]} entry of~\kbd{x}. (\kbd{el} stands for element.)
+
+\fun{GEN}{gcoeff}{GEN x, long i, long j} returns the address of the
+\kbd{x[i,j]} entry of \typ{MAT}~\kbd{x}, i.e.~the coefficient at row~\kbd{i}
+and column~\kbd{j}.
+
+\fun{GEN}{gmael}{GEN x, long i, long j} returns the address of the
+\kbd{x[i][j]} entry of~\kbd{x}. (\kbd{mael} stands for multidimensional array
+element.)
+
+\fun{GEN}{gmael2}{GEN A, long x1, long x2} is an alias for \kbd{gmael}.
+Similar macros \tet{gmael3}, \tet{gmael4}, \tet{gmael5} are available.
+
+\section{Global numerical constants}
+These are defined in the various public PARI headers.
+
+\subsec{Constants related to word size}
+
+\noindent \kbd{long} $\tet{BITS_IN_LONG} = 2^{\tet{TWOPOTBITS_IN_LONG}}$:
+number of bits in a \kbd{long} (32 or 64).
+
+\noindent \kbd{long} \tet{BITS_IN_HALFULONG}: \kbd{BITS\_IN\_LONG} divided by
+$2$.
+
+\noindent \kbd{long} \tet{LONG_MAX}: the largest positive \kbd{long}.
+
+\noindent \kbd{ulong} \tet{ULONG_MAX}: the largest \kbd{ulong}.
+
+\noindent \kbd{long} \tet{DEFAULTPREC}:    the length (\kbd{lg}) of a
+\typ{REAL} with 64 bits of accuracy
+
+\noindent \kbd{long} \tet{MEDDEFAULTPREC}: the length (\kbd{lg}) of a
+\typ{REAL} with 128 bits of accuracy
+
+\noindent \kbd{long} \tet{BIGDEFAULTPREC}: the length (\kbd{lg}) of a
+\typ{REAL} with 192 bits of accuracy
+
+\noindent \kbd{ulong} \tet{HIGHBIT}: the largest power of $2$ fitting in an
+\kbd{ulong}.
+
+\noindent \kbd{ulong} \tet{LOWMASK}: bitmask yielding the least significant
+bits.
+
+\noindent \kbd{ulong} \tet{HIGHMASK}: bitmask yielding the most significant
+bits.
+
+\noindent The last two are used to implement the following convenience macros,
+returning half the bits of their operand:
+
+\fun{ulong}{LOWWORD}{ulong a} returns least significant bits.
+
+\fun{ulong}{HIGHWORD}{ulong a} returns most significant bits.
+
+\noindent Finally
+
+\fun{long}{divsBIL}{long n} returns the Euclidean quotient of $n$ by
+\kbd{BITS\_IN\_LONG} (with non-negative remainder).
+
+\fun{long}{remsBIL}{n} returns the (non-negative) Euclidean remainder of $n$
+by \kbd{BITS\_IN\_LONG}
+
+\fun{long}{dvmdsBIL}{long n, long *r}
+
+\fun{ulong}{dvmduBIL}{ulong n, ulong *r} sets $r$ to \kbd{remsBIL(n)}
+and returns \kbd{divsBIL(n)}.
+
+\subsec{Masks used to implement the \kbd{GEN} type}
+
+These constants are used by higher level macros, like \kbd{typ} or \kbd{lg}:
+
+\noindent \tet{EXPOnumBITS},
+\tet{LGnumBITS},
+\tet{SIGNnumBITS},
+\tet{TYPnumBITS},
+\tet{VALPnumBITS},
+\tet{VARNnumBITS}:
+number of bits used to encode \kbd{expo}, \kbd{lg}, \kbd{signe},
+\kbd{typ}, \kbd{valp}, \kbd{varn}.
+
+\noindent \tet{PRECPSHIFT},
+\tet{SIGNSHIFT},
+\tet{TYPSHIFT},
+\tet{VARNSHIFT}: shifts used to recover or encode \kbd{precp}, \kbd{varn},
+\kbd{typ}, \kbd{signe}
+
+\noindent \tet{CLONEBIT},
+\tet{EXPOBITS},
+\tet{LGBITS},
+\tet{PRECPBITS},
+\tet{SIGNBITS},
+\tet{TYPBITS},
+\tet{VALPBITS},
+\tet{VARNBITS}: bitmasks used to extract \kbd{isclone}, \kbd{expo}, \kbd{lg},
+\kbd{precp}, \kbd{signe}, \kbd{typ}, \kbd{valp}, \kbd{varn} from \kbd{GEN}
+codewords.
+
+\noindent \tet{MAXVARN}: the largest possible variable number.
+
+\noindent \tet{NO_VARIABLE}:  sentinel returned by \kbd{gvar(x)} when \kbd{x}
+does not contain any polynomial; has a lower priority than any valid variable
+number.
+
+\noindent \tet{HIGHEXPOBIT}: a power of $2$, one more that the largest possible
+exponent for a \typ{REAL}.
+
+\noindent \tet{HIGHVALPBIT}: a power of $2$, one more that the largest possible
+valuation for a \typ{PADIC} or a \typ{SER}.
+
+\subsec{$\log 2$, $\pi$}
+
+These are \kbd{double} approximations to useful constants:
+
+\noindent \tet{LOG2}: $\log 2$.
+
+\noindent \tet{LOG10_2}: $\log 2 / \log 10$.
+
+\noindent \tet{LOG2_10}: $\log 10 / \log 2$.
+
+\noindent \tet{PI}: $\pi$.
+
+\section{Iterating over small primes, low-level interface}
+\label{se:primetable}
+
+One of the methods used by the high-level prime iterator (see
+\secref{se:primeiter}), is a precomputed table. Its direct use is deprecated,
+but documented here.
+
+After \kbd{pari\_init(size, maxprime)}, a ``prime table'' is
+initialized with the successive \emph{differences} of primes up to (possibly
+just a little beyond) \kbd{maxprime}. The prime table occupies roughly
+$\kbd{maxprime}/\log(\kbd{maxprime})$ bytes in memory, so be sensible when
+choosing \kbd{maxprime}; it is $500000$ by default under \kbd{gp} and there
+is no real benefit in choosing a much larger value: the high-level
+iterator provide \emph{fast} access to primes up to the \emph{square}
+of \kbd{maxprime}. In any case, the implementation requires that
+$\tet{maxprime} < 2^{\B} - 2048$, whatever memory is available.
+
+PARI currently guarantees that the first 6547 primes, up to and including
+65557, are present in the table, even if you set \kbd{maxprime} to zero.
+in the \kbd{pari\_init} call.
+
+\noindent Some convenience functions:
+
+\fun{ulong}{maxprime}{} the largest prime computable using our prime table.
+
+\fun{void}{maxprime_check}{ulong B} raise an error if \kbd{maxprime()} is $< B$.
+
+After the following initializations (the names $p$ and \var{ptr} are
+arbitrary of course)
+\bprog
+byteptr ptr = diffptr;
+ulong p = 0;
+ at eprog
+\noindent calling the macro \tet{NEXT_PRIME_VIADIFF_CHECK}$(p, \var{ptr})$
+repeatedly will assign the successive prime numbers to $p$. Overrunning the
+prime table boundary will raise the error \tet{e_MAXPRIME}, which will just
+print the error message:
+
+\kbd{*** not enough precomputed primes, need primelimit \til $c$}
+
+\noindent (for some numerical value $c$), then abort the computation. The
+alternative macro \tet{NEXT_PRIME_VIADIFF} operates in the same way, but will
+omit that check, and is slightly faster. It should be used in the following
+way:
+%
+\bprog
+byteptr ptr = diffptr;
+ulong p = 0;
+
+if (maxprime() < goal) pari_err_MAXPRIME(goal); /*@Ccom not enough primes */
+while (p <= goal) /*@Ccom run through all primes up to \kbd{goal} */
+{
+  NEXT_PRIME_VIADIFF(p, ptr);
+  ...
+}
+ at eprog\noindent
+Here, we use the general error handling function \kbd{pari\_err} (see
+\secref{se:err}), with the codeword \kbd{e\_MAXPRIME}, raising the ``not enough
+primes'' error. This could be rewritten as
+\bprog
+maxprime_check(goal);
+while (p <= goal) /*@Ccom run through all primes up to \kbd{goal} */
+{
+  NEXT_PRIME_VIADIFF(p, ptr);
+  ...
+}
+ at eprog
+
+\fun{bytepr}{initprimes}{ulong maxprime, long *L, ulong *lastp}
+computes a (malloc'ed) ``prime table'', in fact a table of all prime
+differences for $p < \kbd{maxprime}$ (and possibly a little beyond). Set $L$
+to the table length (argument to \kbd{malloc}), and \var{lastp} to the last
+prime in the table.
+
+\fun{void}{initprimetable}{ulong maxprime} computes a prime table (of all prime
+differences for $p < \kbd{maxprime}$) and assign it to the global variable
+\kbd{diffptr}. Don't change \kbd{diffptr} directly, call this function
+instead. This calls \kbd{initprimes} and updates internal data recording the
+table size.
+
+\fun{ulong}{init_primepointer_geq}{ulong a, byteptr *pd}
+returns the smallest prime $p \geq a$, and sets \kbd{*pd} to the proper offset
+of \kbd{diffptr} so that \kbd{NEXT\_PRIME\_VIADIFF(p, *pd)} correctly
+returns \kbd{unextprime(p + 1)}.
+
+\fun{ulong}{init_primepointer_gt}{ulong a, byteptr *pd} returns the smallest
+prime $p > a$.
+
+\fun{ulong}{init_primepointer_leq}{ulong a, byteptr *pd} returns the largest
+prime $p \leq a$.
+
+\fun{ulong}{init_primepointer_lt}{ulong a, byteptr *pd} returns the largest
+prime $p < a$.
+
+\section{Handling the PARI stack}
+
+\subsec{Allocating memory on the stack}
+
+\fun{GEN}{cgetg}{long n, long t} allocates memory on the stack for
+an object of length \kbd{n} and type~\kbd{t}, and initializes its first
+codeword.
+
+\fun{GEN}{cgeti}{long n} allocates memory on the stack for a \typ{INT}
+of length~\kbd{n}, and initializes its first codeword. Identical to
+\kbd{cgetg(n,\typ{INT})}.
+
+\fun{GEN}{cgetr}{long n} allocates memory on the stack for a \typ{REAL}
+of length~\kbd{n}, and initializes its first codeword. Identical to
+\kbd{cgetg(n,\typ{REAL})}.
+
+\fun{GEN}{cgetc}{long n} allocates memory on the stack for a
+\typ{COMPLEX}, whose real and imaginary parts are \typ{REAL}s
+of length~\kbd{n}.
+
+\fun{GEN}{cgetp}{GEN x} creates space sufficient to hold the
+\typ{PADIC}~\kbd{x}, and sets the prime $p$ and the $p$-adic precision to
+those of~\kbd{x}, but does not copy (the $p$-adic unit or zero representative
+and the modulus of)~\kbd{x}.
+
+\fun{GEN}{new_chunk}{size_t n} allocates a \kbd{GEN} with $n$ components,
+\emph{without} filling the required code words. This is the low-level
+constructor underlying \kbd{cgetg}, which calls \kbd{new\_chunk} then sets
+the first code word. It works by simply returning the address
+\kbd{((GEN)avma) - n}, after checking that it is larger than \kbd{(GEN)bot}.
+
+\fun{char*}{stack_malloc}{size_t n} allocates memory on the stack for $n$
+chars (\emph{not} $n$ \kbd{GEN}s). This is faster than using \kbd{malloc},
+and easier to use in most situations when temporary storage is needed. In
+particular there is no need to \kbd{free} individually all variables thus
+allocated: a simple \kbd{avma = oldavma} might be enough. On the other hand,
+beware that this is not permanent independent storage, but part of the stack.
+
+\fun{char*}{stack_calloc}{size_t n} as \kbd{stack\_malloc}, setting the memory
+to zero.
+
+\noindent Objects allocated through these last three functions cannot be
+\kbd{gerepile}'d, since they are not yet valid \kbd{GEN}s: their codewords
+must be filled first.
+
+\fun{GEN}{cgetalloc}{long t, size_t l}, same as \kbd{cgetg(t, l)}, except
+that the result is allocated using \tet{pari_malloc} instead of the PARI
+stack. The resulting \kbd{GEN} is now impervious to garbage collecting
+routines, but should be freed using \tet{pari_free}.
+
+\subsec{Stack-independent binary objects}
+
+\fun{GENbin*}{copy_bin}{GEN x} copies $x$ into a malloc'ed structure suitable
+for stack-independent binary transmission or storage. The object obtained
+is architecture independent provided, \kbd{sizeof(long)} remains the same
+on all PARI instances involved, as well as the multiprecision kernel (either
+native or GMP).
+
+\fun{GENbin*}{copy_bin_canon}{GEN x} as \kbd{copy\_bin}, ensuring furthermore
+that the binary object is independent of the multiprecision kernel. Slower
+than \kbd{copy\_bin}.
+
+\fun{GEN}{bin_copy}{GENbin *p} assuming $p$ was created by \kbd{copy\_bin(x)}
+(not necessarily by the same PARI instance: transmission or external storage
+may be involved), restores $x$ on the PARI stack.
+
+\noindent The routine \kbd{bin\_copy} transparently encapsulate the following
+functions:
+
+\fun{GEN}{GENbinbase}{GENbin *p} the \kbd{GEN} data actually stored in $p$.
+All addresses are stored as offsets with respect to a common reference point,
+so the resulting \kbd{GEN} is unusable unless it is a non-recursive type;
+private low-level routines must be called first to restore absolute addresses.
+
+\fun{void}{shiftaddress}{GEN x, long dec} converts relative addresses to
+absolute ones.
+
+\fun{void}{shiftaddress_canon}{GEN x, long dec} converts relative addresses to
+absolute ones, and converts leaves from a canonical form to the one
+specific to the multiprecision kernel in use. The \kbd{GENbin} type stores
+whether leaves are stored in canonical form, so \kbd{bin\_copy} can call
+the right variant.
+
+\noindent Objects containing closures are harder to e.g. copy and save to disk,
+since closures contain pointers to libpari functions that will not be valid in
+another gp instance: there is little chance for them to be loaded at the exact
+same address in memory. Such objects must be saved along with a linking table.
+
+\fun{GEN}{copybin_unlink}{GEN C} returns a linking table allowing to safely
+store and transmit \typ{CLOSURE} objects in $C$.  If $C = \kbd{NULL}$ return a
+linking table corresponding to the content of all gp variables. $C$ may then be
+dumped to disk in binary form, for instance.
+
+\fun{void}{bincopy_relink}{GEN C, GEN V} given a binary object $C$, as dumped
+by writebin and read back into a session, and a linking table $V$, restore all
+closures contained in $C$ (function pointers are translated to their current
+value).
+
+\subsec{Garbage collection}
+See \secref{se:garbage} for a detailed explanation and many examples.
+
+\fun{void}{cgiv}{GEN x} frees object \kbd{x}, assuming it is the last created
+on the stack.
+
+\fun{GEN}{gerepile}{pari_sp p, pari_sp q, GEN x} general garbage collector
+for the stack.
+
+\fun{void}{gerepileall}{pari_sp av, int n, ...} cleans up the stack from
+\kbd{av} on (i.e from \kbd{avma} to \kbd{av}), preserving the \kbd{n} objects
+which follow in the argument list (of type \kbd{GEN*}). For instance,
+\kbd{gerepileall(av, 2, \&x, \&y)} preserves \kbd{x} and \kbd{y}.
+
+\fun{void}{gerepileallsp}{pari_sp av, pari_sp ltop, int n, ...}
+cleans up the stack between \kbd{av} and \kbd{ltop}, updating
+the \kbd{n} elements which follow \kbd{n} in the argument list (of type
+\kbd{GEN*}). Check that the elements of \kbd{g} have no component between
+\kbd{av} and \kbd{ltop}, and assumes that no garbage is present between
+\kbd{avma} and \kbd{ltop}. Analogous to (but faster than) \kbd{gerepileall}
+otherwise.
+
+\fun{GEN}{gerepilecopy}{pari_sp av, GEN x} cleans up the stack  from
+\kbd{av} on, preserving the object \kbd{x}. Special case of \kbd{gerepileall}
+(case $\kbd{n} = 1$), except that the routine returns the preserved \kbd{GEN}
+instead of updating its address through a pointer.
+
+\fun{void}{gerepilemany}{pari_sp av, GEN* g[], int n} alternative interface
+to \kbd{gerepileall}. The preserved \kbd{GEN}s are the elements of the array
+\kbd{g} of length $n$: \kbd{g[0]}, \kbd{g[1]}, \dots,
+\kbd{g[$n$-1]}. Obsolete: no more efficient than \kbd{gerepileall},
+error-prone, and clumsy (need to declare an extra \kbd{GEN *g}).
+
+\fun{void}{gerepilemanysp}{pari_sp av, pari_sp ltop, GEN* g[], int n}
+alternative interface to \kbd{gerepileallsp}. Obsolete.
+
+\fun{void}{gerepilecoeffs}{pari_sp av, GEN x, int n} cleans up the stack
+from \kbd{av} on, preserving \kbd{x[0]}, \dots, \kbd{x[n-1]} (which are
+\kbd{GEN}s).
+
+\fun{void}{gerepilecoeffssp}{pari_sp av, pari_sp ltop, GEN x, int n}
+cleans up the stack from \kbd{av} to \kbd{ltop}, preserving \kbd{x[0]},
+\dots, \kbd{x[n-1]} (which are \kbd{GEN}s). Same assumptions as in
+\kbd{gerepilemanysp}, of which this is a variant. For instance
+\bprog
+  z = cgetg(3, t_COMPLEX);
+  av = avma; garbage(); ltop = avma;
+  z[1] = fun1();
+  z[2] = fun2();
+  gerepilecoeffssp(av, ltop, z + 1, 2);
+  return z;
+ at eprog\noindent
+cleans up the garbage between \kbd{av} and \kbd{ltop}, and connects \kbd{z}
+and its two components. This is marginally more efficient than the standard
+\bprog
+  av = avma; garbage(); ltop = avma;
+  z = cgetg(3, t_COMPLEX);
+  z[1] = fun1();
+  z[2] = fun2(); return gerepile(av, ltop, z);
+ at eprog\noindent
+
+\fun{GEN}{gerepileupto}{pari_sp av, GEN q} analogous to (but faster than)
+\kbd{gerepilecopy}. Assumes that \kbd{q} is connected and that its root was
+created before any component. If \kbd{q} is not on the stack, this is
+equivalent to \kbd{avma = av}; in particular, sentinels which are not even
+proper \kbd{GEN}s such as \kbd{q = NULL} are allowed.
+
+\fun{GEN}{gerepileuptoint}{pari_sp av, GEN q} analogous to (but faster than)
+\kbd{gerepileupto}. Assumes further that \kbd{q} is a \typ{INT}. The
+length and effective length of the resulting \typ{INT} are equal.
+
+\fun{GEN}{gerepileuptoleaf}{pari_sp av, GEN q} analogous to (but faster than)
+\kbd{gerepileupto}. Assumes further that \kbd{q} is a leaf, i.e a
+non-recursive type (\kbd{is\_recursive\_t(typ(q))} is non-zero). Contrary to
+\kbd{gerepileuptoint} and \kbd{gerepileupto}, \kbd{gerepileuptoleaf} leaves
+length and effective length of a \typ{INT} unchanged.
+
+\subsec{Garbage collection: advanced use}
+
+\fun{void}{stackdummy}{pari_sp av, pari_sp ltop} inhibits the memory area
+between \kbd{av} \emph{included} and \kbd{ltop} \emph{excluded} with respect to
+\kbd{gerepile}, in order to avoid a call to \kbd{gerepile(av, ltop,...)}.
+The stack space is not reclaimed though.
+
+More precisely, this routine assumes that \kbd{av} is recorded earlier
+than \kbd{ltop}, then marks the specified stack segment as a
+non-recursive type of the correct length. Thus gerepile will not inspect
+the zone, at most copy it. To be used in the following situation:
+\bprog
+  av0 = avma; z = cgetg(t_VEC, 3);
+  gel(z,1) = HUGE(); av = avma; garbage(); ltop = avma;
+  gel(z,2) = HUGE(); stackdummy(av, ltop);
+ at eprog\noindent
+Compared to the orthodox
+\bprog
+  gel(z,2) = gerepile(av, ltop, gel(z,2));
+ at eprog\noindent
+or even more wasteful
+\bprog
+  z = gerepilecopy(av0, z);
+ at eprog\noindent
+we temporarily lose $(\kbd{av} - \kbd{ltop})$ words but save a costly
+\kbd{gerepile}. In principle, a garbage collection higher up the call
+chain should reclaim this later anyway.
+
+Without the \kbd{stackdummy}, if the $[\kbd{av}, \kbd{ltop}]$ zone is
+arbitrary (not even valid \kbd{GEN}s as could happen after direct
+truncation via \kbd{setlg}), we would leave dangerous data in the middle
+of~\kbd{z}, which would be a problem for a later
+\bprog
+  gerepile(..., ... , z);
+ at eprog\noindent
+And even if it were made of valid \kbd{GEN}s, inhibiting the area makes sure
+\kbd{gerepile} will not inspect their components, saving time.
+
+Another natural use in low-level routines is to ``shorten'' an existing
+\kbd{GEN} \kbd{z} to its first $\kbd{n}-1$ components:
+\bprog
+  setlg(z, n);
+  stackdummy((pari_sp)(z + lg(z)), (pari_sp)(z + n));
+ at eprog\noindent
+or to its last \kbd{n} components:
+\bprog
+  long L = lg(z) - n, tz = typ(z);
+  stackdummy((pari_sp)(z + L), (pari_sp)z);
+  z += L; z[0] = evaltyp(tz) | evallg(L);
+ at eprog
+
+The first scenario (safe shortening an existing \kbd{GEN}) is in fact so
+common, that we provide a function for this:
+
+\fun{void}{fixlg}{GEN z, long ly} a safe variant of \kbd{setlg(z, ly)}. If
+\kbd{ly} is larger than \kbd{lg(z)} do nothing. Otherwise, shorten $z$ in
+place, using \kbd{stackdummy} to avoid later \kbd{gerepile} problems.
+
+\fun{GEN}{gcopy_avma}{GEN x, pari_sp *AVMA} return a copy of $x$ as from
+\kbd{gcopy}, except that we pretend that initially \kbd{avma} is \kbd{*AVMA},
+and that \kbd{*AVMA} is updated accordingly (so that the total size of $x$ is
+the difference between the two successive values of \kbd{*AVMA}). It is not
+necessary for \kbd{*AVMA} to initially point on the stack: \tet{gclone} is
+implemented using this mechanism.
+
+\fun{GEN}{icopy_avma}{GEN x, pari_sp av} analogous to \kbd{gcopy\_avma} but
+simpler: assume $x$ is a \typ{INT} and return a copy allocated as if
+initially we had \kbd{avma} equal to \kbd{av}. There is no need to pass a
+pointer and update the value of the second argument: the new (fictitious)
+\kbd{avma} is just the return value (typecast to \kbd{pari\_sp}).
+
+\subsec{Debugging the PARI stack}
+
+\fun{int}{chk_gerepileupto}{GEN x} returns 1 if \kbd{x} is suitable for
+\kbd{gerepileupto}, and 0 otherwise. In the latter case, print a warning
+explaining the problem.
+
+\fun{void}{dbg_gerepile}{pari_sp ltop} outputs the list of all objects on the
+stack between \kbd{avma} and \kbd{ltop}, i.e. the ones that would be inspected
+in a call to \kbd{gerepile(...,ltop,...)}.
+
+\fun{void}{dbg_gerepileupto}{GEN q} outputs the list of all objects on the
+stack that would be inspected in a call to \kbd{gerepileupto(...,q)}.
+
+\subsec{Copies}
+
+\fun{GEN}{gcopy}{GEN x} creates a new copy of $x$ on the stack.
+
+\fun{GEN}{gcopy_lg}{GEN x, long l} creates a new copy of $x$
+on the stack, pretending that \kbd{lg(x)} is $l$, which must be less than or
+equal to \kbd{lg(x)}. If equal, the function is equivalent to \kbd{gcopy(x)}.
+
+\fun{int}{isonstack}{GEN x} \kbd{true} iff $x$ belongs to the stack.
+
+\fun{void}{copyifstack}{GEN x, GEN y} sets \kbd{y = gcopy(x)} if
+$x$ belongs to the stack, and \kbd{y = x} otherwise. This macro evaluates
+its arguments once, contrary to
+\bprog
+  y = isonstack(x)? gcopy(x): x;
+ at eprog
+
+\fun{void}{icopyifstack}{GEN x, GEN y} as \kbd{copyifstack} assuming \kbd{x}
+is a \typ{INT}.
+
+\subsec{Simplify}
+
+\fun{GEN}{simplify}{GEN x} you should not need that function in library mode.
+One rather uses:
+
+\fun{GEN}{simplify_shallow}{GEN x} shallow, faster, version of \tet{simplify}.
+
+\section{The PARI heap}
+\subsec{Introduction}
+
+It is implemented as a doubly-linked list of \kbd{malloc}'ed blocks of
+memory, equipped with reference counts. Each block has type \kbd{GEN} but need
+not be a valid \kbd{GEN}: it is a chunk of data preceded by a hidden header
+(meaning that we allocate $x$ and return $x + \kbd{header size}$). A
+\tev{clone}, created by \tet{gclone}, is a block which is a valid \kbd{GEN}
+and whose \emph{clone bit} is set.
+
+\subsec{Public interface}
+
+\fun{GEN}{newblock}{size_t n} allocates a block of $n$ \emph{words} (not bytes).
+
+\fun{void}{killblock}{GEN x} deletes the block~$x$ created by \kbd{newblock}.
+Fatal error if $x$ not a block.
+
+\fun{GEN}{gclone}{GEN x} creates a new permanent copy of $x$ on the heap
+(allocated using \kbd{newblock}). The \emph{clone bit} of the result is set.
+
+\fun{GEN}{gcloneref}{GEN x} if $x$ is not a clone, clone it and return the
+result; otherwise, increase the clone reference count and return $x$.
+
+\fun{void}{gunclone}{GEN x} deletes a clone. Deletion at first only decreases
+the reference count by $1$. If the count remains positive, no further action is
+taken; if the count becomes zero, then the clone is actually deleted. In the
+current implementation, this is an alias for \kbd{killblock}, but it is cleaner
+to kill clones (valid \kbd{GEN}s) using this function, and other blocks using
+\kbd{killblock}.
+
+\fun{void}{gunclone_deep}{GEN x} is only useful in the context of the GP
+interpreter which may replace arbitrary components of container types
+(\typ{VEC}, \typ{COL}, \typ{MAT}, \typ{LIST}) by clones. If $x$ is such
+a container, the function recursively deletes all clones among the components
+of $x$, then unclones $x$. Useless in library mode: simply use
+\kbd{gunclone}.
+
+\fun{void}{traverseheap}{void(*f)(GEN, void *), void *data} this applies
+\kbd{f($x$, data)} to each object $x$ on the PARI heap, most recent
+first. Mostly for debugging purposes.
+
+\fun{GEN}{getheap}{} a simple wrapper around \kbd{traverseheap}. Returns  a
+two-component row vector giving the number of objects on the heap and the
+amount of memory they occupy in long words.
+
+\fun{GEN}{cgetg_block}{long x, long y} as \kbd{cgetg(x,y)}, creating the return
+value as a \kbd{block}, not on the PARI stack.
+
+\fun{GEN}{cgetr_block}{long prec} as \kbd{cgetr(prec)}, creating the return
+value as a \kbd{block}, not on the PARI stack.
+
+\subsec{Implementation note} The hidden block header is manipulated using the
+following private functions:
+
+\fun{void*}{bl_base}{GEN x} returns the pointer that was actually allocated
+by \kbd{malloc} (can be freed).
+
+\fun{long}{bl_refc}{GEN x} the reference count of $x$: the number of pointers
+to this block. Decremented in \kbd{killblock}, incremented by the private
+function \fun{void}{gclone_refc}{GEN x}; block is freed when the reference
+count reaches $0$.
+
+\fun{long}{bl_num}{GEN x} the index of this block in the list of all blocks
+allocated so far (including freed blocks). Uniquely identifies a block until
+$2^\B$ blocks have been allocated and this wraps around.
+
+\fun{GEN}{bl_next}{GEN x} the block \emph{after} $x$ in the linked list of
+blocks (\kbd{NULL} if $x$ is the last block allocated not yet killed).
+
+\fun{GEN}{bl_prev}{GEN x} the block allocated \emph{before} $x$ (never
+\kbd{NULL}).
+
+We documented the last four routines as functions for clarity (and type
+checking) but they are actually macros yielding valid lvalues. It is allowed
+to write \kbd{bl\_refc(x)++} for instance.
+
+\section{Handling user and temp variables}
+Low-level implementation of user / temporary variables is liable to change. We
+describe it nevertheless for completeness. Currently variables are
+implemented by a single array of values divided in 3 zones: 0--\kbd{nvar}
+(user variables), \kbd{max\_avail}--\kbd{MAXVARN} (temporary variables),
+and \kbd{nvar+1}--\kbd{max\_avail-1} (pool of free variable numbers).
+
+\subsec{Low-level}
+
+\fun{void}{pari_var_init}{}: a small part of \kbd{pari\_init}. Resets
+variable counters \kbd{nvar} and \kbd{max\_avail}, notwithstanding existing
+variables! In effect, this even deletes \kbd{x}. Don't use it.
+
+\fun{long}{pari_var_next}{}: returns \kbd{nvar}, the number of the next user
+variable we can create.
+
+\fun{long}{pari_var_next_temp}{} returns \kbd{max\_avail}, the number of the
+next temp variable we can create.
+
+\fun{void}{pari_var_create}{entree *ep} low-level initialization of an
+\kbd{EpVAR}.
+
+\noindent The obsolete function \fun{long}{manage_var}{long n, entree *ep}
+is kept for backward compatibility only. Don't use it.
+
+\subsec{User variables}
+
+\fun{long}{fetch_user_var}{char *s} returns a user variable whose name
+is \kbd{s}, creating it is needed (and using an existing variable otherwise).
+Returns its variable number.
+
+\fun{entree*}{fetch_named_var}{char *s} as \kbd{fetch\_user\_var}, but
+returns an \kbd{entree*} suitable for inclusion in the interpreter hashlists
+of symbols, not a variable number. \kbd{fetch\_user\_var} is a trivial
+wrapper.
+
+\fun{GEN}{fetch_var_value}{long v} returns a shallow copy of the
+current value of the variable numbered $v$. Return \kbd{NULL} for a temporary
+variable.
+
+\fun{entree*}{is_entry}{const char *s} returns the \kbd{entree*} associated
+to an identifier \kbd{s} (variable or function), from the interpreter
+hashtables. Return \kbd{NULL} is the identifier is unknown.
+
+\subsec{Temporary variables}
+
+\fun{long}{fetch_var}{void} returns the number of a new temporary variable
+(decreasing \kbd{max\_avail}).
+
+\fun{long}{delete_var}{void} delete latest temp variable created and return
+the number of previous one.
+
+\fun{void}{name_var}{long n, char *s} rename temporary variable number
+\kbd{n} to \kbd{s}; mostly useful for nicer printout. Error when trying to
+rename a user variable: use \kbd{fetch\_named\_var} to get a user variable of
+the right name in the first place.
+
+\section{Adding functions to PARI}
+\subsec{Nota Bene}
+%
+As mentioned in the \kbd{COPYING} file, modified versions of the PARI package
+can be distributed under the conditions of the GNU General Public License. If
+you do modify PARI, however, it is certainly for a good reason, and we
+would like to know about it, so that everyone can benefit from your changes.
+There is then a good chance that your improvements are incorporated into the
+next release.
+
+We classify changes to PARI into four rough classes, where changes of the
+first three types are almost certain to be accepted. The first type includes
+all improvements to the documentation, in a broad sense. This includes
+correcting typos or inaccuracies of course, but also items which are not
+really covered in this document, e.g.~if you happen to write a tutorial,
+or pieces of code exemplifying fine points unduly omitted in the present
+manual.
+
+The second type is to expand or modify the configuration routines and skeleton
+files (the \kbd{Configure} script and anything in the \kbd{config/}
+subdirectory) so that compilation is possible (or easier, or more efficient)
+on an operating system previously not catered for. This includes discovering
+and removing idiosyncrasies in the code that would hinder its portability.
+
+The third type is to modify existing (mathematical) code, either to correct
+bugs, to add new functionality to existing functions, or to improve their
+efficiency.
+
+Finally the last type is to add new functions to PARI. We explain here how
+to do this, so that in particular the new function can be called from \kbd{gp}.
+
+\subsec{Coding guidelines}\label{se:coding_guidelines}
+\noindent
+Code your function in a file of its own, using as a guide other functions
+in the PARI sources. One important thing to remember is to clean the stack
+before exiting your main function, since otherwise successive calls to
+the function clutters the stack with unnecessary garbage, and stack
+overflow occurs sooner. Also, if it returns a \kbd{GEN} and you want it
+to be accessible to \kbd{gp}, you have to make sure this \kbd{GEN} is
+suitable for \kbd{gerepileupto} (see \secref{se:garbage}).
+
+If error messages or warnings are to be generated in your function, use
+\kbd{pari\_err} and \kbd{pari\_warn} respectively.
+Recall that \kbd{pari\_err} does not return but ends with a \kbd{longjmp}
+statement. As well, instead of explicit \kbd{printf}~/ \kbd{fprintf}
+statements, use the following encapsulated variants:
+
+\fun{void}{pari_putc}{char c}: write character \kbd{c} to the output stream.
+
+\fun{void}{pari_puts}{char *s}: write \kbd{s} to the output stream.
+
+\fun{void}{pari_printf}{const char *fmt, ...}: write following arguments to the
+output stream, according to the conversion specifications in format \kbd{fmt}
+(see \tet{printf}).
+
+\fun{void}{err_printf}{const char *fmt, ...}: as \tet{pari_printf}, writing to
+PARI's current error stream.
+
+\fun{void}{err_flush}{void} flush error stream.
+
+Declare all public functions in an appropriate header file, if you
+want to access them from C. The other functions should be declared
+\kbd{static} in your file.
+
+Your function is now ready to be used in library mode after compilation and
+creation of the library. If possible, compile it as a shared library (see
+the \kbd{Makefile} coming with the \kbd{extgcd} example in the
+distribution). It is however still inaccessible from \kbd{gp}.\smallskip
+
+\subsec{GP prototypes, parser codes}
+\label{se:gp.interface}
+A \tev{GP prototype} is a character string describing all the GP parser needs
+to know about the function prototype. It contains a sequence of the following
+atoms:
+
+\settabs\+\indent&\kbd{Dxxx}\quad&\cr
+
+\noindent\item Return type: \kbd{GEN} by default (must be valid for
+\kbd{gerepileupto}), otherwise the following can appear as the \emph{first}
+char of the code string:
+%
+\+& \kbd{i} & return \kbd{int}\cr
+\+& \kbd{l} & return \kbd{long}\cr
+\+& \kbd{v} & return \kbd{void}\cr
+\+& \kbd{m} & return a \kbd{GEN} which is not \kbd{gerepile}-safe.\cr
+
+The \kbd{m} code is used for member functions, to avoid unnecessary copies. A
+copy opcode is generated by the compiler if the result needs to be kept safe
+for later use.
+
+\noindent\item Mandatory arguments, appearing in the same order as the
+input arguments they describe:
+%
+\+& \kbd{G} & \kbd{GEN}\cr
+\+& \kbd{\&}& \kbd{*GEN}\cr
+\+& \kbd{L} & \kbd{long} (we implicitly typecast \kbd{int} to \kbd{long})\cr
+\+& \kbd{V} & loop variable\cr
+\+& \kbd{n} & variable, expects a \idx{variable number} (a \kbd{long}, not an
+\kbd{*entree})\cr
+\+& \kbd{W} & a \kbd{GEN} which is a lvalue to be modified in place
+(for \typ{LIST})\cr
+\+& \kbd{r} & raw input (treated as a string without quotes). Quoted
+ args are copied as strings\cr
+\+&&\quad Stops at first unquoted \kbd{')'} or \kbd{','}. Special chars can
+be quoted using \kbd{'\bs'}\cr
+\+&&\quad Example: \kbd{aa"b\bs n)"c} yields the string \kbd{"aab\bs{n})c"}\cr
+\+& \kbd{s} & expanded string. Example: \kbd{Pi"x"2} yields \kbd{"3.142x2"}\cr
+\+&&\quad Unquoted components can be of any PARI type, converted to string
+          following\cr
+\+&&\quad current output format\cr
+\+& \kbd{I} & closure whose value is ignored, as in \kbd{for} loops,\cr
+\+&&\quad to be processed by \fun{void}{closure_evalvoid}{GEN C}\cr
+\+& \kbd{E} & closure whose value is used, as in \kbd{sum} loops,\cr
+\+&&\quad to be processed by \fun{void}{closure_evalgen}{GEN C}\cr
+\+& \kbd{J} & implicit function of arity $1$, as in \kbd{parsum} loops,\cr
+\+&&\quad to be processed by \fun{void}{closure_callgen1}{GEN C}\cr
+
+\noindent A \tev{closure} is a GP function in compiled (bytecode) form. It
+can be efficiently evaluated using the \kbd{closure\_eval}$xxx$ functions.
+
+\noindent\item Automatic arguments:
+%
+\+& \kbd{f} &  Fake \kbd{*long}. C function requires a pointer but we
+do not use the resulting \kbd{long}\cr
+\+& \kbd{p} &  real precision (default \kbd{realprecision})\cr
+\+& \kbd{P} &  series precision (default \kbd{seriesprecision},
+ global variable \kbd{precdl} for the library)\cr
+
+\noindent\item Syntax requirements, used by functions like
+ \kbd{for}, \kbd{sum}, etc.:
+%
+\+& \kbd{=} & separator \kbd{=} required at this point (between two
+arguments)\cr
+
+\noindent\item Optional arguments and default values:
+%
+\+& \kbd{E*} & any number of expressions, possibly 0 (see \kbd{E})\cr
+\+& \kbd{s*} & any number of strings, possibly 0 (see \kbd{s})\cr
+\+& \kbd{D\var{xxx}} &  argument can be omitted and has a default value\cr
+
+The \kbd{E*} code reads all remaining arguments in closure context and passes
+them as a single \typ{VEC}.
+The \kbd{s*} code reads all remaining arguments in
+\tev{string context} (see \secref{se:strings}), and passes the list of
+strings as a single \typ{VEC}.  The automatic concatenation rules in string
+context are implemented so that adjacent strings
+are read as different arguments, as if they had been comma-separated. For
+instance, if the remaining argument sequence is: \kbd{"xx" 1, "yy"}, the
+\kbd{s*} atom sends \kbd{[a, b, c]}, where
+$a$, $b$, $c$ are \kbd{GEN}s of type \typ{STR} (content \kbd{"xx"}),
+\typ{INT} (equal to $1$) and \typ{STR} (content \kbd{"yy"}).
+
+The format to indicate a default value (atom starts with a \kbd{D}) is
+``\kbd{D\var{value},\var{type},}'', where \var{type} is the code for any
+mandatory atom (previous group), \var{value} is any valid GP expression
+which is converted according to \var{type}, and the ending comma is
+mandatory. For instance \kbd{D0,L,} stands for ``this optional argument is
+converted to a \kbd{long}, and is \kbd{0} by default''. So if the
+user-given argument reads \kbd{1 + 3} at this point, \kbd{4L} is sent to
+the function; and \kbd{0L} if the argument is omitted. The following
+special notations are available:
+
+\settabs\+\indent\indent&\kbd{Dxxx}\quad& optional \kbd{*GEN},&\cr
+\+&\kbd{DG}& optional \kbd{GEN}, & send \kbd{NULL} if argument omitted.\cr
+
+\+&\kbd{D\&}& optional \kbd{*GEN}, send \kbd{NULL} if argument omitted.\cr
+\+&&\quad The argument must be prefixed by \kbd{\&}.\cr
+
+\+&\kbd{Dr}& optional raw string, send \kbd{NULL} if argument omitted.\cr
+
+\+&\kbd{Ds}& optional \kbd{char *}, send \kbd{NULL} if argument omitted.\cr
+
+\+&\kbd{DV}& optional \kbd{*entree}, send \kbd{NULL} if argument omitted.\cr
+
+\+&\kbd{DI}, \kbd{DE}& optional closure, send \kbd{NULL} if argument omitted.\cr
+
+\+&\kbd{Dn}& optional variable number, $-1$ if omitted.\cr
+
+\misctitle{Hardcoded limit} C functions using more than 20 arguments are not
+supported. Use vectors if you really need that many parameters.
+
+When the function is called under \kbd{gp}, the prototype is scanned and each
+time an atom corresponding to a mandatory argument is met, a user-given
+argument is read (\kbd{gp} outputs an error message it the argument was
+missing). Each time an optional atom is met, a default value is inserted if the
+user omits the argument. The ``automatic'' atoms fill in the argument list
+transparently, supplying the current value of the corresponding variable (or a
+dummy pointer).
+
+For instance, here is how you would code the following prototypes, which
+do not involve default values:
+\bprog
+GEN f(GEN x, GEN y, long prec)   ----> "GGp"
+void f(GEN x, GEN y, long prec)  ----> "vGGp"
+void f(GEN x, long y, long prec) ----> "vGLp"
+long f(GEN x)                    ----> "lG"
+int f(long x)                    ----> "iL"
+ at eprog\noindent
+If you want more examples, \kbd{gp} gives you easy access to the parser codes
+associated to all GP functions: just type \kbd{\b{h} \var{function}}. You
+can then compare with the C prototypes as they stand in \kbd{paridecl.h}.
+
+\misctitle{Remark} If you need to implement complicated control statements
+(probably for some improved summation functions), you need to know
+how the parser implements closures and lexicals and how the evaluator lets
+you deal with them, in particular the \tet{push_lex} and \tet{pop_lex}
+functions. Check their descriptions and adapt the source code in
+\kbd{language/sumiter.c} and \kbd{language/intnum.c}.
+
+\subsec{Integration with \kbd{gp} as a shared module}
+
+In this section we assume that your Operating System is supported by
+\tet{install}. You have written a function in C following the guidelines is
+\secref{se:coding_guidelines}; in case the function returns a \kbd{GEN}, it
+must satisfy \kbd{gerepileupto} assumptions (see \secref{se:garbage}).
+
+You then succeeded in building it as part of a shared library and want to
+finally tell \kbd{gp} about your function. First, find a name for it. It does
+not have to match the one used in library mode, but consistency is nice. It
+has to be a valid GP identifier, i.e.~use only alphabetic characters, digits
+and the underscore character (\kbd{\_}), the first character being
+alphabetic.
+
+Then figure out the correct \idx{parser code} corresponding to the function
+prototype (as explained in~\secref{se:gp.interface}) and write a GP script
+like the following:
+\bprog
+install(libname, code, gpname, library)
+addhelp(gpname, "some help text")
+ at eprog
+\noindent(see \secref{se:addhelp} and~\ref{se:install}). The \idx{addhelp}
+part is not mandatory, but very useful if you want others to use your
+module. \kbd{libname} is how the function is named in the library,
+usually the same name as one visible from C.
+
+Read that file from your \kbd{gp} session, for instance from your
+\idx{preferences file} (\secref{se:gprc}), and that's it. You
+can now use the new function \var{gpname} under \kbd{gp}, and we would very
+much like to hear about it! \smallskip
+
+\misctitle{Example}
+A complete description could look like this:
+\bprog
+{
+  install(bnfinit0, "GD0,L,DGp", ClassGroupInit, "libpari.so");
+  addhelp(ClassGroupInit, "ClassGroupInit(P,{flag=0},{data=[]}):
+    compute the necessary data for ...");
+}
+ at eprog\noindent which means we have a function \kbd{ClassGroupInit} under
+\kbd{gp}, which calls the library function \kbd{bnfinit0} . The function has
+one mandatory argument, and possibly two more (two \kbd{'D'} in the code),
+plus the current real precision. More precisely, the first argument is a
+\kbd{GEN}, the second one is converted to a \kbd{long} using \kbd{itos}
+(\kbd{0} is passed if it is omitted), and the third one is also a \kbd{GEN},
+but we pass \kbd{NULL} if no argument was supplied by the user. This matches
+the C prototype (from \kbd{paridecl.h}):
+%
+\bprog
+  GEN bnfinit0(GEN P, long flag, GEN data, long prec)
+ at eprog\noindent
+This function is in fact coded in \kbd{basemath/buch2.c}, and is in this case
+completely identical to the GP function \kbd{bnfinit} but \kbd{gp} does not
+need to know about this, only that it can be found somewhere in the shared
+library \kbd{libpari.so}.
+
+\misctitle{Important note} You see in this example that it is the
+function's responsibility to correctly interpret its operands: \kbd{data =
+NULL} is interpreted \emph{by the function} as an empty vector. Note that
+since \kbd{NULL} is never a valid \kbd{GEN} pointer, this trick always
+enables you to distinguish between a default value and actual input: the
+user could explicitly supply an empty vector!
+
+\subsec{Library interface for \kbd{install}}
+
+There is a corresponding library interface for this \kbd{install}
+functionality, letting you expand the GP parser/evaluator available in the
+library with new functions from your C source code. Functions such as
+\tet{gp_read_str} may then evaluate a GP expression sequence involving calls
+to these new function!
+
+\fun{entree *}{install}{void *f, const char *gpname, const char *code}
+
+\noindent where \kbd{f} is the (address of the) function (cast to
+\kbd{void*}), \kbd{gpname} is the name by which you want to access your
+function from within your GP expressions, and \kbd{code} is as above.
+
+
+\subsec{Integration by patching \kbd{gp}}
+
+If \tet{install} is not available, and installing Linux or a BSD operating
+system is not an option (why?), you have to hardcode your function in the
+\kbd{gp} binary. Here is what needs to be done:
+
+\item Fetch the complete sources of the PARI distribution.
+
+\item Drop the function source code module in an appropriate directory
+(a priori \kbd{src/modules}), and declare all public functions
+in \kbd{src/headers/paridecl.h}.
+
+\item Choose a help section and add a file
+\kbd{src/functions/\var{section}/\var{gpname}}
+containing the following, keeping the notation above:
+\bprog
+Function:  @com\var{gpname}
+Section:   @com\var{section}
+C-Name:    @com\var{libname}
+Prototype: @com\var{code}
+Help:      @com\var{some help text}
+ at eprog\noindent
+(If the help text does not fit on a single line, continuation lines must
+start by a whitespace character.) Two GP2C-related fields (\kbd{Description}
+and \kbd{Wrapper}) are also available to improve the code GP2C generates when
+compiling scripts involving your function. See the GP2C documentation for
+details.
+
+\item Launch \kbd{Configure}, which should pick up your C files and build an
+appropriate \kbd{Makefile}. At this point you can recompile \kbd{gp}, which
+will first rebuild the functions database.
+
+\misctitle{Example} We reuse the \kbd{ClassGroupInit} / \kbd{bnfinit0}
+from the preceding section. Since the C source code is already part
+of PARI, we only need to add a file
+
+ \kbd{functions/number\_fields/ClassGroupInit}
+
+\noindent containing the following:
+\bprog
+Function: ClassGroupInit
+Section: number_fields
+C-Name: bnfinit0
+Prototype: GD0,L,DGp
+Help: ClassGroupInit(P,{flag=0},{tech=[]}): this routine does @com\dots
+ at eprog\noindent
+and recompile \kbd{gp}.
+
+\section{Globals related to PARI configuration}
+\subsec{PARI version numbers}
+
+\noindent \tet{paricfg_version_code} encodes in a single \kbd{long}, the Major
+and minor version numbers as well as the patchlevel.
+
+\fun{long}{PARI_VERSION}{long M, long m, long p} produces the version code
+associated to release $M.m.p$. Each code identifies a unique PARI release,
+and corresponds to the natural total order on the set of releases (bigger
+code number means more recent release).
+
+\noindent \tet{PARI_VERSION_SHIFT} is the number of bits used to store each of
+the integers $M$, $m$, $p$ in the version code.
+
+\noindent \tet{paricfg_vcsversion} is a version string related to the
+revision control system used to handle your sources, if any. For instance
+\kbd{git-}\emph{commit hash} if compiled from a git repository.
+
+The two character strings \tet{paricfg_version} and \tet{paricfg_buildinfo},
+correspond to the first two lines printed by \kbd{gp} just before the
+Copyright message. The character string \tet{paricfg_compiledate} is the
+date of compilation which appears on the next line. The character string
+\tet{paricfg_mt_engine} is the name of the threading engine on the next line.
+
+\fun{GEN}{pari_version}{} returns the version number as a PARI object, a
+\typ{VEC} with three \typ{INT} and one \typ{STR} components.
+
+\subsec{Miscellaneous}
+
+\tet{paricfg_datadir}: character string. The location of PARI's \tet{datadir}.
+
+\newpage
+\chapter{Arithmetic kernel: Level 0 and 1}
+
+\section{Level 0 kernel (operations on ulongs)}
+
+\subsec{Micro-kernel}
+The Level 0 kernel simulates basic operations of the 68020 processor on which
+PARI was originally implemented. They need ``global'' \kbd{ulong} variables
+\kbd{overflow} (which will contain only 0 or 1) and \kbd{hiremainder} to
+function properly. A routine using one of these lowest-level functions
+where the description mentions either \kbd{hiremainder} or \kbd{overflow}
+must declare the corresponding
+\bprog
+  LOCAL_HIREMAINDER;  /* provides 'hiremainder' */
+  LOCAL_OVERFLOW;     /* provides 'overflow' */
+ at eprog\noindent
+in a declaration block. Variables \kbd{hiremainder} and \kbd{overflow} then
+become available in the enclosing block. For instance a loop over the powers
+of an \kbd{ulong}~\kbd{p} protected from overflows could read
+\bprog
+ while (pk < lim)
+ {
+   LOCAL_HIREMAINDER;
+   ...
+   pk = mulll(pk, p); if (hiremainder) break;
+ }
+ at eprog\noindent
+For most architectures, the functions mentioned below are really chunks of
+inlined assembler code, and the above `global' variables are actually
+local register values.
+
+\fun{ulong}{addll}{ulong x, ulong y} adds \kbd{x} and \kbd{y}, returns the
+lower \B\ bits and puts the carry bit into \kbd{overflow}.
+
+\fun{ulong}{addllx}{ulong x, ulong y} adds \kbd{overflow} to the sum of the
+\kbd{x} and \kbd{y}, returns the lower \B\ bits and puts the carry bit into
+\kbd{overflow}.
+
+\fun{ulong}{subll}{ulong x, ulong y} subtracts \kbd{x} and \kbd{y}, returns
+the lower \B\ bits and put the carry (borrow) bit into \kbd{overflow}.
+
+\fun{ulong}{subllx}{ulong x, ulong y} subtracts \kbd{overflow} from the
+difference of \kbd{x} and \kbd{y}, returns the lower \B\ bits and puts the
+carry (borrow) bit into \kbd{overflow}.
+
+\fun{int}{bfffo}{ulong x} returns the number of leading zero bits in \kbd{x}.
+That is, the number of bit positions by which it would have to be shifted
+left until its leftmost bit first becomes equal to~1, which can be between 0
+and $\B-1$ for nonzero \kbd{x}. When \kbd{x} is~0, the result is undefined.
+
+\fun{ulong}{mulll}{ulong x, ulong y} multiplies \kbd{x} by \kbd{y}, returns
+the lower \B\ bits and stores the high-order \B\ bits into \kbd{hiremainder}.
+
+\fun{ulong}{addmul}{ulong x, ulong y} adds \kbd{hiremainder} to the product
+of \kbd{x} and \kbd{y}, returns the lower \B\ bits and stores the high-order
+\B\ bits into \kbd{hiremainder}.
+
+\fun{ulong}{divll}{ulong x, ulong y} returns the quotient of
+$  \left(\kbd{hiremainder} * 2^{\B}\right) + \kbd{x} $
+by \kbd{y} and stores the remainder into \kbd{hiremainder}. An error occurs
+if the quotient cannot be represented by an \kbd{ulong}, i.e.~if initially
+$\kbd{hiremainder}\ge\kbd{y}$.
+
+\misctitle{Obsolete routines} Those functions are awkward and no longer used;
+they are only provided for backward compatibility:
+
+\fun{ulong}{shiftl}{ulong x, ulong y} returns $x$ shifted left by $y$ bits,
+i.e.~\kbd{$x$ << $y$}, where we assume that $0\leq y\leq\B$. The global variable
+\kbd{hiremainder} receives the bits that were shifted out,
+i.e.~\kbd{$x$ >> $(\B - y)$}.
+
+\fun{ulong}{shiftlr}{ulong x, ulong y} returns $x$ shifted right by $y$ bits,
+i.e.~\kbd{$x$ >> $y$}, where we assume that $0\leq y\leq\B$. The global variable
+\kbd{hiremainder} receives the bits that were shifted out,
+i.e.~\kbd{$x$ << $(\B - y)$}.
+
+\subsec{Modular kernel}
+The following routines are not part of the level 0 kernel per se, but
+implement modular operations on words in terms of the above. They are written
+so that no overflow may occur. Let $m \geq 1$ be the modulus; all operands
+representing classes modulo $m$ are assumed to belong to $[0,m-1]$. The
+result may be wrong for a number of reasons otherwise: it may not be reduced,
+overflow can occur, etc.
+
+\fun{int}{odd}{ulong x} returns 1 if $x$ is odd, and 0 otherwise.
+
+\fun{int}{both_odd}{ulong x, ulong y} returns 1 if $x$ and $y$ are both odd,
+and 0 otherwise.
+
+\fun{ulong}{invmod2BIL}{ulong x} returns the smallest
+positive representative of $x^{-1}$ mod $2^\B$, assuming $x$ is odd.
+
+\fun{ulong}{Fl_add}{ulong x, ulong y, ulong m} returns the smallest
+positive representative of $x + y$ modulo $m$.
+
+\fun{ulong}{Fl_neg}{ulong x, ulong m} returns the smallest
+positive representative of $-x$ modulo $m$.
+
+\fun{ulong}{Fl_sub}{ulong x, ulong y, ulong m} returns the smallest
+positive representative of $x - y$ modulo $m$.
+
+\fun{long}{Fl_center}{ulong x, ulong m, ulong mo2} returns the representative
+in $]-m/2,m/2]$ of $x$ modulo $m$. Assume $0 \leq x < m$ and
+$\kbd{mo2}  = m >> 1$.
+
+\fun{ulong}{Fl_mul}{ulong x, ulong y, ulong m} returns the smallest positive
+representative of $x y$ modulo $m$.
+
+\fun{ulong}{Fl_double}{ulong x, ulong m} returns $2x$ modulo $m$.
+
+\fun{ulong}{Fl_triple}{ulong x, ulong m} returns $3x$ modulo $m$.
+
+\fun{ulong}{Fl_sqr}{ulong x, ulong m} returns the smallest positive
+representative of $x^2$ modulo $m$.
+
+\fun{ulong}{Fl_inv}{ulong x, ulong m} returns the smallest
+positive representative of $x^{-1}$ modulo $m$. If $x$ is not invertible
+mod~$m$, raise an exception.
+
+\fun{ulong}{Fl_invsafe}{ulong x, ulong m} returns the smallest
+positive representative of $x^{-1}$ modulo $m$. If $x$ is not invertible
+mod~$m$, return $0$ (which is ambiguous if $m=1$).
+
+\fun{ulong}{Fl_div}{ulong x, ulong y, ulong m} returns the smallest
+positive representative of $x y^{-1}$ modulo $m$. If $y$ is not invertible
+mod $m$, raise an exception.
+
+\fun{ulong}{Fl_powu}{ulong x, ulong n, ulong m} returns the smallest
+positive representative of $x^n$ modulo $m$.
+
+\fun{ulong}{Fl_sqrt}{ulong x, ulong p} returns the square root of \kbd{x}
+modulo \kbd{p} (smallest positive representative). Assumes \kbd{p} to be
+prime, and \kbd{x} to be a square modulo \kbd{p}.
+
+\fun{ulong}{Fl_order}{ulong a, ulong o, ulong p} returns the order of the
+\typ{Fp} \kbd{a}. It is assumed that \kbd{o} is a multiple of the order of
+\kbd{a}, $0$ being allowed (no non-trivial information).
+
+\fun{ulong}{random_Fl}{ulong p} returns a pseudo-random integer uniformly
+distributed in $0, 1, \dots p-1$.
+
+\fun{ulong}{pgener_Fl}{ulong p} returns the smallest \idx{primitive root}
+modulo \kbd{p}, assuming \kbd{p} is prime.
+
+\fun{ulong}{pgener_Zl}{ulong p} returns the smallest primitive root modulo
+$p^k$, $k > 1$, assuming $p$ is an odd prime.
+
+\fun{ulong}{pgener_Fl_local}{ulong p, GEN L}, see \kbd{gener\_Fp\_local},
+\kbd{L} is an \kbd{Flv}.
+
+\subsec{Switching between Fl\_xxx and standard operators}
+
+Even though the \kbd{Fl\_xxx} routines are efficient, they are slower than
+ordinary \kbd{long} operations, using the standard \kbd{+}, \kbd{\%}, etc.
+operators.
+The following macro is used to choose in a portable way the most efficient
+functions for given operands:
+
+\fun{int}{SMALL_ULONG}{ulong p} true if $2p^2 <2^\B$. In that case, it is
+possible to use ordinary operators efficiently. If $p < 2^\B$, one
+may still use the \kbd{Fl\_xxx} routines. Otherwise, one must use generic
+routines. For instance, the scalar product of the \kbd{GEN}s $x$ and $y$ mod
+$p$ could be computed as follows.
+\bprog
+    long i, l = lg(x);
+    if (lgefint(p) > 3)
+    { /* arbitrary */
+      GEN s = gen_0;
+      for (i = 1; i < l; i++) s = addii(s, mulii(gel(x,i), gel(y,i)));
+      return modii(s, p).
+    }
+    else
+    {
+      ulong s = 0, pp = itou(p);
+      x = ZV_to_Flv(x, pp);
+      y = ZV_to_Flv(y, pp);
+      if (SMALL_ULONG(pp))
+      { /* very small */
+        for (i = 1; i < l; i++)
+        {
+          s += x[i] * y[i];
+          if (s & HIGHBIT) s %= pp;
+        }
+        s %= pp;
+      }
+      else
+      { /* small */
+        for (i = 1; i < l; i++)
+          s = Fl_add(s, Fl_mul(x[i], y[i], pp), pp);
+      }
+      return utoi(s);
+    }
+ at eprog\noindent
+In effect, we have three versions of the same code: very small, small, and
+arbitrary inputs. The very small and arbitrary variants use lazy reduction
+and reduce only when it becomes necessary: when overflow might occur (very
+small), and at the very end (very small, arbitrary).
+
+\section{Level 1 kernel (operations on longs, integers and reals)}
+
+\misctitle{Note} Some functions consist of an elementary operation,
+immediately followed by an assignment statement. They will be introduced as
+in the following example:
+
+\fun{GEN}{gadd[z]}{GEN x, GEN y[, GEN z]} followed by the explicit
+description of the function
+
+\fun{GEN}{gadd}{GEN x, GEN y}
+
+\noindent which creates its result on the stack, returning a \kbd{GEN} pointer
+to it, and the parts in brackets indicate that there exists also a function
+
+\fun{void}{gaddz}{GEN x, GEN y, GEN z}
+
+\noindent which assigns its result to the pre-existing object
+\kbd{z}, leaving the stack unchanged. These assignment variants are kept for
+backward compatibility but are inefficient: don't use them.
+
+\subsec{Creation}
+
+\fun{GEN}{cgeti}{long n} allocates memory on the PARI stack for a \typ{INT}
+of length~\kbd{n}, and initializes its first codeword. Identical to
+\kbd{cgetg(n,\typ{INT})}.
+
+\fun{GEN}{cgetipos}{long n} allocates memory on the PARI stack for a
+\typ{INT} of length~\kbd{n}, and initializes its two codewords. The sign
+of \kbd{n} is set to $1$.
+
+\fun{GEN}{cgetineg}{long n} allocates memory on the PARI stack for a negative
+\typ{INT} of length~\kbd{n}, and initializes its two codewords. The sign
+of \kbd{n} is set to $-1$.
+
+\fun{GEN}{cgetr}{long n} allocates memory on the PARI stack for a \typ{REAL}
+of length~\kbd{n}, and initializes its first codeword. Identical to
+\kbd{cgetg(n,\typ{REAL})}.
+
+\fun{GEN}{cgetc}{long n} allocates memory on the PARI stack for a
+\typ{COMPLEX}, whose real and imaginary parts are \typ{REAL}s
+of length~\kbd{n}.
+
+\fun{GEN}{real_1}{long prec} create a \typ{REAL} equal to $1$ to \kbd{prec}
+words of accuracy.
+
+\fun{GEN}{real_m1}{long prec} create a \typ{REAL} equal to $-1$ to \kbd{prec}
+words of accuracy.
+
+\fun{GEN}{real_0_bit}{long bit} create a \typ{REAL} equal to $0$ with
+exponent $-\kbd{bit}$.
+
+\fun{GEN}{real_0}{long prec} is a shorthand for
+\bprog
+  real_0_bit( -bit_accuracy(prec) )
+ at eprog
+
+\fun{GEN}{int2n}{long n} creates a \typ{INT} equal to \kbd{1<<n} (i.e
+$2^n$ if $n \geq 0$, and $0$ otherwise).
+
+\fun{GEN}{int2u}{ulong n} creates a \typ{INT} equal to $2^n$.
+
+\fun{GEN}{real2n}{long n, long prec} create a \typ{REAL} equal to $2^n$
+to \kbd{prec} words of accuracy.
+
+\fun{GEN}{real_m2n}{long n, long prec} create a \typ{REAL} equal to $-2^n$
+to \kbd{prec} words of accuracy.
+
+\fun{GEN}{strtoi}{char *s} convert the character string \kbd{s} to a
+non-negative \typ{INT}. The string \kbd{s} consists exclusively of digits: no
+leading sign, no whitespace. Leading zeroes are discarded.
+
+\fun{GEN}{strtor}{char *s, long prec} convert the character string \kbd{s} to
+a non-negative \typ{REAL} of precision \kbd{prec}. The string \kbd{s}
+consists exclusively of digits and optional decimal point and exponent
+(\kbd{e} or \kbd{E}): no leading sign, no whitespace. Leading zeroes are
+discarded.
+
+\subsec{Assignment}
+In this section, the \kbd{z} argument in the \kbd{z}-functions must be of type
+\typ{INT} or~\typ{REAL}.
+
+\fun{void}{mpaff}{GEN x, GEN z} assigns \kbd{x} into~\kbd{z} (where \kbd{x}
+and \kbd{z} are \typ{INT} or \typ{REAL}).
+Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affii}{GEN x, GEN z} assigns the \typ{INT} \kbd{x} into the
+\typ{INT}~\kbd{z}.
+
+\fun{void}{affir}{GEN x, GEN z} assigns the \typ{INT} \kbd{x} into the
+\typ{REAL}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affiz}{GEN x, GEN z} assigns \typ{INT}~\kbd{x} into \typ{INT} or
+\typ{REAL}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affsi}{long s, GEN z} assigns the \kbd{long}~\kbd{s} into the
+\typ{INT}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affsr}{long s, GEN z} assigns the \kbd{long}~\kbd{s} into the
+\typ{REAL}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affsz}{long s, GEN z} assigns the \kbd{long}~\kbd{s} into the
+\typ{INT} or \typ{REAL}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affui}{ulong u, GEN z} assigns the \kbd{ulong}~\kbd{u} into the
+\typ{INT}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affur}{ulong u, GEN z} assigns the \kbd{ulong}~\kbd{u} into the
+\typ{REAL}~\kbd{z}. Assumes that $\kbd{lg(z)} > 2$.
+
+\fun{void}{affrr}{GEN x, GEN z} assigns the \typ{REAL}~\kbd{x} into the
+\typ{REAL}~\kbd{z}.
+
+\fun{void}{affgr}{GEN x, GEN z} assigns the scalar \kbd{x} into the
+\typ{REAL}~\kbd{z}, if possible.
+
+\noindent The function \kbd{affrs} and \kbd{affri} do not exist. So don't use
+them.
+
+\fun{void}{affrr_fixlg}{GEN y, GEN z} a variant of \kbd{affrr}. First shorten
+$z$ so that it is no longer than $y$, then assigns $y$ to $z$. This is used
+in the following scenario: room is reserved for the result but, due to
+cancellation, fewer words of accuracy are available than had been
+anticipated; instead of appending meaningless $0$s to the mantissa, we store
+what was actually computed.
+
+Note that shortening $z$ is not quite straightforward, since \kbd{setlg(z, ly)}
+would leave garbage on the stack, which \kbd{gerepile} might later inspect.
+It is done using
+
+\fun{void}{fixlg}{GEN z, long ly} see \tet{stackdummy} and the examples that
+follow.
+
+\subsec{Copy}
+
+\fun{GEN}{icopy}{GEN x} copy relevant words of the \typ{INT}~\kbd{x} on the
+stack: the length and effective length of the copy are equal.
+
+\fun{GEN}{rcopy}{GEN x} copy the \typ{REAL}~\kbd{x} on the stack.
+
+\fun{GEN}{leafcopy}{GEN x} copy the leaf~\kbd{x} on the
+stack (works in particular for \typ{INT}s and \typ{REAL}s).
+Contrary to \kbd{icopy}, \kbd{leafcopy} preserves the original
+length of a \typ{INT}. The obsolete form \fun{GEN}{mpcopy}{GEN x}
+is still provided for backward compatibility.
+
+This function also works on recursive types, copying them as if they were
+leaves, i.e.~making a shallow copy in that case: the components of the copy
+point to the same data as the component of the source; see also
+\kbd{shallowcopy}.
+
+\fun{GEN}{leafcopy_avma}{GEN x, pari_sp av} analogous to \kbd{gcopy\_avma}
+but simpler: assume $x$ is a leaf and return a copy allocated as if
+initially we had \kbd{avma} equal to \kbd{av}. There is no need to pass a
+pointer and update the value of the second argument: the new (fictitious)
+\kbd{avma} is just the return value (typecast to \kbd{pari\_sp}).
+
+\fun{GEN}{icopyspec}{GEN x, long nx} copy the \kbd{nx} words
+\kbd{x[2]}, \dots, \kbd{x[nx+1]} to make up a new \typ{INT}. Set the sign
+to $1$.
+
+\subsec{Conversions}
+
+\fun{GEN}{itor}{GEN x, long prec} converts the \typ{INT}~\kbd{x} to a
+\typ{REAL} of length \kbd{prec} and return the latter.
+Assumes that $\kbd{prec} > 2$.
+
+\fun{long}{itos}{GEN x} converts the \typ{INT}~\kbd{x} to a \kbd{long} if
+possible, otherwise raise an exception.
+
+\fun{long}{itos_or_0}{GEN x} converts the \typ{INT}~\kbd{x} to a \kbd{long} if
+possible, otherwise return $0$.
+
+\fun{int}{is_bigint}{GEN n} true if \kbd{itos(n)} would succeed.
+
+\fun{int}{is_bigint_lg}{GEN n, long l} true if \kbd{itos(n)} would succeed.
+Assumes \kbd{lgefint(n)} is equal to \kbd{l}.
+
+\fun{ulong}{itou}{GEN x} converts the \typ{INT}~\kbd{|x|} to an \kbd{ulong} if
+possible, otherwise raise an exception.
+
+\fun{long}{itou_or_0}{GEN x} converts the \typ{INT}~\kbd{|x|} to an
+\kbd{ulong} if possible, otherwise return $0$.
+
+\fun{GEN}{stoi}{long s} creates the \typ{INT} corresponding to the
+\kbd{long}~\kbd{s}.
+
+\fun{GEN}{stor}{long s, long prec} converts the \kbd{long}~\kbd{s} into a
+\typ{REAL} of length \kbd{prec} and return the latter. Assumes that
+$\kbd{prec} > 2$.
+
+\fun{GEN}{utoi}{ulong s} converts the \kbd{ulong}~\kbd{s} into a \typ{INT}
+and return the latter.
+
+\fun{GEN}{utoipos}{ulong s} converts the \emph{non-zero} \kbd{ulong}~\kbd{s}
+into a \typ{INT} and return the latter.
+
+\fun{GEN}{utoineg}{ulong s} converts the \emph{non-zero} \kbd{ulong}~\kbd{s}
+into the \typ{INT} $-s$ and return the latter.
+
+\fun{GEN}{utor}{ulong s, long prec} converts the \kbd{ulong}~\kbd{s} into a
+\typ{REAL} of length \kbd{prec} and return the latter. Assumes that
+$\kbd{prec} > 2$.
+
+\fun{GEN}{rtor}{GEN x, long prec} converts the \typ{REAL}~\kbd{x} to a
+\typ{REAL} of length \kbd{prec} and return the latter. If
+$\kbd{prec} < \kbd{lg(x)}$, round properly. If $\kbd{prec} > \kbd{lg(x)}$,
+pad with zeroes. Assumes that $\kbd{prec} > 2$.
+
+\noindent The following function is also available as a special case of
+\tet{mkintn}:
+
+\fun{GEN}{uu32toi}{ulong a, ulong b} returns the \kbd{GEN} equal to $2^{32} a +
+b$, \emph{assuming} that $a,b < 2^{32}$. This does not depend on
+\kbd{sizeof(long)}: the behavior is as above on both $32$ and $64$-bit
+machines.
+
+\fun{GEN}{uutoi}{ulong a, ulong b} returns the \kbd{GEN} equal to
+$2^{\B} a + b$.
+
+\fun{GEN}{uutoineg}{ulong a, ulong b} returns the \kbd{GEN} equal to
+$-(2^{\B} a + b)$.
+
+\subsec{Integer parts}
+The following four functions implement the conversion from \typ{REAL} to
+\typ{INT} using standard rounding modes. Contrary to usual semantics
+(complement the mantissa with an infinite number of 0), they will raise an
+error \emph{precision loss in truncation} if the \typ{REAL} represents a
+range containing more than one integer.
+
+\fun{GEN}{ceilr}{GEN x} smallest integer larger or equal
+to the \typ{REAL}~\kbd{x} (i.e.~the \kbd{ceil} function).
+
+\fun{GEN}{floorr}{GEN x} largest integer smaller or equal to the
+\typ{REAL}~\kbd{x} (i.e.~the \kbd{floor} function).
+
+\fun{GEN}{roundr}{GEN x} rounds the \typ{REAL} \kbd{x} to the nearest integer
+(towards~$+\infty$ in case of tie).
+
+\fun{GEN}{truncr}{GEN x} truncates the \typ{REAL}~\kbd{x} (not the same as
+\kbd{floorr} if \kbd{x} is negative).
+
+The following four function are analogous, but can also treat the trivial
+case when the argument is a \typ{INT}:
+
+\fun{GEN}{mpceil}{GEN x}
+as \kbd{ceilr} except that \kbd{x} may be a \typ{INT}.
+
+\fun{GEN}{mpfloor}{GEN x}
+as \kbd{floorr} except that \kbd{x} may be a \typ{INT}.
+
+\fun{GEN}{mpround}{GEN x}
+as \kbd{roundr} except that \kbd{x} may be a \typ{INT}.
+
+\fun{GEN}{mptrunc}{GEN x}
+as \kbd{truncr} except that \kbd{x} may be a \typ{INT}.
+
+\fun{GEN}{diviiround}{GEN x, GEN y} if \kbd{x} and \kbd{y} are \typ{INT}s,
+returns the quotient $\kbd{x}/\kbd{y}$ of \kbd{x} and~\kbd{y}, rounded to
+the nearest integer. If $\kbd{x}/\kbd{y}$ falls exactly halfway between
+two consecutive integers, then it is rounded towards~$+\infty$ (as for
+\tet{roundr}).
+
+\fun{GEN}{ceil_safe}{GEN x}, \kbd{x} being a real number (not necessarily a
+\typ{REAL}) returns the smallest integer which is larger than any possible
+incarnation of \kbd{x}. (Recall that a \typ{REAL} represents an interval of
+possible values.) Note that \kbd{gceil} raises an exception if the input
+accuracy is too low compared to its magnitude.
+
+\fun{GEN}{floor_safe}{GEN x}, \kbd{x} being a real number (not necessarily a
+\typ{REAL}) returns the largest integer which is smaller than any possible
+incarnation of \kbd{x}. (Recall that a \typ{REAL} represents an interval of
+possible values.) Note that \kbd{gfloor} raises an exception if the input
+accuracy is too low compared to its magnitude.
+
+\fun{GEN}{trunc_safe}{GEN x}, \kbd{x} being a real number (not necessarily a
+\typ{REAL}) returns the integer with the largest absolute value, which is closer
+to $0$ than any possible incarnation of \kbd{x}. (Recall that a \typ{REAL}
+represents an interval of possible values.)
+
+\fun{GEN}{roundr_safe}{GEN x} rounds the \typ{REAL} \kbd{x} to the nearest
+integer (towards~$+\infty$). Complement the mantissa with an infinite number
+of $0$ before rounding, hence never raise an exception.
+
+\subsec{$2$-adic valuations and shifts}
+
+\fun{long}{vals}{long s} 2-adic valuation of the \kbd{long}~\kbd{s}. Returns
+$-1$ if \kbd{s} is equal to 0.
+
+\fun{long}{vali}{GEN x} 2-adic valuation of the \typ{INT}~\kbd{x}. Returns $-1$
+if \kbd{x} is equal to 0.
+
+\fun{GEN}{mpshift}{GEN x, long n} shifts the~\typ{INT} or
+\typ{REAL} \kbd{x} by~\kbd{n}. If \kbd{n} is positive, this is a left shift,
+i.e.~multiplication by $2^{\kbd{n}}$. If \kbd{n} is negative, it is a right
+shift by~$-\kbd{n}$, which amounts to the truncation of the quotient of \kbd{x}
+by~$2^{-\kbd{n}}$.
+
+\fun{GEN}{shifti}{GEN x, long n} shifts the \typ{INT}~$x$ by~$n$.
+
+\fun{GEN}{shiftr}{GEN x, long n} shifts the \typ{REAL}~$x$ by~$n$.
+
+\fun{void}{shiftr_inplace}{GEN x, long n} shifts the \typ{REAL}~$x$ by~$n$,
+in place.
+
+\fun{GEN}{trunc2nr}{GEN x, long n} given a \typ{REAL} $x$, returns
+\kbd{truncr(shiftr(x,n))}, but faster, without leaving garbage on the stack
+and never raising a \emph{precision loss in truncation} error.
+Called by \tet{gtrunc2n}.
+
+\fun{GEN}{trunc2nr_lg}{GEN x, long lx, long n} given a \typ{REAL} $x$, returns
+\kbd{trunc2nr(x,n)}, pretending that the length of $x$ is \kbd{lx}, which
+must be $\leq \kbd{lg}(x)$.
+
+\fun{GEN}{mantissa2nr}{GEN x, long n} given a \typ{REAL} $x$, returns
+the mantissa of $x 2^n$ (disregards the exponent of $x$). Equivalent to
+\bprog
+  trunc2nr(x, n-expo(x)+bit_prec(x)-1)
+ at eprog
+
+\fun{GEN}{mantissa_real}{GEN z, long *e} returns the mantissa $m$ of $z$, and
+sets \kbd{*e} to the exponent $\kbd{bit\_accuracy(lg(z))}-1-\kbd{expo}(z)$,
+so that $z = m / 2^e$.
+
+\misctitle{Low-level} In the following two functions, $s$(ource) and $t$(arget)
+need not be valid \kbd{GEN}s (in practice, they usually point to some part of a
+\typ{REAL} mantissa): they are considered as arrays of words representing some
+mantissa, and we shift globally $s$ by $n > 0$ bits, storing the result in
+$t$. We assume that $m\leq M$ and only access $s[m], s[m+1],\ldots s[M]$
+(read) and likewise for $t$ (write); we may have $s = t$ but more general
+overlaps are not allowed. The word $f$ is concatenated to $s$ to supply extra
+bits.
+
+\fun{void}{shift_left}{GEN t, GEN s, long m, long M, ulong f, ulong n}
+shifts the mantissa
+$$s[m], s[m+1],\ldots s[M], f$$
+left by $n$ bits.
+
+\fun{void}{shift_right}{GEN t, GEN s, long m, long M, ulong f, ulong n}
+shifts the mantissa
+$$f, s[m], s[m+1],\ldots s[M]$$
+right by $n$ bits.
+
+\subsec{Integer valuation}
+For integers $x$ and $p$, such that $x\neq 0$ and $|p| > 1$, we define
+$v_p(x)$ to be the largest integer exponent $e$ such that $p^e$ divides $x$.
+If $p$ is prime, this is the ordinary valuation of $x$ at $p$.
+
+\fun{long}{Z_pvalrem}{GEN x, GEN p, GEN *r} applied to \typ{INT}s
+$\kbd{x}\neq 0$ and~\kbd{p}, $|\kbd{p}| > 1$, returns $e := v_p(x)$
+The quotient $\kbd{x}/\kbd{p}^e$ is returned in~\kbd{*r}. If
+$|\kbd{p}|$ is a prime, \kbd{*r} is the prime-to-\kbd{p} part of~\kbd{x}.
+
+\fun{long}{Z_pval}{GEN x, GEN p} as \kbd{Z\_pvalrem} but only returns
+$v_p(x)$.
+
+\fun{long}{Z_lvalrem}{GEN x, ulong p, GEN *r} as \kbd{Z\_pvalrem},
+except that \kbd{p} is an \kbd{ulong} ($\kbd{p} > 1$).
+
+\fun{long}{Z_lvalrem_stop}{GEN *x, ulong p, int *stop} returns $e := v_p(x)$
+and replaces $x$ by $x / p^e$. Set \kbd{stop} to $1$ if the new value
+of $x$ is $ < p^2$ (and $0$ otherwise). To be used when trial dividing $x$
+by successive primes: the \kbd{stop} condition is cheaply tested while
+testing whether $p$ divides $x$ (is the quotient less than $p$?), and allows
+to decide that $n$ is prime if no prime $< p$ divides $n$. Not memory-clean.
+
+\fun{long}{Z_lval}{GEN x, ulong p} as \kbd{Z\_pval},
+except that \kbd{p} is an \kbd{ulong} ($\kbd{p} > 1$).
+
+\fun{long}{u_lvalrem}{ulong x, ulong p, ulong *r} as \kbd{Z\_pvalrem},
+except the inputs/outputs are now \kbd{ulong}s.
+
+\fun{long}{u_lvalrem_stop}{ulong *n, ulong p, int *stop} as
+\kbd{Z\_pvalrem\_stop}.
+
+\fun{long}{u_pvalrem}{ulong x, GEN p, ulong *r} as \kbd{Z\_pvalrem},
+except \kbd{x} and \kbd{r} are now \kbd{ulong}s.
+
+\fun{long}{u_lval}{ulong x, ulong p} as \kbd{Z\_pval},
+except the inputs are now \kbd{ulong}s.
+
+\fun{long}{u_pval}{ulong x, GEN p} as \kbd{Z\_pval},
+except \kbd{x} is now an \kbd{ulong}.
+
+\fun{long}{z_lval}{long x, ulong p} as \kbd{u\_lval}, for signed \kbd{x}.
+
+\fun{long}{z_lvalrem}{long x, ulong p} as \kbd{u\_lvalrem}, for signed \kbd{x}.
+
+\fun{long}{z_pval}{long x, GEN p} as \kbd{Z\_pval},
+except \kbd{x} is now a \kbd{long}.
+
+\fun{long}{z_pvalrem}{long x, GEN p} as \kbd{Z\_pvalrem},
+except \kbd{x} is now a \kbd{long}.
+
+\fun{long}{Q_pval}{GEN x, GEN p} valuation at the \typ{INT} \kbd{p}
+of the \typ{INT} or \typ{FRAC}~\kbd{x}.
+
+\fun{long}{factorial_lval}{ulong n, ulong p} returns $v_p(n!)$, assuming
+$p$ is prime.
+
+
+The following convenience functions generalize \kbd{Z\_pval} and its variants
+to ``containers'' (\kbd{ZV} and \kbd{ZX}):
+
+
+\fun{long}{ZV_pvalrem}{GEN x, GEN p, GEN *r} $x$ being a \kbd{ZV} (a vector
+of \typ{INT}s), return the min $v$ of the valuations of its components and
+set \kbd{*r} to $x/p^v$. Infinite loop if $x$ is the zero vector.
+This function is not stack clean.
+
+\fun{long}{ZV_pval}{GEN x, GEN p} as \kbd{ZV\_pvalrem} but only returns the
+``valuation''.
+
+\fun{int}{ZV_Z_dvd}{GEN x, GEN p} returns $1$ if $p$ divides all components
+of $x$ and $0$ otherwise. Faster than testing \kbd{ZV\_pval(x,p) >= 1}.
+
+\fun{long}{ZV_lvalrem}{GEN x, ulong p, GEN *px} as \kbd{ZV\_pvalrem},
+except that \kbd{p} is an \kbd{ulong} ($\kbd{p} > 1$).
+This function is not stack-clean.
+
+\fun{long}{ZV_lval}{GEN x, ulong p} as \kbd{ZV\_pval},
+except that \kbd{p} is an \kbd{ulong} ($\kbd{p} > 1$).
+
+
+\fun{long}{ZX_pvalrem}{GEN x, GEN p, GEN *r} as \kbd{ZV\_pvalrem}, for
+a \kbd{ZX} $x$ (a \typ{POL} with \typ{INT} coefficients).
+This function is not stack-clean.
+
+\fun{long}{ZX_pval}{GEN x, GEN p} as \kbd{ZV\_pval} for a \kbd{ZX} $x$.
+
+\fun{long}{ZX_lvalrem}{GEN x, ulong p, GEN *px} as \kbd{ZV\_lvalrem},
+a \kbd{ZX} $x$.
+This function is not stack-clean.
+
+\fun{long}{ZX_lval}{GEN x, ulong p} as \kbd{ZX\_pval},
+except that \kbd{p} is an \kbd{ulong} ($\kbd{p} > 1$).
+
+\subsec{Generic unary operators} Let ``\op'' be a unary operation among
+
+\item \key{neg}: negation ($-x$).
+
+\item \key{abs}: absolute value ($|x|$).
+
+\item \key{sqr}: square ($x^2$).
+
+\noindent The names and prototypes of the low-level functions corresponding
+to \op\ are as follows. The result is of the same type as~\kbd{x}.
+
+\funno{GEN}{\op i}{GEN x} creates the result of \op\ applied to the
+\typ{INT}~\kbd{x}.
+
+\funno{GEN}{\op r}{GEN x} creates the result of \op\ applied to the
+\typ{REAL}~\kbd{x}.
+
+\funno{GEN}{mp\op}{GEN x} creates the result of \op\ applied to the
+\typ{INT} or \typ{REAL}~\kbd{x}.
+
+\noindent Complete list of available functions:
+
+\fun{GEN}{absi}{GEN x}, \fun{GEN}{absr}{GEN x}, \fun{GEN}{mpabs}{GEN x}
+
+\fun{GEN}{negi}{GEN x}, \fun{GEN}{negr}{GEN x}, \fun{GEN}{mpneg}{GEN x}
+
+\fun{GEN}{sqri}{GEN x}, \fun{GEN}{sqrr}{GEN x}, \fun{GEN}{mpsqr}{GEN x}
+
+\fun{GEN}{absi_shallow}{GEN x} $x$ being a \typ{INT}, returns a shallow copy of
+$|x|$, in particular returns $x$ itself when $x \geq 0$, and \kbd{negi($x$)}
+otherwise.
+
+\fun{GEN}{mpabs_shallow}{GEN x} $x$ being a \typ{INT} or a \typ{REAL}, returns
+a shallow copy of $|x|$, in particular returns $x$ itself when $x \geq 0$, and
+\kbd{mpneg($x$)} otherwise.
+
+
+\noindent Some miscellaneous routines:
+
+\fun{GEN}{sqrs}{long x} returns $x^2$.
+
+\fun{GEN}{sqru}{ulong x} returns $x^2$.
+
+\subsec{Comparison operators}
+
+\fun{long}{minss}{long x, long y}
+
+\fun{ulong}{minuu}{ulong x, ulong y}
+
+\fun{double}{mindd}{double x, double y} returns the \kbd{min} of $x$ and $y$.
+
+
+\fun{long}{maxss}{long x, long y}
+
+\fun{ulong}{maxuu}{ulong x, ulong y}
+
+\fun{double}{maxdd}{double x, double y} returns the \kbd{max} of $x$ and $y$.
+
+\smallskip
+
+\fun{int}{mpcmp}{GEN x, GEN y} compares the \typ{INT} or \typ{REAL}~\kbd{x}
+to the \typ{INT} or \typ{REAL}~\kbd{y}. The result is the sign of
+$\kbd{x}-\kbd{y}$.
+
+\fun{int}{cmpii}{GEN x, GEN y} compares the \typ{INT} \kbd{x} to the
+\typ{INT}~\kbd{y}.
+
+\fun{int}{cmpir}{GEN x, GEN y} compares the \typ{INT} \kbd{x} to the
+\typ{REAL}~\kbd{y}.
+
+\fun{int}{cmpis}{GEN x, long s} compares the \typ{INT}~\kbd{x} to the
+\kbd{long}~\kbd{s}.
+
+\fun{int}{cmpsi}{long s, GEN x} compares the \kbd{long}~\kbd{s} to the
+\typ{INT}~\kbd{x}.
+
+\fun{int}{cmpsr}{long s, GEN x} compares the \kbd{long}~\kbd{s} to the
+\typ{REAL}~\kbd{x}.
+
+\fun{int}{cmpri}{GEN x, GEN y} compares the \typ{REAL}~\kbd{x} to the
+\typ{INT}~\kbd{y}.
+
+\fun{int}{cmprr}{GEN x, GEN y} compares the \typ{REAL}~\kbd{x} to the
+\typ{REAL}~\kbd{y}.
+
+\fun{int}{cmprs}{GEN x, long s} compares the \typ{REAL}~\kbd{x} to the
+\kbd{long}~\kbd{s}.
+
+\fun{int}{equalii}{GEN x, GEN y} compares the \typ{INT}s \kbd{x} and~\kbd{y}.
+The result is $1$ if $\kbd{x} = \kbd{y}$, $0$ otherwise.
+
+\fun{int}{equalrr}{GEN x, GEN y} compares the \typ{REAL}s \kbd{x} and~\kbd{y}.
+The result is $1$ if $\kbd{x} = \kbd{y}$, $0$ otherwise. Equality is decided
+according to the following rules: all real zeroes are equal, and
+different from a non-zero real; two non-zero reals are equal if all their
+digits coincide up to the length of the shortest of the two, and the
+remaining words in the mantissa of the longest are all $0$.
+
+\fun{int}{equalsi}{long s, GEN x}
+
+\fun{int}{equalis}{GEN x, long s} compare the \typ{INT} \kbd{x} and
+the \kbd{long}~\kbd{s}. The result is $1$ if $\kbd{x} = \kbd{y}$, $0$ otherwise.
+
+The remaining comparison operators disregard the sign of their operands:
+
+\fun{int}{equalui}{ulong s, GEN x}
+
+\fun{int}{equaliu}{GEN x, ulong s} compare the absolute value of the
+\typ{INT} \kbd{x} and the \kbd{ulong}~\kbd{s}. The result is $1$ if
+$|\kbd{x}| = \kbd{y}$, $0$ otherwise.
+
+\fun{int}{cmpui}{ulong u, GEN x}
+
+\fun{int}{cmpiu}{GEN x, ulong u} compare the absolute value of the
+\typ{INT} \kbd{x} and the \kbd{ulong}~\kbd{s}.
+
+\fun{int}{absi_cmp}{GEN x, GEN y} compares the \typ{INT}s \kbd{x} and~\kbd{y}.
+The result is the sign of $|\kbd{x}| - |\kbd{y}|$.
+
+\fun{int}{absi_equal}{GEN x, GEN y} compares the \typ{INT}s \kbd{x}
+and~\kbd{y}. The result is $1$ if $|\kbd{x}| = |\kbd{y}|$, $0$ otherwise.
+
+\fun{int}{absr_cmp}{GEN x, GEN y} compares the \typ{REAL}s \kbd{x} and~\kbd{y}.
+The result is the sign of $|\kbd{x}| - |\kbd{y}|$.
+
+\fun{int}{absrnz_equal2n}{GEN x} tests whether a non-zero \typ{REAL} \kbd{x}
+is equal to $\pm 2^e$ for some integer $e$.
+
+\fun{int}{absrnz_equal1}{GEN x} tests whether a non-zero \typ{REAL} \kbd{x}
+is equal to $\pm 1$.
+
+\subsec{Generic binary operators}\label{se:genbinop} The operators in this
+section have arguments of C-type \kbd{GEN}, \kbd{long}, and \kbd{ulong}, and
+only \typ{INT} and \typ{REAL} \kbd{GEN}s are allowed. We say an argument is a
+real type if it is a \typ{REAL} \kbd{GEN}, and an integer type otherwise. The
+result is always a \typ{REAL} unless both \kbd{x} and \kbd{y} are integer
+types.
+
+Let ``\op'' be a binary operation among
+
+\item \key{add}: addition (\kbd{x + y}).
+
+\item \key{sub}: subtraction (\kbd{x - y}).
+
+\item \key{mul}: multiplication (\kbd{x * y}).
+
+\item \key{div}: division (\kbd{x / y}). In the case where \kbd{x} and \kbd{y}
+are both integer types, the result is the Euclidean quotient, where the
+remainder has the same sign as the dividend~\kbd{x}. It is the ordinary
+division otherwise. A division-by-$0$ error occurs if \kbd{y} is equal to
+$0$.
+
+The last two generic operations are defined only when arguments have integer
+types; and the result is a \typ{INT}:
+
+\item \key{rem}: remainder (``\kbd{x \% y}''). The result is the Euclidean
+remainder corresponding to \kbd{div},~i.e. its sign is that of the
+dividend~\kbd{x}.
+
+\item \key{mod}: true remainder (\kbd{x \% y}). The result is the true
+Euclidean remainder, i.e.~non-negative and less than the absolute value
+of~\kbd{y}.
+
+\misctitle{Important technical note} The rules given above fixing the output
+type (to \typ{REAL} unless both inputs are integer types) are subtly
+incompatible with the general rules obeyed by PARI's generic functions, such
+as \kbd{gmul} or \kbd{gdiv} for instance: the latter return a result
+containing as much information as could be deduced from the inputs, so it is
+not true that if $x$ is a \typ{INT} and $y$ a \typ{REAL}, then
+\kbd{gmul(x,y)} is always the same as \kbd{mulir(x,y)}. The exception
+is $x = 0$, in that case we can deduce that the result is an exact $0$,
+so \kbd{gmul} returns \kbd{gen\_0}, while \kbd{mulir} returns a
+\typ{REAL} $0$. Specifically, the one resulting from the conversion of
+\kbd{gen\_0} to a \typ{REAL} of precision \kbd{precision(y)}, multiplied by
+$y$; this determines the exponent of the real $0$ we obtain.
+
+The reason for the discrepancy between the two rules is that we use the two
+sets of functions in different contexts: generic functions allow to write
+high-level code forgetting about types, letting PARI return results which are
+sensible and as simple as possible; type specific functions are used in
+kernel programming, where we do care about types and need to maintain strict
+consistency: it is much easier to compute the types of results when they are
+determined from the types of the inputs only (without taking into account
+further arithmetic properties, like being non-0).
+\smallskip
+
+The names and prototypes of the low-level functions corresponding
+to \op\ are as follows. In this section, the \kbd{z} argument in the
+\kbd{z}-functions must be of type \typ{INT} when no \kbd{r} or \kbd{mp}
+appears in the argument code (no \typ{REAL} operand is involved, only integer
+types), and of type \typ{REAL} otherwise.
+
+\funno{GEN}{mp\op[z]}{GEN x, GEN y[, GEN z]} applies \op\ to
+the \typ{INT} or \typ{REAL} \kbd{x} and~\kbd{y}. The function
+\kbd{mpdivz} does not exist (its semantic would change drastically
+depending on the type of the \kbd{z} argument), and neither do
+\kbd{mprem[z]} nor \kbd{mpmod[z]} (specific to integers).
+
+\funno{GEN}{\op si[z]}{long s, GEN x[, GEN z]} applies \op\ to the
+\kbd{long}~\kbd{s} and the \typ{INT}~\kbd{x}.
+ These functions always return the global constant
+\kbd{gen\_0} (not a copy) when the sign of the result is $0$.
+
+\funno{GEN}{\op sr[z]}{long s, GEN x[, GEN z]} applies \op\ to the
+\kbd{long}~\kbd{s} and the \typ{REAL}~\kbd{x}.
+
+\funno{GEN}{\op ss[z]}{long s, long t[, GEN z]} applies \op\ to the longs
+\kbd{s} and~\kbd{t}. These functions always return the global constant
+\kbd{gen\_0} (not a copy) when the sign of the result is $0$.
+
+\funno{GEN}{\op ii[z]}{GEN x, GEN y[, GEN z]} applies \op\ to the
+\typ{INT}s \kbd{x} and~\kbd{y}. These functions always return the global
+constant \kbd{gen\_0} (not a copy) when the sign of the result is $0$.
+
+\funno{GEN}{\op ir[z]}{GEN x, GEN y[, GEN z]} applies \op\ to the
+\typ{INT} \kbd{x} and the \typ{REAL}~\kbd{y}.
+
+\funno{GEN}{\op is[z]}{GEN x, long s[, GEN z]} applies \op\ to the
+\typ{INT}~\kbd{x} and the \kbd{long}~\kbd{s}. These functions always return
+the global constant \kbd{gen\_0} (not a copy) when the sign of the result
+is $0$.
+
+\funno{GEN}{\op ri[z]}{GEN x, GEN y[, GEN z]} applies \op\ to the
+\typ{REAL}~\kbd{x} and the \typ{INT}~\kbd{y}.
+
+\funno{GEN}{\op rr[z]}{GEN x, GEN y[, GEN z]} applies \op\ to the
+\typ{REAL}s~\kbd{x} and~\kbd{y}.
+
+\funno{GEN}{\op rs[z]}{GEN x, long s[, GEN z]} applies \op\ to the
+\typ{REAL}~\kbd{x} and the \kbd{long}~\kbd{s}.
+
+\noindent Some miscellaneous routines:
+
+\fun{long}{expu}{ulong x} assuming $x > 0$, returns the binary exponent of
+the real number equal to $x$. This is a special case of \kbd{gexpo}.
+
+\fun{GEN}{adduu}{ulong x, ulong y}
+
+\fun{GEN}{addiu}{GEN x, ulong y}
+
+\fun{GEN}{addui}{ulong x, GEN y} adds \kbd{x} and \kbd{y}.
+
+\fun{GEN}{subuu}{ulong x, ulong y}
+
+\fun{GEN}{subiu}{GEN x, ulong y}
+
+\fun{GEN}{subui}{ulong x, GEN y} subtracts \kbd{x} by \kbd{y}.
+
+\fun{GEN}{muluu}{ulong x, ulong y} multiplies \kbd{x} by \kbd{y}.
+
+\fun{GEN}{mului}{ulong x, GEN y} multiplies \kbd{x} by \kbd{y}.
+
+\fun{GEN}{muluui}{ulong x, ulong y, GEN z} return $xyz$.
+
+\fun{GEN}{muliu}{GEN x, ulong y} multiplies \kbd{x} by \kbd{y}.
+
+\fun{void}{addumului}{ulong a, ulong b, GEN x} return $a + b|X|$.
+
+\fun{GEN}{addmuliu}{GEN x, GEN y, ulong u} returns $x +yu$.
+
+\fun{GEN}{addmulii}{GEN x, GEN y, GEN z} returns $x + yz$.
+
+\fun{GEN}{addmulii_inplace}{GEN x, GEN y, GEN z} returns $x + yz$, but
+returns $x$ itself and not a copy if $yz = 0$. Not suitable for
+\tet{gerepile} or \tet{gerepileupto}.
+
+\fun{GEN}{addmuliu_inplace}{GEN x, GEN y, ulong u} returns $x +yu$, but
+returns $x$ itself and not a copy if $yu = 0$. Not suitable for
+\tet{gerepile} or \tet{gerepileupto}.
+
+\fun{GEN}{submuliu_inplace}{GEN x, GEN y, ulong u} returns $x- yu$, but
+returns $x$ itself and not a copy if $yu = 0$. Not suitable for
+\tet{gerepile} or \tet{gerepileupto}.
+
+\fun{GEN}{lincombii}{GEN u, GEN v, GEN x, GEN y} returns $ux + vy$.
+
+\fun{GEN}{mulsubii}{GEN y, GEN z, GEN x} returns $yz - x$.
+
+\fun{GEN}{submulii}{GEN x, GEN y, GEN z} returns $x - yz$.
+
+\fun{GEN}{submuliu}{GEN x, GEN y, ulong u} returns $x -yu$.
+
+\fun{GEN}{mulu_interval}{ulong a, ulong b} returns $a(a+1)\cdots b$, assuming
+that $a \leq b$. Very inefficient when $a = 0$.
+
+\fun{GEN}{invr}{GEN x} returns the inverse of the non-zero \typ{REAL}~$x$.
+
+\fun{GEN}{truedivii}{GEN x, GEN y} returns the true Euclidean quotient
+(with non-negative remainder less than $|y|$).
+
+\fun{GEN}{truedivis}{GEN x, long y} returns the true Euclidean quotient
+(with non-negative remainder less than $|y|$).
+
+\fun{GEN}{truedivsi}{long x, GEN y} returns the true Euclidean quotient
+(with non-negative remainder less than $|y|$).
+
+\fun{GEN}{centermodii}{GEN x, GEN y, GEN y2}, given
+\typ{INT}s \kbd{x}, \kbd{y}, returns $z$ congruent to \kbd{x} modulo \kbd{y},
+such that $-\kbd{y}/2 \leq z < \kbd{y}/2$. The function requires an extra
+argument \kbd{y2}, such that \kbd{y2 = shifti(y, -1)}. (In most cases, \kbd{y}
+is constant for many reductions and \kbd{y2} need only be computed once.)
+
+\fun{GEN}{remi2n}{GEN x, long n} returns \kbd{x} mod $2^n$.
+
+\fun{GEN}{addii_sign}{GEN x, long sx, GEN y, long sy} add the \typ{INT}s
+$x$ and $y$ as if their signs were \kbd{sx} and \kbd{sy}.
+
+\fun{GEN}{addir_sign}{GEN x, long sx, GEN y, long sy}
+add the \typ{INT} $x$ and the \typ{REAL} $y$ as if their signs were \kbd{sx}
+and \kbd{sy}.
+
+\fun{GEN}{addrr_sign}{GEN x, long sx, GEN y, long sy} add the \typ{REAL}s $x$
+and $y$ as if their signs were \kbd{sx} and \kbd{sy}.
+
+\fun{GEN}{addsi_sign}{long x, GEN y, long sy} add $x$ and the \typ{INT} $y$
+as if its sign was \kbd{sy}.
+
+\fun{GEN}{addui_sign}{ulong x, GEN y, long sy} add $x$ and the \typ{INT} $y$
+as if its sign was \kbd{sy}.
+
+\subsec{Exact division and divisibility}
+
+\fun{GEN}{diviiexact}{GEN x, GEN y} returns the Euclidean quotient
+$\kbd{x} / \kbd{y}$, assuming $\kbd{y}$ divides $\kbd{x}$. Uses Jebelean
+algorithm (Jebelean-Krandick bidirectional exact division is not
+implemented).
+
+\fun{GEN}{diviuexact}{GEN x, ulong y} returns the Euclidean quotient
+$\kbd{x} / \kbd{y}$, assuming $\kbd{y}$ divides
+$\kbd{x}$ and $\kbd{y}$ is non-zero.
+
+\fun{GEN}{diviuuexact}{GEN x, ulong y, ulong z} returns the Euclidean
+quotient $x/(yz)$, assuming $yz$ divides $x$ and $yz \neq 0$.
+
+The following routines return 1 (true) if \kbd{y} divides \kbd{x}, and
+0 otherwise. (Error if $y$ is $0$, even if $x$ is $0$.) All \kbd{GEN} are
+assumed to be \typ{INT}s:
+
+\fun{int}{dvdii}{GEN x, GEN y},
+\fun{int}{dvdis}{GEN x, long y},
+\fun{int}{dvdiu}{GEN x, ulong y},
+
+\fun{int}{dvdsi}{long x, GEN y},
+\fun{int}{dvdui}{ulong x, GEN y}.
+
+The following routines return 1 (true) if \kbd{y} divides \kbd{x}, and in
+that case assign the quotient to \kbd{z}; otherwise they return 0. All
+\kbd{GEN} are assumed to be \typ{INT}s:
+
+\fun{int}{dvdiiz}{GEN x, GEN y, GEN z},
+\fun{int}{dvdisz}{GEN x, long y, GEN z}.
+
+\fun{int}{dvdiuz}{GEN x, ulong y, GEN z} if \kbd{y} divides \kbd{x}, assigns
+the quotient $|\kbd{x}|/\kbd{y}$ to \kbd{z} and returns 1 (true), otherwise
+returns 0 (false).
+
+\subsec{Division with integral operands and \typ{REAL} result}
+
+\fun{GEN}{rdivii}{GEN x, GEN y, long prec}, assuming $x$ and $y$
+are both of type \typ{INT}, return the quotient $x/y$ as a \typ{REAL} of
+precision \kbd{prec}.
+
+\fun{GEN}{rdiviiz}{GEN x, GEN y, GEN z}, assuming $x$ and $y$
+are both of type \typ{INT}, and $z$ is a \typ{REAL},
+assign the quotient $x/y$ to $z$.
+
+\fun{GEN}{rdivis}{GEN x, long y, long prec}, assuming \kbd{x}
+is of type \typ{INT}, return the quotient x/y as a \typ{REAL} of
+precision \kbd{prec}.
+
+\fun{GEN}{rdivsi}{long x, GEN y, long prec}, assuming \kbd{y}
+is of type \typ{INT}, return the quotient x/y as a \typ{REAL} of
+precision \kbd{prec}.
+
+\fun{GEN}{rdivss}{long x, long y, long prec}, return the quotient x/y as a
+\typ{REAL} of precision \kbd{prec}.
+
+
+\subsec{Division with remainder} The following functions return two objects,
+unless specifically asked for only one of them~--- a quotient and a remainder.
+The quotient is returned and the remainder is returned through the variable
+whose address is passed as the \kbd{r} argument. The term \emph{true
+Euclidean remainder} refers to the non-negative one (\kbd{mod}), and
+\emph{Euclidean remainder} by itself to the one with the same sign as the
+dividend (\kbd{rem}). All \kbd{GEN}s, whether returned directly or through a
+pointer, are created on the stack.
+
+\fun{GEN}{dvmdii}{GEN x, GEN y, GEN *r} returns the Euclidean quotient of the
+\typ{INT}~\kbd{x} by a \typ{INT}~\kbd{y} and puts the remainder
+into~\kbd{*r}. If \kbd{r} is equal to \kbd{NULL}, the remainder is not
+created, and if \kbd{r} is equal to  \kbd{ONLY\_REM}, only the remainder is
+created and returned. In the generic case, the remainder is created after the
+quotient and can be disposed of individually with a \kbd{cgiv(r)}. The
+remainder is always of the sign of the dividend~\kbd{x}. If the remainder
+is $0$ set \kbd{r = gen\_0}.
+
+\fun{void}{dvmdiiz}{GEN x, GEN y, GEN z, GEN t} assigns the Euclidean
+quotient of the \typ{INT}s \kbd{x} and \kbd{y} into the \typ{INT}~\kbd{z},
+and the Euclidean remainder into the \typ{INT}~\kbd{t}.
+
+\noindent Analogous routines \tet{dvmdis}\kbd{[z]}, \tet{dvmdsi}\kbd{[z]},
+\tet{dvmdss}\kbd{[z]} are available, where \kbd{s} denotes a \kbd{long}
+argument. But the following routines are in general more flexible:
+
+\fun{long}{sdivss_rem}{long s, long t, long *r} computes the Euclidean
+quotient and remainder of the longs \kbd{s} and~\kbd{t}. Puts the remainder
+into \kbd{*r}, and returns the quotient. The remainder is of the sign of the
+dividend~\kbd{s}, and has strictly smaller absolute value than~\kbd{t}.
+
+\fun{long}{sdivsi_rem}{long s, GEN x, long *r} computes the Euclidean
+quotient and remainder of the \kbd{long}~\kbd{s} by the \typ{INT}~\kbd{x}. As
+\kbd{sdivss\_rem} otherwise.
+
+\fun{long}{sdivsi}{long s, GEN x} as \kbd{sdivsi\_rem}, without
+remainder.
+
+\fun{GEN}{divis_rem}{GEN x, long s, long *r} computes the Euclidean quotient
+and remainder of the \typ{INT}~\kbd{x} by the \kbd{long}~\kbd{s}. As
+\kbd{sdivss\_rem} otherwise.
+
+\fun{GEN}{diviu_rem}{GEN x, ulong s, ulong *r} computes the Euclidean quotient
+and remainder of \emph{absolute value} of the \typ{INT}~\kbd{x} by the
+\kbd{ulong}~\kbd{s}. As \kbd{sdivss\_rem} otherwise.
+
+\fun{ulong}{udiviu_rem}{GEN n, ulong d, ulong *r} as \tet{diviu_rem}, assuming
+that $|n|/d$ fits into an \kbd{ulong}.
+
+\fun{ulong}{udivui_rem}{ulong x, GEN y, ulong *rem}
+computes the Euclidean quotient and remainder of $x$ by $y$. As
+\kbd{sdivss\_rem} otherwise.
+
+\fun{ulong}{udivuu_rem}{ulong x, ulong y, ulong *rem}
+computes the Euclidean quotient and remainder of $x$ by $y$. As
+\kbd{sdivss\_rem} otherwise.
+
+\fun{GEN}{divsi_rem}{long s, GEN y, long *r} computes the Euclidean quotient
+and remainder of the \typ{long}~\kbd{s} by the \kbd{GEN}~\kbd{y}. As
+\kbd{sdivss\_rem} otherwise.
+
+\fun{GEN}{divss_rem}{long x, long y, long *r} computes the Euclidean quotient
+and remainder of the \typ{long}~\kbd{x} by the \kbd{long}~\kbd{y}. As
+\kbd{sdivss\_rem} otherwise.
+\smallskip
+\fun{GEN}{truedvmdii}{GEN x, GEN y, GEN *r}, as \kbd{dvmdii} but with a
+non-negative remainder.
+
+\fun{GEN}{truedvmdis}{GEN x, long y, GEN *z}, as \kbd{dvmdis} but with a
+non-negative remainder.
+
+\fun{GEN}{truedvmdsi}{long x, GEN y, GEN *z}, as \kbd{dvmdsi} but with a
+non-negative remainder.
+
+\subsec{Modulo to longs} The following variants of \kbd{modii} do not
+clutter the stack:
+
+\fun{long}{smodis}{GEN x, long y} computes the true Euclidean
+remainder of the \typ{INT}~\kbd{x} by the \kbd{long}~\kbd{y}. This is the
+non-negative remainder, not the one whose sign is the sign of \kbd{x}
+as in the \kbd{div} functions.
+
+\fun{long}{smodss}{long x, long y} computes the true Euclidean
+remainder of the \kbd{long}~\kbd{x} by a \kbd{long}~\kbd{y}.
+
+\fun{ulong}{umodiu}{GEN x, ulong y} computes the true Euclidean
+remainder of the \typ{INT}~\kbd{x} by the \kbd{ulong}~\kbd{y}.
+
+\fun{ulong}{umodui}{ulong x, GEN y} computes the true Euclidean
+remainder of the \kbd{ulong}~\kbd{x} by the \typ{INT}~\kbd{|y|}.
+
+The routine \tet{smodsi} does not exist, since it would not always be
+defined: for a \emph{negative} \kbd{x}, if the quotient is $\pm1$, the result
+\kbd{x + |y|} would in general not fit into a \kbd{long}. Use either
+\kbd{umodui} or \kbd{modsi}.
+
+\subsec{Powering, Square root}
+
+\fun{GEN}{powii}{GEN x, GEN n}, assumes $x$ and $n$ are \typ{INT}s and
+returns $x^n$.
+
+\fun{GEN}{powuu}{ulong x, ulong n}, returns $x^n$.
+
+\fun{GEN}{powiu}{GEN x, ulong n}, assumes $x$ is a \typ{INT} and returns $x^n$.
+
+\fun{GEN}{powis}{GEN x, long n}, assumes $x$ is a \typ{INT} and returns $x^n$
+(possibly a \typ{FRAC} if $n < 0$).
+
+\fun{GEN}{powrs}{GEN x, long n}, assumes $x$ is a \typ{REAL} and returns
+$x^n$. This is considered as a sequence of \kbd{mulrr}, possibly empty:
+as such the result has type \typ{REAL}, even if $n = 0$.
+Note that the generic function \kbd{gpowgs(x,0)} would return \kbd{gen\_1},
+see the technical note in \secref{se:genbinop}.
+
+\fun{GEN}{powru}{GEN x, ulong n}, assumes $x$ is a \typ{REAL} and returns $x^n$
+(always a \typ{REAL}, even if $n = 0$).
+
+\fun{GEN}{powruvec}{GEN e, ulong n}. Given a \typ{REAL} $e$, return the vector
+of all $e^i$, $1 \leq i \leq n$.
+
+\fun{GEN}{powrshalf}{GEN x, long n}, assumes $x$ is a \typ{REAL} and returns
+$x^{n/2}$ (always a \typ{REAL}, even if $n = 0$).
+
+\fun{GEN}{powruhalf}{GEN x, ulong n}, assumes $x$ is a \typ{REAL} and returns
+$x^{n/2}$ (always a \typ{REAL}, even if $n = 0$).
+
+\fun{GEN}{powrfrac}{GEN x, long n, long d}, assumes $x$ is a \typ{REAL} and
+returns $x^{n/d}$ (always a \typ{REAL}, even if $n = 0$).
+
+\fun{GEN}{powIs}{long n} returns $I^n\in\{1,I,-1,-I\}$ (\typ{INT} for even $n$,
+\typ{COMPLEX} otherwise).
+
+\fun{ulong}{upowuu}{ulong x, ulong n}, returns $x^n$ when $< 2^\B$, and $0$
+otherwise (overflow).
+
+\fun{GEN}{sqrtremi}{GEN N, GEN *r}, returns the integer square root $S$ of
+the non-negative \typ{INT}~\kbd{N} (rounded towards 0) and puts the remainder
+$R$ into~\kbd{*r}. Precisely, $N = S^2 + R$ with $0\leq R \leq 2S$. If
+\kbd{r} is equal to \kbd{NULL}, the remainder is not created. In the generic
+case, the remainder is created after the quotient and can be disposed of
+individually with \kbd{cgiv(R)}. If the remainder is $0$ set \kbd{R = gen\_0}.
+
+Uses a divide and conquer algorithm (discrete variant of Newton iteration)
+due to Paul Zimmermann (``Karatsuba Square Root'', INRIA Research Report 3805
+(1999)).
+
+\fun{GEN}{sqrti}{GEN N}, returns the integer square root $S$ of
+the non-negative \typ{INT}~\kbd{N} (rounded towards 0). This is identical
+to \kbd{sqrtremi(N, NULL)}.
+
+\subsec{GCD, extended GCD and LCM}
+
+\fun{long}{cgcd}{long x, long y} returns the GCD of \kbd{x} and \kbd{y}.
+
+\fun{ulong}{ugcd}{ulong x, ulong y} returns the GCD of \kbd{x} and \kbd{y}.
+
+\fun{long}{clcm}{long x, long y} returns the LCM of \kbd{x} and \kbd{y},
+provided it fits into a \kbd{long}. Silently overflows otherwise.
+
+\fun{GEN}{gcdii}{GEN x, GEN y}, returns the GCD of the \typ{INT}s \kbd{x} and
+\kbd{y}.
+
+\fun{GEN}{lcmii}{GEN x, GEN y}, returns the LCM of the \typ{INT}s \kbd{x} and
+\kbd{y}.
+
+\fun{GEN}{bezout}{GEN a,GEN b, GEN *u,GEN *v}, returns the GCD $d$ of
+\typ{INT}s \kbd{a} and \kbd{b} and sets \kbd{u}, \kbd{v} to the Bezout
+coefficients such that $\kbd{au} + \kbd{bv} = d$.
+
+\fun{long}{cbezout}{long a,long b, long *u,long *v}, returns the GCD
+$d$ of \kbd{a} and \kbd{b} and sets \kbd{u}, \kbd{v} to the Bezout coefficients
+such that $\kbd{au} + \kbd{bv} = d$.
+
+\fun{GEN}{ZV_gcdext}{GEN A} given a vector of $n$ integers $A$, returns $[d,
+U]$, where $d$ is the GCD of the $A[i]$ and $U$ is a matrix
+in $\text{GL}_n(\Z)$ such that $AU = [0,\dots,0,D]$.
+
+\subsec{Pseudo-random integers}
+These routine return pseudo-random integers uniformly distributed in some
+interval. The all use the same underlying generator which can be seeded and
+restarted using \tet{getrand} and \tet{setrand}.
+
+\fun{void}{setrand}{GEN seed} reseeds the random number generator using the
+seed $n$. The seed is either a technical array output by \kbd{getrand}
+or a small positive integer, used to generate deterministically a suitable
+state array. For instance, running a randomized computation starting by
+\kbd{setrand(1)} twice will generate the exact same output.
+
+\fun{GEN}{getrand}{void} returns the current value of the seed used by the
+pseudo-random number generator \tet{random}. Useful mainly for debugging
+purposes, to reproduce a specific chain of computations. The returned value
+is technical (reproduces an internal state array of type \typ{VECSMALL}),
+and can only be used as an argument to \tet{setrand}.
+
+\fun{ulong}{pari_rand}{void} returns a random $0 \leq x < 2^\B$.
+
+\fun{long}{random_bits}{long k} returns a random $0 \leq x < 2^k$. Assumes
+that $0 \leq k \leq \B$.
+
+\fun{ulong}{random_Fl}{ulong p} returns a pseudo-random integer
+in $0, 1, \dots p-1$.
+
+\fun{GEN}{randomi}{GEN n} returns a random \typ{INT} between $0$ and $\kbd{n}
+- 1$.
+
+\fun{GEN}{randomr}{long prec} returns a random \typ{REAL} in $[0,1[$, with
+precision \kbd{prec}.
+
+\subsec{Modular operations} In this subsection, all \kbd{GEN}s are \typ{INT}
+
+\fun{GEN}{Fp_red}{GEN a, GEN m} returns \kbd{a} modulo \kbd{m} (smallest
+non-negative residue). (This is identical to modii).
+
+\fun{GEN}{Fp_neg}{GEN a, GEN m} returns $-$\kbd{a} modulo \kbd{m} (smallest
+non-negative residue).
+
+\fun{GEN}{Fp_add}{GEN a, GEN b, GEN m} returns the sum of \kbd{a} and
+\kbd{b} modulo \kbd{m} (smallest non-negative residue).
+
+\fun{GEN}{Fp_sub}{GEN a, GEN b, GEN m} returns the difference of \kbd{a} and
+\kbd{b} modulo \kbd{m} (smallest non-negative residue).
+
+\fun{GEN}{Fp_center}{GEN a, GEN p, GEN pov2} assuming that \kbd{pov2} is
+\kbd{shifti(p,-1)} and that \kbd{a} is between $0$ and $\kbd{p} - 1$ and,
+returns the representative of \kbd{a} in the symmetric residue system.
+
+\fun{GEN}{Fp_mul}{GEN a, GEN b, GEN m} returns the product of \kbd{a} by
+\kbd{b} modulo \kbd{m} (smallest non-negative residue).
+
+\fun{GEN}{Fp_addmul}{GEN x, GEN y, GEN z, GEN p} returns $x + yz$.
+
+\fun{GEN}{Fp_mulu}{GEN a, ulong b, GEN m} returns the product of \kbd{a} by
+\kbd{b} modulo \kbd{m} (smallest non-negative residue).
+
+\fun{GEN}{Fp_muls}{GEN a, long b, GEN m} returns the product of \kbd{a} by
+\kbd{b} modulo \kbd{m} (smallest non-negative residue).
+
+\fun{GEN}{Fp_sqr}{GEN a, GEN m} returns $\kbd{a}^2$ modulo \kbd{m} (smallest
+non-negative residue).
+
+\fun{ulong}{Fp_powu}{GEN x, ulong n, GEN m} raises \kbd{x} to the \kbd{n}-th
+power modulo \kbd{m} (smallest non-negative residue). Not memory-clean, but
+suitable for \kbd{gerepileupto}.
+
+\fun{ulong}{Fp_pows}{GEN x, long n, GEN m} raises \kbd{x} to the \kbd{n}-th
+power modulo \kbd{m} (smallest non-negative residue). A negative \kbd{n} is
+allowed Not memory-clean, but suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{Fp_pow}{GEN x, GEN n, GEN m} returns $\kbd{x}^\kbd{n}$
+modulo \kbd{m} (smallest non-negative residue).
+
+\fun{GEN}{Fp_inv}{GEN a, GEN m} returns an inverse of \kbd{a} modulo \kbd{m}
+(smallest non-negative residue). Raise an error if \kbd{a} is not invertible.
+
+\fun{GEN}{Fp_invsafe}{GEN a, GEN m} as \kbd{Fp\_inv}, but return
+\kbd{NULL} if \kbd{a} is not invertible.
+
+\fun{GEN}{FpV_inv}{GEN x, GEN m} $x$ being a vector of \typ{INT}s, return
+the vector of inverses of the $x[i]$ mod $m$. The routine uses Montgomery's
+trick, and involves a single inversion mod $m$, plus $3(N-1)$ multiplications
+for $N$ entries. The routine is not stack-clean: $2N$ integers mod $m$
+are left on stack, besides the $N$ in the result.
+
+\fun{GEN}{Fp_div}{GEN a, GEN b, GEN m} returns the quotient of \kbd{a} by
+\kbd{b} modulo \kbd{m} (smallest non-negative residue). Raise an error if
+\kbd{b} is not invertible.
+
+\fun{int}{invmod}{GEN a, GEN m, GEN *g},  return $1$ if \kbd{a}
+modulo \kbd{m} is invertible, else return $0$ and set
+$\kbd{g} = \gcd(\kbd{a},\kbd{m})$.
+
+\fun{GEN}{Fp_log}{GEN a, GEN g, GEN ord, GEN p} Let $g$ such that
+$g^{ord} \equiv 1 \pmod{p}$. Return an integer $e$ such that
+$a^e \equiv g \pmod{p}$. If $e$ does not exist, the result is currently
+undefined.
+
+\fun{GEN}{Fp_order}{GEN a, GEN N, GEN p} returns the order of the
+\typ{Fp} \kbd{a}. If \kbd{N} is non-\kbd{NULL}, it is assumed that \kbd{N}
+is a multiple of the order of \kbd{a}, as a \typ{INT} or a
+factorization matrix.
+
+\fun{GEN}{Fp_factored_order}{GEN a, GEN N, GEN p} returns $[o,F]$, where $o$
+is the multiplicative order of the \kbd{Fp} $a$ in $\F_p^*$, and $F$ is the
+factorization of $o$. If \kbd{N} is non-\kbd{NULL}, it is assumed that
+\kbd{N} is a multiple of the order of \kbd{a}, as a \typ{INT} or a
+factorization matrix.
+
+\fun{int}{Fp_issquare}{GEN x, GEN p} returns $1$ if \kbd{x} is a square
+modulo \kbd{p}, and $0$ otherwise.
+
+\fun{int}{Fp_ispower}{GEN x, GEN n, GEN p} returns $1$ if \kbd{x} is an
+$n$-th power modulo \kbd{p}, and $0$ otherwise.
+
+\fun{GEN}{Fp_sqrt}{GEN x, GEN p} returns a square root of \kbd{x} modulo
+\kbd{p} (the smallest non-negative residue), where \kbd{x}, \kbd{p} are
+\typ{INT}s, and \kbd{p} is assumed to be prime. Return \kbd{NULL}
+if \kbd{x} is not a quadratic residue modulo \kbd{p}.
+
+\fun{GEN}{Fp_sqrtn}{GEN x, GEN n, GEN p, GEN *zn} returns an \kbd{n}-th
+root of $\kbd{x}$ modulo \kbd{p} (smallest non-negative residue), where
+\kbd{x}, \kbd{n}, \kbd{p} are \typ{INT}s, and \kbd{p} is assumed to be prime.
+Return \kbd{NULL} if \kbd{x} is not an \kbd{n}-th power residue. Otherwise,
+if \kbd{zn} is non-\kbd{NULL} set it to a primitive \kbd{n}-th root of $1$.
+
+\fun{GEN}{Zn_sqrt}{GEN x, GEN n} returns one of the square roots of \kbd{x}
+modulo \kbd{n} (possibly not prime), where \kbd{x} is a \typ{INT} and \kbd{n}
+is either a \typ{INT} or is given by its factorisation matrix.  Return
+\kbd{NULL} if no such square root exist.
+
+\fun{long}{kross}{long x, long y} returns the \idx{Kronecker symbol} $(x|y)$,
+i.e.$-1$, $0$ or $1$. If \kbd{y} is an odd prime, this is the \idx{Legendre
+symbol}. (Contrary to \kbd{krouu}, \kbd{kross} also supports $\kbd{y} = 0$)
+
+\fun{long}{krouu}{ulong x, ulong y} returns the \idx{Kronecker symbol}
+$(x|y)$, i.e.~$-1$, $0$ or $1$. Assumes \kbd{y} is non-zero. If \kbd{y} is an
+odd prime, this is the \idx{Legendre symbol}.
+
+\fun{long}{krois}{GEN x, long y} returns the \idx{Kronecker symbol} $(x|y)$
+of \typ{INT}~x and \kbd{long}~\kbd{y}. As \kbd{kross} otherwise.
+
+\fun{long}{kroiu}{GEN x, ulong y} returns the \idx{Kronecker symbol} $(x|y)$
+of \typ{INT}~x and non-zero \kbd{ulong}~\kbd{y}. As \kbd{krouu} otherwise.
+
+\fun{long}{krosi}{long x, GEN y} returns the \idx{Kronecker symbol} $(x|y)$
+of \kbd{long}~x and \typ{INT}~\kbd{y}. As \kbd{kross} otherwise.
+
+\fun{long}{kronecker}{GEN x, GEN y} returns the \idx{Kronecker symbol} $(x|y)$
+of \typ{INT}s~x and~\kbd{y}. As \kbd{kross} otherwise.
+
+\fun{GEN}{pgener_Fp}{GEN p} returns the smallest primitive root modulo
+\kbd{p}, assuming \kbd{p} is prime.
+
+\fun{GEN}{pgener_Zp}{GEN p} returns the smallest primitive root modulo $p^k$,
+$k > 1$, assuming \kbd{p} is an odd prime.
+
+\fun{long}{Zp_issquare}{GEN x, GEN p} returns 1 if the \typ{INT} $x$ is
+a $p$-adic square, $0$ otherwise.
+
+\fun{long}{Zn_issquare}{GEN x, GEN n} returns 1 if \typ{INT} $x$ is
+a square modulo \kbd{n} (possibly not prime), where $n$ is either a \typ{INT}
+or is given by its factorisation matrix. Return $0$ otherwise.
+
+\fun{long}{Zn_ispower}{GEN x, GEN n, GEN K, GEN *py} returns 1 if \typ{INT}
+$x$ is a $K$-th power modulo \kbd{n} (possibly not prime), where $n$ is
+either a \typ{INT} or is given by its factorisation matrix. Return $0$
+otherwise. If \kbd{py} is not \kbd{NULL}, set it to $y$ such that $y^K = x$
+modulo $n$.
+
+\fun{GEN}{pgener_Fp_local}{GEN p, GEN L}, \kbd{L} being a vector of
+primes dividing $p - 1$, returns the smallest integer $x > 1$ which is a
+generator of the $\ell$-Sylow of $\F_p^*$ for every $\ell$ in \kbd{L}. In
+other words, $x^{(p-1)/\ell} \neq 1$ for all such $\ell$. In particular,
+returns \kbd{pgener\_Fp(p)} if \kbd{L} contains all primes dividing $p - 1$.
+It is not necessary, and in fact slightly inefficient, to include $\ell=2$,
+since 2 is treated separately in any case, i.e. the generator obtained is
+never a square.
+
+\fun{GEN}{rootsof1_Fp}{GEN n, GEN p} returns a primitive $n$-th root modulo
+the prime $p$.
+
+\fun{GEN}{rootsof1u_Fp}{ulong n, GEN p} returns a primitive $n$-th root modulo
+the prime $p$.
+
+\fun{ulong}{rootsof1_Fl}{ulong n, ulong p} returns a primitive $n$-th root
+modulo the prime $p$.
+
+\subsec{Extending functions to vector inputs}
+
+The following functions apply $f$ to the given arguments, recursively
+if they are of vector / matrix type:
+
+\fun{GEN}{map_proto_G}{GEN (*f)(GEN), GEN x} For instance, if $x$ is a
+\typ{VEC}, return a \typ{VEC} whose components are the $f(x[i])$.
+
+\fun{GEN}{map_proto_lG}{long (*f)(GEN), GEN x} As above, applying the
+function \kbd{stoi( f() )}.
+
+\fun{GEN}{map_proto_GL}{GEN (*f)(GEN,long), GEN x, long y}
+
+\fun{GEN}{map_proto_lGL}{long (*f)(GEN,long), GEN x, long y}
+
+In the last function, $f$ implements an associative binary operator, which we
+extend naturally to an $n$-ary operator $f_n$ for any $n$: by convention,
+$f_0() = 1$, $f_1(x) = x$, and
+$$ f_n(x_1,\dots,x_n) = f( f_{n-1}(x_1,\dots,x_{n-1}), x_n)),$$
+for $n \geq 2$.
+
+\fun{GEN}{gassoc_proto}{GEN (*f)(GEN,GEN),GEN x, GEN y} If $y$ is not
+\kbd{NULL}, return $f(x,y)$. Otherwise, $x$ must be of vector type, and we
+return the result of $f$ applied to its components, computed using a
+divide-and-conquer algorithm. More precisely, return
+$$f( f(x_1,\kbd{NULL}), f(x_2,\kbd{NULL}) ),$$
+where $x_1$, $x_2$ are the two halves of $x$.
+
+\subsec{Miscellaneous arithmetic functions}
+
+\fun{ulong}{coreu}{ulong n}, unique squarefree integer $d$ dividing $n$ such
+that $n/d$ is a square.
+
+\fun{ulong}{eulerphiu}{ulong n}, Euler's totient function of $n$.
+
+\fun{ulong}{eulerphiu_fact}{GEN fa}, Euler's totient function of the
+\kbd{ulong} $n$, where \kbd{fa} is \kbd{factoru(n)}.
+
+\fun{long}{moebiusu}{ulong n}, Moebius $\mu$-function of $n$.
+
+\fun{GEN}{divisorsu}{ulong n}, returns the divisors of $n$ in a
+\typ{VECSMALL}, sorted by increasing order.
+
+\fun{long}{uissquarefree}{ulong n} returns $1$ if \kbd{n}
+is square-free, and $0$ otherwise.
+
+\fun{ulong}{uissquarefree_fact}{GEN fa} returns \kbd{uissquarefree(n)}, where
+\kbd{fa} is \kbd{factoru(n)}.
+
+\fun{long}{uposisfundamental}{ulong x} return $1$ is $x$ is a fundamental
+discriminant, and $0$ otherwise.
+
+\fun{long}{unegisfundamental}{ulong x} return $1$ is $-x$ is a fundamental
+discriminant, and $0$ otherwise.
+
+\fun{int}{uis_357_power}{ulong x, ulong *pt, ulong *mask} as \tet{is_357_power}
+for \kbd{ulong} $x$.
+
+\fun{int}{uis_357_powermod}{ulong x, ulong *mask} as \tet{uis_357_power}, but
+only check for 3rd, 5th or 7th powers modulo
+$211\times209\times61\times203\times117\times31\times43\times71$.
+
+\fun{long}{uisprimepower}{ulong n, ulong *p} as \tet{isprimepower}, for
+\kbd{ulong} $n$.
+
+\fun{int}{uislucaspsp}{ulong n} returns $1$ if the \kbd{ulong} $n$ fails Lucas
+compositeness test (it thus may be prime or composite), and $0$ otherwise
+(proving that $n$ is composite).
+
+\fun{ulong}{sumdigitsu}{ulong n} returns the sum of decimal digits of $u$.
+
+\fun{GEN}{usumdivkvec}{ulong n, GEN K} $K$ being a \typ{VECSMALL} of
+positive integers. Returns the vector of \kbd{sumdivk}$(n, K[i])$.
+
+\fun{GEN}{hilbertii}{GEN x, GEN y, GEN p}, returns the Hilbert symbol
+$(x,y)$ at the prime $p$ (\kbd{NULL} for the place at infinity); $x$ and $y$
+are \typ{INT}s.
+
+\fun{GEN}{sumdedekind}{GEN h, GEN k} returns the Dedekind sum associated to
+the \typ{INT} $h$ and $k$, $k > 0$.
+
+\fun{GEN}{sumdedekind_coprime}{GEN h, GEN k} as \kbd{sumdedekind}, except
+that $h$ and $k$ are assumed to be coprime \typ{INT}s.
+
+\fun{GEN}{u_sumdedekind_coprime}{long h, long k}
+Let $k > 0$, $0 \leq h < k$, $(h,k) = 1$. Returns $[s_1,s_2]$
+in a \typ{VECSMALL}, such that $s(h,k) = (s_2 + k s_1) / (12k)$.
+Requires $\max(h + k/2, k) < \kbd{LONG\_MAX}$
+to avoid overflow, in particular $k \leq (2/3)\kbd{LONG\_MAX}$ is fine.
+
+\newpage
+\chapter{Level 2 kernel}
+
+These functions deal with modular arithmetic, linear algebra and polynomials
+where assumptions can be made about the types of the coefficients.
+
+\section{Naming scheme}\label{se:level2names}
+A function name is built in the following way:
+$A_1\kbd{\_}\dots\kbd{\_}A_n\var{fun}$ for an operation \var{fun} with $n$
+arguments of class $A_1$,\dots, $A_n$. A class name is given by a base ring
+followed by a number of code letters. Base rings are among
+
+  \kbd{Fl}: $\Z/l\Z$ where $l < 2^{\B}$ is not necessarily prime. Implemented
+            using \kbd{ulong}s
+
+  \kbd{Fp}: $\Z/p\Z$ where $p$ is a \typ{INT}, not necessarily prime.
+Implemented as \typ{INT}s $z$, preferably satisfying $0 \leq z < p$.
+More precisely, any \typ{INT} can be used as an \kbd{Fp}, but reduced
+inputs are treated more efficiently. Outputs from \kbd{Fp}xxx routines are
+reduced.
+
+  \kbd{Fq}: $\Z[X]/(p,T(X))$, $p$ a \typ{INT}, $T$ a \typ{POL} with \kbd{Fp}
+coefficients or \kbd{NULL} (in which case no reduction modulo \kbd{T} is
+performed). Implemented as \typ{POL}s $z$ with \kbd{Fp} coefficients,
+$\deg(z) < \deg \kbd{T}$, although $z$ a \typ{INT} is allowed for elements in
+the prime field.
+
+  \kbd{Z}:  the integers $\Z$, implemented as \typ{INT}s.
+
+  \kbd{z}:  the integers $\Z$, implemented using (signed) \kbd{long}s.
+
+  \kbd{Q}:  the rational numbers $\Q$, implemented as \typ{INT}s and
+\typ{FRAC}s.
+
+  \kbd{Rg}:  a commutative ring, whose elements can be
+\kbd{gadd}-ed, \kbd{gmul}-ed, etc.
+
+\noindent Possible letters are:
+
+  \kbd{X}: polynomial in $X$ (\typ{POL} in a fixed variable), e.g. \kbd{FpX}
+           means $\Z/p\Z[X]$
+
+  \kbd{Y}: polynomial in $Y\neq X$. This is used to resolve ambiguities.
+           E.g. \kbd{FpXY} means $((\Z/p\Z)[X])[Y]$.
+
+  \kbd{V}: vector (\typ{VEC} or \typ{COL}), treated as a line vector
+  (independently of the actual type). E.g. \kbd{ZV} means $\Z^k$ for some $k$.
+
+  \kbd{C}: vector (\typ{VEC} or \typ{COL}), treated as a column vector
+  (independently of the actual type). The difference with \kbd{V} is purely
+  semantic: if the result is a vector, it will be of type \typ{COL} unless
+  mentioned otherwise. For instance the function \kbd{ZC\_add} receives two
+  integral vectors (\typ{COL} or \typ{VEC}, possibly different types) of the
+  same length and returns a \typ{COL} whose entries are the sums of the input
+  coefficients.
+
+  \kbd{M}: matrix (\typ{MAT}). E.g. \kbd{QM} means a matrix with rational
+  entries
+
+  \kbd{T}: Trees. Either a leaf or a \typ{VEC} of trees.
+
+  \kbd{E}: point over an elliptic curve, represented
+  as two-component vectors \kbd{[x,y]}, except for the  represented by the
+  one-component vector \kbd{[0]}. Not all curve models are supported.
+
+  \kbd{Q}: representative (\typ{POL}) of a class in a polynomial quotient ring.
+  E.g.~an \kbd{FpXQ} belongs to $(\Z/p\Z)[X]/(T(X))$, \kbd{FpXQV} means a
+  vector of such elements, etc.
+
+  \kbd{x}, \kbd{y}, \kbd{m}, \kbd{v}, \kbd{c}, \kbd{q}: as their uppercase
+  counterpart, but coefficient arrays are implemented using \typ{VECSMALL}s,
+  which coefficient understood as \kbd{ulong}s.
+
+  \kbd{x} and \kbd{y} (and \kbd{q}) are implemented by a \typ{VECSMALL} whose
+  first coefficient is used as a code-word and the following are the
+  coefficients , similarly to a \typ{POL}. This is known as a 'POLSMALL'.
+
+  \kbd{m} are implemented by a \typ{MAT} whose components (columns) are
+  \typ{VECSMALL}s. This is known as a 'MATSMALL'.
+
+  \kbd{v} and \kbd{c} are regular \typ{VECSMALL}s. Difference between the
+  two is purely semantic.
+
+\noindent Omitting the letter means the argument is a scalar in the base
+ring. Standard functions \var{fun} are
+
+  \kbd{add}: add
+
+  \kbd{sub}: subtract
+
+  \kbd{mul}: multiply
+
+  \kbd{sqr}: square
+
+  \kbd{div}: divide (Euclidean quotient)
+
+  \kbd{rem}: Euclidean remainder
+
+  \kbd{divrem}: return Euclidean quotient, store remainder in a pointer
+argument. Three special values of that pointer argument modify the default
+behavior: \kbd{NULL} (do not store the remainder, used to implement
+\kbd{div}), \tet{ONLY_REM} (return the remainder, used to implement
+\kbd{rem}), \tet{ONLY_DIVIDES} (return the quotient if the division is exact,
+and \kbd{NULL} otherwise).
+
+  \kbd{gcd}: GCD
+
+  \kbd{extgcd}: return GCD, store Bezout coefficients in pointer arguments
+
+  \kbd{pow}: exponentiate
+
+  \kbd{eval}: evaluation / composition
+
+
+\section{Modular arithmetic}
+
+\noindent These routines implement univariate polynomial arithmetic and
+linear algebra over finite fields, in fact over finite rings of the form
+$(\Z/p\Z)[X]/(T)$, where $p$ is not necessarily prime and $T\in(\Z/p\Z)[X]$ is
+possibly reducible; and finite extensions thereof. All this can be emulated
+with \typ{INTMOD} and \typ{POLMOD} coefficients and using generic routines,
+at a considerable loss of efficiency. Also, specialized routines are
+available that have no obvious generic equivalent.
+
+\subsec{\kbd{FpC} / \kbd{FpV}, \kbd{FpM}} A \kbd{ZV}
+(resp.~a~\kbd{ZM}) is a \typ{VEC} or \typ{COL} (resp.~\typ{MAT}) with
+\typ{INT} coefficients. An \kbd{FpV} or \kbd{FpM}, with respect to a given
+\typ{INT}~\kbd{p}, is the same with \kbd{Fp} coordinates; operations are
+understood over $\Z/p\Z$.
+
+\subsubsec{Conversions}
+
+\fun{int}{Rg_is_Fp}{GEN z, GEN *p}, checks if \kbd{z} can be mapped to
+$\Z/p\Z$: a \typ{INT} or a \typ{INTMOD} whose modulus is equal to \kbd{*p},
+(if \kbd{*p} not \kbd{NULL}), in that case return $1$, else $0$. If a modulus
+is found it is put in \kbd{*p}, else \kbd{*p} is left unchanged.
+
+\fun{int}{RgV_is_FpV}{GEN z, GEN *p}, \kbd{z} a \typ{VEC} (resp. \typ{COL}),
+checks if it can be mapped to a \kbd{FpV} (resp. \kbd{FpC}), by checking
+\kbd{Rg\_is\_Fp} coefficientwise.
+
+\fun{int}{RgM_is_FpM}{GEN z, GEN *p}, \kbd{z} a \typ{MAT},
+checks if it can be mapped to a \kbd{FpM}, by checking \kbd{RgV\_is\_FpV}
+columnwise.
+
+\fun{GEN}{Rg_to_Fp}{GEN z, GEN p}, \kbd{z} a scalar which can be mapped to
+$\Z/p\Z$: a \typ{INT}, a \typ{INTMOD} whose modulus is divisible by $p$,
+a \typ{FRAC} whose denominator is coprime to $p$, or a \typ{PADIC} with
+underlying prime $\ell$ satisfying $p = \ell^n$ for some $n$ (less than the
+accuracy of the input). Returns \kbd{lift(z * Mod(1,p))}, normalized.
+
+\fun{GEN}{padic_to_Fp}{GEN x, GEN p} special case of \tet{Rg_to_Fp},
+for a $x$ a \typ{PADIC}.
+
+\fun{GEN}{RgV_to_FpV}{GEN z, GEN p}, \kbd{z} a \typ{VEC} or \typ{COL},
+returns the \kbd{FpV} (as a \typ{VEC}) obtained by applying \kbd{Rg\_to\_Fp}
+coefficientwise.
+
+\fun{GEN}{RgC_to_FpC}{GEN z, GEN p}, \kbd{z} a \typ{VEC} or \typ{COL},
+returns the \kbd{FpC} (as a \typ{COL}) obtained by applying \kbd{Rg\_to\_Fp}
+coefficientwise.
+
+\fun{GEN}{RgM_to_FpM}{GEN z, GEN p}, \kbd{z} a \typ{MAT},
+returns the \kbd{FpM} obtained by applying \kbd{RgC\_to\_FpC}
+columnwise.
+
+\fun{GEN}{RgM_Fp_init}{GEN z, GEN p, ulong *pp}, given an \kbd{RgM} $z$,
+whose entries can be mapped to $\F_p$ (as per \tet{Rg_to_Fp}), and a prime
+number $p$. This routine returns a normal form of $z$: either an
+\kbd{F2m} ($p = 2$), an \kbd{Flm} ($p$ fits into an \kbd{ulong})
+or an \kbd{FpM}. In the first two cases, \kbd{pp} is set to \kbd{itou}$(p)$,
+and to $0$ in the last.
+
+
+The functions above are generally used as follow:
+\bprog
+GEN add(GEN x, GEN y)
+{
+  GEN p = NULL;
+  if (Rg_is_Fp(x, &p) && Rg_is_Fp(y, &p) && p)
+  {
+    x = Rg_to_Fp(x, p); y = Rg_to_Fp(y, p);
+    z = Fp_add(x, y, p);
+    return Fp_to_mod(z);
+  }
+  else return gadd(x, y);
+}
+ at eprog
+
+\fun{GEN}{FpC_red}{GEN z, GEN p}, \kbd{z} a \kbd{ZC}. Returns \kbd{lift(Col(z) *
+Mod(1,p))}, hence a \typ{COL}.
+
+\fun{GEN}{FpV_red}{GEN z, GEN p}, \kbd{z} a \kbd{ZV}. Returns \kbd{lift(Vec(z) *
+Mod(1,p))}, hence a \typ{VEC}
+
+\fun{GEN}{FpM_red}{GEN z, GEN p}, \kbd{z} a \kbd{ZM}. Returns \kbd{lift(z *
+Mod(1,p))}, which is an \kbd{FpM}.
+
+\subsubsec{Basic operations}
+
+\fun{GEN}{FpC_center}{GEN z, GEN p, GEN pov2} returns a \typ{COL} whose
+entries are the \kbd{Fp\_center} of the \kbd{gel(z,i)}.
+
+\fun{GEN}{FpM_center}{GEN z, GEN p, GEN pov2} returns a matrix whose
+entries are the \kbd{Fp\_center} of the \kbd{gcoeff(z,i,j)}.
+
+\fun{GEN}{FpC_add}{GEN x, GEN y, GEN p} adds the \kbd{ZC} $x$ and $y$
+and reduce modulo $p$ to obtain an \kbd{FpC}.
+
+\fun{GEN}{FpV_add}{GEN x, GEN y, GEN p} same as \kbd{FpC\_add}, returning and
+\kbd{FpV}.
+
+\fun{GEN}{FpC_sub}{GEN x, GEN y, GEN p} subtracts the \kbd{ZC} $y$ to
+the \kbd{ZC} $x$ and reduce modulo $p$ to obtain an \kbd{FpC}.
+
+\fun{GEN}{FpV_sub}{GEN x, GEN y, GEN p} same as \kbd{FpC\_sub}, returning and
+\kbd{FpV}.
+
+\fun{GEN}{FpC_Fp_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{ZC}~\kbd{x}
+(seen as a column vector) by the \typ{INT}~\kbd{y} and reduce modulo \kbd{p} to
+obtain an \kbd{FpC}.
+
+\fun{GEN}{FpC_FpV_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{ZC}~\kbd{x}
+(seen as a column vector) by the \kbd{ZV}~\kbd{y} (seen as a row vector,
+assumed to have compatible dimensions), and reduce modulo \kbd{p} to obtain
+an \kbd{FpM}.
+
+\fun{GEN}{FpM_mul}{GEN x, GEN y, GEN p} multiplies the two \kbd{ZM}s~\kbd{x}
+and \kbd{y} (assumed to have compatible dimensions), and reduce modulo
+\kbd{p} to obtain an \kbd{FpM}.
+
+\fun{GEN}{FpM_powu}{GEN x, ulong n, GEN p} computes $x^n$ where $x$ is a
+square \kbd{FpM}.
+
+\fun{GEN}{FpM_FpC_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{ZM}~\kbd{x}
+by the \kbd{ZC}~\kbd{y} (seen as a column vector, assumed to have compatible
+dimensions), and reduce modulo \kbd{p} to obtain an \kbd{FpC}.
+
+\fun{GEN}{FpM_FpC_mul_FpX}{GEN x, GEN y, GEN p, long v} is a memory-clean
+version of
+\bprog
+  GEN tmp = FpM_FpC_mul(x,y,p);
+  return RgV_to_RgX(tmp, v);
+ at eprog
+
+\fun{GEN}{FpV_FpC_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{ZV}~\kbd{x}
+(seen as a row vector) by the \kbd{ZC}~\kbd{y} (seen as a column vector,
+assumed to have compatible dimensions), and reduce modulo \kbd{p} to obtain
+an \kbd{Fp}.
+
+\fun{GEN}{FpV_dotproduct}{GEN x,GEN y,GEN p} scalar product of
+$x$ and $y$ (assumed to have the same length).
+
+\fun{GEN}{FpV_dotsquare}{GEN x, GEN p} scalar product of $x$ with itself.
+has \typ{INT} entries.
+
+\subsubsec{\kbd{Fp}-linear algebra} The implementations are not
+asymptotically efficient ($O(n^3)$ standard algorithms).
+
+\fun{GEN}{FpM_deplin}{GEN x, GEN p} returns a non-trivial kernel vector,
+or \kbd{NULL} if none exist.
+
+\fun{GEN}{FpM_det}{GEN x, GEN p} as \kbd{det}
+
+\fun{GEN}{FpM_gauss}{GEN a, GEN b, GEN p} as \kbd{gauss}, where $b$ is a
+\kbd{FpM}.
+
+\fun{GEN}{FpM_FpC_gauss}{GEN a, GEN b, GEN p} as \kbd{gauss}, where $b$
+is a \kbd{FpC}.
+
+\fun{GEN}{FpM_image}{GEN x, GEN p} as \kbd{image}
+
+\fun{GEN}{FpM_intersect}{GEN x, GEN y, GEN p} as \kbd{intersect}
+
+\fun{GEN}{FpM_inv}{GEN x, GEN p} returns the inverse of \kbd{x}, or
+\kbd{NULL} if \kbd{x} is not invertible.
+
+\fun{GEN}{FpM_FpC_invimage}{GEN m, GEN v, GEN p}
+ given an \kbd{FpM} $x$ and an \kbd{FpC} $y$, returns an $x$ such that $Ax =
+ y$, or \kbd{NULL} if no such vector exist.
+
+\fun{GEN}{FpM_invimage}{GEN m, GEN v, GEN p}
+given two \kbd{FpM} $x$ and $y$, returns $x$ such that $Ax = y$, or \kbd{NULL}
+if no such matrix exist.
+
+\fun{GEN}{FpM_ker}{GEN x, GEN p} as \kbd{ker}
+
+\fun{long}{FpM_rank}{GEN x, GEN p} as \kbd{rank}
+
+\fun{GEN}{FpM_indexrank}{GEN x, GEN p} as \kbd{indexrank}
+
+\fun{GEN}{FpM_suppl}{GEN x, GEN p} as \kbd{suppl}
+
+\fun{GEN}{FpM_hess}{GEN x, GEN p} upper Hessenberg form of $x$ over $\F_p$.
+
+\fun{GEN}{FpM_charpoly}{GEN x, GEN p} characteristic polynomial of $x$.
+
+\subsubsec{\kbd{FqC}, \kbd{FqM} and \kbd{Fq}-linear algebra}
+
+An \kbd{FqM} (resp. \kbd{FqC}) is a matrix (resp a \typ{COL}) with
+\kbd{Fq} coefficients (with respect to given \kbd{T}, \kbd{p}), not necessarily
+reduced (i.e arbitrary \typ{INT}s and \kbd{ZX}s in the same variable as
+\kbd{T}).
+
+\fun{GEN}{FqC_add}{GEN a, GEN b, GEN T, GEN p}
+
+\fun{GEN}{FqC_sub}{GEN a, GEN b, GEN T, GEN p}
+
+\fun{GEN}{FqC_Fq_mul}{GEN a, GEN b, GEN T, GEN p}
+
+\fun{GEN}{FqM_deplin}{GEN x, GEN T, GEN p} returns a non-trivial kernel vector,
+or \kbd{NULL} if none exist.
+
+\fun{GEN}{FqM_gauss}{GEN a, GEN b, GEN T, GEN p}
+as \kbd{gauss}, where $b$ is a \kbd{FqM}.
+
+\fun{GEN}{FqM_FqC_gauss}{GEN a, GEN b, GEN T, GEN p}
+as \kbd{gauss}, where $b$ is a \kbd{FqC}.
+
+\fun{GEN}{FqM_FqC_mul}{GEN a, GEN b, GEN T, GEN p}
+
+\fun{GEN}{FqM_ker}{GEN x, GEN T, GEN p} as \kbd{ker}
+
+\fun{GEN}{FqM_image}{GEN x, GEN T, GEN p} as \kbd{image}
+
+\fun{GEN}{FqM_inv}{GEN x, GEN T, GEN p} returns the inverse of \kbd{x}, or
+\kbd{NULL} if \kbd{x} is not invertible.
+
+\fun{GEN}{FqM_mul}{GEN a, GEN b, GEN T, GEN p}
+
+\fun{long}{FqM_rank}{GEN x, GEN T, GEN p} as \kbd{rank}
+
+\fun{GEN}{FqM_suppl}{GEN x, GEN T, GEN p} as \kbd{suppl}
+
+\fun{GEN}{FqM_det}{GEN x, GEN T, GEN p} as \kbd{det}
+
+
+\subsec{\kbd{Flc} / \kbd{Flv}, \kbd{Flm}} See \kbd{FpV}, \kbd{FpM}
+operations.
+
+\fun{GEN}{Flv_copy}{GEN x} returns a copy of \kbd{x}.
+
+\fun{GEN}{Flv_center}{GEN z, ulong p, ulong ps2}
+
+\fun{GEN}{Flm_copy}{GEN x} returns a copy of \kbd{x}.
+
+\fun{GEN}{matid_Flm}{long n} returns an \kbd{Flm} which is an $n \times n$
+identity matrix.
+
+\fun{GEN}{scalar_Flm}{long s, long n} returns an \kbd{Flm} which is $s$ times
+the $n \times n$ identity matrix.
+
+\fun{GEN}{Flm_center}{GEN z, ulong p, ulong ps2}
+
+\fun{GEN}{Flm_Fl_add}{GEN x, ulong y, ulong p} returns $x + y*\text{Id}$
+($x$ must be square).
+
+\fun{GEN}{Flm_Flc_mul}{GEN x, GEN y, ulong p} multiplies  \kbd{x} and \kbd{y}
+(assumed to have compatible dimensions).
+
+\fun{GEN}{Flm_Fl_mul}{GEN x, ulong y, ulong p} multiplies the \kbd{Flm}
+\kbd{x} by \kbd{y}.
+
+\fun{GEN}{Flm_neg}{GEN x, ulong p} negates the \kbd{Flm} \kbd{x}.
+
+\fun{void}{Flm_Fl_mul_inplace}{GEN x, ulong y, ulong p} replaces
+the \kbd{Flm} \kbd{x} by $\kbd{x}*\kbd{y}$.
+
+\fun{GEN}{Flc_Fl_mul}{GEN x, ulong y, ulong p} multiplies the \kbd{Flv}
+\kbd{x} by \kbd{y}.
+
+\fun{void}{Flc_Fl_mul_inplace}{GEN x, ulong y, ulong p} replaces
+the \kbd{Flc} \kbd{x} by $\kbd{x}*\kbd{y}$.
+
+\fun{void}{Flc_Fl_mul_part_inplace}{GEN x, ulong y, ulong p, long l}
+multiplies $x[1..l]$ by $y$ modulo $p$. In place.
+
+\fun{GEN}{Flc_Fl_div}{GEN x, ulong y, ulong p} divides the \kbd{Flv}
+\kbd{x} by \kbd{y}.
+
+\fun{void}{Flc_Fl_div_inplace}{GEN x, ulong y, ulong p} replaces
+the \kbd{Flv} \kbd{x} by $\kbd{x}/\kbd{y}$.
+
+\fun{void}{Flc_lincomb1_inplace}{GEN X, GEN Y, ulong v, ulong q}
+sets $X\leftarrow X + vY$, where $X,Y$ are \kbd{Flc}. Memory efficient (e.g.
+no-op if $v = 0$), and gerepile-safe.
+
+\fun{GEN}{Flv_add}{GEN x, GEN y, ulong p} adds two \kbd{Flv}.
+
+\fun{void}{Flv_add_inplace}{GEN x, GEN y, ulong p} replaces
+$x$ by $x+y$.
+
+\fun{GEN}{Flv_sub}{GEN x, GEN y, ulong p} subtracts \kbd{y} to \kbd{x}.
+
+\fun{void}{Flv_sub_inplace}{GEN x, GEN y, ulong p} replaces
+$x$ by $x-y$.
+
+\fun{ulong}{Flv_dotproduct}{GEN x, GEN y, ulong p} returns the scalar product
+of \kbd{x} and \kbd{y}
+
+\fun{ulong}{Flv_sum}{GEN x, ulong p} returns the sums of the components of $x$.
+
+\fun{GEN}{zero_Flm}{long m, long n} creates a \kbd{Flm} with \kbd{m} x \kbd{n}
+components set to $0$. Note that the result allocates a
+\emph{single} column, so modifying an entry in one column modifies it in
+all columns.
+
+\fun{GEN}{zero_Flm_copy}{long m, long n} creates a \kbd{Flm} with \kbd{m} x
+\kbd{n} components set to $0$.
+
+\fun{GEN}{zero_Flv}{long n} creates a \kbd{Flv} with \kbd{n} components set to
+$0$.
+
+\fun{GEN}{row_Flm}{GEN A, long x0} return $A[i,]$, the $i$-th row of the
+\kbd{Flm} (or \kbd{zm}) $A$.
+
+\fun{GEN}{Flm_mul}{GEN x, GEN y, ulong p} multiplies  \kbd{x} and \kbd{y}
+(assumed to have compatible dimensions).
+
+\fun{GEN}{Flm_powu}{GEN x, ulong n, ulong p} computes $x^n$ where $x$ is a
+square \kbd{Flm}.
+
+\fun{GEN}{Flm_charpoly}{GEN x, ulong p} return the characteristic polynomial of
+the square \kbd{Flm} $x$, as a \kbd{Flx}.
+
+\fun{GEN}{Flm_deplin}{GEN x, ulong p}
+
+\fun{ulong}{Flm_det}{GEN x, ulong p}
+
+\fun{ulong}{Flm_det_sp}{GEN x, ulong p}, as \kbd{Flm\_det}, in place
+(destroys~\kbd{x}).
+
+\fun{GEN}{Flm_gauss}{GEN a, GEN b, ulong p} as \kbd{gauss}, where $b$ is a
+\kbd{Flm}.
+
+\fun{GEN}{Flm_Flc_gauss}{GEN a, GEN b, ulong p} as \kbd{gauss}, where $b$ is
+a \kbd{Flc}.
+
+\fun{GEN}{Flm_indexrank}{GEN x, ulong p}
+
+\fun{GEN}{Flm_inv}{GEN x, ulong p}
+
+\fun{GEN}{Flm_Flc_invimage}{GEN A, GEN y, ulong p} given an \kbd{Flm}
+$x$ and an \kbd{Flc} $y$, returns an $x$ such that $Ax = y$, or \kbd{NULL}
+if no such vector exist.
+
+\fun{GEN}{Flm_invimage}{GEN x, GEN y, ulong p}
+given two \kbd{Flm} $x$ and $y$, returns $x$ such that $Ax = y$, or \kbd{NULL}
+if no such matrix exist.
+
+\fun{GEN}{Flm_ker}{GEN x, ulong p}
+
+\fun{GEN}{Flm_ker_sp}{GEN x, ulong p, long deplin}, as \kbd{Flm\_ker} (if
+\kbd{deplin=0}) or \kbd{Flm\_deplin} (if \kbd{deplin=1}) , in place
+(destroys~\kbd{x}).
+
+\fun{long}{Flm_rank}{GEN x, ulong p}
+
+\fun{long}{Flm_suppl}{GEN x, ulong p}
+
+\fun{GEN}{Flm_image}{GEN x, ulong p}
+
+\fun{GEN}{Flm_transpose}{GEN x}
+
+\fun{GEN}{Flm_hess}{GEN x, ulong p} upper Hessenberg form of $x$ over $\F_p$.
+
+\subsec{\kbd{F2c} / \kbd{F2v}, \kbd{F2m}}  An \kbd{F2v}~\kbd{v} is a
+\typ{VECSMALL} representing a vector over $\F_2$. Specifically \kbd{z[0]} is
+the usual codeword, \kbd{z[1]} is the number of components of $v$ and the
+coefficients are given by the bits of remaining words by increasing indices.
+
+\fun{ulong}{F2v_coeff}{GEN x, long i} returns the coefficient $i\ge 1$ of $x$.
+
+\fun{void}{F2v_clear}{GEN x, long i} sets the coefficient $i\ge 1$ of $x$ to
+$0$.
+
+\fun{void}{F2v_flip}{GEN x, long i} adds $1$ to the coefficient $i\ge 1$ of $x$.
+
+\fun{void}{F2v_set}{GEN x, long i} sets the coefficient $i\ge 1$ of $x$ to $1$.
+
+\fun{void}{F2v_copy}{GEN x} returns a copy of $x$.
+
+\fun{GEN}{F2v_slice}{GEN x, long a, long b} returns the \kbd{F2v} with
+entries $x[a]$, \dots, $x[b]$. Assumes $a \leq b$.
+
+\fun{ulong}{F2m_coeff}{GEN x, long i, long j} returns the coefficient $(i,j)$
+of $x$.
+
+\fun{void}{F2m_clear}{GEN x, long i, long j} sets the coefficient $(i,j)$ of $x$
+to $0$.
+
+\fun{void}{F2m_flip}{GEN x, long i, long j} adds $1$ to the coefficient $(i,j)$
+of $x$.
+
+\fun{void}{F2m_set}{GEN x, long i, long j} sets the coefficient $(i,j)$ of $x$
+to $1$.
+
+\fun{void}{F2m_copy}{GEN x} returns a copy of $x$.
+
+\fun{GEN}{F2m_rowslice}{GEN x, long a, long b} returns the \kbd{F2m} built
+from the $a$-th to $b$-th rows of the \kbd{F2m} $x$. Assumes $a \leq b$.
+
+\fun{GEN}{F2m_F2c_mul}{GEN x, GEN y} multiplies  \kbd{x} and \kbd{y} (assumed
+to have compatible dimensions).
+
+\fun{GEN}{F2m_image}{GEN x} gives a subset of the columns of $x$ that generate
+the image of $x$.
+
+\fun{GEN}{F2m_invimage}{GEN A, GEN B}
+
+\fun{GEN}{F2m_F2c_invimage}{GEN A, GEN y}
+
+\fun{GEN}{F2m_gauss}{GEN a, GEN b}
+as \kbd{gauss}, where $b$ is a \kbd{F2m}.
+
+\fun{GEN}{F2m_F2c_gauss}{GEN a, GEN b}
+as \kbd{gauss}, where $b$ is a \kbd{F2c}.
+
+
+\fun{GEN}{F2m_indexrank}{GEN x} $x$ being a matrix of rank $r$, returns a
+vector with two \typ{VECSMALL} components $y$ and $z$ of length $r$ giving a
+list of rows and columns respectively (starting from 1) such that the extracted
+matrix obtained from these two vectors using \kbd{vecextract}$(x,y,z)$ is
+invertible.
+
+\fun{GEN}{F2m_mul}{GEN x, GEN y} multiplies  \kbd{x} and \kbd{y} (assumed to
+have compatible dimensions).
+
+\fun{GEN}{F2m_powu}{GEN x, ulong n} computes $x^n$ where $x$ is a square
+\kbd{F2m}.
+
+\fun{long}{F2m_rank}{GEN x} as \kbd{rank}.
+
+\fun{long}{F2m_suppl}{GEN x} as \kbd{suppl}.
+
+\fun{GEN}{matid_F2m}{long n} returns an \kbd{F2m} which is an $n \times n$
+identity matrix.
+
+\fun{GEN}{zero_F2v}{long n} creates a \kbd{F2v} with \kbd{n} components set to
+$0$.
+
+\fun{GEN}{F2v_ei}{long n, long i} creates a \kbd{F2v} with \kbd{n} components
+set to $0$, but for the $i$-th one, which is set to $1$ ($i$-th vector in the
+canonical basis).
+
+\fun{GEN}{zero_F2m}{long m, long n} creates a \kbd{Flm} with \kbd{m} x \kbd{n}
+components set to $0$. Note that the result allocates a
+\emph{single} column, so modifying an entry in one column modifies it in
+all columns.
+
+\fun{GEN}{zero_F2m_copy}{long m, long n} creates a \kbd{F2m} with \kbd{m} x
+\kbd{n} components set to $0$.
+
+\fun{GEN}{F2c_to_Flc}{GEN x}
+
+\fun{GEN}{F2c_to_ZC}{GEN x}
+
+\fun{GEN}{ZV_to_F2v}{GEN x}
+
+\fun{GEN}{RgV_to_F2v}{GEN x}
+
+\fun{GEN}{F2m_to_Flm}{GEN x}
+
+\fun{GEN}{F2m_to_ZM}{GEN x}
+
+\fun{GEN}{Flv_to_F2v}{GEN x}
+
+\fun{GEN}{Flm_to_F2m}{GEN x}
+
+\fun{GEN}{ZM_to_F2m}{GEN x}
+
+\fun{GEN}{RgM_to_F2m}{GEN x}
+
+\fun{void}{F2v_add_inplace}{GEN x, GEN y} replaces $x$ by $x+y$. It is
+allowed for $y$ to be shorter than $x$.
+
+\fun{ulong}{F2m_det}{GEN x}
+
+\fun{ulong}{F2m_det_sp}{GEN x}, as \kbd{F2m\_det}, in place (destroys~\kbd{x}).
+
+\fun{GEN}{F2m_deplin}{GEN x}
+
+\fun{ulong}{F2v_dotproduct}{GEN x, GEN y} returns the scalar product of \kbd{x}
+and \kbd{y}
+
+\fun{GEN}{F2m_inv}{GEN x}
+
+\fun{GEN}{F2m_ker}{GEN x}
+
+\fun{GEN}{F2m_ker_sp}{GEN x, long deplin}, as \kbd{F2m\_ker} (if
+\kbd{deplin=0}) or \kbd{F2m\_deplin} (if \kbd{deplin=1}), in place
+(destroys~\kbd{x}).
+
+\subsec{\kbd{FlxqV}, \kbd{FlxqM}} See \kbd{FqV}, \kbd{FqM} operations.
+
+\fun{GEN}{FlxqV_dotproduct}{GEN x, GEN y, GEN T, ulong p} as
+\kbd{FpV\_dotproduct}.
+
+\fun{GEN}{FlxM_Flx_add_shallow}{GEN x, GEN y, ulong p} as
+\kbd{RgM\_Rg\_add\_shallow}.
+
+\fun{GEN}{FlxqM_gauss}{GEN a, GEN b, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_FlxqC_gauss}{GEN a, GEN b, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_FlxqC_mul}{GEN a, GEN b, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_ker}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_image}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_det}{GEN a, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_inv}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{FlxqM_mul}{GEN a, GEN b, GEN T, ulong p}
+
+\fun{long}{FlxqM_rank}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{matid_FlxqM}{long n, GEN T, ulong p}
+
+\subsec{\kbd{Zlm}}
+\fun{GEN}{ZlM_gauss}{GEN a, GEN b, ulong p, long e, GEN C} as \kbd{gauss}
+with the following peculiarities: $a$ and $b$ are \kbd{ZM}, such that $a$ is
+invertible modulo $p$. Optional $C$ is an \kbd{Flm} that is an inverse of
+$a\mod p$ or \kbd{NULL}. Return the matrix $x$ such that $ax=b\mod p^e$ and
+all elements of $x$ are in $[0,p^e-1]$. For efficiency, it is better
+to reduce $a$ and $b$ mod $p^e$ first.
+
+\subsec{\kbd{FpX}} Let \kbd{p} an understood \typ{INT}, to be given in
+the function arguments; in practice \kbd{p} is not assumed to be prime, but
+be wary. Recall than an \kbd{Fp} object is a \typ{INT}, preferably belonging
+to $[0, \kbd{p}-1]$; an \kbd{FpX} is a \typ{POL} in a fixed variable whose
+coefficients are \kbd{Fp} objects. Unless mentioned otherwise, all outputs in
+this section are \kbd{FpX}s. All operations are understood to take place in
+$(\Z/\kbd{p}\Z)[X]$.
+
+\subsubsec{Conversions} In what follows \kbd{p} is always a \typ{INT},
+not necessarily prime.
+
+\fun{int}{RgX_is_FpX}{GEN z, GEN *p}, \kbd{z} a \typ{POL},
+checks if it can be mapped to a \kbd{FpX}, by checking \kbd{Rg\_is\_Fp}
+coefficientwise.
+
+\fun{GEN}{RgX_to_FpX}{GEN z, GEN p}, \kbd{z} a \typ{POL}, returns the
+\kbd{FpX} obtained by applying \kbd{Rg\_to\_Fp} coefficientwise.
+
+\fun{GEN}{FpX_red}{GEN z, GEN p}, \kbd{z} a \kbd{ZX}, returns \kbd{lift(z *
+Mod(1,p))}, normalized.
+
+\fun{GEN}{FpXV_red}{GEN z, GEN p}, \kbd{z} a \typ{VEC} of \kbd{ZX}. Applies
+\kbd{FpX\_red} componentwise and returns the result (and we obtain a vector
+of \kbd{FpX}s).
+
+\fun{GEN}{FpXT_red}{GEN z, GEN p}, \kbd{z} a tree of \kbd{ZX}. Applies
+\kbd{FpX\_red} to each leaf and returns the result (and we obtain a tree
+of \kbd{FpX}s).
+
+\subsubsec{Basic operations} In what follows \kbd{p} is always a \typ{INT},
+not necessarily prime.
+
+\noindent Now, except for \kbd{p}, the operands and outputs are all \kbd{FpX}
+objects. Results are undefined on other inputs.
+
+\fun{GEN}{FpX_add}{GEN x,GEN y, GEN p} adds \kbd{x} and \kbd{y}.
+
+\fun{GEN}{FpX_neg}{GEN x,GEN p} returns $-\kbd{x}$, the components are
+between $0$ and $p$ if this is the case for the components of $x$.
+
+\fun{GEN}{FpX_renormalize}{GEN x, long l}, as \kbd{normalizepol}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{GEN}{FpX_sub}{GEN x,GEN y,GEN p} returns $x-y$.
+
+\fun{GEN}{FpX_mul}{GEN x,GEN y,GEN p} returns $x\*y$.
+
+\fun{GEN}{FpX_mulspec}{GEN a, GEN b, GEN p, long na, long nb}
+see \kbd{ZX\_mulspec}
+
+\fun{GEN}{FpX_sqr}{GEN x,GEN p} returns $\kbd{x}^2$.
+
+\fun{GEN}{FpX_divrem}{GEN x, GEN y, GEN p, GEN *pr} returns the quotient
+of \kbd{x} by \kbd{y}, and sets \kbd{pr} to the remainder.
+
+\fun{GEN}{FpX_div}{GEN x, GEN y, GEN p} returns the quotient of \kbd{x} by
+\kbd{y}.
+
+\fun{GEN}{FpX_div_by_X_x}{GEN A, GEN a, GEN p, GEN *r} returns the
+quotient of the \kbd{FpX}~\kbd{A} by $(X - \kbd{a})$, and sets \kbd{r} to the
+remainder $\kbd{A}(\kbd{a})$.
+
+\fun{GEN}{FpX_rem}{GEN x, GEN y, GEN p} returns the remainder \kbd{x} mod
+\kbd{y}.
+
+\fun{long}{FpX_valrem}{GEN x, GEN t, GEN p, GEN *r} The arguments \kbd{x} and
+\kbd{e} being non-zero \kbd{FpX} returns the highest exponent $e$ such that
+$\kbd{t}^{e}$ divides~\kbd{x}. The quotient $\kbd{x}/\kbd{t}^{e}$ is returned
+in~\kbd{*r}. In particular, if \kbd{t} is irreducible, this returns the
+valuation at \kbd{t} of~\kbd{x}, and \kbd{*r} is the prime-to-\kbd{t} part
+of~\kbd{x}.
+
+\fun{GEN}{FpX_deriv}{GEN x, GEN p} returns the derivative of \kbd{x}.
+This function is not memory-clean, but nevertheless suitable for
+\kbd{gerepileupto}.
+
+\fun{GEN}{FpX_translate}{GEN P, GEN c, GEN p} let $c$ be an \kbd{Fp} and let
+$P$ be an \kbd{FpX}; returns the translated \kbd{FpX} of $P(X+c)$.
+
+\fun{GEN}{FpX_gcd}{GEN x, GEN y, GEN p} returns a (not necessarily monic)
+greatest common divisor of $x$  and $y$.
+
+\fun{GEN}{FpX_halfgcd}{GEN x, GEN y, GEN p} returns a two-by-two \kbd{FpXM}
+$M$ with determinant $\pm 1$ such that the image $(a,b)$ of $(x,y)$ by $M$
+has the property that $\deg a \geq {\deg x \over 2} > \deg b$.
+
+\fun{GEN}{FpX_extgcd}{GEN x, GEN y, GEN p, GEN *u, GEN *v} returns
+$d = \text{GCD}(\kbd{x},\kbd{y})$ (not necessarily monic), and sets \kbd{*u},
+\kbd{*v} to the Bezout coefficients such that $\kbd{*ux} + \kbd{*vy} = d$.
+If \kbd{*u} is set to \kbd{NULL}, it is not computed which is a bit faster.
+This is useful when computing the inverse of $y$ modulo $x$.
+
+\fun{GEN}{FpX_center}{GEN z, GEN p, GEN pov2} returns the polynomial whose
+coefficient belong to the symmetric residue system. Assumes the coefficients
+already belong to $[0,\kbd{p}-1]$) and \kbd{pov2} is \kbd{shifti(p,-1)}.
+
+
+\subsubsec{Mixed operations}
+The following functions implement arithmetic operations between \kbd{FpX}
+and \kbd{Fp} operands, the result being of type \kbd{FpX}. The integer
+\kbd{p} need not be prime.
+
+\fun{GEN}{Z_to_FpX}{GEN x, GEN p, long v} converts a \typ{INT} to a scalar
+polynomial in variable $v$, reduced modulo $p$.
+
+\fun{GEN}{FpX_Fp_add}{GEN y, GEN x, GEN p} add the \kbd{Fp}~\kbd{x} to the
+\kbd{FpX}~\kbd{y}.
+
+\fun{GEN}{FpX_Fp_add_shallow}{GEN y, GEN x, GEN p} add the \kbd{Fp}~\kbd{x}
+to the \kbd{FpX}~\kbd{y}, using a shallow copy (result not suitable for
+\kbd{gerepileupto})
+
+\fun{GEN}{FpX_Fp_sub}{GEN y, GEN x, GEN p} subtract the \kbd{Fp}~\kbd{x} from
+the \kbd{FpX}~\kbd{y}.
+
+\fun{GEN}{FpX_Fp_sub_shallow}{GEN y, GEN x, GEN p} subtract the
+\kbd{Fp}~\kbd{x} from the \kbd{FpX}~\kbd{y}, using a shallow copy (result not
+suitable for \kbd{gerepileupto})
+
+\fun{GEN}{Fp_FpX_sub}{GEN x,GEN y,GEN p} returns $x - y$, where $x$ is
+a \typ{INT} and $y$ an \kbd{FpX}.
+
+\fun{GEN}{FpX_Fp_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{FpX}~\kbd{x}
+by the \kbd{Fp}~\kbd{y}.
+
+\fun{GEN}{FpX_Fp_mulspec}{GEN x, GEN y, GEN p, long lx} see \kbd{ZX\_mulspec}
+
+\fun{GEN}{FpX_mulu}{GEN x, ulong y, GEN p} multiplies the \kbd{FpX}~\kbd{x}
+by \kbd{y}.
+
+\fun{GEN}{FpX_Fp_mul_to_monic}{GEN y,GEN x,GEN p} returns $y\*x$ assuming the
+result is monic of the same degree as $y$ (in particular $x\neq 0$).
+
+\subsubsec{Miscellaneous operations}
+
+\fun{GEN}{FpX_normalize}{GEN z, GEN p} divides the \kbd{FpX}~\kbd{z} by its
+leading coefficient. If the latter is~$1$, \kbd{z} itself is returned, not a
+copy. If not, the inverse remains uncollected on the stack.
+
+\fun{GEN}{FpX_invBarrett}{GEN T, GEN p}, returns the Barrett inverse
+$M$ of $T$ defined by $M(x)\*x^n\*T(1/x)\equiv 1\pmod{x^{n-1}}$ where $n$ is
+the degree of $T$.
+
+\fun{GEN}{FpX_rescale}{GEN P, GEN h, GEN p} returns $h^{\deg(P)} P(x/h)$.
+\kbd{P} is an \kbd{FpX} and \kbd{h} is a non-zero \kbd{Fp} (the routine would
+work with any non-zero \typ{INT} but is not efficient in this case).
+
+\fun{GEN}{FpX_eval}{GEN x, GEN y, GEN p} evaluates the \kbd{FpX}~\kbd{x}
+at the \kbd{Fp}~\kbd{y}. The result is an~\kbd{Fp}.
+
+\fun{GEN}{FpXV_FpC_mul}{GEN V, GEN W, GEN p} multiplies a non-empty line
+vector of\kbd{FpX} by a column vector of \kbd{Fp} of compatible dimensions.
+The result is an~\kbd{FpX}.
+
+\fun{GEN}{FpXV_prod}{GEN V, GEN p}, \kbd{V} being a vector of \kbd{FpX},
+returns their product.
+
+\fun{GEN}{FpV_roots_to_pol}{GEN V, GEN p, long v}, \kbd{V} being a vector
+of \kbd{INT}s, returns the monic \kbd{FpX}
+$\prod_i (\kbd{pol\_x[v]} - \kbd{V[i]})$.
+
+\fun{GEN}{FpX_chinese_coprime}{GEN x,GEN y, GEN Tx,GEN Ty, GEN Tz, GEN p}:
+returns an \kbd{FpX}, congruent to \kbd{x} mod \kbd{Tx} and to \kbd{y} mod
+\kbd{Ty}. Assumes \kbd{Tx} and \kbd{Ty} are coprime, and \kbd{Tz = Tx * Ty}
+or \kbd{NULL} (in which case it is computed within).
+
+\fun{GEN}{FpV_polint}{GEN x, GEN y, GEN p} returns the \kbd{FpX}
+interpolation polynomial with value \kbd{y[i]} at \kbd{x[i]}. Assumes lengths
+are the same, components are \typ{INT}s, and the \kbd{x[i]} are distinct
+modulo \kbd{p}.
+
+\fun{int}{FpX_is_squarefree}{GEN f, GEN p} returns $1$ if the
+\kbd{FpX}~\kbd{f} is squarefree, $0$ otherwise.
+
+\fun{int}{FpX_is_irred}{GEN f, GEN p} returns $1$ if the \kbd{FpX}~\kbd{f}
+is irreducible, $0$ otherwise. Assumes that \kbd{p} is prime. If~\kbd{f} has
+few factors, \kbd{FpX\_nbfact(f,p) == 1} is much faster.
+
+\fun{int}{FpX_is_totally_split}{GEN f, GEN p} returns $1$ if the
+\kbd{FpX}~\kbd{f} splits into a product of distinct linear factors, $0$
+otherwise. Assumes that \kbd{p} is prime.
+
+\fun{GEN}{FpX_factor}{GEN f, GEN p}, factors the \kbd{FpX}~\kbd{f}. Assumes
+that \kbd{p} is prime. The returned value \kbd{v} is a \typ{VEC} with two
+components: \kbd{v[1]} is a vector of distinct irreducible (\kbd{FpX})
+factors, and \kbd{v[2]} is a \typ{VECSMALL} of corresponding exponents. The
+order of the factors is deterministic (the computation is not).
+
+\fun{long}{FpX_nbfact}{GEN f, GEN p}, assuming the \kbd{FpX}~f is squarefree,
+returns the number of its irreducible factors. Assumes that \kbd{p} is prime.
+
+\fun{long}{FpX_degfact}{GEN f, GEN p}, as \kbd{FpX\_factor}, but the
+degrees of the irreducible factors are returned instead of the factors
+themselves (as a \typ{VECSMALL}). Assumes that \kbd{p} is prime.
+
+\fun{long}{FpX_nbroots}{GEN f, GEN p} returns the number of distinct
+roots in \kbd{\Z/p\Z} of the \kbd{FpX}~\kbd{f}. Assumes that \kbd{p} is prime.
+
+\fun{GEN}{FpX_oneroot}{GEN f, GEN p} returns one root in \kbd{\Z/p\Z} of
+the \kbd{FpX}~\kbd{f}. Return \kbd{NULL} if no root exists.
+Assumes that \kbd{p} is prime.
+
+\fun{GEN}{FpX_roots}{GEN f, GEN p} returns the roots in \kbd{\Z/p\Z} of
+the \kbd{FpX}~\kbd{f} (without multiplicity, as a vector of \kbd{Fp}s).
+Assumes that \kbd{p} is prime.
+
+\fun{GEN}{random_FpX}{long d, long v, GEN p} returns a random \kbd{FpX}
+in variable \kbd{v}, of degree less than~\kbd{d}.
+
+\fun{GEN}{FpX_resultant}{GEN x, GEN y, GEN p} returns the resultant
+of \kbd{x} and \kbd{y}, both \kbd{FpX}. The result is a \typ{INT}
+belonging to $[0,p-1]$.
+
+\fun{GEN}{FpX_disc}{GEN x, GEN p} returns the discriminant
+of the \kbd{FpX} \kbd{x}. The result is a \typ{INT} belonging to $[0,p-1]$.
+
+\fun{GEN}{FpX_FpXY_resultant}{GEN a, GEN b, GEN p}, \kbd{a} a \typ{POL} of
+\typ{INT}s (say in variable $X$), \kbd{b} a \typ{POL} (say in variable $X$)
+whose coefficients are either \typ{POL}s in $\Z[Y]$ or \typ{INT}s.
+Returns $\text{Res}_X(a, b)$ in $\F_p[Y]$ as an \kbd{FpY}. The function
+assumes that $X$ has lower priority than $Y$.
+
+\subsec{\kbd{FpXQ}, \kbd{Fq}} Let \kbd{p} a \typ{INT} and \kbd{T} an
+\kbd{FpX} for \kbd{p}, both to be given in the function arguments; an \kbd{FpXQ}
+object is an \kbd{FpX} whose degree is strictly less than the degree of
+\kbd{T}. An \kbd{Fq} is either an \kbd{FpXQ} or an \kbd{Fp}. Both represent
+a class in $(\Z/\kbd{p}\Z)[X] / (T)$, in which all operations below take
+place. In addition, \kbd{Fq} routines also allow $\kbd{T} = \kbd{NULL}$, in
+which case no reduction mod \kbd{T} is performed on the result.
+
+For efficiency, the routines in this section may leave small unused objects
+behind on the stack (their output is still suitable for \kbd{gerepileupto}).
+Besides \kbd{T} and \kbd{p}, arguments are either \kbd{FpXQ} or \kbd{Fq}
+depending on the function name. (All \kbd{Fq} routines accept \kbd{FpXQ}s by
+definition, not the other way round.)
+
+\subsubsec{Preconditioned reduction}
+
+For faster reduction, the modulus \kbd{T} can be replaced by an extended
+modulus, which is an \kbd{FpXT}, in all \kbd{FpXQ}- and \kbd{Fq}-classes
+functions, and in \kbd{FpX\_rem} and \kbd{FpX\_divrem}.
+
+\fun{GEN}{FpX_get_red}{GEN T, GEN p} returns the extended modulus \kbd{eT}.
+
+To write code that works both with plain and extended moduli, the following
+accessors are defined:
+
+\fun{GEN}{get_FpX_mod}{GEN eT} returns the underlying modulus \kbd{T}.
+
+\fun{GEN}{get_FpX_var}{GEN eT} returns the variable number of the modulus.
+
+\fun{GEN}{get_FpX_degree}{GEN eT} returns the degree of the modulus.
+
+Furthermore, \kbd{ZXT\_to\_FlxT} allows to convert an extended modulus for
+a \kbd{FpX} to an extended modulus for the corresponding \kbd{Flx}.
+
+\subsubsec{Conversions}
+
+\fun{GEN}{Rg_is_FpXQ}{GEN z, GEN *T, GEN *p}, checks if \kbd{z} is a \kbd{GEN}
+which can be mapped to $\F_p[X]/(T)$: anything for which \kbd{Rg\_is\_Fp} return
+$1$, a \typ{POL} for which \kbd{RgX\_to\_FpX} return $1$, a \typ{POLMOD}
+whose modulus is equal to \kbd{*T} if \kbd{*T} is not \kbd{NULL} (once mapped
+to a \kbd{FpX}).
+If an integer modulus is found it is put in \kbd{*p}, else \kbd{*p} is left
+unchanged. If a polynomial modulus is found it is put in \kbd{*T}, else
+\kbd{*T} is left unchanged.
+
+\fun{int}{RgX_is_FpXQX}{GEN z, GEN *T, GEN *p}, \kbd{z} a \typ{POL},
+checks if it can be mapped to a \kbd{FpXQX}, by checking \kbd{Rg\_is\_FpXQ}
+coefficientwise.
+
+\fun{GEN}{Rg_to_FpXQ}{GEN z, GEN T, GEN p}, \kbd{z} a \kbd{GEN} which can be
+mapped to $\F_p[X]/(T)$: anything \kbd{Rg\_to\_Fp} can be applied to,
+a \typ{POL} to which \kbd{RgX\_to\_FpX} can be applied to, a \typ{POLMOD}
+whose modulus is divisible by $T$ (once mapped to a \kbd{FpX}), a suitable
+\typ{RFRAC}. Returns \kbd{z} as an \kbd{FpXQ}, normalized.
+
+\fun{GEN}{RgX_to_FpXQX}{GEN z, GEN T, GEN p}, \kbd{z} a \typ{POL}, returns the
+\kbd{FpXQ} obtained by applying \kbd{Rg\_to\_FpXQ} coefficientwise.
+
+\fun{GEN}{RgX_to_FqX}{GEN z, GEN T, GEN p}: let \kbd{z} be a \typ{POL};
+returns the \kbd{FpXQ} obtained by applying \kbd{Rg\_to\_FpXQ}
+coefficientwise and simplifying scalars to \typ{INT}s.
+
+\fun{GEN}{Fq_red}{GEN x, GEN T, GEN p}, \kbd{x} a \kbd{ZX} or \typ{INT},
+reduce it to an \kbd{Fq} ($\kbd{T} = \kbd{NULL}$ is allowed iff \kbd{x} is a
+\typ{INT}).
+
+\fun{GEN}{FqX_red}{GEN x, GEN T, GEN p}, \kbd{x} a \typ{POL}
+whose coefficients are \kbd{ZX}s or \typ{INT}s, reduce them to \kbd{Fq}s. (If
+$\kbd{T} = \kbd{NULL}$, as \kbd{FpXX\_red(x, p)}.)
+
+\fun{GEN}{FqV_red}{GEN x, GEN T, GEN p}, \kbd{x} a vector of \kbd{ZX}s or
+\typ{INT}s, reduce them to \kbd{Fq}s. (If $\kbd{T} = \kbd{NULL}$, only
+reduce components mod \kbd{p} to \kbd{FpX}s or \kbd{Fp}s.)
+
+\fun{GEN}{FpXQ_red}{GEN x, GEN T,GEN p} \kbd{x} a \typ{POL}
+whose coefficients are \typ{INT}s, reduce them to \kbd{FpXQ}s.
+
+\subsec{\kbd{FpXQ}}
+
+\fun{GEN}{FpXQ_add}{GEN x, GEN y, GEN T,GEN p}
+
+\fun{GEN}{FpXQ_sub}{GEN x, GEN y, GEN T,GEN p}
+
+\fun{GEN}{FpXQ_mul}{GEN x, GEN y, GEN T,GEN p}
+
+\fun{GEN}{FpXQ_sqr}{GEN x, GEN T, GEN p}
+
+\fun{GEN}{FpXQ_div}{GEN x, GEN y, GEN T,GEN p}
+
+\fun{GEN}{FpXQ_inv}{GEN x, GEN T, GEN p} computes the inverse of \kbd{x}
+
+\fun{GEN}{FpXQ_invsafe}{GEN x,GEN T,GEN p}, as \kbd{FpXQ\_inv}, returning
+\kbd{NULL} if \kbd{x} is not invertible.
+
+\fun{GEN}{FpXQX_renormalize}{GEN x, long lx}
+
+\fun{GEN}{FpXQ_pow}{GEN x, GEN n, GEN T, GEN p} computes $\kbd{x}^\kbd{n}$.
+
+\fun{GEN}{FpXQ_powu}{GEN x, ulong n, GEN T, GEN p} computes $\kbd{x}^\kbd{n}$
+for small $n$.
+
+\fun{GEN}{FpXQ_log}{GEN a, GEN g, GEN ord, GEN T, GEN p} Let \kbd{g} be of
+order \kbd{ord} in the finite field $\F_p[X]/(T)$, return $e$ such that
+$a^e=g$. If $e$ does not exists, the result is currently undefined. Assumes
+that \kbd{T} is irreducible mod \kbd{p}.
+
+\fun{GEN}{Fp_FpXQ_log}{GEN a, GEN g, GEN ord, GEN T, GEN p} As
+\kbd{FpXQ\_log}, \kbd{a} being a \kbd{Fp}.
+
+\fun{GEN}{FpXQ_order}{GEN a, GEN ord, GEN T, GEN p} returns the order of the
+\typ{FpXQ} \kbd{a}. If \kbd{o} is non-\kbd{NULL}, it is assumed that \kbd{o}
+is a multiple of the order of \kbd{a}, either as a \typ{INT} or a
+factorization matrix. Assumes that \kbd{T} is irreducible mod \kbd{p}.
+
+\fun{int}{FpXQ_issquare}{GEN x, GEN T, GEN p} returns $1$ if $x$ is a square
+and $0$ otherwise. Assumes that \kbd{T} is irreducible mod \kbd{p}.
+
+\fun{GEN}{FpXQ_sqrt}{GEN x, GEN T, GEN p} returns a square root of \kbd{x}.
+Return \kbd{NULL} if \kbd{x} is not a square.
+
+\fun{GEN}{FpXQ_sqrtn}{GEN x, GEN n, GEN T, GEN p, GEN *zn} returns an
+\kbd{n}-th root of $\kbd{x}$.  Return \kbd{NULL} if \kbd{x} is not an
+\kbd{n}-th power residue. Otherwise, if \kbd{zn} is non-\kbd{NULL} set it to
+a primitive \kbd{n}-th root of the unity. Assumes that \kbd{T} is irreducible
+mod \kbd{p}.
+
+\subsec{\kbd{Fq}}
+
+\fun{GEN}{Fq_add}{GEN x, GEN y, GEN T/*unused*/, GEN p}
+
+\fun{GEN}{Fq_sub}{GEN x, GEN y, GEN T/*unused*/, GEN p}
+
+\fun{GEN}{Fq_mul}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{Fq_Fp_mul}{GEN x, GEN y, GEN T, GEN p} multiplies the \kbd{Fq} $x$
+by the \typ{INT} $y$.
+
+\fun{GEN}{Fq_mulu}{GEN x, ulong y, GEN T, GEN p} multiplies the \kbd{Fq} $x$
+by the scalar $y$.
+
+\fun{GEN}{Fq_sqr}{GEN x, GEN T, GEN p}
+
+\fun{GEN}{Fq_neg}{GEN x, GEN T, GEN p}
+
+\fun{GEN}{Fq_neg_inv}{GEN x, GEN T, GEN p} computes $-\kbd{x}^{-1}$
+
+\fun{GEN}{Fq_inv}{GEN x, GEN pol, GEN p} computes $\kbd{x}^{-1}$, raising an
+error if \kbd{x} is not invertible.
+
+\fun{GEN}{Fq_invsafe}{GEN x, GEN pol, GEN p} as \kbd{Fq\_inv}, but returns
+\kbd{NULL} if \kbd{x} is not invertible.
+
+\fun{GEN}{Fq_div}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FqV_inv}{GEN x, GEN T, GEN p} $x$ being a vector of \typ{Fq}s,
+return the vector of inverses of the $x[i]$. The routine uses Montgomery's
+trick, and involves a single inversion, plus $3(N-1)$ multiplications for
+$N$ entries. The routine is not stack-clean: $2N$ \kbd{FpXQ} are left on
+stack, besides the $N$ in the result.
+
+\fun{GEN}{Fq_pow}{GEN x, GEN n, GEN pol, GEN p} returns $\kbd{x}^\kbd{n}$.
+
+\fun{GEN}{Fq_powu}{GEN x, ulong n, GEN pol, GEN p} returns $\kbd{x}^\kbd{n}$
+for small $n$.
+
+\fun{int}{Fq_issquare}{GEN x, GEN T, GEN p} returns $1$ if $x$ is a square
+and $0$ otherwise. Assumes that \kbd{T} is irreducible mod \kbd{p}. $T =
+\kbd{NULL}$ is forbidden unless $x$ is an \kbd{Fp}.
+
+\fun{GEN}{Fq_sqrt}{GEN x, GEN T, GEN p} returns a square root of \kbd{x}.
+Return \kbd{NULL} if \kbd{x} is not a square.
+
+\fun{GEN}{Fq_sqrtn}{GEN x, GEN n, GEN T, GEN p, GEN *zn} returns an
+\kbd{n}-th root of $\kbd{x}$.  Return \kbd{NULL} if \kbd{x} is not an
+\kbd{n}-th power residue. Otherwise, if \kbd{zn} is non-\kbd{NULL} set it to
+a primitive \kbd{n}-th root of the unity. Assumes that \kbd{T} is irreducible
+mod \kbd{p}.
+
+\fun{GEN}{FpXQ_charpoly}{GEN x, GEN T, GEN p} returns the characteristic
+polynomial of \kbd{x}
+
+\fun{GEN}{FpXQ_minpoly}{GEN x, GEN T, GEN p} returns the minimal polynomial
+of \kbd{x}
+
+\fun{GEN}{FpXQ_norm}{GEN x, GEN T, GEN p} returns the norm of \kbd{x}
+
+\fun{GEN}{FpXQ_trace}{GEN x, GEN T, GEN p} returns the trace of \kbd{x}
+
+\fun{GEN}{FpXQ_conjvec}{GEN x, GEN T, GEN p} returns the vector of conjugates
+$[x,x^p,x^{p^2},\ldots,x^{p^{n-1}}]$ where $n$ is the degree of $T$.
+
+\fun{GEN}{gener_FpXQ}{GEN T, GEN p, GEN *po} returns a primitive root modulo
+$(T,p)$. $T$ is an \kbd{FpX} assumed to be irreducible modulo the prime
+$p$. If \kbd{po} is not \kbd{NULL} it is set to $[o,\var{fa}]$, where $o$ is
+the order of the multiplicative group of the finite field, and \var{fa} is
+its factorization.
+
+\fun{GEN}{gener_FpXQ_local}{GEN T, GEN p, GEN L}, \kbd{L} being a vector of
+primes dividing $p^{\deg T} - 1$, returns an element of $G:=\F_p[x]/(T)$
+which is a generator of the $\ell$-Sylow of $G$ for every $\ell$ in
+\kbd{L}. It is not necessary, and in fact slightly inefficient, to include
+$\ell=2$, since 2 is treated separately in any case, i.e. the generator
+obtained is never a square if $p$ is odd.
+
+\fun{GEN}{FpXQ_powers}{GEN x, long n, GEN T, GEN p} returns $[\kbd{x}^0,
+\dots, \kbd{x}^\kbd{n}]$ as a \typ{VEC} of \kbd{FpXQ}s.
+
+\fun{GEN}{FpXQ_matrix_pow}{GEN x, long m, long n, GEN T, GEN p}, as
+\kbd{FpXQ\_powers}$(x, n-1, T, p)$, but returns the powers as a an
+$m\times n$ matrix. Usually, we have $m = n = \deg T$.
+
+\fun{GEN}{FpXQ_autpow}{GEN a, ulong n, GEN T, GEN p} computes $\sigma^n(X)$
+assuming $a=\sigma(X)$ where $\sigma$ is an automorphism of the algebra
+$\F_p[X]/T(X)$.
+
+\fun{GEN}{FpXQ_autsum}{GEN a, ulong n, GEN T, GEN p}
+$\sigma$ being the automorphism defined by $\sigma(X)=a[1]\pmod{T(X)}$,
+returns the vector $[\sigma^n(X),b\sigma(b)\ldots\sigma^{n-1}(b)]$
+where $b=a[2]$.
+
+\fun{GEN}{FpXQ_autpowers}{GEN S, long n, GEN T, GEN p} returns
+$[x,S(x),S(S(x)),\dots,S^{(n)}(x)]$ as a \typ{VEC} of \kbd{FpXQ}s.
+
+\fun{GEN}{FpX_FpXQ_eval}{GEN f,GEN x,GEN T,GEN p} returns
+$\kbd{f}(\kbd{x})$.
+
+\fun{GEN}{FpX_FpXQV_eval}{GEN f,GEN V,GEN T,GEN p} returns
+$\kbd{f}(\kbd{x})$, assuming that \kbd{V} was computed by
+$\kbd{FpXQ\_powers}(\kbd{x}, n, \kbd{T}, \kbd{p})$.
+
+\subsec{\kbd{FpXX}, \kbd{FpXY}}
+Contrary to what the name implies, an \kbd{FpXX} is a \typ{POL} whose
+coefficients are either \typ{INT}s or \typ{FpX}s. This reduces memory
+overhead at the expense of consistency. The prefix \kbd{FpXY} is an
+alias for \kbd{FpXX} when variables matters.
+
+\fun{GEN}{FpXX_red}{GEN z, GEN p}, \kbd{z} a \typ{POL} whose coefficients are
+either \kbd{ZX}s or \typ{INT}s. Returns the \typ{POL} equal to \kbd{z} with
+all components reduced modulo \kbd{p}.
+
+\fun{GEN}{FpXX_renormalize}{GEN x, long l}, as \kbd{normalizepol}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{GEN}{FpXX_add}{GEN x, GEN y, GEN p} adds \kbd{x} and \kbd{y}.
+
+\fun{GEN}{FpXX_sub}{GEN x, GEN y, GEN p} returns $\kbd{x}-\kbd{y}$.
+
+\fun{GEN}{FpXX_neg}{GEN x, GEN p} returns $-\kbd{x}$.
+
+\fun{GEN}{FpXX_Fp_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{FpXX}~\kbd{x}
+by the \kbd{Fp}~\kbd{y}.
+
+\fun{GEN}{FpXX_FpX_mul}{GEN x, GEN y, GEN p} multiplies the coefficients of the
+\kbd{FpXX}~\kbd{x} by the \kbd{FpX}~\kbd{y}.
+
+\fun{GEN}{FpXX_mulu}{GEN x, GEN y, GEN p} multiplies the \kbd{FpXX}~\kbd{x}
+by the scalar \kbd{y}.
+
+\fun{GEN}{FpXY_eval}{GEN Q, GEN y, GEN x, GEN p} $Q$ being an \kbd{FpXY},
+i.e.~a \typ{POL} with \kbd{Fp} or \kbd{FpX} coefficients representing an
+element of $\F_p[X][Y]$. Returns the \kbd{Fp} $Q(x,y)$.
+
+\fun{GEN}{FpXY_evalx}{GEN Q, GEN x, GEN p} $Q$ being an \kbd{FpXY}, returns the
+\kbd{FpX} $Q(x,Y)$, where $Y$ is the main variable of $Q$.
+
+\fun{GEN}{FpXY_evaly}{GEN Q, GEN y, GEN p, long vx} $Q$ an \kbd{FpXY}, returns
+the \kbd{FpX} $Q(X,y)$, where $X$ is the second variable of $Q$, and \kbd{vx}
+is the variable number of $X$.
+
+\fun{GEN}{FpXY_Fq_evaly}{GEN Q, GEN y, GEN T, GEN p, long vx} $Q$ an \kbd{FpXY}
+and $y$ being an \kbd{Fq}, returns the \kbd{FqX} $Q(X,y)$, where $X$ is the
+second variable of $Q$, and \kbd{vx} is the variable number of $X$.
+
+\fun{GEN}{FpXY_FpXQ_evalx}{GEN x, GEN Y, ulong p} $Q$ an \kbd{FpXY} and
+$y$ being an \kbd{FpXQ}, returns the \kbd{FpXQX} $Q(x,Y)$, where $Y$ is the
+first variable of $Q$.
+
+\fun{GEN}{FpXYQQ_pow}{GEN x, GEN n, GEN S, GEN T, GEN p}, \kbd{x} being a
+\kbd{FpXY}, \kbd{T} being a \kbd{FpX} and \kbd{S} being a \kbd{FpY},
+return $x^n \pmod{S,T,p}$.
+
+\subsec{\kbd{FpXQX}, \kbd{FqX}}
+Contrary to what the name implies, an \kbd{FpXQX} is a \typ{POL} whose
+coefficients are \kbd{Fq}s. So the only difference between \kbd{FqX} and
+\kbd{FpXQX} routines is that $\kbd{T} = \kbd{NULL}$ is not allowed in the
+latter. (It was thought more useful to allow \typ{INT} components than to
+enforce strict consistency, which would not imply any efficiency gain.)
+
+\subsubsec{Basic operations}
+
+\fun{GEN}{FqX_add}{GEN x,GEN y,GEN T,GEN p}
+
+\fun{GEN}{FqX_Fq_add}{GEN x, GEN y, GEN T, GEN p} adds the
+\kbd{Fq}~\kbd{y} to the \kbd{FqX}~\kbd{x}.
+
+\fun{GEN}{FqX_neg}{GEN x,GEN T,GEN p}
+
+\fun{GEN}{FqX_sub}{GEN x,GEN y,GEN T,GEN p}
+
+\fun{GEN}{FqX_mul}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FqX_Fq_mul}{GEN x, GEN y, GEN T, GEN p} multiplies the
+\kbd{FqX}~\kbd{x} by the \kbd{Fq}~\kbd{y}.
+
+\fun{GEN}{FqX_mulu}{GEN x, ulong y, GEN T, GEN p} multiplies the
+\kbd{FqX}~\kbd{x} by the scalar~\kbd{y}.
+
+\fun{GEN}{FqX_Fp_mul}{GEN x, GEN y, GEN T, GEN p} multiplies the
+\kbd{FqX}~\kbd{x} by the \typ{INT}~\kbd{y}.
+
+\fun{GEN}{FqX_Fq_mul_to_monic}{GEN x, GEN y, GEN T, GEN p}
+returns $x\*y$ assuming the result is monic of the same degree as $x$ (in
+particular $y\neq 0$).
+
+\fun{GEN}{FqX_normalize}{GEN z, GEN T, GEN p} divides the \kbd{FqX}~\kbd{z}
+by its leading term. The leading coefficient becomes $1$ as a \typ{INT}.
+
+\fun{GEN}{FqX_sqr}{GEN x, GEN T, GEN p}
+
+\fun{GEN}{FqX_divrem}{GEN x, GEN y, GEN T, GEN p, GEN *z}
+
+\fun{GEN}{FqX_div}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FqX_rem}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FqX_deriv}{GEN x, GEN T, GEN p} returns the derivative of \kbd{x}.
+(This function is suitable for \kbd{gerepilupto} but not memory-clean.)
+
+\fun{GEN}{FqX_translate}{GEN P, GEN c, GEN T, GEN p} let $c$ be an \kbd{Fq}
+defined modulo $(p, T)$, and let $P$ be an \kbd{FqX}; returns the translated
+\kbd{FqX} of $P(X+c)$.
+
+\fun{GEN}{FqX_gcd}{GEN P, GEN Q, GEN T, GEN p} returns a (not necessarily
+monic) greatest common divisor of $x$  and $y$.
+
+\fun{GEN}{FqX_extgcd}{GEN x, GEN y, GEN T, GEN p, GEN *ptu, GEN *ptv}
+returns $d = \text{GCD}(\kbd{x},\kbd{y})$ (not necessarily monic), and sets
+\kbd{*u}, \kbd{*v} to the Bezout coefficients such that $\kbd{*ux} +
+\kbd{*vy} = d$.
+
+\fun{GEN}{FqX_eval}{GEN x, GEN y, GEN T, GEN p} evaluates the \kbd{FqX}~\kbd{x}
+at the \kbd{Fq}~\kbd{y}. The result is an~\kbd{Fq}.
+
+\fun{GEN}{FqXY_eval}{GEN Q, GEN y, GEN x, GEN T, GEN p} $Q$ an \kbd{FqXY},
+i.e.~a \typ{POL} with \kbd{Fq} or \kbd{FqX} coefficients representing an
+element of $\F_q[X][Y]$. Returns the \kbd{Fq} $Q(x,y)$.
+
+\fun{GEN}{FqXY_evalx}{GEN Q, GEN x, GEN T, GEN p} $Q$ being an \kbd{FqXY},
+returns the \kbd{FqX} $Q(x,Y)$, where $Y$ is the main variable of $Q$.
+
+\fun{GEN}{FpXQX_red}{GEN z, GEN T, GEN p} \kbd{z} a \typ{POL} whose
+coefficients are \kbd{ZX}s or \typ{INT}s, reduce them to \kbd{FpXQ}s.
+
+\fun{GEN}{FpXQX_mul}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{Kronecker_to_FpXQX}{GEN z, GEN T, GEN p}. Let $n = \deg T$ and let
+$P(X,Y)\in \Z[X,Y]$ lift a polynomial in $K[Y]$, where $K := \F_p[X]/(T)$ and
+$\deg_X P < 2n-1$ --- such as would result from multiplying minimal degree
+lifts of two polynomials in $K[Y]$. Let $z = P(t,t^{2*n-1})$ be a Kronecker
+form of $P$, this function returns $Q\in \Z[X,t]$ such that $Q$ is congruent to
+$P(X,t)$ mod $(p, T(X))$, $\deg_X Q < n$, and all coefficients are in $[0,p[$.
+Not stack-clean. Note that $t$ need not be the same variable as $Y$!
+
+\fun{GEN}{FpXQX_FpXQ_mul}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FpXQX_sqr}{GEN x, GEN T, GEN p}
+
+\fun{GEN}{FpXQX_divrem}{GEN x, GEN y, GEN T, GEN p, GEN *pr}
+
+\fun{GEN}{FpXQX_div}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FpXQX_rem}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FpXQX_invBarrett}{GEN y, GEN T, GEN p} returns the Barrett inverse of
+the \kbd{FpXQX} $y$, namely a lift of $1/\kbd{polrecip}(y)+O(x^{\deg(y)-1})$.
+
+\fun{GEN}{FpXQX_rem_Barrett}{GEN x, GEN iy, GEN y, GEN T, GEN p} returns $x$
+mod $y$, assuming \kbd{iy} is the Barrett inverse of $y$.
+
+\fun{GEN}{FpXQX_divrem_Barrett}{GEN x, GEN iy, GEN y, GEN T, GEN p, GEN *pr}
+performs the Euclidean division of $x$ by $y$, assuming \kbd{iy} is the Barrett
+inverse of $y$. Returns the quotient and set \kbd{*pr} to the remainder.
+
+\fun{GEN}{FpXQXV_prod}{GEN V, GEN T, GEN p}, \kbd{V} being a vector of
+\kbd{FpXQX}, returns their product.
+
+\fun{GEN}{FpXQX_gcd}{GEN x, GEN y, GEN T, GEN p}
+
+\fun{GEN}{FpXQX_extgcd}{GEN x, GEN y, GEN T, GEN p, GEN *ptu, GEN *ptv}
+
+\fun{GEN}{FpXQX_FpXQXQ_eval}{GEN f,GEN x,GEN S, GEN T,GEN p} returns
+$\kbd{f}(\kbd{x})$.
+
+\fun{GEN}{FpXQX_FpXQXQV_eval}{GEN f,GEN V,GEN S,GEN T,GEN p} returns
+$\kbd{f}(\kbd{x})$, assuming that \kbd{V} was computed by
+$\kbd{FpXQXQ\_powers}(\kbd{x}, n, \kbd{S}, \kbd{T}, \kbd{p})$.
+
+\fun{GEN}{FpXQXQ_div}{GEN x, GEN y, GEN S, GEN T, GEN p}, \kbd{x}, \kbd{y} and
+\kbd{S} being \kbd{FpXQX}s, returns $\kbd{x}*\kbd{y}^{-1}$ modulo \kbd{S}.
+
+\fun{GEN}{FpXQXQ_inv}{GEN x, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FpXQX}s, returns $\kbd{x}^{-1}$ modulo \kbd{S}.
+
+\fun{GEN}{FpXQXQ_invsafe}{GEN x, GEN S, GEN T,GEN p}, as \kbd{FpXQXQ\_inv},
+returning \kbd{NULL} if \kbd{x} is not invertible.
+
+\fun{GEN}{FpXQXQ_mul}{GEN x, GEN y, GEN S, GEN T, GEN p}, \kbd{x}, \kbd{y} and
+\kbd{S} being \kbd{FpXQX}s, returns $\kbd{x}\*\kbd{y}$ modulo \kbd{S}.
+
+\fun{GEN}{FpXQXQ_sqr}{GEN x, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FpXQX}s, returns $\kbd{x}^2$ modulo \kbd{S}.
+
+\fun{GEN}{FpXQXQ_pow}{GEN x, GEN n, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FpXQX}s, returns $\kbd{x}^\kbd{n}$ modulo \kbd{S}.
+
+\fun{GEN}{FpXQXQ_powers}{GEN x, long n, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FpXQX}s, returns $[\kbd{x}^0, \dots, \kbd{x}^\kbd{n}]$ as a
+\typ{VEC} of \kbd{FpXQXQ}s.
+
+\fun{GEN}{FpXQXQ_matrix_pow}{GEN x, long m, long n, GEN S, GEN T, GEN p}
+returns the same powers of \kbd{x} as \kbd{FpXQXQ\_powers}$(x, n-1,S, T, p)$,
+but as an $m\times n$ matrix.
+
+\fun{GEN}{FpXQXQV_autpow}{GEN a, long n, GEN S, GEN T, GEN p}
+$\sigma$ being the automorphism defined by $\sigma(X)=a[1]\pmod{T(X)}$,
+$\sigma(Y)=a[2]\pmod{S(X,Y),T(X)}$, returns $[\sigma^n(X),\sigma^n(Y)]$.
+
+\fun{GEN}{FpXQXQV_autsum}{GEN a, long n, GEN S, GEN T, GEN p}
+$\sigma$ being the automorphism defined by $\sigma(X)=a[1]\pmod{T(X)}$,
+$\sigma(Y)=a[2]\pmod{S(X,Y),T(X)}$, returns the vector
+$[\sigma^n(X),\sigma^n(Y),b\sigma(b)\ldots\sigma^{n-1}(b)]$
+where $b=a[3]$.
+
+% FqXQ
+
+\fun{GEN}{FqXQ_add}{GEN x, GEN y, GEN S, GEN T, GEN p}, \kbd{x}, \kbd{y} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x} + \kbd{y}$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_sub}{GEN x, GEN y, GEN S, GEN T, GEN p}, \kbd{x}, \kbd{y} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x} - \kbd{y}$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_mul}{GEN x, GEN y, GEN S, GEN T, GEN p}, \kbd{x}, \kbd{y} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x}\*\kbd{y}$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_div}{GEN x, GEN y, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x}/\kbd{y}$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_inv}{GEN x, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x}^{-1}$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_invsafe}{GEN x, GEN S, GEN T, GEN p} , as \kbd{FqXQ\_inv},
+returning \kbd{NULL} if \kbd{x} is not invertible.
+
+\fun{GEN}{FqXQ_sqr}{GEN x, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x}^2$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_pow}{GEN x, GEN n, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FqX}s, returns $\kbd{x}^\kbd{n}$ modulo \kbd{S}.
+
+\fun{GEN}{FqXQ_powers}{GEN x, long n, GEN S, GEN T, GEN p}, \kbd{x} and
+\kbd{S} being \kbd{FqX}s, returns $[\kbd{x}^0, \dots, \kbd{x}^\kbd{n}]$ as a
+\typ{VEC} of \kbd{FqXQ}s.
+
+\fun{GEN}{FqXQ_matrix_pow}{GEN x, long m, long n, GEN S, GEN T, GEN p}
+returns the same powers of \kbd{x} as \kbd{FqXQ\_powers}$(x, n-1,S, T, p)$,
+but as an $m\times n$ matrix.
+
+\fun{GEN}{FqV_roots_to_pol}{GEN V, GEN T, GEN p, long v},
+\kbd{V} being a vector of \kbd{Fq}s, returns the monic \kbd{FqX}
+$\prod_i (\kbd{pol\_x[v]} - \kbd{V[i]})$.
+
+\subsubsec{Miscellaneous operations}
+
+\fun{GEN}{init_Fq}{GEN p, long n, long v} returns an irreducible polynomial
+of degree $\kbd{n} > 0$ over $\F_p$, in variable \kbd{v}.
+
+\fun{int}{FqX_is_squarefree}{GEN P, GEN T, GEN p}
+
+\fun{GEN}{FqX_roots}{GEN x, GEN T, GEN p} return the roots of \kbd{x} in
+$\F_p[X]/(T)$. Assumes \kbd{p} is prime and \kbd{T} irreducible in $\F_p[X]$.
+
+\fun{GEN}{FqX_factor}{GEN x, GEN T, GEN p} same output convention as
+\kbd{FpX\_factor}. Assumes \kbd{p} is prime and \kbd{T} irreducible
+in $\F_p[X]$.
+
+\fun{GEN}{FpX_factorff}{GEN P, GEN T, GEN p}. Assumes \kbd{p} prime
+and \kbd{T} irreducible in $\F_p[X]$. Factor the \kbd{FpX} \kbd{P}
+over the finite field $\F_p[Y]/(T(Y))$. See \kbd{FpX\_factorff\_irred}
+if \kbd{P} is known to be irreducible of $\F_p$.
+
+\fun{GEN}{FpX_rootsff}{GEN P, GEN T, GEN p}. Assumes \kbd{p} prime
+and \kbd{T} irreducible in $\F_p[X]$. Returns the roots of the \kbd{FpX}
+\kbd{P} belonging to the finite field $\F_p[Y]/(T(Y))$.
+
+\fun{GEN}{FpX_factorff_irred}{GEN P, GEN T, GEN p}. Assumes \kbd{p} prime
+and \kbd{T} irreducible in $\F_p[X]$. Factors the \emph{irreducible}
+\kbd{FpX} \kbd{P} over the finite field $\F_p[Y]/(T(Y))$ and returns the
+vector of irreducible \kbd{FqX}s factors (the exponents, being all equal to
+$1$, are not included).
+
+\fun{GEN}{FpX_ffisom}{GEN P, GEN Q, GEN p}. Assumes \kbd{p} prime,
+\kbd{P}, \kbd{Q} are \kbd{ZX}s, both irreducible mod \kbd{p}, and
+$\deg(P) \mid \deg Q$. Outputs a monomorphism between $\F_p[X]/(P)$ and
+$\F_p[X]/(Q)$, as a polynomial $R$ such that $\kbd{Q} \mid \kbd{P}(R)$ in
+$\F_p[X]$. If \kbd{P} and \kbd{Q} have the same degree, it is of course an
+isomorphism.
+
+\fun{void}{FpX_ffintersect}{GEN P, GEN Q, long n, GEN p, GEN *SP,GEN *SQ, GEN
+MA,GEN MB}\hfil\break
+Assumes \kbd{p} is prime, \kbd{P}, \kbd{Q} are \kbd{ZX}s, both
+irreducible mod \kbd{p}, and \kbd{n} divides both the degree of \kbd{P} and
+\kbd{Q}. Compute \kbd{SP} and \kbd{SQ} such that the subfield of
+$\F_p[X]/(P)$ generated by \kbd{SP} and the subfield of $\F_p[X]/(Q)$
+generated by \kbd{SQ} are isomorphic of degree \kbd{n}. The polynomials
+\kbd{P} and \kbd{Q} do not need to be of the same variable. If \kbd{MA}
+(resp. \kbd{MB}) is not \kbd{NULL}, it must be the matrix of the Frobenius
+map in $\F_p[X]/(P)$ (resp.~$\F_p[X]/(Q)$).
+
+\fun{GEN}{FpXQ_ffisom_inv}{GEN S, GEN T, GEN p}. Assumes \kbd{p} is prime,
+\kbd{T} a \kbd{ZX}, which is irreducible modulo \kbd{p}, \kbd{S} a
+\kbd{ZX} representing an automorphism of $\F_q := \F_p[X]/(\kbd{T})$.
+($\kbd{S}(X)$ is the image of $X$ by the automorphism.) Returns the
+inverse automorphism of \kbd{S}, in the same format, i.e.~an \kbd{FpX}~$H$
+such that $H(\kbd{S}) \equiv X$ modulo $(\kbd{T}, \kbd{p})$.
+
+\fun{long}{FpXQX_nbfact}{GEN S, GEN T, GEN p} returns the number of
+irreducible factors of the polynomial $S$ over the finite field $\F_q$
+defined by $T$ and $p$.
+
+\fun{long}{FqX_nbfact}{GEN S, GEN T, GEN p} as above but accept \kbd{T=NULL}.
+
+\fun{long}{FpXQX_nbroots}{GEN S, GEN T, GEN p} returns the number of roots of
+the polynomial $S$ over the finite field $\F_q$ defined by $T$ and $p$.
+
+\fun{long}{FqX_nbroots}{GEN S, GEN T, GEN p} as above but accept \kbd{T=NULL}.
+
+\fun{GEN}{FpXQX_Frobenius}{GEN S, GEN T, GEN p} returns
+$X^{q}\pmod{S(X)}$ over the finite field $\F_q$ defined by $T$ and $p$, thus
+$q=p^n$ where $n$ is the degree of $T$.
+
+\fun{GEN}{FpXQXQ_halfFrobenius}{GEN A, GEN S, GEN T, GEN p} returns
+$A(X)^{(q-1)/2}\pmod{S(X)}$ over the finite field $\F_q$ defined by $T$
+and $p$, thus $q=p^n$ where $n$ is the degree of $T$.
+
+\subsec{\kbd{Flx}} Let \kbd{p} an understood \kbd{ulong}, assumed to be
+prime, to be given the the function arguments; an \kbd{Fl} is an \kbd{ulong}
+belonging to $[0,\kbd{p}-1]$, an \kbd{Flx}~\kbd{z} is a \typ{VECSMALL}
+representing a polynomial with small integer coefficients. Specifically
+\kbd{z[0]} is the usual codeword, \kbd{z[1] = evalvarn($v$)} for some
+variable $v$, then the coefficients by increasing degree. An \kbd{FlxX} is a
+\typ{POL} whose coefficients are \kbd{Flx}s.
+
+\noindent In the following, an argument called \kbd{sv} is of the form
+\kbd{evalvarn}$(v)$ for some variable number~$v$.
+
+\subsubsec{Preconditioned reduction}
+
+For faster reduction, the modulus \kbd{T} can be replaced by an extended
+modulus, which is an \kbd{FlxT}, in all \kbd{Flxq}-classes functions, and in
+\kbd{Flx\_divrem}.
+
+\fun{GEN}{Flx_get_red}{GEN T, ulong p} returns the extended modulus \kbd{eT}.
+
+To write code that works both with plain and extended moduli, the following
+accessors are defined:
+
+\fun{GEN}{get_Flx_mod}{GEN eT} returns the underlying modulus \kbd{T}.
+
+\fun{GEN}{get_Flx_var}{GEN eT} returns the variable number of the modulus.
+
+\fun{GEN}{get_Flx_degree}{GEN eT} returns the degree of the modulus.
+
+Furthermore, \kbd{ZXT\_to\_FlxT} allows to convert an extended modulus for
+a \kbd{FpX} to an extended modulus for the corresponding \kbd{Flx}.
+
+\subsubsec{Basic operations}
+
+\fun{ulong}{Flx_lead}{GEN x} returns the leading coefficient of $x$ as a
+\kbd{ulong} (return $0$ for the zero polynomial).
+
+\fun{GEN}{Flx_red}{GEN z, ulong p} converts from \kbd{zx} with
+non-negative coefficients to \kbd{Flx} (by reducing them mod \kbd{p}).
+
+\fun{int}{Flx_equal1}{GEN x} returns 1 (true) if the \kbd{Flx} $x$ is equal
+to~1, 0~(false) otherwise.
+
+\fun{int}{Flx_equal}{GEN x, GEN y} returns 1 (true) if the \kbd{Flx} $x$
+and $y$ are equal, and 0~(false) otherwise.
+
+\fun{GEN}{Flx_copy}{GEN x} returns a copy of \kbd{x}.
+
+\fun{GEN}{Flx_add}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{Flx_Fl_add}{GEN y, ulong x, ulong p}
+
+\fun{GEN}{Flx_neg}{GEN x, ulong p}
+
+\fun{GEN}{Flx_neg_inplace}{GEN x, ulong p}, same as \kbd{Flx\_neg}, in place
+(\kbd{x} is destroyed).
+
+\fun{GEN}{Flx_sub}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{Flx_mul}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{Flx_Fl_mul}{GEN y, ulong x, ulong p}
+
+\fun{GEN}{Flx_double}{GEN y, ulong p} returns $2\*y$.
+
+\fun{GEN}{Flx_triple}{GEN y, ulong p} returns $3\*y$.
+
+\fun{GEN}{Flx_mulu}{GEN y, ulong x, ulong p} as \kbd{Flx\_Fl\_mul} but do not
+assume that $x<p$.
+
+\fun{GEN}{Flx_Fl_mul_to_monic}{GEN y, ulong x, ulong p} returns $y\*x$
+assuming the result is monic of the same degree as $y$ (in particular $x\neq
+0$).
+
+\fun{GEN}{Flx_sqr}{GEN x, ulong p}
+
+\fun{GEN}{Flx_divrem}{GEN x, GEN y, ulong p, GEN *pr}
+
+\fun{GEN}{Flx_div}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{Flx_rem}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{Flx_deriv}{GEN z, ulong p}
+
+\fun{GEN}{Flx_gcd}{GEN a, GEN b, ulong p} returns a (not necessarily monic)
+greatest common divisor of $x$  and $y$.
+
+\fun{GEN}{Flx_halfgcd}{GEN x, GEN y, GEN p} returns a two-by-two \kbd{FlxM}
+$M$ with determinant $\pm 1$ such that the image $(a,b)$ of $(x,y)$ by $M$
+has the property that $\deg a \geq {\deg x \over 2} > \deg b$.
+
+\fun{GEN}{Flx_extgcd}{GEN a, GEN b, ulong p, GEN *ptu, GEN *ptv}
+
+\fun{GEN}{Flx_pow}{GEN x, long n, ulong p}
+
+\fun{GEN}{Flx_roots}{GEN f, ulong p} returns the vector of roots
+of $f$ (without multiplicity, as a \typ{VECSMALL}). Assumes that $p$ is
+prime.
+
+\fun{ulong}{Flx_oneroot}{GEN f, ulong p} returns one root $0 \leq r < p$ of
+the \kbd{Flx}~\kbd{f} in \kbd{\Z/p\Z}. Return $p$ if no root exists. Assumes
+that \kbd{p} is prime.
+
+\fun{GEN}{Flx_roots_naive}{GEN f, ulong p} returns the vector of roots
+of $f$ as a \typ{VECSMALL} (multiple roots are not repeated), found
+by an exhaustive search. Efficient for very small $p$ !
+
+\fun{GEN}{Flx_factor}{GEN f, ulong p}
+
+\fun{GEN}{Flx_mod_Xn1}{GEN T, ulong n, ulong p} return $T$ modulo
+$(X^n + 1, p)$. Shallow function.
+
+\fun{GEN}{Flx_mod_Xnm1}{GEN T, ulong n, ulong p} return $T$ modulo
+$(X^n - 1, p)$. Shallow function.
+
+\fun{GEN}{Flx_degfact}{GEN f, ulong p} as \tet{FpX_degfact}.
+
+\fun{GEN}{Flx_factorff_irred}{GEN P, GEN Q, ulong p} as
+\tet{FpX_factorff_irred}.
+
+\fun{GEN}{Flx_ffisom}{GEN P,GEN Q,ulong l} as \tet{FpX_ffisom}.
+
+\subsubsec{Miscellaneous operations}
+
+\fun{GEN}{pol0_Flx}{long sv} returns a zero \kbd{Flx} in variable $v$.
+
+\fun{GEN}{zero_Flx}{long sv} alias for \kbd{pol0\_Flx}
+
+\fun{GEN}{pol1_Flx}{long sv} returns the unit \kbd{Flx} in variable $v$.
+
+\fun{GEN}{polx_Flx}{long sv} returns the variable $v$ as degree~1~\kbd{Flx}.
+
+\fun{GEN}{Flx_normalize}{GEN z, ulong p}, as \kbd{FpX\_normalize}.
+
+\fun{GEN}{random_Flx}{long d, long sv, ulong p} returns a random \kbd{Flx}
+in variable \kbd{v}, of degree less than~\kbd{d}.
+
+\fun{GEN}{Flx_recip}{GEN x}, returns the reciprocal polynomial
+
+\fun{ulong}{Flx_resultant}{GEN a, GEN b, ulong p}, returns the resultant
+of \kbd{a} and \kbd{b}
+
+\fun{ulong}{Flx_extresultant}{GEN a, GEN b, ulong p, GEN *ptU, GEN *ptV}
+given two \kbd{Flx} \kbd{a} and \kbd{b},
+returns their resultant and sets Bezout coefficients (if the resultant is 0,
+the latter are not set).
+
+\fun{GEN}{Flx_invBarrett}{GEN T, ulong p}, returns the Barrett inverse
+$M$ of $T$ defined by $M(x)\*x^n\*T(1/x)\equiv 1\pmod{x^{n-1}}$ where $n$ is
+the degree of $T$.
+
+\fun{GEN}{Flx_renormalize}{GEN x, long l}, as \kbd{FpX\_renormalize}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{GEN}{Flx_shift}{GEN T, long n} returns $\kbd{T} * x^n$ if $n\geq 0$,
+and $\kbd{T} \bs x^{-n}$ otherwise.
+
+\fun{long}{Flx_val}{GEN x} returns the valuation of \kbd{x}, i.e. the
+multiplicity of the $0$ root.
+
+\fun{long}{Flx_valrem}{GEN x, GEN *Z} as \kbd{RgX\_valrem}, returns the
+valuation of \kbd{x}. In particular, if the valuation is $0$, set \kbd{*Z}
+to $x$, not a copy.
+
+\fun{GEN}{Flx_div_by_X_x}{GEN A, ulong a, ulong p, ulong *rem}, returns the
+Euclidean quotient of the \kbd{Flx}~\kbd{A} by $X - \kbd{a}$, and sets
+\kbd{rem} to the remainder $ \kbd{A}(\kbd{a})$.
+
+\fun{ulong}{Flx_eval}{GEN x, ulong y, ulong p}, as \kbd{FpX\_eval}.
+
+\fun{GEN}{Flx_deflate}{GEN P, long d} assuming $P$ is a polynomial of the
+form $Q(X^d)$, return $Q$.
+
+\fun{GEN}{Flx_splitting}{GEN p, long k}, as \tet{RgX_splitting}.
+
+\fun{GEN}{Flx_inflate}{GEN P, long d} returns $P(X^d)$.
+
+\fun{int}{Flx_is_squarefree}{GEN z, ulong p}
+
+\fun{int}{Flx_is_irred}{GEN f, ulong p}, as \kbd{FpX\_is\_irred}.
+
+\fun{int}{Flx_is_smooth}{GEN f, long r, ulong p} return $1$ if all
+irreducible factors of $f$ are of degree at most $r$, $0$ otherwise.
+
+\fun{long}{Flx_nbroots}{GEN f, ulong p}, as \kbd{FpX\_nbroots}.
+
+\fun{long}{Flx_nbfact}{GEN z, ulong p}, as \kbd{FpX\_nbfact}.
+
+\fun{GEN}{Flx_degfact}{GEN f, ulong p}, as \kbd{FpX\_degfact}.
+
+\fun{GEN}{Flx_nbfact_by_degree}{GEN z, long *nb, ulong p} Assume
+that the \kbd{Flx} $z$ is squarefree mod the prime $p$. Returns a
+\typ{VECSMALL} $D$ with $\deg z$ entries, such that $D[i]$ is the number of
+irreducible factors of degree $i$. Set \kbd{nb} to the total number of
+irreducible factors (the sum of the $D[i]$).
+
+\fun{void}{Flx_ffintersect}{GEN P,GEN Q, long n, ulong p, GEN*SP, GEN*SQ, GEN
+MA,GEN MB},\hfil\break
+as \kbd{FpX\_ffintersect}
+
+\fun{GEN}{Flv_polint}{GEN x, GEN y, ulong p, long sv} as \kbd{FpV\_polint},
+returning an \kbd{Flx} in variable $v$.
+
+\fun{GEN}{Flv_roots_to_pol}{GEN a, ulong p, long sv} as
+\kbd{FpV\_roots\_to\_pol} returning an \kbd{Flx} in variable $v$.
+
+\subsec{\kbd{FlxV}} See \kbd{FpXV} operations.
+
+\fun{GEN}{FlxV_Flc_mul}{GEN V, GEN W, ulong p}, as \kbd{FpXV\_FpC\_mul}.
+
+\fun{GEN}{FlxV_red}{GEN V, ulong p} reduces each components with \kbd{Flx\_red}.
+
+\subsec{\kbd{FlxT}} See \kbd{FpXT} operations.
+
+\fun{GEN}{FlxT_red}{GEN V, ulong p} reduces each leaf with \kbd{Flx\_red}.
+
+\subsec{\kbd{Flxq}} See \kbd{FpXQ} operations.
+
+\fun{GEN}{Flxq_add}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{Flxq_sub}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{Flxq_mul}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{Flxq_sqr}{GEN y, GEN T, ulong p}
+
+\fun{GEN}{Flxq_inv}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{Flxq_invsafe}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{Flxq_div}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{Flxq_pow}{GEN x, GEN n, GEN T, ulong p}
+
+\fun{GEN}{Flxq_powu}{GEN x, ulong n, GEN T, ulong p}
+
+\fun{GEN}{Flxq_powers}{GEN x, long n, GEN T, ulong p}
+
+\fun{GEN}{Flxq_matrix_pow}{GEN x, long m, long n, GEN T, ulong p},
+see \kbd{FpXQ\_matrix\_pow}.
+
+\fun{GEN}{Flxq_autpow}{GEN a, long n, GEN T, ulong p}
+see \kbd{FpXQ\_autpow}.
+
+\fun{GEN}{Flxq_autsum}{GEN a, long n, GEN T, GEN p}
+see \kbd{Flxq\_autsum}.
+
+\fun{GEN}{Flxq_ffisom_inv}{GEN S, GEN T, ulong p}, as \kbd{FpXQ\_ffisom\_inv}.
+
+\fun{GEN}{Flx_Flxq_eval}{GEN f, GEN x, GEN T, ulong p} returns
+$\kbd{f}(\kbd{x})$.
+
+\fun{GEN}{Flx_FlxqV_eval}{GEN f, GEN x, GEN T, ulong p},
+see \kbd{FpX\_FpXQV\_eval}.
+
+\fun{GEN}{FlxqV_roots_to_pol}{GEN V, GEN T, ulong p, long v} as
+\kbd{FqV\_roots\_to\_pol} returning an \kbd{FlxqX} in variable $v$.
+
+\fun{GEN}{Flxq_order}{GEN a, GEN ord, GEN T, ulong p}
+returns the order of the \typ{Flxq} \kbd{a}.
+If \kbd{o} is non-\kbd{NULL}, it is assumed that \kbd{o} is a multiple of the
+order of \kbd{a}, either as a \typ{INT} or a factorization matrix.
+
+\fun{int}{Flxq_issquare}{GEN x, GEN T, ulong p} returns $1$ if $x$ is a square
+and $0$ otherwise. Assume that \kbd{T} is irreducible mod \kbd{p}.
+
+\fun{int}{Flxq_is2npower}{GEN x, long n, GEN T, ulong p} returns $1$ if $x$ is
+a $2^n$-th power and $0$ otherwise. Assume that \kbd{T} is irreducible mod
+\kbd{p}.
+
+\fun{GEN}{Flxq_log}{GEN a, GEN g, GEN ord, GEN T, ulong p} Let $g$ of exact
+order \kbd{ord} in the field $F_p[X]/(T)$. Return $e$ such that $a^e=g$. If
+$e$ does not exists, the result is currently undefined. Assumes that \kbd{T}
+is irreducible mod \kbd{p}.
+
+\fun{GEN}{Flxq_sqrtn}{GEN x, GEN n, GEN T, ulong p, GEN *zn} returns an
+\kbd{n}-th root of $\kbd{x}$.  Return \kbd{NULL} if \kbd{x} is not an
+\kbd{n}-th power residue. Otherwise, if \kbd{zn} is non-\kbd{NULL} set it to
+a primitive \kbd{n}-th root of $1$. Assumes that \kbd{T} is irreducible mod
+\kbd{p}.
+
+\fun{GEN}{Flxq_sqrt}{GEN x, GEN T, ulong p} returns a square root of \kbd{x}.
+Return \kbd{NULL} if \kbd{x} is not a square.
+
+\fun{GEN}{Flxq_lroot}{GEN a, GEN T, ulong p} returns $x$ such that $x^p = a$.
+
+\fun{GEN}{Flxq_lroot_fast}{GEN a, GEN V, GEN T, ulong p} assuming that
+\kbd{V=Flxq\_powers(s,p-1,T,p)} where $s(x)^p \equiv x\pmod{T(x),p}$,
+returns $b$ such that $b^p=a$. Only useful if $p$ is less than the degree of
+$T$.
+
+\fun{GEN}{Flxq_charpoly}{GEN x, GEN T, ulong p} returns the characteristic
+polynomial of \kbd{x}
+
+\fun{GEN}{Flxq_minpoly}{GEN x, GEN T, ulong p} returns the minimal polynomial
+of \kbd{x}
+
+\fun{ulong}{Flxq_norm}{GEN x, GEN T, ulong p} returns the norm of \kbd{x}
+
+\fun{ulong}{Flxq_trace}{GEN x, GEN T, ulong p} returns the trace of \kbd{x}
+
+\fun{GEN}{Flxq_conjvec}{GEN x, GEN T, ulong p} returns the conjugates
+$[x,x^p,x^{p^2},\ldots,x^{p^{n-1}}]$ where $n$ is the degree of $T$.
+
+\fun{GEN}{gener_Flxq}{GEN T, ulong p, GEN *po} returns a primitive root modulo
+$(T,p)$. $T$ is an \kbd{Flx} assumed to be irreducible modulo the prime
+$p$. If \kbd{po} is not \kbd{NULL} it is set to $[o,\var{fa}]$, where $o$ is the
+order of the multiplicative group of the finite field, and \var{fa} is
+its factorization.
+
+\subsec{\kbd{FlxX}} See \kbd{FpXX} operations.
+
+\fun{GEN}{pol1_FlxX}{long vX, long sx} returns the unit \kbd{FlxX} as a
+\typ{POL} in variable \kbd{vX} which only coefficient is \kbd{pol1\_Flx(sx)}.
+
+\fun{GEN}{polx_FlxX}{long vX, long sx} returns the variable $X$ as a
+degree~1~\typ{POL} with \kbd{Flx} coefficients in the variable $x$.
+
+\fun{GEN}{FlxX_add}{GEN P, GEN Q, ulong p}
+
+\fun{GEN}{FlxX_sub}{GEN P, GEN Q, ulong p}
+
+\fun{GEN}{FlxX_Fl_mul}{GEN x, ulong y, ulong p}
+
+\fun{GEN}{FlxX_double}{GEN x, ulong p}
+
+\fun{GEN}{FlxX_triple}{GEN x, ulong p}
+
+\fun{GEN}{FlxX_neg}{GEN x, ulong p}
+
+\fun{GEN}{FlxX_Flx_add}{GEN y, GEN x, ulong p}
+
+\fun{GEN}{FlxX_Flx_mul}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{FlxY_Flx_div}{GEN x, GEN y, ulong p} divides the coefficients of $x$
+by $y$ using \kbd{Flx\_div}.
+
+\fun{GEN}{FlxY_evalx}{GEN x, ulong y, ulong p}
+
+\fun{GEN}{FlxY_Flxq_evalx}{GEN x, GEN y, ulong p}
+
+\fun{GEN}{FlxX_renormalize}{GEN x, long l}, as \kbd{normalizepol}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{GEN}{FlxX_resultant}{GEN u, GEN v, ulong p, long sv} Returns
+$\text{Res}_X(u, v)$, which is an \kbd{Flx}. The coefficients of \kbd{u}
+and \kbd{v} are assumed to be in the variable $v$.
+
+\fun{GEN}{Flx_FlxY_resultant}{GEN a, GEN b, ulong p}
+Returns $\text{Res}_x(a, b)$, which is an \kbd{Flx}
+in the main variable of \kbd{b}.
+
+\fun{GEN}{FlxX_shift}{GEN a, long n}
+
+\fun{GEN}{FlxX_swap}{GEN x, long n, long ws}, as \kbd{RgXY\_swap}.
+
+\fun{GEN}{FlxYqq_pow}{GEN x, GEN n, GEN S, GEN T, ulong p}, as
+\kbd{FpXYQQ\_pow}.
+
+\subsec{\kbd{FlxqX}} See \kbd{FpXQX} operations.
+
+\fun{GEN}{zxX_to_Kronecker}{GEN P, GEN Q} assuming $P(X,Y)$ is a polynomial
+of degree in $X$ strictly less than $n$, returns $P(X,X^{2*n-1})$, the
+Kronecker form of $P$.
+
+\fun{GEN}{Kronecker_to_FlxqX}{GEN z, GEN T, ulong p}. Let $n = \deg T$ and let
+$P(X,Y)\in \Z[X,Y]$ lift a polynomial in $K[Y]$, where $K := \F_p[X]/(T)$ and
+$\deg_X P < 2n-1$ --- such as would result from multiplying minimal degree
+lifts of two polynomials in $K[Y]$. Let $z = P(t,t^{2*n-1})$ be a Kronecker
+form of $P$, this function returns $Q\in \Z[X,t]$ such that $Q$ is congruent to
+$P(X,t)$ mod $(p, T(X))$, $\deg_X Q < n$, and all coefficients are in $[0,p[$.
+Not stack-clean. Note that $t$ need not be the same variable as $Y$!
+
+\fun{GEN}{FlxqX_red}{GEN z, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_normalize}{GEN z, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_mul}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_Flxq_mul}{GEN P, GEN U, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_Flxq_mul_to_monic}{GEN P, GEN U, GEN T, ulong p}
+returns $P*U$ assuming the result is monic of the same degree as $P$ (in
+particular $U\neq 0$).
+
+\fun{GEN}{FlxqX_sqr}{GEN x, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_pow}{GEN x, long n, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_divrem}{GEN x, GEN y, GEN T, ulong p, GEN *pr}
+
+\fun{GEN}{FlxqX_div}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_rem}{GEN x, GEN y, GEN T, ulong p}
+
+\fun{GEN}{FlxqX_invBarrett}{GEN T, GEN Q, ulong p}
+
+\fun{GEN}{FlxqX_rem_Barrett}{GEN x, GEN mg, GEN T, GEN Q, ulong p}
+
+\fun{GEN}{FlxqX_gcd}{GEN x, GEN y, ulong p} returns a (not necessarily monic)
+greatest common divisor of $x$  and $y$.
+
+\fun{GEN}{FlxqX_extgcd}{GEN x, GEN y, GEN T, ulong p, GEN *ptu, GEN *ptv}
+
+\fun{GEN}{FlxqXV_prod}{GEN V, GEN T, ulong p}
+
+
+\fun{GEN}{FlxqX_safegcd}{GEN P, GEN Q, GEN T, ulong p} Returns the \emph{monic}
+GCD of $P$ and $Q$ if Euclid's algorithm succeeds and \kbd{NULL} otherwise. In
+particular, if $p$ is not prime or $T$ is not irreducible over $\F_p[X]$, the
+routine may still be used (but will fail if non-invertible leading terms
+occur).
+
+\fun{GEN}{FlxqX_Frobenius}{GEN S, GEN T, GEN p}, as \kbd{FpXQX\_Frobenius}
+
+\fun{GEN}{FlxqXQ_halfFrobenius}{GEN A, GEN S, GEN T, GEN p}, as
+\kbd{FpXQXQ\_halfFrobenius}
+
+\fun{long}{FlxqX_nbroots}{GEN S, GEN T, GEN p}, as \kbd{FpX\_nbroots}.
+
+\fun{GEN}{FlxqX_FlxqXQ_eval}{GEN Q, GEN x, GEN S, GEN T, ulong p} as
+\kbd{FpX\_FpXQ\_eval}.
+
+\fun{GEN}{FlxqX_FlxqXQV_eval}{GEN P, GEN V, GEN S, GEN T, ulong p} as
+\kbd{FpX\_FpXQV\_eval}.
+
+\subsec{\kbd{FlxqXQ}} See \kbd{FpXQXQ} operations.
+
+\fun{GEN}{FlxqXQ_mul}{GEN x, GEN y, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_sqr}{GEN x, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_inv}{GEN x, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_invsafe}{GEN x, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_div}{GEN x, GEN y, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_pow}{GEN x, GEN n, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_powers}{GEN x, long n, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQ_matrix_pow}{GEN x, long n, long m, GEN S, GEN T, ulong p}
+
+\fun{GEN}{FlxqXQV_autpow}{GEN a, long n, GEN S, GEN T, ulong p}
+as \kbd{FpXQXQV\_autpow}
+
+\fun{GEN}{FlxqXQV_autsum}{GEN a, long n, GEN S, GEN T, ulong p}
+as \kbd{FpXQXQV\_autsum}
+
+\subsec{\kbd{F2x}} An \kbd{F2x}~\kbd{z} is a \typ{VECSMALL}
+representing a polynomial over $\F_2[X]$. Specifically
+\kbd{z[0]} is the usual codeword, \kbd{z[1] = evalvarn($v$)} for some
+variable $v$ and the coefficients are given by the bits of remaining
+words by increasing degree.
+
+\subsubsec{Basic operations}
+
+\fun{ulong}{F2x_coeff}{GEN x, long i} returns the coefficient $i\ge 0$ of $x$.
+
+\fun{void}{F2x_clear}{GEN x, long i} sets the coefficient $i\ge 0$ of $x$ to
+$0$.
+
+\fun{void}{F2x_flip}{GEN x, long i} adds $1$ to the coefficient $i\ge 0$ of $x$.
+
+\fun{void}{F2x_set}{GEN x, long i} sets the coefficient $i\ge 0$ of $x$ to $1$.
+
+\fun{GEN}{Flx_to_F2x}{GEN x}
+
+\fun{GEN}{Z_to_F2x}{GEN x, long v}
+
+\fun{GEN}{ZX_to_F2x}{GEN x}
+
+\fun{GEN}{F2v_to_F2x}{GEN x, long sv}
+
+\fun{GEN}{ZXX_to_F2xX}{GEN x, long v}
+
+\fun{GEN}{F2x_to_Flx}{GEN x}
+
+\fun{GEN}{F2x_to_ZX}{GEN x}
+
+\fun{GEN}{pol0_F2x}{long sv} returns a zero \kbd{F2x} in variable $v$.
+
+\fun{GEN}{zero_F2x}{long sv} alias for \kbd{pol0\_F2x}.
+
+\fun{GEN}{pol1_F2x}{long sv} returns the \kbd{F2x} in variable $v$ constant to
+$1$.
+
+\fun{GEN}{polx_F2x}{long sv} returns the variable $v$ as degree~1~\kbd{F2x}.
+
+\fun{GEN}{random_F2x}{long d, long sv} returns a random \kbd{F2x}
+in variable \kbd{v}, of degree less than~\kbd{d}.
+
+\fun{long}{F2x_degree}{GEN x} returns the degree of the \kbd{F2x x}. The
+degree of $0$ is defined as $-1$.
+
+\fun{int}{F2x_equal1}{GEN x}
+
+\fun{int}{F2x_equal}{GEN x, GEN y}
+
+\fun{GEN}{F2x_1_add}{GEN y} returns \kbd{y+1} where \kbd{y} is a \kbd{Flx}.
+
+\fun{GEN}{F2x_add}{GEN x, GEN y}
+
+\fun{GEN}{F2x_mul}{GEN x, GEN y}
+
+\fun{GEN}{F2x_sqr}{GEN x}
+
+\fun{GEN}{F2x_divrem}{GEN x, GEN y, GEN *pr}
+
+\fun{GEN}{F2x_rem}{GEN x, GEN y}
+
+\fun{GEN}{F2x_div}{GEN x, GEN y}
+
+\fun{GEN}{F2x_renormalize}{GEN x, long lx}
+
+\fun{GEN}{F2x_deriv}{GEN x}
+
+\fun{GEN}{F2x_deflate}{GEN x, long d}
+
+\fun{void}{F2x_shift}{GEN x, long d} as \tet{RgX_shift}
+
+\fun{void}{F2x_even_odd}{GEN p, GEN *pe, GEN *po} as \tet{RgX_even_odd}
+
+\fun{long}{F2x_valrem}{GEN x, GEN *Z}
+
+\fun{GEN}{F2x_extgcd}{GEN a, GEN b, GEN *ptu, GEN *ptv}
+
+\fun{GEN}{F2x_gcd}{GEN a, GEN b}
+
+\fun{GEN}{F2x_halfgcd}{GEN a, GEN b}
+
+\fun{int}{F2x_issquare}{GEN x} returns $1$ if $x$ is a square of a \kbd{F2x}
+and $0$ otherwise.
+
+\fun{int}{F2x_is_irred}{GEN f}, as \tet{FpX_is_irred}.
+
+\fun{GEN}{F2x_sqrt}{GEN x} returns the squareroot of $x$, assuming $x$ is a
+square of a \kbd{F2x}.
+
+\fun{GEN}{F2x_factor}{GEN f}
+
+\subsec{\kbd{F2xq}} See \kbd{FpXQ} operations.
+
+\fun{GEN}{F2xq_mul}{GEN x, GEN y, GEN pol}
+
+\fun{GEN}{F2xq_sqr}{GEN x,GEN pol}
+
+\fun{GEN}{F2xq_div}{GEN x,GEN y,GEN T}
+
+\fun{GEN}{F2xq_inv}{GEN x, GEN T}
+
+\fun{GEN}{F2xq_invsafe}{GEN x, GEN T}
+
+\fun{GEN}{F2xq_pow}{GEN x, GEN n, GEN pol}
+
+\fun{GEN}{F2xq_powu}{GEN x, ulong n, GEN pol}
+
+\fun{ulong}{F2xq_trace}{GEN x, GEN T}
+
+\fun{GEN}{F2xq_conjvec}{GEN x, GEN T} returns the vector of conjugates
+$[x,x^2,x^{2^2},\ldots,x^{2^{n-1}}]$ where $n$ is the degree of $T$.
+
+\fun{GEN}{F2xq_log}{GEN a, GEN g, GEN ord, GEN T}
+
+\fun{GEN}{F2xq_order}{GEN a, GEN ord, GEN T}
+
+\fun{GEN}{F2xq_Artin_Schreier}{GEN a, GEN T} returns a solution of $x^2+x=a$,
+assuming it exists.
+
+\fun{GEN}{F2xq_sqrt}{GEN a, GEN T}
+
+\fun{GEN}{F2xq_sqrt_fast}{GEN a, GEN s, GEN T} assuming that
+$s^2 \equiv x\pmod{T(x)}$, computes $b \equiv a(s)\pmod{T}$ so that $b^2=a$.
+
+\fun{GEN}{F2xq_sqrtn}{GEN a, GEN n, GEN T, GEN *zeta}
+
+\fun{GEN}{gener_F2xq}{GEN T, GEN *po}
+
+\fun{GEN}{F2xq_powers}{GEN x, long n, GEN T}
+
+\fun{GEN}{F2xq_matrix_pow}{GEN x, long m, long n, GEN T}
+
+\fun{GEN}{F2x_F2xq_eval}{GEN f, GEN x, GEN T}
+
+\fun{GEN}{F2x_F2xqV_eval}{GEN f, GEN x, GEN T}, see \kbd{FpX\_FpXQV\_eval}.
+
+\fun{GEN}{F2xq_autpow}{GEN a, long n, GEN T} computes $\sigma^n(X)$ assuming
+$a=\sigma(X)$ where $\sigma$ is an automorphism of the algebra $\F_2[X]/T(X)$.
+
+\subsec{\kbd{F2xqV}, \kbd{F2xqM}}. See \kbd{FqV}, \kbd{FqM} operations.
+
+\fun{GEN}{F2xqM_F2xqC_mul}{GEN a, GEN b, GEN T}
+
+\fun{GEN}{F2xqM_ker}{GEN x, GEN T}
+
+\fun{GEN}{F2xqM_det}{GEN a, GEN T}
+
+\fun{GEN}{F2xqM_image}{GEN x, GEN T}
+
+\fun{GEN}{F2xqM_inv}{GEN a, GEN T}
+
+\fun{GEN}{F2xqM_mul}{GEN a, GEN b, GEN T}
+
+\fun{long}{F2xqM_rank}{GEN x, GEN T}
+
+\fun{GEN}{matid_F2xqM}{long n, GEN T}
+
+\subsec{Functions returning objects with \typ{INTMOD} coefficients}
+
+Those functions are mostly needed for interface reasons: \typ{INTMOD}s should
+not be used in library mode since the modular kernel is more flexible and more
+efficient, but GP users do not have access to the modular kernel.
+We document them for completeness:
+
+\fun{GEN}{Fp_to_mod}{GEN z, GEN p}, \kbd{z} a \typ{INT}. Returns \kbd{z *
+Mod(1,p)}, normalized. Hence the returned value is a \typ{INTMOD}.
+
+\fun{GEN}{FpX_to_mod}{GEN z, GEN p}, \kbd{z} a \kbd{ZX}. Returns \kbd{z *
+Mod(1,p)}, normalized. Hence the returned value has \typ{INTMOD} coefficients.
+
+\fun{GEN}{FpC_to_mod}{GEN z, GEN p}, \kbd{z} a \kbd{ZC}. Returns \kbd{Col(z) *
+Mod(1,p)}, a \typ{COL} with \typ{INTMOD} coefficients.
+
+\fun{GEN}{FpV_to_mod}{GEN z, GEN p}, \kbd{z} a \kbd{ZV}. Returns \kbd{Vec(z) *
+Mod(1,p)}, a \typ{VEC} with \typ{INTMOD} coefficients.
+
+\fun{GEN}{FpVV_to_mod}{GEN z, GEN p}, \kbd{z} a \kbd{ZVV}. Returns \kbd{Vec(z) *
+Mod(1,p)}, a \typ{VEC} of \typ{VEC} with \typ{INTMOD} coefficients.
+
+\fun{GEN}{FpM_to_mod}{GEN z, GEN p}, \kbd{z} a \kbd{ZM}. Returns \kbd{z *
+Mod(1,p)}, with \typ{INTMOD} coefficients.
+
+\fun{GEN}{F2c_to_mod}{GEN x}
+
+\fun{GEN}{F2m_to_mod}{GEN x}
+
+\fun{GEN}{Flc_to_mod}{GEN z}
+
+\fun{GEN}{Flm_to_mod}{GEN z}
+
+\fun{GEN}{FpXQC_to_mod}{GEN V, GEN T, GEN p} $V$ being a vector of \kbd{FpXQ},
+converts each entry to a \typ{POLMOD} with \typ{INTMOD} coefficients, and return
+a \typ{COL}.
+
+\fun{GEN}{QXQV_to_mod}{GEN V, GEN T} $V$ a vector of \kbd{QXQ}, which
+are lifted representatives of elements of $\Q[X]/(T)$ (number field elements
+in most applications) and $T$ is in $\Z[X]$. Return a vector where all
+non-rational entries are converted to \typ{POLMOD} modulo $T$; no reduction
+mod $T$ is attempted: the representatives should be already reduced. Used to
+normalize the output of \kbd{nfroots}.
+
+\fun{GEN}{QXQXV_to_mod}{GEN V, GEN T} $V$ a vector of polynomials whose
+coefficients are \kbd{QXQ}. Analogous to \kbd{QXQV\_to\_mod}.
+Used to normalize the output of \kbd{nffactor}.
+
+\fun{GEN}{QXQX_to_mod_shallow}{GEN z, GEN T} $v$ a polynomial with \kbd{QXQ}
+coefficients; replace them by \kbd{mkpolmod(.,T)}. Shallow function.
+
+The following functions are obsolete and should not be used: they receive a
+polynomial with arbitrary coefficients, apply \kbd{RgX\_to\_FpX}, a function
+from the modular kernel, then \kbd{*\_to\_mod}:
+
+\fun{GEN}{rootmod}{GEN f, GEN p}, applies \kbd{FpX\_roots}.
+
+\fun{GEN}{rootmod2}{GEN f, GEN p}, applies \kbd{ZX\_to\_flx} then
+\kbd{Flx\_roots\_naive}.
+
+\fun{GEN}{factmod}{GEN f, GEN p} applies \kbd{FpX\_factor}.
+
+\fun{GEN}{simplefactmod}{GEN f, GEN p} applies \kbd{FpX\_degfact}.
+
+\subsec{Chinese remainder theorem over $\Z$}
+
+\fun{GEN}{Z_chinese}{GEN a, GEN b, GEN A, GEN B} returns the integer
+in $[0, \lcm(A,B)[$ congruent to $a$ mod $A$ and $b$ mod $B$, assuming it
+exists; in other words, that $a$ and $b$ are congruent mod $\gcd(A,B)$.
+
+\fun{GEN}{Z_chinese_all}{GEN a, GEN b, GEN A, GEN B, GEN *pC} as
+\kbd{Z\_chinese}, setting \kbd{*pC} to the lcm of $A$ and $B$.
+
+\fun{GEN}{Z_chinese_coprime}{GEN a, GEN b, GEN A, GEN B, GEN C}, as
+\kbd{Z\_chinese}, assuming that $\gcd(A,B) = 1$ and that $C = \lcm(A,B) = AB$.
+
+\fun{void}{Z_chinese_pre}{GEN A, GEN B, GEN *pC, GEN *pU, GEN *pd}
+initializes chinese remainder computations modulo $A$ and $B$. Sets
+\kbd{*pC} to $\lcm(A,B)$, \kbd{*pd} to $\gcd(A,B)$,
+\kbd{*pU} to an integer congruent to $0$ mod $(A/d)$ and $1$ mod $(B/d)$.
+It is allowed to set \kbd{pd = NULL}, in which case, $d$ is still
+computed, but not saved.
+
+\fun{GEN}{Z_chinese_post}{GEN a, GEN b, GEN C, GEN U, GEN d} returns
+the solution to the chinese remainder problem $x$ congruent
+to $a$ mod $A$ and $b$ mod $B$, where $C, U, d$ were set in
+\kbd{Z\_chinese\_pre}. If $d$ is \kbd{NULL}, assume the problem has a
+solution. Otherwise, return \kbd{NULL} if it has no solution.
+
+\medskip
+
+The following pair of functions is used in homomorphic imaging schemes,
+when reconstructing an integer from its images modulo pairwise coprime
+integers. The idea is as follows: we want to discover an integer $H$ which
+satisfies $|H| < B$ for some known bound $B$; we are given pairs $(H_p, p)$
+with $H$ congruent to $H_p$ mod $p$ and all $p$ pairwise coprime.
+
+Given \kbd{H} congruent to $H_p$ modulo a number of $p$, whose product is
+$q$, and a new pair $(\kbd{Hp}, \kbd{p})$, \kbd{p} coprime to $q$, the
+following incremental functions use the chinese remainder theorem (CRT) to
+find a new \kbd{H}, congruent to the preceding one modulo $q$, but also to
+\kbd{Hp} modulo \kbd{p}. It is defined uniquely modulo $qp$, and we choose
+the centered representative. When $P$ is larger than $2B$, we have $\kbd{H} =
+H$, but of course, the value of \kbd{H} may stabilize sooner. In many
+applications it is possible to directly check that such a partial result is
+correct.
+
+\fun{GEN}{Z_init_CRT}{ulong Hp, ulong p} given a \kbd{Fl} \kbd{Hp} in
+$[0, p-1]$, returns the centered representative \kbd{H} congruent to \kbd{Hp}
+modulo \kbd{p}.
+
+\fun{int}{Z_incremental_CRT}{GEN *H, ulong Hp, GEN *q, ulong p}
+given a \typ{INT} \kbd{*H}, centered modulo \kbd{*q}, a new pair $(\kbd{Hp},
+\kbd{p})$ with \kbd{p} coprime to \kbd{q}, this function updates \kbd{*H} so
+that it also becomes congruent to $(\kbd{Hp}, \kbd{p})$, and \kbd{*q} to the
+product$\kbd{qp} = \kbd{p} \cdot \kbd{*q}$. It returns $1$ if the new value
+is equal to the old one, and $0$ otherwise.
+
+\fun{GEN}{chinese1_coprime_Z}{GEN v} an alternative divide-and-conquer
+implementation: $v$ is a vector of \typ{INTMOD} with pairwise coprime moduli.
+Return the \typ{INTMOD} solving the corresponding chinese remainder problem.
+This is a streamlined version of
+
+\fun{GEN}{chinese1}{GEN v}, which solves a general chinese remainder problem
+(not necessarily over $\Z$, moduli not assumed coprime).
+
+
+As above, for $H$ a \kbd{ZM}: we assume that $H$ and all \kbd{Hp} have
+dimension $> 0$. The original \kbd{*H} is destroyed.
+
+\fun{GEN}{ZM_init_CRT}{GEN Hp, ulong p}
+
+\fun{int}{ZM_incremental_CRT}{GEN *H, GEN Hp, GEN *q, ulong p}
+
+As above for $H$ a \kbd{ZX}: note that the degree may increase or decrease.
+The original \kbd{*H} is destroyed.
+
+\fun{GEN}{ZX_init_CRT}{GEN Hp, ulong p, long v}
+
+\fun{int}{ZX_incremental_CRT}{GEN *H, GEN Hp, GEN *q, ulong p}
+
+\subsec{Rational reconstruction}
+
+\fun{int}{Fp_ratlift}{GEN x, GEN m, GEN amax, GEN bmax, GEN *a, GEN *b}.
+Assuming that $0 \leq x < m$, $\kbd{amax} \geq 0$, and
+$\kbd{bmax} > 0$ are \typ{INT}s, and that $2 \kbd{amax} \kbd{bmax} < m$,
+attempts to recognize $x$ as a rational $a/b$, i.e. to find \typ{INT}s $a$
+and $b$ such that
+
+\item $a \equiv b x$ modulo $m$,
+
+\item $|a| \leq \kbd{amax}$, $0 < b \leq \kbd{bmax}$,
+
+\item $\gcd(m,b) = \gcd(a,b)$.
+
+\noindent If unsuccessful, the routine returns $0$ and leaves $a$, $b$
+unchanged; otherwise it returns $1$ and sets $a$ and $b$.
+
+In almost all applications, we actually know that a solution exists, as well
+as a non-zero multiple $B$ of $b$, and $m = p^\ell$ is a prime power, for a
+prime $p$ chosen coprime to $B$ hence to $b$. Under the single assumption
+$\gcd(m,b) = 1$, if a solution $a,b$ exists satisfying the three conditions
+above, then it is unique.
+
+\fun{GEN}{FpM_ratlift}{GEN M, GEN m, GEN amax, GEN bmax, GEN denom}
+given an \kbd{FpM} modulo $m$ with reduced or \kbd{Fp\_center}-ed entries,
+reconstructs a matrix with rational coefficients by applying \kbd{Fp\_ratlift}
+to all entries. Assume that all preconditions for \kbd{Fp\_ratlift} are
+satisfied, as well $\gcd(m,b) = 1$ (so that the solution is unique if it
+exists). Return \kbd{NULL} if the reconstruction fails, and the rational
+matrix otherwise. If \kbd{denom} is not \kbd{NULL} check further that all
+denominators divide \kbd{denom}.
+
+The functions is not stack clean if one coefficients of $M$ is negative
+(centered residues), but still suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{FpX_ratlift}{GEN P, GEN m, GEN amax, GEN bmax, GEN denom} as
+\kbd{FpM\_ratlift}, where $P$ is an \kbd{FpX}.
+
+\fun{GEN}{FpC_ratlift}{GEN P, GEN m, GEN amax, GEN bmax, GEN denom} as
+\kbd{FpM\_ratlift}, where $P$ is an \kbd{FpC}.
+
+\subsec{Hensel lifts}
+
+\fun{GEN}{Zp_sqrtlift}{GEN b, GEN a, GEN p, long e} let
+$a,b,p$ be \typ{INT}s, with $p > 1$ odd, such that $a^2\equiv b\mod p$.
+Returns a \typ{INT} $A$ such that $A^2 \equiv b \mod p^e$. Special case
+of \tet{Zp_sqrtnlift}.
+
+\fun{GEN}{Zp_sqrtnlift}{GEN b, GEN n, GEN a, GEN p, long e} let
+$a,b,n,p$ be \typ{INT}s, with $n,p > 1$, and $p$ coprime to $n$,
+such that $a^n \equiv b \mod p$. Returns a \typ{INT} $A$ such that
+$A^n \equiv b \mod p^e$. Special case of \tet{ZpX_liftroot}.
+
+\fun{GEN}{Zp_teichmuller}{GEN x, GEN p, long e, GEN pe} for $p$ an odd prime,
+$x$ a \typ{INT} coprime to $p$, and $pe = p^e$, returns the $(p-1)$-th root of
+$1$ congruent to $x$ modulo $p$, modulo $p^e$. For convenience, $p = 2$ is
+also allowed and we return $1$ ($x$ is $1$ mod $4$) or $2^e - 1$ ($x$ is $3$
+mod $4$).
+
+\fun{GEN}{ZpXQ_invlift}{GEN b, GEN a, GEN T, GEN p, long e} let
+$p$ be a prime \typ{INT} and $a,b$ be \kbd{FpXQ}s (modulo $T$) such that $a\*b
+\equiv 1 \mod (p,T)$.  Returns an \kbd{FpXQ} $A$ such that
+$A\*b \equiv 1 \mod (p^e, T)$.  Special case of \tet{ZpXQ_liftroot}.
+
+\fun{GEN}{ZpXQ_inv}{GEN b, GEN T, GEN p, long e} let
+$p$ be a prime \typ{INT} and $b$ be a \kbd{FpXQ} (modulo $T, p^e$).
+Returns an \kbd{FpXQ} $A$ such that $A\*b \equiv 1 \mod (p^e, T)$.
+
+\fun{GEN}{ZpXQ_sqrtnlift}{GEN b, GEN n, GEN a, GEN T, GEN p, long e} let
+$n,p$ be \typ{INT}s, with $n,p > 1$ and $p$ coprime to $n$, and $a,b$
+be \kbd{FpXQ}s (modulo $T$) such that $a^n \equiv b \mod (p,T)$.
+Returns an \kbd{Fq} $A$ such that $A^n \equiv b \mod (p^e, T)$.
+Special case of \tet{ZpXQ_liftroot}.
+
+\fun{GEN}{rootpadicfast}{GEN f, GEN p, long e} $f$ a \kbd{ZX} with leading
+term prime to $p$, and without multiple roots mod $p$. Return a vector
+of \typ{INT}s which are the roots of $f$ mod $p^e$. This is a very important
+special case of \kbd{rootpadic}.
+
+\fun{GEN}{ZpX_liftroot}{GEN f, GEN a, GEN p, long e} $f$ a \kbd{ZX} with
+leading term prime to $p$, and $a$ a root mod $p$ such that
+$v_p(f'(a))=0$.  Return a \typ{INT} which is the root of $f$ mod $p^e$
+congruent to $a$ mod $p$.
+
+\fun{GEN}{ZX_Zp_root}{GEN f, GEN a, GEN p, long e} same as \tet{ZpX_liftroot}
+without the assumption $v_p(f'(a)) = 0$. Return a \typ{VEC} of \typ{INT}s,
+which are the $p$-adic roots of $f$ congruent to $a$ mod $p$ (given modulo
+$p^e$).
+
+\fun{GEN}{ZpX_liftroots}{GEN f, GEN S, GEN p, long e} $f$ a \kbd{ZX} with
+leading term prime to $p$, and $S$ a vector of simple roots mod $p$. Return a
+vector of \typ{INT}s which are the root of $f$ mod $p^e$ congruent to the
+$S[i]$ mod $p$.
+
+\fun{GEN}{ZpXQX_liftroot}{GEN f, GEN a, GEN T, GEN p, long e} as
+\tet{ZpX_liftroot}, but $f$ is now a polynomial in $\Z[X,Y]$ and we find
+a root in the unramified extension of $\Q_p$ with residue field $\F_p[Y]/(T)$,
+assuming $v_p(f(a))>0$ and $v_p(f'(a))=0$.
+
+\fun{GEN}{ZpXQX_liftroot_vald}{GEN f, GEN a, long v, GEN T, GEN p, long e}
+returns the foots of $f$ as \tet{ZpXQX_liftroot}, where $v$ is the valuation
+of the content of $f'$ and it is required that $v_p(f(a))>v$ and
+$v_p(f'(a))=v$.
+
+\fun{GEN}{ZpX_liftfact}{GEN A, GEN B, GEN T, GEN p, long e, GEN pe} is
+the routine underlying \tet{polhensellift}. Here, $p$ is prime, $T(Y)$
+defines a finite field $\F_q$, either $\F_q = \F_p$ ($T$ is \kbd{NULL})
+or a non-prime finite field ($T$ an \kbd{FpX}). $A$ is a polynomial in
+$\Z[X]$ ($T$ \kbd{NULL}) or $\Z[X,Y]$, whose leading coefficient
+is non-zero in $\F_q$. $B$ is a vector of monic \kbd{FpX} ($T$ \kbd{NULL}) or
+\kbd{FqX}, pairwise coprime in $\F_q[X]$, whose product is congruent to
+$A/\text{lc}(A)$ in $\F_q[X]$. Lifts the elements of $B$ mod $\kbd{pe} =
+p^e$, such that the congruence now holds mod $(T,p^e)$.
+
+The following technical function returns an optimal sequence of $p$-adic
+accuracies, for a given target accuracy:
+
+\fun{ulong}{quadratic_prec_mask}{long n} we want to reach accuracy
+$n\geq 1$, starting from accuracy 1, using a quadratically convergent,
+self-correcting, algorithm; in other words, from inputs correct to accuracy
+$l$ one iteration outputs a result correct to accuracy $2l$.
+For instance, to reach $n = 9$, we want to use accuracies
+$[1,2,3,5,9]$ instead of $[1,2,4,8,9]$. The idea is to essentially double
+the accuracy at each step, and not overshoot in the end.
+
+Let $a_0$ = 1, $a_1 = 2, \ldots, a_k = n$, be the desired sequence of
+accuracies. To obtain it, we work backwards and set
+$$ a_k = n,\quad a_{i-1} = (a_i + 1)\,\bs\, 2.$$
+This is in essence what the function returns.
+But we do not want to store the $a_i$ explicitly, even as a \typ{VECSMALL},
+since this would leave an object on the stack. Instead, we store $a_i$
+implicitly in a bitmask \kbd{MASK}: let $a_0 = 1$, if the $i$-th bit of the
+mask is set, set $a_{i+1} = 2a_i - 1$, and $2a_i$ otherwise; in short the
+bits indicate the places where we do something special and do not quite
+double the accuracy (which would be the straightforward thing to do).
+
+In fact, to avoid returning separately the mask and the sequence length
+$k+1$, the function returns $\kbd{MASK} + 2^{k+1}$, so the highest bit of
+the mask indicates the length of the sequence, and the following ones give
+an algorithm to obtain the accuracies. This is much simpler than it sounds,
+here is what it looks like in practice:
+\bprog
+  ulong mask = quadratic_prec_mask(n);
+  long l = 1;
+  while (mask > 1) {            /* here, the result is known to accuracy l */
+    l = 2*l; if (mask & 1) l--; /* new accuracy l for the iteration */
+    mask >>= 1;                 /* pop low order bit */
+    /* ... lift to the new accuracy ... */
+  }
+  /* we are done. At this point l = n */
+ at eprog\noindent We just pop the bits in \kbd{mask} starting from the low
+order bits, stop when \kbd{mask} is $1$ (that last bit corresponds to the
+$2^{k+1}$ that we added to the mask proper). Note that there is nothing
+specific to Hensel lifts in that function: it would work equally well for
+an Archimedean Newton iteration.
+
+Note that in practice, we rather use an infinite loop, and insert an
+\bprog
+  if (mask == 1) break;
+ at eprog\noindent in the middle of the loop: the loop body usually includes
+preparations for the next iterations (e.g. lifting Bezout coefficients
+in a quadratic Hensel lift), which are costly and useless in the \emph{last}
+iteration.
+
+\subsec{Other $p$-adic functions}
+
+\fun{long}{ZpX_disc_val}{GEN f, GEN p} returns the valuation at $p$ of the
+discriminant of $f$. Assume that $f$ is a monic \emph{separable} \kbd{ZX}
+and that $p$ is a prime number. Proceeds by dynamically increasing the
+$p$-adic accuracy; infinite loop if the discriminant of $f$ is
+$0$.
+
+\fun{long}{ZpX_resultant_val}{GEN f, GEN g, GEN p, long M} returns the
+valuation at $p$ of $\text{Res}(f,g)$. Assume $f,g$ are both \kbd{ZX},
+and that $p$ is a prime number coprime to the leading coefficient of $f$.
+Proceeds by dynamically increasing the $p$-adic accuracy.
+To avoid an infinite loop when the resultant is $0$, we return $M$ if
+the Sylvester matrix mod $p^M$ still does not have maximal rank.
+
+\fun{GEN}{ZpX_gcd}{GEN f,GEN g, GEN p, GEN pm} $f$ a monic \kbd{ZX},
+$g$ a \kbd{ZX}, $\kbd{pm} = p^m$ a prime power. There is a unique integer
+$r\geq 0$ and a monic $h\in \Q_p[X]$ such that
+$$p^rh\Z_p[X] + p^m\Z_p[X] = f\Z_p[X] + g\Z_p[X] + p^m\Z_p[X].$$
+Return the $0$ polynomial if $r\geq m$ and a monic $h\in\Z[1/p][X]$ otherwise
+(whose valuation at $p$ is $> -m$).
+
+\fun{GEN}{ZpX_reduced_resultant}{GEN f, GEN g, GEN p, GEN pm} $f$ a monic
+\kbd{ZX}, $g$ a \kbd{ZX}, $\kbd{pm} = p^m$ a prime power. The $p$-adic
+\emph{reduced resultant}\varsidx{resultant (reduced)} of $f$ and $g$ is
+$0$ if $f$, $g$ not coprime in $\Z_p[X]$, and otherwise the generator of the
+form $p^d$ of
+$$ (f\Z_p[X] + g\Z_p[X])\cap \Z_p. $$
+Return the reduced resultant modulo $p^m$.
+
+\fun{GEN}{ZpX_reduced_resultant_fast}{GEN f, GEN g, GEN p, long M} $f$
+a monic \kbd{ZX}, $g$ a \kbd{ZX}, $p$ a prime. Returns the
+the $p$-adic reduced resultant of $f$ and $g$ modulo $p^M$. This function
+computes resultants for a sequence of increasing $p$-adic accuracies
+(up to $M$ $p$-adic digits), returning as soon as it obtains a non-zero
+result. It is very inefficient when the resultant is $0$, but otherwise
+usually more efficient than computations using a priori bounds.
+
+\fun{GEN}{ZpM_echelon}{GEN M, long early_abort, GEN p, GEN pm} given a
+\kbd{ZM} $M$, a prime $p$ and $\kbd{pm} = p^m$, returns an echelon form
+$E$ for $M$ mod $p^m$. I.e. there exist a square integral matrix $U$ with
+$\det U$ coprime to $p$ such that $E = MU$ modulo $p^m$. I
+\kbd{early\_abort} is non-zero, return NULL as soon as one pivot in
+the echelon form is divisible by $p^m$. The echelon form is an upper
+triangular HNF, we do not waste time to reduce it to Gauss-Jordan form.
+
+\fun{GEN}{zlm_echelon}{GEN M, long early_abort, ulong p, ulong pm}
+variant of \kbd{ZpM\_echelon}, for a \kbd{Zlm} $M$.
+
+\fun{GEN}{ZpXQ_log}{GEN a, GEN T, GEN p, long e} $T$ being a \kbd{ZpX}
+irreducible modulo $p$, return the logarithm of $a$ in $\Z_p[X]/(T)$ to
+precision $e$, assuming that $a\equiv 1 \pmod{p\Z_p[X]}$ if $p$ odd or
+$a\equiv 1 \pmod{4\Z_2[X]}$ if $p=2$.
+
+\fun{GEN}{padic_to_Q}{GEN x} truncate the \typ{PADIC} to a \typ{INT} or
+\typ{FRAC}.
+
+\fun{GEN}{padic_to_Q_shallow}{GEN x} shallow version of \tet{padic_to_Q}
+
+\fun{GEN}{QpV_to_QV}{GEN v} apply \tet{padic_to_Q_shallow}
+
+\fun{long}{padicprec}{GEN x, GEN p} returns the absolute $p$-adic precision of
+the object $x$, by definition the minimum precision of the components of $x$.
+For a non-zero \typ{PADIC}, this returns \kbd{valp(x) + precp(x)}.
+
+\fun{long}{padicprec_relative}{GEN x} returns the relative $p$-adic
+precision of the \typ{INT}, \typ{FRAC}, or \typ{PADIC} $x$ (minimum precision
+of the components of $x$ for \typ{POL} or vector/matrices).
+For a \typ{PADIC}, this returns \kbd{precp(x)} if $x\neq0$, and $0$ for $x=0$.
+
+\subsec{Conversions involving single precision objects}
+
+\subsubsec{To single precision}
+
+\fun{ulong}{Rg_to_Fl}{GEN z, ulong p}, \kbd{z} which can be mapped to
+$\Z/p\Z$: a \typ{INT}, a \typ{INTMOD} whose modulus is divisible by $p$,
+a \typ{FRAC} whose denominator is coprime to $p$, or a \typ{PADIC} with
+underlying prime $\ell$ satisfying $p = \ell^n$ for some $n$ (less than the
+accuracy of the input). Returns \kbd{lift(z * Mod(1,p))}, normalized, as an
+\kbd{Fl}.
+
+\fun{ulong}{Rg_to_F2}{GEN z}, as \tet{Rg_to_Fl} for $p = 2$.
+
+\fun{ulong}{padic_to_Fl}{GEN x, ulong p} special case of \tet{Rg_to_Fl},
+for a $x$ a \typ{PADIC}.
+
+\fun{GEN}{RgX_to_F2x}{GEN x}, \kbd{x} a \typ{POL}, returns the
+\kbd{F2x} obtained by applying \kbd{Rg\_to\_Fl} coefficientwise.
+
+\fun{GEN}{RgX_to_Flx}{GEN x, ulong p}, \kbd{x} a \typ{POL}, returns the
+\kbd{Flx} obtained by applying \kbd{Rg\_to\_Fl} coefficientwise.
+
+\fun{GEN}{Rg_to_F2xq}{GEN z, GEN T}, \kbd{z} a \kbd{GEN} which can be
+mapped to $\F_2[X]/(T)$: anything \kbd{Rg\_to\_Fl} can be applied to,
+a \typ{POL} to which \kbd{RgX\_to\_F2x} can be applied to, a \typ{POLMOD}
+whose modulus is divisible by $T$ (once mapped to a \kbd{F2x}), a suitable
+\typ{RFRAC}. Returns \kbd{z} as an \kbd{F2xq}, normalized.
+
+\fun{GEN}{Rg_to_Flxq}{GEN z, GEN T, ulong p}, \kbd{z} a \kbd{GEN} which can be
+mapped to $\F_p[X]/(T)$: anything \kbd{Rg\_to\_Fl} can be applied to,
+a \typ{POL} to which \kbd{RgX\_to\_Flx} can be applied to, a \typ{POLMOD}
+whose modulus is divisible by $T$ (once mapped to a \kbd{Flx}), a suitable
+\typ{RFRAC}. Returns \kbd{z} as an \kbd{Flxq}, normalized.
+
+\fun{GEN}{ZX_to_Flx}{GEN x, ulong p} reduce \kbd{ZX}~\kbd{x} modulo \kbd{p}
+(yielding an \kbd{Flx}). Faster than \kbd{RgX\_to\_Flx}.
+
+\fun{GEN}{ZV_to_Flv}{GEN x, ulong p} reduce \kbd{ZV}~\kbd{x} modulo \kbd{p}
+(yielding an \kbd{Flv}).
+
+\fun{GEN}{ZXV_to_FlxV}{GEN v, ulong p}, as \kbd{ZX\_to\_Flx}, repeatedly
+called on the vector's coefficients.
+
+\fun{GEN}{ZXT_to_FlxT}{GEN v, ulong p}, as \kbd{ZX\_to\_Flx}, repeatedly
+called on the tree leaves.
+
+\fun{GEN}{ZXX_to_FlxX}{GEN B, ulong p, long v}, as \kbd{ZX\_to\_Flx},
+repeatedly called on the polynomial's coefficients.
+
+\fun{GEN}{ZXXV_to_FlxXV}{GEN V, ulong p, long v}, as \kbd{ZXX\_to\_FlxX},
+repeatedly called on the vector's coefficients.
+
+\fun{GEN}{RgC_to_Flc}{GEN x, ulong p} reduce the \typ{VEC}/\typ{COL}
+$x$ modulo $p$, yielding a \typ{VECSMALL}.
+
+\fun{GEN}{RgM_to_Flm}{GEN x, ulong p} reduce the \typ{MAT} $x$ modulo $p$.
+
+\fun{GEN}{ZM_to_Flm}{GEN x, ulong p} reduce \kbd{ZM}~$x$ modulo $p$
+(yielding an \kbd{Flm}).
+
+\fun{GEN}{ZV_to_zv}{GEN z}, converts coefficients using \kbd{itos}
+
+\fun{GEN}{ZV_to_nv}{GEN z}, converts coefficients using \kbd{itou}
+
+\fun{GEN}{ZM_to_zm}{GEN z}, converts coefficients using \kbd{itos}
+
+\fun{GEN}{FqC_to_FlxC}{GEN x, GEN T, GEN p}, converts coefficients in \kbd{Fq}
+to coefficient in Flx, result being a column vector.
+
+\fun{GEN}{FqV_to_FlxV}{GEN x, GEN T, GEN p}, converts coefficients in \kbd{Fq}
+to coefficient in Flx, result being a line vector.
+
+
+\fun{GEN}{FqM_to_FlxM}{GEN x, GEN T, GEN p}, converts coefficients in \kbd{Fq}
+to coefficient in Flx.
+
+\subsubsec{From single precision}
+
+\fun{GEN}{Flx_to_ZX}{GEN z}, converts to \kbd{ZX} (\typ{POL} of non-negative
+\typ{INT}s in this case)
+
+\fun{GEN}{Flx_to_FlxX}{GEN z}, converts to \kbd{FlxX} (\typ{POL} of constant
+\kbd{Flx} in this case).
+
+\fun{GEN}{Flx_to_ZX_inplace}{GEN z}, same as \kbd{Flx\_to\_ZX}, in place
+(\kbd{z} is destroyed).
+
+\fun{GEN}{FlxX_to_ZXX}{GEN B}, converts an \kbd{FlxX} to a polynomial with
+\kbd{ZX} or \typ{INT} coefficients (repeated calls to \kbd{Flx\_to\_ZX}).
+
+\fun{GEN}{FlxC_to_ZXC}{GEN x}, converts a vector of \kbd{Flx} to a column
+vector of polynomials with \typ{INT} coefficients (repeated calls to
+\kbd{Flx\_to\_ZX}).
+
+\fun{GEN}{FlxV_to_ZXV}{GEN x}, as above but return a \typ{VEC}.
+
+\fun{void}{F2xV_to_FlxV_inplace}{GEN v} v is destroyed.
+
+\fun{void}{F2xV_to_ZXV_inplace}{GEN v} v is destroyed.
+
+\fun{void}{FlxV_to_ZXV_inplace}{GEN v} v is destroyed.
+
+\fun{GEN}{FlxM_to_ZXM}{GEN z}, converts a matrix of \kbd{Flx} to a matrix of
+polynomials with \typ{INT} coefficients (repeated calls to \kbd{Flx\_to\_ZX}).
+
+\fun{GEN}{zx_to_ZX}{GEN z}, as \kbd{Flx\_to\_ZX}, without assuming
+coefficients are non-negative.
+
+\fun{GEN}{Flc_to_ZC}{GEN z}, converts to \kbd{ZC} (\typ{COL} of non-negative
+\typ{INT}s in this case)
+
+\fun{GEN}{Flv_to_ZV}{GEN z}, converts to \kbd{ZV} (\typ{VEC} of non-negative
+\typ{INT}s in this case)
+
+\fun{GEN}{Flm_to_ZM}{GEN z}, converts to \kbd{ZM} (\typ{MAT} with
+non-negative \typ{INT}s coefficients in this case)
+
+\fun{GEN}{zc_to_ZC}{GEN z} as \kbd{Flc\_to\_ZC}, without assuming
+coefficients are non-negative.
+
+\fun{GEN}{zv_to_ZV}{GEN z} as \kbd{Flv\_to\_ZV}, without assuming
+coefficients are non-negative.
+
+\fun{GEN}{zm_to_ZM}{GEN z} as \kbd{Flm\_to\_ZM}, without assuming
+coefficients are non-negative.
+
+\fun{GEN}{zv_to_Flv}{GEN z, ulong p}
+
+\fun{GEN}{zm_to_Flm}{GEN z, ulong p}
+
+\subsubsec{Mixed precision linear algebra} Assumes dimensions are compatible.
+Multiply a multiprecision object by a single-precision one.
+
+\fun{GEN}{RgM_zc_mul}{GEN x, GEN y}
+
+\fun{GEN}{RgM_zm_mul}{GEN x, GEN y}
+
+\fun{GEN}{RgV_zc_mul}{GEN x, GEN y}
+
+\fun{GEN}{RgV_zm_mul}{GEN x, GEN y}
+
+\fun{GEN}{ZM_zc_mul}{GEN x, GEN y}
+
+\fun{GEN}{ZM_zm_mul}{GEN x, GEN y}
+
+\fun{GEN}{ZC_z_mul}{GEN x, long y}
+
+\fun{GEN}{ZM_nm_mul}{GEN x, GEN y} the entries of $y$ are \kbd{ulong}s.
+
+\fun{GEN}{nm_Z_mul}{GEN y, GEN c} the entries of $y$ are \kbd{ulong}s.
+
+\subsubsec{Miscellaneous involving Fl}
+
+\fun{GEN}{Fl_to_Flx}{ulong x, long evx} converts a \kbd{unsigned long} to a
+scalar \kbd{Flx}. Assume that \kbd{evx = evalvarn(vx)} for some variable
+number \kbd{vx}.
+
+\fun{GEN}{Z_to_Flx}{GEN x, ulong p, long v} converts a \typ{INT} to a scalar
+polynomial in variable $v$.
+
+\fun{GEN}{Flx_to_Flv}{GEN x, long n} converts from \kbd{Flx} to \kbd{Flv}
+with \kbd{n} components (assumed larger than the number of coefficients of
+\kbd{x}).
+
+\fun{GEN}{zx_to_zv}{GEN x, long n} as \kbd{Flx\_to\_Flv}.
+
+\fun{GEN}{Flv_to_Flx}{GEN x, long sv} converts from vector (coefficient
+array) to (normalized) polynomial in variable $v$.
+
+\fun{GEN}{zv_to_zx}{GEN x, long n} as \kbd{Flv\_to\_Flx}.
+
+\fun{GEN}{Flm_to_FlxV}{GEN x, long sv} converts the columns of
+\kbd{Flm}~\kbd{x} to an array of \kbd{Flx} in the variable $v$
+(repeated calls to \kbd{Flv\_to\_Flx}).
+
+\fun{GEN}{zm_to_zxV}{GEN x, long n} as \kbd{Flm\_to\_FlxV}.
+
+\fun{GEN}{Flm_to_FlxX}{GEN x, long sw, long sv} same as
+\kbd{Flm\_to\_FlxV(x,sv)} but returns the result as a (normalized) polynomial
+in variable $w$.
+
+\fun{GEN}{FlxV_to_Flm}{GEN v, long n} reverse \kbd{Flm\_to\_FlxV}, to obtain
+an \kbd{Flm} with \kbd{n} rows (repeated calls to \kbd{Flx\_to\_Flv}).
+
+\fun{GEN}{FlxX_to_Flm}{GEN v, long n} reverse \kbd{Flm\_to\_FlxX}, to obtain
+an \kbd{Flm} with \kbd{n} rows (repeated calls to \kbd{Flx\_to\_Flv}).
+
+\fun{GEN}{FlxX_to_FlxC}{GEN B, long n, long sv} see \kbd{RgX\_to\_RgV}.
+The coefficients of \kbd{B} are assumed to be in the variable $v$.
+
+\fun{GEN}{FlxXV_to_FlxM}{GEN V, long n, long sv} see \kbd{RgXV\_to\_RgM}.
+The coefficients of \kbd{V[i]} are assumed to be in the variable $v$.
+
+\fun{GEN}{Fly_to_FlxY}{GEN a, long sv} convert coefficients of \kbd{a} to
+constant \kbd{Flx} in variable $v$.
+
+\subsubsec{Miscellaneous involving \kbd{F2x}}
+
+\fun{GEN}{F2x_to_F2v}{GEN x, long n} converts from \kbd{F2x} to \kbd{F2v}
+with \kbd{n} components (assumed larger than the number of coefficients of
+\kbd{x}).
+
+\fun{GEN}{F2xC_to_ZXC}{GEN x}, converts a vector of \kbd{F2x} to a column
+vector of polynomials with \typ{INT} coefficients (repeated calls to
+\kbd{F2x\_to\_ZX}).
+
+\fun{GEN}{F2xV_to_F2m}{GEN v, long n} \kbd{F2x\_to\_F2v} to each polynomial
+to get an \kbd{F2m} with \kbd{n} rows.
+
+\section{Higher arithmetic over $\Z$: primes, factorization}
+
+\subsec{Pure powers}
+
+\fun{long}{Z_issquare}{GEN n} returns $1$ if the \typ{INT} $n$ is
+a square, and $0$ otherwise. This is tested first modulo small prime
+powers, then \kbd{sqrtremi} is called.
+
+\fun{long}{Z_issquareall}{GEN n, GEN *sqrtn} as \kbd{Z\_issquare}. If
+$n$ is indeed a square, set \kbd{sqrtn} to its integer square root.
+Uses a fast congruence test mod $64\times 63\times 65\times 11$ before
+computing an integer square root.
+
+\fun{long}{Z_ispow2}{GEN x} returns $1$ if the \typ{INT} $x$ is a power of
+$2$, and $0$ otherwise.
+
+\fun{long}{uissquare}{ulong n} as \kbd{Z\_issquare},
+for an \kbd{ulong} operand \kbd{n}.
+
+\fun{long}{uissquareall}{ulong n, ulong *sqrtn} as \kbd{Z\_issquareall},
+for an \kbd{ulong} operand \kbd{n}.
+
+\fun{ulong}{usqrt}{ulong a} returns the floor of the square root of $a$.
+
+\fun{ulong}{usqrtn}{ulong a, ulong n} returns the floor of the $n$-th root
+of $a$.
+
+\fun{long}{Z_ispower}{GEN x, ulong k} returns $1$ if the \typ{INT} $n$ is a
+$k$-th power, and $0$ otherwise; assume that $k > 1$.
+
+\fun{long}{Z_ispowerall}{GEN x, ulong k, GEN *pt} as \kbd{Z\_ispower}. If
+$n$ is indeed a $k$-th power, set \kbd{*pt} to its integer $k$-th root.
+
+\fun{long}{Z_isanypower}{GEN x, GEN *ptn} returns the maximal $k\geq 2$  such
+that the \typ{INT} $x = n^k$ is a perfect power, or $0$ if no such $k$ exist;
+in particular \kbd{ispower(1)}, \kbd{ispower(0)}, \kbd{ispower(-1)} all
+return 0. If the return value $k$ is not $0$ (so that $x = n^k$) and
+\kbd{ptn} is not \kbd{NULL}, set \kbd{*ptn} to $n$.
+
+The following low-level functions are called by \tet{Z_isanypower} but can
+be directly useful:
+
+\fun{int}{is_357_power}{GEN x, GEN *ptn, ulong *pmask} tests whether the
+integer $x > 0$ is a $3$-rd, $5$-th or $7$-th power. The bits of \kbd{*mask}
+initially indicate which test is to be performed;
+bit $0$: $3$-rd,
+bit $1$: $5$-th,
+bit $2$: $7$-th (e.g.~$\kbd{*pmask} = 7$ performs all tests). They are
+updated during the call: if the ``$i$-th power'' bit is set to $0$
+then $x$ is not a $k$-th power. The function returns $0$
+(not a
+$3$-rd,
+$5$-th or
+$7$-th power),
+$3$
+($3$-rd power,
+not a $5$-th or
+$7$-th power),
+$5$
+($5$-th power,
+not a $7$-th power),
+or $7$
+($7$-th power); if an $i$-th power bit is initially set to $0$, we take it
+at face value and assume $x$ is not an $i$-th power without performing any
+test. If the return value $k$ is non-zero, set \kbd{*ptn} to $n$ such that $x
+= n^k$.
+
+\fun{int}{is_pth_power}{GEN x, GEN *ptn, forprime_t *T, ulong cutoff}
+let $x > 0$ be an integer, $\kbd{cutoff} > 0$ and $T$ be an iterator over
+primes $\geq 11$, we look for the smallest prime $p$ such that $x = n^p$
+(advancing $T$ as we go along). The $11$ is due to the fact that
+\tet{is_357_power} and \kbd{issquare} are faster than the generic version for
+$p < 11$.
+
+Fail and return $0$ when the existence of $p$ would imply $2^{\kbd{cutoff}} >
+x^{1/p}$, meaning that a possible $n$ is so small that it should have been
+found by trial division; for maximal speed, you should start by a round of
+trial division, but the cut-off may also be set to $1$ for a rigorous result
+without any trial division.
+
+Otherwise returns the smallest suitable prime power $p^i$ and set \kbd{*ptn}
+to the $p^i$-th root of $x$ (which is now not a $p$-th power). We may
+immediately recall the function with the same parameters after setting $x =
+\kbd{*ptn}$: it will start at the next prime.
+
+\subsec{Factorization}
+
+\fun{GEN}{Z_factor}{GEN n} factors the \typ{INT} \kbd{n}. The ``primes''
+in the factorization are actually strong pseudoprimes.
+
+\fun{GEN}{absi_factor}{GEN n} returns \kbd{Z\_factor(absi(n))}.
+
+\fun{long}{Z_issmooth}{GEN n, ulong lim} returns $1$ if all the
+prime factors of the \typ{INT} $n$ are less or equal to $lim$.
+
+\fun{GEN}{Z_issmooth_fact}{GEN n, ulong lim} returns \kbd{NULL} if a prime
+factor of the \typ{INT} $n$ is $> lim$, and returns the factorization
+of $n$ otherwise, as a \typ{MAT} with \typ{VECSMALL} columns (word-size
+primes and exponents). Neither memory-clean nor suitable for
+\kbd{gerepileupto}.
+
+\fun{GEN}{Z_factor_until}{GEN n, GEN lim} as \kbd{Z\_factor}, but stop the
+factorization process as soon as the unfactored part is smaller than \kbd{lim}.
+The resulting factorization matrix only contains the factors found. No other
+assumptions can be made on the remaining factors.
+
+\fun{GEN}{Z_factor_limit}{GEN n, ulong lim} trial divide $n$ by all primes $p
+< \kbd{lim}$ in the precomputed list of prime numbers and return the
+corresponding factorization matrix. In this case, the last ``prime'' divisor
+in the first column of the factorization matrix may well be a proven
+composite.
+
+If $\kbd{lim} = 0$, the effect is the same as setting $\kbd{lim} =
+\kbd{maxprime()} + 1$: use all precomputed primes.
+
+\fun{GEN}{absi_factor_limit}{GEN n, ulong all}returns
+\kbd{Z\_factor\_limit(absi(n))}.
+
+\fun{GEN}{boundfact}{GEN x, ulong lim} as \tet{Z_factor_limit}, applying to
+\typ{INT} or \typ{FRAC} inputs.
+
+\fun{GEN}{Z_smoothen}{GEN n, GEN L, GEN *pP, GEN *pE} given a \typ{VECSMALL}
+$L$ containing a list of small primes and a \typ{INT} $n$, trial divide
+$n$ by the elements of $L$ and return the cofactor. Return \kbd{NULL} if the
+cofactor is $\pm 1$. \kbd{*P} and \kbd{*E} contain the list of prime divisors
+found and their exponents, as \typ{VECSMALL}s. Neither memory-clean, nor
+suitable for \tet{gerepileupto}.
+
+\fun{GEN}{Z_factor_listP}{GEN N, GEN L} given a \typ{INT} $N$, a vector or
+primes $L$ containing all prime divisors of $N$ (and possibly others). Return
+\kbd{factor(N)}. Neither memory-clean, nor suitable for \tet{gerepileupto}.
+
+\fun{GEN}{factor_pn_1}{GEN p, ulong n} returns the factorization of $p^n-1$,
+where $p$ is prime and $n$ is a positive integer.
+
+\fun{GEN}{factor_pn_1_limit}{GEN p, ulong n, ulong B} returns a partial
+factorization of $p^n-1$, where $p$ is prime and $n$ is a positive integer.
+Don't actively search for prime divisors $p > B$, but we may find still find
+some due to Aurifeuillian factorizations. Any entry $> B^2$ in the output
+factorization matrix is \emph{a priori} not a prime (but may well be).
+
+\fun{GEN}{factor_Aurifeuille_prime}{GEN p, long n} an Aurifeuillian factor
+of $\phi_n(p)$, assuming $p$ prime and an Aurifeuillian factor exists
+($p \zeta_n$ is a square in $\Q(\zeta_n)$).
+
+\fun{GEN}{factor_Aurifeuille}{GEN a, long d} an Aurifeuillian factor of
+$\phi_n(a)$, assuming $a$ is a non-zero integer and $n > 2$. Returns $1$
+if no Aurifeuillian factor exists.
+
+\fun{GEN}{odd_prime_divisors}{GEN a} \typ{VEC} of all prime divisors of the
+\typ{INT} $a$.
+
+\fun{GEN}{factoru}{ulong n}, returns the factorization of $n$. The result
+is a $2$-component vector $[P,E]$, where $P$ and $E$ are \typ{VECSMALL}
+containing the prime divisors of $n$, and the $v_p(n)$.
+
+\fun{GEN}{factoru_pow}{ulong n}, returns the factorization of $n$. The result
+is a $3$-component vector $[P,E,C]$, where $P$, $E$ and $C$ are
+\typ{VECSMALL} containing the prime divisors of $n$, the $v_p(n)$
+and the $p^{v_p(n)}$.
+
+\fun{ulong}{tridiv_bound}{GEN n} returns the trial division bound used by
+\tet{Z_factor}$(n)$.
+
+\subsec{Checks associated to arithmetic functions}
+
+Arithmetic functions accept arguments of the following kind: a plain positive
+integer $N$ (\typ{INT}), the factorization \var{fa} of a positive integer (a
+\typ{MAT} with two columns containing respectively primes and exponents), or
+a vector $[N,\var{fa}]$. A few functions accept non-zero
+integers (e.g.~\tet{omega}), and some others arbitrary integers
+(e.g.~\tet{factorint}, \dots).
+
+\fun{int}{is_Z_factorpos}{GEN f} returns $1$ if $f$ looks like the
+factorization of a positive integer, and $0$ otherwise. Useful for sanity
+checks but not 100\% foolproof. Specifically, this routine checks that $f$ is
+a two-column matrix all of whose entries are positive integers. It does
+\emph{not} check that entries in the first column (``primes'') are prime,
+or even pairwise coprime, nor that they are stricly increasing.
+
+\fun{int}{is_Z_factornon0}{GEN f} returns $1$ if $f$ looks like the
+factorization of a non-zero integer, and $0$ otherwise. Useful for sanity
+checks but not 100\% foolproof, analogous to \tet{is_Z_factorpos}. (Entries
+in the first column need only be non-zero integers.)
+
+\fun{int}{is_Z_factor}{GEN f} returns $1$ if $f$ looks like the
+factorization of an integer, and $0$ otherwise. Useful for sanity
+checks but not 100\% foolproof. Specifically, this routine checks that $f$ is
+a two-column matrix all of whose entries are integers. Entries in the second
+column (``exponents'') are all positive. Either it encodes the
+``factorization'' $0^e$, $e > 0$, or entries in the first column (``primes'')
+are all non-zero.
+
+\fun{GEN}{clean_Z_factor}{GEN f} assuming $f$ is the factorization of an
+integer $n$, return the factorization of $|n|$, i.e.~remove $-1$ from the
+factorization. Shallow function.
+
+In the following two routines, $f$ is the name of an arithmetic function,
+and $n$ a supplied argument. They all raise exceptions if $n$ does not
+correspond to an integer or an integer factorization of the expected shape.
+
+\fun{GEN}{check_arith_pos}{GEN n, const char *f} check whether $n$
+is associated to the factorization of a positive integer, and return
+\kbd{NULL} (plain \typ{INT}) or a factorization extracted from $n$ otherwise.
+May raise an \tet{e_DOMAIN} ($n \leq 0$) or an \tet{e_TYPE} exception (other
+failures).
+
+\fun{GEN}{check_arith_non0}{GEN n, const char *f} check whether $n$
+is associated to the factorization of a non-$0$ integer, and return
+\kbd{NULL} (plain \typ{INT}) or a factorization extracted from $n$ otherwise.
+May raise an \tet{e_TYPE} exception.
+
+\fun{GEN}{check_arith_all}{GEN n, const char *f}
+is associated to the factorization of an integer, and return \kbd{NULL}
+(plain \typ{INT}) or a factorization extracted from $n$ otherwise.
+
+\subsec{Incremental integer factorization}
+
+Routines associated to the dynamic factorization of an integer $n$, iterating
+over successive prime divisors. This is useful to implement high-level
+routines allowed to take shortcuts given enough partial information: e.g.
+\kbd{moebius}$(n)$ can be trivially computed if we hit $p$ such that $p^2
+\mid n$. For efficiency, trial division by small primes should have already
+taken place. In any case, the functions below assume that no prime $< 2^{14}$
+divides $n$.
+
+\fun{GEN}{ifac_start}{GEN n, int moebius} schedules a new factorization
+attempt for the integer $n$. If \kbd{moebius} is non-zero, the factorization
+will be aborted as soon as a repeated factor is detected (Moebius mode).
+The function assumes that $n > 1$ is a \emph{composite} \typ{INT} whose prime
+divisors satisfy $p > 2^{14}$ \emph{and} that one can write to $n$ in place.
+
+This function stores data on the stack, no \kbd{gerepile} call should
+delete this data until the factorization is complete. Returns \kbd{partial},
+a data structure recording the partial factorization state.
+
+\fun{int}{ifac_next}{GEN *partial, GEN *p, long *e} deletes a primary factor
+$p^e$ from \kbd{partial} and sets \kbd{p} (prime) and \kbd{e} (exponent), and
+normally returns $1$. Whatever remains in the \kbd{partial} structure is now
+coprime to $p$.
+
+Returns $0$ if all primary factors have been used already, so we are done
+with the factorization. In this case $p$ is set to \kbd{NULL}. If we ran in
+Moebius mode and the factorization was in fact aborted, we have $e = 1$,
+otherwise $e = 0$.
+
+\fun{int}{ifac_read}{GEN part, GEN *k, long *e} peeks at the next integer
+to be factored in the list $k^e$, where $k$ is not necessarily prime
+and can be a perfect power as well, but will be factored by the next call to
+\tet{ifac_next}. You can remove this factorization from the schedule by
+calling:
+
+\fun{void}{ifac_skip}{GEN part} removes the next scheduled factorization.
+
+\fun{int}{ifac_isprime}{GEN n} given $n$ whose prime divisors are $> 2^{14}$,
+returns the decision the factoring engine would take about the compositeness
+of $n$: $0$ if $n$ is a proven composite, and $1$ if we believe it to be
+prime; more precisely, $n$ is a proven prime if \tet{factor_proven} is
+set, and only a BPSW-pseudoprime otherwise.
+
+\subsec{Integer core, squarefree factorization}
+
+\fun{long}{Z_issquarefree}{GEN n} returns $1$ if the \typ{INT} \kbd{n}
+is square-free, and $0$ otherwise.
+
+\fun{long}{Z_isfundamental}{GEN x} returns $1$ if the \typ{INT} \kbd{x}
+is a fundamental discriminant, and $0$ otherwise.
+
+\fun{GEN}{core}{GEN n} unique squarefree integer $d$ dividing $n$ such that
+$n/d$ is a square. The core of $0$ is defined to be $0$.
+
+\fun{GEN}{core2}{GEN n} return $[d,f]$ with $d$ squarefree and $n = df^2$.
+
+\fun{GEN}{corepartial}{GEN n, long lim} as \kbd{core}, using
+\kbd{boundfact(n,lim)} to partially factor \kbd{n}. The result is not
+necessarily squarefree, but $p^2 \mid n$ implies $p > \kbd{lim}$.
+
+\fun{GEN}{core2partial}{GEN n, long lim} as \kbd{core2}, using
+\kbd{boundfact(n,lim)} to partially factor \kbd{n}. The resulting $d$ is not
+necessarily squarefree, but $p^2 \mid n$ implies $p > \kbd{lim}$.
+
+
+
+\subsec{Primes, primality and compositeness tests}
+
+\subsubsec{Chebyshev's $\pi$ function, bounds}
+
+\fun{ulong}{uprimepi}{ulong n}, returns the number of primes $p\leq n$
+(Chebyshev's $\pi$ function).
+
+\fun{double}{primepi_upper_bound}{double x} return a quick upper bound for
+$\pi(x)$, using Dusart bounds.
+
+\fun{GEN}{gprimepi_upper_bound}{GEN x} as \tet{primepi_upper_bound}, returns a
+\typ{REAL}.
+
+\fun{double}{primepi_lower_bound}{double x} return a quick lower bound for
+$\pi(x)$, using Dusart bounds.
+
+\fun{GEN}{gprimepi_lower_bound}{GEN x} as \tet{primepi_lower_bound}, returns
+a \typ{REAL} or \kbd{gen\_0}.
+
+\subsubsec{Primes, primes in intervals}
+
+\fun{ulong}{unextprime}{ulong n}, returns the smallest prime $\geq n$. Return
+$0$ if it cannot be represented as an \kbd{ulong} ($n$ bigger than $2^{64} -
+59$ or $2^{32} - 5$ depending on the word size).
+
+\fun{ulong}{uprecprime}{ulong n}, returns the largest prime $\leq n$. Return
+$0$ if $n\leq 1$.
+
+\fun{ulong}{uprime}{long n} returns the $n$-th prime, assuming it fits in an
+\kbd{ulong} (overflow error otherwise).
+
+\fun{GEN}{prime}{long n} same as \kbd{utoi(uprime(n))}.
+
+\fun{GEN}{primes_zv}{long m} returns the first $m$ primes, in a
+\typ{VECSMALL}.
+
+\fun{GEN}{primes}{long m} return the first $m$ primes, as a \typ{VEC} of
+\typ{INT}s.
+
+\fun{GEN}{primes_interval}{GEN a, GEN b} return the primes in the interval
+$[a,b]$, as a \typ{VEC} of \typ{INT}s.
+
+\fun{GEN}{primes_interval_zv}{ulong a, ulong b} return the primes in the
+interval $[a,b]$, as a \typ{VECSMALL} of \kbd{ulongs}s.
+
+\fun{GEN}{primes_upto_zv}{ulong b} return the primes in the interval $[2,b]$,
+as a \typ{VECSMALL} of \kbd{ulongs}s.
+
+\subsubsec{Tests}
+
+\fun{int}{uisprime}{ulong p}, returns $1$ if \kbd{p} is a prime number and
+$0$ otherwise.
+
+\fun{int}{isprime}{GEN n}, returns $1$ if the \typ{INT} \kbd{n} is a
+(fully proven) prime number and $0$ otherwise.
+
+\fun{long}{isprimeAPRCL}{GEN n}, returns $1$ if the \typ{INT} \kbd{n} is a
+prime number and $0$ otherwise, using only the APRCL test --- not even trial
+division or compositeness tests. The workhorse \kbd{isprime} should be
+faster on average, especially if non-primes are included!
+
+\fun{long}{BPSW_psp}{GEN n}, returns $1$ if the \typ{INT} \kbd{n} is a
+Baillie-Pomerance-Selfridge-Wagstaff pseudoprime, and $0$ otherwise (proven
+composite).
+
+\fun{int}{BPSW_isprime}{GEN x} assuming $x$ is a BPSW-pseudoprime, rigorously
+prove its primality. The function \tet{isprime} is currently implemented
+as
+\bprog
+ BPSW_psp(x) && BPSW_isprime(x)
+ at eprog
+
+\fun{long}{millerrabin}{GEN n, long k} performs $k$ strong Rabin-Miller
+compositeness tests on the \typ{INT} $n$, using $k$ random bases. This
+function also caches square roots of $-1$ that are encountered during the
+successive tests and stops as soon as three distinct square roots have been
+produced; we have in principle factored $n$ at this point, but
+unfortunately, there is currently no way for the factoring machinery to
+become aware of it. (It is highly implausible that hard to find factors
+would be exhibited in this way, though.) This should be slower than
+\tet{BPSW_psp} for $k\geq 4$ and we would expect it to be less reliable.
+
+\subsec{Iterators over primes}
+
+\fun{int}{forprime_init}{forprime_t *T, GEN a, GEN b} initialize an
+iterator $T$ over primes in $[a,b]$; over primes $\geq a$ if $b =
+\kbd{NULL}$. Return $0$ if the range is known to be empty from the start
+(as if $b < a$ or $b < 0$), and return $1$ otherwise.
+
+\fun{GEN}{forprime_next}{forprime_t *T} returns the next prime in the range,
+assuming that $T$ was initialized by \tet{forprime_init}.
+
+\fun{int}{u_forprime_init}{forprime_t *T, ulong a, ulong b}
+
+\fun{ulong}{u_forprime_next}{forprime_t *T}
+
+\fun{void}{u_forprime_restrict}{forprime_t *T, ulong c} let $T$ an iterator
+over primes initialized via \kbd{u\_forprime\_init(\&T, a, b)}, possibly
+followed by a number of calls to \tet{u_forprime_next}, and $a \leq c \leq
+b$. Restrict the range of primes considered to $[a,c]$.
+
+\fun{int}{u_forprime_arith_init}{forprime_t *T, ulong a,ulong b, ulong c,ulong q} initialize an iterator over primes in $[a,b]$, congruent to $c$
+modulo $q$. Assume $0 \leq c < q$ and $(c,q) = 1$. Subsequent calls to
+\tet{u_forprime_next} will only return primes congruent to $c$ modulo $q$.
+
+\section{Integral, rational and generic linear algebra}
+\subsec{\kbd{ZC} / \kbd{ZV}, \kbd{ZM}} A \kbd{ZV} (resp.~a~\kbd{ZM},
+resp.~a~\kbd{ZX}) is a \typ{VEC} or \typ{COL} (resp.~\typ{MAT},
+resp.~\typ{POL}) with \typ{INT} coefficients.
+
+\subsubsec{\kbd{ZC} / \kbd{ZV}}
+
+\fun{void}{RgV_check_ZV}{GEN x, const char *s} Assuming \kbd{x} is a \typ{VEC}
+or \typ{COL} raise an error if it is not a \kbd{ZV} ($s$ should point to the
+name of the caller).
+
+\fun{int}{RgV_is_ZV}{GEN x} Assuming \kbd{x} is a \typ{VEC}
+or \typ{COL} return $1$ if it is a \kbd{ZV}, and $0$ otherwise.
+
+\fun{int}{RgV_is_QV}{GEN P} return 1 is the \kbd{RgV}~$P$ has only
+\typ{INT} and \typ{FRAC} coefficients, and 0 otherwise.
+
+\fun{int}{ZV_equal0}{GEN x} returns 1 if all entries of the \kbd{ZV} $x$ are
+zero, and $0$ otherwise.
+
+\fun{int}{ZV_cmp}{GEN x, GEN y} compare two \kbd{ZV}, which we assume have
+the same length (lexicographic order, comparing absolute values).
+
+\fun{int}{ZV_abscmp}{GEN x, GEN y} compare two \kbd{ZV}, which we assume have
+the same length (lexicographic order).
+
+\fun{int}{ZV_equal}{GEN x, GEN y} returns $1$ if the two \kbd{ZV} are equal
+and $0$ otherwise. A \typ{COL} and a \typ{VEC} with the same entries are
+declared equal.
+
+\fun{GEN}{ZC_add}{GEN x, GEN y} adds \kbd{x} and \kbd{y}.
+
+\fun{GEN}{ZC_sub}{GEN x, GEN y} subtracts \kbd{x} and \kbd{y}.
+
+\fun{GEN}{ZC_Z_add}{GEN x, GEN y} adds \kbd{y} to \kbd{x[1]}.
+
+\fun{GEN}{ZC_Z_sub}{GEN x, GEN y} subtracts \kbd{y} to \kbd{x[1]}.
+
+\fun{GEN}{ZC_copy}{GEN x} returns a (\typ{COL}) copy of \kbd{x}.
+
+\fun{GEN}{ZC_neg}{GEN x} returns $-\kbd{x}$ as a \typ{COL}.
+
+\fun{void}{ZV_neg_inplace}{GEN x} negates the \kbd{ZV} \kbd{x} in place, by
+replacing each component by its opposite (the type of \kbd{x} remains the
+same, \typ{COL} or \typ{COL}). If you want to save even more memory by
+avoiding the implicit component copies, use \kbd{ZV\_togglesign}.
+
+\fun{void}{ZV_togglesign}{GEN x} negates \kbd{x} in place, by toggling the
+sign of its integer components. Universal constants \kbd{gen\_1},
+\kbd{gen\_m1}, \kbd{gen\_2} and \kbd{gen\_m2} are handled specially and will
+not be corrupted. (We use \tet{togglesign_safe}.)
+
+\fun{GEN}{ZC_Z_mul}{GEN x, GEN y} multiplies the \kbd{ZC} or \kbd{ZV}~\kbd{x}
+(which can be a column or row vector) by the \typ{INT}~\kbd{y}, returning a
+\kbd{ZC}.
+
+\fun{GEN}{ZC_Z_divexact}{GEN x, GEN y} returns $x/y$ assuming all divisions
+are exact.
+
+\fun{GEN}{ZV_dotproduct}{GEN x,GEN y} as \kbd{RgV\_dotproduct} assuming $x$
+and $y$ have \typ{INT} entries.
+
+\fun{GEN}{ZV_dotsquare}{GEN x} as \kbd{RgV\_dotsquare} assuming $x$
+has \typ{INT} entries.
+
+\fun{GEN}{ZC_lincomb}{GEN u, GEN v, GEN x, GEN y} returns $ux + vy$, where
+$u$, $v$ are \typ{INT} and $x,y$ are \kbd{ZC} or \kbd{ZV}. Return a \kbd{ZC}
+
+\fun{void}{ZC_lincomb1_inplace}{GEN X, GEN Y, GEN v} sets $X\leftarrow X +
+vY$, where $v$ is a \typ{INT} and $X,Y$ are \kbd{ZC} or \kbd{ZV}. (The result
+has the type of $X$.) Memory efficient (e.g. no-op if $v = 0$), but not
+gerepile-safe.
+
+\fun{GEN}{ZC_ZV_mul}{GEN x, GEN y, GEN p} multiplies the \kbd{ZC}~\kbd{x}
+(seen as a column vector) by the \kbd{ZV}~\kbd{y} (seen as a row vector,
+assumed to have compatible dimensions).
+
+\fun{GEN}{ZV_content}{GEN x} returns the GCD of all the components
+of~\kbd{x}.
+
+\fun{GEN}{ZV_gcdext}{GEN A} given a vector of $n$ integers $A$, returns $[d,
+U]$, where $d$ is the content of $A$ and $U$ is a matrix
+in $\text{GL}_n(\Z)$ such that $AU = [D,0, \dots,0]$.
+
+\fun{GEN}{ZV_prod}{GEN x} returns the product of all the components
+of~\kbd{x} ($1$ for the empty vector).
+
+\fun{GEN}{ZV_sum}{GEN x} returns the sum of all the components
+of~\kbd{x} ($0$ for the empty vector).
+
+\fun{long}{ZV_max_lg}{GEN x} returns the effective length of the longest
+entry in $x$.
+
+\fun{int}{ZV_dvd}{GEN x, GEN y} assuming $x$, $y$ are two \kbd{ZV}s of the same
+length, return $1$ if $y[i]$ divides $x[i]$ for all $i$ and $0$ otherwise.
+Error if one of the $y[i]$ is $0$.
+
+\fun{GEN}{ZV_sort}{GEN L} sort the \kbd{ZV} $L$.
+Returns a vector with the same type as $L$.
+
+\fun{GEN}{ZV_sort_uniq}{GEN L} sort the \kbd{ZV} $L$, removing duplicate
+entries. Returns a vector with the same type as $L$.
+
+\fun{long}{ZV_search}{GEN L, GEN y} look for the \typ{INT} $y$ in the sorted
+\kbd{ZV} $L$. Return an index $i$ such that $L[i] = y$, and  $0$ otherwise.
+
+\fun{GEN}{ZV_indexsort}{GEN L} returns the permutation which, applied to the
+\kbd{ZV} $L$, would sort the vector. The result is a \typ{VECSMALL}.
+
+\fun{GEN}{ZV_union_shallow}{GEN x, GEN y} given two \emph{sorted} ZV (as per
+\tet{ZV_sort}, returns the union of $x$ and $y$. Shallow function. In case two
+entries are equal in $x$ and $y$,  include the one from $x$.
+
+\subsubsec{\kbd{ZM}}
+
+\fun{void}{RgM_check_ZM}{GEN A, const char *s} Assuming \kbd{x} is a \typ{MAT}
+raise an error if it is not a \kbd{ZM} ($s$ should point to the name of the
+caller).
+
+\fun{GEN}{ZM_copy}{GEN x} returns a copy of \kbd{x}.
+
+\fun{int}{ZM_equal}{GEN A, GEN B} returns $1$ if the two \kbd{ZM} are equal
+and $0$ otherwise.
+
+\fun{GEN}{ZM_add}{GEN x, GEN y} returns $\kbd{x} + \kbd{y}$ (assumed to have
+compatible dimensions).
+
+\fun{GEN}{ZM_sub}{GEN x, GEN y} returns $\kbd{x} - \kbd{y}$ (assumed to have
+compatible dimensions).
+
+\fun{GEN}{ZM_neg}{GEN x} returns $-\kbd{x}$.
+
+\fun{void}{ZM_togglesign}{GEN x} negates \kbd{x} in place, by toggling the
+sign of its integer components. Universal constants \kbd{gen\_1},
+\kbd{gen\_m1}, \kbd{gen\_2} and \kbd{gen\_m2} are handled specially and will
+not be corrupted. (We use \tet{togglesign_safe}.)
+
+\fun{GEN}{ZM_mul}{GEN x, GEN y} multiplies \kbd{x} and \kbd{y} (assumed to
+have compatible dimensions).
+
+\fun{GEN}{ZM_Z_mul}{GEN x, GEN y} multiplies the \kbd{ZM}~\kbd{x}
+by the \typ{INT}~\kbd{y}.
+
+\fun{GEN}{ZM_ZC_mul}{GEN x, GEN y} multiplies the \kbd{ZM}~\kbd{x}
+by the \kbd{ZC}~\kbd{y} (seen as a column vector, assumed to have compatible
+dimensions).
+
+\fun{GEN}{ZM_multosym}{GEN x, GEN y}
+
+\fun{GEN}{ZM_transmultosym}{GEN x, GEN y}
+
+\fun{GEN}{ZMrow_ZC_mul}{GEN x, GEN y, long i} multiplies the $i$-th row
+of \kbd{ZM}~\kbd{x} by the \kbd{ZC}~\kbd{y} (seen as a column vector, assumed
+to have compatible dimensions). Assumes that $x$ is non-empty and
+$0 < i < \kbd{lg(x[1])}$.
+
+\fun{GEN}{ZV_ZM_mul}{GEN x, GEN y} multiplies the \kbd{ZV}~\kbd{x}
+by the \kbd{ZM}~\kbd{y}. Returns a \typ{VEC}.
+
+\fun{GEN}{ZM_Z_divexact}{GEN x, GEN y} returns $x/y$ assuming all divisions
+are exact.
+
+\fun{GEN}{ZM_pow}{GEN x, GEN n} returns $\kbd{x}^\kbd{n}$, assuming \kbd{x}
+is a square \kbd{ZM} and $\kbd{n}\geq 0$.
+
+\fun{GEN}{ZM_powu}{GEN x, ulong n} returns $\kbd{x}^\kbd{n}$, assuming \kbd{x}
+is a square \kbd{ZM} and $\kbd{n}\geq 0$.
+
+\fun{GEN}{ZM_det}{GEN M} if \kbd{M} is a \kbd{ZM}, returns the determinant of
+$M$. This is the function underlying \tet{matdet} whenever $M$ is a \kbd{ZM}.
+
+\fun{GEN}{ZM_detmult}{GEN M} if \kbd{M} is a \kbd{ZM}, returns a multiple of
+the determinant of the lattice generated by its columns. This is the function
+underlying \tet{detint}.
+
+\fun{GEN}{ZM_supnorm}{GEN x} return the sup norm of the \kbd{ZM} $x$.
+
+\fun{GEN}{ZM_charpoly}{GEN M} returns the characteristic polynomial (in
+variable $0$) of the \kbd{ZM} $M$.
+
+\fun{GEN}{ZM_imagecompl}{GEN x} returns \kbd{matimagecompl(x)}.
+
+\fun{long}{ZM_rank}{GEN x} returns \kbd{matrank(x)}.
+
+\fun{GEN}{ZM_indexrank}{GEN x} returns \kbd{matindexrank(x)}.
+
+\fun{GEN}{ZM_indeximage}{GEN x} returns \kbd{gel(ZM\_indexrank(x), 2)}.
+
+\fun{long}{ZM_max_lg}{GEN x} returns the effective length of the longest
+entry in $x$.
+
+\fun{GEN}{ZM_inv}{GEN M, GEN d} if \kbd{M} is a \kbd{ZM} and \kbd{d}
+is a \typ{INT} such that $M' := \kbd{d}\kbd{M}^{-1}$ is integral,
+return $M'$. It is allowed to set \kbd{d = NULL}, in which case, the
+determinant of \kbd{M} is used instead.
+
+\fun{GEN}{QM_inv}{GEN M, GEN d} as above, with \kbd{M} a \kbd{QM}. We
+still assume that $M'$ has integer coefficients.
+
+\fun{GEN}{ZM_det_triangular}{GEN x} returns the product of the diagonal
+entries of $x$ (its determinant if it is indeed triangular).
+
+\fun{int}{ZM_isidentity}{GEN x} return 1 is the \typ{ZM} $x$ is the
+identity matrix, and 0 otherwise.
+
+\fun{int}{ZM_ishnf}{GEN x} return $1$ if $x$ is in HNF form, i.e. is upper
+triangular with positive diagonal coefficients, and  for $j>i$,
+$x_{i,i}>x_{i,j} \ge 0$.
+
+\subsec{\kbd{zv}, \kbd{zm}}
+
+\fun{GEN}{zv_neg}{GEN x} return $-x$. No check for overflow is done, which
+occurs in the fringe case where an entry is equal to $2^{\B-1}$.
+
+\fun{GEN}{zv_neg_inplace}{GEN x} negates $x$ in place and return it. No check
+for overflow is done, which occurs in the fringe case where an entry is equal
+to $2^{\B-1}$.
+
+\fun{GEN}{zm_zc_mul}{GEN x, GEN y}
+
+\fun{GEN}{zm_mul}{GEN x, GEN y}
+
+\fun{GEN}{zv_z_mul}{GEN x, long n} return $n\*x$. No check for overflow is
+done.
+
+\fun{long}{zv_content}{GEN x} returns the gcd of the entries of $x$.
+
+\fun{long}{zv_dotproduct}{GEN x, GEN y}
+
+\fun{long}{zv_prod}{GEN x} returns the product of all the components
+of~\kbd{x} (assumes no overflow occurs).
+
+\fun{GEN}{zv_prod_Z}{GEN x} returns the product of all the components
+of~\kbd{x}; consider all $x[i]$ as \kbd{ulong}s.
+
+\fun{long}{zv_sum}{GEN x} returns the sum of all the components
+of~\kbd{x} (assumes no overflow occurs).
+
+\fun{int}{zv_cmp0}{GEN x} returns 1 if all entries of the \kbd{zv} $x$ are $0$,
+and $0$ otherwise.
+
+\fun{int}{zv_equal}{GEN x, GEN y} returns $1$ if the two \kbd{zv} are equal
+and $0$ otherwise.
+
+\fun{int}{zv_equal0}{GEN x} returns $1$ if all entries are $0$, and return
+$0$ otherwise.
+
+\fun{long}{zv_search}{GEN L, long y} look for $y$ in the sorted
+\kbd{zv} $L$. Return an index $i$ such that $L[i] = y$, and  $0$ otherwise.
+
+\fun{GEN}{zv_copy}{GEN x} as \kbd{Flv\_copy}.
+
+\fun{GEN}{zm_transpose}{GEN x} as \kbd{Flm\_transpose}.
+
+\fun{GEN}{zm_copy}{GEN x} as \kbd{Flm\_copy}.
+
+\fun{GEN}{zero_zm}{long m, long n} as \kbd{zero\_Flm}.
+
+\fun{GEN}{zero_zv}{long n} as \kbd{zero\_Flv}.
+
+\fun{GEN}{row_zm}{GEN A, long x0} as \kbd{row\_Flm}.
+
+\fun{int}{zvV_equal}{GEN x, GEN y} returns $1$ if the two \kbd{zvV} (vectors
+of \kbd{zv}) are equal and $0$ otherwise.
+
+\subsec{\kbd{ZMV} / \kbd{zmV} (vectors of \kbd{ZM}/\kbd{zm})}
+
+\fun{int}{RgV_is_ZMV}{GEN x} Assuming \kbd{x} is a \typ{VEC}
+or \typ{COL} return $1$ if its components are \kbd{ZM}, and $0$ otherwise.
+
+\fun{GEN}{ZMV_to_zmV}{GEN z}
+
+\fun{GEN}{zmV_to_ZMV}{GEN z}
+
+\subsec{\kbd{RgC} / \kbd{RgV}, \kbd{RgM}}
+
+\kbd{RgC} and \kbd{RgV} routines assume the inputs are \kbd{VEC} or \kbd{COL}
+of the same dimension. \kbd{RgM} assume the inputs are \kbd{MAT} of
+compatible dimensions.
+
+\subsubsec{Matrix arithmetic}
+
+\fun{void}{RgM_dimensions}{GEN}{x, long *m, long *n} sets $m$, resp.~$n$, to
+the number of rows, resp.~columns of the \typ{MAT} $x$.
+
+\fun{GEN}{RgC_add}{GEN x, GEN y} returns $x + y$ as a \typ{COL}.
+
+\fun{GEN}{RgC_neg}{GEN x} returns $-x$ as a \typ{COL}.
+
+\fun{GEN}{RgC_sub}{GEN x, GEN y} returns $x - y$ as a \typ{COL}.
+
+\fun{GEN}{RgV_add}{GEN x, GEN y} returns $x + y$ as a \typ{VEC}.
+
+\fun{GEN}{RgV_neg}{GEN x} returns $-x$ as a \typ{VEC}.
+
+\fun{GEN}{RgV_sub}{GEN x, GEN y} returns $x - y$ as a \typ{VEC}.
+
+\fun{GEN}{RgM_add}{GEN x, GEN y} return $x+y$.
+
+\fun{GEN}{RgM_neg}{GEN x} returns $-x$.
+
+\fun{GEN}{RgM_sub}{GEN x, GEN y} returns $x-y$.
+
+\fun{GEN}{RgM_Rg_add}{GEN x, GEN y} assuming $x$ is a square matrix
+and $y$ a scalar, returns the square matrix $x + y*\text{Id}$.
+
+\fun{GEN}{RgM_Rg_add_shallow}{GEN x, GEN y} as \kbd{RgM\_Rg\_add} with much
+fewer copies. Not suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{RgM_Rg_sub}{GEN x, GEN y} assuming $x$ is a square matrix
+and $y$ a scalar, returns the square matrix $x - y*\text{Id}$.
+
+\fun{GEN}{RgM_Rg_sub_shallow}{GEN x, GEN y} as \kbd{RgM\_Rg\_sub} with much
+fewer copies. Not suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{RgC_Rg_add}{GEN x, GEN y} assuming $x$ is a non-empty column vector
+and $y$ a scalar, returns the vector $[x_1 + y, x_2,\dots,x_n]$.
+
+\fun{GEN}{RgC_Rg_div}{GEN x, GEN y}
+
+\fun{GEN}{RgM_Rg_div}{GEN x, GEN y} returns $x/y$ ($y$ treated as a scalar).
+
+\fun{GEN}{RgC_Rg_mul}{GEN x, GEN y}
+
+\fun{GEN}{RgV_Rg_mul}{GEN x, GEN y}
+
+\fun{GEN}{RgM_Rg_mul}{GEN x, GEN y} returns $x\times y$ ($y$ treated as a
+scalar).
+
+\fun{GEN}{RgV_RgC_mul}{GEN x, GEN y} returns $x\times y$.
+
+\fun{GEN}{RgV_RgM_mul}{GEN x, GEN y} returns $x\times y$.
+
+\fun{GEN}{RgM_RgC_mul}{GEN x, GEN y} returns $x\times y$.
+
+\fun{GEN}{RgM_mul}{GEN x, GEN y} returns $x\times y$.
+
+\fun{GEN}{RgM_transmul}{GEN x, GEN y} returns $x\til \times y$.
+
+\fun{GEN}{RgM_multosym}{GEN x, GEN y} returns $x\times y$, assuming
+the result is a symmetric matrix (about twice faster than a generic matrix
+multiplication).
+
+\fun{GEN}{RgM_transmultosym}{GEN x, GEN y} returns $x\til \times y$, assuming
+the result is a symmetric matrix (about twice faster than a generic matrix
+multiplication).
+
+\fun{GEN}{RgMrow_RgC_mul}{GEN x, GEN y, long i} multiplies the $i$-th row of
+\kbd{RgM}~\kbd{x} by the \kbd{RgC}~\kbd{y} (seen as a column vector, assumed
+to have compatible dimensions). Assumes that $x$ is non-empty and $0 < i <
+\kbd{lg(x[1])}$.
+
+\fun{GEN}{RgM_mulreal}{GEN x, GEN y} returns the real part of $x\times y$
+(whose entries are \typ{INT}, \typ{FRAC}, \typ{REAL} or \typ{COMPLEX}).
+
+\fun{GEN}{RgM_sqr}{GEN x} returns $x^2$.
+
+\fun{GEN}{RgC_RgV_mul}{GEN x, GEN y} returns $x\times y$ (the square matrix
+$(x_iy_j)$).
+
+The following two functions are not well defined in general and only provided
+for convenience in specific cases:
+
+\fun{GEN}{RgC_RgM_mul}{GEN x, GEN y} returns $x\times y[1,]$ if $y$ is
+a row matrix $1\times n$, error otherwise.
+
+\fun{GEN}{RgM_RgV_mul}{GEN x, GEN y} returns $x\times y[,1]$ if $y$ is
+a column matrix $n\times 1$, error otherwise.
+
+\fun{GEN}{RgM_powers}{GEN x, long n} returns $[\kbd{x}^0,
+\dots, \kbd{x}^\kbd{n}]$ as a \typ{VEC} of \kbd{RgM}s.
+
+\smallskip
+\fun{GEN}{RgV_sum}{GEN v} sum of the entries of $v$
+
+\fun{GEN}{RgV_sumpart}{GEN v, long n} returns the sum $v[1] + \dots + v[n]$
+(assumes that \kbd{lg}$(v) > n$).
+
+\fun{GEN}{RgV_sumpart2}{GEN v, long m, long n} returns the sum $v[m] + \dots +
+v[n]$ (assumes that \kbd{lg}$(v) > n$ and $m > 0$). Returns \kbd{gen\_0}
+when $m > n$.
+
+\fun{GEN}{RgV_dotproduct}{GEN x,GEN y} returns the scalar product of $x$ and $y$
+
+\fun{GEN}{RgV_dotsquare}{GEN x} returns the scalar product of $x$ with itself.
+
+\fun{GEN}{gram_matrix}{GEN v} returns the \idx{Gram matrix} $(v_i\cdot v_j)$
+associated to the entries of $v$ (matrix, or vector of vectors).
+
+\fun{GEN}{RgV_polint}{GEN X, GEN Y, long v} $X$ and $Y$ being two vectors of
+the same length, returns the polynomial $T$ in variable $v$ such that
+$T(X[i]) = Y[i]$ for all $i$. The special case $X = \kbd{NULL}$
+corresponds to $X = [1,2,\dots,n]$, where $n$ is the length of $Y$.
+
+\subsubsec{Special shapes}
+
+The following routines check whether matrices or vectors have a special
+shape, using \kbd{gequal1} and \kbd{gequal0} to test components. (This makes
+a difference when components are inexact.)
+
+\fun{int}{RgV_isscalar}{GEN x} return 1 if all the entries of $x$ are $0$
+(as per \kbd{gequal0}), except possibly the first one. The name comes from
+vectors expressing polynomials on the standard basis $1,T,\dots, T^{n-1}$, or
+on \kbd{nf.zk} (whose first element is $1$).
+
+\fun{int}{QV_isscalar}{GEN x} as \kbd{RgV\_isscalar}, assuming $x$ is a
+\kbd{QV} (\typ{INT} and \typ{FRAC} entries only).
+
+\fun{int}{ZV_isscalar}{GEN x} as \kbd{RgV\_isscalar}, assuming $x$ is a
+\kbd{ZV} (\typ{INT} entries only).
+
+\fun{int}{RgM_isscalar}{GEN x, GEN s} return 1 if $x$ is the scalar matrix
+equal to $s$ times the identity, and 0 otherwise. If $s$ is \kbd{NULL}, test
+whether $x$ is an arbitrary scalar matrix.
+
+\fun{int}{RgM_isidentity}{GEN x} return 1 is the \typ{MAT} $x$ is the
+identity matrix, and 0 otherwise.
+
+\fun{int}{RgM_isdiagonal}{GEN x} return 1 is the \typ{MAT} $x$ is a
+diagonal matrix, and 0 otherwise.
+
+\fun{int}{RgM_is_ZM}{GEN x} return 1 is the \typ{MAT}~$x$ has only
+\typ{INT} coefficients, and 0 otherwise.
+
+\fun{long}{RgV_isin}{GEN v, GEN x} return the first index $i$ such that
+$v[i] = x$ if it exists, and $0$ otherwise. Naive search in linear time, does
+not assume that \kbd{v} is sorted.
+
+\fun{GEN}{RgM_diagonal}{GEN m} returns the diagonal of $m$ as a \typ{VEC}.
+
+\fun{GEN}{RgM_diagonal_shallow}{GEN m} shallow version of \kbd{RgM\_diagonal}
+
+\subsubsec{Conversion to floating point entries}
+
+\fun{GEN}{RgC_gtofp}{GEN x, GEN prec} returns the \typ{COL} obtained by
+applying \kbd{gtofp(gel(x,i), prec)} to all coefficients of $x$.
+
+\fun{GEN}{RgC_gtomp}{GEN x, long prec} returns the \typ{COL} obtained by
+applying \kbd{gtomp(gel(x,i), prec)} to all coefficients of $x$.
+
+\fun{GEN}{RgC_fpnorml2}{GEN x, long prec} returns (a stack-clean variant of)
+\bprog
+  gnorml2( RgC_gtofp(x, prec) )
+ at eprog
+
+\fun{GEN}{RgM_gtofp}{GEN x, GEN prec} returns the \typ{MAT} obtained by
+applying \kbd{gtofp(gel(x,i), prec)} to all coefficients of $x$.
+
+\fun{GEN}{RgM_gtomp}{GEN x, long prec} returns the \typ{MAT} obtained by
+applying \kbd{gtomp(gel(x,i), prec)} to all coefficients of $x$.
+
+\fun{GEN}{RgM_fpnorml2}{GEN x, long prec} returns (a stack-clean variant of)
+\bprog
+  gnorml2( RgM_gtofp(x, prec) )
+ at eprog
+
+\subsubsec{Linear algebra, linear systems}
+
+\fun{GEN}{RgM_inv}{GEN a} returns a left inverse of $a$ (which needs not be
+square), or \kbd{NULL} if this turns out to be impossible. The latter
+happens when the matrix does not have maximal rank (or when rounding errors
+make it appear so).
+
+\fun{GEN}{RgM_inv_upper}{GEN a} as \kbd{RgM\_inv}, assuming that $a$ is a
+non-empty invertible upper triangular matrix, hence a little faster.
+
+\fun{GEN}{RgM_RgC_invimage}{GEN A, GEN B} returns a \typ{COL} $X$ such that
+$A X = B$ if one such exists, and \kbd{NULL} otherwise.
+
+\fun{GEN}{RgM_invimage}{GEN A, GEN B} returns a \typ{MAT} $X$ such that
+$A X = B$ if one such exists, and \kbd{NULL} otherwise.
+
+\fun{GEN}{RgM_Hadamard}{GEN a} returns a upper bound for the absolute
+value of $\text{det}(a)$. The bound is a \typ{INT}.
+
+\fun{GEN}{RgM_solve}{GEN a, GEN b} returns $a^{-1}b$ where $a$ is a square
+\typ{MAT} and $b$ is a \typ{COL} or \typ{MAT}. Returns \kbd{NULL} if $a^{-1}$
+cannot be computed, see \tet{RgM_inv}.
+
+If $b = \kbd{NULL}$, the matrix $a$ need no longer be square, and we strive
+to return a left inverse for $a$ (\kbd{NULL} if it does not exist).
+
+\fun{GEN}{RgM_solve_realimag}{GEN M, GEN b} $M$ being a \typ{MAT}
+with $r_1+r_2$ rows and $r_1+2r_2$ columns, $y$ a \typ{COL} or \typ{MAT}
+such that the equation $Mx = y$ makes sense, returns $x$ under the following
+simplifying assumptions: the first $r_1$ rows of $M$ and $y$ are real
+(the $r_2$ others are complex), and $x$ is real. This is stabler and faster
+than calling $\kbd{RgM\_solve}(M, b)$ over $\C$. In most applications,
+$M$ approximates the complex embeddings of an integer basis in a number
+field, and $x$ is actually rational.
+
+\fun{GEN}{split_realimag}{GEN x, long r1, long r2} $x$ is a \typ{COL} or
+\typ{MAT} with $r_1 + r_2$ rows, whose first $r_1$ rows have real entries
+(the $r_2$ others are complex). Return an object of the same type as
+$x$ and $r_1 + 2r_2$ rows, such that the first $r_1 + r_2$ rows contain
+the real part of $x$, and the $r_2$ following ones contain the imaginary part
+of the last $r_2$ rows of $x$. Called by \tet{RgM_solve_realimag}.
+
+\fun{GEN}{RgM_det_triangular}{GEN x} returns the product of the diagonal
+entries of $x$ (its determinant if it is indeed triangular).
+
+\fun{GEN}{Frobeniusform}{GEN V, long n} given the vector $V$ of elementary
+divisors for $M - x\text{Id}$, where $M$ is an $n\times n$ square matrix.
+Returns the Frobenius form of $M$. Used by \kbd{matfrobenius}.
+
+\fun{int}{RgM_QR_init}{GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec}
+QR-decomposition of a square invertible \typ{MAT} $x$ with real coefficients.
+Sets \kbd{*pB} to the vector of squared lengths of the $x[i]$, \kbd{*pL} to
+the Gram-Schmidt coefficients and \kbd{*pQ} to a vector of successive
+Householder transforms. If $R$ denotes the transpose of $L$ and $Q$ is the
+result of applying \kbd{*pQ} to the identity matrix, then $x = QR$ is the QR
+decomposition of $x$. Returns $0$ is $x$ is not invertible or we hit a
+precision problem, and $1$ otherwise.
+
+\fun{int}{QR_init}{GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec} as
+\kbd{RgM\_QR\_init}, assuming further that $x$ has \typ{INT} or \typ{REAL}
+coefficients.
+
+\fun{GEN}{R_from_QR}{GEN x, long prec} assuming that $x$ is a square
+invertible \typ{MAT} with \typ{INT} or \typ{REAL} coefficients, return
+the upper triangular $R$ from the $QR$ docomposition of $x$. Not memory
+clean. If the matrix is not known to have \typ{INT} or \typ{REAL}
+coefficients, apply \tet{RgM_gtomp} first.
+
+\fun{GEN}{gaussred_from_QR}{GEN x, long prec} assuming that $x$ is a square
+invertible \typ{MAT} with \typ{INT} or \typ{REAL} coefficients, returns
+\kbd{qfgaussred(x\til * x)}; this is essentially the upper triangular $R$
+matrix from the $QR$ decomposition of $x$, renormalized to accomodate
+\kbd{qfgaussred} conventions. Not memory clean.
+
+\subsec{Blackbox linear algebra}
+
+A sparse column \kbd{zCs} $v$ is a \typ{COL} with two components $C$ and $E$
+which are \typ{VECSMALL} of the same length, representing $\sum_i
+E[i]*e_{C[i]}$, where $(e_j)$ is the canonical basis. A sparse matrix
+(\kbd{zMs}) is a \typ{VEC} of \kbd{zCs}.
+
+\kbd{FpCs} and \kbd{FpMs} are identical to the above, but $E[i]$ is now
+interpreted as a \emph{signed} C long integer representing an element of
+$\F_p$. This is important since $p$ can be so large that $p+E[i]$ would not
+fit in a C long.
+
+\kbd{RgCs} and \kbd{RgMs} are similar, except that the type of the components
+of $E$ is now unspecified. Functions handling those later objects
+must not depend on the type of those components.
+
+It is not possible to derive the space dimension (number of rows) from the
+above data. Thus most functions take an argument \kbd{nbrow} which is the
+number of rows of the corresponding column/matrix in dense representation.
+
+\fun{GEN}{zCs_to_ZC}{GEN C, long nbrow} convert the sparse vector $C$
+to a dense \kbd{ZC} of dimension \kbd{nbrow}.
+
+\fun{GEN}{zMs_to_ZM}{GEN M, long nbrow} convert the sparse matrix $M$
+to a dense \kbd{ZM} whose columns have dimension \kbd{nbrow}.
+
+\fun{GEN}{FpMs_FpC_mul}{GEN M, GEN B, GEN p} multiply the sparse matrix $M$
+(over $\F_p$) by the sparse vector $B$. The result is an \kbd{FpC}, i.e.~a
+dense vector.
+
+\fun{GEN}{zMs_ZC_mul}{GEN M, GEN B, GEN p} multiply the sparse matrix $M$
+by the sparse vector $B$ (over $\Z$). The result is an \kbd{ZC}, i.e.~a
+dense vector.
+
+\fun{GEN}{FpV_FpMs_mul}{GEN B, GEN M, GEN p} multiply the sparse vector $B$
+by the sparse matrix $M$ (over $\F_p$). The result is an \kbd{FpV}, i.e.~a
+dense vector.
+
+\fun{GEN}{ZV_zMs_mul}{GEN B, GEN M, GEN p} multiply the sparse vector $B$ (over
+$\Z$) by the sparse matrix $M$. The result is an \kbd{ZV}, i.e.~a
+dense vector.
+
+\fun{void}{RgMs_structelim}{GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row}
+$M$ being a RgMs with \kbd{nbrow} rows, $A$ being a list of row indices,
+Perform structured elimination on $M$ by removing some rows and columns until
+the number of effectively present rows is equal to the number of columns.
+the result is stored in two \typ{VECSMALL}s, \kbd{*p\_col} and \kbd{*p\_row}:
+\kbd{*p\_col} is a map from the new columns indices to the old one.
+\kbd{*p\_row} is a map from the old rows indices to the new one ($0$ if removed).
+
+\fun{GEN}{FpMs_leftkernel_elt}{GEN M, long nbrow, GEN p}
+$M$ being a sparse matrix over $\F_p$, return a non-zero kbd{FpV} $X$ such
+that $X\*M$ components are almost all $0$.
+
+\fun{GEN}{FpMs_FpCs_solve}{GEN M, GEN B, long nbrow, GEN p}
+solve the equation $M\*X = B$, where $M$ is a sparse matrix and $B$ is a sparse
+vector, both over $\F_p$. Return either a solution as a \typ{COL} (dense
+vector), the index of a column which is linearly dependent from the
+others as a \typ{VECSMALL} with a single component, or \kbd{NULL}
+(can happen if $B$ is not in the image of $M$).
+
+\fun{GEN}{FpMs_FpCs_solve_safe}{GEN M, GEN B, long nbrow, GEN p}
+as above, but in the event that $p$ is not a prime and an impossible division
+occurs, return \kbd{NULL}.
+
+\fun{GEN}{ZpMs_ZpCs_solve}{GEN M, GEN B, long nbrow, GEN p, long e}
+solve the equation $MX = B$, where $M$ is a sparse matrix and $B$ is a sparse
+vector, both over $\Z/p^e\Z$. Return either a solution as a \typ{COL} (dense
+vector), or the index of a column which is linearly dependent from the
+others as a \typ{VECSMALL} with a single component.
+
+\fun{GEN}{gen_FpM_Wiedemann}{void *E, GEN (*f)(void*, GEN), GEN B, GEN p}
+solve the equation $f(X) = B$ over $\F_p$, where $B$ is a \kbd{FpV}, and $f$
+is a blackbox endomorphism, where $f(E, X)$ computes the value of $f$ at the
+(dense) column vector $X$. Returns either a solution \typ{COL}, or a kernel
+vector as a \typ{VEC}.
+
+\fun{GEN}{gen_ZpM_Dixon}{void *E, GEN (*f)(void*, GEN), GEN B, GEN p, long e}
+solve equation $f(X) = B$ over $\Z/p^e\Z$, where $B$ is a \kbd{ZV}, and $f$ is a
+blackbox endomorphism, where $f(E, X)$ computes the value of $f$ at the
+(dense) column vector $X$. Returns either a solution \typ{COL}, or a kernel
+vector as a \typ{VEC}.
+
+\subsec{Obsolete functions}
+
+The functions in this section are kept for backward compatibility only
+and will eventually disappear.
+
+\fun{GEN}{image2}{GEN x} compute the image of $x$ using a very slow
+algorithm. Use \tet{image} instead.
+
+\section{Integral, rational and generic polynomial arithmetic}
+
+\subsec{\kbd{ZX}}
+
+\fun{void}{RgX_check_ZX}{GEN x, const char *s} Assuming \kbd{x} is a \typ{POL}
+raise an error if it is not a \kbd{ZX} ($s$ should point to the name of the
+caller).
+
+\fun{GEN}{ZX_copy}{GEN x,GEN p} returns a copy of \kbd{x}.
+
+\fun{long}{ZX_max_lg}{GEN x} returns the effective length of the longest
+component in $x$.
+
+\fun{GEN}{scalar_ZX}{GEN x, long v} returns the constant \kbd{ZX} in variable
+$v$ equal to the \typ{INT} $x$.
+
+\fun{GEN}{scalar_ZX_shallow}{GEN x, long v} returns the constant \kbd{ZX} in
+variable $v$ equal to the \typ{INT} $x$. Shallow function not suitable for
+\kbd{gerepile} and friends.
+
+\fun{GEN}{ZX_renormalize}{GEN x, long l}, as \kbd{normalizepol}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{int}{ZX_equal}{GEN x, GEN y} returns $1$ if the two \kbd{ZX} have
+the same \kbd{degpol} and their coefficients are equal. Variable numbers are
+not checked.
+
+\fun{int}{ZX_equal1}{GEN x} returns $1$ if the \kbd{ZX} is equal to $1$
+and $0$ otherwise.
+
+\fun{GEN}{ZX_add}{GEN x,GEN y} adds \kbd{x} and \kbd{y}.
+
+\fun{GEN}{ZX_sub}{GEN x,GEN y} subtracts \kbd{x} and \kbd{y}.
+
+\fun{GEN}{ZX_neg}{GEN x,GEN p} returns $-\kbd{x}$.
+
+\fun{GEN}{ZX_Z_add}{GEN x,GEN y} adds the integer \kbd{y} to the
+\kbd{ZX}~\kbd{x}.
+
+\fun{GEN}{ZX_Z_add_shallow}{GEN x,GEN y} shallow version of \tet{ZX_Z_add}.
+
+\fun{GEN}{ZX_Z_sub}{GEN x,GEN y} subtracts the integer \kbd{y} to the
+\kbd{ZX}~\kbd{x}.
+
+\fun{GEN}{Z_ZX_sub}{GEN x,GEN y} subtracts the \kbd{ZX} \kbd{y} to the
+integer \kbd{x}.
+
+\fun{GEN}{ZX_Z_mul}{GEN x,GEN y} multiplies the \kbd{ZX} \kbd{x} by the
+integer \kbd{y}.
+
+\fun{GEN}{ZX_mulu}{GEN x, ulong y} multiplies \kbd{x} by the integer \kbd{y}.
+
+\fun{GEN}{ZX_shifti}{GEN x, long n} shifts all coefficients of \kbd{x} by $n$
+bits, which can be negative.
+
+\fun{GEN}{ZX_Z_divexact}{GEN x, GEN y} returns $x/y$ assuming all divisions
+are exact.
+
+\fun{GEN}{ZX_remi2n}{GEN x, long n} reduces all coefficients of \kbd{x} to
+$n$ bits, using \tet{remi2n}.
+
+\fun{GEN}{ZX_mul}{GEN x,GEN y} multiplies \kbd{x} and \kbd{y}.
+
+\fun{GEN}{ZX_sqr}{GEN x,GEN p} returns $\kbd{x}^2$.
+
+\fun{GEN}{ZX_mulspec}{GEN a, GEN b, long na, long nb}. Internal routine:
+\kbd{a} and \kbd{b} are arrays of coefficients representing polynomials
+$\sum_{i = 0}^{\kbd{na-1}} \kbd{a}[i] X^i$ and
+$\sum_{i = 0}^{\kbd{nb-1}} \kbd{b}[i] X^i$. Returns their product (as a true
+\kbd{GEN}).
+
+\fun{GEN}{ZX_sqrspec}{GEN a, long na}. Internal routine:
+\kbd{a} is an array of coefficients representing polynomial
+$\sum_{i = 0}^{\kbd{na-1}} \kbd{a}[i] X^i$. Return its square (as a true
+\kbd{GEN}).
+
+\fun{GEN}{ZX_rem}{GEN x, GEN y} returns the remainder of the Euclidean
+division of $x$ mod $y$. Assume that $x$, $y$ are two \kbd{ZX} and that
+$y$ is monic.
+
+\fun{GEN}{ZX_mod_Xnm1}{GEN T, ulong n} return $T$ modulo $X^n - 1)$. Shallow
+function.
+
+\fun{GEN}{ZX_gcd}{GEN x,GEN y} returns a gcd of the \kbd{ZX} $x$ and $y$.
+Not memory-clean, but suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{ZX_gcd_all}{GEN x, GEN y, GEN *pX}. returns a gcd $d$ of $x$ and
+$y$. If \kbd{pX} is not \kbd{NULL}, set $\kbd{*pX}$ to a (non-zero) integer
+multiple of $x/d$. If $x$ and $y$ are both monic, then $d$ is monic and
+\kbd{*pX} is exactly $x/d$. Not memory clean if the gcd is $1$
+(in that case \kbd{*pX} is set to $x$).
+
+\fun{GEN}{ZX_content}{GEN x} returns the content of the \kbd{ZX} $x$.
+
+\fun{long}{ZX_val}{GEN P} as \kbd{RgX\_val}, but assumes \kbd{P} has \typ{INT}
+coefficients.
+
+\fun{long}{ZX_valrem}{GEN P, GEN *z} as \kbd{RgX\_valrem}, but assumes
+\kbd{P} has \typ{INT} coefficients.
+
+\fun{GEN}{ZX_to_monic}{GEN q GEN *L} given $q$ a non-zero \kbd{ZX},
+returns a monic integral polynomial $Q$ such that $Q(x) = C q(x/L)$, for some
+rational $C$ and positive integer $L > 0$. If $\kbd{L}$ is not \kbd{NULL},
+set \kbd{*L} to $L$; if $L = 1$, \kbd{*L} is set to \kbd{gen\_1}. Not
+suitable for gerepileupto.
+
+\fun{GEN}{ZX_primitive_to_monic}{GEN q, GEN *L} as \tet{ZX_to_monic} except
+$q$ is assumed to have trivial content, which avoids recomputing it.
+The result is suboptimal if $q$ is not primitive ($L$ larger than
+necessary), but remains correct.
+
+\fun{GEN}{ZX_Z_normalize}{GEN q, GEN *L} a restricted version of
+\kbd{ZX\_primitive\_to\_monic}, where $q$ is a \emph{monic} \kbd{ZX}
+of degree $> 0$. Finds the largest integer $L > 0$ such that
+$Q(X) := L^{-\deg q} q(Lx)$ is integral and return $Q$; this is not
+well-defined if $q$ is a monomial, in that case, set $L=1$ and $Q = q$. If
+\kbd{L} is not \kbd{NULL}, set \kbd{*L} to $L$.
+
+\fun{GEN}{ZX_Q_normalize}{GEN q, GEN *L} a variant of \tet{ZX_Z_normalize}
+where $L > 0$ is allowed to be rational, the monic $Q\in \Z[X]$ has possibly
+smaller coefficients.
+
+\fun{GEN}{ZX_rescale}{GEN P, GEN h} returns $h^{\deg(P)} P(x/h)$.
+\kbd{P} is a \kbd{ZX} and \kbd{h} is a non-zero integer. Neither memory-clean
+nor suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{ZX_rescale_lt}{GEN P} returns the monic integral polynomial
+$h^{\deg(P)-1} P(x/h)$, where \kbd{P} is a non-zero \kbd{ZX} and \kbd{h} is
+its leading coefficient. Neither memory-clean nor suitable for
+\kbd{gerepileupto}.
+
+\fun{GEN}{ZX_translate}{GEN P, GEN c} assume $P$ is a \kbd{ZX} and $c$ an
+integer. Returns $P(X + c)$ (optimized for $c = \pm 1$).
+
+\fun{GEN}{ZX_unscale}{GEN P, GEN h} given a \kbd{ZX} $P$ and a \typ{INT} $h$,
+returns $P(hx)$. Not memory clean.
+
+\fun{GEN}{ZX_unscale_div}{GEN P, GEN h} given a \kbd{ZX} $P$ and a \typ{INT} $h$
+such that $h \mid P(0)$, returns $P(hx)/h$. Not memory clean.
+
+\fun{GEN}{ZX_eval1}{GEN P} returns the integer $P(1)$.
+
+\fun{GEN}{ZX_graeffe}{GEN p} returns the Graeffe transform of $p$, i.e. the
+\kbd{ZX} $q$ such that $p(x)p(-x) = q(x^2)$.
+
+\fun{GEN}{ZX_deriv}{GEN x} returns the derivative of \kbd{x}.
+
+\fun{GEN}{ZX_resultant}{GEN A, GEN B} returns the resultant of the
+\kbd{ZX}~\kbd{A} and \kbd{B}.
+
+\fun{GEN}{ZX_disc}{GEN T} returns the discriminant of the \kbd{ZX}
+\kbd{T}.
+
+\fun{GEN}{ZX_factor}{GEN T} returns the factorization of the primitive part
+of \kbd{T} over $\Q[X]$ (the content is lost).
+
+\fun{int}{ZX_is_squarefree}{GEN T} returns $1$ if the
+\kbd{ZX}~\kbd{T} is squarefree, $0$ otherwise.
+
+\fun{long}{ZX_is_irred}{GEN T} returns 1 it \kbd{T} is irreducible, and
+0 otherwise.
+
+\fun{GEN}{ZX_squff}{GEN T, GEN *E} write $T$ as a product $\prod T_i^{e_i}$
+with the $e_1 < e_2 < \cdots$ all distinct and the $T_i$ pairwise coprime.
+Return the vector of the $T_i$, and set \kbd{*E} to the vector of the $e_i$,
+as a \typ{VECSMALL}.
+
+\subsec{\kbd{ZXQ}}
+
+\fun{GEN}{ZXQ_mul}{GEN x,GEN y,GEN T} returns $x*y$ mod $T$, assuming
+that all inputs are \kbd{ZX}s and that $T$ is monic.
+
+\fun{GEN}{ZXQ_sqr}{GEN x,GEN T} returns $x^2$ mod $T$, assuming
+that all inputs are \kbd{ZX}s and that $T$ is monic.
+
+\fun{GEN}{ZXQ_charpoly}{GEN A, GEN T, long v}: let \kbd{T} and \kbd{A} be
+\kbd{ZX}s, returns the characteristic polynomial of \kbd{Mod(A, T)}.
+More generally, \kbd{A} is allowed to be a \kbd{QX}, hence possibly has
+rational coefficients, \emph{assuming} the result is a \kbd{ZX}, i.e.~the
+algebraic number \kbd{Mod(A,T)} is integral over \kbd{Z}.
+
+\fun{GEN}{ZX_ZXY_resultant}{GEN A, GEN B}
+under the assumption that \kbd{A} in $\Z[Y]$, \kbd{B} in $\Q[Y][X]$, and
+$R = \text{Res}_Y(A, B) \in \Z[X]$, returns the resultant $R$.
+
+\fun{GEN}{ZX_compositum_disjoint}{GEN A, GEN B} given two irreducible \kbd{ZX}
+defining linearly disjoint extensions, returns a \kbd{ZX} defining their
+compositum.
+
+\fun{GEN}{ZX_ZXY_rnfequation}{GEN A, GEN B, long *lambda},
+assume \kbd{A} in $\Z[Y]$, \kbd{B} in $\Q[Y][X]$, and $R =
+\text{Res}_Y(A, B) \in \Z[X]$. If \kbd{lambda = NULL}, returns $R$
+as in \kbd{ZY\_ZXY\_resultant}. Otherwise, \kbd{lambda} must point to
+some integer, e.g. $0$ which is used as a seed. The function then finds a
+small $\lambda \in \Z$ (starting from \kbd{*lambda}) such that
+$R_\lambda(X) := \text{Res}_Y(A, B(X + \lambda Y))$ is squarefree, resets
+\kbd{*lambda} to the chosen value and returns $R_{\lambda}$.
+
+\subsec{\kbd{ZXV}}
+
+\fun{GEN}{ZXV_equal}{GEN x,GEN y} returns $1$ if the two vectors of \kbd{ZX}
+are equal, as per \tet{ZX_equal} (variables are not checked to be equal) and
+$0$ otherwise.
+
+\fun{GEN}{ZXV_Z_mul}{GEN x,GEN y} multiplies the vector of \kbd{ZX} \kbd{x}
+by the integer \kbd{y}.
+
+\fun{GEN}{ZXV_remi2n}{GEN x, long n} applies \kbd{ZX\_remi2n} to all
+coefficients of \kbd{x}.
+
+\fun{GEN}{ZXV_dotproduct}{GEN x,GEN y} as \kbd{RgV\_dotproduct} assuming $x$
+and $y$ have \kbd{ZX} entries.
+
+\subsec{\kbd{ZXT}}
+
+\fun{GEN}{ZXT_remi2n}{GEN x, long n} applies \kbd{ZX\_remi2n} to all
+leaves of the tree \kbd{x}.
+
+\subsec{\kbd{ZXX}}
+
+\fun{void}{RgX_check_ZXX}{GEN x, const char *s} Assuming \kbd{x} is a \typ{POL}
+raise an error if it one of its coefficients is not an integer or a \kbd{ZX}
+($s$ should point to the name of the caller).
+
+\fun{GEN}{ZXX_renormalize}{GEN x, long l}, as \kbd{normalizepol}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{long}{ZXX_max_lg}{GEN x} returns the effective length of the longest
+component in $x$; assume all coefficients are \typ{INT} or \kbd{ZX}s.
+
+\fun{GEN}{ZXX_Z_divexact}{GEN x, GEN y} returns $x/y$ assuming all integer
+divisions are exact.
+
+\fun{GEN}{ZXX_to_Kronecker}{GEN P, long n} Assuming $P(X,Y)$ is a polynomial
+of degree in $X$ strictly less than $n$, returns $P(X,X^{2*n-1})$, the
+Kronecker form of $P$. Shallow function.
+
+\fun{GEN}{ZXX_to_Kronecker_spec}{GEN Q, long lQ, long n} return
+\tet{ZXX_to_Kronecker}$(P, n)$, where $P$ is the polynomial
+$\sum_{i = 0}^{\kbd{lQ} - 1} Q[i] x^i$. To be used when splitting
+the coefficients of genuine polynomials into blocks. Shallow function.
+
+\fun{GEN}{Kronecker_to_ZXX}{GEN z, long n, long v} recover $P(X,Y)$
+from its Kronecker form $P(X,X^{2*n-1})$, $v$ is the variable number
+corresponding to $Y$. Shallow function.
+
+\fun{GEN}{ZXX_mul_Kronecker}{GEN P, GEN Q, long n} return \tet{ZX_mul}
+applied to the Kronecker forms $P(X,X^{2n-1})$ and $Q(X,X^{2n-1}$
+of $P$ and $Q$. Not memory clean.
+
+\subsec{\kbd{QX}}
+
+\fun{void}{RgX_check_QX}{GEN x, const char *s} Assuming \kbd{x} is a \typ{POL}
+raise an error if it is not a \kbd{QX} ($s$ should point to the name of the
+caller).
+
+\fun{GEN}{QX_gcd}{GEN x,GEN y} returns a gcd of the \kbd{QX} $x$ and $y$.
+
+\fun{GEN}{QX_disc}{GEN T} returns the discriminant of the \kbd{QX}
+\kbd{T}.
+
+\fun{GEN}{QX_factor}{GEN T} as \kbd{ZX\_factor}.
+
+\fun{GEN}{QX_resultant}{GEN A, GEN B} returns the resultant of the
+\kbd{QX}~\kbd{A} and \kbd{B}.
+
+\fun{GEN}{QX_complex_roots}{GEN p, long l} returns the complex roots of the
+\kbd{QX} $p$ at accuracy $l$, where real roots are returned as \typ{REAL}s.
+More efficient when $p$ is irreducible and primitive. Special case
+of \tet{cleanroots}.
+
+\subsec{\kbd{QXQ}}
+
+\fun{GEN}{QXQ_norm}{GEN A, GEN B} $A$ being a \kbd{QX} and $B$ being a
+\kbd{ZX}, returns the norm of the algebraic number $A \mod B$, using a
+modular algorithm. To ensure that $B$ is a \kbd{ZX}, one may replace it by
+\kbd{Q\_primpart(B)}, which of course does not change the norm.
+
+If $A$ is not a \kbd{ZX} --- it has a denominator ---, but the result is
+nevertheless known to be an integer, it is much more efficient to call
+\tet{QXQ_intnorm} instead.
+
+\fun{GEN}{QXQ_intnorm}{GEN A, GEN B} $A$ being a \kbd{QX} and $B$
+being a \kbd{ZX}, returns the norm of the algebraic number $A \mod B$,
+\emph{assuming} that the result is an integer, which is for instance the case
+is $A\mod B$ is an algebraic integer, in particular if $A$ is a \kbd{ZX}. To
+ensure that $B$ is a \kbd{ZX}, one may replace it by \kbd{Q\_primpart(B)}
+(which of course does not change the norm).
+
+If the result is not known to be an integer, you must use \tet{QXQ_norm}
+instead, which is slower.
+
+\fun{GEN}{QXQ_inv}{GEN A, GEN B} returns the inverse of $A$ modulo $B$
+where $A$ is a \kbd{QX} and $B$ is a \kbd{ZX}. Should you need this for
+a \kbd{QX} $B$, just use
+\bprog
+  QXQ_inv(A, Q_primpart(B));
+ at eprog\noindent But in all cases where modular arithmetic modulo $B$ is
+desired, it is much more efficient to replace $B$ by \kbd{Q\_primpart$(B)$}
+once and for all.
+
+\fun{GEN}{QXQ_powers}{GEN x, long n, GEN T} returns $[\kbd{x}^0, \dots,
+\kbd{x}^\kbd{n}]$ as \kbd{RgXQ\_powers} would, but in a more efficient way when
+$x$ has a huge integer denominator (we start by removing that denominator).
+Meant to be used to precompute powers of algebraic integers in $\Q[t]/(T)$.
+The current implementation does not require $x$ to be a \kbd{QX}: any
+polynomial to which \kbd{Q\_remove\_denom} can be applied is fine.
+
+\fun{GEN}{QXQ_reverse}{GEN f, GEN T} as \kbd{RgXQ\_reverse}, assuming $f$
+is a \kbd{QX}.
+
+\fun{GEN}{QX_ZXQV_eval}{GEN f, GEN nV, GEN dV} as \kbd{RgX\_RgXQV\_eval},
+except that $f$ is assumed to be a \kbd{QX}, $V$ is given implicitly
+by a numerator \kbd{nV} (\kbd{ZV}) and denominator \kbd{dV} (a positive
+\typ{INT} or \kbd{NULL} for trivial denominator). Not memory clean, but
+suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{QXV_QXQ_eval}{GEN v, GEN a, GEN T} $v$ is a vector of \kbd{QX}s
+(possibly scalars, i.e.~rational numbers, for convenience), $a$ and $T$ both
+\kbd{QX}. Return the vector of evaluations at $a$ modulo $T$.
+Not memory clean, nor suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{QXX_QXQ_eval}{GEN P, GEN a, GEN T} $P(X,Y)$ is a \typ{POL} with
+\kbd{QX} coefficients (possibly scalars, i.e.~rational numbers, for
+convenience) , $a$ and $T$ both \kbd{QX}. Return the \kbd{QX} $P(X, a \mod
+T)$. Not memory clean, nor suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{nfgcd}{GEN P, GEN Q, GEN T, GEN den} given $P$ and $Q$ in
+$\Z[X,Y]$, $T$ monic irreducible in $\Z[Y]$, returns the primitive $d$ in
+$\Z[X,Y]$ which is a gcd of $P$, $Q$ in $K[X]$, where $K$ is the number field
+$\Q[Y]/(T)$. If not \kbd{NULL}, \kbd{den} is a multiple of the integral
+denominator of the (monic) gcd of $P,Q$ in $K[X]$.
+
+\fun{GEN}{nfgcd_all}{GEN P, GEN Q, GEN T, GEN den, GEN *Pnew} as \kbd{nfgcd}.
+If \kbd{Pnew} is not \kbd{NULL}, set \kbd{*Pnew} to a non-zero integer
+multiple of $P/d$. If $P$ and $Q$ are both monic, then $d$ is monic and
+\kbd{*Pnew} is exactly $P/d$. Not memory clean if the gcd is $1$
+(in that case \kbd{*Pnew} is set to $P$).
+
+\subsec{\kbd{zx}}
+
+\fun{GEN}{zero_zx}{long sv} returns a zero \kbd{zx} in variable $v$.
+
+\fun{GEN}{polx_zx}{long sv} returns the variable $v$ as degree~1~\kbd{Flx}.
+
+\fun{GEN}{zx_renormalize}{GEN x, long l}, as \kbd{Flx\_renormalize}, where
+$\kbd{l} = \kbd{lg(x)}$, in place.
+
+\fun{GEN}{zx_shift}{GEN T, long n} returns \kbd{T}
+multiplied by $\kbd{x}^n$, assuming $n\geq 0$.
+
+\subsec{\kbd{RgX}}
+
+\fun{long}{RgX_type}{GEN x, GEN *ptp, GEN *ptpol, long *ptprec} returns
+the ``natural'' base ring over which the polynomial $x$ is defined. Raise an
+error if it detects consistency problems in modular objects: incompatible rings
+(e.g. $\F_p$ and $\F_q$ for primes $p\neq q$, $\F_p[X]/(T)$ and $\F_p[X]/(U)$
+for $T\neq U$). Minor discrepancies are supported if they make general sense
+(e.g. $\F_p$ and $\F_{p^k}$, but not $\F_p$ and $\Q_p$); \typ{FFELT} and
+\typ{POLMOD} of \typ{INTMOD}s are considered inconsistent, even if they define
+the same field: if you need to use simultaneously these different finite
+field implementations, multiply the polynomial by a \typ{FFELT} equal to $1$
+first.
+
+\item 0: none of the others (presumably multivariate, possibly inconsistent).
+
+\item \typ{INT}: defined over $\Q$ (not necessarily $\Z$).
+
+\item \typ{INTMOD}: defined over $\Z/p\Z$, where \kbd{*ptp} is set to $p$.
+It is not checked whether $p$ is prime.
+
+\item \typ{COMPLEX}: defined over $\C$ (at least one \typ{COMPLEX} with at
+least one inexact floating point \typ{REAL} component). Set \kbd{*ptprec}
+to the minimal accuracy (as per \kbd{precision}) of inexact components.
+
+\item \typ{REAL}: defined over $\R$ (at least one inexact floating point
+\typ{REAL} component). Set \kbd{*ptprec} to the minimal accuracy (as per
+\kbd{precision}) of inexact components.
+
+\item \typ{PADIC}: defined over $\Q_p$, where \kbd{*ptp} is set to $p$ and
+\kbd{*ptprec} to the $p$-adic accuracy.
+
+\item \typ{FFELT}: defined over a finite field $\F_{p^k}$, where \kbd{*ptp}
+is set to the field characteristic $p$ and \kbd{*ptpol} is set to a
+\typ{FFELT} belonging to the field.
+
+\item other values are composite corresponding to quotients $R[X]/(T)$, with
+one primary type \kbd{t1}, describing the form of the quotient,
+and a secondary type \kbd{t2}, describing $R$. If \kbd{t} is the
+\kbd{RgX\_type}, \kbd{t1} and \kbd{t2} are recovered using
+
+\fun{void}{RgX_type_decode}{long t, long *t1, long *t2}
+
+\kbd{t1} is one of
+
+\typ{POLMOD}: at least one \typ{POLMOD} component,
+set \kbd{*ppol} to the modulus,
+
+\typ{QUAD}: no \typ{POLMOD}, at least one \typ{QUAD} component,
+set \kbd{*ppol} to the modulus (\kbd{$-$.pol}) of the \typ{QUAD},
+
+\typ{COMPLEX}: no \typ{POLMOD} or \typ{QUAD}, at least one \typ{COMPLEX}
+component, set \kbd{*ppol} to $y^2 + 1$.
+
+and the underlying base ring $R$ is given by \kbd{t2}, which
+is one of \typ{INT}, \typ{INTMOD} (set \kbd{*ptp}) or \typ{PADIC}
+(set \kbd{*ptp} and \kbd{*ptprec}), with the same meaning
+as above.
+
+\fun{int}{RgX_type_is_composite}{long t} $t$ as returned by \kbd{RgX\_type},
+return 1 if $t$ is a composite type, and 0 otherwise.
+
+\fun{GEN}{RgX_get_0}{GEN x} returns $0$ in the base ring over which $x$
+is defined, to the proper accuracy (e.g. \kbd{0}, \kbd{Mod(0,3)},
+\kbd{O(5\pow 10)}).
+
+\fun{GEN}{RgX_get_1}{GEN x} returns $1$ in the base ring over which $x$
+is defined, to the proper accuracy (e.g. \kbd{0}, \kbd{Mod(0,3)},
+
+\fun{int}{RgX_isscalar}{GEN x} return 1 if $x$ all the coefficients of
+$x$ of degree $> 0$ are $0$ (as per \kbd{gequal0}).
+
+\fun{int}{RgX_blocks}{GEN P, long n, long m} writes
+$P(X)=a_0(X)+X^n*a_1(X)*X^n+\ldots+X^{n*(m-1)}\*a_{m-1}(X)$,
+where the $a_i$ are polynomial of degree at most $n-1$
+(except possibly for the last one) and returns
+$[a_0(X),a_1(X),\ldots,a_{m-1}(X)]$.  This is a shallow function.
+
+\fun{void}{RgX_even_odd}{GEN p, GEN *pe, GEN *po} write $p(X) = E(X^2) +
+X O(X^2)$ and set \kbd{*pe = E}, \kbd{*po = O}.
+
+\fun{GEN}{RgX_splitting}{GEN P, long k} write
+$P(X)=a_0(X^k)+X\*a_1(X^k)+\ldots+X^{k-1}\*a_{k-1}(X^k)$ and return
+$[a_0(X),a_1(X),\ldots,a_{k-1}(X)]$.  This is a shallow function.
+
+\fun{GEN}{RgX_copy}{GEN x} returns (a deep copy of) $\kbd{x}$.
+
+\fun{GEN}{RgX_add}{GEN x,GEN y} adds \kbd{x} and \kbd{y}.
+
+\fun{GEN}{RgX_sub}{GEN x,GEN y} subtracts \kbd{x} and \kbd{y}.
+
+\fun{GEN}{RgX_neg}{GEN x} returns $-\kbd{x}$.
+
+\fun{GEN}{RgX_Rg_add}{GEN y, GEN x} returns $x+y$.
+
+\fun{GEN}{RgX_Rg_add_shallow}{GEN y, GEN x} returns $x+y$; shallow function.
+
+\fun{GEN}{Rg_RgX_sub}{GEN x, GEN y}
+
+\fun{GEN}{RgX_Rg_sub}{GEN y, GEN x} returns $x-y$
+
+\fun{GEN}{RgX_mul}{GEN x, GEN y} multiplies the two \typ{POL} (in the same
+variable) \kbd{x} and \kbd{y}. Uses Karatsuba algorithm.
+
+\fun{GEN}{RgX_mul_normalized}{GEN A, long a, GEN B, long b}
+returns $(X^a + A)(X^b + B) - X^(a+b)$, where we assume that $\deg A < a$
+and $\deg B < b$ are polynomials in the same variable $X$.
+
+\fun{GEN}{RgX_mulspec}{GEN a, GEN b, long na, long nb}. Internal routine:
+\kbd{a} and \kbd{b} are arrays of coefficients representing polynomials
+$\sum_{i = 0}^{\kbd{na-1}} \kbd{a}[i] X^i$ and
+$\sum_{i = 0}^{\kbd{nb-1}} \kbd{b}[i] X^i$. Returns their product (as a true
+\kbd{GEN}).
+
+\fun{GEN}{RgX_mullow}{GEN a, GEN b, long n} returns $a b$ modulo $X^n$,
+where $a,b$ are two \typ{POL} in the same variable $X$ and $n \geq 0$. Uses
+Karatsuba algorithm (Mulders, Hanrot-Zimmermann variant).
+
+\fun{GEN}{RgX_sqr}{GEN x} squares the \typ{POL} \kbd{x}. Uses Karatsuba
+algorithm.
+
+\fun{GEN}{RgX_sqrspec}{GEN a, long na}. Internal routine:
+\kbd{a} is an array of coefficients representing polynomial
+$\sum_{i = 0}^{\kbd{na-1}} \kbd{a}[i] X^i$. Return its square (as a true
+\kbd{GEN}).
+
+\fun{GEN}{RgX_sqrlow}{GEN a, long n} returns $a^2$ modulo $X^n$,
+where $a$ is a \typ{POL} in the variable $X$ and $n \geq 0$. Uses
+Karatsuba algorithm (Mulders, Hanrot-Zimmermann variant).
+
+\fun{GEN}{RgX_divrem}{GEN x, GEN y, GEN *r} by default, returns the Euclidean
+quotient and store the remainder in $r$. Three special values of $r$ change
+that behavior
+\item \kbd{NULL}: do not store the remainder, used to implement \kbd{RgX\_div},
+
+\item \tet{ONLY_REM}: return the remainder, used to implement \kbd{RgX\_rem},
+
+\item \tet{ONLY_DIVIDES}: return the quotient if the division is exact, and
+\kbd{NULL} otherwise.
+
+\fun{GEN}{RgX_div}{GEN x, GEN y}
+
+\fun{GEN}{RgX_div_by_X_x}{GEN A, GEN a, GEN *r} returns the
+quotient of the \kbd{RgX}~\kbd{A} by $(X - \kbd{a})$, and sets \kbd{r} to the
+remainder $\kbd{A}(\kbd{a})$.
+
+\fun{GEN}{RgX_rem}{GEN x, GEN y}
+
+\fun{GEN}{RgX_pseudodivrem}{GEN x, GEN y, GEN *ptr} compute a pseudo-quotient
+$q$ and pseudo-remainder $r$ such that $\kbd{lc}(y)^{\deg(x) - \deg(y) + 1}x
+= qy + r$. Return $q$ and set \kbd{*ptr} to $r$.
+
+\fun{GEN}{RgX_pseudorem}{GEN x, GEN y} return the remainder
+in the pseudo-division of $x$ by $y$.
+
+\fun{long}{RgX_degree}{GEN x, long v} $x$ being a \typ{POL} and $v \geq 0$,
+returns the degree in $v$ of $x$. Error if $x$ is not a polynomial in $v$.
+
+\fun{GEN}{RgXQX_pseudorem}{GEN x, GEN y, GEN T} return the remainder
+in the pseudo-division of $x$ by $y$ over $R[X]/(T)$.
+
+\fun{int}{ZXQX_dvd}{GEN x, GEN y, GEN T} let $T$ be a monic irreducible
+\kbd{ZX}, let $x, y$ be \typ{POL} whose coefficients are either \typ{INT}s or
+\kbd{ZX} in the same variable as $T$. Assume further that the leading
+coefficient of $y$ is an integer. Return $1$ if $y | x$ in $(\Z[Y]/(T))[X]$,
+and $0$ otherwise.
+
+\fun{GEN}{RgXQX_pseudodivrem}{GEN x, GEN y, GEN T, GEN *ptr} compute
+a pseudo-quotient $q$ and pseudo-remainder $r$ such that
+$\kbd{lc}(y)^{\deg(x) - \deg(y) + 1}x = qy + r$ in $R[X]/(T)$. Return $q$ and
+set \kbd{*ptr} to $r$.
+
+\fun{GEN}{RgX_mulXn}{GEN x, long n} returns $\kbd{x} * t^n$. This may
+be a \typ{FRAC} if $n < 0$ and the valuation of \kbd{x} is not large
+enough.
+
+\fun{GEN}{RgX_shift}{GEN x, long n} returns $\kbd{x} * t^n$ if $n\geq 0$,
+and $\kbd{x} \bs t^{-n}$ otherwise.
+
+\fun{GEN}{RgX_shift_shallow}{GEN x, long n} as \kbd{RgX\_shift}, but
+shallow (coefficients are not copied).
+
+\fun{GEN}{RgX_rotate_shallow}{GEN P, long k, long p} returns $\kbd{P} * X^k
+\pmod {X^p-1}$, assuming the degree of $P$ is strictly less than $p$, and
+$k\geq 0$.
+
+\fun{void}{RgX_shift_inplace_init}{long v} $v \geq 0$, prepare for a later
+call to \tet{RgX_shift_inplace}. Reserves $v$ words on the stack.
+
+\fun{GEN}{RgX_shift_inplace}{GEN x, long v} $v \geq 0$, assume that
+\tet{RgX_shift_inplace_init}$(v)$ has been called (reserving $v$ words on the
+stack), immediately followed by a \typ{POL} $x$. Return \kbd{RgX\_shift}$(x,v)$
+by shifting $x$ in place. To be used as follows
+\bprog
+  RgX_shift_inplace_init(v);
+  av = avma;
+  ...
+  x = gerepileupto(av, ...); /* a t_POL */
+  return RgX_shift_inplace(x, v);
+ at eprog
+
+\fun{long}{RgX_valrem}{GEN P, GEN *pz} returns the valuation $v$ of the
+\typ{POL}~\kbd{P} with respect to its main variable $X$. Check whether
+coefficients are $0$ using \kbd{gequal0}. Set \kbd{*pz} to
+$\kbd{RgX\_shift\_shallow}(P,-v)$.
+
+\fun{long}{RgX_val}{GEN P} returns the valuation $v$ of the
+\typ{POL}~\kbd{P} with respect to its main variable $X$. Check whether
+coefficients are $0$ using \kbd{gequal0}.
+
+\fun{long}{RgX_valrem_inexact}{GEN P, GEN *z} as \kbd{RgX\_valrem}, using
+\kbd{isexactzero} instead of \kbd{gequal0}.
+
+\fun{GEN}{RgX_deriv}{GEN x} returns the derivative of \kbd{x} with respect to
+its main variable.
+
+\fun{GEN}{RgX_integ}{GEN x} returns the primitive of \kbd{x} vanishing at
+$0$, with respect to its main variable.
+
+\fun{GEN}{RgX_gcd}{GEN x, GEN y} returns the GCD of \kbd{x} and \kbd{y},
+assumed to be \typ{POL}s in the same variable.
+
+\fun{GEN}{RgX_gcd_simple}{GEN x, GEN y} as \tet{RgX_gcd} using a standard
+extended Euclidean algorithm. Usually slower than \tet{RgX_gcd}.
+
+\fun{GEN}{RgX_extgcd}{GEN x, GEN y, GEN *u, GEN *v} returns
+$d = \text{GCD}(\kbd{x},\kbd{y})$, and sets \kbd{*u}, \kbd{*v} to the Bezout
+coefficients such that $\kbd{*ux} + \kbd{*vy} = d$. Uses a generic
+subresultant algorithm.
+
+\fun{GEN}{RgX_extgcd_simple}{GEN x, GEN y, GEN *u, GEN *v} as
+\tet{RgX_extgcd} using a standard extended Euclidean algorithm. Usually
+slower than \tet{RgX_extgcd}.
+
+\fun{GEN}{RgX_disc}{GEN x} returns the discriminant of the \typ{POL} \kbd{x}
+with respect to its main variable.
+
+\fun{GEN}{RgX_resultant_all}{GEN x, GEN y, GEN *sol} returns
+\kbd{resultant(x,y)}. If \kbd{sol} is not \kbd{NULL}, sets it to the last
+non-constant remainder in the polynomial remainder sequence if it exists and to
+\kbd{gen\_0} otherwise (e.g. one polynomial has degree 0). Compared to
+\kbd{resultant\_all}, this function always uses the generic subresultant
+algorithm, hence always computes \kbd{sol}.
+
+\fun{GEN}{RgX_modXn_shallow}{GEN x, long n} return $\kbd{x \% } t^n$,
+where $n\geq 0$. Shallow function.
+
+\fun{GEN}{RgX_modXn_eval}{GEN Q, GEN x, long n} special case of
+\tet{RgX_RgXQ_eval}, when the modulus is a monomial:
+returns $\kbd{Q}(\kbd{x})$ modulo $t^n$, where $x \in R[t]$.
+
+\fun{GEN}{RgX_renormalize}{GEN x} remove leading terms in \kbd{x} which are
+equal to (necessarily inexact) zeros.
+
+\fun{GEN}{RgX_renormalize_lg}{GEN x, long lx} as \kbd{setlg(x, lx)}
+followed by \kbd{RgX\_renormalize(x)}. Assumes that $\kbd{lx} \leq
+\kbd{lg(x)}$.
+
+\fun{GEN}{RgX_gtofp}{GEN x, GEN prec} returns the polynomial obtained by
+applying
+\bprog
+  gtofp(gel(x,i), prec)
+ at eprog\noindent to all coefficients of $x$.
+
+\fun{GEN}{RgX_fpnorml2}{GEN x, long prec} returns (a stack-clean variant of)
+\bprog
+  gnorml2( RgX_gtofp(x, prec) )
+ at eprog
+
+\fun{GEN}{RgX_recip}{GEN P} returns the reverse of the polynomial
+$P$, i.e. $X^{\deg P} P(1/X)$.
+
+\fun{GEN}{RgX_recip_shallow}{GEN P} shallow function of \tet{RgX_recip}.
+
+\fun{GEN}{RgX_deflate}{GEN P, long d} assuming $P$ is a polynomial of the
+form $Q(X^d)$, return $Q$. Shallow function, not suitable for
+\kbd{gerepileupto}.
+
+\fun{long}{RgX_deflate_max}{GEN P, long *d} sets \kbd{d} to the largest exponent
+such that $P$ is of the form $P(x^d)$ (use \kbd{gequal0} to check
+whether coefficients are 0), $0$ if $P$ is the zero polynomial. Returns
+\kbd{RgX\_deflate(P,d)}.
+
+\fun{GEN}{RgX_inflate}{GEN P, long d} return $P(X^d)$. Shallow function, not
+suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{RgX_rescale}{GEN P, GEN h} returns $h^{\deg(P)} P(x/h)$.
+\kbd{P} is an \kbd{RgX} and \kbd{h} is non-zero. (Leaves small objects on the
+stack. Suitable but inefficient for \kbd{gerepileupto}.)
+
+\fun{GEN}{RgX_unscale}{GEN P, GEN h} returns $P(h x)$. (Leaves small objects
+on the stack. Suitable but inefficient for \kbd{gerepileupto}.)
+
+\fun{GEN}{RgXV_unscale}{GEN v, GEN h} apply \kbd{RgX\_unscale} to a vector
+of \kbd{RgX}.
+
+\fun{int}{RgX_is_rational}{GEN P} return 1 is the \kbd{RgX}~$P$ has only
+rational coefficients (\typ{INT} and \typ{FRAC}), and 0 otherwise.
+
+\fun{int}{RgX_is_QX}{GEN P} return 1 is the \kbd{RgX}~$P$ has only
+\typ{INT} and \typ{FRAC} coefficients, and 0 otherwise.
+
+\fun{int}{RgX_is_ZX}{GEN P} return 1 is the \kbd{RgX}~$P$ has only
+\typ{INT} coefficients, and 0 otherwise.
+
+\fun{int}{RgX_is_monomial}{GEN x} returns 1 (true) if \kbd{x} is a non-zero
+monomial in its main variable, 0~otherwise.
+
+\fun{long}{RgX_equal}{GEN x, GEN y} returns $1$ if the \typ{POL}s $x$ and $y$
+have the same \kbd{degpol} and their coefficients are equal (as per
+\tet{gequal}). Variable numbers are not checked. Note that this is more
+stringent than \kbd{gequal(x,y)}, which only checks whether $x - y$ satisfies
+\kbd{gequal0}; in particular, they may have different apparent degrees provided
+the extra leading terms are $0$.
+
+\fun{long}{RgX_equal_var}{GEN x, GEN y} returns $1$ if $x$ and $y$
+have the same variable number and \kbd{RgX\_equal(x,y)} is $1$.
+\smallskip
+
+\fun{GEN}{RgXQ_mul}{GEN y, GEN x, GEN T} computes $xy$ mod $T$
+
+\fun{GEN}{RgXQ_sqr}{GEN x, GEN T} computes $x^2$ mod $T$
+
+\fun{GEN}{RgXQ_inv}{GEN x, GEN T} return the inverse of $x$ mod $T$.
+
+\fun{GEN}{RgXQ_pow}{GEN x, GEN n, GEN T} computes $x^n$ mod $T$
+
+\fun{GEN}{RgXQ_powu}{GEN x, ulong n, GEN T} computes $x^n$ mod $T$,
+$n$ being an \kbd{ulong}.
+
+\fun{GEN}{RgXQ_powers}{GEN x, long n, GEN T} returns $[\kbd{x}^0,
+\dots, \kbd{x}^\kbd{n}]$ as a \typ{VEC} of \kbd{RgXQ}s.
+
+\fun{int}{RgXQ_ratlift}{GEN x, GEN T, long amax, long bmax, GEN *P, GEN *Q}
+Assuming that $\kbd{amax}+\kbd{bmax}<\deg T$, attempts to recognize $x$ as a
+rational function $a/b$, i.e. to find \typ{POL}s $P$ and $Q$ such that
+
+\item $P \equiv Q x$ modulo $T$,
+
+\item $\deg P \leq \kbd{amax}$, $\deg Q \leq \kbd{bmax}$,
+
+\item $\gcd(T,P) = \gcd(P,Q)$.
+
+\noindent If unsuccessful, the routine returns $0$ and leaves $P$, $Q$
+unchanged; otherwise it returns $1$ and sets $P$ and $Q$.
+
+\fun{GEN}{RgXQ_reverse}{GEN f, GEN T} returns a \typ{POL} $g$ of degree $< n
+= \text{deg}~T$ such that $T(x)$ divides $(g \circ f)(x) - x$, by solving a
+linear system. Low-level function underlying \tet{modreverse}: it returns a
+lift of \kbd[modreverse(f,T)]; faster than the high-level function since it
+needs not compute the characteristic polynomial of $f$ mod $T$ (often already
+known in applications). In the trivial case where $n \leq 1$, returns a
+scalar, not a constant \typ{POL}.
+
+\fun{GEN}{RgXQ_matrix_pow}{GEN y, long n, long m, GEN P} returns
+\kbd{RgXQ\_powers(y,m-1,P)}, as a matrix of dimension $n \geq \deg P$.
+
+\fun{GEN}{RgXQ_norm}{GEN x, GEN T} returns the norm of \kbd{Mod(x, T)}.
+
+\fun{GEN}{RgXQ_charpoly}{GEN x, GEN T, long v} returns the characteristic
+polynomial of \kbd{Mod(x, T)}, in variable $v$.
+
+\fun{GEN}{RgX_RgXQ_eval}{GEN f, GEN x, GEN T} returns $\kbd{f}(\kbd{x})$ modulo
+$T$.
+
+\fun{GEN}{RgX_RgXQV_eval}{GEN f, GEN V, GEN T} as
+\kbd{RgX\_RgXQ\_eval(f, x, T)},
+assuming $V$ was output by \kbd{RgXQ\_powers(x, n, T)} for some $n\geq 1$.
+
+\fun{GEN}{RgX_translate}{GEN P, GEN c} assume $c$ is a scalar or
+a polynomials whose main variable has lower priority than the main variable
+$X$ of $P$. Returns $P(X + c)$ (optimized for $c = \pm 1$).
+
+\fun{GEN}{RgXQX_translate}{GEN P, GEN c, GEN T} assume the main variable
+$X$ of $P$ has higher priority than the main variable $Y$ of $T$ and $c$.
+Return a lift of $P(X+\text{Mod}(c(Y), T(Y)))$.
+
+\fun{GEN}{RgXQC_red}{GEN z, GEN T} \kbd{z} a vector whose
+coefficients are \kbd{RgX}s (arbitrary \kbd{GEN}s in fact), reduce them to
+\kbd{RgXQ}s (applying \kbd{grem} coefficientwise) in a \typ{COL}.
+
+\fun{GEN}{RgXQV_red}{GEN z, GEN T} \kbd{z} a \typ{POL} whose
+coefficients are \kbd{RgX}s (arbitrary \kbd{GEN}s in fact), reduce them to
+\kbd{RgXQ}s (applying \kbd{grem} coefficientwise) in a \typ{VEC}.
+
+\fun{GEN}{RgXQX_red}{GEN z, GEN T} \kbd{z} a \typ{POL} whose
+coefficients are \kbd{RgX}s (arbitrary \kbd{GEN}s in fact), reduce them to
+\kbd{RgXQ}s (applying \kbd{grem} coefficientwise).
+
+\fun{GEN}{RgXQX_mul}{GEN x, GEN y, GEN T}
+
+\fun{GEN}{Kronecker_to_mod}{GEN z, GEN T} $z\in R[X]$ represents an element
+$P(X,Y)$ in $R[X,Y]$ mod $T(Y)$ in Kronecker form, i.e. $z = P(X,X^{2*n-1})$
+
+Let $R$ be some commutative ring, $n = \deg T$ and let $P(X,Y)\in R[X,Y]$ lift
+a polynomial in $K[Y]$, where $K := R[X]/(T)$ and $\deg_X P < 2n-1$ --- such as
+would result from multiplying minimal degree lifts of two polynomials in
+$K[Y]$. Let $z = P(t,t^{2*n-1})$ be a Kronecker form of $P$, this function
+returns the image of $P(X,t)$ in $K[t]$, with \typ{POLMOD} coefficients.
+Not stack-clean. Note that $t$ need not be the same variable as $Y$!
+
+\fun{GEN}{RgX_Rg_mul}{GEN y, GEN x} multiplies the \kbd{RgX} \kbd{y}
+by the scalar \kbd{x}.
+
+\fun{GEN}{RgX_muls}{GEN y, long s} multiplies the \kbd{RgX} \kbd{y}
+by the \kbd{long}~\kbd{s}.
+
+\fun{GEN}{RgX_Rg_div}{GEN y, GEN x} divides the \kbd{RgX} \kbd{y}
+by the scalar \kbd{x}.
+
+\fun{GEN}{RgX_divs}{GEN y, long s} divides the \kbd{RgX} \kbd{y}
+by the \kbd{long}~\kbd{s}.
+
+\fun{GEN}{RgX_Rg_divexact}{GEN x, GEN y} exact division of the \kbd{RgX}
+\kbd{y} by the scalar \kbd{x}.
+
+\fun{GEN}{RgXQX_RgXQ_mul}{GEN x, GEN y, GEN T} multiplies the \kbd{RgXQX}
+\kbd{y} by the scalar (\kbd{RgXQ}) \kbd{x}.
+
+\fun{GEN}{RgXQX_sqr}{GEN x, GEN T}
+
+\fun{GEN}{RgXQX_divrem}{GEN x, GEN y, GEN T, GEN *pr}
+
+\fun{GEN}{RgXQX_div}{GEN x, GEN y, GEN T, GEN *r}
+
+\fun{GEN}{RgXQX_rem}{GEN x, GEN y, GEN T, GEN *r}
+
+\newpage
+\chapter{Operations on general PARI objects}
+
+\section{Assignment}
+
+It is in general easier to use a direct conversion,
+e.g.~\kbd{y = stoi(s)}, than to allocate a target of correct type and
+sufficient size, then assign to it:
+\bprog
+  GEN y = cgeti(3); affsi(s, y);
+ at eprog\noindent
+These functions can still be moderately useful in complicated garbage
+collecting scenarios but you will be better off not using them.
+
+\fun{void}{gaffsg}{long s, GEN x} assigns the \kbd{long}~\kbd{s} into the
+object~\kbd{x}.
+
+\fun{void}{gaffect}{GEN x, GEN y} assigns the object \kbd{x} into the
+object~\kbd{y}. Both \kbd{x} and \kbd{y} must be scalar types. Type
+conversions (e.g.~from \typ{INT} to \typ{REAL} or \typ{INTMOD}) occur if
+legitimate.
+
+\fun{int}{is_universal_constant}{GEN x} returns $1$ if $x$ is a global PARI
+constant you should never assign to (such as \kbd{gen\_1}), and $0$
+otherwise.
+
+\section{Conversions}
+
+\subsec{Scalars}
+
+\fun{double}{rtodbl}{GEN x} applied to a \typ{REAL}~\kbd{x}, converts \kbd{x}
+into a \kbd{double} if possible.
+
+\fun{GEN}{dbltor}{double x} converts the \kbd{double} \kbd{x} into a
+\typ{REAL}.
+
+\fun{long}{dblexpo}{double x} returns \kbd{expo(dbltor(x))}, but
+faster and without cluttering the stack.
+
+\fun{ulong}{dblmantissa}{double x} returns the most significant word
+in the mantissa of \kbd{dbltor(x)}.
+
+\fun{double}{gtodouble}{GEN x} if \kbd{x} is a real number (not necessarily
+a~\typ{REAL}), converts \kbd{x} into a \kbd{double} if possible.
+
+\fun{long}{gtos}{GEN x} converts the \typ{INT} \kbd{x} to a small
+integer if possible, otherwise raise an exception. This function
+is similar to \tet{itos}, slightly slower since it checks the type of \kbd{x}.
+
+\fun{double}{dbllog2r}{GEN x} assuming \kbd{x} is a non-zero \typ{REAL},
+returns an approximation to \kbd{log2(|x|)}.
+
+\fun{long}{gtolong}{GEN x} if \kbd{x} is an integer (not necessarily
+a~\typ{INT}), converts \kbd{x} into a \kbd{long} if possible.
+
+\fun{GEN}{fractor}{GEN x, long l} applied to a \typ{FRAC}~\kbd{x}, converts
+\kbd{x} into a \typ{REAL} of length \kbd{prec}.
+
+\fun{GEN}{quadtofp}{GEN x, long l} applied to a \typ{QUAD}~\kbd{x}, converts
+\kbd{x} into a \typ{REAL} or \typ{COMPLEX} depending on the sign of the
+discriminant of~\kbd{x}, to precision \hbox{\kbd{l} \B-bit} words.
+% forbid line brk at hyphen here [GN]
+
+\fun{GEN}{cxtofp}{GEN x, long prec} converts the \typ{COMPLEX}~\kbd{x} to a
+a complex whose real and imaginary parts are \typ{REAL} of length \kbd{prec}
+(special case of~\kbd{gtofp}.
+
+\fun{GEN}{cxcompotor}{GEN x, long prec} converts the
+\typ{INT}, \typ{REAL} or \typ{FRAC} $x$ to a \typ{REAL} of length \kbd{prec}.
+These are all the real types which may occur as components of a
+\typ{COMPLEX}; special case of~\kbd{gtofp} (introduced so that the latter is
+not recursive and can thus be inlined).
+
+\fun{GEN}{gtofp}{GEN x, long prec} converts the complex number~\kbd{x}
+(\typ{INT}, \typ{REAL}, \typ{FRAC}, \typ{QUAD} or \typ{COMPLEX}) to either
+a \typ{REAL} or \typ{COMPLEX} whose components are \typ{REAL} of precision
+\kbd{prec}; not necessarily of \emph{length} \kbd{prec}: a real $0$ may be
+given as \kbd{real\_0(...)}). If the result is a \typ{COMPLEX} extra care is
+taken so that its modulus really has accuracy \kbd{prec}: there is a problem
+if the real part of the input is an exact $0$; indeed, converting it to
+\kbd{real\_0(prec)} would be wrong if the imaginary part is tiny, since the
+modulus would then become equal to $0$, as in $1.E-100 + 0.E-28 = 0.E-28$.
+
+\fun{GEN}{gtomp}{GEN z, long prec} converts the real number~\kbd{x}
+(\typ{INT}, \typ{REAL}, \typ{FRAC}, real \typ{QUAD}) to either
+a \typ{INT} or a \typ{REAL} of precision \kbd{prec}. Not memory clean
+if $x$ is a \typ{INT}: we return $x$ itself and not a copy.
+
+\fun{GEN}{gcvtop}{GEN x, GEN p, long l} converts $x$ into a \typ{PADIC}
+of precision~$l$. Works componentwise on recursive objects,
+e.g.~\typ{POL} or \typ{VEC}. Converting $0$ yields $O(p^l)$; converting a
+non-zero number yield a result well defined modulo $p^{v_p(x) + l}$.
+
+\fun{GEN}{cvtop}{GEN x, GEN p, long l} as \kbd{gcvtop}, assuming that $x$
+is a scalar.
+
+\fun{GEN}{cvtop2}{GEN x, GEN y} $y$ being a $p$-adic, converts the scalar $x$
+to a $p$-adic of the same accuracy. Shallow function.
+
+\fun{GEN}{cvstop2}{long s, GEN y} $y$ being a $p$-adic, converts the scalar $s$
+to a $p$-adic of the same accuracy. Shallow function.
+
+\fun{GEN}{gprec}{GEN x, long l} returns a copy of $x$ whose precision is
+changed to $l$ digits. The precision change is done recursively on all
+components of $x$. Digits means \emph{decimal}, $p$-adic and $X$-adic digits
+for \typ{REAL}, \typ{SER}, \typ{PADIC} components, respectively.
+
+\fun{GEN}{gprec_w}{GEN x, long l} returns a shallow copy of $x$ whose
+\typ{REAL} components have their precision changed to $l$ \emph{words}. This
+is often more useful than \kbd{gprec}.
+
+\fun{GEN}{gprec_wtrunc}{GEN x, long l} returns a shallow copy of $x$ whose
+\typ{REAL} components have their precision \emph{truncated} to $l$
+\emph{words}. Contrary to \kbd{gprec\_w}, this function may never increase
+the precision of~$x$.
+
+\subsec{Modular objects / lifts}
+
+\fun{GEN}{gmodulo}{GEN x, GEN y} creates the object \kbd{\key{Mod}(x,y)} on
+the PARI stack, where \kbd{x} and \kbd{y} are either both \typ{INT}s, and the
+result is a \typ{INTMOD}, or \kbd{x} is a scalar or a \typ{POL} and \kbd{y} a
+\typ{POL}, and the result is a \typ{POLMOD}.
+
+\fun{GEN}{gmodulgs}{GEN x, long y} same as \key{gmodulo} except \kbd{y} is a
+\kbd{long}.
+
+\fun{GEN}{gmodulsg}{long x, GEN y} same as \key{gmodulo} except \kbd{x} is a
+\kbd{long}.
+
+\fun{GEN}{gmodulss}{long x, long y} same as \key{gmodulo} except both
+\kbd{x} and \kbd{y} are \kbd{long}s.
+
+\fun{GEN}{liftall_shallow}{GEN x} shallow version of \tet{liftall}
+
+\fun{GEN}{liftint_shallow}{GEN x} shallow version of \tet{liftint}
+
+\fun{GEN}{liftpol_shallow}{GEN x} shallow version of \tet{liftpol}
+
+\fun{GEN}{centerlift0}{GEN x,long v} DEPRECATED, kept for backward
+compatibility only: use either \tet{lift0}$(x,v)$ or \tet{centerlift}$(x)$.
+
+\subsec{Between polynomials and coefficient arrays}
+
+\fun{GEN}{gtopoly}{GEN x, long v} converts or truncates the object~\kbd{x}
+into a \typ{POL} with main variable number~\kbd{v}. A common application
+would be the conversion of coefficient vectors (coefficients are given by
+decreasing degree). E.g.~\kbd{[2,3]} goes to \kbd{2*v + 3}
+
+\fun{GEN}{gtopolyrev}{GEN x, long v} converts or truncates the object~\kbd{x}
+into a \typ{POL} with main variable number~\kbd{v}, but vectors are converted
+in reverse order compared to \kbd{gtopoly} (coefficients are given by
+increasing degree). E.g.~\kbd{[2,3]} goes to \kbd{3*v + 2}. In other words
+the vector represents a polynomial in the basis $(1,v,v^2,v^3,\dots)$.
+
+\fun{GEN}{normalizepol}{GEN x} applied to an unnormalized \typ{POL}~\kbd{x}
+(with all coefficients correctly set except that \kbd{leading\_term(x)} might
+be zero), normalizes \kbd{x} correctly in place and returns~\kbd{x}. For
+internal use. Normalizing means deleting all leading \emph{exact} zeroes
+(as per \kbd{isexactzero}), except if the polynomial turns out to be $0$,
+in which case we try to find a coefficient $c$ which is a non-rational zero,
+and return the constant polynomial $c$. (We do this so that information
+about the base ring is not lost.)
+
+\fun{GEN}{normalizepol_lg}{GEN x, long l} applies \kbd{normalizepol} to
+\kbd{x}, pretending that \kbd{lg(x)} is $l$, which must be less than
+or equal to \kbd{lg(x)}. If equal, the function is equivalent to
+\kbd{normalizepol(x)}.
+
+\fun{GEN}{normalizepol_approx}{GEN x, long lx} as \kbd{normalizepol\_lg},
+with the difference that we just delete all leading zeroes (as per
+\kbd{gequal0}). This rougher normalization is used when we have no other
+choice, for instance before attempting a Euclidean division by $x$.
+
+The following routines do \emph{not} copy coefficients on the stack (they
+only move pointers around), hence are very fast but not suitable for
+\kbd{gerepile} calls. Recall that an \kbd{RgV} (resp.~an \kbd{RgX}, resp.~an
+\kbd{RgM}) is a \typ{VEC} or \typ{COL} (resp.~a \typ{POL}, resp.~a \typ{MAT})
+with arbitrary components. Similarly, an \kbd{RgXV} is a \typ{VEC} or
+\typ{COL} with \kbd{RgX} components, etc.
+
+\fun{GEN}{RgV_to_RgX}{GEN x, long v} converts the \kbd{RgV}~\kbd{x} to a
+(normalized) polynomial in variable~\kbd{v} (as \kbd{gtopolyrev}, without
+copy).
+
+\fun{GEN}{RgV_to_RgX_reverse}{GEN x, long v} converts the \kbd{RgV}~\kbd{x}
+to a (normalized) polynomial in variable~\kbd{v} (as \kbd{gtopoly},
+without copy).
+
+\fun{GEN}{RgX_to_RgV}{GEN x, long N} converts the \typ{POL}~\kbd{x} to a
+\typ{COL}~\kbd{v} with \kbd{N} components. Coefficients of \kbd{x} are listed
+by increasing degree, so that \kbd{y[i]} is the coefficient of the term of
+degree $i-1$ in \kbd{x}.
+
+\fun{GEN}{Rg_to_RgV}{GEN x, long N} as \tet{RgX_to_RgV}, except that other
+types than \typ{POL} are allowed for \kbd{x}, which is then considered as a
+constant polynomial.
+
+\fun{GEN}{RgM_to_RgXV}{GEN x, long v} converts the \kbd{RgM}~\kbd{x} to a
+\typ{VEC} of \kbd{RgX}, by repeated calls to \kbd{RgV\_to\_RgX}.
+
+\fun{GEN}{RgV_to_RgM}{GEN v, long N} converts the vector~\kbd{v} to
+a~\typ{MAT} with \kbd{N}~rows, by repeated calls to \kbd{Rg\_to\_RgV}.
+
+\fun{GEN}{RgXV_to_RgM}{GEN v, long N} converts the vector of \kbd{RgX}~\kbd{v}
+to a~\typ{MAT} with \kbd{N}~rows, by repeated calls to \kbd{RgX\_to\_RgV}.
+
+\fun{GEN}{RgM_to_RgXX}{GEN x, long v,long w} converts the \kbd{RgM}~\kbd{x} into
+a \typ{POL} in variable~\kbd{v}, whose coefficients are \typ{POL}s in
+variable~\kbd{w}. This is a shortcut for
+\bprog
+  RgV_to_RgX( RgM_to_RgXV(x, w), v );
+ at eprog\noindent
+There are no consistency checks with respect to variable
+priorities: the above is an invalid object if $\kbd{varncmp(v, w)} \geq 0$.
+
+\fun{GEN}{RgXX_to_RgM}{GEN x, long N} converts the \typ{POL}~\kbd{x} with
+\kbd{RgX} (or constant) coefficients to a matrix with \kbd{N} rows.
+
+\fun{GEN}{RgXY_swap}{GEN P, long n, long w} converts the bivariate polynomial
+$\kbd{P}(u,v)$ (a \typ{POL} with \typ{POL} or scalar coefficients) to
+$P(\kbd{pol\_x[w]},u)$, assuming \kbd{n} is an upper bound for
+$\deg_v(\kbd{P})$.
+
+\fun{GEN}{RgXY_swapspec}{GEN C, long n, long w, long lP}
+as \kbd{RgXY\_swap} where the coefficients of $P$ are given by
+\kbd{gel(C,0),\dots,gel(C,lP-1)}.
+
+\fun{GEN}{RgX_to_ser}{GEN x, long l} applied to a \typ{POL}~\kbd{x}, creates
+a \emph{shallow} \typ{SER} of length~$l\geq 2$ starting with~\kbd{x}.
+Unless the polynomial is an exact zero, the coefficient of lowest degree
+$T^d$ of the result is not an exact zero (as per \kbd{isexactzero}). The
+remainder is $O(T^{d+l})$.
+
+\fun{GEN}{RgX_to_ser_inexact}{GEN x, long l} applied to a \typ{POL}~\kbd{x},
+creates a \emph{shallow} \typ{SER} of length~\kbd{l} starting with~\kbd{x}.
+Unless the polynomial is zero, the coefficient of lowest degree
+$T^d$ of the result is not zero (as per \kbd{gequal0}). The
+remainder is $O(T^{d+l})$.
+
+\fun{GEN}{rfrac_to_ser}{GEN x, long l} applied to a \typ{RFRAC}~\kbd{x},
+creates a \typ{SER} of length~\kbd{l} congruent to $x$. Not memory-clean
+but suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{gtoser}{GEN s, long v, long d} converts the object~$s$ into
+a \typ{SER} with main variable number~\kbd{v} and $d > 0$ significant terms.
+More precisely
+
+\item if $s$ is a scalar,  we return a constant power series with $d$
+significant terms.
+
+\item if $s$ is a \typ{POL}, it is truncated to $d$ terms if needed.
+
+\item If $s$ is a vector, the coefficients of the vector  are understood to
+be the coefficients of the power series starting from the constant term (as
+in \tet{Polrev}), and the precision $d$ is \emph{ignored}.
+
+\item If $s$ is already a power series in $v$, we retur a copy, and
+the precision $d$ is again \emph{ignored}.
+
+\fun{GEN}{gtocol}{GEN x} converts the object~\kbd{x} into a \typ{COL}
+
+\fun{GEN}{gtomat}{GEN x} converts the object~\kbd{x} into a \typ{MAT}.
+
+\fun{GEN}{gtovec}{GEN x} converts the object~\kbd{x} into a \typ{VEC}.
+
+\fun{GEN}{gtovecsmall}{GEN x} converts the object~\kbd{x} into a
+\typ{VECSMALL}.
+
+\fun{GEN}{normalize}{GEN x} applied to an unnormalized \typ{SER}~\kbd{x}
+(i.e.~type \typ{SER} with all coefficients correctly set except that \kbd{x[2]}
+might be zero), normalizes \kbd{x} correctly in place. Returns~\kbd{x}.
+For internal use.
+
+\fun{GEN}{serchop0}{GEN s} given a \typ{SER} of the form $x^v s(x)$, with
+$s(0)\neq 0$, return $x^v(s - s(0))$. Shallow function.
+
+\section{Constructors}
+
+\subsec{Clean constructors}\label{se:clean}
+
+\fun{GEN}{zeropadic}{GEN p, long n} creates a $0$ \typ{PADIC} equal to
+$O(\kbd{p}^\kbd{n})$.
+
+\fun{GEN}{zeroser}{long v, long n} creates a $0$ \typ{SER} in variable
+\kbd{v} equal to $O(X^\kbd{n})$.
+
+\fun{GEN}{scalarser}{GEN x, long v, long prec} creates a constant \typ{SER}
+in variable \kbd{v} and precision \kbd{prec}, whose constant coefficient is
+(a copy of) \kbd{x}, in other words $\kbd{x} + O(\kbd{v}^\kbd{prec})$.
+Assumes that \kbd{x} is non-zero.
+
+\fun{GEN}{pol_0}{long v} Returns the constant polynomial $0$ in variable $v$.
+
+\fun{GEN}{pol_1}{long v} Returns the constant polynomial $1$ in variable $v$.
+
+\fun{GEN}{pol_x}{long v} Returns the monomial of degree $1$ in variable $v$.
+
+\fun{GEN}{pol_x_powers}{long N, long v} returns the powers of
+\kbd{pol\_x(v)}, of degree $0$ to $N$, in a vector with $N+1$ components.
+
+\fun{GEN}{scalarpol}{GEN x, long v} creates a constant \typ{POL} in variable
+\kbd{v}, whose constant coefficient is (a copy of) \kbd{x}.
+
+\fun{GEN}{deg1pol}{GEN a, GEN b,long v} creates the degree 1 \typ{POL}
+$a \kbd{pol\_x}(v) + b$
+
+\fun{GEN}{zeropol}{long v} is identical \kbd{pol\_0}.
+
+\fun{GEN}{zerocol}{long n} creates a \typ{COL} with \kbd{n} components set to
+\kbd{gen\_0}.
+
+\fun{GEN}{zerovec}{long n} creates a \typ{VEC} with \kbd{n} components set to
+\kbd{gen\_0}.
+
+\fun{GEN}{col_ei}{long n, long i} creates a \typ{COL} with \kbd{n} components
+set to \kbd{gen\_0}, but for the \kbd{i}-th one which is set to \kbd{gen\_1}
+(\kbd{i}-th vector in the canonical basis).
+
+\fun{GEN}{vec_ei}{long n, long i} creates a \typ{VEC} with \kbd{n} components
+set to \kbd{gen\_0}, but for the \kbd{i}-th one which is set to \kbd{gen\_1}
+(\kbd{i}-th vector in the canonical basis).
+
+\fun{GEN}{trivial_fact}{void} returns the trivial (empty) factorization
+\kbd{Mat([]\til,[]\til)}
+
+\fun{GEN}{prime_fact}{GEN x} returns the factorization
+\kbd{Mat([x]\til, [1]\til)}
+
+\fun{GEN}{Rg_col_ei}{GEN x, long n, long i} creates a \typ{COL} with \kbd{n}
+components set to \kbd{gen\_0}, but for the \kbd{i}-th one which is set to
+\kbd{x}.
+
+\fun{GEN}{vecsmall_ei}{long n, long i} creates a \typ{VECSMALL} with \kbd{n}
+components set to \kbd{0}, but for the \kbd{i}-th one which is set to
+\kbd{1} (\kbd{i}-th vector in the canonical basis).
+
+\fun{GEN}{scalarcol}{GEN x, long n} creates a \typ{COL} with \kbd{n}
+components set to \kbd{gen\_0}, but the first one which is set to a copy
+of \kbd{x}. (The name comes from \kbd{RgV\_isscalar}.)
+
+\smallskip
+
+\fun{GEN}{mkintmodu}{ulong x, ulong y} creates the \typ{INTMOD} \kbd{Mod(x, y)}.
+The inputs must satisfy $x < y$.
+
+\fun{GEN}{zeromat}{long m, long n} creates a \typ{MAT} with \kbd{m} x \kbd{n}
+components set to \kbd{gen\_0}. Note that the result allocates a
+\emph{single} column, so modifying an entry in one column modifies it in
+all columns. To fully allocate a matrix initialized with zero entries,
+use \kbd{zeromatcopy}.
+
+\fun{GEN}{zeromatcopy}{long m, long n} creates a \typ{MAT} with \kbd{m} x
+\kbd{n} components set to \kbd{gen\_0}.
+
+\fun{GEN}{matid}{long n} identity matrix in dimension \kbd{n} (with
+components \kbd{gen\_1} and\kbd{gen\_0}).
+
+\fun{GEN}{scalarmat}{GEN x, long n} scalar matrix, \kbd{x} times the identity.
+
+\fun{GEN}{scalarmat_s}{long x, long n} scalar matrix, \kbd{stoi(x)} times
+the identity.
+
+\fun{GEN}{vecrange}{GEN a, GEN b} returns the \typ{VEC} $[a..b]$.
+
+\fun{GEN}{vecrangess}{long a, long b} returns the \typ{VEC} $[a..b]$.
+
+\smallskip
+See also next section for analogs of the following functions:
+
+\fun{GEN}{mkfraccopy}{GEN x, GEN y} creates the \typ{FRAC} $x/y$. Assumes that
+$y > 1$ and $(x,y) = 1$.
+
+\fun{GEN}{mkrfraccopy}{GEN x, GEN y} creates the \typ{RFRAC} $x/y$.
+Assumes that $y$ is a \typ{POL}, $x$ a compatible type whose variable has
+lower or same priority, with $(x,y) = 1$.
+
+\fun{GEN}{mkcolcopy}{GEN x} creates a 1-dimensional \typ{COL} containing
+\kbd{x}.
+
+\fun{GEN}{mkmatcopy}{GEN x} creates a 1-by-1 \typ{MAT} wrapping the \typ{COL}
+\kbd{x}.
+
+\fun{GEN}{mkveccopy}{GEN x} creates a 1-dimensional \typ{VEC} containing
+\kbd{x}.
+
+\fun{GEN}{mkvec2copy}{GEN x, GEN y} creates a 2-dimensional \typ{VEC} equal
+to \kbd{[x,y]}.
+
+\fun{GEN}{mkcols}{long x} creates a 1-dimensional \typ{COL}
+containing \kbd{stoi(x)}.
+
+\fun{GEN}{mkcol2s}{long x, long y} creates a 2-dimensional \typ{COL}
+containing \kbd{[stoi(x), stoi(y)]~}.
+
+\fun{GEN}{mkcol3s}{long x, long y, long z} creates a 3-dimensional \typ{COL}
+containing \kbd{[stoi(x), stoi(y), stoi(z)]~}.
+
+\fun{GEN}{mkcol4s}{long x, long y, long z, long t} creates a 4-dimensional
+\typ{COL} containing \kbd{[stoi(x), stoi(y), stoi(z), stoi(t)]~}.
+
+\fun{GEN}{mkvecs}{long x} creates a 1-dimensional \typ{VEC}
+containing \kbd{stoi(x)}.
+
+\fun{GEN}{mkvec2s}{long x, long y} creates a 2-dimensional \typ{VEC}
+containing \kbd{[stoi(x), stoi(y)]}.
+
+\fun{GEN}{mkvec3s}{long x, long y, long z} creates a 3-dimensional \typ{VEC}
+containing \kbd{[stoi(x), stoi(y), stoi(z)]}.
+
+\fun{GEN}{mkvec4s}{long x, long y, long z, long t} creates a 4-dimensional
+\typ{VEC} containing \kbd{[stoi(x), stoi(y), stoi(z), stoi(t)]}.
+
+\fun{GEN}{mkvecsmall}{long x} creates a 1-dimensional \typ{VECSMALL}
+containing \kbd{x}.
+
+\fun{GEN}{mkvecsmall2}{long x, long y} creates a 2-dimensional \typ{VECSMALL}
+containing \kbd{[x, y]}.
+
+\fun{GEN}{mkvecsmall3}{long x, long y, long z} creates a 3-dimensional
+\typ{VECSMALL} containing \kbd{[x, y, z]}.
+
+\fun{GEN}{mkvecsmall4}{long x, long y, long z, long t} creates a 4-dimensional
+\typ{VECSMALL} containing \kbd{[x, y, z, t]}.
+
+\fun{GEN}{mkvecsmalln}{long n, ...} returns the \typ{VECSMALL} whose $n$
+coefficients (\kbd{long}) follow.
+
+\subsec{Unclean constructors}\label{se:unclean}
+
+Contrary to the policy of general PARI functions, the functions in this
+subsection do \emph{not} copy their arguments, nor do they produce an object
+a priori suitable for \tet{gerepileupto}. In particular, they are
+faster than their clean equivalent (which may not exist). \emph{If} you
+restrict their arguments to universal objects (e.g \kbd{gen\_0}),
+then the above warning does not apply.
+
+\fun{GEN}{mkcomplex}{GEN x, GEN y} creates the \typ{COMPLEX} $x + iy$.
+
+\fun{GEN}{mulcxI}{GEN x} creates the \typ{COMPLEX} $ix$. The result in
+general contains data pointing back to the original $x$. Use \kbd{gcopy} if
+this is a problem. But in most cases, the result is to be used immediately,
+before $x$ is subject to garbage collection.
+
+\fun{GEN}{mulcxmI}{GEN x}, as \tet{mulcxI}, but returns the \typ{COMPLEX}
+$-ix$.
+
+\fun{GEN}{mkquad}{GEN n, GEN x, GEN y} creates the \typ{QUAD} $x + yw$,
+where $w$ is a root of $n$, which is of the form \kbd{quadpoly(D)}.
+
+\fun{GEN}{mkfrac}{GEN x, GEN y} creates the \typ{FRAC} $x/y$. Assumes that
+$y > 1$ and $(x,y) = 1$.
+
+\fun{GEN}{mkrfrac}{GEN x, GEN y} creates the \typ{RFRAC} $x/y$. Assumes
+that $y$ is a \typ{POL}, $x$ a compatible type whose variable has lower
+or same priority, with $(x,y) = 1$.
+
+\fun{GEN}{mkcol}{GEN x} creates a 1-dimensional \typ{COL} containing \kbd{x}.
+
+\fun{GEN}{mkcol2}{GEN x, GEN y} creates a 2-dimensional \typ{COL} equal to
+\kbd{[x,y]}.
+
+\fun{GEN}{mkcol3}{GEN x, GEN y, GEN z} creates a 3-dimensional \typ{COL}
+equal to \kbd{[x,y,z]}.
+
+\fun{GEN}{mkcol4}{GEN x, GEN y, GEN z, GEN t} creates a 4-dimensional \typ{COL}
+equal to \kbd{[x,y,z,t]}.
+
+\fun{GEN}{mkcol5}{GEN a1, GEN a2, GEN a3, GEN a4, GEN a5} creates the
+5-dimensional \typ{COL} equal to $[a_1,a_2,a_3,a_4,a_5]$.
+
+\fun{GEN}{mkintmod}{GEN x, GEN y} creates the \typ{INTMOD} \kbd{Mod(x, y)}.
+The inputs must be \typ{INT}s satisfying $0 \leq x < y$.
+
+\fun{GEN}{mkpolmod}{GEN x, GEN y} creates the \typ{POLMOD} \kbd{Mod(x, y)}.
+The input must satisfy $\deg x < \deg y$ with respect to the main variable of
+the \typ{POL} $y$. $x$ may be a scalar.
+
+\fun{GEN}{mkmat}{GEN x} creates a 1-column \typ{MAT} with column $x$
+(a \typ{COL}).
+
+\fun{GEN}{mkmat2}{GEN x, GEN y} creates a 2-column \typ{MAT} with columns
+$x$, $y$ (\typ{COL}s of the same length).
+
+\fun{GEN}{mkmat3}{GEN x, GEN y, GEN z} creates a 3-column \typ{MAT} with columns
+$x$, $y$, $z$ (\typ{COL}s of the same length).
+
+\fun{GEN}{mkmat4}{GEN x, GEN y, GEN z, GEN t} creates a 4-column \typ{MAT}
+with columns $x$, $y$, $z$, $t$ (\typ{COL}s of the same length).
+
+\fun{GEN}{mkmat5}{GEN x, GEN y, GEN z, GEN t, GEN u} creates a 5-column
+\typ{MAT} with columns $x$, $y$, $z$, $t$, $u$ (\typ{COL}s of the same
+length).
+
+\fun{GEN}{mkvec}{GEN x} creates a 1-dimensional \typ{VEC} containing \kbd{x}.
+
+\fun{GEN}{mkvec2}{GEN x, GEN y} creates a 2-dimensional \typ{VEC} equal to
+\kbd{[x,y]}.
+
+\fun{GEN}{mkvec3}{GEN x, GEN y, GEN z} creates a 3-dimensional \typ{VEC}
+equal to \kbd{[x,y,z]}.
+
+\fun{GEN}{mkvec4}{GEN x, GEN y, GEN z, GEN t} creates a 4-dimensional \typ{VEC}
+equal to \kbd{[x,y,z,t]}.
+
+\fun{GEN}{mkvec5}{GEN a1, GEN a2, GEN a3, GEN a4, GEN a5} creates the
+5-dimensional \typ{VEC} equal to $[a_1,a_2,a_3,a_4,a_5]$.
+
+\fun{GEN}{mkqfi}{GEN x, GEN y, GEN z} creates \typ{QFI} equal
+to \kbd{Qfb(x,y,z)}, assuming that $y^2 - 4xz < 0$.
+
+\fun{GEN}{mkerr}{long n} returns a \typ{ERROR} with error code $n$
+(\kbd{enum err\_list}).
+
+\smallskip
+
+It is sometimes useful to return such a container whose entries are not
+universal objects, but nonetheless suitable for \tet{gerepileupto}.
+If the entries can be computed at the time the result is returned, the
+following macros achieve this effect:
+
+\fun{GEN}{retmkvec}{GEN x} returns a vector containing the single entry $x$,
+where the vector root is created just before the function argument $x$ is
+evaluated. Expands to
+\bprog
+  {
+    GEN res = cgetg(2, t_VEC);
+    gel(res, 1) = x; /* @Ccom or rather, the \emph{expansion} of $x$ */
+    return res;
+  }
+ at eprog\noindent For instance, the \kbd{retmkvec(gcopy(x))} returns a clean
+object, just like \kbd{return mkveccopy(x)} would.
+
+\fun{GEN}{retmkvec2}{GEN x, GEN y}
+returns the $2$-dimensional \typ{VEC} \kbd{[x,y]}.
+
+\fun{GEN}{retmkvec3}{GEN x, GEN y, GEN z}
+returns the $3$-dimensional \typ{VEC} \kbd{[x,y,z]}.
+
+\fun{GEN}{retmkvec4}{GEN x, GEN y, GEN z, GEN t}
+returns the $4$-dimensional \typ{VEC} \kbd{[x,y,z,t]}.
+
+\fun{GEN}{retmkvec5}{GEN x, GEN y, GEN z, GEN t, GEN u}
+returns the $5$-dimensional row vector \kbd{[x,y,z,t,u]}.
+
+\fun{GEN}{retconst_vec}{long n, GEN x}
+returns the $n$-dimensional \typ{VEC} whose entries are constant and all
+equal to $x$.
+
+\fun{GEN}{retmkcol}{GEN x}
+returns the $1$-dimensional \typ{COL} \kbd{[x]~}.
+
+\fun{GEN}{retmkcol2}{GEN x, GEN y}
+returns the $2$-dimensional \typ{COL} \kbd{[x,y]~}.
+
+\fun{GEN}{retmkcol3}{GEN x, GEN y, GEN z}
+returns the $3$-dimensional \typ{COL} \kbd{[x,y,z]~}.
+
+\fun{GEN}{retmkcol4}{GEN x, GEN y, GEN z, GEN t}
+returns the $4$-dimensional \typ{COL} \kbd{[x,y,z,t]~}.
+
+\fun{GEN}{retmkcol5}{GEN x, GEN y, GEN z, GEN t, GEN u}
+returns the $5$-dimensional column vector \kbd{[x,y,z,t,u]~}.
+
+\fun{GEN}{retconst_col}{long n, GEN x}
+returns the $n$-dimensional \typ{COL} whose entries are constant and all
+equal to $x$.
+
+\fun{GEN}{retmkmat}{GEN x}
+returns the $1$-column \typ{MAT} with colum \kbd{x}.
+
+\fun{GEN}{retmkmat2}{GEN x, GEN y}
+returns the $2$-column \typ{MAT} with columns \kbd{x}, \kbd{y}.
+
+\fun{GEN}{retmkmat3}{GEN x, GEN y, GEN z}
+returns the $3$-dimensional \typ{MAT} with columns
+\kbd{x}, \kbd{y}, \kbd{z}.
+
+\fun{GEN}{retmkmat4}{GEN x, GEN y, GEN z, GEN t}
+returns the $4$-dimensional \typ{MAT} with columns
+\kbd{x}, \kbd{y}, \kbd{z}, \kbd{t}.
+
+\fun{GEN}{retmkmat5}{GEN x, GEN y, GEN z, GEN t, GEN u}
+returns the $5$-dimensional \typ{MAT} with columns
+\kbd{x}, \kbd{y}, \kbd{z}, \kbd{t}, \kbd{u}.
+
+\fun{GEN}{retmkcomplex}{GEN x, GEN y}
+returns the \typ{COMPLEX} \kbd{x + I*y}.
+
+\fun{GEN}{retmkfrac}{GEN x, GEN y}
+returns the \typ{FRAC} \kbd{x / y}. Assume $x$ and $y$ are coprime and $y > 1$.
+
+\fun{GEN}{retmkintmod}{GEN x, GEN y}
+returns the \typ{INTMOD} \kbd{Mod(x, y)}.
+
+\fun{GEN}{retmkqfi}{GEN a, GEN b, GEN c}.
+
+\fun{GEN}{retmkqfr}{GEN a, GEN b, GEN c, GEN d}.
+
+\fun{GEN}{retmkquad}{GEN n, GEN a, GEN b}.
+
+\fun{GEN}{retmkpolmod}{GEN x, GEN y}
+returns the \typ{POLMOD} \kbd{Mod(x, y)}.
+
+\smallskip
+
+\fun{GEN}{mkintn}{long n, ...} returns the non-negative \typ{INT} whose
+development in base $2^{32}$ is given by the following $n$ words
+(\kbd{unsigned long}). It is assumed that all such arguments are less than
+$2^{32}$ (the actual \kbd{sizeof(long)} is irrelevant, the behavior is also
+as above on $64$-bit machines).
+\bprog
+  mkintn(3, a2, a1, a0);
+ at eprog
+\noindent returns $a_2 2^{64} + a_1 2^{32} + a_0$.
+
+\fun{GEN}{mkpoln}{long n, ...} Returns the \typ{POL} whose $n$
+coefficients (\kbd{GEN}) follow, in order of decreasing degree.
+\bprog
+  mkpoln(3, gen_1, gen_2, gen_0);
+ at eprog
+\noindent returns the polynomial $X^2 + 2X$ (in variable $0$, use
+\tet{setvarn} if you want other variable numbers). Beware that $n$ is the
+number of coefficients, hence \emph{one more} than the degree.
+
+\fun{GEN}{mkvecn}{long n, ...} returns the \typ{VEC} whose $n$
+coefficients (\kbd{GEN}) follow.
+
+\fun{GEN}{mkcoln}{long n, ...} returns the \typ{COL} whose $n$
+coefficients (\kbd{GEN}) follow.
+
+\fun{GEN}{scalarcol_shallow}{GEN x, long n} creates a \typ{COL} with \kbd{n}
+components set to \kbd{gen\_0}, but the first one which is set to a shallow
+copy of \kbd{x}. (The name comes from \kbd{RgV\_isscalar}.)
+
+\fun{GEN}{scalarmat_shallow}{GEN x, long n} creates an $n\times n$
+scalar matrix whose diagonal is set to shallow copies of the scalar \kbd{x}.
+
+\fun{GEN}{diagonal_shallow}{GEN x} returns a diagonal matrix whose diagonal
+is given by the vector $x$. Shallow function.
+
+\fun{GEN}{scalarpol_shallow}{GEN a, long v} returns the degree 0
+\typ{POL} $a \kbd{pol\_x}(v)^0$.
+
+\fun{GEN}{deg1pol_shallow}{GEN a, GEN b,long v} returns the degree 1
+\typ{POL} $a\kbd{pol\_x}(v) + b$
+
+\fun{GEN}{zeropadic_shallow}{GEN p, long n} returns a (shallow) $0$
+\typ{PADIC} equal to $O(\kbd{p}^\kbd{n})$.
+
+\subsec{From roots to polynomials}
+
+\fun{GEN}{deg1_from_roots}{GEN L, long v} given a vector $L$ of scalars,
+returns the vector of monic linear polynomials in variable $v$ whose roots
+are the $L[i]$, i.e. the $x - L[i]$.
+
+\fun{GEN}{roots_from_deg1}{GEN L} given a vector $L$ of monic linear
+polynomials, return their roots, i.e. the $- L[i](0)$.
+
+\fun{GEN}{roots_to_pol}{GEN L, long v} given a vector of scalars $L$,
+returns the monic polynomial in variable $v$ whose roots are the $L[i]$.
+Calls \tet{divide_conquer_prod}, so leaves some garbage on stack, but
+suitable for \kbd{gerepileupto}.
+
+\fun{GEN}{roots_to_pol_r1}{GEN L, long v, long r1} as \kbd{roots\_to\_pol}
+assuming the first $r_1$ roots are ``real'', and the following ones are
+representatives of conjugate pairs of ``complex'' roots. So if $L$ has $r_1 +
+r_2$ elements, we obtain a polynomial of degree $r_1 + 2r_2$. In most
+applications, the roots are indeed real and complex, but the implementation
+assumes only that each ``complex'' root $z$ introduces a quadratic
+factor $X^2 - \kbd{trace}(z) X + \kbd{norm}(z)$. Calls
+\tet{divide_conquer_prod}.
+Calls \tet{divide_conquer_prod}, so leaves some garbage on stack, but
+suitable for \kbd{gerepileupto}.
+
+\section{Integer parts}
+
+\fun{GEN}{gfloor}{GEN x} creates the floor of~\kbd{x}, i.e.\ the (true)
+integral part.
+
+\fun{GEN}{gfrac}{GEN x} creates the fractional part of~\kbd{x}, i.e.\ \kbd{x}
+minus the floor of~\kbd{x}.
+
+\fun{GEN}{gceil}{GEN x} creates the ceiling of~\kbd{x}.
+
+\fun{GEN}{ground}{GEN x} rounds towards~$+\infty$ the components of \kbd{x}
+to the nearest integers.
+
+\fun{GEN}{grndtoi}{GEN x, long *e} same as \kbd{ground}, but in addition sets
+\kbd{*e} to the binary exponent of $x - \kbd{ground}(x)$. If this is
+positive, all significant bits are lost. This kind of situation raises an
+error message in \key{ground} but not in \key{grndtoi}.
+
+\fun{GEN}{gtrunc}{GEN x} truncates~\kbd{x}. This is the false integer part
+if \kbd{x} is a real number (i.e.~the unique integer closest to \kbd{x} among
+those between 0 and~\kbd{x}). If \kbd{x} is a \typ{SER}, it is truncated
+to a \typ{POL}; if \kbd{x} is a \typ{RFRAC}, this takes the polynomial part.
+
+\fun{GEN}{gtrunc2n}{GEN x, long n} creates the floor of~$2^n$\kbd{x}, this is
+only implemented for \typ{INT}, \typ{REAL}, \typ{FRAC} and \typ{COMPLEX} of
+those.
+
+\fun{GEN}{gcvtoi}{GEN x, long *e} analogous to \key{grndtoi} for
+\typ{REAL} inputs except that rounding is replaced by truncation. Also applies
+componentwise for vector or matrix inputs; otherwise, sets \kbd{*e} to
+\kbd{-HIGHEXPOBIT} (infinite real accuracy) and return \kbd{gtrunc(x)}.
+
+\section{Valuation and shift}
+
+\fun{GEN}{gshift[z]}{GEN x, long n[, GEN z]} yields the result of shifting
+(the components of) \kbd{x} left by \kbd{n} (if \kbd{n} is non-negative)
+or right by $-\kbd{n}$ (if \kbd{n} is negative). Applies only to \typ{INT}
+and vectors/matrices of such. For other types, it is simply multiplication
+by~$2^{\kbd{n}}$.
+
+\fun{GEN}{gmul2n[z]}{GEN x, long n[, GEN z]} yields the product of \kbd{x}
+and~$2^{\kbd{n}}$. This is different from \kbd{gshift} when \kbd{n} is negative
+and \kbd{x} is a \typ{INT}: \key{gshift} truncates, while \key{gmul2n}
+creates a fraction if necessary.
+
+\fun{long}{gvaluation}{GEN x, GEN p} returns the greatest exponent~$e$ such that
+$\kbd{p}^e$ divides~\kbd{x}, when this makes sense.
+
+\fun{long}{gval}{GEN x, long v} returns the highest power of the variable
+number \kbd{v} dividing the \typ{POL}~\kbd{x}.
+
+\section{Comparison operators}
+
+\subsec{Generic}
+
+\fun{long}{gcmp}{GEN x, GEN y} comparison of \kbd{x} with \kbd{y}: returns
+$1$ ($x > y$), $0$ ($x = y$) or $-1$ ($x < y$). Two \typ{STR}
+are compared using the standard lexicographic ordering; a \typ{STR}
+is considered strictly larger than any non-string type. If neither
+$x$ nor $y$ is a \typ{STR}, their allowed types are \typ{INT}, \typ{REAL}
+or \typ{FRAC}. Used \tet{cmp_universal} to compare arbitrary \kbd{GEN}s.
+
+\fun{long}{lexcmp}{GEN x, GEN y} comparison of \kbd{x} with \kbd{y} for the
+lexicographic ordering; when comparing objects of different lengths whose
+components are all equal up to the smallest of their length, consider that
+the longest is largest. Consider scalars as $1$-component vectors. Return
+\kbd{gcmp}$(x,y)$ if both arguments are scalars.
+
+\fun{int}{gequalX}{GEN x} return 1 (true) if \kbd{x} is a variable
+(monomial of degree $1$ with \typ{INT} coefficients equal to $1$ and $0$),
+and $0$ otherwise
+
+\fun{long}{gequal}{GEN x, GEN y} returns 1 (true) if \kbd{x} is equal
+to~\kbd{y}, 0~otherwise. A priori, this makes sense only if \kbd{x} and
+\kbd{y} have the same type, in which case they are recursively compared
+componentwise. When the types are different, a \kbd{true} result
+means that \kbd{x - y} was successfully computed and that
+\kbd{gequal0} found it equal to $0$. In particular
+\bprog
+  gequal(cgetg(1, t_VEC), gen_0)
+ at eprog\noindent is true, and the relation is not transitive. E.g.~an empty
+\typ{COL} and an empty \typ{VEC} are not equal but are both equal to
+\kbd{gen\_0}.
+
+\fun{long}{gidentical}{GEN x, GEN y} returns 1 (true) if \kbd{x} is identical
+to~\kbd{y}, 0~otherwise. In particular, the types and length of \kbd{x} and
+\kbd{y} must be equal. This test is much stricter than \tet{gequal}, in
+particular, \typ{REAL} with different accuracies are tested different. This
+relation is transitive.
+
+\subsec{Comparison with a small integer}
+
+\fun{int}{isexactzero}{GEN x} returns 1 (true) if \kbd{x} is exactly equal
+to~0 (including \typ{INTMOD}s like \kbd{Mod(0,2)}), and 0~(false) otherwise.
+This includes recursive objects, for instance vectors, whose components are $0$.
+
+\fun{int}{isrationalzero}{GEN x} returns 1 (true) if \kbd{x} is equal
+to an integer~0 (excluding \typ{INTMOD}s like \kbd{Mod(0,2)}), and 0~(false)
+otherwise. Contrary to \kbd{isintzero}, this includes recursive objects, for
+instance vectors, whose components are $0$.
+
+\fun{int}{ismpzero}{GEN x} returns 1 (true) if \kbd{x} is a \typ{INT} or
+a \typ{REAL} equal to~0.
+
+\fun{int}{isintzero}{GEN x} returns 1 (true) if \kbd{x} is a \typ{INT}
+equal to~0.
+
+\fun{int}{isint1}{GEN x} returns 1 (true) if \kbd{x} is a \typ{INT}
+equal to~1.
+
+\fun{int}{isintm1}{GEN x} returns 1 (true) if \kbd{x} is a \typ{INT}
+equal to~$-1$.
+
+\fun{int}{equali1}{GEN n}
+Assuming that \kbd{x} is a \typ{INT}, return 1 (true) if \kbd{x} is equal to
+$1$, and return 0~(false) otherwise.
+
+\fun{int}{equalim1}{GEN n}
+Assuming that \kbd{x} is a \typ{INT}, return 1 (true) if \kbd{x} is equal to
+$-1$, and return 0~(false) otherwise.
+
+\fun{int}{is_pm1}{GEN x}. Assuming that \kbd{x} is a
+\emph{non-zero} \typ{INT}, return 1 (true) if \kbd{x} is equal to $-1$ or
+$1$, and return 0~(false) otherwise.
+
+\fun{int}{gequal0}{GEN x} returns 1 (true) if \kbd{x} is equal to~0, 0~(false)
+otherwise.
+
+\fun{int}{gequal1}{GEN x} returns 1 (true) if \kbd{x} is equal to~1, 0~(false)
+otherwise.
+
+\fun{int}{gequalm1}{GEN x} returns 1 (true) if \kbd{x} is equal to~$-1$,
+0~(false) otherwise.
+
+
+\fun{long}{gcmpsg}{long s, GEN x}
+
+\fun{long}{gcmpgs}{GEN x, long s} comparison of \kbd{x} with the
+\kbd{long}~\kbd{s}.
+
+\fun{GEN}{gmaxsg}{long s, GEN x}
+
+\fun{GEN}{gmaxgs}{GEN x, long s} returns the largest of \kbd{x} and
+the \kbd{long}~\kbd{s} (converted to \kbd{GEN})
+
+\fun{GEN}{gminsg}{long s, GEN x}
+
+\fun{GEN}{gmings}{GEN x, long s} returns the smallest of \kbd{x} and the
+\kbd{long}~\kbd{s} (converted to \kbd{GEN})
+
+\fun{long}{gequalsg}{long s, GEN x}
+
+\fun{long}{gequalgs}{GEN x, long s} returns 1 (true) if \kbd{x} is equal to
+the \kbd{long}~\kbd{s}, 0~otherwise.
+
+\section{Miscellaneous Boolean functions}
+
+\fun{int}{isrationalzeroscalar}{GEN x} equivalent to, but faster than,
+\bprog
+  is_scalar_t(typ(x)) && isrationalzero(x)
+ at eprog
+
+\fun{int}{isinexact}{GEN x} returns 1 (true) if $x$ has an inexact
+component, and 0 (false) otherwise.
+
+\fun{int}{isinexactreal}{GEN x} return 1 if $x$ has an inexact
+\typ{REAL} component, and 0  otherwise.
+
+\fun{int}{isrealappr}{GEN x, long e} applies (recursively) to complex inputs;
+returns $1$ if $x$ is approximately real to the bit accuracy $e$, and 0
+otherwise. This means that any \typ{COMPLEX} component must have imaginary part
+$t$ satisfying $\kbd{gexpo}(t) < e$.
+
+\fun{int}{isint}{GEN x, GEN *n} returns 0 (false) if \kbd{x} does not round
+to an integer. Otherwise, returns 1 (true) and set \kbd{n} to the rounded
+value.
+
+\fun{int}{issmall}{GEN x, long *n} returns 0 (false) if \kbd{x} does not
+round to a small integer (suitable for \kbd{itos}). Otherwise, returns 1
+(true) and set \kbd{n} to the rounded value.
+
+\fun{long}{iscomplex}{GEN x} returns 1 (true) if \kbd{x} is a complex number
+(of component types embeddable into the reals) but is not itself real, 0~if
+\kbd{x} is a real (not necessarily of type \typ{REAL}), or raises an error if
+\kbd{x} is not embeddable into the complex numbers.
+
+\subsec{Obsolete}
+
+The following less convenient comparison functions and Boolean operators were
+used by the historical GP interpreter. They are provided for backward
+compatibility only and should not be used:
+
+\fun{GEN}{gle}{GEN x, GEN y}
+
+\fun{GEN}{glt}{GEN x, GEN y}
+
+\fun{GEN}{gge}{GEN x, GEN y}
+
+\fun{GEN}{ggt}{GEN x, GEN y}
+
+\fun{GEN}{geq}{GEN x, GEN y}
+
+\fun{GEN}{gne}{GEN x, GEN y}
+
+\fun{GEN}{gor}{GEN x, GEN y}
+
+\fun{GEN}{gand}{GEN x, GEN y}
+
+\fun{GEN}{gnot}{GEN x, GEN y}
+
+\section{Sorting}
+
+\subsec{Basic sort}
+
+\fun{GEN}{sort}{GEN x} sorts the vector \kbd{x} in ascending order using a
+mergesort algorithm, and \kbd{gcmp} as the underlying comparison routine
+(returns the sorted vector). This routine copies all components of $x$, use
+\kbd{gen\_sort\_inplace} for a more memory-efficient function.
+
+\fun{GEN}{lexsort}{GEN x}, as \kbd{sort}, using \kbd{lexcmp} instead of
+\kbd{gcmp} as the underlying comparison routine.
+
+\fun{GEN}{vecsort}{GEN x, GEN k}, as \kbd{sort}, but sorts the
+vector \kbd{x} in ascending \emph{lexicographic} order, according to the
+entries of the \typ{VECSMALL} \kbd{k}. For example,  if $\kbd{k} = [2,1,3]$,
+sorting will be done with respect to the second component,  and when these
+are  equal, with respect to the first,  and when these are equal,  with
+respect to the third.
+
+\subsec{Indirect sorting}
+
+\fun{GEN}{indexsort}{GEN x} as \kbd{sort}, but only returns the permutation
+which, applied to \kbd{x}, would sort the vector. The result is a
+\typ{VECSMALL}.
+
+\fun{GEN}{indexlexsort}{GEN x}, as \kbd{indexsort}, using \kbd{lexcmp}
+instead of \kbd{gcmp} as the underlying comparison routine.
+
+\fun{GEN}{indexvecsort}{GEN x, GEN k}, as \kbd{vecsort}, but only
+returns the permutation that would sort the vector \kbd{x}.
+
+\fun{long}{vecindexmin}{GEN x} returns the index for a maximal element of $x$
+(\typ{VEC}, \typ{COL} or \typ{VECSMALL}).
+
+\fun{long}{vecindexmax}{GEN x} returns the index for a maximal element of $x$
+(\typ{VEC}, \typ{COL} or \typ{VECSMALL}).
+
+\fun{long}{vecindexmax}{GEN x}
+
+\subsec{Generic sort and search} The following routines allow to use an
+arbitrary comparison function \kbd{int (*cmp)(void* data, GEN x, GEN y)},
+such that \kbd{cmp(data,x,y)} returns a negative result if $x
+< y$, a positive one if $x > y$ and 0 if $x = y$. The \kbd{data} argument is
+there in case your \kbd{cmp} requires additional context.
+
+\fun{GEN}{gen_sort}{GEN x, void *data, int (*cmp)(void *,GEN,GEN)}, as
+\kbd{sort}, with an explicit comparison routine.
+
+\fun{GEN}{gen_sort_uniq}{GEN x, void *data, int (*cmp)(void *,GEN,GEN)}, as
+\kbd{gen\_sort}, removing duplicate entries.
+
+\fun{GEN}{gen_indexsort}{GEN x, void *data, int (*cmp)(void*,GEN,GEN)},
+as \kbd{indexsort}.
+
+\fun{GEN}{gen_indexsort_uniq}{GEN x, void *data, int (*cmp)(void*,GEN,GEN)},
+as \kbd{indexsort}, removing duplicate entries.
+
+\fun{void}{gen_sort_inplace}{GEN x, void *data, int (*cmp)(void*,GEN,GEN), GEN
+*perm} sort \kbd{x} in place, without copying its components. If
+\kbd{perm} is non-\kbd{NULL}, it is set to the permutation that would sort
+the original \kbd{x}.
+
+\fun{GEN}{gen_setminus}{GEN A, GEN B, int (*cmp)(GEN,GEN)} given two sorted
+vectors $A$ and $B$, returns the vector of elements of $A$ not belonging to
+$B$.
+
+\fun{GEN}{sort_factor}{GEN y, void *data, int (*cmp)(void *,GEN,GEN)}:
+assuming \kbd{y} is a factorization matrix, sorts its rows in place (no copy
+is made) according to the comparison function \kbd{cmp} applied to its first
+column.
+
+\fun{GEN}{merge_sort_uniq}{GEN x,GEN y, void *data, int (*cmp)(void *,GEN,GEN)}
+assuming \kbd{x} and \kbd{y} are sorted vectors, with respect to the \kbd{cmp}
+comparison function, return a sorted concatenation, with duplicates removed.
+
+\fun{GEN}{merge_factor}{GEN fx, GEN fy, void *data, int (*cmp)(void *,GEN,GEN)}
+let \kbd{fx} and \kbd{fy} be factorization matrices for $X$ and $Y$
+sorted with respect to the comparison function \kbd{cmp} (see
+\tet{sort_factor}), returns the factorization of $X * Y$. Zero exponents in
+the latter factorization are preserved, e.g. when merging the factorization
+of $2$ and $1/2$, the result is $2^0$.
+
+\fun{long}{gen_search}{GEN v, GEN y, long flag, void *data, int
+(*cmp)(void*,GEN,GEN)}.\hfil\break
+Let \kbd{v} be a vector sorted according to \kbd{cmp(data,a,b)}; look for an
+index $i$ such that  \kbd{v[$i$]} is equal to \kbd{y}. \kbd{flag} has the
+same meaning as in \kbd{setsearch}: if \kbd{flag} is 0, return $i$ if it
+exists and 0 otherwise; if \kbd{flag} is non-zero, return $0$ if $i$ exists
+and the index where \kbd{y} should be inserted otherwise.
+
+\fun{long}{tablesearch}{GEN T, GEN x, int (*cmp)(GEN,GEN)} is a faster
+implementation for the common case \kbd{gen\_search(T,x,0,cmp,cmp\_nodata)}.
+
+\subsec{Further useful comparison functions}
+
+\fun{int}{cmp_universal}{GEN x, GEN y} a somewhat arbitrary universal
+comparison function, devoid of sensible mathematical meaning. It is
+transitive, and returns 0 if and only if \kbd{gidentical(x,y)} is true.
+Useful to sort and search vectors of arbitrary data.
+
+\fun{int}{cmp_nodata}{void *data, GEN x, GEN y}. This function is a hack
+used to pass an existing basic comparison function lacking the \kbd{data}
+argument, i.e. with prototype \kbd{int (*cmp)(GEN x, GEN y)}. Instead of
+\kbd{gen\_sort(x, NULL, cmp)} which may or may not work depending on how your
+compiler handles typecasts between incompatible function pointers, one should
+use \kbd{gen\_sort(x, (void*)cmp, cmp\_nodata)}.
+
+Here are a few basic comparison functions, to be used with \kbd{cmp\_nodata}:
+
+\fun{int}{ZV_cmp}{GEN x, GEN y} compare two \kbd{ZV}, which we assume have
+the same length (lexicographic order).
+
+\fun{int}{cmp_RgX}{GEN x, GEN y} compare two polynomials, which we assume
+have the same main variable (lexicographic order). The coefficients are
+compared using \kbd{gcmp}.
+
+\fun{int}{cmp_prime_over_p}{GEN x, GEN y} compare two prime ideals, which
+we assume divide the same prime number. The comparison is ad hoc but orders
+according to increasing residue degrees.
+
+\fun{int}{cmp_prime_ideal}{GEN x, GEN y} compare two prime ideals in the same
+\var{nf}. Orders by increasing primes, breaking ties using
+\kbd{cmp\_prime\_over\_p}.
+
+Finally a more elaborate comparison function:
+
+\fun{int}{gen_cmp_RgX}{void *data, GEN x, GEN y} compare two polynomials,
+ordering first by increasing degree, then according to the coefficient
+comparison function:
+\bprog
+  int (*cmp_coeff)(GEN,GEN) = (int(*)(GEN,GEN)) data;
+ at eprog
+
+\section{Divisibility, Euclidean division}
+
+\fun{GEN}{gdivexact}{GEN x, GEN y} returns the quotient $\kbd{x} / \kbd{y}$,
+assuming $\kbd{y}$ divides $\kbd{x}$. Not stack clean if $y = 1$
+(we return $x$, not a copy).
+
+\fun{int}{gdvd}{GEN x, GEN y}  returns 1 (true) if \kbd{y} divides~\kbd{x},
+0~otherwise.
+
+\fun{GEN}{gdiventres}{GEN x, GEN y} creates a 2-component vertical
+vector whose components are the true Euclidean quotient and remainder
+of \kbd{x} and~\kbd{y}.
+
+\fun{GEN}{gdivent[z]}{GEN x, GEN y[, GEN z]} yields the true Euclidean
+quotient of \kbd{x} and the \typ{INT} or \typ{POL}~\kbd{y}.
+
+\fun{GEN}{gdiventsg}{long s, GEN y[, GEN z]}, as \kbd{gdivent}
+except that \kbd{x} is a \kbd{long}.
+
+\fun{GEN}{gdiventgs[z]}{GEN x, long s[, GEN z]}, as \kbd{gdivent}
+except that \kbd{y} is a \kbd{long}.
+
+\fun{GEN}{gmod[z]}{GEN x, GEN y[, GEN z]} yields the remainder of \kbd{x}
+modulo the \typ{INT} or \typ{POL}~\kbd{y}. A \typ{REAL} or \typ{FRAC} \kbd{y}
+is also allowed, in which case the remainder is the unique real $r$ such that
+$0 \leq r < |\kbd{y}|$ and $\kbd{y} = q\kbd{x} + r$ for some (in fact unique)
+integer $q$.
+
+\fun{GEN}{gmodsg}{long s, GEN y[, GEN z]} as \kbd{gmod}, except \kbd{x} is
+a \kbd{long}.
+
+\fun{GEN}{gmodgs}{GEN x, long s[, GEN z]} as \kbd{gmod}, except \kbd{y} is
+a \kbd{long}.
+
+\fun{GEN}{gdivmod}{GEN x, GEN y, GEN *r} If \kbd{r} is not equal to
+\kbd{NULL} or \kbd{ONLY\_REM}, creates the (false) Euclidean quotient of
+\kbd{x} and~\kbd{y}, and puts (the address of) the remainder into~\kbd{*r}.
+If \kbd{r} is equal to \kbd{NULL}, do not create the remainder, and if
+\kbd{r} is equal to \kbd{ONLY\_REM}, create and output only the remainder.
+The remainder is created after the quotient and can be disposed of
+individually with a \kbd{cgiv(r)}.
+
+\fun{GEN}{poldivrem}{GEN x, GEN y, GEN *r} same as \key{gdivmod} but
+specifically for \typ{POL}s~\kbd{x} and~\kbd{y}, not necessarily in the same
+variable. Either of \kbd{x} and \kbd{y} may also be scalars, treated as
+polynomials of degree $0$.
+
+\fun{GEN}{gdeuc}{GEN x, GEN y} creates the Euclidean quotient of the
+\typ{POL}s~\kbd{x} and~\kbd{y}. Either of \kbd{x} and \kbd{y} may also be
+scalars, treated as polynomials of degree $0$.
+
+\fun{GEN}{grem}{GEN x, GEN y} creates the Euclidean remainder of the
+\typ{POL}~\kbd{x} divided by the \typ{POL}~\kbd{y}. Either of \kbd{x} and
+\kbd{y} may also be scalars, treated as polynomials of degree $0$.
+
+
+\fun{GEN}{gdivround}{GEN x, GEN y} if \kbd{x} and \kbd{y} are \typ{INT},
+as \kbd{diviiround}. Operate componentwise if \kbd{x} is
+a \typ{COL}, \typ{VEC} or \typ{MAT}. Otherwise as \key{gdivent}.
+
+\fun{GEN}{centermod_i}{GEN x, GEN y, GEN y2}, as \kbd{centermodii},
+componentwise.
+
+\fun{GEN}{centermod}{GEN x, GEN y}, as \kbd{centermod\_i}, except that
+\kbd{y2} is computed (and left on the stack for efficiency).
+
+\fun{GEN}{ginvmod}{GEN x, GEN y} creates the inverse of \kbd{x} modulo \kbd{y}
+when it exists. \kbd{y} must be of type \typ{INT} (in which case \kbd{x} is
+of type \typ{INT}) or \typ{POL} (in which case \kbd{x} is either a scalar
+type or a \typ{POL}).
+
+\section{GCD, content and primitive part}
+
+\subsec{Generic}
+
+\fun{GEN}{resultant}{GEN x, GEN y} creates the resultant of the \typ{POL}s
+\kbd{x} and~\kbd{y} computed using Sylvester's matrix (inexact inputs), a
+modular algorithm (inputs in $\Q[X]$) or the subresultant algorithm, as
+optimized by Lazard and Ducos. Either of \kbd{x} and \kbd{y} may also be
+scalars (treated as polynomials of degree $0$)
+
+\fun{GEN}{ggcd}{GEN x, GEN y} creates the GCD of \kbd{x} and~\kbd{y}.
+
+\fun{GEN}{glcm}{GEN x, GEN y} creates the LCM of \kbd{x} and~\kbd{y}.
+
+\fun{GEN}{gbezout}{GEN x,GEN y, GEN *u,GEN *v} returns the GCD of \kbd{x}
+and~\kbd{y}, and puts (the addresses of) objects $u$ and~$v$ such that
+$u\kbd{x}+v\kbd{y}=\gcd(\kbd{x},\kbd{y})$ into \kbd{*u} and~\kbd{*v}.
+
+\fun{GEN}{subresext}{GEN x, GEN y, GEN *U, GEN *V} returns the resultant
+of \kbd{x} and~\kbd{y}, and puts (the addresses of) polynomials $u$ and~$v$
+such that $u\kbd{x}+v\kbd{y}=\text{Res}(\kbd{x},\kbd{y})$ into \kbd{*U}
+and~\kbd{*V}.
+
+\fun{GEN}{content}{GEN x} returns the GCD of all the components of~\kbd{x}.
+
+\fun{GEN}{primitive_part}{GEN x, GEN *c} sets \kbd{c} to \kbd{content(x)}
+and returns the primitive part \kbd{x} / \kbd{c}. A trivial content is set to
+\kbd{NULL}.
+
+\fun{GEN}{primpart}{GEN x} as above but the content is lost.
+(For efficiency, the content remains on the stack.)
+
+\subsec{Over the rationals}
+
+\fun{long}{Q_pval}{GEN x, GEN p} valuation at the \typ{INT} \kbd{p}
+of the \typ{INT} or \typ{FRAC}~\kbd{x}.
+
+\fun{long}{Q_pvalrem}{GEN x, GEN p, GEN *r} returns the valuation $e$ at the
+\typ{INT} \kbd{p} of the \typ{INT} or \typ{FRAC}~\kbd{x}. The quotient
+$\kbd{x}/\kbd{p}^{e}$ is returned in~\kbd{*r}.
+
+\fun{GEN}{Q_abs}{GEN x} absolute value of the \typ{INT} or
+\typ{FRAC}~\kbd{x}.
+
+\fun{GEN}{Q_abs_shallow}{GEN x} $x$ being a \typ{INT} or a \typ{FRAC}, returns
+a shallow copy of $|x|$, in particular returns $x$ itself when $x \geq 0$, and
+\kbd{gneg($x$)} otherwise.
+
+\fun{GEN}{Q_gcd}{GEN x, GEN y} gcd of the \typ{INT} or \typ{FRAC}~\kbd{x}
+and~\kbd{y}.
+\smallskip
+
+In the following functions, arguments belong to a $M\otimes_\Z\Q$
+for some natural $\Z$-module $M$, e.g. multivariate polynomials with integer
+coefficients (or vectors/matrices recursively built from such objects), and
+an element of $M$ is said to be \emph{integral}.
+We are interested in contents, denominators, etc. with respect to this
+canonical integral structure; in particular, contents belong to $\Q$,
+denominators to $\Z$. For instance the $\Q$-content of $(1/2)xy$ is $(1/2)$,
+and its $\Q$-denominator is $2$, whereas \kbd{content} would return $y/2$ and
+\kbd{denom}~1.
+
+\fun{GEN}{Q_content}{GEN x} the $\Q$-content of $x$
+
+\fun{GEN}{Q_denom}{GEN x} the $\Q$-denominator of $x$. Shallow function.
+
+\fun{GEN}{Q_primitive_part}{GEN x, GEN *c} sets \kbd{c} to the $\Q$-content
+of \kbd{x} and returns \kbd{x / c}, which is integral.
+
+\fun{GEN}{Q_primpart}{GEN x} as above but the content is lost. (For
+efficiency, the content remains on the stack.)
+
+\fun{GEN}{Q_remove_denom}{GEN x, GEN *ptd} sets \kbd{d} to the
+$\Q$-denominator of \kbd{x} and returns \kbd{x * d}, which is integral.
+Shallow function.
+
+\fun{GEN}{Q_div_to_int}{GEN x, GEN c} returns \kbd{x / c}, assuming $c$
+is a rational number (\typ{INT} or \typ{FRAC}) and the result is integral.
+
+\fun{GEN}{Q_mul_to_int}{GEN x, GEN c} returns \kbd{x * c}, assuming $c$
+is a rational number (\typ{INT} or \typ{FRAC}) and the result is integral.
+
+\fun{GEN}{Q_muli_to_int}{GEN x, GEN d} returns \kbd{x * c}, assuming $c$
+is a \typ{INT} and the result is integral.
+
+\fun{GEN}{mul_content}{GEN cx, GEN cy}  \kbd{cx} and \kbd{cy} are
+as set by \kbd{primitive\_part}: either a \kbd{GEN} or \kbd{NULL}
+representing the trivial content $1$. Returns their product (either a
+\kbd{GEN} or \kbd{NULL}).
+
+\fun{GEN}{mul_denom}{GEN dx, GEN dy} \kbd{dx} and \kbd{dy} are
+as set by \kbd{Q\_remove\_denom}: either a \typ{INT} or \kbd{NULL} representing
+the trivial denominator $1$. Returns their product (either a \typ{INT} or
+\kbd{NULL}).
+
+\section{Generic arithmetic operators}
+
+\subsec{Unary operators}
+
+\fun{GEN}{gneg[z]}{GEN x[, GEN z]} yields $-\kbd{x}$.
+
+\fun{GEN}{gneg_i}{GEN x} shallow function yielding $-\kbd{x}$.
+
+\fun{GEN}{gabs[z]}{GEN x[, GEN z]} yields $|\kbd{x}|$.
+
+\fun{GEN}{gsqr}{GEN x} creates the square of~\kbd{x}.
+
+\fun{GEN}{ginv}{GEN x} creates the inverse of~\kbd{x}.
+
+\subsec{Binary operators}
+
+Let ``\op'' be a binary operation among
+
+\op=\key{add}: addition (\kbd{x + y}).
+
+\op=\key{sub}: subtraction (\kbd{x - y}).
+
+\op=\key{mul}: multiplication (\kbd{x * y}).
+
+\op=\key{div}: division (\kbd{x / y}).
+
+\noindent The names and prototypes of the functions corresponding
+to \op\ are as follows:
+
+\funno{GEN}{g\op}{GEN x, GEN y}
+
+\funno{GEN}{g\op gs}{GEN x, long s}
+
+\funno{GEN}{g\op sg}{long s, GEN y}
+
+\noindent Explicitly
+
+\fun{GEN}{gadd}{GEN x, GEN y}, \fun{GEN}{gaddgs}{GEN x, long s},
+\fun{GEN}{gaddsg}{GEN s, GEN x}
+
+\fun{GEN}{gmul}{GEN x, GEN y}, \fun{GEN}{gmulgs}{GEN x, long s},
+\fun{GEN}{gmulsg}{GEN s, GEN x}
+
+\fun{GEN}{gsub}{GEN x, GEN y}, \fun{GEN}{gsubgs}{GEN x, long s},
+\fun{GEN}{gsubsg}{GEN s, GEN x}
+
+\fun{GEN}{gdiv}{GEN x, GEN y}, \fun{GEN}{gdivgs}{GEN x, long s},
+\fun{GEN}{gdivsg}{GEN s, GEN x}
+
+
+\fun{GEN}{gpow}{GEN x, GEN y, long l} creates $\kbd{x}^{\kbd{y}}$. If
+\kbd{y} is a \typ{INT}, return \kbd{powgi(x,y)} (the precision \kbd{l} is not
+taken into account). Otherwise, the result is $\exp(\kbd{y}*\log(\kbd{x}))$
+where exact arguments are converted to floats of precision~\kbd{l} in case of
+need; if there is no need, for instance if $x$ is a \typ{REAL}, $l$ is
+ignored. Indeed, if $x$ is a \typ{REAL}, the accuracy of $\log x$ is
+determined from the accuracy of $x$, it is no problem to multiply by $y$,
+even if it is an exact type, and the accuracy of the exponential is
+determined, exactly as in the case of the initial $\log x$.
+
+\fun{GEN}{gpowgs}{GEN x, long n} creates $\kbd{x}^{\kbd{n}}$ using
+binary powering. To treat the special case $n = 0$, we consider
+\kbd{gpowgs} as a series of \kbd{gmul}, so we follow the rule of returning
+result which is as exact as possible given the input. More precisely,
+we return
+\item \kbd{gen\_1} if $x$ has type \typ{INT}, \typ{REAL},  \typ{FRAC}, or
+\typ{PADIC}
+
+\item \kbd{Mod(1,N)} if $x$ is a \typ{INTMOD} modulo $N$.
+
+\item \kbd{gen\_1} for \typ{COMPLEX}, \typ{QUAD} unless one component
+is a \typ{INTMOD}, in which case we return \kbd{Mod(1, N)} for a suitable
+$N$ (the gcd of the moduli that appear).
+
+\item \kbd{FF\_1}$(x)$ for a \typ{FFELT}.
+
+\item \kbd{RgX\_get\_1}$(x)$ for a \typ{POL}.
+
+\item \kbd{qfi\_1}$(x)$ and \kbd{qfr\_1}$(x)$ for \typ{QFI} and \typ{QFR}.
+
+\item the identity permutation for \typ{VECSMALL}.
+
+\item etc. Of course, the only practical use of this routine for $n = 0$ is
+to obtain the multiplicative neutral element in the base ring (or to treat
+marginal cases that should be special cased anyway if there is the slightest
+doubt about what the result should be).
+
+\fun{GEN}{powgi}{GEN x, GEN y} creates $\kbd{x}^{\kbd{y}}$, where \kbd{y} is a
+\typ{INT}, using left-shift binary powering. The case where $y = 0$
+(as all cases where $y$ is small) is handled by \kbd{gpowgs(x, 0)}.
+
+In addition we also have the obsolete forms:
+
+\fun{void}{gaddz}{GEN x, GEN y, GEN z}
+
+\fun{void}{gsubz}{GEN x, GEN y, GEN z}
+
+\fun{void}{gmulz}{GEN x, GEN y, GEN z}
+
+\fun{void}{gdivz}{GEN x, GEN y, GEN z}
+
+\section{Generic operators: product, powering, factorback}
+
+\fun{GEN}{divide_conquer_prod}{GEN v, GEN (*mul)(GEN,GEN)} $v$ is a vector of
+objects, which can be ``multiplied'' using the \kbd{mul} function. Return
+the ``product'' of the $v[i]$ using a product tree: by convention
+return \kbd{gen\_1} if $v$ is the empty vector, a copy of $v[1]$ if it has a
+single entry; and otherwise apply the function recursively on the vector
+(twice smaller)
+
+\kbd{mul}$(v[1],v[2])$, \kbd{mul}$(v[3],v[4])$, \dots
+
+\noindent Only requires that \kbd{mul} is an associative binary operator,
+which need not correspond to a true multiplication. \kbd{D} is meant to encode
+an arbitrary evaluation context, set it to \kbd{NULL} in simple cases where you
+do not need this. Leaves some garbage on stack, but suitable for
+\kbd{gerepileupto} if \kbd{mul} is.
+
+To describe the following functions, we use the following private typedefs
+to simplify the description:
+\bprog
+  typedef (*F0)(void *);
+  typedef (*F1)(void *, GEN);
+  typedef (*F2)(void *, GEN, GEN);
+ at eprog
+\noindent They correspond to generic functions with one and two arguments
+respectively (the \kbd{void*} argument provides some arbitrary evaluation
+context).
+
+\fun{GEN}{divide_conquer_assoc}{GEN v, void *D, F2 op}
+general version of \tet{divide_conquer_prod}. Given two objects
+$x,y$, assume that \kbd{op(D, $x$, $y$)} implements an associative binary
+operator. If $v$ has $k$ entries, return
+$$v[1]~\var{op}~v[2]~\var{op}~\ldots ~\var{op}~v[k];$$
+returns \kbd{gen\_1} if $k = 0$ and a copy of $v[1]$ if $k = 1$.
+
+\fun{GEN}{gen_pow}{GEN x, GEN n, void *D, F1 sqr, F2 mul} $n > 0$ a
+\typ{INT}, returns $x^n$; \kbd{mul(D, $x$, $y$)} implements the multiplication
+in the underlying monoid; \kbd{sqr} is a (presumably optimized) shortcut for
+\kbd{mul(D, $x$, $x$)}.
+
+\fun{GEN}{gen_powu}{GEN x, ulong n, void *D, F1 sqr, F2 mul} $n > 0$,
+returns $x^n$. See \tet{gen_pow}.
+
+\fun{GEN}{gen_pow_i}{GEN x, GEN n, void *E, F1 sqr, F2 mul}
+internal variant of \tet{gen_pow}, not memory-clean.
+
+\fun{GEN}{gen_powu_i}{GEN x, ulong n, void *E, F1 sqr, F2 mul}
+internal variant of \tet{gen_powu}, not memory-clean.
+
+\fun{GEN}{gen_pow_fold}{GEN x, GEN n, void *D, F1 sqr, F1 msqr} variant
+of \tet{gen_pow}, where \kbd{mul} is replaced by \kbd{msqr}, with
+\kbd{msqr(D, $y$)} returning $xy^2$. In particular \kbd{D} must implicitly
+contain $x$.
+
+\fun{GEN}{gen_pow_fold_i}{GEN x, GEN n, void *E, F1 sqr, F1 msqr}
+internal variant of the function \tet{gen_pow_fold}, not memory-clean.
+
+\fun{GEN}{gen_powu_fold}{GEN x, ulong n, void *D, F1 sqr, F1 msqr}, see
+\tet{gen_pow_fold}.
+
+\fun{GEN}{gen_powu_fold_i}{GEN x, ulong n, void *E, F1 sqr, F1 msqr}
+see \tet{gen_pow_fold_i}.
+
+\fun{GEN}{gen_powers}{GEN x, long n, long usesqr, void *D, F1 sqr, F2 mul, F0 one}
+returns $[\kbd{x}^0, \dots, \kbd{x}^\kbd{n}]$ as a \typ{VEC}; \kbd{mul(D,
+$x$, $y$)} implements the multiplication in the underlying monoid; \kbd{sqr}
+is a (presumably optimized) shortcut for \kbd{mul(D, $x$, $x$)}; \kbd{one}
+returns the monoid unit. The flag \kbd{usesqr} should be set to $1$ if
+squaring are faster than multiplication by $x$.
+
+\fun{GEN}{gen_factorback}{GEN L, GEN e, F2 mul, F2 pow, void *D} generic form
+of \tet{factorback}. The pair $[L,e]$ is of the form
+
+\item \kbd{[fa, NULL]}, \kbd{fa} a two-column factorization matrix: expand it.
+
+\item  \kbd{[v, NULL]}, $v$ a vector of objects: return their
+product.
+
+\item or \kbd{[v, e]},  $v$ a vector of objects, $e$ a vector of integral
+exponents: return the product of the $v[i]^{e[i]}$.
+
+\noindent \kbd{mul(D, $x$, $y$)} and \kbd{pow(D, $x$, $n$)}
+return $xy$ and $x^n$ respectively.
+
+\section{Matrix and polynomial norms} This section concerns only standard norms
+of $\R$ and $\C$ vector spaces, not algebraic norms given by the determinant of
+some multiplication operator. We have already seen type-specific functions like
+\tet{ZM_supnorm} or \tet{RgM_fpnorml2} and limit ourselves to generic functions
+assuming nothing about their \kbd{GEN} argument; these functions allow
+the following scalar types: \typ{INT}, \typ{FRAC}, \typ{REAL}, \typ{COMPLEX},
+\typ{QUAD} and are defined recursively (in terms of norms of their components)
+for the following ``container'' types: \typ{POL}, \typ{VEC}, \typ{COL} and
+\typ{MAT}. They raise an error if some other type appears in the argument.
+
+\fun{GEN}{gnorml2}{GEN x} The norm of a scalar is the square of its complex
+modulus, the norm of a recursive type is the sum of the norms of its components.
+For polynomials, vectors or matrices of complex numbers one recovers the
+\emph{square} of the usual $L^2$ norm. In most applications, the missing square
+root computation can be skipped.
+
+\fun{GEN}{gnorml1}{GEN x, long prec} The norm of a scalar is its complex
+modulus, the norm of a recursive type is the sum of the norms of its components.
+For polynomials, vectors or matrices of complex numbers one recovers the
+the usual $L^1$ norm. One must include a real precision \kbd{prec} in case
+the inputs include \typ{COMPLEX} or \typ{QUAD} with exact rational components:
+a square root must be computed and we must choose an accuracy.
+
+\fun{GEN}{gnorml1_fake}{GEN x} as \tet{gnorml1}, except that the norm
+of a \typ{QUAD} $x + wy$ or \typ{COMPLEX} $x + Iy$ is defined as
+$|x| + |y|$, where we use the ordinary real absolute value. This is still a norm
+of $\R$ vector spaces, which is easier to compute than
+\kbd{gnorml1} and can often be used in its place.
+
+\fun{GEN}{gsupnorm}{GEN x, long prec} The norm of a scalar is its complex
+modulus, the norm of a recursive type is the max of the norms of its
+components. A precision \kbd{prec} must be included for the same reason as in
+\kbd{gnorml1}.
+
+\fun{void}{gsupnorm_aux}{GEN x, GEN *m, GEN *m2, long prec}
+Low-level function underlying
+\kbd{gsupnorm}, used as follows:
+\bprog
+  GEN m = NULL, m2 = NULL;
+  gsupnorm_aux(x, &m, &m2);
+ at eprog
+After the call, the sup norm of $x$ is the min of \kbd{m} and the square root
+of \kbd{m2};  one or both of \kbd{m}, \kbd{m2} may be \kbd{NULL}, in
+which case it must be omitted. You may initially set \kbd{m} and \kbd{m2} to
+non-\kbd{NULL} values, in which case, the above procedure yields the max of
+(the initial) \kbd{m}, the square root of (the initial) \kbd{m2}, and the sup
+norm of $x$.
+
+The strange interface is due to the fact that $|z|^2$ is easier to compute
+than $|z|$ for a \typ{QUAD} or \typ{COMPLEX} $z$: \kbd{m2} is the max of
+those $|z|^2$, and \kbd{m} is the max of the other $|z|$.
+
+\section{Substitution and evaluation}
+
+\fun{GEN}{gsubst}{GEN x, long v, GEN y} substitutes the object \kbd{y}
+into~\kbd{x} for the variable number~\kbd{v}.
+
+\fun{GEN}{poleval}{GEN q, GEN x} evaluates the \typ{POL} or \typ{RFRAC}
+$q$ at $x$. For convenience, a \typ{VEC} or \typ{COL} is also recognized as
+the \typ{POL} \kbd{gtovecrev(q)}.
+
+\fun{GEN}{RgX_RgM_eval}{GEN q, GEN x} evaluates the \typ{POL} $q$ at the
+square matrix $x$.
+
+\fun{GEN}{RgX_RgMV_eval}{GEN f, GEN V} returns
+the evaluation $\kbd{f}(\kbd{x})$, assuming that \kbd{V} was computed by
+$\kbd{FpXQ\_powers}(\kbd{x}, n)$ for some $n>1$.
+
+\fun{GEN}{RgX_RgM_eval_col}{GEN q, GEN x, long c} evaluates the \typ{POL} $q$
+at the square matrix $x$ but only returns the \kbd{c}-th column of the result.
+
+\fun{GEN}{qfeval}{GEN q, GEN x} evaluates the quadratic form
+$q$ (symmetric matrix) at $x$ (column vector of compatible dimensions).
+
+\fun{GEN}{qfevalb}{GEN q, GEN x, GEN y} evaluates the polar bilinear form
+associated to the quadratic form $q$ (symmetric matrix) at $x$, $y$ (column
+vectors of compatible dimensions).
+
+\fun{GEN}{hqfeval}{GEN q, GEN x} evaluates the Hermitian form $q$
+(a Hermitian complex matrix) at $x$.
+
+\fun{GEN}{qf_apply_RgM}{GEN q, GEN M} $q$ is a symmetric $n\times n$ matrix,
+$M$ an $n\times k$ matrix, return $M' q M$.
+
+\fun{GEN}{qf_apply_ZM}{GEN q, GEN M} as above assuming that both
+$q$ and $M$ have integer entries.
+
+\newpage
+\chapter{Miscellaneous mathematical functions}
+
+\section{Fractions}
+
+\fun{GEN}{absfrac}{GEN x} returns the absolute value of the \typ{FRAC} $x$.
+
+\fun{GEN}{absfrac_shallow}{GEN x} $x$ being a \typ{FRAC}, returns a shallow
+copy of $|x|$, in particular returns $x$ itself when $x \geq 0$, and
+\kbd{gneg($x$)} otherwise.
+
+\fun{GEN}{sqrfrac}{GEN x} returns the square of the \typ{FRAC} $x$.
+
+\section{Complex numbers}
+
+\fun{GEN}{imag}{GEN x} returns a copy of the imaginary part of \kbd{x}.
+
+\fun{GEN}{real}{GEN x} returns a copy of the real part of \kbd{x}. If \kbd{x}
+is a \typ{QUAD}, returns the coefficient of $1$ in the ``canonical'' integral
+basis $(1,\omega)$.
+
+The last two functions are shallow, and not suitable for \tet{gerepileupto}:
+
+\fun{GEN}{imag_i}{GEN x} as \kbd{gimag}, returns a pointer to the imaginary
+part.
+\fun{GEN}{real_i}{GEN x} as \kbd{greal}, returns a pointer to the real part.
+
+\fun{GEN}{mulreal}{GEN x, GEN} returns the real part of $xy$;
+$x,y$ have type \typ{INT}, \typ{FRAC}, \typ{REAL} or \typ{COMPLEX}. See also
+\kbd{RgM\_mulreal}.
+
+\fun{GEN}{cxnorm}{GEN x} norm of the \typ{COMPLEX} $x$ (modulus squared).
+
+\fun{GEN}{cxexpm1}{GEN x} returns $\exp(x)-1$, for a \typ{COMPLEX} $x$.
+
+\section{Quadratic numbers and binary quadratic forms}
+
+\fun{GEN}{quad_disc}{GEN x} returns the discriminant of the \typ{QUAD} $x$.
+
+\fun{GEN}{quadnorm}{GEN x} norm of the \typ{QUAD} $x$.
+
+\fun{GEN}{qfb_disc}{GEN x} returns the discriminant of the \typ{QFI}
+or \typ{QFR} \kbd{x}.
+
+\fun{GEN}{qfb_disc3}{GEN x, GEN y, GEN z} returns $y^2 - 4xz$ assuming all
+inputs are \typ{INT}s. Not stack-clean.
+
+\section{Polynomials}\label{se:polynomials}
+
+\fun{GEN}{truecoeff}{GEN x, long n} returns \kbd{polcoeff0(x,n, -1)}, i.e.
+the coefficient of the term of degree \kbd{n} in the main variable.
+
+\fun{GEN}{polcoeff_i}{GEN x, long n, long v} internal shallow function. Rewrite
+$x$ as a Laurent polynomial in the variable $v$ and returns its coefficient
+of degree $n$ (\kbd{gen\_0} if this falls outside the coefficient array).
+Allow \typ{POL}, \typ{SER}, \typ{RFRAC} and scalars.
+
+\fun{long}{degree}{GEN x} returns \kbd{poldegree(x, -1)}, the degree of
+\kbd{x} with respect to its main variable, with the usual meaning if the
+leading coefficient of $x$ is non-zero. If the sign of $x$ is $0$, this
+function always returns $-1$. Otherwise, we return the index of the leading
+coefficient of $x$, i.e. the coefficient of largest index stored in $x$.
+For instance the ``degrees'' of
+\bprog
+  0. E-38 * x^4 + 0.E-19 * x + 1
+  Mod(0,2) * x^0    \\ sign is 0 !
+ at eprog\noindent are $4$ and $-1$ respectively.
+
+\fun{long}{degpol}{GEN x} is a simple macro returning \kbd{lg(x) - 3}.
+This is the degree of the \typ{POL}~\kbd{x} with respect to its main
+variable, \emph{if} its leading coefficient is non-zero (a rational $0$ is
+impossible, but an inexact $0$ is allowed, as well as an exact modular $0$,
+e.g. \kbd{Mod(0,2)}). If $x$ has no coefficients (rational $0$ polynomial),
+its length is $2$ and we return the expected $-1$.
+
+\fun{GEN}{characteristic}{GEN x} returns the characteristic of the
+base ring over which the polynomial is defined (as defined by \typ{INTMOD}
+and \typ{FFELT} components). The function raises an exception if incompatible
+primes arise from \typ{FFELT} and \typ{PADIC} components. Shallow function.
+
+\fun{GEN}{residual_characteristic}{GEN x} returns a kind of ``residual
+characteristic'' of the base ring over which the polynomial is defined. This
+is defined as the gcd of all moduli \typ{INTMOD}s occurring in the structure,
+as well as primes $p$ arising from \typ{PADIC}s or \typ{FFELT}s. The function
+raises an exception if incompatible primes arise from \typ{FFELT} and
+\typ{PADIC} components. Shallow function.
+
+\fun{GEN}{resultant}{GEN x,GEN y} resultant of \kbd{x} and \kbd{y}, with respect
+to the main variable of highest priority. Uses either
+the subresultant algorithm (generic case), a modular algorithm (inputs in
+$\Q[X]$) or Sylvester's matrix (inexact inputs).
+
+\fun{GEN}{resultant2}{GEN x, GEN y} resultant of \kbd{x} and \kbd{y}, with
+respect to the main variable of highest priority. Computes the determinant
+of Sylvester's matrix.
+
+\fun{GEN}{resultant_all}{GEN u, GEN v, GEN *sol} returns
+\kbd{resultant(x,y)}. If \kbd{sol} is not \kbd{NULL}, sets it to the last
+non-constant remainder in the polynomial remainder sequence if such a sequence
+was computed, and to \kbd{gen\_0} otherwise (e.g. polynomials of degree 0,
+$u,v$ in $\Q[X]$).
+
+\fun{GEN}{cleanroots}{GEN x, long prec} returns the complex roots of
+the complex polynomial $x$ (with coefficients \typ{INT}, \typ{FRAC},
+\typ{REAL} or \typ{COMPLEX} of the above). The roots are returned
+as \typ{REAL} or \typ{COMPLEX} of \typ{REAL}s of precision \kbd{prec}
+(guaranteeing a non-$0$ imaginary part). See \tet{QX_complex_roots}.
+
+\fun{GEN}{polmod_to_embed}{GEN x, long prec} return the vector of complex
+embeddings of the \typ{POLMOD} $x$ (with complex coefficients). Shallow
+function, simple complex variant of \tet{conjvec}.
+
+\section{Power series}
+
+\fun{GEN}{derivser}{GEN x} returns the derivative of the \typ{SER} \kbd{x}
+with respect to its main variable.
+
+\fun{GEN}{integser}{GEN x} returns the primitive of the \typ{SER} \kbd{x}
+with respect to its main variable.
+
+\fun{GEN}{truecoeff}{GEN x, long n} returns \kbd{polcoeff0(x,n, -1)}, i.e.
+the coefficient of the term of degree \kbd{n} in the main variable.
+
+\fun{GEN}{ser_unscale}{GEN P, GEN h} return $P(h x)$, not memory clean.
+
+\fun{GEN}{ser_normalize}{GEN x} divide $x$ by its ``leading term'' so that
+the series is either $0$ or equal to $t^v(1+O(t))$. Shallow function if the
+``leading term'' is $1$.
+
+\section{Functions to handle \typ{FFELT}}
+These functions define the public interface of the \typ{FFELT} type to use in
+generic functions.  However, in specific functions, it is better to use the
+functions class \kbd{FpXQ} and/or \kbd{Flxq} as appropriate.
+
+\fun{GEN}{FF_p}{GEN a} returns the characteristic of the definition field of the
+\typ{FFELT} element \kbd{a}.
+
+\fun{long}{FF_f}{GEN a} returns the dimension of the definition field over
+its prime field; the cardinality of the dimension field is thus $p^f$.
+
+\fun{GEN}{FF_p_i}{GEN a} shallow version of \kbd{FF\_p}.
+
+\fun{GEN}{FF_q}{GEN a} returns the cardinal of the definition field of the
+\typ{FFELT} element \kbd{a}.
+
+\fun{GEN}{FF_mod}{GEN a} returns the polynomial (with reduced \typ{INT}
+coefficients) defining the finite field, in the variable used to display $a$.
+
+\fun{GEN}{FF_to_FpXQ}{GEN a} converts the \typ{FFELT} \kbd{a} to a polynomial
+$P$ with reduced \typ{INT} coefficients such that $a=P(g)$ where $g$ is the
+generator of the finite field returned by \kbd{ffgen}, in the variable used to
+display $g$.
+
+\fun{GEN}{FF_to_FpXQ_i}{GEN a} shallow version of \kbd{FF\_to\_FpXQ}.
+
+\fun{GEN}{FF_to_F2xq}{GEN a} converts the \typ{FFELT} \kbd{a} to a \kbd{F2x}
+$P$ such that $a=P(g)$ where $g$ is the generator of the finite field returned
+by \kbd{ffgen}, in the variable used to display $g$. This only work if the
+characteristic is $2$.
+
+\fun{GEN}{FF_to_F2xq_i}{GEN a} shallow version of \kbd{FF\_to\_F2xq}.
+
+\fun{GEN}{FF_to_Flxq}{GEN a} converts the \typ{FFELT} \kbd{a} to a \kbd{Flx}
+$P$ such that $a=P(g)$ where $g$ is the generator of the finite field returned
+by \kbd{ffgen}, in the variable used to display $g$. This only work if the
+characteristic is small enough.
+
+\fun{GEN}{FF_to_Flxq_i}{GEN a} shallow version of \kbd{FF\_to\_Flxq}.
+
+\fun{GEN}{p_to_FF}{GEN p, long v} returns a \typ{FFELT} equal to $1$ in the
+finite field $\Z/p\Z$. Useful for generic code that wants to handle
+(inefficiently) $\Z/p\Z$ as if it were not a prime field.
+
+\fun{GEN}{FF_1}{GEN a} returns the unity in the definition field of the
+\typ{FFELT} element \kbd{a}.
+
+\fun{GEN}{FF_zero}{GEN a} returns the zero element of the definition field of
+the \typ{FFELT} element \kbd{a}.
+
+\fun{int}{FF_equal0}{GEN a}, \fun{int}{FF_equal1}{GEN a},
+\fun{int}{FF_equalm1}{GEN a} returns $1$ if the \typ{FFELT} \kbd{a} is equal
+to $0$ (resp. $1$, resp. $-1$) else $0$.
+
+\fun{int}{FF_equal}{GEN a, GEN b} return $1$ if the \typ{FFELT} \kbd{a} and
+\kbd{b} have the same definition field and are equal, else $0$.
+
+\fun{int}{FF_samefield}{GEN a, GEN b} return $1$ if the \typ{FFELT} \kbd{a} and
+\kbd{b} have the same definition field, else $0$.
+
+\fun{int}{Rg_is_FF}{GEN c, GEN *ff} to be called successively on many objects,
+setting \kbd{*ff = NULL} (unset) initially. Returns $1$ as long as $c$ is a
+\typ{FFELT} defined over the same field as \kbd{*ff} (setting \kbd{*ff = c}
+if unset), and $0$ otherwise.
+
+\fun{int}{RgC_is_FFC}{GEN x, GEN *ff} apply \tet{Rg_is_FF} successively to all
+components of the \typ{VEC} or \typ{COL} $x$. Return $0$ if one call fails,
+and $1$ otherwise.
+
+\fun{int}{RgM_is_FFM}{GEN x, GEN *ff} apply \tet{Rg_is_FF} to all components
+of the \typ{MAT}. Return $0$ if one call fails, and $1$ otherwise.
+
+\fun{GEN}{FF_add}{GEN a, GEN b} returns $a+b$ where \kbd{a} and \kbd{b} are
+\typ{FFELT} having the same definition field.
+
+\fun{GEN}{FF_Z_add}{GEN a, GEN x} returns $a+x$, where \kbd{a} is a
+\typ{FFELT}, and \kbd{x} is a \typ{INT}, the computation being
+performed in the definition field of \kbd{a}.
+
+\fun{GEN}{FF_Q_add}{GEN a, GEN x} returns $a+x$, where \kbd{a} is a
+\typ{FFELT}, and \kbd{x} is a \typ{RFRAC}, the computation being
+performed in the definition field of \kbd{a}.
+
+\fun{GEN}{FF_sub}{GEN a, GEN b} returns $a-b$ where \kbd{a} and \kbd{b} are
+\typ{FFELT} having the same definition field.
+
+\fun{GEN}{FF_mul}{GEN a, GEN b} returns $a\*b$ where \kbd{a} and \kbd{b} are
+\typ{FFELT} having the same definition field.
+
+\fun{GEN}{FF_Z_mul}{GEN a, GEN b} returns $a\*b$, where \kbd{a} is a
+\typ{FFELT}, and \kbd{b} is a \typ{INT}, the computation being
+performed in the definition field of \kbd{a}.
+
+\fun{GEN}{FF_div}{GEN a, GEN b} returns $a/b$ where \kbd{a} and \kbd{b} are
+\typ{FFELT} having the same definition field.
+
+\fun{GEN}{FF_neg}{GEN a} returns $-a$ where \kbd{a} is a \typ{FFELT}.
+
+\fun{GEN}{FF_neg_i}{GEN a} shallow function returning $-a$ where \kbd{a} is a
+\typ{FFELT}.
+
+\fun{GEN}{FF_inv}{GEN a} returns $a^{-1}$ where \kbd{a} is a \typ{FFELT}.
+
+\fun{GEN}{FF_sqr}{GEN a} returns $a^2$ where \kbd{a} is a \typ{FFELT}.
+
+\fun{GEN}{FF_mul2n}{GEN a, long n} returns $a\*2^n$ where \kbd{a} is a
+\typ{FFELT}.
+
+\fun{GEN}{FF_pow}{GEN x, GEN n} returns $a^n$ where \kbd{a} is a \typ{FFELT}
+and\kbd{n} is a \typ{INT}.
+
+\fun{GEN}{FF_Z_Z_muldiv}{GEN a, GEN x, GEN y} returns $a\*y/z$, where \kbd{a}
+is a \typ{FFELT}, and \kbd{x} and \kbd{y} are \typ{INT}, the computation being
+performed in the definition field of \kbd{a}.
+
+\fun{GEN}{Z_FF_div}{GEN x, GEN a} return $x/a$ where \kbd{a} is a
+\typ{FFELT}, and \kbd{x} is a \typ{INT}, the computation being
+performed in the definition field of \kbd{a}.
+
+\fun{GEN}{FF_norm}{GEN a} returns the norm of the \typ{FFELT} \kbd{a} with
+respect to its definition field.
+
+\fun{GEN}{FF_trace}{GEN a} returns the trace of the \typ{FFELT} \kbd{a} with
+respect to its definition field.
+
+\fun{GEN}{FF_conjvec}{GEN a} returns the vector of conjugates
+$[a,a^p,a^{p^2},\ldots,a^{p^{n-1}}]$ where the \typ{FFELT} \kbd{a} belong to a
+field with $p^n$ elements.
+
+\fun{GEN}{FF_charpoly}{GEN a} returns the characteristic polynomial) of the
+\typ{FFELT} \kbd{a} with respect to its definition field.
+
+\fun{GEN}{FF_minpoly}{GEN a} returns the minimal polynomial of
+the \typ{FFELT} \kbd{a}.
+
+\fun{GEN}{FF_sqrt}{GEN a} returns an \typ{FFELT} $b$ such that $a=b^2$ if
+it exist, where \kbd{a} is a \typ{FFELT}.
+
+\fun{long}{FF_issquareall}{GEN x, GEN *pt} returns $1$ if \kbd{x} is a
+square, and $0$ otherwise. If \kbd{x} is indeed a square, set \kbd{pt} to its
+square root.
+
+\fun{long}{FF_issquare}{GEN x} returns $1$ if \kbd{x} is a square and $0$
+otherwise.
+
+\fun{long}{FF_ispower}{GEN x, GEN K, GEN *pt} Given $K$ a positive integer,
+returns $1$ if \kbd{x} is a $K$-th power, and $0$ otherwise. If \kbd{x} is
+indeed a $K$-th power, set \kbd{pt} to its $K$-th root.
+
+\fun{GEN}{FF_sqrtn}{GEN a, GEN n, GEN *zn} returns an \kbd{n}-th root of
+$\kbd{a}$ if it exist. If \kbd{zn} is non-\kbd{NULL} set it to a primitive
+\kbd{n}-th root of the unity.
+
+\fun{GEN}{FF_log}{GEN a, GEN g, GEN o} the \typ{FFELT} \kbd{g} being a
+generator for the definition field of the \typ{FFELT} \kbd{a}, returns a
+\typ{INT} $e$ such that $a^e=g$.  If $e$ does not exists, the result is
+currently undefined. If \kbd{o} is not \kbd{NULL} it is assumed to be a
+factorization of the multiplicative order of \kbd{g} (as set by
+\tet{FF_primroot})
+
+\fun{GEN}{FF_order}{GEN a, GEN o} returns the order of the \typ{FFELT} \kbd{a}.
+If \kbd{o} is non-\kbd{NULL}, it is assumed that \kbd{o} is a multiple of the
+order of \kbd{a}.
+
+\fun{GEN}{FF_primroot}{GEN a, GEN *o} returns a generator of the
+multiplicative group of the definition field of the \typ{FFELT} \kbd{a}.
+If \kbd{o} is not \kbd{NULL}, set it to the factorization of the order
+of the primitive root (to speed up \tet{FF_log}).
+
+\fun{GEN}{FFX_factor}{GEN f, GEN a} returns the factorization of the univariate
+polynomial \kbd{f} over the definition field of the \typ{FFELT} \kbd{a}. The
+coefficients of \kbd{f} must be of type \typ{INT}, \typ{INTMOD} or \typ{FFELT}
+and compatible with \kbd{a}.
+
+\fun{GEN}{FFX_roots}{GEN f, GEN a} returns the roots (\typ{FFELT})
+of the univariate polynomial \kbd{f} over the definition field of the
+\typ{FFELT} \kbd{a}. The coefficients of \kbd{f} must be of type \typ{INT},
+\typ{INTMOD} or \typ{FFELT} and compatible with \kbd{a}.
+
+\fun{GEN}{FFM_FFC_mul}{GEN M, GEN C, GEN ff} returns the product of
+the matrix~\kbd{M} (\typ{MAT}) and the column vector~\kbd{C}
+(\typ{COL}) over the finite field given by \kbd{ff} (\typ{FFELT}).
+
+\fun{GEN}{FFM_ker}{GEN M, GEN ff} returns the kernel of the \typ{MAT} \kbd{M}
+defined over the finite field given by the \typ{FFELT} \kbd{ff} (obtained
+by \tet{RgM_is_FFM(M,\&ff)}).
+
+\fun{GEN}{FFM_det}{GEN M, GEN ff}
+
+\fun{GEN}{FFM_image}{GEN M, GEN ff}
+
+\fun{GEN}{FFM_inv}{GEN M, GEN ff}
+
+\fun{GEN}{FFM_mul}{GEN M, GEN N, GEN ff} returns the product of the
+matrices \kbd{M} and~\kbd{N} (\typ{MAT}) over the finite field given
+by \kbd{ff} (\typ{FFELT}).
+
+\fun{long}{FFM_rank}{GEN M, GEN ff}
+
+\section{Transcendental functions}
+
+The following two functions are only useful when interacting with \kbd{gp},
+to manipulate its internal default precision (expressed as a number of
+decimal digits, not in words as used everywhere else):
+
+\fun{long}{getrealprecision}{void} returns \kbd{realprecision}.
+
+\fun{long}{setrealprecision}{long n, long *prec} sets the new
+\kbd{realprecision} to $n$, which is returned. As a side effect, set
+\kbd{prec} to the corresponding number of words \kbd{ndec2prec(n)}.
+
+\subsec{Transcendental functions with \typ{REAL} arguments}
+
+In the following routines, $x$ is assumed to be a \typ{REAL} and the result
+is a \typ{REAL} (sometimes a \typ{COMPLEX} with \typ{REAL} components), with
+the largest accuracy which can be deduced from the input. The naming scheme
+is inconsistent here, since we sometimes use the prefix \kbd{mp} even though
+\typ{INT} inputs are forbidden:
+
+\fun{GEN}{sqrtr}{GEN x} returns the square root of $x$.
+
+\fun{GEN}{sqrtnr}{GEN x, long n} returns the $n$-th root of $x$, assuming
+$n\geq 1$ and $x > 0$. Not stack clean.
+
+\fun{GEN}{mpcos[z]}{GEN x[, GEN z]} returns $\cos(x)$.
+
+\fun{GEN}{mpsin[z]}{GEN x[, GEN z]} returns $\sin(x)$.
+
+\fun{GEN}{mplog[z]}{GEN x[, GEN z]} returns $\log(x)$. We must have $x > 0$
+since the result must be a \typ{REAL}. Use \kbd{glog} for the general case,
+where you want such computations as $\log(-1) = I$.
+
+\fun{GEN}{mpexp[z]}{GEN x[, GEN z]} returns $\exp(x)$.
+
+\fun{GEN}{mpexpm1}{GEN x} returns $\exp(x)-1$, but is more accurate than
+\kbd{subrs(mpexp(x), 1)}, which suffers from catastrophic cancellation if
+$|x|$ is very small.
+
+\fun{void}{mpsincosm1}{GEN x, GEN *s, GEN *c} sets $s$ and $c$ to
+$\sin(x)$ and $\cos(x)-1$ respectively, where $x$ is a \typ{REAL}; the latter
+is more accurate than \kbd{subrs(mpcos(y), 1)}, which suffers from
+catastrophic cancellation if $|x|$ is very small.
+
+\fun{GEN}{mpveceint1}{GEN C, GEN eC, long n} as \kbd{veceint1}; assumes
+that $C > 0$ is a \typ{REAL} and that \kbd{eC} is \kbd{NULL} or \kbd{mpexp(C)}.
+
+\fun{GEN}{mpeint1}{GEN x, GEN expx} returns \kbd{eint1}$(x)$, for a \typ{REAL}
+$x\geq 0$, assuming that \kbd{expx} is \kbd{mpexp}$(x)$.
+
+\fun{GEN}{szeta}{long s, long prec} returns the value of Riemann's zeta
+function at the (possibly negative) integer $s\neq 1$, in relative accuracy
+\kbd{prec}.
+
+\fun{GEN}{mplambertW}{GEN y} solution $x$ of the implicit equation
+$x \exp(x) = y$, for $y > 0$ a \typ{REAL}.
+
+\noindent Useful low-level functions which \emph{disregard} the sign of $x$:
+
+\fun{GEN}{sqrtr_abs}{GEN x} returns $\sqrt{|x|}$ assuming $x\neq 0$.
+
+\fun{GEN}{exp1r_abs}{GEN x} returns $\exp(|x|) - 1$, assuming $x \neq 0$.
+
+\fun{GEN}{logr_abs}{GEN x} returns $\log(|x|)$, assuming $x \neq 0$.
+
+\noindent A few variants on sin and cos:
+
+\fun{void}{mpsincos}{GEN x, GEN *s, GEN *c} sets $s$ and $c$ to
+$\sin(x)$ and $\cos(x)$ respectively, where $x$ is a \typ{REAL}
+
+\fun{GEN}{expIr}{GEN x} returns $\exp(ix)$, where $x$ is a \typ{REAL}.
+The return type is \typ{COMPLEX} unless the imaginary part is equal to $0$
+to the current accuracy (its sign is $0$).
+
+\fun{GEN}{expIxy}{GEN x, GEN y, long prec} returns $\exp(ixy)$. Efficient
+when $x$ is real and $y$ pure imaginary.
+
+\fun{void}{gsincos}{GEN x, GEN *s, GEN *c, long prec} general case.
+
+\noindent A generalization of \tet{affrr_fixlg}
+
+\fun{GEN}{affc_fixlg}{GEN x, GEN res} assume \kbd{res} was allocated using
+\tet{cgetc}, and that $x$ is either a \typ{REAL} or a \typ{COMPLEX}
+with \typ{REAL} components. Assign $x$ to \kbd{res}, first shortening
+the components of \kbd{res} if needed (in a \kbd{gerepile}-safe way). Further
+convert \kbd{res} to a \typ{REAL} if $x$ is a \typ{REAL}.
+
+\fun{GEN}{trans_eval}{const char *fun, GEN (*f) (GEN, long), GEN x, long prec}
+evaluate transcendental function $f$ (named \kbd{"fun"} at the argument
+$x$ and precision \kbd{prec}. This is a quick way to implement a transcendental
+function to be made available under GP, starting from a $C$ function
+handling only \typ{REAL} and \typ{COMPLEX} arguments. This routine first
+converts $x$ to a suitable type:
+
+\item \typ{INT}/\typ{FRAC} to \typ{REAL} of precision \kbd{prec}, \typ{QUAD} to
+\typ{REAL} or \typ{COMPLEX} of precision \kbd{prec}.
+
+\item \typ{POLMOD} to a \typ{COL} of complex embeddings (as in \tet{conjvec})
+
+Then evaluates the function at \typ{VEC}, \typ{COL}, \typ{MAT} arguments
+coefficientwise.
+
+\subsec{Transcendental functions with \typ{PADIC} arguments}
+
+\fun{GEN}{Qp_exp}{GEN x} shortcut for \kbd{gexp(x, /*ignored*/prec)}
+
+\fun{GEN}{Qp_gamma}{GEN x} shortcut for \kbd{ggamma(x, /*ignored*/prec)}
+
+\fun{GEN}{Qp_log}{GEN x} shortcut for \kbd{glog(x, /*ignored*/prec)}
+
+\fun{GEN}{Qp_sqrt}{GEN x} shortcut for \kbd{gsqrt(x, /*ignored*/prec)}
+Return \kbd{NULL} if $x$ is not a square.
+
+\fun{GEN}{Qp_sqrtn}{GEN x, GEN n, GEN *z} shortcut for \kbd{gsqrtn(x, n, z,
+/*ignored*/prec)}. Return \kbd{NULL} if $x$ is not an $n$-th power.
+
+\subsec{Cached constants}
+
+The cached constant is returned at its current precision, which may be larger
+than \kbd{prec}. One should always use the \kbd{mp\var{xxx}} variant:
+\kbd{mppi}, \kbd{mpeuler}, or \kbd{mplog2}.
+
+\fun{GEN}{consteuler}{long prec} precomputes Euler-Mascheroni's constant
+at precision \kbd{prec}.
+
+\fun{GEN}{constcatalan}{long prec} precomputes Catalan's constant at precision
+\kbd{prec}.
+
+\fun{GEN}{constpi}{long prec} precomputes $\pi$ at precision \kbd{prec}.
+
+\fun{GEN}{constlog2}{long prec} precomputes $\log(2)$ at precision
+\kbd{prec}.
+
+\fun{void}{mpbern}{long n, long prec} precomputes the $n$ even
+\idx{Bernoulli} numbers $B_2,\dots,B_{2n}$ as \typ{FRAC} or \typ{REAL}s of
+precision \kbd{prec}. For any $2 \leq k \leq 2n$, if a floating point
+approximation of $B_k$ to accuracy \kbd{prec} is enough to reconstruct it
+exactly, a \typ{FRAC} is stored; otherwise a \typ{REAL} at the requested
+accuracy. No more than $n$ Bernoulli numbers will ever be stored (by
+\tet{bernfrac} or \tet{bernreal}), unless a subsequent call to \kbd{mpbern}
+increases the cache. If \kbd{prec} is $0$, the $B_k$ are computed exactly.
+
+The following functions use cached data if \kbd{prec} is smaller than the
+precision of the cached value; otherwise the newly computed data replaces the
+old cache.
+
+\fun{GEN}{mppi}{long prec} returns $\pi$ at precision \kbd{prec}.
+
+\fun{GEN}{Pi2n}{long n, long prec} returns $2^n\pi$ at precision \kbd{prec}.
+
+\fun{GEN}{PiI2}{long n, long prec} returns the complex number $2\pi i$ at
+precision \kbd{prec}.
+
+\fun{GEN}{PiI2n}{long n, long prec} returns the complex number $2^n\pi i$ at
+precision \kbd{prec}.
+
+\fun{GEN}{mpeuler}{long prec} returns Euler-Mascheroni's constant at
+precision \kbd{prec}.
+
+\fun{GEN}{mpeuler}{long prec} returns Catalan's number at precision \kbd{prec}.
+
+\fun{GEN}{mplog2}{long prec} returns $\log 2$ at precision \kbd{prec}.
+
+\fun{GEN}{bernreal}{long i, long prec} returns the \idx{Bernoulli} number
+$B_i$ as a \typ{REAL} at precision \kbd{prec}. If \kbd{mpbern(n,
+p)} was called previously with $n \geq i$ and $p \geq \kbd{prec}$, then
+the cached value is (converted to a \typ{REAL} of accuracy \kbd{prec} then)
+returned. Otherwise, the missing value is computed. In the latter case,
+if $n \geq i$, the cached table is updated.
+
+\fun{GEN}{bernfrac}{long i} returns the \idx{Bernoulli} number $B_i$ as a
+rational number (\typ{FRAC} or \typ{INT}). If a cached table includes $B_i$
+as a rational number, the latter is returned. Otherwise, the missing value is
+computed. In the latter case, the cached Bernoulli table may be updated.
+
+\section{Permutations }
+
+\noindent Permutation are represented in two different ways
+
+\item (\kbd{perm}) a \typ{VECSMALL} $p$ representing the bijection $i\mapsto
+p[i]$; unless mentioned otherwise, this is the form used in the functions
+below for both input and output,
+
+\item (\kbd{cyc}) a \typ{VEC} of \typ{VECSMALL}s representing a product of
+disjoint cycles.
+
+\fun{GEN}{identity_perm}{long n} return the identity permutation on $n$
+symbols.
+
+\fun{GEN}{cyclic_perm}{long n, long d} return the cyclic permutation mapping
+$i$ to $i+d$ (mod $n$) in $S_n$. Assume that $d \leq n$.
+
+\fun{GEN}{perm_mul}{GEN s, GEN t} multiply $s$ and $t$ (composition $s\circ t$)
+
+\fun{GEN}{perm_conj}{GEN s, GEN t} return $sts^{-1}$.
+
+\fun{int}{perm_commute}{GEN p, GEN q} return $1$ if $p$ and $q$ commute, 0
+otherwise.
+
+\fun{GEN}{perm_inv}{GEN p} returns the inverse of $p$.
+
+\fun{GEN}{perm_pow}{GEN p, long n} returns $p^n$
+
+\fun{GEN}{cyc_pow_perm}{GEN p, long n} the permutation $p$ is given as
+a product of disjoint cycles (\kbd{cyc}); return $p^n$ (as a \kbd{perm}).
+
+\fun{GEN}{cyc_pow}{GEN p, long n} the permutation $p$ is given as
+a product of disjoint cycles (\kbd{cyc}); return $p^n$ (as a \kbd{cyc}).
+
+\fun{GEN}{perm_cycles}{GEN p} return the cyclic decomposition of $p$.
+
+\fun{long}{perm_order}{GEN p} returns the order of the permutation $p$
+(as the lcm of its cycle lengths).
+
+\fun{GEN}{vecperm_orbits}{GEN p, long n} the permutation $p\in S_n$ being
+given as a product of disjoint cycles, return the orbits of the subgroup
+generated by $p$ on $\{1,2,\ldots,n\}$.
+
+\section{Small groups}
+
+The small (finite) groups facility is meant to deal with subgroups of Galois
+groups obtained by \tet{galoisinit} and thus is currently limited to weakly
+super-solvable groups.
+
+A group \var{grp} of order $n$ is represented by its regular representation
+(for an arbitrary ordering of its element) in $S_n$.  A subgroup of such group
+is represented by the restriction of the representation to the subgroup.
+A \emph{small group} can be either a group or a subgroup. Thus it is embedded
+in some $S_n$, where $n$ is the multiple of the order. Such $n$ is called the
+\emph{domain} of the small group. The domain of a trivial subgroup cannot be
+derived from the subgroup data, so some functions require the subgroup domain
+as argument.
+
+The small group \var{grp} is represented by a \typ{VEC} with two
+components:
+
+$\var{grp}[1]$ is a generating subset $[s_1,\ldots,s_g]$ of \var{grp}
+expressed as a vector of permutation of length $n$.
+
+$\var{grp}[2]$ contains the relative orders $[o_1,\ldots,o_g]$ of
+the generators $\var{grp}[1]$.
+
+See \tet{galoisinit} for the technical details.
+
+\fun{GEN}{checkgroup}{GEN gal, GEN *elts} checks whether \var{gal} is a
+small group or a Galois group. Returns the underlying small
+group and set \var{elts} to the list of elements or to \kbd{NULL} if it is not
+known.
+
+\fun{GEN}{galois_group}{GEN gal} return the underlying small group of the
+Galois group \var{gal}.
+
+\fun{GEN}{cyclicgroup}{GEN g, long s} returns the cyclic group with generator
+$g$ of order $s$.
+
+\fun{GEN}{trivialgroup}{void} returns the trivial group.
+
+\fun{GEN}{dicyclicgroup}{GEN g1, GEN g2, long s1, long s2} returns the group
+with generators \var{g1}, \var{g2} with respecting relative orders \var{s1},
+\var{s2}.
+
+\fun{GEN}{abelian_group}{GEN v} let v be a \typ{VECSMALL} seen as the SNF of
+a small abelian group, return its regular representation.
+
+\fun{long}{group_domain}{GEN grp} returns the \kbd{domain} of the
+\emph{non-trivial} small group \var{grp}. Return an error if \var{grp} is
+trivial.
+
+\fun{GEN}{group_elts}{GEN grp, long n} returns the list of elements of the
+small group \var{grp} of domain \var{n} as permutations.
+
+\fun{GEN}{group_set}{GEN grp, long n} returns a \var{F2v} $b$ such that
+$b[i]$ is set if and only if the small group \var{grp} of domain \var{n}
+contains a permutation sending $1$ to $i$.
+
+\fun{GEN}{groupelts_set}{GEN elts, long n}, where \var{elts} is the list of
+elements of a small group of domain \var{n}, returns a \var{F2v} $b$ such that
+$b[i]$ is set if and only if the small group contains a permutation sending $1$
+to $i$.
+
+\fun{long}{group_order}{GEN grp} returns the order of the small group
+\var{grp} (which is the product of the relative orders).
+
+\fun{long}{group_isabelian}{GEN grp} returns $1$ the the small group
+\var{grp} is Abelian, else $0$.
+
+\fun{GEN}{group_abelianHNF}{GEN grp, GEN elts} if \var{grp} is not Abelian,
+returns \kbd{NULL}, else returns the HNF matrix of \var{grp} with respect to
+the generating family $\var{grp}[1]$. If \var{elts} is no \kbd{NULL}, it must
+be the list of elements of \var{grp}.
+
+\fun{GEN}{group_abelianSNF}{GEN grp, GEN elts} if \var{grp} is not Abelian,
+returns \kbd{NULL}, else returns its cyclic decomposition. If \var{elts} is no
+\kbd{NULL}, it must be the list of elements of \var{grp}.
+
+\fun{long}{group_subgroup_isnormal}{GEN G, GEN H}, $H$ being a subgroup of the
+small group $G$, returns $1$ if $H$ is normal in $G$, else $0$.
+
+\fun{long}{group_isA4S4}{GEN grp} returns $1$ if the small group
+\var{grp} is isomorphic to $A_4$, $2$ if it is isomorphic to $S_4$ and
+$0$ else. This is mainly to deal with the idiosyncrasy of the format.
+
+\fun{GEN}{group_leftcoset}{GEN G, GEN g} where $G$ is a small group and $g$ a
+permutation of the same domain, the the left coset $gG$ as a vector of
+permutations.
+
+\fun{GEN}{group_rightcoset}{GEN G, GEN g} where $G$ is a small group and $g$ a
+permutation of the same domain, the the right coset $Gg$  as a vector of
+permutations.
+
+\fun{long}{group_perm_normalize}{GEN G, GEN g} where $G$ is a small group and
+$g$ a permutation of the same domain, return $1$ if $gGg^-1=G$, else $0$.
+
+\fun{GEN}{group_quotient}{GEN G, GEN H}, where $G$ is a small group and
+$H$ is a subgroup of $G$, returns the quotient map $G\rightarrow G/H$
+as an abstract data structure.
+
+\fun{GEN}{quotient_perm}{GEN C, GEN g} where $C$ is the quotient map
+$G\rightarrow G/H$ for some subgroup $H$ of $G$ and $g$ an element of $G$,
+return the image of $g$ by $C$ (i.e. the coset $gH$).
+
+\fun{GEN}{quotient_group}{GEN C, GEN G} where $C$ is the quotient map
+$G\rightarrow G/H$ for some \emph{normal} subgroup $H$ of $G$, return the
+quotient group $G/H$ as a small group.
+
+\fun{GEN}{quotient_subgroup_lift}{GEN C, GEN H, GEN S} where $C$ is the
+quotient map $G\rightarrow G/H$ for some group $G$ normalizing $H$ and $S$ is
+a subgroup of $G/H$, return the inverse image of $S$ by $C$.
+
+\fun{GEN}{group_subgroups}{GEN grp} returns the list of subgroups of the
+small group \var{grp} as a \typ{VEC}.
+
+\fun{GEN}{subgroups_tableset}{GEN S, long n} where $S$ is a vector of subgroups
+of domain $n$, returns a table which matchs the set of elements of the
+subgroups against the index of the subgroups.
+
+\fun{long}{tableset_find_index}{GEN tbl, GEN set} searchs the set \kbd{set} in
+the table \kbd{tbl} and returns its associated index, or $0$ if not found.
+
+\fun{GEN}{groupelts_abelian_group}{GEN elts} where \var{elts} is the list of
+elements of an \emph{Abelian} small group, returns the corresponding
+small group.
+
+\fun{GEN}{groupelts_center}{GEN elts} where \var{elts} is the list of elements
+of a small group, returns the list of elements of the center of the
+group.
+
+\fun{GEN}{group_export}{GEN grp, long format} exports a small group
+to another format, see \tet{galoisexport}.
+
+\fun{long}{group_ident}{GEN grp, GEN elts} returns the index of the small group
+\var{grp} in the GAP4 Small Group library, see \tet{galoisidentify}. If
+\var{elts} is not \kbd{NULL}, it must be the list of elements of \var{grp}.
+
+\fun{long}{group_ident_trans}{GEN grp, GEN elts} returns the index of the
+regular representation of the small group \var{grp} in the GAP4 Transitive
+Group library, see \tet{polgalois}. If \var{elts} is no \kbd{NULL}, it must be
+the list of elements of \var{grp}.
+
+\newpage
+\chapter{Standard data structures}
+
+\section{Character strings}
+
+\subsec{Functions returning a \kbd{char *}}
+
+\fun{char*}{pari_strdup}{const char *s} returns a malloc'ed copy of $s$
+(uses \kbd{pari\_malloc}).
+
+\fun{char*}{pari_strndup}{const char *s, long n} returns a malloc'ed copy of
+at most $n$ chars from $s$ (uses \kbd{pari\_malloc}). If $s$ is longer than
+$n$, only $n$ characters are copied and a terminal null byte is added.
+
+\fun{char*}{stack_strdup}{const char *s} returns a copy of $s$, allocated
+on the PARI stack (uses \kbd{stack\_malloc}).
+
+\fun{char*}{stack_strcat}{const char *s, const char *t} returns the
+concatenation of $s$ and $t$, allocated on the PARI stack (uses
+\kbd{stack\_malloc}).
+
+\fun{char*}{stack_sprintf}{const char *fmt, ...} runs \kbd{pari\_sprintf}
+on the given arguments, returning a string allocated on the PARI stack.
+
+\fun{char*}{itostr}{GEN x} writes the \typ{INT} $x$ to a \tet{stack_malloc}'ed
+string.
+
+\fun{char*}{GENtostr}{GEN x}, using the current default output format
+(\kbd{GP\_DATA->fmt}, which contains the output style and the number of
+significant digits to print), converts $x$ to a malloc'ed string. Simple
+variant of \tet{pari_sprintf}.
+
+\fun{char*}{GENtostr_unquoted}{GEN x} as \tet{GENtostr} with the following
+differences: 1) a \typ{STR} $x$ is printed without enclosing quotes
+(to be used by \kbd{print}); 2) the result is allocated on the stack
+and \emph{must not} be freed.
+
+\fun{char*}{GENtoTeXstr}{GEN x}, as \kbd{GENtostr}, except that
+\tet{f_TEX} overrides the output format from \kbd{GP\_DATA->fmt}.
+
+\fun{char*}{RgV_to_str}{GEN g, long flag} $g$ being a vector of \kbd{GEN}s,
+returns a malloc'ed string, the concatenation of the \kbd{GENtostr} applied
+to its elements, except that \typ{STR} are printed without enclosing quotes.
+\kbd{flag} determines the output format: \tet{f_RAW}, \tet{f_PRETTYMAT}
+or \tet{f_TEX}.
+
+\subsec{Functions returning a \typ{STR}}
+
+\fun{GEN}{strtoGENstr}{const char *s} returns a \typ{STR} with content $s$.
+
+\fun{GEN}{strntoGENstr}{const char *s, long n}
+returns a \typ{STR} containing the first $n$ characters of $s$.
+
+\fun{GEN}{chartoGENstr}{char c} returns a \typ{STR} containing the character
+$c$.
+
+\fun{GEN}{GENtoGENstr}{GEN x} returns a \typ{STR} containing the printed
+form of $x$ (in \tet{raw} format). This is often easier to use that
+\tet{GENtostr} (which returns a malloc-ed \kbd{char*}) since there is no need
+to free the string after use.
+
+\fun{GEN}{GENtoGENstr_nospace}{GEN x} as \kbd{GENtoGENstr}, removing all
+spaces from the output.
+
+\fun{GEN}{Str}{GEN g} as \tet{RgV_to_str} with output format \tet{f_RAW},
+but returns a \typ{STR}, not a malloc'ed string.
+
+\fun{GEN}{Strtex}{GEN g} as \tet{RgV_to_str} with output format \tet{f_TEX},
+but returns a \typ{STR}, not a malloc'ed string.
+
+\fun{GEN}{Strexpand}{GEN g} as \tet{RgV_to_str} with output format \tet{f_RAW},
+performing tilde and environment expansion on the result. Returns a
+\typ{STR}, not a malloc'ed string.
+
+\fun{GEN}{gsprintf}{const char *fmt, ...} equivalent to
+\kbd{pari\_sprintf(fmt,...}, followed by \tet{strtoGENstr}. Returns a \typ{STR},
+not a malloc'ed string.
+
+\fun{GEN}{gvsprintf}{const char *fmt, va_list ap} variadic version of
+\tet{gsprintf}
+
+\section{Output}
+
+\subsec{Output contexts}
+
+An output coutext, of type \tet{PariOUT}, is a \kbd{struct}
+that models a stream and contains the following function pointers:
+\bprog
+void (*putch)(char);           /* fputc()-alike */
+void (*puts)(const char*);     /* fputs()-alike */
+void (*flush)(void);           /* fflush()-alike */
+ at eprog\noindent
+The methods \tet{putch} and \tet{puts} are used to print a character
+or a string respectively.  The method \tet{flush} is called to finalize a
+messages.
+
+The generic functions \tet{pari_putc}, \tet{pari_puts}, \tet{pari_flush} and
+\tet{pari_printf} print according to a \emph{default output context}, which
+should be sufficient for most purposes. Lower level functions are available,
+which take an explicit output context as first argument:
+
+\fun{void}{out_putc}{PariOUT *out, char c} essentially equivalent to
+\kbd{out->putc(c)}. In addition, registers whether the last character printed
+was a \kbd{\bs n}.
+
+\fun{void}{out_puts}{PariOUT *out, const char *s} essentially equivalent to
+\kbd{out->puts(s)}. In addition, registers whether the last character printed
+was a \kbd{\bs n}.
+
+\fun{void}{out_printf}{PariOUT *out, const char *fmt, ...}
+
+\fun{void}{out_vprintf}{PariOUT *out, const char *fmt, va_list ap}
+
+\noindent N.B. The function \kbd{out\_flush} does not exist since it would be
+identical to \kbd{out->flush()}
+
+\fun{int}{pari_last_was_newline}{void} returns a non-zero value if the last
+character printed via \tet{out_putc} or \tet{out_puts} was \kbd{\bs
+n}, and $0$ otherwise.
+
+\fun{void}{pari_set_last_newline}{int last} sets the boolean value
+to be returned by the function \tet{pari_last_was_newline} to \var{last}.
+
+\subsec{Default output context} They are defined by the global variables
+\tet{pariOut} and \tet{pariErr} for normal outputs and warnings/errors, and you
+probably do not want to change them. If you \emph{do} change them, diverting
+output in non-trivial ways, this probably means that you are rewriting
+\kbd{gp}. For completeness, we document in this section what the default
+output contexts do.
+
+\misctitle{pariOut} writes output to the \kbd{FILE*} \tet{pari_outfile},
+initialized to \tet{stdout}.  The low-level methods are actually the standard
+\kbd{putc} / \kbd{fputs}, plus some magic to handle a log file if one is
+open.
+
+\misctitle{pariErr} prints to the \kbd{FILE*} \tet{pari_errfile}, initialized
+to \tet{stderr}. The low-level methods are as above.
+
+You can stick with the default \kbd{pariOut} output context and change PARI's
+standard output, redirecting \tet{pari_outfile} to another file, using
+
+\fun{void}{switchout}{const char *name} where \kbd{name} is a character string
+giving the name of the file you want to write to; the output is
+\emph{appended} at the end of the file. To close the file and revert to
+outputting to \kbd{stdout}, call \kbd{switchout(NULL)}.
+
+\subsec{PARI colors}
+In this section we describe the low-level functions used to implement GP's
+color scheme, associated to the \tet{colors} default. The following symbolic
+names are associated to gp's output strings:
+
+\item \tet{c_ERR} an error message
+
+\item \tet{c_HIST} a history number (as in \kbd{\%1 = ...})
+
+\item \tet{c_PROMPT} a prompt
+
+\item \tet{c_INPUT} an input line (minus the prompt part)
+
+\item \tet{c_OUTPUT} an output
+
+\item \tet{c_HELP} a help message
+
+\item \tet{c_TIME} a timer
+
+\item \tet{c_NONE} everything else
+
+\emph{If} the \tet{colors} default is set to a non-empty value, before gp
+outputs a string, it first outputs an ANSI colors escape sequence ---
+understood by most terminals ---, according to the \kbd{colors}
+specifications. As long as this is in effect, the following strings are
+rendered in color, possibly in bold or underlined.
+
+\fun{void}{term_color}{long c} prints (as if using \tet{pari_puts}) the ANSI
+color escape sequence associated to output object \kbd{c}. If \kbd{c} is
+\tet{c_NONE}, revert to default printing style.
+
+\fun{void}{out_term_color}{PariOUT *out, long c} as \tet{term_color},
+using output context \kbd{out}.
+
+\fun{char*}{term_get_color}{char *s, long c} returns as a character
+string the ANSI color escape sequence associated to output object \kbd{c}.
+If \kbd{c} is \tet{c_NONE}, the value used to revert to default printing
+style is returned. The argument \kbd{s} is either \kbd{NULL} (string
+allocated on the PARI stack), or preallocated storage (in which case, it must
+be able to hold at least 16 chars, including the final \kbd{\bs 0}).
+
+\subsec{Obsolete output functions}
+
+These variants of \fun{void}{output}{GEN x}, which prints \kbd{x}, followed by
+a newline and a buffer flush are complicated to use and less flexible
+than what we saw above, or than the \tet{pari_printf} variants. They are
+provided for backward compatibility and are scheduled to disappear.
+
+\fun{void}{brute}{GEN x, char format, long dec}
+
+\fun{void}{matbrute}{GEN x, char format, long dec}
+
+\fun{void}{texe}{GEN x, char format, long dec}
+
+\section{Files}
+
+The following routines are trivial wrappers around system functions
+(possibly around one of several functions depending on availability).
+They are usually integrated within PARI's diagnostics system, printing
+messages if \kbd{DEBUGFILES} is high enough.
+
+\fun{int}{pari_is_dir}{const char *name} returns $1$ if \kbd{name} points to
+a directory, $0$ otherwise.
+
+\fun{int}{pari_is_file}{const char *name} returns $1$ if \kbd{name} points to
+a directory, $0$ otherwise.
+
+\fun{int}{file_is_binary}{FILE *f} returns $1$ if the file $f$ is a binary
+file (in the \tet{writebin} sense), $0$ otherwise.
+
+\fun{void}{pari_unlink}{const char *s} deletes the file named $s$. Warn
+if the operation fails.
+
+\fun{void}{pari_fread_chars}{void *b, size_t n, FILE *f} read $n$ chars from
+stream $f$, storing the result in pre-allocated buffer $b$ (assumed to be
+large enough).
+
+\fun{char*}{path_expand}{const char *s} perform tilde and environment expansion
+on $s$. Returns a \kbd{malloc}'ed buffer.
+
+\fun{void}{strftime_expand}{const char *s, char *buf, long max} perform
+time expansion on $s$, storing the result (at most \kbd{max} chars) in
+buffer \kbd{buf}. Trivial wrapper around
+\bprog
+  time_t t = time(NULL);
+  strftime(but, max, s, localtime(&t);
+ at eprog
+
+\fun{char*}{pari_get_homedir}{const char *user} expands \kbd{\til user}
+constructs, returning the home directory of user \kbd{user}, or \kbd{NULL} if
+it could not be determined (in particular if the operating system has no such
+concept). The return value may point to static area and may be overwritten
+by subsequent system calls: use immediately or \kbd{strdup} it.
+
+\fun{int}{pari_stdin_isatty}{void} returns $1$ if our standard input
+\kbd{stdin} is attached to a terminal. Trivial wrapper around \kbd{isatty}.
+
+\subsec{pariFILE}
+
+PARI maintains a linked list of open files, to reclaim resources
+(file descriptors) on error or interrupts. The corresponding data structure
+is a \kbd{pariFILE}, which is a wrapper around a standard \kbd{FILE*},
+containing further the file name, its type (regular file, pipe, input or
+output file, etc.). The following functions create and manipulate this
+structure; they are integrated within PARI's diagnostics system, printing
+messages if \kbd{DEBUGFILES} is high enough.
+
+\fun{pariFILE*}{pari_fopen}{const char *s, const char *mode} wrapper
+around \kbd{fopen(s, mode)}, return \kbd{NULL} on failure.
+
+\fun{pariFILE*}{pari_fopen_or_fail}{const char *s, const char *mode}
+simple wrapper around \kbd{fopen(s, mode)}; error on failure.
+
+\fun{pariFILE*}{pari_fopengz}{const char *s} opens the file whose name is
+$s$,  and associates a (read-only) \kbd{pariFILE} with it. If $s$ is a
+compressed file (\kbd{.gz} suffix), it is uncompressed on the fly.
+If $s$ cannot be opened, also try to open \kbd{$s$.gz}. Returns \kbd{NULL}
+on failure.
+
+\fun{void}{pari_fclose}{pariFILE *f} closes
+the underlying file descriptor and deletes the \kbd{pariFILE} struct.
+
+\fun{pariFILE*}{pari_safefopen}{const char *s, const char *mode}
+creates a \emph{new} file $s$ (a priori for writing) with \kbd{600}
+permissions. Error if the file already exists. To avoid symlink attacks,
+a symbolic link exists, regardless of where it points to.
+
+\subsec{Temporary files}
+
+PARI has its own idea of the system temp directory derived from from an
+environment variable (\kbd{\$GPTMPDIR}, else \kbd{\$TMPDIR}), or the first
+writable directory among \kbd{/tmp}, \kbd{/var/tmp} and \kbd{.}.
+
+\fun{char*}{pari_unique_dir}{const char *s} creates a ``unique directory''
+and return its name built from the string $s$, the user id and process pid
+(on Unix systems). This directory is itself located in the temp
+directory mentioned above. The name returned is \tet{malloc}'ed.
+
+\fun{char*}{pari_unique_filename}{const char *s} creates a \emph{new} empty
+file in the temp directory, whose name contains the id-string $s$ (truncated
+to its first $8$ chars), followed by a system-dependent suffix (incorporating
+the ids of both the user and the running process, for instance). The function
+returns the tempfile name. The name returned is \tet{malloc}'ed.
+
+\section{Errors}\label{se:errors}
+
+This section documents the various error classes, and the corresponding
+arguments to \tet{pari_err}. The general syntax is
+
+\fun{void}{pari_err}{numerr,...}
+
+\noindent In the sequel, we mostly use sequences of arguments of the form
+\bprog
+  const char *s
+  const char *fmt, ...
+ at eprog\noindent where \kbd{fmt} is a PARI
+format, producing a string $s$ from the remaining arguments. Since
+providing the correct arguments to \tet{pari_err} is quite error-prone, we
+also provide specialized routines \kbd{pari\_err\_\var{ERRORCLASS}(\dots)}
+instead of \kbd{pari\_err(e\_\var{ERRORCLASS}, \dots)} so that the C compiler
+can check their arguments.
+
+\noindent We now inspect the list of valid keywords (error classes) for
+\kbd{numerr}, and the corresponding required arguments.
+
+\subsec{Internal errors, ``system'' errors}
+
+\subsubsec{e\_ARCH} A requested feature $s$ is not available on this
+architecture or operating system.
+\bprog
+  pari_err(e_ARCH)
+ at eprog\noindent prints the error message: \kbd{sorry, '$s$' not available on
+this system}.
+
+\subsubsec{e\_BUG} A bug in the PARI library, in function $s$.
+\bprog
+  pari_err(e_BUG, const char *s)
+  pari_err_BUG(const char *s)
+ at eprog\noindent prints the error message: \kbd{Bug in $s$, please report}.
+
+\subsubsec{e\_FILE} Error while trying to open a file.
+\bprog
+  pari_err(e_FILE, const char *what, const char *name)
+  pari_err_FILE(const char *what, const char *name)
+ at eprog\noindent prints the error message: \kbd{error opening
+\emph{what}: `\emph{name}'}.
+
+\subsubsec{e\_IMPL} A requested feature $s$ is not implemented.
+\bprog
+  pari_err(e_IMPL, const char *s)
+  pari_err_IMPL(const char *s)
+ at eprog\noindent prints the error message: \kbd{sorry, $s$ is not yet
+implemented}.
+
+\subsubsec{e\_PACKAGE} Missing optional package $s$.
+\bprog
+  pari_err(e_PACKAGE, const char *s)
+  pari_err_PACKAGE(const char *s)
+ at eprog\noindent prints the error message: \kbd{package $s$ is required,
+please install it}
+
+\subsec{Syntax errors, type errors}
+
+\subsubsec{e\_DIM} arguments submitted to function $s$ have inconsistent
+dimensions. E.g., when solving a linear system, or trying to compute the
+determinant of a non-square matrix.
+\bprog
+  pari_err(e_DIM, const char *s)
+  pari_err_DIM(const char *s)
+ at eprog\noindent prints the error message: \kbd{inconsistent dimensions in $s$}.
+
+\subsubsec{e\_FLAG} A flag argument is out of bounds in function $s$.
+\bprog
+  pari_err(e_FLAG, const char *s)
+  pari_err_FLAG(const char *s)
+ at eprog\noindent prints the error message: \kbd{invalid flag in $s$}.
+
+\subsubsec{e\_NOTFUNC} Generated by the PARI evaluator; tried to use a
+\kbd{GEN} which is not a \typ{CLOSURE} in a function call syntax (as in
+\kbd{f = 1; f(2);}).
+\bprog
+  pari_err(e_NOTFUNC, GEN fun)
+ at eprog\noindent prints the error message: \kbd{not a function in a function
+call}.
+
+\subsubsec{e\_OP} Impossible operation between two objects than cannot be
+typecast to a sensible common domain for deeper reasons than a type mismatch,
+usually for arithmetic reasons. As in \kbd{O(2) + O(3)}: it is valid to add
+two \typ{PADIC}s, provided the underlying prime is the same; so the addition
+is not forbidden a priori for type reasons, it only becomes so when
+inspecting the objects and trying to perform the operation.
+\bprog
+  pari_err(e_OP, const char *op, GEN x, GEN y)
+  pari_err_OP(const char *op, GEN x, GEN y)
+ at eprog\noindent As \kbd{e\_TYPE2}, replacing \kbd{forbidden} by
+\kbd{inconsistent}.
+
+\subsubsec{e\_PRIORITY} object $o$ in function $s$ contains
+variables whose priority is incompatible with the expected operation.
+E.g.~\kbd{Pol([x,1], 'y)}: this raises an error because it's not possible to
+create a polynomial whose coefficients involve variables with higher priority
+than the main variable.
+\bprog
+  pari_err(e_PRIORITY, const char *s, GEN o, const char *op, long v)
+  pari_err_PRIORITY(const char *s, GEN o, const char *op, long v)
+ at eprog\noindent prints the error message: \kbd{incorrect priority
+in $s$, variable $v_o$ \var{op} $v$}, were $v_o$ is \kbd{gvar(o)}.
+
+\subsubsec{e\_SYNTAX} Syntax error, generated by the PARI parser.
+\bprog
+  pari_err(e_SYNTAX, const char *msg, const char *e, const char *entry)
+ at eprog\noindent where \kbd{msg} is a complete error message, and \kbd{e} and
+\kbd{entry} point into the \emph{same} character string, which is the input
+that was incorrectly parsed: \kbd{e} points to the character where the parser
+failed, and $\kbd{entry}\leq \kbd{e}$ points somewhat before.
+
+\noindent Prints the error message: \kbd{msg}, followed by a colon, then
+a part of the input character string (in general \kbd{entry} itself, but an
+initial segment may be truncated if $\kbd{e}-\kbd{entry}$ is large); a caret
+points at \kbd{e}, indicating where the error took place.
+
+\subsubsec{e\_TYPE} An argument $x$ of function $s$ had an unexpected type.
+(As in \kbd{factor("blah")}.)
+\bprog
+  pari_err(e_TYPE, const char *s, GEN x)
+  pari_err_TYPE(const char *s, GEN x)
+ at eprog\noindent prints the error message: \kbd{incorrect type in $s$
+(\typ{$x$})}, where \typ{$x$} is the type of $x$.
+
+\subsubsec{e\_TYPE2} Forbidden operation between two objects than cannot be
+typecast to a sensible common domain, because their types do not match up.
+(As in \kbd{Mod(1,2) + Pi}.)
+\bprog
+  pari_err(e_TYPE2, const char *op, GEN x, GEN y)
+  pari_err_TYPE2(const char *op, GEN x, GEN y)
+ at eprog\noindent prints the error message: \kbd{forbidden} $s$
+\typ{$x$} \var{op} \typ{$y$}, where \typ{$z$} denotes the type of $z$.
+Here, $s$ denotes the spelled out name of the operator
+$\var{op}\in\{\kbd{+}, \kbd{*}, \kbd{/}, \kbd{\%}, \kbd{=}\}$, e.g.
+\emph{addition} for \kbd{"+"} or \emph{assignment} for \kbd{"="}. If \var{op}
+is not in the above operator, list, it is taken to be the already spelled out
+name of a function, e.g. \kbd{"gcd"}, and the error message becomes
+\kbd{forbidden} \var{op} \typ{$x$}, \typ{$y$}.
+
+\subsubsec{e\_VAR} polynomials $x$ and $y$ submitted to function $s$ have
+inconsistent variables. E.g., considering the algebraic number
+\kbd{Mod(t,t\pow2+1)} in \kbd{nfinit(x\pow2+1)}.
+\bprog
+  pari_err(e_VAR, const char *s, GEN x, GEN y)
+  pari_err_VAR(const char *s, GEN x, GEN y)
+ at eprog\noindent prints the error message: \kbd{inconsistent variables in $s$
+$X$ != $Y$}, where $X$ and $Y$ are the names of the variables of $x$ and $y$,
+respectively.
+
+\subsec{Overflows}
+
+\subsubsec{e\_COMPONENT} Trying to access an inexistent component in a
+vector/matrix/list in a function: the index is less than $1$ or greater
+than the allowed length.
+\bprog
+  pari_err(e_COMPONENT, const char *f, const char *op, GEN lim, GEN x)
+  pari_err_COMPONENT(const char *f, const char *op, GEN lim, GEN x)
+ at eprog\noindent prints the error message: \kbd{non-existent component in $f$:
+index \var{op} \var{lim}}. Special case: if $f$ is the empty string (no
+meaningful public function name can be used), we ignore it and print the
+message: \kbd{non-existent component: index \var{op} \var{lim}}.
+
+\subsubsec{e\_DOMAIN} An argument $x$ is not in the function's domain (as in
+\kbd{moebius(0)} or \kbd{zeta(1)}).
+\bprog
+  pari_err(e_DOMAIN, char *f, char *v, char *op, GEN lim, GEN x)
+  pari_err_DOMAIN(char *f, char *v, char *op, GEN lim, GEN x)
+ at eprog\noindent prints the error message: \kbd{domain error in $f$: $v$
+\var{op} \var{lim}}. Special case: if \var{op} is the empty string, we ignore
+\var{lim} and print the error message: \kbd{domain error in $f$: $v$ out of
+range}.
+
+\subsubsec{e\_MAXPRIME} A function using the precomputed list of prime numbers
+ran out of primes.
+\bprog
+  pari_err(e_MAXPRIME, ulong c)
+  pari_err_MAXPRIME(ulong c)
+ at eprog\noindent prints the error message: \kbd{not enough precomputed primes,
+need primelimit \til $c$} if $c$ is non-zero. And simply \kbd{not enough
+precomputed primes} otherwise.
+
+\subsubsec{e\_MEM} A call to \tet{pari_malloc} or \tet{pari_realloc} failed.
+\bprog
+  pari_err(e_MEM)
+ at eprog\noindent prints the error message: \kbd{not enough memory}.
+
+\subsubsec{e\_OVERFLOW} An object in function $s$ becomes too large to be
+represented within PARI's hardcoded limits. (As in \kbd{2\pow2\pow2\pow10}
+or \kbd{exp(1e100)}, which overflow in \kbd{lg} and \kbd{expo}.)
+\bprog
+  pari_err(e_OVERFLOW, const char *s)
+  pari_err_OVERFLOW(const char *s)
+ at eprog\noindent prints the error message: \kbd{overflow in $s$}.
+
+\subsubsec{e\_PREC} Function $s$ fails because input accuracy is too low.
+(As in \kbd{floor(1e100)} at default accuracy.)
+\bprog
+  pari_err(e_PREC, const char *s)
+  pari_err_PREC(const char *s)
+ at eprog\noindent prints the error message: \kbd{precision too low in $s$}.
+
+\subsubsec{e\_STACK} The PARI stack overflows.
+\bprog
+  pari_err(e_STACK)
+ at eprog\noindent prints the error message: \kbd{the PARI stack overflows !}
+as well as some statistics concerning stack usage.
+
+\subsec{Errors triggered intentionally}
+
+\subsubsec{e\_ALARM} A timeout, generated by the \tet{alarm} function.
+\bprog
+  pari_err(e_ALARM, const char *fmt, ...)
+ at eprog\noindent prints the error message: $s$.
+
+\subsubsec{e\_USER} A user error, as triggered by \tet{error}($g_1,\dots,g_n)$
+in GP.
+\bprog
+  pari_err(e_USER, GEN g)
+ at eprog\noindent prints the error message: \kbd{user error:}, then the
+entries of the vector $g$.
+
+\subsec{Mathematical errors}
+
+\subsubsec{e\_CONSTPOL} An argument of function $s$ is a constant polynomial,
+which does not make sense. (As in \kbd{galoisinit(Pol(1))}.)
+\bprog
+  pari_err(e_CONSTPOL, const char *s)
+  pari_err_CONSTPOL(const char *s)
+ at eprog\noindent prints the error message: \kbd{constant polynomial in $s$}.
+
+\subsubsec{e\_COPRIME} Function $s$ expected two coprime arguments, and did
+receive $x$, $y$ which were not.
+\bprog
+  pari_err(e_COPRIME, const char *s, GEN x, GEN y)
+  pari_err_COPRIME(const char *s, GEN x, GEN y)
+ at eprog\noindent prints the error message: \kbd{elements not coprime in $s$:
+$x, y$}.
+
+\subsubsec{e\_INV} Tried to invert a non-invertible object $x$.
+\bprog
+  pari_err(e_INV, GEN x)
+  pari_err_INV(GEN x)
+ at eprog\noindent prints the error message: \kbd{impossible inverse: $x$}.
+If $x = \kbd{Mod}(a,b)$ is a \typ{INTMOD} and $a$ is not $0$ mod $b$, this
+allows to factor the modulus, as \kbd{gcd}$(a,b)$ is a non-trivial divisor of
+$b$.
+
+\subsubsec{e\_IRREDPOL} Function $s$ expected an irreducible polynomial,
+and did not receive one. (As in \kbd{nfinit(x\pow2-1)}.)
+\bprog
+  pari_err(e_IRREDPOL, const char *s, GEN x)
+  pari_err_IRREDPOL(const char *s, GEN x)
+ at eprog\noindent prints the error message: \kbd{not an irreducible polynomial
+in $s$: $x$}.
+
+\subsubsec{e\_MISC} Generic uncategorized error.
+\bprog
+  pari_err(e_MISC, const char *fmt, ...)
+ at eprog\noindent prints the error message: $s$.
+
+\subsubsec{e\_MODULUS} moduli $x$ and $y$ submitted to function $s$ are
+inconsistent. E.g., considering the algebraic number
+\kbd{Mod(t,t\pow2+1)} in \kbd{nfinit(t\pow3-2)}.
+\bprog
+  pari_err(e_MODULUS, const char *s, GEN x, GEN y)
+  pari_err_MODULUS(const char *s, GEN x, GEN y)
+ at eprog\noindent prints the error message: \kbd{inconsistent moduli in $s$},
+then the moduli.
+
+\subsubsec{e\_PRIME} Function $s$ expected a prime number, and did receive $p$,
+which was not. (As in \kbd{idealprimedec(nf, 4)}.)
+\bprog
+  pari_err(e_PRIME, const char *s, GEN x)
+  pari_err_PRIME(const char *s, GEN x)
+ at eprog\noindent prints the error message: \kbd{not a prime in $s$: $x$}.
+
+\subsubsec{e\_ROOTS0} An argument of function $s$ is a zero polynomial, and
+we need to consider its roots. (As in \kbd{polroots(0)}.)
+\bprog
+  pari_err(e_ROOTS0, const char *s)
+  pari_err_ROOTS0(const char *s)
+ at eprog\noindent prints the error message: \kbd{zero polynomial in $s$}.
+
+\subsubsec{e\_SQRTN} Tried to compute an $n$-th root of $x$, which does not
+exist, in function $s$.
+(As in \kbd{sqrt(Mod(-1,3))}.)
+\bprog
+  pari_err(e_SQRTN, GEN x)
+  pari_err_SQRTN(GEN x)
+ at eprog\noindent prints the error message: \kbd{not an n-th power residue in
+$s$: $x$}.
+
+\subsec{Miscellaneous functions}
+
+\fun{long}{name_numerr}{const char *s} return the error number corresponding to
+an error name. E.g. \kbd{name\_numerr("e\_DIM")} returns \kbd{e\_DIM}.
+
+\fun{const char*}{numerr_name}{long errnum} returns the error name
+corresponding to an error number. E.g. \kbd{name\_numerr(e\_DIM)} returns
+\kbd{"e\_DIM"}.
+
+\fun{char*}{pari_err2str}{GEN err} returns the error message that would be
+printed on \typ{ERROR} \kbd{err}. The name is allocated on the PARI stack and
+must not be freed.
+
+\section{Hashtables}
+A \tet{hashtable}, or associative array, is a set of pairs $(k,v)$ of keys
+and values. PARI implements general extensible hashtables for fast data
+retrieval: when creating a table, we may either choose to use the PARI stack,
+or \kbd{malloc} so as to be stack-independent. A hashtable is implemented as
+a table of linked lists, each list containing all entries sharing the same
+hash value. The table length is a prime number, which roughly doubles as the
+table overflows by gaining new entries; both the current number of entries
+and the threshold before the table grows are stored in the table. Finally the
+table remembers the functions used to hash the entries's keys and to test for
+equality two entries hashed to the same value.
+
+An entry, or \tet{hashentry}, contains
+
+\item a key/value pair $(k,v)$, both of type \kbd{void*} for maximal
+flexibility,
+
+\item the hash value of the key, for the table hash function. This hash is
+mapped to a table index (by reduction modulo the table length), but it
+contains more information, and is used to bypass costly general equality
+tests if possible,
+
+\item a link pointer to the next entry sharing the same table cell.
+
+\bprog
+typedef struct {
+  void *key, *val;
+  ulong hash; /* hash(key) */
+  struct hashentry *next;
+} hashentry;
+
+typedef struct {
+  ulong len; /* table length */
+  hashentry **table; /* the table */
+  ulong nb, maxnb; /* number of entries stored and max nb before enlarging */
+  ulong pindex; /* prime index */
+  ulong (*hash) (void *k); /* hash function */
+  int (*eq) (void *k1, void *k2); /* equality test */
+  int use_stack; /* use the PARI stack, resp. malloc */
+} hashtable;
+ at eprog\noindent
+
+\fun{hashtable*}{hash_create}{size, hash, eq, use_stack}
+\vskip -1em % switch to K&R style to avoid atrocious line break
+\bprog
+  ulong size;
+  ulong (*hash)(void*);
+  int (*eq)(void*,void*);
+  int use_stack;
+ at eprog\noindent
+creates a hashtable with enough room to contain
+\kbd{size} entries. The functions \kbd{hash} and \kbd{eq} compute the hash
+value of keys and test keys for equality, respectively. If \kbd{use\_stack}
+is non zero, the resulting table will use the PARI stack; otherwise, we use
+\kbd{malloc}.
+
+\fun{void}{hash_insert}{hashtable *h, void *k, void *v} inserts $(k,v)$
+in hashtable $h$. No copy is made: $k$ and $v$ themselves are stored. The
+implementation does not prevent one to insert two entries with equal
+keys $k$, but which of the two is affected by later commands is undefined.
+
+\fun{hashentry*}{hash_search}{hashtable *h, void *k} look for an entry
+with key $k$ in $h$. Return it if it one exists, and \kbd{NULL} if not.
+
+\fun{hashentry*}{hash_remove}{hashtable *h, void *k} deletes an entry $(k,v)$
+with key $k$ from $h$ and return it. (Return \kbd{NULL} if none was found.)
+Only the linking structures are freed, memory associated to $k$ and $v$
+is not reclaimed.
+
+\fun{void}{hash_destroy}{hashtable *h} deletes the hashtable, by removing all
+entries.
+
+Some interesting hash functions are available:
+
+\fun{ulong}{hash_str}{const char *s}
+
+\fun{ulong}{hash_str2}{const char *s} is the historical PARI string hashing
+function and seems to be generally inferior to \kbd{hash\_str}.
+
+\fun{ulong}{hash_GEN}{GEN x}
+
+\section{Dynamic arrays}
+
+A \teb{dynamic array} is a generic way to manage stacks of data that need
+to grow dynamically. It allocates memory using \kbd{pari\_malloc}, and is
+independent of the PARI stack; it even works before the \kbd{pari\_init} call.
+
+\subsec{Initialization}
+
+To create a stack of objects of type \kbd{foo}, we proceed as follows:
+\bprog
+foo *t_foo;
+pari_stack s_foo;
+pari_stack_init(&s_foo, sizeof(*t_foo), (void**)t_foo);
+ at eprog\noindent Think of \kbd{s\_foo} as the controlling interface, and
+\kbd{t\_foo} as the (dynamic) array tied to it. The value of \kbd{t\_foo}
+may be changed as you add more elements.
+
+\subsec{Adding elements}
+The following function pushes an element on the stack.
+\bprog
+/* access globals t_foo and s_foo */
+void push_foo(foo x)
+{
+  long n = pari_stack_new(&s_foo);
+  t_foo[n] = x;
+}
+ at eprog
+
+\subsec{Accessing elements}
+
+Elements are accessed naturally through the \kbd{t\_foo} pointer.
+For example this function swaps two elements:
+\bprog
+void swapfoo(long a, long b)
+{
+  foo x;
+  if (a > s_foo.n || b > s_foo.n) pari_err_BUG("swapfoo");
+  x        = t_foo[a];
+  t_foo[a] = t_foo[b];
+  t_foo[b] = x;
+}
+ at eprog
+
+\subsec{Stack of stacks}
+Changing the address of \kbd{t\_foo} is not supported in general.
+In particular \kbd{realloc()}'ed array of stacks and stack of stacks are not
+supported.
+
+\subsec{Public interface}
+Let \kbd{s} be a \kbd{pari\_stack} and \kbd{data} the data linked to it. The
+following public fields are defined:
+
+\item \kbd{s.alloc} is the number of elements allocated for \kbd{data}.
+
+\item \kbd{s.n} is the number of elements in the stack and \kbd{data[s.n-1]} is
+the topmost element of the stack.  \kbd{s.n} can be changed as long as
+$0\leq\kbd{s.n}\leq\kbd{s.alloc}$ holds.
+
+\fun{void}{pari_stack_init}{pari_stack *s, size_t size, void **data} links
+\kbd{*s} to the data pointer \kbd{*data}, where \kbd{size} is the size of
+data element. The pointer \kbd{*data} is set to \kbd{NULL}, \kbd{s->n} and
+\kbd{s->alloc} are set to $0$: the array is empty.
+
+\fun{void}{pari_stack_alloc}{pari_stack *s, long nb} makes room for \kbd{nb}
+more elements, i.e.~makes sure that $\kbd{s.alloc}\geq\kbd{s.n} + \kbd{nb}$,
+possibly reallocating \kbd{data}.
+
+\fun{long}{pari_stack_new}{pari_stack *s} increases \kbd{s.n} by one unit,
+possibly reallocating \kbd{data}, and returns $\kbd{s.n}-1$.
+
+\misctitle{Caveat} The following construction is incorrect because
+\kbd{stack\_new} can change the value of \kbd{t\_foo}:
+\bprog
+t_foo[ pari_stack_new(&s_foo) ] = x;
+ at eprog
+
+\fun{void}{pari_stack_delete}{pari_stack *s} frees \kbd{data} and resets the
+stack to the state immediately following \kbd{stack\_init} (\kbd{s->n} and
+\kbd{s->alloc} are set to $0$).
+
+\fun{void *}{pari_stack_pushp}{pari_stack *s, void *u} This function assumes
+that \kbd{*data} is of pointer type. Pushes the element \kbd{u} on the stack
+\kbd{s}.
+
+\fun{void **}{pari_stack_base}{pari_stack *s} returns the address of \kbd{data},
+typecast to a \kbd{void **}.
+
+\section{Vectors and Matrices}
+
+\subsec{Access and extract}
+See~\secref{se:clean} and~\secref{se:unclean} for various useful constructors.
+Coefficients are accessed and set using \tet{gel}, \tet{gcoeff},
+see~\secref{se:accessors}. There are many internal functions to extract or
+manipulate subvectors or submatrices but, like the accessors above, none of
+them are suitable for \tet{gerepileupto}. Worse, there are no type
+verification, nor bound checking, so use at your own risk.
+
+\fun{GEN}{shallowcopy}{GEN x} returns a \kbd{GEN} whose components are the
+components of $x$ (no copy is made). The result may now be used to compute in
+place without destroying $x$. This is essentially equivalent to
+\bprog
+  GEN y = cgetg(lg(x), typ(x));
+  for (i = 1; i < lg(x); i++) y[i] = x[i];
+  return y;
+ at eprog\noindent
+except that \typ{MAT} is treated specially since shallow copies of all columns
+are made. The function also works for non-recursive types, but is useless
+in that case since it makes a deep copy. If $x$ is known to be a \typ{MAT}, you
+may call \tet{RgM_shallowcopy} directly; if $x$ is known not to be a \typ{MAT},
+you may call \tet{leafcopy} directly.
+
+\fun{GEN}{RgM_shallowcopy}{GEN x} returns \kbd{shallowcopy(x)}, where $x$
+is a \typ{MAT}.
+
+\fun{GEN}{shallowtrans}{GEN x} returns the transpose of $x$, \emph{without}
+copying its components, i.~e.,~it returns a \kbd{GEN} whose components are
+(physically) the components of $x$. This is the internal function underlying
+\tet{gtrans}.
+
+\fun{GEN}{shallowconcat}{GEN x, GEN y} concatenate $x$ and $y$, \emph{without}
+copying components, i.~e.,~it returns a \kbd{GEN} whose components are
+(physically) the components of $x$ and $y$.
+
+\fun{GEN}{shallowconcat1}{GEN x}
+$x$ must be \typ{VEC} or \typ{LIST}, concatenate
+its elements from left to right. Shallow version of \kbd{concat1}.
+
+\fun{GEN}{shallowmatconcat}{GEN v} shallow version of \kbd{matconcat}.
+
+\fun{GEN}{shallowextract}{GEN x, GEN y} extract components
+of the vector or matrix $x$ according to the selection parameter $y$.
+This is the shallow analog of \kbd{extract0(x, y, NULL)}, see \tet{vecextract}.
+\kbdsidx{extract0}
+
+\fun{GEN}{RgM_minor}{GEN A, long i, long j} given a square \typ{MAT} A,
+return the matrix with $i$-th row and $j$-th column removed.
+
+\fun{GEN}{vconcat}{GEN A, GEN B} concatenate vertically the two \typ{MAT} $A$
+and $B$ of compatible dimensions. A \kbd{NULL} pointer is accepted for an
+empty matrix. See \tet{shallowconcat}.
+
+\fun{GEN}{row}{GEN A, long i} return $A[i,]$, the $i$-th row of the \typ{MAT}
+$A$.
+
+\fun{GEN}{row_i}{GEN A, long i, long j1, long j2} return part of the $i$-th
+row of \typ{MAT}~$A$: $A[i,j_1]$, $A[i,j_1+1]\dots,A[i,j_2]$. Assume $j_1
+\leq j_2$.
+
+\fun{GEN}{rowcopy}{GEN A, long i} return the row $A[i,]$ of
+the~\typ{MAT}~$A$. This function is memory clean and suitable for
+\kbd{gerepileupto}. See \kbd{row} for the shallow equivalent.
+
+\fun{GEN}{rowslice}{GEN A, long i1, long i2} return the \typ{MAT}
+formed by the $i_1$-th through $i_2$-th rows of \typ{MAT} $A$. Assume $i_1
+\leq i_2$.
+
+\fun{GEN}{rowpermute}{GEN A, GEN p}, $p$ being a \typ{VECSMALL}
+representing a list $[p_1,\dots,p_n]$ of rows of \typ{MAT} $A$, returns the
+matrix whose rows are $A[p_1,],\dots, A[p_n,]$.
+
+\fun{GEN}{rowslicepermute}{GEN A, GEN p, long x1, long x2}, short for
+\bprog
+  rowslice(rowpermute(A,p), x1, x2)
+ at eprog\noindent
+(more efficient).
+
+\fun{GEN}{vecslice}{GEN A, long j1, long j2}, return $A[j_1], \dots,
+A[j_2]$. If $A$ is a \typ{MAT}, these correspond to \emph{columns} of $A$.
+The object returned has the same type as $A$ (\typ{VEC}, \typ{COL} or
+\typ{MAT}). Assume $j_1 \leq j_2$.
+
+\fun{GEN}{vecsplice}{GEN A, long j} return $A$ with $j$-th entry removed
+(\typ{VEC}, \typ{COL}) or $j$-th column removed (\typ{MAT}).
+
+\fun{GEN}{vecreverse}{GEN A}. Returns a \kbd{GEN} which has the same
+type as $A$ (\typ{VEC}, \typ{COL} or \typ{MAT}), and whose components
+are the $A[n],\dots,A[1]$. If $A$ is a \typ{MAT}, these are the
+\emph{columns} of $A$.
+
+\fun{GEN}{vecpermute}{GEN A, GEN p} $p$ is a \typ{VECSMALL} representing
+a list $[p_1,\dots,p_n]$ of indices. Returns a \kbd{GEN} which has the same
+type as $A$ (\typ{VEC}, \typ{COL} or \typ{MAT}), and whose components
+are $A[p_1],\dots,A[p_n]$. If $A$ is a \typ{MAT}, these are the
+\emph{columns} of $A$.
+
+\fun{GEN}{vecslicepermute}{GEN A, GEN p, long y1, long y2} short for
+\bprog
+  vecslice(vecpermute(A,p), y1, y2)
+ at eprog\noindent
+(more efficient).
+
+\subsec{Componentwise operations}
+
+The following convenience routines automate trivial loops of the form
+\bprog
+  for (i = 1; i < lg(a); i++) gel(v,i) = f(gel(a,i), gel(b,i))
+ at eprog\noindent
+for suitable $f$:
+
+\fun{GEN}{vecinv}{GEN a}. Given a vector $a$,
+returns the vector whose $i$-th component is \kbd{ginv}$(a[i])$.
+
+\fun{GEN}{vecmul}{GEN a, GEN b}. Given $a$ and $b$ two vectors of the same
+length, returns the vector whose $i$-th component is \kbd{gmul}$(a[i], b[i])$.
+
+\fun{GEN}{vecdiv}{GEN a, GEN b}. Given $a$ and $b$ two vectors of the same
+length, returns the vector whose $i$-th component is \kbd{gdiv}$(a[i], b[i])$.
+
+\fun{GEN}{vecpow}{GEN a, GEN n}. Given $n$ a \typ{INT}, returns
+the vector whose $i$-th component is $a[i]^n$.
+
+\fun{GEN}{vecmodii}{GEN a, GEN b}. Assuming $a$ and $b$ are two \kbd{ZV}
+of the same length, returns the vector whose $i$-th component
+is \kbd{modii}$(a[i], b[i])$.
+
+Note that \kbd{vecadd} or \kbd{vecsub} do not exist since \kbd{gadd}
+and \kbd{gsub} have the expected behavior. On the other hand,
+\kbd{ginv} does not accept vector types, hence \kbd{vecinv}.
+
+\subsec{Low-level vectors and columns functions}
+
+These functions handle \typ{VEC} as an abstract container type of
+\kbd{GEN}s. No specific meaning is attached to the content. They accept both
+\typ{VEC} and \typ{COL} as input, but \kbd{col} functions always return
+\typ{COL} and \kbd{vec} functions always return \typ{VEC}.
+
+\misctitle{Note} All the functions below are shallow.
+
+\fun{GEN}{const_col}{long n, GEN x} returns a \typ{COL} of \kbd{n} components
+equal to \kbd{x}.
+
+\fun{GEN}{const_vec}{long n, GEN x} returns a \typ{VEC} of \kbd{n} components
+equal to \kbd{x}.
+
+\fun{int}{vec_isconst}{GEN v} Returns 1 if all the components of \kbd{v} are
+equal, else returns 0.
+
+\fun{void}{vec_setconst}{GEN v, GEN x} $v$ a pre-existing vector. Set all its
+components to $x$.
+
+\fun{int}{vec_is1to1}{GEN v}  Returns 1 if the components of \kbd{v} are
+pair-wise distinct, i.e. if $i\mapsto v[i]$ is a 1-to-1 mapping, else returns
+0.
+
+\fun{GEN}{vec_shorten}{GEN v, long n} shortens the vector \kbd{v} to \kbd{n}
+components.
+
+\fun{GEN}{vec_lengthen}{GEN v, long n} lengthens the vector \kbd{v}
+to \kbd{n} components. The extra components are not initialized.
+
+\fun{GEN}{vec_insert}{GEN v, long n, GEN x} inserts $x$ at position $n$ in the vector
+$v$.
+
+\section{Vectors of small integers}
+
+\subsec{\typ{VECSMALL}}
+
+These functions handle \typ{VECSMALL} as an abstract container type
+of small signed integers. No specific meaning is attached to the content.
+
+\fun{GEN}{const_vecsmall}{long n, long c} returns a \typ{VECSMALL}
+of \kbd{n} components equal to \kbd{c}.
+
+\fun{GEN}{vec_to_vecsmall}{GEN z} identical to \kbd{ZV\_to\_zv(z)}.
+
+\fun{GEN}{vecsmall_to_vec}{GEN z} identical to \kbd{zv\_to\_ZV(z)}.
+
+\fun{GEN}{vecsmall_to_col}{GEN z} identical to \kbd{zv\_to\_ZC(z)}.
+
+\fun{GEN}{vecsmall_copy}{GEN x} makes a copy of \kbd{x} on the stack.
+
+\fun{GEN}{vecsmall_shorten}{GEN v, long n} shortens the \typ{VECSMALL} \kbd{v}
+to \kbd{n} components.
+
+\fun{GEN}{vecsmall_lengthen}{GEN v, long n} lengthens the \typ{VECSMALL}
+\kbd{v} to \kbd{n} components. The extra components are not initialized.
+
+\fun{GEN}{vecsmall_indexsort}{GEN x} performs an indirect sort of the
+components of the \typ{VECSMALL} \kbd{x} and return a permutation stored in a
+\typ{VECSMALL}.
+
+\fun{void}{vecsmall_sort}{GEN v} sorts the \typ{VECSMALL} \kbd{v} in place.
+
+\fun{long}{vecsmall_max}{GEN v} returns the maximum of the elements of
+\typ{VECSMALL} \kbd{v}, assumed non-empty.
+
+\fun{long}{vecsmall_indexmax}{GEN v} returns the index of the largest
+element of \typ{VECSMALL} \kbd{v}, assumed non-empty.
+
+\fun{long}{vecsmall_min}{GEN v} returns the minimum of the elements of
+\typ{VECSMALL} \kbd{v}, assumed non-empty.
+
+\fun{long}{vecsmall_indexmin}{GEN v} returns the index of the smallest
+element of \typ{VECSMALL} \kbd{v}, assumed non-empty.
+
+\fun{long}{vecsmall_isin}{GEN v, long x} returns the first index $i$
+such that \kbd{v[$i$]} is equal to \kbd{x}. Naive search in linear time, does
+not assume that \kbd{v} is sorted.
+
+\fun{GEN}{vecsmall_uniq}{GEN v} given a \typ{VECSMALL} \kbd{v}, return
+the vector of unique occurrences.
+
+\fun{GEN}{vecsmall_uniq_sorted}{GEN v} same as \kbd{vecsmall\_uniq}, but assumes
+ \kbd{v} sorted.
+
+\fun{long}{vecsmall_duplicate}{GEN v} given a \typ{VECSMALL} \kbd{v}, return
+$0$ if there is no duplicates, or the index of the first duplicate
+(\kbd{vecsmall\_duplicate([1,1])} returns $2$).
+
+\fun{long}{vecsmall_duplicate_sorted}{GEN v} same as
+\kbd{vecsmall\_duplicate}, but assume \kbd{v} sorted.
+
+\fun{int}{vecsmall_lexcmp}{GEN x, GEN y} compares two \typ{VECSMALL} lexically.
+
+\fun{int}{vecsmall_prefixcmp}{GEN x, GEN y} truncate the longest \typ{VECSMALL}
+to the length of the shortest and compares them lexicographically.
+
+\fun{GEN}{vecsmall_prepend}{GEN V, long s} prepend \kbd{s} to the
+\typ{VECSMALL} \kbd{V}.
+
+\fun{GEN}{vecsmall_append}{GEN V, long s} append \kbd{s} to the
+\typ{VECSMALL} \kbd{V}.
+
+\fun{GEN}{vecsmall_concat}{GEN u, GEN v} concat the \typ{VECSMALL} \kbd{u}
+and \kbd{v}.
+
+\fun{long}{vecsmall_coincidence}{GEN u, GEN v} returns the numbers of indices
+where \kbd{u} and \kbd{v} agree.
+
+\fun{long}{vecsmall_pack}{GEN v, long base, long mod} handles the
+\typ{VECSMALL} \kbd{v} as the digit of a number in base \kbd{base} and return
+this number modulo \kbd{mod}. This can be used as an hash function.
+
+\subsec{Vectors of \typ{VECSMALL}}
+These functions manipulate vectors of \typ{VECSMALL} (vecvecsmall).
+
+\fun{GEN}{vecvecsmall_sort}{GEN x} sorts lexicographically the components of
+the vector \kbd{x}.
+
+\fun{GEN}{vecvecsmall_sort_uniq}{GEN x} sorts lexicographically the components of
+the vector \kbd{x}, removing duplicates entries.
+
+\fun{GEN}{vecvecsmall_indexsort}{GEN x} performs an indirect lexicographic
+sorting of the components of the vector \kbd{x} and return a permutation
+stored in a \typ{VECSMALL}.
+
+\fun{long}{vecvecsmall_search}{GEN x, GEN y, long flag} \kbd{x} being a sorted
+vecvecsmall and \kbd{y} a \typ{VECSMALL}, search \kbd{y} inside \kbd{x}.
+\kbd{flag} has the same meaning as for \kbd{setsearch}.
+
+\newpage
+\chapter{Functions related to the GP interpreter}
+
+\section{Handling closures}\label{se:closure}
+
+\subsec{Functions to evaluate \typ{CLOSURE}}
+
+\fun{void}{closure_disassemble}{GEN C} print the \typ{CLOSURE} \kbd{C} in
+GP assembly format.
+
+\fun{GEN}{closure_callgenall}{GEN C, long n, ...} evaluate the \typ{CLOSURE}
+\kbd{C} with the \kbd{n} arguments (of type \kbd{GEN}) following \kbd{n} in
+the function call. Assumes \kbd{C} has arity $\geq \kbd{n}$.
+
+\fun{GEN}{closure_callgenvec}{GEN C, GEN args} evaluate the \typ{CLOSURE}
+\kbd{C} with the arguments supplied in the vector \kbd{args}. Assumes \kbd{C}
+has arity $\geq \kbd{lg(args)-1}$.
+
+\fun{GEN}{closure_callgen1}{GEN C, GEN x} evaluate the \typ{CLOSURE}
+\kbd{C} with argument \kbd{x}. Assumes \kbd{C} has arity $\geq 1$.
+
+\fun{GEN}{closure_callgen2}{GEN C, GEN x, GEN y} evaluate the \typ{CLOSURE}
+\kbd{C} with argument \kbd{x}, \kbd{y}. Assumes \kbd{C} has arity $\geq 2$.
+
+\fun{void}{closure_callvoid1}{GEN C, GEN x} evaluate the \typ{CLOSURE}
+\kbd{C} with argument \kbd{x} and discard the result. Assumes \kbd{C}
+has arity $\geq 1$.
+
+The following technical functions are used to evaluate \emph{inline}
+closures and closures of arity 0.
+
+The control flow statements (break, next and return) will cause the
+evaluation of the closure to be interrupted; this is called below a
+\emph{flow change}. When that occurs, the functions below generally
+ return \kbd{NULL}. The caller can then adopt three positions:
+
+\item raises an exception (\kbd{closure\_evalnobrk}).
+
+\item passes through (by returning NULL itself).
+
+\item handles the flow change.
+
+\fun{GEN}{closure_evalgen}{GEN code} evaluates a closure and returns the result,
+or \kbd{NULL} if a flow change occurred.
+
+\fun{GEN}{closure_evalnobrk}{GEN code} as \kbd{closure\_evalgen} but raise
+an exception if a flow change occurs. Meant for iterators where
+interrupting the closure is meaningless, e.g.~\kbd{intnum} or \kbd{sumnum}.
+
+\fun{void}{closure_evalvoid}{GEN code} evaluates a closure whose return
+value is ignored. The caller has to deal with eventual flow changes by
+calling \kbd{loop\_break}.
+
+The remaining functions below are for exceptional situations:
+
+\fun{GEN}{closure_evalres}{GEN code} evaluates a closure and returns the result.
+The difference with \kbd{closure\_evalgen} being that, if the flow end by a
+\kbd{return} statement, the result will be the returned value instead of
+\kbd{NULL}. Used by the main GP loop.
+
+\fun{GEN}{closure_evalbrk}{GEN code, long *status} as \kbd{closure\_evalres}
+but set \kbd{status} to a non-zero value if a flow change occurred. This
+variant is not stack clean. Used by the break loop.
+
+\fun{GEN}{closure_trapgen}{long numerr, GEN code} evaluates closure, while
+trapping error \kbd{numerr}. Return \kbd{(GEN)1L} if error trapped, and the
+result otherwise, or \kbd{NULL} if a flow change occurred. Used by trap.
+
+
+\subsec{Functions to handle control flow changes}
+
+\fun{long}{loop_break}{void} processes an eventual flow changes inside an
+iterator. If this function return $1$, the iterator should stop.
+
+\subsec{Functions to deal with lexical local variables}\label{se:pushlex}
+
+Function using the prototype code \kbd{`V'} need to manually create and delete a
+lexical variable for each code \kbd{`V'}, which will be given a number $-1, -2,
+\ldots$.
+
+\fun{void}{push_lex}{GEN a, GEN code} creates a new lexical variable whose
+initial value is $a$ on the top of the stack. This variable get the number
+$-1$, and the number of the other variables is decreased by one unit. When
+the first variable of a closure is created, the argument \kbd{code} must be the
+closure that references this lexical variable. The argument \kbd{code} must be
+\kbd{NULL} for all subsequent variables (if any).  (The closure contains the
+debugging data for the variable).
+
+\fun{void}{pop_lex}{long n} deletes the $n$ topmost lexical variables,
+increasing the number of other variables by $n$. The argument $n$ must match
+the number of variables allocated through \kbd{push\_lex}.
+
+\fun{GEN}{get_lex}{long vn} get the value of the variable with number \kbd{vn}.
+
+\fun{void}{set_lex}{long vn, GEN x} set the value of the variable with number
+\kbd{vn}.
+
+\subsec{Functions returning new closures}
+
+\fun{GEN}{compile_str}{const char *s} returns the closure corresponding to the
+GP expression $s$.
+
+\fun{GEN}{closure_deriv}{GEN code} returns a closure corresponding to the
+numerical derivative of the closure \kbd{code}.
+
+\fun{GEN}{snm_closure}{entree *ep, GEN data}
+Let \kbd{data} be a vector of length $m$, \kbd{ep} be an \kbd{entree}
+pointing to a C function $f$ of arity $n+m$, returns a \typ{CLOSURE} object
+$g$ of arity $n$ such that
+$g(x_1,\ldots,x_n)=f(x_1,\ldots,x_n,gel(data,1),...,gel(data,m))$. If
+\kbd{data} is \kbd{NULL}, then $m=0$ is assumed.  This function has a low
+overhead since it does not copy \kbd{data}.
+
+\fun{GEN}{strtofunction}{char *str} returns a closure corresponding to the
+built-in or install'ed function named \kbd{str}.
+
+\fun{GEN}{strtoclosure}{char *str, long n, ...} returns a closure
+corresponding to the built-in or install'ed function named \kbd{str} with the
+$n$ last parameters set to the $n$ \kbd{GEN}s following $n$, see
+\tet{snm_closure}. This function has an higher overhead since it copies the
+parameters and does more input validation.
+
+In the example code below, \kbd{agm1} is set to the function
+\kbd{x->agm(x,1)} and \kbd{res} is set to \kbd{agm(2,1)}.
+
+\bprog
+  GEN agm1 = strtoclosure("agm",1, gen_1);
+  GEN res = closure_callgen1(agm1, gen_2);
+ at eprog
+
+\subsec{Functions used by the gp debugger (break loop)}
+\fun{long}{closure_context}{long s} restores the compilation context starting
+at frame \kbd{s+1}, and returns the index of the topmost frame. This allow to
+compile expressions in the topmost lexical scope.
+
+\fun{void}{closure_err}{void} prints a backtrace of the last $20$ stack frames.
+
+\subsec{Standard wrappers for iterators}
+Two families of standard wrappers are provided to interface iterators like
+\kbd{intnum} or \kbd{sumnum} with GP.
+
+\subsubsec{Standard wrappers for inline closures}
+Theses wrappers are used to implement GP functions taking inline closures as
+input. The object \kbd{(GEN)E} must be an inline closure which is evaluated
+with the lexical variable number $-1$ set to $x$.
+
+\fun{GEN}{gp_eval}{void *E, GEN x} is used for the prototype code \kbd{`E'}.
+
+\fun{long}{gp_evalvoid}{void *E, GEN x} is used for the prototype code
+\kbd{`I'}. The resulting value is discarded.  Return a non-zero value if a
+control-flow instruction request the iterator to terminate immediately.
+
+\fun{long}{gp_evalbool}{void *E, GEN x} returns the boolean
+\kbd{gp\_eval(E, x)} evaluates to (i.e. true iff the value is non-zero).
+
+\fun{GEN}{gp_evalupto}{void *E, GEN x} memory-safe version of \kbd{gp\_eval},
+\kbd{gcopy}-ing the result, when the evaluator returns components of
+previously allocated objects (e.g. member functions).
+
+\subsubsec{Standard wrappers for true closures}
+These wrappers are used to implement GP functions taking true closures as
+input.
+
+\fun{GEN}{gp_call}{void *E, GEN x} evaluates the closure \kbd{(GEN)E} on $x$.
+
+\fun{long}{gp_callbool}{void *E, GEN x} evaluates the closure \kbd{(GEN)E} on
+$x$, returns \kbd{1} if its result is non-zero, and \kbd{0} otherwise.
+
+\fun{long}{gp_callvoid}{void *E, GEN x} evaluates the closure \kbd{(GEN)E} on
+$x$, discarding the result. Return a non-zero value if a control-flow
+instruction request the iterator to terminate immediately.
+
+\section{Defaults}
+
+\fun{int}{pari_is_default}{const char *s} return $1$ if $s$ is the name
+of a default, $0$ otherwise.
+
+\fun{GEN}{setdefault}{const char *s, const char *v, long flag} is the
+low-level function underlying \kbd{default0}. If $s$ is \kbd{NULL}, call all
+default setting functions with string argument \kbd{NULL} and flag
+\tet{d_ACKNOWLEDGE}. Otherwise, check whether $s$ corresponds to a default
+and call the corresponding default setting function with arguments $v$ and
+\fl.
+
+We shall describe these functions below: if $v$ is \kbd{NULL}, we only look
+at the default value (and possibly print or return it, depending on
+\kbd{flag}); otherwise the value of the default to $v$, possibly after some
+translation work. The flag is one of
+
+\item \tet{d_INITRC} called while reading the \kbd{gprc}: print and return
+\kbd{gnil}, possibly defer until \kbd{gp} actually starts.
+
+\item \tet{d_RETURN} return the current value, as a \typ{INT} if possible, as
+a \typ{STR} otherwise.
+
+\item \tet{d_ACKNOWLEDGE} print the current value, return \kbd{gnil}.
+
+\item \tet{d_SILENT} print nothing, return \kbd{gnil}.
+
+\noindent Low-level functions called by \kbd{setdefault}:
+
+\fun{GEN}{sd_TeXstyle}{const char *v, long flag}
+
+\fun{GEN}{sd_colors}{const char *v, long flag}
+
+\fun{GEN}{sd_compatible}{const char *v, long flag}
+
+\fun{GEN}{sd_datadir}{const char *v, long flag}
+
+\fun{GEN}{sd_debug}{const char *v, long flag}
+
+\fun{GEN}{sd_debugfiles}{const char *v, long flag}
+
+\fun{GEN}{sd_debugmem}{const char *v, long flag}
+
+\fun{GEN}{sd_factor_add_primes}{const char *v, long flag}
+
+\fun{GEN}{sd_factor_proven}{const char *v, long flag}
+
+\fun{GEN}{sd_format}{const char *v, long flag}
+
+\fun{GEN}{sd_histsize}{const char *v, long flag}
+
+\fun{GEN}{sd_log}{const char *v, long flag}
+
+\fun{GEN}{sd_logfile}{const char *v, long flag}
+
+\fun{GEN}{sd_nbthreads}{const char *v, long flag}
+
+\fun{GEN}{sd_new_galois_format}{const char *v, long flag}
+
+\fun{GEN}{sd_output}{const char *v, long flag}
+
+\fun{GEN}{sd_parisize}{const char *v, long flag}
+
+\fun{GEN}{sd_path}{const char *v, long flag}
+
+\fun{GEN}{sd_prettyprinter}{const char *v, long flag}
+
+\fun{GEN}{sd_primelimit}{const char *v, long flag}
+
+\fun{GEN}{sd_realprecision}{const char *v, long flag}
+
+\fun{GEN}{sd_recover}{const char *v, long flag}
+
+\fun{GEN}{sd_secure}{const char *v, long flag}
+
+\fun{GEN}{sd_seriesprecision}{const char *v, long flag}
+
+\fun{GEN}{sd_simplify}{const char *v, long flag}
+
+\fun{GEN}{sd_sopath}{char *v, int flag}
+
+\fun{GEN}{sd_strictargs}{const char *v, long flag}
+
+\fun{GEN}{sd_strictmatch}{const char *v, long flag}
+
+\fun{GEN}{sd_threadsize}{const char *v, long flag}
+
+\noindent Generic functions used to implement defaults: most of the above
+routines are implemented in terms of the following generic ones. In all
+routines below
+
+\item \kbd{v} and \kbd{flag} are the arguments passed to \kbd{default}:
+\kbd{v} is a new value (or the empty string: no change), and \kbd{flag} is one
+of \tet{d_INITRC}, \tet{d_RETURN}, etc.
+
+\item \kbd{s} is the name of the default being changed, used to display error
+messages or acknowledgements.
+
+\fun{GEN}{sd_toggle}{const char *v, long flag, const char *s, int *ptn}
+
+\item if \kbd{v} is neither \kbd{"0"} nor \kbd{"1"}, an error is raised using
+\tet{pari_err}.
+
+\item \kbd{ptn} points to the current numerical value of the toggle (1 or 0),
+and is set to the new value (when \kbd{v} is non-empty).
+
+For instance, here is how the timer default is implemented internally:
+\bprog
+GEN
+sd_timer(const char *v, long flag)
+{ return sd_toggle(v,flag,"timer", &(GP_DATA->chrono)); }
+ at eprog
+
+The exact behavior and return value depends on \kbd{flag}:
+
+\item \tet{d_RETURN}: returns the new toggle value, as a \kbd{GEN}.
+
+\item \tet{d_ACKNOWLEDGE}: prints a message indicating the new toggle value
+and return \kbd{gnil}.
+
+\item other cases: print nothing and return \kbd{gnil}.
+
+
+\fun{GEN}{sd_ulong}{const char *v, long flag, const char *s, ulong *ptn,
+ulong Min, ulong Max, const char **msg}\hbadness 10000
+
+\item \kbd{ptn} points to the current numerical value of the toggle, and is set
+to the new value (when \kbd{v} is non-empty).
+
+\item \kbd{Min} and \kbd{Max} point to the minimum and maximum values allowed
+for the default.
+
+\item \kbd{v} must translate to an integer in the allowed ranger, a suffix
+among
+\kbd{k}/\kbd{K} ($\times 10^3$),
+\kbd{m}/\kbd{M} ($\times 10^6$),
+or
+\kbd{g}/\kbd{G} ($\times 10^9$) is allowed, but no arithmetic expression.
+
+\item \kbd{msg} is a \kbd[NULL]-terminated array of messages or \kbd{NULL}
+(ignored). If \kbd{msg} is not \kbd{NULL}, \kbd{msg}$[i]$ contains
+a message associated to the value $i$ of the default. The last entry in the
+\kbd{msg} array is used as a message associated to all subsequent ones.
+
+The exact behavior and return value depends on \kbd{flag}:
+
+\item \tet{d_RETURN}: returns the new toggle value, as a \kbd{GEN}.
+
+\item \tet{d_ACKNOWLEDGE}: prints a message indicating the new value,
+possibly a message associated to it via the \kbd{msg} argument, and return
+\kbd{gnil}.
+
+\item other cases: print nothing and return \kbd{gnil}.
+
+\fun{GEN}{sd_string}{const char *v, long flag, const char *s, char **pstr}
+\item \kbd{v} is subjet to environment expansion, then time expansion.
+
+\item \kbd{pstr} points to the current string value, and is set to the new
+value (when \kbd{v} is non-empty).
+
+\section{Records and Lazy vectors}
+The functions in this section are used to implement \kbd{ell} structures and
+analogous objects, which are vectors some of whose components are initialized
+to dummy values, later computed on demand. We start by initializing the
+structure:
+
+\fun{GEN}{obj_init}{long d, long n} returns an \tev{obj} $S$, a \typ{VEC}
+with $d$ regular components, accessed as \kbd{gel(S,1)}, \dots,
+\kbd{gel(S,d)}; together with a record of $n$ members, all initialized to
+$0$. The arguments $d$ and $n$ must be non-negative.
+
+After \kbd{S = obj\_init(d, n)}, the prototype of our other functions are of
+the form
+\bprog
+  GEN obj_do(GEN S, long tag, ...)
+ at eprog\noindent The first argument $S$ holds the structure to be managed.
+The second argument \var{tag} is the index of the struct member (from $1$ to
+$n$) we operate on. We recommend to define an \kbd{enum} and use descriptive
+names instead of hardcoded numbers. For instance, if $n = 3$, after defining
+\bprog
+  enum { TAG_p = 1, TAG_list, TAG_data };
+ at eprog\noindent one may use \kbd{TAG\_list} or $2$ indifferently as a tag.
+The former being preferred, of course.
+
+\misctitle{Technical note}
+In the current implementation, $S$ is a \typ{VEC} with $d+1$ entries.
+The first $d$ components are ordinary \typ{GEN} entries, which you can
+read or assign to in the customary way. But the last component $\kbd{gel(S,
+d+1)}$, a \typ{VEC} of length $n$ initialized to \kbd{zerovec}$(n)$, must
+be handled in a special way: you should never access or modify its components
+directly, only through the API we are about to describe. Indeed, its entries
+are meant to contain dynamic data, which will be stored, retrieved and
+replaced (for instance by a value computed to a higher accuracy), while
+interacting safely with intermediate \kbd{gerepile} calls. This mechanism
+allows to simulate C \kbd{struct}s, in a simpler way than with general
+hashtables, while remaining compatible with the GP language, which knows
+neither structs nor hashtables. It also serialize the structure in an
+ordinary \kbd{GEN}, which facilitates copies and garbage collection (use
+\kbd{gcopy} or \kbd{gerepile}), rather than having to deal with individual
+components of actual C \kbd{struct}s.
+
+
+\fun{GEN}{obj_check}{GEN S, long tag} if the \emph{tag}-component in $S$
+is non empty, return it. Otherwise return \kbd{NULL}. The \typ{INT} $0$
+(initial value) is used as a sentinel to indicated an empty component.
+
+\fun{GEN}{obj_insert}{GEN S, long tag, GEN O} insert (a clone of) $O$
+as \emph{tag}-component of $S$. Any previous value is deleted, and
+data pointing to it become invalid.
+
+\fun{GEN}{obj_insert_shallow}{GEN S, long K, GEN O} as \tet{obj_insert},
+inserting $O$ as-is, not via a clone.
+
+\fun{GEN}{obj_checkbuild}{GEN S, long tag, GEN (*build)(GEN)} if the
+\emph{tag}-component of $S$ is non empty, return it. Otherwise insert
+(a clone of) \kbd{build(S)} as \emph{tag}-component in $S$, and return it.
+
+\fun{GEN}{obj_checkbuild_padicprec}{GEN S, long tag, GEN (*build)(GEN,long),
+long prec}
+if the \emph{tag}-component of $S$ is non empty \emph{and} has relative
+$p$-adic precision $\geq \kbd{prec}$, return it. Otherwise insert (a clone
+of) \kbd{build(S, prec)} as \emph{tag}-component in $S$, and return it.
+
+\fun{GEN}{obj_checkbuild_prec}{GEN S, long tag, GEN (*build)(GEN,long), long
+prec} if the \emph{tag}-component of $S$ is non empty \emph{and} has
+\kbd{gprecision} $\geq \kbd{prec}$, return it. Otherwise insert (a clone of)
+\kbd{build(S, prec)} as \emph{tag}-component in $S$, and return it.
+
+\fun{void}{obj_free}{GEN S} destroys all clones stored in the $n$ tagged
+components, and replace them by the initial value $0$. The regular entries of
+$S$ are unaffected, and $S$ remains a valid object. This is used to
+avoid memory leaks.
diff --git a/doc/usersch6.tex b/doc/usersch6.tex
new file mode 100644
index 0000000..c2b9ce0
--- /dev/null
+++ b/doc/usersch6.tex
@@ -0,0 +1,2100 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Technical Reference Guide for Algebraic Number Theory}
+
+\section{General Number Fields}
+
+\subsec{Number field types}
+
+None of the following routines thoroughly check their input: they
+distinguish between \emph{bona fide} structures as output by PARI routines,
+but designing perverse data will easily fool them. To give an example, a
+square matrix will be interpreted as an ideal even though the $\Z$-module
+generated by its columns may not be an $\Z_K$-module (i.e. the expensive
+\kbd{nfisideal} routine will \emph{not} be called).
+
+\fun{long}{nftyp}{GEN x}. Returns the type of number field structure stored in
+\kbd{x}, \tet{typ_NF}, \tet{typ_BNF}, or \tet{typ_BNR}. Other answers
+are possible, meaning \kbd{x} is not a number field structure.
+
+\fun{GEN}{get_nf}{GEN x, long *t}. Extract an \var{nf} structure from
+\kbd{x} if possible and return it, otherwise return \kbd{NULL}. Sets
+\kbd{t} to the \kbd{nftyp} of \kbd{x} in any case.
+
+\fun{GEN}{get_bnf}{GEN x, long *t}. Extract a \kbd{bnf} structure from
+\kbd{x} if possible and return it, otherwise return \kbd{NULL}. Sets
+\kbd{t} to the \kbd{nftyp} of \kbd{x} in any case.
+
+\fun{GEN}{get_nfpol}{GEN x, GEN *nf} try to extract an \var{nf} structure
+from \kbd{x}, and sets \kbd{*nf} to \kbd{NULL} (failure) or to the \var{nf}.
+Returns the (monic, integral) polynomial defining the field.
+
+\fun{GEN}{get_bnfpol}{GEN x, GEN *bnf, GEN *nf} try to extract a \var{bnf}
+and an \var{nf} structure from \kbd{x}, and sets \kbd{*bnf}
+and \kbd{*nf} to \kbd{NULL} (failure) or to the corresponding structure.
+Returns the (monic, integral) polynomial defining the field.
+
+\fun{GEN}{checknf}{GEN x} if an \var{nf} structure can be extracted from
+\kbd{x}, return it; otherwise raise an exception. The more general
+\kbd{get\_nf} is often more flexible.
+
+\fun{GEN}{checkbnf}{GEN x} if an \var{bnf} structure can be extracted from
+\kbd{x}, return it; otherwise raise an exception. The more general
+\kbd{get\_bnf} is often more flexible.
+
+\fun{void}{checkbnr}{GEN bnr} Raise an exception if the argument
+is not a \var{bnr} structure.
+
+\fun{void}{checkbnrgen}{GEN bnr} Raise an exception if the argument is not a
+\var{bnr} structure, complete with explicit generators for the ray class group.
+This is normally useless and \tet{checkbnr} should be instead, unless
+you are absolutely certain that the generators will be needed at a later
+point, and you are about to embark in a costly intermediate computation.
+PARI functions do check that generators are present in \var{bnr} before
+accessing them: they will raise an error themselves; many functions
+that may require them, e.g. \kbd{bnrconductor}, often
+do not actually need them.
+
+\fun{void}{checkrnf}{GEN rnf} Raise an exception if the argument is not an
+\var{rnf} structure.
+
+\fun{void}{checkbid}{GEN bid} Raise an exception if the argument is not a
+\var{bid} structure.
+
+\fun{GEN}{checkgal}{GEN x} if a \var{galoisinit} structure can be extracted
+from \kbd{x}, return it; otherwise raise an exception.
+
+\fun{void}{checksqmat}{GEN x, long N} check whether \kbd{x} is a square matrix
+of dimension \kbd{N}. May be used to check for ideals if \kbd{N} is the field
+degree.
+
+\fun{void}{checkprid}{GEN pr} Raise an exception if the argument is not a
+prime ideal structure.
+
+\fun{GEN}{get_prid}{GEN ideal} return the underlying prime ideal structure
+if one can be extracted from \kbd{ideal} (ideal or extended ideal), and
+return \kbd{NULL} otherwise.
+
+\fun{void}{checkabgrp}{GEN v} Raise an exception if the argument
+is not an abelian group structure, i.e. a \typ{VEC} with either $2$ or $3$
+entries: $[N,\var{cyc}]$ or $[N,\var{cyc}, \var{gen}]$.
+
+\fun{GEN}{abgrp_get_no}{GEN x}
+ extract the cardinality $N$ from an abelian group structure.
+
+\fun{GEN}{abgrp_get_cyc}{GEN x}
+ extract the elementary divisors \var{cyc} from an abelian group structure.
+
+\fun{GEN}{abgrp_get_gen}{GEN x}
+ extract the generators \var{gen} from an abelian group structure.
+
+\fun{void}{checkmodpr}{GEN modpr} Raise an exception if the argument is not a
+ prime ideal structure.
+
+\fun{GEN}{checknfelt_mod}{GEN nf, GEN x, const char *s} given an \var{nf}
+structure \kbd{nf} and a \typ{POLMOD} \kbd{x}, return the associated
+polynomial representative (shallow) if \kbd{x} and \kbd{nf} are compatible.
+Raise an exception otherwise. Set $s$ to the name of the caller for a
+meaningful error message.
+
+\fun{void}{check_ZKmodule}{GEN x, const char *s} check whether $x$ looks like
+$\Z_K$-module (a pair $[A,I]$, where $A$ is a matrix and $I$ is a list of
+ideals; $A$ has as many columns as $I$ has elements. Otherwise
+raises an exception. Set $s$ to the name of the caller for a
+meaningful error message.
+
+\fun{long}{idealtyp}{GEN *ideal, GEN *fa} The input is \kbd{ideal}, a pointer
+to an ideal (or extended ideal), which is usually modified, \kbd{fa} being
+set as a side-effect. Returns the type of the underlying ideal among
+\tet{id_PRINCIPAL} (a number field element), \tet{id_PRIME} (a prime ideal)
+\tet{id_MAT} (an ideal in matrix form).
+
+If \kbd{ideal} pointed to an ideal, set \kbd{fa} to \kbd{NULL}, and
+possibly simplify \kbd{ideal} (for instance the zero ideal is replaced by
+\kbd{gen\_0}). If it pointed to an extended ideal, replace
+\kbd{ideal} by the underlying ideal and set \kbd{fa} to the factorization
+matrix component.
+
+\subsec{Extracting info from a \kbd{nf} structure}
+
+These functions expect a true \var{nf} argument associated to a number field
+$K = \Q[x]/(T)$, e.g.~a \var{bnf} will not work. Let $n = [K:\Q]$ be the
+field degree.
+
+\fun{GEN}{nf_get_pol}{GEN nf} returns the polynomial $T$ (monic, in $\Z[x]$).
+
+\fun{long}{nf_get_varn}{GEN nf} returns the variable number of the number
+field defining polynomial.
+
+\fun{long}{nf_get_r1}{GEN nf} returns the number of real places $r_1$.
+
+\fun{long}{nf_get_r2}{GEN nf} returns the number of complex places $r_2$.
+
+\fun{void}{nf_get_sign}{GEN nf, long *r1, long *r2} sets $r_1$ and $r_2$
+to the number of real and complex places respectively. Note that
+$r_1+2r_2$ is the field degree.
+
+\fun{long}{nf_get_degree}{GEN nf} returns the number field degree, $n = r_1 +
+2r_2$.
+
+\fun{GEN}{nf_get_disc}{GEN nf} returns the field discriminant.
+
+\fun{GEN}{nf_get_index}{GEN nf} returns the index of $T$, i.e. the index of
+the order generated by the power basis $(1,x,\ldots,x^{n-1})$ in the
+maximal order of $K$.
+
+\fun{GEN}{nf_get_zk}{GEN nf} returns a basis $(w_1,w_2,\ldots,w_n)$ for the
+maximal order of $K$. Those are polynomials in $\Q[x]$ of degree $<n$; it is
+guaranteed that $w_1 = 1$.
+
+\fun{GEN}{nf_get_invzk}{GEN nf} returns the matrix $(m_{i,j})\in M_n(\Z)$
+giving the power basis $(x^i)$ in terms of the $(w_j)$, i.e such that
+$x^{j-1} = \sum_{i = 1}^n m_{i,j} w_i$ for all $1\leq j \leq n$; since $w_1 =
+1 = x^0$, we have $m_{i,1} = \delta_{i,1}$ for all $i$. The conversion
+functions in the \kbd{algtobasis} family essentially amount to a left
+multiplication by this matrix.
+
+\fun{GEN}{nf_get_roots}{GEN nf} returns the $r_1$ real roots of the polynomial
+defining the number fields: first the $r_1$ real roots (as \typ{REAL}s), then
+the $r_2$ representatives of the pairs of complex conjugates.
+
+\fun{GEN}{nf_get_allroots}{GEN nf} returns all the complex roots of $T$:
+first the $r_1$ real roots (as \typ{REAL}s), then the $r_2$ pairs of complex
+conjugates.
+
+\fun{GEN}{nf_get_M}{GEN nf} returns the $(r_1+r_2)\times n$ matrix $M$
+giving the embeddings of $K$: $M[i,j]$ contains $w_j(\alpha_i)$, where
+$\alpha_i$ is the $i$-th element of \kbd{nf\_get\_roots(nf)}. In particular,
+if $v$ is an $n$-th dimensional \typ{COL} representing the element
+$\sum_{i=1}^n v[i] w_i$ of $K$, then \kbd{RgM\_RgC\_mul(M,v)} represents the
+embeddings of $v$.
+
+\fun{GEN}{nf_get_G}{GEN nf} returns a $n\times n$ real matrix $G$ such that
+$Gv \cdot Gv = T_2(v)$, where $v$ is an $n$-th dimensional \typ{COL}
+representing the element $\sum_{i=1}^n v[i] w_i$ of $K$ and $T_2$ is the
+standard Euclidean form on $K\otimes \R$, i.e.~$T_2(v)
+= \sum_{\sigma} |\sigma(v)|^2$, where $\sigma$ runs through all $n$ complex
+embeddings of $K$.
+
+\fun{GEN}{nf_get_roundG}{GEN nf} returns a rescaled version of $G$, rounded
+to nearest integers, specifically \tet{RM_round_maxrank}$(G)$.
+
+\fun{GEN}{nf_get_Tr}{GEN nf} returns the matrix of the Trace quadratic form
+on the basis $(w_1,\ldots,w_n)$: its $(i,j)$ entry is $\text{Tr} w_i w_j$.
+
+\fun{GEN}{nf_get_diff}{GEN nf} returns the primitive part of the inverse of
+the above Trace matrix.
+
+\fun{long}{nf_get_prec}{GEN nf} returns the precision (in words) to which the
+\var{nf} was computed.
+
+\subsec{Extracting info from a \kbd{bnf} structure}
+
+These functions expect a true \var{bnf} argument, e.g.~a \var{bnr} will not
+work.
+
+\fun{GEN}{bnf_get_nf}{GEN bnf} returns the underlying \var{nf}.
+
+\fun{GEN}{bnf_get_clgp}{GEN bnf} returns the class group in \var{bnf},
+which is a $3$-component vector $[h, \var{cyc}, \var{gen}]$.
+
+\fun{GEN}{bnf_get_cyc}{GEN bnf} returns the elementary divisors
+of the class group (cyclic components) $[d_1,\ldots, d_k]$, where
+$d_k \mid \ldots \mid d_1$.
+
+\fun{GEN}{bnf_get_gen}{GEN bnf} returns the generators $[g_1,\ldots,g_k]$
+of the class group. Each $g_i$ has order $d_i$, and the full module of relations
+between the $g_i$ is generated by the $d_ig_i = 0$.
+
+\fun{GEN}{bnf_get_no}{GEN bnf} returns the class number.
+
+\fun{GEN}{bnf_get_reg}{GEN bnf} returns the regulator.
+
+\fun{GEN}{bnf_get_logfu}{GEN bnf} returns (complex floating point
+approximations to) the logarithms of the complex embeddings of our system of
+fundamental units.
+
+\fun{GEN}{bnf_get_fu}{GEN bnf} returns the fundamental units. Raise
+an error if the \var{bnf} does not contain units in algebraic form.
+
+\fun{GEN}{bnf_get_fu_nocheck}{GEN bnf} as \tet{bnf_get_fu} without
+checking whether units are present. Do not use this unless
+you initialize the \var{bnf} yourself!
+
+\fun{GEN}{bnf_get_tuU}{GEN bnf} returns a generator of the torsion part
+of $\Z_K^*$.
+
+\fun{long}{bnf_get_tuN}{GEN bnf} returns the order of the torsion part of
+$\Z_K^*$, i.e.~the number of roots of unity in $K$.
+
+\subsec{Extracting info from a \kbd{bnr} structure}
+
+These functions expect a true \var{bnr} argument.
+
+\fun{GEN}{bnr_get_bnf}{GEN bnr} returns the underlying \var{bnf}.
+
+\fun{GEN}{bnr_get_nf}{GEN bnr} returns the underlying \var{nf}.
+
+\fun{GEN}{bnr_get_clgp}{GEN bnr} returns the ray class group.
+
+\fun{GEN}{bnr_get_no}{GEN bnr} returns the ray class number.
+
+\fun{GEN}{bnr_get_cyc}{GEN bnr} returns the elementary divisors
+of the ray class group (cyclic components) $[d_1,\ldots, d_k]$, where
+$d_k \mid \ldots \mid d_1$.
+
+\fun{GEN}{bnr_get_gen}{GEN bnr} returns the generators $[g_1,\ldots,g_k]$ of
+the ray class group. Each $g_i$ has order $d_i$, and the full module of
+relations between the $g_i$ is generated by the $d_ig_i = 0$. Raise
+a generic error if the \var{bnr} does not contain the ray class group
+generators.
+
+\fun{GEN}{bnr_get_gen_nocheck}{GEN bnr} as \tet{bnr_get_gen} without
+checking whether generators are present. Do not use this unless
+you initialize the \var{bnr} yourself!
+
+\fun{GEN}{bnr_get_bid}{GEN bnr} returns the \var{bid} associated
+to the \var{bnr} modulus.
+
+\fun{GEN}{bnr_get_mod}{GEN bnr} returns the modulus associated
+to the \var{bnr}.
+
+\subsec{Extracting info from an \kbd{rnf} structure}
+
+These functions expect a true \var{rnf} argument, associated to an extension
+$L/K$, $K = \Q[y]/(T)$, $L = K[x]/(P)$.
+
+\fun{long}{rnf_get_degree}{GEN rnf} returns the \emph{relative} degree
+$[L:K]$.
+
+\fun{long}{rnf_get_absdegree}{GEN rnf} returns the absolute degree
+$[L:\Q]$.
+
+\fun{long}{rnf_get_nfdegree}{GEN rnf} returns the degree of the base
+field $[K:\Q]$.
+
+\fun{GEN}{rnf_get_nf}{GEN rnf} returns the base field $K$, an \var{nf}
+structure.
+
+\fun{GEN}{rnf_get_nfpol}{GEN rnf} returns the polynomial $T$ defining the
+base field $K$.
+
+\fun{long}{rnf_get_nfvarn}{GEN rnf} returns the variable $y$ associated to the
+base field $K$.
+
+\fun{void}{rnf_get_nfzk}{GEN rnf, GEN *b, GEN *cb} returns the integer basis
+of the base field $K$.
+
+\fun{GEN}{rnf_get_pol}{GEN rnf} returns the relative polynomial defining
+$L/K$.
+
+\fun{long}{rnf_get_varn}{GEN rnf} returns the variable $x$ associated to $L$.
+
+\fun{GEN}{rnf_get_zk}{GEN nf} returns the relative integer basis generating
+$\Z_L$ as a $\Z_K$-module, as a pseudo-matrix $(A,I)$ in HNF.
+
+\fun{GEN}{rnf_get_disc}{GEN rnf} is the output $[\goth{d}, s]$
+ of \kbd{rnfdisc}
+
+\fun{GEN}{rnf_get_index}{GEN rnf} is the index ideal $\goth{f}$
+
+\fun{GEN}{rnf_get_polabs}{GEN rnf} returns an absolute polynomial defining
+$L/\Q$.
+
+\fun{GEN}{rnf_get_invzk}{GEN rnf} contains $A^{-1}$, where $(A,I)$
+is the chosen pseudo-basis for $\Z_L$ over $\Z_K$.
+
+\fun{GEN}{rnf_get_map}{GEN rnf} returns technical data associated to the map
+$K\to L$. Currently, this contains data from \kbd{rnfequation},
+as well as the polynomials $T$ and $P$.
+
+\subsec{Extracting info from a \kbd{bid} structure}
+
+These functions expect a true \var{bid} argument, associated to a modulus $I$
+in a number field $K$.
+
+\fun{GEN}{bid_get_mod}{GEN bid} returns the modulus associated to the \var{bid}.
+
+\fun{GEN}{bid_get_grp}{GEN bid} returns the Abelian group associated
+to $(\Z_K/I)^*$modulus associated to the \var{bid}.
+
+\fun{GEN}{bid_get_ideal}{GEN bid} return the finite part of the \var{bid}
+modulus (an integer ideal).
+
+\fun{GEN}{bid_get_arch}{GEN bid} return the Archimedean part of the \var{bid}
+modulus (a vector of real places).
+
+\fun{GEN}{bid_get_no}{GEN bid} returns the cardinality of the
+group $(\Z_K/I)^*$.
+
+\fun{GEN}{bid_get_cyc}{GEN bid} returns the elementary divisors
+of the group $(\Z_K/I)^*$ (cyclic components) $[d_1,\ldots, d_k]$, where $d_k
+\mid \ldots \mid d_1$.
+
+\fun{GEN}{bid_get_gen}{GEN bid} returns the generators of $(\Z_K/I)^*$
+contained in \var{bid}. Raise a generic error if \var{bid} does not contain
+generators.
+
+\fun{GEN}{bid_get_gen_nocheck}{GEN bid} as \tet{bid_get_gen} without
+checking whether generators are present. Do not use this unless
+you initialize the \var{bid} yourself!
+
+\subsec{Increasing accuracy}
+
+\fun{GEN}{nfnewprec}{GEN x, long prec}. Raise an exception if \kbd{x}
+is not a number field structure (\var{nf}, \var{bnf} or \var{bnr}).
+Otherwise, sets its accuracy to \kbd{prec} and return the new structure.
+This is mostly useful with \kbd{prec} larger than the accuracy to
+which \kbd{x} was computed, but it is also possible to decrease the accuracy
+of \kbd{x} (truncating relevant components, which may speed up later
+computations). This routine may modify the original \kbd{x} (see below).
+
+This routine is straightforward for \var{nf} structures, but for the
+other ones, it requires all principal ideals corresponding to the \var{bnf}
+relations in algebraic form (they are originally only available via floating
+point approximations). This in turn requires many calls to
+\tet{bnfisprincipal0}, which is often slow, and may fail if the initial
+accuracy was too low. In this case, the routine will not actually fail but
+recomputes a \var{bnf} from scratch!
+
+Since this process may be very expensive, the corresponding data is cached
+(as a \emph{clone}) in the \emph{original} \kbd{x} so that later precision
+increases become very fast. In particular, the copy returned by
+\kbd{nfnewprec} also contains this additional data.
+
+\fun{GEN}{bnfnewprec}{GEN x, long prec}. As \kbd{nfnewprec}, but extracts
+a \var{bnf} structure form \kbd{x} before increasing its accuracy, and
+returns only the latter.
+
+\fun{GEN}{bnrnewprec}{GEN x, long prec}. As \kbd{nfnewprec}, but extracts a
+\var{bnr} structure form \kbd{x} before increasing its accuracy, and
+returns only the latter.
+
+\fun{GEN}{nfnewprec_shallow}{GEN nf, long prec}
+
+\fun{GEN}{bnfnewprec_shallow}{GEN bnf, long prec}
+
+\fun{GEN}{bnrnewprec_shallow}{GEN bnr, long prec} Shallow functions
+underlying the above, except that the first argument must now have the
+corresponding number field type. I.e. one cannot call
+\kbd{nfnewprec\_shallow(nf, prec)} if \kbd{nf} is actually a \var{bnf}.
+
+\subsec{Number field arithmetic}
+The number field $K = \Q[X]/(T)$ is represented by an \kbd{nf} (or \kbd{bnf}
+or \kbd{bnr} structure). An algebraic number belonging to $K$ is given as
+
+\item a \typ{INT}, \typ{FRAC} or \typ{POL} (implicitly modulo $T$), or
+
+\item a \typ{POLMOD} (modulo $T$), or
+
+\item a \typ{COL}~\kbd{v} of dimension $N = [K:\Q]$, representing
+the element in terms of the computed integral basis $(e_i)$, as
+\bprog
+  sum(i = 1, N, v[i] * nf.zk[i])
+ at eprog
+The preferred forms are \typ{INT} and \typ{COL} of \typ{INT}. Routines can
+handle denominators but it is much more efficient to remove  denominators
+first (\tet{Q_remove_denom}) and take them into account at the end.
+
+\misctitle{Safe routines} The following routines do not assume that their
+\kbd{nf} argument is a true \var{nf} (it can be any number field type, e.g.~a
+\var{bnf}), and accept number field elements in all the above forms. They
+return their result in \typ{COL} form.
+
+\fun{GEN}{nfadd}{GEN nf, GEN x, GEN y} returns $x+y$.
+
+\fun{GEN}{nfdiv}{GEN nf, GEN x, GEN y} returns $x / y$.
+
+\fun{GEN}{nfinv}{GEN nf, GEN x} returns $x^{-1}$.
+
+\fun{GEN}{nfmul}{GEN nf,GEN x,GEN y} returns $xy$.
+
+\fun{GEN}{nfpow}{GEN nf,GEN x,GEN k} returns $x^k$, $k$ is in $\Z$.
+
+\fun{GEN}{nfpow_u}{GEN nf,GEN x, ulong k} returns $x^k$, $k\geq 0$.
+
+\fun{GEN}{nfsqr}{GEN nf,GEN x} returns $x^2$.
+
+\fun{long}{nfval}{GEN nf, GEN x, GEN pr} returns the valuation of $x$ at the
+maximal ideal $\goth{p}$ associated to the \var{prid} \kbd{pr}.
+Returns \kbd{LONG\_MAX} is $x$ is $0$.
+
+\fun{GEN}{nfnorm}{GEN nf,GEN x} absolute norm of $x$.
+
+\fun{GEN}{nftrace}{GEN nf,GEN x} absolute trace of $x$.
+
+\fun{GEN}{nfpoleval}{GEN nf, GEN pol, GEN a} evaluate the \typ{POL} \kbd{pol}
+(with coefficients in \kbd{nf}) on the algebraic number $a$ (also in $nf$).
+
+\fun{GEN}{FpX_FpC_nfpoleval}{GEN nf, GEN pol, GEN a, GEN p} evaluate the
+\typ{FpX} \kbd{pol} on the algebraic number $a$ (also in $nf$).
+
+The following three functions implement trivial functionality akin to
+Euclidean division for which we currently have no real use. Of course, even if
+the number field is actually Euclidean, these do not in general implement a
+true Euclidean division.
+
+\fun{GEN}{nfdiveuc}{GEN nf, GEN a, GEN b} returns the algebraic integer
+closest to $x / y$. Functionally identical to \kbd{ground( nfdiv(nf,x,y) )}.
+
+\fun{GEN}{nfdivrem}{GEN nf, GEN a, GEN b} returns the vector $[q,r]$, where
+\bprog
+  q = nfdiveuc(nf, a,b);
+  r = nfadd(nf, a,nfmul(nf,q,gneg(b)));    \\ or r = nfmod(nf,a,b);
+ at eprog
+
+\fun{GEN}{nfmod}{GEN nf, GEN a, GEN b} returns $r$ such that
+\bprog
+  q = nfdiveuc(nf, a,b);
+  r = nfadd(nf, a, nfmul(nf,q, gneg(b)));
+ at eprog
+
+\fun{GEN}{nf_to_scalar_or_basis}{GEN nf, GEN x} let $x$ be a number field
+element. If it is a rational scalar, i.e.~can be represented by a \typ{INT}
+or \typ{FRAC}, return the latter. Otherwise returns its basis representation
+(\tet{nfalgtobasis}). Shallow function.
+
+\fun{GEN}{nf_to_scalar_or_alg}{GEN nf, GEN x} let $x$ be a number field
+element. If it is a rational scalar, i.e.~can be represented by a \typ{INT}
+or \typ{FRAC}, return the latter. Otherwise returns its lifted \typ{POLMOD}
+representation (lifted \tet{nfbasistoalg}). Shallow function.
+
+\fun{GEN}{RgX_to_nfX}{GEN nf, GEN x} let $x$ be a \typ{POL} whose coefficients
+are number field elements; apply \kbd{nf\_to\_scalar\_or\_basis} to each
+coefficient and return the resulting new polynomial. Shallow function.
+
+\fun{GEN}{RgM_to_nfM}{GEN nf, GEN x} let $x$ be a \typ{MAT} whose coefficients
+are number field elements; apply \kbd{nf\_to\_scalar\_or\_basis} to each
+coefficient and return the resulting new matrix. Shallow function.
+
+\fun{GEN}{RgC_to_nfC}{GEN nf, GEN x} let $x$ be a \typ{COL} or
+\typ{VEC} whose coefficients
+are number field elements; apply \kbd{nf\_to\_scalar\_or\_basis} to each
+coefficient and return the resulting new \typ{COL}. Shallow function.
+
+\misctitle{Unsafe routines} The following routines assume that their \kbd{nf}
+argument is a true \var{nf} (e.g.~a \var{bnf} is not allowed) and their
+argument are restricted in various ways, see the precise description below.
+
+\fun{GEN}{nfinvmodideal}{GEN nf, GEN x, GEN A} given an algebraic integer
+$x$ and a non-zero integral ideal $A$ in HNF, returns a $y$ such that
+$xy \equiv 1$ modulo $A$.
+
+\fun{GEN}{nfpowmodideal}{GEN nf, GEN x, GEN n, GEN ideal} given an algebraic
+integer $x$, an integer $n$, and a non-zero integral ideal $A$ in HNF,
+returns an algebraic integer congruent to $x^n$ modulo $A$.
+
+\fun{GEN}{nfmuli}{GEN nf, GEN x, GEN y} returns $x\times y$ assuming
+that both $x$ and $y$ are either \typ{INT}s or \kbd{ZV}s of the correct
+dimension.
+
+\fun{GEN}{nfsqri}{GEN nf, GEN x} returns $x^2$ assuming that $x$ is a \typ{INT}
+or a \kbd{ZV} of the correct dimension.
+
+\fun{GEN}{nfC_nf_mul}{GEN nf, GEN v, GEN x} given a \typ{VEC} or \typ{COL}
+$v$ of elements of $K$ in \typ{INT}, \typ{FRAC} or \typ{COL} form, multiply
+it by the element $x$ (arbitrary form). This is faster than multiplying
+coordinatewise since pre-computations related to $x$ (computing the
+multiplication table) are done only once. The components of the result
+are in most cases \typ{COL}s but are allowed to be \typ{INT}s or \typ{FRAC}s.
+Shallow function.
+
+\fun{GEN}{zk_multable}{GEN nf, GEN x} given a \kbd{ZC} $x$ (implicitly
+representing an algebraic integer), returns the \kbd{ZM} giving the
+multiplication table by $x$. Shallow function (the first column of the result
+points to the same data as $x$).
+
+\fun{GEN}{zk_scalar_or_multable}{GEN nf, GEN x} given a \typ{INT} or \kbd{ZC}
+$x$, returns a \typ{INT} equal to $x$ if the latter is a scalar
+(\typ{INT} or \kbd{ZV\_isscalar}$(x)$ is 1) and
+\kbd{zk\_multable}$(\var{nf},x)$ otherwise. Shallow function.
+
+
+The following routines implement multiplication in a commutative $R$-algebra,
+generated by $(e_1 = 1,\dots, e_n)$, and given by a multiplication table $M$:
+elements in the algebra are $n$-dimensional \typ{COL}s, and the matrix
+$M$ is such that for all $1\leq i,j\leq n$, its column with index $(i-1)n +
+j$, say $(c_k)$, gives $e_i\cdot e_j = \sum c_k e_k$. It is assumed that
+$e_1$ is the neutral element for the multiplication (a convenient
+optimization, true in practice for all multiplications we needed to implement).
+If $x$ has any other type than \typ{COL} where an algebra element is
+expected, it is understood as $x e_1$.
+
+\fun{GEN}{multable}{GEN M, GEN x} given a column vector $x$, representing
+the quantity $\sum_{i=1}^N x_i e_i$, returns the multiplication table by $x$.
+Shallow function.
+
+\fun{GEN}{ei_multable}{GEN M, long i} returns the multiplication table
+by the $i$-th basis element $e_i$. Shallow function.
+
+\fun{GEN}{tablemul}{GEN M, GEN x, GEN y} returns $x\cdot y$.
+
+\fun{GEN}{tablesqr}{GEN M, GEN x} returns $x^2$.
+
+\fun{GEN}{tablemul_ei}{GEN M, GEN x, long i} returns $x\cdot e_i$.
+
+\fun{GEN}{tablemul_ei_ej}{GEN M, long i, long j} returns $e_i\cdot e_j$.
+
+\fun{GEN}{tablemulvec}{GEN M, GEN x, GEN v} given a vector $v$ of elements
+in the algebra, returns the $x\cdot v[i]$.
+
+\subsec{Elements in factored form}
+
+Computational algebraic theory performs extensively linear
+algebra on $\Z$-modules with a natural multiplicative structure ($K^*$,
+fractional ideals in $K$, $\Z_K^*$, ideal class group), thereby raising
+elements to horrendously large powers. A seemingly innocuous elementary linear
+algebra operation like $C_i\leftarrow C_i - 10000 C_1$ involves raising
+entries in $C_1$ to the $10000$-th power. Understandably, it is often more
+efficient to keep elements in factored form rather than expand every such
+expression. A \emph{factorization matrix} (or \tev{famat}) is a two column
+matrix, the first column containing \emph{elements} (arbitrary objects which
+may be repeated in the column), and the second one contains \emph{exponents}
+(\typ{INT}s, allowed to be 0). By abuse of notation, the empty matrix
+\kbd{cgetg(1, t\_MAT)} is recognized as the trivial factorization (no
+element, no exponent).
+
+Even though we think of a \var{famat} with columns $g$ and $e$
+as one meaningful object when fully expanded as $\prod g[i]^{e[i]}$,
+\var{famat}s are basically about concatenating information to keep track of
+linear algebra: the objects stored in a \var{famat} need not be
+operation-compatible, they will not even be compared to each other (with one
+exception: \tet{famat_reduce}). Multiplying two \var{famat}s just
+concatenates their elements and exponents columns. In a context where a
+\var{famat} is expected, an object $x$ which is not of type \typ{MAT} will be
+treated as the factorization $x^1$. The following functions all return
+\var{famat}s:
+
+\fun{GEN}{famat_mul}{GEN f, GEN g} $f$, $g$ are \var{famat},
+or objects whose type is \emph{not} \typ{MAT} (understood as $f^1$ or $g^1$).
+Returns $fg$. The empty factorization is the neutral element for \var{famat}
+multiplication.
+
+\fun{GEN}{famat_mul_shallow}{GEN f, GEN g} $f$, $g$ are \var{famat}, returns
+$fg$. Shallow function.
+
+\fun{GEN}{famat_pow}{GEN f, GEN n} $n$ is a \typ{INT}. If $f$ is a \typ{MAT},
+assume it is a \var{famat} and return $f^n$ (multiplies the exponent column
+by $n$). Otherwise, understand it as an element and returns the $1$-line
+\var{famat} $f^n$.
+
+\fun{GEN}{famat_sqr}{GEN f} returns $f^2$.
+
+\fun{GEN}{famat_inv}{GEN f} returns $f^{-1}$.
+
+\fun{GEN}{famat_inv_shallow}{GEN f} shallow version of \tet{famat_inv}.
+
+\fun{GEN}{to_famat}{GEN x, GEN k} given an element $x$ and an exponent
+$k$, returns the \var{famat} $x^k$.
+
+\fun{GEN}{to_famat_shallow}{GEN x, GEN k} same, as a shallow function.
+
+Note that it is trivial to break up a \var{famat} into its two constituent
+columns: \kbd{gel(f,1)} and \kbd{gel(f,2)} are the elements and exponents
+respectively. Conversely, \kbd{mkmat2} builds a (shallow) \var{famat} from
+two \typ{COL}s of the same length.
+
+The last two functions makes an assumption about the elements: they must be
+regular algebraic numbers (not \var{famat}s) over a given number field:
+
+\fun{GEN}{famat_reduce}{GEN f} given a \var{famat} $f$, returns a \var{famat}
+$g$ without repeated elements or 0 exponents, such that the expanded forms
+of $f$ and $g$ would be equal. Shallow function.
+
+\fun{GEN}{ZM_famat_limit}{GEN f, GEN limit} given a \var{famat} $f$ with
+\typ{INT} entries, returns a \var{famat} $g$ with all factors larger than
+\kbd{limit} multiplied out as the last entry (with exponent $1$).
+
+\fun{GEN}{famat_to_nf}{GEN nf, GEN f} You normally never want to do this!
+This is a simplified form of \tet{nffactorback}, where we do not check the
+user input for consistency.
+
+The description of \tet{famat_to_nf} says that you do not want to use this
+function. Then how do we recover genuine number field elements? Well, in
+most cases, we do not need to: most of the functions useful in this
+context accept \var{famat}s as inputs, for instance \tet{nfsign},
+\tet{nfsign_arch}, \tet{ideallog} and \tet{bnfisunit}. Otherwise, we can
+generally make good use of a quotient operation (modulo a fixed conductor,
+modulo $\ell$-th powers); see the end of \secref{se:Ideal_reduction}.
+
+\misctitle{Caveat} Receiving a \var{famat} input, \kbd{bnfisunit} assumes that
+it is an algebraic integer, since this is expensive to check, and normally
+easy to ensure from the user's side; do not feed it ridiculous inputs.
+
+\fun{GEN}{famatsmall_reduce}{GEN f} as \kbd{famat\_reduce}, but for exponents
+given by a \typ{VECSMALL}.
+
+\subsec{Ideal arithmetic}
+
+\misctitle{Conversion to HNF}
+
+\fun{GEN}{idealhnf}{GEN nf, GEN x} returns the HNF of the ideal defined by $x$:
+$x$ may be an algebraic  number (defining a principal ideal),  a maximal ideal
+(as given by \tet{idealprimedec} or  \tet{idealfactor}), or a matrix whose
+columns give generators for the  ideal. This  last format is complicated,  but
+useful to reduce general modules to the canonical form once in a while:
+
+\item if strictly less than $N = [K:Q]$ generators are given,  $x$ is the
+$\Z_K$-module they generate,
+
+\item if $N$ or more are given,  it is assumed that they form a $\Z$-basis
+(that the matrix has maximal rank $N$).  This acts as \tet{mathnf} since the
+$\Z_K$-module structure is (taken for granted hence) not taken into account
+in this case.
+
+Extended ideals are also accepted, their principal part being discarded.
+
+\fun{GEN}{idealhnf0}{GEN nf, GEN x, GEN y} returns the HNF of the ideal
+generated by the two algebraic numbers $x$ and $y$.
+
+The following low-level functions underlie the above two: they all assume
+that \kbd{nf} is a true \var{nf} and perform no type checks:
+
+\fun{GEN}{idealhnf_principal}{GEN nf, GEN x}
+returns the ideal generated by the algebraic number $x$.
+
+\fun{GEN}{idealhnf_shallow}{GEN nf, GEN x} is \tet{idealhnf} except that the
+result may not be suitable for \kbd{gerepile}: if $x$ is already in HNF, we
+return $x$, not a copy!
+
+\fun{GEN}{idealhnf_two}{GEN nf, GEN v} assuming $a = v[1]$ is a non-zero
+\typ{INT} and $b = v[2]$ is an algebraic integer, possibly given in regular
+representation by a \typ{MAT} (the multiplication table by $b$, see
+\tet{zk_multable}), returns the HNF of $a\Z_K+b\Z_K$.
+
+\misctitle{Operations}
+
+The basic ideal routines accept all \kbd{nf}s (\var{nf}, \var{bnf},
+\var{bnr}) and ideals in any form, including extended ideals, and return
+ideals in HNF, or an extended ideal when that makes sense:
+
+\fun{GEN}{idealadd}{GEN nf, GEN x, GEN y} returns $x+y$.
+
+\fun{GEN}{idealdiv}{GEN nf, GEN x, GEN y} returns $x/y$. Returns an extended
+ideal if $x$ or $y$ is an extended ideal.
+
+\fun{GEN}{idealmul}{GEN nf, GEN x, GEN y} returns $xy$.
+Returns an extended ideal if $x$ or $y$ is an extended ideal.
+
+\fun{GEN}{idealsqr}{GEN nf, GEN x} returns $x^2$.
+Returns an extended ideal if $x$ is an extended ideal.
+
+\fun{GEN}{idealinv}{GEN nf, GEN x} returns $x^{-1}$.
+Returns an extended ideal if $x$ is an extended ideal.
+
+\fun{GEN}{idealpow}{GEN nf, GEN x, GEN n} returns $x^n$.
+Returns an extended ideal if $x$ is an extended ideal.
+
+\fun{GEN}{idealpows}{GEN nf, GEN ideal, long n} returns $x^n$.
+Returns an extended ideal if $x$ is an extended ideal.
+
+\fun{GEN}{idealmulred}{GEN nf, GEN x, GEN y} returns an extended ideal equal
+to $xy$.
+
+\fun{GEN}{idealpowred}{GEN nf, GEN x, GEN n} returns an extended ideal equal
+to $x^n$.
+
+More specialized routines suffer from various restrictions:
+
+\fun{GEN}{idealdivexact}{GEN nf, GEN x, GEN y} returns $x/y$, assuming that
+the quotient is an integral ideal. Much faster than \tet{idealdiv} when the
+norm of the quotient is small compared to $Nx$. Strips the principal parts
+if either $x$ or $y$ is an extended ideal.
+
+\fun{GEN}{idealdivpowprime}{GEN nf, GEN x, GEN pr, GEN n} returns $x
+\goth{p}^{-n}$, assuming $x$ is an ideal in HNF, and \kbd{pr}
+a \var{prid} associated to $\goth{p}$. Not suitable for \tet{gerepileupto}
+since it returns $x$ when $n = 0$.
+
+\fun{GEN}{idealmulpowprime}{GEN nf, GEN x, GEN pr, GEN n} returns $x
+\goth{p}^{n}$, assuming $x$ is an ideal in HNF, and \kbd{pr} a \var{prid}
+associated to $\goth{p}$. Not suitable for \tet{gerepileupto} since it
+retunrs $x$ when $n = 0$.
+
+\fun{GEN}{idealprodprime}{GEN nf, GEN P} given a list $P$ of prime ideals
+in \var{prid} form, return their product.
+
+\fun{GEN}{idealmul_HNF}{GEN nf, GEN x, GEN y} returns $xy$, assuming
+than \kbd{nf} is a true \var{nf}, $x$ is an integral ideal in HNF and $y$
+is an integral ideal in HNF or precompiled form (see below).
+For maximal speed, the second ideal $y$ may be given in precompiled form $y =
+[a,b]$, where $a$ is a non-zero \typ{INT} and $b$ is an algebraic integer in
+regular representation (a \typ{MAT} giving the multiplication table by the
+fixed element): very useful when many ideals $x$ are going to be multiplied by
+the same ideal $y$. This essentially reduces each ideal multiplication to
+an $N\times N$ matrix multiplication followed by a $N\times 2N$ modular
+HNF reduction (modulo $xy\cap \Z$).
+
+\misctitle{Approximation}
+
+\fun{GEN}{idealaddtoone}{GEN nf, GEN A, GEN B} given to coprime integer ideals
+$A$, $B$, returns $[a,b]$ with $a\in A$, $b\in B$, such that $a + b = 1$
+The result is reduced mod $AB$, so $a$, $b$ will be small.
+
+\fun{GEN}{idealaddtoone_i}{GEN nf, GEN A, GEN B} as \tet{idealaddtoone} except
+that \kbd{nf} must be a true \var{nf}, and only $a$ is returned.
+
+\fun{GEN}{hnfmerge_get_1}{GEN A, GEN B} given two square upper HNF integral
+matrices $A$, $B$ of the same dimension $n > 0$, return $a$ in the image of
+$A$ such that $1-a$ is in the image of $B$. (By abuse of notation we denote
+$1$ the column vector $[1,0,\dots,0]$.) If such an $a$ does not exist, return
+\kbd{NULL}. This is the function underlying \tet{idealaddtoone}.
+
+\fun{GEN}{idealaddmultoone}{GEN nf, GEN v} given a list of $n$ (globally)
+coprime integer ideals $(v[i])$ returns an $n$-dimensional vector $a$ such that
+$a[i]\in v[i]$ and $\sum a[i] = 1$. If $[K:\Q] = N$, this routine computes
+the HNF reduction (with $Gl_{nN}(\Z)$ base change) of an $N\times nN$ matrix;
+so it is well worth pruning "useless" ideals from the list (as long as the
+ideals remain globally coprime).
+
+\fun{GEN}{idealappr}{GEN nf, GEN x} given a fractional ideal $x$, returns
+an algebraic number $\alpha$ such that $v(x) = v(\alpha)$ for all valuations
+such that $v(x) > 0$, and $v(\alpha) \geq 0$ at all others.
+
+\fun{GEN}{idealapprfact}{GEN nf, GEN fx} same as \tet{idealappr}, $x$ being
+given in factored form, as after \kbd{fx = idealfactor(nf,x)}, except that we
+allow $0$ exponents in the factorization. Returns an algebraic number
+$\alpha$ such that $v(x) = v(\alpha)$ for all valuations associated to the
+prime ideal decomposition of $x$, and $v(\alpha) \geq 0$ at all others.
+
+\fun{GEN}{idealcoprime}{GEN nf, GEN x, GEN y}. Given 2 integral ideals $x$ and
+$y$, returns an algebraic number $\alpha$ such that
+$\alpha x$ is an integral ideal coprime to $y$.
+
+\fun{GEN}{idealcoprimefact}{GEN nf, GEN x, GEN fy} same as
+\tet{idealcoprime}, except that $y$ is given in factored form, as from
+\tet{idealfactor}.
+
+\fun{GEN}{idealchinese}{GEN nf, GEN x, GEN y} $x$ being a prime ideal
+factorization (i.e.~a 2 by 2 matrix whose first column contain prime ideals,
+and the second column integral exponents), $y$ a vector of elements in
+$\var{nf}$ indexed by the ideals in $x$, computes an element $b$ such that
+$v_\wp(b - y_\wp) \geq v_\wp(x)$ for all prime ideals in $x$ and $v_\wp(b)\geq
+0$ for all other $\wp$.
+
+\subsec{Maximal ideals}
+
+The PARI structure associated to maximal ideals is a \tev{prid} (for
+\emph{pr}ime \emph{id}eal), usually produced by \tet{idealprimedec}
+and \tet{idealfactor}. In this section, we describe the format; other sections
+will deal with their daily use.
+
+A \var{prid} associated to a maximal ideal $\goth{p}$ stores the following
+data: the underlying rational prime $p$, the ramification degree $e\geq 1$,
+the residue field degree $f\geq 1$, a $p$-uniformizer $\pi$ with valuation
+$1$ at $\goth{p}$ and valuation $0$ at all other primes dividing $p$ and
+a rescaled ``anti-uniformizer'' $\tau$ used to compute valuations. This
+$\tau$ is an algebraic integer such that $\tau/p$ has valuation $-1$ at
+$\goth{p}$ and valuation $0$ at all other primes dividing $p$; in particular,
+the valuation of $x\in\Z_K$ is positive if and only if the algebraic integer
+$x\tau$ is divisible by $p$ (easy to check for elements in \typ{COL} form).
+
+The following functions are shallow and return directly components of the
+\var{prid} \kbd{pr}:
+
+\fun{GEN}{pr_get_p}{GEN pr} returns $p$. Shallow function.
+
+\fun{GEN}{pr_get_gen}{GEN pr} returns $\pi$. Shallow function.
+
+\fun{long}{pr_get_e}{GEN pr} returns $e$.
+
+\fun{long}{pr_get_f}{GEN pr} returns $f$.
+
+\fun{GEN}{pr_get_tau}{GEN pr} returns
+$\tet{zk_scalar_or_multable}(\var{nf}, \tau)$, which is the \typ{INT}~$1$
+iff $p$ is inert, and a \kbd{ZM} otherwise. Shallow function.
+
+\fun{int}{pr_is_inert}{GEN pr} returns $1$ if $p$ is inert, $0$ otherwise.
+
+\fun{GEN}{pr_norm}{GEN pr} returns the norm $p^f$ of the maximal ideal.
+
+\subsec{Reducing modulo maximal ideals}
+
+\fun{GEN}{nfmodprinit}{GEN nf, GEN pr} returns an abstract \kbd{modpr}
+structure, associated to reduction modulo the maximal ideal \kbd{pr}, in
+\kbd{idealprimedec} format. From this data we can quickly project any
+\kbd{pr}-integral number field element to the residue field. This function is
+almost useless in library mode, we rather use:
+
+\fun{GEN}{nf_to_Fq_init}{GEN nf, GEN *ppr, GEN *pT, GEN *pp} concrete
+version of \kbd{nfmodprinit}: \kbd{nf} and \kbd{*ppr} are the inputs, the
+return value is a \kbd{modpr} and \kbd{*ppr}, \kbd{*pT} and \kbd{*pp} are set
+as side effects.
+
+The input \kbd{*ppr} is either a maximal ideal or already a \kbd{modpr} (in
+which case it is replaced by the underlying maximal ideal). The residue field
+is realized as $\F_p[X]/(T)$ for some monic $T\in\F_p[X]$, and we set
+\kbd{*pT} to $T$ and \kbd{*pp} to $p$. Set $T = \kbd{NULL}$ if the prime has
+degree $1$ and the residue field is $\F_p$.
+
+In short, this receives (or initializes) a \kbd{modpr} structure, and
+extracts from it $T$, $p$ and $\goth{p}$.
+
+\fun{GEN}{nf_to_Fq}{GEN nf, GEN x, GEN modpr} returns an \kbd{Fq} congruent
+to $x$ modulo the maximal ideal associated to \kbd{modpr}. The output is
+canonical: all elements in a given residue class are represented by the same
+\kbd{Fq}.
+
+\fun{GEN}{Fq_to_nf}{GEN x, GEN modpr} returns an \kbd{nf} element lifting
+the residue field element $x$, either a \typ{INT} or an algebraic integer
+in \kbd{algtobasis} format.
+
+\fun{GEN}{modpr_genFq}{GEN modpr} Returns an \kbd{nf} element whose image by
+\tet{nf_to_Fq} is $X \pmod T$, if $\deg T>1$, else $1$.
+
+\fun{GEN}{zkmodprinit}{GEN nf, GEN pr} as \tet{nfmodprinit}, but we assume we
+will only reduce algebraic integers, hence do not initialize data allowing to
+remove denominators. More precisely, we can in fact still handle an $x$ whose
+rational denominator is not $0$ in the residue field (i.e. if the valuation
+of $x$ is non-negative at all primes dividing $p$).
+
+\fun{GEN}{zk_to_Fq_init}{GEN nf, GEN *pr, GEN *T, GEN *p} as
+\kbd{nf\_to\_Fq\_init}, able to reduce only $p$-integral elements.
+
+\fun{GEN}{zk_to_Fq}{GEN x, GEN modpr} as \kbd{nf\_to\_Fq}, for
+a $p$-integral $x$.
+
+\fun{GEN}{nfM_to_FqM}{GEN M, GEN nf,GEN modpr} reduces a matrix
+of \kbd{nf} elements to the residue field; returns an \kbd{FqM}.
+
+\fun{GEN}{FqM_to_nfM}{GEN M, GEN modpr} lifts an \kbd{FqM} to a matrix of
+\kbd{nf} elements.
+
+\fun{GEN}{nfV_to_FqV}{GEN A, GEN nf,GEN modpr} reduces a vector
+of \kbd{nf} elements to the residue field; returns an \kbd{FqV}
+with the same type as \kbd{A} (\typ{VEC} or \typ{COL}).
+
+\fun{GEN}{FqV_to_nfV}{GEN A, GEN modpr} lifts an \kbd{FqV} to a vector of
+\kbd{nf} elements (same type as \kbd{A}).
+
+\fun{GEN}{nfX_to_FqX}{GEN Q, GEN nf,GEN modpr} reduces a polynomial
+with \kbd{nf} coefficients to the residue field; returns an \kbd{FqX}.
+
+\fun{GEN}{FqX_to_nfX}{GEN Q, GEN modpr} lifts an \kbd{FqX} to a polynomial
+with coefficients in \kbd{nf}.
+
+\subsec{Valuations}
+
+\fun{long}{nfval}{GEN nf, GEN x, GEN P} return $v_P(x)$
+
+\misctitle{Unsafe functions} assume \var{nf} is a genuine \kbd{nf}
+structure, that $P$, $Q$ are \kbd{prid}.
+
+\fun{long}{ZC_nfval}{GEN nf, GEN x, GEN P} returns $v_P(x)$,
+assuming $x$ is a \kbd{ZC}, representing a non-zero algebraic integer.
+
+\fun{long}{ZC_nfvalrem}{GEN nf, GEN x, GEN pr, GEN *newx} returns $v = v_P(x)$,
+assuming $x$ is a \kbd{ZC}, representing a non-zero algebraic integer, and sets
+\kbd{*newx} to $x\tau^v$ which is an algebraic integer coprime to $p$.
+
+\fun{int}{ZC_prdvd}{GEN nf, GEN x, GEN P} returns $1$ is $P$ divides $x$ and
+$0$ otherwise. Assumes that $x$ is a \kbd{ZC}, representing an algebraic
+integer. Faster than computing $v_P(x)$.
+
+\fun{int}{pr_equal}{GEN nf, GEN P, GEN Q} returns 1 is $P$ and $Q$ represent
+the same maximal ideal: they must lie above the same $p$ and share the same
+$e,f$ invariants, but the $p$-uniformizer and $\tau$ element may differ.
+Returns $0$ otherwise.
+
+\subsec{Signatures}
+
+``Signs'' of the real embeddings of number field element are represented in
+additive notation, using the standard identification $(\Z/2\Z, +) \to
+(\{-1,1\},\times)$, $s\mapsto (-1)^s$.
+
+With respect to a fixed \kbd{nf} structure, a selection of real places (a
+divisor at infinity) is normally given as a \typ{VECSMALL} of indices of the
+roots \kbd{nf.roots} of the defining polynomial for the number field. For
+compatibility reasons, in particular under GP, the (obsolete) \kbd{vec01}
+form is also accepted: a \typ{VEC} with \kbd{gen\_0} or \kbd{gen\_1} entries.
+
+The following internal functions go back and forth between the two
+representations for the Archimedean part of divisors (GP: $0/1$ vectors,
+library: list of indices):
+
+\fun{GEN}{vec01_to_indices}{GEN v} given a \typ{VEC} $v$ with \typ{INT} entries
+equal to $0$ or $1$, return as a \typ{VECSMALL} the list of indices $i$
+such that $v[i] = 1$. If $v$ is already a \typ{VECSMALL}, return it
+(not suitable for \kbd{gerepile} in this case).
+
+\fun{GEN}{indices_to_vec01}{GEN p, long n} return the $0/1$ vector of length
+$n$ with ones exactly at the positions $p[1], p[2], \ldots$
+
+
+\fun{GEN}{nfsign}{GEN nf,GEN x} $x$ being a number field element and \kbd{nf}
+any form of number field, return the $0-1$-vector giving the signs of the
+$r_1$ real embeddings of $x$, as a \typ{VECSMALL}. Linear algebra functions
+like \tet{Flv_add_inplace} then allow keeping track of signs in series of
+multiplications.
+
+If $x$ is a \typ{VEC} of number field elements, return the matrix whose
+columns are the signs of the $x[i]$.
+
+\fun{GEN}{nfsign_arch}{GEN nf,GEN x,GEN arch} \kbd{arch} being a list of
+distinct real places, either in \kbd{vec01} (\typ{VEC} with \kbd{gen\_0} or
+\kbd{gen\_1} entries) or \kbd{indices} (\typ{VECSMALL}) form (see
+\tet{vec01_to_indices}), returns the signs of $x$ at the corresponding
+places. This is the low-level function underlying \kbd{nfsign}.
+
+\fun{GEN}{nfsign_units}{GEN bnf, GEN archp, int add_tu}
+\kbd{archp} being a divisor at infinity in \kbd{indices} form
+(or \kbd{NULL} for the divisor including all real places), return the signs
+at \kbd{archp} of a system of fundamental units for the field, in the same
+order as \kbd{bnf.tufu} if \kbd{add\_tu} is set; and in the same order as
+\kbd{bnf.fu} otherwise.
+
+\fun{GEN}{nfsign_from_logarch}{GEN L, GEN invpi, GEN archp} given $L$
+the vector of the $\log \sigma(x)$, where $\sigma$ runs through the (real
+or complex) embeddings of some number field, \kbd{invpi} being
+a floating point approximation to $1/\pi$, and \kbd{archp} being a divisor
+at infinity in \kbd{indices} form, return the signs of $x$
+at the corresponding places. This is the low-level function underlying
+\kbd{nfsign\_units}; the latter is actually a trivial wrapper
+\kbd{bnf} structures include the $\log \sigma(x)$ for a system of fundamental
+units of the field.
+
+\fun{GEN}{set_sign_mod_divisor}{GEN nf, GEN x, GEN y, GEN module, GEN sarch}
+let $f = f_0f_\infty$ be the divisor represented by \kbd{module}, $x$, $y$ two
+number field elements. Returns $yt$ with $t = 1 \text{mod}^* f$ such that $x$
+and $ty$ have the same signs at $f_\infty$; if $x =
+\kbd{NULL}$, make $ty$ totally positive at $f_\infty$. \kbd{sarch} is the
+output of \kbd{nfarchstar(nf, f0, finf)}.
+
+\fun{GEN}{nfarchstar}{GEN nf, GEN f0, GEN finf} for a divisor $f =
+f_0f_\infty$ represented by the integral ideal \kbd{f0} in HNF and
+the \kbd{finf} in \kbd{indices} form, returns $(\Z_K/f_\infty)^*$ in a form
+suitable for computations mod $f$. More precisely, returns
+$[c, g, M]$, where $c = [2,\ldots, 2]$ gives the cyclic structure of that
+group ($\#f_\infty$ copies of $\Z/2\Z$), $g$ a minimal system of independent
+generators, which are furthermore congruent to $1$ mod $f_0$ (no condition if
+$f_0 = \Z_K$), and $M$ is the matrix of signs of the $g[i]$ at $f_\infty$,
+which is square and invertible over $\F_2$.
+
+\fun{GEN}{idealprincipalunits}{GEN nf, GEN pr, long e} returns the
+multiplicative group $(1 + \var{pr}) / (1 + \var{pr}^e)$ as an abelian group.
+Faster than \tet{idealstar} when the norm of \var{pr} is large, since it
+avoids (useless) work in the multiplicative group of the residue field.
+
+\subsec{Maximal order and discriminant}
+
+A number field $K = \Q[X]/(T)$ is defined by a monic $T\in\Z[X]$. The
+low-level function computing a maximal order is
+
+\fun{void}{nfmaxord}{nfmaxord_t *S, GEN T0, long flag}, where
+the polynomial $T_0$ is squarefree with integer coefficients. Let $K$ be the
+\'etale algebra $\Q[X]/(T_0)$ and let $T = \kbd{ZX\_Q\_normalize}(T_0)$,
+i.e. $T = C T_0(X/L)$ is monic and integral for some $C,Q\in \Q$.
+
+The structure \tet{nfmaxord_t} is initialized by the call; it has the
+following fields:
+\bprog
+  GEN T0, T, dT, dK; /* T0, T, discriminants of T and K */
+  GEN unscale; /* the integer L */
+  GEN index; /* index of power basis in maximal order */
+  GEN dTP, dTE; /* factorization of |dT|, primes / exponents */
+  GEN dKP, dKE; /* factorization of |dK|, primes / exponents */
+  GEN basis; /* Z-basis for maximal order of Q[X]/(T) */
+ at eprog\noindent The exponent vectors are \typ{VECSMALL}. The primes
+in \kbd{dTP} and \kbd{dKP} are pseudoprimes, not proven primes. We recommend
+restricting to $T = T_0$, i.e. either to pass the input polynomial through
+\tet{ZX_Q_normalize} \emph{before} the call, or to forget about $T_0$
+and go on with the polynomial $T$; otherwise $\kbd{unscale}\neq 1$, all data
+is expressed in terms of $T\neq T_0$, and needs to be converted to $T_0$. For
+instance to convert the basis to $\Q[X]/(T_0)$:
+\bprog
+  RgXV_unscale(S.basis, S.unscale)
+ at eprog
+
+Instead of passing $T$, one can use the format $[T,\var{listP}]$ as in
+\kbd{nfbasis} or \kbd{nfinit}, which computes an order which is maximal
+at a set of primes, but need not be the maximal order.
+
+The \kbd{flag} is an or-ed combination of the binary flags:
+
+\tet{nf_PARTIALFACT}: do not try to fully factor \kbd{dT} and only look for
+primes less than \kbd{primelimit}. In that case, the elements in \kbd{dTP}
+and \kbd{dKP} need not all be primes. But the resulting \kbd{dK},
+\kbd{index} and \kbd{basis} are correct provided there exists no prime $p >
+\kbd{primelimit}$ such that $p^2$ divides the field discriminant \kbd{dK}.
+This flag is \emph{deprecated}: the $[T,\var{listP}]$ is safer and more
+flexible.
+
+\tet{nf_ROUND2}: use the ROUND2 algorithm instead of the default ROUND4.
+This flag is \emph{deprecated}: this algorithm is consistently slower.
+
+$T$ is the input polynomial (monic \kbd{ZX}). The format $[T, \kbd{listP}]$
+is also recognized, where \kbd{listP} is as in \tet{nfbasis} and is used to
+compute a local integral basis with respect to a specific set of primes.
+
+\fun{GEN}{indexpartial}{GEN T, GEN dT} $T$ a monic separable \kbd{ZX},
+\kbd{dT} is either \kbd{NULL} (no information) or a multiple of the
+discriminant of $T$. Let $K = \Q[X]/(T)$ and $\Z_K$ its maximal order.
+Returns a multiple of the exponent of the quotient group $\Z_K/(\Z[X]/(T))$.
+In other word, a \emph{denominator} $d$ such that $d x\in\Z[X]/(T)$ for all
+$x\in\Z_K$.
+
+\subsec{Computing in the class group}
+
+We compute with arbitrary ideal representatives (in any of the various
+formats seen above), and call
+
+\fun{GEN}{bnfisprincipal0}{GEN bnf, GEN x, long flag}. The \kbd{bnf}
+structure already contains information about the class group in the form
+$\oplus_{i=1}^n (\Z/d_i\Z) g_i$ for canonical integers $d_i$
+(with $d_n\mid\dots\mid d_1$ all $> 1$) and essentially random generators
+$g_i$, which are ideals in HNF. We normally do not need the value of the
+$g_i$, only that they are fixed once and for all and that any (non-zero)
+fractional ideal $x$ can be expressed uniquely as $x = (t)\prod_{i=1}^n
+g_i^{e_i}$, where $0 \leq e_i < d_i$, and $(t)$ is some principal ideal.
+Computing $e$ is straightforward, but $t$ may be very expensive to obtain
+explicitly. The routine returns (possibly partial) information about the pair
+$[e,t]$, depending on \kbd{flag}, which is an or-ed combination of the
+following symbolic flags:
+
+\item \tet{nf_GEN} tries to compute $t$.
+Returns $[e,t]$, with $t$ an empty vector if the computation failed. This
+flag is normally useless in non-trivial situations since the next two serve
+analogous purposes in more efficient ways.
+
+\item \tet{nf_GENMAT} tries to compute $t$ in factored form, which is
+much more efficient than \kbd{nf\_GEN} if the class group is moderately
+large; imagine a small ideal $x = (t)g^{10000}$: the norm of $t$ has $10000$
+as many digits as the norm of $g$; do we want to see it as a vector
+of huge meaningless integers? The idea is to compute $e$ first, which is
+easy, then compute $(t)$ as $x \prod g_i^{-e_i}$ using successive
+\tet{idealmulred}, where the ideal reduction extracts small principal ideals
+along the way, eventually raised to large powers because of the binary
+exponentiation technique; the point is to keep this principal part in
+factored \emph{unexpanded} form. Returns $[e,t]$, with $t$ an empty vector if
+the computation failed; this should be exceedingly rare, unless the initial
+accuracy to which \kbd{bnf} was computed was ridiculously low (and then
+\kbd{bnfinit} should not have succeeded either). Setting/unsetting
+\kbd{nf\_GEN} has no effect when this flag is set.
+
+\item \tet{nf_GEN_IF_PRINCIPAL} tries to compute $t$ \emph{only} if the
+ideal is principal ($e = 0$). Returns \kbd{gen\_0} if the ideal is not
+principal. Setting/unsetting \kbd{nf\_GEN} has no effect when this flag is
+set, but setting/unsetting \kbd{nf\_GENMAT} is possible.
+
+\item \tet{nf_FORCE} in the above, insist on computing $t$, even if it
+requires recomputing a \kbd{bnf} from scratch. This is a last resort, and
+normally the accuracy of a \kbd{bnf} can be increased without trouble, but it
+may be that some algebraic information simply cannot be recovered from what
+we have: see \tet{bnfnewprec}. It should be very rare, though.
+
+In simple cases where you do not care about $t$, you may use
+
+\fun{GEN}{isprincipal}{GEN bnf, GEN x}, which is a shortcut for
+\kbd{bnfisprincipal0(bnf, x, 0)}.
+
+The following low-level functions are often more useful:
+
+\fun{GEN}{isprincipalfact}{GEN bnf, GEN C, GEN L, GEN f, long flag} is
+about the same as \kbd{bnfisprincipal0} applied to $C \prod L[i]^{f[i]}$,
+where the $L[i]$ are ideals, the $f[i]$ integers and $C$ is either an ideal
+or \kbd{NULL} (omitted). Make sure to include \tet{nf_GENMAT} in \kbd{flag}!
+
+\fun{GEN}{isprincipalfact_or_fail}{GEN bnf, GEN C, GEN L, GEN f} is
+for delicate cases, where we must be more clever than \kbd{nf\_FORCE}
+(it is used when trying to increase the accuracy of a \var{bnf}, for
+instance). If performs
+\bprog
+  isprincipalfact(bnf,C, L, f, nf_GENMAT);
+ at eprog\noindent
+but if it fails to compute $t$, it just returns a \typ{INT}, which is the
+estimated precision (in words, as usual) that would have been sufficient to
+complete the computation. The point is that \kbd{nf\_FORCE} does exactly this
+internally, but goes on increasing the accuracy of the \kbd{bnf}, then
+discarding it, which is a major inefficiency if you intend to compute lots of
+discrete logs and have selected a precision which is just too low.
+(It is sometimes not so bad since most of the really expensive data is cached
+in \kbd{bnf} anyway, if all goes well.)  With this function, the \emph{caller}
+may decide to increase the accuracy using \tet{bnfnewprec} (and keep the
+resulting \kbd{bnf}!), or avoid the computation altogether. In any case the
+decision can be taken at the place where it is most likely to be correct.
+
+\subsec{Floating point embeddings, the $T_2$ quadratic form}
+
+We assume the \var{nf} is a true \kbd{nf} structure, associated to a number
+field $K$ of degree $n$ and signature $(r_1,r_2)$. We saw that
+
+\fun{GEN}{nf_get_M}{GEN nf} returns the $(r_1+r_2)\times n$ matrix $M$
+giving the embeddings of $K$, so that if $v$ is an $n$-th dimensional
+\typ{COL} representing the element $\sum_{i=1}^n v[i] w_i$ of $K$, then
+\kbd{RgM\_RgC\_mul(M,v)} represents the embeddings of $v$. Its first $r_1$
+components are real numbers (\typ{INT}, \typ{FRAC} or \typ{REAL}, usually the
+latter), and the last $r_2$ are complex numbers (usually of \typ{COMPLEX},
+but not necessarily for embeddings of rational numbers).
+
+\fun{GEN}{embed_T2}{GEN x, long r1} assuming $x$ is the vector of floating point
+embeddings of some algebraic number $v$, i.e.
+\bprog
+  x = RgM_RgC_mul(nf_get_M(nf), algtobasis(nf,v));
+ at eprog\noindent returns $T_2(v)$. If the floating point embeddings themselves
+are not needed, but only the values of $T_2$, it is more efficient to
+restrict to real arithmetic and use
+\bprog
+  gnorml2( RgM_RgC_mul(nf_get_G(nf), algtobasis(nf,v)));
+ at eprog
+
+\fun{GEN}{embednorm_T2}{GEN x, long r1} analogous to \tet{embed_T2},
+applied to the \kbd{gnorm} of the floating point embeddings. Assuming that
+\bprog
+  x = gnorm( RgM_RgC_mul(nf_get_M(nf), algtobasis(nf,v)) );
+ at eprog\noindent returns $T_2(v)$.
+
+\fun{GEN}{embed_roots}{GEN z, long r1} given a vector $z$ of $r_1+r_2$
+complex embeddings of the algebraic number $v$, return the $r_1+2r_2$ roots
+of its characteristic polynomial. Shallow function.
+
+\fun{GEN}{embed_disc}{GEN z, long r1, long prec} given a vector $z$ of
+$r_1+r_2$ complex embeddings of the algebraic number $v$, return a floating
+point approximation of the discriminant of its characteristic polynomial as a
+\typ{REAL} of precision \kbd{prec}.
+
+\fun{GEN}{embed_norm}{GEN x, long r1} given a vector $z$ of $r_1+r_2$ complex
+embeddings of the algebraic number $v$, return (a floating point
+approximation of) the norm of $v$.
+
+\subsec{Ideal reduction, low level}
+
+In the following routines \var{nf} is a true \kbd{nf}, associated to a number
+field $K$ of degree $n$:
+
+\fun{GEN}{nf_get_Gtwist}{GEN nf, GEN v} assuming $v$ is a \typ{VECSMALL}
+with $r_1+r_2$ entries, let
+$$|| x ||_v^2 = \sum_{i=1}^{r_1+r_2} 2^{v_i}\varepsilon_i|\sigma_i(x)|^2,$$
+where as usual the $\sigma_i$ are the (real and) complex embeddings and
+$\varepsilon_i = 1$, resp.~$2$, for a real, resp.~complex place.
+This is a twisted variant of the $T_2$ quadratic form, the standard Euclidean
+form on $K\otimes \R$. In applications, only the relative size of the $v_i$
+will matter.
+
+Let $G_v\in M_n(\R)$ be a square matrix such that if $x\in K$ is represented by
+the column vector $X$ in terms of the fixed $\Z$-basis of $\Z_K$ in \var{nf},
+then
+$$||x||_v^2 = {}^t (G_v X) \cdot G_v X.$$
+(This is a kind of Cholesky decomposition.) This function
+returns a rescaled copy of $G_v$, rounded to nearest integers, specifically
+\tet{RM_round_maxrank}$(G_v)$.
+Suitable for \kbd{gerepileupto}, but does not collect garbage.
+
+\fun{GEN}{nf_get_Gtwist1}{GEN nf, long i}. Simple special case. Returns the
+twisted $G$ matrix associated to the vector $v$ whose entries are all $0$
+except the $i$-th one, which is equal to $10$.
+
+\fun{GEN}{idealpseudomin}{GEN x, GEN G}. Let $x$, $G$ be two \kbd{ZM}s,
+such that the product $Gx$ is well-defined. This returns a ``small'' integral
+linear combinations of the columns of $x$, given by the LLL-algorithm applied
+to the lattice $G x$. Suitable for \kbd{gerepileupto}, but does not collect
+garbage.
+
+In applications, $x$ is an integral ideal, $G$ approximates a Cholesky form for
+the $T_2$ quadratic form as returned by \tet{nf_get_Gtwist}, and we return
+a small element $a$ in the lattice $(x,T_2)$. This is used to implement
+\tet{idealred}.
+
+\fun{GEN}{idealpseudomin_nonscalar}{GEN x, GEN G}. As \tet{idealpseudomin},
+but we insist of returning a non-scalar $a$ (\kbd{ZV\_isscalar} is false), if
+the dimension of $x$ is $> 1$.
+
+In the interpretation where $x$ defines an integral ideal on a fixed $\Z_K$
+basis whose first element is $1$, this means that $a$ is not rational.
+
+\fun{GEN}{idealred_elt}{GEN nf, GEN x} shortcut for
+\bprog
+  idealpseudomin(x, nf_get_roundG(nf))
+ at eprog
+
+\subsec{Ideal reduction, high level} \label{se:Ideal_reduction}
+
+Given an ideal $x$ this means finding a ``simpler'' ideal in the same ideal
+class. The public GP function is of course available
+
+\fun{GEN}{idealred0}{GEN nf, GEN x, GEN v} finds a small $a\in x$ and returns
+the primitive part of $x/(a)$, as an ideal in HNF. What ``small'' means
+depends on the parameter $v$, see the GP description. More precisely,
+$a$ is returned by \kbd{idealpseudomin}$(x,G)$, where $G$
+is \tet{nf_get_Gtwist}$(\var{nf}, v)$ for $v\neq \kbd{NULL}$
+and \tet{nf_get_roundG}$(\var{nf})$ otherwise.
+
+\noindent Usually one sets $v = \kbd{NULL}$ to obtain an element of small $T_2$
+norm in $x$:
+
+\fun{GEN}{idealred}{GEN nf, GEN x} is a shortcut for \kbd{idealred0(nf,x,NULL)}.
+
+The function \kbd{idealred} remains complicated to use: in order not to lose
+information $x$ must be an extended ideal, otherwise the value of $a$ is lost.
+There is a subtlety here: the principal ideal $(a)$ is easy to recover, but $a$
+itself is an instance of the principal ideal problem which is very difficult
+given only an \var{nf} (once a \var{bnf} structure is available,
+\tet{bnfisprincipal0} will recover it). It is in general simpler to use
+directly \tet{idealred_elt}.
+
+\fun{GEN}{idealmoddivisor}{GEN bnr, GEN x} A proof-of-concept implementation,
+useless in practice. If \kbd{bnr} is associated to some modulus $f$, returns a
+``small'' ideal in the same class as $x$ in the ray class group modulo $f$.
+The reason why this is useless is that using extended ideals with principal
+part in a computation, there is a simple way to reduce them: simply reduce
+the generator of the principal part in $(\Z_K/f)^*$.
+
+\fun{GEN}{famat_to_nf_moddivisor}{GEN nf, GEN g, GEN e, GEN bid}
+given a true \var{nf} associated to a number field $K$, a \var{bid} structure
+associated to a modulus $f$, and an algebraic number in factored form $\prod
+g[i]^{e[i]}$, such that $(g[i],f) = 1$ for all $i$, returns a small element in
+$\Z_K$ congruent to it mod $f$. Note that if $f$ contains places at infinity,
+this includes sign conditions at the specified places.
+
+A simpler case when the conductor has no place at infinity:
+
+\fun{GEN}{famat_to_nf_modideal_coprime}{GEN nf, GEN g, GEN e, GEN f, GEN expo}
+as above except that the ideal $f$ is now integral in HNF (no need for a full
+\var{bid}), and we pass the exponent of the group $(\Z_K/f)^*$ as \kbd{expo};
+any multiple will also do, at the expense of efficiency. Of course if a
+\var{bid} for $f$ is available, if is easy to extract $f$ and the exact value
+of \kbd{expo} from it (the latter is the first elementary divisor in the
+group structure). A useful trick: if you set \kbd{expo} to \emph{any}
+positive integer, the result is correct up to \kbd{expo}-th powers, hence
+exact if \kbd{expo} is a multiple of the exponent; this is useful when trying
+to decide whether an element is a square in a residue field for instance!
+(take \kbd{expo}$ = 2$).
+
+What to do when the $g[i]$ are not coprime to $f$, but only $\prod
+g[i]^{e[i]}$ is? Then the situation is more complicated, and we advise to
+solve it one prime divisor of $f$ at a time. Let $v$ the valuation
+associated to a maximal ideal \kbd{pr} and assume $v(f) = k > 0$:
+
+\fun{GEN}{famat_makecoprime}{GEN nf, GEN g, GEN e, GEN pr, GEN prk, GEN expo}
+returns an element in $(\Z_K/\kbd{pr}^k)^*$ congruent to the product
+$\prod g[i]^{e[i]}$, assumed to be globally coprime to $f$. As above,
+\kbd{expo} is any positive multiple of the exponent of $(\Z_K/\kbd{pr}^k)^*$,
+for instance $(Nv-1)p^{k-1}$, if $p$ is the underlying rational prime. You
+may use other values of \kbd{expo} (see the useful trick in
+\tet{famat_to_nf_modideal_coprime}).
+
+\subsec{Class field theory}
+
+Under GP, a class-field theoretic description of a number field is given by a
+triple $A$, $B$, $C$, where the defining set $[A,B,C]$ can have any of the
+following forms: $[\var{bnr}]$, $[\var{bnr},\var{subgroup}]$,
+$[\var{bnf},\var{modulus}]$, $[\var{bnf},\var{modulus},\var{subgroup}]$.
+You can still use directly all of (\kbd{libpari}'s routines implementing) GP's
+functions as described in Chapter~3, but they are often awkward in the context
+of \kbd{libpari} programming. In particular, it does not make much sense to
+always input a triple $A,B,C$ because of the fringe
+$[\var{bnf},\var{modulus},\var{subgroup}]$. The first routine to call, is
+thus
+
+\fun{GEN}{Buchray}{GEN bnf, GEN mod, long flag} initializes a \var{bnr}
+structure from \kbd{bnf} and modulus \kbd{mod}. \kbd{flag} is an or-ed
+combination of \kbd{nf\_GEN} (include generators) and \kbd{nf\_INIT} (if
+omitted, do not return a \var{bnr}, only the ray class group as an abelian
+group). In fact, a single value of \kbd{flag} actually makes sense:
+\kbd{nf\_GEN | nf\_INIT} to initialize a proper \var{bnr}: removing
+\kbd{nf\_GEN} saves very little time, but the corresponding crippled
+\var{bnr} structure will raise errors in most class field theoretic
+functions. Possibly also 0 to quickly compute the ray class group structure;
+\tet{bnrclassno} is faster if we only need the \emph{order} of the ray class
+group.
+
+Now we have a proper \var{bnr} encoding a \kbd{bnf} and a modulus, we no longer
+need the $[\var{bnf},\var{modulus}]$ and
+$[\var{bnf},\var{modulus},\var{subgroup}]$ forms, which would internally call
+\tet{Buchray} anyway. Recall that a subgroup $H$ is given by a matrix in HNF,
+whose column express generators of $H$ on the fixed generators of the ray class
+group that stored in our \var{bnr}. You may also code the trivial subgroup by
+\kbd{NULL}.
+
+\fun{GEN}{bnrconductor}{GEN bnr, GEN H, long flag} see the documentation of
+the GP function.
+
+\fun{long}{bnrisconductor}{GEN bnr, GEN H} returns 1 is the class field
+defined by the subgroup $H$ (of the ray class group mod $f$ coded in \kbd{bnr})
+has conductor $f$. Returns 0 otherwise.
+
+\fun{GEN}{bnrdisc}{GEN bnr, GEN H, long flag} returns the discriminant and
+signature of the class field defined by \kbd{bnr} and $H$. See the description
+of the GP function for details. \fl\ is an or-ed combination of the flags
+\tet{rnf_REL} (output relative data) and \tet{rnf_COND} (return 0 unless the
+modulus is the conductor).
+
+\fun{GEN}{bnrsurjection}{GEN BNR, GEN bnr} \kbd{BNR} and \kbd{bnr}
+defined over the same field $K$, for moduli $F$ and $f$ with
+$F\mid f$, returns the matrix of the canonical surjection
+$\text{Cl}_K(F)\to \text{Cl}_K(f)$ (giving the image of the fixed ray class
+group generators of \kbd{BNR} in terms of the ones in \kbd{bnr}).
+\kbd{BNR} must include the ray class group generators.
+
+\fun{GEN}{ABC_to_bnr}{GEN A, GEN B, GEN C, GEN *H, int addgen} This is a
+quick conversion function designed to go from the too general (inefficient)
+$A$, $B$, $C$ form to the preferred \var{bnr}, $H$ form for class fields.
+Given $A$, $B$, $C$ as explained above (omitted entries coded by \kbd{NULL}),
+return the associated \var{bnr}, and set $H$ to the associated subgroup. If
+\kbd{addgen} is $1$, make sure that if the \var{bnr} needed to be computed,
+then it contains generators.
+
+\subsec{Relative equations, Galois conjugates}
+
+\fun{GEN}{rnfequationall}{GEN A, GEN B, long *pk, GEN *pLPRS} $A$ is either an
+\var{nf} type (corresponding to a number field $K$) or an irreducible \kbd{ZX}
+defining a number field $K$. $B$ is an irreducible polynomial in $K[X]$.
+Returns an absolute equation $C$ (over $\Q$) for the number field $K[X]/(B)$.
+$C$ is the characteristic polynomial of $b + k a$ for some roots $a$ of $A$
+and $b$ of $B$, and $k$ is a small rational integer. Set \kbd{*pk} to $k$.
+
+If \kbd{pLPRS} is not \kbd{NULL} set it to $[h_0, h_1]$, $h_i\in \Q[X]$,
+where $h_0+h_1 Y$ is the last non-constant polynomial in the pseudo-Euclidean
+remainder sequence associated to $A(Y)$ and $B(X-kY)$, leading to $C =
+\text{Res}_Y(A(Y), B(Y-kX))$. In particular $a := -h_0/h_1$ is a root of $A$
+in $\Q[X]/(C)$, and $X - ka$ is a root of $B$.
+
+\fun{GEN}{nf_rnfeq}{GEN A, GEN B} wrapper around \tet{rnfequationall} to allow
+mapping $K\to L$ (\kbd{eltup}) and converting elements of $L$
+between absolute and relative form (\kbd{reltoabs}, \kbd{abstorel}),
+\emph{without} computing a full \var{rnf} structure, which is useful if the
+relative integral basis is not required. In fact, since $A$ may be a \typ{POL}
+or an \var{nf}, the integral basis of the base field is not needed either. The
+return value is the same as \tet{rnf_get_map}. Shallow function.
+
+\fun{GEN}{nf_rnfeqsimple}{GEN nf, GEN relpol} as \tet{nf_rnfeq} except some
+fields are omitted, so that only the \tet{abstorel} operation is supported.
+Shallow function.
+
+\fun{GEN}{eltabstorel}{GEN rnfeq, GEN x} \kbd{rnfeq} is as
+given by \tet{rnf_get_map} (but in this case \tet{rnfeltabstorel} is more
+robust), \tet{nf_rnfeq} or \tet{nf_rnfeqsimple}, return $x$ as an element
+of $L/K$, i.e. as a \typ{POLMOD} with \typ{POLMOD} coefficients. Shallow
+function.
+
+\fun{GEN}{eltabstorel_lift}{GEN rnfeq, GEN x} same as \tet{eltabstorel},
+except that $x$ is returned in partially lifted form, i.e.~ as a
+\typ{POL} with \typ{POLMOD} coefficients.
+
+\fun{GEN}{eltreltoabs}{GEN rnfeq, GEN x} \kbd{rnfeq} is as given by
+\tet{rnf_get_map} (but in this case \tet{rnfeltreltoabs} is more robust)
+or \tet{nf_rnfeq}, return $x$ in absolute form.
+
+\fun{void}{nf_nfzk}{GEN nf, GEN rnfeq, GEN *zknf, GEN *czknf} \kbd{rnfeq} as
+given by \tet{nf_rnfeq}, \kbd{nf} a true \var{nf} structure, set \kbd{*zknf}
+and \kbd{*czknf} to a suitable representation of \kbd{nf.zk} allowing quick
+computation of the map $K\to L$ by the function \tet{nfeltup}, \emph{without}
+computing a full \var{rnf} structure, which is useful if the relative
+integral basis is not required. The computed values are the same as in
+\tet{rnf_get_nfzk}. Shallow function.
+
+\fun{GEN}{nfeltup}{GEN nf, GEN x, GEN zknf, GEN czknf} \kbd{zknf} and
+\kbd{czknf} are initialized by \tet{nf_nfzk} or \tet{rnf_get_nfzk} (but in
+this case \tet{rnfeltup} is more robust); \kbd{nf} is a true \var{nf}
+structure for $K$, returns $x \in K$ as a (lifted) element of $L$, in
+absolute form.
+
+\fun{GEN}{Rg_nffix}{const char *f, GEN T, GEN c, int lift} given
+a \kbd{ZX} $T$ and a ``coefficient'' $c$ supposedly belonging to $\Q[y]/(T)$,
+check whether this is a the case and return a cleaned up version of $c$.
+The string $f$ is the calling function name, used to report errors.
+
+This means that $c$ must be one of \typ{INT}, \typ{FRAC}, \typ{POL} in the
+variable $y$ with rational coefficients, or \typ{POLMOD} modulo $T$ which lift
+to a rational \typ{POL} as above. The cleanup consists in the following
+improvements:
+
+\item \typ{POL} coefficients are reduced modulo $T$.
+
+\item \typ{POL} and \typ{POLMOD} belonging to $\Q$ are converted to rationals,
+\typ{INT} or \typ{FRAC}.
+
+\item if \kbd{lift} is non-zero, convert \typ{POLMOD} to \typ{POL},
+and otherwise convert \typ{POL} to \typ{POLMOD}s modulo $T$.
+
+\fun{GEN}{RgX_nffix}{const char *f, GEN T, GEN P, int lift} check whether
+$P$ is a polynomials with coefficients in the number field defined by the
+absolute equation $T(y) = 0$, where $T$ is a \kbd{ZX} and returns a cleaned
+up version of $P$. This checks whether $P$ is indeed a \typ{POL}
+with variable compatible with coefficients in $\Q[y]/(T)$, i.e.
+\bprog
+  varncmp(varn(P), varn(T)) < 0
+ at eprog\noindent and applies \tet{Rg_nffix} to each coefficient.
+
+\fun{GEN}{RgV_nffix}{const char *f, GEN T, GEN P, int lift} as \tet{RgX_nffix}
+for a vector of coefficients.
+
+\fun{GEN}{polmod_nffix}{const char *f, GEN rnf, GEN x, int lift} given
+a \typ{POLMOD} $x$ supposedly defining an element of \var{rnf}, check this
+and perform \tet{Rg_nffix} cleanups.
+
+\fun{GEN}{polmod_nffix2}{const char *f, GEN T, GEN P, GEN x, int lift} as
+\tet{polmod_nffix}, where the relative extension is explicitly defined
+as $L = (\Q[y]/(T))[x]/(P)$, instead of by an \kbd{rnf} structure.
+
+\fun{long}{numberofconjugates}{GEN T, long pinit} returns a quick
+multiple for the number of  $\Q$-automorphism of the (integral, monic)
+\typ{POL} $T$, from modular factorizations, starting from prime \kbd{pinit}
+(you can set it to $2$). This upper bounds often coincides with the
+actual number of conjugates. Of course, you should use \tet{nfgaloisconj}
+to be sure.
+
+\subsec{Obsolete routines}
+
+Still provided for backward compatibility, but should not be used in new
+programs. They will eventually disappear.
+
+\fun{GEN}{zidealstar}{GEN nf, GEN x} short for \kbd{Idealstar(nf,x,nf\_GEN)}
+
+\fun{GEN}{zidealstarinit}{GEN nf, GEN x}
+short for \kbd{Idealstar(nf,x,nf\_INIT)}
+
+\fun{GEN}{zidealstarinitgen}{GEN nf, GEN x}
+short for \kbd{Idealstar(nf,x,nf\_GEN|nf\_INIT)}
+
+\fun{GEN}{buchimag}{GEN D, GEN c1, GEN c2, GEN gCO} short for
+\bprog
+  Buchquad(D,gtodouble(c1),gtodouble(c2), /*ignored*/0)
+ at eprog
+
+\fun{GEN}{buchreal}{GEN D, GEN gsens, GEN c1, GEN c2, GEN RELSUP, long prec}
+short for
+\bprog
+Buchquad(D,gtodouble(c1),gtodouble(c2), prec)
+ at eprog
+
+The following use a naming scheme which is error-prone and not easily
+extensible; besides, they compute generators as per \kbd{nf\_GEN} and
+not \kbd{nf\_GENMAT}. Don't use them:
+
+\fun{GEN}{isprincipalforce}{GEN bnf,GEN x}
+
+\fun{GEN}{isprincipalgen}{GEN bnf, GEN x}
+
+\fun{GEN}{isprincipalgenforce}{GEN bnf, GEN x}
+
+\fun{GEN}{isprincipalraygen}{GEN bnr, GEN x}, use \tet{bnrisprincipal}.
+
+\noindent Variants on \kbd{polred}: use \kbd{polredbest}.
+
+\fun{GEN}{factoredpolred}{GEN x, GEN fa}
+
+\fun{GEN}{factoredpolred2}{GEN x, GEN fa}
+
+\fun{GEN}{smallpolred}{GEN x}
+
+\fun{GEN}{smallpolred2}{GEN x}, use \tet{Polred}.
+
+\fun{GEN}{polredabs}{GEN x}
+
+\fun{GEN}{polredabs2}{GEN x}
+
+\fun{GEN}{polredabsall}{GEN x, long flun}
+
+\noindent \kbd{nfmaxord} wrappers implementing the old \kbd{nfbasis} interface:
+use the $[T,\var{listP}$ format.
+
+\fun{GEN}{polred0}{GEN x, long flag, GEN p}
+
+\fun{GEN}{nfbasis0}{GEN x,long flag,GEN p}
+
+\fun{GEN}{nfdisc0}{GEN x,long flag, GEN p}
+
+\fun{GEN}{factorpadic0}{GEN f,GEN p,long r,long flag}
+
+\noindent Superseded by \tet{bnrdisc}:
+
+\fun{GEN}{discrayabs}{GEN bnr,GEN subgroup}
+
+\fun{GEN}{discrayabscond}{GEN bnr,GEN subgroup}
+
+\fun{GEN}{discrayrel}{GEN bnr,GEN subgroup}
+
+\fun{GEN}{discrayrelcond}{GEN bnr,GEN subgroup}
+
+\noindent Superseded by \tet{bnrdisclist0}:
+
+\fun{GEN}{discrayabslist}{GEN bnf,GEN listes}
+
+\fun{GEN}{discrayabslistarch}{GEN bnf, GEN arch, long bound}
+
+\fun{GEN}{discrayabslistlong}{GEN bnf, long bound}
+
+\section{Galois extensions of $\Q$}
+
+This section describes the data structure output by the function
+\tet{galoisinit}. This will be called a \kbd{gal} structure in
+the following.
+
+\subsec{Extracting info from a \kbd{gal} structure}
+
+The functions below expect a \kbd{gal} structure and are shallow. See the
+documentation of \tet{galoisinit} for the meaning of the member functions.
+
+\fun{GEN}{gal_get_pol}{GEN gal} returns \kbd{gal.pol}
+
+\fun{GEN}{gal_get_p}{GEN gal} returns \kbd{gal.p}
+
+\fun{GEN}{gal_get_e}{GEN gal} returns the integer $e$ such that
+\kbd{gal.mod==gal.p\pow e}.
+
+\fun{GEN}{gal_get_mod}{GEN gal} returns \kbd{gal.mod}.
+
+\fun{GEN}{gal_get_roots}{GEN gal} returns \kbd{gal.roots}.
+
+\fun{GEN}{gal_get_invvdm}{GEN gal} \kbd{gal[4]}.
+
+\fun{GEN}{gal_get_den}{GEN gal} return \kbd{gal[5]}.
+
+\fun{GEN}{gal_get_group}{GEN gal} returns \kbd{gal.group}.
+
+\fun{GEN}{gal_get_gen}{GEN gal} returns \kbd{gal.gen}.
+
+\fun{GEN}{gal_get_orders}{GEN gal} returns \kbd{gal.orders}.
+
+\subsec{Miscellaneous functions}
+
+\fun{GEN}{nfgaloismatrix}{GEN nf, GEN s} returns the \kbd{ZM} associated to
+the automorphism $s$, seen as a linear operator expressend on the number
+field integer basis. This allows to use
+\bprog
+  M = nfgaloismatrix(nf, s);
+  sx = ZM_ZC_mul(M, x);   /* or RgM_RgC_mul(M, x) if x is not integral */
+ at eprog\noindent
+instead of
+\bprog
+  sx = nfgaloisapply(nf, s, x);
+ at eprog\noindent
+for an algebraic integer $x$.
+
+\section{Quadratic number fields and quadratic forms}
+
+\subsec{Checks}
+
+\fun{void}{check_quaddisc}{GEN x, long *s, long *mod4, const char *f}
+checks whether the \kbd{GEN} $x$ is a quadratic discriminant (\typ{INT},
+not a square, congruent to $0,1$ modulo $4$), and raise an exception
+otherwise. Set \kbd{*s} to the sign of $x$ and \kbd{*mod4} to $x$ modulo
+$4$ (0 or 1).
+
+\fun{void}{check_quaddisc_real}{GEN x, long *mod4, const char *f} as
+\tet{check_quaddisc}; check that \kbd{signe(x)} is positive.
+
+\fun{void}{check_quaddisc_imag}{GEN x, long *mod4, const char *f} as
+\tet{check_quaddisc}; check that \kbd{signe(x)} is negative.
+
+\subsec{\typ{QFI}, \typ{QFR}}
+
+\fun{GEN}{qfi}{GEN x, GEN y, GEN z} creates the \typ{QFI} $(x,y,z)$.
+
+\fun{GEN}{qfr}{GEN x, GEN y, GEN z, GEN d} creates the \typ{QFR} $(x,y,z)$
+with distance component $d$.
+
+\fun{GEN}{qfr_1}{GEN q} given a \typ{QFR} $q$, return the unit form $q^0$.
+
+\fun{GEN}{qfi_1}{GEN q} given a \typ{QFI} $q$, return the unit form $q^0$.
+
+\subsubsec{Composition}
+
+\fun{GEN}{qficomp}{GEN x, GEN y} compose the two \typ{QFI} $x$ and $y$,
+then reduce the result. This is the same as \kbd{gmul(x,y)}.
+
+\fun{GEN}{qfrcomp}{GEN x, GEN y} compose the two \typ{QFR} $x$ and $y$,
+then reduce the result. This is the same as \kbd{gmul(x,y)}.
+
+\fun{GEN}{qfisqr}{GEN x} as \kbd{qficomp(x,y)}.
+
+\fun{GEN}{qfrsqr}{GEN x} as \kbd{qfrcomp(x,y)}.
+
+\noindent Same as above, \emph{without} reducing the result:
+
+\fun{GEN}{qficompraw}{GEN x, GEN y}
+
+\fun{GEN}{qfrcompraw}{GEN x, GEN y}
+
+\fun{GEN}{qfisqrraw}{GEN x}
+
+\fun{GEN}{qfrsqrraw}{GEN x}
+
+\fun{GEN}{qfbcompraw}{GEN x, GEN y} compose two \typ{QFI}s or two \typ{QFR}s,
+without reduce the result.
+
+\subsubsec{Powering}
+
+\fun{GEN}{powgi}{GEN x, GEN n} computes $x^n$ (will work for many more types
+than \typ{QFI} and \typ{QFR}, of course). Reduce the result.
+
+\fun{GEN}{qfrpow}{GEN x, GEN n} computes $x^n$ for a \typ{QFR} $x$, reducing
+along the way. If the distance component is initially $0$, leave it alone;
+otherwise update it.
+
+\fun{GEN}{qfbpowraw}{GEN x, long n} compute $x^n$ (pure composition, no
+reduction), for a \typ{QFI} or \typ{QFR} $x$.
+
+\fun{GEN}{qfipowraw}{GEN x, long n} as \tet{qfbpowraw}, for a \typ{QFI} $x$.
+
+\fun{GEN}{qfrpowraw}{GEN x, long n} as \tet{qfbpowraw}, for a \typ{QFR} $x$.
+
+\subsubsec{Solve, Cornacchia}
+
+The following functions underly \tet{qfbsolve}; $p$ denotes a prime number.
+
+\fun{GEN}{qfisolvep}{GEN Q, GEN p} solves $Q(x,y) = p$ over the integers, for
+a \typ{QFI} $Q$. Return \kbd{gen\_0} if there are no solutions.
+
+\fun{GEN}{qfrsolvep}{GEN Q, GEN p} solves $Q(x,y) = p$ over the integers, for
+a \typ{QFR} $Q$. Return \kbd{gen\_0} if there are no solutions.
+
+\fun{long}{cornacchia}{GEN d, GEN p, GEN *px, GEN *py} solves
+$x^2+ dy^2 = p$ over the integers, where $d > 0$. Return $1$ if there is a
+solution (and store it in \kbd{*x} and \kbd{*y}), $0$ otherwise.
+
+\fun{long}{cornacchia2}{GEN d, GEN p, GEN *px, GEN *py} as \kbd{cornacchia},
+for the equation $x^2 + dy^2 = 4p$.
+
+\subsubsec{Prime forms}
+
+\fun{GEN}{primeform_u}{GEN x, ulong p} \typ{QFI} whose first coefficient
+is the prime $p$.
+
+\fun{GEN}{primeform}{GEN x, GEN p, long prec}
+
+\subsec{Efficient real quadratic forms} Unfortunately, \typ{QFR}s
+are very inefficient, and are only provided for backward compatibility.
+
+\item they do not contain needed quantities, which are thus constantly
+recomputed (the discriminant $D$, $\sqrt{D}$ and its integer part),
+
+\item the distance component is stored in logarithmic form, which involves
+computing one extra logarithm per operation. It is much more efficient
+to store its exponential, computed from ordinary multiplications and
+divisions (taking exponent overflow into account), and compute its logarithm
+at the very end.
+
+Internally, we have two representations for real quadratic forms:
+
+\item \tet{qfr3}, a container $[a,b,c]$ with at least 3 entries: the three
+coefficients; the idea is to ignore the distance component.
+
+\item \tet{qfr5}, a container with at least 5 entries $[a,b,c,e,d]$: the
+three coefficients a \typ{REAL} $d$ and a \typ{INT} $e$ coding the distance
+component $2^{Ne} d$, in exponential form, for some large fixed $N$.
+
+It is a feature that \kbd{qfr3} and \kbd{qfr5} have no specified length or
+type. It implies that a \kbd{qfr5} or \typ{QFR} will do whenever a \kbd{qfr3}
+is expected. Routines using these objects all require a global context,
+provided by a \kbd{struct qfr\_data *}:
+\bprog
+  struct qfr_data {
+    GEN D;        /* discriminant, t_INT   */
+    GEN sqrtD;    /* sqrt(D), t_REAL       */
+    GEN isqrtD;   /* floor(sqrt(D)), t_INT */
+  };
+ at eprog
+\fun{void}{qfr_data_init}{GEN D, long prec, struct qfr_data *S}
+given a discriminant $D > 0$, initialize $S$ for computations at precision
+\kbd{prec} ($\sqrt{D}$ is computed to that initial accuracy).
+
+\noindent All functions below are shallow, and not stack clean.
+
+\fun{GEN}{qfr3_comp}{GEN x, GEN y, struct qfr_data *S} compose two
+\kbd{qfr3}, reducing the result.
+
+\fun{GEN}{qfr3_pow}{GEN x, GEN n, struct qfr_data *S} compute $x^n$, reducing
+along the way.
+
+\fun{GEN}{qfr3_red}{GEN x, struct qfr_data *S} reduce $x$.
+
+\fun{GEN}{qfr3_rho}{GEN x, struct qfr_data *S} perform one reduction step;
+\kbd{qfr3\_red} just performs reduction steps until we hit a reduced form.
+
+\fun{GEN}{qfr3_to_qfr}{GEN x, GEN d} recover an ordinary \typ{QFR} from the
+\kbd{qfr3} $x$, adding distance component $d$.
+
+Before we explain \kbd{qfr5}, recall that it corresponds to an ideal, that
+reduction corresponds to multiplying by a principal ideal, and that the
+distance component is a clever way to keep track of these principal ideals.
+More precisely, reduction consists in a number of reduction steps,
+going from the form $(a,b,c)$ to $\rho(a,b,c) = (c, -b \mod 2c, *)$;
+the distance component is multiplied by (a floating point approximation to)
+$(b + \sqrt{D}) / (b - \sqrt{D})$.
+
+\fun{GEN}{qfr5_comp}{GEN x, GEN y, struct qfr_data *S} compose two
+\kbd{qfr5}, reducing the result, and updating the distance component.
+
+\fun{GEN}{qfr5_pow}{GEN x, GEN n, struct qfr_data *S} compute $x^n$, reducing
+along the way.
+
+\fun{GEN}{qfr5_red}{GEN x, struct qfr_data *S} reduce $x$.
+
+\fun{GEN}{qfr5_rho}{GEN x, struct qfr_data *S} perform one reduction step.
+
+\fun{GEN}{qfr5_dist}{GEN e, GEN d, long prec} decode the distance component
+from exponential (\kbd{qfr5}-specific) to logarithmic form (as in a
+\typ{QFR}).
+
+\fun{GEN}{qfr_to_qfr5}{GEN x, long prec} convert a \typ{QFR} to a
+\kbd{qfr5} with initial trivial distance component ($= 1$).
+
+\fun{GEN}{qfr5_to_qfr}{GEN x, GEN d}, assume $x$ is a \kbd{qfr5} and
+$d$ was the original distance component of some \typ{QFR} that we converted
+using \tet{qfr_to_qfr5} to perform efficiently a number of operations.
+Convert $x$ to a \typ{QFR} with the correct (logarithmic) distance component.
+
+\section{Linear algebra over $\Z$}
+\subsec{Hermite and Smith Normal Forms}
+
+\fun{GEN}{ZM_hnf}{GEN x} returns the upper triangular Hermite Normal Form of the
+\kbd{ZM} $x$ (removing $0$ columns), using the \tet{ZM_hnfall} algorithm. If you
+want the true HNF, use \kbd{ZM\_hnfall(x, NULL, 0)}.
+
+\fun{GEN}{ZM_hnfmod}{GEN x, GEN d} returns the HNF of the \kbd{ZM} $x$
+(removing $0$ columns), assuming the \typ{INT} $d$ is a multiple of the
+determinant of $x$. This is usually faster than \tet{ZM_hnf} (and uses less
+memory) if the dimension is large, $> 50$ say.
+
+\fun{GEN}{ZM_hnfmodid}{GEN x, GEN d} returns the HNF of the matrix $(x \mid d
+\text{Id})$ (removing $0$ columns), for a \kbd{ZM} $x$ and a \typ{INT} $d$.
+
+\fun{GEN}{ZM_hnfmodall}{GEN x, GEN d, long flag} low-level function
+underlying the \kbd{ZM\_hnfmod} variants. If \kbd{flag} is $0$, calls
+\kbd{ZM\_hnfmod(x,d)}; \kbd{flag} is an or-ed combination of:
+
+\item \tet{hnf_MODID} call \kbd{ZM\_hnfmodid} instead of \kbd{ZM\_hnfmod},
+
+\item \tet{hnf_PART} return as soon as we obtain an upper triangular matrix,
+saving time. The pivots are non-negative and give the diagonal of the true HNF,
+but the entries to the right of the pivots need not be reduced, i.e.~they may be
+large or negative.
+
+\item \tet{hnf_CENTER} returns the centered HNF, where the entries to the
+right of a pivot $p$ are centered residues in $[-p/2, p/2[$, hence smallest
+possible in absolute value, but possibly negative.
+
+\fun{GEN}{ZM_hnfall}{GEN x, GEN *U, long remove} returns the upper triangular
+HNF $H$ of the \kbd{ZM} $x$; if $U$ is not \kbd{NULL}, set if to the matrix
+$U$ such that $x U = H$. If $\kbd{remove} = 0$, $H$ is the true HNF,
+including $0$ columns; if $\kbd{remove} = 1$, delete the $0$ columns from $H$
+but do not update $U$ accordingly (so that the integer kernel may still be
+recovered): we no longer have $x U = H$; if $\kbd{remove} = 2$, remove $0$
+columns from $H$ and update $U$ so that $x U = H$. The matrix $U$ is square
+and invertible unless $\kbd{remove} = 2$.
+
+This routine uses a naive algorithm which is potentially exponential in the
+dimension (due to coefficient explosion) but is fast in practice, although it
+may require lots of memory. The base change matrix $U$ may be very large,
+when the kernel is large.
+
+\fun{GEN}{ZM_hnfperm}{GEN A, GEN *ptU, GEN *ptperm} returns the hnf
+$H = P A U$ of the matrix $P A$, where $P$ is a suitable permutation matrix,
+and $U\in \text{Gl}_n(\Z)$. $P$ is chosen so as to (heuristically) minimize the
+size of $U$; in this respect it is less efficient than \kbd{ZM\_hnflll}
+but usually faster. Set \kbd{*ptU} to $U$ and \kbd{*pterm} to a \typ{VECSMALL}
+representing the row permutation associated to $P = (\delta_{i,\kbd{perm}[i]}$.
+If \kbd{ptU} is set to \kbd{NULL}, $U$ is not computed, saving some time;
+although useless, setting \kbd{ptperm} to \kbd{NULL} is also allowed.
+
+\fun{GEN}{ZM_hnflll}{GEN x, GEN *U, int remove} returns the HNF $H$ of the
+\kbd{ZM} $x$; if $U$ is not \kbd{NULL}, set if to the matrix $U$ such that $x
+U = H$. The meaning of \kbd{remove} is the same as in \tet{ZM_hnfall}.
+
+This routine uses the \idx{LLL} variant of Havas, Majewski and Mathews, which is
+polynomial time, but rather slow in practice because it uses an exact LLL
+over the integers instead of a floating point variant; it uses polynomial
+space but lots of memory is needed for large dimensions, say larger than 300.
+On the other hand, the base change matrix $U$ is essentially optimally small
+with respect to the $L_2$ norm.
+
+\fun{GEN}{ZM_hnfcenter}{GEN M}. Given a \kbd{ZM} in HNF $M$, update it in
+place so that non-diagonal entries belong to a system of \emph{centered}
+residues. Not suitable for gerepile.
+
+Some direct applications: the following routines apply to upper triangular
+integral matrices; in practice, these come from HNF algorithms.
+
+\fun{GEN}{hnf_divscale}{GEN A, GEN B,GEN t} $A$ an upper triangular \kbd{ZM},
+$B$ a \kbd{ZM}, $t$ an integer, such that $C := tA^{-1}B$ is integral.
+Return $C$.
+
+\fun{GEN}{hnf_solve}{GEN A, GEN B} $A$ a \kbd{ZM} in upper HNF (not
+necessarily square), $B$ a \kbd{ZM} or \kbd{ZC}. Return $A^{-1}B$ if it is
+integral, and \kbd{NULL} if it is not.
+
+\fun{GEN}{hnf_invimage}{GEN A, GEN b} $A$ a \kbd{ZM} in upper HNF
+(not necessarily square), $b$ a \kbd{ZC}.  Return $A^{-1}B$ if it is
+integral, and \kbd{NULL} if it is not.
+
+\fun{int}{hnfdivide}{GEN A, GEN B} $A$ and $B$ are two upper triangular
+\kbd{ZM}. Return $1$ if $A^{-1} B$ is integral, and $0$ otherwise.
+
+\misctitle{Smith Normal Form}
+
+\fun{GEN}{ZM_snf}{GEN x} returns the Smith Normal Form (vector of
+elementary divisors) of the \kbd{ZM} $x$.
+
+\fun{GEN}{ZM_snfall}{GEN x, GEN *U, GEN *V} returns
+\kbd{ZM\_smith(x)} and sets $U$ and $V$ to unimodular matrices such that $U\,
+x\, V = D$ (diagonal matrix of elementary divisors). Either (or both) $U$ or
+$V$ may be \kbd{NULL} in which case the corresponding matrix is not computed.
+
+\fun{GEN}{ZM_snfall_i}{GEN x, GEN *U, GEN *V, int returnvec} same as
+\kbd{ZM\_snfall}, except that, depending on the value of \kbd{returnvec}, we
+either return a diagonal matrix (as in \kbd{ZM\_snfall}, \kbd{returnvec} is 0)
+or a vector of elementary divisors (as in \kbd{ZM\_snf}, \kbd{returnvec} is 1) .
+
+\fun{void}{ZM_snfclean}{GEN d, GEN U, GEN V} assuming $d$, $U$, $V$ come
+from \kbd{d = ZM\_snfall(x, \&U, \&V)}, where $U$ or $V$ may be \kbd{NULL},
+cleans up the output in place. This means that elementary divisors equal to 1
+are deleted and $U$, $V$ are updated. The output is not suitable for
+\kbd{gerepileupto}.
+
+\fun{GEN}{ZM_snf_group}{GEN H, GEN *U, GEN *Uinv} this function computes data
+to go back and forth between an abelian group (of finite type) given by
+generators and relations, and its canonical SNF form. Given an abstract
+abelian group with generators $g = (g_1,\dots,g_n)$ and a vector
+$X=(x_i)\in\Z^n$, we write $g X$ for the group element $\sum_i x_i g_i$;
+analogously if $M$ is an $n\times r$ integer matrix $g M$ is a vector
+containing $r$ group elements. The group neutral element is $0$; by abuse of
+notation, we still write $0$ for a vector of group elements all equal to the
+neutral element. The input is a full relation matrix $H$ among the
+generators, i.e. a \kbd{ZM} (not necessarily square) such that $gX = 0$ for
+some $X\in\Z^n$ if and only if $X$ is in the integer image of $H$, so that
+the abelian group is isomorphic to $\Z^n/\text{Im} H$. \emph{The routine
+assumes that $H$ is in HNF;} replace it by its HNF if it is not the case. (Of
+course this defines the same group.)
+
+Let $G$ a minimal system of generators in SNF for our abstract group:
+if the $d_i$ are the elementary divisors ($\dots \mid d_2\mid d_1$), each
+$G_i$ has either infinite order ($d_i = 0$) or order $d_i > 1$. Let $D$
+the matrix with diagonal $(d_i)$, then
+$$G D = 0,\quad G = g U_{\text{inv}},\quad g = G U,$$
+for some integer matrices $U$ and $U_{\text{inv}}$. Note that these are not
+even square in general; even if square, there is no guarantee that these are
+unimodular: they are chosen to have minimal entries given the known relations
+in the group and only satisfy $D \mid (U U_{\text{inv}} - \text{Id})$ and $H
+\mid (U_{\text{inv}}U - \text{Id})$.
+
+The function returns the vector of elementary divisors $(d_i)$; if \kbd{U} is
+not \kbd{NULL}, it is set to $U$; if \kbd{Uinv} is not \kbd{NULL} it is
+set to $U_{\text{inv}}$. The function is not memory clean.
+
+
+The following 3 routines underly the various \tet{matrixqz} variants.
+In all case the $m\times n$ \typ{MAT} $x$ is assumed to have rational
+(\typ{INT} and \typ{FRAC}) coefficients
+
+\fun{GEN}{QM_ImQ_hnf}{GEN x} returns an HNF basis for
+$\text{Im}_\Q x \cap \Z^n$.
+
+\fun{GEN}{QM_ImZ_hnf}{GEN x} returns an HNF basis for
+$\text{Im}_\Z x \cap \Z^n$.
+
+\fun{GEN}{QM_minors_coprime}{GEN x, GEN D}, assumes $m\geq n$, and returns
+a matrix in $M_{m,n}(\Z)$ with the same $\Q$-image as $x$, such that
+the GCD of all $n\times n$ minors is coprime to $D$; if $D$ is \kbd{NULL},
+we want the GCD to be $1$.
+\smallskip
+
+The following routines are simple wrappers around the above ones and are
+normally useless in library mode:
+
+\fun{GEN}{hnf}{GEN x} checks whether $x$ is a \kbd{ZM}, then calls \tet{ZM_hnf}.
+Normally useless in library mode.
+
+\fun{GEN}{hnfmod}{GEN x, GEN d} checks whether $x$ is a \kbd{ZM}, then calls
+\tet{ZM_hnfmod}. Normally useless in library mode.
+
+\fun{GEN}{hnfmodid}{GEN x,GEN d} checks whether $x$ is a \kbd{ZM}, then calls
+\tet{ZM_hnfmodid}. Normally useless in library mode.
+
+\fun{GEN}{hnfall}{GEN x} calls
+\kbd{ZM\_hnfall(x, \&U, 1)} and returns $[H, U]$. Normally useless in library
+mode.
+
+\fun{GEN}{hnflll}{GEN x} calls \kbd{ZM\_hnflll(x, \&U, 1)} and returns $[H,
+U]$. Normally useless in library mode.
+
+\fun{GEN}{hnfperm}{GEN x} calls \kbd{ZM\_hnfperm(x, \&U, \&P)} and returns
+$[H, U, P]$. Normally useless in library mode.
+
+\fun{GEN}{smith}{GEN x} checks whether $x$ is a \kbd{ZM}, then calls
+\kbd{ZM\_smith}. Normally useless in library mode.
+
+\fun{GEN}{smithall}{GEN x} checks whether $x$ is a \kbd{ZM}, then calls
+\kbd{ZM\_smithall(x, \&U, \&V)} and returns $[U,V,D]$. Normally useless in
+library mode.
+
+\noindent Some related functions over $K[X]$, $K$ a field:
+
+\fun{GEN}{gsmith}{GEN A} the input matrix must be square, returns the
+elementary divisors.
+
+\fun{GEN}{gsmithall}{GEN A} the input matrix must be square, returns the
+$[U,V,D]$, $D$ diagonal, such that $UAV = D$.
+
+\fun{GEN}{RgM_hnfall}{GEN A, GEN *pB, long remove} analogous to \tet{ZM_hnfall}.
+
+\fun{GEN}{smithclean}{GEN z} cleanup the output of \kbd{smithall} or
+\kbd{gsmithall} (delete elementary divisors equal to $1$, updating base
+change matrices).
+
+\subsec{The LLL algorithm}\sidx{LLL}
+
+The basic GP functions and their immediate variants are normally not very
+useful in library mode. We briefly list them here for completeness, see the
+documentation of \kbd{qflll} and \kbd{qflllgram} for details:
+
+\item \fun{GEN}{qflll0}{GEN x, long flag}
+
+\fun{GEN}{lll}{GEN x} \fl = 0
+
+\fun{GEN}{lllint}{GEN x} \fl = 1
+
+\fun{GEN}{lllkerim}{GEN x} \fl = 4
+
+\fun{GEN}{lllkerimgen}{GEN x} \fl = 5
+
+\fun{GEN}{lllgen}{GEN x} \fl = 8
+
+\item \fun{GEN}{qflllgram0}{GEN x, long flag}
+
+\fun{GEN}{lllgram}{GEN x} \fl = 0
+
+\fun{GEN}{lllgramint}{GEN x} \fl = 1
+
+\fun{GEN}{lllgramkerim}{GEN x} \fl = 4
+
+\fun{GEN}{lllgramkerimgen}{GEN x} \fl = 5
+
+\fun{GEN}{lllgramgen}{GEN x} \fl = 8
+
+\smallskip
+
+The basic workhorse underlying all integral and floating point LLLs is
+
+\fun{GEN}{ZM_lll}{GEN x, double D, long flag}, where $x$ is a \kbd{ZM};
+$D \in ]1/4,1[$ is the Lov\'{a}sz constant determining the frequency of
+swaps during the algorithm: a larger values means better guarantees for
+the basis (in principle smaller basis vectors) but longer running times
+(suggested value: $D = 0.99$).
+
+\misctitle{Important} This function does not collect garbage and its output
+is not suitable for either \kbd{gerepile} or \kbd{gerepileupto}. We expect
+the caller to do something simple with the output (e.g. matrix
+multiplication), then collect garbage immediately.
+
+\noindent\kbd{flag} is an or-ed combination of the following flags:
+
+\item  \tet{LLL_GRAM}. If set, the input matrix $x$ is the Gram matrix ${}^t
+v v$ of some lattice vectors $v$.
+
+\item  \tet{LLL_INPLACE}. If unset, we return the base change matrix $U$,
+otherwise the transformed matrix $x U$ or ${}^t U x U$ (\kbd{LLL\_GRAM}).
+Implies \tet{LLL_IM} (see below).
+
+\item  \tet{LLL_KEEP_FIRST}. The first vector in the output basis is the same
+one as was originally input. Provided this is a shortest non-zero vector of
+the lattice, the output basis is still LLL-reduced. This is used to reduce
+maximal orders of number fields with respect to the $T_2$ quadratic form, to
+ensure that the first vector in the output basis corresponds to $1$ (which is
+a shortest vector).
+
+The last three flags are mutually exclusive, either 0 or a single one must be
+set:
+
+\item  \tet{LLL_KER} If set, only return a kernel basis $K$ (not LLL-reduced).
+
+\item  \tet{LLL_IM} If set, only return an LLL-reduced lattice basis $T$.
+(This is implied by \tet{LLL_INPLACE}).
+
+\item  \tet{LLL_ALL} If set, returns a 2-component vector $[K, T]$
+corresponding to both kernel and image.
+
+
+\fun{GEN}{lllfp}{GEN x, double D, long flag} is a variant for matrices
+with inexact entries: $x$ is a matrix with real coefficients (types
+\typ{INT}, \typ{FRAC} and \typ{REAL}), $D$ and $\fl$ are as in \tet{ZM_lll}.
+The matrix is rescaled, rounded to nearest integers, then fed to
+\kbd{ZM\_lll}. The flag \kbd{LLL\_INPLACE} is still accepted but less useful
+(it returns an LLL-reduced basis associated to rounded input, instead of an
+exact base change matrix).
+
+\fun{GEN}{ZM_lll_norms}{GEN x, double D, long flag, GEN *ptB} slightly more
+general version of \kbd{ZM\_lll}, setting \kbd{*ptB} to a vector containing
+the squared norms of the Gram-Schmidt vectors $(b_i^*)$ associated to the
+output basis $(b_i)$, $b_i^* = b_i + \sum_{j < i} \mu_{i,j} b_j^*$.
+
+
+\fun{GEN}{lllintpartial_inplace}{GEN x} given a \kbd{ZM} $x$ of maximal rank,
+returns a partially reduced basis $(b_i)$ for the space spanned by the
+columns of $x$: $|b_i \pm b_j| \geq |b_i|$ for any two distinct basis vectors
+$b_i$, $b_j$. This is faster than the LLL algorithm, but produces much larger
+bases.
+
+\fun{GEN}{lllintpartial}{GEN x} as \kbd{lllintpartial\_inplace}, but returns
+the base change matrix $U$ from the canonical basis to the $b_i$, i.e. $x U$
+is the output of \kbd{lllintpartial\_inplace}.
+
+\subsec{Reduction modulo matrices}
+
+\fun{GEN}{ZC_hnfremdiv}{GEN x, GEN y, GEN *Q} assuming $y$ is an
+invertible \kbd{ZM} in HNF and $x$ is a \kbd{ZC}, returns the \kbd{ZC} $R$
+equal to $x$ mod $y$ (whose $i$-th entry belongs to $[-y_{i,i}/2, y_{i,i}/2[$).
+Stack clean \emph{unless} $x$ is already reduced (in which case, returns $x$
+itself, not a copy). If $Q$ is not \kbd{NULL}, set it to the \kbd{ZC} such that
+$x = yQ + R$.
+
+\fun{GEN}{ZM_hnfdivrem}{GEN x, GEN y, GEN *Q} reduce
+each column of the \kbd{ZM} $x$ using \kbd{ZC\_hnfremdiv}. If $Q$ is not
+\kbd{NULL}, set it to the \kbd{ZM} such that $x = yQ + R$.
+
+\fun{GEN}{ZC_hnfrem}{GEN x, GEN y} alias for \kbd{ZC\_hnfremdiv(x,y,NULL)}.
+
+\fun{GEN}{ZM_hnfrem}{GEN x, GEN y} alias for \kbd{ZM\_hnfremdiv(x,y,NULL)}.
+
+\fun{GEN}{ZC_reducemodmatrix}{GEN v, GEN y} Let $y$ be a ZM, not necessarily
+square, which is assumed to be LLL-reduced (otherwise, very poor reduction is
+expected). Size-reduces the ZC $v$ modulo the $\Z$-module $Y$ spanned by $y$
+: if the columns of $y$ are denoted by $(y_1,\dots, y_{n-1})$, we return $y_n
+\equiv v$ modulo $Y$, such that the Gram-Schmidt coefficients $\mu_{n,j}$ are
+less than $1/2$ in absolute value for all $j < n$. In short, $y_n$ is almost
+orthogonal to $Y$.
+
+\fun{GEN}{ZM_reducemodmatrix}{GEN v, GEN y} Let $y$ be as in
+\tet{ZC_reducemodmatrix}, and $v$ be a ZM. This returns a matrix $v$ which is
+congruent to $v$ modulo the $\Z$-module spanned by $y$, whose columns are
+size-reduced. This is faster than repeatedly calling \tet{ZC_reducemodmatrix}
+on the columns since most of the Gram-Schmidt coefficients can be reused.
+
+\fun{GEN}{ZC_reducemodlll}{GEN v, GEN y} Let $y$ be an arbitrary ZM,
+LLL-reduce it then call \tet{ZC_reducemodmatrix}.
+
+\fun{GEN}{ZM_reducemodlll}{GEN v, GEN y} Let $y$ be an arbitrary ZM,
+LLL-reduce it then call \tet{ZM_reducemodmatrix}.
+
+Besides the above functions, which were specific to integral input, we also
+have:
+
+\fun{GEN}{reducemodinvertible}{GEN x, GEN y} $y$ is an invertible matrix
+and $x$ a \typ{COL} or \typ{MAT} of compatible dimension.
+Returns $x - y\lfloor y^{-1}x \rceil$, which has small entries and differs
+from $x$ by an integral linear combination of the columns of $y$. Suitable
+for \kbd{gerepileupto}, but does not collect garbage.
+
+\fun{GEN}{closemodinvertible}{GEN x, GEN y} returns $x -
+\kbd{reducemodinvertible}(x,y)$, i.e. an integral linear combination of
+the columns of $y$, which is close to $x$.
+
+\fun{GEN}{reducemodlll}{GEN x,GEN y} LLL-reduce the non-singular \kbd{ZM} $y$
+and call \kbd{reducemodinvertible} to find a small representative of $x$ mod $y
+\Z^n$. Suitable for \kbd{gerepileupto}, but does not collect garbage.
+
+\subsec{Miscellaneous}
+
+\fun{GEN}{RM_round_maxrank}{GEN G} given a matrix $G$ with real floating
+point entries and independent columns, let $G_e$ be the
+rescaled matrix $2^e G$ rounded to nearest integers, for $e \geq 0$.
+Finds a small $e$ such that the rank of $G_e$ is equal to the rank of $G$
+(its number of columns) and return $G_e$. This is useful as a preconditioning
+step to speed up LLL reductions, see \tet{nf_get_Gtwist}.
+Suitable for \kbd{gerepileupto}, but does not collect garbage.
+
+\newpage
diff --git a/doc/usersch7.tex b/doc/usersch7.tex
new file mode 100644
index 0000000..8c0da49
--- /dev/null
+++ b/doc/usersch7.tex
@@ -0,0 +1,626 @@
+% Copyright (c) 2000  The PARI Group
+%
+% This file is part of the PARI/GP documentation
+%
+% Permission is granted to copy, distribute and/or modify this document
+% under the terms of the GNU General Public License
+\chapter{Technical Reference Guide for Elliptic curves and arithmetic geometry}
+
+This chapter is quite short, but is added as a placeholder, since
+we expect the library to expand in that direction.
+
+\section{Elliptic curves}
+Elliptic curves are represented in the Weierstrass model
+$$ (E): y^2z + a_1xyz + a_3 yz = x^3 + a_2 x^2z + a_4 xz^2 + a_6z^3, $$
+by the $5$-tuple $[a_1,a_2,a_3,a_4,a_6]$. Points in the projective
+plane are represented as follows: the point at infinity $(0:1:0)$ is coded
+as \kbd{[0]}, a finite point $(x:y:1)$ outside the projective line at infinity
+$z = 0$ is coded as $[x,y]$. Note that other points at infinity than $(0:1:0)$
+cannot be represented; this is harmless, since they do not belong to any of
+the elliptic curves $E$ above.
+
+\emph{Points on the curve} are just projective points as described above,
+they are not tied to a curve in any way: the same point may be used in
+conjunction with different curves, provided it satisfies their equations (if
+it does not, the result is usually undefined). In particular, the point at
+infinity belongs to all elliptic curves.
+
+As with \tet{factor} for polynomial factorization, the $5$-tuple
+$[a_1,a_2,a_3,a_4,a_6]$ implicitly defines a base ring over which the curve
+is defined. Point coordinates must be operation-compatible with this
+base ring (\kbd{gadd}, \kbd{gmul}, \kbd{gdiv} involving them should not give
+errors).
+
+\subsec{Types of elliptic curves}
+
+We call a $5$-tuble as above an \kbd{ell5}; most functions require an
+\kbd{ell} structure, as returned by \tet{ellinit}, which contains additional
+data (usually dynamically computed as needed), depending on the base field.
+
+\fun{GEN}{ellinit}{GEN E, GEN D, long prec}, returns an \tet{ell} structure,
+associated to the elliptic curve $E$ : either an \kbd{ell5}, a pair $[a_4,a_6]$
+or a \typ{STR} in Cremona's notation, e.g. \kbd{"11a1"}. The optional $D$
+(\kbd{NULL} to omit) describes the domain over which the curve is defined.
+
+\subsec{Type checking}
+
+\fun{void}{checkell}{GEN e} raise an error unless $e$ is a \var{ell}.
+
+\fun{void}{checkell5}{GEN e} raise an error unless $e$ is an \var{ell}
+or an \var{ell5}.
+
+\fun{void}{checkellpt}{GEN z} raise an error unless $z$ is a point
+(either finite or at infinity).
+
+\fun{long}{ell_get_type}{GEN e} returns the domain type over which the curve
+is defined, one of
+
+  \tet{t_ELL_Q} the field of rational numbers;
+
+  \tet{t_ELL_Qp} the field of $p$-adic numbers, for some prime $p$;
+
+  \tet{t_ELL_Fp} a prime finite field, base field elements are represented as
+  \kbd{Fp} (\typ{INT} reduced modulo $p$);
+
+  \tet{t_ELL_Fq} a non-prime finite field (a prime finite field can also be
+  represented by this subtype, but this is inefficient), base field elements
+  are represented as \typ{FFELT};
+
+  \tet{t_ELL_Rg} none of the above.
+
+\fun{void}{checkell_Fq}{GEN e} checks whether $e$ is an \kbd{ell}, defined
+over a finite field (either prime or non-prime), raises \tet{pari_err_TYPE}
+otherwise.
+
+\fun{void}{checkell_Q}{GEN e} checks whether $e$ is an \kbd{ell}, defined
+over $\Q$, raises \tet{pari_err_TYPE} otherwise.
+
+\fun{void}{checkell_Qp}{GEN e} checks whether $e$ is an \kbd{ell}, defined
+over some $\Q_p$, raises \tet{pari_err_TYPE} otherwise.
+
+
+\subsec{Extracting info from an \kbd{ell} structure}
+
+These functions expect an \kbd{ell} argument. If the required data is not
+part of the structure, it is computed then inserted, and the new value is
+returned.
+
+\subsubsec{All domains}
+
+\fun{GEN}{ell_get_a1}{GEN e}
+
+\fun{GEN}{ell_get_a2}{GEN e}
+
+\fun{GEN}{ell_get_a3}{GEN e}
+
+\fun{GEN}{ell_get_a4}{GEN e}
+
+\fun{GEN}{ell_get_a6}{GEN e}
+
+\fun{GEN}{ell_get_b2}{GEN e}
+
+\fun{GEN}{ell_get_b4}{GEN e}
+
+\fun{GEN}{ell_get_b6}{GEN e}
+
+\fun{GEN}{ell_get_b8}{GEN e}
+
+\fun{GEN}{ell_get_c4}{GEN e}
+
+\fun{GEN}{ell_get_c6}{GEN e}
+
+\fun{GEN}{ell_get_disc}{GEN e}
+
+\fun{GEN}{ell_get_j}{GEN e}
+
+\subsubsec{Curves over $\Q$}
+
+\fun{GEN}{ellQ_get_N}{GEN e} returns the curve conductor
+
+\fun{void}{ellQ_get_Nfa}{GEN e, GEN *N, GEN *faN} sets $N$ to the conductor
+and \kbd{faN} to its factorization
+
+\fun{long}{ellrootno_global}{GEN e} returns $[c, [c_{p_1}, \dots,c_{p_k}]]$,
+where the \typ{INT} $c\in \{-1,1\}$ is the global root number, and the
+$c_{p_i}$ are the local root numbers at all prime divisors of the conductor,
+ordered as in \kbd{faN} above.
+
+\fun{GEN}{elldatagenerators}{GEN E} returns generators for $E(\Q)$
+extracted from Cremona's table.
+
+\fun{GEN}{ellanal_globalred}{GEN e, GEN *v} takes an \var{ell} over $\Q$
+and returns a global minimal model $E$ (in \kbd{ellinit} form, over $\Q$) for
+$e$ suitable for analytic computations related to the curve $L$ series: it
+contains \kbd{ellglobalred} data, as well as global and local root numbers. If
+\kbd{v} is not \kbd{NULL}, set \kbd{*v} to the needed change of variable:
+\kbd{NULL} if $e$ was already the standard minimal model, such that $E =
+\kbd{ellchangecurve(e,v)}$ otherwise. Compared to the direct use of
+\kbd{ellchangecurve} followed by \kbd{ellrootno}, this function avoids
+converting unneeded dynamic data and avoids potential memory leaks
+(the changed curve would have had to be deleted using \tet{obj_free}). The
+original curve $e$ is updated as well with the same information.
+
+\subsubsec{Curves over $\Q_p$}
+
+\fun{GEN}{ellQp_get_p}{GEN E} returns $p$
+
+\fun{long}{ellQp_get_prec}{GEN E} returns the default $p$-adic accuracy to
+which we must compute approximate results associated to $E$.
+
+\fun{GEN}{ellQp_get_zero}{GEN x} returns $O(p^n)$, where $n$ is the default
+$p$-adic accuracy as above.
+
+The following functions are only defined when $E$ has multiplicative
+reduction (Tate curves):
+
+\fun{GEN}{ellQp_Tate_uniformization}{GEN E, long prec} returns a
+\typ{VEC} containing $u^2, u, q, [a,b]$, at $p$-adic precision \kbd{prec}.
+
+\fun{GEN}{ellQp_u}{GEN E, long prec} returns $u$.
+
+\fun{GEN}{ellQp_u2}{GEN E, long prec} returns $u^2$.
+
+\fun{GEN}{ellQp_q}{GEN E, long prec} returns the Tate period $q$.
+
+\fun{GEN}{ellQp_ab}{GEN E, long prec} returns $[a,b]$.
+
+\fun{GEN}{ellQp_root}{GEN E, long prec} returns $e_1$.
+
+\subsubsec{Curves over a finite field $\F_q$}
+
+\fun{GEN}{ellff_get_p}{GEN E} returns the characteristic
+
+\fun{GEN}{ellff_get_field}{GEN E} returns $p$ if $\F_q$ is a prime field, and
+a \typ{FFELT} belonging to $\F_q$ otherwise.
+
+\fun{GEN}{ellff_get_card}{GEN E} returns $\#E(\F_q)$
+
+\fun{GEN}{ellff_get_gens}{GEN E} returns a minimal set of generators for
+$E(\F_q)$.
+
+\fun{GEN}{ellff_get_group}{GEN E} returns \kbd{ellgroup}$(E)$.
+
+\fun{GEN}{ellff_get_o}{GEN E} returns $[d, \kbd{factor{d}}]$, where $d$ is
+the exponent of $E(\F_q)$.
+
+\fun{GEN}{ellff_get_a4a6}{GEN E} returns a canonical ``short model'' for $E$,
+and the corresponding change of variable $[u,r,s,t]$. For $p\neq 2,3$,
+this is $[A_4,A_6,[u,r,s,t]]$, corresponding to $y^2 = x^3 + A_4x + A_6$,
+where $A_4 = -27c_4$, $A_6 = -54c_6$, $[u,r,s,t] = [6, 3b_2,3a_1,108a_3]$.
+
+\item If $p = 3$ and the curve is ordinary ($b_2\neq 0$), this is
+$[[b_2], A_6, [1,v,-a_1,-a_3]]$, corresponding to
+$$y^2 = x^3 + b_2 x + A_6,$$
+where $v = b_4/b_2$, $A_6 = b_6 - v(b_4+v^2)$.
+
+\item If $p = 3$ and the curve is supersingular ($b_2 = 0$), this is
+$[-b_4, b_6, [1,0,-a_1,-a_3]]$, corresponding to
+$$y^2 = x^3 + 2b_4 x + b_6.$$
+
+\item If $p = 2$ and the curve is ordinary ($a_1 \neq 0$), return
+$[A_2,A_6,[a_1^{-1}, da_1^{-2}, 0, (a_4+d^2)a_1^{-1}]]$, corresponding to
+$$ y^2 + xy = x^3 + A_2 x^2 + A_6,$$
+where
+$d = a_3/a_1$, $a_1^2 A_2 = (a_2 + d)$ and
+$$ a_1^6 A_6 = d^3 + a_2 d^2 + a_4 d + a_6 + (a_4^2 + d^4)a_1^{-2}.$$
+
+\item If $p = 2$ and the curve is supersingular ($a_1 = 0$, $a_3\neq 0$), return
+$[[a_3, A_4, 1/a_3], A_6, [1,a_2,0,0]]$, corresponding to
+$$ y^2 + a_3 y = x^3 + A_4 x + A_6,$$
+where $A_4 = a_2^2 + a_4$, $ A_6 = a_2a_4 + a_6$. The value $1/a_3$ is
+included in the vector since it is frequently needed in computations.
+
+\subsubsec{Curves over $\C$} (This includes curves over $\Q$!)
+
+\fun{long}{ellR_get_prec}{GEN E} returns the default accuracy to
+which we must compute approximate results associated to $E$.
+
+\fun{GEN}{ellR_ab}{GEN E, long prec} returns $[a,b]$
+
+\fun{GEN}{ellR_omega}{GEN x, long prec} returns periods
+$[\omega_1,\omega_2]$.
+
+\fun{GEN}{ellR_eta}{GEN E, long prec} returns quasi-periods
+$[\eta_1,\eta_2]$.
+
+\fun{GEN}{ellR_roots}{GEN E, long prec} returns $[e_1,e_2,e_3]$. If $E$ is
+defined over $\R$, then $e_1$ is real. If furthermore $\disc E > 0$, then
+$e_1 > e_2 > e_3$.
+
+\fun{long}{ellR_get_sign}{GEN E} if $E$ is defined over $\R$ returns the
+signe of its discriminant, otherwise return $0$.
+
+\subsec{Points}
+
+\fun{int}{ell_is_inf}{GEN z} tests whether the point $z$ is the point at
+infinity.
+
+\fun{GEN}{ellinf}{} returns the point at infinity \kbd{[0]}.
+
+\subsec{Change of variables}
+\fun{GEN}{ellchangeinvert}{GEN w} given a change of variables $w =
+[u,r,s,t]$, returns the inverse change of variables $w'$, such that if $E' =
+\kbd{ellchangecurve(E, w)}$, then $E = \kbd{ellchangecurve}(E, w')$.
+
+\subsec{Functions to handle elliptic curves over finite fields}
+
+\subsubsec{Tolerant routines}
+
+\fun{GEN}{ellap}{GEN E, GEN p} given a prime number $p$ and an elliptic curve
+defined over $\Q$ or $\Q_p$ (assumed integral and minimal at $p$), computes
+the  trace of  Frobenius  $a_p = p+1 - \#E(\F_p)$. If $E$ is defined over
+a non-prime finite field $\F_q$, ignore $p$ and return $q+1 - \#E(\F_q)$.
+When $p$ is implied ($E$ defined over $\Q_p$ or a finite field), $p$ can be
+omitted (set to \kbd{NULL}).
+
+\fun{GEN}{ellsea}{GEN E, GEN p, long s} available if the \kbd{seadata}
+package is installed. This function returns $\#E(\F_p)$, using the
+Schoof-Elkies-Atkin algorithm; it is called by \kbd{ellap}: same conditions
+as above for $E$, except that \tet{t_ELL_Fq} are not allowed. The extra flag
+\kbd{s}, if set to a non-zero value, causes the computation to return
+\kbd{gen\_0} (an impossible cardinality) if one of the small primes $\ell>s$
+divides the curve order. For cryptographic applications, where one is usually
+interested in curves of prime order, setting $s=1$ efficiently weeds out most
+uninteresting curves; if curves of order a power of $2$ times a prime are
+acceptable, set $s=2$. There is no guarantee that the resulting cardinality
+is prime, only that it has no small prime divisor larger than $s$.
+
+\subsubsec{Curves defined a non-prime finite field}
+In this subsection, we assume that \tet{ell_get_type}$(E)$ is \tet{t_ELL_Fq}.
+(As noted above, a curve defined over $\Z/p\Z$ can be represented as a
+\tet{t_ELL_Fq}.)
+
+\fun{GEN}{FF_ellmul}{GEN E, GEN P, GEN n} returns $[n]P$ where $n$ is an
+integer and $P$ is a point on the curve $E$.
+
+\fun{GEN}{FF_ellrandom}{GEN E} returns a random point in $E(\F_q)$.
+This function never returns the point at infinity, unless this is the
+only point on the curve.
+
+\fun{GEN}{FF_ellorder}{GEN E, GEN P, GEN o} returns the order of the point
+$P$, where $o$ is a multiple of the order of $P$, or its factorization.
+
+\fun{GEN}{FF_ellcard}{GEN E} returns $\#E(\F_q)$.
+
+\fun{GEN}{FF_ellgens}{GEN E} returns the generators of the group $E(\F_q)$.
+
+\fun{GEN}{FF_elllog}{GEN E, GEN P, GEN G, GEN o} Let \kbd{G} be a point of
+order \kbd{o}, return $e$ such that $[e]P=G$. If $e$ does not exists, the
+result is undefined.
+
+\fun{GEN}{FF_ellgroup}{GEN E} returns the Abelian group $E(\F_q)$ in the form
+$[h,\kbd{cyc},\kbd{gen}]$.
+
+\fun{GEN}{FF_ellweilpairing}{GEN E, GEN P, GEN Q, GEN m} returns the
+Weil pairing of the points of $m$-torsion $P$ and $Q$.
+
+\fun{GEN}{FF_elltatepairing}{GEN E, GEN P, GEN Q, GEN m} returns the Tate
+pairing of $P$ and $Q$, where $[m]P = 0$.
+
+\section{Arithmetic on elliptic curve over a finite field in simple form}
+
+The functions in this section no longer operate on elliptic curve structures,
+as seen up to now. They are used to implement those higher-level functions
+without using cached information and thus require suitable explicitly
+enumerated data.
+
+\subsec{Helper functions}
+
+\fun{GEN}{elltrace_extension}{GEN t, long n, GEN q} Let $E$ some elliptic curve
+over $\F_q$ such that the trace of the Frobenius is $t$, returns the trace of
+the Frobenius over $\F_q^n$.
+
+\subsec{Elliptic curves over $\F_p$, $p>3$}
+
+Let $p$ a prime number and $E$ the elliptic curve given by the equation
+$E:y^2=x^3+a_4\*x+a_6$, with $a_4$ and $a_6$ in $\F_p$. A \kbd{FpE} is a
+point of $E(\F_p)$.  Since an affine point and $a_4$ determine an unique
+$a6$, most functions do not take $a_6$ as an argument. A \kbd{FpE} is either
+the point at infinity (\kbd{ellinf()}) or a $FpV$ whith two components. The
+parameters $a_4$ and $a_6$ are given as \typ{INT}s when required.
+
+\fun{GEN}{Fp_ellj}{GEN a4, GEN a6, GEN p}
+returns the $j$-invariant of the curve $E$.
+
+\fun{GEN}{Fp_ellcard}{GEN a4, GEN a6, GEN p} returns the cardinal of the group
+$E(\F_p)$.
+
+\fun{GEN}{Fp_ellcard_SEA}{GEN a4, GEN a6, GEN p, long s} same as
+\tet{ellsea} when only $[a_4,a_6]$ are given.
+
+\fun{GEN}{Fq_ellcard_SEA}{GEN a4, GEN a6, GEN q, GEN T, GEN p, long s} same
+as \tet{ellsea} when only $[a_4,a_6]$ are given, over $\F_p[t]/(T)$.
+Assume $p\neq 2,3$.
+
+\fun{GEN}{Fp_ffellcard}{GEN a4, GEN a6, GEN q, long n, GEN p} returns the
+cardinal of the group $E(\F_q)$ where $q=p^n$.
+
+\fun{GEN}{Fp_ellgroup}{GEN a4, GEN a6, GEN N, GEN p, GEN *pt_m} returns the
+group structure $D$ of the group $E(\F_p)$, which is assumed to be of order $N$
+and set $*pt_m=m$.
+
+\fun{GEN}{Fp_ellgens}{GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN p} returns
+generators of the group $E(\F_p)$ with the base change \kbd{ch} (see
+\kbd{FpE\_changepoint}), where $D$ and $m$ are as returned by
+\kbd{Fp\_ellgroup}.
+
+\fun{GEN}{Fp_elldivpol}{GEN a4, GEN a6, long n, GEN p} returns the $n$-division
+polynomial of the elliptic curve $E$.
+
+\subsec{\kbd{FpE}}
+
+\fun{GEN}{FpE_add}{GEN P, GEN Q, GEN a4, GEN p} returns the sum $P+Q$
+in the group $E(\F_p)$, where $E$ is defined by $E:y^2=x^3+a_4\*x+a_6$,
+for any value of $a_6$ compatible with the points given.
+
+\fun{GEN}{FpE_sub}{GEN P, GEN Q, GEN a4, GEN p} returns $P-Q$.
+
+\fun{GEN}{FpE_dbl}{GEN P, GEN a4, GEN p} returns $2.P$.
+
+\fun{GEN}{FpE_neg}{GEN P, GEN p} returns $-P$.
+
+\fun{GEN}{FpE_mul}{GEN P, GEN n, GEN a4, GEN p} return $n.P$.
+
+\fun{GEN}{FpE_changepoint}{GEN P, GEN m, GEN a4, GEN p} returns the image
+$Q$ of the point $P$ on the curve $E:y^2=x^3+a_4\*x+a_6$ by the coordinate
+change $m$ (which is a \kbd{FpV}).
+
+\fun{GEN}{FpE_changepointinv}{GEN P, GEN m, GEN a4, GEN p} returns the image
+$Q$ on the curve $E:y^2=x^3+a_4\*x+a_6$ of the point $P$ by the inverse of the
+coordinate change $m$ (which is a \kbd{FpV}).
+
+\fun{GEN}{random_FpE}{GEN a4, GEN a6, GEN p} returns a random point on
+$E(\F_p)$, where $E$ is defined by $E:y^2=x^3+a_4\*x+a_6$.
+
+\fun{GEN}{FpE_order}{GEN P, GEN o, GEN a4, GEN p} returns the order of $P$ in
+the group $E(\F_p)$, where $o$ is a multiple of the order of $P$, or its
+factorization.
+
+\fun{GEN}{FpE_log}{GEN P, GEN G, GEN o, GEN a4, GEN p} Let \kbd{G} be a
+point of order \kbd{o}, return $e$ such that $e.P=G$. If $e$ does not exists,
+the result is currently undefined.
+
+\fun{GEN}{FpE_tatepairing}{GEN P, GEN Q, GEN m, GEN a4, GEN p} returns the
+Tate pairing of the point of $m$-torsion $P$ and the point $Q$.
+
+\fun{GEN}{FpE_weilpairing}{GEN P, GEN Q, GEN m, GEN a4, GEN p} returns the
+Weil pairing of the points of $m$-torsion $P$ and $Q$.
+
+\fun{GEN}{FpE_to_mod}{GEN P, GEN p} returns $P$ as a vector of \typ{INTMOD}s.
+
+\fun{GEN}{RgE_to_FpE}{GEN P, GEN p} returns the \kbd{FpE} obtained by applying
+\kbd{Rg\_to\_Fp} coefficientwise.
+
+\subsec{\kbd{Fle}}
+Let $p$ be a prime \kbd{ulong}, and $E$ the elliptic curve given by the
+equation $E:y^2=x^3+a_4\*x+a_6$, where $a_4$ and $a_6$ are \kbd{ulong}.
+A \kbd{Fle} is either the point at infinity (\kbd{ellinf()}), or a \kbd{Flv}
+with two components.
+
+\fun{long}{Fl_elltrace}{ulong a4, ulong a6, ulong p} returns the trace $t$ of the Frobenius of
+$E(\F_p)$. The cardinal of $E(\F_p)$ is thus $p+1-t$, which might not fit in a \kbd{ulong}.
+
+\fun{GEN}{Fle_add}{GEN P, GEN Q, ulong a4, ulong p}
+
+\fun{GEN}{Fle_dbl}{GEN P, ulong a4, ulong p}
+
+\fun{GEN}{Fle_sub}{GEN P, GEN Q, ulong a4, ulong p}
+
+\fun{GEN}{Fle_mul}{GEN P, GEN n, ulong a4, ulong p}
+
+\fun{GEN}{Fle_mulu}{GEN P, ulong n, ulong a4, ulong p}
+
+\fun{GEN}{Fle_order}{GEN P, GEN o, ulong a4, ulong p}
+
+\fun{GEN}{random_Fle}{ulong a4, ulong a6, ulong p}
+
+\subsec{Elliptic curves over $\F_{2^n}$}
+Let $T$ be an irreducible \kbd{F2x} and $E$ the
+elliptic curve given by either the equation
+$E:y^2+x*y=x^3+a_2\*x^2+a_6$, where $a_2, a_6$ are \kbd{F2x} in
+$\F_2[X]/(T)$ (ordinary case) or $E:y^2+a_3*y=x^3+a_4\*x+a_6$, where
+$a_3, a_4, a_6$ are \kbd{F2x} in $\F_2[X]/(T)$ (supersingular case).
+
+A \kbd{F2xqE} is a point of $E(\F_2[X]/(T))$.  In the supersingular case, the
+parameter \kbd{a2} is actually the \typ{VEC} $[a_3,a_4,a_3^{-1}]$.
+
+\fun{GEN}{F2xq_ellcard}{GEN a2, GEN a6, GEN T}
+Return the order of the group $E(\F_2[X]/(T))$.
+
+\fun{GEN}{F2xq_ellgroup}{GEN a2, GEN a6, GEN N, GEN T, GEN *pt_m}
+Return the group structure $D$ of the group $E(\F_2[X]/(T))$,
+which is assumed to be of order $N$ and set $*pt_m=m$.
+
+\fun{GEN}{F2xq_ellgens}{GEN a2, GEN a6, GEN ch, GEN D, GEN m, GEN T}
+Returns generators of the group $E(\F_2[X]/(T))$ with the base change \kbd{ch}
+(see \kbd{F2xqE\_changepoint}), where $D$ and $m$ are as returned by
+\kbd{F2xq\_ellgroup}.
+
+\subsec{\kbd{F2xqE}}
+
+\fun{GEN}{F2xqE_changepoint}{GEN P, GEN m, GEN a2, GEN T} returns the image
+$Q$ of the point $P$ on the curve $E:y^2+x*y=x^3+a_2\*x^2+a_6$ by the coordinate
+change $m$ (which is a \kbd{F2xqV}).
+
+\fun{GEN}{F2xqE_changepointinv}{GEN P, GEN m, GEN a2, GEN T} returns the image
+$Q$ on the curve $E:y^2=x^3+a_4\*x+a_6$ of the point $P$ by the inverse of the
+coordinate change $m$ (which is a \kbd{F2xqV}).
+
+\fun{GEN}{F2xqE_add}{GEN P, GEN Q, GEN a2, GEN T}
+
+\fun{GEN}{F2xqE_sub}{GEN P, GEN Q, GEN a2, GEN T}
+
+\fun{GEN}{F2xqE_dbl}{GEN P, GEN a2, GEN T}
+
+\fun{GEN}{F2xqE_neg}{GEN P, GEN a2, GEN T}
+
+\fun{GEN}{F2xqE_mul}{GEN P, GEN n, GEN a2, GEN T}
+
+\fun{GEN}{random_F2xqE}{GEN a2, GEN a6, GEN T}
+
+\fun{GEN}{F2xqE_order}{GEN P, GEN o, GEN a2, GEN T} returns the order of $P$ in
+the group $E(\F_2[X]/(T))$, where $o$ is a multiple of the order of $P$, or its
+factorization.
+
+\fun{GEN}{F2xqE_log}{GEN P, GEN G, GEN o, GEN a2, GEN T} Let \kbd{G} be a
+point of order \kbd{o}, return $e$ such that $e.P=G$. If $e$ does not exists,
+the result is currently undefined.
+
+\fun{GEN}{F2xqE_tatepairing}{GEN P, GEN Q, GEN m, GEN a2, GEN T} returns the
+Tate pairing of the point of $m$-torsion $P$ and the point $Q$.
+
+\fun{GEN}{F2xqE_weilpairing}{GEN Q, GEN Q, GEN m, GEN a2, GEN T} returns the
+Weil pairing of the points of $m$-torsion $P$ and $Q$.
+
+\fun{GEN}{RgE_to_F2xqE}{GEN P, GEN T} returns the \kbd{F2xqE} obtained by
+applying \kbd{Rg\_to\_F2xq} coefficientwise.
+
+\subsec{Elliptic curves over $\F_q$, small characteristic $p>2$ }
+Let $p$ be a prime \kbd{ulong}, $T$ an irreducible \kbd{Flx} mod $p$, and $E$
+the elliptic curve given by the equation $E:y^2=x^3+a_4\*x+a_6$, where $a_4$
+and $a_6$ are \kbd{Flx} in $\F_p[X]/(T)$.  A \kbd{FlxqE} is a point of
+$E(\F_p[X]/(T))$.
+
+In the special case $p = 3$, ordinary elliptic curves ($j(E)\neq 0$) cannot
+be represented as above, but admit a model $E:y^2 = x^3+a_2\*x^2+a_6$ with
+$a_2$ and $a_6$ being \kbd{Flx} in $\F_3[X]/(T)$. In that case, the parameter
+\kbd{a2} is actually stored as a \typ{VEC}, $[a_2]$, to avoid ambiguities.
+
+\fun{GEN}{Flxq_ellj}{GEN a4, GEN a6, GEN T, ulong p}
+returns the $j$-invariant of the curve $E$.
+
+\fun{GEN}{Flxq_ellcard}{GEN a4, GEN a6, GEN T, ulong p}
+returns the order of $E(\F_p[X]/(T))$.
+
+\fun{GEN}{Flxq_ellgroup}{GEN a4, GEN a6, GEN N, GEN T, ulong p, GEN *pt_m}
+returns the group structure $D$ of the group $E(\F_p[X]/(T))$,
+which is assumed to be of order $N$ and set $*pt_m=m$.
+
+\fun{GEN}{Flxq_ellgens}{GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, ulong p}
+returns generators of the group $E(\F_p[X]/(T))$ with the base change \kbd{ch}
+(see \kbd{FlxqE\_changepoint}), where $D$ and $m$ are as returned by
+\kbd{Flxq\_ellgroup}.
+
+\subsec{\kbd{FlxqE}}
+
+\fun{GEN}{FlxqE_changepoint}{GEN P, GEN m, GEN a4, GEN T, ulong p} returns
+the image $Q$ of the point $P$ on the curve $E:y^2=x^3+a_4\*x+a_6$ by the
+coordinate change $m$ (which is a \kbd{FlxqV}).
+
+\fun{GEN}{FlxqE_changepointinv}{GEN P, GEN m, GEN a4, GEN T, ulong p} returns
+the image $Q$ on the curve $E:y^2=x^3+a_4\*x+a_6$ of the point $P$ by the
+inverse of the coordinate change $m$ (which is a \kbd{FlxqV}).
+
+\fun{GEN}{FlxqE_add}{GEN P, GEN Q, GEN a4, GEN T, ulong p}
+
+\fun{GEN}{FlxqE_sub}{GEN P, GEN Q, GEN a4, GEN T, ulong p}
+
+\fun{GEN}{FlxqE_dbl}{GEN P, GEN a4, GEN T, ulong p}
+
+\fun{GEN}{FlxqE_neg}{GEN P, GEN T, ulong p}
+
+\fun{GEN}{FlxqE_mul}{GEN P, GEN n, GEN a4, GEN T, ulong p}
+
+\fun{GEN}{random_FlxqE}{GEN a4, GEN a6, GEN T, ulong p}
+
+\fun{GEN}{FlxqE_order}{GEN P, GEN o, GEN a4, GEN T, ulong p} returns the
+order of $P$ in the group $E(\F_p[X]/(T))$, where $o$ is a multiple of the
+order of $P$, or its factorization.
+
+\fun{GEN}{FlxqE_log}{GEN P, GEN G, GEN o, GEN a4, GEN T, ulong p} Let \kbd{G}
+be a point of order \kbd{o}, return $e$ such that $e.P=G$. If $e$ does not
+exists, the result is currently undefined.
+
+\fun{GEN}{FlxqE_tatepairing}{GEN P, GEN Q, GEN m, GEN a4, GEN T, ulong p}
+returns the Tate pairing of the point of $m$-torsion $P$ and the point $Q$.
+
+\fun{GEN}{FlxqE_weilpairing}{GEN P, GEN Q, GEN m, GEN a4, GEN T, ulong p}
+returns the Weil pairing of the points of $m$-torsion $P$ and $Q$.
+
+\fun{GEN}{RgE_to_FlxqE}{GEN P, GEN T, ulong p} returns the \kbd{FlxqE}
+obtained by applying \kbd{Rg\_to\_Flxq} coefficientwise.
+
+\subsec{Elliptic curves over $\F_q$, large characteristic }
+
+Let $p$ be a prime number, $T$ an irreducible polynomial mod $p$, and $E$ the
+elliptic curve given by the equation $E:y^2=x^3+a_4\*x+a_6$ with $a_4$ and $a_6$
+in $\F_p[X]/(T)$.  A \kbd{FpXQE} is a point of $E(\F_p[X]/(T))$.
+
+\fun{GEN}{FpXQ_ellj}{GEN a4, GEN a6, GEN T, GEN p}
+returns the $j$-invariant of the curve $E$.
+
+\fun{GEN}{FpXQ_ellcard}{GEN a4, GEN a6, GEN T, GEN p}
+returns the order of $E(\F_p[X]/(T))$.
+
+\fun{GEN}{FpXQ_ellgroup}{GEN a4, GEN a6, GEN N, GEN T, GEN p, GEN *pt_m}
+Return the group structure $D$ of the group $E(\F_p[X]/(T))$,
+which is assumed to be of order $N$ and set $*pt_m=m$.
+
+\fun{GEN}{FpXQ_ellgens}{GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, GEN p}
+Returns generators of the group $E(\F_p[X]/(T))$ with the base change \kbd{ch}
+(see \kbd{FpXQE\_changepoint}), where $D$ and $m$ are as returned by
+\kbd{FpXQ\_ellgroup}.
+
+\fun{GEN}{FpXQ_elldivpol}{GEN a4, GEN a6, long n, GEN T, GEN p} returns the
+$n$-division polynomial of the elliptic curve $E$.
+
+\fun{GEN}{Fq_elldivpolmod}{GEN a4,GEN a6, long n, GEN h, GEN T, GEN p}
+returns the $n$-division polynomial of the elliptic curve $E$ modulo the
+polynomial $h$.
+
+\subsec{\kbd{FpXQE}}
+
+\fun{GEN}{FpXQE_changepoint}{GEN P, GEN m, GEN a4, GEN T, GEN p} returns the
+image $Q$ of the point $P$ on the curve $E:y^2=x^3+a_4\*x+a_6$ by the
+coordinate change $m$ (which is a \kbd{FpXQV}).
+
+\fun{GEN}{FpXQE_changepointinv}{GEN P, GEN m, GEN a4, GEN T, GEN p} returns
+the image $Q$ on the curve $E:y^2=x^3+a_4\*x+a_6$ of the point $P$ by the
+inverse of the coordinate change $m$ (which is a \kbd{FpXQV}).
+
+\fun{GEN}{FpXQE_add}{GEN P, GEN Q, GEN a4, GEN T, GEN p}
+
+\fun{GEN}{FpXQE_sub}{GEN P, GEN Q, GEN a4, GEN T, GEN p}
+
+\fun{GEN}{FpXQE_dbl}{GEN P, GEN a4, GEN T, GEN p}
+
+\fun{GEN}{FpXQE_neg}{GEN P, GEN T, GEN p}
+
+\fun{GEN}{FpXQE_mul}{GEN P, GEN n, GEN a4, GEN T, GEN p}
+
+\fun{GEN}{random_FpXQE}{GEN a4, GEN a6, GEN T, GEN p}
+
+\fun{GEN}{FpXQE_log}{GEN P, GEN G, GEN o, GEN a4, GEN T, GEN p} Let \kbd{G} be a
+point of order \kbd{o}, return $e$ such that $e.P=G$. If $e$ does not exists,
+the result is currently undefined.
+
+\fun{GEN}{FpXQE_order}{GEN P, GEN o, GEN a4, GEN T, GEN p} returns the order
+of $P$ in the group $E(\F_p[X]/(T))$, where $o$ is a multiple of the order of
+$P$, or its factorization.
+
+\fun{GEN}{FpXQE_tatepairing}{GEN P,GEN Q, GEN m, GEN a4, GEN T, GEN p}
+returns the Tate pairing of the point of $m$-torsion $P$ and the point $Q$.
+
+\fun{GEN}{FpXQE_weilpairing}{GEN P,GEN Q, GEN m, GEN a4, GEN T, GEN p}
+returns the Weil pairing of the points of $m$-torsion $P$ and $Q$.
+
+\fun{GEN}{RgE_to_FpXQE}{GEN P, GEN T, GEN p} returns the \kbd{FpXQE} obtained
+by applying \kbd{Rg\_to\_FpXQ} coefficientwise.
+
+\section{Other curves}
+
+The following functions deal with hyperelliptic curves in weighted projective
+space $\P_{(1,d,1)}$, with coordinates $(x,y,z)$ and a model of the form
+$ y^2 = T(x,z)$, where $T$ is homogeneous of degree $2d$, and squarefree.
+Thus the curve is nonsingular of genus $d-1$.
+
+\fun{long}{hyperell_locally_soluble}{GEN T, GEN p} assumes that $T\in\Z[X]$ is
+integral. Returns $1$ if the curve is locally soluble over $\Q_p$, $0$
+otherwise.
+
+\fun{long}{nf_hyperell_locally_soluble}{GEN nf, GEN T, GEN pr} let $K$
+be a number field, associated to \kbd{nf}, \kbd{pr} a \var{prid} associated
+to some maximal ideal $\goth{p}$; assumes that $T\in\Z_K[X]$ is integral.
+Returns $1$ if the curve is locally soluble over $K_{\goth{p}}$.
+
+\newpage
diff --git a/examples/EXPLAIN b/examples/EXPLAIN
new file mode 100644
index 0000000..d13e1dc
--- /dev/null
+++ b/examples/EXPLAIN
@@ -0,0 +1,101 @@
+This directory contains:
+
+* Inputrc: an example of .inputrc file for the readline library.
+
+* A generic Makefile for PARI programs, adapted to your system by Configure.
+
+* the C program extgcd.c using the Pari library described in Chapter 4 of
+the users' manual.  To build it, run:
+make TARGET=extgcd
+
+* the C program minigp.c using the Pari library to implement a basic GP
+interpretor (Need readline). To build it, run
+make TARGET=minigp EXTRALIBS=-lreadline
+
+* the C program pari-mt.c using the Pari library interface for parallel
+computation. To build it, run
+make TARGET=pari-mt
+
+* the C program thread.c using the Pari library in a multi-threaded context,
+POSIX threads version, as described in Appendix D of the users' manual.
+This requires that PARI is Configured with --enable-tls. To build it, run
+make TARGET=thread EXTRALIBS=-lpthread
+
+* the C program openmp.c using the Pari library in a multi-threaded context,
+OpenMP version, as described in Appendix D of the users' manual.
+This requires that PARI is Configured with --enable-tls. To build it, run
+make TARGET=openmp EXTRACFLAGS=-fopenmp
+
+* Several examples of complete GP programs. The rest of this file gives a
+brief description of these programs. They should be read into GP
+by the command \r file.
+
+-- bench.gp: This program computes the first 1000 terms of the Fibonacci
+sequence, the product p of successive terms, and the lowest common multiple q.
+It outputs the ratio log(p)/log(q) every 50 terms, which tends to Pi^2/6
+as k tends to infinity. The name bench.gp comes from the fact that this
+program is one (among many) examples where GP/PARI performs orders of
+magnitude faster than systems such as Maple or Mathematica. (Try it!)
+
+-- cl.gp: Written entirely in the GP language without using bnfinit, the
+programs included in this file computes the class number, the structure of
+the class group and a system of fundamental units of a general number field.
+It sometimes fail to give an answer and works only if nfinit finds a power
+basis. Evidently it is less powerful, less reliable and much slower than
+bnfinit, but it is given as an example of a sophisticated use of GP.  The
+first thing to do is to call
+
+   clareg(pol, {limp=19},{lima=50},{extra=5})
+
+where pol is the monic irreducible polynomial defining the number field, limp
+is the prime factor base limit (try values between 19 and 113), lima is
+another search limit (try 50 or 100) and extra is the number of desired extra
+relations (try 2 to 10). Default values are provided, so that you need only
+supply pol.
+
+  The program prints the number of relations that it needs, and tries to find
+them. If you see that it slows down too much before succeeding, abort and try
+other values. If it succeeds, it will print the class number, class group and
+regulator. These are tentative values. Then use
+
+   check({lim = 100})
+
+to check if the value is consistent with the value of the L-series (the value
+returned should be close to 1). Finally, 
+
+   fu() (no parameters)
+
+returns a family of units which generates the unit group (you must extract a
+system of fundamental units yourself).
+
+-- classno.gp: a simple function to compute analytically the class number of
+imaginary quadratic fields (written by Fernando Rodriguez Villegas)
+
+-- contfrac.gp: period(D) computes period of continued fraction for sqrt(D)
+[slower than quadregulator, which does a bit more work, but is written in C!]
+(written by Igor Schein)
+
+-- lucas.gp: The function lucas(p) defined in this file performs the
+Lucas-Lehmer primality test on the Mersenne number 2^p-1. If the result is 1,
+the Mersenne number is prime, otherwise not.
+
+-- rho.gp: a simple implementation of Pollard's rho method. The function
+rho(n) outputs the complete factorization of n in the same format as factor.
+
+-- squfof.gp: This defines a function squfof of a positive integer variable
+n, which may allow you to factor the number n. SQUFOF is a nice factoring
+method invented in the 70's by Dan Shanks for factoring integers, and is
+reasonably fast for numbers having up to 15 or 16 digits. The squfof program
+given here is a crude implementation, which prints out some intermediate
+information as it goes along. The final result is some factor of the number
+to be factored.
+
+-- taylor.gp: the function
+
+   plot_taylor(f,xmin,xmax, ordlim, first,step)
+
+plots the Taylor polynomials T_i (truncated series expansion of order i)
+of the function f in the interval [xmin,xmax]; i goes from first to ordlim in
+increments of steps. Sensible default values are provided for all arguments
+(adapted from an original idea by Ilya Zakharevich). A few examples are
+provided, together with a simple colormap generator
diff --git a/examples/Inputrc b/examples/Inputrc
new file mode 100644
index 0000000..d0a986f
--- /dev/null
+++ b/examples/Inputrc
@@ -0,0 +1,14 @@
+# initialization file used by the readline library
+# modify it then copy it to $HOME/.inputrc
+
+"\C-h": backward-delete-char
+"\e\C-h": backward-kill-word
+"\C-xd": dump-functions
+
+$if Pari-GP
+  set convert-meta on
+  set output-meta off
+  set show-all-if-ambiguous
+# toggle between normal-mode (electric parentheses) and copy-paste mode
+  "\C-q": "\M--2("
+$endif
diff --git a/examples/bench.gp b/examples/bench.gp
new file mode 100644
index 0000000..f2708e0
--- /dev/null
+++ b/examples/bench.gp
@@ -0,0 +1,10 @@
+{
+  u=v=p=q=1;
+  for (k=1, 2000,
+    [u,v] = [v,u+v];
+    p *= v; q = lcm(q,v);
+    if (k%50 == 0,
+      print(k, " ", log(p)/log(q))
+    )
+  )
+}
diff --git a/examples/cl.gp b/examples/cl.gp
new file mode 100644
index 0000000..41ebbbe
--- /dev/null
+++ b/examples/cl.gp
@@ -0,0 +1,118 @@
+rgcd(a,b)=
+{
+  [a,b] = [abs(a), abs(b)];
+  while (b > 0.01, [a,b] = [b,a%b]);
+  a;
+}
+
+global(nf, km, m, clh, R, areg, re, res, mreg);
+
+f(a,b, nf,v,ind)=
+{ my(u, vreg);
+  my(n = idealnorm(nf, a + b*variable(nf.pol)));
+  my(mv = vectorv(#v));
+  forprime (p=2, #ind,
+    my (l = valuation(n,p));
+    if (l,
+      my(cp, j = ind[p]);
+      n /= p^l; cp = v[j][2];
+      while((a+b*cp)%p,
+        j++; cp = v[j][2]
+      );
+      mv[j] = l
+    )
+  );
+  if (n!=1, return);
+  my (r1 = nf.sign[1]);
+
+  /* found a relation */
+  vreg = vectorv(#re,j,
+    u = a+b*re[j];
+    if (j<=r1, abs(u), norm(u))
+  );
+  mreg = concat(mreg, log(vreg));
+  m = concat(m, mv);
+  areg = concat(areg, a+b*t);
+  print1("(" res++ ": " a "," b ")");
+}
+
+clareg(pol, plim=19, lima=50, extra=5)=
+{ my(coreg,lireg,r1,ind,fa,co,a,b,mh,ms,mhs,mregh);
+
+  nf=nfinit(pol); pol=nf.pol;
+  re = nf.roots; r1=nf.sign[1];
+  if (nf.index > 1, /* power basis <==> index = 1 */
+    error("sorry, the case 'index>1' is not implemented")
+  );
+  printf("discriminant = %s, signature = %s\n", nf.disc, nf.sign);
+
+  lireg = sum(i=1,2, nf.sign[i]); /* r1 + r2 */
+  ind=vector(plim); v=[];
+  forprime(p=2,plim,
+    my (w = factormod(pol,p));
+    my (e = w[,2]);
+    my (find = 0);
+    for(l=1,#e,
+      fa = lift(w[l,1]);
+      if (poldegree(fa) == 1,
+        if (!find, find=1; ind[p]=#v+1);
+        v = concat(v, [[p,-polcoeff(fa,0),e[l]]])
+      )
+    )
+  );
+  co = #v+extra;
+  res=0; print("need ", co, " relations");
+  areg=[]~; mreg = m = [;];
+  a=1; b=1; f(0,1, nf,v,ind);
+  while (res<co,
+    if (gcd(a,b)==1,
+      f(a,b, nf,v,ind); f(-a,b, nf,v,ind)
+    );
+    a++;
+    if (a*b>lima, b++; a=1)
+  );
+  print(" ");
+  mh=mathnf(m); ms=matsize(mh);
+  if (ms[1]!=ms[2],
+    print("not enough relations for class group: matrix size = ",ms);
+    return
+  );
+
+  mhs = matsnf(mh,4);
+  clh = prod(i=1,#mhs, mhs[i]);
+  printf("class number = %s, class group = %s\n", clh, mhs);
+  areg=Mat(areg); km=matkerint(m); mregh=mreg*km;
+  if (lireg==1,
+    R = 1
+  ,
+    coreg = #mregh;
+    if (coreg < lireg-1,
+      print("not enough relations for regulator: matsize = ", matsize(mregh));
+      R = "(not given)";
+    ,
+      mreg1 = mregh[1 .. lireg-1, ];
+      R = 0;
+      for(j=lireg-1,coreg,
+        a = matdet(mreg1[, j-lireg+2 .. j]);
+        R = rgcd(a,R)
+      )
+    )
+  );
+  print("regulator = " R);
+}
+
+check(lim=200) =
+{ my(r1,r2,pol,z,Res,fa);
+
+  [r1,r2] = nf.sign;
+  pol = nf.pol;
+  z = 2^r1 * (2*Pi)^r2 / sqrt(abs(nf.disc)) / nfrootsof1(nf)[1];
+  Res = 1.; \\ Res (Zeta_K,s=1) ~ z * h * R
+  forprime (q=2,lim,
+    fa = factormod(pol,q,1)[,1];
+    Res *= (q-1)/q / prod(i=1, #fa, 1 - q^(-fa[i]))
+  );
+  z * clh * R / Res;
+}
+
+fu() = vector(#km, k, factorback(concat(areg, km[,k])));
diff --git a/examples/classno.gp b/examples/classno.gp
new file mode 100644
index 0000000..62640ab
--- /dev/null
+++ b/examples/classno.gp
@@ -0,0 +1,34 @@
+\\ ---------------  GP code  ---------------------------------------
+\\
+\\ Time-stamp: <Fri, Mar 26, 1999 - 14:13:17 - villegas at linux47>
+\\
+\\ Description: Compute class number of imaginary quadratic field
+\\ analytically
+\\
+\\ File: classno.gp
+\\
+\\ Original Author: Fernando Rodriguez-Villegas
+\\                  villegas at math.utexas.edu
+\\                  University of Texas at Austin
+\\
+\\ Created:         Fri Mar 26 1999
+\\-----------------------------------------------------------------
+
+\\ Class number h(-d), -d fundamental.
+\\ Adjust constant cc for accuracy, default at least 9 decimal places.
+
+cl(d, cc = 5) =
+{ my(q0,sd,c, s = 0, q = 1);
+
+  if (!isfundamental(-d), error("Discriminant not fundamental"));
+  sd = sqrt(d);
+  q0 = exp(-2*Pi/sd); c = -4*Pi/sd;
+  for (n=1, ceil(sd*cc),
+    my(t);
+    q *= q0; t = 1/(1-q);
+    s += kronecker(-d,n) * q * t * (1 + c*t*n)
+  );
+  if (d==3, s *= 3,
+      d==4, s *= 2);
+  -2*s;
+}
diff --git a/examples/contfrac.gp b/examples/contfrac.gp
new file mode 100644
index 0000000..bfbf1d6
--- /dev/null
+++ b/examples/contfrac.gp
@@ -0,0 +1,13 @@
+period(D) =
+{ my(u,v,j,r,s);
+
+  if (type(D) != "t_INT" || D < 2, return(-1));
+  u = sqrtint(D); v = D-u^2;
+  if (!v, return(0));
+  s = v;
+  r = u; j = 0;
+  until (u == r && v == s,
+    u = (r+u)\v * v - u;
+    v = (D-u^2)\v; j++;
+  ); j;
+}
diff --git a/examples/extgcd.c b/examples/extgcd.c
new file mode 100644
index 0000000..3c8a0b5
--- /dev/null
+++ b/examples/extgcd.c
@@ -0,0 +1,40 @@
+#include <pari/pari.h>
+
+/*
+GP;install("extgcd", "GG&&", "gcdex", "./libextgcd.so");
+*/
+
+/* return d = gcd(a,b), sets u, v such that au + bv = gcd(a,b) */
+GEN
+extgcd(GEN A, GEN B, GEN *U, GEN *V)
+{
+  pari_sp av = avma;
+  GEN ux = gen_1, vx = gen_0, a = A, b = B;
+
+  if (typ(a) != t_INT) pari_err_TYPE("extgcd",a);
+  if (typ(b) != t_INT) pari_err_TYPE("extgcd",b);
+  if (signe(a) < 0) { a = negi(a); ux = negi(ux); }
+  while (!gequal0(b))
+  {
+    GEN r, q = dvmdii(a, b, &r), v = vx;
+
+    vx = subii(ux, mulii(q, vx));
+    ux = v; a = b; b = r;
+  }
+  *U = ux;
+  *V = diviiexact( subii(a, mulii(A,ux)), B );
+  gerepileall(av, 3, &a, U, V); return a;
+}
+
+int
+main()
+{
+  GEN x, y, d, u, v;
+  pari_init(1000000,2);
+  printf("x = "); x = gp_read_stream(stdin);
+  printf("y = "); y = gp_read_stream(stdin);
+  d = extgcd(x, y, &u, &v);
+  pari_printf("gcd = %Ps\nu = %Ps\nv = %Ps\n", d, u, v);
+  pari_close();
+  return 0;
+}
diff --git a/examples/lucas.gp b/examples/lucas.gp
new file mode 100644
index 0000000..145de47
--- /dev/null
+++ b/examples/lucas.gp
@@ -0,0 +1,5 @@
+lucas(p) =
+{ my(u = 4, q = 1<<p - 1);
+  for(k=3, p, u = (sqr(u)-2) % q);
+  u == 0;
+}
diff --git a/examples/minigp.c b/examples/minigp.c
new file mode 100644
index 0000000..a20fc33
--- /dev/null
+++ b/examples/minigp.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <pari/pari.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <setjmp.h>
+
+char * prompt = NULL;
+int chrono = 0;
+jmp_buf env;
+
+GEN sd_prompt(const char *v, long flag)
+{
+  if (v) { pari_free(prompt); prompt = strdup(v); }
+  if (flag == d_RETURN) return strtoGENstr(prompt);
+  else if (flag == d_ACKNOWLEDGE)
+    pari_printf("   prompt = \"%s\"\n", prompt);
+  return gnil;
+}
+GEN
+sd_timer(const char *v, long flag)
+{ return sd_toggle(v,flag,"timer", &chrono); }
+void gp_err_recover(long numerr) { longjmp(env, numerr); }
+void gp_quit(long exitcode) { exit(exitcode); }
+void help(const char *s)
+{
+  entree *ep = is_entry(s);
+  if (ep && ep->help)
+    pari_printf("%s\n",ep->help);
+  else
+    pari_printf("Function %s not found\n",s);
+}
+
+entree functions_gp[]={
+  {"quit",0,(void*)gp_quit,11,"vD0,L,","quit({status = 0}): quit, return to the system with exit status 'status'."},
+  {"help",0,(void*)help,11,"vr","help(fun): display help for function fun"},
+  {NULL,0,NULL,0,NULL,NULL}};
+
+entree default_gp[]={
+  {"prompt",0,(void*)sd_prompt,16,"","(default) string to be printed as prompt"},
+  {"timer",0,(void*)sd_timer,16,"","(default) toggle timer on/off"},
+  {NULL,0,NULL,0,NULL,NULL}};
+
+#define col(a) term_get_color(NULL, a)
+
+int main(int argc, char **argv)
+{
+  pari_init(8000000,500000);
+  pari_add_module(functions_gp);
+  pari_add_defaults_module(default_gp);
+  sd_colors("lightbg",d_INITRC);
+  sd_prompt("? ",d_INITRC);
+  pari_printf("Welcome to minigp!\n");
+  cb_pari_err_recover = gp_err_recover;
+  (void)setjmp(env);
+  while(1)
+  {
+    GEN z;
+    char *pr = pari_sprintf("%s%s%s",col(c_PROMPT),prompt,col(c_INPUT));
+    char *in = readline(pr);
+    pari_timer T;
+    long time;
+
+    if (!in) break;
+    if (!*in) continue;
+
+    add_history(in);
+    timer_start(&T); z = gp_read_str(in); time = timer_delay(&T);
+    pari_add_hist(z, time);
+    if (z != gnil && in[strlen(in)-1] != ';')
+    {
+      pari_printf("%s%%%lu = %s",col(c_HIST),pari_nb_hist(),col(c_OUTPUT));
+      output(z);
+    }
+    if (chrono && time)
+      pari_printf("%stime = %s%ld ms%s.\n",
+                  col(c_NONE),col(c_TIME),time,col(c_NONE));
+    free(in); avma = top;
+  }
+  return 0;
+}
diff --git a/examples/openmp.c b/examples/openmp.c
new file mode 100644
index 0000000..0c231fa
--- /dev/null
+++ b/examples/openmp.c
@@ -0,0 +1,50 @@
+#include <pari/pari.h> /* Include PARI headers */
+
+#include <omp.h>       /* Include OpenMP headers */
+
+#define MAXTHREADS 3  /* Max number of parallel threads */
+
+int
+main(void)
+{
+  GEN M,N1,N2, F1,F2,D;
+  struct pari_thread pth[MAXTHREADS];
+  int numth = omp_get_max_threads(), i;
+  /* Initialise the main PARI stack and global objects (gen_0, etc.) */
+  pari_init(4000000,500000);
+  if (numth > MAXTHREADS)
+  {
+    numth = MAXTHREADS;
+    omp_set_num_threads(numth);
+  }
+  /* Compute in the main PARI stack */
+  N1 = addis(int2n(256), 1); /* 2^256 + 1 */
+  N2 = subis(int2n(193), 1); /* 2^193 - 1 */
+  M = mathilbert(80);
+  /*Allocate pari thread structures */
+  for (i = 1; i < numth; i++) pari_thread_alloc(&pth[i],4000000,NULL);
+#pragma omp parallel
+  {
+    int this_th = omp_get_thread_num();
+    if (this_th) (void)pari_thread_start(&pth[this_th]);
+#pragma omp sections
+    {
+#pragma omp section
+      {
+        F1 = factor(N1);
+      }
+#pragma omp section
+      {
+        F2 = factor(N2);
+      }
+#pragma omp section
+      {
+        D = det(M);
+      }
+    } /* omp sections */
+    if (this_th) pari_thread_close();
+  } /* omp parallel */
+  pari_printf("F1=%Ps\nF2=%Ps\nlog(D)=%Ps\n", F1, F2, glog(D,3));
+  for (i = 1; i < numth; i++) pari_thread_free(&pth[i]);
+  return 0;
+}
diff --git a/examples/pari-mt.c b/examples/pari-mt.c
new file mode 100644
index 0000000..fc3a4bc
--- /dev/null
+++ b/examples/pari-mt.c
@@ -0,0 +1,51 @@
+#include <pari/pari.h> /* Include PARI headers */
+
+GEN
+my_worker(GEN d, long l)
+{
+  return l==1 ? Z_factor(d) : det(d);
+}
+
+int
+main(void)
+{
+  long i;
+  GEN M,N1,N2, F1,F2,D;
+  GEN input, output;
+  struct pari_mt pt;
+  GEN done, worker;
+  long workid, pending;
+  entree ep_worker={"_worker",0,(void*)my_worker,14,"GL",""};
+  /* Initialize the main PARI stack and global objects (gen_0, etc.)
+     Postpone initialization of parallelism */
+  pari_init_opts(8000000,500000,INIT_JMPm | INIT_SIGm | INIT_DFTm | INIT_noIMTm);
+  /* Add my_worker function to gp */
+  pari_add_function(&ep_worker);
+  /* Initialize parallelism, now that my_worker is registered */
+  pari_mt_init();
+  /* Compute in the main PARI stack */
+  N1 = addis(int2n(256), 1); /* 2^256 + 1 */
+  N2 = subis(int2n(193), 1); /* 2^193 - 1 */
+  M = mathilbert(80);
+  /* Create input and output vectors */
+  input  = mkvec3(N1,N2,M);
+  output = cgetg(4,t_VEC);
+  /* Initialize parallel evaluation of my_worker */
+  worker = strtofunction("_worker");
+  mt_queue_start(&pt, worker);
+  for (i=1; i<=3 || pending; i++)
+  {
+    /* submit job (input) */
+    mt_queue_submit(&pt, i,
+      i<=3? mkvec2(gel(input,i),i<=2 ? gen_1: gen_2): NULL);
+    /* get result (output) */
+    done = mt_queue_get(&pt, &workid, &pending);
+    if (done) gel(output,workid) = done;
+  }
+  /* end parallelism */
+  mt_queue_end(&pt);
+  F1 = gel(output,1); F2 = gel(output,2); D = gel(output,3);
+  pari_printf("F1=%Ps\nF2=%Ps\nlog(D)=%Ps\n", F1, F2, glog(D,3));
+  pari_close();
+  return 0;
+}
diff --git a/examples/rho.gp b/examples/rho.gp
new file mode 100644
index 0000000..e0ff663
--- /dev/null
+++ b/examples/rho.gp
@@ -0,0 +1,53 @@
+rho1(n)=
+{ my(x = 2,y = 5);
+
+  while(gcd(y-x,n) == 1,
+    x = (x^2+1)%n;
+    y = (y^2+1)%n; y = (y^2+1)%n
+  );
+  gcd(n, y-x);
+}
+
+rho2(n)=
+{ my(m = rho1(n));
+
+  if (isprime(m), print(m), rho2(m));
+  if (isprime(n/m), print(n/m), rho2(n/m));
+}
+
+rho(n)=
+{ my(m = factor(n,0));
+
+  print(m); m = m[,1]; n = m[#m];
+  if (!isprime(n), rho2(n));
+}
+
+rhobrent(n)=
+{ my(x,y,x1,k,l,p,c,g);
+
+  x1 = x = y = 2;
+  k = l = p = 1;
+  c = 0;
+  while (1,
+    x=(x^2+1)%n; p=(p*(x1-x))%n;
+    c++;
+    if (c==20,
+      if (gcd(p,n)>1, break);
+      y = x; c = 0
+    );
+    k--;
+    if (!k,
+      if (gcd(p,n)>1, break);
+
+      x1 = x; k = l; l <<= 1;
+      for (j=1,k, x = (x^2+1)%n);
+      y = x; c = 0
+    )
+  );
+  until (g != 1,
+    y = (y^2+1)%n;
+    g = gcd(x1-y,n)
+  );
+  if (g==n, error("algorithm fails"));
+  g;
+}
diff --git a/examples/squfof.gp b/examples/squfof.gp
new file mode 100644
index 0000000..36acf13
--- /dev/null
+++ b/examples/squfof.gp
@@ -0,0 +1,51 @@
+\\ return one non-trivial divisor of n > 1 using Shanks's SQUFOF
+squfof(n) =
+{ my (p, D, d, a, b, b1, f, g, l, bb, gs);
+  if (isprime(n) || issquare(n, &n), return(n));
+
+  p = factor(n,0)[1,1];
+  if (p != n, return(p));
+
+  if (n%4==1,
+    D = n;      d = sqrtint(D); b = (((d-1)\2) << 1) + 1
+  ,
+    D = n << 2; d = sqrtint(D); b = (d\2) << 1
+  );
+  f = Qfb(1, b, (b^2-D)>>2);
+  l = sqrtint(d);
+
+  q = []; lq = 0; i = 0;
+  while (1,
+    i++;
+    f = qfbred(f, 3, D, d);
+    a = component(f, 1);
+    if (!(i%2) && issquare(a, &as),
+      my(j = 1);
+      while (j<=lq,
+        if (as == q[j], break);
+        j++
+      );
+      if (j > lq, break)
+    );
+
+    if (abs(a) <= l,
+      q = concat(q, abs(a));
+      print(q); lq++;
+    )
+  );
+
+  print("i = ", i); print(f);
+  bb = component(f, 2);
+  gs = gcd([as, bb, D]);
+  if (gs > 1, return (gs));
+
+  f = Qfb(as, -bb, as*component(f,3));
+  g = qfbred(f, 3, D, d);
+  b = component(g, 2);
+  until (b1 == b,
+    b1 = b; g = qfbred(g, 3, D, d);
+    b = component(g, 2);
+  );
+  a = abs(component(g, 1));
+  if (a % 2, a, a>>1);
+}
diff --git a/examples/taylor.gp b/examples/taylor.gp
new file mode 100644
index 0000000..a0979c3
--- /dev/null
+++ b/examples/taylor.gp
@@ -0,0 +1,58 @@
+\\ adapted from an original idea by Ilya Zakharevich
+
+\\ generate an RGB color triple from a "magnitude" between 0 and 255
+\\ (low = close to a cold blue, high = close to a hot red).
+\\ To generate simple colormaps.
+rgb(mag) =
+{ my(x = mag/255., B, G, R);
+  B = min(max(4*(0.75-x),     0), 1);
+  R = min(max(4*(x-0.25),     0), 1);
+  G = min(max(4*abs(x-0.5)-1, 0), 1);
+  return (floor([R, G, B]*255));
+}
+default(graphcolormap, concat(["white","black","blue"], vector(25,i,rgb(10*i))));
+default(graphcolors, vector(25,i,i+2));
+
+\\ plot Taylor polynomials of f,
+\\ of index  first + i*step <= ordlim, for x in [xmin,xmax].
+plot_taylor(f, xmin=-5, xmax=5, ordlim=16, first=1, step=1) =
+{
+  my(T,s,t,w,h,dw,dh,cw,ch,gh, extrasize = 0.6);
+  my(Taylor_array);
+
+  default(seriesprecision,ordlim+1);
+  T = f('q);
+  ordlim = (ordlim-first)\step + first;
+  Taylor_array = vector(ordlim+1);
+  forstep(i=ordlim+1, 1, -1,
+    T += O('q^(1 + first + (i-1)*step));
+    Taylor_array[i] = truncate(T)
+  );
+
+  t = plothsizes();
+  w=floor(t[1]*0.9)-2; dw=floor(t[1]*0.05)+1; cw=t[5];
+  h=floor(t[2]*0.9)-2; dh=floor(t[2]*0.05)+1; ch=t[6];
+
+  plotinit(2, w+2*dw, h+2*dh);
+  plotinit(3, w, floor(h/1.2));
+  \\ few points (but Recursive!), to determine bounding box
+  s = plotrecth(3, x=xmin,xmax, f(x),
+                "Recursive|no_X_axis|no_Y_axis|no_Frame", 16);
+  gh=s[4]-s[3];
+
+  plotinit(3, w, h);
+  plotscale(3, s[1], s[2], s[3]-gh*extrasize/2, s[4]+gh*extrasize/2);
+  plotrecth(3, x=xmin,xmax, subst(Taylor_array, 'q, x), "no_Rescale");
+  plotclip(3);
+  plotcopy(3, 2, dw, dh);
+
+  plotmove(2, floor(dw+w/2-15*cw), floor(dh/2));
+  plotstring(2, "Multiple Taylor Approximations");
+  plotdraw([2, 0, 0]);
+}
+
+\p9
+plot_taylor(sin)
+plot_taylor(exp,-3,3)
+plot_taylor(x->besselk(2,x), 1,5)
+plot_taylor(x->1/(1+x^2), -1.2,1.2)
diff --git a/examples/thread.c b/examples/thread.c
new file mode 100644
index 0000000..5ed3c01
--- /dev/null
+++ b/examples/thread.c
@@ -0,0 +1,57 @@
+#include <pari/pari.h> /* Include PARI headers */
+
+#include <pthread.h>   /* Include POSIX threads headers */
+
+void *
+mydet(void *arg)
+{
+  GEN F, M;
+  /* Set up thread stack and get thread parameter */
+  M = pari_thread_start((struct pari_thread*) arg);
+  F = det(M);
+  /* Free memory used by the thread */
+  pari_thread_close();
+  return (void*)F;
+}
+
+void *
+myfactor(void *arg)  /* same principle */
+{
+  GEN F, N;
+  N = pari_thread_start((struct pari_thread*) arg);
+  F = factor(N);
+  pari_thread_close();
+  return (void*)F;
+}
+
+int
+main(void)
+{
+  GEN M,N1,N2, F1,F2,D;
+  pthread_t th1, th2, th3; /* POSIX-thread variables */
+  struct pari_thread pth1, pth2, pth3; /* pari thread variables */
+
+  /* Initialise the main PARI stack and global objects (gen_0, etc.) */
+  pari_init(4000000,500000);
+  /* Compute in the main PARI stack */
+  N1 = addis(int2n(256), 1); /* 2^256 + 1 */
+  N2 = subis(int2n(193), 1); /* 2^193 - 1 */
+  M = mathilbert(80);
+  /* Allocate pari thread structures */
+  pari_thread_alloc(&pth1,4000000,N1);
+  pari_thread_alloc(&pth2,4000000,N2);
+  pari_thread_alloc(&pth3,4000000,M);
+  /* pthread_create() and pthread_join() are standard POSIX-thread
+   * functions to start and get the result of threads. */
+  pthread_create(&th1,NULL, &myfactor, (void*)&pth1);
+  pthread_create(&th2,NULL, &myfactor, (void*)&pth2);
+  pthread_create(&th3,NULL, &mydet,    (void*)&pth3); /* Start 3 threads */
+  pthread_join(th1,(void*)&F1);
+  pthread_join(th2,(void*)&F2);
+  pthread_join(th3,(void*)&D); /* Wait for termination, get the results */
+  pari_printf("F1=%Ps\nF2=%Ps\nlog(D)=%Ps\n", F1, F2, glog(D,3));
+  pari_thread_free(&pth1);
+  pari_thread_free(&pth2);
+  pari_thread_free(&pth3); /* clean up */
+  return 0;
+}
diff --git a/misc/README b/misc/README
new file mode 100644
index 0000000..53e77bc
--- /dev/null
+++ b/misc/README
@@ -0,0 +1,9 @@
+This directory contains:
+
+color.dft: color ressources to use with color_xterm.
+gpflog   : a script to filter out escape sequences from a gp log file
+gpalias  : useful aliases to be read from the gprc
+gprc.dft : a sample gprc (generic)
+gprc.dos : a sample gprc for DOS (move it to C:\_GPRC, or to the file $GPRC)
+tex2mail : an alternate prettyprinter ( for default(output, 3) or \o3 )
+xgp      : a simple script starting gp in a beefed-up xterm.
diff --git a/misc/color.dft b/misc/color.dft
new file mode 100644
index 0000000..ba6177e
--- /dev/null
+++ b/misc/color.dft
@@ -0,0 +1,32 @@
+!
+! Suggested default value for the color_xterm ressources for use under GP.
+! See the color_xterm man for the meaning of these
+!
+
+XTerm*highlightSelection:  true
+XTerm.VT100.eightBitInput:  true
+XTerm.VT100.eightBitOutput:  true
+XTerm.VT100.titeInhibit:  true
+XTerm.VT100.altScreenInhibit:  true
+
+XTerm.VT100*colorMode:  on
+XTerm.VT100*dynamicColors:  on
+
+XTerm*color0: black
+XTerm*color1: red3
+XTerm*color2: green3
+XTerm*color3: #e52d2d
+XTerm*color4: blue3
+XTerm*color5: magenta3
+XTerm*color6: cyan3
+XTerm*color7: gray90
+XTerm*color8: gray30
+XTerm*color9: red
+XTerm*color10: green
+XTerm*color11: yellow
+XTerm*color12: blue
+XTerm*color13: magenta
+XTerm*color14: cyan
+XTerm*color15: white
+XTerm*colorUL: yellow
+XTerm*colorBD: white
diff --git a/misc/gpalias b/misc/gpalias
new file mode 100644
index 0000000..afe7af3
--- /dev/null
+++ b/misc/gpalias
@@ -0,0 +1,31 @@
+alias(algtobasis,nfalgtobasis);
+alias(basistoalg,nfbasistoalg);
+alias(cf,contfrac);
+alias(classgroup,bnfclassunit);
+alias(coeff,polcoeff);
+alias(compositum,polcompositum);
+alias(degree,poldegree);
+alias(disc,poldisc);
+alias(discreduced,poldiscreduced);
+alias(extract,vecextract);
+alias(galois,polgalois);
+alias(hnf,mathnf);
+alias(hnfmod,mathnfmod);
+alias(ker,matker);
+alias(kerint,matkerint);
+alias(kro,kronecker);
+alias(ln,log);
+alias(matconcat,concat);
+alias(matextract,vecextract);
+alias(mod,Mod);
+alias(pnqn,contfracpnqn);
+alias(resultant,polresultant);
+alias(roots,polroots);
+alias(rootsmod,polrootsmod);
+alias(rootspadic,polrootspadic);
+alias(sercoeff,polcoeff);
+alias(snf,matsnf);
+alias(trunc,truncate);
+alias(tschirnhaus,poltschirnhaus);
+alias(vecbern,bernvec);
+alias(vectorh,vector);
diff --git a/misc/gpflog b/misc/gpflog
new file mode 100755
index 0000000..be77fbb
--- /dev/null
+++ b/misc/gpflog
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+# Strip escape sequences from PARI log file.
+
+for i in "$@"; do
+  sed -e 's/[^]*//g; s/\[[0-9;]*m//g' $i
+done
diff --git a/misc/gprc.dft b/misc/gprc.dft
new file mode 100644
index 0000000..b8d7b7c
--- /dev/null
+++ b/misc/gprc.dft
@@ -0,0 +1,92 @@
+/* SAMPLE GP INIT FILE.
+ *
+ * Customize (by uncommenting and modifying the relevant lines) and put in
+ * $HOME/.gprc (or $GPRC) or /etc/gprc. Syntax is explained at the end. */
+
+\\ Read ~/.gprc.gp (GP script) before gp prompts you for commands.
+\\ read "~/.gprc.gp"
+
+/* Most important options */
+
+\\ Set PARI stack size to 10 Mbytes = 10^7 bytes
+\\ parisize = 10M
+
+\\ Save a history of all input commands in this file, and load it in each
+\\ new session's history
+\\ histfile = ~/.gp_history
+
+\\ Limit output of results to 40 lines
+\\ lines = 40
+
+\\ Colors:
+\\ clear background:
+\\   #ifnot EMACS colors = "lightbg"
+\\ dark background:
+\\   #ifnot EMACS colors = "darkbg"
+
+\\ Set gp prompt. % is used for macros related to the time of day [back to
+\\ the shell prompt, try "man strftime"]. Example: %H:%M = time of day in the
+\\ form HH:MM. Characters can be escaped UNIX-style using '\', e.g \e = <ESC>
+\\
+\\ prompt = "(%H:%M) \e[1mgp\e[m > "
+\\ #if READL prompt = "(%H:%M) \e[1mgp\e[m > "
+\\ #if EMACS prompt = "? "
+
+\\ Set timer on
+\\ timer = 1
+
+\\ Path: directories where gp will look for scripts
+\\ path = ".:~:~/gp"
+
+\\ Use an alternate prettyprinter
+\\ prettyprinter = "/usr/local/bin/tex2mail -TeX -noindent -ragged -by_par"
+
+\\ Extended help options (does not interact well with emacs):
+\\ Don't use TeX + xdvi, but outputs formatted help in GP window:
+\\   #ifnot EMACS help = "gphelp -detex"
+\\ Same, using colors:
+\\   #ifnot EMACS help = "gphelp -detex -ch 4 -cb 0 -cu 2"
+
+/* Some less important options */
+
+\\ Biggest precomputed prime (= precprime(10^6))
+\\ primelimit = 1M
+
+\\ Set logfile name and enable logging.
+\\ Uncommenting the next two lines produces a different logfile each day:
+\\ logfile = "~/tmp/pari-%d.%m"
+\\ log = 1
+
+\\ Output for postscript-producing gp commands.
+\\ psfile = "~/tmp/pari.ps"
+
+\\ secure = 1
+\\ Disable commands system() and extern(). These commands allow scripts to
+\\ execute arbitrary Unix commands
+
+\\********************** FORMAT OF THIS FILE :    ***************************
+\\  Lines starting with '\\' and between '/*' '*/' pairs are comments
+\\  Blank lines are ignored
+\\  Line starting with #if BOOLEAN is read iff BOOLEAN is TRUE
+\\  Currently recognized booleans:
+\\    EMACS  are we running under Emacs?
+\\    READL  is readline available?
+\\    VERSION {<,>,<=,>=} a.b.c  does version number satisfy the inequality?
+\\
+\\  This file should be put in $HOME/.gprc or /etc/gprc and contains:
+\\  * references to gp scripts that are to be run BEFORE the first gp prompt.
+\\
+\\    Syntax: read "filename"  (quotes are mandatory. ~ syntax allowed)
+\\
+\\  * options settings
+\\
+\\    Syntax: option_name = value
+\\
+\\  Options which are not set here assume default values in gp. The command
+\\  default() under GP lists available options and their default values.
+\\
+\\  Options settings can be overriden by command line switches. For instance
+\\  gp --default parisize=10M
+\\  will set parisize to 10000000, regardless of what is in .gprc
+\\  They can also be changed under GP using default(): e.g.
+\\  default(parisize,"10M") 
diff --git a/misc/gprc.dos b/misc/gprc.dos
new file mode 100644
index 0000000..5f59d0e
--- /dev/null
+++ b/misc/gprc.dos
@@ -0,0 +1,33 @@
+\\ $Id$
+\\
+\\  SAMPLE GP INIT FILE (for DOS boxes)
+\\
+\\  See gprc.dft for the general syntax and explanations
+\\  Customize, then copy to $HOME/_gprc (or $GPRC) or /etc/gprc
+
+\\ compatible = 0
+
+\\ read "lib/gpalias"
+
+\\ secure = 1
+
+\\ under DOS, directories are separated by ';', not ':'
+\\ path = ".;~;..;~/gpdir"
+
+\\ the readline we supply is old and doesn't have ^A / ^B
+prompt = "(%H:%M) \e[1mgp\e[m > "
+#if READL prompt = "(%H:%M) gp > "
+#if EMACS prompt = "? "
+
+#ifnot EMACS colors = "1, 5, no, no, 6, 1, 2"
+
+\\ #ifnot EMACS help = "/usr/local/bin/gphelp -detex -ch 4 -cb 0 -cu 2"
+
+\\ parisize = 10M
+\\ primelimit = 1M
+\\ timer = 1
+
+\\ logfile  = "~/tmp/pari-%d.%m"
+\\ log = 1
+
+\\ psfile = "~/tmp/pari.ps"
diff --git a/misc/tex2mail.in b/misc/tex2mail.in
new file mode 100755
index 0000000..316f00e
--- /dev/null
+++ b/misc/tex2mail.in
@@ -0,0 +1,2671 @@
+#!@perl@
+# Original version by Ilya Zakharevich. Minor updates by the PARI group
+# Features:
+#      % at the end of a line followed by \n\n is recognized as end of 
+#      paragraph :-(
+#
+# Original change log is at bottom.
+#
+# Options:
+#	linelength=75		# Cut at this line
+#	maxdef=400		# definition loops: croak if many substitutions
+#	debug=0
+#	by_par=0		# Expect each paragraph to be terminated
+#				# by *exactly* 2 "\n", and do not print
+#				# an extra "\n" between paragraphs
+#	TeX			# Assume it is not LaTeX
+#	ragged			# leave right ragged
+#	noindent		# assume \noindent everywhere
+#	ignorefonts		# make math fonts (\mathfrak etc) into NOPs
+#	scissors		# Symbol to emit when a long line is cut
+$@ = '';
+$opt_TeX      = 1;
+$opt_noindent = 1;
+$opt_ragged   = 1;
+$opt_by_par   = 1;
+eval '
+  use Getopt::Long;
+  &GetOptions(
+    "linelength=i" => \$opt_linelength,
+    "maxdef=i" => \$opt_maxdef,
+    "debug=i" => \$opt_debug,
+    "by_par" => \$opt_by_par,
+    "TeX" => \$opt_TeX,
+    "ragged" => \$opt_ragged,
+    "noindent" => \$opt_noindent,
+    "ignorefonts" => \$opt_ignorefonts,
+    "scissors=s" => \$opt_scissors,
+    "noflush" => \$opt_noflush)
+';
+if ($@ && $@ !~ /Getopt\/Long/) {
+  warn "Errors while parsing command line options $@\n";
+}
+$linelength= $opt_linelength || 75;
+$maxdef= $opt_maxdef || 10000;
+$debug=$opt_debug;
+$opt_scissors = "---8<---8<---" unless defined $opt_scissors;
+
+$notusualtoks="\\\\" . '\${}^_~&@';
+$notusualtokenclass="[$notusualtoks]";
+$usualtokenclass="[^$notusualtoks]";
+$macro='\\\\([^a-zA-Z]|([a-zA-Z]+\s*))'; # Why \\\\? double interpretation!
+$active="$macro|\\\$\\\$|$notusualtokenclass";
+$tokenpattern="$usualtokenclass|$active";
+$multitokenpattern="$usualtokenclass+|$active";
+
+$| = 1 unless $opt_noflush;
+
+# Format of the record: height,length,baseline,expandable-spaces,string
+# The string is not terminated by \n, but separated into rows by \n.
+# height=0 denotes expandable string
+# Baseline=3 means the 4th row is the baseline
+
+sub debug_print_record {
+  local($h,$l,$b,$xs,$s) = split /,/, shift, 5;
+  local(@arr) = split /\n/, $s;
+  print STDERR "len=$l, h=$h, b=$b, exp_sp=$xs.\n";
+  local($i) = 0;
+  for (@arr) {
+    local($lead) = ($i++ == $b) ? 'b [' : '  [';
+    print STDERR "$lead$_]\n";
+  }
+  while ($i < $h) {		# Empty lines may skipped
+    local($lead) = ($i++ == $b) ? 'b' : '';
+    print STDERR "$lead\n";
+  }
+}
+
+# Takes length and a record, returns 2 records
+
+sub cut {
+  local($length)=(shift);
+  local($h,$l,$b,$sp,$str)=split(/,/,shift,5);
+  local($st1,$st2)=("","");
+  local($sp1,$sp2,$first,$l2)=(0,0,1,$l-$length);
+  return (shift,&empty) if $l2<0;
+  if ($h) {
+    for (split(/\n/,$str,$h)) {
+      if (!$first) {
+        $st1 .= "\n";
+        $st2 .= "\n";
+      } else {$first=0;}
+      $st1 .= substr($_,0,$length);
+      $st2 .= substr($_,$length);
+    }
+  } else {
+    $st1 = substr($str,0,$length);
+    $st2 = substr($str,$length);
+    #if ($sp && ($st1 =~ /(\S)(\s+\S*)$/)) {
+    # $st2 = $2 . $st2;
+    # $st1 = $` . $1;
+    # $sp1 = ($st1 =~ /(\s)/g);
+    # $sp2 = ($st2 =~ /(\s)/g);
+    #}  
+  }
+  return ("$h,$length,$b,$sp1,$st1","$h,$l2,$b,$sp2,$st2");
+}
+
+# Outputs a record
+
+sub printrecord {
+  warn "Printing $_[0]\n__ENDPRINT__\n" if $debug & $debug_record;  
+  local($h,$l,$b,$sp,$str)=split(/,/,shift,5);
+  print $str,"\n";
+} 
+
+# Joins two records
+
+sub join {
+  local($h1,$l1,$b1,$sp1,$str1)=split(/,/,shift,5);
+  local($h2,$l2,$b2,$sp2,$str2)=split(/,/,shift,5);
+  $h1 || $h1++;
+  $h2 || $h2++;
+  local($h,$l,$b,$sp,$str, at str, at str2)=(0,0,0,$sp1+$sp2,"");
+  $b = $b1 > $b2 ? $b1 : $b2;
+  # Calculate space below baseline
+  $h = $h1-$b1 > $h2-$b2 ? $h1-$b1 : $h2-$b2;
+  # And height
+  $h += $b;
+  $l=$l1+$l2;
+  @str="" x $h;
+  @str[$b-$b1 .. $b-$b1+$h1-1]=split(/\n/,$str1,$h1);
+  @str2[0..$h2-1]=split(/\n/,$str2,$h2);
+  unless (length($str2[$b2])) {
+    $str2[$b2] = ' ' x $l2;	# Needed for length=0 "color" strings
+                                # in the baseline.
+  }
+  if ($debug & $debug_record && (grep(/\n/, at str) || grep(/\n/, at str2))) {
+    warn "\\n found in \@str or \@str2";
+    warn "`$str1', need $h1 rows\n";
+    warn "`$str2', need $h2 rows\n";
+  }
+  # This is may be wrong if a zero-length record with escape sequences
+  # is appended to with something not on the same row...  But
+  # apparently, it should be OK for PARI...
+  for (0..$h2-1) {
+    $str[$b-$b2+$_] .= " " x ($l1 - length ($str[$b-$b2+$_])) . $str2[$_];
+  } 
+  return "$h,$l,$b,$sp," . join("\n", at str);
+} 
+
+# The current line is contained in the array @out of records and, possibly,
+# one additional record $last. If $last exists, $islast is set to 1.
+# The output channel length is contained in $linelength, the accumulated
+# length of @out and $last is contained in $curlength.
+# We guaranty that if $curlength>$linelength, then @out is empty.
+
+# Gets a length of a record
+
+sub length {
+  (warn "Wrong format of a record `$_[0]'", return 0)
+      unless $_[0] =~ /^\d+,(\d+)/;
+  $1;
+}
+
+# Gets a height of a record
+
+sub height {
+  (warn "Wrong format of a record `$_[0]'", return 0)
+      unless $_[0] =~ /^(\d+),/;
+  $1;
+}
+
+# Sets baseline of a record, Usage s...(rec,base)
+
+sub setbaseline {
+  (warn("Wrong format of a record `$_[0]'"), return undef)
+      unless $_[0] =~ s/^(\d+,\d+,)(\d+)/\1$_[1]/;
+}
+
+# The hierarchical structure: the records to work are in the array @out.
+# The array @chunks keeps the beginning record of the chunks,
+# The array @level keeps the beginning chunks of the given level.
+# The last chunk can begin after the last record if this chunk is still empty.
+
+# We do not keep the inner structure of the chunk unless it is the last
+# chunk on the given level.
+
+# Each record is a rectangle to output to the "page".
+
+# Each chunk is a sequence of records which reflect one finished subgroup
+# on the given level.
+
+# Each level is a sequence of chunks which correspond to a
+# not-yet-finished group in TeX input.
+
+
+# The parallel to @level array @wait
+# contains an event we wait to complete the given level of array.
+
+# Chunks on a given level
+
+# Used to expand spaces
+
+sub exp_sp {$c1++;$c2=0 if $c1>$re; return " " x ($c2+$fr+1);}
+
+# Outputs the outermost level of the output list (until the start of level 1)
+# If gets a true argument, does not expand spaces
+
+sub print {
+  warn "Printing...\n" if $debug & $debug_flow; 
+  local($last,$l,$exp) = ($#level? $chunks[$level[1]]-1: $#out);
+  ($last >=0) || return;
+  $l=&length($out[0]);
+  if ($last >= 1) {
+    for (1..$last) {
+      $l += &length($out[$_]);
+    }
+  }
+  if ($debug & $debug_length)  {
+    if ($l != $curlength) {
+      for (0..$last) {
+        warn "Wrong lengths Record $_ $out[$_]\n__ENDREC__\n" ; 
+      }
+    } 
+  } 
+  $curlength=$l;
+  warn "l=$l, linelength=$linelength, curlength=$curlength\n"
+    if $debug & $debug_length;
+ IF_L:
+ {
+  if (!shift && ($l=$linelength-$curlength)>=0) {
+    warn "entered branch for long string\n"
+      if $debug & $debug_length;
+    $exp=0;
+    (($out[$last] =~ s/\s+$//) && ($l+=length($&))) 
+        if $out[$last] =~ /^0,/;
+    warn "l=$l with whitespace\n"
+      if $debug & $debug_length;
+    last IF_L if $l<=0;
+    local($str,$h,$fr,$re,$c1,$c2, at t);
+    for (0..$last) {
+      ($str,$h)=(split(/,/,$out[$_],5))[4,0];
+      (@t = ($str =~ /( )/g), $exp+=@t) if (!$h);
+    } 
+    if ($exp) {
+      $re=$l % $exp;
+      $fr=int(($l-$re)/$exp);
+      warn "$l Extra spaces in $exp places, Fr=$fr," . 
+          " Remainder=$re, LL=$linelength, CL=$curlength\n" if $debug & $debug_length;
+      $c1=0;
+      $c2=1;
+      for (0..$last) {
+        ($str,$h)=(split(/,/,$out[$_],5))[4,0];
+        unless ($h || $opt_ragged) {
+          $str =~ s/ /&exp_sp/ge;
+          $out[$_]=&string2record($str);
+        }
+      } 
+    }     
+  } 
+  else {warn "Do not want to expand $l spaces\n" if $debug & $debug_length;}
+ }
+  if ($last >= 1) {
+    for (1..$last) {
+      $out[0] = &join($out[0],$out[$_]);
+    }
+  }
+  $l=&length($out[0]);
+  warn "LL=$linelength, CurL=$curlength, OutL=$l\n" if $debug & $debug_length;  
+  &printrecord($out[0]);
+  $curlength=0;
+  if ($#out>$last) {
+    @out=@out[$last+1..$#out];
+    for (0..$#chunks) {$chunks[$_] -= $last+1;}
+  } else {
+    @out=();
+  } 
+  if ($#level) {
+    splice(@chunks,1,$level[1]-2);
+  } else {
+    @chunks=(0);
+  }   
+}
+  
+# Cuts prepared piece and arg into printable parts (unfinished)
+# Suppose that level==0
+
+sub prepare_cut {
+  warn "Preparing to cut $_[0]\n" if $debug & $debug_flow;  
+  warn "B:Last chunk number $#chunks, last record $#out\n" if $debug & $debug_flow;
+  (warn "\$#level non 0", return $_[0]) if ($#level!=0);
+  local($lenadd)=(&length($_[0]));
+  local($lenrem)=($linelength-$curlength);
+  if ($lenadd+$curlength<=$linelength) {
+    warn "No need to cut, extra=$lenrem\n" if $debug & $debug_flow; 
+    return $_[0];
+  }
+  # Try to find a cut in the added record before $lenrem
+  local($rec)=@_;
+  local($h,$str,$ind, at p)=(split(/,/,$rec,5))[0,4];
+  local($good)=(0);
+  if ($h<2) {
+    while ($lenrem<$lenadd && ($ind=rindex($str," ",$lenrem))>-1) {
+      warn "Cut found at $ind, lenrem=$lenrem\n" if $debug & $debug_flow; 
+      $good=1;
+      # $ind=1 means we can cut 2 chars 
+      @p= &cut($ind+1,$rec);
+      warn "After cut: @p\n" if $debug & $debug_record; 
+      push(@out,$p[0]);
+      $curlength+=$ind+1;
+      #if ($#out!=$chunks[$#chunks]) {push(@chunks,$#out);}
+      &print();
+      $rec=$p[1];
+      ($lenadd,$str)=(split(/,/,$rec,5))[1,4];
+      $lenrem=$linelength;
+    }
+    return $rec if $good;
+  }
+  # If the added record is too long, there is no sense in cutting
+  # things we have already, since we will cut the added record anyway...
+  local($forcedcut);
+  if ($lenadd > $linelength && $lenrem) {
+      @p= &cut($lenrem,$rec);
+      warn "After forced cut: @p\n" if $debug & $debug_record;
+      push(@out,$p[0]);
+      $curlength+=$lenrem;
+      &print();
+      $rec=$p[1];
+      ($lenadd,$str)=(split(/,/,$rec,5))[1,4];
+      $lenrem=$linelength;
+  }
+  # Now try to find a cut before the added record
+  if ($#out>=0 && !$forcedcut) {
+    for (0..$#out) {
+      ($h,$str)=(split(/,/,$out[$#out-$_],5))[0,4];
+      if ($h<2 && ($ind=rindex($str," "))>-1 && ($ind>0 || $_<$#out)) {
+        warn "Cut found at $ind, in chunk $#out-$_\n" 
+            if $debug & $debug_flow;  
+        # split at given position
+        @p=&cut($ind+1,$out[$#out-$_]);
+        $out[$#out-$_]=$p[0];
+        @p=($p[1], at out[$#out-$_+1..$#out]);
+        @out=@out[0..$#out-$_];
+warn "\@p is !", join('!', @p), "!\n\@out is !", join('!', @out), "!\n" 
+            if $debug & $debug_flow;	
+        &print();
+	warn "did reach that\n"
+	  if $debug & $debug_length;
+        @out=@p;
+        $good=1;
+        $curlength=0;
+        for (@out) {$curlength+=&length($_);}
+        last;
+      }
+      warn "did reach wow-this\n"
+	if $debug & $debug_length;
+    }
+    warn "did reach this\n"
+      if $debug & $debug_length;
+  }
+  return &prepare_cut if $good;
+  warn "No cut found!\n" if $debug & $debug_flow; 
+  # If anything else fails use force
+  &print();
+  print "$opt_scissors\n" if length($opt_scissors) && $h > 1;
+  while (&length($rec)>$linelength) {
+    @p=&cut($linelength,$rec);
+    @out=($p[0]);
+    &print();
+    print "$opt_scissors\n" if length($opt_scissors) && $h > 1;
+    $rec=$p[1];
+  } 
+  $curlength=0;
+  return $rec;
+} 
+
+# Adds a record to the output list
+
+sub commit {
+  warn "Adding $_[0]\n" if $debug & $debug_flow;  
+  warn "B:Last chunk number $#chunks, last record $#out\n" if $debug & $debug_flow;
+  local($rec)=@_;
+  if ($#level==0) {
+    local($len)=&length($_[0]);
+    if ($curlength+$len>$linelength) {
+      $rec=&prepare_cut;
+      $len=&length($rec);
+    } 
+    $curlength+=$len;
+  }
+  push(@out,$rec);
+  if ($#out!=$chunks[$#chunks]) {push(@chunks,$#out);}
+  warn "a:Last chunk number $#chunks, last record $#out, the first chunk\n" if $debug & $debug_flow;
+  warn " on the last level=$#level is $level[$#level], waiting for $wait[$#level]\n" if $debug & $debug_flow;
+  if ($#level && $wait[$#level] == $#chunks-$level[$#level]+1) {
+    local($sub,$arg)=($action[$#level]);
+    if ($sub eq "") {&finish($wait[$#level]);}
+    else {
+      &callsub($sub);
+    }
+  }
+  warn "curlength=$curlength on level=$#level\n" if $debug & $debug_length;
+} 
+
+# Calls a subroutine, possibly with arguments
+
+sub callsub {
+  local($sub)=(shift);
+  index($sub,";")>=0?
+    (($sub,$arg)=split(";",$sub,2), &$sub($arg)):
+      &$sub;
+}
+
+# Simulates Removing a record from the output list (unfinished)
+
+sub uncommit {
+  warn "Deleting...\n" if $debug & $debug_flow; 
+  warn "B:Last chunk number $#chunks, last record $#out\n" if $debug & $debug_flow;
+  (warn "Nothing to uncommit", return) if $#out<0;
+  if ($#level==0) {
+    local($len)=&length($out[$#out]);
+    $curlength-=$len;
+  }
+  local($rec);
+  $rec=$out[$#out];
+  $out[$#out]=&empty();
+  warn "UnCommit: now $chunks[$#chunks] $rec\n__ENDREC__\n"
+      if $debug & $debug_record;  
+  #if ($#out<$chunks[$#chunks]) {pop(@chunks);}
+  warn "a:Last chunk number $#chunks, last record $#out, the first chunk\n" if $debug & $debug_flow;
+  warn " on the last level=$#level is $level[$#level], waiting for $wait[$#level]" if $debug & $debug_flow;
+  warn "curlength=$curlength on level=$#level\n" if $debug & $debug_length;
+  return $rec;
+} 
+
+# finish($event, $force_one_group)
+
+# Finish the inner scope with the event $event. If this scope is empty,
+# add an empty record.  If finishing the group would bring us to toplevel
+# and $force_one_group is not set, can break things into chunks to improve
+# line-breaking.
+
+# No additional action is executed
+
+sub finish {
+  warn "Finishing with $_[0]\n" if $debug & $debug_flow;  
+  local($event,$len,$rec)=(shift);
+  if (($wait[$#level] ne "") && ($wait[$#level] ne $event)) {
+    warn "Got `$event' while waiting for `$wait[$#wait]', rest=$par";
+  }
+  warn "Got finishing event `$event' in the outermost block, rest=$par" 
+      unless $#level;
+  return unless $#level;
+  if ($#out<$chunks[$level[$#level]]) {push(@out,&empty);}
+  # Make anything after $level[$#level] one chunk if there is anything
+  warn "B:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  $#chunks=$level[$#level]; #if $chunks[$level[$#level]]<=$#out;
+  local(@t);
+  if ($#level==1 && !$_[0]) {
+    @t=@out[$chunks[$#chunks]..$#out];
+    $#out=$chunks[$#chunks]-1;
+  }
+  #  $#chunks-- if $chunks[$#chunks-1]==$chunks[$#chunks];
+  $#level--;
+  $#action--;
+  $#tokenByToken--;
+  $#wait--;
+  if ($#level==0 && !$_[0]) {
+    for (@t) {&commit($_);}
+  }
+  warn 
+      "a:Last $#chunks, the first on the last level=$#level is $level[$#level]"
+           if $debug & $debug_flow;
+  if ($wait[$#level] == $#chunks-$level[$#level]+1) {
+    local($sub)=($action[$#level]);
+    if ($sub eq "") {&finish($wait[$#level]);}
+    else {&callsub($sub);}
+  }
+} 
+
+# finish level and discard it
+
+sub finish_ignore {
+        warn "Finish_ignoring with $_[0]\n" if $debug & $debug_flow;
+        local($event,$len)=(shift);
+        if (($wait[$#level] ne "") && ($wait[$#level] ne $event)) {
+                warn "Got `$event' while waiting for `$wait[$#wait]', rest=$par";
+        }
+        warn "Got finishing event `$event' in the outermost block, rest=$par" unless $#level;
+  $#out=$chunks[$level[$#level]]-1;
+  pop(@level);
+  pop(@tokenByToken);
+  pop(@action);
+  pop(@wait);
+}
+
+# Begin a new level with waiting for $event
+
+# Special events: If number, wait this number of chunks
+
+sub start {
+  warn "Beginning with $_[0], $_[1]\n" if $debug & $debug_flow;
+  warn "B:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  warn "#out=$#out, chunks[first of last level]=$chunks[$level[$#level]], chunk
+s[-1]=$chunks[$#chunks]" if $debug & $debug_flow;
+
+  # !@chunks: may be true in \noindent{1\over2}
+  if (!@chunks || ($chunks[$level[$#level]]<=$#out && $chunks[$#chunks]<=$#out)) {
+    # the last level is non empty
+    push(@chunks,$#out+1);
+  }
+  push(@level,$#chunks);
+  push(@tokenByToken,0);
+  $wait[$#level]=shift;
+  if ($#_<0) {$action[$#level]="";} else {$action[$#level]=shift;}
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+} 
+    
+# Asserts that the given number of chunks exists in the last level
+
+sub assertHave {
+  local($i,$ii)=(shift);
+  if (($ii=$#chunks-$level[$#level]+1)<$i) {
+    warn "Too few chunks ($ii) in inner level, expecting $i";
+    return 0;
+  }
+  return 1;
+} 
+
+# Takes the last ARGUMENT chunks, collapse them to records
+
+sub collapse {
+  warn "Collapsing $_[0]...\n" if $debug & $debug_flow; 
+  local($i,$ii,$_)=(shift);
+  if (($ii=$#chunks-$level[$#level]+1)<$i) {
+    warn "Too few chunks ($ii) in inner level, expecting $i";
+    $i=$ii;
+  }
+  if ($i>0) {
+    for (0..$i-1) {
+      &collapseOne($#chunks-$_);
+    } 
+    for (1..$i-1) {
+      $chunks[$#chunks-$_+1]=$chunks[$#chunks-$i+1]+$i-$_;
+    } 
+  } 
+} 
+
+# Collapses all the chunks on given level
+
+sub collapseAll {&collapse($#chunks-$level[$#level]+1);}
+
+# Collapses a given chunk in the array @out. No correction of @chunks is
+# performed
+
+sub collapseOne {
+  local($n)=(shift);
+  local($out,$last,$_)=($out[$chunks[$n]]);
+  if ($n==$#chunks) {$last=$#out;} else {$last=$chunks[$n+1]-1;}
+  warn "Collapsing_one $n (last=$#chunks), records $chunks[$n]..$last\n"
+                if $debug & $debug_flow;
+  return unless $last>$chunks[$n];
+  warn "Collapsing chunk $n beginning at $chunks[$n], ending at $last\n" if $debug & $debug_flow; 
+  for ($chunks[$n]+1..$last) {
+    $out=&join($out,$out[$_]);
+  } 
+  splice(@out,$chunks[$n],$last+1-$chunks[$n],$out);
+  # $#out-=$last-$chunks[$n]; #bug in perl?
+  warn "Collapsed $chunks[$n]: $out[$chunks[$n]]\n__END__\n" if $debug & $debug_record;
+} 
+
+# Return an empty record
+
+sub empty {
+  return "0,0,0,0,";
+} 
+
+# Commits a record with a sum symbol
+
+sub sum {
+  &commit("4,3,2,0," . <<'EOF');
+___
+\   
+ >
+/__ 
+EOF
+} 
+
+# Additional argument specifies if to make not-expandable, not-trimmable
+
+sub string2record {
+  local($h,$sp)=(0);
+  if ($_[1]) {$h=1;$sp=0;}
+  else {
+    $sp=($_[0] =~ /(\s)/g);
+    $sp || ($sp=0); # Sometimes it is undef?
+  }
+  return "$h," . length($_[0]) . ",0,$sp,$_[0]";
+}
+
+# The second argument forces the block length no matter what is the
+# length the string (for strings with screen escapes).
+
+sub record_forcelength {
+  $_[0] =~ s/^(\d+),(\d+)/$1,$_[1]/;
+}
+
+sub finishBuffer {
+  while ($#level>0) {&finish("");}
+  &print(1);
+} 
+
+# Takes two records, returns a record that concatenates them vertically
+# To make fraction simpler, baseline is the last line of the first record
+
+sub vStack {
+  local($h1,$l1,$b1,$sp1,$str1)=split(/,/,shift,5);
+  local($h2,$l2,$b2,$sp2,$str2)=split(/,/,shift,5);
+  $h1 || $h1++;
+  $h2 || $h2++;
+  local($h,$l,$b)=($h1+$h2, ($l1>$l2 ? $l1: $l2), $h1-1);
+  warn "\$h1=$h1, \$h2=$h2, Vstacked: $h,$l,$b,0,$str1\n$str2\n__END__\n" if $debug & $debug_record;
+  return "$h,$l,$b,0,$str1\n$str2";
+} 
+
+# Takes two records, returns a record that contains them and forms 
+# SupSub block
+
+sub superSub {
+  local($h1,$l1,$b1,$sp1,$str1)=split(/,/,shift,5);
+  local($h2,$l2,$b2,$sp2,$str2)=split(/,/,shift,5);
+  $h1 || $h1++;
+  $h2 || $h2++;
+  local($h,$l)=($h1+$h2+1, ($l1>$l2 ? $l1: $l2));
+  return "$h,$l,$h1,0,$str1\n\n$str2";
+} 
+
+# Takes two records, returns a record that contains them and forms 
+# SupSub block
+
+sub subSuper {
+  local($h1,$l1,$b1,$sp1,$str1)=split(/,/,shift,5);
+  local($h2,$l2,$b2,$sp2,$str2)=split(/,/,shift,5);
+  $h1 || $h1++;
+  $h2 || $h2++;
+  local($h,$l)=($h1+$h2+1, ($l1>$l2 ? $l1: $l2));
+  return "$h,$l,$h1,0,$str2\n\n$str1";
+} 
+
+# Takes the last two records, returns a record that contains them and forms 
+# SupSub block
+
+sub f_subSuper {
+  warn "Entering f_subSuper...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  &sup_sub(0,1);
+} 
+
+sub sup_sub {
+  local($p1,$p2)=($#out-shift,$#out-shift);
+  warn "Super $p1 $out[$p1]\nSub $p2 $out[$p2]\n__END__\n" if $debug & $debug_record;
+  local($h1,$l1,$b1,$sp1,$str1)=split(/,/,$out[$p1],5);
+  local($h2,$l2,$b2,$sp2,$str2)=split(/,/,$out[$p2],5);
+  if ($l1==0 && $l2==0) {return;}
+  $h1 || $h1++;
+  $h2 || $h2++;
+  local($h,$l)=($h1+$h2+1, ($l1>$l2 ? $l1: $l2));
+  $#chunks--;
+  $#out--;
+  if ($l1==0) {
+    $h2++;
+    $out[$#out]="$h2,$l,0,0,\n$str2";
+  } elsif ($l2==0) {
+    $h=$h1+1;
+    $out[$#out]="$h,$l,$h1,0,$str1\n";
+  } else {
+    $out[$#out]="$h,$l,$h1,0,$str1\n\n$str2";
+  } 
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+} 
+
+# Takes the last two records, returns a record that contains them and forms 
+# SupSub block
+
+sub f_superSub {
+  warn "Entering f_superSub...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  &sup_sub(1,0);
+} 
+
+# digest \begin{...} and similar: handles argument to a subroutine
+# given as argument
+
+sub f_get1 {
+  warn "Entering f_get1...\n" if $debug & $debug_flow;  
+  (warn "Argument of f_get1 consists of 2 or more chunks", return)
+      if $#out != $chunks[$#chunks];
+  local($rec,$sub);
+  #$rec=&uncommit;
+  $rec=$out[$#out];
+  $rec=~s/.*,//;
+  $sub=shift;
+  defined $sub ? return &$sub($rec): return $rec;
+} 
+
+sub f_begin {
+  warn "Entering f_begin...\n" if $debug & $debug_flow; 
+  &collapse(1);
+  &assertHave(1) || &finish("");
+  local($arg,$env)=(&f_get1());
+  &finish_ignore(1);
+  $arg=~s/^\s+//;
+  $arg=~s/\s+$//;
+  return if defined $environment_none{$arg};
+  if (defined ($env=$environment{$arg})) {
+    local($b,$e)=split(/,/,$env);
+    for (split(":",$b)) {&callsub($_);}
+  } else {&puts("\\begin{$arg}");}
+} 
+
+sub f_end {
+  warn "Entering f_end...\n" if $debug & $debug_flow;
+  &collapse(1);
+  &assertHave(1) || &finish("");
+  local($arg,$env)=(&f_get1());
+  &finish_ignore(1);
+  $arg=~s/^\s+//;
+  $arg=~s/\s+$//;
+  return if defined $environment_none{$arg};
+  if (defined ($env=$environment{$arg})) {
+    local($b,$e)=split(/,/,$env,2);
+    for (split(":",$e)) {&callsub($_);}
+  } else {&puts("\\end{$arg}");}
+} 
+
+sub f_newtheorem {
+  warn "Entering f_newtheorem...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  warn "Name `$out[$#out-1]'\nExpansion `$out[$#out]'\n__END__\n" if $debug & $debug_record;
+
+  local($name, $expand) = ($out[$#out-1], $out[$#out]);
+  $#out -= 2;
+  $#chunks -= 2;
+  return unless $name =~ s/^\d+,\d+.\d+,\d+,//;
+  return unless $expand =~ s/^\d+,\d+.\d+,\d+,//;
+  $environment{$name}="par:unindent:puts;$expand. ,par";
+  &finish(2,1);
+} 
+
+sub f_newtheorem1 {
+  warn "Entering f_newtheorem1...\n" if $debug & $debug_flow;  
+  &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Name `$out[$#out]'\n__END__\n" if $debug & $debug_record;
+
+  local($name) = ($out[$#out]);
+  $#out -= 1;
+  $#chunks -= 1;
+  $par =~ s/^\[[^\]]*\]//;	# Optional argument 2
+  return unless $name =~ s/^\d+,\d+.\d+,\d+,//;
+  $last_newtheorem = $name;
+  &finish(1,1);
+} 
+
+sub f_newtheorem2 {
+  warn "Entering f_newtheorem2...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  warn "Expansion `$out[$#out]'\n__END__\n" if $debug & $debug_record;
+
+  local($expand) = ($out[$#out]);
+  $#out -= 2;
+  $#chunks -= 2;
+  $par =~ s/^\[[^\]]*\]//;	# Optional argument 4
+  return unless $expand =~ s/^\d+,\d+.\d+,\d+,//;
+  $environment{$last_newtheorem} = "par:unindent:puts;$expand. ,par";
+  &finish(2,1);
+}
+
+sub f_literal_no_length {
+  warn "Entering f_literal_no_length...\n" if $debug & $debug_flow;
+  # &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  record_forcelength($out[$#out], 0);
+  &finish(1,1);
+}
+
+sub f_discard {
+  warn "Entering f_discard...\n" if $debug & $debug_flow;
+  &finish_ignore($wait[$#level]);
+} 
+
+# Takes a number and a record, returns a centered record
+
+sub center {
+  local($len,$left)=(shift,0);
+        warn "Entering center, ll=$len, rec=$_[0]\n__ENDREC__\n" if $debug & $debug_flow;
+  #$_[0]; # bug in perl?
+  local($h1,$l1,$b1,$sp1,$str1)=split(/,/,$_[0],5);
+  $h1 || $h1++;
+  if (($left=$len-$l1)<=0) {return $_[0];}
+  local($LHS,$RHS);
+  $LHS = int($left/2); $RHS = $left - $LHS;
+  local($out,$first)=("",1);
+  for (split(/\n/,$str1,$h1)) {
+    if ($first) {$first=0;}
+    else {$out .= "\n";}
+    $out .= " " x $LHS. $_ . " " x $RHS;
+  } 
+  return "$h1,$len,$b1,0,$out";
+} 
+
+# Example of radical
+#<<'EOF';
+# +--+
+#\|12
+#EOF
+<<EOF;				# To hide HERE-DOC start above  from old CPerl
+EOF
+
+# Takes the last record, returns a record that contains it and forms 
+# radical block
+
+sub f_radical {
+  warn "Entering f_radical...\n" if $debug & $debug_flow; 
+  &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Radical of $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($h,$l,$b)=($out[$#out] =~ /^(\d+),(\d+),(\d+)/g);
+  $h || $h++;
+  local($out,$b1,$h1);
+  $out=&vStack(&string2record(("-" x $l)."+" ),$out[$#out]);
+  $b1=$b+1;
+  $h1=$h+1;
+  #$out =~ s/^(\d+,\d+,)(\d+)/\1$b1/;
+  &setbaseline($out,$b1);
+  $out[$#out]=&join("$h1,2,$b1,0, +\n" . (" |\n" x ($h-1)) . '\|',$out);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(1,1);
+} 
+
+# Takes the last two records, returns a record that contains them and forms 
+# fraction block
+
+sub arrow_fraction {
+  local($minlength, $l, $r, $flip) = @_;
+  warn "Entering arrow_fraction, minlength=$minlength, left=$l, right=$r...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  warn "Numer `$out[$#out-1]'\nDenom `$out[$#out]'\n__END__\n" if $debug & $debug_record;
+
+  local($l1,$l2)=(&length($out[$#out-1]),&length($out[$#out]));
+  local($len)=(($l1>$l2 ? $l1: $l2));
+  $len = $minlength if $len < $minlength;
+  local $line = $len <= 1? "-" : ($l . ("-" x ($len - 2)) . $r);
+
+  if ($flip) {
+    $out[$#out-1]=&vStack(&vStack(&center($len,$out[$#out]),
+                           &string2record($line)),
+                   &center($len,$out[$#out-1]));
+  } else {
+    $out[$#out-1]=&vStack(&vStack(&center($len,$out[$#out-1]),
+                           &string2record($line)),
+                   &center($len,$out[$#out]));
+  }
+  $#chunks--;
+  $#out--;
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+} 
+
+sub f_fraction {
+  arrow_fraction(1,"-","-");
+} 
+
+sub f_right_arrow {
+  arrow_fraction(3,"-",">",1);
+} 
+
+sub f_left_arrow {
+  arrow_fraction(3,"<","-",1);
+} 
+
+# Takes the last two records, returns a record that contains them and forms 
+# fraction block
+
+sub f_overset {
+  warn "Entering f_overset...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  warn "Over `$out[$#out-1]'\nBase `$out[$#out]'\n__END__\n" if $debug & $debug_record;
+  local($l1,$l2)=(&length($out[$#out-1]),&length($out[$#out]));
+  local($len)=(($l1>$l2 ? $l1: $l2));
+
+  local($b, $b1)=($out[$#out] =~ /^\d+,\d+,(\d+)/);
+  ($b1)=($out[$#out-1] =~ /^(\d+)/);
+  $b1 ||= 1;				# height=0 means 1!
+  $b += $b1;
+  $out[$#out-1]=&vStack(&center($len,$out[$#out-1]),           
+	                &center($len,$out[$#out]));                          
+  $#chunks--;
+  $#out--;
+  &setbaseline($out[$#out],$b);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+} 
+
+sub f_choose {
+  warn "Entering f_choose...\n" if $debug & $debug_flow;  
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  warn "Numer `$out[$#out-1]'\nDenom `$out[$#out]'\n__END__\n" if $debug & $debug_record;
+  local($l1,$l2)=(&length($out[$#out-1]),&length($out[$#out]));
+  local($len)=(($l1>$l2 ? $l1: $l2));
+  $out[$#out]=&vStack(&vStack(&center($len,$out[$#out-1]),                 
+                         &string2record(" " x $len)),           
+                 &center($len,$out[$#out]));                          
+  $#chunks++;
+  $#out++;
+  #warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  $out[$#out - 2] = &string2record("(");
+  $out[$#out] = &string2record(")");
+  local($h,$b)=($out[$#out-1] =~ /^(\d+),\d+,(\d+)/)[0,1];
+  &makehigh($out[$#out-2],$h,$b,0,1);
+  &makehigh($out[$#out],$h,$b,1,0);
+  &finish(2,1);
+}
+
+
+sub f_buildrel {
+        warn "Entering f_buildrel...\n" if $debug & $debug_flow;
+  &trim(3);
+        &collapse(3);
+        &assertHave(3) || &finish("",1);
+        warn "What: $out[$#out-2]\nOver $out[$#out]\n__END__\n" if $debug & $debug_record;
+        local($rec)=($out[$#out-2]);
+        $out[$#out-2]=$out[$#out];
+        $#chunks-=2;
+        $#out-=2;
+  &f_putover($rec,1);
+        warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+        &finish(3,1);
+}
+
+# Takes two records, returns a record that contains them and forms a
+# fraction block
+
+sub fraction {
+  local($l1,$l2)=(&length($_[0]),&length($_[1]));
+  local($len)=(($l1>$l2 ? $l1: $l2));
+  return &vStack(&vStack(&center($len,shift),                 
+                         &string2record("-" x $len)),           
+                 &center($len,shift));                          
+} 
+
+# Commits a given string
+
+sub puts {
+  &commit(&string2record);
+} 
+
+# digests an eaten paragraph
+
+$ending = 0;
+
+sub end_TeX {
+  $par = '';
+  $ending = 1;
+}
+
+sub paragraph {
+  local($par);
+  return 0 if $ending;
+  $par=<>;
+  return 0 unless defined $par;
+  return 1 unless $par =~ /\S/;			# whitespace only
+  print "\n" if $secondtime++ && !$opt_by_par;
+  #$par =~ s/(^|[^\\])%.*\n[ \t]*/\1/g;
+  $par =~ s/((^|[^\\])(\\\\)*)(%.*\n[ \t]*)+/\1/g;
+  $par =~ s/\n\s*\n/\\par /g;
+  $par =~ s/\s+/ /g;
+  $par =~ s/\s+$//;
+  $par =~ s/(\$\$)\s+/\1/g;
+  $par =~ s/\\par\s*$//;
+  local($defcount,$piece,$pure,$type,$sub, at t,$arg)=(0);
+  &commit("1,5,0,0,     ")
+    unless $opt_noindent || ($par =~ s/^\s*\\noindent\s*([^a-zA-Z\s]|$)/\1/);
+  while ($tokenByToken[$#level] ?
+      ($par =~ s/^\s*($tokenpattern)//o): ($par =~ s/^($multitokenpattern)//o)) {
+    warn "tokenByToken=$tokenByToken[$#level], eaten=`$1'\n"  
+        if $debug & $debug_parsing;
+    if (($piece=$1) =~ /^$usualtokenclass/o) {
+      # plain piece
+      &puts($piece);
+    } else {
+      # macro or delimiter
+      ($pure = $piece);
+      if ($pure =~ /^\\\s+$/) {
+        $pure =~ s/(\\\s)\s+$/$1/;
+      } else {
+        $pure =~ s/\s+$//;
+      }
+      if (defined ($type=$type{$pure})) {
+        if ($type eq "def") {
+          warn "To many def expansions in a paragraph" if $defcount++==$maxdef;
+          last if $defcount>$maxdef;
+          @t=(0);
+          for (1..$args{$pure}) {
+            push(@t,&get_balanced());
+          }
+          warn "Defined token `$pure' found with $args{$pure} arguments @t[1..$#t]\n"
+          if $debug & $debug_parsing;
+          $sub=$def{$pure};
+          $sub =~ s/(^|[^\\#])#(\d)/$1 . $t[$2]/ge if $args{$pure};
+          $par=$sub . $par;
+        } elsif ($type eq "sub") {
+          $sub=$contents{$pure};
+          index($sub,";")>=0?
+            (($sub,$arg)=split(";",$sub,2), &$sub($pure,$arg)):
+              &$sub($pure);
+        } elsif ($type =~ /^sub(\d+)$/) {
+	  local($n) = $1;
+          $sub=$contents{$pure};	  
+	  if ($sub =~ s/^\[\]//) {
+            if ($par =~ /^\[/) {
+              $par =~ s/^\[([^\]]*)\]/{$1}/;
+            } else {
+              substr($par, 0, 0) = "{}";
+            }
+          }
+          &start($n,"f_$sub");
+          $tokenByToken[$#level]=1;
+        } elsif ($type =~ /^get(\d+)$/) {
+          &start($1+1);
+          &puts($piece);
+          $tokenByToken[$#level]=1;
+        } elsif ($type =~ /^discard(\d+)$/) {
+          &start($1,"f_discard");
+          $tokenByToken[$#level]=1;
+        } elsif ($type eq "record") {
+          &commit($contents{$pure});
+        } elsif ($type eq "self") {
+          &puts(substr($pure,1) . ($pure =~ /^\\[a-zA-Z]/ ? " ": ""));
+        } elsif ($type eq "par_self") {
+          &finishBuffer;
+          &commit("1,5,0,0,     ");
+                &puts($pure . ($pure =~ /^\\[a-zA-Z]/ ? " ": ""));
+        } elsif ($type eq "self_par") {
+          &puts($pure . ($pure =~ /^\\[a-zA-Z]/ ? " ": ""));
+          &finishBuffer;
+          &commit("1,5,0,0,     ")
+            unless $par =~ s/^\s*\\noindent(\s+|([^a-zA-Z\s])|$)/\2/;
+        } elsif ($type eq "string") {
+          &puts($contents{$pure},1);
+        } elsif ($type eq "nothing") {
+        } else {
+          warn "Error with type `$type' while interpreting `$pure'";
+        } 
+      } else {
+        &puts($piece);
+      } 
+    } 
+  }
+  warn "Unrecognized part of input `$par',\n\ttoken-by-token[$#level]=$tokenByToken[$#level]" 
+    if $par ne "";   
+  &finishBuffer if $#out>=0;
+# return 0 if eof();
+  1;
+} 
+
+sub subscript {
+  &start(1,"f_subscript");
+  $tokenByToken[$#level]=1;
+}
+
+sub superscript {
+  &start(1,"f_superscript");
+  $tokenByToken[$#level]=1;
+}
+
+
+sub f_subscript {
+  $wait[$#level]=2;
+  $action[$#level]="f_subSuper";
+  if (($par !~ s/^\s*\^//) &&
+      ($par !~ s:^\s*\\begin\s*\{Sp\}:\\begin\{matrix\}:)) {
+    &commit(&empty);
+  }   
+} 
+
+sub f_overline {
+  warn "Entering f_overline...\n" if $debug & $debug_flow;  
+  &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Overlining $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($h,$len,$b)=($out[$#out] =~ /^(\d+),(\d+),(\d+)/);
+  $out[$#out]=&vStack(&string2record("_" x $len),
+                      $out[$#out]);
+  $b++;
+  #$out[$#out] =~ s/^(\d+,\d+,)(\d+)/\1$b/;
+  &setbaseline($out[$#out],$b);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(1,1);
+} 
+
+sub f_underline {
+  warn "Entering f_underline...\n" if $debug & $debug_flow; 
+  &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Underlining $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($h,$len,$b)=($out[$#out] =~ /^(\d+),(\d+),(\d+)/);
+  $out[$#out]=&vStack($out[$#out],&string2record("_" x $len));
+  #$out[$#out] =~ s/^(\d+,\d+,)(\d+)/\1$b/;
+  &setbaseline($out[$#out],$b);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(1,1);
+} 
+
+sub f_not {
+  warn "Entering f_not...\n" if $debug & $debug_flow;  
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Negating $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($str)=(split(/,/,$out[$#out]))[4];
+  if ($str eq "=") {
+    $out[$#out]=$contents{"\\neq"};
+  } elsif ($str =~ /^\s*\|\s*$/) {
+    $out[$#out]=$contents{"\\nmid"};
+  } elsif ($out[$#out] eq $contents{"\\in"}) {
+    $out[$#out]=$contents{"\\notin"};
+  } else {
+    $out[$#out]=&join(&string2record("\\not"),$out[$#out]);
+  }
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(1,1);
+} 
+
+sub f_putunder {
+  warn "Entering f_putunder...\n" if $debug & $debug_flow;
+  &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Putting Under $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($h,$len,$b)=($out[$#out] =~ /^(\d+),(\d+),(\d+)/);
+        local($l2)=(&length($_[0]));
+        local($len)=(($l1>$l2 ? $l1: $l2));
+  $out[$#out]=&vStack(&center($len,$out[$#out]),&center($len,shift));
+  #$out[$#out] =~ s/^(\d+,\d+,)(\d+)/\1$b/;
+  &setbaseline($out[$#out],$b);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(1,1);
+}
+
+# if has additional true argument will not finish
+# Takes record to put over
+
+sub f_putover {
+  warn "Entering f_putover...\n" if $debug & $debug_flow; 
+  &trim(1);
+  &collapse(1);
+  &assertHave(1) || &finish("",1);
+  warn "Putting Over $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($h,$l1,$b,$b1)=($out[$#out] =~ /^(\d+),(\d+),(\d+)/);
+        local($l2)=(&length($_[0]));
+        local($len)=(($l1>$l2 ? $l1: $l2));
+  ($b1)=($_[0] =~ /^(\d+)/);
+  $b1 ||= 1;				# height=0 means 1!
+  $b+=$b1;
+  $out[$#out]=&vStack(&center($len,shift),&center($len,$out[$#out]));
+  #$out[$#out] =~ s/^(\d+,\d+,)(\d+)/\1$b/;
+  &setbaseline($out[$#out],$b);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(1,1) unless shift;
+} 
+
+sub f_putpar {
+        warn "Entering f_putpar...\n" if $debug & $debug_flow;
+  &trim(1);
+  local($l,$r)=split(";",shift);
+        &collapse(1);
+        &assertHave(1) || &finish("",1);
+        warn "Putting Parentheses $out[$#out]\n__END__\n" if $debug & $debug_record;
+  $out[$#out]=&join(&string2record($l),
+      &join($out[$#out],&string2record($r)));
+  &finish(1);
+}
+
+sub f_putover_string {
+  &f_putover(&string2record);
+} 
+
+sub f_putunder_string {
+  &f_putunder(&string2record);
+} 
+
+sub f_widehat {
+        &trim(1);
+  &collapse(1);
+        local($l)=(&length($out[$#out]));
+        if ($l<=1) {&f_putover(&string2record("^"));}
+        else {&f_putover(&string2record("/" . "~" x ($l-2) . "\\"));}
+}
+
+sub f_widetilde {
+  &trim(1);
+  &collapse(1);
+  local($l,$l1)=(&length($out[$#out]));
+  if ($l<=1) {&f_putover(&string2record("~"));}
+  elsif ($l<=3) {&f_putover(&string2record("/\\/"));}
+  else {&f_putover(&string2record("/" . "~" x ($l1=int($l/2-1)) .
+     "\\" . "_" x ($l-3-$l1) . "/"));}
+}
+
+sub f_underbrace {
+  &trim(1);
+  &collapse(1);
+  local($l,$l1)=(&length($out[$#out]));
+  if ($l < 5) { $l = 5 }
+  &f_putunder(vStack(&string2record("\\" . "_" x ($l1=int($l/2-1)) . " "
+				    . "_" x ($l-3-$l1) . "/"),
+		     &string2record(" " x ($l1=int($l/2)) . "v"
+				    . " " x ($l-2-$l1))));
+}
+
+sub f_underbrace2 {
+  &trim(2);
+  &collapse(2);
+  local($l,$l1)=(&length($out[$#out - 1]));
+  if ($l < 5) { $l = 5 }
+  local($b)=($out[$#out - 1] =~ /^\d+,\d+,(\d+)/);
+  $out[$#out-1]
+    = &vStack(&center($l,$out[$#out-1]),
+	      vStack(vStack(&string2record("\\" . "_" x ($l1=int($l/2-1)) . " "
+					   . "_" x ($l-3-$l1) . "/"),
+			    &string2record(" " x ($l1=int($l/2)) . "v"
+					   . " " x ($l-2-$l1))),
+		     &center($l,$out[$#out])));
+  $#chunks--;
+  $#out--;
+  &setbaseline($out[$#out],$b);
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+}
+
+sub f_overbrace {
+  &trim(1);
+  &collapse(1);
+  local($l)=(&length($out[$#out]));
+  if ($l < 6) { $l = 6 }
+  local($l1) = int($l/2);
+  &f_putover(vStack(&string2record(" " . "_" x ($l1 - 2) . "/\\"
+				    . "_" x ($l-2-$l1) . " "),
+		    &string2record("/" . " " x ($l1 - 2) . "  "
+				    . " " x ($l-2-$l1) . "\\"),
+		    ));
+}
+
+sub f_overbrace2 {
+  &trim(2);
+  &collapse(2);
+  local($l)=(&length($out[$#out - 1]));
+  if ($l < 5) { $l = 5 }
+  local($h,$b)=($out[$#out - 1] =~ /^(\d+),\d+,(\d+)/);
+  $h ||= 1;
+  local($l1) = int($l/2);
+  $out[$#out-1]
+    = &vStack(vStack(&center($l,$out[$#out]),
+		     vStack(&string2record(" " . "_" x ($l1 - 2) . "/\\"
+					   . "_" x ($l-2-$l1) . " "),
+			    &string2record("/" . " " x ($l1 - 2) . "  "
+					   . " " x ($l-2-$l1) . "\\"))),
+	      &center($l,$out[$#out-1]));
+  $#chunks--;
+  $#out--;
+  local($h1) = ($out[$#out] =~ /^(\d+),/);
+  &setbaseline($out[$#out], $h1 - ($h - $b));
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+}
+
+sub f_superscript {
+  $wait[$#level]=2;
+  $action[$#level]="f_superSub";
+  if (($par !~ s/^\s*\_//) &&
+      ($par !~ s:^\s*\\begin\s*\{Sb\}:\\begin\{matrix\}:)) {
+    &commit(&empty);
+  }   
+}
+
+sub let {
+        $par =~ s/^($tokenpattern)(= ?)?($tokenpattern)//o;
+}
+
+sub let_exp {
+        $par =~ s/^($tokenpattern)(= ?)?($tokenpattern)//o;
+  return if index($&,'@')>=0;
+  local($what)=$1;
+  $type{$what}='def';
+  $& =~ /($tokenpattern)$/;
+  $def{$what}=$1;
+  $args{$what}=0;
+        warn "Definition of `$what' with $args{$what} args is `$def{$what}'\n"
+                        if $debug & $debug_parsing;
+}
+
+
+sub def {
+  $par =~ s/^[^{]*//;
+  &start(1,"f_discard");
+  $tokenByToken[$#level]=1;
+}
+
+sub def_exp {
+  return unless $par =~ s:^(([^\\{]|\\.)*)\{:\{:;
+  local($arg)=($1);
+  local($def,$act)=(&get_balanced());
+  return unless defined $def;
+  return if index("$arg$def",'@')>=0;
+  return if $def =~ /\\([egx]?def|fi)([^a-zA-Z]|$)/;
+  $def .= " "  if $def =~ /($macro)$/o;
+  &define($arg,$def);
+}
+
+sub def_exp_block {
+  return unless $par =~ s:\{(\\([a-zA-Z]+|.))\}\{:\{:;
+  local($arg)=($1);
+  local($def,$act)=(&get_balanced());
+  return unless defined $def;
+  return if index("$arg$def",'@')>=0;
+  return if $def =~ /\\([egx]?def|fi)([^a-zA-Z]|$)/;
+  $def .= " "  if $def =~ /($macro)$/o;
+  &define($arg,$def);
+}
+
+# Arguments: Token . Parameters, Expansion
+
+sub define {
+  local($arg,$def,$act)=(shift,shift);
+  return unless $arg =~ /^($active)/o;
+  $act=$1;
+  $args{$act}=$';
+  return unless $args{$act} =~ /^(#\d)*$/;
+  $args{$act}=length($args{$act})/2;
+  $def{$act}=$def;
+  $type{$act}='def';
+  warn "Definition of `$act' with $args{$act} args is `$def'\n"
+      if $debug & $debug_parsing;
+}
+
+sub defb {
+  for (@_) {
+    &define("\\$_","\\begin{$_}");&define("\\end$_","\\endTeXtoMail{$_}");
+  }
+}
+
+# Discards surrounding {}
+
+sub get_balanced {
+        return undef unless $par =~ s/^($tokenpattern)//;
+  return $1 unless $1 eq '{';
+        local($def,$lev)=('',1);
+        while ($lev) {
+                last unless $par =~ s/^[^\\{}]|\\.|[{}]//;
+                $lev++ if $& eq '{';
+                $lev-- if $& eq '}';
+                $def .= $& if $lev;
+        }
+        (warn "Balanced text not finished!",return undef) if $lev;
+        return $def;
+}
+
+
+sub open_curly {
+  #&puts("{") unless $tokenByToken[$#level];
+  &start("}");
+}
+
+# Deletes extra spaces at the end of a record
+
+sub trim_end {
+  local($h,$str)=(split(/,/,$_[0],5))[0,4];
+  if (!$h) {
+    $str =~ s/\s+$//;
+    $_[0]=&string2record($str);
+    warn "Trimmed End `$_[0]'\n__END__\n" if $debug & $debug_record;
+  }
+}
+
+# Deletes extra spaces at the beginning of a record
+
+sub trim_beg {
+  local($h,$str)=(split(/,/,$_[0],5))[0,4];
+  if (!$h) {
+    $str =~ s/^\s+//;
+    $_[0]=&string2record($str);
+    warn "Trimmed Beg `$_[0]'\n__END__\n" if $debug & $debug_record;
+  }
+}
+
+# Deletes extra spaces at the ends of a chunk with given number
+
+sub trim_one {
+  &trim_beg($out[$chunks[$_[0]]]);
+  &trim_end($_[0]==$#chunks? $out[$#out]: $out[$chunks[$_[0]+1]-1]);
+}
+
+# Deletes extra spaces at the ends of a given number of chunks
+
+sub trim {
+  for ($#chunks-$_[0]+1..$#chunks) {&trim_one($_);}
+}
+
+sub dollar {
+  if ($wait[$#level] eq '$') {        # ';
+    &trim_end($out[$#out]);
+    &finish('$');
+  }
+  else {
+    &start('$');
+    $par =~ s/^\s+//;
+  }
+} 
+
+sub ddollar {
+  if ($wait[$#level] eq '$$') {
+    &trim_end($out[$#out]);
+    &finish('$$');
+    return unless $#out>=0;
+    $#chunks=0;
+    $chunks[0]=0;
+    &trim(1);
+    &collapse(1);
+    &printrecord(&center($linelength,$out[0]));
+    @level=(0);
+    @chunks=(0);
+    @tokenByToken=(0);
+    @out=();
+    $curlength=0;
+    # Maybe after \begin{align}
+  }
+  else {
+    &finishBuffer;
+    &start('$$');
+  }
+  $par =~ s/^\s+//;
+}
+
+sub item {
+  &finishBuffer;
+  # To make unexpandable:
+  &commit("1,11,0,0,     (\@)   ");
+}
+
+sub bbackslash {
+  if ($wait[$#level] eq '$$') {
+    &ddollar();
+    &ddollar();
+  } elsif ($wait[$#level] eq 'endCell') {
+    return if $par =~ /^\s*\\end/;		# Ignore the last one
+    &finish('endCell', 1);
+    &trim(1);
+    &collapse(1);
+    &finish('endRow', 1);
+    &start('endRow');
+    &start('endCell');
+  } else {
+    #&puts(" \\\\ ");
+    ∥
+  }
+}
+
+sub ampersand {
+  if ($wait[$#level] eq 'endCell') {
+    &finish('endCell',1);
+    &trim(1);
+    &collapse(1);
+    &start('endCell');
+  } 
+}
+
+sub matrix {
+  &start('endMatrix');
+  &start('endRow');
+  &start('endCell');
+}
+
+sub endmatrix {
+  &finish('endCell',1);
+  &trim(1);
+  &collapse(1);
+  &finish('endRow',1);
+  # Now chunks correspond to rows of the matrix, records inside chunks to 
+  # Cells
+  &halign(split(";",shift));
+  &finish('endMatrix',1);
+}
+
+sub endmatrixArg {
+  my $aspec = pop(@argStack);
+  # Replace *{20}{c} by "c" x 20; same for omited {}.
+  $aspec =~ s/\*(?:\{(\d+)\}|([^{]))(?:\{(.*?)\}|([^{]))/
+	(defined($1) ? $1 : $2) x (defined($3) ? $3 : $4) /eg;
+  &endmatrix(join(";",($_[0],split("", $aspec))));
+}
+
+# Takes a matrix in the following form: chunks on the last level
+# are row of the matrix, records inside chunks are cells.
+# Puts the resulting matrix in the first record on the given level 
+# and truncates the rest
+
+# I'm trying to add parameters: 
+#	length to insert between columns
+#	Array of centering options one for a column (last one repeated if needed)
+#		Currently supported:	c for center
+#					r for right
+#					l for left
+
+sub halign {
+  local($explength)=(shift);
+  local(@c)=@_;
+  local($last,$le,$b,$h);
+  local(@w)=();
+  #warn "levels @level, chunks @chunks, records @out\n";
+  # Find metrics of cells
+  for $r (0..$#chunks-$level[$#level]) {
+    $last= ($r==$#chunks-$level[$#level]) ? $#out:
+                                            $chunks[$r+1+$level[$#level]]-1;
+  warn "Row $r: last column " . ($last-$chunks[$r+$level[$#level]]) ."\n"
+                                if $debug & $debug_matrix;
+    for $c (0..$last-$chunks[$r+$level[$#level]]) {
+      ($h,$le,$b)=
+                ($out[$chunks[$r+$level[$#level]]+$c] =~ /(\d+),(\d+),(\d+)/);
+        # Format is Height:Length:Baseline
+      $w[$c]=$le unless $w[$c]>$le;
+    }
+  }
+  # expand the height and depth
+  for $c (0..$#w-1) {$w[$c]+=$explength;}
+  # Extend the @c array by the last element or "c" if it is empty
+  @c=("c") x @w unless @c;
+  @c=(@c,($c[$#c]) x (@w- at c));
+  # Now expand the cells
+  warn "Widths of columns @w\n" if $debug & $debug_matrix;
+  for $r (0..$#chunks-$level[$#level]) {
+    $last= ($r==$#chunks-$level[$#level]) ? $#out: 
+                                            $chunks[$r+1+$level[$#level]]-1;
+    warn "Row $r: last column " . ($last-$chunks[$r+$level[$#level]]) ."\n"
+        if $debug & $debug_matrix;
+    for $c (0..$last-$chunks[$r+$level[$#level]]) {
+      if ($c[$c] eq "c") {
+        warn "Centering row $r col $c to width $w[$c]\n"
+            if $debug & $debug_matrix;
+        $out[$chunks[$r+$level[$#level]]+$c]=
+          &center($w[$c],$out[$chunks[$r+$level[$#level]]+$c]);
+      } elsif ($c[$c] eq "l") {
+        warn "Expanding row $r col $c to width $w[$c]\n"
+            if $debug & $debug_matrix;
+        $out[$chunks[$r+$level[$#level]]+$c]=
+          &join($out[$chunks[$r+$level[$#level]]+$c],
+                &string2record(" " x 
+                  ($w[$c] - &length($out[$chunks[$r+$level[$#level]]+$c]))));
+      } elsif ($c[$c] eq "r") {
+        warn "Expanding row $r col $c to width $w[$c] on the left\n"
+            if $debug & $debug_matrix;
+        $out[$chunks[$r+$level[$#level]]+$c]=
+          &join(&string2record(" " x
+                  ($w[$c]-$explength- 
+                       &length($out[$chunks[$r+$level[$#level]]+$c]))),
+                $out[$chunks[$r+$level[$#level]]+$c]);
+        $out[$chunks[$r+$level[$#level]]+$c]=
+          &join($out[$chunks[$r+$level[$#level]]+$c],
+                &string2record(" " x $explength));
+      } else {warn "Unknown centering option `$c[$c]' for halign";}
+    } 
+  }
+  # Now we creat rows
+  &collapseAll;
+  # And stack them vertically
+  for ($chunks[$level[$#level]]+1..$#out) {
+    $out[$chunks[$level[$#level]]]=&vStack($out[$chunks[$level[$#level]]],
+                                           $out[$_]);
+  }
+  &setbaseline($out[$chunks[$level[$#level]]],
+               int((&height($out[$chunks[$level[$#level]]])-1)/2));
+  $#chunks=$level[$#level];
+  $#out=$chunks[$level[$#level]];
+}
+
+sub close_curly {
+  &finish("}");
+  #&puts("}") unless $tokenByToken[$#level]; # well, this can change under our foot...
+} 
+
+sub at {
+  local($c,$first,$second,$t,$m)=($par =~ /^(.)/);
+  if ($c eq '@') {&puts('@');$par =~ s/^.//;}
+  elsif (index("<>AV",$c)>=0) {
+    $m="&" if ($wait[$#level] eq 'endCell');
+    $m="&&" if $m eq "&" && index("AV",$c)>=0;
+    &ampersand if $m eq "&";
+    $par =~ s/^.//;
+    $first=$second="";
+    while (($t=&get_balanced()) ne $c && defined $t) {
+      $first .= $t;
+    }
+    while (($t=&get_balanced()) ne $c && defined $t) {
+      $second .= $t;
+    }
+    $par="{$first}{$second}$m" . $par;
+    local($l,$r);
+    ($l=$c) =~ tr/A>V/^/d;
+    ($r=$c) =~ tr/<A//d;
+    index("<>",$c)>=0 ?
+       &start(2,"f_arrow;$l;$r"):
+       &start(2,"f_arrow_v;$l;$r");
+  }
+  elsif ($c eq "." && $wait[$#level] eq 'endCell') {
+    &ersand;
+    &ersand;
+    $par =~ s/^.//;
+  }
+  else {&puts('@');}
+}
+
+# takes two tips of arrow as argument separated by ";",
+# we assume that total length is 1
+
+sub f_arrow {
+  warn "Entering f_arrow...\n" if $debug & $debug_flow;
+  local($l,$r)=split(";",shift);
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  warn "Over: $out[$#out-1]\nUnder: $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($l1,$l2)=(&length($out[$#out-1]),&length($out[$#out]));
+  local($len)=(($l1>$l2 ? $l1: $l2));
+  $out[$#out-1]=&vStack(&vStack(&center($len+4,$out[$#out-1]),
+                         &string2record(" $l" ."-" x ($len+1) . "$r ")),
+                 &center($len+4,$out[$#out]));
+  $#chunks--;
+  $#out--;
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+}
+
+# takes two tips of arrow as argument separated by ";",
+# we assume that total length is 1
+
+sub f_arrow_v {
+  warn "Entering f_arrow_v...\n" if $debug & $debug_flow;
+  local($l,$r)=split(";",shift);
+  &trim(2);
+  &collapse(2);
+  &assertHave(2) || &finish("",1);
+  warn "Over: $out[$#out-1]\nUnder: $out[$#out]\n__END__\n" if $debug & $debug_record;
+  local($h1,$b1)=($out[$#out-1] =~ /^(\d+),\d+,(\d+)/);
+  local($h2,$b2)=($out[$#out] =~ /^(\d+),\d+,(\d+)/);
+  local($b)=(($b1>$b2 ? $b1: $b2));
+  local($res)=(&join($out[$#out-1],$out[$#out]));
+  local($h,$bb)=($res =~ /^(\d+),\d+,(\d+)/);
+  $bb=$b+1;
+  $out[$#out-1]=&vStack(&vputs(" " x ($b-$b1+1)),
+                        $out[$#out-1]);
+  #$out[$#out-1] =~ s/^(\d+,\d+,)(\d+)/\1$bb/;
+  &setbaseline($out[$#out-1],$bb);
+  $out[$#out]=&vStack(&vputs(" " x ($b-$b2+1)),
+                                     $out[$#out]);
+  #$out[$#out] =~ s/^(\d+,\d+,)(\d+)/\1$bb/;
+  &setbaseline($out[$#out],$bb);
+  $out[$#out-1]=&join(&join($out[$#out-1],
+                         &vputs($l ."|" x ($h+1) . $r,$b+1)),
+                      $out[$#out]);
+  $#chunks--;
+  $#out--;
+  warn "a:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+  &finish(2,1);
+}
+
+sub noindent {
+  if ($#out == 0 && $#chunks == 0 && $out[$#out] eq '1,5,0,0,     ') {
+    $#out--;
+    $#chunks--;
+  } else {
+    &puts('\\noindent');
+  }
+}
+
+# put strings vertically, returns a record with the second argument as baseline 
+
+sub vputs {
+  local($b)=($_[1]);
+  $b=0 unless defined $b;
+  return length($_[0]) . ",1,$b,0," . join("\n",split('',$_[0]));
+}
+
+sub choose {
+  if ($wait[$#level] eq '}') {
+    local($prevw)=($wait[$#level-1]);
+    $wait[$#level-1]="junk";
+    &finish("}",1);
+          &collapse(1);
+          &assertHave(1) || &finish("",1);
+    local($rec)=$out[$#out];
+    $#out--;
+    $#chunks--;
+    &start(2,"f_choose");
+    $wait[$#level-1]=$prevw;
+    &start("}");
+    &commit($rec);
+    &finish("}",1);
+    &start("}");
+  } else {&puts("\\choose");}
+}
+
+sub over {
+  if ($wait[$#level] eq '}') {
+    local($prevw)=($wait[$#level-1]);
+    $wait[$#level-1]="junk";
+    &finish("}", 1);
+    &collapse(1);
+    warn "X:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+    &assertHave(1) || &finish("",1);
+    warn "Y:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+    local($rec)=$out[$#out];
+    $#out--;
+    $#chunks--;
+    warn "Z:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+    &start(2,"f_fraction");
+    $wait[$#level-1]=$prevw;
+    &start("}");
+    &commit($rec);
+    &finish("}",1);
+    &start("}");
+  } else {&puts("\\over");}
+}
+
+# Takes a record, height, baseline, spaces_toleft and _toright
+# and makes this record this high
+
+sub makehigh {
+  local($str)=(split(",",$_[0],5))[4];
+  local($h,$b,$d)=($_[1],$_[2]+1);
+  warn "Entering makehigh(@_)\n" if $debug & $debug_flow;
+  if ($str eq ".") {$_[0] =~ s/\.$/ /;return;}
+  #$str="<" if $str eq "\\langle";
+  #$str=">" if $str eq "\\rangle";
+  $h=1 unless $h;
+  $d=$h-$b;
+  return if $h<2 || $h==2 && index("()<>",$str)>=0;
+  local(@c);
+  if    ($str eq "(") {@c=split(":",'(: :|:/:\:|');}
+  elsif ($str eq ")") {@c=split(":",'): :|:\:/:|');}
+  elsif ($str eq "{") {@c=split(":",'{: :|:/:\:<');}
+  elsif ($str eq "}") {@c=split(":",'}: :|:\:/:>');}
+  elsif ($str eq "|" && $str eq "||") 
+                      {@c=split(":",'|:|:|:|:|:|');}
+  elsif ($str eq "[") {@c=split(":",'[:[:|:[:[:|');}
+  elsif ($str eq "]") {@c=split(":",']:]:|:]:]:|');}
+  elsif ($str eq "<" || $str eq ">") {
+    return if $h==2;
+    local($l)=($b);
+    $l=$d+1 if $b<$d+1;
+    for (2..$l) {
+      $_[0]=&join($_[0],
+                  &vputs("/" . " " x (2*$_-3) . "\\",$_-1)) if $str eq "<";
+      $_[0]=&join(&vputs("\\" . " " x (2*$_-3) . "/",$_-1),
+                  $_[0]) if $str eq ">";
+    }
+    $_[0]=&join($_[0],&string2record(" ")) if $str eq "<";
+    $_[0]=&join(&string2record(" "),$_[0]) if $str eq ">";
+    return;
+  }
+  else {return;}
+  $_[0]=&vputs(&makecompound($b,$d, at c),$b-1);
+  $_[0]=&join($_[0],$_[0]) if length($str)==2;
+  $_[0]=&join(&string2record(" " x $_[3]),$_[0]) if $_[3];
+  $_[0]=&join($_[0],&string2record(" " x $_[4])) if $_[4];
+}
+
+sub f_FOOTNOTE {
+  &finish(1);
+  puts(">>> ");
+}
+
+sub footnote {
+  puts("<<<FOOTNOTE: ");
+  &start(1, "f_FOOTNOTE");
+  $tokenByToken[$#level]=1;
+}
+
+sub right {
+  close_curly();
+  &finish("LeftRight",1);
+  &trim(1);
+  &collapse(1);
+}
+
+sub f_left {
+  &trim(1);
+  &collapse(1);
+  &finish(1);
+  &start("LeftRight");
+  open_curly();			# Fake a {}-group for \over etc.
+}
+
+sub left {
+  &start(3,"f_leftright");
+  $tokenByToken[$#level]=1;
+  &start(1,"f_left");
+  $tokenByToken[$#level]=1;
+}
+
+sub f_leftright_go {
+  &trim(1);
+  &collapse(1);
+  local($l,$r)=split(";",shift);
+  &assertHave(1) || warn "Left-Right not balanced";
+  local($rec)=($out[$#out]);
+  $#out--;
+  $wait[$#level]="junk";
+  &start(3,"f_leftright");
+  &puts($l);
+  &commit($rec);
+  &puts($r);
+  &finish("junk");
+}
+
+# Used to put \left SOMETHING \right SOMETHING around a group
+sub beg_lr {
+  &start(1,"f_leftright_go" . ";" . shift);
+  $tokenByToken[$#level]=1;
+}
+
+sub f_leftright {
+  &trim(1);
+  &collapse(1);
+  &assertHave(3) || warn "Left-Right not balanced";
+  local($h,$b)=($out[$#out-1] =~ /^(\d+),\d+,(\d+)/)[0,1];
+  &makehigh($out[$#out-2],$h,$b,0,1);
+  &makehigh($out[$#out],$h,$b,1,0);
+  &finish(3);
+}
+
+sub today {
+  &puts(scalar localtime);
+}
+
+sub unindent {
+# a:Last chunk number 0, last record 0, the first chunk
+#  on the last level=0 is 0, waiting for
+#    warn "B:Last $#chunks, the first on the last level=$#level is $level[$#level]" if $debug & $debug_flow;
+   # Silently ignore failures (XXXX sometimes $#chunks is 0, sometimes 1?!)
+   unless ($#chunks <= 1 && $#level == 0 && $level[$#level] == 0
+           && $#out == 0 && $out[0] eq "1,5,0,0,     " && $curlength == 5) {
+     return;
+   }
+   $out[0] = "1,0,0,0,";
+   $curlength = 0;
+}
+
+sub puts_unindent {
+  unindent();
+  puts($_[1]);
+}
+
+sub par_puts_unindent {
+  par();
+  unindent();
+  puts($_[1]);
+}
+
+# Arguments: Ascent, descent, base string, oneside expander, real expander
+#            0       1        2            3                 4
+#            Top tip, Bottom tip, Mid
+#            5        6           7
+# All component should be one character long
+
+sub makecompound {
+  # If Mid is the same as real expander, all depends on the height only
+  # if there is extend on both sides
+  # If it is 3 or more
+  if ($_[0]>1 && $_[1]>0 && $_[4] eq $_[7]) {
+    return $_[5] . $_[4] x ($_[0]+$_[1]-2) . $_[6];
+  }
+  # No descent:
+  if ($_[1] <= 0) {return $_[3] x ($_[0]-1) . $_[2];}
+  # No ascent:
+  if ($_[0] <= 1) {return $_[2] . $_[3] x $_[1];}
+  local($mid,$asc,$des)=($_[2]);
+  # descent == 1
+  $des = ($_[1]==1) ? $_[2]: $_[4] x ($_[1]-1) . $_[6];
+  $asc  = ($_[0]==2) ? $_[2]: $_[5] . $_[4] x ($_[0]-2);
+  $mid = $_[7] unless $_[0]==2 || $_[1]==1;
+  return "$asc$mid$des";
+}
+
+sub arg2stack {push(@argStack,&get_balanced());}
+
+sub par {&finishBuffer;&commit("1,5,0,0,     ")
+	   unless $par =~ s/^\s*\\noindent\s*(\s+|([^a-zA-Z\s])|$)/\2/;}
+
+$type{"\\sum"}="record";
+$contents{"\\sum"}="3,4,1,0," . <<'EOF';
+\~~   
+ >
+/__ 
+EOF
+
+$type{"\\int"}="record";
+$contents{"\\int"}="3,3,1,0," . <<'EOF';
+ ,-
+ |
+-'
+EOF
+
+$type{"\\prod"}="record";
+$contents{"\\prod"}="3,3,1,0," . <<'EOF';
+___
+| |
+| |
+EOF
+
+$type{"\\Pi"}="record";
+$contents{"\\Pi"}="2,3,1,0," . <<'EOF';
+ _
+| |
+EOF
+
+$type{"\\Sigma"}="record";
+$contents{"\\Sigma"}="3,2,1,0," . <<'EOF';
+__
+>
+~~
+EOF
+
+$type{"\\Delta"}="record";
+$contents{"\\Delta"}="2,2,0,0," . <<'EOF';
+/\
+~~
+EOF
+
+$type{"\\nabla"}="record";
+$contents{"\\nable"}="2,2,1,0," . <<'EOF';
+__
+\/
+EOF
+
+$type{"\\oplus"}="record";
+$contents{"\\oplus"}="3,5,1,0," . <<'EOF';
+  _ 
+ (+)
+  ~
+EOF
+
+$type{"\\otimes"}="record";
+$contents{"\\otimes"}="3,5,1,0," . <<'EOF';
+  _ 
+ (x)
+  ~
+EOF
+
+$type{"\\ominus"}="record";
+$contents{"\\ominus"}="3,5,1,0," . <<'EOF';
+  _ 
+ (-)
+  ~
+EOF
+
+$type{"\\odot"}="record";
+$contents{"\\odot"}="3,5,1,0," . <<'EOF';
+  _ 
+ (.)
+  ~
+EOF
+
+$type{"\\leq"}="record";
+$contents{"\\leq"}="2,3,1,0," . <<'EOF';
+ _
+ <
+EOF
+
+$type{"\\equiv"}="record";
+$contents{"\\equiv"}="2,3,1,0," . <<'EOF';
+ _
+ =
+EOF
+
+$type{"\\not"}="record";
+$contents{"\\not"}="2,4,1,0," . <<'EOF';
+ _
+  |
+EOF
+
+$type{"\\geq"}="record";
+$contents{"\\geq"}="2,3,1,0," . <<'EOF';
+ _
+ >
+EOF
+
+$type{"\\partial"}="record";
+$contents{"\\partial"}="2,1,1,0," . <<'EOF';
+\
+d
+EOF
+
+$type{"\\forall"}="record";
+$contents{"\\forall"}="3,4,1,0," . <<'EOF';
+\__/
+ \/
+EOF
+
+$type{"\\exists"}="record";
+$contents{"\\exists"}="3,2,1,0," . <<'EOF';
+_.
+-|
+~'
+EOF
+
+$type{"\\owns"}="record";
+$contents{"\\owns"}="3,4,1,0," . <<'EOF';
+ _
+ -)
+ ~
+EOF
+
+$type{"\\ni"}="record";
+$contents{"\\ni"}="3,4,1,0," . <<'EOF';
+ _
+ -)
+ ~
+EOF
+
+$type{"\\in"}="record";
+$contents{"\\in"}="3,4,1,0," . <<'EOF';
+  _
+ (-
+  ~
+EOF
+
+$type{"\\notin"}="record";
+$contents{"\\notin"}="3,5,1,0," . <<'EOF';
+  |_
+ (|-
+  |~
+EOF
+
+$type{"\\qed"}="record";
+$contents{"\\qed"}="2,6,1,0," . <<'EOF';
+    _
+   |_| 
+EOF
+
+$type{"\\Box"}="record";
+$contents{"\\Box"}="2,5,1,0," . <<'EOF';
+  _
+ |_| 
+EOF
+
+$type{"\\boxtimes"}="record";
+$contents{"\\boxtimes"}="3,5,1,0," . <<'EOF';
+  _
+ |X| 
+  ~
+EOF
+
+$type{"\\pm"}="record";
+$contents{"\\pm"}="2,1,0,0," . <<'EOF';
++
+-
+EOF
+
+$type{"\\mp"}="record";
+$contents{"\\mp"}="2,1,1,0," . <<'EOF';
+_
++
+EOF
+
+$type{"\\cong"}="record";
+$contents{"\\cong"}="2,1,0,0," . <<'EOF';
+=
+~
+EOF
+
+$type{"\\neq"}="record";
+$contents{"\\neq"}="1,5,0,0," . <<'EOF';
+ =/= 
+EOF
+
+$type{"\\nmid"}="record";
+$contents{"\\nmid"}="3,3,1,0," . <<'EOF';
+ |/
+ |
+/|
+EOF
+
+$type{"\\subset"}="record";
+$contents{"\\subset"}="2,4,1,0," . <<'EOF';
+  _ 
+ (_ 
+EOF
+
+$type{"\\subseteq"}="record";
+$contents{"\\subseteq"}="3,4,1,0," . <<'EOF';
+  _ 
+ (_ 
+  ~
+EOF
+
+$type{"\\supseteq"}="record";
+$contents{"\\subseteq"}="3,4,1,0," . <<'EOF';
+ _ 
+ _) 
+ ~
+EOF
+
+$type{"\\supset"}="record";
+$contents{"\\supset"}="2,4,1,0," . <<'EOF';
+ _
+ _)
+EOF
+
+$type{"\\sqrt"}="sub1";
+$contents{"\\sqrt"}="radical";
+
+$type{"\\buildrel"}="sub3";
+$contents{"\\buildrel"}="buildrel";
+
+$type{"\\frac"}="sub2";
+$contents{"\\frac"}="fraction";
+  
+$type{"\\xrightarrow"}="sub2";
+$contents{"\\xrightarrow"}="[]right_arrow";
+  
+$type{"\\xleftarrow"}="sub2";
+$contents{"\\xleftarrow"}="[]left_arrow";
+  
+$type{"\\overset"}="sub2";
+$contents{"\\overset"}="overset";
+
+$type{"\\LITERALnoLENGTH"}="sub1";
+$contents{"\\LITERALnoLENGTH"}="literal_no_length";
+
+for ("text","operatorname","operatornamewithlimits","relax","-",
+     "notag","!","/","protect","mathcal","Bbb","bf","it","em","boldsymbol",
+     "cal","Cal","goth","ref","maketitle","expandafter","csname","endcsname",
+     "makeatletter","makeatother","topmatter","endtopmatter","rm", "tt",
+     "NoBlackBoxes","document","TagsOnRight","bold","dsize","roster",
+     "endroster","endkey","endRefs","enddocument","displaystyle",
+     "twelverm","tenrm","twelvefm","tenfm","hbox","mbox", "url",
+     "tableofcontents", "bigl", "bigr", "biggl", "biggr", "footnotesize",
+     "nonumber", "tiny", "sc", "huge", "limits",
+    ) {
+  $type{"\\$_"}="nothing";
+}
+
+if ($opt_ignorefonts) {
+  for ("mathbb", "mathit", "mathbit", "mathfrak", "frak", "mathbf",
+      ) {
+    $type{"\\$_"}="nothing";
+  }
+}
+
+for ("par","endtitle","endauthor","endaffil","endaddress","endemail","enddate",
+     "endhead","key","medskip","smallskip","bigskip","newpage",
+     "vfill","eject") {
+  $type{"\\$_"}="sub";
+  $contents{"\\$_"}="par";
+}
+
+for ("proclaim","demo",) {
+  $type{"\\$_"}="par_self";
+}
+
+for ("endproclaim","enddemo",) {
+  $type{"\\$_"}="self_par";
+}
+
+$type{"\\today"}="sub";
+$contents{"\\today"}="today";
+
+#$type{"&"}="nothing";
+
+$type{"\\let"}="sub";
+$contents{"\\let"}="let_exp";
+
+$type{"\\def"}="sub";
+$contents{"\\def"}="def_exp";
+
+$type{"\\newcommand"}="sub";
+$contents{"\\newcommand"}="def_exp_block";
+
+$type{"\\item"}="sub";
+$contents{"\\item"}="item";
+
+$type{"{"}="sub";
+$contents{"{"}="open_curly";
+
+$type{"}"}="sub";
+$contents{"}"}="close_curly";
+
+$type{"&"}="sub";
+$contents{"&"}="ampersand";
+
+$type{'$'}="sub";
+$contents{'$'}="dollar";
+
+$type{'$$'}="sub";
+$contents{'$$'}="ddollar";
+
+$type{'\\\\'}="sub";
+$contents{'\\\\'}="bbackslash";
+
+$type{"^"}="sub1";
+$contents{"^"}="superscript";
+
+$type{"_"}="sub1";
+$contents{"_"}="subscript";
+
+$type{"@"}="sub";
+$contents{"@"}="at";
+
+$type{"\\over"}="sub";
+$contents{"\\over"}="over";
+
+
+$type{"\\choose"}="sub";
+$contents{"\\choose"}="choose";
+
+$type{"\\noindent"}="sub";
+$contents{"\\noindent"}="noindent";
+
+
+$type{"\\left"}="sub";
+$contents{"\\left"}="left";
+
+$type{"\\right"}="sub";
+$contents{"\\right"}="right";
+
+$type{'\footnote'}="sub";
+$contents{'\footnote'}="footnote";
+
+$type{"\\underline"}="sub1";
+$contents{"\\underline"}="underline";
+
+$type{"\\overline"}="sub1";
+$contents{"\\overline"}="overline";
+
+$type{"\\bar"}="sub1";
+$contents{"\\bar"}="overline";
+
+$type{"\\v"}="sub1";
+$contents{"\\v"}="putover_string;v";
+
+$type{"\\widetilde"}="sub1";
+$contents{"\\widetilde"}="widetilde";
+
+$type{"\\underbrace"}="sub2";
+$contents{"\\underbrace"}="underbrace2";
+
+$type{"\\overbrace"}="sub2";
+$contents{"\\overbrace"}="overbrace2";
+
+$type{"\\~"}="sub1";
+$contents{"\\~"}="putover_string;~";
+
+$type{"\\tilde"}="sub1";
+$contents{"\\tilde"}="putover_string;~";
+
+$type{"\\widehat"}="sub1";
+$contents{"\\widehat"}="widehat";
+
+$type{"\\hat"}="sub1";
+$contents{"\\hat"}="putover_string;^";
+
+$type{"\\^"}="sub1";
+$contents{"\\^"}="putover_string;^";
+
+$type{'\\"'}="sub1";
+$contents{'\\"'}='putover_string;"';
+
+$type{'\\dot'}="sub1";
+$contents{'\\dot'}='putover_string;.';
+
+$type{"\\not"}="sub1";
+$contents{"\\not"}="not";
+
+$type{"\\label"}="sub1";
+$contents{"\\label"}="putpar;(;)";
+
+$type{"\\eqref"}="sub1";
+$contents{"\\eqref"}="putpar;(;)";
+
+$type{"\\cite"}="sub1";
+$contents{"\\cite"}="putpar;[;]";
+
+$type{"\\begin"}="sub1";
+$contents{"\\begin"}="begin";
+
+for ('@',"_","\$","{","}","#","&","arccos","arcsin","arctan","arg","cos",
+    "cosh","cot","coth","csc","deg","det","dim","exp","gcd","hom",
+    "inf","ker","lg","lim","liminf","limsup","ln","log","max","min",
+    "mod","Pr","sec","sin","sinh","sup","tan","tanh", "%") {
+  $type{"\\$_"}="self";
+}
+
+for ("bibliography","myLabel","theoremstyle","theorembodyfont", "usepackage",
+     "bibliographystyle","hphantom","vphantom","phantom","hspace") {
+  $type{"\\$_"}="discard1";
+}
+
+for ("numberwithin","renewcommand","setcounter"
+    ) {
+  $type{"\\$_"}="discard2";
+}
+
+$type{"\\newenvironment"}="discard3";
+$type{"\\DeclareMathAccent"}="discard4";
+$type{"\\DeclareMathAlphabet"}="discard5";
+
+#$type{"\\newtheorem"}="sub2";
+#$contents{"\\newtheorem"}="newtheorem";
+
+$type{"\\NEWTHEOREMone"}="sub1";
+$contents{"\\NEWTHEOREMone"}="newtheorem1";
+
+$type{"\\NEWTHEOREMtwo"}="sub2";
+$contents{"\\NEWTHEOREMtwo"}="[]newtheorem2";
+
+for ("equation","gather","align"
+     ) {$environment{"$_"}="ddollar,ddollar";} 
+
+for ("matrix","CD","smallmatrix"
+     ) {$environment{"$_"}="matrix,endmatrix;1;c";} 
+
+for ("remark", "example", "theorem", "definition", "proof", "abstract",
+     "proposition", "amplification", "lemma", "corollary",
+     ) {$environment{"$_"}="par:unindent:puts;\U$_\E. ,par";} 
+
+# \thanks inside \author confuse things a lot
+for ("title", "author", "affil", "address", "email", "date", # "thanks",
+     "section", "subsection", "subsubsection") {
+  $type{"\\$_"}="sub";
+  $contents{"\\$_"}="par_puts_unindent;\U$_: ";
+}
+
+for ("document","split","enumerate"
+     ) {$environment_none{"$_"}++;} 
+
+$environment{"Sb"}="subscript:matrix,endmatrix;1;l";
+
+$environment{"Sp"}="superscript:matrix,endmatrix;1;l";
+
+$environment{"eqnarray"}="ddollar:matrix,endmatrix;0;r;c;l:ddollar";
+$environment{"eqnarray*"}="ddollar:matrix,endmatrix;0;r;c;l:ddollar";
+$environment{"split"}="ddollar:matrix,endmatrix;0;r;l:ddollar";
+$environment{"multiline"}="ddollar:matrix,endmatrix;0;r;l:ddollar";
+$environment{"align"}="ddollar:matrix,endmatrix;0;r;l:ddollar";
+$environment{"aligned"}="matrix,endmatrix;0;r;l";
+$environment{"gather"}="ddollar:matrix,endmatrix;0;c:ddollar";
+$environment{"gathered"}="matrix,endmatrix;0;c";
+$environment{"array"}="arg2stack:matrix,endmatrixArg;1";
+
+# $environment{"pmatrix"}="beg_lr;(;):matrix,endmatrix;1;c";
+$environment{"bmatrix"}="beg_lr;[;]:matrix,endmatrix;1;c";
+$environment{"vmatrix"}="beg_lr;|;|:matrix,endmatrix;1;c";
+
+$type{"~"}="string";
+$contents{"~"}=" ";
+
+$type{"\\,"}="string";
+$contents{"\\,"}=" ";
+
+$type{"\\dots"}="string";
+$contents{"\\dots"}="...";
+
+$type{"\\and"}="string";
+$contents{"\\and"}=" & ";
+
+$type{"\\ldots"}="string";
+$contents{"\\ldots"}="...";
+
+$type{"\\cdots"}="string";
+$contents{"\\cdots"}="...";
+
+$type{"\\colon"}="string";
+$contents{"\\colon"}=": ";
+
+$type{"\\mid"}="string";
+$contents{"\\mid"}=" | ";
+
+$type{"\\smallsetminus"}="string";
+$contents{"\\smallsetminus"}=" \\ ";
+
+$type{"\\setminus"}="string";
+$contents{"\\setminus"}=" \\ ";
+
+$type{"\\backslash"}="string";
+$contents{"\\backslash"}="\\";
+
+$type{"\\iff"}="string";
+$contents{"\\iff"}=" <==> ";
+
+$type{"\\approx"}="string";
+$contents{"\\approx"}=" ~ ";
+
+$type{"\\simeq"}="string";
+$contents{"\\simeq"}=" ~ ";
+
+$type{"\\quad"}="string";
+$contents{"\\quad"}="   ";
+
+$type{"\\qquad"}="string";
+$contents{"\\qquad"}="     ";
+
+$type{"\\to"}="string";
+$contents{"\\to"}=" --> ";
+
+$type{"\\from"}="string";
+$contents{"\\from"}=" <-- ";
+
+$type{"\\wedge"}="string";
+$contents{"\\wedge"}="/\\";
+
+$type{"\\vee"}="string";
+$contents{"\\vee"}="\\/";
+
+$type{"\\Lambda"}="string";
+$contents{"\\Lambda"}="/\\";
+
+$type{"\\ltimes"}="string";
+$contents{"\\ltimes"}=" |>< ";
+
+$type{"\\lhd"}="string";
+$contents{"\\lhd"}=" <| ";
+
+$type{"\\rhd"}="string";
+$contents{"\\rhd"}=" |> ";
+
+$type{"\\cdot"}="string";
+$contents{"\\cdot"}=" . ";
+
+# $type{"\dot"}="string";
+# $contents{"\\dot"}=" . ";
+
+$type{"\\circ"}="string";
+$contents{"\\circ"}=" o ";
+
+$type{"\\bullet"}="string";
+$contents{"\\bullet"}="\@";
+
+$type{"\\infty"}="string";
+$contents{"\\infty"}="oo";
+
+$type{"\\rtimes"}="string";
+$contents{"\\rtimes"}=" ><| ";
+
+$type{"\\times"}="string";
+$contents{"\\times"}=" >< ";
+
+$type{"\\gg"}="string";
+$contents{"\\gg"}=" >> ";
+
+$type{"\\ll"}="string";
+$contents{"\\ll"}=" << ";
+
+$type{"\\hookrightarrow"}="string";
+$contents{"\\hookrightarrow"}=" c--> ";
+
+$type{"\\hookleftarrow"}="string";
+$contents{"\\hookleftarrow"}=" <--j ";
+
+$type{"\\longleftarrow"}="string";
+$contents{"\\longleftarrow"}=" <----- ";
+
+$type{"\\longleftrightarrow"}="string";
+$contents{"\\longleftrightarrow"}=" <----> ";
+
+$type{"\\longrightarrow"}="string";
+$contents{"\\longrightarrow"}=" -----> ";
+
+$type{"\\rightarrow"}="string";
+$contents{"\\rightarrow"}=" ---> ";
+
+$type{"\\leftarrow"}="string";
+$contents{"\\leftarrow"}=" <--- ";
+
+$type{"\\mapsto"}="string";
+$contents{"\\mapsto"}=" |--> ";
+
+$type{"\\longmapsto"}="string";
+$contents{"\\longmapsto"}=" |----> ";
+
+$type{"\\cap"}="string";
+$contents{"\\cap"}=" /~\\ ";
+
+$type{"\\cup"}="string";
+$contents{"\\cup"}=" \\_/ ";
+
+$type{"\|"}="string";
+$contents{"\|"}="||";
+
+$type{'\;'}="string";
+$contents{'\;'}=" ";
+
+$type{'\ '}="string";
+$contents{'\ '}=" ";
+
+$type{'\noindent'}="string";
+$contents{'\noindent'}="";
+
+$type{'\enspace'}="string";
+$contents{'\enspace'}="  ";
+
+$type{"\\thanks"}="string";
+$contents{"\\thanks"}=" THANKS: ";
+
+$type{"\\vert"}="string";
+$contents{"\\vert"}=" | ";
+
+$type{"\\Vert"}="string";
+$contents{"\\Vert"}=" || ";
+
+$type{"\\ast"}="string";
+$contents{"\\ast"}=" * ";
+
+$type{"\\prime"}="string";
+$contents{"\\prime"}="'";
+
+$type{"\\endgraph"}="string";
+$contents{"\\endgraph"}=" \\NEWPAR ";
+
+&define('\\define','\\def');
+&define('\\ge','\\geq ');
+&define('\\le','\\leq ');
+&define('\\ne','\\neq ');
+&define('\\langle','<');
+&define('\\rangle','>');
+&define('\\subheading','\\par\\underline ');
+&define('\\(','$');
+&define('\\)','$');
+&define('\\[','$$');
+&define('\\]','$$');
+&define('\\centerline#1','$$#1$$');
+&define('\\eqalign#1','\\aligned #1 \\endaligned ');
+&define('\\eqalignno#1','\\aligned #1 \\endaligned ');
+&define('\\cr','\\\\');
+&define('\\sb','_');
+&define('\\sp','^');
+&define('\\iint','\int\int ');
+&define('\\iiint','\int\int\int ');
+&define('\\proclaim','\\noindent ');
+&define('\\mathring','\\overset{\circ}');
+&define('\\binom#1#2','{#1\choose{}#2}');
+&define('\\bibitem#1','[#1]');
+&define('\\newline','\par ');
+&define('\\newtheorem#1','\\NEWTHEOREMone{#1}\\NEWTHEOREMtwo ');
+&defb("matrix","vmatrix","Vmatrix","smallmatrix","bmatrix","Sp","Sb",
+      "CD","align","aligned","split","multiline","gather","gathered");
+
+for ("documentclass", "documentstyle") {
+  $type{"\\$_"}="sub2";
+  $contents{"\\$_"}="[]discard";
+}
+
+# works as \end in LaTeX
+$type{"\\endTeXtoMail"}="sub1";
+$contents{"\\endTeXtoMail"}="end";
+
+if ($opt_TeX) {
+  &define('\pmatrix#1','\left(\begin{matrix}#1\endTeXtoMail{matrix}\right)');
+  $type{"\\end"}="sub";
+  $contents{"\\end"}="end_TeX";
+} else {
+  $environment{"pmatrix"}="beg_lr;(;):matrix,endmatrix;1;c";
+  &defb("pmatrix") unless $opt_TeX;
+  $type{"\\end"}="sub1";
+  $contents{"\\end"}="end";
+}
+
+  
+  ## All the records should be specified before this point
+  {local(@a)=grep("record" eq $type{$_},keys %type); 
+		for (@a) {chop $contents{$_} if 
+      substr($contents{$_},length($contents{$_})-1,1) eq "\n";}}
+
+for ("oplus","otimes","cup","wedge") {
+  $type{"\\big$_"}=$type{"\\$_"};
+  $contents{"\\big$_"}=$contents{"\\$_"};
+}
+
+
+ at level=(0);
+ at chunks=(0);
+ at tokenByToken=(0);
+ at out=();
+$curlength=0;
+$debug_flow=1;
+$debug_record=2;
+$debug_parsing=4;
+$debug_length=8;
+$debug_matrix=16;
+#$debug |= $debug_flow | $debug_record | $debug_parsing | $debug_length;
+#$debug |= $debug_flow;
+#$debug |= $debug_record;
+#$debug |= $debug_parsing;
+#$debug |= $debug_length;
+#$debug |= $debug_matrix;
+
+
+$/ = $opt_by_par ? "\n\n" : ''; # whole paragraph mode
+while (&paragraph()) { 1 }
+&finishBuffer;
+
+__END__
+
+# History: Jul 98: \choose added, fixed RE for \noindent, \eqalign and \cr.
+#			\proclaim and better \noindent added.
+# Sep 98: last was used inside an if block, was leaking out.
+# Jan 00: \sb \sp
+# Feb 00: remove extraneous second EOF needed at end.
+	  remove an empty line at end of output
+	  New option -by_par to support per-paragraph processing
+	  New option -TeX which support a different \pmatrix
+	  New option -ragged to not insert whitespace to align right margin.
+	  New option -noindent to not insert whitespace at beginning.
+	  Ignore \\ and \cr if followed by \end{whatever}.
+	  Ignore \noindent if not important.
+	  Ignore whitespace paragraphs.
+# Apr 00: Finishing a level 1 would not merge things into one chunk.
+# May 00: Additional argument to finish() to distinguish finishing
+	  things which cannot be broken between lines.
+# Sep 00: Add support for new macro for strings with screen escapes sequences:
+	  \LITERALnoLENGTH{escapeseq}.
+# Oct 00: \LITERALnoLENGTH can have a chance to work in the baseline only;
+	   in fact the previous version did not work even there...
+	  If the added record is longer than line length, do not try to
+	  break the line before it...
+# Apr 03: new option ignorefonts
+	  Hanging `}' (e.g., caused by translation of \\ to \par) would break
+	    processing of the rest of the file.
+	  Could not treat fraction as the first thing on the line (\noindent
+	    required).
+	  Support many more macros.
+	  Better support for \end (with -TeX), \footnote, \endgraph.
+	  Allow \over and other infix macros inside \left(\right).
+	  \label would not update $curlength.
+	  Whitespace edits.
+	  Update the limit of expansion of definitions to 10000 per paragraph
+	  (a recursion guard - used, e.g., per TeX's \pmatrix).
+	  New option scissors to emit a line when a high line is cut into
+	    pieces (disabled when "").
+	  New option noflush (rarily useful optimization).
+	  Flush the output by default.
+	  \limits ignored.
+	  Allow one-level-deep *{NUMBER}{PRE} in array specifier (in addition
+		to c,r,l).
diff --git a/misc/xgp b/misc/xgp
new file mode 100644
index 0000000..640e394
--- /dev/null
+++ b/misc/xgp
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# A simple-minded script to launch gp in an xterm. The application name is
+# set to "gp". You can use it to have specific X resources, to tell your
+# window manager to use the icon pari.xbm, etc.
+#
+# set correct paths below
+xterm="xterm"
+gp="/usr/local/bin/gp"
+
+$xterm -geometry 80x40 -sl 2000 -sb \
+       -name gp -title PARI/GP -rw -e $gp &
diff --git a/src/basemath/F2x.c b/src/basemath/F2x.c
new file mode 100644
index 0000000..0e523f5
--- /dev/null
+++ b/src/basemath/F2x.c
@@ -0,0 +1,1672 @@
+/* Copyright (C) 2007  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with polynomials over F_2 */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                             F2x                                   **/
+/**                                                                   **/
+/***********************************************************************/
+/* F2x objects are defined as follows:
+   An F2x is a t_VECSMALL:
+   x[0] = codeword
+   x[1] = evalvarn(variable number)  (signe is not stored).
+   x[2] = a_0...a_31 x[3] = a_32..a_63, etc.  on 32bit
+   x[2] = a_0...a_63 x[3] = a_64..a_127, etc. on 64bit
+
+   where the a_i are bits.
+
+   signe(x) is not valid. Use lgpol(x)!=0 instead.
+   Note: pol0_F2x=pol0_Flx and pol1_F2x=pol1_Flx
+*/
+
+INLINE long
+F2x_degree_lg(GEN x, long l)
+{
+  return (l==2)?-1:bit_accuracy(l)-bfffo(x[l-1])-1;
+}
+
+long
+F2x_degree(GEN x)
+{
+  return F2x_degree_lg(x, lg(x));
+}
+
+GEN
+F2x_to_ZX(GEN x)
+{
+  long l=3+F2x_degree(x);
+  GEN z=cgetg(l,t_POL);
+  long i,j,k;
+  for(i=2,k=2;i<lg(x);i++)
+    for(j=0; j<BITS_IN_LONG && k<l; j++,k++)
+      gel(z,k)=(x[i]&(1UL<<j))?gen_1:gen_0;
+  z[1]=evalsigne(l>=3)|x[1];
+  return z;
+}
+
+GEN
+F2xC_to_ZXC(GEN v)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_COL);
+  for (j=1; j<N; j++) gel(y,j) = F2x_to_ZX(gel(v,j));
+  return y;
+}
+
+GEN
+F2x_to_Flx(GEN x)
+{
+  long l=3+F2x_degree(x);
+  GEN z=cgetg(l,t_VECSMALL);
+  long i,j,k;
+  z[1]=x[1];
+  for(i=2,k=2;i<lg(x);i++)
+    for(j=0;j<BITS_IN_LONG && k<l; j++,k++)
+      z[k]=(x[i]>>j)&1UL;
+  return z;
+}
+
+GEN
+Z_to_F2x(GEN x, long v)
+{
+  long sv = evalvarn(v);
+  return mpodd(x) ? pol1_F2x(sv): pol0_F2x(sv);
+}
+
+GEN
+ZX_to_F2x(GEN x)
+{
+  long l=nbits2lg(lgpol(x));
+  GEN z=cgetg(l,t_VECSMALL);
+  long i,j,k;
+  z[1]=((ulong)x[1])&VARNBITS;
+  for(i=2, k=1,j=BITS_IN_LONG;i<lg(x);i++,j++)
+  {
+    if (j==BITS_IN_LONG)
+    {
+      j=0; k++; z[k]=0;
+    }
+    if (mpodd(gel(x,i)))
+      z[k]|=1UL<<j;
+  }
+  return F2x_renormalize(z,l);
+}
+
+GEN
+Flx_to_F2x(GEN x)
+{
+  long l=nbits2lg(lgpol(x));
+  GEN z=cgetg(l,t_VECSMALL);
+  long i,j,k;
+  z[1]=x[1];
+  for(i=2, k=1,j=BITS_IN_LONG;i<lg(x);i++,j++)
+  {
+    if (j==BITS_IN_LONG)
+    {
+      j=0; k++; z[k]=0;
+    }
+    if (x[i]&1UL)
+      z[k]|=1UL<<j;
+  }
+  return F2x_renormalize(z,l);
+}
+
+GEN
+F2x_to_F2v(GEN x, long N)
+{
+  long i, l = lg(x);
+  long n = nbits2lg(N);
+  GEN z = cgetg(n,t_VECSMALL);
+  z[1] = N;
+  for (i=2; i<l ; i++) z[i]=x[i];
+  for (   ; i<n; i++) z[i]=0;
+  return z;
+}
+
+GEN
+RgX_to_F2x(GEN x)
+{
+  long l=nbits2lg(lgpol(x));
+  GEN z=cgetg(l,t_VECSMALL);
+  long i,j,k;
+  z[1]=((ulong)x[1])&VARNBITS;
+  for(i=2, k=1,j=BITS_IN_LONG;i<lg(x);i++,j++)
+  {
+    if (j==BITS_IN_LONG)
+    {
+      j=0; k++; z[k]=0;
+    }
+    if (Rg_to_F2(gel(x,i)))
+      z[k]|=1UL<<j;
+  }
+  return F2x_renormalize(z,l);
+}
+
+/* If x is a POLMOD, assume modulus is a multiple of T. */
+GEN
+Rg_to_F2xq(GEN x, GEN T)
+{
+  long ta, tx = typ(x), v = T[1];
+  GEN a, b;
+  if (is_const_t(tx))
+  {
+    if (tx == t_FFELT) return FF_to_F2xq(x);
+    return Rg_to_F2(x)? pol1_F2x(v): pol0_F2x(v);
+  }
+  switch(tx)
+  {
+    case t_POLMOD:
+      b = gel(x,1);
+      a = gel(x,2); ta = typ(a);
+      if (is_const_t(ta)) return Rg_to_F2(a)? pol1_F2x(v): pol0_F2x(v);
+      b = RgX_to_F2x(b); if (b[1] != v) break;
+      a = RgX_to_F2x(a); if (F2x_equal(b,T)) return a;
+      return F2x_rem(a, T);
+    case t_POL:
+      x = RgX_to_F2x(x);
+      if (x[1] != v) break;
+      return F2x_rem(x, T);
+    case t_RFRAC:
+      a = Rg_to_F2xq(gel(x,1), T);
+      b = Rg_to_F2xq(gel(x,2), T);
+      return F2xq_div(a,b, T);
+  }
+  pari_err_TYPE("Rg_to_F2xq",x);
+  return NULL; /* not reached */
+}
+
+GEN
+F2x_add(GEN x, GEN y)
+{
+  long i,lz;
+  GEN z;
+  long lx=lg(x);
+  long ly=lg(y);
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx; z = cgetg(lz, t_VECSMALL); z[1]=x[1];
+  for (i=2; i<ly; i++) z[i] = x[i]^y[i];
+  for (   ; i<lx; i++) z[i] = x[i];
+  return F2x_renormalize(z, lz);
+}
+
+static GEN
+F2x_addspec(GEN x, GEN y, long lx, long ly)
+{
+  long i,lz;
+  GEN z;
+
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx+2; z = cgetg(lz, t_VECSMALL) + 2;
+  for (i=0; i<ly; i++) z[i] = x[i]^y[i];
+  for (   ; i<lx; i++) z[i] = x[i];
+  z -= 2; return F2x_renormalize(z, lz);
+}
+
+GEN
+F2x_1_add(GEN y)
+{
+  GEN z;
+  long lz, i;
+  if (!lgpol(y))
+    return pol1_F2x(y[1]);
+  lz=lg(y);
+  z=cgetg(lz,t_VECSMALL);
+  z[1] = y[1];
+  z[2] = y[2]^1UL;
+  for(i=3;i<lz;i++)
+    z[i] = y[i];
+  if (lz==3) z = F2x_renormalize(z,lz);
+  return z;
+}
+
+static GEN
+F2x_addshift(GEN x, GEN y, long d)
+{
+  GEN xd,yd,zd = (GEN)avma;
+  long a,lz,ny = lgpol(y), nx = lgpol(x);
+  long vs = x[1];
+
+  x += 2; y += 2; a = ny-d;
+  if (a <= 0)
+  {
+    lz = (a>nx)? ny+2: nx+d+2;
+    (void)new_chunk(lz); xd = x+nx; yd = y+ny;
+    while (xd > x) *--zd = *--xd;
+    x = zd + a;
+    while (zd > x) *--zd = 0;
+  }
+  else
+  {
+    xd = new_chunk(d); yd = y+d;
+    x = F2x_addspec(x,yd,nx,a);
+    lz = (a>nx)? ny+2: lg(x)+d;
+    x += 2; while (xd > x) *--zd = *--xd;
+  }
+  while (yd > y) *--zd = *--yd;
+  *--zd = vs;
+  *--zd = evaltyp(t_VECSMALL) | evallg(lz); return zd;
+}
+
+/* shift polynomial + gerepile */
+/* Do not set evalvarn. Cf Flx_shiftip */
+static GEN
+F2x_shiftip(pari_sp av, GEN x, long v)
+{
+  long i, lx = lg(x), ly;
+  GEN y;
+  if (!v || lx==2) return gerepileuptoleaf(av, x);
+  ly = lx + v;
+  (void)new_chunk(ly); /* check that result fits */
+  x += lx; y = (GEN)av;
+  for (i = 2; i<lx; i++) *--y = *--x;
+  for (i = 0; i< v; i++) *--y = 0;
+  y -= 2; y[0] = evaltyp(t_VECSMALL) | evallg(ly);
+  avma = (pari_sp)y; return y;
+}
+
+static GEN
+F2x_mul1(ulong x, ulong y)
+{
+  ulong x1=(x&HIGHMASK)>>BITS_IN_HALFULONG;
+  ulong x2=x&LOWMASK;
+  ulong y1=(y&HIGHMASK)>>BITS_IN_HALFULONG;
+  ulong y2=y&LOWMASK;
+  ulong r1,r2,rr;
+  GEN z;
+  ulong i;
+  rr=r1=r2=0UL;
+  if (x2)
+    for(i=0;i<BITS_IN_HALFULONG;i++)
+      if (x2&(1UL<<i))
+      {
+        r2^=y2<<i;
+        rr^=y1<<i;
+      }
+  if (x1)
+    for(i=0;i<BITS_IN_HALFULONG;i++)
+      if (x1&(1UL<<i))
+      {
+        r1^=y1<<i;
+        rr^=y2<<i;
+      }
+  r2^=(rr&LOWMASK)<<BITS_IN_HALFULONG;
+  r1^=(rr&HIGHMASK)>>BITS_IN_HALFULONG;
+  z=cgetg((r1?4:3),t_VECSMALL);
+  z[2]=r2;
+  if (r1) z[3]=r1;
+  return z;
+}
+
+/* fast product (Karatsuba) of polynomials a,b. These are not real GENs, a+2,
+ * b+2 were sent instead. na, nb = number of terms of a, b.
+ * Only c, c0, c1, c2 are genuine GEN.
+ */
+static GEN
+F2x_mulspec(GEN a, GEN b, long na, long nb)
+{
+  GEN a0,c,c0;
+  long n0, n0a, i, v = 0;
+  pari_sp av;
+
+  while (na && !a[0]) { a++; na-=1; v++; }
+  while (nb && !b[0]) { b++; nb-=1; v++; }
+  if (na < nb) swapspec(a,b, na,nb);
+  if (!nb) return pol0_F2x(0);
+
+  av = avma;
+  if (na <=1) return F2x_shiftip(av,F2x_mul1(*a,*b),v);
+  i=(na>>1); n0=na-i; na=i;
+  a0=a+n0; n0a=n0;
+  while (n0a && !a[n0a-1]) n0a--;
+
+  if (nb > n0)
+  {
+    GEN b0,c1,c2;
+    long n0b;
+
+    nb -= n0; b0 = b+n0; n0b = n0;
+    while (n0b && !b[n0b-1]) n0b--;
+    c =  F2x_mulspec(a,b,n0a,n0b);
+    c0 = F2x_mulspec(a0,b0,na,nb);
+
+    c2 = F2x_addspec(a0,a,na,n0a);
+    c1 = F2x_addspec(b0,b,nb,n0b);
+
+    c1 = F2x_mul(c1,c2);
+    c2 = F2x_add(c0,c);
+
+    c2 = F2x_add(c1,c2);
+    c0 = F2x_addshift(c0,c2,n0);
+  }
+  else
+  {
+    c  = F2x_mulspec(a,b,n0a,nb);
+    c0 = F2x_mulspec(a0,b,na,nb);
+  }
+  c0 = F2x_addshift(c0,c,n0);
+  return F2x_shiftip(av,c0, v);
+}
+
+GEN
+F2x_mul(GEN x, GEN y)
+{
+  GEN z = F2x_mulspec(x+2,y+2, lgpol(x),lgpol(y));
+  z[1] = x[1]; return z;
+}
+
+GEN
+F2x_sqr(GEN x)
+{
+  const ulong sq[]={0,1,4,5,16,17,20,21,64,65,68,69,80,81,84,85};
+  long i,ii,j,jj;
+  long lx=lg(x), lz=2+((lx-2)<<1);
+  GEN z;
+  z = cgetg(lz, t_VECSMALL); z[1]=x[1];
+  for (j=2,jj=2;j<lx;j++,jj++)
+  {
+    ulong x1=((ulong)x[j]&HIGHMASK)>>BITS_IN_HALFULONG;
+    ulong x2=(ulong)x[j]&LOWMASK;
+    z[jj]=0;
+    if (x2)
+      for(i=0,ii=0;i<BITS_IN_HALFULONG;i+=4,ii+=8)
+        z[jj]|=sq[(x2>>i)&15UL]<<ii;
+    z[++jj]=0;
+    if (x1)
+      for(i=0,ii=0;i<BITS_IN_HALFULONG;i+=4,ii+=8)
+        z[jj]|=sq[(x1>>i)&15UL]<<ii;
+  }
+  return F2x_renormalize(z, lz);
+}
+
+static GEN
+F2x_pow2n(GEN x, long n)
+{
+  long i;
+  for(i=1;i<=n;i++)
+    x = F2x_sqr(x);
+  return x;
+}
+
+int
+F2x_issquare(GEN x)
+{
+  const ulong mask = (ULONG_MAX/3UL)*2;
+  ulong i, lx = lg(x);
+  for (i=2; i<lx; i++)
+    if ((x[i]&mask)) return 0;
+  return 1;
+}
+
+/* Assume x is a perfect square */
+GEN
+F2x_sqrt(GEN x)
+{
+  const ulong sq[]={0,1,4,5,2,3,6,7,8,9,12,13,10,11,14,15};
+  long i,ii,j,jj;
+  long lx=lg(x), lz=2+((lx-1)>>1);
+  GEN z;
+  z = cgetg(lz, t_VECSMALL); z[1]=x[1];
+  for (j=2,jj=2;jj<lz;j++,jj++)
+  {
+    ulong x2=x[j++];
+    z[jj]=0;
+    if (x2)
+      for(i=0,ii=0;ii<BITS_IN_HALFULONG;i+=8,ii+=4)
+      {
+        ulong rl = (x2>>i)&15UL, rh = (x2>>(i+4))&15UL;
+        z[jj]|=sq[rl|(rh<<1)]<<ii;
+      }
+    if (j<lx)
+    {
+      x2 = x[j];
+      if (x2)
+        for(i=0,ii=0;ii<BITS_IN_HALFULONG;i+=8,ii+=4)
+        {
+          ulong rl = (x2>>i)&15UL, rh = (x2>>(i+4))&15UL;
+          z[jj]|=(sq[rl|(rh<<1)]<<ii)<<BITS_IN_HALFULONG;
+        }
+    }
+  }
+  return F2x_renormalize(z, lz);
+}
+
+INLINE void
+F2x_addshiftip(GEN x, GEN y, ulong d)
+{
+  ulong db, dl=dvmduBIL(d, &db);
+  long i, ly = lg(y);
+  if (db)
+  {
+    ulong dc=BITS_IN_LONG-db;
+    ulong r=0;
+    for(i=2; i<ly; i++)
+    {
+      x[i+dl] ^= (((ulong)y[i])<<db)|r;
+      r = ((ulong)y[i])>>dc;
+    }
+    if (r) x[i+dl] ^= r;
+  }
+  else
+    for(i=2; i<ly; i++)
+      x[i+dl] ^= y[i];
+}
+
+static GEN
+F2x_shiftneg(GEN y, ulong d)
+{
+  long db, dl=dvmdsBIL(d, &db);
+  long i, ly = lg(y), lx = ly-dl;
+  GEN x = cgetg(lx, t_VECSMALL);
+  x[1] = y[1];
+  if (db)
+  {
+    ulong dc=BITS_IN_LONG-db;
+    ulong r=0;
+    for(i=lx-1; i>=2; i--)
+    {
+      x[i] = (((ulong)y[i+dl])>>db)|r;
+      r = ((ulong)y[i+dl])<<dc;
+    }
+  }
+  else
+    for(i=2; i<lx; i++)
+      x[i] = y[i+dl];
+  return F2x_renormalize(x,lx);
+}
+
+static GEN
+F2x_shiftpos(GEN y, ulong d)
+{
+  long db, dl=dvmdsBIL(d, &db);
+  long i, ly = lg(y), lx = ly+dl+(!!db);
+  GEN x = cgetg(lx, t_VECSMALL);
+  x[1] = y[1]; for(i=0; i<dl; i++) x[i+2] = 0;
+  if (db)
+  {
+    ulong dc=BITS_IN_LONG-db;
+    ulong r=0;
+    for(i=2; i<ly; i++)
+    {
+      x[i+dl] = (((ulong)y[i])<<db)|r;
+      r = ((ulong)y[i])>>dc;
+    }
+    x[i+dl] = r;
+  }
+  else
+    for(i=2; i<ly; i++)
+      x[i+dl] = y[i];
+  return F2x_renormalize(x,lx);
+}
+
+GEN
+F2x_shift(GEN y, long d)
+{
+  return d<0 ? F2x_shiftneg(y,-d): F2x_shiftpos(y,d);
+}
+
+/* separate from F2x_divrem for maximal speed. */
+GEN
+F2x_rem(GEN x, GEN y)
+{
+  long dx,dy;
+  long lx=lg(x);
+  dy = F2x_degree(y); if (!dy) return pol0_F2x(x[1]);
+  dx = F2x_degree_lg(x,lx);
+  x  = vecsmall_copy(x);
+  while (dx>=dy)
+  {
+    F2x_addshiftip(x,y,dx-dy);
+    while (lx>2 && x[lx-1]==0) lx--;
+    dx = F2x_degree_lg(x,lx);
+  }
+  return F2x_renormalize(x, lx);
+}
+
+GEN
+F2x_divrem(GEN x, GEN y, GEN *pr)
+{
+  long dx, dy, dz, lx = lg(x), vs = x[1];
+  GEN z;
+
+  dy = F2x_degree(y);
+  if (dy<0) pari_err_INV("F2x_divrem",y);
+  if (pr == ONLY_REM) return F2x_rem(x, y);
+  if (!dy)
+  {
+    z = vecsmall_copy(x);
+    if (pr && pr != ONLY_DIVIDES) *pr = pol0_F2x(vs);
+    return z;
+  }
+  dx = F2x_degree_lg(x,lx);
+  dz = dx-dy;
+  if (dz < 0)
+  {
+    if (pr == ONLY_DIVIDES) return dx < 0? vecsmall_copy(x): NULL;
+    z = pol0_F2x(vs);
+    if (pr) *pr = vecsmall_copy(x);
+    return z;
+  }
+  z = zero_zv(lg(x)-lg(y)+2); z[1] = vs;
+  x = vecsmall_copy(x);
+  while (dx>=dy)
+  {
+    F2x_set(z,dx-dy);
+    F2x_addshiftip(x,y,dx-dy);
+    while (lx>2 && x[lx-1]==0) lx--;
+    dx = F2x_degree_lg(x,lx);
+  }
+  z = F2x_renormalize(z, lg(z));
+  if (!pr) { cgiv(x); return z; }
+  x = F2x_renormalize(x, lx);
+  if (pr == ONLY_DIVIDES) {
+    if (lg(x) == 2) { cgiv(x); return z; }
+    avma = (pari_sp)(z + lg(z)); return NULL;
+  }
+  *pr = x; return z;
+}
+
+long
+F2x_valrem(GEN x, GEN *Z)
+{
+  long v, v2, i, l=lg(x);
+  GEN y;
+  if (l==2) { *Z = leafcopy(x); return LONG_MAX; }
+  for (i=2; i<l && x[i]==0; i++) /*empty*/;
+  v = i-2;
+  v2 = (i < l)? vals(x[i]): 0;
+  if (v+v2 == 0) { *Z = x; return 0; }
+  l -= i-2;
+  y = cgetg(l, t_VECSMALL); y[1] = x[1];
+  if (v2 == 0)
+    for (i=2; i<l; i++) y[i] = x[i+v];
+  else if (l == 3)
+    y[2] = ((ulong)x[2+v]) >> v2;
+  else
+  {
+    const ulong sh = BITS_IN_LONG - v2;
+    ulong r = x[2+v];
+    for (i=3; i<l; i++)
+    {
+      y[i-1] = (x[i+v] << sh) | (r >> v2);
+      r = x[i+v];
+    }
+    y[l-1] = r >> v2;
+    (void)F2x_renormalize(y,l);
+  }
+  *Z = y; return (v << TWOPOTBITS_IN_LONG) + v2;
+}
+
+GEN
+F2x_deflate(GEN x, long d)
+{
+  GEN y;
+  long i,id, dy, dx = F2x_degree(x);
+  if (d <= 1) return Flx_copy(x);
+  if (dx < 0) return leafcopy(x);
+  dy = dx/d; /* dy+1 coefficients + 1 extra word for variable */
+  y = zero_zv(nbits2lg(dy+1)-1); y[1] = x[1];
+  for (i=id=0; i<=dy; i++,id+=d)
+    if (F2x_coeff(x,id)) F2x_set(y, i);
+  return y;
+}
+
+/* write p(X) = e(X^2) + Xo(X^2), shallow function */
+void
+F2x_even_odd(GEN p, GEN *pe, GEN *po)
+{
+  long n = F2x_degree(p), n0, n1, i;
+  GEN p0, p1;
+
+  if (n <= 0) { *pe = leafcopy(p); *po = pol0_F2x(p[1]); return; }
+
+  n0 = (n>>1)+1; n1 = n+1 - n0; /* n1 <= n0 <= n1+1 */
+  p0 = zero_zv(nbits2lg(n0+1)-1); p0[1] = p[1];
+  p1 = zero_zv(nbits2lg(n1+1)-1); p1[1] = p[1];
+  for (i=0; i<n1; i++)
+  {
+    if (F2x_coeff(p,i<<1)) F2x_set(p0,i);
+    if (F2x_coeff(p,1+(i<<1))) F2x_set(p1,i);
+  }
+  if (n1 != n0 && F2x_coeff(p,i<<1)) F2x_set(p0,i);
+  *pe = F2x_renormalize(p0,lg(p0));
+  *po = F2x_renormalize(p1,lg(p1));
+}
+
+GEN
+F2x_deriv(GEN z)
+{
+  const ulong mask = ULONG_MAX/3UL;
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_VECSMALL); x[1] = z[1];
+  for (i=2; i<l; i++) x[i] = (((ulong)z[i])>>1)&mask;
+  return F2x_renormalize(x,l);
+}
+
+GEN
+F2x_gcd(GEN a, GEN b)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  if (lg(b) > lg(a)) swap(a, b);
+  while (lgpol(b))
+  {
+    GEN c = F2x_rem(a,b);
+    a = b; b = c;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"F2x_gcd (d = %ld)",F2x_degree(c));
+      gerepileall(av,2, &a,&b);
+    }
+  }
+  if (low_stack(lim,stack_lim(av,2))) a = gerepileuptoleaf(av, a);
+  return a;
+}
+
+GEN
+F2x_extgcd(GEN a, GEN b, GEN *ptu, GEN *ptv)
+{
+  pari_sp av=avma, lim = stack_lim(av,2);
+  GEN u,v,d,d1,v1;
+  long vx = a[1];
+  d = a; d1 = b;
+  v = pol0_F2x(vx); v1 = pol1_F2x(vx);
+  while (lgpol(d1))
+  {
+    GEN r, q = F2x_divrem(d,d1, &r);
+    v = F2x_add(v,F2x_mul(q,v1));
+    u=v; v=v1; v1=u;
+    u=r; d=d1; d1=u;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"F2x_extgcd (d = %ld)",F2x_degree(d));
+      gerepileall(av,5, &d,&d1,&u,&v,&v1);
+    }
+  }
+  if (ptu) *ptu = F2x_div(F2x_add(d, F2x_mul(b,v)), a);
+  *ptv = v;
+  if (low_stack(lim,stack_lim(av,2))) gerepileall(av,ptu?3:2,&d,ptv,ptu);
+  return d;
+}
+
+static GEN
+F2x_halfgcd_i(GEN a, GEN b)
+{
+  pari_sp av=avma, lim = stack_lim(av,2);
+  GEN u,u1,v,v1;
+  long vx = a[1];
+  long n = (F2x_degree(a)+1)>>1;
+  u1 = v = pol0_F2x(vx);
+  u = v1 = pol1_F2x(vx);
+  while (F2x_degree(b)>=n)
+  {
+    GEN r, q = F2x_divrem(a,b, &r);
+    a = b; b = r; swap(u,u1); swap(v,v1);
+    u1 = F2x_add(u1, F2x_mul(u, q));
+    v1 = F2x_add(v1, F2x_mul(v, q));
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"F2x_halfgcd (d = %ld)",F2x_degree(b));
+      gerepileall(av,6, &a,&b,&u1,&v1,&u,&v);
+    }
+  }
+  return gerepilecopy(av, mkmat2(mkcol2(u,u1), mkcol2(v,v1)));
+}
+
+GEN
+F2x_halfgcd(GEN x, GEN y)
+{
+  pari_sp av;
+  GEN M,q,r;
+  if (F2x_degree(y)<F2x_degree(x)) return F2x_halfgcd_i(x,y);
+  av = avma;
+  q = F2x_divrem(y,x,&r);
+  M = F2x_halfgcd_i(x,r);
+  gcoeff(M,1,1) = F2x_add(gcoeff(M,1,1), F2x_mul(q, gcoeff(M,1,2)));
+  gcoeff(M,2,1) = F2x_add(gcoeff(M,2,1), F2x_mul(q, gcoeff(M,2,2)));
+  return gerepilecopy(av, M);
+}
+
+GEN
+F2xq_mul(GEN x,GEN y,GEN pol)
+{
+  GEN z = F2x_mul(x,y);
+  return F2x_rem(z,pol);
+}
+
+GEN
+F2xq_sqr(GEN x,GEN pol)
+{
+  GEN z = F2x_sqr(x);
+  return F2x_rem(z,pol);
+}
+
+GEN
+F2xq_invsafe(GEN x, GEN T)
+{
+  GEN V, z = F2x_extgcd(T, x, NULL, &V);
+  if (F2x_degree(z)) return NULL;
+  return V;
+}
+
+GEN
+F2xq_inv(GEN x,GEN T)
+{
+  pari_sp av=avma;
+  GEN U = F2xq_invsafe(x, T);
+  if (!U) pari_err_INV("F2xq_inv",x);
+  return gerepileuptoleaf(av, U);
+}
+
+GEN
+F2xq_div(GEN x,GEN y,GEN T)
+{
+  pari_sp av = avma;
+  return gerepileuptoleaf(av, F2xq_mul(x,F2xq_inv(y,T),T));
+}
+
+static GEN
+_F2xq_red(void *E, GEN x)
+{ return F2x_rem(x, (GEN)E); }
+static GEN
+_F2xq_add(void *E, GEN x, GEN y)
+{ (void)E; return F2x_add(x,y); }
+
+static GEN
+_F2xq_cmul(void *E, GEN P, long a, GEN x)
+{
+  GEN pol = (GEN) E;
+  return F2x_coeff(P,a) ? x: pol0_F2x(pol[1]);
+}
+static GEN
+_F2xq_sqr(void *E, GEN x)
+{ return F2xq_sqr(x, (GEN) E); }
+
+static GEN
+_F2xq_mul(void *E, GEN x, GEN y)
+{ return F2xq_mul(x,y, (GEN) E); }
+
+static GEN
+_F2xq_one(void *E)
+{
+  GEN pol = (GEN) E;
+  return pol1_F2x(pol[1]);
+}
+static GEN
+_F2xq_zero(void *E)
+{
+  GEN pol = (GEN) E;
+  return pol0_F2x(pol[1]);
+}
+
+GEN
+F2xq_pow(GEN x, GEN n, GEN pol)
+{
+  pari_sp av=avma;
+  GEN y;
+
+  if (!signe(n)) return pol1_F2x(x[1]);
+  if (is_pm1(n)) /* +/- 1 */
+    return (signe(n) < 0)? F2xq_inv(x,pol): vecsmall_copy(x);
+
+  if (signe(n) < 0) x = F2xq_inv(x,pol);
+  y = gen_pow(x, n, (void*)pol, &_F2xq_sqr, &_F2xq_mul);
+  return gerepileupto(av, y);
+}
+
+GEN
+F2xq_powu(GEN x, ulong n, GEN pol)
+{
+  pari_sp av=avma;
+  GEN y;
+  switch(n)
+  {
+    case 0: return pol1_F2x(x[1]);
+    case 1: return vecsmall_copy(x);
+    case 2: return F2xq_sqr(x,pol);
+  }
+  y = gen_powu(x, n, (void*)pol, &_F2xq_sqr, &_F2xq_mul);
+  return gerepileupto(av, y);
+}
+
+/* generates the list of powers of x of degree 0,1,2,...,l*/
+GEN
+F2xq_powers(GEN x, long l, GEN T)
+{
+  int use_sqr = (F2x_degree(x)<<1) >= F2x_degree(T);
+  return gen_powers(x, l, use_sqr, (void*)T, &_F2xq_sqr, &_F2xq_mul, &_F2xq_one);
+}
+
+GEN
+F2xq_matrix_pow(GEN y, long n, long m, GEN P)
+{
+  return F2xV_to_F2m(F2xq_powers(y,m-1,P),n);
+}
+
+static struct bb_algebra F2xq_algebra = { _F2xq_red,_F2xq_add,_F2xq_mul,_F2xq_sqr,_F2xq_one,_F2xq_zero};
+
+GEN
+F2x_F2xqV_eval(GEN Q, GEN x, GEN T)
+{
+  long d = F2x_degree(Q);
+  return gen_bkeval_powers(Q,d,x,(void*)T,&F2xq_algebra,_F2xq_cmul);
+}
+
+GEN
+F2x_F2xq_eval(GEN Q, GEN x, GEN T)
+{
+  long d = F2x_degree(Q);
+  int use_sqr = (F2x_degree(x)<<1) >= F2x_degree(T);
+  return gen_bkeval(Q, d, x, use_sqr, (void*)T, &F2xq_algebra, _F2xq_cmul);
+}
+
+static GEN
+F2xq_autpow_sqr(void * T, GEN x) { return F2x_F2xq_eval(x, x, (GEN) T); }
+
+static GEN
+F2xq_autpow_mul(void * T, GEN x, GEN y) { return F2x_F2xq_eval(x, y, (GEN) T); }
+
+GEN
+F2xq_autpow(GEN x, long n, GEN T)
+{
+  return gen_powu(x,n,(void*)T,F2xq_autpow_sqr,F2xq_autpow_mul);
+}
+
+ulong
+F2xq_trace(GEN x, GEN T)
+{
+  pari_sp av = avma;
+  ulong t;
+  long n = F2x_degree(T)-1;
+  GEN z = F2x_mul(x, F2x_deriv(T));
+  z = F2x_rem(z, T);
+  t = F2x_degree(z)<n ? 0 : 1;
+  avma = av; return t;
+}
+
+GEN
+F2xq_conjvec(GEN x, GEN T)
+{
+  long i, l = F2x_degree(T);
+  GEN z = cgetg(l,t_COL);
+  gel(z,1) = vecsmall_copy(x);
+  for (i=2; i<l; i++) gel(z,i) = F2xq_sqr(gel(z,i-1), T);
+  return z;
+}
+
+GEN
+random_F2x(long d, long vs)
+{
+  long i, l = nbits2lg(d+1);
+  GEN y = cgetg(l,t_VECSMALL); y[1] = vs;
+  for (i=2; i<l; i++) y[i] = pari_rand();
+  y[l-1] &= (1UL<<remsBIL(d))-1UL;
+  return F2x_renormalize(y,l);
+}
+
+static GEN
+_F2xq_pow(void *data, GEN x, GEN n)
+{
+  GEN pol = (GEN) data;
+  return F2xq_pow(x,n, pol);
+}
+
+static GEN
+_F2xq_rand(void *data)
+{
+  pari_sp av=avma;
+  GEN pol = (GEN) data;
+  long d = F2x_degree(pol);
+  GEN z;
+  do
+  {
+    avma = av;
+    z = random_F2x(d,pol[1]);
+  } while (lgpol(z)==0);
+  return z;
+}
+
+static GEN F2xq_easylog(void* E, GEN a, GEN g, GEN ord);
+
+static const struct bb_group F2xq_star={_F2xq_mul,_F2xq_pow,_F2xq_rand,hash_GEN,F2x_equal,F2x_equal1,F2xq_easylog};
+
+GEN
+F2xq_order(GEN a, GEN ord, GEN T)
+{
+  return gen_order(a,ord,(void*)T,&F2xq_star);
+}
+
+static long
+F2x_is_smooth_squarefree(GEN f, long r)
+{
+  pari_sp av = avma;
+  long i;
+  GEN sx = polx_F2x(f[1]), a = sx;
+  for(i=1;  ;i++)
+  {
+    a = F2xq_sqr(F2x_rem(a,f),f);
+    if (F2x_equal(a, F2x_rem(sx,f))) {avma = av; return 1;}
+    if (i==r) {avma = av; return 0;}
+    f = F2x_div(f, F2x_gcd(F2x_add(a,sx),f));
+  }
+}
+
+static long
+F2x_is_smooth(GEN g, long r)
+{
+  GEN f = gen_0;
+  if (lgpol(g)==0) return 0;
+  while (1)
+  {
+    f = F2x_gcd(g, F2x_deriv(g));
+    if (!F2x_is_smooth_squarefree(F2x_div(g, f), r))
+      return 0;
+    if (F2x_degree(f)==0) return 1;
+    g = F2x_issquare(f) ? F2x_sqrt(f): f;
+  }
+  return 0;
+}
+
+static GEN
+F2x_factorel(GEN h)
+{
+  GEN F = F2x_factcantor(h, 0);
+  GEN F1 = gel(F, 1), F2 = gel(F, 2);
+  long i, l1 = lg(F1)-1;
+  GEN p2 = cgetg(l1+1, t_VECSMALL);
+  GEN e2 = cgetg(l1+1, t_VECSMALL);
+  for (i = 1; i <= l1; ++i)
+  {
+    p2[i] = mael(F1, i, 2);
+    e2[i] = F2[i];
+  }
+  return mkmat2(p2, e2);
+}
+
+static GEN
+mkF2(ulong x, long v) { return mkvecsmall2(v, x); }
+
+static GEN F2xq_log_Coppersmith_d(GEN W, GEN g, long r, long n, GEN T, GEN mo);
+
+static GEN
+F2xq_log_from_rel(GEN W, GEN rel, long r, long n, GEN T, GEN m)
+{
+  pari_sp av = avma;
+  GEN F = gel(rel,1), E = gel(rel,2), o = gen_0;
+  long i, l = lg(F);
+  for(i=1; i<l; i++)
+  {
+    GEN R = gel(W, F[i]);
+    if (signe(R)==0) /* Already failed */
+      return NULL;
+    else if (signe(R)<0) /* Not yet tested */
+    {
+      setsigne(gel(W,F[i]),0);
+      R = F2xq_log_Coppersmith_d(W, mkF2(F[i],T[1]), r, n, T, m);
+      if (!R) return NULL;
+    }
+    o = Fp_add(o, mulis(R, E[i]), m);
+  }
+  return gerepileuptoint(av, o);
+}
+
+static GEN
+F2xq_log_Coppersmith_d(GEN W, GEN g, long r, long n, GEN T, GEN mo)
+{
+  pari_sp av = avma, av2;
+  long dg = F2x_degree(g), k = r-1, m = maxss((dg-k)/2,0);
+  long i, j, l = dg-m, N;
+  GEN v = cgetg(k+m+1,t_MAT);
+  long dT = F2x_degree(T);
+  long h = dT>>n, d = dT-(h<<n);
+  GEN R = F2x_add(F2x_shift(pol1_F2x(T[1]), dT), T);
+  GEN z = F2x_rem(F2x_shift(pol1_F2x(T[1]),h), g);
+  for(i=1; i<=k+m; i++)
+  {
+    gel(v,i) = F2x_to_F2v(F2x_shift(z,-l),m);
+    z = F2x_rem(F2x_shift(z,1),g);
+  }
+  v = F2m_ker(v);
+  for(i=1; i<=k; i++)
+    gel(v,i) = F2v_to_F2x(gel(v,i),T[1]);
+  N = 1<<k;
+  av2 = avma;
+  for (i=1; i<N; i++)
+  {
+    GEN p,q,qh,a,b;
+    avma = av2;
+    q = pol0_F2x(T[1]);
+    for(j=0; j<k; j++)
+      if (i&(1UL<<j))
+        q = F2x_add(q, gel(v,j+1));
+    qh= F2x_shift(q,h);
+    p = F2x_rem(qh,g);
+    b = F2x_add(F2x_mul(R, F2x_pow2n(q, n)), F2x_shift(F2x_pow2n(p, n), d));
+    if (lgpol(b)==0 || !F2x_is_smooth(b, r)) continue;
+    a = F2x_div(F2x_add(qh,p),g);
+    if (F2x_degree(F2x_gcd(a,q)) &&  F2x_degree(F2x_gcd(a,p))) continue;
+    if (!(lgpol(a)==0 || !F2x_is_smooth(a, r)))
+    {
+      GEN F = F2x_factorel(b);
+      GEN G = F2x_factorel(a);
+      GEN FG = vecsmall_concat(vecsmall_append(gel(F, 1), 2), gel(G, 1));
+      GEN E  = vecsmall_concat(vecsmall_append(gel(F, 2), -d),
+                               zv_z_mul(gel(G, 2),-(1L<<n)));
+      GEN R  = famatsmall_reduce(mkmat2(FG, E));
+      GEN l  = F2xq_log_from_rel(W, R, r, n, T, mo);
+      if (!l) continue;
+      l = Fp_div(l,int2n(n),mo);
+      if (dg <= r)
+      {
+        affii(l,gel(W,g[2]));
+        if (DEBUGLEVEL>1) err_printf("Found %lu\n", g[2]);
+      }
+      return gerepileuptoint(av, l);
+    }
+  }
+  avma = av;
+  return NULL;
+}
+
+static GEN
+F2xq_log_find_rel(GEN b, long r, GEN T, GEN *g, ulong *e)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  while (1)
+  {
+    GEN M;
+    *g = F2xq_mul(*g, b, T); (*e)++;
+    M = F2x_halfgcd(*g,T);
+    if (F2x_is_smooth(gcoeff(M,1,1), r))
+    {
+      GEN z = F2x_add(F2x_mul(gcoeff(M,1,1),*g), F2x_mul(gcoeff(M,1,2),T));
+      if (F2x_is_smooth(z, r))
+      {
+        GEN F = F2x_factorel(z);
+        GEN G = F2x_factorel(gcoeff(M,1,1));
+        GEN rel = mkmat2(vecsmall_concat(gel(F, 1),gel(G, 1)),
+                         vecsmall_concat(gel(F, 2),zv_neg(gel(G, 2))));
+        gerepileall(av, 2, g, &rel);
+        return rel;
+      }
+    }
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"F2xq_log_find_rel");
+      *g = gerepileuptoleaf(av, *g);
+    }
+  }
+}
+
+static GEN
+F2xq_log_Coppersmith_rec(GEN W, long r2, GEN a, long r, long n, GEN T, GEN m)
+{
+  GEN b = polx_F2x(T[1]);
+  ulong AV = 0;
+  GEN g = a, bad = pol0_F2x(T[1]);
+  pari_timer ti;
+  while(1)
+  {
+    long i, l;
+    GEN V, F, E, Ao;
+    timer_start(&ti);
+    V = F2xq_log_find_rel(b, r2, T, &g, &AV);
+    if (DEBUGLEVEL>1) timer_printf(&ti,"%ld-smooth element",r2);
+    F = gel(V,1); E = gel(V,2);
+    l = lg(F);
+    Ao = gen_0;
+    for(i=1; i<l; i++)
+    {
+      GEN Fi = mkF2(F[i], T[1]);
+      GEN R;
+      if (F2x_degree(Fi) <= r)
+      {
+        if (signe(gel(W,F[i]))==0)
+          break;
+        else if (signe(gel(W,F[i]))<0)
+        {
+          setsigne(gel(W,F[i]),0);
+          R = F2xq_log_Coppersmith_d(W,Fi,r,n,T,m);
+        } else
+          R = gel(W,F[i]);
+      }
+      else
+      {
+        if (F2x_equal(Fi,bad)) break;
+        R = F2xq_log_Coppersmith_d(W,Fi,r,n,T,m);
+        if (!R) bad = Fi;
+      }
+      if (!R) break;
+      Ao = Fp_add(Ao, mulis(R, E[i]), m);
+    }
+    if (i==l) return subis(Ao,AV);
+  }
+}
+
+/* Coppersmith:
+ T*X^e = X^(h*2^n)-R
+ (u*x^h + v)^(2^n) = u^(2^n)*X^(h*2^n)+v^(2^n)
+ (u*x^h + v)^(2^n) = u^(2^n)*R+v^(2^n)
+*/
+
+static GEN
+rel_Coppersmith(GEN u, GEN v, long h, GEN R, long r, long n, long d)
+{
+  GEN b, F, G, M;
+  GEN a = F2x_add(F2x_shift(u, h), v);
+  if (!F2x_is_smooth(a, r)) return NULL;
+  b = F2x_add(F2x_mul(R, F2x_pow2n(u, n)), F2x_shift(F2x_pow2n(v, n),d));
+  if (!F2x_is_smooth(b, r)) return NULL;
+  F = F2x_factorel(a);
+  G = F2x_factorel(b);
+  M = mkmat2(vecsmall_concat(gel(F, 1), vecsmall_append(gel(G, 1), 2)),
+             vecsmall_concat(zv_z_mul(gel(F, 2),1UL<<n), vecsmall_append(zv_neg(gel(G, 2)),d)));
+  return famatsmall_reduce(M);
+}
+
+static GEN
+F2xq_log_Coppersmith(long nbrel, long r, long n, GEN T)
+{
+  long dT = F2x_degree(T);
+  long h = dT>>n, d = dT-(h<<n);
+  GEN R = F2x_add(F2x_shift(pol1_F2x(T[1]), dT), T);
+  GEN u = mkF2(0,T[1]), v = mkF2(0,T[1]);
+  long rel = 1, nbtest = 0;
+  GEN M = cgetg(nbrel+1, t_VEC);
+  pari_sp av = avma;
+  long i,j;
+  if (DEBUGLEVEL) err_printf("Coppersmith (R = %ld): ",F2x_degree(R));
+  for (i=1; ; i++)
+  {
+    u[2] = i;
+    for(j=1; j<=i; j++)
+    {
+      v[2] = j;
+      avma = av;
+      if (F2x_degree(F2x_gcd(u,v))==0)
+      {
+        GEN z = rel_Coppersmith(u, v, h, R, r, n, d);
+        nbtest++;
+        if (z)
+        {
+          gel(M,rel++) = gerepilecopy(av, z); av = avma;
+          if (DEBUGLEVEL && (rel&511UL)==0)
+            err_printf("%ld%%[%ld] ",rel*100/nbrel,i);
+        }
+        if (rel>nbrel) break;
+        if (i==j) continue;
+        z = rel_Coppersmith(v, u, h, R, r, n, d);
+        nbtest++;
+        if (!z) continue;
+        gel(M,rel++) = gerepilecopy(av, z); av = avma;
+        if (DEBUGLEVEL && (rel&511UL)==0)
+          err_printf("%ld%%[%ld] ",rel*100/nbrel,i);
+        if (rel>nbrel) break;
+      }
+    }
+    if (rel>nbrel) break;
+  }
+  if (DEBUGLEVEL) err_printf(": %ld tests\n", nbtest);
+  return M;
+}
+
+static GEN
+smallirred_F2x(ulong n, long sv)
+{
+  GEN a = zero_zv(nbits2lg(n+1)-1);
+  a[1] = sv; F2x_set(a,n); a[2]++;
+  while (!F2x_is_irred(a)) a[2]+=2;
+  return a;
+}
+
+static GEN
+check_kernel(long N, GEN M, GEN T, GEN m)
+{
+  pari_sp av = avma;
+  GEN K = FpMs_leftkernel_elt(M, N, m);
+  long i, f=0;
+  long l = lg(K), lm = lgefint(m);
+  GEN g = polx_F2x(T[1]);
+  GEN idx = diviiexact(subis(int2n(F2x_degree(T)),1),m);
+  pari_timer ti;
+  if (DEBUGLEVEL) timer_start(&ti);
+  K = FpC_Fp_mul(K, Fp_inv(gel(K,g[2]), m), m);
+  for(i=1; i<l; i++)
+  {
+    GEN k = gel(K,i);
+    if (signe(k)==0 || !F2x_equal(F2xq_pow(g, mulii(k,idx), T),
+                                  F2xq_pow(mkF2(i,T[1]), idx, T)))
+      gel(K,i) = cgetineg(lm);
+    else
+      f++;
+  }
+  if (DEBUGLEVEL) timer_printf(&ti,"found %ld logs", f);
+  return gerepileupto(av, K);
+}
+
+static GEN
+F2xq_log_index(GEN a0, GEN b0, GEN m, GEN T0)
+{
+  pari_sp av = avma;
+  GEN  M, S, a, b, Ao=NULL, Bo=NULL, W, e;
+  pari_timer ti;
+  long n = F2x_degree(T0), r = (long) (sqrt((double) 2*n))-(n>100);
+  GEN T = smallirred_F2x(n,T0[1]);
+  long d = 2, r2 = 3*r/2, d2 = 2;
+  long N = (1UL<<(r+1))-1UL;
+  long nbi = itos(ffsumnbirred(gen_2, r)), nbrel=nbi*5/4;
+  if (DEBUGLEVEL)
+  {
+    err_printf("F2xq_log: Parameters r=%ld r2=%ld\n", r,r2);
+    err_printf("F2xq_log: Size FB=%ld rel. needed=%ld\n", nbi, nbrel);
+    timer_start(&ti);
+  }
+  S = Flx_to_F2x(Flx_ffisom(F2x_to_Flx(T0),F2x_to_Flx(T),2));
+  a = F2x_F2xq_eval(a0, S, T);
+  b = F2x_F2xq_eval(b0, S, T);
+  if (DEBUGLEVEL) timer_printf(&ti,"model change");
+  M = F2xq_log_Coppersmith(nbrel,r,d,T);
+  if(DEBUGLEVEL)
+    timer_printf(&ti,"relations");
+  W = check_kernel(N, M, T, m);
+  timer_start(&ti);
+  Ao = F2xq_log_Coppersmith_rec(W, r2, a, r, d2, T, m);
+  if (DEBUGLEVEL) timer_printf(&ti,"smooth element");
+  Bo = F2xq_log_Coppersmith_rec(W, r2, b, r, d2, T, m);
+  if (DEBUGLEVEL) timer_printf(&ti,"smooth generator");
+  e = Fp_div(Ao, Bo, m);
+  if (!F2x_equal(F2xq_pow(b0,e,T0),a0)) pari_err_BUG("F2xq_log");
+  return gerepileupto(av, e);
+}
+
+static GEN
+F2xq_easylog(void* E, GEN a, GEN g, GEN ord)
+{
+  if (F2x_equal1(a)) return gen_0;
+  if (F2x_equal(a,g)) return gen_1;
+  if (typ(ord)!=t_INT) return NULL;
+  if (expi(ord)<28) return NULL;
+  return F2xq_log_index(a,g,ord,(GEN)E);
+}
+
+GEN
+F2xq_log(GEN a, GEN g, GEN ord, GEN T)
+{
+  GEN z, v = dlog_get_ordfa(ord);
+  ord = mkvec2(gel(v,1),ZM_famat_limit(gel(v,2),int2n(28)));
+  z = gen_PH_log(a,g,ord,(void*)T,&F2xq_star);
+  return z? z: cgetg(1,t_VEC);
+}
+
+GEN
+F2xq_Artin_Schreier(GEN a, GEN T)
+{
+  pari_sp ltop=avma;
+  long j,N = F2x_degree(T);
+  GEN Q, XP;
+  pari_timer ti;
+  timer_start(&ti);
+  XP = F2xq_sqr(polx_F2x(T[1]),T);
+  Q  = F2xq_matrix_pow(XP,N,N,T);
+  for (j=1; j<=N; j++)
+    F2m_flip(Q,j,j);
+  if(DEBUGLEVEL>=9) timer_printf(&ti,"Berlekamp matrix");
+  F2v_add_inplace(gel(Q,1),a);
+  Q = F2m_ker_sp(Q,0);
+  if(DEBUGLEVEL>=9) timer_printf(&ti,"kernel");
+  if (lg(Q)!=2) return NULL;
+  Q = gel(Q,1);
+  Q[1] = T[1];
+  return gerepileuptoleaf(ltop, Q);
+}
+
+GEN
+F2xq_sqrt_fast(GEN c, GEN sqx, GEN T)
+{
+  GEN c0, c1;
+  F2x_even_odd(c, &c0, &c1);
+  return F2x_add(c0, F2xq_mul(c1, sqx, T));
+}
+
+static int
+F2x_is_x(GEN a) { return lg(a)==3 && a[2]==2; }
+
+GEN
+F2xq_sqrt(GEN a, GEN T)
+{
+  pari_sp av = avma;
+  long n = F2x_degree(T);
+  GEN sqx;
+  if (n==1) return leafcopy(a);
+  if (n==2) return F2xq_sqr(a,T);
+  sqx = F2xq_autpow(mkF2(4, T[1]), n-1, T);
+  return gerepileuptoleaf(av, F2x_is_x(a)? sqx: F2xq_sqrt_fast(a,sqx,T));
+}
+
+GEN
+F2xq_sqrtn(GEN a, GEN n, GEN T, GEN *zeta)
+{
+  if (!lgpol(a))
+  {
+    if (signe(n) < 0) pari_err_INV("F2xq_sqrtn",a);
+    if (zeta)
+      *zeta=pol1_F2x(T[1]);
+    return pol0_F2x(T[1]);
+  }
+  return gen_Shanks_sqrtn(a,n,addis(powuu(2,F2x_degree(T)),-1),zeta,(void*)T,&F2xq_star);
+}
+
+GEN
+gener_F2xq(GEN T, GEN *po)
+{
+  long i, j, vT = T[1], f = F2x_degree(T);
+  pari_sp av0 = avma, av;
+  GEN g, L2, o, q;
+
+  if (f == 1) {
+    if (po) *po = mkvec2(gen_1, trivial_fact());
+    return pol1_F2x(vT);
+  }
+  q = subis(powuu(2,f), 1);
+  o = factor_pn_1(gen_2,f);
+  L2 = leafcopy( gel(o, 1) );
+  for (i = j = 1; i < lg(L2); i++)
+  {
+    if (equaliu(gel(L2,i),2)) continue;
+    gel(L2,j++) = diviiexact(q, gel(L2,i));
+  }
+  setlg(L2, j);
+  for (av = avma;; avma = av)
+  {
+    g = random_F2x(f, vT);
+    if (F2x_degree(g) < 1) continue;
+    for (i = 1; i < j; i++)
+    {
+      GEN a = F2xq_pow(g, gel(L2,i), T);
+      if (F2x_equal1(a)) break;
+    }
+    if (i == j) break;
+  }
+  if (!po) g = gerepilecopy(av0, g);
+  else {
+    *po = mkvec2(subis(int2n(f), 1), o);
+    gerepileall(av0, 2, &g, po);
+  }
+  return g;
+}
+
+GEN
+ZXX_to_F2xX(GEN B, long v)
+{
+  long lb=lg(B);
+  long i;
+  GEN b=cgetg(lb,t_POL);
+  b[1]=evalsigne(1)|(((ulong)B[1])&VARNBITS);
+  for (i=2; i<lb; i++)
+    switch (typ(gel(B,i)))
+    {
+    case t_INT:
+      gel(b,i) = Z_to_F2x(gel(B,i), v);
+      break;
+    case t_POL:
+      gel(b,i) = ZX_to_F2x(gel(B,i));
+      break;
+    }
+  return FlxX_renormalize(b, lb);
+}
+
+static GEN
+_F2xq_neg(void *E, GEN x)
+{ (void) E; return vecsmall_copy(x); }
+
+static GEN
+_F2xq_rmul(void *E, GEN x, GEN y)
+{ (void) E; return F2x_mul(x,y); }
+
+static GEN
+_F2xq_inv(void *E, GEN x)
+{ return F2xq_inv(x, (GEN) E); }
+
+static int
+_F2xq_equal0(GEN x) { return lgpol(x)==0; }
+
+static GEN
+_F2xq_s(void *E, long x)
+{ GEN T = (GEN) E;
+  return odd(x)? pol1_F2x(T[1]): pol0_F2x(T[0]);
+}
+
+static const struct bb_field F2xq_field={_F2xq_red,_F2xq_add,_F2xq_rmul,_F2xq_neg,
+                                         _F2xq_inv,_F2xq_equal0,_F2xq_s};
+
+const struct bb_field *get_F2xq_field(void **E, GEN T)
+{
+  *E = (void *) T;
+  return &F2xq_field;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                             F2v                                   **/
+/**                                                                   **/
+/***********************************************************************/
+/* F2v objects are defined as follows:
+   An F2v is a t_VECSMALL:
+   v[0] = codeword
+   v[1] = number of components
+   x[2] = a_0...a_31 x[3] = a_32..a_63, etc.  on 32bit
+   x[2] = a_0...a_63 x[3] = a_64..a_127, etc. on 64bit
+
+   where the a_i are bits.
+*/
+
+GEN
+F2c_to_ZC(GEN x)
+{
+  long l=x[1]+1;
+  GEN  z = cgetg(l, t_COL);
+  long i,j,k;
+  for (i=2,k=1; i<lg(x); i++)
+    for (j=0; j<BITS_IN_LONG && k<l; j++,k++)
+      gel(z,k) = (x[i]&(1UL<<j))? gen_1: gen_0;
+  return z;
+}
+GEN
+F2c_to_mod(GEN x)
+{
+  long l=x[1]+1;
+  GEN  z = cgetg(l, t_COL);
+  GEN _0 = mkintmod(gen_0,gen_2);
+  GEN _1 = mkintmod(gen_1,gen_2);
+  long i,j,k;
+  for (i=2,k=1; i<lg(x); i++)
+    for (j=0; j<BITS_IN_LONG && k<l; j++,k++)
+      gel(z,k) = (x[i]&(1UL<<j))? _1: _0;
+  return z;
+}
+
+/* x[a..b], a <= b */
+GEN
+F2v_slice(GEN x, long a, long b)
+{
+  long i,j,k, l = b-a+1;
+  GEN z = cgetg(nbits2lg(l), t_VECSMALL);
+  z[1] = l;
+  for(i=a,k=1,j=BITS_IN_LONG; i<=b; i++,j++)
+  {
+    if (j==BITS_IN_LONG) { j=0; z[++k]=0; }
+    if (F2v_coeff(x,i)) z[k] |= 1UL<<j;
+  }
+  return z;
+}
+/* x[a..b,], a <= b */
+GEN
+F2m_rowslice(GEN x, long a, long b)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+  for (i = 1; i < l; i++) gel(y,i) = F2v_slice(gel(x,i),a,b);
+  return y;
+}
+
+GEN
+F2m_to_ZM(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = F2c_to_ZC(gel(z,i));
+  return x;
+}
+GEN
+F2m_to_mod(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = F2c_to_mod(gel(z,i));
+  return x;
+}
+
+GEN
+F2c_to_Flc(GEN x)
+{
+  long l=x[1]+1;
+  GEN  z=cgetg(l, t_VECSMALL);
+  long i,j,k;
+  for (i=2,k=1; i<lg(x); i++)
+    for (j=0; j<BITS_IN_LONG && k<l; j++,k++)
+      z[k] = (x[i]>>j)&1UL;
+  return z;
+}
+
+GEN
+F2m_to_Flm(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = F2c_to_Flc(gel(z,i));
+  return x;
+}
+
+GEN
+ZV_to_F2v(GEN x)
+{
+  long l = lg(x)-1;
+  GEN z = cgetg(nbits2lg(l), t_VECSMALL);
+  long i,j,k;
+  z[1] = l;
+  for(i=1,k=1,j=BITS_IN_LONG; i<=l; i++,j++)
+  {
+    if (j==BITS_IN_LONG) { j=0; z[++k]=0; }
+    if (mpodd(gel(x,i))) z[k] |= 1UL<<j;
+  }
+  return z;
+}
+
+GEN
+RgV_to_F2v(GEN x)
+{
+  long l = lg(x)-1;
+  GEN z = cgetg(nbits2lg(l), t_VECSMALL);
+  long i,j,k;
+  z[1] = l;
+  for(i=1,k=1,j=BITS_IN_LONG; i<=l; i++,j++)
+  {
+    if (j==BITS_IN_LONG) { j=0; z[++k]=0; }
+    if (Rg_to_F2(gel(x,i))) z[k] |= 1UL<<j;
+  }
+  return z;
+}
+
+GEN
+Flv_to_F2v(GEN x)
+{
+  long l = lg(x)-1;
+  GEN z = cgetg(nbits2lg(l), t_VECSMALL);
+  long i,j,k;
+  z[1] = l;
+  for(i=1,k=1,j=BITS_IN_LONG; i<=l; i++,j++)
+  {
+    if (j==BITS_IN_LONG) { j=0; z[++k]=0; }
+    if (x[i]&1L) z[k] |= 1UL<<j;
+  }
+  return z;
+}
+
+GEN
+ZM_to_F2m(GEN x)
+{
+  long j, l = lg(x);
+  GEN y = cgetg(l,t_MAT);
+  if (l == 1) return y;
+  for (j=1; j<l; j++) gel(y,j) = ZV_to_F2v(gel(x,j));
+  return y;
+}
+
+GEN
+RgM_to_F2m(GEN x)
+{
+  long j, l = lg(x);
+  GEN y = cgetg(l,t_MAT);
+  if (l == 1) return y;
+  for (j=1; j<l; j++) gel(y,j) = RgV_to_F2v(gel(x,j));
+  return y;
+}
+
+GEN
+Flm_to_F2m(GEN x)
+{
+  long j, l = lg(x);
+  GEN y = cgetg(l,t_MAT);
+  if (l == 1) return y;
+  for (j=1; j<l; j++) gel(y,j) = Flv_to_F2v(gel(x,j));
+  return y;
+}
+
+/* Allow lg(y)<lg(x) */
+void
+F2v_add_inplace(GEN x, GEN y)
+{
+  long n = lg(y);
+  long r = (n-2)&7L, q = n-r, i;
+  for (i = 2; i < q; i += 8)
+  {
+    x[  i] ^= y[  i]; x[1+i] ^= y[1+i]; x[2+i] ^= y[2+i]; x[3+i] ^= y[3+i];
+    x[4+i] ^= y[4+i]; x[5+i] ^= y[5+i]; x[6+i] ^= y[6+i]; x[7+i] ^= y[7+i];
+  }
+  switch (r)
+  {
+    case 7: x[i] ^= y[i]; i++; case 6: x[i] ^= y[i]; i++;
+    case 5: x[i] ^= y[i]; i++; case 4: x[i] ^= y[i]; i++;
+    case 3: x[i] ^= y[i]; i++; case 2: x[i] ^= y[i]; i++;
+    case 1: x[i] ^= y[i]; i++;
+  }
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                               F2xV                                **/
+/**                                                                   **/
+/***********************************************************************/
+/* F2xV are t_VEC with F2x coefficients. */
+
+GEN
+F2xV_to_F2m(GEN v, long n)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_MAT);
+  for (j=1; j<N; j++) gel(y,j) = F2x_to_F2v(gel(v,j), n);
+  return y;
+}
diff --git a/src/basemath/F2xqE.c b/src/basemath/F2xqE.c
new file mode 100644
index 0000000..1f8b1ca
--- /dev/null
+++ b/src/basemath/F2xqE.c
@@ -0,0 +1,814 @@
+/* Copyright (C) 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with points over elliptic curves over F_2^n */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              F2xqE                                **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Theses functions deal with point over elliptic curves over F_2^n defined
+ * by an equation of the form:
+ ** y^2+x*y=x^3+a_2*x^2+a_6 if the curve is ordinary.
+ ** y^2+a_3*y=x^3+a_4*x+a_6 if the curve is supersingular.
+ * Most of the time a6 is omitted since it can be recovered from any point
+ * on the curve.
+ * For supersingular curves, the parameter a2 is replaced by [a3,a4,a3^-1].
+ */
+
+GEN
+RgE_to_F2xqE(GEN x, GEN T)
+{
+  if (ell_is_inf(x)) return x;
+  retmkvec2(Rg_to_F2xq(gel(x,1),T),Rg_to_F2xq(gel(x,2),T));
+}
+
+GEN
+F2xqE_changepoint(GEN x, GEN ch, GEN T)
+{
+  pari_sp av = avma;
+  GEN p1,z,u,r,s,t,v,v2,v3;
+  if (ell_is_inf(x)) return x;
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  v = F2xq_inv(u, T); v2 = F2xq_sqr(v, T); v3 = F2xq_mul(v,v2, T);
+  p1 = F2x_add(gel(x,1),r);
+  z = cgetg(3,t_VEC);
+  gel(z,1) = F2xq_mul(v2, p1, T);
+  gel(z,2) = F2xq_mul(v3, F2x_add(gel(x,2), F2x_add(F2xq_mul(s, p1, T),t)), T);
+  return gerepileupto(av, z);
+}
+
+GEN
+F2xqE_changepointinv(GEN x, GEN ch, GEN T)
+{
+  GEN u, r, s, t, X, Y, u2, u3, u2X, z;
+  if (ell_is_inf(x)) return x;
+  X = gel(x,1); Y = gel(x,2);
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  u2 = F2xq_sqr(u, T); u3 = F2xq_mul(u,u2, T);
+  u2X = F2xq_mul(u2,X, T);
+  z = cgetg(3, t_VEC);
+  gel(z,1) = F2x_add(u2X,r);
+  gel(z,2) = F2x_add(F2xq_mul(u3,Y, T), F2x_add(F2xq_mul(s,u2X, T), t));
+  return z;
+}
+
+static GEN
+F2xqE_dbl_slope(GEN P, GEN a, GEN T, GEN *slope)
+{
+  GEN x, y, Q;
+  if (ell_is_inf(P)) return ellinf();
+  x = gel(P,1); y = gel(P,2);
+  if (typ(a)==t_VECSMALL)
+  {
+    GEN a2 = a;
+    if (!lgpol(gel(P,1))) return ellinf();
+    *slope = F2x_add(x, F2xq_div(y, x, T));
+    Q = cgetg(3,t_VEC);
+    gel(Q, 1) = F2x_add(F2xq_sqr(*slope, T), F2x_add(*slope, a2));
+    gel(Q, 2) = F2x_add(F2xq_mul(*slope, F2x_add(x, gel(Q, 1)), T), F2x_add(y, gel(Q, 1)));
+  }
+  else
+  {
+    GEN a3 = gel(a,1), a4 = gel(a,2), a3i = gel(a,3);
+    *slope = F2xq_mul(F2x_add(a4, F2xq_sqr(x, T)), a3i, T);
+    Q = cgetg(3,t_VEC);
+    gel(Q, 1) = F2xq_sqr(*slope, T);
+    gel(Q, 2) = F2x_add(F2xq_mul(*slope, F2x_add(x, gel(Q, 1)), T), F2x_add(y, a3));
+  }
+  return Q;
+}
+
+GEN
+F2xqE_dbl(GEN P, GEN a, GEN T)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, F2xqE_dbl_slope(P, a, T,&slope));
+}
+
+static GEN
+F2xqE_add_slope(GEN P, GEN Q, GEN a, GEN T, GEN *slope)
+{
+  GEN Px, Py, Qx, Qy, R;
+  if (ell_is_inf(P)) return Q;
+  if (ell_is_inf(Q)) return P;
+  Px = gel(P,1); Py = gel(P,2);
+  Qx = gel(Q,1); Qy = gel(Q,2);
+  if (F2x_equal(Px, Qx))
+  {
+    if (F2x_equal(Py, Qy))
+      return F2xqE_dbl_slope(P, a, T, slope);
+    else
+      return ellinf();
+  }
+  *slope = F2xq_div(F2x_add(Py, Qy), F2x_add(Px, Qx), T);
+  R = cgetg(3,t_VEC);
+  if (typ(a)==t_VECSMALL)
+  {
+    GEN a2 = a;
+    gel(R, 1) = F2x_add(F2x_add(F2x_add(F2x_add(F2xq_sqr(*slope, T), *slope), Px), Qx), a2);
+    gel(R, 2) = F2x_add(F2xq_mul(*slope, F2x_add(Px, gel(R, 1)), T), F2x_add(Py, gel(R, 1)));
+  }
+  else
+  {
+    GEN a3 = gel(a,1);
+    gel(R, 1) = F2x_add(F2x_add(F2xq_sqr(*slope, T), Px), Qx);
+    gel(R, 2) = F2x_add(F2xq_mul(*slope, F2x_add(Px, gel(R, 1)), T), F2x_add(Py, a3));
+  }
+  return R;
+}
+
+GEN
+F2xqE_add(GEN P, GEN Q, GEN a, GEN T)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, F2xqE_add_slope(P, Q, a, T, &slope));
+}
+
+static GEN
+F2xqE_neg_i(GEN P, GEN a)
+{
+  GEN LHS;
+  if (ell_is_inf(P)) return P;
+  LHS = typ(a)==t_VECSMALL ? gel(P,1): gel(a,1);
+  return mkvec2(gel(P,1), F2x_add(LHS, gel(P,2)));
+}
+
+GEN
+F2xqE_neg(GEN P, GEN a, GEN T)
+{
+  GEN LHS;
+  (void) T;
+  if (ell_is_inf(P)) return ellinf();
+  LHS = typ(a)==t_VECSMALL ? gel(P,1): gel(a,1);
+  return mkvec2(gcopy(gel(P,1)), F2x_add(LHS, gel(P,2)));
+}
+
+GEN
+F2xqE_sub(GEN P, GEN Q, GEN a2, GEN T)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, F2xqE_add_slope(P, F2xqE_neg_i(Q, a2), a2, T, &slope));
+}
+
+struct _F2xqE
+{
+  GEN a2, a6;
+  GEN T;
+};
+
+static GEN
+_F2xqE_dbl(void *E, GEN P)
+{
+  struct _F2xqE *ell = (struct _F2xqE *) E;
+  return F2xqE_dbl(P, ell->a2, ell->T);
+}
+
+static GEN
+_F2xqE_add(void *E, GEN P, GEN Q)
+{
+  struct _F2xqE *ell=(struct _F2xqE *) E;
+  return F2xqE_add(P, Q, ell->a2, ell->T);
+}
+
+static GEN
+_F2xqE_mul(void *E, GEN P, GEN n)
+{
+  pari_sp av = avma;
+  struct _F2xqE *e=(struct _F2xqE *) E;
+  long s = signe(n);
+  if (!s || ell_is_inf(P)) return ellinf();
+  if (s<0) P = F2xqE_neg(P, e->a2, e->T);
+  if (is_pm1(n)) return s>0? gcopy(P): P;
+  return gerepileupto(av, gen_pow(P, n, e, &_F2xqE_dbl, &_F2xqE_add));
+}
+
+GEN
+F2xqE_mul(GEN P, GEN n, GEN a2, GEN T)
+{
+  struct _F2xqE E;
+  E.a2 = a2; E.T = T;
+  return _F2xqE_mul(&E, P, n);
+}
+
+/* Finds a random non-singular point on E */
+GEN
+random_F2xqE(GEN a, GEN a6, GEN T)
+{
+  pari_sp ltop = avma;
+  GEN x, y, rhs, u;
+  do
+  {
+    avma= ltop;
+    x   = random_F2x(F2x_degree(T),T[1]);
+    if (typ(a) == t_VECSMALL)
+    {
+      GEN a2 = a, x2;
+      if (!lgpol(x))
+        { avma=ltop; retmkvec2(pol0_Flx(T[1]), F2xq_sqrt(a6,T)); }
+      u = x; x2  = F2xq_sqr(x, T);
+      rhs = F2x_add(F2xq_mul(x2,F2x_add(x,a2),T),a6);
+      rhs = F2xq_div(rhs,x2,T);
+    }
+    else
+    {
+      GEN a3 = gel(a,1), a4 = gel(a,2), a3i = gel(a,3), u2i;
+      u = a3; u2i = F2xq_sqr(a3i,T);
+      rhs = F2x_add(F2xq_mul(x,F2x_add(F2xq_sqr(x,T),a4),T),a6);
+      rhs = F2xq_mul(rhs,u2i,T);
+    }
+  } while (F2xq_trace(rhs,T));
+  y = F2xq_mul(F2xq_Artin_Schreier(rhs, T), u, T);
+  return gerepilecopy(ltop, mkvec2(x, y));
+}
+
+static GEN
+_F2xqE_rand(void *E)
+{
+  struct _F2xqE *ell=(struct _F2xqE *) E;
+  return random_F2xqE(ell->a2, ell->a6, ell->T);
+}
+
+static const struct bb_group F2xqE_group={_F2xqE_add,_F2xqE_mul,_F2xqE_rand,hash_GEN,zvV_equal,ell_is_inf, NULL};
+
+const struct bb_group *
+get_F2xqE_group(void ** pt_E, GEN a2, GEN a6, GEN T)
+{
+  struct _F2xqE *e = (struct _F2xqE *) stack_malloc(sizeof(struct _F2xqE));
+  e->a2 = a2; e->a6 = a6; e->T = T;
+  *pt_E = (void *) e;
+  return &F2xqE_group;
+}
+
+GEN
+F2xqE_order(GEN z, GEN o, GEN a2, GEN T)
+{
+  pari_sp av = avma;
+  struct _F2xqE e;
+  e.a2=a2; e.T=T;
+  return gerepileuptoint(av, gen_order(z, o, (void*)&e, &F2xqE_group));
+}
+
+GEN
+F2xqE_log(GEN a, GEN b, GEN o, GEN a2, GEN T)
+{
+  pari_sp av = avma;
+  struct _F2xqE e;
+  e.a2=a2; e.T=T;
+  return gerepileuptoint(av, gen_PH_log(a, b, o, (void*)&e, &F2xqE_group));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                            Pairings                               **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Derived from APIP from and by Jerome Milan, 2012 */
+
+static GEN
+F2xqE_vert(GEN P, GEN Q, GEN T)
+{
+  if (ell_is_inf(P))
+    return pol1_F2x(T[1]);
+  return F2x_add(gel(Q, 1), gel(P, 1));
+}
+
+/* Computes the equation of the line tangent to R and returns its
+   evaluation at the point Q. Also doubles the point R.
+ */
+
+static GEN
+F2xqE_tangent_update(GEN R, GEN Q, GEN a2, GEN T, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = ellinf();
+    return pol1_F2x(T[1]);
+  }
+  else if (!lgpol(gel(R,1)))
+  {
+    *pt_R = ellinf();
+    return F2xqE_vert(R, Q, T);
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = F2xqE_dbl_slope(R, a2, T, &slope);
+    tmp1 = F2x_add(gel(Q, 1), gel(R, 1));
+    tmp2 = F2x_add(F2xq_mul(tmp1, slope, T), gel(R,2));
+    return F2x_add(gel(Q, 2), tmp2);
+  }
+}
+
+/* Computes the equation of the line through R and P, and returns its
+   evaluation at the point Q. Also adds P to the point R.
+ */
+
+static GEN
+F2xqE_chord_update(GEN R, GEN P, GEN Q, GEN a2, GEN T, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = gcopy(P);
+    return F2xqE_vert(P, Q, T);
+  }
+  else if (ell_is_inf(P))
+  {
+    *pt_R = gcopy(R);
+    return F2xqE_vert(R, Q, T);
+  }
+  else if (F2x_equal(gel(P, 1), gel(R, 1)))
+  {
+    if (F2x_equal(gel(P, 2), gel(R, 2)))
+      return F2xqE_tangent_update(R, Q, a2, T, pt_R);
+    else
+    {
+      *pt_R = ellinf();
+      return F2xqE_vert(R, Q, T);
+    }
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = F2xqE_add_slope(P, R, a2, T, &slope);
+    tmp1  = F2xq_mul(F2x_add(gel(Q, 1), gel(R, 1)), slope, T);
+    tmp2  = F2x_add(tmp1, gel(R, 2));
+    return F2x_add(gel(Q, 2), tmp2);
+  }
+}
+
+/* Returns the Miller function f_{m, Q} evaluated at the point P using
+   the standard Miller algorithm.
+ */
+
+struct _F2xqE_miller
+{
+  GEN T, a2, P;
+};
+
+static GEN
+F2xqE_Miller_dbl(void* E, GEN d)
+{
+  struct _F2xqE_miller *m = (struct _F2xqE_miller *)E;
+  GEN T = m->T, a2 = m->a2, P = m->P;
+  GEN v, line;
+  GEN num = F2xq_sqr(gel(d,1), T);
+  GEN denom = F2xq_sqr(gel(d,2), T);
+  GEN point = gel(d,3);
+  line = F2xqE_tangent_update(point, P, a2, T, &point);
+  num  = F2xq_mul(num, line, T);
+  v = F2xqE_vert(point, P, T);
+  denom = F2xq_mul(denom, v, T);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+F2xqE_Miller_add(void* E, GEN va, GEN vb)
+{
+  struct _F2xqE_miller *m = (struct _F2xqE_miller *)E;
+  GEN T = m->T, a2 = m->a2, P = m->P;
+  GEN v, line, point;
+  GEN na = gel(va,1), da = gel(va,2), pa = gel(va,3);
+  GEN nb = gel(vb,1), db = gel(vb,2), pb = gel(vb,3);
+  GEN num   = F2xq_mul(na, nb, T);
+  GEN denom = F2xq_mul(da, db, T);
+  line = F2xqE_chord_update(pa, pb, P, a2, T, &point);
+  num  = F2xq_mul(num, line, T);
+  v = F2xqE_vert(point, P, T);
+  denom = F2xq_mul(denom, v, T);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+F2xqE_Miller(GEN Q, GEN P, GEN m, GEN a2, GEN T)
+{
+  pari_sp ltop = avma;
+  struct _F2xqE_miller d;
+  GEN v, num, denom, g1;
+
+  d.a2 = a2; d.T = T; d.P = P;
+  g1 = pol1_F2x(T[1]);
+  v = gen_pow(mkvec3(g1,g1,Q), m, (void*)&d, F2xqE_Miller_dbl, F2xqE_Miller_add);
+  num = gel(v,1); denom = gel(v,2);
+  if (!lgpol(num) || !lgpol(denom)) { avma = ltop; return NULL; }
+  return gerepileupto(ltop, F2xq_div(num, denom, T));
+}
+
+GEN
+F2xqE_weilpairing(GEN P, GEN Q, GEN m, GEN a2, GEN T)
+{
+  pari_sp ltop = avma;
+  GEN num, denom, result;
+  if (ell_is_inf(P) || ell_is_inf(Q) || F2x_equal(P,Q))
+    return pol1_F2x(T[1]);
+  num    = F2xqE_Miller(P, Q, m, a2, T);
+  if (!num) return pol1_F2x(T[1]);
+  denom  = F2xqE_Miller(Q, P, m, a2, T);
+  if (!denom) { avma=ltop; return pol1_F2x(T[1]); }
+  result = F2xq_div(num, denom, T);
+  return gerepileupto(ltop, result);
+}
+
+GEN
+F2xqE_tatepairing(GEN P, GEN Q, GEN m, GEN a2, GEN T)
+{
+  GEN num;
+  if (ell_is_inf(P) || ell_is_inf(Q))
+    return pol1_F2x(T[1]);
+  num = F2xqE_Miller(P, Q, m, a2, T);
+  return num? num: pol1_F2x(T[1]);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          Point counting                           **/
+/**                                                                   **/
+/***********************************************************************/
+
+static GEN
+Z2x_rshift(GEN y, long x)
+{
+  GEN z;
+  long i, l;
+  if (!x) return pol0_Flx(y[1]);
+  z = cgetg_copy(y, &l); z[1] = y[1];
+  for(i=2; i<l; i++) z[i] = y[i]>>x;
+  return Flx_renormalize(z, l);
+}
+
+/* Solve the linear equation approximation in the Newton algorithm */
+
+static GEN
+gen_Z2x_Dixon(GEN F, GEN V, long N, void *E, GEN lin(void *E, GEN F, GEN d, long N), GEN invl(void *E, GEN d))
+{
+  pari_sp av = avma;
+  long N2, M;
+  GEN VN2, V2, VM, bil;
+  ulong q = 1UL<<N;
+  if (N == 1) return invl(E, V);
+  V = Flx_red(V, q);
+  N2 = (N + 1)>>1; M = N - N2;
+  F = FlxT_red(F, q);
+  VN2 = gen_Z2x_Dixon(F, V, N2, E, lin, invl);
+  bil = lin(E, F, VN2, N);
+  V2 = Z2x_rshift(Flx_sub(V, bil, q), N2);
+  VM = gen_Z2x_Dixon(F, V2, M, E, lin, invl);
+  return gerepileupto(av, Flx_add(VN2, Flx_Fl_mul(VM, 1UL<<N2, q), q));
+}
+
+/* Solve F(X) = V mod 2^N
+   F(Xn) = V [mod 2^n]
+   Vm = (V-F(Xn))/(2^n)
+   F(Xm) = Vm
+   X = Xn + 2^n*Xm
+*/
+
+static GEN
+gen_Z2X_Dixon(GEN F, GEN V, long N, void *E,
+                     GEN lin(void *E, GEN F, GEN d, long N),
+                     GEN lins(void *E, GEN F, GEN d, long N),
+                     GEN invls(void *E, GEN d))
+{
+  pari_sp av = avma;
+  long n, m;
+  GEN Xn, Xm, FXn, Vm;
+  if (N<BITS_IN_LONG)
+  {
+    ulong q = 1UL<<N;
+    return Flx_to_ZX(gen_Z2x_Dixon(ZXT_to_FlxT(F,q), ZX_to_Flx(V,q),N,E,lins,invls));
+  }
+  V = ZX_remi2n(V, N);
+  n = (N + 1)>>1; m = N - n;
+  F = ZXT_remi2n(F, N);
+  Xn = gen_Z2X_Dixon(F, V, n, E, lin, lins, invls);
+  FXn = lin(E, F, Xn, N);
+  Vm = ZX_shifti(ZX_sub(V, FXn), -n);
+  Xm = gen_Z2X_Dixon(F, Vm, m, E, lin, lins, invls);
+  return gerepileupto(av, ZX_remi2n(ZX_add(Xn, ZX_shifti(Xm, n)), N));
+}
+
+/* H -> H mod 2*/
+
+static GEN _can_invls(void *E, GEN V) {(void) E; return V; }
+
+/* H -> H-(f0*H0-f1*H1) */
+
+static GEN _can_lin(void *E, GEN F, GEN V, long N)
+{
+  pari_sp av=avma;
+  GEN d0, d1, z;
+  (void) E;
+  RgX_even_odd(V, &d0, &d1);
+  z =  ZX_sub(V, ZX_sub(ZX_mul(gel(F,1), d0), ZX_mul(gel(F,2), d1)));
+  return gerepileupto(av, ZX_remi2n(z, N));
+}
+
+static GEN _can_lins(void *E, GEN F, GEN V, long N)
+{
+  GEN D=Flx_splitting(V, 2), z;
+  ulong q = 1UL<<N;
+  (void) E;
+  z = Flx_sub(Flx_mul(gel(F,1), gel(D,1), q), Flx_mul(gel(F,2), gel(D,2), q), q);
+  return Flx_sub(V, z, q);
+}
+
+/* P -> P-(P0^2-X*P1^2) */
+
+static GEN
+_can_iter(void *E, GEN f2, GEN q)
+{
+  GEN f0, f1, z;
+  (void) E;
+  RgX_even_odd(f2, &f0, &f1);
+  z = ZX_add(ZX_sub(f2, FpX_sqr(f0, q)), RgX_shift_shallow(FpX_sqr(f1, q), 1));
+  return mkvec3(z,f0,f1);
+}
+
+/* H -> H-(2*P0*H0-2*X*P1*H1) */
+
+static GEN
+_can_invd(void *E, GEN V, GEN v, GEN q, long M)
+{
+  GEN F;
+  (void)E; (void)q;
+  F = mkvec2(ZX_shifti(gel(v,2),1), ZX_shifti(RgX_shift_shallow(gel(v,3),1),1));
+  return gen_Z2X_Dixon(F, V, M, NULL, _can_lin, _can_lins, _can_invls);
+}
+
+/* Lift P to Q such that Q(x^2)=Q(x)*Q(-x) mod 2^n
+   if Q = Q0(X^2)+X*Q1(X^2), solve Q(x^2) = Q0^2-X*Q1^2
+*/
+static GEN
+F2x_canonlift(GEN P, long n)
+{ return gen_ZpX_Newton(F2x_to_ZX(P),gen_2, n, NULL, _can_iter, _can_invd); }
+
+static GEN
+Z2XQ_frob(GEN x, GEN T, GEN q)
+{
+  return FpX_rem(RgX_inflate(x, 2), T, q);
+}
+
+static GEN
+Z2xq_frob(GEN x, GEN T, ulong q)
+{
+  return Flx_rem(Flx_inflate(x, 2), T, q);
+}
+
+struct _frob_lift
+{
+  GEN T, sqx;
+};
+
+/* H -> S^-1(H) mod 2 */
+
+static GEN _frob_invls(void *E, GEN V)
+{
+  struct _frob_lift *F = (struct _frob_lift*) E;
+  GEN sqx = F->sqx;
+  return F2x_to_Flx(F2xq_sqrt_fast(Flx_to_F2x(V), gel(sqx,1), gel(sqx,2)));
+}
+
+/* H -> f1*S(H) + f2*H */
+
+static GEN _frob_lin(void *E, GEN F, GEN x2, long N)
+{
+  GEN T = gel(F,3);
+  GEN q = int2n(N);
+  GEN y2  = Z2XQ_frob(x2, T, q);
+  GEN lin = ZX_add(ZX_mul(gel(F,1), y2), ZX_mul(gel(F,2), x2));
+  (void) E;
+  return FpX_rem(ZX_remi2n(lin, N), T, q);
+}
+
+static GEN _frob_lins(void *E, GEN F, GEN x2, long N)
+{
+  GEN T = gel(F,3);
+  ulong q = 1UL<<N;
+  GEN y2  = Z2xq_frob(x2, T, q);
+  GEN lin = Flx_add(Flx_mul(gel(F,1), y2,q), Flx_mul(gel(F,2), x2,q),q);
+  (void) E;
+  return Flx_rem(lin, T, q);
+}
+
+/* X -> P(X,S(X)) */
+
+static GEN
+_lift_iter(void *E, GEN x2, GEN q)
+{
+  struct _frob_lift *F = (struct _frob_lift*) E;
+  long N = expi(q);
+  GEN TN = ZXT_remi2n(F->T, N);
+  GEN y2 = Z2XQ_frob(x2, TN, q);
+  GEN x2y2 = FpX_rem(ZX_remi2n(ZX_mul(x2, y2), N), TN, q);
+  GEN s = ZX_add(ZX_add(x2, ZX_shifti(y2, 1)), ZX_shifti(x2y2, 3));
+  GEN V = ZX_add(ZX_add(ZX_sqr(s), y2), ZX_shifti(x2y2, 2));
+  return mkvec4(FpX_rem(ZX_remi2n(V, N), TN, q),x2,y2,s);
+}
+
+/* H -> Dx*H+Dy*S(H) */
+
+static GEN
+_lift_invd(void *E, GEN V, GEN v, GEN qM, long M)
+{
+  struct _frob_lift *F = (struct _frob_lift*) E;
+  GEN TM = ZXT_remi2n(F->T, M);
+  GEN x2 = gel(v,2), y2 = gel(v,3), s = gel(v,4), r;
+  GEN Dx = ZX_add(ZX_mul(ZX_Z_add(ZX_shifti(y2, 4), gen_2), s),
+                         ZX_shifti(y2, 2));
+  GEN Dy = ZX_add(ZX_Z_add(ZX_mul(ZX_Z_add(ZX_shifti(x2, 4), utoi(4)), s),
+                           gen_1), ZX_shifti(x2, 2));
+  Dx = FpX_rem(ZX_remi2n(Dx, M), TM, qM);
+  Dy = FpX_rem(ZX_remi2n(Dy, M), TM, qM);
+  r = mkvec3(Dy, Dx, TM);
+  return gen_Z2X_Dixon(r, V, M, E, _frob_lin, _frob_lins, _frob_invls);
+}
+
+/*
+  Let P(X,Y)=(X+2*Y+8*X*Y)^2+Y+4*X*Y
+  Solve   P(x,S(x))=0 [mod 2^n,T]
+  assuming  x = x0    [mod 2,T]
+
+  we set s = X+2*Y+8*X*Y, P = s^2+Y+4*X*Y
+  Dx = dP/dx = (16*s+4)*x+(4*s+1)
+  Dy = dP/dy = (16*y+2)*s+4*y
+*/
+
+static GEN
+solve_AGM_eqn(GEN x0, long n, GEN T, GEN sqx)
+{
+  struct _frob_lift F;
+  F.T=T; F.sqx=sqx;
+  return gen_ZpX_Newton(x0, gen_2, n, &F, _lift_iter, _lift_invd);
+}
+
+static GEN
+Z2XQ_invnorm_pcyc(GEN a, GEN T, long e)
+{
+  GEN q = int2n(e);
+  GEN z = ZpXQ_norm_pcyc(a, T, q, gen_2);
+  return Fp_inv(z, q);
+}
+
+/* Assume a = 1 [4] */
+static GEN
+Z2XQ_invnorm(GEN a, GEN T, long e)
+{
+  pari_timer ti;
+  GEN pe = int2n(e), s;
+  if (degpol(a)==0)
+    return Fp_inv(Fp_powu(gel(a,2), get_FpX_degree(T), pe), pe);
+  if (DEBUGLEVEL>=3) timer_start(&ti);
+  s = ZpXQ_log(a, T, gen_2, e);
+  if (DEBUGLEVEL>=3) timer_printf(&ti,"Z2XQ_log");
+  s = Fp_neg(FpXQ_trace(s, T, pe), pe);
+  if (DEBUGLEVEL>=3) timer_printf(&ti,"FpXQ_trace");
+  s = modii(gel(Qp_exp(cvtop(s, gen_2, e-2)),4),pe);
+  if (DEBUGLEVEL>=3) timer_printf(&ti,"Qp_exp");
+  return s;
+}
+
+/* Assume a2==0, so 4|E(F_p): if t^4 = a6 then (t,t^2) is of order 4
+   8|E(F_p) <=> trace(a6)==0
+ */
+
+static GEN
+F2xq_elltrace_Harley(GEN a6, GEN T2)
+{
+  pari_sp ltop = avma;
+  pari_timer ti;
+  GEN T, sqx;
+  GEN x, x2, t;
+  long n = F2x_degree(T2), N = ((n + 1)>>1) + 2;
+  long ispcyc;
+  if (n==1) return gen_m1;
+  if (n==2) return F2x_degree(a6) ? gen_1 : stoi(-3);
+  if (n==3) return F2x_degree(a6) ? (F2xq_trace(a6,T2) ?  stoi(-3): gen_1)
+                                  : stoi(5);
+  timer_start(&ti);
+  sqx = mkvec2(F2xq_sqrt(polx_F2x(T2[1]),T2), T2);
+  if (DEBUGLEVEL>1) timer_printf(&ti,"Sqrtx");
+  ispcyc = zx_is_pcyc(F2x_to_Flx(T2));
+  T = ispcyc? F2x_to_ZX(T2): F2x_canonlift(T2, N-2);
+  if (DEBUGLEVEL>1) timer_printf(&ti,"Teich");
+  T = FpX_get_red(T, int2n(N));
+  if (DEBUGLEVEL>1) timer_printf(&ti,"Barrett");
+  x = solve_AGM_eqn(F2x_to_ZX(a6), N-2, T, sqx);
+  if (DEBUGLEVEL>1) timer_printf(&ti,"Lift");
+  x2 = ZX_Z_add_shallow(ZX_shifti(x,2), gen_1);
+  t = (ispcyc? Z2XQ_invnorm_pcyc: Z2XQ_invnorm)(x2, T, N);
+  if (DEBUGLEVEL>1) timer_printf(&ti,"Norm");
+  if (cmpii(sqri(t), int2n(n + 2)) > 0)
+    t = subii(t, int2n(N));
+  return gerepileuptoint(ltop, t);
+}
+
+GEN
+F2xq_ellcard(GEN a, GEN a6, GEN T)
+{
+  pari_sp av = avma;
+  long n = F2x_degree(T);
+  GEN q = int2u(n), c;
+  if (typ(a)==t_VECSMALL)
+  {
+    GEN t = F2xq_elltrace_Harley(a6, T);
+    c = addii(q, F2xq_trace(a,T) ? addui(1,t): subui(1,t));
+  } else if (n==1)
+  {
+    long a4i = lgpol(gel(a,2)), a6i = lgpol(a6);
+    return utoi(a4i? (a6i? 1: 5): 3);
+  }
+  else if (n==2)
+  {
+    GEN a3 = gel(a,1), a4 = gel(a,2), x = polx_F2x(T[1]), x1 = pol1_F2x(T[1]);
+    GEN a613 = F2xq_mul(F2x_add(x1, a6),a3,T), a43= F2xq_mul(a4,a3,T);
+    long f0= F2xq_trace(F2xq_mul(a6,a3,T),T);
+    long f1= F2xq_trace(F2x_add(a43,a613),T);
+    long f2= F2xq_trace(F2x_add(F2xq_mul(a43,x,T),a613),T);
+    long f3= F2xq_trace(F2x_add(F2xq_mul(a43,F2x_add(x,x1),T),a613),T);
+    c = utoi(9-2*(f0+f1+f2+f3));
+  }
+  else
+  {
+    struct _F2xqE e;
+    long m = (n+1)>>1;
+    GEN q1 = addis(q, 1);
+    GEN v = n==4 ? mkvec4s(13,17,21,25)
+                 : odd(n) ? mkvec3(subii(q1,int2u(m)),q1,addii(q1,int2u(m))):
+                            mkvec5(subii(q1,int2u(m+1)),subii(q1,int2u(m)),q1,
+                                   addii(q1,int2u(m)),addii(q1,int2u(m+1)));
+    e.a2=a; e.a6=a6; e.T=T;
+    c = gen_select_order(v,(void*)&e, &F2xqE_group);
+    if (n==4 && equaliu(c, 21)) /* Ambiguous case */
+    {
+      GEN d = F2xq_powu(polx_F2x(T[1]),3,T), a3 = gel(a,1);
+      e.a6 = F2x_add(a6,F2xq_mul(d,F2xq_sqr(a3,T),T)); /* twist */
+      c = subui(34, gen_select_order(mkvec2s(13,25),(void*)&e, &F2xqE_group));
+    }
+  }
+  return gerepileuptoint(av, c);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          Group structure                          **/
+/**                                                                   **/
+/***********************************************************************/
+
+static GEN
+_F2xqE_pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)
+{
+  struct _F2xqE *e = (struct _F2xqE *) E;
+  return  F2xq_order(F2xqE_weilpairing(P,Q,m,e->a2,e->T), F, e->T);
+}
+
+GEN
+F2xq_ellgroup(GEN a2, GEN a6, GEN N, GEN T, GEN *pt_m)
+{
+  struct _F2xqE e;
+  GEN q = int2u(F2x_degree(T));
+  e.a2=a2; e.a6=a6; e.T=T;
+  return gen_ellgroup(N, subis(q,1), pt_m, (void*)&e, &F2xqE_group,
+                                                      _F2xqE_pairorder);
+}
+
+GEN
+F2xq_ellgens(GEN a2, GEN a6, GEN ch, GEN D, GEN m, GEN T)
+{
+  GEN P;
+  pari_sp av = avma;
+  struct _F2xqE e;
+  e.a2=a2; e.a6=a6; e.T=T;
+  switch(lg(D)-1)
+  {
+  case 0:
+    return cgetg(1,t_VEC);
+  case 1:
+    P = gen_gener(gel(D,1), (void*)&e, &F2xqE_group);
+    P = mkvec(F2xqE_changepoint(P, ch, T));
+    break;
+  default:
+    P = gen_ellgens(gel(D,1), gel(D,2), m, (void*)&e, &F2xqE_group,
+                                                      _F2xqE_pairorder);
+    gel(P,1) = F2xqE_changepoint(gel(P,1), ch, T);
+    gel(P,2) = F2xqE_changepoint(gel(P,2), ch, T);
+    break;
+  }
+  return gerepilecopy(av, P);
+}
diff --git a/src/basemath/FF.c b/src/basemath/FF.c
new file mode 100644
index 0000000..1dfb9aa
--- /dev/null
+++ b/src/basemath/FF.c
@@ -0,0 +1,1776 @@
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*************************************************************************/
+/**                                                                     **/
+/**                   Routines for handling FFELT                       **/
+/**                                                                     **/
+/*************************************************************************/
+
+INLINE void
+_getFF(GEN x, GEN *T, GEN *p, ulong *pp)
+{
+  *T=gel(x,3);
+  *p=gel(x,4);
+  *pp=(*p)[2];
+}
+
+INLINE GEN
+_initFF(GEN x, GEN *T, GEN *p, ulong *pp)
+{
+  _getFF(x,T,p,pp);
+  return cgetg(5,t_FFELT);
+}
+
+INLINE void
+_checkFF(GEN x, GEN y, const char *s)
+{ if (!FF_samefield(x,y)) pari_err_OP(s,x,y); }
+
+INLINE GEN
+_mkFF(GEN x, GEN z, GEN r)
+{
+  z[1]=x[1];
+  gel(z,2)=r;
+  gel(z,3)=gcopy(gel(x,3));
+  gel(z,4)=icopy(gel(x,4));
+  return z;
+}
+
+INLINE GEN
+_mkFF_i(GEN x, GEN z, GEN r)
+{
+  z[1]=x[1];
+  gel(z,2)=r;
+  gel(z,3)=gel(x,3);
+  gel(z,4)=gel(x,4);
+  return z;
+}
+
+INLINE GEN
+mkFF_i(GEN x, GEN r)
+{
+  GEN z = cgetg(5,t_FFELT);
+  return _mkFF_i(x,z,r);
+}
+
+/* Return true if x and y are defined in the same field */
+int
+FF_samefield(GEN x, GEN y)
+{
+  return x[1] == y[1] && equalii(gel(x,4),gel(y,4))
+                      && gidentical(gel(x,3),gel(y,3));
+}
+
+int
+FF_equal(GEN x, GEN y)
+{ return FF_samefield(x,y) && gidentical(gel(x,2),gel(y,2)); }
+
+int
+FF_equal0(GEN x)
+{
+  return lgpol(gel(x,2))==0;
+}
+
+int
+FF_equal1(GEN x)
+{
+  GEN A = gel(x,2);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return degpol(A)==0 && gequal1(gel(A,2));
+  default:
+    return degpol(A)==0 && A[2]==1;
+  }
+}
+
+static int
+Fp_cmp_1(GEN x, GEN p)
+{
+  pari_sp av = avma;
+  int b = equalii(x, addis(p,-1));
+  avma = av; return b;
+}
+
+int
+FF_equalm1(GEN x)
+{
+  ulong pp;
+  GEN T, p, y = gel(x,2);
+  _getFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return (degpol(y) == 0 && Fp_cmp_1(gel(y,2), p));
+  default:
+    return (degpol(y) == 0 && (ulong)y[2] == pp-1);
+  }
+}
+
+GEN
+FF_zero(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=zeropol(varn(T));
+    break;
+  case t_FF_F2xq:
+    r=zero_F2x(T[1]);
+    break;
+  default:
+    r=zero_Flx(T[1]);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_1(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=pol_1(varn(T));
+    break;
+  case t_FF_F2xq:
+    r=pol1_F2x(T[1]);
+    break;
+  default:
+    r=pol1_Flx(T[1]);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_q(GEN x)
+{
+  ulong pp;
+  GEN T, p;
+  _getFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return powiu(p, degpol(T));
+    break;
+  case t_FF_F2xq:
+    return int2n(F2x_degree(T));
+    break;
+  default:
+    return powuu(pp,degpol(T));
+  }
+}
+
+GEN
+FF_p(GEN x)
+{
+  return icopy(gel(x,4));
+}
+
+GEN
+FF_p_i(GEN x)
+{
+  return gel(x,4);
+}
+
+GEN
+FF_mod(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return ZX_copy(gel(x,3));
+  case t_FF_F2xq:
+    return F2x_to_ZX(gel(x,3));
+  default:
+    return Flx_to_ZX(gel(x,3));
+  }
+}
+
+long
+FF_f(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_F2xq:
+    return F2x_degree(gel(x,3));
+  default:
+    return degpol(gel(x,3));
+  }
+}
+
+GEN
+FF_to_F2xq(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return ZX_to_F2x(gel(x,2));
+  case t_FF_F2xq:
+    return zv_copy(gel(x,2));
+  default:
+    return Flx_to_F2x(gel(x,2));
+  }
+}
+
+GEN
+FF_to_F2xq_i(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return ZX_to_F2x(gel(x,2));
+  case t_FF_F2xq:
+    return gel(x,2);
+  default:
+    return Flx_to_F2x(gel(x,2));
+  }
+}
+
+GEN
+FF_to_Flxq(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return ZX_to_Flx(gel(x,2),itou(gel(x,4)));
+  case t_FF_F2xq:
+    return F2x_to_Flx(gel(x,2));
+  default:
+    return zv_copy(gel(x,2));
+  }
+}
+
+GEN
+FF_to_Flxq_i(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return ZX_to_Flx(gel(x,2),itou(gel(x,4)));
+  case t_FF_F2xq:
+    return F2x_to_Flx(gel(x,2));
+  default:
+    return gel(x,2);
+  }
+}
+
+GEN
+FF_to_FpXQ(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return ZX_copy(gel(x,2));
+  case t_FF_F2xq:
+    return F2x_to_ZX(gel(x,2));
+  default:
+    return Flx_to_ZX(gel(x,2));
+  }
+}
+
+GEN
+FF_to_FpXQ_i(GEN x)
+{
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return gel(x,2);
+  case t_FF_F2xq:
+    return F2x_to_ZX(gel(x,2));
+  default:
+    return Flx_to_ZX(gel(x,2));
+  }
+}
+
+GEN
+FF_add(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  _checkFF(x,y,"+");
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=FpX_add(gel(x,2),gel(y,2),p);
+    break;
+  case t_FF_F2xq:
+    r=F2x_add(gel(x,2),gel(y,2));
+    break;
+  default:
+    r=Flx_add(gel(x,2),gel(y,2),pp);
+  }
+  return _mkFF(x,z,r);
+}
+GEN
+FF_sub(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  _checkFF(x,y,"+");
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=FpX_sub(gel(x,2),gel(y,2),p);
+    break;
+  case t_FF_F2xq:
+    r=F2x_add(gel(x,2),gel(y,2));
+    break;
+  default:
+    r=Flx_sub(gel(x,2),gel(y,2),pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_Z_add(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    {
+      pari_sp av=avma;
+      r=gerepileupto(av,FpX_Fp_add(gel(x,2),modii(y,p),p));
+      break;
+    }
+  case t_FF_F2xq:
+    r=mpodd(y)?F2x_1_add(gel(x,2)):vecsmall_copy(gel(x,2));
+    break;
+  default:
+    r=Flx_Fl_add(gel(x,2),umodiu(y,pp),pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_Q_add(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    {
+      pari_sp av=avma;
+      r=gerepileupto(av,FpX_Fp_add(gel(x,2),Rg_to_Fp(y,p),p));
+      break;
+    }
+  case t_FF_F2xq:
+    r=Rg_to_Fl(y,pp)?F2x_1_add(gel(x,2)):vecsmall_copy(gel(x,2));
+    break;
+  default:
+    r=Flx_Fl_add(gel(x,2),Rg_to_Fl(y,pp),pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_neg(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=FpX_neg(gel(x,2),p);
+    break;
+  case t_FF_F2xq:
+    r=vecsmall_copy(gel(x,2));
+    break;
+  default:
+    r=Flx_neg(gel(x,2),pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_neg_i(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=FpX_neg(gel(x,2),p);
+    break;
+  case t_FF_F2xq:
+    r=gel(x,2);
+    break;
+  default:
+    r=Flx_neg(gel(x,2),pp);
+  }
+  return _mkFF_i(x,z,r);
+}
+
+GEN
+FF_mul(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  _checkFF(x,y,"*");
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    {
+      pari_sp av=avma;
+      r=gerepileupto(av,FpXQ_mul(gel(x,2),gel(y,2),T,p));
+      break;
+    }
+  case t_FF_F2xq:
+    r=F2xq_mul(gel(x,2),gel(y,2),T);
+    break;
+  default:
+    r=Flxq_mul(gel(x,2),gel(y,2),T,pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_Z_mul(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, A = gel(x,2), z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ: /* modii(y,p) left on stack for efficiency */
+    r = FpX_Fp_mul(A, modii(y,p),p);
+    break;
+  case t_FF_F2xq:
+    r = mpodd(y)? vecsmall_copy(A): zero_Flx(A[1]);
+    break;
+  default:
+    r = Flx_Fl_mul(A, umodiu(y,pp), pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_Z_Z_muldiv(GEN x, GEN a, GEN b)
+{
+  ulong pp;
+  GEN r, T, p, A = gel(x,2), z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ: /* Fp_div(a,b,p) left on stack for efficiency */
+    r = FpX_Fp_mul(A, Fp_div(a,b,p), p);
+    break;
+  case t_FF_F2xq:
+    if (!mpodd(b)) pari_err_INV("FF_Z_Z_muldiv", b);
+    r = mpodd(a)? vecsmall_copy(A): zero_Flx(A[1]);
+    break;
+  default:
+    r = Flx_Fl_mul(A, Fl_div(umodiu(a,pp),umodiu(b,pp),pp),pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_sqr(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    {
+      pari_sp av=avma;
+      r=gerepileupto(av,FpXQ_sqr(gel(x,2),T,p));
+      break;
+    }
+  case t_FF_F2xq:
+    r=F2xq_sqr(gel(x,2),T);
+    break;
+  default:
+    r=Flxq_sqr(gel(x,2),T,pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_mul2n(GEN x, long n)
+{
+  ulong pp;
+  GEN r, T, p, A = gel(x,2), z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    {
+      GEN p1; /* left on stack for efficiency */
+      if (n>0) p1=remii(int2n(n),p);
+      else p1=Fp_inv(remii(int2n(-n),p),p);
+      r = FpX_Fp_mul(A, p1, p);
+    }
+    break;
+  case t_FF_F2xq:
+    if (n<0) pari_err_INV("FF_mul2n", gen_2);
+    r = n==0? vecsmall_copy(A): zero_Flx(A[1]);
+    break;
+  default:
+    {
+      ulong l1;
+      if (n>0) l1 = umodiu(int2n(n),pp);
+      else l1 = Fl_inv(umodiu(int2n(-n),pp),pp);
+      r = Flx_Fl_mul(A,l1,pp);
+    }
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_inv(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  pari_sp av=avma;
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=gerepileupto(av,FpXQ_inv(gel(x,2),T,p));
+    break;
+  case t_FF_F2xq:
+    r=F2xq_inv(gel(x,2),T);
+    break;
+  default:
+    r=Flxq_inv(gel(x,2),T,pp);
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_div(GEN x, GEN y)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  pari_sp av=avma;
+  _checkFF(x,y,"/");
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r=gerepileupto(av,FpXQ_div(gel(x,2),gel(y,2),T,p));
+    break;
+  case t_FF_F2xq:
+    r=gerepileupto(av,F2xq_div(gel(x,2),gel(y,2),T));
+    break;
+  default:
+    r=gerepileupto(av,Flxq_div(gel(x,2),gel(y,2),T,pp));
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+Z_FF_div(GEN n, GEN x)
+{
+  ulong pp;
+  GEN r, T, p, A = gel(x,2), z=_initFF(x,&T,&p,&pp);
+  pari_sp av=avma;
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r = gerepileupto(av,FpX_Fp_mul(FpXQ_inv(A,T,p),modii(n,p),p));
+    break;
+  case t_FF_F2xq:
+    r = F2xq_inv(A,T); /*Check for division by 0*/
+    if(!mpodd(n)) { avma = av; r = zero_Flx(A[1]); }
+    break;
+  default:
+    r = gerepileupto(av, Flx_Fl_mul(Flxq_inv(A,T,pp),umodiu(n,pp),pp));
+  }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_sqrtn(GEN x, GEN n, GEN *zetan)
+{
+  ulong pp;
+  GEN r, T, p, y=_initFF(x,&T,&p,&pp);
+  switch (x[1])
+  {
+  case t_FF_FpXQ:
+    r=FpXQ_sqrtn(gel(x,2),n,T,p,zetan);
+    break;
+  case t_FF_F2xq:
+    r=F2xq_sqrtn(gel(x,2),n,T,zetan);
+    break;
+  default:
+    r=Flxq_sqrtn(gel(x,2),n,T,pp,zetan);
+  }
+  if (!r) pari_err_SQRTN("FF_sqrtn",x);
+  (void)_mkFF(x, y, r);
+  if (zetan)
+  {
+    GEN z = cgetg(lg(y),t_FFELT);
+    *zetan=_mkFF(x, z, *zetan);
+  }
+  return y;
+}
+
+GEN
+FF_sqrt(GEN x)
+{
+  ulong pp;
+  GEN r, T, p, y=_initFF(x,&T,&p,&pp);
+  switch (x[1])
+  {
+  case t_FF_FpXQ:
+    r = FpXQ_sqrt(gel(x,2),T,p);
+    break;
+  case t_FF_F2xq:
+    r = F2xq_sqrt(gel(x,2),T);
+    break;
+  default:
+    r = Flxq_sqrt(gel(x,2),T,pp);
+  }
+  if (!r) pari_err_SQRTN("FF_sqrt",x);
+  return _mkFF(x, y, r);
+}
+
+long
+FF_issquare(GEN x)
+{
+  GEN T, p;
+  ulong pp;
+  _getFF(x, &T, &p, &pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return FpXQ_issquare(gel(x,2), T, p);
+  case t_FF_F2xq:
+    return 1;
+  default: /* case t_FF_Flxq: */
+    return Flxq_issquare(gel(x,2), T, pp);
+  }
+}
+
+long
+FF_issquareall(GEN x, GEN *pt)
+{
+  if (!pt) return FF_issquare(x);
+  return FF_ispower(x, gen_2, pt);
+}
+
+long
+FF_ispower(GEN x, GEN K, GEN *pt)
+{
+  ulong pp;
+  GEN r, T, p;
+  pari_sp av = avma;
+
+  if (FF_equal0(x)) { if (pt) *pt = gcopy(x); return 1; }
+  _getFF(x, &T, &p, &pp);
+  if (pt) *pt = cgetg(5,t_FFELT);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r = FpXQ_sqrtn(gel(x,2),K,T,p,NULL);
+    break;
+  case t_FF_F2xq:
+    r = F2xq_sqrtn(gel(x,2),K,T,NULL);
+    break;
+  default: /* case t_FF_Flxq: */
+    r = Flxq_sqrtn(gel(x,2),K,T,pp,NULL);
+    break;
+  }
+  if (!r) { avma = av; return 0; }
+  if (pt) { (void)_mkFF(x,*pt,r); }
+  return 1;
+}
+
+GEN
+FF_pow(GEN x, GEN n)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+   {
+   case t_FF_FpXQ:
+     r = FpXQ_pow(gel(x,2), n, T, p);
+     break;
+   case t_FF_F2xq:
+     r = F2xq_pow(gel(x,2), n, T);
+     break;
+   default:
+     r = Flxq_pow(gel(x,2), n, T, pp);
+   }
+  return _mkFF(x,z,r);
+}
+
+GEN
+FF_norm(GEN x)
+{
+  ulong pp;
+  GEN T,p;
+  _getFF(x,&T,&p,&pp);
+  switch (x[1])
+  {
+  case t_FF_FpXQ:
+    return FpXQ_norm(gel(x,2),T,p);
+  case t_FF_F2xq:
+    return lgpol(gel(x,2))?gen_1:gen_0;
+  default:
+    return utoi(Flxq_norm(gel(x,2),T,pp));
+  }
+}
+
+GEN
+FF_trace(GEN x)
+{
+  ulong pp;
+  GEN T,p;
+  _getFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return FpXQ_trace(gel(x,2),T,p);
+  case t_FF_F2xq:
+    return F2xq_trace(gel(x,2),T)?gen_1:gen_0;
+  default:
+    return utoi(Flxq_trace(gel(x,2),T,pp));
+  }
+}
+
+GEN
+FF_conjvec(GEN x)
+{
+  ulong pp;
+  GEN r,T,p,v;
+  long i,l;
+  pari_sp av;
+  _getFF(x,&T,&p,&pp);
+  av = avma;
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    v = FpXQ_conjvec(gel(x,2), T, p);
+    break;
+  case t_FF_F2xq:
+    v = F2xq_conjvec(gel(x,2), T);
+    break;
+  default:
+    v = Flxq_conjvec(gel(x,2), T, pp);
+  }
+  l = lg(v); r = cgetg(l, t_COL);
+  for(i=1; i<l; i++)
+    gel(r,i) = mkFF_i(x, gel(v,i));
+  return gerepilecopy(av, r);
+}
+
+GEN
+FF_charpoly(GEN x)
+{
+  ulong pp;
+  GEN T,p;
+  pari_sp av=avma;
+  _getFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return gerepileupto(av,FpXQ_charpoly(gel(x,2), T, p));
+  case t_FF_F2xq:
+    return gerepileupto(av,Flx_to_ZX(Flxq_charpoly(F2x_to_Flx(gel(x,2)),
+                                                   F2x_to_Flx(T), 2UL)));
+  default:
+    return gerepileupto(av,Flx_to_ZX(Flxq_charpoly(gel(x,2), T, pp)));
+  }
+}
+
+GEN
+FF_minpoly(GEN x)
+{
+  ulong pp;
+  GEN T,p;
+  pari_sp av=avma;
+  _getFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    return gerepileupto(av,FpXQ_minpoly(gel(x,2), T, p));
+  case t_FF_F2xq:
+    return gerepileupto(av,Flx_to_ZX(Flxq_minpoly(F2x_to_Flx(gel(x,2)),
+                                                  F2x_to_Flx(T), 2UL)));
+  default:
+    return gerepileupto(av,Flx_to_ZX(Flxq_minpoly(gel(x,2), T, pp)));
+  }
+}
+
+GEN
+FF_log(GEN x, GEN g, GEN ord)
+{
+  pari_sp av=avma;
+  ulong pp;
+  GEN r, T, p;
+  _getFF(x,&T,&p,&pp);
+  _checkFF(x,g,"log");
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    if (!ord) ord = factor_pn_1(p,degpol(T));
+    r = FpXQ_log(gel(x,2), gel(g,2), ord, T, p);
+    break;
+  case t_FF_F2xq:
+    if (!ord) ord = factor_pn_1(gen_2,F2x_degree(T));
+    r = F2xq_log(gel(x,2), gel(g,2), ord, T);
+    break;
+  default:
+    if (!ord) ord = factor_pn_1(p,degpol(T));
+    r = Flxq_log(gel(x,2), gel(g,2), ord, T, pp);
+  }
+  return gerepileupto(av, r);
+}
+
+GEN
+FF_order(GEN x, GEN o)
+{
+  pari_sp av=avma;
+  ulong pp;
+  GEN r, T,p;
+  _getFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    if (!o) o = factor_pn_1(p,degpol(T));
+    r = FpXQ_order(gel(x,2), o, T, p);
+    break;
+  case t_FF_F2xq:
+    if (!o) o = factor_pn_1(gen_2,F2x_degree(T));
+    r = F2xq_order(gel(x,2), o, T);
+    break;
+  default:
+    if (!o) o = factor_pn_1(p,degpol(T));
+    r = Flxq_order(gel(x,2), o, T, pp);
+  }
+  if (!o) r = gerepileuptoint(av,r);
+  return r;
+}
+
+GEN
+FF_primroot(GEN x, GEN *o)
+{
+  ulong pp;
+  GEN r,T,p,z=_initFF(x,&T,&p,&pp);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    r = gener_FpXQ(T, p, o);
+    break;
+  case t_FF_F2xq:
+    r = gener_F2xq(T, o);
+    break;
+  default:
+    r = gener_Flxq(T, pp, o);
+  }
+  return _mkFF(x,z,r);
+}
+
+static GEN
+to_FFE(GEN P, GEN fg)
+{
+  if(ell_is_inf(P))
+    return ellinf();
+  else
+    retmkvec2(mkFF_i(fg,gel(P,1)), mkFF_i(fg,gel(P,2)));
+}
+
+static GEN
+to_FFE_vec(GEN x, GEN ff)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++) gel(x,i) = to_FFE(gel(x,i), ff);
+  return x;
+}
+
+static GEN
+FpXQ_ell_to_a4a6(GEN E, GEN T, GEN p)
+{
+  GEN a1, a3, b2, c4, c6;
+  a1 = Rg_to_FpXQ(ell_get_a1(E),T,p);
+  a3 = Rg_to_FpXQ(ell_get_a3(E),T,p);
+  b2 = Rg_to_FpXQ(ell_get_b2(E),T,p);
+  c4 = Rg_to_FpXQ(ell_get_c4(E),T,p);
+  c6 = Rg_to_FpXQ(ell_get_c6(E),T,p);
+  retmkvec3(FpX_neg(FpX_mulu(c4, 27, p), p), FpX_neg(FpX_mulu(c6, 54, p), p),
+            mkvec4(Z_to_FpX(utoi(6),p,varn(T)),FpX_mulu(b2,3,p),
+                   FpX_mulu(a1,3,p),FpX_mulu(a3,108,p)));
+}
+
+static GEN
+F3xq_ell_to_a4a6(GEN E, GEN T)
+{
+  GEN a1, a3, b2, b4, b6;
+  a1 = Rg_to_Flxq(ell_get_a1(E),T,3);
+  a3 = Rg_to_Flxq(ell_get_a3(E),T,3);
+  b2 = Rg_to_Flxq(ell_get_b2(E),T,3);
+  b4 = Rg_to_Flxq(ell_get_b4(E),T,3);
+  b6 = Rg_to_Flxq(ell_get_b6(E),T,3);
+  if(lgpol(b2)) /* ordinary case */
+  {
+    GEN b4b2 = Flxq_div(b4,b2,T,3);
+    GEN a6 = Flx_sub(b6,Flxq_mul(b4b2,Flx_add(b4,Flxq_sqr(b4b2,T,3),3),T,3),3);
+    retmkvec3(mkvec(b2), a6,
+       mkvec4(Fl_to_Flx(1,T[1]),b4b2,Flx_neg(a1,3),Flx_neg(a3,3)));
+  }
+  else /* super-singular case */
+    retmkvec3(Flx_neg(b4, 3), b6,
+       mkvec4(Fl_to_Flx(1,T[1]),zero_Flx(T[1]), Flx_neg(a1,3), Flx_neg(a3,3)));
+}
+
+static GEN
+Flxq_ell_to_a4a6(GEN E, GEN T, ulong p)
+{
+  GEN a1, a3, b2, c4, c6;
+  if(p==3) return F3xq_ell_to_a4a6(E, T);
+  a1 = Rg_to_Flxq(ell_get_a1(E),T,p);
+  a3 = Rg_to_Flxq(ell_get_a3(E),T,p);
+  b2 = Rg_to_Flxq(ell_get_b2(E),T,p);
+  c4 = Rg_to_Flxq(ell_get_c4(E),T,p);
+  c6 = Rg_to_Flxq(ell_get_c6(E),T,p);
+  retmkvec3(Flx_neg(Flx_mulu(c4, 27, p), p), Flx_neg(Flx_mulu(c6, 54, p), p),
+            mkvec4(Fl_to_Flx(6%p,T[1]),Flx_mulu(b2,3,p),
+                   Flx_mulu(a1,3,p),Flx_mulu(a3,108,p)));
+}
+
+static GEN
+F2xq_ell_to_a4a6(GEN E, GEN T)
+{
+  long v = T[1];
+  GEN a1 = Rg_to_F2xq(ell_get_a1(E),T);
+  GEN a2 = Rg_to_F2xq(ell_get_a2(E),T);
+  GEN a3 = Rg_to_F2xq(ell_get_a3(E),T);
+  GEN a4 = Rg_to_F2xq(ell_get_a4(E),T);
+  GEN a6 = Rg_to_F2xq(ell_get_a6(E),T);
+  if (lgpol(a1))
+  {
+    GEN a1i = F2xq_inv(a1,T);
+    GEN a1i2 = F2xq_sqr(a1i,T);
+    GEN a1i3 = F2xq_mul(a1i,a1i2,T);
+    GEN a1i6 = F2xq_sqr(a1i3,T);
+    GEN d  = F2xq_mul(a3,a1i,T);
+    GEN dd = F2xq_mul(d,a1i2,T);
+    GEN e  = F2xq_mul(F2x_add(a4,F2xq_sqr(d,T)),a1i,T);
+    GEN ee = F2xq_mul(e,a1i3,T);
+    GEN da2 = F2x_add(a2,d);
+    GEN d2 = F2xq_mul(da2,a1i2,T);
+    GEN d6 = F2xq_mul(F2x_add(F2x_add(F2xq_mul(F2x_add(F2xq_mul(da2,d,T),a4),d,T),a6),F2xq_sqr(e,T)),a1i6,T);
+    retmkvec3(d2, d6, mkvec4(a1i,dd,pol0_F2x(v),ee));
+  }
+  else if (lgpol(a3))
+  {
+    GEN d4 = F2x_add(F2xq_sqr(a2,T),a4);
+    GEN d6 = F2x_add(F2xq_mul(a2,a4,T),a6);
+    GEN a3i = F2xq_inv(a3,T);
+    retmkvec3(mkvec3(a3,d4,a3i), d6, mkvec4(pol1_F2x(v),a2,pol0_F2x(T[1]),pol0_F2x(T[1])));
+  }
+  return NULL;
+}
+
+static GEN
+Fq_to_FpXQ(GEN x, GEN T)
+{
+  if (typ(x)==t_INT)
+    return scalarpol(x, varn(T));
+  else
+    return x;
+}
+
+static GEN
+FqV_to_FpXQV(GEN x, GEN T)
+{
+  pari_sp av = avma;
+  long v = varn(T), i, s=0, l = lg(x);
+  GEN y = shallowcopy(x);
+  for(i=1; i<l; i++)
+    if (typ(gel(x,i))==t_INT)
+    {
+      gel(y,i) = scalarpol(gel(x,i), v);
+      s = 1;
+    }
+  if (!s) { avma = av; return x; }
+  return y;
+}
+
+GEN
+FF_ellcard(GEN E)
+{
+  GEN T,p;
+  ulong pp;
+  GEN e = ellff_get_a4a6(E);
+  GEN fg = ellff_get_field(E);
+  _getFF(fg,&T,&p,&pp);
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    return FpXQ_ellcard(Fq_to_FpXQ(gel(e,1),T), Fq_to_FpXQ(gel(e,2),T),T,p);
+  case t_FF_F2xq:
+    return F2xq_ellcard(gel(e,1),gel(e,2),T);
+  default:
+    return Flxq_ellcard(gel(e,1),gel(e,2),T,pp);
+  }
+}
+
+/* return [G,m] */
+GEN
+FF_ellgroup(GEN E)
+{
+  GEN T, p, e, fg, N, G, m;
+  ulong pp;
+
+  N = ellff_get_card(E);
+  e = ellff_get_a4a6(E);
+  fg = ellff_get_field(E);
+  _getFF(fg,&T,&p,&pp);
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    G = FpXQ_ellgroup(Fq_to_FpXQ(gel(e,1),T),Fq_to_FpXQ(gel(e,2),T),N,T,p,&m);
+    break;
+  case t_FF_F2xq:
+    G = F2xq_ellgroup(gel(e,1),gel(e,2),N,T,&m); break;
+  default:
+    G = Flxq_ellgroup(gel(e,1),gel(e,2),N,T,pp,&m); break;
+  }
+  return mkvec2(G,m);
+}
+
+GEN
+FF_ellgens(GEN E)
+{
+  GEN fg, e, Gm, G, m, T, p, F, e3;
+  ulong pp;
+
+  fg = ellff_get_field(E);
+  e = ellff_get_a4a6(E);
+  Gm = ellff_get_group(E); G = gel(Gm,1); m = gel(Gm,2);
+  _getFF(fg,&T,&p,&pp);
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    e3 = FqV_to_FpXQV(gel(e,3),T);
+    F = FpXQ_ellgens(Fq_to_FpXQ(gel(e,1),T),Fq_to_FpXQ(gel(e,2),T),e3,G,m,T,p);
+    break;
+  case t_FF_F2xq:
+    F = F2xq_ellgens(gel(e,1),gel(e,2),gel(e,3),G,m,T);
+    break;
+  default:
+    F = Flxq_ellgens(gel(e,1),gel(e,2),gel(e,3),G,m,T, pp);
+    break;
+  }
+  return to_FFE_vec(F,fg);
+}
+
+GEN
+FF_elldata(GEN E, GEN fg)
+{
+  GEN T,p,e;
+  ulong pp;
+  _getFF(fg,&T,&p,&pp);
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    e = FpXQ_ell_to_a4a6(E,T,p); break;
+  case t_FF_F2xq:
+    e = F2xq_ell_to_a4a6(E,T); break;
+  default:
+    e = Flxq_ell_to_a4a6(E,T,pp); break;
+  }
+  return mkvec2(fg,e);
+}
+
+GEN
+FF_ellinit(GEN E, GEN fg)
+{
+  GEN T,p,e;
+  ulong pp;
+  long i;
+  GEN y=E;
+  _getFF(fg,&T,&p,&pp);
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    e = FpXQ_ell_to_a4a6(E,T,p);
+    for(i=1;i<=12;i++)
+      gel(y,i) = mkFF_i(fg,Rg_to_FpXQ(gel(E,i),T,p));
+    if (FF_equal0(gel(y,12))) return NULL;
+    gel(y,i) = mkFF_i(fg,Rg_to_FpXQ(gel(E,i),T,p));
+    break;
+  case t_FF_F2xq:
+    e = F2xq_ell_to_a4a6(E,T);
+    if (!e) return NULL;
+    for(i=1;i<=12;i++)
+      gel(y,i) = mkFF_i(fg,Rg_to_F2xq(gel(E,i),T));
+    if (FF_equal0(gel(y,12))) return NULL;
+    gel(y,i) = mkFF_i(fg,Rg_to_F2xq(gel(E,i),T));
+    break;
+  default:
+    e = Flxq_ell_to_a4a6(E,T,pp);
+    for(i=1;i<=12;i++)
+      gel(y,i) = mkFF_i(fg,Rg_to_Flxq(gel(E,i),T,pp));
+    if (FF_equal0(gel(y,12))) return NULL;
+    gel(y,i) = mkFF_i(fg,Rg_to_Flxq(gel(E,i),T,pp));
+    break;
+  }
+  gel(y,14) = mkvecsmall(t_ELL_Fq);
+  gel(y,15) = mkvec2(fg,e);
+  return y;
+}
+
+static long
+F3x_equalm1(GEN x)
+{ return degpol(x)==0 && x[2] == 2; }
+
+GEN
+FF_ellrandom(GEN E)
+{
+  pari_sp av = avma;
+  GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E), Q;
+  GEN T,p;
+  ulong pp;
+  _getFF(fg,&T,&p,&pp);
+  switch (fg[1])
+  {
+  case t_FF_FpXQ:
+    Q = random_FpXQE(Fq_to_FpXQ(gel(e,1),T), Fq_to_FpXQ(gel(e,2),T), T, p);
+    Q = FpXQE_changepoint(Q, FqV_to_FpXQV(gel(e,3), T) , T, p);
+    break;
+  case t_FF_F2xq:
+    {
+      long d = F2x_degree(T);
+      /* if #E(Fq) = 1 return [0] */
+      if (d<=2 && typ(gel(e,1)) == t_VEC)
+      { /* over F2 or F4, supersingular */
+        GEN v = gel(e,1), A6 = gel(e,2), a3 = gel(v,1), A4 = gel(v,2);
+        if (F2x_equal1(a3) &&
+             ((d==1 && F2x_equal1(A4) && F2x_equal1(A6))
+           || (d==2 && !lgpol(A4)     && F2x_degree(A6)==1))) return ellinf();
+      }
+      Q = random_F2xqE(gel(e,1), gel(e,2), T);
+      Q = F2xqE_changepoint(Q, gel(e,3), T);
+      break;
+    }
+  default:
+    /* if #E(Fq) = 1 return [0] */
+    if (pp==3 && degpol(T)==1 && typ(gel(e,1))==t_VECSMALL)
+    { /* over F3, supersingular */
+      GEN mb4 = gel(e,1), b6 = gel(e,2);
+      if (F3x_equalm1(mb4) && F3x_equalm1(b6)) return ellinf();
+    }
+    Q = random_FlxqE(gel(e,1), gel(e,2), T, pp);
+    Q = FlxqE_changepoint(Q, gel(e,3), T, pp);
+  }
+  return gerepilecopy(av, to_FFE(Q, fg));
+}
+
+GEN
+FF_ellmul(GEN E, GEN P, GEN n)
+{
+  pari_sp av = avma;
+  GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E), Q;
+  GEN T,p, Pp, Qp, e3;
+  ulong pp;
+  _getFF(fg,&T,&p,&pp);
+  switch (fg[1])
+  {
+  case t_FF_FpXQ:
+    e3 = FqV_to_FpXQV(gel(e,3),T);
+    Pp = FpXQE_changepointinv(RgE_to_FpXQE(P, T, p), e3, T, p);
+    Qp = FpXQE_mul(Pp, n, gel(e,1), T, p);
+    Q = FpXQE_changepoint(Qp, gel(e,3), T, p);
+    break;
+  case t_FF_F2xq:
+    Pp = F2xqE_changepointinv(RgE_to_F2xqE(P, T), gel(e,3), T);
+    Qp = F2xqE_mul(Pp, n, gel(e,1), T);
+    Q = F2xqE_changepoint(Qp, gel(e,3), T);
+    break;
+  default:
+    Pp = FlxqE_changepointinv(RgE_to_FlxqE(P, T, pp), gel(e,3), T, pp);
+    Qp = FlxqE_mul(Pp, n, gel(e,1), T, pp);
+    Q = FlxqE_changepoint(Qp, gel(e,3), T, pp);
+  }
+  return gerepilecopy(av, to_FFE(Q, fg));
+}
+
+GEN
+FF_ellorder(GEN E, GEN P, GEN o)
+{
+  pari_sp av = avma;
+  GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E);
+  GEN r,T,p,Pp,e3;
+  ulong pp;
+  _getFF(fg,&T,&p,&pp);
+  switch (fg[1])
+  {
+  case t_FF_FpXQ:
+    e3 = FqV_to_FpXQV(gel(e,3),T);
+    Pp = FpXQE_changepointinv(RgE_to_FpXQE(P,T,p), e3, T, p);
+    r = FpXQE_order(Pp, o, gel(e,1), T, p);
+    break;
+  case t_FF_F2xq:
+    Pp = F2xqE_changepointinv(RgE_to_F2xqE(P,T), gel(e,3), T);
+    r = F2xqE_order(Pp, o, gel(e,1), T);
+    break;
+  default:
+    Pp = FlxqE_changepointinv(RgE_to_FlxqE(P,T,pp), gel(e,3), T, pp);
+    r = FlxqE_order(Pp, o, gel(e,1), T, pp);
+  }
+  return gerepileupto(av, r);
+}
+
+GEN
+FF_elllog(GEN E, GEN P, GEN Q, GEN o)
+{
+  pari_sp av = avma;
+  GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E);
+  GEN r,T,p, Pp,Qp, e3;
+  ulong pp;
+  _getFF(fg,&T,&p,&pp);
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    e3 = FqV_to_FpXQV(gel(e,3),T);
+    Pp = FpXQE_changepointinv(RgE_to_FpXQE(P,T,p), e3, T, p);
+    Qp = FpXQE_changepointinv(RgE_to_FpXQE(Q,T,p), e3, T, p);
+    r = FpXQE_log(Pp, Qp, o, gel(e,1), T, p);
+    break;
+  case t_FF_F2xq:
+    Pp = F2xqE_changepointinv(RgE_to_F2xqE(P,T), gel(e,3), T);
+    Qp = F2xqE_changepointinv(RgE_to_F2xqE(Q,T), gel(e,3), T);
+    r = F2xqE_log(Pp, Qp, o, gel(e,1), T);
+    break;
+  default:
+    Pp = FlxqE_changepointinv(RgE_to_FlxqE(P,T,pp), gel(e,3), T, pp);
+    Qp = FlxqE_changepointinv(RgE_to_FlxqE(Q,T,pp), gel(e,3), T, pp);
+    r = FlxqE_log(Pp, Qp, o, gel(e,1), T, pp);
+  }
+  return gerepileupto(av, r);
+}
+
+GEN
+FF_ellweilpairing(GEN E, GEN P, GEN Q, GEN m)
+{
+  GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E);
+  GEN r,T,p, Pp,Qp, e3;
+  ulong pp;
+  GEN z=_initFF(fg,&T,&p,&pp);
+  pari_sp av = avma;
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    e3 = FqV_to_FpXQV(gel(e,3),T);
+    Pp = FpXQE_changepointinv(RgE_to_FpXQE(P,T,p), e3, T, p);
+    Qp = FpXQE_changepointinv(RgE_to_FpXQE(Q,T,p), e3, T, p);
+    r = FpXQE_weilpairing(Pp, Qp, m, gel(e,1), T, p);
+    break;
+  case t_FF_F2xq:
+    Pp = F2xqE_changepointinv(RgE_to_F2xqE(P,T), gel(e,3), T);
+    Qp = F2xqE_changepointinv(RgE_to_F2xqE(Q,T), gel(e,3), T);
+    r = F2xqE_weilpairing(Pp, Qp, m, gel(e,1), T);
+    break;
+  default:
+    Pp = FlxqE_changepointinv(RgE_to_FlxqE(P,T,pp), gel(e,3), T, pp);
+    Qp = FlxqE_changepointinv(RgE_to_FlxqE(Q,T,pp), gel(e,3), T, pp);
+    r = FlxqE_weilpairing(Pp, Qp, m, gel(e,1), T, pp);
+  }
+  r = gerepileupto(av, r);
+  return _mkFF(fg,z,r);
+}
+
+GEN
+FF_elltatepairing(GEN E, GEN P, GEN Q, GEN m)
+{
+  GEN fg = ellff_get_field(E), e = ellff_get_a4a6(E);
+  GEN r,T,p, Pp,Qp, e3;
+  ulong pp;
+  GEN z=_initFF(fg,&T,&p,&pp);
+  pari_sp av = avma;
+  switch(fg[1])
+  {
+  case t_FF_FpXQ:
+    e3 = FqV_to_FpXQV(gel(e,3),T);
+    Pp = FpXQE_changepointinv(RgE_to_FpXQE(P,T,p), e3, T, p);
+    Qp = FpXQE_changepointinv(RgE_to_FpXQE(Q,T,p), e3, T, p);
+    r = FpXQE_tatepairing(Pp, Qp, m, gel(e,1), T, p);
+    break;
+  case t_FF_F2xq:
+    Pp = F2xqE_changepointinv(RgE_to_F2xqE(P,T), gel(e,3), T);
+    Qp = F2xqE_changepointinv(RgE_to_F2xqE(Q,T), gel(e,3), T);
+    r = F2xqE_tatepairing(Pp, Qp, m, gel(e,1), T);
+    break;
+  default:
+    Pp = FlxqE_changepointinv(RgE_to_FlxqE(P,T,pp), gel(e,3), T, pp);
+    Qp = FlxqE_changepointinv(RgE_to_FlxqE(Q,T,pp), gel(e,3), T, pp);
+    r = FlxqE_tatepairing(Pp, Qp, m, gel(e,1), T, pp);
+  }
+  r = gerepileupto(av, r);
+  return _mkFF(fg,z,r);
+}
+
+static GEN
+to_FF(GEN x, GEN ff)
+{
+  ulong pp;
+  GEN r, T, p, z=_initFF(ff,&T,&p,&pp);
+  int is_int = typ(x)==t_INT;
+  switch(ff[1])
+  {
+  case t_FF_FpXQ:
+    r= is_int ? scalarpol(x, varn(T)): x;
+    break;
+  case t_FF_F2xq:
+    r= is_int ? Z_to_F2x(x,T[1]): ZX_to_F2x(x);
+    break;
+  default:
+    r= is_int ? Z_to_Flx(x,pp,T[1]): ZX_to_Flx(x,pp);
+  }
+  setvarn(r, varn(T)); /* paranoia */
+  return _mkFF_i(ff,z,r);
+}
+
+/* in place */
+static GEN
+to_FFX(GEN x, GEN ff)
+{
+  long i, lx = lg(x);
+  if (typ(x) != t_POL) pari_err_TYPE("to_FFX",x);
+  for (i=2; i<lx; i++) gel(x,i) = to_FF(gel(x,i), ff);
+  return x;
+}
+/* in place */
+static GEN
+to_FFC(GEN x, GEN ff)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++) gel(x,i) = to_FF(gel(x,i), ff);
+  return x;
+}
+
+/* in place */
+static GEN
+raw_to_FFC(GEN x, GEN ff)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++) gel(x,i) = mkFF_i(ff, gel(x,i));
+  return x;
+}
+
+/* in place */
+static GEN
+raw_to_FFM(GEN x, GEN ff)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++) gel(x,i) = raw_to_FFC(gel(x,i), ff);
+  return x;
+}
+
+/* P vector of t_POL, E t_VECSMALL of exponents, ff a t_FFELT. Update elts of
+ * P so that 1) variable number is vP, 2) coefficients are ff-compatible.
+ * Collect garbage wrt av */
+static GEN
+to_FF_fact(long vP, GEN P, GEN E, GEN ff, pari_sp av)
+{
+  GEN y = cgetg(3,t_MAT), u, v, zf;
+  long j, l = lg(P), nbf = lg(P);
+
+  u = cgetg(nbf,t_COL); gel(y,1) = u;
+  v = cgetg(nbf,t_COL); gel(y,2) = v;
+  for (j=1; j<l; j++)
+  {
+    GEN Q = simplify_shallow(gel(P,j)); /* may contain pols of degree 0 */
+    if (typ(Q) == t_POL) setvarn(Q, vP);
+    gel(u,j) = Q;
+    gel(v,j) = utoi((ulong)E[j]);
+  }
+  y = gerepilecopy(av, y); u = gel(y,1);
+  zf = FF_zero(ff);
+  for (j=1; j<nbf; j++) gel(u,j) = to_FFX(gel(u,j), zf);
+  return y;
+}
+
+/*Warning: FFX are polynomials whose coefficients are compatible with FF:
+ * t_INT t_INTMOD, t_FFELT. Assume varncmp(varn(T), varn(x)) < 0 */
+static GEN
+FFX_to_FqX(GEN x, GEN T, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_POL); z[1] = x[1];
+
+  for (i = 2; i < l; i++)
+  {
+    GEN y = gel(x,i);
+    if (typ(y) == t_FFELT)
+    {
+      y = FF_to_FpXQ(y);
+      setvarn(y, varn(T)); /* paranoia */
+    }
+    else
+      y = Rg_to_FpXQ(y, T,p);
+    gel(z,i) = simplify_shallow(y);
+  }
+  return normalizepol_lg(z, l);
+}
+
+static GEN
+FFX_init_fix_varn(GEN P, GEN x, GEN *pT, GEN *pp)
+{
+  ulong junk;
+  GEN Q, T, p;
+
+  _getFF(x, &T, &p, &junk);
+  switch(x[1])
+  {
+  case t_FF_FpXQ:
+    T=shallowcopy(T);
+    break;
+  case t_FF_F2xq:
+    T=F2x_to_ZX(T);
+    break;
+  default:
+    T=Flx_to_ZX(T);
+  }
+  setvarn(T, 1);
+  Q = FFX_to_FqX(P, T,p);
+  setvarn(Q, 0);
+
+  *pT = T;
+  *pp = p; return Q;
+}
+
+/* Factor P over the field of definition of x */
+GEN
+FFX_factor(GEN P, GEN x)
+{
+  long vP = varn(P);
+  GEN r, T, p;
+  pari_sp av=avma;
+
+  P = FFX_init_fix_varn(P, x, &T, &p);
+  r = FqX_factor(P, T,p);
+  return to_FF_fact(vP, gel(r,1),gel(r,2), x,av);
+}
+
+/* Roots of P over the field of definition of x */
+GEN
+FFX_roots(GEN P, GEN x)
+{
+  GEN r, T, p;
+  pari_sp av=avma;
+
+  P = FFX_init_fix_varn(P, x, &T, &p);
+  r = FqX_roots(P, T,p);
+  return gerepilecopy(av, to_FFC(r, x));
+}
+
+GEN
+ffgen(GEN T, long v)
+{
+  GEN A, p = NULL, ff = cgetg(5,t_FFELT);
+  long d;
+  switch(typ(T))
+  {
+    case t_POL:
+      d = degpol(T); p = NULL;
+      if (d < 1 || !RgX_is_FpX(T, &p) || !p) pari_err_TYPE("ffgen",T);
+      T = RgX_to_FpX(T, p);
+      /* testing for irreducibility is too costly */
+      if (!FpX_is_squarefree(T,p)) pari_err_IRREDPOL("ffgen",T);
+      break;
+    case t_INT:
+      d = Z_isanypower(T, &p);
+      if (!d) { d = 1; p = T; }
+      if (!BPSW_psp(p)) pari_err_PRIME("ffgen",p);
+      T = init_Fq(p, d, v);
+      break;
+    default:
+      pari_err_TYPE("ffgen",T);
+      return NULL;
+  }
+  if (v < 0) v = varn(T);
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    long sv = evalvarn(v);
+    if (pp==2)
+    {
+      ff[1] = t_FF_F2xq;
+      T = ZX_to_F2x(T); T[1] = sv;
+      A = polx_F2x(sv); if (d == 1) A = F2x_rem(A, T);
+      p = gen_2;
+    }
+    else
+    {
+      ff[1] = t_FF_Flxq;
+      T = ZX_to_Flx(T,pp); T[1] = sv;
+      A = polx_Flx(sv); if (d == 1) A = Flx_rem(A, T, pp);
+      p = icopy(p);
+    }
+  }
+  else
+  {
+    ff[1] = t_FF_FpXQ;
+    setvarn(T,v);
+    A = pol_x(v); if (d == 1) A = FpX_rem(A, T, p);
+    p = icopy(p);
+  }
+  gel(ff,2) = A;
+  gel(ff,3) = T;
+  gel(ff,4) = p; return ff;
+}
+
+GEN
+p_to_FF(GEN p, long v)
+{
+  GEN A, T;
+  GEN ff = cgetg(5,t_FFELT);
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    long sv = evalvarn(v);
+    if (pp==2)
+    {
+      ff[1] = t_FF_F2xq;
+      T = polx_F2x(sv);
+      A = pol1_F2x(sv);
+      p = gen_2;
+    }
+    else
+    {
+      ff[1] = t_FF_Flxq;
+      T = polx_Flx(sv);
+      A = pol1_Flx(sv);
+      p = icopy(p);
+    }
+  }
+  else
+  {
+    ff[1] = t_FF_FpXQ;
+    T = pol_x(v);
+    A = pol_1(v);
+    p = icopy(p);
+  }
+  gel(ff,2) = A;
+  gel(ff,3) = T;
+  gel(ff,4) = p; return ff;
+}
+
+GEN
+fforder(GEN x, GEN o)
+{
+  if (typ(x)!=t_FFELT) pari_err_TYPE("fforder",x);
+  return FF_order(x,o);
+}
+
+GEN
+ffprimroot(GEN x, GEN *o)
+{
+  if (typ(x)!=t_FFELT) pari_err_TYPE("ffprimroot",x);
+  return FF_primroot(x,o);
+}
+
+GEN
+fflog(GEN x, GEN g, GEN o)
+{
+  if (typ(x)!=t_FFELT) pari_err_TYPE("fflog",x);
+  if (typ(g)!=t_FFELT) pari_err_TYPE("fflog",g);
+  return FF_log(x,g,o);
+}
+
+GEN
+ffrandom(GEN ff)
+{
+  ulong pp;
+  GEN r, T, p, z = _initFF(ff,&T,&p,&pp);
+  switch(ff[1])
+  {
+  case t_FF_FpXQ:
+    r = random_FpX(degpol(T), varn(T), p);
+    break;
+  case t_FF_F2xq:
+    r = random_F2x(F2x_degree(T), T[1]);
+    break;
+  default:
+    r = random_Flx(degpol(T), T[1], pp);
+  }
+  return _mkFF(ff,z,r);
+}
+
+int
+Rg_is_FF(GEN c, GEN *ff)
+{
+  switch(typ(c))
+  {
+  case t_FFELT:
+    if (!*ff) *ff = c;
+    else if (!FF_samefield(*ff, c)) return 0;
+    break;
+  default:
+    return 0;
+  }
+  return 1;
+}
+
+int
+RgC_is_FFC(GEN x, GEN *ff)
+{
+  long i, lx = lg(x);
+  for (i=lx-1; i>0; i--)
+    if (!Rg_is_FF(gel(x,i), ff)) return 0;
+  return (*ff != NULL);
+}
+
+int
+RgM_is_FFM(GEN x, GEN *ff)
+{
+  long j, lx = lg(x);
+  for (j=lx-1; j>0; j--)
+    if (!RgC_is_FFC(gel(x,j), ff)) return 0;
+  return (*ff != NULL);
+}
+
+static GEN
+FFC_to_raw(GEN x)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x,&lx);
+  for(i=1; i<lx; i++)
+    gel(y, i) = gmael(x, i, 2);
+  return y;
+}
+
+static GEN
+FFM_to_raw(GEN x)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x,&lx);
+  for(i=1; i<lx; i++)
+    gel(y, i) = FFC_to_raw(gel(x, i));
+  return y;
+}
+
+static GEN
+FqC_to_FpXQC(GEN x, GEN T)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x,&lx);
+  for(i=1; i<lx; i++)
+    gel(y, i) = Fq_to_FpXQ(gel(x, i), T);
+  return y;
+}
+
+static GEN
+FqM_to_FpXQM(GEN x, GEN T)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x,&lx);
+  for(i=1; i<lx; i++)
+    gel(y, i) = FqC_to_FpXQC(gel(x, i), T);
+  return y;
+}
+
+static GEN
+FFM_wrap(GEN M, GEN ff, GEN (*Fq)(GEN,GEN,GEN),
+                       GEN (*Flxq)(GEN,GEN,ulong),
+                       GEN (*F2xq)(GEN,GEN))
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN T, p;
+  _getFF(ff,&T,&p,&pp); M = FFM_to_raw(M);
+  switch(ff[1])
+  {
+  case t_FF_FpXQ: M = FqM_to_FpXQM(Fq(M,T,p), T); break;
+  case t_FF_F2xq: M = F2xq(M,T); break;
+  default: M = Flxq(M,T,pp); break;
+  }
+  return gerepilecopy(av, raw_to_FFM(M, ff));
+}
+GEN
+FFM_ker(GEN M, GEN ff)
+{ return FFM_wrap(M,ff, &FqM_ker,&FlxqM_ker,&F2xqM_ker); }
+GEN
+FFM_image(GEN M, GEN ff)
+{ return FFM_wrap(M,ff, &FqM_image,&FlxqM_image,&F2xqM_image); }
+GEN
+FFM_inv(GEN M, GEN ff)
+{ return FFM_wrap(M,ff, &FqM_inv,&FlxqM_inv,&F2xqM_inv); }
+long
+FFM_rank(GEN M, GEN ff)
+{
+  pari_sp av = avma;
+  long r;
+  ulong pp;
+  GEN T, p;
+  _getFF(ff,&T,&p,&pp); M = FFM_to_raw(M);
+  switch(ff[1])
+  {
+  case t_FF_FpXQ: r = FqM_rank(M,T,p); break;
+  case t_FF_F2xq: r = F2xqM_rank(M,T); break;
+  default: r = FlxqM_rank(M,T,pp); break;
+  }
+  avma = av; return r;
+}
+GEN
+FFM_det(GEN M, GEN ff)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN d, T, p;
+  _getFF(ff,&T,&p,&pp); M = FFM_to_raw(M);
+  switch(ff[1])
+  {
+  case t_FF_FpXQ: d = FqM_det(M,T,p); break;
+  case t_FF_F2xq: d = F2xqM_det(M,T); break;
+  default: d = FlxqM_det(M,T,pp); break;
+  }
+  return gerepilecopy(av, mkFF_i(ff, d));
+}
+
+GEN
+FFM_FFC_mul(GEN M, GEN C, GEN ff)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN P, T, p;
+  _getFF(ff, &T, &p, &pp);
+  M = FFM_to_raw(M);
+  C = FFC_to_raw(C);
+  switch (ff[1])
+  {
+  case t_FF_FpXQ: P = FqM_FqC_mul(M, C, T, p); break;
+  case t_FF_F2xq: P = F2xqM_F2xqC_mul(M, C, T); break;
+  default: P = FlxqM_FlxqC_mul(M, C, T, pp); break;
+  }
+  return gerepilecopy(av, raw_to_FFC(P, ff));
+}
+
+GEN
+FFM_mul(GEN M, GEN N, GEN ff)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN P, T, p;
+  _getFF(ff, &T, &p, &pp);
+  M = FFM_to_raw(M);
+  N = FFM_to_raw(N);
+  switch (ff[1])
+  {
+  case t_FF_FpXQ: P = FqM_mul(M, N, T, p); break;
+  case t_FF_F2xq: P = F2xqM_mul(M, N, T); break;
+  default: P = FlxqM_mul(M, N, T, pp); break;
+  }
+  return gerepilecopy(av, raw_to_FFM(P, ff));
+}
diff --git a/src/basemath/Flx.c b/src/basemath/Flx.c
new file mode 100644
index 0000000..3b0c874
--- /dev/null
+++ b/src/basemath/Flx.c
@@ -0,0 +1,3777 @@
+/* Copyright (C) 2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with polynomials with small coefficients. */
+
+static GEN
+get_Flx_red(GEN T, GEN *B)
+{
+  if (typ(T)!=t_VEC) { *B=NULL; return T; }
+  *B = gel(T,1); return gel(T,2);
+}
+
+GEN
+get_Flx_mod(GEN T) { return typ(T)==t_VEC? gel(T,2): T; }
+
+long
+get_Flx_var(GEN T) { return typ(T)==t_VEC? mael(T,2,1): T[1]; }
+
+long
+get_Flx_degree(GEN T) { return typ(T)==t_VEC? degpol(gel(T,2)): degpol(T); }
+
+/***********************************************************************/
+/**                                                                   **/
+/**               Flx                                                 **/
+/**                                                                   **/
+/***********************************************************************/
+/* Flx objects are defined as follows:
+   Let l an ulong. An Flx is a t_VECSMALL:
+   x[0] = codeword
+   x[1] = evalvarn(variable number)  (signe is not stored).
+   x[2] = a_0 x[3] = a_1, etc.
+   With 0 <= a_i < l
+
+   signe(x) is not valid. Use degpol(x)>=0 instead.
+*/
+/***********************************************************************/
+/**                                                                   **/
+/**          Conversion from Flx                                      **/
+/**                                                                   **/
+/***********************************************************************/
+
+GEN
+Flx_to_ZX(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_POL);
+  for (i=2; i<l; i++) gel(x,i) = utoi(z[i]);
+  x[1] = evalsigne(l-2!=0)| z[1]; return x;
+}
+
+GEN
+Flx_to_FlxX(GEN z, long sv)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_POL);
+  for (i=2; i<l; i++) gel(x,i) = Fl_to_Flx(z[i], sv);
+  x[1] = evalsigne(l-2!=0)| z[1]; return x;
+}
+
+GEN
+Flv_to_ZV(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = utoi(z[i]);
+  return x;
+}
+
+GEN
+Flc_to_ZC(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_COL);
+  for (i=1; i<l; i++) gel(x,i) = utoi(z[i]);
+  return x;
+}
+
+GEN
+Flm_to_ZM(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = Flc_to_ZC(gel(z,i));
+  return x;
+}
+
+/* same as Flx_to_ZX, in place */
+GEN
+Flx_to_ZX_inplace(GEN z)
+{
+  long i, l = lg(z);
+  for (i=2; i<l; i++) gel(z,i) = utoi(z[i]);
+  settyp(z, t_POL); z[1]=evalsigne(l-2!=0)|z[1]; return z;
+}
+
+/*Flx_to_Flv=zx_to_zv*/
+GEN
+Flx_to_Flv(GEN x, long N)
+{
+  long i, l;
+  GEN z = cgetg(N+1,t_VECSMALL);
+  if (typ(x) != t_VECSMALL) pari_err_TYPE("Flx_to_Flv",x);
+  l = lg(x)-1; x++;
+  for (i=1; i<l ; i++) z[i]=x[i];
+  for (   ; i<=N; i++) z[i]=0;
+  return z;
+}
+
+/*Flv_to_Flx=zv_to_zx*/
+GEN
+Flv_to_Flx(GEN x, long sv)
+{
+  long i, l=lg(x)+1;
+  GEN z = cgetg(l,t_VECSMALL); z[1]=sv;
+  x--;
+  for (i=2; i<l ; i++) z[i]=x[i];
+  return Flx_renormalize(z,l);
+}
+
+/*Flm_to_FlxV=zm_to_zxV*/
+GEN
+Flm_to_FlxV(GEN x, long sv)
+{
+  long j, lx = lg(x);
+  GEN y = cgetg(lx, t_VEC);
+  for (j=1; j<lx; j++) gel(y,j) = Flv_to_Flx(gel(x,j), sv);
+  return y;
+}
+
+/*FlxC_to_ZXC=zxC_to_ZXC*/
+GEN
+FlxC_to_ZXC(GEN x)
+{
+  long i, l=lg(x);
+  GEN z = cgetg(l,t_COL);
+  for (i=1; i<l ; i++) gel(z,i) = Flx_to_ZX(gel(x,i));
+  return z;
+}
+
+/*FlxC_to_ZXC=zxV_to_ZXV*/
+GEN
+FlxV_to_ZXV(GEN x)
+{
+  long i, l=lg(x);
+  GEN z = cgetg(l,t_VEC);
+  for (i=1; i<l ; i++) gel(z,i) = Flx_to_ZX(gel(x,i));
+  return z;
+}
+
+/*FlxM_to_ZXM=zxM_to_ZXM*/
+GEN
+FlxM_to_ZXM(GEN z)
+{
+  long i, l;
+  GEN x = cgetg_copy(z, &l);
+  for (i=1; i<l; i++) gel(x,i) = FlxC_to_ZXC(gel(z,i));
+  return x;
+}
+
+GEN
+FlxM_Flx_add_shallow(GEN x, GEN y, ulong p)
+{
+  long l = lg(x), i, j;
+  GEN z = cgetg(l,t_MAT);
+
+  if (l==1) return z;
+  if (l != lgcols(x)) pari_err_OP( "+", x, y);
+  for (i=1; i<l; i++)
+  {
+    GEN zi = cgetg(l,t_COL), xi = gel(x,i);
+    gel(z,i) = zi;
+    for (j=1; j<l; j++) gel(zi,j) = gel(xi,j);
+    gel(zi,i) = Flx_add(gel(zi,i), y, p);
+  }
+  return z;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**          Conversion to Flx                                        **/
+/**                                                                   **/
+/***********************************************************************/
+/* Take an integer and return a scalar polynomial mod p,  with evalvarn=vs */
+GEN
+Fl_to_Flx(ulong x, long sv)
+{
+  return x? mkvecsmall2(sv, x): pol0_Flx(sv);
+}
+
+GEN
+Z_to_Flx(GEN x, ulong p, long v)
+{
+  long sv = evalvarn(v), u = umodiu(x,p);
+  return u? mkvecsmall2(sv, u): pol0_Flx(sv);
+}
+
+/* return x[0 .. dx] mod p as t_VECSMALL. Assume x a t_POL*/
+GEN
+ZX_to_Flx(GEN x, ulong p)
+{
+  long i, lx = lg(x);
+  GEN a = cgetg(lx, t_VECSMALL);
+  a[1]=((ulong)x[1])&VARNBITS;
+  for (i=2; i<lx; i++) a[i] = umodiu(gel(x,i), p);
+  return Flx_renormalize(a,lx);
+}
+
+ulong
+Rg_to_Fl(GEN x, ulong p)
+{
+  switch(typ(x))
+  {
+    case t_INT: return umodiu(x, p);
+    case t_FRAC: {
+      ulong z = umodiu(gel(x,1), p);
+      if (!z) return 0;
+      return Fl_div(z, umodiu(gel(x,2), p), p);
+    }
+    case t_PADIC: return padic_to_Fl(x, p);
+    case t_INTMOD: {
+      GEN q = gel(x,1), a = gel(x,2);
+      if (equaliu(q, p)) return itou(a);
+      if (!dvdiu(q,p)) pari_err_MODULUS("Rg_to_Fl", q, utoi(p));
+      return umodiu(a, p);
+    }
+    default: pari_err_TYPE("Rg_to_Fl",x);
+      return 0; /* not reached */
+  }
+}
+
+ulong
+Rg_to_F2(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: return mpodd(x);
+    case t_FRAC:
+      if (!mpodd(gel(x,2))) (void)Fl_inv(0,2); /* error */
+      return mpodd(gel(x,1));
+    case t_PADIC:
+      if (!equaliu(gel(x,2),2)) pari_err_OP("",x, mkintmodu(1,2));
+      if (valp(x) < 0) (void)Fl_inv(0,2);
+      return valp(x) & 1;
+    case t_INTMOD: {
+      GEN q = gel(x,1), a = gel(x,2);
+      if (mpodd(q)) pari_err_MODULUS("Rg_to_F2", q, gen_2);
+      return mpodd(a);
+    }
+    default: pari_err_TYPE("Rg_to_F2",x);
+      return 0; /* not reached */
+  }
+}
+
+GEN
+RgX_to_Flx(GEN x, ulong p)
+{
+  long i, lx = lg(x);
+  GEN a = cgetg(lx, t_VECSMALL);
+  a[1]=((ulong)x[1])&VARNBITS;
+  for (i=2; i<lx; i++) a[i] = Rg_to_Fl(gel(x,i), p);
+  return Flx_renormalize(a,lx);
+}
+
+/* If x is a POLMOD, assume modulus is a multiple of T. */
+GEN
+Rg_to_Flxq(GEN x, GEN T, ulong p)
+{
+  long ta, tx = typ(x), v = T[1];
+  GEN a, b;
+  if (is_const_t(tx))
+  {
+    if (tx == t_FFELT) return FF_to_Flxq(x);
+    return Fl_to_Flx(Rg_to_Fl(x, p), v);
+  }
+  switch(tx)
+  {
+    case t_POLMOD:
+      b = gel(x,1);
+      a = gel(x,2); ta = typ(a);
+      if (is_const_t(ta)) return Fl_to_Flx(Rg_to_Fl(a, p), v);
+      b = RgX_to_Flx(b, p); if (b[1] != v) break;
+      a = RgX_to_Flx(a, p); if (Flx_equal(b,T)) return a;
+      return Flx_rem(a, T, p);
+    case t_POL:
+      x = RgX_to_Flx(x,p);
+      if (x[1] != v) break;
+      return Flx_rem(x, T, p);
+    case t_RFRAC:
+      a = Rg_to_Flxq(gel(x,1), T,p);
+      b = Rg_to_Flxq(gel(x,2), T,p);
+      return Flxq_div(a,b, T,p);
+  }
+  pari_err_TYPE("Rg_to_Flxq",x);
+  return NULL; /* not reached */
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**          Basic operation on Flx                                   **/
+/**                                                                   **/
+/***********************************************************************/
+/* = zx_renormalize. Similar to normalizepol, in place */
+GEN
+Flx_renormalize(GEN /*in place*/ x, long lx)
+{
+  long i;
+  for (i = lx-1; i>1; i--)
+    if (x[i]) break;
+  stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
+  setlg(x, i+1); return x;
+}
+
+GEN
+Flx_red(GEN z, ulong p)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_VECSMALL);
+  x[1] = z[1];
+  for (i=2; i<l; i++) x[i] = ((ulong) z[i])%p;
+  return Flx_renormalize(x,l);
+}
+
+GEN
+random_Flx(long d1, long vs, ulong p)
+{
+  long i, d = d1+2;
+  GEN y = cgetg(d,t_VECSMALL); y[1] = vs;
+  for (i=2; i<d; i++) y[i] = random_Fl(p);
+  return Flx_renormalize(y,d);
+}
+
+static GEN
+Flx_addspec(GEN x, GEN y, ulong p, long lx, long ly)
+{
+  long i,lz;
+  GEN z;
+
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx+2; z = cgetg(lz, t_VECSMALL) + 2;
+  for (i=0; i<ly; i++) z[i] = Fl_add(x[i], y[i], p);
+  for (   ; i<lx; i++) z[i] = x[i];
+  z -= 2; return Flx_renormalize(z, lz);
+}
+
+GEN
+Flx_add(GEN x, GEN y, ulong p)
+{
+  long i,lz;
+  GEN z;
+  long lx=lg(x);
+  long ly=lg(y);
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx; z = cgetg(lz, t_VECSMALL); z[1]=x[1];
+  for (i=2; i<ly; i++) z[i] = Fl_add(x[i], y[i], p);
+  for (   ; i<lx; i++) z[i] = x[i];
+  return Flx_renormalize(z, lz);
+}
+
+GEN
+Flx_Fl_add(GEN y, ulong x, ulong p)
+{
+  GEN z;
+  long lz, i;
+  if (!lgpol(y))
+    return Fl_to_Flx(x,y[1]);
+  lz=lg(y);
+  z=cgetg(lz,t_VECSMALL);
+  z[1]=y[1];
+  z[2] = Fl_add(y[2],x,p);
+  for(i=3;i<lz;i++)
+    z[i] = y[i];
+  if (lz==3) z = Flx_renormalize(z,lz);
+  return z;
+}
+
+static GEN
+Flx_subspec(GEN x, GEN y, ulong p, long lx, long ly)
+{
+  long i,lz;
+  GEN z;
+
+  if (ly <= lx)
+  {
+    lz = lx+2; z = cgetg(lz, t_VECSMALL)+2;
+    for (i=0; i<ly; i++) z[i] = Fl_sub(x[i],y[i],p);
+    for (   ; i<lx; i++) z[i] = x[i];
+  }
+  else
+  {
+    lz = ly+2; z = cgetg(lz, t_VECSMALL)+2;
+    for (i=0; i<lx; i++) z[i] = Fl_sub(x[i],y[i],p);
+    for (   ; i<ly; i++) z[i] = Fl_neg(y[i],p);
+  }
+ return Flx_renormalize(z-2, lz);
+}
+
+GEN
+Flx_sub(GEN x, GEN y, ulong p)
+{
+  long i,lz,lx = lg(x), ly = lg(y);
+  GEN z;
+
+  if (ly <= lx)
+  {
+    lz = lx; z = cgetg(lz, t_VECSMALL);
+    for (i=2; i<ly; i++) z[i] = Fl_sub(x[i],y[i],p);
+    for (   ; i<lx; i++) z[i] = x[i];
+  }
+  else
+  {
+    lz = ly; z = cgetg(lz, t_VECSMALL);
+    for (i=2; i<lx; i++) z[i] = Fl_sub(x[i],y[i],p);
+    for (   ; i<ly; i++) z[i] = y[i]? (long)(p - y[i]): y[i];
+  }
+  z[1]=x[1]; return Flx_renormalize(z, lz);
+}
+
+static GEN
+Flx_negspec(GEN x, ulong p, long l)
+{
+  long i;
+  GEN z = cgetg(l+2, t_VECSMALL) + 2;
+  for (i=0; i<l; i++) z[i] = Fl_neg(x[i], p);
+  return z-2;
+}
+
+
+GEN
+Flx_neg(GEN x, ulong p)
+{
+  GEN z = Flx_negspec(x+2, p, lgpol(x));
+  z[1] = x[1];
+  return z;
+}
+
+GEN
+Flx_neg_inplace(GEN x, ulong p)
+{
+  long i, l = lg(x);
+  for (i=2; i<l; i++)
+    if (x[i]) x[i] = p - x[i];
+  return x;
+}
+
+GEN
+Flx_double(GEN y, ulong p)
+{
+  long i, l;
+  GEN z = cgetg_copy(y, &l); z[1] = y[1];
+  for(i=2; i<l; i++) z[i] = Fl_double(y[i], p);
+  return Flx_renormalize(z, l);
+}
+GEN
+Flx_triple(GEN y, ulong p)
+{
+  long i, l;
+  GEN z = cgetg_copy(y, &l); z[1] = y[1];
+  for(i=2; i<l; i++) z[i] = Fl_triple(y[i], p);
+  return Flx_renormalize(z, l);
+}
+GEN
+Flx_Fl_mul(GEN y, ulong x, ulong p)
+{
+  GEN z;
+  long i, l;
+  if (!x) return pol0_Flx(y[1]);
+  z = cgetg_copy(y, &l); z[1] = y[1];
+  if (HIGHWORD(x | p))
+    for(i=2; i<l; i++) z[i] = Fl_mul(y[i], x, p);
+  else
+    for(i=2; i<l; i++) z[i] = (y[i] * x) % p;
+  return Flx_renormalize(z, l);
+}
+GEN
+Flx_Fl_mul_to_monic(GEN y, ulong x, ulong p)
+{
+  GEN z;
+  long i, l;
+  z = cgetg_copy(y, &l); z[1] = y[1];
+  if (HIGHWORD(x | p))
+    for(i=2; i<l-1; i++) z[i] = Fl_mul(y[i], x, p);
+  else
+    for(i=2; i<l-1; i++) z[i] = (y[i] * x) % p;
+  z[l-1] = 1; return z;
+}
+
+/* Return a*x^n if n>=0 and a\x^(-n) if n<0 */
+GEN
+Flx_shift(GEN a, long n)
+{
+  long i, l = lg(a);
+  GEN  b;
+  if (l==2 || !n) return Flx_copy(a);
+  if (l+n<=2) return pol0_Flx(a[1]);
+  b = cgetg(l+n, t_VECSMALL);
+  b[1] = a[1];
+  if (n < 0)
+    for (i=2-n; i<l; i++) b[i+n] = a[i];
+  else
+  {
+    for (i=0; i<n; i++) b[2+i] = 0;
+    for (i=2; i<l; i++) b[i+n] = a[i];
+  }
+  return b;
+}
+
+GEN
+Flx_normalize(GEN z, ulong p)
+{
+  long l = lg(z)-1;
+  ulong p1 = z[l]; /* leading term */
+  if (p1 == 1) return z;
+  return Flx_Fl_mul_to_monic(z, Fl_inv(p1,p), p);
+}
+
+/* return (x * X^d) + y. Assume d > 0, x > 0 and y >= 0 */
+static GEN
+Flx_addshift(GEN x, GEN y, ulong p, long d)
+{
+  GEN xd,yd,zd = (GEN)avma;
+  long a,lz,ny = lgpol(y), nx = lgpol(x);
+  long vs = x[1];
+
+  x += 2; y += 2; a = ny-d;
+  if (a <= 0)
+  {
+    lz = (a>nx)? ny+2: nx+d+2;
+    (void)new_chunk(lz); xd = x+nx; yd = y+ny;
+    while (xd > x) *--zd = *--xd;
+    x = zd + a;
+    while (zd > x) *--zd = 0;
+  }
+  else
+  {
+    xd = new_chunk(d); yd = y+d;
+    x = Flx_addspec(x,yd,p, nx,a);
+    lz = (a>nx)? ny+2: lg(x)+d;
+    x += 2; while (xd > x) *--zd = *--xd;
+  }
+  while (yd > y) *--zd = *--yd;
+  *--zd = vs;
+  *--zd = evaltyp(t_VECSMALL) | evallg(lz); return zd;
+}
+
+/* shift polynomial + gerepile */
+/* Do not set evalvarn*/
+static GEN
+Flx_shiftip(pari_sp av, GEN x, long v)
+{
+  long i, lx = lg(x), ly;
+  GEN y;
+  if (!v || lx==2) return gerepileuptoleaf(av, x);
+  ly = lx + v; /* result length */
+  (void)new_chunk(ly); /* check that result fits */
+  x += lx; y = (GEN)av;
+  for (i = 2; i<lx; i++) *--y = *--x;
+  for (i = 0; i< v; i++) *--y = 0;
+  y -= 2; y[0] = evaltyp(t_VECSMALL) | evallg(ly);
+  avma = (pari_sp)y; return y;
+}
+
+INLINE long
+maxlengthcoeffpol(ulong p, long n)
+{
+  pari_sp ltop = avma;
+  GEN z = muliu(sqru(p-1), n);
+  long l = lgefint(z);
+  avma = ltop;
+  if (l==3 && HIGHWORD(z[2])==0) return 0;
+  return l-2;
+}
+
+INLINE ulong
+Flx_mullimb_ok(GEN x, GEN y, ulong p, long a, long b)
+{ /* Assume OK_ULONG*/
+  ulong p1 = 0;
+  long i;
+  for (i=a; i<b; i++)
+    if (y[i])
+    {
+      p1 += y[i] * x[-i];
+      if (p1 & HIGHBIT) p1 %= p;
+    }
+  return p1 % p;
+}
+
+INLINE ulong
+Flx_mullimb(GEN x, GEN y, ulong p, long a, long b)
+{
+  ulong p1 = 0;
+  long i;
+  for (i=a; i<b; i++)
+    if (y[i])
+      p1 = Fl_add(p1, Fl_mul(y[i],x[-i],p), p);
+  return p1;
+}
+
+/* assume nx >= ny > 0 */
+static GEN
+Flx_mulspec_basecase(GEN x, GEN y, ulong p, long nx, long ny)
+{
+  long i,lz,nz;
+  GEN z;
+
+  lz = nx+ny+1; nz = lz-2;
+  z = cgetg(lz, t_VECSMALL) + 2; /* x:y:z [i] = term of degree i */
+  if (SMALL_ULONG(p))
+  {
+    for (i=0; i<ny; i++)z[i] = Flx_mullimb_ok(x+i,y,p,0,i+1);
+    for (  ; i<nx; i++) z[i] = Flx_mullimb_ok(x+i,y,p,0,ny);
+    for (  ; i<nz; i++) z[i] = Flx_mullimb_ok(x+i,y,p,i-nx+1,ny);
+  }
+  else
+  {
+    for (i=0; i<ny; i++)z[i] = Flx_mullimb(x+i,y,p,0,i+1);
+    for (  ; i<nx; i++) z[i] = Flx_mullimb(x+i,y,p,0,ny);
+    for (  ; i<nz; i++) z[i] = Flx_mullimb(x+i,y,p,i-nx+1,ny);
+  }
+  z -= 2; return Flx_renormalize(z, lz);
+}
+
+static GEN
+int_to_Flx(GEN z, ulong p)
+{
+  long i, l = lgefint(z);
+  GEN x = cgetg(l, t_VECSMALL);
+  for (i=2; i<l; i++) x[i] = ((ulong) z[i])%p;
+  return Flx_renormalize(x, l);
+}
+
+INLINE GEN
+Flx_mulspec_mulii(GEN a, GEN b, ulong p, long na, long nb)
+{
+  GEN z=muliispec(a,b,na,nb);
+  return int_to_Flx(z,p);
+}
+
+static GEN
+Flx_to_int_halfspec(GEN a, long na)
+{
+  long j;
+  long n = (na+1)>>1UL;
+  GEN V = cgetipos(2+n);
+  GEN w;
+  for (w = int_LSW(V), j=0; j+1<na; j+=2, w=int_nextW(w))
+    *w = a[j]|(a[j+1]<<BITS_IN_HALFULONG);
+  if (j<na)
+    *w = a[j];
+  return V;
+}
+
+static GEN
+int_to_Flx_half(GEN z, ulong p)
+{
+  long i;
+  long lx = (lgefint(z)-2)*2+2;
+  GEN w, x = cgetg(lx, t_VECSMALL);
+  for (w = int_LSW(z), i=2; i<lx; i+=2, w=int_nextW(w))
+  {
+    x[i]   = LOWWORD((ulong)*w)%p;
+    x[i+1] = HIGHWORD((ulong)*w)%p;
+  }
+  return Flx_renormalize(x, lx);
+}
+
+static GEN
+Flx_mulspec_halfmulii(GEN a, GEN b, ulong p, long na, long nb)
+{
+  GEN A = Flx_to_int_halfspec(a,na);
+  GEN B = Flx_to_int_halfspec(b,nb);
+  GEN z = mulii(A,B);
+  return int_to_Flx_half(z,p);
+}
+
+/*Eval x in 2^(k*BIL) in linear time, k==2 or 3*/
+static GEN
+Flx_eval2BILspec(GEN x, long k, long l)
+{
+  long i, lz = k*l, ki;
+  GEN pz = cgetipos(2+lz);
+  for (i=0; i < lz; i++)
+    *int_W(pz,i) = 0UL;
+  for (i=0, ki=0; i<l; i++, ki+=k)
+    *int_W(pz,ki) = x[i];
+  return int_normalize(pz,0);
+}
+
+static GEN
+Z_mod2BIL_Flx(GEN x, long bs, long d, ulong p)
+{
+  long i, offset, lm = lgefint(x)-2, l = d+3;
+  GEN pol = cgetg(l, t_VECSMALL);
+  pari_sp av = avma;
+  pol[1] = 0;
+  for (i=0, offset=0; i <= d; i++, offset += bs)
+  {
+    long lz = minss(bs, lm-offset);
+    GEN z = adduispec_offset(0, x, offset, lz);
+    pol[i+2] = umodiu(z, p);
+    avma = av;
+  }
+  return Flx_renormalize(pol,l);
+}
+
+static GEN
+Flx_mulspec_mulii_inflate(GEN x, GEN y, long N, ulong p, long nx, long ny)
+{
+  pari_sp av = avma;
+  GEN z = mulii(Flx_eval2BILspec(x,N,nx), Flx_eval2BILspec(y,N,ny));
+  return gerepileupto(av, Z_mod2BIL_Flx(z, N, nx+ny-2, p));
+}
+
+/* fast product (Karatsuba) of polynomials a,b. These are not real GENs, a+2,
+ * b+2 were sent instead. na, nb = number of terms of a, b.
+ * Only c, c0, c1, c2 are genuine GEN.
+ */
+static GEN
+Flx_mulspec(GEN a, GEN b, ulong p, long na, long nb)
+{
+  GEN a0,c,c0;
+  long n0, n0a, i, v = 0;
+  pari_sp av;
+
+  while (na && !a[0]) { a++; na--; v++; }
+  while (nb && !b[0]) { b++; nb--; v++; }
+  if (na < nb) swapspec(a,b, na,nb);
+  if (!nb) return pol0_Flx(0);
+
+  av = avma;
+  switch (maxlengthcoeffpol(p,nb))
+  {
+  case 0:
+    if (na>=Flx_MUL_HALFMULII_LIMIT)
+      return Flx_shiftip(av,Flx_mulspec_halfmulii(a,b,p,na,nb), v);
+    break;
+  case 1:
+    if (na>=Flx_MUL_MULII_LIMIT)
+      return Flx_shiftip(av,Flx_mulspec_mulii(a,b,p,na,nb), v);
+    break;
+  case 2:
+    if (na>=Flx_MUL_MULII2_LIMIT)
+      return Flx_shiftip(av,Flx_mulspec_mulii_inflate(a,b,2,p,na,nb), v);
+    break;
+  case 3:
+    if (na>70)
+      return Flx_shiftip(av,Flx_mulspec_mulii_inflate(a,b,3,p,na,nb), v);
+    break;
+  }
+  if (nb < Flx_MUL_KARATSUBA_LIMIT)
+    return Flx_shiftip(av,Flx_mulspec_basecase(a,b,p,na,nb), v);
+  i=(na>>1); n0=na-i; na=i;
+  a0=a+n0; n0a=n0;
+  while (n0a && !a[n0a-1]) n0a--;
+
+  if (nb > n0)
+  {
+    GEN b0,c1,c2;
+    long n0b;
+
+    nb -= n0; b0 = b+n0; n0b = n0;
+    while (n0b && !b[n0b-1]) n0b--;
+    c =  Flx_mulspec(a,b,p,n0a,n0b);
+    c0 = Flx_mulspec(a0,b0,p,na,nb);
+
+    c2 = Flx_addspec(a0,a,p,na,n0a);
+    c1 = Flx_addspec(b0,b,p,nb,n0b);
+
+    c1 = Flx_mul(c1,c2,p);
+    c2 = Flx_add(c0,c,p);
+
+    c2 = Flx_neg_inplace(c2,p);
+    c2 = Flx_add(c1,c2,p);
+    c0 = Flx_addshift(c0,c2 ,p, n0);
+  }
+  else
+  {
+    c  = Flx_mulspec(a,b,p,n0a,nb);
+    c0 = Flx_mulspec(a0,b,p,na,nb);
+  }
+  c0 = Flx_addshift(c0,c,p,n0);
+  return Flx_shiftip(av,c0, v);
+}
+
+
+GEN
+Flx_mul(GEN x, GEN y, ulong p)
+{
+ GEN z = Flx_mulspec(x+2,y+2,p, lgpol(x),lgpol(y));
+ z[1] = x[1]; return z;
+}
+
+static GEN
+Flx_sqrspec_basecase(GEN x, ulong p, long nx)
+{
+  long i, lz, nz;
+  ulong p1;
+  GEN z;
+
+  if (!nx) return pol0_Flx(0);
+  lz = (nx << 1) + 1, nz = lz-2;
+  z = cgetg(lz, t_VECSMALL) + 2;
+  if (SMALL_ULONG(p))
+  {
+    z[0] = x[0]*x[0]%p;
+    for (i=1; i<nx; i++)
+    {
+      p1 = Flx_mullimb_ok(x+i,x,p,0, (i+1)>>1);
+      p1 <<= 1;
+      if ((i&1) == 0) p1 += x[i>>1] * x[i>>1];
+      z[i] = p1 % p;
+    }
+    for (  ; i<nz; i++)
+    {
+      p1 = Flx_mullimb_ok(x+i,x,p,i-nx+1, (i+1)>>1);
+      p1 <<= 1;
+      if ((i&1) == 0) p1 += x[i>>1] * x[i>>1];
+      z[i] = p1 % p;
+    }
+  }
+  else
+  {
+    z[0] = Fl_sqr(x[0], p);
+    for (i=1; i<nx; i++)
+    {
+      p1 = Flx_mullimb(x+i,x,p,0, (i+1)>>1);
+      p1 = Fl_add(p1, p1, p);
+      if ((i&1) == 0) p1 = Fl_add(p1, Fl_sqr(x[i>>1], p), p);
+      z[i] = p1;
+    }
+    for (  ; i<nz; i++)
+    {
+      p1 = Flx_mullimb(x+i,x,p,i-nx+1, (i+1)>>1);
+      p1 = Fl_add(p1, p1, p);
+      if ((i&1) == 0) p1 = Fl_add(p1, Fl_sqr(x[i>>1], p), p);
+      z[i] = p1;
+    }
+  }
+  z -= 2; return Flx_renormalize(z, lz);
+}
+
+static GEN
+Flx_sqrspec_sqri(GEN a, ulong p, long na)
+{
+  GEN z=sqrispec(a,na);
+  return int_to_Flx(z,p);
+}
+
+static GEN
+Flx_sqrspec_halfsqri(GEN a, ulong p, long na)
+{
+  GEN z = sqri(Flx_to_int_halfspec(a,na));
+  return int_to_Flx_half(z,p);
+}
+
+static GEN
+Flx_sqrspec_sqri_inflate(GEN x, long N, ulong p, long nx)
+{
+  pari_sp av = avma;
+  GEN  z = sqri(Flx_eval2BILspec(x,N,nx));
+  return gerepileupto(av, Z_mod2BIL_Flx(z, N, (nx-1)*2, p));
+}
+
+static GEN
+Flx_sqrspec(GEN a, ulong p, long na)
+{
+  GEN a0, c, c0;
+  long n0, n0a, i, v = 0;
+  pari_sp av;
+
+  while (na && !a[0]) { a++; na--; v += 2; }
+  if (!na) return pol0_Flx(0);
+
+  av = avma;
+  switch(maxlengthcoeffpol(p,na))
+  {
+  case 0:
+    if (na>=Flx_SQR_HALFSQRI_LIMIT)
+      return Flx_shiftip(av, Flx_sqrspec_halfsqri(a,p,na), v);
+    break;
+  case 1:
+    if (na>=Flx_SQR_SQRI_LIMIT)
+      return Flx_shiftip(av, Flx_sqrspec_sqri(a,p,na), v);
+    break;
+  case 2:
+    if (na>=Flx_SQR_SQRI2_LIMIT)
+      return Flx_shiftip(av, Flx_sqrspec_sqri_inflate(a,2,p,na), v);
+    break;
+  case 3:
+    if (na>70)
+      return Flx_shiftip(av, Flx_sqrspec_sqri_inflate(a,3,p,na), v);
+    break;
+  }
+  if (na < Flx_SQR_KARATSUBA_LIMIT)
+    return Flx_shiftip(av, Flx_sqrspec_basecase(a,p,na), v);
+  i=(na>>1); n0=na-i; na=i;
+  a0=a+n0; n0a=n0;
+  while (n0a && !a[n0a-1]) n0a--;
+
+  c = Flx_sqrspec(a,p,n0a);
+  c0= Flx_sqrspec(a0,p,na);
+  if (p == 2) n0 *= 2;
+  else
+  {
+    GEN c1, t = Flx_addspec(a0,a,p,na,n0a);
+    t = Flx_sqr(t,p);
+    c1= Flx_add(c0,c, p);
+    c1= Flx_sub(t, c1, p);
+    c0 = Flx_addshift(c0,c1,p,n0);
+  }
+  c0 = Flx_addshift(c0,c,p,n0);
+  return Flx_shiftip(av,c0,v);
+}
+
+GEN
+Flx_sqr(GEN x, ulong p)
+{
+  GEN z = Flx_sqrspec(x+2,p, lgpol(x));
+  z[1] = x[1]; return z;
+}
+
+GEN
+Flx_pow(GEN x, long n, ulong p)
+{
+  GEN y = pol1_Flx(x[1]), z;
+  long m;
+  if (n == 0) return y;
+  m = n; z = x;
+  for (;;)
+  {
+    if (m&1) y = Flx_mul(y,z, p);
+    m >>= 1; if (!m) return y;
+    z = Flx_sqr(z, p);
+  }
+}
+
+static GEN
+Flx_recipspec(GEN x, long l, long n)
+{
+  long i;
+  GEN z=cgetg(n+2,t_VECSMALL)+2;
+  for(i=0; i<l; i++)
+    z[n-i-1] = x[i];
+  for(   ; i<n; i++)
+    z[n-i-1] = 0;
+  return Flx_renormalize(z-2,n+2);
+}
+
+GEN
+Flx_recip(GEN x)
+{
+  GEN z=Flx_recipspec(x+2,lgpol(x),lgpol(x));
+  z[1]=x[1];
+  return z;
+}
+
+/*
+ * x/polrecip(P)+O(x^n)
+ */
+static GEN
+Flx_invBarrett_basecase(GEN T, ulong p)
+{
+  long i, l=lg(T)-1, lr=l-1, k;
+  GEN r=cgetg(lr,t_VECSMALL); r[1] = T[1];
+  r[2] = 1;
+  if (SMALL_ULONG(p))
+    for (i=3;i<lr;i++)
+    {
+      long u = T[l-i+2];
+      for (k=3;k<i;k++) { u += T[l-i+k] * r[k]; if (u & HIGHBIT) u %= p; }
+      r[i] = Fl_neg(u % p, p);
+    }
+  else
+    for (i=3;i<lr;i++)
+    {
+      ulong u = Fl_neg(T[l-i+2], p);
+      for (k=3;k<i;k++) u = Fl_sub(u, Fl_mul(T[l-i+k],r[k],p),p);
+      r[i] = u;
+    }
+  return Flx_renormalize(r,lr);
+}
+
+/* Return new lgpol */
+static long
+Flx_lgrenormalizespec(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>=0; i--)
+    if (x[i]) break;
+  return i+1;
+}
+static GEN
+Flx_invBarrett_Newton(GEN T, ulong p)
+{
+  long nold, lx, lz, lq, l = degpol(T), lQ;
+  GEN q, y, z, x = zero_zv(l+1) + 2;
+  ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
+  pari_sp av;
+
+  y = T+2;
+  q = Flx_recipspec(y,l+1,l+1); lQ = lgpol(q); q+=2;
+  av = avma;
+  /* We work on _spec_ Flx's, all the l[xzq12] below are lgpol's */
+
+  /* initialize */
+  x[0] = Fl_inv(q[0], p);
+  if (lQ>1 && q[1])
+  {
+    ulong u = q[1];
+    if (x[0] != 1) u = Fl_mul(u, Fl_sqr(x[0],p), p);
+    x[1] = p - u; lx = 2;
+  }
+  else
+    lx = 1;
+  nold = 1;
+  for (; mask > 1; avma = av)
+  { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
+    long i, lnew, nnew = nold << 1;
+
+    if (mask & 1) nnew--;
+    mask >>= 1;
+
+    lnew = nnew + 1;
+    lq = Flx_lgrenormalizespec(q, minss(lQ, lnew));
+    z = Flx_mulspec(x, q, p, lx, lq); /* FIXME: high product */
+    lz = lgpol(z); if (lz > lnew) lz = lnew;
+    z += 2;
+    /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
+    for (i = nold; i < lz; i++) if (z[i]) break;
+    nold = nnew;
+    if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
+
+    /* z + i represents (x*q - 1) / t^i */
+    lz = Flx_lgrenormalizespec (z+i, lz-i);
+    z = Flx_mulspec(x, z+i, p, lx, lz); /* FIXME: low product */
+    lz = lgpol(z); z += 2;
+    if (lz > lnew-i) lz = Flx_lgrenormalizespec(z, lnew-i);
+
+    lx = lz+ i;
+    y  = x + i; /* x -= z * t^i, in place */
+    for (i = 0; i < lz; i++) y[i] = Fl_neg(z[i], p);
+  }
+  x -= 2; setlg(x, lx + 2); x[1] = T[1];
+  return x;
+}
+
+/* x/polrecip(T)+O(x^deg(T)) */
+GEN
+Flx_invBarrett(GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  long l=lg(T);
+  GEN r;
+  if (l<5) return pol0_Flx(T[1]);
+  if (l<=Flx_INVBARRETT_LIMIT)
+  {
+    ulong c = T[l-1];
+    if (c!=1)
+    {
+      ulong ci = Fl_inv(c,p);
+      T=Flx_Fl_mul(T, ci, p);
+      r=Flx_invBarrett_basecase(T,p);
+      r=Flx_Fl_mul(r,ci,p);
+    }
+    else
+      r=Flx_invBarrett_basecase(T,p);
+  }
+  else
+    r = Flx_invBarrett_Newton(T,p);
+  return gerepileuptoleaf(ltop, r);
+}
+
+GEN
+Flx_get_red(GEN T, ulong p)
+{
+  if (typ(T)==t_VECSMALL && lg(T) >= Flx_BARRETT_LIMIT)
+    retmkvec2(Flx_invBarrett(T,p),T);
+  return T;
+}
+
+/* separate from Flx_divrem for maximal speed. */
+static GEN
+Flx_rem_basecase(GEN x, GEN y, ulong p)
+{
+  pari_sp av;
+  GEN z, c;
+  long dx,dy,dz,i,j;
+  ulong p1,inv;
+  long vs=x[1];
+
+  dy = degpol(y); if (!dy) return pol0_Flx(x[1]);
+  dx = degpol(x);
+  dz = dx-dy; if (dz < 0) return Flx_copy(x);
+  x += 2; y += 2;
+  inv = y[dy];
+  if (inv != 1UL) inv = Fl_inv(inv,p);
+
+  c = cgetg(dy+3, t_VECSMALL); c[1]=vs; c += 2; av=avma;
+  z = cgetg(dz+3, t_VECSMALL); z[1]=vs; z += 2;
+
+  if (SMALL_ULONG(p))
+  {
+    z[dz] = (inv*x[dx]) % p;
+    for (i=dx-1; i>=dy; --i)
+    {
+      p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
+      for (j=i-dy+1; j<=i && j<=dz; j++)
+      {
+        p1 += z[j]*y[i-j];
+        if (p1 & HIGHBIT) p1 %= p;
+      }
+      p1 %= p;
+      z[i-dy] = p1? ((p - p1)*inv) % p: 0;
+    }
+    for (i=0; i<dy; i++)
+    {
+      p1 = z[0]*y[i];
+      for (j=1; j<=i && j<=dz; j++)
+      {
+        p1 += z[j]*y[i-j];
+        if (p1 & HIGHBIT) p1 %= p;
+      }
+      c[i] = Fl_sub(x[i], p1%p, p);
+    }
+  }
+  else
+  {
+    z[dz] = Fl_mul(inv, x[dx], p);
+    for (i=dx-1; i>=dy; --i)
+    {
+      p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
+      for (j=i-dy+1; j<=i && j<=dz; j++)
+        p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
+      z[i-dy] = p1? Fl_mul(p - p1, inv, p): 0;
+    }
+    for (i=0; i<dy; i++)
+    {
+      p1 = Fl_mul(z[0],y[i],p);
+      for (j=1; j<=i && j<=dz; j++)
+        p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
+      c[i] = Fl_sub(x[i], p1, p);
+    }
+  }
+  i = dy-1; while (i>=0 && !c[i]) i--;
+  avma=av;
+  return Flx_renormalize(c-2, i+3);
+}
+
+/* as FpX_divrem but working only on ulong types.
+ * if relevant, *pr is the last object on stack */
+static GEN
+Flx_divrem_basecase(GEN x, GEN y, ulong p, GEN *pr)
+{
+  GEN z,q,c;
+  long dx,dy,dz,i,j;
+  ulong p1,inv;
+  long sv=x[1];
+
+  dy = degpol(y);
+  if (dy<0) pari_err_INV("Flx_divrem",y);
+  if (pr == ONLY_REM) return Flx_rem_basecase(x, y, p);
+  if (!dy)
+  {
+    if (pr && pr != ONLY_DIVIDES) *pr = pol0_Flx(sv);
+    if (y[2] == 1UL) return Flx_copy(x);
+    return Flx_Fl_mul(x, Fl_inv(y[2], p), p);
+  }
+  dx = degpol(x);
+  dz = dx-dy;
+  if (dz < 0)
+  {
+    q = pol0_Flx(sv);
+    if (pr && pr != ONLY_DIVIDES) *pr = Flx_copy(x);
+    return q;
+  }
+  x += 2;
+  y += 2;
+  z = cgetg(dz + 3, t_VECSMALL); z[1] = sv; z += 2;
+  inv = (ulong)y[dy];
+  if (inv != 1UL) inv = Fl_inv(inv,p);
+
+  if (SMALL_ULONG(p))
+  {
+    z[dz] = (inv*x[dx]) % p;
+    for (i=dx-1; i>=dy; --i)
+    {
+      p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
+      for (j=i-dy+1; j<=i && j<=dz; j++)
+      {
+        p1 += z[j]*y[i-j];
+        if (p1 & HIGHBIT) p1 %= p;
+      }
+      p1 %= p;
+      z[i-dy] = p1? (long) ((p - p1)*inv) % p: 0;
+    }
+  }
+  else
+  {
+    z[dz] = Fl_mul(inv, x[dx], p);
+    for (i=dx-1; i>=dy; --i)
+    { /* compute -p1 instead of p1 (pb with ulongs otherwise) */
+      p1 = p - (ulong)x[i];
+      for (j=i-dy+1; j<=i && j<=dz; j++)
+        p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
+      z[i-dy] = p1? Fl_mul(p - p1, inv, p): 0;
+    }
+  }
+  q = Flx_renormalize(z-2, dz+3);
+  if (!pr) return q;
+
+  c = cgetg(dy + 3, t_VECSMALL); c[1] = sv; c += 2;
+  if (SMALL_ULONG(p))
+  {
+    for (i=0; i<dy; i++)
+    {
+      p1 = (ulong)z[0]*y[i];
+      for (j=1; j<=i && j<=dz; j++)
+      {
+        p1 += (ulong)z[j]*y[i-j];
+        if (p1 & HIGHBIT) p1 %= p;
+      }
+      c[i] = Fl_sub(x[i], p1%p, p);
+    }
+  }
+  else
+  {
+    for (i=0; i<dy; i++)
+    {
+      p1 = Fl_mul(z[0],y[i],p);
+      for (j=1; j<=i && j<=dz; j++)
+        p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
+      c[i] = Fl_sub(x[i], p1, p);
+    }
+  }
+  i=dy-1; while (i>=0 && !c[i]) i--;
+  c = Flx_renormalize(c-2, i+3);
+  if (pr == ONLY_DIVIDES)
+  { if (lg(c) != 2) return NULL; }
+  else
+    *pr = c;
+  return q;
+}
+
+
+/* Compute x mod T where 2 <= degpol(T) <= l+1 <= 2*(degpol(T)-1)
+ * and mg is the Barrett inverse of T. */
+static GEN
+Flx_divrem_Barrettspec(GEN x, long l, GEN mg, GEN T, ulong p, GEN *pr)
+{
+  GEN q, r;
+  long lt = degpol(T); /*We discard the leading term*/
+  long ld, lm, lT, lmg;
+  ld = l-lt;
+  lm = minss(ld, lgpol(mg));
+  lT  = Flx_lgrenormalizespec(T+2,lt);
+  lmg = Flx_lgrenormalizespec(mg+2,lm);
+  q = Flx_recipspec(x+lt,ld,ld);               /* q = rec(x)      lz<=ld*/
+  q = Flx_mulspec(q+2,mg+2,p,lgpol(q),lmg);    /* q = rec(x) * mg lz<=ld+lm*/
+  q = Flx_recipspec(q+2,minss(ld,lgpol(q)),ld);/* q = rec (rec(x) * mg) lz<=ld*/
+  if (!pr) return q;
+  r = Flx_mulspec(q+2,T+2,p,lgpol(q),lT);      /* r = q*pol       lz<=ld+lt*/
+  r = Flx_subspec(x,r+2,p,lt,minss(lt,lgpol(r)));/* r = x - q*pol lz<=lt */
+  if (pr == ONLY_REM) return r;
+  *pr = r; return q;
+}
+
+static GEN
+Flx_divrem_Barrett_noGC(GEN x, GEN mg, GEN T, ulong p, GEN *pr)
+{
+  long l = lgpol(x), lt = degpol(T), lm = 2*lt-1;
+  GEN q = NULL, r;
+  long i;
+  if (l <= lt)
+  {
+    if (pr == ONLY_REM) return Flx_copy(x);
+    if (pr == ONLY_DIVIDES) return lgpol(x)? NULL: pol0_Flx(x[1]);
+    if (pr) *pr = Flx_copy(x);
+    return pol0_Flx(x[1]);
+  }
+  if (lt <= 1)
+    return Flx_divrem_basecase(x,T,p,pr);
+  if (pr != ONLY_REM && l>lm)
+    q = zero_zv(l-lt+1);
+  r = Flx_copy(x);
+  while (l>lm)
+  {
+    GEN zr, zq = Flx_divrem_Barrettspec(r+2+l-lm,lm,mg,T,p,&zr);
+    long lz = lgpol(zr);
+    if (pr != ONLY_REM)
+    {
+      long lq = lgpol(zq);
+      for(i=0; i<lq; i++) q[2+l-lm+i] = zq[2+i];
+    }
+    for(i=0; i<lz; i++)   r[2+l-lm+i] = zr[2+i];
+    l = l-lm+lz;
+  }
+  if (pr != ONLY_REM)
+  {
+    if (l > lt)
+    {
+      GEN zq = Flx_divrem_Barrettspec(r+2,l,mg,T,p,&r);
+      if (!q) q = zq;
+      else
+      {
+        long lq = lgpol(zq);
+        for(i=0; i<lq; i++) q[2+i] = zq[2+i];
+      }
+    }
+    else
+      r = Flx_renormalize(r, l+2);
+  }
+  else
+  {
+    if (l > lt)
+      r = Flx_divrem_Barrettspec(r+2,l,mg,T,p,ONLY_REM);
+    else
+      r = Flx_renormalize(r, l+2);
+    r[1] = x[1]; return Flx_renormalize(r, lg(r));
+  }
+  if (pr) { r[1] = x[1]; r = Flx_renormalize(r, lg(r)); }
+  q[1] = x[1]; q = Flx_renormalize(q, lg(q));
+  if (pr == ONLY_DIVIDES) return lgpol(r)? NULL: q;
+  if (pr) *pr = r;
+  return q;
+}
+
+GEN
+Flx_divrem(GEN x, GEN T, ulong p, GEN *pr)
+{
+  GEN B, y = get_Flx_red(T, &B);
+  long dy = degpol(y), dx = degpol(x), d = dx-dy;
+  if (pr==ONLY_REM) return Flx_rem(x, y, p);
+  if (!B && d+3 < Flx_DIVREM_BARRETT_LIMIT)
+    return Flx_divrem_basecase(x,y,p,pr);
+  else
+  {
+    pari_sp av=avma;
+    GEN mg = B? B: Flx_invBarrett(y, p);
+    GEN q1 = Flx_divrem_Barrett_noGC(x,mg,y,p,pr);
+    if (!q1) {avma=av; return NULL;}
+    if (!pr || pr==ONLY_DIVIDES) return gerepileuptoleaf(av, q1);
+    gerepileall(av,2,&q1,pr);
+    return q1;
+  }
+}
+
+GEN
+Flx_rem(GEN x, GEN T, ulong p)
+{
+  GEN B, y = get_Flx_red(T, &B);
+  long dy = degpol(y), dx = degpol(x), d = dx-dy;
+  if (d < 0) return Flx_copy(x);
+  if (!B && d+3 < Flx_REM_BARRETT_LIMIT)
+    return Flx_rem_basecase(x,y,p);
+  else
+  {
+    pari_sp av=avma;
+    GEN mg = B ? B: Flx_invBarrett(y, p);
+    GEN r  = Flx_divrem_Barrett_noGC(x, mg, y, p, ONLY_REM);
+    return gerepileuptoleaf(av, r);
+  }
+}
+
+/* reduce T mod (X^n - 1, p). Shallow function */
+GEN
+Flx_mod_Xnm1(GEN T, ulong n, ulong p)
+{
+  long i, j, L = lg(T), l = n+2;
+  GEN S;
+  if (L <= l || n & ~LGBITS) return T;
+  S = cgetg(l, t_VECSMALL);
+  S[1] = T[1];
+  for (i = 2; i < l; i++) S[i] = T[i];
+  for (j = 2; i < L; i++) {
+    S[j] = Fl_add(S[j], T[i], p);
+    if (++j == l) j = 2;
+  }
+  return Flx_renormalize(S, l);
+}
+/* reduce T mod (X^n + 1, p). Shallow function */
+GEN
+Flx_mod_Xn1(GEN T, ulong n, ulong p)
+{
+  long i, j, L = lg(T), l = n+2;
+  GEN S;
+  if (L <= l || n & ~LGBITS) return T;
+  S = cgetg(l, t_VECSMALL);
+  S[1] = T[1];
+  for (i = 2; i < l; i++) S[i] = T[i];
+  for (j = 2; i < L; i++) {
+    S[j] = Fl_sub(S[j], T[i], p);
+    if (++j == l) j = 2;
+  }
+  return Flx_renormalize(S, l);
+}
+
+long
+Flx_val(GEN x)
+{
+  long i, l=lg(x);
+  if (l==2)  return LONG_MAX;
+  for (i=2; i<l && x[i]==0; i++) /*empty*/;
+  return i-2;
+}
+long
+Flx_valrem(GEN x, GEN *Z)
+{
+  long v, i, l=lg(x);
+  GEN y;
+  if (l==2) { *Z = Flx_copy(x); return LONG_MAX; }
+  for (i=2; i<l && x[i]==0; i++) /*empty*/;
+  v = i-2;
+  if (v == 0) { *Z = x; return 0; }
+  l -= v;
+  y = cgetg(l, t_VECSMALL); y[1] = x[1];
+  for (i=2; i<l; i++) y[i] = x[i+v];
+  *Z = y; return v;
+}
+
+GEN
+Flx_deriv(GEN z, ulong p)
+{
+  long i,l = lg(z)-1;
+  GEN x;
+  if (l < 2) l = 2;
+  x = cgetg(l, t_VECSMALL); x[1] = z[1]; z++;
+  if (HIGHWORD(l | p))
+    for (i=2; i<l; i++) x[i] = Fl_mul((ulong)i-1, z[i], p);
+  else
+    for (i=2; i<l; i++) x[i] = ((i-1) * z[i]) % p;
+  return Flx_renormalize(x,l);
+}
+
+GEN
+Flx_deflate(GEN x0, long d)
+{
+  GEN z, y, x;
+  long i,id, dy, dx = degpol(x0);
+  if (d == 1 || dx <= 0) return Flx_copy(x0);
+  dy = dx/d;
+  y = cgetg(dy+3, t_VECSMALL); y[1] = x0[1];
+  z = y + 2;
+  x = x0+ 2;
+  for (i=id=0; i<=dy; i++,id+=d) z[i] = x[id];
+  return y;
+}
+
+GEN
+Flx_inflate(GEN x0, long d)
+{
+  long i, id, dy, dx = degpol(x0);
+  GEN x = x0 + 2, z, y;
+  if (dx <= 0) return Flx_copy(x0);
+  dy = dx*d;
+  y = cgetg(dy+3, t_VECSMALL); y[1] = x0[1];
+  z = y + 2;
+  for (i=0; i<=dy; i++) z[i] = 0;
+  for (i=id=0; i<=dx; i++,id+=d) z[id] = x[i];
+  return y;
+}
+
+/* write p(X) = a_0(X^k) + X*a_1(X^k) + ... + X^(k-1)*a_{k-1}(X^k) */
+GEN
+Flx_splitting(GEN p, long k)
+{
+  long n = degpol(p), v = p[1], m, i, j, l;
+  GEN r;
+
+  m = n/k;
+  r = cgetg(k+1,t_VEC);
+  for(i=1; i<=k; i++)
+  {
+    gel(r,i) = cgetg(m+3, t_VECSMALL);
+    mael(r,i,1) = v;
+  }
+  for (j=1, i=0, l=2; i<=n; i++)
+  {
+    mael(r,j,l) = p[2+i];
+    if (j==k) { j=1; l++; } else j++;
+  }
+  for(i=1; i<=k; i++)
+    gel(r,i) = Flx_renormalize(gel(r,i),i<j?l+1:l);
+  return r;
+}
+static GEN
+Flx_halfgcd_basecase(GEN a, GEN b, ulong p)
+{
+  pari_sp av=avma, lim = stack_lim(av,2);
+  GEN u,u1,v,v1;
+  long vx = a[1];
+  long n = lgpol(a)>>1;
+  u1 = v = pol0_Flx(vx);
+  u = v1 = pol1_Flx(vx);
+  while (lgpol(b)>n)
+  {
+    GEN r, q = Flx_divrem(a,b,p, &r);
+    a = b; b = r; swap(u,u1); swap(v,v1);
+    u1 = Flx_sub(u1, Flx_mul(u, q, p), p);
+    v1 = Flx_sub(v1, Flx_mul(v, q ,p), p);
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flx_halfgcd (d = %ld)",degpol(b));
+      gerepileall(av,6, &a,&b,&u1,&v1,&u,&v);
+    }
+  }
+  return gerepilecopy(av, mkmat2(mkcol2(u,u1), mkcol2(v,v1)));
+}
+/* ux + vy */
+static GEN
+Flx_addmulmul(GEN u, GEN v, GEN x, GEN y, ulong p)
+{ return Flx_add(Flx_mul(u,x, p), Flx_mul(v,y, p), p); }
+
+static GEN
+FlxM_Flx_mul2(GEN M, GEN x, GEN y, ulong p)
+{
+  GEN res = cgetg(3, t_COL);
+  gel(res, 1) = Flx_addmulmul(gcoeff(M,1,1), gcoeff(M,1,2), x, y, p);
+  gel(res, 2) = Flx_addmulmul(gcoeff(M,2,1), gcoeff(M,2,2), x, y, p);
+  return res;
+}
+
+#if 0
+static GEN
+FlxM_mul2_old(GEN M, GEN N, ulong p)
+{
+  GEN res = cgetg(3, t_MAT);
+  gel(res, 1) = FlxM_Flx_mul2(M,gcoeff(N,1,1),gcoeff(N,2,1),p);
+  gel(res, 2) = FlxM_Flx_mul2(M,gcoeff(N,1,2),gcoeff(N,2,2),p);
+  return res;
+}
+#endif
+/* A,B are 2x2 matrices, Flx entries. Return A x B using Strassen 7M formula */
+static GEN
+FlxM_mul2(GEN A, GEN B, ulong p)
+{
+  GEN A11=gcoeff(A,1,1),A12=gcoeff(A,1,2), B11=gcoeff(B,1,1),B12=gcoeff(B,1,2);
+  GEN A21=gcoeff(A,2,1),A22=gcoeff(A,2,2), B21=gcoeff(B,2,1),B22=gcoeff(B,2,2);
+  GEN M1 = Flx_mul(Flx_add(A11,A22, p), Flx_add(B11,B22, p), p);
+  GEN M2 = Flx_mul(Flx_add(A21,A22, p), B11, p);
+  GEN M3 = Flx_mul(A11, Flx_sub(B12,B22, p), p);
+  GEN M4 = Flx_mul(A22, Flx_sub(B21,B11, p), p);
+  GEN M5 = Flx_mul(Flx_add(A11,A12, p), B22, p);
+  GEN M6 = Flx_mul(Flx_sub(A21,A11, p), Flx_add(B11,B12, p), p);
+  GEN M7 = Flx_mul(Flx_sub(A12,A22, p), Flx_add(B21,B22, p), p);
+  GEN T1 = Flx_add(M1,M4, p), T2 = Flx_sub(M7,M5, p);
+  GEN T3 = Flx_sub(M1,M2, p), T4 = Flx_add(M3,M6, p);
+  retmkmat2(mkcol2(Flx_add(T1,T2, p), Flx_add(M2,M4, p)),
+            mkcol2(Flx_add(M3,M5, p), Flx_add(T3,T4, p)));
+}
+
+/* Return [0,1;1,-q]*M */
+static GEN
+Flx_FlxM_qmul(GEN q, GEN M, ulong p)
+{
+  GEN u, v, res = cgetg(3, t_MAT);
+  u = Flx_sub(gcoeff(M,1,1), Flx_mul(gcoeff(M,2,1), q, p), p);
+  gel(res,1) = mkcol2(gcoeff(M,2,1), u);
+  v = Flx_sub(gcoeff(M,1,2), Flx_mul(gcoeff(M,2,2), q, p), p);
+  gel(res,2) = mkcol2(gcoeff(M,2,2), v);
+  return res;
+}
+
+static GEN
+matid2_FlxM(long v)
+{
+  return mkmat2(mkcol2(pol1_Flx(v),pol0_Flx(v)),
+                mkcol2(pol0_Flx(v),pol1_Flx(v)));
+}
+
+static GEN
+Flx_halfgcd_split(GEN x, GEN y, ulong p)
+{
+  pari_sp av=avma;
+  GEN R, S, V;
+  GEN y1, r, q;
+  long l = lgpol(x), n = l>>1, k;
+  if (lgpol(y)<=n) return matid2_FlxM(x[1]);
+  R = Flx_halfgcd(Flx_shift(x,-n),Flx_shift(y,-n),p);
+  V = FlxM_Flx_mul2(R,x,y,p); y1 = gel(V,2);
+  if (lgpol(y1)<=n) return gerepilecopy(av, R);
+  q = Flx_divrem(gel(V,1), y1, p, &r);
+  k = 2*n-degpol(y1);
+  S = Flx_halfgcd(Flx_shift(y1,-k), Flx_shift(r,-k),p);
+  return gerepileupto(av, FlxM_mul2(S,Flx_FlxM_qmul(q,R,p),p));
+}
+
+/* Return M in GL_2(Fl[X]) such that:
+if [a',b']~=M*[a,b]~ then degpol(a')>= (lgpol(a)>>1) >degpol(b')
+*/
+
+static GEN
+Flx_halfgcd_i(GEN x, GEN y, ulong p)
+{
+  if (lg(x)<=Flx_HALFGCD_LIMIT) return Flx_halfgcd_basecase(x,y,p);
+  return Flx_halfgcd_split(x,y,p);
+}
+
+GEN
+Flx_halfgcd(GEN x, GEN y, ulong p)
+{
+  pari_sp av;
+  GEN M,q,r;
+  long lx=lgpol(x), ly=lgpol(y);
+  if (!lx)
+  {
+      long v = x[1];
+      retmkmat2(mkcol2(pol0_Flx(v),pol1_Flx(v)),
+                mkcol2(pol1_Flx(v),pol0_Flx(v)));
+  }
+  if (ly < lx) return Flx_halfgcd_i(x,y,p);
+  av = avma;
+  q = Flx_divrem(y,x,p,&r);
+  M = Flx_halfgcd_i(x,r,p);
+  gcoeff(M,1,1) = Flx_sub(gcoeff(M,1,1), Flx_mul(q, gcoeff(M,1,2), p), p);
+  gcoeff(M,2,1) = Flx_sub(gcoeff(M,2,1), Flx_mul(q, gcoeff(M,2,2), p), p);
+  return gerepilecopy(av, M);
+}
+
+/*Do not garbage collect*/
+static GEN
+Flx_gcd_basecase(GEN a, GEN b, ulong p)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  ulong iter = 0;
+  if (lg(b) > lg(a)) swap(a, b);
+  while (lgpol(b))
+  {
+    GEN c = Flx_rem(a,b,p);
+    iter++; a = b; b = c;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flx_gcd (d = %ld)",degpol(c));
+      gerepileall(av,2, &a,&b);
+    }
+  }
+  return iter < 2 ? Flx_copy(a) : a;
+}
+
+GEN
+Flx_gcd(GEN x, GEN y, ulong p)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  if (!lgpol(x)) return Flx_copy(y);
+  while (lg(y)>Flx_GCD_LIMIT)
+  {
+    GEN c;
+    if (lgpol(y)<=(lgpol(x)>>1))
+    {
+      GEN r = Flx_rem(x, y, p);
+      x = y; y = r;
+    }
+    c = FlxM_Flx_mul2(Flx_halfgcd(x,y, p), x, y, p);
+    x = gel(c,1); y = gel(c,2);
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flx_gcd (y = %ld)",degpol(y));
+      gerepileall(av,2,&x,&y);
+    }
+  }
+  return gerepileuptoleaf(av, Flx_gcd_basecase(x,y,p));
+}
+
+int
+Flx_is_squarefree(GEN z, ulong p)
+{
+  pari_sp av = avma;
+  GEN d = Flx_gcd(z, Flx_deriv(z,p) , p);
+  long res= (degpol(d) == 0);
+  avma = av; return res;
+}
+
+static long
+Flx_is_smooth_squarefree(GEN f, long r, ulong p)
+{
+  pari_sp av = avma;
+  long i;
+  GEN sx = polx_Flx(f[1]), a = sx;
+  for(i=1;;i++)
+  {
+    if (degpol(f)<=r) {avma = av; return 1;}
+    a = Flxq_pow(Flx_rem(a,f,p),utoi(p),f,p);
+    if (Flx_equal(a, sx)) {avma = av; return 1;}
+    if (i==r) {avma = av; return 0;}
+    f = Flx_div(f, Flx_gcd(Flx_sub(a,sx,p),f,p),p);
+  }
+}
+
+static long
+Flx_is_l_pow(GEN x, ulong p)
+{
+  ulong i, lx = lgpol(x);
+  for (i=1; i<lx; i++)
+    if (x[i+2] && i%p) return 0;
+  return 1;
+}
+
+int
+Flx_is_smooth(GEN g, long r, ulong p)
+{
+  GEN f = gen_0;
+  while (1)
+  {
+    f = Flx_gcd(g, Flx_deriv(g, p), p);
+    if (!Flx_is_smooth_squarefree(Flx_div(g, f, p), r, p))
+      return 0;
+    if (degpol(f)==0) return 1;
+    g = Flx_is_l_pow(f,p) ? Flx_deflate(f, p): f;
+  }
+  return 0;
+}
+
+static GEN
+Flx_extgcd_basecase(GEN a, GEN b, ulong p, GEN *ptu, GEN *ptv)
+{
+  pari_sp av=avma, lim = stack_lim(av,2);
+  GEN u,v,d,d1,v1;
+  long vx = a[1];
+  d = a; d1 = b;
+  v = pol0_Flx(vx); v1 = pol1_Flx(vx);
+  while (lgpol(d1))
+  {
+    GEN r, q = Flx_divrem(d,d1,p, &r);
+    v = Flx_sub(v,Flx_mul(q,v1,p),p);
+    u=v; v=v1; v1=u;
+    u=r; d=d1; d1=u;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flx_extgcd (d = %ld)",degpol(d));
+      gerepileall(av,5, &d,&d1,&u,&v,&v1);
+    }
+  }
+  if (ptu) *ptu = Flx_div(Flx_sub(d, Flx_mul(b,v,p), p), a, p);
+  *ptv = v; return d;
+}
+
+static GEN
+Flx_extgcd_halfgcd(GEN x, GEN y, ulong p, GEN *ptu, GEN *ptv)
+{
+  pari_sp av=avma;
+  GEN u,v,R = matid2_FlxM(x[1]);
+  while (lg(y)>Flx_EXTGCD_LIMIT)
+  {
+    GEN M, c;
+    if (lgpol(y)<=(lgpol(x)>>1))
+    {
+      GEN r, q = Flx_divrem(x, y, p, &r);
+      x = y; y = r;
+      R = Flx_FlxM_qmul(q, R, p);
+    }
+    M = Flx_halfgcd(x,y, p);
+    c = FlxM_Flx_mul2(M, x,y, p);
+    R = FlxM_mul2(M, R, p);
+    x = gel(c,1); y = gel(c,2);
+    gerepileall(av,3,&x,&y,&R);
+  }
+  y = Flx_extgcd_basecase(x,y,p,&u,&v);
+  if (ptu) *ptu = Flx_addmulmul(u,v,gcoeff(R,1,1),gcoeff(R,2,1),p);
+  *ptv = Flx_addmulmul(u,v,gcoeff(R,1,2),gcoeff(R,2,2),p);
+  return y;
+}
+
+/* x and y in Z[X], return lift(gcd(x mod p, y mod p)). Set u and v st
+ * ux + vy = gcd (mod p) */
+GEN
+Flx_extgcd(GEN x, GEN y, ulong p, GEN *ptu, GEN *ptv)
+{
+  GEN d;
+  pari_sp ltop=avma;
+  if (lg(y)>Flx_EXTGCD_LIMIT)
+    d = Flx_extgcd_halfgcd(x, y, p, ptu, ptv);
+  else
+    d = Flx_extgcd_basecase(x, y, p, ptu, ptv);
+  gerepileall(ltop,ptu?3:2,&d,ptv,ptu);
+  return d;
+}
+
+ulong
+Flx_resultant(GEN a, GEN b, ulong p)
+{
+  long da,db,dc,cnt;
+  ulong lb, res = 1UL;
+  pari_sp av;
+  GEN c;
+
+  if (lgpol(a)==0 || lgpol(b)==0) return 0;
+  da = degpol(a);
+  db = degpol(b);
+  if (db > da)
+  {
+    swapspec(a,b, da,db);
+    if (both_odd(da,db)) res = p-res;
+  }
+  else if (!da) return 1; /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
+  cnt = 0; av = avma;
+  while (db)
+  {
+    lb = b[db+2];
+    c = Flx_rem(a,b, p);
+    a = b; b = c; dc = degpol(c);
+    if (dc < 0) { avma = av; return 0; }
+
+    if (both_odd(da,db)) res = p - res;
+    if (lb != 1) res = Fl_mul(res, Fl_powu(lb, da - dc, p), p);
+    if (++cnt == 100) { cnt = 0; gerepileall(av, 2, &a, &b); }
+    da = db; /* = degpol(a) */
+    db = dc; /* = degpol(b) */
+  }
+  avma = av; return Fl_mul(res, Fl_powu(b[2], da, p), p);
+}
+
+/* If resultant is 0, *ptU and *ptU are not set */
+ulong
+Flx_extresultant(GEN a, GEN b, ulong p, GEN *ptU, GEN *ptV)
+{
+  GEN z,q,u,v, x = a, y = b;
+  ulong lb, res = 1UL;
+  pari_sp av = avma;
+  long dx, dy, dz;
+  long vs=a[1];
+
+  dx = degpol(x);
+  dy = degpol(y);
+  if (dy > dx)
+  {
+    swap(x,y); lswap(dx,dy); pswap(ptU, ptV);
+    a = x; b = y;
+    if (both_odd(dx,dy)) res = p-res;
+  }
+  /* dx <= dy */
+  if (dx < 0) return 0;
+
+  u = pol0_Flx(vs);
+  v = pol1_Flx(vs); /* v = 1 */
+  while (dy)
+  { /* b u = x (a), b v = y (a) */
+    lb = y[dy+2];
+    q = Flx_divrem(x,y, p, &z);
+    x = y; y = z; /* (x,y) = (y, x - q y) */
+    dz = degpol(z); if (dz < 0) { avma = av; return 0; }
+    z = Flx_sub(u, Flx_mul(q,v, p), p);
+    u = v; v = z; /* (u,v) = (v, u - q v) */
+
+    if (both_odd(dx,dy)) res = p - res;
+    if (lb != 1) res = Fl_mul(res, Fl_powu(lb, dx-dz, p), p);
+    dx = dy; /* = degpol(x) */
+    dy = dz; /* = degpol(y) */
+  }
+  res = Fl_mul(res, Fl_powu(y[2], dx, p), p);
+  lb = Fl_mul(res, Fl_inv(y[2],p), p);
+  v = gerepileuptoleaf(av, Flx_Fl_mul(v, lb, p));
+  av = avma;
+  u = Flx_sub(Fl_to_Flx(res,vs), Flx_mul(b,v,p), p);
+  u = gerepileuptoleaf(av, Flx_div(u,a,p)); /* = (res - b v) / a */
+  *ptU = u;
+  *ptV = v; return res;
+}
+
+ulong
+Flx_eval(GEN x, ulong y, ulong p)
+{
+  ulong p1,r;
+  long j, i=lg(x)-1;
+  if (i<=2)
+    return (i==2)? x[2]: 0;
+  p1 = x[i];
+  /* specific attention to sparse polynomials (see poleval)*/
+  if (SMALL_ULONG(p))
+  {
+    for (i--; i>=2; i=j-1)
+    {
+      for (j=i; !x[j]; j--)
+        if (j==2)
+        {
+          if (i != j) y = Fl_powu(y, i-j+1, p);
+          return (p1 * y) % p;
+        }
+      r = (i==j)? y: Fl_powu(y, i-j+1, p);
+      p1 = ((p1*r) + x[j]) % p;
+    }
+  }
+  else
+  {
+    for (i--; i>=2; i=j-1)
+    {
+      for (j=i; !x[j]; j--)
+        if (j==2)
+        {
+          if (i != j) y = Fl_powu(y, i-j+1, p);
+          return Fl_mul(p1, y, p);
+        }
+      r = (i==j)? y: Fl_powu(y, i-j+1, p);
+      p1 = Fl_add((ulong)x[j], Fl_mul(p1,r,p), p);
+    }
+  }
+  return p1;
+}
+
+static GEN
+_Flx_mul(void *p, GEN a, GEN b)
+{
+  return Flx_mul(a,b, *(ulong*)p);
+}
+
+/* compute prod (x - a[i]) */
+GEN
+Flv_roots_to_pol(GEN a, ulong p, long vs)
+{
+  long i,k,lx = lg(a);
+  GEN p1;
+  if (lx == 1) return pol1_Flx(vs);
+  p1 = cgetg(lx, t_VEC);
+  for (k=1,i=1; i<lx-1; i+=2)
+    gel(p1,k++) = mkvecsmall4(vs, Fl_mul(a[i], a[i+1], p),
+                              Fl_neg(Fl_add(a[i],a[i+1],p),p), 1);
+  if (i < lx)
+    gel(p1,k++) = mkvecsmall3(vs, Fl_neg(a[i],p), 1);
+  setlg(p1, k); return divide_conquer_assoc(p1, (void *)&p, _Flx_mul);
+}
+
+GEN
+Flx_div_by_X_x(GEN a, ulong x, ulong p, ulong *rem)
+{
+  long l = lg(a), i;
+  GEN a0, z0;
+  GEN z = cgetg(l-1,t_VECSMALL);
+  z[1] = a[1];
+  a0 = a + l-1;
+  z0 = z + l-2; *z0 = *a0--;
+  if (SMALL_ULONG(p))
+  {
+    for (i=l-3; i>1; i--) /* z[i] = (a[i+1] + x*z[i+1]) % p */
+    {
+      ulong t = (*a0-- + x *  *z0--) % p;
+      *z0 = (long)t;
+    }
+    if (rem) *rem = (*a0 + x *  *z0) % p;
+  }
+  else
+  {
+    for (i=l-3; i>1; i--)
+    {
+      ulong t = Fl_add((ulong)*a0--, Fl_mul(x, *z0--, p), p);
+      *z0 = (long)t;
+    }
+    if (rem) *rem = Fl_add((ulong)*a0, Fl_mul(x, *z0, p), p);
+  }
+  return z;
+}
+
+/* u P(X) + v P(-X) */
+static GEN
+Flx_even_odd_comb(GEN P, ulong u, ulong v, ulong p)
+{
+  long i, l = lg(P);
+  GEN y = cgetg(l,t_VECSMALL);
+  y[1]=P[1];
+  for (i=2; i<l; i++)
+  {
+    ulong t = P[i];
+    y[i] = (t == 0)? 0:
+                     (i&1)? Fl_mul(t, Fl_sub(u, v, p), p)
+                          : Fl_mul(t, Fl_add(u, v, p), p);
+  }
+  return Flx_renormalize(y,l);
+}
+
+/* xa, ya = t_VECSMALL */
+GEN
+Flv_polint(GEN xa, GEN ya, ulong p, long vs)
+{
+  long i, j, n = lg(xa);
+  GEN T,dP, P = cgetg(n+1, t_VECSMALL);
+  GEN Q = Flv_roots_to_pol(xa, p, vs);
+  ulong inv;
+  P[1] = vs;
+  for (j=2; j<=n; j++) P[j] = 0UL;
+  for (i=1; i<n; i++)
+  {
+    if (!ya[i]) continue;
+    T = Flx_div_by_X_x(Q, xa[i], p, NULL);
+    inv = Fl_inv(Flx_eval(T,xa[i], p), p);
+    if (i < n-1 && (ulong)(xa[i] + xa[i+1]) == p)
+    {
+      dP = Flx_even_odd_comb(T, Fl_mul(ya[i],inv,p), Fl_mul(ya[i+1],inv,p), p);
+      i++; /* x_i = -x_{i+1} */
+    }
+    else
+      dP = Flx_Fl_mul(T, Fl_mul(ya[i],inv,p), p);
+    for (j=2; j<lg(dP); j++) P[j] = Fl_add(P[j], dP[j], p);
+    avma = (pari_sp)Q;
+  }
+  avma = (pari_sp)P;
+  return Flx_renormalize(P,n+1);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                               Flxq                                **/
+/**                                                                   **/
+/***********************************************************************/
+/* Flxq objects are defined as follows:
+   They are Flx modulo another Flx called q.
+*/
+
+/* Product of y and x in Z/pZ[X]/(T), as t_VECSMALL. */
+GEN
+Flxq_mul(GEN x,GEN y,GEN T,ulong p)
+{
+  return Flx_rem(Flx_mul(x,y,p),T,p);
+}
+
+/* Square of y in Z/pZ[X]/(T), as t_VECSMALL. */
+GEN
+Flxq_sqr(GEN x,GEN T,ulong p)
+{
+  return Flx_rem(Flx_sqr(x,p),T,p);
+}
+
+struct _Flxq {
+  GEN aut;
+  GEN T;
+  ulong p;
+};
+
+static GEN
+_Flxq_red(void *E, GEN x)
+{ struct _Flxq *s = (struct _Flxq *)E;
+  return Flx_rem(x, s->T, s->p); }
+static GEN
+_Flxq_add(void *E, GEN x, GEN y)
+{ struct _Flxq *s = (struct _Flxq *)E;
+  return Flx_add(x,y,s->p); }
+static GEN
+_Flxq_sqr(void *data, GEN x)
+{
+  struct _Flxq *D = (struct _Flxq*)data;
+  return Flxq_sqr(x, D->T, D->p);
+}
+static GEN
+_Flxq_mul(void *data, GEN x, GEN y)
+{
+  struct _Flxq *D = (struct _Flxq*)data;
+  return Flxq_mul(x,y, D->T, D->p);
+}
+static GEN
+_Flxq_one(void *data)
+{
+  struct _Flxq *D = (struct _Flxq*)data;
+  return pol1_Flx(get_Flx_var(D->T));
+}
+static GEN
+_Flxq_zero(void *data)
+{
+  struct _Flxq *D = (struct _Flxq*)data;
+  return pol0_Flx(get_Flx_var(D->T));
+}
+static GEN
+_Flxq_cmul(void *data, GEN P, long a, GEN x)
+{
+  struct _Flxq *D = (struct _Flxq*)data;
+  return Flx_Fl_mul(x, P[a+2], D->p);
+}
+
+/* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
+GEN
+Flxq_powu(GEN x, ulong n, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  struct _Flxq D;
+  GEN y;
+  switch(n)
+  {
+    case 0: return pol1_Flx(T[1]);
+    case 1: return Flx_copy(x);
+    case 2: return Flxq_sqr(x, T, p);
+  }
+  D.T = Flx_get_red(T, p); D.p = p;
+  y = gen_powu_i(x, n, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
+  return gerepileuptoleaf(av, y);
+}
+
+/* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
+GEN
+Flxq_pow(GEN x, GEN n, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  struct _Flxq D;
+  GEN y;
+  long s = signe(n);
+  if (!s) return pol1_Flx(get_Flx_var(T));
+  if (s < 0)
+    x = Flxq_inv(x,T,p);
+  if (is_pm1(n)) return s < 0 ? x : Flx_copy(x);
+  D.T = Flx_get_red(T, p); D.p = p;
+  y = gen_pow_i(x, n, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
+  return gerepileuptoleaf(av, y);
+}
+
+/* Inverse of x in Z/lZ[X]/(T) or NULL if inverse doesn't exist
+ * not stack clean.
+ */
+GEN
+Flxq_invsafe(GEN x, GEN T, ulong p)
+{
+  GEN V, z = Flx_extgcd(get_Flx_mod(T), x, p, NULL, &V);
+  ulong iz;
+  if (degpol(z)) return NULL;
+  iz = Fl_inv ((ulong)z[2], p);
+  return Flx_Fl_mul(V, iz, p);
+}
+
+GEN
+Flxq_inv(GEN x,GEN T,ulong p)
+{
+  pari_sp av=avma;
+  GEN U = Flxq_invsafe(x, T, p);
+  if (!U) pari_err_INV("Flxq_inv",Flx_to_ZX(x));
+  return gerepileuptoleaf(av, U);
+}
+
+GEN
+Flxq_div(GEN x,GEN y,GEN T,ulong p)
+{
+  pari_sp av = avma;
+  return gerepileuptoleaf(av, Flxq_mul(x,Flxq_inv(y,T,p),T,p));
+}
+
+GEN
+Flxq_powers(GEN x, long l, GEN T, ulong p)
+{
+  struct _Flxq D;
+  int use_sqr = (degpol(x)<<1) >= get_Flx_degree(T);
+  D.T = Flx_get_red(T, p); D.p = p;
+  return gen_powers(x, l, use_sqr, (void*)&D, &_Flxq_sqr, &_Flxq_mul, &_Flxq_one);
+}
+
+GEN
+Flxq_matrix_pow(GEN y, long n, long m, GEN P, ulong l)
+{
+  return FlxV_to_Flm(Flxq_powers(y,m-1,P,l),n);
+}
+
+static struct bb_algebra Flxq_algebra = { _Flxq_red,_Flxq_add,_Flxq_mul,_Flxq_sqr,_Flxq_one,_Flxq_zero};
+
+GEN
+Flx_FlxqV_eval(GEN Q, GEN x, GEN T, ulong p)
+{
+  struct _Flxq D;
+  D.T = Flx_get_red(T, p); D.p=p;
+  return gen_bkeval_powers(Q,degpol(Q),x,(void*)&D,&Flxq_algebra,_Flxq_cmul);
+}
+
+GEN
+Flx_Flxq_eval(GEN Q, GEN x, GEN T, ulong p)
+{
+  int use_sqr = (degpol(x)<<1) >= get_Flx_degree(T);
+  struct _Flxq D;
+  D.T = Flx_get_red(T, p); D.p=p;
+  return gen_bkeval(Q,degpol(Q),x,use_sqr,(void*)&D,&Flxq_algebra,_Flxq_cmul);
+}
+
+static GEN
+Flxq_autpow_sqr(void *E, GEN x)
+{
+  struct _Flxq *D = (struct _Flxq*)E;
+  return Flx_Flxq_eval(x, x, D->T, D->p);
+}
+static GEN
+Flxq_autpow_mul(void *E, GEN x, GEN y)
+{
+  struct _Flxq *D = (struct _Flxq*)E;
+  return Flx_Flxq_eval(x, y, D->T, D->p);
+}
+
+GEN
+Flxq_autpow(GEN x, ulong n, GEN T, ulong p)
+{
+  struct _Flxq D;
+  D.T = Flx_get_red(T, p); D.p = p;
+  if (n==0) return polx_Flx(T[1]);
+  if (n==1) return Flx_copy(x);
+  return gen_powu(x,n,(void*)&D,Flxq_autpow_sqr,Flxq_autpow_mul);
+}
+
+static GEN
+Flxq_autsum_mul(void *E, GEN x, GEN y)
+{
+  struct _Flxq *D = (struct _Flxq*)E;
+  GEN phi1 = gel(x,1), a1 = gel(x,2);
+  GEN phi2 = gel(y,1), a2 = gel(y,2);
+  ulong d = brent_kung_optpow(maxss(degpol(phi1),degpol(a1)),2,1);
+  GEN V2 = Flxq_powers(phi2,d,D->T,D->p);
+  GEN phi3 = Flx_FlxqV_eval(phi1,V2,D->T,D->p);
+  GEN aphi = Flx_FlxqV_eval(a1,V2,D->T,D->p);
+  GEN a3 = Flxq_mul(aphi,a2,D->T,D->p);
+  return mkvec2(phi3, a3);
+}
+static GEN
+Flxq_autsum_sqr(void *E, GEN x)
+{ return Flxq_autsum_mul(E, x, x); }
+
+GEN
+Flxq_autsum(GEN x, ulong n, GEN T, ulong p)
+{
+  struct _Flxq D;
+  D.T = Flx_get_red(T, p); D.p = p;
+  return gen_powu(x,n,(void*)&D,Flxq_autsum_sqr,Flxq_autsum_mul);
+}
+
+static long
+bounded_order(ulong p, GEN b, long k)
+{
+  long i;
+  GEN a=modii(utoi(p),b);
+  for(i=1;i<k;i++)
+  {
+    if (equali1(a))
+      return i;
+    a = modii(muliu(a,p),b);
+  }
+  return 0;
+}
+
+/*
+  n = (p^d-a)\b
+  b = bb*p^vb
+  p^k = 1 [bb]
+  d = m*k+r+vb
+  u = (p^k-1)/bb;
+  v = (p^(r+vb)-a)/b;
+  w = (p^(m*k)-1)/(p^k-1)
+  n = p^r*w*u+v
+  w*u = p^vb*(p^(m*k)-1)/b
+  n = p^(r+vb)*(p^(m*k)-1)/b+(p^(r+vb)-a)/b
+*/
+
+static GEN
+Flxq_pow_Frobenius(GEN x, GEN n, GEN aut, GEN T, ulong p)
+{
+  pari_sp av=avma;
+  long d = get_Flx_degree(T);
+  GEN an = absi(n), z, q;
+  if (cmpiu(an,p)<0 || cmpis(an,d)<=0)
+    return Flxq_pow(x, n, T, p);
+  q = powuu(p, d);
+  if (dvdii(q, n))
+  {
+    long vn = logint(an,utoi(p),NULL)-1;
+    GEN autvn = vn==1 ? aut: Flxq_autpow(aut,vn,T,p);
+    z = Flx_Flxq_eval(x,autvn,T,p);
+  } else
+  {
+    GEN b = diviiround(q, an), a = subii(q, mulii(an,b));
+    GEN bb, u, v, autk;
+    long vb = Z_lvalrem(b,p,&bb);
+    long m, r, k = is_pm1(bb) ? 1 : bounded_order(p,bb,d);
+    if (!k || d-vb<k) return Flxq_pow(x,n, T, p);
+    m = (d-vb)/k; r = (d-vb)%k;
+    u = diviiexact(subis(powuu(p,k),1),bb);
+    v = diviiexact(subii(powuu(p,r+vb),a),b);
+    autk = k==1 ? aut: Flxq_autpow(aut,k,T,p);
+    if (r)
+    {
+      GEN autr = r==1 ? aut: Flxq_autpow(aut,r,T,p);
+      z = Flx_Flxq_eval(x,autr,T,p);
+    } else z = x;
+    if (m > 1) z = gel(Flxq_autsum(mkvec2(autk, z), m, T, p), 2);
+    if (!is_pm1(u)) z = Flxq_pow(z, u, T, p);
+    if (signe(v)) z = Flxq_mul(z, Flxq_pow(x, v, T, p), T, p);
+  }
+  return gerepileupto(av,signe(n)>0 ? z : Flxq_inv(z,T,p));
+}
+
+static GEN
+Flx_Frobenius(GEN T, ulong p)
+{
+  return Flxq_powu(polx_Flx(get_Flx_var(T)), p, T, p);
+}
+
+static GEN
+_Flxq_pow(void *data, GEN x, GEN n)
+{
+  struct _Flxq *D = (struct _Flxq*)data;
+  return Flxq_pow_Frobenius(x, n, D->aut, D->T, D->p);
+}
+
+static GEN
+_Flxq_rand(void *data)
+{
+  pari_sp av=avma;
+  struct _Flxq *D = (struct _Flxq*)data;
+  GEN z;
+  do
+  {
+    avma = av;
+    z = random_Flx(get_Flx_degree(D->T),get_Flx_var(D->T),D->p);
+  } while (lgpol(z)==0);
+  return z;
+}
+
+static GEN
+Flxq_easylog(void* E, GEN a, GEN g, GEN ord)
+{
+  struct _Flxq *f = (struct _Flxq *)E;
+  if (Flx_equal1(a)) return gen_0;
+  if (Flx_equal(a,g)) return gen_1;
+  if (!degpol(a) && !degpol(g))
+    return Fp_log(utoi(a[2]),utoi(g[2]),ord, utoi(f->p));
+  if (typ(ord)!=t_INT || get_Flx_degree(f->T)<4 || cmpiu(ord,1UL<<27)<0)
+    return NULL;
+  return Flxq_log_index(a,g,ord,f->T,f->p);
+}
+int
+Flx_equal(GEN V, GEN W)
+{
+  long l = lg(V);
+  if (lg(W) != l) return 0;
+  while (--l > 1) /* do not compare variables, V[1] */
+    if (V[l] != W[l]) return 0;
+  return 1;
+}
+
+static const struct bb_group Flxq_star={_Flxq_mul,_Flxq_pow,_Flxq_rand,hash_GEN,Flx_equal,Flx_equal1,Flxq_easylog};
+
+GEN
+Flxq_order(GEN a, GEN ord, GEN T, ulong p)
+{
+  struct _Flxq E;
+  E.T=T; E.p=p; E.aut = Flx_Frobenius(T,p);
+  return gen_order(a,ord,(void*)&E,&Flxq_star);
+}
+
+GEN
+Flxq_log(GEN a, GEN g, GEN ord, GEN T, ulong p)
+{
+  struct _Flxq E;
+  GEN v = dlog_get_ordfa(ord);
+  ord = mkvec2(gel(v,1),ZM_famat_limit(gel(v,2),int2n(27)));
+  E.T=T; E.p=p; E.aut = Flx_Frobenius(T,p);
+  return gen_PH_log(a,g,ord,(void*)&E,&Flxq_star);
+}
+
+GEN
+Flxq_sqrtn(GEN a, GEN n, GEN T, ulong p, GEN *zeta)
+{
+  struct _Flxq E;
+  GEN o;
+  if (!lgpol(a))
+  {
+    if (signe(n) < 0) pari_err_INV("Flxq_sqrtn",a);
+    if (zeta)
+      *zeta=pol1_Flx(get_Flx_var(T));
+    return pol0_Flx(get_Flx_var(T));
+  }
+  E.T=T; E.p=p; E.aut = Flx_Frobenius(T,p);
+  o = addis(powuu(p,get_Flx_degree(T)),-1);
+  return gen_Shanks_sqrtn(a,n,o,zeta,(void*)&E,&Flxq_star);
+}
+
+GEN
+Flxq_sqrt(GEN a, GEN T, ulong p)
+{
+  return Flxq_sqrtn(a, gen_2, T, p, NULL);
+}
+
+/* assume T irreducible mod p */
+int
+Flxq_issquare(GEN x, GEN T, ulong p)
+{
+  pari_sp av;
+  GEN m;
+  ulong z;
+  if (lgpol(x) == 0 || p == 2) return 1;
+  av = avma;
+  m = diviuexact(subis(powuu(p, get_Flx_degree(T)), 1), p - 1);
+  z = Flxq_pow(x, m, T, p)[2];
+  avma = av; return krouu(z, p) == 1;
+}
+
+/* assume T irreducible mod p */
+int
+Flxq_is2npower(GEN x, long n, GEN T, ulong p)
+{
+  pari_sp av;
+  GEN m;
+  int z;
+  if (n==1) return Flxq_issquare(x, T, p);
+  if (lgpol(x) == 0 || p == 2) return 1;
+  av = avma;
+  m = shifti(subis(powuu(p, get_Flx_degree(T)), 1), -n);
+  z = Flx_equal1(Flxq_pow(x, m, T, p));
+  avma = av; return z;
+}
+
+GEN
+Flxq_lroot_fast(GEN a, GEN sqx, GEN T, long p)
+{
+  pari_sp av=avma;
+  GEN A = Flx_splitting(a,p);
+  return gerepileuptoleaf(av, FlxqV_dotproduct(A,sqx,T,p));
+}
+
+GEN
+Flxq_lroot(GEN a, GEN T, long p)
+{
+  pari_sp av=avma;
+  long n = get_Flx_degree(T), d = degpol(a);
+  long v = get_Flx_var(T);
+  GEN sqx,V;
+  if (n==1) return leafcopy(a);
+  if (n==2) return Flxq_powu(a, p, T, p);
+  sqx = Flxq_autpow(Flxq_powu(polx_Flx(v), p, T, p), n-1, T, p);
+  if (d==1 && a[2]==0 && a[3]==1) return gerepileuptoleaf(av, sqx);
+  if (d>=p)
+  {
+    V = Flxq_powers(sqx,p-1,T,p);
+    return gerepileuptoleaf(av, Flxq_lroot_fast(a,V,T,p));
+  } else
+    return gerepileuptoleaf(av, Flx_Flxq_eval(a,sqx,T,p));
+}
+
+ulong
+Flxq_norm(GEN x, GEN TB, ulong p)
+{
+  GEN T = get_Flx_mod(TB);
+  ulong y = Flx_resultant(T, x, p);
+  ulong L = Flx_lead(T);
+  if ( L==1 || lgpol(x)==0) return y;
+  return Fl_div(y, Fl_powu(L, (ulong)degpol(x), p), p);
+}
+
+ulong
+Flxq_trace(GEN x, GEN TB, ulong p)
+{
+  pari_sp av = avma;
+  ulong t;
+  GEN T = get_Flx_mod(TB);
+  long n = degpol(T)-1;
+  GEN z = Flxq_mul(x, Flx_deriv(T, p), TB, p);
+  t = degpol(z)<n ? 0 : Fl_div(z[2+n],T[3+n],p);
+  avma=av;
+  return t;
+}
+
+/*x must be reduced*/
+GEN
+Flxq_charpoly(GEN x, GEN TB, ulong p)
+{
+  pari_sp ltop=avma;
+  GEN T = get_Flx_mod(TB);
+  GEN xm1 = deg1pol_shallow(pol1_Flx(x[1]),Flx_neg(x,p),evalvarn(MAXVARN));
+  return gerepileupto(ltop, Flx_FlxY_resultant(T, xm1 ,p));
+}
+
+GEN
+Flxq_minpoly(GEN x, GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  GEN G, R=Flxq_charpoly(x, T, p);
+  GEN dR=Flx_deriv(R,p);
+  while (!lgpol(dR))
+  {
+    R  = Flx_deflate(R,p);
+    dR = Flx_deriv(R,p);
+  }
+  G=Flx_gcd(R,dR,p);
+  G=Flx_normalize(G,p);
+  G=Flx_div(R,G,p);
+  return gerepileupto(ltop,G);
+}
+
+GEN
+Flxq_conjvec(GEN x, GEN T, ulong p)
+{
+  long i, l = 1+get_Flx_degree(T);
+  GEN z = cgetg(l,t_COL);
+  T = Flx_get_red(T,p);
+  gel(z,1) = Flx_copy(x);
+  for (i=2; i<l; i++) gel(z,i) = Flxq_powu(gel(z,i-1), p, T, p);
+  return z;
+}
+
+GEN
+gener_Flxq(GEN T, ulong p, GEN *po)
+{
+  long i, j;
+  long vT = get_Flx_var(T), f =get_Flx_degree(T);
+  ulong p_1;
+  GEN g, L, L2, o, q, F;
+  pari_sp av0, av;
+
+  if (f == 1) {
+    GEN fa;
+    o = utoipos(p-1);
+    fa = Z_factor(o);
+    L = gel(fa,1);
+    L = vecslice(L, 2, lg(L)-1); /* remove 2 for efficiency */
+    g = Fl_to_Flx(pgener_Fl_local(p, vec_to_vecsmall(L)), vT);
+    if (po) *po = mkvec2(o, fa);
+    return g;
+  }
+
+  av0 = avma; p_1 = p - 1;
+  q = diviuexact(subis(powuu(p,f), 1), p_1);
+
+  L = cgetg(1, t_VECSMALL);
+  if (p > 3)
+  {
+    ulong t;
+    (void)u_lvalrem(p_1, 2, &t);
+    L = gel(factoru(t),1);
+    for (i=lg(L)-1; i; i--) L[i] = p_1 / L[i];
+  }
+  o = factor_pn_1(utoipos(p),f);
+  L2 = leafcopy( gel(o, 1) );
+  for (i = j = 1; i < lg(L2); i++)
+  {
+    if (umodui(p_1, gel(L2,i)) == 0) continue;
+    gel(L2,j++) = diviiexact(q, gel(L2,i));
+  }
+  setlg(L2, j);
+  F = Flxq_powu(polx_Flx(evalvarn(vT)), p, T, p); /* Frobenius */
+  for (av = avma;; avma = av)
+  {
+    ulong RES;
+    GEN tt;
+    g = random_Flx(f, vT, p);
+    if (degpol(g) < 1) continue;
+    if (p == 2) tt = g;
+    else
+    {
+      ulong t = Flxq_norm(g, T, p);
+      if (t == 1 || !is_gener_Fl(t, p, p_1, L)) continue;
+      tt = Flxq_powu(g, p_1>>1, T, p);
+    }
+    RES = p_1;
+    for (i = 1; i < j; i++)
+    {
+      GEN a = Flxq_pow_Frobenius(tt, gel(L2,i), F, T, p);
+      if (!degpol(a) && (ulong)a[2] == RES) break;
+    }
+    if (i == j) break;
+  }
+  if (!po)
+  {
+    avma = (pari_sp)g;
+    g = gerepileuptoleaf(av0, g);
+  }
+  else {
+    *po = mkvec2(subis(powuu(p,f), 1), o);
+    gerepileall(av0, 2, &g, po);
+  }
+  return g;
+}
+
+static GEN
+_Flxq_neg(void *E, GEN x)
+{ struct _Flxq *s = (struct _Flxq *)E;
+  return Flx_neg(x,s->p); }
+
+static GEN
+_Flxq_rmul(void *E, GEN x, GEN y)
+{ struct _Flxq *s = (struct _Flxq *)E;
+  return Flx_mul(x,y,s->p); }
+
+static GEN
+_Flxq_inv(void *E, GEN x)
+{ struct _Flxq *s = (struct _Flxq *)E;
+  return Flxq_inv(x,s->T,s->p); }
+
+static int
+_Flxq_equal0(GEN x) { return lgpol(x)==0; }
+
+static GEN
+_Flxq_s(void *E, long x)
+{ struct _Flxq *s = (struct _Flxq *)E;
+  ulong u = x<0 ? s->p+x: (ulong)x;
+  return Fl_to_Flx(u, get_Flx_var(s->T));
+}
+
+static const struct bb_field Flxq_field={_Flxq_red,_Flxq_add,_Flxq_rmul,_Flxq_neg,
+                                         _Flxq_inv,_Flxq_equal0,_Flxq_s};
+
+const struct bb_field *get_Flxq_field(void **E, GEN T, ulong p)
+{
+  GEN z = new_chunk(sizeof(struct _Flxq));
+  struct _Flxq *e = (struct _Flxq *) z;
+  e->T = Flx_get_red(T, p); e->p  = p; *E = (void*)e;
+  return &Flxq_field;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                               FlxV                                **/
+/**                                                                   **/
+/***********************************************************************/
+/* FlxV are t_VEC with Flx coefficients. */
+
+GEN
+FlxV_Flc_mul(GEN V, GEN W, ulong p)
+{
+  pari_sp ltop=avma;
+  long i;
+  GEN z = Flx_Fl_mul(gel(V,1),W[1],p);
+  for(i=2;i<lg(V);i++)
+    z=Flx_add(z,Flx_Fl_mul(gel(V,i),W[i],p),p);
+  return gerepileuptoleaf(ltop,z);
+}
+
+GEN
+ZXV_to_FlxV(GEN v, ulong p)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_VEC);
+  for (j=1; j<N; j++) gel(y,j) = ZX_to_Flx(gel(v,j), p);
+  return y;
+}
+
+GEN
+ZXT_to_FlxT(GEN z, ulong p)
+{
+  if (typ(z) == t_POL)
+    return ZX_to_Flx(z, p);
+  else
+  {
+    long i,l = lg(z);
+    GEN x = cgetg(l, t_VEC);
+    for (i=1; i<l; i++) gel(x,i) = ZXT_to_FlxT(gel(z,i), p);
+    return x;
+  }
+}
+
+GEN
+FlxV_to_Flm(GEN v, long n)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_MAT);
+  for (j=1; j<N; j++) gel(y,j) = Flx_to_Flv(gel(v,j), n);
+  return y;
+}
+
+GEN
+FlxV_red(GEN z, ulong p)
+{
+  GEN res;
+  long i, l = lg(z);
+  res = cgetg(l,t_VEC);
+  for(i=1;i<l;i++) gel(res,i) = Flx_red(gel(z,i),p);
+  return res;
+}
+
+GEN
+FlxT_red(GEN z, ulong p)
+{
+  if (typ(z) == t_VECSMALL)
+    return Flx_red(z, p);
+  else
+  {
+    long i,l = lg(z);
+    GEN x = cgetg(l, t_VEC);
+    for (i=1; i<l; i++) gel(x,i) = FlxT_red(gel(z,i), p);
+    return x;
+  }
+}
+
+GEN
+FlxqV_dotproduct(GEN x, GEN y, GEN T, ulong p)
+{
+  long i, lx = lg(x);
+  pari_sp av;
+  GEN c;
+  if (lx == 1) return gen_0;
+  av = avma; c = Flx_mul(gel(x,1),gel(y,1), p);
+  for (i=2; i<lx; i++) c = Flx_add(c, Flx_mul(gel(x,i),gel(y,i), p), p);
+  return gerepileuptoleaf(av, Flx_rem(c,T,p));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                               FlxX                                **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* FlxX are t_POL with Flx coefficients.
+ * Normally the variable ordering should be respected.*/
+
+/*Similar to normalizepol, in place*/
+/*FlxX_renormalize=zxX_renormalize */
+GEN
+FlxX_renormalize(GEN /*in place*/ x, long lx)
+{
+  long i;
+  for (i = lx-1; i>1; i--)
+    if (lgpol(gel(x,i))) break;
+  stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
+  setlg(x, i+1); setsigne(x, i!=1); return x;
+}
+
+GEN
+pol1_FlxX(long v, long sv)
+{
+  GEN z = cgetg(3, t_POL);
+  z[1] = evalsigne(1) | evalvarn(v);
+  gel(z,2) = pol1_Flx(sv); return z;
+}
+
+GEN
+polx_FlxX(long v, long sv)
+{
+  GEN z = cgetg(4, t_POL);
+  z[1] = evalsigne(1) | evalvarn(v);
+  gel(z,2) = pol0_Flx(sv);
+  gel(z,3) = pol1_Flx(sv); return z;
+}
+
+/*Lift coefficient of B to constant Flx, to give a FlxY*/
+GEN
+Fly_to_FlxY(GEN B, long sv)
+{
+  long lb=lg(B);
+  long i;
+  GEN b=cgetg(lb,t_POL);
+  b[1]=evalsigne(1)|(((ulong)B[1])&VARNBITS);
+  for (i=2; i<lb; i++)
+    gel(b,i) = Fl_to_Flx(B[i], sv);
+  return FlxX_renormalize(b, lb);
+}
+
+GEN
+FlxX_to_ZXX(GEN B)
+{
+  long i, lb = lg(B);
+  GEN b = cgetg(lb,t_POL);
+  for (i=2; i<lb; i++)
+  {
+    GEN c = gel(B,i);
+    switch(lgpol(c))
+    {
+      case 0:  c = gen_0; break;
+      case 1:  c = utoi(c[2]); break;
+      default: c = Flx_to_ZX(c); break;
+    }
+    gel(b,i) = c;
+  }
+  b[1] = B[1]; return b;
+}
+
+/* Note: v is used _only_ for the t_INT. It must match
+ * the variable of any t_POL coefficients. */
+GEN
+ZXX_to_FlxX(GEN B, ulong p, long v)
+{
+  long lb=lg(B);
+  long i;
+  GEN b=cgetg(lb,t_POL);
+  b[1]=evalsigne(1)|(((ulong)B[1])&VARNBITS);
+  for (i=2; i<lb; i++)
+    switch (typ(gel(B,i)))
+    {
+    case t_INT:
+      gel(b,i) = Z_to_Flx(gel(B,i), p, v);
+      break;
+    case t_POL:
+      gel(b,i) = ZX_to_Flx(gel(B,i), p);
+      break;
+    }
+  return FlxX_renormalize(b, lb);
+}
+
+GEN
+ZXXV_to_FlxXV(GEN V, ulong p, long v)
+{
+  long j, N = lg(V);
+  GEN y = cgetg(N, t_VEC);
+  for (j=1; j<N; j++) gel(y,j) = ZXX_to_FlxX(gel(V,j), p, v);
+  return y;
+}
+
+GEN
+FlxX_to_FlxC(GEN x, long N, long sv)
+{
+  long i, l;
+  GEN z;
+  l = lg(x)-1; x++;
+  if (l > N+1) l = N+1; /* truncate higher degree terms */
+  z = cgetg(N+1,t_COL);
+  for (i=1; i<l ; i++) gel(z,i) = gel(x,i);
+  for (   ; i<=N; i++) gel(z,i) = pol0_Flx(sv);
+  return z;
+}
+
+GEN
+FlxXV_to_FlxM(GEN v, long n, long sv)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_MAT);
+  for (j=1; j<N; j++) gel(y,j) = FlxX_to_FlxC(gel(v,j), n, sv);
+  return y;
+}
+
+/* matrix whose entries are given by the coeffs of the polynomial v in
+ * two variables (considered as degree n polynomials) */
+GEN
+FlxX_to_Flm(GEN v, long n)
+{
+  long j, N = lg(v)-1;
+  GEN y = cgetg(N, t_MAT);
+  v++;
+  for (j=1; j<N; j++) gel(y,j) = Flx_to_Flv(gel(v,j), n);
+  return y;
+}
+
+GEN
+Flm_to_FlxX(GEN x, long v,long w)
+{
+  long j, lx = lg(x);
+  GEN y = cgetg(lx+1, t_POL);
+  y[1]=evalsigne(1) | v;
+  y++;
+  for (j=1; j<lx; j++) gel(y,j) = Flv_to_Flx(gel(x,j), w);
+  return FlxX_renormalize(--y, lx+1);
+}
+
+/* P(X,Y) --> P(Y,X), n-1 is the degree in Y */
+GEN
+FlxX_swap(GEN x, long n, long ws)
+{
+  long j, lx = lg(x), ly = n+3;
+  GEN y = cgetg(ly, t_POL);
+  y[1] = x[1];
+  for (j=2; j<ly; j++)
+  {
+    long k;
+    GEN p1 = cgetg(lx, t_VECSMALL);
+    p1[1] = ws;
+    for (k=2; k<lx; k++)
+      if (j<lg(gel(x,k)))
+        p1[k] = mael(x,k,j);
+      else
+        p1[k] = 0;
+    gel(y,j) = Flx_renormalize(p1,lx);
+  }
+  return FlxX_renormalize(y,ly);
+}
+
+static GEN
+zxX_to_Kronecker_spec(GEN P, long lp, long n)
+{ /* P(X) = sum Pi(Y) * X^i, return P( Y^(2n-1) ) */
+  long i, j, k, l, N = (n<<1) + 1;
+  GEN y = cgetg((N-2)*lp + 2, t_VECSMALL) + 2;
+  for (k=i=0; i<lp; i++)
+  {
+    GEN c = gel(P,i);
+    l = lg(c);
+    if (l-3 >= n)
+      pari_err_BUG("zxX_to_Kronecker, P is not reduced mod Q");
+    for (j=2; j < l; j++) y[k++] = c[j];
+    if (i == lp-1) break;
+    for (   ; j < N; j++) y[k++] = 0;
+  }
+  y -= 2;
+  y[1] = P[1]; setlg(y, k+2); return y;
+}
+
+GEN
+zxX_to_Kronecker(GEN P, GEN Q)
+{
+  GEN z = zxX_to_Kronecker_spec(P+2, lg(P)-2, degpol(Q));
+  z[1] = P[1]; return z;
+}
+
+GEN
+FlxX_add(GEN x, GEN y, ulong p)
+{
+  long i,lz;
+  GEN z;
+  long lx=lg(x);
+  long ly=lg(y);
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx; z = cgetg(lz, t_POL); z[1]=x[1];
+  for (i=2; i<ly; i++) gel(z,i) = Flx_add(gel(x,i), gel(y,i), p);
+  for (   ; i<lx; i++) gel(z,i) = Flx_copy(gel(x,i));
+  return FlxX_renormalize(z, lz);
+}
+
+GEN
+FlxX_Flx_add(GEN y, GEN x, ulong p)
+{
+  long i, lz = lg(y);
+  GEN z;
+  if (signe(y) == 0) return scalarpol(x, varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = Flx_add(gel(y,2), x, p);
+  if (lz == 3) z = FlxX_renormalize(z,lz);
+  else
+    for(i=3;i<lz;i++) gel(z,i) = Flx_copy(gel(y,i));
+  return z;
+}
+
+GEN
+FlxX_neg(GEN x, ulong p)
+{
+  long i, lx=lg(x);
+  GEN z = cgetg(lx, t_POL);
+  z[1]=x[1];
+  for (i=2; i<lx; i++) gel(z,i) = Flx_neg(gel(x,i), p);
+  return z;
+}
+
+GEN
+FlxX_Fl_mul(GEN x, ulong y, ulong p)
+{
+  long i, lx=lg(x);
+  GEN z = cgetg(lx, t_POL);
+  z[1]=x[1];
+  for (i=2; i<lx; i++) gel(z,i) = Flx_Fl_mul(gel(x,i), y, p);
+  return FlxX_renormalize(z, lx);
+}
+
+GEN
+FlxX_triple(GEN x, ulong p)
+{
+  long i, lx=lg(x);
+  GEN z = cgetg(lx, t_POL);
+  z[1]=x[1];
+  for (i=2; i<lx; i++) gel(z,i) = Flx_triple(gel(x,i), p);
+  return FlxX_renormalize(z, lx);
+}
+
+GEN
+FlxX_double(GEN x, ulong p)
+{
+  long i, lx=lg(x);
+  GEN z = cgetg(lx, t_POL);
+  z[1]=x[1];
+  for (i=2; i<lx; i++) gel(z,i) = Flx_double(gel(x,i), p);
+  return FlxX_renormalize(z, lx);
+}
+
+static GEN
+FlxX_subspec(GEN x, GEN y, ulong p, long lx, long ly)
+{
+  long i,lz;
+  GEN z;
+
+  if (ly <= lx)
+  {
+    lz = lx+2; z = cgetg(lz, t_POL)+2;
+    for (i=0; i<ly; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
+    for (   ; i<lx; i++) gel(z,i) = Flx_copy(gel(x,i));
+  }
+  else
+  {
+    lz = ly+2; z = cgetg(lz, t_POL)+2;
+    for (i=0; i<lx; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
+    for (   ; i<ly; i++) gel(z,i) = Flx_neg(gel(y,i),p);
+  }
+ return FlxX_renormalize(z-2, lz);
+}
+
+GEN
+FlxX_sub(GEN x, GEN y, ulong p)
+{
+  long lx,ly,i,lz;
+  GEN z;
+  lx = lg(x); ly = lg(y);
+  lz=maxss(lx,ly);
+  z = cgetg(lz,t_POL);
+  if (lx >= ly)
+  {
+    z[1] = x[1];
+    for (i=2; i<ly; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
+    for (   ; i<lx; i++) gel(z,i) = Flx_copy(gel(x,i));
+    if (lx==ly) z = FlxX_renormalize(z, lz);
+  }
+  else
+  {
+    z[1] = y[1];
+    for (i=2; i<lx; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
+    for (   ; i<ly; i++) gel(z,i) = Flx_neg(gel(y,i),p);
+  }
+  if (!lgpol(z)) { avma = (pari_sp)(z + lz); z = pol_0(varn(x)); }
+  return z;
+}
+
+GEN
+FlxX_Flx_mul(GEN P, GEN U, ulong p)
+{
+  long i, lP = lg(P);
+  GEN res = cgetg(lP,t_POL);
+  res[1] = P[1];
+  for(i=2; i<lP; i++)
+    gel(res,i) = Flx_mul(U,gel(P,i), p);
+  return FlxX_renormalize(res, lP);
+}
+
+GEN
+FlxY_evalx(GEN Q, ulong x, ulong p)
+{
+  GEN z;
+  long i, lb = lg(Q);
+  z = cgetg(lb,t_VECSMALL); z[1] = evalvarn(varn(Q));
+  for (i=2; i<lb; i++) z[i] = Flx_eval(gel(Q,i), x, p);
+  return Flx_renormalize(z, lb);
+}
+
+GEN
+FlxY_Flxq_evalx(GEN P, GEN x, GEN T, ulong p)
+{
+  long i, lP = lg(P);
+  GEN res = cgetg(lP,t_POL);
+  res[1] = P[1];
+  for(i=2; i<lP; i++)
+    gel(res,i) = Flx_Flxq_eval(gel(P,i), x, T, p);
+  return FlxX_renormalize(res, lP);
+}
+
+GEN
+FlxY_Flx_div(GEN x, GEN y, ulong p)
+{
+  long i, l;
+  GEN z;
+  if (degpol(y) == 0)
+  {
+    ulong t = (ulong)y[2];
+    if (t == 1) return x;
+    t = Fl_inv(t, p);
+    z = cgetg_copy(x, &l); z[1] = x[1];
+    for (i=2; i<l; i++) gel(z,i) = Flx_Fl_mul(gel(x,i),t,p);
+  }
+  else
+  {
+    z = cgetg_copy(x, &l); z[1] = x[1];
+    for (i=2; i<l; i++) gel(z,i) = Flx_div(gel(x,i),y,p);
+  }
+  return z;
+}
+
+GEN
+FlxX_shift(GEN a, long n)
+{
+  long i, l = lg(a);
+  GEN  b;
+  long vs;
+  if (!signe(a)) return a;
+  vs = mael(a,2,1);
+  b = cgetg(l+n, t_POL);
+  b[1] = a[1];
+  for (i=0; i<n; i++) gel(b,2+i) = pol0_Flx(vs);
+  for (i=2; i<l; i++) b[i+n] = a[i];
+  return b;
+}
+
+static GEN
+FlxX_recipspec(GEN x, long l, long n, long vs)
+{
+  long i;
+  GEN z=cgetg(n+2,t_POL)+2;
+  for(i=0; i<l; i++)
+    gel(z,n-i-1) = Flx_copy(gel(x,i));
+  for(   ; i<n; i++)
+    gel(z,n-i-1) = pol0_Flx(vs);
+  return FlxX_renormalize(z-2,n+2);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                               FlxqX                               **/
+/**                                                                   **/
+/***********************************************************************/
+
+
+/* FlxqX are t_POL with Flxq coefficients.
+ * Normally the variable ordering should be respected.*/
+
+/*Not stack clean*/
+GEN
+Kronecker_to_FlxqX(GEN z, GEN T, ulong p)
+{
+  long i,j,lx,l, N = (get_Flx_degree(T)<<1) + 1;
+  GEN x, t = cgetg(N,t_VECSMALL);
+  t[1] = get_Flx_var(T);
+  l = lg(z); lx = (l-2) / (N-2);
+  x = cgetg(lx+3,t_POL);
+  x[1] = z[1];
+  for (i=2; i<lx+2; i++)
+  {
+    for (j=2; j<N; j++) t[j] = z[j];
+    z += (N-2);
+    gel(x,i) = Flx_rem(Flx_renormalize(t,N), T,p);
+  }
+  N = (l-2) % (N-2) + 2;
+  for (j=2; j<N; j++) t[j] = z[j];
+  gel(x,i) = Flx_rem(Flx_renormalize(t,N), T,p);
+  return FlxX_renormalize(x, i+1);
+}
+
+GEN
+FlxqX_red(GEN z, GEN T, ulong p)
+{
+  GEN res;
+  long i, l = lg(z);
+  res = cgetg(l,t_POL); res[1] = z[1];
+  for(i=2;i<l;i++) gel(res,i) = Flx_rem(gel(z,i),T,p);
+  return FlxX_renormalize(res,l);
+}
+
+static GEN
+FlxqX_mulspec(GEN x, GEN y, GEN T, ulong p, long lx, long ly)
+{
+  pari_sp ltop=avma;
+  GEN z,kx,ky;
+  long dT =  get_Flx_degree(T);
+  kx= zxX_to_Kronecker_spec(x,lx,dT);
+  ky= zxX_to_Kronecker_spec(y,ly,dT);
+  z = Flx_mul(ky, kx, p);
+  z = Kronecker_to_FlxqX(z,T,p);
+  return gerepileupto(ltop,z);
+}
+
+GEN
+FlxqX_mul(GEN x, GEN y, GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  GEN z,kx,ky;
+  kx= zxX_to_Kronecker(x,get_Flx_mod(T));
+  ky= zxX_to_Kronecker(y,get_Flx_mod(T));
+  z = Flx_mul(ky, kx, p);
+  z = Kronecker_to_FlxqX(z,T,p);
+  return gerepileupto(ltop,z);
+}
+
+GEN
+FlxqX_sqr(GEN x, GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  GEN z,kx;
+  kx= zxX_to_Kronecker(x,get_Flx_mod(T));
+  z = Flx_sqr(kx, p);
+  z = Kronecker_to_FlxqX(z,T,p);
+  return gerepileupto(ltop,z);
+}
+
+GEN
+FlxqX_Flxq_mul(GEN P, GEN U, GEN T, ulong p)
+{
+  long i, lP = lg(P);
+  GEN res = cgetg(lP,t_POL);
+  res[1] = P[1];
+  for(i=2; i<lP; i++)
+    gel(res,i) = Flxq_mul(U,gel(P,i), T,p);
+  return FlxX_renormalize(res, lP);
+}
+GEN
+FlxqX_Flxq_mul_to_monic(GEN P, GEN U, GEN T, ulong p)
+{
+  long i, lP = lg(P);
+  GEN res = cgetg(lP,t_POL);
+  res[1] = P[1];
+  for(i=2; i<lP-1; i++) gel(res,i) = Flxq_mul(U,gel(P,i), T,p);
+  gel(res,lP-1) = pol1_Flx(get_Flx_var(T));
+  return FlxX_renormalize(res, lP);
+}
+
+GEN
+FlxqX_normalize(GEN z, GEN T, ulong p)
+{
+  GEN p1 = leading_term(z);
+  if (!lgpol(z) || (!degpol(p1) && p1[1] == 1)) return z;
+  return FlxqX_Flxq_mul_to_monic(z, Flxq_inv(p1,T,p), T,p);
+}
+
+/* x and y in Z[Y][X]. Assume T irreducible mod p */
+GEN
+FlxqX_divrem(GEN x, GEN y, GEN T, ulong p, GEN *pr)
+{
+  long vx, dx, dy, dz, i, j, sx, lr;
+  pari_sp av0, av, tetpil;
+  GEN z,p1,rem,lead;
+
+  if (!signe(y)) pari_err_INV("FlxqX_divrem",y);
+  vx=varn(x); dy=degpol(y); dx=degpol(x);
+  if (dx < dy)
+  {
+    if (pr)
+    {
+      av0 = avma; x = FlxqX_red(x, T, p);
+      if (pr == ONLY_DIVIDES)
+      {
+        avma=av0;
+        return signe(x)? NULL: pol_0(vx);
+      }
+      if (pr == ONLY_REM) return x;
+      *pr = x;
+    }
+    return pol_0(vx);
+  }
+  lead = leading_term(y);
+  if (!dy) /* y is constant */
+  {
+    if (pr && pr != ONLY_DIVIDES)
+    {
+      if (pr == ONLY_REM) return pol_0(vx);
+      *pr = pol_0(vx);
+    }
+    if (Flx_equal1(lead)) return gcopy(x);
+    av0 = avma;
+    return gerepileupto(av0,FlxqX_Flxq_mul(x,Flxq_inv(lead,T,p),T,p));
+  }
+  av0 = avma; dz = dx-dy;
+  lead = Flx_equal1(lead)? NULL: gclone(Flxq_inv(lead,T,p));
+  avma = av0;
+  z = cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2; y += 2; z += 2;
+
+  p1 = gel(x,dx); av = avma;
+  gel(z,dz) = lead? gerepileupto(av, Flxq_mul(p1,lead, T, p)): gcopy(p1);
+  for (i=dx-1; i>=dy; i--)
+  {
+    av=avma; p1=gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++)
+      p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p),p);
+    if (lead) p1 = Flx_mul(p1, lead,p);
+    tetpil=avma; gel(z,i-dy) = gerepile(av,tetpil,Flx_rem(p1,T,p));
+  }
+  if (!pr) { if (lead) gunclone(lead); return z-2; }
+
+  rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
+  for (sx=0; ; i--)
+  {
+    p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p),p);
+    tetpil=avma; p1 = Flx_rem(p1, T, p); if (lgpol(p1)) { sx = 1; break; }
+    if (!i) break;
+    avma=av;
+  }
+  if (pr == ONLY_DIVIDES)
+  {
+    if (lead) gunclone(lead);
+    if (sx) { avma=av0; return NULL; }
+    avma = (pari_sp)rem; return z-2;
+  }
+  lr=i+3; rem -= lr;
+  rem[0] = evaltyp(t_POL) | evallg(lr);
+  rem[1] = z[-1];
+  p1 = gerepile((pari_sp)rem,tetpil,p1);
+  rem += 2; gel(rem,i) = p1;
+  for (i--; i>=0; i--)
+  {
+    av=avma; p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p), p);
+    tetpil=avma; gel(rem,i) = gerepile(av,tetpil, Flx_rem(p1, T, p));
+  }
+  rem -= 2;
+  if (lead) gunclone(lead);
+  if (!sx) (void)FlxX_renormalize(rem, lr);
+  if (pr == ONLY_REM) return gerepileupto(av0,rem);
+  *pr = rem; return z-2;
+}
+
+static GEN
+FlxqX_invBarrett_basecase(GEN T, GEN Q, ulong p)
+{
+  long i, l=lg(T)-1, lr = l-1, k;
+  long sv=Q[1];
+  GEN r=cgetg(lr,t_POL); r[1]=T[1];
+  gel(r,2) = pol1_Flx(sv);
+  for (i=3;i<lr;i++)
+  {
+    pari_sp ltop=avma;
+    GEN u = Flx_neg(gel(T,l-i+2),p);
+    for (k=3;k<i;k++)
+      u = Flx_sub(u, Flxq_mul(gel(T,l-i+k),gel(r,k),Q,p),p);
+    gel(r,i) = gerepileupto(ltop, u);
+  }
+  r = FlxX_renormalize(r,lr);
+  return r;
+}
+
+/* Return new lgpol */
+static long
+FlxX_lgrenormalizespec(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>=0; i--)
+    if (lgpol(gel(x,i))) break;
+  return i+1;
+}
+
+static GEN
+FlxqX_invBarrett_Newton(GEN S, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long nold, lx, lz, lq, l = degpol(S), i, lQ;
+  GEN q, y, z, x = cgetg(l+2, t_POL) + 2;
+  long dT = get_Flx_degree(T);
+  ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
+  for (i=0;i<l;i++) gel(x,i) = pol0_Flx(T[1]);
+  q = FlxX_recipspec(S+2,l+1,l+1,dT);
+  lQ = lgpol(q); q+=2;
+  /* We work on _spec_ FlxX's, all the l[xzq] below are lgpol's */
+
+  /* initialize */
+  gel(x,0) = Flxq_inv(gel(q,0),T, p);
+  if (lQ>1 && degpol(gel(q,1)) >= dT)
+    gel(q,1) = Flx_rem(gel(q,1), T, p);
+  if (lQ>1 && lgpol(gel(q,1)))
+  {
+    GEN u = gel(q, 1);
+    if (!Flx_equal1(gel(x,0))) u = Flxq_mul(u, Flxq_sqr(gel(x,0), T,p), T,p);
+    gel(x,1) = Flx_neg(u, p); lx = 2;
+  }
+  else
+    lx = 1;
+  nold = 1;
+  for (; mask > 1; )
+  { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
+    long i, lnew, nnew = nold << 1;
+
+    if (mask & 1) nnew--;
+    mask >>= 1;
+
+    lnew = nnew + 1;
+    lq = FlxX_lgrenormalizespec(q, minss(lQ,lnew));
+    z = FlxqX_mulspec(x, q, T, p, lx, lq); /* FIXME: high product */
+    lz = lgpol(z); if (lz > lnew) lz = lnew;
+    z += 2;
+    /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
+    for (i = nold; i < lz; i++) if (lgpol(gel(z,i))) break;
+    nold = nnew;
+    if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
+
+    /* z + i represents (x*q - 1) / t^i */
+    lz = FlxX_lgrenormalizespec (z+i, lz-i);
+    z = FlxqX_mulspec(x, z+i, T,p, lx, lz); /* FIXME: low product */
+    lz = lgpol(z); z += 2;
+    if (lz > lnew-i) lz = FlxX_lgrenormalizespec(z, lnew-i);
+
+    lx = lz+ i;
+    y  = x + i; /* x -= z * t^i, in place */
+    for (i = 0; i < lz; i++) gel(y,i) = Flx_neg(gel(z,i), p);
+  }
+  x -= 2; setlg(x, lx + 2); x[1] = S[1];
+  return gerepilecopy(av, x);
+}
+
+static const long FlxqX_INVBARRETT_LIMIT = 5;
+
+/* x/polrecip(P)+O(x^n) */
+GEN
+FlxqX_invBarrett(GEN T, GEN Q, ulong p)
+{
+  pari_sp ltop=avma;
+  long l=lg(T), v = varn(T);
+  GEN r;
+  GEN c = gel(T,l-1);
+  if (l<5) return pol_0(v);
+  if (l<=FlxqX_INVBARRETT_LIMIT)
+  {
+    if (!Flx_equal1(c))
+    {
+      GEN ci = Flxq_inv(c,Q,p);
+      T = FlxqX_Flxq_mul(T, ci, Q, p);
+      r = FlxqX_invBarrett_basecase(T,Q,p);
+      r = FlxqX_Flxq_mul(r,ci,Q,p);
+    } else
+      r = FlxqX_invBarrett_basecase(T,Q,p);
+  } else
+    r = FlxqX_invBarrett_Newton(T,Q,p);
+  return gerepileupto(ltop, r);
+}
+
+GEN
+FlxqX_rem_Barrett(GEN x, GEN mg, GEN T, GEN Q, ulong p)
+{
+  pari_sp ltop=avma;
+  GEN z;
+  long vs = get_Flx_var(Q);
+  long l=lgpol(x);
+  long lt=degpol(T); /*We discard the leading term*/
+  long ld, lm, lT, lmg;
+  if (l<=lt)
+    return gcopy(x);
+  ld = l-lt;
+  lm = minss(ld, lgpol(mg));
+  lT  = FlxX_lgrenormalizespec(T+2,lt);
+  lmg = FlxX_lgrenormalizespec(mg+2,lm);
+  z = FlxX_recipspec(x+2+lt,ld,ld,vs);         /* z = rec(x)       lz<=ld*/
+  z = FlxqX_mulspec(z+2,mg+2,Q,p,lgpol(z),lmg); /* z = rec(x) * mg lz<=ld+lm*/
+  z = FlxX_recipspec(z+2,minss(ld,lgpol(z)),ld,vs);/*z= rec(rec(x)*mg) lz<=ld*/
+  z = FlxqX_mulspec(z+2,T+2,Q,p,lgpol(z),lT);    /* z*= pol        lz<=ld+lt*/
+  z = FlxX_subspec(x+2,z+2,p,lt,minss(lt,lgpol(z)));/*z = x - z    lz<=lt */
+  setvarn(z,varn(x));
+  return gerepileupto(ltop,z);
+}
+
+GEN
+FlxqX_gcd(GEN x, GEN y, GEN T, ulong p)
+{
+  GEN a,b,c;
+  pari_sp av0, av=avma;
+
+  a = FlxqX_red(x, T, p); av0 = avma;
+  b = FlxqX_red(y, T, p);
+  while (signe(b))
+  {
+    av0 = avma; c = FlxqX_rem(a,b,T,p); a=b; b=c;
+  }
+  avma = av0; return gerepileupto(av, a);
+}
+
+GEN
+FlxqX_safegcd(GEN P, GEN Q, GEN T, ulong p)
+{
+  pari_sp btop, ltop = avma, st_lim;
+  GEN U;
+  if (!signe(P)) return gcopy(Q);
+  if (!signe(Q)) return gcopy(P);
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for(;;)
+  {
+    U = Flxq_invsafe(leading_term(Q), T, p);
+    if (!U) { avma = ltop; return NULL; }
+    Q = FlxqX_Flxq_mul_to_monic(Q,U,T,p);
+    P = FlxqX_rem(P,Q,T,p);
+    if (!signe(P)) break;
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_safegcd");
+      gerepileall(btop, 2, &P,&Q);
+    }
+    swap(P, Q);
+  }
+  return gerepileupto(ltop, Q);
+}
+
+GEN
+FlxqX_extgcd(GEN a, GEN b, GEN T, ulong p, GEN *ptu, GEN *ptv)
+{
+  GEN q, r, u, v, d, d1, v1;
+  long vx = varn(a);
+  pari_sp ltop=avma;
+
+  d = a; d1 = b; v = pol_0(vx); v1 = pol1_FlxX(vx,get_Flx_var(T));
+  while (signe(d1))
+  {
+    q = FlxqX_divrem(d,d1,T,p, &r);
+    v = FlxX_sub(v, FlxqX_mul(q,v1, T,p), p);
+    u=v; v=v1; v1=u;
+    u=r; d=d1; d1=u;
+  }
+  if (ptu) *ptu = FlxqX_div(FlxX_sub(d, FlxqX_mul(b,v, T,p), p), a, T,p);
+  *ptv = v;
+  gerepileall(ltop,ptu?3:2,&d,ptv,ptu);
+  return d;
+}
+
+struct _FlxqX {ulong p; GEN T;};
+static GEN _FlxqX_mul(void *data,GEN a,GEN b)
+{
+  struct _FlxqX *d=(struct _FlxqX*)data;
+  return FlxqX_mul(a,b,d->T,d->p);
+}
+static GEN _FlxqX_sqr(void *data,GEN a)
+{
+  struct _FlxqX *d=(struct _FlxqX*)data;
+  return FlxqX_sqr(a,d->T,d->p);
+}
+
+GEN
+FlxqX_pow(GEN V, long n, GEN T, ulong p)
+{
+  struct _FlxqX d; d.p=p; d.T=T;
+  return gen_powu(V, n, (void*)&d, &_FlxqX_sqr, &_FlxqX_mul);
+}
+
+GEN
+FlxqXV_prod(GEN V, GEN T, ulong p)
+{
+  struct _FlxqX d; d.p=p; d.T=T;
+  return divide_conquer_assoc(V, (void*)&d, &_FlxqX_mul);
+}
+
+GEN
+FlxqV_roots_to_pol(GEN V, GEN T, ulong p, long v)
+{
+  pari_sp ltop = avma;
+  long k, sv = get_Flx_var(T);
+  GEN W = cgetg(lg(V),t_VEC);
+  for(k=1; k < lg(V); k++)
+    gel(W,k) = deg1pol_shallow(pol1_Flx(sv),Flx_neg(gel(V,k),p),v);
+  return gerepileupto(ltop, FlxqXV_prod(W, T, p));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       (Fl[X]/T(X))[Y] / S(Y)                    */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+FlxqXQ_mul(GEN x, GEN y, GEN S, GEN T, ulong p) {
+  return FlxqX_rem(FlxqX_mul(x,y,T,p),S,T,p);
+}
+
+GEN
+FlxqXQ_sqr(GEN x, GEN S, GEN T, ulong p) {
+  return FlxqX_rem(FlxqX_sqr(x,T,p),S,T,p);
+}
+
+GEN
+FlxqXQ_invsafe(GEN x, GEN S, GEN T, ulong p)
+{
+  GEN V, z = FlxqX_extgcd(S, x, T, p, NULL, &V);
+  if (degpol(z)) return NULL;
+  z = Flxq_invsafe(gel(z,2),T,p);
+  if (!z) return NULL;
+  return FlxqX_Flxq_mul(V, z, T, p);
+}
+
+GEN
+FlxqXQ_inv(GEN x, GEN S, GEN T,ulong p)
+{
+  pari_sp av = avma;
+  GEN U = FlxqXQ_invsafe(x, S, T, p);
+  if (!U) pari_err_INV("FlxqXQ_inv",x);
+  return gerepileupto(av, U);
+}
+
+GEN
+FlxqXQ_div(GEN x, GEN y, GEN S, GEN T, ulong p) {
+  return FlxqXQ_mul(x, FlxqXQ_inv(y,S,T,p),S,T,p);
+}
+
+static GEN
+FlxqXQ_mul_mg(GEN x,GEN y,GEN mg, GEN S, GEN T,ulong p)
+{
+  GEN z = FlxqX_mul(x,y,T,p);
+  if (lg(S) > lg(z)) return z;
+  return FlxqX_rem_Barrett(z, mg, S, T, p);
+}
+
+static GEN
+FlxqXQ_sqr_mg(GEN y,GEN mg,GEN S, GEN T,ulong p)
+{
+  GEN z = FlxqX_sqr(y,T,p);
+  if (lg(S) > lg(z)) return z;
+  return FlxqX_rem_Barrett(z, mg, S, T, p);
+}
+
+typedef struct {
+  GEN T, S, mg;
+  ulong p;
+} FlxqXQ_muldata;
+static GEN
+_FlxqXQ_add(void *data, GEN x, GEN y) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return FlxX_add(x,y, d->p);
+}
+static GEN
+_FlxqXQ_cmul(void *data, GEN P, long a, GEN x) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return FlxX_Flx_mul(x,gel(P,a+2), d->p);
+}
+static GEN
+_FlxqXQ_red(void *data, GEN x) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return FlxqX_red(x, d->T, d->p);
+}
+static GEN
+_FlxqXQ_mul(void *data, GEN x, GEN y) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return FlxqXQ_mul_mg(x,y, d->mg, d->S,d->T, d->p);
+}
+static GEN
+_FlxqXQ_sqr(void *data, GEN x) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return FlxqXQ_sqr_mg(x, d->mg, d->S,d->T, d->p);
+}
+
+static GEN
+_FlxqXQ_one(void *data) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return pol1_FlxX(varn(d->S),d->T[1]);
+}
+
+static GEN
+_FlxqXQ_zero(void *data) {
+  FlxqXQ_muldata *d = (FlxqXQ_muldata*) data;
+  return pol_0(varn(d->S));
+}
+
+static struct bb_algebra FlxqXQ_algebra = { _FlxqXQ_red,_FlxqXQ_add,_FlxqXQ_mul,_FlxqXQ_sqr,_FlxqXQ_one,_FlxqXQ_zero };
+
+/* x over Fq, return lift(x^n) mod S */
+GEN
+FlxqXQ_pow(GEN x, GEN n, GEN S, GEN T, ulong p)
+{
+  FlxqXQ_muldata D;
+  long s = signe(n);
+  if (!s) return pol1_FlxX(varn(S),get_Flx_var(T));
+  if (s < 0) x = FlxqXQ_inv(x,S,T,p);
+  if (is_pm1(n)) return s < 0 ? x : gcopy(x);
+  if (degpol(x)>=degpol(S)) x = FlxqX_rem(x,S,T,p);
+  D.mg = FlxqX_invBarrett(S,T,p);
+  D.S = S;
+  D.T = T;
+  D.p = p;
+  return gen_pow(x, n, (void*)&D, &_FlxqXQ_sqr, &_FlxqXQ_mul);
+}
+
+GEN
+FlxqXQ_powers(GEN x, long l, GEN S, GEN T, ulong p)
+{
+  FlxqXQ_muldata D;
+  int use_sqr;
+  if (degpol(x)>=degpol(S)) x = FlxqX_rem(x,S,T,p);
+  use_sqr = (degpol(x)<<1)>=degpol(S);
+  D.mg = FlxqX_invBarrett(S,T,p);
+  D.S = S; D.T = T; D.p = p;
+  return gen_powers(x, l, use_sqr, (void*)&D, &_FlxqXQ_sqr, &_FlxqXQ_mul,&_FlxqXQ_one);
+}
+
+GEN
+FlxqXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, ulong p)
+{
+  return FlxXV_to_FlxM(FlxqXQ_powers(y,m-1,S,T,p), n, T[1]);
+}
+
+GEN
+FlxqX_FlxqXQV_eval(GEN P, GEN V, GEN S, GEN T, ulong p)
+{
+  FlxqXQ_muldata D;
+  D.mg = FlxqX_invBarrett(S,T,p);
+  D.S=S; D.T=T; D.p=p;
+  return gen_bkeval_powers(P, degpol(P), V, (void*)&D, &FlxqXQ_algebra,
+                                                   _FlxqXQ_cmul);
+}
+
+GEN
+FlxqX_FlxqXQ_eval(GEN Q, GEN x, GEN S, GEN T, ulong p)
+{
+  FlxqXQ_muldata D;
+  int use_sqr = (degpol(x)<<1) >= degpol(S);
+  D.mg = FlxqX_invBarrett(S,T,p);
+  D.S=S; D.T=T; D.p=p;
+  return gen_bkeval(Q, degpol(Q), x, use_sqr, (void*)&D, &FlxqXQ_algebra,
+                                                    _FlxqXQ_cmul);
+}
+
+static GEN
+FlxqXQ_autpow_sqr(void * T, GEN x)
+{
+  FlxqXQ_muldata *D = (FlxqXQ_muldata *)T;
+  GEN phi = gel(x,1), S = gel(x,2);
+  GEN phi2 = Flx_Flxq_eval(phi,phi,D->T,D->p);
+  GEN Sphi = FlxY_Flxq_evalx(S,phi,D->T,D->p);
+  GEN S2 = FlxqX_FlxqXQ_eval(Sphi, S, D->S,D->T,D->p);
+  return mkvec2(phi2, S2);
+}
+
+static GEN
+FlxqXQ_autpow_mul(void * T, GEN x, GEN y)
+{
+  FlxqXQ_muldata *D = (FlxqXQ_muldata *)T;
+  GEN phi1 = gel(x,1), S1 = gel(x,2);
+  GEN phi2 = gel(y,1), S2 = gel(y,2);
+  GEN phi3 = Flx_Flxq_eval(phi1,phi2,D->T,D->p);
+  GEN Sphi = FlxY_Flxq_evalx(S1,phi2,D->T,D->p);
+  GEN S3 = FlxqX_FlxqXQ_eval(Sphi, S2, D->S,D->T,D->p);
+  return mkvec2(phi3, S3);
+}
+
+GEN
+FlxqXQV_autpow(GEN aut, long n, GEN S, GEN T, ulong p)
+{
+  FlxqXQ_muldata D;
+  D.S=S; D.T=T; D.p=p;
+  return gen_powu(aut,n,&D,FlxqXQ_autpow_sqr,FlxqXQ_autpow_mul);
+}
+
+static GEN
+FlxqXQ_autsum_mul(void * T, GEN x, GEN y)
+{
+  FlxqXQ_muldata *D = (FlxqXQ_muldata *)T;
+  GEN phi1 = gel(x,1), S1 = gel(x,2), a1 = gel(x,3);
+  GEN phi2 = gel(y,1), S2 = gel(y,2), a2 = gel(y,3);
+  GEN phi3 = Flx_Flxq_eval(phi1,phi2,D->T,D->p);
+  GEN Sphi = FlxY_Flxq_evalx(S1,phi2,D->T,D->p);
+  long n = brent_kung_optpow(degpol(D->S)-1,2,1);
+  GEN V = FlxqXQ_powers(S2, n, D->S,D->T,D->p);
+  GEN S3 = FlxqX_FlxqXQV_eval(Sphi, V, D->S,D->T,D->p);
+  GEN aphi = FlxY_Flxq_evalx(a1,phi2,D->T,D->p);
+  GEN aS = FlxqX_FlxqXQV_eval(aphi,V,D->S,D->T,D->p);
+  GEN a3 = FlxqXQ_mul(aS,a2,D->S,D->T,D->p);
+  return mkvec3(phi3, S3, a3);
+}
+
+static GEN
+FlxqXQ_autsum_sqr(void * T, GEN x)
+{ return FlxqXQ_autsum_mul(T,x,x); }
+
+
+GEN
+FlxqXQV_autsum(GEN aut, long n, GEN S, GEN T, ulong p)
+{
+  FlxqXQ_muldata D;
+  D.S=S; D.T=T; D.p=p;
+  return gen_powu(aut,n,&D,FlxqXQ_autsum_sqr,FlxqXQ_autsum_mul);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                      FlxYqQ                                     */
+/*                                                                 */
+/*******************************************************************/
+
+/*Preliminary implementation to speed up FpX_ffisom*/
+typedef struct {
+  GEN S, T;
+  ulong p;
+} FlxYqq_muldata;
+
+/* reduce x in Fl[X, Y] in the algebra Fl[X, Y]/ (P(X),Q(Y)) */
+static GEN
+FlxYqq_redswap(GEN x, GEN S, GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  long n = get_Flx_degree(S);
+  long m = get_Flx_degree(T);
+  long w = get_Flx_var(T);
+  GEN V = FlxX_swap(x,m,w);
+  V = FlxqX_red(V,S,p);
+  V = FlxX_swap(V,n,w);
+  return gerepilecopy(ltop,V);
+}
+static GEN
+FlxYqq_sqr(void *data, GEN x)
+{
+  FlxYqq_muldata *D = (FlxYqq_muldata*)data;
+  return FlxYqq_redswap(FlxqX_sqr(x, D->T, D->p),D->S,D->T,D->p);
+}
+
+static GEN
+FlxYqq_mul(void *data, GEN x, GEN y)
+{
+  FlxYqq_muldata *D = (FlxYqq_muldata*)data;
+  return FlxYqq_redswap(FlxqX_mul(x,y, D->T, D->p),D->S,D->T,D->p);
+}
+
+/* x in Z[X,Y], S in Z[X] over Fq = Z[Y]/(p,T); compute lift(x^n mod (S,T,p)) */
+GEN
+FlxYqq_pow(GEN x, GEN n, GEN S, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  FlxYqq_muldata D;
+  GEN y;
+  D.S = S;
+  D.T = T;
+  D.p = p;
+  y = gen_pow(x, n, (void*)&D, &FlxYqq_sqr, &FlxYqq_mul);
+  return gerepileupto(av, y);
+}
diff --git a/src/basemath/FlxqE.c b/src/basemath/FlxqE.c
new file mode 100644
index 0000000..963ebbf
--- /dev/null
+++ b/src/basemath/FlxqE.c
@@ -0,0 +1,1430 @@
+/* Copyright (C) 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with points over elliptic curves over Fq,
+small characteristic. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              FlxqE                                **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Theses functions deal with point over elliptic curves over Fq defined
+ * by an equation of the form y^2=x^3+a4*x+a6.
+ * Most of the time a6 is omitted since it can be recovered from any point
+ * on the curve.
+ */
+
+GEN
+RgE_to_FlxqE(GEN x, GEN T, ulong p)
+{
+  if (ell_is_inf(x)) return x;
+  retmkvec2(Rg_to_Flxq(gel(x,1),T,p),Rg_to_Flxq(gel(x,2),T,p));
+}
+
+GEN
+FlxqE_changepoint(GEN x, GEN ch, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  GEN p1,z,u,r,s,t,v,v2,v3;
+  if (ell_is_inf(x)) return x;
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  v = Flxq_inv(u, T, p); v2 = Flxq_sqr(v, T, p); v3 = Flxq_mul(v,v2, T, p);
+  p1 = Flx_sub(gel(x,1),r, p);
+  z = cgetg(3,t_VEC);
+  gel(z,1) = Flxq_mul(v2, p1, T, p);
+  gel(z,2) = Flxq_mul(v3, Flx_sub(gel(x,2), Flx_add(Flxq_mul(s, p1, T, p),t, p), p), T, p);
+  return gerepileupto(av, z);
+}
+
+GEN
+FlxqE_changepointinv(GEN x, GEN ch, GEN T, ulong p)
+{
+  GEN u, r, s, t, X, Y, u2, u3, u2X, z;
+  if (ell_is_inf(x)) return x;
+  X = gel(x,1); Y = gel(x,2);
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  u2 = Flxq_sqr(u, T, p); u3 = Flxq_mul(u,u2, T, p);
+  u2X = Flxq_mul(u2,X, T, p);
+  z = cgetg(3, t_VEC);
+  gel(z,1) = Flx_add(u2X,r, p);
+  gel(z,2) = Flx_add(Flxq_mul(u3,Y, T, p), Flx_add(Flxq_mul(s,u2X, T, p), t, p), p);
+  return z;
+}
+
+static GEN
+FlxqE_dbl_slope(GEN P, GEN a4, GEN T, ulong p, GEN *slope)
+{
+  GEN x, y, Q;
+  if (ell_is_inf(P) || !lgpol(gel(P,2))) return ellinf();
+  x = gel(P,1); y = gel(P,2);
+  if (p==3UL)
+    *slope = typ(a4)==t_VEC ? Flxq_div(Flxq_mul(x, gel(a4, 1), T, p), y, T, p)
+                            : Flxq_div(a4, Flx_neg(y, p), T, p);
+  else
+  {
+    GEN sx = Flx_add(Flx_triple(Flxq_sqr(x, T, p), p), a4, p);
+    *slope = Flxq_div(sx, Flx_double(y, p), T, p);
+  }
+  Q = cgetg(3,t_VEC);
+  gel(Q, 1) = Flx_sub(Flxq_sqr(*slope, T, p), Flx_double(x, p), p);
+  if (typ(a4)==t_VEC) gel(Q, 1) = Flx_sub(gel(Q, 1), gel(a4, 1), p);
+  gel(Q, 2) = Flx_sub(Flxq_mul(*slope, Flx_sub(x, gel(Q, 1), p), T, p), y, p);
+  return Q;
+}
+
+GEN
+FlxqE_dbl(GEN P, GEN a4, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FlxqE_dbl_slope(P,a4, T, p,&slope));
+}
+
+static GEN
+FlxqE_add_slope(GEN P, GEN Q, GEN a4, GEN T, ulong p, GEN *slope)
+{
+  GEN Px, Py, Qx, Qy, R;
+  if (ell_is_inf(P)) return Q;
+  if (ell_is_inf(Q)) return P;
+  Px = gel(P,1); Py = gel(P,2);
+  Qx = gel(Q,1); Qy = gel(Q,2);
+  if (Flx_equal(Px, Qx))
+  {
+    if (Flx_equal(Py, Qy))
+      return FlxqE_dbl_slope(P, a4, T, p, slope);
+    else
+      return ellinf();
+  }
+  *slope = Flxq_div(Flx_sub(Py, Qy, p), Flx_sub(Px, Qx, p), T, p);
+  R = cgetg(3,t_VEC);
+  gel(R, 1) = Flx_sub(Flx_sub(Flxq_sqr(*slope, T, p), Px, p), Qx, p);
+  if (typ(a4)==t_VEC) gel(R, 1) = Flx_sub(gel(R, 1),gel(a4, 1), p);
+  gel(R, 2) = Flx_sub(Flxq_mul(*slope, Flx_sub(Px, gel(R, 1), p), T, p), Py, p);
+  return R;
+}
+
+GEN
+FlxqE_add(GEN P, GEN Q, GEN a4, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FlxqE_add_slope(P,Q,a4, T, p,&slope));
+}
+
+static GEN
+FlxqE_neg_i(GEN P, ulong p)
+{
+  if (ell_is_inf(P)) return P;
+  return mkvec2(gel(P,1), Flx_neg(gel(P,2), p));
+}
+
+GEN
+FlxqE_neg(GEN P, GEN T, ulong p)
+{
+  (void) T;
+  if (ell_is_inf(P)) return ellinf();
+  return mkvec2(gcopy(gel(P,1)), Flx_neg(gel(P,2), p));
+}
+
+GEN
+FlxqE_sub(GEN P, GEN Q, GEN a4, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FlxqE_add_slope(P, FlxqE_neg_i(Q, p), a4, T, p, &slope));
+}
+
+struct _FlxqE
+{
+  GEN a4, a6;
+  GEN T;
+  ulong p;
+};
+
+static GEN
+_FlxqE_dbl(void *E, GEN P)
+{
+  struct _FlxqE *ell = (struct _FlxqE *) E;
+  return FlxqE_dbl(P, ell->a4, ell->T, ell->p);
+}
+
+static GEN
+_FlxqE_add(void *E, GEN P, GEN Q)
+{
+  struct _FlxqE *ell=(struct _FlxqE *) E;
+  return FlxqE_add(P, Q, ell->a4, ell->T, ell->p);
+}
+
+static GEN
+_FlxqE_mul(void *E, GEN P, GEN n)
+{
+  pari_sp av = avma;
+  struct _FlxqE *e=(struct _FlxqE *) E;
+  long s = signe(n);
+  if (!s || ell_is_inf(P)) return ellinf();
+  if (s<0) P = FlxqE_neg(P, e->T, e->p);
+  if (is_pm1(n)) return s>0? gcopy(P): P;
+  return gerepileupto(av, gen_pow(P, n, e, &_FlxqE_dbl, &_FlxqE_add));
+}
+
+GEN
+FlxqE_mul(GEN P, GEN n, GEN a4, GEN T, ulong p)
+{
+  struct _FlxqE E;
+  E.a4= a4; E.T = T; E.p = p;
+  return _FlxqE_mul(&E, P, n);
+}
+
+/* 3*x^2+2*a2*x = -a2*x, and a2!=0 */
+
+/* Finds a random non-singular point on E */
+static GEN
+random_F3xqE(GEN a2, GEN a6, GEN T)
+{
+  pari_sp ltop = avma;
+  GEN x, y, rhs;
+  const ulong p=3;
+  do
+  {
+    avma= ltop;
+    x   = random_Flx(get_Flx_degree(T),get_Flx_var(T),p);
+    rhs = Flx_add(Flxq_mul(Flxq_sqr(x, T, p), Flx_add(x, a2, p), T, p), a6, p);
+  } while ((!lgpol(rhs) && !lgpol(x)) || !Flxq_issquare(rhs, T, p));
+  y = Flxq_sqrt(rhs, T, p);
+  if (!y) pari_err_PRIME("random_F3xqE", T);
+  return gerepilecopy(ltop, mkvec2(x, y));
+}
+
+/* Finds a random non-singular point on E */
+GEN
+random_FlxqE(GEN a4, GEN a6, GEN T, ulong p)
+{
+  pari_sp ltop = avma;
+  GEN x, x2, y, rhs;
+  if (typ(a4)==t_VEC)
+    return random_F3xqE(gel(a4,1), a6, T);
+  do
+  {
+    avma= ltop;
+    x   = random_Flx(get_Flx_degree(T),get_Flx_var(T),p);
+    x2  = Flxq_sqr(x, T, p); /*  x^3+a4*x+a6 = x*(x^2+a4)+a6  */
+    rhs = Flx_add(Flxq_mul(x, Flx_add(x2, a4, p), T, p), a6, p);
+  } while ((!lgpol(rhs) && !lgpol(Flx_add(Flx_triple(x2, p), a4, p)))
+          || !Flxq_issquare(rhs, T, p));
+  y = Flxq_sqrt(rhs, T, p);
+  if (!y) pari_err_PRIME("random_FlxqE", T);
+  return gerepilecopy(ltop, mkvec2(x, y));
+}
+
+static GEN
+_FlxqE_rand(void *E)
+{
+  struct _FlxqE *ell=(struct _FlxqE *) E;
+  return random_FlxqE(ell->a4, ell->a6, ell->T, ell->p);
+}
+
+static const struct bb_group FlxqE_group={_FlxqE_add,_FlxqE_mul,_FlxqE_rand,hash_GEN,zvV_equal,ell_is_inf, NULL};
+
+const struct bb_group *
+get_FlxqE_group(void ** pt_E, GEN a4, GEN a6, GEN T, ulong p)
+{
+  struct _FlxqE *e = (struct _FlxqE *) stack_malloc(sizeof(struct _FlxqE));
+  e->a4 = a4; e->a6 = a6; e->T = Flx_get_red(T, p); e->p = p;
+  *pt_E = (void *) e;
+  return &FlxqE_group;
+}
+
+GEN
+FlxqE_order(GEN z, GEN o, GEN a4, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  struct _FlxqE e;
+  e.a4=a4; e.T=T; e.p=p;
+  return gerepileuptoint(av, gen_order(z, o, (void*)&e, &FlxqE_group));
+}
+
+GEN
+FlxqE_log(GEN a, GEN b, GEN o, GEN a4, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  struct _FlxqE e;
+  e.a4=a4; e.T=T; e.p=p;
+  return gerepileuptoint(av, gen_PH_log(a, b, o, (void*)&e, &FlxqE_group));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                            Pairings                               **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Derived from APIP from and by Jerome Milan, 2012 */
+
+static GEN
+FlxqE_vert(GEN P, GEN Q, GEN T, ulong p)
+{
+  if (ell_is_inf(P))
+    return pol1_Flx(get_Flx_var(T));
+  return Flx_sub(gel(Q, 1), gel(P, 1), p);
+}
+
+/* Computes the equation of the line tangent to R and returns its
+   evaluation at the point Q. Also doubles the point R.
+ */
+
+static GEN
+FlxqE_tangent_update(GEN R, GEN Q, GEN a4, GEN T, ulong p, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = ellinf();
+    return pol1_Flx(get_Flx_var(T));
+  }
+  else if (!lgpol(gel(R,2)))
+  {
+    *pt_R = ellinf();
+    return FlxqE_vert(R, Q, T, p);
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = FlxqE_dbl_slope(R, a4, T, p, &slope);
+    tmp1 = Flx_sub(gel(Q, 1), gel(R, 1), p);
+    tmp2 = Flx_add(Flxq_mul(tmp1, slope, T, p), gel(R,2), p);
+    return Flx_sub(gel(Q, 2), tmp2, p);
+  }
+}
+
+/* Computes the equation of the line through R and P, and returns its
+   evaluation at the point Q. Also adds P to the point R.
+ */
+
+static GEN
+FlxqE_chord_update(GEN R, GEN P, GEN Q, GEN a4, GEN T, ulong p, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = gcopy(P);
+    return FlxqE_vert(P, Q, T, p);
+  }
+  else if (ell_is_inf(P))
+  {
+    *pt_R = gcopy(R);
+    return FlxqE_vert(R, Q, T, p);
+  }
+  else if (Flx_equal(gel(P, 1), gel(R, 1)))
+  {
+    if (Flx_equal(gel(P, 2), gel(R, 2)))
+      return FlxqE_tangent_update(R, Q, a4, T, p, pt_R);
+    else
+    {
+      *pt_R = ellinf();
+      return FlxqE_vert(R, Q, T, p);
+    }
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = FlxqE_add_slope(P, R, a4, T, p, &slope);
+    tmp1  = Flxq_mul(Flx_sub(gel(Q, 1), gel(R, 1), p), slope, T, p);
+    tmp2  = Flx_add(tmp1, gel(R, 2), p);
+    return Flx_sub(gel(Q, 2), tmp2, p);
+  }
+}
+
+/* Returns the Miller function f_{m, Q} evaluated at the point P using
+   the standard Miller algorithm.
+ */
+
+struct _FlxqE_miller
+{
+  ulong p;
+  GEN T, a4, P;
+};
+
+static GEN
+FlxqE_Miller_dbl(void* E, GEN d)
+{
+  struct _FlxqE_miller *m = (struct _FlxqE_miller *)E;
+  ulong p  = m->p;
+  GEN T = m->T, a4 = m->a4, P = m->P;
+  GEN v, line;
+  GEN num = Flxq_sqr(gel(d,1), T, p);
+  GEN denom = Flxq_sqr(gel(d,2), T, p);
+  GEN point = gel(d,3);
+  line = FlxqE_tangent_update(point, P, a4, T, p, &point);
+  num  = Flxq_mul(num, line, T, p);
+  v = FlxqE_vert(point, P, T, p);
+  denom = Flxq_mul(denom, v, T, p);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+FlxqE_Miller_add(void* E, GEN va, GEN vb)
+{
+  struct _FlxqE_miller *m = (struct _FlxqE_miller *)E;
+  ulong p = m->p;
+  GEN T = m->T, a4 = m->a4, P = m->P;
+  GEN v, line, point;
+  GEN na = gel(va,1), da = gel(va,2), pa = gel(va,3);
+  GEN nb = gel(vb,1), db = gel(vb,2), pb = gel(vb,3);
+  GEN num   = Flxq_mul(na, nb, T, p);
+  GEN denom = Flxq_mul(da, db, T, p);
+  line = FlxqE_chord_update(pa, pb, P, a4, T, p, &point);
+  num  = Flxq_mul(num, line, T, p);
+  v = FlxqE_vert(point, P, T, p);
+  denom = Flxq_mul(denom, v, T, p);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+FlxqE_Miller(GEN Q, GEN P, GEN m, GEN a4, GEN T, ulong p)
+{
+  pari_sp ltop = avma;
+  struct _FlxqE_miller d;
+  GEN v, num, denom, g1;
+
+  d.a4 = a4; d.T = T; d.p = p; d.P = P;
+  g1 = pol1_Flx(get_Flx_var(T));
+  v = gen_pow(mkvec3(g1,g1,Q), m, (void*)&d, FlxqE_Miller_dbl, FlxqE_Miller_add);
+  num = gel(v,1); denom = gel(v,2);
+  if (!lgpol(num) || !lgpol(denom)) { avma = ltop; return NULL; }
+  return gerepileupto(ltop, Flxq_div(num, denom, T, p));
+}
+
+GEN
+FlxqE_weilpairing(GEN P, GEN Q, GEN m, GEN a4, GEN T, ulong p)
+{
+  pari_sp ltop = avma;
+  GEN num, denom, result;
+  if (ell_is_inf(P) || ell_is_inf(Q) || Flx_equal(P,Q))
+    return pol1_Flx(get_Flx_var(T));
+  num    = FlxqE_Miller(P, Q, m, a4, T, p);
+  if (!num) return pol1_Flx(get_Flx_var(T));
+  denom  = FlxqE_Miller(Q, P, m, a4, T, p);
+  if (!denom) {avma = ltop; return pol1_Flx(get_Flx_var(T)); }
+  result = Flxq_div(num, denom, T, p);
+  if (mpodd(m))
+    result  = Flx_neg(result, p);
+  return gerepileupto(ltop, result);
+}
+
+GEN
+FlxqE_tatepairing(GEN P, GEN Q, GEN m, GEN a4, GEN T, ulong p)
+{
+  GEN num;
+  if (ell_is_inf(P) || ell_is_inf(Q))
+    return pol1_Flx(get_Flx_var(T));
+  num = FlxqE_Miller(P, Q, m, a4, T, p);
+  return num? num: pol1_Flx(get_Flx_var(T));
+}
+
+static GEN
+_FlxqE_pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)
+{
+  struct _FlxqE *e = (struct _FlxqE *) E;
+  return  Flxq_order(FlxqE_weilpairing(P,Q,m,e->a4,e->T,e->p), F, e->T, e->p);
+}
+
+GEN
+Flxq_ellgroup(GEN a4, GEN a6, GEN N, GEN T, ulong p, GEN *pt_m)
+{
+  struct _FlxqE e;
+  GEN q = powuu(p, get_Flx_degree(T));
+  e.a4=a4; e.a6=a6; e.T=T; e.p=p;
+  return gen_ellgroup(N, subis(q,1), pt_m, (void*)&e, &FlxqE_group, _FlxqE_pairorder);
+}
+
+GEN
+Flxq_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, ulong p)
+{
+  GEN P;
+  pari_sp av = avma;
+  struct _FlxqE e;
+  e.a4=a4; e.a6=a6; e.T=T; e.p=p;
+  switch(lg(D)-1)
+  {
+  case 1:
+    P = gen_gener(gel(D,1), (void*)&e, &FlxqE_group);
+    P = mkvec(FlxqE_changepoint(P, ch, T, p));
+    break;
+  default:
+    P = gen_ellgens(gel(D,1), gel(D,2), m, (void*)&e, &FlxqE_group, _FlxqE_pairorder);
+    gel(P,1) = FlxqE_changepoint(gel(P,1), ch, T, p);
+    gel(P,2) = FlxqE_changepoint(gel(P,2), ch, T, p);
+    break;
+  }
+  return gerepilecopy(av, P);
+}
+/***********************************************************************/
+/**                                                                   **/
+/**                          Point counting                           **/
+/**                                                                   **/
+/***********************************************************************/
+
+static GEN _can_invl(void *E, GEN V) {(void) E; return V; }
+
+static GEN _can_lin(void *E, GEN F, GEN V, GEN q)
+{
+  GEN v = RgX_splitting(V, 3);
+  (void) E;
+  return FpX_sub(V,ZXV_dotproduct(v, F), q);
+}
+
+static GEN
+_can_iter(void *E, GEN f, GEN q)
+{
+  GEN h = RgX_splitting(f,3);
+  GEN h1s = ZX_sqr(gel(h,1)), h2s = ZX_sqr(gel(h,2)), h3s = ZX_sqr(gel(h,3));
+  GEN h12 = ZX_mul(gel(h,1), gel(h,2));
+  GEN h13 = ZX_mul(gel(h,1), gel(h,3));
+  GEN h23 = ZX_mul(gel(h,2), gel(h,3));
+  GEN h1c = ZX_mul(gel(h,1), h1s);
+  GEN h3c = ZX_mul(gel(h,3), h3s);
+  GEN th = ZX_mul(ZX_sub(h2s,ZX_mulu(h13,3)),gel(h,2));
+  GEN y = FpX_sub(f,ZX_add(RgX_shift_shallow(h3c,2),ZX_add(RgX_shift_shallow(th,1),h1c)),q);
+  (void) E;
+  return mkvecn(7,y,h1s,h2s,h3s,h12,h13,h23);
+}
+
+static GEN
+_can_invd(void *E, GEN V, GEN v, GEN qM, long M)
+{
+  GEN h1s=gel(v,2), h2s=gel(v,3), h3s=gel(v,4);
+  GEN h12=gel(v,5), h13=gel(v,6), h23=gel(v,7);
+  GEN F = mkvec3(ZX_sub(h1s,RgX_shift_shallow(h23,1)),RgX_shift_shallow(ZX_sub(h2s,h13),1),
+                 ZX_sub(RgX_shift_shallow(h3s,2),RgX_shift_shallow(h12,1)));
+  (void)E;
+  return gen_ZpX_Dixon(ZXV_Z_mul(F, utoi(3)), V, qM, utoi(3), M, NULL,
+                                                 _can_lin, _can_invl);
+}
+
+static GEN
+F3x_canonlift(GEN P, long n)
+{ return gen_ZpX_Newton(Flx_to_ZX(P),utoi(3), n, NULL, _can_iter, _can_invd); }
+
+static GEN _can5_invl(void *E, GEN V) {(void) E; return V; }
+
+static GEN _can5_lin(void *E, GEN F, GEN V, GEN q)
+{
+  ulong p = *(ulong*)E;
+  GEN v = RgX_splitting(V, p);
+  return FpX_sub(V,ZXV_dotproduct(v, F), q);
+}
+
+/* P(X,t) -> P(X*t^n,t) mod (t^p-1) */
+static GEN
+_shift(GEN P, long n, ulong p, long v)
+{
+  long i, l=lg(P);
+  GEN r = cgetg(l,t_POL); r[1] = P[1];
+  for(i=2;i<l;i++)
+  {
+    long s = n*(i-2)%p;
+    GEN ci = gel(P,i);
+    if (typ(ci)==t_INT)
+      gel(r,i) = monomial(ci, s, v);
+    else
+      gel(r,i) = RgX_rotate_shallow(ci, s, p);
+  }
+  return FpXX_renormalize(r, l);
+}
+
+struct _can_mul
+{
+  GEN T, q;
+  ulong p;
+};
+
+static GEN
+_can5_mul(void *E, GEN A, GEN B)
+{
+  struct _can_mul *d = (struct _can_mul *)E;
+  GEN a = gel(A,1), b = gel(B,1);
+  long n = itos(gel(A,2));
+  GEN bn = _shift(b, n, d->p, get_FpX_var(d->T));
+  GEN c = FpXQX_mul(a, bn, d->T, d->q);
+  return mkvec2(c, addii(gel(A,2), gel(B,2)));
+}
+
+static GEN
+_can5_sqr(void *E, GEN A)
+{
+  return _can5_mul(E,A,A);
+}
+
+static GEN
+_can5_iter(void *E, GEN f, GEN q)
+{
+  pari_sp av = avma;
+  struct _can_mul D;
+  ulong p = *(ulong*)E;
+  long i;
+  GEN N, P, d, V, fs;
+  D.q = q; D.T = ZX_Z_sub(monomial(gen_1,p,MAXVARN),gen_1);
+  D.p = p;
+  fs = mkvec2(_shift(f, 1, p, MAXVARN), gen_1);
+  N = gel(gen_powu(fs,p-1,(void*)&D,_can5_sqr,_can5_mul),1);
+  N = simplify_shallow(FpXQX_red(N,polcyclo(p,MAXVARN),q));
+  P = FpX_mul(N,f,q);
+  P = RgX_deflate(P, p);
+  d = RgX_splitting(N, p);
+  V = cgetg(p+1,t_VEC);
+  gel(V,1) = ZX_mulu(gel(d,1), p);
+  for(i=2; i<= (long)p; i++)
+    gel(V,i) = ZX_mulu(RgX_shift_shallow(gel(d,p+2-i), 1), p);
+  return gerepilecopy(av, mkvec2(ZX_sub(f,P),V));
+}
+
+static GEN
+_can5_invd(void *E, GEN H, GEN v, GEN qM, long M)
+{
+  ulong p = *(long*)E;
+  return gen_ZpX_Dixon(gel(v,2), H, qM, utoi(p), M, E, _can5_lin, _can5_invl);
+}
+
+static GEN
+Flx_canonlift(GEN P, long n, ulong p)
+{
+  return p==3 ? F3x_canonlift(P,n):
+         gen_ZpX_Newton(Flx_to_ZX(P),utoi(p), n, &p, _can5_iter, _can5_invd);
+}
+
+/* assume a and n  are coprime */
+static GEN
+RgX_circular_shallow(GEN P, long a, long n)
+{
+  long i, l = lgpol(P);
+  GEN Q = cgetg(2+n,t_POL);
+  Q[1] = P[1];
+  for(i=0; i<l; i++)
+    gel(Q,2+(i*a)%n) = gel(P,2+i);
+  for(   ; i<n; i++)
+    gel(Q,2+(i*a)%n) = gen_0;
+  return normalizepol_lg(Q,2+n);
+}
+
+static GEN
+ZpXQ_frob_cyc(GEN x, GEN T, GEN q, ulong p)
+{
+  long n = get_FpX_degree(T);
+  return FpX_rem(RgX_circular_shallow(x,p,n+1), T, q);
+}
+
+static GEN
+ZpXQ_frob(GEN x, GEN Xm, GEN T, GEN q, ulong p)
+{
+  if (lg(Xm)==1)
+    return ZpXQ_frob_cyc(x, T, q, p);
+  else
+  {
+    long n = get_FpX_degree(T);
+    GEN V = RgX_blocks(RgX_inflate(x, p), n, p);
+    GEN W = ZXV_dotproduct(V, Xm);
+    return FpX_rem(W, T, q);
+  }
+}
+
+struct _lift_lin
+{
+  ulong p;
+  GEN sqx, Tp;
+  GEN ai, Xm;
+};
+
+static GEN _lift_invl(void *E, GEN x)
+{
+  struct _lift_lin *d = (struct _lift_lin *) E;
+  GEN T = d->Tp;
+  ulong p = d->p;
+  GEN xai = Flxq_mul(ZX_to_Flx(x, p), d->ai, T, p);
+  return Flx_to_ZX(Flxq_lroot_fast(xai, d->sqx, T, p));
+}
+
+static GEN _lift_lin(void *E, GEN F, GEN x2, GEN q)
+{
+  struct _lift_lin *d = (struct _lift_lin *) E;
+  pari_sp av = avma;
+  GEN T = gel(F,3), Xm = gel(F,4);
+  GEN y2  = ZpXQ_frob(x2, Xm, T, q, d->p);
+  GEN lin = FpX_add(ZX_mul(gel(F,1), y2), ZX_mul(gel(F,2), x2), q);
+  return gerepileupto(av, FpX_rem(lin, T, q));
+}
+
+static GEN
+FpM_FpXV_bilinear(GEN P, GEN X, GEN Y, GEN p)
+{
+   pari_sp av = avma;
+   GEN s =  ZX_mul(FpXV_FpC_mul(X,gel(P,1),p),gel(Y,1));
+   long i, l = lg(P);
+   for(i=2; i<l; i++)
+     s = ZX_add(s, ZX_mul(FpXV_FpC_mul(X,gel(P,i),p),gel(Y,i)));
+   return gerepileupto(av, FpX_red(s, p));
+}
+
+static GEN
+FpM_FpXQV_bilinear(GEN P, GEN X, GEN Y, GEN T, GEN p)
+{
+  return FpX_rem(FpM_FpXV_bilinear(P,X,Y,p),T,p);
+}
+
+static GEN
+FpXC_powderiv(GEN M, GEN p)
+{
+  long i, l;
+  long v = varn(gel(M,2));
+  GEN m = cgetg_copy(M, &l);
+  gel(m,1) = pol_0(v);
+  gel(m,2) = pol_1(v);
+  for(i=2; i<l-1; i++)
+    gel(m,i+1) = FpX_Fp_mul(gel(M,i),utoi(i), p);
+  return m;
+}
+
+struct _lift_iso
+{
+  GEN phi;
+  GEN Xm,T;
+  GEN sqx, Tp;
+  ulong p;
+};
+
+static GEN
+_lift_iter(void *E, GEN x2, GEN q)
+{
+  struct _lift_iso *d = (struct _lift_iso *) E;
+  ulong p = d->p;
+  GEN TN = FpXT_red(d->T, q), XN = FpXV_red(d->Xm, q);
+  GEN y2 = ZpXQ_frob(x2, XN, TN, q, p);
+  GEN xp = FpXQ_powers(x2, p, TN, q);
+  GEN yp = FpXQ_powers(y2, p, TN, q);
+  GEN V  = FpM_FpXQV_bilinear(d->phi,xp,yp,TN,q);
+  return mkvec3(V,xp,yp);
+}
+
+static GEN
+_lift_invd(void *E, GEN V, GEN v, GEN qM, long M)
+{
+  struct _lift_iso *d = (struct _lift_iso *) E;
+  struct _lift_lin e;
+  ulong p = d->p;
+  GEN TM = FpXT_red(d->T, qM), XM = FpXV_red(d->Xm, qM);
+  GEN xp = FpXV_red(gel(v,2), qM);
+  GEN yp = FpXV_red(gel(v,3), qM);
+  GEN Dx = FpM_FpXQV_bilinear(d->phi, FpXC_powderiv(xp, qM), yp, TM, qM);
+  GEN Dy = FpM_FpXQV_bilinear(d->phi, xp, FpXC_powderiv(yp, qM), TM, qM);
+  GEN F = mkvec4(Dy, Dx, TM, XM);
+  e.ai = Flxq_inv(ZX_to_Flx(Dy,p),d->Tp,p);
+  e.sqx = d->sqx; e.Tp = d->Tp; e.p=p; e.Xm = XM;
+  return gen_ZpX_Dixon(F,V,qM,utoi(p),M,(void*) &e, _lift_lin, _lift_invl);
+}
+
+static GEN
+lift_isogeny(GEN phi, GEN x0, long n, GEN Xm, GEN T, GEN sqx, GEN Tp, ulong p)
+{
+  struct _lift_iso d;
+  d.phi=phi;
+  d.Xm=Xm; d.T=T;
+  d.sqx=sqx; d.Tp=Tp; d.p=p;
+  return gen_ZpX_Newton(x0, utoi(p), n,(void*)&d, _lift_iter, _lift_invd);
+}
+
+static GEN
+getc2(GEN act, GEN X, GEN T, GEN q, ulong p, long N)
+{
+  GEN A1 = RgV_to_RgX(gel(act,1),0), A2 =  RgV_to_RgX(gel(act,2),0);
+  long n = brent_kung_optpow(maxss(degpol(A1),degpol(A2)),2,1);
+  GEN xp = FpXQ_powers(X,n,T,q);
+  GEN P  = FpX_FpXQV_eval(A1, xp, T, q);
+  GEN Q  = FpX_FpXQV_eval(A2, xp, T, q);
+  return FpXQ_mul(P,ZpXQ_inv(Q,T,utoi(p),N),T,q);
+}
+
+struct _ZpXQ_norm
+{
+  long n;
+  GEN T, p;
+};
+
+static GEN
+ZpXQ_norm_mul(void *E, GEN x, GEN y)
+{
+  struct _ZpXQ_norm *D = (struct _ZpXQ_norm*)E;
+  GEN P = gel(x,1), Q = gel(y,1);
+  long a = mael(x,2,1), b = mael(y,2,1);
+  retmkvec2(FpXQ_mul(P,ZpXQ_frob_cyc(Q, D->T, D->p, a), D->T, D->p),
+            mkvecsmall((a*b)%D->n));
+}
+
+static GEN
+ZpXQ_norm_sqr(void *E, GEN x)
+{
+  return ZpXQ_norm_mul(E, x, x);
+}
+
+/* Assume T = Phi_(n) and n prime */
+GEN
+ZpXQ_norm_pcyc(GEN x, GEN T, GEN q, GEN p)
+{
+  GEN z;
+  struct _ZpXQ_norm D;
+  long d = get_FpX_degree(T);
+  D.T = T; D.p = q; D.n = d+1;
+  if (d==1) return ZX_copy(x);
+  z = mkvec2(x,mkvecsmall(p[2]));
+  z = gen_powu(z,d,(void*)&D,ZpXQ_norm_sqr,ZpXQ_norm_mul);
+  return gmael(z,1,2);
+}
+
+/* Assume T = Phi_(n) and n prime */
+static GEN
+ZpXQ_sqrtnorm_pcyc(GEN x, GEN T, GEN q, GEN p, long e)
+{
+  GEN z = ZpXQ_norm_pcyc(x, T, q, p);
+  return Zp_sqrtlift(z,Fp_sqrt(z,p),p,e);
+}
+
+/* Assume a = 1 [p], return the square root of the norm */
+static GEN
+ZpXQ_sqrtnorm(GEN a, GEN T, GEN q, GEN p, long e)
+{
+  GEN s = Fp_div(FpXQ_trace(ZpXQ_log(a, T, p, e), T, q), gen_2, q);
+  return modii(gel(Qp_exp(cvtop(s, p, e-1)),4), q);
+}
+
+struct _teich_lin
+{
+  ulong p;
+  GEN sqx, Tp;
+  long m;
+};
+
+static GEN
+_teich_invl(void *E, GEN x)
+{
+  struct _teich_lin *d = (struct _teich_lin *) E;
+  ulong p = d->p;
+  GEN T = d->Tp;
+  return Flx_to_ZX(Flxq_lroot_fast(ZX_to_Flx(x, p), d->sqx, T, p));
+}
+
+static GEN
+_teich_lin(void *E, GEN F, GEN x2, GEN q)
+{
+  struct _teich_lin *d = (struct _teich_lin *) E;
+  pari_sp av = avma;
+  GEN T = gel(F,2), Xm = gel(F,3);
+  GEN y2  = ZpXQ_frob(x2, Xm, T, q, d->p);
+  GEN lin = FpX_sub(y2, ZX_mulu(ZX_mul(gel(F,1), x2), d->p), q);
+  return gerepileupto(av, FpX_rem(lin, T, q));
+}
+
+struct _teich_iso
+{
+  GEN Xm, T;
+  GEN sqx, Tp;
+  ulong p;
+};
+
+static GEN
+_teich_iter(void *E, GEN x2, GEN q)
+{
+  struct _teich_iso *d = (struct _teich_iso *) E;
+  ulong p = d->p;
+  GEN TN = FpXT_red(d->T, q), XN = FpXV_red(d->Xm, q);
+  GEN y2 = ZpXQ_frob(x2, XN, TN, q, d->p);
+  GEN x1 = FpXQ_powu(x2, p-1, TN, q);
+  GEN xp = FpXQ_mul(x2, x1, TN, q);
+  GEN V = FpX_sub(y2,xp,q);
+  return mkvec2(V,x1);
+}
+
+static GEN
+_teich_invd(void *E, GEN V, GEN v, GEN qM, long M)
+{
+  struct _teich_iso *d = (struct _teich_iso *) E;
+  struct _teich_lin e;
+  ulong p = d->p;
+  GEN TM = FpXT_red(d->T, qM), XM = FpXV_red(d->Xm, qM);
+  GEN x1 = FpX_red(gel(v,2), qM);
+  GEN F = mkvec3(x1, TM, XM);
+  e.sqx = d->sqx; e.Tp = d->Tp; e.p=p;
+  return gen_ZpX_Dixon(F,V,qM,utoi(p),M,(void*) &e, _teich_lin, _teich_invl);
+}
+
+static GEN
+Teichmuller_lift(GEN x, GEN Xm, GEN T, GEN sqx, GEN Tp, ulong p, long N)
+{
+  struct _teich_iso d;
+  d.Xm = Xm; d.T = T; d.sqx = sqx; d.Tp = Tp; d.p = p;
+  return gen_ZpX_Newton(x,utoi(p), N,(void*)&d, _teich_iter, _teich_invd);
+}
+
+static GEN
+get_norm(GEN a4, GEN a6, GEN T, ulong p, long N)
+{
+  long sv=T[1];
+  GEN a;
+  if (p==3) a = gel(a4,1);
+  else
+  {
+    GEN P = mkpoln(4, pol1_Flx(sv), pol0_Flx(sv), a4, a6);
+    a = gel(FlxqX_pow(P,p>>1,T,p),2+p-1);
+  }
+  return Zp_sqrtnlift(gen_1,subss(p,1),utoi(Flxq_norm(a,T,p)),utoi(p), N);
+}
+
+static GEN
+fill_pols(long n, const long *v, long m, const long *vn,
+          const long *vd, GEN *act)
+{
+  long i, j;
+  long d = upowuu(n,12/(n-1));
+  GEN N, D, M = zeromatcopy(n+1,n+1);
+  gmael(M,1,n+1) = gen_1;
+  for(i=2;i<=n+1;i++)
+    for(j=i-1;j<=n;j++)
+      gmael(M,i,j) = mulis(powuu(d,i-2),v[j-i+1]);
+  N = cgetg(m+1,t_COL);
+  D = cgetg(m+1,t_COL);
+  for(i=1;i<=m;i++)
+  {
+    gel(N,i) = stoi(*vn++);
+    gel(D,i) = stoi(*vd++);
+  }
+  *act = mkmat2(N,D);
+  return M;
+}
+
+/*
+  These polynomials were extracted from the ECHIDNA databases
+  available at <http://echidna.maths.usyd.edu.au/echidna/>
+  and computed by David R. Kohel.
+  Return the matrix of the modular polynomial, set act to the parametrization,
+  and set dj to the opposite of the supersingular j-invariant.
+*/
+static GEN
+get_Kohel_polynomials(ulong p, GEN *act, long *dj)
+{
+  const long mat3[] = {-1,-36,-270};
+  const long num3[] = {1,-483,-21141,-59049};
+  const long den3[] = {1,261, 4347, -6561};
+  const long mat5[] = {-1,-30,-315,-1300,-1575};
+  const long num5[] = {-1,490,20620,158750,78125};
+  const long den5[] = {-1,-254,-4124,-12250,3125};
+  const long mat7[] = {-1,-28,-322,-1904,-5915,-8624,-4018};
+  const long num7[] = {1,-485,-24058,-343833,-2021642,-4353013,-823543};
+  const long den7[] = {1,259,5894,49119,168406,166355,-16807};
+  const long mat13[]= {-1,-26,-325,-2548,-13832,-54340,-157118,-333580,-509366,
+                       -534820,-354536,-124852,-15145};
+  const long num13[]= {1,-487,-24056,-391463,-3396483,-18047328,-61622301,
+                       -133245853,-168395656,-95422301,-4826809};
+  const long den13[]= {1,257,5896,60649,364629,1388256,3396483,5089019,4065464,
+                       1069939,-28561};
+  switch(p)
+  {
+  case 3:
+    *dj = 0;
+    return fill_pols(3,mat3,4,num3,den3,act);
+  case 5:
+    *dj = 0;
+    return fill_pols(5,mat5,5,num5,den5,act);
+  case 7:
+    *dj = 1;
+    return fill_pols(7,mat7,7,num7,den7,act);
+  case 13:
+    *dj = 8;
+    return fill_pols(13,mat13,11,num13,den13,act);
+  }
+  *dj=0; *act = NULL;
+  return NULL;
+}
+
+long
+zx_is_pcyc(GEN T)
+{
+  long i, n = degpol(T);
+  if (!uisprime(n+1))
+    return 0;
+  for (i=0; i<=n; i++)
+    if (T[i+2]!=1UL)
+      return 0;
+  return 1;
+}
+
+static GEN
+Flxq_ellcard_Harley(GEN a4, GEN a6, GEN T, ulong p)
+{
+  pari_sp av = avma, av2;
+  pari_timer ti;
+  long n = get_Flx_degree(T), N = (n+4)/2, dj;
+  GEN q = powuu(p, N);
+  GEN T2, Xm, s1, c2, t, lr;
+  GEN S1, sqx;
+  GEN Nc2, Np;
+  GEN act, phi = get_Kohel_polynomials(p, &act, &dj);
+  long ispcyc = zx_is_pcyc(get_Flx_mod(T));
+  timer_start(&ti);
+  if (!ispcyc)
+  {
+    T2 = Flx_canonlift(get_Flx_mod(T),N,p);
+    if (DEBUGLEVEL) timer_printf(&ti,"Teich");
+  } else
+    T2 = Flx_to_ZX(get_Flx_mod(T));
+  T2 = FpX_get_red(T2, q); T = ZXT_to_FlxT(T2, p);
+  av2 = avma;
+  if (DEBUGLEVEL) timer_printf(&ti,"Barrett");
+  if (!ispcyc)
+  {
+    Xm = FpXQ_powers(monomial(gen_1,n,get_FpX_var(T2)),p-1,T2,q);
+    if (DEBUGLEVEL) timer_printf(&ti,"Xm");
+  } else
+    Xm = cgetg(1,t_VEC);
+  s1 = Flxq_inv(Flx_Fl_add(Flxq_ellj(a4,a6,T,p),dj, p),T,p);
+  lr = Flxq_lroot(polx_Flx(get_Flx_var(T)), T, p);
+  sqx = Flxq_powers(lr, p-1, T, p);
+  S1 = lift_isogeny(phi, Flx_to_ZX(s1), N, Xm, T2, sqx, T ,p);
+  if (DEBUGLEVEL) timer_printf(&ti,"Lift isogeny");
+  c2 = getc2(act, S1, T2, q, p, N);
+  if (DEBUGLEVEL) timer_printf(&ti,"c^2");
+  if (p>3 && !ispcyc)
+  {
+    GEN c2p = Flx_to_ZX(Flxq_inv(ZX_to_Flx(c2,p),T,p));
+    GEN tc2 = Teichmuller_lift(c2p,Xm, T2,sqx,T,p,N);
+    if (DEBUGLEVEL) timer_printf(&ti,"Teichmuller/Fq");
+    c2 = FpX_rem(FpX_mul(tc2,c2,q),T2,q);
+  }
+  c2 = gerepileupto(av2, c2);
+  if (DEBUGLEVEL) timer_printf(&ti,"tc2");
+  Nc2 = (ispcyc? ZpXQ_sqrtnorm_pcyc: ZpXQ_sqrtnorm)(c2, T2, q, utoi(p), N);
+  if (DEBUGLEVEL) timer_printf(&ti,"Norm");
+  Np = get_norm(a4,a6,T,p,N);
+  if (p>3 && ispcyc)
+  {
+    GEN Ncpi =  utoi(Fl_inv(umodiu(Nc2,p), p));
+    GEN tNc2 = Zp_sqrtnlift(gen_1, subss(p,1), Ncpi, utoi(p),N);
+    if (DEBUGLEVEL) timer_printf(&ti,"Teichmuller/Fp");
+    Nc2 = Fp_mul(Nc2,tNc2,q);
+  }
+  t = Fp_center(Fp_mul(Nc2,Np,q),q,shifti(q,-1));
+  return gerepileupto(av, subii(addis(powuu(p,n),1),t));
+}
+
+/***************************************************************************/
+/*                                                                         */
+/*                          Shanks Mestre                                  */
+/*                                                                         */
+/***************************************************************************/
+
+/* Return the lift of a (mod b), which is closest to h */
+static GEN
+closest_lift(GEN a, GEN b, GEN h)
+{
+  return addii(a, mulii(b, diviiround(subii(h,a), b)));
+}
+
+static GEN
+FlxqE_find_order(GEN f, GEN h, GEN bound, GEN B, GEN a4, GEN T, ulong p)
+{
+  pari_sp av = avma, av1, lim;
+  pari_timer Ti;
+  long s = itos( gceil(gsqrt(gdiv(bound,B),DEFAULTPREC)) ) >> 1;
+  GEN tx, ti;
+  GEN fh = FlxqE_mul(f, h, a4, T, p);
+  GEN F, P = fh, fg;
+  long i;
+  if (DEBUGLEVEL) timer_start(&Ti);
+  if (ell_is_inf(fh)) return h;
+  F = FlxqE_mul(f, B, a4, T, p);
+  if (s < 3)
+  { /* we're nearly done: naive search */
+    GEN Q = P;
+    for (i=1;; i++)
+    {
+      P = FlxqE_add(P, F, a4, T, p); /* h.f + i.F */
+      if (ell_is_inf(P)) return gerepileupto(av, addii(h, mului(i,B)));
+      Q = FlxqE_sub(Q, F, a4, T, p); /* h.f - i.F */
+      if (ell_is_inf(Q)) return gerepileupto(av, subii(h, mului(i,B)));
+    }
+  }
+  tx = cgetg(s+1,t_VECSMALL);
+  /* Baby Step/Giant Step */
+  av1 = avma; lim = stack_lim(av1,3);
+  for (i=1; i<=s; i++)
+  { /* baby steps */
+    tx[i] = hash_GEN(gel(P, 1));
+    P = FlxqE_add(P, F, a4, T, p); /* h.f + i.F */
+    if (ell_is_inf(P)) return gerepileupto(av, addii(h, mului(i,B)));
+    if (low_stack(lim, stack_lim(av1,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"[ellap3] baby steps, i=%ld",i);
+      P = gerepileupto(av1,P);
+    }
+  }
+  if (DEBUGLEVEL) timer_printf(&Ti, "[ellap3] baby steps, s = %ld",s);
+  /* giant steps: fg = s.F */
+  fg = gerepileupto(av1, FlxqE_sub(P, fh, a4, T, p));
+  if (ell_is_inf(fg)) return gerepileupto(av,mului(s,B));
+  ti = vecsmall_indexsort(tx); /* = permutation sorting tx */
+  tx = perm_mul(tx,ti);
+  if (DEBUGLEVEL) timer_printf(&Ti, "[ellap3] sorting");
+  av1 = avma; lim = stack_lim(av1,3);
+  for (P=fg, i=1; ; i++)
+  {
+    long k = hash_GEN(gel(P,1));
+    long r = zv_search(tx, k);
+    if (r)
+    {
+      while (r && tx[r] == k) r--;
+      for (r++; r <= s && tx[r] == k; r++)
+      {
+        long j = ti[r]-1;
+        GEN Q = FlxqE_add(FlxqE_mul(F, stoi(j), a4, T, p), fh, a4, T, p);
+        if (DEBUGLEVEL) timer_printf(&Ti, "[ellap3] giant steps, i = %ld",i);
+        if (Flx_equal(gel(P,1), gel(Q,1)))
+        {
+          if (Flx_equal(gel(P,2), gel(Q,2))) i = -i;
+          return gerepileupto(av,addii(h, mulii(addis(mulss(s,i), j), B)));
+        }
+      }
+    }
+    P = FlxqE_add(P,fg,a4,T,p);
+    if (low_stack(lim, stack_lim(av1,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"[ellap3] giants steps, i=%ld",i);
+      P = gerepileupto(av1,P);
+    }
+  }
+}
+
+static void
+Flx_next(GEN t, ulong p)
+{
+  long i;
+  for(i=2;;i++)
+    if ((ulong)t[i]==p-1)
+      t[i]=0;
+    else
+    {
+      t[i]++;
+      break;
+    }
+}
+
+static void
+Flx_renormalize_ip(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>=2; i--)
+    if (x[i]) break;
+  setlg(x, i+1);
+}
+
+static ulong
+F3xq_ellcard_naive(GEN a2, GEN a6, GEN T)
+{
+  pari_sp av = avma;
+  long i, d = get_Flx_degree(T), lx = d+2;
+  long q = upowuu(3, d), a;
+  GEN x = zero_zv(lx); x[1] = get_Flx_var(T);
+  for(a=1, i=0; i<q; i++)
+  {
+    GEN rhs;
+    Flx_renormalize_ip(x, lx);
+    rhs = Flx_add(Flxq_mul(Flxq_sqr(x, T, 3), Flx_add(x, a2, 3), T, 3), a6, 3);
+    if (!lgpol(rhs)) a++; else if (Flxq_issquare(rhs, T, 3)) a+=2;
+    Flx_next(x, 3);
+  }
+  avma = av;
+  return a;
+}
+
+static ulong
+Flxq_ellcard_naive(GEN a4, GEN a6, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long i, d = get_Flx_degree(T), lx = d+2;
+  long q = upowuu(p, d), a;
+  GEN x = zero_zv(lx); x[1] = get_Flx_var(T);
+  for(a=1, i=0; i<q; i++)
+  {
+    GEN x2, rhs;
+    Flx_renormalize_ip(x, lx);
+    x2  = Flxq_sqr(x, T, p);
+    rhs = Flx_add(Flxq_mul(x, Flx_add(x2, a4, p), T, p), a6, p);
+    if (!lgpol(rhs)) a++; else if (Flxq_issquare(rhs,T,p)) a+=2;
+    Flx_next(x,p);
+  }
+  avma = av;
+  return a;
+}
+
+static GEN
+Flxq_ellcard_Shanks(GEN a4, GEN a6, GEN q, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long vn = get_Flx_var(T);
+  long i, twistp, twistpold=0;
+  GEN h,f, ta4, u, x, A, B;
+  long n = get_Flx_degree(T);
+  GEN q1p = addsi(1, q), q2p = shifti(q1p, 1);
+  GEN bound = addis(sqrti(gmul2n(q,4)), 1); /* ceil( 4sqrt(q) ) */
+  /* once #E(Flxq) is know mod B >= bound, it is completely determined */
+  /* how many 2-torsion points ? */
+  switch(FlxqX_nbroots(mkpoln(4, pol1_Flx(vn), pol0_Flx(vn), a4, a6), T, p))
+  {
+  case 3:  A = gen_0; B = utoipos(4); break;
+  case 1:  A = gen_0; B = gen_2; break;
+  default: A = gen_1; B = gen_2; break; /* 0 */
+  }
+  h = closest_lift(A, B, q1p);
+  for(;;)
+  {
+    do
+    { /* look for points alternatively on E and its quadratic twist E' */
+      x = random_Flx(n,vn,p);
+      u = Flx_add(a6, Flxq_mul(Flx_add(a4, Flxq_sqr(x, T, p), p), x , T, p), p);
+      twistp = Flxq_issquare(u, T, p);
+    } while (twistp == twistpold);
+    twistpold = twistp;
+    /* [ux, u^2] is on E_u: y^2 = x^3 + c4 u^2 x + c6 u^3
+     * E_u isomorphic to E (resp. E') iff twistp = 1 (resp. -1)
+     * #E(F_p) = p+1 - a_p, #E'(F_p) = p+1 + a_p
+     *
+     * #E_u(Flxq) = A (mod B),  h is close to #E_u(Flxq) */
+
+    f = mkvec2(Flxq_mul(u, x, T, p), Flxq_sqr(u, T, p));
+    ta4 = Flxq_mul(a4, gel(f,2), T, p); /* a4 for E_u */
+    h = FlxqE_find_order(f, h, bound, B, ta4,T,p);
+    h = FlxqE_order(f, h, ta4, T, p);
+    /* h | #E_u(Flxq) = A (mod B) */
+    if (B == gen_1)
+      B = h;
+    else
+      A = Z_chinese_all(A, gen_0, B, h, &B);
+
+    i = (cmpii(B, bound) < 0);
+    /* If we are not done, update A mod B for the _next_ curve, isomorphic to
+     * the quadratic twist of this one */
+    if (i) A = remii(subii(q2p,A), B); /* #E(Fq)+#E'(Fq) = 2q+2 */
+
+    /* h = A mod B, closest lift to p+1 */
+    h = closest_lift(A, B, q1p);
+    if (!i) break;
+  }
+  return gerepileuptoint(av, twistp? h: subii(shifti(q1p,1),h));
+}
+
+static GEN
+F3xq_ellcard(GEN a2, GEN a6, GEN T)
+{
+  long n = get_Flx_degree(T);
+  if (n <= 2)
+    return utoi(F3xq_ellcard_naive(a2, a6, T));
+  else
+  {
+    GEN q1 = addis(powuu(3, get_Flx_degree(T)), 1), t;
+    GEN a = Flxq_div(a6,Flxq_powu(a2,3,T,3),T,3);
+    if (Flx_equal1(Flxq_powu(a, 8, T, 3)))
+    {
+      GEN P = Flxq_minpoly(a,T,3);
+      long dP = degpol(P); /* dP <= 2 */
+      ulong q = upowuu(3,dP);
+      GEN A2 = pol1_Flx(P[1]), A6 = Flx_rem(polx_Flx(P[1]), P, 3);
+      long tP = q + 1 - F3xq_ellcard_naive(A2, A6, P);
+      t = elltrace_extension(stoi(tP), n/dP, utoi(q));
+      if (umodiu(t, 3)!=1) t = negi(t);
+      return Flx_equal1(a2) || Flxq_issquare(a2,T,3) ? subii(q1,t): addii(q1,t);
+    }
+    else return Flxq_ellcard_Harley(mkvec(a2), a6, T, 3);
+  }
+}
+
+static GEN
+Flxq_ellcard_Satoh(GEN a4, GEN a6, GEN T, ulong p)
+{
+  long n = get_Flx_degree(T);
+  if (n <= 2)
+    return utoi(Flxq_ellcard_naive(a4, a6, T, p));
+  else
+  {
+    GEN q1 = addis(powuu(p, get_Flx_degree(T)), 1), t;
+    GEN j = Flxq_ellj(a4,a6,T,p);
+    if (Flx_equal1(Flxq_powu(j, p*p-1, T, p)))
+    {
+      GEN sk, sA4, u;
+      GEN P = Flxq_minpoly(j, T, p);
+      long dP = degpol(P); /* dP <= 2 */
+      GEN q = powuu(p,dP);
+      GEN k = mkvecsmall3(T[1], 1728%p, p-1);
+      GEN kj = Flx_shift(k, 1), k2j = Flxq_mul(kj, k, P, p);
+      GEN A4 = Flx_triple(kj,p), A6 = Flx_double(k2j,p);
+      GEN tP = addis(q, 1 - Flxq_ellcard_naive(A4, A6, P, p));
+      t = elltrace_extension(tP, n/dP, q);
+      sk = Flx_Fl_add(Flx_neg(j,p),1728%p,p);
+      sA4 = Flx_triple(Flxq_mul(sk,j,T,p),p);
+      u = Flxq_div(a4,sA4, T, p);
+      return Flxq_is2npower(u, 2, T, p) ? subii(q1,t): addii(q1,t);
+    }
+    else return Flxq_ellcard_Harley(a4, a6, T, p);
+  }
+}
+
+GEN
+Flxq_ellj(GEN a4, GEN a6, GEN T, ulong p)
+{
+  pari_sp av=avma;
+  if (p==3)
+  {
+    GEN J;
+    if (typ(a4)!=t_VEC) return pol0_Flx(get_Flx_var(T));
+    J = Flxq_div(Flxq_powu(gel(a4,1),3, T, p),Flx_neg(a6,p), T, p);
+    return gerepileuptoleaf(av, J);
+  }
+  else
+  {
+    pari_sp av=avma;
+    GEN a43 = Flxq_mul(a4,Flxq_sqr(a4,T,p),T,p);
+    GEN a62 = Flxq_sqr(a6,T,p);
+    GEN num = Flx_mulu(a43,6912,p);
+    GEN den = Flx_add(Flx_mulu(a43,4,p),Flx_mulu(a62,27,p),p);
+    return gerepileuptoleaf(av, Flxq_div(num, den, T, p));
+  }
+}
+
+static GEN
+F3xq_ellcardj(GEN a4, GEN a6, GEN T, GEN q, long n)
+{
+  const ulong p = 3;
+  ulong t;
+  GEN q1 = addis(q,1);
+  GEN na4 = Flx_neg(a4,p), ra4;
+  if (!Flxq_issquare(na4,T,p))
+    return q1;
+  ra4 = Flxq_sqrt(na4,T,p);
+  t = Flxq_trace(Flxq_div(a6,Flxq_mul(na4,ra4,T,p),T,p),T,p);
+  if (n%2==1)
+  {
+    GEN q3;
+    if (t==0) return q1;
+    q3 = powuu(p,(n+1)>>1);
+    return (t==1)^(n%4==1) ? subii(q1,q3): addii(q1,q3);
+  }
+  else
+  {
+    GEN q22, q2 = powuu(p,n>>1);
+    GEN W = Flxq_pow(a4,shifti(q,-2),T,p);
+    long s = (W[2]==1)^(n%4==2);
+    if (t!=0) return s ? addii(q1,q2): subii(q1, q2);
+    q22 = shifti(q2,1);
+    return s ? subii(q1,q22):  addii(q1, q22);
+  }
+}
+
+static GEN
+Flxq_ellcardj(GEN a4, GEN a6, ulong j, GEN T, GEN q, ulong p, long n)
+{
+  GEN q1 = addis(q,1);
+  if (j==0)
+  {
+    ulong w;
+    GEN W, t, N;
+    if (umodiu(q,6)!=1) return q1;
+    N = Fp_ffellcard(gen_0,gen_1,q,n,utoi(p));
+    t = subii(q1, N);
+    W = Flxq_pow(a6,diviuexact(shifti(q,-1), 3),T,p);
+    if (degpol(W)>0) /*p=5 mod 6*/
+      return Flx_equal1(Flxq_powu(W,3,T,p)) ? addii(q1,shifti(t,-1)):
+                                              subii(q1,shifti(t,-1));
+    w = W[2];
+    if (w==1)   return N;
+    if (w==p-1) return addii(q1,t);
+    else /*p=1 mod 6*/
+    {
+      GEN u = shifti(t,-1), v = sqrtint(diviuexact(subii(q,sqri(u)),3));
+      GEN a = addii(u,v), b = shifti(v,1);
+      if (Fl_powu(w,3,p)==1)
+      {
+        if (Fl_add(umodiu(a,p),Fl_mul(w,umodiu(b,p),p),p)==0)
+          return subii(q1,subii(shifti(b,1),a));
+        else
+          return addii(q1,addii(a,b));
+      }
+      else
+      {
+        if (Fl_sub(umodiu(a,p),Fl_mul(w,umodiu(b,p),p),p)==0)
+          return subii(q1,subii(a,shifti(b,1)));
+        else
+          return subii(q1,addii(a,b));
+      }
+    }
+  } else if (j==1728%p)
+  {
+    ulong w;
+    GEN W, N, t;
+    if (mod4(q)==3) return q1;
+    W = Flxq_pow(a4,shifti(q,-2),T,p);
+    if (degpol(W)>0) return q1; /*p=3 mod 4*/
+    w = W[2];
+    N = Fp_ffellcard(gen_1,gen_0,q,n,utoi(p));
+    if(w==1) return N;
+    t = subii(q1, N);
+    if(w==p-1) return addii(q1, t);
+    else /*p=1 mod 4*/
+    {
+      GEN u = shifti(t,-1), v = sqrtint(subii(q,sqri(u)));
+      if (Fl_add(umodiu(u,p),Fl_mul(w,umodiu(v,p),p),p)==0)
+        return subii(q1,shifti(v,1));
+      else
+        return addii(q1,shifti(v,1));
+    }
+  } else
+  {
+    ulong g = Fl_div(j, Fl_sub(1728%p, j, p), p);
+    GEN l = Flxq_div(Flx_triple(a6,p),Flx_double(a4,p),T,p);
+    GEN N = Fp_ffellcard(utoi(Fl_triple(g,p)),utoi(Fl_double(g,p)),q,n,utoi(p));
+    if (Flxq_issquare(l,T,p)) return N;
+    return subii(shifti(q1,1),N);
+  }
+}
+
+GEN
+Flxq_ellcard(GEN a4, GEN a6, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long n = get_Flx_degree(T);
+  GEN J, r, q = powuu(p,  n);
+  if (typ(a4)==t_VEC)
+    r = F3xq_ellcard(gel(a4,1), a6, T);
+  else if (p==3)
+    r = F3xq_ellcardj(a4, a6, T, q, n);
+  else if (degpol(a4)<=0 && degpol(a6)<=0)
+    r = Fp_ffellcard(utoi(Flx_eval(a4,0,p)),utoi(Flx_eval(a6,0,p)),q,n,utoi(p));
+  else if (degpol(J=Flxq_ellj(a4,a6,T,p))<=0)
+    r = Flxq_ellcardj(a4,a6,lgpol(J)?J[2]:0,T,q,p,n);
+  else if (p <= 7 || p==13)
+    r = Flxq_ellcard_Satoh(a4,a6,T,p);
+  else if (cmpis(q,100)<0)
+    r = utoi(Flxq_ellcard_naive(a4, a6, T, p));
+  else if (expi(q)<=62)
+    r = Flxq_ellcard_Shanks(a4, a6, q, T, p);
+  else
+  {
+    r = Fq_ellcard_SEA(Flx_to_ZX(a4),Flx_to_ZX(a6),q,Flx_to_ZX(T),utoi(p),0);
+    if (!r) pari_err_PACKAGE("seadata");
+  }
+  return gerepileuptoint(av, r);
+}
diff --git a/src/basemath/Flxq_log.c b/src/basemath/Flxq_log.c
new file mode 100644
index 0000000..e688dce
--- /dev/null
+++ b/src/basemath/Flxq_log.c
@@ -0,0 +1,695 @@
+/* Copyright (C) 2013 The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Let [ be the following order on Fp: 0 [ p-1 [ 1 [ p-2 [ 2 .. [ p\2
+and [[ the lexicographic extension of [ to Fp[T]. Compute the
+isomorphism (Fp[X], [[) -> (N,<) on P */
+
+static long
+Flx_cindex(GEN P, ulong p)
+{
+  long d = degpol(P), i;
+  ulong s = 0, p2 = (p-1)>>1;
+  for (i = 0; i <= d; ++i)
+  {
+    ulong x = P[d-i+2];
+    if (x<=p2) x = 2*x; else x = 1+2*(p-1-x);
+    s = p*s+x;
+  }
+  return s;
+}
+
+/* Compute the polynomial immediately after t for the [[ order */
+
+static void
+Flx_cnext(GEN t, ulong p)
+{
+  long i;
+  long p2 = p>>1;
+  for(i=2;;i++)
+    if (t[i]==p2)
+      t[i]=0;
+    else
+    {
+      t[i] = t[i]<p2 ? p-1-t[i]: p-t[i];
+      break;
+    }
+}
+
+static int
+has_deg1_auto(GEN T, ulong p)
+{
+  long i, n = degpol(T);
+  GEN a = polx_Flx(get_Flx_var(T));
+  for (i=1; i<n; i++)
+  {
+    a = Flxq_powu(a, p, T, p);
+    if (degpol(a)==1) return 1;
+  }
+  return 0;
+}
+
+static void
+smallirred_Flx_next(GEN a, long p)
+{
+  do
+  {
+    long i;
+    for(i=2;;i++)
+      if (++a[i]==p) a[i]=0;
+      else break;
+  } while (!Flx_is_irred(a, p) || has_deg1_auto(a,p) );
+}
+
+/* Avoid automorphisms of degree 1 */
+static GEN
+smallirred_Flx(long p, ulong n, long sv)
+{
+  GEN a = zero_zv(n+2);
+  a[1] = sv; a[3] = 1; a[n+2] = 1;
+  smallirred_Flx_next(a, p);
+  return a;
+}
+
+struct Flxq_log_rel
+{
+  long nbrel;
+  GEN rel;
+  long nb;
+  long r, off, nbmax, nbexp;
+  ulong nbtest;
+};
+
+static GEN
+cindex_Flx(long c, long d, ulong p, long v)
+{
+  GEN P = cgetg(d+3, t_VECSMALL);
+  long i;
+  P[1] = v;
+  for (i = 0; i <= d; ++i)
+  {
+    ulong x = c%p;
+    P[i+2] = (x&1) ? p-1-(x>>1) : x>>1;
+    c/=p;
+  }
+  return Flx_renormalize(P, d+3);
+}
+
+static GEN
+factorel(GEN h, ulong p)
+{
+  GEN F = Flx_factcantor(h, p, 0);
+  GEN F1 = gel(F, 1), F2 = gel(F, 2);
+  long i, l1 = lg(F1)-1;
+  GEN p2 = cgetg(l1+1, t_VECSMALL);
+  GEN e2 = cgetg(l1+1, t_VECSMALL);
+  for (i = 1; i <= l1; ++i)
+  {
+    p2[i] = Flx_cindex(gel(F1, i), p);
+    e2[i] = F2[i];
+  }
+  return mkmat2(p2, e2);
+}
+
+static long
+Flx_addifsmooth3(pari_sp *av, struct Flxq_log_rel *r, GEN h, long u, long v, long w, ulong p)
+{
+  long off = r->off;
+  r->nbtest++;
+  if (Flx_is_smooth(h, r->r, p))
+  {
+    GEN z = factorel(h, p);
+    if (v<0)
+      z = mkmat2(vecsmall_append(gel(z,1),off+u),vecsmall_append(gel(z,2),-1));
+    else
+      z = famatsmall_reduce(mkmat2(
+            vecsmall_concat(gel(z,1),mkvecsmall3(off+u,off+v,off+w)),
+            vecsmall_concat(gel(z,2),mkvecsmall3(-1,-1,-1))));
+    gel(r->rel,++r->nbrel) = gerepilecopy(*av,z);
+    if (DEBUGLEVEL && (r->nbrel&511UL)==0)
+      err_printf("%ld%% ",r->nbrel*100/r->nbexp);
+    *av = avma;
+  } else avma = *av;
+  return r->nbrel==r->nb || r->nbrel==r->nbmax;
+}
+
+static void
+Flx_renormalize_inplace(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>1; i--)
+    if (x[i]) break;
+  setlg(x, i+1);
+}
+
+/*
+   Let T*X^e=C^3-R
+   a+b+c = 0
+   (C+a)*(C+b)*(C+c) = C^3+ (a*b+a*c+b*c)*C+a*b*c
+   = R + (a*b+a*c+b*c)*C+a*b*c
+   = R + (a*b-c^2)*C+a*b*c
+ */
+static void
+Flxq_log_cubic(struct Flxq_log_rel *r, GEN C, GEN R, ulong p)
+{
+  long l = lg(C);
+  GEN a = zero_zv(l); /*We allocate one extra word to catch overflow*/
+  GEN b = zero_zv(l);
+  pari_sp av = avma;
+  long i,j,k, dh=0;
+  for(i=0; ; i++, Flx_cnext(a, p))
+  {
+    Flx_renormalize_inplace(a, l+1);
+    r->nb++;
+    if (Flx_addifsmooth3(&av, r, Flx_add(a, C, p), i, -1, -1, p)) return;
+    for(j=2; j<=l; j++) b[j] = 0;
+    for(j=0; j<=i; j++, Flx_cnext(b, p))
+    {
+      GEN h,c;
+      GEN pab,pabc,pabc2;
+      Flx_renormalize_inplace(b, l+1);
+      c = Flx_neg(Flx_add(a,b,p),p);
+      k = Flx_cindex(c, p);
+      if (k > j) continue;
+      pab  = Flx_mul(a, b, p);
+      pabc = Flx_mul(pab,c,p);
+      pabc2= Flx_sub(pab,Flx_sqr(c,p),p);
+      h = Flx_add(R,Flx_add(Flx_mul(C,pabc2,p),pabc,p), p);
+      h = Flx_normalize(h, p);
+      dh = maxss(dh,degpol(h));
+      if (Flx_addifsmooth3(&av, r, h, i, j, k, p)) return;
+    }
+  }
+}
+
+static GEN
+Flxq_log_find_rel(GEN b, long r, GEN T, ulong p, GEN *g, long *e)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  while (1)
+  {
+    GEN M;
+    *g = Flxq_mul(*g, b, T, p); (*e)++;
+    M = Flx_halfgcd(*g,T,p);
+    if (Flx_is_smooth(gcoeff(M,1,1), r, p))
+    {
+      GEN z = Flx_add(Flx_mul(gcoeff(M,1,1),*g,p), Flx_mul(gcoeff(M,1,2),T,p),p);
+      if (Flx_is_smooth(z, r, p))
+      {
+        GEN F = factorel(z, p);
+        GEN G = factorel(gcoeff(M,1,1), p);
+        GEN rel = mkmat2(vecsmall_concat(gel(F, 1),gel(G, 1)),
+                         vecsmall_concat(gel(F, 2),zv_neg(gel(G, 2))));
+        gerepileall(av,2,g,&rel); return rel;
+      }
+    }
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flxq_log_find_rel");
+      *g = gerepilecopy(av, *g);
+    }
+  }
+}
+
+/* Generalised Odlyzko formulae ( EUROCRYPT '84, LNCS 209, pp. 224-314, 1985. ) */
+/* Return the number of monic, k smooth, degree n polynomials for k=1..r */
+static GEN
+smoothness_vec(ulong p, long r, long n)
+{
+  long i,j,k;
+  GEN R = cgetg(r+1, t_VEC);
+  GEN V = cgetg(n+1, t_VEC);
+  for (j = 1; j <= n; ++j)
+    gel(V, j) =  binomialuu(p+j-1,j);
+  gel(R, 1) = gel(V, n);
+  for (k = 2; k <= r; ++k)
+  {
+    GEN W = cgetg(n+1, t_VEC);
+    GEN Ik = ffnbirred(utoi(p),k);
+    for (j = 1; j <= n; ++j)
+    {
+      long l = j/k;
+      GEN s = gen_0;
+      pari_sp av2 = avma;
+      if (l*k == j)
+      {
+        s = binomial(addis(Ik,l-1), l);
+        l--;
+      }
+      for (i = 0; i <= l; ++i)
+        s = addii(s, mulii(gel(V, j-k*i), binomial(addis(Ik,i-1), i)));
+      gel(W, j) = gerepileuptoint(av2, s);
+    }
+    V = W;
+    gel(R, k) = gel(V, n);
+  }
+  return R;
+}
+
+/* Solve N^2*pr/6 + N*prC = N+fb
+   N^2*pr/6 + N*(prC-1) -fb = 0
+ */
+
+static GEN
+smooth_cost(GEN fb, GEN pr, GEN prC)
+{
+  GEN a = gdivgs(pr,6);
+  GEN b = gsubgs(prC,1);
+  GEN c = gneg(fb);
+  GEN vD = gsqrt(gsub(gsqr(b),gmul2n(gmul(a,c),2)),BIGDEFAULTPREC);
+  return ceil_safe(gdiv(gsub(vD,b),gmul2n(a,1)));
+}
+
+/* Return best choice of r.
+   We loop over d until there is sufficiently many triples (a,b,c) (a+b+c=0)
+   of degree <=d with respect to the probability of smoothness of (a*b-c^2)*C
+ */
+
+static GEN
+smooth_best(long p, long n, long *pt_r, long *pt_nb)
+{
+  pari_sp av = avma, av2;
+  GEN bestc = NULL;
+  long bestr = 0, bestFB = 0;
+  long r,d, dC = (n+2)/3;
+  for (r = 1; r < dC; ++r)
+  {
+    GEN fb = ffsumnbirred(utoi(p), r);
+    GEN smoothC = smoothness_vec(p,r,dC);
+    GEN prC = gdiv(gel(smoothC,r), powuu(p,dC));
+    ulong rels = 0;
+    av2 = avma;
+    for(d=0; d<dC && rels < ULONG_MAX; d++)
+    {
+      GEN c;
+      long dt = dC+2*d;
+      GEN smooth = smoothness_vec(p,r,dt);
+      GEN pr = gdiv(gel(smooth,r), powuu(p,dt));
+      GEN FB = addii(fb,powuu(p,d));
+      GEN N = smooth_cost(subiu(FB,rels),pr,prC);
+      GEN Nmax = powuu(p,d+1);
+      if (gcmp(N,Nmax) >= 0)
+      {
+        rels = itou_or_0(addui(rels, gceil(gmul(gdivgs(sqri(Nmax),6),pr))));
+        if (!rels) rels = ULONG_MAX;
+        avma = av2;
+        continue;
+      }
+      c = gdivgs(addii(powuu(p,2*d),sqri(N)),6);
+      FB = addii(FB,N);
+      if ((!bestc || gcmp(gmul2n(c,r), gmul2n(bestc,bestr)) < 0))
+      {
+        if (DEBUGLEVEL)
+          err_printf("r=%ld d=%ld fb=%Ps early rels=%lu P=%.5Pe -> C=%.5Pe \n",
+                      r, dt, FB, rels, pr, c);
+        bestc = c;
+        bestr = r;
+        bestFB = itos_or_0(FB);
+      }
+      break;
+    }
+  }
+  *pt_r=bestr;
+  *pt_nb=bestFB;
+  return bestc ? gerepileupto(av, gceil(bestc)): NULL;
+}
+
+static GEN
+check_kernel(long r, GEN M, long nbi, long nbrow, GEN T, ulong p, GEN m)
+{
+  pari_sp av = avma;
+  long N = 3*upowuu(p, r);
+  GEN K = FpMs_leftkernel_elt(M, nbrow, m);
+  long i, f=0;
+  long lm = lgefint(m), u=1;
+  GEN g;
+  GEN idx = diviiexact(subis(powuu(p,degpol(T)),1),m);
+  pari_timer ti;
+  if (DEBUGLEVEL) timer_start(&ti);
+  while (signe(gel(K,u))==0)
+   u++;
+  K = FpC_Fp_mul(K, Fp_inv(gel(K, u), m), m);
+  g = Flxq_pow(cindex_Flx(u, r, p, T[1]), idx, T, p);
+  setlg(K, N);
+  for (i=1; i<N; i++)
+  {
+    GEN k = gel(K,i);
+    pari_sp av = avma;
+    long t = signe(k) && Flx_equal(Flxq_pow(g, k, T, p),
+                                   Flxq_pow(cindex_Flx(i,r,p,T[1]), idx, T, p));
+    avma = av;
+    if (!t)
+      gel(K,i) = cgetineg(lm);
+    else
+      f++;
+  }
+  if (DEBUGLEVEL) timer_printf(&ti,"found %ld/%ld logs", f, nbi);
+  if (f < maxss(3,maxss(p/2,nbi/p))) return NULL; /* Not enough logs found */
+  return gerepilecopy(av, K);
+}
+
+static GEN
+Flxq_log_rec(GEN W, GEN a, long r, GEN T, ulong p, GEN m)
+{
+  long AV = 0, u = 1;
+  GEN g = a, b;
+  pari_timer ti;
+  while (!equali1(gel(W,u)))
+   u++;
+  b = cindex_Flx(u, r, p, T[1]);
+  while(1)
+  {
+    long i, l;
+    GEN V, F, E, Ao;
+    timer_start(&ti);
+    V = Flxq_log_find_rel(b, r, T, p, &g, &AV);
+    if (DEBUGLEVEL>1) timer_printf(&ti,"%ld-smooth element",r);
+    F = gel(V,1); E = gel(V,2);
+    l = lg(F);
+    Ao = gen_0;
+    for(i=1; i<l; i++)
+    {
+      GEN R = gel(W,F[i]);
+      if (signe(R)<=0)
+        break;
+      Ao = Fp_add(Ao, mulis(R, E[i]), m);
+    }
+    if (i==l) return subis(Ao,AV);
+  }
+}
+
+static GEN
+Flxq_log_index_cubic(GEN a0, GEN b0, GEN m, GEN T0, ulong p)
+{
+  long n = get_Flx_degree(T0), r, nb;
+  pari_sp av = avma;
+  struct Flxq_log_rel rel;
+  long nbi;
+  GEN W, M, S, T, a, b, Ao, Bo, e, C, R;
+  pari_timer ti;
+  GEN cost = smooth_best(p, n, &r, &nb);
+  GEN cost_rho = sqrti(shifti(m,2));
+  if (!cost || gcmp(cost,cost_rho)>=0) { avma = av; return NULL; }
+  nbi = itos(ffsumnbirred(stoi(p), r));
+  if (DEBUGLEVEL)
+  {
+    err_printf("Size FB=%ld, looking for %ld relations, %Ps tests needed\n", nbi, nb,cost);
+    timer_start(&ti);
+  }
+  T = smallirred_Flx(p,n,get_Flx_var(T0));
+  for(;;)
+  {
+    S = Flx_ffisom(T0,T,p);
+    a = Flx_Flxq_eval(a0, S, T, p);
+    b = Flx_Flxq_eval(b0, S, T, p);
+    C = Flx_shift(pol1_Flx(get_Flx_var(T)), (n+2)/3);
+    R = Flxq_powu(C,3,T,p);
+    if (DEBUGLEVEL)
+      timer_printf(&ti," model change: %Ps",Flx_to_ZX(T));
+    rel.nbmax=2*nb;
+    M = cgetg(rel.nbmax+1, t_VEC);
+    rel.rel = M;
+    rel.nbrel = 0; rel.r = r; rel.off = 3*upowuu(p,r);
+    rel.nb = nbi; rel.nbexp = nb; rel.nbtest=0;
+    Flxq_log_cubic(&rel, C, R, p);
+    setlg(M,1+rel.nbrel);
+    if (DEBUGLEVEL)
+    {
+      err_printf("\n");
+      timer_printf(&ti," %ld relations, %ld generators (%ld tests)",rel.nbrel,rel.nb,rel.nbtest);
+    }
+    W = check_kernel(r, M, nbi, rel.off + rel.nb - nbi, T, p, m);
+    if (W) break;
+    if (DEBUGLEVEL) timer_start(&ti);
+    smallirred_Flx_next(T,p);
+  }
+  if (DEBUGLEVEL) timer_start(&ti);
+  Ao = Flxq_log_rec(W, a, r, T, p, m);
+  if (DEBUGLEVEL) timer_printf(&ti,"smooth element");
+  Bo = Flxq_log_rec(W, b, r, T, p, m);
+  if (DEBUGLEVEL) timer_printf(&ti,"smooth generator");
+  e = Fp_div(Ao, Bo, m);
+  if (!Flx_equal(Flxq_pow(b0, e, T0, p), a0)) pari_err_BUG("Flxq_log");
+  return gerepileupto(av, e);
+}
+
+INLINE GEN Flx_frob(GEN u, ulong p) { return Flx_inflate(u, p); }
+
+static GEN
+rel_Coppersmith(long r, GEN u, GEN v, long h, GEN R, long d, ulong p)
+{
+  GEN a, b, F, G, M;
+  if (degpol(Flx_gcd(u,v,p))) return NULL;
+  a = Flx_add(Flx_shift(u, h), v, p);
+  if (lgpol(a)==0 || !Flx_is_smooth(a, r, p)) return NULL;
+  b = Flx_add(Flx_mul(R, Flx_frob(u, p), p), Flx_shift(Flx_frob(v, p),d), p);
+  if (!Flx_is_smooth(b, r, p)) return NULL;
+  F = factorel(a, p); G = factorel(b, p);
+  M = mkmat2(vecsmall_concat(gel(F, 1), vecsmall_append(gel(G, 1), 2*p)),
+             vecsmall_concat(zv_z_mul(gel(F, 2),p), vecsmall_append(zv_neg(gel(G, 2)),d)));
+  return famatsmall_reduce(M);
+}
+
+static GEN
+Flxq_log_Coppersmith(long nbrel, long r, GEN T, ulong p)
+{
+  long dT = degpol(T);
+  long h = dT/p, d = dT-(h*p);
+  GEN R = Flx_sub(Flx_shift(pol1_Flx(T[1]), dT), T, p);
+  GEN u = zero_zv(dT+2), v = zero_zv(dT+2);
+  long nbtest = 0, rel = 0;
+  GEN M = cgetg(nbrel+1, t_VEC);
+  pari_sp av = avma;
+  long i, j;
+  if (DEBUGLEVEL) err_printf("Coppersmith (R = %ld): ",degpol(R));
+  for (i=1; ; i++)
+  {
+    Flx_cnext(u, p);
+    Flx_renormalize_inplace(u, dT+2);
+    for(j=2; j<lg(v); j++) v[j] = 0;
+    for(j=1; j<i; j++)
+    {
+      GEN z;
+      Flx_cnext(v, p);
+      Flx_renormalize_inplace(v, dT+2);
+      avma = av;
+      if (Flx_lead(u)==1)
+      {
+        z = rel_Coppersmith(r, u, v, h, R, d, p);
+        nbtest++;
+        if (z)
+        {
+          gel(M,++rel) = gerepilecopy(av, z); av = avma;
+          if (DEBUGLEVEL && (rel&511UL)==0)
+            err_printf("%ld%%[%ld] ",rel*100/nbrel,i);
+        }
+        if (rel>nbrel) break;
+      }
+      if (i==j) continue;
+      if (Flx_lead(v)==1)
+      {
+        z = rel_Coppersmith(r, v, u, h, R, d, p);
+        nbtest++;
+        if (z)
+        {
+          gel(M,++rel) = gerepilecopy(av, z); av = avma;
+          if (DEBUGLEVEL && (rel&511UL)==0)
+            err_printf("%ld%%[%ld] ",rel*100/nbrel,i);
+        }
+        if (rel>nbrel) break;
+      }
+    }
+    if (rel>nbrel) break;
+  }
+  if (DEBUGLEVEL) err_printf(": %ld tests\n", nbtest);
+  return M;
+}
+
+static GEN Flxq_log_Coppersmith_d(GEN W, GEN g, long r, GEN T, ulong p, GEN mo);
+
+static GEN
+Flxq_log_from_rel(GEN W, GEN rel, long r, GEN T, ulong p, GEN m)
+{
+  pari_sp av = avma;
+  GEN F = gel(rel,1), E = gel(rel,2), o = gen_0;
+  long i, l = lg(F);
+  for(i=1; i<l; i++)
+  {
+    GEN R = gel(W, F[i]);
+    if (signe(R)==0) /* Already failed */
+      return NULL;
+    else if (signe(R)<0) /* Not yet tested */
+    {
+      setsigne(gel(W,F[i]),0);
+      R = Flxq_log_Coppersmith_d(W, cindex_Flx(F[i],r,p,T[1]), r, T, p, m);
+      if (!R) return NULL;
+    }
+    o = Fp_add(o, mulis(R, E[i]), m);
+  }
+  return gerepileuptoint(av, o);
+}
+
+static GEN
+Flxq_log_Coppersmith_d(GEN W, GEN g, long r, GEN T, ulong p, GEN mo)
+{
+  pari_sp av = avma, av2;
+  long dg = degpol(g), k = r-1, m = maxss((dg-k)/2,0);
+  long i, j, l = dg-m, N;
+  GEN v = cgetg(k+m+1,t_MAT);
+  long dT = degpol(T);
+  long h = dT/p, d = dT-h*p;
+  GEN R = Flx_rem(Flx_shift(pol1_Flx(T[1]), dT), T, p);
+  GEN z = Flx_rem(Flx_shift(pol1_Flx(T[1]), h), g, p);
+  for(i=1; i<=k+m; i++)
+  {
+    gel(v,i) = Flx_to_Flv(Flx_shift(z,-l),m);
+    z = Flx_rem(Flx_shift(z,1),g,p);
+  }
+  v = Flm_ker(v,p);
+  for(i=1; i<=k; i++)
+    gel(v,i) = Flv_to_Flx(gel(v,i),T[1]);
+  N = upowuu(p,k);
+  av2 = avma;
+  for (i=1; i<N; i++)
+  {
+    GEN p0,q,qh,a,b;
+    ulong el = i;
+    avma = av2;
+    q = pol0_Flx(T[1]);
+    for (j=1; j<=k; j++)
+    {
+      ulong r = el % p;
+      el /= p;
+      if (r) q = Flx_add(q, Flx_Fl_mul(gel(v,j), r, p), p);
+    }
+    qh = Flx_shift(q, h);
+    p0 = Flx_rem(qh, g, p);
+    b = Flx_sub(Flx_mul(R, Flx_frob(q, p), p), Flx_shift(Flx_frob(p0, p), d), p);
+    if (lgpol(b)==0 || !Flx_is_smooth(b, r, p)) continue;
+    a = Flx_div(Flx_sub(qh, p0, p), g, p);
+    if (degpol(Flx_gcd(a, q, p)) &&  degpol(Flx_gcd(a, p0 ,p)))
+      continue;
+    if (!(lgpol(a)==0 || !Flx_is_smooth(a, r, p)))
+    {
+      GEN F = factorel(b, p);
+      GEN G = factorel(a, p);
+      GEN FG = vecsmall_concat(vecsmall_append(gel(F, 1), 2*p), gel(G, 1));
+      GEN E  = vecsmall_concat(vecsmall_append(gel(F, 2), -d),
+          zv_z_mul(gel(G, 2),-p));
+      GEN R  = famatsmall_reduce(mkmat2(FG, E));
+      GEN l  = Flxq_log_from_rel(W, R, r, T, p, mo);
+      if (!l) continue;
+      l = Fp_div(l,utoi(p),mo);
+      if (dg <= r)
+      {
+        affii(l,gel(W,g[2]));
+        if (DEBUGLEVEL>1) err_printf("Found %lu\n", g[2]);
+      }
+      return gerepileuptoint(av, l);
+    }
+  }
+  avma = av;
+  return NULL;
+}
+
+static GEN
+Flxq_log_Coppersmith_rec(GEN W, long r2, GEN a, long r, GEN T, ulong p, GEN m)
+{
+  GEN b = polx_Flx(T[1]);
+  long AV = 0;
+  GEN g = a, bad = pol0_Flx(T[1]);
+  pari_timer ti;
+  while(1)
+  {
+    long i, l;
+    GEN V, F, E, Ao;
+    timer_start(&ti);
+    V = Flxq_log_find_rel(b, r2, T, p, &g, &AV);
+    if (DEBUGLEVEL>1) timer_printf(&ti,"%ld-smooth element",r2);
+    F = gel(V,1); E = gel(V,2);
+    l = lg(F);
+    Ao = gen_0;
+    for(i=1; i<l; i++)
+    {
+      GEN Fi = cindex_Flx(F[i], r2, p, T[1]);
+      GEN R;
+      if (degpol(Fi) <= r)
+      {
+        if (signe(gel(W,F[i]))==0)
+          break;
+        else if (signe(gel(W,F[i]))<0)
+        {
+          setsigne(gel(W,F[i]),0);
+          R = Flxq_log_Coppersmith_d(W,Fi,r,T,p,m);
+        } else
+          R = gel(W,F[i]);
+      }
+      else
+      {
+        if (Flx_equal(Fi,bad)) break;
+        R = Flxq_log_Coppersmith_d(W,Fi,r,T,p,m);
+        if (!R) bad = Fi;
+      }
+      if (!R) break;
+      Ao = Fp_add(Ao, mulis(R, E[i]), m);
+    }
+    if (i==l) return subis(Ao,AV);
+  }
+}
+
+
+static GEN
+Flxq_log_index_Coppersmith(GEN a0, GEN b0, GEN m, GEN T0, ulong p)
+{
+  pari_sp av = avma;
+  GEN  M, S, a, b, Ao=NULL, Bo=NULL, W, e;
+  pari_timer ti;
+  double rf = p ==3 ? 1.2 : .9;
+  long n = degpol(T0), r = (long) sqrt(n*rf);
+  GEN T;
+  long r2 = 3*r/2;
+  long nbi = itos(ffsumnbirred(utoi(p), r)), nbrel=nbi*5/4;
+  if (DEBUGLEVEL)
+  {
+    err_printf("Coppersmith: Parameters r=%ld r2=%ld\n", r,r2);
+    err_printf("Coppersmith: Size FB=%ld rel. needed=%ld\n", nbi, nbrel);
+    timer_start(&ti);
+  }
+  T = smallirred_Flx(p,n,get_Flx_var(T0));
+  S = Flx_ffisom(T0,T,p);
+  a = Flx_Flxq_eval(a0, S, T, p);
+  b = Flx_Flxq_eval(b0, S, T, p);
+  if (DEBUGLEVEL) timer_printf(&ti,"model change");
+  M = Flxq_log_Coppersmith(nbrel, r, T, p);
+  if (DEBUGLEVEL) timer_printf(&ti,"relations");
+  W = check_kernel(r, M, nbi, 3*upowuu(p,r), T, p, m);
+  timer_start(&ti);
+  Ao = Flxq_log_Coppersmith_rec(W, r2, a, r, T, p, m);
+  if (DEBUGLEVEL) timer_printf(&ti,"smooth element");
+  Bo = Flxq_log_Coppersmith_rec(W, r2, b, r, T, p, m);
+  if (DEBUGLEVEL) timer_printf(&ti,"smooth generator");
+  e = Fp_div(Ao, Bo, m);
+  if (!Flx_equal(Flxq_pow(b0,e,T0,p),a0)) pari_err_BUG("Flxq_log");
+  return gerepileupto(av, e);
+}
+
+GEN
+Flxq_log_index(GEN a, GEN b, GEN m, GEN T, ulong p)
+{
+  if (p==3 || (p==5 && degpol(T)>41))
+    return Flxq_log_index_Coppersmith(a, b, m, T, p);
+  else    return Flxq_log_index_cubic(a, b, m, T, p);
+}
diff --git a/src/basemath/FpE.c b/src/basemath/FpE.c
new file mode 100644
index 0000000..59c6ed2
--- /dev/null
+++ b/src/basemath/FpE.c
@@ -0,0 +1,1904 @@
+/* Copyright (C) 2009  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with points over elliptic curves over Fp */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              Fle                                  **/
+/**                                                                   **/
+/***********************************************************************/
+static GEN
+Fle_dbl_slope(GEN P, ulong a4, ulong p, ulong *slope)
+{
+  ulong x, y, Qx, Qy;
+  if (ell_is_inf(P) || !P[2]) return ellinf();
+  x = P[1]; y = P[2];
+  *slope = Fl_div(Fl_add(Fl_triple(Fl_sqr(x,p), p), a4, p),
+                  Fl_double(y, p), p);
+  Qx = Fl_sub(Fl_sqr(*slope, p), Fl_double(x, p), p);
+  Qy = Fl_sub(Fl_mul(*slope, Fl_sub(x, Qx, p), p), y, p);
+  return mkvecsmall2(Qx, Qy);
+}
+
+GEN
+Fle_dbl(GEN P, ulong a4, ulong p)
+{
+  ulong slope;
+  return Fle_dbl_slope(P,a4,p,&slope);
+}
+
+static GEN
+Fle_add_slope(GEN P, GEN Q, ulong a4, ulong p, ulong *slope)
+{
+  ulong Px, Py, Qx, Qy, Rx, Ry;
+  if (ell_is_inf(P)) return Q;
+  if (ell_is_inf(Q)) return P;
+  Px = P[1]; Py = P[2];
+  Qx = Q[1]; Qy = Q[2];
+  if (Px==Qx)
+    return Py==Qy ? Fle_dbl_slope(P, a4, p, slope): ellinf();
+  *slope = Fl_div(Fl_sub(Py, Qy, p), Fl_sub(Px, Qx, p), p);
+  Rx = Fl_sub(Fl_sub(Fl_sqr(*slope, p), Px, p), Qx, p);
+  Ry = Fl_sub(Fl_mul(*slope, Fl_sub(Px, Rx, p), p), Py, p);
+  return mkvecsmall2(Rx, Ry);
+}
+
+GEN
+Fle_add(GEN P, GEN Q, ulong a4, ulong p)
+{
+  ulong slope;
+  return Fle_add_slope(P,Q,a4,p,&slope);
+}
+
+static long
+Fle_dbl_inplace(GEN P, ulong a4, ulong p)
+{
+  ulong x, y, slope;
+  if (!P[2]) return 1;
+  x = P[1]; y = P[2];
+  slope = Fl_div(Fl_add(Fl_triple(Fl_sqr(x,p), p), a4, p),
+                 Fl_double(y, p), p);
+  P[1] = Fl_sub(Fl_sqr(slope, p), Fl_double(x, p), p);
+  P[2] = Fl_sub(Fl_mul(slope, Fl_sub(x, P[1], p), p), y, p);
+  return 0;
+}
+
+static long
+Fle_add_inplace(GEN P, GEN Q, ulong a4, ulong p)
+{
+  ulong Px, Py, Qx, Qy, slope;
+  if (ell_is_inf(Q)) return 0;
+  Px = P[1]; Py = P[2];
+  Qx = Q[1]; Qy = Q[2];
+  if (Px==Qx)
+    return Py==Qy ? Fle_dbl_inplace(P, a4, p): 1;
+  slope = Fl_div(Fl_sub(Py, Qy, p), Fl_sub(Px, Qx, p), p);
+  P[1] = Fl_sub(Fl_sub(Fl_sqr(slope, p), Px, p), Qx, p);
+  P[2] = Fl_sub(Fl_mul(slope, Fl_sub(Px, P[1], p), p), Py, p);
+  return 0;
+}
+
+static GEN
+Fle_neg(GEN P, ulong p)
+{
+  if (ell_is_inf(P)) return P;
+  return mkvecsmall2(P[1], Fl_neg(P[2], p));
+}
+
+GEN
+Fle_sub(GEN P, GEN Q, ulong a4, ulong p)
+{
+  pari_sp av = avma;
+  ulong slope;
+  return gerepileupto(av, Fle_add_slope(P, Fle_neg(Q, p), a4, p, &slope));
+}
+
+struct _Fle
+{
+  ulong a4,a6;
+  ulong p;
+};
+
+static GEN
+_Fle_dbl(void *E, GEN P)
+{
+  struct _Fle *ell = (struct _Fle *) E;
+  return Fle_dbl(P, ell->a4, ell->p);
+}
+
+static GEN
+_Fle_add(void *E, GEN P, GEN Q)
+{
+  struct _Fle *ell=(struct _Fle *) E;
+  return Fle_add(P, Q, ell->a4, ell->p);
+}
+
+static GEN
+_Fle_mulu(void *E, GEN P, ulong n)
+{
+  pari_sp av = avma;
+  struct _Fle *e=(struct _Fle *) E;
+  if (!n || ell_is_inf(P)) return ellinf();
+  if (n==1) return zv_copy(P);
+  if (n==2) return Fle_dbl(P,e->a4, e->p);
+  return gerepileupto(av, gen_powu(P, n, (void*)e, &_Fle_dbl, &_Fle_add));
+}
+
+GEN
+Fle_mulu(GEN P, ulong n, ulong a4, ulong p)
+{
+  struct _Fle E;
+  E.a4= a4; E.p = p;
+  return _Fle_mulu(&E, P, n);
+}
+
+static GEN
+_Fle_mul(void *E, GEN P, GEN n)
+{
+  pari_sp av = avma;
+  struct _Fle *e=(struct _Fle *) E;
+  long s = signe(n);
+  if (!s || ell_is_inf(P)) return ellinf();
+  if (s<0) P = Fle_neg(P, e->p);
+  if (is_pm1(n)) return s>0? zv_copy(P): P;
+  return gerepileupto(av, gen_pow(P, n, (void*)e, &_Fle_dbl, &_Fle_add));
+}
+
+GEN
+Fle_mul(GEN P, GEN n, ulong a4, ulong p)
+{
+  struct _Fle E;
+  E.a4 = a4; E.p = p;
+  return _Fle_mul(&E, P, n);
+}
+
+/* Finds a random non-singular point on E */
+
+GEN
+random_Fle(ulong a4, ulong a6, ulong p)
+{
+  ulong x, x2, y, rhs;
+  do
+  {
+    x   = random_Fl(p); /*  x^3+a4*x+a6 = x*(x^2+a4)+a6  */
+    x2  = Fl_sqr(x, p);
+    rhs = Fl_add(Fl_mul(x, Fl_add(x2, a4, p), p), a6, p);
+  } while ((!rhs && !Fl_add(Fl_triple(x2,p),a4,p))
+          || krouu(rhs, p) < 0);
+  y = Fl_sqrt(rhs, p);
+  return mkvecsmall2(x, y);
+}
+
+static GEN
+_Fle_rand(void *E)
+{
+  struct _Fle *e=(struct _Fle *) E;
+  return random_Fle(e->a4, e->a6, e->p);
+}
+
+static const struct bb_group Fle_group={_Fle_add,_Fle_mul,_Fle_rand,hash_GEN,zv_equal,ell_is_inf,NULL};
+
+GEN
+Fle_order(GEN z, GEN o, ulong a4, ulong p)
+{
+  pari_sp av = avma;
+  struct _Fle e;
+  e.a4=a4;
+  e.p=p;
+  return gerepileuptoint(av, gen_order(z, o, (void*)&e, &Fle_group));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              FpE                                  **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Theses functions deal with point over elliptic curves over Fp defined
+ * by an equation of the form y^2=x^3+a4*x+a6.
+ * Most of the time a6 is omitted since it can be recovered from any point
+ * on the curve.
+ */
+
+GEN
+RgE_to_FpE(GEN x, GEN p)
+{
+  if (ell_is_inf(x)) return x;
+  retmkvec2(Rg_to_Fp(gel(x,1),p),Rg_to_Fp(gel(x,2),p));
+}
+
+GEN
+FpE_to_mod(GEN x, GEN p)
+{
+  if (ell_is_inf(x)) return x;
+  retmkvec2(Fp_to_mod(gel(x,1),p),Fp_to_mod(gel(x,2),p));
+}
+
+GEN
+FpE_changepoint(GEN x, GEN ch, GEN p)
+{
+  pari_sp av = avma;
+  GEN p1,z,u,r,s,t,v,v2,v3;
+  if (ell_is_inf(x)) return x;
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  v = Fp_inv(u, p); v2 = Fp_sqr(v,p); v3 = Fp_mul(v,v2,p);
+  p1 = Fp_sub(gel(x,1),r,p);
+  z = cgetg(3,t_VEC);
+  gel(z,1) = Fp_mul(v2, p1, p);
+  gel(z,2) = Fp_mul(v3, Fp_sub(gel(x,2), Fp_add(Fp_mul(s,p1, p),t, p),p),p);
+  return gerepileupto(av, z);
+}
+
+GEN
+FpE_changepointinv(GEN x, GEN ch, GEN p)
+{
+  GEN u, r, s, t, X, Y, u2, u3, u2X, z;
+  if (ell_is_inf(x)) return x;
+  X = gel(x,1); Y = gel(x,2);
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  u2 = Fp_sqr(u, p); u3 = Fp_mul(u,u2,p);
+  u2X = Fp_mul(u2,X, p);
+  z = cgetg(3, t_VEC);
+  gel(z,1) = Fp_add(u2X,r,p);
+  gel(z,2) = Fp_add(Fp_mul(u3,Y,p), Fp_add(Fp_mul(s,u2X,p), t, p), p);
+  return z;
+}
+
+static GEN
+FpE_dbl_slope(GEN P, GEN a4, GEN p, GEN *slope)
+{
+  GEN x, y, Q;
+  if (ell_is_inf(P) || !signe(gel(P,2))) return ellinf();
+  x = gel(P,1); y = gel(P,2);
+  *slope = Fp_div(Fp_add(Fp_mulu(Fp_sqr(x,p), 3, p), a4, p),
+                  Fp_mulu(y, 2, p), p);
+  Q = cgetg(3,t_VEC);
+  gel(Q, 1) = Fp_sub(Fp_sqr(*slope, p), Fp_mulu(x, 2, p), p);
+  gel(Q, 2) = Fp_sub(Fp_mul(*slope, Fp_sub(x, gel(Q, 1), p), p), y, p);
+  return Q;
+}
+
+GEN
+FpE_dbl(GEN P, GEN a4, GEN p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FpE_dbl_slope(P,a4,p,&slope));
+}
+
+static GEN
+FpE_add_slope(GEN P, GEN Q, GEN a4, GEN p, GEN *slope)
+{
+  GEN Px, Py, Qx, Qy, R;
+  if (ell_is_inf(P)) return Q;
+  if (ell_is_inf(Q)) return P;
+  Px = gel(P,1); Py = gel(P,2);
+  Qx = gel(Q,1); Qy = gel(Q,2);
+  if (equalii(Px, Qx))
+  {
+    if (equalii(Py, Qy))
+      return FpE_dbl_slope(P, a4, p, slope);
+    else
+      return ellinf();
+  }
+  *slope = Fp_div(Fp_sub(Py, Qy, p), Fp_sub(Px, Qx, p), p);
+  R = cgetg(3,t_VEC);
+  gel(R, 1) = Fp_sub(Fp_sub(Fp_sqr(*slope, p), Px, p), Qx, p);
+  gel(R, 2) = Fp_sub(Fp_mul(*slope, Fp_sub(Px, gel(R, 1), p), p), Py, p);
+  return R;
+}
+
+GEN
+FpE_add(GEN P, GEN Q, GEN a4, GEN p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FpE_add_slope(P,Q,a4,p,&slope));
+}
+
+static GEN
+FpE_neg_i(GEN P, GEN p)
+{
+  if (ell_is_inf(P)) return P;
+  return mkvec2(gel(P,1), Fp_neg(gel(P,2), p));
+}
+
+GEN
+FpE_neg(GEN P, GEN p)
+{
+  if (ell_is_inf(P)) return ellinf();
+  return mkvec2(gcopy(gel(P,1)), Fp_neg(gel(P,2), p));
+}
+
+GEN
+FpE_sub(GEN P, GEN Q, GEN a4, GEN p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FpE_add_slope(P, FpE_neg_i(Q, p), a4, p, &slope));
+}
+
+struct _FpE
+{
+  GEN a4,a6;
+  GEN p;
+};
+
+static GEN
+_FpE_dbl(void *E, GEN P)
+{
+  struct _FpE *ell = (struct _FpE *) E;
+  return FpE_dbl(P, ell->a4, ell->p);
+}
+
+static GEN
+_FpE_add(void *E, GEN P, GEN Q)
+{
+  struct _FpE *ell=(struct _FpE *) E;
+  return FpE_add(P, Q, ell->a4, ell->p);
+}
+
+static GEN
+_FpE_mul(void *E, GEN P, GEN n)
+{
+  pari_sp av = avma;
+  struct _FpE *e=(struct _FpE *) E;
+  long s = signe(n);
+  if (!s || ell_is_inf(P)) return ellinf();
+  if (s<0) P = FpE_neg(P, e->p);
+  if (is_pm1(n)) return s>0? gcopy(P): P;
+  return gerepileupto(av, gen_pow(P, n, e, &_FpE_dbl, &_FpE_add));
+}
+
+GEN
+FpE_mul(GEN P, GEN n, GEN a4, GEN p)
+{
+  struct _FpE E;
+  E.a4= a4; E.p = p;
+  return _FpE_mul(&E, P, n);
+}
+
+/* Finds a random non-singular point on E */
+
+GEN
+random_FpE(GEN a4, GEN a6, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN x, x2, y, rhs;
+  do
+  {
+    avma= ltop;
+    x   = randomi(p); /*  x^3+a4*x+a6 = x*(x^2+a4)+a6  */
+    x2  = Fp_sqr(x, p);
+    rhs = Fp_add(Fp_mul(x, Fp_add(x2, a4, p), p), a6, p);
+  } while ((!signe(rhs) && !signe(Fp_add(Fp_mulu(x2,3,p),a4,p)))
+          || kronecker(rhs, p) < 0);
+  y = Fp_sqrt(rhs, p);
+  if (!y) pari_err_PRIME("random_FpE", p);
+  return gerepilecopy(ltop, mkvec2(x, y));
+}
+
+static GEN
+_FpE_rand(void *E)
+{
+  struct _FpE *e=(struct _FpE *) E;
+  return random_FpE(e->a4, e->a6, e->p);
+}
+
+static const struct bb_group FpE_group={_FpE_add,_FpE_mul,_FpE_rand,hash_GEN,ZV_equal,ell_is_inf,NULL};
+
+const struct bb_group *
+get_FpE_group(void ** pt_E, GEN a4, GEN a6, GEN p)
+{
+  struct _FpE *e = (struct _FpE *) stack_malloc(sizeof(struct _FpE));
+  e->a4 = a4; e->a6 = a6; e->p  = p;
+  *pt_E = (void *) e;
+  return &FpE_group;
+}
+
+GEN
+FpE_order(GEN z, GEN o, GEN a4, GEN p)
+{
+  pari_sp av = avma;
+  struct _FpE e;
+  e.a4=a4;
+  e.p=p;
+  return gerepileuptoint(av, gen_order(z, o, (void*)&e, &FpE_group));
+}
+
+GEN
+FpE_log(GEN a, GEN b, GEN o, GEN a4, GEN p)
+{
+  pari_sp av = avma;
+  struct _FpE e;
+  e.a4=a4;
+  e.p=p;
+  return gerepileuptoint(av, gen_PH_log(a, b, o, (void*)&e, &FpE_group));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                            Pairings                               **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Derived from APIP from and by Jerome Milan, 2012 */
+
+static GEN
+FpE_vert(GEN P, GEN Q, GEN p)
+{
+  if (ell_is_inf(P))
+    return gen_1;
+  return Fp_sub(gel(Q, 1), gel(P, 1), p);
+}
+
+/* Computes the equation of the line tangent to R and returns its
+   evaluation at the point Q. Also doubles the point R.
+ */
+
+static GEN
+FpE_tangent_update(GEN R, GEN Q, GEN a4, GEN p, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = ellinf();
+    return gen_1;
+  }
+  else if (signe(gel(R,2)) == 0)
+  {
+    *pt_R = ellinf();
+    return FpE_vert(R, Q, p);
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = FpE_dbl_slope(R, a4, p, &slope);
+    tmp1 = Fp_sub(gel(Q, 1), gel(R, 1), p);
+    tmp2 = Fp_add(Fp_mul(tmp1, slope, p), gel(R,2), p);
+    return Fp_sub(gel(Q, 2), tmp2, p);
+  }
+}
+
+/* Computes the equation of the line through R and P, and returns its
+   evaluation at the point Q. Also adds P to the point R.
+ */
+
+static GEN
+FpE_chord_update(GEN R, GEN P, GEN Q, GEN a4, GEN p, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = gcopy(P);
+    return FpE_vert(P, Q, p);
+  }
+  else if (ell_is_inf(P))
+  {
+    *pt_R = gcopy(R);
+    return FpE_vert(R, Q, p);
+  }
+  else if (equalii(gel(P, 1), gel(R, 1)))
+  {
+    if (equalii(gel(P, 2), gel(R, 2)))
+      return FpE_tangent_update(R, Q, a4, p, pt_R);
+    else {
+      *pt_R = ellinf();
+      return FpE_vert(R, Q, p);
+    }
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = FpE_add_slope(P, R, a4, p, &slope);
+    tmp1  = Fp_mul(Fp_sub(gel(Q, 1), gel(R, 1), p), slope, p);
+    tmp2  = Fp_add(tmp1, gel(R, 2), p);
+    return Fp_sub(gel(Q, 2), tmp2, p);
+  }
+}
+
+/* Returns the Miller function f_{m, Q} evaluated at the point P using
+   the standard Miller algorithm.
+ */
+
+struct _FpE_miller
+{
+  GEN p, a4, P;
+};
+
+static GEN
+FpE_Miller_dbl(void* E, GEN d)
+{
+  struct _FpE_miller *m = (struct _FpE_miller *)E;
+  GEN p = m->p, a4 = m->a4, P = m->P;
+  GEN v, line;
+  GEN num = Fp_sqr(gel(d,1), p);
+  GEN denom = Fp_sqr(gel(d,2), p);
+  GEN point = gel(d,3);
+  line = FpE_tangent_update(point, P, a4, p, &point);
+  num  = Fp_mul(num, line, p);
+  v = FpE_vert(point, P, p);
+  denom = Fp_mul(denom, v, p);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+FpE_Miller_add(void* E, GEN va, GEN vb)
+{
+  struct _FpE_miller *m = (struct _FpE_miller *)E;
+  GEN p = m->p, a4= m->a4, P = m->P;
+  GEN v, line, point;
+  GEN na = gel(va,1), da = gel(va,2), pa = gel(va,3);
+  GEN nb = gel(vb,1), db = gel(vb,2), pb = gel(vb,3);
+  GEN num   = Fp_mul(na, nb, p);
+  GEN denom = Fp_mul(da, db, p);
+  line = FpE_chord_update(pa, pb, P, a4, p, &point);
+  num  = Fp_mul(num, line, p);
+  v = FpE_vert(point, P, p);
+  denom = Fp_mul(denom, v, p);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+FpE_Miller(GEN Q, GEN P, GEN m, GEN a4, GEN p)
+{
+  pari_sp ltop = avma;
+  struct _FpE_miller d;
+  GEN v, num, denom;
+
+  d.a4 = a4; d.p = p; d.P = P;
+  v = gen_pow(mkvec3(gen_1,gen_1,Q), m, (void*)&d, FpE_Miller_dbl, FpE_Miller_add);
+  num = gel(v,1); denom = gel(v,2);
+  if (!signe(num) || !signe(denom)) { avma = ltop; return NULL; }
+  return gerepileupto(ltop, Fp_div(num, denom, p));
+}
+
+GEN
+FpE_weilpairing(GEN P, GEN Q, GEN m, GEN a4, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN num, denom, result;
+  if (ell_is_inf(P) || ell_is_inf(Q) || ZV_equal(P,Q))
+    return gen_1;
+  num    = FpE_Miller(P, Q, m, a4, p);
+  if (!num) return gen_1;
+  denom  = FpE_Miller(Q, P, m, a4, p);
+  if (!denom) { avma = ltop; return gen_1; }
+  result = Fp_div(num, denom, p);
+  if (mpodd(m))
+    result  = Fp_neg(result, p);
+  return gerepileupto(ltop, result);
+}
+
+GEN
+FpE_tatepairing(GEN P, GEN Q, GEN m, GEN a4, GEN p)
+{
+  GEN num;
+  if (ell_is_inf(P) || ell_is_inf(Q))
+    return gen_1;
+  num = FpE_Miller(P, Q, m, a4, p);
+  return num? num: gen_1;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                            Cardinal                               **/
+/**                                                                   **/
+/***********************************************************************/
+
+/*assume a4,a6 reduced mod p and 3 < p < 1627 */
+static ulong
+Fl_ellcard_naive(ulong a4, ulong a6, ulong p)
+{
+  ulong i;
+  long a = p+1;
+  for(i=0; i<p; i++)
+    a += krouu((i*i+a4)*i+a6,p); /* no overflow */
+  return a;
+}
+
+/* z1 <-- z1 + z2, with precomputed inverse */
+static void
+FpE_add_ip(GEN z1, GEN z2, GEN a4, GEN p, GEN p2inv)
+{
+  GEN p1,x,x1,x2,y,y1,y2;
+
+  x1 = gel(z1,1); y1 = gel(z1,2);
+  x2 = gel(z2,1); y2 = gel(z2,2);
+  if (x1 == x2)
+    p1 = Fp_add(a4, mulii(x1,mului(3,x1)), p);
+  else
+    p1 = Fp_sub(y2,y1, p);
+
+  p1 = Fp_mul(p1, p2inv, p);
+  x = Fp_sub(sqri(p1), addii(x1,x2), p);
+  y = Fp_sub(mulii(p1,subii(x1,x)), y1, p);
+  affii(x, x1);
+  affii(y, y1);
+}
+
+/* make sure *x has lgefint >= k */
+static void
+_fix(GEN x, long k)
+{
+  GEN y = (GEN)*x;
+  if (lgefint(y) < k) { GEN p1 = cgeti(k); affii(y,p1); *x = (long)p1; }
+}
+
+/* Return the lift of a (mod b), which is closest to h */
+static GEN
+closest_lift(GEN a, GEN b, GEN h)
+{
+  return addii(a, mulii(b, diviiround(subii(h,a), b)));
+}
+
+static long
+get_table_size(GEN pordmin, GEN B)
+{
+  pari_sp av = avma;
+  GEN t = ceilr( sqrtr( divri(itor(pordmin, DEFAULTPREC), B) ) );
+  if (is_bigint(t))
+    pari_err_OVERFLOW("ellap [large prime: install the 'seadata' package]");
+  avma = av;
+  return itos(t) >> 1;
+}
+
+/* compute a_p using Shanks/Mestre + Montgomery's trick. Assume p > 457 */
+static GEN
+Fp_ellcard_Shanks(GEN c4, GEN c6, GEN p)
+{
+  pari_timer T;
+  long *tx, *ty, *ti, pfinal, i, j, s, KRO, KROold, nb;
+  ulong x;
+  pari_sp av = avma, av2;
+  GEN p1, P, h, mfh, F,f, fh,fg, pordmin, u, v, p1p, p2p, A, B, a4, pts;
+  tx = NULL;
+  ty = ti = NULL; /* gcc -Wall */
+
+  if (DEBUGLEVEL) timer_start(&T);
+  /* once #E(Fp) is know mod B >= pordmin, it is completely determined */
+  pordmin = addis(sqrti(gmul2n(p,4)), 1); /* ceil( 4sqrt(p) ) */
+  p1p = addsi(1, p);
+  p2p = shifti(p1p, 1);
+  x = 0; u = c6; KRO = kronecker(u, p); KROold = - KRO;
+  /* how many 2-torsion points ? */
+  switch(FpX_nbroots(mkpoln(4, gen_1, gen_0, c4, c6), p))
+  {
+    case 3:  A = gen_0; B = utoipos(4); break;
+    case 1:  A = gen_0; B = gen_2; break;
+    default: A = gen_1; B = gen_2; break; /* 0 */
+  }
+  h = closest_lift(A, B, p1p);
+  for(;;)
+  {
+    long CODE;
+    while (!KRO || KRO == KROold)
+    { /* look for points alternatively on E and its quadratic twist E' */
+      x++; /* u = x^3 + c4 x + c6 */
+      u = modii(addii(c6, mului(x, addii(c4, sqru(x)))), p);
+      KRO = kronecker(u, p);
+    }
+    KROold = KRO;
+    /* [ux, u^2] is on E_u: y^2 = x^3 + c4 u^2 x + c6 u^3
+     * E_u isomorphic to E (resp. E') iff KRO = 1 (resp. -1)
+     * #E(F_p) = p+1 - a_p, #E'(F_p) = p+1 + a_p
+     *
+     * #E_u(Fp) = A (mod B),  h is close to #E_u(Fp) */
+
+    f = cgetg(3,t_VEC);
+    gel(f,1) = modii(mului(x,u), p);
+    gel(f,2) = modii(sqri(u),    p);
+    a4 = modii(mulii(c4, gel(f,2)), p); /* c4 for E_u */
+    fh = FpE_mul(f, h, a4, p);
+    if (ell_is_inf(fh)) goto FOUND;
+
+    s = get_table_size(pordmin, B);
+    CODE = evaltyp(t_VECSMALL) | evallg(s+1);
+    /* look for h s.t f^h = 0 */
+    if (!tx)
+    { /* first time: initialize */
+      tx = newblock(3*(s+1));
+      ty = tx + (s+1);
+      ti = ty + (s+1);
+    }
+    F = FpE_mul(f,B,a4,p);
+    *tx = CODE;
+
+    /* F = B.f */
+    P = gcopy(fh);
+    if (s < 3)
+    { /* we're nearly done: naive search */
+      GEN q1 = P, mF = FpE_neg(F, p); /* -F */
+      for (i=1;; i++)
+      {
+        P = FpE_add(P,F,a4,p); /* h.f + i.F */
+        if (ell_is_inf(P)) { h = addii(h, mului(i,B)); goto FOUND; }
+        q1 = FpE_add(q1,mF,a4,p); /* h.f - i.F */
+        if (ell_is_inf(q1)) { h = subii(h, mului(i,B)); goto FOUND; }
+      }
+    }
+    /* Baby Step/Giant Step */
+    nb = minss(128, s >> 1); /* > 0. Will do nb pts at a time: faster inverse */
+    pts = cgetg(nb+1, t_VEC);
+    j = lgefint(p);
+    for (i=1; i<=nb; i++)
+    { /* baby steps */
+      gel(pts,i) = P; /* h.f + (i-1).F */
+      _fix(P+1, j); tx[i] = mod2BIL(gel(P,1));
+      _fix(P+2, j); ty[i] = mod2BIL(gel(P,2));
+      P = FpE_add(P,F,a4,p); /* h.f + i.F */
+      if (ell_is_inf(P)) { h = addii(h, mului(i,B)); goto FOUND; }
+    }
+    mfh = FpE_neg(fh, p);
+    fg = FpE_add(P,mfh,a4,p); /* h.f + nb.F - h.f = nb.F */
+    if (ell_is_inf(fg)) { h = mului(nb,B); goto FOUND; }
+    u = cgetg(nb+1, t_VEC);
+    av2 = avma; /* more baby steps, nb points at a time */
+    while (i <= s)
+    {
+      long maxj;
+      for (j=1; j<=nb; j++) /* adding nb.F (part 1) */
+      {
+        P = gel(pts,j); /* h.f + (i-nb-1+j-1).F */
+        gel(u,j) = subii(gel(fg,1), gel(P,1));
+        if (!signe(gel(u,j))) /* sum = 0 or doubling */
+        {
+          long k = i+j-2;
+          if (equalii(gel(P,2),gel(fg,2))) k -= 2*nb; /* fg == P */
+          h = addii(h, mulsi(k,B)); goto FOUND;
+        }
+      }
+      v = FpV_inv(u, p);
+      maxj = (i-1 + nb <= s)? nb: s % nb;
+      for (j=1; j<=maxj; j++,i++) /* adding nb.F (part 2) */
+      {
+        P = gel(pts,j);
+        FpE_add_ip(P,fg, a4,p, gel(v,j));
+        tx[i] = mod2BIL(gel(P,1));
+        ty[i] = mod2BIL(gel(P,2));
+      }
+      avma = av2;
+    }
+    P = FpE_add(gel(pts,j-1),mfh,a4,p); /* = (s-1).F */
+    if (ell_is_inf(P)) { h = mului(s-1,B); goto FOUND; }
+    if (DEBUGLEVEL) timer_printf(&T, "[Fp_ellcard_Shanks] baby steps, s = %ld",s);
+
+    /* giant steps: fg = s.F */
+    fg = FpE_add(P,F,a4,p);
+    if (ell_is_inf(fg)) { h = mului(s,B); goto FOUND; }
+    pfinal = mod2BIL(p); av2 = avma;
+    /* Goal of the following: sort points by increasing x-coordinate hash.
+     * Done in a complicated way to avoid allocating a large temp vector */
+    p1 = vecsmall_indexsort(tx); /* = permutation sorting tx */
+    for (i=1; i<=s; i++) ti[i] = tx[p1[i]];
+    /* ti = tx sorted */
+    for (i=1; i<=s; i++) { tx[i] = ti[i]; ti[i] = ty[p1[i]]; }
+    /* tx is sorted. ti = ty sorted */
+    for (i=1; i<=s; i++) { ty[i] = ti[i]; ti[i] = p1[i]; }
+    /* ty is sorted. ti = permutation sorting tx */
+    if (DEBUGLEVEL) timer_printf(&T, "[Fp_ellcard_Shanks] sorting");
+    avma = av2;
+
+    gaffect(fg, gel(pts,1));
+    for (j=2; j<=nb; j++) /* pts[j] = j.fg = (s*j).F */
+    {
+      P = FpE_add(gel(pts,j-1),fg,a4,p);
+      if (ell_is_inf(P)) { h = mulii(mulss(s,j), B); goto FOUND; }
+      gaffect(P, gel(pts,j));
+    }
+    /* replace fg by nb.fg since we do nb points at a time */
+    avma = av2;
+    fg = gcopy(gel(pts,nb)); /* copy: we modify (temporarily) pts[nb] below */
+    av2 = avma;
+
+    for (i=1,j=1; ; i++)
+    {
+      GEN ftest = gel(pts,j);
+      long m, l = 1, r = s+1;
+      long k, k2, j2;
+
+      avma = av2;
+      k = mod2BIL(gel(ftest,1));
+      while (l < r)
+      {
+        m = (l+r) >> 1;
+        if (tx[m] < k) l = m+1; else r = m;
+      }
+      if (r <= s && tx[r] == k)
+      {
+        while (r && tx[r] == k) r--;
+        k2 = mod2BIL(gel(ftest,2));
+        for (r++; r <= s && tx[r] == k; r++)
+          if (ty[r] == k2 || ty[r] == pfinal - k2)
+          { /* [h+j2] f == +/- ftest (= [i.s] f)? */
+            j2 = ti[r] - 1;
+            if (DEBUGLEVEL) timer_printf(&T, "[Fp_ellcard_Shanks] giant steps, i = %ld",i);
+            P = FpE_add(FpE_mul(F,stoi(j2),a4,p),fh,a4,p);
+            if (equalii(gel(P,1), gel(ftest,1)))
+            {
+              if (equalii(gel(P,2), gel(ftest,2))) i = -i;
+              h = addii(h, mulii(addis(mulss(s,i), j2), B));
+              goto FOUND;
+            }
+          }
+      }
+      if (++j > nb)
+      { /* compute next nb points */
+        long save = 0; /* gcc -Wall */;
+        for (j=1; j<=nb; j++)
+        {
+          P = gel(pts,j);
+          gel(u,j) = subii(gel(fg,1), gel(P,1));
+          if (gel(u,j) == gen_0) /* occurs once: i = j = nb, P == fg */
+          {
+            gel(u,j) = shifti(gel(P,2),1);
+            save = fg[1]; fg[1] = P[1];
+          }
+        }
+        v = FpV_inv(u, p);
+        for (j=1; j<=nb; j++)
+          FpE_add_ip(gel(pts,j),fg,a4,p, gel(v,j));
+        if (i == nb) { fg[1] = save; }
+        j = 1;
+      }
+    }
+FOUND: /* found a point of exponent h on E_u */
+    h = FpE_order(f, h, a4, p);
+    /* h | #E_u(Fp) = A (mod B) */
+    if (B == gen_1)
+      B = h;
+    else
+      A = Z_chinese_all(A, gen_0, B, h, &B);
+
+    i = (cmpii(B, pordmin) < 0);
+    /* If we are not done, update A mod B for the _next_ curve, isomorphic to
+     * the quadratic twist of this one */
+    if (i) A = remii(subii(p2p,A), B); /* #E(Fp)+#E'(Fp) = 2p+2 */
+
+    /* h = A mod B, closest lift to p+1 */
+    h = closest_lift(A, B, p1p);
+    if (!i) break;
+  }
+  if (tx) killblock(tx);
+  return gerepileuptoint(av, KRO==1? h: subii(shifti(p1p,1),h));
+}
+
+typedef struct
+{
+  ulong x,y,i;
+} multiple;
+
+static int
+compare_multiples(multiple *a, multiple *b) { return a->x > b->x? 1:a->x<b->x?-1:0; }
+
+static long
+sclosest_lift(long A, long B, ulong p2p)
+{
+  return A + B * (((ulong)(p2p + B - (A << 1))) / (B << 1));
+}
+
+/* assume p > 99 and e has good reduction at p. Should use Montgomery.
+ * See Fp_ellcard_Shanks() */
+static long
+Fl_ellcard_Shanks(ulong c4, ulong c6, ulong p)
+{
+  GEN f, fh, fg, ftest, F;
+  ulong x, u, cp4, p1p, p2p, h;
+  long pordmin,A,B;
+  long i, s, KRO, KROold, l, r, m;
+  pari_sp av = avma;
+  multiple *table;
+
+  pordmin = (long)(1 + 4*sqrt((double)p));
+  p1p = p+1;
+  p2p = p1p << 1;
+  x = 0; u = c6; KRO = krouu(u, p); KROold = -KRO;
+
+  switch(Flx_nbroots(mkvecsmalln(5,0, c6,c4,0,1), p))
+  {
+    case 3:  A = 0; B = 4; break;
+    case 1:  A = 0; B = 2; break;
+    default: A = 1; B = 2; break; /* 0 */
+  }
+  h = sclosest_lift(A, B, p2p);
+  for(;;)
+  {
+    while (!KRO || KRO == KROold)
+    {
+      ulong t;
+      if (++x >= p) pari_err_PRIME("ellap",utoi(p));
+      t = Fl_add(c4, Fl_mul(x,x,p), p);
+      u = Fl_add(c6, Fl_mul(x, t, p), p);
+      KRO = krouu(u,p);
+    }
+    KROold = KRO;
+    f = mkvecsmall2(Fl_mul(x, u, p), Fl_mul(u, u, p));
+    cp4 = Fl_mul(c4, f[2], p);
+    fh = Fle_mulu(f, h, cp4, p);
+    s = (long) (sqrt(((double)pordmin)/B) / 2);
+    if (!s) s = 1;
+    table = (multiple *) stack_malloc((s+1) * sizeof(multiple));
+    F = Fle_mulu(f, B, cp4, p);
+    if (ell_is_inf(fh)) goto FOUND;
+    for (i=0; i < s; i++)
+    {
+      table[i].x = fh[1];
+      table[i].y = fh[2];
+      table[i].i = i;
+      if (Fle_add_inplace(fh, F, cp4, p)) { h += B*(i+1); goto FOUND; }
+    }
+    qsort(table,s,sizeof(multiple),(QSCOMP)compare_multiples);
+    fg = Fle_mulu(F, s, cp4, p); ftest = zv_copy(fg);
+    if (ell_is_inf(ftest)) {
+      if (!uisprime(p)) pari_err_PRIME("ellap",utoi(p));
+      pari_err_BUG("ellap (f^(i*s) = 1)");
+    }
+    for (i=1; ; i++)
+    {
+      l=0; r=s;
+      while (l<r)
+      {
+        m = (l+r) >> 1;
+        if (table[m].x < (ulong) ftest[1]) l=m+1; else r=m;
+      }
+      if (r < s && table[r].x == (ulong) ftest[1]) break;
+      if (Fle_add_inplace(ftest, fg, cp4, p))
+        pari_err_PRIME("ellap",utoi(p));
+    }
+    h += table[r].i * B;
+    if (table[r].y == (ulong) ftest[2]) i = -i;
+    h += s * i * B;
+
+FOUND:
+    h = itou(Fle_order(f, utoi(h), cp4, p));
+    if (B == 1) B = h;
+    else
+    {
+      GEN C;
+      A = itos( Z_chinese_all(gen_0, modss(A,B), utoipos(h), utoipos(B), &C) );
+      if (is_bigint(C)) { h = A; break; }
+      B = itos(C);
+    }
+
+    i = (B < pordmin);
+    if (i)
+    {
+      A = (p2p - A) % B;
+      if ((A << 1) > B) A -= B;
+    }
+    /* h = A mod B, closest lift to p+1 */
+    h = sclosest_lift(A, B, p2p);
+    avma = av; if (!i) break;
+  }
+  return KRO==1? h: 2*p1p-h;
+}
+
+/** ellap from CM (original code contributed by Mark Watkins) **/
+
+static ulong
+Mod16(GEN x) {
+  long s = signe(x);
+  ulong m;
+  if (!s) return 0;
+  m = mod16(x); if (!m) return m;
+  if (s < 0) m = 16 - m;
+  return m;
+}
+#define Mod2(x) (Mod16(x) & 1)
+#define Mod4(x) (Mod16(x) & 3)
+#define Mod8(x) (Mod16(x) & 7)
+
+static GEN
+ap_j0(GEN a6,GEN p)
+{
+  GEN a, b, e, d;
+  if (umodiu(p,3) != 1) return gen_0;
+  (void)cornacchia2(utoipos(27),p, &a,&b);
+  if (umodiu(a, 3) == 1) a = negi(a);
+  d = mulis(a6,-108);
+  e = diviuexact(shifti(p,-1), 3); /* (p-1) / 6 */
+  return centermod(mulii(a, Fp_pow(d, e, p)), p);
+}
+static GEN
+ap_j1728(GEN a4,GEN p)
+{
+  GEN a, b, e;
+  if (mod4(p) != 1) return gen_0;
+  (void)cornacchia2(utoipos(4),p, &a,&b);
+  if (Mod4(a)==0) a = b;
+  if (Mod2(a)==1) a = shifti(a,1);
+  if (Mod8(a)==6) a = negi(a);
+  e = shifti(p,-2); /* (p-1) / 4 */
+  return centermod(mulii(a, Fp_pow(a4, e, p)), p);
+}
+static GEN
+ap_j8000(GEN a6, GEN p)
+{
+  GEN a, b;
+  long r = mod8(p), s = 1;
+  if (r != 1 && r != 3) return gen_0;
+  (void)cornacchia2(utoipos(8),p, &a,&b);
+  switch(Mod16(a)) {
+    case 2: case 6:   if (Mod4(b)) s = -s;
+      break;
+    case 10: case 14: if (!Mod4(b)) s = -s;
+      break;
+  }
+  if (kronecker(mulis(a6, 42), p) < 0) s = -s;
+  return s > 0? a: negi(a);
+}
+static GEN
+ap_j287496(GEN a6, GEN p)
+{
+  GEN a, b;
+  long s = 1;
+  if (mod4(p) != 1) return gen_0;
+  (void)cornacchia2(utoipos(4),p, &a,&b);
+  if (Mod4(a)==0) a = b;
+  if (Mod2(a)==1) a = shifti(a,1);
+  if (Mod8(a)==6) s = -s;
+  if (krosi(2,p) < 0) s = -s;
+  if (kronecker(mulis(a6, -14), p) < 0) s = -s;
+  return s > 0? a: negi(a);
+}
+static GEN
+ap_cm(int CM, long A6B, GEN a6, GEN p)
+{
+  GEN a, b;
+  long s = 1;
+  if (krosi(CM,p) < 0) return gen_0;
+  (void)cornacchia2(utoipos(-CM),p, &a, &b);
+  if ((CM&3) == 0) CM >>= 2;
+  if ((krois(a, -CM) > 0) ^ (CM == -7)) s = -s;
+  if (kronecker(mulis(a6,A6B), p) < 0) s = -s;
+  return s > 0? a: negi(a);
+}
+/* is jn/jd = J (mod p) */
+static int
+is_CMj(long J, GEN jn, GEN jd, GEN p)
+{ return remii(subii(mulis(jd,J), jn), p) == gen_0; }
+#ifndef LONG_IS_64BIT
+/* is jn/jd = -(2^32 a + b) (mod p) */
+static int
+u2_is_CMj(ulong a, ulong b, GEN jn, GEN jd, GEN p)
+{
+  GEN mJ = uu32toi(a,b);
+  return remii(addii(mulii(jd,mJ), jn), p) == gen_0;
+}
+#endif
+static GEN
+ec_ap_cm(int CM, GEN a4, GEN a6, GEN p)
+{
+  switch(CM)
+  {
+    case  -3: return ap_j0(a6, p);
+    case  -4: return ap_j1728(a4, p);
+    case  -8: return ap_j8000(a6, p);
+    case -16: return ap_j287496(a6, p);
+    case  -7: return ap_cm(CM, -2, a6, p);
+    case -11: return ap_cm(CM, 21, a6, p);
+    case -12: return ap_cm(CM, 22, a6, p);
+    case -19: return ap_cm(CM, 1, a6, p);
+    case -27: return ap_cm(CM, 253, a6, p);
+    case -28: return ap_cm(-7, -114, a6, p); /* yes, -7 ! */
+    case -43: return ap_cm(CM, 21, a6, p);
+    case -67: return ap_cm(CM, 217, a6, p);
+    case -163:return ap_cm(CM, 185801, a6, p);
+    default: return NULL;
+  }
+}
+long
+Fl_elltrace_CM(int CM, ulong a4, ulong a6, ulong p)
+{
+  pari_sp av = avma;
+  GEN a;
+  if (p < 127) return p+1-Fl_ellcard_naive(a4, a6, p);
+  a = ec_ap_cm(CM, utoi(a4), utoi(a6), utoipos(p));
+  avma = av; return itos(a);
+}
+static GEN
+CM_ellap(GEN a4, GEN a6, GEN jn, GEN jd, GEN p)
+{
+#define CHECK(CM,J) if (is_CMj(J,jn,jd,p)) return ec_ap_cm(CM,a4,a6,p);
+  if (!signe(a4)) return ap_j0(a6,p);
+  if (!signe(a6)) return ap_j1728(a4,p);
+  CHECK(-7,  -3375);
+  CHECK(-8,  8000);
+  CHECK(-12, 54000);
+  CHECK(-11, -32768);
+  CHECK(-16, 287496);
+  CHECK(-19, -884736);
+  CHECK(-27, -12288000);
+  CHECK(-28, 16581375);
+  CHECK(-43, -884736000);
+#ifdef LONG_IS_64BIT
+  CHECK(-67, -147197952000);
+  CHECK(-163, -262537412640768000);
+#else
+  if (u2_is_CMj(0x00000022UL,0x45ae8000UL,jn,jd,p)) return ec_ap_cm(-67,a4,a6,p);
+  if (u2_is_CMj(0x03a4b862UL,0xc4b40000UL,jn,jd,p)) return ec_ap_cm(-163,a4,a6,p);
+#endif
+#undef CHECK
+  return NULL;
+}
+
+static GEN
+Fp_ellj_nodiv(GEN a4, GEN a6, GEN p)
+{
+  GEN a43 = Fp_mulu(Fp_powu(a4, 3, p), 4, p);
+  GEN a62 = Fp_mulu(Fp_sqr(a6, p), 27, p);
+  return mkvec2(Fp_mulu(a43, 1728, p), Fp_add(a43, a62, p));
+}
+
+GEN
+Fp_ellj(GEN a4, GEN a6, GEN p)
+{
+  pari_sp av=avma;
+  GEN z = Fp_ellj_nodiv(a4, a6, p);
+  return gerepileuptoint(av,Fp_div(gel(z,1),gel(z,2),p));
+}
+
+static GEN /* Only compute a mod p, so assume p>=17 */
+Fp_ellcard_CM(GEN a4, GEN a6, GEN p)
+{
+  pari_sp  av = avma;
+  GEN j = Fp_ellj_nodiv(a4, a6, p);
+  GEN a = CM_ellap(a4, a6, gel(j,1), gel(j,2), p);
+  return a ? gerepileupto(av, subii(addis(p,1),a)): NULL;
+}
+
+GEN
+Fp_ellcard(GEN a4, GEN a6, GEN p)
+{
+  long lp = expi(p);
+  ulong pp = p[2];
+  if (lp < 7)
+    return utoi(Fl_ellcard_naive(umodiu(a4,pp), umodiu(a6,pp), pp));
+  { GEN a = Fp_ellcard_CM(a4,a6,p); if (a) return a; }
+  if (lp >= 56)
+  { GEN a = Fp_ellcard_SEA(a4, a6, p, 0); if (a) return a; }
+  if (lp <= BITS_IN_LONG-3)
+    return utoi(Fl_ellcard_Shanks(umodiu(a4,pp), umodiu(a6,pp), pp));
+  if (lp >= 90) pari_err_PACKAGE("seadata");
+  return Fp_ellcard_Shanks(a4, a6, p);
+}
+
+long
+Fl_elltrace(ulong a4, ulong a6, ulong p)
+{
+  pari_sp av = avma;
+  long lp = expu(p), t;
+  if (lp < 7)
+    return p+1-Fl_ellcard_naive(a4, a6, p);
+  if (lp <= minss(56, BITS_IN_LONG-2))
+    return p+1-Fl_ellcard_Shanks(a4, a6, p);
+  t = itos(subui(p, Fp_ellcard(utoi(a4), utoi(a6), utoi(p)))) + 1;
+  avma = av; return t;
+}
+
+static GEN
+_FpE_pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)
+{
+  struct _FpE *e = (struct _FpE *) E;
+  return  Fp_order(FpE_weilpairing(P,Q,m,e->a4,e->p), F, e->p);
+}
+
+GEN
+Fp_ellgroup(GEN a4, GEN a6, GEN N, GEN p, GEN *pt_m)
+{
+  struct _FpE e;
+  e.a4=a4; e.a6=a6; e.p=p;
+  return gen_ellgroup(N, subis(p, 1), pt_m, (void*)&e, &FpE_group, _FpE_pairorder);
+}
+
+GEN
+Fp_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN p)
+{
+  GEN P;
+  pari_sp av = avma;
+  struct _FpE e;
+  e.a4=a4; e.a6=a6; e.p=p;
+  switch(lg(D)-1)
+  {
+  case 1:
+    P = gen_gener(gel(D,1), (void*)&e, &FpE_group);
+    P = mkvec(FpE_changepoint(P, ch, p));
+    break;
+  default:
+    P = gen_ellgens(gel(D,1), gel(D,2), m, (void*)&e, &FpE_group, _FpE_pairorder);
+    gel(P,1) = FpE_changepoint(gel(P,1), ch, p);
+    gel(P,2) = FpE_changepoint(gel(P,2), ch, p);
+    break;
+  }
+  return gerepilecopy(av, P);
+}
+
+/* Not so fast arithmetic with points over elliptic curves over FpXQ */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              FpXQE                                  **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Theses functions deal with point over elliptic curves over FpXQ defined
+ * by an equation of the form y^2=x^3+a4*x+a6.
+ * Most of the time a6 is omitted since it can be recovered from any point
+ * on the curve.
+ */
+
+GEN
+RgE_to_FpXQE(GEN x, GEN T, GEN p)
+{
+  if (ell_is_inf(x)) return x;
+  retmkvec2(Rg_to_FpXQ(gel(x,1),T,p),Rg_to_FpXQ(gel(x,2),T,p));
+}
+
+GEN
+FpXQE_changepoint(GEN x, GEN ch, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN p1,z,u,r,s,t,v,v2,v3;
+  if (ell_is_inf(x)) return x;
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  v = FpXQ_inv(u, T, p); v2 = FpXQ_sqr(v, T, p); v3 = FpXQ_mul(v,v2, T, p);
+  p1 = FpX_sub(gel(x,1),r, p);
+  z = cgetg(3,t_VEC);
+  gel(z,1) = FpXQ_mul(v2, p1, T, p);
+  gel(z,2) = FpXQ_mul(v3, FpX_sub(gel(x,2), FpX_add(FpXQ_mul(s,p1, T, p),t, p), p), T, p);
+  return gerepileupto(av, z);
+}
+
+GEN
+FpXQE_changepointinv(GEN x, GEN ch, GEN T, GEN p)
+{
+  GEN u, r, s, t, X, Y, u2, u3, u2X, z;
+  if (ell_is_inf(x)) return x;
+  X = gel(x,1); Y = gel(x,2);
+  u = gel(ch,1); r = gel(ch,2);
+  s = gel(ch,3); t = gel(ch,4);
+  u2 = FpXQ_sqr(u, T, p); u3 = FpXQ_mul(u,u2, T, p);
+  u2X = FpXQ_mul(u2,X, T, p);
+  z = cgetg(3, t_VEC);
+  gel(z,1) = FpX_add(u2X,r, p);
+  gel(z,2) = FpX_add(FpXQ_mul(u3,Y, T, p), FpX_add(FpXQ_mul(s,u2X, T, p), t, p), p);
+  return z;
+}
+
+static GEN
+FpXQE_dbl_slope(GEN P, GEN a4, GEN T, GEN p, GEN *slope)
+{
+  GEN x, y, Q;
+  if (ell_is_inf(P) || !signe(gel(P,2))) return ellinf();
+  x = gel(P,1); y = gel(P,2);
+  *slope = FpXQ_div(FpX_add(FpX_mulu(FpXQ_sqr(x, T, p), 3, p), a4, p),
+                            FpX_mulu(y, 2, p), T, p);
+  Q = cgetg(3,t_VEC);
+  gel(Q, 1) = FpX_sub(FpXQ_sqr(*slope, T, p), FpX_mulu(x, 2, p), p);
+  gel(Q, 2) = FpX_sub(FpXQ_mul(*slope, FpX_sub(x, gel(Q, 1), p), T, p), y, p);
+  return Q;
+}
+
+GEN
+FpXQE_dbl(GEN P, GEN a4, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FpXQE_dbl_slope(P,a4,T,p,&slope));
+}
+
+static GEN
+FpXQE_add_slope(GEN P, GEN Q, GEN a4, GEN T, GEN p, GEN *slope)
+{
+  GEN Px, Py, Qx, Qy, R;
+  if (ell_is_inf(P)) return Q;
+  if (ell_is_inf(Q)) return P;
+  Px = gel(P,1); Py = gel(P,2);
+  Qx = gel(Q,1); Qy = gel(Q,2);
+  if (ZX_equal(Px, Qx))
+  {
+    if (ZX_equal(Py, Qy))
+      return FpXQE_dbl_slope(P, a4, T, p, slope);
+    else
+      return ellinf();
+  }
+  *slope = FpXQ_div(FpX_sub(Py, Qy, p), FpX_sub(Px, Qx, p), T, p);
+  R = cgetg(3,t_VEC);
+  gel(R, 1) = FpX_sub(FpX_sub(FpXQ_sqr(*slope, T, p), Px, p), Qx, p);
+  gel(R, 2) = FpX_sub(FpXQ_mul(*slope, FpX_sub(Px, gel(R, 1), p), T, p), Py, p);
+  return R;
+}
+
+GEN
+FpXQE_add(GEN P, GEN Q, GEN a4, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FpXQE_add_slope(P,Q,a4,T,p,&slope));
+}
+
+static GEN
+FpXQE_neg_i(GEN P, GEN p)
+{
+  if (ell_is_inf(P)) return P;
+  return mkvec2(gel(P,1), FpX_neg(gel(P,2), p));
+}
+
+GEN
+FpXQE_neg(GEN P, GEN T, GEN p)
+{
+  (void) T;
+  if (ell_is_inf(P)) return ellinf();
+  return mkvec2(gcopy(gel(P,1)), FpX_neg(gel(P,2), p));
+}
+
+GEN
+FpXQE_sub(GEN P, GEN Q, GEN a4, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN slope;
+  return gerepileupto(av, FpXQE_add_slope(P, FpXQE_neg_i(Q, p), a4, T, p, &slope));
+}
+
+struct _FpXQE
+{
+  GEN a4,a6;
+  GEN T,p;
+};
+
+static GEN
+_FpXQE_dbl(void *E, GEN P)
+{
+  struct _FpXQE *ell = (struct _FpXQE *) E;
+  return FpXQE_dbl(P, ell->a4, ell->T, ell->p);
+}
+
+static GEN
+_FpXQE_add(void *E, GEN P, GEN Q)
+{
+  struct _FpXQE *ell=(struct _FpXQE *) E;
+  return FpXQE_add(P, Q, ell->a4, ell->T, ell->p);
+}
+
+static GEN
+_FpXQE_mul(void *E, GEN P, GEN n)
+{
+  pari_sp av = avma;
+  struct _FpXQE *e=(struct _FpXQE *) E;
+  long s = signe(n);
+  if (!s || ell_is_inf(P)) return ellinf();
+  if (s<0) P = FpXQE_neg(P, e->T, e->p);
+  if (is_pm1(n)) return s>0? gcopy(P): P;
+  return gerepileupto(av, gen_pow(P, n, e, &_FpXQE_dbl, &_FpXQE_add));
+}
+
+GEN
+FpXQE_mul(GEN P, GEN n, GEN a4, GEN T, GEN p)
+{
+  struct _FpXQE E;
+  E.a4= a4; E.T = T; E.p = p;
+  return _FpXQE_mul(&E, P, n);
+}
+
+/* Finds a random non-singular point on E */
+
+GEN
+random_FpXQE(GEN a4, GEN a6, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN x, x2, y, rhs;
+  long v = get_FpX_var(T), d = get_FpX_degree(T);
+  do
+  {
+    avma= ltop;
+    x   = random_FpX(d,v,p); /*  x^3+a4*x+a6 = x*(x^2+a4)+a6  */
+    x2  = FpXQ_sqr(x, T, p);
+    rhs = FpX_add(FpXQ_mul(x, FpX_add(x2, a4, p), T, p), a6, p);
+  } while ((!signe(rhs) && !signe(FpX_add(FpX_mulu(x2,3,p), a4, p)))
+          || !FpXQ_issquare(rhs, T, p));
+  y = FpXQ_sqrt(rhs, T, p);
+  if (!y) pari_err_PRIME("random_FpE", p);
+  return gerepilecopy(ltop, mkvec2(x, y));
+}
+
+static GEN
+_FpXQE_rand(void *E)
+{
+  struct _FpXQE *e=(struct _FpXQE *) E;
+  return random_FpXQE(e->a4, e->a6, e->T, e->p);
+}
+
+static const struct bb_group FpXQE_group={_FpXQE_add,_FpXQE_mul,_FpXQE_rand,hash_GEN,ZXV_equal,ell_is_inf};
+
+const struct bb_group *
+get_FpXQE_group(void ** pt_E, GEN a4, GEN a6, GEN T, GEN p)
+{
+  struct _FpXQE *e = (struct _FpXQE *) stack_malloc(sizeof(struct _FpXQE));
+  e->a4 = a4; e->a6 = a6; e->T = T; e->p = p;
+  *pt_E = (void *) e;
+  return &FpXQE_group;
+}
+
+GEN
+FpXQE_order(GEN z, GEN o, GEN a4, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  struct _FpXQE e;
+  e.a4=a4; e.T=T; e.p=p;
+  return gerepileuptoint(av, gen_order(z, o, (void*)&e, &FpXQE_group));
+}
+
+GEN
+FpXQE_log(GEN a, GEN b, GEN o, GEN a4, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  struct _FpXQE e;
+  e.a4=a4; e.T=T; e.p=p;
+  return gerepileuptoint(av, gen_PH_log(a, b, o, (void*)&e, &FpXQE_group));
+}
+
+
+/***********************************************************************/
+/**                                                                   **/
+/**                            Pairings                               **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Derived from APIP from and by Jerome Milan, 2012 */
+
+static GEN
+FpXQE_vert(GEN P, GEN Q, GEN T, GEN p)
+{
+  if (ell_is_inf(P))
+    return pol_1(get_FpX_var(T));
+  return FpX_sub(gel(Q, 1), gel(P, 1), p);
+}
+
+/* Computes the equation of the line tangent to R and returns its
+   evaluation at the point Q. Also doubles the point R.
+ */
+
+static GEN
+FpXQE_tangent_update(GEN R, GEN Q, GEN a4, GEN T, GEN p, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = ellinf();
+    return pol_1(get_FpX_var(T));
+  }
+  else if (!signe(gel(R,2)))
+  {
+    *pt_R = ellinf();
+    return FpXQE_vert(R, Q, T, p);
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = FpXQE_dbl_slope(R, a4, T, p, &slope);
+    tmp1 = FpX_sub(gel(Q, 1), gel(R, 1), p);
+    tmp2 = FpX_add(FpXQ_mul(tmp1, slope, T, p), gel(R,2), p);
+    return FpX_sub(gel(Q, 2), tmp2, p);
+  }
+}
+
+/* Computes the equation of the line through R and P, and returns its
+   evaluation at the point Q. Also adds P to the point R.
+ */
+
+static GEN
+FpXQE_chord_update(GEN R, GEN P, GEN Q, GEN a4, GEN T, GEN p, GEN *pt_R)
+{
+  if (ell_is_inf(R))
+  {
+    *pt_R = gcopy(P);
+    return FpXQE_vert(P, Q, T, p);
+  }
+  else if (ell_is_inf(P))
+  {
+    *pt_R = gcopy(R);
+    return FpXQE_vert(R, Q, T, p);
+  }
+  else if (ZX_equal(gel(P, 1), gel(R, 1)))
+  {
+    if (ZX_equal(gel(P, 2), gel(R, 2)))
+      return FpXQE_tangent_update(R, Q, a4, T, p, pt_R);
+    else
+    {
+      *pt_R = ellinf();
+      return FpXQE_vert(R, Q, T, p);
+    }
+  } else {
+    GEN slope, tmp1, tmp2;
+    *pt_R = FpXQE_add_slope(P, R, a4, T, p, &slope);
+    tmp1  = FpXQ_mul(FpX_sub(gel(Q, 1), gel(R, 1), p), slope, T, p);
+    tmp2  = FpX_add(tmp1, gel(R, 2), p);
+    return FpX_sub(gel(Q, 2), tmp2, p);
+  }
+}
+
+/* Returns the Miller function f_{m, Q} evaluated at the point P using
+   the standard Miller algorithm.
+ */
+
+struct _FpXQE_miller
+{
+  GEN p;
+  GEN T, a4, P;
+};
+
+static GEN
+FpXQE_Miller_dbl(void* E, GEN d)
+{
+  struct _FpXQE_miller *m = (struct _FpXQE_miller *)E;
+  GEN p  = m->p;
+  GEN T = m->T, a4 = m->a4, P = m->P;
+  GEN v, line;
+  GEN num = FpXQ_sqr(gel(d,1), T, p);
+  GEN denom = FpXQ_sqr(gel(d,2), T, p);
+  GEN point = gel(d,3);
+  line = FpXQE_tangent_update(point, P, a4, T, p, &point);
+  num  = FpXQ_mul(num, line, T, p);
+  v = FpXQE_vert(point, P, T, p);
+  denom = FpXQ_mul(denom, v, T, p);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+FpXQE_Miller_add(void* E, GEN va, GEN vb)
+{
+  struct _FpXQE_miller *m = (struct _FpXQE_miller *)E;
+  GEN p = m->p;
+  GEN T = m->T, a4 = m->a4, P = m->P;
+  GEN v, line, point;
+  GEN na = gel(va,1), da = gel(va,2), pa = gel(va,3);
+  GEN nb = gel(vb,1), db = gel(vb,2), pb = gel(vb,3);
+  GEN num   = FpXQ_mul(na, nb, T, p);
+  GEN denom = FpXQ_mul(da, db, T, p);
+  line = FpXQE_chord_update(pa, pb, P, a4, T, p, &point);
+  num  = FpXQ_mul(num, line, T, p);
+  v = FpXQE_vert(point, P, T, p);
+  denom = FpXQ_mul(denom, v, T, p);
+  return mkvec3(num, denom, point);
+}
+
+static GEN
+FpXQE_Miller(GEN Q, GEN P, GEN m, GEN a4, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  struct _FpXQE_miller d;
+  GEN v, num, denom, g1;
+
+  d.a4 = a4; d.T = T; d.p = p; d.P = P;
+  g1 = pol_1(get_FpX_var(T));
+  v = gen_pow(mkvec3(g1,g1,Q), m, (void*)&d, FpXQE_Miller_dbl, FpXQE_Miller_add);
+  num = gel(v,1); denom = gel(v,2);
+  if (!signe(num) || !signe(denom)) { avma = ltop; return NULL; }
+  return gerepileupto(ltop, FpXQ_div(num, denom, T, p));
+}
+
+GEN
+FpXQE_weilpairing(GEN P, GEN Q, GEN m, GEN a4, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN num, denom, result;
+  if (ell_is_inf(P) || ell_is_inf(Q) || ZXV_equal(P,Q))
+    return pol_1(get_FpX_var(T));
+  num    = FpXQE_Miller(P, Q, m, a4, T, p);
+  if (!num) return pol_1(get_FpX_var(T));
+  denom  = FpXQE_Miller(Q, P, m, a4, T, p);
+  if (!denom) { avma = ltop; return pol_1(get_FpX_var(T)); }
+  result = FpXQ_div(num, denom, T, p);
+  if (mpodd(m))
+    result  = FpX_neg(result, p);
+  return gerepileupto(ltop, result);
+}
+
+GEN
+FpXQE_tatepairing(GEN P, GEN Q, GEN m, GEN a4, GEN T, GEN p)
+{
+  GEN num;
+  if (ell_is_inf(P) || ell_is_inf(Q))
+    return pol_1(get_FpX_var(T));
+  num = FpXQE_Miller(P, Q, m, a4, T, p);
+  return num? num: pol_1(get_FpX_var(T));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                           Point counting                          **/
+/**                                                                   **/
+/***********************************************************************/
+
+GEN
+FpXQ_ellj(GEN a4, GEN a6, GEN T, GEN p)
+{
+  if (equaliu(p,3)) return pol_0(get_FpX_var(T));
+  else
+  {
+    pari_sp av=avma;
+    GEN a43 = FpXQ_mul(a4,FpXQ_sqr(a4,T,p),T,p);
+    GEN a62 = FpXQ_sqr(a6,T,p);
+    GEN num = FpX_mulu(a43,6912,p);
+    GEN den = FpX_add(FpX_mulu(a43,4,p),FpX_mulu(a62,27,p),p);
+    return gerepileuptoleaf(av, FpXQ_div(num, den, T, p));
+  }
+}
+
+GEN
+elltrace_extension(GEN t, long n, GEN q)
+{
+  pari_sp av = avma;
+  GEN v = RgX_to_RgV(RgXQ_powu(pol_x(0), n, mkpoln(3,gen_1,negi(t),q)),2);
+  GEN te = addii(shifti(gel(v,1),1), mulii(t,gel(v,2)));
+  return gerepileuptoint(av, te);
+}
+
+GEN
+Fp_ffellcard(GEN a4, GEN a6, GEN q, long n, GEN p)
+{
+  pari_sp av = avma;
+  GEN ap = subii(addis(p, 1), Fp_ellcard(a4, a6, p));
+  GEN te = elltrace_extension(ap, n, p);
+  return gerepileuptoint(av, subii(addis(q, 1), te));
+}
+
+static GEN
+FpXQ_ellcardj(GEN a4, GEN a6, GEN j, GEN T, GEN q, GEN p, long n)
+{
+  GEN q1 = addis(q,1);
+  if (signe(j)==0)
+  {
+    GEN W, w, t, N;
+    if (umodiu(q,6)!=1) return q1;
+    N = Fp_ffellcard(gen_0,gen_1,q,n,p);
+    t = subii(q1, N);
+    W = FpXQ_pow(a6,diviuexact(shifti(q,-1), 3),T,p);
+    if (degpol(W)>0) /*p=5 mod 6*/
+      return ZX_equal1(FpXQ_powu(W,3,T,p)) ? addii(q1,shifti(t,-1)):
+                                             subii(q1,shifti(t,-1));
+    w = modii(gel(W,2),p);
+    if (equali1(w))  return N;
+    if (equalii(w,subiu(p,1))) return addii(q1,t);
+    else /*p=1 mod 6*/
+    {
+      GEN u = shifti(t,-1), v = sqrtint(diviuexact(subii(q,sqri(u)),3));
+      GEN a = addii(u,v), b = shifti(v,1);
+      if (equali1(Fp_powu(w,3,p)))
+      {
+        if (signe(Fp_add(modii(a,p),Fp_mul(w,modii(b,p),p),p))==0)
+          return subii(q1,subii(shifti(b,1),a));
+        else
+          return addii(q1,addii(a,b));
+      }
+      else
+      {
+        if (signe(Fp_sub(modii(a,p),Fp_mul(w,modii(b,p),p),p))==0)
+          return subii(q1,subii(a,shifti(b,1)));
+        else
+          return subii(q1,addii(a,b));
+      }
+    }
+  } else if (equalii(j,modsi(1728,p)))
+  {
+    GEN w, W, N, t;
+    if (mod4(q)==3) return q1;
+    W = FpXQ_pow(a4,shifti(q,-2),T,p);
+    if (degpol(W)>0) return q1; /*p=3 mod 4*/
+    w = modii(gel(W,2),p);
+    N = Fp_ffellcard(gen_1,gen_0,q,n,p);
+    if (equali1(w)) return N;
+    t = subii(q1, N);
+    if (equalii(w,subiu(p,1))) return addii(q1,t);
+    else /*p=1 mod 4*/
+    {
+      GEN u = shifti(t,-1), v = sqrtint(subii(q,sqri(u)));
+      if (signe(Fp_add(modii(u,p),Fp_mul(w,modii(v,p),p),p))==0)
+        return subii(q1,shifti(v,1));
+      else
+        return addii(q1,shifti(v,1));
+    }
+  } else
+  {
+    GEN g = Fp_div(j, Fp_sub(utoi(1728), j, p), p);
+    GEN l = FpXQ_div(FpX_mulu(a6,3,p),FpX_mulu(a4,2,p),T,p);
+    GEN N = Fp_ffellcard(Fp_mulu(g,3,p),Fp_mulu(g,2,p),q,n,p);
+    if (FpXQ_issquare(l,T,p)) return N;
+    return subii(shifti(q1,1),N);
+  }
+}
+
+GEN
+FpXQ_ellcard(GEN a4, GEN a6, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  long n = get_FpX_degree(T);
+  GEN q = powiu(p, n), r, J;
+  if (degpol(a4)<=0 && degpol(a6)<=0)
+    r = Fp_ffellcard(constant_term(a4),constant_term(a6),q,n,p);
+  else if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    r =  Flxq_ellcard(ZX_to_Flx(a4,pp),ZX_to_Flx(a6,pp),ZX_to_Flx(T,pp),pp);
+  }
+  else if (degpol(J=FpXQ_ellj(a4,a6,T,p))<=0)
+    r = FpXQ_ellcardj(a4,a6,constant_term(J),T,q,p,n);
+  else
+  {
+    r = Fq_ellcard_SEA(a4, a6, q, T, p, 0);
+    if (!r) pari_err_PACKAGE("seadata");
+  }
+  return gerepileuptoint(av, r);
+}
+
+static GEN
+_FpXQE_pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)
+{
+  struct _FpXQE *e = (struct _FpXQE *) E;
+  return  FpXQ_order(FpXQE_weilpairing(P,Q,m,e->a4,e->T,e->p), F, e->T, e->p);
+}
+
+GEN
+FpXQ_ellgroup(GEN a4, GEN a6, GEN N, GEN T, GEN p, GEN *pt_m)
+{
+  struct _FpXQE e;
+  GEN q = powiu(p, get_FpX_degree(T));
+  e.a4=a4; e.a6=a6; e.T=T; e.p=p;
+  return gen_ellgroup(N, subis(q,1), pt_m, (void*)&e, &FpXQE_group, _FpXQE_pairorder);
+}
+
+GEN
+FpXQ_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, GEN p)
+{
+  GEN P;
+  pari_sp av = avma;
+  struct _FpXQE e;
+  e.a4=a4; e.a6=a6; e.T=T; e.p=p;
+  switch(lg(D)-1)
+  {
+  case 1:
+    P = gen_gener(gel(D,1), (void*)&e, &FpXQE_group);
+    P = mkvec(FpXQE_changepoint(P, ch, T, p));
+    break;
+  default:
+    P = gen_ellgens(gel(D,1), gel(D,2), m, (void*)&e, &FpXQE_group, _FpXQE_pairorder);
+    gel(P,1) = FpXQE_changepoint(gel(P,1), ch, T, p);
+    gel(P,2) = FpXQE_changepoint(gel(P,2), ch, T, p);
+    break;
+  }
+  return gerepilecopy(av, P);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                      n-division polynomial                        **/
+/**                                                                   **/
+/***********************************************************************/
+
+struct divpol_red
+{
+  GEN h, T, p;
+};
+
+INLINE GEN
+_red(GEN x, struct divpol_red *r)
+{ return r->h ? FqX_rem(x,r->h,r->T,r->p): gcopy(x); }
+
+INLINE GEN
+_rsqr(GEN x, struct divpol_red *r)
+{
+  GEN h=r->h, T=r->T, p=r->p;
+  return h ? FqXQ_sqr(x,h,T,p): FqX_sqr(x,T,p);
+}
+
+INLINE GEN
+_rmul(GEN x, GEN y, struct divpol_red *r)
+{
+  GEN h=r->h, T=r->T, p=r->p;
+  return h ? FqXQ_mul(x,y,h,T,p): FqX_mul(x,y,T,p);
+}
+
+static GEN divpol(GEN t, GEN a4, GEN a6, GEN r2, long n, struct divpol_red *r);
+
+static GEN
+divpol_f2(GEN t, GEN a4, GEN a6, GEN r2, long n, struct divpol_red *r)
+{
+  if (gmael(t,2,n)) return gmael(t,2,n);
+  if (n<=2) return scalarpol(gen_1,0);
+  gmael(t,2,n) = _rsqr(divpol(t,a4,a6,r2,n,r),r);
+  return gmael(t,2,n);
+}
+
+static GEN
+divpol_ff(GEN t, GEN a4, GEN a6, GEN r2, long n, struct divpol_red *r)
+{
+  if(gmael(t,3,n)) return gmael(t,3,n);
+  if (n<=4) return divpol(t,a4,a6,r2,n,r);
+  gmael(t,3,n) = _rmul(divpol(t,a4,a6,r2,n,r), divpol(t,a4,a6,r2,n-2,r),r);
+  return gmael(t,3,n);
+}
+
+static GEN
+divpol(GEN t, GEN a4, GEN a6, GEN r2, long n, struct divpol_red *r)
+{
+  long m = n/2;
+  GEN res;
+  GEN T=r->T, p=r->p;
+  if (gmael(t,1,n)) return gmael(t,1,n);
+  switch(n)
+  {
+  case 1:
+  case 2:
+    res = scalarpol(gen_1,0);
+    break;
+  case 3:
+    res = _red(mkpoln(5, utoi(3), gen_0, Fq_mulu(a4, 6, T, p),
+          Fq_mulu(a6, 12, T, p), Fq_neg(Fq_sqr(a4, T, p), T, p)), r);
+    break;
+  case 4:
+    {
+      GEN a42 = Fq_sqr(a4, T, p);
+      res = _red(FqX_mulu(mkpoln(7, gen_1, gen_0, Fq_mulu(a4, 5, T, p),
+              Fq_mulu(a6, 20, T, p), Fq_Fp_mul(a42,stoi(-5), T, p),
+              Fq_Fp_mul(Fq_mul(a4, a6, T, p), stoi(-4), T, p),
+              Fq_sub(Fq_Fp_mul(Fq_sqr(a6, T, p), stoi(-8), T, p),
+                Fq_mul(a4,a42, T, p), T, p)), 2, T, p), r);
+    }
+    break;
+  default:
+    if (odd(n))
+      if (odd(m))
+        res = FqX_sub(_rmul(divpol_ff(t,a4,a6,r2,m+2,r),
+              divpol_f2(t,a4,a6,r2,m,r), r),
+            _rmul(r2,  _rmul(divpol_ff(t,a4,a6,r2,m+1,r),
+                divpol_f2(t,a4,a6,r2,m+1,r), r), r), T, p);
+      else
+        res = FqX_sub(_rmul(r2, _rmul(divpol_ff(t,a4,a6,r2,m+2,r),
+                divpol_f2(t,a4,a6,r2,m,r), r), r),
+            _rmul(divpol_ff(t,a4,a6,r2,m+1,r),
+              divpol_f2(t,a4,a6,r2,m+1,r), r), T, p);
+    else
+      res = FqX_sub(_rmul(divpol_ff(t,a4,a6,r2,m+2,r),
+            divpol_f2(t,a4,a6,r2,m-1,r), r),
+          _rmul(divpol_ff(t,a4,a6,r2,m,r),
+            divpol_f2(t,a4,a6,r2,m+1,r), r), T, p);
+  }
+  gmael(t,1,n) = res;
+  return res;
+}
+
+/*Computes the n-division polynomial modulo the polynomial h \in Fq[x] */
+GEN
+Fq_elldivpolmod(GEN a4, GEN a6, long n, GEN h, GEN T, GEN p)
+{
+  struct divpol_red r;
+  pari_sp ltop = avma;
+  GEN t, rhs, r2;
+  if (n <= 2) return scalarpol(gen_1,0);
+  r.h=h; r.T=T;
+  r.p=p;
+  t  = mkvec3(const_vec(n, NULL),const_vec(n, NULL),const_vec(n, NULL));
+  rhs = FqX_mulu(_red(mkpoln(4, gen_1, gen_0, a4, a6), &r), 4, T, p);
+  r2 = _rsqr(rhs,&r);
+  return gerepilecopy(ltop, divpol(t,a4,a6,r2,n,&r));
+}
+
+GEN
+FpXQ_elldivpol(GEN a4, GEN a6, long n, GEN T, GEN p)
+{
+  return Fq_elldivpolmod(a4,a6,n,NULL,T,p);
+}
+
+GEN
+Fp_elldivpol(GEN a4, GEN a6, long n, GEN p)
+{
+  return Fq_elldivpolmod(a4,a6,n,NULL,NULL,p);
+}
diff --git a/src/basemath/FpV.c b/src/basemath/FpV.c
new file mode 100644
index 0000000..abf25d8
--- /dev/null
+++ b/src/basemath/FpV.c
@@ -0,0 +1,1299 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/**                                                                **/
+/**                           REDUCTION                            **/
+/**                                                                **/
+/********************************************************************/
+/* z in Z^n, return lift(Col(z) * Mod(1,p)) */
+GEN
+FpC_red(GEN z, GEN p)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_COL);
+  for (i=1; i<l; i++) gel(x,i) = modii(gel(z,i),p);
+  return x;
+}
+
+/* z in Z^n, return lift(Vec(z) * Mod(1,p)) */
+GEN
+FpV_red(GEN z, GEN p)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = modii(gel(z,i),p);
+  return x;
+}
+GEN
+FpC_center(GEN z, GEN p, GEN pov2)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_COL);
+  for (i=1; i<l; i++) gel(x,i) = Fp_center(gel(z,i),p, pov2);
+  return x;
+}
+
+GEN
+Flv_center(GEN z, ulong p, ulong ps2)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_VECSMALL);
+  for (i=1; i<l; i++) x[i] = Fl_center(z[i],p,ps2);
+  return x;
+}
+
+/* z in Mat m,n(Z), return lift(z * Mod(1,p)) */
+GEN
+FpM_red(GEN z, GEN p)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = FpC_red(gel(z,i), p);
+  return x;
+}
+GEN
+FpM_center(GEN z, GEN p, GEN pov2)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = FpC_center(gel(z,i), p, pov2);
+  return x;
+}
+
+GEN
+Flm_center(GEN z, ulong p, ulong ps2)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = Flv_center(gel(z,i),p,ps2);
+  return x;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           ADD, SUB                             **/
+/**                                                                **/
+/********************************************************************/
+GEN
+FpC_add(GEN x, GEN y, GEN p)
+{
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  for (i = 1; i < lx; i++) gel(z, i) = Fp_add(gel(x, i), gel(y, i), p);
+  return z;
+}
+GEN
+FpV_add(GEN x, GEN y, GEN p)
+{
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_VEC);
+  for (i = 1; i < lx; i++) gel(z, i) = Fp_add(gel(x, i), gel(y, i), p);
+  return z;
+}
+
+GEN
+Flv_add(GEN x, GEN y, ulong p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_VECSMALL);
+  if (p==2)
+    for (i = 1; i < l; i++) z[i] = x[i]^y[i];
+  else
+    for (i = 1; i < l; i++) z[i] = Fl_add(x[i], y[i], p);
+  return z;
+}
+
+void
+Flv_add_inplace(GEN x, GEN y, ulong p)
+{
+  long i, l = lg(x);
+  if (p==2)
+    for (i = 1; i < l; i++) x[i] ^= y[i];
+  else
+    for (i = 1; i < l; i++) x[i] = Fl_add(x[i], y[i], p);
+}
+
+ulong
+Flv_sum(GEN x, ulong p)
+{
+  long i, l = lg(x);
+  ulong s = 0;
+  if (p==2)
+    for (i = 1; i < l; i++) s ^= x[i];
+  else
+    for (i = 1; i < l; i++) s = Fl_add(s, x[i], p);
+  return s;
+}
+
+GEN
+FpC_sub(GEN x, GEN y, GEN p)
+{
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  for (i = 1; i < lx; i++) gel(z, i) = Fp_sub(gel(x, i), gel(y, i), p);
+  return z;
+}
+GEN
+FpV_sub(GEN x, GEN y, GEN p)
+{
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_VEC);
+  for (i = 1; i < lx; i++) gel(z, i) = Fp_sub(gel(x, i), gel(y, i), p);
+  return z;
+}
+
+GEN
+Flv_sub(GEN x, GEN y, ulong p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_VECSMALL);
+  for (i = 1; i < l; i++) z[i] = Fl_sub(x[i], y[i], p);
+  return z;
+}
+
+void
+Flv_sub_inplace(GEN x, GEN y, ulong p)
+{
+  long i, l = lg(x);
+  for (i = 1; i < l; i++) x[i] = Fl_sub(x[i], y[i], p);
+}
+
+GEN
+Flm_Fl_add(GEN x, ulong y, ulong p)
+{
+  long l = lg(x), i, j;
+  GEN z = cgetg(l,t_MAT);
+
+  if (l==1) return z;
+  if (l != lgcols(x)) pari_err_OP( "+", x, utoi(y));
+  for (i=1; i<l; i++)
+  {
+    GEN zi = cgetg(l,t_VECSMALL), xi = gel(x,i);
+    gel(z,i) = zi;
+    for (j=1; j<l; j++) zi[j] = xi[j];
+    zi[i] = Fl_add(zi[i], y, p);
+  }
+  return z;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           MULTIPLICATION                       **/
+/**                                                                **/
+/********************************************************************/
+GEN
+FpC_Fp_mul(GEN x, GEN y, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_COL);
+  for (i=1;i<l;i++) gel(z,i) = Fp_mul(gel(x,i),y,p);
+  return z;
+}
+GEN
+Flc_Fl_mul(GEN x, ulong y, ulong p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_VECSMALL);
+  for (i=1;i<l;i++) z[i] = Fl_mul(x[i], y, p);
+  return z;
+}
+GEN
+Flc_Fl_div(GEN x, ulong y, ulong p)
+{
+  return Flc_Fl_mul(x, Fl_inv(y, p), p);
+}
+void
+Flc_Fl_div_inplace(GEN x, ulong y, ulong p)
+{
+  Flc_Fl_mul_inplace(x, Fl_inv(y, p), p);
+}
+
+/* x *= y */
+void
+Flc_Fl_mul_part_inplace(GEN x, ulong y, ulong p, long l)
+{
+  long i;
+  for (i=1;i<=l;i++) x[i] = Fl_mul(x[i], y, p);
+}
+void
+Flc_Fl_mul_inplace(GEN x, ulong y, ulong p)
+{ Flc_Fl_mul_part_inplace(x, y, p, lg(x)-1); }
+
+/* set y *= x */
+void
+Flm_Fl_mul_inplace(GEN y, ulong x, ulong p)
+{
+  long i, j, m = lgcols(y), l = lg(y);
+  if (HIGHWORD(x | p))
+    for(j=1; j<l; j++)
+      for(i=1; i<m; i++) ucoeff(y,i,j) = Fl_mul(ucoeff(y,i,j), x, p);
+  else
+    for(j=1; j<l; j++)
+      for(i=1; i<m; i++) ucoeff(y,i,j) = (ucoeff(y,i,j) * x) % p;
+}
+/* return x * y */
+GEN
+Flm_Fl_mul(GEN y, ulong x, ulong p)
+{
+  long i, j, m = lgcols(y), l = lg(y);
+  GEN z = cgetg(l, t_MAT);
+  if (HIGHWORD(x | p))
+    for(j=1; j<l; j++) {
+      GEN c = cgetg(m, t_VECSMALL); gel(z,j) = c;
+      for(i=1; i<m; i++) c[i] = Fl_mul(ucoeff(y,i,j), x, p);
+    }
+  else
+    for(j=1; j<l; j++) {
+      GEN c = cgetg(m, t_VECSMALL); gel(z,j) = c;
+      for(i=1; i<m; i++) c[i] = (ucoeff(y,i,j) * x) % p;
+    }
+  return z;
+}
+GEN
+Flm_neg(GEN y, ulong p)
+{
+  long i, j, m = lgcols(y), l = lg(y);
+  GEN z = cgetg(l, t_MAT);
+  for(j=1; j<l; j++) {
+    GEN c = cgetg(m, t_VECSMALL); gel(z,j) = c;
+    for(i=1; i<m; i++) c[i] = Fl_neg(ucoeff(y,i,j), p);
+  }
+  return z;
+}
+
+/* x[i,]*y. Assume lx > 1 and 0 < i < lgcols(x) */
+static GEN
+ZMrow_ZC_mul_i(GEN x, GEN y, long lx, long i)
+{
+  GEN c = mulii(gcoeff(x,i,1), gel(y,1));
+  long k;
+  for (k = 2; k < lx; k++)
+  {
+    GEN t = mulii(gcoeff(x,i,k), gel(y,k));
+    if (signe(t)) c = addii(c, t);
+  }
+  return c;
+}
+
+static long
+zmrow_zc_mul(GEN x, GEN y, long lx, long i)
+{
+  long k;
+  long c = coeff(x,i,1) * y[1];
+  for (k = 2; k < lx; k++)
+    c += coeff(x,i,k) * y[k];
+  return c;
+}
+
+GEN
+zm_zc_mul(GEN x, GEN y)
+{
+  long lx = lg(x), l, i;
+  GEN z;
+  if (lx == 1) return cgetg(1, t_VECSMALL);
+  l = lg(gel(x,1));
+  z = cgetg(l,t_VECSMALL);
+  for (i=1; i<l; i++) z[i] = zmrow_zc_mul(x, y, lx, i);
+  return z;
+}
+
+GEN
+zm_mul(GEN x, GEN y)
+{
+  long i,j,lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  z = cgetg(ly,t_MAT);
+  if (lx==1)
+  {
+    for (i=1; i<ly; i++) gel(z,i) = cgetg(1,t_VECSMALL);
+    return z;
+  }
+  for (j=1; j<ly; j++)
+    gel(z,j) = zm_zc_mul(x, gel(y,j));
+  return z;
+}
+
+static ulong
+Flmrow_Flc_mul_SMALL(GEN x, GEN y, ulong p, long lx, long i)
+{
+  ulong c = ucoeff(x,i,1) * y[1];
+  long k;
+  for (k = 2; k < lx; k++) {
+    c += ucoeff(x,i,k) * y[k];
+    if (c & HIGHBIT) c %= p;
+  }
+  return c % p;
+}
+static ulong
+Flmrow_Flc_mul(GEN x, GEN y, ulong p, long lx, long i)
+{
+  ulong c = Fl_mul(ucoeff(x,i,1), y[1], p);
+  long k;
+  for (k = 2; k < lx; k++)
+    c = Fl_add(c, Fl_mul(ucoeff(x,i,k), y[k], p), p);
+  return c;
+}
+
+static GEN
+Flm_Flc_mul_i_2(GEN x, GEN y, long lx, long l)
+{
+  long i,j;
+  GEN z = NULL;
+
+  for (j=1; j<lx; j++)
+  {
+    if (!y[j]) continue;
+    if (!z) z = Flv_copy(gel(x,j));
+    else for (i = 1; i < l; i++) z[i] ^= coeff(x,i,j);
+  }
+  if (!z) z = zero_zv(l-1);
+  return z;
+}
+
+static GEN
+FpM_FpC_mul_i(GEN x, GEN y, long lx, long l, GEN p)
+{
+  GEN z = cgetg(l,t_COL);
+  long i;
+  for (i = 1; i < l; i++)
+  {
+    pari_sp av = avma;
+    GEN c = ZMrow_ZC_mul_i(x, y, lx, i);
+    gel(z,i) = gerepileuptoint(av, modii(c,p));
+  }
+  return z;
+}
+static GEN
+Flm_Flc_mul_i_SMALL(GEN x, GEN y, long lx, long l, ulong p)
+{
+  GEN z = cgetg(l,t_VECSMALL);
+  long i;
+  for (i = 1; i < l; i++) z[i] = Flmrow_Flc_mul_SMALL(x, y, p, lx, i);
+  return z;
+}
+static GEN
+Flm_Flc_mul_i(GEN x, GEN y, long lx, long l, ulong p)
+{
+  GEN z = cgetg(l,t_VECSMALL);
+  long i;
+  for (i=1; i<l; i++) z[i] = Flmrow_Flc_mul(x, y, p, lx, i);
+  return z;
+}
+INLINE GEN
+F2m_F2c_mul_i(GEN x, GEN y, long lx, long l)
+{
+  long j;
+  GEN z = NULL;
+
+  for (j=1; j<lx; j++)
+  {
+    if (!F2v_coeff(y,j)) continue;
+    if (!z) z = vecsmall_copy(gel(x,j));
+    else F2v_add_inplace(z,gel(x,j));
+  }
+  if (!z) z = zero_F2v(l);
+  return z;
+}
+
+GEN
+FpM_mul(GEN x, GEN y, GEN p)
+{
+  long j, l, lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  if (lx==1) return zeromat(0, ly-1);
+  if (lgefint(p) == 3)
+  {
+    pari_sp av = avma;
+    ulong pp = (ulong)p[2];
+    if (pp == 2)
+    {
+      x = ZM_to_F2m(x);
+      y = ZM_to_F2m(y);
+      z = F2m_to_ZM(F2m_mul(x,y));
+    }
+    else
+    {
+      x = ZM_to_Flm(x, pp);
+      y = ZM_to_Flm(y, pp);
+      z = Flm_to_ZM(Flm_mul(x,y, pp));
+    }
+    return gerepileupto(av, z);
+  }
+  l = lgcols(x); z = cgetg(ly,t_MAT);
+  for (j=1; j<ly; j++) gel(z,j) = FpM_FpC_mul_i(x, gel(y,j), lx, l, p);
+  return z;
+}
+GEN
+Flm_mul(GEN x, GEN y, ulong p)
+{
+  long i,j,l,lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  z = cgetg(ly,t_MAT);
+  if (lx==1)
+  {
+    for (i=1; i<ly; i++) gel(z,i) = cgetg(1,t_VECSMALL);
+    return z;
+  }
+  l = lgcols(x);
+  if (SMALL_ULONG(p)) {
+    for (j=1; j<ly; j++)
+      gel(z,j) = Flm_Flc_mul_i_SMALL(x, gel(y,j), lx, l, p);
+  } else {
+    for (j=1; j<ly; j++)
+      gel(z,j) = Flm_Flc_mul_i(x, gel(y,j), lx, l, p);
+  }
+  return z;
+}
+GEN
+F2m_mul(GEN x, GEN y)
+{
+  long i,j,l,lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  z = cgetg(ly,t_MAT);
+  if (lx==1)
+  {
+    for (i=1; i<ly; i++) gel(z,i) = mkvecsmall(0);
+    return z;
+  }
+  l = coeff(x,1,1);
+  for (j=1; j<ly; j++) gel(z,j) = F2m_F2c_mul_i(x, gel(y,j), lx, l);
+  return z;
+}
+
+static GEN
+_Flm_mul(void *p , GEN x, GEN y)
+{ return Flm_mul(x,y,*(ulong*)p); }
+static GEN
+_Flm_sqr(void *p, GEN x)
+{ return Flm_mul(x,x,*(ulong*)p); }
+GEN
+Flm_powu(GEN x, ulong n, ulong p)
+{
+  pari_sp av = avma;
+  if (!n) return matid(lg(x)-1);
+  return gerepileupto(av, gen_powu(x, n, (void*)&p, &_Flm_sqr, &_Flm_mul));
+}
+static GEN
+_F2m_mul(void *data, GEN x, GEN y)
+{ (void) data; return F2m_mul(x,y); }
+static GEN
+_F2m_sqr(void *data, GEN x)
+{ (void) data; return F2m_mul(x,x); }
+GEN
+F2m_powu(GEN x, ulong n)
+{
+  pari_sp av = avma;
+  if (!n) return matid(lg(x)-1);
+  return gerepileupto(av, gen_powu(x, n,NULL, &_F2m_sqr, &_F2m_mul));
+}
+static GEN
+_FpM_mul(void *p , GEN x, GEN y)
+{ return FpM_mul(x,y,(GEN)p); }
+static GEN
+_FpM_sqr(void *p, GEN x)
+{ return FpM_mul(x,x,(GEN)p); }
+GEN
+FpM_powu(GEN x, ulong n, GEN p)
+{
+  pari_sp av = avma;
+  if (!n) return matid(lg(x)-1);
+  if (lgefint(p) == 3)
+  {
+    pari_sp av = avma;
+    ulong pp = (ulong)p[2];
+    GEN z;
+    if (pp == 2)
+      z = F2m_to_ZM(F2m_powu(ZM_to_F2m(x),n));
+    else
+      z = Flm_to_ZM(Flm_powu(ZM_to_Flm(x, pp), n, pp));
+    return gerepileupto(av, z);
+  }
+  return gerepileupto(av, gen_powu(x, n, (void*)p, &_FpM_sqr, &_FpM_mul));
+}
+
+/*Multiple a column vector by a line vector to make a matrix*/
+GEN
+FpC_FpV_mul(GEN x, GEN y, GEN p)
+{
+  long i,j, lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  z = cgetg(ly,t_MAT);
+  for (j=1; j < ly; j++)
+  {
+    gel(z,j) = cgetg(lx,t_COL);
+    for (i=1; i<lx; i++) gcoeff(z,i,j) = Fp_mul(gel(x,i),gel(y,j), p);
+  }
+  return z;
+}
+
+/* Multiply a line vector by a column and return a scalar (t_INT) */
+GEN
+FpV_dotproduct(GEN x, GEN y, GEN p)
+{
+  long i, lx = lg(x);
+  pari_sp av;
+  GEN c;
+  if (lx == 1) return gen_0;
+  av = avma; c = mulii(gel(x,1),gel(y,1));
+  for (i=2; i<lx; i++) c = addii(c, mulii(gel(x,i),gel(y,i)));
+  return gerepileuptoint(av, modii(c,p));
+}
+GEN
+FpV_dotsquare(GEN x, GEN p)
+{
+  long i, lx = lg(x);
+  pari_sp av;
+  GEN c;
+  if (lx == 1) return gen_0;
+  av = avma; c = sqri(gel(x,1));
+  for (i=2; i<lx; i++) c = addii(c, sqri(gel(x,i)));
+  return gerepileuptoint(av, modii(c,p));
+}
+ulong
+Flv_dotproduct(GEN x, GEN y, ulong p)
+{
+  long i, lx = lg(x);
+  ulong c;
+  if (lx == 1) return 0;
+  c = Fl_mul(x[1], y[1], p);
+  for (i=2; i<lx; i++) c = Fl_add(c, Fl_mul(x[i], y[i], p), p);
+  return c;
+}
+ulong
+F2v_dotproduct(GEN x0, GEN y0)
+{
+  GEN x = x0, y = y0;
+  long i, lx = lg(x);
+  ulong c;
+  if (lx == 2) return 0;
+  c = uel(x,2) & uel(y,2);
+  for (i=3; i<lx; i++) c ^= uel(x,i) & uel(y,i);
+#ifdef LONG_IS_64BIT
+  c ^= c >> 32;
+#endif
+  c ^= c >> 16;
+  c ^= c >>  8;
+  c ^= c >>  4;
+  c ^= c >>  2;
+  c ^= c >>  1;
+  return c & 1;
+}
+
+GEN
+FpM_FpC_mul(GEN x, GEN y, GEN p)
+{
+  long lx = lg(x);
+  return lx==1? cgetg(1,t_COL): FpM_FpC_mul_i(x, y, lx, lgcols(x), p);
+}
+GEN
+Flm_Flc_mul(GEN x, GEN y, ulong p)
+{
+  long l, lx = lg(x);
+  if (lx==1) return cgetg(1,t_VECSMALL);
+  l = lgcols(x);
+  if (p==2)
+    return Flm_Flc_mul_i_2(x, y, lx, l);
+  else if (SMALL_ULONG(p))
+    return Flm_Flc_mul_i_SMALL(x, y, lx, l, p);
+  else
+    return Flm_Flc_mul_i(x, y, lx, l, p);
+}
+GEN
+F2m_F2c_mul(GEN x, GEN y)
+{
+  long l, lx = lg(x);
+  if (lx==1) return cgetg(1,t_VECSMALL);
+  l = coeff(x,1,1);
+  return F2m_F2c_mul_i(x, y, lx, l);
+}
+/* RgV_to_RgX(FpM_FpC_mul(x,y,p), v), p != NULL, memory clean */
+GEN
+FpM_FpC_mul_FpX(GEN x, GEN y, GEN p, long v)
+{
+  long i, l, lx = lg(x);
+  GEN z;
+  if (lx==1) return pol_0(v);
+  l = lgcols(x);
+  z = new_chunk(l+1);
+  for (i=l-1; i; i--)
+  {
+    pari_sp av = avma;
+    GEN p1 = ZMrow_ZC_mul_i(x,y,lx,i);
+    p1 = modii(p1, p);
+    if (signe(p1))
+    {
+      if (i != l-1) stackdummy((pari_sp)(z + l+1), (pari_sp)(z + i+2));
+      gel(z,i+1) = gerepileuptoint(av, p1);
+      break;
+    }
+    avma = av;
+  }
+  if (!i) { avma = (pari_sp)(z + l+1); return pol_0(v); }
+  z[0] = evaltyp(t_POL) | evallg(i+2);
+  z[1] = evalsigne(1) | evalvarn(v);
+  for (; i; i--)
+  {
+    pari_sp av = avma;
+    GEN p1 = ZMrow_ZC_mul_i(x,y,lx,i);
+    gel(z,i+1) = gerepileuptoint(av, modii(p1,p));
+  }
+  return z;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           TRANSPOSITION                        **/
+/**                                                                **/
+/********************************************************************/
+
+/* == zm_transpose */
+GEN
+Flm_transpose(GEN x)
+{
+  long i, dx, lx = lg(x);
+  GEN y;
+  if (lx == 1) return cgetg(1,t_MAT);
+  dx = lgcols(x); y = cgetg(dx,t_MAT);
+  for (i=1; i<dx; i++) gel(y,i) = row_Flm(x,i);
+  return y;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           SCALAR MATRICES                      **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+gen_matid(long n, void *E, const struct bb_field *S)
+{
+  GEN y = cgetg(n+1,t_MAT), _0, _1;
+  long i;
+  if (n < 0) pari_err_DOMAIN("gen_matid", "dimension","<",gen_0,stoi(n));
+  _0 = S->s(E,0);
+  _1 = S->s(E,1);
+  for (i=1; i<=n; i++)
+  {
+    GEN z = const_col(n, _0); gel(z,i) = _1;
+    gel(y, i) = z;
+  }
+  return y;
+}
+
+GEN
+matid_F2m(long n)
+{
+  GEN y = cgetg(n+1,t_MAT);
+  long i;
+  if (n < 0) pari_err_DOMAIN("matid_F2m", "dimension","<",gen_0,stoi(n));
+  for (i=1; i<=n; i++) { gel(y,i) = zero_F2v(n); F2v_set(gel(y,i),i); }
+  return y;
+}
+
+GEN
+matid_F2xqM(long n, GEN T)
+{
+  void *E;
+  const struct bb_field *S = get_F2xq_field(&E, T);
+  return gen_matid(n, E, S);
+}
+GEN
+matid_FlxqM(long n, GEN T, ulong p)
+{
+  void *E;
+  const struct bb_field *S = get_Flxq_field(&E, T, p);
+  return gen_matid(n, E, S);
+}
+
+GEN
+matid_Flm(long n)
+{
+  GEN y = cgetg(n+1,t_MAT);
+  long i;
+  if (n < 0) pari_err_DOMAIN("matid_Flm", "dimension","<",gen_0,stoi(n));
+  for (i=1; i<=n; i++) { gel(y,i) = zero_zv(n); ucoeff(y, i,i) = 1; }
+  return y;
+}
+
+GEN
+scalar_Flm(long s, long n)
+{
+  long i;
+  GEN y = cgetg(n+1,t_MAT);
+  for (i=1; i<=n; i++) { gel(y,i) = zero_Flv(n); coeff(y, i,i) = s; }
+  return y;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           CONVERSIONS                          **/
+/**                                                                **/
+/********************************************************************/
+GEN
+ZV_to_Flv(GEN x, ulong p)
+{
+  long i, n = lg(x);
+  GEN y = cgetg(n,t_VECSMALL);
+  for (i=1; i<n; i++) y[i] = umodiu(gel(x,i), p);
+  return y;
+}
+GEN
+ZM_to_Flm(GEN x, ulong p)
+{
+  long j,n = lg(x);
+  GEN y = cgetg(n,t_MAT);
+  if (n == 1) return y;
+  for (j=1; j<n; j++) gel(y,j) = ZV_to_Flv(gel(x,j), p);
+  return y;
+}
+
+/*                          TO INTMOD                        */
+static GEN
+to_intmod(GEN x, GEN p) { retmkintmod(modii(x, p), p); }
+static GEN
+Fl_to_intmod(ulong x, GEN p) { retmkintmod(utoi(x), p); }
+
+GEN
+Fp_to_mod(GEN z, GEN p)
+{
+  retmkintmod(modii(z, p), icopy(p));
+}
+
+/* z in Z[X], return z * Mod(1,p), normalized*/
+GEN
+FpX_to_mod(GEN z, GEN p)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l,t_POL);
+  if (l >2) p = icopy(p);
+  for (i=2; i<l; i++) gel(x,i) = to_intmod(gel(z,i), p);
+  x[1] = z[1]; return normalizepol_lg(x,l);
+}
+
+/* z in Z^n, return z * Mod(1,p), normalized*/
+GEN
+FpV_to_mod(GEN z, GEN p)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_VEC);
+  if (l == 1) return x;
+  p = icopy(p);
+  for (i=1; i<l; i++) gel(x,i) = to_intmod(gel(z,i), p);
+  return x;
+}
+/* z in Z^n, return z * Mod(1,p), normalized*/
+GEN
+FpC_to_mod(GEN z, GEN p)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_COL);
+  if (l == 1) return x;
+  p = icopy(p);
+  for (i=1; i<l; i++) gel(x,i) = to_intmod(gel(z,i), p);
+  return x;
+}
+/* z in Mat m,n(Z), return z * Mod(1,p), normalized*/
+GEN
+FpM_to_mod(GEN z, GEN p)
+{
+  long i, j, m, l = lg(z);
+  GEN  x = cgetg(l,t_MAT), y, zi;
+  if (l == 1) return x;
+  m = lgcols(z);
+  p = icopy(p);
+  for (i=1; i<l; i++)
+  {
+    gel(x,i) = cgetg(m,t_COL);
+    y = gel(x,i); zi= gel(z,i);
+    for (j=1; j<m; j++) gel(y,j) = to_intmod(gel(zi,j), p);
+  }
+  return x;
+}
+GEN
+Flc_to_mod(GEN z, ulong pp)
+{
+  long i, l = lg(z);
+  GEN p, x = cgetg(l, t_COL);
+  if (l == 1) return x;
+  p = utoipos(pp);
+  for (i=1; i<l; i++) gel(x,i) = Fl_to_intmod(z[i], p);
+  return x;
+}
+GEN
+Flm_to_mod(GEN z, ulong pp)
+{
+  long i, j, m, l = lg(z);
+  GEN p, x = cgetg(l,t_MAT), y, zi;
+  if (l == 1) return x;
+  m = lgcols(z);
+  p = utoipos(pp);
+  for (i=1; i<l; i++)
+  {
+    gel(x,i) = cgetg(m,t_COL);
+    y = gel(x,i); zi= gel(z,i);
+    for (j=1; j<m; j++) gel(y,j) = Fl_to_intmod(zi[j], p);
+  }
+  return x;
+}
+
+GEN
+FpVV_to_mod(GEN z, GEN p)
+{
+  long i, j, m, l = lg(z);
+  GEN  x = cgetg(l,t_VEC), y, zi;
+  if (l == 1) return x;
+  m = lgcols(z);
+  p = icopy(p);
+  for (i=1; i<l; i++)
+  {
+    gel(x,i) = cgetg(m,t_VEC);
+    y = gel(x,i); zi= gel(z,i);
+    for (j=1; j<m; j++) gel(y,j) = to_intmod(gel(zi,j), p);
+  }
+  return x;
+}
+
+/* z in Z^n, return z * Mod(1,p), normalized*/
+GEN
+FpXQC_to_mod(GEN z, GEN T, GEN p)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_COL); T = FpX_to_mod(T, p);
+  for (i=1; i<l; i++)
+    gel(x,i) = mkpolmod(FpX_to_mod(gel(z,i), p), T);
+  return x;
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                     Blackbox linear algebra                      */
+/*                                                                  */
+/********************************************************************/
+
+/* A sparse column (zCs) v is a t_COL with two components C and E which are
+ * t_VECSMALL of the same length, representing sum_i E[i]*e_{C[i]}, where
+ * (e_j) is the canonical basis.
+ * A sparse matrix (zMs) is a t_VEC of zCs */
+
+/* FpCs and FpMs are identical but E[i] is interpreted as a _signed_ C long
+ * integer representing an element of Fp. This is important since p can be
+ * large and p+E[i] would not fit in a C long.  */
+
+/* RgCs and RgMs are similar, except that the type of the component is
+ * unspecified. Functions handling RgCs/RgMs must be independent of the type
+ * of E. */
+
+/* Most functions take an argument nbrow which is the number of lines of the
+ * column/matrix, which cannot be derived from the data. */
+
+GEN
+zCs_to_ZC(GEN R, long nbrow)
+{
+  GEN C = gel(R,1), E = gel(R,2), c = zerocol(nbrow);
+  long j, l = lg(C);
+  for (j = 1; j < l; ++j) gel(c, C[j]) = stoi(E[j]);
+  return c;
+}
+
+GEN
+zMs_to_ZM(GEN M, long nbrow)
+{
+  long i, l = lg(M);
+  GEN m = cgetg(l, t_MAT);
+  for (i = 1; i < l; ++i) gel(m,i) = zCs_to_ZC(gel(M,i), nbrow);
+  return m;
+}
+
+/* Solve equation f(X) = B (mod p) where B is a FpV, and f is an endomorphism.
+ * Return either a solution as a t_COL, or a kernel vector as a t_VEC. */
+GEN
+gen_FpM_Wiedemann(void *E, GEN (*f)(void*, GEN), GEN B, GEN p)
+{
+  pari_sp ltop = avma;
+  long col = 0, n = lg(B)-1, m = 2*n+1;
+  if (ZV_equal0(B)) return zerocol(n);
+  while (++col <= n)
+  {
+    pari_sp btop = avma, av, lim;
+    long i, lQ;
+    GEN V, Q, M, W = B;
+    GEN b = cgetg(m+2, t_POL);
+    b[1] = evalsigne(1)|evalvarn(0);
+    gel(b, 2) = gel(W, col);
+    for (i = 3; i<m+2; i++)
+      gel(b, i) = cgeti(lgefint(p));
+    av = avma; lim = stack_lim(av,1);
+    for (i = 3; i<m+2; i++)
+    {
+      W = f(E, W);
+      affii(gel(W, col),gel(b, i));
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"Wiedemann: first loop, %ld",i);
+        W = gerepileupto(av, W);
+      }
+    }
+    b = FpX_renormalize(b, m+2);
+    if (lgpol(b)==0) {avma = btop; continue; }
+    M = FpX_halfgcd(b, monomial(gen_1, m, 0), p);
+    Q = FpX_neg(FpX_normalize(gcoeff(M, 2, 1),p),p);
+    W = B; lQ =lg(Q);
+    if (DEBUGLEVEL) err_printf("Wiedemann: deg. minpoly: %ld\n",lQ-3);
+    V = FpC_Fp_mul(W, gel(Q, lQ-2), p);
+    av = avma; lim = stack_lim(av,1);
+    for (i = lQ-3; i > 1; i--)
+    {
+      W = f(E, W);
+      V = ZC_lincomb(gen_1, gel(Q,i), V, W);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"Wiedemann: second loop, %ld",i);
+        gerepileall(av, 2, &V, &W);
+      }
+    }
+    V = FpC_red(V, p);
+    W = FpC_sub(f(E,V), B, p);
+    if (ZV_equal0(W)) return gerepilecopy(ltop, V);
+    av = avma;
+    for (i = 1; i <= n; ++i)
+    {
+      V = W;
+      W = f(E, V);
+      if (ZV_equal0(W))
+        return gerepilecopy(ltop, shallowtrans(V));
+      gerepileall(av, 2, &V, &W);
+    }
+    avma = btop;
+  }
+  return NULL;
+}
+
+GEN
+zMs_ZC_mul(GEN M, GEN B)
+{
+  long i, j;
+  long n = lg(B)-1;
+  GEN V = zerocol(n);
+  for (i = 1; i <= n; ++i)
+    if (signe(gel(B, i)))
+    {
+      GEN R = gel(M, i), C = gel(R, 1), E = gel(R, 2);
+      long l = lg(C);
+      for (j = 1; j < l; ++j)
+      {
+        long k = C[j];
+        switch(E[j])
+        {
+        case 1:
+          gel(V, k) = gel(V,k)==gen_0 ? gel(B,i) : addii(gel(V, k), gel(B,i));
+          break;
+        case -1:
+          gel(V, k) = gel(V,k)==gen_0 ? negi(gel(B,i)) : subii(gel(V, k), gel(B,i));
+          break;
+        default:
+          gel(V, k) = gel(V,k)==gen_0 ? mulis(gel(B, i), E[j]) : addii(gel(V, k), mulis(gel(B, i), E[j]));
+          break;
+        }
+      }
+    }
+  return V;
+}
+
+GEN
+FpMs_FpC_mul(GEN M, GEN B, GEN p) { return FpC_red(zMs_ZC_mul(M, B), p); }
+
+GEN
+ZV_zMs_mul(GEN B, GEN M)
+{
+  long i, j;
+  long m = lg(M)-1;
+  GEN V = cgetg(m+1,t_VEC);
+  for (i = 1; i <= m; ++i)
+  {
+    GEN R = gel(M, i), C = gel(R, 1), E = gel(R, 2);
+    long l = lg(C);
+    GEN z = mulis(gel(B, C[1]), E[1]);
+    for (j = 2; j < l; ++j)
+    {
+      long k = C[j];
+      switch(E[j])
+      {
+      case 1:
+        z = addii(z, gel(B,k));
+        break;
+      case -1:
+        z = subii(z, gel(B,k));
+        break;
+      default:
+        z = addii(z, mulis(gel(B,k), E[j]));
+        break;
+      }
+    }
+    gel(V,i) = z;
+  }
+  return V;
+}
+
+GEN
+FpV_FpMs_mul(GEN B, GEN M, GEN p) { return FpV_red(ZV_zMs_mul(B, M), p); }
+
+GEN
+ZlM_gauss(GEN a, GEN b, ulong p, long e, GEN C)
+{
+  pari_sp av = avma, lim = stack_lim(av, 2), av2;
+  GEN xi, xb, pi = gen_1, P;
+  long i;
+  if (!C) {
+    C = Flm_inv(ZM_to_Flm(a, p), p);
+    if (!C) pari_err_INV("ZlM_gauss", a);
+  }
+  P = utoipos(p);
+  av2 = avma;
+  xi = Flm_mul(C, ZM_to_Flm(b, p), p);
+  xb = Flm_to_ZM(xi);
+  for (i = 2; i <= e; i++)
+  {
+    pi = muliu(pi, p); /* = p^(i-1) */
+    b = ZM_Z_divexact(ZM_sub(b, ZM_nm_mul(a, xi)), P);
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"ZlM_gauss. i=%ld",i);
+      gerepileall(av2,3, &pi,&b,&xb);
+    }
+    xi = Flm_mul(C, ZM_to_Flm(b, p), p);
+    xb = ZM_add(xb, nm_Z_mul(xi, pi));
+  }
+  return gerepileupto(av, xb);
+}
+
+struct wrapper_modp_s {
+  GEN (*f)(void*, GEN);
+  void *E;
+  GEN p;
+};
+
+/* compute f(x) mod p */
+static GEN
+wrap_relcomb_modp(void *E, GEN x)
+{
+  struct wrapper_modp_s *W = (struct wrapper_modp_s*)E;
+  return FpC_red(W->f(W->E, x), W->p);
+}
+static GEN
+wrap_relcomb(void*E, GEN x) { return zMs_ZC_mul((GEN)E, x); }
+
+static GEN
+wrap_relker(void*E, GEN x) { return ZV_zMs_mul(x, (GEN)E); }
+
+/* Solve f(X) = B (mod p^e); blackbox version of ZlM_gauss */
+GEN
+gen_ZpM_Dixon(void *E, GEN (*f)(void*, GEN), GEN B, GEN p, long e)
+{
+  struct wrapper_modp_s W;
+  pari_sp av = avma, lim = stack_lim(av, 2);
+  GEN xb, xi, pi = gen_1;
+  long i;
+  W.E = E;
+  W.f = f;
+  W.p = p;
+  xi = gen_FpM_Wiedemann((void*)&W, wrap_relcomb_modp, FpC_red(B, p), p); /* f^(-1) B */
+  if (!xi || e == 1 || typ(xi) == t_VEC) return xi;
+  xb = xi;
+  for (i = 2; i <= e; i++)
+  {
+    pi = mulii(pi, p); /* = p^(i-1) */
+    B = ZC_Z_divexact(ZC_sub(B, f(E, xi)), p);
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_ZpM_Dixon. i=%ld",i);
+      gerepileall(av,3, &pi,&B,&xb);
+    }
+    xi = gen_FpM_Wiedemann((void*)&W, wrap_relcomb_modp, FpC_red(B, p), p);
+    if (!xi) return NULL;
+    if (typ(xi) == t_VEC) return gerepileupto(av, xi);
+    xb = ZC_add(xb, ZC_Z_mul(xi, pi));
+  }
+  return gerepileupto(av, xb);
+}
+
+static GEN
+vecprow(GEN A, GEN prow)
+{
+  return mkvec2(vecpermute(prow,gel(A,1)), gel(A,2));
+}
+
+/* Solve the equation MX = A. Return either a solution as a t_COL,
+ * or the index of a column which is linearly dependent from the others as a
+ * t_VECSMALL with a single component. */
+GEN
+ZpMs_ZpCs_solve(GEN M, GEN A, long nbrow, GEN p, long e)
+{
+  pari_sp av = avma;
+  GEN pcol, prow;
+  long nbi=lg(M)-1, lR;
+  long i, n;
+  GEN Mp, Ap, Rp;
+  pari_timer ti;
+  if (DEBUGLEVEL) timer_start(&ti);
+  RgMs_structelim(M, nbrow, gel(A, 1), &pcol, &prow);
+  if (!pcol) { avma = av; return NULL; }
+  if (DEBUGLEVEL)
+    timer_printf(&ti,"structured elimination (%ld -> %ld)",nbi,lg(pcol)-1);
+  n = lg(pcol)-1;
+  Mp = cgetg(n+1, t_MAT);
+  for(i=1; i<=n; i++)
+    gel(Mp, i) = vecprow(gel(M,pcol[i]), prow);
+  Ap = zCs_to_ZC(vecprow(A, prow), n);
+  if (DEBUGLEVEL) timer_start(&ti);
+  Rp = gen_ZpM_Dixon((void*)Mp,wrap_relcomb, Ap, p, e);
+  if (DEBUGLEVEL) timer_printf(&ti,"linear algebra");
+  if (!Rp) { avma = av; return NULL; }
+  lR = lg(Rp)-1;
+  if (typ(Rp) == t_COL)
+  {
+    GEN R = zerocol(nbi+1);
+    for (i=1; i<=lR; i++)
+       gel(R,pcol[i]) = gel(Rp,i);
+    return gerepilecopy(av, R);
+  }
+  for (i = 1; i <= lR; ++i)
+    if (signe(gel(Rp, i)))
+      return gerepileuptoleaf(av, mkvecsmall(pcol[i]));
+  return NULL; /* NOT REACHED */
+}
+
+GEN
+FpMs_FpCs_solve(GEN M, GEN A, long nbrow, GEN p)
+{
+  return ZpMs_ZpCs_solve(M, A, nbrow, p, 1);
+}
+
+GEN
+FpMs_FpCs_solve_safe(GEN M, GEN A, long nbrow, GEN p)
+{
+  GEN res;
+  pari_CATCH(e_INV)
+  {
+    GEN E = pari_err_last();
+    GEN x = err_get_compo(E,2);
+    if (typ(x) != t_INTMOD) pari_err(0,E);
+    if (DEBUGLEVEL)
+      pari_warn(warner,"FpMs_FpCs_solve_safe, impossible inverse %Ps", x);
+    res = NULL;
+  } pari_TRY {
+    res = ZpMs_ZpCs_solve(M, A, nbrow, p, 1);
+  } pari_ENDCATCH
+  return res;
+}
+
+static GEN
+FpMs_structelim_back(GEN M, GEN V, GEN prow, GEN p)
+{
+  long i, j, oldf = 0, f = 0;
+  long lrow = lg(prow), lM = lg(M);
+  GEN W = const_vecsmall(lM-1,1);
+  GEN R = cgetg(lrow, t_VEC);
+  for (i=1; i<lrow; i++)
+    gel(R,i) = prow[i]==0 ? NULL: gel(V,prow[i]);
+  do
+  {
+    oldf = f;
+    for(i=1; i<lM; i++)
+      if (W[i])
+      {
+        GEN C = gel(M,i), F = gel(C,1), E = gel(C,2);
+        long c=0, cj=0, lF = lg(F);
+        for(j=1; j<lF; j++)
+          if (!gel(R,F[j]))
+          { c++; cj=j; }
+        if (c>=2) continue;
+        if (c==1)
+        {
+          pari_sp av = avma;
+          GEN s = gen_0;
+          for(j=1; j<lF; j++)
+            if (j!=cj) s = Fp_add(s, mulis(gel(R,F[j]), E[j]), p);
+          gel(R,F[cj]) = gerepileupto(av, Fp_div(Fp_neg(s, p), stoi(E[cj]), p));
+          f++;
+        }
+        W[i]=0;
+      }
+  } while(oldf!=f);
+  for (i=1; i<lrow; i++)
+    if (!gel(R,i)) gel(R,i) = gen_0;
+  return R;
+}
+
+/* Return a linear form Y such that YM is essentially 0 */
+GEN
+FpMs_leftkernel_elt(GEN M, long nbrow, GEN p)
+{
+  pari_sp av = avma, av2;
+  GEN pcol, prow;
+  long nbi=lg(M)-1;
+  long i, n;
+  GEN Mp, B, MB, R, Rp;
+  pari_timer ti;
+  struct wrapper_modp_s W;
+  if (DEBUGLEVEL) timer_start(&ti);
+  RgMs_structelim(M, nbrow, cgetg(1,t_VECSMALL), &pcol, &prow);
+  if (!pcol) { avma = av; return NULL; }
+  if (DEBUGLEVEL)
+    timer_printf(&ti,"structured elimination (%ld -> %ld)",nbi,lg(pcol)-1);
+  n = lg(pcol)-1;
+  Mp = cgetg(n+1, t_MAT);
+  for(i=1; i<=n; i++)
+    gel(Mp, i) = vecprow(gel(M,pcol[i]), prow);
+  W.E = (void*) Mp;
+  W.f = wrap_relker;
+  W.p = p;
+  av2 = avma;
+  for(;;)
+  {
+    avma = av2;
+    B = cgetg(n+1,t_VEC);
+    for(i=1; i<=n; i++)
+      gel(B,i) = randomi(p);
+    MB = FpV_FpMs_mul(B, Mp, p);
+    if (DEBUGLEVEL) timer_start(&ti);
+    pari_CATCH(e_INV)
+    {
+      GEN E = pari_err_last();
+      GEN x = err_get_compo(E,2);
+      if (typ(x) != t_INTMOD) pari_err(0,E);
+      if (DEBUGLEVEL)
+        pari_warn(warner,"FpMs_leftkernel_elt, impossible inverse %Ps", x);
+      Rp = NULL;
+    } pari_TRY {
+      Rp = gen_FpM_Wiedemann((void*)&W, wrap_relcomb_modp, MB, p);
+    } pari_ENDCATCH
+    if (!Rp) continue;
+    if (typ(Rp)==t_COL)
+    {
+      Rp = FpC_sub(Rp,B,p);
+      if (ZV_equal0(Rp)) continue;
+    }
+    R = FpMs_structelim_back(M, Rp, prow, p);
+    if (DEBUGLEVEL) timer_printf(&ti,"Wiedemann left kernel");
+    return gerepilecopy(av, R);
+  }
+}
diff --git a/src/basemath/FpX.c b/src/basemath/FpX.c
new file mode 100644
index 0000000..2138102
--- /dev/null
+++ b/src/basemath/FpX.c
@@ -0,0 +1,1911 @@
+/* Copyright (C) 2007  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with polynomials over Fp */
+
+static GEN
+get_FpX_red(GEN T, GEN *B)
+{
+  if (typ(T)!=t_VEC) { *B=NULL; return T; }
+  *B = gel(T,1); return gel(T,2);
+}
+
+GEN
+get_FpX_mod(GEN T) { return typ(T)==t_VEC? gel(T,2): T; }
+
+long
+get_FpX_var(GEN T) { return typ(T)==t_VEC? varn(gel(T,2)): varn(T); }
+
+long
+get_FpX_degree(GEN T) { return typ(T)==t_VEC? degpol(gel(T,2)): degpol(T); }
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              FpX                                  **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* FpX are polynomials over Z/pZ represented as t_POL with
+ * t_INT coefficients.
+ * 1) Coefficients should belong to {0,...,p-1}, though non-reduced
+ * coefficients should work but be slower.
+ *
+ * 2) p is not assumed to be prime, but it is assumed that impossible divisions
+ *    will not happen.
+ * 3) Theses functions let some garbage on the stack, but are gerepileupto
+ * compatible.
+ */
+
+static ulong
+to_Flx(GEN *P, GEN *Q, GEN p)
+{
+  ulong pp = (ulong)p[2];
+  *P = ZX_to_Flx(*P, pp);
+  *Q = ZX_to_Flx(*Q, pp); return pp;
+}
+
+static ulong
+to_Flxq(GEN *P, GEN *T, GEN p)
+{
+  ulong pp = (ulong)p[2];
+  if (P) *P = ZX_to_Flx(*P, pp);
+  *T = ZXT_to_FlxT(*T, pp); return pp;
+}
+
+GEN
+Z_to_FpX(GEN a, GEN p, long v)
+{
+  pari_sp av = avma;
+  GEN z = cgetg(3, t_POL);
+  GEN x = modii(a, p);
+  if (!signe(x)) { avma =av; return pol_0(v); }
+  z[1] = evalsigne(1) | evalvarn(v);
+  gel(z,2) = x; return z;
+}
+
+/* z in Z[X], return lift(z * Mod(1,p)), normalized*/
+GEN
+FpX_red(GEN z, GEN p)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_POL);
+  for (i=2; i<l; i++) gel(x,i) = modii(gel(z,i),p);
+  x[1] = z[1]; return FpX_renormalize(x,l);
+}
+GEN
+FpXV_red(GEN z, GEN p)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = FpX_red(gel(z,i), p);
+  return x;
+}
+
+GEN
+FpXT_red(GEN z, GEN p)
+{
+  if (typ(z) == t_POL)
+    return FpX_red(z, p);
+  else
+  {
+    long i,l = lg(z);
+    GEN x = cgetg(l, t_VEC);
+    for (i=1; i<l; i++) gel(x,i) = FpXT_red(gel(z,i), p);
+    return x;
+  }
+}
+
+GEN
+FpX_normalize(GEN z, GEN p)
+{
+  GEN p1 = leading_term(z);
+  if (lg(z) == 2 || equali1(p1)) return z;
+  return FpX_Fp_mul_to_monic(z, Fp_inv(p1,p), p);
+}
+
+GEN
+FpX_center(GEN T, GEN p, GEN pov2)
+{
+  long i, l = lg(T);
+  GEN P = cgetg(l,t_POL);
+  for(i=2; i<l; i++) gel(P,i) = Fp_center(gel(T,i), p, pov2);
+  P[1] = T[1]; return P;
+}
+
+GEN
+FpX_add(GEN x,GEN y,GEN p)
+{
+  long lx = lg(x), ly = lg(y), i;
+  GEN z;
+  if (lx < ly) swapspec(x,y, lx,ly);
+  z = cgetg(lx,t_POL); z[1] = x[1];
+  for (i=2; i<ly; i++) gel(z,i) = Fp_add(gel(x,i),gel(y,i), p);
+  for (   ; i<lx; i++) gel(z,i) = modii(gel(x,i), p);
+  z = ZX_renormalize(z, lx);
+  if (!lgpol(z)) { avma = (pari_sp)(z + lx); return pol_0(varn(x)); }
+  return z;
+}
+
+static GEN
+Fp_red_FpX(GEN x, GEN p, long v)
+{
+  GEN z;
+  if (!signe(x)) return pol_0(v);
+  z = cgetg(3, t_POL);
+  gel(z,2) = Fp_red(x,p);
+  z[1] = evalvarn(v);
+  return FpX_renormalize(z, 3);
+}
+
+static GEN
+Fp_neg_FpX(GEN x, GEN p, long v)
+{
+  GEN z;
+  if (!signe(x)) return pol_0(v);
+  z = cgetg(3, t_POL);
+  gel(z,2) = Fp_neg(x,p);
+  z[1] = evalvarn(v);
+  return FpX_renormalize(z, 3);
+}
+
+GEN
+FpX_Fp_add(GEN y,GEN x,GEN p)
+{
+  long i, lz = lg(y);
+  GEN z;
+  if (lz == 2) return Fp_red_FpX(x,p,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = Fp_add(gel(y,2),x, p);
+  if (lz == 3) z = FpX_renormalize(z,lz);
+  else
+    for(i=3;i<lz;i++) gel(z,i) = icopy(gel(y,i));
+  return z;
+}
+GEN
+FpX_Fp_add_shallow(GEN y,GEN x,GEN p)
+{
+  long i, lz = lg(y);
+  GEN z;
+  if (lz == 2) return scalar_ZX_shallow(x,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = Fp_add(gel(y,2),x, p);
+  if (lz == 3) z = FpX_renormalize(z,lz);
+  else
+    for(i=3;i<lz;i++) gel(z,i) = gel(y,i);
+  return z;
+}
+GEN
+FpX_Fp_sub(GEN y,GEN x,GEN p)
+{
+  long i, lz = lg(y);
+  GEN z;
+  if (lz == 2) return Fp_neg_FpX(x,p,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = Fp_sub(gel(y,2),x, p);
+  if (lz == 3) z = FpX_renormalize(z,lz);
+  else
+    for(i=3;i<lz;i++) gel(z,i) = icopy(gel(y,i));
+  return z;
+}
+GEN
+FpX_Fp_sub_shallow(GEN y,GEN x,GEN p)
+{
+  long i, lz = lg(y);
+  GEN z;
+  if (lz == 2) return Fp_neg_FpX(x,p,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = Fp_sub(gel(y,2),x, p);
+  if (lz == 3) z = FpX_renormalize(z,lz);
+  else
+    for(i=3;i<lz;i++) gel(z,i) = gel(y,i);
+  return z;
+}
+
+GEN
+FpX_neg(GEN x,GEN p)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx,t_POL);
+  y[1] = x[1];
+  for(i=2; i<lx; i++) gel(y,i) = Fp_neg(gel(x,i), p);
+  return ZX_renormalize(y, lx);
+}
+
+static GEN
+FpX_subspec(GEN x,GEN y,GEN p, long nx, long ny)
+{
+  long i, lz;
+  GEN z;
+  if (nx >= ny)
+  {
+    lz = nx+2;
+    z = cgetg(lz,t_POL); z[1] = 0; z += 2;
+    for (i=0; i<ny; i++) gel(z,i) = Fp_sub(gel(x,i),gel(y,i), p);
+    for (   ; i<nx; i++) gel(z,i) = modii(gel(x,i), p);
+  }
+  else
+  {
+    lz = ny+2;
+    z = cgetg(lz,t_POL); z[1] = 0; z += 2;
+    for (i=0; i<nx; i++) gel(z,i) = Fp_sub(gel(x,i),gel(y,i), p);
+    for (   ; i<ny; i++) gel(z,i) = Fp_neg(gel(y,i), p);
+  }
+  z = FpX_renormalize(z-2, lz);
+  if (!lgpol(z)) { avma = (pari_sp)(z + lz); return pol_0(0); }
+  return z;
+}
+
+GEN
+FpX_sub(GEN x,GEN y,GEN p)
+{
+  GEN z = FpX_subspec(x+2,y+2,p,lgpol(x),lgpol(y));
+  setvarn(z, varn(x));
+  return z;
+}
+
+GEN
+Fp_FpX_sub(GEN x, GEN y, GEN p)
+{
+  long ly = lg(y), i;
+  GEN z;
+  if (ly <= 3) {
+    z = cgetg(3, t_POL);
+    x = (ly == 3)? Fp_sub(x, gel(y,2), p): modii(x, p);
+    if (!signe(x)) { avma = (pari_sp)(z + 3); return pol_0(varn(y)); }
+    z[1] = evalsigne(1)|y[1]; gel(z,2) = x; return z;
+  }
+  z = cgetg(ly,t_POL);
+  gel(z,2) = Fp_sub(x, gel(y,2), p);
+  for (i = 3; i < ly; i++) gel(z,i) = Fp_neg(gel(y,i), p);
+  z = ZX_renormalize(z, ly);
+  if (!lgpol(z)) { avma = (pari_sp)(z + ly); return pol_0(varn(x)); }
+  z[1] = y[1]; return z;
+}
+
+GEN
+FpX_mul(GEN x,GEN y,GEN p) { return FpX_red(ZX_mul(x, y), p); }
+
+GEN
+FpX_mulspec(GEN a, GEN b, GEN p, long na, long nb)
+{ return FpX_red(ZX_mulspec(a, b, na, nb), p); }
+
+GEN
+FpX_sqr(GEN x,GEN p) { return FpX_red(ZX_sqr(x), p); }
+
+GEN
+FpX_mulu(GEN y, ulong x,GEN p)
+{
+  GEN z;
+  long i, l;
+  if (!x) return zeropol(varn(y));
+  z = cgetg_copy(y, &l); z[1] = y[1];
+  for(i=2; i<l; i++) gel(z,i) = Fp_mulu(gel(y,i), x, p);
+  return z;
+}
+
+GEN
+FpX_Fp_mulspec(GEN y,GEN x,GEN p,long ly)
+{
+  GEN z;
+  long i;
+  if (!signe(x)) return pol_0(0);
+  z = cgetg(ly+2,t_POL); z[1] = evalsigne(1);
+  for(i=0; i<ly; i++) gel(z,i+2) = Fp_mul(gel(y,i), x, p);
+  return ZX_renormalize(z, ly+2);
+}
+
+GEN
+FpX_Fp_mul(GEN y,GEN x,GEN p)
+{
+  GEN z = FpX_Fp_mulspec(y+2,x,p,lgpol(y));
+  setvarn(z, varn(y)); return z;
+}
+
+GEN
+FpX_Fp_mul_to_monic(GEN y,GEN x,GEN p)
+{
+  GEN z;
+  long i, l;
+  z = cgetg_copy(y, &l); z[1] = y[1];
+  for(i=2; i<l-1; i++) gel(z,i) = Fp_mul(gel(y,i), x, p);
+  gel(z,l-1) = gen_1; return z;
+}
+
+static GEN
+FpX_divrem_basecase(GEN x, GEN y, GEN p, GEN *pr)
+{
+  long vx, dx, dy, dz, i, j, sx, lr;
+  pari_sp av0, av;
+  GEN z,p1,rem,lead;
+
+  if (!signe(y)) pari_err_INV("FpX_divrem",y);
+  vx = varn(x);
+  dy = degpol(y);
+  dx = degpol(x);
+  if (dx < dy)
+  {
+    if (pr)
+    {
+      av0 = avma; x = FpX_red(x, p);
+      if (pr == ONLY_DIVIDES) { avma=av0; return signe(x)? NULL: pol_0(vx); }
+      if (pr == ONLY_REM) return x;
+      *pr = x;
+    }
+    return pol_0(vx);
+  }
+  lead = leading_term(y);
+  if (!dy) /* y is constant */
+  {
+    if (pr && pr != ONLY_DIVIDES)
+    {
+      if (pr == ONLY_REM) return pol_0(vx);
+      *pr = pol_0(vx);
+    }
+    av0 = avma;
+    if (equali1(lead)) return FpX_red(x, p);
+    else return gerepileupto(av0, FpX_Fp_mul(x, Fp_inv(lead,p), p));
+  }
+  av0 = avma; dz = dx-dy;
+  if (lgefint(p) == 3)
+  { /* assume ab != 0 mod p */
+    ulong pp = to_Flx(&x, &y, p);
+    z = Flx_divrem(x, y, pp, pr);
+    avma = av0; /* HACK: assume pr last on stack, then z */
+    if (!z) return NULL;
+    z = leafcopy(z);
+    if (pr && pr != ONLY_DIVIDES && pr != ONLY_REM)
+    {
+      *pr = leafcopy(*pr);
+      *pr = Flx_to_ZX_inplace(*pr);
+    }
+    return Flx_to_ZX_inplace(z);
+  }
+  lead = equali1(lead)? NULL: gclone(Fp_inv(lead,p));
+  avma = av0;
+  z=cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2; y += 2; z += 2;
+
+  p1 = gel(x,dx); av = avma;
+  gel(z,dz) = lead? gerepileuptoint(av, Fp_mul(p1,lead, p)): icopy(p1);
+  for (i=dx-1; i>=dy; i--)
+  {
+    av=avma; p1=gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    if (lead) p1 = mulii(p1,lead);
+    gel(z,i-dy) = gerepileuptoint(av,modii(p1, p));
+  }
+  if (!pr) { if (lead) gunclone(lead); return z-2; }
+
+  rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
+  for (sx=0; ; i--)
+  {
+    p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    p1 = modii(p1,p); if (signe(p1)) { sx = 1; break; }
+    if (!i) break;
+    avma=av;
+  }
+  if (pr == ONLY_DIVIDES)
+  {
+    if (lead) gunclone(lead);
+    if (sx) { avma=av0; return NULL; }
+    avma = (pari_sp)rem; return z-2;
+  }
+  lr=i+3; rem -= lr;
+  rem[0] = evaltyp(t_POL) | evallg(lr);
+  rem[1] = z[-1];
+  p1 = gerepileuptoint((pari_sp)rem, p1);
+  rem += 2; gel(rem,i) = p1;
+  for (i--; i>=0; i--)
+  {
+    av=avma; p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    gel(rem,i) = gerepileuptoint(av, modii(p1,p));
+  }
+  rem -= 2;
+  if (lead) gunclone(lead);
+  if (!sx) (void)FpX_renormalize(rem, lr);
+  if (pr == ONLY_REM) return gerepileupto(av0,rem);
+  *pr = rem; return z-2;
+}
+
+GEN
+FpX_div_by_X_x(GEN a, GEN x, GEN p, GEN *r)
+{
+  long l = lg(a)-1, i;
+  GEN z = cgetg(l, t_POL);
+  z[1] = evalsigne(1) | evalvarn(0);
+  gel(z, l-1) = gel(a,l);
+  for (i=l-2; i>1; i--) /* z[i] = a[i+1] + x*z[i+1] */
+    gel(z, i) = Fp_addmul(gel(a,i+1), x, gel(z,i+1), p);
+  if (r) *r = Fp_addmul(gel(a,2), x, gel(z,2), p);
+  return z;
+}
+
+long
+FpX_valrem(GEN x, GEN t, GEN p, GEN *py)
+{
+  pari_sp av=avma;
+  long k;
+  GEN r, y;
+
+  for (k=0; ; k++)
+  {
+    y = FpX_divrem(x, t, p, &r);
+    if (signe(r)) break;
+    x = y;
+  }
+  *py = gerepilecopy(av,x);
+  return k;
+}
+
+static GEN
+FpX_halfgcd_basecase(GEN a, GEN b, GEN p)
+{
+  pari_sp av=avma, lim = stack_lim(av,2);
+  GEN u,u1,v,v1;
+  long vx = varn(a);
+  long n = lgpol(a)>>1;
+  u1 = v = pol_0(vx);
+  u = v1 = pol_1(vx);
+  while (lgpol(b)>n)
+  {
+    GEN r, q = FpX_divrem(a,b,p, &r);
+    a = b; b = r; swap(u,u1); swap(v,v1);
+    u1 = FpX_sub(u1, FpX_mul(u, q, p), p);
+    v1 = FpX_sub(v1, FpX_mul(v, q ,p), p);
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FpX_halfgcd (d = %ld)",degpol(b));
+      gerepileall(av,6, &a,&b,&u1,&v1,&u,&v);
+    }
+  }
+  return gerepilecopy(av, mkmat2(mkcol2(u,u1), mkcol2(v,v1)));
+}
+static GEN
+FpX_addmulmul(GEN u, GEN v, GEN x, GEN y, GEN p)
+{
+  return FpX_add(FpX_mul(u, x, p),FpX_mul(v, y, p), p);
+}
+
+static GEN
+FpXM_FpX_mul2(GEN M, GEN x, GEN y, GEN p)
+{
+  GEN res = cgetg(3, t_COL);
+  gel(res, 1) = FpX_addmulmul(gcoeff(M,1,1), gcoeff(M,1,2), x, y, p);
+  gel(res, 2) = FpX_addmulmul(gcoeff(M,2,1), gcoeff(M,2,2), x, y, p);
+  return res;
+}
+
+/*TODO: implement Strassen 7 multiplications formula (p is large) */
+static GEN
+FpXM_mul2(GEN M, GEN N, GEN p)
+{
+  GEN res = cgetg(3, t_MAT);
+  gel(res, 1) = FpXM_FpX_mul2(M,gcoeff(N,1,1),gcoeff(N,2,1),p);
+  gel(res, 2) = FpXM_FpX_mul2(M,gcoeff(N,1,2),gcoeff(N,2,2),p);
+  return res;
+}
+
+/* Return [0,1;1,-q]*M */
+static GEN
+FpX_FpXM_qmul(GEN q, GEN M, GEN p)
+{
+  GEN u, v, res = cgetg(3, t_MAT);
+  u = FpX_sub(gcoeff(M,1,1), FpX_mul(gcoeff(M,2,1), q, p), p);
+  gel(res,1) = mkcol2(gcoeff(M,2,1), u);
+  v = FpX_sub(gcoeff(M,1,2), FpX_mul(gcoeff(M,2,2), q, p), p);
+  gel(res,2) = mkcol2(gcoeff(M,2,2), v);
+  return res;
+}
+
+static GEN
+matid2_FpXM(long v)
+{
+  retmkmat2(mkcol2(pol_1(v),pol_0(v)),
+            mkcol2(pol_0(v),pol_1(v)));
+}
+
+static GEN
+FpX_halfgcd_split(GEN x, GEN y, GEN p)
+{
+  pari_sp av=avma;
+  GEN R, S, V;
+  GEN y1, r, q;
+  long l = lgpol(x), n = l>>1, k;
+  if (lgpol(y)<=n) return matid2_FpXM(varn(x));
+  R = FpX_halfgcd(RgX_shift_shallow(x,-n),RgX_shift_shallow(y,-n),p);
+  V = FpXM_FpX_mul2(R,x,y,p); y1 = gel(V,2);
+  if (lgpol(y1)<=n) return gerepilecopy(av, R);
+  q = FpX_divrem(gel(V,1), y1, p, &r);
+  k = 2*n-degpol(y1);
+  S = FpX_halfgcd(RgX_shift_shallow(y1,-k), RgX_shift_shallow(r,-k),p);
+  return gerepileupto(av, FpXM_mul2(S,FpX_FpXM_qmul(q,R,p),p));
+}
+
+/* Return M in GL_2(Fp[X]) such that:
+if [a',b']~=M*[a,b]~ then degpol(a')>= (lgpol(a)>>1) >degpol(b')
+*/
+
+static GEN
+FpX_halfgcd_i(GEN x, GEN y, GEN p)
+{
+  if (lg(x)<=FpX_HALFGCD_LIMIT) return FpX_halfgcd_basecase(x,y,p);
+  return FpX_halfgcd_split(x,y,p);
+}
+
+GEN
+FpX_halfgcd(GEN x, GEN y, GEN p)
+{
+  pari_sp av = avma;
+  GEN M,q,r;
+  if (lgefint(p)==3)
+  {
+    ulong pp = to_Flx(&x, &y, p);
+    M = FlxM_to_ZXM(Flx_halfgcd(x, y, pp));
+  }
+  else
+  {
+    if (!signe(x))
+    {
+      long v = varn(x);
+      retmkmat2(mkcol2(pol_0(v),pol_1(v)),
+                mkcol2(pol_1(v),pol_0(v)));
+    }
+    if (degpol(y)<degpol(x)) return FpX_halfgcd_i(x,y,p);
+    q = FpX_divrem(y,x,p,&r);
+    M = FpX_halfgcd_i(x,r,p);
+    gcoeff(M,1,1) = FpX_sub(gcoeff(M,1,1), FpX_mul(q, gcoeff(M,1,2), p), p);
+    gcoeff(M,2,1) = FpX_sub(gcoeff(M,2,1), FpX_mul(q, gcoeff(M,2,2), p), p);
+  }
+  return gerepilecopy(av, M);
+}
+
+static GEN
+FpX_gcd_basecase(GEN a, GEN b, GEN p)
+{
+  pari_sp av = avma, av0=avma, lim = stack_lim(av0,2);
+  while (signe(b))
+  {
+    GEN c;
+    if (low_stack(lim,stack_lim(av0,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FpX_gcd (d = %ld)",degpol(b));
+      gerepileall(av0,2, &a,&b);
+    }
+    av = avma; c = FpX_rem(a,b,p); a=b; b=c;
+  }
+  avma = av; return a;
+}
+
+GEN
+FpX_gcd(GEN x, GEN y, GEN p)
+{
+  pari_sp av = avma;
+  if (lgefint(p)==3)
+  {
+    ulong pp;
+    (void)new_chunk((lg(x) + lg(y)) << 2); /* scratch space */
+    pp = to_Flx(&x, &y, p);
+    x = Flx_gcd(x, y, pp);
+    avma = av; return Flx_to_ZX(x);
+  }
+  x = FpX_red(x, p);
+  y = FpX_red(y, p);
+  if (!signe(x)) return gerepileupto(av, y);
+  while (lg(y)>FpX_GCD_LIMIT)
+  {
+    GEN c;
+    if (lgpol(y)<=(lgpol(x)>>1))
+    {
+      GEN r = FpX_rem(x, y, p);
+      x = y; y = r;
+    }
+    c = FpXM_FpX_mul2(FpX_halfgcd(x,y, p), x, y, p);
+    x = gel(c,1); y = gel(c,2);
+    gerepileall(av,2,&x,&y);
+  }
+  return gerepileupto(av, FpX_gcd_basecase(x,y,p));
+}
+
+/*Return 1 if gcd can be computed
+ * else return a factor of p*/
+
+GEN
+FpX_gcd_check(GEN x, GEN y, GEN p)
+{
+  GEN a,b,c;
+  pari_sp av=avma;
+
+  a = FpX_red(x, p);
+  b = FpX_red(y, p);
+  while (signe(b))
+  {
+    GEN lead = leading_term(b);
+    GEN g = gcdii(lead,p);
+    if (!equali1(g)) return gerepileuptoint(av,g);
+    c = FpX_rem(a,b,p); a=b; b=c;
+  }
+  avma = av; return gen_1;
+}
+
+static GEN
+FpX_extgcd_basecase(GEN a, GEN b, GEN p, GEN *ptu, GEN *ptv)
+{
+  pari_sp av=avma, lim = stack_lim(av,2);
+  GEN u,v,d,d1,v1;
+  long vx = varn(a);
+  d = a; d1 = b;
+  v = pol_0(vx); v1 = pol_1(vx);
+  while (signe(d1))
+  {
+    GEN r, q = FpX_divrem(d,d1,p, &r);
+    v = FpX_sub(v,FpX_mul(q,v1,p),p);
+    u=v; v=v1; v1=u;
+    u=r; d=d1; d1=u;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FpX_extgcd (d = %ld)",degpol(d));
+      gerepileall(av,5, &d,&d1,&u,&v,&v1);
+    }
+  }
+  if (ptu) *ptu = FpX_div(FpX_sub(d,FpX_mul(b,v,p),p),a,p);
+  *ptv = v; return d;
+}
+
+static GEN
+FpX_extgcd_halfgcd(GEN x, GEN y, GEN p, GEN *ptu, GEN *ptv)
+{
+  pari_sp av=avma;
+  GEN u,v,R = matid2_FpXM(varn(x));
+  while (lg(y)>FpX_EXTGCD_LIMIT)
+  {
+    GEN M, c;
+    if (lgpol(y)<=(lgpol(x)>>1))
+    {
+      GEN r, q = FpX_divrem(x, y, p, &r);
+      x = y; y = r;
+      R = FpX_FpXM_qmul(q, R, p);
+    }
+    M = FpX_halfgcd(x,y, p);
+    c = FpXM_FpX_mul2(M, x,y, p);
+    R = FpXM_mul2(M, R, p);
+    x = gel(c,1); y = gel(c,2);
+    gerepileall(av,3,&x,&y,&R);
+  }
+  y = FpX_extgcd_basecase(x,y,p,&u,&v);
+  if (ptu) *ptu = FpX_addmulmul(u,v,gcoeff(R,1,1),gcoeff(R,2,1),p);
+  *ptv = FpX_addmulmul(u,v,gcoeff(R,1,2),gcoeff(R,2,2),p);
+  return y;
+}
+
+/* x and y in Z[X], return lift(gcd(x mod p, y mod p)). Set u and v st
+ * ux + vy = gcd (mod p) */
+GEN
+FpX_extgcd(GEN x, GEN y, GEN p, GEN *ptu, GEN *ptv)
+{
+  GEN d;
+  pari_sp ltop=avma;
+  if (lgefint(p)==3)
+  {
+    ulong pp = to_Flx(&x, &y, p);
+    d = Flx_extgcd(x,y, pp, ptu,ptv);
+    d = Flx_to_ZX(d);
+    if (ptu) *ptu=Flx_to_ZX(*ptu);
+    *ptv=Flx_to_ZX(*ptv);
+  }
+  else
+  {
+    x = FpX_red(x, p);
+    y = FpX_red(y, p);
+    if (lg(y)>FpX_EXTGCD_LIMIT)
+      d = FpX_extgcd_halfgcd(x, y, p, ptu, ptv);
+    else
+      d = FpX_extgcd_basecase(x, y, p, ptu, ptv);
+  }
+  gerepileall(ltop,ptu?3:2,&d,ptv,ptu);
+  return d;
+}
+
+GEN
+FpX_rescale(GEN P, GEN h, GEN p)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l,t_POL), hi = h;
+  Q[l-1] = P[l-1];
+  for (i=l-2; i>=2; i--)
+  {
+    gel(Q,i) = Fp_mul(gel(P,i), hi, p);
+    if (i == 2) break;
+    hi = Fp_mul(hi,h, p);
+  }
+  Q[1] = P[1]; return Q;
+}
+
+GEN
+FpX_deriv(GEN x, GEN p) { return FpX_red(ZX_deriv(x), p); }
+
+int
+FpX_is_squarefree(GEN f, GEN p)
+{
+  pari_sp av = avma;
+  GEN z = FpX_gcd(f,FpX_deriv(f,p),p);
+  avma = av;
+  return degpol(z)==0;
+}
+
+GEN
+random_FpX(long d1, long v, GEN p)
+{
+  long i, d = d1+2;
+  GEN y = cgetg(d,t_POL); y[1] = evalsigne(1) | evalvarn(v);
+  for (i=2; i<d; i++) gel(y,i) = randomi(p);
+  return FpX_renormalize(y,d);
+}
+
+/* Evaluation in Fp
+ * x a ZX and y an Fp, return x(y) mod p
+ *
+ * If p is very large (several longs) and x has small coefficients(<<p),
+ * then Brent & Kung algorithm is faster. */
+GEN
+FpX_eval(GEN x,GEN y,GEN p)
+{
+  pari_sp av;
+  GEN p1,r,res;
+  long j, i=lg(x)-1;
+  if (i<=2)
+    return (i==2)? modii(gel(x,2),p): gen_0;
+  res=cgeti(lgefint(p));
+  av=avma; p1=gel(x,i);
+  /* specific attention to sparse polynomials (see poleval)*/
+  /*You've guessed it! It's a copy-paste(tm)*/
+  for (i--; i>=2; i=j-1)
+  {
+    for (j=i; !signe(gel(x,j)); j--)
+      if (j==2)
+      {
+        if (i!=j) y = Fp_powu(y,i-j+1,p);
+        p1=mulii(p1,y);
+        goto fppoleval;/*sorry break(2) no implemented*/
+      }
+    r = (i==j)? y: Fp_powu(y,i-j+1,p);
+    p1 = Fp_addmul(gel(x,j), p1, r, p);
+    if ((i & 7) == 0) { affii(p1, res); p1 = res; avma = av; }
+  }
+ fppoleval:
+  modiiz(p1,p,res);
+  avma = av; return res;
+}
+
+/* Tz=Tx*Ty where Tx and Ty coprime
+ * return lift(chinese(Mod(x*Mod(1,p),Tx*Mod(1,p)),Mod(y*Mod(1,p),Ty*Mod(1,p))))
+ * if Tz is NULL it is computed
+ * As we do not return it, and the caller will frequently need it,
+ * it must compute it and pass it.
+ */
+GEN
+FpX_chinese_coprime(GEN x,GEN y,GEN Tx,GEN Ty,GEN Tz,GEN p)
+{
+  pari_sp av = avma;
+  GEN ax,p1;
+  ax = FpX_mul(FpXQ_inv(Tx,Ty,p), Tx,p);
+  p1 = FpX_mul(ax, FpX_sub(y,x,p),p);
+  p1 = FpX_add(x,p1,p);
+  if (!Tz) Tz=FpX_mul(Tx,Ty,p);
+  p1 = FpX_rem(p1,Tz,p);
+  return gerepileupto(av,p1);
+}
+
+/* Res(A,B) = Res(B,R) * lc(B)^(a-r) * (-1)^(ab), with R=A%B, a=deg(A) ...*/
+GEN
+FpX_resultant(GEN a, GEN b, GEN p)
+{
+  long da,db,dc;
+  pari_sp av, lim;
+  GEN c,lb, res = gen_1;
+
+  if (!signe(a) || !signe(b)) return gen_0;
+  da = degpol(a);
+  db = degpol(b);
+  if (db > da)
+  {
+    swapspec(a,b, da,db);
+    if (both_odd(da,db)) res = subii(p, res);
+  }
+  if (!da) return gen_1; /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
+  av = avma; lim = stack_lim(av,2);
+  while (db)
+  {
+    lb = gel(b,db+2);
+    c = FpX_rem(a,b, p);
+    a = b; b = c; dc = degpol(c);
+    if (dc < 0) { avma = av; return NULL; }
+
+    if (both_odd(da,db)) res = subii(p, res);
+    if (!equali1(lb)) res = Fp_mul(res, Fp_powu(lb, da - dc, p), p);
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FpX_resultant (da = %ld)",da);
+      gerepileall(av,3, &a,&b,&res);
+    }
+    da = db; /* = degpol(a) */
+    db = dc; /* = degpol(b) */
+  }
+  res = Fp_mul(res, Fp_powu(gel(b,2), da, p), p);
+  return gerepileuptoint(av, res);
+}
+
+GEN
+FpX_disc(GEN x, GEN p)
+{
+  pari_sp av = avma;
+  GEN L, D = FpX_resultant(x, FpX_deriv(x,p), p);
+  if (!D || !signe(D)) return gen_0;
+  L = leading_term(x); if (!equali1(L)) D = Fp_div(D,L,p);
+  if (degpol(x) & 2) D = Fp_neg(D,p);
+  return gerepileuptoint(av, D);
+}
+
+static GEN _FpX_mul(void *p,GEN a,GEN b){return FpX_mul(a,b,(GEN)p);}
+GEN
+FpXV_prod(GEN V, GEN p)
+{
+  return divide_conquer_assoc(V, (void *)p, &_FpX_mul);
+}
+
+GEN
+FpV_roots_to_pol(GEN V, GEN p, long v)
+{
+  pari_sp ltop=avma;
+  long i;
+  GEN g=cgetg(lg(V),t_VEC);
+  for(i=1;i<lg(V);i++)
+    gel(g,i) = deg1pol_shallow(gen_1,modii(negi(gel(V,i)),p),v);
+  return gerepileupto(ltop,FpXV_prod(g,p));
+}
+
+/* invert all elements of x mod p using Montgomery's multi-inverse trick.
+ * Not stack-clean. */
+GEN
+FpV_inv(GEN x, GEN p)
+{
+  long i, lx = lg(x);
+  GEN u, y = cgetg(lx, t_VEC);
+
+  gel(y,1) = gel(x,1);
+  for (i=2; i<lx; i++) gel(y,i) = Fp_mul(gel(y,i-1), gel(x,i), p);
+
+  u = Fp_inv(gel(y,--i), p);
+  for ( ; i > 1; i--)
+  {
+    gel(y,i) = Fp_mul(u, gel(y,i-1), p);
+    u = Fp_mul(u, gel(x,i), p); /* u = 1 / (x[1] ... x[i-1]) */
+  }
+  gel(y,1) = u; return y;
+}
+GEN
+FqV_inv(GEN x, GEN T, GEN p)
+{
+  long i, lx = lg(x);
+  GEN u, y = cgetg(lx, t_VEC);
+
+  gel(y,1) = gel(x,1);
+  for (i=2; i<lx; i++) gel(y,i) = Fq_mul(gel(y,i-1), gel(x,i), T,p);
+
+  u = Fq_inv(gel(y,--i), T,p);
+  for ( ; i > 1; i--)
+  {
+    gel(y,i) = Fq_mul(u, gel(y,i-1), T,p);
+    u = Fq_mul(u, gel(x,i), T,p); /* u = 1 / (x[1] ... x[i-1]) */
+  }
+  gel(y,1) = u; return y;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                      Barrett reduction                            **/
+/**                                                                   **/
+/***********************************************************************/
+
+static GEN
+FpX_invBarrett_basecase(GEN T, GEN p)
+{
+  long i, l=lg(T)-1, lr = l-1, k;
+  GEN r=cgetg(lr, t_POL); r[1]=T[1];
+  gel(r,2) = gen_1;
+  for (i=3; i<lr; i++)
+  {
+    pari_sp av = avma;
+    GEN u = gel(T,l-i+2);
+    for (k=3; k<i; k++)
+      u = addii(u, mulii(gel(T,l-i+k), gel(r,k)));
+    gel(r,i) = gerepileupto(av, modii(negi(u), p));
+  }
+  return FpX_renormalize(r,lr);
+}
+
+/* Return new lgpol */
+static long
+ZX_lgrenormalizespec(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>=0; i--)
+    if (signe(gel(x,i))) break;
+  return i+1;
+}
+
+INLINE GEN
+FpX_recipspec(GEN x, long l, long n)
+{
+  return RgX_recipspec_shallow(x, l, n);
+}
+
+static GEN
+FpX_invBarrett_Newton(GEN T, GEN p)
+{
+  pari_sp av = avma;
+  long nold, lx, lz, lq, l = degpol(T), i, lQ;
+  GEN q, y, z, x = cgetg(l+2, t_POL) + 2;
+  ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
+  for (i=0;i<l;i++) gel(x,i) = gen_0;
+  q = FpX_recipspec(T+2,l+1,l+1); lQ = lgpol(q); q+=2;
+  /* We work on _spec_ FpX's, all the l[xzq] below are lgpol's */
+
+  /* initialize */
+  gel(x,0) = Fp_inv(gel(q,0), p);
+  if (lQ>1) gel(q,1) = Fp_red(gel(q,1), p);
+  if (lQ>1 && signe(gel(q,1)))
+  {
+    GEN u = gel(q, 1);
+    if (!equali1(gel(x,0))) u = Fp_mul(u, Fp_sqr(gel(x,0), p), p);
+    gel(x,1) = Fp_neg(u, p); lx = 2;
+  }
+  else
+    lx = 1;
+  nold = 1;
+  for (; mask > 1; )
+  { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
+    long i, lnew, nnew = nold << 1;
+
+    if (mask & 1) nnew--;
+    mask >>= 1;
+
+    lnew = nnew + 1;
+    lq = ZX_lgrenormalizespec(q, minss(lQ,lnew));
+    z = FpX_mulspec(x, q, p, lx, lq); /* FIXME: high product */
+    lz = lgpol(z); if (lz > lnew) lz = lnew;
+    z += 2;
+    /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
+    for (i = nold; i < lz; i++) if (signe(gel(z,i))) break;
+    nold = nnew;
+    if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
+
+    /* z + i represents (x*q - 1) / t^i */
+    lz = ZX_lgrenormalizespec (z+i, lz-i);
+    z = FpX_mulspec(x, z+i, p, lx, lz); /* FIXME: low product */
+    lz = lgpol(z); z += 2;
+    if (lz > lnew-i) lz = ZX_lgrenormalizespec(z, lnew-i);
+
+    lx = lz+ i;
+    y  = x + i; /* x -= z * t^i, in place */
+    for (i = 0; i < lz; i++) gel(y,i) = Fp_neg(gel(z,i), p);
+  }
+  x -= 2; setlg(x, lx + 2); x[1] = T[1];
+  return gerepilecopy(av, x);
+}
+
+/* 1/polrecip(T)+O(x^(deg(T)-1)) */
+GEN
+FpX_invBarrett(GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  long l = lg(T);
+  GEN r;
+  if (l<5) return pol_0(varn(T));
+  if (l<=FpX_INVBARRETT_LIMIT)
+  {
+    GEN c = gel(T,l-1), ci=gen_1;
+    if (!equali1(c))
+    {
+      ci = Fp_inv(c, p);
+      T = FpX_Fp_mul(T, ci, p);
+      r = FpX_invBarrett_basecase(T, p);
+      r = FpX_Fp_mul(r, ci, p);
+    } else
+      r = FpX_invBarrett_basecase(T, p);
+  }
+  else
+    r = FpX_invBarrett_Newton(T, p);
+  return gerepileupto(ltop, r);
+}
+
+GEN
+FpX_get_red(GEN T, GEN p)
+{
+  if (typ(T)==t_POL && lg(T)>FpX_BARRETT_LIMIT)
+    retmkvec2(FpX_invBarrett(T,p),T);
+  return T;
+}
+
+/* Compute x mod T where 2 <= degpol(T) <= l+1 <= 2*(degpol(T)-1)
+ * and mg is the Barrett inverse of T. */
+static GEN
+FpX_divrem_Barrettspec(GEN x, long l, GEN mg, GEN T, GEN p, GEN *pr)
+{
+  GEN q, r;
+  long lt = degpol(T); /*We discard the leading term*/
+  long ld, lm, lT, lmg;
+  ld = l-lt;
+  lm = minss(ld, lgpol(mg));
+  lT  = ZX_lgrenormalizespec(T+2,lt);
+  lmg = ZX_lgrenormalizespec(mg+2,lm);
+  q = FpX_recipspec(x+lt,ld,ld);              /* q = rec(x)     lq<=ld*/
+  q = FpX_mulspec(q+2,mg+2,p,lgpol(q),lmg);    /* q = rec(x) * mg lq<=ld+lm*/
+  q = FpX_recipspec(q+2,minss(ld,lgpol(q)),ld);/* q = rec (rec(x) * mg) lq<=ld*/
+  if (!pr) return q;
+  r = FpX_mulspec(q+2,T+2,p,lgpol(q),lT);      /* r = q*pol        lr<=ld+lt*/
+  r = FpX_subspec(x,r+2,p,lt,minss(lt,lgpol(r)));/* r = x - r   lr<=lt */
+  if (pr == ONLY_REM) return r;
+  *pr = r; return q;
+}
+
+static GEN
+FpX_divrem_Barrett_noGC(GEN x, GEN mg, GEN T, GEN p, GEN *pr)
+{
+  GEN q = NULL, r = FpX_red(x, p);
+  long l = lgpol(r), lt = degpol(T), lm = 2*lt-1;
+  long i;
+  if (l <= lt)
+  {
+    if (pr == ONLY_REM) return r;
+    if (pr == ONLY_DIVIDES) return signe(x)? NULL: pol_0(varn(x));
+    if (pr) *pr = r;
+    return pol_0(varn(T));
+  }
+  if (lt <= 1)
+    return FpX_divrem_basecase(r,T,p,pr);
+  if (pr != ONLY_REM && l>lm)
+  {
+    q = cgetg(l-lt+2, t_POL);
+    for (i=0;i<l-lt;i++) gel(q+2,i) = gen_0;
+  }
+  while (l>lm)
+  {
+    GEN zr, zq = FpX_divrem_Barrettspec(r+2+l-lm,lm,mg,T,p,&zr);
+    long lz = lgpol(zr);
+    if (pr != ONLY_REM)
+    {
+      long lq = lgpol(zq);
+      for(i=0; i<lq; i++) gel(q+2+l-lm,i) = gel(zq,2+i);
+    }
+    for(i=0; i<lz; i++) gel(r+2+l-lm,i) = gel(zr,2+i);
+    l = l-lm+lz;
+  }
+  if (pr != ONLY_REM)
+  {
+    if (l > lt)
+    {
+      GEN zq = FpX_divrem_Barrettspec(r+2,l,mg,T,p,&r);
+      if (!q) q = zq;
+      else
+      {
+        long lq = lgpol(zq);
+        for(i=0; i<lq; i++) gel(q+2,i) = gel(zq,2+i);
+      }
+    }
+    else
+      r = FpX_renormalize(r, l+2);
+  }
+  else
+  {
+    if (l > lt)
+      r = FpX_divrem_Barrettspec(r+2, l, mg, T, p, ONLY_REM);
+    else
+      r = FpX_renormalize(r, l+2);
+    r[1] = x[1]; return FpX_renormalize(r, lg(r));
+  }
+  if (pr) { r[1] = x[1]; r = FpX_renormalize(r, lg(r)); }
+  q[1] = x[1]; q = FpX_renormalize(q, lg(q));
+  if (pr == ONLY_DIVIDES) return signe(r)? NULL: q;
+  if (pr) *pr = r;
+  return q;
+}
+
+GEN
+FpX_divrem(GEN x, GEN T, GEN p, GEN *pr)
+{
+  GEN B, y = get_FpX_red(T, &B);
+  long dy = degpol(y), dx = degpol(x), d = dx-dy;
+  if (pr==ONLY_REM) return FpX_rem(x, y, p);
+  if (!B && d+3 < FpX_DIVREM_BARRETT_LIMIT)
+    return FpX_divrem_basecase(x,y,p,pr);
+  else if (lgefint(p)==3)
+  {
+    pari_sp av = avma;
+    ulong pp = to_Flxq(&x, &T, p);
+    GEN z = Flx_divrem(x, T, pp, pr);
+    if (!z) return NULL;
+    if (!pr || pr == ONLY_DIVIDES)
+      return Flx_to_ZX_inplace(gerepileuptoleaf(av, z));
+    z = Flx_to_ZX(z);
+    *pr = Flx_to_ZX(*pr);
+    gerepileall(av, 2, &z, pr);
+    return z;
+  } else
+  {
+    pari_sp av=avma;
+    GEN mg = B? B: FpX_invBarrett(y, p);
+    GEN q1 = FpX_divrem_Barrett_noGC(x,mg,y,p,pr);
+    if (!q1) {avma=av; return NULL;}
+    if (!pr || pr==ONLY_DIVIDES) return gerepilecopy(av, q1);
+    gerepileall(av,2,&q1,pr);
+    return q1;
+  }
+}
+
+GEN
+FpX_rem(GEN x, GEN T, GEN p)
+{
+  GEN B, y = get_FpX_red(T, &B);
+  long dy = degpol(y), dx = degpol(x), d = dx-dy;
+  if (d < 0) return FpX_red(x,p);
+  if (!B && d+3 < FpX_REM_BARRETT_LIMIT)
+    return FpX_divrem_basecase(x,y,p,ONLY_REM);
+  else if (lgefint(p)==3)
+  {
+    pari_sp av = avma;
+    ulong pp = to_Flxq(&x, &T, p);
+    return Flx_to_ZX_inplace(gerepileuptoleaf(av, Flx_rem(x, T, pp)));
+  } else
+  {
+    pari_sp av = avma;
+    GEN mg = B? B: FpX_invBarrett(y, p);
+    return gerepileupto(av, FpX_divrem_Barrett_noGC(x, mg, y, p, ONLY_REM));
+  }
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                              FpXQ                                 **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* FpXQ are elements of Fp[X]/(T), represented by FpX*/
+
+GEN
+FpXQ_red(GEN x, GEN T, GEN p)
+{
+  GEN z = FpX_red(x,p);
+  return FpX_rem(z, T,p);
+}
+
+GEN
+FpXQ_mul(GEN x,GEN y,GEN T,GEN p)
+{
+  GEN z = FpX_mul(x,y,p);
+  return FpX_rem(z, T, p);
+}
+
+GEN
+FpXQ_sqr(GEN x, GEN T, GEN p)
+{
+  GEN z = FpX_sqr(x,p);
+  return FpX_rem(z, T, p);
+}
+
+/* Inverse of x in Z/pZ[X]/(pol) or NULL if inverse doesn't exist
+ * return lift(1 / (x mod (p,pol))) */
+GEN
+FpXQ_invsafe(GEN x, GEN y, GEN p)
+{
+  GEN V, z = FpX_extgcd(get_FpX_mod(y), x, p, NULL, &V);
+  if (degpol(z)) return NULL;
+  z = Fp_invsafe(gel(z,2), p);
+  if (!z) return NULL;
+  return FpX_Fp_mul(V, z, p);
+}
+
+GEN
+FpXQ_inv(GEN x,GEN T,GEN p)
+{
+  pari_sp av = avma;
+  GEN U = FpXQ_invsafe(x, T, p);
+  if (!U) pari_err_INV("FpXQ_inv",x);
+  return gerepileupto(av, U);
+}
+
+GEN
+FpXQ_div(GEN x,GEN y,GEN T,GEN p)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, FpXQ_mul(x,FpXQ_inv(y,T,p),T,p));
+}
+
+struct _FpXQ {
+  GEN T, p, mg, aut;
+};
+
+static GEN
+_FpXQ_add(void *data, GEN x, GEN y)
+{
+  (void) data;
+  return ZX_add(x, y);
+}
+static GEN
+_FpXQ_cmul(void *data, GEN P, long a, GEN x)
+{
+  (void) data;
+  return ZX_Z_mul(x, gel(P,a+2));
+}
+static GEN
+_FpXQ_sqr(void *data, GEN x)
+{
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  return FpXQ_sqr(x, D->T, D->p);
+}
+static GEN
+_FpXQ_mul(void *data, GEN x, GEN y)
+{
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  return FpXQ_mul(x,y, D->T, D->p);
+}
+static GEN
+_FpXQ_zero(void *data)
+{
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  return pol_0(get_FpX_var(D->T));
+}
+static GEN
+_FpXQ_one(void *data)
+{
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  return pol_1(get_FpX_var(D->T));
+}
+static GEN
+_FpXQ_red(void *data, GEN x)
+{
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  return FpX_red(x,D->p);
+}
+
+static struct bb_algebra FpXQ_algebra = { _FpXQ_red,_FpXQ_add,_FpXQ_mul,_FpXQ_sqr,_FpXQ_one,_FpXQ_zero };
+
+/* x,pol in Z[X], p in Z, n in Z, compute lift(x^n mod (p, pol)) */
+GEN
+FpXQ_pow(GEN x, GEN n, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  pari_sp av;
+  long s = signe(n);
+  GEN y;
+  if (!s) return pol_1(varn(x));
+  if (is_pm1(n)) /* +/- 1 */
+    return (s < 0)? FpXQ_inv(x,T,p): FpXQ_red(x,T,p);
+  av = avma;
+  if (!is_bigint(p))
+  {
+    ulong pp = to_Flxq(&x, &T, p);
+    y = Flxq_pow(x, n, T, pp);
+    return Flx_to_ZX_inplace(gerepileuptoleaf(av, y));
+  }
+  if (s < 0) x = FpXQ_inv(x,T,p);
+  D.p = p; D.T = FpX_get_red(T,p);
+  y = gen_pow(x, n, (void*)&D, &_FpXQ_sqr, &_FpXQ_mul);
+  return gerepileupto(av, y);
+}
+
+GEN /*Assume n is very small*/
+FpXQ_powu(GEN x, ulong n, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  pari_sp av;
+  GEN y;
+  if (!n) return pol_1(varn(x));
+  if (n==1) return FpXQ_red(x,T,p);
+  av = avma;
+  if (!is_bigint(p))
+  {
+    ulong pp = to_Flxq(&x, &T, p);
+    y = Flxq_powu(x, n, T, pp);
+    return Flx_to_ZX_inplace(gerepileuptoleaf(av, y));
+  }
+  D.T = FpX_get_red(T, p); D.p = p;
+  y = gen_powu(x, n, (void*)&D, &_FpXQ_sqr, &_FpXQ_mul);
+  return gerepileupto(av, y);
+}
+
+/* generates the list of powers of x of degree 0,1,2,...,l*/
+GEN
+FpXQ_powers(GEN x, long l, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  int use_sqr;
+  if (l>2 && lgefint(p) == 3) {
+    pari_sp av = avma;
+    ulong pp = to_Flxq(&x, &T, p);
+    GEN z = FlxV_to_ZXV(Flxq_powers(x, l, T, pp));
+    return gerepileupto(av, z);
+  }
+  use_sqr = (degpol(x)<<1)>=get_FpX_degree(T);
+  D.T = FpX_get_red(T,p); D.p = p;
+  return gen_powers(x, l, use_sqr, (void*)&D, &_FpXQ_sqr, &_FpXQ_mul,&_FpXQ_one);
+}
+
+GEN
+FpXQ_matrix_pow(GEN y, long n, long m, GEN P, GEN l)
+{
+  return RgXV_to_RgM(FpXQ_powers(y,m-1,P,l),n);
+}
+
+GEN
+FpX_FpXQV_eval(GEN Q, GEN x, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  D.T = FpX_get_red(T,p); D.p = p;
+  return gen_bkeval_powers(Q,degpol(Q),x,(void*)&D,&FpXQ_algebra,_FpXQ_cmul);
+}
+
+GEN
+FpX_FpXQ_eval(GEN Q, GEN x, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  int use_sqr;
+  if (lgefint(p) == 3)
+  {
+    pari_sp av = avma;
+    ulong pp = to_Flxq(&x, &T, p);
+    GEN z = Flx_Flxq_eval(ZX_to_Flx(Q, pp), x, T, pp);
+    return Flx_to_ZX_inplace(gerepileuptoleaf(av, z));
+  }
+  use_sqr = (degpol(x)<<1) >= get_FpX_degree(T);
+  D.T = FpX_get_red(T,p); D.p = p;
+  return gen_bkeval(Q,degpol(Q),x,use_sqr,(void*)&D,&FpXQ_algebra,_FpXQ_cmul);
+}
+
+GEN
+FpXQ_autpowers(GEN aut, long f, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  long n = get_FpX_degree(T);
+  long i, nautpow = brent_kung_optpow(n-1,f-2,1);
+  long v = get_FpX_var(T);
+  GEN autpow, V;
+  T = FpX_get_red(T, p);
+  autpow = FpXQ_powers(aut, nautpow,T,p);
+  V = cgetg(f + 2, t_VEC);
+  gel(V,1) = pol_x(v); if (f==0) return gerepileupto(av, V);
+  gel(V,2) = gcopy(aut);
+  for (i = 3; i <= f+1; i++)
+    gel(V,i) = FpX_FpXQV_eval(gel(V,i-1),autpow,T,p);
+  return gerepileupto(av, V);
+}
+
+static GEN
+FpXQ_autpow_sqr(void *E, GEN x)
+{
+  struct _FpXQ *D = (struct _FpXQ*)E;
+  return FpX_FpXQ_eval(x, x, D->T, D->p);
+}
+
+static GEN
+FpXQ_autpow_mul(void *E, GEN x, GEN y)
+{
+  struct _FpXQ *D = (struct _FpXQ*)E;
+  return FpX_FpXQ_eval(x, y, D->T, D->p);
+}
+
+GEN
+FpXQ_autpow(GEN x, ulong n, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  D.T = FpX_get_red(T, p); D.p = p;
+  if (n==0) return pol_x(varn(x));
+  if (n==1) return ZX_copy(x);
+  return gen_powu(x,n,(void*)&D,FpXQ_autpow_sqr,FpXQ_autpow_mul);
+}
+
+static GEN
+FpXQ_autsum_mul(void *E, GEN x, GEN y)
+{
+  struct _FpXQ *D = (struct _FpXQ*)E;
+  GEN phi1 = gel(x,1), a1 = gel(x,2);
+  GEN phi2 = gel(y,1), a2 = gel(y,2);
+  ulong d = brent_kung_optpow(maxss(degpol(phi1),degpol(a1)),2,1);
+  GEN V2 = FpXQ_powers(phi2,d,D->T,D->p);
+  GEN phi3 = FpX_FpXQV_eval(phi1,V2,D->T,D->p);
+  GEN aphi = FpX_FpXQV_eval(a1,V2,D->T,D->p);
+  GEN a3 = FpXQ_mul(aphi,a2,D->T,D->p);
+  return mkvec2(phi3, a3);
+}
+static GEN
+FpXQ_autsum_sqr(void *E, GEN x)
+{ return FpXQ_autsum_mul(E, x, x); }
+
+GEN
+FpXQ_autsum(GEN x, ulong n, GEN T, GEN p)
+{
+  struct _FpXQ D;
+  D.T = FpX_get_red(T, p); D.p = p;
+  return gen_powu(x,n,(void*)&D,FpXQ_autsum_sqr,FpXQ_autsum_mul);
+}
+
+static long
+bounded_order(GEN p, GEN b, long k)
+{
+  long i;
+  GEN a=modii(p,b);
+  for(i=1;i<k;i++)
+  {
+    if (equali1(a))
+      return i;
+    a = Fp_mul(a,p,b);
+  }
+  return 0;
+}
+
+/*
+  n = (p^d-a)\b
+  b = bb*p^vb
+  p^k = 1 [bb]
+  d = m*k+r+vb
+  u = (p^k-1)/bb;
+  v = (p^(r+vb)-a)/b;
+  w = (p^(m*k)-1)/(p^k-1)
+  n = p^r*w*u+v
+  w*u = p^vb*(p^(m*k)-1)/b
+  n = p^(r+vb)*(p^(m*k)-1)/b+(p^(r+vb)-a)/b
+*/
+
+static GEN
+FpXQ_pow_Frobenius(GEN x, GEN n, GEN aut, GEN T, GEN p)
+{
+  pari_sp av=avma;
+  long d = get_FpX_degree(T);
+  GEN an = absi(n), z, q;
+  if (cmpii(an,p)<0 || cmpis(an,d)<=0)
+    return FpXQ_pow(x, n, T, p);
+  q = powiu(p, d);
+  if (dvdii(q, n))
+  {
+    long vn = logint(an,p,NULL)-1;
+    GEN autvn = vn==1 ? aut: FpXQ_autpow(aut,vn,T,p);
+    z = FpX_FpXQ_eval(x,autvn,T,p);
+  } else
+  {
+    GEN b = diviiround(q, an), a = subii(q, mulii(an,b));
+    GEN bb, u, v, autk;
+    long vb = Z_pvalrem(b,p,&bb);
+    long m, r, k = is_pm1(bb) ? 1 : bounded_order(p,bb,d);
+    if (!k || d-vb<k) return FpXQ_pow(x,n, T, p);
+    m = (d-vb)/k; r = (d-vb)%k;
+    u = diviiexact(subis(powiu(p,k),1),bb);
+    v = diviiexact(subii(powiu(p,r+vb),a),b);
+    autk = k==1 ? aut: FpXQ_autpow(aut,k,T,p);
+    if (r)
+    {
+      GEN autr = r==1 ? aut: FpXQ_autpow(aut,r,T,p);
+      z = FpX_FpXQ_eval(x,autr,T,p);
+    } else z = x;
+    if (m > 1) z = gel(FpXQ_autsum(mkvec2(autk, z), m, T, p), 2);
+    if (!is_pm1(u)) z = FpXQ_pow(z, u, T, p);
+    if (signe(v)) z = FpXQ_mul(z, FpXQ_pow(x, v, T, p), T, p);
+  }
+  return gerepileupto(av,signe(n)>0 ? z : FpXQ_inv(z,T,p));
+}
+
+/* assume T irreducible mod p */
+int
+FpXQ_issquare(GEN x, GEN T, GEN p)
+{
+  pari_sp av;
+  long res;
+  if (lg(x) == 2 || equalui(2, p)) return 1;
+  if (lg(x) == 3) return Fq_issquare(gel(x,2), T, p);
+  /* Ng = g^((q-1)/(p-1)) */
+  av = avma; res = kronecker(FpX_resultant(T,x,p), p) == 1;
+  avma = av; return res;
+}
+int
+Fp_issquare(GEN x, GEN p)
+{
+  if (equalui(2, p)) return 1;
+  return kronecker(x, p) == 1;
+}
+/* assume T irreducible mod p */
+int
+Fq_issquare(GEN x, GEN T, GEN p)
+{
+  if (typ(x) != t_INT) return FpXQ_issquare(x, T, p);
+  return (T && ! odd(get_FpX_degree(T))) || Fp_issquare(x, p);
+}
+
+static GEN
+famat_Z_gcd(GEN M, GEN n)
+{
+  pari_sp av=avma;
+  long i, j, l=lgcols(M);
+  GEN F=cgetg(3,t_MAT);
+  gel(F,1)=cgetg(l,t_COL);
+  gel(F,2)=cgetg(l,t_COL);
+  for (i=1, j=1; i<l; i++)
+  {
+    GEN p = gcoeff(M,i,1);
+    GEN e = gminsg(Z_pval(n,p),gcoeff(M,i,2));
+    if (signe(e))
+    {
+      gcoeff(F,j,1)=p;
+      gcoeff(F,j,2)=e;
+      j++;
+    }
+  }
+  setlg(gel(F,1),j); setlg(gel(F,2),j);
+  return gerepilecopy(av,F);
+}
+
+/* discrete log in FpXQ for a in Fp^*, g in FpXQ^* of order ord */
+GEN
+Fp_FpXQ_log(GEN a, GEN g, GEN o, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN q,n_q,ord,ordp, op;
+
+  if (equali1(a)) return gen_0;
+  /* p > 2 */
+
+  ordp = subis(p, 1); /* even */
+  ord  = dlog_get_ord(o);
+  if (!ord) ord = T? subis(powiu(p, get_FpX_degree(T)), 1): ordp;
+  if (equalii(a, ordp)) /* -1 */
+    return gerepileuptoint(av, shifti(ord,-1));
+  ordp = gcdii(ordp,ord);
+  op = typ(o)==t_MAT ? famat_Z_gcd(o,ordp) : ordp;
+
+  q = NULL;
+  if (T)
+  { /* we want < g > = Fp^* */
+    if (!equalii(ord,ordp)) {
+      q = diviiexact(ord,ordp);
+      g = FpXQ_pow(g,q,T,p);
+    }
+    g = constant_term(g);
+  }
+  n_q = Fp_log(a,g,op,p);
+  if (lg(n_q)==1) return gerepileuptoleaf(av, n_q);
+  if (q) n_q = mulii(q, n_q);
+  return gerepileuptoint(av, n_q);
+}
+
+static GEN
+_FpXQ_pow(void *data, GEN x, GEN n)
+{
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  return FpXQ_pow_Frobenius(x,n, D->aut, D->T, D->p);
+}
+
+static GEN
+_FpXQ_rand(void *data)
+{
+  pari_sp av=avma;
+  struct _FpXQ *D = (struct _FpXQ*)data;
+  GEN z;
+  do
+  {
+    avma=av;
+    z=random_FpX(get_FpX_degree(D->T),get_FpX_var(D->T),D->p);
+  } while (!signe(z));
+  return z;
+}
+
+static GEN
+_FpXQ_easylog(void *E, GEN a, GEN g, GEN ord)
+{
+  struct _FpXQ *s=(struct _FpXQ*) E;
+  if (degpol(a)) return NULL;
+  return Fp_FpXQ_log(constant_term(a),g,ord,s->T,s->p);
+}
+
+static const struct bb_group FpXQ_star={_FpXQ_mul,_FpXQ_pow,_FpXQ_rand,hash_GEN,ZX_equal,ZX_equal1,_FpXQ_easylog};
+
+const struct bb_group *get_FpXQ_star(void **E, GEN T, GEN p)
+{
+  struct _FpXQ *e = (struct _FpXQ *) stack_malloc(sizeof(struct _FpXQ));
+  e->T = T; e->p  = p; e->aut =  FpXQ_pow(pol_x(get_FpX_var(T)), p, T, p);
+  *E = (void*)e; return &FpXQ_star;
+}
+
+GEN
+FpXQ_order(GEN a, GEN ord, GEN T, GEN p)
+{
+  if (lgefint(p)==3)
+  {
+    pari_sp av=avma;
+    ulong pp = to_Flxq(&a, &T, p);
+    GEN z = Flxq_order(a, ord, T, pp);
+    return gerepileuptoint(av,z);
+  }
+  else
+  {
+    void *E;
+    const struct bb_group *S = get_FpXQ_star(&E,T,p);
+    return gen_order(a,ord,E,S);
+  }
+}
+
+GEN
+FpXQ_log(GEN a, GEN g, GEN ord, GEN T, GEN p)
+{
+  if (lgefint(p)==3)
+  {
+    pari_sp av=avma;
+    ulong pp = to_Flxq(&a, &T, p);
+    GEN z = Flxq_log(a, ZX_to_Flx(g, pp), ord, T, pp);
+    return gerepileuptoint(av,z);
+  }
+  else
+  {
+    void *E;
+    const struct bb_group *S = get_FpXQ_star(&E,T,p);
+    GEN z = gen_PH_log(a,g,ord,E,S);
+    return z? z: cgetg(1,t_VEC);
+  }
+}
+
+GEN
+FpXQ_sqrtn(GEN a, GEN n, GEN T, GEN p, GEN *zeta)
+{
+  pari_sp av = avma;
+  GEN z;
+  if (!signe(a))
+  {
+    long v=varn(a);
+    if (signe(n) < 0) pari_err_INV("FpXQ_sqrtn",a);
+    if (zeta) *zeta=pol_1(v);
+    return pol_0(v);
+  }
+  if (lgefint(p)==3)
+  {
+    ulong pp = to_Flxq(&a, &T, p);
+    z = Flxq_sqrtn(a, n, T, pp, zeta);
+    if (!z) return NULL;
+    if (!zeta) return Flx_to_ZX_inplace(gerepileuptoleaf(av, z));
+    z = Flx_to_ZX(z);
+    *zeta=Flx_to_ZX(*zeta);
+  }
+  else
+  {
+    void *E;
+    const struct bb_group *S = get_FpXQ_star(&E,T,p);
+    GEN o = addis(powiu(p,get_FpX_degree(T)),-1);
+    z = gen_Shanks_sqrtn(a,n,o,zeta,E,S);
+    if (!z) return NULL;
+    if (!zeta) return gerepileupto(av, z);
+  }
+  gerepileall(av, 2, &z,zeta);
+  return z;
+}
+
+GEN
+FpXQ_sqrt(GEN a, GEN T, GEN p)
+{
+  return FpXQ_sqrtn(a, gen_2, T, p, NULL);
+}
+
+GEN
+FpXQ_norm(GEN x, GEN TB, GEN p)
+{
+  pari_sp av = avma;
+  GEN T = get_FpX_mod(TB);
+  GEN y = FpX_resultant(T, x, p);
+  GEN L = leading_term(T);
+  if (gequal1(L) || signe(x)==0) return y;
+  return gerepileupto(av, Fp_div(y, Fp_pows(L, degpol(x), p), p));
+}
+
+GEN
+FpXQ_trace(GEN x, GEN TB, GEN p)
+{
+  pari_sp av = avma;
+  GEN T = get_FpX_mod(TB);
+  GEN dT = FpX_deriv(T,p);
+  long n = degpol(dT);
+  GEN z = FpXQ_mul(x, dT, TB, p);
+  if (degpol(z)<n) { avma = av; return gen_0; }
+  return gerepileuptoint(av, Fp_div(gel(z,2+n), gel(T,3+n),p));
+}
+
+GEN
+FpXQ_charpoly(GEN x, GEN T, GEN p)
+{
+  pari_sp ltop=avma;
+  long v;
+  GEN R;
+  T = leafcopy(get_FpX_mod(T));
+  v = varn(T); setvarn(T, MAXVARN);
+  x = leafcopy(x); setvarn(x, MAXVARN);
+  R = FpX_FpXY_resultant(T, deg1pol_shallow(gen_1,FpX_neg(x,p),v),p);
+  return gerepileupto(ltop,R);
+}
+
+GEN
+FpXQ_minpoly(GEN x, GEN T, GEN p)
+{
+  pari_sp ltop=avma;
+  GEN G,R=FpXQ_charpoly(x, T, p);
+  GEN dR=FpX_deriv(R,p);
+  while (signe(dR)==0)
+  {
+    R  = RgX_deflate(R,itos(p));
+    dR = FpX_deriv(R,p);
+  }
+  G=FpX_gcd(R,dR,p);
+  G=FpX_normalize(G,p);
+  G=FpX_div(R,G,p);
+  return gerepileupto(ltop,G);
+}
+
+GEN
+FpXQ_conjvec(GEN x, GEN T, GEN p)
+{
+  pari_sp av=avma;
+  long i;
+  long n = get_FpX_degree(T), v = varn(x);
+  GEN M = FpXQ_matrix_pow(FpXQ_pow(pol_x(v),p,T,p),n,n,T,p);
+  GEN z = cgetg(n+1,t_COL);
+  gel(z,1) = RgX_to_RgV(x,n);
+  for (i=2; i<=n; i++) gel(z,i) = FpM_FpC_mul(M,gel(z,i-1),p);
+  gel(z,1) = x;
+  for (i=2; i<=n; i++) gel(z,i) = RgV_to_RgX(gel(z,i),v);
+  return gerepilecopy(av,z);
+}
+
+/* p prime, p_1 = p-1, q = p^deg T, Lp = cofactors of some prime divisors
+ * l_p of p-1, Lq = cofactors of some prime divisors l_q of q-1, return a
+ * g in Fq such that
+ * - Ng generates all l_p-Sylows of Fp^*
+ * - g generates all l_q-Sylows of Fq^* */
+static GEN
+gener_FpXQ_i(GEN T, GEN p, GEN p_1, GEN Lp, GEN Lq)
+{
+  pari_sp av;
+  long vT = varn(T), f = degpol(T), l = lg(Lq);
+  GEN F = FpXQ_pow(pol_x(vT), p, T, p); /* Frobenius */
+  int p_is_2 = is_pm1(p_1);
+  for (av = avma;; avma = av)
+  {
+    GEN t, g = random_FpX(f, vT, p);
+    long i;
+    if (degpol(g) < 1) continue;
+    if (p_is_2)
+      t = g;
+    else
+    {
+      t = FpX_resultant(T, g, p); /* Ng = g^((q-1)/(p-1)), assuming T monic */
+      if (kronecker(t, p) == 1) continue;
+      if (lg(Lp) > 1 && !is_gener_Fp(t, p, p_1, Lp)) continue;
+      t = FpXQ_pow(g, shifti(p_1,-1), T, p);
+    }
+    for (i = 1; i < l; i++)
+    {
+      GEN a = FpXQ_pow_Frobenius(t, gel(Lq,i), F, T, p);
+      if (!degpol(a) && equalii(gel(a,2), p_1)) break;
+    }
+    if (i == l) return g;
+  }
+}
+
+GEN
+gener_FpXQ(GEN T, GEN p, GEN *po)
+{
+  long i, j, f = get_FpX_degree(T);
+  GEN g, Lp, Lq, p_1, q_1, N, o;
+  pari_sp av = avma;
+
+  p_1 = subiu(p,1);
+  if (f == 1) {
+    GEN Lp, fa;
+    o = p_1;
+    fa = Z_factor(o);
+    Lp = gel(fa,1);
+    Lp = vecslice(Lp, 2, lg(Lp)-1); /* remove 2 for efficiency */
+
+    g = cgetg(3, t_POL);
+    g[1] = evalsigne(1) | evalvarn(get_FpX_var(T));
+    gel(g,2) = pgener_Fp_local(p, Lp);
+    if (po) *po = mkvec2(o, fa);
+    return g;
+  }
+  if (lgefint(p) == 3)
+  {
+    ulong pp = to_Flxq(NULL, &T, p);
+    g = gener_Flxq(T, pp, po);
+    if (!po) return Flx_to_ZX_inplace(gerepileuptoleaf(av, g));
+    g = Flx_to_ZX(g);
+    gel(*po,2) = Flx_to_ZX(gel(*po,2));
+    gerepileall(av, 2, &g, po);
+    return g;
+  }
+  /* p now odd */
+  q_1 = subiu(powiu(p,f), 1);
+  N = diviiexact(q_1, p_1);
+  Lp = odd_prime_divisors(p_1);
+  for (i=lg(Lp)-1; i; i--) gel(Lp,i) = diviiexact(p_1, gel(Lp,i));
+  o = factor_pn_1(p,f);
+  Lq = leafcopy( gel(o, 1) );
+  for (i = j = 1; i < lg(Lq); i++)
+  {
+    if (remii(p_1, gel(Lq,i)) == gen_0) continue;
+    gel(Lq,j++) = diviiexact(N, gel(Lq,i));
+  }
+  setlg(Lq, j);
+  g = gener_FpXQ_i(get_FpX_mod(T), p, p_1, Lp, Lq);
+  if (!po) g = gerepilecopy(av, g);
+  else {
+    *po = mkvec2(q_1, o);
+    gerepileall(av, 2, &g, po);
+  }
+  return g;
+}
+
+#if 0 /* generic version: slower */
+GEN
+gener_FpXQ2(GEN T, GEN p, GEN *po)
+{
+  pari_sp av = avma;
+  void *E;
+  long f = get_FpX_degree(T);
+  GEN g, o = factor_pn_1(p,f);
+  const struct bb_group *S = get_FpXQ_star(&E,T,p);
+  g = gen_gener(o,E,S);
+  if (!po) g = gerepilecopy(av, g);
+  else {
+    *po = mkvec2(powiu(p,f), o);
+    gerepileall(av, 2, &g, po);
+  }
+  return g;
+}
+#endif
+
+GEN
+gener_FpXQ_local(GEN T, GEN p, GEN L)
+{
+  GEN Lp, Lq, p_1 = subiu(p,1), q_1, N, Q;
+  long f, i, ip, iq, l = lg(L);
+  T = get_FpX_mod(T);
+  f = degpol(T);
+  q_1 = subiu(powiu(p,f), 1);
+  N = diviiexact(q_1, p_1);
+
+  Q = is_pm1(p_1)? gen_1: shifti(p_1,-1);
+  Lp = cgetg(l, t_VEC); ip = 1;
+  Lq = cgetg(l, t_VEC); iq = 1;
+  for (i=1; i < l; i++)
+  {
+    GEN a, b, ell = gel(L,i);
+    if (equaliu(ell,2)) continue;
+    a = dvmdii(Q, ell, &b);
+    if (b == gen_0)
+      gel(Lp,ip++) = a;
+    else
+      gel(Lq,iq++) = diviiexact(N,ell);
+  }
+  setlg(Lp, ip);
+  setlg(Lq, iq);
+  return gener_FpXQ_i(T, p, p_1, Lp, Lq);
+}
diff --git a/src/basemath/FpXX.c b/src/basemath/FpXX.c
new file mode 100644
index 0000000..5811b1d
--- /dev/null
+++ b/src/basemath/FpXX.c
@@ -0,0 +1,1080 @@
+/* Copyright (C) 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* Not so fast arithmetic with polynomials over FpX */
+
+/*******************************************************************/
+/*                                                                 */
+/*                             FpXX                                */
+/*                                                                 */
+/*******************************************************************/
+/*Polynomials whose coefficients are either polynomials or integers*/
+
+static ulong
+to_FlxqX(GEN P, GEN Q, GEN T, GEN p, GEN *pt_P, GEN *pt_Q, GEN *pt_T)
+{
+  ulong pp = (ulong)p[2];
+  long v = get_FpX_var(T);
+  *pt_P = ZXX_to_FlxX(P, pp, v);
+  if (pt_Q) *pt_Q = ZXX_to_FlxX(Q, pp, v);
+  *pt_T = ZXT_to_FlxT(T, pp);
+  return pp;
+}
+
+static GEN
+ZXX_copy(GEN a) { return gcopy(a); }
+
+GEN
+FpXX_red(GEN z, GEN p)
+{
+  GEN res;
+  long i, l = lg(z);
+  res = cgetg(l,t_POL); res[1] = z[1];
+  for (i=2; i<l; i++)
+  {
+    GEN zi = gel(z,i), c;
+    if (typ(zi)==t_INT)
+      c = modii(zi,p);
+    else
+    {
+      pari_sp av = avma;
+      c = FpX_red(zi,p);
+      switch(lg(c)) {
+        case 2: avma = av; c = gen_0; break;
+        case 3: c = gerepilecopy(av, gel(c,2)); break;
+      }
+    }
+    gel(res,i) = c;
+  }
+  return FpXX_renormalize(res,lg(res));
+}
+GEN
+FpXX_add(GEN x, GEN y, GEN p)
+{
+  long i,lz;
+  GEN z;
+  long lx=lg(x);
+  long ly=lg(y);
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx; z = cgetg(lz, t_POL); z[1]=x[1];
+  for (i=2; i<ly; i++) gel(z,i) = Fq_add(gel(x,i), gel(y,i), NULL, p);
+  for (   ; i<lx; i++) gel(z,i) = gcopy(gel(x,i));
+  return FpXX_renormalize(z, lz);
+}
+GEN
+FpXX_sub(GEN x, GEN y, GEN p)
+{
+  long i,lz;
+  GEN z;
+  long lx=lg(x);
+  long ly=lg(y);
+  if (ly <= lx)
+  {
+    lz = lx; z = cgetg(lz, t_POL); z[1]=x[1];
+    for (i=2; i<ly; i++) gel(z,i) = Fq_sub(gel(x,i), gel(y,i), NULL, p);
+    for (   ; i<lx; i++) gel(z,i) = gcopy(gel(x,i));
+  }
+  else
+  {
+    lz = ly; z = cgetg(lz, t_POL); z[1]=x[1];
+    for (i=2; i<lx; i++) gel(z,i) = Fq_sub(gel(x,i), gel(y,i), NULL, p);
+    for (   ; i<ly; i++) gel(z,i) = Fq_neg(gel(y,i), NULL, p);
+  }
+  return FpXX_renormalize(z, lz);
+}
+
+static GEN
+FpXX_subspec(GEN x, GEN y, GEN p, long nx, long ny)
+{
+  long i,lz;
+  GEN z;
+  if (ny <= nx)
+  {
+    lz = nx+2; z = cgetg(lz, t_POL)+2;
+    for (i=0; i<ny; i++) gel(z,i) = Fq_sub(gel(x,i), gel(y,i), NULL, p);
+    for (   ; i<nx; i++) gel(z,i) = gcopy(gel(x,i));
+  }
+  else
+  {
+    lz = ny+2; z = cgetg(lz, t_POL)+2;
+    for (i=0; i<nx; i++) gel(z,i) = Fq_sub(gel(x,i), gel(y,i), NULL, p);
+    for (   ; i<ny; i++) gel(z,i) = Fq_neg(gel(y,i), NULL, p);
+  }
+  return FpXX_renormalize(z-2, lz);
+}
+
+GEN
+FpXX_neg(GEN x, GEN p)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx,t_POL);
+  y[1] = x[1];
+  for(i=2; i<lx; i++) gel(y,i) = Fq_neg(gel(x,i), NULL, p);
+  return FpXX_renormalize(y, lx);
+}
+
+GEN
+FpXX_Fp_mul(GEN P, GEN u, GEN p)
+{
+  long i, lP;
+  GEN res = cgetg_copy(P, &lP); res[1] = P[1];
+  for(i=2; i<lP; i++)
+  {
+    GEN x = gel(P,i);
+    gel(res,i) = typ(x)==t_INT? Fp_mul(x,u,p): FpX_Fp_mul(x,u,p);
+  }
+  return FpXX_renormalize(res,lP);
+}
+
+GEN
+FpXX_mulu(GEN P, ulong u, GEN p)
+{
+  long i, lP;
+  GEN res = cgetg_copy(P, &lP); res[1] = P[1];
+  for(i=2; i<lP; i++)
+  {
+    GEN x = gel(P,i);
+    gel(res,i) = typ(x)==t_INT? Fp_mulu(x,u,p): FpX_mulu(x,u,p);
+  }
+  return FpXX_renormalize(res,lP);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                             (Fp[X]/(Q))[Y]                      */
+/*                                                                 */
+/*******************************************************************/
+/*Not stack clean*/
+GEN
+Kronecker_to_FpXQX(GEN Z, GEN T, GEN p)
+{
+  long i,j,lx,l, N = (get_FpX_degree(T)<<1) + 1;
+  GEN x, t = cgetg(N,t_POL), z = FpX_red(Z, p);
+  t[1] = evalvarn(get_FpX_var(T));
+  l = lg(z); lx = (l-2) / (N-2);
+  x = cgetg(lx+3,t_POL);
+  x[1] = z[1];
+  for (i=2; i<lx+2; i++)
+  {
+    for (j=2; j<N; j++) gel(t,j) = gel(z,j);
+    z += (N-2);
+    gel(x,i) = FpX_rem(FpX_renormalize(t,N), T,p);
+  }
+  N = (l-2) % (N-2) + 2;
+  for (j=2; j<N; j++) gel(t,j) = gel(z,j);
+  gel(x,i) = FpX_rem(FpX_renormalize(t,N), T,p);
+  return FpXQX_renormalize(x, i+1);
+}
+
+/* shallow, n = deg(T) */
+GEN
+Kronecker_to_ZXX(GEN z, long n, long v)
+{
+  long i,j,lx,l, N = (n<<1)+1;
+  GEN x, t;
+  l = lg(z); lx = (l-2) / (N-2);
+  x = cgetg(lx+3,t_POL);
+  x[1] = z[1];
+  for (i=2; i<lx+2; i++)
+  {
+    t = cgetg(N,t_POL); t[1] = evalvarn(v);
+    for (j=2; j<N; j++) gel(t,j) = gel(z,j);
+    z += (N-2);
+    gel(x,i) = ZX_renormalize(t,N);
+  }
+  N = (l-2) % (N-2) + 2;
+  t = cgetg(N,t_POL); t[1] = evalvarn(v);
+  for (j=2; j<N; j++) gel(t,j) = gel(z,j);
+  gel(x,i) = ZX_renormalize(t,N);
+  return ZXX_renormalize(x, i+1);
+}
+/* shallow */
+GEN
+ZXX_mul_Kronecker(GEN x, GEN y, long n)
+{ return ZX_mul(ZXX_to_Kronecker(x,n), ZXX_to_Kronecker(y,n)); }
+
+GEN
+FpXQX_red(GEN z, GEN T, GEN p)
+{
+  long i, l = lg(z);
+  GEN res = cgetg(l,t_POL); res[1] = z[1];
+  for(i=2;i<l;i++)
+    if (typ(gel(z,i)) == t_INT)
+      gel(res,i) = modii(gel(z,i),p);
+    else
+      gel(res,i) = FpXQ_red(gel(z,i),T,p);
+  return FpXQX_renormalize(res,l);
+}
+
+static int
+ZXX_is_ZX_spec(GEN a,long na)
+{
+  long i;
+  for(i=0;i<na;i++)
+    if(typ(gel(a,i))!=t_INT) return 0;
+  return 1;
+}
+
+static int
+ZXX_is_ZX(GEN a) { return ZXX_is_ZX_spec(a+2,lgpol(a)); }
+
+static GEN
+FpXX_FpX_mulspec(GEN P, GEN U, GEN p, long v, long lU)
+{
+  long i, lP =lg(P);
+  GEN res;
+  res = cgetg(lP, t_POL); res[1] = P[1];
+  for(i=2; i<lP; i++)
+  {
+    GEN Pi = gel(P,i);
+    gel(res,i) = typ(Pi)==t_INT? FpX_Fp_mulspec(U, Pi, p, lU):
+                                 FpX_mulspec(U, Pi+2, p, lU, lgpol(Pi));
+    setvarn(gel(res,i),v);
+  }
+  return FpXQX_renormalize(res,lP);
+}
+
+GEN
+FpXX_FpX_mul(GEN P, GEN U, GEN p)
+{ return FpXX_FpX_mulspec(P,U+2,p,varn(U),lgpol(U)); }
+
+static GEN
+FpXY_FpY_mulspec(GEN x, GEN y, GEN T, GEN p, long lx, long ly)
+{
+  pari_sp av = avma;
+  GEN z = RgXY_swapspec(x,get_FpX_degree(T)-1,MAXVARN,lx);
+  z = FpXX_FpX_mulspec(z,y,p,MAXVARN,ly);
+  z = RgXY_swapspec(z+2,lx+ly+3,get_FpX_var(T),lgpol(z));
+  return gerepilecopy(av,z);
+}
+
+static GEN
+FpXQX_mulspec(GEN x, GEN y, GEN T, GEN p, long lx, long ly)
+{
+  pari_sp av = avma;
+  GEN z, kx, ky;
+  long n;
+  if (ZXX_is_ZX_spec(y,ly))
+  {
+    if (ZXX_is_ZX_spec(x,lx))
+      return FpX_mulspec(x,y,p,lx,ly);
+    else
+      return FpXY_FpY_mulspec(x,y,T,p,lx,ly);
+  } else if (ZXX_is_ZX_spec(x,lx))
+      return FpXY_FpY_mulspec(y,x,T,p,ly,lx);
+  n = get_FpX_degree(T);
+  kx = ZXX_to_Kronecker_spec(x, lx, n);
+  ky = ZXX_to_Kronecker_spec(y, ly, n);
+  z = Kronecker_to_FpXQX(ZX_mul(ky,kx), T, p);
+  return gerepileupto(av, z);
+}
+
+GEN
+FpXQX_mul(GEN x, GEN y, GEN T, GEN p)
+{
+  GEN z = FpXQX_mulspec(x+2,y+2,T,p,lgpol(x),lgpol(y));
+  setvarn(z,varn(x)); return z;
+}
+
+GEN
+FpXQX_sqr(GEN x, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN z, kx;
+  if (ZXX_is_ZX(x)) return ZX_sqr(x);
+  kx= ZXX_to_Kronecker(x, get_FpX_degree(T));
+  z = Kronecker_to_FpXQX(ZX_sqr(kx), T, p);
+  return gerepileupto(av, z);
+}
+
+GEN
+FpXQX_FpXQ_mul(GEN P, GEN U, GEN T, GEN p)
+{
+  long i, lP;
+  GEN res;
+  res = cgetg_copy(P, &lP); res[1] = P[1];
+  for(i=2; i<lP; i++)
+    gel(res,i) = typ(gel(P,i))==t_INT? FpX_Fp_mul(U, gel(P,i), p):
+                                       FpXQ_mul(U, gel(P,i), T,p);
+  return FpXQX_renormalize(res,lP);
+}
+
+/* x and y in Z[Y][X]. Assume T irreducible mod p */
+static GEN
+FpXQX_divrem_basecase(GEN x, GEN y, GEN T, GEN p, GEN *pr)
+{
+  long vx, dx, dy, dz, i, j, sx, lr;
+  pari_sp av0, av, tetpil;
+  GEN z,p1,rem,lead;
+
+  if (!T) return FpX_divrem(x,y,p,pr);
+  if (!signe(y)) pari_err_INV("FpX_divrem",y);
+  vx=varn(x); dy=degpol(y); dx=degpol(x);
+  if (dx < dy)
+  {
+    if (pr)
+    {
+      av0 = avma; x = FpXQX_red(x, T, p);
+      if (pr == ONLY_DIVIDES) { avma=av0; return signe(x)? NULL: pol_0(vx); }
+      if (pr == ONLY_REM) return x;
+      *pr = x;
+    }
+    return pol_0(vx);
+  }
+  lead = leading_term(y);
+  if (!dy) /* y is constant */
+  {
+    if (pr && pr != ONLY_DIVIDES)
+    {
+      if (pr == ONLY_REM) return pol_0(vx);
+      *pr = pol_0(vx);
+    }
+    if (gequal1(lead)) return FpXQX_red(x,T,p);
+    av0 = avma; x = FqX_Fq_mul(x, Fq_inv(lead, T,p), T,p);
+    return gerepileupto(av0,x);
+  }
+  av0 = avma; dz = dx-dy;
+  if (lgefint(p) == 3)
+  { /* assume ab != 0 mod p */
+    {
+      GEN *gptr[2];
+      GEN a, b, t;
+      ulong pp = to_FlxqX(x, y, T, p, &a, &b, &t);
+      z = FlxqX_divrem(a,b,t,pp,pr);
+      tetpil=avma;
+      z = FlxX_to_ZXX(z);
+      if (pr && pr != ONLY_DIVIDES && pr != ONLY_REM)
+        *pr = FlxX_to_ZXX(*pr);
+      else return gerepile(av0,tetpil,z);
+      gptr[0]=pr; gptr[1]=&z;
+      gerepilemanysp(av0,tetpil,gptr,2);
+      return z;
+    }
+  }
+  lead = gequal1(lead)? NULL: gclone(Fq_inv(lead,T,p));
+  avma = av0;
+  z = cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2; y += 2; z += 2;
+
+  p1 = gel(x,dx); av = avma;
+  gel(z,dz) = lead? gerepileupto(av, Fq_mul(p1,lead, T, p)): gcopy(p1);
+  for (i=dx-1; i>=dy; i--)
+  {
+    av=avma; p1=gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++)
+      p1 = Fq_sub(p1, Fq_mul(gel(z,j),gel(y,i-j),NULL,p),NULL,p);
+    if (lead) p1 = Fq_mul(p1, lead, NULL,p);
+    tetpil=avma; gel(z,i-dy) = gerepile(av,tetpil,Fq_red(p1,T,p));
+  }
+  if (!pr) { if (lead) gunclone(lead); return z-2; }
+
+  rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
+  for (sx=0; ; i--)
+  {
+    p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = Fq_sub(p1, Fq_mul(gel(z,j),gel(y,i-j),NULL,p),NULL,p);
+    tetpil=avma; p1 = Fq_red(p1, T, p); if (signe(p1)) { sx = 1; break; }
+    if (!i) break;
+    avma=av;
+  }
+  if (pr == ONLY_DIVIDES)
+  {
+    if (lead) gunclone(lead);
+    if (sx) { avma=av0; return NULL; }
+    avma = (pari_sp)rem; return z-2;
+  }
+  lr=i+3; rem -= lr;
+  rem[0] = evaltyp(t_POL) | evallg(lr);
+  rem[1] = z[-1];
+  p1 = gerepile((pari_sp)rem,tetpil,p1);
+  rem += 2; gel(rem,i) = p1;
+  for (i--; i>=0; i--)
+  {
+    av=avma; p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = Fq_sub(p1, Fq_mul(gel(z,j),gel(y,i-j), NULL,p), NULL,p);
+    tetpil=avma; gel(rem,i) = gerepile(av,tetpil, Fq_red(p1, T, p));
+  }
+  rem -= 2;
+  if (lead) gunclone(lead);
+  if (!sx) (void)FpXQX_renormalize(rem, lr);
+  if (pr == ONLY_REM) return gerepileupto(av0,rem);
+  *pr = rem; return z-2;
+}
+
+GEN
+FpXQX_gcd(GEN P, GEN Q, GEN T, GEN p)
+{
+  pari_sp av=avma, av0;
+  GEN R;
+  if (lgefint(p) == 3)
+  {
+    GEN Pl, Ql, Tl, U;
+    ulong pp = to_FlxqX(P, Q, T, p, &Pl, &Ql, &Tl);
+    U  = FlxqX_gcd(Pl, Ql, Tl, pp);
+    return gerepileupto(av, FlxX_to_ZXX(U));
+  }
+  P = FpXX_red(P, p); av0 = avma;
+  Q = FpXX_red(Q, p);
+  while (signe(Q))
+  {
+    av0 = avma; R = FpXQX_rem(P,Q,T,p); P=Q; Q=R;
+  }
+  avma = av0; return gerepileupto(av, P);
+}
+
+/* x and y in Z[Y][X], return lift(gcd(x mod T,p, y mod T,p)). Set u and v st
+ * ux + vy = gcd (mod T,p) */
+GEN
+FpXQX_extgcd(GEN x, GEN y, GEN T, GEN p, GEN *ptu, GEN *ptv)
+{
+  GEN a, b, q, r, u, v, d, d1, v1;
+  long vx = varn(x);
+  pari_sp ltop=avma;
+  if (lgefint(p) == 3)
+  {
+    GEN Pl, Ql, Tl, Dl;
+    ulong pp = to_FlxqX(x, y, T, p, &Pl, &Ql, &Tl);
+    Dl = FlxqX_extgcd(Pl, Ql, Tl, pp, ptu, ptv);
+    if (ptu) *ptu = FlxX_to_ZXX(*ptu);
+    *ptv = FlxX_to_ZXX(*ptv);
+    d = FlxX_to_ZXX(Dl);
+  }
+  else
+  {
+    a = FpXQX_red(x, T, p);
+    b = FpXQX_red(y, T, p);
+    d = a; d1 = b; v = pol_0(vx); v1 = pol_1(vx);
+    while (signe(d1))
+    {
+      q = FpXQX_divrem(d,d1,T,p, &r);
+      v = FpXX_sub(v, FpXQX_mul(q,v1, T,p), p);
+      u=v; v=v1; v1=u;
+      u=r; d=d1; d1=u;
+    }
+    if (ptu) *ptu = FpXQX_div(FpXX_sub(d, FpXQX_mul(b,v, T,p), p),a, T,p);
+    *ptv = v;
+  }
+  gerepileall(ltop,ptu?3:2,&d,ptv,ptu);
+  return d;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       Barrett reduction                           **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Return new lgpol */
+static long
+ZXX_lgrenormalizespec(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>=0; i--)
+    if (signe(gel(x,i))) break;
+  return i+1;
+}
+
+static GEN
+FpXQX_invBarrett_basecase(GEN S, GEN T, GEN p)
+{
+  long i, l=lg(S)-1, lr = l-1, k;
+  GEN r=cgetg(lr, t_POL); r[1]=S[1];
+  gel(r,2) = gen_1;
+  for (i=3; i<lr; i++)
+  {
+    pari_sp av = avma;
+    GEN u = gel(S,l-i+2);
+    for (k=3; k<i; k++)
+      u = Fq_add(u, Fq_mul(gel(S,l-i+k), gel(r,k), NULL, p), NULL, p);
+    gel(r,i) = gerepileupto(av, Fq_red(Fq_neg(u, NULL, p), T, p));
+  }
+  return FpXQX_renormalize(r,lr);
+}
+
+INLINE GEN
+FpXQX_recipspec(GEN x, long l, long n)
+{
+  return RgX_recipspec_shallow(x, l, n);
+}
+
+static GEN
+FpXQX_invBarrett_Newton(GEN S, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  long nold, lx, lz, lq, l = degpol(S), i, lQ;
+  GEN q, y, z, x = cgetg(l+2, t_POL) + 2;
+  ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
+  for (i=0;i<l;i++) gel(x,i) = gen_0;
+  q = RgX_recipspec_shallow(S+2,l+1,l+1); lQ = lgpol(q); q+=2;
+  /* We work on _spec_ FpX's, all the l[xzq] below are lgpol's */
+
+  /* initialize */
+  gel(x,0) = Fq_inv(gel(q,0), T, p);
+  if (lQ>1) gel(q,1) = Fq_red(gel(q,1), T, p);
+  if (lQ>1 && signe(gel(q,1)))
+  {
+    GEN u = gel(q, 1);
+    if (!gequal1(gel(x,0))) u = Fq_mul(u, Fq_sqr(gel(x,0), T, p), T, p);
+    gel(x,1) = Fq_neg(u, T, p); lx = 2;
+  }
+  else
+    lx = 1;
+  nold = 1;
+  for (; mask > 1; )
+  { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
+    long i, lnew, nnew = nold << 1;
+
+    if (mask & 1) nnew--;
+    mask >>= 1;
+
+    lnew = nnew + 1;
+    lq = ZXX_lgrenormalizespec(q, minss(lQ,lnew));
+    z = FpXQX_mulspec(x, q, T, p, lx, lq); /* FIXME: high product */
+    lz = lgpol(z); if (lz > lnew) lz = lnew;
+    z += 2;
+    /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
+    for (i = nold; i < lz; i++) if (signe(gel(z,i))) break;
+    nold = nnew;
+    if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
+
+    /* z + i represents (x*q - 1) / t^i */
+    lz = ZXX_lgrenormalizespec (z+i, lz-i);
+    z = FpXQX_mulspec(x, z+i, T, p, lx, lz); /* FIXME: low product */
+    lz = lgpol(z); z += 2;
+    if (lz > lnew-i) lz = ZXX_lgrenormalizespec(z, lnew-i);
+
+    lx = lz+ i;
+    y  = x + i; /* x -= z * t^i, in place */
+    for (i = 0; i < lz; i++) gel(y,i) = Fq_neg(gel(z,i), T, p);
+  }
+  x -= 2; setlg(x, lx + 2); x[1] = S[1];
+  return gerepilecopy(av, x);
+}
+
+const long FpXQX_INVBARRETT_LIMIT = 50;
+const long FpXQX_DIVREM_BARRETT_LIMIT = 50;
+const long FpXQX_REM_BARRETT_LIMIT = 50;
+
+/* 1/polrecip(S)+O(x^(deg(S)-1)) */
+GEN
+FpXQX_invBarrett(GEN S, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  long l = lg(S);
+  GEN r;
+  if (l<5) return pol_0(varn(S));
+  if (l<=FpXQX_INVBARRETT_LIMIT)
+  {
+    GEN c = gel(S,l-1), ci=gen_1;
+    if (!gequal1(c))
+    {
+      ci = Fq_inv(c, T, p);
+      S = FqX_Fq_mul(S, ci, T, p);
+      r = FpXQX_invBarrett_basecase(S, T, p);
+      r = FqX_Fq_mul(r, ci, T, p);
+    } else
+      r = FpXQX_invBarrett_basecase(S, T, p);
+  }
+  else
+    r = FpXQX_invBarrett_Newton(S, T, p);
+  return gerepileupto(ltop, r);
+}
+
+/* Compute x mod S where 2 <= degpol(S) <= l+1 <= 2*(degpol(S)-1)
+ * and mg is the Barrett inverse of S. */
+static GEN
+FpXQX_divrem_Barrettspec(GEN x, long l, GEN mg, GEN S, GEN T, GEN p, GEN *pr)
+{
+  GEN q, r;
+  long lt = degpol(S); /*We discard the leading term*/
+  long ld, lm, lT, lmg;
+  ld = l-lt;
+  lm = minss(ld, lgpol(mg));
+  lT  = ZXX_lgrenormalizespec(S+2,lt);
+  lmg = ZXX_lgrenormalizespec(mg+2,lm);
+  q = FpXQX_recipspec(x+lt,ld,ld);                 /* q = rec(x)     lq<=ld*/
+  q = FpXQX_mulspec(q+2,mg+2,T,p,lgpol(q),lmg);    /* q = rec(x) * mg lq<=ld+lm*/
+  q = FpXQX_recipspec(q+2,minss(ld,lgpol(q)),ld);  /* q = rec (rec(x) * mg) lq<=ld*/
+  if (!pr) return q;
+  r = FpXQX_mulspec(q+2,S+2,T,p,lgpol(q),lT);      /* r = q*pol        lr<=ld+lt*/
+  r = FpXX_subspec(x,r+2,p,lt,minss(lt,lgpol(r))); /* r = x - r   lr<=lt */
+  if (pr == ONLY_REM) return r;
+  *pr = r; return q;
+}
+
+static GEN
+FpXQX_divrem_Barrett_noGC(GEN x, GEN mg, GEN S, GEN T, GEN p, GEN *pr)
+{
+  long l = lgpol(x), lt = degpol(S), lm = 2*lt-1;
+  GEN q = NULL, r;
+  long i;
+  if (l <= lt)
+  {
+    if (pr == ONLY_REM) return ZXX_copy(x);
+    if (pr == ONLY_DIVIDES) return signe(x)? NULL: pol_0(varn(x));
+    if (pr) *pr =  ZXX_copy(x);
+    return pol_0(varn(x));
+  }
+  if (lt <= 1)
+    return FpXQX_divrem_basecase(x,S,T,p,pr);
+  if (pr != ONLY_REM && l>lm)
+  {
+    q = cgetg(l-lt+2, t_POL);
+    for (i=0;i<l-lt;i++) gel(q+2,i) = gen_0;
+  }
+  r = l>lm ? shallowcopy(x): x;
+  while (l>lm)
+  {
+    GEN zr, zq = FpXQX_divrem_Barrettspec(r+2+l-lm,lm,mg,S,T,p,&zr);
+    long lz = lgpol(zr);
+    if (pr != ONLY_REM)
+    {
+      long lq = lgpol(zq);
+      for(i=0; i<lq; i++) gel(q+2+l-lm,i) = gel(zq,2+i);
+    }
+    for(i=0; i<lz; i++) gel(r+2+l-lm,i) = gel(zr,2+i);
+    l = l-lm+lz;
+  }
+  if (pr != ONLY_REM)
+  {
+    if (l > lt)
+    {
+      GEN zq = FpXQX_divrem_Barrettspec(r+2,l,mg,S,T,p,&r);
+      if (!q) q = zq;
+      else
+      {
+        long lq = lgpol(zq);
+        for(i=0; i<lq; i++) gel(q+2,i) = gel(zq,2+i);
+      }
+    }
+    else
+    { setlg(r, l+2); r = ZXX_copy(r); }
+  }
+  else
+  {
+    if (l > lt)
+      (void) FpXQX_divrem_Barrettspec(r+2,l,mg,S,T,p,&r);
+    else
+    { setlg(r, l+2); r = ZXX_copy(r); }
+    r[1] = x[1]; return FpXQX_renormalize(r, lg(r));
+  }
+  if (pr) { r[1] = x[1]; r = FpXQX_renormalize(r, lg(r)); }
+  q[1] = x[1]; q = FpXQX_renormalize(q, lg(q));
+  if (pr == ONLY_DIVIDES) return signe(r)? NULL: q;
+  if (pr) *pr = r;
+  return q;
+}
+
+GEN
+FpXQX_divrem_Barrett(GEN x, GEN B, GEN S, GEN T, GEN p, GEN *pr)
+{
+  pari_sp av = avma;
+  GEN q = FpXQX_divrem_Barrett_noGC(x,B,S,T,p,pr);
+  if (!q) {avma=av; return NULL;}
+  if (!pr || pr==ONLY_REM || pr==ONLY_DIVIDES) return gerepilecopy(av, q);
+  gerepileall(av,2,&q,pr);
+  return q;
+}
+
+GEN
+FpXQX_divrem(GEN x, GEN y, GEN T, GEN p, GEN *pr)
+{
+  long dy = degpol(y), dx = degpol(x), d = dx-dy;
+  if (pr==ONLY_REM) return FpXQX_rem(x, y, T, p);
+  if (d+3 < FpXQX_DIVREM_BARRETT_LIMIT)
+    return FpXQX_divrem_basecase(x,y,T,p,pr);
+  else
+  {
+    pari_sp av=avma;
+    GEN mg = FpXQX_invBarrett(y, T, p);
+    GEN q = FpXQX_divrem_Barrett_noGC(x,mg,y,T,p,pr);
+    if (!q) {avma=av; return NULL;}
+    if (!pr || pr==ONLY_DIVIDES) return gerepilecopy(av, q);
+    gerepileall(av,2,&q,pr);
+    return q;
+  }
+}
+
+GEN
+FpXQX_rem_Barrett(GEN x, GEN mg, GEN S, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, FpXQX_divrem_Barrett_noGC(x,mg,S, T, p, ONLY_REM));
+}
+
+GEN
+FpXQX_rem(GEN x, GEN y, GEN T, GEN p)
+{
+  long dy = degpol(y), dx = degpol(x), d = dx-dy;
+  if (d < 0) return FpXQX_red(x, T, p);
+  if (d+3 < FpXQX_REM_BARRETT_LIMIT)
+    return FpXQX_divrem_basecase(x,y, T, p, ONLY_REM);
+  else
+  {
+    pari_sp av=avma;
+    GEN mg = FpXQX_invBarrett(y, T, p);
+    GEN r = FpXQX_divrem_Barrett_noGC(x, mg, y, T, p, ONLY_REM);
+    return gerepileupto(av, r);
+  }
+}
+
+struct _FpXQX { GEN T,p; };
+static GEN _FpXQX_mul(void *data, GEN a,GEN b)
+{
+  struct _FpXQX *d=(struct _FpXQX*)data;
+  return FpXQX_mul(a,b,d->T,d->p);
+}
+GEN
+FpXQXV_prod(GEN V, GEN T, GEN p)
+{
+  if (lgefint(p) == 3)
+  {
+    pari_sp av = avma;
+    ulong pp = p[2];
+    GEN Tl = ZXT_to_FlxT(T, pp);
+    GEN Vl = ZXXV_to_FlxXV(V, pp, get_FpX_var(T));
+    Tl = FlxqXV_prod(Vl, Tl, pp);
+    return gerepileupto(av, FlxX_to_ZXX(Tl));
+  }
+  else
+  {
+    struct _FpXQX d;
+    d.p=p;
+    d.T=T;
+    return divide_conquer_assoc(V, (void*)&d, &_FpXQX_mul);
+  }
+}
+
+/* Q an FpXY (t_POL with FpX coeffs), evaluate at X = x */
+GEN
+FpXY_evalx(GEN Q, GEN x, GEN p)
+{
+  long i, lb = lg(Q);
+  GEN z;
+  z = cgetg(lb, t_POL); z[1] = Q[1];
+  for (i=2; i<lb; i++)
+  {
+    GEN q = gel(Q,i);
+    gel(z,i) = typ(q) == t_INT? modii(q,p): FpX_eval(q, x, p);
+  }
+  return FpX_renormalize(z, lb);
+}
+/* Q an FpXY, evaluate at Y = y */
+GEN
+FpXY_evaly(GEN Q, GEN y, GEN p, long vx)
+{
+  pari_sp av = avma;
+  long i, lb = lg(Q);
+  GEN z;
+  if (lb == 2) return pol_0(vx);
+  z = gel(Q, lb-1);
+  if (lb == 3) return typ(z)==t_INT? scalar_ZX(z, vx): ZX_copy(z);
+  if (!signe(y)) {
+    z = gel(Q, 2);
+    return typ(z)==t_INT? scalar_ZX(z, vx): ZX_copy(z);
+  }
+
+  if (typ(z) == t_INT) z = scalar_ZX_shallow(z, vx);
+  for (i=lb-2; i>=2; i--) z = Fq_add(gel(Q,i), FpX_Fp_mul(z, y, p), NULL, p);
+  return gerepileupto(av, z);
+}
+/* Q an FpXY, evaluate at (X,Y) = (x,y) */
+GEN
+FpXY_eval(GEN Q, GEN y, GEN x, GEN p)
+{
+  pari_sp av = avma;
+  return gerepileuptoint(av, FpX_eval(FpXY_evalx(Q, x, p), y, p));
+}
+
+GEN
+FpXY_FpXQ_evalx(GEN P, GEN x, GEN T, GEN p)
+{
+  long i, lP = lg(P);
+  GEN res = cgetg(lP,t_POL);
+  res[1] = P[1];
+  for(i=2; i<lP; i++)
+    gel(res,i) = FpX_FpXQ_eval(gel(P,i), x, T, p);
+  return FlxX_renormalize(res, lP);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       (Fp[X]/T(X))[Y] / S(Y)                    */
+/*                                                                 */
+/*******************************************************************/
+
+/*Preliminary implementation to speed up FpX_ffisom*/
+typedef struct {
+  GEN S, T, p;
+} FpXYQQ_muldata;
+
+/* reduce x in Fp[X, Y] in the algebra Fp[X,Y]/ (S(X),T(Y)) */
+static GEN
+FpXYQQ_redswap(GEN x, GEN S, GEN T, GEN p)
+{
+  pari_sp ltop=avma;
+  long n = get_FpX_degree(S);
+  long m = get_FpX_degree(T);
+  long v = get_FpX_var(T);
+  GEN V = RgXY_swap(x,m,v);
+  V = FpXQX_red(V,S,p);
+  V = RgXY_swap(V,n,v);
+  return gerepilecopy(ltop,V);
+}
+static GEN
+FpXYQQ_sqr(void *data, GEN x)
+{
+  FpXYQQ_muldata *D = (FpXYQQ_muldata*)data;
+  return FpXYQQ_redswap(FpXQX_sqr(x, D->T, D->p),D->S,D->T,D->p);
+
+}
+static GEN
+FpXYQQ_mul(void *data, GEN x, GEN y)
+{
+  FpXYQQ_muldata *D = (FpXYQQ_muldata*)data;
+  return FpXYQQ_redswap(FpXQX_mul(x,y, D->T, D->p),D->S,D->T,D->p);
+}
+
+/* x in Z[X,Y], S in Z[X] over Fq = Z[Y]/(p,T); compute lift(x^n mod (S,T,p)) */
+GEN
+FpXYQQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  FpXYQQ_muldata D;
+  GEN y;
+  if (lgefint(p) == 3)
+  {
+    ulong pp = to_FlxqX(x, NULL, T, p, &x, NULL, &T);
+    S = ZX_to_Flx(S, pp);
+    y = FlxX_to_ZXX( FlxYqq_pow(x, n, S, T, pp) );
+  }
+  else
+  {
+    D.S = S;
+    D.T = T;
+    D.p = p;
+    y = gen_pow(x, n, (void*)&D, &FpXYQQ_sqr, &FpXYQQ_mul);
+  }
+  return gerepileupto(av, y);
+}
+
+GEN
+FpXQXQ_mul(GEN x, GEN y, GEN S, GEN T, GEN p) {
+  return FpXQX_rem(FpXQX_mul(x, y, T, p), S, T, p);
+}
+
+GEN
+FpXQXQ_sqr(GEN x, GEN S, GEN T, GEN p) {
+  return FpXQX_rem(FpXQX_sqr(x, T, p), S, T, p);
+}
+
+/* Inverse of x in Z/pZ[X]/(pol) or NULL if inverse doesn't exist
+ * return lift(1 / (x mod (p,pol))) */
+GEN
+FpXQXQ_invsafe(GEN x, GEN S, GEN T, GEN p)
+{
+  GEN V, z = FpXQX_extgcd(S, x, T, p, NULL, &V);
+  if (degpol(z)) return NULL;
+  z = gel(z,2);
+  z = typ(z)==t_INT ? Fp_invsafe(z,p) : FpXQ_invsafe(z,T,p);
+  if (!z) return NULL;
+  return typ(z)==t_INT ? FpXX_Fp_mul(V, z, p): FpXQX_FpXQ_mul(V, z, T, p);
+}
+
+GEN
+FpXQXQ_inv(GEN x, GEN S, GEN T,GEN p)
+{
+  pari_sp av = avma;
+  GEN U = FpXQXQ_invsafe(x, S, T, p);
+  if (!U) pari_err_INV("FpXQXQ_inv",x);
+  return gerepileupto(av, U);
+}
+
+GEN
+FpXQXQ_div(GEN x,GEN y,GEN S, GEN T,GEN p)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, FpXQXQ_mul(x, FpXQXQ_inv(y,S,T,p),S,T,p));
+}
+
+typedef struct {
+  GEN T, S;
+  GEN p;
+} FpXQXQ_muldata;
+static GEN
+_FpXQXQ_add(void *data, GEN x, GEN y) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  return FpXX_add(x,y, d->p);
+}
+static GEN
+_FpXQXQ_cmul(void *data, GEN P, long a, GEN x) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  GEN y = gel(P,a+2);
+  return typ(y)==t_INT ? FpXX_Fp_mul(x,y, d->p):
+                         FpXX_FpX_mul(x,y,d->p);
+}
+static GEN
+_FpXQXQ_red(void *data, GEN x) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  return FpXQX_red(x, d->T, d->p);
+}
+static GEN
+_FpXQXQ_mul(void *data, GEN x, GEN y) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  return FpXQXQ_mul(x,y, d->S,d->T, d->p);
+}
+static GEN
+_FpXQXQ_sqr(void *data, GEN x) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  return FpXQXQ_sqr(x, d->S,d->T, d->p);
+}
+
+static GEN
+_FpXQXQ_one(void *data) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  return pol_1(varn(d->S));
+}
+
+static GEN
+_FpXQXQ_zero(void *data) {
+  FpXQXQ_muldata *d = (FpXQXQ_muldata*) data;
+  return pol_0(varn(d->S));
+}
+
+static struct bb_algebra FpXQXQ_algebra = { _FpXQXQ_red,_FpXQXQ_add,_FpXQXQ_mul,_FpXQXQ_sqr,_FpXQXQ_one,_FpXQXQ_zero };
+
+/* x over Fq, return lift(x^n) mod S */
+GEN
+FpXQXQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN y;
+  FpXQXQ_muldata D;
+  long s = signe(n);
+  if (!s) return pol_1(varn(x));
+  if (is_pm1(n)) /* +/- 1 */
+    return (s < 0)? FpXQXQ_inv(x,S,T,p): ZXX_copy(x);
+  if (lgefint(p) == 3)
+  {
+    ulong pp = to_FlxqX(x, S, T, p, &x, &S, &T);
+    GEN z = FlxqXQ_pow(x, n, S, T, pp);
+    y = FlxX_to_ZXX(z);
+  }
+  else
+  {
+    D.S = S; D.T = T; D.p = p;
+    if (s < 0) x = FpXQXQ_inv(x,S,T,p);
+    y = gen_pow(x, n, (void*)&D,&_FpXQXQ_sqr,&_FpXQXQ_mul);
+  }
+  return gerepileupto(ltop, y);
+}
+
+/* generates the list of powers of x of degree 0,1,2,...,l*/
+GEN
+FpXQXQ_powers(GEN x, long l, GEN S, GEN T, GEN p)
+{
+  FpXQXQ_muldata D;
+  int use_sqr = (degpol(x)<<1) >= degpol(S);
+  D.S = S; D.T = T; D.p = p;
+  return gen_powers(x, l, use_sqr, (void*)&D, &_FpXQXQ_sqr, &_FpXQXQ_mul,&_FpXQXQ_one);
+}
+
+GEN
+FpXQXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, GEN p)
+{
+  return RgXV_to_RgM(FpXQXQ_powers(y,m-1,S,T,p),n);
+}
+
+GEN
+FpXQX_FpXQXQV_eval(GEN P, GEN V, GEN S, GEN T, GEN p)
+{
+  FpXQXQ_muldata D;
+  D.S=S; D.T=T; D.p=p;
+  return gen_bkeval_powers(P, degpol(P), V, (void*)&D, &FpXQXQ_algebra,
+                                                   _FpXQXQ_cmul);
+}
+
+GEN
+FpXQX_FpXQXQ_eval(GEN Q, GEN x, GEN S, GEN T, GEN p)
+{
+  FpXQXQ_muldata D;
+  int use_sqr = (degpol(x)<<1) >= degpol(S);
+  D.S=S; D.T=T; D.p=p;
+  return gen_bkeval(Q, degpol(Q), x, use_sqr, (void*)&D, &FpXQXQ_algebra,
+                                                    _FpXQXQ_cmul);
+}
+
+static GEN
+FpXQXQ_autpow_sqr(void * T, GEN x)
+{
+  FpXQXQ_muldata *D = (FpXQXQ_muldata *)T;
+  GEN phi = gel(x,1), S = gel(x,2);
+  GEN phi2 = FpX_FpXQ_eval(phi,phi,D->T,D->p);
+  GEN Sphi = FpXY_FpXQ_evalx(S,phi,D->T,D->p);
+  GEN S2 = FpXQX_FpXQXQ_eval(Sphi, S, D->S,D->T,D->p);
+  return mkvec2(phi2, S2);
+}
+
+static GEN
+FpXQXQ_autpow_mul(void * T, GEN x, GEN y)
+{
+  FpXQXQ_muldata *D = (FpXQXQ_muldata *)T;
+  GEN phi1 = gel(x,1), S1 = gel(x,2);
+  GEN phi2 = gel(y,1), S2 = gel(y,2);
+  GEN phi3 = FpX_FpXQ_eval(phi1,phi2,D->T,D->p);
+  GEN Sphi = FpXY_FpXQ_evalx(S1,phi2,D->T,D->p);
+  GEN S3 = FpXQX_FpXQXQ_eval(Sphi, S2, D->S,D->T,D->p);
+  return mkvec2(phi3, S3);
+}
+
+GEN
+FpXQXQV_autpow(GEN aut, long n, GEN S, GEN T, GEN p)
+{
+  FpXQXQ_muldata D;
+  D.S=S; D.T=T; D.p=p;
+  return gen_powu(aut,n,&D,FpXQXQ_autpow_sqr,FpXQXQ_autpow_mul);
+}
+
+static GEN
+FpXQXQ_autsum_mul(void * T, GEN x, GEN y)
+{
+  FpXQXQ_muldata *D = (FpXQXQ_muldata *)T;
+  GEN phi1 = gel(x,1), S1 = gel(x,2), a1 = gel(x,3);
+  GEN phi2 = gel(y,1), S2 = gel(y,2), a2 = gel(y,3);
+  GEN phi3 = FpX_FpXQ_eval(phi1,phi2,D->T,D->p);
+  GEN Sphi = FpXY_FpXQ_evalx(S1,phi2,D->T,D->p);
+  long n = brent_kung_optpow(degpol(D->S)-1,2,1);
+  GEN V = FpXQXQ_powers(S2, n, D->S,D->T,D->p);
+  GEN S3 = FpXQX_FpXQXQV_eval(Sphi, V, D->S,D->T,D->p);
+  GEN aphi = FpXY_FpXQ_evalx(a1,phi2,D->T,D->p);
+  GEN aS = FpXQX_FpXQXQV_eval(aphi,V,D->S,D->T,D->p);
+  GEN a3 = FpXQXQ_mul(aS,a2,D->S,D->T,D->p);
+  return mkvec3(phi3, S3, a3);
+}
+
+static GEN
+FpXQXQ_autsum_sqr(void * T, GEN x)
+{ return FpXQXQ_autsum_mul(T,x,x); }
+
+GEN
+FpXQXQV_autsum(GEN aut, long n, GEN S, GEN T, GEN p)
+{
+  FpXQXQ_muldata D;
+  D.S=S; D.T=T; D.p=p;
+  return gen_powu(aut,n,&D,FpXQXQ_autsum_sqr,FpXQXQ_autsum_mul);
+}
diff --git a/src/basemath/FpX_factor.c b/src/basemath/FpX_factor.c
new file mode 100644
index 0000000..dc2e97c
--- /dev/null
+++ b/src/basemath/FpX_factor.c
@@ -0,0 +1,2904 @@
+/* Copyright (C) 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/***********************************************************************/
+/**                                                                   **/
+/**               Factorisation over finite field                     **/
+/**                                                                   **/
+/***********************************************************************/
+
+/*******************************************************************/
+/*                                                                 */
+/*           ROOTS MODULO a prime p (no multiplicities)            */
+/*                                                                 */
+/*******************************************************************/
+/* Check types and replace F by a monic normalized FpX having the same roots
+ * Don't bother to make constant polynomials monic */
+static void
+factmod_init(GEN *F, GEN p)
+{
+  if (typ(p)!=t_INT) pari_err_TYPE("factmod",p);
+  if (signe(p) < 0) pari_err_PRIME("factmod",p);
+  if (typ(*F)!=t_POL) pari_err_TYPE("factmod",*F);
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    if (pp < 2) pari_err_PRIME("factmod", p);
+    *F = RgX_to_Flx(*F, pp);
+    if (lg(*F) > 3) *F = Flx_normalize(*F, pp);
+  }
+  else
+  {
+    *F = RgX_to_FpX(*F, p);
+    if (lg(*F) > 3) *F = FpX_normalize(*F, p);
+  }
+}
+/* as above, assume p prime and *F a ZX */
+static void
+ZX_factmod_init(GEN *F, GEN p)
+{
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    *F = ZX_to_Flx(*F, pp);
+    if (lg(*F) > 3) *F = Flx_normalize(*F, pp);
+  }
+  else
+  {
+    *F = FpX_red(*F, p);
+    if (lg(*F) > 3) *F = FpX_normalize(*F, p);
+  }
+}
+
+/* return 1,...,p-1 [not_0 = 1] or 0,...,p [not_0 = 0] */
+static GEN
+all_roots_mod_p(ulong p, int not_0)
+{
+  GEN r;
+  ulong i;
+  if (not_0) {
+    r = cgetg(p, t_VECSMALL);
+    for (i = 1; i < p; i++) r[i] = i;
+  } else {
+    r = cgetg(p+1, t_VECSMALL);
+    for (i = 0; i < p; i++) r[i+1] = i;
+  }
+  return r;
+}
+
+/* X^n - 1 */
+static GEN
+Flx_Xnm1(long sv, long n, ulong p)
+{
+  GEN t = cgetg(n+3, t_VECSMALL);
+  long i;
+  t[1] = sv;
+  t[2] = p - 1;
+  for (i = 3; i <= n+1; i++) t[i] = 0;
+  t[i] = 1; return t;
+}
+/* X^n + 1 */
+static GEN
+Flx_Xn1(long sv, long n, ulong p)
+{
+  GEN t = cgetg(n+3, t_VECSMALL);
+  long i;
+  (void) p;
+  t[1] = sv;
+  t[2] = 1;
+  for (i = 3; i <= n+1; i++) t[i] = 0;
+  t[i] = 1; return t;
+}
+
+static ulong
+Fl_nonsquare(ulong p)
+{
+  long k = 2;
+  for (;; k++)
+  {
+    long i = krouu(k, p);
+    if (!i) pari_err_PRIME("Fl_nonsquare",utoipos(p));
+    if (i < 0) return k;
+  }
+}
+
+/* f monic Flx, f(0) != 0. Return a monic squarefree g with the same
+ * roots as f */
+static GEN
+Flx_cut_out_roots(GEN f, ulong p)
+{
+  GEN g = Flx_mod_Xnm1(f, p-1, p); /* f mod x^(p-1) - 1 */
+  if (g != f && degpol(g) >= 0) {
+    (void)Flx_valrem(g, &g); /* reduction may introduce 0 root */
+    g = Flx_gcd(g, Flx_Xnm1(g[1], p-1, p), p);
+    g = Flx_normalize(g, p);
+  }
+  return g;
+}
+
+/* by checking f(0..p-1) */
+GEN
+Flx_roots_naive(GEN f, ulong p)
+{
+  long d, n = 0;
+  ulong s = 1UL, r;
+  GEN q, y = cgetg(degpol(f) + 1, t_VECSMALL);
+  pari_sp av2, av = avma;
+
+  if (Flx_valrem(f, &f)) y[++n] = 0;
+  f = Flx_cut_out_roots(f, p);
+  d = degpol(f);
+  if (d < 0) return all_roots_mod_p(p, n == 0);
+  av2 = avma;
+  while (d > 1) /* d = current degree of f */
+  {
+    q = Flx_div_by_X_x(f, s, p, &r); /* TODO: FFT-type multi-evaluation */
+    if (r) avma = av2; else { y[++n] = s; d--; f = q; av2 = avma; }
+    if (++s == p) break;
+  }
+  if (d == 1)
+  { /* -f[2]/f[3], root of deg 1 polynomial */
+    r = Fl_mul(p - Fl_inv(f[3], p), f[2], p);
+    if (r >= s) y[++n] = r; /* otherwise double root */
+  }
+  avma = av; fixlg(y, n+1); return y;
+}
+static GEN
+Flx_root_mod_2(GEN f)
+{
+  int z1, z0 = !(f[2] & 1);
+  long i,n;
+  GEN y;
+
+  for (i=2, n=1; i < lg(f); i++) n += f[i];
+  z1 = n & 1;
+  y = cgetg(z0+z1+1, t_VECSMALL); i = 1;
+  if (z0) y[i++] = 0;
+  if (z1) y[i  ] = 1;
+  return y;
+}
+static ulong
+Flx_oneroot_mod_2(GEN f)
+{
+  long i,n;
+  if (!(f[2] & 1)) return 0;
+  for (i=2, n=1; i < lg(f); i++) n += f[i];
+  if (n & 1) return 1;
+  return 2;
+}
+
+static GEN FpX_roots_i(GEN f, GEN p);
+static GEN Flx_roots_i(GEN f, ulong p);
+static GEN FpX_Berlekamp_i(GEN f, GEN pp, long flag);
+
+/* Generic driver to computes the roots of f modulo pp, using 'Roots' when
+ * pp is a small prime.
+ * if (gpwrap), check types thoroughly and return t_INTMODs, otherwise
+ * assume that f is an FpX, pp a prime and return t_INTs */
+static GEN
+rootmod_aux(GEN f, GEN pp, GEN (*Roots)(GEN,ulong), int gpwrap)
+{
+  pari_sp av = avma;
+  GEN y;
+  if (gpwrap)
+    factmod_init(&f, pp);
+  else
+    ZX_factmod_init(&f, pp);
+  switch(lg(f))
+  {
+    case 2: pari_err_ROOTS0("rootmod");
+    case 3: avma = av; return cgetg(1,t_COL);
+  }
+  if (typ(f) == t_VECSMALL)
+  {
+    ulong p = pp[2];
+    if (p == 2)
+      y = Flx_root_mod_2(f);
+    else
+    {
+      if (!odd(p)) pari_err_PRIME("rootmod",utoi(p));
+      y = Roots(f, p);
+    }
+    y = Flc_to_ZC(y);
+  }
+  else
+    y = FpX_roots_i(f, pp);
+  if (gpwrap) y = FpC_to_mod(y, pp);
+  return gerepileupto(av, y);
+}
+/* assume that f is a ZX an pp a prime */
+GEN
+FpX_roots(GEN f, GEN pp)
+{ return rootmod_aux(f, pp, Flx_roots_i, 0); }
+/* no assumptions on f and pp */
+GEN
+rootmod2(GEN f, GEN pp) { return rootmod_aux(f, pp, &Flx_roots_naive, 1); }
+GEN
+rootmod(GEN f, GEN pp) { return rootmod_aux(f, pp, &Flx_roots_i, 1); }
+GEN
+rootmod0(GEN f, GEN p, long flag)
+{
+  switch(flag)
+  {
+    case 0: return rootmod(f,p);
+    case 1: return rootmod2(f,p);
+    default: pari_err_FLAG("polrootsmod");
+  }
+  return NULL; /* not reached */
+}
+
+/* assume x reduced mod p > 2, monic. */
+static int
+FpX_quad_factortype(GEN x, GEN p)
+{
+  GEN b = gel(x,3), c = gel(x,2);
+  GEN D = subii(sqri(b), shifti(c,2));
+  return kronecker(D,p);
+}
+/* assume x reduced mod p, monic. Return one root, or NULL if irreducible */
+GEN
+FpX_quad_root(GEN x, GEN p, int unknown)
+{
+  GEN s, u, D, b = gel(x,3), c = gel(x,2);
+
+  if (equaliu(p, 2)) {
+    if (!signe(b)) return c;
+    return signe(c)? NULL: gen_1;
+  }
+  D = subii(sqri(b), shifti(c,2));
+  D = remii(D,p);
+  if (unknown && kronecker(D,p) == -1) return NULL;
+
+  s = Fp_sqrt(D,p);
+  /* p is not prime, go on and give e.g. maxord a chance to recover */
+  if (!s) return NULL;
+  u = addis(shifti(p,-1), 1); /* = 1/2 */
+  return Fp_mul(u, subii(s,b), p);
+}
+static GEN
+FpX_otherroot(GEN x, GEN r, GEN p)
+{
+  GEN s = addii(gel(x,3), r);
+  if (!signe(s)) return s;
+  s = subii(p, s); if (signe(s) < 0) s = addii(s,p);
+  return s;
+}
+
+/* disc(x^2+bx+c) = b^2 - 4c */
+static ulong
+Fl_disc_bc(ulong b, ulong c, ulong p)
+{ return Fl_sub(Fl_sqr(b,p), Fl_double(Fl_double(c,p),p), p); }
+/* p > 2 */
+static ulong
+Flx_quad_root(GEN x, ulong p, int unknown)
+{
+  ulong s, u, b = x[3], c = x[2];
+  ulong D = Fl_disc_bc(b, c, p);
+  if (unknown && krouu(D,p) == -1) return p;
+  s = Fl_sqrt(D,p);
+  if (s==~0UL) return p;
+  u = (p>>1)+1;
+  return Fl_mul(u, Fl_sub(s,b, p), p);
+}
+static ulong
+Flx_otherroot(GEN x, ulong r, ulong p)
+{ return Fl_neg(Fl_add(x[3], r, p), p); }
+
+
+/* 'todo' contains the list of factors to be split.
+ * 'done' the list of finished factors, no longer touched */
+struct split_t { GEN todo, done; };
+static void
+split_init(struct split_t *S, long max)
+{
+  S->done = vectrunc_init(max);
+  S->todo = vectrunc_init(max);
+}
+#if 0
+/* move todo[i] to done */
+static void
+split_convert(struct split_t *S, long i)
+{
+  long n = lg(S->todo)-1;
+  vectrunc_append(S->done, gel(S->todo,i));
+  if (n) gel(S->todo,i) = gel(S->todo, n);
+  setlg(S->todo, n);
+}
+#endif
+/* append t to todo */
+static void
+split_add(struct split_t *S, GEN t) { vectrunc_append(S->todo, t); }
+/* delete todo[i], add t to done */
+static void
+split_moveto_done(struct split_t *S, long i, GEN t)
+{
+  long n = lg(S->todo)-1;
+  vectrunc_append(S->done, t);
+  if (n) gel(S->todo,i) = gel(S->todo, n);
+  setlg(S->todo, n);
+
+}
+/* append t to done */
+static void
+split_add_done(struct split_t *S, GEN t)
+{ vectrunc_append(S->done, t); }
+/* split todo[i] into a and b */
+static void
+split_todo(struct split_t *S, long i, GEN a, GEN b)
+{
+  gel(S->todo, i) = a;
+  split_add(S, b);
+}
+/* split todo[i] into a and b, moved to done */
+static void
+split_done(struct split_t *S, long i, GEN a, GEN b)
+{
+  split_moveto_done(S, i, a);
+  split_add_done(S, b);
+}
+
+/* by splitting, assume p > 2 prime, deg(f) > 0, and f monic */
+static GEN
+FpX_roots_i(GEN f, GEN p)
+{
+  GEN pol, pol0, a, q;
+  struct split_t S;
+
+  split_init(&S, lg(f)-1);
+  settyp(S.done, t_COL);
+  if (ZX_valrem(f, &f)) split_add_done(&S, gen_0);
+  switch(degpol(f))
+  {
+    case 0: return S.done;
+    case 1: split_add_done(&S, subii(p, gel(f,2))); return S.done;
+    case 2: {
+      GEN s, r = FpX_quad_root(f, p, 1);
+      if (r) {
+        split_add_done(&S, r);
+        s = FpX_otherroot(f,r, p);
+        /* f not known to be square free yet */
+        if (!equalii(r, s)) split_add_done(&S, s);
+      }
+      return sort(S.done);
+    }
+  }
+
+  a = FpXQ_pow(pol_x(varn(f)), subiu(p,1), f,p);
+  if (lg(a) < 3) pari_err_PRIME("rootmod",p);
+  a = FpX_Fp_sub_shallow(a, gen_1, p); /* a = x^(p-1) - 1 mod f */
+  a = FpX_gcd(f,a, p);
+  if (!degpol(a)) return S.done;
+  split_add(&S, FpX_normalize(a,p));
+
+  q = shifti(p,-1);
+  pol0 = icopy(gen_1); /* constant term, will vary in place */
+  pol = deg1pol_shallow(gen_1, pol0, varn(f));
+  for (pol0[2] = 1;; pol0[2]++)
+  {
+    long j, l = lg(S.todo);
+    if (l == 1) return sort(S.done);
+    if (pol0[2] == 100 && !BPSW_psp(p)) pari_err_PRIME("polrootsmod",p);
+    for (j = 1; j < l; j++)
+    {
+      GEN c = gel(S.todo,j);
+      switch(degpol(c))
+      { /* convert linear and quadratics to roots, try to split the rest */
+        case 1:
+          split_moveto_done(&S, j, subii(p, gel(c,2)));
+          j--; l--; break;
+        case 2: {
+          GEN r = FpX_quad_root(c, p, 0), s = FpX_otherroot(c,r, p);
+          split_done(&S, j, r, s);
+          j--; l--; break;
+        }
+        default: {
+          /* b = pol^(p-1)/2 - 1 */
+          GEN b = FpX_Fp_sub_shallow(FpXQ_pow(pol,q, c,p), gen_1, p);
+          long db;
+          b = FpX_gcd(c,b, p); db = degpol(b);
+          if (db && db < degpol(c))
+          {
+            b = FpX_normalize(b, p);
+            c = FpX_div(c,b, p);
+            split_todo(&S, j, b, c);
+          }
+        }
+      }
+    }
+  }
+}
+/* assume p > 2 prime */
+static ulong
+Flx_oneroot_i(GEN f, ulong p)
+{
+  GEN pol, a;
+  ulong q;
+  long da;
+
+  if (Flx_val(f)) return 0;
+  switch(degpol(f))
+  {
+    case 1: return Fl_neg(f[2], p);
+    case 2: return Flx_quad_root(f, p, 1);
+  }
+
+  a = Flxq_powu(polx_Flx(f[1]), p - 1, f,p);
+  if (lg(a) < 3) pari_err_PRIME("rootmod",utoipos(p));
+  a = Flx_Fl_add(a, p-1, p); /* a = x^(p-1) - 1 mod f */
+  a = Flx_gcd(f,a, p);
+  da = degpol(a);
+  if (!da) return p;
+  a = Flx_normalize(a,p);
+
+  q = p >> 1;
+  pol = polx_Flx(f[1]);
+  for(pol[2] = 1;; pol[2]++)
+  {
+    if (pol[2] == 1000 && !uisprime(p)) pari_err_PRIME("Flx_oneroot",utoipos(p));
+    switch(da)
+    {
+      case 1: return Fl_neg(a[2], p);
+      case 2: return Flx_quad_root(a, p, 0);
+      default: {
+        GEN b = Flx_Fl_add(Flxq_powu(pol,q, a,p), p-1, p);
+        long db;
+        b = Flx_gcd(a,b, p); db = degpol(b);
+        if (db && db < da)
+        {
+          b = Flx_normalize(b, p);
+          if (db <= (da >> 1)) {
+            a = b;
+            da = db;
+          } else {
+            a = Flx_div(a,b, p);
+            da -= db;
+          }
+        }
+      }
+    }
+  }
+}
+/* assume p > 2 prime */
+static GEN
+FpX_oneroot_i(GEN f, GEN p)
+{
+  GEN pol, pol0, a, q;
+  long da;
+
+  if (ZX_val(f)) return gen_0;
+  switch(degpol(f))
+  {
+    case 1: return subii(p, gel(f,2));
+    case 2: return FpX_quad_root(f, p, 1);
+  }
+
+  a = FpXQ_pow(pol_x(varn(f)), subiu(p,1), f,p);
+  if (lg(a) < 3) pari_err_PRIME("rootmod",p);
+  a = FpX_Fp_sub_shallow(a, gen_1, p); /* a = x^(p-1) - 1 mod f */
+  a = FpX_gcd(f,a, p);
+  da = degpol(a);
+  if (!da) return NULL;
+  a = FpX_normalize(a,p);
+
+  q = shifti(p,-1);
+  pol0 = icopy(gen_1); /* constant term, will vary in place */
+  pol = deg1pol_shallow(gen_1, pol0, varn(f));
+  for (pol0[2]=1; ; pol0[2]++)
+  {
+    if (pol0[2] == 1000 && !BPSW_psp(p)) pari_err_PRIME("FpX_oneroot",p);
+    switch(da)
+    {
+      case 1: return subii(p, gel(a,2));
+      case 2: return FpX_quad_root(a, p, 0);
+      default: {
+        GEN b = FpX_Fp_sub_shallow(FpXQ_pow(pol,q, a,p), gen_1, p);
+        long db;
+        b = FpX_gcd(a,b, p); db = degpol(b);
+        if (db && db < da)
+        {
+          b = FpX_normalize(b, p);
+          if (db <= (da >> 1)) {
+            a = b;
+            da = db;
+          } else {
+            a = FpX_div(a,b, p);
+            da -= db;
+          }
+        }
+      }
+    }
+  }
+}
+
+ulong
+Flx_oneroot(GEN f, ulong p)
+{
+  pari_sp av = avma;
+  ulong r;
+  switch(lg(f))
+  {
+    case 2: return 0;
+    case 3: avma = av; return p;
+  }
+  if (p == 2) return Flx_oneroot_mod_2(f);
+  r = Flx_oneroot_i(Flx_normalize(f, p), p);
+  avma = av; return r;
+}
+
+/* assume that p is prime */
+GEN
+FpX_oneroot(GEN f, GEN pp) {
+  pari_sp av = avma;
+  ZX_factmod_init(&f, pp);
+  switch(lg(f))
+  {
+    case 2: avma = av; return gen_0;
+    case 3: avma = av; return NULL;
+  }
+  if (typ(f) == t_VECSMALL)
+  {
+    ulong r, p = pp[2];
+    if (p == 2)
+      r = Flx_oneroot_mod_2(f);
+    else
+      r = Flx_oneroot_i(f, p);
+    avma = av;
+    return (r == p)? NULL: utoi(r);
+  }
+  f = FpX_oneroot_i(f, pp);
+  if (!f) { avma = av; return NULL; }
+  return gerepileuptoint(av, f);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     FACTORISATION MODULO p                      */
+/*                                                                 */
+/*******************************************************************/
+
+/* Functions giving information on the factorisation. */
+
+/* u in Z[X], return kernel of (Frob - Id) over Fp[X] / u */
+GEN
+FpX_Berlekamp_ker(GEN u, GEN p)
+{
+  pari_sp ltop=avma;
+  long j,N = degpol(u);
+  GEN XP = FpXQ_pow(pol_x(varn(u)),p,u,p);
+  GEN Q  = FpXQ_matrix_pow(XP,N,N,u,p);
+  for (j=1; j<=N; j++)
+    gcoeff(Q,j,j) = Fp_sub(gcoeff(Q,j,j), gen_1, p);
+  return gerepileupto(ltop, FpM_ker(Q,p));
+}
+
+GEN
+F2x_Berlekamp_ker(GEN u)
+{
+  pari_sp ltop=avma;
+  long j,N = F2x_degree(u);
+  GEN Q, XP;
+  pari_timer T;
+  timer_start(&T);
+  XP = F2xq_sqr(polx_F2x(u[1]),u);
+  Q  = F2xq_matrix_pow(XP,N,N,u);
+  for (j=1; j<=N; j++)
+    F2m_flip(Q,j,j);
+  if(DEBUGLEVEL>=9) timer_printf(&T,"Berlekamp matrix");
+  Q = F2m_ker_sp(Q,0);
+  if(DEBUGLEVEL>=9) timer_printf(&T,"kernel");
+  return gerepileupto(ltop,Q);
+}
+
+GEN
+Flx_Berlekamp_ker(GEN u, ulong l)
+{
+  pari_sp ltop=avma;
+  long j,N = degpol(u);
+  GEN Q, XP;
+  pari_timer T;
+  timer_start(&T);
+  XP = Flxq_powu(polx_Flx(u[1]),l,u,l);
+  Q  = Flxq_matrix_pow(XP,N,N,u,l);
+  for (j=1; j<=N; j++)
+    coeff(Q,j,j) = Fl_sub(coeff(Q,j,j),1,l);
+  if(DEBUGLEVEL>=9) timer_printf(&T,"Berlekamp matrix");
+  Q = Flm_ker_sp(Q,l,0);
+  if(DEBUGLEVEL>=9) timer_printf(&T,"kernel");
+  return gerepileupto(ltop,Q);
+}
+
+/* product of terms of degree 1 in factorization of f */
+static GEN
+FpX_split_part(GEN f, GEN p)
+{
+  long n = degpol(f);
+  GEN z, X = pol_x(varn(f));
+  if (n <= 1) return f;
+  f = FpX_red(f, p);
+  z = FpXQ_pow(X, p, f, p);
+  z = FpX_sub(z, X, p);
+  return FpX_gcd(z,f,p);
+}
+
+/* Compute the number of roots in Fp without counting multiplicity
+ * return -1 for 0 polynomial. lc(f) must be prime to p. */
+long
+FpX_nbroots(GEN f, GEN p)
+{
+  pari_sp av = avma;
+  GEN z = FpX_split_part(f, p);
+  avma = av; return degpol(z);
+}
+
+int
+FpX_is_totally_split(GEN f, GEN p)
+{
+  long n=degpol(f);
+  pari_sp av = avma;
+  GEN z;
+  if (n <= 1) return 1;
+  if (cmpui(n, p) > 0) return 0;
+  f = FpX_red(f, p);
+  z = FpXQ_pow(pol_x(varn(f)), p, f, p);
+  avma = av;
+  return degpol(z) == 1 && gequal1(gel(z,3)) && !signe(gel(z,2)); /* x^p = x ? */
+}
+
+/* Flv_Flx( Flm_Flc_mul(x, Flx_Flv(y), p) ) */
+static GEN
+Flm_Flx_mul(GEN x, GEN y, ulong p)
+{
+  long i,k,l, ly = lg(y)-1;
+  GEN z;
+  long vs=y[1];
+  if (ly==1) return zero_Flx(vs);
+  l = lgcols(x);
+  y++;
+  z = zero_zv(l) + 1;
+  if (SMALL_ULONG(p))
+  {
+    for (k=1; k<ly; k++)
+    {
+      GEN c;
+      if (!y[k]) continue;
+      c = gel(x,k);
+      if (y[k] == 1)
+        for (i=1; i<l; i++)
+        {
+          z[i] += c[i];
+          if (z[i] & HIGHBIT) z[i] %= p;
+        }
+      else
+        for (i=1; i<l; i++)
+        {
+          z[i] += c[i] * y[k];
+          if (z[i] & HIGHBIT) z[i] %= p;
+        }
+    }
+    for (i=1; i<l; i++) z[i] %= p;
+  }
+  else
+  {
+    for (k=1; k<ly; k++)
+    {
+      GEN c;
+      if (!y[k]) continue;
+      c = gel(x,k);
+      if (y[k] == 1)
+        for (i=1; i<l; i++)
+          z[i] = Fl_add(z[i], c[i], p);
+      else
+        for (i=1; i<l; i++)
+          z[i] = Fl_add(z[i], Fl_mul(c[i],y[k],p), p);
+    }
+  }
+  while (--l && !z[l]);
+  if (!l) return zero_Flx(vs);
+  *z-- = vs; return z;
+}
+
+/* z must be squarefree mod p*/
+GEN
+Flx_nbfact_by_degree(GEN z, long *nb, ulong p)
+{
+  long lgg, d = 0, e = degpol(z);
+  GEN D = zero_zv(e);
+  pari_sp av = avma;
+  GEN g, w,  PolX = polx_Flx(z[1]);
+  GEN XP = Flxq_powu(PolX,p,z,p);
+  GEN MP = Flxq_matrix_pow(XP,e,e,z,p);
+
+  w = PolX; *nb = 0;
+  while (d < (e>>1))
+  { /* here e = degpol(z) */
+    d++;
+    w = Flm_Flx_mul(MP, w, p); /* w^p mod (z,p) */
+    g = Flx_gcd(z, Flx_sub(w, PolX, p), p);
+    lgg = degpol(g); if (!lgg) continue;
+
+    e -= lgg;
+    D[d] = lgg/d; *nb += D[d];
+    if (DEBUGLEVEL>5) err_printf("   %3ld fact. of degree %3ld\n", D[d], d);
+    if (!e) break;
+    z = Flx_div(z, g, p);
+    w = Flx_rem(w, z, p);
+  }
+  if (e)
+  {
+    if (DEBUGLEVEL>5) err_printf("   %3ld fact. of degree %3ld\n",1,e);
+    D[e] = 1; (*nb)++;
+  }
+  avma = av; return D;
+}
+
+/* z must be squarefree mod p*/
+long
+Flx_nbfact(GEN z, ulong p)
+{
+  pari_sp av = avma;
+  long nb; (void)Flx_nbfact_by_degree(z, &nb, p);
+  avma = av; return nb;
+}
+
+long
+Flx_nbroots(GEN f, ulong p)
+{
+  long n = degpol(f);
+  pari_sp av = avma;
+  GEN z, X;
+  if (n <= 1) return n;
+  X = polx_Flx(f[1]);
+  z = Flxq_powu(X, p, f, p);
+  z = Flx_sub(z, X, p);
+  z = Flx_gcd(z, f, p);
+  avma = av; return degpol(z);
+}
+
+long
+FpX_nbfact(GEN u, GEN p)
+{
+  pari_sp av = avma;
+  GEN vker = FpX_Berlekamp_ker(u, p);
+  avma = av; return lg(vker)-1;
+}
+
+static GEN
+try_pow(GEN w0, GEN pol, GEN p, GEN q, long r)
+{
+  GEN w2, w = FpXQ_pow(w0,q, pol,p);
+  long s;
+  if (gequal1(w)) return w0;
+  for (s=1; s<r; s++,w=w2)
+  {
+    w2 = gsqr(w);
+    w2 = FpX_rem(w2, pol, p);
+    if (gequal1(w2)) break;
+  }
+  return gequalm1(w)? NULL: w;
+}
+
+static GEN
+Flx_try_pow(GEN w0, GEN pol, ulong p, GEN q, long r)
+{
+  GEN w2, w = Flxq_pow(w0,q, pol,p);
+  long s;
+  if (Flx_equal1(w)) return w0;
+  for (s=1; s<r; s++,w=w2)
+  {
+    w2 = Flxq_sqr(w,pol,p);
+    if (Flx_equal1(w2)) break;
+  }
+  return degpol(w)==0 && (ulong)w[2] == p-1 ? NULL: w;
+}
+
+/* INPUT:
+ *  m integer (converted to polynomial w in Z[X] by stoFpX)
+ *  p prime; q = (p^d-1) / 2^r
+ *  t[0] polynomial of degree k*d product of k irreducible factors of degree d
+ *  t[0] is expected to be normalized (leading coeff = 1)
+ * OUTPUT:
+ *  t[0],t[1]...t[k-1] the k factors, normalized */
+
+static void
+F2x_split(ulong m, GEN *t, long d)
+{
+  long l, v, dv;
+  pari_sp av0, av;
+  GEN w;
+
+  dv = F2x_degree(*t); if (dv==d) return;
+  v=(*t)[1]; av0=avma;
+  for(av=avma;;avma=av)
+  {
+    GEN w0 = w = F2xq_powu(polx_F2x(v), m-1, *t); m += 2;
+    for (l=1; l<d; l++) w = F2x_add(w0, F2xq_sqr(w, *t));
+    w = F2x_gcd(*t,w);
+    l = F2x_degree(w); if (l && l!=dv) break;
+  }
+  w = gerepileupto(av0, w);
+  l /= d; t[l]=F2x_div(*t,w); *t=w;
+  F2x_split(m,t+l,d);
+  F2x_split(m,t,  d);
+}
+
+static void
+Flx_split(GEN *t, long d, ulong p, GEN q, long r)
+{
+  long l, v, dv;
+  pari_sp av0, av;
+  GEN w;
+
+  dv=degpol(*t); if (dv==d) return;
+  v=(*t)[1]; av0=avma;
+  for(av=avma;;avma=av)
+  {
+    w = random_Flx(dv,v,p);
+    w = Flx_try_pow(w,*t,p,q,r);
+    if (!w) continue;
+    w = Flx_Fl_add(w, p-1, p);
+    w = Flx_gcd(*t,w, p);
+    l = degpol(w); if (l && l!=dv) break;
+  }
+  w = Flx_normalize(w, p);
+  w = gerepileupto(av0, w);
+  l /= d; t[l]=Flx_div(*t,w,p); *t=w;
+  Flx_split(t+l,d,p,q,r);
+  Flx_split(t,  d,p,q,r);
+}
+
+
+static void
+FpX_split(GEN *t, long d, GEN  p, GEN q, long r)
+{
+  long l, v, dv = degpol(*t);
+  pari_sp av;
+  GEN w;
+
+  if (dv==d) return;
+  v = varn(*t);
+  av = avma;
+  for(;; avma = av)
+  {
+    w = random_FpX(dv, v, p);
+    w = try_pow(w,*t,p,q,r);
+    if (!w) continue;
+    w = FpX_Fp_sub_shallow(w, gen_1, p);
+    w = FpX_gcd(*t,w, p); l=degpol(w);
+    if (l && l!=dv) break;
+  }
+  w = FpX_normalize(w, p);
+  w = gerepileupto(av, w);
+  l /= d; t[l]=FpX_div(*t,w,p); *t=w;
+  FpX_split(t+l,d,p,q,r);
+  FpX_split(t,  d,p,q,r);
+}
+
+static int
+cmpGuGu(GEN a, GEN b) { return (ulong)a < (ulong)b? -1: (a == b? 0: 1); }
+
+/* p > 2 */
+static GEN
+FpX_is_irred_2(GEN f, GEN p, long d)
+{
+  switch(d)
+  {
+    case -1:
+    case 0: return NULL;
+    case 1: return gen_1;
+  }
+  return FpX_quad_factortype(f, p) == -1? gen_1: NULL;
+}
+/* p > 2 */
+static GEN
+FpX_degfact_2(GEN f, GEN p, long d)
+{
+  switch(d)
+  {
+    case -1:retmkvec2(mkvecsmall(-1),mkvecsmall(1));
+    case 0: return trivial_fact();
+    case 1: retmkvec2(mkvecsmall(1), mkvecsmall(1));
+  }
+  switch(FpX_quad_factortype(f, p)) {
+    case  1: retmkvec2(mkvecsmall2(1,1), mkvecsmall2(1,1));
+    case -1: retmkvec2(mkvecsmall(2), mkvecsmall(1));
+    default: retmkvec2(mkvecsmall(1), mkvecsmall(2));
+  }
+}
+
+GEN
+prime_fact(GEN x) { retmkmat2(mkcolcopy(x), mkcol(gen_1)); }
+GEN
+trivial_fact(void) { retmkmat2(cgetg(1,t_COL), cgetg(1,t_COL)); }
+
+/* Mod(0,p) * x, where x is f's main variable */
+static GEN
+Mod0pX(GEN f, GEN p)
+{ return scalarpol(mkintmod(gen_0, p), varn(f)); }
+static GEN
+zero_fact_intmod(GEN f, GEN p) { return prime_fact(Mod0pX(f,p)); }
+
+/* not gerepile safe */
+static GEN
+FpX_factor_2(GEN f, GEN p, long d)
+{
+  GEN r, s, R, S;
+  long v;
+  int sgn;
+  switch(d)
+  {
+    case -1: retmkvec2(mkcol(pol_0(varn(f))), mkvecsmall(1));
+    case  0: retmkvec2(cgetg(1,t_COL), cgetg(1,t_VECSMALL));
+    case  1: retmkvec2(mkcol(f), mkvecsmall(1));
+  }
+  r = FpX_quad_root(f, p, 1);
+  if (!r) return mkvec2(mkcol(f), mkvecsmall(1));
+  v = varn(f);
+  s = FpX_otherroot(f, r, p);
+  if (signe(r)) r = subii(p, r);
+  if (signe(s)) s = subii(p, s);
+  sgn = cmpii(s, r); if (sgn < 0) swap(s,r);
+  R = deg1pol_shallow(gen_1, r, v);
+  if (!sgn) return mkvec2(mkcol(R), mkvecsmall(2));
+  S = deg1pol_shallow(gen_1, s, v);
+  return mkvec2(mkcol2(R,S), mkvecsmall2(1,1));
+}
+static GEN
+FpX_factor_deg2(GEN f, GEN p, long d, long flag)
+{
+  switch(flag) {
+    case 2: return FpX_is_irred_2(f, p, d);
+    case 1: return FpX_degfact_2(f, p, d);
+    default: return FpX_factor_2(f, p, d);
+  }
+}
+
+static int
+F2x_quad_factortype(GEN x)
+{ return x[2] == 7 ? -1: x[2] == 6 ? 1 :0; }
+
+static GEN
+F2x_is_irred_2(GEN f, long d)
+{ return d == 1 || (d==2 && F2x_quad_factortype(f) == -1)? gen_1: NULL; }
+
+static GEN
+F2x_degfact_2(GEN f, long d)
+{
+  if (!d) return trivial_fact();
+  if (d == 1) return mkvec2(mkvecsmall(1), mkvecsmall(1));
+  switch(F2x_quad_factortype(f)) {
+    case 1: return mkvec2(mkvecsmall2(1,1), mkvecsmall2(1,1));
+    case -1:return mkvec2(mkvecsmall(2), mkvecsmall(1));
+    default: return mkvec2(mkvecsmall(1), mkvecsmall(2));
+  }
+}
+
+static GEN
+F2x_factor_2(GEN f, long d)
+{
+  long v = f[1];
+  if (d < 0) pari_err(e_ROOTS0,"Flx_factor_2");
+  if (!d) return mkvec2(cgetg(1,t_COL), cgetg(1,t_VECSMALL));
+  if (d == 1) return mkvec2(mkcol(f), mkvecsmall(1));
+  switch(F2x_quad_factortype(f))
+  {
+  case -1: return mkvec2(mkcol(f), mkvecsmall(1));
+  case 0:  return mkvec2(mkcol(mkvecsmall2(v,2+F2x_coeff(f,0))), mkvecsmall(2));
+  default: return mkvec2(mkcol2(mkvecsmall2(v,2),mkvecsmall2(v,3)), mkvecsmall2(1,1));
+  }
+}
+static GEN
+F2x_factor_deg2(GEN f, long d, long flag)
+{
+  switch(flag) {
+    case 2: return F2x_is_irred_2(f, d);
+    case 1: return F2x_degfact_2(f, d);
+    default: return F2x_factor_2(f, d);
+  }
+}
+
+static void
+split_squares(struct split_t *S, GEN g, ulong p)
+{
+  ulong q = p >> 1;
+  GEN a = Flx_mod_Xnm1(g, q, p); /* mod x^(p-1)/2 - 1 */
+  if (degpol(a) < 0)
+  {
+    ulong i;
+    split_add_done(S, (GEN)1);
+    for (i = 2; i <= q; i++) split_add_done(S, (GEN)Fl_sqr(i,p));
+  } else {
+    (void)Flx_valrem(a, &a);
+    if (degpol(a))
+    {
+      a = Flx_gcd(a, Flx_Xnm1(g[1], q, p), p);
+      if (degpol(a)) split_add(S, Flx_normalize(a, p));
+    }
+  }
+}
+static void
+split_nonsquares(struct split_t *S, GEN g, ulong p)
+{
+  ulong q = p >> 1;
+  GEN a = Flx_mod_Xn1(g, q, p); /* mod x^(p-1)/2 + 1 */
+  if (degpol(a) < 0)
+  {
+    ulong i, z = Fl_nonsquare(p);
+    split_add_done(S, (GEN)z);
+    for (i = 2; i <= q; i++) split_add_done(S, (GEN)Fl_mul(z, Fl_sqr(i,p), p));
+  } else {
+    (void)Flx_valrem(a, &a);
+    if (degpol(a))
+    {
+      a = Flx_gcd(a, Flx_Xn1(g[1], q, p), p);
+      if (degpol(a)) split_add(S, Flx_normalize(a, p));
+    }
+  }
+}
+/* p > 2. f monic Flx, f(0) != 0. Add to split_t structs coprime factors
+ * of g = \prod_{f(a) = 0} (X - a). Return 0 when f(x) = 0 for all x in Fp* */
+static int
+split_Flx_cut_out_roots(struct split_t *S, GEN f, ulong p)
+{
+  GEN a, g = Flx_mod_Xnm1(f, p-1, p); /* f mod x^(p-1) - 1 */
+  long d = degpol(g);
+  if (d < 0) return 0;
+  if (g != f) { (void)Flx_valrem(g, &g); d = degpol(g); } /*kill powers of x*/
+  if (!d) return 1;
+  if (p <= 1.4 * (ulong)d) {
+    /* small p; split further using x^((p-1)/2) +/- 1.
+     * 30% degree drop makes the extra gcd worth it. */
+    split_squares(S, g, p);
+    split_nonsquares(S, g, p);
+  } else { /* large p; use x^(p-1) - 1 directly */
+    a = Flxq_powu(polx_Flx(f[1]), p-1, g,p);
+    if (lg(a) < 3) pari_err_PRIME("rootmod",utoipos(p));
+    a = Flx_Fl_add(a, p-1, p); /* a = x^(p-1) - 1 mod g */
+    g = Flx_gcd(g,a, p);
+    if (degpol(g)) split_add(S, Flx_normalize(g,p));
+  }
+  return 1;
+}
+
+/* by splitting, assume p > 2 prime, deg(f) > 0, and f monic */
+static GEN
+Flx_roots_i(GEN f, ulong p)
+{
+  GEN pol, g;
+  ulong q = p >> 1;
+  struct split_t S;
+
+  split_init(&S, lg(f)-1);
+  settyp(S.done, t_VECSMALL);
+  if (Flx_valrem(f, &g)) split_add_done(&S, (GEN)0);
+  if (! split_Flx_cut_out_roots(&S, g, p))
+    return all_roots_mod_p(p, lg(S.done) == 1);
+  pol = polx_Flx(f[1]);
+  for (pol[2]=1; ; pol[2]++)
+  {
+    long j, l = lg(S.todo);
+    if (l == 1) { vecsmall_sort(S.done); return S.done; }
+    if (pol[2] == 100 && !uisprime(p)) pari_err_PRIME("polrootsmod",utoipos(p));
+    for (j = 1; j < l; j++)
+    {
+      GEN c = gel(S.todo,j);
+      switch(degpol(c))
+      {
+        case 1:
+          split_moveto_done(&S, j, (GEN)(p - c[2]));
+          j--; l--; break;
+        case 2: {
+          ulong r = Flx_quad_root(c, p, 0), s = Flx_otherroot(c,r, p);
+          split_done(&S, j, (GEN)r, (GEN)s);
+          j--; l--; break;
+        }
+        default: {
+          GEN b = Flx_Fl_add(Flxq_powu(pol,q, c,p), p-1, p); /* pol^(p-1)/2 */
+          long db;
+          b = Flx_gcd(c,b, p); db = degpol(b);
+          if (db && db < degpol(c))
+          {
+            b = Flx_normalize(b, p);
+            c = Flx_div(c,b, p);
+            split_todo(&S, j, b, c);
+          }
+        }
+      }
+    }
+  }
+}
+
+GEN
+Flx_roots(GEN f, ulong p)
+{
+  pari_sp av = avma;
+  switch(lg(f))
+  {
+    case 2: pari_err_ROOTS0("Flx_roots");
+    case 3: avma = av; return cgetg(1, t_VECSMALL);
+  }
+  if (p == 2) return Flx_root_mod_2(f);
+  return gerepileuptoleaf(av, Flx_roots_i(Flx_normalize(f, p), p));
+}
+
+/* assume x reduced mod p, monic. */
+static int
+Flx_quad_factortype(GEN x, ulong p)
+{
+  ulong b = x[3], c = x[2];
+  return krouu(Fl_disc_bc(b, c, p), p);
+}
+static GEN
+Flx_is_irred_2(GEN f, ulong p, long d)
+{
+  if (!d) return NULL;
+  if (d == 1) return gen_1;
+  return Flx_quad_factortype(f, p) == -1? gen_1: NULL;
+}
+static GEN
+Flx_degfact_2(GEN f, ulong p, long d)
+{
+  if (!d) return trivial_fact();
+  if (d == 1) return mkvec2(mkvecsmall(1), mkvecsmall(1));
+  switch(Flx_quad_factortype(f, p)) {
+    case 1: return mkvec2(mkvecsmall2(1,1), mkvecsmall2(1,1));
+    case -1:return mkvec2(mkvecsmall(2), mkvecsmall(1));
+    default: return mkvec2(mkvecsmall(1), mkvecsmall(2));
+  }
+}
+/* p > 2 */
+static GEN
+Flx_factor_2(GEN f, ulong p, long d)
+{
+  ulong r, s;
+  GEN R,S;
+  long v = f[1];
+  if (d < 0) pari_err(e_ROOTS0,"Flx_factor_2");
+  if (!d) return mkvec2(cgetg(1,t_COL), cgetg(1,t_VECSMALL));
+  if (d == 1) return mkvec2(mkcol(f), mkvecsmall(1));
+  r = Flx_quad_root(f, p, 1);
+  if (r==p) return mkvec2(mkcol(f), mkvecsmall(1));
+  s = Flx_otherroot(f, r, p);
+  r = Fl_neg(r, p);
+  s = Fl_neg(s, p);
+  if (s < r) lswap(s,r);
+  R = mkvecsmall3(v,r,1);
+  if (s == r) return mkvec2(mkcol(R), mkvecsmall(2));
+  S = mkvecsmall3(v,s,1);
+  return mkvec2(mkcol2(R,S), mkvecsmall2(1,1));
+}
+static GEN
+Flx_factor_deg2(GEN f, ulong p, long d, long flag)
+{
+  switch(flag) {
+    case 2: return Flx_is_irred_2(f, p, d);
+    case 1: return Flx_degfact_2(f, p, d);
+    default: return Flx_factor_2(f, p, d);
+  }
+}
+
+static GEN
+F2x_factcantor_i(GEN f, long flag)
+{
+  long j, e, nbfact, d = F2x_degree(f);
+  ulong k;
+  GEN X, E, f2, g, g1, u, v, y, t;
+
+  if (d <= 2) return F2x_factor_deg2(f, d, flag);
+  /* to hold factors and exponents */
+  t = flag ? cgetg(d+1,t_VECSMALL): cgetg(d+1,t_VEC);
+  E = cgetg(d+1, t_VECSMALL);
+  X = polx_F2x(f[1]);
+  e = nbfact = 1;
+  for(;;)
+  {
+    f2 = F2x_gcd(f,F2x_deriv(f));
+    if (flag == 2 && F2x_degree(f2) > 0) return NULL;
+    g1 = F2x_div(f,f2);
+    k = 0;
+    while (F2x_degree(g1)>0)
+    {
+      pari_sp av;
+      long du, dg, dg1;
+      k++; if (k%2==0) { k++; f2 = F2x_div(f2,g1); }
+      u = g1; g1 = F2x_gcd(f2,g1);
+      du = F2x_degree(u);
+      dg1 = F2x_degree(g1);
+      if (dg1>0)
+      {
+        f2= F2x_div(f2,g1);
+        if (du == dg1) continue;
+        u = F2x_div( u,g1);
+        du -= dg1;
+      }
+      /* here u is square-free (product of irred. of multiplicity e * k) */
+      v = X;
+      av = avma;
+      for (d=1; d <= du>>1; d++)
+      {
+        v = F2xq_sqr(v, u);
+        g = F2x_gcd(F2x_add(v, X), u);
+        dg = F2x_degree(g);
+        if (dg <= 0) {avma = (pari_sp)v; v = gerepileuptoleaf(av,v); continue;}
+        /* g is a product of irred. pols, all of which have degree d */
+        j = nbfact+dg/d;
+        if (flag)
+        {
+          if (flag == 2) return NULL;
+          for ( ; nbfact<j; nbfact++) { t[nbfact]=d; E[nbfact]=e*k; }
+        }
+        else
+        {
+          gel(t,nbfact) = g;
+          F2x_split(2,&gel(t,nbfact),d);
+          for (; nbfact<j; nbfact++) E[nbfact]=e*k;
+        }
+        du -= dg;
+        u = F2x_div(u,g);
+        v = F2x_rem(v,u);
+        av = avma;
+      }
+      if (du)
+      {
+        if (flag) t[nbfact] = du;
+        else  gel(t,nbfact) = u;
+        E[nbfact++]=e*k;
+      }
+    }
+    if (F2x_degree(f2)==0) break;
+    e <<=1; f = F2x_sqrt(f2);
+  }
+  if (flag == 2) return gen_1; /* irreducible */
+  setlg(t, nbfact);
+  setlg(E, nbfact); y = mkvec2(t, E);
+  return flag ? sort_factor(y, (void*)cmpGuGu, cmp_nodata)
+              : sort_factor_pol(y, cmpGuGu);
+}
+GEN
+F2x_factcantor(GEN f, long flag)
+{
+  pari_sp av = avma;
+  GEN z = F2x_factcantor_i(f, flag);
+  if (flag == 2) { avma = av; return z; }
+  return gerepilecopy(av, z);
+}
+
+int
+F2x_is_irred(GEN f) { return !!F2x_factcantor_i(f,2); }
+
+void
+F2xV_to_FlxV_inplace(GEN v)
+{
+  long i;
+  for(i=1;i<lg(v);i++) gel(v,i)= F2x_to_Flx(gel(v,i));
+}
+void
+FlxV_to_ZXV_inplace(GEN v)
+{
+  long i;
+  for(i=1;i<lg(v);i++) gel(v,i)= Flx_to_ZX(gel(v,i));
+}
+void
+F2xV_to_ZXV_inplace(GEN v)
+{
+  long i;
+  for(i=1;i<lg(v);i++) gel(v,i)= F2x_to_ZX(gel(v,i));
+}
+
+/* factor f mod pp.
+ * flag = 1: return the degrees, not the factors
+ * flag = 2: return NULL if f is not irreducible */
+static GEN
+Flx_factcantor_i(GEN f, ulong p, long flag)
+{
+  long j, e, nbfact, d = degpol(f);
+  ulong k;
+  GEN X, E, f2, g, g1, u, v, q, y, t;
+  if (p==2) { /*We need to handle 2 specially */
+    GEN F = F2x_factcantor_i(Flx_to_F2x(f),flag);
+    if (flag==0) F2xV_to_FlxV_inplace(gel(F,1));
+    return F;
+  }
+  /* Now we assume p odd */
+  if (d <= 2) return Flx_factor_deg2(f, p, d, flag);
+
+  /* to hold factors and exponents */
+  t = cgetg(d+1, flag? t_VECSMALL: t_VEC);
+  E = cgetg(d+1, t_VECSMALL);
+  X = polx_Flx(f[1]);
+  e = nbfact = 1;
+  for(;;)
+  {
+    f2 = Flx_gcd(f,Flx_deriv(f,p), p);
+    if (flag == 2 && degpol(f2) > 0) return NULL;
+    g1 = Flx_div(f,f2,p);
+    k = 0;
+    while (degpol(g1)>0)
+    {
+      pari_sp av;
+      long du,dg;
+      k++; if (k%p==0) { k++; f2 = Flx_div(f2,g1,p); }
+      u = g1; g1 = Flx_gcd(f2,g1, p);
+      if (degpol(g1)>0)
+      {
+        u = Flx_div( u,g1,p);
+        f2= Flx_div(f2,g1,p);
+      }
+      du = degpol(u);
+      if (du <= 0) continue;
+
+      /* here u is square-free (product of irred. of multiplicity e * k) */
+      v=X;
+      av = avma;
+      for (d=1; d <= du>>1; d++)
+      {
+        v = Flxq_powu(v, p, u, p);
+        g = Flx_gcd(Flx_sub(v, X, p), u, p);
+        dg = degpol(g);
+        if (dg <= 0) {avma = (pari_sp)v; v = gerepileuptoleaf(av,v); continue;}
+        /* g is a product of irred. pols, all of which have degree d */
+        j = nbfact+dg/d;
+        if (flag)
+        {
+          if (flag == 2) return NULL;
+          for ( ; nbfact<j; nbfact++) { t[nbfact]=d; E[nbfact]=e*k; }
+        }
+        else
+        {
+          GEN pd = powuu(p, d);
+          long r;
+          g = Flx_normalize(g, p);
+          gel(t,nbfact) = g; q = subis(pd,1);
+          r = vali(q); q = shifti(pd,-r);
+          Flx_split(&gel(t,nbfact),d,p,q,r);
+          for (; nbfact<j; nbfact++) E[nbfact]=e*k;
+        }
+        du -= dg;
+        u = Flx_div(u,g,p);
+        v = Flx_rem(v,u,p);
+        av = avma;
+      }
+      if (du)
+      {
+        if (flag) t[nbfact] = du;
+        else  gel(t,nbfact) = Flx_normalize(u, p);
+        E[nbfact++]=e*k;
+      }
+    }
+    if (degpol(f2)==0) break;
+    e *= p; f = Flx_deflate(f2, p);
+  }
+  if (flag == 2) return gen_1; /* irreducible */
+  setlg(t, nbfact);
+  setlg(E, nbfact); y = mkvec2(t, E);
+  return flag ? sort_factor(y, (void*)cmpGuGu, cmp_nodata)
+              : sort_factor_pol(y, cmpGuGu);
+}
+GEN
+Flx_factcantor(GEN f, ulong p, long flag)
+{
+  pari_sp av = avma;
+  GEN z = Flx_factcantor_i(Flx_normalize(f,p),p,flag);
+  if (flag == 2) { avma = av; return z; }
+  return gerepilecopy(av, z);
+}
+GEN
+Flx_degfact(GEN f, ulong p)
+{
+  pari_sp av = avma;
+  GEN z = Flx_factcantor_i(Flx_normalize(f,p),p,1);
+  return gerepilecopy(av, z);
+}
+int
+Flx_is_irred(GEN f, ulong p) { return !!Flx_factcantor_i(f,p,2); }
+
+/* factor f (FpX or Flx) mod pp.
+ * flag = 1: return the degrees, not the factors
+ * flag = 2: return NULL if f is not irreducible.
+ * Not gerepile-safe */
+static GEN
+FpX_factcantor_i(GEN f, GEN pp, long flag)
+{
+  long j, nbfact, d = degpol(f);
+  ulong k;
+  GEN X, E,y,f2,g,g1,v,q;
+  GEN t;
+  if (typ(f) == t_VECSMALL)
+  { /* lgefint(pp) = 3 */
+    GEN F;
+    ulong p = pp[2];
+    if (p==2) {
+      F = F2x_factcantor_i(Flx_to_F2x(f),flag);
+      if (flag==0) F2xV_to_ZXV_inplace(gel(F,1));
+    } else {
+      F = Flx_factcantor_i(f,p,flag);
+      if (flag==0) FlxV_to_ZXV_inplace(gel(F,1));
+    }
+    return F;
+  }
+  /*Now, we can assume that p is large and odd*/
+  if (d <= 2) return FpX_factor_deg2(f,pp,d,flag);
+
+  /* to hold factors and exponents */
+  t = cgetg(d+1, flag? t_VECSMALL: t_VEC);
+  E = cgetg(d+1, t_VECSMALL);
+  X = pol_x(varn(f)); nbfact = 1;
+  f2 = FpX_gcd(f,ZX_deriv(f), pp);
+  if (flag == 2 && degpol(f2)) return NULL;
+  g1 = degpol(f2)? FpX_div(f,f2,pp): f; /* squarefree */
+  k = 0;
+  while (degpol(g1)>0)
+  {
+    long du,dg;
+    GEN u, S;
+    k++;
+    u = g1; g1 = FpX_gcd(f2,g1, pp);
+    if (degpol(g1)>0)
+    {
+      u = FpX_div( u,g1,pp);
+      f2= FpX_div(f2,g1,pp);
+    }
+    du = degpol(u);
+    if (du <= 0) continue;
+
+    /* here u is square-free (product of irred. of multiplicity e * k) */
+    v=X;
+    S = du==1 ?  cgetg(1, t_VEC): FpXQ_powers(FpXQ_pow(v, pp, u, pp), du-1, u, pp);
+    for (d=1; d <= du>>1; d++)
+    {
+      v = FpX_FpXQV_eval(v, S, u, pp);
+      g = FpX_gcd(ZX_sub(v, X), u, pp);
+      dg = degpol(g);
+      if (dg <= 0) continue;
+
+      /* g is a product of irred. pols, all of which have degree d */
+      j = nbfact+dg/d;
+      if (flag)
+      {
+        if (flag == 2) return NULL;
+        for ( ; nbfact<j; nbfact++) { t[nbfact]=d; E[nbfact]=k; }
+      }
+      else
+      {
+        GEN pd = powiu(pp,d);
+        long r;
+        g = FpX_normalize(g, pp);
+        gel(t,nbfact) = g; q = subis(pd,1);
+        r = vali(q); q = shifti(q,-r);
+        FpX_split(&gel(t,nbfact),d,pp,q,r);
+        for (; nbfact<j; nbfact++) E[nbfact]=k;
+      }
+      du -= dg;
+      u = FpX_div(u,g,pp);
+      v = FpX_rem(v,u,pp);
+    }
+    if (du)
+    {
+      if (flag) t[nbfact] = du;
+      else gel(t,nbfact) = FpX_normalize(u, pp);
+      E[nbfact++]=k;
+    }
+  }
+  if (flag == 2) return gen_1; /* irreducible */
+  setlg(t, nbfact);
+  setlg(E, nbfact); y = mkvec2(t, E);
+  return flag ? sort_factor(y, (void*)&cmpGuGu, cmp_nodata)
+              : sort_factor_pol(y, cmpii);
+}
+GEN
+FpX_factcantor(GEN f, GEN pp, long flag)
+{
+  pari_sp av = avma;
+  GEN z;
+  ZX_factmod_init(&f,pp);
+  z = FpX_factcantor_i(f,pp,flag);
+  if (flag == 2) { avma = av; return z; }
+  return gerepilecopy(av, z);
+}
+
+static GEN
+factmod_aux(GEN f, GEN p, GEN (*Factor)(GEN,GEN,long), long flag)
+{
+  pari_sp av = avma;
+  long j, nbfact;
+  GEN z, y, t, E, u, v;
+
+  factmod_init(&f, p);
+  switch(lg(f))
+  {
+    case 3: avma = av; return trivial_fact();
+    case 2: return gerepileupto(av, zero_fact_intmod(f, p));
+  }
+  z = Factor(f,p,flag); t = gel(z,1); E = gel(z,2);
+  y = cgetg(3, t_MAT); nbfact = lg(t);
+  u = cgetg(nbfact,t_COL); gel(y,1) = u;
+  v = cgetg(nbfact,t_COL); gel(y,2) = v;
+  if (flag)
+    for (j=1; j<nbfact; j++)
+    {
+      gel(u,j) = utoi((ulong)t[j]);
+      gel(v,j) = utoi((ulong)E[j]);
+    }
+  else
+    for (j=1; j<nbfact; j++)
+    {
+      gel(u,j) = FpX_to_mod(gel(t,j), p);
+      gel(v,j) = utoi((ulong)E[j]);
+    }
+  return gerepileupto(av, y);
+}
+GEN
+factcantor0(GEN f, GEN p, long flag)
+{ return factmod_aux(f, p, &FpX_factcantor_i, flag); }
+GEN
+factmod(GEN f, GEN p)
+{ return factmod_aux(f, p, &FpX_Berlekamp_i, 0); }
+
+/* Use this function when you think f is reducible, and that there are lots of
+ * factors. If you believe f has few factors, use FpX_nbfact(f,p)==1 instead */
+int
+FpX_is_irred(GEN f, GEN p) {
+  ZX_factmod_init(&f,p);
+  return !!FpX_factcantor_i(f,p,2);
+}
+GEN
+FpX_degfact(GEN f, GEN p) {
+  pari_sp av = avma;
+  GEN z;
+  ZX_factmod_init(&f,p);
+  z = FpX_factcantor_i(f,p,1);
+  return gerepilecopy(av, z);
+}
+GEN
+factcantor(GEN f, GEN p) { return factcantor0(f,p,0); }
+GEN
+simplefactmod(GEN f, GEN p) { return factcantor0(f,p,1); }
+
+/* set x <-- x + c*y mod p */
+/* x is not required to be normalized.*/
+static void
+Flx_addmul_inplace(GEN gx, GEN gy, ulong c, ulong p)
+{
+  long i, lx, ly;
+  ulong *x=(ulong *)gx;
+  ulong *y=(ulong *)gy;
+  if (!c) return;
+  lx = lg(gx);
+  ly = lg(gy);
+  if (lx<ly) pari_err_BUG("lx<ly in Flx_addmul_inplace");
+  if (SMALL_ULONG(p))
+    for (i=2; i<ly;  i++) x[i] = (x[i] + c*y[i]) % p;
+  else
+    for (i=2; i<ly;  i++) x[i] = Fl_add(x[i], Fl_mul(c,y[i],p),p);
+}
+
+/* return a random polynomial in F_q[v], degree < d1 */
+GEN
+FqX_rand(long d1, long v, GEN T, GEN p)
+{
+  long i, d = d1+2, k = get_FpX_degree(T), w = get_FpX_var(T);
+  GEN y = cgetg(d,t_POL); y[1] = evalsigne(1) | evalvarn(v);
+  for (i=2; i<d; i++) gel(y,i) = random_FpX(k, w, p);
+  (void)normalizepol_lg(y,d); return y;
+}
+
+#define set_irred(i) { if ((i)>ir) swap(t[i],t[ir]); ir++;}
+/* assume x1 != 0 */
+static GEN
+deg1_Flx(ulong x1, ulong x0, ulong sv)
+{
+  return mkvecsmall3(sv, x0, x1);
+}
+
+long
+F2x_split_Berlekamp(GEN *t)
+{
+  GEN u = *t, a, b, vker;
+  long lb, d, i, ir, L, la, sv = u[1], du = F2x_degree(u);
+
+  if (du == 1) return 1;
+  if (du == 2)
+  {
+    if (F2x_quad_factortype(u) == 1) /* 0 is a root: shouldn't occur */
+    {
+      t[0] = mkvecsmall2(sv, 2);
+      t[1] = mkvecsmall2(sv, 3);
+      return 2;
+    }
+    return 1;
+  }
+
+  vker = F2x_Berlekamp_ker(u);
+  lb = lgcols(vker);
+  d = lg(vker)-1;
+  ir = 0;
+  /* t[i] irreducible for i < ir, still to be treated for i < L */
+  for (L=1; L<d; )
+  {
+    GEN pol;
+    if (d == 2)
+      pol = F2v_to_F2x(gel(vker,2), sv);
+    else
+    {
+      GEN v = zero_zv(lb);
+      v[1] = du;
+      v[2] = random_Fl(2); /*Assume vker[1]=1*/
+      for (i=2; i<=d; i++)
+        if (random_Fl(2)) F2v_add_inplace(v, gel(vker,i));
+      pol = F2v_to_F2x(v, sv);
+    }
+    for (i=ir; i<L && L<d; i++)
+    {
+      a = t[i]; la = F2x_degree(a);
+      if (la == 1) { set_irred(i); }
+      else if (la == 2)
+      {
+        if (F2x_quad_factortype(a) == 1) /* 0 is a root: shouldn't occur */
+        {
+          t[i] = mkvecsmall2(sv, 2);
+          t[L] = mkvecsmall2(sv, 3); L++;
+        }
+        set_irred(i);
+      }
+      else
+      {
+        pari_sp av = avma;
+        long lb;
+        b = F2x_rem(pol, a);
+        if (F2x_degree(b) <= 0) { avma=av; continue; }
+        b = F2x_gcd(a,b); lb = F2x_degree(b);
+        if (lb && lb < la)
+        {
+          t[L] = F2x_div(a,b);
+          t[i]= b; L++;
+        }
+        else avma = av;
+      }
+    }
+  }
+  return d;
+}
+
+/* p != 2 */
+long
+Flx_split_Berlekamp(GEN *t, ulong p)
+{
+  GEN u = *t, a,b,vker;
+  long d, i, ir, L, la, lb, sv = u[1];
+  long l = lg(u);
+  ulong po2;
+
+  if (p == 2)
+  {
+    *t = Flx_to_F2x(*t);
+    d = F2x_split_Berlekamp(t);
+    for (i = 1; i <= d; i++) t[i] = F2x_to_Flx(t[i]);
+    return d;
+  }
+  la = degpol(u);
+  if (la == 1) return 1;
+  if (la == 2)
+  {
+    ulong r = Flx_quad_root(u,p,1);
+    if (r != p)
+    {
+      t[0] = deg1_Flx(1, p - r, sv); r = Flx_otherroot(u,r,p);
+      t[1] = deg1_Flx(1, p - r, sv);
+      return 2;
+    }
+    return 1;
+  }
+
+  vker = Flx_Berlekamp_ker(u,p);
+  vker = Flm_to_FlxV(vker, sv);
+  d = lg(vker)-1;
+  po2 = p >> 1; /* (p-1) / 2 */
+  ir = 0;
+  /* t[i] irreducible for i < ir, still to be treated for i < L */
+  for (L=1; L<d; )
+  {
+    GEN pol = zero_zv(l-2);
+    pol[1] = sv;
+    pol[2] = random_Fl(p); /*Assume vker[1]=1*/
+    for (i=2; i<=d; i++)
+      Flx_addmul_inplace(pol, gel(vker,i), random_Fl(p), p);
+    (void)Flx_renormalize(pol,l-1);
+
+    for (i=ir; i<L && L<d; i++)
+    {
+      a = t[i]; la = degpol(a);
+      if (la == 1) { set_irred(i); }
+      else if (la == 2)
+      {
+        ulong r = Flx_quad_root(a,p,1);
+        if (r != p)
+        {
+          t[i] = deg1_Flx(1, p - r, sv); r = Flx_otherroot(a,r,p);
+          t[L] = deg1_Flx(1, p - r, sv); L++;
+        }
+        set_irred(i);
+      }
+      else
+      {
+        pari_sp av = avma;
+        b = Flx_rem(pol, a, p);
+        if (degpol(b) <= 0) { avma=av; continue; }
+        b = Flx_Fl_add(Flxq_powu(b,po2, a,p), p-1, p);
+        b = Flx_gcd(a,b, p); lb = degpol(b);
+        if (lb && lb < la)
+        {
+          b = Flx_normalize(b, p);
+          t[L] = Flx_div(a,b,p);
+          t[i]= b; L++;
+        }
+        else avma = av;
+      }
+    }
+  }
+  return d;
+}
+
+long
+FpX_split_Berlekamp(GEN *t, GEN p)
+{
+  GEN u = *t, a,b,po2,vker;
+  long d, i, ir, L, la, lb, vu = varn(u);
+  if (lgefint(p) == 3)
+  {
+    ulong up = p[2];
+    if (up == 2)
+    {
+      *t = ZX_to_F2x(*t);
+      d = F2x_split_Berlekamp(t);
+      for (i = 0; i < d; i++) t[i] = F2x_to_ZX(t[i]);
+    }
+    else
+    {
+      *t = ZX_to_Flx(*t, up);
+      d = Flx_split_Berlekamp(t, up);
+      for (i = 0; i < d; i++) t[i] = Flx_to_ZX(t[i]);
+    }
+    return d;
+  }
+  la = degpol(u);
+  if (la == 1) return 1;
+  if (la == 2)
+  {
+    GEN r = FpX_quad_root(u,p,1);
+    if (r)
+    {
+      t[0] = deg1pol_shallow(gen_1, subii(p,r), vu); r = FpX_otherroot(u,r,p);
+      t[1] = deg1pol_shallow(gen_1, subii(p,r), vu);
+      return 2;
+    }
+    return 1;
+  }
+  vker = FpX_Berlekamp_ker(u,p);
+  vker = RgM_to_RgXV(vker,vu);
+  d = lg(vker)-1;
+  po2 = shifti(p, -1); /* (p-1) / 2 */
+  ir = 0;
+  /* t[i] irreducible for i < ir, still to be treated for i < L */
+  for (L=1; L<d; )
+  {
+    GEN pol = scalar_ZX_shallow(randomi(p), vu);
+    for (i=2; i<=d; i++)
+      pol = ZX_add(pol, ZX_Z_mul(gel(vker,i), randomi(p)));
+    pol = FpX_red(pol,p);
+    for (i=ir; i<L && L<d; i++)
+    {
+      a = t[i]; la = degpol(a);
+      if (la == 1) { set_irred(i); }
+      else if (la == 2)
+      {
+        GEN r = FpX_quad_root(a,p,1);
+        if (r)
+        {
+          t[i] = deg1pol_shallow(gen_1, subii(p,r), vu); r = FpX_otherroot(a,r,p);
+          t[L] = deg1pol_shallow(gen_1, subii(p,r), vu); L++;
+        }
+        set_irred(i);
+      }
+      else
+      {
+        pari_sp av = avma;
+        b = FpX_rem(pol, a, p);
+        if (degpol(b) <= 0) { avma=av; continue; }
+        b = FpX_Fp_sub_shallow(FpXQ_pow(b,po2, a,p), gen_1, p);
+        b = FpX_gcd(a,b, p); lb = degpol(b);
+        if (lb && lb < la)
+        {
+          b = FpX_normalize(b, p);
+          t[L] = FpX_div(a,b,p);
+          t[i]= b; L++;
+        }
+        else avma = av;
+      }
+    }
+  }
+  return d;
+}
+
+GEN
+FqX_deriv(GEN f, /*unused*/GEN T, GEN p) {
+  (void)T; return FpXX_red(RgX_deriv(f), p);
+}
+
+long
+FqX_is_squarefree(GEN P, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN z = FqX_gcd(P, FqX_deriv(P, T, p), T, p);
+  avma = av;
+  return degpol(z)==0;
+}
+
+static GEN
+F2x_Berlekamp_i(GEN f, long flag)
+{
+  long e, nbfact, val, d = F2x_degree(f);
+  ulong k, j;
+  GEN y, E, f2, g1, t;
+
+  if (d <= 2) return F2x_factor_deg2(f, d, flag);
+
+  /* to hold factors and exponents */
+  t = cgetg(d+1, flag? t_VECSMALL: t_VEC);
+  E = cgetg(d+1,t_VECSMALL);
+  val = F2x_valrem(f, &f);
+  e = nbfact = 1;
+  if (val) {
+    if (flag == 2 && val > 1) return NULL;
+    if (flag == 1)
+      t[1] = 1;
+    else
+      gel(t,1) = polx_F2x(f[1]);
+    E[1] = val; nbfact++;
+  }
+
+  for(;;)
+  {
+    f2 = F2x_gcd(f,F2x_deriv(f));
+    if (flag == 2 && F2x_degree(f2)) return NULL;
+    g1 = F2x_degree(f2)? F2x_div(f,f2): f; /* squarefree */
+    k = 0;
+    while (F2x_degree(g1)>0)
+    {
+      GEN u;
+      k++; if (k%2 == 0) { k++; f2 = F2x_div(f2,g1); }
+      u = g1; /* deg(u) > 0 */
+      if (!F2x_degree(f2)) g1 = pol1_F2x(0); /* only its degree (= 0) matters */
+      else
+      {
+        long dg1;
+        g1 = F2x_gcd(f2,g1);
+        dg1 = F2x_degree(g1);
+        if (dg1)
+        {
+          f2= F2x_div(f2,g1);
+          if (F2x_degree(u) == dg1) continue;
+          u = F2x_div( u,g1);
+        }
+      }
+      /* u is square-free (product of irred. of multiplicity e * k) */
+      gel(t,nbfact) = u;
+      d = F2x_split_Berlekamp(&gel(t,nbfact));
+      if (flag == 2 && d != 1) return NULL;
+      if (flag == 1)
+        for (j=0; j<(ulong)d; j++) t[nbfact+j] = F2x_degree(gel(t,nbfact+j));
+      for (j=0; j<(ulong)d; j++) E[nbfact+j] = e*k;
+      nbfact += d;
+    }
+    j = F2x_degree(f2); if (!j) break;
+    e *= 2; f = F2x_deflate(f2, 2);
+  }
+  if (flag == 2) return gen_1; /* irreducible */
+  setlg(t, nbfact);
+  setlg(E, nbfact); y = mkvec2(t,E);
+  return flag ? sort_factor(y, (void*)&cmpGuGu, cmp_nodata)
+              : sort_factor_pol(y, cmpGuGu);
+}
+
+static GEN
+Flx_Berlekamp_i(GEN f, ulong p, long flag)
+{
+  long e, nbfact, val, d = degpol(f);
+  ulong k, j;
+  GEN y, E, f2, g1, t;
+
+  if (p == 2)
+  {
+    GEN F = F2x_Berlekamp_i(Flx_to_F2x(f),flag);
+    if (flag==0) F2xV_to_FlxV_inplace(gel(F,1));
+    return F;
+  }
+  if (d <= 2) return Flx_factor_deg2(f,p,d,flag);
+
+  /* to hold factors and exponents */
+  t = cgetg(d+1, flag? t_VECSMALL: t_VEC);
+  E = cgetg(d+1,t_VECSMALL);
+  val = Flx_valrem(f, &f);
+  e = nbfact = 1;
+  if (val) {
+    if (flag == 2 && val > 1) return NULL;
+    if (flag == 1)
+      t[1] = 1;
+    else
+      gel(t,1) = polx_Flx(f[1]);
+    E[1] = val; nbfact++;
+  }
+
+  for(;;)
+  {
+    f2 = Flx_gcd(f,Flx_deriv(f,p), p);
+    if (flag == 2 && degpol(f2)) return NULL;
+    g1 = degpol(f2)? Flx_div(f,f2,p): f; /* squarefree */
+    k = 0;
+    while (degpol(g1)>0)
+    {
+      GEN u;
+      k++; if (k%p == 0) { k++; f2 = Flx_div(f2,g1,p); }
+      u = g1; /* deg(u) > 0 */
+      if (!degpol(f2)) g1 = pol1_Flx(0); /* only its degree (= 0) matters */
+      else
+      {
+        g1 = Flx_gcd(f2,g1, p);
+        if (degpol(g1))
+        {
+          f2= Flx_div(f2,g1,p);
+          if (lg(u) == lg(g1)) continue;
+          u = Flx_div( u,g1,p);
+        }
+      }
+      /* u is square-free (product of irred. of multiplicity e * k) */
+      u = Flx_normalize(u,p);
+      gel(t,nbfact) = u;
+      d = Flx_split_Berlekamp(&gel(t,nbfact), p);
+      if (flag == 2 && d != 1) return NULL;
+      if (flag == 1)
+        for (j=0; j<(ulong)d; j++) t[nbfact+j] = degpol(gel(t,nbfact+j));
+      for (j=0; j<(ulong)d; j++) E[nbfact+j] = e*k;
+      nbfact += d;
+    }
+    if (!p) break;
+    j = degpol(f2); if (!j) break;
+    if (j % p) pari_err_PRIME("factmod",utoi(p));
+    e *= p; f = Flx_deflate(f2, p);
+  }
+  if (flag == 2) return gen_1; /* irreducible */
+  setlg(t, nbfact);
+  setlg(E, nbfact); y = mkvec2(t,E);
+  return flag ? sort_factor(y, (void*)&cmpGuGu, cmp_nodata)
+              : sort_factor_pol(y, cmpGuGu);
+}
+
+/* f an FpX or an Flx */
+static GEN
+FpX_Berlekamp_i(GEN f, GEN pp, long flag)
+{
+  long e, nbfact, val, d = degpol(f);
+  ulong k, j;
+  GEN y, E, f2, g1, t;
+
+  if (typ(f) == t_VECSMALL)
+  {/* lgefint(pp) == 3 */
+    ulong p = pp[2];
+    GEN F;
+    if (p == 2) {
+      F = F2x_Berlekamp_i(Flx_to_F2x(f), flag);
+      if (flag==0) F2xV_to_ZXV_inplace(gel(F,1));
+    } else {
+      F = Flx_Berlekamp_i(f, p, flag);
+      if (flag==0) FlxV_to_ZXV_inplace(gel(F,1));
+    }
+    return F;
+  }
+  /* p is large (and odd) */
+  if (d <= 2) return FpX_factor_deg2(f,pp,d,flag);
+
+  /* to hold factors and exponents */
+  t = cgetg(d+1, flag? t_VECSMALL: t_VEC);
+  E = cgetg(d+1,t_VECSMALL);
+  val = ZX_valrem(f, &f);
+  e = nbfact = 1;
+  if (val) {
+    if (flag == 2 && val > 1) return NULL;
+    if (flag == 1)
+      t[1] = 1;
+    else
+      gel(t,1) = pol_x(varn(f));
+    E[1] = val; nbfact++;
+  }
+
+  f2 = FpX_gcd(f,ZX_deriv(f), pp);
+  if (flag == 2 && degpol(f2)) return NULL;
+  g1 = degpol(f2)? FpX_div(f,f2,pp): f; /* squarefree */
+  k = 0;
+  while (degpol(g1)>0)
+  {
+    GEN u;
+    k++;
+    u = g1; /* deg(u) > 0 */
+    if (!degpol(f2)) g1 = pol_1(0); /* only its degree (= 0) matters */
+    else
+    {
+      g1 = FpX_gcd(f2,g1, pp);
+      if (degpol(g1))
+      {
+        f2= FpX_div(f2,g1,pp);
+        if (lg(u) == lg(g1)) continue;
+        u = FpX_div( u,g1,pp);
+      }
+    }
+    /* u is square-free (product of irred. of multiplicity e * k) */
+    u = FpX_normalize(u,pp);
+    gel(t,nbfact) = u;
+    d = FpX_split_Berlekamp(&gel(t,nbfact), pp);
+    if (flag == 2 && d != 1) return NULL;
+    if (flag == 1)
+      for (j=0; j<(ulong)d; j++) t[nbfact+j] = degpol(gel(t,nbfact+j));
+    for (j=0; j<(ulong)d; j++) E[nbfact+j] = e*k;
+    nbfact += d;
+  }
+  if (flag == 2) return gen_1; /* irreducible */
+  setlg(t, nbfact);
+  setlg(E, nbfact); y = mkvec2(t,E);
+  return flag ? sort_factor(y, (void*)&cmpGuGu, cmp_nodata)
+              : sort_factor_pol(y, cmpii);
+}
+GEN
+FpX_factor(GEN f, GEN p)
+{
+  pari_sp av = avma;
+  ZX_factmod_init(&f, p);
+  return gerepilecopy(av, FpX_Berlekamp_i(f, p, 0));
+}
+GEN
+Flx_factor(GEN f, ulong p)
+{
+  pari_sp av = avma;
+  GEN F = (degpol(f)>log2(p))? Flx_factcantor_i(f,p,0): Flx_Berlekamp_i(f,p,0);
+  return gerepilecopy(av, F);
+}
+GEN
+F2x_factor(GEN f)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, F2x_Berlekamp_i(f, 0));
+}
+
+GEN
+factormod0(GEN f, GEN p, long flag)
+{
+  switch(flag)
+  {
+    case 0: return factmod(f,p);
+    case 1: return simplefactmod(f,p);
+    default: pari_err_FLAG("factormod");
+  }
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     FACTORIZATION IN F_q                        */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+to_Fq(GEN x, GEN T, GEN p)
+{
+  long i, lx, tx = typ(x);
+  GEN y;
+
+  if (tx == t_INT)
+    y = mkintmod(x,p);
+  else
+  {
+    if (tx != t_POL) pari_err_TYPE("to_Fq",x);
+    lx = lg(x);
+    y = cgetg(lx,t_POL); y[1] = x[1];
+    for (i=2; i<lx; i++) gel(y,i) = mkintmod(gel(x,i), p);
+  }
+  return mkpolmod(y, T);
+}
+
+static GEN
+to_Fq_pol(GEN x, GEN T, GEN p)
+{
+  long i, lx = lg(x);
+  for (i=2; i<lx; i++) gel(x,i) = to_Fq(gel(x,i),T,p);
+  return x;
+}
+
+static GEN
+to_Fq_fact(GEN P, GEN E, GEN T, GEN p, pari_sp av)
+{
+  GEN y, u, v;
+  long j, l = lg(P), nbf = lg(P);
+
+  u = cgetg(nbf,t_COL);
+  v = cgetg(nbf,t_COL);
+  for (j=1; j<l; j++)
+  {
+    gel(u,j) = simplify_shallow(gel(P,j)); /* may contain pols of degree 0 */
+    gel(v,j) = utoi((ulong)E[j]);
+  }
+  y = gerepilecopy(av, mkmat2(u, v));
+  u = gel(y,1);
+  p = icopy(p);
+  T = FpX_to_mod(T, p);
+  for (j=1; j<nbf; j++) gel(u,j) = to_Fq_pol(gel(u,j), T,p);
+  return y;
+}
+static GEN
+to_FqC(GEN P, GEN T, GEN p, pari_sp av)
+{
+  GEN u;
+  long j, l = lg(P), nbf = lg(P);
+
+  u = cgetg(nbf,t_COL);
+  for (j=1; j<l; j++)
+    gel(u,j) = simplify_shallow(gel(P,j)); /* may contain pols of degree 0 */
+  u = gerepilecopy(av, u);
+  p = icopy(p);
+  T = FpX_to_mod(T, p);
+  for (j=1; j<nbf; j++) gel(u,j) = to_Fq(gel(u,j), T,p);
+  return u;
+}
+
+GEN
+FlxqXQ_halfFrobenius(GEN a, GEN S, GEN T, ulong p)
+{
+  long vT = get_Flx_var(T);
+  GEN xp = Flxq_powu(polx_Flx(vT), p, T, p);
+  GEN Xp = FlxqXQ_pow(polx_FlxX(varn(S), vT), utoi(p), S, T, p);
+  GEN ap2 = FlxqXQ_pow(a,utoi(p>>1), S, T, p);
+  GEN V = FlxqXQV_autsum(mkvec3(xp, Xp, ap2), get_Flx_degree(T), S, T, p);
+  return gel(V,3);
+}
+
+GEN
+FpXQXQ_halfFrobenius(GEN a, GEN S, GEN T, GEN p)
+{
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    long v = get_FpX_var(T);
+    GEN Tp = ZXT_to_FlxT(T,pp), Sp = ZXX_to_FlxX(S, pp, v);
+    return FlxX_to_ZXX(FlxqXQ_halfFrobenius(ZXX_to_FlxX(a,pp,v),Sp,Tp,pp));
+  }
+  else
+  {
+    GEN xp = FpXQ_pow(pol_x(get_FpX_var(T)), p, T, p);
+    GEN Xp = FpXQXQ_pow(pol_x(varn(S)), p, S, T, p);
+    GEN ap2 = FpXQXQ_pow(a,shifti(p,-1), S, T, p);
+    GEN V = FpXQXQV_autsum(mkvec3(xp,Xp,ap2), get_FpX_degree(T), S, T, p);
+    return gel(V,3);
+  }
+}
+
+GEN
+FlxqX_Frobenius(GEN S, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long n = get_Flx_degree(T), vT = get_Flx_var(T);
+  GEN X  = polx_FlxX(varn(S), vT);
+  GEN xp = Flxq_powu(polx_Flx(vT), p, T, p);
+  GEN Xp = FlxqXQ_pow(X, utoi(p), S, T, p);
+  GEN Xq = gel(FlxqXQV_autpow(mkvec2(xp,Xp), n, S, T, p), 2);
+  return gerepilecopy(av, Xq);
+}
+
+GEN
+FpXQX_Frobenius(GEN S, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  long n = get_FpX_degree(T);
+  GEN X  = pol_x(varn(S));
+  GEN xp = FpXQ_pow(pol_x(get_FpX_var(T)), p, T, p);
+  GEN Xp = FpXQXQ_pow(X, p, S, T, p);
+  GEN Xq = gel(FpXQXQV_autpow(mkvec2(xp,Xp), n, S, T, p), 2);
+  return gerepilecopy(av, Xq);
+}
+
+static GEN
+FqX_Frobenius_powers(GEN S, GEN T, GEN p)
+{
+  long N = degpol(S);
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    GEN Tp = ZXT_to_FlxT(T, pp), Sp = ZXX_to_FlxX(S, pp, get_FpX_var(T));
+    GEN Xq = FlxqX_Frobenius(Sp, Tp, pp);
+    return FlxqXQ_powers(Xq, N-1, Sp, Tp, pp);
+  } else
+  {
+    GEN Xq = FpXQX_Frobenius(S, T, p);
+    return FpXQXQ_powers(Xq, N-1, S, T, p);
+  }
+}
+
+static GEN
+FqX_Frobenius_eval(GEN x, GEN V, GEN S, GEN T, GEN p)
+{
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    long v = get_FpX_var(T);
+    GEN Tp = ZXT_to_FlxT(T, pp), Sp = ZXX_to_FlxX(S, pp, v);
+    GEN xp = ZXX_to_FlxX(x, pp, v);
+    return FlxX_to_ZXX(FlxqX_FlxqXQV_eval(xp, V, Sp, Tp, pp));
+  }
+  else
+    return FpXQX_FpXQXQV_eval(x, V, S, T, p);
+}
+
+static GEN
+FpXQX_split_part(GEN f, GEN T, GEN p)
+{
+  long n = degpol(f);
+  GEN z, X = pol_x(varn(f));
+  if (n <= 1) return f;
+  f = FpXQX_red(f, T, p);
+  z = FpXQX_Frobenius(f, T, p);
+  z = FpXX_sub(z, X , p);
+  return FpXQX_gcd(z, f, T, p);
+}
+
+static GEN
+FlxqX_split_part(GEN f, GEN T, ulong p)
+{
+  long n = degpol(f);
+  GEN z, Xq, X = polx_FlxX(varn(f),get_Flx_var(T));
+  if (n <= 1) return f;
+  f = FlxqX_red(f, T, p);
+  Xq = FlxqX_Frobenius(f, T, p);
+  z = FlxX_sub(Xq, X , p);
+  return FlxqX_gcd(z, f, T, p);
+}
+
+long
+FpXQX_nbroots(GEN f, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN z;
+  if(lgefint(p)==3)
+  {
+    ulong pp=p[2];
+    z = FlxqX_split_part(ZXX_to_FlxX(f,pp,varn(T)),ZXT_to_FlxT(T,pp),pp);
+  }
+  else
+    z = FpXQX_split_part(f, T, p);
+  avma = av; return degpol(z);
+}
+
+long
+FqX_nbroots(GEN f, GEN T, GEN p)
+{ return T ? FpXQX_nbroots(f, T, p): FpX_nbroots(f, p); }
+
+long
+FlxqX_nbroots(GEN f, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  GEN z = FlxqX_split_part(f, T, p);
+  avma = av; return degpol(z);
+}
+
+GEN
+FlxqX_Berlekamp_ker(GEN u, GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  long j,N = degpol(u);
+  GEN Xq = FlxqX_Frobenius(u,T,p);
+  GEN Q  = FlxqXQ_matrix_pow(Xq,N,N,u,T,p);
+  for (j=1; j<=N; j++)
+    gcoeff(Q,j,j) = Flx_Fl_add(gcoeff(Q,j,j), p-1, p);
+  return gerepileupto(ltop, FlxqM_ker(Q,T,p));
+}
+
+GEN
+FpXQX_Berlekamp_ker(GEN u, GEN T, GEN p)
+{
+  if (lgefint(p)==3)
+  {
+    ulong pp=p[2];
+    long v = get_FpX_var(T);
+    GEN Tp = ZXT_to_FlxT(T,pp), Sp = ZXX_to_FlxX(u,pp,v);
+    return FlxM_to_ZXM(FlxqX_Berlekamp_ker(Sp, Tp, pp));
+  } else
+  {
+    pari_sp ltop=avma;
+    long j,N = degpol(u);
+    GEN Xq = FpXQX_Frobenius(u,T,p);
+    GEN Q  = FpXQXQ_matrix_pow(Xq,N,N,u,T,p);
+    for (j=1; j<=N; j++)
+      gcoeff(Q,j,j) = Fq_sub(gcoeff(Q,j,j), gen_1, T, p);
+    return gerepileupto(ltop, FqM_ker(Q,T,p));
+  }
+}
+
+long
+FpXQX_nbfact(GEN u, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN vker = FpXQX_Berlekamp_ker(u, T, p);
+  avma = av; return lg(vker)-1;
+}
+
+long
+FqX_nbfact(GEN u, GEN T, GEN p)
+{
+  return T ? FpX_nbfact(u, p): FpXQX_nbfact(u, T, p);
+}
+
+long
+FqX_split_Berlekamp(GEN *t, GEN T, GEN p)
+{
+  GEN u = *t, a,b,vker,pol;
+  long vu = varn(u), vT = varn(T), dT = degpol(T);
+  long d, i, ir, L, la, lb;
+  T = FpX_get_red(T, p);
+  vker = FpXQX_Berlekamp_ker(u,T,p);
+  vker = RgM_to_RgXV(vker,vu);
+  d = lg(vker)-1;
+  ir = 0;
+  /* t[i] irreducible for i < ir, still to be treated for i < L */
+  for (L=1; L<d; )
+  {
+    pol= scalarpol(random_FpX(dT,vT,p),vu);
+    for (i=2; i<=d; i++)
+      pol = FqX_add(pol, FqX_Fq_mul(gel(vker,i),
+                                    random_FpX(dT,vT,p), T, p), T, p);
+    pol = FpXQX_red(pol,T,p);
+    for (i=ir; i<L && L<d; i++)
+    {
+      a = t[i]; la = degpol(a);
+      if (la == 1) { set_irred(i); }
+      else
+      {
+        pari_sp av = avma;
+        b = FqX_rem(pol, a, T,p);
+        if (degpol(b)<=0) { avma=av; continue; }
+        b = FpXQXQ_halfFrobenius(b, a,T,p);
+        if (degpol(b)<=0) { avma=av; continue; }
+        gel(b,2) = Fq_sub(gel(b,2), gen_1,T,p);
+        b = FqX_gcd(a,b, T,p); lb = degpol(b);
+        if (lb && lb < la)
+        {
+          b = FqX_normalize(b, T,p);
+          t[L] = FqX_div(a,b,T,p);
+          t[i]= b; L++;
+        }
+        else avma = av;
+      }
+    }
+  }
+  return d;
+}
+
+/* split into r factors of degree d */
+static void
+FqX_split(GEN *t, long d, GEN q, GEN S, GEN T, GEN p)
+{
+  GEN u = *t;
+  long l, v, is2, cnt, dt = degpol(u), dT = degpol(T);
+  pari_sp av;
+  pari_timer ti;
+  GEN w,w0;
+
+  if (dt == d) return;
+  v = varn(*t);
+  if (DEBUGLEVEL > 6) timer_start(&ti);
+  av = avma; is2 = equaliu(p, 2);
+  for(cnt = 1;;cnt++, avma = av)
+  { /* splits *t with probability ~ 1 - 2^(1-r) */
+    w = w0 = FqX_rand(dt,v, T,p);
+    if (degpol(w) <= 0) continue;
+    for (l=1; l<d; l++) /* sum_{0<i<d} w^(q^i), result in (F_q)^r */
+      w = RgX_add(w0, FqX_Frobenius_eval(w, S, u, T, p));
+    w = FpXQX_red(w, T,p);
+    if (is2)
+    {
+      w0 = w;
+      for (l=1; l<dT; l++) /* sum_{0<i<k} w^(2^i), result in (F_2)^r */
+      {
+        w = FqX_rem(FqX_sqr(w,T,p), *t, T,p);
+        w = FpXX_red(RgX_add(w0,w), p);
+      }
+    }
+    else
+    {
+      w = FpXQXQ_halfFrobenius(w, *t, T, p);
+      /* w in {-1,0,1}^r */
+      if (degpol(w) <= 0) continue;
+      gel(w,2) = gadd(gel(w,2), gen_1);
+    }
+    w = FqX_gcd(*t,w, T,p); l = degpol(w);
+    if (l && l != dt) break;
+  }
+  w = gerepileupto(av,FqX_normalize(w,T,p));
+  if (DEBUGLEVEL > 6)
+    err_printf("[FqX_split] splitting time: %ld (%ld trials)\n",
+               timer_delay(&ti),cnt);
+  l /= d; t[l] = FqX_div(*t,w, T,p); *t = w;
+  FqX_split(t+l,d,q,S,T,p);
+  FqX_split(t  ,d,q,S,T,p);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                  FACTOR USING TRAGER'S TRICK                    */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+FqX_frob_deflate(GEN f, GEN T, GEN p)
+{
+  GEN F = RgX_deflate(f, itos(p)), frobinv = powiu(p, degpol(T)-1);
+  long i, l = lg(F);
+  for (i=2; i<l; i++) gel(F,i) = Fq_pow(gel(F,i), frobinv, T,p);
+  return F;
+}
+/* Factor _sqfree_ polynomial a on the finite field Fp[X]/(T). Assumes
+ * varncmp (varn(T), varn(A)) > 0 */
+static GEN
+FqX_split_Trager(GEN A, GEN T, GEN p)
+{
+  GEN c, P, u, fa, n;
+  long lx, i, k;
+
+  u = A;
+  n = NULL;
+  for (k = 0; cmpui(k, p) < 0; k++)
+  {
+    GEN U;
+    c = deg1pol_shallow(stoi(k) , gen_0, varn(T));
+    U = FqX_translate(u, c, T, p);
+    n = FpX_FpXY_resultant(T, U, p);
+    if (FpX_is_squarefree(n, p)) break;
+    n = NULL;
+  }
+  if (!n) return NULL;
+  if (DEBUGLEVEL>4) err_printf("FqX_split_Trager: choosing k = %ld\n",k);
+  /* n guaranteed to be squarefree */
+  fa = FpX_factor(n, p); fa = gel(fa,1); lx = lg(fa);
+  if (lx == 2) return mkcol(A); /* P^k, P irreducible */
+
+  P = cgetg(lx,t_COL);
+  c = FpX_neg(c,p);
+  for (i=lx-1; i>1; i--)
+  {
+    GEN F = FqX_translate(gel(fa,i), c, T, p);
+    F = FqX_normalize(FqX_gcd(u, F, T, p), T, p);
+    if (typ(F) != t_POL || degpol(F) == 0)
+      pari_err_IRREDPOL("FqX_split_Trager [modulus]",T);
+    u = FqX_div(u, F, T, p);
+    gel(P,i) = F;
+  }
+  gel(P,1) = u; return P;
+}
+
+static long
+isabsolutepol(GEN f)
+{
+  long i, l = lg(f);
+  for(i=2; i<l; i++)
+  {
+    GEN c = gel(f,i);
+    if (typ(c) == t_POL && degpol(c) > 0) return 0;
+  }
+  return 1;
+}
+
+static void
+add(GEN z, GEN g, long d) { vectrunc_append(z, mkvec2(utoipos(d), g)); }
+/* return number of roots of u; assume deg u >= 0 */
+long
+FqX_split_deg1(GEN *pz, GEN u, GEN T, GEN p)
+{
+  long dg, N = degpol(u);
+  GEN v, S, g, X, z = vectrunc_init(N+1);
+
+  *pz = z;
+  if (N == 0) return 0;
+  if (N == 1) return 1;
+  v = X = pol_x(varn(u));
+  S = FqX_Frobenius_powers(u, T, p);
+  vectrunc_append(z, S);
+  v = FqX_Frobenius_eval(v, S, u, T, p);
+  g = FqX_gcd(FpXX_sub(v,X,p),u, T,p);
+  dg = degpol(g);
+  if (dg > 0) add(z, FqX_normalize(g,T,p), dg);
+  return dg;
+}
+
+/* return number of factors; z not properly initialized if deg(u) <= 1 */
+long
+FqX_split_by_degree(GEN *pz, GEN u, GEN T, GEN p)
+{
+  long nb = 0, d, dg, N = degpol(u);
+  GEN v, S, g, X, z = vectrunc_init(N+1);
+
+  *pz = z;
+  if (N <= 1) return 1;
+  v = X = pol_x(varn(u));
+  S = FqX_Frobenius_powers(u, T, p);
+  vectrunc_append(z, S);
+  for (d=1; d <= N>>1; d++)
+  {
+    v = FqX_Frobenius_eval(v, S, u, T, p);
+    g = FqX_gcd(FpXX_sub(v,X,p),u, T,p);
+    dg = degpol(g); if (dg <= 0) continue;
+    /* all factors of g have degree d */
+    add(z, FqX_normalize(g, T,p), dg / d); nb += dg / d;
+    N -= dg;
+    if (N)
+    {
+      u = FqX_div(u,g, T,p);
+      v = FqX_rem(v,u, T,p);
+    }
+  }
+  if (N) { add(z, FqX_normalize(u, T,p), 1); nb++; }
+  return nb;
+}
+
+/* see roots_from_deg1() */
+static GEN
+FqXC_roots_from_deg1(GEN x, GEN T, GEN p)
+{
+  long i,l = lg(x);
+  GEN r = cgetg(l,t_COL);
+  for (i=1; i<l; i++) { GEN P = gel(x,i); gel(r,i) = Fq_neg(gel(P,2), T, p); }
+  return r;
+}
+
+static GEN
+FqX_split_equal(GEN L, GEN S, GEN T, GEN p)
+{
+  long n = itos(gel(L,1));
+  GEN u = gel(L,2), z = cgetg(n + 1, t_COL);
+  gel(z,1) = u;
+  FqX_split((GEN*)(z+1), degpol(u) / n, powiu(p, degpol(T)), S, T, p);
+  return z;
+}
+GEN
+FqX_split_roots(GEN z, GEN T, GEN p, GEN pol)
+{
+  GEN S = gel(z,1), L = gel(z,2), rep = FqX_split_equal(L, S, T, p);
+  if (pol) rep = shallowconcat(rep, FqX_div(pol, gel(L,2), T,p));
+  return rep;
+}
+GEN
+FqX_split_all(GEN z, GEN T, GEN p)
+{
+  GEN S = gel(z,1), rep = cgetg(1, t_VEC);
+  long i, l = lg(z);
+  for (i = 2; i < l; i++)
+    rep = shallowconcat(rep, FqX_split_equal(gel(z,i), S, T, p));
+  return rep;
+}
+
+/* not memory-clean, as FpX_factorff_i, returning only linear factors */
+static GEN
+FpX_rootsff_i(GEN P, GEN T, GEN p)
+{
+  GEN V, F = gel(FpX_factor(P,p), 1);
+  long i, lfact = 1, nmax = lgpol(P), n = lg(F), dT = degpol(T);
+
+  V = cgetg(nmax,t_COL);
+  for(i=1;i<n;i++)
+  {
+    GEN R, Fi = gel(F,i);
+    long di = degpol(Fi), j, r;
+    if (dT % di) continue;
+    R = FpX_factorff_irred(gel(F,i),T,p);
+    r = lg(R);
+    for (j=1; j<r; j++,lfact++)
+      gel(V,lfact) = Fq_neg(gmael(R,j, 2), T, p);
+  }
+  setlg(V,lfact);
+  gen_sort_inplace(V, (void*) &cmp_RgX, &cmp_nodata, NULL);
+  return V;
+}
+GEN
+FpX_rootsff(GEN P, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, FpX_rootsff_i(P, T, p));
+}
+
+static GEN
+F2xqX_quad_roots(GEN P, GEN T)
+{
+  GEN b= gel(P,3), c = gel(P,2);
+  if (degpol(b)>=0)
+  {
+    GEN z, d = F2xq_div(c, F2xq_sqr(b,T),T);
+    if (F2xq_trace(d,T))
+      return cgetg(1, t_COL);
+    z = F2xq_mul(b, F2xq_Artin_Schreier(d, T), T);
+    return mkcol2(z, F2x_add(b, z));
+  }
+  else
+    return mkcol(F2xq_sqrt(c, T));
+}
+
+static GEN
+FqX_quad_roots(GEN x, GEN T, GEN p)
+{
+  GEN s, u, D, nb, b = gel(x,3), c = gel(x,2);
+  if (equaliu(p, 2))
+  {
+    GEN f2 = ZXX_to_F2xX(x, get_FpX_var(T));
+    s = F2xqX_quad_roots(f2, ZX_to_F2x(get_FpX_mod(T)));
+    return F2xC_to_ZXC(s);
+  }
+  D = Fq_sub(Fq_sqr(b,T,p), Fq_Fp_mul(c,utoi(4),T,p), T,p);
+  u = addis(shifti(p,-1), 1); /* = 1/2 */
+  nb = Fq_neg(b,T,p);
+  if (signe(D)==0)
+    return mkcol(Fq_Fp_mul(nb,u,T, p));
+  s = Fq_sqrt(D,T,p);
+  if (!s) return cgetg(1, t_COL);
+  s = Fq_Fp_mul(Fq_add(s,nb,T,p),u,T, p);
+  return mkcol2(s,Fq_sub(nb,s,T,p));
+}
+
+static GEN
+FqX_roots_i(GEN f, GEN T, GEN p)
+{
+  GEN R;
+  f = FqX_normalize(f, T, p);
+  if (!signe(f)) pari_err_ROOTS0("FqX_roots");
+  if (isabsolutepol(f))
+  {
+    f = simplify_shallow(f);
+    if (typ(f) == t_INT) return cgetg(1, t_COL);
+    return FpX_rootsff_i(f, T, p);
+  }
+  if (degpol(f)==2)
+    return gen_sort(FqX_quad_roots(f,T,p), (void*) &cmp_RgX, &cmp_nodata);
+  switch( FqX_split_deg1(&R, f, T, p) )
+  {
+  case 0: return cgetg(1, t_COL);
+  case 1: if (lg(R) == 1) { R = mkvec(f); break; }
+            /* fall through */
+  default: R = FqX_split_roots(R, T, p, NULL);
+  }
+  R = FqXC_roots_from_deg1(R, T, p);
+  gen_sort_inplace(R, (void*) &cmp_RgX, &cmp_nodata, NULL);
+  return R;
+}
+
+GEN
+FqX_roots(GEN x, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  if (!T) return FpX_roots(x, p);
+  return gerepileupto(av, FqX_roots_i(x, T, p));
+}
+
+static long
+FqX_sqf_split(GEN *t0, GEN q, GEN T, GEN p)
+{
+  GEN *t = t0, u = *t, v, S, g, X;
+  long d, dg, N = degpol(u);
+
+  if (N == 1) return 1;
+  v = X = pol_x(varn(u));
+  S = FqX_Frobenius_powers(u, T, p);
+  for (d=1; d <= N>>1; d++)
+  {
+    v = FqX_Frobenius_eval(v, S, u, T, p);
+    g = FqX_normalize(FqX_gcd(FpXX_sub(v,X,p),u, T,p),T,p);
+    dg = degpol(g); if (dg <= 0) continue;
+
+    /* all factors of g have degree d */
+    *t = g;
+    FqX_split(t, d, q, S, T, p);
+    t += dg / d;
+    N -= dg;
+    if (N)
+    {
+      u = FqX_div(u,g, T,p);
+      v = FqX_rem(v,u, T,p);
+    }
+  }
+  if (N) *t++ = u;
+  return t - t0;
+}
+
+/* not memory-clean */
+static GEN
+FpX_factorff_i(GEN P, GEN T, GEN p)
+{
+  GEN V, E, F = FpX_factor(P,p);
+  long i, lfact = 1, nmax = lgpol(P), n = lgcols(F);
+
+  V = cgetg(nmax,t_VEC);
+  E = cgetg(nmax,t_VECSMALL);
+  for(i=1;i<n;i++)
+  {
+    GEN R = FpX_factorff_irred(gmael(F,1,i),T,p), e = gmael(F,2,i);
+    long j, r = lg(R);
+    for (j=1; j<r; j++,lfact++)
+    {
+      gel(V,lfact) = gel(R,j);
+      gel(E,lfact) = e;
+    }
+  }
+  setlg(V,lfact);
+  setlg(E,lfact); return sort_factor_pol(mkvec2(V,E), cmp_RgX);
+}
+GEN
+FpX_factorff(GEN P, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, FpX_factorff_i(P, T, p));
+}
+
+/* assumes varncmp (varn(T), varn(f)) > 0 */
+static GEN
+FqX_factor_i(GEN f, GEN T, GEN p)
+{
+  long pg, j, k, e, N, lfact, pk, d = degpol(f);
+  GEN E, f2, f3, df1, df2, g1, u, q, t;
+
+  switch(d)
+  {
+    case -1: retmkmat2(mkcolcopy(f), mkvecsmall(1));
+    case 0: return trivial_fact();
+  }
+  T = FpX_normalize(T, p);
+  f = FqX_normalize(f, T, p);
+  if (isabsolutepol(f)) return FpX_factorff_i(simplify_shallow(f), T, p);
+
+  pg = itos_or_0(p);
+  df2  = NULL; /* gcc -Wall */
+  t = cgetg(d+1,t_VEC);
+  E = cgetg(d+1, t_VECSMALL);
+
+  q = powiu(p, degpol(T));
+  e = lfact = 1;
+  pk = 1;
+  f3 = NULL;
+  df1 = FqX_deriv(f, T, p);
+  for(;;)
+  {
+    long nb0;
+    while (!signe(df1))
+    { /* needs d >= p: pg = 0 can't happen  */
+      pk *= pg; e = pk;
+      f = FqX_frob_deflate(f, T, p);
+      df1 = FqX_deriv(f, T, p); f3 = NULL;
+    }
+    f2 = f3? f3: FqX_gcd(f,df1, T,p);
+    if (!degpol(f2)) u = f;
+    else
+    {
+      g1 = FqX_div(f,f2, T,p);
+      df2 = FqX_deriv(f2, T,p);
+      if (gequal0(df2)) { u = g1; f3 = f2; }
+      else
+      {
+        f3 = FqX_gcd(f2,df2, T,p);
+        u = degpol(f3)? FqX_div(f2, f3, T,p): f2;
+        u = FqX_div(g1, u, T,p);
+      }
+    }
+    /* u is square-free (product of irreducibles of multiplicity e) */
+    N = degpol(u);
+    if (N) {
+      nb0 = lfact;
+      gel(t,lfact) = FqX_normalize(u, T,p);
+      if (N == 1) lfact++;
+      else
+      {
+        if (!equaliu(p,2))
+          lfact += FqX_split_Berlekamp(&gel(t,lfact), T, p);
+        else
+        {
+          GEN P = FqX_split_Trager(gel(t,lfact), T, p);
+          if (P) {
+            for (j = 1; j < lg(P); j++) gel(t,lfact++) = gel(P,j);
+          } else {
+            if (DEBUGLEVEL) pari_warn(warner, "FqX_split_Trager failed!");
+            lfact += FqX_sqf_split(&gel(t,lfact), q, T, p);
+          }
+        }
+      }
+      for (j = nb0; j < lfact; j++) E[j] = e;
+    }
+
+    if (!degpol(f2)) break;
+    f = f2; df1 = df2; e += pk;
+  }
+  setlg(t, lfact);
+  setlg(E, lfact);
+  for (j=1; j<lfact; j++) gel(t,j) = FqX_normalize(gel(t,j), T,p);
+  (void)sort_factor_pol(mkvec2(t, E), cmp_RgX);
+  k = 1;
+  for (j = 2; j < lfact; j++)
+  {
+    if (RgX_equal(gel(t,j), gel(t,k)))
+      E[k] += E[j];
+    else
+    { /* new factor */
+      k++;
+      E[k] = E[j];
+      gel(t,k) = gel(t,j);
+    }
+  }
+  setlg(t, k+1);
+  setlg(E, k+1); return mkvec2(t, E);
+}
+
+static void
+ffcheck(pari_sp *av, GEN *f, GEN *T, GEN p)
+{
+  long v;
+  if (typ(*T)!=t_POL) pari_err_TYPE("factorff",*T);
+  if (typ(*f)!=t_POL) pari_err_TYPE("factorff",*f);
+  if (typ(p)!=t_INT) pari_err_TYPE("factorff",p);
+  v = varn(*T);
+  if (varncmp(v, varn(*f)) <= 0)
+    pari_err_PRIORITY("factorff", *T, "<=", varn(*f));
+  *T = RgX_to_FpX(*T, p); *av = avma;
+  *f = RgX_to_FqX(*f, *T,p);
+}
+GEN
+factorff(GEN f, GEN p, GEN T)
+{
+  pari_sp av;
+  GEN z;
+  if (!p || !T)
+  {
+    long pa, t;
+    if (typ(f) != t_POL) pari_err_TYPE("factorff",f);
+    T = p = NULL;
+    t = RgX_type(f, &p, &T, &pa);
+    if (t != t_FFELT) pari_err_TYPE("factorff",f);
+    return FFX_factor(f,T);
+  }
+  ffcheck(&av, &f, &T, p); z = FqX_factor_i(f, T, p);
+  return to_Fq_fact(gel(z,1),gel(z,2), T,p, av);
+}
+GEN
+polrootsff(GEN f, GEN p, GEN T)
+{
+  pari_sp av;
+  GEN z;
+  if (!p || !T)
+  {
+    long pa, t;
+    if (typ(f) != t_POL) pari_err_TYPE("polrootsff",f);
+    T = p = NULL;
+    t = RgX_type(f, &p, &T, &pa);
+    if (t != t_FFELT) pari_err_TYPE("polrootsff",f);
+    return FFX_roots(f,T);
+  }
+  ffcheck(&av, &f, &T, p); z = FqX_roots_i(f, T, p);
+  return to_FqC(z, T,p, av);
+}
+
+/* factorization of x modulo (T,p). Assume x already reduced */
+GEN
+FqX_factor(GEN x, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  if (!T) return FpX_factor(x, p);
+  return gerepilecopy(av, FqX_factor_i(x, T, p));
+}
+/* See also: Isomorphisms between finite field and relative
+ * factorization in polarit3.c */
diff --git a/src/basemath/Hensel.c b/src/basemath/Hensel.c
new file mode 100644
index 0000000..997a9f2
--- /dev/null
+++ b/src/basemath/Hensel.c
@@ -0,0 +1,800 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/***********************************************************************/
+/**                                                                   **/
+/**       QUADRATIC HENSEL LIFT (adapted from V. Shoup's NTL)         **/
+/**                                                                   **/
+/***********************************************************************/
+/* Setup for divide/conquer quadratic Hensel lift
+ *  a = set of k t_POL in Z[X] = factors over Fp (T=NULL) or Fp[Y]/(T)
+ *  V = set of products of factors built as follows
+ *  1) V[1..k] = initial a
+ *  2) iterate:
+ *      append to V the two smallest factors (minimal degree) in a, remove them
+ *      from a and replace them by their product [net loss for a = 1 factor]
+ *
+ * W = bezout coeffs W[i]V[i] + W[i+1]V[i+1] = 1
+ *
+ * link[i] = -j if V[i] = a[j]
+ *            j if V[i] = V[j] * V[j+1]
+ * Arrays (link, V, W) pre-allocated for 2k - 2 elements */
+static void
+BuildTree(GEN link, GEN V, GEN W, GEN a, GEN T, GEN p)
+{
+  long k = lg(a)-1;
+  long i, j, s, minp, mind;
+
+  for (i=1; i<=k; i++) { gel(V,i) = gel(a,i); link[i] = -i; }
+
+  for (j=1; j <= 2*k-5; j+=2,i++)
+  {
+    minp = j;
+    mind = degpol(gel(V,j));
+    for (s=j+1; s<i; s++)
+      if (degpol(gel(V,s)) < mind) { minp = s; mind = degpol(gel(V,s)); }
+
+    swap(gel(V,j), gel(V,minp));
+    lswap(link[j], link[minp]);
+
+    minp = j+1;
+    mind = degpol(gel(V,j+1));
+    for (s=j+2; s<i; s++)
+      if (degpol(gel(V,s)) < mind) { minp = s; mind = degpol(gel(V,s)); }
+
+    swap(gel(V,j+1), gel(V,minp));
+    lswap(link[j+1], link[minp]);
+
+    gel(V,i) = FqX_mul(gel(V,j), gel(V,j+1), T, p);
+    link[i] = j;
+  }
+
+  for (j=1; j <= 2*k-3; j+=2)
+  {
+    GEN d, u, v;
+    d = FqX_extgcd(gel(V,j), gel(V,j+1), T, p, &u, &v);
+    if (degpol(d) > 0) pari_err_COPRIME("BuildTree", gel(V,j), gel(V,j+1));
+    d = gel(d,2);
+    if (!gequal1(d))
+    {
+      if (typ(d)==t_POL)
+      {
+        d = FpXQ_inv(d, T, p);
+        u = FqX_Fq_mul(u, d, T, p);
+        v = FqX_Fq_mul(v, d, T, p);
+      }
+      else
+      {
+        d = Fp_inv(d, p);
+        u = FqX_Fp_mul(u, d, T,p);
+        v = FqX_Fp_mul(v, d, T,p);
+      }
+    }
+    gel(W,j) = u;
+    gel(W,j+1) = v;
+  }
+}
+
+/* au + bv = 1 (p0), ab = f (p0). Lift mod p1 = p0 pd (<= p0^2).
+ * If noinv is set, don't lift the inverses u and v */
+static void
+ZpX_HenselLift(GEN V, GEN W, long j, GEN f, GEN pd, GEN p0, GEN p1, int noinv)
+{
+  pari_sp av = avma;
+  long space = lg(f) * lgefint(p1);
+  GEN a2, b2, g, z, s, t;
+  GEN a = gel(V,j), b = gel(V,j+1);
+  GEN u = gel(W,j), v = gel(W,j+1);
+
+  (void)new_chunk(space); /* HACK */
+  g = ZX_sub(f, ZX_mul(a,b));
+  g = ZX_Z_divexact(g, p0);
+  g = FpX_red(g, pd);
+  z = FpX_mul(v,g, pd);
+  t = FpX_divrem(z,a, pd, &s);
+  t = ZX_add(ZX_mul(u,g), ZX_mul(t,b));
+  t = FpX_red(t, pd);
+  t = ZX_Z_mul(t,p0);
+  s = ZX_Z_mul(s,p0);
+  avma = av;
+  a2 = ZX_add(a,s);
+  b2 = ZX_add(b,t);
+
+  /* already reduced mod p1 = pd p0 */
+  gel(V,j)   = a2;
+  gel(V,j+1) = b2;
+  if (noinv) return;
+
+  av = avma;
+  (void)new_chunk(space); /* HACK */
+  g = ZX_add(ZX_mul(u,a2), ZX_mul(v,b2));
+  g = Z_ZX_sub(gen_1, g);
+  g = ZX_Z_divexact(g, p0);
+  g = FpX_red(g, pd);
+  z = FpX_mul(v,g, pd);
+  t = FpX_divrem(z,a, pd, &s);
+  t = ZX_add(ZX_mul(u,g), ZX_mul(t,b));
+  t = FpX_red(t, pd);
+  t = ZX_Z_mul(t,p0);
+  s = ZX_Z_mul(s,p0);
+  avma = av;
+  gel(W,j)   = ZX_add(u,t);
+  gel(W,j+1) = ZX_add(v,s);
+}
+
+static void
+ZpXQ_HenselLift(GEN V, GEN W, long j, GEN f, GEN Td, GEN T1, GEN pd, GEN p0, GEN p1, int noinv)
+{
+  pari_sp av = avma;
+  const long n = degpol(T1), vT = varn(T1);
+  long space = lg(f) * lgefint(p1) * lg(T1);
+  GEN a2, b2, g, z, s, t;
+  GEN a = gel(V,j), b = gel(V,j+1);
+  GEN u = gel(W,j), v = gel(W,j+1);
+
+  (void)new_chunk(space); /* HACK */
+  g = RgX_sub(f, Kronecker_to_ZXX(ZXX_mul_Kronecker(a,b,n), n, vT));
+  g = FpXQX_red(g, T1, p1);
+  g = RgX_Rg_divexact(g, p0);
+  z = FpXQX_mul(v,g, Td,pd);
+  t = FpXQX_divrem(z,a, Td,pd, &s);
+  t = ZX_add(ZXX_mul_Kronecker(u,g,n), ZXX_mul_Kronecker(t,b,n));
+  t = Kronecker_to_ZXX(t, n, vT);
+  t = FpXQX_red(t, Td, pd);
+  t = RgX_Rg_mul(t,p0);
+  s = RgX_Rg_mul(s,p0);
+  avma = av;
+
+  a2 = RgX_add(a,s);
+  b2 = RgX_add(b,t);
+  /* already reduced mod p1 = pd p0 */
+  gel(V,j)   = a2;
+  gel(V,j+1) = b2;
+  if (noinv) return;
+
+  av = avma;
+  (void)new_chunk(space); /* HACK */
+  g = ZX_add(ZXX_mul_Kronecker(u,a2,n), ZXX_mul_Kronecker(v,b2,n));
+  g = Kronecker_to_ZXX(g, n, vT);
+  g = Rg_RgX_sub(gen_1, g);
+  g = FpXQX_red(g, T1, p1);
+  g = RgX_Rg_divexact(g, p0);
+  z = FpXQX_mul(v,g, Td,pd);
+  t = FpXQX_divrem(z,a, Td,pd, &s);
+  t = ZX_add(ZXX_mul_Kronecker(u,g,n), ZXX_mul_Kronecker(t,b,n));
+  t = Kronecker_to_ZXX(t, n, vT);
+  t = FpXQX_red(t, Td, pd);
+  t = RgX_Rg_mul(t,p0);
+  s = RgX_Rg_mul(s,p0);
+  avma = av;
+  gel(W,j)   = RgX_add(u,t);
+  gel(W,j+1) = RgX_add(v,s);
+}
+
+/* v list of factors, w list of inverses.  f = v[j] v[j+1]
+ * Lift v[j] and v[j+1] mod p0 pd (possibly mod T), then all their divisors */
+static void
+ZpX_RecTreeLift(GEN link, GEN v, GEN w, GEN pd, GEN p0, GEN p1,
+                GEN f, long j, int noinv)
+{
+  if (j < 0) return;
+  ZpX_HenselLift(v, w, j, f, pd, p0,p1, noinv);
+  ZpX_RecTreeLift(link, v, w, pd, p0,p1, gel(v,j)  , link[j  ], noinv);
+  ZpX_RecTreeLift(link, v, w, pd, p0,p1, gel(v,j+1), link[j+1], noinv);
+}
+static void
+ZpXQ_RecTreeLift(GEN link, GEN v, GEN w, GEN Td, GEN T1, GEN pd, GEN p0, GEN p1,
+                 GEN f, long j, int noinv)
+{
+  if (j < 0) return;
+  ZpXQ_HenselLift(v, w, j, f, Td,T1, pd, p0,p1, noinv);
+  ZpXQ_RecTreeLift(link, v, w, Td,T1, pd, p0,p1, gel(v,j)  , link[j  ], noinv);
+  ZpXQ_RecTreeLift(link, v, w, Td,T1, pd, p0,p1, gel(v,j+1), link[j+1], noinv);
+}
+
+/* Assume n > 0. We want to go to accuracy n, starting from accuracy 1, using
+ * a quadratically convergent algorithm. Goal: 9 -> 1,2,3,5,9 instead of
+ * 1,2,4,8,9 (sequence of accuracies).
+ *
+ * Let a0 = 1, a1 = 2, a2, ... ak = n, the sequence of accuracies. To obtain
+ * it, work backwards:
+ *   a(k) = n, a(i-1) = (a(i) + 1) \ 2,
+ * but we do not want to store a(i) explicitly, even as a t_VECSMALL, since
+ * this would leave an object on the stack. We store a(i) implicitly in a
+ * MASK: let a(0) = 1, if the i-bit of MASK is set, set a(i+1) = 2 a(i) - 1,
+ * and 2a(i) otherwise.
+ *
+ * In fact, we do something a little more complicated to simplify the
+ * function interface and avoid returning k and MASK separately: we return
+ * MASK + 2^(k+1), so the highest bit of the mask indicates the length of the
+ * sequence, and the following ones are as above. */
+ulong
+quadratic_prec_mask(long n)
+{
+  long a = n, i;
+  ulong mask = 0;
+  for(i = 1;; i++, mask <<= 1)
+  {
+    mask |= (a&1); a = (a+1)>>1;
+    if (a==1) return mask | (1UL << i);
+  }
+}
+
+/* Lift to precision p^e0.
+ * a = modular factors of f mod (p,T) [possibly T=NULL]
+ *  OR a TreeLift structure [e, link, v, w]: go on lifting
+ * flag = 0: standard.
+ * flag = 1: return TreeLift structure */
+static GEN
+MultiLift(GEN f, GEN a, GEN T, GEN p, long e0, long flag)
+{
+  long i, eold, e, k = lg(a) - 1;
+  GEN E, v, w, link, penew, Tnew;
+  ulong mask;
+  pari_timer Ti;
+
+  if (k < 2) pari_err_DOMAIN("MultiLift", "#(modular factors)", "<", gen_2,a);
+  if (e0 < 1) pari_err_DOMAIN("MultiLift", "precision", "<", gen_1,stoi(e0));
+  if (e0 == 1) return a;
+
+  if (DEBUGLEVEL > 3) timer_start(&Ti);
+  if (typ(gel(a,1)) == t_INT)
+  { /* a = TreeLift structure */
+    e = itos(gel(a,1));
+    link = gel(a,2);
+    v    = gel(a,3);
+    w    = gel(a,4);
+  }
+  else
+  {
+    e = 1;
+    v = cgetg(2*k-2 + 1, t_VEC);
+    w = cgetg(2*k-2 + 1, t_VEC);
+    link=cgetg(2*k-2 + 1, t_VECSMALL);
+    BuildTree(link, v, w, a, T? FpX_red(T,p): NULL, p);
+    if (DEBUGLEVEL > 3) timer_printf(&Ti, "building tree");
+  }
+  mask = quadratic_prec_mask(e0);
+  eold = 1;
+  penew = NULL;
+  Tnew = NULL;
+  while (mask > 1)
+  {
+    long enew = eold << 1;
+    if (mask & 1) enew--;
+    mask >>= 1;
+    if (enew >= e) { /* mask == 1: last iteration */
+      GEN peold = penew? penew: powiu(p, eold);
+      GEN Td = NULL, pd;
+      long d = enew - eold; /* = eold or eold-1 */
+      /* lift from p^eold to p^enew */
+      pd = (d == eold)? peold: diviiexact(peold, p); /* p^d */
+      penew = mulii(peold,pd);
+      if (T) {
+        if (Tnew)
+          Td = (d == eold)? Tnew: FpX_red(Tnew,pd);
+        else
+          Td = FpX_red(T, peold);
+        Tnew = FpX_red(T, penew);
+        ZpXQ_RecTreeLift(link, v, w, Td, Tnew, pd, peold, penew, f, lgpol(v),
+                         (flag == 0 && mask == 1));
+      }
+      else
+        ZpX_RecTreeLift(link, v, w, pd, peold, penew, f, lgpol(v),
+                        (flag == 0 && mask == 1));
+      if (DEBUGLEVEL > 3) timer_printf(&Ti, "lifting to prec %ld", enew);
+    }
+    eold = enew;
+  }
+
+  if (flag)
+    E = mkvec4(utoipos(e0), link, v, w);
+  else
+  {
+    E = cgetg(k+1, t_VEC);
+    for (i = 1; i <= 2*k-2; i++)
+    {
+      long t = link[i];
+      if (t < 0) gel(E,-t) = gel(v,i);
+    }
+  }
+  return E;
+}
+
+/* Q list of (coprime, monic) factors of pol mod (T,p). Lift mod p^e = pe.
+ * T may be NULL */
+GEN
+ZpX_liftfact(GEN pol, GEN Q, GEN T, GEN p, long e, GEN pe)
+{
+  pari_sp av = avma;
+  if (lg(Q) == 2) return mkvec(pol);
+  pol = FqX_normalize(pol, T, pe);
+  return gerepilecopy(av, MultiLift(pol, Q, T, p, e, 0));
+}
+
+/* U = NULL treated as 1 */
+static void
+BezoutPropagate(GEN link, GEN v, GEN w, GEN pe, GEN U, GEN f, long j)
+{
+  GEN Q, R;
+  if (j < 0) return;
+
+  Q = FpX_mul(gel(v,j), gel(w,j), pe);
+  if (U)
+  {
+    Q = FpXQ_mul(Q, U, f, pe);
+    R = FpX_sub(U, Q, pe);
+  }
+  else
+    R = Fp_FpX_sub(gen_1, Q, pe);
+  gel(w,j+1) = Q; /*  0 mod U v[j],  1 mod (1-U) v[j+1] */
+  gel(w,j) = R; /*  1 mod U v[j],  0 mod (1-U) v[j+1] */
+  BezoutPropagate(link, v, w, pe, R, f, link[j  ]);
+  BezoutPropagate(link, v, w, pe, Q, f, link[j+1]);
+}
+
+/* as above, but return the Bezout coefficients for the lifted modular factors
+ *   U[i] = 1 mod Qlift[i]
+ *          0 mod Qlift[j], j != i */
+GEN
+bezout_lift_fact(GEN pol, GEN Q, GEN p, long e)
+{
+  pari_sp av = avma;
+  GEN E, link, v, w, pe;
+  long i, k = lg(Q)-1;
+  if (k == 1) return mkvec(pol);
+  pe = powiu(p, e);
+  pol = FpX_normalize(pol, pe);
+  E = MultiLift(pol, Q, NULL, p, e, 1);
+  link = gel(E,2);
+  v    = gel(E,3);
+  w    = gel(E,4);
+  BezoutPropagate(link, v, w, pe, NULL, pol, lgpol(v));
+  E = cgetg(k+1, t_VEC);
+  for (i = 1; i <= 2*k-2; i++)
+  {
+    long t = link[i];
+    if (t < 0) E[-t] = w[i];
+  }
+  return gerepilecopy(av, E);
+}
+
+/* Front-end for ZpX_liftfact:
+   lift the factorization of pol mod p given by L to p^N (if possible) */
+GEN
+polhensellift(GEN pol, GEN L, GEN p, long N)
+{
+  GEN T = NULL;
+  long i, l, t;
+  pari_sp av = avma;
+
+  if (typ(pol) != t_POL) pari_err_TYPE("polhensellift",pol);
+  RgX_check_ZXX(pol, "polhensellift");
+  if (!is_vec_t(typ(L)) || lg(L) < 3) pari_err_TYPE("polhensellift",L);
+  t = typ(p);
+  if (t == t_VEC) /* [p, T] */
+  {
+    T = gel(p,2);
+    if (typ(T) != t_POL) pari_err_TYPE("polhensellift",pol);
+    RgX_check_ZX(T, "polhensellift");
+    p = gel(p,1); t = typ(p);
+  }
+  if (t != t_INT) pari_err_TYPE("polhensellift",p);
+  if (N < 1) pari_err_DOMAIN("polhensellift", "precision", "<", gen_1,stoi(N));
+
+  l = lg(L); L = leafcopy(L);
+  for (i = 1; i < l; i++)
+  {
+    if (typ(gel(L,i)) != t_POL)
+      gel(L,i) = scalar_ZX_shallow(gel(L,i), varn(pol));
+    RgX_check_ZXX(gel(L,i), "polhensellift");
+  }
+  return gerepilecopy(av, ZpX_liftfact(pol, L, T, p, N, powiu(p,N)));
+}
+
+/*************************************************************************/
+/*                             rootpadicfast                             */
+/*************************************************************************/
+/* SPEC:
+ * p is a t_INT > 1, e >= 1
+ * f is a ZX with leading term prime to p.
+ * a is a simple root mod l for all l|p.
+ * Return roots of f mod p^e, as integers (implicitly mod p^e)
+ * STANDARD USE: p is a prime power */
+GEN
+ZpX_liftroot(GEN f, GEN a, GEN p, long e)
+{
+  pari_sp av = avma;
+  GEN q = p, fr, W;
+  ulong mask;
+
+  a = modii(a,q);
+  if (e == 1) return a;
+  mask = quadratic_prec_mask(e);
+  fr = FpX_red(f,q);
+  W = Fp_inv(FpX_eval(ZX_deriv(fr), a, q), q); /* 1/f'(a) mod p */
+  for(;;)
+  {
+    q = sqri(q);
+    if (mask & 1) q = diviiexact(q, p);
+    mask >>= 1;
+    fr = FpX_red(f,q);
+    a = Fp_sub(a, Fp_mul(W, FpX_eval(fr, a,q), q), q);
+    if (mask == 1) return gerepileuptoint(av, a);
+    W = Fp_sub(shifti(W,1), Fp_mul(Fp_sqr(W,q), FpX_eval(ZX_deriv(fr),a,q), q), q);
+  }
+}
+
+GEN
+ZpXQX_liftroot_vald(GEN f, GEN a, long v, GEN T, GEN p, long e)
+{
+  pari_sp av = avma, av2, lim;
+  GEN pv = p, q, qv, W, df, Tq, fr, dfr;
+  ulong mask;
+  a = Fq_red(a, T, p);
+  if (e <= v+1) return a;
+  df = RgX_deriv(f);
+  if (v) { pv = powiu(p,v); qv = mulii(pv,p); df = ZXX_Z_divexact(df, pv); }
+  else qv = p;
+  mask = quadratic_prec_mask(e-v);
+  Tq = FpXT_red(T, qv); dfr = FpXQX_red(df, Tq, p);
+  W = Fq_inv(FqX_eval(dfr, a, Tq, p), Tq, p); /* 1/f'(a) mod (T,p) */
+  q = p;
+  av2 = avma; lim = stack_lim(av2, 2);
+  for (;;)
+  {
+    GEN u, fa, qv, q2v, q2, Tq2;
+    q2 = q; q = sqri(q);
+    if (mask & 1) q = diviiexact(q,p);
+    mask >>= 1;
+    if (v) { qv = mulii(q, pv); q2v = mulii(q2, pv); }
+    else { qv = q; q2v = q2; }
+    Tq2 = FpXT_red(T, q2v); Tq = FpXT_red(T, qv);
+    fr = FpXQX_red(f, Tq, qv);
+    fa = FqX_eval(fr, a, Tq, qv);
+    fa = typ(fa)==t_INT? diviiexact(fa,q2v): ZX_Z_divexact(fa, q2v);
+    a = Fq_sub(a, ZX_Z_mul(Fq_mul(W, fa, Tq2, q2v), q2), Tq, qv);
+    if (mask == 1) return gerepileupto(av, a);
+    dfr = FpXQX_red(df, Tq, q);
+    u = ZX_Z_divexact(FpX_Fp_sub(Fq_mul(W,FqX_eval(dfr,a,Tq,q),Tq,q),gen_1,q),q2);
+    W = Fq_sub(W,ZX_Z_mul(Fq_mul(u,W,Tq2,q2),q2),Tq,q);
+    if (low_stack(lim, stack_lim(av2,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"ZpXQX_liftroot, e = %ld", e);
+      gerepileall(av2, 3, &a, &W, &q);
+    }
+  }
+}
+
+GEN
+ZpXQX_liftroot(GEN f, GEN a, GEN T, GEN p, long e) { return ZpXQX_liftroot_vald(f,a,0,T,p,e); }
+
+/* Apply ZpX_liftroot to all roots in S and trace trick.
+ * Elements of S must be distinct simple roots mod p for all p|q. */
+GEN
+ZpX_liftroots(GEN f, GEN S, GEN q, long e)
+{
+  long i, d, l = lg(S), n = l-1;
+  GEN y = cgetg(l, typ(S));
+  if (!n) return y;
+  for (i=1; i<n; i++)
+    gel(y,i) = ZpX_liftroot(f, gel(S,i), q, e);
+  d = degpol(f);
+  if (n != d) /* not totally split*/
+    gel(y,n) = ZpX_liftroot(f, gel(S,n), q, e);
+  else
+  { /* totally split: use trace trick */
+    pari_sp av = avma;
+    GEN z = gel(f, d+1);/* -trace(roots) */
+    for(i=1; i<n;i++) z = addii(z, gel(y,i));
+    z = modii(negi(z), powiu(q,e));
+    gel(y,n) = gerepileuptoint(av,z);
+  }
+  return y;
+}
+
+/* Same as ZpX_liftroot for the polynomial X^2-b */
+GEN
+Zp_sqrtlift(GEN b, GEN a, GEN p, long e)
+{
+  pari_sp ltop=avma;
+  GEN q, w;
+  ulong mask;
+
+  if (e == 1) return icopy(a);
+  mask = quadratic_prec_mask(e);
+  w = Fp_inv(modii(shifti(a,1), p), p);
+  q = p;
+  for(;;)
+  {
+    q = sqri(q);
+    if (mask & 1) q = diviiexact(q, p);
+    mask >>= 1;
+    if (lgefint(q) == 3)
+    {
+      ulong Q = (ulong)q[2];
+      ulong A = umodiu(a, Q);
+      ulong B = umodiu(b, Q);
+      ulong W = umodiu(w, Q);
+      A = Fl_sub(A, Fl_mul(W, Fl_sub(Fl_sqr(A,Q), B, Q), Q), Q);
+      a = utoi(A);
+      if (mask == 1) break;
+      W = Fl_sub(Fl_add(W,W,Q), Fl_mul(Fl_sqr(W,Q), Fl_add(A,A,Q),Q), Q);
+      w = utoi(W);
+    }
+    else
+    {
+      a = modii(subii(a, mulii(w, subii(Fp_sqr(a,q),b))), q);
+      if (mask == 1) break;
+      w = subii(shifti(w,1), Fp_mul(Fp_sqr(w,q), shifti(a,1),q));
+    }
+  }
+  return gerepileuptoint(ltop,a);
+}
+/* Same as ZpX_liftroot for the polynomial X^n-b
+ * TODO: generalize to sparse polynomials. */
+GEN
+Zp_sqrtnlift(GEN b, GEN n, GEN a, GEN p, long e)
+{
+  pari_sp ltop=avma;
+  GEN q, w, n_1;
+  ulong mask;
+
+  if (equalii(n, gen_2)) return Zp_sqrtlift(b,a,p,e);
+  if (e == 1) return icopy(a);
+  n_1 = subis(n,1);
+  mask = quadratic_prec_mask(e);
+  w = Fp_inv(Fp_mul(n,Fp_pow(a,n_1,p), p), p);
+  q = p;
+  for(;;)
+  {
+    q = sqri(q);
+    if (mask & 1) q = diviiexact(q, p);
+    mask >>= 1;
+    if (lgefint(q) == 3 && lgefint(n) == 3)
+    {
+      ulong Q = (ulong)q[2], N = (ulong)n[2];
+      ulong A = umodiu(a, Q);
+      ulong B = umodiu(b, Q);
+      ulong W = umodiu(w, Q);
+
+      A = Fl_sub(A, Fl_mul(W, Fl_sub(Fl_powu(A,N,Q), B, Q), Q), Q);
+      a = utoi(A);
+      if (mask == 1) break;
+      W = Fl_sub(Fl_add(W,W,Q),
+                 Fl_mul(Fl_sqr(W,Q), Fl_mul(N,Fl_powu(A, N-1, Q), Q), Q), Q);
+      w = utoi(W);
+    }
+    else
+    {
+      /* a -= w (a^n - b) */
+      a = modii(subii(a, mulii(w, subii(Fp_pow(a,n,q),b))), q);
+      if (mask == 1) break;
+      /* w += w - w^2 n a^(n-1)*/
+      w = subii(shifti(w,1), Fp_mul(Fp_sqr(w,q), mulii(n,Fp_pow(a,n_1,q)), q));
+    }
+  }
+  return gerepileuptoint(ltop,a);
+}
+
+/* Compute (x-1)/(x+1)/p^k */
+static GEN
+ZpXQ_log_to_ath(GEN x, long k, GEN T, GEN p, long e, GEN pe)
+{
+  pari_sp av = avma;
+  long vT = get_FpX_var(T);
+  GEN bn, bdi;
+  GEN bd = ZX_Z_add(x, gen_1);
+  if (equaliu(p,2)) /*For p=2, we need to simplify by 2*/
+  {
+    bn = ZX_shifti(x,-(k+1));
+    bdi= ZpXQ_invlift(ZX_shifti(bd ,-1), pol_1(vT), T, p, e);
+  }
+  else
+  {
+    bn = ZX_Z_divexact(ZX_Z_sub(x, gen_1),powiu(p,k));
+    bdi= ZpXQ_invlift(bd, scalarpol(Fp_inv(gen_2,p),vT), T, p, e);
+  }
+  return gerepileupto(av, FpXQ_mul(bn, bdi, T, pe));
+}
+
+/* Assume p odd, a = 1 [p], return log(a) */
+GEN
+ZpXQ_log(GEN a, GEN T, GEN p, long N)
+{
+  pari_sp av = avma;
+  pari_timer ti;
+  long is2 = equaliu(p,2);
+  ulong pp = is2 ? 0: itou_or_0(p);
+  double lp = is2 ? 1: pp ? log2(pp): expi(p);
+  long k = maxss(1 , (long) .5+pow((double)(N>>1)/(lp*lp), 1./3));
+  GEN ak, s, b, pol;
+  long e = is2 ? N-1: N;
+  long i, l = (e-2)/(2*(k+is2));
+  GEN pe = powiu(p,e);
+  GEN TNk, pNk = powiu(p,N+k);
+  if( DEBUGLEVEL>=3) timer_start(&ti);
+  TNk = FpX_get_red(get_FpX_mod(T), pNk);
+  ak = FpXQ_pow(a, powiu(p,k), TNk, pNk);
+  if( DEBUGLEVEL>=3) timer_printf(&ti,"FpXQ_pow(%ld)",k);
+  b = ZpXQ_log_to_ath(ak, k, T, p, e, pe);
+  if( DEBUGLEVEL>=3) timer_printf(&ti,"ZpXQ_log_to_ath");
+  pol= cgetg(l+3,t_POL);
+  pol[1] = evalsigne(1)|evalvarn(0);
+  for(i=0; i<=l; i++)
+  {
+    GEN g;
+    ulong z = 2*i+1;
+    if (pp)
+    {
+      long w = u_lvalrem(z, pp, &z);
+      g = powuu(pp,2*i*k-w);
+    }
+    else g = powiu(p,2*i*k);
+    gel(pol,i+2) = Fp_div(g, utoi(z),pe);
+  }
+  if( DEBUGLEVEL>=3) timer_printf(&ti,"pol(%ld)",l);
+  s = FpX_FpXQ_eval(pol, FpXQ_sqr(b, T, pe), T,  pe);
+  if( DEBUGLEVEL>=3) timer_printf(&ti,"FpX_FpXQ_eval");
+  s = ZX_shifti(FpXQ_mul(b, s, T, pe), 1);
+  return gerepileupto(av, is2? s: FpX_red(s, pe));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                 Generic quadratic hensel lift over Zp[X]          **/
+/**                                                                   **/
+/***********************************************************************/
+/* q = p^N */
+
+GEN
+gen_ZpX_Dixon(GEN F, GEN V, GEN q, GEN p, long N, void *E,
+                            GEN lin(void *E, GEN F, GEN d, GEN q),
+                            GEN invl(void *E, GEN d))
+{
+  pari_sp av = avma;
+  long N2, M;
+  GEN VN2, V2, VM, bil;
+  GEN q2, qM;
+  V = FpX_red(V, q);
+  if (N == 1) return invl(E, V);
+  N2 = (N + 1)>>1; M = N - N2;
+  F = FpXT_red(F, q);
+  qM = powiu(p, M);
+  q2 = M == N2? qM: mulii(qM, p);
+  /* q2 = p^N2, qM = p^M, q = q2 * qM */
+  VN2 = gen_ZpX_Dixon(F, V, q2, p, N2, E, lin, invl);
+  bil = lin(E, F, VN2, q);
+  V2 = ZX_Z_divexact(ZX_sub(V, bil), q2);
+  VM = gen_ZpX_Dixon(F, V2, qM, p, M, E, lin, invl);
+  return gerepileupto(av, FpX_red(ZX_add(VN2, ZX_Z_mul(VM, q2)), q));
+}
+
+GEN
+gen_ZpX_Newton(GEN x, GEN p, long n, void *E,
+                      GEN eval(void *E, GEN f, GEN q),
+                      GEN invd(void *E, GEN V, GEN v, GEN q, long M))
+{
+  pari_sp ltop = avma, av, st_lim;
+  long N = 1, N2, M;
+  long mask;
+  GEN q = p;
+  if (n == 1) return gcopy(x);
+  mask = quadratic_prec_mask(n);
+  av = avma; st_lim = stack_lim(av, 1);
+  while (mask > 1)
+  {
+    GEN qM, q2, v, V;
+    N2 = N; N <<= 1;
+    q2 = q;
+    if (mask&1UL) { /* can never happen when q2 = p */
+      N--; M = N2-1;
+      qM = diviiexact(q2,p); /* > 1 */
+      q = mulii(qM,q2);
+    } else {
+      M = N2;
+      qM = q2;
+      q = sqri(q2);
+    }
+    /* q2 = p^N2, qM = p^M, q = p^N = q2 * qM */
+    mask >>= 1;
+    v = eval(E, x, q);
+    V = ZX_Z_divexact(gel(v,1), q2);
+    x = FpX_sub(x, ZX_Z_mul(invd(E, V, v, qM, M), q2), q);
+    if (low_stack(st_lim, stack_lim(av, 1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_ZpX_Newton");
+      gerepileall(av, 2, &x, &q);
+    }
+  }
+  return gerepileupto(ltop, x);
+}
+
+struct _ZpXQ_inv
+{
+  GEN T, a, p ,n;
+};
+
+static GEN
+_inv_invd(void *E, GEN V, GEN v, GEN q, long M/*unused*/)
+{
+  struct _ZpXQ_inv *d = (struct _ZpXQ_inv *) E;
+  GEN Tq = FpXT_red(d->T, q);
+  (void)M;
+  return FpXQ_mul(V, gel(v,2), Tq, q);
+}
+
+static GEN
+_inv_eval(void *E, GEN x, GEN q)
+{
+  struct _ZpXQ_inv *d = (struct _ZpXQ_inv *) E;
+  GEN Tq = FpXT_red(d->T, q);
+  GEN f = FpX_Fp_sub(FpXQ_mul(x, FpX_red(d->a, q), Tq, q), gen_1, q);
+  return mkvec2(f, x);
+}
+
+GEN
+ZpXQ_invlift(GEN a, GEN x, GEN T, GEN p, long e)
+{
+  struct _ZpXQ_inv d;
+  d.a = a; d.T = T; d.p = p;
+  return gen_ZpX_Newton(x, p, e, &d, _inv_eval, _inv_invd);
+}
+
+GEN
+ZpXQ_inv(GEN a, GEN T, GEN p, long e)
+{
+  pari_sp av=avma;
+  GEN ai;
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    ai = Flx_to_ZX(Flxq_inv(ZX_to_Flx(a,pp), ZXT_to_FlxT(T, pp), pp));
+  } else
+    ai = FpXQ_inv(FpX_red(a,p), FpXT_red(T,p),p);
+  return gerepileupto(av, ZpXQ_invlift(a, ai, T, p, e));
+}
+
+struct _ZpXQ_sqrtn
+{
+  GEN T, a, n, ai;
+};
+
+static GEN
+_sqrtn_invd(void *E, GEN V, GEN v, GEN q, long M)
+{
+  struct _ZpXQ_sqrtn *d = (struct _ZpXQ_sqrtn *) E;
+  GEN Tq = FpX_red(d->T, q), aiq = FpX_red(d->ai, q);
+  (void)M;
+  return FpXQ_mul(FpXQ_mul(V, gel(v,2), Tq, q), aiq, Tq, q);
+}
+
+static GEN
+_sqrtn_eval(void *E, GEN x, GEN q)
+{
+  struct _ZpXQ_sqrtn *d = (struct _ZpXQ_sqrtn *) E;
+  GEN Tq = FpX_red(d->T, q);
+  GEN f = FpX_sub(FpXQ_pow(x, d->n, Tq, q), d->a, q);
+  return mkvec2(f, x);
+}
+
+GEN
+ZpXQ_sqrtnlift(GEN a, GEN n, GEN x, GEN T, GEN p, long e)
+{
+  struct _ZpXQ_sqrtn d;
+  d.a = a; d.T = T; d.n = n;
+  d.ai = ZpXQ_inv(ZX_Z_mul(a, n),T,p,(e+1)>>1);
+  return gen_ZpX_Newton(x, p, e, &d, _sqrtn_eval, _sqrtn_invd);
+}
diff --git a/src/basemath/QX_factor.c b/src/basemath/QX_factor.c
new file mode 100644
index 0000000..2efb912
--- /dev/null
+++ b/src/basemath/QX_factor.c
@@ -0,0 +1,1413 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/* x,y two ZX, y non constant. Return q = x/y if y divides x in Z[X] and NULL
+ * otherwise. If not NULL, B is a t_INT upper bound for ||q||_oo. */
+static GEN
+ZX_divides_i(GEN x, GEN y, GEN B)
+{
+  long dx, dy, dz, i, j;
+  pari_sp av;
+  GEN z,p1,y_lead;
+
+  dy=degpol(y);
+  dx=degpol(x);
+  dz=dx-dy; if (dz<0) return NULL;
+  z=cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2; y += 2; z += 2;
+  y_lead = gel(y,dy);
+  if (equali1(y_lead)) y_lead = NULL;
+
+  p1 = gel(x,dx);
+  if (y_lead) {
+    GEN r;
+    p1 = dvmdii(p1,y_lead, &r);
+    if (r != gen_0) return NULL;
+  }
+  else p1 = icopy(p1);
+  gel(z,dz) = p1;
+  for (i=dx-1; i>=dy; i--)
+  {
+    av = avma; p1 = gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    if (y_lead) {
+      GEN r;
+      p1 = dvmdii(p1,y_lead, &r);
+      if (r != gen_0) return NULL;
+    }
+    if (B && absi_cmp(p1, B) > 0) return NULL;
+    p1 = gerepileuptoint(av, p1);
+    gel(z,i-dy) = p1;
+  }
+  av = avma;
+  for (; i >= 0; i--)
+  {
+    p1 = gel(x,i);
+    /* we always enter this loop at least once */
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    if (signe(p1)) return NULL;
+    avma = av;
+  }
+  return z - 2;
+}
+static GEN
+ZX_divides(GEN x, GEN y) { return ZX_divides_i(x,y,NULL); }
+
+#if 0
+/* cf Beauzamy et al: upper bound for
+ *      lc(x) * [2^(5/8) / pi^(3/8)] e^(1/4n) 2^(n/2) sqrt([x]_2)/ n^(3/8)
+ * where [x]_2 = sqrt(\sum_i=0^n x[i]^2 / binomial(n,i)). One factor has
+ * all coeffs less than then bound */
+static GEN
+two_factor_bound(GEN x)
+{
+  long i, j, n = lg(x) - 3;
+  pari_sp av = avma;
+  GEN *invbin, c, r = cgetr(3), z;
+
+  x += 2; invbin = (GEN*)new_chunk(n+1);
+  z = real_1(LOWDEFAULTPREC); /* invbin[i] = 1 / binomial(n, i) */
+  for (i=0,j=n; j >= i; i++,j--)
+  {
+    invbin[i] = invbin[j] = z;
+    z = divru(mulru(z, i+1), n-i);
+  }
+  z = invbin[0]; /* = 1 */
+  for (i=0; i<=n; i++)
+  {
+    c = gel(x,i); if (!signe(c)) continue;
+    affir(c, r);
+    z = addrr(z, mulrr(sqrr(r), invbin[i]));
+  }
+  z = shiftr(sqrtr(z), n);
+  z = divrr(z, dbltor(pow((double)n, 0.75)));
+  z = roundr_safe(sqrtr(z));
+  z = mulii(z, absi(gel(x,n)));
+  return gerepileuptoint(av, shifti(z, 1));
+}
+#endif
+
+/* A | S ==> |a_i| <= binom(d-1, i-1) || S ||_2 + binom(d-1, i) lc(S) */
+static GEN
+Mignotte_bound(GEN S)
+{
+  long i, d = degpol(S);
+  GEN C, N2, t, binlS, lS = leading_term(S), bin = vecbinome(d-1);
+
+  N2 = sqrtr(RgX_fpnorml2(S,DEFAULTPREC));
+  binlS = is_pm1(lS)? bin: ZC_Z_mul(bin, lS);
+
+  /* i = 0 */
+  C = gel(binlS,1);
+  /* i = d */
+  t = N2; if (gcmp(C, t) < 0) C = t;
+  for (i = 1; i < d; i++)
+  {
+    t = addri(mulir(gel(bin,i), N2), gel(binlS,i+1));
+    if (mpcmp(C, t) < 0) C = t;
+  }
+  return C;
+}
+/* A | S ==> |a_i|^2 <= 3^{3/2 + d} / (4 \pi d) [P]_2^2,
+ * where [P]_2 is Bombieri's 2-norm */
+static GEN
+Beauzamy_bound(GEN S)
+{
+  const long prec = DEFAULTPREC;
+  long i, d = degpol(S);
+  GEN bin, lS, s, C;
+  bin = vecbinome(d);
+
+  s = real_0(prec);
+  for (i=0; i<=d; i++)
+  {
+    GEN c = gel(S,i+2);
+    if (gequal0(c)) continue;
+    /* s += P_i^2 / binomial(d,i) */
+    s = addrr(s, divri(itor(sqri(c), prec), gel(bin,i+1)));
+  }
+  /* s = [S]_2^2 */
+  C = powruhalf(stor(3,prec), 3 + 2*d); /* 3^{3/2 + d} */
+  C = divrr(mulrr(C, s), mulur(4*d, mppi(prec)));
+  lS = absi(leading_term(S));
+  return mulir(lS, sqrtr(C));
+}
+
+static GEN
+factor_bound(GEN S)
+{
+  pari_sp av = avma;
+  GEN a = Mignotte_bound(S);
+  GEN b = Beauzamy_bound(S);
+  if (DEBUGLEVEL>2)
+  {
+    err_printf("Mignotte bound: %Ps\n",a);
+    err_printf("Beauzamy bound: %Ps\n",b);
+  }
+  return gerepileupto(av, ceil_safe(gmin(a, b)));
+}
+
+/* Naive recombination of modular factors: combine up to maxK modular
+ * factors, degree <= klim
+ *
+ * target = polynomial we want to factor
+ * famod = array of modular factors.  Product should be congruent to
+ * target/lc(target) modulo p^a
+ * For true factors: S1,S2 <= p^b, with b <= a and p^(b-a) < 2^31 */
+static GEN
+cmbf(GEN pol, GEN famod, GEN bound, GEN p, long a, long b,
+     long klim, long *pmaxK, int *done)
+{
+  long K = 1, cnt = 1, i,j,k, curdeg, lfamod = lg(famod)-1;
+  ulong spa_b, spa_bs2, Sbound;
+  GEN lc, lcpol, pa = powiu(p,a), pas2 = shifti(pa,-1);
+  GEN trace1   = cgetg(lfamod+1, t_VECSMALL);
+  GEN trace2   = cgetg(lfamod+1, t_VECSMALL);
+  GEN ind      = cgetg(lfamod+1, t_VECSMALL);
+  GEN deg      = cgetg(lfamod+1, t_VECSMALL);
+  GEN degsofar = cgetg(lfamod+1, t_VECSMALL);
+  GEN listmod  = cgetg(lfamod+1, t_VEC);
+  GEN fa       = cgetg(lfamod+1, t_VEC);
+
+  *pmaxK = cmbf_maxK(lfamod);
+  lc = absi(leading_term(pol));
+  if (is_pm1(lc)) lc = NULL;
+  lcpol = lc? ZX_Z_mul(pol, lc): pol;
+
+  {
+    GEN pa_b,pa_bs2,pb, lc2 = lc? sqri(lc): NULL;
+
+    pa_b = powiu(p, a-b); /* < 2^31 */
+    pa_bs2 = shifti(pa_b,-1);
+    pb= powiu(p, b);
+    for (i=1; i <= lfamod; i++)
+    {
+      GEN T1,T2, P = gel(famod,i);
+      long d = degpol(P);
+
+      deg[i] = d; P += 2;
+      T1 = gel(P,d-1);/* = - S_1 */
+      T2 = sqri(T1);
+      if (d > 1) T2 = subii(T2, shifti(gel(P,d-2),1));
+      T2 = modii(T2, pa); /* = S_2 Newton sum */
+      if (lc)
+      {
+        T1 = Fp_mul(lc, T1, pa);
+        T2 = Fp_mul(lc2,T2, pa);
+      }
+      trace1[i] = itou(diviiround(T1, pb));
+      trace2[i] = itou(diviiround(T2, pb));
+    }
+    spa_b   = (ulong)  pa_b[2]; /* < 2^31 */
+    spa_bs2 = (ulong)pa_bs2[2]; /* < 2^31 */
+  }
+  degsofar[0] = 0; /* sentinel */
+
+  /* ind runs through strictly increasing sequences of length K,
+   * 1 <= ind[i] <= lfamod */
+nextK:
+  if (K > *pmaxK || 2*K > lfamod) goto END;
+  if (DEBUGLEVEL > 3)
+    err_printf("\n### K = %d, %Ps combinations\n", K,binomial(utoipos(lfamod), K));
+  setlg(ind, K+1); ind[1] = 1;
+  Sbound = (ulong) ((K+1)>>1);
+  i = 1; curdeg = deg[ind[1]];
+  for(;;)
+  { /* try all combinations of K factors */
+    for (j = i; j < K; j++)
+    {
+      degsofar[j] = curdeg;
+      ind[j+1] = ind[j]+1; curdeg += deg[ind[j+1]];
+    }
+    if (curdeg <= klim) /* trial divide */
+    {
+      GEN y, q, list;
+      pari_sp av;
+      ulong t;
+
+      /* d - 1 test */
+      for (t=trace1[ind[1]],i=2; i<=K; i++)
+        t = Fl_add(t, trace1[ind[i]], spa_b);
+      if (t > spa_bs2) t = spa_b - t;
+      if (t > Sbound)
+      {
+        if (DEBUGLEVEL>6) err_printf(".");
+        goto NEXT;
+      }
+      /* d - 2 test */
+      for (t=trace2[ind[1]],i=2; i<=K; i++)
+        t = Fl_add(t, trace2[ind[i]], spa_b);
+      if (t > spa_bs2) t = spa_b - t;
+      if (t > Sbound)
+      {
+        if (DEBUGLEVEL>6) err_printf("|");
+        goto NEXT;
+      }
+
+      av = avma;
+      /* check trailing coeff */
+      y = lc;
+      for (i=1; i<=K; i++)
+      {
+        GEN q = constant_term(gel(famod,ind[i]));
+        if (y) q = mulii(y, q);
+        y = centermodii(q, pa, pas2);
+      }
+      if (!signe(y) || remii(constant_term(lcpol), y) != gen_0)
+      {
+        if (DEBUGLEVEL>3) err_printf("T");
+        avma = av; goto NEXT;
+      }
+      y = lc; /* full computation */
+      for (i=1; i<=K; i++)
+      {
+        GEN q = gel(famod,ind[i]);
+        if (y) q = gmul(y, q);
+        y = centermod_i(q, pa, pas2);
+      }
+
+      /* y is the candidate factor */
+      if (! (q = ZX_divides_i(lcpol,y,bound)) )
+      {
+        if (DEBUGLEVEL>3) err_printf("*");
+        avma = av; goto NEXT;
+      }
+      /* found a factor */
+      list = cgetg(K+1, t_VEC);
+      gel(listmod,cnt) = list;
+      for (i=1; i<=K; i++) list[i] = famod[ind[i]];
+
+      y = Q_primpart(y);
+      gel(fa,cnt++) = y;
+      /* fix up pol */
+      pol = q;
+      if (lc) pol = Q_div_to_int(pol, leading_term(y));
+      for (i=j=k=1; i <= lfamod; i++)
+      { /* remove used factors */
+        if (j <= K && i == ind[j]) j++;
+        else
+        {
+          gel(famod,k) = gel(famod,i);
+          trace1[k] = trace1[i];
+          trace2[k] = trace2[i];
+          deg[k] = deg[i]; k++;
+        }
+      }
+      lfamod -= K;
+      *pmaxK = cmbf_maxK(lfamod);
+      if (lfamod < 2*K) goto END;
+      i = 1; curdeg = deg[ind[1]];
+      bound = factor_bound(pol);
+      if (lc) lc = absi(leading_term(pol));
+      lcpol = lc? ZX_Z_mul(pol, lc): pol;
+      if (DEBUGLEVEL>3)
+        err_printf("\nfound factor %Ps\nremaining modular factor(s): %ld\n",
+                   y, lfamod);
+      continue;
+    }
+
+NEXT:
+    for (i = K+1;;)
+    {
+      if (--i == 0) { K++; goto nextK; }
+      if (++ind[i] <= lfamod - K + i)
+      {
+        curdeg = degsofar[i-1] + deg[ind[i]];
+        if (curdeg <= klim) break;
+      }
+    }
+  }
+END:
+  *done = 1;
+  if (degpol(pol) > 0)
+  { /* leftover factor */
+    if (signe(leading_term(pol)) < 0) pol = ZX_neg(pol);
+    if (lfamod >= 2*K) *done = 0;
+
+    setlg(famod, lfamod+1);
+    gel(listmod,cnt) = leafcopy(famod);
+    gel(fa,cnt++) = pol;
+  }
+  if (DEBUGLEVEL>6) err_printf("\n");
+  setlg(listmod, cnt);
+  setlg(fa, cnt); return mkvec2(fa, listmod);
+}
+
+void
+factor_quad(GEN x, GEN res, long *ptcnt)
+{
+  GEN a = gel(x,4), b = gel(x,3), c = gel(x,2), d, u, z1, z2, t;
+  GEN D = subii(sqri(b), shifti(mulii(a,c), 2));
+  long v, cnt = *ptcnt;
+
+  if (!Z_issquareall(D, &d)) { gel(res,cnt++) = x; *ptcnt = cnt; return; }
+
+  t = shifti(negi(addii(b, d)), -1);
+  z1 = gdiv(t, a); u = denom(z1);
+  z2 = gdiv(addii(t, d), a);
+  v = varn(x);
+  gel(res,cnt++) = gmul(u, gsub(pol_x(v), z1)); u = diviiexact(a, u);
+  gel(res,cnt++) = gmul(u, gsub(pol_x(v), z2)); *ptcnt = cnt;
+}
+
+/* recombination of modular factors: van Hoeij's algorithm */
+
+/* Q in Z[X], return Q(2^n) */
+static GEN
+shifteval(GEN Q, long n)
+{
+  long i, l = lg(Q);
+  GEN s;
+
+  if (!signe(Q)) return gen_0;
+  s = gel(Q,l-1);
+  for (i = l-2; i > 1; i--) s = addii(gel(Q,i), shifti(s, n));
+  return s;
+}
+
+/* return integer y such that all |a| <= y if P(a) = 0 */
+static GEN
+root_bound(GEN P0)
+{
+  GEN Q = leafcopy(P0), lP = absi(leading_term(Q)), x,y,z;
+  long k, d = degpol(Q);
+
+  /* P0 = lP x^d + Q, deg Q < d */
+  Q = normalizepol_lg(Q, d+2);
+  for (k=lg(Q)-1; k>1; k--) gel(Q,k) = absi(gel(Q,k));
+  k = (long)(cauchy_bound(P0) / LOG2);
+  for (  ; k >= 0; k--)
+  {
+    pari_sp av = avma;
+    /* y = 2^k; Q(y) >= lP y^d ? */
+    if (cmpii(shifteval(Q,k), shifti(lP, d*k)) >= 0) break;
+    avma = av;
+  }
+  if (k < 0) k = 0;
+  x = int2n(k);
+  y = int2n(k+1);
+  for(k=0; ; k++)
+  {
+    z = shifti(addii(x,y), -1);
+    if (equalii(x,z) || k > 5) break;
+    if (cmpii(poleval(Q,z), mulii(lP, powiu(z, d))) < 0)
+      y = z;
+    else
+      x = z;
+  }
+  return y;
+}
+
+GEN
+special_pivot(GEN x)
+{
+  GEN t, perm, H = ZM_hnfperm(x,NULL,&perm);
+  long i,j, l = lg(H), h = lgcols(H);
+  for (i=1; i<h; i++)
+  {
+    int fl = 0;
+    for (j=1; j<l; j++)
+    {
+      t = gcoeff(H,i,j);
+      if (signe(t))
+      {
+        if (!is_pm1(t) || fl) return NULL;
+        fl = 1;
+      }
+    }
+  }
+  return rowpermute(H, perm_inv(perm));
+}
+
+GEN
+chk_factors_get(GEN lt, GEN famod, GEN c, GEN T, GEN N)
+{
+  long i = 1, j, l = lg(famod);
+  GEN V = cgetg(l, t_VEC);
+  for (j = 1; j < l; j++)
+    if (signe(gel(c,j))) gel(V,i++) = gel(famod,j);
+  if (lt && i > 1) gel(V,1) = RgX_Rg_mul(gel(V,1), lt);
+  setlg(V, i);
+  return T? FpXQXV_prod(V, T, N): FpXV_prod(V,N);
+}
+
+static GEN
+chk_factors(GEN P, GEN M_L, GEN bound, GEN famod, GEN pa)
+{
+  long i, r;
+  GEN pol = P, list, piv, y, ltpol, lt, paov2;
+
+  piv = special_pivot(M_L);
+  if (!piv) return NULL;
+  if (DEBUGLEVEL>7) err_printf("special_pivot output:\n%Ps\n",piv);
+
+  r  = lg(piv)-1;
+  list = cgetg(r+1, t_VEC);
+  lt = absi(leading_term(pol));
+  if (is_pm1(lt)) lt = NULL;
+  ltpol = lt? ZX_Z_mul(pol, lt): pol;
+  paov2 = shifti(pa,-1);
+  for (i = 1;;)
+  {
+    if (DEBUGLEVEL) err_printf("LLL_cmbf: checking factor %ld\n",i);
+    y = chk_factors_get(lt, famod, gel(piv,i), NULL, pa);
+    y = FpX_center(y, pa, paov2);
+    if (! (pol = ZX_divides_i(ltpol,y,bound)) ) return NULL;
+    if (lt) y = Q_primpart(y);
+    gel(list,i) = y;
+    if (++i >= r) break;
+
+    if (lt)
+    {
+      pol = ZX_Z_divexact(pol, leading_term(y));
+      lt = absi(leading_term(pol));
+      ltpol = ZX_Z_mul(pol, lt);
+    }
+    else
+      ltpol = pol;
+  }
+  y = Q_primpart(pol);
+  gel(list,i) = y; return list;
+}
+
+GEN
+LLL_check_progress(GEN Bnorm, long n0, GEN m, int final, long *ti_LLL)
+{
+  GEN norm, u;
+  long i, R;
+  pari_timer T;
+
+  if (DEBUGLEVEL>2) timer_start(&T);
+  u = ZM_lll_norms(m, final? 0.999: 0.75, LLL_INPLACE, &norm);
+  if (DEBUGLEVEL>2) *ti_LLL += timer_delay(&T);
+  for (R=lg(m)-1; R > 0; R--)
+    if (cmprr(gel(norm,R), Bnorm) < 0) break;
+  for (i=1; i<=R; i++) setlg(u[i], n0+1);
+  if (R <= 1)
+  {
+    if (!R) pari_err_BUG("LLL_cmbf [no factor]");
+    return NULL; /* irreducible */
+  }
+  setlg(u, R+1); return u;
+}
+
+static ulong
+next2pow(ulong a)
+{
+  ulong b = 1;
+  while (b < a) b <<= 1;
+  return b;
+}
+
+/* Recombination phase of Berlekamp-Zassenhaus algorithm using a variant of
+ * van Hoeij's knapsack
+ *
+ * P = squarefree in Z[X].
+ * famod = array of (lifted) modular factors mod p^a
+ * bound = Mignotte bound for the size of divisors of P (for the sup norm)
+ * previously recombined all set of factors with less than rec elts */
+static GEN
+LLL_cmbf(GEN P, GEN famod, GEN p, GEN pa, GEN bound, long a, long rec)
+{
+  const long N0 = 1; /* # of traces added at each step */
+  double BitPerFactor = 0.4; /* nb bits in p^(a-b) / modular factor */
+  long i,j,tmax,n0,C, dP = degpol(P);
+  double logp = log((double)itos(p)), LOGp2 = LOG2/logp;
+  double b0 = log((double)dP*2) / logp, logBr;
+  GEN lP, Br, Bnorm, Tra, T2, TT, CM_L, m, list, ZERO;
+  pari_sp av, av2, lim;
+  long ti_LLL = 0, ti_CF  = 0;
+
+  lP = absi(leading_term(P));
+  if (is_pm1(lP)) lP = NULL;
+  Br = root_bound(P);
+  if (lP) Br = mulii(lP, Br);
+  logBr = gtodouble(glog(Br, DEFAULTPREC)) / logp;
+
+  n0 = lg(famod) - 1;
+  C = (long)ceil( sqrt(N0 * n0 / 4.) ); /* > 1 */
+  Bnorm = dbltor(n0 * (C*C + N0*n0/4.) * 1.00001);
+  ZERO = zeromat(n0, N0);
+
+  av = avma; lim = stack_lim(av, 1);
+  TT = cgetg(n0+1, t_VEC);
+  Tra  = cgetg(n0+1, t_MAT);
+  for (i=1; i<=n0; i++)
+  {
+    TT[i]  = 0;
+    gel(Tra,i) = cgetg(N0+1, t_COL);
+  }
+  CM_L = scalarmat_s(C, n0);
+  /* tmax = current number of traces used (and computed so far) */
+  for (tmax = 0;; tmax += N0)
+  {
+    long b, bmin, bgood, delta, tnew = tmax + N0, r = lg(CM_L)-1;
+    GEN M_L, q, CM_Lp, oldCM_L;
+    int first = 1;
+    pari_timer ti2, TI;
+
+    bmin = (long)ceil(b0 + tnew*logBr);
+    if (DEBUGLEVEL>2)
+      err_printf("\nLLL_cmbf: %ld potential factors (tmax = %ld, bmin = %ld)\n",
+                 r, tmax, bmin);
+
+    /* compute Newton sums (possibly relifting first) */
+    if (a <= bmin)
+    {
+      a = (long)ceil(bmin + 3*N0*logBr) + 1; /* enough for 3 more rounds */
+      a = (long)next2pow((ulong)a);
+
+      pa = powiu(p,a);
+      famod = ZpX_liftfact(P,famod,NULL,p,a,pa);
+      for (i=1; i<=n0; i++) TT[i] = 0;
+    }
+    for (i=1; i<=n0; i++)
+    {
+      GEN p1 = gel(Tra,i);
+      GEN p2 = polsym_gen(gel(famod,i), gel(TT,i), tnew, NULL, pa);
+      gel(TT,i) = p2;
+      p2 += 1+tmax; /* ignore traces number 0...tmax */
+      for (j=1; j<=N0; j++) gel(p1,j) = gel(p2,j);
+      if (lP)
+      { /* make Newton sums integral */
+        GEN lPpow = powiu(lP, tmax);
+        for (j=1; j<=N0; j++)
+        {
+          lPpow = mulii(lPpow,lP);
+          gel(p1,j) = mulii(gel(p1,j), lPpow);
+        }
+      }
+    }
+
+    /* compute truncation parameter */
+    if (DEBUGLEVEL>2) { timer_start(&ti2); timer_start(&TI); }
+    oldCM_L = CM_L;
+    av2 = avma;
+    delta = b = 0; /* -Wall */
+AGAIN:
+    M_L = Q_div_to_int(CM_L, utoipos(C));
+    T2 = centermod( ZM_mul(Tra, M_L), pa );
+    if (first)
+    { /* initialize lattice, using few p-adic digits for traces */
+      double t = gexpo(T2) - maxdd(32.0, BitPerFactor*r);
+      bgood = (long) (t * LOGp2);
+      b = maxss(bmin, bgood);
+      delta = a - b;
+    }
+    else
+    { /* add more p-adic digits and continue reduction */
+      long b0 = (long)(gexpo(T2) * LOGp2);
+      if (b0 < b) b = b0;
+      b = maxss(b-delta, bmin);
+      if (b - delta/2 < bmin) b = bmin; /* near there. Go all the way */
+    }
+
+    q = powiu(p, b);
+    m = vconcat( CM_L, gdivround(T2, q) );
+    if (first)
+    {
+      GEN P1 = scalarmat(powiu(p, a-b), N0);
+      first = 0;
+      m = shallowconcat( m, vconcat(ZERO, P1) );
+      /*     [ C M_L        0     ]
+       * m = [                    ]   square matrix
+       *     [  T2'  p^(a-b) I_N0 ]   T2' = Tra * M_L  truncated
+       */
+    }
+
+    CM_L = LLL_check_progress(Bnorm, n0, m, b == bmin, /*dbg:*/ &ti_LLL);
+    if (DEBUGLEVEL>2)
+      err_printf("LLL_cmbf: (a,b) =%4ld,%4ld; r =%3ld -->%3ld, time = %ld\n",
+                 a,b, lg(m)-1, CM_L? lg(CM_L)-1: 1, timer_delay(&TI));
+    if (!CM_L) { list = mkvec(P); break; }
+    if (b > bmin)
+    {
+      CM_L = gerepilecopy(av2, CM_L);
+      goto AGAIN;
+    }
+    if (DEBUGLEVEL>2) timer_printf(&ti2, "for this block of traces");
+
+    i = lg(CM_L) - 1;
+    if (i == r && ZM_equal(CM_L, oldCM_L))
+    {
+      CM_L = oldCM_L;
+      avma = av2; continue;
+    }
+
+    CM_Lp = FpM_image(CM_L, utoipos(27449)); /* inexpensive test */
+    if (lg(CM_Lp) != lg(CM_L))
+    {
+      if (DEBUGLEVEL>2) err_printf("LLL_cmbf: rank decrease\n");
+      CM_L = ZM_hnf(CM_L);
+    }
+
+    if (i <= r && i*rec < n0)
+    {
+      pari_timer ti;
+      if (DEBUGLEVEL>2) timer_start(&ti);
+      list = chk_factors(P, Q_div_to_int(CM_L,utoipos(C)), bound, famod, pa);
+      if (DEBUGLEVEL>2) ti_CF += timer_delay(&ti);
+      if (list) break;
+      if (DEBUGLEVEL>2) err_printf("LLL_cmbf: chk_factors failed");
+    }
+    CM_L = gerepilecopy(av2, CM_L);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"LLL_cmbf");
+      gerepileall(av, 5, &CM_L, &TT, &Tra, &famod, &pa);
+    }
+  }
+  if (DEBUGLEVEL>2)
+    err_printf("* Time LLL: %ld\n* Time Check Factor: %ld\n",ti_LLL,ti_CF);
+  return list;
+}
+
+/* Find a,b minimal such that A < q^a, B < q^b, 1 << q^(a-b) < 2^31 */
+static int
+cmbf_precs(GEN q, GEN A, GEN B, long *pta, long *ptb, GEN *qa, GEN *qb)
+{
+  long a,b,amin,d = (long)(31 * LOG2/gtodouble(glog(q,DEFAULTPREC)) - 1e-5);
+  int fl = 0;
+
+  b = logint(B, q, qb);
+  amin = b + d;
+  if (gcmp(powiu(q, amin), A) <= 0)
+  {
+    a = logint(A, q, qa);
+    b = a - d; *qb = powiu(q, b);
+  }
+  else
+  { /* not enough room */
+    a = amin;  *qa = powiu(q, a);
+    fl = 1;
+  }
+  if (DEBUGLEVEL > 3) {
+    err_printf("S_2   bound: %Ps^%ld\n", q,b);
+    err_printf("coeff bound: %Ps^%ld\n", q,a);
+  }
+  *pta = a;
+  *ptb = b; return fl;
+}
+
+/* use van Hoeij's knapsack algorithm */
+static GEN
+combine_factors(GEN target, GEN famod, GEN p, long klim)
+{
+  GEN la, B, A, res, L, pa, pb, listmod;
+  long a,b, l, maxK, n = degpol(target);
+  int done;
+  pari_timer T;
+
+  A = factor_bound(target);
+
+  la = absi(leading_term(target));
+  B = mului(n, sqri(mulii(la, root_bound(target)))); /* = bound for S_2 */
+
+  (void)cmbf_precs(p, A, B, &a, &b, &pa, &pb);
+
+  if (DEBUGLEVEL>2) timer_start(&T);
+  famod = ZpX_liftfact(target,famod,NULL,p,a,pa);
+  if (DEBUGLEVEL>2) timer_printf(&T, "Hensel lift (mod %Ps^%ld)", p,a);
+  L = cmbf(target, famod, A, p, a, b, klim, &maxK, &done);
+  if (DEBUGLEVEL>2) timer_printf(&T, "Naive recombination");
+
+  res     = gel(L,1);
+  listmod = gel(L,2); l = lg(listmod)-1;
+  famod = gel(listmod,l);
+  if (maxK > 0 && lg(famod)-1 > 2*maxK)
+  {
+    if (l!=1) A = factor_bound(gel(res,l));
+    if (DEBUGLEVEL > 4) err_printf("last factor still to be checked\n");
+    L = LLL_cmbf(gel(res,l), famod, p, pa, A, a, maxK);
+    if (DEBUGLEVEL>2) timer_printf(&T,"Knapsack");
+    /* remove last elt, possibly unfactored. Add all new ones. */
+    setlg(res, l); res = shallowconcat(res, L);
+  }
+  return res;
+}
+
+/* Assume 'a' a squarefree ZX; return 0 if no root (fl=1) / irreducible (fl=0).
+ * Otherwise return prime p such that a mod p has fewest roots / factors */
+static ulong
+pick_prime(GEN a, long fl, pari_timer *T)
+{
+  pari_sp av = avma, av1;
+  const long MAXNP = 7, da = degpol(a);
+  long nmax = da+1, np;
+  ulong chosenp = 0;
+  GEN lead = gel(a,da+2);
+  forprime_t S;
+  if (equali1(lead)) lead = NULL;
+  u_forprime_init(&S, 2, ULONG_MAX);
+  av1 = avma;
+  for (np = 0; np < MAXNP; avma = av1)
+  {
+    ulong p = u_forprime_next(&S);
+    long nfacp;
+    GEN z;
+
+    if (!p) pari_err_OVERFLOW("DDF [out of small primes]");
+    if (lead && !umodiu(lead,p)) continue;
+    z = ZX_to_Flx(a, p);
+    if (!Flx_is_squarefree(z, p)) continue;
+
+    if (fl)
+    {
+      nfacp = Flx_nbroots(z, p);
+      if (!nfacp) { chosenp = 0; break; } /* no root */
+    }
+    else
+    {
+      nfacp = Flx_nbfact(z, p);
+      if (nfacp == 1) { chosenp = 0; break; } /* irreducible */
+    }
+    if (DEBUGLEVEL>4)
+      err_printf("...tried prime %3lu (%-3ld %s). Time = %ld\n",
+                  p, nfacp, fl? "roots": "factors", timer_delay(T));
+    if (nfacp < nmax)
+    {
+      nmax = nfacp; chosenp = p;
+      if (da > 100 && nmax < 5) break; /* large degree, few factors. Enough */
+    }
+    np++;
+  }
+  avma = av; return chosenp;
+}
+
+/* Assume pol squarefree mod p; return vector of rational roots of a */
+static GEN
+DDF_roots(GEN A)
+{
+  GEN p, Ap, lc, lcpol, z, pe, pes2, bound;
+  long i, m, e, lz, v = varn(A);
+  ulong pp;
+  pari_sp av, lim;
+  pari_timer T;
+
+  if (DEBUGLEVEL>2) timer_start(&T);
+  pp = pick_prime(A, 1, &T);
+  if (!pp) return cgetg(1,t_VEC); /* no root */
+  p = utoipos(pp);
+  lc = leading_term(A);
+  if (is_pm1(lc))
+  { lc = NULL; lcpol = A; }
+  else
+  { lc = absi(lc); lcpol = ZX_Z_mul(A, lc); }
+  Ap = Flx_normalize(ZX_to_Flx(A, pp), pp);
+  bound = root_bound(A);
+  if (lc) bound = mulii(lc, bound);
+  e = logint(addiu(shifti(bound, 1), 1), p, &pe);
+  pes2 = shifti(pe, -1);
+  if (DEBUGLEVEL>2) timer_printf(&T, "Root bound");
+
+  av = avma; lim = stack_lim(av,2);
+  z = Flx_roots(Ap, pp);
+  lz = lg(z)-1;
+  if (lz > (degpol(A) >> 2))
+  { /* many roots */
+    GEN Bp = Flx_div(Ap, Flv_roots_to_pol(z, pp, v), pp);
+    z = Flv_to_ZV(z);
+    z = shallowconcat(deg1_from_roots(z, v), Flx_to_ZX(Bp));
+    z = ZpX_liftfact(A, z, NULL, p, e, pe);
+  }
+  else
+  {
+    z = Flv_to_ZV(z);
+    z = ZpX_liftroots(A, z, p, e);
+    z = deg1_from_roots(z, v);
+  }
+  if (DEBUGLEVEL>2) timer_printf(&T, "Hensel lift (mod %lu^%ld)", pp,e);
+
+  for (m=1, i=1; i <= lz; i++)
+  {
+    GEN q, r, y = gel(z,i);
+    if (lc) y = ZX_Z_mul(y, lc);
+    y = centermod_i(y, pe, pes2);
+    if (! (q = ZX_divides(lcpol, y)) ) continue;
+
+    lcpol = A = q;
+    r = negi( constant_term(y) );
+    if (lc) {
+      r = gdiv(r,lc);
+      A = Q_primpart(A);
+      lc = absi( leading_term(A) );
+      if (is_pm1(lc)) lc = NULL; else lcpol = ZX_Z_mul(A, lc);
+    }
+    gel(z,m++) = r;
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"DDF_roots, m = %ld", m);
+      gerepileall(av, lc? 4:2, &z, &A, &lc, &lcpol);
+
+    }
+  }
+  if (DEBUGLEVEL>2) timer_printf(&T, "Recombination");
+  z[0] = evaltyp(t_VEC) | evallg(m); return z;
+}
+
+/* Assume a squarefree ZX, deg(a) > 0, return rational factors.
+ * In fact, a(0) != 0 but we don't use this */
+static GEN
+DDF(GEN a)
+{
+  GEN ap, prime, famod, z;
+  long ti = 0;
+  ulong p = 0;
+  pari_sp av = avma;
+  pari_timer T, T2;
+
+  if (DEBUGLEVEL>2) { timer_start(&T); timer_start(&T2); }
+  p = pick_prime(a, 0, &T2);
+  if (!p) return mkvec(a);
+  prime = utoipos(p);
+  ap = Flx_normalize(ZX_to_Flx(a, p), p);
+  famod = gel(Flx_factor(ap, p), 1);
+  if (DEBUGLEVEL>2)
+  {
+    if (DEBUGLEVEL>4) timer_printf(&T2, "splitting mod p = %lu", p);
+    ti = timer_delay(&T);
+    err_printf("Time setup: %ld\n", ti);
+  }
+  z = combine_factors(a, FlxV_to_ZXV(famod), prime, degpol(a)-1);
+  if (DEBUGLEVEL>2)
+    err_printf("Total Time: %ld\n===========\n", ti + timer_delay(&T));
+  return gerepilecopy(av, z);
+}
+
+/* Distinct Degree Factorization (deflating first)
+ * Assume x squarefree, degree(x) > 0, x(0) != 0 */
+GEN
+ZX_DDF(GEN x)
+{
+  GEN L;
+  long m;
+  x = RgX_deflate_max(x, &m);
+  L = DDF(x);
+  if (m > 1)
+  {
+    GEN e, v, fa = factoru(m);
+    long i,j,k, l;
+
+    e = gel(fa,2); k = 0;
+    fa= gel(fa,1); l = lg(fa);
+    for (i=1; i<l; i++) k += e[i];
+    v = cgetg(k+1, t_VECSMALL); k = 1;
+    for (i=1; i<l; i++)
+      for (j=1; j<=e[i]; j++) v[k++] = fa[i];
+    for (k--; k; k--)
+    {
+      GEN L2 = cgetg(1,t_VEC);
+      for (i=1; i < lg(L); i++)
+              L2 = shallowconcat(L2, DDF(RgX_inflate(gel(L,i), v[k])));
+      L = L2;
+    }
+  }
+  return L;
+}
+
+/* SquareFree Factorization. f = prod P^e, all e distinct, in Z[X] (char 0
+ * would be enough, if ZX_gcd --> ggcd). Return (P), set *ex = (e) */
+GEN
+ZX_squff(GEN f, GEN *ex)
+{
+  GEN T, V, P, e;
+  long i, k, n, val;
+
+  if (signe(leading_term(f)) < 0) f = gneg_i(f);
+  val = ZX_valrem(f, &f);
+  n = 1 + degpol(f); if (val) n++;
+  e = cgetg(n,t_VECSMALL);
+  P = cgetg(n,t_COL);
+
+  T = ZX_gcd_all(f, ZX_deriv(f), &V);
+  for (k=i=1;; k++)
+  {
+    pari_sp av = avma;
+    GEN W = ZX_gcd_all(T,V, &T);
+    long dW = degpol(W);
+    /* W = prod P^e, e > k; V = prod P^e, e >= k */
+    if (dW == degpol(V)) /* V | T */
+    {
+      GEN U;
+      if (!dW) { avma = av; break; }
+      while ( (U = ZX_divides(T, V)) ) { k++; T = U; }
+      T = gerepileupto(av, T);
+    }
+    else
+    {
+      gel(P,i) = Q_primpart(RgX_div(V,W));
+      e[i] = k; i++;
+      if (!dW) break;
+      V = W;
+    }
+  }
+  if (val) { gel(P,i) = pol_x(varn(f)); e[i] = val; i++;}
+  setlg(P,i);
+  setlg(e,i); *ex = e; return P;
+}
+
+GEN
+fact_from_DDF(GEN fa, GEN e, long n)
+{
+  GEN v,w, y = cgetg(3, t_MAT);
+  long i,j,k, l = lg(fa);
+
+  v = cgetg(n+1,t_COL); gel(y,1) = v;
+  w = cgetg(n+1,t_COL); gel(y,2) = w;
+  for (k=i=1; i<l; i++)
+  {
+    GEN L = gel(fa,i), ex = utoipos(e[i]);
+    long J = lg(L);
+    for (j=1; j<J; j++,k++)
+    {
+      gel(v,k) = gcopy(gel(L,j));
+      gel(w,k) = ex;
+    }
+  }
+  return y;
+}
+
+/* Factor x in Z[t] */
+static GEN
+ZX_factor_i(GEN x)
+{
+  GEN fa,ex,y;
+  long n,i,l;
+
+  if (!signe(x)) return prime_fact(x);
+  fa = ZX_squff(x, &ex);
+  l = lg(fa); n = 0;
+  for (i=1; i<l; i++)
+  {
+    gel(fa,i) = ZX_DDF(gel(fa,i));
+    n += lg(gel(fa,i))-1;
+  }
+  y = fact_from_DDF(fa,ex,n);
+  return sort_factor_pol(y, cmpii);
+}
+GEN
+ZX_factor(GEN x)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, ZX_factor_i(x));
+}
+GEN
+QX_factor(GEN x)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, ZX_factor_i(Q_primpart(x)));
+}
+
+long
+ZX_is_irred(GEN x)
+{
+  pari_sp av = avma;
+  long l = lg(x);
+  GEN y;
+  if (l <= 3) return 0; /* degree < 1 */
+  if (l == 4) return 1; /* degree 1 */
+  if (ZX_val(x)) return 0;
+  if (!ZX_is_squarefree(x)) return 0;
+  y = ZX_DDF(x); avma = av;
+  return (lg(y) == 2);
+}
+
+GEN
+nfrootsQ(GEN x)
+{
+  pari_sp av = avma;
+  GEN z;
+  long val;
+
+  if (typ(x)!=t_POL) pari_err_TYPE("nfrootsQ",x);
+  if (!signe(x)) pari_err_ROOTS0("nfrootsQ");
+  x = Q_primpart(x);
+  if (!RgX_is_ZX(x)) pari_err_TYPE("nfrootsQ",x);
+  val = ZX_valrem(x, &x);
+  (void)ZX_gcd_all(x, ZX_deriv(x), &x);
+  z = DDF_roots(x);
+  if (val) z = shallowconcat(z, gen_0);
+  return gerepileupto(av, sort(z));
+}
+
+/************************************************************************
+ *                   GCD OVER Z[X] / Q[X]                               *
+ ************************************************************************/
+int
+ZX_is_squarefree(GEN x)
+{
+  pari_sp av = avma;
+  GEN d = ZX_gcd(x,ZX_deriv(x));
+  int r = (lg(d) == 3); avma = av; return r;
+}
+
+#if 0
+/* ceil( || p ||_oo / lc(p) ) */
+static GEN
+maxnorm(GEN p)
+{
+  long i, n = degpol(p), av = avma;
+  GEN x, m = gen_0;
+
+  p += 2;
+  for (i=0; i<n; i++)
+  {
+    x = gel(p,i);
+    if (absi_cmp(x,m) > 0) m = x;
+  }
+  m = divii(m, gel(p,n));
+  return gerepileuptoint(av, addis(absi(m),1));
+}
+#endif
+
+/* A, B in Z[X] */
+GEN
+ZX_gcd_all(GEN A, GEN B, GEN *Anew)
+{
+  GEN R, a, b, q, H, Hp, g, Ag, Bg;
+  long m, n, valX, valA, vA = varn(A);
+  ulong p;
+  pari_sp ltop, av, avlim;
+  forprime_t S;
+
+  if (!signe(A)) { if (Anew) *Anew = pol_0(vA); return ZX_copy(B); }
+  if (!signe(B)) { if (Anew) *Anew = pol_1(vA); return ZX_copy(A); }
+  valA = ZX_valrem(A, &A);
+  valX = minss(valA, ZX_valrem(B, &B));
+  ltop = avma;
+
+  n = 1 + minss(degpol(A), degpol(B)); /* > degree(gcd) */
+  g = gcdii(leading_term(A), leading_term(B)); /* multiple of lead(gcd) */
+  if (is_pm1(g)) {
+    g = NULL;
+    Ag = A;
+    Bg = B;
+  } else {
+    Ag = ZX_Z_mul(A,g);
+    Bg = ZX_Z_mul(B,g);
+  }
+  init_modular(&S);
+  av = avma; avlim = stack_lim(av, 1);
+  R = NULL;/*-Wall*/
+  H = NULL;
+  while ((p = u_forprime_next(&S)))
+  {
+    if (g && !umodiu(g,p)) continue;
+    a = ZX_to_Flx(A, p);
+    b = ZX_to_Flx(B, p); Hp = Flx_gcd(a,b, p);
+    m = degpol(Hp);
+    if (m == 0) { /* coprime. DONE */
+      avma = ltop;
+      if (Anew) {
+        if (valA != valX) A = RgX_shift(A, valA - valX);
+        *Anew = A;
+      }
+      return monomial(gen_1, valX, vA);
+    }
+    if (m > n) continue; /* p | Res(A/G, B/G). Discard */
+
+    if (!g) /* make sure lead(H) = g mod p */
+      Hp = Flx_normalize(Hp, p);
+    else
+    {
+      ulong t = Fl_mul(umodiu(g, p), Fl_inv(Hp[m+2],p), p);
+      Hp = Flx_Fl_mul(Hp, t, p);
+    }
+    if (m < n)
+    { /* First time or degree drop [all previous p were as above; restart]. */
+      H = ZX_init_CRT(Hp,p,vA);
+      q = utoipos(p); n = m; continue;
+    }
+    if (DEBUGLEVEL>5) err_printf("gcd mod %lu (bound 2^%ld)\n", p,expi(q));
+    if (low_stack(avlim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"QX_gcd");
+      gerepileall(av, 3, &H, &q, &Hp);
+    }
+
+    if (!ZX_incremental_CRT(&H, Hp, &q, p)) continue;
+    /* H stable: check divisibility */
+    if (!ZX_divides(Bg, H)) continue;
+    R = ZX_divides(Ag, H);
+    if (R) break;
+  }
+  if (!p) pari_err_OVERFLOW("ZX_gcd_all [ran out of primes]");
+  if (Anew) {
+    A = R;
+    if (valA != valX) A = RgX_shift(A, valA - valX);
+    *Anew = A;
+  }
+  return valX ? RgX_shift(H, valX): H;
+}
+GEN
+ZX_gcd(GEN A, GEN B) { return ZX_gcd_all(A,B,NULL); }
+
+static GEN
+_gcd(GEN a, GEN b)
+{
+  if (!a) a = gen_1;
+  if (!b) b = gen_1;
+  return Q_gcd(a,b);
+}
+/* A0 and B0 in Q[X] */
+GEN
+QX_gcd(GEN A0, GEN B0)
+{
+  GEN a, b, D;
+  pari_sp av = avma, av2;
+
+  D = ZX_gcd(Q_primitive_part(A0, &a), Q_primitive_part(B0, &b));
+  av2 = avma; a = _gcd(a,b);
+  if (isint1(a)) avma = av2; else D = RgX_Rg_mul(D, a);
+  return gerepileupto(av, D);
+}
+
+/*****************************************************************************
+ * Variants of the Bradford-Davenport algorithm: look for cyclotomic         *
+ * factors, and decide whether a ZX is cyclotomic or a product of cyclotomic *
+ *****************************************************************************/
+/* f of degree 1, return a cyclotomic factor (Phi_1 or Phi_2) or NULL */
+static GEN
+BD_deg1(GEN f)
+{
+  GEN a = gel(f,3), b = gel(f,2); /* f = ax + b */
+  if (!absi_equal(a,b)) return NULL;
+  return polcyclo((signe(a) == signe(b))? 2: 1, varn(f));
+}
+
+/* f a squarefree ZX; not divisible by any Phi_n, n even */
+static GEN
+BD_odd(GEN f)
+{
+  while(degpol(f) > 1)
+  {
+    GEN f1 = ZX_graeffe(f); /* contain all cyclotomic divisors of f */
+    if (ZX_equal(f1, f)) return f; /* product of cyclotomics */
+    f = ZX_gcd(f, f1);
+  }
+  if (degpol(f) == 1) return BD_deg1(f);
+  return NULL; /* no cyclotomic divisor */
+}
+
+static GEN
+myconcat(GEN v, GEN x)
+{
+  if (typ(x) != t_VEC) x = mkvec(x);
+  if (!v) return x;
+  return shallowconcat(v, x);
+}
+
+/* Bradford-Davenport algorithm.
+ * f a squarefree ZX of degree > 0, return NULL or a vector of coprime
+ * cyclotomic factors of f [ possibly reducible ] */
+static GEN
+BD(GEN f)
+{
+  GEN G = NULL, Gs = NULL, Gp = NULL, Gi = NULL;
+  GEN fs2, fp, f2, f1, fe, fo, fe1, fo1;
+  RgX_even_odd(f, &fe, &fo);
+  fe1 = ZX_eval1(fe);
+  fo1 = ZX_eval1(fo);
+  if (absi_equal(fe1, fo1)) /* f(1) = 0 or f(-1) = 0 */
+  {
+    long i, v = varn(f);
+    if (!signe(fe1))
+      G = mkvec2(polcyclo(1, v), polcyclo(2, v)); /* both 0 */
+    else if (signe(fe1) == signe(fo1))
+      G = mkvec(polcyclo(2, v)); /*f(-1) = 0*/
+    else
+      G = mkvec(polcyclo(1, v)); /*f(1) = 0*/
+    for (i = lg(G)-1; i; i--) f = RgX_div(f, gel(G,i));
+  }
+  /* f no longer divisible by Phi_1 or Phi_2 */
+  if (degpol(f) <= 1) return G;
+  f1 = ZX_graeffe(f); /* has at most square factors */
+  if (ZX_equal(f1, f)) return myconcat(G,f); /* f = product of Phi_n, n odd */
+
+  fs2 = ZX_gcd_all(f1, ZX_deriv(f1), &f2); /* fs2 squarefree */
+  if (degpol(fs2))
+  { /* fs contains all Phi_n | f, 4 | n; and only those */
+    /* In that case, Graeffe(Phi_n) = Phi_{n/2}^2, and Phi_n = Phi_{n/2}(x^2) */
+    GEN fs = RgX_inflate(fs2, 2);
+    (void)ZX_gcd_all(f, fs, &f); /* remove those Phi_n | f, 4 | n */
+    Gs = BD(fs2);
+    if (Gs)
+    {
+      long i;
+      for (i = lg(Gs)-1; i; i--) gel(Gs,i) = RgX_inflate(gel(Gs,i), 2);
+      /* prod Gs[i] is the product of all Phi_n | f, 4 | n */
+      G = myconcat(G, Gs);
+    }
+    /* f2 = f1 / fs2 */
+    f1 = RgX_div(f2, fs2); /* f1 / fs2^2 */
+  }
+  fp = ZX_gcd(f, f1); /* contains all Phi_n | f, n > 1 odd; and only those */
+  if (degpol(fp))
+  {
+    Gp = BD_odd(fp);
+    /* Gp is the product of all Phi_n | f, n odd */
+    if (Gp) G = myconcat(G, Gp);
+    f = RgX_div(f, fp);
+  }
+  if (degpol(f))
+  { /* contains all Phi_n originally dividing f, n = 2 mod 4, n > 2;
+     * and only those
+     * In that case, Graeffe(Phi_n) = Phi_{n/2}, and Phi_n = Phi_{n/2}(-x) */
+    Gi = BD_odd(ZX_unscale(f, gen_m1));
+    if (Gi)
+    { /* N.B. Phi_2 does not divide f */
+      Gi = ZX_unscale(Gi, gen_m1);
+      /* Gi is the product of all Phi_n | f, n = 2 mod 4 */
+      G = myconcat(G, Gi);
+    }
+  }
+  return G;
+}
+
+/* Let f be a non-zero QX, return the (squarefree) product of cyclotomic
+ * divisors of f */
+GEN
+polcyclofactors(GEN f)
+{
+  pari_sp av = avma;
+  if (typ(f) != t_POL) pari_err_TYPE("polcyclofactors",f);
+  (void)RgX_valrem(f, &f);
+  f = Q_primpart(f);
+  if (!RgX_is_ZX(f) || !signe(f)) pari_err_TYPE("polcyclofactors",f);
+  if (degpol(f))
+  {
+    (void)ZX_gcd_all(f, ZX_deriv(f), &f);
+    f = BD(f);
+    if (f) return gerepilecopy(av, f);
+  }
+  avma = av; return cgetg(1,t_VEC);
+}
+
+/* return t*x mod T(x), T a monic ZX. Assume deg(t) < deg(T) */
+static GEN
+ZXQ_mul_by_X(GEN t, GEN T)
+{
+  GEN lt;
+  t = RgX_shift_shallow(t, 1);
+  if (degpol(t) < degpol(T)) return t;
+  lt = leading_term(t);
+  if (is_pm1(lt)) return signe(lt) > 0 ? ZX_sub(t, T): ZX_add(t, T);
+  return ZX_sub(t, ZX_Z_mul(T, leading_term(t)));
+}
+/* f a product of Phi_n, all n odd; deg f > 1. Is it irreducible ? */
+static long
+BD_odd_iscyclo(GEN f)
+{
+  pari_sp av, lim;
+  long d, e, n, bound;
+  GEN t;
+  f = RgX_deflate_max(f, &e);
+  av = avma; lim = stack_lim(av,1);
+  /* The original f is cyclotomic (= Phi_{ne}) iff the present one is Phi_n,
+   * where all prime dividing e also divide n. If current f is Phi_n,
+   * then n is odd and squarefree */
+  d = degpol(f); /* = phi(n) */
+  /* Let e > 0, g multiplicative such that
+       g(p) = p / (p-1)^(1+e) < 1 iff p < (p-1)^(1+e)
+     For all squarefree odd n, we have g(n) < C, hence n < C phi(n)^(1+e), where
+       C = \prod_{p odd | p > (p-1)^(1+e)} g(p)
+     For e = 1/10,   we obtain p = 3, 5 and C < 1.523
+     For e = 1/100,  we obtain p = 3, 5, ..., 29 and C < 2.573
+     In fact, for n <= 10^7 odd & squarefree, we have n < 2.92 * phi(n)
+     By the above, n<10^7 covers all d <= (10^7/2.573)^(1/(1+1/100)) < 3344391.
+  */
+  if (d <= 3344391)
+    bound = (long)(2.92 * d);
+  else
+    bound = (long)(2.573 * pow(d,1.01));
+  /* IF f = Phi_n, n squarefree odd, then n <= bound */
+  t = monomial(gen_1, d-1, varn(f));
+  for (n = d; n <= bound; n++)
+  {
+    t = ZXQ_mul_by_X(t, f);
+    /* t = (X mod f(X))^d */
+    if (degpol(t) == 0) break;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"BD_odd_iscyclo");
+      t = gerepilecopy(av, t);
+    }
+  }
+  if (n > bound || eulerphiu(n) != (ulong)d) return 0;
+
+  if (e > 1) return (ucoprime_part(e, n) == 1)? e * n : 0;
+  return n;
+}
+
+/* Checks if f, monic squarefree ZX with |constant coeff| = 1, is a cyclotomic
+ * polynomial. Returns n if f = Phi_n, and 0 otherwise */
+static long
+BD_iscyclo(GEN f)
+{
+  pari_sp av = avma;
+  GEN f2, fn, f1;
+
+  if (degpol(f) == 1) return isint1(gel(f,2))? 2: 1;
+  f1 = ZX_graeffe(f);
+  /* f = product of Phi_n, n odd */
+  if (ZX_equal(f, f1)) { avma = av; return BD_odd_iscyclo(f); }
+
+  fn = ZX_unscale(f, gen_m1); /* f(-x) */
+  /* f = product of Phi_n, n = 2 mod 4 */
+  if (ZX_equal(f1, fn)) return 2*BD_odd_iscyclo(fn);
+
+  if (issquareall(f1, &f2))
+  {
+    GEN lt = leading_term(f2);
+    long c;
+    if (signe(lt) < 0) f2 = ZX_neg(f2);
+    c = BD_iscyclo(f2);
+    return odd(c)? 0: 2*c;
+  }
+  avma = av; return 0;
+}
+long
+poliscyclo(GEN f)
+{
+  pari_sp av = avma;
+  long d = degpol(f);
+  if (typ(f) != t_POL) pari_err_TYPE("poliscyclo", f);
+  if (d <= 0 || !RgX_is_ZX(f)) return 0;
+  if (!equali1(gel(f,d+2)) || !is_pm1(gel(f,2))) return 0;
+  if (d == 1) return signe(gel(f,2)) > 0? 2: 1;
+  if (degpol(ZX_gcd(f, ZX_deriv(f)))) { avma = av; return 0; }
+  return BD_iscyclo(f);
+}
+
+long
+poliscycloprod(GEN f)
+{
+  pari_sp av = avma;
+  long i, d = degpol(f);
+  if (typ(f) != t_POL) pari_err_TYPE("poliscycloprod",f);
+  if (!RgX_is_ZX(f)) return 0;
+  if (!equali1(leading_term(f)) || !is_pm1(constant_term(f))) return 0;
+  if (d < 2) return (d == 1);
+  if ( degpol(ZX_gcd_all(f, ZX_deriv(f), &f)) )
+  {
+    d = degpol(f);
+    if (d == 1) return 1;
+  }
+  f = BD(f); if (!f) return 0;
+  for (i = lg(f)-1; i; i--) d -= degpol(gel(f,i));
+  avma = av; return d == 0;
+}
diff --git a/src/basemath/Qfb.c b/src/basemath/Qfb.c
new file mode 100644
index 0000000..5eb0d0c
--- /dev/null
+++ b/src/basemath/Qfb.c
@@ -0,0 +1,1474 @@
+/* Copyright (C) 2000-2005  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+/*******************************************************************/
+/*                                                                 */
+/*         QUADRATIC POLYNOMIAL ASSOCIATED TO A DISCRIMINANT       */
+/*                                                                 */
+/*******************************************************************/
+
+void
+check_quaddisc(GEN x, long *s, long *r, const char *f)
+{
+  if (typ(x) != t_INT) pari_err_TYPE(f,x);
+  *s = signe(x);
+  if (Z_issquare(x)) pari_err_DOMAIN(f,"issquare(disc)","=", gen_1,x);
+  *r = mod4(x); if (*s < 0 && *r) *r = 4 - *r;
+  if (*r > 1) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);
+}
+void
+check_quaddisc_real(GEN x, long *r, const char *f)
+{
+  long sx; check_quaddisc(x, &sx, r, f);
+  if (sx < 0) pari_err_DOMAIN(f, "disc","<",gen_0,x);
+}
+void
+check_quaddisc_imag(GEN x, long *r, const char *f)
+{
+  long sx; check_quaddisc(x, &sx, r, f);
+  if (sx > 0) pari_err_DOMAIN(f, "disc",">",gen_0,x);
+}
+
+/* X^2 + b X + c is the canonical quadratic t_POL of discriminant D.
+ * Dodd is non-zero iff D is odd */
+static void
+quadpoly_bc(GEN D, long Dodd, GEN *b, GEN *c)
+{
+  if (Dodd)
+  {
+    pari_sp av = avma;
+    *b = gen_m1;
+    *c = gerepileuptoint(av, shifti(subui(1,D), -2));
+  }
+  else
+  {
+    *b = gen_0;
+    *c = shifti(D,-2); togglesign(*c);
+  }
+}
+/* X^2 - X - (D-1)/4 or X^2 - D/4 */
+GEN
+quadpoly(GEN D)
+{
+  long Dmod4, s;
+  GEN b, c, y = cgetg(5,t_POL);
+  check_quaddisc(D, &s, &Dmod4, "quadpoly");
+  y[1] = evalsigne(1) | evalvarn(0);
+  quadpoly_bc(D, Dmod4, &b,&c);
+  gel(y,2) = c;
+  gel(y,3) = b;
+  gel(y,4) = gen_1; return y;
+}
+
+GEN
+quadpoly0(GEN x, long v)
+{
+  GEN T = quadpoly(x);
+  if (v > 0) setvarn(T, v);
+  return T;
+}
+
+GEN
+quadgen(GEN x)
+{ retmkquad(quadpoly(x), gen_0, gen_1); }
+
+/***********************************************************************/
+/**                                                                   **/
+/**                      BINARY QUADRATIC FORMS                       **/
+/**                                                                   **/
+/***********************************************************************/
+GEN
+qfi(GEN x, GEN y, GEN z)
+{
+  if (signe(x) < 0) pari_err_IMPL("negative definite t_QFI");
+  retmkqfi(icopy(x),icopy(y),icopy(z));
+}
+GEN
+qfr(GEN x, GEN y, GEN z, GEN d)
+{
+  if (typ(d) != t_REAL) pari_err_TYPE("qfr",d);
+  retmkqfr(icopy(x),icopy(y),icopy(z),rcopy(d));
+}
+
+GEN
+Qfb0(GEN x, GEN y, GEN z, GEN d, long prec)
+{
+  pari_sp av = avma;
+  GEN D;
+  long s, r;
+  if (typ(x)!=t_INT) pari_err_TYPE("Qfb",x);
+  if (typ(y)!=t_INT) pari_err_TYPE("Qfb",y);
+  if (typ(z)!=t_INT) pari_err_TYPE("Qfb",z);
+  D = qfb_disc3(x,y,z);
+  check_quaddisc(D, &s, &r, "Qfb");
+  avma = av;
+  if (s < 0) return qfi(x, y, z);
+
+  d = d? gtofp(d,prec): real_0(prec);
+  return qfr(x,y,z,d);
+}
+
+/* composition */
+static void
+qfb_sqr(GEN z, GEN x)
+{
+  GEN c, d1, x2, v1, v2, c3, m, p1, r;
+
+  d1 = bezout(gel(x,2),gel(x,1),&x2, NULL); /* usually 1 */
+  c = gel(x,3);
+  m = mulii(c,x2);
+  if (is_pm1(d1))
+    v1 = v2 = gel(x,1);
+  else
+  {
+    v1 = diviiexact(gel(x,1),d1);
+    v2 = mulii(v1, gcdii(d1,c)); /* = v1 iff x primitive */
+    c = mulii(c, d1);
+  }
+  togglesign(m);
+  r = modii(m,v2);
+  p1 = mulii(r, v1);
+  c3 = addii(c, mulii(r,addii(gel(x,2),p1)));
+  gel(z,1) = mulii(v1,v2);
+  gel(z,2) = addii(gel(x,2), shifti(p1,1));
+  gel(z,3) = diviiexact(c3,v2);
+}
+/* z <- x * y */
+static void
+qfb_comp(GEN z, GEN x, GEN y)
+{
+  GEN n, c, d, y1, v1, v2, c3, m, p1, r;
+
+  if (x == y) { qfb_sqr(z,x); return; }
+  n = shifti(subii(gel(y,2),gel(x,2)), -1);
+  v1 = gel(x,1);
+  v2 = gel(y,1);
+  c  = gel(y,3);
+  d = bezout(v2,v1,&y1,NULL);
+  if (is_pm1(d))
+    m = mulii(y1,n);
+  else
+  {
+    GEN s = subii(gel(y,2), n);
+    GEN x2, y2, d1 = bezout(s,d,&x2,&y2); /* x2 s + y2 (x1 v1 + y1 v2) = d1 */
+    if (!is_pm1(d1))
+    {
+      v1 = diviiexact(v1,d1);
+      v2 = diviiexact(v2,d1); /* gcd = 1 iff x or y primitive */
+      v1 = mulii(v1, gcdii(c,gcdii(gel(x,3),gcdii(d1,n))));
+      c = mulii(c, d1);
+    }
+    m = addii(mulii(mulii(y1,y2),n), mulii(gel(y,3),x2));
+  }
+  togglesign(m);
+  r = modii(m, v1);
+  p1 = mulii(r, v2);
+  c3 = addii(c, mulii(r,addii(gel(y,2),p1)));
+  gel(z,1) = mulii(v1,v2);
+  gel(z,2) = addii(gel(y,2), shifti(p1,1));
+  gel(z,3) = diviiexact(c3,v1);
+}
+
+static GEN redimag_av(pari_sp av, GEN q);
+static GEN
+qficomp0(GEN x, GEN y, int raw)
+{
+  pari_sp av = avma;
+  GEN z = cgetg(4,t_QFI);
+  qfb_comp(z, x,y);
+  if (raw) return gerepilecopy(av,z);
+  return redimag_av(av, z);
+}
+static GEN
+qfrcomp0(GEN x, GEN y, int raw)
+{
+  pari_sp av = avma;
+  GEN z = cgetg(5,t_QFR);
+  qfb_comp(z, x,y); gel(z,4) = addrr(gel(x,4),gel(y,4));
+  if (raw) return gerepilecopy(av,z);
+  return gerepileupto(av, redreal(z));
+}
+GEN
+qfrcomp(GEN x, GEN y) { return qfrcomp0(x,y,0); }
+GEN
+qfrcompraw(GEN x, GEN y) { return qfrcomp0(x,y,1); }
+GEN
+qficomp(GEN x, GEN y) { return qficomp0(x,y,0); }
+GEN
+qficompraw(GEN x, GEN y) { return qficomp0(x,y,1); }
+GEN
+qfbcompraw(GEN x, GEN y)
+{
+  long tx = typ(x);
+  if (typ(y) != tx) pari_err_TYPE2("*",x,y);
+  switch(tx) {
+    case t_QFI: return qficompraw(x,y);
+    case t_QFR: return qfrcompraw(x,y);
+  }
+  pari_err_TYPE("composition",x);
+  return NULL; /* not reached */
+}
+
+static GEN
+qfisqr0(GEN x, long raw)
+{
+  pari_sp av = avma;
+  GEN z = cgetg(4,t_QFI);
+
+  if (typ(x)!=t_QFI) pari_err_TYPE("composition",x);
+  qfb_sqr(z,x);
+  if (raw) return gerepilecopy(av,z);
+  return redimag_av(av, z);
+}
+static GEN
+qfrsqr0(GEN x, long raw)
+{
+  pari_sp av = avma;
+  GEN z = cgetg(5,t_QFR);
+
+  if (typ(x)!=t_QFR) pari_err_TYPE("composition",x);
+  qfb_sqr(z,x); gel(z,4) = shiftr(gel(x,4),1);
+  if (raw) return gerepilecopy(av,z);
+  return gerepileupto(av, redreal(z));
+}
+GEN
+qfrsqr(GEN x) { return qfrsqr0(x,0); }
+GEN
+qfrsqrraw(GEN x) { return qfrsqr0(x,1); }
+GEN
+qfisqr(GEN x) { return qfisqr0(x,0); }
+GEN
+qfisqrraw(GEN x) { return qfisqr0(x,1); }
+
+static GEN
+qfr_1_by_disc(GEN D, long prec)
+{
+  GEN y = cgetg(5,t_QFR), isqrtD;
+  pari_sp av = avma;
+  long r;
+
+  check_quaddisc_real(D, &r, "qfr_1_by_disc");
+  gel(y,1) = gen_1; isqrtD = sqrti(D);
+  if ((r & 1) != mod2(isqrtD)) /* we know isqrtD > 0 */
+    isqrtD = gerepileuptoint(av, addsi(-1,isqrtD));
+  gel(y,2) = isqrtD; av = avma;
+  gel(y,3) = gerepileuptoint(av, shifti(subii(sqri(isqrtD), D),-2));
+  gel(y,4) = real_0(prec); return y;
+}
+GEN
+qfr_1(GEN x)
+{
+  if (typ(x) != t_QFR) pari_err_TYPE("qfr_1",x);
+  return qfr_1_by_disc(qfb_disc(x), precision(gel(x,4)));
+}
+
+static void
+qfr_1_fill(GEN y, struct qfr_data *S)
+{
+  pari_sp av = avma;
+  GEN y2 = S->isqrtD;
+  gel(y,1) = gen_1;
+  if (mod2(S->D) != mod2(y2)) y2 = addsi(-1,y2);
+  gel(y,2) = y2; av = avma;
+  gel(y,3) = gerepileuptoint(av, shifti(subii(sqri(y2), S->D),-2));
+}
+static GEN
+qfr5_1(struct qfr_data *S, long prec)
+{
+  GEN y = cgetg(6, t_VEC);
+  qfr_1_fill(y, S);
+  gel(y,4) = gen_0;
+  gel(y,5) = real_1(prec); return y;
+}
+static GEN
+qfr3_1(struct qfr_data *S)
+{
+  GEN y = cgetg(4, t_VEC);
+  qfr_1_fill(y, S); return y;
+}
+
+/* Assume D < 0 is the discriminant of a t_QFI */
+static GEN
+qfi_1_by_disc(GEN D)
+{
+  GEN b,c, y = cgetg(4,t_QFI);
+  quadpoly_bc(D, mod2(D), &b,&c);
+  gel(y,1) = gen_1;
+  gel(y,2) = b;
+  gel(y,3) = c; return y;
+}
+GEN
+qfi_1(GEN x)
+{
+  if (typ(x) != t_QFI) pari_err_TYPE("qfi_1",x);
+  return qfi_1_by_disc(qfb_disc(x));
+}
+
+static GEN
+invraw(GEN x)
+{
+  GEN y = gcopy(x);
+  if (typ(y) == t_QFR) togglesign(gel(y,4));
+  togglesign(gel(y,2)); return y;
+}
+GEN
+qfrpowraw(GEN x, long n)
+{
+  pari_sp av = avma;
+  long m;
+  GEN y;
+
+  if (typ(x) != t_QFR) pari_err_TYPE("qfrpowraw",x);
+  if (!n) return qfr_1(x);
+  if (n== 1) return gcopy(x);
+  if (n==-1) return invraw(x);
+
+  y = NULL; m = labs(n);
+  for (; m>1; m>>=1)
+  {
+    if (m&1) y = y? qfrcompraw(y,x): x;
+    x = qfrsqrraw(x);
+  }
+  y = y? qfrcompraw(y,x): x;
+  if (n < 0) y = invraw(y);
+  return gerepileupto(av,y);
+}
+GEN
+qfipowraw(GEN x, long n)
+{
+  pari_sp av = avma;
+  long m;
+  GEN y;
+
+  if (typ(x) != t_QFI) pari_err_TYPE("qfipow",x);
+  if (!n) return qfi_1(x);
+  if (n== 1) return gcopy(x);
+  if (n==-1) return invraw(x);
+
+  y = NULL; m = labs(n);
+  for (; m>1; m>>=1)
+  {
+    if (m&1) y = y? qficompraw(y,x): x;
+    x = qfisqrraw(x);
+  }
+  y = y? qficompraw(y,x): x;
+  if (n < 0) y = invraw(y);
+  return gerepileupto(av,y);
+}
+
+GEN
+qfbpowraw(GEN x, long n)
+{ return (typ(x)==t_QFI)? qfipowraw(x,n): qfrpowraw(x,n); }
+
+static long
+parteucl(GEN L, GEN *d, GEN *v3, GEN *v, GEN *v2)
+{
+  long z;
+  *v = gen_0; *v2 = gen_1;
+  for (z=0; absi_cmp(*v3,L) > 0; z++)
+  {
+    GEN t3, t2 = subii(*v, mulii(truedvmdii(*d,*v3,&t3),*v2));
+    *v = *v2; *d = *v3; *v2 = t2; *v3 = t3;
+  }
+  return z;
+}
+
+/* composition: Shanks' NUCOMP & NUDUPL */
+/* L = floor((|d|/4)^(1/4)) */
+GEN
+nucomp(GEN x, GEN y, GEN L)
+{
+  pari_sp av = avma;
+  long z;
+  GEN a, a1, a2, b2, b, d, d1, g, n, p1, q1, q2, s, u, u1, v, v2, v3, Q;
+
+  if (x==y) return nudupl(x,L);
+  if (typ(x) != t_QFI) pari_err_TYPE("nucomp",x);
+  if (typ(y) != t_QFI) pari_err_TYPE("nucomp",y);
+
+  if (absi_cmp(gel(x,1),gel(y,1)) < 0) swap(x, y);
+  s = shifti(addii(gel(x,2),gel(y,2)), -1);
+  n = subii(gel(y,2), s);
+  a1 = gel(x,1);
+  a2 = gel(y,1); d = bezout(a2,a1,&u,&v);
+  if (is_pm1(d)) { a = negi(mulii(u,n)); d1 = d; }
+  else
+    if (remii(s,d) == gen_0) /* d | s */
+    {
+      a = negi(mulii(u,n)); d1 = d;
+      a1 = diviiexact(a1, d1);
+      a2 = diviiexact(a2, d1);
+      s = diviiexact(s, d1);
+    }
+    else
+    {
+      GEN p2, l;
+      d1 = bezout(s,d,&u1,NULL);
+      if (!is_pm1(d1))
+      {
+        a1 = diviiexact(a1,d1);
+        a2 = diviiexact(a2,d1);
+        s = diviiexact(s,d1);
+        d = diviiexact(d,d1);
+      }
+      p1 = remii(gel(x,3),d);
+      p2 = remii(gel(y,3),d);
+      l = modii(mulii(negi(u1), addii(mulii(u,p1),mulii(v,p2))), d);
+      a = subii(mulii(l,diviiexact(a1,d)), mulii(u,diviiexact(n,d)));
+    }
+  a = modii(a,a1); p1 = subii(a,a1); if (absi_cmp(a,p1) > 0) a = p1;
+  d = a1; v3 = a; z = parteucl(L, &d,&v3, &v,&v2);
+  Q = cgetg(4,t_QFI);
+  if (!z) {
+    g = diviiexact(addii(mulii(v3,s),gel(y,3)), d);
+    b = a2;
+    b2 = gel(y,2);
+    v2 = d1;
+    gel(Q,1) = mulii(d,b);
+  } else {
+    GEN e, q3, q4;
+    if (z&1) { v3 = negi(v3); v2 = negi(v2); }
+    b = diviiexact(addii(mulii(a2,d), mulii(n,v)), a1);
+    e = diviiexact(addii(mulii(s,d),mulii(gel(y,3),v)), a1);
+    q3 = mulii(e,v2);
+    q4 = subii(q3,s);
+    b2 = addii(q3,q4);
+    g = diviiexact(q4,v);
+    if (!is_pm1(d1)) { v2 = mulii(d1,v2); v = mulii(d1,v); b2 = mulii(d1,b2); }
+    gel(Q,1) = addii(mulii(d,b), mulii(e,v));
+  }
+  q1 = mulii(b, v3);
+  q2 = addii(q1,n);
+  gel(Q,2) = addii(b2, z? addii(q1,q2): shifti(q1, 1));
+  gel(Q,3) = addii(mulii(v3,diviiexact(q2,d)), mulii(g,v2));
+  return redimag_av(av, Q);
+}
+
+GEN
+nudupl(GEN x, GEN L)
+{
+  pari_sp av = avma;
+  long z;
+  GEN u, v, d, d1, p1, a, b, c, a2, b2, c2, Q, v2, v3, g;
+
+  if (typ(x) != t_QFI) pari_err_TYPE("nudupl",x);
+  a = gel(x,1);
+  b = gel(x,2);
+  d1 = bezout(b,a, &u,NULL);
+  if (!is_pm1(d1))
+  {
+    a = diviiexact(a, d1);
+    b = diviiexact(b, d1);
+  }
+  c = modii(negi(mulii(u,gel(x,3))), a);
+  p1 = subii(c,a); if (absi_cmp(c,p1) > 0) c = p1;
+  d = a; v3 = c; z = parteucl(L, &d,&v3, &v,&v2);
+  a2 = sqri(d);
+  c2 = sqri(v3);
+  Q = cgetg(4,t_QFI);
+  if (!z) {
+    g = diviiexact(addii(mulii(v3,b),gel(x,3)), d);
+    b2 = gel(x,2);
+    v2 = d1;
+    gel(Q,1) = a2;
+  } else {
+    GEN e;
+    if (z&1) { v = negi(v); d = negi(d); }
+    e = diviiexact(addii(mulii(gel(x,3),v), mulii(b,d)), a);
+    g = diviiexact(subii(mulii(e,v2), b), v);
+    b2 = addii(mulii(e,v2), mulii(v,g));
+    if (!is_pm1(d1)) { b2 = mulii(d1,b2); v = mulii(d1,v); v2 = mulii(d1,v2); }
+    gel(Q,1) = addii(a2, mulii(e,v));
+  }
+  gel(Q,2) = addii(b2, subii(sqri(addii(d,v3)), addii(a2,c2)));
+  gel(Q,3) = addii(c2, mulii(g,v2));
+  return redimag_av(av, Q);
+}
+
+static GEN
+mul_nucomp(void *l, GEN x, GEN y) { return nucomp(x, y, (GEN)l); }
+static GEN
+mul_nudupl(void *l, GEN x) { return nudupl(x, (GEN)l); }
+GEN
+nupow(GEN x, GEN n)
+{
+  pari_sp av;
+  GEN y, D;
+
+  if (typ(n) != t_INT) pari_err_TYPE("nupow",n);
+  if (typ(x) != t_QFI) pari_err_TYPE("nupow",x);
+  if (gequal1(n)) return gcopy(x);
+  av = avma;
+  D = qfb_disc(x);
+  y = qfi_1_by_disc(D);
+  if (!signe(n)) return y;
+  y = gen_pow(x, n, (void*)sqrtnint(absi(D), 4), &mul_nudupl, &mul_nucomp);
+  if (signe(n) < 0
+  && !absi_equal(gel(y,1),gel(y,2))
+  && !absi_equal(gel(y,1),gel(y,3))) togglesign(gel(y,2));
+  return gerepileupto(av, y);
+}
+
+/* Reduction */
+
+/* assume a > 0. Write b = q*2a + r, with -a < r <= a */
+static GEN
+dvmdii_round(GEN b, GEN a, GEN *r)
+{
+  GEN a2 = shifti(a, 1), q = dvmdii(b, a2, r);
+  if (signe(b) >= 0) {
+    if (absi_cmp(*r, a) > 0) { q = addis(q,  1); *r = subii(*r, a2); }
+  } else { /* r <= 0 */
+    if (absi_cmp(*r, a) >= 0){ q = addis(q, -1); *r = addii(*r, a2); }
+  }
+  return q;
+}
+/* Assume 0 < a <= LONG_MAX. Ensure no overflow */
+static long
+dvmdsu_round(long b, ulong a, long *r)
+{
+  ulong a2 = a << 1, q, ub, ur;
+  if (b >= 0) {
+    ub = b;
+    q = ub / a2;
+    ur = ub % a2;
+    if (ur > a) { ur -= a; q++; *r = (long)ur; *r -= (long)a; }
+    else *r = (long)ur;
+    return (long)q;
+  } else { /* r <= 0 */
+    ub = (ulong)-b; /* |b| */
+    q = ub / a2;
+    ur = ub % a2;
+    if (ur >= a) { ur -= a; q++; *r = (long)ur; *r = (long)a - *r; }
+    else *r = -(long)ur;
+    return -(long)q;
+  }
+}
+/* reduce b mod 2*a. Update b,c */
+static void
+REDB(GEN a, GEN *b, GEN *c)
+{
+  GEN r, q = dvmdii_round(*b, a, &r);
+  if (!signe(q)) return;
+  *c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));
+  *b = r;
+}
+/* Assume a > 0. Reduce b mod 2*a. Update b,c */
+static void
+sREDB(ulong a, long *b, ulong *c)
+{
+  long r, q;
+  ulong uz;
+  if (a > LONG_MAX) return; /* b already reduced */
+  q = dvmdsu_round(*b, a, &r);
+  if (q == 0) return;
+  /* Final (a,r,c2) satisfies |r| <= |b| hence c2 <= c, c2 = c - q*z,
+   * where z = (b+r) / 2, representable as long, has the same sign as q. */
+  if (*b < 0)
+  { /* uz = -z >= 0, q < 0 */
+    if (r >= 0) /* different signs=>no overflow, exact division */
+      uz = (ulong)-((*b + r)>>1);
+    else
+    {
+      ulong ub = (ulong)-*b, ur = (ulong)-r;
+      uz = (ub + ur) >> 1;
+    }
+    *c -= (-q) * uz; /* c -= qz */
+  }
+  else
+  { /* uz = z >= 0, q > 0 */
+    if (r <= 0)
+      uz = (*b + r)>>1;
+    else
+    {
+      ulong ub = (ulong)*b, ur = (ulong)r;
+      uz = ((ub + ur) >> 1);
+    }
+    *c -= q * uz; /* c -= qz */
+  }
+  *b = r;
+}
+static void
+REDBU(GEN a, GEN *b, GEN *c, GEN u1, GEN *u2)
+{ /* REDB(a,b,c) */
+  GEN r, q = dvmdii_round(*b, a, &r);
+  *c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));
+  *b = r;
+  *u2 = subii(*u2, mulii(q, u1));
+}
+
+/* q t_QFI, return reduced representative and set base change U in Sl2(Z) */
+GEN
+redimagsl2(GEN q, GEN *U)
+{
+  GEN Q = cgetg(4, t_QFI);
+  pari_sp av = avma, av2, lim = stack_lim(av, 1);
+  GEN z, u1,u2,v1,v2, a = gel(q,1), b = gel(q,2), c = gel(q,3);
+  long cmp;
+  /* upper bound for size of final (a,b,c) */
+  (void)new_chunk(2*(lgefint(a) + lgefint(b) + lgefint(c) + 3));
+  av2 = avma;
+  u1 = gen_1; u2 = gen_0;
+  cmp = absi_cmp(a, b);
+  if (cmp < 0)
+    REDBU(a,&b,&c, u1,&u2);
+  else if (cmp == 0 && signe(b) < 0)
+  { /* b = -a */
+    b = negi(b);
+    u2 = gen_1;
+  }
+  for(;;)
+  {
+    cmp = absi_cmp(a, c); if (cmp <= 0) break;
+    swap(a,c); b = negi(b);
+    z = u1; u1 = u2; u2 = negi(z);
+    REDBU(a,&b,&c, u1,&u2);
+    if (low_stack(lim, stack_lim(av, 1))) {
+      if (DEBUGMEM>1) pari_warn(warnmem, "redimagsl2");
+      gerepileall(av2, 5, &a,&b,&c, &u1,&u2);
+    }
+  }
+  if (cmp == 0 && signe(b) < 0)
+  {
+    b = negi(b);
+    z = u1; u1 = u2; u2 = negi(z);
+  }
+  avma = av;
+  a = icopy(a); gel(Q,1) = a;
+  b = icopy(b); gel(Q,2) = b;
+  c = icopy(c); gel(Q,3) = c;
+  u1 = icopy(u1);
+  u2 = icopy(u2); av = avma;
+
+  /* Let q = (A,B,C). q o [u1,u2; v1,v2] = Q implies
+   * [v1,v2] = (1/C) [(b-B)/2 u1 - a u2, c u1 - (b+B)/2 u2] */
+  z = shifti(subii(b, gel(q,2)), -1);
+  v1 = subii(mulii(z, u1), mulii(a, u2)); v1 = diviiexact(v1, gel(q,3));
+  z = subii(z, b);
+  v2 = addii(mulii(z, u2), mulii(c, u1)); v2 = diviiexact(v2, gel(q,3));
+  avma = av;
+  v1 = icopy(v1);
+  v2 = icopy(v2);
+  *U = mkmat2(mkcol2(u1,v1), mkcol2(u2,v2)); return Q;
+}
+
+static GEN
+setq_b0(ulong a, ulong c)
+{ retmkqfi( utoipos(a), gen_0, utoipos(c) ); }
+/* assume |sb| = 1 */
+static GEN
+setq(ulong a, ulong b, ulong c, long sb)
+{ retmkqfi( utoipos(a), sb == 1? utoipos(b): utoineg(b), utoipos(c) ); }
+/* 0 < a, c < 2^BIL, b = 0 */
+static GEN
+redimag_1_b0(ulong a, ulong c)
+{ return (a <= c)? setq_b0(a, c): setq_b0(c, a); }
+
+/* 0 < a, c < 2^BIL: single word affair */
+static GEN
+redimag_1(pari_sp av, GEN a, GEN b, GEN c)
+{
+  ulong ua, ub, uc;
+  long sb;
+  for(;;)
+  { /* at most twice */
+    long lb = lgefint(b); /* <= 3 after first loop */
+    if (lb == 2) return redimag_1_b0(a[2],c[2]);
+    if (lb == 3 && (ulong)b[2] <= (ulong)LONG_MAX) break;
+    REDB(a,&b,&c);
+    if ((ulong)a[2] <= (ulong)c[2])
+    { /* lg(b) <= 3 but may be too large for itos */
+      long s = signe(b);
+      avma = av;
+      if (!s) return redimag_1_b0(a[2], c[2]);
+      if (a[2] == c[2]) s = 1;
+      return setq(a[2], b[2], c[2], s);
+    }
+    swap(a,c); b = negi(b);
+  }
+  /* b != 0 */
+  avma = av;
+  ua = a[2];
+  ub = sb = b[2]; if (signe(b) < 0) sb = -sb;
+  uc = c[2];
+  if (ua < ub)
+    sREDB(ua, &sb, &uc);
+  else if (ua == ub && sb < 0) sb = (long)ub;
+  while(ua > uc)
+  {
+    lswap(ua,uc); sb = -sb;
+    sREDB(ua, &sb, &uc);
+  }
+  if (!sb) return setq_b0(ua, uc);
+  else
+  {
+    long s = 1;
+    if (sb < 0)
+    {
+      sb = -sb;
+      if (ua != uc) s = -1;
+    }
+    return setq(ua, sb, uc, s);
+  }
+}
+
+static GEN
+redimag_av(pari_sp av, GEN q)
+{
+  GEN a = gel(q,1), b = gel(q,2), c = gel(q,3);
+  long cmp, lc = lgefint(c);
+
+  if (lgefint(a) == 3 && lc == 3) return redimag_1(av, a, b, c);
+  cmp = absi_cmp(a, b);
+  if (cmp < 0)
+    REDB(a,&b,&c);
+  else if (cmp == 0 && signe(b) < 0)
+    b = negi(b);
+  for(;;)
+  {
+    cmp = absi_cmp(a, c); if (cmp <= 0) break;
+    lc = lgefint(a); /* lg(future c): we swap a & c next */
+    if (lc == 3) return redimag_1(av, a, b, c);
+    swap(a,c); b = negi(b); /* apply rho */
+    REDB(a,&b,&c);
+  }
+  if (cmp == 0 && signe(b) < 0) b = negi(b);
+  /* size of reduced Qfb(a,b,c) <= 3 lg(c) + 4 <= 4 lg(c) */
+  (void)new_chunk(lc<<2);
+  a = icopy(a); b = icopy(b); c = icopy(c);
+  avma = av;
+  retmkqfi(icopy(a), icopy(b), icopy(c));
+}
+GEN
+redimag(GEN q) { return redimag_av(avma, q); }
+
+static GEN
+rhoimag(GEN x)
+{
+  GEN a = gel(x,1), b = gel(x,2), c = gel(x,3);
+  int fl = absi_cmp(a, c);
+  if (fl <= 0) {
+    int fg = absi_cmp(a, b);
+    if (fg >= 0) {
+      x = qfi(a,b,c);
+      if ((!fl || !fg) && signe(gel(x,2)) < 0) setsigne(gel(x,2), 1);
+      return x;
+    }
+  }
+  x = cgetg(4, t_QFI);
+  (void)new_chunk(lgefint(a) + lgefint(b) + lgefint(c) + 3);
+  swap(a,c); b = negi(b);
+  REDB(a, &b, &c); avma = (pari_sp)x;
+  gel(x,1) = icopy(a);
+  gel(x,2) = icopy(b);
+  gel(x,3) = icopy(c); return x;
+}
+
+/* qfr3 / qfr5 */
+
+/* t_QFR are unusable: D, sqrtD, isqrtD are recomputed all the time and the
+ * logarithmic Shanks's distance is costly and hard to control.
+ * qfr3 / qfr5 routines take a container of t_INTs (e.g a t_VEC) as argument,
+ * at least 3 (resp. 5) components [it is a feature that they do not check the
+ * precise type or length of the input]. They return a vector of length 3
+ * (resp. 5). A qfr3 [a,b,c] contains the form coeffs, in a qfr5 [a,b,c, e,d]
+ * the t_INT e is a binary exponent, d a t_REAL, coding the distance in
+ * multiplicative form: the true distance is obtained from qfr5_dist.
+ * All other qfr routines are obsolete (inefficient) wrappers */
+
+/* static functions are not stack-clean. Unless mentionned otherwise, public
+ * functions are. */
+
+#define EMAX 22
+static void
+fix_expo(GEN x)
+{
+  if (expo(gel(x,5)) >= (1L << EMAX)) {
+    gel(x,4) = addsi(1, gel(x,4));
+    shiftr_inplace(gel(x, 5), - (1L << EMAX));
+  }
+}
+
+/* (1/2) log (d * 2^{e * 2^EMAX}). Not stack clean if e != 0 */
+GEN
+qfr5_dist(GEN e, GEN d, long prec)
+{
+  GEN t = logr_abs(d);
+  if (signe(e)) {
+    GEN u = mulir(e, mplog2(prec));
+    shiftr_inplace(u, EMAX); t = addrr(t, u);
+  }
+  shiftr_inplace(t, -1); return t;
+}
+
+static void
+rho_get_BC(GEN *B, GEN *C, GEN b, GEN c, struct qfr_data *S)
+{
+  GEN t, u;
+  u = shifti(c,1);
+  t = (absi_cmp(S->isqrtD,c) >= 0)? S->isqrtD: c;
+  u = remii(addii_sign(t,1, b,signe(b)), u);
+  *B = addii_sign(t, 1, u, -signe(u)); /* |t| - (|t|+b) % |2c| */
+  if (*B == gen_0)
+  { u = shifti(S->D, -2); setsigne(u, -1); }
+  else
+    u = shifti(addii_sign(sqri(*B),1, S->D,-1), -2);
+  *C = diviiexact(u, c); /* = (B^2-D)/4c */
+}
+/* Not stack-clean */
+GEN
+qfr3_rho(GEN x, struct qfr_data *S)
+{
+  GEN B, C, b = gel(x,2), c = gel(x,3);
+  rho_get_BC(&B, &C, b, c, S);
+  return mkvec3(c,B,C);
+}
+/* Not stack-clean */
+GEN
+qfr5_rho(GEN x, struct qfr_data *S)
+{
+  GEN B, C, y, b = gel(x,2), c = gel(x,3);
+  long sb = signe(b);
+
+  rho_get_BC(&B, &C, b, c, S);
+  y = mkvec5(c,B,C, gel(x,4), gel(x,5));
+  if (sb) {
+    GEN t = subii(sqri(b), S->D);
+    if (sb < 0)
+      t = divir(t, sqrr(subir(b,S->sqrtD)));
+    else
+      t = divri(sqrr(addir(b,S->sqrtD)), t);
+    /* t = (b + sqrt(D)) / (b - sqrt(D)), evaluated stably */
+    gel(y,5) = mulrr(t, gel(y,5)); fix_expo(y);
+  }
+  return y;
+}
+
+/* Not stack-clean */
+GEN
+qfr_to_qfr5(GEN x, long prec)
+{ return mkvec5(gel(x,1),gel(x,2),gel(x,3),gen_0,real_1(prec)); }
+
+/* d0 = initial distance, x = [a,b,c, expo(d), d], d = exp(2*distance) */
+GEN
+qfr5_to_qfr(GEN x, GEN d0)
+{
+  GEN y;
+  if (lg(x) ==  6)
+  {
+    GEN n = gel(x,4), d = absr(gel(x,5));
+    if (signe(n))
+    {
+      n = addis(shifti(n, EMAX), expo(d));
+      setexpo(d, 0); d = logr_abs(d);
+      if (signe(n)) d = addrr(d, mulir(n, mplog2(lg(d0))));
+      shiftr_inplace(d, -1);
+      d0 = addrr(d0, d);
+    }
+    else if (!gequal1(d)) /* avoid loss of precision */
+    {
+      d = logr_abs(d);
+      shiftr_inplace(d, -1);
+      d0 = addrr(d0, d);
+    }
+  }
+  y = cgetg(5, t_QFR);
+  gel(y,1) = gel(x,1);
+  gel(y,2) = gel(x,2);
+  gel(y,3) = gel(x,3);
+  gel(y,4) = d0; return y;
+}
+
+/* Not stack-clean */
+GEN
+qfr3_to_qfr(GEN x, GEN d)
+{
+  GEN z = cgetg(5, t_QFR);
+  gel(z,1) = gel(x,1);
+  gel(z,2) = gel(x,2);
+  gel(z,3) = gel(x,3);
+  gel(z,4) = d; return z;
+}
+
+static int
+ab_isreduced(GEN a, GEN b, GEN isqrtD)
+{
+  if (signe(b) > 0 && absi_cmp(b, isqrtD) <= 0)
+  {
+    GEN t = addii_sign(isqrtD,1, shifti(a,1),-1);
+    long l = absi_cmp(b, t); /* compare |b| and |floor(sqrt(D)) - |2a|| */
+    if (l > 0 || (l == 0 && signe(t) < 0)) return 1;
+  }
+  return 0;
+}
+
+INLINE int
+qfr_isreduced(GEN x, GEN isqrtD)
+{
+  return ab_isreduced(gel(x,1),gel(x,2),isqrtD);
+}
+
+/* Not stack-clean */
+GEN
+qfr5_red(GEN x, struct qfr_data *S) {
+  while (!qfr_isreduced(x,S->isqrtD)) x = qfr5_rho(x,S);
+  return x;
+}
+/* Not stack-clean */
+GEN
+qfr3_red(GEN x, struct qfr_data *S) {
+  while (!qfr_isreduced(x, S->isqrtD)) x = qfr3_rho(x, S);
+  return x;
+}
+
+static void
+get_disc(GEN x, struct qfr_data *S)
+{
+  if (!S->D) S->D = qfb_disc(x);
+  else if (typ(S->D) != t_INT) pari_err_TYPE("qfr_init",S->D);
+  if (!signe(S->D)) pari_err_DOMAIN("qfr_init", "disc", "=", gen_0,x);
+}
+
+void
+qfr_data_init(GEN D, long prec, struct qfr_data *S)
+{
+  S->D = D;
+  S->sqrtD = sqrtr(itor(S->D,prec));
+  S->isqrtD = truncr(S->sqrtD);
+}
+
+static GEN
+qfr5_init(GEN x, struct qfr_data *S)
+{
+  GEN d = gel(x,4);
+  long prec = realprec(d), l = nbits2prec(-expo(d));
+  if (l > prec) prec = l;
+  if (prec < LOWDEFAULTPREC) prec = LOWDEFAULTPREC;
+  x = qfr_to_qfr5(x,prec);
+
+  get_disc(x, S);
+  if (!S->sqrtD) S->sqrtD = sqrtr(itor(S->D,prec));
+  else if (typ(S->sqrtD) != t_REAL) pari_err_TYPE("qfr_init",S->sqrtD);
+
+  if (!S->isqrtD)
+  {
+    pari_sp av=avma;
+    long e;
+    S->isqrtD = gcvtoi(S->sqrtD,&e);
+    if (e>-2) { avma = av; S->isqrtD = sqrti(S->D); }
+  }
+  else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);
+  return x;
+}
+static GEN
+qfr3_init(GEN x, struct qfr_data *S)
+{
+  get_disc(x, S);
+  if (!S->isqrtD) S->isqrtD = sqrti(S->D);
+  else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);
+  return x;
+}
+
+#define qf_NOD  2
+#define qf_STEP 1
+
+static GEN
+redreal0(GEN x, long flag, GEN D, GEN isqrtD, GEN sqrtD)
+{
+  pari_sp av = avma;
+  struct qfr_data S;
+  GEN d;
+  if (typ(x) != t_QFR) pari_err_TYPE("redreal",x);
+  d = gel(x,4);
+  S.D = D;
+  S.sqrtD = sqrtD;
+  S.isqrtD = isqrtD;
+  x = (flag & qf_NOD)? qfr3_init(x, &S): qfr5_init(x, &S);
+  switch(flag) {
+    case 0:              x = qfr5_red(x,&S); break;
+    case qf_NOD:         x = qfr3_red(x,&S); break;
+    case qf_STEP:        x = qfr5_rho(x,&S); break;
+    case qf_STEP|qf_NOD: x = qfr3_rho(x,&S); break;
+    default: pari_err_FLAG("qfbred");
+  }
+  return gerepilecopy(av, qfr5_to_qfr(x,d));
+}
+GEN
+redreal(GEN x)
+{ return redreal0(x,0,NULL,NULL,NULL); }
+GEN
+rhoreal(GEN x)
+{ return redreal0(x,qf_STEP,NULL,NULL,NULL); }
+GEN
+redrealnod(GEN x, GEN isqrtD)
+{ return redreal0(x,qf_NOD,NULL,isqrtD,NULL); }
+GEN
+rhorealnod(GEN x, GEN isqrtD)
+{ return redreal0(x,qf_STEP|qf_NOD,NULL,isqrtD,NULL); }
+GEN
+qfbred0(GEN x, long flag, GEN D, GEN isqrtD, GEN sqrtD)
+{
+  if (typ(x) == t_QFI)
+    return (flag & qf_STEP)? rhoimag(x): redimag(x);
+  return redreal0(x,flag,D,isqrtD,sqrtD);
+}
+
+GEN
+qfr5_comp(GEN x, GEN y, struct qfr_data *S)
+{
+  pari_sp av = avma;
+  GEN z = cgetg(6,t_VEC); qfb_comp(z,x,y);
+  if (x == y)
+  {
+    gel(z,4) = shifti(gel(x,4),1);
+    gel(z,5) = sqrr(gel(x,5));
+  }
+  else
+  {
+    gel(z,4) = addii(gel(x,4),gel(y,4));
+    gel(z,5) = mulrr(gel(x,5),gel(y,5));
+  }
+  fix_expo(z); z = qfr5_red(z,S);
+  return gerepilecopy(av,z);
+}
+/* Not stack-clean */
+GEN
+qfr3_comp(GEN x, GEN y, struct qfr_data *S)
+{
+  GEN z = cgetg(4,t_VEC); qfb_comp(z,x,y);
+  return qfr3_red(z, S);
+}
+
+/* valid for t_QFR, qfr3, qfr5 */
+static GEN
+qfr_inv(GEN x) {
+  GEN z = shallowcopy(x);
+  gel(z,2) = negi(gel(z,2));
+  return z;
+}
+
+/* return x^n. Not stack-clean */
+GEN
+qfr5_pow(GEN x, GEN n, struct qfr_data *S)
+{
+  GEN y = NULL;
+  long i, m, s = signe(n);
+  if (!s) return qfr5_1(S, lg(gel(x,5)));
+  for (i=lgefint(n)-1; i>1; i--)
+  {
+    m = n[i];
+    for (; m; m>>=1)
+    {
+      if (m&1) y = y? qfr5_comp(y,x,S): x;
+      if (m == 1 && i == 2) break;
+      x = qfr5_comp(x,x,S);
+    }
+  }
+  return y;
+}
+/* return x^n. Not stack-clean */
+GEN
+qfr3_pow(GEN x, GEN n, struct qfr_data *S)
+{
+  GEN y = NULL;
+  long i, m, s = signe(n);
+  if (!s) return qfr3_1(S);
+  if (s < 0) x = qfr_inv(x);
+  for (i=lgefint(n)-1; i>1; i--)
+  {
+    m = n[i];
+    for (; m; m>>=1)
+    {
+      if (m&1) y = y? qfr3_comp(y,x,S): x;
+      if (m == 1 && i == 2) break;
+      x = qfr3_comp(x,x,S);
+    }
+  }
+  return y;
+}
+GEN
+qfrpow(GEN x, GEN n)
+{
+  struct qfr_data S = { NULL, NULL, NULL };
+  long s = signe(n);
+  pari_sp av = avma;
+  GEN d0;
+
+  if (!s) return qfr_1(x);
+  if (is_pm1(n)) return s > 0? redreal(x): ginv(x);
+  if (s < 0) x = qfr_inv(x);
+  d0 = gel(x,4);
+  if (!signe(d0)) {
+    x = qfr3_init(x, &S);
+    x = qfr3_pow(x, n, &S);
+    x = qfr3_to_qfr(x, d0);
+  } else {
+    x = qfr5_init(x, &S);
+    x = qfr5_pow(qfr_to_qfr5(x, lg(S.sqrtD)), n, &S);
+    x = qfr5_to_qfr(x, mulri(d0,n));
+  }
+  return gerepilecopy(av, x);
+}
+
+/* Prime forms associated to prime ideals of degree 1 */
+
+/* assume x != 0 a t_INT, p > 0
+ * Return a t_QFI, but discriminant sign is not checked: can be used for
+ * real forms as well */
+GEN
+primeform_u(GEN x, ulong p)
+{
+  GEN c, y = cgetg(4, t_QFI);
+  pari_sp av = avma;
+  ulong b;
+  long s;
+
+  s = mod8(x); if (signe(x) < 0 && s) s = 8-s;
+  /* 2 or 3 mod 4 */
+  if (s & 2) pari_err_DOMAIN("primeform", "disc % 4", ">",gen_1, x);
+  if (p == 2) {
+    switch(s) {
+      case 0: b = 0; break;
+      case 1: b = 1; break;
+      case 4: b = 2; break;
+      default: pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );
+               b = 0; /* -Wall */
+    }
+    c = shifti(subsi(s,x), -3);
+  } else {
+    b = Fl_sqrt(umodiu(x,p), p);
+    if (b == ~0UL) pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );
+    /* mod(b) != mod2(x) ? */
+    if ((b ^ s) & 1) b = p - b;
+    c = diviuexact(shifti(subii(sqru(b), x), -2), p);
+  }
+  gel(y,3) = gerepileuptoint(av, c);
+  gel(y,2) = utoi(b);
+  gel(y,1) = utoipos(p); return y;
+}
+
+/* special case: p = 1 return unit form */
+GEN
+primeform(GEN x, GEN p, long prec)
+{
+  const char *f = "primeform";
+  pari_sp av;
+  long s, sx = signe(x), sp = signe(p);
+  GEN y, b, absp;
+
+  if (typ(x) != t_INT) pari_err_TYPE(f,x);
+  if (typ(p) != t_INT) pari_err_TYPE(f,p);
+  if (!sp) pari_err_DOMAIN(f,"p","=",gen_0,p);
+  if (!sx) pari_err_DOMAIN(f,"D","=",gen_0,x);
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    if (pp == 1) {
+      if (sx < 0) {
+        long r;
+        if (sp < 0) pari_err_IMPL("negative definite t_QFI");
+        r = mod4(x);
+        if (r && r != 3) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);
+        return qfi_1_by_disc(x);
+      }
+      y = qfr_1_by_disc(x,prec);
+      if (sp < 0) { gel(y,1) = gen_m1; togglesign(gel(y,3)); }
+      return y;
+    }
+    y = primeform_u(x, pp);
+    if (sx < 0) {
+      if (sp < 0) pari_err_IMPL("negative definite t_QFI");
+      return y;
+    }
+    if (sp < 0) { togglesign(gel(y,1)); togglesign(gel(y,3)); }
+    return gcopy( qfr3_to_qfr(y, real_0(prec)) );
+  }
+  s = mod8(x);
+  if (sx < 0)
+  {
+    if (sp < 0) pari_err_IMPL("negative definite t_QFI");
+    if (s) s = 8-s;
+    y = cgetg(4, t_QFI);
+  }
+  else
+  {
+    y = cgetg(5, t_QFR);
+    gel(y,4) = real_0(prec);
+  }
+  /* 2 or 3 mod 4 */
+  if (s & 2) pari_err_DOMAIN(f, "disc % 4", ">",gen_1, x);
+  absp = absi(p); av = avma;
+  b = Fp_sqrt(x, absp); if (!b) pari_err_SQRTN(f, mkintmod(x,absp));
+  s &= 1; /* s = x mod 2 */
+  /* mod(b) != mod2(x) ? [Warning: we may have b == 0] */
+  if ((!signe(b) && s) || mod2(b) != s) b = gerepileuptoint(av, subii(absp,b));
+
+  av = avma;
+  gel(y,3) = gerepileuptoint(av, diviiexact(shifti(subii(sqri(b), x), -2), p));
+  gel(y,2) = b;
+  gel(y,1) = icopy(p);
+  return y;
+}
+
+/* Let M and N in SL2(Z), return (N*M^-1)[,1] */
+static GEN
+SL2_div_mul_e1(GEN N, GEN M)
+{
+  GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);
+  GEN p = subii(mulii(gcoeff(N,1,1), d), mulii(gcoeff(N,1,2), b));
+  GEN q = subii(mulii(gcoeff(N,2,1), d), mulii(gcoeff(N,2,2), b));
+  return mkvec2(p,q);
+}
+/* Let M and N in SL2(Z), return (N*[1,0;0,-1]*M^-1)[,1] */
+static GEN
+SL2_swap_div_mul_e1(GEN N, GEN M)
+{
+  GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);
+  GEN p = addii(mulii(gcoeff(N,1,1), d), mulii(gcoeff(N,1,2), b));
+  GEN q = addii(mulii(gcoeff(N,2,1), d), mulii(gcoeff(N,2,2), b));
+  return mkvec2(p,q);
+}
+
+/* Test equality modulo GL2 of two reduced forms */
+static int
+GL2_qfb_equal(GEN a, GEN b)
+{
+  return equalii(gel(a,1),gel(b,1))
+   && absi_equal(gel(a,2),gel(b,2))
+   &&    equalii(gel(a,3),gel(b,3));
+}
+
+static GEN
+qfbsolve_cornacchia(GEN c, GEN p, int swap)
+{
+  pari_sp av = avma;
+  GEN M, N;
+  if (kronecker(negi(c), p) < 0 || !cornacchia(c, p, &M,&N)) {
+    avma = av; return gen_0;
+  }
+  return gerepilecopy(av, swap? mkvec2(N,M): mkvec2(M,N));
+}
+
+GEN
+qfisolvep(GEN Q, GEN p)
+{
+  GEN M, N, x,y, a,b,c, d;
+  pari_sp av = avma;
+  if (!signe(gel(Q,2)))
+  {
+    a = gel(Q,1);
+    c = gel(Q,3); /* if principal form, use faster cornacchia */
+    if (equali1(a)) return qfbsolve_cornacchia(c, p, 0);
+    if (equali1(c)) return qfbsolve_cornacchia(a, p, 1);
+  }
+  d = qfb_disc(Q); if (kronecker(d,p) < 0) return gen_0;
+  a = redimagsl2(Q, &N);
+  if (is_pm1(gel(a,1))) /* principal form */
+  {
+    long r;
+    if (!signe(gel(a,2)))
+    {
+      a = qfbsolve_cornacchia(gel(a,3), p, 0);
+      if (a == gen_0) { avma = av; return gen_0; }
+      a = ZM_ZC_mul(N, a);
+      a[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */
+      return gerepileupto(av, a);
+    }
+    /* x^2 + xy + ((1-d)/4)y^2 = p <==> (2x + y)^2 - d y^2 = 4p */
+    if (!cornacchia2(negi(d), p, &x, &y)) { avma = av; return gen_0; }
+    x = divis_rem(subii(x,y), 2, &r); if (r) { avma = av; return gen_0; }
+    a = ZM_ZC_mul(N, mkvec2(x,y));
+    a[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */
+    return gerepileupto(av, a);
+  }
+  b = redimagsl2(primeform(d, p, 0), &M);
+  if (!GL2_qfb_equal(a,b)) { avma = av; return gen_0; }
+  if (signe(gel(a,2))==signe(gel(b,2)))
+    x = SL2_div_mul_e1(N,M);
+  else
+    x = SL2_swap_div_mul_e1(N,M);
+  return gerepilecopy(av, x);
+}
+
+GEN
+redrealsl2step(GEN A, GEN d, GEN rd)
+{
+  pari_sp ltop = avma;
+  GEN N, V = gel(A,1), M = gel(A,2);
+  GEN a = gel(V,1), b = gel(V,2), c = gel(V,3);
+  GEN ac = mpabs(c);
+  GEN r = addii(b, gmax(rd, ac));
+  GEN q = truedvmdii(r, shifti(ac, 1), NULL);
+  r = subii(mulii(shifti(q, 1), ac), b);
+  a = c; b = r;
+  c = truedvmdii(subii(sqri(r), d), shifti(c,2), NULL);
+  if (signe(a) < 0) togglesign(q);
+  N = mkmat2(gel(M,2),
+             mkcol2(subii(mulii(q, gcoeff(M, 1, 2)), gcoeff(M, 1, 1)),
+                    subii(mulii(q, gcoeff(M, 2, 2)), gcoeff(M, 2, 1))));
+  return gerepilecopy(ltop, mkvec2(mkvec3(a,b,c),N));
+}
+
+GEN
+redrealsl2(GEN V, GEN d, GEN rd)
+{
+  pari_sp ltop = avma, st_lim = stack_lim(ltop, 1);
+  GEN M, u1, u2, v1, v2;
+  GEN a = gel(V,1), b = gel(V,2), c = gel(V,3);
+  u1 = v2 = gen_1; v1 = u2 = gen_0;
+  while (!ab_isreduced(a,b,rd))
+  {
+    GEN ac = mpabs(c);
+    GEN r = addii(b, gmax(rd,ac));
+    GEN q = truedvmdii(r, shifti(ac, 1), NULL);
+    r = subii(mulii(shifti(q, 1), ac), b);
+    a = c; b = r;
+    c = truedvmdii(subii(sqri(r), d), shifti(c, 2), NULL);
+    if (signe(a) < 0) togglesign(q);
+    r = u1; u1 = v1; v1 = subii(mulii(q, v1), r);
+    r = u2; u2 = v2; v2 = subii(mulii(q, v2), r);
+    if (low_stack(st_lim, stack_lim(ltop, 1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"redrealsl2");
+      gerepileall(ltop, 7, &a,&b,&c,&u1,&u2,&v1,&v2);
+    }
+  }
+  M = mkmat2(mkcol2(u1,u2), mkcol2(v1,v2));
+  return gerepilecopy(ltop, mkvec2(mkvec3(a,b,c), M));
+}
+
+GEN
+qfrsolvep(GEN Q, GEN p)
+{
+  pari_sp ltop = avma, btop, st_lim;
+  GEN N, P, P1, P2, M, rd, d = qfb_disc(Q);
+  if (kronecker(d, p) < 0) { avma = ltop; return gen_0; }
+  rd = sqrti(d);
+  M = N = redrealsl2(Q, d,rd);
+  P = primeform(d, p, DEFAULTPREC);
+  P1 = redrealsl2(P, d,rd);
+  togglesign( gel(P,2) );
+  P2 = redrealsl2(P, d,rd);
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for(;;)
+  {
+    if (ZV_equal(gel(M,1), gel(P1,1))) { N = gel(P1,2); break; }
+    if (ZV_equal(gel(M,1), gel(P2,1))) { N = gel(P2,2); break; }
+    M = redrealsl2step(M, d,rd);
+    if (ZV_equal(gel(M,1), gel(N,1))) { avma = ltop; return gen_0; }
+    if (low_stack(st_lim, stack_lim(btop, 1))) M = gerepileupto(btop, M);
+  }
+  return gerepilecopy(ltop, SL2_div_mul_e1(gel(M,2),N));
+}
+
+GEN
+qfbsolve(GEN Q,GEN n)
+{
+  if (typ(n)!=t_INT) pari_err_TYPE("qfbsolve",n);
+  switch(typ(Q))
+  {
+  case t_QFI: return qfisolvep(Q,n);
+  case t_QFR: return qfrsolvep(Q,n);
+  default:
+    pari_err_TYPE("qfbsolve",Q);
+    return NULL; /* NOT REACHED */
+  }
+}
+
+/* 1 if there exists x,y such that x^2 + dy^2 = p [prime], 0 otherwise */
+long
+cornacchia(GEN d, GEN p, GEN *px, GEN *py)
+{
+  pari_sp av = avma, av2, lim;
+  GEN a, b, c, L, r;
+
+  if (typ(d) != t_INT) pari_err_TYPE("cornacchia", d);
+  if (typ(p) != t_INT) pari_err_TYPE("cornacchia", p);
+  if (signe(d) <= 0) pari_err_DOMAIN("cornacchia", "d","<=",gen_0,d);
+  *px = *py = gen_0;
+  b = subii(p, d);
+  if (signe(b) < 0) return 0;
+  if (signe(b) == 0) { avma = av; *py = gen_1; return 1; }
+  b = Fp_sqrt(b, p); /* sqrt(-d) */
+  if (!b) { avma = av; return 0; }
+  if (absi_cmp(shifti(b,1), p) > 0) b = subii(b,p);
+  a = p; L = sqrti(p);
+  av2 = avma; lim = stack_lim(av2, 1);
+  while (absi_cmp(b, L) > 0)
+  {
+    r = remii(a, b); a = b; b = r;
+    if (low_stack(lim, stack_lim(av2, 1))) {
+      if (DEBUGMEM>1) pari_warn(warnmem,"cornacchia");
+      gerepileall(av2, 2, &a,&b);
+    }
+  }
+  a = subii(p, sqri(b));
+  c = dvmdii(a, d, &r);
+  if (r != gen_0 || !Z_issquareall(c, &c)) { avma = av; return 0; }
+  avma = av;
+  *px = icopy(b);
+  *py = icopy(c); return 1;
+}
+/* 1 if there exists x,y such that x^2 + dy^2 = 4p [p prime], 0 otherwise */
+long
+cornacchia2(GEN d, GEN p, GEN *px, GEN *py)
+{
+  pari_sp av = avma, av2, lim;
+  GEN a, b, c, L, r, px4;
+  long k;
+
+  if (typ(d) != t_INT) pari_err_TYPE("cornacchia2", d);
+  if (typ(p) != t_INT) pari_err_TYPE("cornacchia2", p);
+  if (signe(d) <= 0) pari_err_DOMAIN("cornacchia2", "d","<=",gen_0,d);
+  *px = *py = gen_0;
+  k = mod4(d);
+  if (k == 1 || k == 2) pari_err_DOMAIN("cornacchia2","-d mod 4", ">",gen_1,d);
+  px4 = shifti(p,2);
+  if (absi_cmp(px4, d) < 0) { avma = av; return 0; }
+  if (equaliu(p, 2))
+  {
+    avma = av;
+    switch (itou_or_0(d)) {
+      case 4: *px = gen_2; break;
+      case 7: *px = gen_1; break;
+      default: return 0;
+    }
+    *py = gen_1; return 1;
+  }
+  b = Fp_sqrt(negi(d), p);
+  if (!b) { avma = av; return 0; }
+  if (!signe(b)) { /* d = p,2p,3p,4p */
+    avma = av;
+    if (absi_equal(d, px4)){ *py = gen_1; return 1; }
+    if (absi_equal(d, p))  { *py = gen_2; return 1; }
+    return 0;
+  }
+  if (mod2(b) != (k & 1)) b = subii(p,b);
+  a = shifti(p,1); L = sqrti(px4);
+  av2 = avma; lim = stack_lim(av2, 1);
+  while (cmpii(b, L) > 0)
+  {
+    r = remii(a, b); a = b; b = r;
+    if (low_stack(lim, stack_lim(av2, 1))) {
+      if (DEBUGMEM>1) pari_warn(warnmem,"cornacchia");
+      gerepileall(av2, 2, &a,&b);
+    }
+  }
+  a = subii(px4, sqri(b));
+  c = dvmdii(a, d, &r);
+  if (r != gen_0 || !Z_issquareall(c, &c)) { avma = av; return 0; }
+  avma = av;
+  *px = icopy(b);
+  *py = icopy(c); return 1;
+}
diff --git a/src/basemath/RgV.c b/src/basemath/RgV.c
new file mode 100644
index 0000000..e451de1
--- /dev/null
+++ b/src/basemath/RgV.c
@@ -0,0 +1,847 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+int
+RgM_is_ZM(GEN x)
+{
+  long i, j, h, l = lg(x);
+  if (l == 1) return 1;
+  h = lgcols(x);
+  if (h == 1) return 1;
+  for (j = l-1; j > 0; j--)
+    for (i = h-1; i > 0; i--)
+      if (typ(gcoeff(x,i,j)) != t_INT) return 0;
+  return 1;
+}
+
+int
+RgV_is_ZMV(GEN V)
+{
+  long i, l = lg(V);
+  for (i=1; i<l; i++)
+    if (typ(gel(V,i))!=t_MAT || !RgM_is_ZM(gel(V,i)))
+      return 0;
+  return 1;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                   GENERIC LINEAR ALGEBRA                       **/
+/**                                                                **/
+/********************************************************************/
+/*           GENERIC  MULTIPLICATION involving zc/zm                */
+/* x non-empty t_MAT, y a compatible zc (dimension > 0). */
+static GEN
+RgM_zc_mul_i(GEN x, GEN y, long c, long l)
+{
+  long i, j;
+  pari_sp av;
+  GEN z = cgetg(l,t_COL), s;
+
+  for (i=1; i<l; i++)
+  {
+    av = avma; s = gmulgs(gcoeff(x,i,1),y[1]);
+    for (j=2; j<c; j++)
+    {
+      long t = y[j];
+      switch(t)
+      {
+        case  0: break;
+        case  1: s = gadd(s, gcoeff(x,i,j)); break;
+        case -1: s = gsub(s, gcoeff(x,i,j)); break;
+        default: s = gadd(s, gmulgs(gcoeff(x,i,j), t)); break;
+      }
+    }
+    gel(z,i) = gerepileupto(av,s);
+  }
+  return z;
+}
+GEN
+RgM_zc_mul(GEN x, GEN y) { return RgM_zc_mul_i(x,y, lg(x), lgcols(x)); }
+/* x t_MAT, y a compatible zm (dimension > 0). */
+GEN
+RgM_zm_mul(GEN x, GEN y)
+{
+  long j, c, l = lg(x), ly = lg(y);
+  GEN z = cgetg(ly, t_MAT);
+  if (l == 1) return z;
+  c = lgcols(x);
+  for (j = 1; j < ly; j++) gel(z,j) = RgM_zc_mul_i(x, gel(y,j), l,c);
+  return z;
+}
+
+static GEN
+RgV_zc_mul_i(GEN x, GEN y, long l)
+{
+  long i;
+  GEN z = gen_0;
+  pari_sp av = avma;
+  for (i = 1; i < l; i++) z = gadd(z, gmulgs(gel(x,i), y[i]));
+  return gerepileupto(av, z);
+}
+GEN
+RgV_zc_mul(GEN x, GEN y) { return RgV_zc_mul_i(x, y, lg(x)); }
+
+GEN
+RgV_zm_mul(GEN x, GEN y)
+{
+  long j, l = lg(x), ly = lg(y);
+  GEN z = cgetg(ly, t_VEC);
+  for (j = 1; j < ly; j++) gel(z,j) = RgV_zc_mul_i(x, gel(y,j), l);
+  return z;
+}
+
+/* scalar product x.x */
+GEN
+RgV_dotsquare(GEN x)
+{
+  long i, lx = lg(x);
+  pari_sp av = avma, lim = stack_lim(av,3);
+  GEN z;
+  if (lx == 1) return gen_0;
+  z = gsqr(gel(x,1));
+  for (i=2; i<lx; i++)
+  {
+    z = gadd(z, gsqr(gel(x,i)));
+    if (low_stack(lim,stack_lim(av,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgV_dotsquare, i = %ld",i);
+      z = gerepileupto(av, z);
+    }
+  }
+  return gerepileupto(av,z);
+}
+
+/* scalar product x.y, lx = lg(x) = lg(y) */
+static GEN
+RgV_dotproduct_i(GEN x, GEN y, long lx)
+{
+  pari_sp av = avma, lim = stack_lim(av,3);
+  long i;
+  GEN z;
+  if (lx == 1) return gen_0;
+  z = gmul(gel(x,1),gel(y,1));
+  for (i=2; i<lx; i++)
+  {
+    z = gadd(z, gmul(gel(x,i), gel(y,i)));
+    if (low_stack(lim,stack_lim(av,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgV_dotproduct, i = %ld",i);
+      z = gerepileupto(av, z);
+    }
+  }
+  return gerepileupto(av,z);
+}
+GEN
+RgV_dotproduct(GEN x,GEN y)
+{
+  if (x == y) return RgV_dotsquare(x);
+  return RgV_dotproduct_i(x, y, lg(x));
+}
+/* v[1] + ... + v[lg(v)-1] */
+GEN
+RgV_sum(GEN v)
+{
+  GEN p;
+  long i, l = lg(v);
+  if (l == 1) return gen_0;
+  p = gel(v,1); for (i=2; i<l; i++) p = gadd(p, gel(v,i));
+  return p;
+}
+/* v[1] + ... + v[n]. Assume lg(v) > n. */
+GEN
+RgV_sumpart(GEN v, long n)
+{
+  GEN p;
+  long i;
+  if (!n) return gen_0;
+  p = gel(v,1); for (i=2; i<=n; i++) p = gadd(p, gel(v,i));
+  return p;
+}
+/* v[m] + ... + v[n]. Assume lg(v) > n, m > 0. */
+GEN
+RgV_sumpart2(GEN v, long m, long n)
+{
+  GEN p;
+  long i;
+  if (n < m) return gen_0;
+  p = gel(v,m); for (i=m+1; i<=n; i++) p = gadd(p, gel(v,i));
+  return p;
+}
+
+/*                    ADDITION SCALAR + MATRIX                     */
+/* x square matrix, y scalar; create the square matrix x + y*Id */
+GEN
+RgM_Rg_add(GEN x, GEN y)
+{
+  long l = lg(x), i, j;
+  GEN z = cgetg(l,t_MAT);
+
+  if (l==1) return z;
+  if (l != lgcols(x)) pari_err_OP( "+", x, y);
+  z = cgetg(l,t_MAT);
+  for (i=1; i<l; i++)
+  {
+    GEN zi = cgetg(l,t_COL), xi = gel(x,i);
+    gel(z,i) = zi;
+    for (j=1; j<l; j++)
+      gel(zi,j) = i==j? gadd(y,gel(xi,j)): gcopy(gel(xi,j));
+  }
+  return z;
+}
+GEN
+RgM_Rg_sub(GEN x, GEN y)
+{
+  long l = lg(x), i, j;
+  GEN z = cgetg(l,t_MAT);
+
+  if (l==1) return z;
+  if (l != lgcols(x)) pari_err_OP( "-", x, y);
+  z = cgetg(l,t_MAT);
+  for (i=1; i<l; i++)
+  {
+    GEN zi = cgetg(l,t_COL), xi = gel(x,i);
+    gel(z,i) = zi;
+    for (j=1; j<l; j++)
+      gel(zi,j) = i==j? gsub(y,gel(xi,j)): gcopy(gel(xi,j));
+  }
+  return z;
+}
+GEN
+RgM_Rg_add_shallow(GEN x, GEN y)
+{
+  long l = lg(x), i, j;
+  GEN z = cgetg(l,t_MAT);
+
+  if (l==1) return z;
+  if (l != lgcols(x)) pari_err_OP( "+", x, y);
+  for (i=1; i<l; i++)
+  {
+    GEN zi = cgetg(l,t_COL), xi = gel(x,i);
+    gel(z,i) = zi;
+    for (j=1; j<l; j++) gel(zi,j) = gel(xi,j);
+    gel(zi,i) = gadd(gel(zi,i), y);
+  }
+  return z;
+}
+GEN
+RgM_Rg_sub_shallow(GEN x, GEN y)
+{
+  long l = lg(x), i, j;
+  GEN z = cgetg(l,t_MAT);
+
+  if (l==1) return z;
+  if (l != lgcols(x)) pari_err_OP( "-", x, y);
+  for (i=1; i<l; i++)
+  {
+    GEN zi = cgetg(l,t_COL), xi = gel(x,i);
+    gel(z,i) = zi;
+    for (j=1; j<l; j++) gel(zi,j) = gel(xi,j);
+    gel(zi,i) = gsub(gel(zi,i), y);
+  }
+  return z;
+}
+
+GEN
+RgC_Rg_add(GEN x, GEN y)
+{
+  long k, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  if (lx == 1)
+  {
+    if (isintzero(y)) return z;
+    pari_err_TYPE2("+",x,y);
+  }
+  gel(z,1) = gadd(y,gel(x,1));
+  for (k = 2; k < lx; k++) gel(z,k) = gcopy(gel(x,k));
+  return z;
+}
+
+static GEN
+RgC_add_i(GEN x, GEN y, long lx)
+{
+  GEN A = cgetg(lx, t_COL);
+  long i;
+  for (i=1; i<lx; i++) gel(A,i) = gadd(gel(x,i), gel(y,i));
+  return A;
+}
+GEN
+RgC_add(GEN x, GEN y) { return RgC_add_i(x, y, lg(x)); }
+GEN
+RgV_add(GEN x, GEN y)
+{
+  long i, lx = lg(x);
+  GEN A = cgetg(lx, t_VEC);
+  for (i=1; i<lx; i++) gel(A,i) = gadd(gel(x,i), gel(y,i));
+  return A;
+}
+
+static GEN
+RgC_sub_i(GEN x, GEN y, long lx)
+{
+  long i;
+  GEN A = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++) gel(A,i) = gsub(gel(x,i), gel(y,i));
+  return A;
+}
+GEN
+RgC_sub(GEN x, GEN y) { return RgC_sub_i(x, y, lg(x)); }
+GEN
+RgV_sub(GEN x, GEN y)
+{
+  long i, lx = lg(x);
+  GEN A = cgetg(lx, t_VEC);
+  for (i=1; i<lx; i++) gel(A,i) = gsub(gel(x,i), gel(y,i));
+  return A;
+}
+
+GEN
+RgM_add(GEN x, GEN y)
+{
+  long lx = lg(x), l, j;
+  GEN z;
+  if (lx == 1) return cgetg(1, t_MAT);
+  z = cgetg(lx, t_MAT); l = lgcols(x);
+  for (j = 1; j < lx; j++) gel(z,j) = RgC_add_i(gel(x,j), gel(y,j), l);
+  return z;
+}
+GEN
+RgM_sub(GEN x, GEN y)
+{
+  long lx = lg(x), l, j;
+  GEN z;
+  if (lx == 1) return cgetg(1, t_MAT);
+  z = cgetg(lx, t_MAT); l = lgcols(x);
+  for (j = 1; j < lx; j++) gel(z,j) = RgC_sub_i(gel(x,j), gel(y,j), l);
+  return z;
+}
+
+static GEN
+RgC_neg_i(GEN x, long lx)
+{
+  long i;
+  GEN y = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++) gel(y,i) = gneg(gel(x,i));
+  return y;
+}
+GEN
+RgC_neg(GEN x) { return RgC_neg_i(x, lg(x)); }
+GEN
+RgV_neg(GEN x)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx, t_VEC);
+  for (i=1; i<lx; i++) gel(y,i) = gneg(gel(x,i));
+  return y;
+}
+GEN
+RgM_neg(GEN x)
+{
+  long i, hx, lx = lg(x);
+  GEN y = cgetg(lx, t_MAT);
+  if (lx == 1) return y;
+  hx = lgcols(x);
+  for (i=1; i<lx; i++) gel(y,i) = RgC_neg_i(gel(x,i), hx);
+  return y;
+}
+
+GEN
+RgV_RgC_mul(GEN x, GEN y)
+{
+  long lx = lg(x);
+  if (lx != lg(y)) pari_err_OP("operation 'RgV_RgC_mul'", x, y);
+  return RgV_dotproduct_i(x, y, lx);
+}
+GEN
+RgC_RgV_mul(GEN x, GEN y)
+{
+  long i, ly = lg(y);
+  GEN z = cgetg(ly,t_MAT);
+  for (i=1; i<ly; i++) gel(z,i) = RgC_Rg_mul(x, gel(y,i));
+  return z;
+}
+GEN
+RgC_RgM_mul(GEN x, GEN y)
+{
+  long i, ly = lg(y);
+  GEN z = cgetg(ly,t_MAT);
+  if (ly != 1 && lgcols(y) != 2) pari_err_OP("operation 'RgC_RgM_mul'",x,y);
+  for (i=1; i<ly; i++) gel(z,i) = RgC_Rg_mul(x, gcoeff(y,1,i));
+  return z;
+}
+GEN
+RgM_RgV_mul(GEN x, GEN y)
+{
+  if (lg(x) != 2) pari_err_OP("operation 'RgM_RgV_mul'", x,y);
+  return RgC_RgV_mul(gel(x,1), y);
+}
+
+/* x[i,]*y, l = lg(y) > 1 */
+static GEN
+RgMrow_RgC_mul_i(GEN x, GEN y, long i, long l)
+{
+  pari_sp av = avma;
+  GEN t = gmul(gcoeff(x,i,1), gel(y,1)); /* l > 1 ! */
+  long j;
+  for (j=2; j<l; j++) t = gadd(t, gmul(gcoeff(x,i,j), gel(y,j)));
+  return gerepileupto(av,t);
+}
+GEN
+RgMrow_RgC_mul(GEN x, GEN y, long i)
+{ return RgMrow_RgC_mul_i(x, y, i, lg(x)); }
+
+/* compatible t_MAT * t_COL, lx = lg(x) = lg(y) > 1, l = lgcols(x) */
+static GEN
+RgM_RgC_mul_i(GEN x, GEN y, long lx, long l)
+{
+  GEN z = cgetg(l,t_COL);
+  long i;
+  for (i=1; i<l; i++) gel(z,i) = RgMrow_RgC_mul_i(x,y,i,lx);
+  return z;
+}
+
+GEN
+RgM_RgC_mul(GEN x, GEN y)
+{
+  long lx = lg(x);
+  GEN ffx = NULL, ffy = NULL;
+  if (lx != lg(y)) pari_err_OP("operation 'RgM_RgC_mul'", x,y);
+  if (lx == 1) return cgetg(1,t_COL);
+  if (RgM_is_FFM(x, &ffx) && RgC_is_FFC(y, &ffy)) {
+    if (!FF_samefield(ffx, ffy))
+      pari_err_OP("*", ffx, ffy);
+    return FFM_FFC_mul(x, y, ffx);
+  }
+  return RgM_RgC_mul_i(x, y, lx, lgcols(x));
+}
+
+GEN
+RgV_RgM_mul(GEN x, GEN y)
+{
+  long i, lx, ly = lg(y);
+  GEN z;
+  if (ly == 1) return cgetg(1,t_VEC);
+  lx = lg(x);
+  if (lx != lgcols(y)) pari_err_OP("operation 'RgV_RgM_mul'", x,y);
+  z = cgetg(ly, t_VEC);
+  for (i=1; i<ly; i++) gel(z,i) = RgV_dotproduct_i(x, gel(y,i), lx);
+  return z;
+}
+
+static int
+is_modular_mul(GEN a, GEN b, GEN *z)
+{
+  GEN p1 = NULL, p2 = NULL, p;
+  ulong pp;
+  if (!RgM_is_FpM(a, &p1) || !p1) return 0;
+  if (!RgM_is_FpM(b, &p2) || !p2) return 0;
+  p = gcdii(p1, p2);
+  a = RgM_Fp_init(a, p, &pp);
+  switch(pp)
+  {
+  case 0:
+    b = RgM_to_FpM(b,p);
+    b = FpM_mul(a,b,p);
+    *z = FpM_to_mod(b,p);
+    break;
+  case 2:
+    b = RgM_to_F2m(b);
+    b = F2m_mul(a,b);
+    *z = F2m_to_mod(b);
+    break;
+  default:
+    b = RgM_to_Flm(b,pp);
+    b = Flm_mul(a,b,pp);
+    *z = Flm_to_mod(b,pp);
+  }
+  return 1;
+}
+static int
+is_modular_sqr(GEN a, GEN *z)
+{
+  GEN p = NULL;
+  ulong pp;
+  if (!RgM_is_FpM(a, &p) || !p) return 0;
+  a = RgM_Fp_init(a, p, &pp);
+  switch(pp)
+  {
+    case 0: *z = FpM_to_mod(FpM_mul(a,a, p), p); break;
+    case 2: *z = F2m_to_mod(F2m_mul(a,a)); break;
+    default:*z = Flm_to_mod(Flm_mul(a,a, pp), pp); break;
+  }
+  return 1;
+}
+
+GEN
+RgM_mul(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long j, l, lx, ly = lg(y);
+  GEN z, ffx = NULL, ffy = NULL;
+  if (ly == 1) return cgetg(1,t_MAT);
+  lx = lg(x);
+  if (lx != lgcols(y)) pari_err_OP("operation 'RgM_mul'", x,y);
+  if (lx == 1) return zeromat(0,ly-1);
+  if (is_modular_mul(x,y,&z)) return gerepileupto(av, z);
+  if (RgM_is_FFM(x, &ffx) && RgM_is_FFM(y, &ffy)) {
+    if (!FF_samefield(ffx, ffy))
+      pari_err_OP("*", ffx, ffy);
+    return FFM_mul(x, y, ffx);
+  }
+  z = cgetg(ly, t_MAT);
+  l = lgcols(x);
+  for (j=1; j<ly; j++) gel(z,j) = RgM_RgC_mul_i(x, gel(y,j), lx, l);
+  return z;
+}
+/* assume result is symmetric */
+GEN
+RgM_multosym(GEN x, GEN y)
+{
+  long j, lx, ly = lg(y);
+  GEN M;
+  if (ly == 1) return cgetg(1,t_MAT);
+  lx = lg(x);
+  if (lx != lgcols(y)) pari_err_OP("operation 'RgM_multosym'", x,y);
+  if (lx == 1) return cgetg(1,t_MAT);
+  if (ly != lgcols(x)) pari_err_OP("operation 'RgM_multosym'", x,y);
+  M = cgetg(ly, t_MAT);
+  for (j=1; j<ly; j++)
+  {
+    GEN z = cgetg(ly,t_COL), yj = gel(y,j);
+    long i;
+    for (i=1; i<j; i++) gel(z,i) = gcoeff(M,j,i);
+    for (i=j; i<ly; i++)gel(z,i) = RgMrow_RgC_mul_i(x,yj,i,lx);
+    gel(M,j) = z;
+  }
+  return M;
+}
+/* x~ * y, assuming result is symmetric */
+GEN
+RgM_transmultosym(GEN x, GEN y)
+{
+  long i, j, l, ly = lg(y);
+  GEN M;
+  if (ly == 1) return cgetg(1,t_MAT);
+  if (lg(x) != ly) pari_err_OP("operation 'RgM_transmultosym'", x,y);
+  l = lgcols(y);
+  if (lgcols(x) != l) pari_err_OP("operation 'RgM_transmultosym'", x,y);
+  M = cgetg(ly, t_MAT);
+  for (i=1; i<ly; i++)
+  {
+    GEN xi = gel(x,i), c = cgetg(ly,t_COL);
+    gel(M,i) = c;
+    for (j=1; j<i; j++)
+      gcoeff(M,i,j) = gel(c,j) = RgV_dotproduct_i(xi,gel(y,j),l);
+    gel(c,i) = RgV_dotproduct_i(xi,gel(y,i),l);
+  }
+  return M;
+}
+/* x~ * y */
+GEN
+RgM_transmul(GEN x, GEN y)
+{
+  long i, j, l, lx, ly = lg(y);
+  GEN M;
+  if (ly == 1) return cgetg(1,t_MAT);
+  lx = lg(x);
+  l = lgcols(y);
+  if (lgcols(x) != l) pari_err_OP("operation 'RgM_transmul'", x,y);
+  M = cgetg(ly, t_MAT);
+  for (i=1; i<ly; i++)
+  {
+    GEN yi = gel(y,i), c = cgetg(lx,t_COL);
+    gel(M,i) = c;
+    for (j=1; j<lx; j++) gel(c,j) = RgV_dotproduct_i(yi,gel(x,j),l);
+  }
+  return M;
+}
+
+GEN
+gram_matrix(GEN x)
+{
+  long i,j, l, lx = lg(x);
+  GEN M;
+  if (!is_matvec_t(typ(x))) pari_err_TYPE("gram",x);
+  if (lx == 1) return cgetg(1,t_MAT);
+  l = lgcols(x);
+  M = cgetg(lx,t_MAT);
+  for (i=1; i<lx; i++)
+  {
+    GEN xi = gel(x,i), c = cgetg(lx,t_COL);
+    gel(M,i) = c;
+    for (j=1; j<i; j++)
+      gcoeff(M,i,j) = gel(c,j) = RgV_dotproduct_i(xi,gel(x,j),l);
+    gel(c,i) = RgV_dotsquare(xi);
+  }
+  return M;
+}
+
+GEN
+RgM_sqr(GEN x)
+{
+  pari_sp av = avma;
+  long j, lx = lg(x);
+  GEN z;
+  if (lx == 1) return cgetg(1, t_MAT);
+  if (lx != lgcols(x)) pari_err_OP("operation 'RgM_mul'", x,x);
+  if (is_modular_sqr(x,&z)) return gerepileupto(av, z);
+  z = cgetg(lx, t_MAT);
+  for (j=1; j<lx; j++) gel(z,j) = RgM_RgC_mul_i(x, gel(x,j), lx, lx);
+  return z;
+}
+
+static GEN
+_RgM_add(void *E, GEN x, GEN y) { (void)E; return RgM_add(x, y); }
+
+static GEN
+_RgM_cmul(void *E, GEN P, long a, GEN x) { (void)E; return RgM_Rg_mul(x,gel(P,a+2)); }
+
+static GEN
+_RgM_sqr(void *E, GEN x) { (void) E; return RgM_sqr(x); }
+
+static GEN
+_RgM_mul(void *E, GEN x, GEN y) { (void) E; return RgM_mul(x, y); }
+
+static GEN
+_RgM_one(void *E) { long *n = (long*) E; return matid(*n); }
+
+static GEN
+_RgM_zero(void *E) { long *n = (long*) E; return zeromat(*n,*n); }
+
+static GEN
+_RgM_red(void *E, GEN x) { (void)E; return x; }
+
+static struct bb_algebra RgM_algebra = { _RgM_red,_RgM_add,_RgM_mul,_RgM_sqr,_RgM_one,_RgM_zero };
+
+/* generates the list of powers of x of degree 0,1,2,...,l*/
+GEN
+RgM_powers(GEN x, long l)
+{
+  long n = lg(x)-1;
+  return gen_powers(x,l,1,(void *) &n, &_RgM_sqr, &_RgM_mul, &_RgM_one);
+}
+
+GEN
+RgX_RgMV_eval(GEN Q, GEN x)
+{
+  long n = lg(x)>1 ? lg(gel(x,1))-1:0;
+  return gen_bkeval_powers(Q,degpol(Q),x,(void*)&n,&RgM_algebra,&_RgM_cmul);
+}
+
+GEN
+RgX_RgM_eval(GEN Q, GEN x)
+{
+  long n = lg(x)-1;
+  return gen_bkeval(Q,degpol(Q),x,1,(void*)&n,&RgM_algebra,&_RgM_cmul);
+}
+
+GEN
+RgC_Rg_div(GEN x, GEN y) {
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++) gel(z,i) = gdiv(gel(x,i),y);
+  return z;
+}
+GEN
+RgC_Rg_mul(GEN x, GEN y) {
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++) gel(z,i) = gmul(gel(x,i),y);
+  return z;
+}
+GEN
+RgV_Rg_mul(GEN x, GEN y) {
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_VEC);
+  for (i=1; i<lx; i++) gel(z,i) = gmul(gel(x,i),y);
+  return z;
+}
+GEN
+RgM_Rg_div(GEN X, GEN c) {
+  long i, j, h, l = lg(X);
+  GEN A = cgetg(l, t_MAT);
+  if (l == 1) return A;
+  h = lgcols(X);
+  for (j=1; j<l; j++)
+  {
+    GEN a = cgetg(h, t_COL), x = gel(X, j);
+    for (i = 1; i < h; i++) gel(a,i) = gdiv(gel(x,i), c);
+    gel(A,j) = a;
+  }
+  return A;
+}
+GEN
+RgM_Rg_mul(GEN X, GEN c) {
+  long i, j, h, l = lg(X);
+  GEN A = cgetg(l, t_MAT);
+  if (l == 1) return A;
+  h = lgcols(X);
+  for (j=1; j<l; j++)
+  {
+    GEN a = cgetg(h, t_COL), x = gel(X, j);
+    for (i = 1; i < h; i++) gel(a,i) = gmul(gel(x,i), c);
+    gel(A,j) = a;
+  }
+  return A;
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                    SCALAR TO MATRIX/VECTOR                       */
+/*                                                                  */
+/********************************************************************/
+/* fill the square nxn matrix equal to t*Id */
+static void
+fill_scalmat(GEN y, GEN t, long n)
+{
+  long i;
+  for (i = 1; i <= n; i++)
+  {
+    gel(y,i) = zerocol(n);
+    gcoeff(y,i,i) = t;
+  }
+}
+
+GEN
+scalarmat(GEN x, long n) {
+  GEN y = cgetg(n+1, t_MAT);
+  if (!n) return y;
+  fill_scalmat(y, gcopy(x), n); return y;
+}
+GEN
+scalarmat_shallow(GEN x, long n) {
+  GEN y = cgetg(n+1, t_MAT);
+  fill_scalmat(y, x, n); return y;
+}
+GEN
+scalarmat_s(long x, long n) {
+  GEN y = cgetg(n+1, t_MAT);
+  if (!n) return y;
+  fill_scalmat(y, stoi(x), n); return y;
+}
+GEN
+matid(long n) {
+  GEN y;
+  if (n < 0) pari_err_DOMAIN("matid", "size", "<", gen_0, stoi(n));
+  y = cgetg(n+1, t_MAT);
+  fill_scalmat(y, gen_1, n); return y;
+}
+
+INLINE GEN
+scalarcol_i(GEN x, long n, long c)
+{
+  long i;
+  GEN y = cgetg(n+1,t_COL);
+  if (!n) return y;
+  gel(y,1) = c? gcopy(x): x;
+  for (i=2; i<=n; i++) gel(y,i) = gen_0;
+  return y;
+}
+
+GEN
+scalarcol(GEN x, long n) { return scalarcol_i(x,n,1); }
+
+GEN
+scalarcol_shallow(GEN x, long n) { return scalarcol_i(x,n,0); }
+
+int
+RgM_isscalar(GEN x, GEN s)
+{
+  long i, j, lx = lg(x);
+
+  if (lx == 1) return 1;
+  if (lx != lgcols(x)) return 0;
+  if (!s) s = gcoeff(x,1,1);
+
+  for (j=1; j<lx; j++)
+  {
+    GEN c = gel(x,j);
+    for (i=1; i<j; )
+      if (!gequal0(gel(c,i++))) return 0;
+    /* i = j */
+      if (!gequal(gel(c,i++),s)) return 0;
+    for (   ; i<lx; )
+      if (!gequal0(gel(c,i++))) return 0;
+  }
+  return 1;
+}
+
+int
+RgM_isidentity(GEN x)
+{
+  long i,j, lx = lg(x);
+
+  if (lx == 1) return 1;
+  if (lx != lgcols(x)) return 0;
+  for (j=1; j<lx; j++)
+  {
+    GEN c = gel(x,j);
+    for (i=1; i<j; )
+      if (!gequal0(gel(c,i++))) return 0;
+    /* i = j */
+      if (!gequal1(gel(c,i++))) return 0;
+    for (   ; i<lx; )
+      if (!gequal0(gel(c,i++))) return 0;
+  }
+  return 1;
+}
+
+int
+RgM_isdiagonal(GEN x)
+{
+  long i,j, lx = lg(x);
+  if (lx == 1) return 1;
+  if (lx != lgcols(x)) return 0;
+
+  for (j=1; j<lx; j++)
+  {
+    GEN c = gel(x,j);
+    for (i=1; i<j; i++)
+      if (!gequal0(gel(c,i))) return 0;
+    for (i++; i<lx; i++)
+      if (!gequal0(gel(c,i))) return 0;
+  }
+  return 1;
+}
+int
+isdiagonal(GEN x)
+{
+  return (typ(x)==t_MAT) && RgM_isdiagonal(x);
+}
+
+/* returns the first index i<=n such that x=v[i] if it exists, 0 otherwise */
+long
+RgV_isin(GEN v, GEN x)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+    if (gequal(gel(v,i), x)) return i;
+  return 0;
+}
+
+GEN
+RgM_det_triangular(GEN mat)
+{
+  long i,l = lg(mat);
+  pari_sp av;
+  GEN s;
+
+  if (l<3) return l<2? gen_1: gcopy(gcoeff(mat,1,1));
+  av = avma; s = gcoeff(mat,1,1);
+  for (i=2; i<l; i++) s = gmul(s,gcoeff(mat,i,i));
+  return av==avma? gcopy(s): gerepileupto(av,s);
+}
+
diff --git a/src/basemath/RgX.c b/src/basemath/RgX.c
new file mode 100644
index 0000000..8b21d07
--- /dev/null
+++ b/src/basemath/RgX.c
@@ -0,0 +1,2213 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*                         GENERIC                                 */
+/*                                                                 */
+/*******************************************************************/
+
+/* Return optimal parameter l for the evaluation of n/m polynomials of degree d
+   Fractional values can be used if the evaluations are done with different
+   accuracies, and thus have different weights.
+ */
+long
+brent_kung_optpow(long d, long n, long m)
+{
+  long p, r;
+  long pold=1, rold=n*(d-1);
+  for(p=2; p<=d; p++)
+  {
+    r = m*(p-1) + n*((d-1)/p);
+    if (r<rold) { pold=p; rold=r; }
+  }
+  return pold;
+}
+
+static GEN
+gen_RgXQ_eval_powers(GEN P, GEN V, long a, long n, void *E, const struct bb_algebra *ff,
+                                           GEN cmul(void *E, GEN P, long a, GEN x))
+{
+  pari_sp av = avma, lim=stack_lim(av,2);
+  long i;
+  GEN z = cmul(E,P,a,ff->one(E));
+  for (i=1; i<=n; i++)
+  {
+    z = ff->add(E, z, cmul(E,P,a+i,gel(V,i+1)));
+    if (low_stack(lim, stack_lim(av,2)))
+      z = gerepileupto(av, z);
+  }
+  return ff->red(E,z);
+}
+
+/* Brent & Kung
+ * (Fast algorithms for manipulating formal power series, JACM 25:581-595, 1978)
+ *
+ * V as output by FpXQ_powers(x,l,T,p). For optimal performance, l is as given
+ * by brent_kung_optpow */
+GEN
+gen_bkeval_powers(GEN P, long d, GEN V, void *E, const struct bb_algebra *ff,
+                                     GEN cmul(void *E, GEN P, long a, GEN x))
+{
+  pari_sp av = avma, lim;
+  long l = lg(V)-1;
+  GEN z, u;
+
+  if (d < 0) return ff->zero(E);
+  if (d < l) return gerepileupto(av, gen_RgXQ_eval_powers(P,V,0,d,E,ff,cmul));
+  if (l<2) pari_err_DOMAIN("gen_RgX_bkeval_powers", "#powers", "<",gen_2,V);
+  d -= l;
+  z = gen_RgXQ_eval_powers(P,V,d+1,l-1,E,ff,cmul);
+  lim = stack_lim(av,2);
+  while (d >= l-1)
+  {
+    d -= l-1;
+    u = gen_RgXQ_eval_powers(P,V,d+1,l-2,E,ff,cmul);
+    z = ff->add(E,u, ff->mul(E,z,gel(V,l)));
+    if (low_stack(lim, stack_lim(av,2)))
+      z = gerepileupto(av, z);
+  }
+  u = gen_RgXQ_eval_powers(P,V,0,d,E,ff,cmul);
+  z = ff->add(E,u, ff->mul(E,z,gel(V,d+2)));
+  if (DEBUGLEVEL>=8)
+  {
+    long cnt = 1 + (d - l) / (l-1);
+    err_printf("RgX_RgXQV_eval: %ld RgXQ_mul [%ld]\n", cnt, l-1);
+  }
+  return gerepileupto(av, ff->red(E,z));
+}
+
+GEN
+gen_bkeval(GEN Q, long d, GEN x, int use_sqr, void *E, const struct bb_algebra *ff,
+                                      GEN cmul(void *E, GEN P, long a, GEN x))
+{
+  pari_sp av = avma;
+  GEN z, V;
+  long rtd;
+  if (d < 0) return ff->zero(E);
+  rtd = (long) sqrt((double)d);
+  V = gen_powers(x,rtd,use_sqr,E,ff->sqr,ff->mul,ff->one);
+  z = gen_bkeval_powers(Q, d, V, E, ff, cmul);
+  return gerepileupto(av, z);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         RgX                                     */
+/*                                                                 */
+/*******************************************************************/
+
+long
+RgX_equal(GEN x, GEN y)
+{
+  long i = lg(x);
+
+  if (i != lg(y)) return 0;
+  for (i--; i > 1; i--)
+    if (!gequal(gel(x,i),gel(y,i))) return 0;
+  return 1;
+}
+
+/* Returns 1 in the base ring over which x is defined */
+/* HACK: this also works for t_SER */
+GEN
+RgX_get_1(GEN x)
+{
+  GEN p, T;
+  long i, lx, tx = RgX_type(x, &p, &T, &lx);
+  if (RgX_type_is_composite(tx))
+    RgX_type_decode(tx, &i /*junk*/, &tx);
+  switch(tx)
+  {
+    case t_INTMOD: retmkintmod(gen_1, icopy(p));
+    case t_PADIC: return cvtop(gen_1, p, lx);
+    case t_FFELT: return FF_1(T);
+    default: return gen_1;
+  }
+}
+/* Returns 0 in the base ring over which x is defined */
+/* HACK: this also works for t_SER */
+GEN
+RgX_get_0(GEN x)
+{
+  GEN p, T;
+  long i, lx, tx = RgX_type(x, &p, &T, &lx);
+  if (RgX_type_is_composite(tx))
+    RgX_type_decode(tx, &i /*junk*/, &tx);
+  switch(tx)
+  {
+    case t_INTMOD: retmkintmod(gen_0, icopy(p));
+    case t_PADIC: return cvtop(gen_0, p, lx);
+    case t_FFELT: return FF_zero(T);
+    default: return gen_0;
+  }
+}
+
+GEN
+QX_ZXQV_eval(GEN P, GEN V, GEN dV)
+{
+  long i, n = degpol(P);
+  GEN z, dz, dP;
+  if (n < 0) return gen_0;
+  P = Q_remove_denom(P, &dP);
+  z = gel(P,2); if (n == 0) return icopy(z);
+  if (dV) z = mulii(dV, z); /* V[1] = dV */
+  z = ZX_Z_add_shallow(ZX_Z_mul(gel(V,2),gel(P,3)), z);
+  for (i=2; i<=n; i++) z = ZX_add(ZX_Z_mul(gel(V,i+1),gel(P,2+i)), z);
+  dz = mul_denom(dP, dV);
+  return dz? RgX_Rg_div(z, dz): z;
+}
+
+/* Return P(h * x), not memory clean */
+GEN
+RgX_unscale(GEN P, GEN h)
+{
+  long i, l = lg(P);
+  GEN hi = gen_1, Q = cgetg(l, t_POL);
+  Q[1] = P[1];
+  if (l == 2) return Q;
+  gel(Q,2) = gcopy(gel(P,2));
+  for (i=3; i<l; i++)
+  {
+    hi = gmul(hi,h);
+    gel(Q,i) = gmul(gel(P,i), hi);
+  }
+  return Q;
+}
+/* P a ZX, h a t_INT. Return P(h * x), not memory clean; optimize for h = -1 */
+GEN
+ZX_unscale(GEN P, GEN h)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l, t_POL);
+  Q[1] = P[1];
+  if (l == 2) return Q;
+  gel(Q,2) = gel(P,2);
+  if (l == 3) return Q;
+  if (equalim1(h))
+    for (i=3; i<l; i++)
+    {
+      gel(Q,i) = negi(gel(P,i));
+      if (++i == l) break;
+      gel(Q,i) = gel(P,i);
+    }
+  else
+  {
+    GEN hi = h;
+    gel(Q,3) = mulii(gel(P,3), hi);
+    for (i=4; i<l; i++)
+    {
+      hi = mulii(hi,h);
+      gel(Q,i) = mulii(gel(P,i), hi);
+    }
+  }
+  return Q;
+}
+/* P(h*X) / h, assuming h | P(0), i.e. the result is a ZX */
+GEN
+ZX_unscale_div(GEN P, GEN h)
+{
+  long i, l = lg(P);
+  GEN hi, Q = cgetg(l, t_POL);
+  Q[1] = P[1];
+  if (l == 2) return Q;
+  gel(Q,2) = diviiexact(gel(P,2), h);
+  if (l == 3) return Q;
+  gel(Q,3) = gel(P,3);
+  if (l == 4) return Q;
+  hi = h;
+  gel(Q,4) = mulii(gel(P,4), hi);
+  for (i=5; i<l; i++)
+  {
+    hi = mulii(hi,h);
+    gel(Q,i) = mulii(gel(P,i), hi);
+  }
+  return Q;
+}
+
+GEN
+RgXV_unscale(GEN v, GEN h)
+{
+  long i, l;
+  GEN w;
+  if (!h || isint1(h)) return v;
+  w = cgetg_copy(v, &l);
+  for (i=1; i<l; i++) gel(w,i) = RgX_unscale(gel(v,i), h);
+  return w;
+}
+
+/* Return h^degpol(P) P(x / h), not memory clean */
+GEN
+RgX_rescale(GEN P, GEN h)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l,t_POL), hi = h;
+  Q[l-1] = P[l-1];
+  for (i=l-2; i>=2; i--)
+  {
+    gel(Q,i) = gmul(gel(P,i), hi);
+    if (i == 2) break;
+    hi = gmul(hi,h);
+  }
+  Q[1] = P[1]; return Q;
+}
+
+/* A(X^d) --> A(X) */
+GEN
+RgX_deflate(GEN x0, long d)
+{
+  GEN z, y, x;
+  long i,id, dy, dx = degpol(x0);
+  if (d == 1 || dx <= 0) return leafcopy(x0);
+  dy = dx/d;
+  y = cgetg(dy+3, t_POL); y[1] = x0[1];
+  z = y + 2;
+  x = x0+ 2;
+  for (i=id=0; i<=dy; i++,id+=d) gel(z,i) = gel(x,id);
+  return y;
+}
+
+/* return x0(X^d) */
+GEN
+RgX_inflate(GEN x0, long d)
+{
+  long i, id, dy, dx = degpol(x0);
+  GEN x = x0 + 2, z, y;
+  if (dx <= 0) return leafcopy(x0);
+  dy = dx*d;
+  y = cgetg(dy+3, t_POL); y[1] = x0[1];
+  z = y + 2;
+  for (i=0; i<=dy; i++) gel(z,i) = gen_0;
+  for (i=id=0; i<=dx; i++,id+=d) gel(z,id) = gel(x,i);
+  return y;
+}
+
+/* return P(X + c) using destructive Horner, optimize for c = 1,-1 */
+GEN
+RgX_translate(GEN P, GEN c)
+{
+  pari_sp av = avma, lim;
+  GEN Q, *R;
+  long i, k, n;
+
+  if (!signe(P) || gequal0(c)) return RgX_copy(P);
+  Q = leafcopy(P);
+  R = (GEN*)(Q+2); n = degpol(P);
+  lim = stack_lim(av, 2);
+  if (gequal1(c))
+  {
+    for (i=1; i<=n; i++)
+    {
+      for (k=n-i; k<n; k++) R[k] = gadd(R[k], R[k+1]);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"TR_POL(1), i = %ld/%ld", i,n);
+        Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+      }
+    }
+  }
+  else if (gequalm1(c))
+  {
+    for (i=1; i<=n; i++)
+    {
+      for (k=n-i; k<n; k++) R[k] = gsub(R[k], R[k+1]);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"TR_POL(-1), i = %ld/%ld", i,n);
+        Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+      }
+    }
+  }
+  else
+  {
+    for (i=1; i<=n; i++)
+    {
+      for (k=n-i; k<n; k++) R[k] = gadd(R[k], gmul(c, R[k+1]));
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"TR_POL, i = %ld/%ld", i,n);
+        Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+      }
+    }
+  }
+  return gerepilecopy(av, Q);
+}
+
+/* return P(X + c) using destructive Horner, optimize for c = 1,-1 */
+GEN
+ZX_translate(GEN P, GEN c)
+{
+  pari_sp av = avma, lim;
+  GEN Q, *R;
+  long i, k, n;
+
+  if (!signe(P) || !signe(c)) return ZX_copy(P);
+  Q = leafcopy(P);
+  R = (GEN*)(Q+2); n = degpol(P);
+  lim = stack_lim(av, 2);
+  if (equali1(c))
+  {
+    for (i=1; i<=n; i++)
+    {
+      for (k=n-i; k<n; k++) R[k] = addii(R[k], R[k+1]);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"ZX_translate(1), i = %ld/%ld", i,n);
+        Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+      }
+    }
+  }
+  else if (equalim1(c))
+  {
+    for (i=1; i<=n; i++)
+    {
+      for (k=n-i; k<n; k++) R[k] = subii(R[k], R[k+1]);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"ZX_translate(-1), i = %ld/%ld", i,n);
+        Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+      }
+    }
+  }
+  else
+  {
+    for (i=1; i<=n; i++)
+    {
+      for (k=n-i; k<n; k++) R[k] = addii(R[k], mulii(c, R[k+1]));
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"ZX_translate, i = %ld/%ld", i,n);
+        Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+      }
+    }
+  }
+  return gerepilecopy(av, Q);
+}
+/* return lift( P(X + c) ) using Horner, c in R[y]/(T) */
+GEN
+RgXQX_translate(GEN P, GEN c, GEN T)
+{
+  pari_sp av = avma, lim;
+  GEN Q, *R;
+  long i, k, n;
+
+  if (!signe(P) || gequal0(c)) return RgX_copy(P);
+  Q = leafcopy(P);
+  R = (GEN*)(Q+2); n = degpol(P);
+  lim = stack_lim(av, 2);
+  for (i=1; i<=n; i++)
+  {
+    for (k=n-i; k<n; k++)
+    {
+      pari_sp av2 = avma;
+      R[k] = gerepileupto(av2, RgX_rem(gadd(R[k], gmul(c, R[k+1])), T));
+    }
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgXQX_translate, i = %ld/%ld", i,n);
+      Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+    }
+  }
+  return gerepilecopy(av, Q);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                          CONVERSIONS                           **/
+/**                       (not memory clean)                       **/
+/**                                                                **/
+/********************************************************************/
+/* to INT / FRAC / (POLMOD mod T), not memory clean because T not copied,
+ * but everything else is */
+static GEN
+QXQ_to_mod_copy(GEN x, GEN T)
+{
+  long d;
+  switch(typ(x))
+  {
+    case t_INT:  return icopy(x);
+    case t_FRAC: return gcopy(x);
+    case t_POL:
+      d = degpol(x);
+      if (d < 0) return gen_0;
+      if (d == 0) return gcopy(gel(x,2));
+      return mkpolmod(RgX_copy(x), T);
+    default: pari_err_TYPE("QXQ_to_mod",x);
+             return NULL;/* not reached */
+  }
+}
+/* pure shallow version */
+static GEN
+QXQ_to_mod(GEN x, GEN T)
+{
+  long d;
+  switch(typ(x))
+  {
+    case t_INT:
+    case t_FRAC: return x;
+    case t_POL:
+      d = degpol(x);
+      if (d < 0) return gen_0;
+      if (d == 0) return gel(x,2);
+      return mkpolmod(x, T);
+    default: pari_err_TYPE("QXQ_to_mod",x);
+             return NULL;/* not reached */
+  }
+}
+/* T a ZX, z lifted from (Q[Y]/(T(Y)))[X], apply QXQ_to_mod_copy to all coeffs.
+ * Not memory clean because T not copied, but everything else is */
+static GEN
+QXQX_to_mod(GEN z, GEN T)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l,t_POL);
+  for (i=2; i<l; i++) gel(x,i) = QXQ_to_mod_copy(gel(z,i), T);
+  x[1] = z[1]; return normalizepol_lg(x,l);
+}
+/* pure shallow version */
+GEN
+QXQX_to_mod_shallow(GEN z, GEN T)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l,t_POL);
+  for (i=2; i<l; i++) gel(x,i) = QXQ_to_mod(gel(z,i), T);
+  x[1] = z[1]; return normalizepol_lg(x,l);
+}
+/* Apply QXQX_to_mod to all entries. Memory-clean ! */
+GEN
+QXQXV_to_mod(GEN V, GEN T)
+{
+  long i, l = lg(V);
+  GEN z = cgetg(l, t_VEC); T = ZX_copy(T);
+  for (i=1;i<l; i++) gel(z,i) = QXQX_to_mod(gel(V,i), T);
+  return z;
+}
+/* Apply QXQ_to_mod_copy to all entries. Memory-clean ! */
+GEN
+QXQV_to_mod(GEN V, GEN T)
+{
+  long i, l = lg(V);
+  GEN z = cgetg(l, t_VEC); T = ZX_copy(T);
+  for (i=1;i<l; i++) gel(z,i) = QXQ_to_mod_copy(gel(V,i), T);
+  return z;
+}
+
+GEN
+RgX_renormalize_lg(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>1; i--)
+    if (! gequal0(gel(x,i))) break; /* _not_ isexactzero */
+  stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
+  setlg(x, i+1); setsigne(x, i != 1); return x;
+}
+
+GEN
+RgV_to_RgX(GEN x, long v)
+{
+  long i, k = lg(x);
+  GEN p;
+
+  while (--k && gequal0(gel(x,k)));
+  if (!k) return pol_0(v);
+  i = k+2; p = cgetg(i,t_POL);
+  p[1] = evalsigne(1) | evalvarn(v);
+  x--; for (k=2; k<i; k++) gel(p,k) = gel(x,k);
+  return p;
+}
+GEN
+RgV_to_RgX_reverse(GEN x, long v)
+{
+  long j, k, l = lg(x);
+  GEN p;
+
+  for (k = 2; k < l; k++)
+    if (!gequal0(gel(x,k))) break;
+  if (k == l) return pol_0(v);
+  k -= 2;
+  l -= k;
+  x += k;
+  p = cgetg(l+1,t_POL);
+  p[1] = evalsigne(1) | evalvarn(v);
+  for (j=2, k=l; j<=l; j++) gel(p,j) = gel(x,--k);
+  return p;
+}
+
+/* return the (N-dimensional) vector of coeffs of p */
+GEN
+RgX_to_RgV(GEN x, long N)
+{
+  long i, l;
+  GEN z;
+  l = lg(x)-1; x++;
+  if (l > N+1) l = N+1; /* truncate higher degree terms */
+  z = cgetg(N+1,t_COL);
+  for (i=1; i<l ; i++) gel(z,i) = gel(x,i);
+  for (   ; i<=N; i++) gel(z,i) = gen_0;
+  return z;
+}
+GEN
+Rg_to_RgV(GEN x, long N)
+{
+  return (typ(x) == t_POL)? RgX_to_RgV(x,N): scalarcol_shallow(x, N);
+}
+
+/* vector of polynomials (in v) whose coeffs are given by the columns of x */
+GEN
+RgM_to_RgXV(GEN x, long v)
+{
+  long j, lx = lg(x);
+  GEN y = cgetg(lx, t_VEC);
+  for (j=1; j<lx; j++) gel(y,j) = RgV_to_RgX(gel(x,j), v);
+  return y;
+}
+
+/* matrix whose entries are given by the coeffs of the polynomials in
+ * vector v (considered as degree n-1 polynomials) */
+GEN
+RgV_to_RgM(GEN v, long n)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_MAT);
+  for (j=1; j<N; j++) gel(y,j) = Rg_to_RgV(gel(v,j), n);
+  return y;
+}
+GEN
+RgXV_to_RgM(GEN v, long n)
+{
+  long j, N = lg(v);
+  GEN y = cgetg(N, t_MAT);
+  for (j=1; j<N; j++) gel(y,j) = RgX_to_RgV(gel(v,j), n);
+  return y;
+}
+
+/* polynomial (in v) of polynomials (in w) whose coeffs are given by the columns of x */
+GEN
+RgM_to_RgXX(GEN x, long v,long w)
+{
+  long j, lx = lg(x);
+  GEN y = cgetg(lx+1, t_POL);
+  y[1] = evalsigne(1) | evalvarn(v);
+  y++;
+  for (j=1; j<lx; j++) gel(y,j) = RgV_to_RgX(gel(x,j), w);
+  return normalizepol_lg(--y, lx+1);
+}
+
+/* matrix whose entries are given by the coeffs of the polynomial v in
+ * two variables (considered as degree n-1 polynomials) */
+GEN
+RgXX_to_RgM(GEN v, long n)
+{
+  long j, N = lg(v)-1;
+  GEN y = cgetg(N, t_MAT);
+  for (j=1; j<N; j++) gel(y,j) = Rg_to_RgV(gel(v,j+1), n);
+  return y;
+}
+
+/* P(X,Y) --> P(Y,X), n is an upper bound for deg_Y(P) */
+GEN
+RgXY_swapspec(GEN x, long n, long w, long nx)
+{
+  long j, ly = n+3;
+  GEN y = cgetg(ly, t_POL);
+  y[1] = evalsigne(1);
+  for (j=2; j<ly; j++)
+  {
+    long k;
+    GEN a = cgetg(nx+2,t_POL);
+    a[1] = evalsigne(1) | evalvarn(w);
+    for (k=0; k<nx; k++)
+    {
+      GEN xk = gel(x,k);
+      if (typ(xk)==t_POL)
+        gel(a,k+2) = j<lg(xk)? gel(xk,j): gen_0;
+      else
+        gel(a,k+2) = j==2 ? xk: gen_0;
+    }
+    gel(y,j) = normalizepol_lg(a, nx+2);
+  }
+  return normalizepol_lg(y,ly);
+}
+
+/* P(X,Y) --> P(Y,X), n is an upper bound for deg_Y(P) */
+GEN
+RgXY_swap(GEN x, long n, long w)
+{
+  GEN z = RgXY_swapspec(x+2, n, w, lgpol(x));
+  setvarn(z, varn(x)); return z;
+}
+
+/* return (x % X^n). Shallow */
+GEN
+RgX_modXn_shallow(GEN a, long n)
+{
+  long i, L, l = lg(a);
+  GEN  b;
+  if (l == 2 || !n) return pol_0(varn(a));
+  if (n < 0) pari_err_DOMAIN("RgX_modXn", "n", "<", gen_0, stoi(n));
+  L = n+2; if (L > l) L = l;
+  b = cgetg(L, t_POL); b[1] = a[1];
+  for (i=2; i<L; i++) gel(b,i) = gel(a,i);
+  return b;
+}
+
+/* return (x * X^n). Shallow */
+GEN
+RgX_shift_shallow(GEN a, long n)
+{
+  long i, l = lg(a);
+  GEN  b;
+  if (l == 2 || !n) return a;
+  l += n;
+  if (n < 0)
+  {
+    if (l <= 2) return pol_0(varn(a));
+    b = cgetg(l, t_POL); b[1] = a[1];
+    a -= n;
+    for (i=2; i<l; i++) gel(b,i) = gel(a,i);
+  } else {
+    b = cgetg(l, t_POL); b[1] = a[1];
+    a -= n; n += 2;
+    for (i=2; i<n; i++) gel(b,i) = gen_0;
+    for (   ; i<l; i++) gel(b,i) = gel(a,i);
+  }
+  return b;
+}
+/* return (x * X^n). */
+GEN
+RgX_shift(GEN a, long n)
+{
+  long i, l = lg(a);
+  GEN  b;
+  if (l == 2 || !n) return RgX_copy(a);
+  l += n;
+  if (n < 0)
+  {
+    if (l <= 2) return pol_0(varn(a));
+    b = cgetg(l, t_POL); b[1] = a[1];
+    a -= n;
+    for (i=2; i<l; i++) gel(b,i) = gcopy(gel(a,i));
+  } else {
+    b = cgetg(l, t_POL); b[1] = a[1];
+    a -= n; n += 2;
+    for (i=2; i<n; i++) gel(b,i) = gen_0;
+    for (   ; i<l; i++) gel(b,i) = gcopy(gel(a,i));
+  }
+  return b;
+}
+
+GEN
+RgX_rotate_shallow(GEN P, long k, long p)
+{
+  long i, l = lgpol(P);
+  GEN r;
+  if (signe(P)==0)
+    return pol_0(varn(P));
+  r = cgetg(p+2,t_POL); r[1] = P[1];
+  for(i=0; i<p; i++)
+  {
+    long s = 2+(i+k)%p;
+    gel(r,s) = i<l? gel(P,2+i): gen_0;
+  }
+  return RgX_renormalize(r);
+}
+
+GEN
+RgX_mulXn(GEN x, long d)
+{
+  pari_sp av;
+  GEN z;
+  long v;
+  if (d >= 0) return RgX_shift(x, d);
+  d = -d;
+  v = RgX_val(x);
+  if (v >= d) return RgX_shift(x, -d);
+  av = avma;
+  z = gred_rfrac_simple( RgX_shift_shallow(x, -v),
+                         monomial(gen_1, d - v, varn(x)));
+  return gerepileupto(av, z);
+}
+
+long
+RgX_val(GEN x)
+{
+  long i, lx = lg(x);
+  if (lx == 2) return LONG_MAX;
+  for (i = 2; i < lx; i++)
+    if (!isexactzero(gel(x,i))) break;
+  if (i == lx) i--; /* possible with non-rational zeros */
+  return i - 2;
+}
+long
+RgX_valrem(GEN x, GEN *Z)
+{
+  long v, i, lx = lg(x);
+  if (lx == 2) { *Z = pol_0(varn(x)); return LONG_MAX; }
+  for (i = 2; i < lx; i++)
+    if (!isexactzero(gel(x,i))) break;
+  if (i == lx) i--; /* possible with non-rational zeros */
+  v = i - 2;
+  *Z = RgX_shift_shallow(x, -v);
+  return v;
+}
+long
+RgX_valrem_inexact(GEN x, GEN *Z)
+{
+  long v;
+  if (!signe(x)) { if (Z) *Z = pol_0(varn(x)); return LONG_MAX; }
+  for (v = 0;; v++)
+    if (!gequal0(gel(x,2+v))) break;
+  if (Z) *Z = RgX_shift_shallow(x, -v);
+  return v;
+}
+
+GEN
+RgXQC_red(GEN P, GEN T)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l, t_COL);
+  for (i=1; i<l; i++) gel(Q,i) = grem(gel(P,i), T);
+  return Q;
+}
+
+GEN
+RgXQV_red(GEN P, GEN T)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(Q,i) = grem(gel(P,i), T);
+  return Q;
+}
+
+GEN
+RgXQX_red(GEN P, GEN T)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l, t_POL);
+  Q[1] = P[1];
+  for (i=2; i<l; i++) gel(Q,i) = grem(gel(P,i), T);
+  return normalizepol_lg(Q, l);
+}
+
+GEN
+RgX_deriv(GEN x)
+{
+  long i,lx = lg(x)-1;
+  GEN y;
+
+  if (lx<3) return pol_0(varn(x));
+  y = cgetg(lx,t_POL); gel(y,2) = gcopy(gel(x,3));
+  for (i=3; i<lx ; i++) gel(y,i) = gmulsg(i-1,gel(x,i+1));
+  y[1] = x[1]; return normalizepol_lg(y,i);
+}
+
+GEN
+RgX_recipspec_shallow(GEN x, long l, long n)
+{
+  long i;
+  GEN z=cgetg(n+2,t_POL)+2;
+  for(i=0; i<l; i++)
+    gel(z,n-i-1) = gel(x,i);
+  for(   ; i<n; i++)
+    gel(z, n-i-1) = gen_0;
+  return normalizepol_lg(z-2,n+2);
+}
+
+/* return coefficients s.t x = x_0 X^n + ... + x_n */
+GEN
+RgX_recip(GEN x)
+{
+  long lx, i, j;
+  GEN y = cgetg_copy(x, &lx);
+  y[1] = x[1]; for (i=2,j=lx-1; i<lx; i++,j--) gel(y,i) = gcopy(gel(x,j));
+  return normalizepol_lg(y,lx);
+}
+/* shallow version */
+GEN
+RgX_recip_shallow(GEN x)
+{
+  long lx, i, j;
+  GEN y = cgetg_copy(x, &lx);
+  y[1] = x[1]; for (i=2,j=lx-1; i<lx; i++,j--) gel(y,i) = gel(x,j);
+  return y;
+}
+/*******************************************************************/
+/*                                                                 */
+/*                      ADDITION / SUBTRACTION                     */
+/*                                                                 */
+/*******************************************************************/
+/* same variable */
+GEN
+RgX_add(GEN x, GEN y)
+{
+  long i, lx = lg(x), ly = lg(y);
+  GEN z;
+  if (ly <= lx) {
+    z = cgetg(lx,t_POL); z[1] = x[1];
+    for (i=2; i < ly; i++) gel(z,i) = gadd(gel(x,i),gel(y,i));
+    for (   ; i < lx; i++) gel(z,i) = gcopy(gel(x,i));
+    z = normalizepol_lg(z, lx);
+  } else {
+    z = cgetg(ly,t_POL); z[1] = y[1];
+    for (i=2; i < lx; i++) gel(z,i) = gadd(gel(x,i),gel(y,i));
+    for (   ; i < ly; i++) gel(z,i) = gcopy(gel(y,i));
+    z = normalizepol_lg(z, ly);
+  }
+  return z;
+}
+GEN
+RgX_sub(GEN x, GEN y)
+{
+  long i, lx = lg(x), ly = lg(y);
+  GEN z;
+  if (ly <= lx) {
+    z = cgetg(lx,t_POL); z[1] = x[1];
+    for (i=2; i < ly; i++) gel(z,i) = gsub(gel(x,i),gel(y,i));
+    for (   ; i < lx; i++) gel(z,i) = gcopy(gel(x,i));
+    z = normalizepol_lg(z, lx);
+  } else {
+    z = cgetg(ly,t_POL); z[1] = y[1];
+    for (i=2; i < lx; i++) gel(z,i) = gsub(gel(x,i),gel(y,i));
+    for (   ; i < ly; i++) gel(z,i) = gneg(gel(y,i));
+    z = normalizepol_lg(z, ly);
+  }
+  return z;
+}
+GEN
+RgX_neg(GEN x)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx, t_POL); y[1] = x[1];
+  for (i=2; i<lx; i++) gel(y,i) = gneg(gel(x,i));
+  return y;
+}
+
+GEN
+RgX_Rg_add(GEN y, GEN x)
+{
+  GEN z;
+  long lz = lg(y), i;
+  if (lz == 2) return scalarpol(x,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = gadd(gel(y,2),x);
+  for(i=3; i<lz; i++) gel(z,i) = gcopy(gel(y,i));
+  /* probably useless unless lz = 3, but cannot be skipped if y is
+   * an inexact 0 */
+  return normalizepol_lg(z,lz);
+}
+GEN
+RgX_Rg_add_shallow(GEN y, GEN x)
+{
+  GEN z;
+  long lz = lg(y), i;
+  if (lz == 2) return scalarpol(x,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = gadd(gel(y,2),x);
+  for(i=3; i<lz; i++) gel(z,i) = gel(y,i);
+  return z = normalizepol_lg(z,lz);
+}
+GEN
+RgX_Rg_sub(GEN y, GEN x)
+{
+  GEN z;
+  long lz = lg(y), i;
+  if (lz == 2)
+  { /* scalarpol(gneg(x),varn(y)) optimized */
+    long v = varn(y);
+    if (isrationalzero(x)) return pol_0(v);
+    z = cgetg(3,t_POL);
+    z[1] = gequal0(x)? evalvarn(v)
+                   : evalvarn(v) | evalsigne(1);
+    gel(z,2) = gneg(x); return z;
+  }
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = gsub(gel(y,2),x);
+  for(i=3; i<lz; i++) gel(z,i) = gcopy(gel(y,i));
+  return z = normalizepol_lg(z,lz);
+}
+GEN
+Rg_RgX_sub(GEN x, GEN y)
+{
+  GEN z;
+  long lz = lg(y), i;
+  if (lz == 2) return scalarpol(x,varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = gsub(x, gel(y,2));
+  for(i=3; i<lz; i++) gel(z,i) = gneg(gel(y,i));
+  return z = normalizepol_lg(z,lz);
+}
+/*******************************************************************/
+/*                                                                 */
+/*                  KARATSUBA MULTIPLICATION                       */
+/*                                                                 */
+/*******************************************************************/
+#if 0
+/* to debug Karatsuba-like routines */
+GEN
+zx_debug_spec(GEN x, long nx)
+{
+  GEN z = cgetg(nx+2,t_POL);
+  long i;
+  for (i=0; i<nx; i++) gel(z,i+2) = stoi(x[i]);
+  z[1] = evalsigne(1); return z;
+}
+
+GEN
+RgX_debug_spec(GEN x, long nx)
+{
+  GEN z = cgetg(nx+2,t_POL);
+  long i;
+  for (i=0; i<nx; i++) z[i+2] = x[i];
+  z[1] = evalsigne(1); return z;
+}
+#endif
+
+/* generic multiplication */
+
+static GEN
+addpol(GEN x, GEN y, long lx, long ly)
+{
+  long i,lz;
+  GEN z;
+
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx+2; z = cgetg(lz,t_POL) + 2;
+  for (i=0; i<ly; i++) gel(z,i) = gadd(gel(x,i),gel(y,i));
+  for (   ; i<lx; i++) gel(z,i) = gel(x,i);
+  z -= 2; z[1]=0; return normalizepol_lg(z, lz);
+}
+
+static GEN
+addpolcopy(GEN x, GEN y, long lx, long ly)
+{
+  long i,lz;
+  GEN z;
+
+  if (ly>lx) swapspec(x,y, lx,ly);
+  lz = lx+2; z = cgetg(lz,t_POL) + 2;
+  for (i=0; i<ly; i++) gel(z,i) = gadd(gel(x,i),gel(y,i));
+  for (   ; i<lx; i++) gel(z,i) = gcopy(gel(x,i));
+  z -= 2; z[1]=0; return normalizepol_lg(z, lz);
+}
+
+/* Return the vector of coefficients of x, where we replace rational 0s by NULL
+ * [ to speed up basic operation s += x[i]*y[j] ]. We create a proper
+ * t_VECSMALL, to hold this, which can be left on stack: gerepile
+ * will not crash on it. The returned vector itself is not a proper GEN,
+ * we access the coefficients as x[i], i = 0..deg(x) */
+static GEN
+RgXspec_kill0(GEN x, long lx)
+{
+  GEN z = cgetg(lx+1, t_VECSMALL) + 1; /* inhibit gerepile-wise */
+  long i;
+  for (i=0; i <lx; i++)
+  {
+    GEN c = gel(x,i);
+    z[i] = (long)(isrationalzero(c)? NULL: c);
+  }
+  return z;
+}
+
+INLINE GEN
+RgX_mulspec_basecase_limb(GEN x, GEN y, long a, long b)
+{
+  pari_sp av = avma;
+  GEN s = NULL;
+  long i;
+
+  for (i=a; i<b; i++)
+    if (gel(y,i) && gel(x,-i))
+    {
+      GEN t = gmul(gel(y,i), gel(x,-i));
+      s = s? gadd(s, t): t;
+    }
+  return s? gerepileupto(av, s): gen_0;
+}
+
+/* assume nx >= ny > 0, return x * y * t^v */
+static GEN
+RgX_mulspec_basecase(GEN x, GEN y, long nx, long ny, long v)
+{
+  long i, lz, nz;
+  GEN z;
+
+  x = RgXspec_kill0(x,nx);
+  y = RgXspec_kill0(y,ny);
+  lz = nx + ny + 1; nz = lz-2;
+  lz += v;
+  z = cgetg(lz, t_POL) + 2; /* x:y:z [i] = term of degree i */
+  for (i=0; i<v; i++) gel(z++, 0) = gen_0;
+  for (i=0; i<ny; i++)gel(z,i) = RgX_mulspec_basecase_limb(x+i,y, 0, i+1);
+  for (  ; i<nx; i++) gel(z,i) = RgX_mulspec_basecase_limb(x+i,y, 0,ny);
+  for (  ; i<nz; i++) gel(z,i) = RgX_mulspec_basecase_limb(x+i,y, i-nx+1,ny);
+  z -= v+2; z[1] = 0; return normalizepol_lg(z, lz);
+}
+
+/* return (x * X^d) + y. Assume d > 0 */
+GEN
+addmulXn(GEN x, GEN y, long d)
+{
+  GEN xd, yd, zd;
+  long a, lz, nx, ny;
+
+  if (!signe(x)) return y;
+  ny = lgpol(y);
+  nx = lgpol(x);
+  zd = (GEN)avma;
+  x += 2; y += 2; a = ny-d;
+  if (a <= 0)
+  {
+    lz = nx+d+2;
+    (void)new_chunk(lz); xd = x+nx; yd = y+ny;
+    while (xd > x) gel(--zd,0) = gel(--xd,0);
+    x = zd + a;
+    while (zd > x) gel(--zd,0) = gen_0;
+  }
+  else
+  {
+    xd = new_chunk(d); yd = y+d;
+    x = addpol(x,yd, nx,a);
+    lz = (a>nx)? ny+2: lg(x)+d;
+    x += 2; while (xd > x) *--zd = *--xd;
+  }
+  while (yd > y) *--zd = *--yd;
+  *--zd = evalsigne(1);
+  *--zd = evaltyp(t_POL) | evallg(lz); return zd;
+}
+
+GEN
+addshiftpol(GEN x, GEN y, long d)
+{
+  long v = varn(x);
+  x = addmulXn(x,y,d);
+  setvarn(x,v); return x;
+}
+
+/* as above, producing a clean malloc */
+static GEN
+addmulXncopy(GEN x, GEN y, long d)
+{
+  GEN xd, yd, zd;
+  long a, lz, nx, ny;
+
+  if (!signe(x)) return RgX_copy(y);
+  nx = lgpol(x);
+  ny = lgpol(y);
+  zd = (GEN)avma;
+  x += 2; y += 2; a = ny-d;
+  if (a <= 0)
+  {
+    lz = nx+d+2;
+    (void)new_chunk(lz); xd = x+nx; yd = y+ny;
+    while (xd > x) gel(--zd,0) = gcopy(gel(--xd,0));
+    x = zd + a;
+    while (zd > x) gel(--zd,0) = gen_0;
+  }
+  else
+  {
+    xd = new_chunk(d); yd = y+d;
+    x = addpolcopy(x,yd, nx,a);
+    lz = (a>nx)? ny+2: lg(x)+d;
+    x += 2; while (xd > x) *--zd = *--xd;
+  }
+  while (yd > y) gel(--zd,0) = gcopy(gel(--yd,0));
+  *--zd = evalsigne(1);
+  *--zd = evaltyp(t_POL) | evallg(lz); return zd;
+}
+
+/* return x * y mod t^n */
+static GEN
+RgX_mullow_basecase(GEN x, GEN y, long n)
+{
+  long i, lz = n+2, lx = lgpol(x), ly = lgpol(y);
+  GEN z;
+  if (lx < 0) return pol_0(varn(x));
+  if (ly < 0) return pol_0(varn(x));
+  z = cgetg(lz, t_POL) + 2;
+  x+=2; if (lx > n) lx = n;
+  y+=2; if (ly > n) ly = n;
+  z[-1] = x[-1];
+  if (ly > lx) { swap(x,y); lswap(lx,ly); }
+  x = RgXspec_kill0(x, lx);
+  y = RgXspec_kill0(y, ly);
+  /* x:y:z [i] = term of degree i */
+  for (i=0;i<ly; i++) gel(z,i) = RgX_mulspec_basecase_limb(x+i,y, 0,i+1);
+  for (  ; i<lx; i++) gel(z,i) = RgX_mulspec_basecase_limb(x+i,y, 0,ly);
+  for (  ; i<n; i++)  gel(z,i) = RgX_mulspec_basecase_limb(x+i,y, i-lx+1,ly);
+  return normalizepol_lg(z - 2, lz);
+}
+/* Mulders / Karatsuba product f*g mod t^n (Hanrot-Zimmermann variant) */
+GEN
+RgX_mullow(GEN f, GEN g, long n)
+{
+  pari_sp av = avma;
+  GEN fe,fo, ge,go, l,h,m;
+  long n0, n1;
+  if (degpol(f) + degpol(g) < n) return RgX_mul(f,g);
+  if (n < 80) return RgX_mullow_basecase(f,g,n);
+  n0 = n>>1; n1 = n-n0;
+  RgX_even_odd(f, &fe, &fo);
+  RgX_even_odd(g, &ge, &go);
+  l = RgX_mullow(fe,ge,n1);
+  h = RgX_mullow(fo,go,n0);
+  m = RgX_sub(RgX_mullow(RgX_add(fe,fo),RgX_add(ge,go),n0), RgX_add(l,h));
+  /* n1-1 <= n0 <= n1, deg l,m <= n1-1, deg h <= n0-1
+   * result is t^2 h(t^2) + t m(t^2) + l(t^2) */
+  l = RgX_inflate(l,2); /* deg l <= 2n1 - 2 <= n-1 */
+  /* deg(t m(t^2)) <= 2n1 - 1 <= n, truncate to < n */
+  if (2*degpol(m)+1 == n) m = normalizepol_lg(m, lg(m)-1);
+  m = RgX_inflate(m,2);
+  /* deg(t^2 h(t^2)) <= 2n0 <= n, truncate to < n */
+  if (2*degpol(h)+2 == n) h = normalizepol_lg(h, lg(h)-1);
+  h = RgX_inflate(h,2);
+  h = addmulXncopy(addmulXn(h,m,1), l,1);
+  setvarn(h, varn(f)); return gerepileupto(av, h);
+}
+
+/* fast product (Karatsuba) of polynomials a,b. These are not real GENs, a+2,
+ * b+2 were sent instead. na, nb = number of terms of a, b.
+ * Only c, c0, c1, c2 are genuine GEN.
+ */
+GEN
+RgX_mulspec(GEN a, GEN b, long na, long nb)
+{
+  GEN a0, c, c0;
+  long n0, n0a, i, v = 0;
+  pari_sp av;
+
+  while (na && isrationalzero(gel(a,0))) { a++; na--; v++; }
+  while (nb && isrationalzero(gel(b,0))) { b++; nb--; v++; }
+  if (na < nb) swapspec(a,b, na,nb);
+  if (!nb) return pol_0(0);
+
+  if (nb < RgX_MUL_LIMIT) return RgX_mulspec_basecase(a,b,na,nb, v);
+  RgX_shift_inplace_init(v);
+  i = (na>>1); n0 = na-i; na = i;
+  av = avma; a0 = a+n0; n0a = n0;
+  while (n0a && isrationalzero(gel(a,n0a-1))) n0a--;
+
+  if (nb > n0)
+  {
+    GEN b0,c1,c2;
+    long n0b;
+
+    nb -= n0; b0 = b+n0; n0b = n0;
+    while (n0b && isrationalzero(gel(b,n0b-1))) n0b--;
+    c = RgX_mulspec(a,b,n0a,n0b);
+    c0 = RgX_mulspec(a0,b0, na,nb);
+
+    c2 = addpol(a0,a, na,n0a);
+    c1 = addpol(b0,b, nb,n0b);
+
+    c1 = RgX_mulspec(c1+2,c2+2, lgpol(c1),lgpol(c2));
+    c2 = RgX_sub(c1, RgX_add(c0,c));
+    c0 = addmulXn(c0, c2, n0);
+  }
+  else
+  {
+    c = RgX_mulspec(a,b,n0a,nb);
+    c0 = RgX_mulspec(a0,b,na,nb);
+  }
+  c0 = addmulXncopy(c0,c,n0);
+  return RgX_shift_inplace(gerepileupto(av,c0), v);
+}
+
+INLINE GEN
+RgX_sqrspec_basecase_limb(GEN x, long a, long i)
+{
+  pari_sp av = avma;
+  GEN s = NULL;
+  long j, l = (i+1)>>1;
+  for (j=a; j<l; j++)
+  {
+    GEN xj = gel(x,j), xx = gel(x,i-j);
+    if (xj && xx)
+    {
+      GEN t = gmul(xj, xx);
+      s = s? gadd(s, t): t;
+    }
+  }
+  if (s) s = gshift(s,1);
+  if ((i&1) == 0)
+  {
+    GEN t = gel(x, i>>1);
+    if (t) {
+      t = gsqr(t);
+      s = s? gadd(s, t): t;
+    }
+  }
+  return s? gerepileupto(av,s): gen_0;
+}
+static GEN
+RgX_sqrspec_basecase(GEN x, long nx, long v)
+{
+  long i, lz, nz;
+  GEN z;
+
+  if (!nx) return pol_0(0);
+  x = RgXspec_kill0(x,nx);
+  lz = (nx << 1) + 1, nz = lz-2;
+  lz += v;
+  z = cgetg(lz,t_POL) + 2;
+  for (i=0; i<v; i++) gel(z++, 0) = gen_0;
+  for (i=0; i<nx; i++)gel(z,i) = RgX_sqrspec_basecase_limb(x, 0, i);
+  for (  ; i<nz; i++) gel(z,i) = RgX_sqrspec_basecase_limb(x, i-nx+1, i);
+  z -= v+2; z[1] = 0; return normalizepol_lg(z, lz);
+}
+/* return x^2 mod t^n */
+static GEN
+RgX_sqrlow_basecase(GEN x, long n)
+{
+  long i, lz = n+2, lx = lgpol(x);
+  GEN z;
+  if (lx < 0) return pol_0(varn(x));
+  z = cgetg(lz, t_POL);
+  z[1] = x[1];
+  x+=2; if (lx > n) lx = n;
+  x = RgXspec_kill0(x,lx);
+  z+=2;/* x:z [i] = term of degree i */
+  for (i=0;i<lx; i++) gel(z,i) = RgX_sqrspec_basecase_limb(x, 0, i);
+  for (  ; i<n; i++)  gel(z,i) = RgX_sqrspec_basecase_limb(x, i-lx+1, i);
+  z -= 2; return normalizepol_lg(z, lz);
+}
+/* Mulders / Karatsuba product f^2 mod t^n (Hanrot-Zimmermann variant) */
+GEN
+RgX_sqrlow(GEN f, long n)
+{
+  pari_sp av = avma;
+  GEN fe,fo, l,h,m;
+  long n0, n1;
+  if (2*degpol(f) < n) return RgX_sqr(f);
+  if (n < 80) return RgX_sqrlow_basecase(f,n);
+  n0 = n>>1; n1 = n-n0;
+  RgX_even_odd(f, &fe, &fo);
+  l = RgX_sqrlow(fe,n1);
+  h = RgX_sqrlow(fo,n0);
+  m = RgX_sub(RgX_sqrlow(RgX_add(fe,fo),n0), RgX_add(l,h));
+  /* n1-1 <= n0 <= n1, deg l,m <= n1-1, deg h <= n0-1
+   * result is t^2 h(t^2) + t m(t^2) + l(t^2) */
+  l = RgX_inflate(l,2); /* deg l <= 2n1 - 2 <= n-1 */
+  /* deg(t m(t^2)) <= 2n1 - 1 <= n, truncate to < n */
+  if (2*degpol(m)+1 == n) m = normalizepol_lg(m, lg(m)-1);
+  m = RgX_inflate(m,2);
+  /* deg(t^2 h(t^2)) <= 2n0 <= n, truncate to < n */
+  if (2*degpol(h)+2 == n) h = normalizepol_lg(h, lg(h)-1);
+  h = RgX_inflate(h,2);
+  h = addmulXncopy(addmulXn(h,m,1), l,1);
+  setvarn(h, varn(f)); return gerepileupto(av, h);
+}
+
+GEN
+RgX_sqrspec(GEN a, long na)
+{
+  GEN a0, c, c0, c1;
+  long n0, n0a, i, v = 0;
+  pari_sp av;
+
+  while (na && isrationalzero(gel(a,0))) { a++; na--; v += 2; }
+  if (na<RgX_SQR_LIMIT) return RgX_sqrspec_basecase(a, na, v);
+  RgX_shift_inplace_init(v);
+  i = (na>>1); n0 = na-i; na = i;
+  av = avma; a0 = a+n0; n0a = n0;
+  while (n0a && isrationalzero(gel(a,n0a-1))) n0a--;
+
+  c = RgX_sqrspec(a,n0a);
+  c0 = RgX_sqrspec(a0,na);
+  c1 = gmul2n(RgX_mulspec(a0,a, na,n0a), 1);
+  c0 = addmulXn(c0,c1, n0);
+  c0 = addmulXncopy(c0,c,n0);
+  return RgX_shift_inplace(gerepileupto(av,c0), v);
+}
+
+/* (X^a + A)(X^b + B) - X^(a+b), where deg A < a, deg B < b */
+GEN
+RgX_mul_normalized(GEN A, long a, GEN B, long b)
+{
+  GEN z = RgX_mul(A, B);
+  if (a < b)
+    z = addmulXn(addmulXn(A, B, b-a), z, a);
+  else if (a > b)
+    z = addmulXn(addmulXn(B, A, a-b), z, b);
+  else
+    z = addmulXn(RgX_add(A, B), z, a);
+  setvarn(z,varn(A)); return z;
+}
+
+GEN
+RgX_mul(GEN x, GEN y)
+{
+  GEN z = RgX_mulspec(y+2, x+2, lgpol(y), lgpol(x));
+  setvarn(z,varn(x)); return z;
+}
+
+GEN
+RgX_sqr(GEN x)
+{
+  GEN z = RgX_sqrspec(x+2, lgpol(x));
+  setvarn(z,varn(x)); return z;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                               DIVISION                          */
+/*                                                                 */
+/*******************************************************************/
+GEN
+RgX_Rg_divexact(GEN x, GEN y) {
+  long i, lx;
+  GEN z;
+  if (typ(y) == t_INT && is_pm1(y))
+    return signe(y) < 0 ? RgX_neg(x): RgX_copy(x);
+  z = cgetg_copy(x, &lx); z[1] = x[1];
+  for (i=2; i<lx; i++) gel(z,i) = gdivexact(gel(x,i),y);
+  return z;
+}
+GEN
+RgX_Rg_div(GEN x, GEN y) {
+  long i, lx;
+  GEN z = cgetg_copy(x, &lx); z[1] = x[1];
+  for (i=2; i<lx; i++) gel(z,i) = gdiv(gel(x,i),y);
+  return normalizepol_lg(z, lx);
+}
+GEN
+RgX_divs(GEN x, long y) {
+  long i, lx;
+  GEN z = cgetg_copy(x, &lx); z[1] = x[1];
+  for (i=2; i<lx; i++) gel(z,i) = gdivgs(gel(x,i),y);
+  return normalizepol_lg(z, lx);
+}
+GEN
+RgX_div_by_X_x(GEN a, GEN x, GEN *r)
+{
+  long l = lg(a), i;
+  GEN a0, z0, z = cgetg(l-1, t_POL);
+  z[1] = a[1];
+  a0 = a + l-1;
+  z0 = z + l-2; *z0 = *a0--;
+  for (i=l-3; i>1; i--) /* z[i] = a[i+1] + x*z[i+1] */
+  {
+    GEN t = gadd(gel(a0--,0), gmul(x, gel(z0--,0)));
+    gel(z0,0) = t;
+  }
+  if (r) *r = gadd(gel(a0,0), gmul(x, gel(z0,0)));
+  return z;
+}
+/* Polynomial division x / y:
+ *   if z = ONLY_REM  return remainder, otherwise return quotient
+ *   if z != NULL set *z to remainder
+ *   *z is the last object on stack (and thus can be disposed of with cgiv
+ *   instead of gerepile) */
+/* assume, typ(x) = typ(y) = t_POL, same variable */
+GEN
+RgX_divrem(GEN x, GEN y, GEN *pr)
+{
+  pari_sp avy, av, av1;
+  long dx,dy,dz,i,j,sx,lr;
+  GEN z,p1,p2,rem,y_lead,mod;
+  GEN (*f)(GEN,GEN);
+
+  if (!signe(y)) pari_err_INV("RgX_divrem",y);
+
+  dy = degpol(y);
+  y_lead = gel(y,dy+2);
+  if (gequal0(y_lead)) /* normalize denominator if leading term is 0 */
+  {
+    pari_warn(warner,"normalizing a polynomial with 0 leading term");
+    for (dy--; dy>=0; dy--)
+    {
+      y_lead = gel(y,dy+2);
+      if (!gequal0(y_lead)) break;
+    }
+  }
+  if (!dy) /* y is constant */
+  {
+    if (pr == ONLY_REM) return pol_0(varn(x));
+    z = RgX_Rg_div(x, y_lead);
+    if (pr == ONLY_DIVIDES) return z;
+    if (pr) *pr = pol_0(varn(x));
+    return z;
+  }
+  dx = degpol(x);
+  if (dx < dy)
+  {
+    if (pr == ONLY_REM) return RgX_copy(x);
+    if (pr == ONLY_DIVIDES) return signe(x)? NULL: pol_0(varn(x));
+    z = pol_0(varn(x));
+    if (pr) *pr = RgX_copy(x);
+    return z;
+  }
+
+  /* x,y in R[X], y non constant */
+  av = avma;
+  switch(typ(y_lead))
+  {
+    case t_REAL:
+      y_lead = ginv(y_lead);
+      f = gmul; mod = NULL;
+      break;
+    case t_INTMOD:
+    case t_POLMOD: y_lead = ginv(y_lead);
+      f = gmul; mod = gmodulo(gen_1, gel(y_lead,1));
+      break;
+    default: if (gequal1(y_lead)) y_lead = NULL;
+      f = gdiv; mod = NULL;
+  }
+
+  if (y_lead == NULL)
+    p2 = gel(x,dx+2);
+  else {
+    for(;;) {
+      p2 = f(gel(x,dx+2),y_lead);
+      p2 = simplify_shallow(p2);
+      if (!isexactzero(p2) || (--dx < 0)) break;
+    }
+    if (dx < dy) /* leading coeff of x was in fact zero */
+    {
+      if (pr == ONLY_DIVIDES) {
+        avma = av;
+        return (dx < 0)? pol_0(varn(x)) : NULL;
+      }
+      if (pr == ONLY_REM)
+      {
+        if (dx < 0)
+          return gerepilecopy(av, scalarpol(p2, varn(x)));
+        else
+        {
+          GEN t;
+          avma = av;
+          t = cgetg(dx + 3, t_POL); t[1] = x[1];
+          for (i = 2; i < dx + 3; i++) gel(t,i) = gcopy(gel(x,i));
+          return t;
+        }
+      }
+      if (pr) /* cf ONLY_REM above */
+      {
+        if (dx < 0)
+        {
+          p2 = gclone(p2);
+          avma = av;
+          z = pol_0(varn(x));
+          x = scalarpol(p2, varn(x));
+          gunclone(p2);
+        }
+        else
+        {
+          GEN t;
+          avma = av;
+          z = pol_0(varn(x));
+          t = cgetg(dx + 3, t_POL); t[1] = x[1];
+          for (i = 2; i < dx + 3; i++) gel(t,i) = gcopy(gel(x,i));
+          x = t;
+        }
+        *pr = x;
+      }
+      else
+      {
+        avma = av;
+        z = pol_0(varn(x));
+      }
+      return z;
+    }
+  }
+  /* dx >= dy */
+  avy = avma;
+  dz = dx-dy;
+  z = cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2;
+  z += 2;
+  y += 2;
+  gel(z,dz) = gcopy(p2);
+
+  for (i=dx-1; i>=dy; i--)
+  {
+    av1=avma; p1=gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++) p1 = gsub(p1, gmul(gel(z,j),gel(y,i-j)));
+    if (y_lead) p1 = simplify(f(p1,y_lead));
+
+    if (isrationalzero(p1)) { avma=av1; p1 = gen_0; }
+    else
+      p1 = avma==av1? gcopy(p1): gerepileupto(av1,p1);
+    gel(z,i-dy) = p1;
+  }
+  if (!pr) return gerepileupto(av,z-2);
+
+  rem = (GEN)avma; av1 = (pari_sp)new_chunk(dx+3);
+  for (sx=0; ; i--)
+  {
+    p1 = gel(x,i);
+    /* we always enter this loop at least once */
+    for (j=0; j<=i && j<=dz; j++) p1 = gsub(p1, gmul(gel(z,j),gel(y,i-j)));
+    if (mod && avma==av1) p1 = gmul(p1,mod);
+    if (!gequal0(p1)) { sx = 1; break; } /* remainder is non-zero */
+    if (!isexactzero(p1)) break;
+    if (!i) break;
+    avma=av1;
+  }
+  if (pr == ONLY_DIVIDES)
+  {
+    if (sx) { avma=av; return NULL; }
+    avma = (pari_sp)rem;
+    return gerepileupto(av,z-2);
+  }
+  lr=i+3; rem -= lr;
+  if (avma==av1) { avma = (pari_sp)rem; p1 = gcopy(p1); }
+  else p1 = gerepileupto((pari_sp)rem,p1);
+  rem[0] = evaltyp(t_POL) | evallg(lr);
+  rem[1] = z[-1];
+  rem += 2;
+  gel(rem,i) = p1;
+  for (i--; i>=0; i--)
+  {
+    av1=avma; p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++) p1 = gsub(p1, gmul(gel(z,j),gel(y,i-j)));
+    if (mod && avma==av1) p1 = gmul(p1,mod);
+    gel(rem,i) = avma==av1? gcopy(p1):gerepileupto(av1,p1);
+  }
+  rem -= 2;
+  if (!sx) (void)normalizepol_lg(rem, lr);
+  if (pr == ONLY_REM) return gerepileupto(av,rem);
+  z -= 2;
+  {
+    GEN *gptr[2]; gptr[0]=&z; gptr[1]=&rem;
+    gerepilemanysp(av,avy,gptr,2); *pr = rem; return z;
+  }
+}
+
+/* x and y in (R[Y]/T)[X]  (lifted), T in R[Y]. y preferably monic */
+GEN
+RgXQX_divrem(GEN x, GEN y, GEN T, GEN *pr)
+{
+  long vx, dx, dy, dz, i, j, sx, lr;
+  pari_sp av0, av, tetpil;
+  GEN z,p1,rem,lead;
+
+  if (!signe(y)) pari_err_INV("RgXQX_divrem",y);
+  vx = varn(x);
+  dx = degpol(x);
+  dy = degpol(y);
+  if (dx < dy)
+  {
+    if (pr)
+    {
+      av0 = avma; x = RgXQX_red(x, T);
+      if (pr == ONLY_DIVIDES) { avma=av0; return signe(x)? NULL: gen_0; }
+      if (pr == ONLY_REM) return x;
+      *pr = x;
+    }
+    return pol_0(vx);
+  }
+  lead = leading_term(y);
+  if (!dy) /* y is constant */
+  {
+    if (pr && pr != ONLY_DIVIDES)
+    {
+      if (pr == ONLY_REM) return pol_0(vx);
+      *pr = pol_0(vx);
+    }
+    if (gequal1(lead)) return RgX_copy(x);
+    av0 = avma; x = gmul(x, ginvmod(lead,T)); tetpil = avma;
+    return gerepile(av0,tetpil,RgXQX_red(x,T));
+  }
+  av0 = avma; dz = dx-dy;
+  lead = gequal1(lead)? NULL: gclone(ginvmod(lead,T));
+  avma = av0;
+  z = cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2; y += 2; z += 2;
+
+  p1 = gel(x,dx); av = avma;
+  gel(z,dz) = lead? gerepileupto(av, grem(gmul(p1,lead), T)): gcopy(p1);
+  for (i=dx-1; i>=dy; i--)
+  {
+    av=avma; p1=gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++) p1 = gsub(p1, gmul(gel(z,j),gel(y,i-j)));
+    if (lead) p1 = gmul(grem(p1, T), lead);
+    tetpil=avma; gel(z,i-dy) = gerepile(av,tetpil, grem(p1, T));
+  }
+  if (!pr) { if (lead) gunclone(lead); return z-2; }
+
+  rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
+  for (sx=0; ; i--)
+  {
+    p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++) p1 = gsub(p1, gmul(gel(z,j),gel(y,i-j)));
+    tetpil=avma; p1 = grem(p1, T); if (!gequal0(p1)) { sx = 1; break; }
+    if (!i) break;
+    avma=av;
+  }
+  if (pr == ONLY_DIVIDES)
+  {
+    if (lead) gunclone(lead);
+    if (sx) { avma=av0; return NULL; }
+    avma = (pari_sp)rem; return z-2;
+  }
+  lr=i+3; rem -= lr;
+  rem[0] = evaltyp(t_POL) | evallg(lr);
+  rem[1] = z[-1];
+  p1 = gerepile((pari_sp)rem,tetpil,p1);
+  rem += 2; gel(rem,i) = p1;
+  for (i--; i>=0; i--)
+  {
+    av=avma; p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = gsub(p1, gmul(gel(z,j),gel(y,i-j)));
+    tetpil=avma; gel(rem,i) = gerepile(av,tetpil, grem(p1, T));
+  }
+  rem -= 2;
+  if (lead) gunclone(lead);
+  if (!sx) (void)normalizepol_lg(rem, lr);
+  if (pr == ONLY_REM) return gerepileupto(av0,rem);
+  *pr = rem; return z-2;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                        PSEUDO-DIVISION                          */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+rem(GEN c, GEN T)
+{
+  if (T && typ(c) == t_POL && varn(c) == varn(T)) c = RgX_rem(c, T);
+  return c;
+}
+
+/* x, y, are ZYX, lc(y) is an integer, T is a ZY */
+int
+ZXQX_dvd(GEN x, GEN y, GEN T)
+{
+  long dx, dy, dz, i, p, T_ismonic;
+  pari_sp av = avma, av2, lim;
+  GEN y_lead;
+
+  if (!signe(y)) pari_err_INV("ZXQX_dvd",y);
+  dy = degpol(y); y_lead = gel(y,dy+2);
+  if (typ(y_lead) == t_POL) y_lead = gel(y_lead, 2); /* t_INT */
+  /* if monic, no point in using pseudo-division */
+  if (gequal1(y_lead)) return signe(RgXQX_rem(x, y, T)) == 0;
+  T_ismonic = gequal1(leading_term(T));
+  dx = degpol(x);
+  if (dx < dy) return !signe(x);
+  (void)new_chunk(2);
+  x = RgX_recip_shallow(x)+2;
+  y = RgX_recip_shallow(y)+2;
+  /* pay attention to sparse divisors */
+  for (i = 1; i <= dy; i++)
+    if (!signe(gel(y,i))) gel(y,i) = NULL;
+  dz = dx-dy; p = dz+1;
+  av2 = avma; lim = stack_lim(av2,1);
+  for (;;)
+  {
+    GEN m, x0 = gel(x,0), y0 = y_lead, cx = content(x0);
+    x0 = gneg(x0); p--;
+    m = gcdii(cx, y0);
+    if (!equali1(m))
+    {
+      x0 = gdiv(x0, m);
+      y0 = diviiexact(y0, m);
+      if (equali1(y0)) y0 = NULL;
+    }
+    for (i=1; i<=dy; i++)
+    {
+      GEN c = gel(x,i); if (y0) c = gmul(y0, c);
+      if (gel(y,i)) c = gadd(c, gmul(x0,gel(y,i)));
+      if (typ(c) == t_POL) c = T_ismonic ? ZX_rem(c, T): RgX_rem(c, T);
+      gel(x,i) = c;
+    }
+    for (   ; i<=dx; i++)
+    {
+      GEN c = gel(x,i); if (y0) c = gmul(y0, c);
+      if (typ(c) == t_POL) c = T_ismonic ? ZX_rem(c, T): RgX_rem(c, T);
+      gel(x,i) = c;
+    }
+    do { x++; dx--; } while (dx >= 0 && !signe(gel(x,0)));
+    if (dx < dy) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"ZXQX_dvd dx = %ld >= %ld",dx,dy);
+      gerepilecoeffs(av2,x,dx+1);
+    }
+  }
+  avma = av; return (dx < 0);
+}
+
+/* T either NULL or a t_POL. */
+GEN
+RgXQX_pseudorem(GEN x, GEN y, GEN T)
+{
+  long vx = varn(x), dx, dy, dz, i, lx, p;
+  pari_sp av = avma, av2, lim;
+  GEN y_lead;
+
+  if (!signe(y)) pari_err_INV("RgXQX_pseudorem",y);
+  dy = degpol(y); y_lead = gel(y,dy+2);
+  /* if monic, no point in using pseudo-division */
+  if (gequal1(y_lead)) return T? RgXQX_rem(x, y, T): RgX_rem(x, y);
+  dx = degpol(x);
+  if (dx < dy) return RgX_copy(x);
+  (void)new_chunk(2);
+  x = RgX_recip_shallow(x)+2;
+  y = RgX_recip_shallow(y)+2;
+  /* pay attention to sparse divisors */
+  for (i = 1; i <= dy; i++)
+    if (isexactzero(gel(y,i))) gel(y,i) = NULL;
+  dz = dx-dy; p = dz+1;
+  av2 = avma; lim = stack_lim(av2,1);
+  for (;;)
+  {
+    gel(x,0) = gneg(gel(x,0)); p--;
+    for (i=1; i<=dy; i++)
+    {
+      GEN c = gmul(y_lead, gel(x,i));
+      if (gel(y,i)) c = gadd(c, gmul(gel(x,0),gel(y,i)));
+      gel(x,i) = rem(c, T);
+    }
+    for (   ; i<=dx; i++)
+    {
+      GEN c = gmul(y_lead, gel(x,i));
+      gel(x,i) = rem(c, T);
+    }
+    do { x++; dx--; } while (dx >= 0 && gequal0(gel(x,0)));
+    if (dx < dy) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgX_pseudorem dx = %ld >= %ld",dx,dy);
+      gerepilecoeffs(av2,x,dx+1);
+    }
+  }
+  if (dx < 0) return pol_0(vx);
+  lx = dx+3; x -= 2;
+  x[0] = evaltyp(t_POL) | evallg(lx);
+  x[1] = evalsigne(1) | evalvarn(vx);
+  x = RgX_recip_shallow(x);
+  if (p)
+  { /* multiply by y[0]^p   [beware dummy vars from FpX_FpXY_resultant] */
+    GEN t = y_lead;
+    if (T && typ(t) == t_POL && varn(t) == varn(T))
+      t = RgXQ_powu(t, p, T);
+    else
+      t = gpowgs(t, p);
+    for (i=2; i<lx; i++)
+    {
+      GEN c = gmul(gel(x,i), t);
+      gel(x,i) = rem(c,T);
+    }
+    if (!T) return gerepileupto(av, x);
+  }
+  return gerepilecopy(av, x);
+}
+
+GEN
+RgX_pseudorem(GEN x, GEN y) { return RgXQX_pseudorem(x,y, NULL); }
+
+/* Compute z,r s.t lc(y)^(dx-dy+1) x = z y + r */
+GEN
+RgXQX_pseudodivrem(GEN x, GEN y, GEN T, GEN *ptr)
+{
+  long vx = varn(x), dx, dy, dz, i, iz, lx, lz, p;
+  pari_sp av = avma, av2, lim;
+  GEN z, r, ypow, y_lead;
+
+  if (!signe(y)) pari_err_INV("RgXQX_pseudodivrem",y);
+  dy = degpol(y); y_lead = gel(y,dy+2);
+  if (gequal1(y_lead)) return T? RgXQX_divrem(x,y, T, ptr): RgX_divrem(x,y, ptr);
+  dx = degpol(x);
+  if (dx < dy) { *ptr = RgX_copy(x); return pol_0(vx); }
+  if (dx == dy)
+  {
+    GEN x_lead = gel(x,lg(x)-1);
+    x = RgX_renormalize_lg(leafcopy(x), lg(x)-1);
+    y = RgX_renormalize_lg(leafcopy(y), lg(y)-1);
+    r = RgX_sub(RgX_Rg_mul(x, y_lead), RgX_Rg_mul(y, x_lead));
+    *ptr = gerepileupto(av, r); return scalarpol(x_lead, vx);
+  }
+  (void)new_chunk(2);
+  x = RgX_recip_shallow(x)+2;
+  y = RgX_recip_shallow(y)+2;
+  /* pay attention to sparse divisors */
+  for (i = 1; i <= dy; i++)
+    if (isexactzero(gel(y,i))) gel(y,i) = NULL;
+  dz = dx-dy; p = dz+1;
+  lz = dz+3;
+  z = cgetg(lz, t_POL);
+  z[1] = evalsigne(1) | evalvarn(vx);
+  for (i = 2; i < lz; i++) gel(z,i) = gen_0;
+  ypow = new_chunk(dz+1);
+  gel(ypow,0) = gen_1;
+  gel(ypow,1) = y_lead;
+  for (i=2; i<=dz; i++)
+  {
+    GEN c = gmul(gel(ypow,i-1), y_lead);
+    gel(ypow,i) = rem(c,T);
+  }
+  av2 = avma; lim = stack_lim(av2,1);
+  for (iz=2;;)
+  {
+    p--;
+    gel(z,iz++) = rem(gmul(gel(x,0), gel(ypow,p)), T);
+    for (i=1; i<=dy; i++)
+    {
+      GEN c = gmul(y_lead, gel(x,i));
+      if (gel(y,i)) c = gsub(c, gmul(gel(x,0),gel(y,i)));
+      gel(x,i) = rem(c, T);
+    }
+    for (   ; i<=dx; i++)
+    {
+      GEN c = gmul(y_lead, gel(x,i));
+      gel(x,i) = rem(c,T);
+    }
+    x++; dx--;
+    while (dx >= dy && gequal0(gel(x,0))) { x++; dx--; iz++; }
+    if (dx < dy) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      GEN X = x-2;
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgX_pseudodivrem dx=%ld >= %ld",dx,dy);
+      X[0] = evaltyp(t_POL)|evallg(dx+3); X[1] = z[1]; /* hack */
+      gerepileall(av2,2, &X, &z); x = X+2;
+    }
+  }
+  while (dx >= 0 && gequal0(gel(x,0))) { x++; dx--; }
+  if (dx < 0)
+    x = pol_0(vx);
+  else
+  {
+    lx = dx+3; x -= 2;
+    x[0] = evaltyp(t_POL) | evallg(lx);
+    x[1] = evalsigne(1) | evalvarn(vx);
+    x = RgX_recip_shallow(x);
+  }
+  z = RgX_recip_shallow(z);
+  r = x;
+  if (p)
+  {
+    GEN c = gel(ypow,p); r = RgX_Rg_mul(r, c);
+    if (T && typ(c) == t_POL && varn(c) == varn(T)) r = RgXQX_red(r, T);
+  }
+  gerepileall(av, 2, &z, &r);
+  *ptr = r; return z;
+}
+GEN
+RgX_pseudodivrem(GEN x, GEN y, GEN *ptr)
+{ return RgXQX_pseudodivrem(x,y,NULL,ptr); }
+
+GEN
+RgXQX_mul(GEN x, GEN y, GEN T)
+{
+  return RgXQX_red(RgX_mul(x,y), T);
+}
+GEN
+RgX_Rg_mul(GEN y, GEN x) {
+  long i, ly;
+  GEN z = cgetg_copy(y, &ly); z[1] = y[1];
+  if (ly == 2) return z;
+  for (i = 2; i < ly; i++) gel(z,i) = gmul(x,gel(y,i));
+  return normalizepol_lg(z,ly);
+}
+GEN
+RgX_muls(GEN y, long x) {
+  long i, ly;
+  GEN z = cgetg_copy(y, &ly); z[1] = y[1];
+  if (ly == 2) return z;
+  for (i = 2; i < ly; i++) gel(z,i) = gmulsg(x,gel(y,i));
+  return normalizepol_lg(z,ly);
+}
+GEN
+RgXQX_RgXQ_mul(GEN x, GEN y, GEN T)
+{
+  return RgXQX_red(RgX_Rg_mul(x,y), T);
+}
+GEN
+RgXQX_sqr(GEN x, GEN T)
+{
+  return RgXQX_red(RgX_sqr(x), T);
+}
+
+static GEN
+_add(void *data, GEN x, GEN y) { (void)data; return RgX_add(x, y); }
+static GEN
+_sqr(void *data, GEN x) { return RgXQ_sqr(x, (GEN)data); }
+static GEN
+_mul(void *data, GEN x, GEN y) { return RgXQ_mul(x,y, (GEN)data); }
+static GEN
+_cmul(void *data, GEN P, long a, GEN x) { (void)data; return RgX_Rg_mul(x,gel(P,a+2)); }
+static GEN
+_one(void *data) { return pol_1(varn((GEN)data)); }
+static GEN
+_zero(void *data) { return pol_0(varn((GEN)data)); }
+static GEN
+_red(void *data, GEN x) { (void)data; return gcopy(x); }
+
+static struct bb_algebra RgXQ_algebra = { _red,_add,_mul,_sqr,_one,_zero };
+
+GEN
+RgX_RgXQV_eval(GEN Q, GEN x, GEN T)
+{
+  return gen_bkeval_powers(Q,degpol(Q),x,(void*)T,&RgXQ_algebra,_cmul);
+}
+
+GEN
+RgX_RgXQ_eval(GEN Q, GEN x, GEN T)
+{
+  int use_sqr = (degpol(x)<<1) >= degpol(T);
+  return gen_bkeval(Q,degpol(Q),x,use_sqr,(void*)T,&RgXQ_algebra,_cmul);
+}
+
+/* mod X^n */
+struct modXn {
+  long v; /* varn(X) */
+  long n;
+} ;
+static GEN
+_sqrXn(void *data, GEN x) {
+  struct modXn *S = (struct modXn*)data;
+  return RgX_sqrlow(x, S->n);
+}
+static GEN
+_mulXn(void *data, GEN x, GEN y) {
+  struct modXn *S = (struct modXn*)data;
+  return RgX_mullow(x,y, S->n);
+}
+static GEN
+_oneXn(void *data) {
+  struct modXn *S = (struct modXn*)data;
+  return pol_1(S->v);
+}
+static GEN
+_zeroXn(void *data) {
+  struct modXn *S = (struct modXn*)data;
+  return pol_0(S->v);
+}
+static struct bb_algebra RgX_modXn_algebra = { _red,_add, _mulXn,_sqrXn, _oneXn,_zeroXn };
+
+/* Q(x) mod t^n, x in R[t], n >= 1 */
+GEN
+RgX_modXn_eval(GEN Q, GEN x, long n)
+{
+  long d = degpol(x);
+  int use_sqr;
+  struct modXn S;
+  if (d == 1 && isrationalzero(gel(x,2)))
+  {
+    GEN y = RgX_unscale(Q, gel(x,3));
+    setvarn(y, varn(x)); return y;
+  }
+  S.v = varn(x);
+  S.n = n;
+  use_sqr = (d<<1) >= n;
+  return gen_bkeval(Q,degpol(Q),x,use_sqr,(void*)&S,&RgX_modXn_algebra,_cmul);
+}
+
+/* x,T in Rg[X], n in N, compute lift(x^n mod T)) */
+GEN
+RgXQ_powu(GEN x, ulong n, GEN T)
+{
+  pari_sp av;
+  GEN y;
+
+  if (!n) return pol_1(varn(x));
+  if (n == 1) return RgX_copy(x);
+  av = avma;
+  y = gen_powu(x, n, (void*)T, &_sqr, &_mul);
+  return gerepileupto(av, y);
+}
+/* x,T in Rg[X], n in N, compute lift(x^n mod T)) */
+GEN
+RgXQ_pow(GEN x, GEN n, GEN T)
+{
+  pari_sp av;
+  long s = signe(n);
+  GEN y;
+
+  if (!s) return pol_1(varn(x));
+  if (is_pm1(n) == 1)
+    return (s < 0)? RgXQ_inv(x, T): RgX_copy(x);
+  av = avma;
+  if (s < 0) x = RgXQ_inv(x, T);
+  y = gen_pow(x, n, (void*)T, &_sqr, &_mul);
+  return gerepileupto(av, y);
+}
+
+/* generates the list of powers of x of degree 0,1,2,...,l*/
+GEN
+RgXQ_powers(GEN x, long l, GEN T)
+{
+  int use_sqr = (degpol(x)<<1) >= degpol(T);
+  return gen_powers(x, l, use_sqr, (void *)T,_sqr,_mul,_one);
+}
+
+/* a in K = Q[X]/(T), returns [a^0, ..., a^n] */
+GEN
+QXQ_powers(GEN a, long n, GEN T)
+{
+  GEN den, v = RgXQ_powers(Q_remove_denom(a, &den), n, T);
+  /* den*a integral; v[i+1] = (den*a)^i in K */
+  if (den)
+  { /* restore denominators */
+    GEN d = den;
+    long i;
+    gel(v,2) = a;
+    for (i=3; i<=n+1; i++) {
+      d = mulii(d,den);
+      gel(v,i) = RgX_Rg_div(gel(v,i), d);
+    }
+  }
+  return v;
+}
+
+static GEN
+do_QXQ_eval(GEN v, long imin, GEN a, GEN T)
+{
+  long l, i, m = degpol(T);
+  GEN dz, z = Q_remove_denom(QXQ_powers(a, m-1, T), &dz);
+  GEN V = cgetg_copy(v, &l);
+  for (i = 1; i < imin; i++) V[i] = v[i];
+  for (i = imin; i < l; i++)
+  {
+    GEN c = gel(v,i);
+    if (typ(c) == t_POL) c = QX_ZXQV_eval(c, z, dz);
+    gel(V,i) = c;
+  }
+  return V;
+}
+/* [ s(a mod T) | s <- lift(v) ], a,T are QX, v a QXV */
+GEN
+QXV_QXQ_eval(GEN v, GEN a, GEN T)
+{ return do_QXQ_eval(v, 1, a, T); }
+GEN
+QXX_QXQ_eval(GEN v, GEN a, GEN T)
+{ return normalizepol(do_QXQ_eval(v, 2, a, T)); }
+
+GEN
+RgXQ_matrix_pow(GEN y, long n, long m, GEN P)
+{
+  return RgXV_to_RgM(RgXQ_powers(y,m-1,P),n);
+}
+
+GEN
+RgXQ_minpoly_naive(GEN y, GEN P)
+{
+  pari_sp ltop=avma;
+  long n=lgpol(P);
+  GEN M=ker(RgXQ_matrix_pow(y,n,n,P));
+  M=content(RgM_to_RgXV(M,varn(P)));
+  return gerepileupto(ltop,M);
+}
+
+GEN
+RgXQ_norm(GEN x, GEN T)
+{
+  pari_sp av;
+  long dx = degpol(x);
+  GEN L, y;
+
+  av = avma; y = resultant(T, x);
+  L = leading_term(T);
+  if (gequal1(L) || !signe(x)) return y;
+  return gerepileupto(av, gdiv(y, gpowgs(L, dx)));
+}
+
+GEN
+RgX_blocks(GEN P, long n, long m)
+{
+  GEN z = cgetg(m+1,t_VEC);
+  long i,j, k=2, l = lg(P);
+  for(i=1; i<=m; i++)
+  {
+    GEN zi = cgetg(n+2,t_POL);
+    zi[1] = P[1];
+    gel(z,i) = zi;
+    for(j=2; j<n+2; j++)
+      gel(zi, j) = k==l ? gen_0 : gel(P,k++);
+    zi = ZX_renormalize(zi, n+2);
+  }
+  return z;
+}
+
+/* write p(X) = e(X^2) + Xo(X^2), shallow function */
+void
+RgX_even_odd(GEN p, GEN *pe, GEN *po)
+{
+  long n = degpol(p), v = varn(p), n0, n1, i;
+  GEN p0, p1;
+
+  if (n <= 0) { *pe = RgX_copy(p); *po = zeropol(v); return; }
+
+  n0 = (n>>1)+1; n1 = n+1 - n0; /* n1 <= n0 <= n1+1 */
+  p0 = cgetg(n0+2, t_POL); p0[1] = evalvarn(v)|evalsigne(1);
+  p1 = cgetg(n1+2, t_POL); p1[1] = evalvarn(v)|evalsigne(1);
+  for (i=0; i<n1; i++)
+  {
+    p0[2+i] = p[2+(i<<1)];
+    p1[2+i] = p[3+(i<<1)];
+  }
+  if (n1 != n0)
+    p0[2+i] = p[2+(i<<1)];
+  *pe = normalizepol(p0);
+  *po = normalizepol(p1);
+}
+
+/* write p(X) = a_0(X^k) + Xa_1(X^k) + ... + X^(k-1)a_{k-1}(X^k), shallow function */
+GEN
+RgX_splitting(GEN p, long k)
+{
+  long n = degpol(p), v = varn(p), m, i, j, l;
+  GEN r;
+
+  m = n/k;
+  r = cgetg(k+1,t_VEC);
+  for(i=1; i<=k; i++)
+  {
+    gel(r,i) = cgetg(m+3, t_POL);
+    mael(r,i,1) = evalvarn(v)|evalsigne(1);
+  }
+  for (j=1, i=0, l=2; i<=n; i++)
+  {
+    gmael(r,j,l) = gel(p,2+i);
+    if (j==k) { j=1; l++; } else j++;
+  }
+  for(i=1; i<=k; i++)
+    gel(r,i) = normalizepol_lg(gel(r,i),i<j?l+1:l);
+  return r;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                        Kronecker form                           */
+/*                                                                 */
+/*******************************************************************/
+
+/* z in R[Y] representing an elt in R[X,Y] mod T(Y) in Kronecker form,
+ * i.e subst(lift(z), x, y^(2deg(z)-1)). Recover the "real" z, with
+ * normalized coefficients */
+GEN
+Kronecker_to_mod(GEN z, GEN T)
+{
+  long i,j,lx,l = lg(z), N = (degpol(T)<<1) + 1;
+  GEN x, t = cgetg(N,t_POL);
+  t[1] = T[1];
+  lx = (l-2) / (N-2); x = cgetg(lx+3,t_POL);
+  x[1] = z[1];
+  T = RgX_copy(T);
+  for (i=2; i<lx+2; i++, z+= N-2)
+  {
+    for (j=2; j<N; j++) gel(t,j) = gel(z,j);
+    gel(x,i) = mkpolmod(RgX_rem(normalizepol_lg(t,N), T), T);
+  }
+  N = (l-2) % (N-2) + 2;
+  for (j=2; j<N; j++) t[j] = z[j];
+  gel(x,i) = mkpolmod(RgX_rem(normalizepol_lg(t,N), T), T);
+  return normalizepol_lg(x, i+1);
+}
diff --git a/src/basemath/ZV.c b/src/basemath/ZV.c
new file mode 100644
index 0000000..06a18cb
--- /dev/null
+++ b/src/basemath/ZV.c
@@ -0,0 +1,1116 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+static int
+check_ZV(GEN x, long l)
+{
+  long i;
+  for (i=1; i<l; i++)
+    if (typ(gel(x,i)) != t_INT) return 0;
+  return 1;
+}
+void
+RgV_check_ZV(GEN A, const char *s)
+{
+  if (!RgV_is_ZV(A)) pari_err_TYPE(stack_strcat(s," [integer vector]"), A);
+}
+void
+RgM_check_ZM(GEN A, const char *s)
+{
+  long n = lg(A);
+  if (n != 1)
+  {
+    long j, m = lgcols(A);
+    for (j=1; j<n; j++)
+      if (!check_ZV(gel(A,j), m))
+        pari_err_TYPE(stack_strcat(s," [integer matrix]"), A);
+  }
+}
+
+long
+ZV_max_lg(GEN x)
+{
+  long i, prec = 2, m = lg(x);
+  for (i=1; i<m; i++) { long l = lgefint(gel(x,i)); if (l > prec) prec = l; }
+  return prec;
+}
+long
+ZM_max_lg(GEN x)
+{
+  long i, prec = 2, n = lg(x);
+  if (n != 1)
+  {
+    long j, m = lgcols(x);
+    for (j=1; j<n; j++)
+    {
+      GEN c = gel(x,j);
+      for (i=1; i<m; i++) { long l = lgefint(gel(c,i)); if (l > prec) prec = l; }
+    }
+  }
+  return prec;
+}
+
+GEN
+ZM_supnorm(GEN x)
+{
+  long i, j, h, lx = lg(x);
+  GEN s = gen_0;
+  if (lx == 1) return gen_1;
+  h = lgcols(x);
+  for (j=1; j<lx; j++)
+  {
+    GEN xj = gel(x,j);
+    for (i=1; i<h; i++)
+    {
+      GEN c = gel(xj,i);
+      if (absi_cmp(c, s) > 0) s = c;
+    }
+  }
+  return absi(s);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           MULTIPLICATION                       **/
+/**                                                                **/
+/********************************************************************/
+/* x non-empty ZM, y a compatible nc (dimension > 0). */
+static GEN
+ZM_nc_mul_i(GEN x, GEN y, long c, long l)
+{
+  long i, j;
+  pari_sp av;
+  GEN z = cgetg(l,t_COL), s;
+
+  for (i=1; i<l; i++)
+  {
+    av = avma; s = muliu(gcoeff(x,i,1),y[1]);
+    for (j=2; j<c; j++)
+      if (y[j]) s = addii(s, muliu(gcoeff(x,i,j),y[j]));
+    gel(z,i) = gerepileuptoint(av,s);
+  }
+  return z;
+}
+/* x non-empty ZM, y a compatible zc (dimension > 0). */
+static GEN
+ZM_zc_mul_i(GEN x, GEN y, long c, long l)
+{
+  long i, j;
+  pari_sp av;
+  GEN z = cgetg(l,t_COL), s;
+
+  for (i=1; i<l; i++)
+  {
+    av = avma; s = mulis(gcoeff(x,i,1),y[1]);
+    for (j=2; j<c; j++)
+      if (y[j]) s = addii(s, mulis(gcoeff(x,i,j),y[j]));
+    gel(z,i) = gerepileuptoint(av,s);
+  }
+  return z;
+}
+GEN
+ZM_zc_mul(GEN x, GEN y) {
+  long l = lg(x);
+  if (l == 1) return cgetg(1, t_COL);
+  return ZM_zc_mul_i(x,y, l, lgcols(x));
+}
+
+/* x ZM, y a compatible zm (dimension > 0). */
+GEN
+ZM_zm_mul(GEN x, GEN y)
+{
+  long j, c, l = lg(x), ly = lg(y);
+  GEN z = cgetg(ly, t_MAT);
+  if (l == 1) return z;
+  c = lgcols(x);
+  for (j = 1; j < ly; j++) gel(z,j) = ZM_zc_mul_i(x, gel(y,j), l,c);
+  return z;
+}
+/* x ZM, y a compatible zn (dimension > 0). */
+GEN
+ZM_nm_mul(GEN x, GEN y)
+{
+  long j, c, l = lg(x), ly = lg(y);
+  GEN z = cgetg(ly, t_MAT);
+  if (l == 1) return z;
+  c = lgcols(x);
+  for (j = 1; j < ly; j++) gel(z,j) = ZM_nc_mul_i(x, gel(y,j), l,c);
+  return z;
+}
+
+/* x[i,]*y. Assume lg(x) > 1 and 0 < i < lgcols(x) */
+static GEN
+ZMrow_ZC_mul_i(GEN x, GEN y, long i, long lx)
+{
+  pari_sp av = avma;
+  GEN c = mulii(gcoeff(x,i,1), gel(y,1)), ZERO = gen_0;
+  long k;
+  for (k = 2; k < lx; k++)
+  {
+    GEN t = mulii(gcoeff(x,i,k), gel(y,k));
+    if (t != ZERO) c = addii(c, t);
+  }
+  return gerepileuptoint(av, c);
+}
+GEN
+ZMrow_ZC_mul(GEN x, GEN y, long i)
+{ return ZMrow_ZC_mul_i(x, y, i, lg(x)); }
+
+/* return x * y, 1 < lx = lg(x), l = lgcols(x) */
+static GEN
+ZM_ZC_mul_i(GEN x, GEN y, long lx, long l)
+{
+  GEN z = cgetg(l,t_COL);
+  long i;
+  for (i=1; i<l; i++) gel(z,i) = ZMrow_ZC_mul_i(x,y,i,lx);
+  return z;
+}
+GEN
+ZM_mul(GEN x, GEN y)
+{
+  long j, l, lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  if (lx==1) return zeromat(0, ly-1);
+  l = lgcols(x); z = cgetg(ly,t_MAT);
+  for (j=1; j<ly; j++) gel(z,j) = ZM_ZC_mul_i(x, gel(y,j), lx, l);
+  return z;
+}
+/* assume result is symmetric */
+GEN
+ZM_multosym(GEN x, GEN y)
+{
+  long j, lx, ly = lg(y);
+  GEN M;
+  if (ly == 1) return cgetg(1,t_MAT);
+  lx = lg(x); /* = lgcols(y) */
+  if (lx == 1) return cgetg(1,t_MAT);
+  /* ly = lgcols(x) */
+  M = cgetg(ly, t_MAT);
+  for (j=1; j<ly; j++)
+  {
+    GEN z = cgetg(ly,t_COL), yj = gel(y,j);
+    long i;
+    for (i=1; i<j; i++) gel(z,i) = gcoeff(M,j,i);
+    for (i=j; i<ly; i++)gel(z,i) = ZMrow_ZC_mul_i(x,yj,i,lx);
+    gel(M,j) = z;
+  }
+  return M;
+}
+
+/* assume lx > 1 is lg(x) = lg(y) */
+static GEN
+ZV_dotproduct_i(GEN x, GEN y, long lx)
+{
+  pari_sp av = avma;
+  GEN c = mulii(gel(x,1), gel(y,1));
+  long i;
+  for (i = 2; i < lx; i++)
+  {
+    GEN t = mulii(gel(x,i), gel(y,i));
+    if (t != gen_0) c = addii(c, t);
+  }
+  return gerepileuptoint(av, c);
+}
+
+/* x~ * y, assuming result is symmetric */
+GEN
+ZM_transmultosym(GEN x, GEN y)
+{
+  long i, j, l, ly = lg(y);
+  GEN M;
+  if (ly == 1) return cgetg(1,t_MAT);
+  /* lg(x) = ly */
+  l = lgcols(y); /* = lgcols(x) */
+  M = cgetg(ly, t_MAT);
+  for (i=1; i<ly; i++)
+  {
+    GEN xi = gel(x,i), c = cgetg(ly,t_COL);
+    gel(M,i) = c;
+    for (j=1; j<i; j++)
+      gcoeff(M,i,j) = gel(c,j) = ZV_dotproduct_i(xi,gel(y,j),l);
+    gel(c,i) = ZV_dotproduct_i(xi,gel(y,i),l);
+  }
+  return M;
+}
+GEN
+ZM_ZC_mul(GEN x, GEN y)
+{
+  long lx = lg(x);
+  return lx==1? cgetg(1,t_COL): ZM_ZC_mul_i(x, y, lx, lgcols(x));
+}
+
+long
+zv_dotproduct(GEN x, GEN y)
+{
+  long i, lx = lg(x);
+  ulong c;
+  if (lx == 1) return 0;
+  c = uel(x,1)*uel(y,1);
+  for (i = 2; i < lx; i++)
+    c += uel(x,i)*uel(y,i);
+  return c;
+}
+
+GEN
+ZV_ZM_mul(GEN x, GEN y)
+{
+  long i, lx = lg(x), ly = lg(y);
+  GEN z;
+  if (lx == 1) return zerovec(ly-1);
+  z = cgetg(ly, t_VEC);
+  for (i = 1; i < ly; i++) gel(z,i) = ZV_dotproduct_i(x, gel(y,i), lx);
+  return z;
+}
+
+GEN
+ZC_ZV_mul(GEN x, GEN y)
+{
+  long i,j, lx=lg(x), ly=lg(y);
+  GEN z;
+  if (ly==1) return cgetg(1,t_MAT);
+  z = cgetg(ly,t_MAT);
+  for (j=1; j < ly; j++)
+  {
+    gel(z,j) = cgetg(lx,t_COL);
+    for (i=1; i<lx; i++) gcoeff(z,i,j) = mulii(gel(x,i),gel(y,j));
+  }
+  return z;
+}
+
+GEN
+ZV_dotsquare(GEN x)
+{
+  long i, lx;
+  pari_sp av;
+  GEN z;
+  lx = lg(x);
+  if (lx == 1) return gen_0;
+  av = avma; z = sqri(gel(x,1));
+  for (i=2; i<lx; i++) z = addii(z, sqri(gel(x,i)));
+  return gerepileuptoint(av,z);
+}
+
+GEN
+ZV_dotproduct(GEN x,GEN y)
+{
+  long lx;
+  if (x == y) return ZV_dotsquare(x);
+  lx = lg(x);
+  if (lx == 1) return gen_0;
+  return ZV_dotproduct_i(x, y, lx);
+}
+
+static GEN
+_ZM_mul(void *data /*ignored*/, GEN x, GEN y)
+{ (void)data; return ZM_mul(x,y); }
+static GEN
+_ZM_sqr(void *data /*ignored*/, GEN x)
+{ (void)data; return ZM_mul(x,x); }
+GEN
+ZM_pow(GEN x, GEN n)
+{
+  pari_sp av = avma;
+  if (!signe(n)) return matid(lg(x)-1);
+  return gerepileupto(av, gen_pow(x, n, NULL, &_ZM_sqr, &_ZM_mul));
+}
+GEN
+ZM_powu(GEN x, ulong n)
+{
+  pari_sp av = avma;
+  if (!n) return matid(lg(x)-1);
+  return gerepileupto(av, gen_powu(x, n, NULL, &_ZM_sqr, &_ZM_mul));
+}
+/********************************************************************/
+/**                                                                **/
+/**                           ADD, SUB                             **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+ZC_add_i(GEN x, GEN y, long lx)
+{
+  GEN A = cgetg(lx, t_COL);
+  long i;
+  for (i=1; i<lx; i++) gel(A,i) = addii(gel(x,i), gel(y,i));
+  return A;
+}
+GEN
+ZC_add(GEN x, GEN y) { return ZC_add_i(x, y, lg(x)); }
+GEN
+ZC_Z_add(GEN x, GEN y)
+{
+  long k, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  if (lx == 1) pari_err_TYPE2("+",x,y);
+  gel(z,1) = addii(y,gel(x,1));
+  for (k = 2; k < lx; k++) gel(z,k) = icopy(gel(x,k));
+  return z;
+}
+
+static GEN
+ZC_sub_i(GEN x, GEN y, long lx)
+{
+  long i;
+  GEN A = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++) gel(A,i) = subii(gel(x,i), gel(y,i));
+  return A;
+}
+GEN
+ZC_sub(GEN x, GEN y) { return ZC_sub_i(x, y, lg(x)); }
+GEN
+ZC_Z_sub(GEN x, GEN y)
+{
+  long k, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  if (lx == 1) pari_err_TYPE2("+",x,y);
+  gel(z,1) = subii(gel(x,1), y);
+  for (k = 2; k < lx; k++) gel(z,k) = icopy(gel(x,k));
+  return z;
+}
+
+GEN
+ZM_add(GEN x, GEN y)
+{
+  long lx = lg(x), l, j;
+  GEN z;
+  if (lx == 1) return cgetg(1, t_MAT);
+  z = cgetg(lx, t_MAT); l = lgcols(x);
+  for (j = 1; j < lx; j++) gel(z,j) = ZC_add_i(gel(x,j), gel(y,j), l);
+  return z;
+}
+GEN
+ZM_sub(GEN x, GEN y)
+{
+  long lx = lg(x), l, j;
+  GEN z;
+  if (lx == 1) return cgetg(1, t_MAT);
+  z = cgetg(lx, t_MAT); l = lgcols(x);
+  for (j = 1; j < lx; j++) gel(z,j) = ZC_sub_i(gel(x,j), gel(y,j), l);
+  return z;
+}
+/********************************************************************/
+/**                                                                **/
+/**                         LINEAR COMBINATION                     **/
+/**                                                                **/
+/********************************************************************/
+/* return X/c assuming division is exact */
+GEN
+ZC_Z_divexact(GEN X, GEN c)
+{
+  long i, l = lg(X);
+  GEN A = cgetg(l, t_COL);
+  for (i=1; i<l; i++) gel(A,i) = diviiexact(gel(X,i), c);
+  return A;
+}
+GEN
+ZM_Z_divexact(GEN X, GEN c)
+{
+  long i, l = lg(X);
+  GEN A = cgetg(l, t_MAT);
+  for (i = 1; i < l; i++) gel(A,i) = ZC_Z_divexact(gel(X,i), c);
+  return A;
+}
+/* Return c * X */
+GEN
+ZC_Z_mul(GEN X, GEN c)
+{
+  long i, l;
+  GEN A;
+  if (!signe(c)) return zerocol(lg(X)-1);
+  if (is_pm1(c)) return (signe(c) > 0)? ZC_copy(X): ZC_neg(X);
+  l = lg(X); A = cgetg(l, t_COL);
+  for (i=1; i<l; i++) gel(A,i) = mulii(c,gel(X,i));
+  return A;
+}
+GEN
+ZC_z_mul(GEN X, long c)
+{
+  long i, l;
+  GEN A;
+  if (!c) return zerocol(lg(X)-1);
+  if (c == 1) return ZC_copy(X);
+  if (c ==-1) return ZC_neg(X);
+  l = lg(X); A = cgetg(l, t_COL);
+  for (i=1; i<l; i++) gel(A,i) = mulsi(c,gel(X,i));
+  return A;
+}
+
+GEN
+zv_z_mul(GEN M, long n)
+{
+  long l;
+  GEN N = cgetg_copy(M, &l);
+  while (--l > 0) N[l] = M[l]*n;
+  return N;
+}
+
+/* return a ZM */
+GEN
+nm_Z_mul(GEN X, GEN c)
+{
+  long i, j, h, l = lg(X), s = signe(c);
+  GEN A;
+  if (l == 1) return cgetg(1, t_MAT);
+  h = lgcols(X);
+  if (!s) return zeromat(h-1, l-1);
+  if (is_pm1(c)) {
+    if (s > 0) return Flm_to_ZM(X);
+    X = Flm_to_ZM(X); ZM_togglesign(X); return X;
+  }
+  A = cgetg(l, t_MAT);
+  for (j = 1; j < l; j++)
+  {
+    GEN a = cgetg(h, t_COL), x = gel(X, j);
+    for (i = 1; i < h; i++) gel(a,i) = muliu(c, x[i]);
+    gel(A,j) = a;
+  }
+  return A;
+}
+GEN
+ZM_Z_mul(GEN X, GEN c)
+{
+  long i, j, h, l = lg(X);
+  GEN A;
+  if (l == 1) return cgetg(1, t_MAT);
+  h = lgcols(X);
+  if (!signe(c)) return zeromat(h-1, l-1);
+  if (is_pm1(c)) return (signe(c) > 0)? ZM_copy(X): ZM_neg(X);
+  A = cgetg(l, t_MAT);
+  for (j = 1; j < l; j++)
+  {
+    GEN a = cgetg(h, t_COL), x = gel(X, j);
+    for (i = 1; i < h; i++) gel(a,i) = mulii(c, gel(x,i));
+    gel(A,j) = a;
+  }
+  return A;
+}
+
+/* X <- X + v Y (elementary col operation) */
+void
+ZC_lincomb1_inplace(GEN X, GEN Y, GEN v)
+{
+  long i, m = lgefint(v);
+  if (m == 2) return; /* v = 0 */
+  for (i = lg(X)-1; i; i--) gel(X,i) = addmulii_inplace(gel(X,i), gel(Y,i), v);
+}
+void
+Flc_lincomb1_inplace(GEN X, GEN Y, ulong v, ulong q)
+{
+  long i;
+  if (!v) return; /* v = 0 */
+  for (i = lg(X)-1; i; i--) X[i] = Fl_add(X[i], Fl_mul(Y[i], v, q), q);
+}
+
+/* X + v Y, wasteful if (v = 0) */
+static GEN
+ZC_lincomb1(GEN v, GEN X, GEN Y)
+{
+  long i, lx = lg(X);
+  GEN A = cgetg(lx,t_COL);
+  for (i=1; i<lx; i++) gel(A,i) = addmulii(gel(X,i), gel(Y,i), v);
+  return A;
+}
+/* -X + vY */
+static GEN
+ZC_lincomb_1(GEN v, GEN X, GEN Y)
+{
+  long i, lx = lg(X);
+  GEN A = cgetg(lx,t_COL);
+  for (i=1; i<lx; i++) gel(A,i) = mulsubii(gel(Y,i), v, gel(X,i));
+  return A;
+}
+/* X,Y compatible ZV; u,v in Z. Returns A = u*X + v*Y */
+GEN
+ZC_lincomb(GEN u, GEN v, GEN X, GEN Y)
+{
+  long su, sv;
+  GEN A;
+
+  su = signe(u); if (!su) return ZC_Z_mul(Y, v);
+  sv = signe(v); if (!sv) return ZC_Z_mul(X, u);
+  if (is_pm1(v))
+  {
+    if (is_pm1(u))
+    {
+      if (su != sv) A = ZC_sub(X, Y);
+      else          A = ZC_add(X, Y);
+      if (su < 0) ZV_togglesign(A); /* in place but was created above */
+    }
+    else
+    {
+      if (sv > 0) A = ZC_lincomb1 (u, Y, X);
+      else        A = ZC_lincomb_1(u, Y, X);
+    }
+  }
+  else if (is_pm1(u))
+  {
+    if (su > 0) A = ZC_lincomb1 (v, X, Y);
+    else        A = ZC_lincomb_1(v, X, Y);
+  }
+  else
+  { /* not cgetg_copy: x may be a t_VEC */
+    long i, lx = lg(X);
+    A = cgetg(lx,t_COL);
+    for (i=1; i<lx; i++) gel(A,i) = lincombii(u,v,gel(X,i),gel(Y,i));
+  }
+  return A;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           CONVERSIONS                          **/
+/**                                                                **/
+/********************************************************************/
+GEN
+ZV_to_nv(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) x[i] = itou(gel(z,i));
+  return x;
+}
+
+GEN
+zm_to_ZM(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = zc_to_ZC(gel(z,i));
+  return x;
+}
+
+GEN
+zmV_to_ZMV(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = zm_to_ZM(gel(z,i));
+  return x;
+}
+
+/* same as Flm_to_ZM but do not assume positivity */
+GEN
+ZM_to_zm(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = ZV_to_zv(gel(z,i));
+  return x;
+}
+
+GEN
+zv_to_Flv(GEN z, ulong p)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_VECSMALL);
+  for (i=1; i<l; i++) x[i] = smodss(z[i],p);
+  return x;
+}
+
+GEN
+zm_to_Flm(GEN z, ulong p)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = zv_to_Flv(gel(z,i),p);
+  return x;
+}
+
+GEN
+ZMV_to_zmV(GEN z)
+{
+  long i,l = lg(z);
+  GEN x = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = ZM_to_zm(gel(z,i));
+  return x;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         COPY, NEGATION                         **/
+/**                                                                **/
+/********************************************************************/
+GEN
+ZC_copy(GEN x)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++)
+  {
+    GEN c = gel(x,i);
+    gel(y,i) = lgefint(c) == 2? gen_0: icopy(c);
+  }
+  return y;
+}
+
+GEN
+ZM_copy(GEN x)
+{
+  long l;
+  GEN y = cgetg_copy(x, &l);
+  while (--l > 0) gel(y,l) = ZC_copy(gel(x,l));
+  return y;
+}
+
+void
+ZV_neg_inplace(GEN M)
+{
+  long l = lg(M);
+  while (--l > 0) gel(M,l) = negi(gel(M,l));
+}
+GEN
+ZC_neg(GEN M)
+{
+  long l = lg(M);
+  GEN N = cgetg(l, t_COL);
+  while (--l > 0) gel(N,l) = negi(gel(M,l));
+  return N;
+}
+GEN
+zv_neg(GEN M)
+{
+  long l;
+  GEN N = cgetg_copy(M, &l);
+  while (--l > 0) N[l] = -M[l];
+  return N;
+}
+GEN
+zv_neg_inplace(GEN M)
+{
+  long l = lg(M);
+  while (--l > 0) M[l] = -M[l];
+  return M;
+}
+GEN
+ZM_neg(GEN x)
+{
+  long l;
+  GEN y = cgetg_copy(x, &l);
+  while (--l > 0) gel(y,l) = ZC_neg(gel(x,l));
+  return y;
+}
+
+void
+ZV_togglesign(GEN M)
+{
+  long l = lg(M);
+  while (--l > 0) togglesign_safe(&gel(M,l));
+}
+void
+ZM_togglesign(GEN M)
+{
+  long l = lg(M);
+  while (--l > 0) ZV_togglesign(gel(M,l));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        "DIVISION" mod HNF                      **/
+/**                                                                **/
+/********************************************************************/
+/* Reduce ZC x modulo ZM y in HNF, may return x itself (not a copy) */
+GEN
+ZC_hnfremdiv(GEN x, GEN y, GEN *Q)
+{
+  long i, l = lg(x);
+  GEN q;
+
+  if (Q) *Q = cgetg(l,t_COL);
+  if (l == 1) return cgetg(1,t_COL);
+  for (i = l-1; i>0; i--)
+  {
+    q = diviiround(gel(x,i), gcoeff(y,i,i));
+    if (signe(q)) {
+      togglesign(q);
+      x = ZC_lincomb(gen_1, q, x, gel(y,i));
+    }
+    if (Q) gel(*Q, i) = q;
+  }
+  return x;
+}
+
+/* x = y Q + R, may return some columns of x (not copies) */
+GEN
+ZM_hnfdivrem(GEN x, GEN y, GEN *Q)
+{
+  long lx = lg(x), i;
+  GEN R = cgetg(lx, t_MAT);
+  if (Q)
+  {
+    GEN q = cgetg(lx, t_MAT); *Q = q;
+    for (i=1; i<lx; i++) gel(R,i) = ZC_hnfremdiv(gel(x,i),y,(GEN*)(q+i));
+  }
+  else
+    for (i=1; i<lx; i++)
+    {
+      pari_sp av = avma;
+      GEN z = ZC_hnfrem(gel(x,i),y);
+      gel(R,i) = (avma == av)? ZC_copy(z): gerepileupto(av, z);
+    }
+  return R;
+}
+
+
+/********************************************************************/
+/**                                                                **/
+/**                               TESTS                            **/
+/**                                                                **/
+/********************************************************************/
+int
+zv_equal0(GEN V)
+{
+  long l = lg(V);
+  while (--l > 0)
+    if (V[l]) return 0;
+  return 1;
+}
+
+int
+ZV_equal0(GEN V)
+{
+  long l = lg(V);
+  while (--l > 0)
+    if (signe(gel(V,l))) return 0;
+  return 1;
+}
+
+static int
+ZV_equal_lg(GEN V, GEN W, long l)
+{
+  while (--l > 0)
+    if (!equalii(gel(V,l), gel(W,l))) return 0;
+  return 1;
+}
+int
+ZV_equal(GEN V, GEN W)
+{
+  long l = lg(V);
+  if (lg(W) != l) return 0;
+  return ZV_equal_lg(V, W, l);
+}
+int
+ZM_equal(GEN A, GEN B)
+{
+  long i, m, l = lg(A);
+  if (lg(B) != l) return 0;
+  if (l == 1) return 1;
+  m = lgcols(A);
+  if (lgcols(B) != m) return 0;
+  for (i = 1; i < l; i++)
+    if (!ZV_equal_lg(gel(A,i), gel(B,i), m)) return 0;
+  return 1;
+}
+int
+zv_equal(GEN V, GEN W)
+{
+  long l = lg(V);
+  if (lg(W) != l) return 0;
+  while (--l > 0)
+    if (V[l] != W[l]) return 0;
+  return 1;
+}
+
+int
+zvV_equal(GEN V, GEN W)
+{
+  long l = lg(V);
+  if (lg(W) != l) return 0;
+  while (--l > 0)
+    if (!zv_equal(gel(V,l),gel(W,l))) return 0;
+  return 1;
+}
+
+int
+ZM_ishnf(GEN x)
+{
+  long i,j, lx = lg(x);
+  for (i=1; i<lx; i++)
+  {
+    GEN xii = gcoeff(x,i,i);
+    if (signe(xii) <= 0) return 0;
+    for (j=1; j<i; j++)
+      if (signe(gcoeff(x,i,j))) return 0;
+    for (j=i+1; j<lx; j++)
+    {
+      GEN xij = gcoeff(x,i,j);
+      if (signe(xij)<0 || cmpii(xij,xii)>=0) return 0;
+    }
+  }
+  return 1;
+}
+int
+ZM_isidentity(GEN x)
+{
+  long i,j, lx = lg(x);
+
+  if (lx == 1) return 1;
+  if (lx != lgcols(x)) return 0;
+  for (j=1; j<lx; j++)
+  {
+    GEN c = gel(x,j), t;
+    for (i=1; i<j; )
+      if (signe(gel(c,i++))) return 0;
+    /* i = j */
+    t = gel(c,i++);
+      if (!is_pm1(t) || signe(t) < 0) return 0;
+    for (   ; i<lx; )
+      if (signe(gel(c,i++))) return 0;
+  }
+  return 1;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       MISCELLANEOUS                            **/
+/**                                                                **/
+/********************************************************************/
+/* assume lg(x) = lg(y), x,y in Z^n */
+int
+ZV_cmp(GEN x, GEN y)
+{
+  long fl,i, lx = lg(x);
+  for (i=1; i<lx; i++)
+    if (( fl = cmpii(gel(x,i), gel(y,i)) )) return fl;
+  return 0;
+}
+/* assume lg(x) = lg(y), x,y in Z^n */
+int
+ZV_abscmp(GEN x, GEN y)
+{
+  long fl,i, lx = lg(x);
+  for (i=1; i<lx; i++)
+    if (( fl = absi_cmp(gel(x,i), gel(y,i)) )) return fl;
+  return 0;
+}
+
+long
+zv_content(GEN x)
+{
+  long i, l = lg(x), s = labs(x[1]);
+  for (i=2; i<l && s!=1; i++) s = cgcd(x[i],s);
+  return s;
+}
+GEN
+ZV_content(GEN x)
+{
+  long i, l = lg(x);
+  pari_sp av = avma;
+  GEN c;
+  if (l == 1) return gen_1;
+  if (l == 2) return absi(gel(x,1));
+  c = gel(x,1);
+  for (i = 2; i < l; i++)
+  {
+    c = gcdii(c, gel(x,i));
+    if (is_pm1(c)) { avma = av; return gen_1; }
+  }
+  return gerepileuptoint(av, c);
+}
+
+GEN
+ZM_det_triangular(GEN mat)
+{
+  pari_sp av;
+  long i,l = lg(mat);
+  GEN s;
+
+  if (l<3) return l<2? gen_1: icopy(gcoeff(mat,1,1));
+  av = avma; s = gcoeff(mat,1,1);
+  for (i=2; i<l; i++) s = mulii(s,gcoeff(mat,i,i));
+  return gerepileuptoint(av,s);
+}
+
+/* assumes no overflow */
+long
+zv_prod(GEN v)
+{
+  long n, i, l = lg(v);
+  if (l == 1) return 1;
+  n = v[1]; for (i = 2; i < l; i++) n *= v[i];
+  return n;
+}
+/* product of ulongs */
+GEN
+zv_prod_Z(GEN v)
+{
+  pari_sp av = avma;
+  long k, n = lg(v)-1, m;
+  GEN x;
+  if (n == 0) return gen_1;
+  if (n == 1) return utoi(v[1]);
+  if (n == 2) return muluu(v[1], v[2]);
+  m = n >> 1;
+  x = cgetg(m + (odd(n)? 2: 1), t_VEC);
+  for (k = 1; k <= m; k++) gel(x,k) = muluu(v[k<<1], v[(k<<1)-1]);
+  if (odd(n)) gel(x,k) = utoipos(v[n]);
+  return gerepileuptoint(av, divide_conquer_prod(x, mulii));
+}
+
+GEN
+ZV_prod(GEN v)
+{
+  pari_sp av = avma;
+  long i, l = lg(v);
+  GEN n;
+  if (l == 1) return gen_1;
+  if (l > 7) return gerepileuptoint(av, divide_conquer_prod(v, mulii));
+  n = gel(v,1);
+  if (l == 2) return icopy(n);
+  for (i = 2; i < l; i++) n = mulii(n, gel(v,i));
+  return gerepileuptoint(av, n);
+}
+/* assumes no overflow */
+long
+zv_sum(GEN v)
+{
+  long n, i, l = lg(v);
+  if (l == 1) return 0;
+  n = v[1]; for (i = 2; i < l; i++) n += v[i];
+  return n;
+}
+GEN
+ZV_sum(GEN v)
+{
+  pari_sp av = avma;
+  long i, l = lg(v);
+  GEN n;
+  if (l == 1) return gen_0;
+  n = gel(v,1);
+  if (l == 2) return icopy(n);
+  for (i = 2; i < l; i++) n = addii(n, gel(v,i));
+  return gerepileuptoint(av, n);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**         GRAM SCHMIDT REDUCTION (integer matrices)              **/
+/**                                                                **/
+/********************************************************************/
+
+/* L[k,] += q * L[l,], l < k. Inefficient if q = 0 */
+static void
+Zupdate_row(long k, long l, GEN q, GEN L, GEN B)
+{
+  long i, qq = itos_or_0(q);
+  if (!qq)
+  {
+    for(i=1;i<l;i++)  gcoeff(L,k,i) = addii(gcoeff(L,k,i),mulii(q,gcoeff(L,l,i)));
+    gcoeff(L,k,l) = addii(gcoeff(L,k,l), mulii(q,B));
+    return;
+  }
+  if (qq == 1) {
+    for (i=1;i<l; i++) gcoeff(L,k,i) = addii(gcoeff(L,k,i),gcoeff(L,l,i));
+    gcoeff(L,k,l) = addii(gcoeff(L,k,l), B);
+  } else if (qq == -1) {
+    for (i=1;i<l; i++) gcoeff(L,k,i) = subii(gcoeff(L,k,i),gcoeff(L,l,i));
+    gcoeff(L,k,l) = addii(gcoeff(L,k,l), negi(B));
+  } else {
+    for(i=1;i<l;i++) gcoeff(L,k,i) = addii(gcoeff(L,k,i),mulsi(qq,gcoeff(L,l,i)));
+    gcoeff(L,k,l) = addii(gcoeff(L,k,l), mulsi(qq,B));
+  }
+}
+
+/* update L[k,] */
+static void
+ZRED(long k, long l, GEN x, GEN L, GEN B)
+{
+  GEN q = truedivii(addii(B,shifti(gcoeff(L,k,l),1)), shifti(B,1));
+  if (!signe(q)) return;
+  q = negi(q);
+  Zupdate_row(k,l,q,L,B);
+  gel(x,k) = ZC_lincomb(gen_1, q, gel(x,k), gel(x,l));
+}
+
+/* Gram-Schmidt reduction, x a ZM */
+static void
+ZincrementalGS(GEN x, GEN L, GEN B, long k)
+{
+  long i, j;
+  for (j=1; j<=k; j++)
+  {
+    pari_sp av = avma;
+    GEN u = ZV_dotproduct(gel(x,k), gel(x,j));
+    for (i=1; i<j; i++)
+    {
+      u = subii(mulii(gel(B,i+1), u), mulii(gcoeff(L,k,i), gcoeff(L,j,i)));
+      u = diviiexact(u, gel(B,i));
+    }
+    gcoeff(L,k,j) = gerepileuptoint(av, u);
+  }
+  gel(B,k+1) = gcoeff(L,k,k); gcoeff(L,k,k) = gen_1;
+}
+
+/* Variant reducemodinvertible(ZC v, ZM y), when y singular.
+ * Very inefficient if y is not LLL-reduced of maximal rank */
+static GEN
+ZC_reducemodmatrix_i(GEN v, GEN y)
+{
+  GEN B, L, x = shallowconcat(y, v);
+  long k, lx = lg(x), nx = lx-1;
+
+  B = scalarcol_shallow(gen_1, lx);
+  L = zeromatcopy(nx, nx);
+  for (k=1; k <= nx; k++) ZincrementalGS(x, L, B, k);
+  for (k = nx-1; k >= 1; k--) ZRED(nx,k, x,L,gel(B,k+1));
+  return gel(x,nx);
+}
+GEN
+ZC_reducemodmatrix(GEN v, GEN y) {
+  pari_sp av = avma;
+  return gerepilecopy(av, ZC_reducemodmatrix_i(v,y));
+}
+
+/* Variant reducemodinvertible(ZM v, ZM y), when y singular.
+ * Very inefficient if y is not LLL-reduced of maximal rank */
+static GEN
+ZM_reducemodmatrix_i(GEN v, GEN y)
+{
+  GEN B, L, V;
+  long j, k, lv = lg(v), nx = lg(y), lx = nx+1;
+
+  V = cgetg(lv, t_MAT);
+  B = scalarcol_shallow(gen_1, lx);
+  L = zeromatcopy(nx, nx);
+  for (k=1; k < nx; k++) ZincrementalGS(y, L, B, k);
+  for (j = 1; j < lg(v); j++)
+  {
+    GEN x = shallowconcat(y, gel(v,j));
+    ZincrementalGS(x, L, B, nx); /* overwrite last */
+    for (k = nx-1; k >= 1; k--) ZRED(nx,k, x,L,gel(B,k+1));
+    gel(V,j) = gel(x,nx);
+  }
+  return V;
+}
+GEN
+ZM_reducemodmatrix(GEN v, GEN y) {
+  pari_sp av = avma;
+  return gerepilecopy(av, ZM_reducemodmatrix_i(v,y));
+}
+
+GEN
+ZC_reducemodlll(GEN x,GEN y)
+{
+  pari_sp av = avma;
+  GEN z = ZC_reducemodmatrix(x, ZM_lll(y, 0.75, LLL_INPLACE));
+  return gerepilecopy(av, z);
+}
+GEN
+ZM_reducemodlll(GEN x,GEN y)
+{
+  pari_sp av = avma;
+  GEN z = ZM_reducemodmatrix(x, ZM_lll(y, 0.75, LLL_INPLACE));
+  return gerepilecopy(av, z);
+}
diff --git a/src/basemath/ZX.c b/src/basemath/ZX.c
new file mode 100644
index 0000000..ea274f7
--- /dev/null
+++ b/src/basemath/ZX.c
@@ -0,0 +1,773 @@
+/* Copyright (C) 2007  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*                                ZX                               */
+/*                                                                 */
+/*******************************************************************/
+void
+RgX_check_QX(GEN x, const char *s)
+{ if (!RgX_is_QX(x)) pari_err_TYPE(stack_strcat(s," [not in Q[X]]"), x); }
+void
+RgX_check_ZX(GEN x, const char *s)
+{ if (!RgX_is_ZX(x)) pari_err_TYPE(stack_strcat(s," [not in Z[X]]"), x); }
+long
+ZX_max_lg(GEN x)
+{
+  long i, prec = 0, lx = lg(x);
+
+  for (i=2; i<lx; i++) { long l = lgefint(gel(x,i)); if (l > prec) prec = l; }
+  return prec;
+}
+
+GEN
+ZX_add(GEN x, GEN y)
+{
+  long lx,ly,i;
+  GEN z;
+  lx = lg(x); ly = lg(y); if (lx < ly) swapspec(x,y, lx,ly);
+  z = cgetg(lx,t_POL); z[1] = x[1];
+  for (i=2; i<ly; i++) gel(z,i) = addii(gel(x,i),gel(y,i));
+  for (   ; i<lx; i++) gel(z,i) = icopy(gel(x,i));
+  if (lx == ly) z = ZX_renormalize(z, lx);
+  if (!lgpol(z)) { avma = (pari_sp)(z + lx); return pol_0(varn(x)); }
+  return z;
+}
+
+GEN
+ZX_sub(GEN x,GEN y)
+{
+  long i, lx = lg(x), ly = lg(y);
+  GEN z;
+  if (lx >= ly)
+  {
+    z = cgetg(lx,t_POL); z[1] = x[1];
+    for (i=2; i<ly; i++) gel(z,i) = subii(gel(x,i),gel(y,i));
+    if (lx == ly)
+    {
+      z = ZX_renormalize(z, lx);
+      if (!lgpol(z)) { avma = (pari_sp)(z + lx); z = pol_0(varn(x)); }
+    }
+    else
+      for (   ; i<lx; i++) gel(z,i) = icopy(gel(x,i));
+  }
+  else
+  {
+    z = cgetg(ly,t_POL); z[1] = y[1];
+    for (i=2; i<lx; i++) gel(z,i) = subii(gel(x,i),gel(y,i));
+    for (   ; i<ly; i++) gel(z,i) = negi(gel(y,i));
+  }
+  return z;
+}
+
+GEN
+ZX_neg(GEN x)
+{
+  long i, l = lg(x);
+  GEN y = cgetg(l,t_POL);
+  y[1] = x[1]; for(i=2; i<l; i++) gel(y,i) = negi(gel(x,i));
+  return y;
+}
+GEN
+ZX_copy(GEN x)
+{
+  long i, l = lg(x);
+  GEN y = cgetg(l, t_POL);
+  y[1] = x[1];
+  for (i=2; i<l; i++)
+  {
+    GEN c = gel(x,i);
+    gel(y,i) = lgefint(c) == 2? gen_0: icopy(c);
+  }
+  return y;
+}
+
+GEN
+scalar_ZX(GEN x, long v)
+{
+  GEN z;
+  if (!signe(x)) return pol_0(v);
+  z = cgetg(3, t_POL);
+  z[1] = evalsigne(1) | evalvarn(v);
+  gel(z,2) = icopy(x); return z;
+}
+
+GEN
+scalar_ZX_shallow(GEN x, long v)
+{
+  GEN z;
+  if (!signe(x)) return pol_0(v);
+  z = cgetg(3, t_POL);
+  z[1] = evalsigne(1) | evalvarn(v);
+  gel(z,2) = x; return z;
+}
+
+GEN
+ZX_Z_add(GEN y, GEN x)
+{
+  long lz, i;
+  GEN z = cgetg_copy(y, &lz);
+  if (lz == 2) { avma = (pari_sp)(z + 2); return scalar_ZX(x,varn(y)); }
+  z[1] = y[1];
+  gel(z,2) = addii(gel(y,2),x);
+  for(i=3; i<lz; i++) gel(z,i) = icopy(gel(y,i));
+  if (lz==3) z = ZX_renormalize(z,lz);
+  return z;
+}
+GEN
+ZX_Z_add_shallow(GEN y, GEN x)
+{
+  long lz, i;
+  GEN z = cgetg_copy(y, &lz);
+  if (lz == 2) { avma = (pari_sp)(z + 2); return scalar_ZX_shallow(x,varn(y)); }
+  z[1] = y[1];
+  gel(z,2) = addii(gel(y,2),x);
+  for(i=3; i<lz; i++) gel(z,i) = gel(y,i);
+  if (lz==3) z = ZX_renormalize(z,lz);
+  return z;
+}
+
+GEN
+ZX_Z_sub(GEN y, GEN x)
+{
+  long lz, i;
+  GEN z = cgetg_copy(y, &lz);
+  if (lz == 2)
+  { /* scalarpol(negi(x), v) */
+    long v = varn(y);
+    avma = (pari_sp)(z + 2);
+    if (!signe(x)) return pol_0(v);
+    z = cgetg(3,t_POL);
+    z[1] = evalvarn(v) | evalsigne(1);
+    gel(z,2) = negi(x); return z;
+  }
+  z[1] = y[1];
+  gel(z,2) = subii(gel(y,2),x);
+  for(i=3; i<lz; i++) gel(z,i) = icopy(gel(y,i));
+  if (lz==3) z = ZX_renormalize(z,lz);
+  return z;
+}
+
+GEN
+Z_ZX_sub(GEN x, GEN y)
+{
+  long lz, i;
+  GEN z = cgetg_copy(y, &lz);
+  if (lz == 2) { avma = (pari_sp)(z + 2); return scalar_ZX(x,varn(y)); }
+  z[1] = y[1];
+  gel(z,2) = subii(x, gel(y,2));
+  for(i=3; i<lz; i++) gel(z,i) = negi(gel(y,i));
+  if (lz==3) z = ZX_renormalize(z,lz);
+  return z;
+}
+
+GEN
+ZX_Z_divexact(GEN y,GEN x)
+{
+  long i, l = lg(y);
+  GEN z = cgetg(l,t_POL); z[1] = y[1];
+  for(i=2; i<l; i++) gel(z,i) = diviiexact(gel(y,i),x);
+  return z;
+}
+
+GEN
+ZX_Z_mul(GEN y,GEN x)
+{
+  GEN z;
+  long i, l;
+  if (!signe(x)) return pol_0(varn(y));
+  l = lg(y); z = cgetg(l,t_POL); z[1] = y[1];
+  for(i=2; i<l; i++) gel(z,i) = mulii(gel(y,i),x);
+  return z;
+}
+
+GEN
+ZX_mulu(GEN y, ulong x)
+{
+  GEN z;
+  long i, l;
+  if (!x) return pol_0(varn(y));
+  l = lg(y); z = cgetg(l,t_POL); z[1] = y[1];
+  for(i=2; i<l; i++) gel(z,i) = mului(x,gel(y,i));
+  return z;
+}
+
+GEN
+ZX_shifti(GEN y, long n)
+{
+  GEN z;
+  long i, l;
+  l = lg(y); z = cgetg(l,t_POL); z[1] = y[1];
+  for(i=2; i<l; i++) gel(z,i) = shifti(gel(y,i),n);
+  return ZX_renormalize(z,l);
+}
+
+GEN
+ZX_remi2n(GEN y, long n)
+{
+  GEN z;
+  long i, l;
+  l = lg(y); z = cgetg(l,t_POL); z[1] = y[1];
+  for(i=2; i<l; i++) gel(z,i) = remi2n(gel(y,i),n);
+  return ZX_renormalize(z,l);
+}
+
+GEN
+ZXT_remi2n(GEN z, long n)
+{
+  if (typ(z) == t_POL)
+    return ZX_remi2n(z, n);
+  else
+  {
+    long i,l = lg(z);
+    GEN x = cgetg(l, t_VEC);
+    for (i=1; i<l; i++) gel(x,i) = ZXT_remi2n(gel(z,i), n);
+    return x;
+  }
+}
+
+GEN
+zx_to_ZX(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_POL);
+  for (i=2; i<l; i++) gel(x,i) = stoi(z[i]);
+  x[1] = evalsigne(l-2!=0)| z[1]; return x;
+}
+
+GEN
+ZX_deriv(GEN x)
+{
+  long i,lx = lg(x)-1;
+  GEN y;
+
+  if (lx<3) return pol_0(varn(x));
+  y = cgetg(lx,t_POL);
+  for (i=2; i<lx ; i++) gel(y,i) = mului(i-1,gel(x,i+1));
+  y[1] = x[1]; return y;
+}
+
+int
+ZX_equal(GEN V, GEN W)
+{
+  long i, l = lg(V);
+  if (lg(W) != l) return 0;
+  for (i = 2; i < l; i++)
+    if (!equalii(gel(V,i), gel(W,i))) return 0;
+  return 1;
+}
+
+static long
+ZX_valspec(GEN x, long nx)
+{
+  long vx;
+  for (vx = 0; vx<nx ; vx++)
+    if (signe(gel(x,vx))) break;
+  return vx;
+}
+
+long
+ZX_val(GEN x)
+{
+  long vx;
+  if (!signe(x)) return LONG_MAX;
+  for (vx = 0;; vx++)
+    if (signe(gel(x,2+vx))) break;
+  return vx;
+}
+long
+ZX_valrem(GEN x, GEN *Z)
+{
+  long vx;
+  if (!signe(x)) { *Z = pol_0(varn(x)); return LONG_MAX; }
+  for (vx = 0;; vx++)
+    if (signe(gel(x,2+vx))) break;
+  *Z = RgX_shift_shallow(x, -vx);
+  return vx;
+}
+
+/* Return h^deg(P) P(x / h), not memory clean. h integer, P ZX */
+GEN
+ZX_rescale(GEN P, GEN h)
+{
+  long l = lg(P);
+  GEN Q = cgetg(l,t_POL);
+  if (l != 2)
+  {
+    long i = l-1;
+    GEN hi = h;
+    gel(Q,i) = gel(P,i);
+    if (l != 3) { i--; gel(Q,i) = mulii(gel(P,i), h); }
+    for (i--; i>=2; i--) { hi = mulii(hi,h); gel(Q,i) = mulii(gel(P,i), hi); }
+  }
+  Q[1] = P[1]; return Q;
+}
+/* Return h^(deg(P)-1) P(x / h), P!=0, h=lt(P), memory unclean; monic result */
+GEN
+ZX_rescale_lt(GEN P)
+{
+  long l = lg(P);
+  GEN Q = cgetg(l,t_POL);
+  gel(Q,l-1) = gen_1;
+  if (l != 3)
+  {
+    long i = l-1;
+    GEN h = gel(P,i), hi = h;
+    i--; gel(Q,i) = gel(P,i);
+    if (l != 4) { i--; gel(Q,i) = mulii(gel(P,i), h); }
+    for (i--; i>=2; i--) { hi = mulii(hi,h); gel(Q,i) = mulii(gel(P,i), hi); }
+  }
+  Q[1] = P[1]; return Q;
+}
+
+
+/*Eval x in 2^(k*BIL) in linear time*/
+static GEN
+ZX_eval2BILspec(GEN x, long k, long nx)
+{
+  long i,j, lz = k*nx, ki;
+  GEN pz = cgetipos(2+lz);
+  GEN nz = cgetipos(2+lz);
+  for(i=0; i < lz; i++)
+  {
+    *int_W(pz,i) = 0UL;
+    *int_W(nz,i) = 0UL;
+  }
+  for(i=0, ki=0; i<nx; i++, ki+=k)
+  {
+    GEN c = gel(x,i);
+    long lc = lgefint(c)-2;
+    if (signe(c)==0) continue;
+    if (signe(c) > 0)
+      for (j=0; j<lc; j++) *int_W(pz,j+ki) = *int_W(c,j);
+    else
+      for (j=0; j<lc; j++) *int_W(nz,j+ki) = *int_W(c,j);
+  }
+  pz = int_normalize(pz,0);
+  nz = int_normalize(nz,0); return subii(pz,nz);
+}
+
+static long
+ZX_expispec(GEN x, long nx)
+{
+  long i, m = 0;
+  for(i = 0; i < nx; i++)
+  {
+    long e = expi(gel(x,i));
+    if (e > m) m = e;
+  }
+  return m;
+}
+
+static GEN
+Z_mod2BIL_ZX(GEN x, long bs, long d, long vx)
+{
+  long i, offset, lm = lgefint(x)-2, l = d+vx+3, sx = signe(x);
+  GEN s1 = int2n(bs*BITS_IN_LONG), pol = cgetg(l, t_POL);
+  int carry = 0;
+
+  for (i=0; i<vx; i++) gel(pol,i+2) = gen_0;
+  for (offset=0; i <= d+vx; i++, offset += bs)
+  {
+    pari_sp av = avma;
+    long lz = minss(bs, lm-offset);
+    GEN z = adduispec_offset(carry, x, offset, lz);
+    if (lgefint(z) == 3+bs) { carry = 1; z = gen_0;}
+    else
+    {
+      carry = (lgefint(z) == 2+bs && (HIGHBIT & *int_W(z,bs-1)));
+      if (carry)
+        z = gerepileuptoint(av, (sx==-1)? subii(s1,z): subii(z,s1));
+      else if (sx==-1) togglesign(z);
+    }
+    gel(pol,i+2) = z;
+  }
+  return ZX_renormalize(pol,l);
+}
+
+static GEN
+ZX_sqrspec_sqri(GEN x, long nx, long ex, long v)
+{
+  long e = 2*ex + expu(nx) + 3;
+  long N = divsBIL(e)+1;
+  GEN  z = sqri(ZX_eval2BILspec(x,N,nx));
+  return Z_mod2BIL_ZX(z, N, nx*2-2, v);
+}
+
+static GEN
+ZX_mulspec_mulii(GEN x, GEN y, long nx, long ny, long ex, long ey, long v)
+{
+  long e = ex + ey + expu(minss(nx,ny)) + 3;
+  long N = divsBIL(e)+1;
+  GEN  z = mulii(ZX_eval2BILspec(x,N,nx), ZX_eval2BILspec(y,N,ny));
+  return Z_mod2BIL_ZX(z, N, nx+ny-2, v);
+}
+
+INLINE GEN
+ZX_sqrspec_basecase_limb(GEN x, long a, long i)
+{
+  pari_sp av = avma;
+  GEN s = gen_0;
+  long j, l = (i+1)>>1;
+  for (j=a; j<l; j++)
+  {
+    GEN xj = gel(x,j), xx = gel(x,i-j);
+    if (signe(xj) && signe(xx))
+      s = addii(s, mulii(xj, xx));
+  }
+  s = shifti(s,1);
+  if ((i&1) == 0)
+  {
+    GEN t = gel(x, i>>1);
+    if (signe(t))
+      s = addii(s, sqri(t));
+  }
+  return gerepileuptoint(av,s);
+}
+
+static GEN
+ZX_sqrspec_basecase(GEN x, long nx, long v)
+{
+  long i, lz, nz;
+  GEN z;
+
+  lz = (nx << 1) + 1; nz = lz-2;
+  lz += v;
+  z = cgetg(lz,t_POL); z[1] = evalsigne(1); z += 2;
+  for (i=0; i<v; i++) gel(z++, 0) = gen_0;
+  for (i=0; i<nx; i++)
+    gel(z,i) = ZX_sqrspec_basecase_limb(x, 0, i);
+  for (  ; i<nz; i++) gel(z,i) = ZX_sqrspec_basecase_limb(x, i-nx+1, i);
+  z -= v+2; return z;
+}
+
+static GEN
+Z_sqrshiftspec_ZX(GEN x, long vx)
+{
+  long i, nz = 2*vx+1;
+  GEN z = cgetg(2+nz, t_POL);
+  z[1] = evalsigne(1);
+  for(i=2;i<nz+1;i++) gel(z,i) = gen_0;
+  gel(z,nz+1) = sqri(x);
+  return z;
+}
+
+static GEN
+Z_ZX_mulshiftspec(GEN x, GEN y, long ny, long vz)
+{
+  long i, nz = vz+ny;
+  GEN z = cgetg(2+nz, t_POL);
+  z[1] = evalsigne(1);
+  for (i=0; i<vz; i++)   gel(z,i+2)    = gen_0;
+  for (i=0; i<ny; i++) gel(z,i+vz+2) = mulii(x, gel(y,i));
+  return z;
+}
+
+GEN
+ZX_sqrspec(GEN x, long nx)
+{
+#ifdef PARI_KERNEL_GMP
+  const long low[]={ 17, 32, 96, 112, 160, 128, 128, 160, 160, 160, 160, 160, 176, 192, 192, 192, 192, 192, 224, 224, 224, 240, 240, 240, 272, 288, 288, 240, 288, 304, 304, 304, 304, 304, 304, 352, 352, 368, 352, 352, 352, 368, 368, 432, 432, 496, 432, 496, 496};
+  const long high[]={ 102860, 70254, 52783, 27086, 24623, 18500, 15289, 13899, 12635, 11487, 10442, 9493, 8630, 7845, 7132, 7132, 6484, 6484, 5894, 5894, 4428, 4428, 3660, 4428, 3660, 3660, 2749, 2499, 2272, 2066, 1282, 1282, 1166, 1166, 1166, 1166, 1166, 1166, 1166, 963, 963, 724, 658, 658, 658, 528, 528, 528, 528};
+#else
+  const long low[]={ 17, 17, 32, 32, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 112, 112, 128, 112, 112, 112, 112, 112, 128, 128, 160, 160, 112, 128, 128, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 176, 160, 160, 176, 160, 160, 176, 176, 208, 176, 176, 176, 192, 192, 176, 176, 224, 176, 224, 224, 176, 224, 224, 224, 176, 176, 176, 176, 176, 176, 176, 176, 224, 176, 176, 224, 224, 224, 224, 224, 224, 224, 240, 288, 240, 288, 288, 240, 288, 288, 240,  [...]
+  const long high[]={ 165657, 85008, 52783, 43622, 32774, 27086, 22385, 15289, 13899, 12635, 11487, 10442, 9493, 7845, 6484, 6484, 5894, 5894, 4871, 4871, 4428, 4026, 3660, 3660, 3660, 3327, 3327, 3024, 2749, 2749, 2272, 2749, 2499, 2499, 2272, 1878, 1878, 1878, 1707, 1552, 1552, 1552, 1552, 1552, 1411, 1411, 1411, 1282, 1282, 1282, 1282, 1282, 1166, 1166, 1166, 1166, 1166, 1166, 1166, 1166, 1060, 1060, 963, 963, 963, 963, 963, 963, 963, 963, 963, 963, 963, 876, 876, 876, 876, 796, 658,  [...]
+#endif
+  const long nblow = sizeof(low)/sizeof(*low);
+  pari_sp av;
+  long ex, vx;
+  GEN z;
+  if (!nx) return pol_0(0);
+  vx = ZX_valspec(x,nx); nx-=vx; x+=vx;
+  if (nx==1) return Z_sqrshiftspec_ZX(gel(x, 0), vx);
+  av = avma;
+  ex = ZX_expispec(x,nx);
+  if (nx-2 < nblow && low[nx-2]<=ex && ex<=high[nx-2])
+    z = ZX_sqrspec_basecase(x, nx, 2*vx);
+  else
+    z = ZX_sqrspec_sqri(x, nx, ex, 2*vx);
+  return gerepileupto(av, z);
+}
+
+GEN
+ZX_sqr(GEN x)
+{
+  GEN z = ZX_sqrspec(x+2, lgpol(x));
+  z[1] = x[1];
+  return z;
+}
+
+GEN
+ZX_mulspec(GEN x, GEN y, long nx, long ny)
+{
+  pari_sp av;
+  long ex, ey, vx, vy;
+  GEN z;
+  if (!nx || !ny) return pol_0(0);
+  vx = ZX_valspec(x,nx); nx-=vx; x += vx;
+  vy = ZX_valspec(y,ny); ny-=vy; y += vy;
+  if (nx==1) return Z_ZX_mulshiftspec(gel(x,0), y, ny, vx+vy);
+  if (ny==1) return Z_ZX_mulshiftspec(gel(y,0), x, nx, vy+vx);
+  av = avma;
+  ex = ZX_expispec(x, nx); ey = ZX_expispec(y, ny);
+  z  = ZX_mulspec_mulii(x,y,nx,ny,ex,ey,vx+vy);
+  return gerepileupto(av, z);
+}
+GEN
+ZX_mul(GEN x, GEN y)
+{
+  GEN z;
+  if (x == y) return ZX_sqr(x);
+  z = ZX_mulspec(x+2,y+2,lgpol(x),lgpol(y));
+  z[1] = x[1];
+  if (!signe(y)) z[1] &= VARNBITS;
+  return z;
+}
+
+/* x,y two ZX in the same variable; assume y is monic */
+GEN
+ZX_rem(GEN x, GEN y)
+{
+  long vx, dx, dy, dz, i, j, sx, lr;
+  pari_sp av0, av;
+  GEN z,p1,rem;
+
+  vx = varn(x);
+  dy = degpol(y);
+  dx = degpol(x);
+  if (dx < dy) return ZX_copy(x);
+  if (!dy) return pol_0(vx); /* y is constant */
+  av0 = avma; dz = dx-dy;
+  z=cgetg(dz+3,t_POL); z[1] = x[1];
+  x += 2; y += 2; z += 2;
+
+  p1 = gel(x,dx);
+  gel(z,dz) = icopy(p1);
+  for (i=dx-1; i>=dy; i--)
+  {
+    av=avma; p1=gel(x,i);
+    for (j=i-dy+1; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    gel(z,i-dy) = avma == av? icopy(p1): gerepileuptoint(av, p1);
+  }
+  rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
+  for (sx=0; ; i--)
+  {
+    p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    if (signe(p1)) { sx = 1; break; }
+    if (!i) break;
+    avma=av;
+  }
+  lr=i+3; rem -= lr;
+  rem[0] = evaltyp(t_POL) | evallg(lr);
+  rem[1] = z[-1];
+  p1 = gerepileuptoint((pari_sp)rem, p1);
+  rem += 2; gel(rem,i) = p1;
+  for (i--; i>=0; i--)
+  {
+    av=avma; p1 = gel(x,i);
+    for (j=0; j<=i && j<=dz; j++)
+      p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
+    gel(rem,i) = avma == av? icopy(p1): gerepileuptoint(av, p1);
+  }
+  rem -= 2;
+  if (!sx) (void)ZX_renormalize(rem, lr);
+  return gerepileupto(av0,rem);
+}
+
+/* return x(1) */
+GEN
+ZX_eval1(GEN x)
+{
+  pari_sp av = avma;
+  long i = lg(x)-1;
+  GEN s;
+  if (i < 2) return gen_0;
+  s = gel(x,i); i--;
+  if (i == 1) return icopy(s);
+  for ( ; i>=2; i--)
+  {
+    GEN c = gel(x,i);
+    if (signe(c)) s = addii(s, c);
+  }
+  return gerepileuptoint(av,s);
+}
+
+/* reduce T mod X^n - 1. Shallow function */
+GEN
+ZX_mod_Xnm1(GEN T, ulong n)
+{
+  long i, j, L = lg(T), l = n+2;
+  GEN S;
+  if (L <= l) return T;
+  S = cgetg(l, t_POL);
+  S[1] = T[1];
+  for (i = 2; i < l; i++) gel(S,i) = gel(T,i);
+  for (j = 2; i < L; i++) {
+    gel(S,j) = addii(gel(S,j), gel(T,i));
+    if (++j == l) j = 2;
+  }
+  return normalizepol_lg(S, l);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                                ZXV                              */
+/*                                                                 */
+/*******************************************************************/
+
+int
+ZXV_equal(GEN V, GEN W)
+{
+  long l = lg(V);
+  if (l!=lg(W)) return 0;
+  while (--l > 0)
+    if (!ZX_equal(gel(V,l), gel(W,l))) return 0;
+  return 1;
+}
+
+GEN
+ZXV_Z_mul(GEN y, GEN x)
+{
+  long i, l;
+  GEN z = cgetg_copy(y, &l);
+  for(i=1; i<l; i++) gel(z,i) = ZX_Z_mul(gel(y,i), x);
+  return z;
+}
+
+GEN
+ZXV_remi2n(GEN y, long N)
+{
+  long i, l;
+  GEN z = cgetg_copy(y, &l);
+  for(i=1; i<l; i++) gel(z,i) = ZX_remi2n(gel(y,i), N);
+  return z;
+}
+GEN
+ZXV_dotproduct(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long i, lx = lg(x);
+  GEN c;
+  if (lx == 1) return pol_0(varn(x));
+  c = ZX_mul(gel(x,1), gel(y,1));
+  for (i = 2; i < lx; i++)
+  {
+    GEN t = ZX_mul(gel(x,i), gel(y,i));
+    if (signe(t)) c = ZX_add(c, t);
+  }
+  return gerepileupto(av, c);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                                ZXX                              */
+/*                                                                 */
+/*******************************************************************/
+
+void
+RgX_check_ZXX(GEN x, const char *s)
+{
+  long k = lg(x)-1;
+  for ( ; k>1; k--) {
+    GEN t = gel(x,k);
+    switch(typ(t)) {
+      case t_INT: break;
+      case t_POL: if (RgX_is_ZX(t)) break;
+      /* fall through */
+      default: pari_err_TYPE(stack_strcat(s, " not in Z[X,Y]"),x);
+    }
+  }
+}
+
+/*Renormalize (in place) polynomial with t_INT or ZX coefficients.*/
+GEN
+ZXX_renormalize(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>1; i--)
+    if (signe(gel(x,i))) break;
+  stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + (i+1)));
+  setlg(x, i+1); setsigne(x, i!=1); return x;
+}
+
+long
+ZXX_max_lg(GEN x)
+{
+  long i, prec = 0, lx = lg(x);
+  for (i=2; i<lx; i++)
+  {
+    GEN p = gel(x,i);
+    long l = (typ(p) == t_INT)? lgefint(p): ZX_max_lg(p);
+    if (l > prec) prec = l;
+  }
+  return prec;
+}
+
+GEN
+ZXX_Z_divexact(GEN y, GEN x)
+{
+  long i, l = lg(y);
+  GEN z = cgetg(l,t_POL); z[1] = y[1];
+  for(i=2; i<l; i++)
+    if(typ(gel(y,i))==t_INT)
+      gel(z,i) = diviiexact(gel(y,i),x);
+    else
+      gel(z,i) = ZX_Z_divexact(gel(y,i),x);
+  return z;
+}
+
+/* Kronecker substitution, ZXX -> ZX:
+ * P(X,Y) = sum_{0<=i<lP} P_i(X) * Y^i, where deg P_i < n.
+ * Returns P(X,X^(2n-1)) */
+GEN
+ZXX_to_Kronecker_spec(GEN P, long lP, long n)
+{
+  long i, k, N = (n<<1) + 1;
+  GEN y;
+  if (!lP) return pol_0(0);
+  y = cgetg((N-2)*lP + 2, t_POL) + 2;
+  for (k=i=0; i<lP; i++)
+  {
+    long j;
+    GEN c = gel(P,i);
+    if (typ(c)==t_INT)
+    {
+      gel(y,k++) = c;
+      j = 3;
+    }
+    else
+    {
+      long l = lg(c);
+      if (l-3 >= n)
+        pari_err_BUG("ZXX_to_Kronecker, P is not reduced mod Q");
+      for (j=2; j < l; j++) gel(y,k++) = gel(c,j);
+    }
+    if (i == lP-1) break;
+    for (   ; j < N; j++) gel(y,k++) = gen_0;
+  }
+  y-=2; setlg(y, k+2); y[1] = evalsigne(1); return y;
+}
+
+GEN
+ZXX_to_Kronecker(GEN P, long n)
+{
+  GEN z = ZXX_to_Kronecker_spec(P+2, lgpol(P), n);
+  setvarn(z,varn(P)); return z;
+}
diff --git a/src/basemath/alglin1.c b/src/basemath/alglin1.c
new file mode 100644
index 0000000..d3868dc
--- /dev/null
+++ b/src/basemath/alglin1.c
@@ -0,0 +1,4194 @@
+/* Copyright (C) 2000, 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                         LINEAR ALGEBRA                         **/
+/**                          (first part)                          **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*                         GEREPILE                                */
+/*                                                                 */
+/*******************************************************************/
+
+static void
+gerepile_mat(pari_sp av, pari_sp tetpil, GEN x, long k, long m, long n, long t)
+{
+  pari_sp A;
+  long u, i;
+  size_t dec;
+
+  (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
+
+  for (u=t+1; u<=m; u++)
+  {
+    A = (pari_sp)coeff(x,u,k);
+    if (A < av && A >= bot) coeff(x,u,k) += dec;
+  }
+  for (i=k+1; i<=n; i++)
+    for (u=1; u<=m; u++)
+    {
+      A = (pari_sp)coeff(x,u,i);
+      if (A < av && A >= bot) coeff(x,u,i) += dec;
+    }
+}
+
+static void
+gen_gerepile_gauss_ker(GEN x, long k, long t, pari_sp av, void *E, GEN (*copy)(void*, GEN))
+{
+  pari_sp tetpil = avma;
+  long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
+
+  if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot_ker. k=%ld, n=%ld",k,n);
+  for (u=t+1; u<=m; u++) gcoeff(x,u,k) = copy(E,gcoeff(x,u,k));
+  for (i=k+1; i<=n; i++)
+    for (u=1; u<=m; u++) gcoeff(x,u,i) = copy(E,gcoeff(x,u,i));
+  gerepile_mat(av,tetpil,x,k,m,n,t);
+}
+
+/* special gerepile for huge matrices */
+
+#define COPY(x) {\
+  GEN _t = (x); if (!is_universal_constant(_t)) x = gcopy(_t); \
+}
+
+INLINE GEN
+_copy(void *E, GEN x)
+{
+  (void) E; COPY(x);
+  return x;
+}
+
+static void
+gerepile_gauss_ker(GEN x, long k, long t, pari_sp av)
+{
+  return gen_gerepile_gauss_ker(x, k, t, av, NULL, &_copy);
+}
+
+static void
+gerepile_gauss(GEN x,long k,long t,pari_sp av, long j, GEN c)
+{
+  pari_sp tetpil = avma, A;
+  long u,i, n = lg(x)-1, m = n? nbrows(x): 0;
+  size_t dec;
+
+  if (DEBUGMEM > 1) pari_warn(warnmem,"gauss_pivot. k=%ld, n=%ld",k,n);
+  for (u=t+1; u<=m; u++)
+    if (u==j || !c[u]) COPY(gcoeff(x,u,k));
+  for (u=1; u<=m; u++)
+    if (u==j || !c[u])
+      for (i=k+1; i<=n; i++) COPY(gcoeff(x,u,i));
+
+  (void)gerepile(av,tetpil,NULL); dec = av-tetpil;
+  for (u=t+1; u<=m; u++)
+    if (u==j || !c[u])
+    {
+      A=(pari_sp)coeff(x,u,k);
+      if (A<av && A>=bot) coeff(x,u,k)+=dec;
+    }
+  for (u=1; u<=m; u++)
+    if (u==j || !c[u])
+      for (i=k+1; i<=n; i++)
+      {
+        A=(pari_sp)coeff(x,u,i);
+        if (A<av && A>=bot) coeff(x,u,i)+=dec;
+      }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         GENERIC                                 */
+/*                                                                 */
+/*******************************************************************/
+GEN
+gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff)
+{
+  pari_sp av0 = avma, av, lim, tetpil;
+  GEN y, c, d;
+  long i, j, k, r, t, n, m;
+
+  n=lg(x)-1; if (!n) return cgetg(1,t_MAT);
+  m=nbrows(x); r=0;
+  x = RgM_shallowcopy(x);
+  c = zero_zv(m);
+  d=new_chunk(n+1);
+  av=avma; lim=stack_lim(av,1);
+  for (k=1; k<=n; k++)
+  {
+    for (j=1; j<=m; j++)
+      if (!c[j])
+      {
+        gcoeff(x,j,k) = ff->red(E, gcoeff(x,j,k));
+        if (!ff->equal0(gcoeff(x,j,k))) break;
+      }
+    if (j>m)
+    {
+      if (deplin)
+      {
+        GEN c = cgetg(n+1, t_COL), g0 = ff->s(E,0), g1=ff->s(E,1);
+        for (i=1; i<k; i++) gel(c,i) = ff->red(E, gcoeff(x,d[i],k));
+        gel(c,k) = g1; for (i=k+1; i<=n; i++) gel(c,i) = g0;
+        return gerepileupto(av0, c);
+      }
+      r++; d[k]=0;
+      for(j=1; j<k; j++)
+        if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
+    }
+    else
+    {
+      GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
+      c[j] = k; d[k] = j;
+      gcoeff(x,j,k) = ff->s(E,-1);
+      for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
+      for (t=1; t<=m; t++)
+      {
+        if (t==j) continue;
+
+        piv = ff->red(E,gcoeff(x,t,k));
+        if (ff->equal0(piv)) continue;
+
+        gcoeff(x,t,k) = ff->s(E,0);
+        for (i=k+1; i<=n; i++)
+           gcoeff(x,t,i) = ff->add(E, gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
+        if (low_stack(lim, stack_lim(av,1)))
+          gen_gerepile_gauss_ker(x,k,t,av,E,ff->red);
+      }
+    }
+  }
+  if (deplin) { avma = av0; return NULL; }
+
+  tetpil=avma; y=cgetg(r+1,t_MAT);
+  for (j=k=1; j<=r; j++,k++)
+  {
+    GEN C = cgetg(n+1,t_COL);
+    GEN g0 = ff->s(E,0), g1 = ff->s(E,1);
+    gel(y,j) = C; while (d[k]) k++;
+    for (i=1; i<k; i++)
+      if (d[i])
+      {
+        GEN p1=gcoeff(x,d[i],k);
+        gel(C,i) = ff->red(E,p1); gunclone(p1);
+      }
+      else
+        gel(C,i) = g0;
+    gel(C,k) = g1; for (i=k+1; i<=n; i++) gel(C,i) = g0;
+  }
+  return gerepile(av0,tetpil,y);
+}
+
+GEN
+gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff)
+{
+  pari_sp av, lim;
+  GEN c, d;
+  long i, j, k, r, t, m, n = lg(x)-1;
+
+  if (!n) { *rr = 0; return NULL; }
+
+  m=nbrows(x); r=0;
+  d = cgetg(n+1, t_VECSMALL);
+  x = RgM_shallowcopy(x);
+  c = zero_zv(m);
+  av=avma; lim=stack_lim(av,1);
+  for (k=1; k<=n; k++)
+  {
+    for (j=1; j<=m; j++)
+      if (!c[j])
+      {
+        gcoeff(x,j,k) = ff->red(E,gcoeff(x,j,k));
+        if (!ff->equal0(gcoeff(x,j,k))) break;
+      }
+    if (j>m) { r++; d[k]=0; }
+    else
+    {
+      GEN piv = ff->neg(E,ff->inv(E,gcoeff(x,j,k)));
+      GEN g0 = ff->s(E,0);
+      c[j] = k; d[k] = j;
+      for (i=k+1; i<=n; i++) gcoeff(x,j,i) = ff->red(E,ff->mul(E,piv,gcoeff(x,j,i)));
+      for (t=1; t<=m; t++)
+      {
+        if (c[t]) continue; /* already a pivot on that line */
+
+        piv = ff->red(E,gcoeff(x,t,k));
+        if (ff->equal0(piv)) continue;
+        gcoeff(x,t,k) = g0;
+        for (i=k+1; i<=n; i++)
+          gcoeff(x,t,i) = ff->add(E,gcoeff(x,t,i), ff->mul(E,piv,gcoeff(x,j,i)));
+        if (low_stack(lim, stack_lim(av,1)))
+          gerepile_gauss(x,k,t,av,j,c);
+      }
+      for (i=k; i<=n; i++) gcoeff(x,j,i) = g0; /* dummy */
+    }
+  }
+  *rr = r; avma = (pari_sp)d; return d;
+}
+
+GEN
+gen_det(GEN a, void *E, const struct bb_field *ff)
+{
+  pari_sp av = avma, lim = stack_lim(av,1);
+  long i,j,k, s = 1, nbco = lg(a)-1;
+  GEN q, x = ff->s(E,1);
+  a = RgM_shallowcopy(a);
+  for (i=1; i<nbco; i++)
+  {
+    for(k=i; k<=nbco; k++)
+    {
+      gcoeff(a,k,i) = ff->red(E,gcoeff(a,k,i));
+      if (!ff->equal0(gcoeff(a,k,i))) break;
+    }
+    if (k > nbco) return gerepileupto(av, gcoeff(a,i,i));
+    if (k != i)
+    { /* exchange the lines s.t. k = i */
+      for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
+      s = -s;
+    }
+    q = gcoeff(a,i,i);
+
+    x = ff->red(E,ff->mul(E,x,q));
+    q = ff->inv(E,q);
+    for (k=i+1; k<=nbco; k++)
+    {
+      GEN m = ff->red(E,gcoeff(a,i,k));
+      if (ff->equal0(m)) continue;
+
+      m = ff->neg(E, ff->mul(E,m, q));
+      for (j=i+1; j<=nbco; j++)
+      {
+        gcoeff(a,j,k) = ff->add(E, gcoeff(a,j,k), ff->mul(E,m,gcoeff(a,j,i)));
+        if (low_stack(lim, stack_lim(av,1)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
+          gerepileall(av,4, &a,&x,&q,&m);
+        }
+      }
+    }
+  }
+  if (s < 0) x = ff->neg(E,x);
+  return gerepileupto(av, ff->red(E,ff->mul(E, x, gcoeff(a,nbco,nbco))));
+}
+
+INLINE void
+_gen_addmul(GEN b, long k, long i, GEN m, void *E, const struct bb_field *ff)
+{
+  gel(b,i) = ff->red(E,gel(b,i));
+  gel(b,k) = ff->add(E,gel(b,k), ff->mul(E,m, gel(b,i)));
+}
+
+static GEN
+_gen_get_col(GEN a, GEN b, long li, void *E, const struct bb_field *ff)
+{
+  GEN u = cgetg(li+1,t_COL);
+  pari_sp av = avma;
+  long i, j;
+
+  gel(u,li) = gerepileupto(av, ff->red(E,ff->mul(E,gel(b,li), gcoeff(a,li,li))));
+  for (i=li-1; i>0; i--)
+  {
+    pari_sp av = avma;
+    GEN m = gel(b,i);
+    for (j=i+1; j<=li; j++) m = ff->add(E,m, ff->neg(E,ff->mul(E,gcoeff(a,i,j), gel(u,j))));
+    m = ff->red(E, m);
+    gel(u,i) = gerepileupto(av, ff->red(E,ff->mul(E,m, gcoeff(a,i,i))));
+  }
+  return u;
+}
+
+GEN
+gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff)
+{
+  long i, j, k, li, bco, aco;
+  GEN u, g0 = ff->s(E,0);
+  pari_sp av = avma, lim = stack_lim(av,1);
+  a = RgM_shallowcopy(a);
+  b = RgM_shallowcopy(b);
+  aco = lg(a)-1; bco = lg(b)-1; li = nbrows(a);
+  for (i=1; i<=aco; i++)
+  {
+    GEN invpiv;
+    for (k = i; k <= li; k++)
+    {
+      GEN piv = ff->red(E,gcoeff(a,k,i));
+      if (!ff->equal0(piv)) { gcoeff(a,k,i) = ff->inv(E,piv); break; }
+      gcoeff(a,k,i) = g0;
+    }
+    /* found a pivot on line k */
+    if (k > li) return NULL;
+    if (k != i)
+    { /* swap lines so that k = i */
+      for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
+      for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
+    }
+    if (i == aco) break;
+
+    invpiv = gcoeff(a,i,i); /* 1/piv mod p */
+    for (k=i+1; k<=li; k++)
+    {
+      GEN m = ff->red(E,gcoeff(a,k,i)); gcoeff(a,k,i) = g0;
+      if (ff->equal0(m)) continue;
+
+      m = ff->red(E,ff->neg(E,ff->mul(E,m, invpiv)));
+      for (j=i+1; j<=aco; j++) _gen_addmul(gel(a,j),k,i,m,E,ff);
+      for (j=1  ; j<=bco; j++) _gen_addmul(gel(b,j),k,i,m,E,ff);
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_Gauss. i=%ld",i);
+      gerepileall(av,2, &a,&b);
+    }
+  }
+
+  if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
+  u = cgetg(bco+1,t_MAT);
+  for (j=1; j<=bco; j++) gel(u,j) = _gen_get_col(a, gel(b,j), aco, E, ff);
+  return u;
+}
+
+/* compatible t_MAT * t_COL, lgA = lg(A) = lg(B) > 1, l = lgcols(A) */
+static GEN
+gen_matcolmul_i(GEN A, GEN B, ulong lgA, ulong l,
+                void *E, const struct bb_field *ff)
+{
+  GEN C = cgetg(l, t_COL);
+  ulong i;
+  for (i = 1; i < l; i++) {
+    pari_sp av = avma;
+    GEN e = ff->mul(E, gcoeff(A, i, 1), gel(B, 1));
+    ulong k;
+    for(k = 2; k < lgA; k++)
+      e = ff->add(E, e, ff->mul(E, gcoeff(A, i, k), gel(B, k)));
+    gel(C, i) = gerepileupto(av, ff->red(E, e));
+  }
+  return C;
+}
+
+GEN
+gen_matcolmul(GEN A, GEN B, void *E, const struct bb_field *ff)
+{
+  ulong lgA = lg(A);
+  if (lgA != lg(B))
+    pari_err_OP("operation 'gen_matcolmul'", A, B);
+  if (lgA == 1)
+    return cgetg(1, t_COL);
+  return gen_matcolmul_i(A, B, lgA, lgcols(A), E, ff);
+}
+
+GEN
+gen_matmul(GEN A, GEN B, void *E, const struct bb_field *ff)
+{
+  ulong j, l, lgA, lgB = lg(B);
+  GEN C;
+  if (lgB == 1)
+    return cgetg(1, t_MAT);
+  lgA = lg(A);
+  if (lgA != (ulong)lgcols(B))
+    pari_err_OP("operation 'gen_matmul'", A, B);
+  if (lgA == 1)
+    return zeromat(0, lgB - 1);
+  l = lgcols(A);
+  C = cgetg(lgB, t_MAT);
+  for(j = 1; j < lgB; j++)
+    gel(C, j) = gen_matcolmul_i(A, gel(B, j), lgA, l, E, ff);
+  return C;
+}
+
+static GEN
+image_from_pivot(GEN x, GEN d, long r)
+{
+  GEN y;
+  long j, k;
+
+  if (!d) return gcopy(x);
+  /* d left on stack for efficiency */
+  r = lg(x)-1 - r; /* = dim Im(x) */
+  y = cgetg(r+1,t_MAT);
+  for (j=k=1; j<=r; k++)
+    if (d[k]) gel(y,j++) = gcopy(gel(x,k));
+  return y;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    LINEAR ALGEBRA MODULO P                      */
+/*                                                                 */
+/*******************************************************************/
+
+static long
+F2v_find_nonzero(GEN x0, GEN mask0, long l, long m)
+{
+  ulong *x = (ulong *)x0+2, *mask = (ulong *)mask0+2, e;
+  long i, j;
+  for (i = 0; i < l; i++)
+  {
+    e = *x++ & *mask++;
+    if (e)
+      for (j = 1; ; j++, e >>= 1) if (e & 1uL) return i*BITS_IN_LONG+j;
+  }
+  return m+1;
+}
+
+/* in place, destroy x */
+GEN
+F2m_ker_sp(GEN x, long deplin)
+{
+  GEN y, c, d;
+  long i, j, k, l, r, m, n;
+
+  n = lg(x)-1;
+  m = mael(x,1,1); r=0;
+
+  d = cgetg(n+1, t_VECSMALL);
+  c = zero_F2v(m);
+  l = lg(c)-1;
+  for (i = 2; i <= l; i++) c[i] = -1;
+  if (remsBIL(m)) c[l] = (1uL<<remsBIL(m))-1uL;
+  for (k=1; k<=n; k++)
+  {
+    GEN xk = gel(x,k);
+    j = F2v_find_nonzero(xk, c, l, m);
+    if (j>m)
+    {
+      if (deplin) {
+        GEN c = zero_F2v(n);
+        for (i=1; i<k; i++)
+          if (F2v_coeff(xk, d[i]))
+            F2v_set(c, i);
+        F2v_set(c, k);
+        return c;
+      }
+      r++; d[k] = 0;
+    }
+    else
+    {
+      F2v_clear(c,j); d[k] = j;
+      F2v_clear(xk, j);
+      for (i=k+1; i<=n; i++)
+      {
+        GEN xi = gel(x,i);
+        if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
+      }
+      F2v_set(xk, j);
+    }
+  }
+  if (deplin) return NULL;
+
+  y = zero_F2m_copy(n,r);
+  for (j=k=1; j<=r; j++,k++)
+  {
+    GEN C = gel(y,j); while (d[k]) k++;
+    for (i=1; i<k; i++)
+      if (d[i] && F2m_coeff(x,d[i],k))
+        F2v_set(C,i);
+    F2v_set(C, k);
+  }
+  return y;
+}
+
+static void /* assume m < p */
+_Fl_submul(GEN b, long k, long i, ulong m, ulong p)
+{
+  b[k] = Fl_sub(b[k], Fl_mul(m, b[i], p), p);
+}
+static void /* same m = 1 */
+_Fl_sub(GEN b, long k, long i, ulong p)
+{
+  b[k] = Fl_sub(b[k], b[i], p);
+}
+static void /* assume m < p && SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
+_Fl_addmul_OK(GEN b, long k, long i, ulong m, ulong p)
+{
+  uel(b,k) += m * uel(b,i);
+  if (b[k] & HIGHMASK) uel(b,k) %= p;
+}
+static void /* assume SMALL_ULONG(p) && (! (b[i] & b[k] & HIGHMASK)) */
+_Fl_add_OK(GEN b, long k, long i, ulong p)
+{
+  uel(b,k) += uel(b,i);
+  if (b[k] & HIGHMASK) uel(b,k) %= p;
+}
+static void /* assume m < p */
+_Fl_addmul(GEN b, long k, long i, ulong m, ulong p)
+{
+  uel(b,i) %= p;
+  b[k] = Fl_add(b[k], Fl_mul(m, b[i], p), p);
+}
+static void /* same m = 1 */
+_Fl_add(GEN b, long k, long i, ulong p)
+{
+  b[k] = Fl_add(b[k], b[i], p);
+}
+
+/* in place, destroy x */
+GEN
+Flm_ker_sp(GEN x, ulong p, long deplin)
+{
+  GEN y, c, d;
+  long i, j, k, r, t, m, n;
+  ulong a;
+  const int OK_ulong = SMALL_ULONG(p);
+
+  n = lg(x)-1;
+  m=nbrows(x); r=0;
+
+  c = zero_zv(m);
+  d = new_chunk(n+1);
+  a = 0; /* for gcc -Wall */
+  for (k=1; k<=n; k++)
+  {
+    for (j=1; j<=m; j++)
+      if (!c[j])
+      {
+        a = ucoeff(x,j,k) % p;
+        if (a) break;
+      }
+    if (j > m)
+    {
+      if (deplin) {
+        c = cgetg(n+1, t_VECSMALL);
+        for (i=1; i<k; i++) c[i] = ucoeff(x,d[i],k) % p;
+        c[k] = 1; for (i=k+1; i<=n; i++) c[i] = 0;
+        return c;
+      }
+      r++; d[k] = 0;
+    }
+    else
+    {
+      ulong piv = p - Fl_inv(a, p); /* -1/a */
+      c[j] = k; d[k] = j;
+      ucoeff(x,j,k) = p-1;
+      if (piv == 1) { /* nothing */ }
+      else if (OK_ulong)
+        for (i=k+1; i<=n; i++) ucoeff(x,j,i) = (piv * ucoeff(x,j,i)) % p;
+      else
+        for (i=k+1; i<=n; i++) ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);
+      for (t=1; t<=m; t++)
+      {
+        if (t == j) continue;
+
+        piv = ( ucoeff(x,t,k) %= p );
+        if (!piv) continue;
+
+        if (OK_ulong)
+        {
+          if (piv == 1)
+            for (i=k+1; i<=n; i++) _Fl_add_OK(gel(x,i),t,j, p);
+          else
+            for (i=k+1; i<=n; i++) _Fl_addmul_OK(gel(x,i),t,j,piv, p);
+        } else {
+          if (piv == 1)
+            for (i=k+1; i<=n; i++) _Fl_add(gel(x,i),t,j,p);
+          else
+            for (i=k+1; i<=n; i++) _Fl_addmul(gel(x,i),t,j,piv,p);
+        }
+      }
+    }
+  }
+  if (deplin) return NULL;
+
+  y = cgetg(r+1, t_MAT);
+  for (j=k=1; j<=r; j++,k++)
+  {
+    GEN C = cgetg(n+1, t_VECSMALL);
+
+    gel(y,j) = C; while (d[k]) k++;
+    for (i=1; i<k; i++)
+      if (d[i])
+        C[i] = ucoeff(x,d[i],k) % p;
+      else
+        C[i] = 0;
+    C[k] = 1; for (i=k+1; i<=n; i++) C[i] = 0;
+  }
+  return y;
+}
+
+GEN
+FpM_intersect(GEN x, GEN y, GEN p)
+{
+  pari_sp av = avma;
+  long j, lx = lg(x);
+  GEN z;
+
+  if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
+  z = FpM_ker(shallowconcat(x,y), p);
+  for (j=lg(z)-1; j; j--) setlg(z[j],lx);
+  return gerepileupto(av, FpM_mul(x,z,p));
+}
+
+/* not memory clean */
+GEN
+F2m_ker(GEN x) { return F2m_ker_sp(F2m_copy(x), 0); }
+GEN
+F2m_deplin(GEN x) { return F2m_ker_sp(F2m_copy(x), 1); }
+GEN
+Flm_ker(GEN x, ulong p) { return Flm_ker_sp(Flm_copy(x), p, 0); }
+GEN
+Flm_deplin(GEN x, ulong p) { return Flm_ker_sp(Flm_copy(x), p, 1); }
+
+ulong
+F2m_det_sp(GEN x) { return !F2m_ker_sp(x, 1); }
+
+ulong
+F2m_det(GEN x)
+{
+  pari_sp av = avma;
+  ulong d = F2m_det_sp(F2m_copy(x));
+  avma = av; return d;
+}
+
+/* in place, destroy a, SMALL_ULONG(p) is TRUE */
+static ulong
+Flm_det_sp_OK(GEN a, long nbco, ulong p)
+{
+  long i,j,k, s = 1;
+  ulong q, x = 1;
+
+  for (i=1; i<nbco; i++)
+  {
+    for(k=i; k<=nbco; k++)
+    {
+      ulong c = ucoeff(a,k,i) % p;
+      ucoeff(a,k,i) = c;
+      if (c) break;
+    }
+    for(j=k+1; j<=nbco; j++) ucoeff(a,j,i) %= p;
+    if (k > nbco) return ucoeff(a,i,i);
+    if (k != i)
+    { /* exchange the lines s.t. k = i */
+      for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
+      s = -s;
+    }
+    q = ucoeff(a,i,i);
+
+    if (x & HIGHMASK) x %= p;
+    x *= q;
+    q = Fl_inv(q,p);
+    for (k=i+1; k<=nbco; k++)
+    {
+      ulong m = ucoeff(a,i,k) % p;
+      if (!m) continue;
+
+      m = p - ((m*q)%p);
+      for (j=i+1; j<=nbco; j++)
+      {
+        ulong c = ucoeff(a,j,k);
+        if (c & HIGHMASK) c %= p;
+        ucoeff(a,j,k) = c  + m*ucoeff(a,j,i);
+      }
+    }
+  }
+  if (x & HIGHMASK) x %= p;
+  q = ucoeff(a,nbco,nbco);
+  if (q & HIGHMASK) q %= p;
+  x = (x*q) % p;
+  if (s < 0 && x) x = p - x;
+  return x;
+}
+/* in place, destroy a */
+ulong
+Flm_det_sp(GEN a, ulong p)
+{
+  long i,j,k, s = 1, nbco = lg(a)-1;
+  ulong q, x = 1;
+
+  if (SMALL_ULONG(p)) return Flm_det_sp_OK(a, nbco, p);
+  for (i=1; i<nbco; i++)
+  {
+    for(k=i; k<=nbco; k++)
+      if (ucoeff(a,k,i)) break;
+    if (k > nbco) return ucoeff(a,i,i);
+    if (k != i)
+    { /* exchange the lines s.t. k = i */
+      for (j=i; j<=nbco; j++) lswap(ucoeff(a,i,j), ucoeff(a,k,j));
+      s = -s;
+    }
+    q = ucoeff(a,i,i);
+
+    x = Fl_mul(x,q,p);
+    q = Fl_inv(q,p);
+    for (k=i+1; k<=nbco; k++)
+    {
+      ulong m = ucoeff(a,i,k);
+      if (!m) continue;
+
+      m = Fl_mul(m, q, p);
+      for (j=i+1; j<=nbco; j++)
+        ucoeff(a,j,k) = Fl_sub(ucoeff(a,j,k), Fl_mul(m,ucoeff(a,j,i), p), p);
+    }
+  }
+  if (s < 0) x = Fl_neg(x, p);
+  return Fl_mul(x, ucoeff(a,nbco,nbco), p);
+}
+
+ulong
+Flm_det(GEN x, ulong p)
+{
+  pari_sp av = avma;
+  ulong d = Flm_det_sp(Flm_copy(x), p);
+  avma = av; return d;
+}
+
+static GEN
+FpM_init(GEN a, GEN p, ulong *pp)
+{
+  if (lgefint(p) == 3)
+  {
+    *pp = (ulong)p[2];
+    return (*pp==2)? ZM_to_F2m(a): ZM_to_Flm(a, *pp);
+  }
+  *pp = 0; return a;
+}
+GEN
+RgM_Fp_init(GEN a, GEN p, ulong *pp)
+{
+  if (lgefint(p) == 3)
+  {
+    *pp = (ulong)p[2];
+    return (*pp==2)? RgM_to_F2m(a): RgM_to_Flm(a, *pp);
+  }
+  *pp = 0; return RgM_to_FpM(a,p);
+}
+
+static GEN
+FpM_det_gen(GEN a, GEN p)
+{
+  void *E;
+  const struct bb_field *S = get_Fp_field(&E,p);
+  return gen_det(a, E, S);
+}
+GEN
+FpM_det(GEN a, GEN p)
+{
+  pari_sp av = avma;
+  ulong pp, d;
+  a = FpM_init(a, p, &pp);
+  switch(pp)
+  {
+  case 0: return FpM_det_gen(a, p);
+  case 2: d = F2m_det_sp(a); break;
+  default:d = Flm_det_sp(a,pp); break;
+  }
+  avma = av; return utoi(d);
+}
+
+/* Destroy x */
+static GEN
+F2m_gauss_pivot(GEN x, long *rr)
+{
+  GEN c, d;
+  long i, j, k, l, r, m, n;
+
+  n = lg(x)-1; if (!n) { *rr=0; return NULL; }
+  m = mael(x,1,1); r=0;
+
+  d = cgetg(n+1, t_VECSMALL);
+  c = zero_F2v(m);
+  l = lg(c)-1;
+  for (i = 2; i <= l; i++) c[i] = -1;
+  if (remsBIL(m)) c[l] = (1uL<<remsBIL(m))-1uL;
+  for (k=1; k<=n; k++)
+  {
+    GEN xk = gel(x,k);
+    j = F2v_find_nonzero(xk, c, l, m);
+    if (j>m) { r++; d[k] = 0; }
+    else
+    {
+      F2v_clear(c,j); d[k] = j;
+      for (i=k+1; i<=n; i++)
+      {
+        GEN xi = gel(x,i);
+        if (F2v_coeff(xi,j)) F2v_add_inplace(xi, xk);
+      }
+    }
+  }
+
+  *rr = r; avma = (pari_sp)d; return d;
+}
+
+/* Destroy x */
+static GEN
+Flm_gauss_pivot(GEN x, ulong p, long *rr)
+{
+  GEN c,d;
+  long i,j,k,r,t,n,m;
+
+  n=lg(x)-1; if (!n) { *rr=0; return NULL; }
+
+  m=nbrows(x); r=0;
+  d=cgetg(n+1,t_VECSMALL);
+  c = zero_zv(m);
+  for (k=1; k<=n; k++)
+  {
+    for (j=1; j<=m; j++)
+      if (!c[j])
+      {
+        ucoeff(x,j,k) %= p;
+        if (ucoeff(x,j,k)) break;
+      }
+    if (j>m) { r++; d[k]=0; }
+    else
+    {
+      ulong piv = p - Fl_inv(ucoeff(x,j,k), p);
+      c[j]=k; d[k]=j;
+      for (i=k+1; i<=n; i++)
+        ucoeff(x,j,i) = Fl_mul(piv, ucoeff(x,j,i), p);
+      for (t=1; t<=m; t++)
+        if (!c[t]) /* no pivot on that line yet */
+        {
+          piv = ucoeff(x,t,k);
+          if (piv)
+          {
+            ucoeff(x,t,k) = 0;
+            for (i=k+1; i<=n; i++)
+              ucoeff(x,t,i) = Fl_add(ucoeff(x,t,i),
+                                     Fl_mul(piv,ucoeff(x,j,i),p),p);
+          }
+        }
+      for (i=k; i<=n; i++) ucoeff(x,j,i) = 0; /* dummy */
+    }
+  }
+  *rr = r; avma = (pari_sp)d; return d;
+}
+
+static GEN
+FpM_gauss_pivot_gen(GEN x, GEN p, long *rr)
+{
+  void *E;
+  const struct bb_field *S = get_Fp_field(&E,p);
+  return gen_Gauss_pivot(x, rr, E, S);
+}
+static GEN
+FpM_gauss_pivot(GEN x, GEN p, long *rr)
+{
+  ulong pp;
+  if (lg(x)==1) { *rr = 0; return NULL; }
+  x = FpM_init(x, p, &pp);
+  switch(pp)
+  {
+  case 0: return FpM_gauss_pivot_gen(x, p, rr);
+  case 2: return F2m_gauss_pivot(x, rr);
+  default:return Flm_gauss_pivot(x, pp, rr);
+  }
+}
+
+GEN
+FpM_image(GEN x, GEN p)
+{
+  long r;
+  GEN d = FpM_gauss_pivot(x,p,&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+GEN
+Flm_image(GEN x, ulong p)
+{
+  long r;
+  GEN d = Flm_gauss_pivot(Flm_copy(x),p,&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+GEN
+F2m_image(GEN x)
+{
+  long r;
+  GEN d = F2m_gauss_pivot(F2m_copy(x),&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+
+long
+FpM_rank(GEN x, GEN p)
+{
+  pari_sp av = avma;
+  long r;
+  (void)FpM_gauss_pivot(x,p,&r);
+  avma = av; return lg(x)-1 - r;
+}
+long
+Flm_rank(GEN x, ulong p)
+{
+  pari_sp av = avma;
+  long r;
+  (void)Flm_gauss_pivot(Flm_copy(x),p,&r);
+  avma = av; return lg(x)-1 - r;
+}
+long
+F2m_rank(GEN x)
+{
+  pari_sp av = avma;
+  long r;
+  (void)F2m_gauss_pivot(F2m_copy(x),&r);
+  avma = av; return lg(x)-1 - r;
+}
+
+
+static GEN
+FlxqM_gauss_pivot(GEN x, GEN T, ulong p, long *rr)
+{
+  void *E;
+  const struct bb_field *S = get_Flxq_field(&E, T, p);
+  return gen_Gauss_pivot(x, rr, E, S);
+}
+
+GEN
+FlxqM_image(GEN x, GEN T, ulong p)
+{
+  long r;
+  GEN d = FlxqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+
+long
+FlxqM_rank(GEN x, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long r;
+  (void)FlxqM_gauss_pivot(x,T,p,&r);
+  avma = av; return lg(x)-1 - r;
+}
+GEN
+FlxqM_det(GEN a, GEN T, ulong p)
+{
+  void *E;
+  const struct bb_field *S = get_Flxq_field(&E, T, p);
+  return gen_det(a, E, S);
+}
+
+GEN
+FlxqM_FlxqC_mul(GEN A, GEN B, GEN T, unsigned long p) {
+  void *E;
+  const struct bb_field *ff = get_Flxq_field(&E, T, p);
+  return gen_matcolmul(A, B, E, ff);
+}
+
+GEN
+FlxqM_mul(GEN A, GEN B, GEN T, unsigned long p) {
+  void *E;
+  const struct bb_field *ff = get_Flxq_field(&E, T, p);
+  return gen_matmul(A, B, E, ff);
+}
+
+GEN
+F2xqM_det(GEN a, GEN T)
+{
+  void *E;
+  const struct bb_field *S = get_F2xq_field(&E, T);
+  return gen_det(a, E, S);
+}
+
+static GEN
+F2xqM_gauss_gen(GEN a, GEN b, GEN T)
+{
+  void *E;
+  const struct bb_field *S = get_F2xq_field(&E, T);
+  return gen_Gauss(a, b, E, S);
+}
+GEN
+F2xqM_inv(GEN a, GEN T)
+{
+  pari_sp av = avma;
+  long n = lg(a)-1;
+  GEN u;
+  if (!n) { avma = av; return cgetg(1, t_MAT); }
+  u = F2xqM_gauss_gen(a, matid_F2xqM(n,T), T);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, u);
+}
+
+GEN
+F2xqM_F2xqC_mul(GEN A, GEN B, GEN T) {
+  void *E;
+  const struct bb_field *ff = get_F2xq_field(&E, T);
+  return gen_matcolmul(A, B, E, ff);
+}
+
+GEN
+F2xqM_mul(GEN A, GEN B, GEN T) {
+  void *E;
+  const struct bb_field *ff = get_F2xq_field(&E, T);
+  return gen_matmul(A, B, E, ff);
+}
+
+static GEN
+FqM_gauss_pivot_gen(GEN x, GEN T, GEN p, long *rr)
+{
+  void *E;
+  const struct bb_field *S = get_Fq_field(&E,T,p);
+  return gen_Gauss_pivot(x, rr, E, S);
+}
+static GEN
+FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr)
+{
+  if (lg(x)==1) { *rr = 0; return NULL; }
+  if (!T) return FpM_gauss_pivot(x, p, rr);
+  if (lgefint(p) == 3)
+  {
+    pari_sp av = avma;
+    ulong pp = (ulong)p[2];
+    GEN Tp = ZXT_to_FlxT(T, pp);
+    GEN d = FlxqM_gauss_pivot(FqM_to_FlxM(x, T, p), Tp, pp, rr);
+    return d ? gerepileuptoleaf(av, d): d;
+  }
+  return FqM_gauss_pivot_gen(x, T, p, rr);
+}
+
+GEN
+FqM_image(GEN x, GEN T, GEN p)
+{
+  long r;
+  GEN d = FqM_gauss_pivot(x,T,p,&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+
+long
+FqM_rank(GEN x, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  long r;
+  (void)FqM_gauss_pivot(x,T,p,&r);
+  avma = av; return lg(x)-1 - r;
+}
+
+GEN
+FqM_det(GEN x, GEN T, GEN p)
+{
+  void *E;
+  const struct bb_field *S = get_Fq_field(&E,T,p);
+  return gen_det(x, E, S);
+}
+
+GEN
+FqM_FqC_mul(GEN A, GEN B, GEN T, GEN p) {
+  void *E;
+  const struct bb_field *ff = get_Fq_field(&E, T, p);
+  return gen_matcolmul(A, B, E, ff);
+}
+
+GEN
+FqM_mul(GEN A, GEN B, GEN T, GEN p) {
+  void *E;
+  const struct bb_field *ff = get_Fq_field(&E, T, p);
+  return gen_matmul(A, B, E, ff);
+}
+
+static GEN
+FpM_ker_gen(GEN x, GEN p, long deplin)
+{
+  void *E;
+  const struct bb_field *S = get_Fp_field(&E,p);
+  return gen_ker(x, deplin, E, S);
+}
+static GEN
+FpM_ker_i(GEN x, GEN p, long deplin)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN y;
+
+  if (lg(x)==1) return cgetg(1,t_MAT);
+  x = FpM_init(x, p, &pp);
+  switch(pp)
+  {
+  case 0: return FpM_ker_gen(x,p,deplin);
+  case 2:
+    y = F2m_ker_sp(x, deplin);
+    if (!y) return y;
+    y = deplin? F2c_to_ZC(y): F2m_to_ZM(y);
+    return gerepileupto(av, y);
+  default:
+    y = Flm_ker_sp(x, pp, deplin);
+    if (!y) return y;
+    y = deplin? Flc_to_ZC(y): Flm_to_ZM(y);
+    return gerepileupto(av, y);
+  }
+}
+
+GEN
+FpM_ker(GEN x, GEN p) { return FpM_ker_i(x,p,0); }
+
+GEN
+FpM_deplin(GEN x, GEN p) { return FpM_ker_i(x,p,1); }
+
+static GEN
+FqM_ker_gen(GEN x, GEN T, GEN p, long deplin)
+{
+  void *E;
+  const struct bb_field *S = get_Fq_field(&E,T,p);
+  return gen_ker(x,deplin,E,S);
+}
+static GEN
+FqM_ker_i(GEN x, GEN T, GEN p, long deplin)
+{
+  if (!T) return FpM_ker_i(x,p,deplin);
+  if (lg(x)==1) return cgetg(1,t_MAT);
+
+  if (lgefint(p)==3)
+  {
+    pari_sp ltop=avma;
+    ulong l= p[2];
+    GEN Ml = FqM_to_FlxM(x, T, p);
+    GEN Tl = ZXT_to_FlxT(T,l);
+    GEN p1 = FlxM_to_ZXM(FlxqM_ker(Ml,Tl,l));
+    return gerepileupto(ltop,p1);
+  }
+  return FqM_ker_gen(x, T, p, deplin);
+}
+
+GEN
+FqM_ker(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,0); }
+
+GEN
+FqM_deplin(GEN x, GEN T, GEN p) { return FqM_ker_i(x,T,p,1); }
+
+static GEN
+FlxqM_ker_i(GEN x, GEN T, ulong p, long deplin)
+{
+  const struct bb_field *ff;
+  void *E;
+
+  if (lg(x)==1) return cgetg(1,t_MAT);
+  ff=get_Flxq_field(&E,T,p);
+  return gen_ker(x,deplin, E, ff);
+}
+
+GEN
+FlxqM_ker(GEN x, GEN T, ulong p)
+{
+  return FlxqM_ker_i(x, T, p, 0);
+}
+
+static GEN
+F2xqM_ker_i(GEN x, GEN T, long deplin)
+{
+  const struct bb_field *ff;
+  void *E;
+
+  if (lg(x)==1) return cgetg(1,t_MAT);
+  ff = get_F2xq_field(&E,T);
+  return gen_ker(x,deplin, E, ff);
+}
+
+GEN
+F2xqM_ker(GEN x, GEN T)
+{
+  return F2xqM_ker_i(x, T, 0);
+}
+static GEN
+F2xqM_gauss_pivot(GEN x, GEN T, long *rr)
+{
+  void *E;
+  const struct bb_field *S = get_F2xq_field(&E,T);
+  return gen_Gauss_pivot(x, rr, E, S);
+}
+GEN
+F2xqM_image(GEN x, GEN T)
+{
+  long r;
+  GEN d = F2xqM_gauss_pivot(x,T,&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+long
+F2xqM_rank(GEN x, GEN T)
+{
+  pari_sp av = avma;
+  long r;
+  (void)F2xqM_gauss_pivot(x,T,&r);
+  avma = av; return lg(x)-1 - r;
+}
+/*******************************************************************/
+/*                                                                 */
+/*                       Solve A*X=B (Gauss pivot)                 */
+/*                                                                 */
+/*******************************************************************/
+/* x ~ 0 compared to reference y */
+int
+approx_0(GEN x, GEN y)
+{
+  long tx = typ(x);
+  if (tx == t_COMPLEX)
+    return approx_0(gel(x,1), y) && approx_0(gel(x,2), y);
+  return gequal0(x) ||
+         (tx == t_REAL && gexpo(y) - gexpo(x) > bit_prec(x));
+}
+/* x a column, x0 same column in the original input matrix (for reference),
+ * c list of pivots so far */
+static long
+gauss_get_pivot_max(GEN X, GEN X0, long ix, GEN c)
+{
+  GEN p, r, x = gel(X,ix), x0 = gel(X0,ix);
+  long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
+  if (c)
+  {
+    for (i=1; i<lx; i++)
+      if (!c[i])
+      {
+        long e = gexpo(gel(x,i));
+        if (e > ex) { ex = e; k = i; }
+      }
+  }
+  else
+  {
+    for (i=ix; i<lx; i++)
+    {
+      long e = gexpo(gel(x,i));
+      if (e > ex) { ex = e; k = i; }
+    }
+  }
+  if (!k) return lx;
+  p = gel(x,k);
+  r = gel(x0,k); if (isrationalzero(r)) r = x0;
+  return approx_0(p, r)? lx: k;
+}
+static long
+gauss_get_pivot_padic(GEN X, GEN p, long ix, GEN c)
+{
+  GEN x = gel(X, ix);
+  long i, k = 0, ex = (long)HIGHVALPBIT, lx = lg(x);
+  if (c)
+  {
+    for (i=1; i<lx; i++)
+      if (!c[i] && !gequal0(gel(x,i)))
+      {
+        long e = gvaluation(gel(x,i), p);
+        if (e < ex) { ex = e; k = i; }
+      }
+  }
+  else
+  {
+    for (i=ix; i<lx; i++)
+      if (!gequal0(gel(x,i)))
+      {
+        long e = gvaluation(gel(x,i), p);
+        if (e < ex) { ex = e; k = i; }
+      }
+  }
+  return k? k: lx;
+}
+static long
+gauss_get_pivot_NZ(GEN X, GEN x0/*unused*/, long ix, GEN c)
+{
+  GEN x = gel(X, ix);
+  long i, lx = lg(x);
+  (void)x0;
+  if (c)
+  {
+    for (i=1; i<lx; i++)
+      if (!c[i] && !gequal0(gel(x,i))) return i;
+  }
+  else
+  {
+    for (i=ix; i<lx; i++)
+      if (!gequal0(gel(x,i))) return i;
+  }
+  return lx;
+}
+
+/* Return pivot seeking function appropriate for the domain of the RgM x
+ * (first non zero pivot, maximal pivot...)
+ * x0 is a reference point used when guessing whether x[i,j] ~ 0
+ * (iff x[i,j] << x0[i,j]); typical case: mateigen, Gauss pivot on x - vp.Id,
+ * but use original x when deciding whether a prospective pivot is non-0 */
+static pivot_fun
+get_pivot_fun(GEN x, GEN x0, GEN *data)
+{
+  long i, j, hx, lx = lg(x);
+  int res = t_INT;
+  GEN p = NULL;
+
+  *data = NULL;
+  if (lx == 1) return &gauss_get_pivot_NZ;
+  hx = lgcols(x);
+  for (j=1; j<lx; j++)
+  {
+    GEN xj = gel(x,j);
+    for (i=1; i<hx; i++)
+    {
+      GEN c = gel(xj,i);
+      switch(typ(c))
+      {
+        case t_REAL:
+          res = t_REAL;
+          break;
+        case t_COMPLEX:
+          if (typ(gel(c,1)) == t_REAL || typ(gel(c,2)) == t_REAL) res = t_REAL;
+          break;
+        case t_INT: case t_INTMOD: case t_FRAC: case t_FFELT: case t_QUAD:
+        case t_POLMOD: /* exact types */
+          break;
+        case t_PADIC:
+          p = gel(c,2);
+          res = t_PADIC;
+          break;
+        default: return &gauss_get_pivot_NZ;
+      }
+    }
+  }
+  switch(res)
+  {
+    case t_REAL: *data = x0; return &gauss_get_pivot_max;
+    case t_PADIC: *data = p; return &gauss_get_pivot_padic;
+    default: return &gauss_get_pivot_NZ;
+  }
+}
+
+static GEN
+get_col(GEN a, GEN b, GEN p, long li)
+{
+  GEN u = cgetg(li+1,t_COL);
+  long i, j;
+
+  gel(u,li) = gdiv(gel(b,li), p);
+  for (i=li-1; i>0; i--)
+  {
+    pari_sp av = avma;
+    GEN m = gel(b,i);
+    for (j=i+1; j<=li; j++) m = gsub(m, gmul(gcoeff(a,i,j), gel(u,j)));
+    gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(a,i,i)));
+  }
+  return u;
+}
+/* assume 0 <= a[i,j] < p */
+static GEN
+Fl_get_col_OK(GEN a, GEN b, long li, ulong p)
+{
+  GEN u = cgetg(li+1,t_VECSMALL);
+  ulong m = uel(b,li) % p;
+  long i,j;
+
+  uel(u,li) = (m * ucoeff(a,li,li)) % p;
+  for (i = li-1; i > 0; i--)
+  {
+    m = p - uel(b,i)%p;
+    for (j = i+1; j <= li; j++) {
+      if (m & HIGHBIT) m %= p;
+      m += ucoeff(a,i,j) * uel(u,j); /* 0 <= u[j] < p */
+    }
+    m %= p;
+    if (m) m = ((p-m) * ucoeff(a,i,i)) % p;
+    u[i] = m;
+  }
+  return u;
+}
+static GEN
+Fl_get_col(GEN a, GEN b, long li, ulong p)
+{
+  GEN u = cgetg(li+1,t_VECSMALL);
+  ulong m = b[li] % p;
+  long i,j;
+
+  u[li] = Fl_mul(m, ucoeff(a,li,li), p);
+  for (i=li-1; i>0; i--)
+  {
+    m = b[i]%p;
+    for (j = i+1; j <= li; j++)
+      m = Fl_sub(m, Fl_mul(ucoeff(a,i,j), u[j], p), p);
+    if (m) m = Fl_mul(m, ucoeff(a,i,i), p);
+    u[i] = m;
+  }
+  return u;
+}
+
+/* bk -= m * bi */
+static void
+_submul(GEN b, long k, long i, GEN m)
+{
+  gel(b,k) = gsub(gel(b,k), gmul(m, gel(b,i)));
+}
+static int
+init_gauss(GEN a, GEN *b, long *aco, long *li, int *iscol)
+{
+  *iscol = *b ? (typ(*b) == t_COL): 0;
+  *aco = lg(a) - 1;
+  if (!*aco) /* a empty */
+  {
+    if (*b && lg(*b) != 1) pari_err_DIM("gauss");
+    *li = 0; return 0;
+  }
+  *li = nbrows(a);
+  if (*li < *aco) pari_err_INV("gauss [no left inverse]", a);
+  if (*b)
+  {
+    if (*li != *aco) pari_err_DIM("gauss");
+    switch(typ(*b))
+    {
+      case t_MAT:
+        if (lg(*b) == 1) return 0;
+        *b = RgM_shallowcopy(*b);
+        break;
+      case t_COL:
+        *b = mkmat( leafcopy(*b) );
+        break;
+      default: pari_err_TYPE("gauss",*b);
+    }
+    if (nbrows(*b) != *li) pari_err_DIM("gauss");
+  }
+  else
+    *b = matid(*li);
+  return 1;
+}
+
+static int
+is_modular_solve(GEN a, GEN b, GEN *u)
+{
+  GEN p = NULL;
+  ulong pp;
+  if (!RgM_is_FpM(a, &p) || !p) return 0;
+  if (!b)
+  {
+    a = RgM_Fp_init(a, p, &pp);
+    switch(pp)
+    {
+    case 0:
+      a = FpM_inv(a,p);
+      if (a) a = FpM_to_mod(a, p);
+      break;
+    case 2:
+      a = F2m_inv(a);
+      if (a) a = F2m_to_mod(a);
+      break;
+    default:
+      a = Flm_inv(a,pp);
+      if (a) a = Flm_to_mod(a, pp);
+    }
+  }
+  else switch(typ(b))
+  {
+    case t_COL:
+      if (!RgV_is_FpV(b, &p)) return 0;
+      a = RgM_Fp_init(a, p, &pp);
+      switch(pp)
+      {
+      case 0:
+        b = RgC_to_FpC(b, p);
+        a = FpM_FpC_gauss(a,b,p);
+        if (a) a = FpC_to_mod(a, p);
+        break;
+      case 2:
+        b = RgV_to_F2v(b);
+        a = F2m_F2c_gauss(a,b);
+        if (a) a = F2c_to_mod(a);
+        break;
+      default:
+        b = RgC_to_Flc(b, pp);
+        a = Flm_Flc_gauss(a,b,pp);
+        if (a) a = Flc_to_mod(a, pp);
+        break;
+      }
+      break;
+    case t_MAT:
+      if (!RgM_is_FpM(b, &p)) return 0;
+      a = RgM_Fp_init(a, p, &pp);
+      switch(pp)
+      {
+      case 0:
+        b = RgM_to_FpM(b, p);
+        a = FpM_gauss(a,b,p);
+        if (a) a = FpM_to_mod(a, p);
+        break;
+      case 2:
+        b = RgM_to_F2m(b);
+        a = F2m_gauss(a,b);
+        if (a) a = F2m_to_mod(a);
+        break;
+      default:
+        b = RgM_to_Flm(b, pp);
+        a = Flm_gauss(a,b,pp);
+        if (a) a = Flm_to_mod(a, pp);
+        break;
+      }
+      break;
+    default: return 0;
+  }
+  *u = a; return 1;
+}
+/* Gaussan Elimination. If a is square, return a^(-1)*b;
+ * if a has more rows than columns and b is NULL, return c such that c a = Id.
+ * a is a (not necessarily square) matrix
+ * b is a matrix or column vector, NULL meaning: take the identity matrix,
+ *   effectively returning the inverse of a
+ * If a and b are empty, the result is the empty matrix.
+ *
+ * li: number of rows of a and b
+ * aco: number of columns of a
+ * bco: number of columns of b (if matrix)
+ */
+GEN
+RgM_solve(GEN a, GEN b)
+{
+  pari_sp av = avma, lim = stack_lim(av,1);
+  long i, j, k, li, bco, aco;
+  int iscol;
+  pivot_fun pivot;
+  GEN p, u, data;
+
+  if (is_modular_solve(a,b,&u)) return gerepileupto(av, u);
+  avma = av;
+
+  if (lg(a)-1 == 2 && nbrows(a) == 2) {
+    /* 2x2 matrix, start by inverting a */
+    GEN detinv = ginv(det (a));
+    GEN ainv = cgetg(3, t_MAT);
+    for (j = 1; j <= 2; j++)
+      gel (ainv, j) = cgetg (3, t_COL);
+    gcoeff(ainv, 1, 1) = gcoeff(a, 2, 2);
+    gcoeff(ainv, 2, 2) = gcoeff(a, 1, 1);
+    gcoeff(ainv, 1, 2) = gneg(gcoeff (a, 1, 2));
+    gcoeff(ainv, 2, 1) = gneg(gcoeff (a, 2, 1));
+    ainv = gmul(ainv, detinv);
+    if (b != NULL)
+      ainv = gmul(ainv, b);
+    return gerepileupto(av, ainv);
+  }
+
+  if (!init_gauss(a, &b, &aco, &li, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
+  pivot = get_pivot_fun(a, a, &data);
+  a = RgM_shallowcopy(a);
+  bco = lg(b)-1;
+  if(DEBUGLEVEL>4) err_printf("Entering gauss\n");
+
+  p = NULL; /* gcc -Wall */
+  for (i=1; i<=aco; i++)
+  {
+    /* k is the line where we find the pivot */
+    k = pivot(a, data, i, NULL);
+    if (k > li) return NULL;
+    if (k != i)
+    { /* exchange the lines s.t. k = i */
+      for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
+      for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
+    }
+    p = gcoeff(a,i,i);
+    if (i == aco) break;
+
+    for (k=i+1; k<=li; k++)
+    {
+      GEN m = gcoeff(a,k,i);
+      if (!gequal0(m))
+      {
+        m = gdiv(m,p);
+        for (j=i+1; j<=aco; j++) _submul(gel(a,j),k,i,m);
+        for (j=1;   j<=bco; j++) _submul(gel(b,j),k,i,m);
+      }
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gauss. i=%ld",i);
+      gerepileall(av,2, &a,&b);
+    }
+  }
+
+  if(DEBUGLEVEL>4) err_printf("Solving the triangular system\n");
+  u = cgetg(bco+1,t_MAT);
+  for (j=1; j<=bco; j++) gel(u,j) = get_col(a,gel(b,j),p,aco);
+  return gerepilecopy(av, iscol? gel(u,1): u);
+}
+
+/* assume dim A >= 1, A invertible + upper triangular  */
+static GEN
+RgM_inv_upper_ind(GEN A, long index)
+{
+  long n = lg(A)-1, i = index, j;
+  GEN u = zerocol(n);
+  gel(u,i) = ginv(gcoeff(A,i,i));
+  for (i--; i>0; i--)
+  {
+    pari_sp av = avma;
+    GEN m = gneg(gmul(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
+    for (j=i+2; j<=n; j++) m = gsub(m, gmul(gcoeff(A,i,j),gel(u,j)));
+    gel(u,i) = gerepileupto(av, gdiv(m, gcoeff(A,i,i)));
+  }
+  return u;
+}
+GEN
+RgM_inv_upper(GEN A)
+{
+  long i, l;
+  GEN B = cgetg_copy(A, &l);
+  for (i = 1; i < l; i++) gel(B,i) = RgM_inv_upper_ind(A, i);
+  return B;
+}
+
+/* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal  */
+static GEN
+FpM_inv_upper_1_ind(GEN A, long index, GEN p)
+{
+  long n = lg(A)-1, i = index, j;
+  GEN u = zerocol(n);
+  gel(u,i) = gen_1;
+  for (i--; i>0; i--)
+  {
+    pari_sp av = avma;
+    GEN m = negi(mulii(gcoeff(A,i,i+1),gel(u,i+1))); /* j = i+1 */
+    for (j=i+2; j<=n; j++) m = subii(m, mulii(gcoeff(A,i,j),gel(u,j)));
+    gel(u,i) = gerepileuptoint(av, modii(m,p));
+  }
+  return u;
+}
+static GEN
+FpM_inv_upper_1(GEN A, GEN p)
+{
+  long i, l;
+  GEN B = cgetg_copy(A, &l);
+  for (i = 1; i < l; i++) gel(B,i) = FpM_inv_upper_1_ind(A, i, p);
+  return B;
+}
+/* assume dim A >= 1, A invertible + upper triangular, 1s on diagonal,
+ * reduced mod p */
+static GEN
+Flm_inv_upper_1_ind(GEN A, long index, ulong p)
+{
+  long n = lg(A)-1, i = index, j;
+  GEN u = const_vecsmall(n, 0);
+  u[i] = 1;
+  if (SMALL_ULONG(p))
+    for (i--; i>0; i--)
+    {
+      ulong m = ucoeff(A,i,i+1) * uel(u,i+1); /* j = i+1 */
+      for (j=i+2; j<=n; j++)
+      {
+        if (m & HIGHMASK) m %= p;
+        m += ucoeff(A,i,j) * uel(u,j);
+      }
+      u[i] = Fl_neg(m % p, p);
+    }
+  else
+    for (i--; i>0; i--)
+    {
+      ulong m = Fl_mul(ucoeff(A,i,i+1),uel(u,i+1), p); /* j = i+1 */
+      for (j=i+2; j<=n; j++) m = Fl_add(m, Fl_mul(ucoeff(A,i,j),uel(u,j),p), p);
+      u[i] = Fl_neg(m, p);
+    }
+  return u;
+}
+static GEN
+F2m_inv_upper_1_ind(GEN A, long index)
+{
+  pari_sp av = avma;
+  long n = lg(A)-1, i = index, j;
+  GEN u = const_vecsmall(n, 0);
+  u[i] = 1;
+  for (i--; i>0; i--)
+  {
+    ulong m = F2m_coeff(A,i,i+1) & uel(u,i+1); /* j = i+1 */
+    for (j=i+2; j<=n; j++) m ^= F2m_coeff(A,i,j) & uel(u,j);
+    u[i] = m & 1;
+  }
+  return gerepileuptoleaf(av, Flv_to_F2v(u));
+}
+static GEN
+Flm_inv_upper_1(GEN A, ulong p)
+{
+  long i, l;
+  GEN B = cgetg_copy(A, &l);
+  for (i = 1; i < l; i++) gel(B,i) = Flm_inv_upper_1_ind(A, i, p);
+  return B;
+}
+static GEN
+F2m_inv_upper_1(GEN A)
+{
+  long i, l;
+  GEN B = cgetg_copy(A, &l);
+  for (i = 1; i < l; i++) gel(B,i) = F2m_inv_upper_1_ind(A, i);
+  return B;
+}
+
+static GEN
+split_realimag_col(GEN z, long r1, long r2)
+{
+  long i, ru = r1+r2;
+  GEN x = cgetg(ru+r2+1,t_COL), y = x + r2;
+  for (i=1; i<=r1; i++) {
+    GEN a = gel(z,i);
+    if (typ(a) == t_COMPLEX) a = gel(a,1); /* paranoia: a should be real */
+    gel(x,i) = a;
+  }
+  for (   ; i<=ru; i++) {
+    GEN b, a = gel(z,i);
+    if (typ(a) == t_COMPLEX) { b = gel(a,2); a = gel(a,1); } else b = gen_0;
+    gel(x,i) = a;
+    gel(y,i) = b;
+  }
+  return x;
+}
+GEN
+split_realimag(GEN x, long r1, long r2)
+{
+  long i,l; GEN y;
+  if (typ(x) == t_COL) return split_realimag_col(x,r1,r2);
+  y = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(y,i) = split_realimag_col(gel(x,i), r1, r2);
+  return y;
+}
+
+/* assume M = (r1+r2) x (r1+2r2) matrix and y compatible vector or matrix
+ * r1 first lines of M,y are real. Solve the system obtained by splitting
+ * real and imaginary parts. */
+GEN
+RgM_solve_realimag(GEN M, GEN y)
+{
+  long l = lg(M), r2 = l - lgcols(M), r1 = l-1 - 2*r2;
+  return RgM_solve(split_realimag(M, r1,r2),
+                   split_realimag(y, r1,r2));
+}
+
+GEN
+gauss(GEN a, GEN b)
+{
+  GEN z;
+  if (typ(a)!=t_MAT) pari_err_TYPE("gauss",a);
+  if (RgM_is_ZM(a) && b &&
+      ((typ(b) == t_COL && RgV_is_ZV(b)) || (typ(b) == t_MAT && RgM_is_ZM(b))))
+    z = ZM_gauss(a,b);
+  else
+    z = RgM_solve(a,b);
+  if (!z) pari_err_INV("gauss",a);
+  return z;
+}
+
+/* destroy a, b */
+static GEN
+F2m_gauss_sp(GEN a, GEN b)
+{
+  long i, j, k, l, li, bco, aco = lg(a)-1;
+  GEN u, d;
+
+  if (!aco) return cgetg(1,t_MAT);
+  li = coeff(a,1,1);
+  d = zero_Flv(li);
+  bco = lg(b)-1;
+  for (i=1; i<=aco; i++)
+  {
+    GEN ai = vecsmall_copy(gel(a,i));
+    if (!d[i] && F2v_coeff(ai, i))
+      k = i;
+    else
+      for (k = 1; k <= li; k++) if (!d[k] && F2v_coeff(ai,k)) break;
+    /* found a pivot on row k */
+    if (k > li) return NULL;
+    d[k] = i;
+
+    /* Clear k-th row but column-wise */
+    F2v_clear(ai,k);
+    for (l=1; l<=aco; l++)
+    {
+      GEN al = gel(a,l);
+      if (!F2v_coeff(al,k)) continue;
+
+      F2v_add_inplace(al,ai);
+    }
+    for (l=1; l<=bco; l++)
+    {
+      GEN al = gel(b,l);
+      if (!F2v_coeff(al,k)) continue;
+
+      F2v_add_inplace(al,ai);
+    }
+  }
+  u = gcopy(b);
+  for (j = 1; j <= bco; j++)
+  {
+    GEN bj = gel(b, j), uj = gel(u, j);
+
+    for (i = 1; i <= li; i++)
+      if (d[i] && d[i] != i) /* can d[i] still be 0 ? */
+      {
+        if (F2v_coeff(bj, i))
+          F2v_set(uj, d[i]);
+        else
+          F2v_clear(uj, d[i]);
+      }
+  }
+  return u;
+}
+
+GEN
+F2m_gauss(GEN a, GEN b)
+{
+  pari_sp av = avma;
+  if (lg(a) == 1) return cgetg(1,t_MAT);
+  return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), F2m_copy(b)));
+}
+GEN
+F2m_F2c_gauss(GEN a, GEN b)
+{
+  pari_sp av = avma;
+  GEN z = F2m_gauss(a, mkmat(b));
+  if (lg(z) == 1) { avma = av; return cgetg(1,t_VECSMALL); }
+  return gerepileuptoleaf(av, gel(z,1));
+}
+
+GEN
+F2m_inv(GEN a)
+{
+  pari_sp av = avma;
+  if (lg(a) == 1) return cgetg(1,t_MAT);
+  return gerepileupto(av, F2m_gauss_sp(F2m_copy(a), matid_F2m(lg(a)-1)));
+}
+
+/* destroy a, b */
+static GEN
+Flm_gauss_sp(GEN a, GEN b, ulong *detp, ulong p)
+{
+  long i, j, k, li, bco, aco = lg(a)-1, s = 1;
+  const int OK_ulong = SMALL_ULONG(p);
+  ulong det = 1;
+  GEN u;
+
+  if (!aco) { if (detp) *detp = 1; return cgetg(1,t_MAT); }
+  li = nbrows(a);
+  bco = lg(b)-1;
+  for (i=1; i<=aco; i++)
+  {
+    ulong invpiv;
+    /* Fl_get_col wants 0 <= a[i,j] < p for all i,j */
+    if (OK_ulong)
+    {
+      for (k = 1; k < i; k++) ucoeff(a,k,i) %= p;
+      for (k = i; k <= li; k++)
+      {
+        ulong piv = ( ucoeff(a,k,i) %= p );
+        if (piv)
+        {
+          ucoeff(a,k,i) = Fl_inv(piv, p);
+          if (detp)
+          {
+            if (det & HIGHMASK) det %= p;
+            det *= piv;
+          }
+          break;
+        }
+      }
+    }
+    else
+    {
+      for (k = i; k <= li; k++)
+      {
+        ulong piv = ucoeff(a,k,i);
+        if (piv)
+        {
+          ucoeff(a,k,i) = Fl_inv(piv, p);
+          if (detp) det = Fl_mul(det, piv, p);
+          break;
+        }
+      }
+    }
+    /* found a pivot on line k */
+    if (k > li) return NULL;
+    if (k != i)
+    { /* swap lines so that k = i */
+      s = -s;
+      for (j=i; j<=aco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
+      for (j=1; j<=bco; j++) swap(gcoeff(b,i,j), gcoeff(b,k,j));
+    }
+    if (i == aco) break;
+
+    invpiv = ucoeff(a,i,i); /* 1/piv mod p */
+    for (k=i+1; k<=li; k++)
+    {
+      ulong m = ( ucoeff(a,k,i) %= p );
+      if (!m) continue;
+
+      m = Fl_mul(m, invpiv, p);
+      if (OK_ulong)
+      {
+        m = p - m; /* = -m */
+        if (m == 1) {
+          for (j=i+1; j<=aco; j++) _Fl_add_OK(gel(a,j),k,i, p);
+          for (j=1;   j<=bco; j++) _Fl_add_OK(gel(b,j),k,i, p);
+        } else {
+          for (j=i+1; j<=aco; j++) _Fl_addmul_OK(gel(a,j),k,i,m, p);
+          for (j=1;   j<=bco; j++) _Fl_addmul_OK(gel(b,j),k,i,m, p);
+        }
+      } else {
+        if (m == 1) {
+          for (j=i+1; j<=aco; j++) _Fl_sub(gel(a,j),k,i, p);
+          for (j=1;   j<=bco; j++) _Fl_sub(gel(b,j),k,i, p);
+        } else {
+          for (j=i+1; j<=aco; j++) _Fl_submul(gel(a,j),k,i,m, p);
+          for (j=1;   j<=bco; j++) _Fl_submul(gel(b,j),k,i,m, p);
+        }
+      }
+    }
+  }
+  if (detp)
+  {
+    det %=  p;
+    if (s < 0 && det) det = p - det;
+    *detp = det;
+  }
+  u = cgetg(bco+1,t_MAT);
+  if (OK_ulong)
+    for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col_OK(a,gel(b,j), aco,p);
+  else
+    for (j=1; j<=bco; j++) gel(u,j) = Fl_get_col(a,gel(b,j), aco,p);
+  return u;
+}
+
+GEN
+Flm_gauss(GEN a, GEN b, ulong p) {
+  return Flm_gauss_sp(RgM_shallowcopy(a), RgM_shallowcopy(b), NULL, p);
+}
+static GEN
+Flm_inv_sp(GEN a, ulong *detp, ulong p) {
+  return Flm_gauss_sp(a, matid_Flm(lg(a)-1), detp, p);
+}
+GEN
+Flm_inv(GEN a, ulong p) {
+  return Flm_inv_sp(RgM_shallowcopy(a), NULL, p);
+}
+GEN
+Flm_Flc_gauss(GEN a, GEN b, ulong p) {
+  pari_sp av = avma;
+  GEN z = Flm_gauss(a, mkmat(b), p);
+  if (lg(z) == 1) { avma = av; return cgetg(1,t_VECSMALL); }
+  return gerepileuptoleaf(av, gel(z,1));
+}
+
+static GEN
+FpM_gauss_gen(GEN a, GEN b, GEN p)
+{
+  void *E;
+  const struct bb_field *S = get_Fp_field(&E,p);
+  return gen_Gauss(a,b, E, S);
+}
+/* a an FpM; b an FpM or NULL (replace by identity) */
+static GEN
+FpM_gauss_i(GEN a, GEN b, GEN p, ulong *pp)
+{
+  long n = lg(a)-1;
+  a = FpM_init(a,p,pp);
+  switch(*pp)
+  {
+  case 0:
+    if (!b) b = matid(n);
+    return FpM_gauss_gen(a,b,p);
+  case 2:
+    if (b) b = ZM_to_F2m(b); else b = matid_F2m(n);
+    return F2m_gauss_sp(a,b);
+  default:
+    if (b) b = ZM_to_Flm(b, *pp); else b = matid_Flm(n);
+    return Flm_gauss_sp(a,b, NULL, *pp);
+  }
+}
+GEN
+FpM_gauss(GEN a, GEN b, GEN p)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN u;
+  if (lg(a) == 1 || lg(b)==1) return cgetg(1, t_MAT);
+  u = FpM_gauss_i(a, b, p, &pp);
+  if (!u) { avma = av; return NULL; }
+  switch(pp)
+  {
+  case 0: return gerepilecopy(av, u);
+  case 2:  u = F2m_to_ZM(u); break;
+  default: u = Flm_to_ZM(u); break;
+  }
+  return gerepileupto(av, u);
+}
+GEN
+FpM_inv(GEN a, GEN p)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN u;
+  if (lg(a) == 1) return cgetg(1, t_MAT);
+  u = FpM_gauss_i(a, NULL, p, &pp);
+  if (!u) { avma = av; return NULL; }
+  switch(pp)
+  {
+  case 0: return gerepilecopy(av, u);
+  case 2:  u = F2m_to_ZM(u); break;
+  default: u = Flm_to_ZM(u); break;
+  }
+  return gerepileupto(av, u);
+}
+
+GEN
+FpM_FpC_gauss(GEN a, GEN b, GEN p)
+{
+  pari_sp av = avma;
+  ulong pp;
+  GEN u;
+  if (lg(a) == 1) return cgetg(1, t_COL);
+  u = FpM_gauss_i(a, mkmat(b), p, &pp);
+  if (!u) { avma = av; return NULL; }
+  switch(pp)
+  {
+  case 0: return gerepilecopy(av, gel(u,1));
+  case 2:  u = F2c_to_ZC(gel(u,1)); break;
+  default: u = Flc_to_ZC(gel(u,1)); break;
+  }
+  return gerepileupto(av, u);
+}
+
+static GEN
+FlxqM_gauss_gen(GEN a, GEN b, GEN T, ulong p)
+{
+  void *E;
+  const struct bb_field *S = get_Flxq_field(&E, T, p);
+  return gen_Gauss(a, b, E, S);
+}
+GEN
+FlxqM_gauss(GEN a, GEN b, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long n = lg(a)-1;
+  GEN u;
+  if (!n || lg(b)==1) { avma = av; return cgetg(1, t_MAT); }
+  u = FlxqM_gauss_gen(a, b, T, p);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, u);
+}
+GEN
+FlxqM_inv(GEN a, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  long n = lg(a)-1;
+  GEN u;
+  if (!n) { avma = av; return cgetg(1, t_MAT); }
+  u = FlxqM_gauss_gen(a, matid_FlxqM(n,T,p), T,p);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, u);
+}
+GEN
+FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p)
+{
+  pari_sp av = avma;
+  GEN u;
+  if (lg(a) == 1) return cgetg(1, t_COL);
+  u = FlxqM_gauss_gen(a, mkmat(b), T, p);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, gel(u,1));
+}
+
+static GEN
+FqM_gauss_gen(GEN a, GEN b, GEN T, GEN p)
+{
+  void *E;
+  const struct bb_field *S = get_Fq_field(&E,T,p);
+  return gen_Gauss(a,b,E,S);
+}
+GEN
+FqM_gauss(GEN a, GEN b, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN u;
+  long n;
+  if (!T) return FpM_gauss(a,b,p);
+  n = lg(a)-1; if (!n || lg(b)==1) return cgetg(1, t_MAT);
+  u = FqM_gauss_gen(a,b,T,p);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, u);
+}
+GEN
+FqM_inv(GEN a, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN u;
+  long n;
+  if (!T) return FpM_inv(a,p);
+  n = lg(a)-1; if (!n) return cgetg(1, t_MAT);
+  u = FqM_gauss_gen(a,matid(n),T,p);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, u);
+}
+GEN
+FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN u;
+  if (!T) return FpM_FpC_gauss(a,b,p);
+  if (lg(a) == 1) return cgetg(1, t_COL);
+  u = FqM_gauss_gen(a,mkmat(b),T,p);
+  if (!u) { avma = av; return NULL; }
+  return gerepilecopy(av, gel(u,1));
+}
+
+/* Dixon p-adic lifting algorithm.
+ * Numer. Math. 40, 137-141 (1982), DOI: 10.1007/BF01459082 */
+GEN
+ZM_gauss(GEN a, GEN b0)
+{
+  pari_sp av = avma, av2;
+  int iscol;
+  long n, ncol, i, m, elim;
+  ulong p;
+  GEN N, C, delta, xb, nb, nmin, res, b = b0;
+
+  if (!init_gauss(a, &b, &n, &ncol, &iscol)) return cgetg(1, iscol?t_COL:t_MAT);
+  nb = gen_0; ncol = lg(b);
+  for (i = 1; i < ncol; i++)
+  {
+    GEN ni = gnorml2(gel(b, i));
+    if (cmpii(nb, ni) < 0) nb = ni;
+  }
+  if (!signe(nb)) { avma = av; return gcopy(b0); }
+  delta = gen_1; nmin = nb;
+  for (i = 1; i <= n; i++)
+  {
+    GEN ni = gnorml2(gel(a, i));
+    if (cmpii(ni, nmin) < 0)
+    {
+      delta = mulii(delta, nmin); nmin = ni;
+    }
+    else
+      delta = mulii(delta, ni);
+  }
+  if (!signe(nmin)) return NULL;
+  elim = expi(delta)+1;
+  av2 = avma;
+#ifdef LONG_IS_64BIT
+  p = 1000000000000000000;
+#else
+  p = 1000000000;
+#endif
+  for(;;)
+  {
+    p = unextprime(p+1);
+    C = Flm_inv(ZM_to_Flm(a, p), p);
+    if (C) break;
+    elim -= expu(p);
+    if (elim < 0) return NULL;
+    avma = av2;
+  }
+  /* N.B. Our delta/lambda are SQUARES of those in the paper
+   * log(delta lambda) / log p, where lambda is 3+sqrt(5) / 2,
+   * whose log is < 1, hence + 1 (to cater for rounding errors) */
+  m = (long)ceil((rtodbl(logr_abs(itor(delta,LOWDEFAULTPREC))) + 1)
+                 / log((double)p));
+  xb = ZlM_gauss(a, b, p, m, C);
+  N = powuu(p, m);
+  delta = sqrti(delta);
+  if (iscol)
+    res = FpC_ratlift(gel(xb,1), N, delta,delta, NULL);
+  else
+    res = FpM_ratlift(xb, N, delta,delta, NULL);
+  return gerepileupto(av, res);
+}
+
+/* M integral, dM such that M' = dM M^-1 is integral [e.g det(M)]. Return M' */
+GEN
+ZM_inv(GEN M, GEN dM)
+{
+  pari_sp av2, av = avma, lim = stack_lim(av,1);
+  GEN Hp,q,H;
+  ulong p;
+  long lM = lg(M), stable = 0;
+  int negate = 0;
+  forprime_t S;
+
+  if (lM == 1) return cgetg(1,t_MAT);
+
+  /* HACK: include dM = -1 ! */
+  if (dM && is_pm1(dM))
+  {
+    /* modular algorithm computes M^{-1}, NOT multiplied by det(M) = -1.
+     * We will correct (negate) at the end. */
+    if (signe(dM) < 0) negate = 1;
+    dM = gen_1;
+  }
+  init_modular(&S);
+  av2 = avma;
+  H = NULL;
+  while ((p = u_forprime_next(&S)))
+  {
+    ulong dMp;
+    GEN Mp;
+    Mp = ZM_to_Flm(M,p);
+    if (dM == gen_1)
+      Hp = Flm_inv_sp(Mp, NULL, p);
+    else
+    {
+      if (dM) {
+        dMp = umodiu(dM,p); if (!dMp) continue;
+        Hp = Flm_inv_sp(Mp, NULL, p);
+        if (!Hp) pari_err_INV("ZM_inv", Mp);
+      } else {
+        Hp = Flm_inv_sp(Mp, &dMp, p);
+        if (!Hp) continue;
+      }
+      if (dMp != 1) Flm_Fl_mul_inplace(Hp, dMp, p);
+    }
+
+    if (!H)
+    {
+      H = ZM_init_CRT(Hp, p);
+      q = utoipos(p);
+    }
+    else
+      stable = ZM_incremental_CRT(&H, Hp, &q, p);
+    if (DEBUGLEVEL>5) err_printf("inverse mod %ld (stable=%ld)\n", p,stable);
+    if (stable) {/* DONE ? */
+      if (dM != gen_1)
+      { if (RgM_isscalar(ZM_mul(M, H), dM)) break; }
+      else
+      { if (ZM_isidentity(ZM_mul(M, H))) break; }
+    }
+
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ZM_inv");
+      gerepileall(av2, 2, &H, &q);
+    }
+  }
+  if (!p) pari_err_OVERFLOW("ZM_inv [ran out of primes]");
+  if (DEBUGLEVEL>5) err_printf("ZM_inv done\n");
+  if (negate)
+    return gerepileupto(av, ZM_neg(H));
+  else
+    return gerepilecopy(av, H);
+}
+
+/* same as above, M rational */
+GEN
+QM_inv(GEN M, GEN dM)
+{
+  pari_sp av = avma;
+  GEN cM, pM = Q_primitive_part(M, &cM);
+  if (!cM) return ZM_inv(pM,dM);
+  return gerepileupto(av, ZM_inv(pM, gdiv(dM,cM)));
+}
+
+/* x a ZM. Return a multiple of the determinant of the lattice generated by
+ * the columns of x. From Algorithm 2.2.6 in GTM138 */
+GEN
+detint(GEN A)
+{
+  if (typ(A) != t_MAT) pari_err_TYPE("detint",A);
+  RgM_check_ZM(A, "detint");
+  return ZM_detmult(A);
+}
+GEN
+ZM_detmult(GEN A)
+{
+  pari_sp av1, av = avma, lim = stack_lim(av,1);
+  GEN B, c, v, piv;
+  long rg, i, j, k, m, n = lg(A) - 1;
+
+  if (!n) return gen_1;
+  m = nbrows(A);
+  if (n < m) return gen_0;
+  c = zero_zv(m);
+  av1 = avma;
+  B = zeromatcopy(m,m);
+  v = cgetg(m+1, t_COL);
+  piv = gen_1; rg = 0;
+  for (k=1; k<=n; k++)
+  {
+    GEN pivprec = piv;
+    long t = 0;
+    for (i=1; i<=m; i++)
+    {
+      pari_sp av2 = avma;
+      GEN vi;
+      if (c[i]) continue;
+
+      vi = mulii(piv, gcoeff(A,i,k));
+      for (j=1; j<=m; j++)
+        if (c[j]) vi = addii(vi, mulii(gcoeff(B,j,i),gcoeff(A,j,k)));
+      if (!t && signe(vi)) t = i;
+      gel(v,i) = gerepileuptoint(av2, vi);
+    }
+    if (!t) continue;
+    /* at this point c[t] = 0 */
+
+    if (++rg >= m) { /* full rank; mostly done */
+      GEN det = gel(v,t); /* last on stack */
+      if (++k > n)
+        det = absi(det);
+      else
+      {
+        /* improve further; at this point c[i] is set for all i != t */
+        gcoeff(B,t,t) = piv; v = centermod(gel(B,t), det);
+        av1 = avma; lim = stack_lim(av1,1);
+        for ( ; k<=n; k++)
+        {
+          det = gcdii(det, ZV_dotproduct(v, gel(A,k)));
+          if (low_stack(lim, stack_lim(av1,1)))
+          {
+            if(DEBUGMEM>1) pari_warn(warnmem,"detint end. k=%ld",k);
+            det = gerepileuptoint(av1, det);
+          }
+        }
+      }
+      return gerepileuptoint(av, det);
+    }
+
+    piv = gel(v,t);
+    for (i=1; i<=m; i++)
+    {
+      GEN mvi;
+      if (c[i] || i == t) continue;
+
+      gcoeff(B,t,i) = mvi = negi(gel(v,i));
+      for (j=1; j<=m; j++)
+        if (c[j]) /* implies j != t */
+        {
+          pari_sp av2 = avma;
+          GEN z = addii(mulii(gcoeff(B,j,i), piv), mulii(gcoeff(B,j,t), mvi));
+          if (rg > 1) z = diviiexact(z, pivprec);
+          gcoeff(B,j,i) = gerepileuptoint(av2, z);
+        }
+    }
+    c[t] = k;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"detint. k=%ld",k);
+      gerepileall(av1, 2, &piv,&B); v = zerovec(m);
+    }
+  }
+  avma = av; return gen_0;
+}
+
+/* Reduce x modulo (invertible) y */
+GEN
+closemodinvertible(GEN x, GEN y)
+{
+  return gmul(y, ground(RgM_solve(y,x)));
+}
+GEN
+reducemodinvertible(GEN x, GEN y)
+{
+  return gsub(x, closemodinvertible(x,y));
+}
+GEN
+reducemodlll(GEN x,GEN y)
+{
+  return reducemodinvertible(x, ZM_lll(y, 0.75, LLL_INPLACE));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    KERNEL of an m x n matrix                    */
+/*          return n - rk(x) linearly independent vectors          */
+/*                                                                 */
+/*******************************************************************/
+/* x has INTEGER coefficients. Gauss-Bareiss */
+GEN
+keri(GEN x)
+{
+  pari_sp av, av0, lim;
+  GEN c, l, y, p, pp;
+  long i, j, k, r, t, n, m;
+
+  n = lg(x)-1; if (!n) return cgetg(1,t_MAT);
+  av0 = avma; m = nbrows(x);
+  pp = cgetg(n+1,t_COL);
+  x = RgM_shallowcopy(x);
+  c = zero_zv(m);
+  l = cgetg(n+1, t_VECSMALL);
+  av = avma; lim = stack_lim(av,1);
+  for (r=0, p=gen_1, k=1; k<=n; k++)
+  {
+    j = 1;
+    while ( j <= m && (c[j] || !signe(gcoeff(x,j,k))) ) j++;
+    if (j > m)
+    {
+      r++; l[k] = 0;
+      for(j=1; j<k; j++)
+        if (l[j]) gcoeff(x,l[j],k) = gclone(gcoeff(x,l[j],k));
+      gel(pp,k) = gclone(p);
+    }
+    else
+    {
+      GEN p0 = p;
+      c[j] = k; l[k] = j; p = gcoeff(x,j,k);
+      for (t=1; t<=m; t++)
+        if (t!=j)
+        {
+          GEN q = gcoeff(x,t,k);
+          for (i=k+1; i<=n; i++)
+          {
+            pari_sp av1 = avma;
+            GEN p1 = subii(mulii(p,gcoeff(x,t,i)), mulii(q,gcoeff(x,j,i)));
+            gcoeff(x,t,i) = gerepileuptoint(av1, diviiexact(p1,p0));
+          }
+          if (low_stack(lim, stack_lim(av,1)))
+          {
+            GEN _p0 = gclone(p0);
+            GEN _p  = gclone(p);
+            gerepile_gauss_ker(x,k,t,av);
+            p = icopy(_p);  gunclone(_p);
+            p0= icopy(_p0); gunclone(_p0);
+          }
+        }
+    }
+  }
+  if (!r) { avma = av0; y = cgetg(1,t_MAT); return y; }
+
+  /* non trivial kernel */
+  y = cgetg(r+1,t_MAT);
+  for (j=k=1; j<=r; j++,k++)
+  {
+    p = cgetg(n+1, t_COL);
+    gel(y,j) = p; while (l[k]) k++;
+    for (i=1; i<k; i++)
+      if (l[i])
+      {
+        c = gcoeff(x,l[i],k);
+        gel(p,i) = icopy(c); gunclone(c);
+      }
+      else
+        gel(p,i) = gen_0;
+    gel(p,k) = negi(gel(pp,k)); gunclone(gel(pp,k));
+    for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
+  }
+  return gerepileupto(av0, y);
+}
+
+static GEN
+deplin_aux(GEN x0)
+{
+  pari_sp av = avma;
+  long i, j, k, nl, nc = lg(x0)-1;
+  GEN D, x, y, c, l, d, ck;
+
+  if (!nc) { avma=av; return cgetg(1,t_COL); }
+  x = RgM_shallowcopy(x0);
+  nl = nbrows(x);
+  d = const_vec(nl, gen_1); /* pivot list */
+  c = zero_zv(nl);
+  l = cgetg(nc+1, t_VECSMALL); /* not initialized */
+  ck = NULL; /* gcc -Wall */
+  for (k=1; k<=nc; k++)
+  {
+    ck = gel(x,k);
+    for (j=1; j<k; j++)
+    {
+      GEN cj = gel(x,j), piv = gel(d,j), q = gel(ck,l[j]);
+      for (i=1; i<=nl; i++)
+        if (i!=l[j]) gel(ck,i) = gsub(gmul(piv, gel(ck,i)), gmul(q, gel(cj,i)));
+    }
+
+    i = gauss_get_pivot_NZ(x, NULL, k, c);
+    if (i > nl) break;
+
+    gel(d,k) = gel(ck,i);
+    c[i] = k; l[k] = i; /* pivot d[k] in x[i,k] */
+  }
+  if (k > nc) { avma = av; return cgetg(1,t_COL); }
+  if (k == 1) { avma = av; return scalarcol_shallow(gen_1,nc); }
+  y = cgetg(nc+1,t_COL);
+  gel(y,1) = gcopy(gel(ck, l[1]));
+  for (D=gel(d,1),j=2; j<k; j++)
+  {
+    gel(y,j) = gmul(gel(ck, l[j]), D);
+    D = gmul(D, gel(d,j));
+  }
+  gel(y,j) = gneg(D);
+  for (j++; j<=nc; j++) gel(y,j) = gen_0;
+  y = primitive_part(y, &c);
+  return c? gerepileupto(av, y): gerepilecopy(av, y);
+}
+static GEN
+RgV_deplin(GEN v)
+{
+  pari_sp av = avma;
+  long n = lg(v)-1;
+  GEN y, p = NULL;
+  if (n <= 1)
+  {
+    if (n == 1 && gequal0(gel(v,1))) return mkcol(gen_1);
+    return cgetg(1, t_COL);
+  }
+  if (gequal0(gel(v,1))) return scalarcol_shallow(gen_1, n);
+  v = primpart(mkvec2(gel(v,1),gel(v,2)));
+  if (RgV_is_FpV(v, &p) && p) v = centerlift(v);
+  y = zerocol(n);
+  gel(y,1) = gneg(gel(v,2));
+  gel(y,2) = gcopy(gel(v,1));
+  return gerepileupto(av, y);
+
+}
+GEN
+deplin(GEN x)
+{
+  GEN p = NULL;
+  switch(typ(x))
+  {
+    case t_MAT: break;
+    case t_VEC: return RgV_deplin(x);
+    default: pari_err_TYPE("deplin",x);
+  }
+  if (RgM_is_FpM(x, &p) && p)
+  {
+    pari_sp av = avma;
+    ulong pp;
+    x = RgM_Fp_init(x, p, &pp);
+    switch(pp)
+    {
+    case 0:
+      x = FpM_ker_gen(x,p,1);
+      if (!x) { avma = av; return cgetg(1,t_COL); }
+      x = FpC_center(x,p,shifti(p,-1));
+      break;
+    case 2:
+      x = F2m_ker_sp(x,1);
+      if (!x) { avma = av; return cgetg(1,t_COL); }
+      x = F2c_to_ZC(x); break;
+    default:
+      x = Flm_ker_sp(x,pp,1);
+      if (!x) { avma = av; return cgetg(1,t_COL); }
+      x = Flv_center(x, pp, pp>>1);
+      x = zc_to_ZC(x);
+      break;
+    }
+    return gerepileupto(av, x);
+  }
+  return deplin_aux(x);
+}
+/*******************************************************************/
+/*                                                                 */
+/*         GAUSS REDUCTION OF MATRICES  (m lines x n cols)         */
+/*           (kernel, image, complementary image, rank)            */
+/*                                                                 */
+/*******************************************************************/
+/* return the transform of x under a standard Gauss pivot.
+ * x0 is a reference point when guessing whether x[i,j] ~ 0
+ * (iff x[i,j] << x0[i,j])
+ * Set r = dim ker(x). d[k] contains the index of the first non-zero pivot
+ * in column k */
+static GEN
+gauss_pivot_ker(GEN x, GEN x0, GEN *dd, long *rr)
+{
+  GEN c, d, p, data;
+  pari_sp av, lim;
+  long i, j, k, r, t, n, m;
+  pivot_fun pivot;
+
+  n=lg(x)-1; if (!n) { *dd=NULL; *rr=0; return cgetg(1,t_MAT); }
+  m=nbrows(x); r=0;
+  pivot = get_pivot_fun(x, x0, &data);
+  x = RgM_shallowcopy(x);
+  c = zero_zv(m);
+  d = cgetg(n+1,t_VECSMALL);
+  av=avma; lim=stack_lim(av,1);
+  for (k=1; k<=n; k++)
+  {
+    j = pivot(x, data, k, c);
+    if (j > m)
+    {
+      r++; d[k]=0;
+      for(j=1; j<k; j++)
+        if (d[j]) gcoeff(x,d[j],k) = gclone(gcoeff(x,d[j],k));
+    }
+    else
+    { /* pivot for column k on row j */
+      c[j]=k; d[k]=j; p = gdiv(gen_m1,gcoeff(x,j,k));
+      gcoeff(x,j,k) = gen_m1;
+      /* x[j,] /= - x[j,k] */
+      for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
+      for (t=1; t<=m; t++)
+        if (t!=j)
+        { /* x[t,] -= 1 / x[j,k] x[j,] */
+          p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
+          for (i=k+1; i<=n; i++)
+            gcoeff(x,t,i) = gadd(gcoeff(x,t,i),gmul(p,gcoeff(x,j,i)));
+          if (low_stack(lim, stack_lim(av,1))) gerepile_gauss_ker(x,k,t,av);
+        }
+    }
+  }
+  *dd=d; *rr=r; return x;
+}
+
+static GEN FpM_gauss_pivot(GEN x, GEN p, long *rr);
+static GEN FqM_gauss_pivot(GEN x, GEN T, GEN p, long *rr);
+static GEN F2m_gauss_pivot(GEN x, long *rr);
+static GEN Flm_gauss_pivot(GEN x, ulong p, long *rry);
+
+/* r = dim ker(x).
+ * Returns d:
+ *   d[k] != 0 contains the index of a non-zero pivot in column k
+ *   d[k] == 0 if column k is a linear combination of the (k-1) first ones */
+GEN
+RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot)
+{
+  GEN x, c, d, p;
+  long i, j, k, r, t, m, n = lg(x0)-1;
+  pari_sp av, lim;
+
+  if (RgM_is_ZM(x0)) return ZM_pivots(x0, rr);
+  if (!n) { *rr = 0; return NULL; }
+
+  d = cgetg(n+1, t_VECSMALL);
+  x = RgM_shallowcopy(x0);
+  m = nbrows(x); r = 0;
+  c = zero_zv(m);
+  av = avma; lim = stack_lim(av,1);
+  for (k=1; k<=n; k++)
+  {
+    j = pivot(x, data, k, c);
+    if (j > m) { r++; d[k] = 0; }
+    else
+    {
+      c[j] = k; d[k] = j; p = gdiv(gen_m1, gcoeff(x,j,k));
+      for (i=k+1; i<=n; i++) gcoeff(x,j,i) = gmul(p,gcoeff(x,j,i));
+
+      for (t=1; t<=m; t++)
+        if (!c[t]) /* no pivot on that line yet */
+        {
+          p = gcoeff(x,t,k); gcoeff(x,t,k) = gen_0;
+          for (i=k+1; i<=n; i++)
+            gcoeff(x,t,i) = gadd(gcoeff(x,t,i), gmul(p, gcoeff(x,j,i)));
+          if (low_stack(lim, stack_lim(av,1))) gerepile_gauss(x,k,t,av,j,c);
+        }
+      for (i=k; i<=n; i++) gcoeff(x,j,i) = gen_0; /* dummy */
+    }
+  }
+  *rr = r; avma = (pari_sp)d; return d;
+}
+
+static long
+ZM_count_0_cols(GEN M)
+{
+  long i, l = lg(M), n = 0;
+  for (i = 1; i < l; i++)
+    if (ZV_equal0(gel(M,i))) n++;
+  return n;
+}
+
+static void indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol);
+/* As above, integer entries */
+GEN
+ZM_pivots(GEN M0, long *rr)
+{
+  GEN d, dbest = NULL;
+  long m, n, i, imax, rmin, rbest, zc;
+  int beenthere = 0;
+  pari_sp av, av0 = avma;
+  forprime_t S;
+
+  rbest = n = lg(M0)-1;
+  if (n == 0) { *rr = 0; return NULL; }
+  zc = ZM_count_0_cols(M0);
+  if (n == zc) { *rr = zc; return zero_zv(n); }
+
+  m = nbrows(M0);
+  rmin = (m < n-zc) ? n-m : zc;
+  init_modular(&S);
+  imax = (n < (1<<4))? 1: (n>>3); /* heuristic */
+
+  for(;;)
+  {
+    GEN row, col, M, KM, IM, RHS, X, cX;
+    long rk;
+    for (av = avma, i = 0;; avma = av, i++)
+    {
+      ulong p = u_forprime_next(&S);
+      if (!p) pari_err_OVERFLOW("ZM_pivots [ran out of primes]");
+      d = Flm_gauss_pivot(ZM_to_Flm(M0, p), p, rr);
+      if (*rr == rmin) goto END; /* maximal rank, return */
+      if (*rr < rbest) { /* save best r so far */
+        rbest = *rr;
+        if (dbest) gunclone(dbest);
+        dbest = gclone(d);
+        if (beenthere) break;
+      }
+      if (!beenthere && i >= imax) break;
+    }
+    beenthere = 1;
+    /* Dubious case: there is (probably) a non trivial kernel */
+    indexrank_all(m,n, rbest, dbest, &row, &col);
+    M = rowpermute(vecpermute(M0, col), row);
+    rk = n - rbest; /* (probable) dimension of image */
+    IM = vecslice(M,1,rk);
+    KM = vecslice(M,rk+1, n);
+    M = rowslice(IM, 1,rk); /* square maximal rank */
+    X = ZM_gauss(M, rowslice(KM, 1,rk));
+    X = Q_remove_denom(X, &cX);
+    RHS = rowslice(KM,rk+1,m);
+    if (cX) RHS = ZM_Z_mul(RHS, cX);
+    if (ZM_equal(ZM_mul(rowslice(IM,rk+1,m), X), RHS))
+    {
+      d = vecsmall_copy(dbest);
+      goto END;
+    }
+    avma = av;
+  }
+END:
+  if (dbest) gunclone(dbest);
+  return gerepileuptoleaf(av0, d);
+}
+
+/* set *pr = dim Ker x */
+static GEN
+gauss_pivot(GEN x, long *pr) {
+  GEN data;
+  pivot_fun pivot = get_pivot_fun(x, x, &data);
+  return RgM_pivots(x, data, pr, pivot);
+}
+
+/* compute ker(x), x0 is a reference point when guessing whether x[i,j] ~ 0
+ * (iff x[i,j] << x0[i,j]) */
+static GEN
+ker_aux(GEN x, GEN x0)
+{
+  pari_sp av = avma;
+  GEN d,y;
+  long i,j,k,r,n;
+
+  x = gauss_pivot_ker(x,x0,&d,&r);
+  if (!r) { avma=av; return cgetg(1,t_MAT); }
+  n = lg(x)-1; y=cgetg(r+1,t_MAT);
+  for (j=k=1; j<=r; j++,k++)
+  {
+    GEN p = cgetg(n+1,t_COL);
+
+    gel(y,j) = p; while (d[k]) k++;
+    for (i=1; i<k; i++)
+      if (d[i])
+      {
+        GEN p1=gcoeff(x,d[i],k);
+        gel(p,i) = gcopy(p1); gunclone(p1);
+      }
+      else
+        gel(p,i) = gen_0;
+    gel(p,k) = gen_1; for (i=k+1; i<=n; i++) gel(p,i) = gen_0;
+  }
+  return gerepileupto(av,y);
+}
+GEN
+ker(GEN x)
+{
+  pari_sp av = avma;
+  GEN p = NULL, ff = NULL;
+  if (RgM_is_FpM(x, &p) && p)
+  {
+    ulong pp;
+    x = RgM_Fp_init(x, p, &pp);
+    switch(pp)
+    {
+    case 0: x = FpM_to_mod(FpM_ker_gen(x,p,0),p); break;
+    case 2: x = F2m_to_mod(F2m_ker_sp(x,0)); break;
+    default:x = Flm_to_mod(Flm_ker_sp(x,pp,0), pp); break;
+    }
+    return gerepileupto(av, x);
+  }
+  if (RgM_is_FFM(x, &ff)) return FFM_ker(x, ff);
+  return ker_aux(x,x);
+}
+GEN
+matker0(GEN x,long flag)
+{
+  if (typ(x)!=t_MAT) pari_err_TYPE("matker",x);
+  if (!flag) return ker(x);
+  RgM_check_ZM(x, "keri");
+  return keri(x);
+}
+
+GEN
+image(GEN x)
+{
+  pari_sp av = avma;
+  GEN d, ff = NULL, p = NULL;
+  long r;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("matimage",x);
+  if (RgM_is_FpM(x, &p) && p)
+  {
+    ulong pp;
+    x = RgM_Fp_init(x, p, &pp);
+    switch(pp)
+    {
+    case 0: x = FpM_to_mod(FpM_image(x,p), p); break;
+    case 2: x = F2m_to_mod(F2m_image(x)); break;
+    default:x = Flm_to_mod(Flm_image(x,pp), pp);
+    }
+    return gerepileupto(av, x);
+  }
+  if (RgM_is_FFM(x, &ff)) return FFM_image(x, ff);
+  d = gauss_pivot(x,&r); /* d left on stack for efficiency */
+  return image_from_pivot(x,d,r);
+}
+
+static GEN
+imagecompl_aux(GEN x, GEN(*PIVOT)(GEN,long*))
+{
+  pari_sp av = avma;
+  GEN d,y;
+  long j,i,r;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("imagecompl",x);
+  (void)new_chunk(lg(x) * 4 + 1); /* HACK */
+  d = PIVOT(x,&r); /* if (!d) then r = 0 */
+  avma = av; y = cgetg(r+1,t_VECSMALL);
+  for (i=j=1; j<=r; i++)
+    if (!d[i]) y[j++] = i;
+  return y;
+}
+GEN
+imagecompl(GEN x) { return imagecompl_aux(x, &gauss_pivot); }
+GEN
+ZM_imagecompl(GEN x) { return imagecompl_aux(x, &ZM_pivots); }
+
+/* permutation giving imagecompl(x') | image(x'), x' = transpose of x */
+static GEN
+imagecomplspec_aux(GEN x, long *nlze, GEN(*PIVOT)(GEN,long*))
+{
+  pari_sp av = avma;
+  GEN d,y;
+  long i,j,k,l,r;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("imagecomplspec",x);
+  x = shallowtrans(x); l = lg(x);
+  d = PIVOT(x,&r);
+  *nlze = r;
+  avma = av; /* HACK: shallowtrans(x) big enough to avoid overwriting d */
+  if (!d) return identity_perm(l-1);
+  y = cgetg(l,t_VECSMALL);
+  for (i=j=1, k=r+1; i<l; i++)
+    if (d[i]) y[k++]=i; else y[j++]=i;
+  return y;
+}
+GEN
+imagecomplspec(GEN x, long *nlze)
+{ return imagecomplspec_aux(x,nlze,&gauss_pivot); }
+GEN
+ZM_imagecomplspec(GEN x, long *nlze)
+{ return imagecomplspec_aux(x,nlze,&ZM_pivots); }
+
+GEN
+RgM_RgC_invimage(GEN A, GEN y)
+{
+  pari_sp av = avma;
+  long i, l = lg(A);
+  GEN M, x, t, p = NULL;
+
+  if (RgM_is_FpM(A, &p) && RgV_is_FpV(y, &p) && p)
+  {
+    ulong pp;
+    A = RgM_Fp_init(A,p,&pp);
+    switch(pp)
+    {
+    case 0:
+      y = RgC_to_FpC(y,p);
+      x = FpM_FpC_invimage(A, y, p);
+      if (x) x = FpC_to_mod(x,p);
+      break;
+    case 2:
+      y = RgV_to_F2v(y);
+      x = F2m_F2c_invimage(A, y);
+      if (x) x = F2c_to_mod(x);
+      break;
+    default:
+      y = RgC_to_Flc(y,pp);
+      x = Flm_Flc_invimage(A, y, pp);
+      if (x) x = Flc_to_mod(x,pp);
+    }
+    if (!x) { avma = av; return NULL; }
+    return gerepileupto(av, x);
+  }
+
+  if (l==1) return NULL;
+  if (lg(y) != lgcols(A)) pari_err_DIM("inverseimage");
+  M = ker(shallowconcat(A, y));
+  i = lg(M)-1;
+  if (!i) { avma = av; return NULL; }
+
+  x = gel(M,i); t = gel(x,l);
+  if (gequal0(t)) { avma = av; return NULL; }
+
+  t = gneg_i(t); setlg(x,l);
+  return gerepileupto(av, RgC_Rg_div(x, t));
+}
+GEN
+FpM_FpC_invimage(GEN A, GEN y, GEN p)
+{
+  pari_sp av = avma;
+  long i, l = lg(A);
+  GEN M, x, t;
+
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    A = ZM_to_Flm(A, pp);
+    y = ZV_to_Flv(y, pp);
+    x = Flm_Flc_invimage(A,y,pp);
+    if (!x) { avma = av; return NULL; }
+    return gerepileupto(av, Flc_to_ZC(x));
+  }
+  if (l==1) return NULL;
+  if (lg(y) != lgcols(A)) pari_err_DIM("FpM_FpC_invimage");
+  M = FpM_ker(shallowconcat(A,y),p);
+  i = lg(M)-1; if (!i) { avma = av; return NULL; }
+
+  x = gel(M,i); t = gel(x,l);
+  if (!signe(t)) { avma = av; return NULL; }
+
+  setlg(x,l); t = Fp_inv(negi(t),p);
+  if (is_pm1(t)) return gerepilecopy(av, x);
+  return gerepileupto(av, FpC_Fp_mul(x, t, p));
+}
+GEN
+Flm_Flc_invimage(GEN A, GEN y, ulong p)
+{
+  pari_sp av = avma;
+  long i, l = lg(A);
+  GEN M, x;
+  ulong t;
+
+  if (l==1) return NULL;
+  if (lg(y) != lgcols(A)) pari_err_DIM("Flm_Flc_invimage");
+  M = cgetg(l+1,t_MAT);
+  for (i=1; i<l; i++) gel(M,i) = gel(A,i);
+  gel(M,l) = y; M = Flm_ker(M,p);
+  i = lg(M)-1; if (!i) { avma = av; return NULL; }
+
+  x = gel(M,i); t = x[l];
+  if (!t) { avma = av; return NULL; }
+
+  setlg(x,l); t = Fl_inv(Fl_neg(t,p),p);
+  if (t!=1) x = Flc_Fl_mul(x, t, p);
+  return gerepileuptoleaf(av, x);
+}
+GEN
+F2m_F2c_invimage(GEN A, GEN y)
+{
+  pari_sp av = avma;
+  long i, l = lg(A);
+  GEN M, x;
+
+  if (l==1) return NULL;
+  if (lg(y) != lgcols(A)) pari_err_DIM("F2m_F2c_invimage");
+  M = cgetg(l+1,t_MAT);
+  for (i=1; i<l; i++) gel(M,i) = gel(A,i);
+  gel(M,l) = y; M = F2m_ker(M);
+  i = lg(M)-1; if (!i) { avma = av; return NULL; }
+
+  x = gel(M,i);
+  if (!F2v_coeff(x,l)) { avma = av; return NULL; }
+  x[1]--; /* remove last coord */
+  return gerepileuptoleaf(av, x);
+}
+
+/* Return X such that m X = v (t_COL or t_MAT), resp. an empty t_COL / t_MAT
+ * if no solution exist */
+GEN
+inverseimage(GEN m, GEN v)
+{
+  GEN y;
+  if (typ(m)!=t_MAT) pari_err_TYPE("inverseimage",m);
+  switch(typ(v))
+  {
+    case t_COL:
+      y = RgM_RgC_invimage(m,v);
+      return y? y: cgetg(1,t_COL);
+    case t_MAT:
+      y = RgM_invimage(m, v);
+      return y? y: cgetg(1,t_MAT);
+  }
+  pari_err_TYPE("inverseimage",v);
+  return NULL;/*not reached*/
+}
+
+static GEN
+Flm_invimage_i(GEN A, GEN B, ulong p)
+{
+  GEN d, x, X, Y;
+  long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
+  x = Flm_ker_sp(shallowconcat(Flm_neg(A,p), B), p, 0);
+  /* AX = BY, Y in strict upper echelon form with pivots = 1.
+   * We must find T such that Y T = Id_nB then X T = Z. This exists iff
+   * Y has at least nB columns and full rank */
+  nY = lg(x)-1;
+  if (nY < nB) return NULL;
+  Y = rowslice(x, nA+1, nA+nB); /* nB rows */
+  d = cgetg(nB+1, t_VECSMALL);
+  for (i = nB, j = nY; i >= 1; i--)
+  {
+    for (; j>=1; j--)
+      if (coeff(Y,i,j)) { d[i] = j; break; }
+    if (!j) return NULL;
+  }
+  /* reduce to the case Y square, upper triangular with 1s on diagonal */
+  Y = vecpermute(Y, d);
+  x = vecpermute(x, d);
+  X = rowslice(x, 1, nA);
+  return Flm_mul(X, Flm_inv_upper_1(Y,p), p);
+}
+
+static GEN
+F2m_invimage_i(GEN A, GEN B)
+{
+  GEN d, x, X, Y;
+  long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
+  x = F2m_ker_sp(shallowconcat(A, B), 0);
+  /* AX = BY, Y in strict upper echelon form with pivots = 1.
+   * We must find T such that Y T = Id_nB then X T = Z. This exists iff
+   * Y has at least nB columns and full rank */
+  nY = lg(x)-1;
+  if (nY < nB) return NULL;
+
+  /* implicitly: Y = rowslice(x, nA+1, nA+nB), nB rows */
+  d = cgetg(nB+1, t_VECSMALL);
+  for (i = nB, j = nY; i >= 1; i--)
+  {
+    for (; j>=1; j--)
+      if (F2m_coeff(x,nA+i,j)) { d[i] = j; break; } /* Y[i,j] */
+    if (!j) return NULL;
+  }
+  x = vecpermute(x, d);
+
+  X = F2m_rowslice(x, 1, nA);
+  Y = F2m_rowslice(x, nA+1, nA+nB);
+  return F2m_mul(X, F2m_inv_upper_1(Y));
+}
+GEN
+Flm_invimage(GEN A, GEN B, ulong p)
+{
+  pari_sp av = avma;
+  GEN X = Flm_invimage_i(A,B,p);
+  if (!X) { avma = av; return NULL; }
+  return gerepileupto(av, X);
+}
+GEN
+F2m_invimage(GEN A, GEN B)
+{
+  pari_sp av = avma;
+  GEN X = F2m_invimage_i(A,B);
+  if (!X) { avma = av; return NULL; }
+  return gerepileupto(av, X);
+}
+static GEN
+FpM_invimage_i(GEN A, GEN B, GEN p)
+{
+  GEN d, x, X, Y;
+  long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    A = ZM_to_Flm(A, pp);
+    B = ZM_to_Flm(B, pp);
+    x = Flm_invimage_i(A, B, pp);
+    return x? Flm_to_ZM(x): NULL;
+  }
+  x = FpM_ker(shallowconcat(ZM_neg(A), B), p);
+  /* AX = BY, Y in strict upper echelon form with pivots = 1.
+   * We must find T such that Y T = Id_nB then X T = Z. This exists iff
+   * Y has at least nB columns and full rank */
+  nY = lg(x)-1;
+  if (nY < nB) return NULL;
+  Y = rowslice(x, nA+1, nA+nB); /* nB rows */
+  d = cgetg(nB+1, t_VECSMALL);
+  for (i = nB, j = nY; i >= 1; i--)
+  {
+    for (; j>=1; j--)
+      if (signe(gcoeff(Y,i,j))) { d[i] = j; break; }
+    if (!j) return NULL;
+  }
+  /* reduce to the case Y square, upper triangular with 1s on diagonal */
+  Y = vecpermute(Y, d);
+  x = vecpermute(x, d);
+  X = rowslice(x, 1, nA);
+  return FpM_mul(X, FpM_inv_upper_1(Y,p), p);
+}
+GEN
+FpM_invimage(GEN A, GEN B, GEN p)
+{
+  pari_sp av = avma;
+  GEN X = FpM_invimage_i(A,B,p);
+  if (!X) { avma = av; return NULL; }
+  return gerepileupto(av, X);
+}
+
+/* find Z such that A Z = B. Return NULL if no solution */
+GEN
+RgM_invimage(GEN A, GEN B)
+{
+  pari_sp av = avma;
+  GEN d, x, X, Y;
+  long i, j, nY, nA = lg(A)-1, nB = lg(B)-1;
+  GEN p = NULL;
+  if (RgM_is_FpM(A, &p) && RgM_is_FpM(B, &p) && p)
+  {
+    ulong pp;
+    A = RgM_Fp_init(A,p,&pp);
+    switch(pp)
+    {
+    case 0:
+      B = RgM_to_FpM(B,p);
+      x = FpM_invimage_i(A, B, p);
+      if (x) x = FpM_to_mod(x, p);
+    break;
+    case 2:
+      B = RgM_to_F2m(B);
+      x = F2m_invimage_i(A, B);
+      if (x) x = F2m_to_mod(x);
+      break;
+    default:
+      B = RgM_to_Flm(B,pp);
+      x = Flm_invimage_i(A, B, pp);
+      if (x) x = Flm_to_mod(x,pp);
+      break;
+    }
+    if (!x) { avma = av; return NULL; }
+    return gerepileupto(av, x);
+  }
+  x = ker(shallowconcat(RgM_neg(A), B));
+  /* AX = BY, Y in strict upper echelon form with pivots = 1.
+   * We must find T such that Y T = Id_nB then X T = Z. This exists iff
+   * Y has at least nB columns and full rank */
+  nY = lg(x)-1;
+  if (nY < nB) { avma = av; return NULL; }
+  Y = rowslice(x, nA+1, nA+nB); /* nB rows */
+  d = cgetg(nB+1, t_VECSMALL);
+  for (i = nB, j = nY; i >= 1; i--)
+  {
+    for (; j>=1; j--)
+      if (!gequal0(gcoeff(Y,i,j))) { d[i] = j; break; }
+    if (!j) { avma = av; return NULL; }
+  }
+  /* reduce to the case Y square, upper triangular with 1s on diagonal */
+  Y = vecpermute(Y, d);
+  x = vecpermute(x, d);
+  X = rowslice(x, 1, nA);
+  return gerepileupto(av, RgM_mul(X, RgM_inv_upper(Y)));
+}
+
+/* r = dim Ker x, n = nbrows(x) */
+static GEN
+get_suppl(GEN x, GEN d, long n, long r, GEN(*ei)(long,long))
+{
+  pari_sp av;
+  GEN y, c;
+  long j, k, rx = lg(x)-1; /* != 0 due to init_suppl() */
+
+  if (rx == n && r == 0) return gcopy(x);
+  y = cgetg(n+1, t_MAT);
+  av = avma; c = zero_zv(n);
+  /* c = lines containing pivots (could get it from gauss_pivot, but cheap)
+   * In theory r = 0 and d[j] > 0 for all j, but why take chances? */
+  for (k = j = 1; j<=rx; j++)
+    if (d[j]) { c[ d[j] ] = 1; gel(y,k++) = gel(x,j); }
+  for (j=1; j<=n; j++)
+    if (!c[j]) gel(y,k++) = (GEN)j; /* HACK */
+  avma = av;
+
+  rx -= r;
+  for (j=1; j<=rx; j++) gel(y,j) = gcopy(gel(y,j));
+  for (   ; j<=n; j++)  gel(y,j) = ei(n, y[j]);
+  return y;
+}
+
+static void
+init_suppl(GEN x)
+{
+  if (lg(x) == 1) pari_err_IMPL("suppl [empty matrix]");
+  /* HACK: avoid overwriting d from gauss_pivot() after avma=av */
+  (void)new_chunk(lgcols(x) * 2);
+}
+
+/* x is an n x k matrix, rank(x) = k <= n. Return an invertible n x n matrix
+ * whose first k columns are given by x. If rank(x) < k, undefined result. */
+GEN
+suppl(GEN x)
+{
+  pari_sp av = avma;
+  GEN d, X = x, p = NULL;
+  long r;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("suppl",x);
+  if (RgM_is_FpM(x, &p) && p)
+  {
+    ulong pp;
+    x = RgM_Fp_init(x, p, &pp);
+    switch(pp)
+    {
+    case 0: x = FpM_to_mod(FpM_suppl(x,p), p); break;
+    case 2: x = F2m_to_mod(F2m_suppl(x)); break;
+    default:x = Flm_to_mod(Flm_suppl(x,pp), pp); break;
+    }
+    return gerepileupto(av, x);
+  }
+  avma = av; init_suppl(x);
+  d = gauss_pivot(X,&r);
+  avma = av; return get_suppl(X,d,nbrows(X),r,&col_ei);
+}
+GEN
+FpM_suppl(GEN x, GEN p)
+{
+  pari_sp av = avma;
+  GEN d;
+  long r;
+  init_suppl(x); d = FpM_gauss_pivot(x,p, &r);
+  avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
+}
+GEN
+Flm_suppl(GEN x, ulong p)
+{
+  pari_sp av = avma;
+  GEN d;
+  long r;
+  init_suppl(x); d = Flm_gauss_pivot(Flm_copy(x),p, &r);
+  avma = av; return get_suppl(x,d,nbrows(x),r,&vecsmall_ei);
+
+}
+GEN
+F2m_suppl(GEN x)
+{
+  pari_sp av = avma;
+  GEN d;
+  long r;
+  init_suppl(x); d = F2m_gauss_pivot(F2m_copy(x), &r);
+  avma = av; return get_suppl(x,d,mael(x,1,1),r,&F2v_ei);
+}
+
+GEN
+FqM_suppl(GEN x, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN d;
+  long r;
+
+  if (!T) return FpM_suppl(x,p);
+  init_suppl(x);
+  d = FqM_gauss_pivot(x,T,p,&r);
+  avma = av; return get_suppl(x,d,nbrows(x),r,&col_ei);
+}
+
+GEN
+image2(GEN x)
+{
+  pari_sp av = avma;
+  long k, n, i;
+  GEN A, B;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("image2",x);
+  if (lg(x) == 1) return cgetg(1,t_MAT);
+  A = ker(x); k = lg(A)-1;
+  if (!k) { avma = av; return gcopy(x); }
+  A = suppl(A); n = lg(A)-1;
+  B = cgetg(n-k+1, t_MAT);
+  for (i = k+1; i <= n; i++) gel(B,i-k) = RgM_RgC_mul(x, gel(A,i));
+  return gerepileupto(av, B);
+}
+
+GEN
+matimage0(GEN x,long flag)
+{
+  switch(flag)
+  {
+    case 0: return image(x);
+    case 1: return image2(x);
+    default: pari_err_FLAG("matimage");
+  }
+  return NULL; /* not reached */
+}
+
+long
+rank(GEN x)
+{
+  pari_sp av = avma;
+  long r;
+  GEN ff = NULL, p = NULL;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("rank",x);
+  if (RgM_is_FpM(x, &p) && p)
+  {
+    ulong pp;
+    x = RgM_Fp_init(x,p,&pp);
+    switch(pp)
+    {
+    case 0: r = FpM_rank(x,p); break;
+    case 2: r = F2m_rank(x); break;
+    default:r = Flm_rank(x,pp); break;
+    }
+    avma = av; return r;
+  }
+  if (RgM_is_FFM(x, &ff)) return FFM_rank(x, ff);
+  (void)gauss_pivot(x, &r);
+  avma = av; return lg(x)-1 - r;
+}
+
+/* d a t_VECSMALL of integers in 1..n. Return the vector of the d[i]
+ * followed by the missing indices */
+static GEN
+perm_complete(GEN d, long n)
+{
+  GEN y = cgetg(n+1, t_VECSMALL);
+  long i, j = 1, k = n, l = lg(d);
+  pari_sp av = avma;
+  char *T = stack_calloc(n+1);
+  for (i = 1; i < l; i++) T[d[i]] = 1;
+  for (i = 1; i <= n; i++)
+    if (T[i]) y[j++] = i; else y[k--] = i;
+  avma = av; return y;
+}
+
+/* n = dim x, r = dim Ker(x), d from gauss_pivot */
+static GEN
+indexrank0(long n, long r, GEN d)
+{
+  GEN p1, p2, res = cgetg(3,t_VEC);
+  long i, j;
+
+  r = n - r; /* now r = dim Im(x) */
+  p1 = cgetg(r+1,t_VECSMALL); gel(res,1) = p1;
+  p2 = cgetg(r+1,t_VECSMALL); gel(res,2) = p2;
+  if (d)
+  {
+    for (i=0,j=1; j<=n; j++)
+      if (d[j]) { i++; p1[i] = d[j]; p2[i] = j; }
+    vecsmall_sort(p1);
+  }
+  return res;
+}
+/* n = dim x, r = dim Ker(x), d from gauss_pivot */
+static GEN
+indeximage0(long n, long r, GEN d)
+{
+  long i, j;
+  GEN v;
+
+  r = n - r; /* now r = dim Im(x) */
+  v = cgetg(r+1,t_VECSMALL);
+  if (d) for (i=j=1; j<=n; j++)
+    if (d[j]) v[i++] = j;
+  return v;
+}
+/* x an m x n t_MAT, n > 0, r = dim Ker(x), d from gauss_pivot */
+static void
+indexrank_all(long m, long n, long r, GEN d, GEN *prow, GEN *pcol)
+{
+  GEN IR = indexrank0(n, r, d);
+  *prow = perm_complete(gel(IR,1), m);
+  *pcol = perm_complete(gel(IR,2), n);
+}
+static void
+init_indexrank(GEN x) {
+  (void)new_chunk(3 + 2*lg(x)); /* HACK */
+}
+
+GEN
+indexrank(GEN x) {
+  pari_sp av = avma;
+  long r;
+  GEN d, p = NULL;
+  if (typ(x)!=t_MAT) pari_err_TYPE("indexrank",x);
+  init_indexrank(x);
+  if (RgM_is_FpM(x, &p) && p)
+  {
+    ulong pp;
+    x = RgM_Fp_init(x,p,&pp);
+    switch(pp)
+    {
+    case 0: d = FpM_gauss_pivot(x,p,&r); break;
+    case 2: d = F2m_gauss_pivot(x,&r); break;
+    default:d = Flm_gauss_pivot(x,pp,&r); break;
+    }
+  }
+  else
+    d = gauss_pivot(x,&r);
+  avma = av; return indexrank0(lg(x)-1, r, d);
+}
+
+GEN
+FpM_indexrank(GEN x, GEN p) {
+  pari_sp av = avma;
+  long r;
+  GEN d;
+  init_indexrank(x);
+  d = FpM_gauss_pivot(x,p,&r);
+  avma = av; return indexrank0(lg(x)-1, r, d);
+}
+GEN
+Flm_indexrank(GEN x, ulong p) {
+  pari_sp av = avma;
+  long r;
+  GEN d;
+  init_indexrank(x);
+  d = Flm_gauss_pivot(Flm_copy(x),p,&r);
+  avma = av; return indexrank0(lg(x)-1, r, d);
+}
+GEN
+F2m_indexrank(GEN x) {
+  pari_sp av = avma;
+  long r;
+  GEN d;
+  init_indexrank(x);
+  d = F2m_gauss_pivot(F2m_copy(x),&r);
+  avma = av; return indexrank0(lg(x)-1, r, d);
+}
+
+GEN
+ZM_indeximage(GEN x) {
+  pari_sp av = avma;
+  long r;
+  GEN d;
+  init_indexrank(x);
+  d = ZM_pivots(x,&r);
+  avma = av; return indeximage0(lg(x)-1, r, d);
+}
+long
+ZM_rank(GEN x) {
+  pari_sp av = avma;
+  long r;
+  (void)ZM_pivots(x,&r);
+  avma = av; return lg(x)-1-r;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                   Structured Elimination                        */
+/*                                                                 */
+/*******************************************************************/
+
+static void
+rem_col(GEN c, long i, GEN iscol, GEN Wrow, long *rcol, long *rrow)
+{
+  long lc = lg(c), k;
+  iscol[i] = 0; (*rcol)--;
+  for (k = 1; k < lc; ++k)
+  {
+    Wrow[c[k]]--;
+    if (Wrow[c[k]]==0) (*rrow)--;
+  }
+}
+
+static void
+rem_singleton(GEN M, GEN iscol, GEN Wrow, long *rcol, long *rrow)
+{
+  long i, j;
+  long nbcol = lg(iscol)-1, last;
+  do
+  {
+    last = 0;
+    for (i = 1; i <= nbcol; ++i)
+      if (iscol[i])
+      {
+        GEN c = gmael(M, i, 1);
+        long lc = lg(c);
+        for (j = 1; j < lc; ++j)
+          if (Wrow[c[j]] == 1)
+          {
+            rem_col(c, i, iscol, Wrow, rcol, rrow);
+            last=1; break;
+          }
+      }
+  } while (last);
+}
+
+static GEN
+fill_wcol(GEN M, GEN iscol, GEN Wrow, long *w, GEN wcol)
+{
+  long nbcol = lg(iscol)-1;
+  long i, j, m, last;
+  GEN per;
+  for (m = 2, last=0; !last ; m++)
+  {
+    for (i = 1; i <= nbcol; ++i)
+    {
+      wcol[i] = 0;
+      if (iscol[i])
+      {
+        GEN c = gmael(M, i, 1);
+        long lc = lg(c);
+        for (j = 1; j < lc; ++j)
+          if (Wrow[c[j]] == m) {  wcol[i]++; last = 1; }
+      }
+    }
+  }
+  per = vecsmall_indexsort(wcol);
+  *w = wcol[per[nbcol]];
+  return per;
+}
+
+/* M is a RgMs with nbrow rows, A a list of row indices.
+   Eliminate rows of M with a single entry that do not belong to A,
+   and the corresponding columns. Also eliminate columns until #colums=#rows.
+   Return pcol and prow:
+   pcol is a map from the new columns indices to the old one.
+   prow is a map from the old rows indices to the new one (0 if removed).
+*/
+
+void
+RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_row)
+{
+  long i,j,k;
+  long nbcol = lg(M)-1, lA = lg(A);
+  GEN prow = cgetg(nbrow+1, t_VECSMALL);
+  GEN pcol = zero_zv(nbcol);
+  pari_sp av = avma;
+  long rcol = nbcol, rrow = 0, imin = nbcol - usqrt(nbcol);
+  GEN iscol = const_vecsmall(nbcol, 1);
+  GEN Wrow  = zero_zv(nbrow);
+  GEN wcol = cgetg(nbcol+1, t_VECSMALL);
+  pari_sp av2=avma;
+  for (i = 1; i <= nbcol; ++i)
+  {
+    GEN F = gmael(M, i, 1);
+    long l = lg(F)-1;
+    for (j = 1; j <= l; ++j)
+      Wrow[F[j]]++;
+  }
+  for (j = 1; j < lA; ++j)
+  {
+    if (Wrow[A[j]] == 0) { *p_col=NULL; return; }
+    Wrow[A[j]] = -1;
+  }
+  for (i = 1; i <= nbrow; ++i)
+    if (Wrow[i])
+      rrow++;
+  rem_singleton(M, iscol, Wrow, &rcol, &rrow);
+  if (rcol<rrow) pari_err_BUG("RgMs_structelim, rcol<rrow");
+  for (; rcol>rrow;)
+  {
+    long w;
+    GEN per = fill_wcol(M, iscol, Wrow, &w, wcol);
+    for (i = nbcol; i>=imin && wcol[per[i]]>=w && rcol>rrow; i--)
+      rem_col(gmael(M, per[i], 1), per[i], iscol, Wrow, &rcol, &rrow);
+    rem_singleton(M, iscol, Wrow, &rcol, &rrow);
+    avma = av2;
+  }
+  for (j = 1, i = 1; i <= nbcol; ++i)
+    if (iscol[i])
+      pcol[j++] = i;
+  setlg(pcol,j);
+  for (k = 1, i = 1; i <= nbrow; ++i)
+    prow[i] = Wrow[i] ? k++: 0;
+  avma = av;
+  *p_col = pcol; *p_row = prow;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                        EIGENVECTORS                             */
+/*   (independent eigenvectors, sorted by increasing eigenvalue)   */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+mateigen(GEN x, long flag, long prec)
+{
+  GEN y, R, T;
+  long k, l, ex, n = lg(x);
+  pari_sp av = avma;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("eigen",x);
+  if (n != 1 && n != lgcols(x)) pari_err_DIM("eigen");
+  if (flag < 0 || flag > 1) pari_err_FLAG("mateigen");
+  if (n == 1)
+  {
+    if (flag) retmkvec2(cgetg(1,t_VEC), cgetg(1,t_MAT));
+    return cgetg(1,t_VEC);
+  }
+  if (n == 2)
+  {
+    if (flag) retmkvec2(mkveccopy(gcoeff(x,1,1)), matid(1));
+    return matid(1);
+  }
+
+  ex = 16 - prec2nbits(prec);
+  T = charpoly(x,0);
+  if (RgX_is_QX(T))
+  {
+    T = Q_primpart(T);
+    (void)ZX_gcd_all(T, ZX_deriv(T),  &T);
+    R = nfrootsQ(T);
+    if (lg(R)-1 < degpol(T))
+    { /* add missing complex roots */
+      GEN r = cleanroots(RgX_div(T, roots_to_pol(R, 0)), prec);
+      settyp(r, t_VEC);
+      R = shallowconcat(R, r);
+    }
+  }
+  else
+  {
+    GEN r1, v = vectrunc_init(lg(T));
+    long e;
+    R = cleanroots(T,prec);
+    r1 = NULL;
+    for (k = 1; k < lg(R); k++)
+    {
+      GEN r2 = gel(R,k), r = grndtoi(r2, &e);
+      if (e < ex) r2 = r;
+      if (r1)
+      {
+        r = gsub(r1,r2);
+        if (gequal0(r) || gexpo(r) < ex) continue;
+      }
+      vectrunc_append(v, r2);
+      r1 = r2;
+    }
+    R = v;
+  }
+  /* R = distinct complex roots of charpoly(x) */
+  l = lg(R); y = cgetg(l, t_VEC);
+  for (k = 1; k < l; k++)
+  {
+    GEN F = ker_aux(RgM_Rg_sub_shallow(x, gel(R,k)), x);
+    long d = lg(F)-1;
+    if (!d) pari_err_PREC("mateigen");
+    gel(y,k) = F;
+    if (flag) gel(R,k) = const_vec(d, gel(R,k));
+  }
+  y = shallowconcat1(y);
+  if (lg(y) > n) pari_err_PREC("mateigen");
+  /* lg(y) < n if x is not diagonalizable */
+  if (flag) y = mkvec2(shallowconcat1(R), y);
+  return gerepilecopy(av,y);
+}
+GEN
+eigen(GEN x, long prec) { return mateigen(x, 0, prec); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                           DETERMINANT                           */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+det0(GEN a,long flag)
+{
+  switch(flag)
+  {
+    case 0: return det(a);
+    case 1: return det2(a);
+    default: pari_err_FLAG("matdet");
+  }
+  return NULL; /* not reached */
+}
+
+/* M a 2x2 matrix, returns det(M) */
+static GEN
+RgM_det2(GEN M)
+{
+  pari_sp av = avma;
+  GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
+  GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
+  return gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
+}
+/* M a 2x2 ZM, returns det(M) */
+static GEN
+ZM_det2(GEN M)
+{
+  pari_sp av = avma;
+  GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2);
+  GEN c = gcoeff(M,2,1), d = gcoeff(M,2,2);
+  return gerepileuptoint(av, subii(mulii(a,d), mulii(b, c)));
+}
+/* M a 3x3 ZM, return det(M) */
+static GEN
+ZM_det3(GEN M)
+{
+  pari_sp av = avma;
+  GEN a = gcoeff(M,1,1), b = gcoeff(M,1,2), c = gcoeff(M,1,3);
+  GEN d = gcoeff(M,2,1), e = gcoeff(M,2,2), f = gcoeff(M,2,3);
+  GEN g = gcoeff(M,3,1), h = gcoeff(M,3,2), i = gcoeff(M,3,3);
+  GEN p1,p2,p3,p4,p5,p6, D;
+  if (!signe(i)) p1 = p5 = gen_0;
+  else
+  {
+    p1 = mulii(mulii(a,e), i);
+    p5 = mulii(mulii(b,d), i);
+  }
+  if (!signe(g)) p2 = p6 = gen_0;
+  else
+  {
+    p2 = mulii(mulii(b,f), g);
+    p6 = mulii(mulii(c,e), g);
+  }
+  if (!signe(h)) p3 = p4 = gen_0;
+  else
+  {
+    p3 = mulii(mulii(c,d), h);
+    p4 = mulii(mulii(a,f), h);
+  }
+  D = subii(addii(p1,addii(p2,p3)), addii(p4,addii(p5,p6)));
+  return gerepileuptoint(av, D);
+}
+
+static GEN
+det_simple_gauss(GEN a, GEN data, pivot_fun pivot)
+{
+  pari_sp av = avma, lim = stack_lim(av,3);
+  long i,j,k, s = 1, nbco = lg(a)-1;
+  GEN p, x = gen_1;
+
+  a = RgM_shallowcopy(a);
+  for (i=1; i<nbco; i++)
+  {
+    k = pivot(a, data, i, NULL);
+    if (k > nbco) return gerepilecopy(av, gcoeff(a,i,i));
+    if (k != i)
+    { /* exchange the lines s.t. k = i */
+      for (j=i; j<=nbco; j++) swap(gcoeff(a,i,j), gcoeff(a,k,j));
+      s = -s;
+    }
+    p = gcoeff(a,i,i);
+
+    x = gmul(x,p);
+    for (k=i+1; k<=nbco; k++)
+    {
+      GEN m = gcoeff(a,i,k);
+      if (gequal0(m)) continue;
+
+      m = gdiv(m,p);
+      for (j=i+1; j<=nbco; j++)
+      {
+        gcoeff(a,j,k) = gsub(gcoeff(a,j,k), gmul(m,gcoeff(a,j,i)));
+        if (low_stack(lim, stack_lim(av,3)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
+          gerepileall(av,2, &a,&x);
+          p = gcoeff(a,i,i);
+          m = gcoeff(a,i,k); m = gdiv(m, p);
+        }
+      }
+    }
+  }
+  if (s < 0) x = gneg_i(x);
+  return gerepileupto(av, gmul(x, gcoeff(a,nbco,nbco)));
+}
+
+GEN
+det2(GEN a)
+{
+  GEN data;
+  pivot_fun pivot;
+  long n = lg(a)-1;
+  if (typ(a)!=t_MAT) pari_err_TYPE("det2",a);
+  if (!n) return gen_1;
+  if (n != nbrows(a)) pari_err_DIM("det2");
+  if (n == 1) return gcopy(gcoeff(a,1,1));
+  if (n == 2) return RgM_det2(a);
+  pivot = get_pivot_fun(a, a, &data);
+  return det_simple_gauss(a, data, pivot);
+}
+
+static GEN
+mydiv(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y);
+  if (tx == ty && tx == t_POL && varn(x) == varn(y)) return RgX_div(x,y);
+  return gdiv(x,y);
+}
+
+/* Assumes a a square t_MAT of dimension n > 0. Returns det(a) using
+ * Gauss-Bareiss. */
+static GEN
+det_bareiss(GEN a)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  long nbco = lg(a)-1,i,j,k,s = 1;
+  GEN p, pprec;
+
+  a = RgM_shallowcopy(a);
+  for (pprec=gen_1,i=1; i<nbco; i++,pprec=p)
+  {
+    GEN ci, ck, m;
+    int diveuc = (gequal1(pprec)==0);
+
+    p = gcoeff(a,i,i);
+    if (gequal0(p))
+    {
+      k=i+1; while (k<=nbco && gequal0(gcoeff(a,i,k))) k++;
+      if (k>nbco) return gerepilecopy(av, p);
+      swap(gel(a,k), gel(a,i)); s = -s;
+      p = gcoeff(a,i,i);
+    }
+    ci = gel(a,i);
+    for (k=i+1; k<=nbco; k++)
+    {
+      ck = gel(a,k); m = gel(ck,i);
+      if (gequal0(m))
+      {
+        if (gequal1(p))
+        {
+          if (diveuc)
+            gel(a,k) = mydiv(gel(a,k), pprec);
+        }
+        else
+          for (j=i+1; j<=nbco; j++)
+          {
+            GEN p1 = gmul(p, gel(ck,j));
+            if (diveuc) p1 = mydiv(p1,pprec);
+            gel(ck,j) = p1;
+          }
+      }
+      else
+      {
+        for (j=i+1; j<=nbco; j++)
+        {
+          pari_sp av2 = avma;
+          GEN p1 = gsub(gmul(p,gel(ck,j)), gmul(m,gel(ci,j)));
+          if (diveuc) p1 = mydiv(p1,pprec);
+          gel(ck,j) = gerepileupto(av2, p1);
+          if (low_stack(lim,stack_lim(av,2)))
+          {
+            if(DEBUGMEM>1) pari_warn(warnmem,"det. col = %ld",i);
+            gerepileall(av,2, &a,&pprec);
+            ci = gel(a,i);
+            ck = gel(a,k); m = gel(ck,i);
+            p = gcoeff(a,i,i);
+          }
+        }
+      }
+    }
+  }
+  p = gcoeff(a,nbco,nbco);
+  p = (s < 0)? gneg(p): gcopy(p);
+  return gerepileupto(av, p);
+}
+
+/* count non-zero entries in col j, at most 'max' of them.
+ * Return their indices */
+static GEN
+col_count_non_zero(GEN a, long j, long max)
+{
+  GEN v = cgetg(max+1, t_VECSMALL);
+  GEN c = gel(a,j);
+  long i, l = lg(a), k = 1;
+  for (i = 1; i < l; i++)
+    if (!gequal0(gel(c,i)))
+    {
+      if (k > max) return NULL; /* fail */
+      v[k++] = i;
+    }
+  setlg(v, k); return v;
+}
+/* count non-zero entries in row i, at most 'max' of them.
+ * Return their indices */
+static GEN
+row_count_non_zero(GEN a, long i, long max)
+{
+  GEN v = cgetg(max+1, t_VECSMALL);
+  long j, l = lg(a), k = 1;
+  for (j = 1; j < l; j++)
+    if (!gequal0(gcoeff(a,i,j)))
+    {
+      if (k > max) return NULL; /* fail */
+      v[k++] = j;
+    }
+  setlg(v, k); return v;
+}
+
+static GEN det_develop(GEN a, long max, double bound);
+/* (-1)^(i+j) a[i,j] * det RgM_minor(a,i,j) */
+static GEN
+coeff_det(GEN a, long i, long j, long max, double bound)
+{
+  GEN c = gcoeff(a, i, j);
+  c = gmul(c, det_develop(RgM_minor(a, i,j), max, bound));
+  if (odd(i+j)) c = gneg(c);
+  return c;
+}
+/* a square t_MAT, 'bound' a rough upper bound for the number of
+ * multiplications we are willing to pay while developing rows/columns before
+ * switching to Gaussian elimination */
+static GEN
+det_develop(GEN M, long max, double bound)
+{
+  pari_sp av = avma;
+  long i,j, n = lg(M)-1, lbest = max+2, best_col = 0, best_row = 0;
+  GEN best = NULL;
+
+  if (bound < 1.) return det_bareiss(M); /* too costly now */
+
+  switch(n)
+  {
+    case 0: return gen_1;
+    case 1: return gcopy(gcoeff(M,1,1));
+    case 2: return RgM_det2(M);
+  }
+  if (max > ((n+2)>>1)) max = (n+2)>>1;
+  for (j = 1; j <= n; j++)
+  {
+    pari_sp av2 = avma;
+    GEN v = col_count_non_zero(M, j, max);
+    long lv;
+    if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
+    if (lv == 1) { avma = av; return gen_0; }
+    if (lv == 2) {
+      avma = av;
+      return gerepileupto(av, coeff_det(M,v[1],j,max,bound));
+    }
+    best = v; lbest = lv; best_col = j;
+  }
+  for (i = 1; i <= n; i++)
+  {
+    pari_sp av2 = avma;
+    GEN v = row_count_non_zero(M, i, max);
+    long lv;
+    if (!v || (lv = lg(v)) >= lbest) { avma = av2; continue; }
+    if (lv == 1) { avma = av; return gen_0; }
+    if (lv == 2) {
+      avma = av;
+      return gerepileupto(av, coeff_det(M,i,v[1],max,bound));
+    }
+    best = v; lbest = lv; best_row = i;
+  }
+  if (best_row)
+  {
+    double d = lbest-1;
+    GEN s = NULL;
+    long k;
+    bound /= d*d*d;
+    for (k = 1; k < lbest; k++)
+    {
+      GEN c = coeff_det(M, best_row, best[k], max, bound);
+      s = s? gadd(s, c): c;
+    }
+    return gerepileupto(av, s);
+  }
+  if (best_col)
+  {
+    double d = lbest-1;
+    GEN s = NULL;
+    long k;
+    bound /= d*d*d;
+    for (k = 1; k < lbest; k++)
+    {
+      GEN c = coeff_det(M, best[k], best_col, max, bound);
+      s = s? gadd(s, c): c;
+    }
+    return gerepileupto(av, s);
+  }
+  return det_bareiss(M);
+}
+
+/* area of parallelogram bounded by (v1,v2) */
+static GEN
+parallelogramarea(GEN v1, GEN v2)
+{ return gsub(gmul(gnorml2(v1), gnorml2(v2)), gsqr(RgV_dotproduct(v1, v2))); }
+
+/* Square of Hadamard bound for det(a), a square matrix.
+ * Slightly improvement: instead of using the column norms, use the area of
+ * the parallelogram formed by pairs of consecutive vectors */
+GEN
+RgM_Hadamard(GEN a)
+{
+  pari_sp av = avma;
+  long n = lg(a)-1, i;
+  GEN B;
+  if (n == 0) return gen_1;
+  if (n == 1) return gsqr(gcoeff(a,1,1));
+  a = RgM_gtofp(a, LOWDEFAULTPREC);
+  B = gen_1;
+  for (i = 1; i <= n/2; i++)
+    B = gmul(B, parallelogramarea(gel(a,2*i-1), gel(a,2*i)));
+  if (odd(n)) B = gmul(B, gnorml2(gel(a, n)));
+  return gerepileuptoint(av, ceil_safe(B));
+}
+
+/* assume dim(a) = n > 0 */
+static GEN
+ZM_det_i(GEN M, long n)
+{
+  const long DIXON_THRESHOLD = 40;
+  pari_sp av = avma, av2;
+  long i;
+  ulong p, compp, Dp = 1;
+  forprime_t S;
+  GEN D, h, q, v, comp;
+  if (n == 1) return icopy(gcoeff(M,1,1));
+  if (n == 2) return ZM_det2(M);
+  if (n == 3) return ZM_det3(M);
+  h = RgM_Hadamard(M);
+  if (!signe(h)) { avma = av; return gen_0; }
+  h = sqrti(h); q = gen_1;
+  init_modular(&S);
+  p = 0; /* -Wall */
+  while( cmpii(q, h) <= 0 && (p = u_forprime_next(&S)) )
+  {
+    av2 = avma; Dp = Flm_det(ZM_to_Flm(M, p), p);
+    avma = av2;
+    if (Dp) break;
+    q = muliu(q, p);
+  }
+  if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
+  if (!Dp) { avma = av; return gen_0; }
+  if (n <= DIXON_THRESHOLD)
+    D = q;
+  else
+  {
+    av2 = avma;
+    v = cgetg(n+1, t_COL);
+    gel(v, 1) = gen_1; /* ensure content(v) = 1 */
+    for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
+    D = Q_denom(ZM_gauss(M, v));
+    if (expi(D) < expi(h) >> 1)
+    { /* First try unlucky, try once more */
+      for (i = 2; i <= n; i++) gel(v, i) = stoi(random_Fl(15) - 7);
+      D = lcmii(D, Q_denom(ZM_gauss(M, v)));
+    }
+    D = gerepileuptoint(av2, D);
+    if (q != gen_1) D = lcmii(D, q);
+  }
+  /* determinant is M multiple of D */
+  h = shifti(divii(h, D), 1);
+
+  compp = Fl_div(Dp, umodiu(D,p), p);
+  comp = Z_init_CRT(compp, p);
+  q = utoipos(p);
+  while (cmpii(q, h) <= 0)
+  {
+    p = u_forprime_next(&S);
+    if (!p) pari_err_OVERFLOW("ZM_det [ran out of primes]");
+    Dp = umodiu(D, p);
+    if (!Dp) continue;
+    av2 = avma;
+    compp = Fl_div(Flm_det(ZM_to_Flm(M, p), p), Dp, p);
+    avma = av2;
+    (void) Z_incremental_CRT(&comp, compp, &q, p);
+  }
+  return gerepileuptoint(av, mulii(comp, D));
+}
+
+static long
+det_init_max(long n)
+{
+  if (n > 100) return 0;
+  if (n > 50) return 1;
+  if (n > 30) return 4;
+  return 7;
+}
+
+GEN
+det(GEN a)
+{
+  long n = lg(a)-1;
+  double B;
+  GEN data, ff = NULL, p = NULL;
+  pivot_fun pivot;
+
+  if (typ(a)!=t_MAT) pari_err_TYPE("det",a);
+  if (!n) return gen_1;
+  if (n != nbrows(a)) pari_err_DIM("det");
+  if (n == 1) return gcopy(gcoeff(a,1,1));
+  if (n == 2) return RgM_det2(a);
+  if (RgM_is_FpM(a, &p))
+  {
+    pari_sp av = avma;
+    ulong pp, d;
+    if (!p) return ZM_det_i(a, n); /* ZM */
+    /* FpM */
+    a = RgM_Fp_init(a,p,&pp);
+    switch(pp)
+    {
+    case 0: return gerepileupto(av, Fp_to_mod(FpM_det(a,p),p)); break;
+    case 2: d = F2m_det(a); break;
+    default:d = Flm_det(a,pp); break;
+    }
+    avma = av; return mkintmodu(d, pp);
+  }
+  if (RgM_is_FFM(a, &ff)) return FFM_det(a, ff);
+  pivot = get_pivot_fun(a, a, &data);
+  if (pivot != gauss_get_pivot_NZ) return det_simple_gauss(a, data, pivot);
+  B = (double)n;
+  return det_develop(a, det_init_max(n), B*B*B);
+}
+
+GEN
+ZM_det(GEN a)
+{
+  long n = lg(a)-1;
+  if (!n) return gen_1;
+  return ZM_det_i(a, n);
+}
+
+/* return a solution of congruence system sum M_{ i,j } X_j = Y_i mod D_i
+ * If ptu1 != NULL, put in *ptu1 a Z-basis of the homogeneous system */
+static GEN
+gaussmoduloall(GEN M, GEN D, GEN Y, GEN *ptu1)
+{
+  pari_sp av = avma;
+  long n, m, j, l, lM;
+  GEN delta, H, U, u1, u2, x;
+
+  if (typ(M)!=t_MAT) pari_err_TYPE("gaussmodulo",M);
+  lM = lg(M);
+  if (lM == 1)
+  {
+    switch(typ(Y))
+    {
+      case t_INT: break;
+      case t_COL: if (lg(Y) != 1) pari_err_DIM("gaussmodulo");
+                  break;
+      default: pari_err_TYPE("gaussmodulo",Y);
+    }
+    switch(typ(D))
+    {
+      case t_INT: break;
+      case t_COL: if (lg(D) != 1) pari_err_DIM("gaussmodulo");
+                  break;
+      default: pari_err_TYPE("gaussmodulo",D);
+    }
+    if (ptu1) *ptu1 = cgetg(1, t_MAT);
+    return gen_0;
+  }
+  n = nbrows(M);
+  switch(typ(D))
+  {
+    case t_COL:
+      if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
+      delta = diagonal_shallow(D); break;
+    case t_INT: delta = scalarmat_shallow(D,n); break;
+    default: pari_err_TYPE("gaussmodulo",D);
+      return NULL; /* not reached */
+  }
+  switch(typ(Y))
+  {
+    case t_INT: Y = const_col(n, Y); break;
+    case t_COL:
+      if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
+      break;
+    default: pari_err_TYPE("gaussmodulo",Y);
+      return NULL; /* not reached */
+  }
+  H = ZM_hnfall(shallowconcat(M,delta), &U, 1);
+  Y = hnf_solve(H,Y); if (!Y) return gen_0;
+  l = lg(H); /* may be smaller than lM if some moduli are 0 */
+  n = l-1;
+  m = lg(U)-1 - n;
+  u1 = cgetg(m+1,t_MAT);
+  u2 = cgetg(n+1,t_MAT);
+  for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
+  U += m;
+  for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
+  /*       (u1 u2)
+   * (M D) (*  * ) = (0 H) */
+  u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
+  Y = ZM_ZC_mul(u2,Y);
+  x = ZC_reducemodmatrix(Y, u1);
+  if (!ptu1) x = gerepileupto(av, x);
+  else
+  {
+    gerepileall(av, 2, &x, &u1);
+    *ptu1 = u1;
+  }
+  return x;
+}
+
+GEN
+matsolvemod0(GEN M, GEN D, GEN Y, long flag)
+{
+  pari_sp av;
+  GEN p1,y;
+
+  if (!flag) return gaussmoduloall(M,D,Y,NULL);
+
+  av=avma; y = cgetg(3,t_VEC);
+  p1 = gaussmoduloall(M,D,Y, (GEN*)y+2);
+  if (p1==gen_0) { avma=av; return gen_0; }
+  gel(y,1) = p1; return y;
+}
+
+GEN
+gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,1); }
+
+GEN
+gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod0(M,D,Y,0); }
diff --git a/src/basemath/alglin2.c b/src/basemath/alglin2.c
new file mode 100644
index 0000000..35a41aa
--- /dev/null
+++ b/src/basemath/alglin2.c
@@ -0,0 +1,1556 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                         LINEAR ALGEBRA                         **/
+/**                         (second part)                          **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+/*******************************************************************/
+/*                                                                 */
+/*                   CHARACTERISTIC POLYNOMIAL                     */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+charpoly0(GEN x, long v, long flag)
+{
+  if (v<0) v = 0;
+  switch(flag)
+  {
+    case 0: return caradj(x,v,NULL);
+    case 1: return caract(x,v);
+    case 2: return carhess(x,v);
+    case 3: return carberkowitz(x,v);
+    case 4:
+      if (typ(x) != t_MAT) pari_err_TYPE("charpoly",x);
+      RgM_check_ZM(x, "charpoly");
+      x = ZM_charpoly(x); setvarn(x, v); return x;
+    case 5:
+      return charpoly(x, v);
+  }
+  pari_err_FLAG("charpoly"); return NULL; /* not reached */
+}
+
+/* characteristic pol. Easy cases. Return NULL in case it's not so easy. */
+static GEN
+easychar(GEN x, long v)
+{
+  pari_sp av;
+  long lx;
+  GEN p1;
+
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_INTMOD:
+    case t_FRAC: case t_PADIC:
+      p1=cgetg(4,t_POL);
+      p1[1]=evalsigne(1) | evalvarn(v);
+      gel(p1,2) = gneg(x); gel(p1,3) = gen_1;
+      return p1;
+
+    case t_COMPLEX: case t_QUAD:
+      p1 = cgetg(5,t_POL);
+      p1[1] = evalsigne(1) | evalvarn(v);
+      gel(p1,2) = gnorm(x); av = avma;
+      gel(p1,3) = gerepileupto(av, gneg(gtrace(x)));
+      gel(p1,4) = gen_1; return p1;
+
+    case t_FFELT: {
+      pari_sp ltop=avma;
+      p1 = FpX_to_mod(FF_charpoly(x), FF_p_i(x));
+      setvarn(p1,v); return gerepileupto(ltop,p1);
+    }
+
+    case t_POLMOD:
+      return RgXQ_charpoly(gel(x,2), gel(x,1), v);
+
+    case t_MAT:
+      lx=lg(x);
+      if (lx==1) return pol_1(v);
+      if (lgcols(x) != lx) break;
+      return NULL;
+  }
+  pari_err_TYPE("easychar",x);
+  return NULL; /* not reached */
+}
+/* compute charpoly by mapping to Fp first, return lift to Z */
+static GEN
+RgM_Fp_charpoly(GEN x, GEN p, long v)
+{
+  GEN T;
+  if (lgefint(p) == 3)
+  {
+    ulong pp = itou(p);
+    T = Flm_charpoly(RgM_to_Flm(x, pp), pp);
+    T = Flx_to_ZX(T);
+  }
+  else
+    T = FpM_charpoly(RgM_to_FpM(x, p), p);
+  setvarn(T, v); return T;
+}
+GEN
+charpoly(GEN x, long v)
+{
+  GEN T, p = NULL;
+  if ((T = easychar(x,v))) return T;
+  if (RgM_is_ZM(x))
+  {
+    T = ZM_charpoly(x);
+    setvarn(T, v);
+  }
+  else if (RgM_is_FpM(x, &p) && BPSW_psp(p))
+  {
+    pari_sp av = avma;
+    T = RgM_Fp_charpoly(x,p,v);
+    T = gerepileupto(av, FpX_to_mod(T,p));
+  }
+  else if (isinexact(x))
+    T = carhess(x, v);
+  else
+    T = carberkowitz(x, v);
+  return T;
+}
+
+/* We possibly worked with an "invalid" polynomial p, satisfying
+ * varn(p) > gvar2(p). Fix this. */
+static GEN
+fix_pol(pari_sp av, GEN p)
+{
+  long w = gvar2(p), v = varn(p);
+  if (w == v) pari_err_PRIORITY("charpoly", p, "=", w);
+  if (varncmp(w,v) < 0) p = gerepileupto(av, poleval(p, pol_x(v)));
+  return p;
+}
+GEN
+caract(GEN x, long v)
+{
+  pari_sp av = avma;
+  GEN  T, C, x_k, Q;
+  long k, n;
+
+  if ((T = easychar(x,v))) return T;
+
+  n = lg(x)-1;
+  if (n == 1) return fix_pol(av, deg1pol(gen_1, gneg(gcoeff(x,1,1)), v));
+
+  x_k = pol_x(v); /* to be modified in place */
+  T = scalarpol(det(x), v); C = utoineg(n); Q = pol_x(v);
+  for (k=1; k<=n; k++)
+  {
+    GEN mk = utoineg(k), d;
+    gel(x_k,2) = mk;
+    d = det(RgM_Rg_add_shallow(x, mk));
+    T = RgX_add(RgX_mul(T, x_k), RgX_Rg_mul(Q, gmul(C, d)));
+    if (k == n) break;
+
+    Q = RgX_mul(Q, x_k);
+    C = diviuexact(mulsi(k-n,C), k+1); /* (-1)^k binomial(n,k) */
+  }
+  return fix_pol(av, RgX_Rg_div(T, mpfact(n)));
+}
+
+/* C = charpoly(x, v) */
+static GEN
+RgM_adj_from_char(GEN x, long v, GEN C)
+{
+  if (varn(C) != v) /* problem with variable priorities */
+  {
+    C = gdiv(gsub(C, gsubst(C, v, gen_0)), pol_x(v));
+    if (odd(lg(x))) C = RgX_neg(C); /* even dimension */
+    return gsubst(C, v, x);
+  }
+  else
+  {
+    C = RgX_shift_shallow(C, -1);
+    if (odd(lg(x))) C = RgX_neg(C); /* even dimension */
+    return RgX_RgM_eval(C, x);
+  }
+}
+/* assume x square matrice */
+static GEN
+mattrace(GEN x)
+{
+  long i, lx = lg(x);
+  GEN t;
+  if (lx < 3) return lx == 1? gen_0: gcopy(gcoeff(x,1,1));
+  t = gcoeff(x,1,1);
+  for (i = 2; i < lx; i++) t = gadd(t, gcoeff(x,i,i));
+  return t;
+}
+static int
+bad_char(GEN q, long n)
+{
+  forprime_t S;
+  ulong p;
+  if (!signe(q)) return 0;
+  (void)u_forprime_init(&S, 2, n);
+  while ((p = u_forprime_next(&S)))
+    if (!umodiu(q, p)) return 1;
+  return 0;
+}
+/* Using traces: return the characteristic polynomial of x (in variable v).
+ * If py != NULL, the adjoint matrix is put there. */
+GEN
+caradj(GEN x, long v, GEN *py)
+{
+  pari_sp av, av0;
+  long i, k, n;
+  GEN T, y, t;
+
+  if ((T = easychar(x, v)))
+  {
+    if (py)
+    {
+      if (typ(x) != t_MAT) pari_err_TYPE("matadjoint",x);
+      *py = cgetg(1,t_MAT);
+    }
+    return T;
+  }
+
+  n = lg(x)-1; av0 = avma;
+  T = cgetg(n+3,t_POL); T[1] = evalsigne(1) | evalvarn(v);
+  gel(T,n+2) = gen_1;
+  if (!n) { if (py) *py = cgetg(1,t_MAT); return T; }
+  av = avma; t = gerepileupto(av, gneg(mattrace(x)));
+  gel(T,n+1) = t;
+  if (n == 1) {
+    T = fix_pol(av0, T);
+    if (py) *py = matid(1); return T;
+  }
+  if (n == 2) {
+    GEN a = gcoeff(x,1,1), b = gcoeff(x,1,2);
+    GEN c = gcoeff(x,2,1), d = gcoeff(x,2,2);
+    av = avma;
+    gel(T,2) = gerepileupto(av, gsub(gmul(a,d), gmul(b,c)));
+    T = fix_pol(av0, T);
+    if (py) {
+      y = cgetg(3, t_MAT);
+      gel(y,1) = mkcol2(gcopy(d), gneg(c));
+      gel(y,2) = mkcol2(gneg(b), gcopy(a));
+      *py = y;
+    }
+    return T;
+  }
+  /* l > 3 */
+  if (bad_char(residual_characteristic(x), n))
+  { /* n! not invertible in base ring */
+    T = charpoly(x, v);
+    if (!py) return gerepileupto(av, T);
+    *py = RgM_adj_from_char(x, v, T);
+    gerepileall(av, 2, &T,py);
+    return T;
+  }
+  av = avma; y = RgM_shallowcopy(x);
+  for (i = 1; i <= n; i++) gcoeff(y,i,i) = gadd(gcoeff(y,i,i), t);
+  for (k = 2; k < n; k++)
+  {
+    GEN y0 = y;
+    y = RgM_mul(y, x);
+    t = gdivgs(mattrace(y), -k);
+    for (i = 1; i <= n; i++) gcoeff(y,i,i) = gadd(gcoeff(y,i,i), t);
+    y = gclone(y);
+    gel(T,n-k+2) = gerepilecopy(av, t); av = avma;
+    if (k > 2) gunclone(y0);
+  }
+  t = gmul(gcoeff(x,1,1),gcoeff(y,1,1));
+  for (i=2; i<=n; i++) t = gadd(t, gmul(gcoeff(x,1,i),gcoeff(y,i,1)));
+  gel(T,2) = gerepileupto(av, gneg(t));
+  T = fix_pol(av0, T);
+  if (py) *py = odd(n)? gcopy(y): RgM_neg(y);
+  gunclone(y); return T;
+}
+
+GEN
+adj(GEN x)
+{
+  GEN y;
+  (void)caradj(x, MAXVARN, &y); return y;
+}
+
+GEN
+adjsafe(GEN x)
+{
+  const long v = MAXVARN;
+  pari_sp av = avma;
+  GEN C;
+  if (typ(x) != t_MAT) pari_err_TYPE("matadjoint",x);
+  if (lg(x) < 3) return gcopy(x);
+  C = charpoly(x,v);
+  return gerepileupto(av, RgM_adj_from_char(x, v, C));
+}
+
+GEN
+matadjoint0(GEN x, long flag)
+{
+  switch(flag)
+  {
+    case 0: return adj(x);
+    case 1: return adjsafe(x);
+  }
+  pari_err_FLAG("matadjoint"); return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       MINIMAL POLYNOMIAL                        */
+/*                                                                 */
+/*******************************************************************/
+
+static GEN
+easymin(GEN x, long v)
+{
+  pari_sp ltop=avma;
+  GEN G, R, dR;
+  if (typ(x)==t_POLMOD && !issquarefree(gel(x,1)))
+    return NULL;
+  R = easychar(x, v);
+  if (!R) return R;
+  dR=RgX_deriv(R);
+  if (!lgpol(dR)) {avma=ltop; return NULL;}
+  G=RgX_gcd(R,dR);
+  G=RgX_Rg_div(G,leading_term(G));
+  return gerepileupto(ltop, RgX_div(R,G));
+}
+
+GEN
+minpoly(GEN x, long v)
+{
+  pari_sp ltop=avma;
+  GEN P;
+  if (v<0) v = 0;
+  if (typ(x)==t_FFELT)
+  {
+      GEN p1 = FpX_to_mod(FF_minpoly(x), FF_p_i(x));
+      setvarn(p1,v); return gerepileupto(ltop,p1);
+  }
+
+  P=easymin(x,v);
+  if (P) return P;
+  if (typ(x)==t_POLMOD)
+  {
+    P = gcopy(RgXQ_minpoly_naive(gel(x,2), gel(x,1)));
+    setvarn(P,v);
+    return gerepileupto(ltop,P);
+  }
+  if (typ(x)!=t_MAT) pari_err_TYPE("minpoly",x);
+  if (lg(x) == 1) return pol_1(v);
+  return gerepilecopy(ltop,gel(matfrobenius(x,1,v),1));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       HESSENBERG FORM                           */
+/*                                                                 */
+/*******************************************************************/
+GEN
+hess(GEN x)
+{
+  pari_sp av = avma, lim;
+  long lx = lg(x), m, i, j;
+
+  if (typ(x) != t_MAT) pari_err_TYPE("hess",x);
+  if (lx == 1) return cgetg(1,t_MAT);
+  if (lgcols(x) != lx) pari_err_DIM("hess");
+
+  x = RgM_shallowcopy(x); lim = stack_lim(av,2);
+  for (m=2; m<lx-1; m++)
+  {
+    GEN t = NULL;
+    for (i=m+1; i<lx; i++) { t = gcoeff(x,i,m-1); if (!gequal0(t)) break; }
+    if (i == lx) continue;
+    for (j=m-1; j<lx; j++) swap(gcoeff(x,i,j), gcoeff(x,m,j));
+    swap(gel(x,i), gel(x,m)); t = ginv(t);
+
+    for (i=m+1; i<lx; i++)
+    {
+      GEN c = gcoeff(x,i,m-1);
+      if (gequal0(c)) continue;
+
+      c = gmul(c,t); gcoeff(x,i,m-1) = gen_0;
+      for (j=m; j<lx; j++)
+        gcoeff(x,i,j) = gsub(gcoeff(x,i,j), gmul(c,gcoeff(x,m,j)));
+      for (j=1; j<lx; j++)
+        gcoeff(x,j,m) = gadd(gcoeff(x,j,m), gmul(c,gcoeff(x,j,i)));
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"hess, m = %ld", m);
+        gerepileall(av,2, &x, &t);
+      }
+    }
+  }
+  return gerepilecopy(av,x);
+}
+
+GEN
+Flm_hess(GEN x, ulong p)
+{
+  long lx = lg(x), m, i, j;
+  if (lx == 1) return cgetg(1,t_MAT);
+  if (lgcols(x) != lx) pari_err_DIM("hess");
+
+  x = Flm_copy(x);
+  for (m=2; m<lx-1; m++)
+  {
+    ulong t = 0;
+    for (i=m+1; i<lx; i++) { t = ucoeff(x,i,m-1); if (t) break; }
+    if (i == lx) continue;
+    for (j=m-1; j<lx; j++) lswap(ucoeff(x,i,j), ucoeff(x,m,j));
+    swap(gel(x,i), gel(x,m)); t = Fl_inv(t, p);
+
+    for (i=m+1; i<lx; i++)
+    {
+      ulong c = ucoeff(x,i,m-1);
+      if (!c) continue;
+
+      c = Fl_mul(c,t,p); ucoeff(x,i,m-1) = 0;
+      for (j=m; j<lx; j++)
+        ucoeff(x,i,j) = Fl_sub(ucoeff(x,i,j), Fl_mul(c,ucoeff(x,m,j), p), p);
+      for (j=1; j<lx; j++)
+        ucoeff(x,j,m) = Fl_add(ucoeff(x,j,m), Fl_mul(c,ucoeff(x,j,i), p), p);
+    }
+  }
+  return x;
+}
+GEN
+FpM_hess(GEN x, GEN p)
+{
+  pari_sp av = avma, lim;
+  long lx = lg(x), m, i, j;
+  if (lx == 1) return cgetg(1,t_MAT);
+  if (lgcols(x) != lx) pari_err_DIM("hess");
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    x = Flm_hess(ZM_to_Flm(x, pp), pp);
+    return gerepileupto(av, Flm_to_ZM(x));
+  }
+  x = RgM_shallowcopy(x); lim = stack_lim(av,2);
+  for (m=2; m<lx-1; m++)
+  {
+    GEN t = NULL;
+    for (i=m+1; i<lx; i++) { t = gcoeff(x,i,m-1); if (signe(t)) break; }
+    if (i == lx) continue;
+    for (j=m-1; j<lx; j++) swap(gcoeff(x,i,j), gcoeff(x,m,j));
+    swap(gel(x,i), gel(x,m)); t = Fp_inv(t, p);
+
+    for (i=m+1; i<lx; i++)
+    {
+      GEN c = gcoeff(x,i,m-1);
+      if (!signe(c)) continue;
+
+      c = Fp_mul(c,t, p); gcoeff(x,i,m-1) = gen_0;
+      for (j=m; j<lx; j++)
+        gcoeff(x,i,j) = Fp_sub(gcoeff(x,i,j), Fp_mul(c,gcoeff(x,m,j),p), p);
+      for (j=1; j<lx; j++)
+        gcoeff(x,j,m) = Fp_add(gcoeff(x,j,m), Fp_mul(c,gcoeff(x,j,i),p), p);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"hess, m = %ld", m);
+        gerepileall(av,2, &x, &t);
+      }
+    }
+  }
+  return gerepilecopy(av,x);
+}
+GEN
+carhess(GEN x, long v)
+{
+  pari_sp av;
+  long lx, r, i;
+  GEN y, H;
+
+  if ((H = easychar(x,v))) return H;
+
+  lx = lg(x); av = avma; y = cgetg(lx+1, t_VEC);
+  gel(y,1) = pol_1(v); H = hess(x);
+  for (r = 1; r < lx; r++)
+  {
+    pari_sp av2 = avma;
+    GEN z, a = gen_1, b = pol_0(v);
+    for (i = r-1; i; i--)
+    {
+      a = gmul(a, gcoeff(H,i+1,i));
+      if (gequal0(a)) break;
+      b = RgX_add(b, RgX_Rg_mul(gel(y,i), gmul(a,gcoeff(H,i,r))));
+    }
+    z = RgX_sub(RgX_shift_shallow(gel(y,r), 1),
+                RgX_Rg_mul(gel(y,r), gcoeff(H,r,r)));
+    gel(y,r+1) = gerepileupto(av2, RgX_sub(z, b)); /* (X - H[r,r])y[r] - b */
+  }
+  return fix_pol(av, gel(y,lx));
+}
+
+GEN
+FpM_charpoly(GEN x, GEN p)
+{
+  pari_sp av = avma;
+  long lx, r, i;
+  GEN y, H;
+
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    y = Flx_to_ZX(Flm_charpoly(ZM_to_Flm(x,pp), pp));
+    return gerepileupto(av, y);
+  }
+  lx = lg(x); y = cgetg(lx+1, t_VEC);
+  gel(y,1) = pol_1(0); H = FpM_hess(x, p);
+  for (r = 1; r < lx; r++)
+  {
+    pari_sp av2 = avma;
+    GEN z, a = gen_1, b = pol_0(0);
+    for (i = r-1; i; i--)
+    {
+      a = Fp_mul(a, gcoeff(H,i+1,i), p);
+      if (!signe(a)) break;
+      b = ZX_add(b, ZX_Z_mul(gel(y,i), Fp_mul(a,gcoeff(H,i,r),p)));
+    }
+    b = FpX_red(b, p);
+    z = FpX_sub(RgX_shift_shallow(gel(y,r), 1),
+                FpX_Fp_mul(gel(y,r), gcoeff(H,r,r), p), p);
+    z = FpX_sub(z,b,p);
+    if (r+1 == lx) { gel(y,lx) = z; break; }
+    gel(y,r+1) = gerepileupto(av2, z); /* (X - H[r,r])y[r] - b */
+  }
+  return gerepileupto(av, gel(y,lx));
+}
+GEN
+Flm_charpoly(GEN x, long p)
+{
+  pari_sp av;
+  long lx, r, i;
+  GEN y, H;
+
+  lx = lg(x); av = avma; y = cgetg(lx+1, t_VEC);
+  gel(y,1) = pol1_Flx(0); H = Flm_hess(x, p);
+  for (r = 1; r < lx; r++)
+  {
+    pari_sp av2 = avma;
+    ulong a = 1;
+    GEN z, b = zero_Flx(0);
+    for (i = r-1; i; i--)
+    {
+      a = Fl_mul(a, ucoeff(H,i+1,i), p);
+      if (!a) break;
+      b = Flx_add(b, Flx_Fl_mul(gel(y,i), Fl_mul(a,ucoeff(H,i,r),p), p), p);
+    }
+    z = Flx_sub(Flx_shift(gel(y,r), 1),
+                Flx_Fl_mul(gel(y,r), ucoeff(H,r,r), p), p);
+    /* (X - H[r,r])y[r] - b */
+    gel(y,r+1) = gerepileuptoleaf(av2, Flx_sub(z, b, p));
+  }
+  return gerepileuptoleaf(av, gel(y,lx));
+}
+
+/* s = max_k binomial(n,k) (kB^2)^(k/2),  B = |M|oo. Return ceil(log2(s)) */
+static double
+charpoly_bound(GEN M)
+{
+  pari_sp av = avma;
+  GEN s = real_0(LOWDEFAULTPREC), bin, B2 = itor(sqri(ZM_supnorm(M)), LOWDEFAULTPREC);
+  long n = lg(M)-1, k;
+  double d;
+  bin = gen_1;
+  for (k = n; k >= (n+1)>>1; k--)
+  {
+    GEN t = mulri(powruhalf(mulur(k, B2), k), bin);
+    if (absr_cmp(t, s) > 0) s = t;
+    bin = diviuexact(muliu(bin, k), n-k+1);
+  }
+  d = dbllog2(s); avma = av; return ceil(d);
+}
+
+GEN
+ZM_charpoly(GEN M)
+{
+  pari_timer T;
+  pari_sp av = avma;
+  long l = lg(M), n = l-1, bit;
+  GEN q = NULL, H = NULL, Hp;
+  forprime_t S;
+  ulong p;
+  if (!n) return pol_1(0);
+
+  bit = (long)charpoly_bound(M) + 1;
+  if (DEBUGLEVEL>5) {
+    err_printf("ZM_charpoly: bit-bound 2^%ld\n", bit);
+    timer_start(&T);
+  }
+  init_modular(&S);
+  while ((p = u_forprime_next(&S)))
+  {
+    Hp = Flm_charpoly(ZM_to_Flm(M, p), p);
+    if (!H)
+    {
+      H = ZX_init_CRT(Hp, p, 0);
+      if (DEBUGLEVEL>5)
+        timer_printf(&T, "charpoly mod %lu, bound = 2^%ld", p, expu(p));
+      if (expu(p) > bit) break;
+      q = utoipos(p);
+    }
+    else
+    {
+      int stable = ZX_incremental_CRT(&H, Hp, &q,p);
+      if (DEBUGLEVEL>5)
+        timer_printf(&T, "charpoly mod %lu (stable=%ld), bound = 2^%ld",
+                     p, stable, expi(q));
+      if (stable && expi(q) > bit) break;
+    }
+  }
+  if (!p) pari_err_OVERFLOW("ZM_charpoly [ran out of primes]");
+  return gerepilecopy(av, H);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*        CHARACTERISTIC POLYNOMIAL (BERKOWITZ'S ALGORITHM)        */
+/*                                                                 */
+/*******************************************************************/
+GEN
+carberkowitz(GEN x, long v)
+{
+  long lx, i, j, k, r;
+  GEN V, S, C, Q;
+  pari_sp av0, av, lim;
+  if ((V = easychar(x,v))) return V;
+  lx = lg(x); av0 = avma; lim = stack_lim(av0,1);
+  V = cgetg(lx+1, t_VEC);
+  S = cgetg(lx+1, t_VEC);
+  C = cgetg(lx+1, t_VEC);
+  Q = cgetg(lx+1, t_VEC);
+  av = avma;
+  gel(C,1) = gen_m1;
+  gel(V,1) = gen_m1;
+  for (i=2;i<=lx; i++) gel(C,i) = gel(Q,i) = gel(S,i) = gel(V,i) = gen_0;
+  gel(V,2) = gcoeff(x,1,1);
+  for (r = 2; r < lx; r++)
+  {
+    pari_sp av2;
+    GEN t;
+
+    for (i = 1; i < r; i++) gel(S,i) = gcoeff(x,i,r);
+    gel(C,2) = gcoeff(x,r,r);
+    for (i = 1; i < r-1; i++)
+    {
+      av2 = avma; t = gmul(gcoeff(x,r,1), gel(S,1));
+      for (j = 2; j < r; j++) t = gadd(t, gmul(gcoeff(x,r,j), gel(S,j)));
+      gel(C,i+2) = gerepileupto(av2, t);
+      for (j = 1; j < r; j++)
+      {
+        av2 = avma; t = gmul(gcoeff(x,j,1), gel(S,1));
+        for (k = 2; k < r; k++) t = gadd(t, gmul(gcoeff(x,j,k), gel(S,k)));
+        gel(Q,j) = gerepileupto(av2, t);
+      }
+      for (j = 1; j < r; j++) gel(S,j) = gel(Q,j);
+    }
+    av2 = avma; t = gmul(gcoeff(x,r,1), gel(S,1));
+    for (j = 2; j < r; j++) t = gadd(t, gmul(gcoeff(x,r,j), gel(S,j)));
+    gel(C,r+1) = gerepileupto(av2, t);
+    if (low_stack(lim, stack_lim(av0,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"carberkowitz");
+      gerepileall(av, 2, &C, &V);
+    }
+    for (i = 1; i <= r+1; i++)
+    {
+      av2 = avma; t = gmul(gel(C,i), gel(V,1));
+      for (j = 2; j <= minss(r,i); j++)
+        t = gadd(t, gmul(gel(C,i+1-j), gel(V,j)));
+      gel(Q,i) = gerepileupto(av2, t);
+    }
+    for (i = 1; i <= r+1; i++) gel(V,i) = gel(Q,i);
+  }
+  V = RgV_to_RgX(vecreverse(V), v); /* not gtopoly: fail if v > gvar(V) */
+  V = odd(lx)? gcopy(V): RgX_neg(V);
+  return fix_pol(av0, V);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            NORMS                                */
+/*                                                                 */
+/*******************************************************************/
+GEN
+gnorm(GEN x)
+{
+  pari_sp av;
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT:  return sqri(x);
+    case t_REAL: return sqrr(x);
+    case t_FRAC: return sqrfrac(x);
+    case t_COMPLEX: av = avma; return gerepileupto(av, cxnorm(x));
+    case t_QUAD:    av = avma; return gerepileupto(av, quadnorm(x));
+
+    case t_POL: case t_SER: case t_RFRAC: av = avma;
+      return gerepileupto(av, greal(gmul(gconj(x),x)));
+
+    case t_FFELT:
+      y = cgetg(3, t_INTMOD);
+      gel(y,1) = FF_p(x);
+      gel(y,2) = FF_norm(x); return y;
+
+    case t_POLMOD:
+    {
+      GEN T = gel(x,1), a = gel(x,2);
+      if (typ(a) != t_POL || varn(a) != varn(T)) return gpowgs(a, degpol(T));
+      return RgXQ_norm(a, T);
+    }
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gnorm(gel(x,i));
+      return y;
+  }
+  pari_err_TYPE("gnorm",x);
+  return NULL; /* not reached */
+}
+
+/* return |q|^2, complex modulus */
+static GEN
+cxquadnorm(GEN q, long prec)
+{
+  GEN X = gel(q,1), c = gel(X,2); /* (1-D)/4, -D/4 */
+  if (signe(c) > 0) return quadnorm(q); /* imaginary */
+  if (!prec) pari_err_TYPE("gnorml2", q);
+  return sqrr(quadtofp(q, prec));
+}
+
+static GEN
+gnorml2_i(GEN x, long prec)
+{
+  pari_sp av, lim;
+  long i, lx;
+  GEN s;
+
+  switch(typ(x))
+  {
+    case t_INT:  return sqri(x);
+    case t_REAL: return sqrr(x);
+    case t_FRAC: return sqrfrac(x);
+    case t_COMPLEX: av = avma; return gerepileupto(av, cxnorm(x));
+    case t_QUAD:    av = avma; return gerepileupto(av, cxquadnorm(x,prec));
+
+    case t_POL: lx = lg(x)-1; x++; break;
+
+    case t_VEC:
+    case t_COL:
+    case t_MAT: lx = lg(x); break;
+
+    default: pari_err_TYPE("gnorml2",x);
+      return NULL; /* not reached */
+  }
+  if (lx == 1) return gen_0;
+  av = avma; lim = stack_lim(av,1);
+  s = gnorml2(gel(x,1));
+  for (i=2; i<lx; i++)
+  {
+    s = gadd(s, gnorml2(gel(x,i)));
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"gnorml2");
+      s = gerepileupto(av, s);
+    }
+  }
+  return gerepileupto(av,s);
+}
+GEN
+gnorml2(GEN x) { return gnorml2_i(x, 0); }
+
+static GEN pnormlp(GEN,GEN,long);
+static GEN
+pnormlpvec(long i0, GEN x, GEN p, long prec)
+{
+  pari_sp av = avma, lim = stack_lim(av,1);
+  long i, lx = lg(x);
+  GEN s = gen_0;
+  for (i=i0; i<lx; i++)
+  {
+    s = gadd(s, pnormlp(gel(x,i),p,prec));
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"gnormlp, i = %ld", i);
+      s = gerepileupto(av, s);
+    }
+  }
+  return s;
+}
+/* (||x||_p)^p */
+static GEN
+pnormlp(GEN x, GEN p, long prec)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: x = mpabs(x); break;
+    case t_FRAC: x = absfrac(x); break;
+    case t_COMPLEX: case t_QUAD: x = gabs(x,prec); break;
+    case t_POL: return pnormlpvec(2, x, p, prec);
+    case t_VEC: case t_COL: case t_MAT: return pnormlpvec(1, x, p, prec);
+    default: pari_err_TYPE("gnormlp",x);
+  }
+  return gpow(x, p, prec);
+}
+
+GEN
+gnormlp(GEN x, GEN p, long prec)
+{
+  pari_sp av = avma;
+  if (!p) return gsupnorm(x, prec);
+  if (gsigne(p) <= 0) pari_err_DOMAIN("normlp", "p", "<=", gen_0, p);
+  if (is_scalar_t(typ(x))) return gabs(x, prec);
+  if (typ(p) == t_INT)
+  {
+    ulong pp = itou_or_0(p);
+    switch(pp)
+    {
+      case 1: return gnorml1(x, prec);
+      case 2: x = gnorml2_i(x, prec); break;
+      default: x = pnormlp(x, p, prec); break;
+    }
+    if (pp && typ(x) == t_INT && Z_ispowerall(x, pp, &x))
+      return gerepileuptoleaf(av, x);
+    if (pp == 2) return gerepileupto(av, gsqrt(x, prec));
+  }
+  else
+    x = pnormlp(x, p, prec);
+  x = gpow(x, ginv(p), prec);
+  return gerepileupto(av, x);
+}
+
+GEN
+gnorml1(GEN x,long prec)
+{
+  pari_sp av = avma;
+  long lx,i;
+  GEN s;
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: return mpabs(x);
+    case t_FRAC: return absfrac(x);
+
+    case t_COMPLEX: case t_QUAD:
+      return gabs(x,prec);
+
+    case t_POL:
+      lx = lg(x); s = gen_0;
+      for (i=2; i<lx; i++) s = gadd(s, gnorml1(gel(x,i),prec));
+      break;
+
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x); s = gen_0;
+      for (i=1; i<lx; i++) s = gadd(s, gnorml1(gel(x,i),prec));
+      break;
+
+    default: pari_err_TYPE("gnorml1",x);
+      return NULL; /* not reached */
+  }
+  return gerepileupto(av, s);
+}
+/* As gnorml1, except for t_QUAD and t_COMPLEX: |x + wy| := |x| + |y|
+ * Still a norm of R-vector spaces, and can be cheaply computed without
+ * square roots */
+GEN
+gnorml1_fake(GEN x)
+{
+  pari_sp av = avma;
+  long lx, i;
+  GEN s;
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: return mpabs(x);
+    case t_FRAC: return absfrac(x);
+
+    case t_COMPLEX:
+      s = gadd(gnorml1_fake(gel(x,1)), gnorml1_fake(gel(x,2)));
+      break;
+    case t_QUAD:
+      s = gadd(gnorml1_fake(gel(x,2)), gnorml1_fake(gel(x,3)));
+      break;
+
+    case t_POL:
+      lx = lg(x); s = gen_0;
+      for (i=2; i<lx; i++) s = gadd(s, gnorml1_fake(gel(x,i)));
+      break;
+
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x); s = gen_0;
+      for (i=1; i<lx; i++) s = gadd(s, gnorml1_fake(gel(x,i)));
+      break;
+
+    default: pari_err_TYPE("gnorml1_fake",x);
+      return NULL; /* not reached */
+  }
+  return gerepileupto(av, s);
+}
+
+static void
+store(GEN z, GEN *m) { if (!*m || gcmp(z, *m) > 0) *m = z; }
+/* compare |x| to *m or |x|^2 to *msq, whichever is easiest, and update
+ * the pointed value if x is larger */
+void
+gsupnorm_aux(GEN x, GEN *m, GEN *msq, long prec)
+{
+  long i, lx;
+  GEN z;
+  switch(typ(x))
+  {
+    case t_COMPLEX: z = cxnorm(x); store(z, msq); return;
+    case t_QUAD:  z = cxquadnorm(x,prec); store(z, msq); return;
+    case t_INT: case t_REAL: z = mpabs(x); store(z,m); return;
+    case t_FRAC: z = absfrac(x); store(z,m); return;
+
+    case t_POL: lx = lg(x)-1; x++; break;
+
+    case t_VEC:
+    case t_COL:
+    case t_MAT: lx = lg(x); break;
+
+    default: pari_err_TYPE("gsupnorm",x);
+      return; /* not reached */
+  }
+  for (i=1; i<lx; i++) gsupnorm_aux(gel(x,i), m, msq, prec);
+}
+GEN
+gsupnorm(GEN x, long prec)
+{
+  GEN m = NULL, msq = NULL;
+  pari_sp av = avma;
+  gsupnorm_aux(x, &m, &msq, prec);
+  /* now set m = max (m, sqrt(msq)) */
+  if (msq) {
+    msq = gsqrt(msq, prec);
+    if (!m || gcmp(m, msq) < 0) m = msq;
+  } else if (!m) m = gen_0;
+  return gerepilecopy(av, m);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            TRACES                               */
+/*                                                                 */
+/*******************************************************************/
+GEN
+matcompanion(GEN x)
+{
+  long n = degpol(x), j;
+  GEN y, c;
+
+  if (typ(x)!=t_POL) pari_err_TYPE("matcompanion",x);
+  if (!signe(x)) pari_err_DOMAIN("matcompanion","polynomial","=",gen_0,x);
+  if (n == 0) return cgetg(1, t_MAT);
+
+  y = cgetg(n+1,t_MAT);
+  for (j=1; j < n; j++) gel(y,j) = col_ei(n, j+1);
+  c = cgetg(n+1,t_COL); gel(y,n) = c;
+  if (gequal1(gel(x, n+2)))
+    for (j=1; j<=n; j++) gel(c,j) = gneg(gel(x,j+1));
+  else
+  { /* not monic. Hardly ever used */
+    pari_sp av = avma;
+    GEN d = gclone(gneg(gel(x,n+2)));
+    avma = av;
+    for (j=1; j<=n; j++) gel(c,j) = gdiv(gel(x,j+1), d);
+    gunclone(d);
+  }
+  return y;
+}
+
+GEN
+gtrace(GEN x)
+{
+  pari_sp av;
+  long i, lx, tx = typ(x);
+  GEN y, z;
+
+  switch(tx)
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return gmul2n(x,1);
+
+    case t_COMPLEX:
+      return gmul2n(gel(x,1),1);
+
+    case t_QUAD:
+      y = gel(x,1);
+      if (!gequal0(gel(y,3)))
+      { /* assume quad. polynomial is either x^2 + d or x^2 - x + d */
+        av = avma;
+        return gerepileupto(av, gadd(gel(x,3), gmul2n(gel(x,2),1)));
+      }
+      return gmul2n(gel(x,2),1);
+
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gtrace(gel(x,i));
+      return normalizepol_lg(y, lx);
+
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gtrace(gel(x,i));
+      return normalize(y);
+
+    case t_POLMOD:
+      y = gel(x,1); z = gel(x,2);
+      if (typ(z) != t_POL || varn(y) != varn(z)) return gmulsg(degpol(y), z);
+      av = avma;
+      return gerepileupto(av, quicktrace(z, polsym(y, degpol(y)-1)));
+
+    case t_FFELT:
+      y=cgetg(3, t_INTMOD);
+      gel(y,1) = FF_p(x);
+      gel(y,2) = FF_trace(x);
+      return y;
+
+
+    case t_RFRAC:
+      return gadd(x, gconj(x));
+
+    case t_VEC: case t_COL:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gtrace(gel(x,i));
+      return y;
+
+    case t_MAT:
+      lx = lg(x); if (lx == 1) return gen_0;
+      /*now lx >= 2*/
+      if (lx != lgcols(x)) pari_err_DIM("gtrace");
+      av = avma; return gerepileupto(av, mattrace(x));
+  }
+  pari_err_TYPE("gtrace",x);
+  return NULL; /* not reached */
+}
+
+/* Cholesky decomposition for positive definite matrix a
+ * [GTM138, Algo 2.7.6, matrix Q]
+ * If a is not positive definite return NULL. */
+GEN
+qfgaussred_positive(GEN a)
+{
+  pari_sp av = avma, lim=stack_lim(av,1);
+  GEN b;
+  long i,j,k, n = lg(a);
+
+  if (typ(a)!=t_MAT) pari_err_TYPE("qfgaussred_positive",a);
+  if (n == 1) return cgetg(1, t_MAT);
+  if (lgcols(a)!=n) pari_err_DIM("qfgaussred_positive");
+  b = cgetg(n,t_MAT);
+  for (j=1; j<n; j++)
+  {
+    GEN p1=cgetg(n,t_COL), p2=gel(a,j);
+
+    gel(b,j) = p1;
+    for (i=1; i<=j; i++) gel(p1,i) = gel(p2,i);
+    for (   ; i<n ; i++) gel(p1,i) = gen_0;
+  }
+  for (k=1; k<n; k++)
+  {
+    GEN bk, p = gcoeff(b,k,k), invp;
+    if (gsigne(p)<=0) { avma = av; return NULL; } /* not positive definite */
+    invp = ginv(p);
+    bk = row(b, k);
+    for (i=k+1; i<n; i++) gcoeff(b,k,i) = gmul(gel(bk,i), invp);
+    for (i=k+1; i<n; i++)
+    {
+      GEN c = gel(bk, i);
+      for (j=i; j<n; j++)
+        gcoeff(b,i,j) = gsub(gcoeff(b,i,j), gmul(c,gcoeff(b,k,j)));
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"qfgaussred_positive");
+      b=gerepilecopy(av,b);
+    }
+  }
+  return gerepilecopy(av,b);
+}
+
+/* Maximal pivot strategy: x is a suitable pivot if it is non zero and either
+ * - an exact type, or
+ * - it is maximal among remaining non-zero (t_REAL) pivots */
+static int
+suitable(GEN x, long k, GEN *pp, long *pi)
+{
+  long t = typ(x);
+  switch(t)
+  {
+    case t_INT: return signe(x) != 0;
+    case t_FRAC: return 1;
+    case t_REAL: {
+      GEN p = *pp;
+      if (signe(x) && (!p || absr_cmp(p, x) < 0)) { *pp = x; *pi = k; }
+      return 0;
+    }
+    default: return !gequal0(x);
+  }
+}
+
+/* Gauss reduction (arbitrary symetric matrix, only the part above the
+ * diagonal is considered). If signature is non-zero, return only the
+ * signature, in which case gsigne() should be defined for elements of a. */
+static GEN
+gaussred(GEN a, long signature)
+{
+  GEN r, ak, al;
+  pari_sp av, av1, lim;
+  long n = lg(a), i, j, k, l, sp, sn, t;
+
+  if (typ(a) != t_MAT) pari_err_TYPE("gaussred",a);
+  if (n == 1) return signature? mkvec2(gen_0, gen_0): cgetg(1, t_MAT);
+  if (lgcols(a) != n) pari_err_DIM("gaussred");
+  n--;
+
+  av = avma;
+  r = const_vecsmall(n, 1);
+  av1= avma; lim = stack_lim(av1,1);
+  a = RgM_shallowcopy(a);
+  t = n; sp = sn = 0;
+  while (t)
+  {
+    long pind = 0;
+    GEN invp, p = NULL;
+    k=1; while (k<=n && (!r[k] || !suitable(gcoeff(a,k,k), k, &p, &pind))) k++;
+    if (k > n && p) k = pind;
+    if (k <= n)
+    {
+      p = gcoeff(a,k,k); invp = ginv(p); /* != 0 */
+      if (signature) { /* skip if (!signature): gsigne may fail ! */
+        if (gsigne(p) > 0) sp++; else sn++;
+      }
+      r[k] = 0; t--;
+      ak = row(a, k);
+      for (i=1; i<=n; i++)
+        gcoeff(a,k,i) = r[i]? gmul(gcoeff(a,k,i), invp): gen_0;
+
+      for (i=1; i<=n; i++) if (r[i])
+      {
+        GEN c = gel(ak,i); /* - p * a[k,i] */
+        if (gequal0(c)) continue;
+        for (j=1; j<=n; j++) if (r[j])
+          gcoeff(a,i,j) = gsub(gcoeff(a,i,j), gmul(c,gcoeff(a,k,j)));
+      }
+      gcoeff(a,k,k) = p;
+    }
+    else
+    { /* all remaining diagonal coeffs are currently 0 */
+      for (k=1; k<=n; k++) if (r[k])
+      {
+        l=k+1; while (l<=n && (!r[l] || !suitable(gcoeff(a,k,l), l, &p, &pind))) l++;
+        if (l > n && p) l = pind;
+        if (l > n) continue;
+
+        p = gcoeff(a,k,l); invp = ginv(p);
+        sp++; sn++;
+        r[k] = r[l] = 0; t -= 2;
+        ak = row(a, k);
+        al = row(a, l);
+        for (i=1; i<=n; i++) if (r[i])
+        {
+          gcoeff(a,k,i) = gmul(gcoeff(a,k,i), invp);
+          gcoeff(a,l,i) = gmul(gcoeff(a,l,i), invp);
+        } else {
+          gcoeff(a,k,i) = gen_0;
+          gcoeff(a,l,i) = gen_0;
+        }
+
+        for (i=1; i<=n; i++) if (r[i])
+        { /* c = a[k,i] * p, d = a[l,i] * p; */
+          GEN c = gel(ak,i), d = gel(al,i);
+          for (j=1; j<=n; j++) if (r[j])
+            gcoeff(a,i,j) = gsub(gcoeff(a,i,j),
+                                 gadd(gmul(gcoeff(a,l,j), c),
+                                      gmul(gcoeff(a,k,j), d)));
+        }
+        for (i=1; i<=n; i++) if (r[i])
+        {
+          GEN c = gcoeff(a,k,i), d = gcoeff(a,l,i);
+          gcoeff(a,k,i) = gadd(c, d);
+          gcoeff(a,l,i) = gsub(c, d);
+        }
+        gcoeff(a,k,l) = gen_1;
+        gcoeff(a,l,k) = gen_m1;
+        gcoeff(a,k,k) = gmul2n(p,-1);
+        gcoeff(a,l,l) = gneg(gcoeff(a,k,k));
+        if (low_stack(lim, stack_lim(av1,1)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"gaussred");
+          a = gerepilecopy(av1, a);
+        }
+        break;
+      }
+      if (k > n) break;
+    }
+  }
+  if (!signature) return gerepilecopy(av, a);
+  avma = av; return mkvec2s(sp, sn);
+}
+
+GEN
+qfgaussred(GEN a) { return gaussred(a,0); }
+
+GEN
+qfsign(GEN a) { return gaussred(a,1); }
+
+/* x -= s(y+u*x) */
+/* y += s(x-u*y), simultaneously */
+static void
+rot(GEN x, GEN y, GEN s, GEN u) {
+  GEN x1 = subrr(x, mulrr(s,addrr(y,mulrr(u,x))));
+  GEN y1 = addrr(y, mulrr(s,subrr(x,mulrr(u,y))));
+  affrr(x1,x);
+  affrr(y1,y);
+}
+
+/* Diagonalization of a REAL symetric matrix. Return a vector [L, r]:
+ * L = vector of eigenvalues
+ * r = matrix of eigenvectors */
+GEN
+jacobi(GEN a, long prec)
+{
+  pari_sp av1;
+  long de, e, e1, e2, i, j, p, q, l = lg(a);
+  GEN c, ja, L, r, L2, r2, unr;
+
+  if (typ(a) != t_MAT) pari_err_TYPE("jacobi",a);
+  ja = cgetg(3,t_VEC);
+  L = cgetg(l,t_COL); gel(ja,1) = L;
+  r = cgetg(l,t_MAT); gel(ja,2) = r;
+  if (l == 1) return ja;
+  if (lgcols(a) != l) pari_err_DIM("jacobi");
+
+  e1 = HIGHEXPOBIT-1;
+  for (j=1; j<l; j++)
+  {
+    GEN z = gtofp(gcoeff(a,j,j), prec);
+    gel(L,j) = z;
+    e = expo(z); if (e < e1) e1 = e;
+  }
+  for (j=1; j<l; j++)
+  {
+    gel(r,j) = cgetg(l,t_COL);
+    for (i=1; i<l; i++) gcoeff(r,i,j) = utor(i==j? 1: 0, prec);
+  }
+  av1 = avma;
+
+  e2 = -(long)HIGHEXPOBIT; p = q = 1;
+  c = cgetg(l,t_MAT);
+  for (j=1; j<l; j++)
+  {
+    gel(c,j) = cgetg(j,t_COL);
+    for (i=1; i<j; i++)
+    {
+      GEN z = gtofp(gcoeff(a,i,j), prec);
+      gcoeff(c,i,j) = z;
+      if (!signe(z)) continue;
+      e = expo(z); if (e > e2) { e2 = e; p = i; q = j; }
+    }
+  }
+  a = c; unr = real_1(prec);
+  de = prec2nbits(prec);
+
+ /* e1 = min expo(a[i,i])
+  * e2 = max expo(a[i,j]), i != j */
+  while (e1-e2 < de)
+  {
+    pari_sp av2 = avma;
+    GEN x, y, t, c, s, u;
+    /* compute associated rotation in the plane formed by basis vectors number
+     * p and q */
+    x = subrr(gel(L,q),gel(L,p));
+    if (signe(x))
+    {
+      x = divrr(x, shiftr(gcoeff(a,p,q),1));
+      y = sqrtr(addrr(unr, sqrr(x)));
+      t = invr((signe(x)>0)? addrr(x,y): subrr(x,y));
+    }
+    else
+      y = t = unr;
+    c = sqrtr(addrr(unr,sqrr(t)));
+    s = divrr(t,c);
+    u = divrr(t,addrr(unr,c));
+
+    /* compute successive transforms of a and the matrix of accumulated
+     * rotations (r) */
+    for (i=1;   i<p; i++) rot(gcoeff(a,i,p), gcoeff(a,i,q), s,u);
+    for (i=p+1; i<q; i++) rot(gcoeff(a,p,i), gcoeff(a,i,q), s,u);
+    for (i=q+1; i<l; i++) rot(gcoeff(a,p,i), gcoeff(a,q,i), s,u);
+    y = gcoeff(a,p,q);
+    t = mulrr(t, y); shiftr_inplace(y, -de - 1);
+    x = gel(L,p); subrrz(x,t, x);
+    y = gel(L,q); addrrz(y,t, y);
+    for (i=1; i<l; i++) rot(gcoeff(r,i,p), gcoeff(r,i,q), s,u);
+
+    e2 = -(long)HIGHEXPOBIT; p = q = 1;
+    for (j=1; j<l; j++)
+    {
+      for (i=1; i<j; i++)
+      {
+        GEN z = gcoeff(a,i,j);
+        if (!signe(z)) continue;
+        e = expo(z); if (e > e2) { e2=e; p=i; q=j; }
+      }
+      for (i=j+1; i<l; i++)
+      {
+        GEN z = gcoeff(a,j,i);
+        if (!signe(z)) continue;
+        e = expo(z); if (e > e2) { e2=e; p=j; q=i; }
+      }
+    }
+    avma = av2;
+  }
+  /* sort eigenvalues from smallest to largest */
+  c = indexsort(L);
+  r2 = vecpermute(r, c); for (i=1; i<l; i++) gel(r,i) = gel(r2,i);
+  L2 = vecpermute(L, c); for (i=1; i<l; i++) gel(L,i) = gel(L2,i);
+  avma = av1; return ja;
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**              MATRICE RATIONNELLE --> ENTIERE                        **/
+/**                                                                     **/
+/*************************************************************************/
+
+GEN
+matrixqz0(GEN x,GEN p)
+{
+  if (typ(x) != t_MAT) pari_err_TYPE("QM_minors_coprime",x);
+  if (!p) return QM_minors_coprime(x,NULL);
+  if (typ(p) != t_INT) pari_err_TYPE("QM_minors_coprime",p);
+  if (signe(p)>=0) return QM_minors_coprime(x,p);
+  if (equaliu(p,1)) return QM_ImZ_hnf(x); /* p = -1 */
+  if (equaliu(p,2)) return QM_ImQ_hnf(x); /* p = -2 */
+  pari_err_FLAG("QM_minors_coprime"); return NULL; /* not reached */
+}
+
+GEN
+QM_minors_coprime(GEN x, GEN D)
+{
+  pari_sp av = avma, av1, lim;
+  long i, j, m, n, lP;
+  GEN P, y;
+
+  n = lg(x)-1; if (!n) return gcopy(x);
+  m = nbrows(x);
+  if (n > m) pari_err_DOMAIN("QM_minors_coprime","n",">",strtoGENstr("m"),x);
+  y = x; x = cgetg(n+1,t_MAT);
+  for (j=1; j<=n; j++)
+  {
+    gel(x,j) = Q_primpart(gel(y,j));
+    RgV_check_ZV(gel(x,j), "QM_minors_coprime");
+  }
+  /* x now a ZM */
+  if (n==m)
+  {
+    if (gequal0(ZM_det(x)))
+      pari_err_DOMAIN("QM_minors_coprime", "rank(A)", "<",stoi(n),x);
+    avma = av; return matid(n);
+  }
+  /* m > n */
+  if (!D || gequal0(D))
+  {
+    pari_sp av2 = avma;
+    D = ZM_detmult(shallowtrans(x));
+    if (is_pm1(D)) { avma = av2; return ZM_copy(x); }
+  }
+  P = gel(Z_factor(D), 1); lP = lg(P);
+  av1 = avma; lim = stack_lim(av1,1);
+  for (i=1; i < lP; i++)
+  {
+    GEN p = gel(P,i), pov2 = shifti(p, -1);
+    for(;;)
+    {
+      GEN N, M = FpM_ker(x, p);
+      long lM = lg(M);
+      if (lM==1) break;
+
+      M = FpM_center(M, p, pov2);
+      N = ZM_Z_divexact(ZM_mul(x,M), p);
+      for (j=1; j<lM; j++)
+      {
+        long k = n; while (!signe(gcoeff(M,k,j))) k--;
+        gel(x,k) = gel(N,j);
+      }
+      if (low_stack(lim, stack_lim(av1,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"QM_minors_coprime, p = %Ps", p);
+        x = gerepilecopy(av1, x); pov2 = shifti(p, -1);
+      }
+    }
+  }
+  return gerepilecopy(av, x);
+}
+
+static GEN
+RgC_Z_mul(GEN A, GEN u)
+{
+  long s = signe(u);
+  if (is_pm1(u)) return s > 0? A: RgC_neg(A);
+  return s? gmul(u,A): zerocol(lg(A)-1);
+}
+
+/* u,v integral, A,B RgC */
+static GEN
+RgC_lincomb(GEN u, GEN v, GEN A, GEN B)
+{
+  if (!signe(u)) return RgC_Z_mul(B,v);
+  if (!signe(v)) return RgC_Z_mul(A,u);
+  return RgC_add(RgC_Z_mul(A,u), RgC_Z_mul(B,v));
+}
+
+/* cf ZC_elem */
+/* zero aj = Aij (!= 0)  using  ak = Aik (maybe 0), via linear combination of
+ * A[j] and A[k] of determinant 1. */
+static void
+QC_elem(GEN aj, GEN ak, GEN A, long j, long k)
+{
+  GEN p1, u, v, d;
+
+  if (gequal0(ak)) { swap(gel(A,j), gel(A,k)); return; }
+  if (typ(aj) == t_INT) {
+    if (typ(ak) != t_INT) { aj = mulii(aj, gel(ak,2)); ak = gel(ak,1); }
+  } else {
+    if (typ(ak) == t_INT) { ak = mulii(ak, gel(aj,2)); aj = gel(aj,1); }
+    else {
+      GEN daj = gel(aj,2), dak = gel(ak,2), D = gcdii(daj, dak);
+      aj = gel(aj,1); ak = gel(ak,1);
+      if (!is_pm1(D)) { daj = diviiexact(daj, D); dak = diviiexact(dak, D); }
+      if (!is_pm1(dak)) aj = mulii(aj, dak);
+      if (!is_pm1(daj)) ak = mulii(ak, daj);
+    }
+  }
+  /* aj,ak were multiplied by their least common denominator */
+
+  d = bezout(aj,ak,&u,&v);
+  /* frequent special case (u,v) = (1,0) or (0,1) */
+  if (!signe(u))
+  { /* ak | aj */
+    GEN c = negi(diviiexact(aj,ak));
+    gel(A,j) = RgC_lincomb(gen_1, c, gel(A,j), gel(A,k));
+    return;
+  }
+  if (!signe(v))
+  { /* aj | ak */
+    GEN c = negi(diviiexact(ak,aj));
+    gel(A,k) = RgC_lincomb(gen_1, c, gel(A,k), gel(A,j));
+    swap(gel(A,j), gel(A,k));
+    return;
+  }
+
+  if (!is_pm1(d)) { aj = diviiexact(aj,d); ak = diviiexact(ak,d); }
+  p1 = gel(A,k);
+  gel(A,k) = RgC_lincomb(u,v, gel(A,j),p1);
+  gel(A,j) = RgC_lincomb(negi(aj),ak, p1,gel(A,j));
+}
+
+static GEN
+QM_imZ_hnf_aux(GEN A)
+{
+  pari_sp av = avma, lim = stack_lim(av,1);
+  long i,j,k,n,m;
+  GEN a;
+
+  n = lg(A);
+  if (n == 1) return cgetg(1,t_MAT);
+  if (n == 2) {
+    GEN c;
+    A = Q_primitive_part(A, &c);
+    if (!c) A = ZM_copy(A); else if ( isintzero(c) ) A = cgetg(1,t_MAT);
+    return A;
+  }
+  m = lgcols(A);
+  for (i=1; i<m; i++)
+  {
+    for (j = k = 1; j<n; j++)
+    {
+      GEN a = gcoeff(A,i,j);
+      if (gequal0(a)) continue;
+
+      k = j+1; if (k == n) k = 1;
+      /* zero a = Aij  using  b = Aik */
+      QC_elem(a, gcoeff(A,i,k), A, j,k);
+    }
+    a = gcoeff(A,i,k);
+    if (!gequal0(a))
+    {
+      a = Q_denom(a);
+      if (!is_pm1(a)) gel(A,k) = RgC_Rg_mul(gel(A,k), a);
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"QM_imZ_hnf_aux");
+      A = gerepilecopy(av,A);
+    }
+  }
+  return ZM_hnf(A);
+}
+
+GEN
+QM_ImZ_hnf(GEN x)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, QM_imZ_hnf_aux( RgM_shallowcopy(x) ));
+}
+
+GEN
+QM_ImQ_hnf(GEN x)
+{
+  pari_sp av = avma, av1, lim;
+  long j,j1,k,m,n;
+  GEN c;
+
+  n = lg(x); if (n==1) return gcopy(x);
+  m = lgcols(x); x = RgM_shallowcopy(x);
+  c = zero_zv(n-1);
+  av1 = avma; lim = stack_lim(av1,1);
+  for (k=1; k<m; k++)
+  {
+    j=1; while (j<n && (c[j] || gequal0(gcoeff(x,k,j)))) j++;
+    if (j==n) continue;
+
+    c[j]=k; gel(x,j) = RgC_Rg_div(gel(x,j),gcoeff(x,k,j));
+    for (j1=1; j1<n; j1++)
+      if (j1!=j)
+      {
+        GEN t = gcoeff(x,k,j1);
+        if (!gequal0(t)) gel(x,j1) = RgC_sub(gel(x,j1), RgC_Rg_mul(gel(x,j),t));
+      }
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"QM_ImQ_hnf");
+      x = gerepilecopy(av1,x);
+    }
+  }
+  return gerepileupto(av, QM_imZ_hnf_aux(x));
+}
+
+GEN
+intersect(GEN x, GEN y)
+{
+  long j, lx = lg(x);
+  pari_sp av;
+  GEN z;
+
+  if (typ(x)!=t_MAT) pari_err_TYPE("intersect",x);
+  if (typ(y)!=t_MAT) pari_err_TYPE("intersect",y);
+  if (lx==1 || lg(y)==1) return cgetg(1,t_MAT);
+
+  av = avma; z = ker(shallowconcat(x,y));
+  for (j=lg(z)-1; j; j--) setlg(z[j], lx);
+  return gerepileupto(av, RgM_mul(x,z));
+}
diff --git a/src/basemath/alglin3.c b/src/basemath/alglin3.c
new file mode 100644
index 0000000..8551c71
--- /dev/null
+++ b/src/basemath/alglin3.c
@@ -0,0 +1,892 @@
+/* Copyright (C) 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                         LINEAR ALGEBRA                         **/
+/**                          (third part)                          **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*                               SUM                               */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+vecsum(GEN v)
+{
+  pari_sp av = avma, lim;
+  long i, l;
+  GEN p;
+  if (!is_vec_t(typ(v)))
+    pari_err_TYPE("vecsum", v);
+  l = lg(v);
+  if (l == 1) return gen_0;
+  p = gel(v,1);
+  if (l == 2) return gcopy(p);
+  lim = stack_lim(av, 2);
+  for (i=2; i<l; i++)
+  {
+    p = gadd(p, gel(v,i));
+    if (low_stack(lim, stack_lim(av, 2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"sum");
+      p = gerepileupto(av, p);
+    }
+  }
+  return gerepileupto(av, p);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         TRANSPOSE                               */
+/*                                                                 */
+/*******************************************************************/
+/* A[x0,]~ */
+static GEN
+row_transpose(GEN A, long x0)
+{
+  long i, lB = lg(A);
+  GEN B  = cgetg(lB, t_COL);
+  for (i=1; i<lB; i++) gel(B, i) = gcoeff(A, x0, i);
+  return B;
+}
+static GEN
+row_transposecopy(GEN A, long x0)
+{
+  long i, lB = lg(A);
+  GEN B  = cgetg(lB, t_COL);
+  for (i=1; i<lB; i++) gel(B, i) = gcopy(gcoeff(A, x0, i));
+  return B;
+}
+
+/* No copy*/
+GEN
+shallowtrans(GEN x)
+{
+  long i, dx, lx;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_VEC: y = leafcopy(x); settyp(y,t_COL); break;
+    case t_COL: y = leafcopy(x); settyp(y,t_VEC); break;
+    case t_MAT:
+      lx = lg(x); if (lx==1) return cgetg(1,t_MAT);
+      dx = lgcols(x); y = cgetg(dx,t_MAT);
+      for (i = 1; i < dx; i++) gel(y,i) = row_transpose(x,i);
+      break;
+    default: pari_err_TYPE("shallowtrans",x); return NULL;
+  }
+  return y;
+}
+
+GEN
+gtrans(GEN x)
+{
+  long i, dx, lx;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_VEC: y = gcopy(x); settyp(y,t_COL); break;
+    case t_COL: y = gcopy(x); settyp(y,t_VEC); break;
+    case t_MAT:
+      lx = lg(x); if (lx==1) return cgetg(1,t_MAT);
+      dx = lgcols(x); y = cgetg(dx,t_MAT);
+      for (i = 1; i < dx; i++) gel(y,i) = row_transposecopy(x,i);
+      break;
+    default: pari_err_TYPE("gtrans",x); return NULL;
+  }
+  return y;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                           EXTRACTION                            */
+/*                                                                 */
+/*******************************************************************/
+
+static long
+str_to_long(char *s, char **pt)
+{
+  long a = atol(s);
+  while (isspace((int)*s)) s++;
+  if (*s == '-' || *s == '+') s++;
+  while (isdigit((int)*s) || isspace((int)*s)) s++;
+  *pt = s; return a;
+}
+
+static int
+get_range(char *s, long *a, long *b, long *cmpl, long lx)
+{
+  long max = lx - 1;
+
+  *a = 1; *b = max;
+  if (*s == '^') { *cmpl = 1; s++; } else *cmpl = 0;
+  if (!*s) return 0;
+  if (*s != '.')
+  {
+    *a = str_to_long(s, &s);
+    if (*a < 0) *a += lx;
+    if (*a<1 || *a>max) return 0;
+  }
+  if (*s == '.')
+  {
+    s++; if (*s != '.') return 0;
+    do s++; while (isspace((int)*s));
+    if (*s)
+    {
+      *b = str_to_long(s, &s);
+      if (*b < 0) *b += lx;
+      if (*b<1 || *b>max || *s) return 0;
+    }
+    return 1;
+  }
+  if (*s) return 0;
+  *b = *a; return 1;
+}
+
+static int
+extract_selector_ok(long lx, GEN L)
+{
+  long i, l;
+  switch (typ(L))
+  {
+    case t_INT: {
+      long maxj;
+      if (!signe(L)) return 1;
+      l = lgefint(L)-1;
+      maxj = BITS_IN_LONG - bfffo(*int_MSW(L));
+      return ((l-2) * BITS_IN_LONG + maxj < lx);
+    }
+    case t_STR: {
+      long first, last, cmpl;
+      return get_range(GSTR(L), &first, &last, &cmpl, lx);
+    }
+    case t_VEC: case t_COL:
+      l = lg(L);
+      for (i=1; i<l; i++)
+      {
+        long j = itos(gel(L,i));
+        if (j>=lx || j<=0) return 0;
+      }
+      return 1;
+    case t_VECSMALL:
+      l = lg(L);
+      for (i=1; i<l; i++)
+      {
+        long j = L[i];
+        if (j>=lx || j<=0) return 0;
+      }
+      return 1;
+  }
+  return 0;
+}
+
+GEN
+shallowextract(GEN x, GEN L)
+{
+  long i,j, tl = typ(L), tx = typ(x), lx = lg(x);
+  GEN y;
+
+  switch(tx)
+  {
+    case t_VEC:
+    case t_COL:
+    case t_MAT:
+    case t_VECSMALL: break;
+    default: pari_err_TYPE("extract",x);
+
+  }
+  if (tl==t_INT)
+  { /* extract components of x as per the bits of mask L */
+    long k, l, ix, iy, maxj;
+    GEN Ld;
+    if (!signe(L)) return cgetg(1,tx);
+    y = new_chunk(lx);
+    l = lgefint(L)-1; ix = iy = 1;
+    maxj = BITS_IN_LONG - bfffo(*int_MSW(L));
+    if ((l-2) * BITS_IN_LONG + maxj >= lx)
+      pari_err_TYPE("vecextract [mask too large]", L);
+    for (k = 2, Ld = int_LSW(L); k < l; k++, Ld = int_nextW(Ld))
+    {
+      ulong B = *Ld;
+      for (j = 0; j < BITS_IN_LONG; j++, B >>= 1, ix++)
+        if (B & 1) y[iy++] = x[ix];
+    }
+    { /* k = l */
+      ulong B = *Ld;
+      for (j = 0; j < maxj; j++, B >>= 1, ix++)
+        if (B & 1) y[iy++] = x[ix];
+    }
+    y[0] = evaltyp(tx) | evallg(iy);
+    return y;
+  }
+  if (tl==t_STR)
+  {
+    char *s = GSTR(L);
+    long first, last, cmpl, d;
+    if (! get_range(s, &first, &last, &cmpl, lx))
+      pari_err_TYPE("vecextract [incorrect range]", L);
+    if (lx == 1) return cgetg(1,tx);
+    d = last - first;
+    if (cmpl)
+    {
+      if (d >= 0)
+      {
+        y = cgetg(lx - (1+d),tx);
+        for (j=1; j<first; j++) gel(y,j) = gel(x,j);
+        for (i=last+1; i<lx; i++,j++) gel(y,j) = gel(x,i);
+      }
+      else
+      {
+        y = cgetg(lx - (1-d),tx);
+        for (j=1,i=lx-1; i>first; i--,j++) gel(y,j) = gel(x,i);
+        for (i=last-1; i>0; i--,j++) gel(y,j) = gel(x,i);
+      }
+    }
+    else
+    {
+      if (d >= 0)
+      {
+        y = cgetg(d+2,tx);
+        for (i=first,j=1; i<=last; i++,j++) gel(y,j) = gel(x,i);
+      }
+      else
+      {
+        y = cgetg(2-d,tx);
+        for (i=first,j=1; i>=last; i--,j++) gel(y,j) = gel(x,i);
+      }
+    }
+    return y;
+  }
+
+  if (is_vec_t(tl))
+  {
+    long ll=lg(L); y=cgetg(ll,tx);
+    for (i=1; i<ll; i++)
+    {
+      j = itos(gel(L,i));
+      if (j<=0) pari_err_COMPONENT("vecextract","<=",gen_0,stoi(j));
+      if (j>=lx) pari_err_COMPONENT("vecextract",">=",stoi(lx),stoi(j));
+      gel(y,i) = gel(x,j);
+    }
+    return y;
+  }
+  if (tl == t_VECSMALL)
+  {
+    long ll=lg(L); y=cgetg(ll,tx);
+    for (i=1; i<ll; i++)
+    {
+      j = L[i];
+      if (j<=0) pari_err_COMPONENT("vecextract","<=",gen_0,stoi(j));
+      if (j>=lx) pari_err_COMPONENT("vecextract",">=",stoi(lx),stoi(j));
+      gel(y,i) = gel(x,j);
+    }
+    return y;
+  }
+  pari_err_TYPE("vecextract [mask]", L);
+  return NULL; /* not reached */
+}
+
+/* does the component selector l select 0 component ? */
+static int
+select_0(GEN l)
+{
+  switch(typ(l))
+  {
+    case t_INT:
+      return (!signe(l));
+    case t_VEC: case t_COL: case t_VECSMALL:
+      return (lg(l) == 1);
+  }
+  return 0;
+}
+
+GEN
+extract0(GEN x, GEN l1, GEN l2)
+{
+  pari_sp av = avma, av2;
+  GEN y;
+  if (! l2)
+  {
+    y = shallowextract(x, l1);
+    if (lg(y) == 1 || typ(y) == t_VECSMALL) return y;
+    av2 = avma;
+    y = gcopy(y);
+  }
+  else
+  {
+    if (typ(x) != t_MAT) pari_err_TYPE("extract",x);
+    y = shallowextract(x,l2);
+    if (select_0(l1)) { avma = av; return zeromat(0, lg(y)-1); }
+    if (lg(y) == 1 && lg(x) > 1)
+    {
+      if (!extract_selector_ok(lgcols(x), l1))
+        pari_err_TYPE("vecextract [incorrect mask]", l1);
+      avma = av; return cgetg(1, t_MAT);
+    }
+    y = shallowextract(shallowtrans(y), l1);
+    av2 = avma;
+    y = gtrans(y);
+  }
+  stackdummy(av, av2);
+  return y;
+}
+
+static long
+vecslice_parse_arg(long lA, long *y1, long *y2, long *skip)
+{
+  *skip=0;
+  if (!*y1)
+  {
+    if (*y2)
+    {
+      if (*y2<0) *y2 += lA;
+      if (*y2<=0 || *y2>=lA)
+        pari_err_DIM("_[..]");
+      *skip=*y2;
+    }
+    *y1 = 1; *y2 = lA-1;
+  }
+  else if (!*y2) *y2 = *y1;
+  if (*y1<0) *y1 += lA;
+  if (*y2<0) *y2 += lA;
+  if (*y1<=0 || *y1>*y2 || *y2>=lA) pari_err_DIM("_[..]");
+  return *y2 - *y1 + 2 - !!*skip;
+}
+
+static GEN
+vecslice_i(GEN A, long t, long lB, long y1, long skip)
+{
+  GEN B = cgetg(lB, t);
+  long i;
+  for (i=1; i<lB; i++, y1++)
+  {
+    if (y1 == skip) { i--; continue; }
+    gel(B,i) = gcopy(gel(A,y1));
+  }
+  return B;
+}
+
+static GEN
+rowslice_i(GEN A, long lB, long x1, long y1, long skip)
+{
+  GEN B = cgetg(lB, t_VEC);
+  long i;
+  for (i=1; i<lB; i++, y1++)
+  {
+    if (y1 == skip) { i--; continue; }
+    gel(B,i) = gcopy(gcoeff(A,x1,y1));
+  }
+  return B;
+}
+
+static GEN
+rowsmallslice_i(GEN A, long lB, long x1, long y1, long skip)
+{
+  GEN B = cgetg(lB, t_VECSMALL);
+  long i;
+  for (i=1; i<lB; i++, y1++)
+  {
+    if (y1 == skip) { i--; continue; }
+    B[i] = coeff(A,x1,y1);
+  }
+  return B;
+}
+
+static GEN
+vecsmallslice_i(GEN A, long t, long lB, long y1, long skip)
+{
+  GEN B = cgetg(lB, t);
+  long i;
+  for (i=1; i<lB; i++, y1++)
+  {
+    if (y1 == skip) { i--; continue; }
+    B[i] = A[y1];
+  }
+  return B;
+}
+GEN
+vecslice0(GEN A, long y1, long y2)
+{
+  long skip, lB, t = typ(A);
+  lB = vecslice_parse_arg(lg(A), &y1, &y2, &skip);
+  switch(t)
+  {
+    case t_VEC: case t_COL:
+      return vecslice_i(A, t,lB,y1,skip);
+    case t_VECSMALL:
+      return vecsmallslice_i(A, t,lB,y1,skip);
+    default:
+      pari_err_TYPE("_[_.._]",A);
+      return NULL;
+  }
+}
+
+GEN
+matslice0(GEN A, long x1, long x2, long y1, long y2)
+{
+  GEN B;
+  long i, lB, lA = lg(A), t, skip, rskip, rlB;
+  long is_col = y1 && !y2, is_row = x1 && !x2;
+  GEN (*slice)(GEN A, long t, long lB, long y1, long skip);
+  if (typ(A)!=t_MAT) pari_err_TYPE("_[_.._,_.._]",A);
+  lB = vecslice_parse_arg(lA, &y1, &y2, &skip);
+  if (is_col) return vecslice0(gel(A, y1), x1, x2);
+
+  /* lA > 1 */
+  rlB = vecslice_parse_arg(lg(gel(A,1)), &x1, &x2, &rskip);
+  t = typ(gel(A,1));
+  if (is_row) return t == t_COL ? rowslice_i(A, lB, x1, y1, skip):
+                                  rowsmallslice_i(A, lB, x1, y1, skip);
+  slice = t == t_COL? &vecslice_i: &vecsmallslice_i;
+
+  B = cgetg(lB, t_MAT);
+  for (i=1; i<lB; i++, y1++)
+  {
+    if (y1 == skip) { i--; continue; }
+    gel(B,i) = slice(gel(A,y1),t,rlB, x1, rskip);
+  }
+  return B;
+}
+
+GEN
+vecrange(GEN a, GEN b)
+{
+  GEN y;
+  long i, l;
+  if (typ(a)!=t_INT) pari_err_TYPE("[_.._]",a);
+  if (typ(b)!=t_INT) pari_err_TYPE("[_.._]",b);
+  if (cmpii(a,b)>0) return cgetg(1,t_VEC);
+  l = itos(subii(b,a))+1;
+  a = setloop(a);
+  y = cgetg(l+1, t_VEC);
+  for (i=1; i<=l; a = incloop(a), i++)
+    gel(y,i) = icopy(a);
+  return y;
+}
+
+GEN
+vecrangess(long a, long b)
+{
+  GEN y;
+  long i, l;
+  if (a>b) return cgetg(1,t_VEC);
+  l = b-a+1;
+  y = cgetg(l+1, t_VEC);
+  for (i=1; i<=l; a++, i++)
+    gel(y,i) = stoi(a);
+  return y;
+}
+
+GEN
+genindexselect(void *E, long (*f)(void* E, GEN x), GEN A)
+{
+  long l, i, lv;
+  GEN v, z;
+  pari_sp av;
+  clone_lock(A);
+  if (typ(A) == t_LIST)
+  {
+    z = list_data(A);
+    l = z? lg(z): 1;
+  }
+  else
+  {
+    l = lg(A);
+    z = A;
+  }
+  v = cgetg(l, t_VECSMALL);
+  av = avma;
+  for (i = lv = 1; i < l; i++) {
+    if (f(E, gel(z,i))) v[lv++] = i;
+    avma = av;
+  }
+  clone_unlock(A); fixlg(v, lv); return v;
+}
+static GEN
+extract_copy(GEN A, GEN v)
+{
+  long i, l = lg(v);
+  GEN B = cgetg(l, typ(A));
+  for (i = 1; i < l; i++) gel(B,i) = gcopy(gel(A,v[i]));
+  return B;
+}
+/* as genselect, but treat A [ t_VEC,t_COL, or t_MAT] as a t_VEC */
+GEN
+vecselect(void *E, long (*f)(void* E, GEN x), GEN A)
+{
+  GEN v;
+  clone_lock(A);
+  v = genindexselect(E, f, A);
+  A = extract_copy(A, v); settyp(A, t_VEC);
+  clone_unlock(A); return A;
+}
+GEN
+genselect(void *E, long (*f)(void* E, GEN x), GEN A)
+{
+  GEN y, z, v;/* v left on stack for efficiency */
+  clone_lock(A);
+  switch(typ(A))
+  {
+    case t_LIST:
+      z = list_data(A);
+      if (!z) y = listcreate();
+      else
+      {
+        GEN B;
+        y = cgetg(3, t_LIST);
+        v = genindexselect(E, f, z);
+        B = extract_copy(z, v);
+        list_nmax(y) = lg(B)-1;
+        list_data(y) = B;
+      }
+      break;
+    case t_VEC: case t_COL: case t_MAT:
+      v = genindexselect(E, f, A);
+      y = extract_copy(A, v);
+      break;
+    default:
+      pari_err_TYPE("select",A);
+      return NULL;/*not reached*/
+  }
+  clone_unlock(A); return y;
+}
+
+GEN
+select0(GEN f, GEN x, long flag)
+{
+  if (typ(f) != t_CLOSURE || closure_arity(f) < 1) pari_err_TYPE("select", f);
+  switch(flag)
+  {
+    case 0: return genselect((void *) f, gp_callbool, x);
+    case 1: return genindexselect((void *) f, gp_callbool, x);
+    default: pari_err_FLAG("select");
+             return NULL;/*not reached*/
+  }
+}
+
+GEN
+parselect(GEN C, GEN D, long flag)
+{
+  pari_sp av, av2;
+  long lv, l = lg(D), i, pending = 0, workid;
+  GEN V, worker, done;
+  struct pari_mt pt;
+  if (typ(C) != t_CLOSURE || closure_arity(C) < 1) pari_err_TYPE("parapply",C);
+  if (!is_vec_t(typ(D))) pari_err_TYPE("parapply",D);
+  V = cgetg(l, t_VECSMALL); av = avma;
+  worker = strtoclosure("_parapply_worker", 1, C);
+  av2 = avma;
+  mt_queue_start(&pt, worker);
+  for (i=1; i<l || pending; i++)
+  {
+    mt_queue_submit(&pt, i, i<l? mkvec(gel(D,i)): NULL);
+    done = mt_queue_get(&pt, &workid, &pending);
+    if (done) V[workid] = !gequal0(done);
+    avma = av2;
+  }
+  mt_queue_end(&pt);
+  avma = av;
+  for (lv=1, i=1; i<l; i++)
+    if (V[i]) V[lv++]=i;
+  fixlg(V, lv);
+  return flag? V: extract_copy(D, V);
+}
+
+GEN
+veccatapply(void *E, GEN (*f)(void* E, GEN x), GEN x)
+{
+  pari_sp av = avma;
+  GEN v = vecapply(E, f, x);
+  return lg(v) == 1? v: gerepilecopy(av, shallowconcat1(v));
+}
+
+static GEN
+vecapply2(void *E, GEN (*f)(void* E, GEN x), GEN x)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x, &lx); y[1] = x[1];
+  for (i=2; i<lx; i++) gel(y,i) = f(E, gel(x,i));
+  return y;
+}
+static GEN
+vecapply1(void *E, GEN (*f)(void* E, GEN x), GEN x)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x, &lx);
+  for (i=1; i<lx; i++) gel(y,i) = f(E, gel(x,i));
+  return y;
+}
+/* as genapply, but treat A [ t_VEC,t_COL, or t_MAT] as a t_VEC */
+GEN
+vecapply(void *E, GEN (*f)(void* E, GEN x), GEN x)
+{
+  GEN y;
+  clone_lock(x); y = vecapply1(E,f,x);
+  clone_unlock(x); settyp(y, t_VEC); return y;
+}
+GEN
+genapply(void *E, GEN (*f)(void* E, GEN x), GEN x)
+{
+  long i, lx, tx = typ(x);
+  GEN y, z;
+  if (is_scalar_t(tx)) return f(E, x);
+  clone_lock(x);
+  switch(tx) {
+    case t_POL: y = normalizepol(vecapply2(E,f,x)); break;
+    case t_SER: y = normalize(vecapply2(E,f,x)); break;
+    case t_LIST:
+      z = list_data(x);
+      if (!z)
+        y = listcreate();
+      else
+      {
+        y = cgetg(3, t_LIST);
+        list_nmax(y) = lg(z)-1;
+        list_data(y) = vecapply1(E,f,z);
+      }
+      break;
+    case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i = 1; i < lx; i++) gel(y,i) = vecapply1(E,f,gel(x,i));
+      break;
+
+    case t_VEC: case t_COL: y = vecapply1(E,f,x); break;
+    default:
+      pari_err_TYPE("apply",x); return NULL;/*not reached*/
+  }
+  clone_unlock(x); return y;
+}
+
+GEN
+apply0(GEN f, GEN x)
+{
+  if (typ(f) != t_CLOSURE || closure_arity(f) < 1) pari_err_TYPE("apply",f);
+  return genapply((void *) f, gp_call, x);
+}
+
+GEN
+vecselapply(void *Epred, long (*pred)(void* E, GEN x), void *Efun,
+                         GEN (*fun)(void* E, GEN x), GEN A)
+{
+  GEN y;
+  long i, l = lg(A), nb=1;
+  clone_lock(A); y = cgetg(l, t_VEC);
+  for (i=1; i<l; i++)
+    if (pred(Epred, gel(A,i))) gel(y,nb++) = fun(Efun, gel(A,i));
+  fixlg(y,nb); clone_unlock(A); return y;
+}
+
+GEN
+veccatselapply(void *Epred, long (*pred)(void* E, GEN x), void *Efun,
+                            GEN (*fun)(void* E, GEN x), GEN A)
+{
+  pari_sp av = avma;
+  GEN v = vecselapply(Epred, pred, Efun, fun, A);
+  return lg(v) == 1? v: gerepilecopy(av, shallowconcat1(v));
+}
+
+GEN
+parapply_worker(GEN d, GEN C)
+{
+  return closure_callgen1(C, d);
+}
+
+GEN
+parapply(GEN C, GEN D)
+{
+  pari_sp av = avma;
+  long l = lg(D), i, pending = 0, workid;
+  GEN V, worker, done;
+  struct pari_mt pt;
+  if (typ(C) != t_CLOSURE || closure_arity(C) < 1) pari_err_TYPE("parapply",C);
+  if (!is_vec_t(typ(D))) pari_err_TYPE("parapply",D);
+  worker = strtoclosure("_parapply_worker", 1, C);
+  V = cgetg(l, typ(D));
+  mt_queue_start(&pt, worker);
+  for (i=1; i<l || pending; i++)
+  {
+    mt_queue_submit(&pt, i, i<l? mkvec(gel(D,i)): NULL);
+    done = mt_queue_get(&pt, &workid, &pending);
+    if (done) gel(V,workid) = done;
+  }
+  mt_queue_end(&pt);
+  return gerepilecopy(av, V);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     SCALAR-MATRIX OPERATIONS                    */
+/*                                                                 */
+/*******************************************************************/
+GEN
+gtomat(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  if (!x) return cgetg(1, t_MAT);
+  switch(typ(x))
+  {
+    case t_LIST:
+      x = list_data(x);
+      if (!x) return cgetg(1, t_MAT);
+      /* fall through */
+    case t_VEC: {
+      lx=lg(x); y=cgetg(lx,t_MAT);
+      if (lx == 1) break;
+      if (typ(gel(x,1)) == t_COL) {
+        long h = lgcols(x);
+        for (i=2; i<lx; i++) {
+          if (typ(gel(x,i)) != t_COL || lg(gel(x,i)) != h) break;
+        }
+        if (i == lx) { /* matrix with h-1 rows */
+          y = cgetg(lx, t_MAT);
+          for (i=1 ; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+          return y;
+        }
+      }
+      for (i=1; i<lx; i++) gel(y,i) = mkcolcopy(gel(x,i));
+      break;
+    }
+    case t_COL:
+      lx = lg(x);
+      if (lx == 1) return cgetg(1, t_MAT);
+      if (typ(gel(x,1)) == t_VEC) {
+        long j, h = lgcols(x);
+        for (i=2; i<lx; i++) {
+          if (typ(gel(x,i)) != t_VEC || lg(gel(x,i)) != h) break;
+        }
+        if (i == lx) { /* matrix with h cols */
+          y = cgetg(h, t_MAT);
+          for (j=1 ; j<h; j++) {
+            gel(y,j) = cgetg(lx, t_COL);
+            for (i=1; i<lx; i++) gcoeff(y,i,j) = gcopy(gmael(x,i,j));
+          }
+          return y;
+        }
+      }
+      y = mkmatcopy(x); break;
+    case t_MAT:
+      y = gcopy(x); break;
+    case t_QFI: case t_QFR: {
+      GEN b;
+      y = cgetg(3,t_MAT); b = gmul2n(gel(x,2),-1);
+      gel(y,1) = mkcol2(icopy(gel(x,1)), b);
+      gel(y,2) = mkcol2(b, icopy(gel(x,3)));
+      break;
+    }
+    default:
+      y = cgetg(2,t_MAT); gel(y,1) = mkcolcopy(x);
+      break;
+  }
+  return y;
+}
+
+/* create the diagonal matrix, whose diagonal is given by x */
+GEN
+diagonal(GEN x)
+{
+  long j, lx, tx = typ(x);
+  GEN y;
+
+  if (! is_matvec_t(tx)) return scalarmat(x,1);
+  if (tx==t_MAT)
+  {
+    if (RgM_isdiagonal(x)) return gcopy(x);
+    pari_err_TYPE("diagonal",x);
+  }
+  lx=lg(x); y=cgetg(lx,t_MAT);
+  for (j=1; j<lx; j++)
+  {
+    gel(y,j) = zerocol(lx-1);
+    gcoeff(y,j,j) = gcopy(gel(x,j));
+  }
+  return y;
+}
+/* same, assuming x is a t_VEC/t_COL. Not memory clean. */
+GEN
+diagonal_shallow(GEN x)
+{
+  long j, lx = lg(x);
+  GEN y = cgetg(lx,t_MAT);
+
+  for (j=1; j<lx; j++)
+  {
+    gel(y,j) = zerocol(lx-1);
+    gcoeff(y,j,j) = gel(x,j);
+  }
+  return y;
+}
+
+/* compute m*diagonal(d) */
+GEN
+matmuldiagonal(GEN m, GEN d)
+{
+  long j, lx;
+  GEN y = cgetg_copy(m, &lx);
+
+  if (typ(m)!=t_MAT) pari_err_TYPE("matmuldiagonal",m);
+  if (! is_vec_t(typ(d))) pari_err_TYPE("matmuldiagonal",d);
+  if (lg(d) != lx) pari_err_OP("operation 'matmuldiagonal'", m,d);
+  for (j=1; j<lx; j++) gel(y,j) = RgC_Rg_mul(gel(m,j), gel(d,j));
+  return y;
+}
+
+/* compute A*B assuming the result is a diagonal matrix */
+GEN
+matmultodiagonal(GEN A, GEN B)
+{
+  long i, j, hA, hB, lA = lg(A), lB = lg(B);
+  GEN y = matid(lB-1);
+
+  if (typ(A) != t_MAT) pari_err_TYPE("matmultodiagonal",A);
+  if (typ(B) != t_MAT) pari_err_TYPE("matmultodiagonal",B);
+  hA = (lA == 1)? lB: lgcols(A);
+  hB = (lB == 1)? lA: lgcols(B);
+  if (lA != hB || lB != hA) pari_err_OP("operation 'matmultodiagonal'", A,B);
+  for (i=1; i<lB; i++)
+  {
+    GEN z = gen_0;
+    for (j=1; j<lA; j++) z = gadd(z, gmul(gcoeff(A,i,j),gcoeff(B,j,i)));
+    gcoeff(y,i,i) = z;
+  }
+  return y;
+}
+
+/* [m[1,1], ..., m[l,l]], internal */
+GEN
+RgM_diagonal_shallow(GEN m)
+{
+  long i, lx = lg(m);
+  GEN y = cgetg(lx,t_VEC);
+  for (i=1; i<lx; i++) gel(y, i) = gcoeff(m,i,i);
+  return y;
+}
+
+/* same, public function */
+GEN
+RgM_diagonal(GEN m)
+{
+  long i, lx = lg(m);
+  GEN y = cgetg(lx,t_VEC);
+  for (i=1; i<lx; i++) gel(y,i) = gcopy(gcoeff(m,i,i));
+  return y;
+}
+
+
diff --git a/src/basemath/arith1.c b/src/basemath/arith1.c
new file mode 100644
index 0000000..2f278ca
--- /dev/null
+++ b/src/basemath/arith1.c
@@ -0,0 +1,4404 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*********************************************************************/
+/**                                                                 **/
+/**                     ARITHMETIC FUNCTIONS                        **/
+/**                         (first part)                            **/
+/**                                                                 **/
+/*********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/******************************************************************/
+/*                                                                */
+/*                 GENERATOR of (Z/mZ)*                           */
+/*                                                                */
+/******************************************************************/
+static GEN
+remove2(GEN q) { long v = vali(q); return v? shifti(q, -v): q; }
+static ulong
+u_remove2(ulong q) { return q >> vals(q); }
+GEN
+odd_prime_divisors(GEN q) { return gel(Z_factor(remove2(q)), 1); }
+static GEN
+u_odd_prime_divisors(ulong q) { return gel(factoru(u_remove2(q)), 1); }
+/* p odd prime, q=(p-1)/2; L0 list of (some) divisors of q = (p-1)/2 or NULL
+ * (all prime divisors of q); return the q/l, l in L0 */
+static GEN
+is_gener_expo(GEN p, GEN L0)
+{
+  GEN L, q = shifti(p,-1);
+  long i, l;
+  if (L0) {
+    l = lg(L0);
+    L = cgetg(l, t_VEC);
+  } else {
+    L0 = L = odd_prime_divisors(q);
+    l = lg(L);
+  }
+  for (i=1; i<l; i++) gel(L,i) = diviiexact(q, gel(L0,i));
+  return L;
+}
+static GEN
+u_is_gener_expo(ulong p, GEN L0)
+{
+  const ulong q = p >> 1;
+  long i, l;
+  GEN L;
+  if (L0) {
+    l = lg(L0);
+    L = cgetg(l, t_VECSMALL);
+  } else {
+    L0 = L = u_odd_prime_divisors(q);
+    l = lg(L);
+  }
+  for (i=1; i<l; i++) L[i] = q / (ulong)L0[i];
+  return L;
+}
+
+int
+is_gener_Fl(ulong x, ulong p, ulong p_1, GEN L)
+{
+  long i;
+  if (krouu(x, p) >= 0) return 0;
+  for (i=lg(L)-1; i; i--)
+  {
+    ulong t = Fl_powu(x, (ulong)L[i], p);
+    if (t == p_1 || t == 1) return 0;
+  }
+  return 1;
+}
+/* assume p prime */
+ulong
+pgener_Fl_local(ulong p, GEN L0)
+{
+  const pari_sp av = avma;
+  const ulong p_1 = p-1;
+  long x;
+  GEN L;
+  if (p <= 19) switch(p)
+  { /* quick trivial cases */
+    case 2:  return 1;
+    case 7:
+    case 17: return 3;
+    default: return 2;
+  }
+  L = u_is_gener_expo(p,L0);
+  for (x=2;;x++) { if (is_gener_Fl(x,p,p_1,L)) break; }
+  avma = av; return x;
+}
+ulong
+pgener_Fl(ulong p) { return pgener_Fl_local(p, NULL); }
+
+/* L[i] = set of (p-1)/2l, l ODD prime divisor of p-1 (l=2 can be included,
+ * but wasteful) */
+int
+is_gener_Fp(GEN x, GEN p, GEN p_1, GEN L)
+{
+  long i, t = lgefint(x)==3 && x[2]>0? krosi(x[2], p): kronecker(x, p);
+  if (t >= 0) return 0;
+  for (i = lg(L)-1; i; i--)
+  {
+    GEN t = Fp_pow(x, gel(L,i), p);
+    if (equalii(t, p_1) || equali1(t)) return 0;
+  }
+  return 1;
+}
+
+/* assume p prime, return a generator of all L[i]-Sylows in F_p^*. */
+GEN
+pgener_Fp_local(GEN p, GEN L0)
+{
+  pari_sp av0 = avma;
+  GEN x, p_1, L;
+  if (lgefint(p) == 3)
+  {
+    ulong z;
+    if (p[2] == 2) return gen_1;
+    if (L0) L0 = ZV_to_nv(L0);
+    z = pgener_Fl_local((ulong)p[2], L0);
+    avma = av0; return utoipos(z);
+  }
+  p_1 = subis(p,1); L = is_gener_expo(p, L0);
+  x = utoipos(2);
+  for (;; x[2]++) { if (is_gener_Fp(x, p, p_1, L)) break; }
+  avma = av0; return utoipos((ulong)x[2]);
+}
+
+GEN
+pgener_Fp(GEN p) { return pgener_Fp_local(p, NULL); }
+
+ulong
+pgener_Zl(ulong p)
+{
+  if (p == 2) pari_err_DOMAIN("pgener_Zl","p","=",gen_2,gen_2);
+  /* only p < 2^32 such that znprimroot(p) != znprimroot(p^2) */
+  if (p == 40487) return 10;
+#ifndef LONG_IS_64BIT
+  return pgener_Fl(p);
+#else
+  if (p < (1UL<<32)) return pgener_Fl(p);
+  else
+  {
+    const pari_sp av = avma;
+    const ulong p_1 = p-1;
+    long x ;
+    GEN p2 = sqru(p), L = u_is_gener_expo(p, NULL);
+    for (x=2;;x++)
+      if (is_gener_Fl(x,p,p_1,L) && !is_pm1(Fp_powu(utoipos(x),p_1,p2))) break;
+    avma = av; return x;
+  }
+#endif
+}
+
+/* p prime. Return a primitive root modulo p^e, e > 1 */
+GEN
+pgener_Zp(GEN p)
+{
+  if (lgefint(p) == 3) return utoipos(pgener_Zl(p[2]));
+  else
+  {
+    const pari_sp av = avma;
+    GEN p_1 = subis(p,1), p2 = sqri(p), L = is_gener_expo(p,NULL);
+    GEN x = utoipos(2);
+    for (;; x[2]++)
+      if (is_gener_Fp(x,p,p_1,L) && !equali1(Fp_pow(x,p_1,p2))) break;
+    avma = av; return utoipos((ulong)x[2]);
+  }
+}
+
+static GEN
+gener_Zp(GEN q, GEN F)
+{
+  GEN p = NULL;
+  long e = 0;
+  if (F)
+  {
+    GEN P = gel(F,1), E = gel(F,2);
+    long i, l = lg(P);
+    for (i = 1; i < l; i++)
+    {
+      p = gel(P,i);
+      if (equaliu(p, 2)) continue;
+      if (i < l-1) pari_err_DOMAIN("znprimroot", "argument","=",F,F);
+      e = itos(gel(E,i));
+    }
+    if (!p) pari_err_DOMAIN("znprimroot", "argument","=",F,F);
+  }
+  else
+    e = Z_isanypower(q, &p);
+  return e > 1? pgener_Zp(p): pgener_Fp(q);
+}
+
+GEN
+znprimroot(GEN N)
+{
+  pari_sp av = avma;
+  GEN x, n, F;
+
+  if ((F = check_arith_non0(N,"znprimroot")))
+  {
+    F = clean_Z_factor(F);
+    N = typ(N) == t_VEC? gel(N,1): factorback(F);
+  }
+  if (signe(N) < 0) N = absi(N);
+  if (cmpiu(N, 4) <= 0) { avma = av; return mkintmodu(N[2]-1,N[2]); }
+  switch(mod4(N))
+  {
+    case 0: /* N = 0 mod 4 */
+      pari_err_DOMAIN("znprimroot", "argument","=",N,N);
+      x = NULL; break;
+    case 2: /* N = 2 mod 4 */
+      n = shifti(N,-1); /* becomes odd */
+      x = gener_Zp(n,F); if (!mod2(x)) x = addii(x,n);
+      break;
+    default: /* N odd */
+      x = gener_Zp(N,F);
+      break;
+  }
+  return gerepilecopy(av, mkintmod(x, N));
+}
+
+/* n | (p-1), returns a primitive n-th root of 1 in F_p^* */
+GEN
+rootsof1_Fp(GEN n, GEN p)
+{
+  pari_sp av = avma;
+  GEN L = odd_prime_divisors(n); /* 2 implicit in pgener_Fp_local */
+  GEN z = pgener_Fp_local(p, L);
+  z = Fp_pow(z, diviiexact(subis(p,1), n), p); /* prim. n-th root of 1 */
+  return gerepileuptoint(av, z);
+}
+
+GEN
+rootsof1u_Fp(ulong n, GEN p)
+{
+  pari_sp av = avma;
+  GEN z, L = u_odd_prime_divisors(n); /* 2 implicit in pgener_Fp_local */
+  z = pgener_Fp_local(p, Flv_to_ZV(L));
+  z = Fp_pow(z, diviuexact(subis(p,1), n), p); /* prim. n-th root of 1 */
+  return gerepileuptoint(av, z);
+}
+
+ulong
+rootsof1_Fl(ulong n, ulong p)
+{
+  pari_sp av = avma;
+  GEN L = u_odd_prime_divisors(n); /* 2 implicit in pgener_Fl_local */
+  ulong z = pgener_Fl_local(p, L);
+  z = Fl_powu(z, (p-1) / n, p); /* prim. n-th root of 1 */
+  avma = av; return z;
+}
+
+GEN
+znstar(GEN N)
+{
+  GEN F = NULL, P, E, cyc, gen, mod;
+  long i, j, nbp, sizeh;
+  pari_sp av = avma;
+
+  if ((F = check_arith_all(N,"znstar")))
+  {
+    F = clean_Z_factor(F);
+    N = typ(N) == t_VEC? gel(N,1): factorback(F);
+  }
+  if (!signe(N))
+  {
+    avma = av;
+    retmkvec3(gen_2, mkvec(gen_2), mkvec(gen_m1));
+  }
+  if (signe(N) < 0) N = absi(N);
+  if (cmpiu(N,2) <= 0)
+  {
+    avma = av;
+    retmkvec3(gen_1, cgetg(1,t_VEC), cgetg(1,t_VEC));
+  }
+  if (!F) F = Z_factor(N);
+  P = gel(F,1);
+  E = gel(F,2); nbp = lg(P)-1;
+  cyc = cgetg(nbp+2,t_VEC);
+  gen = cgetg(nbp+2,t_VEC);
+  mod = cgetg(nbp+2,t_VEC);
+  switch(mod8(N))
+  {
+    case 0: {
+      long v2 = itos(gel(E,1));
+      gel(cyc,1) = int2n(v2-2);
+      gel(cyc,2) = gen_2;
+      gel(gen,1) = utoipos(5);
+      gel(gen,2) = addis(int2n(v2-1), -1);
+      gel(mod,1) = gel(mod,2) = int2n(v2);
+      sizeh = nbp+1; i = 3; j = 2; break;
+    }
+    case 4:
+      gel(cyc,1) = gen_2;
+      gel(gen,1) = utoipos(3);
+      gel(mod,1) = utoipos(4);
+      sizeh = nbp; i = j = 2; break;
+    case 2: case 6:
+      sizeh = nbp-1; i=1; j=2; break;
+    default: /* 1, 3, 5, 7 */
+      sizeh = nbp; i = j = 1;
+  }
+  for ( ; j<=nbp; i++,j++)
+  {
+    long e = itos(gel(E,j));
+    GEN p = gel(P,j), q = powiu(p, e-1), Q = mulii(p, q);
+    gel(cyc,i) = subii(Q, q); /* phi(p^e) */
+    gel(gen,i) = e > 1? pgener_Zp(p): pgener_Fp(p);
+    gel(mod,i) = Q;
+  }
+  setlg(gen, sizeh+1);
+  setlg(cyc, sizeh+1);
+  if (nbp > 1)
+    for (i=1; i<=sizeh; i++)
+    {
+      GEN Q = gel(mod,i), g = gel(gen,i), qinv = Fp_inv(Q, diviiexact(N,Q));
+      g = addii(g, mulii(mulii(subsi(1,g),qinv),Q));
+      gel(gen,i) = modii(g, N);
+    }
+
+  /*The cyc[i] are > 1. They remain so in the loop*/
+  for (i=sizeh; i>=2; i--)
+  {
+    GEN ci = gel(cyc,i), gi = gel(gen,i);
+    for (j=i-1; j>=1; j--) /* we want cyc[i] | cyc[j] */
+    {
+      GEN cj = gel(cyc,j), gj, qj, v, d;
+
+      d = bezout(ci,cj,NULL,&v); /* > 1 */
+      if (absi_equal(ci, d)) continue; /* ci | cj */
+      if (absi_equal(cj, d)) { /* cj | ci */
+        swap(gel(gen,j),gel(gen,i)); gi = gel(gen,i);
+        swap(gel(cyc,j),gel(cyc,i)); ci = gel(cyc,i); continue;
+      }
+
+      gj = gel(gen,j);
+      qj = diviiexact(cj,d);
+      gel(cyc,j) = mulii(ci,qj);
+      gel(cyc,i) = d;
+      /* [1,v*cj/d; 0,1]*[1,0;-1,1]*diag(cj,ci)*[ci/d,-v; cj/d,u]
+       * = diag(lcm,gcd), with u ci + v cj = d */
+
+      /* (gj, gi) *= [1,0; -1,1]^-1 */
+      gj = Fp_mul(gj, gi, N); /* order ci*qj = lcm(ci,cj) */
+      /* (gj,gi) *= [1,v*qj; 0,1]^-1 */
+      togglesign_safe(&v);
+      if (signe(v) < 0) v = modii(v,ci); /* >= 0 to avoid inversions */
+      gi = Fp_mul(gi, Fp_pow(gj, mulii(qj, v), N), N);
+      gel(gen,i) = gi;
+      gel(gen,j) = gj;
+      ci = d; if (equaliu(ci, 2)) break;
+    }
+  }
+  return gerepilecopy(av, mkvec3(ZV_prod(cyc), cyc, FpV_to_mod(gen,N)));
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                     INVERSE TOTIENT FUNCTION                    **/
+/**                                                                 **/
+/*********************************************************************/
+/* N t_INT, L a ZV containing all prime divisors of N, and possibly other
+ * primes. Return factor(N) */
+GEN
+Z_factor_listP(GEN N, GEN L)
+{
+  long i, k, l = lg(L);
+  GEN P = cgetg(l, t_COL), E = cgetg(l, t_COL);
+  for (i = k = 1; i < l; i++)
+  {
+    GEN p = gel(L,i);
+    long v = Z_pvalrem(N, p, &N);
+    if (v)
+    {
+      gel(P,k) = p;
+      gel(E,k) = utoipos(v);
+      k++;
+    }
+  }
+  setlg(P, k);
+  setlg(E, k); return mkmat2(P,E);
+}
+
+/* look for x such that phi(x) = n, p | x => p > m (if m = NULL: no condition).
+ * L is a list of primes containing all prime divisors of n. */
+static long
+istotient_i(GEN n, GEN m, GEN L, GEN *px)
+{
+  pari_sp av = avma, av2;
+  GEN k, D;
+  long i, v;
+  if (m && mod2(n))
+  {
+    if (!equali1(n)) return 0;
+    if (px) *px = gen_1;
+    return 1;
+  }
+  D = divisors(Z_factor_listP(shifti(n, -1), L));
+  /* loop through primes p > m, d = p-1 | n */
+  av2 = avma;
+  if (!m)
+  { /* special case p = 2, d = 1 */
+    k = n;
+    for (v = 1;; v++) {
+      if (istotient_i(k, gen_2, L, px)) {
+        if (px) *px = shifti(*px, v);
+        return 1;
+      }
+      if (mod2(k)) break;
+      k = shifti(k,-1);
+    }
+    avma = av2;
+  }
+  for (i = 1; i < lg(D); ++i)
+  {
+    GEN p, d = shifti(gel(D, i), 1); /* even divisors of n */
+    if (m && cmpii(d, m) < 0) continue;
+    p = addiu(d, 1);
+    if (!isprime(p)) continue;
+    k = diviiexact(n, d);
+    for (v = 1;; v++) {
+      GEN r;
+      if (istotient_i(k, p, L, px)) {
+        if (px) *px = mulii(*px, powiu(p, v));
+        return 1;
+      }
+      k = dvmdii(k, p, &r);
+      if (r != gen_0) break;
+    }
+    avma = av2;
+  }
+  avma = av; return 0;
+}
+
+/* find x such that phi(x) = n */
+long
+istotient(GEN n, GEN *px)
+{
+  pari_sp av = avma;
+  if (typ(n) != t_INT) pari_err_TYPE("istotient", n);
+  if (signe(n) < 1) return 0;
+  if (mod2(n))
+  {
+    if (!equali1(n)) return 0;
+    if (px) *px = gen_1;
+    return 1;
+  }
+  if (istotient_i(n, NULL, gel(Z_factor(n), 1), px))
+  {
+    if (!px) avma = av;
+    else
+      *px = gerepileuptoint(av, *px);
+    return 1;
+  }
+  avma = av; return 0;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                     INTEGRAL LOGARITHM                          **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* y > 1 and B > 0 integers. Return e such that y^(e-1) <= B < y^e, i.e
+ * e = 1 + floor(log_y B). Set *ptq = y^e if non-NULL */
+long
+logint(GEN B, GEN y, GEN *ptq)
+{
+  pari_sp av = avma, av2;
+  long eB, ey, e, i, fl;
+  GEN q,pow2, r = y;
+
+  if (typ(B) != t_INT) B = ceil_safe(B);
+  eB = expi(B); /* 2^eB <= B < 2^(eB + 1) */
+  ey = expi(y); /* result e satisfies e > eB / (ey+1) */
+  if (eB <= 13 * ey) /* e small, be naive */
+  {
+    for (e=1;; e++)
+    { /* here, r = y^e */
+      fl = cmpii(r, B);
+      if (fl > 0) goto END;
+      r = mulii(r,y);
+    }
+  }
+  /* e > 13 ey / (ey + 1) >= 6.5 */
+
+  /* binary splitting: compute bits of e one by one */
+  /* compute pow2[i] = y^(2^i) [i < crude upper bound for log_2 log_y(B)] */
+  pow2 = new_chunk((long)log2(eB)+2);
+  gel(pow2,0) = y;
+  for (i=0,q=r;; )
+  { /* r = y^2^i */
+    fl = cmpii(r,B);
+    if (!fl) { e = 1L<<i; e++; r = mulii(r,y); goto END; }
+    if (fl > 0) break;
+    q = r; r = sqri(q);
+    i++; gel(pow2,i) = r;
+  }
+
+  av2 = avma;
+  for (i--, e=1L<<i;;)
+  { /* y^e = q < B <= r = q * y^(2^i) */
+    if (!fl) break; /* B = r */
+    /* q < B < r */
+    if (--i < 0) { if (fl > 0) e++; break; }
+    r = mulii(q, gel(pow2,i));
+    fl = cmpii(r, B);
+    if (fl <= 0) { e += (1L<<i); q = r = gerepileuptoint(av2, r); }
+  }
+  if (fl <= 0) { e++; r = mulii(r,y); }
+END:
+  if (ptq) *ptq = gerepileuptoint(av, r); else avma = av;
+  return e;
+}
+
+long
+logint0(GEN B, GEN y, GEN *ptq)
+{
+  long e;
+  if (typ(B) != t_INT) pari_err_TYPE("logint",B);
+  if (signe(B)<=0)
+    pari_err_DOMAIN("logint", "x" ,"<=", gen_0, B);
+  if (typ(y) != t_INT) pari_err_TYPE("logint",y);
+  if (signe(y)<=0 || equali1(y))
+    pari_err_DOMAIN("logint", "b" ,"<=", gen_1, y);
+  if (equaliu(y, 2))
+  {
+    e = expi(B);
+    if (ptq) *ptq = int2n(e);
+    return e;
+  }
+  e = logint(B,y,ptq)-1;
+  if (ptq)
+  {
+    pari_sp av = avma;
+    *ptq = gerepileuptoint(av, diviiexact(*ptq, y));
+  }
+  return e;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                     INTEGRAL SQUARE ROOT                        **/
+/**                                                                 **/
+/*********************************************************************/
+GEN
+sqrtint(GEN a)
+{
+  if (typ(a) != t_INT) pari_err_TYPE("sqrtint",a);
+  switch (signe(a))
+  {
+    case 1: return sqrti(a);
+    case 0: return gen_0;
+    default: pari_err_DOMAIN("sqrtint", "argument", "<", gen_0,a);
+  }
+  return NULL; /* not reached */
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                      PERFECT SQUARE                             **/
+/**                                                                 **/
+/*********************************************************************/
+static int
+carremod(ulong A)
+{
+  const int carresmod64[]={
+    1,1,0,0,1,0,0,0,0,1, 0,0,0,0,0,0,1,1,0,0, 0,0,0,0,0,1,0,0,0,0,
+    0,0,0,1,0,0,1,0,0,0, 0,1,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,1,0,0, 0,0,0,0};
+  const int carresmod63[]={
+    1,1,0,0,1,0,0,1,0,1, 0,0,0,0,0,0,1,0,1,0, 0,0,1,0,0,1,0,0,1,0,
+    0,0,0,0,0,0,1,1,0,0, 0,0,0,1,0,0,1,0,0,1, 0,0,0,0,0,0,0,0,1,0, 0,0,0};
+  const int carresmod65[]={
+    1,1,0,0,1,0,0,0,0,1, 1,0,0,0,1,0,1,0,0,0, 0,0,0,0,0,1,1,0,0,1,
+    1,0,0,0,0,1,1,0,0,1, 1,0,0,0,0,0,0,0,0,1, 0,1,0,0,0,1,1,0,0,0, 0,1,0,0,1};
+  const int carresmod11[]={1,1,0,1,1,1,0,0,0,1, 0};
+  return (carresmod64[A & 0x3fUL]
+    && carresmod63[A % 63UL]
+    && carresmod65[A % 65UL]
+    && carresmod11[A % 11UL]);
+}
+
+/* emulate Z_issquareall on single-word integers */
+long
+uissquareall(ulong A, ulong *sqrtA)
+{
+  if (!A) { *sqrtA = 0; return 1; }
+  if (carremod(A))
+  {
+    ulong a = usqrt(A);
+    if (a * a == A) { *sqrtA = a; return 1; }
+  }
+  return 0;
+}
+long
+uissquare(ulong A)
+{
+  if (!A) return 1;
+  if (carremod(A))
+  {
+    ulong a = usqrt(A);
+    if (a * a == A) return 1;
+  }
+  return 0;
+}
+
+long
+Z_issquareall(GEN x, GEN *pt)
+{
+  pari_sp av;
+  GEN y, r;
+
+  switch(signe(x))
+  {
+    case -1: return 0;
+    case 0: if (pt) *pt=gen_0; return 1;
+  }
+  if (lgefint(x) == 3)
+  {
+    ulong u = (ulong)x[2], a;
+    if (!pt) return uissquare(u);
+    if (!uissquareall(u, &a)) return 0;
+    *pt = utoipos(a); return 1;
+  }
+  if (!carremod(umodiu(x, 64*63*65*11))) return 0;
+  av = avma; y = sqrtremi(x, &r);
+  if (r != gen_0) { avma = av; return 0; }
+  if (pt) { *pt = y; avma = (pari_sp)y; } else avma = av;
+  return 1;
+}
+
+/* a t_INT, p prime */
+long
+Zp_issquare(GEN a, GEN p)
+{
+  long v;
+  GEN ap;
+
+  if (!signe(a) || gequal1(a)) return 1;
+  v = Z_pvalrem(a, p, &ap);
+  if (v&1) return 0;
+  return equaliu(p, 2)? umodiu(ap, 8) == 1
+                      : kronecker(ap,p) == 1;
+}
+
+static int
+is_char_2(GEN a)
+{
+  long j;
+  GEN b;
+  switch(typ(a))
+  {
+  case t_INTMOD:
+    b = gel(a,1);
+    if (!mod2(b))
+    {
+      if (!equaliu(b, 2)) pari_err_IMPL( "issquare for this input");
+      return 1;
+    }
+    return 0;
+  case t_FFELT:
+    if (equaliu(FF_p_i(a), 2)) return 1;
+    return 0;
+  case t_POLMOD:
+    if (is_char_2(gel(a,1)) || is_char_2(gel(a,2))) return 1;
+    return 0;
+  case t_POL:
+    for (j = 2; j < lg(a); j++)
+      if (is_char_2(gel(a,j))) return 1;
+    return 0;
+  }
+  return 0;
+}
+
+static long
+polissquareall(GEN x, GEN *pt)
+{
+  pari_sp av;
+  long v, l = degpol(x);
+  GEN y, a, b;
+
+  if (!signe(x))
+  {
+    if (pt) *pt = gcopy(x);
+    return 1;
+  }
+  if (pt) *pt = gen_0;
+  if (l&1) return 0; /* odd degree */
+  av = avma;
+  v = RgX_valrem(x, &x);
+  if (v) {
+    l = degpol(x);
+    if (l&1) return 0;
+  }
+  a = gel(x,2);
+  switch (typ(a))
+  {
+    case t_INT:
+      if (!Z_issquareall(a,&b)) { avma = av; return 0; }
+      break;
+    case t_POL:
+      if (!polissquareall(a,&b)) { avma = av; return 0; }
+      break;
+    default:
+      if (!issquare(a)) { avma = av; return 0; }
+      b = NULL; break;
+  }
+  if (!l) {
+    if (!pt) { avma = av; return 1; }
+    if (!b) b = gsqrt(a,DEFAULTPREC);
+    y = scalarpol(b, varn(x)); goto END;
+  }
+  if (is_char_2(x))
+  {
+    long i, lx;
+    x = gmul(x, mkintmod(gen_1, gen_2));
+    lx = lg(x);
+    if ((lx-3) & 1) { avma = av; return 0; }
+    for (i = 3; i < lx; i+=2)
+      if (!gequal0(gel(x,i))) { avma = av; return 0; }
+    if (pt) {
+      y = cgetg((lx+3) / 2, t_POL);
+      for (i = 2; i < lx; i+=2)
+        if (!issquareall(gel(x,i), &gel(y, (i+2)>>1))) { avma = av; return 0; }
+      y[1] = evalsigne(1) | evalvarn(varn(x));
+      goto END;
+    } else {
+      for (i = 2; i < lx; i+=2)
+        if (!issquare(gel(x,i))) { avma = av; return 0; }
+      avma = av; return 1;
+    }
+  }
+  else
+  {
+    x = RgX_Rg_div(x,a);
+    y = gtrunc(gsqrt(RgX_to_ser(x,2+l),0));
+    if (!RgX_equal(gsqr(y), x)) { avma = av; return 0; }
+    if (!pt) { avma = av; return 1; }
+    if (!gequal1(a))
+    {
+      if (!b) b = gsqrt(a,DEFAULTPREC);
+      y = gmul(b, y);
+    }
+  }
+END:
+  *pt = v? gerepilecopy(av, RgX_shift_shallow(y, v >> 1)): gerepileupto(av, y);
+  return 1;
+}
+
+/* b unit mod p */
+static int
+Up_ispower(GEN b, GEN K, GEN p, long d, GEN *pt)
+{
+  if (d == 1)
+  { /* mod p: faster */
+    if (!Fp_ispower(b, K, p)) return 0;
+    if (pt) *pt = Fp_sqrtn(b, K, p, NULL);
+  }
+  else
+  { /* mod p^{2 +} */
+    if (!ispower(cvtop(b, p, d), K, pt)) return 0;
+    if (pt) *pt = gtrunc(*pt);
+  }
+  return 1;
+}
+
+/* We're studying whether a mod (q*p^e) is a K-th power, (q,p) = 1.
+ * Decide mod p^e, then reduce a mod q unless q = NULL. */
+static int
+handle_pe(GEN *pa, GEN q, GEN L, GEN K, GEN p, long e)
+{
+  GEN t, A;
+  long v = Z_pvalrem(*pa, p, &A), d = e - v;
+  if (d <= 0) t = gen_0;
+  else
+  {
+    ulong r;
+    v = udivui_rem(v, K, &r);
+    if (r || !Up_ispower(A, K, p, d, L? &t: NULL)) return 0;
+    if (L && v) t = mulii(t, powiu(p, v));
+  }
+  if (q) *pa = modii(*pa, q);
+  if (L) vectrunc_append(L, mkintmod(t, powiu(p, e)));
+  return 1;
+}
+long
+Zn_ispower(GEN a, GEN q, GEN K, GEN *pt)
+{
+  GEN L, N;
+  pari_sp av;
+  long e, i, l;
+  ulong pp;
+  forprime_t S;
+
+  if (!signe(a))
+  {
+    if (pt) {
+      GEN t = cgetg(3, t_INTMOD);
+      gel(t,1) = icopy(q); gel(t,2) = gen_0; *pt = t;
+    }
+    return 1;
+  }
+  /* a != 0 */
+  av = avma;
+
+  if (typ(q) != t_INT) /* integer factorization */
+  {
+    GEN P = gel(q,1), E = gel(q,2);
+    l = lg(P);
+    L = pt? vectrunc_init(l): NULL;
+    for (i = 1; i < l; i++)
+    {
+      GEN p = gel(P,i);
+      long e = itos(gel(E,i));
+      if (!handle_pe(&a, NULL, L, K, p, e)) { avma = av; return 0; }
+    }
+    goto END;
+  }
+  if (!mod2(K)
+      && kronecker(a, shifti(q,-vali(q))) == -1) { avma = av; return 0; }
+  L = pt? vectrunc_init(expi(q)+1): NULL;
+  u_forprime_init(&S, 2, tridiv_bound(q));
+  while ((pp = u_forprime_next(&S)))
+  {
+    int stop;
+    e = Z_lvalrem_stop(&q, pp, &stop);
+    if (!e) continue;
+    if (!handle_pe(&a, q, L, K, utoipos(pp), e)) { avma = av; return 0; }
+    if (stop)
+    {
+      if (!is_pm1(q) && !handle_pe(&a, q, L, K, q, 1)) { avma = av; return 0; }
+      goto END;
+    }
+  }
+  l = lg(primetab);
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(primetab,i);
+    e = Z_pvalrem(q, p, &q);
+    if (!e) continue;
+    if (!handle_pe(&a, q, L, K, p, e)) { avma = av; return 0; }
+    if (is_pm1(q)) goto END;
+  }
+  N = gcdii(a,q);
+  if (!is_pm1(N))
+  {
+    if (ifac_isprime(N))
+    {
+      e = Z_pvalrem(q, N, &q);
+      if (!handle_pe(&a, q, L, K, N, e)) { avma = av; return 0; }
+    }
+    else
+    {
+      GEN part = ifac_start(N, 0);
+      for(;;)
+      {
+        long e;
+        GEN p;
+        if (!ifac_next(&part, &p, &e)) break;
+        e = Z_pvalrem(q, p, &q);
+        if (!handle_pe(&a, q, L, K, p, e)) { avma = av; return 0; }
+      }
+    }
+  }
+  if (!is_pm1(q))
+  {
+    if (ifac_isprime(q))
+    {
+      if (!handle_pe(&a, q, L, K, q, 1)) { avma = av; return 0; }
+    }
+    else
+    {
+      GEN part = ifac_start(q, 0);
+      for(;;)
+      {
+        long e;
+        GEN p;
+        if (!ifac_next(&part, &p, &e)) break;
+        if (!handle_pe(&a, q, L, K, p, e)) { avma = av; return 0; }
+      }
+    }
+  }
+END:
+  if (pt) *pt = gerepileupto(av, chinese1_coprime_Z(L));
+  return 1;
+}
+
+long
+issquareall(GEN x, GEN *pt)
+{
+  long tx = typ(x);
+  GEN F;
+  pari_sp av;
+
+  if (!pt) return issquare(x);
+  switch(tx)
+  {
+    case t_INT: return Z_issquareall(x, pt);
+    case t_FRAC: av = avma;
+      F = cgetg(3, t_FRAC);
+      if (   !Z_issquareall(gel(x,1), &gel(F,1))
+          || !Z_issquareall(gel(x,2), &gel(F,2))) { avma = av; return 0; }
+      *pt = F; return 1;
+
+    case t_POL: return polissquareall(x,pt);
+    case t_RFRAC: av = avma;
+      F = cgetg(3, t_RFRAC);
+      if (   !issquareall(gel(x,1), &gel(F,1))
+          || !polissquareall(gel(x,2), &gel(F,2))) { avma = av; return 0; }
+      *pt = F; return 1;
+
+    case t_REAL: case t_COMPLEX: case t_PADIC: case t_SER:
+      if (!issquare(x)) return 0;
+      *pt = gsqrt(x, DEFAULTPREC); return 1;
+
+    case t_INTMOD:
+      return Zn_ispower(gel(x,2), gel(x,1), gen_2, pt);
+
+    case t_FFELT: return FF_issquareall(x, pt);
+
+  }
+  pari_err_TYPE("issquareall",x);
+  return 0; /* not reached */
+}
+
+long
+issquare(GEN x)
+{
+  pari_sp av;
+  GEN a, p;
+  long i, v;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      return Z_issquare(x);
+
+    case t_REAL:
+      return (signe(x)>=0);
+
+    case t_INTMOD:
+      return Zn_ispower(gel(x,2), gel(x,1), gen_2, NULL);
+
+    case t_FRAC:
+      return Z_issquare(gel(x,1)) && Z_issquare(gel(x,2));
+
+    case t_FFELT: return FF_issquareall(x, NULL);
+
+    case t_COMPLEX:
+      return 1;
+
+    case t_PADIC:
+      a = gel(x,4); if (!signe(a)) return 1;
+      if (valp(x)&1) return 0;
+      p = gel(x,2);
+      if (!equaliu(p, 2)) return (kronecker(a,p) != -1);
+
+      v = precp(x); /* here p=2, a is odd */
+      if ((v>=3 && mod8(a) != 1 ) ||
+          (v==2 && mod4(a) != 1)) return 0;
+      return 1;
+
+    case t_POL:
+      return polissquareall(x,NULL);
+
+    case t_SER:
+      if (!signe(x)) return 1;
+      if (valp(x)&1) return 0;
+      return issquare(gel(x,2));
+
+    case t_RFRAC:
+      av = avma; i = issquare(gmul(gel(x,1),gel(x,2)));
+      avma = av; return i;
+  }
+  pari_err_TYPE("issquare",x);
+  return 0; /* not reached */
+}
+GEN gissquare(GEN x) { return issquare(x)? gen_1: gen_0; }
+GEN gissquareall(GEN x, GEN *pt) { return issquareall(x,pt)? gen_1: gen_0; }
+
+long
+ispolygonal(GEN x, GEN S, GEN *N)
+{
+  pari_sp av = avma;
+  GEN D, d, n;
+  if (typ(x) != t_INT) pari_err_TYPE("ispolygonal", x);
+  if (typ(S) != t_INT) pari_err_TYPE("ispolygonal", S);
+  if (cmpiu(S,3) < 0) pari_err_DOMAIN("ispolygonal","s","<", utoipos(3),S);
+  if (signe(x) < 0) return 0;
+  if (signe(x) == 0) { if (N) *N = gen_0; return 1; }
+  if (is_pm1(x)) { if (N) *N = gen_1; return 1; }
+  /* n = (sqrt( (8s - 16) x + (s-4)^2 ) + s - 4) / 2(s - 2) */
+  if (cmpiu(S, 1<<16) < 0) /* common case ! */
+  {
+    ulong s = S[2], r;
+    if (s == 4) return Z_issquareall(x, N);
+    if (s == 3)
+      D = addiu(shifti(x, 3), 1);
+    else
+      D = addiu(mului(8*s - 16, x), (s-4)*(s-4));
+    if (!Z_issquareall(D, &d)) { avma = av; return 0; }
+    if (s == 3)
+      d = subiu(d, 1);
+    else
+      d = addiu(d, s - 4);
+    n = diviu_rem(d, 2*s - 4, &r);
+    if (r) { avma = av; return 0; }
+  }
+  else
+  {
+    GEN r, S_2 = subiu(S,2), S_4 = subiu(S,4);
+    D = addii(mulii(shifti(S_2,3), x), sqri(S_4));
+    if (!Z_issquareall(D, &d)) { avma = av; return 0; }
+    d = addii(d, S_4);
+    n = dvmdii(shifti(d,-1), S_2, &r);
+    if (r != gen_0) { avma = av; return 0; }
+  }
+  if (N) *N = gerepileuptoint(av, n); else avma = av;
+  return 1;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                        PERFECT POWER                            **/
+/**                                                                 **/
+/*********************************************************************/
+static long
+polispower(GEN x, GEN K, GEN *pt)
+{
+  pari_sp av;
+  long v, l = degpol(x), k = itos(K);
+  GEN y, a, b;
+
+  if (!signe(x)) return 1;
+  if (l % k) return 0; /* degree not multiple of k */
+  v = RgX_valrem(x, &x);
+  if (v % k) return 0;
+  av = avma; a = gel(x,2); b = NULL;
+  if (!ispower(a, K, &b)) { avma = av; return 0; }
+  av = avma;
+  if (degpol(x))
+  {
+    x = RgX_Rg_div(x,a);
+    y = gtrunc(gsqrtn(RgX_to_ser(x,lg(x)), K, NULL, 0));
+    if (!RgX_equal(powgi(y, K), x)) { avma = av; return 0; }
+  }
+  else y = pol_1(varn(x));
+  if (pt)
+  {
+    if (!gequal1(a))
+    {
+      if (!b) b = gsqrtn(a, K, NULL, DEFAULTPREC);
+      y = gmul(b,y);
+    }
+    *pt = v? gerepilecopy(av, RgX_shift_shallow(y, v/k)): gerepileupto(av, y);
+  }
+  else avma = av;
+  return 1;
+}
+
+long
+Z_ispowerall(GEN x, ulong k, GEN *pt)
+{
+  long s = signe(x);
+  ulong mask;
+  if (!s) { if (pt) *pt = gen_0; return 1; }
+  if (s > 0) {
+    if (k == 2) return Z_issquareall(x, pt);
+    if (k == 3) { mask = 1; return !!is_357_power(x, pt, &mask); }
+    if (k == 5) { mask = 2; return !!is_357_power(x, pt, &mask); }
+    if (k == 7) { mask = 4; return !!is_357_power(x, pt, &mask); }
+    return is_kth_power(x, k, pt);
+  }
+  if (!odd(k)) return 0;
+  if (Z_ispowerall(absi(x), k, pt))
+  {
+    if (pt) *pt = negi(*pt);
+    return 1;
+  };
+  return 0;
+}
+
+/* is x a K-th power mod p ? Assume p prime. */
+int
+Fp_ispower(GEN x, GEN K, GEN p)
+{
+  pari_sp av = avma;
+  GEN p_1;
+  long r;
+  x = modii(x, p);
+  if (!signe(x) || equali1(x)) { avma = av; return 1; }
+  /* implies p > 2 */
+  p_1 = subiu(p,1);
+  K = gcdii(K, p_1);
+  if (equaliu(K, 2)) { r = kronecker(x,p); avma = av; return (r > 0); }
+  x = Fp_pow(x, diviiexact(p_1,K), p);
+  avma = av; return equali1(x);
+}
+
+/* x unit defined modulo 2^e, e > 0, p prime */
+static int
+U2_issquare(GEN x, long e)
+{
+  long r = signe(x)>=0?mod8(x):8-mod8(x);
+  if (e==1) return 1;
+  if (e==2) return (r&3L) == 1;
+  return r == 1;
+}
+/* x unit defined modulo p^e, e > 0, p prime */
+static int
+Up_issquare(GEN x, GEN p, long e)
+{ return (equaliu(p,2))? U2_issquare(x, e): kronecker(x,p)==1; }
+
+long
+Zn_issquare(GEN d, GEN fn)
+{
+  long j, np;
+  if (typ(d) != t_INT) pari_err_TYPE("Zn_issquare",d);
+  if (typ(fn) == t_INT) return Zn_ispower(d, fn, gen_2, NULL);
+  /* integer factorization */
+  np = nbrows(fn);
+  for (j = 1; j <= np; ++j)
+  {
+    GEN  r, p = gcoeff(fn, j, 1);
+    long e = itos(gcoeff(fn, j, 2));
+    long v = Z_pvalrem(d,p,&r);
+    if (v < e && (odd(v) || !Up_issquare(r, p, e-v))) return 0;
+  }
+  return 1;
+}
+
+long
+ispower(GEN x, GEN K, GEN *pt)
+{
+  GEN z;
+
+  if (!K) return gisanypower(x, pt);
+  if (typ(K) != t_INT) pari_err_TYPE("ispower",K);
+  if (signe(K) <= 0) pari_err_DOMAIN("ispower","exponent","<=",gen_0,K);
+  if (equali1(K)) { if (pt) *pt = gcopy(x); return 1; }
+  switch(typ(x)) {
+    case t_INT:
+      return Z_ispowerall(x, itou(K), pt);
+    case t_FRAC:
+    {
+      GEN a = gel(x,1), b = gel(x,2);
+      ulong k = itou(K);
+      if (pt) {
+        z = cgetg(3, t_FRAC);
+        if (Z_ispowerall(a, k, &a) && Z_ispowerall(b, k, &b)) {
+          *pt = z; gel(z,1) = a; gel(z,2) = b; return 1;
+        }
+        avma = (pari_sp)(z + 3); return 0;
+      }
+      return Z_ispower(a, k) && Z_ispower(b, k);
+    }
+    case t_INTMOD:
+      return Zn_ispower(gel(x,2), gel(x,1), K, pt);
+    case t_FFELT:
+      return FF_ispower(x, K, pt);
+
+    case t_PADIC:
+      z = Qp_sqrtn(x, K, NULL);
+      if (!z) return 0;
+      if (pt) *pt = z;
+      return 1;
+
+    case t_POL:
+      return polispower(x, K, pt);
+    case t_RFRAC: {
+      GEN a = gel(x,1), b = gel(x,2);
+      if (pt) {
+        z = cgetg(3, t_RFRAC);
+        if (ispower(a, K, &a) && polispower(b, K, &b)) {
+          *pt = z; gel(z,1) = a; gel(z,2) = b; return 1;
+        }
+        avma = (pari_sp)(z + 3); return 0;
+      }
+      return (ispower(a, K, NULL) && polispower(b, K, NULL));
+    }
+    case t_REAL:
+      if (signe(x) < 0 && !mpodd(K)) return 0;
+    case t_COMPLEX:
+      if (pt) *pt = gsqrtn(x, K, NULL, DEFAULTPREC);
+      return 1;
+
+    case t_SER:
+      if (signe(x) && (!dvdsi(valp(x), K) || !ispower(gel(x,2), K, NULL)))
+        return 0;
+      if (pt) *pt = gsqrtn(x, K, NULL, DEFAULTPREC);
+      return 1;
+
+    default: pari_err_TYPE("ispower",x);
+    return 0; /* not reached */
+  }
+}
+
+long
+gisanypower(GEN x, GEN *pty)
+{
+  long tx = typ(x);
+  ulong k, h;
+  if (tx == t_INT) return Z_isanypower(x, pty);
+  if (tx == t_FRAC)
+  {
+    pari_sp av = avma;
+    GEN fa, P, E, a = gel(x,1), b = gel(x,2);
+    long i, j, p, e;
+    int sw = (absi_cmp(a, b) > 0);
+
+    if (sw) swap(a, b);
+    k = Z_isanypower(a, pty? &a: NULL);
+    if (!k)
+    { /* a = -1,1 or not a pure power */
+      if (!is_pm1(a)) { avma = av; return 0; }
+      if (signe(a) < 0) b = negi(b);
+      k = Z_isanypower(b, pty? &b: NULL);
+      if (!k || !pty) { avma = av; return k; }
+      *pty = gerepileupto(av, ginv(b));
+      return k;
+    }
+    fa = factoru(k);
+    P = gel(fa,1);
+    E = gel(fa,2); h = k;
+    for (i = lg(P) - 1; i > 0; i--)
+    {
+      p = P[i];
+      e = E[i];
+      for (j = 0; j < e; j++)
+        if (!is_kth_power(b, p, &b)) break;
+      if (j < e) k /= upowuu(p, e - j);
+    }
+    if (k == 1) { avma = av; return 0; }
+    if (!pty) { avma = av; return k; }
+    if (k != h) a = powiu(a, h/k);
+    *pty = gerepilecopy(av, mkfrac(a, b));
+    return k;
+  }
+  pari_err_TYPE("gisanypower", x);
+  return 0; /* not reached */
+}
+
+/* v_p(x) = e != 0 for some p; return ispower(x,,&x), updating x.
+ * No need to optimize for 2,3,5,7 powers (done before) */
+static long
+split_exponent(ulong e, GEN *x)
+{
+  GEN fa, P, E;
+  long i, j, l, k = 1;
+  if (e == 1) return 1;
+  fa = factoru(e);
+  P = gel(fa,1);
+  E = gel(fa,2); l = lg(P);
+  for (i = 1; i < l; i++)
+  {
+    ulong p = P[i];
+    for (j = 0; j < E[i]; j++)
+    {
+      GEN y;
+      if (!is_kth_power(*x, p, &y)) break;
+      k *= p; *x = y;
+    }
+  }
+  return k;
+}
+
+static long
+Z_isanypower_nosmalldiv(GEN *px)
+{ /* any prime divisor of x is > 102 */
+  const double LOG2_103 = 6.6865; /* lower bound for log_2(103) */
+  const double LOG103 = 4.6347; /* lower bound for log(103) */
+  forprime_t T;
+  ulong mask = 7, e2;
+  long k, ex;
+  GEN y, x = *px;
+
+  k = 1;
+  while (Z_issquareall(x, &y)) { k <<= 1; x = y; }
+  while ( (ex = is_357_power(x, &y, &mask)) ) { k *= ex; x = y; }
+  e2 = (ulong)((expi(x) + 1) / LOG2_103); /* >= log_103 (x) */
+  if (u_forprime_init(&T, 11, e2))
+  {
+    GEN logx = NULL;
+    const ulong Q = 30011; /* prime */
+    ulong p, xmodQ;
+    double dlogx = 0;
+    /* cut off at x^(1/p) ~ 2^30 bits which seems to be about optimum;
+     * for large p the modular checks are no longer competitively fast */
+    while ( (ex = is_pth_power(x, &y, &T, 30)) )
+    {
+      k *= ex; x = y;
+      e2 = (ulong)((expi(x) + 1) / LOG2_103);
+      u_forprime_restrict(&T, e2);
+    }
+    if (DEBUGLEVEL>4) err_printf("Z_isanypower: now k=%ld, x=%ld-bit\n", k, expi(x));
+    xmodQ = umodiu(x, Q);
+    /* test Q | x, just in case */
+    if (!xmodQ) return k * split_exponent(Z_lval(x,Q), px);
+    /* x^(1/p) < 2^31 */
+    p = T.p;
+    if (p <= e2)
+    {
+      logx = logr_abs( itor(x, DEFAULTPREC) );
+      dlogx = rtodbl(logx);
+      e2 = (ulong)(dlogx / LOG103); /* >= log_103(x) */
+    }
+    while (p && p <= e2)
+    { /* is x a p-th power ? By computing y = round(x^(1/p)).
+       * Check whether y^p = x, first mod Q, then exactly. */
+      pari_sp av = avma;
+      long e;
+      GEN logy = divru(logx, p), y = grndtoi(mpexp(logy), &e);
+      ulong ymodQ = umodiu(y,Q);
+      if (e >= -10 || Fl_powu(ymodQ, p % (Q-1), Q) != xmodQ
+                   || !equalii(powiu(y, p), x)) avma = av;
+      else
+      {
+        k *= p; x = y; xmodQ = ymodQ; logx = logy; dlogx /= p;
+        e2 = (ulong)(dlogx / LOG103); /* >= log_103(x) */
+        u_forprime_restrict(&T, e2);
+        continue; /* if success, retry same p */
+      }
+      p = u_forprime_next(&T);
+    }
+  }
+  *px = x; return k;
+}
+
+static ulong tinyprimes[] = {
+  2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+  73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
+  157, 163, 167, 173, 179, 181, 191, 193, 197, 199
+};
+
+/* disregard the sign of x, caller will take care of x < 0 */
+static long
+Z_isanypower_aux(GEN x, GEN *pty)
+{
+  long ex, v, i, l, k;
+  GEN y, P, E;
+  ulong mask, e = 0;
+
+  if (absi_cmp(x, gen_2) < 0) return 0; /* -1,0,1 */
+
+  k = l = 1;
+  P = cgetg(26 + 1, t_VECSMALL);
+  E = cgetg(26 + 1, t_VECSMALL);
+  /* trial division */
+  for(i = 0; i < 26; i++)
+  {
+    ulong p = tinyprimes[i];
+    int stop;
+    v = Z_lvalrem_stop(&x, p, &stop);
+    if (v)
+    {
+      P[l] = p;
+      E[l] = v; l++;
+      e = ugcd(e, v); if (e == 1) goto END;
+    }
+    if (stop) {
+      if (is_pm1(x)) k = e;
+      goto END;
+    }
+  }
+
+  if (e)
+  { /* Bingo. Result divides e */
+    long v3, v5, v7;
+    ulong e2 = e;
+    v = u_lvalrem(e2, 2, &e2);
+    if (v)
+    {
+      for (i = 0; i < v; i++)
+      {
+        if (!Z_issquareall(x, &y)) break;
+        k <<= 1; x = y;
+      }
+    }
+    mask = 0;
+    v3 = u_lvalrem(e2, 3, &e2); if (v3) mask = 1;
+    v5 = u_lvalrem(e2, 5, &e2); if (v5) mask |= 2;
+    v7 = u_lvalrem(e2, 7, &e2); if (v7) mask |= 4;
+    while ( (ex = is_357_power(x, &y, &mask)) ) {
+      x = y;
+      switch(ex)
+      {
+        case 3: k *= 3; if (--v3 == 0) mask &= ~1; break;
+        case 5: k *= 5; if (--v5 == 0) mask &= ~2; break;
+        case 7: k *= 7; if (--v7 == 0) mask &= ~4; break;
+      }
+    }
+    k *= split_exponent(e2, &x);
+  }
+  else
+    k = Z_isanypower_nosmalldiv(&x);
+END:
+  if (pty && k != 1)
+  {
+    if (e)
+    { /* add missing small factors */
+      y = powuu(P[1], E[1] / k);
+      for (i = 2; i < l; i++) y = mulii(y, powuu(P[i], E[i] / k));
+      x = equali1(x)? y: mulii(x,y);
+    }
+    *pty = x;
+  }
+  return k == 1? 0: k;
+}
+
+long
+Z_isanypower(GEN x, GEN *pty)
+{
+  pari_sp av = avma;
+  long k = Z_isanypower_aux(x, pty);
+  if (!k) { avma = av; return 0; }
+  if (signe(x) < 0)
+  {
+    long v = vals(k);
+    if (v)
+    {
+      GEN y;
+      k >>= v;
+      if (k == 1) { avma = av; return 0; }
+      if (!pty) { avma = av; return k; }
+      y = *pty;
+      y = powiu(y, 1<<v);
+      togglesign(y);
+      *pty = gerepileuptoint(av, y);
+      return k;
+    }
+    if (pty) togglesign_safe(pty);
+  }
+  if (pty) *pty = gerepilecopy(av, *pty); else avma = av;
+  return k;
+}
+
+/* Faster than */
+/*   !cmpii(n, int2n(vali(n))) */
+/*   !cmpis(shifti(n, -vali(n)), 1) */
+/*   expi(n) == vali(n) */
+/*   hamming(n) == 1 */
+/* even for single-word values, and much faster for multiword values. */
+/* If all you have is a word, you can just use n & !(n & (n-1)). */
+long
+Z_ispow2(GEN n)
+{
+  GEN xp;
+  long i, lx;
+  ulong u;
+  if (signe(n) != 1) return 0;
+  xp = int_LSW(n);
+  lx = lgefint(n);
+  u = *xp;
+  for (i = 3; i < lx; ++i)
+  {
+    if (u) return 0;
+    xp = int_nextW(xp);
+    u = *xp;
+  }
+  return !(u & (u-1)); /* faster than hamming_word(u) == 1 */
+}
+
+long
+isprimepower(GEN n, GEN *pt)
+{
+  pari_sp av = avma;
+  long i, v;
+
+  if (typ(n) != t_INT) pari_err_TYPE("isprimepower", n);
+  if (signe(n) <= 0) return 0;
+
+  if (lgefint(n) == 3)
+  {
+    ulong p;
+    v = uisprimepower(n[2], &p);
+    if (v)
+    {
+      if (pt) *pt = utoipos(p);
+      return v;
+    }
+    return 0;
+  }
+  for (i = 0; i < 26; i++)
+  {
+    ulong p = tinyprimes[i];
+    v = Z_lvalrem(n, p, &n);
+    if (v)
+    {
+      avma = av;
+      if (!is_pm1(n)) return 0;
+      if (pt) *pt = utoipos(p);
+      return v;
+    }
+  }
+  /* p | n => p >= 103 */
+  v = Z_isanypower_nosmalldiv(&n); /* expensive */
+  if (!isprime(n)) { avma = av; return 0; }
+  if (pt) *pt = gerepilecopy(av, n); else avma = av;
+  return v;
+}
+
+long
+uisprimepower(ulong n, ulong *pp)
+{ /* We must have CUTOFF^11 >= ULONG_MAX and CUTOFF^3 < ULONG_MAX.
+   * Tests suggest that 200-300 is the best range for 64-bit platforms. */
+  const ulong CUTOFF = 200UL;
+  const long TINYCUTOFF = 46;  /* tinyprimes[45] = 199 */
+  const ulong CUTOFF3 = CUTOFF*CUTOFF*CUTOFF;
+#ifdef LONG_IS_64BIT
+  /* primes preceeding the appropriate root of ULONG_MAX. */
+  const ulong ROOT9 = 137;
+  const ulong ROOT8 = 251;
+  const ulong ROOT7 = 563;
+  const ulong ROOT5 = 7129;
+  const ulong ROOT4 = 65521;
+#else
+  const ulong ROOT9 = 11;
+  const ulong ROOT8 = 13;
+  const ulong ROOT7 = 23;
+  const ulong ROOT5 = 83;
+  const ulong ROOT4 = 251;
+#endif
+  ulong mask;
+  long v, i;
+  int e;
+  if (n < 2) return 0;
+  if (!odd(n)) {
+    if (n & (n-1)) return 0;
+    *pp = 2; return vals(n);
+  }
+  if (n < 8) { *pp = n; return 1; } /* 3,5,7 */
+  for (i = 1/*skip p=2*/; i < TINYCUTOFF; i++)
+  {
+    ulong p = tinyprimes[i];
+    if (n % p == 0)
+    {
+      v = u_lvalrem(n, p, &n);
+      if (n == 1) { *pp = p; return v; }
+      return 0;
+    }
+  }
+  /* p | n => p >= CUTOFF */
+
+  if (n < CUTOFF3)
+  {
+    if (n < CUTOFF*CUTOFF || uisprime_101(n)) { *pp = n; return 1; }
+    if (uissquareall(n, &n)) { *pp = n; return 2; }
+    return 0;
+  }
+
+  /* Check for squares, fourth powers, and eighth powers as appropriate. */
+  v = 1;
+  if (uissquareall(n, &n)) {
+    v <<= 1;
+    if (CUTOFF <= ROOT4 && uissquareall(n, &n)) {
+      v <<= 1;
+      if (CUTOFF <= ROOT8 && uissquareall(n, &n)) v <<= 1;
+    }
+  }
+
+  if (CUTOFF > ROOT5) mask = 1;
+  else
+  {
+    const ulong CUTOFF5 = CUTOFF3*CUTOFF*CUTOFF;
+    if (n < CUTOFF5) mask = 1; else mask = 3;
+    if (CUTOFF <= ROOT7)
+    {
+      const ulong CUTOFF7 = CUTOFF5*CUTOFF*CUTOFF;
+      if (n >= CUTOFF7) mask = 7;
+    }
+  }
+
+  if (CUTOFF <= ROOT9 && (e = uis_357_power(n, &n, &mask))) { v *= e; mask=1; }
+  if ((e = uis_357_power(n, &n, &mask))) v *= e;
+
+  if (uisprime_101(n)) { *pp = n; return v; }
+  return 0;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                        KRONECKER SYMBOL                         **/
+/**                                                                 **/
+/*********************************************************************/
+/* t = 3,5 mod 8 ?  (= 2 not a square mod t) */
+static int
+ome(long t)
+{
+  switch(t & 7)
+  {
+    case 3:
+    case 5: return 1;
+    default: return 0;
+  }
+}
+/* t a t_INT, is t = 3,5 mod 8 ? */
+static int
+gome(GEN t)
+{ return signe(t)? ome( mod2BIL(t) ): 0; }
+
+/* t a t_INT, return 1 if t = 3 (mod 4), 0 otherwise */
+static int
+eps(GEN t)
+{
+  switch(signe(t))
+  {
+    case -1: return mod4(t) == 1;
+    case 1:  return mod4(t) == 3;
+    default: return 0;
+  }
+}
+
+/* assume y odd, return kronecker(x,y) * s */
+static long
+krouu_s(ulong x, ulong y, long s)
+{
+  ulong x1 = x, y1 = y, z;
+  while (x1)
+  {
+    long r = vals(x1);
+    if (r)
+    {
+      if (odd(r) && ome(y1)) s = -s;
+      x1 >>= r;
+    }
+    if (x1 & y1 & 2) s = -s;
+    z = y1 % x1; y1 = x1; x1 = z;
+  }
+  return (y1 == 1)? s: 0;
+}
+
+long
+kronecker(GEN x, GEN y)
+{
+  pari_sp av = avma, lim;
+  long s = 1, r;
+  ulong xu, yu;
+
+  if (typ(x) != t_INT) pari_err_TYPE("kronecker",x);
+  if (typ(y) != t_INT) pari_err_TYPE("kronecker",y);
+  switch (signe(y))
+  {
+    case -1: y = negi(y); if (signe(x) < 0) s = -1; break;
+    case 0: return is_pm1(x);
+  }
+  r = vali(y);
+  if (r)
+  {
+    if (!mpodd(x)) { avma = av; return 0; }
+    if (odd(r) && gome(x)) s = -s;
+    y = shifti(y,-r);
+  }
+  lim = stack_lim(av,2);
+  x = modii(x,y);
+  while (lgefint(x) > 3) /* x < y */
+  {
+    GEN z;
+    r = vali(x);
+    if (r)
+    {
+      if (odd(r) && gome(y)) s = -s;
+      x = shifti(x,-r);
+    }
+    /* x=3 mod 4 && y=3 mod 4 ? (both are odd here) */
+    if (mod2BIL(x) & mod2BIL(y) & 2) s = -s;
+    z = remii(y,x); y = x; x = z;
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"kronecker");
+      gerepileall(av, 2, &x, &y);
+    }
+  }
+  xu = itou(x);
+  if (!xu) return is_pm1(y)? s: 0;
+  r = vals(xu);
+  if (r)
+  {
+    if (odd(r) && gome(y)) s = -s;
+    xu >>= r;
+  }
+  /* x=3 mod 4 && y=3 mod 4 ? (both are odd here) */
+  if (xu & mod2BIL(y) & 2) s = -s;
+  yu = umodiu(y, xu);
+  avma = av; return krouu_s(yu, xu, s);
+}
+
+long
+krois(GEN x, long y)
+{
+  ulong yu;
+  long s = 1, r;
+
+  if (y <= 0)
+  {
+    if (y == 0) return is_pm1(x);
+    yu = (ulong)-y; if (signe(x) < 0) s = -1;
+  }
+  else
+    yu = (ulong)y;
+  r = vals(yu);
+  if (r)
+  {
+    if (!mpodd(x)) return 0;
+    if (odd(r) && gome(x)) s = -s;
+    yu >>= r;
+  }
+  return krouu_s(umodiu(x, yu), yu, s);
+}
+/* assume y != 0 */
+long
+kroiu(GEN x, ulong y)
+{
+  long s = 1, r = vals(y);
+  if (r)
+  {
+    if (!mpodd(x)) return 0;
+    if (odd(r) && gome(x)) s = -s;
+    y >>= r;
+  }
+  return krouu_s(umodiu(x, y), y, s);
+}
+
+long
+krosi(long x, GEN y)
+{
+  const pari_sp av = avma;
+  long s = 1, r;
+  ulong u, xu;
+
+  switch (signe(y))
+  {
+    case -1: y = negi(y); if (x < 0) s = -1; break;
+    case 0: return (x==1 || x==-1);
+  }
+  r = vali(y);
+  if (r)
+  {
+    if (!odd(x)) { avma = av; return 0; }
+    if (odd(r) && ome(x)) s = -s;
+    y = shifti(y,-r);
+  }
+  if (x < 0) { x = -x; if (mod4(y) == 3) s = -s; }
+  xu = (ulong)x;
+  if (lgefint(y) == 3)
+    return krouu_s(xu, itou(y), s);
+  if (!xu) return 0; /* y != 1 */
+  r = vals(xu);
+  if (r)
+  {
+    if (odd(r) && gome(y)) s = -s;
+    xu >>= r;
+  }
+  /* xu=3 mod 4 && y=3 mod 4 ? (both are odd here) */
+  if (xu & mod2BIL(y) & 2) s = -s;
+  u = umodiu(y, xu);
+  avma = av; return krouu_s(u, xu, s);
+}
+
+long
+kross(long x, long y)
+{
+  ulong yu;
+  long s = 1, r;
+
+  if (y <= 0)
+  {
+    if (y == 0) return (labs(x)==1);
+    yu = (ulong)-y; if (x < 0) s = -1;
+  }
+  else
+    yu = (ulong)y;
+  r = vals(yu);
+  if (r)
+  {
+    if (!odd(x)) return 0;
+    if (odd(r) && ome(x)) s = -s;
+    yu >>= r;
+  }
+  x %= (long)yu; if (x < 0) x += yu;
+  return krouu_s((ulong)x, yu, s);
+}
+
+long
+krouu(ulong x, ulong y)
+{
+  long r;
+  if (y & 1) return krouu_s(x, y, 1);
+  if (!odd(x)) return 0;
+  r = vals(y);
+  return krouu_s(x, y >> r, (odd(r) && ome(x))? -1: 1);
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                          HILBERT SYMBOL                         **/
+/**                                                                 **/
+/*********************************************************************/
+/* x,y are t_INT or t_REAL */
+static long
+mphilbertoo(GEN x, GEN y)
+{
+  long sx = signe(x), sy = signe(y);
+  if (!sx || !sy) return 0;
+  return (sx < 0 && sy < 0)? -1: 1;
+}
+
+long
+hilbertii(GEN x, GEN y, GEN p)
+{
+  pari_sp av;
+  long oddvx, oddvy, z;
+
+  if (!p) return mphilbertoo(x,y);
+  if (is_pm1(p) || signe(p) < 0) pari_err_PRIME("hilbertii",p);
+  if (!signe(x) || !signe(y)) return 0;
+  av = avma;
+  oddvx = odd(Z_pvalrem(x,p,&x));
+  oddvy = odd(Z_pvalrem(y,p,&y));
+  /* x, y are p-units, compute hilbert(x * p^oddvx, y * p^oddvy, p) */
+  if (equaliu(p, 2))
+  {
+    z = (eps(x) && eps(y))? -1: 1;
+    if (oddvx && gome(y)) z = -z;
+    if (oddvy && gome(x)) z = -z;
+  }
+  else
+  {
+    z = (oddvx && oddvy && eps(p))? -1: 1;
+    if (oddvx && kronecker(y,p) < 0) z = -z;
+    if (oddvy && kronecker(x,p) < 0) z = -z;
+  }
+  avma = av; return z;
+}
+
+static void
+err_prec(void) { pari_err_PREC("hilbert"); }
+static void
+err_p(GEN p, GEN q) { pari_err_MODULUS("hilbert", p,q); }
+static void
+err_oo(GEN p) { pari_err_MODULUS("hilbert", p, strtoGENstr("oo")); }
+
+/* x t_INTMOD, *pp = prime or NULL [ unset, set it to x.mod ].
+ * Return lift(x) provided it's p-adic accuracy is large enough to decide
+ * hilbert()'s value [ problem at p = 2 ] */
+static GEN
+lift_intmod(GEN x, GEN *pp)
+{
+  GEN p = *pp, N = gel(x,1);
+  x = gel(x,2);
+  if (!p)
+  {
+    *pp = p = N;
+    switch(itos_or_0(p))
+    {
+      case 2:
+      case 4: err_prec();
+    }
+    return x;
+  }
+  if (!signe(p)) err_oo(N);
+  if (equaliu(p,2))
+  { if (vali(N) <= 2) err_prec(); }
+  else
+  { if (!dvdii(N,p)) err_p(N,p); }
+  if (!signe(x)) err_prec();
+  return x;
+}
+/* x t_PADIC, *pp = prime or NULL [ unset, set it to x.p ].
+ * Return lift(x)*p^(v(x) mod 2) provided it's p-adic accuracy is large enough
+ * to decide hilbert()'s value [ problem at p = 2 ]*/
+static GEN
+lift_padic(GEN x, GEN *pp)
+{
+  GEN p = *pp, q = gel(x,2), y = gel(x,4);
+  if (!p) *pp = p = q;
+  else if (!equalii(p,q)) err_p(p, q);
+  if (equaliu(p,2) && precp(x) <= 2) err_prec();
+  if (!signe(y)) err_prec();
+  return odd(valp(x))? mulii(p,y): y;
+}
+
+long
+hilbert(GEN x, GEN y, GEN p)
+{
+  pari_sp av = avma;
+  long tx = typ(x), ty = typ(y), z;
+
+  if (p && typ(p) != t_INT) pari_err_TYPE("hilbert",p);
+  if (tx == t_REAL)
+  {
+    if (p && signe(p)) err_oo(p);
+    switch (ty)
+    {
+      case t_INT:
+      case t_REAL: return mphilbertoo(x,y);
+      case t_FRAC: return mphilbertoo(x,gel(y,1));
+      default: pari_err_TYPE2("hilbert",x,y);
+    }
+  }
+  if (ty == t_REAL)
+  {
+    if (p && signe(p)) err_oo(p);
+    switch (tx)
+    {
+      case t_INT:
+      case t_REAL: return mphilbertoo(x,y);
+      case t_FRAC: return mphilbertoo(gel(x,1),y);
+      default: pari_err_TYPE2("hilbert",x,y);
+    }
+  }
+  if (tx == t_INTMOD) { x = lift_intmod(x, &p); tx = t_INT; }
+  if (ty == t_INTMOD) { y = lift_intmod(y, &p); ty = t_INT; }
+
+  if (tx == t_PADIC) { x = lift_padic(x, &p); tx = t_INT; }
+  if (ty == t_PADIC) { y = lift_padic(y, &p); ty = t_INT; }
+
+  if (tx == t_FRAC) { tx = t_INT; x = p? mulii(gel(x,1),gel(x,2)): gel(x,1); }
+  if (ty == t_FRAC) { ty = t_INT; y = p? mulii(gel(y,1),gel(y,2)): gel(y,1); }
+
+  if (tx != t_INT || ty != t_INT) pari_err_TYPE2("hilbert",x,y);
+  if (p && !signe(p)) p = NULL;
+  z = hilbertii(x,y,p); avma = av; return z;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       SQUARE ROOT MODULO p                      */
+/*                                                                 */
+/*******************************************************************/
+
+/* Tonelli-Shanks. Assume p is prime and (a,p) != -1. */
+ulong
+Fl_sqrt(ulong a, ulong p)
+{
+  long i, e, k;
+  ulong p1, q, v, y, w, m;
+
+  if (!a) return 0;
+  p1 = p - 1; e = vals(p1);
+  if (e == 0) /* p = 2 */
+  {
+    if (p != 2) pari_err_PRIME("Fl_sqrt [modulus]",utoi(p));
+    return ((a & 1) == 0)? 0: 1;
+  }
+  q = p1 >> e; /* q = (p-1)/2^oo is odd */
+  if (e == 1) y = p1;
+  else /* look for an odd power of a primitive root */
+    for (k=2; ; k++)
+    { /* loop terminates for k < p (even if p composite) */
+      i = krouu(k, p);
+      if (i >= 0)
+      {
+        if (i) continue;
+        pari_err_PRIME("Fl_sqrt [modulus]",utoi(p));
+      }
+      y = m = Fl_powu(k, q, p);
+      for (i=1; i<e; i++)
+        if ((m = Fl_sqr(m,p)) == 1) break;
+      if (i == e) break; /* success */
+    }
+
+  p1 = Fl_powu(a, q >> 1, p); /* a ^ [(q-1)/2] */
+  if (!p1) return 0;
+  v = Fl_mul(a, p1, p);
+  w = Fl_mul(v, p1, p);
+  while (w != 1)
+  { /* a*w = v^2, y primitive 2^e-th root of 1
+       a square --> w even power of y, hence w^(2^(e-1)) = 1 */
+    p1 = Fl_sqr(w,p);
+    for (k=1; p1 != 1 && k < e; k++) p1 = Fl_sqr(p1,p);
+    if (k == e) return ~0UL;
+    /* w ^ (2^k) = 1 --> w = y ^ (u * 2^(e-k)), u odd */
+    p1 = y;
+    for (i=1; i < e-k; i++) p1 = Fl_sqr(p1,p);
+    y = Fl_sqr(p1, p); e = k;
+    w = Fl_mul(y, w, p);
+    v = Fl_mul(v, p1, p);
+  }
+  p1 = p - v; if (v > p1) v = p1;
+  return v;
+}
+
+/* Cipolla is better than Tonelli-Shanks when e = v_2(p-1) is "too big".
+ * Otherwise, is a constant times worse; for p = 3 (mod 4), is about 3 times worse,
+ * and in average is about 2 or 2.5 times worse. But try both algorithms for
+ * S(n) = (2^n+3)^2-8 with n = 750, 771, 779, 790, 874, 1176, 1728, 2604, etc.
+ *
+ * If X^2 := t^2 - a  is not a square in F_p (so X is in F_p^2), then
+ *   (t+X)^(p+1) = (t-X)(t+X) = a,   hence  sqrt(a) = (t+X)^((p+1)/2)  in F_p^2.
+ * If (a|p)=1, then sqrt(a) is in F_p.
+ * cf: LNCS 2286, pp 430-434 (2002)  [Gonzalo Tornaria] */
+
+/* compute y^2, y = y[1] + y[2] X */
+static GEN
+sqrt_Cipolla_sqr(void *data, GEN y)
+{
+  GEN u = gel(y,1), v = gel(y,2), p = gel(data,2), n = gel(data,3);
+  GEN u2 = sqri(u), v2 = sqri(v);
+  v = subii(sqri(addii(v,u)), addii(u2,v2));
+  u = addii(u2, mulii(v2,n));
+  /* NOT mkvec2: must be gerepileupto-able */
+  retmkvec2(modii(u,p), modii(v,p));
+}
+/* compute (t+X) y^2 */
+static GEN
+sqrt_Cipolla_msqr(void *data, GEN y)
+{
+  GEN u = gel(y,1), v = gel(y,2), a = gel(data,1), p = gel(data,2), gt = gel(data,4);
+  ulong t = gt[2];
+  GEN d = addii(u, mului(t,v)), d2= sqri(d);
+  GEN b = remii(mulii(a,v), p);
+  u = subii(mului(t,d2), mulii(b,addii(u,d)));
+  v = subii(d2, mulii(b,v));
+  /* NOT mkvec2: must be gerepileupto-able */
+  retmkvec2(modii(u,p), modii(v,p));
+}
+/* assume a reduced mod p [ otherwise correct but inefficient ] */
+static GEN
+sqrt_Cipolla(GEN a, GEN p)
+{
+  pari_sp av1;
+  GEN u, v, n, y, pov2;
+  ulong t;
+
+  if (kronecker(a, p) < 0) return NULL;
+  pov2 = shifti(p,-1);
+  if (cmpii(a,pov2) > 0) a = subii(a,p); /* center: avoid multiplying by huge base*/
+
+  av1 = avma;
+  for(t=1; ; t++)
+  {
+    n = subsi((long)(t*t), a);
+    if (kronecker(n, p) < 0) break;
+    avma = av1;
+  }
+
+  /* compute (t+X)^((p-1)/2) =: u+vX */
+  u = utoipos(t);
+  y = gen_pow_fold(mkvec2(u, gen_1), pov2, mkvec4(a,p,n,u),
+                         sqrt_Cipolla_sqr, sqrt_Cipolla_msqr);
+  /* Now u+vX = (t+X)^((p-1)/2); thus
+   *   (u+vX)(t+X) = sqrt(a) + 0 X
+   * Whence,
+   *   sqrt(a) = (u+vt)t - v*a
+   *   0       = (u+vt)
+   * Thus a square root is v*a */
+
+  v = Fp_mul(gel(y, 2), a, p);
+  if (cmpii(v,pov2) > 0) v = subii(p,v);
+  return v;
+}
+
+#define sqrmod(x,p) (remii(sqri(x),p))
+
+/* Tonelli-Shanks. Assume p is prime and return NULL if (a,p) = -1. */
+GEN
+Fp_sqrt(GEN a, GEN p)
+{
+  pari_sp av = avma, av1,lim;
+  long i, k, e;
+  GEN p1, q, v, y, w, m;
+
+  if (typ(a) != t_INT) pari_err_TYPE("Fp_sqrt",a);
+  if (typ(p) != t_INT) pari_err_TYPE("Fp_sqrt",p);
+  if (signe(p) <= 0 || equali1(p)) pari_err_PRIME("Fp_sqrt",p);
+  if (lgefint(p) == 3)
+  {
+    ulong u = (ulong)p[2]; u = Fl_sqrt(umodiu(a, u), u);
+    if (u == ~0UL) return NULL;
+    return utoi(u);
+  }
+
+  p1 = addsi(-1,p); e = vali(p1);
+  a = modii(a, p);
+
+  /* On average, the algorithm of Cipolla is better than the algorithm of
+   * Tonelli and Shanks if and only if e(e-1)>8*log2(n)+20
+   * see LNCS 2286 pp 430 [GTL] */
+  if (e*(e-1) > 20 + 8 * expi(p))
+  {
+    v = sqrt_Cipolla(a,p);
+    if (!v) { avma = av; return NULL; }
+    return gerepileuptoint(av,v);
+  }
+
+  if (e == 0) /* p = 2 */
+  {
+    avma = av;
+    if (!equaliu(p,2)) pari_err_PRIME("Fp_sqrt [modulus]",p);
+    if (!signe(a) || !mod2(a)) return gen_0;
+    return gen_1;
+  }
+  q = shifti(p1,-e); /* q = (p-1)/2^oo is odd */
+  if (e == 1) y = p1;
+  else /* look for an odd power of a primitive root */
+    for (k=2; ; k++)
+    { /* loop terminates for k < p (even if p composite) */
+
+      i = krosi(k,p);
+      if (i >= 0)
+      {
+        if (i) continue;
+        pari_err_PRIME("Fp_sqrt [modulus]",p);
+      }
+      av1 = avma;
+      y = m = Fp_pow(utoipos((ulong)k),q,p);
+      for (i=1; i<e; i++)
+        if (gequal1(m = sqrmod(m,p))) break;
+      if (i == e) break; /* success */
+      avma = av1;
+    }
+
+  p1 = Fp_pow(a, shifti(q,-1), p); /* a ^ [(q-1)/2] */
+  if (!signe(p1)) { avma=av; return gen_0; }
+  v = Fp_mul(a, p1, p);
+  w = Fp_mul(v, p1, p);
+  lim = stack_lim(av,1);
+  while (!equali1(w))
+  { /* a*w = v^2, y primitive 2^e-th root of 1
+       a square --> w even power of y, hence w^(2^(e-1)) = 1 */
+    p1 = sqrmod(w,p);
+    for (k=1; !equali1(p1) && k < e; k++) p1 = sqrmod(p1,p);
+    if (k == e) { avma=av; return NULL; } /* p composite or (a/p) != 1 */
+    /* w ^ (2^k) = 1 --> w = y ^ (u * 2^(e-k)), u odd */
+    p1 = y;
+    for (i=1; i < e-k; i++) p1 = sqrmod(p1,p);
+    y = sqrmod(p1, p); e = k;
+    w = Fp_mul(y, w, p);
+    v = Fp_mul(v, p1, p);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"Fp_sqrt");
+      gerepileall(av,3, &y,&w,&v);
+    }
+  }
+  av1 = avma;
+  p1 = subii(p,v); if (cmpii(v,p1) > 0) v = p1; else avma = av1;
+  return gerepileuptoint(av, v);
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                        GCD & BEZOUT                             **/
+/**                                                                 **/
+/*********************************************************************/
+
+GEN
+lcmii(GEN x, GEN y)
+{
+  pari_sp av;
+  GEN a, b;
+  if (!signe(x) || !signe(y)) return gen_0;
+  av = avma;
+  a = gcdii(x,y); if (!equali1(a)) y = diviiexact(y,a);
+  b = mulii(x,y); setabssign(b); return gerepileuptoint(av, b);
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                      CHINESE REMAINDERS                         **/
+/**                                                                 **/
+/*********************************************************************/
+
+/*  P.M. & M.H.
+ *
+ *  Chinese Remainder Theorem.  x and y must have the same type (integermod,
+ *  polymod, or polynomial/vector/matrix recursively constructed with these
+ *  as coefficients). Creates (with the same type) a z in the same residue
+ *  class as x and the same residue class as y, if it is possible.
+ *
+ *  We also allow (during recursion) two identical objects even if they are
+ *  not integermod or polymod. For example, if
+ *
+ *    x = [1. mod(5, 11), mod(X + mod(2, 7), X^2 + 1)]
+ *    y = [1, mod(7, 17), mod(X + mod(0, 3), X^2 + 1)],
+ *
+ *  then chinese(x, y) returns
+ *
+ *    [1, mod(16, 187), mod(X + mod(9, 21), X^2 + 1)]
+ *
+ *  Someone else may want to allow power series, complex numbers, and
+ *  quadratic numbers.
+ */
+
+GEN
+chinese1(GEN x) { return gassoc_proto(chinese,x,NULL); }
+
+GEN
+chinese(GEN x, GEN y)
+{
+  pari_sp av,tetpil;
+  long tx = typ(x);
+  GEN z,p1,p2,d,u,v;
+
+  if (!y) return chinese1(x);
+  if (gequal(x,y)) return gcopy(x);
+  if (tx == typ(y)) switch(tx)
+  {
+    case t_POLMOD:
+    {
+      GEN A = gel(x,1), B = gel(y,1);
+      GEN a = gel(x,2), b = gel(y,2);
+      z = cgetg(3, t_POLMOD);
+      if (varn(A)!=varn(B)) pari_err_VAR("chinese",A,B);
+      if (RgX_equal(A,B))  /* same modulus */
+      {
+        gel(z,1) = gcopy(A);
+        gel(z,2) = chinese(a,b);
+        return z;
+      }
+      av = avma;
+      d = RgX_extgcd(A,B,&u,&v);
+      p2 = gsub(b, a);
+      if (!gequal0(gmod(p2, d))) break;
+      p1 = gdiv(A,d);
+      p2 = gadd(a, gmul(gmul(u,p1), p2));
+
+      tetpil = avma;
+      gel(z,1) = gmul(p1,B);
+      gel(z,2) = gmod(p2,gel(z,1));
+      gerepilecoeffssp(av,tetpil,z+1,2); return z;
+    }
+    case t_INTMOD:
+    {
+      GEN A = gel(x,1), B = gel(y,1);
+      GEN a = gel(x,2), b = gel(y,2), c, d, C, U;
+      z = cgetg(3,t_INTMOD);
+      Z_chinese_pre(A, B, &C, &U, &d);
+      c = Z_chinese_post(a, b, C, U, d);
+      if (!c) pari_err_OP("chinese", x,y);
+      gel(z,1) = icopy_avma(C, (pari_sp)z);
+      gel(z,2) = icopy_avma(c, (pari_sp)gel(z,1));
+      avma = (pari_sp)gel(z,2); return z;
+    }
+    case t_POL:
+    {
+      long i, lx = lg(x), ly = lg(y);
+      if (varn(x) != varn(y)) break;
+      if (lx < ly) { swap(x,y); lswap(lx,ly); }
+      z = cgetg(lx, t_POL); z[1] = x[1];
+      for (i=2; i<ly; i++) gel(z,i) = chinese(gel(x,i),gel(y,i));
+      for (   ; i<lx; i++) gel(z,i) = gcopy(gel(x,i));
+      return z;
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+    {
+      long i, lx;
+      z = cgetg_copy(x, &lx); if (lx!=lg(y)) break;
+      for (i=1; i<lx; i++) gel(z,i) = chinese(gel(x,i),gel(y,i));
+      return z;
+    }
+  }
+  pari_err_OP("chinese",x,y);
+  return NULL; /* not reached */
+}
+
+/* init chinese(Mod(.,A), Mod(.,B)) */
+void
+Z_chinese_pre(GEN A, GEN B, GEN *pC, GEN *pU, GEN *pd)
+{
+  GEN u, d = bezout(A,B,&u,NULL); /* U = u(A/d), u(A/d) + v(B/d) = 1 */
+  GEN t = diviiexact(A,d);
+  *pU = mulii(u, t);
+  *pC = mulii(t, B);
+  if (pd) *pd = d;
+}
+/* Assume C = lcm(A, B), U = 0 mod (A/d), U = 1 mod (B/d), a = b mod d,
+ * where d = gcd(A,B) or NULL, return x = a (mod A), b (mod B).
+ * If d not NULL, check wether a = b mod d. */
+GEN
+Z_chinese_post(GEN a, GEN b, GEN C, GEN U, GEN d)
+{
+  GEN b_a;
+  if (!signe(a))
+  {
+    if (d && remii(b, d) != gen_0) return NULL;
+    return Fp_mul(b, U, C);
+  }
+  b_a = subii(b,a);
+  if (d && remii(b_a, d) != gen_0) return NULL;
+  return modii(addii(a, mulii(U, b_a)), C);
+}
+GEN
+Z_chinese(GEN a, GEN b, GEN A, GEN B)
+{
+  pari_sp av = avma;
+  GEN C, U; Z_chinese_pre(A, B, &C, &U, NULL);
+  return gerepileuptoint(av, Z_chinese_post(a,b, C, U, NULL));
+}
+GEN
+Z_chinese_all(GEN a, GEN b, GEN A, GEN B, GEN *pC)
+{
+  GEN U; Z_chinese_pre(A, B, pC, &U, NULL);
+  return Z_chinese_post(a,b, *pC, U, NULL);
+}
+
+/* return lift(chinese(a mod A, b mod B))
+ * assume(A,B)=1, a,b,A,B integers and C = A*B */
+GEN
+Z_chinese_coprime(GEN a, GEN b, GEN A, GEN B, GEN C)
+{
+  pari_sp av = avma;
+  GEN U = mulii(Fp_inv(A,B), A);
+  return gerepileuptoint(av, Z_chinese_post(a,b,C,U, NULL));
+}
+
+/* chinese1 for coprime moduli in Z */
+static GEN
+chinese1_coprime_Z_aux(GEN x, GEN y)
+{
+  GEN z = cgetg(3, t_INTMOD);
+  GEN A = gel(x,1), a = gel(x, 2);
+  GEN B = gel(y,1), b = gel(y, 2), C = mulii(A,B);
+  pari_sp av = avma;
+  GEN U = mulii(Fp_inv(A,B), A);
+  gel(z,2) = gerepileuptoint(av, Z_chinese_post(a,b,C,U, NULL));
+  gel(z,1) = C; return z;
+}
+GEN
+chinese1_coprime_Z(GEN x) {return gassoc_proto(chinese1_coprime_Z_aux,x,NULL);}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                    MODULAR EXPONENTIATION                       **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* modified Barrett reduction with one fold */
+/* See Fast Modular Reduction, W. Hasenplaugh, G. Gaubatz, V. Gopal, ARITH 18 */
+
+static GEN
+Fp_invmBarrett(GEN p, long s)
+{
+  GEN R, Q = dvmdii(int2n(3*s),p,&R);
+  return mkvec2(Q,R);
+}
+
+static GEN
+Fp_rem_mBarrett(GEN a, GEN B, long s, GEN p)
+{
+  pari_sp av = avma;
+  GEN Q = gel(B, 1), R = gel(B, 2);
+  long sQ = expi(Q);
+  GEN A = addii(remi2n(a, 3*s), mulii(R,shifti(a, -3*s)));
+  GEN q = shifti(mulii(shifti(A, sQ-3*s), Q), -sQ);
+  GEN r = subii(A, mulii(q, p));
+  GEN sr= subii(r,p);     /* Now 0 <= r < 4*p */
+  if (signe(sr)<0) return gerepileuptoint(av, r);
+  r=sr; sr = subii(r,p);  /* Now 0 <= r < 3*p */
+  if (signe(sr)<0) return gerepileuptoint(av, r);
+  r=sr; sr = subii(r,p);  /* Now 0 <= r < 2*p */
+  return gerepileuptoint(av, signe(sr)>=0 ? sr:r);
+}
+
+/* Montgomery reduction */
+
+INLINE ulong
+init_montdata(GEN N) { return (ulong) -invmod2BIL(mod2BIL(N)); }
+
+typedef struct muldata {
+  GEN N;
+  GEN iM;
+  ulong inv, s;
+  GEN (*res)(struct muldata *,GEN);
+  GEN (*mul2)(struct muldata *,GEN);
+} muldata;
+
+/* Montgomery reduction */
+static GEN
+_montred(muldata *D, GEN x)
+{
+  return red_montgomery(x, D->N, D->inv);
+}
+
+static GEN
+_remii(muldata *D, GEN x) { return remii(x, D->N); }
+
+static GEN
+_remiibar(muldata *D, GEN x) { return Fp_rem_mBarrett(x, D->iM, D->s, D->N); }
+
+/* 2x mod N */
+static GEN
+_muli2red(muldata *D, GEN x)
+{
+  GEN z = shifti(x,1);
+  return (cmpii(z,D->N) >= 0)? subii(z,D->N): z;
+}
+static GEN
+_muli2montred(muldata *D, GEN x)
+{
+  GEN z = _muli2red(D,x);
+  long l = lgefint(D->N);
+  while (lgefint(z) > l) z = subii(z,D->N);
+  return z;
+}
+static GEN
+_mul(void *data, GEN x, GEN y)
+{
+  muldata *D = (muldata *)data;
+  return D->res(D, mulii(x,y));
+}
+static GEN
+_sqr(void *data, GEN x)
+{
+  muldata *D = (muldata *)data;
+  return D->res(D, sqri(x));
+}
+static GEN
+_m2sqr(void *data, GEN x)
+{
+  muldata *D = (muldata *)data;
+  return D->mul2(D, D->res(D, sqri(x)));
+}
+ulong
+Fl_powu(ulong x, ulong n0, ulong p)
+{
+  ulong y, z, n;
+  if (n0 <= 2)
+  { /* frequent special cases */
+    if (n0 == 2) return Fl_sqr(x,p);
+    if (n0 == 1) return x;
+    if (n0 == 0) return 1;
+  }
+  if (x <= 1) return x; /* 0 or 1 */
+  y = 1; z = x; n = n0;
+  for(;;)
+  {
+    if (n&1) y = Fl_mul(y,z,p);
+    n>>=1; if (!n) return y;
+    z = Fl_sqr(z,p);
+  }
+}
+
+static long
+Fp_select_red(GEN *y, ulong k, GEN N, long lN, muldata *D)
+{
+  D->N = N;
+  if (lN >= Fp_POW_BARRETT_LIMIT && (k==0 || ((double)k)*expi(*y) > 2 + expi(N)))
+  {
+    D->mul2 = &_muli2red;
+    D->res = &_remiibar;
+    D->s = 1+(expi(N)>>1);
+    D->iM = Fp_invmBarrett(N, D->s);
+    return 0;
+  }
+  else if (mod2(N) && lN < Fp_POW_REDC_LIMIT)
+  {
+    *y = remii(shifti(*y, bit_accuracy(lN)), N);
+    D->mul2 = &_muli2montred;
+    D->res = &_montred;
+    D->inv = init_montdata(N);
+    return 1;
+  }
+  else
+  {
+    D->mul2 = &_muli2red;
+    D->res = &_remii;
+    return 0;
+  }
+}
+
+GEN
+Fp_powu(GEN A, ulong k, GEN N)
+{
+  long lN = lgefint(N), sA;
+  int base_is_2, use_montgomery;
+  muldata  D;
+  pari_sp av;
+
+  if (lN == 3) {
+    ulong n = (ulong)N[2];
+    return utoi( Fl_powu(umodiu(A, n), k, n) );
+  }
+  if (k <= 2)
+  { /* frequent special cases */
+    if (k == 2) return Fp_sqr(A,N);
+    if (k == 1) return A;
+    if (k == 0) return gen_1;
+  }
+  sA = signe(A)==-1 && odd(k);
+  base_is_2 = 0;
+  if (lgefint(A) == 3) switch(A[2])
+  {
+    case 1: return sA ? gen_m1 : gen_1;
+    case 2:  base_is_2 = 1; break;
+  }
+
+  /* TODO: Move this out of here and use for general modular computations */
+  av = avma;
+  use_montgomery = Fp_select_red(&A, k, N, lN, &D);
+  if (base_is_2)
+    A = gen_powu_fold_i(A, k, (void*)&D, &_sqr, &_m2sqr);
+  else
+    A = gen_powu_i(A, k, (void*)&D, &_sqr, &_mul);
+  if (use_montgomery)
+  {
+    A = _montred(&D, A);
+    if (cmpii(A,N) >= 0) A = subii(A,N);
+    if (sA) A = subii(N, A);
+  }
+  return gerepileuptoint(av, A);
+}
+
+GEN
+Fp_pows(GEN A, long k, GEN N)
+{
+  if (lgefint(N) == 3) {
+    ulong n = N[2];
+    ulong a = umodiu(A, n);
+    if (k < 0) {
+      a = Fl_inv(a, n);
+      k = -k;
+    }
+    return utoi( Fl_powu(a, (ulong)k, n) );
+  }
+  if (k < 0) { A = Fp_inv(A, N); k = -k; };
+  return Fp_powu(A, (ulong)k, N);
+}
+
+/* A^K mod N */
+GEN
+Fp_pow(GEN A, GEN K, GEN N)
+{
+  pari_sp av = avma;
+  long t,s, lN = lgefint(N), sA;
+  int base_is_2, use_montgomery;
+  GEN y;
+  muldata  D;
+
+  s = signe(K);
+  if (!s)
+  {
+    t = signe(remii(A,N)); avma = av;
+    return t? gen_1: gen_0;
+  }
+  if (lN == 3)
+  {
+    ulong k, n = N[2], a = umodiu(A, n);
+    if (s < 0) a = Fl_inv(a, n);
+    if (a <= 1) return utoi(a); /* 0 or 1 */
+    if (lgefint(K) > 3)
+    { /* silly case : huge exponent, small modulus */
+      pari_warn(warner, "Mod(a,b)^n with n >> b : wasteful");
+      if (s > 0)
+      {
+        ulong d = ugcd(a, n);
+        if (d != 1)
+        { /* write n = n1 n2, with n2 maximal such that (n1,a) = 1 */
+          ulong n1 = ucoprime_part(n, d), n2 = n/n1;
+
+          k = umodiu(K, eulerphiu(n1));
+          /* CRT: = a^K (mod n1), = 0 (mod n2)*/
+          return utoi( Fl_mul(Fl_powu(a, k, n1), n2 * Fl_inv(n2,n1), n) );
+        }
+        /* gcd(a,n) = 1 */
+        k = umodiu(K, eulerphiu(n));
+      }
+      else
+        k = umodiu(negi(K), eulerphiu(n));
+    }
+    else
+      k = (ulong)K[2];
+    return utoi(Fl_powu(a, k, n));
+  }
+
+  if (s < 0) y = Fp_inv(A,N);
+  else
+  {
+    y = modii(A,N);
+    if (!signe(y)) { avma = av; return gen_0; }
+  }
+  if (lgefint(K) == 3) return gerepileuptoint(av, Fp_powu(y, K[2], N));
+
+  base_is_2 = 0;
+  sA = signe(y)==-1 && mod2(K);
+  if (lgefint(y) == 3) switch(y[2])
+  {
+    case 1: return sA ? gen_m1 : gen_1;
+    case 2:  base_is_2 = 1; break;
+  }
+
+  /* TODO: Move this out of here and use for general modular computations */
+  use_montgomery = Fp_select_red(&y, 0UL, N, lN, &D);
+  if (base_is_2)
+    y = gen_pow_fold_i(y, K, (void*)&D, &_sqr, &_m2sqr);
+  else
+    y = gen_pow_i(y, K, (void*)&D, &_sqr, &_mul);
+  if (use_montgomery)
+  {
+    y = _montred(&D,y);
+    if (cmpii(y,N) >= 0) y = subii(y,N);
+    if (sA) y = subii(N, y);
+  }
+  return gerepileuptoint(av,y);
+}
+
+static GEN
+_Fp_mul(void *E, GEN x, GEN y) { return Fp_mul(x,y,(GEN)E); }
+
+static GEN
+_Fp_pow(void *E, GEN x, GEN n) { return Fp_pow(x,n,(GEN)E); }
+
+static GEN
+_Fp_rand(void *E) { return addis(randomi(subis((GEN)E,1)),1); }
+
+static GEN Fp_easylog(void *E, GEN a, GEN g, GEN ord);
+
+static const struct bb_group Fp_star={_Fp_mul,_Fp_pow,_Fp_rand,hash_GEN,
+                                      equalii,equali1,Fp_easylog};
+
+static GEN
+_Fp_red(void *E, GEN x) { return Fp_red(x, (GEN)E); }
+
+static GEN
+_Fp_add(void *E, GEN x, GEN y) { (void) E; return addii(x,y); }
+
+static GEN
+_Fp_neg(void *E, GEN x) { (void) E; return negi(x); }
+
+static GEN
+_Fp_rmul(void *E, GEN x, GEN y) { (void) E; return mulii(x,y); }
+
+static GEN
+_Fp_inv(void *E, GEN x) { return Fp_inv(x,(GEN)E); }
+
+static int
+_Fp_equal0(GEN x) { return signe(x)==0; }
+
+static GEN
+_Fp_s(void *E, long x) { (void) E; return stoi(x); }
+
+static const struct bb_field Fp_field={_Fp_red,_Fp_add,_Fp_rmul,_Fp_neg,
+                                        _Fp_inv,_Fp_equal0,_Fp_s};
+
+const struct bb_field *get_Fp_field(void **E, GEN p)
+{
+  *E = (void*)p; return &Fp_field;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**               ORDER of INTEGERMOD x  in  (Z/nZ)*                **/
+/**                                                                 **/
+/*********************************************************************/
+ulong
+Fl_order(ulong a, ulong o, ulong p)
+{
+  pari_sp av = avma;
+  GEN m, P, E;
+  long i;
+  if (!o) o = p-1;
+  m = factoru(o);
+  P = gel(m,1);
+  E = gel(m,2);
+  for (i = lg(P)-1; i; i--)
+  {
+    ulong j, l=P[i], e=E[i], t = o / upowuu(l,e), y = Fl_powu(a, t, p);
+    if (y == 1) o = t;
+    else {
+      for (j = 1; j < e; j++) { y = Fl_powu(y, l, p); if (y == 1) break; }
+      o = t *  upowuu(l, j);
+    }
+  }
+  avma = av; return o;
+}
+
+/*Find the exact order of a assuming a^o==1*/
+GEN
+Fp_order(GEN a, GEN o, GEN p) {
+  if (lgefint(p) == 3 && typ(o) == t_INT && lgefint(o)==3)
+  {
+    ulong pp = p[2], oo = o[2];
+    return utoi( Fl_order(umodiu(a, pp), oo, pp) );
+  }
+  return gen_order(a, o, (void*)p, &Fp_star);
+}
+GEN
+Fp_factored_order(GEN a, GEN o, GEN p)
+{ return gen_factored_order(a, o, (void*)p, &Fp_star); }
+
+/* return order of a mod p^e, e > 0, pe = p^e */
+static GEN
+Zp_order(GEN a, GEN p, long e, GEN pe)
+{
+  GEN ap, op;
+  if (equaliu(p, 2))
+  {
+    if (e == 1) return gen_1;
+    if (e == 2) return mod4(a) == 1? gen_1: gen_2;
+    if (mod4(a) == 1)
+      op = gen_1;
+    else {
+      op = gen_2;
+      a = Fp_sqr(a, pe);
+    }
+  } else {
+    ap = (e == 1)? a: remii(a,p);
+    op = Fp_order(ap, subis(p,1), p);
+    if (e == 1) return op;
+    a = Fp_pow(a, op, pe); /* 1 mod p */
+  }
+  if (equali1(a)) return op;
+  return mulii(op, powiu(p, e - Z_pval(subis(a,1), p)));
+}
+
+GEN
+znorder(GEN x, GEN o)
+{
+  pari_sp av = avma;
+  GEN b, a;
+
+  if (typ(x) != t_INTMOD) pari_err_TYPE("znorder [t_INTMOD expected]",x);
+  b = gel(x,1); a = gel(x,2);
+  if (!equali1(gcdii(a,b))) pari_err_COPRIME("znorder", a,b);
+  if (!o)
+  {
+    GEN fa = Z_factor(b), P = gel(fa,1), E = gel(fa,2);
+    long i, l = lg(P);
+    o = gen_1;
+    for (i = 1; i < l; i++)
+    {
+      GEN p = gel(P,i);
+      long e = itos(gel(E,i));
+
+      if (l == 2)
+        o = Zp_order(a, p, e, b);
+      else {
+        GEN pe = powiu(p,e);
+        o = lcmii(o, Zp_order(remii(a,pe), p, e, pe));
+      }
+    }
+    return gerepileuptoint(av, o);
+  }
+  return Fp_order(a, o, b);
+}
+GEN
+order(GEN x) { return znorder(x, NULL); }
+
+/*********************************************************************/
+/**                                                                 **/
+/**               DISCRETE LOGARITHM  in  (Z/nZ)*                   **/
+/**                                                                 **/
+/*********************************************************************/
+static GEN
+Fp_log_halfgcd(ulong bnd, GEN C, GEN g, GEN p)
+{
+  pari_sp av = avma;
+  GEN h1, h2, F, G;
+  if (!Fp_ratlift(g,p,C,shifti(C,-1),&h1,&h2)) return NULL;
+  if ((F = Z_issmooth_fact(h1, bnd)) && (G = Z_issmooth_fact(h2, bnd)))
+  {
+    GEN M = cgetg(3, t_MAT);
+    gel(M,1) = vecsmall_concat(gel(F, 1),gel(G, 1));
+    gel(M,2) = vecsmall_concat(gel(F, 2),zv_neg_inplace(gel(G, 2)));
+    return gerepileupto(av, M);
+  }
+  avma = av; return NULL;
+}
+
+static GEN
+Fp_log_find_rel(GEN b, ulong bnd, GEN C, GEN p, GEN *g, long *e)
+{
+  GEN rel;
+  do
+  {
+    (*e)++; *g = Fp_mul(*g, b, p);
+    rel = Fp_log_halfgcd(bnd, C, *g, p);
+  } while (!rel);
+  return rel;
+}
+
+struct Fp_log_rel
+{
+  GEN rel;
+  long *sieve;
+  ulong prmax;
+  long nbrel, nbmax;
+};
+
+/* add u^e */
+static long
+addifsmooth1(struct Fp_log_rel *r, GEN h, long u, long e)
+{
+  pari_sp av = avma;
+  GEN z;
+  if ((z = Z_issmooth_fact(h, r->prmax)))
+  {
+    long off = r->prmax+1;
+    GEN F = cgetg(3, t_MAT);
+    gel(F,1) = vecsmall_append(gel(z,1), off+u);
+    gel(F,2) = vecsmall_append(gel(z,2), e);
+    gel(r->rel,++r->nbrel) = gerepileupto(av, F);
+  }
+  return r->nbrel==r->nbmax;
+}
+
+/* add u^-1 v^-1 */
+static long
+addifsmooth2(struct Fp_log_rel *r, GEN h, long u, long v)
+{
+  pari_sp av = avma;
+  GEN z;
+  if ((z = Z_issmooth_fact(h, r->prmax)))
+  {
+    long off = r->prmax+1;
+    GEN P = mkvecsmall2(off+u,off+v), E = mkvecsmall2(-1,-1);
+    GEN F = cgetg(3, t_MAT);
+    gel(F,1) = vecsmall_concat(gel(z,1), P);
+    gel(F,2) = vecsmall_concat(gel(z,2), E);
+    gel(r->rel,++r->nbrel) = gerepileupto(av, F);
+  }
+  return r->nbrel==r->nbmax;
+}
+
+/*
+Let p=C^2+c
+Solve h = (C+x)*(C+a)-p = 0 [mod l]
+h= -c+x*(C+a)+C*a = 0  [mod l]
+x = (c-C*a)/(C+a) [mod l]
+h = -c+C*(x+a)+a*x
+*/
+
+static void
+Fp_log_sieve_h(struct Fp_log_rel *r, GEN C, GEN c, GEN Ci, GEN ci, long a, GEN pr, GEN sz)
+{
+  long th = expi(C), n = lg(pr)-1;
+  long i,j;
+  if (addifsmooth1(r, addis(C,a), a, -1)) return;
+  for(j=0; j<=a; j++)
+    r->sieve[j]=0;
+  for(i=1; i<=n; i++)
+  {
+    ulong li = pr[i], s = sz[i], al = a % li;
+    ulong u, iv = Fl_invsafe(Fl_add(Ci[i],al,li),li);
+    if (!iv) continue;
+    u = Fl_mul(Fl_sub(ci[i],Fl_mul(Ci[i],al,li),li), iv ,li);
+    for(j = u; j<=a; j+=li)
+      r->sieve[j] += s;
+  }
+  th = th - expu(th)-1;
+  for(j=0; j<a; j++)
+    if (r->sieve[j]>=th)
+    {
+      GEN h = addiu(subii(muliu(C,a+j),c), a*j);
+      if (addifsmooth2(r, h, a, j)) return;
+    }
+  /* j = a */
+    if (r->sieve[a]>=th)
+    {
+      GEN h = addiu(subii(muliu(C,2*a),c), a*a);
+      if (addifsmooth1(r, h, a, -2)) return;
+    }
+}
+
+static GEN
+_psi(void*E, GEN y)
+{
+  GEN lx = (GEN) E;
+  long prec = lg(lx);
+  GEN ly = glog(y, prec);
+  GEN u = gdiv(lx, ly);
+  return gsub(gdiv(y ,ly), gpow(u, u, prec));
+}
+
+static GEN
+opt_param(GEN x, long prec)
+{
+  return zbrent((void*)glog(x,prec), _psi, gen_2, x, prec);
+}
+
+static GEN
+check_kernel(long N, long prmax, GEN C, GEN M, GEN p, GEN m)
+{
+  pari_sp av = avma;
+  GEN K = FpMs_leftkernel_elt(M, N, m);
+  long i, f=0;
+  long l = lg(K), lm = lgefint(m);
+  GEN idx = diviiexact(subis(p,1),m), g;
+  pari_timer ti;
+  if (DEBUGLEVEL) timer_start(&ti);
+  for(i=1; i<l; i++)
+    if (signe(gel(K,i)))
+      break;
+  g = utoi(i);
+  K = FpC_Fp_mul(K, Fp_inv(gel(K,i), m), m);
+  for(i=1; i<l; i++)
+  {
+    GEN k = gel(K,i);
+    GEN j = i<=prmax ? utoi(i): addis(C,i-(prmax+1));
+    if (signe(k)==0 || !equalii(Fp_pow(g, mulii(k,idx), p),
+                                Fp_pow(j, idx, p)))
+      gel(K,i) = cgetineg(lm);
+    else
+      f++;
+  }
+  if (DEBUGLEVEL) timer_printf(&ti,"found %ld logs", f);
+  return gerepileupto(av, K);
+}
+
+static GEN
+Fp_log_find_ind(GEN a, GEN K, long prmax, GEN C, GEN p, GEN m)
+{
+  pari_sp av=avma;
+  GEN aa = gen_1;
+  long AV = 0;
+  for(;;)
+  {
+    GEN A = Fp_log_find_rel(a, prmax, C, p, &aa, &AV);
+    GEN F = gel(A,1), E = gel(A,2);
+    GEN Ao = gen_0;
+    long i, l = lg(F);
+    for(i=1; i<l; i++)
+    {
+      GEN Ki = gel(K,F[i]);
+      if (signe(Ki)<0) break;
+      Ao = addii(Ao, mulis(Ki, E[i]));
+    }
+    if (i==l) return Fp_div(Ao, utoi(AV), m);
+    aa = gerepileuptoint(av, aa);
+  }
+}
+
+static GEN
+Fp_log_index(GEN a, GEN b, GEN m, GEN p)
+{
+  pari_sp av = avma, av2;
+  long i, nbi, nbrow;
+  GEN C, c, Ci, ci, pr, sz, l, Ao, Bo, K, d, p_1;
+  pari_timer ti;
+  struct Fp_log_rel r;
+  ulong bnds = itou(roundr_safe(opt_param(sqrti(p),DEFAULTPREC)));
+  ulong bnd = 4*bnds;
+  if (!bnds || cmpii(sqru(bnds),m)>=0) return NULL;
+
+  p_1 = subiu(p,1);
+  if (!is_pm1(gcdii(m,diviiexact(p_1,m))))
+    m = diviiexact(p_1, coprime_part(p_1, m));
+  pr = primes_upto_zv(bnd);
+  nbi = lg(pr)-1;
+  if (DEBUGLEVEL)
+  {
+    err_printf("bnd=%lu Size FB=%ld\n", bnd, nbi);
+    timer_start(&ti);
+  }
+  C = sqrtremi(p, &c);
+  av2 = avma;
+  Ci = cgetg(nbi+1,t_VECSMALL);
+  ci = cgetg(nbi+1,t_VECSMALL);
+  sz = cgetg(nbi+1,t_VECSMALL);
+  for (i = 1; i <= nbi; ++i)
+  {
+    ulong lp = pr[i];
+    Ci[i] = umodiu(C, lp);
+    ci[i] = umodiu(c, lp);
+    sz[i] = expu(lp);
+  }
+  r.nbrel = 0;
+  r.nbmax = 8*nbi;
+  r.rel = cgetg(r.nbmax+1,t_VEC);
+  r.sieve = cgetg(r.nbmax+2,t_VECSMALL)+1;
+  r.prmax = pr[nbi];
+  for(i=0; r.nbrel < r.nbmax; i++)
+  {
+    Fp_log_sieve_h(&r, C, c, Ci, ci, i, pr, sz);
+    if (DEBUGLEVEL && (i&127)==0)
+      err_printf("%ld%% ",100*r.nbrel/(r.nbmax));
+  }
+  nbrow = r.prmax+i;
+  if (DEBUGLEVEL)
+  {
+    err_printf("\n");
+    timer_printf(&ti," %ld relations, %ld generators", r.nbrel, nbi+i);
+  }
+  setlg(r.rel,r.nbrel+1);
+  r.rel = gerepileupto(av2, r.rel);
+  K = check_kernel(nbrow, r.prmax, C, r.rel, p, m);
+  if (DEBUGLEVEL) timer_start(&ti);
+  Ao = Fp_log_find_ind(a, K, r.prmax, C, p, m);
+  if (DEBUGLEVEL) timer_printf(&ti," log element");
+  Bo = Fp_log_find_ind(b, K, r.prmax, C, p, m);
+  if (DEBUGLEVEL) timer_printf(&ti," log generator");
+  d = gcdii(Ao,Bo);
+  l = Fp_div(diviiexact(Ao, d) ,diviiexact(Bo, d), m);
+  if (!equalii(a,Fp_pow(b,l,p))) pari_err_BUG("Fp_log_index");
+  return gerepileuptoint(av, l);
+}
+
+/* Trivial cases a = 1, -1. Return x s.t. g^x = a or [] if no such x exist */
+static GEN
+Fp_easylog(void *E, GEN a, GEN g, GEN ord)
+{
+  pari_sp av = avma;
+  GEN p = (GEN)E;
+  /* assume a reduced mod p, p not necessarily prime */
+  if (equali1(a)) return gen_0;
+  /* p > 2 */
+  if (equalii(subis(p,1), a))  /* -1 */
+  {
+    pari_sp av2;
+    GEN t;
+    ord = dlog_get_ord(ord);
+    if (mpodd(ord)) { avma = av; return cgetg(1, t_VEC); } /* no solution */
+    t = shifti(ord,-1); /* only possible solution */
+    av2 = avma;
+    if (!equalii(Fp_pow(g, t, p), a)) { avma = av; return cgetg(1, t_VEC); }
+    avma = av2; return gerepileuptoint(av, t);
+  }
+  if (typ(ord)==t_INT && expi(ord)>=27 && BPSW_psp(p))
+    return Fp_log_index(a, g, ord, p);
+  avma = av; return NULL; /* not easy */
+}
+
+GEN
+Fp_log(GEN a, GEN g, GEN ord, GEN p)
+{
+  GEN v = dlog_get_ordfa(ord);
+  ord = mkvec2(gel(v,1),ZM_famat_limit(gel(v,2),int2n(27)));
+  return gen_PH_log(a,g,ord,(void*)p,&Fp_star);
+}
+
+/* find x such that h = g^x mod N > 1, N = prod_{i <= l} P[i]^E[i], P[i] prime.
+ * PHI[l] = eulerphi(N / P[l]^E[l]).   Destroys P/E */
+static GEN
+znlog_rec(GEN h, GEN g, GEN N, GEN P, GEN E, GEN PHI)
+{
+  long l = lg(P) - 1, e = E[l];
+  GEN p = gel(P, l), phi = gel(PHI,l), pe = e == 1? p: powiu(p, e);
+  GEN a,b, hp,gp, hpe,gpe, ogpe; /* = order(g mod p^e) | p^(e-1)(p-1) */
+
+  if (l == 1) {
+    hpe = h;
+    gpe = g;
+  } else {
+    hpe = modii(h, pe);
+    gpe = modii(g, pe);
+  }
+  if (e == 1) {
+    hp = hpe;
+    gp = gpe;
+  } else {
+    hp = remii(hpe, p);
+    gp = remii(gpe, p);
+  }
+  if (hp == gen_0 || gp == gen_0) return NULL;
+  if (equaliu(p, 2))
+  {
+    GEN N = int2n(e);
+    ogpe = Zp_order(gpe, gen_2, e, N);
+    a = Fp_log(hpe, gpe, ogpe, N);
+    if (typ(a) != t_INT) return NULL;
+  }
+  else
+  { /* Avoid black box groups: (Z/p^2)^* / (Z/p)^* ~ (Z/pZ, +), where DL
+       is trivial */
+    /* [order(gp), factor(order(gp))] */
+    GEN v = Fp_factored_order(gp, subis(p,1), p);
+    GEN ogp = gel(v,1);
+    if (!equali1(Fp_pow(hp, ogp, p))) return NULL;
+    a = Fp_log(hp, gp, v, p);
+    if (typ(a) != t_INT) return NULL;
+    if (e == 1) ogpe = ogp;
+    else
+    { /* find a s.t. g^a = h (mod p^e), p odd prime, e > 0, (h,p) = 1 */
+      /* use p-adic log: O(log p + e) mul*/
+      long vpogpe, vpohpe;
+
+      hpe = Fp_mul(hpe, Fp_pow(gpe, negi(a), pe), pe);
+      gpe = Fp_pow(gpe, ogp, pe);
+      /* g,h = 1 mod p; compute b s.t. h = g^b */
+
+      /* v_p(order g mod pe) */
+      vpogpe = equali1(gpe)? 0: e - Z_pval(subis(gpe,1), p);
+      /* v_p(order h mod pe) */
+      vpohpe = equali1(gpe)? 0: e - Z_pval(subis(hpe,1), p);
+      if (vpohpe > vpogpe) return NULL;
+
+      ogpe = mulii(ogp, powiu(p, vpogpe)); /* order g mod p^e */
+      if (is_pm1(gpe)) return is_pm1(hpe)? a: NULL;
+      b = gdiv(Qp_log(cvtop(hpe, p, e)), Qp_log(cvtop(gpe, p, e)));
+      a = addii(a, mulii(ogp, gtrunc(b)));
+    }
+  }
+  /* gp^a = hp => x = a mod ogpe => generalized Pohlig-Hellman strategy */
+  if (l == 1) return a;
+
+  N = diviiexact(N, pe); /* make N coprime to p */
+  h = Fp_mul(h, Fp_pow(g, modii(negi(a), phi), N), N);
+  g = Fp_pow(g, modii(ogpe, phi), N);
+  setlg(P, l); /* remove last element */
+  setlg(E, l);
+  b = znlog_rec(h, g, N, P, E, PHI);
+  if (!b) return NULL;
+  return addmulii(a, b, ogpe);
+}
+
+static GEN
+get_PHI(GEN P, GEN E)
+{
+  long i, l = lg(P);
+  GEN PHI = cgetg(l, t_VEC);
+  gel(PHI,1) = gen_1;
+  for (i=1; i<l-1; i++)
+  {
+    GEN t, p = gel(P,i);
+    long e = E[i];
+    t = mulii(powiu(p, e-1), subis(p,1));
+    if (i > 1) t = mulii(t, gel(PHI,i));
+    gel(PHI,i+1) = t;
+  }
+  return PHI;
+}
+
+GEN
+znlog(GEN h, GEN g, GEN o)
+{
+  pari_sp av = avma;
+  GEN N, fa, P, E, x;
+  switch (typ(g))
+  {
+    case t_PADIC:
+    {
+      GEN p = gel(g,2);
+      long v = valp(g);
+      if (v < 0) pari_err_DIM("znlog");
+      if (v > 0) {
+        long k = gvaluation(h, p);
+        if (k % v) return cgetg(1,t_VEC);
+        k /= v;
+        if (!gequal(h, gpowgs(g,k))) { avma = av; return cgetg(1,t_VEC); }
+        avma = av; return stoi(k);
+      }
+      N = gel(g,3);
+      g = Rg_to_Fp(g, N);
+      break;
+    }
+    case t_INTMOD:
+      N = gel(g,1);
+      g = gel(g,2); break;
+    default: pari_err_TYPE("znlog", g);
+      return NULL; /* not reached */
+  }
+  if (equali1(N)) { avma = av; return gen_0; }
+  h = Rg_to_Fp(h, N);
+  if (o) return gerepileupto(av, Fp_log(h, g, o, N));
+  fa = Z_factor(N);
+  P = gel(fa,1);
+  E = vec_to_vecsmall(gel(fa,2));
+  x = znlog_rec(h, g, N, P, E, get_PHI(P,E));
+  if (!x) { avma = av; return cgetg(1,t_VEC); }
+  return gerepileuptoint(av, x);
+}
+
+GEN
+Fp_sqrtn(GEN a, GEN n, GEN p, GEN *zeta)
+{
+  a = modii(a,p);
+  if (!signe(a))
+  {
+    if (zeta) *zeta = gen_1;
+    if (signe(n) < 0) pari_err_INV("Fp_sqrtn", mkintmod(gen_0,p));
+    return gen_0;
+  }
+  if (equaliu(n,2))
+  {
+    if (zeta) *zeta = addis(p,-1);
+    return Fp_sqrt(a,p);
+  }
+  return gen_Shanks_sqrtn(a,n,addis(p,-1),zeta,(void*)p,&Fp_star);
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                    FUNDAMENTAL DISCRIMINANTS                    **/
+/**                                                                 **/
+/*********************************************************************/
+long
+isfundamental(GEN x) {
+  if (typ(x) != t_INT) pari_err_TYPE("isfundamental",x);
+  return Z_isfundamental(x);
+}
+
+/* x fundamental ? */
+long
+uposisfundamental(ulong x)
+{
+  ulong r = x & 15; /* x mod 16 */
+  if (!r) return 0;
+  switch(r & 3)
+  { /* x mod 4 */
+    case 0: return (r == 4)? 0: uissquarefree(x >> 2);
+    case 1: return uissquarefree(x);
+    default: return 0;
+  }
+}
+/* -x fundamental ? */
+long
+unegisfundamental(ulong x)
+{
+  ulong r = x & 15; /* x mod 16 */
+  if (!r) return 0;
+  switch(r & 3)
+  { /* x mod 4 */
+    case 0: return (r == 12)? 0: uissquarefree(x >> 2);
+    case 3: return uissquarefree(x);
+    default: return 0;
+  }
+}
+long
+Z_isfundamental(GEN x)
+{
+  long r;
+  switch(lgefint(x))
+  {
+    case 2: return 0;
+    case 3: return signe(x) < 0? unegisfundamental(x[2])
+                               : uposisfundamental(x[2]);
+  }
+  r = mod16(x);
+  if (!r) return 0;
+  if ((r & 3) == 0)
+  {
+    pari_sp av;
+    r >>= 2; /* |x|/4 mod 4 */
+    if (signe(x) < 0) r = 4-r;
+    if (r == 1) return 0;
+    av = avma;
+    r = Z_issquarefree( shifti(x,-2) );
+    avma = av; return r;
+  }
+  r &= 3; /* |x| mod 4 */
+  if (signe(x) < 0) r = 4-r;
+  return (r==1) ? Z_issquarefree(x) : 0;
+}
+
+GEN
+quaddisc(GEN x)
+{
+  const pari_sp av = avma;
+  long i,r,tx=typ(x);
+  GEN P,E,f,s;
+
+  if (!is_rational_t(tx)) pari_err_TYPE("quaddisc",x);
+  f = factor(x);
+  P = gel(f,1);
+  E = gel(f,2); s = gen_1;
+  for (i=1; i<lg(P); i++)
+    if (odd(mael(E,i,2))) s = mulii(s,gel(P,i));
+  r = mod4(s); if (gsigne(x) < 0) r = 4-r;
+  if (r>1) s = shifti(s,2);
+  return gerepileuptoint(av, s);
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                              FACTORIAL                          **/
+/**                                                                 **/
+/*********************************************************************/
+/* return a * (a+1) * ... * b. Assume a <= b  [ note: factoring out powers of 2
+ * first is slower ... ] */
+GEN
+mulu_interval(ulong a, ulong b)
+{
+  pari_sp av = avma;
+  ulong k, l, N, n = b - a + 1;
+  long lx;
+  GEN x;
+
+  if (n < 61)
+  {
+    x = utoi(a);
+    for (k=a+1; k<=b; k++) x = mului(k,x);
+    return gerepileuptoint(av, x);
+  }
+  lx = 1; x = cgetg(2 + n/2, t_VEC);
+  N = b + a;
+  for (k = a;; k++)
+  {
+    l = N - k; if (l <= k) break;
+    gel(x,lx++) = muluu(k,l);
+  }
+  if (l == k) gel(x,lx++) = utoipos(k);
+  setlg(x, lx);
+  return gerepileuptoint(av, divide_conquer_prod(x, mulii));
+}
+
+GEN
+mpfact(long n)
+{
+  if (n < 2)
+  {
+    if (n < 0) pari_err_DOMAIN("factorial", "argument","<",gen_0,stoi(n));
+    return gen_1;
+  }
+  return mulu_interval(2UL, (ulong)n);
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                      LUCAS & FIBONACCI                        **/
+/**                                                               **/
+/*******************************************************************/
+static void
+lucas(ulong n, GEN *a, GEN *b)
+{
+  GEN z, t, zt;
+  if (!n) { *a = gen_2; *b = gen_1; return; }
+  lucas(n >> 1, &z, &t); zt = mulii(z, t);
+  switch(n & 3) {
+    case  0: *a = addsi(-2,sqri(z)); *b = addsi(-1,zt); break;
+    case  1: *a = addsi(-1,zt);      *b = addsi(2,sqri(t)); break;
+    case  2: *a = addsi(2,sqri(z));  *b = addsi(1,zt); break;
+    case  3: *a = addsi(1,zt);       *b = addsi(-2,sqri(t));
+  }
+}
+
+GEN
+fibo(long n)
+{
+  pari_sp av = avma;
+  GEN a, b;
+  if (!n) return gen_0;
+  lucas((ulong)(labs(n)-1), &a, &b);
+  a = diviuexact(addii(shifti(a,1),b), 5);
+  if (n < 0 && !odd(n)) setsigne(a, -1);
+  return gerepileuptoint(av, a);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                      CONTINUED FRACTIONS                        */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+icopy_lg(GEN x, long l)
+{
+  long lx = lgefint(x);
+  GEN y;
+
+  if (lx >= l) return icopy(x);
+  y = cgeti(l); affii(x, y); return y;
+}
+
+/* continued fraction of a/b. If y != NULL, stop when partial quotients
+ * differ from y */
+static GEN
+Qsfcont(GEN a, GEN b, GEN y, ulong k)
+{
+  GEN  z, c;
+  ulong i, l, ly = lgefint(b);
+
+  /* times log(2) / log2( (1+sqrt(5)) / 2 )  */
+  l = (ulong)(3 + bit_accuracy_mul(ly, 1.44042009041256));
+  if (k > 0 && k+1 > 0 && l > k+1) l = k+1; /* beware overflow */
+  if (l > LGBITS) l = LGBITS;
+
+  z = cgetg(l,t_VEC);
+  l--;
+  if (y) {
+    pari_sp av = avma;
+    if (l >= (ulong)lg(y)) l = lg(y)-1;
+    for (i = 1; i <= l; i++)
+    {
+      GEN q = gel(y,i);
+      gel(z,i) = q;
+      c = b; if (!gequal1(q)) c = mulii(q, b);
+      c = subii(a, c);
+      if (signe(c) < 0)
+      { /* partial quotient too large */
+        c = addii(c, b);
+        if (signe(c) >= 0) i++; /* by 1 */
+        break;
+      }
+      if (cmpii(c, b) >= 0)
+      { /* partial quotient too small */
+        c = subii(c, b);
+        if (cmpii(c, b) < 0) {
+          /* by 1. If next quotient is 1 in y, add 1 */
+          if (i < l && equali1(gel(y,i+1))) gel(z,i) = addis(q,1);
+          i++;
+        }
+        break;
+      }
+      if ((i & 0xff) == 0) gerepileall(av, 2, &b, &c);
+      a = b; b = c;
+    }
+  } else {
+    a = icopy_lg(a, ly);
+    b = icopy(b);
+    for (i = 1; i <= l; i++)
+    {
+      gel(z,i) = truedvmdii(a,b,&c);
+      if (c == gen_0) { i++; break; }
+      affii(c, a); cgiv(c); c = a;
+      a = b; b = c;
+    }
+  }
+  i--;
+  if (i > 1 && gequal1(gel(z,i)))
+  {
+    cgiv(gel(z,i)); --i;
+    gel(z,i) = addsi(1, gel(z,i)); /* unclean: leave old z[i] on stack */
+  }
+  setlg(z,i+1); return z;
+}
+
+static GEN
+sersfcont(GEN a, GEN b, long k)
+{
+  long i, l = typ(a) == t_POL? lg(a): 3;
+  GEN y, c;
+  if (lg(b) > l) l = lg(b);
+  if (k > 0 && l > k+1) l = k+1;
+  y = cgetg(l,t_VEC);
+  for (i=1; i<l; i++)
+  {
+    gel(y,i) = poldivrem(a,b,&c);
+    if (gequal0(c)) { i++; break; }
+    a = b; b = c;
+  }
+  setlg(y, i); return y;
+}
+
+GEN
+gboundcf(GEN x, long k)
+{
+  pari_sp av;
+  long tx = typ(x), e;
+  GEN y, a, b, c;
+
+  if (k < 0) pari_err_DOMAIN("gboundcf","nmax","<",gen_0,stoi(k));
+  if (is_scalar_t(tx))
+  {
+    if (gequal0(x)) return mkvec(gen_0);
+    switch(tx)
+    {
+      case t_INT: return mkveccopy(x);
+      case t_REAL:
+        av = avma;
+        c = mantissa_real(x,&e);
+        if (e < 0) pari_err_PREC("gboundcf");
+        y = int2n(e);
+        a = Qsfcont(c,y, NULL, k);
+        b = addsi(signe(x), c);
+        return gerepilecopy(av, Qsfcont(b,y, a, k));
+
+      case t_FRAC:
+        av = avma;
+        return gerepileupto(av, Qsfcont(gel(x,1),gel(x,2), NULL, k));
+    }
+    pari_err_TYPE("gboundcf",x);
+  }
+
+  switch(tx)
+  {
+    case t_POL: return mkveccopy(x);
+    case t_SER:
+      av = avma;
+      return gerepileupto(av, gboundcf(ser2rfrac_i(x), k));
+    case t_RFRAC:
+      av = avma;
+      return gerepilecopy(av, sersfcont(gel(x,1), gel(x,2), k));
+  }
+  pari_err_TYPE("gboundcf",x);
+  return NULL; /* not reached */
+}
+
+static GEN
+sfcont2(GEN b, GEN x, long k)
+{
+  pari_sp av = avma;
+  long lb = lg(b), tx = typ(x), i;
+  GEN y,p1;
+
+  if (k)
+  {
+    if (k >= lb) pari_err_DIM("contfrac [too few denominators]");
+    lb = k+1;
+  }
+  y = cgetg(lb,t_VEC);
+  if (lb==1) return y;
+  if (is_scalar_t(tx))
+  {
+    if (!is_intreal_t(tx) && tx != t_FRAC) pari_err_TYPE("sfcont2",x);
+  }
+  else if (tx == t_SER) x = ser2rfrac_i(x);
+
+  if (!gequal1(gel(b,1))) x = gmul(gel(b,1),x);
+  for (i = 1;;)
+  {
+    if (tx == t_REAL)
+    {
+      long e = expo(x);
+      if (e > 0 && nbits2prec(e+1) > realprec(x)) break;
+      gel(y,i) = floorr(x);
+      p1 = subri(x, gel(y,i));
+    }
+    else
+    {
+      gel(y,i) = gfloor(x);
+      p1 = gsub(x, gel(y,i));
+    }
+    if (++i >= lb) break;
+    if (gequal0(p1)) break;
+    x = gdiv(gel(b,i),p1);
+  }
+  setlg(y,i);
+  return gerepilecopy(av,y);
+}
+
+
+GEN
+gcf(GEN x) { return gboundcf(x,0); }
+GEN
+gcf2(GEN b, GEN x) { return contfrac0(x,b,0); }
+GEN
+contfrac0(GEN x, GEN b, long nmax)
+{
+  long tb;
+
+  if (!b) return gboundcf(x,nmax);
+  tb = typ(b);
+  if (tb == t_INT) return gboundcf(x,itos(b));
+  if (! is_vec_t(tb)) pari_err_TYPE("contfrac0",b);
+  if (nmax < 0) pari_err_DOMAIN("contfrac","nmax","<",gen_0,stoi(nmax));
+  return sfcont2(b,x,nmax);
+}
+
+GEN
+contfracpnqn(GEN x, long n)
+{
+  pari_sp av = avma;
+  long i, lx = lg(x);
+  GEN M,A,B, p0,p1, q0,q1;
+
+  if (lx == 1)
+  {
+    if (! is_matvec_t(typ(x))) pari_err_TYPE("pnqn",x);
+    if (n >= 0) return cgetg(1,t_MAT);
+    return matid(2);
+  }
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL: A = x; B = NULL; break;
+    case t_MAT:
+      switch(lgcols(x))
+      {
+        case 2: A = row(x,1); B = NULL; break;
+        case 3: A = row(x,2); B = row(x,1); break;
+        default: pari_err_DIM("pnqn [ nbrows != 1,2 ]");
+                 return NULL; /*not reached*/
+      }
+      break;
+    default: pari_err_TYPE("pnqn",x);
+      return NULL; /*not reached*/
+  }
+  p1 = gel(A,1);
+  q1 = B? gel(B,1): gen_1; /* p[0], q[0] */
+  if (n >= 0)
+  {
+    lx = minss(lx, n+2);
+    if (lx == 2) return gerepilecopy(av, mkmat(mkcol2(p1,q1)));
+  }
+  else if (lx == 2)
+    return gerepilecopy(av, mkmat2(mkcol2(gen_1,gen_0), mkcol2(p1,q1)));
+  /* lx >= 3 */
+  p0 = gen_1;
+  q0 = gen_0; /* p[-1], q[-1] */
+  M = cgetg(lx, t_MAT);
+  gel(M,1) = mkcol2(p1,q1);
+  for (i=2; i<lx; i++)
+  {
+    GEN a = gel(A,i), p2,q2;
+    if (B) {
+      GEN b = gel(B,i);
+      p0 = gmul(b,p0);
+      q0 = gmul(b,q0);
+    }
+    p2 = gadd(gmul(a,p1),p0); p0=p1; p1=p2;
+    q2 = gadd(gmul(a,q1),q0); q0=q1; q1=q2;
+    gel(M,i) = mkcol2(p1,q1);
+  }
+  if (n < 0) M = mkmat2(gel(M,lx-1), gel(M,lx-2));
+  return gerepilecopy(av, M);
+}
+GEN
+pnqn(GEN x) { return contfracpnqn(x,-1); }
+
+/* write Mod(x,N) as a/b, gcd(a,b) = 1, b <= B (no condition if B = NULL) */
+static GEN
+mod_to_frac(GEN x, GEN N, GEN B)
+{
+  GEN a, b, A;
+  if (B)
+  {
+    A = divii(shifti(N, -1), B);
+    /* denominator bound useless, don't use it */
+    if (cmpii(A, B) < 0) B = NULL;
+  }
+  if (!B)
+  {
+    A = sqrti(shifti(N, -1));
+    B = A;
+  }
+  if (!Fp_ratlift(x, N, A,B,&a,&b) || !equali1( gcdii(a,b) )) return NULL;
+  return equali1(b)? a: mkfrac(a,b);
+}
+
+static GEN
+mod_to_rfrac(GEN x, GEN N, long B)
+{
+  GEN a, b;
+  long A, d = degpol(N);
+  if (B >= 0)
+  {
+    A = d-1 - B;
+    /* denominator bound useless, don't use it */
+    if (A < B) B = -1;
+  }
+  if (B < 0)
+  {
+    B = d >> 1;
+    A = odd(d)? B : B-1;
+  }
+  if (varn(N) != varn(x)) x = scalarpol(x, varn(N));
+  if (! RgXQ_ratlift(x, N, A, B, &a,&b)) return NULL;
+  if (degpol(RgX_gcd(a,b)) > 0) return NULL;
+  return gdiv(a,b);
+}
+
+/* k > 0 t_INT, x a t_FRAC, returns the convergent a/b
+ * of the continued fraction of x with b <= k maximal */
+static GEN
+bestappr_frac(GEN x, GEN k)
+{
+  pari_sp av;
+  GEN p0, p1, p, q0, q1, q, a, y;
+
+  if (cmpii(gel(x,2),k) <= 0) return gcopy(x);
+  av = avma; y = x;
+  p1 = gen_1; p0 = truedvmdii(gel(x,1), gel(x,2), &a); /* = floor(x) */
+  q1 = gen_0; q0 = gen_1;
+  x = mkfrac(a, gel(x,2)); /* = frac(x); now 0<= x < 1 */
+  for(;;)
+  {
+    x = ginv(x); /* > 1 */
+    a = typ(x)==t_INT? x: divii(gel(x,1), gel(x,2));
+    if (cmpii(a,k) > 0)
+    { /* next partial quotient will overflow limits */
+      GEN n, d;
+      a = divii(subii(k, q1), q0);
+      p = addii(mulii(a,p0), p1); p1=p0; p0=p;
+      q = addii(mulii(a,q0), q1); q1=q0; q0=q;
+      /* compare |y-p0/q0|, |y-p1/q1| */
+      n = gel(y,1);
+      d = gel(y,2);
+      if (absi_cmp(mulii(q1, subii(mulii(q0,n), mulii(d,p0))),
+                   mulii(q0, subii(mulii(q1,n), mulii(d,p1)))) < 0)
+                   { p1 = p0; q1 = q0; }
+      break;
+    }
+    p = addii(mulii(a,p0), p1); p1=p0; p0=p;
+    q = addii(mulii(a,q0), q1); q1=q0; q0=q;
+
+    if (cmpii(q0,k) > 0) break;
+    x = gsub(x,a); /* 0 <= x < 1 */
+    if (typ(x) == t_INT) { p1 = p0; q1 = q0; break; } /* x = 0 */
+
+  }
+  return gerepileupto(av, gdiv(p1,q1));
+}
+/* bestappr(t_REAL != 0), to maximal accuracy */
+static GEN
+bestappr_real_max(GEN x)
+{
+  pari_sp av = avma;
+  GEN p0, p1, p, q0, q1, q, a;
+  long e;
+  p1 = gen_1; a = p0 = floorr(x); q1 = gen_0; q0 = gen_1;
+  x = subri(x,a); /* 0 <= x < 1 */
+  e = bit_prec(x) - expo(x);
+  for(;;)
+  {
+    long d;
+    if (!signe(x) || expi(q0) > e) { p1 = p0; q1 = q0; break; }
+    x = invr(x); /* > 1 */
+    d = nbits2prec(expo(x) + 1);
+    if (d > lg(x)) { p1 = p0; q1 = q0; break; } /* original x was ~ 0 */
+
+    a = truncr(x); /* truncr(x) will NOT raise e_PREC */
+    p = addii(mulii(a,p0), p1); p1=p0; p0=p;
+    q = addii(mulii(a,q0), q1); q1=q0; q0=q;
+    x = subri(x,a); /* 0 <= x < 1 */
+  }
+  return gerepileupto(av, gdiv(p1,q1));
+}
+/* k > 0 t_INT, x != 0 a t_REAL, returns the convergent a/b
+ * of the continued fraction of x with b <= k maximal */
+static GEN
+bestappr_real(GEN x, GEN k)
+{
+  pari_sp av = avma;
+  GEN kr, p0, p1, p, q0, q1, q, a, y;
+
+  y = x;
+  p1 = gen_1; a = p0 = floorr(x);
+  q1 = gen_0; q0 = gen_1;
+  x = subri(x,a); /* 0 <= x < 1 */
+  if (!signe(x)) { cgiv(x); return a; }
+  kr = itor(k, realprec(x));
+  for(;;)
+  {
+    long d;
+    x = invr(x); /* > 1 */
+    if (cmprr(x,kr) > 0)
+    { /* next partial quotient will overflow limits */
+      a = divii(subii(k, q1), q0);
+      p = addii(mulii(a,p0), p1); p1=p0; p0=p;
+      q = addii(mulii(a,q0), q1); q1=q0; q0=q;
+      /* compare |y-p0/q0|, |y-p1/q1| */
+      if (absr_cmp(mulir(q1, subri(mulir(q0,y), p0)),
+                   mulir(q0, subri(mulir(q1,y), p1))) < 0)
+                   { p1 = p0; q1 = q0; }
+      break;
+    }
+    d = nbits2prec(expo(x) + 1);
+    if (d > lg(x)) { p1 = p0; q1 = q0; break; } /* original x was ~ 0 */
+
+    a = truncr(x); /* truncr(x) will NOT raise e_PREC */
+    p = addii(mulii(a,p0), p1); p1=p0; p0=p;
+    q = addii(mulii(a,q0), q1); q1=q0; q0=q;
+
+    if (cmpii(q0,k) > 0) break;
+    x = subri(x,a); /* 0 <= x < 1 */
+    if (!signe(x)) { p1 = p0; q1 = q0; break; }
+  }
+  return gerepileupto(av, gdiv(p1,q1));
+}
+
+/* k t_INT or NULL */
+static GEN
+bestappr_Q(GEN x, GEN k)
+{
+  long lx, tx = typ(x), i;
+  GEN a, y;
+
+  switch(tx)
+  {
+    case t_INT: return icopy(x);
+    case t_FRAC: return k? bestappr_frac(x, k): gcopy(x);
+    case t_REAL:
+      if (!signe(x)) return gen_0;
+      return k? bestappr_real(x, k): bestappr_real_max(x);
+
+    case t_INTMOD: {
+      pari_sp av = avma;
+      a = mod_to_frac(gel(x,2), gel(x,1), k); if (!a) return NULL;
+      return gerepilecopy(av, a);
+    }
+    case t_PADIC: {
+      pari_sp av = avma;
+      long v = valp(x);
+      a = mod_to_frac(gel(x,4), gel(x,3), k); if (!a) return NULL;
+      if (v) a = gmul(a, powis(gel(x,2), v));
+      return gerepilecopy(av, a);
+    }
+
+    case t_COMPLEX: case t_POLMOD: case t_POL: case t_SER: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+      for (; i<lx; i++)
+      {
+        a = bestappr_Q(gel(x,i),k); if (!a) return NULL;
+        gel(y,i) = a;
+      }
+      if (tx == t_POL) return normalizepol(y);
+      if (tx == t_SER) return normalize(y);
+      return y;
+  }
+  pari_err_TYPE("bestappr_Q",x);
+  return NULL; /* not reached */
+}
+
+static GEN
+bestappr_ser(GEN x, long B)
+{
+  long v = valp(x), lx = lg(x);
+  GEN N, t;
+  x = normalizepol(ser2pol_i(x, lx));
+  N = monomial(gen_1, lx-2, varn(x));
+  t = mod_to_rfrac(x, N, B); if (!t) return NULL;
+  if (v)
+  {
+    GEN a, b;
+    long vx;
+    if (typ(t) == t_POL) return RgX_mulXn(t, v);
+    /* t_RFRAC */
+    vx = varn(x);
+    a = gel(t,1);
+    b = gel(t,2);
+    v -= RgX_valrem(b, &b);
+    if (typ(a) == t_POL && varn(a) == vx) v += RgX_valrem(a, &a);
+    if (v < 0) b = RgX_shift(b, -v);
+    else if (v > 0) {
+      if (typ(a) != t_POL || varn(a) != vx) a = scalarpol_shallow(a, vx);
+      a = RgX_shift(a, v);
+    }
+    t = mkrfraccopy(a, b);
+  }
+  return t;
+}
+static GEN bestappr_RgX(GEN x, long B);
+/* x t_POLMOD, B >= 0 or < 0 [omit condition on B].
+ * Look for coprime t_POL a,b, deg(b)<=B, such that a/b = x */
+static GEN
+bestappr_RgX(GEN x, long B)
+{
+  long i, lx, tx = typ(x);
+  GEN y, t;
+  switch(tx)
+  {
+    case t_INT: case t_REAL: case t_INTMOD: case t_FRAC:
+    case t_COMPLEX: case t_PADIC: case t_QUAD: case t_POL:
+      return gcopy(x);
+
+    case t_RFRAC: {
+      pari_sp av = avma;
+      if (B < 0 || degpol(gel(x,2)) <= B) return gcopy(x);
+      x = rfractoser(x, varn(gel(x,2)), 2*B+1);
+      t = bestappr_ser(x, B); if (!t) return NULL;
+      return gerepileupto(av, t);
+    }
+    case t_POLMOD: {
+      pari_sp av = avma;
+      t = mod_to_rfrac(gel(x,2), gel(x,1), B); if (!t) return NULL;
+      return gerepileupto(av, t);
+    }
+    case t_SER: {
+      pari_sp av = avma;
+      t = bestappr_ser(x, B); if (!t) return NULL;
+      return gerepileupto(av, t);
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+      for (; i<lx; i++)
+      {
+        t = bestappr_RgX(gel(x,i),B); if (!t) return NULL;
+        gel(y,i) = t;
+      }
+      return y;
+  }
+  pari_err_TYPE("bestappr_RgX",x);
+  return NULL; /* not reached */
+}
+
+/* allow k = NULL: maximal accuracy */
+GEN
+bestappr(GEN x, GEN k)
+{
+  pari_sp av = avma;
+  if (k) { /* replace by floor(k) */
+    switch(typ(k))
+    {
+      case t_INT:
+        break;
+      case t_REAL: case t_FRAC:
+        k = floor_safe(k); /* left on stack for efficiency */
+        if (!signe(k)) k = gen_1;
+        break;
+      default:
+        pari_err_TYPE("bestappr [bound type]", k);
+        break;
+    }
+  }
+  x = bestappr_Q(x, k);
+  if (!x) { avma = av; return cgetg(1,t_VEC); }
+  return x;
+}
+GEN
+bestapprPade(GEN x, long B)
+{
+  pari_sp av = avma;
+  GEN t = bestappr_RgX(x, B);
+  if (!t) { avma = av; return cgetg(1,t_VEC); }
+  return t;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**         FUNDAMENTAL UNIT AND REGULATOR (QUADRATIC FIELDS)         **/
+/**                                                                   **/
+/***********************************************************************/
+
+static GEN
+get_quad(GEN f, GEN pol, long r)
+{
+  GEN p1 = gcoeff(f,1,2), q1 = gcoeff(f,2,2);
+  return mkquad(pol, r? subii(p1,q1): p1, q1);
+}
+
+/* replace f by f * [a,1; 1,0] */
+static void
+update_f(GEN f, GEN a)
+{
+  GEN p1;
+  p1 = gcoeff(f,1,1);
+  gcoeff(f,1,1) = addii(mulii(a,p1), gcoeff(f,1,2));
+  gcoeff(f,1,2) = p1;
+
+  p1 = gcoeff(f,2,1);
+  gcoeff(f,2,1) = addii(mulii(a,p1), gcoeff(f,2,2));
+  gcoeff(f,2,2) = p1;
+}
+
+GEN
+quadunit(GEN x)
+{
+  pari_sp av = avma, av2, lim;
+  GEN pol, y, a, u, v, sqd, f;
+  long r;
+
+  check_quaddisc_real(x, &r, "quadunit");
+  pol = quadpoly(x);
+  sqd = sqrti(x); av2 = avma; lim = stack_lim(av2,2);
+  a = shifti(addsi(r,sqd),-1);
+  f = mkmat2(mkcol2(a, gen_1), mkcol2(gen_1, gen_0)); /* [a,0; 1,0] */
+  u = stoi(r); v = gen_2;
+  for(;;)
+  {
+    GEN u1, v1;
+    u1 = subii(mulii(a,v),u);
+    v1 = divii(subii(x,sqri(u1)),v);
+    if ( equalii(v,v1) ) {
+      y = get_quad(f,pol,r);
+      update_f(f,a);
+      y = gdiv(get_quad(f,pol,r), gconj(y));
+      break;
+    }
+    a = divii(addii(sqd,u1), v1);
+    if ( equalii(u,u1) ) {
+      y = get_quad(f,pol,r);
+      y = gdiv(y, gconj(y));
+      break;
+    }
+    update_f(f,a);
+    u = u1; v = v1;
+    if (low_stack(lim, stack_lim(av2,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"quadunit");
+      gerepileall(av2,4, &a,&f,&u,&v);
+    }
+  }
+  if (signe(gel(y,3)) < 0) y = gneg(y);
+  return gerepileupto(av, y);
+}
+
+GEN
+quadregulator(GEN x, long prec)
+{
+  pari_sp av = avma, av2, lim;
+  GEN R, rsqd, u, v, sqd;
+  long r, Rexpo;
+
+  check_quaddisc_real(x, &r, "quadregulator");
+  sqd = sqrti(x);
+  rsqd = gsqrt(x,prec);
+  Rexpo = 0; R = real2n(1, prec); /* = 2 */
+  av2 = avma; lim = stack_lim(av2,2);
+  u = stoi(r); v = gen_2;
+  for(;;)
+  {
+    GEN u1 = subii(mulii(divii(addii(u,sqd),v), v), u);
+    GEN v1 = divii(subii(x,sqri(u1)),v);
+    if (equalii(v,v1))
+    {
+      R = sqrr(R); shiftr_inplace(R, -1);
+      R = mulrr(R, divri(addir(u1,rsqd),v));
+      break;
+    }
+    if (equalii(u,u1))
+    {
+      R = sqrr(R); shiftr_inplace(R, -1);
+      break;
+    }
+    R = mulrr(R, divri(addir(u1,rsqd),v));
+    Rexpo += expo(R); setexpo(R,0);
+    u = u1; v = v1;
+    if (Rexpo & ~EXPOBITS) pari_err_OVERFLOW("quadregulator [exponent]");
+    if (low_stack(lim, stack_lim(av2,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"quadregulator");
+      gerepileall(av2,3, &R,&u,&v);
+    }
+  }
+  R = logr_abs(divri(R,v));
+  if (Rexpo)
+  {
+    GEN t = mulsr(Rexpo, mplog2(prec));
+    shiftr_inplace(t, 1);
+    R = addrr(R,t);
+  }
+  return gerepileuptoleaf(av, R);
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**                            CLASS NUMBER                             **/
+/**                                                                     **/
+/*************************************************************************/
+static int qfb_is_1(GEN f) { return equali1(gel(f,1)); }
+static GEN qfb_pow(void *E, GEN f, GEN n) { (void)E; return powgi(f,n); }
+static const struct bb_group qfb_group={ NULL,qfb_pow,NULL,NULL,
+                                         NULL,qfb_is_1,NULL};
+
+GEN
+qfbclassno0(GEN x,long flag)
+{
+  switch(flag)
+  {
+    case 0: return map_proto_G(classno,x);
+    case 1: return map_proto_G(classno2,x);
+    default: pari_err_FLAG("qfbclassno");
+  }
+  return NULL; /* not reached */
+}
+
+/* f^h = 1, return order(f) */
+static GEN
+find_order(GEN f, GEN h) { return gen_order(f, h, NULL, &qfb_group); }
+
+static GEN
+end_classno(GEN h, GEN hin, GEN forms)
+{
+  GEN a, b, p1, q, fh, fg, f = gel(forms,1);
+  long i, com, l = lg(forms);
+
+  h = find_order(f,h); /* H = <f> */
+  q = diviiround(hin, h); /* approximate order of G/H */
+  for (i=2; i < l; i++)
+  {
+    pari_sp av = avma;
+    fg = powgi(gel(forms,i), h);
+    fh = powgi(fg, q);
+    a = gel(fh,1);
+    if (equali1(a)) continue;
+    b = gel(fh,2); p1 = fg;
+    for (com=1; ; com++, p1 = gmul(p1,fg))
+      if (equalii(gel(p1,1), a) && absi_equal(gel(p1,2), b)) break;
+    if (signe(gel(p1,2)) == signe(b)) com = -com;
+    /* f_i ^ h(q+com) = 1 */
+    q = addsi(com,q);
+    if (gequal0(q))
+    { /* f^(ih) != 1 for all 0 < i <= oldq. Happen if the original upper bound
+         for h was wrong */
+      ulong c;
+      p1 = fh;
+      for (c=1; ; c++, p1 = gmul(p1,fh))
+        if (qfb_is_1(p1)) break;
+      q = mulsi(-com, find_order(fh, utoipos(c)));
+    }
+    q = gerepileuptoint(av, q);
+  }
+  return mulii(q,h);
+}
+
+/* Write x = Df^2, where D = fundamental discriminant,
+ * P^E = factorisation of conductor f, with E[i] >= 0 */
+static void
+corediscfact(GEN x, long xmod4, GEN *ptD, GEN *ptP, GEN *ptE)
+{
+  long s = signe(x), l, i;
+  GEN fa = absi_factor(x);
+  GEN d, P = gel(fa,1), E = gtovecsmall(gel(fa,2));
+
+  l = lg(P); d = gen_1;
+  for (i=1; i<l; i++)
+  {
+    if (E[i] & 1) d = mulii(d, gel(P,i));
+    E[i] >>= 1;
+  }
+  if (!xmod4 && mod4(d) != ((s < 0)? 3: 1)) { d = shifti(d,2); E[1]--; }
+  *ptD = (s < 0)? negi(d): d;
+  *ptP = P;
+  *ptE = E;
+}
+
+static GEN
+conductor_part(GEN x, long xmod4, GEN *ptD, GEN *ptreg)
+{
+  long l, i, s = signe(x);
+  GEN E, H, D, P, reg;
+
+  corediscfact(x, xmod4, &D, &P, &E);
+  H = gen_1; l = lg(P);
+  /* f \prod_{p|f}  [ 1 - (D/p) p^-1 ] = \prod_{p^e||f} p^(e-1) [ p - (D/p) ] */
+  for (i=1; i<l; i++)
+  {
+    long e = E[i];
+    if (e)
+    {
+      GEN p = gel(P,i);
+      H = mulii(H, subis(p, kronecker(D,p)));
+      if (e >= 2) H = mulii(H, powiu(p,e-1));
+    }
+  }
+
+  /* divide by [ O_K^* : O^* ] */
+  if (s < 0)
+  {
+    reg = NULL;
+    switch(itou_or_0(D))
+    {
+      case 4: H = shifti(H,-1); break;
+      case 3: H = divis(H,3); break;
+    }
+  } else {
+    reg = quadregulator(D,DEFAULTPREC);
+    if (!equalii(x,D))
+      H = divii(H, roundr(divrr(quadregulator(x,DEFAULTPREC), reg)));
+  }
+  if (ptreg) *ptreg = reg;
+  *ptD = D; return H;
+}
+
+static long
+two_rank(GEN x)
+{
+  GEN p = gel(absi_factor(x),1);
+  long l = lg(p)-1;
+#if 0 /* positive disc not needed */
+  if (signe(x) > 0)
+  {
+    long i;
+    for (i=1; i<=l; i++)
+      if (mod4(gel(p,i)) == 3) { l--; break; }
+  }
+#endif
+  return l-1;
+}
+
+static GEN
+sqr_primeform(GEN x, ulong p) { return redimag(qfisqr(primeform_u(x, p))); }
+
+static ulong
+_low(GEN x) { return signe(x)? mod2BIL(x): 0; }
+
+/* h(x) for x<0 using Baby Step/Giant Step.
+ * Assumes G is not too far from being cyclic.
+ *
+ * Compute G^2 instead of G so as to kill most of the non-cyclicity */
+GEN
+classno(GEN x)
+{
+  const long MAXFORM = 12;
+  pari_sp av = avma, av2, lim;
+  long r2, p, nforms, k, i, j, com, s;
+  GEN forms, count, index, tabla, tablb, hash, p1, p2;
+  GEN hin, h, f, fh, fg, ftest, Hf, D;
+  forprime_t S;
+
+  if (signe(x) >= 0) return classno2(x);
+
+  check_quaddisc(x, &s, &k, "classno");
+  if (cmpiu(x,12) <= 0) return gen_1;
+
+  Hf = conductor_part(x, k, &D, NULL);
+  if (cmpiu(D,12) <= 0) return gerepilecopy(av, Hf);
+
+  p2 = gsqrt(absi(D),DEFAULTPREC);
+  p1 = mulrr(divrr(p2,mppi(DEFAULTPREC)), dbltor(1.005)); /*overshoot by 0.5%*/
+  s = itos_or_0( truncr(shiftr(sqrtr(p2), 1)) );
+  if (!s) pari_err_OVERFLOW("classno [discriminant too large]");
+  if      (s < 10)   s = 200;
+  else if (s < 20)   s = 1000;
+  else if (s < 5000) s = 5000;
+  forms = vectrunc_init(MAXFORM+1);
+
+  u_forprime_init(&S, 2, s);
+  nforms = 0;
+  while ( (p = u_forprime_next(&S)) )
+  {
+    long d, k = kroiu(D,p);
+    pari_sp av3;
+    if (!k) continue;
+    if (k > 0)
+    {
+      if (++nforms < MAXFORM) vectrunc_append(forms, sqr_primeform(D,p));
+      d = p - 1;
+    }
+    else
+      d = p + 1;
+    av3 = avma; affrr(divru(mulur(p,p1),d), p1);
+    avma = av3;
+  }
+  r2 = two_rank(D);
+  h = hin = roundr(shiftr(p1, -r2));
+  s = 2*itos(gceil(sqrtnr(p1, 4)));
+  if (s > 10000) s = 10000;
+
+  count = new_chunk(256); for (i=0; i<=255; i++) count[i]=0;
+  index = new_chunk(257);
+  tabla = new_chunk(10000);
+  tablb = new_chunk(10000);
+  hash  = new_chunk(10000);
+  f = gel(forms,1);
+  p1 = fh = powgi(f, h);
+  for (i=0; i<s; i++, p1 = qficomp(p1,f))
+  {
+    tabla[i] = _low(gel(p1,1));
+    tablb[i] = _low(gel(p1,2)); count[tabla[i]&255]++;
+  }
+  /* follow the idea of counting sort to avoid maintaining linked lists in
+   * hashtable */
+  index[0]=0; for (i=0; i< 255; i++) index[i+1] = index[i]+count[i];
+  /* index[i] = # of forms hashing to <= i */
+  for (i=0; i<s; i++) hash[ index[tabla[i]&255]++ ] = i;
+  index[0]=0; for (i=0; i<=255; i++) index[i+1] = index[i]+count[i];
+  /* hash[index[i-1]..index[i]-1] = forms hashing to i */
+
+  fg = gpowgs(f,s); av2 = avma; lim = stack_lim(av2,2);
+  ftest = gpowgs(p1,0);
+  for (com=0; ; com++)
+  {
+    long j1, k, l;
+    GEN a, b;
+    a = gel(ftest,1); k = _low(a);
+    b = gel(ftest,2); l = _low(b); j = k&255;
+    for (j1=index[j]; j1 < index[j+1]; j1++)
+    {
+      long j2 = hash[j1];
+      if (tabla[j2] == k && tablb[j2] == l)
+      {
+        p1 = gmul(gpowgs(f,j2),fh);
+        if (equalii(gel(p1,1), a) && absi_equal(gel(p1,2), b))
+        { /* p1 = ftest or ftest^(-1), we are done */
+          if (signe(gel(p1,2)) == signe(b)) com = -com;
+          h = addii(addis(h,j2), mulss(s,com));
+          h = end_classno(h, hin, forms);
+          return gerepileuptoint(av, shifti(mulii(h,Hf), r2));
+        }
+      }
+    }
+    ftest = gmul(ftest,fg);
+    if (equali1(gel(ftest,1))) pari_err_IMPL("classno with too small order");
+    if (low_stack(lim, stack_lim(av2,2))) ftest = gerepileupto(av2,ftest);
+  }
+}
+
+/* use Euler products */
+GEN
+classno2(GEN x)
+{
+  pari_sp av = avma;
+  const long prec = DEFAULTPREC;
+  long n, i, r, s;
+  GEN p1, p2, S, p4, p5, p7, Hf, Pi, reg, logd, d, dr, D, half;
+
+  check_quaddisc(x, &s, &r, "classno2");
+  if (s < 0 && cmpiu(x,12) <= 0) return gen_1;
+
+  Hf = conductor_part(x, r, &D, &reg);
+  if (s < 0 && cmpiu(D,12) <= 0) return gerepilecopy(av, Hf); /* |D| < 12*/
+
+  Pi = mppi(prec);
+  d = absi(D); dr = itor(d, prec);
+  logd = logr_abs(dr);
+  p1 = sqrtr(divrr(mulir(d,logd), gmul2n(Pi,1)));
+  if (s > 0)
+  {
+    GEN invlogd = invr(logd);
+    p2 = subsr(1, shiftr(mulrr(logr_abs(reg),invlogd),1));
+    if (cmprr(sqrr(p2), shiftr(invlogd,1)) >= 0) p1 = mulrr(p2,p1);
+  }
+  n = itos_or_0( mptrunc(p1) );
+  if (!n) pari_err_OVERFLOW("classno [discriminant too large]");
+
+  p4 = divri(Pi,d);
+  p7 = invr(sqrtr_abs(Pi));
+  half = real2n(-1, prec);
+  if (s > 0)
+  { /* i = 1, shortcut */
+    p1 = sqrtr_abs(dr);
+    p5 = subsr(1, mulrr(p7,incgamc(half,p4,prec)));
+    S = addrr(mulrr(p1,p5), eint1(p4,prec));
+    for (i=2; i<=n; i++)
+    {
+      long k = kroiu(D,i); if (!k) continue;
+      p2 = mulir(sqru(i), p4);
+      p5 = subsr(1, mulrr(p7,incgamc(half,p2,prec)));
+      p5 = addrr(divru(mulrr(p1,p5),i), eint1(p2,prec));
+      S = (k>0)? addrr(S,p5): subrr(S,p5);
+    }
+    S = shiftr(divrr(S,reg),-1);
+  }
+  else
+  { /* i = 1, shortcut */
+    p1 = gdiv(sqrtr_abs(dr), Pi);
+    p5 = subsr(1, mulrr(p7,incgamc(half,p4,prec)));
+    S = addrr(p5, divrr(p1, mpexp(p4)));
+    for (i=2; i<=n; i++)
+    {
+      long k = kroiu(D,i); if (!k) continue;
+      p2 = mulir(sqru(i), p4);
+      p5 = subsr(1, mulrr(p7,incgamc(half,p2,prec)));
+      p5 = addrr(p5, divrr(p1, mulur(i, mpexp(p2))));
+      S = (k>0)? addrr(S,p5): subrr(S,p5);
+    }
+  }
+  return gerepileuptoint(av, mulii(Hf, roundr(S)));
+}
+
+static GEN
+hclassno2(GEN x)
+{
+  long i, l, s, xmod4;
+  GEN Q, H, D, P, E;
+
+  x = negi(x);
+  check_quaddisc(x, &s, &xmod4, "hclassno");
+  corediscfact(x, xmod4, &D, &P, &E);
+
+  Q = quadclassunit0(D, 0, NULL, 0);
+  H = gel(Q,1); l = lg(P);
+
+  /* H \prod_{p^e||f}  (1 + (p^e-1)/(p-1))[ p - (D/p) ] */
+  for (i=1; i<l; i++)
+  {
+    long e = E[i];
+    if (e)
+    {
+      GEN p = gel(P,i), t = subis(p, kronecker(D,p));
+      if (e > 1) t = mulii(t, diviiexact(subis(powiu(p,e), 1), subis(p,1)));
+      H = mulii(H, addsi(1, t));
+    }
+  }
+  switch( itou_or_0(D) )
+  {
+    case 3: H = gdivgs(H, 3); break;
+    case 4: H = gdivgs(H, 2); break;
+  }
+  return H;
+}
+
+GEN
+hclassno(GEN x)
+{
+  ulong a, b, b2, d, h;
+  long s;
+  int f;
+
+  if (typ(x) != t_INT) pari_err_TYPE("hclassno",x);
+  s = signe(x);
+  if (s < 0) return gen_0;
+  if (!s) return gdivgs(gen_1, -12);
+
+  a = mod4(x); if (a == 1 || a == 2) return gen_0;
+
+  d = itou_or_0(x);
+  if (!d || d > 500000) return hclassno2(x);
+
+  h = 0; b = d&1; b2 = (1+d)>>2; f=0;
+  if (!b)
+  {
+    for (a=1; a*a<b2; a++)
+      if (b2%a == 0) h++;
+    f = (a*a==b2); b=2; b2=(4+d)>>2;
+  }
+  while (b2*3 < d)
+  {
+    if (b2%b == 0) h++;
+    for (a=b+1; a*a < b2; a++)
+      if (b2%a == 0) h += 2;
+    if (a*a == b2) h++;
+    b += 2; b2 = (b*b+d)>>2;
+  }
+  if (b2*3 == d)
+  {
+    GEN y = cgetg(3,t_FRAC);
+    gel(y,1) = utoipos(3*h+1);
+    gel(y,2) = utoipos(3); return y;
+  }
+  if (f)
+  {
+    GEN y = cgetg(3,t_FRAC);
+    gel(y,1) = utoipos(2*h+1);
+    gel(y,2) = gen_2; return y;
+  }
+  return utoipos(h);
+}
+
diff --git a/src/basemath/arith2.c b/src/basemath/arith2.c
new file mode 100644
index 0000000..5a671c7
--- /dev/null
+++ b/src/basemath/arith2.c
@@ -0,0 +1,1440 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*********************************************************************/
+/**                                                                 **/
+/**                     ARITHMETIC FUNCTIONS                        **/
+/**                        (second part)                            **/
+/**                                                                 **/
+/*********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+static ulong _maxprime = 0;
+static ulong diffptrlen;
+
+/* Building/Rebuilding the diffptr table. The actual work is done by the
+ * following two subroutines;  the user entry point is the function
+ * initprimes() below.  initprimes1() is the old algorithm, called when
+ * maxnum (size) is moderate. Must be called after pari_init_stack() )*/
+static void
+initprimes1(ulong size, long *lenp, ulong *lastp, byteptr p1)
+{
+  pari_sp av = avma;
+  long k;
+  byteptr q, r, s, p = (byteptr)stack_calloc(size+2), fin = p + size;
+
+  for (r=q=p,k=1; r<=fin; )
+  {
+    do { r+=k; k+=2; r+=k; } while (*++q);
+    for (s=r; s<=fin; s+=k) *s = 1;
+  }
+  r = p1; *r++ = 2; *r++ = 1; /* 2 and 3 */
+  for (s=q=p+1; ; s=q)
+  {
+    do q++; while (*q);
+    if (q > fin) break;
+    *r++ = (unsigned char) ((q-s) << 1);
+  }
+  *r++ = 0;
+  *lenp = r - p1;
+  *lastp = ((s - p) << 1) + 1;
+  avma = av;
+}
+
+/*  Timing in ms (Athlon/850; reports 512K of secondary cache; looks
+    like there is 64K of quickier cache too).
+
+      arena|    30m     100m    300m    1000m    2000m  <-- primelimit
+      =================================================
+      16K       1.1053  1.1407  1.2589  1.4368   1.6086
+      24K       1.0000  1.0625  1.1320  1.2443   1.3095
+      32K       1.0000  1.0469  1.0761  1.1336   1.1776
+      48K       1.0000  1.0000  1.0254  1.0445   1.0546
+      50K       1.0000  1.0000  1.0152  1.0345   1.0464
+      52K       1.0000  1.0000  1.0203  1.0273   1.0362
+      54K       1.0000  1.0000  1.0812  1.0216   1.0281
+      56K       1.0526  1.0000  1.0051  1.0144   1.0205
+      58K       1.0000  1.0000  1.0000  1.0086   1.0123
+      60K       0.9473  0.9844  1.0051  1.0014   1.0055
+      62K       1.0000  0.9844  0.9949  0.9971   0.9993
+      64K       1.0000  1.0000  1.0000  1.0000   1.0000
+      66K       1.2632  1.2187  1.2183  1.2055   1.1953
+      68K       1.4211  1.4844  1.4721  1.4425   1.4188
+      70K       1.7368  1.7188  1.7107  1.6767   1.6421
+      72K       1.9474  1.9531  1.9594  1.9023   1.8573
+      74K       2.2105  2.1875  2.1827  2.1207   2.0650
+      76K       2.4211  2.4219  2.4010  2.3305   2.2644
+      78K       2.5789  2.6250  2.6091  2.5330   2.4571
+      80K       2.8421  2.8125  2.8223  2.7213   2.6380
+      84K       3.1053  3.1875  3.1776  3.0819   2.9802
+      88K       3.5263  3.5312  3.5228  3.4124   3.2992
+      92K       3.7895  3.8438  3.8375  3.7213   3.5971
+      96K       4.0000  4.1093  4.1218  3.9986   3.9659
+      112K      4.3684  4.5781  4.5787  4.4583   4.6115
+      128K      4.7368  4.8750  4.9188  4.8075   4.8997
+      192K      5.5263  5.7188  5.8020  5.6911   5.7064
+      256K      6.0000  6.2187  6.3045  6.1954   6.1033
+      384K      6.7368  6.9531  7.0405  6.9181   6.7912
+      512K      7.3158  7.5156  7.6294  7.5000   7.4654
+      768K      9.1579  9.4531  9.6395  9.5014   9.1075
+      1024K    10.368  10.7497 10.9999 10.878   10.8201
+      1536K    12.579  13.3124 13.7660 13.747   13.4739
+      2048K    13.737  14.4839 15.0509 15.151   15.1282
+      3076K    14.789  15.5780 16.2993 16.513   16.3365
+
+    Now the same number relative to the model
+
+    (1 + 0.36*sqrt(primelimit)/arena) * (arena <= 64 ? 1.05 : (arena-64)**0.38)
+
+     [SLOW2_IN_ROOTS = 0.36, ALPHA = 0.38]
+
+      arena|    30m     100m    300m    1000m    2000m  <-- primelimit
+      =================================================
+        16K    1.014    0.9835  0.9942  0.9889  1.004
+        24K    0.9526   0.9758  0.9861  0.9942  0.981
+        32K    0.971    0.9939  0.9884  0.9849  0.9806
+        48K    0.9902   0.9825  0.996   0.9945  0.9885
+        50K    0.9917   0.9853  0.9906  0.9926  0.9907
+        52K    0.9932   0.9878  0.9999  0.9928  0.9903
+        54K    0.9945   0.9902  1.064   0.9939  0.9913
+        56K    1.048    0.9924  0.9925  0.993   0.9921
+        58K    0.9969   0.9945  0.9909  0.9932  0.9918
+        60K    0.9455   0.9809  0.9992  0.9915  0.9923
+        62K    0.9991   0.9827  0.9921  0.9924  0.9929
+        64K    1        1       1       1       1
+        66K    1.02     0.9849  0.9857  0.9772  0.9704
+        68K    0.8827   0.9232  0.9176  0.9025  0.8903
+        70K    0.9255   0.9177  0.9162  0.9029  0.8881
+        72K    0.9309   0.936   0.9429  0.9219  0.9052
+        74K    0.9715   0.9644  0.967   0.9477  0.9292
+        76K    0.9935   0.9975  0.9946  0.9751  0.9552
+        78K    0.9987   1.021   1.021   1.003   0.9819
+        80K    1.047    1.041   1.052   1.027   1.006
+        84K    1.052    1.086   1.092   1.075   1.053
+        88K    1.116    1.125   1.133   1.117   1.096
+        92K    1.132    1.156   1.167   1.155   1.134
+        96K    1.137    1.177   1.195   1.185   1.196
+       112K    1.067    1.13    1.148   1.15    1.217
+       128K    1.04     1.083   1.113   1.124   1.178
+       192K    0.9368   0.985   1.025   1.051   1.095
+       256K    0.8741   0.9224  0.9619  0.995   1.024
+       384K    0.8103   0.8533  0.8917  0.9282  0.9568
+       512K    0.7753   0.8135  0.8537  0.892   0.935
+       768K    0.8184   0.8638  0.9121  0.9586  0.9705
+      1024K    0.8241   0.8741  0.927   0.979   1.03
+      1536K    0.8505   0.9212  0.9882  1.056   1.096
+      2048K    0.8294   0.8954  0.9655  1.041   1.102
+
+*/
+
+#ifndef SLOW2_IN_ROOTS
+  /* SLOW2_IN_ROOTS below 3: some slowdown starts to be noticable
+   * when things fit into the cache on Sparc.
+   * The choice of 2.6 gives a slowdown of 1-2% on UltraSparcII,
+   * but makes calculations for "maximum" of 436273009
+   * fit into 256K cache (still common for some architectures).
+   *
+   * One may change it when small caches become uncommon, but the gain
+   * is not going to be very noticable... */
+#  ifdef i386           /* gcc defines this? */
+#    define SLOW2_IN_ROOTS      0.36
+#  else
+#    define SLOW2_IN_ROOTS      2.6
+#  endif
+#endif
+#ifndef CACHE_ARENA
+#  ifdef i386           /* gcc defines this? */
+   /* Due to smaller SLOW2_IN_ROOTS, smaller arena is OK; fit L1 cache */
+#    define CACHE_ARENA (63 * 1024UL) /* No slowdown even with 64K L1 cache */
+#  else
+#    define CACHE_ARENA (200 * 1024UL) /* No slowdown even with 256K L2 cache */
+#  endif
+#endif
+
+#define CACHE_ALPHA     (0.38)          /* Cache performance model parameter */
+#define CACHE_CUTOFF    (0.018)         /* Cache performance not smooth here */
+
+static double slow2_in_roots = SLOW2_IN_ROOTS;
+
+typedef struct {
+    ulong arena;
+    double power;
+    double cutoff;
+} cache_model_t;
+
+static cache_model_t cache_model = { CACHE_ARENA, CACHE_ALPHA, CACHE_CUTOFF };
+
+/* Assume that some calculation requires a chunk of memory to be
+   accessed often in more or less random fashion (as in sieving).
+   Assume that the calculation can be done in steps by subdividing the
+   chunk into smaller subchunks (arenas) and treating them
+   separately.  Assume that the overhead of subdivision is equivalent
+   to the number of arenas.
+
+   Find an optimal size of the arena taking into account the overhead
+   of subdivision, and the overhead of arena not fitting into the
+   cache.  Assume that arenas of size slow2_in_roots slows down the
+   calculation 2x (comparing to very big arenas; when cache hits do
+   not matter).  Since cache performance varies wildly with
+   architecture, load, and wheather (especially with cache coloring
+   enabled), use an idealized cache model based on benchmarks above.
+
+   Assume that an independent region of FIXED_TO_CACHE bytes is accessed
+   very often concurrently with the arena access.
+ */
+static ulong
+good_arena_size(ulong slow2_size, ulong total, ulong fixed_to_cache,
+                cache_model_t *cache_model)
+{
+  ulong asize, cache_arena = cache_model->arena;
+  double Xmin, Xmax, A, B, C1, C2, D, V;
+  double alpha = cache_model->power, cut_off = cache_model->cutoff;
+
+  /* Estimated relative slowdown,
+     with overhead = max((fixed_to_cache+arena)/cache_arena - 1, 0):
+
+     1 + slow2_size/arena due to initialization overhead;
+
+     max(1, 4.63 * overhead^0.38 ) due to footprint > cache size.
+
+     [The latter is hard to substantiate theoretically, but this
+     function describes benchmarks pretty close; it does not hurt that
+     one can minimize it explicitly too ;-).  The switch between
+     different choices of max() happens when overhead=0.018.]
+
+     Thus the problem is minimizing (1 + slow2_size/arena)*overhead**0.29.
+     This boils down to F=((X+A)/(X+B))X^alpha, X=overhead,
+     B = (1 - fixed_to_cache/cache_arena), A = B +
+     slow2_size/cache_arena, alpha = 0.38, and X>=0.018, X>-B.
+
+     We need to find the rightmost root of (X+A)*(X+B) - alpha(A-B)X to the
+     right of 0.018 (if such exists and is below Xmax).  Then we manually
+     check the remaining region [0, 0.018].
+
+     Since we cannot trust the purely-experimental cache-hit slowdown
+     function, as a sanity check always prefer fitting into the
+     cache (or "almost fitting") if F-law predicts that the larger
+     value of the arena provides less than 10% speedup.
+
+   */
+
+  /* The simplest case: we fit into cache */
+  if (total + fixed_to_cache <= cache_arena)
+      return total;
+  /* The simple case: fitting into cache doesn't slow us down more than 10% */
+  if (cache_arena - fixed_to_cache > 10 * slow2_size) {
+      asize = cache_arena - fixed_to_cache;
+      if (asize > total) asize = total; /* Automatically false... */
+      return asize;
+  }
+  /* Slowdown of not fitting into cache is significant.  Try to optimize.
+     Do not be afraid to spend some time on optimization - in trivial
+     cases we do not reach this point; any gain we get should
+     compensate the time spent on optimization.  */
+
+  B = (1 - ((double)fixed_to_cache)/cache_arena);
+  A = B + ((double)slow2_size)/cache_arena;
+  C2 = A*B;
+  C1 = (A + B - 1/alpha*(A - B))/2;
+  D = C1*C1 - C2;
+  if (D > 0)
+      V = cut_off*cut_off + 2*C1*cut_off + C2; /* Value at CUT_OFF */
+  else
+      V = 0;                            /* Peacify the warning */
+  Xmin = cut_off;
+  Xmax = ((double)total - fixed_to_cache)/cache_arena; /* Two candidates */
+
+  if ( D <= 0 || (V >= 0 && C1 + cut_off >= 0) ) /* slowdown increasing */
+      Xmax = cut_off;                   /* Only one candidate */
+  else if (V >= 0 &&                    /* slowdown concave down */
+           ((Xmax + C1) <= 0 || (Xmax*Xmax + 2*C1*Xmax + C2) <= 0))
+      /* DO NOTHING */;                 /* Keep both candidates */
+  else if (V <= 0 && (Xmax*Xmax + 2*C1*Xmax + C2) <= 0) /* slowdown decreasing */
+      Xmin = cut_off;                   /* Only one candidate */
+  else /* Now we know: 2 roots, the largest is in CUT_OFF..Xmax */
+      Xmax = sqrt(D) - C1;
+  if (Xmax != Xmin) {   /* Xmin == CUT_OFF; Check which one is better */
+      double v1 = (cut_off + A)/(cut_off + B);
+      double v2 = 2.33 * (Xmax + A)/(Xmax + B) * pow(Xmax, alpha);
+
+      if (1.1 * v2 >= v1) /* Prefer fitting into the cache if slowdown < 10% */
+          V = v1;
+      else {
+          Xmin = Xmax;
+          V = v2;
+      }
+  } else if (B > 0)                     /* We need V */
+      V = 2.33 * (Xmin + A)/(Xmin + B) * pow(Xmin, alpha);
+  if (B > 0 && 1.1 * V > A/B)  /* Now Xmin is the minumum.  Compare with 0 */
+      Xmin = 0;
+
+  asize = (ulong)((1 + Xmin)*cache_arena - fixed_to_cache);
+  if (asize > total) asize = total;     /* May happen due to approximations */
+  return asize;
+}
+
+/* Use as in
+    install(set_optimize,lLDG)          \\ Through some M too?
+    set_optimize(2,1) \\ disable dependence on limit
+    \\ 1: how much cache usable, 2: slowdown of setup, 3: alpha, 4: cutoff
+    \\ 2,3,4 are in units of 0.001
+
+    { time_primes_arena(ar,limit) =     \\ ar = arena size in K
+        set_optimize(1,floor(ar*1024));
+        default(primelimit, 200 000);   \\ 100000 results in *larger* malloc()!
+        gettime;
+        default(primelimit, floor(limit));
+        if(ar >= 1, ar=floor(ar));
+        print("arena "ar"K => "gettime"ms");
+    }
+*/
+long
+set_optimize(long what, GEN g)
+{
+  long ret = 0;
+
+  switch (what) {
+  case 1:
+    ret = (long)cache_model.arena;
+    break;
+  case 2:
+    ret = (long)(slow2_in_roots * 1000);
+    break;
+  case 3:
+    ret = (long)(cache_model.power * 1000);
+    break;
+  case 4:
+    ret = (long)(cache_model.cutoff * 1000);
+    break;
+  default:
+    pari_err_BUG("set_optimize");
+    break;
+  }
+  if (g != NULL) {
+    ulong val = itou(g);
+
+    switch (what) {
+    case 1: cache_model.arena = val; break;
+    case 2: slow2_in_roots     = (double)val / 1000.; break;
+    case 3: cache_model.power  = (double)val / 1000.; break;
+    case 4: cache_model.cutoff = (double)val / 1000.; break;
+    }
+  }
+  return ret;
+}
+
+/* s is odd; prime differences (starting from 5-3=2) start at known_primes[2],
+  terminated by a 0 byte. Checks n odd numbers starting at 'start', setting
+  bytes starting at data to 0 (composite) or 1 (prime) */
+static void
+sieve_chunk(byteptr known_primes, ulong s, byteptr data, ulong n)
+{
+  ulong p, cnt = n-1, start = s, delta = 1;
+  byteptr q;
+
+  memset(data, 0, n);
+  start >>= 1;  /* (start - 1)/2 */
+  start += n; /* Corresponds to the end */
+  /* data corresponds to start, q runs over primediffs */
+  for (q = known_primes + 1, p = 3; delta; delta = *++q, p += delta)
+  { /* first odd number >= start > p and divisible by p
+       = last odd number <= start + 2p - 2 and 0 (mod p)
+       = p + last number <= start + p - 2 and 0 (mod 2p)
+       = p + start+p-2 - (start+p-2) % 2p
+       = start + 2(p - 1 - ((start-1)/2 + (p-1)/2) % p). */
+    long off = cnt - ((start+(p>>1)) % p);
+    while (off >= 0) { data[off] = 1; off -= p; }
+  }
+}
+
+/* assume maxnum <= 436273289 < 2^29 */
+static void
+initprimes0(ulong maxnum, long *lenp, ulong *lastp, byteptr p1)
+{
+  pari_sp av = avma;
+  long alloced, psize;
+  byteptr q, end, p, end1, plast, curdiff;
+  ulong last, remains, curlow, rootnum, asize;
+  ulong prime_above;
+  byteptr p_prime_above;
+
+  maxnum |= 1; /* make it odd. */
+  /* base case */
+  if (maxnum < 1ul<<17) { initprimes1(maxnum>>1, lenp, lastp, p1); return; }
+
+  /* Checked to be enough up to 40e6, attained at 155893 */
+  rootnum = usqrt(maxnum) | 1;
+  initprimes1(rootnum>>1, &psize, &last, p1);
+  end1 = p1 + psize - 1;
+  remains = (maxnum - last) >> 1; /* number of odd numbers to check */
+
+  /* we access primes array of psize too; but we access it consecutively,
+   * thus we do not include it in fixed_to_cache */
+  asize = good_arena_size((ulong)(rootnum * slow2_in_roots), remains+1, 0,
+                          &cache_model) - 1;
+  /* enough room on the stack ? */
+  alloced = (((byteptr)avma) <= ((byteptr)bot) + asize);
+  if (alloced)
+    p = (byteptr)pari_malloc(asize+1);
+  else
+    p = (byteptr)stack_malloc(asize+1);
+  end = p + asize; /* the 0 sentinel goes at end. */
+  curlow = last + 2; /* First candidate: know primes up to last (odd). */
+  curdiff = end1;
+
+  /* During each iteration p..end-1 represents a range of odd
+     numbers.  plast is a pointer which represents the last prime seen,
+     it may point before p..end-1. */
+  plast = p - 1;
+  p_prime_above = p1 + 2;
+  prime_above = 3;
+  while (remains)
+  { /* cycle over arenas; performance not crucial */
+    unsigned char was_delta;
+    if (asize > remains) { asize = remains; end = p + asize; }
+    /* Fake the upper limit appropriate for the given arena */
+    while (prime_above*prime_above <= curlow + (asize << 1) && *p_prime_above)
+      prime_above += *p_prime_above++;
+    was_delta = *p_prime_above;
+    *p_prime_above = 0; /* sentinel for sieve_chunk */
+    sieve_chunk(p1, curlow, p, asize);
+    *p_prime_above = was_delta; /* restore */
+
+    p[asize] = 0; /* sentinel */
+    for (q = p; ; plast = q++)
+    { /* q runs over addresses corresponding to primes */
+      while (*q) q++; /* use sentinel at end */
+      if (q >= end) break;
+      *curdiff++ = (unsigned char)(q-plast) << 1; /* < 255 for q < 436273291 */
+    }
+    plast -= asize;
+    remains -= asize;
+    curlow += (asize<<1);
+  }
+  last = curlow - ((p - plast) << 1);
+  *curdiff++ = 0; /* sentinel */
+  *lenp = curdiff - p1;
+  *lastp = last;
+  if (alloced) pari_free(p); else avma = av;
+}
+
+ulong
+maxprime(void) { return diffptr ? _maxprime : 0; }
+
+void
+maxprime_check(ulong c) { if (_maxprime < c) pari_err_MAXPRIME(c); }
+
+/* We ensure 65302 <= maxnum <= 436273289: the LHS ensures modular function
+ * have enough fast primes to work, the RHS ensures that p_{n+1} - p_n < 255
+ * (N.B. RHS would be incorrect since initprimes0 would make it odd, thereby
+ * increasing it by 1) */
+byteptr
+initprimes(ulong maxnum, long *lenp, ulong *lastp)
+{
+  byteptr t;
+  if (maxnum < 65537)
+    maxnum = 65537;
+  else if (maxnum > 436273289)
+    maxnum = 436273289;
+  t = (byteptr)pari_malloc((size_t) (1.09 * maxnum/log((double)maxnum)) + 146);
+  initprimes0(maxnum, lenp, lastp, t);
+  return (byteptr)pari_realloc(t, *lenp);
+}
+
+void
+initprimetable(ulong maxnum)
+{
+  long len;
+  ulong last;
+  byteptr p = initprimes(maxnum, &len, &last), old = diffptr;
+  diffptrlen = minss(diffptrlen, len);
+  _maxprime  = minss(_maxprime,last); /*Protect against ^C*/
+  diffptr = p; diffptrlen = len; _maxprime = last;
+  if (old) free(old);
+}
+
+/* all init_primepointer_xx routines set *ptr to the corresponding place
+ * in prime table */
+/* smallest p >= a */
+ulong
+init_primepointer_geq(ulong a, byteptr *pd)
+{
+  ulong n, p;
+  prime_table_next_p(a, pd, &p, &n);
+  return p;
+}
+/* largest p < a */
+ulong
+init_primepointer_lt(ulong a, byteptr *pd)
+{
+  ulong n, p;
+  prime_table_next_p(a, pd, &p, &n);
+  PREC_PRIME_VIADIFF(p, *pd);
+  return p;
+}
+/* largest p <= a */
+ulong
+init_primepointer_leq(ulong a, byteptr *pd)
+{
+  ulong n, p;
+  prime_table_next_p(a, pd, &p, &n);
+  if (p != a) PREC_PRIME_VIADIFF(p, *pd);
+  return p;
+}
+/* smallest p > a */
+ulong
+init_primepointer_gt(ulong a, byteptr *pd)
+{
+  ulong n, p;
+  prime_table_next_p(a, pd, &p, &n);
+  if (p == a) NEXT_PRIME_VIADIFF(p, *pd);
+  return p;
+}
+
+GEN
+boundfact(GEN n, ulong lim)
+{
+  switch(typ(n))
+  {
+    case t_INT: return Z_factor_limit(n,lim);
+    case t_FRAC: {
+      pari_sp av = avma;
+      GEN a = Z_factor_limit(gel(n,1),lim);
+      GEN b = Z_factor_limit(gel(n,2),lim);
+      gel(b,2) = ZC_neg(gel(b,2));
+      return gerepilecopy(av, merge_factor_i(a,b));
+    }
+  }
+  pari_err_TYPE("boundfact",n);
+  return NULL; /* not reached */
+}
+
+/* NOT memory clean */
+GEN
+Z_smoothen(GEN N, GEN L, GEN *pP, GEN *pe)
+{
+  long i, j, l = lg(L);
+  GEN e = new_chunk(l), P = new_chunk(l);
+  for (i = j = 1; i < l; i++)
+  {
+    ulong p = (ulong)L[i];
+    long v = Z_lvalrem(N, p, &N);
+    if (v) { P[j] = p; e[j] = v; j++; if (is_pm1(N)) { N = NULL; break; } }
+  }
+  P[0] = evaltyp(t_VECSMALL) | evallg(j); *pP = P;
+  e[0] = evaltyp(t_VECSMALL) | evallg(j); *pe = e; return N;
+}
+/***********************************************************************/
+/**                                                                   **/
+/**                    SIMPLE FACTORISATIONS                          **/
+/**                                                                   **/
+/***********************************************************************/
+/* Factor n and output [p,e,c] where
+ * p, e and c are vecsmall with n = prod{p[i]^e[i]} and c[i] = p[i]^e[i] */
+GEN
+factoru_pow(ulong n)
+{
+  GEN f = cgetg(4,t_VEC);
+  pari_sp av = avma;
+  GEN F, P, E, p, e, c;
+  long i, l;
+  /* enough room to store <= 15 * [p,e,p^e] (OK if n < 2^64) */
+  (void)new_chunk((15 + 1)*3);
+  F = factoru(n);
+  P = gel(F,1);
+  E = gel(F,2); l = lg(P);
+  avma = av;
+  gel(f,1) = p = cgetg(l,t_VECSMALL);
+  gel(f,2) = e = cgetg(l,t_VECSMALL);
+  gel(f,3) = c = cgetg(l,t_VECSMALL);
+  for(i = 1; i < l; i++)
+  {
+    p[i] = P[i];
+    e[i] = E[i];
+    c[i] = upowuu(p[i], e[i]);
+  }
+  return f;
+}
+
+static GEN
+factorlim(GEN n, ulong lim)
+{ return lim? Z_factor_limit(n, lim): Z_factor(n); }
+/* factor p^n - 1, assuming p prime. If lim != 0, limit factorization to
+ * primes <= lim */
+GEN
+factor_pn_1_limit(GEN p, long n, ulong lim)
+{
+  pari_sp av = avma;
+  GEN A = factorlim(subiu(p,1), lim), d = divisorsu(n);
+  long i, pp = itos_or_0(p);
+  for(i=2; i<lg(d); i++)
+  {
+    GEN B;
+    if (pp && d[i]%pp==0 && (
+       ((pp&3)==1 && (d[i]&1)) ||
+       ((pp&3)==3 && (d[i]&3)==2) ||
+       (pp==2 && (d[i]&7)==4)))
+    {
+      GEN f=factor_Aurifeuille_prime(p,d[i]);
+      B = factorlim(f, lim);
+      A = merge_factor(A, B, (void*)&cmpii, cmp_nodata);
+      B = factorlim(diviiexact(polcyclo_eval(d[i],p), f), lim);
+    }
+    else
+      B = factorlim(polcyclo_eval(d[i],p), lim);
+    A = merge_factor(A, B, (void*)&cmpii, cmp_nodata);
+  }
+  return gerepilecopy(av, A);
+}
+GEN
+factor_pn_1(GEN p, ulong n)
+{ return factor_pn_1_limit(p, n, 0); }
+
+#if 0
+static GEN
+to_mat(GEN p, long e) {
+  GEN B = cgetg(3, t_MAT);
+  gel(B,1) = mkcol(p);
+  gel(B,2) = mkcol(utoipos(e)); return B;
+}
+/* factor phi(n) */
+GEN
+factor_eulerphi(GEN n)
+{
+  pari_sp av = avma;
+  GEN B = NULL, A, P, E, AP, AE;
+  long i, l, v = vali(n);
+
+  l = lg(n);
+  /* result requires less than this: at most expi(n) primes */
+  (void)new_chunk(bit_accuracy(l) * (l /*p*/ + 3 /*e*/ + 2 /*vectors*/) + 3+2);
+  if (v) { n = shifti(n, -v); v--; }
+  A = Z_factor(n); P = gel(A,1); E = gel(A,2); l = lg(P);
+  for(i = 1; i < l; i++)
+  {
+    GEN p = gel(P,i), q = subis(p,1), fa;
+    long e = itos(gel(E,i)), w;
+
+    w = vali(q); v += w; q = shifti(q,-w);
+    if (! is_pm1(q))
+    {
+      fa = Z_factor(q);
+      B = B? merge_factor(B, fa, (void*)&cmpii, cmp_nodata): fa;
+    }
+    if (e > 1) {
+      if (B) {
+        gel(B,1) = shallowconcat(gel(B,1), p);
+        gel(B,2) = shallowconcat(gel(B,2), utoipos(e-1));
+      } else
+        B = to_mat(p, e-1);
+    }
+  }
+  avma = av;
+  if (!B) return v? to_mat(gen_2, v): trivial_fact();
+  A = cgetg(3, t_MAT);
+  P = gel(B,1); E = gel(B,2); l = lg(P);
+  AP = cgetg(l+1, t_COL); gel(A,1) = AP; AP++;
+  AE = cgetg(l+1, t_COL); gel(A,2) = AE; AE++;
+  /* prepend "2^v" */
+  gel(AP,0) = gen_2;
+  gel(AE,0) = utoipos(v);
+  for (i = 1; i < l; i++)
+  {
+    gel(AP,i) = icopy(gel(P,i));
+    gel(AE,i) = icopy(gel(E,i));
+  }
+  return A;
+}
+#endif
+
+/***********************************************************************/
+/**                                                                   **/
+/**         CHECK FACTORIZATION FOR ARITHMETIC FUNCTIONS              **/
+/**                                                                   **/
+/***********************************************************************/
+static int
+RgV_is_ZVpos(GEN v)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    GEN c = gel(v,i);
+    if (typ(c) != t_INT || signe(c) <= 0) return 0;
+  }
+  return 1;
+}
+/* check whether v is a ZV with non-0 entries */
+static int
+RgV_is_ZVnon0(GEN v)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    GEN c = gel(v,i);
+    if (typ(c) != t_INT || !signe(c)) return 0;
+  }
+  return 1;
+}
+/* check whether v is a ZV with non-zero entries OR exactly [0] */
+static int
+RgV_is_ZV0(GEN v)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    GEN c = gel(v,i);
+    long s;
+    if (typ(c) != t_INT) return 0;
+    s = signe(c);
+    if (!s) return (l == 2);
+  }
+  return 1;
+}
+
+static int
+is_Z_factor_i(GEN f)
+{ return typ(f) == t_MAT && lg(f) == 3 && RgV_is_ZVpos(gel(f,2)); }
+int
+is_Z_factorpos(GEN f)
+{ return is_Z_factor_i(f) && RgV_is_ZVpos(gel(f,1)); }
+int
+is_Z_factor(GEN f)
+{ return is_Z_factor_i(f) && RgV_is_ZV0(gel(f,1)); }
+/* as is_Z_factorpos, also allow factor(0) */
+int
+is_Z_factornon0(GEN f)
+{ return is_Z_factor_i(f) && RgV_is_ZVnon0(gel(f,1)); }
+GEN
+clean_Z_factor(GEN f)
+{
+  GEN P = gel(f,1);
+  long n = lg(P)-1;
+  if (n && equalim1(gel(P,1)))
+    return mkmat2(vecslice(P,2,n), vecslice(gel(f,2),2,n));
+  return f;
+}
+
+/* n associated to a factorization of a positive integer: either N (t_INT)
+ * a factorization matrix faN, or a t_VEC: [N, faN] */
+GEN
+check_arith_pos(GEN n, const char *f) {
+  switch(typ(n))
+  {
+    case t_INT:
+      if (signe(n) <= 0 ) pari_err_DOMAIN(f, "argument", "<=", gen_0, gen_0);
+      return NULL;
+    case t_VEC:
+      if (lg(n) != 3 || typ(gel(n,1)) != t_INT) break;
+      n = gel(n,2); /* fall through */
+    case t_MAT:
+      if (!is_Z_factorpos(n)) break;
+      return n;
+  }
+  pari_err_TYPE(f,n);
+  return NULL;
+}
+/* n associated to a factorization of a non-0 integer */
+GEN
+check_arith_non0(GEN n, const char *f) {
+  switch(typ(n))
+  {
+    case t_INT:
+      if (!signe(n)) pari_err_DOMAIN(f, "argument", "=", gen_0, gen_0);
+      return NULL;
+    case t_VEC:
+      if (lg(n) != 3 || typ(gel(n,1)) != t_INT) break;
+      n = gel(n,2); /* fall through */
+    case t_MAT:
+      if (!is_Z_factornon0(n)) break;
+      return n;
+  }
+  pari_err_TYPE(f,n);
+  return NULL;
+}
+/* n associated to a factorization of an integer */
+GEN
+check_arith_all(GEN n, const char *f) {
+  switch(typ(n))
+  {
+    case t_INT:
+      return NULL;
+    case t_VEC:
+      if (lg(n) != 3 || typ(gel(n,1)) != t_INT) break;
+      n = gel(n,2); /* fall through */
+    case t_MAT:
+      if (!is_Z_factor(n)) break;
+      return n;
+  }
+  pari_err_TYPE(f,n);
+  return NULL;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                MISCELLANEOUS ARITHMETIC FUNCTIONS                 **/
+/**                (ultimately depend on Z_factor())                  **/
+/**                                                                   **/
+/***********************************************************************/
+/* set P,E from F. Check whether F is an integer and kill "factor" -1 */
+static void
+set_fact_check(GEN F, GEN *pP, GEN *pE, int *isint)
+{
+  GEN E, P;
+  if (lg(F) != 3) pari_err_TYPE("divisors",F);
+  P = gel(F,1);
+  E = gel(F,2);
+  RgV_check_ZV(E, "divisors");
+  *isint = RgV_is_ZV(P);
+  if (*isint)
+  {
+    long i, l = lg(P);
+    /* skip -1 */
+    if (l>1 && signe(gel(P,1)) < 0) { E++; P = vecslice(P,2,--l); }
+    /* test for 0 */
+    for (i = 1; i < l; i++)
+      if (!signe(gel(P,i)) && signe(gel(E,i)))
+        pari_err_DOMAIN("divisors", "argument", "=", gen_0, F);
+  }
+  *pP = P;
+  *pE = E;
+}
+static void
+set_fact(GEN F, GEN *pP, GEN *pE) { *pP = gel(F,1); *pE = gel(F,2); }
+
+int
+divisors_init(GEN n, GEN *pP, GEN *pE)
+{
+  long i,l;
+  GEN E, P, e;
+  int isint;
+
+  switch(typ(n))
+  {
+    case t_INT:
+      if (!signe(n)) pari_err_DOMAIN("divisors", "argument", "=", gen_0, gen_0);
+      set_fact(absi_factor(n), &P,&E);
+      isint = 1; break;
+    case t_VEC:
+      if (lg(n) != 3) pari_err_TYPE("divisors",n);
+      set_fact_check(gel(n,2), &P,&E, &isint);
+      break;
+    case t_MAT:
+      set_fact_check(n, &P,&E, &isint);
+      break;
+    default:
+      set_fact(factor(n), &P,&E);
+      isint = 0; break;
+  }
+  l = lg(P);
+  e = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++)
+  {
+    e[i] = itos(gel(E,i));
+    if (e[i] < 0) pari_err_TYPE("divisors [denominator]",n);
+  }
+  *pP = P; *pE = e; return isint;
+}
+
+GEN
+divisors(GEN n)
+{
+  pari_sp av = avma;
+  long i, j, l;
+  ulong ndiv;
+  GEN *d, *t, *t1, *t2, *t3, P, E, e;
+  int isint = divisors_init(n, &P, &E);
+
+  l = lg(E); e = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) e[i] = E[i]+1;
+  ndiv = itou_or_0( zv_prod_Z(e) );
+  if (!ndiv || ndiv & ~LGBITS) pari_err_OVERFLOW("divisors");
+  d = t = (GEN*) cgetg(ndiv+1,t_VEC);
+  *++d = gen_1;
+  if (isint)
+  {
+    for (i=1; i<l; i++)
+      for (t1=t,j=E[i]; j; j--,t1=t2)
+        for (t2=d, t3=t1; t3<t2; ) *++d = mulii(*++t3, gel(P,i));
+    e = ZV_sort((GEN)t);
+  } else {
+    for (i=1; i<l; i++)
+      for (t1=t,j=E[i]; j; j--,t1=t2)
+        for (t2=d, t3=t1; t3<t2; ) *++d = gmul(*++t3, gel(P,i));
+    e = (GEN)t;
+  }
+  return gerepileupto(av, e);
+}
+
+GEN
+divisorsu(ulong n)
+{
+  pari_sp av = avma;
+  long i, j, l;
+  ulong nbdiv;
+  GEN d, t, t1, t2, t3, P, e, fa = factoru(n);
+
+  P = gel(fa,1); l = lg(P);
+  e = gel(fa,2);
+  nbdiv = 1;
+  for (i=1; i<l; i++) nbdiv *= 1+e[i];
+  d = t = cgetg(nbdiv+1,t_VECSMALL);
+  *++d = 1;
+  for (i=1; i<l; i++)
+    for (t1=t,j=e[i]; j; j--,t1=t2)
+      for (t2=d, t3=t1; t3<t2; ) *(++d) = *(++t3) * P[i];
+  vecsmall_sort(t);
+  return gerepileupto(av, t);
+}
+
+static GEN
+corefa(GEN fa)
+{
+  GEN P = gel(fa,1), E = gel(fa,2), c = gen_1;
+  long i;
+  for (i=1; i<lg(P); i++)
+    if (mod2(gel(E,i))) c = mulii(c,gel(P,i));
+  return c;
+}
+static GEN
+core2fa(GEN fa)
+{
+  GEN P = gel(fa,1), E = gel(fa,2), c = gen_1, f = gen_1;
+  long i;
+  for (i=1; i<lg(P); i++)
+  {
+    long e = itos(gel(E,i));
+    if (e & 1)  c = mulii(c, gel(P,i));
+    if (e != 1) f = mulii(f, powiu(gel(P,i), e >> 1));
+  }
+  return mkvec2(c,f);
+}
+GEN
+corepartial(GEN n, long all)
+{
+  pari_sp av = avma;
+  if (typ(n) != t_INT) pari_err_TYPE("corepartial",n);
+  return gerepileuptoint(av, corefa(Z_factor_limit(n,all)));
+}
+GEN
+core2partial(GEN n, long all)
+{
+  pari_sp av = avma;
+  if (typ(n) != t_INT) pari_err_TYPE("core2partial",n);
+  return gerepilecopy(av, core2fa(Z_factor_limit(n,all)));
+}
+static GEN
+core2_i(GEN n)
+{
+  GEN f = core(n);
+  if (!signe(f)) return mkvec2(gen_0, gen_1);
+  switch(typ(n))
+  {
+    case t_VEC: n = gel(n,1); break;
+    case t_MAT: n = factorback(n); break;
+  }
+  return mkvec2(f, sqrtint(diviiexact(n, f)));
+}
+GEN
+core2(GEN n) { pari_sp av = avma; return gerepilecopy(av, core2_i(n)); }
+
+GEN
+core0(GEN n,long flag) { return flag? core2(n): core(n); }
+
+static long
+_mod4(GEN c) {
+  long r, s = signe(c);
+  if (!s) return 0;
+  r = mod4(c); if (s < 0) r = 4-r;
+  return r;
+}
+
+GEN
+coredisc(GEN n)
+{
+  pari_sp av = avma;
+  GEN c = core(n);
+  if (_mod4(c)<=1) return c; /* c = 0 or 1 mod 4 */
+  return gerepileuptoint(av, shifti(c,2));
+}
+
+GEN
+coredisc2(GEN n)
+{
+  pari_sp av = avma;
+  GEN y = core2_i(n);
+  GEN c = gel(y,1), f = gel(y,2);
+  if (_mod4(c)<=1) return gerepilecopy(av, y);
+  y = cgetg(3,t_VEC);
+  gel(y,1) = shifti(c,2);
+  gel(y,2) = gmul2n(f,-1); return gerepileupto(av, y);
+}
+
+GEN
+coredisc0(GEN n,long flag) { return flag? coredisc2(n): coredisc(n); }
+
+long
+omega(GEN n)
+{
+  pari_sp av = avma;
+  GEN F, P;
+  if ((F = check_arith_non0(n,"omega"))) {
+    long n;
+    P = gel(F,1); n = lg(P)-1;
+    if (n && equalim1(gel(P,1))) n--;
+    return n;
+  }
+  else if (lgefint(n) == 3)
+  {
+    if (n[2] == 1) return 0;
+    F = factoru(n[2]);
+  }
+  else
+    F = absi_factor(n);
+  P = gel(F,1); avma = av; return lg(P)-1;
+}
+
+long
+bigomega(GEN n)
+{
+  pari_sp av = avma;
+  GEN F, E;
+  if ((F = check_arith_non0(n,"bigomega")))
+  {
+    GEN P = gel(F,1);
+    long n = lg(P)-1;
+    E = gel(F,2);
+    if (n && equalim1(gel(P,1))) E = vecslice(E,2,n);
+    E = ZV_to_zv(E);
+  }
+  else if (lgefint(n) == 3)
+  {
+    if (n[2] == 1) return 0;
+    F = factoru(n[2]);
+    E = gel(F,2);
+  }
+  else
+    E = ZV_to_zv(gel(absi_factor(n), 2));
+  avma = av; return zv_sum(E);
+}
+
+/* assume f = factoru(n), possibly with 0 exponents. Return phi(n) */
+ulong
+eulerphiu_fact(GEN f)
+{
+  GEN P = gel(f,1), E = gel(f,2);
+  long i, m = 1, l = lg(P);
+  for (i = 1; i < l; i++)
+  {
+    ulong p = P[i], e = E[i];
+    if (!e) continue;
+    if (p == 2)
+    { if (e > 1) m <<= e-1; }
+    else
+    {
+      m *= (p-1);
+      if (e > 1) m *= upowuu(p, e-1);
+    }
+  }
+  return m;
+}
+ulong
+eulerphiu(ulong n)
+{
+  pari_sp av = avma;
+  GEN F;
+  if (!n) return 2;
+  F = factoru(n);
+  avma = av; return eulerphiu_fact(F);
+}
+GEN
+eulerphi(GEN n)
+{
+  pari_sp av = avma;
+  GEN Q, F, P, E;
+  long i, l;
+
+  if ((F = check_arith_all(n,"eulerphi")))
+  {
+    F = clean_Z_factor(F);
+    n = (typ(n) == t_VEC)? gel(n,1): factorback(F);
+    if (lgefint(n) == 3)
+    {
+      ulong e;
+      F = mkmat2(ZV_to_nv(gel(F,1)), ZV_to_nv(gel(F,2)));
+      e = eulerphiu_fact(F);
+      avma = av; return utoipos(e);
+    }
+  }
+  else if (lgefint(n) == 3) return utoipos(eulerphiu((ulong)n[2]));
+  else
+    F = absi_factor(n);
+  if (!signe(n)) return gen_2;
+  P = gel(F,1);
+  E = gel(F,2); l = lg(P);
+  Q = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(P,i), q;
+    ulong v = itou(gel(E,i));
+    q = subiu(p,1);
+    if (v != 1) q = mulii(q, v == 2? p: powiu(p, v-1));
+    gel(Q,i) = q;
+  }
+  return gerepileuptoint(av, ZV_prod(Q));
+}
+
+static GEN
+numdiv_aux(GEN F)
+{
+  GEN x, E = gel(F,2);
+  long i, l = lg(E);
+  x = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) x[i] = itou(gel(E,i))+1;
+  return x;
+}
+GEN
+numdiv(GEN n)
+{
+  pari_sp av = avma;
+  GEN F, E;
+  long i, l;
+  if ((F = check_arith_non0(n,"numdiv")))
+  {
+    F = clean_Z_factor(F);
+    E = numdiv_aux(F);
+  }
+  else if (lgefint(n) == 3)
+  {
+    if (n[2] == 1) return gen_1;
+    F = factoru(n[2]);
+    E = gel(F,2); l = lg(E);
+    for (i=1; i<l; i++) E[i]++;
+  }
+  else
+    E = numdiv_aux(absi_factor(n));
+  return gerepileuptoint(av, zv_prod_Z(E));
+}
+
+/* 1 + p + ... + p^v, p != 2^BIL - 1 */
+static GEN
+u_euler_sumdiv(ulong p, long v)
+{
+  GEN u = utoipos(1 + p); /* can't overflow */
+  for (; v > 1; v--) u = addsi(1, mului(p, u));
+  return u;
+}
+/* 1 + q + ... + q^v */
+static GEN
+euler_sumdiv(GEN q, long v)
+{
+  GEN u = addui(1, q);
+  for (; v > 1; v--) u = addui(1, mulii(q, u));
+  return u;
+}
+static GEN
+u_euler_sumdivk(ulong p, long v, long k) { return euler_sumdiv(powuu(p,k), v); }
+static GEN
+euler_sumdivk(GEN p, long v, long k) { return euler_sumdiv(powiu(p,k), v); }
+
+static GEN
+sumdiv_aux(GEN F)
+{
+  GEN x, P = gel(F,1), E = gel(F,2);
+  long i, l = lg(P);
+  x = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = euler_sumdiv(gel(P,i), itou(gel(E,i)));
+  return x;
+}
+GEN
+sumdiv(GEN n)
+{
+  pari_sp av = avma;
+  GEN F, P, E;
+  long i, l;
+
+  if ((F = check_arith_non0(n,"sumdiv")))
+  {
+    F = clean_Z_factor(F);
+    P = sumdiv_aux(F);
+  }
+  else if (lgefint(n) == 3)
+  {
+    if (n[2] == 1) return gen_1;
+    F = factoru(n[2]);
+    P = gel(F,1);
+    E = gel(F,2); l = lg(P);
+    for (i=1; i<l; i++) gel(P,i) = u_euler_sumdiv(P[i], E[i]);
+  }
+  else
+    P = sumdiv_aux(absi_factor(n));
+  return gerepileuptoint(av, ZV_prod(P));
+}
+
+static GEN
+sumdivk_aux(GEN F, long k)
+{
+  GEN x, P = gel(F,1), E = gel(F,2);
+  long i, l = lg(P);
+  x = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = euler_sumdivk(gel(P,i), gel(E,i)[2], k);
+  return x;
+}
+GEN
+sumdivk(GEN n, long k)
+{
+  pari_sp av = avma;
+  GEN E, F, P;
+  long i, l, k1;
+
+  if (!k) return numdiv(n);
+  if (k == 1) return sumdiv(n);
+  if (k ==-1) return gerepileupto(av, gdiv(sumdiv(n), n));
+  k1 = k;
+  if (k < 0)  k = -k;
+  if ((F = check_arith_non0(n,"sumdivk")))
+  {
+    F = clean_Z_factor(F);
+    P = sumdivk_aux(F,k);
+  }
+  else if (lgefint(n) == 3)
+  {
+    if (n[2] == 1) return gen_1;
+    F = factoru(n[2]);
+    P = gel(F,1);
+    E = gel(F,2); l = lg(P);
+    for (i=1; i<l; i++) gel(P,i) = u_euler_sumdivk(P[i], E[i], k);
+  }
+  else
+    P = sumdivk_aux(absi_factor(n), k);
+  P = ZV_prod(P);
+  if (k1 > 0) return gerepileuptoint(av, P);
+  return gerepileupto(av, gdiv(P, powiu(n,k)));
+}
+
+/* K t_VECSMALL of k >= 0 */
+GEN
+usumdivkvec(ulong n, GEN K)
+{
+  pari_sp av = avma;
+  GEN F = factoru(n), P = gel(F,1), E = gel(F,2), Z, S;
+  long i,j, l = lg(P), lK = lg(K);
+  Z = cgetg(l, t_VEC);
+  S = cgetg(lK, t_VEC);
+  for (j=1; j<lK; j++)
+  {
+    long k = K[j];
+    for (i=1; i<l; i++) gel(Z,i) = u_euler_sumdivk(P[i], E[i], k);
+    gel(S,j) = ZV_prod(Z);
+  }
+  return gerepilecopy(av, S);
+}
+
+long
+uissquarefree_fact(GEN f)
+{
+  GEN E = gel(f,2);
+  long i, l = lg(E);
+  for (i = 1; i < l; i++)
+    if (E[i] > 1) return 0;
+  return 1;
+}
+long
+uissquarefree(ulong n)
+{
+  if (!n) return 0;
+  return moebiusu(n)? 1: 0;
+}
+long
+Z_issquarefree(GEN n)
+{
+  switch(lgefint(n))
+  {
+    case 2: return 0;
+    case 3: return uissquarefree(n[2]);
+  }
+  return moebius(n)? 1: 0;
+}
+long
+issquarefree(GEN x)
+{
+  pari_sp av;
+  GEN d;
+  switch(typ(x))
+  {
+    case t_INT: return Z_issquarefree(x);
+    case t_POL:
+      if (!signe(x)) return 0;
+      av = avma; d = RgX_gcd(x, RgX_deriv(x));
+      avma = av; return (lg(d) == 3);
+    default: pari_err_TYPE("issquarefree",x);
+      return 0; /* not reached */
+  }
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                    DIGITS / SUM OF DIGITS                       **/
+/**                                                                 **/
+/*********************************************************************/
+
+static ulong DS[] ={
+  0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8,9,10,11,3,4,5,6,7,8,
+  9,10,11,12,4,5,6,7,8,9,10,11,12,13,5,6,7,8,9,10,11,12,13,14,6,7,8,9,10,11,
+  12,13,14,15,7,8,9,10,11,12,13,14,15,16,8,9,10,11,12,13,14,15,16,17,9,10,11,
+  12,13,14,15,16,17,18,1,2,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8,9,10,11,3,4,5,6,7,8,
+  9,10,11,12,4,5,6,7,8,9,10,11,12,13,5,6,7,8,9,10,11,12,13,14,6,7,8,9,10,11,
+  12,13,14,15,7,8,9,10,11,12,13,14,15,16,8,9,10,11,12,13,14,15,16,17,9,10,11,
+  12,13,14,15,16,17,18,10,11,12,13,14,15,16,17,18,19,2,3,4,5,6,7,8,9,10,11,3,
+  4,5,6,7,8,9,10,11,12,4,5,6,7,8,9,10,11,12,13,5,6,7,8,9,10,11,12,13,14,6,7,8,
+  9,10,11,12,13,14,15,7,8,9,10,11,12,13,14,15,16,8,9,10,11,12,13,14,15,16,17,
+  9,10,11,12,13,14,15,16,17,18,10,11,12,13,14,15,16,17,18,19,11,12,13,14,15,
+  16,17,18,19,20,3,4,5,6,7,8,9,10,11,12,4,5,6,7,8,9,10,11,12,13,5,6,7,8,9,10,
+  11,12,13,14,6,7,8,9,10,11,12,13,14,15,7,8,9,10,11,12,13,14,15,16,8,9,10,11,
+  12,13,14,15,16,17,9,10,11,12,13,14,15,16,17,18,10,11,12,13,14,15,16,17,18,
+  19,11,12,13,14,15,16,17,18,19,20,12,13,14,15,16,17,18,19,20,21,4,5,6,7,8,9,
+  10,11,12,13,5,6,7,8,9,10,11,12,13,14,6,7,8,9,10,11,12,13,14,15,7,8,9,10,11,
+  12,13,14,15,16,8,9,10,11,12,13,14,15,16,17,9,10,11,12,13,14,15,16,17,18,10,
+  11,12,13,14,15,16,17,18,19,11,12,13,14,15,16,17,18,19,20,12,13,14,15,16,17,
+  18,19,20,21,13,14,15,16,17,18,19,20,21,22,5,6,7,8,9,10,11,12,13,14,6,7,8,9,
+  10,11,12,13,14,15,7,8,9,10,11,12,13,14,15,16,8,9,10,11,12,13,14,15,16,17,9,
+  10,11,12,13,14,15,16,17,18,10,11,12,13,14,15,16,17,18,19,11,12,13,14,15,16,
+  17,18,19,20,12,13,14,15,16,17,18,19,20,21,13,14,15,16,17,18,19,20,21,22,14,
+  15,16,17,18,19,20,21,22,23,6,7,8,9,10,11,12,13,14,15,7,8,9,10,11,12,13,14,
+  15,16,8,9,10,11,12,13,14,15,16,17,9,10,11,12,13,14,15,16,17,18,10,11,12,13,
+  14,15,16,17,18,19,11,12,13,14,15,16,17,18,19,20,12,13,14,15,16,17,18,19,20,
+  21,13,14,15,16,17,18,19,20,21,22,14,15,16,17,18,19,20,21,22,23,15,16,17,18,
+  19,20,21,22,23,24,7,8,9,10,11,12,13,14,15,16,8,9,10,11,12,13,14,15,16,17,9,
+  10,11,12,13,14,15,16,17,18,10,11,12,13,14,15,16,17,18,19,11,12,13,14,15,16,
+  17,18,19,20,12,13,14,15,16,17,18,19,20,21,13,14,15,16,17,18,19,20,21,22,14,
+  15,16,17,18,19,20,21,22,23,15,16,17,18,19,20,21,22,23,24,16,17,18,19,20,21,
+  22,23,24,25,8,9,10,11,12,13,14,15,16,17,9,10,11,12,13,14,15,16,17,18,10,11,
+  12,13,14,15,16,17,18,19,11,12,13,14,15,16,17,18,19,20,12,13,14,15,16,17,18,
+  19,20,21,13,14,15,16,17,18,19,20,21,22,14,15,16,17,18,19,20,21,22,23,15,16,
+  17,18,19,20,21,22,23,24,16,17,18,19,20,21,22,23,24,25,17,18,19,20,21,22,23,
+  24,25,26,9,10,11,12,13,14,15,16,17,18,10,11,12,13,14,15,16,17,18,19,11,12,
+  13,14,15,16,17,18,19,20,12,13,14,15,16,17,18,19,20,21,13,14,15,16,17,18,19,
+  20,21,22,14,15,16,17,18,19,20,21,22,23,15,16,17,18,19,20,21,22,23,24,16,17,
+  18,19,20,21,22,23,24,25,17,18,19,20,21,22,23,24,25,26,18,19,20,21,22,23,24,
+  25,26,27
+};
+
+ulong
+sumdigitsu(ulong n)
+{
+  ulong s = 0;
+  while (n) { s += DS[n % 1000]; n /= 1000; }
+  return s;
+}
+
+/* res=array of 9-digits integers, return \sum_{0 <= i < l} sumdigits(res[i]) */
+static ulong
+sumdigits_block(ulong *res, long l)
+{
+  long s = sumdigitsu(*--res);
+  while (--l > 0) s += sumdigitsu(*--res);
+  return s;
+}
+
+GEN
+sumdigits(GEN n)
+{
+  pari_sp av = avma;
+  ulong s, *res;
+  long l;
+
+  if (typ(n) != t_INT) pari_err_TYPE("sumdigits", n);
+  l = lgefint(n);
+  switch(l)
+  {
+    case 2: return gen_0;
+    case 3: return utoipos(sumdigitsu(n[2]));
+  }
+  res = convi(n, &l);
+  if ((ulong)l < ULONG_MAX / 81)
+  {
+    s = sumdigits_block(res, l);
+    avma = av; return utoipos(s);
+  }
+  else /* Huge. Overflows ulong */
+  {
+    const long L = (long)(ULONG_MAX / 81);
+    GEN S = gen_0;
+    while (l > L)
+    {
+      S = addiu(S, sumdigits_block(res, L));
+      res += L; l -= L;
+    }
+    if (l)
+      S = addiu(S, sumdigits_block(res, l));
+    return gerepileuptoint(av, S);
+  }
+}
+
+static void
+digits_dac(GEN x, GEN B, long l, GEN* z)
+{
+  GEN q,r;
+  long m;
+  if (l==1) { *z=x; return; }
+  m=l>>1;
+  q=dvmdii(x,powiu(B,m),&r);
+  digits_dac(q,B,l-m,z);
+  digits_dac(r,B,m,z+l-m);
+}
+
+static void
+digits_dacsmall(GEN x, ulong B, long l, ulong* z)
+{
+  pari_sp av = avma;
+  GEN q,r;
+  long m;
+  if (l==1) { *z=itou(x); return; }
+  m=l>>1;
+  q=dvmdii(x,powuu(B,m),&r);
+  digits_dacsmall(q,B,l-m,z);
+  digits_dacsmall(r,B,m,z+l-m);
+  avma = av;
+}
+
+GEN
+digits(GEN x, GEN B)
+{
+  pari_sp av=avma;
+  long lz;
+  GEN z;
+  if (typ(x)!=t_INT) pari_err_TYPE("digits",x);
+  if (!B)
+    B = utoi(10);
+  else {
+    if (typ(B)!=t_INT) pari_err_TYPE("digits",B);
+    if (cmpis(B,2)<0) pari_err_DOMAIN("digits","B","<",gen_2,B);
+  }
+  if (equalis(B,2))    {avma = av; return binaire(x); }
+  if (!signe(x))       {avma = av; return cgetg(1,t_VEC); }
+  if (absi_cmp(x,B)<0) {avma = av; retmkvec(absi(x)); }
+  x = absi(x); lz = logint(x,B,NULL);
+  if (lgefint(B)>3)
+  {
+    z = zerovec(lz);
+    digits_dac(x,B,lz,(GEN*)(z+1));
+    return gerepilecopy(av,z);
+  }
+  else
+  {
+    ulong b = B[2];
+    z = zero_zv(lz);
+    digits_dacsmall(x,b,lz,(ulong*)(z+1));
+    return gerepileupto(av,vecsmall_to_vec(z));
+  }
+}
diff --git a/src/basemath/base1.c b/src/basemath/base1.c
new file mode 100644
index 0000000..9507d4d
--- /dev/null
+++ b/src/basemath/base1.c
@@ -0,0 +1,2828 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/**************************************************************/
+/*                                                            */
+/*                        NUMBER FIELDS                       */
+/*                                                            */
+/**************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+int new_galois_format = 0;
+
+void
+checkrnf(GEN rnf)
+{
+  if (typ(rnf)!=t_VEC || lg(rnf)!=13) pari_err_TYPE("checkrnf",rnf);
+}
+
+GEN
+checkbnf_i(GEN X)
+{
+  if (typ(X) == t_VEC)
+    switch (lg(X))
+    {
+      case 11: return X;
+      case 7:  return checkbnf_i(bnr_get_bnf(X));
+    }
+  return NULL;
+}
+
+GEN
+checknf_i(GEN X)
+{
+  if (typ(X)==t_VEC)
+    switch(lg(X))
+    {
+      case 10: return X;
+      case 11: return checknf_i(bnf_get_nf(X));
+      case 7:  return checknf_i(bnr_get_bnf(X));
+      case 3: if (typ(gel(X,2)) == t_POLMOD) return checknf_i(gel(X,1));
+    }
+  return NULL;
+}
+
+GEN
+checkbnf(GEN x)
+{
+  GEN bnf = checkbnf_i(x);
+  if (!bnf) pari_err_TYPE("checkbnf [please apply bnfinit()]",x);
+  return bnf;
+}
+
+GEN
+checknf(GEN x)
+{
+  GEN nf = checknf_i(x);
+  if (!nf) pari_err_TYPE("checknf [please apply nfinit()]",x);
+  return nf;
+}
+
+void
+checkbnr(GEN bnr)
+{
+  if (typ(bnr)!=t_VEC || lg(bnr)!=7)
+    pari_err_TYPE("checkbnr [please apply bnrinit()]",bnr);
+  (void)checkbnf(bnr_get_bnf(bnr));
+}
+
+void
+checkbnrgen(GEN bnr)
+{
+  checkbnr(bnr);
+  if (lg(bnr_get_clgp(bnr))<=3)
+    pari_err_TYPE("checkbnrgen [apply bnrinit(,,1), not bnrinit()]",bnr);
+}
+
+void
+checksqmat(GEN x, long N)
+{
+  if (typ(x)!=t_MAT) pari_err_TYPE("checksqmat",x);
+  if (lg(x) == 1 || lgcols(x) != N+1) pari_err_DIM("checksqmat");
+}
+
+GEN
+checkbid_i(GEN bid)
+{
+  GEN f;
+  if (typ(bid)!=t_VEC || lg(bid)!=6) return NULL;
+  f = bid_get_mod(bid);
+  if (typ(f)!=t_VEC || lg(f)!=3) return NULL;
+  return bid;
+}
+void
+checkbid(GEN bid)
+{
+  if (!checkbid_i(bid)) pari_err_TYPE("checkbid",bid);
+}
+void
+checkabgrp(GEN v)
+{
+  if (typ(v) == t_VEC) switch(lg(v))
+  {
+    case 4: if (typ(gel(v,3)) != t_VEC) break;
+    case 3: if (typ(gel(v,2)) != t_VEC) break;
+            if (typ(gel(v,1)) != t_INT) break;
+            return;/*OK*/
+    default: break;
+  }
+  pari_err_TYPE("checkabgrp",v);
+}
+
+GEN
+checknfelt_mod(GEN nf, GEN x, const char *s)
+{
+  GEN T = gel(x,1), a = gel(x,2), Tnf = nf_get_pol(nf);
+  if (!RgX_equal_var(T, Tnf)) pari_err_MODULUS(s, T, Tnf);
+  return a;
+}
+
+void
+check_ZKmodule(GEN x, const char *s)
+{
+  if (typ(x) != t_VEC || lg(x) < 3) pari_err_TYPE(s,x);
+  if (typ(gel(x,1)) != t_MAT) pari_err_TYPE(s,gel(x,1));
+  if (typ(gel(x,2)) != t_VEC) pari_err_TYPE(s,gel(x,2));
+  if (lg(gel(x,2)) != lgcols(x)) pari_err_DIM(s);
+}
+
+GEN
+get_bnf(GEN x, long *t)
+{
+  switch(typ(x))
+  {
+    case t_POL: *t = typ_POL;  return NULL;
+    case t_QUAD: *t = typ_Q  ; return NULL;
+    case t_VEC:
+      switch(lg(x))
+      {
+        case 5: *t = typ_QUA; return NULL;
+        case 6:
+          if (typ(gel(x,1)) == t_VEC && typ(gel(x,3)) == t_MAT)
+          { *t = typ_BID; return NULL; }
+          if (typ(gel(x,2)) == t_COL && typ(gel(x,3)) == t_INT)
+          { *t = typ_PRID; return NULL; }
+          break;
+        case 7:  *t = typ_BNR;
+          x = bnr_get_bnf(x); if (typ(x)!=t_VEC || lg(x)!=11) break;
+          return x;
+        case 9:
+          x = gel(x,2);
+          if (typ(x) == t_VEC && lg(x) == 4) *t = typ_GAL;
+          return NULL;
+        case 10: *t = typ_NF; return NULL;
+        case 11: *t = typ_BNF; return x;
+        case 13: *t = typ_RNF; return NULL;
+        case 17: *t = typ_ELL; return NULL;
+      }
+      break;
+    case t_COL:
+      if (get_prid(x)) { *t = typ_MODPR; return NULL; }
+      break;
+  }
+  *t = typ_NULL; return NULL;
+}
+
+GEN
+get_nf(GEN x, long *t)
+{
+  switch(typ(x))
+  {
+    case t_POL : *t = typ_POL; return NULL;
+    case t_QUAD: *t = typ_Q  ; return NULL;
+    case t_VEC:
+      switch(lg(x))
+      {
+        case 3:
+          if (typ(gel(x,2)) != t_POLMOD) break;
+          return get_nf(gel(x,1),t);
+        case 5: *t = typ_QUA; return NULL;
+        case 6:
+          if (typ(gel(x,1)) == t_VEC && typ(gel(x,3)) == t_MAT)
+          { *t = typ_BID; return NULL; }
+          if (typ(gel(x,2)) == t_COL && typ(gel(x,3)) == t_INT)
+          { *t = typ_PRID; return NULL; }
+          break;
+        case 7: *t = typ_BNR;
+          x = bnr_get_bnf(x); if (typ(x)!=t_VEC || lg(x)!=11) break;
+          x = bnf_get_nf(x);  if (typ(x)!=t_VEC || lg(x)!=10) break;
+          return x;
+        case 9:
+          x = gel(x,2);
+          if (typ(x) == t_VEC && lg(x) == 4) *t = typ_GAL;
+          return NULL;
+        case 10: *t = typ_NF; return x;
+        case 11: *t = typ_BNF;
+          x = bnf_get_nf(x); if (typ(x)!=t_VEC || lg(x)!=10) break;
+          return x;
+        case 13: *t = typ_RNF; return NULL;
+        case 17: *t = typ_ELL; return NULL;
+      }
+      break;
+    case t_COL:
+      if (get_prid(x)) { *t = typ_MODPR; return NULL; }
+      break;
+  }
+  *t = typ_NULL; return NULL;
+}
+
+long
+nftyp(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_POL : return typ_POL;
+    case t_QUAD: return typ_Q;
+    case t_VEC:
+      switch(lg(x))
+      {
+        case 10:
+          if (typ(gel(x,1))!=t_POL) break;
+          return typ_NF;
+        case 11:
+          x = bnf_get_nf(x); if (typ(x)!=t_VEC || lg(x)!=10) break;
+          return typ_BNF;
+        case 7:
+          x = bnr_get_bnf(x); if (typ(x)!=t_VEC || lg(x)!=11) break;
+          x = bnf_get_nf(x);  if (typ(x)!=t_VEC || lg(x)!=10) break;
+          return typ_BNR;
+        case 6:
+          if (typ(gel(x,1)) != t_VEC || typ(gel(x,3)) != t_MAT) break;
+          return typ_BID;
+        case 9:
+          x = gel(x,2);
+          if (typ(x) == t_VEC && lg(x) == 4) return typ_GAL;
+        case 17: return typ_ELL;
+      }
+  }
+  return typ_NULL;
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**                           GALOIS GROUP                              **/
+/**                                                                     **/
+/*************************************************************************/
+
+/* exchange elements i and j in vector x */
+static GEN
+transroot(GEN x, int i, int j)
+{ x = leafcopy(x); swap(gel(x,i), gel(x,j)); return x; }
+
+GEN
+tschirnhaus(GEN x)
+{
+  pari_sp av = avma, av2;
+  long a, v = varn(x);
+  GEN u, y = cgetg(5,t_POL);
+
+  if (typ(x)!=t_POL) pari_err_TYPE("tschirnhaus",x);
+  if (lg(x) < 4) pari_err_CONSTPOL("tschirnhaus");
+  if (v) { u = leafcopy(x); setvarn(u,0); x=u; }
+  y[1] = evalsigne(1)|evalvarn(0);
+  do
+  {
+    a = random_bits(2); if (a==0) a  = 1; gel(y,4) = stoi(a);
+    a = random_bits(3); if (a>=4) a -= 8; gel(y,3) = stoi(a);
+    a = random_bits(3); if (a>=4) a -= 8; gel(y,2) = stoi(a);
+    u = RgXQ_charpoly(y,x,v); av2 = avma;
+  }
+  while (degpol(RgX_gcd(u,RgX_deriv(u)))); /* while u not separable */
+  if (DEBUGLEVEL>1)
+    err_printf("Tschirnhaus transform. New pol: %Ps",u);
+  avma=av2; return gerepileupto(av,u);
+}
+
+/* Assume pol in Z[X], monic of degree n. Find L in Z such that
+ * POL = L^(-n) pol(L x) is monic in Z[X]. Return POL and set *ptk = L.
+ * No GC. */
+GEN
+ZX_Z_normalize(GEN pol, GEN *ptk)
+{
+  long i,j, sk, n = degpol(pol); /* > 0 */
+  GEN k, fa, P, E, a, POL;
+
+  a = pol + 2; k = gel(a,n-1); /* a[i] = coeff of degree i */
+  for (i = n-2; i >= 0; i--)
+  {
+    k = gcdii(k, gel(a,i));
+    if (is_pm1(k)) { if (ptk) *ptk = gen_1; return pol; }
+  }
+  sk = signe(k);
+  if (!sk) { if (ptk) *ptk = gen_1; return pol; /* monomial! */ }
+  fa = absi_factor_limit(k, 0); k = gen_1;
+  P = gel(fa,1);
+  E = gel(fa,2);
+  POL = leafcopy(pol); a = POL+2;
+  for (i = lg(P)-1; i > 0; i--)
+  {
+    GEN p = gel(P,i), pv, pvj;
+    long vmin = itos(gel(E,i));
+    /* find v_p(k) = min floor( v_p(a[i]) / (n-i)) */
+    for (j=n-1; j>=0; j--)
+    {
+      long v;
+      if (!signe(gel(a,j))) continue;
+      v = Z_pval(gel(a,j), p) / (n - j);
+      if (v < vmin) vmin = v;
+    }
+    if (!vmin) continue;
+    pvj = pv = powiu(p,vmin); k = mulii(k, pv);
+    /* a[j] /= p^(v*(n-j)) */
+    for (j=n-1; j>=0; j--)
+    {
+      if (j < n-1) pvj = mulii(pvj, pv);
+      gel(a,j) = diviiexact(gel(a,j), pvj);
+    }
+  }
+  if (ptk) *ptk = k; return POL;
+}
+
+/* Assume pol != 0 in Z[X]. Find C in Q, L in Z such that POL = C pol(x/L) monic
+ * in Z[X]. Return POL and set *pL = L. Wasteful (but correct) if pol is not
+ * primitive: better if caller used Q_primpart already. No GC. */
+GEN
+ZX_primitive_to_monic(GEN pol, GEN *pL)
+{
+  long i,j, n = degpol(pol);
+  GEN lc = leading_term(pol), L, fa, P, E, a, POL;
+
+  if (is_pm1(lc))
+  {
+    if (pL) *pL = gen_1;
+    return signe(lc) < 0? ZX_neg(pol): pol;
+  }
+  if (signe(lc) < 0)
+    POL = ZX_neg(pol);
+  else
+    POL = leafcopy(pol);
+  a = POL+2; lc = gel(a,n);
+  fa = Z_factor_limit(lc,0); L = gen_1;
+  P = gel(fa,1);
+  E = gel(fa,2);
+  for (i = lg(P)-1; i > 0; i--)
+  {
+    GEN p = gel(P,i), pk, pku;
+    long v, j0, e = itos(gel(E,i)), k = e/n, d = k*n - e;
+
+    if (d < 0) { k++; d += n; }
+    /* k = ceil(e[i] / n); find d, k such that  p^d pol(x / p^k) monic */
+    for (j=n-1; j>0; j--)
+    {
+      if (!signe(gel(a,j))) continue;
+      v = Z_pval(gel(a,j), p);
+      while (v + d < k * j) { k++; d += n; }
+    }
+    pk = powiu(p,k); j0 = d/k;
+    L = mulii(L, pk);
+
+    pku = powiu(p,d - k*j0);
+    /* a[j] *= p^(d - kj) */
+    for (j=j0; j>=0; j--)
+    {
+      if (j < j0) pku = mulii(pku, pk);
+      gel(a,j) = mulii(gel(a,j), pku);
+    }
+    j0++;
+    pku = powiu(p,k*j0 - d);
+    /* a[j] /= p^(kj - d) */
+    for (j=j0; j<=n; j++)
+    {
+      if (j > j0) pku = mulii(pku, pk);
+      gel(a,j) = diviiexact(gel(a,j), pku);
+    }
+  }
+  if (pL) *pL = L;
+  return POL;
+}
+/* Assume pol != 0 in Z[X]. Find C,L in Q such that POL = C pol(x/L)
+ * monic in Z[X]. Return POL and set *pL = L.
+ * Wasteful (but correct) if pol is not primitive: better if caller used
+ * Q_primpart already. No GC. */
+GEN
+ZX_Q_normalize(GEN pol, GEN *pL)
+{
+  GEN lc, POL = ZX_primitive_to_monic(pol, &lc);
+  POL = ZX_Z_normalize(POL, pL);
+  if (pL) *pL = gdiv(lc, *pL);
+  return POL;
+}
+/* pol != 0 in Z[x], returns a monic polynomial POL in Z[x] generating the
+ * same field: there exist C in Q, L in Z such that POL(x) = C pol(x/L).
+ * Set *L = NULL if L = 1, and to L otherwise. No garbage collecting. */
+GEN
+ZX_to_monic(GEN pol, GEN *L)
+{
+  long n = lg(pol)-1;
+  GEN lc = gel(pol,n);
+  if (is_pm1(lc)) { *L = gen_1; return signe(lc) > 0? pol: ZX_neg(pol); }
+  return ZX_primitive_to_monic(Q_primpart(pol), L);
+}
+
+/* x1*x2^2 + x2*x3^2 + x3*x4^2 + x4*x1^2 */
+static GEN
+F4(GEN x)
+{
+  return gadd(
+    gmul(gel(x,1), gadd(gsqr(gel(x,2)), gmul(gel(x,4),gel(x,1)))),
+    gmul(gel(x,3), gadd(gsqr(gel(x,4)), gmul(gel(x,2),gel(x,3)))));
+}
+
+static GEN
+roots_to_ZX(GEN z, long *e)
+{
+  GEN a = roots_to_pol(z,0);
+  GEN b = grndtoi(real_i(a),e);
+  long e1 = gexpo(imag_i(a));
+  if (e1 > *e) *e = e1;
+  return b;
+}
+
+static GEN
+polgaloisnames(long a, long b)
+{
+  const char * const t[]={"S1", "S2", "A3", "S3",
+       "C(4) = 4", "E(4) = 2[x]2", "D(4)", "A4", "S4",
+       "C(5) = 5", "D(5) = 5:2", "F(5) = 5:4", "A5", "S5",
+       "C(6) = 6 = 3[x]2", "D_6(6) = [3]2", "D(6) = S(3)[x]2",
+             "A_4(6) = [2^2]3", "F_18(6) = [3^2]2 = 3 wr 2",
+             "2A_4(6) = [2^3]3 = 2 wr 3", "S_4(6d) = [2^2]S(3)",
+             "S_4(6c) = 1/2[2^3]S(3)", "F_18(6):2 = [1/2.S(3)^2]2",
+             "F_36(6) = 1/2[S(3)^2]2", "2S_4(6) = [2^3]S(3) = 2 wr S(3)",
+             "L(6) = PSL(2,5) = A_5(6)", "F_36(6):2 = [S(3)^2]2 = S(3) wr 2",
+             "L(6):2 = PGL(2,5) = S_5(6)", "A6", "S6",
+       "C(7) = 7", "D(7) = 7:2", "F_21(7) = 7:3", "F_42(7) = 7:6",
+             "L(7) = L(3,2)", "A7", "S7"};
+
+   const long idx[]={0,1,2,4,9,14,30};
+   return strtoGENstr(t[idx[a-1]+b-1]);
+}
+
+static GEN
+galois_res(long d, long n, long s, long k)
+{
+  long kk = k;
+  GEN z = cgetg(5,t_VEC);
+  if (!new_galois_format)
+  {
+    switch (d) {
+      case 6:
+        kk = (k == 6 || k == 2)? 2: 1;
+        break;
+      default:
+        kk = 1;
+    }
+  }
+  gel(z,1) = stoi(n);
+  gel(z,2) = stoi(s);
+  gel(z,3) = stoi(kk);
+  gel(z,4) = polgaloisnames(d,k);
+  return z;
+}
+
+GEN
+polgalois(GEN x, long prec)
+{
+  pari_sp av = avma, av1;
+  long i,j,k,n,f,l,l2,e,e1,pr,ind;
+  GEN x1,p1,p2,p3,p4,p5,w,z,ee;
+  const int ind5[20]={2,5,3,4, 1,3,4,5, 1,5,2,4, 1,2,3,5, 1,4,2,3};
+  const int ind6[60]={3,5,4,6, 2,6,4,5, 2,3,5,6, 2,4,3,6, 2,5,3,4,
+                      1,4,5,6, 1,5,3,6, 1,6,3,4, 1,3,4,5, 1,6,2,5,
+                      1,2,4,6, 1,5,2,4, 1,3,2,6, 1,2,3,5, 1,4,2,3};
+  if (typ(x)!=t_POL) pari_err_TYPE("galois",x);
+  n=degpol(x);
+  if (n>11) pari_err_IMPL("galois of degree higher than 11");
+  x = Q_primpart(x);
+  RgX_check_ZX(x, "galois");
+  if (!ZX_is_irred(x)) pari_err_IRREDPOL("galois",x);
+
+  if (n<4)
+  {
+    if (n == 1) { avma = av; return galois_res(n,1, 1,1); }
+    if (n == 2) { avma = av; return galois_res(n,2,-1,1); }
+    /* n = 3 */
+    f = Z_issquare(ZX_disc(x));
+    avma = av;
+    return f? galois_res(n,3,1,1):
+              galois_res(n,6,-1,2);
+  }
+  x1 = x = ZX_Q_normalize(x,NULL); av1=avma;
+  if (n > 7) return galoisbig(x, prec);
+  for(;;)
+  {
+    double cb = cauchy_bound(x);
+    switch(n)
+    {
+      case 4: z = cgetg(7,t_VEC);
+        prec = nbits2prec((long)(cb*(18./ LOG2)) + 64);
+        for(;;)
+        {
+          p1=QX_complex_roots(x,prec);
+          gel(z,1) = F4(p1);
+          gel(z,2) = F4(transroot(p1,1,2));
+          gel(z,3) = F4(transroot(p1,1,3));
+          gel(z,4) = F4(transroot(p1,1,4));
+          gel(z,5) = F4(transroot(p1,2,3));
+          gel(z,6) = F4(transroot(p1,3,4));
+          p5 = roots_to_ZX(z, &e); if (e <= -10) break;
+          prec = precdbl(prec);
+        }
+        if (!ZX_is_squarefree(p5)) goto tchi;
+        p2 = gel(ZX_factor(p5),1);
+        switch(lg(p2)-1)
+        {
+          case 1: f = Z_issquare(ZX_disc(x)); avma = av;
+            return f? galois_res(n,12,1,4): galois_res(n,24,-1,5);
+
+          case 2: avma = av; return galois_res(n,8,-1,3);
+
+          case 3: avma = av;
+            return (degpol(gel(p2,1))==2)? galois_res(n,4,1,2)
+                                         : galois_res(n,4,-1,1);
+
+          default: pari_err_BUG("galois (bug1)");
+        }
+
+      case 5: z = cgetg(7,t_VEC);
+        ee= cgetg(7,t_VECSMALL);
+        w = cgetg(7,t_VECSMALL);
+        prec = nbits2prec((long)(cb*(21. / LOG2)) + 64);
+        for(;;)
+        {
+          for(;;)
+          {
+            p1=QX_complex_roots(x,prec);
+            for (l=1; l<=6; l++)
+            {
+              p2=(l==1)?p1: ((l<6)?transroot(p1,1,l): transroot(p1,2,5));
+              p3=gen_0;
+              for (k=0,i=1; i<=5; i++,k+=4)
+              {
+                p5 = gadd(gmul(gel(p2,ind5[k]),gel(p2,ind5[k+1])),
+                          gmul(gel(p2,ind5[k+2]),gel(p2,ind5[k+3])));
+                p3 = gadd(p3, gmul(gsqr(gel(p2,i)),p5));
+              }
+              gel(w,l) = grndtoi(real_i(p3),&e);
+              e1 = gexpo(imag_i(p3)); if (e1>e) e=e1;
+              ee[l]=e; gel(z,l) = p3;
+            }
+            p5 = roots_to_ZX(z, &e); if (e <= -10) break;
+            prec = precdbl(prec);
+          }
+          if (!ZX_is_squarefree(p5)) goto tchi;
+          p3=gel(ZX_factor(p5),1);
+          f=Z_issquare(ZX_disc(x));
+          if (lg(p3)-1==1)
+          {
+            avma = av;
+            return f? galois_res(n,60,1,4): galois_res(n,120,-1,5);
+          }
+          if (!f) { avma = av; return galois_res(n,20,-1,3); }
+
+          pr = - (prec2nbits(prec) >> 1);
+          for (l=1; l<=6; l++)
+            if (ee[l] <= pr && gequal0(poleval(p5,gel(w,l)))) break;
+          if (l>6) pari_err_BUG("galois (bug4)");
+          p2=(l==6)? transroot(p1,2,5):transroot(p1,1,l);
+          p3=gen_0;
+          for (i=1; i<=5; i++)
+          {
+            j = (i == 5)? 1: i+1;
+            p3 = gadd(p3,gmul(gmul(gel(p2,i),gel(p2,j)),
+                              gsub(gel(p2,j),gel(p2,i))));
+          }
+          p5=gsqr(p3); p4=grndtoi(real_i(p5),&e);
+          e1 = gexpo(imag_i(p5)); if (e1>e) e=e1;
+          if (e <= -10)
+          {
+            if (gequal0(p4)) goto tchi;
+            f = Z_issquare(p4); avma = av;
+            return f? galois_res(n,5,1,1): galois_res(n,10,1,2);
+          }
+          prec = precdbl(prec);
+        }
+
+      case 6: z = cgetg(7, t_VEC);
+        prec = nbits2prec((long) (cb * (42. / LOG2)) + 64);
+        for(;;)
+        {
+          for(;;)
+          {
+            p1=QX_complex_roots(x,prec);
+            for (l=1; l<=6; l++)
+            {
+              p2=(l==1)?p1:transroot(p1,1,l);
+              p3=gen_0; k=0;
+              for (i=1; i<=5; i++) for (j=i+1; j<=6; j++)
+              {
+                p5=gadd(gmul(gel(p2,ind6[k]),gel(p2,ind6[k+1])),
+                        gmul(gel(p2,ind6[k+2]),gel(p2,ind6[k+3])));
+                p3=gadd(p3,gmul(gsqr(gmul(gel(p2,i),gel(p2,j))),p5));
+                k += 4;
+              }
+              gel(z,l) = p3;
+            }
+            p5 = roots_to_ZX(z, &e); if (e <= -10) break;
+            prec = precdbl(prec);
+          }
+          if (!ZX_is_squarefree(p5)) goto tchi;
+          p2=gel(ZX_factor(p5),1);
+          switch(lg(p2)-1)
+          {
+            case 1:
+              z = cgetg(11,t_VEC); ind=0;
+              p3=gadd(gmul(gmul(gel(p1,1),gel(p1,2)),gel(p1,3)),
+                      gmul(gmul(gel(p1,4),gel(p1,5)),gel(p1,6)));
+              gel(z,++ind) = p3;
+              for (i=1; i<=3; i++)
+                for (j=4; j<=6; j++)
+                {
+                  p2=transroot(p1,i,j);
+                  p3=gadd(gmul(gmul(gel(p2,1),gel(p2,2)),gel(p2,3)),
+                          gmul(gmul(gel(p2,4),gel(p2,5)),gel(p2,6)));
+                  gel(z,++ind) = p3;
+                }
+              p5 = roots_to_ZX(z, &e);
+              if (e <= -10)
+              {
+                if (!ZX_is_squarefree(p5)) goto tchi;
+                p2 = gel(ZX_factor(p5),1);
+                f = Z_issquare(ZX_disc(x));
+                avma = av;
+                if (lg(p2)-1==1)
+                  return f? galois_res(n,360,1,15): galois_res(n,720,-1,16);
+                else
+                  return f? galois_res(n,36,1,10): galois_res(n,72,-1,13);
+              }
+              prec = precdbl(prec); break;
+
+            case 2: l2=degpol(gel(p2,1)); if (l2>3) l2=6-l2;
+              switch(l2)
+              {
+                case 1: f = Z_issquare(ZX_disc(x));
+                  avma = av;
+                  return f? galois_res(n,60,1,12): galois_res(n,120,-1,14);
+                case 2: f = Z_issquare(ZX_disc(x));
+                  if (f) { avma = av; return galois_res(n,24,1,7); }
+                  p3 = (degpol(gel(p2,1))==2)? gel(p2,2): gel(p2,1);
+                  f = Z_issquare(ZX_disc(p3));
+                  avma = av;
+                  return f? galois_res(n,24,-1,6): galois_res(n,48,-1,11);
+                case 3: f = Z_issquare(ZX_disc(gel(p2,1)))
+                         || Z_issquare(ZX_disc(gel(p2,2)));
+                  avma = av;
+                  return f? galois_res(n,18,-1,5): galois_res(n,36,-1,9);
+              }
+            case 3:
+              for (l2=1; l2<=3; l2++)
+                if (degpol(gel(p2,l2)) >= 3) p3 = gel(p2,l2);
+              if (degpol(p3) == 3)
+              {
+                f = Z_issquare(ZX_disc(p3)); avma = av;
+                return f? galois_res(n,6,-1,1): galois_res(n,12,-1,3);
+              }
+              else
+              {
+                f = Z_issquare(ZX_disc(x)); avma = av;
+                return f? galois_res(n,12,1,4): galois_res(n,24,-1,8);
+              }
+            case 4: avma = av; return galois_res(n,6,-1,2);
+            default: pari_err_BUG("galois (bug3)");
+          }
+        }
+
+      case 7: z = cgetg(36,t_VEC);
+        prec = nbits2prec((long)(cb*(7. / LOG2)) + 64);
+        for(;;)
+        {
+          ind = 0; p1=QX_complex_roots(x,prec);
+          for (i=1; i<=5; i++)
+            for (j=i+1; j<=6; j++)
+            {
+              GEN t = gadd(gel(p1,i),gel(p1,j));
+              for (k=j+1; k<=7; k++) gel(z,++ind) = gadd(t, gel(p1,k));
+            }
+          p5 = roots_to_ZX(z, &e); if (e <= -10) break;
+          prec = precdbl(prec);
+        }
+        if (!ZX_is_squarefree(p5)) goto tchi;
+        p2=gel(ZX_factor(p5),1);
+        switch(lg(p2)-1)
+        {
+          case 1: f = Z_issquare(ZX_disc(x)); avma = av;
+            return f? galois_res(n,2520,1,6): galois_res(n,5040,-1,7);
+          case 2: f = degpol(gel(p2,1)); avma = av;
+            return (f==7 || f==28)? galois_res(n,168,1,5): galois_res(n,42,-1,4);
+          case 3: avma = av; return galois_res(n,21,1,3);
+          case 4: avma = av; return galois_res(n,14,-1,2);
+          case 5: avma = av; return galois_res(n,7,1,1);
+          default: pari_err_BUG("galois (bug2)");
+        }
+    }
+    tchi: avma = av1; x = tschirnhaus(x1);
+  }
+}
+
+#undef _res
+
+/* Evaluate pol in s using nfelt arithmetic and Horner rule */
+GEN
+nfpoleval(GEN nf, GEN pol, GEN s)
+{
+  pari_sp av=avma;
+  long i=lg(pol)-1;
+  GEN res;
+  if (i==1) return gen_0;
+  res = nf_to_scalar_or_basis(nf, gel(pol,i));
+  for (i-- ; i>=2; i--)
+    res = nfadd(nf, nfmul(nf, s, res), gel(pol,i));
+  return gerepileupto(av, res);
+}
+
+static GEN
+QX_table_nfpoleval(GEN nf, GEN pol, GEN m)
+{
+  pari_sp av = avma;
+  long i = lg(pol)-1;
+  GEN res, den;
+  if (i==1) return gen_0;
+  pol = Q_remove_denom(pol, &den);
+  res = scalarcol_shallow(gel(pol,i), nf_get_degree(nf));
+  for (i-- ; i>=2; i--)
+    res = ZC_Z_add(ZM_ZC_mul(m, res), gel(pol,i));
+  if (den) res = RgC_Rg_div(res, den);
+  return gerepileupto(av, res);
+}
+
+GEN
+FpX_FpC_nfpoleval(GEN nf, GEN pol, GEN a, GEN p)
+{
+  pari_sp av=avma;
+  long i=lg(pol)-1, n=nf_get_degree(nf);
+  GEN res, Ma;
+  if (i==1) return zerocol(n);
+  Ma = FpM_red(zk_multable(nf, a), p);
+  res = scalarcol(gel(pol,i),n);
+  for (i-- ; i>=2; i--)
+  {
+    res = FpM_FpC_mul(Ma, res, p);
+    gel(res,1) = Fp_add(gel(res,1), gel(pol,i), p);
+  }
+  return gerepileupto(av, res);
+}
+
+/* compute s(x), not stack clean */
+static GEN
+table_galoisapply(GEN nf, GEN m, GEN x)
+{
+  x = nf_to_scalar_or_alg(nf, x);
+  if (typ(x) != t_POL) return scalarcol(x, nf_get_degree(nf));
+  return QX_table_nfpoleval(nf, x, m);
+}
+
+/* compute s(x), not stack clean */
+static GEN
+ZC_galoisapply(GEN nf, GEN s, GEN x)
+{
+  x = nf_to_scalar_or_alg(nf, x);
+  if (typ(x) != t_POL) return scalarcol(x, nf_get_degree(nf));
+  return QX_table_nfpoleval(nf, x, zk_multable(nf, s));
+}
+
+static GEN
+QX_galoisapplymod(GEN nf, GEN pol, GEN S, GEN p)
+{
+  GEN den, P = Q_remove_denom(pol,&den);
+  GEN pe, pe1, denpe, R;
+  if (den)
+  {
+    ulong e = Z_pval(den, p);
+    pe = powiu(p, e); pe1 = mulii(pe, p);
+    denpe = Fp_inv(diviiexact(den, pe), pe1);
+  } else {
+    pe = gen_1; pe1 = p; denpe = gen_1;
+  }
+  R = FpX_FpC_nfpoleval(nf, FpX_red(P, pe1), FpC_red(S, pe1), pe1);
+  return gdivexact(FpC_Fp_mul(R, denpe, pe1), pe);
+}
+
+static GEN
+pr_galoisapply(GEN nf, GEN pr, GEN aut)
+{
+  GEN p, t, u;
+  if (typ(pr_get_tau(pr)) == t_INT) return pr; /* inert */
+  p = pr_get_p(pr);
+  u = QX_galoisapplymod(nf, coltoliftalg(nf, pr_get_gen(pr)), aut, p);
+  t = FpM_deplin(zk_multable(nf, u), p);
+  t = zk_scalar_or_multable(nf, t);
+  return mkvec5(p, u, gel(pr,3), gel(pr,4), t);
+}
+
+static GEN
+vecgaloisapply(GEN nf, GEN aut, GEN v)
+{
+  long i, l;
+  GEN V = cgetg_copy(v, &l);
+  for (i = 1; i < l; i++) gel(V,i) = galoisapply(nf, aut, gel(v,i));
+  return V;
+}
+
+/* x: famat or standard algebraic number, aut automorphism in ZC form
+ * simplified from general galoisapply */
+static GEN
+elt_galoisapply(GEN nf, GEN aut, GEN x)
+{
+  pari_sp av = avma;
+  switch(typ(x))
+  {
+    case t_INT:  return icopy(x);
+    case t_FRAC: return gcopy(x);
+    case t_POLMOD: x = gel(x,2); /* fall through */
+    case t_POL: {
+      GEN y = basistoalg(nf, ZC_galoisapply(nf, aut, x));
+      return gerepileupto(av,y);
+    }
+    case t_COL:
+      return gerepileupto(av, ZC_galoisapply(nf, aut, x));
+    case t_MAT:
+      switch(lg(x)) {
+        case 1: return cgetg(1, t_MAT);
+        case 3: retmkmat2(vecgaloisapply(nf,aut,gel(x,1)), ZC_copy(gel(x,2)));
+      }
+  }
+  pari_err_TYPE("galoisapply",x);
+  return NULL; /* not reached */
+}
+
+GEN
+galoisapply(GEN nf, GEN aut, GEN x)
+{
+  pari_sp av = avma;
+  long lx, j;
+  GEN y;
+
+  nf = checknf(nf);
+  switch(typ(x))
+  {
+    case t_INT:  return icopy(x);
+    case t_FRAC: return gcopy(x);
+
+    case t_POLMOD: x = gel(x,2); /* fall through */
+    case t_POL:
+      aut = algtobasis(nf, aut);
+      y = basistoalg(nf, ZC_galoisapply(nf, aut, x));
+      return gerepileupto(av,y);
+
+    case t_VEC:
+      aut = algtobasis(nf, aut);
+      switch(lg(x))
+      {
+        case 6: return gerepilecopy(av, pr_galoisapply(nf, x, aut));
+        case 3: y = cgetg(3,t_VEC);
+          gel(y,1) = galoisapply(nf, aut, gel(x,1));
+          gel(y,2) = elt_galoisapply(nf, aut, gel(x,2));
+          return gerepileupto(av, y);
+      }
+      break;
+
+    case t_COL:
+      aut = algtobasis(nf, aut);
+      return gerepileupto(av, ZC_galoisapply(nf, aut, x));
+
+    case t_MAT: /* ideal */
+      lx = lg(x); if (lx==1) return cgetg(1,t_MAT);
+      if (nbrows(x) != nf_get_degree(nf)) break;
+      aut = zk_multable(nf, algtobasis(nf, aut));
+      y = cgetg(lx,t_MAT);
+      for (j=1; j<lx; j++) gel(y,j) = table_galoisapply(nf, aut, gel(x,j));
+      return gerepileupto(av, idealhnf_shallow(nf,y));
+  }
+  pari_err_TYPE("galoisapply",x);
+  return NULL; /* not reached */
+}
+
+GEN
+nfgaloismatrix(GEN nf, GEN s)
+{
+  GEN zk, M, m;
+  long k, l;
+  nf = checknf(nf);
+  zk = nf_get_zk(nf);
+  if (typ(s) != t_COL) s = algtobasis(nf, s); /* left on stack for efficiency */
+  m = zk_multable(nf, s);
+  l = lg(s); M = cgetg(l, t_MAT);
+  gel(M, 1) = col_ei(l-1, 1); /* s(1) = 1 */
+  for (k = 2; k < l; k++)
+    gel(M, k) = QX_table_nfpoleval(nf, gel(zk, k), m);
+  return M;
+}
+
+static GEN
+idealquasifrob(GEN nf, GEN gal, GEN pr, GEN subg, GEN *S)
+{
+  pari_sp av = avma;
+  long i, n = nf_get_degree(nf), f = pr_get_f(pr);
+  GEN grp = gal_get_group(gal), pi = pr_get_gen(pr);
+  for (i=1; i<=n; i++)
+  {
+    GEN g = gel(grp,i);
+    if ((!subg && perm_order(g)==f)
+      || (subg && perm_relorder(g, subg)==f))
+    {
+      *S = poltobasis(nf, galoispermtopol(gal, g));
+      if (ZC_prdvd(nf, ZC_galoisapply(nf, *S, pi), pr)) return g;
+      avma = av;
+    }
+  }
+  pari_err_BUG("idealquasifrob [Frobenius not found]");
+  return NULL; /*NOT REACHED*/
+}
+
+static void
+gal_check_pol(const char *f, GEN x, GEN y)
+{ if (!RgX_equal_var(x,y)) pari_err_MODULUS(f,x,y); }
+
+GEN
+idealfrobenius(GEN nf, GEN gal, GEN pr)
+{
+  pari_sp av = avma;
+  GEN S=NULL, g=NULL; /*-Wall*/
+  GEN T, p, a, b, modpr;
+  long f, n, s;
+  nf = checknf(nf);
+  checkgal(gal);
+  checkprid(pr);
+  gal_check_pol("idealfrobenius",nf_get_pol(nf),gal_get_pol(gal));
+  if (pr_get_e(pr)>1) pari_err_DOMAIN("idealfrobenius","pr.e", ">", gen_1,pr);
+  f = pr_get_f(pr); n = nf_get_degree(nf);
+  if (f==1) { avma = av; return identity_perm(n); }
+  modpr = zk_to_Fq_init(nf,&pr,&T,&p);
+  g = idealquasifrob(nf, gal, pr, NULL, &S);
+  a = pol_x(nf_get_varn(nf));
+  b = nf_to_Fq(nf, QX_galoisapplymod(nf, modpr_genFq(modpr), S, p), modpr);
+  for (s=0; !ZX_equal(a, b); s++)
+    a = Fq_pow(a, p, T, p);
+  g = perm_pow(g, Fl_inv(s, f));
+  return gerepileupto(av, g);
+}
+
+static GEN
+idealinertiagroup(GEN nf, GEN gal, GEN pr)
+{
+  long i, n = nf_get_degree(nf);
+  GEN p, T, modpr = zk_to_Fq_init(nf,&pr,&T,&p);
+  GEN b = modpr_genFq(modpr);
+  long e = pr_get_e(pr), coprime = cgcd(e, pr_get_f(pr)) == 1;
+  GEN grp = gal_get_group(gal), pi = pr_get_gen(pr);
+  pari_sp ltop = avma;
+  for (i=1; i<=n; i++)
+  {
+    GEN iso = gel(grp,i);
+    if (perm_order(iso) == e)
+    {
+      GEN S = poltobasis(nf, galoispermtopol(gal, iso));
+      if (ZC_prdvd(nf, ZC_galoisapply(nf, S, pi), pr)
+          && (coprime || gequalX(nf_to_Fq(nf, galoisapply(nf,S,b), modpr))))
+          return iso;
+      avma = ltop;
+    }
+  }
+  pari_err_BUG("idealinertiagroup [no isotropic element]");
+  return NULL;
+}
+
+static GEN
+idealramgroupstame(GEN nf, GEN gal, GEN pr)
+{
+  pari_sp av = avma;
+  GEN iso, frob, giso, isog, S, res;
+  long e = pr_get_e(pr), f = pr_get_f(pr);
+  if (e == 1)
+  {
+    if (f==1)
+      return cgetg(1,t_VEC);
+    frob = idealquasifrob(nf, gal, pr, NULL, &S);
+    avma = av;
+    res = cgetg(2, t_VEC);
+    gel(res, 1) = cyclicgroup(frob, f);
+    return res;
+  }
+  res = cgetg(3, t_VEC);
+  av = avma;
+  iso = idealinertiagroup(nf, gal, pr);
+  avma = av;
+  giso = cyclicgroup(iso, e);
+  gel(res, 2) = giso;
+  if (f==1)
+  {
+    gel(res, 1) = giso;
+    return res;
+  }
+  av = avma;
+  isog = group_set(giso, nf_get_degree(nf));
+  frob = idealquasifrob(nf, gal, pr, isog, &S);
+  avma = av;
+  gel(res, 1) = dicyclicgroup(iso,frob,e,f);
+  return res;
+}
+
+static GEN
+idealramgroupindex(GEN nf, GEN gal, GEN pr)
+{
+  pari_sp av = avma;
+  GEN p, T, g, idx, modpr;
+  long i, e, f, n;
+  ulong nt,rorder;
+  GEN grp = vecvecsmall_sort(gal_get_group(gal));
+  e = pr_get_e(pr); f = pr_get_f(pr); n = nf_get_degree(nf);
+  modpr = zk_to_Fq_init(nf,&pr,&T,&p);
+  (void) u_pvalrem(n,p,&nt);
+  rorder = e*f*(n/nt);
+  idx = const_vecsmall(n,-1);
+  g = modpr_genFq(modpr);
+  for (i=2; i<=n; i++)
+  {
+    GEN iso;
+    long o;
+    if (idx[i]>=0) continue;
+    iso = gel(grp,i); o = perm_order(iso);
+    if (rorder%o == 0)
+    {
+      GEN piso = iso;
+      GEN S = poltobasis(nf, galoispermtopol(gal, iso));
+      GEN pi = pr_get_gen(pr);
+      GEN spi = ZC_galoisapply(nf, S, pi);
+      long j;
+      idx[i] = idealval(nf, gsub(spi,pi), pr);
+      if (idx[i] >=1)
+      {
+        if (f>1)
+        {
+          GEN b = nf_to_Fq(nf, QX_galoisapplymod(nf, g, S, p), modpr);
+          if (!gequalX(b)) idx[i] = 0;
+        }
+      }
+      else idx[i] = -1;
+      for(j=2;j<o;j++)
+      {
+        piso = perm_mul(piso,iso);
+        if(cgcd(j,o)==1) idx[piso[1]] = idx[i];
+      }
+    }
+  }
+  return gerepileuptoleaf(av, idx);
+}
+
+GEN
+idealramgroups(GEN nf, GEN gal, GEN pr)
+{
+  pari_sp av = avma;
+  GEN tbl, idx, res, set, sub;
+  long i, j, e, n, maxm, p;
+  ulong et;
+  nf = checknf(nf);
+  checkgal(gal);
+  checkprid(pr);
+  gal_check_pol("idealramgroups",nf_get_pol(nf),gal_get_pol(gal));
+  e = pr_get_e(pr); n = nf_get_degree(nf);
+  p = itos(pr_get_p(pr));
+  if (e%p) return idealramgroupstame(nf, gal, pr);
+  (void) u_lvalrem(e,p,&et);
+  idx = idealramgroupindex(nf, gal, pr);
+  sub = group_subgroups(galois_group(gal));
+  tbl = subgroups_tableset(sub, n);
+  maxm = vecsmall_max(idx)+1;
+  res = cgetg(maxm+1,t_VEC);
+  set = zero_F2v(n); F2v_set(set,1);
+  for(i=maxm; i>0; i--)
+  {
+    for(j=1;j<=n;j++)
+      if (idx[j]==i-1)
+        F2v_set(set,j);
+    gel(res,i) = gel(sub, tableset_find_index(tbl, set));
+  }
+  return gerepilecopy(av, res);
+}
+
+/* x = relative polynomial nf = absolute nf, bnf = absolute bnf */
+GEN
+get_bnfpol(GEN x, GEN *bnf, GEN *nf)
+{
+  *bnf = checkbnf_i(x);
+  *nf  = checknf_i(x);
+  if (*nf) x = nf_get_pol(*nf);
+  if (typ(x) != t_POL) pari_err_TYPE("get_bnfpol",x);
+  return x;
+}
+
+GEN
+get_nfpol(GEN x, GEN *nf)
+{
+  if (typ(x) == t_POL) { *nf = NULL; return x; }
+  *nf = checknf(x); return nf_get_pol(*nf);
+}
+
+/* is isomorphism / inclusion (a \subset b) compatible with what we know about
+ * basic invariants ? (degree, signature, discriminant) */
+static int
+tests_OK(GEN a, GEN nfa, GEN b, GEN nfb, long fliso)
+{
+  GEN da, db, fa, P, E, U;
+  long i, nP, m = degpol(a), n = degpol(b), q = m / n; /* relative degree */
+
+  if (m <= 0) pari_err_IRREDPOL("nfisincl",a);
+  if (n <= 0) pari_err_IRREDPOL("nfisincl",b);
+  if (fliso) { if (n != m) return 0; } else { if (n % m) return 0; }
+  if (m == 1) return 1;
+
+  if (nfa && nfb) /* both nf structures available */
+  {
+    long r1a = nf_get_r1(nfa), r1b = nf_get_r1(nfb) ;
+    if (fliso)
+      return (r1a == r1b && equalii(nf_get_disc(nfa), nf_get_disc(nfb)));
+    else
+      return (r1b <= r1a * q &&
+              dvdii(nf_get_disc(nfb), powiu(nf_get_disc(nfa), q)));
+  }
+  da = nfa? nf_get_disc(nfa): ZX_disc(a);
+  if (!signe(da)) pari_err_IRREDPOL("nfisincl",a);
+  db = nfb? nf_get_disc(nfb): ZX_disc(b);
+  if (!signe(db)) pari_err_IRREDPOL("nfisincl",a);
+  if (fliso) return issquare(gdiv(da,db));
+
+  if (odd(q) && signe(da) != signe(db)) return 0;
+  fa = absi_factor_limit(da, 0);
+  P = gel(fa,1);
+  E = gel(fa,2); nP = lg(P) - 1;
+  for (i=1; i<nP; i++)
+    if (mod2(gel(E,i)) && !dvdii(db, powiu(gel(P,i),q))) return 0;
+  U = gel(P,nP);
+  if (expi(U) < 150) /* "unfactored" cofactor is small, finish */
+  {
+    if (cmpiu(U, maxprime()) > 0)
+    {
+      fa = Z_factor(U);
+      P = gel(fa,1);
+      E = gel(fa,2);
+    }
+    else
+    {
+      P = mkvec(U);
+      E = mkvec(gel(E,nP));
+    }
+    nP = lg(P) - 1;
+    for (i=1; i<=nP; i++)
+      if (mod2(gel(E,i)) && !dvdii(db, powiu(gel(P,i),q))) return 0;
+  }
+  return 1;
+}
+
+/* if fliso test for isomorphism, for inclusion otherwise. */
+static GEN
+nfiso0(GEN a, GEN b, long fliso)
+{
+  pari_sp av = avma;
+  long i, vb, lx;
+  GEN nfa, nfb, y, la, lb;
+
+  a = get_nfpol(a, &nfa);
+  b = get_nfpol(b, &nfb);
+  if (!nfa) { a = Q_primpart(a); RgX_check_ZX(a, "nsiso0"); }
+  if (!nfb) { b = Q_primpart(b); RgX_check_ZX(b, "nsiso0"); }
+  if (fliso && nfa && !nfb) { swap(a,b); nfb = nfa; nfa = NULL; }
+  if (!tests_OK(a, nfa, b, nfb, fliso)) { avma = av; return gen_0; }
+
+  if (nfb) lb = gen_1; else b = ZX_Q_normalize(b,&lb);
+  if (nfa) la = gen_1; else a = ZX_Q_normalize(a,&la);
+  a = leafcopy(a); setvarn(a,0);
+  b = leafcopy(b); vb = varn(b);
+  if (nfb)
+  {
+    if (vb == 0) nfb = gsubst(nfb, 0, pol_x(MAXVARN));
+    y = lift_intern(nfroots(nfb,a));
+  }
+  else
+  {
+    if (vb == 0) setvarn(b, fetch_var());
+    y = gel(polfnf(a,b),1); lx = lg(y);
+    for (i=1; i<lx; i++)
+    {
+      GEN t = gel(y,i);
+      if (degpol(t) != 1) { setlg(y,i); break; }
+      gel(y,i) = gneg_i(lift_intern(gel(t,2)));
+    }
+    if (vb == 0) (void)delete_var();
+    settyp(y, t_VEC);
+    gen_sort_inplace(y, (void*)&cmp_RgX, &cmp_nodata, NULL);
+  }
+  lx = lg(y); if (lx==1) { avma=av; return gen_0; }
+  for (i=1; i<lx; i++)
+  {
+    GEN t = gel(y,i);
+    if (typ(t) == t_POL) setvarn(t, vb); else t = scalarpol(t, vb);
+    if (lb != gen_1) t = RgX_unscale(t, lb);
+    if (la != gen_1) t = RgX_Rg_div(t, la);
+    gel(y,i) = t;
+  }
+  return gerepilecopy(av,y);
+}
+
+GEN
+nfisisom(GEN a, GEN b) { return nfiso0(a,b,1); }
+
+GEN
+nfisincl(GEN a, GEN b) { return nfiso0(a,b,0); }
+
+/*************************************************************************/
+/**                                                                     **/
+/**                               INITALG                               **/
+/**                                                                     **/
+/*************************************************************************/
+
+GEN
+get_roots(GEN x, long r1, long prec)
+{
+  GEN roo = (typ(x)!=t_POL)? leafcopy(x): QX_complex_roots(x,prec);
+  long i, ru = (lg(roo)-1 + r1) >> 1;
+
+  for (i=r1+1; i<=ru; i++) gel(roo,i) = gel(roo, (i<<1)-r1);
+  roo[0]=evaltyp(t_VEC)|evallg(ru+1); return roo;
+}
+
+GEN
+nf_get_allroots(GEN nf)
+{
+  return embed_roots(nf_get_roots(nf), nf_get_r1(nf));
+}
+
+/* For internal use. compute trace(x mod pol), sym=polsym(pol,deg(pol)-1) */
+GEN
+quicktrace(GEN x, GEN sym)
+{
+  GEN p1 = gen_0;
+  long i;
+
+  if (typ(x) != t_POL) return gmul(x, gel(sym,1));
+  if (signe(x))
+  {
+    sym--;
+    for (i=lg(x)-1; i>1; i--)
+      p1 = gadd(p1, gmul(gel(x,i),gel(sym,i)));
+  }
+  return p1;
+}
+
+static GEN
+get_Tr(GEN mul, GEN x, GEN basden)
+{
+  GEN t, bas = gel(basden,1), den = gel(basden,2);
+  long i, j, n = lg(bas)-1;
+  GEN T = cgetg(n+1,t_MAT), TW = cgetg(n+1,t_COL), sym = polsym(x, n-1);
+
+  gel(TW,1) = utoipos(n);
+  for (i=2; i<=n; i++)
+  {
+    t = quicktrace(gel(bas,i), sym);
+    if (den && den[i]) t = diviiexact(t,gel(den,i));
+    gel(TW,i) = t; /* tr(w[i]) */
+  }
+  gel(T,1) = TW;
+  for (i=2; i<=n; i++)
+  {
+    gel(T,i) = cgetg(n+1,t_COL); gcoeff(T,1,i) = gel(TW,i);
+    for (j=2; j<=i; j++) /* Tr(W[i]W[j]) */
+      gcoeff(T,i,j) = gcoeff(T,j,i) = ZV_dotproduct(gel(mul,j+(i-1)*n), TW);
+  }
+  return T;
+}
+
+/* return [bas[i]*denom(bas[i]), denom(bas[i])], denom 1 is given as NULL */
+GEN
+get_bas_den(GEN bas)
+{
+  GEN b,d,den, dbas = leafcopy(bas);
+  long i, l = lg(bas);
+  int power = 1;
+  den = cgetg(l,t_VEC);
+  for (i=1; i<l; i++)
+  {
+    b = Q_remove_denom(gel(bas,i), &d);
+    gel(dbas,i) = b;
+    gel(den,i) = d; if (d) power = 0;
+  }
+  if (power) den = NULL; /* power basis */
+  return mkvec2(dbas, den);
+}
+
+/* Internal: nf partially filled. Require pol; fill zk, invzk, multable */
+void
+nf_set_multable(GEN nf, GEN bas, GEN basden)
+{
+  GEN T = nf_get_pol(nf), invbas, basM;
+  long i,j, n = degpol(T);
+  GEN w, den, mul = cgetg(n*n+1,t_MAT);
+
+  if (typ(bas) == t_MAT)
+  { basM = bas; bas = RgM_to_RgXV(basM, varn(T)); }
+  else
+    basM = RgV_to_RgM(bas, n);
+  gel(nf,7) = bas;
+  gel(nf,8) = invbas = QM_inv(basM, gen_1);
+  gel(nf,9) = mul;
+
+  if (!basden) basden = get_bas_den(nf_get_zk(nf)); /*integral basis*/
+  w   = gel(basden,1);
+  den = gel(basden,2);
+  /* i = 1 split for efficiency, assume w[1] = 1 */
+  for (j=1; j<=n; j++)
+    gel(mul,j) = gel(mul,1+(j-1)*n) = col_ei(n, j);
+  for (i=2; i<=n; i++)
+    for (j=i; j<=n; j++)
+    {
+      pari_sp av = avma;
+      GEN z = (i == j)? ZXQ_sqr(gel(w,i), T): ZXQ_mul(gel(w,i),gel(w,j), T);
+      z = mulmat_pol(invbas, z); /* integral column */
+      if (den)
+      {
+        GEN d = mul_denom(gel(den,i), gel(den,j));
+        if (d) z = ZC_Z_divexact(z, d);
+      }
+      gel(mul,j+(i-1)*n) = gel(mul,i+(j-1)*n) = gerepileupto(av,z);
+    }
+}
+
+/* as get_Tr, mul_table not precomputed */
+static GEN
+make_Tr(GEN x, GEN basden)
+{
+  long i,j, n = degpol(x);
+  GEN c, t, d;
+  GEN T   = cgetg(n+1,t_MAT);
+  GEN sym = polsym(x, n-1);
+  GEN w   = gel(basden,1); /* W[i] = w[i]/den[i] */
+  GEN den = gel(basden,2);
+  /* assume W[1] = 1, case i = 1 split for efficiency */
+  c = cgetg(n+1,t_COL); gel(T,1) = c;
+  gel(c, 1) = utoipos(n);
+  for (j=2; j<=n; j++)
+  {
+    pari_sp av = avma;
+    t = quicktrace(gel(w,j), sym);
+    if (den)
+    {
+      d = gel(den,j);
+      if (d) t = diviiexact(t, d);
+    }
+    gel(c,j) = gerepileuptoint(av, t);
+  }
+  for (i=2; i<=n; i++)
+  {
+    c = cgetg(n+1,t_COL); gel(T,i) = c;
+    for (j=1; j<i ; j++) gel(c,j) = gcoeff(T,i,j);
+    for (   ; j<=n; j++)
+    {
+      pari_sp av = avma;
+      t = (i == j)? ZXQ_sqr(gel(w,i), x): ZXQ_mul(gel(w,i),gel(w,j), x);
+      t = quicktrace(t, sym);
+      if (den)
+      {
+        d = mul_denom(gel(den,i),gel(den,j));
+        if (d) t = diviiexact(t, d);
+      }
+      gel(c,j) = gerepileuptoint(av, t); /* Tr (W[i]W[j]) */
+    }
+  }
+  return T;
+}
+
+/* compute roots so that _absolute_ accuracy of M >= prec [also holds for G] */
+static void
+get_roots_for_M(nffp_t *F)
+{
+  long n, eBD, PREC;
+
+  if (F->extraprec < 0)
+  { /* not initialized yet */
+    double er;
+    n = degpol(F->x);
+    eBD = 1 + gexpo(gel(F->basden,1));
+    er  = F->ro? (1+gexpo(F->ro)): cauchy_bound(F->x)/LOG2;
+    if (er < 0) er = 0;
+    F->extraprec = nbits2extraprec((long)(n*er + eBD + log2(n))-(BITS_IN_LONG-1));/*FIXME*/
+  }
+
+  PREC = F->prec + F->extraprec;
+  if (F->ro && gprecision(gel(F->ro,1)) >= PREC) return;
+  F->ro = get_roots(F->x, F->r1, PREC);
+}
+
+/* [bas[i]/den[i]]= integer basis. roo = real part of the roots */
+static void
+make_M(nffp_t *F, int trunc)
+{
+  GEN bas = gel(F->basden,1), den = gel(F->basden,2), ro = F->ro;
+  GEN m, d, M;
+  long i, j, l = lg(ro), n = lg(bas);
+  M = cgetg(n,t_MAT);
+  gel(M,1) = const_col(l-1, gen_1); /* bas[1] = 1 */
+  for (j=2; j<n; j++)
+  {
+    m = cgetg(l,t_COL); gel(M,j) = m;
+    for (i=1; i<l; i++) gel(m,i) = poleval(gel(bas,j), gel(ro,i));
+  }
+  if (den)
+  {
+    GEN invd, rd = cgetr(F->prec + F->extraprec);
+    for (j=2; j<n; j++)
+    {
+      d = gel(den,j); if (!d) continue;
+      m = gel(M,j); affir(d,rd); invd = invr(rd);
+      for (i=1; i<l; i++) gel(m,i) = gmul(gel(m,i), invd);
+    }
+  }
+
+  if (trunc && gprecision(M) > F->prec)
+  {
+    M     = gprec_w(M, F->prec);
+    F->ro = gprec_w(ro,F->prec);
+  }
+  F->M = M;
+}
+
+/* return G real such that G~ * G = T_2 */
+static void
+make_G(nffp_t *F)
+{
+  GEN G, M = F->M;
+  long i, j, k, r1 = F->r1, l = lg(M);
+
+  G = cgetg(l, t_MAT);
+  for (j=1; j<l; j++)
+  {
+    GEN g = cgetg(l, t_COL);
+    GEN m = gel(M,j);
+    gel(G,j) = g;
+    for (k=i=1; i<=r1; i++) g[k++] = m[i];
+    for (     ; k < l; i++)
+    {
+      GEN r = gel(m,i);
+      if (typ(r) == t_COMPLEX)
+      {
+        gel(g,k++) = mpadd(gel(r,1), gel(r,2));
+        gel(g,k++) = mpsub(gel(r,1), gel(r,2));
+      }
+      else
+      {
+        gel(g,k++) = r;
+        gel(g,k++) = r;
+      }
+    }
+  }
+  F->G = G;
+}
+
+static void
+make_M_G(nffp_t *F, int trunc)
+{
+  get_roots_for_M(F);
+  make_M(F, trunc);
+  make_G(F);
+}
+
+void
+remake_GM(GEN nf, nffp_t *F, long prec)
+{
+  F->x  = nf_get_pol(nf);
+  F->ro = NULL;
+  F->r1 = nf_get_r1(nf);
+  F->basden = get_bas_den(nf_get_zk(nf));
+  F->extraprec = -1;
+  F->prec = prec; make_M_G(F, 1);
+}
+
+static void
+nffp_init(nffp_t *F, nfbasic_t *T, GEN ro, long prec)
+{
+  F->x  = T->x;
+  F->ro = ro;
+  F->r1 = T->r1;
+  if (!T->basden) T->basden = get_bas_den(T->bas);
+  F->basden = T->basden;
+  F->extraprec = -1;
+  F->prec = prec;
+}
+
+static void
+get_nf_fp_compo(nfbasic_t *T, nffp_t *F, GEN ro, int trunc, long prec)
+{
+  nffp_init(F,T,ro,prec);
+  make_M_G(F, trunc);
+}
+
+static GEN
+get_sign(long r1, long n) { return mkvec2s(r1, (n-r1)>>1); }
+
+GEN
+nfbasic_to_nf(nfbasic_t *T, GEN ro, long prec)
+{
+  GEN nf = cgetg(10,t_VEC);
+  GEN x = T->x, absdK, Tr, D, TI, A, dA, MDI, mat = cgetg(9,t_VEC);
+  long n = degpol(T->x);
+  nffp_t F;
+  get_nf_fp_compo(T, &F, ro, 0, prec);
+
+  gel(nf,1) = T->x;
+  gel(nf,2) = get_sign(T->r1, n);
+  gel(nf,3) = T->dK;
+  gel(nf,4) = T->index;
+  gel(nf,6) = F.ro;
+  gel(nf,5) = mat;
+
+  gel(mat,1) = F.M;
+  gel(mat,2) = F.G;
+
+  nf_set_multable(nf, T->bas, F.basden);
+
+  Tr = get_Tr(gel(nf,9), x, F.basden);
+  absdK = T->dK; if (signe(absdK) < 0) absdK = negi(absdK);
+  TI = ZM_inv(Tr, absdK); /* dK T^-1 */
+  A = Q_primitive_part(TI, &dA);
+  gel(mat,6) = A; /* primitive part of codifferent, dA its content */
+  dA = dA? diviiexact(absdK, dA): absdK;
+  A = ZM_hnfmodid(A, dA);
+  MDI = idealtwoelt(nf, A);
+  gel(MDI,2) = zk_scalar_or_multable(nf, gel(MDI,2));
+  gel(mat,7) = MDI;
+  if (is_pm1(T->index)) /* principal ideal (x'), whose norm is |dK| */
+  {
+    D = zk_scalar_or_multable(nf, ZX_deriv(x));
+    if (typ(D) == t_MAT) D = ZM_hnfmod(D, absdK);
+  }
+  else
+    D = RgM_Rg_mul(idealinv(nf, A), dA);
+  gel(mat,3) = RM_round_maxrank(F.G);
+  gel(mat,4) = Tr;
+  gel(mat,5) = D;
+  gel(mat,8) = T->dKP? shallowtrans(T->dKP): cgetg(1,t_VEC);
+  return nf;
+}
+
+static GEN
+primes_certify(GEN dK, GEN dKP)
+{
+  pari_sp av = avma;
+  long i, l = lg(dKP);
+  GEN v, D = dK;
+  v = vectrunc_init(l);
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(dKP,i);
+    if (!isprime(p)) vectrunc_append(v, p);
+    (void)Z_pvalrem(D, p, &D);
+  }
+  if (!is_pm1(D))
+  {
+    if (signe(D) < 0) D = negi(D);
+    if (!isprime(D)) vectrunc_append(v, D);
+  }
+  fixlg(v, lg(v)); return gerepilecopy(av, v);
+}
+GEN
+nfcertify(GEN nf)
+{
+  nf = checknf(nf);
+  return primes_certify(nf_get_disc(nf),gmael(nf, 5, 8));
+}
+
+#if 0 /* used to check benches between HNF nf.zk and LLL-reduced nf.zk */
+static GEN
+hnffromLLL(GEN nf)
+{
+  GEN d, x;
+  x = RgV_to_RgM(nf_get_zk(nf), nf_get_degree(nf));
+  x = Q_remove_denom(x, &d);
+  if (!d) return x; /* power basis */
+  return RgM_solve(ZM_hnfmodid(x, d), x);
+}
+
+static GEN
+nfbasechange(GEN u, GEN x)
+{
+  long i,lx;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_COL: /* nfelt */
+      return RgM_RgC_mul(u, x);
+
+    case t_MAT: /* ideal */
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = RgM_RgC_mul(u, gel(x,i));
+      break;
+
+    case t_VEC: /* pr */
+      checkprid(x); y = leafcopy(x);
+      gel(y,2) = RgM_RgC_mul(u, gel(y,2));
+      gel(y,5) = RgM_RgC_mul(u, gel(y,5));
+      break;
+    default: y = x;
+  }
+  return y;
+}
+
+GEN
+nffromhnfbasis(GEN nf, GEN x)
+{
+  long tx = typ(x);
+  pari_sp av = avma;
+  GEN u;
+  if (!is_vec_t(tx)) return gcopy(x);
+  nf = checknf(nf);
+  u = hnffromLLL(nf);
+  return gerepilecopy(av, nfbasechange(u, x));
+}
+
+GEN
+nftohnfbasis(GEN nf, GEN x)
+{
+  long tx = typ(x);
+  pari_sp av = avma;
+  GEN u;
+  if (!is_vec_t(tx)) return gcopy(x);
+  nf = checknf(nf);
+  u = ZM_inv(hnffromLLL(nf), gen_1);
+  return gerepilecopy(av, nfbasechange(u, x));
+}
+#endif
+
+/* set *pro to roots of T->x */
+static GEN
+get_red_G(nfbasic_t *T, GEN *pro)
+{
+  GEN G, u, u0 = NULL;
+  pari_sp av;
+  long i, prec, n = degpol(T->x);
+  nffp_t F;
+
+  prec = nbits2prec(n+32);
+  nffp_init(&F, T, NULL, prec);
+  av = avma;
+  for (i=1; ; i++)
+  {
+    F.prec = prec; make_M_G(&F, 0); G = F.G;
+    if (u0) G = RgM_mul(G, u0);
+    if (DEBUGLEVEL)
+      err_printf("get_red_G: starting LLL, prec = %ld (%ld + %ld)\n",
+                  prec + F.extraprec, prec, F.extraprec);
+    if ((u = lllfp(G, 0.99, LLL_KEEP_FIRST)))
+    {
+      if (lg(u)-1 == n) break;
+      /* singular ==> loss of accuracy */
+      if (u0) u0 = gerepileupto(av, RgM_mul(u0,u));
+      else    u0 = gerepilecopy(av, u);
+    }
+    prec = precdbl(prec) + nbits2extraprec(gexpo(u0));
+    F.ro = NULL;
+    if (DEBUGLEVEL) pari_warn(warnprec,"get_red_G", prec);
+  }
+  if (u0) u = RgM_mul(u0,u);
+  *pro = F.ro; return u;
+}
+
+/* Compute an LLL-reduced basis for the integer basis of nf(T).
+ * set *pro = roots of x if computed [NULL if not computed] */
+static void
+set_LLL_basis(nfbasic_t *T, GEN *pro, double DELTA)
+{
+  GEN B = T->bas;
+  if (T->r1 == degpol(T->x)) {
+    pari_sp av = avma;
+    GEN u, basden = T->basden;
+    if (!basden) basden = get_bas_den(B);
+    u = ZM_lll(make_Tr(T->x,basden), DELTA, LLL_GRAM|LLL_KEEP_FIRST|LLL_IM);
+    B = gerepileupto(av, RgV_RgM_mul(B, u));
+    *pro = NULL;
+  }
+  else
+    B = RgV_RgM_mul(B, get_red_G(T, pro));
+  T->bas = B;
+  T->basden = get_bas_den(B);
+}
+
+static int
+cmp_abs_ZX(GEN x, GEN y) { return gen_cmp_RgX((void*)&absi_cmp, x, y); }
+/* current best: ZX x of discriminant *dx, is ZX y better than x ?
+ * (if so update *dx) */
+static int
+ZX_is_better(GEN y, GEN x, GEN *dx)
+{
+  GEN d = ZX_disc(y);
+  int cmp;
+  if (!*dx) *dx = ZX_disc(x);
+  cmp = absi_cmp(d, *dx);
+  if (cmp < 0) { *dx = d; return 1; }
+  if (cmp == 0) return cmp_abs_ZX(y, x) < 0;
+  return 0;
+}
+
+static void polredbest_aux(nfbasic_t *T, GEN *pro, GEN *px, GEN *pdx, GEN *pa);
+/* Seek a simpler, polynomial pol defining the same number field as
+ * x (assumed to be monic at this point) */
+static GEN
+nfpolred(nfbasic_t *T, GEN *pro)
+{
+  GEN x = T->x, dx, b, rev;
+  long n = degpol(x), v = varn(x);
+
+  if (n == 1) {
+    T->x = deg1pol_shallow(gen_1, gen_m1, v);
+    *pro = NULL; return pol_1(v);
+  }
+  polredbest_aux(T, pro, &x, &dx, &b);
+  if (x == T->x) return NULL; /* no improvement */
+  if (DEBUGLEVEL>1) err_printf("xbest = %Ps\n",x);
+
+  /* update T */
+  rev = QXQ_reverse(b, T->x);
+  T->bas = QXV_QXQ_eval(T->bas, rev, x);
+  (void)Z_issquareall(diviiexact(dx,T->dK), &(T->index));
+  T->basden = get_bas_den(T->bas);
+  T->dx = dx;
+  T->x = x;
+  *pro = NULL; /* reset */
+  return rev;
+}
+
+/* let bas a t_VEC of QX giving a Z-basis of O_K. Return the index of the
+ * basis. Assume bas[1] is 1 and that the leading coefficient of elements
+ * of bas are of the form 1/b for a t_INT b */
+GEN
+get_nfindex(GEN bas)
+{
+  pari_sp av = avma;
+  long n = lg(bas)-1, i;
+  GEN D, d, mat;
+
+  D = gen_1; /* assume bas[1] = 1 */
+  for (i = 2; i <= n; i++)
+  { /* in most cases [e.g after nfbasis] basis is upper triangular! */
+    GEN B = gel(bas,i), lc;
+    if (degpol(B) != i-1) break;
+    lc = gel(B, i+1);
+    switch (typ(lc))
+    {
+      case t_INT: continue;
+      case t_FRAC: lc = gel(lc,2); break;
+      default: pari_err_TYPE("get_nfindex",lc);
+    }
+    D = mulii(D, lc);
+  }
+  if (i <= n)
+  { /* not triangular after all */
+    bas = Q_remove_denom(bas, &d);
+    if (!d) { avma = av; return D; }
+    mat = RgV_to_RgM(bas, n);
+    d = diviiexact(powiu(d, n), ZM_det(mat));
+    D = mulii(D,absi(d));
+  }
+  return gerepileuptoint(av, D);
+}
+
+/* Either nf type or ZX or [monic ZX, data], where data is either an integral
+ * basis (deprecated), or listP data (nfbasis input format) to specify
+ * a set of primes at with the basis order must be maximal.
+ * 1) nf type (or unrecognized): return t_VEC
+ * 2) ZX or [ZX, listP]: return t_POL
+ * 3) [ZX, order basis]: return 0 (deprecated)
+ * incorrect: return -1 */
+static long
+nf_input_type(GEN x)
+{
+  GEN T, V;
+  long i, d, v;
+  switch(typ(x))
+  {
+    case t_POL: return t_POL;
+    case t_VEC:
+      if (lg(x) != 3) return t_VEC; /* nf or incorrect */
+      T = gel(x,1); V = gel(x,2);
+      if (typ(T) != t_POL) return -1;
+      switch(typ(V))
+      {
+        case t_INT: case t_MAT: return t_POL;
+        case t_VEC:
+          if (RgV_is_ZV(V)) return t_POL;
+          break;
+        default: return -1;
+      }
+      d = degpol(T); v = varn(T);
+      if (d<1 || !RgX_is_ZX(T) || !isint1(gel(T,d+2)) || lg(V)-1!=d) return -1;
+      for (i = 1; i <= d; i++)
+      { /* check integer basis */
+        GEN c = gel(V,i);
+        switch(typ(c))
+        {
+          case t_INT: break;
+          case t_POL: if (varn(c) == v && RgX_is_QX(c) && degpol(c) < d) break;
+          /* fall through */
+          default: return -1;
+        }
+      }
+      return 0;
+  }
+  return t_VEC; /* nf or incorrect */
+}
+
+static void
+nfbasic_add_disc(nfbasic_t *T)
+{
+  if (!T->index) T->index = get_nfindex(T->bas);
+  if (!T->dx) T->dx = ZX_disc(T->x);
+  if (!T->dK) T->dK = diviiexact(T->dx, sqri(T->index));
+}
+
+static void
+nfbasic_init(GEN x, long flag, nfbasic_t *T)
+{
+  GEN bas, dK, dx, index, unscale = gen_1;
+  long r1;
+
+  T->dKP = NULL;
+  switch (nf_input_type(x))
+  {
+    case t_POL:
+    {
+      nfmaxord_t S;
+      nfmaxord(&S, x, flag);
+      x = S.T;
+      T->x0 = S.T0;
+      T->dKP = S.dKP;
+      dK = S.dK;
+      index = S.index;
+      bas = S.basis;
+      dx = S.dT;
+      unscale = S.unscale;
+      r1 = sturm(x);
+      break;
+    }
+    case t_VEC:
+    { /* nf, bnf, bnr */
+      GEN nf = checknf(x);
+      x     = nf_get_pol(nf);
+      dK    = nf_get_disc(nf);
+      index = nf_get_index(nf);
+      bas   = nf_get_zk(nf);
+      T->x0 = x;
+      dx = NULL;
+      r1 = nf_get_r1(nf);
+      break;
+    }
+    case 0: /* monic integral polynomial + integer basis */
+      bas = gel(x,2); x = gel(x,1);
+      T->x0 = x;
+      index = NULL;
+      dx = NULL;
+      dK = NULL;
+      r1 = sturm(x);
+      break;
+    default: /* -1 */
+      pari_err_TYPE("nfbasic_init", x);
+      return;
+  }
+  T->x     = x;
+  T->unscale = unscale;
+  T->r1    = r1;
+  T->dx    = dx;
+  T->dK    = dK;
+  T->bas   = bas;
+  T->basden= NULL;
+  T->index = index;
+}
+
+/* Initialize the number field defined by the polynomial x (in variable v)
+ * flag & nf_RED:     try a polred first.
+ * flag & nf_ORIG
+ *    do a polred and return [nfinit(x), Mod(a,red)], where
+ *    Mod(a,red) = Mod(v,x) (i.e return the base change). */
+GEN
+nfinitall(GEN x, long flag, long prec)
+{
+  const pari_sp av = avma;
+  GEN nf, unscale;
+  nfbasic_t T;
+
+  nfbasic_init(x, flag, &T);
+  if (!ZX_is_irred(T.x)) pari_err_IRREDPOL("nfinit",x);
+  if (!equali1(leading_term(T.x0)) && !(flag & nf_RED))
+  {
+    pari_warn(warner,"non-monic polynomial. Result of the form [nf,c]");
+    flag |= nf_RED | nf_ORIG;
+  }
+  unscale = T.unscale;
+  if (!(flag & nf_RED) && !isint1(unscale))
+  { /* implies lc(x0) = 1 and L := 1/unscale is integral */
+    long d = degpol(T.x0);
+    GEN L = ginv(unscale); /* x = L^(-deg(x)) x0(L X) */
+    GEN f= powiu(L, (d*(d-1)) >> 1);
+    T.x = T.x0; /* restore original user-supplied x0, unscale data */
+    T.unscale = gen_1;
+    T.dx    = gmul(T.dx, sqri(f));
+    T.bas   = RgXV_unscale(T.bas, unscale);
+    T.index = gmul(T.index, f);
+  }
+  nfbasic_add_disc(&T); /* more expensive after set_LLL_basis */
+  if (flag & nf_RED)
+  {
+    GEN ro, rev;
+    /* lie to polred: more efficient to update *after* modreverse, than to
+     * unscale in the polred subsystem */
+    T.unscale = gen_1;
+    rev = nfpolred(&T, &ro);
+    nf = nfbasic_to_nf(&T, ro, prec);
+    if (flag & nf_ORIG)
+    {
+      if (!rev) rev = pol_x(varn(T.x)); /* no improvement */
+      if (!isint1(unscale)) rev = RgX_Rg_div(rev, unscale);
+      nf = mkvec2(nf, mkpolmod(rev, T.x));
+    }
+    T.unscale = unscale; /* restore */
+  } else {
+    GEN ro; set_LLL_basis(&T, &ro, 0.99);
+    nf = nfbasic_to_nf(&T, ro, prec);
+  }
+  return gerepilecopy(av, nf);
+}
+
+GEN
+nfinitred(GEN x, long prec)  { return nfinitall(x, nf_RED, prec); }
+GEN
+nfinitred2(GEN x, long prec) { return nfinitall(x, nf_RED|nf_ORIG, prec); }
+GEN
+nfinit(GEN x, long prec)     { return nfinitall(x, 0, prec); }
+
+GEN
+nfinit0(GEN x, long flag,long prec)
+{
+  switch(flag)
+  {
+    case 0:
+    case 1: return nfinitall(x,0,prec);
+    case 2: case 4: return nfinitall(x,nf_RED,prec);
+    case 3: case 5: return nfinitall(x,nf_RED|nf_ORIG,prec);
+    default: pari_err_FLAG("nfinit");
+  }
+  return NULL; /* not reached */
+}
+
+/* assume x a bnr/bnf/nf */
+long
+nf_get_prec(GEN x)
+{
+  GEN nf = checknf(x), ro = nf_get_roots(nf);
+  return (typ(ro)==t_VEC)? precision(gel(ro,1)): DEFAULTPREC;
+}
+
+/* assume nf is an nf */
+GEN
+nfnewprec_shallow(GEN nf, long prec)
+{
+  GEN NF = leafcopy(nf);
+  nffp_t F;
+  gel(NF,5) = leafcopy(gel(NF,5));
+  remake_GM(NF, &F, prec);
+  gel(NF,6) = F.ro;
+  gmael(NF,5,1) = F.M;
+  gmael(NF,5,2) = F.G;
+  return NF;
+}
+
+GEN
+nfnewprec(GEN nf, long prec)
+{
+  GEN z;
+  switch(nftyp(nf))
+  {
+    default: pari_err_TYPE("nfnewprec", nf);
+    case typ_BNF: z = bnfnewprec(nf,prec); break;
+    case typ_BNR: z = bnrnewprec(nf,prec); break;
+    case typ_NF: {
+      pari_sp av = avma;
+      z = gerepilecopy(av, nfnewprec_shallow(checknf(nf), prec));
+      break;
+    }
+  }
+  return z;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           POLRED                               **/
+/**                                                                **/
+/********************************************************************/
+GEN
+embednorm_T2(GEN x, long r1)
+{
+  pari_sp av = avma;
+  GEN p = RgV_sumpart(x, r1);
+  GEN q = RgV_sumpart2(x,r1+1, lg(x)-1);
+  if (q != gen_0) p = gadd(p, gmul2n(q,1));
+  return avma == av? gcopy(p): gerepileupto(av, p);
+}
+
+/* simplified version of gnorm for scalar, non-complex inputs, without GC */
+static GEN
+real_norm(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT:  return sqri(x);
+    case t_REAL: return sqrr(x);
+    case t_FRAC: return sqrfrac(x);
+  }
+  pari_err_TYPE("real_norm", x);
+  return NULL;
+}
+/* simplified version of gnorm, without GC */
+static GEN
+complex_norm(GEN x)
+{
+  return typ(x) == t_COMPLEX? cxnorm(x): real_norm(x);
+}
+/* return T2(x), argument r1 needed in case x has components whose type
+ * is unexpected, e.g. all of them t_INT for embed(gen_1) */
+GEN
+embed_T2(GEN x, long r1)
+{
+  pari_sp av = avma;
+  long i, l = lg(x);
+  GEN c, s = NULL, t = NULL;
+  if (typ(gel(x,1)) == t_INT) return muliu(gel(x,1), 2*(l-1)-r1);
+  for (i = 1; i <= r1; i++)
+  {
+    c = real_norm(gel(x,i));
+    s = s? gadd(s, c): c;
+  }
+  for (; i < l; i++)
+  {
+    c = complex_norm(gel(x,i));
+    t = t? gadd(t, c): c;
+  }
+  if (t) { t = gmul2n(t,1); s = s? gadd(s,t): t; }
+  return gerepileupto(av, s);
+}
+/* return N(x) */
+GEN
+embed_norm(GEN x, long r1)
+{
+  pari_sp av = avma;
+  long i, l = lg(x);
+  GEN c, s = NULL, t = NULL;
+  if (typ(gel(x,1)) == t_INT) return powiu(gel(x,1), 2*(l-1)-r1);
+  for (i = 1; i <= r1; i++)
+  {
+    c = gel(x,i);
+    s = s? gmul(s, c): c;
+  }
+  for (; i < l; i++)
+  {
+    c = complex_norm(gel(x,i));
+    t = t? gmul(t, c): c;
+  }
+  if (t) s = s? gmul(s,t): t;
+  return gerepileupto(av, s);
+}
+
+typedef struct {
+  long r1, v, prec;
+  GEN ZKembed; /* embeddings of fincke-pohst-reduced Zk basis */
+  GEN u; /* matrix giving fincke-pohst-reduced Zk basis */
+  GEN M; /* embeddings of initial (LLL-reduced) Zk basis */
+  GEN bound; /* T2 norm of the polynomial defining nf */
+  long expo_best_disc; /* expo(disc(x)), best generator so far */
+} CG_data;
+
+/* characteristic pol of x (given by embeddings) */
+static GEN
+get_pol(CG_data *d, GEN x)
+{
+  long e;
+  GEN g = grndtoi(roots_to_pol_r1(x, d->v, d->r1), &e);
+  return (e > -5)? NULL: g;
+}
+
+/* characteristic pol of x (given as vector on (w_i)) */
+static GEN
+get_polchar(CG_data *d, GEN x)
+{ return get_pol(d, RgM_RgC_mul(d->ZKembed,x)); }
+
+/* Choose a canonical polynomial in the pair { z(X), (+/-)z(-X) }.
+ * z a ZX with lc(z) > 0. We want to keep that property, while
+ * ensuring that the leading coeff of the odd (resp. even) part of z is < 0
+ * if deg z is even (resp. odd).
+ * Either leave z alone (return 1) or set z <-- (-1)^deg(z) z(-X). In place. */
+static int
+ZX_canon_neg(GEN z)
+{
+  long i,s;
+
+  for (i = lg(z)-2; i >= 2; i -= 2)
+  { /* examine the odd (resp. even) part of z if deg(z) even (resp. odd). */
+    s = signe(gel(z,i));
+    if (!s) continue;
+    /* non trivial */
+    if (s < 0) break; /* the condition is already satisfied */
+
+    for (; i>=2; i-=2) gel(z,i) = negi(gel(z,i));
+    return 1;
+  }
+  return 0;
+}
+/* return a defining polynomial for Q(alpha), v = embeddings of alpha.
+ * Return NULL on failure: discriminant too large or non primitive */
+static GEN
+try_polmin(CG_data *d, nfbasic_t *T, GEN v, long flag, GEN *ai)
+{
+  const long best = flag & nf_ABSOLUTE;
+  long ed;
+  pari_sp av = avma;
+  GEN g;
+  if (best)
+  {
+    ed = expo(embed_disc(v, d->r1, LOWDEFAULTPREC));
+    avma = av; if (d->expo_best_disc < ed) return NULL;
+  }
+  else
+    ed = 0;
+  g = get_pol(d, v);
+  /* accuracy too low, compute algebraically */
+  if (!g) { avma = av; g = ZXQ_charpoly(*ai, T->x, varn(T->x)); }
+  (void)ZX_gcd_all(g, ZX_deriv(g), &g);
+  if (best && degpol(g) != degpol(T->x)) { avma = av; return NULL; }
+  g = gerepilecopy(av, g);
+  d->expo_best_disc = ed;
+  if (flag & nf_ORIG)
+  {
+    if (ZX_canon_neg(g)) *ai = RgX_neg(*ai);
+    if (!isint1(T->unscale)) *ai = RgX_unscale(*ai, T->unscale);
+  }
+  else
+    (void)ZX_canon_neg(g);
+  if (DEBUGLEVEL>3) err_printf("polred: generator %Ps\n", g);
+  return g;
+}
+
+/* does x generate the correct field ? */
+static GEN
+chk_gen(void *data, GEN x)
+{
+  pari_sp av = avma, av1;
+  GEN h, g = get_polchar((CG_data*)data,x);
+  if (!g) pari_err_PREC("chk_gen");
+  av1 = avma;
+  h = ZX_gcd(g, ZX_deriv(g));
+  if (degpol(h)) { avma = av; return NULL; }
+  if (DEBUGLEVEL>3) err_printf("  generator: %Ps\n",g);
+  avma = av1; return gerepileupto(av, g);
+}
+
+static long
+chk_gen_prec(long N, long bit)
+{ return nbits2prec(10 + (long)log2((double)N) + bit); }
+
+/* Remove duplicate polynomials in P, updating A (same indices), in place.
+ * Among elements having the same characteristic pol, choose the smallest
+ * according to ZV_abscmp */
+static void
+remove_duplicates(GEN P, GEN A)
+{
+  long k, i, l = lg(P);
+  pari_sp av = avma;
+  GEN x, a;
+
+  if (l < 2) return;
+  (void)sort_factor_pol(mkmat2(P, A), cmpii);
+  x = gel(P,1); a = gel(A,1);
+  for  (k=1,i=2; i<l; i++)
+    if (ZX_equal(gel(P,i), x))
+    {
+      if (ZV_abscmp(gel(A,i), a) < 0) a = gel(A,i);
+    }
+    else
+    {
+      gel(A,k) = a;
+      gel(P,k) = x;
+      k++;
+      x = gel(P,i); a = gel(A,i);
+    }
+  gel(A,k) = a;
+  gel(P,k) = x;
+  l = k+1; setlg(A,l); setlg(P,l);
+  avma = av;
+}
+
+static long
+polred_init(nfbasic_t *T, nffp_t *F, CG_data *d)
+{
+  long e, prec, n = degpol(T->x);
+  double log2rho;
+  GEN ro;
+  set_LLL_basis(T, &ro, 0.9999);
+  /* || polchar ||_oo < 2^e ~ 2 (n * rho)^n, rho = max modulus of root */
+  log2rho = ro ? (double)gexpo(ro): cauchy_bound(T->x) / LOG2;
+  e = n * (long)(log2rho + log2((double)n)) + 1;
+  if (e < 0) e = 0; /* can occur if n = 1 */
+  prec = chk_gen_prec(n, e);
+  get_nf_fp_compo(T, F, ro, 1, prec);
+  d->v = varn(T->x);
+  d->expo_best_disc = -1;
+  d->ZKembed = NULL;
+  d->M = NULL;
+  d->u = NULL;
+  d->r1= T->r1; return prec;
+}
+static GEN
+findmindisc(GEN y, GEN *pa)
+{
+  GEN a = *pa, x = gel(y,1), b = gel(a,1), dx = NULL;
+  long i, l = lg(y);
+  for (i = 2; i < l; i++)
+  {
+    GEN yi = gel(y,i);
+    if (ZX_is_better(yi,x,&dx)) { x = yi; b = gel(a,i); }
+  }
+  *pa = b; return x;
+}
+/* filter [y,b] from polred_aux: keep a single polynomial of degree n in y
+ * [ the best wrt discriminant ordering ], but keep all non-primitive
+ * polynomials */
+static void
+filter(GEN y, GEN b, long n)
+{
+  GEN x, a, dx;
+  long i, k = 1, l = lg(y);
+  a = x = dx = NULL;
+  for (i = 1; i < l; i++)
+  {
+    GEN yi = gel(y,i), ai = gel(b,i);
+    if (degpol(yi) == n)
+    {
+      pari_sp av = avma;
+      if (dx && !ZX_is_better(yi,x,&dx)) { avma = av; continue; }
+      if (!dx) dx = ZX_disc(yi);
+      x = yi; a = ai; continue;
+    }
+    gel(y,k) = yi;
+    gel(b,k) = ai; k++;
+  }
+  if (dx)
+  {
+    gel(y,k) = x;
+    gel(b,k) = a; k++;
+  }
+  setlg(y, k);
+  setlg(b, k);
+}
+
+static GEN
+polred_aux(nfbasic_t *T, GEN *pro, long flag)
+{ /* only keep polynomials of max degree and best discriminant */
+  const long best = flag & nf_ABSOLUTE;
+  const long orig = flag & nf_ORIG;
+  GEN M, b, y, x = T->x;
+  long maxi, i, j, k, v = varn(x), n = lg(T->bas)-1;
+  nffp_t F;
+  CG_data d;
+
+  if (n == 1)
+  {
+    if (!best)
+    {
+      GEN ch = deg1pol_shallow(gen_1, gen_m1, v);
+      return orig? mkmat2(mkcol(ch),mkcol(gen_1)): mkvec(ch);
+    }
+    else
+      return orig? trivial_fact(): cgetg(1,t_VEC);
+  }
+
+  (void)polred_init(T, &F, &d);
+  *pro = F.ro;
+  M = F.M;
+  if (best)
+  {
+    if (!T->dx) T->dx = ZX_disc(T->x);
+    d.expo_best_disc = expi(T->dx);
+  }
+
+  /* n + 2 sum_{1 <= i <= n} n-i = n + n(n-1) = n*n */
+  y = cgetg(n*n + 1, t_VEC);
+  b = cgetg(n*n + 1, t_COL);
+  k = 1;
+  if (!best)
+  {
+    GEN ch = deg1pol_shallow(gen_1, gen_m1, v);
+    gel(y,1) = ch; gel(b,1) = gen_1; k++;
+  }
+  for (i = 2; i <= n; i++)
+  {
+    GEN ch, ai;
+    ai = gel(T->bas,i);
+    ch = try_polmin(&d, T, gel(M,i), flag, &ai);
+    if (ch) { gel(y,k) = ch; gel(b,k) = ai; k++; }
+  }
+  maxi = minss(n, 3);
+  for (i = 1; i <= maxi; i++)
+    for (j = i+1; j <= n; j++)
+    {
+      GEN ch, ai, v;
+      ai = gadd(gel(T->bas,i), gel(T->bas,j));
+      v = RgV_add(gel(M,i), gel(M,j));
+      /* defining polynomial for Q(w_i+w_j) */
+      ch = try_polmin(&d, T, v, flag, &ai);
+      if (ch) { gel(y,k) = ch; gel(b,k) = ai; k++; }
+
+      ai = gsub(gel(T->bas,i), gel(T->bas,j));
+      v = RgV_sub(gel(M,i), gel(M,j));
+      /* defining polynomial for Q(w_i-w_j) */
+      ch = try_polmin(&d, T, v, flag, &ai);
+      if (ch) { gel(y,k) = ch; gel(b,k) = ai; k++; }
+    }
+  setlg(y, k);
+  setlg(b, k); filter(y, b, n);
+  if (!orig) return gen_sort_uniq(y, (void*)cmpii, &gen_cmp_RgX);
+  (void)sort_factor_pol(mkmat2(y, b), cmpii);
+  settyp(y, t_COL); return mkmat2(b, y);
+}
+
+static GEN
+Polred(GEN x, long flag, GEN fa)
+{
+  pari_sp av = avma;
+  GEN ro;
+  nfbasic_t T; nfbasic_init(fa? mkvec2(x,fa): x, flag & nf_PARTIALFACT, &T);
+  return gerepilecopy(av, polred_aux(&T, &ro, flag));
+}
+
+/* finds "best" polynomial in polred_aux list, defaulting to T->x if none of
+ * them is primitive. *px is the ZX, characteristic polynomial of Mod(*pb,T->x),
+ * *pdx its discriminant. Set *pro = polroots(T->x) [ NOT *px ]. */
+static void
+polredbest_aux(nfbasic_t *T, GEN *pro, GEN *px, GEN *pdx, GEN *pb)
+{
+  GEN y, x = T->x; /* default value */
+  long i, l;
+  y = polred_aux(T, pro, pb? nf_ORIG|nf_ABSOLUTE: nf_ABSOLUTE);
+  *pdx = T->dx;
+  if (pb)
+  {
+    GEN a, b = deg1pol_shallow(T->unscale, gen_0, varn(x));
+    a = gel(y,1); l = lg(a);
+    y = gel(y,2);
+    for (i=1; i<l; i++)
+    {
+      GEN yi = gel(y,i);
+      pari_sp av = avma;
+      if (ZX_is_better(yi,x,pdx)) { x = yi; b = gel(a,i); } else avma = av;
+    }
+    *pb = b;
+  }
+  else
+  {
+    l = lg(y);
+    for (i=1; i<l; i++)
+    {
+      GEN yi = gel(y,i);
+      pari_sp av = avma;
+      if (ZX_is_better(yi,x,pdx)) x = yi; else avma = av;
+    }
+  }
+  if (!*pdx) *pdx = ZX_disc(x);
+  *px = x;
+}
+GEN
+polredbest(GEN T0, long flag)
+{
+  pari_sp av = avma;
+  GEN T, dT, ro, a;
+  nfbasic_t S;
+  if (flag < 0 || flag > 1) pari_err_FLAG("polredbest");
+  T = T0; nfbasic_init(T, nf_PARTIALFACT, &S);
+  polredbest_aux(&S, &ro, &T, &dT, flag? &a: NULL);
+  if (flag)
+  { /* charpoly(Mod(a,T0)) = T */
+    GEN b;
+    if (T0 == T)
+      b = pol_x(varn(T)); /* no improvement */
+    else
+      b = QXQ_reverse(a, T0); /* charpoly(Mod(b,T)) = S.x */
+    b = (degpol(T) == 1)? gmodulo(b, T): mkpolmod(b,T);
+    T = mkvec2(T, b);
+  }
+  return gerepilecopy(av, T);
+}
+/* DEPRECATED: backward compatibility */
+GEN
+polred0(GEN x, long flag, GEN fa)
+{
+  long fl = 0;
+  if (flag & 1) fl |= nf_PARTIALFACT;
+  if (flag & 2) fl |= nf_ORIG;
+  return Polred(x, fl, fa);
+}
+
+GEN
+polredord(GEN x)
+{
+  pari_sp av = avma;
+  GEN v, lt;
+  long i, n, vx;
+
+  if (typ(x) != t_POL) pari_err_TYPE("polredord",x);
+  x = Q_primpart(x); RgX_check_ZX(x,"polredord");
+  n = degpol(x); if (n <= 0) pari_err_CONSTPOL("polredord");
+  if (n == 1) return gerepilecopy(av, mkvec(x));
+  lt = leading_term(x); vx = varn(x);
+  if (is_pm1(lt))
+  {
+    if (signe(lt) < 0) x = ZX_neg(x);
+    v = pol_x_powers(n, vx);
+  }
+  else
+  { GEN L;
+    /* basis for Dedekind order */
+    v = cgetg(n+1, t_VEC);
+    gel(v,1) = scalarpol_shallow(lt, vx);
+    for (i = 2; i <= n; i++)
+      gel(v,i) = RgX_Rg_add(RgX_mulXn(gel(v,i-1), 1), gel(x,n+3-i));
+    gel(v,1) = pol_1(vx);
+    x = ZX_Q_normalize(x, &L);
+    v = gsubst(v, vx, monomial(ginv(L),1,vx));
+    for (i=2; i <= n; i++)
+      if (Q_denom(gel(v,i)) == gen_1) gel(v,i) = monomial(gen_1, i-1, vx);
+  }
+  return gerepileupto(av, polred(mkvec2(x, v)));
+}
+
+GEN
+polred(GEN x) { return Polred(x, 0, NULL); }
+GEN
+smallpolred(GEN x) { return Polred(x, nf_PARTIALFACT, NULL); }
+GEN
+factoredpolred(GEN x, GEN fa) { return Polred(x, 0, fa); }
+GEN
+polred2(GEN x) { return Polred(x, nf_ORIG, NULL); }
+GEN
+smallpolred2(GEN x) { return Polred(x, nf_PARTIALFACT|nf_ORIG, NULL); }
+GEN
+factoredpolred2(GEN x, GEN fa) { return Polred(x, nf_PARTIALFACT, fa); }
+
+/********************************************************************/
+/**                                                                **/
+/**                           POLREDABS                            **/
+/**                                                                **/
+/********************************************************************/
+/* set V[k] := matrix of multiplication by nk.zk[k] */
+static GEN
+set_mulid(GEN V, GEN M, GEN Mi, long r1, long r2, long N, long k)
+{
+  GEN v, Mk = cgetg(N+1, t_MAT);
+  long i, e;
+  for (i = 1; i < k; i++) gel(Mk,i) = gmael(V, i, k);
+  for (     ; i <=N; i++)
+  {
+    v = vecmul(gel(M,k), gel(M,i));
+    v = RgM_RgC_mul(Mi, split_realimag(v, r1, r2));
+    gel(Mk,i) = grndtoi(v, &e);
+    if (e > -5) return NULL;
+  }
+  gel(V,k) = Mk; return Mk;
+}
+
+static GEN
+ZM_image_shallow(GEN M, long *pr)
+{
+  long j, k, r;
+  GEN y, d = ZM_pivots(M, &k);
+  r = lg(M)-1 - k;
+  y = cgetg(r+1,t_MAT);
+  for (j=k=1; j<=r; k++)
+    if (d[k]) gel(y,j++) = gel(M,k);
+  *pr = r; return y;
+}
+
+/* U = base change matrix, R = Cholesky form of the quadratic form [matrix
+ * Q from algo 2.7.6] */
+static GEN
+chk_gen_init(FP_chk_fun *chk, GEN R, GEN U)
+{
+  CG_data *d = (CG_data*)chk->data;
+  GEN P, V, D, inv, bound, S, M;
+  long N = lg(U)-1, r1 = d->r1, r2 = (N-r1)>>1;
+  long i, j, prec, firstprim = 0, skipfirst = 0;
+  pari_sp av;
+
+  d->u = U;
+  d->ZKembed = M = RgM_mul(d->M, U);
+
+  av = avma; bound = d->bound;
+  D = cgetg(N+1, t_VECSMALL);
+  for (i = 1; i <= N; i++)
+  {
+    pari_sp av2 = avma;
+    P = get_pol(d, gel(M,i));
+    if (!P) pari_err_PREC("chk_gen_init");
+    (void)ZX_gcd_all(P, ZX_deriv(P), &P);
+    P = gerepilecopy(av2, P);
+    D[i] = degpol(P);
+    if (D[i] == N)
+    { /* primitive element */
+      GEN B = embed_T2(gel(M,i), r1);
+      if (!firstprim) firstprim = i; /* index of first primitive element */
+      if (DEBUGLEVEL>2) err_printf("chk_gen_init: generator %Ps\n",P);
+      if (gcmp(B,bound) < 0) bound = gerepileuptoleaf(av2, B);
+    }
+    else
+    {
+      if (DEBUGLEVEL>2) err_printf("chk_gen_init: subfield %Ps\n",P);
+      if (firstprim)
+      { /* cycle basis vectors so that primitive elements come last */
+        GEN u = d->u, e = M;
+        GEN te = gel(e,i), tu = gel(u,i), tR = gel(R,i);
+        long tS = D[i];
+        for (j = i; j > firstprim; j--)
+        {
+          u[j] = u[j-1];
+          e[j] = e[j-1];
+          R[j] = R[j-1];
+          D[j] = D[j-1];
+        }
+        gel(u,firstprim) = tu;
+        gel(e,firstprim) = te;
+        gel(R,firstprim) = tR;
+        D[firstprim] = tS; firstprim++;
+      }
+    }
+  }
+  if (!firstprim)
+  { /* try (a little) to find primitive elements to improve bound */
+    GEN x = cgetg(N+1, t_VECSMALL);
+    if (DEBUGLEVEL>1)
+      err_printf("chk_gen_init: difficult field, trying random elements\n");
+    for (i = 0; i < 10; i++)
+    {
+      GEN e, B;
+      for (j = 1; j <= N; j++) x[j] = (long)random_Fl(7) - 3;
+      e = RgM_zc_mul(M, x);
+      B = embed_T2(e, r1);
+      if (gcmp(B,bound) >= 0) continue;
+      P = get_pol(d, e); if (!P) pari_err_PREC( "chk_gen_init");
+      if (!ZX_is_squarefree(P)) continue;
+      if (DEBUGLEVEL>2) err_printf("chk_gen_init: generator %Ps\n",P);
+      bound = B ;
+    }
+  }
+
+  if (firstprim != 1)
+  {
+    inv = ginv( split_realimag(M, r1, r2) ); /*TODO: use QR?*/
+    V = gel(inv,1);
+    for (i = 2; i <= r1+r2; i++) V = gadd(V, gel(inv,i));
+    /* V corresponds to 1_Z */
+    V = grndtoi(V, &j);
+    if (j > -5) pari_err_BUG("precision too low in chk_gen_init");
+    S = mkmat(V); /* 1 */
+
+    V = cgetg(N+1, t_VEC);
+    for (i = 1; i <= N; i++,skipfirst++)
+    { /* S = Q-basis of subfield generated by nf.zk[1..i-1] */
+      GEN Mx, M2;
+      long j, k, h, rkM, dP = D[i];
+
+      if (dP == N) break; /* primitive */
+      Mx = set_mulid(V, M, inv, r1, r2, N, i);
+      if (!Mx) break; /* prec. problem. Stop */
+      if (dP == 1) continue;
+      rkM = lg(S)-1;
+      M2 = cgetg(N+1, t_MAT); /* we will add to S the elts of M2 */
+      gel(M2,1) = col_ei(N, i); /* nf.zk[i] */
+      k = 2;
+      for (h = 1; h < dP; h++)
+      {
+        long r; /* add to M2 the elts of S * nf.zk[i]  */
+        for (j = 1; j <= rkM; j++) gel(M2,k++) = ZM_ZC_mul(Mx, gel(S,j));
+        setlg(M2, k); k = 1;
+        S = ZM_image_shallow(shallowconcat(S,M2), &r);
+        if (r == rkM) break;
+        if (r > rkM)
+        {
+          rkM = r;
+          if (rkM == N) break;
+        }
+      }
+      if (rkM == N) break;
+      /* Q(w[1],...,w[i-1]) is a strict subfield of nf */
+    }
+  }
+  /* x_1,...,x_skipfirst generate a strict subfield [unless N=skipfirst=1] */
+  chk->skipfirst = skipfirst;
+  if (DEBUGLEVEL>2) err_printf("chk_gen_init: skipfirst = %ld\n",skipfirst);
+
+  /* should be DEF + gexpo( max_k C^n_k (bound/k)^(k/2) ) */
+  bound = gerepileuptoleaf(av, bound);
+  prec = chk_gen_prec(N, (gexpo(bound)*N)/2);
+  if (DEBUGLEVEL)
+    err_printf("chk_gen_init: new prec = %ld (initially %ld)\n", prec, d->prec);
+  if (prec > d->prec) pari_err_BUG("polredabs (precision problem)");
+  if (prec < d->prec) d->ZKembed = gprec_w(M, prec);
+  return bound;
+}
+
+/* z "small" minimal polynomial of Mod(a,x), deg z = deg x */
+static GEN
+store(GEN x, GEN z, GEN a, nfbasic_t *T, long flag, GEN u)
+{
+  GEN y, b;
+
+  if (u) a = RgV_RgC_mul(T->bas, ZM_ZC_mul(u, a));
+  if (flag & (nf_ORIG|nf_ADDZK))
+  {
+    b = QXQ_reverse(a, x);
+    if (!isint1(T->unscale)) b = gdiv(b, T->unscale); /* not RgX_Rg_div */
+  }
+  else
+    b = NULL;
+
+  if (flag & nf_RAW)
+    y = mkvec2(z, a);
+  else if (flag & nf_ORIG) /* store phi(b mod z). */
+    y = mkvec2(z, mkpolmod(b,z));
+  else
+    y = z;
+  if (flag & nf_ADDZK)
+  { /* append integral basis for number field Q[X]/(z) to result */
+    long n = degpol(x);
+    GEN t = RgV_RgM_mul(RgXQ_powers(b, n-1, z), RgV_to_RgM(T->bas,n));
+    y = mkvec2(y, t);
+  }
+  return y;
+}
+static GEN
+polredabs_aux(nfbasic_t *T, GEN *u)
+{
+  long prec;
+  GEN v;
+  FP_chk_fun chk = { &chk_gen, &chk_gen_init, NULL, NULL, 0 };
+  nffp_t F;
+  CG_data d; chk.data = (void*)&d;
+
+  prec = polred_init(T, &F, &d);
+  d.bound = embed_T2(F.ro, d.r1);
+  if (realprec(d.bound) > prec) d.bound = rtor(d.bound, prec);
+  for (;;)
+  {
+    GEN R = R_from_QR(F.G, prec);
+    if (R)
+    {
+      d.prec = prec;
+      d.M    = F.M;
+      v = fincke_pohst(mkvec(R),NULL,-1, 0, &chk);
+      if (v) break;
+    }
+    prec = precdbl(prec);
+    get_nf_fp_compo(T, &F, NULL, 1, prec);
+    if (DEBUGLEVEL) pari_warn(warnprec,"polredabs0",prec);
+  }
+  *u = d.u; return v;
+}
+
+GEN
+polredabs0(GEN x, long flag)
+{
+  pari_sp av = avma;
+  long i, l, vx;
+  GEN y, a, u;
+  nfbasic_t T;
+
+  nfbasic_init(x, flag & nf_PARTIALFACT, &T);
+  x = T.x; vx = varn(x);
+
+  if (degpol(x) == 1)
+  {
+    u = NULL;
+    y = mkvec( pol_x(vx) );
+    a = mkvec( deg1pol_shallow(gen_1, negi(gel(x,2)), vx) );
+    l = 2;
+  }
+  else
+  {
+    GEN v;
+    if (!(flag & nf_PARTIALFACT)
+        && T.dKP && lg(primes_certify(T.dK, T.dKP)) != 1) return gen_0;
+    v = polredabs_aux(&T, &u);
+    y = gel(v,1);
+    a = gel(v,2); l = lg(a);
+    for (i=1; i<l; i++)
+      if (ZX_canon_neg(gel(y,i))) gel(a,i) = ZC_neg(gel(a,i));
+    remove_duplicates(y,a);
+    l = lg(a);
+    if (l == 1)
+      pari_err_BUG("polredabs (missing vector)");
+  }
+  if (DEBUGLEVEL) err_printf("Found %ld minimal polynomials.\n",l-1);
+  if (flag & nf_ALL) {
+    for (i=1; i<l; i++) gel(y,i) = store(x, gel(y,i), gel(a,i), &T, flag, u);
+  } else {
+    GEN z = findmindisc(y, &a);
+    y = store(x, z, a, &T, flag, u);
+  }
+  return gerepilecopy(av, y);
+}
+
+GEN
+polredabsall(GEN x, long flun) { return polredabs0(x, flun | nf_ALL); }
+GEN
+polredabs(GEN x) { return polredabs0(x,0); }
+GEN
+polredabs2(GEN x) { return polredabs0(x,nf_ORIG); }
+
+/* relative polredabs/best. Returns relative polynomial by default (flag = 0)
+ * flag & nf_ORIG: + element (base change)
+ * flag & nf_ABSOLUTE: absolute polynomial */
+static GEN
+rnfpolred_i(GEN nf, GEN relpol, long flag, long best)
+{
+  const char *f = best? "rnfpolredbest": "rnfpolredabs";
+  const long abs = ((flag & nf_ORIG) && (flag & nf_ABSOLUTE));
+  pari_timer ti;
+  GEN listP = NULL, red, bas, A, P, pol, T, rnfeq;
+  long ty = typ(relpol);
+  pari_sp av = avma;
+
+  if (ty == t_VEC) {
+    if (lg(relpol) != 3) pari_err_TYPE(f,relpol);
+    listP = gel(relpol,2);
+    relpol = gel(relpol,1);
+  }
+  if (typ(relpol) != t_POL) pari_err_TYPE(f,relpol);
+  nf = checknf(nf);
+  if (DEBUGLEVEL>1) timer_start(&ti);
+  T = nf_get_pol(nf);
+  relpol = RgX_nffix(f, T, relpol, 0);
+  if (best || (flag & nf_PARTIALFACT))
+  {
+    if (abs)
+    {
+      rnfeq = nf_rnfeq(nf, relpol);
+      pol = gel(rnfeq,1);
+    }
+    else
+    {
+      long sa;
+      pol = rnfequationall(nf, relpol, &sa, NULL);
+      rnfeq = mkvec5(gen_0,gen_0,stoi(sa),T,liftpol_shallow(relpol));
+    }
+    bas = listP? mkvec2(pol, listP): pol;
+    if (best)
+    {
+      if (abs) red = polredbest(bas, 1);
+      else
+      {
+        GEN ro, x, dx, a;
+        nfbasic_t T;
+        nfbasic_init(bas, nf_PARTIALFACT, &T);
+        polredbest_aux(&T, &ro, &x, &dx, &a);
+        red = mkvec2(x, a);
+      }
+    }
+    else
+      red = polredabs0(bas, (abs? nf_ORIG: nf_RAW)|nf_PARTIALFACT);
+  }
+  else
+  {
+    GEN rnf = rnfinit(nf, relpol), M = rnf_basM(rnf);
+    rnfeq = rnf_get_map(rnf);
+    pol = rnf_get_polabs(rnf);
+    bas = mkvec2(pol, RgM_to_RgXV(M, varn(pol)));
+    if (DEBUGLEVEL>1) timer_printf(&ti, "absolute basis");
+    red = polredabs0(bas, nf_RAW);
+  }
+  P = gel(red,1);
+  A = gel(red,2);
+  if (DEBUGLEVEL>1) err_printf("reduced absolute generator: %Ps\n",P);
+  if (flag & nf_ABSOLUTE)
+  {
+    if (flag & nf_ORIG)
+    {
+      GEN a = gel(rnfeq,2); /* Mod(a,pol) root of T */
+      GEN k = gel(rnfeq,3); /* Mod(variable(relpol),relpol) + k*a root of pol */
+      a = RgX_RgXQ_eval(a, lift_intern(A), P); /* Mod(a, P) root of T */
+      P = mkvec3(P, mkpolmod(a,P), gsub(A, gmul(k,a)));
+    }
+    return gerepilecopy(av, P);
+  }
+  A = eltabstorel_lift(rnfeq, A);
+  P = RgXQ_charpoly(A, relpol, varn(relpol));
+  P = lift_if_rational(P);
+  if (flag & nf_ORIG) P = mkvec2(P, mkpolmod(RgXQ_reverse(A,relpol),P));
+  return gerepilecopy(av, P);
+}
+GEN
+rnfpolredabs(GEN nf, GEN relpol, long flag)
+{ return rnfpolred_i(nf,relpol,flag, 0); }
+GEN
+rnfpolredbest(GEN nf, GEN relpol, long flag)
+{
+  if (flag < 0 || flag > 3) pari_err_FLAG("rnfpolredbest");
+  return rnfpolred_i(nf,relpol,flag, 1);
+}
diff --git a/src/basemath/base2.c b/src/basemath/base2.c
new file mode 100644
index 0000000..1875df1
--- /dev/null
+++ b/src/basemath/base2.c
@@ -0,0 +1,3799 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                       MAXIMAL ORDERS                            */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* allow p = -1 from factorizations, avoid oo loop on p = 1 */
+static long
+safe_Z_pvalrem(GEN x, GEN p, GEN *z)
+{
+  if (is_pm1(p))
+  {
+    if (signe(p) > 0) return gvaluation(x,p); /*error*/
+    *z = absi(x); return 1;
+  }
+  return Z_pvalrem(x, p, z);
+}
+/* D an integer, P a ZV, return a factorization matrix for D over P, removing
+ * entries with 0 exponent. */
+static GEN
+fact_from_factors(GEN D, GEN P, long flag)
+{
+  long i, l = lg(P), iq = 1;
+  GEN Q = cgetg(l+1,t_COL);
+  GEN E = cgetg(l+1,t_COL);
+  for (i=1; i<l; i++)
+  {
+    GEN p = gel(P,i);
+    long k;
+    if (flag && !equalim1(p))
+    {
+      p = gcdii(p, D);
+      if (is_pm1(p)) continue;
+    }
+    k = safe_Z_pvalrem(D, p, &D);
+    if (k) { gel(Q,iq) = p; gel(E,iq) = utoipos(k); iq++; }
+  }
+  if (signe(D) < 0) D = absi(D);
+  if (!is_pm1(D))
+  {
+    long k = Z_isanypower(D, &D);
+    gel(Q,iq) = D; gel(E,iq) = utoipos(k ? k: 1);
+  }
+  setlg(Q,iq);
+  setlg(E,iq); return mkmat2(Q,E);
+}
+
+/* d a t_INT; f a t_MAT factorisation of some t_INT sharing some divisors
+ * with d, or a prime (t_INT). Return a factorization F of d: "primes"
+ * entries in f _may_ be composite, and are included as is in d. */
+static GEN
+update_fact(GEN d, GEN f)
+{
+  GEN P;
+  switch (typ(f))
+  {
+    case t_INT: case t_VEC: case t_COL: return f;
+    case t_MAT:
+      if (lg(f) == 3) { P = gel(f,1); break; }
+    /*fall through*/
+    default:
+      pari_err_TYPE("nfbasis [factorization expected]",f);
+      return NULL;
+  }
+  return fact_from_factors(d, P, 1);
+}
+
+/* T = C T0(X/L); C = L^d / lt(T0), d = deg(T)
+ * disc T = C^2(d - 1) L^-(d(d-1)) disc T0 = (L^d / lt(T0)^2)^(d-1) disc T0 */
+static GEN
+set_disc(nfmaxord_t *S)
+{
+  GEN l0, L, dT;
+  long d;
+  if (S->T0 == S->T) return ZX_disc(S->T);
+  d = degpol(S->T0);
+  l0 = leading_term(S->T0);
+  L = S->unscale;
+  if (typ(L) == t_FRAC && absi_cmp(gel(L,1), gel(L,2)) < 0)
+    dT = ZX_disc(S->T); /* more efficient */
+  else
+  {
+    GEN a = gpowgs(gdiv(gpowgs(L, d), sqri(l0)), d-1);
+    dT = gmul(a, ZX_disc(S->T0)); /* more efficient */
+  }
+  return S->dT = dT;
+}
+static void
+nfmaxord_check_args(nfmaxord_t *S, GEN T, long flag)
+{
+  GEN dT, L, E, P, fa = NULL;
+  pari_timer t;
+  long l, ty = typ(T);
+
+  if (DEBUGLEVEL) timer_start(&t);
+  if (ty == t_VEC) {
+    if (lg(T) != 3) pari_err_TYPE("nfmaxord",T);
+    fa = gel(T,2); T = gel(T,1); ty = typ(T);
+  }
+  if (ty != t_POL) pari_err_TYPE("nfmaxord",T);
+  T = Q_primpart(T);
+  if (degpol(T) <= 0) pari_err_CONSTPOL("nfmaxord");
+  RgX_check_ZX(T, "nfmaxord");
+  S->T0 = T;
+  T = ZX_Q_normalize(T, &L);
+  S->unscale = L;
+  S->T = T;
+  S->dT = dT = set_disc(S);
+  if (fa)
+  {
+    if (!isint1(L)) fa = update_fact(dT, fa);
+    switch(typ(fa))
+    {
+      case t_VEC: case t_COL:
+        fa = fact_from_factors(dT, fa, 0);
+        break;
+      case t_INT:
+        fa = absi_factor_limit(dT, (signe(fa) <= 0)? 1: itou(fa));
+        break;
+      case t_MAT:
+        if (is_Z_factornon0(fa)) break;
+        /*fall through*/
+      default:
+        pari_err_TYPE("nfmaxord",fa);
+    }
+    if (!signe(dT)) pari_err_IRREDPOL("nfmaxord",mkvec2(T,fa));
+  } else
+    fa = (flag & nf_PARTIALFACT)? absi_factor_limit(dT, 0): absi_factor(dT);
+  P = gel(fa,1); l = lg(P);
+  E = gel(fa,2);
+  if (l > 1 && is_pm1(gel(P,1)))
+  {
+    l--;
+    P = vecslice(P, 2, l);
+    E = vecslice(E, 2, l);
+  }
+  S->dTP = P;
+  S->dTE = vec_to_vecsmall(E);
+  if (DEBUGLEVEL) timer_printf(&t, "disc. factorisation");
+}
+
+static int
+fnz(GEN x,long j)
+{
+  long i;
+  for (i=1; i<j; i++)
+    if (signe(gel(x,i))) return 0;
+  return 1;
+}
+/* return list u[i], 2 by 2 coprime with the same prime divisors as ab */
+static GEN
+get_coprimes(GEN a, GEN b)
+{
+  long i, k = 1;
+  GEN u = cgetg(3, t_COL);
+  gel(u,1) = a;
+  gel(u,2) = b;
+  /* u1,..., uk 2 by 2 coprime */
+  while (k+1 < lg(u))
+  {
+    GEN d, c = gel(u,k+1);
+    if (is_pm1(c)) { k++; continue; }
+    for (i=1; i<=k; i++)
+    {
+      GEN ui = gel(u,i);
+      if (is_pm1(ui)) continue;
+      d = gcdii(c, ui);
+      if (d == gen_1) continue;
+      c = diviiexact(c, d);
+      gel(u,i) = diviiexact(ui, d);
+      u = shallowconcat(u, d);
+    }
+    gel(u,++k) = c;
+  }
+  for (i = k = 1; i < lg(u); i++)
+    if (!is_pm1(gel(u,i))) gel(u,k++) = gel(u,i);
+  setlg(u, k); return u;
+}
+/* denominator of diagonal. All denominators are powers of a given integer */
+static GEN
+diag_denom(GEN M)
+{
+  GEN d = gen_1;
+  long j, l = lg(M);
+  for (j=1; j<l; j++)
+  {
+    GEN t = gcoeff(M,j,j);
+    if (typ(t) == t_INT) continue;
+    t = gel(t,2);
+    if (absi_cmp(t,d) > 0) d = t;
+  }
+  return d;
+}
+static void
+allbase_from_maxord(nfmaxord_t *S, GEN maxord)
+{
+  pari_sp av = avma;
+  GEN f = S->T, P = S->dTP, a = NULL, da = NULL, index, P2, E2, D;
+  long n = degpol(f), lP = lg(P), i, j, k;
+  int centered = 0;
+  for (i=1; i<lP; i++)
+  {
+    GEN M, db, b = gel(maxord,i);
+    if (b == gen_1) continue;
+    db = diag_denom(b);
+    if (db == gen_1) continue;
+
+    /* db = denom(b), (da,db) = 1. Compute da Im(b) + db Im(a) */
+    b = Q_muli_to_int(b,db);
+    if (!da) { da = db; a = b; }
+    else
+    { /* optimization: easy as long as both matrix are diagonal */
+      j=2; while (j<=n && fnz(gel(a,j),j) && fnz(gel(b,j),j)) j++;
+      k = j-1; M = cgetg(2*n-k+1,t_MAT);
+      for (j=1; j<=k; j++)
+      {
+        gel(M,j) = gel(a,j);
+        gcoeff(M,j,j) = mulii(gcoeff(a,j,j),gcoeff(b,j,j));
+      }
+      /* could reduce mod M(j,j) but not worth it: usually close to da*db */
+      for (  ; j<=n;     j++) gel(M,j) = ZC_Z_mul(gel(a,j), db);
+      for (  ; j<=2*n-k; j++) gel(M,j) = ZC_Z_mul(gel(b,j+k-n), da);
+      da = mulii(da,db);
+      a = ZM_hnfmodall_i(M, da, hnf_MODID|hnf_CENTER);
+      gerepileall(av, 2, &a, &da);
+      centered = 1;
+    }
+  }
+  if (da)
+  {
+    index = diviiexact(da, gcoeff(a,1,1));
+    for (j=2; j<=n; j++) index = mulii(index, diviiexact(da, gcoeff(a,j,j)));
+    if (!centered) a = ZM_hnfcenter(a);
+    a = RgM_Rg_div(a, da);
+  }
+  else
+  {
+    index = gen_1;
+    a = matid(n);
+  }
+  S->dK = diviiexact(S->dT, sqri(index));
+  S->index = index;
+
+  D = S->dK;
+  P2 = cgetg(lP, t_COL);
+  E2 = cgetg(lP, t_VECSMALL);
+  for (k = j = 1; j < lP; j++)
+  {
+    long v = Z_pvalrem(D, gel(P,j), &D);
+    if (v) { gel(P2,k) = gel(P,j); E2[k] = v; k++; }
+  }
+  setlg(P2, k); S->dKP = P2;
+  setlg(E2, k); S->dKE = P2;
+  S->basis = RgM_to_RgXV(a, varn(f));
+}
+static GEN
+disc_from_maxord(nfmaxord_t *S, GEN O)
+{
+  long n = degpol(S->T), lP = lg(O), i, j;
+  GEN index = gen_1;
+  for (i=1; i<lP; i++)
+  {
+    GEN b = gel(O,i);
+    if (b == gen_1) continue;
+    for (j = 1; j <= n; j++)
+    {
+      GEN c = gcoeff(b,j,j);
+      if (typ(c) == t_FRAC) index = mulii(index, gel(c,2)) ;
+    }
+  }
+  return diviiexact(S->dT, sqri(index));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            ROUND 2                              */
+/*                                                                 */
+/*******************************************************************/
+/* transpose of companion matrix of unitary polynomial x, cf matcompanion */
+static GEN
+companion(GEN x)
+{
+  long j, l = degpol(x);
+  GEN c, y = cgetg(l+1,t_MAT);
+
+  c = zerocol(l); gel(c,l) = gneg(gel(x,2));
+  gel(y,1) = c;
+  for (j=2; j<=l; j++)
+  {
+    c = col_ei(l, j-1); gel(c,l) = gneg(gel(x,j+1));
+    gel(y,j) = c;
+  }
+  return y;
+}
+
+/* return (v - qw) mod m (only compute entries k0,..,n)
+ * v and w are expected to have entries smaller than m */
+static GEN
+mtran(GEN v, GEN w, GEN q, GEN m, GEN mo2, long k0)
+{
+  long k;
+  GEN p1;
+
+  if (signe(q))
+    for (k=lg(v)-1; k >= k0; k--)
+    {
+      pari_sp av = avma;
+      p1 = subii(gel(v,k), mulii(q,gel(w,k)));
+      p1 = centermodii(p1, m, mo2);
+      gel(v,k) = gerepileuptoint(av, p1);
+    }
+  return v;
+}
+
+/* entries of v and w are C small integers */
+static GEN
+mtran_long(GEN v, GEN w, long q, long m, long k0)
+{
+  long k, p1;
+
+  if (q)
+  {
+    for (k=lg(v)-1; k>= k0; k--)
+    {
+      p1 = v[k] - q * w[k];
+      v[k] = p1 % m;
+    }
+  }
+  return v;
+}
+
+/* coeffs of a are C-long integers */
+static void
+rowred_long(GEN a, long rmod)
+{
+  long j,k, c = lg(a), r = lgcols(a);
+
+  for (j=1; j<r; j++)
+  {
+    for (k=j+1; k<c; k++)
+      while (coeff(a,j,k))
+      {
+        long q = coeff(a,j,j) / coeff(a,j,k);
+        GEN pro = mtran_long(gel(a,j),gel(a,k),q,rmod, j);
+        gel(a, j) = gel(a, k); gel(a, k)=pro;
+      }
+    if (coeff(a,j,j) < 0)
+      for (k=j; k<r; k++) coeff(a,k,j)=-coeff(a,k,j);
+    for (k=1; k<j; k++)
+    {
+      long q = coeff(a,j,k) / coeff(a,j,j);
+      gel(a,k) = mtran_long(gel(a,k),gel(a,j),q,rmod, k);
+    }
+  }
+  /* don't update the 0s in the last columns */
+  for (j=1; j<r; j++)
+    for (k=1; k<r; k++) gcoeff(a,j,k) = stoi(coeff(a,j,k));
+}
+
+static void
+rowred(GEN a, GEN rmod, GEN rmodo2)
+{
+  long j,k, c = lg(a), r = lgcols(a);
+  pari_sp av=avma, lim=stack_lim(av,1);
+
+  for (j=1; j<r; j++)
+  {
+    for (k=j+1; k<c; k++)
+      while (signe(gcoeff(a,j,k)))
+      {
+        GEN q=diviiround(gcoeff(a,j,j),gcoeff(a,j,k));
+        GEN pro=mtran(gel(a,j),gel(a,k),q,rmod,rmodo2, j);
+        gel(a, j) = gel(a, k); gel(a, k)=pro;
+      }
+    if (signe(gcoeff(a,j,j)) < 0)
+      for (k=j; k<r; k++) gcoeff(a,k,j) = negi(gcoeff(a,k,j));
+    for (k=1; k<j; k++)
+    {
+      GEN q=diviiround(gcoeff(a,j,k),gcoeff(a,j,j));
+      gel(a,k) = mtran(gel(a,k),gel(a,j),q,rmod,rmodo2, k);
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      long j1,k1;
+      GEN p1;
+      if(DEBUGMEM>1) pari_warn(warnmem,"rowred j=%ld", j);
+      p1 = gerepilecopy(av,a);
+      for (j1=1; j1<r; j1++)
+        for (k1=1; k1<c; k1++) gcoeff(a,j1,k1) = gcoeff(p1,j1,k1);
+    }
+  }
+}
+
+/* Compute d/x where d is t_INT, x lower triangular t_MAT with t_INT coeffs
+ * whose diagonal coeffs divide d (lower triangular ZM result). */
+static GEN
+matinv(GEN x, GEN d)
+{
+  pari_sp av,av1;
+  long i,j,k, n = lg(x);
+  GEN y,h;
+
+  y = matid(n-1);
+  for (i=1; i<n; i++)
+    gcoeff(y,i,i) = diviiexact(d,gcoeff(x,i,i));
+  av=avma;
+  for (i=2; i<n; i++)
+    for (j=i-1; j; j--)
+    {
+      for (h=gen_0,k=j+1; k<=i; k++)
+      {
+        GEN p1 = mulii(gcoeff(y,i,k),gcoeff(x,k,j));
+        if (p1 != gen_0) h=addii(h,p1);
+      }
+      togglesign(h); av1=avma;
+      gcoeff(y,i,j) = gerepile(av,av1,diviiexact(h,gcoeff(x,j,j)));
+      av = avma;
+    }
+  return y;
+}
+
+/* epsilon > 1 */
+static GEN
+maxord2(GEN cf, GEN p, long epsilon)
+{
+  long sp,i,n=lg(cf)-1;
+  pari_sp av=avma, av2,limit;
+  GEN T,T2,Tn,m,v,delta,hard_case_exponent, *w;
+  const GEN pp = sqri(p);
+  const GEN ppo2 = shifti(pp,-1);
+  const long pps = (2*expi(pp)+2 < (long)BITS_IN_LONG)? pp[2]: 0;
+
+  if (cmpiu(p,n) > 0)
+  {
+    hard_case_exponent = NULL;
+    sp = 0; /* gcc -Wall */
+  }
+  else
+  {
+    long k;
+    k = sp = itos(p);
+    i=1; while (k < n) { k *= sp; i++; }
+    hard_case_exponent = utoipos(i);
+  }
+  T=cgetg(n+1,t_MAT); for (i=1; i<=n; i++) gel(T,i) = cgetg(n+1,t_COL);
+  T2=cgetg(2*n+1,t_MAT); for (i=1; i<=2*n; i++) gel(T2,i) = cgetg(n+1,t_COL);
+  Tn=cgetg(n*n+1,t_MAT); for (i=1; i<=n*n; i++) gel(Tn,i) = cgetg(n+1,t_COL);
+  v = new_chunk(n+1);
+  w = (GEN*)new_chunk(n+1);
+
+  av2 = avma; limit = stack_lim(av2,1);
+  delta=gen_1; m=matid(n);
+
+  for(;;)
+  {
+    long j, k, h;
+    pari_sp av0 = avma;
+    GEN t,b,jp,hh,index,p1, dd = sqri(delta), ppdd = mulii(dd,pp);
+    GEN ppddo2 = shifti(ppdd,-1);
+
+    if (DEBUGLEVEL > 3)
+      err_printf("ROUND2: epsilon = %ld\tavma = %ld\n",epsilon,avma);
+
+    b=matinv(m,delta);
+    for (i=1; i<=n; i++)
+    {
+      for (j=1; j<=n; j++)
+        for (k=1; k<=n; k++)
+        {
+          p1 = j==k? gcoeff(m,i,1): gen_0;
+          for (h=2; h<=n; h++)
+          {
+            GEN p2 = mulii(gcoeff(m,i,h),gcoeff(gel(cf,h),j,k));
+            if (p2!=gen_0) p1 = addii(p1,p2);
+          }
+          gcoeff(T,j,k) = centermodii(p1, ppdd, ppddo2);
+        }
+      p1 = ZM_mul(m, ZM_mul(T,b));
+      for (j=1; j<=n; j++)
+        for (k=1; k<=n; k++)
+          gcoeff(p1,j,k) = centermodii(diviiexact(gcoeff(p1,j,k),dd),pp,ppo2);
+      w[i] = p1;
+    }
+
+    if (hard_case_exponent)
+    {
+      for (j=1; j<=n; j++)
+      {
+        for (i=1; i<=n; i++) gcoeff(T,i,j) = gcoeff(w[j],1,i);
+        /* ici la boucle en k calcule la puissance p mod p de w[j] */
+        for (k=1; k<sp; k++)
+        {
+          for (i=1; i<=n; i++)
+          {
+            p1 = gen_0;
+            for (h=1; h<=n; h++)
+            {
+              GEN p2=mulii(gcoeff(T,h,j),gcoeff(w[j],h,i));
+              if (p2!=gen_0) p1 = addii(p1,p2);
+            }
+            gel(v,i) = modii(p1, p);
+          }
+          for (i=1; i<=n; i++) gcoeff(T,i,j) = gel(v,i);
+        }
+      }
+      t = ZM_pow(T, hard_case_exponent);
+    }
+    else
+    {
+      for (i=1; i<=n; i++)
+        for (j=1; j<=n; j++)
+        {
+          pari_sp av1 = avma;
+          p1 = gen_0;
+          for (k=1; k<=n; k++)
+            for (h=1; h<=n; h++)
+            {
+              const GEN r=modii(gcoeff(w[i],k,h),p);
+              const GEN s=modii(gcoeff(w[j],h,k),p);
+              const GEN p2 = mulii(r,s);
+              if (p2!=gen_0) p1 = addii(p1,p2);
+            }
+          gcoeff(T,i,j) = gerepileupto(av1,p1);
+        }
+      t = T;
+    }
+
+    setlg(T2, 2*n+1);
+    if (pps)
+    {
+      long ps = p[2];
+      for (i=1; i<=n; i++)
+        for (j=1; j<=n; j++)
+        {
+          coeff(T2,j,i)=(i==j)? ps: 0;
+          coeff(T2,j,n+i)=smodis(gcoeff(t,i,j),ps);
+        }
+      rowred_long(T2,pps);
+    }
+    else
+    {
+      for (i=1; i<=n; i++)
+        for (j=1; j<=n; j++)
+        {
+          gcoeff(T2,j,i)=(i==j)? p: gen_0;
+          gcoeff(T2,j,n+i) = modii(gcoeff(t,i,j),p);
+        }
+      rowred(T2,pp,ppo2);
+    }
+    setlg(T2, n+1);
+    jp=matinv(T2,p);
+    setlg(Tn, n*n+1);
+    if (pps)
+    {
+      for (k=1; k<=n; k++)
+      {
+        pari_sp av1=avma;
+        t = ZM_mul(ZM_mul(jp,w[k]), T2);
+        for (h=i=1; i<=n; i++)
+          for (j=1; j<=n; j++,h++)
+            coeff(Tn,k,h) = itos(diviiexact(gcoeff(t,i,j), p)) % pps;
+        avma=av1;
+      }
+      avma = av0;
+      rowred_long(Tn,pps);
+    }
+    else
+    {
+      for (k=1; k<=n; k++)
+      {
+        t = ZM_mul(ZM_mul(jp,w[k]), T2);
+        for (h=i=1; i<=n; i++)
+          for (j=1; j<=n; j++,h++)
+            gcoeff(Tn,k,h) = diviiexact(gcoeff(t,i,j), p);
+      }
+      rowred(Tn,pp,ppo2);
+    }
+    setlg(Tn, n+1);
+    index = ZM_det_triangular(Tn);
+    if (is_pm1(index)) break;
+
+    m = ZM_mul(matinv(Tn,index), m);
+    m = Q_primitive_part(m, &hh);
+    delta = mulii(index,delta);
+    if (hh) delta = diviiexact(delta,hh);
+    epsilon -= 2 * Z_pval(index,p);
+    if (epsilon < 2) break;
+    if (low_stack(limit,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"maxord2");
+      gerepileall(av2, 2, &m, &delta);
+    }
+  }
+  m = shallowtrans(m);
+  return gerepileupto(av, RgM_Rg_div(ZM_hnfmodid(m, delta), delta));
+}
+
+static GEN
+allbase2(nfmaxord_t *S)
+{
+  GEN cf, O, P = S->dTP, E = S->dTE, f = S->T;
+  long i, lP = lg(P), n = degpol(f);
+
+  cf = cgetg(n+1,t_VEC); gel(cf,2) = companion(f);
+  for (i=3; i<=n; i++) gel(cf,i) = ZM_mul(gel(cf,2), gel(cf,i-1));
+  O = cgetg(lP, t_VEC);
+  for (i=1; i<lP; i++)
+  {
+    GEN p = gel(P, i);
+    long e = E[i];
+    if (DEBUGLEVEL) err_printf("Treating p^k = %Ps^%ld\n", p, e);
+    gel(O,i) = e == 1? gen_1: maxord2(cf, p, e);
+  }
+  return O;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            ROUND 4                              */
+/*                                                                 */
+/*******************************************************************/
+GEN maxord_i(GEN p, GEN f, long mf, GEN w, long flag);
+static GEN dbasis(GEN p, GEN f, long mf, GEN alpha, GEN U);
+static GEN maxord(GEN p,GEN f,long mf);
+static GEN ZX_Dedekind(GEN F, GEN *pg, GEN p);
+
+/* Warning: data computed for T = ZX_Q_normalize(T0). If S.unscale !=
+ * gen_1, caller must take steps to correct the components if it wishes
+ * to stick to the original T0 */
+static GEN
+get_maxord(nfmaxord_t *S, GEN T0, long flag)
+{
+  VOLATILE GEN P, E, O;
+  VOLATILE long lP, i, k;
+
+  nfmaxord_check_args(S, T0, flag);
+  if (flag & nf_ROUND2) return allbase2(S);
+  P = S->dTP; lP = lg(P);
+  E = S->dTE;
+  O = cgetg(1, t_VEC);
+  for (i=1; i<lP; i++)
+  {
+    VOLATILE pari_sp av;
+    /* includes the silly case where P[i] = -1 */
+    if (E[i] <= 1) { O = shallowconcat(O, gen_1); continue; }
+    av = avma;
+    pari_CATCH(CATCH_ALL) {
+      GEN N, u, ERR = pari_err_last();
+      long l;
+      switch(err_get_num(ERR))
+      {
+        case e_INV:
+        {
+          GEN p, x = err_get_compo(ERR, 2);
+          if (typ(x) == t_INTMOD)
+          { /* caught false prime, update factorization */
+            p = gcdii(gel(x,1), gel(x,2));
+            u = diviiexact(gel(x,1),p);
+            if (DEBUGLEVEL) pari_warn(warner,"impossible inverse: %Ps", x);
+            gerepileall(av, 2, &p, &u);
+
+            u = get_coprimes(p, u); l = lg(u);
+            /* no small factors, but often a prime power */
+            for (k = 1; k < l; k++) (void)Z_isanypower(gel(u,k), &gel(u,k));
+            break;
+          }
+          /* fall through */
+        }
+        case e_PRIME: case e_IRREDPOL:
+        { /* we're here because we failed BPSW_isprime(), no point in
+           * reporting a possible counter-example to the BPSW test */
+          GEN p = gel(P,i);
+          avma = av;
+          if (DEBUGLEVEL)
+            pari_warn(warner,"large composite in nfmaxord:loop(), %Ps", p);
+          if (expi(p) < 100) /* factor should require ~20ms for this */
+            u = gel(Z_factor(p), 1);
+          else
+          { /* give up, probably not maximal */
+            GEN B, g, k = ZX_Dedekind(S->T, &g, p);
+            k = FpX_normalize(k, p);
+            B = dbasis(p, S->T, E[i], NULL, FpX_div(S->T,k,p));
+            O = shallowconcat(O, mkvec(B));
+            pari_CATCH_reset(); continue;
+          }
+          break;
+        }
+        default: pari_err(0, ERR);
+          return NULL;
+      }
+      l = lg(u);
+      gel(P,i) = gel(u,1);
+      P = shallowconcat(P, vecslice(u, 2, l-1));
+      av = avma;
+      N = S->dT; E[i] = Z_pvalrem(N, gel(P,i), &N);
+      for (k=lP, lP=lg(P); k < lP; k++) E[k] = Z_pvalrem(N, gel(P,k), &N);
+    } pari_RETRY {
+      if (DEBUGLEVEL) err_printf("Treating p^k = %Ps^%ld\n",P[i],E[i]);
+      O = shallowconcat(O, mkvec( maxord(gel(P,i),S->T,E[i]) ));
+    } pari_ENDCATCH;
+  }
+  S->dTP = P; return O;
+}
+void
+nfmaxord(nfmaxord_t *S, GEN T0, long flag)
+{
+  GEN O = get_maxord(S, T0, flag);
+  allbase_from_maxord(S, O);
+}
+
+static void
+_nfbasis(GEN x, long flag, GEN fa, GEN *pbas, GEN *pdK)
+{
+  nfmaxord_t S;
+  nfmaxord(&S, fa? mkvec2(x,fa): x, flag);
+  if (pbas) *pbas = RgXV_unscale(S.basis, S.unscale);
+  if (pdK)  *pdK = S.dK;
+}
+static GEN
+_nfdisc(GEN x, long flag, GEN fa)
+{
+  pari_sp av = avma;
+  nfmaxord_t S;
+  GEN O = get_maxord(&S, fa? mkvec2(x,fa): x, flag);
+  GEN D = disc_from_maxord(&S, O);
+  D = icopy_avma(D, av); avma = (pari_sp)D; return D;
+}
+
+/* deprecated: backward compatibility only ! */
+GEN
+nfbasis_gp(GEN T, GEN P, GEN junk)
+{
+  if (!P || isintzero(P)) return nfbasis(T, NULL, junk);
+  if (junk) pari_err_FLAG("nfbasis");
+  /* treat specially nfbasis(T, 1): the deprecated way to initialize an nf when
+   * disc(T) is hard to factor */
+  if (typ(P) == t_INT && equali1(P)) P = utoipos(maxprime());
+  return nfbasis(T, NULL, P);
+}
+/* deprecated */
+GEN
+nfdisc_gp(GEN T, GEN P, GEN junk)
+{
+  if (!P || isintzero(P)) return _nfdisc(T, 0, junk);
+  if (junk) pari_err_FLAG("nfdisc");
+  /* treat specially nfdisc(T, 1) */
+  if (typ(P) == t_INT && equali1(P)) P = utoipos(maxprime());
+  return _nfdisc(T, 0, P);
+}
+/* backward compatibility */
+static long
+nfbasis_flag_translate(long flag)
+{
+  switch(flag) {
+    case 0: return 0;
+    case 1: return nf_PARTIALFACT;
+    case 2: return nf_ROUND2;
+    case 3: return nf_ROUND2|nf_PARTIALFACT;
+    default: pari_err_FLAG("nfbasis");
+             return 0;
+  }
+}
+/* deprecated */
+GEN
+nfbasis0(GEN x, long flag, GEN fa)
+{
+  pari_sp av = avma;
+  GEN bas; _nfbasis(x, nfbasis_flag_translate(flag), fa, &bas, NULL);
+  return gerepilecopy(av, bas);
+}
+/* deprecated */
+GEN
+nfdisc0(GEN x, long flag, GEN fa)
+{ return _nfdisc(x, nfbasis_flag_translate(flag), fa); }
+
+GEN
+nfbasis(GEN x, GEN *pdK, GEN fa)
+{
+  pari_sp av = avma;
+  GEN bas; _nfbasis(x, 0, fa, &bas, pdK);
+  gerepileall(av, pdK? 2: 1, &bas, pdK); return bas;
+}
+GEN
+nfdisc(GEN x) { return _nfdisc(x, 0, NULL); }
+
+static ulong
+Flx_checkdeflate(GEN x)
+{
+  ulong d = 0, i, lx = (ulong)lg(x);
+  for (i=3; i<lx; i++)
+    if (x[i]) { d = ugcd(d,i-2); if (d == 1) break; }
+  return d;
+}
+
+/* product of (monic) irreducible factors of f over Fp[X]
+ * Assume f reduced mod p, otherwise valuation at x may be wrong */
+static GEN
+Flx_radical(GEN f, ulong p)
+{
+  long v0 = Flx_valrem(f, &f);
+  ulong du, d, e;
+  GEN u;
+
+  d = Flx_checkdeflate(f);
+  if (!d) return v0? polx_Flx(f[1]): pol1_Flx(f[1]);
+  if (u_lvalrem(d,p, &e)) f = Flx_deflate(f, d/e);
+  u = Flx_gcd(f,Flx_deriv(f, p), p);
+  du = degpol(u);
+  if (du)
+  {
+    if (du == (ulong)degpol(f))
+      f = Flx_radical(Flx_deflate(f,p), p);
+    else
+    {
+      u = Flx_normalize(u, p);
+      f = Flx_div(f, u, p);
+      if (p <= du)
+      {
+        GEN w = Flxq_powu(f, du, u, p);
+        w = Flx_div(u, Flx_gcd(w,u,p), p); /* u / gcd(u, v^(deg u-1)) */
+        f = Flx_mul(f, Flx_radical(Flx_deflate(w,p), p), p);
+      }
+    }
+  }
+  if (v0) f = Flx_shift(f, 1);
+  return f;
+}
+/* Assume f reduced mod p, otherwise valuation at x may be wrong */
+static GEN
+FpX_radical(GEN f, GEN p)
+{
+  GEN u;
+  long v0;
+  if (lgefint(p) == 3)
+  {
+    ulong q = p[2];
+    return Flx_to_ZX( Flx_radical(ZX_to_Flx(f, q), q) );
+  }
+  v0 = ZX_valrem(f, &f);
+  u = FpX_gcd(f,FpX_deriv(f, p), p);
+  if (degpol(u)) f = FpX_div(f, u, p);
+  if (v0) f = RgX_shift(f, 1);
+  return f;
+}
+/* f / a */
+static GEN
+zx_z_div(GEN f, ulong a)
+{
+  long i, l = lg(f);
+  GEN g = cgetg(l, t_VECSMALL);
+  g[1] = f[1];
+  for (i = 2; i < l; i++) g[i] = f[i] / a;
+  return g;
+}
+/* Dedekind criterion; return k = gcd(g,h, (f-gh)/p), where
+ *   f = \prod f_i^e_i, g = \prod f_i, h = \prod f_i^{e_i-1}
+ * k = 1 iff Z[X]/(f) is p-maximal */
+static GEN
+ZX_Dedekind(GEN F, GEN *pg, GEN p)
+{
+  GEN k, h, g, f, f2;
+  ulong q = p[2];
+  if (lgefint(p) == 3 && q < (1UL << BITS_IN_HALFULONG))
+  {
+    ulong q = p[2], q2 = q*q;
+    f2 = ZX_to_Flx(F, q2);
+    f = Flx_red(f2, q);
+    g = Flx_radical(f, q);
+    h = Flx_div(f, g, q);
+    k = zx_z_div(Flx_sub(f2, Flx_mul(g,h,q2), q2), q);
+    k = Flx_gcd(k, Flx_gcd(g,h,q), q);
+    k = Flx_to_ZX(k);
+    g = Flx_to_ZX(g);
+  }
+  else
+  {
+    f2 = FpX_red(F, sqri(p));
+    f = FpX_red(f2, p);
+    g = FpX_radical(f, p);
+    h = FpX_div(f, g, p);
+    k = ZX_Z_divexact(ZX_sub(f2, ZX_mul(g,h)), p);
+    k = FpX_gcd(FpX_red(k, p), FpX_gcd(g,h,p), p);
+  }
+  *pg = g; return k;
+}
+
+/* p-maximal order of Z[x]/f; mf = v_p(Disc(f)) or < 0 [unknown].
+ * Return gen_1 if p-maximal */
+static GEN
+maxord(GEN p, GEN f, long mf)
+{
+  const pari_sp av = avma;
+  GEN res, g, k = ZX_Dedekind(f, &g, p);
+  long dk = degpol(k);
+  if (DEBUGLEVEL>2) err_printf("  ZX_dedekind: gcd has degree %ld\n", dk);
+  if (!dk) { avma = av; return gen_1; }
+  if (mf < 0) mf = ZpX_disc_val(f, p);
+  if (2*dk >= mf-1)
+  {
+    k = FpX_normalize(k, p);
+    res = dbasis(p, f, mf, NULL, FpX_div(f,k,p));
+  }
+  else
+  {
+    GEN w, F1, F2;
+    F1 = FpX_factor(k,p);
+    F2 = FpX_factor(FpX_div(g,k,p),p);
+    w = merge_sort_uniq(gel(F1,1),gel(F2,1),(void*)cmpii,&gen_cmp_RgX);
+    res = maxord_i(p, f, mf, w, 0);
+  }
+  return gerepilecopy(av,res);
+}
+
+static GEN
+Zlx_sylvester_echelon(GEN f1, GEN f2, long early_abort, ulong p, ulong pm)
+{
+  long j, n = degpol(f1);
+  GEN h, a = cgetg(n+1,t_MAT);
+  f1 = Flx_get_red(f1, pm);
+  h = Flx_rem(f2,f1,pm);
+  for (j=1;; j++)
+  {
+    gel(a,j) = Flx_to_Flv(h, n);
+    if (j == n) break;
+    h = Flx_rem(Flx_shift(h, 1), f1, pm);
+  }
+  return zlm_echelon(a, early_abort, p, pm);
+}
+/* Sylvester's matrix, mod p^m (assumes f1 monic). If early_abort
+ * is set, return NULL if one pivot is 0 mod p^m */
+static GEN
+ZpX_sylvester_echelon(GEN f1, GEN f2, long early_abort, GEN p, GEN pm)
+{
+  long j, n = degpol(f1);
+  GEN h, a = cgetg(n+1,t_MAT);
+  h = FpXQ_red(f2,f1,pm);
+  for (j=1;; j++)
+  {
+    gel(a,j) = RgX_to_RgV(h, n);
+    if (j == n) break;
+    h = FpX_rem(RgX_shift_shallow(h, 1), f1, pm);
+  }
+  return ZpM_echelon(a, early_abort, p, pm);
+}
+
+/* polynomial gcd mod p^m (assumes f1 monic). Return a QpX ! */
+static GEN
+Zlx_gcd(GEN f1, GEN f2, ulong p, ulong pm)
+{
+  pari_sp av = avma;
+  GEN a = Zlx_sylvester_echelon(f1,f2,0,p,pm);
+  long c, l = lg(a), v = varn(f1);
+  for (c = 1; c < l; c++)
+  {
+    ulong t = ucoeff(a,c,c);
+    if (t)
+    {
+      a = RgV_to_RgX(Flv_to_ZV(gel(a,c)), v);
+      if (t == 1) return gerepilecopy(av, a);
+      return gerepileupto(av, RgX_Rg_div(a, utoipos(t)));
+    }
+  }
+  avma = av; return pol_0(v);
+}
+GEN
+ZpX_gcd(GEN f1, GEN f2, GEN p, GEN pm)
+{
+  pari_sp av = avma;
+  GEN a;
+  long c, l, v;
+  if (lgefint(pm) == 3)
+  {
+    ulong q = pm[2];
+    return Zlx_gcd(ZX_to_Flx(f1, q), ZX_to_Flx(f2,q), p[2], q);
+  }
+  a = ZpX_sylvester_echelon(f1,f2,0,p,pm);
+  l = lg(a); v = varn(f1);
+  for (c = 1; c < l; c++)
+  {
+    GEN t = gcoeff(a,c,c);
+    if (signe(t))
+    {
+      a = RgV_to_RgX(gel(a,c), v);
+      if (equali1(t)) return gerepilecopy(av, a);
+      return gerepileupto(av, RgX_Rg_div(a, t));
+    }
+  }
+  avma = av; return pol_0(v);
+}
+
+/* Return m > 0, such that p^m ~ 2^16 for initial value of m; p > 1 */
+static long
+init_m(GEN p)
+{
+  if (lgefint(p) > 3) return 1;
+  return (long)(16 / log2(p[2]));
+}
+
+/* reduced resultant mod p^m (assumes x monic) */
+GEN
+ZpX_reduced_resultant(GEN x, GEN y, GEN p, GEN pm)
+{
+  pari_sp av = avma;
+  GEN z;
+  if (lgefint(pm) == 3)
+  {
+    ulong q = pm[2];
+    z = Zlx_sylvester_echelon(ZX_to_Flx(x,q), ZX_to_Flx(y,q),0,p[2],q);
+    if (lg(z) > 1)
+    {
+      ulong c = ucoeff(z,1,1);
+      if (c) { avma = av; return utoipos(c); }
+    }
+  }
+  else
+  {
+    z = ZpX_sylvester_echelon(x,y,0,p,pm);
+    if (lg(z) > 1)
+    {
+      GEN c = gcoeff(z,1,1);
+      if (signe(c)) return gerepileuptoint(av, c);
+    }
+  }
+  avma = av; return gen_0;
+}
+/* Assume Res(f,g) divides p^M. Return Res(f, g), using dynamic p-adic
+ * precision (until result is non-zero or p^M). */
+GEN
+ZpX_reduced_resultant_fast(GEN f, GEN g, GEN p, long M)
+{
+  GEN R, q = NULL;
+  long m;
+  m = init_m(p); if (m < 1) m = 1;
+  for(;; m <<= 1) {
+    if (M < 2*m) break;
+    q = q? sqri(q): powiu(p, m); /* p^m */
+    R = ZpX_reduced_resultant(f,g, p, q); if (signe(R)) return R;
+  }
+  q = powiu(p, M);
+  R = ZpX_reduced_resultant(f,g, p, q); return signe(R)? R: q;
+}
+
+/* v_p(Res(x,y) mod p^m), assumes (lc(x),p) = 1 */
+static long
+ZpX_resultant_val_i(GEN x, GEN y, GEN p, GEN pm)
+{
+  pari_sp av = avma;
+  GEN z;
+  long i, l, v;
+  if (lgefint(pm) == 3)
+  {
+    ulong q = pm[2], pp = p[2];
+    z = Zlx_sylvester_echelon(ZX_to_Flx(x,q), ZX_to_Flx(y,q), 1, pp, q);
+    if (!z) { avma = av; return -1; } /* failure */
+    v = 0; l = lg(z);
+    for (i = 1; i < l; i++) v += u_lval(ucoeff(z,i,i), pp);
+  }
+  else
+  {
+    z = ZpX_sylvester_echelon(x, y, 1, p, pm);
+    if (!z) { avma = av; return -1; } /* failure */
+    v = 0; l = lg(z);
+    for (i = 1; i < l; i++) v += Z_pval(gcoeff(z,i,i), p);
+  }
+  return v;
+}
+
+/* assume (lc(f),p) = 1; no assumption on g */
+long
+ZpX_resultant_val(GEN f, GEN g, GEN p, long M)
+{
+  pari_sp av = avma;
+  GEN q = NULL;
+  long v, m;
+  m = init_m(p); if (m < 2) m = 2;
+  for(;; m <<= 1) {
+    if (m > M) m = M;
+    q = q? sqri(q): powiu(p, m); /* p^m */
+    v = ZpX_resultant_val_i(f,g, p, q); if (v >= 0) break;
+    if (m == M) return M;
+  }
+  avma = av; return v;
+}
+
+/* assume f separable and (lc(f),p) = 1 */
+long
+ZpX_disc_val(GEN f, GEN p)
+{
+  pari_sp av = avma;
+  long v;
+  if (degpol(f) == 1) return 0;
+  v = ZpX_resultant_val(f, ZX_deriv(f), p, LONG_MAX);
+  avma = av; return v;
+}
+
+/* *e a ZX, *d, *z in Z, *d = p^(*vd). Simplify e / d by cancelling a
+ * common factor p^v; if z!=NULL, update it by cancelling the same power of p */
+static void
+update_den(GEN p, GEN *e, GEN *d, long *vd, GEN *z)
+{
+  GEN newe;
+  long ve = ZX_pvalrem(*e, p, &newe);
+  if (ve) {
+    GEN newd;
+    long v = minss(*vd, ve);
+    if (v) {
+      if (v == *vd)
+      { /* rare, denominator cancelled */
+        if (ve != v) newe = ZX_Z_mul(newe, powiu(p, ve - v));
+        newd = gen_1;
+        *vd = 0;
+        if (z) *z =diviiexact(*z, powiu(p, v));
+      }
+      else
+      { /* v = ve < vd, generic case */
+        GEN q = powiu(p, v);
+        newd = diviiexact(*d, q);
+        *vd -= v;
+        if (z) *z = diviiexact(*z, q);
+      }
+      *e = newe;
+      *d = newd;
+    }
+  }
+}
+
+/* return denominator, a power of p */
+static GEN
+QpX_denom(GEN x)
+{
+  long i, l = lg(x);
+  GEN maxd = gen_1;
+  for (i=2; i<l; i++)
+  {
+    GEN d = gel(x,i);
+    if (typ(d) == t_FRAC && cmpii(gel(d,2), maxd) > 0) maxd = gel(d,2);
+  }
+  return maxd;
+}
+static GEN
+QpXV_denom(GEN x)
+{
+  long l = lg(x), i;
+  GEN maxd = gen_1;
+  for (i = 1; i < l; i++)
+  {
+    GEN d = QpX_denom(gel(x,i));
+    if (cmpii(d, maxd) > 0) maxd = d;
+  }
+  return maxd;
+}
+
+static GEN
+QpX_remove_denom(GEN x, GEN p, GEN *pdx, long *pv)
+{
+  *pdx = QpX_denom(x);
+  if (*pdx == gen_1) { *pv = 0; *pdx = NULL; }
+  else {
+    x = Q_muli_to_int(x,*pdx);
+    *pv = Z_pval(*pdx, p);
+  }
+  return x;
+}
+
+/* p^v * f o g mod (T,q). q = p^vq  */
+static GEN
+compmod(GEN p, GEN f, GEN g, GEN T, GEN q, long v)
+{
+  GEN D = NULL, z, df, dg, qD;
+  long vD = 0, vdf, vdg;
+
+  f = QpX_remove_denom(f, p, &df, &vdf);
+  if (typ(g) == t_VEC) /* [num,den,v_p(den)] */
+  { vdg = itos(gel(g,3)); dg = gel(g,2); g = gel(g,1); }
+  else
+    g = QpX_remove_denom(g, p, &dg, &vdg);
+  if (df) { D = df; vD = vdf; }
+  if (dg) {
+    long degf = degpol(f);
+    D = mul_content(D, powiu(dg, degf));
+    vD += degf * vdg;
+  }
+  qD = D ? mulii(q, D): q;
+  if (dg) f = FpX_rescale(f, dg, qD);
+  z = FpX_FpXQ_eval(f, g, T, qD);
+  if (!D) {
+    if (v) {
+      if (v > 0)
+        z = ZX_Z_mul(z, powiu(p, v));
+      else
+        z = RgX_Rg_div(z, powiu(p, -v));
+    }
+    return z;
+  }
+  update_den(p, &z, &D, &vD, NULL);
+  qD = mulii(D,q);
+  if (v) vD -= v;
+  z = FpX_center(z, qD, shifti(qD,-1));
+  if (vD > 0)
+    z = RgX_Rg_div(z, powiu(p, vD));
+  else if (vD < 0)
+    z = ZX_Z_mul(z, powiu(p, -vD));
+  return z;
+}
+
+/* fast implementation of ZM_hnfmodid(M, D) / D, D = p^k */
+static GEN
+ZpM_hnfmodid(GEN M, GEN p, GEN D)
+{
+  long i, l = lg(M);
+  M = RgM_Rg_div(ZpM_echelon(M,0,p,D), D);
+  for (i = 1; i < l; i++)
+    if (gequal0(gcoeff(M,i,i))) gcoeff(M,i,i) = gen_1;
+  return M;
+}
+
+/* Return Z-basis for Z[a] + U(a)/p Z[a] in Z[t]/(f), mf = v_p(disc f), U
+ * a ZX. Special cases: a = t is coded as NULL, U = 0 is coded as NULL */
+static GEN
+dbasis(GEN p, GEN f, long mf, GEN a, GEN U)
+{
+  long n = degpol(f), i, dU;
+  GEN b, h;
+
+  if (n == 1) return matid(1);
+  if (a && gequalX(a)) a = NULL;
+  if (DEBUGLEVEL>5)
+  {
+    err_printf("  entering Dedekind Basis with parameters p=%Ps\n",p);
+    err_printf("  f = %Ps,\n  a = %Ps\n",f, a? a: pol_x(varn(f)));
+  }
+  if (a)
+  {
+    GEN pd = powiu(p, mf >> 1);
+    GEN da, pdp = mulii(pd,p), D = pdp;
+    long vda;
+    dU = U ? degpol(U): 0;
+    b = cgetg(n+1, t_MAT);
+    h = scalarpol(pd, varn(f));
+    a = QpX_remove_denom(a, p, &da, &vda);
+    if (da) D = mulii(D, da);
+    gel(b,1) = scalarcol_shallow(pd, n);
+    for (i=2; i<=n; i++)
+    {
+      if (i == dU+1)
+        h = compmod(p, U, mkvec3(a,da,stoi(vda)), f, pdp, (mf>>1) - 1);
+      else
+      {
+        h = FpXQ_mul(h, a, f, D);
+        if (da) h = ZX_Z_divexact(h, da);
+      }
+      gel(b,i) = RgX_to_RgV(h,n);
+    }
+    return ZpM_hnfmodid(b, p, pd);
+  }
+  else
+  {
+    if (!U) return matid(n);
+    dU = degpol(U);
+    if (dU == n) return matid(n);
+    U = FpX_normalize(U, p);
+    b = cgetg(n+1, t_MAT);
+    for (i = 1; i <= dU; i++) gel(b,i) = vec_ei(n, i);
+    h = RgX_Rg_div(U, p);
+    for ( ; i <= n; i++)
+    {
+      gel(b, i) = RgX_to_RgV(h,n);
+      if (i == n) break;
+      h = RgX_shift_shallow(h,1);
+    }
+    return b;
+  }
+}
+
+static GEN
+get_partial_order_as_pols(GEN p, GEN f)
+{
+  GEN O = maxord(p, f, -1);
+  long v = varn(f);
+  return O == gen_1? pol_x_powers(degpol(f), v): RgM_to_RgXV(O, v);
+}
+
+typedef struct {
+  /* constants */
+  long pisprime; /* -1: unknown, 1: prime,  0: composite */
+  GEN p, f; /* goal: factor f p-adically */
+  long df;
+  GEN pdf; /* p^df = reduced discriminant of f */
+  long mf; /* */
+  GEN psf, pmf; /* stability precision for f, wanted precision for f */
+  long vpsf; /* v_p(p_f) */
+  /* these are updated along the way */
+  GEN phi; /* a p-integer, in Q[X] */
+  GEN phi0; /* a p-integer, in Q[X] from testb2 / testc2, to be composed with
+             * phi when correct precision is known */
+  GEN chi; /* characteristic polynomial of phi (mod psc) in Z[X] */
+  GEN nu; /* irreducible divisor of chi mod p, in Z[X] */
+  GEN invnu; /* numerator ( 1/ Mod(nu, chi) mod pmr ) */
+  GEN Dinvnu;/* denominator ( ... ) */
+  long vDinvnu; /* v_p(Dinvnu) */
+  GEN prc, psc; /* reduced discriminant of chi, stability precision for chi */
+  long vpsc; /* v_p(p_c) */
+  GEN ns, precns; /* cached Newton sums and their precision */
+} decomp_t;
+
+static long
+p_is_prime(decomp_t *S)
+{
+  if (S->pisprime < 0) S->pisprime = BPSW_psp(S->p);
+  return S->pisprime;
+}
+
+/* if flag = 0, maximal order, else factorization to precision r = flag */
+static GEN
+Decomp(decomp_t *S, long flag)
+{
+  pari_sp av = avma;
+  GEN fred, pr, pk, ph, b1, b2, a, e, de, f1, f2, dt, th;
+  GEN p = S->p, chip;
+  long k, r = flag? flag: 2*S->df + 1;
+  long vde, vdt;
+
+  if (DEBUGLEVEL>2)
+  {
+    err_printf("  entering Decomp");
+    if (DEBUGLEVEL>5) err_printf(", parameters: %Ps^%ld\n  f = %Ps",p, r, S->f);
+    err_printf("\n");
+  }
+  chip = FpX_red(S->chi, p);
+  if (!FpX_valrem(chip, S->nu, p, &b1))
+  {
+    if (!p_is_prime(S)) pari_err_PRIME("Decomp",p);
+    pari_err_BUG("Decomp (not a factor)");
+  }
+  b2 = FpX_div(chip, b1, p);
+  a = FpX_mul(FpXQ_inv(b2, b1, p), b2, p);
+  /* E = e / de, e in Z[X], de in Z,  E = a(phi) mod (f, p) */
+  th = QpX_remove_denom(S->phi, p, &dt, &vdt);
+  if (dt)
+  {
+    long dega = degpol(a);
+    vde = dega * vdt;
+    de = powiu(dt, dega);
+    pr = mulii(p, de);
+    a = FpX_rescale(a, dt, pr);
+  }
+  else
+  {
+    vde = 0;
+    de = gen_1;
+    pr = p;
+  }
+  e = FpX_FpXQ_eval(a, th, S->f, pr);
+  update_den(p, &e, &de, &vde, NULL);
+
+  pk = p; k = 1;
+  /* E, (1 - E) tend to orthogonal idempotents in Zp[X]/(f) */
+  while (k < r + vde)
+  { /* E <-- E^2(3-2E) mod p^2k, with E = e/de */
+    GEN D;
+    pk = sqri(pk); k <<= 1;
+    e = ZX_mul(ZX_sqr(e), Z_ZX_sub(mului(3,de), gmul2n(e,1)));
+    de= mulii(de, sqri(de));
+    vde *= 3;
+    D = mulii(pk, de);
+    e = FpX_rem(e, centermod(S->f, D), D); /* e/de defined mod pk */
+    update_den(p, &e, &de, &vde, NULL);
+  }
+  pr = powiu(p, r); /* required precision of the factors */
+  ph = mulii(de, pr);
+  fred = centermod(S->f, ph);
+  e    = centermod(e, ph);
+
+  f1 = ZpX_gcd(fred, Z_ZX_sub(de, e), p, ph); /* p-adic gcd(f, 1-e) */
+  fred = centermod(fred, pr);
+  f1   = centermod(f1,   pr);
+  f2 = FpX_div(fred,f1, pr);
+  f2 = FpX_center(f2, pr, shifti(pr,-1));
+
+  if (DEBUGLEVEL>5)
+    err_printf("  leaving Decomp: f1 = %Ps\nf2 = %Ps\ne = %Ps\nde= %Ps\n", f1,f2,e,de);
+
+  if (flag) {
+    gerepileall(av, 2, &f1, &f2);
+    return famat_mul_shallow(ZX_monic_factorpadic(f1, p, flag),
+                             ZX_monic_factorpadic(f2, p, flag));
+  } else {
+    GEN D, d1, d2, B1, B2, M;
+    long n, n1, n2, i;
+    gerepileall(av, 4, &f1, &f2, &e, &de);
+    D = de;
+    B1 = get_partial_order_as_pols(p,f1); n1 = lg(B1)-1;
+    B2 = get_partial_order_as_pols(p,f2); n2 = lg(B2)-1; n = n1+n2;
+    d1 = QpXV_denom(B1);
+    d2 = QpXV_denom(B2); if (cmpii(d1, d2) < 0) d1 = d2;
+    if (d1 != gen_1) {
+      B1 = Q_muli_to_int(B1, d1);
+      B2 = Q_muli_to_int(B2, d1);
+      D = mulii(d1, D);
+    }
+    fred = centermod_i(S->f, D, shifti(D,-1));
+    M = cgetg(n+1, t_MAT);
+    for (i=1; i<=n1; i++)
+      gel(M,i) = RgX_to_RgV(FpX_rem(FpX_mul(gel(B1,i),e,D), fred, D), n);
+    e = Z_ZX_sub(de, e); B2 -= n1;
+    for (   ; i<=n; i++)
+      gel(M,i) = RgX_to_RgV(FpX_rem(FpX_mul(gel(B2,i),e,D), fred, D), n);
+    return ZpM_hnfmodid(M, p, D);
+  }
+}
+
+/* minimum extension valuation: L/E */
+static void
+vstar(GEN p,GEN h, long *L, long *E)
+{
+  long first, j, k, v, w, m = degpol(h);
+
+  first = 1; k = 1; v = 0;
+  for (j=1; j<=m; j++)
+  {
+    GEN c = gel(h, m-j+2);
+    if (signe(c))
+    {
+      w = Z_pval(c,p);
+      if (first || w*k < v*j) { v = w; k = j; }
+      first = 0;
+    }
+  }
+  /* v/k = max_j ( v_p(h_{m-j}) / j ) */
+  w = (long)ugcd(v,k);
+  *L = v/w;
+  *E = k/w;
+}
+
+static GEN
+redelt_i(GEN a, GEN N, GEN p, GEN *pda, long *pvda)
+{
+  GEN z;
+  a = Q_remove_denom(a, pda);
+  *pvda = 0;
+  if (*pda)
+  {
+    long v = Z_pvalrem(*pda, p, &z);
+    if (v) {
+      *pda = powiu(p, v);
+      *pvda = v;
+      N  = mulii(*pda, N);
+    }
+    else
+      *pda = NULL;
+    if (!is_pm1(z)) a = ZX_Z_mul(a, Fp_inv(z, N));
+  }
+  return centermod(a, N);
+}
+/* reduce the element a modulo N [ a power of p ], taking first care of the
+ * denominators */
+static GEN
+redelt(GEN a, GEN N, GEN p)
+{
+  GEN da;
+  long vda;
+  a = redelt_i(a, N, p, &da, &vda);
+  if (da) a = RgX_Rg_div(a, da);
+  return a;
+}
+
+/* compute the Newton sums of g(x) mod p, assume deg g > 0 */
+GEN
+polsymmodp(GEN g, GEN p)
+{
+  pari_sp av;
+  long d = degpol(g), i, k;
+  GEN s, y, po2;
+
+  y = cgetg(d + 1, t_COL);
+  gel(y,1) = utoipos(d);
+  if (d == 1) return y;
+  /* k = 1, split off for efficiency */
+  po2 = shifti(p,-1); /* to be left on stack */
+  av = avma;
+  s = gel(g,d-1+2);
+  gel(y,2) = gerepileuptoint(av, centermodii(negi(s), p, po2));
+  for (k = 2; k < d; k++)
+  {
+    av = avma;
+    s = mului(k, remii(gel(g,d-k+2), p));
+    for (i = 1; i < k; i++) s = addii(s, mulii(gel(y,k-i+1), gel(g,d-i+2)));
+    togglesign_safe(&s);
+    gel(y,k+1) = gerepileuptoint(av, centermodii(s, p, po2));
+  }
+  return y;
+}
+
+/* compute the c first Newton sums modulo pp of the
+   characteristic polynomial of a/d mod chi, d > 0 power of p (NULL = gen_1),
+   a, chi in Zp[X], vda = v_p(da)
+   ns = Newton sums of chi */
+static GEN
+newtonsums(GEN p, GEN a, GEN da, long vda, GEN chi, long c, GEN pp, GEN ns)
+{
+  GEN va, pa, dpa, s;
+  long j, k, vdpa;
+  pari_sp av, lim;
+
+  a = centermod(a, pp); av = avma; lim = stack_lim(av, 1);
+  dpa = pa = NULL; /* -Wall */
+  vdpa = 0;
+  va = zerovec(c);
+  for (j = 1; j <= c; j++)
+  { /* pa/dpa = (a/d)^(j-1) mod (chi, pp), dpa = p^vdpa */
+    long degpa;
+    pa = j == 1? a: FpXQ_mul(pa, a, chi, pp);
+    degpa = degpol(pa);
+    if (degpa < 0) {
+      for (; j <= c; j++) gel(va,j) = gen_0;
+      return va;
+    }
+
+    if (da) {
+      dpa = j == 1? da: mulii(dpa, da);
+      vdpa += vda;
+      update_den(p, &pa, &dpa, &vdpa, &pp);
+    }
+    s = mulii(gel(pa,2), gel(ns,1)); /* k = 0 */
+    for (k=1; k<=degpa; k++) s = addii(s, mulii(gel(pa,k+2), gel(ns,k+1)));
+    if (da) {
+      GEN r;
+      s = dvmdii(s, dpa, &r);
+      if (r != gen_0) return NULL;
+    }
+    gel(va,j) = centermodii(s, pp, shifti(pp,-1));
+
+    if (low_stack(lim, stack_lim(av, 1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem, "newtonsums");
+      gerepileall(av, dpa?4:3, &pa, &va, &pp, &dpa);
+    }
+  }
+  return va;
+}
+
+/* compute the characteristic polynomial of a/da mod chi (a in Z[X]), given
+ * by its Newton sums to a precision of pp using Newton sums */
+static GEN
+newtoncharpoly(GEN pp, GEN p, GEN NS)
+{
+  long n = lg(NS)-1, j, k;
+  GEN c = cgetg(n + 2, t_VEC);
+
+  gel(c,1) = (n & 1 ? gen_m1: gen_1);
+  for (k = 2; k <= n+1; k++)
+  {
+    pari_sp av2 = avma;
+    GEN s = gen_0;
+    ulong z;
+    long v = u_pvalrem(k - 1, p, &z);
+    for (j = 1; j < k; j++)
+    {
+      GEN t = mulii(gel(NS,j), gel(c,k-j));
+      if (!odd(j)) t = negi(t);
+      s = addii(s, t);
+    }
+    if (v) {
+      s = gdiv(s, powiu(p, v));
+      if (typ(s) != t_INT) return NULL;
+    }
+    s = mulii(s, Fp_inv(utoipos(z), pp));
+    gel(c,k) = gerepileuptoint(av2, centermod(s, pp));
+  }
+  for (k = odd(n)? 1: 2; k <= n+1; k += 2) gel(c,k) = negi(gel(c,k));
+  return gtopoly(c, 0);
+}
+
+static void
+manage_cache(decomp_t *S, GEN f, GEN pp)
+{
+  GEN t = S->precns;
+
+  if (!t) t = mulii(S->pmf, powiu(S->p, S->df));
+  t = gmax(t, pp);
+
+  if (! S->precns || cmpii(S->precns, t) < 0)
+  {
+    if (DEBUGLEVEL>4)
+      err_printf("  Precision for cached Newton sums: %Ps -> %Ps\n",
+                 S->precns? S->precns: gen_0, t);
+    S->ns = polsymmodp(f, t);
+    S->precns = t;
+  }
+}
+
+/* return NULL if a mod f is not an integer
+ * The denominator of any integer in Zp[X]/(f) divides pdr */
+static GEN
+mycaract(decomp_t *S, GEN f, GEN a, GEN pp, GEN pdr)
+{
+  pari_sp av;
+  GEN d, chi, prec1, prec2, prec3, ns;
+  long vd, n = degpol(f);
+
+  if (gequal0(a)) return pol_0(varn(f));
+
+  a = QpX_remove_denom(a, S->p, &d, &vd);
+  prec1 = pp;
+  if (lgefint(S->p) == 3)
+    prec1 = mulii(prec1, powiu(S->p, factorial_lval(n, itou(S->p))));
+  if (d)
+  {
+    GEN p1 = powiu(d, n-1);
+    prec2 = mulii(prec1, p1);
+    prec3 = mulii(prec1, gmin(mulii(p1, d), pdr));
+  }
+  else
+    prec2 = prec3 = prec1;
+  manage_cache(S, f, prec3);
+
+  av = avma;
+  ns = newtonsums(S->p, a, d, vd, f, n, prec2, S->ns);
+  if (!ns) return NULL;
+  chi = newtoncharpoly(prec1, S->p, ns);
+  if (!chi) return NULL;
+  setvarn(chi, varn(f));
+  return gerepileupto(av, centermod(chi, pp));
+}
+
+static GEN
+get_nu(GEN chi, GEN p, long *ptl)
+{
+  GEN P = gel(FpX_factor(chi, p),1);
+  *ptl = lg(P) - 1; return gel(P,*ptl);
+}
+
+/* Factor characteristic polynomial chi of phi mod p. If it splits, update
+ * S->{phi, chi, nu} and return 1. In any case, set *nu to an irreducible
+ * factor mod p of chi */
+static int
+split_char(decomp_t *S, GEN chi, GEN phi, GEN phi0, GEN *nu)
+{
+  long l;
+  *nu  = get_nu(chi, S->p, &l);
+  if (l == 1) return 0; /* single irreducible factor: doesn't split */
+  /* phi o phi0 mod (p, f) */
+  S->phi = compmod(S->p, phi, phi0, S->f, S->p, 0);
+  S->chi = chi;
+  S->nu = *nu; return 1;
+}
+
+/* Return the prime element in Zp[phi], a t_INT (iff *Ep = 1) or QX;
+ * nup, chip are ZX. phi = NULL codes X
+ * If *Ep < oE or Ep divides Ediv (!=0) return NULL (uninteresting) */
+static GEN
+getprime(decomp_t *S, GEN phi, GEN chip, GEN nup, long *Lp, long *Ep,
+         long oE, long Ediv)
+{
+  GEN chin, q, qp;
+  long r, s;
+
+  if (phi && dvdii(constant_term(chip), S->psc))
+  {
+    chip = mycaract(S, S->chi, phi, S->pmf, S->prc);
+    if (dvdii(constant_term(chip), S->pmf))
+      chip = ZXQ_charpoly(phi, S->chi, varn(chip));
+  }
+  if (degpol(nup) == 1)
+  {
+    GEN c = gel(nup,2); /* nup = X + c */
+    chin = signe(c)? RgX_translate(chip, negi(c)): chip;
+  }
+  else
+    chin = ZXQ_charpoly(nup, chip, varn(chip));
+
+  vstar(S->p, chin, Lp, Ep);
+  if (*Ep < oE || (Ediv && Ediv % *Ep == 0)) return NULL;
+
+  if (*Ep == 1) return S->p;
+  (void)cbezout(*Lp, -*Ep, &r, &s); /* = 1 */
+  if (r <= 0)
+  {
+    long t = 1 + ((-r) / *Ep);
+    r += t * *Ep;
+    s += t * *Lp;
+  }
+  /* r > 0 minimal such that r L/E - s = 1/E
+   * pi = nu^r / p^s is an element of valuation 1/E,
+   * so is pi + O(p) since 1/E < 1. May compute nu^r mod p^(s+1) */
+  q = powiu(S->p, s); qp = mulii(q, S->p);
+  nup = FpXQ_powu(nup, r, S->chi, qp);
+  if (!phi) return RgX_Rg_div(nup, q); /* phi = X : no composition */
+  return compmod(S->p, nup, phi, S->chi, qp, -s);
+}
+
+static void
+kill_cache(decomp_t *S) { S->precns = NULL; }
+
+static int
+update_phi(decomp_t *S)
+{
+  GEN PHI = NULL, prc, psc, X = pol_x(varn(S->f));
+  long k;
+  for (k = 1;; k++)
+  {
+    kill_cache(S);
+    prc = ZpX_reduced_resultant_fast(S->chi, ZX_deriv(S->chi), S->p, S->vpsc);
+    if (!equalii(prc, S->psc)) break;
+
+    /* increase precision */
+    S->vpsc = maxss(S->vpsf, S->vpsc + 1);
+    S->psc = (S->vpsc == S->vpsf)? S->psf: mulii(S->psc, S->p);
+
+    PHI = S->phi0? compmod(S->p, S->phi, S->phi0, S->f, S->psc, 0)
+                 : S->phi;
+    PHI = gadd(PHI, ZX_Z_mul(X, mului(k, S->p)));
+    S->chi = mycaract(S, S->f, PHI, S->psc, S->pdf);
+  }
+  psc = mulii(sqri(prc), S->p);
+  S->chi = FpX_red(S->chi, psc);
+  if (!PHI) /* ok above for k = 1 */
+    PHI = S->phi0? compmod(S->p, S->phi, S->phi0, S->f, psc, 0)
+                 : S->phi;
+  S->phi = PHI;
+
+  /* may happen if p is unramified */
+  if (is_pm1(prc)) return 0;
+  S->psc = psc;
+  S->vpsc = 2*Z_pval(prc, S->p) + 1;
+  S->prc = mulii(prc, S->p); return 1;
+}
+
+/* return 1 if at least 2 factors mod p ==> chi splits
+ * Replace S->phi such that F increases (to D) */
+static int
+testb2(decomp_t *S, long D, GEN theta)
+{
+  long v = varn(S->chi), dlim = degpol(S->chi)-1;
+  GEN T0 = S->phi, chi, phi, nu;
+  if (DEBUGLEVEL>4) err_printf("  Increasing Fa\n");
+  for (;;)
+  {
+    phi = gadd(theta, random_FpX(dlim, v, S->p));
+    chi = mycaract(S, S->chi, phi, S->psf, S->prc);
+    /* phi non-primary ? */
+    if (split_char(S, chi, phi, T0, &nu)) return 1;
+    if (degpol(nu) == D) break;
+  }
+  /* F_phi=lcm(F_alpha, F_theta)=D and E_phi=E_alpha */
+  S->phi0 = T0;
+  S->chi = chi;
+  S->phi = phi;
+  S->nu = nu; return 0;
+}
+
+/* return 1 if at least 2 factors mod p ==> chi can be split.
+ * compute a new S->phi such that E = lcm(Ea, Et);
+ * A a ZX, T a t_INT (iff Et = 1, probably impossible ?) or QX */
+static int
+testc2(decomp_t *S, GEN A, long Ea, GEN T, long Et)
+{
+  GEN c, chi, phi, nu, T0 = S->phi;
+
+  if (DEBUGLEVEL>4) err_printf("  Increasing Ea\n");
+  if (Et == 1) /* same as other branch, split for efficiency */
+    c = A; /* Et = 1 => s = 1, r = 0, t = 0 */
+  else
+  {
+    long r, s, t;
+    (void)cbezout(Ea, Et, &r, &s); t = 0;
+    while (r < 0) { r = r + Et; t++; }
+    while (s < 0) { s = s + Ea; t++; }
+
+    /* A^s T^r / p^t */
+    c = RgXQ_mul(RgXQ_powu(A, s, S->chi), RgXQ_powu(T, r, S->chi), S->chi);
+    c = RgX_Rg_div(c, powiu(S->p, t));
+    c = redelt(c, S->psc, S->p);
+  }
+  phi = RgX_add(c,  pol_x(varn(S->chi)));
+  chi = mycaract(S, S->chi, phi, S->psf, S->prc);
+  if (split_char(S, chi, phi, T0, &nu)) return 1;
+  /* E_phi = lcm(E_alpha,E_theta) */
+  S->phi0 = T0;
+  S->chi = chi;
+  S->phi = phi;
+  S->nu = nu; return 0;
+}
+
+/* Return h^(-degpol(P)) P(x * h) if result is integral, NULL otherwise */
+static GEN
+ZX_rescale_inv(GEN P, GEN h)
+{
+  long i, l = lg(P);
+  GEN Q = cgetg(l,t_POL), hi = h;
+  gel(Q,l-1) = gel(P,l-1);
+  for (i=l-2; i>=2; i--)
+  {
+    GEN r;
+    gel(Q,i) = dvmdii(gel(P,i), hi, &r);
+    if (signe(r)) return NULL;
+    if (i == 2) break;
+    hi = mulii(hi,h);
+  }
+  Q[1] = P[1]; return Q;
+}
+
+/* x p^-eq nu^-er mod p */
+static GEN
+get_gamma(decomp_t *S, GEN x, long eq, long er)
+{
+  GEN q, g = x, Dg = powiu(S->p, eq);
+  long vDg = eq;
+  if (er)
+  {
+    if (!S->invnu)
+    {
+      while (gdvd(S->chi, S->nu)) S->nu = RgX_Rg_add(S->nu, S->p);
+      S->invnu = QXQ_inv(S->nu, S->chi);
+      S->invnu = redelt_i(S->invnu, S->psc, S->p, &S->Dinvnu, &S->vDinvnu);
+    }
+    if (S->Dinvnu) {
+      Dg = mulii(Dg, powiu(S->Dinvnu, er));
+      vDg += er * S->vDinvnu;
+    }
+    q = mulii(S->p, Dg);
+    g = ZX_mul(g, FpXQ_powu(S->invnu, er, S->chi, q));
+    g = FpX_rem(g, S->chi, q);
+    update_den(S->p, &g, &Dg, &vDg, NULL);
+    g = centermod(g, mulii(S->p, Dg));
+  }
+  if (!is_pm1(Dg)) g = RgX_Rg_div(g, Dg);
+  return g;
+}
+static GEN
+get_g(decomp_t *S, long Ea, long L, long E, GEN beta, GEN *pchig,
+      long *peq, long *per)
+{
+  long eq, er;
+  GEN g, chig, chib = NULL;
+  for(;;) /* at most twice */
+  {
+    if (L < 0)
+    {
+      chib = ZXQ_charpoly(beta, S->chi, varn(S->chi));
+      vstar(S->p, chib, &L, &E);
+    }
+    eq = L / E; er = L*Ea / E - eq*Ea;
+    /* floor(L Ea/E) = eq Ea + er */
+    if (er || !chib)
+    { /* g might not be an integer ==> chig = NULL */
+      g = get_gamma(S, beta, eq, er);
+      chig = mycaract(S, S->chi, g, S->psc, S->prc);
+    }
+    else
+    { /* g = beta/p^eq, special case of the above */
+      GEN h = powiu(S->p, eq);
+      g = RgX_Rg_div(beta, h);
+      chig = ZX_rescale_inv(chib, h); /* chib(x h) / h^N */
+      if (chig) chig = FpX_red(chig, S->pmf);
+    }
+    /* either success or second consecutive failure */
+    if (chig || chib) break;
+    /* if g fails the v*-test, v(beta) was wrong. Retry once */
+    L = -1;
+  }
+  *pchig = chig; *peq = eq; *per = er; return g;
+}
+
+/* return 1 if at least 2 factors mod p ==> chi can be split */
+static int
+loop(decomp_t *S, long Ea)
+{
+  pari_sp av = avma, limit = stack_lim(av, 1);
+  GEN beta = FpXQ_powu(S->nu, Ea, S->chi, S->p);
+  long N = degpol(S->f), v = varn(S->f);
+  S->invnu = NULL;
+  for (;;)
+  { /* beta tends to a factor of chi */
+    long L, i, Fg, eq, er;
+    GEN chig = NULL, d, g, nug;
+
+    if (DEBUGLEVEL>4) err_printf("  beta = %Ps\n", beta);
+    L = ZpX_resultant_val(S->chi, beta, S->p, S->mf+1);
+    if (L > S->mf) L = -1; /* from scratch */
+    g = get_g(S, Ea, L, N, beta, &chig, &eq, &er);
+    if (DEBUGLEVEL>4) err_printf("  (eq,er) = (%ld,%ld)\n", eq,er);
+    /* g = beta p^-eq  nu^-er (a unit), chig = charpoly(g) */
+    if (split_char(S, chig, g,S->phi, &nug)) return 1;
+
+    Fg = degpol(nug);
+    if (Fg == 1)
+    { /* frequent special case nug = x - d */
+      long Le, Ee;
+      GEN chie, nue, e, pie;
+      d = negi(gel(nug,2));
+      chie = RgX_translate(chig, d);
+      nue = pol_x(v);
+      e = RgX_Rg_sub(g, d);
+      pie = getprime(S, e, chie, nue, &Le, &Ee,  0,Ea);
+      if (pie) return testc2(S, S->nu, Ea, pie, Ee);
+    }
+    else
+    {
+      long Fa = degpol(S->nu), vdeng;
+      GEN deng, numg, nume;
+      if (Fa % Fg) return testb2(S, clcm(Fa,Fg), g);
+      /* nu & nug irreducible mod p, deg nug | deg nu. To improve beta, look
+       * for a root d of nug in Fp[phi] such that v_p(g - d) > 0 */
+      if (ZX_equal(nug, S->nu))
+        d = pol_x(v);
+      else
+      {
+        if (!p_is_prime(S)) pari_err_PRIME("FpX_ffisom",S->p);
+        d = FpX_ffisom(nug, S->nu, S->p);
+      }
+      /* write g = numg / deng, e = nume / deng */
+      numg = QpX_remove_denom(g, S->p, &deng, &vdeng);
+      for (i = 1; i <= Fg; i++)
+      {
+        GEN chie, nue, e;
+        if (i != 1) d = FpXQ_pow(d, S->p, S->nu, S->p); /* next root */
+        nume = ZX_sub(numg, ZX_Z_mul(d, deng));
+        /* test e = nume / deng */
+        if (ZpX_resultant_val(S->chi, nume, S->p, vdeng*N+1) <= vdeng*N)
+          continue;
+        e = RgX_Rg_div(nume, deng);
+        chie = mycaract(S, S->chi, e, S->psc, S->prc);
+        if (split_char(S, chie, e,S->phi, &nue)) return 1;
+        if (RgX_is_monomial(nue))
+        { /* v_p(e) = v_p(g - d) > 0 */
+          long Le, Ee;
+          GEN pie;
+          pie = getprime(S, e, chie, nue, &Le, &Ee,  0,Ea);
+          if (pie) return testc2(S, S->nu, Ea, pie, Ee);
+          break;
+        }
+      }
+      if (i > Fg)
+      {
+        if (!p_is_prime(S)) pari_err_PRIME("nilord",S->p);
+        pari_err_BUG("nilord (no root)");
+      }
+    }
+    if (eq) d = gmul(d, powiu(S->p,  eq));
+    if (er) d = gmul(d, gpowgs(S->nu, er));
+    beta = gsub(beta, d);
+
+    if (low_stack(limit,stack_lim(av,1)))
+    {
+      if (DEBUGMEM > 1) pari_warn(warnmem, "nilord");
+      gerepileall(av, S->invnu? 5: 3, &beta, &(S->precns), &(S->ns), &(S->invnu), &(S->Dinvnu));
+    }
+  }
+}
+
+static long
+loop_init(decomp_t *S, GEN *popa, long *poE)
+{
+  long oE = *poE;
+  GEN opa = *popa;
+  for(;;)
+  {
+    long l, La, Ea; /* N.B If oE = 0, getprime cannot return NULL */
+    GEN pia  = getprime(S, NULL, S->chi, S->nu, &La, &Ea, oE,0);
+    if (pia) { /* success, we break out in THIS loop */
+      opa = (typ(pia) == t_POL)? RgX_RgXQ_eval(pia, S->phi, S->f): pia;
+      oE = Ea;
+      if (La == 1) break; /* no need to change phi so that nu = pia */
+    }
+    /* phi += prime elt */
+    S->phi = typ(opa) == t_INT? RgX_Rg_add_shallow(S->phi, opa)
+                              : RgX_add(S->phi, opa);
+    /* recompute char. poly. chi from scratch */
+    kill_cache(S);
+    S->chi = mycaract(S, S->f, S->phi, S->psf, S->pdf);
+    S->nu = get_nu(S->chi, S->p, &l);
+    if (l > 1) return l; /* we can get a decomposition */
+    if (!update_phi(S)) return 1; /* unramified / irreducible */
+    if (pia) break;
+  }
+  *poE = oE; *popa = opa; return 0;
+}
+/* flag != 0 iff we're looking for the p-adic factorization,
+   in which case it is the p-adic precision we want */
+static GEN
+nilord(decomp_t *S, GEN dred, long flag)
+{
+  GEN p = S->p;
+  long oE, l, N  = degpol(S->f), v = varn(S->f);
+  GEN opa; /* t_INT or QX */
+
+  if (DEBUGLEVEL>2)
+  {
+    err_printf("  entering Nilord");
+    if (DEBUGLEVEL>4)
+    {
+      err_printf(" with parameters: %Ps^%ld\n", p, S->df);
+      err_printf("  fx = %Ps, gx = %Ps", S->f, S->nu);
+    }
+    err_printf("\n");
+  }
+
+  S->psc = mulii(sqri(dred), p);
+  S->vpsc= 2*S->df + 1;
+  S->prc = mulii(dred, p);
+  S->psf = S->psc;
+  S->vpsf = S->vpsc;
+  S->chi = FpX_red(S->f, S->psc);
+  S->phi = pol_x(v);
+  S->pmf = powiu(p, S->mf+1);
+  S->precns = NULL;
+  oE = 0;
+  opa = NULL; /* -Wall */
+  for(;;)
+  {
+    long Fa = degpol(S->nu);
+    S->phi0 = NULL; /* no delayed composition */
+    l = loop_init(S, &opa, &oE);
+    if (l > 1) return Decomp(S,flag);
+    if (l == 1) break;
+    if (DEBUGLEVEL>4) err_printf("  (Fa, oE) = (%ld,%ld)\n", Fa, oE);
+    if (oE*Fa == N)
+    { /* O = Zp[phi] */
+      if (flag) return NULL;
+      return dbasis(p, S->f, S->mf, redelt(S->phi,sqri(p),p), NULL);
+    }
+    if (loop(S, oE)) return Decomp(S,flag);
+    if (!update_phi(S)) break; /* unramified / irreducible */
+  }
+  if (flag) return NULL;
+  S->nu = get_nu(S->chi, S->p, &l);
+  return l != 1? Decomp(S,flag): dbasis(p, S->f, S->mf, S->phi, S->chi);
+}
+
+GEN
+maxord_i(GEN p, GEN f, long mf, GEN w, long flag)
+{
+  long l = lg(w)-1;
+  GEN h = gel(w,l); /* largest factor */
+  GEN D = ZpX_reduced_resultant_fast(f, ZX_deriv(f), p, mf);
+  decomp_t S;
+
+  S.f = f;
+  S.pisprime = -1;
+  S.p = p;
+  S.mf = mf;
+  S.nu = h;
+  S.df = Z_pval(D, p);
+  S.pdf = powiu(p, S.df);
+  if (l == 1) return nilord(&S, D, flag);
+  if (flag && flag <= mf) flag = mf + 1;
+  S.phi = pol_x(varn(f));
+  S.chi = f; return Decomp(&S, flag);
+}
+
+/* DT = multiple of disc(T) or NULL
+ * Return a multiple of the denominator of an algebraic integer (in Q[X]/(T))
+ * when expressed in terms of the power basis */
+GEN
+indexpartial(GEN T, GEN DT)
+{
+  pari_sp av = avma;
+  long i, nb;
+  GEN fa, E, P, res = gen_1, dT = ZX_deriv(T);
+
+  if (!DT) DT = ZX_disc(T);
+  fa = absi_factor_limit(DT, 0);
+  P = gel(fa,1);
+  E = gel(fa,2); nb = lg(P)-1;
+  for (i = 1; i <= nb; i++)
+  {
+    long e = itou(gel(E,i)), e2 = e >> 1;
+    GEN p = gel(P,i), q = p;
+    if (i == nb)
+      q = powiu(p, (odd(e) && !BPSW_psp(p))? e2+1: e2);
+    else if (e2 >= 2)
+      q = ZpX_reduced_resultant_fast(T, dT, p, e2);
+    res = mulii(res, q);
+  }
+  return gerepileuptoint(av,res);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*    2-ELT REPRESENTATION FOR PRIME IDEALS (dividing index)       */
+/*                                                                 */
+/*******************************************************************/
+/* to compute norm of elt in basis form */
+typedef struct {
+  long r1;
+  GEN M;  /* via embed_norm */
+
+  GEN D, w, T; /* via resultant if M = NULL */
+} norm_S;
+
+static GEN
+get_norm(norm_S *S, GEN a)
+{
+  if (S->M)
+  {
+    long e;
+    GEN N = grndtoi( embed_norm(RgM_RgC_mul(S->M, a), S->r1), &e );
+    if (e > -5) pari_err_PREC( "get_norm");
+    return N;
+  }
+  if (S->w) a = RgV_RgC_mul(S->w, a);
+  return ZX_resultant_all(S->T, a, S->D, 0);
+}
+static void
+init_norm(norm_S *S, GEN nf, GEN p)
+{
+  GEN T = nf_get_pol(nf);
+  long N = degpol(T);
+
+  S->r1 = 0;   /* -Wall */
+  S->M = NULL; /* -Wall */
+  S->D = NULL; /* -Wall */
+  S->w = NULL; /* -Wall */
+  S->T = NULL; /* -Wall */
+  if (typ(gel(nf,5)) == t_VEC) /* beware dummy nf from rnf/makenfabs */
+  {
+    GEN M = nf_get_M(nf);
+    long ex = gexpo(M) + gexpo(mului(8 * N, p));
+    /* enough prec to use embed_norm */
+    S->r1 = nf_get_r1(nf);
+    if (N * ex <= prec2nbits(gprecision(M))) S->M = M;
+  }
+  if (!S->M)
+  {
+    GEN D, w = Q_remove_denom(nf_get_zk(nf), &D), Dp = sqri(p);
+    long i;
+    if (!D) w = leafcopy(w);
+    else {
+      GEN w1 = D;
+      long v = Z_pval(D, p);
+      D = powiu(p, v);
+      Dp = mulii(D, Dp);
+      gel(w, 1) = remii(w1, Dp);
+    }
+    for (i=2; i<=N; i++) gel(w,i) = FpX_red(gel(w,i), Dp);
+    S->D = D;
+    S->w = w;
+    S->T = T;
+  }
+}
+/* f = f(pr/p), q = p^(f+1), a in pr.
+ * Return 1 if v_pr(a) = 1, and 0 otherwise */
+static int
+is_uniformizer(GEN a, GEN q, norm_S *S)
+{ return (remii(get_norm(S,a), q) != gen_0); }
+
+/* Return x * y, x, y are t_MAT (Fp-basis of in O_K/p), assume (x,y)=1.
+ * Either x or y may be NULL (= O_K), not both */
+static GEN
+mul_intersect(GEN x, GEN y, GEN p)
+{
+  if (!x) return y;
+  if (!y) return x;
+  return FpM_intersect(x, y, p);
+}
+/* Fp-basis of (ZK/pr): applied to the primes found in primedec_aux() */
+static GEN
+Fp_basis(GEN nf, GEN pr)
+{
+  long i, j, l;
+  GEN x, y;
+  /* already in basis form (from Buchman-Lenstra) ? */
+  if (typ(pr) == t_MAT) return pr;
+  /* ordinary prid (from Kummer) */
+  x = idealhnf_two(nf, pr);
+  l = lg(x);
+  y = cgetg(l, t_MAT);
+  for (i=j=1; i<l; i++)
+    if (gequal1(gcoeff(x,i,i))) gel(y,j++) = gel(x,i);
+  setlg(y, j); return y;
+}
+/* Let Ip = prod_{ P | p } P be the p-radical. The list L contains the
+ * P (mod Ip) seen as sub-Fp-vector spaces of ZK/Ip.
+ * Return the list of (Ip / P) (mod Ip).
+ * N.B: All ideal multiplications are computed as intersections of Fp-vector
+ * spaces. */
+static GEN
+get_LV(GEN nf, GEN L, GEN p, long N)
+{
+  long i, l = lg(L)-1;
+  GEN LV, LW, A, B;
+
+  LV = cgetg(l+1, t_VEC);
+  if (l == 1) { gel(LV,1) = matid(N); return LV; }
+  LW = cgetg(l+1, t_VEC);
+  for (i=1; i<=l; i++) gel(LW,i) = Fp_basis(nf, gel(L,i));
+
+  /* A[i] = L[1]...L[i-1], i = 2..l */
+  A = cgetg(l+1, t_VEC); gel(A,1) = NULL;
+  for (i=1; i < l; i++) gel(A,i+1) = mul_intersect(gel(A,i), gel(LW,i), p);
+  /* B[i] = L[i+1]...L[l], i = 1..(l-1) */
+  B = cgetg(l+1, t_VEC); gel(B,l) = NULL;
+  for (i=l; i>=2; i--) gel(B,i-1) = mul_intersect(gel(B,i), gel(LW,i), p);
+  for (i=1; i<=l; i++) gel(LV,i) = mul_intersect(gel(A,i), gel(B,i), p);
+  return LV;
+}
+
+static void
+errprime(GEN p) { pari_err_PRIME("idealprimedec",p); }
+
+/* P = Fp-basis (over O_K/p) for pr.
+ * V = Z-basis for I_p/pr. ramif != 0 iff some pr|p is ramified.
+ * Return a p-uniformizer for pr. Assume pr not inert, i.e. m > 0 */
+static GEN
+uniformizer(GEN nf, norm_S *S, GEN P, GEN V, GEN p, int ramif)
+{
+  long i, l, f, m = lg(P)-1, N = nf_get_degree(nf);
+  GEN u, Mv, x, q;
+
+  f = N - m; /* we want v_p(Norm(x)) = p^f */
+  q = powiu(p,f+1);
+
+  u = FpM_FpC_invimage(shallowconcat(P, V), col_ei(N,1), p);
+  setlg(u, lg(P));
+  u = centermod(ZM_ZC_mul(P, u), p);
+  if (is_uniformizer(u, q, S)) return u;
+  if (signe(gel(u,1)) <= 0) /* make sure u[1] in ]-p,p] */
+    gel(u,1) = addii(gel(u,1), p); /* try u + p */
+  else
+    gel(u,1) = subii(gel(u,1), p); /* try u - p */
+  if (!ramif || is_uniformizer(u, q, S)) return u;
+
+  /* P/p ramified, u in P^2, not in Q for all other Q|p */
+  Mv = zk_multable(nf, unnf_minus_x(u));
+  l = lg(P);
+  for (i=1; i<l; i++)
+  {
+    x = centermod(ZC_add(u, ZM_ZC_mul(Mv, gel(P,i))), p);
+    if (is_uniformizer(x, q, S)) return x;
+  }
+  errprime(p);
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                   BUCHMANN-LENSTRA ALGORITHM                    */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+mk_pr(GEN p, GEN u, long e, long f, GEN t)
+{ return mkvec5(p, u, utoipos(e), utoipos(f), t); }
+
+/* pr = (p,u) of ramification index e */
+GEN
+primedec_apply_kummer(GEN nf,GEN u,long e,GEN p)
+{
+  GEN t, T = nf_get_pol(nf);
+  long f = degpol(u), N = degpol(T);
+
+  if (f == N) /* inert */
+  {
+    u = scalarcol_shallow(p,N);
+    t = gen_1;
+  }
+  else
+  { /* make sure v_pr(u) = 1 (automatic if e>1) */
+    t = poltobasis(nf, FpX_div(T,u,p));
+    t = centermod(t, p);
+    u = FpX_center(u, p, shifti(p,-1));
+    if (e == 1)
+    {
+      norm_S S;
+      S.D = S.w = S.M = NULL; S.T = T;
+      if (!is_uniformizer(u, powiu(p,f+1), &S)) gel(u,2) = addii(gel(u,2), p);
+    }
+    u = poltobasis(nf,u);
+    t = zk_scalar_or_multable(nf, t);
+  }
+  return mk_pr(p,u,e,f,t);
+}
+
+/* return a Z basis of Z_K's p-radical, phi = x--> x^p-x */
+static GEN
+pradical(GEN nf, GEN p, GEN *phi)
+{
+  long i, N = nf_get_degree(nf);
+  GEN q,m,frob,rad;
+
+  /* matrix of Frob: x->x^p over Z_K/p */
+  frob = cgetg(N+1,t_MAT);
+  for (i=1; i<=N; i++)
+    gel(frob,i) = pow_ei_mod_p(nf,i,p, p);
+
+  m = frob; q = p;
+  while (cmpiu(q,N) < 0) { q = mulii(q,p); m = FpM_mul(m, frob, p); }
+  rad = FpM_ker(m, p); /* m = Frob^k, s.t p^k >= N */
+  for (i=1; i<=N; i++)
+    gcoeff(frob,i,i) = subis(gcoeff(frob,i,i), 1);
+  *phi = frob; return rad;
+}
+
+/* return powers of a: a^0, ... , a^d,  d = dim A */
+static GEN
+get_powers(GEN mul, GEN p)
+{
+  long i, d = lgcols(mul);
+  GEN z, pow = cgetg(d+2,t_MAT), P = pow+1;
+
+  gel(P,0) = scalarcol_shallow(gen_1, d-1);
+  z = gel(mul,1);
+  for (i=1; i<=d; i++)
+  {
+    gel(P,i) = z; /* a^i */
+    if (i!=d) z = FpM_FpC_mul(mul, z, p);
+  }
+  return pow;
+}
+
+/* minimal polynomial of a in A (dim A = d).
+ * mul = multiplication table by a in A */
+static GEN
+pol_min(GEN mul, GEN p)
+{
+  pari_sp av = avma;
+  GEN z = FpM_deplin(get_powers(mul, p), p);
+  return gerepilecopy(av, RgV_to_RgX(z,0));
+}
+
+static GEN
+get_pr(GEN nf, norm_S *S, GEN p, GEN P, GEN V, int ramif, long N)
+{
+  GEN u, t;
+  long e, f;
+
+  if (typ(P) == t_VEC) return P; /* already done (Kummer) */
+  f = N - (lg(P)-1);
+  /* P = (p,u) prime. t is an anti-uniformizer: Z_K + t/p Z_K = P^(-1),
+   * so that v_P(t) = e(P/p)-1 */
+  if (f == N) {
+    u = scalarcol_shallow(p,N);
+    t = gen_1;
+    e = 1;
+  } else {
+    u = uniformizer(nf, S, P, V, p, ramif);
+    t = FpM_deplin(zk_multable(nf,u), p);
+    e = ramif? 1 + ZC_nfval(nf,t,mk_pr(p,u,0,0,t)): 1;
+    t = zk_scalar_or_multable(nf, t);
+  }
+  return mk_pr(p,u,e,f,t);
+}
+
+static GEN
+primedec_end(GEN nf, GEN L, GEN p)
+{
+  long i, l = lg(L), N = nf_get_degree(nf);
+  GEN Lpr = cgetg(l, t_VEC);
+  GEN LV = get_LV(nf, L,p,N);
+  int ramif = dvdii(nf_get_disc(nf), p);
+  norm_S S; init_norm(&S, nf, p);
+  for (i=1; i<l; i++)
+    gel(Lpr,i) = get_pr(nf, &S, p, gel(L,i), gel(LV,i), ramif, N);
+  return Lpr;
+}
+
+/* prime ideal decomposition of p */
+static GEN
+primedec_aux(GEN nf, GEN p)
+{
+  GEN E, F, L, Ip, H, phi, mat1, f, g, h, p1, UN, T = nf_get_pol(nf);
+  long i, k, c, iL, N;
+
+  F = FpX_factor(T, p);
+  E = gel(F,2);
+  F = gel(F,1);
+
+  k = lg(F); if (k == 1) errprime(p);
+  if ( !dvdii(nf_get_index(nf),p) ) /* p doesn't divide index */
+  {
+    L = cgetg(k,t_VEC);
+    for (i=1; i<k; i++)
+      gel(L,i) = primedec_apply_kummer(nf,gel(F,i), E[i],p);
+    return L;
+  }
+
+  g = FpXV_prod(F, p);
+  h = FpX_div(T,g,p);
+  f = FpX_red(ZX_Z_divexact(ZX_sub(ZX_mul(g,h), T), p), p);
+
+  N = degpol(T);
+  L = cgetg(N+1,t_VEC); iL = 1;
+  for (i=1; i<k; i++)
+    if (E[i] == 1 || signe(FpX_rem(f,gel(F,i),p)))
+      gel(L,iL++) = primedec_apply_kummer(nf,gel(F,i), E[i],p);
+    else /* F[i] | (f,g,h), happens at least once by Dedekind criterion */
+      E[i] = 0;
+
+  /* phi matrix of x -> x^p - x in algebra Z_K/p */
+  Ip = pradical(nf,p,&phi);
+
+  /* split etale algebra Z_K / (p,Ip) */
+  h = cgetg(N+1,t_VEC);
+  if (iL > 1)
+  { /* split off Kummer factors */
+    GEN mulbeta, beta = NULL;
+    for (i=1; i<k; i++)
+      if (!E[i]) beta = beta? FpX_mul(beta, gel(F,i), p): gel(F,i);
+    if (!beta) errprime(p);
+    beta = FpC_red(poltobasis(nf,beta), p);
+
+    mulbeta = FpM_red(zk_multable(nf, beta), p);
+    p1 = shallowconcat(mulbeta, Ip);
+    /* Fp-base of ideal (Ip, beta) in ZK/p */
+    gel(h,1) = FpM_image(p1, p);
+  }
+  else
+    gel(h,1) = Ip;
+
+  UN = col_ei(N, 1);
+  for (c=1; c; c--)
+  { /* Let A:= (Z_K/p) / Ip; try to split A2 := A / Im H ~ Im M2
+       H * ? + M2 * Mi2 = Id_N ==> M2 * Mi2 projector A --> A2 */
+    GEN M, Mi, M2, Mi2, phi2;
+    long dim;
+
+    H = gel(h,c); k = lg(H)-1;
+    M   = FpM_suppl(shallowconcat(H,UN), p);
+    Mi  = FpM_inv(M, p);
+    M2  = vecslice(M, k+1,N); /* M = (H|M2) invertible */
+    Mi2 = rowslice(Mi,k+1,N);
+    /* FIXME: FpM_mul(,M2) could be done with vecpermute */
+    phi2 = FpM_mul(Mi2, FpM_mul(phi,M2, p), p);
+    mat1 = FpM_ker(phi2, p);
+    dim = lg(mat1)-1; /* A2 product of 'dim' fields */
+    if (dim > 1)
+    { /* phi2 v = 0 <==> a = M2 v in Ker phi */
+      GEN R, a, mula, mul2, v = gel(mat1,2);
+      long n;
+
+      a = FpM_FpC_mul(M2,v, p);
+      mula = zk_scalar_or_multable(nf, a); /* not a scalar */
+      mula = FpM_red(mula, p);
+      mul2 = FpM_mul(Mi2, FpM_mul(mula,M2, p), p);
+      R = FpX_roots(pol_min(mul2,p), p); /* totally split mod p */
+
+      n = lg(R)-1;
+      for (i=1; i<=n; i++)
+      {
+        GEN r = gel(R,i), I = RgM_Rg_add_shallow(mula, negi(r));
+        gel(h,c++) = FpM_image(shallowconcat(H, I), p);
+      }
+      if (n == dim)
+        for (i=1; i<=n; i++) { H = gel(h,--c); gel(L,iL++) = H; }
+    }
+    else /* A2 field ==> H maximal, f = N-k = dim(A2) */
+      gel(L,iL++) = H;
+  }
+  setlg(L, iL);
+  return primedec_end(nf, L, p);
+}
+
+GEN
+idealprimedec(GEN nf, GEN p)
+{
+  pari_sp av = avma;
+  if (typ(p) != t_INT) pari_err_TYPE("idealprimedec",p);
+  return gerepileupto(av, gen_sort(primedec_aux(checknf(nf),p),
+                                   (void*)&cmp_prime_over_p, &cmp_nodata));
+}
+
+/* return [Fp[x]: Fp] */
+static long
+ffdegree(GEN x, GEN frob, GEN p)
+{
+  pari_sp av = avma;
+  long d, f = lg(frob)-1;
+  GEN y = x;
+
+  for (d=1; d < f; d++)
+  {
+    y = FpM_FpC_mul(frob, y, p);
+    if (ZV_equal(y, x)) break;
+  }
+  avma = av; return d;
+}
+
+static GEN
+lift_to_zk(GEN v, GEN c, long N)
+{
+  GEN w = zerocol(N);
+  long i, l = lg(c);
+  for (i=1; i<l; i++) gel(w,c[i]) = gel(v,i);
+  return w;
+}
+
+/* return integral x = 0 mod p/pr^e, (x,pr) = 1.
+ * Don't reduce mod p here: caller may need result mod pr^k */
+GEN
+special_anti_uniformizer(GEN nf, GEN pr)
+{
+  GEN q, b = pr_get_tau(pr);
+  long e = pr_get_e(pr);
+  if (e == 1) return b;
+  q = powiu(pr_get_p(pr), e-1);
+  if (typ(b) == t_MAT)
+    return ZM_Z_divexact(ZM_powu(b,e), q);
+  else
+    return ZC_Z_divexact(nfpow_u(nf,b,e), q);
+}
+
+/* return t = 1 mod pr, t = 0 mod p / pr^e(pr/p) */
+static GEN
+anti_uniformizer2(GEN nf, GEN pr)
+{
+  GEN p = pr_get_p(pr), z;
+  long N = nf_get_degree(nf);
+  if (pr_get_e(pr)*pr_get_f(pr) == N) return col_ei(N, 1);
+
+  z = special_anti_uniformizer(nf,pr);
+  if (typ(z) == t_MAT)
+    z = FpM_red(z,p);
+  else
+  {
+    z = FpC_red(z,p);
+    z = zk_scalar_or_multable(nf, z); /* not a scalar */
+  }
+  z = ZM_hnfmodid(z, p);
+  z = idealaddtoone_i(nf, pr, z);
+  return unnf_minus_x(z);
+}
+
+#define mpr_TAU 1
+#define mpr_FFP 2
+#define mpr_PR  3
+#define mpr_T   4
+#define mpr_NFP 5
+#define SMALLMODPR 4
+#define LARGEMODPR 6
+static GEN
+modpr_TAU(GEN modpr)
+{
+  GEN tau = gel(modpr,mpr_TAU);
+  return isintzero(tau)? NULL: tau;
+}
+
+/* prh = HNF matrix, which is identity but for the first line. Return a
+ * projector to ZK / prh ~ Z/prh[1,1] */
+GEN
+dim1proj(GEN prh)
+{
+  long i, N = lg(prh)-1;
+  GEN ffproj = cgetg(N+1, t_VEC);
+  GEN x, q = gcoeff(prh,1,1);
+  gel(ffproj,1) = gen_1;
+  for (i=2; i<=N; i++)
+  {
+    x = gcoeff(prh,1,i);
+    if (signe(x)) x = subii(q,x);
+    gel(ffproj,i) = x;
+  }
+  return ffproj;
+}
+
+/* p not necessarily prime, but coprime to denom(basis) */
+GEN
+get_proj_modT(GEN basis, GEN T, GEN p)
+{
+  long i, l = lg(basis), f = degpol(T);
+  GEN z = cgetg(l, t_MAT);
+  for (i = 1; i < l; i++)
+  {
+    GEN cx, w = gel(basis,i);
+    if (typ(w) == t_INT)
+      w = scalarcol_shallow(w, f);
+    else
+    {
+      w = Q_primitive_part(w, &cx);
+      w = FpXQ_red(w, T, p);
+      if (cx) w = FpX_Fp_mul(w, Rg_to_Fp(cx, p), p);
+      w = RgX_to_RgV(w, f);
+    }
+    gel(z,i) = w; /* w_i mod (T,p) */
+  }
+  return z;
+}
+
+/* initialize reduction mod pr; if zk = 1, will only init data required to
+ * reduce *integral* element.  Realize (O_K/pr) as Fp[X] / (T), for a
+ * *monic* T */
+static GEN
+modprinit(GEN nf, GEN pr, int zk)
+{
+  pari_sp av = avma;
+  GEN res, tau, mul, x, p, T, pow, ffproj, nfproj, prh, c;
+  long N, i, k, f;
+
+  nf = checknf(nf); checkprid(pr);
+  f = pr_get_f(pr);
+  N = nf_get_degree(nf);
+  prh = idealhnf_two(nf, pr);
+  tau = zk? gen_0: anti_uniformizer2(nf, pr);
+  p = pr_get_p(pr);
+
+  if (f == 1)
+  {
+    res = cgetg(SMALLMODPR, t_COL);
+    gel(res,mpr_TAU) = tau;
+    gel(res,mpr_FFP) = dim1proj(prh);
+    gel(res,mpr_PR) = pr; return gerepilecopy(av, res);
+  }
+
+  c = cgetg(f+1, t_VECSMALL);
+  ffproj = cgetg(N+1, t_MAT);
+  for (k=i=1; i<=N; i++)
+  {
+    x = gcoeff(prh, i,i);
+    if (!is_pm1(x)) { c[k] = i; gel(ffproj,i) = col_ei(N, i); k++; }
+    else
+      gel(ffproj,i) = ZC_neg(gel(prh,i));
+  }
+  ffproj = rowpermute(ffproj, c);
+  if (! dvdii(nf_get_index(nf), p))
+  {
+    GEN basis = nf_get_zk(nf);
+    if (N == f) T = nf_get_pol(nf); /* pr inert */
+    else
+    {
+      T = RgV_RgC_mul(Q_primpart(basis), pr_get_gen(pr));
+      T = FpX_normalize(T,p);
+      basis = vecpermute(basis, c);
+    }
+    T = FpX_red(T, p);
+    ffproj = FpM_mul(get_proj_modT(basis, T, p), ffproj, p);
+
+    res = cgetg(SMALLMODPR+1, t_COL);
+    gel(res,mpr_TAU) = tau;
+    gel(res,mpr_FFP) = ffproj;
+    gel(res,mpr_PR) = pr;
+    gel(res,mpr_T) = T; return gerepilecopy(av, res);
+  }
+
+  if (uisprime(f))
+  {
+    mul = ei_multable(nf, c[2]);
+    mul = vecpermute(mul, c);
+  }
+  else
+  {
+    GEN v, u, u2, frob;
+    long deg,deg1,deg2;
+
+    /* matrix of Frob: x->x^p over Z_K/pr = < w[c1], ..., w[cf] > over Fp */
+    frob = cgetg(f+1, t_MAT);
+    for (i=1; i<=f; i++)
+    {
+      x = pow_ei_mod_p(nf,c[i],p, p);
+      gel(frob,i) = FpM_FpC_mul(ffproj, x, p);
+    }
+    u = col_ei(f,2); k = 2;
+    deg1 = ffdegree(u, frob, p);
+    while (deg1 < f)
+    {
+      k++; u2 = col_ei(f, k);
+      deg2 = ffdegree(u2, frob, p);
+      deg = clcm(deg1,deg2);
+      if (deg == deg1) continue;
+      if (deg == deg2) { deg1 = deg2; u = u2; continue; }
+      u = ZC_add(u, u2);
+      while (ffdegree(u, frob, p) < deg) u = ZC_add(u, u2);
+      deg1 = deg;
+    }
+    v = lift_to_zk(u,c,N);
+
+    mul = cgetg(f+1,t_MAT);
+    gel(mul,1) = v; /* assume w_1 = 1 */
+    for (i=2; i<=f; i++) gel(mul,i) = zk_ei_mul(nf,v,c[i]);
+  }
+
+  /* Z_K/pr = Fp(v), mul = mul by v */
+  mul = FpM_red(mul, p);
+  mul = FpM_mul(ffproj, mul, p);
+
+  pow = get_powers(mul, p);
+  T = RgV_to_RgX(FpM_deplin(pow, p), nf_get_varn(nf));
+  nfproj = cgetg(f+1, t_MAT);
+  for (i=1; i<=f; i++) gel(nfproj,i) = lift_to_zk(gel(pow,i), c, N);
+  nfproj = coltoliftalg(nf, nfproj);
+
+  setlg(pow, f+1);
+  ffproj = FpM_mul(FpM_inv(pow, p), ffproj, p);
+
+  res = cgetg(LARGEMODPR, t_COL);
+  gel(res,mpr_TAU) = tau;
+  gel(res,mpr_FFP) = ffproj;
+  gel(res,mpr_PR) = pr;
+  gel(res,mpr_T) = T;
+  gel(res,mpr_NFP) = nfproj; return gerepilecopy(av, res);
+}
+
+GEN
+nfmodprinit(GEN nf, GEN pr) { return modprinit(nf, pr, 0); }
+GEN
+zkmodprinit(GEN nf, GEN pr) { return modprinit(nf, pr, 1); }
+
+/* x may be a modpr */
+static int
+ok_modpr(GEN x)
+{ return typ(x) == t_COL && lg(x) >= SMALLMODPR && lg(x) <= LARGEMODPR; }
+void
+checkmodpr(GEN x)
+{
+  if (!ok_modpr(x)) pari_err_TYPE("checkmodpr [use nfmodprinit]", x);
+  checkprid(gel(x,mpr_PR));
+}
+static int
+is_prid(GEN x)
+{
+  return (typ(x) == t_VEC && lg(x) == 6
+          && typ(gel(x,2)) == t_COL && typ(gel(x,3)) == t_INT);
+}
+void
+checkprid(GEN x)
+{ if (!is_prid(x)) pari_err_TYPE("checkprid",x); }
+GEN
+get_prid(GEN x)
+{
+  long lx = lg(x);
+  if (lx == 3 && typ(x) == t_VEC) x = gel(x,1);
+  if (is_prid(x)) return x;
+  if (ok_modpr(x)) {
+    x = gel(x,mpr_PR);
+    if (is_prid(x)) return x;
+  }
+  return NULL;
+}
+
+static GEN
+to_ff_init(GEN nf, GEN *pr, GEN *T, GEN *p, int zk)
+{
+  GEN modpr = (typ(*pr) == t_COL)? *pr: modprinit(nf, *pr, zk);
+  *T = lg(modpr)==SMALLMODPR? NULL: gel(modpr,mpr_T);
+  *pr = gel(modpr,mpr_PR);
+  *p = gel(*pr,1); return modpr;
+}
+
+/* Return an element of O_K which is set to x Mod T */
+GEN
+modpr_genFq(GEN modpr)
+{
+  switch(lg(modpr))
+  {
+    case SMALLMODPR: /* Fp */
+      return gen_1;
+    case LARGEMODPR:  /* painful case, p \mid index */
+      return gmael(modpr,mpr_NFP, 2);
+    default: /* trivial case : p \nmid index */
+    {
+      long v = varn( gel(modpr, mpr_T) );
+      return pol_x(v);
+    }
+  }
+}
+
+GEN
+nf_to_Fq_init(GEN nf, GEN *pr, GEN *T, GEN *p) {
+  GEN modpr = to_ff_init(nf,pr,T,p,0);
+  GEN tau = modpr_TAU(modpr);
+  if (!tau) gel(modpr,mpr_TAU) = anti_uniformizer2(nf, *pr);
+  return modpr;
+}
+GEN
+zk_to_Fq_init(GEN nf, GEN *pr, GEN *T, GEN *p) {
+  return to_ff_init(nf,pr,T,p,1);
+}
+
+/* assume x in 'basis' form (t_COL) */
+GEN
+zk_to_Fq(GEN x, GEN modpr)
+{
+  GEN pr = gel(modpr,mpr_PR), p = pr_get_p(pr);
+  GEN ffproj = gel(modpr,mpr_FFP);
+  if (lg(modpr) == SMALLMODPR) return FpV_dotproduct(ffproj,x, p);
+  return FpM_FpC_mul_FpX(ffproj,x, p, varn(gel(modpr,mpr_T)));
+}
+
+/* REDUCTION Modulo a prime ideal */
+
+/* nf a true nf */
+static GEN
+Rg_to_ff(GEN nf, GEN x0, GEN modpr)
+{
+  GEN x = x0, den, pr = gel(modpr,mpr_PR), p = pr_get_p(pr);
+  long tx = typ(x);
+
+  if (tx == t_POLMOD) { x = gel(x,2); tx = typ(x); }
+  switch(tx)
+  {
+    case t_INT: return modii(x, p);
+    case t_FRAC: return Rg_to_Fp(x, p);
+    case t_POL:
+      switch(lg(x))
+      {
+        case 2: return gen_0;
+        case 3: return Rg_to_Fp(gel(x,2), p);
+      }
+      x = Q_remove_denom(x, &den);
+      x = poltobasis(nf, x);
+      break;
+    case t_COL:
+      x = Q_remove_denom(x, &den);
+      if (lg(x) == lg(nf_get_zk(nf))) break;
+    default: pari_err_TYPE("Rg_to_ff",x);
+      return NULL;
+  }
+  if (den)
+  {
+    long v = Z_pvalrem(den, p, &den);
+    if (v)
+    {
+      GEN tau = modpr_TAU(modpr);
+      long w;
+      if (!tau) pari_err_TYPE("zk_to_ff", x0);
+      x = nfmuli(nf,x, nfpow_u(nf, tau, v));
+      w = ZV_pvalrem(x, p, &x);
+      if (w < v) pari_err_INV("Rg_to_ff", mkintmod(gen_0,p));
+      if (w != v) return gen_0;
+    }
+    if (!is_pm1(den)) x = ZC_Z_mul(x, Fp_inv(den, p));
+    x = FpC_red(x, p);
+  }
+  return zk_to_Fq(x, modpr);
+}
+
+GEN
+nfreducemodpr(GEN nf, GEN x, GEN modpr)
+{
+  pari_sp av = avma;
+  nf = checknf(nf); checkmodpr(modpr);
+  return gerepileupto(av, algtobasis(nf, Fq_to_nf(Rg_to_ff(nf,x,modpr),modpr)));
+}
+
+/* lift A from residue field to nf */
+GEN
+Fq_to_nf(GEN A, GEN modpr)
+{
+  long dA;
+  if (typ(A) == t_INT || lg(modpr) < LARGEMODPR) return A;
+  dA = degpol(A);
+  if (dA <= 0) return dA ? gen_0: gel(A,2);
+  return mulmat_pol(gel(modpr,mpr_NFP), A);
+}
+GEN
+FqV_to_nfV(GEN A, GEN modpr)
+{
+  long i,l = lg(A);
+  GEN B = cgetg(l,typ(A));
+  for (i=1; i<l; i++) gel(B,i) = Fq_to_nf(gel(A,i), modpr);
+  return B;
+}
+GEN
+FqM_to_nfM(GEN A, GEN modpr)
+{
+  long i,j,h,l = lg(A);
+  GEN B = cgetg(l, t_MAT);
+
+  if (l == 1) return B;
+  h = lgcols(A);
+  for (j=1; j<l; j++)
+  {
+    GEN Aj = gel(A,j), Bj = cgetg(h,t_COL); gel(B,j) = Bj;
+    for (i=1; i<h; i++) gel(Bj,i) = Fq_to_nf(gel(Aj,i), modpr);
+  }
+  return B;
+}
+GEN
+FqX_to_nfX(GEN A, GEN modpr)
+{
+  long i, l;
+  GEN B;
+
+  if (typ(A)!=t_POL) return icopy(A); /* scalar */
+  B = cgetg_copy(A, &l); B[1] = A[1];
+  for (i=2; i<l; i++) gel(B,i) = Fq_to_nf(gel(A,i), modpr);
+  return B;
+}
+
+/* reduce A to residue field */
+GEN
+nf_to_Fq(GEN nf, GEN A, GEN modpr)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, Rg_to_ff(checknf(nf), A, modpr));
+}
+/* A t_VEC/t_COL */
+GEN
+nfV_to_FqV(GEN A, GEN nf,GEN modpr)
+{
+  long i,l = lg(A);
+  GEN B = cgetg(l,typ(A));
+  for (i=1; i<l; i++) gel(B,i) = nf_to_Fq(nf,gel(A,i), modpr);
+  return B;
+}
+/* A  t_MAT */
+GEN
+nfM_to_FqM(GEN A, GEN nf,GEN modpr)
+{
+  long i,j,h,l = lg(A);
+  GEN B = cgetg(l,t_MAT);
+
+  if (l == 1) return B;
+  h = lgcols(A);
+  for (j=1; j<l; j++)
+  {
+    GEN Aj = gel(A,j), Bj = cgetg(h,t_COL); gel(B,j) = Bj;
+    for (i=1; i<h; i++) gel(Bj,i) = nf_to_Fq(nf, gel(Aj,i), modpr);
+  }
+  return B;
+}
+/* A t_POL */
+GEN
+nfX_to_FqX(GEN A, GEN nf,GEN modpr)
+{
+  long i,l = lg(A);
+  GEN B = cgetg(l,t_POL); B[1] = A[1];
+  for (i=2; i<l; i++) gel(B,i) = nf_to_Fq(nf,gel(A,i),modpr);
+  return normalizepol_lg(B, l);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       RELATIVE ROUND 2                          */
+/*                                                                 */
+/*******************************************************************/
+
+static void
+fill(long l, GEN H, GEN Hx, GEN I, GEN Ix)
+{
+  long i;
+  if (typ(Ix) == t_VEC) /* standard */
+    for (i=1; i<l; i++) { gel(H,i) = gel(Hx,i); gel(I,i) = gel(Ix,i); }
+  else /* constant ideal */
+    for (i=1; i<l; i++) { gel(H,i) = gel(Hx,i); gel(I,i) = Ix; }
+}
+
+/* given MODULES x and y by their pseudo-bases, returns a pseudo-basis of the
+ * module generated by x and y. */
+static GEN
+rnfjoinmodules_i(GEN nf, GEN Hx, GEN Ix, GEN Hy, GEN Iy)
+{
+  long lx = lg(Hx), ly = lg(Hy), l = lx+ly-1;
+  GEN H = cgetg(l, t_MAT), I = cgetg(l, t_VEC);
+  fill(lx, H     , Hx, I     , Ix);
+  fill(ly, H+lx-1, Hy, I+lx-1, Iy); return nfhnf(nf, mkvec2(H, I));
+}
+static GEN
+rnfjoinmodules(GEN nf, GEN x, GEN y)
+{
+  if (!x) return y;
+  if (!y) return x;
+  return rnfjoinmodules_i(nf, gel(x,1), gel(x,2), gel(y,1), gel(y,2));
+}
+
+typedef struct {
+  GEN multab, T,p;
+  long h;
+} rnfeltmod_muldata;
+
+static GEN
+_sqr(void *data, GEN x)
+{
+  rnfeltmod_muldata *D = (rnfeltmod_muldata *) data;
+  GEN z = x? tablesqr(D->multab,x)
+           : tablemul_ei_ej(D->multab,D->h,D->h);
+  return FqV_red(z,D->T,D->p);
+}
+static GEN
+_msqr(void *data, GEN x)
+{
+  GEN x2 = _sqr(data, x), z;
+  rnfeltmod_muldata *D = (rnfeltmod_muldata *) data;
+  z = tablemul_ei(D->multab, x2, D->h);
+  return FqV_red(z,D->T,D->p);
+}
+
+/* Compute W[h]^n mod (T,p) in the extension, assume n >= 0. T a ZX */
+static GEN
+rnfeltid_powmod(GEN multab, long h, GEN n, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  GEN y;
+  rnfeltmod_muldata D;
+
+  if (!signe(n)) return gen_1;
+
+  D.multab = multab;
+  D.h = h;
+  D.T = T;
+  D.p = p;
+  y = gen_pow_fold(NULL, n, (void*)&D, &_sqr, &_msqr);
+  return gerepilecopy(av, y);
+}
+
+/* P != 0 has at most degpol(P) roots. Look for an element in Fq which is not
+ * a root, cf repres() */
+static GEN
+FqX_non_root(GEN P, GEN T, GEN p)
+{
+  long dP = degpol(P), f, vT;
+  long i, j, k, pi, pp;
+  GEN v;
+
+  if (dP == 0) return gen_1;
+  pp = is_bigint(p) ? dP+1: itos(p);
+  v = cgetg(dP + 2, t_VEC);
+  gel(v,1) = gen_0;
+  if (T)
+  { f = degpol(T); vT = varn(T); }
+  else
+  { f = 1; vT = 0; }
+  for (i=pi=1; i<=f; i++,pi*=pp)
+  {
+    GEN gi = i == 1? gen_1: monomial(gen_1, i-1, vT), jgi = gi;
+    for (j=1; j<pp; j++)
+    {
+      for (k=1; k<=pi; k++)
+      {
+        GEN z = Fq_add(gel(v,k), jgi, T,p);
+        if (!gequal0(FqX_eval(P, z, T,p))) return z;
+        gel(v, j*pi+k) = z;
+      }
+      if (j < pp-1) jgi = Fq_add(jgi, gi, T,p); /* j*g[i] */
+    }
+  }
+  return NULL;
+}
+
+/* Relative Dedekind criterion over (true) nf, applied to the order defined by a
+ * root of monic irreducible polynomial P, modulo the prime ideal pr. Assume
+ * vdisc = v_pr( disc(P) ).
+ * Return NULL if nf[X]/P is pr-maximal. Otherwise, return [flag, O, v]:
+ *   O = enlarged order, given by a pseudo-basis
+ *   flag = 1 if O is proven pr-maximal (may be 0 and O nevertheless pr-maximal)
+ *   v = v_pr(disc(O)). */
+static GEN
+rnfdedekind_i(GEN nf, GEN P, GEN pr, long vdisc, long only_maximal)
+{
+  GEN Ppr, A, I, p, tau, g, h, k, base, T, gzk, hzk, prinvp, pal, nfT, modpr;
+  long m, vt, r, d, i, j, mpr;
+
+  if (vdisc < 0) pari_err_TYPE("rnfdedekind [non integral pol]", P);
+  if (vdisc == 1) return NULL; /* pr-maximal */
+  if (!only_maximal && !gequal1(leading_term(P)))
+    pari_err_IMPL( "the full Dedekind criterion in the non-monic case");
+  /* either monic OR only_maximal = 1 */
+  m = degpol(P);
+  nfT = nf_get_pol(nf);
+  modpr = nf_to_Fq_init(nf,&pr, &T, &p);
+  Ppr = nfX_to_FqX(P, nf, modpr);
+  mpr = degpol(Ppr);
+  if (mpr < m) /* non-monic => only_maximal = 1 */
+  {
+    if (mpr < 0) return NULL;
+    if (! RgX_valrem(Ppr, &Ppr))
+    { /* non-zero constant coefficient */
+      Ppr = RgX_shift_shallow(RgX_recip_shallow(Ppr), m - mpr);
+      P = RgX_recip_shallow(P);
+    }
+    else
+    {
+      GEN z = FqX_non_root(Ppr, T, p);
+      if (!z) pari_err_IMPL( "Dedekind in the difficult case");
+      z = Fq_to_nf(z, modpr);
+      if (typ(z) == t_INT)
+        P = RgX_translate(P, z);
+      else
+        P = RgXQX_translate(P, z, T);
+      P = RgX_recip_shallow(P);
+      Ppr = nfX_to_FqX(P, nf, modpr); /* degpol(P) = degpol(Ppr) = m */
+    }
+  }
+  A = gel(FqX_factor(Ppr,T,p),1);
+  r = lg(A); /* > 1 */
+  g = gel(A,1);
+  for (i=2; i<r; i++) g = FqX_mul(g, gel(A,i), T, p);
+  h = FqX_div(Ppr,g, T, p);
+  gzk = FqX_to_nfX(g, modpr);
+  hzk = FqX_to_nfX(h, modpr);
+
+  k = gsub(P, RgXQX_mul(gzk,hzk, nfT));
+  tau = pr_get_tau(pr);
+  switch(typ(tau))
+  {
+    case t_INT: k = gdiv(k, p); break;
+    case t_MAT:
+      k = RgX_to_nfX(nf, k);
+      k = RgX_Rg_div(tablemulvec(NULL,tau, k), p);
+      break;
+    case t_COL:
+      tau = coltoliftalg(nf, tau);
+      k = RgX_Rg_div(RgXQX_RgXQ_mul(k, tau, nfT), p);
+      break;
+  }
+  k = nfX_to_FqX(k, nf, modpr);
+  k  = FqX_normalize(FqX_gcd(FqX_gcd(g,h,  T,p), k, T,p), T,p);
+  d = degpol(k);  /* <= m */
+  if (!d) return NULL; /* pr-maximal */
+  if (only_maximal) return gen_0; /* not maximal */
+
+  A = cgetg(m+d+1,t_MAT);
+  I = cgetg(m+d+1,t_VEC); base = mkvec2(A, I);
+ /* base[2] temporarily multiplied by p, for the final nfhnfmod,
+  * which requires integral ideals */
+  prinvp = pidealprimeinv(nf,pr); /* again multiplied by p */
+  for (j=1; j<=m; j++)
+  {
+    gel(A,j) = col_ei(m, j);
+    gel(I,j) = p;
+  }
+  pal = FqX_to_nfX(FqX_div(Ppr,k, T,p), modpr);
+  for (   ; j<=m+d; j++)
+  {
+    gel(A,j) = RgX_to_RgV(pal,m);
+    gel(I,j) = prinvp;
+    if (j < m+d) pal = RgXQX_rem(RgX_shift_shallow(pal,1),P,nfT);
+  }
+  /* the modulus is integral */
+  base = nfhnfmod(nf,base, ZM_Z_mul(idealpows(nf, prinvp, d), powiu(p, m-d)));
+  gel(base,2) = gdiv(gel(base,2), p); /* cancel the factor p */
+  vt = vdisc - 2*d;
+  return mkvec3(vt < 2? gen_1: gen_0, base, stoi(vt));
+}
+
+/* [L:K] = n */
+static GEN
+triv_order(long n)
+{
+  GEN z = cgetg(3, t_VEC);
+  gel(z,1) = matid(n);
+  gel(z,2) = const_vec(n, gen_1); return z;
+}
+
+/* if flag is set, return gen_1 (resp. gen_0) if the order K[X]/(P)
+ * is pr-maximal (resp. not pr-maximal). */
+GEN
+rnfdedekind(GEN nf, GEN P, GEN pr, long flag)
+{
+  pari_sp av = avma;
+  long v;
+  GEN z, dP;
+
+  nf = checknf(nf);
+  P = RgX_nffix("rnfdedekind", nf_get_pol(nf), P, 0);
+  dP = RgX_disc(P); P = lift_intern(P);
+  if (!pr) {
+    GEN fa = idealfactor(nf, dP);
+    GEN Q = gel(fa,1), E = gel(fa,2);
+    pari_sp av2 = avma;
+    long i, l = lg(Q);
+    for (i=1; i < l; i++)
+    {
+      v = itos(gel(E,i));
+      if (rnfdedekind_i(nf,P,gel(Q,i),v,1)) { avma=av; return gen_0; }
+      avma = av2;
+    }
+    avma = av; return gen_1;
+  } else if (typ(pr) == t_VEC) {
+    if (lg(pr) == 1) { avma = av; return gen_1; } /* flag = 1 is implicit */
+    if (typ(gel(pr,1)) == t_VEC) {
+      GEN Q = pr;
+      pari_sp av2 = avma;
+      long i, l = lg(Q);
+      for (i=1; i < l; i++)
+      {
+        v = nfval(nf, dP, gel(Q,i));
+        if (rnfdedekind_i(nf,P,gel(Q,i),v,1)) { avma=av; return gen_0; }
+        avma = av2;
+      }
+      avma = av; return gen_1;
+    }
+  }
+
+  v = nfval(nf, dP, pr);
+  z = rnfdedekind_i(nf, P, pr, v, flag);
+  if (z)
+  {
+    if (flag) { avma = av; return gen_0; }
+    z = gerepilecopy(av, z);
+  }
+  else {
+    long d;
+    avma = av; if (flag) return gen_1;
+    d = degpol(P);
+    z = cgetg(4, t_VEC);
+    gel(z,1) = gen_1;
+    gel(z,2) = triv_order(d);
+    gel(z,3) = stoi(v);
+  }
+  return z;
+}
+
+static int
+ideal_is1(GEN x) {
+  switch(typ(x))
+  {
+    case t_INT: return is_pm1(x);
+    case t_MAT: return RgM_isidentity(x);
+  }
+  return 0;
+}
+
+/* nf a true nf. Return NULL if power order if pr-maximal */
+static GEN
+rnfmaxord(GEN nf, GEN pol, GEN pr, long vdisc)
+{
+  pari_sp av = avma, av1, lim;
+  long i, j, k, n, vpol, cmpt, sep;
+  GEN q, q1, p, T, modpr, W, I, MW, C, p1;
+  GEN Tauinv, Tau, prhinv, pip, nfT, rnfId;
+
+  if (DEBUGLEVEL>1) err_printf(" treating %Ps^%ld\n", pr, vdisc);
+  modpr = nf_to_Fq_init(nf,&pr,&T,&p);
+  av1 = avma;
+  p1 = rnfdedekind_i(nf, pol, modpr, vdisc, 0);
+  if (!p1) { avma = av; return NULL; }
+  if (is_pm1(gel(p1,1))) return gerepilecopy(av,gel(p1,2));
+  sep = itos(gel(p1,3));
+  W = gmael(p1,2,1);
+  I = gmael(p1,2,2);
+  gerepileall(av1, 2, &W, &I);
+
+  pip = coltoalg(nf, pr_get_gen(pr));
+  nfT = nf_get_pol(nf);
+  n = degpol(pol); vpol = varn(pol);
+  q = T? powiu(p,degpol(T)): p;
+  q1 = q; while (cmpiu(q1,n) < 0) q1 = mulii(q1,q);
+  rnfId = matid(n);
+
+  prhinv = idealinv(nf, pr);
+  C = cgetg(n+1, t_MAT);
+  for (j=1; j<=n; j++) gel(C,j) = cgetg(n*n+1, t_COL);
+  MW = cgetg(n*n+1, t_MAT);
+  for (j=1; j<=n*n; j++) gel(MW,j) = cgetg(n+1, t_COL);
+  Tauinv = cgetg(n+1, t_VEC);
+  Tau    = cgetg(n+1, t_VEC);
+  av1 = avma; lim = stack_lim(av1,1);
+  for(cmpt=1; ; cmpt++)
+  {
+    GEN I0 = leafcopy(I), W0 = leafcopy(W);
+    GEN Wa, Wainv, Waa, Ip, A, Ainv, MWmod, F, pseudo, G;
+
+    if (DEBUGLEVEL>1) err_printf("    pass no %ld\n",cmpt);
+    for (j=1; j<=n; j++)
+    {
+      GEN tau, tauinv;
+      long v1, v2;
+      if (ideal_is1(gel(I,j))) { gel(Tau,j) = gel(Tauinv,j) = gen_1; continue; }
+
+      p1 = idealtwoelt(nf,gel(I,j));
+      v1 = nfval(nf,gel(p1,1),pr);
+      v2 = nfval(nf,gel(p1,2),pr);
+      tau = (v1 > v2)? gel(p1,2): gel(p1,1);
+      tauinv = nfinv(nf, tau);
+      gel(Tau,j) = tau;
+      gel(Tauinv,j) = tauinv;
+      gel(W,j) = nfC_nf_mul(nf, gel(W,j), tau);
+      gel(I,j) = idealmul(nf,    tauinv, gel(I,j));
+    }
+    /* W = (Z_K/pr)-basis of O/pr. O = (W0,I0) ~ (W, I) */
+    Wa = matbasistoalg(nf,W);
+
+   /* compute MW: W_i*W_j = sum MW_k,(i,j) W_k */
+    Waa = lift_intern(RgM_to_RgXV(Wa,vpol));
+    Wainv = lift_intern(ginv(Wa));
+    for (i=1; i<=n; i++)
+      for (j=i; j<=n; j++)
+      {
+        GEN z = RgXQX_rem(gmul(gel(Waa,i),gel(Waa,j)), pol, nfT);
+        long tz = typ(z);
+        if (is_scalar_t(tz) || (tz == t_POL && varncmp(varn(z), vpol) > 0))
+          z = gmul(z, gel(Wainv,1));
+        else
+          z = mulmat_pol(Wainv, z);
+        for (k=1; k<=n; k++)
+        {
+          GEN c = grem(gel(z,k), nfT);
+          gcoeff(MW, k, (i-1)*n+j) = c;
+          gcoeff(MW, k, (j-1)*n+i) = c;
+        }
+      }
+
+    /* compute Ip =  pr-radical [ could use Ker(trace) if q large ] */
+    MWmod = nfM_to_FqM(MW,nf,modpr);
+    F = cgetg(n+1, t_MAT); gel(F,1) = gel(rnfId,1);
+    for (j=2; j<=n; j++) gel(F,j) = rnfeltid_powmod(MWmod, j, q1, T,p);
+    Ip = FqM_ker(F,T,p);
+    if (lg(Ip) == 1) { W = W0; I = I0; break; }
+
+    /* Fill C: W_k A_j = sum_i C_(i,j),k A_i */
+    A = FqM_to_nfM(FqM_suppl(Ip,T,p), modpr);
+    for (j=1; j<lg(Ip); j++)
+    {
+      p1 = gel(A,j);
+      for (i=1; i<=n; i++) gel(p1,i) = mkpolmod(gel(p1,i), nfT);
+    }
+    for (   ; j<=n; j++)
+    {
+      p1 = gel(A,j);
+      for (i=1; i<=n; i++) gel(p1,i) = gmul(pip, gel(p1,i));
+    }
+    Ainv = lift_intern(RgM_inv(A));
+    A    = lift_intern(A);
+    for (k=1; k<=n; k++)
+      for (j=1; j<=n; j++)
+      {
+        GEN z = RgM_RgC_mul(Ainv, gmod(tablemul_ei(MW, gel(A,j),k), nfT));
+        for (i=1; i<=n; i++)
+        {
+          GEN c = grem(gel(z,i), nfT);
+          gcoeff(C, (j-1)*n+i, k) = nf_to_Fq(nf,c,modpr);
+        }
+      }
+    G = FqM_to_nfM(FqM_ker(C,T,p), modpr);
+
+    pseudo = rnfjoinmodules_i(nf, G,prhinv, rnfId,I);
+    /* express W in terms of the power basis */
+    W = RgM_mul(Wa, matbasistoalg(nf,gel(pseudo,1)));
+    W = RgM_to_nfM(nf, W);
+    I = gel(pseudo,2);
+    /* restore the HNF property W[i,i] = 1. NB: Wa upper triangular, with
+     * Wa[i,i] = Tau[i] */
+    for (j=1; j<=n; j++)
+      if (gel(Tau,j) != gen_1)
+      {
+        gel(W,j) = nfC_nf_mul(nf, gel(W,j), gel(Tauinv,j));
+        gel(I,j) = idealmul(nf, gel(Tau,j), gel(I,j));
+      }
+    if (DEBUGLEVEL>3) err_printf(" new order:\n%Ps\n%Ps\n", W, I);
+    if (sep <= 3 || gequal(I,I0)) break;
+
+    if (low_stack(lim, stack_lim(av1,1)) || (cmpt & 3) == 0)
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"rnfmaxord");
+      gerepileall(av1,2, &W,&I);
+    }
+  }
+  return gerepilecopy(av, mkvec2(W, I));
+}
+
+GEN
+Rg_nffix(const char *f, GEN T, GEN c, int lift)
+{
+  switch(typ(c))
+  {
+    case t_INT: case t_FRAC: return c;
+    case t_POL:
+      if (lg(c) >= lg(T)) c = RgX_rem(c,T);
+      break;
+    case t_POLMOD:
+      if (!RgX_equal_var(gel(c,1), T)) pari_err_MODULUS(f, gel(c,1),T);
+      c = gel(c,2);
+      switch(typ(c))
+      {
+        case t_POL: break;
+        case t_INT: case t_FRAC: return c;
+        default: pari_err_TYPE(f, c);
+      }
+      break;
+    default: pari_err_TYPE(f,c);
+  }
+  /* typ(c) = t_POL */
+  if (varn(c) != varn(T)) pari_err_VAR(f, c,T);
+  switch(lg(c))
+  {
+    case 2: return gen_0;
+    case 3:
+      c = gel(c,2); if (is_rational_t(typ(c))) return c;
+      pari_err_TYPE(f,c);
+  }
+  if (!RgX_is_QX(c)) pari_err_TYPE(f, c);
+  return lift? c: mkpolmod(c, T);
+}
+/* check whether P is a polynomials with coeffs in number field Q[y]/(T) */
+GEN
+RgX_nffix(const char *f, GEN T, GEN P, int lift)
+{
+  long i, l, vT = varn(T);
+  GEN Q = cgetg_copy(P, &l);
+  if (typ(P) != t_POL) pari_err_TYPE(stack_strcat(f," [t_POL expected]"), P);
+  if (varncmp(varn(P), vT) >= 0) pari_err_PRIORITY(f, P, ">=", vT);
+  Q[1] = P[1];
+  for (i=2; i<l; i++) gel(Q,i) = Rg_nffix(f, T, gel(P,i), lift);
+  return normalizepol_lg(Q, l);
+}
+GEN
+RgV_nffix(const char *f, GEN T, GEN P, int lift)
+{
+  long i, l;
+  GEN Q = cgetg_copy(P, &l);
+  for (i=1; i<l; i++) gel(Q,i) = Rg_nffix(f, T, gel(P,i), lift);
+  return Q;
+}
+
+/* determinant of the trace pairing */
+static GEN
+get_d(GEN nf, GEN pol, GEN A)
+{
+  long i, j, n = degpol(pol);
+  GEN W = RgM_to_RgXV(lift_intern(matbasistoalg(nf,A)), varn(pol));
+  GEN T, nfT = nf_get_pol(nf), sym = polsym_gen(pol, NULL, n-1, nfT, NULL);
+  T = cgetg(n+1,t_MAT);
+  for (j=1; j<=n; j++) gel(T,j) = cgetg(n+1,t_COL);
+  for (j=1; j<=n; j++)
+    for (i=j; i<=n; i++)
+    {
+      GEN c = RgXQX_mul(gel(W,i),gel(W,j), nfT);
+      c = RgXQX_rem(c, pol, nfT);
+      c = simplify_shallow(quicktrace(c,sym));
+      gcoeff(T,j,i) = gcoeff(T,i,j) = c;
+    }
+  return nf_to_scalar_or_basis(nf, det(T));
+}
+
+/* nf = base field K
+ * pol= monic polynomial, coefficients in Z_K, defining a relative
+ *   extension L = K[X]/(pol). One MUST have varn(pol) << nf_get_varn(nf).
+ * Returns a pseudo-basis [A,I] of Z_L, set (D,d) to the relative
+ * discriminant, and f to the index-ideal */
+GEN
+rnfallbase(GEN nf, GEN *ppol, GEN *pD, GEN *pd, GEN *pf)
+{
+  long i, n, l;
+  GEN A, nfT, fa, E, P, I, z, d, D, disc, pol = *ppol;
+
+  nf = checknf(nf); nfT = nf_get_pol(nf);
+  pol = RgX_nffix("rnfallbase", nfT,pol,0);
+  if (!gequal1(leading_term(pol)))
+    pari_err_IMPL("non-monic relative polynomials");
+
+  n = degpol(pol);
+  disc = RgX_disc(pol); pol = lift_intern(pol);
+  fa = idealfactor(nf, disc);
+  P = gel(fa,1); l = lg(P);
+  E = gel(fa,2);
+  z = NULL;
+  for (i=1; i < l; i++)
+  {
+    long e = itos(gel(E,i));
+    if (e > 1) z = rnfjoinmodules(nf, z, rnfmaxord(nf, pol, gel(P,i), e));
+  }
+  if (!z) z = triv_order(n);
+  A = gel(z,1); d = get_d(nf, pol, A);
+  I = gel(z,2);
+  i=1; while (i<=n && equali1(gel(I,i))) i++;
+  if (i > n) { D = gen_1; if (pf) *pf = gen_1; }
+  else
+  {
+    D = gel(I,i);
+    for (i++; i<=n; i++) D = idealmul(nf,D,gel(I,i));
+    if (pf) *pf = idealinv(nf, D);
+    D = idealpow(nf,D,gen_2);
+  }
+  if (pd)
+  {
+    GEN f = core2partial(Q_content(d), 0);
+    *pd = gdiv(d, sqri(gel(f,2)));
+  }
+  *pD = idealmul(nf,D,d);
+  *ppol = pol; return z;
+}
+
+GEN
+rnfpseudobasis(GEN nf, GEN pol)
+{
+  pari_sp av = avma;
+  GEN D, d, z = rnfallbase(nf,&pol, &D, &d, NULL);
+  return gerepilecopy(av, mkvec4(gel(z,1), gel(z,2), D, d));
+}
+
+GEN
+rnfdiscf(GEN nf, GEN pol)
+{
+  pari_sp av = avma;
+  GEN D, d; (void)rnfallbase(nf,&pol, &D, &d, NULL);
+  return gerepilecopy(av, mkvec2(D,d));
+}
+
+GEN
+gen_if_principal(GEN bnf, GEN x)
+{
+  pari_sp av = avma;
+  GEN z = bnfisprincipal0(bnf,x, nf_GEN_IF_PRINCIPAL | nf_FORCE);
+  if (isintzero(z)) { avma = av; return NULL; }
+  return z;
+}
+
+static int
+is_pseudo_matrix(GEN O)
+{
+  return (typ(O) ==t_VEC && lg(O) >= 3
+          && typ(gel(O,1)) == t_MAT
+          && typ(gel(O,2)) == t_VEC
+          && lgcols(O) == lg(gel(O,2)));
+}
+
+/* given bnf and a pseudo-basis of an order in HNF [A,I], tries to simplify
+ * the HNF as much as possible. The resulting matrix will be upper triangular
+ * but the diagonal coefficients will not be equal to 1. The ideals are
+ * guaranteed to be integral and primitive. */
+GEN
+rnfsimplifybasis(GEN bnf, GEN x)
+{
+  pari_sp av = avma;
+  long i, l;
+  GEN y, Az, Iz, nf, A, I;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  if (!is_pseudo_matrix(x)) pari_err_TYPE("rnfsimplifybasis",x);
+  A = gel(x,1);
+  I = gel(x,2); l = lg(I);
+  y = cgetg(3, t_VEC);
+  Az = cgetg(l, t_MAT); gel(y,1) = Az;
+  Iz = cgetg(l, t_VEC); gel(y,2) = Iz;
+  for (i = 1; i < l; i++)
+  {
+    GEN c, d;
+    if (ideal_is1(gel(I,i))) {
+      gel(Iz,i) = gen_1;
+      gel(Az,i) = gel(A,i);
+      continue;
+    }
+
+    gel(Iz,i) = Q_primitive_part(gel(I,i), &c);
+    gel(Az,i) = c? RgC_Rg_mul(gel(A,i),c): gel(A,i);
+    if (c && ideal_is1(gel(Iz,i))) continue;
+
+    d = gen_if_principal(bnf, gel(Iz,i));
+    if (d)
+    {
+      gel(Iz,i) = gen_1;
+      gel(Az,i) = nfC_nf_mul(nf, gel(Az,i), d);
+    }
+  }
+  return gerepilecopy(av, y);
+}
+
+static GEN
+get_order(GEN nf, GEN O, const char *s)
+{
+  if (typ(O) == t_POL)
+    return rnfpseudobasis(nf, O);
+  if (!is_pseudo_matrix(O)) pari_err_TYPE(s, O);
+  return O;
+}
+
+GEN
+rnfdet(GEN nf, GEN order)
+{
+  pari_sp av = avma;
+  GEN A, I, D;
+  nf = checknf(nf);
+  order = get_order(nf, order, "rnfdet");
+  A = gel(order,1);
+  I = gel(order,2);
+  D = idealmul(nf, det(matbasistoalg(nf, A)), prodid(nf, I));
+  return gerepileupto(av, D);
+}
+
+/* Given two fractional ideals a and b, gives x in a, y in b, z in b^-1,
+   t in a^-1 such that xt-yz=1. In the present version, z is in Z. */
+static void
+nfidealdet1(GEN nf, GEN a, GEN b, GEN *px, GEN *py, GEN *pz, GEN *pt)
+{
+  GEN x, uv, y, da, db;
+
+  a = idealinv(nf,a);
+  a = Q_remove_denom(a, &da);
+  b = Q_remove_denom(b, &db);
+  x = idealcoprime(nf,a,b);
+  uv = idealaddtoone(nf, idealmul(nf,x,a), b);
+  y = gel(uv,2);
+  if (da) x = RgC_Rg_mul(x,da);
+  if (db) y = RgC_Rg_div(y,db);
+  *px = x;
+  *py = y;
+  *pz = db ? negi(db): gen_m1;
+  *pt = nfdiv(nf, gel(uv,1), x);
+}
+
+/* given a pseudo-basis of an order in HNF [A,I] (or [A,I,D,d]), gives an
+ * n x n matrix (not in HNF) of a pseudo-basis and an ideal vector
+ * [1,1,...,1,I] such that order = Z_K^(n-1) x I.
+ * Uses the approximation theorem ==> slow. */
+GEN
+rnfsteinitz(GEN nf, GEN order)
+{
+  pari_sp av = avma;
+  long i, n, l;
+  GEN A, I, p1;
+
+  nf = checknf(nf);
+  order = get_order(nf, order, "rnfsteinitz");
+  A = RgM_to_nfM(nf, gel(order,1));
+  I = leafcopy(gel(order,2)); n=lg(A)-1;
+  for (i=1; i<n; i++)
+  {
+    GEN c1, c2, b, a = gel(I,i);
+    gel(I,i) = gen_1;
+    if (ideal_is1(a)) continue;
+
+    c1 = gel(A,i);
+    c2 = gel(A,i+1);
+    b = gel(I,i+1);
+    if (ideal_is1(b))
+    {
+      gel(A,i) = c2;
+      gel(A,i+1) = gneg(c1);
+      gel(I,i+1) = a;
+    }
+    else
+    {
+      pari_sp av2 = avma;
+      GEN x, y, z, t;
+      nfidealdet1(nf,a,b, &x,&y,&z,&t);
+      x = RgC_add(nfC_nf_mul(nf, c1, x), nfC_nf_mul(nf, c2, y));
+      y = RgC_add(nfC_nf_mul(nf, c1, z), nfC_nf_mul(nf, c2, t));
+      gerepileall(av2, 2, &x,&y);
+      gel(A,i) = x;
+      gel(A,i+1) = y;
+      gel(I,i+1) = Q_primitive_part(idealmul(nf,a,b), &p1);
+      if (p1) gel(A,i+1) = nfC_nf_mul(nf, gel(A,i+1), p1);
+    }
+  }
+  l = lg(order);
+  p1 = cgetg(l,t_VEC);
+  gel(p1,1) = A;
+  gel(p1,2) = I; for (i=3; i<l; i++) gel(p1,i) = gel(order,i);
+  return gerepilecopy(av, p1);
+}
+
+/* Given bnf and either an order as output by rnfpseudobasis or a polynomial,
+ * and outputs a basis if it is free, an n+1-generating set if it is not */
+GEN
+rnfbasis(GEN bnf, GEN order)
+{
+  pari_sp av = avma;
+  long j, n;
+  GEN nf, A, I, cl, col, a;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  order = get_order(nf, order, "rnfbasis");
+  I = gel(order,2); n = lg(I)-1;
+  j=1; while (j<n && ideal_is1(gel(I,j))) j++;
+  if (j<n)
+  {
+    order = rnfsteinitz(nf,order);
+    I = gel(order,2);
+  }
+  A = gel(order,1);
+  col= gel(A,n); A = vecslice(A, 1, n-1);
+  cl = gel(I,n);
+  a = gen_if_principal(bnf, cl);
+  if (!a)
+  {
+    GEN v = idealtwoelt(nf, cl);
+    A = shallowconcat(A, gmul(gel(v,1), col));
+    a = gel(v,2);
+  }
+  A = shallowconcat(A, nfC_nf_mul(nf, col, a));
+  return gerepilecopy(av, A);
+}
+
+/* Given bnf and either an order as output by rnfpseudobasis or a polynomial,
+ * and outputs a basis (not pseudo) in Hermite Normal Form if it exists, zero
+ * if not
+ */
+GEN
+rnfhnfbasis(GEN bnf, GEN order)
+{
+  pari_sp av = avma;
+  long j, n;
+  GEN nf, A, I, a;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  order = get_order(nf, order, "rnfbasis");
+  A = gel(order,1); A = RgM_shallowcopy(A);
+  I = gel(order,2); n = lg(A)-1;
+  for (j=1; j<=n; j++)
+  {
+    if (ideal_is1(gel(I,j))) continue;
+    a = gen_if_principal(bnf, gel(I,j));
+    if (!a) { avma = av; return gen_0; }
+    gel(A,j) = nfC_nf_mul(nf, gel(A,j), a);
+  }
+  return gerepilecopy(av,A);
+}
+
+static long
+rnfisfree_aux(GEN bnf, GEN order)
+{
+  long n, j;
+  GEN nf, P, I;
+
+  bnf = checkbnf(bnf);
+  if (is_pm1( bnf_get_no(bnf) )) return 1;
+  nf = bnf_get_nf(bnf);
+  order = get_order(nf, order, "rnfisfree");
+  I = gel(order,2); n = lg(I)-1;
+  j=1; while (j<=n && ideal_is1(gel(I,j))) j++;
+  if (j>n) return 1;
+
+  P = gel(I,j);
+  for (j++; j<=n; j++)
+    if (!ideal_is1(gel(I,j))) P = idealmul(nf,P,gel(I,j));
+  return gequal0( isprincipal(bnf,P) );
+}
+
+long
+rnfisfree(GEN bnf, GEN order)
+{
+  pari_sp av = avma;
+  long n = rnfisfree_aux(bnf, order);
+  avma = av; return n;
+}
+
+/**********************************************************************/
+/**                                                                  **/
+/**                   COMPOSITUM OF TWO NUMBER FIELDS                **/
+/**                                                                  **/
+/**********************************************************************/
+static GEN
+compositum_fix(GEN A)
+{
+  A = Q_primpart(A); RgX_check_ZX(A,"polcompositum");
+  if (!ZX_is_squarefree(A))
+    pari_err_DOMAIN("polcompositum","issquarefree(arg)","=",gen_0,A);
+  return A;
+}
+/* modular version */
+GEN
+polcompositum0(GEN A, GEN B, long flall)
+{
+  pari_sp av = avma;
+  int same;
+  long v, k;
+  GEN C, D, LPRS;
+
+  if (typ(A)!=t_POL) pari_err_TYPE("polcompositum",A);
+  if (typ(B)!=t_POL) pari_err_TYPE("polcompositum",B);
+  if (degpol(A)<=0 || degpol(B)<=0) pari_err_CONSTPOL("polcompositum");
+  v = varn(A);
+  if (varn(B) != v) pari_err_VAR("polcompositum", A,B);
+  same = (A == B || RgX_equal(A,B));
+  A = compositum_fix(A);
+  if (!same) B = compositum_fix(B);
+
+  D = NULL; /* -Wall */
+  k = same? -1: 1;
+  C = ZX_ZXY_resultant_all(A, B, &k, flall? &LPRS: NULL);
+  if (same)
+  {
+    D = RgX_rescale(A, stoi(1 - k));
+    C = RgX_div(C, D);
+    if (degpol(C) <= 0) C = mkvec(D); else C = shallowconcat(ZX_DDF(C), D);
+  }
+  else
+    C = ZX_DDF(C); /* C = Res_Y (A(Y), B(X + kY)) guaranteed squarefree */
+  gen_sort_inplace(C, (void*)&cmpii, &gen_cmp_RgX, NULL);
+  if (flall)
+  { /* a,b,c root of A,B,C = compositum, c = b - k a */
+    long i, l = lg(C);
+    GEN a, b, mH0 = RgX_neg(gel(LPRS,1)), H1 = gel(LPRS,2);
+    for (i=1; i<l; i++)
+    {
+      GEN D = gel(C,i);
+      a = RgXQ_mul(mH0, QXQ_inv(H1, D), D);
+      b = gadd(pol_x(v), gmulsg(k,a));
+      gel(C,i) = mkvec4(D, mkpolmod(a,D), mkpolmod(b,D), stoi(-k));
+    }
+  }
+  settyp(C, t_VEC); return gerepilecopy(av, C);
+}
+GEN
+compositum(GEN pol1,GEN pol2) { return polcompositum0(pol1,pol2,0); }
+GEN
+compositum2(GEN pol1,GEN pol2) { return polcompositum0(pol1,pol2,1); }
+
+/* Assume A,B irreducible (in particular squarefree) and define linearly
+ * disjoint extensions: no factorisation needed */
+GEN
+ZX_compositum_disjoint(GEN A, GEN B)
+{
+  long k = 1;
+  return ZX_ZXY_resultant_all(A, B, &k, NULL);
+}
diff --git a/src/basemath/base3.c b/src/basemath/base3.c
new file mode 100644
index 0000000..8871dd9
--- /dev/null
+++ b/src/basemath/base3.c
@@ -0,0 +1,2494 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                       BASIC NF OPERATIONS                       */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*                OPERATIONS OVER NUMBER FIELD ELEMENTS.           */
+/*     represented as column vectors over the integral basis       */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+get_tab(GEN nf, long *N)
+{
+  GEN tab = (typ(nf) == t_MAT)? nf: gel(nf,9);
+  *N = nbrows(tab); return tab;
+}
+
+/* x != 0, y t_INT. Return x * y (not memory clean if x = 1) */
+static GEN
+_mulii(GEN x, GEN y) {
+  return is_pm1(x)? (signe(x) < 0)? negi(y): y
+                  : mulii(x, y);
+}
+
+GEN
+tablemul_ei_ej(GEN M, long i, long j)
+{
+  long N;
+  GEN tab = get_tab(M, &N);
+  tab += (i-1)*N; return gel(tab,j);
+}
+
+/* Outputs x.ei, where ei is the i-th elt of the algebra basis.
+ * x an RgV of correct length and arbitrary content (polynomials, scalars...).
+ * M is the multiplication table ei ej = sum_k M_k^(i,j) ek */
+GEN
+tablemul_ei(GEN M, GEN x, long i)
+{
+  long j, k, N;
+  GEN v, tab;
+
+  if (i==1) return gcopy(x);
+  tab = get_tab(M, &N);
+  if (typ(x) != t_COL) { v = zerocol(N); gel(v,i) = gcopy(x); return v; }
+  tab += (i-1)*N; v = cgetg(N+1,t_COL);
+  /* wi . x = [ sum_j tab[k,j] x[j] ]_k */
+  for (k=1; k<=N; k++)
+  {
+    pari_sp av = avma;
+    GEN s = gen_0;
+    for (j=1; j<=N; j++)
+    {
+      GEN c = gcoeff(tab,k,j);
+      if (!gequal0(c)) s = gadd(s, gmul(c, gel(x,j)));
+    }
+    gel(v,k) = gerepileupto(av,s);
+  }
+  return v;
+}
+/* as tablemul_ei, assume x a ZV of correct length */
+GEN
+zk_ei_mul(GEN nf, GEN x, long i)
+{
+  long j, k, N;
+  GEN v, tab;
+
+  if (i==1) return ZC_copy(x);
+  tab = get_tab(nf, &N); tab += (i-1)*N;
+  v = cgetg(N+1,t_COL);
+  for (k=1; k<=N; k++)
+  {
+    pari_sp av = avma;
+    GEN s = gen_0;
+    for (j=1; j<=N; j++)
+    {
+      GEN c = gcoeff(tab,k,j);
+      if (signe(c)) s = addii(s, _mulii(c, gel(x,j)));
+    }
+    gel(v,k) = gerepileuptoint(av, s);
+  }
+  return v;
+}
+
+/* table of multiplication by wi in R[w1,..., wN] */
+GEN
+ei_multable(GEN TAB, long i)
+{
+  long k,N;
+  GEN m, tab = get_tab(TAB, &N);
+  tab += (i-1)*N;
+  m = cgetg(N+1,t_MAT);
+  for (k=1; k<=N; k++) gel(m,k) = gel(tab,k);
+  return m;
+}
+
+GEN
+zk_multable(GEN nf, GEN x)
+{
+  long i, l = lg(x);
+  GEN mul = cgetg(l,t_MAT);
+  gel(mul,1) = x; /* assume w_1 = 1 */
+  for (i=2; i<l; i++) gel(mul,i) = zk_ei_mul(nf,x,i);
+  return mul;
+}
+GEN
+multable(GEN M, GEN x)
+{
+  long i, N;
+  GEN mul;
+  if (typ(x) == t_MAT) return x;
+  M = get_tab(M, &N);
+  if (typ(x) != t_COL) return scalarmat(x, N);
+  mul = cgetg(N+1,t_MAT);
+  gel(mul,1) = x; /* assume w_1 = 1 */
+  for (i=2; i<=N; i++) gel(mul,i) = tablemul_ei(M,x,i);
+  return mul;
+}
+
+/* x integral in nf; table of multiplication by x in ZK = Z[w1,..., wN].
+ * Return a t_INT if x is scalar, and a ZM otherwise */
+GEN
+zk_scalar_or_multable(GEN nf, GEN x)
+{
+  long tx = typ(x);
+  if (tx == t_MAT || tx == t_INT) return x;
+  x = nf_to_scalar_or_basis(nf, x);
+  return (typ(x) == t_COL)? zk_multable(nf, x): x;
+}
+
+GEN
+nftrace(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  nf = checknf(nf);
+  x = nf_to_scalar_or_basis(nf, x);
+  x = (typ(x) == t_COL)? RgV_dotproduct(x, gel(nf_get_Tr(nf),1))
+                       : gmulgs(x, nf_get_degree(nf));
+  return gerepileupto(av, x);
+}
+GEN
+rnfelttrace(GEN rnf, GEN x)
+{
+  pari_sp av = avma;
+  checkrnf(rnf);
+  x = rnfeltabstorel(rnf, x);
+  x = (typ(x) == t_POLMOD)? rnfeltdown(rnf, gtrace(x))
+                          : gmulgs(x, rnf_get_degree(rnf));
+  return gerepileupto(av, x);
+}
+
+/* assume nf is a genuine nf, fa a famat */
+static GEN
+famat_norm(GEN nf, GEN fa)
+{
+  pari_sp av = avma;
+  GEN g = gel(fa,1), e = gel(fa,2), N = gen_1;
+  long i, l = lg(g);
+  for (i = 1; i < l; i++)
+    N = gmul(N, powgi(nfnorm(nf, gel(g,i)), gel(e,i)));
+  return gerepileupto(av, N);
+}
+GEN
+nfnorm(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  nf = checknf(nf);
+  if (typ(x) == t_MAT) return famat_norm(nf, x);
+  x = nf_to_scalar_or_alg(nf, x);
+  x = (typ(x) == t_POL)? RgXQ_norm(x, nf_get_pol(nf))
+                       : gpowgs(x, nf_get_degree(nf));
+  return gerepileupto(av, x);
+}
+
+GEN
+rnfeltnorm(GEN rnf, GEN x)
+{
+  pari_sp av = avma;
+  checkrnf(rnf);
+  x = rnfeltabstorel(rnf, x);
+  x = (typ(x) == t_POLMOD)? rnfeltdown(rnf, gnorm(x))
+                          : gpowgs(x, rnf_get_degree(rnf));
+  return gerepileupto(av, x);
+}
+
+/* sum of x and y in nf */
+GEN
+nfadd(GEN nf, GEN x, GEN y)
+{
+  GEN z;
+  pari_sp av = avma;
+
+  nf = checknf(nf);
+  x = nf_to_scalar_or_basis(nf, x);
+  y = nf_to_scalar_or_basis(nf, y);
+  if (typ(x) != t_COL) {
+    if (typ(y) == t_COL) z = RgC_Rg_add(y, x);
+    else {
+      long N = nf_get_degree(nf);
+      z = zerocol(N); gel(z,1) = gadd(x,y);
+    }
+  } else {
+    if (typ(y) != t_COL) z = RgC_Rg_add(x, y);
+    else z = RgC_add(x, y);
+  }
+  return gerepileupto(av, z);
+}
+
+/* product of x and y in nf */
+GEN
+nfmul(GEN nf, GEN x, GEN y)
+{
+  GEN z;
+  pari_sp av = avma;
+
+  if (x == y) return nfsqr(nf,x);
+
+  nf = checknf(nf);
+  x = nf_to_scalar_or_basis(nf, x);
+  y = nf_to_scalar_or_basis(nf, y);
+  if (typ(x) != t_COL)
+  {
+    if (typ(y) == t_COL) z = RgC_Rg_mul(y, x);
+    else {
+      long N = nf_get_degree(nf);
+      z = zerocol(N); gel(z,1) = gmul(x,y);
+    }
+  }
+  else
+  {
+    if (typ(y) != t_COL) z = RgC_Rg_mul(x, y);
+    else {
+      GEN dx, dy;
+      x = Q_remove_denom(x, &dx);
+      y = Q_remove_denom(y, &dy);
+      z = nfmuli(nf,x,y);
+      dx = mul_denom(dx,dy);
+      if (dx) z = RgC_Rg_div(z, dx);
+    }
+  }
+  return gerepileupto(av, z);
+}
+/* square of x in nf */
+GEN
+nfsqr(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  GEN z;
+
+  nf = checknf(nf);
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) != t_COL)
+  {
+    long N = nf_get_degree(nf);
+    z = zerocol(N); gel(z,1) = gsqr(x);
+  }
+  else
+  {
+    GEN dx;
+    x = Q_remove_denom(x, &dx);
+    z = nfsqri(nf,x);
+    if (dx) z = RgC_Rg_div(z, sqri(dx));
+  }
+  return gerepileupto(av, z);
+}
+
+GEN
+nfC_nf_mul(GEN nf, GEN v, GEN x)
+{
+  long tx, l, i;
+  GEN y, dx = NULL;
+
+  x = nf_to_scalar_or_basis(nf, x);
+  tx = typ(x);
+  if (tx != t_COL) {
+    if (tx == t_INT)
+    {
+      long s = signe(x);
+      if (!s) return zerocol(lg(v)-1);
+      if (is_pm1(x)) return s > 0? leafcopy(v): RgC_neg(v);
+    }
+    l = lg(v); y = cgetg(l, t_COL);
+    for (i=1; i < l; i++)
+    {
+      GEN c = gel(v,i);
+      if (typ(c) != t_COL) c = gmul(c, x); else c = RgC_Rg_mul(c, x);
+      gel(y,i) = c;
+    }
+  }
+  else
+  {
+    x = Q_remove_denom(x, &dx);
+    x = zk_multable(nf, x);
+    l = lg(v); y = cgetg(l, t_COL);
+    for (i=1; i < l; i++)
+    {
+      GEN c = gel(v,i);
+      if (typ(c)!=t_COL) {
+        if (!isintzero(c)) c = RgC_Rg_mul(gel(x,1), c);
+      } else {
+        c = RgM_RgC_mul(x,c);
+        if (QV_isscalar(c)) c = gel(c,1);
+      }
+      gel(y,i) = c;
+    }
+  }
+  return dx? RgC_Rg_div(y, dx): y;
+}
+static GEN
+mulbytab(GEN M, GEN c)
+{ return typ(c) == t_COL? RgM_RgC_mul(M,c): RgC_Rg_mul(gel(M,1), c); }
+GEN
+tablemulvec(GEN M, GEN x, GEN v)
+{
+  long l, i;
+  GEN y;
+
+  if (typ(x) == t_COL && RgV_isscalar(x))
+  {
+    x = gel(x,1);
+    return typ(v) == t_POL? RgX_Rg_mul(v,x): RgV_Rg_mul(v,x);
+  }
+  x = multable(M, x); /* multiplication table by x */
+  y = cgetg_copy(v, &l);
+  if (typ(v) == t_POL)
+  {
+    y[1] = v[1];
+    for (i=2; i < l; i++) gel(y,i) = mulbytab(x, gel(v,i));
+    y = normalizepol(y);
+  }
+  else
+  {
+    for (i=1; i < l; i++) gel(y,i) = mulbytab(x, gel(v,i));
+  }
+  return y;
+}
+
+/* inverse of x in nf */
+GEN
+nfinv(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  GEN T, z;
+
+  nf = checknf(nf); T = nf_get_pol(nf);
+  x = nf_to_scalar_or_alg(nf, x);
+  if (typ(x) == t_POL)
+    z = poltobasis(nf, QXQ_inv(x, T));
+  else {
+    z = zerocol(degpol(T)); gel(z,1) = ginv(x);
+  }
+  return gerepileupto(av, z);
+}
+
+/* quotient of x and y in nf */
+GEN
+nfdiv(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN T, z;
+
+  nf = checknf(nf); T = nf_get_pol(nf);
+  y = nf_to_scalar_or_alg(nf, y);
+  if (typ(y) != t_POL) {
+    x = nf_to_scalar_or_basis(nf, x);
+    if (typ(x) == t_COL) z = RgC_Rg_div(x, y);
+    else {
+      z = zerocol(degpol(T)); gel(z,1) = gdiv(x,y);
+    }
+  }
+  else
+  {
+    x = nf_to_scalar_or_alg(nf, x);
+    z = QXQ_inv(y, T);
+    z = (typ(x) == t_POL)? RgXQ_mul(z, x, T): RgX_Rg_mul(z, x);
+    z = poltobasis(nf, z);
+  }
+  return gerepileupto(av, z);
+}
+
+/* product of INTEGERS (t_INT or ZC) x and y in nf
+ * compute xy as ( sum_i x_i sum_j y_j m^{i,j}_k )_k */
+GEN
+nfmuli(GEN nf, GEN x, GEN y)
+{
+  long i, j, k, N;
+  GEN s, v, TAB = get_tab(nf, &N);
+
+  if (typ(x) == t_INT)
+  {
+    if (typ(y) == t_INT) return scalarcol(mulii(x,y), N);
+    return ZC_Z_mul(y, x);
+  }
+  if (typ(y) == t_INT) return ZC_Z_mul(x, y);
+  /* both x and y are ZV */
+  v = cgetg(N+1,t_COL);
+  for (k=1; k<=N; k++)
+  {
+    pari_sp av = avma;
+    GEN TABi = TAB;
+    if (k == 1)
+      s = mulii(gel(x,1),gel(y,1));
+    else
+      s = addii(mulii(gel(x,1),gel(y,k)),
+                mulii(gel(x,k),gel(y,1)));
+    for (i=2; i<=N; i++)
+    {
+      GEN t, xi = gel(x,i);
+      TABi += N;
+      if (!signe(xi)) continue;
+
+      t = NULL;
+      for (j=2; j<=N; j++)
+      {
+        GEN p1, c = gcoeff(TABi, k, j); /* m^{i,j}_k */
+        if (!signe(c)) continue;
+        p1 = _mulii(c, gel(y,j));
+        t = t? addii(t, p1): p1;
+      }
+      if (t) s = addii(s, mulii(xi, t));
+    }
+    gel(v,k) = gerepileuptoint(av,s);
+  }
+  return v;
+}
+/* square of INTEGER (t_INT or ZC) x in nf */
+GEN
+nfsqri(GEN nf, GEN x)
+{
+  long i, j, k, N;
+  GEN s, v, TAB = get_tab(nf, &N);
+
+  if (typ(x) == t_INT) return scalarcol(sqri(x), N);
+  v = cgetg(N+1,t_COL);
+  for (k=1; k<=N; k++)
+  {
+    pari_sp av = avma;
+    GEN TABi = TAB;
+    if (k == 1)
+      s = sqri(gel(x,1));
+    else
+      s = shifti(mulii(gel(x,1),gel(x,k)), 1);
+    for (i=2; i<=N; i++)
+    {
+      GEN p1, c, t, xi = gel(x,i);
+      TABi += N;
+      if (!signe(xi)) continue;
+
+      c = gcoeff(TABi, k, i);
+      t = signe(c)? _mulii(c,xi): NULL;
+      for (j=i+1; j<=N; j++)
+      {
+        c = gcoeff(TABi, k, j);
+        if (!signe(c)) continue;
+        p1 = _mulii(c, shifti(gel(x,j),1));
+        t = t? addii(t, p1): p1;
+      }
+      if (t) s = addii(s, mulii(xi, t));
+    }
+    gel(v,k) = gerepileuptoint(av,s);
+  }
+  return v;
+}
+
+/* both x and y are RgV */
+GEN
+tablemul(GEN TAB, GEN x, GEN y)
+{
+  long i, j, k, N;
+  GEN s, v;
+  if (typ(x) != t_COL) return gmul(x, y);
+  if (typ(y) != t_COL) return gmul(y, x);
+  N = lg(x)-1;
+  v = cgetg(N+1,t_COL);
+  for (k=1; k<=N; k++)
+  {
+    pari_sp av = avma;
+    GEN TABi = TAB;
+    if (k == 1)
+      s = gmul(gel(x,1),gel(y,1));
+    else
+      s = gadd(gmul(gel(x,1),gel(y,k)),
+               gmul(gel(x,k),gel(y,1)));
+    for (i=2; i<=N; i++)
+    {
+      GEN t, xi = gel(x,i);
+      TABi += N;
+      if (gequal0(xi)) continue;
+
+      t = NULL;
+      for (j=2; j<=N; j++)
+      {
+        GEN p1, c = gcoeff(TABi, k, j); /* m^{i,j}_k */
+        if (gequal0(c)) continue;
+        p1 = gmul(c, gel(y,j));
+        t = t? gadd(t, p1): p1;
+      }
+      if (t) s = gadd(s, gmul(xi, t));
+    }
+    gel(v,k) = gerepileupto(av,s);
+  }
+  return v;
+}
+GEN
+tablesqr(GEN TAB, GEN x)
+{
+  long i, j, k, N;
+  GEN s, v;
+
+  if (typ(x) != t_COL) return gsqr(x);
+  N = lg(x)-1;
+  v = cgetg(N+1,t_COL);
+
+  for (k=1; k<=N; k++)
+  {
+    pari_sp av = avma;
+    GEN TABi = TAB;
+    if (k == 1)
+      s = gsqr(gel(x,1));
+    else
+      s = gmul2n(gmul(gel(x,1),gel(x,k)), 1);
+    for (i=2; i<=N; i++)
+    {
+      GEN p1, c, t, xi = gel(x,i);
+      TABi += N;
+      if (gequal0(xi)) continue;
+
+      c = gcoeff(TABi, k, i);
+      t = !gequal0(c)? gmul(c,xi): NULL;
+      for (j=i+1; j<=N; j++)
+      {
+        c = gcoeff(TABi, k, j);
+        if (gequal0(c)) continue;
+        p1 = gmul(gmul2n(c,1), gel(x,j));
+        t = t? gadd(t, p1): p1;
+      }
+      if (t) s = gadd(s, gmul(xi, t));
+    }
+    gel(v,k) = gerepileupto(av,s);
+  }
+  return v;
+}
+
+static GEN
+_mul(void *data, GEN x, GEN y) { return nfmuli((GEN)data,x,y); }
+static GEN
+_sqr(void *data, GEN x) { return nfsqri((GEN)data,x); }
+
+/* Compute z^n in nf, left-shift binary powering */
+GEN
+nfpow(GEN nf, GEN z, GEN n)
+{
+  pari_sp av = avma;
+  long s, N;
+  GEN x, cx, T;
+
+  if (typ(n)!=t_INT) pari_err_TYPE("nfpow",n);
+  nf = checknf(nf); T = nf_get_pol(nf); N = degpol(T);
+  s = signe(n); if (!s) return scalarcol_shallow(gen_1,N);
+  x = nf_to_scalar_or_basis(nf, z);
+  if (typ(x) != t_COL) { GEN y = zerocol(N); gel(y,1) = powgi(x,n); return y; }
+  if (s < 0) { /* simplified nfinv */
+    x = nf_to_scalar_or_alg(nf, z);
+    x = poltobasis(nf, QXQ_inv(x, T));
+    n = absi(n);
+  }
+  x = primitive_part(x, &cx);
+  x = gen_pow(x, n, (void*)nf, _sqr, _mul);
+  if (cx) x = RgC_Rg_mul(x, powgi(cx, n));
+  return av==avma? gcopy(x): gerepileupto(av,x);
+}
+/* Compute z^n in nf, left-shift binary powering */
+GEN
+nfpow_u(GEN nf, GEN z, ulong n)
+{
+  pari_sp av = avma;
+  long N;
+  GEN x, cx, T;
+
+  nf = checknf(nf); T = nf_get_pol(nf); N = degpol(T);
+  if (!n) return scalarcol_shallow(gen_1,N);
+  x = nf_to_scalar_or_basis(nf, z);
+  if (typ(x) != t_COL) { GEN y = zerocol(N); gel(y,1) = gpowgs(x,n); return y; }
+  x = primitive_part(x, &cx);
+  x = gen_powu(x, n, (void*)nf, _sqr, _mul);
+  if (cx) x = RgC_Rg_mul(x, powgi(cx, utoipos(n)));
+  return av==avma? gcopy(x): gerepileupto(av,x);
+}
+
+typedef struct {
+  GEN nf, p;
+  long I;
+} eltmod_muldata;
+
+static GEN
+sqr_mod(void *data, GEN x)
+{
+  eltmod_muldata *D = (eltmod_muldata*)data;
+  return FpC_red(nfsqri(D->nf, x), D->p);
+}
+static GEN
+ei_msqr_mod(void *data, GEN x)
+{
+  GEN x2 = sqr_mod(data, x);
+  eltmod_muldata *D = (eltmod_muldata*)data;
+  return FpC_red(zk_ei_mul(D->nf, x2, D->I), D->p);
+}
+
+/* x = I-th vector of the Z-basis of Z_K, in Z^n, compute lift(x^n mod p) */
+GEN
+pow_ei_mod_p(GEN nf, long I, GEN n, GEN p)
+{
+  pari_sp av = avma;
+  eltmod_muldata D;
+  long s,N;
+  GEN y;
+
+  if (typ(n) != t_INT) pari_err_TYPE("nfpow",n);
+  nf = checknf(nf); N = nf_get_degree(nf);
+  s = signe(n);
+  if (s < 0) pari_err_IMPL("negative power in pow_ei_mod_p");
+  if (!s || I == 1) return scalarcol_shallow(gen_1,N);
+  D.nf = nf;
+  D.p = p;
+  D.I = I;
+  y = gen_pow_fold(col_ei(N, I), n, (void*)&D, &sqr_mod, &ei_msqr_mod);
+  return gerepileupto(av,y);
+}
+
+/* valuation of integral x (ZV), with resp. to prime ideal pr */
+long
+ZC_nfvalrem(GEN nf, GEN x, GEN pr, GEN *newx)
+{
+  long i, v, l;
+  GEN r, y, p = pr_get_p(pr), mul = zk_scalar_or_multable(nf, pr_get_tau(pr));
+
+  if (typ(mul) == t_INT) return newx? ZV_pvalrem(x, p, newx):ZV_pval(x, p);
+  y = cgetg_copy(x, &l); /* will hold the new x */
+  x = leafcopy(x);
+  for(v=0;; v++)
+  {
+    for (i=1; i<l; i++)
+    { /* is (x.b)[i] divisible by p ? */
+      gel(y,i) = dvmdii(ZMrow_ZC_mul(mul,x,i),p,&r);
+      if (r != gen_0) { if (newx) *newx = x; return v; }
+    }
+    swap(x, y);
+  }
+}
+long
+ZC_nfval(GEN nf, GEN x, GEN P)
+{ return ZC_nfvalrem(nf, x, P, NULL); }
+
+/* v_P(x) != 0, x a ZV. Simpler version of ZC_nfvalrem */
+int
+ZC_prdvd(GEN nf, GEN x, GEN P)
+{
+  pari_sp av = avma;
+  long i, l;
+  GEN p = pr_get_p(P), mul = zk_scalar_or_multable(nf, pr_get_tau(P));
+  if (typ(mul) == t_INT) return ZV_Z_dvd(x, p);
+  l = lg(x);
+  for (i=1; i<l; i++)
+    if (remii(ZMrow_ZC_mul(mul,x,i), p) != gen_0) { avma = av; return 0; }
+  avma = av; return 1;
+}
+
+int
+pr_equal(GEN nf, GEN P, GEN Q)
+{
+  GEN gQ, p = pr_get_p(P);
+  long e = pr_get_e(P), f = pr_get_f(P), n;
+  if (!equalii(p, pr_get_p(Q)) || e != pr_get_e(Q) || f != pr_get_f(Q))
+    return 0;
+  gQ = pr_get_gen(Q); n = lg(gQ)-1;
+  if (2*e*f > n) return 1; /* room for only one such pr */
+  return ZV_equal(pr_get_gen(P), gQ) || ZC_prdvd(nf, gQ, P);
+}
+
+long
+nfval(GEN nf, GEN x, GEN pr)
+{
+  pari_sp av = avma;
+  long w, e;
+  GEN cx, p;
+
+  if (gequal0(x)) return LONG_MAX;
+  nf = checknf(nf);
+  checkprid(pr);
+  p = pr_get_p(pr);
+  e = pr_get_e(pr);
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) != t_COL) return e*Q_pval(x,p);
+  x = Q_primitive_part(x, &cx);
+  w = ZC_nfval(nf,x,pr);
+  if (cx) w += e*Q_pval(cx,p);
+  avma = av; return w;
+}
+
+/* want to write p^v = uniformizer^(e*v) * z^v, z coprime to pr */
+/* z := tau^e / p^(e-1), algebraic integer coprime to pr; return z^v */
+static GEN
+powp(GEN nf, GEN pr, long v)
+{
+  GEN b, z;
+  long e;
+  if (!v) return gen_1;
+  b = pr_get_tau(pr);
+  if (typ(b) == t_INT) return gen_1;
+  e = pr_get_e(pr);
+  z = gel(b,1);
+  if (e != 1) z = gdiv(nfpow_u(nf, z, e), powiu(pr_get_p(pr),e-1));
+  return nfpow_u(nf, z, v);
+}
+long
+nfvalrem(GEN nf, GEN x, GEN pr, GEN *py)
+{
+  pari_sp av = avma;
+  long w, e;
+  GEN cx, p, t;
+
+  if (!py) return nfval(nf,x,pr);
+  if (gequal0(x)) { *py = gcopy(x); return LONG_MAX; }
+  nf = checknf(nf);
+  checkprid(pr);
+  p = pr_get_p(pr);
+  e = pr_get_e(pr);
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) != t_COL) {
+    w = Q_pvalrem(x,p, py);
+    if (!w) { *py = gerepilecopy(av, x); return 0; }
+    *py = gerepileupto(av, gmul(powp(nf, pr, w), *py));
+    return e*w;
+  }
+  x = Q_primitive_part(x, &cx);
+  w = ZC_nfvalrem(nf,x,pr, py);
+  if (cx)
+  {
+    long v = Q_pvalrem(cx,p, &t);
+    *py = nfmul(nf, *py, gmul(powp(nf,pr,v), t));
+    *py = gerepileupto(av, *py);
+    w += e*v;
+  }
+  else
+    *py = gerepilecopy(av, *py);
+  return w;
+}
+
+GEN
+coltoalg(GEN nf, GEN x)
+{
+  return mkpolmod( coltoliftalg(nf, x), nf_get_pol(nf) );
+}
+
+GEN
+basistoalg(GEN nf, GEN x)
+{
+  GEN z, T;
+
+  nf = checknf(nf);
+  switch(typ(x))
+  {
+    case t_COL: {
+      pari_sp av = avma;
+      return gerepilecopy(av, coltoalg(nf, x));
+    }
+    case t_POLMOD:
+      T = nf_get_pol(nf);
+      if (!RgX_equal_var(T,gel(x,1)))
+        pari_err_MODULUS("basistoalg", T,gel(x,1));
+      return gcopy(x);
+    case t_POL:
+      T = nf_get_pol(nf);
+      if (varn(T) != varn(x)) pari_err_VAR("basistoalg",x,T);
+      z = cgetg(3,t_POLMOD);
+      gel(z,1) = ZX_copy(T);
+      gel(z,2) = RgX_rem(x, T); return z;
+    case t_INT:
+    case t_FRAC:
+      T = nf_get_pol(nf);
+      z = cgetg(3,t_POLMOD);
+      gel(z,1) = ZX_copy(T);
+      gel(z,2) = gcopy(x); return z;
+    default:
+      pari_err_TYPE("basistoalg",x);
+      return NULL; /* not reached */
+  }
+}
+
+/* Assume nf is a genuine nf. */
+GEN
+nf_to_scalar_or_basis(GEN nf, GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC:
+      return x;
+    case t_POLMOD:
+      x = checknfelt_mod(nf,x,"nf_to_scalar_or_basis");
+      if (typ(x) != t_POL) return x;
+      /* fall through */
+    case t_POL:
+    {
+      GEN T = nf_get_pol(nf);
+      long l = lg(x);
+      if (varn(x) != varn(T)) pari_err_VAR("nf_to_scalar_or_basis", x,T);
+      if (l >= lg(T)) { x = RgX_rem(x, T); l = lg(x); }
+      if (l == 2) return gen_0;
+      if (l == 3) return gel(x,2);
+      return poltobasis(nf,x);
+    }
+    case t_COL:
+      if (lg(x) != lg(nf_get_zk(nf))) break;
+      return QV_isscalar(x)? gel(x,1): x;
+  }
+  pari_err_TYPE("nf_to_scalar_or_basis",x);
+  return NULL; /* not reached */
+}
+/* Let x be a polynomial with coefficients in Q or nf. Return the same
+ * polynomial with coefficients expressed as vectors (on the integral basis).
+ * No consistency checks, not memory-clean. */
+GEN
+RgX_to_nfX(GEN nf, GEN x)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l); y[1] = x[1];
+  for (i=2; i<l; i++) gel(y,i) = nf_to_scalar_or_basis(nf, gel(x,i));
+  return y;
+}
+
+/* Assume nf is a genuine nf. */
+GEN
+nf_to_scalar_or_alg(GEN nf, GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC:
+      return x;
+    case t_POLMOD:
+      x = checknfelt_mod(nf,x,"nf_to_scalar_or_alg");
+      if (typ(x) != t_POL) return x;
+      /* fall through */
+    case t_POL:
+    {
+      GEN T = nf_get_pol(nf);
+      long l = lg(x);
+      if (varn(x) != varn(T)) pari_err_VAR("nf_to_scalar_or_alg", x,T);
+      if (l >= lg(T)) { x = RgX_rem(x, T); l = lg(x); }
+      if (l == 2) return gen_0;
+      if (l == 3) return gel(x,2);
+      return x;
+    }
+    case t_COL:
+      if (lg(x) != lg(nf_get_zk(nf))) break;
+      return QV_isscalar(x)? gel(x,1): coltoliftalg(nf, x);
+  }
+  pari_err_TYPE("nf_to_scalar_or_alg",x);
+  return NULL; /* not reached */
+}
+
+/* gmul(A, RgX_to_Rg(x)), A t_MAT (or t_VEC) of compatible dimensions */
+GEN
+mulmat_pol(GEN A, GEN x)
+{
+  long i,l;
+  GEN z;
+  if (typ(x) != t_POL) return gmul(x,gel(A,1)); /* scalar */
+  l=lg(x)-1; if (l == 1) return typ(A)==t_VEC? gen_0: zerocol(nbrows(A));
+  x++; z = gmul(gel(x,1), gel(A,1));
+  for (i=2; i<l ; i++)
+    if (!gequal0(gel(x,i))) z = gadd(z, gmul(gel(x,i), gel(A,i)));
+  return z;
+}
+
+/* x a t_POL, nf a genuine nf. No garbage collecting. No check.  */
+GEN
+poltobasis(GEN nf, GEN x)
+{
+  GEN P = nf_get_pol(nf);
+  if (varn(x) != varn(P)) pari_err_VAR( "poltobasis", x,P);
+  if (degpol(x) >= degpol(P)) x = RgX_rem(x,P);
+  return mulmat_pol(nf_get_invzk(nf), x);
+}
+
+GEN
+algtobasis(GEN nf, GEN x)
+{
+  pari_sp av;
+
+  nf = checknf(nf);
+  switch(typ(x))
+  {
+    case t_POLMOD:
+      if (!RgX_equal_var(nf_get_pol(nf),gel(x,1)))
+        pari_err_MODULUS("algtobasis", nf_get_pol(nf),gel(x,1));
+      x = gel(x,2);
+      switch(typ(x))
+      {
+        case t_INT:
+        case t_FRAC: return scalarcol(x, nf_get_degree(nf));
+        case t_POL:
+          av = avma;
+          return gerepileupto(av,poltobasis(nf,x));
+      }
+      break;
+
+    case t_POL:
+      av = avma;
+      return gerepileupto(av,poltobasis(nf,x));
+
+    case t_COL:
+      if (lg(x)-1 != nf_get_degree(nf)) pari_err_DIM("nfalgtobasis");
+      return gcopy(x);
+
+    case t_INT:
+    case t_FRAC: return scalarcol(x, nf_get_degree(nf));
+  }
+  pari_err_TYPE("algtobasis",x);
+  return NULL; /* not reached */
+}
+
+GEN
+rnfbasistoalg(GEN rnf,GEN x)
+{
+  const char *f = "rnfbasistoalg";
+  long lx, i;
+  pari_sp av = avma;
+  GEN z, nf, relpol, T;
+
+  checkrnf(rnf);
+  nf = rnf_get_nf(rnf);
+  T = nf_get_pol(nf);
+  relpol = QXQX_to_mod_shallow(rnf_get_pol(rnf), T);
+  switch(typ(x))
+  {
+    case t_COL:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++)
+      {
+        GEN c = nf_to_scalar_or_alg(nf, gel(x,i));
+        if (typ(c) == t_POL) c = mkpolmod(c,T);
+        gel(z,i) = c;
+      }
+      z = RgV_RgC_mul(gel(rnf_get_zk(rnf),1), z);
+      return gerepileupto(av, gmodulo(z,relpol));
+
+    case t_POLMOD:
+      x = polmod_nffix(f, rnf, x, 0);
+      if (typ(x) != t_POL) break;
+      retmkpolmod(RgX_copy(x), RgX_copy(relpol));
+    case t_POL:
+      if (varn(x) == varn(T))
+      {
+        if (!RgX_is_QX(x)) pari_err_TYPE(f,x);
+        x = gmodulo(x,T); break;
+      }
+      if (varn(x) == varn(relpol))
+      {
+        x = RgX_nffix(f,nf_get_pol(nf),x,0);
+        return gmodulo(x, relpol);
+      }
+      pari_err_VAR(f, x,relpol);
+  }
+  retmkpolmod(scalarpol(x, varn(relpol)), RgX_copy(relpol));
+}
+
+GEN
+matbasistoalg(GEN nf,GEN x)
+{
+  long i, j, li, lx;
+  GEN z = cgetg_copy(x, &lx);
+
+  if (lx == 1) return z;
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      for (i=1; i<lx; i++) gel(z,i) = basistoalg(nf, gel(x,i));
+      return z;
+    case t_MAT: break;
+    default: pari_err_TYPE("matbasistoalg",x);
+  }
+  li = lgcols(x);
+  for (j=1; j<lx; j++)
+  {
+    GEN c = cgetg(li,t_COL), xj = gel(x,j);
+    gel(z,j) = c;
+    for (i=1; i<li; i++) gel(c,i) = basistoalg(nf, gel(xj,i));
+  }
+  return z;
+}
+
+GEN
+matalgtobasis(GEN nf,GEN x)
+{
+  long i, j, li, lx;
+  GEN z = cgetg_copy(x, &lx);
+
+  if (lx == 1) return z;
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      for (i=1; i<lx; i++) gel(z,i) = algtobasis(nf, gel(x,i));
+      return z;
+    case t_MAT: break;
+    default: pari_err_TYPE("matalgtobasis",x);
+  }
+  li = lgcols(x);
+  for (j=1; j<lx; j++)
+  {
+    GEN c = cgetg(li,t_COL), xj = gel(x,j);
+    gel(z,j) = c;
+    for (i=1; i<li; i++) gel(c,i) = algtobasis(nf, gel(xj,i));
+  }
+  return z;
+}
+GEN
+RgM_to_nfM(GEN nf,GEN x)
+{
+  long i, j, li, lx;
+  GEN z = cgetg_copy(x, &lx);
+
+  if (lx == 1) return z;
+  li = lgcols(x);
+  for (j=1; j<lx; j++)
+  {
+    GEN c = cgetg(li,t_COL), xj = gel(x,j);
+    gel(z,j) = c;
+    for (i=1; i<li; i++) gel(c,i) = nf_to_scalar_or_basis(nf, gel(xj,i));
+  }
+  return z;
+}
+GEN
+RgC_to_nfC(GEN nf,GEN x)
+{
+  long i, lx = lg(x);
+  GEN z = cgetg(lx, t_COL);
+  for (i=1; i<lx; i++) gel(z,i) = nf_to_scalar_or_basis(nf, gel(x,i));
+  return z;
+}
+
+/* x a t_POLMOD, supposedly in rnf = K[z]/(T), K = Q[y]/(Tnf) */
+GEN
+polmod_nffix(const char *f, GEN rnf, GEN x, int lift)
+{ return polmod_nffix2(f, rnf_get_nfpol(rnf), rnf_get_pol(rnf), x,lift); }
+GEN
+polmod_nffix2(const char *f, GEN T, GEN relpol, GEN x, int lift)
+{
+  if (RgX_equal_var(gel(x,1),relpol))
+  {
+    x = gel(x,2);
+    if (typ(x) == t_POL && varn(x) == varn(relpol))
+    {
+      x = RgX_nffix(f, T, x, lift);
+      switch(lg(x))
+      {
+        case 2: return gen_0;
+        case 3: return gel(x,2);
+      }
+      return x;
+    }
+  }
+  return Rg_nffix(f, T, x, lift);
+}
+GEN
+rnfalgtobasis(GEN rnf,GEN x)
+{
+  const char *f = "rnfalgtobasis";
+  pari_sp av = avma;
+  GEN T, relpol;
+
+  checkrnf(rnf);
+  relpol = rnf_get_pol(rnf);
+  T = rnf_get_nfpol(rnf);
+  switch(typ(x))
+  {
+    case t_COL:
+      if (lg(x)-1 != rnf_get_degree(rnf)) pari_err_DIM(f);
+      x = RgV_nffix(f, T, x, 0);
+      return gerepilecopy(av, x);
+
+    case t_POLMOD:
+      x = polmod_nffix(f, rnf, x, 0);
+      if (typ(x) != t_POL) break;
+      return gerepileupto(av, mulmat_pol(rnf_get_invzk(rnf), x));
+    case t_POL:
+      if (varn(x) == varn(T))
+      {
+        if (!RgX_is_QX(x)) pari_err_TYPE(f,x);
+        x = mkpolmod(x,T); break;
+      }
+      x = RgX_nffix(f, T, x, 0);
+      if (degpol(x) >= degpol(relpol)) x = RgX_rem(x,relpol);
+      return gerepileupto(av, mulmat_pol(rnf_get_invzk(rnf), x));
+  }
+  return gerepileupto(av, scalarcol(x, rnf_get_degree(rnf)));
+}
+
+/* Given a and b in nf, gives an algebraic integer y in nf such that a-b.y
+ * is "small" */
+GEN
+nfdiveuc(GEN nf, GEN a, GEN b)
+{
+  pari_sp av = avma;
+  a = nfdiv(nf,a,b);
+  return gerepileupto(av, ground(a));
+}
+
+/* Given a and b in nf, gives a "small" algebraic integer r in nf
+ * of the form a-b.y */
+GEN
+nfmod(GEN nf, GEN a, GEN b)
+{
+  pari_sp av = avma;
+  GEN p1 = gneg_i(nfmul(nf,b,ground(nfdiv(nf,a,b))));
+  return gerepileupto(av, nfadd(nf,a,p1));
+}
+
+/* Given a and b in nf, gives a two-component vector [y,r] in nf such
+ * that r=a-b.y is "small". */
+GEN
+nfdivrem(GEN nf, GEN a, GEN b)
+{
+  pari_sp av = avma;
+  GEN p1,z, y = ground(nfdiv(nf,a,b));
+
+  p1 = gneg_i(nfmul(nf,b,y));
+  z = cgetg(3,t_VEC);
+  gel(z,1) = gcopy(y);
+  gel(z,2) = nfadd(nf,a,p1); return gerepileupto(av, z);
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**                           (Z_K/I)^*                                 **/
+/**                                                                     **/
+/*************************************************************************/
+/* return sign(sigma_k(x)), x t_COL (integral, primitive) */
+static long
+eval_sign(GEN M, GEN x, long k)
+{
+  long i, l = lg(x);
+  GEN z = gel(x,1); /* times M[k,1], which is 1 */
+  for (i = 2; i < l; i++)
+    z = mpadd(z, mpmul(gcoeff(M,k,i), gel(x,i)));
+  if (realprec(z) < DEFAULTPREC) pari_err_PREC("nfsign_arch");
+  return signe(z);
+}
+
+GEN
+vec01_to_indices(GEN v)
+{
+  long i, k, l;
+  GEN p;
+
+  switch (typ(v))
+  {
+   case t_VECSMALL: return v;
+   case t_VEC: break;
+   default: pari_err_TYPE("vec01_to_indices",v);
+  }
+  l = lg(v);
+  p = new_chunk(l) + l;
+  for (k=1, i=l-1; i; i--)
+    if (signe(gel(v,i))) { *--p = i; k++; }
+  *--p = evallg(k) | evaltyp(t_VECSMALL);
+  avma = (pari_sp)p; return p;
+}
+GEN
+indices_to_vec01(GEN p, long r)
+{
+  long i, l = lg(p);
+  GEN v = zerovec(r);
+  for (i = 1; i < l; i++) gel(v, p[i]) = gen_1;
+  return v;
+}
+
+/* return (column) vector of R1 signatures of x (0 or 1) */
+GEN
+nfsign_arch(GEN nf, GEN x, GEN arch)
+{
+  GEN M, V, archp = vec01_to_indices(arch);
+  long i, s, n = lg(archp)-1;
+  pari_sp av;
+
+  if (!n) return cgetg(1,t_VECSMALL);
+  nf = checknf(nf);
+  if (typ(x) == t_MAT)
+  { /* factorisation */
+    GEN g = gel(x,1), e = gel(x,2);
+    V = zero_zv(n);
+    for (i=1; i<lg(g); i++)
+      if (mpodd(gel(e,i)))
+        Flv_add_inplace(V, nfsign_arch(nf,gel(g,i),archp), 2);
+    avma = (pari_sp)V; return V;
+  }
+  av = avma; V = cgetg(n+1,t_VECSMALL);
+  x = nf_to_scalar_or_basis(nf, x);
+  switch(typ(x))
+  {
+    case t_INT:
+      s = signe(x);
+      if (!s) pari_err_DOMAIN("nfsign_arch","element","=",gen_0,x);
+      avma = av; return const_vecsmall(n, (s < 0)? 1: 0);
+    case t_FRAC:
+      s = signe(gel(x,1));
+      avma = av; return const_vecsmall(n, (s < 0)? 1: 0);
+  }
+  x = Q_primpart(x); M = nf_get_M(nf);
+  for (i = 1; i <= n; i++) V[i] = (eval_sign(M, x, archp[i]) < 0)? 1: 0;
+  avma = (pari_sp)V; return V;
+}
+
+/* return the vector of signs of x; the matrix of such if x is a vector
+ * of nf elements */
+GEN
+nfsign(GEN nf, GEN x)
+{
+  long i, l;
+  GEN arch, S;
+
+  nf = checknf(nf);
+  arch = identity_perm( nf_get_r1(nf) );
+  if (typ(x) != t_VEC) return nfsign_arch(nf, x, arch);
+  l = lg(x); S = cgetg(l, t_MAT);
+  for (i=1; i<l; i++) gel(S,i) = nfsign_arch(nf, gel(x,i), arch);
+  return S;
+}
+
+/* multiply y by t = 1 mod^* f such that sign(x) = sign(y) at arch = divisor[2].
+ * If x == NULL, make y >> 0 at sarch */
+GEN
+set_sign_mod_divisor(GEN nf, GEN x, GEN y, GEN divisor, GEN sarch)
+{
+  GEN s, archp, gen;
+  long nba,i;
+  if (!sarch) return y;
+  gen = gel(sarch,2); nba = lg(gen);
+  if (nba == 1) return y;
+
+  archp = vec01_to_indices(gel(divisor,2));
+  s = nfsign_arch(nf, y, archp);
+  if (x) Flv_add_inplace(s, nfsign_arch(nf, x, archp), 2);
+  s = Flm_Flc_mul(gel(sarch,3), s, 2);
+  for (i=1; i<nba; i++)
+    if (s[i]) y = nfmul(nf,y,gel(gen,i));
+  return y;
+}
+
+/* given an element x in Z_K and an integral ideal y in HNF, coprime with x,
+   outputs an element inverse of x modulo y */
+GEN
+nfinvmodideal(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN a, yZ = gcoeff(y,1,1);
+
+  if (is_pm1(yZ)) return zerocol( nf_get_degree(nf) );
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) == t_INT) return gerepileupto(av, Fp_inv(x, yZ));
+
+  a = hnfmerge_get_1(idealhnf_principal(nf,x), y);
+  if (!a) pari_err_INV("nfinvmodideal", x);
+  return gerepileupto(av, ZC_hnfrem(nfdiv(nf,a,x), y));
+}
+
+static GEN
+nfsqrmodideal(GEN nf, GEN x, GEN id) {
+  return ZC_hnfrem(nfsqri(nf,x), id);
+}
+static GEN
+nfmulmodideal(GEN nf, GEN x, GEN y, GEN id) {
+  return x? ZC_hnfrem(nfmuli(nf,x,y), id): y;
+}
+/* assume x integral, k integer, A in HNF */
+GEN
+nfpowmodideal(GEN nf,GEN x,GEN k,GEN A)
+{
+  long s = signe(k);
+  pari_sp av;
+  GEN y;
+
+  if (!s) return gen_1;
+  av = avma;
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) != t_COL) return Fp_pow(x, k, gcoeff(A,1,1));
+  if (s < 0) { x = nfinvmodideal(nf, x,A); k = absi(k); }
+  for(y = NULL;;)
+  {
+    if (mpodd(k)) y = nfmulmodideal(nf,y,x,A);
+    k = shifti(k,-1); if (!signe(k)) break;
+    x = nfsqrmodideal(nf,x,A);
+  }
+  return gerepileupto(av, y);
+}
+
+/* a * g^n mod id */
+static GEN
+elt_mulpow_modideal(GEN nf, GEN a, GEN g, GEN n, GEN id)
+{
+  return nfmulmodideal(nf, a, nfpowmodideal(nf,g,n,id), id);
+}
+
+/* assume (num(g[i]), id) = 1 for all i. Return prod g[i]^e[i] mod id.
+ * EX = multiple of exponent of (O_K/id)^* */
+GEN
+famat_to_nf_modideal_coprime(GEN nf, GEN g, GEN e, GEN id, GEN EX)
+{
+  GEN plus = NULL, minus = NULL, idZ = gcoeff(id,1,1);
+  pari_sp av = avma, lim = stack_lim(av,2);
+  long i, lx = lg(g);
+  GEN EXo2 = (expi(EX) > 10)? shifti(EX,-1): NULL;
+
+  if (is_pm1(idZ)) lx = 1; /* id = Z_K */
+  for (i=1; i<lx; i++)
+  {
+    GEN h, n = centermodii(gel(e,i), EX, EXo2);
+    long sn = signe(n);
+    if (!sn) continue;
+
+    h = nf_to_scalar_or_basis(nf, gel(g,i));
+    switch(typ(h))
+    {
+      case t_INT: break;
+      case t_FRAC:
+        h = Fp_div(gel(h,1), gel(h,2), idZ); break;
+      default:
+      {
+        GEN dh;
+        h = Q_remove_denom(h, &dh);
+        if (dh) h = FpC_Fp_mul(h, Fp_inv(dh,idZ), idZ);
+      }
+    }
+    if (sn > 0)
+      plus = elt_mulpow_modideal(nf, plus, h, n, id);
+    else /* sn < 0 */
+      minus = elt_mulpow_modideal(nf, minus, h, absi(n), id);
+
+    if (low_stack(lim, stack_lim(av, 2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"famat_to_nf_modideal_coprime");
+      if (!plus) plus = gen_0;
+      if (!minus) minus = gen_0;
+      gerepileall(av,2, &plus, &minus);
+      if (isintzero(plus)) plus = NULL;
+      if (isintzero(minus)) minus = NULL;
+    }
+  }
+  if (minus)
+    plus = nfmulmodideal(nf, plus, nfinvmodideal(nf,minus,id), id);
+  return plus? plus: scalarcol_shallow(gen_1, lg(id)-1);
+}
+
+/* given 2 integral ideals x, y in HNF s.t x | y | x^2, compute the quotient
+   (1+x)/(1+y) in the form [[cyc],[gen]], if U != NULL, set *U := ux^-1 */
+static GEN
+zidealij(GEN x, GEN y, GEN *U)
+{
+  GEN G, cyc;
+  long j, N;
+
+  /* x^(-1) y = relations between the 1 + x_i (HNF) */
+  cyc = ZM_snf_group(hnf_solve(x, y), U, &G);
+  N = lg(cyc); G = ZM_mul(x,G); settyp(G, t_VEC); /* new generators */
+  for (j=1; j<N; j++)
+  {
+    GEN c = gel(G,j);
+    gel(c,1) = addiu(gel(c,1), 1); /* 1 + g_j */
+    if (ZV_isscalar(c)) gel(G,j) = gel(c,1);
+  }
+  if (U) *U = RgM_mul(*U, RgM_inv(x));
+  return mkvec2(cyc, G);
+}
+
+static GEN
+Fq_FpXQ_log(GEN a, GEN g, GEN ord, GEN T, GEN p)
+{
+  if (!T) return Fp_log(a,g,ord,p);
+  if (typ(a)==t_INT) return Fp_FpXQ_log(a,g,ord,T,p);
+  return FpXQ_log(a,g,ord,T,p);
+}
+/* same in nf.zk / pr */
+static GEN
+nf_log(GEN nf, GEN a, GEN g, GEN ord, GEN pr)
+{
+  pari_sp av = avma;
+  GEN T,p, modpr = nf_to_Fq_init(nf, &pr, &T, &p);
+  GEN A = nf_to_Fq(nf,a,modpr);
+  GEN G = nf_to_Fq(nf,g,modpr);
+  return gerepileuptoint(av, Fq_FpXQ_log(A,G,ord,T,p));
+}
+
+/* Product of cyc entries, with cyc = diagonal(Smith Normal Form), assumed != 0.
+ * Set L to the index of the last non-trivial (!= 1) entry */
+GEN
+detcyc(GEN cyc, long *L)
+{
+  pari_sp av = avma;
+  long i, l = lg(cyc);
+  GEN s = gel(cyc,1);
+
+  if (l == 1) { *L = 1; return gen_1; }
+  for (i=2; i<l; i++)
+  {
+    GEN t = gel(cyc,i);
+    if (is_pm1(t)) break;
+    s = mulii(s, t);
+  }
+  *L = i; return i <= 2? icopy(s): gerepileuptoint(av,s);
+}
+
+/* (U,V) = 1. Return y = x mod U, = 1 mod V (uv: u + v = 1, u in U, v in V),
+ * namely u + x*v.
+ * Either (u,mv) are both t_INT, or
+ * u is a t_COL, mv is a t_MAT, multiplication table by v */
+static GEN
+makeprimetoideal(GEN UV, GEN u,GEN mv, GEN x)
+{
+  GEN t;
+  if (typ(mv) == t_INT) /* u t_INT */
+  {
+    if (typ(x) == t_INT) return addii(mulii(x,mv), u);
+    t = ZC_Z_add(ZC_Z_mul(x,mv), u);
+  }
+  else
+  {
+    t = typ(x) == t_INT? ZC_Z_mul(gel(mv,1),x): ZM_ZC_mul(mv,x);
+    t = ZC_add(t, u);
+  }
+  return ZC_hnfrem(t, UV);
+}
+
+static GEN
+makeprimetoidealvec(GEN UV, GEN u,GEN mv, GEN gen)
+{
+  long i, ly;
+  GEN y = cgetg_copy(gen, &ly);
+  for (i=1; i<ly; i++) gel(y,i) = makeprimetoideal(UV,u,mv, gel(gen,i));
+  return y;
+}
+
+/* Given an ideal pr^ep, and an integral ideal x (in HNF form) compute a list
+ * of vectors,corresponding to the abelian groups (O_K/pr)^*, and
+ * 1 + pr^i/ 1 + pr^min(2i, ep), i = 1,...
+ * Each vector has 5 components as follows :
+ * [[cyc],[g],[g'],[sign],U.X^-1].
+ * cyc   = type as abelian group
+ * g, g' = generators. (g',x) = 1, not necessarily so for g
+ * sign  = vector of the sign(g') at arch.
+ * If x = NULL, the original ideal was a prime power */
+static GEN
+zprimestar(GEN nf, GEN pr, GEN ep, GEN x, GEN arch)
+{
+  pari_sp av = avma;
+  long a, e = itos(ep), f = pr_get_f(pr);
+  GEN p = pr_get_p(pr), list, g, g0, y, u,v, prb, pre;
+  ulong mask;
+
+  if(DEBUGLEVEL>3) err_printf("treating pr^%ld, pr = %Ps\n",e,pr);
+  if (f == 1)
+    g = pgener_Fp(p);
+  else
+  {
+    GEN T, modpr = zk_to_Fq_init(nf, &pr, &T, &p);
+    g = Fq_to_nf(gener_FpXQ(T,p,NULL), modpr);
+    g = poltobasis(nf, g);
+  }
+  /* g generates  (Z_K / pr)^* */
+  prb = idealhnf_two(nf,pr);
+  pre = (e==1)? prb: idealpow(nf,pr,ep);
+  if (x)
+  {
+    GEN mv;
+    v = idealaddtoone_i(nf,idealdivpowprime(nf,x,pr,ep), pre);
+    mv = zk_scalar_or_multable(nf, v);
+    u = typ(mv) == t_INT? subui(1,mv): unnf_minus_x(v);
+    v = mv;
+    g0 = makeprimetoideal(x,u,mv,g);
+  }
+  else
+  {
+    u = v = NULL; /* gcc -Wall */
+    g0 = g;
+  }
+
+  y = mkvec5(mkvec(subiu(powiu(p,f), 1)),
+             mkvec(g),
+             mkvec(g0),
+             mkvec(nfsign_arch(nf,g0,arch)),
+             gen_1);
+  if (e == 1) return gerepilecopy(av, mkvec(y));
+  list = vectrunc_init(e+1);
+  vectrunc_append(list, y);
+  mask = quadratic_prec_mask(e);
+  a = 1;
+  while (mask > 1)
+  {
+    GEN pra = prb, gen, z, s, U;
+    long i, l, b = a << 1;
+
+    if (mask & 1) b--;
+    mask >>= 1;
+    /* compute 1 + pr^a / 1 + pr^b, 2a <= b */
+    if(DEBUGLEVEL>3) err_printf("  treating a = %ld, b = %ld\n",a,b);
+    prb = (b >= e)? pre: idealpows(nf,pr,b);
+    z = zidealij(pra, prb, &U);
+    gen = leafcopy(gel(z,2));
+    s = cgetg_copy(gen, &l);
+    for (i = 1; i < l; i++)
+    {
+      if (x) gel(gen,i) = makeprimetoideal(x,u,v,gel(gen,i));
+      gel(s,i) = nfsign_arch(nf,gel(gen,i),arch);
+    }
+    y = mkvec5(gel(z,1), gel(z,2), gen, s, U);
+    vectrunc_append(list, y);
+    a = b;
+  }
+  return gerepilecopy(av, list);
+}
+
+/* increment y, which runs through [-d,d]^k. Return 0 when done. */
+static int
+increment(GEN y, long k, long d)
+{
+  long i = 0, j;
+  do
+  {
+    if (++i > k) return 0;
+    y[i]++;
+  } while (y[i] > d);
+  for (j = 1; j < i; j++) y[j] = -d;
+  return 1;
+}
+
+GEN
+archstar_full_rk(GEN x, GEN bas, GEN v, GEN gen)
+{
+  long i, r, lgmat, N = lg(bas)-1, nba = nbrows(v);
+  GEN lambda = cgetg(N+1, t_VECSMALL), mat = cgetg(nba+1,t_MAT);
+
+  lgmat = lg(v); setlg(mat, lgmat+1);
+  for (i = 1; i < lgmat; i++) gel(mat,i) = gel(v,i);
+  for (     ; i <= nba; i++)  gel(mat,i) = cgetg(nba+1, t_VECSMALL);
+
+  if (x) { x = ZM_lll(x, 0.75, LLL_INPLACE); bas = RgV_RgM_mul(bas, x); }
+
+  for (r=1;; r++)
+  { /* reset */
+    (void)vec_setconst(lambda, (GEN)0);
+    if (!x) lambda[1] = r;
+    while (increment(lambda, N, r))
+    {
+      pari_sp av1 = avma;
+      GEN a = RgM_zc_mul(bas, lambda), c = gel(mat,lgmat);
+      for (i = 1; i <= nba; i++)
+      {
+        GEN t = x? gadd(gel(a,i), gen_1): gel(a,i);
+        c[i] = (gsigne(t) < 0)? 1: 0;
+      }
+      avma = av1; if (Flm_deplin(mat, 2)) continue;
+
+      /* c independent of previous sign vectors */
+      if (!x) a = zc_to_ZC(lambda);
+      else
+      {
+        a = ZM_zc_mul(x, lambda);
+        gel(a,1) = addis(gel(a,1), 1);
+      }
+      gel(gen,lgmat) = a;
+      if (lgmat++ == nba) {
+        mat = Flm_inv(mat,2); /* full rank */
+        settyp(mat, t_VEC); return mat;
+      }
+      setlg(mat,lgmat+1);
+    }
+  }
+}
+
+/* x non-0 integral ideal in HNF, compute elements in 1+x (in x, if x = zk)
+ * whose sign matrix is invertible. archp in 'indices' format */
+GEN
+nfarchstar(GEN nf, GEN x, GEN archp)
+{
+  long nba = lg(archp) - 1;
+  GEN cyc, gen, mat;
+
+  if (!nba)
+    cyc = gen = mat = cgetg(1, t_VEC);
+  else
+  {
+    GEN xZ = gcoeff(x,1,1), gZ;
+    pari_sp av = avma;
+    if (is_pm1(xZ)) x = NULL; /* x = O_K */
+    gZ = x? subsi(1, xZ): gen_m1; /* gZ << 0, gZ = 1 mod x */
+    if (nba == 1)
+    {
+      gen = mkvec(gZ);
+      mat = mkvec( mkvecsmall(1) );
+    }
+    else
+    {
+      GEN bas = nf_get_M(nf);
+      if (lgcols(bas) > lg(archp)) bas = rowpermute(bas, archp);
+      gen = cgetg(nba+1,t_VEC);
+      gel(gen,1) = gZ;
+      mat = archstar_full_rk(x, bas, mkmat(const_vecsmall(nba,1)), gen);
+      gerepileall(av,2,&gen,&mat);
+    }
+    cyc = const_vec(nba, gen_2);
+  }
+  return mkvec3(cyc,gen,mat);
+}
+
+static GEN
+apply_U(GEN U, GEN a)
+{
+  GEN e;
+  if (typ(a) == t_INT)
+    e = RgC_Rg_mul(gel(U,1), subis(a, 1));
+  else
+  { /* t_COL */
+    GEN t = gel(a,1);
+    gel(a,1) = addsi(-1, gel(a,1)); /* a -= 1 */
+    e = RgM_RgC_mul(U, a);
+    gel(a,1) = t; /* restore */
+  }
+  return e;
+}
+/* a in Z_K (t_COL or t_INT), pr prime ideal, prk = pr^k,
+ * list = zprimestar(nf, pr, k, ...)  */
+static GEN
+zlog_pk(GEN nf, GEN a, GEN y, GEN pr, GEN prk, GEN list, GEN *psigne)
+{
+  long i,j, llist = lg(list)-1;
+  for (j = 1; j <= llist; j++)
+  {
+    GEN L = gel(list,j), e;
+    GEN cyc = gel(L,1), gen = gel(L,2), s = gel(L,4), U = gel(L,5);
+    if (j == 1)
+      e = mkcol( nf_log(nf, a, gel(gen,1), gel(cyc,1), pr) );
+    else
+      e = apply_U(U, a);
+    /* here lg(e) == lg(cyc) */
+    for (i = 1; i < lg(cyc); i++)
+    {
+      GEN t;
+      if (typ(gel(e,i)) != t_INT) pari_err_COPRIME("zlog_pk", a, pr);
+      t = modii(negi(gel(e,i)), gel(cyc,i));
+      gel(++y,0) = negi(t); if (!signe(t)) continue;
+
+      if (mod2(t)) Flv_add_inplace(*psigne, gel(s,i), 2);
+      if (j != llist) a = elt_mulpow_modideal(nf, a, gel(gen,i), t, prk);
+    }
+  }
+  return y;
+}
+
+static void
+zlog_add_sign(GEN y0, GEN sgn, GEN lists)
+{
+  GEN y, s;
+  long i;
+  if (!sgn) return;
+  y = y0 + lg(y0);
+  s = Flm_Flc_mul(gmael(lists, lg(lists)-1, 3), sgn, 2);
+  for (i = lg(s)-1; i > 0; i--) gel(--y,0) = s[i]? gen_1: gen_0;
+}
+
+static GEN
+famat_zlog(GEN nf, GEN fa, GEN sgn, GEN bid)
+{
+  GEN g = gel(fa,1), e = gel(fa,2);
+  GEN vp = gmael(bid, 3,1), ep = gmael(bid, 3,2);
+  GEN arch = bid_get_arch(bid);
+  GEN cyc = bid_get_cyc(bid), lists = gel(bid,4), U = gel(bid,5);
+  GEN y0, x, y, EX = gel(cyc,1);
+  long i, l;
+
+  y0 = y = cgetg(lg(U), t_COL);
+  if (!sgn) sgn = nfsign_arch(nf, mkmat2(g,e), arch);
+  l = lg(vp);
+  for (i=1; i < l; i++)
+  {
+    GEN pr = gel(vp,i), prk, ex;
+    if (l == 2) {
+      prk = bid_get_ideal(bid);
+      ex = EX;
+    } else { /* try to improve EX: should be group exponent mod prf, not f */
+      GEN k = gel(ep,i);
+      prk = idealpow(nf, pr, k);
+      /* upper bound: gcd(EX, (Nv-1)p^(k-1)) = (Nv-1) p^min(k-1,v_p(EX)) */
+      ex = subis(pr_norm(pr),1);
+      if (!is_pm1(k)) {
+        GEN p = pr_get_p(pr), k_1 = subis(k,1);
+        long v = Z_pval(EX, p);
+        if (cmpui(v, k_1) > 0) v = itos(k_1);
+        if (v) ex = mulii(ex, powiu(p, v));
+      }
+    }
+    x = famat_makecoprime(nf, g, e, pr, prk, ex);
+    y = zlog_pk(nf, x, y, pr, prk, gel(lists,i), &sgn);
+  }
+  zlog_add_sign(y0, sgn, lists);
+  return y0;
+}
+
+static GEN
+get_index(GEN lists)
+{
+  long t = 0, j, k, l = lg(lists)-1;
+  GEN L, ind = cgetg(l+1, t_VECSMALL);
+
+  for (k = 1; k < l; k++)
+  {
+    L = gel(lists,k);
+    ind[k] = t;
+    for (j=1; j<lg(L); j++) t += lg(gmael(L,j,1)) - 1;
+  }
+  /* for arch. components */
+  ind[k] = t; return ind;
+}
+
+static void
+init_zlog(zlog_S *S, long n, GEN P, GEN e, GEN arch, GEN lists, GEN U)
+{
+  S->n = n;
+  S->U = U;
+  S->P = P;
+  S->e = e;
+  S->archp = vec01_to_indices(arch);
+  S->lists = lists;
+  S->ind = get_index(lists);
+}
+void
+init_zlog_bid(zlog_S *S, GEN bid)
+{
+  GEN fa = gel(bid,3), lists = gel(bid,4), U = gel(bid,5);
+  GEN arch = gel(bid_get_mod(bid), 2);
+  init_zlog(S, lg(U)-1, gel(fa,1), gel(fa,2), arch, lists, U);
+}
+
+/* Return decomposition of a on the S->n successive generators contained in
+ * S->lists. If index !=0, do the computation for the corresponding prime
+ * ideal and set to 0 the other components. */
+static GEN
+zlog_ind(GEN nf, GEN a, zlog_S *S, GEN sgn, long index)
+{
+  GEN y0 = zerocol(S->n), y;
+  pari_sp av = avma;
+  long k, kmin, kmax;
+
+  a = nf_to_scalar_or_basis(nf,a);
+  if (index)
+  {
+    kmin = kmax = index;
+    y = y0 + S->ind[index];
+  }
+  else
+  {
+    kmin = 1; kmax = lg(S->P)-1;
+    y = y0;
+  }
+  if (!sgn) sgn = nfsign_arch(nf, a, S->archp);
+  for (k = kmin; k <= kmax; k++)
+  {
+    GEN list= gel(S->lists,k);
+    GEN pr  = gel(S->P,k);
+    GEN prk = idealpow(nf, pr, gel(S->e,k));
+    y = zlog_pk(nf, a, y, pr, prk, list, &sgn);
+  }
+  zlog_add_sign(y0, sgn, S->lists);
+  return gerepilecopy(av, y0);
+}
+/* sgn = sign(a, S->arch) or NULL if unknown */
+GEN
+zlog(GEN nf, GEN a, GEN sgn, zlog_S *S) { return zlog_ind(nf, a, S, sgn, 0); }
+
+/* Log on bid.gen of generators of P_{1,I pr^{e-1}} / P_{1,I pr^e} (I,pr) = 1,
+ * defined implicitly via CRT. 'index' is the index of pr in modulus
+ * factorization */
+GEN
+log_gen_pr(zlog_S *S, long index, GEN nf, long e)
+{
+  long i, l, yind = S->ind[index];
+  GEN y, A, L, L2 = gel(S->lists,index);
+
+  if (e == 1)
+  {
+    L = gel(L2,1);
+    y = col_ei(S->n, yind+1);
+    zlog_add_sign(y, gmael(L,4,1), S->lists);
+    retmkmat( ZM_ZC_mul(S->U, y) );
+  }
+  else
+  {
+    GEN prk, g, pr = gel(S->P,index);
+    long narchp = lg(S->archp)-1;
+
+    if (e == 2)
+      L = gel(L2,2);
+    else
+      L = zidealij(idealpows(nf,pr,e-1), idealpows(nf,pr,e), NULL);
+    g = gel(L,2);
+    l = lg(g); A = cgetg(l, t_MAT);
+    prk = idealpow(nf, pr, gel(S->e,index));
+    for (i = 1; i < l; i++)
+    {
+      GEN G = gel(g,i), sgn = zero_zv(narchp); /*positive at f_oo*/
+      y = zerocol(S->n);
+      (void)zlog_pk(nf, G, y + yind, pr, prk, L2, &sgn);
+      zlog_add_sign(y, sgn, S->lists);
+      gel(A,i) = y;
+    }
+    return ZM_mul(S->U, A);
+  }
+}
+/* Log on bid.gen of generator of P_{1,f} / P_{1,f v[index]}
+ * v = vector of r1 real places */
+GEN
+log_gen_arch(zlog_S *S, long index)
+{
+  GEN y = zerocol(S->n);
+  zlog_add_sign(y, vecsmall_ei(lg(S->archp)-1, index), S->lists);
+  return ZM_ZC_mul(S->U, y);
+}
+
+/* add [h,cyc] or [h,cyc,gen] to bid */
+static void
+add_grp(GEN nf, GEN u1, GEN cyc, GEN gen, GEN bid)
+{
+  GEN h = ZV_prod(cyc);
+  if (u1)
+  {
+    GEN G = mkvec3(h,cyc,NULL/*dummy, bid[2] needed below*/);
+    gel(bid,2) = G;
+    if (u1 != gen_1)
+    {
+      long i, c = lg(u1);
+      GEN g = cgetg(c,t_VEC);
+      for (i=1; i<c; i++)
+        gel(g,i) = famat_to_nf_moddivisor(nf, gen, gel(u1,i), bid);
+      gen = g;
+    }
+    gel(G,3) = gen; /* replace dummy */
+  }
+  else
+    gel(bid,2) = mkvec2(h,cyc);
+}
+
+static int
+RgV_is_prV(GEN v)
+{
+  long l = lg(v), i;
+  for (i = 1; i < l; i++)
+    if (!get_prid(gel(v,i))) return 0;
+  return 1;
+}
+
+static int
+is_nf_factor(GEN F)
+{
+  return typ(F) == t_MAT && lg(F) == 3
+    && RgV_is_prV(gel(F,1)) && RgV_is_ZV(gel(F,2));
+}
+
+/* Compute [[ideal,arch], [h,[cyc],[gen]], idealfact, [liste], U]
+   flag may include nf_GEN | nf_INIT */
+GEN
+Idealstar(GEN nf, GEN ideal, long flag)
+{
+  pari_sp av = avma;
+  long i, j, k, nbp, R1, nbgen;
+  GEN t, y, cyc, U, u1 = NULL, fa, lists, x, arch, archp, E, P, sarch, gen;
+
+  nf = checknf(nf);
+  R1 = nf_get_r1(nf);
+  if (typ(ideal) == t_VEC && lg(ideal) == 3)
+  {
+    arch = gel(ideal,2);
+    ideal= gel(ideal,1);
+    switch(typ(arch))
+    {
+      case t_VEC:
+        if (lg(arch) != R1+1)
+          pari_err_TYPE("Idealstar [incorrect archimedean component]",arch);
+        archp = vec01_to_indices(arch);
+        break;
+      case t_VECSMALL:
+        archp = arch;
+        arch = vec01_to_indices(archp);
+        break;
+      default:
+        pari_err_TYPE("Idealstar [incorrect archimedean component]",arch);
+        return NULL;
+    }
+  }
+  else
+  {
+    arch = zerovec(R1);
+    archp = cgetg(1, t_VECSMALL);
+  }
+  if (is_nf_factor(ideal))
+  {
+    fa = ideal;
+    x = idealfactorback(nf, gel(fa,1), gel(fa,2), 0);
+  }
+  else
+  {
+    fa = NULL;
+    x = idealhnf_shallow(nf, ideal);
+  }
+  if (lg(x) == 1) pari_err_DOMAIN("Idealstar", "ideal","=",gen_0,x);
+  if (typ(gcoeff(x,1,1)) != t_INT)
+    pari_err_DOMAIN("Idealstar","denominator(ideal)", "!=",gen_1,x);
+  sarch = nfarchstar(nf, x, archp);
+  if (!fa) fa = idealfactor(nf, ideal);
+  P = gel(fa,1);
+  E = gel(fa,2); nbp = lg(P)-1;
+  if (nbp)
+  {
+    GEN h;
+    long cp = 0;
+    zlog_S S;
+
+    lists = cgetg(nbp+2,t_VEC);
+    /* rough upper bound */
+    nbgen = nbp + 1; for (i=1; i<=nbp; i++) nbgen += itos(gel(E,i));
+    gen = cgetg(nbgen+1,t_VEC);
+    nbgen = 1;
+    t = (nbp==1)? NULL: x;
+    for (i=1; i<=nbp; i++)
+    {
+      GEN L = zprimestar(nf, gel(P,i), gel(E,i), t, archp);
+      gel(lists,i) = L;
+      for (j = 1; j < lg(L); j++) gel(gen, nbgen++) = gmael(L,j,3);
+    }
+    gel(lists,i) = sarch;
+    gel(gen, nbgen++) = gel(sarch,2); setlg(gen, nbgen);
+    gen = shallowconcat1(gen); nbgen = lg(gen)-1;
+
+    h = cgetg(nbgen+1,t_MAT);
+    init_zlog(&S, nbgen, P, E, archp, lists, NULL);
+    for (i=1; i<=nbp; i++)
+    {
+      GEN L2 = gel(lists,i);
+      for (j=1; j < lg(L2); j++)
+      {
+        GEN L = gel(L2,j), F = gel(L,1), G = gel(L,3);
+        for (k=1; k<lg(G); k++)
+        { /* log(g^f) mod divisor */
+          GEN g = gel(G,k), f = gel(F,k), a = nfpowmodideal(nf,g,f,x);
+          GEN sgn = mpodd(f)? nfsign_arch(nf, g, S.archp)
+                            : zero_zv(lg(S.archp)-1);
+          gel(h,++cp) = ZC_neg(zlog_ind(nf, a, &S, sgn, i));
+          gcoeff(h,cp,cp) = f;
+        }
+      }
+    }
+    for (j=1; j<lg(archp); j++)
+    {
+      gel(h,++cp) = zerocol(nbgen);
+      gcoeff(h,cp,cp) = gen_2;
+    }
+    /* assert(cp == nbgen) */
+    h = ZM_hnfall(h,NULL,0);
+    cyc = ZM_snf_group(h, &U, (flag & nf_GEN)? &u1: NULL);
+  }
+  else
+  {
+    lists = mkvec(sarch);
+    gen = gel(sarch,2); nbgen = lg(gen)-1;
+    cyc = const_vec(nbgen, gen_2);
+    U = matid(nbgen);
+    if (flag & nf_GEN) u1 = gen_1;
+  }
+
+  y = cgetg(6,t_VEC);
+  gel(y,1) = mkvec2(x, arch);
+  gel(y,3) = fa;
+  gel(y,4) = lists;
+  gel(y,5) = U;
+  add_grp(nf, u1, cyc, gen, y);
+  if (!(flag & nf_INIT)) y = gel(y,2);
+  return gerepilecopy(av, y);
+}
+
+/* vectors of [[cyc],[g],U.X^-1] */
+static GEN
+principal_units(GEN nf, GEN pr, long e)
+{
+  pari_sp av = avma;
+  long a;
+  GEN list, y, prb, pre;
+  ulong mask;
+
+  if(DEBUGLEVEL>3) err_printf("treating pr^%ld, pr = %Ps\n",e,pr);
+  if (e == 1) return cgetg(1, t_VEC);
+  prb = idealhnf_two(nf,pr);
+  pre = idealpows(nf,pr,e);
+  list = vectrunc_init(e);
+  mask = quadratic_prec_mask(e);
+  a = 1;
+  while (mask > 1)
+  {
+    GEN pra = prb, z, U;
+    long b = a << 1;
+
+    if (mask & 1) b--;
+    mask >>= 1;
+    /* compute 1 + pr^a / 1 + pr^b, 2a <= b */
+    if(DEBUGLEVEL>3) err_printf("  treating a = %ld, b = %ld\n",a,b);
+    prb = (b >= e)? pre: idealpows(nf,pr,b);
+    z = zidealij(pra, prb, &U);
+    y = mkvec3(gel(z,1), gel(z,2), U);
+    vectrunc_append(list, y);
+    a = b;
+  }
+  return gerepilecopy(av, list);
+}
+
+static GEN
+log_prk(GEN nf, GEN a, long nbgen, GEN list, GEN prk)
+{
+  GEN y = zerocol(nbgen);
+  long i,j, iy = 1, llist = lg(list)-1;
+
+  for (j = 1; j <= llist; j++)
+  {
+    GEN L = gel(list,j);
+    GEN cyc = gel(L,1), gen = gel(L,2), U = gel(L,3);
+    GEN e = apply_U(U, a);
+    /* here lg(e) == lg(cyc) */
+    for (i = 1; i < lg(cyc); i++)
+    {
+      GEN t = modii(negi(gel(e,i)), gel(cyc,i));
+      gel(y, iy++) = negi(t); if (!signe(t)) continue;
+      if (j != llist) a = elt_mulpow_modideal(nf, a, gel(gen,i), t, prk);
+    }
+  }
+  return y;
+}
+
+/* multiplicative group (1 + pr) / (1 + pr^e) */
+GEN
+idealprincipalunits(GEN nf, GEN pr, long e)
+{
+  pari_sp av = avma;
+  long c, i, j, k, nbgen;
+  GEN cyc, u1 = NULL, pre, gen;
+  GEN g, EX, h, L2;
+  long cp = 0;
+
+  nf = checknf(nf); pre = idealpows(nf, pr, e);
+  L2 = principal_units(nf, pr, e);
+  c = lg(L2); gen = cgetg(c, t_VEC);
+  for (j = 1; j < c; j++) gel(gen, j) = gmael(L2,j,2);
+  gen = shallowconcat1(gen); nbgen = lg(gen)-1;
+
+  h = cgetg(nbgen+1,t_MAT);
+  for (j=1; j < lg(L2); j++)
+  {
+    GEN L = gel(L2,j), F = gel(L,1), G = gel(L,2);
+    for (k=1; k<lg(G); k++)
+    { /* log(g^f) mod pr^e */
+      GEN g = gel(G,k), f = gel(F,k), a = nfpowmodideal(nf,g,f,pre);
+      gel(h,++cp) = ZC_neg(log_prk(nf, a, nbgen, L2, pre));
+      gcoeff(h,cp,cp) = f;
+    }
+  }
+  /* assert(cp == nbgen) */
+  h = ZM_hnfall(h,NULL,0);
+  cyc = ZM_snf_group(h, NULL, &u1);
+  c = lg(u1); g = cgetg(c, t_VEC); EX = gel(cyc,1);
+  for (i=1; i<c; i++)
+    gel(g,i) = famat_to_nf_modideal_coprime(nf, gen, gel(u1,i), pre, EX);
+  return gerepilecopy(av, mkvec3(powiu(pr_norm(pr), e-1), cyc, g));
+}
+
+/* FIXME: obsolete */
+GEN
+zidealstarinitgen(GEN nf, GEN ideal)
+{ return Idealstar(nf,ideal, nf_INIT|nf_GEN); }
+GEN
+zidealstarinit(GEN nf, GEN ideal)
+{ return Idealstar(nf,ideal, nf_INIT); }
+GEN
+zidealstar(GEN nf, GEN ideal)
+{ return Idealstar(nf,ideal, nf_GEN); }
+
+GEN
+idealstar0(GEN nf, GEN ideal,long flag)
+{
+  switch(flag)
+  {
+    case 0: return Idealstar(nf,ideal, nf_GEN);
+    case 1: return Idealstar(nf,ideal, nf_INIT);
+    case 2: return Idealstar(nf,ideal, nf_INIT|nf_GEN);
+    default: pari_err_FLAG("idealstar");
+  }
+  return NULL; /* not reached */
+}
+
+void
+check_nfelt(GEN x, GEN *den)
+{
+  long l = lg(x), i;
+  GEN t, d = NULL;
+  if (typ(x) != t_COL) pari_err_TYPE("check_nfelt", x);
+  for (i=1; i<l; i++)
+  {
+    t = gel(x,i);
+    switch (typ(t))
+    {
+      case t_INT: break;
+      case t_FRAC:
+        if (!d) d = gel(t,2); else d = lcmii(d, gel(t,2));
+        break;
+      default: pari_err_TYPE("check_nfelt", x);
+    }
+  }
+  *den = d;
+}
+
+GEN
+vecmodii(GEN a, GEN b)
+{
+  long i, l;
+  GEN c = cgetg_copy(a, &l);
+  for (i = 1; i < l; i++) gel(c,i) = modii(gel(a,i), gel(b,i));
+  return c;
+}
+
+/* Given x (not necessarily integral), and bid as output by zidealstarinit,
+ * compute the vector of components on the generators bid[2].
+ * Assume (x,bid) = 1 and sgn is either NULL or nfsign_arch(x, bid) */
+GEN
+ideallog_sgn(GEN nf, GEN x, GEN sgn, GEN bid)
+{
+  pari_sp av;
+  long lcyc;
+  GEN den, cyc, y;
+
+  nf = checknf(nf); checkbid(bid);
+  cyc = bid_get_cyc(bid);
+  lcyc = lg(cyc); if (lcyc == 1) return cgetg(1, t_COL);
+  av = avma;
+  if (typ(x) == t_MAT) {
+    if (lg(x) == 1) return zerocol(lcyc-1); /* x = 1 */
+    y = famat_zlog(nf, x, sgn, bid);
+    goto END;
+  }
+  x = nf_to_scalar_or_basis(nf, x);
+  switch(typ(x))
+  {
+    case t_INT:
+      den = NULL;
+      break;
+    case t_FRAC:
+      den = gel(x,2);
+      x = gel(x,1);
+      break;
+    default: /* case t_COL: */
+      check_nfelt(x, &den);
+      if (den) x = Q_muli_to_int(x, den);
+  }
+  if (den)
+  {
+    x = mkmat2(mkcol2(x, den), mkcol2(gen_1, gen_m1));
+    y = famat_zlog(nf, x, sgn, bid);
+  }
+  else
+  {
+    zlog_S S; init_zlog_bid(&S, bid);
+    y = zlog(nf, x, sgn, &S);
+  }
+END:
+  y = ZM_ZC_mul(gel(bid,5), y);
+  return gerepileupto(av, vecmodii(y, cyc));
+}
+GEN
+ideallog(GEN nf, GEN x, GEN bid) { return ideallog_sgn(nf, x, NULL, bid); }
+
+/*************************************************************************/
+/**                                                                     **/
+/**               JOIN BID STRUCTURES, IDEAL LISTS                      **/
+/**                                                                     **/
+/*************************************************************************/
+
+/* bid1, bid2: for coprime modules m1 and m2 (without arch. part).
+ * Output: bid [[m1 m2,arch],[h,[cyc],[gen]],idealfact,[liste],U] for m1 m2 */
+static GEN
+join_bid(GEN nf, GEN bid1, GEN bid2)
+{
+  pari_sp av = avma;
+  long i, nbgen, lx, lx1,lx2, l1,l2;
+  GEN I1,I2, G1,G2, fa1,fa2, lists1,lists2, cyc1,cyc2;
+  GEN lists, fa, U, cyc, y, u1 = NULL, x, gen;
+
+  I1 = bid_get_ideal(bid1);
+  I2 = bid_get_ideal(bid2);
+  if (gequal1(gcoeff(I1,1,1))) return bid2; /* frequent trivial case */
+  G1 = bid_get_grp(bid1);
+  G2 = bid_get_grp(bid2);
+  fa1= gel(bid1,3);
+  fa2= gel(bid2,3); x = idealmul(nf, I1,I2);
+  fa = famat_mul_shallow(fa1, fa2);
+  lists1 = gel(bid1,4); lx1 = lg(lists1);
+  lists2 = gel(bid2,4); lx2 = lg(lists2);
+  /* concat (lists1 - last elt [nfarchstar]) + lists2 */
+  lx = lx1+lx2-2; lists = cgetg(lx,t_VEC);
+  for (i=1; i<lx1-1; i++) gel(lists,i) = gel(lists1,i);
+  for (   ; i<lx; i++)    gel(lists,i) = gel(lists2,i-lx1+2);
+
+  cyc1 = abgrp_get_cyc(G1); l1 = lg(cyc1);
+  cyc2 = abgrp_get_cyc(G2); l2 = lg(cyc2);
+  gen = (lg(G1)>3 && lg(G2)>3)? gen_1: NULL;
+  nbgen = l1+l2-2;
+  cyc = ZM_snf_group(diagonal_shallow(shallowconcat(cyc1,cyc2)),
+                     &U, gen? &u1: NULL);
+  if (nbgen) {
+    GEN U1 = gel(bid1,5), U2 = gel(bid2,5);
+    U1 = l1 == 1? zeromat(nbgen,lg(U1)-1): ZM_mul(vecslice(U, 1, l1-1),   U1);
+    U2 = l2 == 1? zeromat(nbgen,lg(U2)-1): ZM_mul(vecslice(U, l1, nbgen), U2);
+    U = shallowconcat(U1, U2);
+  }
+  else
+    U = zeromat(0, lx-2);
+
+  if (gen)
+  {
+    GEN mu,mv, u,v = idealaddtoone_i(nf,I2,I1);
+    mv = zk_scalar_or_multable(nf, v);
+    if (typ(mv) == t_INT)
+    {
+      mu = u = subui(1,mv);
+      v = mv;
+    }
+    else
+    {
+      u = unnf_minus_x(v);
+      mu = RgM_Rg_add(ZM_neg(mv), gen_1); /* mult by u = 1-v */
+    }
+    gen = shallowconcat(makeprimetoidealvec(x,u,mv, abgrp_get_gen(G1)),
+                        makeprimetoidealvec(x,v,mu, abgrp_get_gen(G2)));
+  }
+  y = cgetg(6,t_VEC);
+  gel(y,1) = mkvec2(x, bid_get_arch(bid1));
+  gel(y,3) = fa;
+  gel(y,4) = lists;
+  gel(y,5) = U;
+  add_grp(nf, u1, cyc, gen, y);
+  return gerepilecopy(av,y);
+}
+
+typedef struct _ideal_data {
+  GEN nf, emb, L, pr, prL, arch, sgnU;
+} ideal_data;
+
+/* z <- ( z | f(v[i])_{i=1..#v} ) */
+static void
+concat_join(GEN *pz, GEN v, GEN (*f)(ideal_data*,GEN), ideal_data *data)
+{
+  long i, nz, lv = lg(v);
+  GEN z, Z;
+  if (lv == 1) return;
+  z = *pz; nz = lg(z)-1;
+  *pz = Z = cgetg(lv + nz, typ(z));
+  for (i = 1; i <=nz; i++) gel(Z,i) = gel(z,i);
+  Z += nz;
+  for (i = 1; i < lv; i++) gel(Z,i) = f(data, gel(v,i));
+}
+static GEN
+join_idealinit(ideal_data *D, GEN x) {
+  return join_bid(D->nf, x, D->prL);
+}
+static GEN
+join_ideal(ideal_data *D, GEN x) {
+  return idealmulpowprime(D->nf, x, D->pr, D->L);
+}
+static GEN
+join_unit(ideal_data *D, GEN x) {
+  return mkvec2(join_idealinit(D, gel(x,1)), vconcat(gel(x,2), D->emb));
+}
+
+/* compute matrix of zlogs of units */
+GEN
+zlog_units(GEN nf, GEN U, GEN sgnU, GEN bid)
+{
+  long j, l = lg(U);
+  GEN m = cgetg(l, t_MAT);
+  zlog_S S; init_zlog_bid(&S, bid);
+  for (j = 1; j < l; j++)
+    gel(m,j) = zlog(nf, gel(U,j), vecpermute(gel(sgnU,j), S.archp), &S);
+  return m;
+}
+/* compute matrix of zlogs of units, assuming S.archp = [] */
+GEN
+zlog_units_noarch(GEN nf, GEN U, GEN bid)
+{
+  long j, l = lg(U);
+  GEN m = cgetg(l, t_MAT), empty = cgetg(1, t_COL);
+  zlog_S S; init_zlog_bid(&S, bid);
+  for (j = 1; j < l; j++) gel(m,j) = zlog(nf, gel(U,j), empty, &S);
+  return m;
+}
+
+/* calcule la matrice des zlog des unites */
+static GEN
+zlog_unitsarch(GEN sgnU, GEN bid)
+{
+  GEN lists = gel(bid,4), arch = gmael(bid,1,2);
+  GEN listslast = gel(lists,lg(lists)-1);
+  GEN m = rowpermute(sgnU, vec01_to_indices(arch));
+  return Flm_mul(gel(listslast,3), m, 2);
+}
+
+/*  flag & nf_GEN : generators, otherwise no
+ *  flag &2 : units, otherwise no
+ *  flag &4 : ideals in HNF, otherwise bid */
+static GEN
+Ideallist(GEN bnf, ulong bound, long flag)
+{
+  const long do_units = flag & 2, big_id = !(flag & 4);
+  const long istar_flag = (flag & nf_GEN) | nf_INIT;
+  pari_sp lim, av, av0 = avma;
+  long i, j, l;
+  GEN nf, z, p, fa, id, U, empty = cgetg(1,t_VEC);
+  forprime_t S;
+  ideal_data ID;
+  GEN (*join_z)(ideal_data*, GEN) =
+    do_units? &join_unit
+              : (big_id? &join_idealinit: &join_ideal);
+
+  nf = checknf(bnf);
+  if ((long)bound <= 0) return empty;
+  id = matid(nf_get_degree(nf));
+  if (big_id) id = Idealstar(nf,id, istar_flag);
+
+  /* z[i] will contain all "objects" of norm i. Depending on flag, this means
+   * an ideal, a bid, or a couple [bid, log(units)]. Such objects are stored
+   * in vectors, computed one primary component at a time; join_z
+   * reconstructs the global object */
+  z = cgetg(bound+1,t_VEC);
+  if (do_units) {
+    U = init_units(bnf);
+    gel(z,1) = mkvec( mkvec2(id, zlog_units_noarch(nf, U, id)) );
+  } else {
+    U = NULL; /* -Wall */
+    gel(z,1) = mkvec(id);
+  }
+  for (i=2; i<=(long)bound; i++) gel(z,i) = empty;
+  ID.nf = nf;
+
+  p = cgetipos(3);
+  u_forprime_init(&S, 2, bound);
+  av = avma; lim = stack_lim(av,1);
+  while ((p[2] = u_forprime_next(&S)))
+  {
+    if (DEBUGLEVEL>1) { err_printf("%ld ",p[2]); err_flush(); }
+    fa = idealprimedec(nf, p);
+    for (j=1; j<lg(fa); j++)
+    {
+      GEN pr = gel(fa,j), z2;
+      long f = pr_get_f(pr);
+      ulong q, Q = upowuu(p[2], f);
+      if (!Q || Q > bound) break;
+
+      z2 = leafcopy(z);
+      q = Q;
+      ID.pr = ID.prL = pr;
+      for (l=1; Q <= bound; l++, Q *= q) /* add pr^l */
+      {
+        ulong iQ;
+        ID.L = utoipos(l);
+        if (big_id) {
+          if (l > 1) ID.prL = idealpow(nf,pr,ID.L);
+          ID.prL = Idealstar(nf,ID.prL, istar_flag);
+          if (do_units) ID.emb = zlog_units_noarch(nf, U, ID.prL);
+        }
+        for (iQ = Q,i = 1; iQ <= bound; iQ += Q,i++)
+          concat_join(&gel(z,iQ), gel(z2,i), join_z, &ID);
+      }
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"Ideallist");
+      z = gerepilecopy(av, z);
+    }
+  }
+  if (do_units) for (i = 1; i < lg(z); i++)
+  {
+    GEN s = gel(z,i);
+    long l = lg(s);
+    for (j = 1; j < l; j++) {
+      GEN v = gel(s,j), bid = gel(v,1);
+      gel(v,2) = ZM_mul(gel(bid,5), gel(v,2));
+    }
+  }
+  return gerepilecopy(av0, z);
+}
+GEN
+ideallist0(GEN bnf,long bound, long flag) {
+  if (flag<0 || flag>4) pari_err_FLAG("ideallist");
+  return Ideallist(bnf,bound,flag);
+}
+GEN
+ideallist(GEN bnf,long bound) { return Ideallist(bnf,bound,4); }
+
+/* bid1 = for module m1 (without arch. part), arch = archimedean part.
+ * Output: bid [[m1,arch],[h,[cyc],[gen]],idealfact,[liste],U] for m1.arch */
+static GEN
+join_bid_arch(GEN nf, GEN bid1, GEN arch)
+{
+  pari_sp av = avma;
+  GEN f1, G1, fa1, U;
+  GEN lists, cyc, y, u1 = NULL, x, sarch, gen;
+
+  checkbid(bid1);
+  f1 = gel(bid1,1); G1 = gel(bid1,2); fa1 = gel(bid1,3);
+  x = gel(f1,1);
+  sarch = nfarchstar(nf, x, arch);
+  lists = leafcopy(gel(bid1,4));
+  gel(lists,lg(lists)-1) = sarch;
+
+  gen = (lg(G1)>3)? gen_1: NULL;
+  cyc = diagonal_shallow(shallowconcat(gel(G1,2), gel(sarch,1)));
+  cyc = ZM_snf_group(cyc, &U, gen? &u1: NULL);
+  if (gen) gen = shallowconcat(gel(G1,3), gel(sarch,2));
+  y = cgetg(6,t_VEC);
+  gel(y,1) = mkvec2(x, arch);
+  gel(y,3) = fa1;
+  gel(y,4) = lists;
+  gel(y,5) = U;
+  add_grp(nf, u1, cyc, gen, y);
+  return gerepilecopy(av,y);
+}
+static GEN
+join_arch(ideal_data *D, GEN x) {
+  return join_bid_arch(D->nf, x, D->arch);
+}
+static GEN
+join_archunit(ideal_data *D, GEN x) {
+  GEN bid = join_arch(D, gel(x,1)), U = gel(x,2);
+  U = ZM_mul(gel(bid,5), vconcat(U, zm_to_ZM(zlog_unitsarch(D->sgnU, bid))));
+  return mkvec2(bid, U);
+}
+
+/* L from ideallist, add archimedean part */
+GEN
+ideallistarch(GEN bnf, GEN L, GEN arch)
+{
+  pari_sp av;
+  long i, j, l = lg(L), lz;
+  GEN v, z, V;
+  ideal_data ID;
+  GEN (*join_z)(ideal_data*, GEN);
+
+  if (typ(L) != t_VEC) pari_err_TYPE("ideallistarch",L);
+  if (l == 1) return cgetg(1,t_VEC);
+  z = gel(L,1);
+  if (typ(z) != t_VEC) pari_err_TYPE("ideallistarch",z);
+  z = gel(z,1); /* either a bid or [bid,U] */
+  if (lg(z) == 3) { /* the latter: do units */
+    if (typ(z) != t_VEC) pari_err_TYPE("ideallistarch",z);
+    ID.sgnU = nfsign_units(bnf, NULL, 1);
+    join_z = &join_archunit;
+  } else
+    join_z = &join_arch;
+  ID.nf = checknf(bnf);
+  ID.arch = vec01_to_indices(arch);
+  av = avma; V = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    z = gel(L,i); lz = lg(z);
+    gel(V,i) = v = cgetg(lz,t_VEC);
+    for (j=1; j<lz; j++) gel(v,j) = join_z(&ID, gel(z,j));
+  }
+  return gerepilecopy(av,V);
+}
diff --git a/src/basemath/base4.c b/src/basemath/base4.c
new file mode 100644
index 0000000..e2aea86
--- /dev/null
+++ b/src/basemath/base4.c
@@ -0,0 +1,3110 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                       BASIC NF OPERATIONS                       */
+/*                           (continued)                           */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*                     IDEAL OPERATIONS                            */
+/*                                                                 */
+/*******************************************************************/
+
+/* A valid ideal is either principal (valid nf_element), or prime, or a matrix
+ * on the integer basis in HNF.
+ * A prime ideal is of the form [p,a,e,f,b], where the ideal is p.Z_K+a.Z_K,
+ * p is a rational prime, a belongs to Z_K, e=e(P/p), f=f(P/p), and b
+ * is Lenstra's constant, such that p.P^(-1)= p Z_K + b Z_K.
+ *
+ * An extended ideal is a couple [I,F] where I is a valid ideal and F is
+ * either an algebraic number, or a factorization matrix associated to an
+ * algebraic number. All routines work with either extended ideals or ideals
+ * (an omitted F is assumed to be [;] <-> 1).
+ * All ideals are output in HNF form. */
+
+/* types and conversions */
+
+long
+idealtyp(GEN *ideal, GEN *arch)
+{
+  GEN x = *ideal;
+  long t,lx,tx = typ(x);
+
+  if (tx==t_VEC && lg(x)==3)
+  { *arch = gel(x,2); x = gel(x,1); tx = typ(x); }
+  else
+    *arch = NULL;
+  switch(tx)
+  {
+    case t_MAT: lx = lg(x);
+      if (lx == 1) { t = id_PRINCIPAL; x = gen_0; break; }
+      if (lx != lgcols(x)) pari_err_TYPE("idealtyp [non-square t_MAT]",x);
+      t = id_MAT;
+      break;
+
+    case t_VEC: if (lg(x)!=6) pari_err_TYPE("idealtyp",x);
+      t = id_PRIME; break;
+
+    case t_POL: case t_POLMOD: case t_COL:
+    case t_INT: case t_FRAC:
+      t = id_PRINCIPAL; break;
+    default:
+      pari_err_TYPE("idealtyp",x);
+      return 0; /*not reached*/
+  }
+  *ideal = x; return t;
+}
+
+/* nf a true nf; v = [a,x,...], a in Z. Return (a,x) */
+GEN
+idealhnf_two(GEN nf, GEN v)
+{
+  GEN p = gel(v,1), pi = gel(v,2), m = zk_scalar_or_multable(nf, pi);
+  if (typ(m) == t_INT) return scalarmat(gcdii(m,p), nf_get_degree(nf));
+  return ZM_hnfmodid(m, p);
+}
+
+static GEN
+ZM_Q_mul(GEN x, GEN y)
+{ return typ(y) == t_INT? ZM_Z_mul(x,y): RgM_Rg_mul(x,y); }
+
+
+GEN
+idealhnf_principal(GEN nf, GEN x)
+{
+  GEN cx;
+  x = nf_to_scalar_or_basis(nf, x);
+  switch(typ(x))
+  {
+    case t_COL: break;
+    case t_INT:  if (!signe(x)) return cgetg(1,t_MAT);
+      return scalarmat(absi(x), nf_get_degree(nf));
+    case t_FRAC:
+      return scalarmat(Q_abs(x), nf_get_degree(nf));
+    default: pari_err_TYPE("idealhnf",x);
+  }
+  x = Q_primitive_part(x, &cx);
+  RgV_check_ZV(x, "idealhnf");
+  x = zk_multable(nf, x);
+  x = ZM_hnfmod(x, ZM_detmult(x));
+  return cx? ZM_Q_mul(x,cx): x;
+}
+
+/* x integral ideal in t_MAT form, nx columns */
+static GEN
+vec_mulid(GEN nf, GEN x, long nx, long N)
+{
+  GEN m = cgetg(nx*N + 1, t_MAT);
+  long i, j, k;
+  for (i=k=1; i<=nx; i++)
+    for (j=1; j<=N; j++) gel(m, k++) = zk_ei_mul(nf, gel(x,i),j);
+  return m;
+}
+GEN
+idealhnf_shallow(GEN nf, GEN x)
+{
+  long tx = typ(x), lx = lg(x), N;
+
+  /* cannot use idealtyp because here we allow non-square matrices */
+  if (tx == t_VEC && lx == 3) { x = gel(x,1); tx = typ(x); lx = lg(x); }
+  if (tx == t_VEC && lx == 6) return idealhnf_two(nf,x); /* PRIME */
+  switch(tx)
+  {
+    case t_MAT:
+    {
+      GEN cx;
+      long nx = lx-1;
+      N = nf_get_degree(nf);
+      if (nx == 0) return cgetg(1, t_MAT);
+      if (nbrows(x) != N) pari_err_TYPE("idealhnf [wrong dimension]",x);
+      if (nx == 1) return idealhnf_principal(nf, gel(x,1));
+
+      if (nx == N && RgM_is_ZM(x) && ZM_ishnf(x)) return x;
+      x = Q_primitive_part(x, &cx);
+      if (nx < N) x = vec_mulid(nf, x, nx, N);
+      x = ZM_hnfmod(x, ZM_detmult(x));
+      return cx? ZM_Q_mul(x,cx): x;
+    }
+    case t_QFI:
+    case t_QFR:
+    {
+      pari_sp av = avma;
+      GEN u, D = nf_get_disc(nf), T = nf_get_pol(nf), f = nf_get_index(nf);
+      GEN A = gel(x,1), B = gel(x,2);
+      N = nf_get_degree(nf);
+      if (N != 2)
+        pari_err_TYPE("idealhnf [Qfb for non-quadratic fields]", x);
+      if (!equalii(qfb_disc(x), D))
+        pari_err_DOMAIN("idealhnf [Qfb]", "disc(q)", "!=", D, x);
+      /* x -> A Z + (-B + sqrt(D)) / 2 Z
+         K = Q[t]/T(t), t^2 + ut + v = 0,  u^2 - 4v = Df^2
+         => t = (-u + sqrt(D) f)/2
+         => sqrt(D)/2 = (t + u/2)/f */
+      u = gel(T,3);
+      B = deg1pol_shallow(ginv(f),
+                          gsub(gdiv(u, shifti(f,1)), gdiv(B,gen_2)),
+                          varn(T));
+      return gerepileupto(av, idealhnf_two(nf, mkvec2(A,B)));
+    }
+    default: return idealhnf_principal(nf, x); /* PRINCIPAL */
+  }
+}
+GEN
+idealhnf(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  GEN y = idealhnf_shallow(checknf(nf), x);
+  return (avma == av)? gcopy(y): gerepileupto(av, y);
+}
+
+/* GP functions */
+
+GEN
+idealtwoelt0(GEN nf, GEN x, GEN a)
+{
+  if (!a) return idealtwoelt(nf,x);
+  return idealtwoelt2(nf,x,a);
+}
+
+GEN
+idealpow0(GEN nf, GEN x, GEN n, long flag)
+{
+  if (flag) return idealpowred(nf,x,n);
+  return idealpow(nf,x,n);
+}
+
+GEN
+idealmul0(GEN nf, GEN x, GEN y, long flag)
+{
+  if (flag) return idealmulred(nf,x,y);
+  return idealmul(nf,x,y);
+}
+
+GEN
+idealdiv0(GEN nf, GEN x, GEN y, long flag)
+{
+  switch(flag)
+  {
+    case 0: return idealdiv(nf,x,y);
+    case 1: return idealdivexact(nf,x,y);
+    default: pari_err_FLAG("idealdiv");
+  }
+  return NULL; /* not reached */
+}
+
+GEN
+idealaddtoone0(GEN nf, GEN arg1, GEN arg2)
+{
+  if (!arg2) return idealaddmultoone(nf,arg1);
+  return idealaddtoone(nf,arg1,arg2);
+}
+
+/* b not a scalar */
+static GEN
+hnf_Z_ZC(GEN nf, GEN a, GEN b) { return hnfmodid(zk_multable(nf,b), a); }
+/* b not a scalar */
+static GEN
+hnf_Z_QC(GEN nf, GEN a, GEN b)
+{
+  GEN db;
+  b = Q_remove_denom(b, &db);
+  if (db) a = mulii(a, db);
+  b = hnf_Z_ZC(nf,a,b);
+  return db? RgM_Rg_div(b, db): b;
+}
+/* b not a scalar (not point in trying to optimize for this case) */
+static GEN
+hnf_Q_QC(GEN nf, GEN a, GEN b)
+{
+  GEN da, db;
+  if (typ(a) == t_INT) return hnf_Z_QC(nf, a, b);
+  da = gel(a,2);
+  a = gel(a,1);
+  b = Q_remove_denom(b, &db);
+  /* write da = d*A, db = d*B, gcd(A,B) = 1
+   * gcd(a/(d A), b/(d B)) = gcd(a B, A b) / A B d = gcd(a B, b) / A B d */
+  if (db)
+  {
+    GEN d = gcdii(da,db);
+    if (!is_pm1(d)) db = diviiexact(db,d); /* B */
+    if (!is_pm1(db))
+    {
+      a = mulii(a, db); /* a B */
+      da = mulii(da, db); /* A B d = lcm(denom(a),denom(b)) */
+    }
+  }
+  return RgM_Rg_div(hnf_Z_ZC(nf,a,b), da);
+}
+static GEN
+hnf_QC_QC(GEN nf, GEN a, GEN b)
+{
+  GEN da, db, d, x;
+  a = Q_remove_denom(a, &da);
+  b = Q_remove_denom(b, &db);
+  if (da) b = ZC_Z_mul(b, da);
+  if (db) a = ZC_Z_mul(a, db);
+  d = mul_denom(da, db);
+  x = shallowconcat(zk_multable(nf,a), zk_multable(nf,b));
+  x = ZM_hnfmod(x, ZM_detmult(x));
+  return d? RgM_Rg_div(x, d): x;
+}
+static GEN
+hnf_Q_Q(GEN nf, GEN a, GEN b) {return scalarmat(Q_gcd(a,b), nf_get_degree(nf));}
+GEN
+idealhnf0(GEN nf, GEN a, GEN b)
+{
+  long ta, tb;
+  pari_sp av;
+  GEN x;
+  if (!b) return idealhnf(nf,a);
+
+  /* HNF of aZ_K+bZ_K */
+  av = avma; nf = checknf(nf);
+  a = nf_to_scalar_or_basis(nf,a); ta = typ(a);
+  b = nf_to_scalar_or_basis(nf,b); tb = typ(b);
+  if (ta == t_COL)
+    x = (tb==t_COL)? hnf_QC_QC(nf, a,b): hnf_Q_QC(nf, b,a);
+  else
+    x = (tb==t_COL)? hnf_Q_QC(nf, a,b): hnf_Q_Q(nf, a,b);
+  return gerepileupto(av, x);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       TWO-ELEMENT FORM                          */
+/*                                                                 */
+/*******************************************************************/
+static GEN idealapprfact_i(GEN nf, GEN x, int nored);
+
+static int
+ok_elt(GEN x, GEN xZ, GEN y)
+{
+  pari_sp av = avma;
+  int r = ZM_equal(x, ZM_hnfmodid(y, xZ));
+  avma = av; return r;
+}
+
+static GEN
+addmul_col(GEN a, long s, GEN b)
+{
+  long i,l;
+  if (!s) return a? leafcopy(a): a;
+  if (!a) return gmulsg(s,b);
+  l = lg(a);
+  for (i=1; i<l; i++)
+    if (signe(gel(b,i))) gel(a,i) = addii(gel(a,i), mulsi(s, gel(b,i)));
+  return a;
+}
+
+/* a <-- a + s * b, all coeffs integers */
+static GEN
+addmul_mat(GEN a, long s, GEN b)
+{
+  long j,l;
+  /* copy otherwise next call corrupts a */
+  if (!s) return a? RgM_shallowcopy(a): a;
+  if (!a) return gmulsg(s,b);
+  l = lg(a);
+  for (j=1; j<l; j++)
+    (void)addmul_col(gel(a,j), s, gel(b,j));
+  return a;
+}
+
+static GEN
+get_random_a(GEN nf, GEN x, GEN xZ)
+{
+  pari_sp av1;
+  long i, lm, l = lg(x);
+  GEN a, z, beta, mul;
+
+  beta= cgetg(l, t_VEC);
+  mul = cgetg(l, t_VEC); lm = 1; /* = lg(mul) */
+  /* look for a in x such that a O/xZ = x O/xZ */
+  for (i = 2; i < l; i++)
+  {
+    GEN t, y, xi = gel(x,i);
+    av1 = avma;
+    y = zk_scalar_or_multable(nf, xi); /* ZM, cannot be a scalar */
+    t = FpM_red(y, xZ);
+    if (gequal0(t)) { avma = av1; continue; }
+    if (ok_elt(x,xZ, t)) return xi;
+    gel(beta,lm) = xi;
+    /* mul[i] = { canonical generators for x[i] O/xZ as Z-module } */
+    gel(mul,lm) = t; lm++;
+  }
+  setlg(mul, lm);
+  setlg(beta,lm);
+  z = cgetg(lm, t_VECSMALL);
+  for(av1=avma;;avma=av1)
+  {
+    for (a=NULL,i=1; i<lm; i++)
+    {
+      long t = random_bits(4) - 7; /* in [-7,8] */
+      z[i] = t;
+      a = addmul_mat(a, t, gel(mul,i));
+    }
+    /* a = matrix (NOT HNF) of ideal generated by beta.z in O/xZ */
+    if (a && ok_elt(x,xZ, a)) break;
+  }
+  for (a=NULL,i=1; i<lm; i++)
+    a = addmul_col(a, z[i], gel(beta,i));
+  return a;
+}
+
+/* if x square matrix, assume it is HNF */
+static GEN
+mat_ideal_two_elt(GEN nf, GEN x)
+{
+  GEN y, a, cx, xZ;
+  long N = nf_get_degree(nf);
+  pari_sp av, tetpil;
+
+  if (N == 2) return mkvec2copy(gcoeff(x,1,1), gel(x,2));
+
+  y = cgetg(3,t_VEC); av = avma;
+  cx = Q_content(x);
+  xZ = gcoeff(x,1,1);
+  if (gequal(xZ, cx)) /* x = (cx) */
+  {
+    gel(y,1) = cx;
+    gel(y,2) = scalarcol_shallow(gen_0, N); return y;
+  }
+  if (equali1(cx)) cx = NULL;
+  else
+  {
+    x = Q_div_to_int(x, cx);
+    xZ = gcoeff(x,1,1);
+  }
+  if (N < 6)
+    a = get_random_a(nf, x, xZ);
+  else
+  {
+    const long FB[] = { _evallg(15+1) | evaltyp(t_VECSMALL),
+      2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47
+    };
+    GEN P, E, a1 = Z_smoothen(xZ, (GEN)FB, &P, &E);
+    if (!a1) /* factors completely */
+      a = idealapprfact_i(nf, idealfactor(nf,x), 1);
+    else if (lg(P) == 1) /* no small factors */
+      a = get_random_a(nf, x, xZ);
+    else /* general case */
+    {
+      GEN A0, A1, a0, u0, u1, v0, v1, pi0, pi1, t, u;
+      a0 = diviiexact(xZ, a1);
+      A0 = ZM_hnfmodid(x, a0); /* smooth part of x */
+      A1 = ZM_hnfmodid(x, a1); /* cofactor */
+      pi0 = idealapprfact_i(nf, idealfactor(nf,A0), 1);
+      pi1 = get_random_a(nf, A1, a1);
+      (void)bezout(a0, a1, &v0,&v1);
+      u0 = mulii(a0, v0);
+      u1 = mulii(a1, v1);
+      t = ZC_Z_mul(pi0, u1); gel(t,1) = addii(gel(t,1), u0);
+      u = ZC_Z_mul(pi1, u0); gel(u,1) = addii(gel(u,1), u1);
+      a = nfmuli(nf, centermod(u, xZ), centermod(t, xZ));
+    }
+  }
+  if (cx)
+  {
+    a = centermod(a, xZ);
+    tetpil = avma;
+    if (typ(cx) == t_INT)
+    {
+      gel(y,1) = mulii(xZ, cx);
+      gel(y,2) = ZC_Z_mul(a, cx);
+    }
+    else
+    {
+      gel(y,1) = gmul(xZ, cx);
+      gel(y,2) = RgC_Rg_mul(a, cx);
+    }
+  }
+  else
+  {
+    tetpil = avma;
+    gel(y,1) = icopy(xZ);
+    gel(y,2) = centermod(a, xZ);
+  }
+  gerepilecoeffssp(av,tetpil,y+1,2); return y;
+}
+
+/* Given an ideal x, returns [a,alpha] such that a is in Q,
+ * x = a Z_K + alpha Z_K, alpha in K^*
+ * a = 0 or alpha = 0 are possible, but do not try to determine whether
+ * x is principal. */
+GEN
+idealtwoelt(GEN nf, GEN x)
+{
+  pari_sp av;
+  GEN z;
+  long tx = idealtyp(&x,&z);
+  nf = checknf(nf);
+  if (tx == id_MAT) return mat_ideal_two_elt(nf,x);
+  if (tx == id_PRIME) return mkvec2copy(gel(x,1), gel(x,2));
+  /* id_PRINCIPAL */
+  av = avma; x = nf_to_scalar_or_basis(nf, x);
+  return gerepilecopy(av, typ(x)==t_COL? mkvec2(gen_0,x): mkvec2(Q_abs(x),gen_0));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         FACTORIZATION                           */
+/*                                                                 */
+/*******************************************************************/
+/* x integral ideal in HNF, return v_p(Nx), *vz = v_p(x \cap Z)
+ * Use x[1,1] = x \cap Z */
+long
+val_norm(GEN x, GEN p, long *vz)
+{
+  long i,l = lg(x), v;
+  *vz = v = Z_pval(gcoeff(x,1,1), p);
+  if (!v) return 0;
+  for (i=2; i<l; i++) v += Z_pval(gcoeff(x,i,i), p);
+  return v;
+}
+
+/* return factorization of Nx, x integral in HNF */
+GEN
+factor_norm(GEN x)
+{
+  GEN r = gcoeff(x,1,1), f, p, e;
+  long i, k, l;
+  if (typ(r)!=t_INT) pari_err_TYPE("idealfactor",r);
+  f = Z_factor(r); p = gel(f,1); e = gel(f,2); l = lg(p);
+  for (i=1; i<l; i++) e[i] = val_norm(x,gel(p,i), &k);
+  settyp(e, t_VECSMALL); return f;
+}
+
+/* X integral ideal */
+static GEN
+idealfactor_HNF(GEN nf, GEN x)
+{
+  const long N = lg(x)-1;
+  long i, j, k, lf, lc, v, vc;
+  GEN f, f1, f2, c1, c2, y1, y2, p1, cx, P;
+
+  x = Q_primitive_part(x, &cx);
+  if (!cx)
+  {
+    c1 = c2 = NULL; /* gcc -Wall */
+    lc = 1;
+  }
+  else
+  {
+    f = Z_factor(cx);
+    c1 = gel(f,1);
+    c2 = gel(f,2); lc = lg(c1);
+  }
+  f = factor_norm(x);
+  f1 = gel(f,1);
+  f2 = gel(f,2); lf = lg(f1);
+  y1 = cgetg((lf+lc-2)*N+1, t_COL);
+  y2 = cgetg((lf+lc-2)*N+1, t_VECSMALL);
+  k = 1;
+  for (i=1; i<lf; i++)
+  {
+    long l = f2[i]; /* = v_p(Nx) */
+    p1 = idealprimedec(nf,gel(f1,i));
+    vc = cx? Z_pval(cx,gel(f1,i)): 0;
+    for (j=1; j<lg(p1); j++)
+    {
+      P = gel(p1,j);
+      v = idealval(nf,x,P);
+      l -= v*pr_get_f(P);
+      v += vc * pr_get_e(P); if (!v) continue;
+      gel(y1,k) = P;
+      y2[k] = v; k++;
+      if (l == 0) break; /* now only the content contributes */
+    }
+    if (vc == 0) continue;
+    for (j++; j<lg(p1); j++)
+    {
+      P = gel(p1,j);
+      gel(y1,k) = P;
+      y2[k++] = vc * pr_get_e(P);
+    }
+  }
+  for (i=1; i<lc; i++)
+  {
+    /* p | Nx already treated */
+    if (dvdii(gcoeff(x,1,1),gel(c1,i))) continue;
+    p1 = idealprimedec(nf,gel(c1,i));
+    vc = itos(gel(c2,i));
+    for (j=1; j<lg(p1); j++)
+    {
+      P = gel(p1,j);
+      gel(y1,k) = P;
+      y2[k++] = vc * pr_get_e(P);
+    }
+  }
+  setlg(y1, k);
+  setlg(y2, k);
+  return mkmat2(y1, zc_to_ZC(y2));
+}
+
+GEN
+idealfactor(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  long tx;
+  GEN fa, f, y;
+
+  nf = checknf(nf);
+  tx = idealtyp(&x,&y);
+  if (tx == id_PRIME)
+  {
+    y = cgetg(3,t_MAT);
+    gel(y,1) = mkcolcopy(x);
+    gel(y,2) = mkcol(gen_1); return y;
+  }
+  if (tx == id_PRINCIPAL)
+  {
+    y = nf_to_scalar_or_basis(nf, x);
+    if (typ(y) != t_COL)
+    {
+      GEN c1, c2;
+      long lfa, i,j;
+      if (isintzero(y)) pari_err_DOMAIN("idealfactor", "ideal", "=",gen_0,x);
+      f = factor(Q_abs(y));
+      c1 = gel(f,1); lfa = lg(c1);
+      if (lfa == 1) { avma = av; return trivial_fact(); }
+      c2 = gel(f,2);
+      settyp(c1, t_VEC); /* for shallowconcat */
+      settyp(c2, t_VEC); /* for shallowconcat */
+      for (i = 1; i < lfa; i++)
+      {
+        GEN P = idealprimedec(nf, gel(c1,i)), E = gel(c2,i), z;
+        long lP = lg(P);
+        z = cgetg(lP, t_COL);
+        for (j = 1; j < lP; j++) gel(z,j) = mului(pr_get_e(gel(P,j)), E);
+        gel(c1,i) = P;
+        gel(c2,i) = z;
+      }
+      c1 = shallowconcat1(c1); settyp(c1, t_COL);
+      c2 = shallowconcat1(c2);
+      gel(f,1) = c1;
+      gel(f,2) = c2; return gerepilecopy(av, f);
+    }
+  }
+  y = idealnumden(nf, x);
+  if (isintzero(gel(y,1))) pari_err_DOMAIN("idealfactor", "ideal", "=",gen_0,x);
+  fa = idealfactor_HNF(nf, gel(y,1));
+  if (!isint1(gel(y,2)))
+  {
+    GEN fa2 = idealfactor_HNF(nf, gel(y,2));
+    fa2 = famat_inv_shallow(fa2);
+    fa = famat_mul_shallow(fa, fa2);
+  }
+  fa = gerepilecopy(av, fa);
+  return sort_factor(fa, (void*)&cmp_prime_ideal, &cmp_nodata);
+}
+
+/* P prime ideal in idealprimedec format. Return valuation(ix) at P */
+long
+idealval(GEN nf, GEN ix, GEN P)
+{
+  pari_sp av = avma, av1, lim;
+  long N, vmax, vd, v, e, f, i, j, k, tx = typ(ix);
+  GEN mul, B, a, x, y, r, p, pk, cx, vals;
+
+  if (is_extscalar_t(tx) || tx==t_COL) return nfval(nf,ix,P);
+  tx = idealtyp(&ix,&a);
+  if (tx == id_PRINCIPAL) return nfval(nf,ix,P);
+  checkprid(P);
+  if (tx == id_PRIME) return pr_equal(nf, P, ix)? 1: 0;
+  /* id_MAT */
+  nf = checknf(nf);
+  N = nf_get_degree(nf);
+  ix = Q_primitive_part(ix, &cx);
+  p = pr_get_p(P);
+  f = pr_get_f(P);
+  if (f == N) { v = cx? Q_pval(cx,p): 0; avma = av; return v; }
+  i = val_norm(ix,p, &k);
+  if (!i) { v = cx? pr_get_e(P) * Q_pval(cx,p): 0; avma = av; return v; }
+
+  e = pr_get_e(P);
+  vd = cx? e * Q_pval(cx,p): 0;
+  /* 0 <= ceil[v_P(ix) / e] <= v_p(ix \cap Z) --> v_P <= e * v_p */
+  j = k * e;
+  /* 0 <= v_P(ix) <= floor[v_p(Nix) / f] */
+  i = i / f;
+  vmax = minss(i,j); /* v_P(ix) <= vmax */
+
+  mul = pr_get_tau(P);
+  /* occurs when reading from file a prid in old format */
+  if (typ(mul) != t_MAT) mul = zk_scalar_or_multable(nf,mul);
+  B = cgetg(N+1,t_MAT);
+  pk = powiu(p, (ulong)ceil((double)vmax / e));
+  /* B[1] not needed: v_pr(ix[1]) = v_pr(ix \cap Z) is known already */
+  gel(B,1) = gen_0; /* dummy */
+  for (j=2; j<=N; j++)
+  {
+    x = gel(ix,j);
+    y = cgetg(N+1, t_COL); gel(B,j) = y;
+    for (i=1; i<=N; i++)
+    { /* compute a = (x.t0)_i, ix in HNF ==> x[j+1..N] = 0 */
+      a = mulii(gel(x,1), gcoeff(mul,i,1));
+      for (k=2; k<=j; k++) a = addii(a, mulii(gel(x,k), gcoeff(mul,i,k)));
+      /* p | a ? */
+      gel(y,i) = dvmdii(a,p,&r);
+      if (signe(r)) { avma = av; return vd; }
+    }
+  }
+  vals = cgetg(N+1, t_VECSMALL);
+  /* vals[1] not needed */
+  for (j = 2; j <= N; j++)
+  {
+    gel(B,j) = Q_primitive_part(gel(B,j), &cx);
+    vals[j] = cx? 1 + e * Q_pval(cx, p): 1;
+  }
+  av1 = avma; lim = stack_lim(av1,3);
+  y = cgetg(N+1,t_COL);
+  /* can compute mod p^ceil((vmax-v)/e) */
+  for (v = 1; v < vmax; v++)
+  { /* we know v_pr(Bj) >= v for all j */
+    if (e == 1 || (vmax - v) % e == 0) pk = diviiexact(pk, p);
+    for (j = 2; j <= N; j++)
+    {
+      x = gel(B,j); if (v < vals[j]) continue;
+      for (i=1; i<=N; i++)
+      {
+        pari_sp av2 = avma;
+        a = mulii(gel(x,1), gcoeff(mul,i,1));
+        for (k=2; k<=N; k++) a = addii(a, mulii(gel(x,k), gcoeff(mul,i,k)));
+        /* a = (x.t_0)_i; p | a ? */
+        a = dvmdii(a,p,&r);
+        if (signe(r)) { avma = av; return v + vd; }
+        if (lgefint(a) > lgefint(pk)) a = remii(a, pk);
+        gel(y,i) = gerepileuptoint(av2, a);
+      }
+      gel(B,j) = y; y = x;
+      if (low_stack(lim,stack_lim(av1,3)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"idealval");
+        gerepileall(av1,3, &y,&B,&pk);
+      }
+    }
+  }
+  avma = av; return v + vd;
+}
+
+/* gcd and generalized Bezout */
+
+GEN
+idealadd(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long tx, ty;
+  GEN z, a, dx, dy, dz;
+
+  tx = idealtyp(&x,&z);
+  ty = idealtyp(&y,&z); nf = checknf(nf);
+  if (tx != id_MAT) x = idealhnf_shallow(nf,x);
+  if (ty != id_MAT) y = idealhnf_shallow(nf,y);
+  if (lg(x) == 1) return gerepilecopy(av,y);
+  if (lg(y) == 1) return gerepilecopy(av,x); /* check for 0 ideal */
+  dx = Q_denom(x);
+  dy = Q_denom(y); dz = lcmii(dx,dy);
+  if (is_pm1(dz)) dz = NULL; else {
+    x = Q_muli_to_int(x, dz);
+    y = Q_muli_to_int(y, dz);
+  }
+  a = gcdii(gcoeff(x,1,1), gcoeff(y,1,1));
+  if (is_pm1(a))
+  {
+    long N = lg(x)-1;
+    if (!dz) { avma = av; return matid(N); }
+    return gerepileupto(av, scalarmat(ginv(dz), N));
+  }
+  z = ZM_hnfmodid(shallowconcat(x,y), a);
+  if (dz) z = RgM_Rg_div(z,dz);
+  return gerepileupto(av,z);
+}
+
+static GEN
+trivial_merge(GEN x)
+{
+  long lx = lg(x);
+  GEN a;
+  if (lx == 1) return NULL;
+  a = gcoeff(x,1,1);
+  if (!is_pm1(a)) return NULL;
+  return scalarcol_shallow(gen_1, lx-1);
+}
+GEN
+idealaddtoone_i(GEN nf, GEN x, GEN y)
+{
+  GEN a;
+  long tx = idealtyp(&x, &a/*junk*/);
+  long ty = idealtyp(&y, &a/*junk*/);
+  if (tx != id_MAT) x = idealhnf_shallow(nf, x);
+  if (ty != id_MAT) y = idealhnf_shallow(nf, y);
+  if (lg(x) == 1)
+    a = trivial_merge(y);
+  else if (lg(y) == 1)
+    a = trivial_merge(x);
+  else {
+    a = hnfmerge_get_1(x, y);
+    if (a) a = ZC_reducemodlll(a, idealmul_HNF(nf,x,y));
+  }
+  if (!a) pari_err_COPRIME("idealaddtoone",x,y);
+  return a;
+}
+
+GEN
+unnf_minus_x(GEN x)
+{
+  long i, N = lg(x);
+  GEN y = cgetg(N,t_COL);
+
+  gel(y,1) = gsubsg(1, gel(x,1));
+  for (i=2; i<N; i++) gel(y,i) = gneg(gel(x,i));
+  return y;
+}
+
+GEN
+idealaddtoone(GEN nf, GEN x, GEN y)
+{
+  GEN z = cgetg(3,t_VEC), a;
+  pari_sp av = avma;
+  nf = checknf(nf);
+  a = gerepileupto(av, idealaddtoone_i(nf,x,y));
+  gel(z,1) = a;
+  gel(z,2) = unnf_minus_x(a); return z;
+}
+
+/* assume elements of list are integral ideals */
+GEN
+idealaddmultoone(GEN nf, GEN list)
+{
+  pari_sp av = avma;
+  long N, i, l, nz, tx = typ(list);
+  GEN H, U, perm, L;
+
+  nf = checknf(nf); N = nf_get_degree(nf);
+  if (!is_vec_t(tx)) pari_err_TYPE("idealaddmultoone",list);
+  l = lg(list);
+  L = cgetg(l, t_VEC);
+  if (l == 1)
+    pari_err_DOMAIN("idealaddmultoone", "sum(ideals)", "!=", gen_1, L);
+  nz = 0; /* number of non-zero ideals in L */
+  for (i=1; i<l; i++)
+  {
+    GEN I = gel(list,i);
+    if (typ(I) != t_MAT) I = idealhnf_shallow(nf,I);
+    if (lg(I) != 1)
+    {
+      nz++; RgM_check_ZM(I,"idealaddmultoone");
+      if (lgcols(I) != N+1) pari_err_TYPE("idealaddmultoone [not an ideal]", I);
+    }
+    gel(L,i) = I;
+  }
+  H = ZM_hnfperm(shallowconcat1(L), &U, &perm);
+  if (lg(H) == 1 || !equali1(gcoeff(H,1,1)))
+    pari_err_DOMAIN("idealaddmultoone", "sum(ideals)", "!=", gen_1, L);
+  for (i=1; i<=N; i++)
+    if (perm[i] == 1) break;
+  U = gel(U,(nz-1)*N + i); /* (L[1]|...|L[nz]) U = 1 */
+  nz = 0;
+  for (i=1; i<l; i++)
+  {
+    GEN c = gel(L,i);
+    if (lg(c) == 1)
+      c = zerocol(N);
+    else {
+      c = ZM_ZC_mul(c, vecslice(U, nz*N + 1, (nz+1)*N));
+      nz++;
+    }
+    gel(L,i) = c;
+  }
+  return gerepilecopy(av, L);
+}
+
+/* multiplication */
+
+/* x integral ideal (without archimedean component) in HNF form
+ * y = [a,alpha] corresponds to the integral ideal aZ_K+alpha Z_K, a in Z,
+ * alpha a ZV or a ZM (multiplication table). Multiply them */
+static GEN
+idealmul_HNF_two(GEN nf, GEN x, GEN y)
+{
+  GEN m, a = gel(y,1), alpha = gel(y,2);
+  long i, N;
+
+  if (typ(alpha) != t_MAT)
+  {
+    alpha = zk_scalar_or_multable(nf, alpha);
+    if (typ(alpha) == t_INT) /* e.g. y inert ? 0 should not (but may) occur */
+      return signe(a)? ZM_Z_mul(x, gcdii(a, alpha)): cgetg(1,t_MAT);
+  }
+  N = lg(x)-1; m = cgetg((N<<1)+1,t_MAT);
+  for (i=1; i<=N; i++) gel(m,i)   = ZM_ZC_mul(alpha,gel(x,i));
+  for (i=1; i<=N; i++) gel(m,i+N) = ZC_Z_mul(gel(x,i), a);
+  return ZM_hnfmodid(m, mulii(a, gcoeff(x,1,1)));
+}
+
+/* Assume ix and iy are integral in HNF form [NOT extended]. Not memory clean.
+ * HACK: ideal in iy can be of the form [a,b], a in Z, b in Z_K */
+GEN
+idealmul_HNF(GEN nf, GEN x, GEN y)
+{
+  GEN z;
+  if (typ(y) == t_VEC)
+    z = idealmul_HNF_two(nf,x,y);
+  else
+  { /* reduce one ideal to two-elt form. The smallest */
+    GEN xZ = gcoeff(x,1,1), yZ = gcoeff(y,1,1);
+    if (cmpii(xZ, yZ) < 0)
+    {
+      if (is_pm1(xZ)) return gcopy(y);
+      z = idealmul_HNF_two(nf, y, mat_ideal_two_elt(nf,x));
+    }
+    else
+    {
+      if (is_pm1(yZ)) return gcopy(x);
+      z = idealmul_HNF_two(nf, x, mat_ideal_two_elt(nf,y));
+    }
+  }
+  return z;
+}
+
+/* operations on elements in factored form */
+
+GEN
+famat_mul_shallow(GEN f, GEN g)
+{
+  if (lg(f) == 1) return g;
+  if (lg(g) == 1) return f;
+  return mkmat2(shallowconcat(gel(f,1), gel(g,1)),
+                shallowconcat(gel(f,2), gel(g,2)));
+}
+
+GEN
+to_famat(GEN x, GEN y) {
+  GEN fa = cgetg(3, t_MAT);
+  gel(fa,1) = mkcol(gcopy(x));
+  gel(fa,2) = mkcol(gcopy(y)); return fa;
+}
+GEN
+to_famat_shallow(GEN x, GEN y) {
+  GEN fa = cgetg(3, t_MAT);
+  gel(fa,1) = mkcol(x);
+  gel(fa,2) = mkcol(y); return fa;
+}
+
+static GEN
+append(GEN v, GEN x)
+{
+  long i, l = lg(v);
+  GEN w = cgetg(l+1, typ(v));
+  for (i=1; i<l; i++) gel(w,i) = gcopy(gel(v,i));
+  gel(w,i) = gcopy(x); return w;
+}
+
+/* add x^1 to famat f */
+static GEN
+famat_add(GEN f, GEN x)
+{
+  GEN h = cgetg(3,t_MAT);
+  if (lg(f) == 1)
+  {
+    gel(h,1) = mkcolcopy(x);
+    gel(h,2) = mkcol(gen_1);
+  }
+  else
+  {
+    gel(h,1) = append(gel(f,1), x); /* x may be a t_COL */
+    gel(h,2) = concat(gel(f,2), gen_1);
+  }
+  return h;
+}
+
+GEN
+famat_mul(GEN f, GEN g)
+{
+  GEN h;
+  if (typ(g) != t_MAT) {
+    if (typ(f) == t_MAT) return famat_add(f, g);
+    h = cgetg(3, t_MAT);
+    gel(h,1) = mkcol2(gcopy(f), gcopy(g));
+    gel(h,2) = mkcol2(gen_1, gen_1);
+  }
+  if (typ(f) != t_MAT) return famat_add(g, f);
+  if (lg(f) == 1) return gcopy(g);
+  if (lg(g) == 1) return gcopy(f);
+  h = cgetg(3,t_MAT);
+  gel(h,1) = concat(gel(f,1), gel(g,1));
+  gel(h,2) = concat(gel(f,2), gel(g,2));
+  return h;
+}
+
+GEN
+famat_sqr(GEN f)
+{
+  GEN h;
+  if (lg(f) == 1) return cgetg(1,t_MAT);
+  if (typ(f) != t_MAT) return to_famat(f,gen_2);
+  h = cgetg(3,t_MAT);
+  gel(h,1) = gcopy(gel(f,1));
+  gel(h,2) = gmul2n(gel(f,2),1);
+  return h;
+}
+GEN
+famat_inv_shallow(GEN f)
+{
+  GEN h;
+  if (lg(f) == 1) return cgetg(1,t_MAT);
+  if (typ(f) != t_MAT) return to_famat_shallow(f,gen_m1);
+  h = cgetg(3,t_MAT);
+  gel(h,1) = gel(f,1);
+  gel(h,2) = ZC_neg(gel(f,2));
+  return h;
+}
+GEN
+famat_inv(GEN f)
+{
+  GEN h;
+  if (lg(f) == 1) return cgetg(1,t_MAT);
+  if (typ(f) != t_MAT) return to_famat(f,gen_m1);
+  h = cgetg(3,t_MAT);
+  gel(h,1) = gcopy(gel(f,1));
+  gel(h,2) = ZC_neg(gel(f,2));
+  return h;
+}
+GEN
+famat_pow(GEN f, GEN n)
+{
+  GEN h;
+  if (lg(f) == 1) return cgetg(1,t_MAT);
+  if (typ(f) != t_MAT) return to_famat(f,n);
+  h = cgetg(3,t_MAT);
+  gel(h,1) = gcopy(gel(f,1));
+  gel(h,2) = ZC_Z_mul(gel(f,2),n);
+  return h;
+}
+
+/* x assumed to be a t_MATs (factorization matrix), or compatible with
+ * the element_* functions. */
+static GEN
+ext_sqr(GEN nf, GEN x) {
+  if (typ(x) == t_MAT) return famat_sqr(x);
+  return nfsqr(nf, x);
+}
+static GEN
+ext_mul(GEN nf, GEN x, GEN y) {
+  if (typ(x) == t_MAT) return (x == y)? famat_sqr(x): famat_mul(x,y);
+  return nfmul(nf, x, y);
+}
+static GEN
+ext_inv(GEN nf, GEN x) {
+  if (typ(x) == t_MAT) return famat_inv(x);
+  return nfinv(nf, x);
+}
+static GEN
+ext_pow(GEN nf, GEN x, GEN n) {
+  if (typ(x) == t_MAT) return famat_pow(x,n);
+  return nfpow(nf, x, n);
+}
+
+/* x, y 2 extended ideals whose first component is an integral HNF */
+GEN
+extideal_HNF_mul(GEN nf, GEN x, GEN y)
+{
+  return mkvec2(idealmul_HNF(nf, gel(x,1), gel(y,1)),
+                ext_mul(nf, gel(x,2), gel(y,2)));
+}
+
+GEN
+famat_to_nf(GEN nf, GEN f)
+{
+  GEN t, x, e;
+  long i;
+  if (lg(f) == 1) return gen_1;
+
+  x = gel(f,1);
+  e = gel(f,2);
+  t = nfpow(nf, gel(x,1), gel(e,1));
+  for (i=lg(x)-1; i>1; i--)
+    t = nfmul(nf, t, nfpow(nf, gel(x,i), gel(e,i)));
+  return t;
+}
+
+/* "compare" two nf elt. Goal is to quickly sort for uniqueness of
+ * representation, not uniqueness of represented element ! */
+static int
+elt_cmp(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y);
+  if (ty == tx)
+    return (tx == t_POL || tx == t_POLMOD)? cmp_RgX(x,y): lexcmp(x,y);
+  return tx - ty;
+}
+static int
+elt_egal(GEN x, GEN y)
+{
+  if (typ(x) == typ(y)) return gequal(x,y);
+  return 0;
+}
+
+GEN
+famat_reduce(GEN fa)
+{
+  GEN E, G, L, g, e;
+  long i, k, l;
+
+  if (lg(fa) == 1) return fa;
+  g = gel(fa,1); l = lg(g);
+  e = gel(fa,2);
+  L = gen_indexsort(g, (void*)&elt_cmp, &cmp_nodata);
+  G = cgetg(l, t_COL);
+  E = cgetg(l, t_COL);
+  /* merge */
+  for (k=i=1; i<l; i++,k++)
+  {
+    gel(G,k) = gel(g,L[i]);
+    gel(E,k) = gel(e,L[i]);
+    if (k > 1 && elt_egal(gel(G,k), gel(G,k-1)))
+    {
+      gel(E,k-1) = addii(gel(E,k), gel(E,k-1));
+      k--;
+    }
+  }
+  /* kill 0 exponents */
+  l = k;
+  for (k=i=1; i<l; i++)
+    if (!gequal0(gel(E,i)))
+    {
+      gel(G,k) = gel(G,i);
+      gel(E,k) = gel(E,i); k++;
+    }
+  setlg(G, k);
+  setlg(E, k); return mkmat2(G,E);
+}
+
+GEN
+famatsmall_reduce(GEN fa)
+{
+  GEN E, G, L, g, e;
+  long i, k, l;
+  if (lg(fa) == 1) return fa;
+  g = gel(fa,1); l = lg(g);
+  e = gel(fa,2);
+  L = vecsmall_indexsort(g);
+  G = cgetg(l, t_VECSMALL);
+  E = cgetg(l, t_VECSMALL);
+  /* merge */
+  for (k=i=1; i<l; i++,k++)
+  {
+    G[k] = g[L[i]];
+    E[k] = e[L[i]];
+    if (k > 1 && G[k] == G[k-1])
+    {
+      E[k-1] += E[k];
+      k--;
+    }
+  }
+  /* kill 0 exponents */
+  l = k;
+  for (k=i=1; i<l; i++)
+    if (E[i])
+    {
+      G[k] = G[i];
+      E[k] = E[i]; k++;
+    }
+  setlg(G, k);
+  setlg(E, k); return mkmat2(G,E);
+}
+
+GEN
+ZM_famat_limit(GEN fa, GEN limit)
+{
+  pari_sp av;
+  GEN E, G, g, e, r;
+  long i, k, l, n, lG;
+
+  if (lg(fa) == 1) return fa;
+  g = gel(fa,1); l = lg(g);
+  e = gel(fa,2);
+  for(n=0, i=1; i<l; i++)
+    if (cmpii(gel(g,i),limit)<=0) n++;
+  lG = n<l-1 ? n+2 : n+1;
+  G = cgetg(lG, t_COL);
+  E = cgetg(lG, t_COL);
+  av = avma;
+  for (i=1, k=1, r = gen_1; i<l; i++)
+  {
+    if (cmpii(gel(g,i),limit)<=0)
+    {
+      gel(G,k) = gel(g,i);
+      gel(E,k) = gel(e,i);
+      k++;
+    } else r = mulii(r, powii(gel(g,i), gel(e,i)));
+  }
+  if (k<i)
+  {
+    gel(G, k) = gerepileuptoint(av, r);
+    gel(E, k) = gen_1;
+  }
+  return mkmat2(G,E);
+}
+
+/* assume pr has degree 1 and coprime to numerator(x) */
+static GEN
+nf_to_Fp_simple(GEN x, GEN modpr, GEN p)
+{
+  GEN c, r = zk_to_Fq(Q_primitive_part(x, &c), modpr);
+  if (c) r = Rg_to_Fp(gmul(r, c), p);
+  return r;
+}
+
+static GEN
+famat_to_Fp_simple(GEN nf, GEN x, GEN modpr, GEN p)
+{
+  GEN h, n, t = gen_1, g = gel(x,1), e = gel(x,2), q = subis(p,1);
+  long i, l = lg(g);
+
+  for (i=1; i<l; i++)
+  {
+    n = gel(e,i); n = modii(n,q);
+    if (!signe(n)) continue;
+
+    h = gel(g,i);
+    switch(typ(h))
+    {
+      case t_POL: case t_POLMOD: h = algtobasis(nf, h);  /* fall through */
+      case t_COL: h = nf_to_Fp_simple(h, modpr, p); break;
+      default: h = Rg_to_Fp(h, p);
+    }
+    t = mulii(t, Fp_pow(h, n, p)); /* not worth reducing */
+  }
+  return modii(t, p);
+}
+
+/* cf famat_to_nf_modideal_coprime, but id is a prime of degree 1 (=pr) */
+GEN
+to_Fp_simple(GEN nf, GEN x, GEN pr)
+{
+  GEN T, p, modpr = zk_to_Fq_init(nf, &pr, &T, &p);
+  switch(typ(x))
+  {
+    case t_COL: return nf_to_Fp_simple(x,modpr,p);
+    case t_MAT: return famat_to_Fp_simple(nf,x,modpr,p);
+    default: return Rg_to_Fp(x, p);
+  }
+}
+
+/* Compute A = prod g[i]^e[i] mod pr^k, assuming (A, pr) = 1.
+ * Method: modify each g[i] so that it becomes coprime to pr :
+ *  x / (p^k u) --> x * (b/p)^v_pr(x) / z^k u, where z = b^e/p^(e-1)
+ * b/p = pr^(-1) times something prime to p; both numerator and denominator
+ * are integral and coprime to pr.  Globally, we multiply by (b/p)^v_pr(A) = 1.
+ *
+ * EX = multiple of exponent of (O_K / pr^k)^* used to reduce the product in
+ * case the e[i] are large */
+GEN
+famat_makecoprime(GEN nf, GEN g, GEN e, GEN pr, GEN prk, GEN EX)
+{
+  long i, l = lg(g);
+  GEN prkZ, u, vden = gen_0, p = pr_get_p(pr);
+  pari_sp av = avma, lim = stack_lim(av, 2);
+  GEN newg = cgetg(l+1, t_VEC); /* room for z */
+
+  prkZ = gcoeff(prk, 1,1);
+  for (i=1; i < l; i++)
+  {
+    GEN dx, x = nf_to_scalar_or_basis(nf, gel(g,i));
+    long vdx = 0;
+    x = Q_remove_denom(x, &dx);
+    if (dx)
+    {
+      vdx = Z_pvalrem(dx, p, &u);
+      if (!is_pm1(u))
+      { /* could avoid the inversion, but prkZ is small--> cheap */
+        u = Fp_inv(u, prkZ);
+        x = typ(x) == t_INT? mulii(x,u): ZC_Z_mul(x, u);
+      }
+      if (vdx) vden = addii(vden, mului(vdx, gel(e,i)));
+    }
+    if (typ(x) == t_INT) {
+      if (!vdx) vden = subii(vden, mului(Z_pvalrem(x, p, &x), gel(e,i)));
+    } else {
+      (void)ZC_nfvalrem(nf, x, pr, &x);
+      x =  ZC_hnfrem(x, prk);
+    }
+    gel(newg,i) = x;
+    if (low_stack(lim, stack_lim(av, 2)))
+    {
+      GEN dummy = cgetg(1,t_VEC);
+      long j;
+      if(DEBUGMEM>1) pari_warn(warnmem,"famat_makecoprime");
+      for (j = i+1; j <= l; j++) gel(newg,j) = dummy;
+      gerepileall(av,2, &newg, &vden);
+    }
+  }
+  if (vden == gen_0) setlg(newg, l);
+  else
+  {
+    GEN t = special_anti_uniformizer(nf, pr);
+    if (typ(t) == t_INT) setlg(newg, l); /* = 1 */
+    else {
+      if (typ(t) == t_MAT) t = gel(t,1); /* multiplication table */
+      gel(newg,i) = FpC_red(t, prkZ);
+      e = shallowconcat(e, negi(vden));
+    }
+  }
+  return famat_to_nf_modideal_coprime(nf, newg, e, prk, EX);
+}
+
+/* prod g[i]^e[i] mod bid, assume (g[i], id) = 1 */
+GEN
+famat_to_nf_moddivisor(GEN nf, GEN g, GEN e, GEN bid)
+{
+  GEN t,sarch,module,cyc,fa2;
+  long lc;
+  if (lg(g) == 1) return scalarcol_shallow(gen_1, nf_get_degree(nf)); /* 1 */
+  module = bid_get_mod(bid);
+  cyc = bid_get_cyc(bid); lc = lg(cyc);
+  fa2 = gel(bid,4); sarch = gel(fa2,lg(fa2)-1);
+  t = NULL;
+  if (lc != 1)
+  {
+    GEN EX = gel(cyc,1); /* group exponent */
+    GEN id = gel(module,1);
+    t = famat_to_nf_modideal_coprime(nf, g, e, id, EX);
+  }
+  if (!t) t = gen_1;
+  return set_sign_mod_divisor(nf, mkmat2(g,e), t, module, sarch);
+}
+
+GEN
+vecmul(GEN x, GEN y)
+{
+  long i,lx, tx = typ(x);
+  GEN z;
+  if (is_scalar_t(tx)) return gmul(x,y);
+  z = cgetg_copy(x, &lx);
+  for (i=1; i<lx; i++) gel(z,i) = vecmul(gel(x,i), gel(y,i));
+  return z;
+}
+
+GEN
+vecinv(GEN x)
+{
+  long i,lx, tx = typ(x);
+  GEN z;
+  if (is_scalar_t(tx)) return ginv(x);
+  z = cgetg_copy(x, &lx);
+  for (i=1; i<lx; i++) gel(z,i) = vecinv(gel(x,i));
+  return z;
+}
+
+GEN
+vecpow(GEN x, GEN n)
+{
+  long i,lx, tx = typ(x);
+  GEN z;
+  if (is_scalar_t(tx)) return powgi(x,n);
+  z = cgetg_copy(x, &lx);
+  for (i=1; i<lx; i++) gel(z,i) = vecpow(gel(x,i), n);
+  return z;
+}
+
+GEN
+vecdiv(GEN x, GEN y)
+{
+  long i,lx, tx = typ(x);
+  GEN z;
+  if (is_scalar_t(tx)) return gdiv(x,y);
+  z = cgetg_copy(x, &lx);
+  for (i=1; i<lx; i++) gel(z,i) = vecdiv(gel(x,i), gel(y,i));
+  return z;
+}
+
+/* v ideal as a square t_MAT */
+static GEN
+idealmulelt(GEN nf, GEN x, GEN v)
+{
+  long i, lx;
+  GEN cx;
+  if (lg(v) == 1) return cgetg(1, t_MAT);
+  x = nf_to_scalar_or_basis(nf,x);
+  if (typ(x) != t_COL)
+    return isintzero(x)? cgetg(1,t_MAT): RgM_Rg_mul(v, Q_abs(x));
+  x = nfC_nf_mul(nf, v, x);
+  x = Q_primitive_part(x, &cx);
+  settyp(x, t_MAT); lx = lg(x);
+  /* x may contain scalars (at most 1 since the ideal is non-0)*/
+  for (i=1; i<lx; i++)
+    if (typ(gel(x,i)) == t_INT)
+    {
+      if (i > 1) swap(gel(x,1), gel(x,i)); /* help HNF */
+      gel(x,1) = scalarcol_shallow(gel(x,1), lx-1);
+      break;
+    }
+  x = ZM_hnfmod(x, ZM_detmult(x));
+  return cx? ZM_Q_mul(x,cx): x;
+}
+
+/* tx <= ty */
+static GEN
+idealmul_aux(GEN nf, GEN x, GEN y, long tx, long ty)
+{
+  GEN z, cx, cy;
+  switch(tx)
+  {
+    case id_PRINCIPAL:
+      switch(ty)
+      {
+        case id_PRINCIPAL:
+          return idealhnf_principal(nf, nfmul(nf,x,y));
+        case id_PRIME:
+        {
+          GEN p = gel(y,1), pi = gel(y,2), cx;
+          if (pr_is_inert(y)) return RgM_Rg_mul(idealhnf_principal(nf,x),p);
+
+          x = nf_to_scalar_or_basis(nf, x);
+          switch(typ(x))
+          {
+            case t_INT:
+              if (!signe(x)) return cgetg(1,t_MAT);
+              return ZM_Z_mul(idealhnf_two(nf,y), absi(x));
+            case t_FRAC:
+              return RgM_Rg_mul(idealhnf_two(nf,y), Q_abs(x));
+          }
+          /* t_COL */
+          x = Q_primitive_part(x, &cx);
+          x = zk_multable(nf, x);
+          z = shallowconcat(ZM_Z_mul(x,p), ZM_ZC_mul(x,pi));
+          z = ZM_hnfmod(z, ZM_detmult(z));
+          return cx? ZM_Q_mul(z, cx): z;
+        }
+        default: /* id_MAT */
+          return idealmulelt(nf, x,y);
+      }
+    case id_PRIME:
+      if (ty==id_PRIME)
+      { y = idealhnf_two(nf,y); cy = NULL; }
+      else
+        y = Q_primitive_part(y, &cy);
+      y = idealmul_HNF_two(nf,y,x);
+      return cy? RgM_Rg_mul(y,cy): y;
+
+    default: /* id_MAT */
+      x = Q_primitive_part(x, &cx);
+      y = Q_primitive_part(y, &cy); cx = mul_content(cx,cy);
+      y = idealmul_HNF(nf,x,y);
+      return cx? ZM_Q_mul(y,cx): y;
+  }
+}
+
+/* output the ideal product ix.iy */
+GEN
+idealmul(GEN nf, GEN x, GEN y)
+{
+  pari_sp av;
+  GEN res, ax, ay, z;
+  long tx = idealtyp(&x,&ax);
+  long ty = idealtyp(&y,&ay), f;
+  if (tx>ty) { swap(ax,ay); swap(x,y); lswap(tx,ty); }
+  f = (ax||ay); res = f? cgetg(3,t_VEC): NULL; /*product is an extended ideal*/
+  av = avma;
+  z = gerepileupto(av, idealmul_aux(checknf(nf), x,y, tx,ty));
+  if (!f) return z;
+  if (ax && ay)
+    ax = ext_mul(nf, ax, ay);
+  else
+    ax = gcopy(ax? ax: ay);
+  gel(res,1) = z; gel(res,2) = ax; return res;
+}
+GEN
+idealsqr(GEN nf, GEN x)
+{
+  pari_sp av;
+  GEN res, ax, z;
+  long tx = idealtyp(&x,&ax);
+  res = ax? cgetg(3,t_VEC): NULL; /*product is an extended ideal*/
+  av = avma;
+  z = gerepileupto(av, idealmul_aux(checknf(nf), x,x, tx,tx));
+  if (!ax) return z;
+  gel(res,1) = z;
+  gel(res,2) = ext_sqr(nf, ax); return res;
+}
+
+/* norm of an ideal */
+GEN
+idealnorm(GEN nf, GEN x)
+{
+  pari_sp av;
+  GEN y, T;
+  long tx;
+
+  switch(idealtyp(&x,&y))
+  {
+    case id_PRIME: return pr_norm(x);
+    case id_MAT: return RgM_det_triangular(x);
+  }
+  /* id_PRINCIPAL */
+  nf = checknf(nf); T = nf_get_pol(nf); av = avma;
+  x = nf_to_scalar_or_alg(nf, x);
+  x = (typ(x) == t_POL)? RgXQ_norm(x, T): gpowgs(x, degpol(T));
+  tx = typ(x);
+  if (tx == t_INT) return gerepileuptoint(av, absi(x));
+  if (tx != t_FRAC) pari_err_TYPE("idealnorm",x);
+  return gerepileupto(av, Q_abs(x));
+}
+
+/* inverse */
+
+/* rewritten from original code by P.M & M.H.
+ *
+ * I^(-1) = { x \in K, Tr(x D^(-1) I) \in Z }, D different of K/Q
+ *
+ * nf[5][6] = pp( D^(-1) ) = pp( HNF( T^(-1) ) ), T = (Tr(wi wj))
+ * nf[5][7] = same in 2-elt form.
+ * Assume I integral. Return the integral ideal (I\cap Z) I^(-1) */
+static GEN
+idealinv_HNF_aux(GEN nf, GEN I)
+{
+  GEN J, dual, IZ = gcoeff(I,1,1); /* I \cap Z */
+
+  if (isint1(IZ)) return matid(lg(I)-1);
+  J = idealmul_HNF(nf,I, gmael(nf,5,7));
+ /* I in HNF, hence easily inverted; multiply by IZ to get integer coeffs
+  * missing content cancels while solving the linear equation */
+  dual = shallowtrans( hnf_divscale(J, gmael(nf,5,6), IZ) );
+  return ZM_hnfmodid(dual, IZ);
+}
+/* I HNF with rational coefficients (denominator d). */
+static GEN
+idealinv_HNF(GEN nf, GEN I)
+{
+  GEN J, IQ = gcoeff(I,1,1); /* I \cap Q; d IQ = dI \cap Z */
+
+  /* J = (dI)^(-1) * (d IQ) */
+  J = idealinv_HNF_aux(nf, Q_remove_denom(I, NULL));
+  if (typ(IQ) != t_INT || !is_pm1(IQ)) J = RgM_Rg_div(J, IQ);
+  return J;
+}
+
+/* return p * P^(-1)  [integral] */
+GEN
+pidealprimeinv(GEN nf, GEN x)
+{
+  if (pr_is_inert(x)) return matid(lg(gel(x,2)) - 1);
+  return idealhnf_two(nf, mkvec2(gel(x,1), gel(x,5)));
+}
+
+GEN
+idealinv(GEN nf, GEN x)
+{
+  GEN res,ax;
+  pari_sp av;
+  long tx = idealtyp(&x,&ax);
+
+  res = ax? cgetg(3,t_VEC): NULL;
+  nf = checknf(nf); av = avma;
+  switch (tx)
+  {
+    case id_MAT:
+      if (lg(x)-1 != nf_get_degree(nf)) pari_err_DIM("idealinv");
+      x = idealinv_HNF(nf,x); break;
+    case id_PRINCIPAL: tx = typ(x);
+      if (is_const_t(tx)) x = ginv(x);
+      else
+      {
+        GEN T;
+        switch(tx)
+        {
+          case t_COL: x = coltoliftalg(nf,x); break;
+          case t_POLMOD: x = gel(x,2); break;
+        }
+        if (typ(x) != t_POL) { x = ginv(x); break; }
+        T = nf_get_pol(nf);
+        if (varn(x) != varn(T)) pari_err_VAR("idealinv", x, T);
+        x = QXQ_inv(x, T);
+      }
+      x = idealhnf_principal(nf,x); break;
+    case id_PRIME:
+      x = RgM_Rg_div(pidealprimeinv(nf,x), gel(x,1));
+  }
+  x = gerepileupto(av,x); if (!ax) return x;
+  gel(res,1) = x;
+  gel(res,2) = ext_inv(nf, ax); return res;
+}
+
+/* write x = A/B, A,B coprime integral ideals */
+GEN
+idealnumden(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  GEN ax, c, d, A, B, J;
+  long tx = idealtyp(&x,&ax);
+  nf = checknf(nf);
+  switch (tx)
+  {
+    case id_PRIME:
+      retmkvec2(idealhnf(nf, x), gen_1);
+    case id_PRINCIPAL:
+      x = nf_to_scalar_or_basis(nf, x);
+      switch(typ(x))
+      {
+        case t_INT:
+          return gerepilecopy(av, mkvec2(absi(x),gen_1));
+        case t_FRAC:
+          return gerepilecopy(av, mkvec2(absi(gel(x,1)), gel(x,2)));
+      }
+      /* t_COL */
+      x = Q_remove_denom(x, &d);
+      if (!d) return gerepilecopy(av, mkvec2(idealhnf(nf, x), gen_1));
+      x = idealhnf(nf, x);
+      break;
+    case id_MAT: {
+      long n = lg(x)-1;
+      if (n == 0) return mkvec2(gen_0, gen_1);
+      if (n != nf_get_degree(nf)) pari_err_DIM("idealnumden");
+      x = Q_remove_denom(x, &d);
+      if (!d) return gerepilecopy(av, mkvec2(x, gen_1));
+      break;
+    }
+  }
+  J = hnfmodid(x, d); /* = d/B */
+  c = gcoeff(J,1,1); /* (d/B) \cap Z, divides d */
+  B = idealinv_HNF_aux(nf, J); /* (d/B \cap Z) B/d */
+  c = diviiexact(d, c);
+  if (!is_pm1(c)) B = ZM_Z_mul(B, c); /* = B ! */
+  A = idealmul(nf, x, B); /* d * (original x) * B = d A */
+  if (!is_pm1(d)) A = ZM_Z_divexact(A, d); /* = A ! */
+  if (is_pm1(gcoeff(B,1,1))) B = gen_1;
+  return gerepilecopy(av, mkvec2(A, B));
+}
+
+/* Return x, integral in 2-elt form, such that pr^n = x/d. Assume n != 0 */
+static GEN
+idealpowprime(GEN nf, GEN pr, GEN n, GEN *d)
+{
+  long s = signe(n);
+  GEN q, gen;
+
+  if (is_pm1(n)) /* n = 1 special cased for efficiency */
+  {
+    q = pr_get_p(pr);
+    if (s < 0) {
+      gen = pr_get_tau(pr);
+      if (typ(gen) == t_MAT) gen = gel(gen,1);
+      *d = q;
+    } else {
+      gen = pr_get_gen(pr);
+      *d = NULL;
+    }
+  }
+  else
+  {
+    ulong r;
+    GEN p = pr_get_p(pr);
+    GEN m = diviu_rem(n, pr_get_e(pr), &r);
+    if (r) m = addis(m,1); /* m = ceil(|n|/e) */
+    q = powii(p,m);
+    if (s < 0)
+    {
+      gen = pr_get_tau(pr);
+      if (typ(gen) == t_MAT) gen = gel(gen,1);
+      n = negi(n);
+      gen = ZC_Z_divexact(nfpow(nf, gen, n), powii(p, subii(n,m)));
+      *d = q;
+    }
+    else
+    {
+      gen = nfpow(nf, pr_get_gen(pr), n);
+      *d = NULL;
+    }
+  }
+  return mkvec2(q, gen);
+}
+
+/* x * pr^n. Assume x in HNF (possibly non-integral) */
+GEN
+idealmulpowprime(GEN nf, GEN x, GEN pr, GEN n)
+{
+  GEN cx,y,dx;
+
+  if (!signe(n)) return x;
+  nf = checknf(nf);
+
+  /* inert, special cased for efficiency */
+  if (pr_is_inert(pr)) return RgM_Rg_mul(x, powii(pr_get_p(pr), n));
+
+  y = idealpowprime(nf, pr, n, &dx);
+  x = Q_primitive_part(x, &cx);
+  if (cx && dx)
+  {
+    cx = gdiv(cx, dx);
+    if (typ(cx) != t_FRAC) dx = NULL;
+    else { dx = gel(cx,2); cx = gel(cx,1); }
+    if (is_pm1(cx)) cx = NULL;
+  }
+  x = idealmul_HNF_two(nf,x,y);
+  if (cx) x = RgM_Rg_mul(x,cx);
+  if (dx) x = RgM_Rg_div(x,dx);
+  return x;
+}
+GEN
+idealdivpowprime(GEN nf, GEN x, GEN pr, GEN n)
+{
+  return idealmulpowprime(nf,x,pr, negi(n));
+}
+
+static GEN
+idealpow_aux(GEN nf, GEN x, long tx, GEN n)
+{
+  GEN T = nf_get_pol(nf), m, cx, n1, a, alpha;
+  long N = degpol(T), s = signe(n);
+  if (!s) return matid(N);
+  switch(tx)
+  {
+    case id_PRINCIPAL:
+      x = nf_to_scalar_or_alg(nf, x);
+      x = (typ(x) == t_POL)? RgXQ_pow(x,n,T): powgi(x,n);
+      return idealhnf_principal(nf,x);
+    case id_PRIME: {
+      GEN d;
+      if (pr_is_inert(x)) return scalarmat(powii(gel(x,1), n), N);
+      x = idealpowprime(nf, x, n, &d);
+      x = idealhnf_two(nf,x);
+      return d? RgM_Rg_div(x, d): x;
+    }
+    default:
+      if (is_pm1(n)) return (s < 0)? idealinv(nf, x): gcopy(x);
+      n1 = (s < 0)? negi(n): n;
+
+      x = Q_primitive_part(x, &cx);
+      a = mat_ideal_two_elt(nf,x); alpha = gel(a,2); a = gel(a,1);
+      alpha = nfpow(nf,alpha,n1);
+      m = zk_scalar_or_multable(nf, alpha);
+      if (typ(m) == t_INT) {
+        x = gcdii(m, powii(a,n1));
+        if (s<0) x = ginv(x);
+        if (cx) x = gmul(x, powgi(cx,n));
+        x = scalarmat(x, N);
+      }
+      else {
+        x = ZM_hnfmodid(m, powii(a,n1));
+        if (cx) cx = powgi(cx,n);
+        if (s<0) {
+          GEN xZ = gcoeff(x,1,1);
+          cx = cx ? gdiv(cx, xZ): ginv(xZ);
+          x = idealinv_HNF_aux(nf,x);
+        }
+        if (cx) x = RgM_Rg_mul(x, cx);
+      }
+      return x;
+  }
+}
+
+/* raise the ideal x to the power n (in Z) */
+GEN
+idealpow(GEN nf, GEN x, GEN n)
+{
+  pari_sp av;
+  long tx;
+  GEN res, ax;
+
+  if (typ(n) != t_INT) pari_err_TYPE("idealpow",n);
+  tx = idealtyp(&x,&ax);
+  res = ax? cgetg(3,t_VEC): NULL;
+  av = avma;
+  x = gerepileupto(av, idealpow_aux(checknf(nf), x, tx, n));
+  if (!ax) return x;
+  ax = ext_pow(nf, ax, n);
+  gel(res,1) = x;
+  gel(res,2) = ax;
+  return res;
+}
+
+/* Return ideal^e in number field nf. e is a C integer. */
+GEN
+idealpows(GEN nf, GEN ideal, long e)
+{
+  long court[] = {evaltyp(t_INT) | _evallg(3),0,0};
+  affsi(e,court); return idealpow(nf,ideal,court);
+}
+
+static GEN
+_idealmulred(GEN nf, GEN x, GEN y)
+{ return idealred(nf,idealmul(nf,x,y)); }
+static GEN
+_idealsqrred(GEN nf, GEN x)
+{ return idealred(nf,idealsqr(nf,x)); }
+static GEN
+_mul(void *data, GEN x, GEN y) { return _idealmulred((GEN)data,x,y); }
+static GEN
+_sqr(void *data, GEN x) { return _idealsqrred((GEN)data, x); }
+
+/* compute x^n (x ideal, n integer), reducing along the way */
+GEN
+idealpowred(GEN nf, GEN x, GEN n)
+{
+  pari_sp av = avma;
+  long s;
+  GEN y;
+
+  if (typ(n) != t_INT) pari_err_TYPE("idealpowred",n);
+  s = signe(n); if (s == 0) return idealpow(nf,x,n);
+  y = gen_pow(x, n, (void*)nf, &_sqr, &_mul);
+
+  if (s < 0) y = idealinv(nf,y);
+  if (s < 0 || is_pm1(n)) y = idealred(nf,y);
+  return gerepileupto(av,y);
+}
+
+GEN
+idealmulred(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, _idealmulred(nf,x,y));
+}
+
+long
+isideal(GEN nf,GEN x)
+{
+  long N, i, j, lx, tx = typ(x);
+  pari_sp av;
+  GEN T;
+
+  nf = checknf(nf); T = nf_get_pol(nf); lx = lg(x);
+  if (tx==t_VEC && lx==3) { x = gel(x,1); tx = typ(x); lx = lg(x); }
+  switch(tx)
+  {
+    case t_INT: case t_FRAC: return 1;
+    case t_POL: return varn(x) == varn(T);
+    case t_POLMOD: return RgX_equal_var(T, gel(x,1));
+    case t_VEC: return get_prid(x)? 1 : 0;
+    case t_MAT: break;
+    default: return 0;
+  }
+  N = degpol(T);
+  if (lx-1 != N) return (lx == 1);
+  if (nbrows(x) != N) return 0;
+
+  av = avma; x = Q_primpart(x);
+  if (!ZM_ishnf(x)) return 0;
+  for (i=2; i<=N; i++)
+    for (j=2; j<=N; j++)
+      if (! hnf_invimage(x, zk_ei_mul(nf,gel(x,i),j))) { avma = av; return 0; }
+  avma=av; return 1;
+}
+
+GEN
+idealdiv(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma, tetpil;
+  GEN z = idealinv(nf,y);
+  tetpil = avma; return gerepile(av,tetpil, idealmul(nf,x,z));
+}
+
+/* This routine computes the quotient x/y of two ideals in the number field nf.
+ * It assumes that the quotient is an integral ideal.  The idea is to find an
+ * ideal z dividing y such that gcd(Nx/Nz, Nz) = 1.  Then
+ *
+ *   x + (Nx/Nz)    x
+ *   ----------- = ---
+ *   y + (Ny/Nz)    y
+ *
+ * Proof: we can assume x and y are integral. Let p be any prime ideal
+ *
+ * If p | Nz, then it divides neither Nx/Nz nor Ny/Nz (since Nx/Nz is the
+ * product of the integers N(x/y) and N(y/z)).  Both the numerator and the
+ * denominator on the left will be coprime to p.  So will x/y, since x/y is
+ * assumed integral and its norm N(x/y) is coprime to p.
+ *
+ * If instead p does not divide Nz, then v_p (Nx/Nz) = v_p (Nx) >= v_p(x).
+ * Hence v_p (x + Nx/Nz) = v_p(x).  Likewise for the denominators.  QED.
+ *
+ *                Peter Montgomery.  July, 1994. */
+GEN
+idealdivexact(GEN nf, GEN x0, GEN y0)
+{
+  pari_sp av = avma;
+  GEN x, y, yZ, Nx, Ny, Nz, cy;
+
+  nf = checknf(nf);
+  x = idealhnf_shallow(nf, x0);
+  y = idealhnf_shallow(nf, y0);
+  if (lg(y) == 1) pari_err_INV("idealdivexact", y0);
+  if (lg(x) == 1) { avma = av; return cgetg(1, t_MAT); } /* numerator is zero */
+  y = Q_primitive_part(y, &cy);
+  if (cy) x = RgM_Rg_div(x,cy);
+  Nx = idealnorm(nf,x);
+  Ny = idealnorm(nf,y);
+  if (typ(Nx) != t_INT || typ(Ny) != t_INT || !dvdii(Nx,Ny))
+    pari_err_DOMAIN("idealdivexact","denominator(x/y)", "!=",
+                    gen_1,mkvec2(x,y));
+  /* Find a norm Nz | Ny such that gcd(Nx/Nz, Nz) = 1 */
+  for (Nz = Ny;;)
+  {
+    GEN p1 = gcdii(Nz, diviiexact(Nx,Nz));
+    if (is_pm1(p1)) break;
+    Nz = diviiexact(Nz,p1);
+  }
+  /* Replace x/y  by  x+(Nx/Nz) / y+(Ny/Nz) */
+  x = ZM_hnfmodid(x, diviiexact(Nx,Nz));
+  /* y reduced to unit ideal ? */
+  if (Nz == Ny) return gerepileupto(av, x);
+
+  y = ZM_hnfmodid(y, diviiexact(Ny,Nz));
+  yZ = gcoeff(y,1,1);
+  y = idealmul_HNF(nf,x, idealinv_HNF_aux(nf,y));
+  return gerepileupto(av, RgM_Rg_div(y, yZ));
+}
+
+GEN
+idealintersect(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long lz, lx, i;
+  GEN z, dx, dy, xZ, yZ;;
+
+  nf = checknf(nf);
+  x = idealhnf_shallow(nf,x);
+  y = idealhnf_shallow(nf,y);
+  if (lg(x) == 1 || lg(y) == 1) { avma = av; return cgetg(1,t_MAT); }
+  x = Q_remove_denom(x, &dx);
+  y = Q_remove_denom(y, &dy);
+  if (dx) y = ZM_Z_mul(y, dx);
+  if (dy) x = ZM_Z_mul(x, dy);
+  xZ = gcoeff(x,1,1);
+  yZ = gcoeff(y,1,1);
+  dx = mul_denom(dx,dy);
+  z = ZM_lll(shallowconcat(x,y), 0.99, LLL_KER); lz = lg(z);
+  lx = lg(x);
+  for (i=1; i<lz; i++) setlg(z[i], lx);
+  z = ZM_hnfmodid(ZM_mul(x,z), lcmii(xZ, yZ));
+  if (dx) z = RgM_Rg_div(z,dx);
+  return gerepileupto(av,z);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                      T2-IDEAL REDUCTION                         */
+/*                                                                 */
+/*******************************************************************/
+
+static GEN
+chk_vdir(GEN nf, GEN vdir)
+{
+  long i, t, l = lg(vdir);
+  GEN v;
+  if (l != lg(nf_get_roots(nf))) pari_err_DIM("idealred");
+  t = typ(vdir);
+  if (t == t_VECSMALL) return vdir;
+  if (t != t_VEC) pari_err_TYPE("idealred",vdir);
+  v = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) v[i] = itos(gceil(gel(vdir,i)));
+  return v;
+}
+
+static void
+twistG(GEN G, long r1, long i, long v)
+{
+  long j, lG = lg(G);
+  if (i <= r1) {
+    for (j=1; j<lG; j++) gcoeff(G,i,j) = gmul2n(gcoeff(G,i,j), v);
+  } else {
+    long k = (i<<1) - r1;
+    for (j=1; j<lG; j++)
+    {
+      gcoeff(G,k-1,j) = gmul2n(gcoeff(G,k-1,j), v);
+      gcoeff(G,k  ,j) = gmul2n(gcoeff(G,k  ,j), v);
+    }
+  }
+}
+
+GEN
+nf_get_Gtwist(GEN nf, GEN vdir)
+{
+  long i, l, v, r1;
+  GEN G;
+
+  vdir = chk_vdir(nf, vdir);
+  G = RgM_shallowcopy(nf_get_G(nf));
+  r1 = nf_get_r1(nf);
+  l = lg(vdir);
+  for (i=1; i<l; i++)
+  {
+    v = vdir[i]; if (!v) continue;
+    twistG(G, r1, i, v);
+  }
+  return RM_round_maxrank(G);
+}
+GEN
+nf_get_Gtwist1(GEN nf, long i)
+{
+  GEN G = RgM_shallowcopy( nf_get_G(nf) );
+  long r1 = nf_get_r1(nf);
+  twistG(G, r1, i, 10);
+  return RM_round_maxrank(G);
+}
+
+GEN
+RM_round_maxrank(GEN G0)
+{
+  long e, r = lg(G0)-1;
+  pari_sp av = avma;
+  GEN G = G0;
+  for (e = 4; ; e <<= 1)
+  {
+    GEN H = ground(G);
+    if (ZM_rank(H) == r) return H; /* maximal rank ? */
+    avma = av;
+    G = gmul2n(G0, e);
+  }
+}
+
+GEN
+idealred0(GEN nf, GEN I, GEN vdir)
+{
+  pari_sp av = avma;
+  long N, i;
+  GEN G, J, aI, y, x, T, b, c1, c, pol;
+
+  nf = checknf(nf);
+  pol = nf_get_pol(nf); N = degpol(pol);
+  T = x = c = c1 = NULL;
+  switch (idealtyp(&I,&aI))
+  {
+    case id_PRINCIPAL:
+      if (gequal0(I)) I = cgetg(1,t_MAT); else { c1 = I; I = matid(N); }
+      if (!aI) return I;
+      goto END;
+    case id_PRIME:
+      if (pr_is_inert(I)) {
+        c1 = gel(I,1); I = matid(N);
+        if (!aI) return I;
+        goto END;
+      }
+      I = idealhnf_two(nf,I);
+      break;
+    case id_MAT:
+      I = Q_primitive_part(I, &c1);
+  }
+  if (!vdir)
+    G = nf_get_roundG(nf);
+  else if (typ(vdir) == t_MAT)
+    G = vdir;
+  else
+    G = nf_get_Gtwist(nf, vdir);
+  y = idealpseudomin(I, G);
+
+  if (ZV_isscalar(y))
+  { /* already reduced */
+    if (!aI) return gerepilecopy(av, I);
+    goto END;
+  }
+
+  x = coltoliftalg(nf, y); /* algebraic integer */
+  b = Q_remove_denom(QXQ_inv(x,pol), &T);
+  b = poltobasis(nf,b);
+  if (T)
+  {
+    GEN T2; b = Q_primitive_part(b, &T2);
+    if (T2) { T = diviiexact(T, T2); if (is_pm1(T)) T = NULL; }
+  }
+  /* b = T x^(-1), T rat. integer, minimal such that b alg. integer */
+  if (!T) /* x is a unit, I already reduced */
+  {
+    if (!aI) return gerepilecopy(av, I);
+    goto END;
+  }
+
+  b = zk_multable(nf,b);
+  J = cgetg(N+1,t_MAT); /* = I T/ x integral */
+  for (i=1; i<=N; i++) gel(J,i) = ZM_ZC_mul(b, gel(I,i));
+  J = Q_primitive_part(J, &c);
+ /* c = content (I T / x) = T / den(I/x) --> d = den(I/x) = T / c
+  * J = (d I / x); I[1,1] = I \cap Z --> d I[1,1] belongs to J and Z */
+  I = ZM_hnfmodid(J, mulii(gcoeff(I,1,1), c? diviiexact(T,c): T));
+  if (!aI) return gerepileupto(av, I);
+
+  c = mul_content(c,c1);
+  y = c? gmul(y, gdiv(c,T)): gdiv(y, T);
+  aI = ext_mul(nf, aI,y);
+  return gerepilecopy(av, mkvec2(I, aI));
+
+END:
+  if (c1) aI = ext_mul(nf, aI,c1);
+  return gerepilecopy(av, mkvec2(I, aI));
+}
+
+GEN
+idealmin(GEN nf, GEN x, GEN vdir)
+{
+  pari_sp av = avma;
+  GEN y, dx;
+  nf = checknf(nf);
+  switch( idealtyp(&x,&y) )
+  {
+    case id_PRINCIPAL: return gcopy(x);
+    case id_PRIME: x = idealhnf_two(nf,x); break;
+    case id_MAT: if (lg(x) == 1) return gen_0;
+  }
+  x = Q_remove_denom(x, &dx);
+  y = idealpseudomin(x, vdir? nf_get_Gtwist(nf,vdir): nf_get_roundG(nf));
+  if (dx) y = RgC_Rg_div(y, dx);
+  return gerepileupto(av, y);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                   APPROXIMATION THEOREM                         */
+/*                                                                 */
+/*******************************************************************/
+
+/* write x = x1 x2, x2 maximal s.t. (x2,f) = 1, return x2 */
+GEN
+coprime_part(GEN x, GEN f)
+{
+  for (;;)
+  {
+    f = gcdii(x, f); if (is_pm1(f)) break;
+    x = diviiexact(x, f);
+  }
+  return x;
+}
+/* write x = x1 x2, x2 maximal s.t. (x2,f) = 1, return x2 */
+ulong
+ucoprime_part(ulong x, ulong f)
+{
+  for (;;)
+  {
+    f = ugcd(x, f); if (f == 1) break;
+    x /= f;
+  }
+  return x;
+}
+
+/* x t_INT, f ideal. Write x = x1 x2, sqf(x1) | f, (x2,f) = 1. Return x2 */
+static GEN
+nf_coprime_part(GEN nf, GEN x, GEN listpr)
+{
+  long v, j, lp = lg(listpr), N = nf_get_degree(nf);
+  GEN x1, x2, ex;
+
+#if 0 /*1) via many gcds. Expensive ! */
+  GEN f = idealprodprime(nf, listpr);
+  f = ZM_hnfmodid(f, x); /* first gcd is less expensive since x in Z */
+  x = scalarmat(x, N);
+  for (;;)
+  {
+    if (gequal1(gcoeff(f,1,1))) break;
+    x = idealdivexact(nf, x, f);
+    f = ZM_hnfmodid(shallowconcat(f,x), gcoeff(x,1,1)); /* gcd(f,x) */
+  }
+  x2 = x;
+#else /*2) from prime decomposition */
+  x1 = NULL;
+  for (j=1; j<lp; j++)
+  {
+    GEN pr = gel(listpr,j);
+    v = Z_pval(x, pr_get_p(pr)); if (!v) continue;
+
+    ex = muluu(v, pr_get_e(pr)); /* = v_pr(x) > 0 */
+    x1 = x1? idealmulpowprime(nf, x1, pr, ex)
+           : idealpow(nf, pr, ex);
+  }
+  x = scalarmat(x, N);
+  x2 = x1? idealdivexact(nf, x, x1): x;
+#endif
+  return x2;
+}
+
+/* L0 in K^*, assume (L0,f) = 1. Return L integral, L0 = L mod f  */
+GEN
+make_integral(GEN nf, GEN L0, GEN f, GEN listpr)
+{
+  GEN fZ, t, L, D2, d1, d2, d;
+
+  L = Q_remove_denom(L0, &d);
+  if (!d) return L0;
+
+  /* L0 = L / d, L integral */
+  fZ = gcoeff(f,1,1);
+  if (typ(L) == t_INT) return Fp_mul(L, Fp_inv(d, fZ), fZ);
+  /* Kill denom part coprime to fZ */
+  d2 = coprime_part(d, fZ);
+  t = Fp_inv(d2, fZ); if (!is_pm1(t)) L = ZC_Z_mul(L,t);
+  if (equalii(d, d2)) return L;
+
+  d1 = diviiexact(d, d2);
+  /* L0 = (L / d1) mod f. d1 not coprime to f
+   * write (d1) = D1 D2, D2 minimal, (D2,f) = 1. */
+  D2 = nf_coprime_part(nf, d1, listpr);
+  t = idealaddtoone_i(nf, D2, f); /* in D2, 1 mod f */
+  L = nfmuli(nf,t,L);
+
+  /* if (L0, f) = 1, then L in D1 ==> in D1 D2 = (d1) */
+  return Q_div_to_int(L, d1); /* exact division */
+}
+
+/* assume L is a list of prime ideals. Return the product */
+GEN
+idealprodprime(GEN nf, GEN L)
+{
+  long l = lg(L), i;
+  GEN z;
+
+  if (l == 1) return matid(nf_get_degree(nf));
+  z = idealhnf_two(nf, gel(L,1));
+  for (i=2; i<l; i++) z = idealmul_HNF_two(nf,z, gel(L,i));
+  return z;
+}
+
+/* assume L is a list of prime ideals. Return prod L[i]^e[i] */
+GEN
+factorbackprime(GEN nf, GEN L, GEN e)
+{
+  long l = lg(L), i;
+  GEN z;
+
+  if (l == 1) return matid(nf_get_degree(nf));
+  z = idealpow(nf, gel(L,1), gel(e,1));
+  for (i=2; i<l; i++)
+    if (signe(gel(e,i))) z = idealmulpowprime(nf,z, gel(L,i),gel(e,i));
+  return z;
+}
+
+/* F in Z squarefree, multiple of p. Return F-uniformizer for pr/p */
+GEN
+unif_mod_fZ(GEN pr, GEN F)
+{
+  GEN p = pr_get_p(pr), t = pr_get_gen(pr);
+  if (!equalii(F, p))
+  {
+    GEN u, v, q, a = diviiexact(F,p);
+    q = (pr_get_e(pr) == 1)? sqri(p): p;
+    if (!gequal1(bezout(q, a, &u,&v))) pari_err_BUG("unif_mod_fZ");
+    u = mulii(u,q);
+    v = mulii(v,a);
+    t = ZC_Z_mul(t, v);
+    gel(t,1) = addii(gel(t,1), u); /* return u + vt */
+  }
+  return t;
+}
+/* L = list of prime ideals, return lcm_i (L[i] \cap \ZM) */
+GEN
+init_unif_mod_fZ(GEN L)
+{
+  long i, r = lg(L);
+  GEN pr, p, F = gen_1;
+  for (i = 1; i < r; i++)
+  {
+    pr = gel(L,i); p = pr_get_p(pr);
+    if (!dvdii(F, p)) F = mulii(F,p);
+  }
+  return F;
+}
+
+void
+check_listpr(GEN x)
+{
+  long l = lg(x), i;
+  for (i=1; i<l; i++) checkprid(gel(x,i));
+}
+
+/* Given a prime ideal factorization with possibly zero or negative
+ * exponents, gives b such that v_p(b) = v_p(x) for all prime ideals pr | x
+ * and v_pr(b)> = 0 for all other pr.
+ * For optimal performance, all [anti-]uniformizers should be precomputed,
+ * but no support for this yet.
+ *
+ * If nored, do not reduce result.
+ * No garbage collecting */
+static GEN
+idealapprfact_i(GEN nf, GEN x, int nored)
+{
+  GEN z, d, L, e, e2, F;
+  long i, r;
+  int flagden;
+
+  nf = checknf(nf);
+  L = gel(x,1);
+  e = gel(x,2);
+  F = init_unif_mod_fZ(L);
+  flagden = 0;
+  z = NULL; r = lg(e);
+  for (i = 1; i < r; i++)
+  {
+    long s = signe(gel(e,i));
+    GEN pi, q;
+    if (!s) continue;
+    if (s < 0) flagden = 1;
+    pi = unif_mod_fZ(gel(L,i), F);
+    q = nfpow(nf, pi, gel(e,i));
+    z = z? nfmul(nf, z, q): q;
+  }
+  if (!z) return scalarcol_shallow(gen_1, nf_get_degree(nf));
+  if (nored)
+  {
+    if (flagden) pari_err_IMPL("nored + denominator in idealapprfact");
+    return z;
+  }
+  e2 = cgetg(r, t_VEC);
+  for (i=1; i<r; i++) gel(e2,i) = addis(gel(e,i), 1);
+  x = factorbackprime(nf, L,e2);
+  if (flagden) /* denominator */
+  {
+    z = Q_remove_denom(z, &d);
+    d = diviiexact(d, coprime_part(d, F));
+    x = RgM_Rg_mul(x, d);
+  }
+  else
+    d = NULL;
+  z = ZC_reducemodlll(z, x);
+  return d? RgC_Rg_div(z,d): z;
+}
+
+GEN
+idealapprfact(GEN nf, GEN x) {
+  pari_sp av = avma;
+  if (typ(x) != t_MAT || lg(x) != 3)
+    pari_err_TYPE("idealapprfact [not a factorization]",x);
+  check_listpr(gel(x,1));
+  return gerepileupto(av, idealapprfact_i(nf, x, 0));
+}
+
+GEN
+idealappr(GEN nf, GEN x) {
+  pari_sp av = avma;
+  return gerepileupto(av, idealapprfact_i(nf, idealfactor(nf, x), 0));
+}
+
+GEN
+idealappr0(GEN nf, GEN x, long fl) {
+  return fl? idealapprfact(nf, x): idealappr(nf, x);
+}
+
+/* merge a^e b^f. Assume a and b sorted. Keep 0 exponents */
+static void
+merge_fact(GEN *pa, GEN *pe, GEN b, GEN f)
+{
+  GEN A, E, a = *pa, e = *pe;
+  long k, i, la = lg(a), lb = lg(b), l = la+lb-1;
+
+  A = cgetg(l, t_COL);
+  E = cgetg(l, t_COL);
+  k = 1;
+  for (i=1; i<la; i++)
+  {
+    A[i] = a[i];
+    E[i] = e[i];
+    if (k < lb && gequal(gel(A,i), gel(b,k)))
+    {
+      gel(E,i) = addii(gel(E,i), gel(f,k));
+      k++;
+    }
+  }
+  for (; k < lb; i++,k++)
+  {
+    A[i] = b[k];
+    E[i] = f[k];
+  }
+  setlg(A, i); *pa = A;
+  setlg(E, i); *pe = E;
+}
+
+/* Given a prime ideal factorization x with possibly zero or negative exponents,
+ * and a vector w of elements of nf, gives a b such that
+ * v_p(b-w_p)>=v_p(x) for all prime ideals p in the ideal factorization
+ * and v_p(b)>=0 for all other p, using the (standard) proof given in GTM 138.
+ * Certainly not the most efficient, but sure. */
+GEN
+idealchinese(GEN nf, GEN x, GEN w)
+{
+  pari_sp av = avma;
+  long ty = typ(w), i, N, r;
+  GEN y, L, e, F, s, den;
+
+  nf = checknf(nf); N = nf_get_degree(nf);
+  if (typ(x) != t_MAT || lg(x) != 3)
+    pari_err_TYPE("idealchinese [not a factorization]",x);
+  L = gel(x,1); r = lg(L);
+  e = gel(x,2);
+  if (!is_vec_t(ty) || lg(w) != r) pari_err_TYPE("idealchinese",w);
+  if (r == 1) return scalarcol_shallow(gen_1,N);
+
+  w = Q_remove_denom(matalgtobasis(nf,w), &den);
+  if (den)
+  {
+    GEN p = gen_indexsort(L, (void*)&cmp_prime_ideal, cmp_nodata);
+    GEN fa = idealfactor(nf, den); /* sorted */
+    L = vecpermute(L, p);
+    e = vecpermute(e, p);
+    w = vecpermute(w, p); settyp(w, t_VEC); /* make sure typ = t_VEC */
+    merge_fact(&L, &e, gel(fa,1), gel(fa,2));
+    i = lg(L);
+    w = shallowconcat(w, zerovec(i - r));
+    r = i;
+  }
+  else
+    e = leafcopy(e); /* do not destroy x[2] */
+  for (i=1; i<r; i++)
+    if (signe(gel(e,i)) < 0) gel(e,i) = gen_0;
+
+  F = factorbackprime(nf, L, e);
+  s = NULL;
+  for (i=1; i<r; i++)
+  {
+    GEN u, t;
+    if (gequal0(gel(w,i))) continue;
+    t = idealdivpowprime(nf,F, gel(L,i), gel(e,i));
+    u = hnfmerge_get_1(t, idealpow(nf, gel(L,i), gel(e,i)));
+    if (!u) pari_err_COPRIME("idealchinese", t,gel(L,i));
+    t = nfmuli(nf, u, gel(w,i));
+    s = s? ZC_add(s,t): t;
+  }
+  if (!s) { avma = av; return zerocol(N); }
+  y = ZC_reducemodlll(s, F);
+  return gerepileupto(av, den? RgC_Rg_div(y,den): y);
+}
+
+static GEN
+mat_ideal_two_elt2(GEN nf, GEN x, GEN a)
+{
+  GEN L, e, fact = idealfactor(nf,a);
+  long i, r;
+  L = gel(fact,1);
+  e = gel(fact,2); r = lg(e);
+  for (i=1; i<r; i++) gel(e,i) = stoi( idealval(nf,x,gel(L,i)) );
+  return idealapprfact_i(nf,fact,1);
+}
+
+static void
+not_in_ideal(GEN a) {
+  pari_err_DOMAIN("idealtwoelt2","element mod ideal", "!=", gen_0, a);
+}
+
+/* Given an integral ideal x and a in x, gives a b such that
+ * x = aZ_K + bZ_K using the approximation theorem */
+GEN
+idealtwoelt2(GEN nf, GEN x, GEN a)
+{
+  pari_sp av = avma;
+  GEN cx, b, mod;
+
+  nf = checknf(nf);
+  a = nf_to_scalar_or_basis(nf, a);
+  x = idealhnf_shallow(nf,x);
+  if (lg(x) == 1)
+  {
+    if (!isintzero(a)) not_in_ideal(a);
+    avma = av; return zerocol(nf_get_degree(nf));
+  }
+  x = Q_primitive_part(x, &cx);
+  if (cx) a = gdiv(a, cx);
+  if (typ(a) != t_COL)
+  { /* rational number */
+    if (typ(a) != t_INT || !dvdii(a, gcoeff(x,1,1))) not_in_ideal(a);
+    mod = NULL;
+  }
+  else
+  {
+    if (!hnf_invimage(x, a)) not_in_ideal(a);
+    mod = idealhnf_principal(nf, a);
+  }
+  b = mat_ideal_two_elt2(nf, x, a);
+  b = mod? ZC_hnfrem(b, mod): centermod(b, a);
+  b = cx? RgC_Rg_mul(b,cx): gcopy(b);
+  return gerepileupto(av, b);
+}
+
+/* Given 2 integral ideals x and y in nf, returns a beta in nf such that
+ * beta * x is an integral ideal coprime to y */
+GEN
+idealcoprimefact(GEN nf, GEN x, GEN fy)
+{
+  GEN L = gel(fy,1), e;
+  long i, r = lg(L);
+
+  e = cgetg(r, t_COL);
+  for (i=1; i<r; i++) gel(e,i) = stoi( -idealval(nf,x,gel(L,i)) );
+  return idealapprfact_i(nf, mkmat2(L,e), 0);
+}
+GEN
+idealcoprime(GEN nf, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, idealcoprimefact(nf, x, idealfactor(nf,y)));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                  LINEAR ALGEBRA OVER Z_K  (HNF,SNF)             */
+/*                                                                 */
+/*******************************************************************/
+/* A torsion-free module M over Z_K is given by [A,I].
+ * I=[a_1,...,a_k] is a row vector of k fractional ideals given in HNF.
+ * A is an n x k matrix (same k) such that if A_j is the j-th column of A then
+ * M=a_1 A_1+...+a_k A_k. We say that [A,I] is a pseudo-basis if k=n */
+
+/* Given an element x and an ideal I in HNF, gives an r such that x-r is in H
+ * and r is small */
+GEN
+nfreduce(GEN nf, GEN x, GEN I)
+{
+  pari_sp av = avma;
+  GEN aI;
+  x = nf_to_scalar_or_basis(checknf(nf), x);
+  if (idealtyp(&I,&aI) != id_MAT || lg(I)==1) pari_err_TYPE("nfreduce",I);
+  if (typ(x) != t_COL) x = scalarcol( gmod(x, gcoeff(I,1,1)), lg(I)-1 );
+  else x = reducemodinvertible(x, I);
+  return gerepileupto(av, x);
+}
+/* Given an element x and an ideal in HNF, gives an a in ideal such that
+ * x-a is small. No checks */
+static GEN
+element_close(GEN nf, GEN x, GEN ideal)
+{
+  pari_sp av = avma;
+  GEN y = gcoeff(ideal,1,1);
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(y) == t_INT && is_pm1(y)) return ground(x);
+  if (typ(x) == t_COL)
+    x = closemodinvertible(x, ideal);
+  else
+    x = gmul(y, gdivround(x,y));
+  return gerepileupto(av, x);
+}
+
+/* A + v B */
+static GEN
+colcomb1(GEN nf, GEN v, GEN A, GEN B)
+{
+  if (isintzero(v)) return A;
+  return RgC_to_nfC(nf, RgC_add(A, nfC_nf_mul(nf,B,v)));
+}
+/* u A + v B */
+static GEN
+colcomb(GEN nf, GEN u, GEN v, GEN A, GEN B)
+{
+  if (isintzero(u)) return nfC_nf_mul(nf,B,v);
+  if (u != gen_1) A = nfC_nf_mul(nf,A,u);
+  return colcomb1(nf, v, A, B);
+}
+
+/* return m[i,1..lim] * x */
+static GEN
+element_mulvecrow(GEN nf, GEN x, GEN m, long i, long lim)
+{
+  long j, l = minss(lg(m), lim+1);
+  GEN dx, y = cgetg(l, t_VEC);
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) == t_COL)
+  {
+    x = zk_multable(nf, Q_remove_denom(x, &dx));
+    for (j=1; j<l; j++)
+    {
+      GEN t = gcoeff(m,i,j);
+      if (!isintzero(t))
+      {
+        if (typ(t) == t_COL)
+          t = RgM_RgC_mul(x, t);
+        else
+          t = RgC_Rg_mul(gel(x,1), t);
+        if (dx) t = gdiv(t, dx);
+        t = nf_to_scalar_or_basis(nf,t);
+      }
+      gel(y,j) = t;
+    }
+  }
+  else
+  {
+    for (j=1; j<l; j++) gel(y,j) = gmul(x, gcoeff(m,i,j));
+  }
+  return y;
+}
+
+/* u Z[s,] + v Z[t,], limitied to the first lim entries */
+static GEN
+rowcomb(GEN nf, GEN u, GEN v, long s, long t, GEN Z, long lim)
+{
+  GEN z;
+  if (gequal0(u))
+    z = element_mulvecrow(nf,v,Z,t, lim);
+  else
+  {
+    z = element_mulvecrow(nf,u,Z,s, lim);
+    if (!gequal0(v)) z = gadd(z, element_mulvecrow(nf,v,Z,t, lim));
+  }
+  return z;
+}
+
+/* nfbezout(0,b,A,B). Either bB = NULL or b*B */
+static GEN
+zero_nfbezout(GEN nf,GEN bB, GEN b, GEN A,GEN B,GEN *u,GEN *v,GEN *w,GEN *di)
+{
+  GEN d;
+  if (isint1(b))
+  {
+    *v = gen_1;
+    *w = A;
+    d = B;
+    *di = idealinv(nf,d);
+  }
+  else
+  {
+    *v = nfinv(nf,b);
+    *w = idealmul(nf,A,*v);
+    d = bB? bB: idealmul(nf,b,B);
+    *di = idealinv_HNF(nf,d);
+  }
+  *u = gen_0; return d;
+}
+
+/* Given elements a,b and ideals A, B, outputs d = a.A+b.B and gives
+ * di=d^-1, w=A.B.di, u, v such that au+bv=1 and u in A.di, v in B.di.
+ * Assume A, B non-zero, but a or b can be zero (not both) */
+static GEN
+nfbezout(GEN nf,GEN a,GEN b, GEN A,GEN B, GEN *pu,GEN *pv,GEN *pw,GEN *pdi)
+{
+  GEN w, u,v,uv, d, di, aA, bB;
+
+  if (isintzero(a)) return zero_nfbezout(nf,NULL,b,A,B,pu,pv,pw,pdi);
+  if (isintzero(b)) return zero_nfbezout(nf,NULL,a,B,A,pv,pu,pw,pdi);
+
+  if (a != gen_1) /* frequently called with a = gen_1 */
+  {
+    a = nf_to_scalar_or_basis(nf,a);
+    if (isint1(a)) a = gen_1;
+  }
+  aA = (a == gen_1)? A: idealmul(nf,a,A);
+  bB = idealmul(nf,b,B);
+  d = idealadd(nf,aA,bB);
+  if (gequal(aA, d)) return zero_nfbezout(nf,aA, a,B,A,pv,pu,pw,pdi);
+  if (gequal(bB, d)) return zero_nfbezout(nf,bB, b,A,B,pu,pv,pw,pdi);
+  /* general case is slow */
+  di = idealinv_HNF(nf,d);
+  w = idealmul(nf,aA,di); /* integral */
+  uv = idealaddtoone(nf, w, idealmul(nf,bB,di));
+  w = idealmul(nf,w,B);
+  u = gel(uv,1);
+  v = nfdiv(nf,gel(uv,2),b);
+  if (a != gen_1)
+  {
+    GEN inva = nfinv(nf, a);
+    u =  nfmul(nf,u,inva);
+    w = idealmul(nf, inva, w); /* AB/d */
+  }
+  *pu = u;
+  *pv = v;
+  *pw = w;
+  *pdi = di; return d;
+}
+/* v a vector of ideals, simplify in place the ones generated by elts of Q */
+static void
+idV_simplify(GEN v)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    GEN M = gel(v,i);
+    if (typ(M)==t_MAT && RgM_isscalar(M,NULL)) gel(v,i) = Q_abs(gcoeff(M,1,1));
+  }
+}
+/* Given a torsion-free module x outputs a pseudo-basis for x in HNF */
+GEN
+nfhnf(GEN nf, GEN x)
+{
+  long i, j, def, idef, m, n;
+  pari_sp av0 = avma, av, lim;
+  GEN y, A, I, J;
+
+  nf = checknf(nf);
+  check_ZKmodule(x, "nfhnf");
+  A = gel(x,1); RgM_dimensions(A, &m, &n);
+  I = gel(x,2);
+  if (!n) return gcopy(x);
+  idef = (n < m)? m-n : 0;
+  av = avma; lim = stack_lim(av, 2);
+  A = RgM_to_nfM(nf,A);
+  I = leafcopy(I);
+  J = zerovec(n); def = n;
+  for (i=m; i>idef; i--)
+  {
+    GEN d, di = NULL;
+
+    j=def; while (j>=1 && isintzero(gcoeff(A,i,j))) j--;
+    if (!j)
+    { /* no pivot on line i */
+      if (idef) idef--;
+      continue;
+    }
+    if (j==def) j--;
+    else {
+      swap(gel(A,j), gel(A,def));
+      swap(gel(I,j), gel(I,def));
+    }
+    for (  ; j; j--)
+    {
+      GEN a,b, u,v,w, S, T, S0, T0 = gel(A,j);
+      b = gel(T0,i); if (isintzero(b)) continue;
+
+      S0 = gel(A,def); a = gel(S0,i);
+      d = nfbezout(nf, a,b, gel(I,def),gel(I,j), &u,&v,&w,&di);
+      S = colcomb(nf, u,v, S0,T0);
+      T = colcomb(nf, a,gneg(b), T0,S0);
+      gel(A,def) = S; gel(A,j) = T;
+      gel(I,def) = d; gel(I,j) = w;
+    }
+    y = gcoeff(A,i,def);
+    if (!isint1(y))
+    {
+      gel(A,def) = nfC_nf_mul(nf, gel(A,def), nfinv(nf,y));
+      gel(I,def) = idealmul(nf, y, gel(I,def));
+      di = NULL;
+    }
+    if (!di) di = idealinv(nf,gel(I,def));
+    d = gel(I,def);
+    gel(J,def) = di;
+    for (j=def+1; j<=n; j++)
+    {
+      GEN c = gcoeff(A,i,j); if (isintzero(c)) continue;
+      c = element_close(nf, c, idealmul(nf,d,gel(J,j)));
+      gel(A,j) = colcomb1(nf, gneg(c), gel(A,j),gel(A,def));
+    }
+    def--;
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"nfhnf, i = %ld", i);
+      gerepileall(av,3, &A,&I,&J);
+    }
+  }
+  n -= def;
+  A += def; A[0] = evaltyp(t_MAT)|evallg(n+1);
+  I += def; I[0] = evaltyp(t_VEC)|evallg(n+1);
+  idV_simplify(I);
+  return gerepilecopy(av0, mkvec2(A, I));
+}
+
+static long
+RgV_find_denom(GEN x)
+{
+  long l = lg(x), i = 1;
+  while (i < l && Q_denom(gel(x,i)) == gen_1) i++;
+  return i;
+}
+/* A torsion module M over Z_K will be given by a row vector [A,I,J] with
+ * three components. I=[b_1,...,b_n] is a row vector of n fractional ideals
+ * given in HNF, J=[a_1,...,a_n] is a row vector of n fractional ideals in
+ * HNF. A is an nxn matrix (same n) such that if A_j is the j-th column of A
+ * and e_n is the canonical basis of K^n, then
+ * M=(b_1e_1+...+b_ne_n)/(a_1A_1+...a_nA_n) */
+
+/* x=[A,I,J] a torsion module as above. Output the
+ * smith normal form as K=[c_1,...,c_n] such that x = Z_K/c_1+...+Z_K/c_n */
+GEN
+nfsnf(GEN nf, GEN x)
+{
+  long i, j, k, l, c, n, m, N;
+  pari_sp av, lim;
+  GEN z,u,v,w,d,dinv,A,I,J;
+
+  nf = checknf(nf); N = nf_get_degree(nf);
+  if (typ(x)!=t_VEC || lg(x)!=4) pari_err_TYPE("nfsnf",x);
+  A = gel(x,1);
+  I = gel(x,2);
+  J = gel(x,3);
+  if (typ(A)!=t_MAT) pari_err_TYPE("nfsnf",A);
+  n = lg(A)-1;
+  if (typ(I)!=t_VEC) pari_err_TYPE("nfsnf",I);
+  if (typ(J)!=t_VEC) pari_err_TYPE("nfsnf",J);
+  if (lg(I)!=n+1 || lg(J)!=n+1) pari_err_DIM("nfsnf");
+  RgM_dimensions(A, &m, &n);
+  if (!n || n != m) pari_err_IMPL("nfsnf for empty or non square matrices");
+
+  av = avma; lim = stack_lim(av,1);
+  A = RgM_to_nfM(nf, A);
+  I = leafcopy(I);
+  J = leafcopy(J); for (i = 1; i <= n; i++) gel(J,i) = idealinv(nf, gel(J,i));
+  for (i=n; i>=2; i--)
+  {
+    do
+    {
+      GEN Aii, a, b, db;
+      c = 0;
+      for (j=i-1; j>=1; j--)
+      {
+        GEN S, T, S0, T0 = gel(A,j);
+        b = gel(T0,i); if (gequal0(b)) continue;
+
+        S0 = gel(A,i); a = gel(S0,i);
+        d = nfbezout(nf, a,b, gel(J,i),gel(J,j), &u,&v,&w,&dinv);
+        S = colcomb(nf, u,v, S0,T0);
+        T = colcomb(nf, a,gneg(b), T0,S0);
+        gel(A,i) = S; gel(A,j) = T;
+        gel(J,i) = d; gel(J,j) = w;
+      }
+      for (j=i-1; j>=1; j--)
+      {
+        GEN ri, rj;
+        b = gcoeff(A,j,i); if (gequal0(b)) continue;
+
+        a = gcoeff(A,i,i);
+        d = nfbezout(nf, a,b, gel(I,i),gel(I,j), &u,&v,&w,&dinv);
+        ri = rowcomb(nf, u,v,       i,j, A, i);
+        rj = rowcomb(nf, a,gneg(b), j,i, A, i);
+        for (k=1; k<=i; k++) {
+          gcoeff(A,j,k) = gel(rj,k);
+          gcoeff(A,i,k) = gel(ri,k);
+        }
+        gel(I,i) = d; gel(I,j) = w; c = 1;
+      }
+      if (c) continue;
+
+      Aii = gcoeff(A,i,i); if (gequal0(Aii)) break;
+      gel(J,i) = idealmul(nf, gel(J,i), Aii);
+      gcoeff(A,i,i) = gen_1;
+      b = idealmul(nf,gel(J,i),gel(I,i));
+      b = Q_remove_denom(b, &db);
+      for (k=1; k<i; k++)
+        for (l=1; l<i; l++)
+        {
+          GEN D, p1, p2, p3, Akl = gcoeff(A,k,l);
+          if (gequal0(Akl)) continue;
+
+          p1 = idealmul(nf,Akl,gel(J,l));
+          p3 = idealmul(nf, p1, gel(I,k));
+          if (db) p3 = RgM_Rg_mul(p3, db);
+          if (RgM_is_ZM(p3) && hnfdivide(b, p3)) continue;
+
+          /* find d in D = I[k]/I[i] not in J[i]/(a[k,l] J[l]) */
+          D = idealdiv(nf,gel(I,k),gel(I,i));
+          p2 = idealdiv(nf,gel(J,i), p1);
+          l = RgV_find_denom( RgM_solve(p2, D) );
+          if (l>N) pari_err_BUG("nfsnf");
+          p1 = element_mulvecrow(nf,gel(D,l),A,k,i);
+          for (l=1; l<=i; l++) gcoeff(A,i,l) = gadd(gcoeff(A,i,l),gel(p1,l));
+
+          k = i; c = 1; break;
+        }
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"nfsnf");
+        gerepileall(av,3, &A,&I,&J);
+      }
+    }
+    while (c);
+  }
+  gel(J,1) = idealmul(nf, gcoeff(A,1,1), gel(J,1));
+  z = cgetg(n+1,t_VEC);
+  for (i=1; i<=n; i++) gel(z,i) = idealmul(nf,gel(I,i),gel(J,i));
+  return gerepileupto(av, z);
+}
+
+GEN
+nfmulmodpr(GEN nf, GEN x, GEN y, GEN modpr)
+{
+  pari_sp av = avma;
+  GEN z, p, pr = modpr, T;
+
+  nf = checknf(nf); modpr = nf_to_Fq_init(nf,&pr,&T,&p);
+  x = nf_to_Fq(nf,x,modpr);
+  y = nf_to_Fq(nf,y,modpr);
+  z = Fq_mul(x,y,T,p);
+  return gerepileupto(av, algtobasis(nf, Fq_to_nf(z,modpr)));
+}
+
+GEN
+nfdivmodpr(GEN nf, GEN x, GEN y, GEN modpr)
+{
+  pari_sp av = avma;
+  nf = checknf(nf);
+  return gerepileupto(av, nfreducemodpr(nf, nfdiv(nf,x,y), modpr));
+}
+
+GEN
+nfpowmodpr(GEN nf, GEN x, GEN k, GEN modpr)
+{
+  pari_sp av=avma;
+  GEN z, T, p, pr = modpr;
+
+  nf = checknf(nf); modpr = nf_to_Fq_init(nf,&pr,&T,&p);
+  z = nf_to_Fq(nf,x,modpr);
+  z = Fq_pow(z,k,T,p);
+  return gerepileupto(av, algtobasis(nf, Fq_to_nf(z,modpr)));
+}
+
+GEN
+nfkermodpr(GEN nf, GEN x, GEN modpr)
+{
+  pari_sp av = avma;
+  GEN T, p, pr = modpr;
+
+  nf = checknf(nf); modpr = nf_to_Fq_init(nf, &pr,&T,&p);
+  if (typ(x)!=t_MAT) pari_err_TYPE("nfkermodpr",x);
+  x = nfM_to_FqM(x, nf, modpr);
+  return gerepilecopy(av, FqM_to_nfM(FqM_ker(x,T,p), modpr));
+}
+
+GEN
+nfsolvemodpr(GEN nf, GEN a, GEN b, GEN pr)
+{
+  const char *f = "nfsolvemodpr";
+  pari_sp av = avma;
+  GEN T, p, modpr;
+
+  nf = checknf(nf);
+  modpr = nf_to_Fq_init(nf, &pr,&T,&p);
+  if (typ(a)!=t_MAT) pari_err_TYPE(f,a);
+  a = nfM_to_FqM(a, nf, modpr);
+  switch(typ(b))
+  {
+    case t_MAT:
+      b = nfM_to_FqM(b, nf, modpr);
+      b = FqM_gauss(a,b,T,p);
+      if (!b) pari_err_INV(f,a);
+      a = FqM_to_nfM(b, modpr);
+      break;
+    case t_COL:
+      b = nfV_to_FqV(b, nf, modpr);
+      b = FqM_FqC_gauss(a,b,T,p);
+      if (!b) pari_err_INV(f,a);
+      a = FqV_to_nfV(b, modpr);
+      break;
+    default: pari_err_TYPE(f,b);
+  }
+  return gerepilecopy(av, a);
+}
+
+/* Given a pseudo-basis x, outputs a multiple of its ideal determinant */
+GEN
+nfdetint(GEN nf, GEN x)
+{
+  GEN pass,c,v,det1,piv,pivprec,vi,p1,A,I,id,idprod;
+  long i, j, k, rg, n, m, m1, cm=0, N;
+  pari_sp av = avma, av1, lim;
+
+  nf = checknf(nf); N = nf_get_degree(nf);
+  check_ZKmodule(x, "nfdetint");
+  A = gel(x,1);
+  I = gel(x,2);
+  n = lg(A)-1; if (!n) return gen_1;
+
+  m1 = lgcols(A); m = m1-1;
+  id = matid(N);
+  c = new_chunk(m1); for (k=1; k<=m; k++) c[k] = 0;
+  piv = pivprec = gen_1;
+
+  av1 = avma; lim = stack_lim(av1,1);
+  det1 = idprod = gen_0; /* dummy for gerepileall */
+  pass = cgetg(m1,t_MAT);
+  v = cgetg(m1,t_COL);
+  for (j=1; j<=m; j++)
+  {
+    gel(pass,j) = zerocol(m);
+    gel(v,j) = gen_0; /* dummy */
+  }
+  for (rg=0,k=1; k<=n; k++)
+  {
+    long t = 0;
+    for (i=1; i<=m; i++)
+      if (!c[i])
+      {
+        vi=nfmul(nf,piv,gcoeff(A,i,k));
+        for (j=1; j<=m; j++)
+          if (c[j]) vi=gadd(vi,nfmul(nf,gcoeff(pass,i,j),gcoeff(A,j,k)));
+        gel(v,i) = vi; if (!t && !gequal0(vi)) t=i;
+      }
+    if (t)
+    {
+      pivprec = piv;
+      if (rg == m-1)
+      {
+        if (!cm)
+        {
+          cm=1; idprod = id;
+          for (i=1; i<=m; i++)
+            if (i!=t)
+              idprod = (idprod==id)? gel(I,c[i])
+                                   : idealmul(nf,idprod,gel(I,c[i]));
+        }
+        p1 = idealmul(nf,gel(v,t),gel(I,k)); c[t]=0;
+        det1 = (typ(det1)==t_INT)? p1: idealadd(nf,p1,det1);
+      }
+      else
+      {
+        rg++; piv=gel(v,t); c[t]=k;
+        for (i=1; i<=m; i++)
+          if (!c[i])
+          {
+            for (j=1; j<=m; j++)
+              if (c[j] && j!=t)
+              {
+                p1 = gsub(nfmul(nf,piv,gcoeff(pass,i,j)),
+                          nfmul(nf,gel(v,i),gcoeff(pass,t,j)));
+                gcoeff(pass,i,j) = rg>1? nfdiv(nf,p1,pivprec)
+                                       : p1;
+              }
+            gcoeff(pass,i,t) = gneg(gel(v,i));
+          }
+      }
+    }
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"nfdetint");
+      gerepileall(av1,6, &det1,&piv,&pivprec,&pass,&v,&idprod);
+    }
+  }
+  if (!cm) { avma = av; return cgetg(1,t_MAT); }
+  return gerepileupto(av, idealmul(nf,idprod,det1));
+}
+
+/* reduce in place components of x[1..lim] mod D (destroy x). D in HNF */
+static void
+nfcleanmod(GEN nf, GEN x, long lim, GEN D)
+{
+  long i;
+  GEN DZ, DZ2, dD;
+  D = Q_remove_denom(D, &dD);
+  if (dD) x = RgC_Rg_mul(x, dD);
+  DZ = gcoeff(D,1,1);
+  DZ2 = shifti(DZ,-1);
+  for (i=1; i<=lim; i++) {
+    GEN c = gel(x,i);
+    c = nf_to_scalar_or_basis(nf, c);
+    switch(typ(c)) /* c = centermod(c, D) */
+    {
+      case t_INT:
+        if (!signe(c)) break;
+        c = centermodii(c, DZ, DZ2);
+        if (dD) c = gred_frac2(c,dD);
+        break;
+      case t_FRAC: {
+        GEN dc = gel(c,2), nc = gel(c,1), N = mulii(DZ, dc);
+        c = centermodii(nc, N, shifti(N,-1));
+        c = gred_frac2(c, dD ? mulii(dc,dD): dc);
+        break;
+      }
+      case t_COL: {
+        GEN dc;
+        c = Q_remove_denom(c, &dc);
+        c = ZC_hnfrem(c, dc? ZM_Z_mul(D,dc): D);
+        if (ZV_isscalar(c))
+        {
+          c = gel(c,1);
+          if (dD) c = gred_frac2(c,dD);
+        }
+        else
+          if (dD) c = RgC_Rg_div(c, dD);
+        break;
+      }
+    }
+    gel(x,i) = c;
+  }
+}
+
+GEN
+nfhnfmod(GEN nf, GEN x, GEN detmat)
+{
+  long li, co, i, j, def, ldef;
+  pari_sp av0=avma, av, lim;
+  GEN dA, dI, d0, w, p1, d, u, v, A, I, J, di;
+
+  nf = checknf(nf);
+  check_ZKmodule(x, "nfhnfmod");
+  A = gel(x,1);
+  I = gel(x,2);
+  co = lg(A); if (co==1) return cgetg(1,t_MAT);
+
+  li = lgcols(A);
+  if (typ(detmat)!=t_MAT) detmat = idealhnf_shallow(nf, detmat);
+  detmat = Q_remove_denom(detmat, NULL);
+  RgM_check_ZM(detmat, "nfhnfmod");
+
+  av = avma; lim = stack_lim(av,2);
+  A = RgM_to_nfM(nf, A);
+  A = Q_remove_denom(A, &dA);
+  I = Q_remove_denom(leafcopy(I), &dI);
+  dA = mul_denom(dA,dI);
+  if (dA) detmat = ZM_Z_mul(detmat, powiu(dA, minss(li,co)));
+
+  def = co; ldef = (li>co)? li-co+1: 1;
+  for (i=li-1; i>=ldef; i--)
+  {
+    def--; j=def; while (j>=1 && isintzero(gcoeff(A,i,j))) j--;
+    if (!j) continue;
+    if (j==def) j--;
+    else {
+      swap(gel(A,j), gel(A,def));
+      swap(gel(I,j), gel(I,def));
+    }
+    for (  ; j; j--)
+    {
+      GEN a, b, S, T, S0, T0 = gel(A,j);
+      b = gel(T0,i); if (isintzero(b)) continue;
+
+      S0 = gel(A,def); a = gel(S0,i);
+      d = nfbezout(nf, a,b, gel(I,def),gel(I,j), &u,&v,&w,&di);
+      S = colcomb(nf, u,v, S0,T0);
+      T = colcomb(nf, a,gneg(b), T0,S0);
+      if (u != gen_0 && v != gen_0) /* already reduced otherwise */
+        nfcleanmod(nf, S, i, idealmul(nf,detmat,di));
+      nfcleanmod(nf, T, i, idealdiv(nf,detmat,w));
+      gel(A,def) = S; gel(A,j) = T;
+      gel(I,def) = d; gel(I,j) = w;
+    }
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"[1]: nfhnfmod, i = %ld", i);
+      gerepileall(av,dA? 4: 3, &A,&I,&detmat,&dA);
+    }
+  }
+  def--; d0 = detmat;
+  A += def; A[0] = evaltyp(t_MAT)|evallg(li);
+  I += def; I[0] = evaltyp(t_VEC)|evallg(li);
+  J = cgetg(li,t_VEC);
+  for (i=li-1; i>=1; i--)
+  {
+    GEN b = gcoeff(A,i,i);
+    d = nfbezout(nf, gen_1,b, d0,gel(I,i), &u,&v,&w,&di);
+    p1 = nfC_nf_mul(nf,gel(A,i),v);
+    if (i > 1)
+    {
+      d0 = idealmul(nf,d0,di);
+      nfcleanmod(nf, p1, i, d0);
+    }
+    gel(A,i) = p1; gel(p1,i) = gen_1;
+    gel(I,i) = d;
+    gel(J,i) = di;
+  }
+  for (i=li-2; i>=1; i--)
+  {
+    d = gel(I,i);
+    for (j=i+1; j<li; j++)
+    {
+      GEN c = gcoeff(A,i,j); if (isintzero(c)) continue;
+      c = element_close(nf, c, idealmul(nf,d,gel(J,j)));
+      gel(A,j) = colcomb1(nf, gneg(c), gel(A,j),gel(A,i));
+    }
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"[2]: nfhnfmod, i = %ld", i);
+      gerepileall(av,dA? 4: 3, &A,&I,&J,&dA);
+    }
+  }
+  idV_simplify(I);
+  if (dA) I = gdiv(I,dA);
+  return gerepilecopy(av0, mkvec2(A, I));
+}
diff --git a/src/basemath/base5.c b/src/basemath/base5.c
new file mode 100644
index 0000000..c8db641
--- /dev/null
+++ b/src/basemath/base5.c
@@ -0,0 +1,992 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                       BASIC NF OPERATIONS                       */
+/*                          (continued 2)                          */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* must return a t_POL */
+GEN
+eltreltoabs(GEN rnfeq, GEN x)
+{
+  long i, k, v;
+  pari_sp av = avma;
+  GEN T, pol, teta, a, s;
+
+  pol = gel(rnfeq,1);
+  a = gel(rnfeq,2);
+  k = itos(gel(rnfeq,3));
+  T = gel(rnfeq,4);
+
+  v = varn(pol);
+  if (varncmp(gvar(x), v) > 0) x = scalarpol(x,v);
+  x = RgX_nffix("eltreltoabs", T, x, 1);
+  /* Mod(X - k a, pol(X)), a root of the polynomial defining base */
+  teta = gadd(pol_x(v), gmulsg(-k,a));
+  s = gen_0;
+  for (i=lg(x)-1; i>1; i--)
+  {
+    GEN c = gel(x,i);
+    if (typ(c) == t_POL) c = RgX_RgXQ_eval(c, a, pol);
+    s = RgX_rem(gadd(c, gmul(teta,s)), pol);
+  }
+  return gerepileupto(av, s);
+}
+GEN
+rnfeltreltoabs(GEN rnf,GEN x)
+{
+  const char *f = "rnfeltreltoabs";
+  GEN pol;
+  checkrnf(rnf);
+  pol = rnf_get_polabs(rnf);
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_FRAC: return gcopy(x);
+    case t_POLMOD:
+      if (RgX_equal_var(gel(x,1), pol))
+      { /* already in 'abs' form, unless possibly if nf = Q */
+        if (rnf_get_nfdegree(rnf) == 1)
+        {
+          GEN y = gel(x,2);
+          pari_sp av = avma;
+          y = simplify_shallow(liftpol_shallow(y));
+          return gerepilecopy(av, mkpolmod(y, pol));
+        }
+        return gcopy(x);
+      }
+      x = polmod_nffix(f,rnf,x,0);
+      if (typ(x) == t_POLMOD) return rnfeltup(rnf,x);
+      retmkpolmod(eltreltoabs(rnf_get_map(rnf), x), RgX_copy(pol));
+    case t_POL:
+      if (varn(x) == rnf_get_nfvarn(rnf)) return rnfeltup(rnf,x);
+      retmkpolmod(eltreltoabs(rnf_get_map(rnf), x), RgX_copy(pol));
+  }
+  pari_err_TYPE(f,x); return NULL;
+}
+
+GEN
+eltabstorel_lift(GEN rnfeq, GEN P)
+{
+  GEN k, T = gel(rnfeq,4), relpol = gel(rnfeq,5);
+  if (is_scalar_t(typ(P))) return P;
+  k = gel(rnfeq,3);
+  P = lift_intern(P);
+  if (signe(k)) P = RgXQX_translate(P, deg1pol_shallow(k, gen_0, varn(T)), T);
+  P = RgXQX_rem(P, relpol, T);
+  return QXQX_to_mod_shallow(P, T);
+}
+/* rnfeq = [pol,a,k,T,relpol], P a t_POL or scalar
+ * Return Mod(P(x + k Mod(y, T(y))), pol(x)) */
+GEN
+eltabstorel(GEN rnfeq, GEN P)
+{
+  GEN T = gel(rnfeq,4), relpol = gel(rnfeq,5);
+  return mkpolmod(eltabstorel_lift(rnfeq,P), QXQX_to_mod_shallow(relpol,T));
+}
+GEN
+rnfeltabstorel(GEN rnf,GEN x)
+{
+  const char *f = "rnfeltabstorel";
+  pari_sp av = avma;
+  GEN pol, T, P;
+  checkrnf(rnf);
+  T = rnf_get_nfpol(rnf);
+  P = rnf_get_pol(rnf);
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_FRAC: return gcopy(x);
+    case t_POLMOD:
+      if (RgX_equal_var(P, gel(x,1)))
+      {
+        x = polmod_nffix(f, rnf, x, 0);
+        return gerepilecopy(av, mkpolmod(x,P));
+      }
+      if (RgX_equal_var(T, gel(x,1))) { x = Rg_nffix(f, T, x, 0); goto END; }
+      pol = rnf_get_polabs(rnf);
+      if (!RgX_equal_var(pol, gel(x,1))) pari_err_MODULUS(f, gel(x,1),pol);
+      x = gel(x,2);
+      switch(typ(x))
+      {
+        case t_INT: return icopy(x);
+        case t_FRAC: return gcopy(x);
+        case t_POL: break;
+        default: pari_err_TYPE(f, x);
+      }
+      break;
+    case t_POL:
+      pol = rnf_get_polabs(rnf);
+      break;
+    default:
+      pari_err_TYPE(f,x);
+      return NULL;
+  }
+  if (!RgX_is_QX(x)) pari_err_TYPE(f,x);
+  if (varn(x) != varn(pol))
+  {
+    if (varn(x) == varn(T)) { x = Rg_nffix(f,T,x,0); goto END; }
+    pari_err_VAR(f, x,pol);
+  }
+  switch(lg(x))
+  {
+    case 2: avma = av; return gen_0;
+    case 3: return gerepilecopy(av, gel(x,2));
+  }
+END:
+  return gerepilecopy(av, eltabstorel(rnf_get_map(rnf), x));
+}
+
+
+/* x a t_VEC of rnf elements in 'alg' form (t_POL). Assume maximal rank or 0 */
+static GEN
+modulereltoabs(GEN rnf, GEN x)
+{
+  GEN W=gel(x,1), I=gel(x,2), rnfeq = rnf_get_map(rnf), polabs = gel(rnfeq,1);
+  long i, j, k, m, N = lg(W)-1;
+  GEN zknf, czknf, M;
+
+  if (!N) return cgetg(1, t_VEC);
+  rnf_get_nfzk(rnf, &zknf,&czknf);
+  m = rnf_get_nfdegree(rnf);
+  M = cgetg(N*m+1, t_VEC);
+  for (k=i=1; i<=N; i++)
+  {
+    GEN c0, cid, w = gel(W,i), id = gel(I,i);
+
+    if (lg(id) == 1) continue; /* must be a t_MAT */
+    id = Q_primitive_part(id, &cid);
+    w = Q_primitive_part(eltreltoabs(rnfeq,w), &c0);
+    c0 = mul_content(c0, mul_content(cid,czknf));
+    if (typ(id) == t_INT)
+      for (j=1; j<=m; j++)
+      {
+        GEN z = RgX_rem(gmul(w, gel(zknf,j)), polabs);
+        if (c0) z = RgX_Rg_mul(z, c0);
+        gel(M,k++) = z;
+      }
+    else
+      for (j=1; j<=m; j++)
+      {
+        GEN c, z = Q_primitive_part(RgV_RgC_mul(zknf,gel(id,j)), &c);
+        z = RgX_rem(gmul(w, z), polabs);
+        c = mul_content(c, c0); if (c) z = RgX_Rg_mul(z, c);
+        gel(M,k++) = z;
+      }
+  }
+  setlg(M, k); return M;
+}
+
+/* Z-basis for absolute maximal order, as a t_MAT */
+GEN
+rnf_basM(GEN rnf)
+{
+  GEN M, d, pol = rnf_get_polabs(rnf);
+  long n = degpol(pol);
+  /* t_VEC of t_POL */
+  M = Q_remove_denom(modulereltoabs(rnf, rnf_get_zk(rnf)), &d);
+  if (d)
+  {
+    M = ZM_hnfmodall(RgXV_to_RgM(M,n), d, hnf_MODID|hnf_CENTER);
+    M = RgM_Rg_div(M, d);
+  }
+  else
+    M = matid(n);
+  return M;
+}
+
+/* only fill in nf[1,3,4,7,8,9] */
+static GEN
+makenfabs(GEN rnf)
+{
+  GEN nf = rnf_get_nf(rnf), pol = rnf_get_polabs(rnf), NF = zerovec(9);
+  GEN M = rnf_basM(rnf);
+  gel(NF,1) = pol;
+  gel(NF,3) = mulii(powiu(nf_get_disc(nf), rnf_get_degree(rnf)),
+                    idealnorm(nf, rnf_get_disc(rnf)));
+  nf_set_multable(NF, M, NULL);
+  gel(NF,4) = get_nfindex(nf_get_zk(NF));
+  return NF;
+}
+
+static GEN
+makenorms(GEN rnf)
+{
+  GEN f = rnf_get_index(rnf);
+  return typ(f) == t_INT? gen_1: RgM_det_triangular(f);
+}
+
+#define NFABS 1
+#define NORMS 2
+GEN
+check_and_build_nfabs(GEN rnf) {
+  return obj_checkbuild(rnf, NFABS, &makenfabs);
+}
+GEN
+check_and_build_norms(GEN rnf) {
+  return obj_checkbuild(rnf, NORMS, &makenorms);
+}
+
+void
+nf_nfzk(GEN nf, GEN rnfeq, GEN *zknf, GEN *czknf)
+{
+  GEN pol = gel(rnfeq,1), a = gel(rnfeq,2);
+  GEN zk = QXV_QXQ_eval(nf_get_zk(nf), a, pol);
+  *zknf = Q_primitive_part(zk, czknf);
+  if (!*czknf) *czknf = gen_1;
+}
+
+GEN
+rnfinit(GEN nf, GEN polrel)
+{
+  pari_sp av = avma;
+  GEN rnf, bas, D,d,f, B, rnfeq, basnf,cobasnf;
+  nf = checknf(nf);
+  bas = rnfallbase(nf,&polrel, &D,&d, &f);
+  B = matbasistoalg(nf,gel(bas,1));
+  gel(bas,1) = lift_if_rational( RgM_to_RgXV(B,varn(polrel)) );
+  rnfeq = nf_rnfeq(nf,polrel);
+  nf_nfzk(nf, rnfeq, &basnf, &cobasnf);
+  rnf = cgetg(13, t_VEC);
+  gel(rnf,1) = polrel;
+  gel(rnf,2) = mkvec2(basnf, cobasnf);
+  gel(rnf,3) = mkvec2(D, d);
+  gel(rnf,4) = f;
+  gel(rnf,5) = cgetg(1, t_VEC); /* dummy */
+  gel(rnf,6) = cgetg(1, t_VEC); /* dummy */
+  gel(rnf,7) = bas;
+  gel(rnf,8) = lift_if_rational( RgM_inv(B) );
+  gel(rnf,9) = cgetg(1,t_VEC); /* dummy */
+  gel(rnf,10)= nf;
+  gel(rnf,11)= rnfeq;
+  gel(rnf,12)= zerovec(2);
+  return gerepilecopy(av, rnf);
+}
+
+GEN
+rnfeltup(GEN rnf, GEN x)
+{
+  pari_sp av = avma;
+  GEN zknf, czknf;
+  checkrnf(rnf);
+  if (typ(x) == t_POLMOD && RgX_equal_var(gel(x,1), rnf_get_polabs(rnf)))
+    return gcopy(x);
+  rnf_get_nfzk(rnf, &zknf, &czknf);
+  x = nfeltup(rnf_get_nf(rnf), x, zknf, czknf);
+  if (typ(x) == t_POL) x = mkpolmod(x, rnf_get_polabs(rnf));
+  return gerepilecopy(av, x);
+}
+
+GEN
+nfeltup(GEN nf, GEN x, GEN zknf, GEN czknf)
+{
+  GEN c;
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) != t_COL) return x;
+  x = Q_primitive_part(x, &c);
+  if (!RgV_is_ZV(x)) pari_err_TYPE("rnfeltup", x);
+  c = mul_content(c, czknf);
+  x = RgV_RgC_mul(zknf, x); if (c) x = RgX_Rg_mul(x, c);
+  return x;
+}
+
+static void
+fail(const char *f, GEN x)
+{ pari_err_DOMAIN(f,"element","not in", strtoGENstr("the base field"),x); }
+GEN
+rnfeltdown(GEN rnf,GEN x)
+{
+  const char *f = "rnfeltdown";
+  pari_sp av = avma;
+  GEN z, T;
+  long v;
+
+  checkrnf(rnf);
+  T = rnf_get_nfpol(rnf);
+  v = varn(T);
+  switch(typ(x))
+  { /* directly belonging to base field ? */
+    case t_INT: return icopy(x);
+    case t_FRAC:return gcopy(x);
+    case t_POLMOD:
+      if (RgX_equal_var(gel(x,1), rnf_get_polabs(rnf))) break;
+      x = polmod_nffix(f,rnf,x,0);
+      /* x was defined mod the relative polynomial & non constant => fail */
+      if (typ(x) == t_POL) fail(f,x);
+      return gerepilecopy(av, x);
+
+    case t_POL:
+      if (varn(x) != v) break;
+      x = Rg_nffix(f,T,x,0);
+      return gerepilecopy(av, x);
+  }
+  /* x defined mod the absolute equation */
+  z = rnfeltabstorel(rnf,x);
+  switch(typ(z))
+  {
+    case t_INT:
+    case t_FRAC: return z;
+  }
+  /* typ(z) = t_POLMOD, varn of both components is rnf_get_varn(rnf) */
+  z = gel(z,2);
+  if (typ(z) == t_POL)
+  {
+    if (lg(z) != 3) fail(f,x);
+    z = gel(z,2);
+  }
+  return gerepilecopy(av, z);
+}
+
+/* vector of rnf elt -> matrix of nf elts */
+static GEN
+rnfV_to_nfM(GEN rnf, GEN x)
+{
+  long i, l = lg(x);
+  GEN y = cgetg(l, t_MAT);
+  for (i = 1; i < l; i++) gel(y,i) = rnfalgtobasis(rnf,gel(x,i));
+  return y;
+}
+
+static GEN
+rnfprincipaltohnf(GEN rnf,GEN x)
+{
+  pari_sp av = avma;
+  GEN bas = rnf_get_zk(rnf), nf = rnf_get_nf(rnf);
+  x = rnfbasistoalg(rnf,x);
+  x = gmul(x, gmodulo(gel(bas,1), rnf_get_pol(rnf)));
+  return gerepileupto(av, nfhnf(nf, mkvec2(rnfV_to_nfM(rnf,x), gel(bas,2))));
+}
+
+/* pseudo-basis for the 0 ideal */
+static GEN
+rnfideal0() { retmkvec2(cgetg(1,t_MAT),cgetg(1,t_VEC)); }
+
+GEN
+rnfidealhnf(GEN rnf, GEN x)
+{
+  GEN z, nf, bas;
+
+  checkrnf(rnf); nf = rnf_get_nf(rnf);
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC:
+      if (isintzero(x)) return rnfideal0();
+      bas = rnf_get_zk(rnf); z = cgetg(3,t_VEC);
+      gel(z,1) = matid(rnf_get_degree(rnf));
+      gel(z,2) = gmul(x, gel(bas,2)); return z;
+
+    case t_VEC:
+      if (lg(x) == 3 && typ(gel(x,1)) == t_MAT) return nfhnf(nf, x);
+      return rnfidealabstorel(rnf, x);
+
+    case t_POLMOD: case t_POL: case t_COL:
+      return rnfprincipaltohnf(rnf,x);
+  }
+  pari_err_TYPE("rnfidealhnf",x);
+  return NULL; /* not reached */
+}
+
+GEN
+prodid(GEN nf, GEN I)
+{
+  long i, l = lg(I);
+  GEN z;
+  if (l == 1) return matid(nf_get_degree(nf));
+  z = gel(I,1);
+  for (i=2; i<l; i++) z = idealmul(nf, z, gel(I,i));
+  return z;
+}
+
+static GEN
+prodidnorm(GEN nf, GEN I)
+{
+  long i, l = lg(I);
+  GEN z;
+  if (l == 1) return gen_1;
+  z = idealnorm(nf, gel(I,1));
+  for (i=2; i<l; i++) z = gmul(z, idealnorm(nf, gel(I,i)));
+  return z;
+}
+
+GEN
+rnfidealnormrel(GEN rnf, GEN id)
+{
+  pari_sp av = avma;
+  GEN nf, z = gel(rnfidealhnf(rnf,id), 2);
+  if (lg(z) == 1) return cgetg(1, t_MAT);
+  nf = rnf_get_nf(rnf); z = prodid(nf, z);
+  return gerepileupto(av, idealmul(nf,z, rnf_get_index(rnf)));
+}
+
+GEN
+rnfidealnormabs(GEN rnf, GEN id)
+{
+  pari_sp av = avma;
+  GEN nf, z = gel(rnfidealhnf(rnf,id), 2);
+  if (lg(z) == 1) return gen_0;
+  nf = rnf_get_nf(rnf); z = prodidnorm(nf, z);
+  return gerepileupto(av, gmul(z, check_and_build_norms(rnf)));
+}
+
+GEN
+rnfidealreltoabs(GEN rnf,GEN x)
+{
+  pari_sp av = avma;
+  long i, l;
+  GEN w;
+
+  x = rnfidealhnf(rnf,x);
+  w = gel(x,1); l = lg(w); settyp(w, t_VEC);
+  for (i=1; i<l; i++) gel(w,i) = lift_intern( rnfbasistoalg(rnf, gel(w,i)) );
+  return gerepilecopy(av, modulereltoabs(rnf, x));
+}
+
+GEN
+rnfidealabstorel(GEN rnf, GEN x)
+{
+  long N, j;
+  pari_sp av = avma;
+  GEN A, I, invbas;
+
+  checkrnf(rnf);
+  invbas = rnf_get_invzk(rnf);
+  if (typ(x) != t_VEC) pari_err_TYPE("rnfidealabstorel",x);
+  N = lg(x)-1;
+  if (N != rnf_get_absdegree(rnf))
+  {
+    if (!N) return rnfideal0();
+    pari_err_DIM("rnfidealabstorel");
+  }
+  A = cgetg(N+1,t_MAT);
+  I = cgetg(N+1,t_VEC);
+  for (j=1; j<=N; j++)
+  {
+    GEN t = lift_intern( rnfeltabstorel(rnf, gel(x,j)) );
+    gel(A,j) = mulmat_pol(invbas, t);
+    gel(I,j) = gen_1;
+  }
+  return gerepileupto(av, nfhnf(rnf_get_nf(rnf), mkvec2(A,I)));
+}
+
+GEN
+rnfidealdown(GEN rnf,GEN x)
+{
+  pari_sp av = avma;
+  GEN I;
+  x = rnfidealhnf(rnf,x); I = gel(x,2);
+  if (lg(I) == 1) { avma = av; return cgetg(1,t_MAT); }
+  return gerepilecopy(av, gel(I,1));
+}
+
+/* lift ideal x to the relative extension, returns a Z-basis */
+GEN
+rnfidealup(GEN rnf,GEN x)
+{
+  pari_sp av = avma;
+  long i, n;
+  GEN nf, bas, bas2, I;
+
+  checkrnf(rnf); nf = rnf_get_nf(rnf);
+  n = rnf_get_degree(rnf);
+  bas = rnf_get_zk(rnf); bas2 = gel(bas,2);
+
+  (void)idealtyp(&x, &I); /* I is junk */
+  I = cgetg(n+1,t_VEC);
+  for (i=1; i<=n; i++) gel(I,i) = idealmul(nf,x,gel(bas2,i));
+  return gerepilecopy(av, modulereltoabs(rnf, mkvec2(gel(bas,1), I)));
+}
+
+/* x a relative HNF => vector of 2 generators (relative polmods) */
+GEN
+rnfidealtwoelement(GEN rnf, GEN x)
+{
+  pari_sp av = avma;
+  GEN y, cy, z, NF;
+
+  y = rnfidealreltoabs(rnf,x);
+  NF = check_and_build_nfabs(rnf);
+  y = matalgtobasis(NF, y); settyp(y, t_MAT);
+  y = Q_primitive_part(y, &cy);
+  y = ZM_hnf(y);
+  if (lg(y) == 1) { avma = av; return mkvec2(gen_0, gen_0); }
+  y = idealtwoelt(NF, y);
+  if (cy) y = RgV_Rg_mul(y, cy);
+  z = rnfeltabstorel(rnf, coltoliftalg(NF, gel(y,2)));
+  return gerepilecopy(av, mkvec2(gel(y,1), z));
+}
+
+GEN
+rnfidealmul(GEN rnf,GEN x,GEN y)
+{
+  pari_sp av = avma;
+  GEN nf, z, x1, x2, p1, p2, bas;
+
+  y = rnfidealtwoelement(rnf,y);
+  if (isintzero(gel(y,1))) { avma = av; return rnfideal0(); }
+  nf = rnf_get_nf(rnf);
+  bas = rnf_get_zk(rnf);
+  x = rnfidealhnf(rnf,x);
+  x1 = gmodulo(gmul(gel(bas,1), matbasistoalg(nf,gel(x,1))), rnf_get_pol(rnf));
+  x2 = gel(x,2);
+  p1 = gmul(gel(y,1), gel(x,1));
+  p2 = rnfV_to_nfM(rnf, gmul(gel(y,2), x1));
+  z = mkvec2(shallowconcat(p1, p2), shallowconcat(x2, x2));
+  return gerepileupto(av, nfhnf(nf,z));
+}
+
+int
+nfissquarefree(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  GEN g, y = RgX_deriv(x);
+  if (RgX_is_rational(x))
+    g = QX_gcd(x, y);
+  else
+    g = nfgcd(x, y, nf, NULL);
+  avma = av; return (degpol(g) == 0);
+}
+
+GEN
+rnfequationall(GEN A, GEN B, long *pk, GEN *pLPRS)
+{
+  long lA, lB;
+  GEN nf, C;
+
+  A = get_nfpol(A, &nf); lA = lg(A);
+  if (!nf) {
+    if (lA<=3) pari_err_CONSTPOL("rnfequation");
+    RgX_check_ZX(A,"rnfequation");
+  }
+  B = RgX_nffix("rnfequation", A,B,1); lB = lg(B);
+  if (lB<=3) pari_err_CONSTPOL("rnfequation");
+  B = Q_primpart(B);
+
+  if (!nfissquarefree(A,B))
+    pari_err_DOMAIN("rnfequation","issquarefree(B)","=",gen_0,B);
+
+  *pk = 0; C = ZX_ZXY_resultant_all(A, B, pk, pLPRS);
+  if (gsigne(leading_term(C)) < 0) C = RgX_neg(C);
+  *pk = -*pk; return Q_primpart(C);
+}
+
+GEN
+rnfequation0(GEN A, GEN B, long flall)
+{
+  pari_sp av = avma;
+  GEN LPRS, C;
+  long k;
+
+  C = rnfequationall(A, B, &k, flall? &LPRS: NULL);
+  if (flall)
+  { /* a,b,c root of A,B,C = compositum, c = b + k a */
+    GEN a, mH0 = RgX_neg(gel(LPRS,1)), H1 = gel(LPRS,2);
+    a = RgXQ_mul(mH0, QXQ_inv(H1, C), C);
+    C = mkvec3(C, mkpolmod(a, C), stoi(k));
+  }
+  return gerepilecopy(av, C);
+}
+GEN
+rnfequation(GEN nf, GEN pol) { return rnfequation0(nf,pol,0); }
+GEN
+rnfequation2(GEN nf, GEN pol) { return rnfequation0(nf,pol,1); }
+GEN
+nf_rnfeq(GEN nf, GEN relpol)
+{
+  GEN pol, a, k, junk, eq;
+  relpol = liftpol_shallow(relpol);
+  eq = rnfequation2(nf, relpol);
+  pol = gel(eq,1);
+  a = gel(eq,2); if (typ(a) == t_POLMOD) a = gel(a,2);
+  k = gel(eq,3);
+  return mkvec5(pol,a,k,get_nfpol(nf, &junk),relpol);
+}
+/* only allow abstorel */
+GEN
+nf_rnfeqsimple(GEN nf, GEN relpol)
+{
+  long sa;
+  GEN junk, pol = rnfequationall(nf, relpol, &sa, NULL);
+  return mkvec5(pol,gen_0/*dummy*/,stoi(sa),get_nfpol(nf, &junk),relpol);
+}
+
+static GEN
+nftau(long r1, GEN x)
+{
+  long i, l = lg(x);
+  GEN s = r1? gel(x,1): gmul2n(real_i(gel(x,1)),1);
+  for (i=2; i<=r1; i++) s = gadd(s, gel(x,i));
+  for (   ; i < l; i++) s = gadd(s, gmul2n(real_i(gel(x,i)),1));
+  return s;
+}
+
+static GEN
+initmat(long l)
+{
+  GEN x = cgetg(l, t_MAT);
+  long i;
+  for (i = 1; i < l; i++) gel(x,i) = cgetg(l, t_COL);
+  return x;
+}
+
+static GEN
+nftocomplex(GEN nf, GEN x)
+{
+  GEN M = nf_get_M(nf);
+  x = nf_to_scalar_or_basis(nf,x);
+  if (typ(x) != t_COL) return const_col(nbrows(M), x);
+  return RgM_RgC_mul(M, x);
+}
+/* assume x a square t_MAT, return a t_VEC of embeddings of its columns */
+static GEN
+mattocomplex(GEN nf, GEN x)
+{
+  long i,j, l = lg(x);
+  GEN v = cgetg(l, t_VEC);
+  for (j=1; j<l; j++)
+  {
+    GEN c = gel(x,j), b = cgetg(l, t_MAT);
+    for (i=1; i<l; i++) gel(b,i) = nftocomplex(nf, gel(c,i));
+    b = shallowtrans(b); settyp(b, t_COL);
+    gel(v,j) = b;
+  }
+  return v;
+}
+
+static GEN
+nf_all_roots(GEN nf, GEN x, long prec)
+{
+  long i, j, l = lg(x), ru = lg(nf_get_roots(nf));
+  GEN y = cgetg(l, t_POL), v, z;
+
+  x = RgX_to_nfX(nf, x);
+  y[1] = x[1];
+  for (i=2; i<l; i++) gel(y,i) = nftocomplex(nf, gel(x,i));
+  i = gprecision(y); if (i && i <= 3) return NULL;
+
+  v = cgetg(ru, t_VEC);
+  z = cgetg(l, t_POL); z[1] = x[1];
+  for (i=1; i<ru; i++)
+  {
+    for (j = 2; j < l; j++) gel(z,j) = gmael(y,j,i);
+    gel(v,i) = cleanroots(z, prec);
+  }
+  return v;
+}
+
+static GEN
+rnfscal(GEN m, GEN x, GEN y)
+{
+  long i, l = lg(m);
+  GEN z = cgetg(l, t_COL);
+  for (i = 1; i < l; i++)
+    gel(z,i) = gmul(gconj(shallowtrans(gel(x,i))), gmul(gel(m,i), gel(y,i)));
+  return z;
+}
+
+/* x ideal in HNF */
+static GEN
+findmin(GEN nf, GEN x, GEN muf)
+{
+  pari_sp av = avma;
+  long e;
+  GEN cx, y, m, M = nf_get_M(nf);
+
+  x = Q_primitive_part(x, &cx);
+  if (gequal1(gcoeff(x,1,1))) y = M;
+  else
+  {
+    GEN G = nf_get_G(nf);
+    m = lllfp(RgM_mul(G,x), 0.75, 0);
+    if (typ(m) != t_MAT)
+    {
+      x = ZM_lll(x, 0.75, LLL_INPLACE);
+      m = lllfp(RgM_mul(G,x), 0.75, 0);
+      if (typ(m) != t_MAT) pari_err_PREC("rnflllgram");
+    }
+    x = ZM_mul(x, m);
+    y = RgM_mul(M, x);
+  }
+  m = RgM_solve_realimag(y, muf);
+  if (!m) return NULL; /* precision problem */
+  if (cx) m = RgC_Rg_div(m, cx);
+  m = grndtoi(m, &e);
+  if (e >= 0) return NULL; /* precision problem */
+  m = ZM_ZC_mul(x, m);
+  if (cx) m = RgC_Rg_mul(m, cx);
+  return gerepileupto(av, m);
+}
+
+static int
+RED(long k, long l, GEN U, GEN mu, GEN MC, GEN nf, GEN I, GEN *Ik_inv)
+{
+  GEN x, xc, ideal;
+  long i;
+
+  if (!*Ik_inv) *Ik_inv = idealinv(nf, gel(I,k));
+  ideal = idealmul(nf,gel(I,l), *Ik_inv);
+  x = findmin(nf, ideal, gcoeff(mu,k,l));
+  if (!x) return 0;
+  if (gequal0(x)) return 1;
+
+  xc = nftocomplex(nf,x);
+  gel(MC,k) = gsub(gel(MC,k), vecmul(xc,gel(MC,l)));
+  gel(U,k) = gsub(gel(U,k), gmul(coltoalg(nf,x), gel(U,l)));
+  gcoeff(mu,k,l) = gsub(gcoeff(mu,k,l), xc);
+  for (i=1; i<l; i++)
+    gcoeff(mu,k,i) = gsub(gcoeff(mu,k,i), vecmul(xc,gcoeff(mu,l,i)));
+  return 1;
+}
+
+static int
+check_0(GEN B)
+{
+  long i, l = lg(B);
+  for (i = 1; i < l; i++)
+    if (gsigne(gel(B,i)) <= 0) return 1;
+  return 0;
+}
+
+static int
+do_SWAP(GEN I, GEN MC, GEN MCS, GEN h, GEN mu, GEN B, long kmax, long k,
+        const long alpha, long r1)
+{
+  GEN p1, p2, muf, mufc, Bf, temp;
+  long i, j;
+
+  p1 = nftau(r1, gadd(gel(B,k),
+                      gmul(gnorml2(gcoeff(mu,k,k-1)), gel(B,k-1))));
+  p2 = nftau(r1, gel(B,k-1));
+  if (gcmp(gmulsg(alpha,p1), gmulsg(alpha-1,p2)) > 0) return 0;
+
+  swap(gel(MC,k-1),gel(MC,k));
+  swap(gel(h,k-1), gel(h,k));
+  swap(gel(I,k-1), gel(I,k));
+  for (j=1; j<=k-2; j++) swap(gcoeff(mu,k-1,j),gcoeff(mu,k,j));
+  muf = gcoeff(mu,k,k-1);
+  mufc = gconj(muf);
+  Bf = gadd(gel(B,k), vecmul(real_i(vecmul(muf,mufc)), gel(B,k-1)));
+  if (check_0(Bf)) return 1; /* precision problem */
+
+  p1 = vecdiv(gel(B,k-1),Bf);
+  gcoeff(mu,k,k-1) = vecmul(mufc,p1);
+  temp = gel(MCS,k-1);
+  gel(MCS,k-1) = gadd(gel(MCS,k), vecmul(muf,gel(MCS,k-1)));
+  gel(MCS,k) = gsub(vecmul(vecdiv(gel(B,k),Bf), temp),
+                    vecmul(gcoeff(mu,k,k-1), gel(MCS,k)));
+  gel(B,k) = vecmul(gel(B,k),p1);
+  gel(B,k-1) = Bf;
+  for (i=k+1; i<=kmax; i++)
+  {
+    temp = gcoeff(mu,i,k);
+    gcoeff(mu,i,k) = gsub(gcoeff(mu,i,k-1), vecmul(muf, gcoeff(mu,i,k)));
+    gcoeff(mu,i,k-1) = gadd(temp, vecmul(gcoeff(mu,k,k-1),gcoeff(mu,i,k)));
+  }
+  return 1;
+}
+
+static GEN
+rel_T2(GEN nf, GEN pol, long lx, long prec)
+{
+  long ru, i, j, k, l;
+  GEN T2, s, unro, roorder, powreorder;
+
+  roorder = nf_all_roots(nf, pol, prec);
+  if (!roorder) return NULL;
+  ru = lg(roorder);
+  unro = cgetg(lx,t_COL); for (i=1; i<lx; i++) gel(unro,i) = gen_1;
+  powreorder = cgetg(lx,t_MAT); gel(powreorder,1) = unro;
+  T2 = cgetg(ru, t_VEC);
+  for (i = 1; i < ru; i++)
+  {
+    GEN ro = gel(roorder,i);
+    GEN m = initmat(lx);
+    for (k=2; k<lx; k++)
+    {
+      GEN c = cgetg(lx, t_COL); gel(powreorder,k) = c;
+      for (j=1; j < lx; j++)
+        gel(c,j) = gmul(gel(ro,j), gmael(powreorder,k-1,j));
+    }
+    for (l = 1; l < lx; l++)
+      for (k = 1; k <= l; k++)
+      {
+        s = gen_0;
+        for (j = 1; j < lx; j++)
+          s = gadd(s, gmul(gconj(gmael(powreorder,k,j)),
+                                 gmael(powreorder,l,j)));
+        if (l == k)
+          gcoeff(m, l, l) = real_i(s);
+        else
+        {
+          gcoeff(m, k, l) = s;
+          gcoeff(m, l, k) = gconj(s);
+        }
+      }
+    gel(T2,i) = m;
+  }
+  return T2;
+}
+
+/* given a base field nf (e.g main variable y), a polynomial pol with
+ * coefficients in nf    (e.g main variable x), and an order as output
+ * by rnfpseudobasis, outputs a reduced order. */
+GEN
+rnflllgram(GEN nf, GEN pol, GEN order,long prec)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  long j, k, l, kmax, r1, lx, count = 0;
+  GEN M, I, h, H, mth, MC, MPOL, MCS, B, mu;
+  const long alpha = 10, MAX_COUNT = 4;
+
+  nf = checknf(nf); r1 = nf_get_r1(nf);
+  check_ZKmodule(order, "rnflllgram");
+  M = gel(order,1);
+  I = gel(order,2); lx = lg(I);
+  if (lx < 3) return gcopy(order);
+  if (lx-1 != degpol(pol)) pari_err_DIM("rnflllgram");
+  I = leafcopy(I);
+  H = NULL;
+  MPOL = matbasistoalg(nf, M);
+  MCS = matid(lx-1); /* dummy for gerepile */
+PRECNF:
+  if (count == MAX_COUNT)
+  {
+    prec = precdbl(prec); count = 0;
+    if (DEBUGLEVEL) pari_warn(warnprec,"rnflllgram",prec);
+    nf = nfnewprec_shallow(nf,prec);
+  }
+  mth = rel_T2(nf, pol, lx, prec);
+  if (!mth) { count = MAX_COUNT; goto PRECNF; }
+  h = NULL;
+PRECPB:
+  if (h)
+  { /* precision problem, recompute. If no progress, increase nf precision */
+    if (++count == MAX_COUNT || RgM_isidentity(h)) {count = MAX_COUNT; goto PRECNF;}
+    H = H? gmul(H, h): h;
+    MPOL = gmul(MPOL, h);
+  }
+  h = matid(lx-1);
+  MC = mattocomplex(nf, MPOL);
+  mu = cgetg(lx,t_MAT);
+  B  = cgetg(lx,t_COL);
+  for (j=1; j<lx; j++)
+  {
+    gel(mu,j) = zerocol(lx - 1);
+    gel(B,j) = gen_0;
+  }
+  if (DEBUGLEVEL) err_printf("k = ");
+  gel(B,1) = real_i(rnfscal(mth,gel(MC,1),gel(MC,1)));
+  gel(MCS,1) = gel(MC,1);
+  kmax = 1; k = 2;
+  do
+  {
+    GEN Ik_inv = NULL;
+    if (DEBUGLEVEL) err_printf("%ld ",k);
+    if (k > kmax)
+    { /* Incremental Gram-Schmidt */
+      kmax = k; gel(MCS,k) = gel(MC,k);
+      for (j=1; j<k; j++)
+      {
+        gcoeff(mu,k,j) = vecdiv(rnfscal(mth,gel(MCS,j),gel(MC,k)),
+                                gel(B,j));
+        gel(MCS,k) = gsub(gel(MCS,k), vecmul(gcoeff(mu,k,j),gel(MCS,j)));
+      }
+      gel(B,k) = real_i(rnfscal(mth,gel(MCS,k),gel(MCS,k)));
+      if (check_0(gel(B,k))) goto PRECPB;
+    }
+    if (!RED(k, k-1, h, mu, MC, nf, I, &Ik_inv)) goto PRECPB;
+    if (do_SWAP(I,MC,MCS,h,mu,B,kmax,k,alpha, r1))
+    {
+      if (!B[k]) goto PRECPB;
+      if (k > 2) k--;
+    }
+    else
+    {
+      for (l=k-2; l; l--)
+        if (!RED(k, l, h, mu, MC, nf, I, &Ik_inv)) goto PRECPB;
+      k++;
+    }
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"rnflllgram");
+      gerepileall(av, H?10:9, &nf,&mth,&h,&MPOL,&B,&MC,&MCS,&mu,&I,&H);
+    }
+  }
+  while (k < lx);
+  MPOL = gmul(MPOL,h);
+  if (H) h = gmul(H, h);
+  if (DEBUGLEVEL) err_printf("\n");
+  MPOL = RgM_to_nfM(nf,MPOL);
+  h = RgM_to_nfM(nf,h);
+  return gerepilecopy(av, mkvec2(mkvec2(MPOL,I), h));
+}
+
+GEN
+rnfpolred(GEN nf, GEN pol, long prec)
+{
+  pari_sp av = avma;
+  long i, j, n, v = varn(pol);
+  GEN id, w, I, O, bnf, nfpol;
+
+  if (typ(pol)!=t_POL) pari_err_TYPE("rnfpolred",pol);
+  bnf = nf; nf = checknf(bnf);
+  bnf = (nf == bnf)? NULL: checkbnf(bnf);
+  if (degpol(pol) <= 1) { w = cgetg(2, t_VEC); gel(w,1) = pol_x(v); return w; }
+  nfpol = nf_get_pol(nf);
+
+  id = rnfpseudobasis(nf,pol);
+  if (bnf && is_pm1( bnf_get_no(bnf) )) /* if bnf is principal */
+  {
+    GEN newI, newO;
+    O = gel(id,1);
+    I = gel(id,2); n = lg(I)-1;
+    newI = cgetg(n+1,t_VEC);
+    newO = cgetg(n+1,t_MAT);
+    for (j=1; j<=n; j++)
+    {
+      GEN al = gen_if_principal(bnf,gel(I,j));
+      gel(newI,j) = gen_1;
+      gel(newO,j) = nfC_nf_mul(nf, gel(O,j), al);
+    }
+    id = mkvec2(newO, newI);
+  }
+
+  id = gel(rnflllgram(nf,pol,id,prec),1);
+  O = gel(id,1);
+  I = gel(id,2); n = lg(I)-1;
+  w = cgetg(n+1,t_VEC);
+  pol = lift(pol);
+  for (j=1; j<=n; j++)
+  {
+    GEN newpol, L, a, Ij = gel(I,j);
+    a = RgC_Rg_mul(gel(O,j), (typ(Ij) == t_MAT)? gcoeff(Ij,1,1): Ij);
+    for (i=n; i; i--)
+    {
+      GEN c = gel(a,i);
+      if (typ(c) == t_COL) gel(a,i) = coltoliftalg(nf, c);
+    }
+    a = RgV_to_RgX(a, v);
+    newpol = RgXQX_red(RgXQ_charpoly(a, pol, v), nfpol);
+    newpol = Q_primpart(newpol);
+
+    (void)nfgcd_all(newpol, RgX_deriv(newpol), nfpol, nf_get_index(nf), &newpol);
+    L = leading_term(newpol);
+    gel(w,j) = (typ(L) == t_POL)? RgXQX_div(newpol, L, nfpol)
+                                : RgX_Rg_div(newpol, L);
+  }
+  return gerepilecopy(av,w);
+}
diff --git a/src/basemath/bb_group.c b/src/basemath/bb_group.c
new file mode 100644
index 0000000..df74483
--- /dev/null
+++ b/src/basemath/bb_group.c
@@ -0,0 +1,909 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**             GENERIC ALGORITHMS ON BLACKBOX GROUP                  **/
+/**                                                                   **/
+/***********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#undef pow /* AIX: pow(a,b) is a macro, wrongly expanded on grp->pow(a,b,c) */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                    POWERING                                       **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* return (n>>(i+1-l)) & ((1<<l)-1) */
+static ulong
+int_block(GEN n, long i, long l)
+{
+  long q = divsBIL(i), r = remsBIL(i)+1, lr;
+  GEN nw = int_W(n, q);
+  ulong w = (ulong) *nw, w2;
+  if (r>=l) return (w>>(r-l))&((1UL<<l)-1);
+  w &= (1UL<<r)-1; lr = l-r;
+  w2 = (ulong) *int_precW(nw); w2 >>= (BITS_IN_LONG-lr);
+  return (w<<lr)|w2;
+}
+
+/* assume n != 0, t_INT. Compute x^|n| using sliding window powering */
+static GEN
+sliding_window_powu(GEN x, ulong n, long e, void *E, GEN (*sqr)(void*,GEN),
+                                                     GEN (*mul)(void*,GEN,GEN))
+{
+  pari_sp av, lim;
+  long i, l = expu(n), u = (1UL<<(e-1));
+  long w, v;
+  GEN tab = cgetg(1+u, t_VEC);
+  GEN x2 = sqr(E, x), z = NULL, tw;
+  gel(tab, 1) = x;
+  for (i=2; i<=u; i++) gel(tab,i) = mul(E, gel(tab,i-1), x2);
+  av = avma; lim = stack_lim(av, 1);
+  while (l>=0)
+  {
+    if (e > l+1) e = l+1;
+    w = (n>>(l+1-e)) & ((1UL<<e)-1); v = vals(w); l-=e;
+    tw = gel(tab, 1+(w>>(v+1)));
+    if (z)
+    {
+      for (i=1; i<=e-v; i++) z = sqr(E, z);
+      z = mul(E, z, tw);
+    } else z = tw;
+    for (i=1; i<=v; i++) z = sqr(E, z);
+    while (l>=0)
+    {
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"sliding_window_powu (%ld)", l);
+        z = gerepilecopy(av, z);
+      }
+      if (n&(1UL<<l)) break;
+      z = sqr(E, z); l--;
+    }
+  }
+  return z;
+}
+
+
+/* assume n != 0, t_INT. Compute x^|n| using sliding window powering */
+static GEN
+sliding_window_pow(GEN x, GEN n, long e, void *E, GEN (*sqr)(void*,GEN),
+                                                  GEN (*mul)(void*,GEN,GEN))
+{
+  pari_sp av, lim;
+  long i, l = expi(n), u = (1UL<<(e-1));
+  long w, v;
+  GEN tab = cgetg(1+u, t_VEC);
+  GEN x2 = sqr(E, x), z = NULL, tw;
+  gel(tab, 1) = x;
+  for (i=2; i<=u; i++) gel(tab,i) = mul(E, gel(tab,i-1), x2);
+  av = avma; lim = stack_lim(av, 1);
+  while (l>=0)
+  {
+    if (e > l+1) e = l+1;
+    w = int_block(n,l,e); v = vals(w); l-=e;
+    tw = gel(tab, 1+(w>>(v+1)));
+    if (z)
+    {
+      for (i=1; i<=e-v; i++) z = sqr(E, z);
+      z = mul(E, z, tw);
+    } else z = tw;
+    for (i=1; i<=v; i++) z = sqr(E, z);
+    while (l>=0)
+    {
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"sliding_window_pow (%ld)", l);
+        z = gerepilecopy(av, z);
+      }
+      if (int_bit(n,l)) break;
+      z = sqr(E, z); l--;
+    }
+  }
+  return z;
+}
+
+/* assume n != 0, t_INT. Compute x^|n| using leftright binary powering */
+static GEN
+leftright_binary_powu(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN),
+                                              GEN (*mul)(void*,GEN,GEN))
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN  y;
+  int j;
+
+  if (n == 1) return gcopy(x);
+  y = x; j = 1+bfffo(n);
+  /* normalize, i.e set highest bit to 1 (we know n != 0) */
+  n<<=j; j = BITS_IN_LONG-j;
+  /* first bit is now implicit */
+  for (; j; n<<=1,j--)
+  {
+    y = sqr(E,y);
+    if (n & HIGHBIT) y = mul(E,y,x); /* first bit set: multiply by base */
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"leftright_powu (%d)", j);
+      y = gerepilecopy(av, y);
+    }
+  }
+  return y;
+}
+
+GEN
+gen_powu_i(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN),
+                                    GEN (*mul)(void*,GEN,GEN))
+{
+  long l;
+  if (n == 1) return gcopy(x);
+  l = expu(n);
+  if (l<=8)
+    return leftright_binary_powu(x, n, E, sqr, mul);
+  else
+    return sliding_window_powu(x, n, l<=24? 2: 3, E, sqr, mul);
+}
+
+GEN
+gen_powu(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN),
+                                  GEN (*mul)(void*,GEN,GEN))
+{
+  pari_sp av = avma;
+  if (n == 1) return gcopy(x);
+  return gerepilecopy(av, gen_powu_i(x,n,E,sqr,mul));
+}
+
+GEN
+gen_pow_i(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN),
+                                 GEN (*mul)(void*,GEN,GEN))
+{
+  long l, e;
+  if (lgefint(n)==3) return gen_powu_i(x,(ulong)n[2],E,sqr,mul);
+  l = expi(n);
+  if      (l<=64)  e = 3;
+  else if (l<=160) e = 4;
+  else if (l<=384) e = 5;
+  else if (l<=896) e = 6;
+  else             e = 7;
+  return sliding_window_pow(x, n, e, E, sqr, mul);
+}
+
+GEN
+gen_pow(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN),
+                               GEN (*mul)(void*,GEN,GEN))
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, gen_pow_i(x,n,E,sqr,mul));
+}
+
+/* assume n > 0. Compute x^n using left-right binary powering */
+GEN
+gen_powu_fold_i(GEN x, ulong n, void *E, GEN  (*sqr)(void*,GEN),
+                                         GEN (*msqr)(void*,GEN))
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN y;
+  int j;
+
+  if (n == 1) return gcopy(x);
+  y = x; j = 1+bfffo(n);
+  /* normalize, i.e set highest bit to 1 (we know n != 0) */
+  n<<=j; j = BITS_IN_LONG-j;
+  /* first bit is now implicit */
+  for (; j; n<<=1,j--)
+  {
+    if (n & HIGHBIT) y = msqr(E,y); /* first bit set: multiply by base */
+    else y = sqr(E,y);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"gen_powu_fold (%d)", j);
+      y = gerepilecopy(av, y);
+    }
+  }
+  return y;
+}
+GEN
+gen_powu_fold(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN),
+                                       GEN (*msqr)(void*,GEN))
+{
+  pari_sp av = avma;
+  if (n == 1) return gcopy(x);
+  return gerepilecopy(av, gen_powu_fold_i(x,n,E,sqr,msqr));
+}
+
+/* assume N != 0, t_INT. Compute x^|N| using left-right binary powering */
+GEN
+gen_pow_fold_i(GEN x, GEN N, void *E, GEN (*sqr)(void*,GEN),
+                                      GEN (*msqr)(void*,GEN))
+{
+  long ln = lgefint(N);
+  if (ln == 3) return gen_powu_fold_i(x, N[2], E, sqr, msqr);
+  else
+  {
+    GEN nd = int_MSW(N), y = x;
+    ulong n = *nd;
+    long i;
+    int j = 1+bfffo(n);
+    pari_sp av = avma, lim = stack_lim(av, 1);
+
+    /* normalize, i.e set highest bit to 1 (we know n != 0) */
+    n<<=j; j = BITS_IN_LONG-j;
+    /* first bit is now implicit */
+    for (i=ln-2;;)
+    {
+      for (; j; n<<=1,j--)
+      {
+        if (n & HIGHBIT) y = msqr(E,y); /* first bit set: multiply by base */
+        else y = sqr(E,y);
+        if (low_stack(lim, stack_lim(av,1)))
+        {
+          if (DEBUGMEM>1) pari_warn(warnmem,"gen_pow_fold (%d)", j);
+          y = gerepilecopy(av, y);
+        }
+      }
+      if (--i == 0) return y;
+      nd = int_precW(nd);
+      n = *nd; j = BITS_IN_LONG;
+    }
+  }
+}
+GEN
+gen_pow_fold(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN),
+                                    GEN (*msqr)(void*,GEN))
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, gen_pow_fold_i(x,n,E,sqr,msqr));
+}
+
+GEN
+gen_powers(GEN x, long l, int use_sqr, void *E, GEN (*sqr)(void*,GEN),
+                                      GEN (*mul)(void*,GEN,GEN), GEN (*one)(void*))
+{
+  long i;
+  GEN V = cgetg(l+2,t_VEC);
+  gel(V,1) = one(E); if (l==0) return V;
+  gel(V,2) = gcopy(x); if (l==1) return V;
+  gel(V,3) = sqr(E,x);
+  if (use_sqr)
+    for(i = 4; i < l+2; i++)
+      gel(V,i) = (i&1)? sqr(E,gel(V, (i+1)>>1))
+                      : mul(E,gel(V, i-1),x);
+  else
+    for(i = 4; i < l+2; i++)
+      gel(V,i) = mul(E,gel(V,i-1),x);
+  return V;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                    DISCRETE LOGARITHM                             **/
+/**                                                                   **/
+/***********************************************************************/
+
+static GEN
+iter_rho(GEN x, GEN g, GEN q, GEN A, ulong h, void *E, const struct bb_group *grp)
+{
+  GEN a = gel(A,1);
+  switch((h|grp->hash(a))%3UL)
+  {
+    case 0:
+      return mkvec3(grp->pow(E,a,gen_2),Fp_mulu(gel(A,2),2,q),
+                                        Fp_mulu(gel(A,3),2,q));
+    case 1:
+      return mkvec3(grp->mul(E,a,x),addis(gel(A,2),1),gel(A,3));
+    case 2:
+      return mkvec3(grp->mul(E,a,g),gel(A,2),addis(gel(A,3),1));
+  }
+  return NULL;
+}
+
+/*Generic Pollard rho discrete log algorithm*/
+static GEN
+gen_Pollard_log(GEN x, GEN g, GEN q, void *E, const struct bb_group *grp)
+{
+  pari_sp av=avma, lim=stack_lim(av,2);
+  GEN A, B, l, sqrt4q = sqrti(shifti(q,4));
+  ulong i, h = 0, imax = itou_or_0(sqrt4q);
+  if (!imax) imax = ULONG_MAX;
+  do {
+ rho_restart:
+    A = B = mkvec3(x,gen_1,gen_0);
+    i=0;
+    do {
+      if (i>imax)
+      {
+        h++;
+        if (DEBUGLEVEL)
+          pari_warn(warner,"changing Pollard rho hash seed to %ld",h);
+        goto rho_restart;
+      }
+      A = iter_rho(x, g, q, A, h, E, grp);
+      B = iter_rho(x, g, q, B, h, E, grp);
+      B = iter_rho(x, g, q, B, h, E, grp);
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"gen_Pollard_log");
+        gerepileall(av, 2, &A, &B);
+      }
+      i++;
+    } while (!grp->equal(gel(A,1), gel(B,1)));
+    gel(A,2) = modii(gel(A,2), q);
+    gel(B,2) = modii(gel(B,2), q);
+    h++;
+  } while (equalii(gel(A,2), gel(B,2)));
+  l = Fp_div(Fp_sub(gel(B,3), gel(A,3),q),Fp_sub(gel(A,2), gel(B,2), q), q);
+  return gerepileuptoint(av, l);
+}
+
+/*Generic Shanks baby-step/giant-step algorithm*/
+static GEN
+gen_Shanks_log(GEN x, GEN g0,GEN q, void *E, const struct bb_group *grp)
+{
+  pari_sp av=avma,av1,lim;
+  long lbaby,i,k;
+  GEN p1,table,giant,perm,g0inv;
+  p1 = sqrti(q);
+  if (cmpiu(p1,LGBITS) >= 0)
+    pari_err_OVERFLOW("gen_Shanks_log() [order too large]");
+  lbaby = itos(p1)+1; table = cgetg(lbaby+1,t_VECSMALL);
+  g0inv = grp->pow(E,g0,gen_m1);
+  av1 = avma; lim=stack_lim(av1,2);
+  for (p1=x, i=1;;i++)
+  {
+    if (grp->equal1(p1)) { avma = av; return stoi(i-1); }
+    table[i] = grp->hash(p1); if (i==lbaby) break;
+    p1 = grp->mul(E,p1,g0inv);
+    if (low_stack(lim, stack_lim(av1,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_Shanks_log, baby = %ld", i);
+      p1 = gerepileupto(av1, p1);
+    }
+  }
+  p1 = giant = gerepileupto(av1, grp->mul(E,x,grp->pow(E, p1, gen_m1)));
+  perm = vecsmall_indexsort(table);
+  table = perm_mul(table,perm);
+  av1 = avma; lim=stack_lim(av1,2);
+  for (k=1; k<= lbaby; k++)
+  {
+    long h = grp->hash(p1);
+    long i = zv_search(table, h);
+    if (i)
+    {
+      while (table[i] == h && i) i--;
+      for (i++; i <= lbaby && table[i] == h; i++)
+      {
+        GEN v=addis(mulss(lbaby-1,k),perm[i]-1);
+        if (grp->equal(grp->pow(E,g0,v),x))
+          return gerepileuptoint(av,v);
+        else if (DEBUGLEVEL)
+          err_printf("gen_Shanks_log: false positive, giant = %ld: %lu: %Ps\n", k,h,p1);
+      }
+    }
+    p1 = grp->mul(E,p1,giant);
+
+    if (low_stack(lim, stack_lim(av1,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_Shanks_log, giant = %ld", k);
+      p1 = gerepileupto(av1, p1);
+    }
+  }
+  avma = av; return cgetg(1, t_VEC); /* no solution */
+}
+
+/*Generic discrete logarithme in a group of prime order p*/
+GEN
+gen_plog(GEN x, GEN g, GEN p, void *E, const struct bb_group *grp)
+{
+  if (grp->easylog)
+  {
+    GEN e = grp->easylog(E, x, g, p);
+    if (e) return e;
+  }
+  if (grp->equal1(x)) return gen_0;
+  if (grp->equal(x,g)) return gen_1;
+  if (expi(p)<32) return gen_Shanks_log(x,g,p,E,grp);
+  return gen_Pollard_log(x, g, p, E, grp);
+}
+
+GEN
+dlog_get_ordfa(GEN o)
+{
+  if (!o) return NULL;
+  switch(typ(o))
+  {
+    case t_INT:
+      if (signe(o) > 0) return mkvec2(o, Z_factor(o));
+      break;
+    case t_MAT:
+      if (is_Z_factorpos(o)) return mkvec2(factorback(o), o);
+      break;
+    case t_VEC:
+      if (lg(o) == 3 && signe(gel(o,1)) > 0 && is_Z_factorpos(gel(o,2))) return o;
+      break;
+  }
+  pari_err_TYPE("generic discrete logarithm (order factorization)",o);
+  return NULL; /* not reached */
+}
+GEN
+dlog_get_ord(GEN o)
+{
+  if (!o) return NULL;
+  switch(typ(o))
+  {
+    case t_INT:
+      if (signe(o) > 0) return o;
+      break;
+    case t_MAT:
+      o = factorback(o);
+      if (typ(o) == t_INT && signe(o) > 0) return o;
+      break;
+    case t_VEC:
+      if (lg(o) != 3) break;
+      o = gel(o,1);
+      if (typ(o) == t_INT && signe(o) > 0) return o;
+      break;
+  }
+  pari_err_TYPE("generic discrete logarithm (order factorization)",o);
+  return NULL; /* not reached */
+}
+
+/* grp->easylog() is an optional trapdoor function that catch easy logarithms*/
+/* Generic Pohlig-Hellman discrete logarithm*/
+/* smallest integer n such that g^n=a. Assume g has order ord */
+GEN
+gen_PH_log(GEN a, GEN g, GEN ord, void *E, const struct bb_group *grp)
+{
+  pari_sp av = avma;
+  GEN v,t0,a0,b,q,g_q,n_q,ginv0,qj,ginv;
+  GEN fa, ex;
+  long e,i,j,l;
+
+  if (grp->equal(g, a)) /* frequent special case */
+    return grp->equal1(g)? gen_0: gen_1;
+  if (grp->easylog)
+  {
+    GEN e = grp->easylog(E, a, g, ord);
+    if (e) return e;
+  }
+  v = dlog_get_ordfa(ord);
+  ord= gel(v,1);
+  fa = gel(v,2);
+  ex = gel(fa,2);
+  fa = gel(fa,1); l = lg(fa);
+  ginv = grp->pow(E,g,gen_m1);
+  v = cgetg(l, t_VEC);
+  for (i=1; i<l; i++)
+  {
+    q = gel(fa,i);
+    e = itos(gel(ex,i));
+    if (DEBUGLEVEL>5)
+      err_printf("Pohlig-Hellman: DL mod %Ps^%ld\n",q,e);
+    qj = new_chunk(e+1);
+    gel(qj,0) = gen_1;
+    gel(qj,1) = q;
+    for (j=2; j<=e; j++) gel(qj,j) = mulii(gel(qj,j-1), q);
+    t0 = diviiexact(ord, gel(qj,e));
+    a0 = grp->pow(E, a, t0);
+    ginv0 = grp->pow(E, ginv, t0); /* order q^e */
+    if (grp->equal1(ginv0))
+    {
+      gel(v,i) = mkintmod(gen_0, gen_1);
+      continue;
+    }
+    do { g_q = grp->pow(E,g, mulii(t0, gel(qj,--e))); /* order q */
+    } while (grp->equal1(g_q));
+    n_q = gen_0;
+    for (j=0;; j++)
+    { /* n_q = sum_{i<j} b_i q^i */
+      b = grp->pow(E,a0, gel(qj,e-j));
+      /* early abort: cheap and very effective */
+      if (j == 0 && !grp->equal1(grp->pow(E,b,q))) {
+        avma = av; return cgetg(1, t_VEC);
+      }
+      b = gen_plog(b, g_q, q, E, grp);
+      if (typ(b) != t_INT) { avma = av; return cgetg(1, t_VEC); }
+      n_q = addii(n_q, mulii(b, gel(qj,j)));
+      if (j == e) break;
+
+      a0 = grp->mul(E,a0, grp->pow(E,ginv0, b));
+      ginv0 = grp->pow(E,ginv0, q);
+    }
+    gel(v,i) = mkintmod(n_q, gel(qj,e+1));
+  }
+  return gerepileuptoint(av, lift(chinese1_coprime_Z(v)));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                    ORDER OF AN ELEMENT                            **/
+/**                                                                   **/
+/***********************************************************************/
+/*Find the exact order of a assuming a^o==1*/
+GEN
+gen_order(GEN a, GEN o, void *E, const struct bb_group *grp)
+{
+  pari_sp av = avma;
+  long i, l;
+  GEN m;
+
+  m = dlog_get_ordfa(o);
+  if (!m) pari_err_TYPE("gen_order [missing order]",a);
+  o = gel(m,1);
+  m = gel(m,2); l = lgcols(m);
+  for (i = l-1; i; i--)
+  {
+    GEN t, y, p = gcoeff(m,i,1);
+    long j, e = itos(gcoeff(m,i,2));
+    if (l == 2) {
+      t = gen_1;
+      y = a;
+    } else {
+      t = diviiexact(o, powiu(p,e));
+      y = grp->pow(E, a, t);
+    }
+    if (grp->equal1(y)) o = t;
+    else {
+      for (j = 1; j < e; j++)
+      {
+        y = grp->pow(E, y, p);
+        if (grp->equal1(y)) break;
+      }
+      if (j < e) {
+        if (j > 1) p = powiu(p, j);
+        o = mulii(t, p);
+      }
+    }
+  }
+  return gerepilecopy(av, o);
+}
+
+/*Find the exact order of a assuming a^o==1, return [order,factor(order)] */
+GEN
+gen_factored_order(GEN a, GEN o, void *E, const struct bb_group *grp)
+{
+  pari_sp av = avma;
+  long i, l, ind;
+  GEN m, F, P;
+
+  m = dlog_get_ordfa(o);
+  if (!m) pari_err_TYPE("gen_factored_order [missing order]",a);
+  o = gel(m,1);
+  m = gel(m,2); l = lgcols(m);
+  P = cgetg(l, t_COL); ind = 1;
+  F = cgetg(l, t_COL);
+  for (i = l-1; i; i--)
+  {
+    GEN t, y, p = gcoeff(m,i,1);
+    long j, e = itos(gcoeff(m,i,2));
+    if (l == 2) {
+      t = gen_1;
+      y = a;
+    } else {
+      t = diviiexact(o, powiu(p,e));
+      y = grp->pow(E, a, t);
+    }
+    if (grp->equal1(y)) o = t;
+    else {
+      for (j = 1; j < e; j++)
+      {
+        y = grp->pow(E, y, p);
+        if (grp->equal1(y)) break;
+      }
+      gel(P,ind) = p;
+      gel(F,ind) = utoipos(j);
+      if (j < e) {
+        if (j > 1) p = powiu(p, j);
+        o = mulii(t, p);
+      }
+      ind++;
+    }
+  }
+  setlg(P, ind);
+  setlg(F, ind);
+  return gerepilecopy(av, mkvec2(o, mkmat2(P,F)));
+}
+
+/* E has order o[1], ..., or o[#o], draw random points until all solutions
+ * but one are eliminated */
+GEN
+gen_select_order(GEN o, void *E, const struct bb_group *grp)
+{
+  pari_sp ltop = avma, btop;
+  GEN lastgood, so, vo;
+  long lo = lg(o), nbo=lo-1;
+  if (nbo == 1) return icopy(gel(o,1));
+  so = ZV_indexsort(o); /* minimize max( o[i+1] - o[i] ) */
+  vo = zero_zv(lo);
+  lastgood = gel(o, so[nbo]);
+  btop = avma;
+  for(;;)
+  {
+    GEN lasto = gen_0;
+    GEN P = grp->rand(E), t = mkvec(gen_0);
+    long i;
+    for (i = 1; i < lo; i++)
+    {
+      GEN newo = gel(o, so[i]);
+      if (vo[i]) continue;
+      t = grp->mul(E,t, grp->pow(E, P, subii(newo,lasto)));/*P^o[i]*/
+      lasto = newo;
+      if (!grp->equal1(t))
+      {
+        if (--nbo == 1) { avma=ltop; return icopy(lastgood); }
+        vo[i] = 1;
+      }
+      else
+        lastgood = lasto;
+    }
+    avma = btop;
+  }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                          n-th ROOT                              */
+/*                                                                 */
+/*******************************************************************/
+/* Assume l is prime. Return a generator of the l-th Sylow and set *zeta to an element
+ * of order l.
+ *
+ * q = l^e*r, e>=1, (r,l)=1
+ * UNCLEAN */
+static GEN
+gen_lgener(GEN l, long e, GEN r,GEN *zeta, void *E, const struct bb_group *grp)
+{
+  const pari_sp av1 = avma;
+  GEN m, m1;
+  long i;
+  for (;; avma = av1)
+  {
+    m1 = m = grp->pow(E, grp->rand(E), r);
+    if (grp->equal1(m)) continue;
+    for (i=1; i<e; i++)
+    {
+      m = grp->pow(E,m,l);
+      if (grp->equal1(m)) break;
+    }
+    if (i==e) break;
+  }
+  *zeta = m; return m1;
+}
+
+/* Let G be a cyclic group of order o>1. Returns a (random) generator */
+
+GEN
+gen_gener(GEN o, void *E, const struct bb_group *grp)
+{
+  pari_sp ltop = avma, av, lim;
+  long i, lpr;
+  GEN F, N, pr, z=NULL;
+  F = dlog_get_ordfa(o);
+  N = gel(F,1); pr = gel(F,2); lpr = lgcols(pr);
+  av = avma; lim = stack_lim(av,2);
+
+  for (i = 1; i < lpr; i++)
+  {
+    GEN l = gcoeff(pr,i,1);
+    long e = itos(gcoeff(pr,i,2));
+    GEN r = diviiexact(N,powis(l,e));
+    GEN zetan, zl = gen_lgener(l,e,r,&zetan,E,grp);
+    z = i==1 ? zl: grp->mul(E,z,zl);
+    if (low_stack(lim, stack_lim(av,2)))
+    { /* n can have lots of prime factors*/
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_gener");
+      z = gerepileupto(av, z);
+    }
+  }
+  return gerepileupto(ltop, z);
+}
+
+/* solve x^l = a , l prime in G of order q.
+ *
+ * q =  (l^e)*r, e >= 1, (r,l) = 1
+ * y is not an l-th power, hence generates the l-Sylow of G
+ * m = y^(q/l) != 1 */
+static GEN
+gen_Shanks_sqrtl(GEN a, GEN l, long e, GEN r, GEN y, GEN m,void *E,
+                 const struct bb_group *grp)
+{
+  pari_sp av = avma,lim;
+  long k;
+  GEN p1, u1, u2, v, w, z, dl;
+
+  (void)bezout(r,l,&u1,&u2);
+  v = grp->pow(E,a,u2);
+  w = grp->pow(E,v,l);
+  w = grp->mul(E,w,grp->pow(E,a,gen_m1));
+  lim = stack_lim(av,1);
+  while (!grp->equal1(w))
+  {
+    k = 0;
+    p1 = w;
+    do
+    {
+      z = p1; p1 = grp->pow(E,p1,l);
+      k++;
+    } while(!grp->equal1(p1));
+    if (k==e) { avma = av; return NULL; }
+    dl = gen_plog(z,m,l,E,grp);
+    if (typ(dl) != t_INT) { avma = av; return NULL; }
+    dl = negi(dl);
+    p1 = grp->pow(E, grp->pow(E,y, dl), powiu(l,e-k-1));
+    m = grp->pow(E,m,dl);
+    e = k;
+    v = grp->mul(E,p1,v);
+    y = grp->pow(E,p1,l);
+    w = grp->mul(E,y,w);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gen_Shanks_sqrtl");
+      gerepileall(av,4, &y,&v,&w,&m);
+    }
+  }
+  return gerepilecopy(av, v);
+}
+/* Return one solution of x^n = a in a cyclic group of order q
+ *
+ * 1) If there is no solution, return NULL.
+ *
+ * 2) If there is a solution, there are exactly m of them [m = gcd(q-1,n)].
+ * If zetan!=NULL, *zetan is set to a primitive m-th root of unity so that
+ * the set of solutions is { x*zetan^k; k=0..m-1 }
+ */
+GEN
+gen_Shanks_sqrtn(GEN a, GEN n, GEN q, GEN *zetan, void *E, const struct bb_group *grp)
+{
+  pari_sp ltop = avma, lim;
+  GEN m, u1, u2, z;
+  int is_1;
+
+  if (is_pm1(n))
+  {
+    if (zetan) *zetan = grp->pow(E,a,gen_0);
+    return signe(n) < 0? grp->pow(E,a,gen_m1): gcopy(a);
+  }
+  is_1 = grp->equal1(a);
+  if (is_1 && !zetan) return gcopy(a);
+
+  m = bezout(n,q,&u1,&u2);
+  z = grp->pow(E,a,gen_0);
+  lim = stack_lim(ltop,1);
+  if (!is_pm1(m))
+  {
+    GEN F = Z_factor(m);
+    long i, j, e;
+    GEN r, zeta, y, l;
+    pari_sp av1 = avma;
+    for (i = nbrows(F); i; i--)
+    {
+      l = gcoeff(F,i,1);
+      j = itos(gcoeff(F,i,2));
+      e = Z_pvalrem(q,l,&r);
+      y = gen_lgener(l,e,r,&zeta,E,grp);
+      if (zetan) z = grp->mul(E,z, grp->pow(E,y,powiu(l,e-j)));
+      if (!is_1) {
+        do
+        {
+          a = gen_Shanks_sqrtl(a,l,e,r,y,zeta,E,grp);
+          if (!a) { avma = ltop; return NULL;}
+        } while (--j);
+      }
+      if (low_stack(lim, stack_lim(ltop,1)))
+      { /* n can have lots of prime factors*/
+        if(DEBUGMEM>1) pari_warn(warnmem,"gen_Shanks_sqrtn");
+        gerepileall(av1, zetan? 2: 1, &a, &z);
+      }
+    }
+  }
+  if (!equalii(m, n))
+    a = grp->pow(E,a,modii(u1,q));
+  if (zetan)
+  {
+    *zetan = z;
+    gerepileall(ltop,2,&a,zetan);
+  }
+  else /* is_1 is 0: a was modified above -> gerepileupto valid */
+    a = gerepileupto(ltop, a);
+  return a;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*               structure of groups with pairing                  */
+/*                                                                 */
+/*******************************************************************/
+
+static GEN
+ellgroup_d2(GEN N, GEN d)
+{
+  GEN r = gcdii(N, d);
+  GEN F1 = gel(Z_factor(r), 1);
+  long i, j, l1 = lg(F1);
+  GEN F = cgetg(3, t_MAT);
+  gel(F,1) = cgetg(l1, t_COL);
+  gel(F,2) = cgetg(l1, t_COL);
+  for (i = 1, j = 1; i < l1; ++i)
+  {
+    long v = Z_pval(N, gel(F1, i));
+    if (v<=1) continue;
+    gcoeff(F, j  , 1) = gel(F1, i);
+    gcoeff(F, j++, 2) = stoi(v);
+  }
+  setlg(F[1],j); setlg(F[2],j);
+  return j==1 ? NULL : mkvec2(factorback(F), F);
+}
+
+GEN
+gen_ellgroup(GEN N, GEN d, GEN *pt_m, void *E, const struct bb_group *grp,
+             GEN pairorder(void *E, GEN P, GEN Q, GEN m, GEN F))
+{
+  pari_sp av = avma;
+  GEN N0, N1, F;
+  if (pt_m) *pt_m = gen_1;
+  if (is_pm1(N)) return cgetg(1,t_VEC);
+  F = ellgroup_d2(N, d);
+  if (!F) {avma = av; return mkveccopy(N);}
+  N0 = gel(F,1); N1 = diviiexact(N, N0);
+  while(1)
+  {
+    pari_sp av2 = avma;
+    GEN P, Q, d, s, t, m;
+
+    P = grp->pow(E,grp->rand(E), N1);
+    s = gen_order(P, F, E, grp); if (equalii(s, N0)) {avma = av; return mkveccopy(N);}
+
+    Q = grp->pow(E,grp->rand(E), N1);
+    t = gen_order(Q, F, E, grp); if (equalii(t, N0)) {avma = av; return mkveccopy(N);}
+
+    m = lcmii(s, t);
+    d = pairorder(E, P, Q, m, F);
+    /* structure is [N/d, d] iff m d == N0. Note that N/d = N1 m */
+    if (is_pm1(d) && equalii(m, N0)) {avma = av; return mkveccopy(N);}
+    if (equalii(mulii(m, d), N0))
+    {
+      GEN g = mkvec2(mulii(N1,m), d);
+      if (pt_m) *pt_m = m;
+      gerepileall(av,pt_m?2:1,&g,pt_m);
+      return g;
+    }
+    avma = av2;
+  }
+}
+
+GEN
+gen_ellgens(GEN D1, GEN d2, GEN m, void *E, const struct bb_group *grp,
+             GEN pairorder(void *E, GEN P, GEN Q, GEN m, GEN F))
+{
+  pari_sp ltop = avma, av;
+  GEN F, d1, dm;
+  GEN P, Q, d, s;
+  F = dlog_get_ordfa(D1);
+  d1 = gel(F, 1), dm =  diviiexact(d1,m);
+  av = avma;
+  do
+  {
+    avma = av;
+    P = grp->rand(E);
+    s = gen_order(P, F, E, grp);
+  } while (!equalii(s, d1));
+  av = avma;
+  do
+  {
+    avma = av;
+    Q = grp->rand(E);
+    d = pairorder(E, grp->pow(E, P, dm), grp->pow(E, Q, dm), m, F);
+  } while (!equalii(d, d2));
+  return gerepilecopy(ltop, mkvec2(P,Q));
+}
diff --git a/src/basemath/bibli1.c b/src/basemath/bibli1.c
new file mode 100644
index 0000000..e579b9f
--- /dev/null
+++ b/src/basemath/bibli1.c
@@ -0,0 +1,1808 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                 LLL Algorithm and close friends                **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/**             QR Factorization via Householder matrices          **/
+/********************************************************************/
+static int
+no_prec_pb(GEN x)
+{
+  return (typ(x) != t_REAL || realprec(x) > LOWDEFAULTPREC
+                           || expo(x) < BITS_IN_LONG/2);
+}
+/* Find a Householder transformation which, applied to x[k..#x], zeroes
+ * x[k+1..#x]; fill L = (mu_{i,j}). Return 0 if precision problem [obtained
+ * a 0 vector], 1 otherwise */
+static int
+FindApplyQ(GEN x, GEN L, GEN B, long k, GEN Q, long prec)
+{
+  long i, lx = lg(x)-1;
+  GEN x2, x1, xd = x + (k-1);
+
+  x1 = gel(xd,1);
+  x2 = mpsqr(x1);
+  if (k < lx)
+  {
+    long lv = lx - (k-1) + 1;
+    GEN beta, Nx, v = cgetg(lv, t_VEC);
+    for (i=2; i<lv; i++) {
+      x2 = mpadd(x2, mpsqr(gel(xd,i)));
+      gel(v,i) = gel(xd,i);
+    }
+    if (!signe(x2)) return 0;
+    Nx = gsqrt(x2, prec); if (signe(x1) < 0) setsigne(Nx, -1);
+    gel(v,1) = mpadd(x1, Nx);
+
+    if (!signe(x1))
+      beta = gtofp(x2, prec); /* make sure typ(beta) != t_INT */
+    else
+      beta = mpadd(x2, mpmul(Nx,x1));
+    gel(Q,k) = mkvec2(invr(beta), v);
+
+    togglesign(Nx);
+    gcoeff(L,k,k) = Nx;
+  }
+  else
+    gcoeff(L,k,k) = gel(x,k);
+  gel(B,k) = x2;
+  for (i=1; i<k; i++) gcoeff(L,k,i) = gel(x,i);
+  return no_prec_pb(x2);
+}
+
+/* apply Householder transformation Q = [beta,v] to r with t_INT/t_REAL
+ * coefficients, in place: r -= ((0|v).r * beta) v */
+static void
+ApplyQ(GEN Q, GEN r)
+{
+  GEN s, rd, beta = gel(Q,1), v = gel(Q,2);
+  long i, l = lg(v), lr = lg(r);
+
+  rd = r + (lr - l);
+  s = mpmul(gel(v,1), gel(rd,1));
+  for (i=2; i<l; i++) s = mpadd(s, mpmul(gel(v,i) ,gel(rd,i)));
+  s = mpmul(beta, s);
+  for (i=1; i<l; i++)
+    if (signe(gel(v,i))) gel(rd,i) = mpsub(gel(rd,i), mpmul(s, gel(v,i)));
+}
+/* apply Q[1], ..., Q[j-1] to r */
+static GEN
+ApplyAllQ(GEN Q, GEN r, long j)
+{
+  pari_sp av = avma;
+  long i;
+  r = leafcopy(r);
+  for (i=1; i<j; i++) ApplyQ(gel(Q,i), r);
+  return gerepilecopy(av, r);
+}
+
+/* same, arbitrary coefficients [20% slower for t_REAL at DEFAULTPREC] */
+static void
+RgC_ApplyQ(GEN Q, GEN r)
+{
+  GEN s, rd, beta = gel(Q,1), v = gel(Q,2);
+  long i, l = lg(v), lr = lg(r);
+
+  rd = r + (lr - l);
+  s = gmul(gel(v,1), gel(rd,1));
+  for (i=2; i<l; i++) s = gadd(s, gmul(gel(v,i) ,gel(rd,i)));
+  s = gmul(beta, s);
+  for (i=1; i<l; i++)
+    if (signe(gel(v,i))) gel(rd,i) = gsub(gel(rd,i), gmul(s, gel(v,i)));
+}
+static GEN
+RgC_ApplyAllQ(GEN Q, GEN r, long j)
+{
+  pari_sp av = avma;
+  long i;
+  r = leafcopy(r);
+  for (i=1; i<j; i++) RgC_ApplyQ(gel(Q,i), r);
+  return gerepilecopy(av, r);
+}
+
+int
+RgM_QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec)
+{
+  x = RgM_gtomp(x, prec);
+  return QR_init(x, pB, pQ, pL, prec);
+}
+
+static void
+check_householder(GEN Q)
+{
+  long i, l = lg(Q);
+  if (typ(Q) != t_VEC) pari_err_TYPE("mathouseholder", Q);
+  for (i = 1; i < l; i++)
+  {
+    GEN q = gel(Q,i), v;
+    if (typ(q) != t_VEC || lg(q) != 3) pari_err_TYPE("mathouseholder", Q);
+    v = gel(q,2);
+    if (typ(v) != t_VEC || lg(v)+i-2 != l) pari_err_TYPE("mathouseholder", Q);
+  }
+}
+
+GEN
+mathouseholder(GEN Q, GEN v)
+{
+  long l = lg(Q);
+  check_householder(Q);
+  switch(typ(v))
+  {
+    case t_MAT:
+    {
+      long lx, i;
+      GEN M = cgetg_copy(v, &lx);
+      for (i = 1; i < lx; i++) gel(M,i) = RgC_ApplyAllQ(Q, gel(v,i), l);
+      return M;
+    }
+    case t_COL: if (lg(v) == l) break;
+      /* fall through */
+    default: pari_err_TYPE("mathouseholder", v);
+  }
+  return RgC_ApplyAllQ(Q, v, l);
+}
+
+GEN
+matqr(GEN x, long flag, long prec)
+{
+  pari_sp av = avma;
+  GEN B, Q, L;
+  if (typ(x) != t_MAT) pari_err_TYPE("matqr",x);
+  if (!RgM_QR_init(x, &B,&Q,&L, prec)) pari_err_PREC("matqr");
+  if (!flag) Q = shallowtrans(mathouseholder(Q, matid(lg(x)-1)));
+  return gerepilecopy(av, mkvec2(Q, shallowtrans(L)));
+}
+
+/* compute B = | x[k] |^2, Q = Householder transforms and L = mu_{i,j} */
+int
+QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec)
+{
+  long j, k = lg(x)-1;
+  GEN B = cgetg(k+1, t_VEC), Q = cgetg(k, t_VEC), L = zeromatcopy(k,k);
+  for (j=1; j<=k; j++)
+  {
+    GEN r = gel(x,j);
+    if (j > 1) r = ApplyAllQ(Q, r, j);
+    if (!FindApplyQ(r, L, B, j, Q, prec)) return 0;
+  }
+  *pB = B; *pQ = Q; *pL = L; return 1;
+}
+/* x a square t_MAT with t_INT / t_REAL entries and maximal rank. Return
+ * qfgaussred(x~*x) */
+GEN
+gaussred_from_QR(GEN x, long prec)
+{
+  long j, k = lg(x)-1;
+  GEN B, Q, L;
+  if (!QR_init(x, &B,&Q,&L, prec)) return NULL;
+  for (j=1; j<k; j++)
+  {
+    GEN m = gel(L,j), invNx = invr(gel(m,j));
+    long i;
+    gel(m,j) = gel(B,j);
+    for (i=j+1; i<=k; i++) gel(m,i) = mpmul(invNx, gel(m,i));
+  }
+  gcoeff(L,j,j) = gel(B,j);
+  return shallowtrans(L);
+}
+GEN
+R_from_QR(GEN x, long prec)
+{
+  GEN B, Q, L;
+  if (!QR_init(x, &B,&Q,&L, prec)) return NULL;
+  return shallowtrans(L);
+}
+
+/********************************************************************/
+/**             QR Factorization via Gram-Schmidt                  **/
+/********************************************************************/
+
+#if 0
+/* return Gram-Schmidt orthogonal basis (f) associated to (e), B is the
+ * vector of the (f_i . f_i)*/
+GEN
+gram_schmidt(GEN e, GEN *ptB)
+{
+  long i,j,lx = lg(e);
+  GEN f = RgM_shallowcopy(e), B, iB;
+
+  B = cgetg(lx, t_VEC);
+  iB= cgetg(lx, t_VEC);
+
+  for (i=1;i<lx;i++)
+  {
+    GEN p1 = NULL;
+    pari_sp av = avma;
+    for (j=1; j<i; j++)
+    {
+      GEN mu = gmul(RgV_dotproduct(gel(e,i),gel(f,j)), gel(iB,j));
+      GEN p2 = gmul(mu, gel(f,j));
+      p1 = p1? gadd(p1,p2): p2;
+    }
+    p1 = p1? gerepileupto(av, gsub(gel(e,i), p1)): gel(e,i);
+    gel(f,i) = p1;
+    gel(B,i) = RgV_dotsquare(gel(f,i));
+    gel(iB,i) = ginv(gel(B,i));
+  }
+  *ptB = B; return f;
+}
+#endif
+
+/********************************************************************/
+/**                                                                **/
+/**                          LLL ALGORITHM                         **/
+/**                                                                **/
+/********************************************************************/
+/* Def: a matrix M is said to be -partially reduced- if | m1 +- m2 | >= |m1|
+ * for any two columns m1 != m2, in M.
+ *
+ * Input: an integer matrix mat whose columns are linearly independent. Find
+ * another matrix T such that mat * T is partially reduced.
+ *
+ * Output: mat * T if flag = 0;  T if flag != 0,
+ *
+ * This routine is designed to quickly reduce lattices in which one row
+ * is huge compared to the other rows.  For example, when searching for a
+ * polynomial of degree 3 with root a mod N, the four input vectors might
+ * be the coefficients of
+ *     X^3 - (a^3 mod N), X^2 - (a^2 mod N), X - (a mod N), N.
+ * All four constant coefficients are O(p) and the rest are O(1). By the
+ * pigeon-hole principle, the coefficients of the smallest vector in the
+ * lattice are O(p^(1/4)), hence significant reduction of vector lengths
+ * can be anticipated.
+ *
+ * An improved algorithm would look only at the leading digits of dot*.  It
+ * would use single-precision calculations as much as possible.
+ *
+ * Original code: Peter Montgomery (1994) */
+static GEN
+lllintpartialall(GEN m, long flag)
+{
+  const long ncol = lg(m)-1;
+  const pari_sp av = avma;
+  GEN tm1, tm2, mid;
+
+  if (ncol <= 1) return flag? matid(ncol): gcopy(m);
+
+  tm1 = flag? matid(ncol): NULL;
+  {
+    const pari_sp av2 = avma;
+    GEN dot11 = ZV_dotsquare(gel(m,1));
+    GEN dot22 = ZV_dotsquare(gel(m,2));
+    GEN dot12 = ZV_dotproduct(gel(m,1), gel(m,2));
+    GEN tm  = matid(2); /* For first two columns only */
+
+    int progress = 0;
+    long npass2 = 0;
+
+/* Row reduce the first two columns of m. Our best result so far is
+ * (first two columns of m)*tm.
+ *
+ * Initially tm = 2 x 2 identity matrix.
+ * Inner products of the reduced matrix are in dot11, dot12, dot22. */
+    while (npass2 < 2 || progress)
+    {
+      GEN dot12new, q = diviiround(dot12, dot22);
+
+      npass2++; progress = signe(q);
+      if (progress)
+      {/* Conceptually replace (v1, v2) by (v1 - q*v2, v2), where v1 and v2
+        * represent the reduced basis for the first two columns of the matrix.
+        * We do this by updating tm and the inner products. */
+        togglesign(q);
+        dot12new = addii(dot12, mulii(q, dot22));
+        dot11 = addii(dot11, mulii(q, addii(dot12, dot12new)));
+        dot12 = dot12new;
+        ZC_lincomb1_inplace(gel(tm,1), gel(tm,2), q);
+      }
+
+      /* Interchange the output vectors v1 and v2.  */
+      swap(dot11,dot22);
+      swap(gel(tm,1), gel(tm,2));
+
+      /* Occasionally (including final pass) do garbage collection.  */
+      if ((npass2 & 0xff) == 0 || !progress)
+        gerepileall(av2, 4, &dot11,&dot12,&dot22,&tm);
+    } /* while npass2 < 2 || progress */
+
+    {
+      long i;
+      GEN det12 = subii(mulii(dot11, dot22), sqri(dot12));
+
+      mid = cgetg(ncol+1, t_MAT);
+      for (i = 1; i <= 2; i++)
+      {
+        GEN tmi = gel(tm,i);
+        if (tm1)
+        {
+          GEN tm1i = gel(tm1,i);
+          gel(tm1i,1) = gel(tmi,1);
+          gel(tm1i,2) = gel(tmi,2);
+        }
+        gel(mid,i) = ZC_lincomb(gel(tmi,1),gel(tmi,2), gel(m,1),gel(m,2));
+      }
+      for (i = 3; i <= ncol; i++)
+      {
+        GEN c = gel(m,i);
+        GEN dot1i = ZV_dotproduct(gel(mid,1), c);
+        GEN dot2i = ZV_dotproduct(gel(mid,2), c);
+       /* ( dot11  dot12 ) (q1)   ( dot1i )
+        * ( dot12  dot22 ) (q2) = ( dot2i )
+        *
+        * Round -q1 and -q2 to nearest integer. Then compute
+        *   c - q1*mid[1] - q2*mid[2].
+        * This will be approximately orthogonal to the first two vectors in
+        * the new basis. */
+        GEN q1neg = subii(mulii(dot12, dot2i), mulii(dot22, dot1i));
+        GEN q2neg = subii(mulii(dot12, dot1i), mulii(dot11, dot2i));
+
+        q1neg = diviiround(q1neg, det12);
+        q2neg = diviiround(q2neg, det12);
+        if (tm1)
+        {
+          gcoeff(tm1,1,i) = addii(mulii(q1neg, gcoeff(tm,1,1)),
+                                  mulii(q2neg, gcoeff(tm,1,2)));
+          gcoeff(tm1,2,i) = addii(mulii(q1neg, gcoeff(tm,2,1)),
+                                  mulii(q2neg, gcoeff(tm,2,2)));
+        }
+        gel(mid,i) = ZC_add(c, ZC_lincomb(q1neg,q2neg, gel(mid,1),gel(mid,2)));
+      } /* for i */
+    } /* local block */
+  }
+  if (DEBUGLEVEL>6)
+  {
+    if (tm1) err_printf("tm1 = %Ps",tm1);
+    err_printf("mid = %Ps",mid); /* = m * tm1 */
+  }
+  gerepileall(av, tm1? 2: 1, &mid, &tm1);
+  {
+   /* For each pair of column vectors v and w in mid * tm2,
+    * try to replace (v, w) by (v, v - q*w) for some q.
+    * We compute all inner products and check them repeatedly. */
+    const pari_sp av3 = avma, lim = stack_lim(av3,2);
+    long i, j, npass = 0, e = LONG_MAX;
+    GEN dot = cgetg(ncol+1, t_MAT); /* scalar products */
+
+    tm2 = matid(ncol);
+    for (i=1; i <= ncol; i++)
+    {
+      gel(dot,i) = cgetg(ncol+1,t_COL);
+      for (j=1; j <= i; j++)
+        gcoeff(dot,j,i) = gcoeff(dot,i,j) = ZV_dotproduct(gel(mid,i),gel(mid,j));
+    }
+    for(;;)
+    {
+      long reductions = 0, olde = e;
+      for (i=1; i <= ncol; i++)
+      {
+        long ijdif;
+        for (ijdif=1; ijdif < ncol; ijdif++)
+        {
+          long d, k1, k2;
+          GEN codi, q;
+
+          j = i + ijdif; if (j > ncol) j -= ncol;
+          /* let k1, resp. k2,  index of larger, resp. smaller, column */
+          if (cmpii(gcoeff(dot,i,i), gcoeff(dot,j,j)) > 0) { k1 = i; k2 = j; }
+          else                                             { k1 = j; k2 = i; }
+          codi = gcoeff(dot,k2,k2);
+          q = signe(codi)? diviiround(gcoeff(dot,k1,k2), codi): gen_0;
+          if (!signe(q)) continue;
+
+          /* Try to subtract a multiple of column k2 from column k1.  */
+          reductions++; togglesign_safe(&q);
+          ZC_lincomb1_inplace(gel(tm2,k1), gel(tm2,k2), q);
+          ZC_lincomb1_inplace(gel(dot,k1), gel(dot,k2), q);
+          gcoeff(dot,k1,k1) = addii(gcoeff(dot,k1,k1),
+                                    mulii(q, gcoeff(dot,k2,k1)));
+          for (d = 1; d <= ncol; d++) gcoeff(dot,k1,d) = gcoeff(dot,d,k1);
+        } /* for ijdif */
+        if (low_stack(lim, stack_lim(av3,2)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"lllintpartialall");
+          gerepileall(av3, 2, &dot,&tm2);
+        }
+      } /* for i */
+      if (!reductions) break;
+      e = 0;
+      for (i = 1; i <= ncol; i++) e += expi( gcoeff(dot,i,i) );
+      if (e == olde) break;
+      if (DEBUGLEVEL>6)
+      {
+        npass++;
+        err_printf("npass = %ld, red. last time = %ld, log_2(det) ~ %ld\n\n",
+                    npass, reductions, e);
+      }
+    } /* for(;;)*/
+
+   /* Sort columns so smallest comes first in m * tm1 * tm2.
+    * Use insertion sort. */
+    for (i = 1; i < ncol; i++)
+    {
+      long j, s = i;
+
+      for (j = i+1; j <= ncol; j++)
+        if (cmpii(gcoeff(dot,s,s),gcoeff(dot,j,j)) > 0) s = j;
+      if (i != s)
+      { /* Exchange with proper column; only the diagonal of dot is updated */
+        swap(gel(tm2,i), gel(tm2,s));
+        swap(gcoeff(dot,i,i), gcoeff(dot,s,s));
+      }
+    }
+    i = 1;
+    while (i <= ncol && !signe(gcoeff(dot,i,i))) i++;
+    if (i > 1)
+    {
+      tm2 += (i - 1);
+      tm2[0] = evaltyp(t_MAT)|evallg(ncol - i);
+    }
+  } /* local block */
+  return gerepileupto(av, ZM_mul(tm1? tm1: mid, tm2));
+}
+
+GEN
+lllintpartial(GEN mat) { return lllintpartialall(mat,1); }
+
+GEN
+lllintpartial_inplace(GEN mat) { return lllintpartialall(mat,0); }
+
+/********************************************************************/
+/**                                                                **/
+/**                    COPPERSMITH ALGORITHM                       **/
+/**           Finding small roots of univariate equations.         **/
+/**                                                                **/
+/********************************************************************/
+
+static int
+check_condition(double beta, double tau, double rho, long d, long delta, long t)
+{
+  long dim = d*delta + t;
+  double cond = d*delta*(delta+1)/2 - beta*delta*dim
+    + rho*delta*(delta - 1) / 2
+    + rho * t * delta + tau*dim*(dim - 1)/2;
+
+  if (DEBUGLEVEL >= 4)
+    err_printf("delta = %d, t = %d, cond = %.1lf\n", delta, t, cond);
+
+  return (cond <= 0);
+}
+
+static void
+choose_params(GEN P, GEN N, GEN X, GEN B, long *pdelta, long *pt)
+{
+  long d = degpol(P);
+  GEN P0 = leading_term(P);
+  double logN = gtodouble(glog(N, DEFAULTPREC));
+  double tau, beta, rho;
+  long delta, t;
+  tau = gtodouble(glog(X, DEFAULTPREC)) / logN;
+  beta = B? gtodouble(glog(B, DEFAULTPREC)) / logN: 1.;
+  if (tau >= beta * beta / d)
+    pari_err_OVERFLOW("zncoppersmith [bound too large]");
+  /* TODO : remove P0 completely ! */
+  rho = gtodouble(glog(P0, DEFAULTPREC)) / logN;
+
+  /* Enumerate (delta,t) by increasing dimension of resulting lattice.
+   * Not subtle, but o(1) for computing time */
+  t = d; delta = 0;
+  for(;;)
+  {
+    t += d * delta + 1; delta = 0;
+    while (t >= 0) {
+      if (check_condition(beta, tau, rho, d, delta, t)) {
+        *pdelta = delta; *pt = t; return;
+      }
+      delta++; t -= d;
+    }
+  }
+}
+
+static int
+sol_OK(GEN x, GEN N, GEN B)
+{ return B? (cmpii(gcdii(x,N),B) >= 0): !signe(remii(x,N)); }
+/* deg(P) > 0, x >= 0. Find all j such that gcd(P(j), N) >= B, |j| <= x */
+static GEN
+do_exhaustive(GEN P, GEN N, long x, GEN B)
+{
+  GEN Pe, Po, sol = vecsmalltrunc_init(2*x + 2);
+  pari_sp av;
+  long j;
+  RgX_even_odd(P, &Pe,&Po); av = avma;
+  if (sol_OK(gel(P,2), N,B)) vecsmalltrunc_append(sol, 0);
+  for (j = 1; j <= x; j++, avma = av)
+  {
+    GEN j2 = sqru(j), E = FpX_eval(Pe,j2,N), O = FpX_eval(Po,j2,N);
+    if (sol_OK(addmuliu(E,O,j), N,B)) vecsmalltrunc_append(sol, j);
+    if (sol_OK(submuliu(E,O,j), N,B)) vecsmalltrunc_append(sol,-j);
+  }
+  vecsmall_sort(sol); return zv_to_ZV(sol);
+}
+
+/* General Coppersmith, look for a root x0 <= p, p >= B, p | N, |x0| <= X.
+ * B = N coded as NULL */
+GEN
+zncoppersmith(GEN P0, GEN N, GEN X, GEN B)
+{
+  GEN Q, R, N0, M, sh, short_pol, *Xpowers, sol, nsp, P, Z;
+  long delta, i, j, row, d, l, dim, t, bnd = 10;
+  const ulong X_SMALL = 1000;
+
+  pari_sp av = avma;
+
+  if (typ(P0) != t_POL) pari_err_TYPE("zncoppersmith",P0);
+  if (typ(N) != t_INT) pari_err_TYPE("zncoppersmith",N);
+  if (typ(X) != t_INT) {
+    X = gfloor(X);
+    if (typ(X) != t_INT) pari_err_TYPE("zncoppersmith",X);
+  }
+  if (signe(X) < 0) pari_err_DOMAIN("zncoppersmith", "X", "<", gen_0, X);
+  d = degpol(P0);
+  if (d == 0) { avma = av; return cgetg(1, t_VEC); }
+  if (d < 0) pari_err_ROOTS0("zncoppersmith");
+  if (B && typ(B) != t_INT) B = gceil(B);
+
+  if (cmpiu(X, X_SMALL) <= 0)
+    return gerepileupto(av, do_exhaustive(P0, N, itos(X), B));
+
+  if (B && equalii(B,N)) B = NULL;
+  if (B) bnd = 1; /* bnd-hack is only for the case B = N */
+  P = leafcopy(P0);
+  if (!gequal1(gel(P,d+2)))
+  {
+    GEN r, z;
+    gel(P,d+2) = bezout(gel(P,d+2), N, &z, &r);
+    for (j = 0; j < d; j++) gel(P,j+2) = modii(mulii(gel(P,j+2), z), N);
+  }
+  if (DEBUGLEVEL >= 2) err_printf("Modified P: %Ps\n", P);
+
+  choose_params(P, N, X, B, &delta, &t);
+  if (DEBUGLEVEL >= 2)
+    err_printf("Init: trying delta = %d, t = %d\n", delta, t);
+  for(;;)
+  {
+    dim = d * delta + t;
+
+    /* TODO: In case of failure do not recompute the full vector */
+    Xpowers = (GEN*)new_chunk(dim + 1);
+    Xpowers[0] = gen_1;
+    for (j = 1; j <= dim; j++) Xpowers[j] = mulii(Xpowers[j-1], X);
+
+    /* TODO: in case of failure, use the part of the matrix already computed */
+    M = zeromatcopy(dim,dim);
+
+    /* Rows of M correspond to the polynomials
+     * N^delta, N^delta Xi, ... N^delta (Xi)^d-1,
+     * N^(delta-1)P(Xi), N^(delta-1)XiP(Xi), ... N^(delta-1)P(Xi)(Xi)^d-1,
+     * ...
+     * P(Xi)^delta, XiP(Xi)^delta, ..., P(Xi)^delta(Xi)^t-1 */
+    for (j = 1; j <= d;   j++) gcoeff(M, j, j) = gel(Xpowers,j-1);
+
+    /* P-part */
+    if (delta) row = d + 1; else row = 0;
+
+    Q = P;
+    for (i = 1; i < delta; i++)
+    {
+      for (j = 0; j < d; j++,row++)
+        for (l = j + 1; l <= row; l++)
+          gcoeff(M, l, row) = mulii(Xpowers[l-1], gel(Q,l-j+1));
+      Q = ZX_mul(Q, P);
+    }
+    for (j = 0; j < t; row++, j++)
+      for (l = j + 1; l <= row; l++)
+        gcoeff(M, l, row) = mulii(Xpowers[l-1], gel(Q,l-j+1));
+
+    /* N-part */
+    row = dim - t; N0 = N;
+    while (row >= 1)
+    {
+      for (j = 0; j < d; j++,row--)
+        for (l = 1; l <= row; l++)
+          gcoeff(M, l, row) = mulii(gmael(M, row, l), N0);
+      if (row >= 1) N0 = mulii(N0, N);
+    }
+    /* Z is the upper bound for the L^1 norm of the polynomial,
+       ie. N^delta if B = N, B^delta otherwise */
+    if (B) Z = powiu(B, delta); else Z = N0;
+
+    if (DEBUGLEVEL >= 2)
+    {
+      if (DEBUGLEVEL >= 6) err_printf("Matrix to be reduced:\n%Ps\n", M);
+      err_printf("Entering LLL\nbitsize bound: %ld\n", expi(Z));
+      err_printf("expected shvector bitsize: %ld\n", expi(ZM_det_triangular(M))/dim);
+    }
+
+    sh = ZM_lll(M, 0.75, LLL_INPLACE);
+    /* Take the first vector if it is non constant */
+    short_pol = gel(sh,1);
+    if (ZV_isscalar(short_pol)) short_pol = gel(sh, 2);
+
+    nsp = gen_0;
+    for (j = 1; j <= dim; j++) nsp = addii(nsp, absi(gel(short_pol,j)));
+
+    if (DEBUGLEVEL >= 2)
+    {
+      err_printf("Candidate: %Ps\n", short_pol);
+      err_printf("bitsize Norm: %ld\n", expi(nsp));
+      err_printf("bitsize bound: %ld\n", expi(mului(bnd, Z)));
+    }
+    if (cmpii(nsp, mului(bnd, Z)) < 0) break; /* SUCCESS */
+
+    /* Failed with the precomputed or supplied value */
+    t++; if (t == d) { delta++; t = 1; }
+    if (DEBUGLEVEL >= 2)
+      err_printf("Increasing dim, delta = %d t = %d\n", delta, t);
+  }
+  bnd = itos(divii(nsp, Z)) + 1;
+
+  while (!signe(gel(short_pol,dim))) dim--;
+
+  R = cgetg(dim + 2, t_POL); R[1] = P[1];
+  for (j = 1; j <= dim; j++)
+    gel(R,j+1) = diviiexact(gel(short_pol,j), Xpowers[j-1]);
+  gel(R,2) = subii(gel(R,2), mului(bnd - 1, N0));
+
+  sol = cgetg(1, t_VEC);
+  for (i = -bnd + 1; i < bnd; i++)
+  {
+    GEN r = nfrootsQ(R);
+    if (DEBUGLEVEL >= 2) err_printf("Roots: %Ps\n", r);
+    for (j = 1; j < lg(r); j++)
+    {
+      GEN z = gel(r,j);
+      if (typ(z) == t_INT && sol_OK(FpX_eval(P,z,N), N,B))
+        sol = shallowconcat(sol, z);
+    }
+    if (i < bnd) gel(R,2) = addii(gel(R,2), Z);
+  }
+  return gerepileupto(av, ZV_sort_uniq(sol));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                   LINEAR & ALGEBRAIC DEPENDENCE                **/
+/**                                                                **/
+/********************************************************************/
+
+static int
+real_indep(GEN re, GEN im, long bitprec)
+{
+  GEN p1 = gsub(gmul(gel(re,1),gel(im,2)),
+                gmul(gel(re,2),gel(im,1)));
+  return (!gequal0(p1) && gexpo(p1) > - bitprec);
+}
+
+GEN
+lindep2(GEN x, long bit)
+{
+  long tx=typ(x), lx=lg(x), ly, i, j;
+  pari_sp av = avma;
+  GEN re, im, M;
+
+  if (! is_vec_t(tx)) pari_err_TYPE("lindep2",x);
+  if (lx<=2)
+  {
+    if (lx == 2 && gequal0(x)) return mkcol(gen_1);
+    return cgetg(1,t_COL);
+  }
+  if (bit < 0) pari_err_DOMAIN("lindep2", "accuracy", "<", gen_0, stoi(bit));
+  if (!bit)
+  {
+    bit = gprecision(x);
+    if (!bit)
+    {
+      x = primpart(x);
+      bit = 32 + gexpo(x);
+    }
+    else
+      bit = (long)prec2nbits_mul(bit, 0.8);
+  }
+  else
+    bit = (long) (bit/LOG10_2);
+  re = real_i(x);
+  im = imag_i(x);
+  /* independent over R ? */
+  if (lx == 3 && real_indep(re,im,bit)) { avma = av; return cgetg(1, t_COL); }
+  if (gequal0(im)) im = NULL;
+  ly = im? lx+2: lx+1;
+  M = cgetg(lx,t_MAT);
+  for (i=1; i<lx; i++)
+  {
+    GEN c = cgetg(ly,t_COL); gel(M,i) = c;
+    for (j=1; j<lx; j++) gel(c,j) = (i==j)? gen_1: gen_0;
+    gel(c,lx)           = gtrunc2n(gel(re,i), bit);
+    if (im) gel(c,lx+1) = gtrunc2n(gel(im,i), bit);
+  }
+  M = ZM_lll(M, 0.99, LLL_INPLACE);
+  M = gel(M,1);
+  M[0] = evaltyp(t_COL) | evallg(lx);
+  return gerepilecopy(av, M);
+}
+
+void
+init_dalloc(void)
+{ /* correct alignment for dalloc */
+  (void)new_chunk((avma % sizeof(double)) / sizeof(long));
+}
+
+double *
+dalloc(size_t n)
+{
+  return (double*)new_chunk(n / sizeof(long));
+}
+
+/* x is a vector of elts of a p-adic field */
+GEN
+padic_lindep(GEN x)
+{
+  long i, j, prec = LONG_MAX, nx = lg(x)-1, v;
+  pari_sp av = avma;
+  GEN p = NULL, pn, m, a;
+
+  if (nx < 2) return cgetg(1,t_COL);
+  for (i=1; i<=nx; i++)
+  {
+    GEN c = gel(x,i), q;
+    if (typ(c) != t_PADIC) continue;
+
+    j = precp(c); if (j < prec) prec = j;
+    q = gel(c,2);
+    if (!p) p = q; else if (!equalii(p, q)) pari_err_MODULUS("padic_lindep", p, q);
+  }
+  if (!p) pari_err_TYPE("padic_lindep [not a p-adic vector]",x);
+  v = gvaluation(x,p); pn = powiu(p,prec);
+  if (v) x = gmul(x, powis(p, -v));
+  x = RgV_to_FpV(x, pn);
+
+  a = negi(gel(x,1));
+  m = cgetg(nx,t_MAT);
+  for (i=1; i<nx; i++)
+  {
+    GEN c = zerocol(nx);
+    gel(c,1+i) = a;
+    gel(c,1) = gel(x,i+1);
+    gel(m,i) = c;
+  }
+  m = ZM_lll(ZM_hnfmodid(m, pn), 0.99, LLL_INPLACE);
+  return gerepilecopy(av, gel(m,1));
+}
+/* x is a vector of t_POL/t_SER */
+GEN
+Xadic_lindep(GEN x)
+{
+  long i, prec = LONG_MAX, deg = 0, lx = lg(x), vx, v;
+  pari_sp av = avma;
+  GEN m;
+
+  if (lx == 1) return cgetg(1,t_COL);
+  vx = gvar(x);
+  v = gvaluation(x, pol_x(vx));
+  if (v) x = gmul(x, monomial(gen_1, -v, vx)); else x = shallowcopy(x);
+  /* all t_SER have valuation >= 0 */
+  for (i=1; i<lx; i++)
+  {
+    GEN c = gel(x,i);
+    if (gvar(c) != vx) { gel(x,i) = scalarpol_shallow(c, vx); continue; }
+    switch(typ(c))
+    {
+      case t_POL: deg = maxss(deg, degpol(c)); break;
+      case t_RFRAC: pari_err_TYPE("Xadic_lindep", c);
+      case t_SER:
+        prec = minss(prec, valp(c)+lg(c)-2);
+        gel(x,i) = ser2rfrac_i(c);
+    }
+  }
+  if (prec == LONG_MAX) prec = deg+1;
+  m = RgXV_to_RgM(x, prec);
+  return gerepileupto(av, deplin(m));
+}
+
+GEN
+lindep(GEN x) { return lindep2(x, 0); }
+
+GEN
+lindep0(GEN x,long bit)
+{
+  long i, tx = typ(x);
+  if (tx == t_MAT) return deplin(x);
+  if (! is_vec_t(tx)) pari_err_TYPE("lindep",x);
+  for (i = 1; i < lg(x); i++)
+    switch(typ(gel(x,i)))
+    {
+      case t_PADIC: return padic_lindep(x);
+      case t_POL:
+      case t_RFRAC:
+      case t_SER: return Xadic_lindep(x);
+    }
+  return lindep2(x, bit);
+}
+
+GEN
+algdep0(GEN x, long n, long bit)
+{
+  long tx = typ(x), i;
+  pari_sp av;
+  GEN y;
+
+  if (! is_scalar_t(tx)) pari_err_TYPE("algdep0",x);
+  if (tx==t_POLMOD) { y = RgX_copy(gel(x,1)); setvarn(y,0); return y; }
+  if (gequal0(x)) return pol_x(0);
+  if (n <= 0)
+  {
+    if (!n) return gen_1;
+    pari_err_DOMAIN("algdep", "degree", "<", gen_0, stoi(n));
+  }
+
+  av = avma; y = cgetg(n+2,t_COL);
+  gel(y,1) = gen_1;
+  gel(y,2) = x; /* n >= 1 */
+  for (i=3; i<=n+1; i++) gel(y,i) = gmul(gel(y,i-1),x);
+  if (typ(x) == t_PADIC)
+    y = padic_lindep(y);
+  else
+    y = lindep2(y, bit);
+  if (lg(y) == 1) pari_err(e_DOMAIN,"algdep", "degree(x)",">", stoi(n), x);
+  y = RgV_to_RgX(y, 0);
+  if (signe(leading_term(y)) > 0) return gerepilecopy(av, y);
+  return gerepileupto(av, ZX_neg(y));
+}
+
+GEN
+algdep(GEN x, long n)
+{
+  return algdep0(x,n,0);
+}
+
+GEN
+seralgdep(GEN s, long p, long r)
+{
+  pari_sp av = avma;
+  long vy, i, m, n, prec;
+  GEN S, v, D;
+
+  if (typ(s) != t_SER) pari_err_TYPE("seralgdep",s);
+  if (p <= 0) pari_err_DOMAIN("seralgdep", "p", "<=", gen_0, stoi(p));
+  if (r < 0) pari_err_DOMAIN("seralgdep", "r", "<", gen_0, stoi(r));
+  if (is_bigint(addiu(muluu(p, r), 1))) pari_err_OVERFLOW("seralgdep");
+  vy = varn(s);
+  if (!vy) pari_err_PRIORITY("seralgdep", s, ">", 0);
+  r++; p++;
+  prec = valp(s) + lg(s)-2;
+  if (r > prec) r = prec;
+  S = cgetg(p+1, t_VEC); gel(S, 1) = s;
+  for (i = 2; i <= p; i++) gel(S,i) = gmul(gel(S,i-1), s);
+  v = cgetg(r*p+1, t_VEC); /* v[r*n+m+1] = s^n * y^m */
+  /* n = 0 */
+  for (m = 0; m < r; m++)
+    gel(v, m + 1) = monomial(gen_1, m, vy);
+  for(n=1; n < p; n++)
+    for (m = 0; m < r; m++)
+    {
+      GEN c = gel(S,n);
+      if (m)
+      {
+        c = shallowcopy(c);
+        setvalp(c, valp(c) + m);
+      }
+      gel(v, r*n + m + 1) = c;
+    }
+  D = Xadic_lindep(v);
+  if (lg(D) == 1) { avma = av; return gen_0; }
+  v = cgetg(p+1, t_VEC);
+  for (n = 0; n < p; n++)
+    gel(v, n+1) = RgV_to_RgX(vecslice(D, r*n+1, r*n+r), vy);
+  return gerepilecopy(av, RgV_to_RgX(v, 0));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                              MINIM                             **/
+/**                                                                **/
+/********************************************************************/
+void
+minim_alloc(long n, double ***q, GEN *x, double **y,  double **z, double **v)
+{
+  long i, s;
+
+  *x = cgetg(n, t_VECSMALL);
+  *q = (double**) new_chunk(n);
+  s = n * sizeof(double);
+  init_dalloc();
+  *y = dalloc(s);
+  *z = dalloc(s);
+  *v = dalloc(s);
+  for (i=1; i<n; i++) (*q)[i] = dalloc(s);
+}
+
+/* If V depends linearly from the columns of the matrix, return 0.
+ * Otherwise, update INVP and L and return 1. No GC. */
+static int
+addcolumntomatrix(GEN V, GEN invp, GEN L)
+{
+  GEN a = RgM_zc_mul(invp,V);
+  long i,j,k, n = lg(invp);
+
+  if (DEBUGLEVEL>6)
+  {
+    err_printf("adding vector = %Ps\n",V);
+    err_printf("vector in new basis = %Ps\n",a);
+    err_printf("list = %Ps\n",L);
+    err_printf("base change matrix =\n%Ps\n", invp);
+  }
+  k = 1; while (k<n && (L[k] || gequal0(gel(a,k)))) k++;
+  if (k == n) return 0;
+  L[k] = 1;
+  for (i=k+1; i<n; i++) gel(a,i) = gdiv(gneg_i(gel(a,i)),gel(a,k));
+  for (j=1; j<=k; j++)
+  {
+    GEN c = gel(invp,j), ck = gel(c,k);
+    if (gequal0(ck)) continue;
+    gel(c,k) = gdiv(ck, gel(a,k));
+    if (j==k)
+      for (i=k+1; i<n; i++)
+        gel(c,i) = gmul(gel(a,i), ck);
+    else
+      for (i=k+1; i<n; i++)
+        gel(c,i) = gadd(gel(c,i), gmul(gel(a,i), ck));
+  }
+  return 1;
+}
+
+struct qfvec
+{
+  GEN a, r, u;
+};
+
+static void
+err_minim(GEN a)
+{
+  pari_err_DOMAIN("minim0","form","is not",
+                  strtoGENstr("positive definite"),a);
+}
+
+static void
+forqfvec_init_dolll(struct qfvec *qv, GEN a, long dolll)
+{
+  GEN r, u;
+  if (typ(a) != t_MAT || !RgM_is_ZM(a)) pari_err_TYPE("qfminim",a);
+  if (dolll)
+  {
+    u = lllgramint(a);
+    if (lg(u) != lg(a)) err_minim(a);
+    a = qf_apply_ZM(a,u);
+  } else u = NULL;
+  qv->a = RgM_gtofp(a, DEFAULTPREC);
+  r = qfgaussred_positive(qv->a);
+  if (!r)
+  {
+    r = qfgaussred_positive(a); /* exact computation */
+    if (!r) err_minim(a);
+    r = RgM_gtofp(r, DEFAULTPREC);
+  }
+  qv->r = r;
+  qv->u = u;
+}
+
+static void
+forqfvec_init(struct qfvec *qv, GEN a)
+{
+  forqfvec_init_dolll(qv, a, 1);
+}
+
+static void
+forqfvec(void *E, long (*fun)(void *, GEN, GEN, double), struct qfvec *qv, GEN BORNE)
+{
+  GEN x, a = qv->a, r = qv->r, u = qv->u;
+  long n = lg(a), i, j, k;
+  double p,BOUND,*v,*y,*z,**q;
+  const double eps = 0.0001;
+  if (!BORNE) BORNE = gen_0;
+  else
+  {
+    BORNE = gfloor(BORNE);
+    if (typ(BORNE) != t_INT) pari_err_TYPE("minim0",BORNE);
+  }
+  if (n == 1) return;
+  minim_alloc(n, &q, &x, &y, &z, &v);
+  n--;
+  for (j=1; j<=n; j++)
+  {
+    v[j] = rtodbl(gcoeff(r,j,j));
+    for (i=1; i<j; i++) q[i][j] = rtodbl(gcoeff(r,i,j));
+  }
+
+  if (gequal0(BORNE))
+  {
+    double c;
+    p = rtodbl(gcoeff(a,1,1));
+    for (i=2; i<=n; i++) { c = rtodbl(gcoeff(a,i,i)); if (c < p) p = c; }
+    BORNE = roundr(dbltor(p));
+  }
+  else
+    p = gtodouble(BORNE);
+  BOUND = p * (1 + eps);
+  if (BOUND == p) pari_err_PREC("minim0");
+
+  k = n; y[n] = z[n] = 0;
+  x[n] = (long)sqrt(BOUND/v[n]);
+  for(;;x[1]--)
+  {
+    do
+    {
+      if (k>1)
+      {
+        long l = k-1;
+        z[l] = 0;
+        for (j=k; j<=n; j++) z[l] += q[l][j]*x[j];
+        p = (double)x[k] + z[k];
+        y[l] = y[k] + p*p*v[k];
+        x[l] = (long)floor(sqrt((BOUND-y[l])/v[l])-z[l]);
+        k = l;
+      }
+      for(;;)
+      {
+        p = (double)x[k] + z[k];
+        if (y[k] + p*p*v[k] <= BOUND) break;
+        k++; x[k]--;
+      }
+    } while (k > 1);
+    if (! x[1] && y[1]<=eps) break;
+
+    p = (double)x[1] + z[1]; p = y[1] + p*p*v[1]; /* norm(x) */
+    if (fun(E, u, x, p)) break;
+  }
+}
+
+static long
+_gp_forqf(void *E, GEN u, GEN x, double p/*unused*/)
+{
+  pari_sp av = avma;
+  (void)p;
+  set_lex(-1, ZM_zc_mul(u, x));
+  closure_evalvoid((GEN)E);
+  avma = av;
+  return loop_break();
+}
+
+void
+forqfvec0(GEN a, GEN BORNE, GEN code)
+{
+  pari_sp av = avma;
+  struct qfvec qv;
+  forqfvec_init(&qv, a);
+  push_lex(gen_0, code);
+  forqfvec((void*) code, &_gp_forqf, &qv, BORNE);
+  pop_lex(1);
+  avma = av;
+}
+
+/* Minimal vectors for the integral definite quadratic form: a.
+ * Result u:
+ *   u[1]= Number of vectors of square norm <= BORNE
+ *   u[2]= maximum norm found
+ *   u[3]= list of vectors found (at most STOCKMAX)
+ *
+ *  If BORNE = gen_0: Minimal non-zero vectors.
+ *  flag = min_ALL,   as above
+ *  flag = min_FIRST, exits when first suitable vector is found.
+ *  flag = min_PERF,  only compute rank of the family of v.v~ (v min.)
+ *  flag = min_VECSMALL, return a t_VECSMALL of (half) the number of vectors for each norm
+ *  flag = min_VECSMALL2, same but count only vectors with even norm, and shift the answer
+ */
+static GEN
+minim0_dolll(GEN a, GEN BORNE, GEN STOCKMAX, long flag, long dolll)
+{
+  GEN x, u, r, L, gnorme, invp, V;
+  long n = lg(a), i, j, k, s, maxrank, sBORNE;
+  pari_sp av = avma, av1, lim;
+  double p,maxnorm,BOUND,*v,*y,*z,**q;
+  const double eps = 1e-10;
+  int stockall = 0;
+  struct qfvec qv;
+
+  if (!BORNE)
+    sBORNE = 0;
+  else
+  {
+    BORNE = gfloor(BORNE);
+    if (typ(BORNE) != t_INT) pari_err_TYPE("minim0",BORNE);
+    if (is_bigint(BORNE)) pari_err_PREC( "qfminim");
+    sBORNE = itos(BORNE); avma = av;
+  }
+  if (!STOCKMAX)
+  {
+    stockall = 1;
+    maxrank = 200;
+  }
+  else
+  {
+    STOCKMAX = gfloor(STOCKMAX);
+    if (typ(STOCKMAX) != t_INT) pari_err_TYPE("minim0",STOCKMAX);
+    maxrank = itos(STOCKMAX);
+    if (maxrank < 0)
+      pari_err_TYPE("minim0 [negative number of vectors]",STOCKMAX);
+  }
+
+  L = V = invp = NULL; /* gcc -Wall */
+  switch(flag)
+  {
+    case min_VECSMALL:
+    case min_VECSMALL2:
+      if (sBORNE <= 0) return cgetg(1, t_VECSMALL);
+      L = zero_zv(sBORNE);
+      if (flag == min_VECSMALL2) sBORNE <<= 1;
+      if (n == 1) return L;
+      break;
+    case min_FIRST:
+      if (n == 1) return cgetg(1,t_VEC);
+      break;
+    case min_PERF:
+      if (n == 1) return gen_0;
+      break;
+    default:
+      if (n == 1) retmkvec3(gen_0, gen_0, cgetg(1, t_MAT));
+      break;
+  }
+  minim_alloc(n, &q, &x, &y, &z, &v);
+  av1 = avma;
+
+  forqfvec_init_dolll(&qv, a, dolll);
+  r = qv.r;
+  u = qv.u;
+  n--;
+  for (j=1; j<=n; j++)
+  {
+    v[j] = rtodbl(gcoeff(r,j,j));
+    for (i=1; i<j; i++) q[i][j] = rtodbl(gcoeff(r,i,j));
+  }
+
+  if (!sBORNE)
+  {
+    GEN B = gcoeff(a,1,1);
+    long t = 1;
+    for (i=2; i<=n; i++)
+    {
+      GEN c = gcoeff(a,i,i);
+      if (cmpii(c, B) < 0) { B = c; t = i; }
+    }
+    if (flag == min_FIRST) return gerepilecopy(av, mkvec2(B, gel(u,t)));
+    maxnorm = -1.; /* don't update maxnorm */
+    sBORNE = itos(B);
+  }
+  else
+    maxnorm = 0.;
+  BOUND = sBORNE * (1 + eps);
+  if ((long)BOUND != sBORNE) pari_err_PREC( "qfminim");
+
+  switch(flag)
+  {
+    case min_ALL:
+      L = new_chunk(1+maxrank);
+      break;
+    case min_PERF:
+      avma = av1;
+      maxrank = (n*(n+1)) >> 1;
+      L = zero_zv(maxrank);
+      V = cgetg(1+maxrank, t_VECSMALL);
+  }
+
+  s = 0; av1 = avma; lim = stack_lim(av1,1);
+  k = n; y[n] = z[n] = 0;
+  x[n] = (long)sqrt(BOUND/v[n]);
+  for(;;x[1]--)
+  {
+    do
+    {
+      if (k>1)
+      {
+        long l = k-1;
+        z[l] = 0;
+        for (j=k; j<=n; j++) z[l] += q[l][j]*x[j];
+        p = (double)x[k] + z[k];
+        y[l] = y[k] + p*p*v[k];
+        x[l] = (long)floor(sqrt((BOUND-y[l])/v[l])-z[l]);
+        k = l;
+      }
+      for(;;)
+      {
+        p = (double)x[k] + z[k];
+        if (y[k] + p*p*v[k] <= BOUND) break;
+        k++; x[k]--;
+      }
+    }
+    while (k > 1);
+    if (! x[1] && y[1]<=eps) break;
+
+    p = (double)x[1] + z[1]; p = y[1] + p*p*v[1]; /* norm(x) */
+    if (maxnorm >= 0)
+    {
+      if (p > maxnorm) maxnorm = p;
+    }
+    else
+    { /* maxnorm < 0 : only look for minimal vectors */
+      pari_sp av2 = avma;
+      gnorme = roundr(dbltor(p));
+      if (cmpis(gnorme, sBORNE) >= 0) avma = av2;
+      else
+      {
+        sBORNE = itos(gnorme); avma = av1;
+        BOUND = sBORNE * (1+eps);
+        s = 0;
+      }
+    }
+    s++;
+
+    switch(flag)
+    {
+      case min_FIRST:
+        return gerepilecopy(av, mkvec2(roundr(dbltor(p)), ZM_zc_mul(u,x)));
+
+      case min_ALL:
+        if (s > maxrank && stockall) /* overflow */
+        {
+          long maxranknew = maxrank << 1;
+          GEN Lnew = new_chunk(1 + maxranknew);
+          for (i=1; i<=maxrank; i++) Lnew[i] = L[i];
+          L = Lnew; maxrank = maxranknew;
+        }
+        if (s<=maxrank) gel(L,s) = leafcopy(x);
+        break;
+
+      case min_VECSMALL:
+        {
+          ulong norm = (ulong)(p + 0.5);
+          L[norm]++;
+        }
+        break;
+
+      case min_VECSMALL2:
+        {
+          ulong norm = (ulong)(p + 0.5);
+          if ((norm&1) == 0) L[norm>>1]++;
+        }
+        break;
+
+      case min_PERF:
+      {
+        pari_sp av2;
+        long I;
+
+        if (s == 1) {
+          invp = matid(maxrank);
+          for (i = 1; i <= maxrank; i++) L[i] = 0;
+        }
+        /* must go till the end in case we find a smallest vector last */
+        if (s > maxrank) { s = maxrank; continue; }
+        av2 = avma;
+        for (i = I = 1; i<=n; i++)
+          for (j=i; j<=n; j++,I++) V[I] = x[i]*x[j];
+        if (! addcolumntomatrix(V,invp,L))
+        {
+          if (DEBUGLEVEL>1) { err_printf("."); err_flush(); }
+          s--; avma=av2; continue;
+        }
+        if (DEBUGLEVEL>1) { err_printf("*"); err_flush(); }
+        if (low_stack(lim, stack_lim(av1,1)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"minim0, rank>=%ld",s);
+          invp = gerepilecopy(av1, invp);
+        }
+      }
+    }
+  }
+  switch(flag)
+  {
+    case min_FIRST:
+      avma = av; return cgetg(1,t_VEC);
+    case min_VECSMALL:
+    case min_VECSMALL2:
+      avma = (pari_sp)L; return L;
+    case min_PERF:
+      if (DEBUGLEVEL>1) { err_printf("\n"); err_flush(); }
+      avma = av; return stoi(s);
+  }
+  r = (maxnorm >= 0) ? roundr(dbltor(maxnorm)): stoi(sBORNE);
+  k = minss(s,maxrank);
+  L[0] = evaltyp(t_MAT) | evallg(k + 1);
+  if (dolll)
+    for (j=1; j<=k; j++)
+      gel(L,j) = ZM_zc_mul(u, gel(L,j));
+  return gerepilecopy(av, mkvec3(stoi(s<<1), r, L));
+}
+
+static GEN
+minim0(GEN a, GEN BORNE, GEN STOCKMAX, long flag)
+{
+  return minim0_dolll(a, BORNE, STOCKMAX, flag, 1);
+}
+
+GEN
+qfrep0(GEN a, GEN borne, long flag)
+{ return minim0(a, borne, gen_0, (flag & 1)? min_VECSMALL2: min_VECSMALL); }
+
+GEN
+qfminim0(GEN a, GEN borne, GEN stockmax, long flag, long prec)
+{
+  switch(flag)
+  {
+    case 0: return minim0(a,borne,stockmax,min_ALL);
+    case 1: return minim0(a,borne,gen_0   ,min_FIRST);
+    case 2:
+    {
+      long maxnum = -1;
+      if (typ(a) != t_MAT) pari_err_TYPE("qfminim",a);
+      if (stockmax) {
+        if (typ(stockmax) != t_INT) pari_err_TYPE("qfminim",stockmax);
+        maxnum = itos(stockmax);
+      }
+      a = fincke_pohst(a,borne,maxnum,prec,NULL);
+      if (!a) pari_err_PREC("qfminim");
+      return a;
+    }
+    default: pari_err_FLAG("qfminim");
+  }
+  return NULL; /* not reached */
+}
+
+GEN
+minim(GEN a, GEN borne, GEN stockmax)
+{
+  return minim0(a,borne,stockmax,min_ALL);
+}
+
+GEN
+minim_raw(GEN a, GEN BORNE, GEN STOCKMAX)
+{
+  return minim0_dolll(a, BORNE, STOCKMAX, min_ALL, 0);
+}
+
+GEN
+minim2(GEN a, GEN borne, GEN stockmax)
+{
+  return minim0(a,borne,stockmax,min_FIRST);
+}
+
+GEN
+perf(GEN a)
+{
+  return minim0(a,gen_0,gen_0,min_PERF);
+}
+
+static GEN
+clonefill(GEN S, long s, long t)
+{ /* initialize to dummy values */
+  GEN T = S, dummy = cgetg(1, t_STR);
+  long i;
+  for (i = s+1; i <= t; i++) gel(S,i) = dummy;
+  S = gclone(S); if (isclone(T)) gunclone(T);
+  return S;
+}
+
+/* increment ZV x, by incrementing cell of index k. Initial value x0[k] was
+ * chosen to minimize qf(x) for given x0[1..k-1] and x0[k+1,..] = 0
+ * The last non-zero entry must be positive and goes through x0[k]+1,2,3,...
+ * Others entries go through: x0[k]+1,-1,2,-2,...*/
+INLINE void
+step(GEN x, GEN y, GEN inc, long k)
+{
+  if (!signe(gel(y,k))) /* x[k+1..] = 0 */
+    gel(x,k) = addiu(gel(x,k), 1); /* leading coeff > 0 */
+  else
+  {
+    long i = inc[k];
+    gel(x,k) = addis(gel(x,k), i),
+    inc[k] = (i > 0)? -1-i: 1-i;
+  }
+}
+
+/* 1 if we are "sure" that x < y, up to few rounding errors, i.e.
+ * x < y - epsilon. More precisely :
+ * - sign(x - y) < 0
+ * - lgprec(x-y) > 3 || expo(x - y) - expo(x) > -24 */
+static int
+mplessthan(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN z = mpsub(x, y);
+  avma = av;
+  if (typ(z) == t_INT) return (signe(z) < 0);
+  if (signe(z) >= 0) return 0;
+  if (realprec(z) > LOWDEFAULTPREC) return 1;
+  return ( expo(z) - mpexpo(x) > -24 );
+}
+
+/* 1 if we are "sure" that x > y, up to few rounding errors, i.e.
+ * x > y + epsilon */
+static int
+mpgreaterthan(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN z = mpsub(x, y);
+  avma = av;
+  if (typ(z) == t_INT) return (signe(z) > 0);
+  if (signe(z) <= 0) return 0;
+  if (realprec(z) > LOWDEFAULTPREC) return 1;
+  return ( expo(z) - mpexpo(x) > -24 );
+}
+
+/* x a t_INT, y  t_INT or t_REAL */
+INLINE GEN
+mulimp(GEN x, GEN y)
+{
+  if (typ(y) == t_INT) return mulii(x,y);
+  return signe(x) ? mulir(x,y): gen_0;
+}
+/* x + y*z, x,z two mp's, y a t_INT */
+INLINE GEN
+addmulimp(GEN x, GEN y, GEN z)
+{
+  if (!signe(y)) return x;
+  if (typ(z) == t_INT) return mpadd(x, mulii(y, z));
+  return mpadd(x, mulir(y, z));
+}
+
+/* yk + vk * (xk + zk)^2 */
+static GEN
+norm_aux(GEN xk, GEN yk, GEN zk, GEN vk)
+{
+  GEN t = mpadd(xk, zk);
+  if (typ(t) == t_INT) { /* probably gen_0, avoid loss of accuracy */
+    yk = addmulimp(yk, sqri(t), vk);
+  } else {
+    yk = mpadd(yk, mpmul(sqrr(t), vk));
+  }
+  return yk;
+}
+/* yk + vk * (xk + zk)^2 < B + epsilon */
+static int
+check_bound(GEN B, GEN xk, GEN yk, GEN zk, GEN vk)
+{
+  pari_sp av = avma;
+  int f = mpgreaterthan(norm_aux(xk,yk,zk,vk), B);
+  avma = av; return !f;
+}
+
+/* q(k-th canonical basis vector), where q is given in Cholesky form
+ * q(x) = sum_{i = 1}^n q[i,i] (x[i] + sum_{j > i} q[i,j] x[j])^2.
+ * Namely q(e_k) = q[k,k] + sum_{i < k} q[i,i] q[i,k]^2
+ * Assume 1 <= k <= n. */
+static GEN
+cholesky_norm_ek(GEN q, long k)
+{
+  GEN t = gcoeff(q,k,k);
+  long i;
+  for (i = 1; i < k; i++) t = norm_aux(gen_0, t, gcoeff(q,i,k), gcoeff(q,i,i));
+  return t;
+}
+
+/* q is the Cholesky decomposition of a quadratic form
+ * Enumerate vectors whose norm is less than BORNE (Algo 2.5.7),
+ * minimal vectors if BORNE = NULL (implies check = NULL).
+ * If (check != NULL) consider only vectors passing the check, and assumes
+ *   we only want the smallest possible vectors */
+static GEN
+smallvectors(GEN q, GEN BORNE, long maxnum, FP_chk_fun *CHECK)
+{
+  long N = lg(q), n = N-1, i, j, k, s, stockmax, checkcnt = 1;
+  pari_sp av, av1, lim;
+  GEN inc, S, x, y, z, v, p1, alpha, norms;
+  GEN norme1, normax1, borne1, borne2;
+  GEN (*check)(void *,GEN) = CHECK? CHECK->f: NULL;
+  void *data = CHECK? CHECK->data: NULL;
+  const long skipfirst = CHECK? CHECK->skipfirst: 0;
+  const int stockall = (maxnum == -1);
+
+  alpha = dbltor(0.95);
+  normax1 = gen_0;
+
+  v = cgetg(N,t_VEC);
+  inc = const_vecsmall(n, 1);
+
+  av = avma; lim = stack_lim(av,2);
+  stockmax = stockall? 2000: maxnum;
+  norms = cgetg(check?(stockmax+1): 1,t_VEC); /* unused if (!check) */
+  S = cgetg(stockmax+1,t_VEC);
+  x = cgetg(N,t_COL);
+  y = cgetg(N,t_COL);
+  z = cgetg(N,t_COL);
+  for (i=1; i<N; i++) {
+    gel(v,i) = gcoeff(q,i,i);
+    gel(x,i) = gel(y,i) = gel(z,i) = gen_0;
+  }
+  if (BORNE)
+  {
+    borne1 = BORNE;
+    if (typ(borne1) != t_REAL)
+    {
+      long prec = nbits2prec(gexpo(borne1) + 10);
+      borne1 = gtofp(borne1, maxss(prec, DEFAULTPREC));
+    }
+  }
+  else
+  {
+    borne1 = gcoeff(q,1,1);
+    for (i=2; i<N; i++)
+    {
+      GEN b = cholesky_norm_ek(q, i);
+      if (gcmp(b, borne1) < 0) borne1 = b;
+    }
+    /* borne1 = norm of smallest basis vector */
+  }
+  borne2 = mulrr(borne1,alpha);
+  if (DEBUGLEVEL>2)
+    err_printf("smallvectors looking for norm < %P.4G\n",borne1);
+  s = 0; k = n;
+  for(;; step(x,y,inc,k)) /* main */
+  { /* x (supposedly) small vector, ZV.
+     * For all t >= k, we have
+     *   z[t] = sum_{j > t} q[t,j] * x[j]
+     *   y[t] = sum_{i > t} q[i,i] * (x[i] + z[i])^2
+     *        = 0 <=> x[i]=0 for all i>t */
+    do
+    {
+      int skip = 0;
+      if (k > 1)
+      {
+        long l = k-1;
+        av1 = avma;
+        p1 = mulimp(gel(x,k), gcoeff(q,l,k));
+        for (j=k+1; j<N; j++) p1 = addmulimp(p1, gel(x,j), gcoeff(q,l,j));
+        gel(z,l) = gerepileuptoleaf(av1,p1);
+
+        av1 = avma;
+        p1 = norm_aux(gel(x,k), gel(y,k), gel(z,k), gel(v,k));
+        gel(y,l) = gerepileuptoleaf(av1, p1);
+        /* skip the [x_1,...,x_skipfirst,0,...,0] */
+        if ((l <= skipfirst && !signe(gel(y,skipfirst)))
+         || mplessthan(borne1, gel(y,l))) skip = 1;
+        else /* initial value, minimizing (x[l] + z[l])^2, hence qf(x) for
+                the given x[1..l-1] */
+          gel(x,l) = mpround( mpneg(gel(z,l)) );
+        k = l;
+      }
+      for(;; step(x,y,inc,k))
+      { /* at most 2n loops */
+        if (!skip)
+        {
+          if (check_bound(borne1, gel(x,k),gel(y,k),gel(z,k),gel(v,k))) break;
+          step(x,y,inc,k);
+          if (check_bound(borne1, gel(x,k),gel(y,k),gel(z,k),gel(v,k))) break;
+        }
+        skip = 0; inc[k] = 1;
+        if (++k > n) goto END;
+      }
+
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"smallvectors");
+        if (stockmax) S = clonefill(S, s, stockmax);
+        if (check) {
+          GEN dummy = cgetg(1, t_STR);
+          for (i=s+1; i<=stockmax; i++) gel(norms,i) = dummy;
+        }
+        gerepileall(av,7,&x,&y,&z,&normax1,&borne1,&borne2,&norms);
+      }
+    }
+    while (k > 1);
+    if (!signe(gel(x,1)) && !signe(gel(y,1))) continue; /* exclude 0 */
+
+    av1 = avma;
+    norme1 = norm_aux(gel(x,1),gel(y,1),gel(z,1),gel(v,1));
+    if (mpgreaterthan(norme1,borne1)) { avma = av1; continue; /* main */ }
+
+    norme1 = gerepileuptoleaf(av1,norme1);
+    if (check)
+    {
+      if (checkcnt < 5 && mpcmp(norme1, borne2) < 0)
+      {
+        if (!check(data,x)) { checkcnt++ ; continue; /* main */}
+        if (DEBUGLEVEL>4) err_printf("New bound: %Ps", norme1);
+        borne1 = norme1;
+        borne2 = mulrr(borne1, alpha);
+        s = 0; checkcnt = 0;
+      }
+    }
+    else
+    {
+      if (!BORNE) /* find minimal vectors */
+      {
+        if (mplessthan(norme1, borne1))
+        { /* strictly smaller vector than previously known */
+          borne1 = norme1; /* + epsilon */
+          s = 0;
+        }
+      }
+      else
+        if (mpcmp(norme1,normax1) > 0) normax1 = norme1;
+    }
+    if (++s > stockmax) continue; /* too many vectors: no longer remember */
+    if (check) gel(norms,s) = norme1;
+    gel(S,s) = leafcopy(x);
+    if (s != stockmax) continue; /* still room, get next vector */
+
+    /* overflow, eliminate vectors failing "check" */
+    if (check)
+    {
+      pari_sp av2 = avma;
+      long imin, imax;
+      GEN per = indexsort(norms);
+      if (DEBUGLEVEL>2) err_printf("sorting... [%ld elts]\n",s);
+      /* let N be the minimal norm so far for x satisfying 'check'. Keep
+       * all elements of norm N */
+      for (i = 1; i <= s; i++)
+      {
+        long k = per[i];
+        if (check(data,gel(S,k))) { borne1 = gel(norms,k); break; }
+      }
+      imin = i;
+      for (; i <= s; i++)
+        if (mpgreaterthan(gel(norms,per[i]), borne1)) break;
+      imax = i;
+      for (i=imin, s=0; i < imax; i++) gel(S,++s) = gel(S,per[i]);
+      avma = av2;
+      if (s)
+      {
+        borne2 = mulrr(borne1, alpha);
+        checkcnt = 0;
+      }
+      if (!stockall) continue;
+      if (s > stockmax/2) stockmax <<= 1;
+      norms = cgetg(stockmax+1, t_VEC);
+      for (i = 1; i <= s; i++) gel(norms,i) = borne1;
+    }
+    else
+    {
+      if (!stockall && BORNE) goto END;
+      if (!stockall) continue;
+      stockmax <<= 1;
+    }
+
+    {
+      GEN Snew = cgetg(stockmax + 1, t_VEC);
+      for (i = 1; i <= s; i++) gel(Snew,i) = gel(S,i);
+      Snew = clonefill(Snew, s, stockmax);
+      if (isclone(S)) gunclone(S);
+      S = Snew;
+    }
+  }
+END:
+  if (s < stockmax) stockmax = s;
+  if (check)
+  {
+    GEN per, alph, pols, p;
+    if (DEBUGLEVEL>2) err_printf("final sort & check...\n");
+    setlg(norms,stockmax+1); per = indexsort(norms);
+    alph = cgetg(stockmax+1,t_VEC);
+    pols = cgetg(stockmax+1,t_VEC);
+    for (j=0,i=1; i<=stockmax; i++)
+    {
+      long t = per[i];
+      GEN N = gel(norms,t);
+      if (j && mpgreaterthan(N, borne1)) break;
+      if ((p = check(data,gel(S,t))))
+      {
+        if (!j) borne1 = N;
+        j++;
+        gel(pols,j) = p;
+        gel(alph,j) = gel(S,t);
+      }
+    }
+    setlg(pols,j+1);
+    setlg(alph,j+1);
+    if (stockmax && isclone(S)) { alph = gcopy(alph); gunclone(S); }
+    return mkvec2(pols, alph);
+  }
+  if (stockmax)
+  {
+    setlg(S,stockmax+1);
+    settyp(S,t_MAT);
+    if (isclone(S)) { p1 = S; S = gcopy(S); gunclone(p1); }
+  }
+  else
+    S = cgetg(1,t_MAT);
+  return mkvec3(utoi(s<<1), borne1, S);
+}
+
+/* solve q(x) = x~.a.x <= bound, a > 0.
+ * If check is non-NULL keep x only if check(x).
+ * If a is a vector, assume a[1] is the LLL-reduced Cholesky form of q */
+GEN
+fincke_pohst(GEN a, GEN B0, long stockmax, long PREC, FP_chk_fun *CHECK)
+{
+  pari_sp av = avma;
+  VOLATILE long i,j,l;
+  VOLATILE GEN r,rinv,rinvtrans,u,v,res,z,vnorm,rperm,perm,uperm, bound = B0;
+
+  if (typ(a) == t_VEC)
+  {
+    r = gel(a,1);
+    u = NULL;
+  }
+  else
+  {
+    long prec = PREC;
+    l = lg(a);
+    if (l == 1)
+    {
+      if (CHECK) pari_err_TYPE("fincke_pohst [dimension 0]", a);
+      retmkvec3(gen_0, gen_0, cgetg(1,t_MAT));
+    }
+    u = lllfp(a, 0.75, LLL_GRAM);
+    if (lg(u) != lg(a)) return NULL;
+    r = qf_apply_RgM(a,u);
+    i = gprecision(r);
+    if (i)
+      prec = i;
+    else {
+      prec = DEFAULTPREC + nbits2extraprec(gexpo(r));
+      if (prec < PREC) prec = PREC;
+    }
+    if (DEBUGLEVEL>2) err_printf("first LLL: prec = %ld\n", prec);
+    r = qfgaussred_positive(r);
+    if (!r) return NULL;
+    for (i=1; i<l; i++)
+    {
+      GEN s = gsqrt(gcoeff(r,i,i), prec);
+      gcoeff(r,i,i) = s;
+      for (j=i+1; j<l; j++) gcoeff(r,i,j) = gmul(s, gcoeff(r,i,j));
+    }
+  }
+  /* now r~ * r = a in LLL basis */
+  rinv = RgM_inv_upper(r);
+  if (!rinv) return NULL;
+  rinvtrans = shallowtrans(rinv);
+  if (DEBUGLEVEL>2)
+    err_printf("Fincke-Pohst, final LLL: prec = %ld\n", gprecision(rinvtrans));
+  v = lll(rinvtrans);
+  if (lg(v) != lg(rinvtrans)) return NULL;
+
+  rinvtrans = RgM_mul(rinvtrans, v);
+  v = ZM_inv(shallowtrans(v),gen_1);
+  r = RgM_mul(r,v);
+  u = u? ZM_mul(u,v): v;
+
+  l = lg(r);
+  vnorm = cgetg(l,t_VEC);
+  for (j=1; j<l; j++) gel(vnorm,j) = gnorml2(gel(rinvtrans,j));
+  rperm = cgetg(l,t_MAT);
+  uperm = cgetg(l,t_MAT); perm = indexsort(vnorm);
+  for (i=1; i<l; i++) { uperm[l-i] = u[perm[i]]; rperm[l-i] = r[perm[i]]; }
+  u = uperm;
+  r = rperm; res = NULL;
+  pari_CATCH(e_PREC) { }
+  pari_TRY {
+    GEN q;
+    if (CHECK && CHECK->f_init) bound = CHECK->f_init(CHECK, r, u);
+    q = gaussred_from_QR(r, gprecision(vnorm));
+    if (!q) pari_err_PREC("fincke_pohst");
+    res = smallvectors(q, bound, stockmax, CHECK);
+  } pari_ENDCATCH;
+  if (CHECK)
+  {
+    if (CHECK->f_post) res = CHECK->f_post(CHECK, res, u);
+    return res;
+  }
+  if (!res) pari_err_PREC("fincke_pohst");
+
+  z = cgetg(4,t_VEC);
+  gel(z,1) = gcopy(gel(res,1));
+  gel(z,2) = gcopy(gel(res,2));
+  gel(z,3) = ZM_mul(u, gel(res,3)); return gerepileupto(av,z);
+}
diff --git a/src/basemath/bibli2.c b/src/basemath/bibli2.c
new file mode 100644
index 0000000..a3e44a1
--- /dev/null
+++ b/src/basemath/bibli2.c
@@ -0,0 +1,1901 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/**                                                               **/
+/**                      SPECIAL POLYNOMIALS                      **/
+/**                                                               **/
+/*******************************************************************/
+/* Tchebichev polynomial: T0=1; T1=X; T(n)=2*X*T(n-1)-T(n-2)
+ * T(n) = (n/2) sum_{k=0}^{n/2} a_k x^(n-2k)
+ *   where a_k = (-1)^k 2^(n-2k) (n-k-1)! / k!(n-2k)! is an integer
+ *   and a_0 = 2^(n-1), a_k / a_{k-1} = - (n-2k+2)(n-2k+1) / 4k(n-k) */
+GEN
+polchebyshev1(long n, long v) /* Assume 4*n < LONG_MAX */
+{
+  long k, l;
+  pari_sp av;
+  GEN q,a,r;
+
+  if (v<0) v = 0;
+  /* polchebyshev(-n,1) = polchebyshev(n,1) */
+  if (n < 0) n = -n;
+  if (n==0) return pol_1(v);
+  if (n==1) return pol_x(v);
+
+  q = cgetg(n+3, t_POL); r = q + n+2;
+  a = int2n(n-1);
+  gel(r--,0) = a;
+  gel(r--,0) = gen_0;
+  for (k=1,l=n; l>1; k++,l-=2)
+  {
+    av = avma;
+    a = diviuuexact(muluui(l, l-1, a), 4*k, n-k);
+    togglesign(a); a = gerepileuptoint(av, a);
+    gel(r--,0) = a;
+    gel(r--,0) = gen_0;
+  }
+  q[1] = evalsigne(1) | evalvarn(v);
+  return q;
+}
+static void
+polchebyshev1_eval_aux(long n, GEN x, GEN *pt1, GEN *pt2)
+{
+  GEN t1, t2, b;
+  if (n == 1) { *pt1 = gen_1; *pt2 = x; return; }
+  if (n == 0) { *pt1 = x; *pt2 = gen_1; return; }
+  polchebyshev1_eval_aux((n+1) >> 1, x, &t1, &t2);
+  b = gsub(gmul(gmul2n(t1,1), t2), x);
+  if (odd(n)) { *pt1 = gadd(gmul2n(gsqr(t1), 1), gen_m1); *pt2 = b; }
+  else        { *pt1 = b; *pt2 = gadd(gmul2n(gsqr(t2), 1), gen_m1); }
+}
+static GEN
+polchebyshev1_eval(long n, GEN x)
+{
+  GEN t1, t2;
+  long i, v;
+  pari_sp av;
+
+  if (n < 0) n = -n;
+  if (n==0) return gen_1;
+  if (n==1) return gcopy(x);
+  av = avma;
+  v = u_lvalrem(n, 2, (ulong*)&n);
+  polchebyshev1_eval_aux((n+1)>>1, x, &t1, &t2);
+  if (n != 1) t2 = gsub(gmul(gmul2n(t1,1), t2), x);
+  for (i = 1; i <= v; i++) t2 = gadd(gmul2n(gsqr(t2), 1), gen_m1);
+  return gerepileupto(av, t2);
+}
+
+/* Chebychev  polynomial of the second kind U(n,x): the coefficient in front of
+ * x^(n-2*m) is (-1)^m * 2^(n-2m)*(n-m)!/m!/(n-2m)!  for m=0,1,...,n/2 */
+GEN
+polchebyshev2(long n, long v)
+{
+  pari_sp av;
+  GEN q, a, r;
+  long m;
+  int neg = 0;
+
+  if (v<0) v = 0;
+  /* polchebyshev(-n,2) = -polchebyshev(n-2,2) */
+  if (n < 0) {
+    if (n == -1) return zeropol(v);
+    neg = 1; n = -n-2;
+  }
+  if (n==0) return neg ? scalar_ZX_shallow(gen_m1, v): pol_1(v);
+
+  q = cgetg(n+3, t_POL); r = q + n+2;
+  a = int2n(n);
+  if (neg) togglesign(a);
+  gel(r--,0) = a;
+  gel(r--,0) = gen_0;
+  for (m=1; 2*m<= n; m++)
+  {
+    av = avma;
+    a = diviuuexact(muluui(n-2*m+2, n-2*m+1, a), 4*m, n-m+1);
+    togglesign(a); a = gerepileuptoint(av, a);
+    gel(r--,0) = a;
+    gel(r--,0) = gen_0;
+  }
+  q[1] = evalsigne(1) | evalvarn(v);
+  return q;
+}
+static void
+polchebyshev2_eval_aux(long n, GEN x, GEN *pu1, GEN *pu2)
+{
+  GEN u1, u2, u, mu1;
+  if (n == 1) { *pu1 = gen_1; *pu2 = gmul2n(x,1); return; }
+  if (n == 0) { *pu1 = gen_0; *pu2 = gen_1; return; }
+  polchebyshev2_eval_aux(n >> 1, x, &u1, &u2);
+  mu1 = gneg(u1);
+  u = gmul(gadd(u2,u1), gadd(u2,mu1));
+  if (odd(n)) { *pu1 = u; *pu2 = gmul(gmul2n(u2,1), gadd(gmul(x,u2), mu1)); }
+  else        { *pu2 = u; *pu1 = gmul(gmul2n(u1,1), gadd(u2, gmul(x,mu1))); }
+}
+static GEN
+polchebyshev2_eval(long n, GEN x)
+{
+  GEN u1, u2, mu1;
+  long neg = 0;
+  pari_sp av;
+
+  if (n < 0) {
+    if (n == -1) return gen_0;
+    neg = 1; n = -n-2;
+  }
+  if (n==0) return neg ? gen_m1: gen_1;
+  av = avma;
+  polchebyshev2_eval_aux(n>>1, x, &u1, &u2);
+  mu1 = gneg(u1);
+  if (odd(n)) u2 = gmul(gmul2n(u2,1), gadd(gmul(x,u2), mu1));
+  else        u2 = gmul(gadd(u2,u1), gadd(u2,mu1));
+  if (neg) u2 = gneg(u2);
+  return gerepileupto(av, u2);
+}
+
+GEN
+polchebyshev(long n, long kind, long v)
+{
+  switch (kind)
+  {
+    case 1: return polchebyshev1(n, v);
+    case 2: return polchebyshev2(n, v);
+    default: pari_err_FLAG("polchebyshev");
+  }
+  return NULL; /* not reached */
+}
+GEN
+polchebyshev_eval(long n, long kind, GEN x)
+{
+  if (!x) return polchebyshev(n, kind, 0);
+  if (gequalX(x)) return polchebyshev(n, kind, varn(x));
+  switch (kind)
+  {
+    case 1: return polchebyshev1_eval(n, x);
+    case 2: return polchebyshev2_eval(n, x);
+    default: pari_err_FLAG("polchebyshev");
+  }
+  return NULL; /* not reached */
+}
+
+/* Hermite polynomial H(n,x):  H(n+1) = 2x H(n) - 2n H(n-1)
+ * The coefficient in front of x^(n-2*m) is
+ * (-1)^m * n! * 2^(n-2m)/m!/(n-2m)!  for m=0,1,...,n/2.. */
+GEN
+polhermite(long n, long v)
+{
+  long m;
+  pari_sp av;
+  GEN q,a,r;
+
+  if (v<0) v = 0;
+  if (n < 0) pari_err_DOMAIN("polhermite", "degree", "<", gen_0, stoi(n));
+  if (n==0) return pol_1(v);
+
+  q = cgetg(n+3, t_POL); r = q + n+2;
+  a = int2n(n);
+  gel(r--,0) = a;
+  gel(r--,0) = gen_0;
+  for (m=1; 2*m<= n; m++)
+  {
+    av = avma;
+    a = diviuexact(muluui(n-2*m+2, n-2*m+1, a), 4*m);
+    togglesign(a);
+    gel(r--,0) = a = gerepileuptoint(av, a);
+    gel(r--,0) = gen_0;
+  }
+  q[1] = evalsigne(1) | evalvarn(v);
+  return q;
+}
+GEN
+polhermite_eval(long n, GEN x)
+{
+  long i;
+  pari_sp av, av2;
+  GEN x2, u, v;
+
+  if (!x) return polhermite(n, 0);
+  if (gequalX(x)) return polhermite(n, varn(x));
+  if (n==0) return gen_1;
+  if (n==1) return gmul2n(x,1);
+  av = avma; x2 = gmul2n(x,1); v = gen_1; u = x2;
+  av2= avma;
+  for (i=1; i<n; i++)
+  { /* u = H_i(x), v = H_{i-1}(x), compute t = H_{i+1}(x) */
+    GEN t;
+    if ((i & 0xff) == 0) gerepileall(av2,2,&u, &v);
+    t = gsub(gmul(x2, u), gmulsg(2*i,v));
+    v = u; u = t;
+  }
+  return gerepileupto(av, u);
+}
+
+/* Legendre polynomial
+ * L0=1; L1=X; (n+1)*L(n+1)=(2*n+1)*X*L(n)-n*L(n-1)
+ * L(n) = 2^-n sum_{k=0}^{n/2} a_k x^(n-2k)
+ *   where a_k = (-1)^k (2n-2k)! / k! (n-k)! (n-2k)! is an integer
+ *   and a_0 = binom(2n,n), a_k / a_{k-1} = - (n-2k+1)(n-2k+2) / 2k (2n-2k+1) */
+GEN
+pollegendre(long n, long v)
+{
+  long k, l;
+  pari_sp av;
+  GEN a, r, q;
+
+  if (v<0) v = 0;
+  /* pollegendre(-n) = pollegendre(n-1) */
+  if (n < 0) n = -n-1;
+  if (n==0) return pol_1(v);
+  if (n==1) return pol_x(v);
+
+  av = avma;
+  q = cgetg(n+3, t_POL); r = q + n+2;
+  gel(r--,0) = a = binomialuu(n<<1,n);
+  gel(r--,0) = gen_0;
+  for (k=1,l=n; l>1; k++,l-=2)
+  { /* l = n-2*k+2 */
+    av = avma;
+    a = diviuuexact(muluui(l, l-1, a), 2*k, n+l-1);
+    togglesign(a); a = gerepileuptoint(av, a);
+    gel(r--,0) = a;
+    gel(r--,0) = gen_0;
+  }
+  q[1] = evalsigne(1) | evalvarn(v);
+  return gerepileupto(av, gmul2n(q,-n));
+}
+
+GEN
+pollegendre_eval(long n, GEN x)
+{
+  long i;
+  pari_sp av;
+  GEN u, v;
+
+  if (!x) return pollegendre(n, 0);
+  if (gequalX(x)) return pollegendre(n, varn(x));
+  /* pollegendre(-n) = pollegendre(n-1) */
+  if (n < 0) n = -n-1;
+  if (n==0) return gen_1;
+  if (n==1) return gcopy(x);
+  av = avma; v = gen_1; u = x;
+  for (i=1; i<n; i++)
+  { /* u = P_i(x), v = P_{i-1}(x), compute t = P_{i+1}(x) */
+    GEN t;
+    if ((i & 0xff) == 0) gerepileall(av,2,&u, &v);
+    t = gdivgs(gsub(gmul(gmulsg(2*i+1,x), u), gmulsg(i,v)), i+1);
+    v = u; u = t;
+  }
+  return gerepileupto(av, u);
+}
+
+/* polcyclo(p) = X^(p-1) + ... + 1 */
+static GEN
+polcyclo_prime(long p, long v)
+{
+  GEN T = cgetg(p+2, t_POL);
+  long i;
+  T[1] = evalsigne(1) | evalvarn(v);
+  for (i = 2; i < p+2; i++) gel(T,i) = gen_1;
+  return T;
+}
+
+/* cyclotomic polynomial */
+GEN
+polcyclo(long n, long v)
+{
+  long s, q, i, l;
+  pari_sp av=avma;
+  GEN T, P;
+
+  if (v<0) v = 0;
+  if (n < 3)
+    switch(n)
+    {
+      case 1: return deg1pol_shallow(gen_1, gen_m1, v);
+      case 2: return deg1pol_shallow(gen_1, gen_1, v);
+      default: pari_err_DOMAIN("polcyclo", "index", "<=", gen_0, stoi(n));
+    }
+  P = gel(factoru(n), 1); l = lg(P);
+  s = P[1]; T = polcyclo_prime(s, v);
+  for (i = 2; i < l; i++)
+  { /* Phi_{np}(X) = Phi_n(X^p) / Phi_n(X) */
+    s *= P[i];
+    T = RgX_div(RgX_inflate(T, P[i]), T);
+  }
+  /* s = squarefree part of n */
+  q = n / s;
+  if (q == 1) return gerepileupto(av, T);
+  return gerepilecopy(av, RgX_inflate(T,q));
+}
+
+/* cyclotomic polynomial */
+GEN
+polcyclo_eval(long n, GEN x)
+{
+  pari_sp av= avma;
+  GEN P, md, xd, yneg, ypos;
+  long l, s, i, j, q, tx;
+  long root_of_1 = 0;
+
+  if (!x) return polcyclo(n, 0);
+  tx = typ(x);
+  if (gequalX(x)) return polcyclo(n, varn(x));
+  if (n <= 0) pari_err_DOMAIN("polcyclo", "index", "<=", gen_0, stoi(n));
+  if (n == 1) return gsubgs(x, 1);
+  if (tx == t_INT && !signe(x)) return gen_1;
+  while ((n & 3) == 0) { n >>= 1; x = gsqr(x); } /* Phi_4n(x) = Phi_2n(x^2) */
+  /* n not divisible by 4 */
+  if (n == 2) return gerepileupto(av, gaddgs(x,1));
+  if (!odd(n)) { n >>= 1; x = gneg(x); } /* Phi_2n(x) = Phi_n(-x) for n>1 odd */
+  /* n odd > 2.  s largest squarefree divisor of n */
+  P = gel(factoru(n), 1); s = zv_prod(P);
+  /* replace n by largest squarefree divisor */
+  q = n/s; if (q != 1) { x = gpowgs(x, q); n = s; }
+  l = lg(P)-1;
+  /* n squarefree odd > 2, l distinct prime divisors. Now handle x = 1 or -1 */
+  if (tx == t_INT) { /* shortcut */
+    if (is_pm1(x))
+    {
+      avma = av;
+      if (signe(x) > 0 && l == 1) return utoipos(P[1]);
+      return gen_1;
+    }
+  } else {
+    if (gequal1(x))
+    { /* n is prime, return n; multiply by x to keep the type */
+      if (l == 1) return gerepileupto(av, gmulgs(x,n));
+      return gerepilecopy(av, x); /* else 1 */
+    }
+    if (gequalm1(x)) return gerepileupto(av, gneg(x)); /* -1 */
+  }
+  /* Heuristic: evaluation will probably not improve things */
+  if (tx == t_POL || tx == t_MAT || lg(x) > n)
+    return gerepileupto(av, poleval(polcyclo(n,0), x));
+
+  xd = cgetg((1L<<l) + 1, t_VEC); /* the x^d, where d | n */
+  md = cgetg((1L<<l) + 1, t_VECSMALL); /* the mu(d), where d | n */
+  gel(xd, 1) = x;
+  md[1] = 1;
+  /* Use Phi_n(x) = Prod_{d|n} (x^d-1)^mu(n/d).
+   * If x has exact order D, n = Dq, then the result is 0 if q = 1. Otherwise
+   * the factors with x^d-1, D|d are omitted and we multiply at the end by
+   *   prod_{d | q} d^mu(q/d) = q if prime, 1 otherwise */
+  /* We store the factors with mu(d)= 1 (resp.-1) in ypos (resp yneg).
+   * At the end we return ypos/yneg if mu(n)=1 and yneg/ypos if mu(n)=-1 */
+  ypos = gsubgs(x,1);
+  yneg = gen_1;
+  for (i = 1; i <= l; i++)
+  {
+    long ti = 1L<<(i-1), p = P[i];
+    for (j = 1; j <= ti; j++) {
+      GEN X = gpowgs(gel(xd,j), p), t = gsubgs(X,1);
+      gel(xd,ti+j) = X;
+      md[ti+j] = -md[j];
+      if (gequal0(t))
+      { /* x^d = 1; root_of_1 := the smallest index ti+j such that X == 1
+        * (whose bits code d: bit i-1 is set iff P[i] | d). If no such index
+        * exists, then root_of_1 remains 0. Do not multiply with X-1 if X = 1,
+        * we handle these factors at the end */
+        if (!root_of_1) root_of_1 = ti+j;
+      }
+      else
+      {
+        if (md[ti+j] == 1) ypos = gmul(ypos, t);
+        else               yneg = gmul(yneg, t);
+      }
+    }
+  }
+  ypos = odd(l)? gdiv(yneg,ypos): gdiv(ypos,yneg);
+  if (root_of_1)
+  {
+    GEN X = gel(xd,(1<<l)); /* = x^n = 1 */
+    long bitmask_q = (1<<l) - root_of_1;
+    /* bitmask_q encodes q = n/d: bit (i-1) is 1 iff P[i] | q */
+
+    /* x is a root of unity.  If bitmask_q = 0, then x was a primitive n-th
+     * root of 1 and the result is zero. Return X - 1 to preserve type. */
+    if (!bitmask_q) return gerepileupto(av, gsubgs(X, 1));
+    /* x is a primitive d-th root of unity, where d|n and d<n: we
+     * must multiply ypos by if(isprime(n/d), n/d, 1) */
+    ypos = gmul(ypos, X); /* multiply by X = 1 to preserve type */
+    /* If bitmask_q = 1<<(i-1) for some i <= l, then q == P[i] and we multiply
+     * by P[i]; otherwise q is composite and nothing more needs to be done */
+    if (!(bitmask_q & (bitmask_q-1))) /* detects power of 2, since bitmask!=0 */
+    {
+      i = vals(bitmask_q)+1; /* q = P[i] */
+      ypos = gmulgs(ypos, P[i]);
+    }
+  }
+  return gerepileupto(av, ypos);
+}
+/********************************************************************/
+/**                                                                **/
+/**                  HILBERT & PASCAL MATRICES                     **/
+/**                                                                **/
+/********************************************************************/
+GEN
+mathilbert(long n) /* Hilbert matrix of order n */
+{
+  long i,j;
+  GEN p;
+
+  if (n < 0) pari_err_DOMAIN("mathilbert", "dimension", "<", gen_0, stoi(n));
+  p = cgetg(n+1,t_MAT);
+  for (j=1; j<=n; j++)
+  {
+    gel(p,j) = cgetg(n+1,t_COL);
+    for (i=1+(j==1); i<=n; i++)
+      gcoeff(p,i,j) = mkfrac(gen_1, utoipos(i+j-1));
+  }
+  if (n) gcoeff(p,1,1) = gen_1;
+  return p;
+}
+
+/* q-Pascal triangle = (choose(i,j)_q) (ordinary binomial if q = NULL) */
+GEN
+matqpascal(long n, GEN q)
+{
+  long i, j, I;
+  pari_sp av = avma;
+  GEN m, qpow = NULL; /* gcc -Wall */
+
+  if (n < -1)  pari_err_DOMAIN("matpascal", "n", "<", gen_m1, stoi(n));
+  n++; m = cgetg(n+1,t_MAT);
+  for (j=1; j<=n; j++) gel(m,j) = cgetg(n+1,t_COL);
+  if (q)
+  {
+    I = (n+1)/2;
+    if (I > 1) { qpow = new_chunk(I+1); gel(qpow,2)=q; }
+    for (j=3; j<=I; j++) gel(qpow,j) = gmul(q, gel(qpow,j-1));
+  }
+  for (i=1; i<=n; i++)
+  {
+    I = (i+1)/2; gcoeff(m,i,1)= gen_1;
+    if (q)
+    {
+      for (j=2; j<=I; j++)
+        gcoeff(m,i,j) = gadd(gmul(gel(qpow,j),gcoeff(m,i-1,j)),
+                             gcoeff(m,i-1,j-1));
+    }
+    else
+    {
+      for (j=2; j<=I; j++)
+        gcoeff(m,i,j) = addii(gcoeff(m,i-1,j), gcoeff(m,i-1,j-1));
+    }
+    for (   ; j<=i; j++) gcoeff(m,i,j) = gcoeff(m,i,i+1-j);
+    for (   ; j<=n; j++) gcoeff(m,i,j) = gen_0;
+  }
+  return gerepilecopy(av, m);
+}
+
+/******************************************************************/
+/**                                                              **/
+/**                       PRECISION CHANGES                      **/
+/**                                                              **/
+/******************************************************************/
+
+GEN
+gprec(GEN x, long l)
+{
+  long lx, i;
+  GEN y;
+
+  if (l <= 0) pari_err_DOMAIN("gprec", "precision", "<=", gen_0, stoi(l));
+  switch(typ(x))
+  {
+    case t_REAL:
+      return rtor(x, ndec2prec(l));
+    case t_COMPLEX:
+      y = cgetg(3, t_COMPLEX);
+      gel(y,1) = gprec(gel(x,1),l);
+      gel(y,2) = gprec(gel(x,2),l);
+      break;
+    case t_PADIC:
+      if (!signe(gel(x,4))) return zeropadic(gel(x,2), l+precp(x));
+      y=cgetg(5,t_PADIC);
+      y[1]=x[1]; setprecp(y,l);
+      gel(y,2) = icopy(gel(x,2));
+      gel(y,3) = powiu(gel(x,2),l);
+      gel(y,4) = modii(gel(x,4), gel(y,3));
+      break;
+
+    case t_SER:
+      if (lg(x) == 2) return zeroser(varn(x), l);
+      y=cgetg(l+2,t_SER); y[1]=x[1]; l++; i=l;
+      lx = lg(x);
+      if (l>=lx)
+        for ( ; i>=lx; i--) gel(y,i) = gen_0;
+      for ( ; i>=2; i--) gel(y,i) = gcopy(gel(x,i));
+      break;
+   case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gprec(gel(x,i),l);
+      break;
+    case t_POLMOD: case t_RFRAC: case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gprec(gel(x,i),l);
+      break;
+    default: y = gcopy(x);
+  }
+  return y;
+}
+
+/* internal: precision given in word length (including codewords) */
+GEN
+gprec_w(GEN x, long pr)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      if (signe(x)) return rtor(x,pr);
+      i = -prec2nbits(pr);
+      if (i < expo(x)) return real_0_bit(i);
+      y = cgetr(2); y[1] = x[1]; return y;
+    case t_COMPLEX:
+      y = cgetg(3, t_COMPLEX);
+      gel(y,1) = gprec_w(gel(x,1),pr);
+      gel(y,2) = gprec_w(gel(x,2),pr);
+      break;
+   case t_POL: case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gprec_w(gel(x,i),pr);
+      break;
+    case t_POLMOD: case t_RFRAC: case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gprec_w(gel(x,i),pr);
+      break;
+    default: return x;
+  }
+  return y;
+}
+
+/* internal: precision given in word length (including codewords), truncate
+ * mantissa to precision 'pr' but never _increase_ it */
+GEN
+gprec_wtrunc(GEN x, long pr)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      return (signe(x) && realprec(x) > pr)? rtor(x,pr): x;
+    case t_COMPLEX:
+      y = cgetg(3, t_COMPLEX);
+      gel(y,1) = gprec_wtrunc(gel(x,1),pr);
+      gel(y,2) = gprec_wtrunc(gel(x,2),pr);
+      break;
+    case t_POL:
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gprec_wtrunc(gel(x,i),pr);
+      break;
+    case t_POLMOD: case t_RFRAC: case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gprec_wtrunc(gel(x,i),pr);
+      break;
+    default: return x;
+  }
+  return y;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                      SERIES TRANSFORMS                         **/
+/**                                                                **/
+/********************************************************************/
+/**                  LAPLACE TRANSFORM (OF A SERIES)               **/
+/********************************************************************/
+GEN
+laplace(GEN x)
+{
+  pari_sp av = avma;
+  long i, l = lg(x), e = valp(x);
+  GEN y, t;
+
+  if (typ(x) != t_SER) pari_err_TYPE("laplace",x);
+  if (e < 0) pari_err_DOMAIN("laplace","valuation","<",gen_0,stoi(e));
+  y = cgetg(l,t_SER);
+  t = mpfact(e); y[1] = x[1];
+  for (i=2; i<l; i++)
+  {
+    gel(y,i) = gmul(t, gel(x,i));
+    e++; t = mului(e,t);
+  }
+  return gerepilecopy(av,y);
+}
+
+/********************************************************************/
+/**              CONVOLUTION PRODUCT (OF TWO SERIES)               **/
+/********************************************************************/
+GEN
+convol(GEN x, GEN y)
+{
+  long j, lx, ly, ex, ey, vx = varn(x);
+  GEN z;
+
+  if (typ(x) != t_SER) pari_err_TYPE("convol",x);
+  if (typ(y) != t_SER) pari_err_TYPE("convol",y);
+  if (varn(y) != vx) pari_err_VAR("convol", x,y);
+  ex = valp(x); lx = lg(x) + ex; x -= ex;
+  ey = valp(y); ly = lg(y) + ey; y -= ey;
+  /* inputs shifted: x[i] and y[i] now correspond to monomials of same degree */
+  if (ly < lx) lx = ly; /* min length */
+  if (ex < ey) ex = ey; /* max valuation */
+  if (lx - ex < 3) return zeroser(vx, lx-2);
+
+  z = cgetg(lx - ex, t_SER);
+  z[1] = evalvalp(ex) | evalvarn(vx);
+  for (j = ex+2; j<lx; j++) gel(z,j-ex) = gmul(gel(x,j),gel(y,j));
+  return normalize(z);
+}
+
+/***********************************************************************/
+/*               OPERATIONS ON DIRICHLET SERIES: *, /                  */
+/* (+, -, scalar multiplication are done on the corresponding vectors) */
+/***********************************************************************/
+static long
+dirval(GEN x)
+{
+  long i = 1, lx = lg(x);
+  while (i < lx && gequal0(gel(x,i))) i++;
+  return i;
+}
+
+GEN
+dirmul(GEN x, GEN y)
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  long nx, ny, nz, dx, dy, i, j, k;
+  GEN z;
+
+  if (typ(x)!=t_VEC) pari_err_TYPE("dirmul",x);
+  if (typ(y)!=t_VEC) pari_err_TYPE("dirmul",y);
+  dx = dirval(x); nx = lg(x)-1;
+  dy = dirval(y); ny = lg(y)-1;
+  if (ny-dy < nx-dx) { swap(x,y); lswap(nx,ny); lswap(dx,dy); }
+  nz = minss(nx*dy,ny*dx);
+  z = zerovec(nz);
+  for (j=dx; j<=nx; j++)
+  {
+    GEN c = gel(x,j);
+    if (gequal0(c)) continue;
+    if (gequal1(c))
+      for (k=dy,i=j*dy; i<=nz; i+=j,k++) gel(z,i) = gadd(gel(z,i),gel(y,k));
+    else if (gequalm1(c))
+      for (k=dy,i=j*dy; i<=nz; i+=j,k++) gel(z,i) = gsub(gel(z,i),gel(y,k));
+    else
+      for (k=dy,i=j*dy; i<=nz; i+=j,k++) gel(z,i) = gadd(gel(z,i),gmul(c,gel(y,k)));
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGLEVEL) err_printf("doubling stack in dirmul\n");
+      z = gerepilecopy(av,z);
+    }
+  }
+  return gerepilecopy(av,z);
+}
+
+GEN
+dirdiv(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long nx,ny,nz,dx,dy,i,j;
+  GEN z,p1;
+
+  if (typ(x)!=t_VEC) pari_err_TYPE("dirdiv",x);
+  if (typ(y)!=t_VEC) pari_err_TYPE("dirdiv",y);
+  dx = dirval(x); nx = lg(x)-1;
+  dy = dirval(y); ny = lg(y)-1;
+  if (dy != 1 || !ny) pari_err_INV("dirdiv",y);
+  nz = minss(nx,ny*dx); p1 = gel(y,1);
+  if (!gequal1(p1)) { y = gdiv(y,p1); x = gdiv(x,p1); } else x = leafcopy(x);
+  z = zerovec(nz);
+  for (j=dx; j<=nz; j++)
+  {
+    GEN c = gel(x,j); gel(z,j) = c;
+    if (gequal0(c)) continue;
+    if (gequal1(c))
+      for (i=j+j; i<=nz; i+=j) gel(x,i) = gsub(gel(x,i),gel(y,i/j));
+    else if (gequalm1(c))
+      for (i=j+j; i<=nz; i+=j) gel(x,i) = gadd(gel(x,i),gel(y,i/j));
+    else
+      for (i=j+j; i<=nz; i+=j) gel(x,i) = gsub(gel(x,i),gmul(c,gel(y,i/j)));
+  }
+  return gerepilecopy(av,z);
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                       COMBINATORICS                           **/
+/**                                                               **/
+/*******************************************************************/
+/**                      BINOMIAL COEFFICIENTS                    **/
+/*******************************************************************/
+GEN
+binomialuu(ulong n, ulong k)
+{
+  pari_sp ltop = avma;
+  GEN z;
+  if (k > n) return gen_0;
+  k = minuu(k,n-k);
+  if (!k) return gen_1;
+  if (k == 1) return utoipos(n);
+  z = diviiexact(mulu_interval(n-k+1, n), mulu_interval(2UL, k));
+  return gerepileuptoint(ltop,z);
+}
+
+GEN
+binomial(GEN n, long k)
+{
+  long i, prec;
+  pari_sp av;
+  GEN y;
+
+  if (k <= 1)
+  {
+    if (is_noncalc_t(typ(n))) pari_err_TYPE("binomial",n);
+    if (k < 0) return gen_0;
+    if (k == 0) return gen_1;
+    return gcopy(n);
+  }
+  av = avma;
+  if (typ(n) == t_INT)
+  {
+    if (signe(n) > 0)
+    {
+      GEN z = subis(n,k);
+      if (cmpis(z,k) < 0)
+      {
+        k = itos(z); avma = av;
+        if (k <= 1)
+        {
+          if (k < 0) return gen_0;
+          if (k == 0) return gen_1;
+          return icopy(n);
+        }
+      }
+    }
+    /* k > 1 */
+    if (lgefint(n) == 3 && signe(n) > 0)
+    {
+      y = binomialuu(itou(n),(ulong)k);
+      return gerepileupto(av, y);
+    }
+    else
+    {
+      y = cgetg(k+1,t_VEC);
+      for (i=1; i<=k; i++) gel(y,i) = subis(n,i-1);
+      y = divide_conquer_prod(y,mulii);
+    }
+    y = diviiexact(y, mpfact(k));
+    return gerepileuptoint(av, y);
+  }
+
+  prec = precision(n);
+  if (prec && k > 200 + 0.8*prec2nbits(prec)) {
+    GEN A = mpfactr(k, prec), B = ggamma(gsubgs(n,k-1), prec);
+    return gerepileupto(av, gdiv(ggamma(gaddgs(n,1), prec), gmul(A,B)));
+  }
+
+  y = cgetg(k+1,t_VEC);
+  for (i=1; i<=k; i++) gel(y,i) = gsubgs(n,i-1);
+  y = divide_conquer_prod(y,gmul);
+  y = gdiv(y, mpfact(k));
+  return gerepileupto(av, y);
+}
+
+/* Assume n >= 0, return bin, bin[k+1] = binomial(n, k) */
+GEN
+vecbinome(long n)
+{
+  long d, k;
+  GEN C;
+  if (!n) return mkvec(gen_1);
+  C = cgetg(n+2, t_VEC) + 1; /* C[k] = binomial(n, k) */
+  gel(C,0) = gen_1;
+  gel(C,1) = utoipos(n); d = (n + 1) >> 1;
+  for (k=2; k <= d; k++)
+  {
+    pari_sp av = avma;
+    gel(C,k) = gerepileuptoint(av, diviuexact(mului(n-k+1, gel(C,k-1)), k));
+  }
+  for (   ; k <= n; k++) gel(C,k) = gel(C,n-k);
+  return C - 1;
+}
+
+/********************************************************************/
+/**                  STIRLING NUMBERS                              **/
+/********************************************************************/
+/* Stirling number of the 2nd kind. The number of ways of partitioning
+   a set of n elements into m non-empty subsets. */
+GEN
+stirling2(ulong n, ulong m)
+{
+  pari_sp av = avma, lim = stack_lim(av, 2);
+  GEN s, bmk;
+  ulong k;
+  if (n==0) return (m == 0)? gen_1: gen_0;
+  if (m > n || m == 0) return gen_0;
+  if (m==n) return gen_1;
+  /* k = 0 */
+  bmk = gen_1; s  = powuu(m, n);
+  for (k = 1; k <= ((m-1)>>1); ++k)
+  { /* bmk = binomial(m, k) */
+    GEN c, kn, mkn;
+    bmk = diviuexact(mului(m-k+1, bmk), k);
+    kn  = powuu(k, n); mkn = powuu(m-k, n);
+    c = odd(m)? subii(mkn,kn): addii(mkn,kn);
+    c = mulii(bmk, c);
+    s = odd(k)? subii(s, c): addii(s, c);
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"stirling2");
+      gerepileall(av, 2, &s, &bmk);
+    }
+  }
+  /* k = m/2 */
+  if (!odd(m))
+  {
+    GEN c;
+    bmk = diviuexact(mului(k+1, bmk), k);
+    c = mulii(bmk, powuu(k,n));
+    s = odd(k)? subii(s, c): addii(s, c);
+  }
+  return gerepileuptoint(av, diviiexact(s, mpfact(m)));
+}
+
+/* Stirling number of the first kind. Up to the sign, the number of
+   permutations of n symbols which have exactly m cycles. */
+GEN
+stirling1(ulong n, ulong m)
+{
+  pari_sp ltop=avma;
+  ulong k;
+  GEN s, t;
+  if (n < m) return gen_0;
+  else if (n==m) return gen_1;
+  /* t = binomial(n-1+k, m-1) * binomial(2n-m, n-m-k) */
+  /* k = n-m > 0 */
+  t = binomialuu(2*n-m-1, m-1);
+  s = mulii(t, stirling2(2*(n-m), n-m));
+  if (odd(n-m)) togglesign(s);
+  for (k = n-m-1; k > 0; --k)
+  {
+    GEN c;
+    t = diviuuexact(muluui(n-m+k+1, n+k+1, t), n+k, n-m-k);
+    c = mulii(t, stirling2(n-m+k, k));
+    s = odd(k)? subii(s, c): addii(s, c);
+    if ((k & 0x1f) == 0) {
+      t = gerepileuptoint(ltop, t);
+      s = gerepileuptoint(avma, s);
+    }
+  }
+  return gerepileuptoint(ltop, s);
+}
+
+GEN
+stirling(long n, long m, long flag)
+{
+  if (n < 0) pari_err_DOMAIN("stirling", "n", "<", gen_0, stoi(n));
+  if (m < 0) pari_err_DOMAIN("stirling", "m", "<", gen_0, stoi(m));
+  switch (flag)
+  {
+    case 1: return stirling1((ulong)n,(ulong)m);
+    case 2: return stirling2((ulong)n,(ulong)m);
+    default: pari_err_FLAG("stirling");
+  }
+  return NULL; /*NOT REACHED*/
+}
+
+/***********************************************************************/
+/**                          PERMUTATIONS                             **/
+/***********************************************************************/
+GEN
+numtoperm(long n, GEN x)
+{
+  pari_sp av, lim;
+  ulong i, r;
+  GEN v;
+
+  if (n < 0) pari_err_DOMAIN("numtoperm", "n", "<", gen_0, stoi(n));
+  if (typ(x) != t_INT) pari_err_TYPE("numtoperm",x);
+  v = cgetg(n+1, t_VEC); if (n==0) return v;
+  v[n] = 1; av = avma; lim = stack_lim(av,2);
+  if (signe(x) <= 0) x = modii(x, mpfact(n));
+  for (r=n-1; r>=1; r--)
+  {
+    ulong a;
+    x = diviu_rem(x, n+1-r,&a);
+    for (i=r+1; i<=(ulong)n; i++)
+      if((ulong)v[i]>a) v[i]++;
+    v[r] = a+1;
+    if (low_stack(lim, stack_lim(av,2)))
+      x = gerepileuptoint(av, x);
+  }
+  avma = av;
+  for (i=1; i<=(ulong)n; i++) gel(v,i) = utoipos(v[i]);
+  return v;
+}
+
+GEN
+permtonum(GEN p)
+{
+  long n = lg(p)-1, i, r;
+  pari_sp av = avma, av2, lim;
+  GEN v, x;
+
+  if (!is_vec_t(typ(p))) pari_err_TYPE("permtonum",p);
+  v = cgetg(n+1,t_VECSMALL);
+  for (i=1; i<=n; i++)
+  {
+    GEN pi = gel(p, i);
+    if (typ(pi) != t_INT) pari_err_TYPE("permtonum",pi);
+    v[i] = itos(pi);
+  }
+  x = gen_0; av2 = avma; lim = stack_lim(av2,2);
+  for (i=1; i<=n; i++)
+  {
+    long vi = v[i];
+    x = i==1 ? stoi(v[1]-1): addiu(mulis(x,n+1-i),vi-1);
+    for (r=i+1; r<=n; r++)
+      if (v[r]>vi) v[r]--;
+    if (low_stack(lim, stack_lim(av,2)))
+      x = gerepileuptoint(av2, x);
+  }
+  return gerepileuptoint(av, x);
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                     RECIPROCAL POLYNOMIAL                     **/
+/**                                                               **/
+/*******************************************************************/
+/* return coefficients s.t x = x_0 X^n + ... + x_n */
+GEN
+polrecip(GEN x)
+{
+  if (typ(x) != t_POL) pari_err_TYPE("polrecip",x);
+  return RgX_recip(x);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                  POLYNOMIAL INTERPOLATION                      **/
+/**                                                                **/
+/********************************************************************/
+/* allow X = NULL for [1,...,n] */
+GEN
+RgV_polint(GEN X, GEN Y, long v)
+{
+  pari_sp av0 = avma, av, lim;
+  GEN Q, P = NULL;
+  long i, l = lg(Y);
+  if (!X)
+  {
+    X = cgetg(l, t_VEC);
+    for (i=1; i<l; i++) gel(X,i) = utoipos(i);
+  }
+  Q = roots_to_pol(X, v); av = avma; lim = stack_lim(av,2);
+  for (i=1; i<l; i++)
+  {
+    GEN inv, T, dP;
+    if (gequal0(gel(Y,i))) continue;
+    T = RgX_div_by_X_x(Q, gel(X,i), NULL);
+    inv = ginv(poleval(T,gel(X,i)));
+    dP = RgX_Rg_mul(T, gmul(gel(Y,i),inv));
+    P = P? RgX_add(P, dP): dP;
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FpV_polint");
+      P = gerepileupto(av, P);
+    }
+  }
+  if (!P) { avma = av; return zeropol(v); }
+  return gerepileupto(av0, P);
+}
+/* X,Y are "spec" GEN vectors with n > 1 components ( at X[0], ... X[n-1] ) */
+GEN
+polint_i(GEN X, GEN Y, GEN x, long n, GEN *ptdy)
+{
+  long i, m, ns = 0;
+  pari_sp av = avma;
+  GEN y, c, d, dy = NULL; /* gcc -Wall */
+
+  if (!X)
+  {
+    X = cgetg(n+1, t_VEC);
+    for (i=1; i<=n; i++) gel(X,i) = utoipos(i);
+    X++;
+  }
+  switch(typ(x)) {
+    case t_INT: case t_REAL: case t_FRAC: case t_COMPLEX: case t_QUAD:
+    {
+      GEN D = NULL;
+      for (i=0; i<n; i++)
+      {
+        GEN t = gsub(x,gel(X,i));
+        switch(typ(t))
+        {
+          case t_INT: case t_REAL: case t_FRAC: case t_COMPLEX: case t_QUAD:
+            t = gabs(t, DEFAULTPREC);
+            if (!D || gcmp(t,D) < 0) { ns = i; D = t; }
+            break;
+          default:
+            goto NODY;
+        }
+      }
+      break;
+      /* X[ns] is closest to x */
+    }
+NODY:
+    default:
+      if (ptdy) { *ptdy = gen_0; ptdy = NULL; }
+  }
+  c = new_chunk(n);
+  d = new_chunk(n); for (i=0; i<n; i++) gel(c,i) = gel(d,i) = gel(Y,i);
+  y = gel(d,ns--);
+  /* divided differences */
+  for (m=1; m<n; m++)
+  {
+    for (i=0; i<n-m; i++)
+    {
+      GEN ho = gsub(gel(X,i),x), hp = gsub(gel(X,i+m),x), den = gsub(ho,hp);
+      if (gequal0(den))
+      {
+        char *x1 = stack_sprintf("X[%ld]", i+1);
+        char *x2 = stack_sprintf("X[%ld]", i+m+1);
+        pari_err_DOMAIN("polinterpolate",x1,"=",strtoGENstr(x2), X);
+      }
+      den = gdiv(gsub(gel(c,i+1),gel(d,i)), den);
+      gel(c,i) = gmul(ho,den);
+      gel(d,i) = gmul(hp,den);
+    }
+    dy = (2*(ns+1) < n-m)? gel(c,ns+1): gel(d,ns--);
+    y = gadd(y,dy);
+  }
+  if (!ptdy) return gerepileupto(av, y);
+  *ptdy = dy;
+  gerepileall(av, 2, &y, ptdy);
+  return y;
+}
+
+GEN
+polint(GEN X, GEN Y, GEN x, GEN *ptdy)
+{
+  long lx = lg(X);
+
+  if (! is_vec_t(typ(X))) pari_err_TYPE("polinterpolate",X);
+  if (Y)
+  {
+    if (! is_vec_t(typ(Y))) pari_err_TYPE("polinterpolate",Y);
+    if (lx != lg(Y)) pari_err_DIM("polinterpolate");
+  }
+  else
+  {
+    Y = X;
+    X = NULL;
+  }
+
+  if (lx <= 2)
+  {
+    if (ptdy) *ptdy = gen_0;
+    if (lx == 1) return zeropol(0);
+    Y = gel(Y,1);
+    if (gvar(Y) == 0) pari_err_PRIORITY("polinterpolate", Y, "=", 0);
+    return scalarpol(Y, 0);
+  }
+  if (!x) return RgV_polint(X, Y, 0);
+  if (gequalX(x)) return RgV_polint(X, Y, varn(x));
+  return polint_i(X? X+1: NULL,Y+1,x,lx-1,ptdy);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       MODREVERSE                               **/
+/**                                                                **/
+/********************************************************************/
+static void
+err_reverse(GEN x, GEN T)
+{
+  pari_err_DOMAIN("modreverse","deg(minpoly(z))", "<", stoi(degpol(T)),
+                  mkpolmod(x,T));
+}
+
+/* return y such that Mod(y, charpoly(Mod(a,T)) = Mod(a,T) */
+GEN
+RgXQ_reverse(GEN a, GEN T)
+{
+  pari_sp av = avma;
+  long n = degpol(T);
+  GEN y;
+
+  if (n <= 1) {
+    if (n <= 0) return gcopy(a);
+    return gerepileupto(av, gneg(gdiv(gel(T,2), gel(T,3))));
+  }
+  if (typ(a) != t_POL || !signe(a)) err_reverse(a,T);
+  y = RgXV_to_RgM(RgXQ_powers(a,n-1,T), n);
+  y = RgM_solve(y, col_ei(n, 2));
+  if (!y) err_reverse(a,T);
+  return gerepilecopy(av, RgV_to_RgX(y, varn(T)));
+}
+GEN
+QXQ_reverse(GEN a, GEN T)
+{
+  pari_sp av = avma;
+  long n = degpol(T);
+  GEN y;
+
+  if (n <= 1) {
+    if (n <= 0) return gcopy(a);
+    return gerepileupto(av, gneg(gdiv(gel(T,2), gel(T,3))));
+  }
+  if (typ(a) != t_POL || !signe(a)) err_reverse(a,T);
+  if (gequalX(a)) return gcopy(a);
+  y = RgXV_to_RgM(QXQ_powers(a,n-1,T), n);
+  y = RgM_solve(y, col_ei(n, 2));
+  if (!y) err_reverse(a,T);
+  return gerepilecopy(av, RgV_to_RgX(y, varn(T)));
+}
+
+GEN
+modreverse(GEN x)
+{
+  long v, n;
+  GEN T, a, y;
+
+  if (typ(x)!=t_POLMOD) pari_err_TYPE("modreverse",x);
+  T = gel(x,1); n = degpol(T); if (n <= 0) return gcopy(x);
+  a = gel(x,2);
+  v = varn(T);
+  y = cgetg(3,t_POLMOD);
+  gel(y,1) = (n==1)? gsub(pol_x(v), a): RgXQ_charpoly(a, T, v);
+  gel(y,2) = RgXQ_reverse(a, T); return y;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                          MERGESORT                             **/
+/**                                                                **/
+/********************************************************************/
+static int
+cmp_small(GEN x, GEN y) {
+  long a = (long)x, b = (long)y;
+  return a>b? 1: (a<b? -1: 0);
+}
+
+static int
+veccmp(void *data, GEN x, GEN y)
+{
+  GEN k = (GEN)data;
+  long i, s, lk = lg(k), lx = minss(lg(x), lg(y));
+
+  if (!is_vec_t(typ(x))) pari_err_TYPE("lexicographic vecsort",x);
+  if (!is_vec_t(typ(y))) pari_err_TYPE("lexicographic vecsort",y);
+  for (i=1; i<lk; i++)
+  {
+    long c = k[i];
+    if (c >= lx)
+      pari_err_TYPE("lexicographic vecsort, index too large", stoi(c));
+    s = lexcmp(gel(x,c), gel(y,c));
+    if (s) return s;
+  }
+  return 0;
+}
+
+/* return permutation sorting v[1..n], removing duplicates. Assume n > 0 */
+static GEN
+gen_sortspec_uniq(GEN v, long n, void *E, int (*cmp)(void*,GEN,GEN))
+{
+  pari_sp av;
+  long NX, nx, ny, m, ix, iy, i;
+  GEN x, y, w, W;
+  int s;
+  switch(n)
+  {
+    case 1: return mkvecsmall(1);
+    case 2:
+      s = cmp(E,gel(v,1),gel(v,2));
+      if      (s < 0) return mkvecsmall2(1,2);
+      else if (s > 0) return mkvecsmall2(2,1);
+      return mkvecsmall(1);
+    case 3:
+      s = cmp(E,gel(v,1),gel(v,2));
+      if (s < 0) {
+        s = cmp(E,gel(v,2),gel(v,3));
+        if (s < 0) return mkvecsmall3(1,2,3);
+        else if (s == 0) return mkvecsmall2(1,2);
+        s = cmp(E,gel(v,1),gel(v,3));
+        if      (s < 0) return mkvecsmall3(1,3,2);
+        else if (s > 0) return mkvecsmall3(3,1,2);
+        return mkvecsmall2(1,2);
+      } else if (s > 0) {
+        s = cmp(E,gel(v,1),gel(v,3));
+        if (s < 0) return mkvecsmall3(2,1,3);
+        else if (s == 0) return mkvecsmall2(2,1);
+        s = cmp(E,gel(v,2),gel(v,3));
+        if (s < 0) return mkvecsmall3(2,3,1);
+        else if (s > 0) return mkvecsmall3(3,2,1);
+        return mkvecsmall2(2,1);
+      } else {
+        s = cmp(E,gel(v,1),gel(v,3));
+        if (s < 0) return mkvecsmall2(1,3);
+        else if (s == 0) return mkvecsmall(1);
+        return mkvecsmall2(3,1);
+      }
+  }
+  NX = nx = n>>1; ny = n-nx;
+  av = avma;
+  x = gen_sortspec_uniq(v,   nx,E,cmp); nx = lg(x)-1;
+  y = gen_sortspec_uniq(v+NX,ny,E,cmp); ny = lg(y)-1;
+  w = cgetg(n+1, t_VECSMALL);
+  m = ix = iy = 1;
+  while (ix<=nx && iy<=ny)
+  {
+    s = cmp(E, gel(v,x[ix]), gel(v,y[iy]+NX));
+    if (s < 0)
+      w[m++] = x[ix++];
+    else if (s > 0)
+      w[m++] = y[iy++]+NX;
+    else {
+      w[m++] = x[ix++];
+      iy++;
+    }
+  }
+  while (ix<=nx) w[m++] = x[ix++];
+  while (iy<=ny) w[m++] = y[iy++]+NX;
+  avma = av;
+  W = cgetg(m, t_VECSMALL);
+  for (i = 1; i < m; i++) W[i] = w[i];
+  return W;
+}
+
+/* return permutation sorting v[1..n]. Assume n > 0 */
+static GEN
+gen_sortspec(GEN v, long n, void *E, int (*cmp)(void*,GEN,GEN))
+{
+  long nx, ny, m, ix, iy;
+  GEN x, y, w;
+  switch(n)
+  {
+    case 1:
+      (void)cmp(E,gel(v,1),gel(v,1)); /* check for type error */
+      return mkvecsmall(1);
+    case 2:
+      return cmp(E,gel(v,1),gel(v,2)) <= 0? mkvecsmall2(1,2)
+                                          : mkvecsmall2(2,1);
+    case 3:
+      if (cmp(E,gel(v,1),gel(v,2)) <= 0) {
+        if (cmp(E,gel(v,2),gel(v,3)) <= 0) return mkvecsmall3(1,2,3);
+        return (cmp(E,gel(v,1),gel(v,3)) <= 0)? mkvecsmall3(1,3,2)
+                                              : mkvecsmall3(3,1,2);
+      } else {
+        if (cmp(E,gel(v,1),gel(v,3)) <= 0) return mkvecsmall3(2,1,3);
+        return (cmp(E,gel(v,2),gel(v,3)) <= 0)? mkvecsmall3(2,3,1)
+                                              : mkvecsmall3(3,2,1);
+      }
+  }
+  nx = n>>1; ny = n-nx;
+  w = cgetg(n+1,t_VECSMALL);
+  x = gen_sortspec(v,   nx,E,cmp);
+  y = gen_sortspec(v+nx,ny,E,cmp);
+  m = ix = iy = 1;
+  while (ix<=nx && iy<=ny)
+    if (cmp(E, gel(v,x[ix]), gel(v,y[iy]+nx))<=0)
+      w[m++] = x[ix++];
+    else
+      w[m++] = y[iy++]+nx;
+  while (ix<=nx) w[m++] = x[ix++];
+  while (iy<=ny) w[m++] = y[iy++]+nx;
+  avma = (pari_sp)w; return w;
+}
+
+static void
+init_sort(GEN *x, long *tx, long *lx)
+{
+  *tx = typ(*x);
+  if (*tx == t_LIST) {
+    *x = list_data(*x);
+    *lx = *x? lg(*x): 1;
+  } else {
+    if (!is_matvec_t(*tx) && *tx != t_VECSMALL) pari_err_TYPE("gen_sort",*x);
+    *lx = lg(*x);
+  }
+}
+
+/* (x o y)[1..lx-1], destroy y */
+INLINE GEN
+sort_extract(GEN x, GEN y, long tx, long lx)
+{
+  long i;
+  switch(tx)
+  {
+    case t_VECSMALL:
+      for (i=1; i<lx; i++) y[i] = x[y[i]];
+      break;
+    case t_LIST:
+      settyp(y,t_VEC);
+      for (i=1; i<lx; i++) gel(y,i) = gel(x,y[i]);
+      return gtolist(y);
+    default:
+      settyp(y,tx);
+      for (i=1; i<lx; i++) gel(y,i) = gcopy(gel(x,y[i]));
+  }
+  return y;
+}
+
+/* Sort the vector x, using cmp to compare entries. */
+GEN
+gen_sort_uniq(GEN x, void *E, int (*cmp)(void*,GEN,GEN))
+{
+  long tx, lx;
+  GEN y;
+
+  init_sort(&x, &tx, &lx);
+  if (lx==1) return tx == t_LIST? listcreate(): cgetg(1, tx);
+  y = gen_sortspec_uniq(x,lx-1,E,cmp);
+  return sort_extract(x, y, tx, lg(y)); /* lg(y) <= lx */
+}
+/* Sort the vector x, using cmp to compare entries. */
+GEN
+gen_sort(GEN x, void *E, int (*cmp)(void*,GEN,GEN))
+{
+  long tx, lx;
+  GEN y;
+
+  init_sort(&x, &tx, &lx);
+  if (lx==1) return tx == t_LIST? listcreate(): cgetg(1, tx);
+  y = gen_sortspec(x,lx-1,E,cmp);
+  return sort_extract(x, y, tx, lx);
+}
+/* indirect sort: return the permutation that would sort x */
+GEN
+gen_indexsort_uniq(GEN x, void *E, int (*cmp)(void*,GEN,GEN))
+{
+  long tx, lx;
+  init_sort(&x, &tx, &lx);
+  if (lx==1) return cgetg(1, t_VECSMALL);
+  return gen_sortspec_uniq(x,lx-1,E,cmp);
+}
+/* indirect sort: return the permutation that would sort x */
+GEN
+gen_indexsort(GEN x, void *E, int (*cmp)(void*,GEN,GEN))
+{
+  long tx, lx;
+  init_sort(&x, &tx, &lx);
+  if (lx==1) return cgetg(1, t_VECSMALL);
+  return gen_sortspec(x,lx-1,E,cmp);
+}
+
+/* Sort the vector x in place, using cmp to compare entries */
+void
+gen_sort_inplace(GEN x, void *E, int (*cmp)(void*,GEN,GEN), GEN *perm)
+{
+  long tx, lx, i;
+  pari_sp av = avma;
+  GEN y;
+
+  init_sort(&x, &tx, &lx);
+  if (lx<=2)
+  {
+    if (perm) *perm = lx == 1? cgetg(1, t_VECSMALL): mkvecsmall(1);
+    return;
+  }
+  y = gen_sortspec(x,lx-1, E, cmp);
+  if (perm)
+  {
+    GEN z = new_chunk(lx);
+    for (i=1; i<lx; i++) gel(z,i) = gel(x,y[i]);
+    for (i=1; i<lx; i++) gel(x,i) = gel(z,i);
+    *perm = y;
+    avma = (pari_sp)y;
+  } else {
+    for (i=1; i<lx; i++) gel(y,i) = gel(x,y[i]);
+    for (i=1; i<lx; i++) gel(x,i) = gel(y,i);
+    avma = av;
+  }
+}
+
+static int
+closurecmp(void *data, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN z = closure_callgen2((GEN)data, x,y);
+  if (typ(z) != t_INT)
+    pari_err_TYPE("closurecmp, cmp. fun. must return an integer", z);
+  avma = av; return signe(z);
+}
+
+static void
+check_positive_entries(GEN k)
+{
+  long i, l = lg(k);
+  for (i=1; i<l; i++)
+    if (k[i] <= 0) pari_err_DOMAIN("sort_function", "index", "<", gen_0, stoi(k[i]));
+}
+
+typedef int (*CMP_FUN)(void*,GEN,GEN);
+static CMP_FUN
+sort_function(void **E, GEN x, GEN k)
+{
+  int (*cmp)(GEN,GEN) = &lexcmp;
+  if (!k)
+  {
+    *E = (void*)((typ(x) == t_VECSMALL)? cmp_small: cmp);
+    return &cmp_nodata;
+  }
+  if (typ(x) == t_VECSMALL) pari_err_TYPE("sort_function", x);
+  switch(typ(k))
+  {
+    case t_INT: k = mkvecsmall(itos(k));  break;
+    case t_VEC: case t_COL: k = ZV_to_zv(k); break;
+    case t_VECSMALL: break;
+    case t_CLOSURE:
+     if (closure_arity(k) != 2)
+       pari_err_TYPE("sort_function, cmp. fun. needs exactly 2 arguments",k);
+     *E = (void*)k;
+     return &closurecmp;
+    default: pari_err_TYPE("sort_function",k);
+  }
+  check_positive_entries(k);
+  *E = (void*)k;
+  return &veccmp;
+}
+
+#define cmp_IND 1
+#define cmp_LEX 2 /* FIXME: backward compatibility, ignored */
+#define cmp_REV 4
+#define cmp_UNIQ 8
+GEN
+vecsort0(GEN x, GEN k, long flag)
+{
+  void *E;
+  int (*CMP)(void*,GEN,GEN) = sort_function(&E, x, k);
+
+  if (flag < 0 || flag > (cmp_REV|cmp_LEX|cmp_IND|cmp_UNIQ))
+    pari_err_FLAG("vecsort");
+  if (flag & cmp_UNIQ)
+    x = flag & cmp_IND? gen_indexsort_uniq(x, E, CMP): gen_sort_uniq(x, E, CMP);
+  else
+    x = flag & cmp_IND? gen_indexsort(x, E, CMP): gen_sort(x, E, CMP);
+  if (flag & cmp_REV) { /* reverse order */
+    long j, lx;
+    GEN y;
+    if (typ(x)==t_LIST)
+    {
+      y = list_data(x);
+      if (!y) return x;
+    }
+    else
+      y = x;
+    lx = lg(y);
+    for (j=1; j<=(lx-1)>>1; j++) swap(gel(y,j), gel(y,lx-j));
+  }
+  return x;
+}
+
+GEN
+indexsort(GEN x) { return gen_indexsort(x, (void*)&gcmp, cmp_nodata); }
+GEN
+indexlexsort(GEN x) { return gen_indexsort(x, (void*)&lexcmp, cmp_nodata); }
+GEN
+indexvecsort(GEN x, GEN k)
+{
+  if (typ(k) != t_VECSMALL) pari_err_TYPE("vecsort",k);
+  return gen_indexsort(x, (void*)k, &veccmp);
+}
+
+GEN
+sort(GEN x) { return gen_sort(x, (void*)gcmp, cmp_nodata); }
+GEN
+lexsort(GEN x) { return gen_sort(x, (void*)lexcmp, cmp_nodata); }
+GEN
+vecsort(GEN x, GEN k)
+{
+  if (typ(k) != t_VECSMALL) pari_err_TYPE("vecsort",k);
+  return gen_sort(x, (void*)k, &veccmp);
+}
+long
+vecsearch(GEN v, GEN x, GEN k)
+{
+  pari_sp av = avma;
+  void *E;
+  int (*CMP)(void*,GEN,GEN) = sort_function(&E, x, k);
+  long r;
+  if (!is_matvec_t(typ(v))) pari_err_TYPE("vecsearch", v);
+  r = gen_search(v, x, 0, E, CMP);
+  avma = av; return r;
+}
+
+GEN
+ZV_indexsort(GEN L) { return gen_indexsort(L, (void*)&cmpii, &cmp_nodata); }
+GEN
+ZV_sort(GEN L) { return gen_sort(L, (void*)&cmpii, &cmp_nodata); }
+GEN
+ZV_sort_uniq(GEN L) { return gen_sort_uniq(L, (void*)&cmpii, &cmp_nodata); }
+
+/********************************************************************/
+/**                      SEARCH IN SORTED VECTOR                   **/
+/********************************************************************/
+/* index of x in table T, 0 otherwise */
+long
+tablesearch(GEN T, GEN x, int (*cmp)(GEN,GEN))
+{
+  long l = 1, u = lg(T)-1, i, s;
+
+  while (u>=l)
+  {
+    i = (l+u)>>1; s = cmp(x, gel(T,i));
+    if (!s) return i;
+    if (s<0) u=i-1; else l=i+1;
+  }
+  return 0;
+}
+
+/* looks if x belongs to the set T and returns the index if yes, 0 if no */
+long
+gen_search(GEN T, GEN x, long flag, void *data, int (*cmp)(void*,GEN,GEN))
+{
+  long lx = lg(T), i, l, u, s;
+
+  if (lx==1) return flag? 1: 0;
+  l = 1; u = lx-1;
+  do
+  {
+    i = (l+u)>>1; s = cmp(data, x, gel(T,i));
+    if (!s) return flag? 0: i;
+    if (s<0) u=i-1; else l=i+1;
+  } while (u>=l);
+  if (!flag) return 0;
+  return (s<0)? i: i+1;
+}
+
+long
+ZV_search(GEN x, GEN y) { return tablesearch(x, y, cmpii); }
+
+long
+zv_search(GEN x, long y) { return tablesearch(x, (GEN)y, cmp_small); }
+
+/********************************************************************/
+/**                   COMPARISON FUNCTIONS                         **/
+/********************************************************************/
+int
+cmp_nodata(void *data, GEN x, GEN y)
+{
+  int (*cmp)(GEN,GEN)=(int (*)(GEN,GEN)) data;
+  return cmp(x,y);
+}
+
+/* assume x and y come from the same idealprimedec call (uniformizer unique) */
+int
+cmp_prime_over_p(GEN x, GEN y)
+{
+  long k = pr_get_f(x) - pr_get_f(y); /* diff. between residue degree */
+  return k? ((k > 0)? 1: -1)
+          : ZV_cmp(pr_get_gen(x), pr_get_gen(y));
+}
+
+int
+cmp_prime_ideal(GEN x, GEN y)
+{
+  int k = cmpii(pr_get_p(x), pr_get_p(y));
+  return k? k: cmp_prime_over_p(x,y);
+}
+
+/* assume x and y are t_POL in the same variable whose coeffs can be
+ * compared (used to sort polynomial factorizations) */
+int
+gen_cmp_RgX(void *data, GEN x, GEN y)
+{
+  int (*coeff_cmp)(GEN,GEN)=(int(*)(GEN,GEN))data;
+  long i, lx = lg(x), ly = lg(y);
+  int fl;
+  if (lx > ly) return  1;
+  if (lx < ly) return -1;
+  for (i=lx-1; i>1; i--)
+    if ((fl = coeff_cmp(gel(x,i), gel(y,i)))) return fl;
+  return 0;
+}
+
+static int
+cmp_RgX_Rg(GEN x, GEN y)
+{
+  long lx = lg(x);
+  if (lx > 3) return  1;
+  if (lx < 3) return -1;
+  return gcmp(gel(x,2), y);
+}
+int
+cmp_RgX(GEN x, GEN y)
+{
+  if (typ(x) == t_POLMOD) x = gel(x,2);
+  if (typ(y) == t_POLMOD) y = gel(y,2);
+  if (typ(x) == t_POL) {
+    if (typ(y) != t_POL) return cmp_RgX_Rg(x, y);
+  } else {
+    if (typ(y) != t_POL) return gcmp(x,y);
+    return - cmp_RgX_Rg(y,x);
+  }
+  return gen_cmp_RgX((void*)&gcmp,x,y);
+}
+
+/********************************************************************/
+/**                   MERGE & SORT FACTORIZATIONS                  **/
+/********************************************************************/
+/* merge fx, fy two factorizations, whose 1st column is sorted in strictly
+ * increasing order wrt cmp. Keep 0 exponents. */
+GEN
+merge_factor(GEN fx, GEN fy, void *data, int (*cmp)(void *,GEN,GEN))
+{
+  GEN x = gel(fx,1), e = gel(fx,2), M, E;
+  GEN y = gel(fy,1), f = gel(fy,2);
+  long ix, iy, m, lx = lg(x), ly = lg(y), l = lx+ly-1;
+
+  M = cgetg(l, t_COL);
+  E = cgetg(l, t_COL);
+
+  m = ix = iy = 1;
+  while (ix<lx && iy<ly)
+  {
+    int s = cmp(data, gel(x,ix), gel(y,iy));
+    if (s < 0)
+    { gel(M,m) = gel(x,ix); gel(E,m) = gel(e,ix); ix++; }
+    else if (s == 0)
+    { gel(M,m) = gel(x,ix); gel(E,m) = addii(gel(e,ix), gel(f,iy)); iy++; ix++; }
+    else
+    { gel(M,m) = gel(y,iy); gel(E,m) = gel(f,iy); iy++; }
+    m++;
+  }
+  while (ix<lx) { gel(M,m) = gel(x,ix); gel(E,m) = gel(e,ix); ix++; m++; }
+  while (iy<ly) { gel(M,m) = gel(y,iy); gel(E,m) = gel(f,iy); iy++; m++; }
+  setlg(M, m);
+  setlg(E, m); return mkmat2(M, E);
+}
+/* merge two sorted vectors, removing duplicates. Shallow */
+GEN
+merge_sort_uniq(GEN x, GEN y, void *data, int (*cmp)(void *,GEN,GEN))
+{
+  long ix, iy, m, lx = lg(x), ly = lg(y), l = lx+ly-1;
+  GEN M;
+
+  M = cgetg(l, t_COL);
+  m = ix = iy = 1;
+  while (ix<lx && iy<ly)
+  {
+    int s = cmp(data, gel(x,ix), gel(y,iy));
+    if (s < 0)
+    { gel(M,m) = gel(x,ix); ix++; }
+    else if (s == 0)
+    { gel(M,m) = gel(x,ix); iy++; ix++; }
+    else
+    { gel(M,m) = gel(y,iy); iy++; }
+    m++;
+  }
+  while (ix<lx) { gel(M,m) = gel(x,ix); ix++; m++; }
+  while (iy<ly) { gel(M,m) = gel(y,iy); iy++; m++; }
+  setlg(M, m); return M;
+}
+
+/* sort generic factorization, in place */
+GEN
+sort_factor(GEN y, void *data, int (*cmp)(void *,GEN,GEN))
+{
+  GEN a, b, A, B, w;
+  pari_sp av;
+  long n, i;
+
+  a = gel(y,1); n = lg(a); if (n == 1) return y;
+  b = gel(y,2); av = avma;
+  A = new_chunk(n);
+  B = new_chunk(n);
+  w = gen_sortspec(a, n-1, data, cmp);
+  for (i=1; i<n; i++) { long k = w[i]; A[i] = a[k]; B[i] = b[k]; }
+  for (i=1; i<n; i++) { a[i] = A[i]; b[i] = B[i]; }
+  avma = av; return y;
+}
+/* sort polynomial factorization, in place */
+GEN
+sort_factor_pol(GEN y,int (*cmp)(GEN,GEN))
+{
+  (void)sort_factor(y,(void*)cmp, &gen_cmp_RgX);
+  return y;
+}
+
+/* assume f and g coprime integer factorizations */
+GEN
+merge_factor_i(GEN f, GEN g)
+{
+  if (lg(f) == 1) return g;
+  if (lg(g) == 1) return f;
+  return sort_factor(famat_mul_shallow(f,g), (void*)&cmpii, &cmp_nodata);
+}
+
+/***********************************************************************/
+/*                                                                     */
+/*                          SET OPERATIONS                             */
+/*                                                                     */
+/***********************************************************************/
+GEN
+gtoset(GEN x)
+{
+  long lx;
+  if (!x) return cgetg(1, t_VEC);
+  switch(typ(x))
+  {
+    case t_VEC:
+    case t_COL: lx = lg(x); break;
+    case t_LIST: x = list_data(x); lx = x? lg(x): 1; break;
+    case t_VECSMALL: lx = lg(x); x = zv_to_ZV(x); break;
+    default: return mkveccopy(x);
+  }
+  if (lx==1) return cgetg(1,t_VEC);
+  x = gen_sort_uniq(x, (void*)&cmp_universal, cmp_nodata);
+  settyp(x, t_VEC); /* it may be t_COL */
+  return x;
+}
+
+long
+setisset(GEN x)
+{
+  long i, lx = lg(x);
+
+  if (typ(x) != t_VEC) return 0;
+  if (lx == 1) return 1;
+  for (i=1; i<lx-1; i++)
+    if (cmp_universal(gel(x,i+1), gel(x,i)) <= 0) return 0;
+  return 1;
+}
+
+long
+setsearch(GEN T, GEN y, long flag)
+{
+  long lx;
+  switch(typ(T))
+  {
+    case t_VEC: lx = lg(T); break;
+    case t_LIST: T = list_data(T); lx = T? lg(T): 1; break;
+    default: pari_err_TYPE("setsearch",T);
+      return 0; /*not reached*/
+  }
+  if (lx==1) return flag? 1: 0;
+  return gen_search(T,y,flag,(void*)cmp_universal,cmp_nodata);
+}
+
+GEN
+setunion(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long i, j, k, lx = lg(x), ly = lg(y);
+  GEN z = cgetg(lx + ly - 1, t_VEC);
+  if (typ(x) != t_VEC) pari_err_TYPE("setunion",x);
+  if (typ(y) != t_VEC) pari_err_TYPE("setunion",y);
+  i = j = k = 1;
+  while (i<lx && j<ly)
+  {
+    int s = cmp_universal(gel(x,i), gel(y,j));
+    if (s < 0)
+      z[k++] = x[i++];
+    else if (s > 0)
+      z[k++] = y[j++];
+    else {
+      z[k++] = x[i++];
+      j++;
+    }
+  }
+  while (i<lx) z[k++] = x[i++];
+  while (j<ly) z[k++] = y[j++];
+  setlg(z, k);
+  return gerepilecopy(av, z);
+}
+/* in case of equal keys in x,y, take the key from x */
+GEN
+ZV_union_shallow(GEN x, GEN y)
+{
+  long i, j, k, lx = lg(x), ly = lg(y);
+  GEN z = cgetg(lx + ly - 1, t_VEC);
+  i = j = k = 1;
+  while (i<lx && j<ly)
+  {
+    int s = cmpii(gel(x,i), gel(y,j));
+    if (s < 0)
+      z[k++] = x[i++];
+    else if (s > 0)
+      z[k++] = y[j++];
+    else {
+      z[k++] = x[i++];
+      j++;
+    }
+  }
+  while (i<lx) z[k++] = x[i++];
+  while (j<ly) z[k++] = y[j++];
+  setlg(z, k); return z;
+}
+
+
+GEN
+setintersect(GEN x, GEN y)
+{
+  long ix = 1, iy = 1, iz = 1, lx = lg(x), ly = lg(y);
+  pari_sp av = avma;
+  GEN z = cgetg(lx,t_VEC);
+  if (typ(x) != t_VEC) pari_err_TYPE("setintersect",x);
+  if (typ(y) != t_VEC) pari_err_TYPE("setintersect",y);
+  while (ix < lx && iy < ly)
+  {
+    int c = cmp_universal(gel(x,ix), gel(y,iy));
+    if      (c < 0) ix++;
+    else if (c > 0) iy++;
+    else { gel(z, iz++) = gel(x,ix); ix++; iy++; }
+  }
+  setlg(z,iz); return gerepilecopy(av,z);
+}
+
+GEN
+gen_setminus(GEN A, GEN B, int (*cmp)(GEN,GEN))
+{
+  pari_sp ltop = avma;
+  long i = 1, j = 1, k = 1, lx = lg(A), ly = lg(B);
+  GEN  diff = cgetg(lx,t_VEC);
+  while (i < lx && j < ly)
+    switch ( cmp(gel(A,i),gel(B,j)) )
+    {
+      case -1: gel(diff,k++) = gel(A,i++); break;
+      case 1: j++; break;
+      case 0: i++; break;
+    }
+  while (i < lx) gel(diff,k++) = gel(A,i++);
+  setlg(diff,k);
+  return gerepilecopy(ltop,diff);
+}
+
+GEN
+setminus(GEN x, GEN y)
+{
+  if (typ(x) != t_VEC) pari_err_TYPE("setminus",x);
+  if (typ(y) != t_VEC) pari_err_TYPE("setminus",y);
+  return gen_setminus(x,y,cmp_universal);
+}
+
+GEN
+setbinop(GEN f, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long i, j, lx, ly, k = 1;
+  GEN z;
+  if (typ(f) != t_CLOSURE || closure_arity(f) != 2)
+    pari_err_TYPE("setbinop [function needs exactly 2 arguments]",f);
+  lx = lg(x);
+  if (typ(x) != t_VEC) pari_err_TYPE("setbinop", x);
+  if (y == NULL) { /* assume x = y and f symmetric */
+    z = cgetg((((lx-1)*lx) >> 1) + 1, t_VEC);
+    for (i = 1; i < lx; i++)
+      for (j = i; j < lx; j++)
+        gel(z, k++) = closure_callgen2(f, gel(x,i),gel(x,j));
+  } else {
+    ly = lg(y);
+    if (typ(y) != t_VEC) pari_err_TYPE("setbinop", y);
+    z = cgetg((lx-1)*(ly-1) + 1, t_VEC);
+    for (i = 1; i < lx; i++)
+      for (j = 1; j < ly; j++)
+        gel(z, k++) = closure_callgen2(f, gel(x,i),gel(y,j));
+  }
+  return gerepileupto(av, gtoset(z));
+}
diff --git a/src/basemath/bit.c b/src/basemath/bit.c
new file mode 100644
index 0000000..0d7dd86
--- /dev/null
+++ b/src/basemath/bit.c
@@ -0,0 +1,496 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+/*********************************************************************/
+/**                                                                 **/
+/**                       BINARY DECOMPOSITION                      **/
+/**                                                                 **/
+/*********************************************************************/
+
+INLINE GEN
+inegate(GEN z) { return subsi(-1,z); }
+
+GEN
+binaire(GEN x)
+{
+  ulong m,u;
+  long i,lx,ex,ly,tx=typ(x);
+  GEN y,p1,p2;
+
+  switch(tx)
+  {
+    case t_INT:
+    if (!signe(x))
+      return cgetg(1,t_VEC);
+    else
+    {
+      GEN xp=int_MSW(x);
+      lx=lgefint(x);
+      y = cgetg(2 + expi(x), t_VEC); ly=1;
+      m=HIGHBIT>>bfffo(*xp); u=*xp;
+      do { gel(y,ly) = m & u ? gen_1 : gen_0; ly++; } while (m>>=1);
+      for (i=3; i<lx; i++)
+      {
+        m=HIGHBIT; xp=int_precW(xp); u=*xp;
+        do { gel(y,ly) = m & u ? gen_1 : gen_0; ly++; } while (m>>=1);
+      }
+      break;
+    }
+    case t_REAL:
+      ex=expo(x);
+      if (!signe(x)) return const_vec(maxss(-ex,0), gen_0);
+
+      lx=lg(x); y=cgetg(3,t_VEC);
+      if (ex > bit_prec(x)) pari_err_PREC("binary");
+      p1 = cgetg(maxss(ex,0)+2,t_VEC);
+      p2 = cgetg(bit_prec(x)-ex,t_VEC);
+      gel(y,1) = p1;
+      gel(y,2) = p2;
+      ly = -ex; ex++; m = HIGHBIT;
+      if (ex<=0)
+      {
+        gel(p1,1) = gen_0; for (i=1; i <= -ex; i++) gel(p2,i) = gen_0;
+        i=2;
+      }
+      else
+      {
+        ly=1;
+        for (i=2; i<lx && ly<=ex; i++)
+        {
+          m=HIGHBIT; u=x[i];
+          do
+            { gel(p1,ly) = (m & u) ? gen_1 : gen_0; ly++; }
+          while ((m>>=1) && ly<=ex);
+        }
+        ly=1;
+        if (m) i--; else m=HIGHBIT;
+      }
+      for (; i<lx; i++)
+      {
+        u=x[i];
+        do { gel(p2,ly) = m & u ? gen_1 : gen_0; ly++; } while (m>>=1);
+        m=HIGHBIT;
+      }
+      break;
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = binaire(gel(x,i));
+      break;
+    default: pari_err_TYPE("binary",x);
+      return NULL; /* not reached */
+  }
+  return y;
+}
+
+/* return 1 if bit n of x is set, 0 otherwise */
+long
+bittest(GEN x, long n)
+{
+  if (typ(x) != t_INT) pari_err_TYPE("bittest",x);
+  if (!signe(x) || n < 0) return 0;
+  if (signe(x) < 0)
+  {
+    pari_sp ltop=avma;
+    long b = !int_bit(inegate(x),n);
+    avma=ltop;
+    return b;
+  }
+  return int_bit(x, n);
+}
+
+GEN
+gbittest(GEN x, long n) { return map_proto_lGL(bittest,x,n); }
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          BITMAP OPS                               **/
+/** x & y (and), x | y (or), x ^ y (xor), ~x (neg), x & ~y (negimply) **/
+/**                                                                   **/
+/***********************************************************************/
+/* Truncate a non-negative integer to a number of bits.  */
+static GEN
+ibittrunc(GEN x, long bits)
+{
+  long lowbits, known_zero_words, xl = lgefint(x) - 2;
+  long len_out = nbits2nlong(bits);
+
+  if (xl < len_out)
+    return x;
+      /* Check whether mask is trivial */
+  lowbits = bits & (BITS_IN_LONG-1);
+  if (!lowbits) {
+    if (xl == len_out)
+      return x;
+  } else if (len_out <= xl) {
+    GEN xi = int_W(x, len_out-1);
+    /* Non-trival mask is given by a formula, if x is not
+       normalized, this works even in the exceptional case */
+    *xi &= (1L << lowbits) - 1;
+    if (*xi && xl == len_out) return x;
+  }
+  /* Normalize */
+  known_zero_words = xl - len_out;
+  if (known_zero_words < 0) known_zero_words = 0;
+  return int_normalize(x, known_zero_words);
+}
+
+GEN
+gbitneg(GEN x, long bits)
+{
+  const ulong uzero = 0;
+  long lowbits, xl, len_out, i;
+
+  if (typ(x) != t_INT) pari_err_TYPE("bitwise negation",x);
+  if (bits < -1)
+    pari_err_DOMAIN("bitwise negation","exponent","<",gen_m1,stoi(bits));
+  if (bits == -1) return inegate(x);
+  if (bits == 0) return gen_0;
+  if (signe(x) < 0) { /* Consider as if mod big power of 2 */
+    pari_sp ltop = avma;
+    return gerepileuptoint(ltop, ibittrunc(inegate(x), bits));
+  }
+  xl = lgefint(x);
+  len_out = nbits2lg(bits);
+  lowbits = bits & (BITS_IN_LONG-1);
+  if (len_out > xl) /* Need to grow */
+  {
+    GEN out, outp, xp = int_MSW(x);
+    out = cgetipos(len_out);
+    outp = int_MSW(out);
+    if (!lowbits)
+      *outp = ~uzero;
+    else
+      *outp = (1L << lowbits) - 1;
+    for (i = 3; i < len_out - xl + 2; i++)
+    {
+      outp = int_precW(outp); *outp = ~uzero;
+    }
+    for (     ; i < len_out; i++)
+    {
+      outp = int_precW(outp); *outp = ~*xp;
+      xp   = int_precW(xp);
+    }
+    return out;
+  }
+  x = icopy(x);
+  for (i = 2; i < xl; i++) x[i] = ~x[i];
+  return ibittrunc(int_normalize(x,0), bits);
+}
+
+/* bitwise 'and' of two positive integers (any integers, but we ignore sign).
+ * Inputs are not necessary normalized. */
+GEN
+ibitand(GEN x, GEN y)
+{
+  long lx, ly, lout;
+  long *xp, *yp, *outp;
+  GEN out;
+  long i;
+
+  if (!signe(x) || !signe(y)) return gen_0;
+  lx=lgefint(x); ly=lgefint(y);
+  lout = minss(lx,ly); /* > 2 */
+  xp = int_LSW(x);
+  yp = int_LSW(y);
+  out = cgetipos(lout);
+  outp = int_LSW(out);
+  for (i=2; i<lout; i++)
+  {
+    *outp = (*xp) & (*yp);
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+    yp    = int_nextW(yp);
+  }
+  if ( !*int_MSW(out) ) out = int_normalize(out, 1);
+  return out;
+}
+
+/* bitwise 'or' of absolute values of two integers */
+GEN
+ibitor(GEN x, GEN y)
+{
+  long lx, ly;
+  long *xp, *yp, *outp;
+  GEN  out;
+  long i;
+  if (!signe(x)) return absi(y);
+  if (!signe(y)) return absi(x);
+
+  lx = lgefint(x); xp = int_LSW(x);
+  ly = lgefint(y); yp = int_LSW(y);
+  if (lx < ly) swapspec(xp,yp,lx,ly);
+  /* lx > 2 */
+  out = cgetipos(lx);
+  outp = int_LSW(out);
+  for (i=2;i<ly;i++)
+  {
+    *outp = (*xp) | (*yp);
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+    yp    = int_nextW(yp);
+  }
+  for (   ;i<lx;i++)
+  {
+    *outp = *xp;
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+  }
+  /* If input is normalized, this is not needed */
+  if ( !*int_MSW(out) ) out = int_normalize(out, 1);
+  return out;
+}
+
+/* bitwise 'xor' of absolute values of two integers */
+GEN
+ibitxor(GEN x, GEN y)
+{
+  long lx, ly;
+  long *xp, *yp, *outp;
+  GEN  out;
+  long i;
+  if (!signe(x)) return absi(y);
+  if (!signe(y)) return absi(x);
+
+  lx = lgefint(x); xp = int_LSW(x);
+  ly = lgefint(y); yp = int_LSW(y);
+  if (lx < ly) swapspec(xp,yp,lx,ly);
+  /* lx > 2 */
+  out = cgetipos(lx);
+  outp = int_LSW(out);
+  for (i=2;i<ly;i++)
+  {
+    *outp = (*xp) ^ (*yp);
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+    yp    = int_nextW(yp);
+  }
+  for (   ;i<lx;i++)
+  {
+    *outp = *xp;
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+  }
+  if ( !*int_MSW(out) ) out = int_normalize(out, 1);
+  return out;
+}
+
+/* bitwise 'negimply' of absolute values of two integers */
+/* "negimply(x,y)" is ~(x => y) == ~(~x | y) == x & ~y   */
+GEN
+ibitnegimply(GEN x, GEN y)
+{
+  long lx, ly, lin;
+  long *xp, *yp, *outp;
+  GEN out;
+  long i;
+  if (!signe(x)) return gen_0;
+  if (!signe(y)) return absi(x);
+
+  lx = lgefint(x); xp = int_LSW(x);
+  ly = lgefint(y); yp = int_LSW(y);
+  lin = minss(lx,ly);
+  out = cgetipos(lx);
+  outp = int_LSW(out);
+  for (i=2; i<lin; i++)
+  {
+    *outp = (*xp) & ~(*yp);
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+    yp    = int_nextW(yp);
+  }
+  for (   ;i<lx;i++)
+  {
+    *outp = *xp;
+    outp  = int_nextW(outp);
+    xp    = int_nextW(xp);
+  }
+  if ( !*int_MSW(out) ) out = int_normalize(out, 1);
+  return out;
+}
+
+static int
+signs(GEN x, GEN y) { return (((signe(x) >= 0) << 1) | (signe(y) >= 0)); }
+static void
+checkint2(const char *f,GEN x, GEN y)
+{ if (typ(x)!=t_INT || typ(y)!=t_INT) pari_err_TYPE2(f,x,y); }
+
+GEN
+gbitor(GEN x, GEN y)
+{
+  pari_sp ltop = avma;
+  GEN z;
+
+  checkint2("bitwise or",x,y);
+  switch (signs(x, y))
+  {
+    case 3: /*1,1*/
+      return ibitor(x,y);
+    case 2: /*1,-1*/
+      z = ibitnegimply(inegate(y),x);
+      break;
+    case 1: /*-1,1*/
+      z = ibitnegimply(inegate(x),y);
+      break;
+    default: /*-1,-1*/
+      z = ibitand(inegate(x),inegate(y));
+      break;
+  }
+  return gerepileuptoint(ltop, inegate(z));
+}
+
+GEN
+gbitand(GEN x, GEN y)
+{
+  pari_sp ltop = avma;
+  GEN z;
+
+  checkint2("bitwise and",x,y);
+  switch (signs(x, y))
+  {
+    case 3: /*1,1*/
+      return ibitand(x,y);
+    case 2: /*1,-1*/
+      z = ibitnegimply(x,inegate(y));
+      break;
+    case 1: /*-1,1*/
+      z = ibitnegimply(y,inegate(x));
+      break;
+    default: /*-1,-1*/
+      z = inegate(ibitor(inegate(x),inegate(y)));
+      break;
+  }
+  return gerepileuptoint(ltop, z);
+}
+
+GEN
+gbitxor(GEN x, GEN y)
+{
+  pari_sp ltop = avma;
+  GEN z;
+
+  checkint2("bitwise xor",x,y);
+  switch (signs(x, y))
+  {
+    case 3: /*1,1*/
+      return ibitxor(x,y);
+    case 2: /*1,-1*/
+      z = inegate(ibitxor(x,inegate(y)));
+      break;
+    case 1: /*-1,1*/
+      z = inegate(ibitxor(inegate(x),y));
+      break;
+    default: /*-1,-1*/
+      z = ibitxor(inegate(x),inegate(y));
+      break;
+  }
+  return gerepileuptoint(ltop,z);
+}
+
+/* x & ~y */
+GEN
+gbitnegimply(GEN x, GEN y)
+{
+  pari_sp ltop = avma;
+  GEN z;
+
+  checkint2("bitwise negated imply",x,y);
+  switch (signs(x, y))
+  {
+    case 3: /*1,1*/
+      return ibitnegimply(x,y);
+    case 2: /*1,-1*/
+      z = ibitand(x,inegate(y));
+      break;
+    case 1: /*-1,1*/
+      z = inegate(ibitor(y,inegate(x)));
+      break;
+    default: /*-1,-1*/
+      z = ibitnegimply(inegate(y),inegate(x));
+      break;
+  }
+  return gerepileuptoint(ltop,z);
+}
+
+INLINE long
+hamming_word(ulong w)
+{
+#if 0
+  return __builtin_popcountl(w);
+#endif
+  static long byte_weight[] = {
+    0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+    2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+    1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+    2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+    2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+    3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
+  };
+  long sum = 0;
+  while (w) { sum += byte_weight[w & 255]; w >>= 8; }
+  return sum;
+}
+
+/* number of non-zero entries among x[a], ..., x[b] */
+static long
+hamming_slice(GEN x, long a, long b)
+{
+  long i, nb = 0;
+  for (i = a; i <= b; i++)
+    if (!gequal0(gel(x,i))) nb++;
+  return nb;
+}
+static long
+hamming_mat(GEN x)
+{
+  long i, lx = lg(x), nb = 0;
+  for (i = 1; i < lx; i++) nb += hammingweight(gel(x,i));
+  return nb;
+}
+static long
+hamming_vecsmall(GEN x)
+{
+  long i, lx = lg(x), nb = 0;
+  for (i = 1; i < lx; i++)
+    if (x[i]) nb++;
+  return nb;
+}
+static long
+hamming_int(GEN n)
+{
+  long lx = lgefint(n), i, sum;
+  if (lx == 2) return 0;
+  sum = hamming_word(n[2]);
+  for (i = 3; i < lx; i++) sum += hamming_word(n[i]);
+  return sum;
+}
+
+long
+hammingweight(GEN n)
+{
+  switch(typ(n))
+  {
+    case t_INT: return hamming_int(n);
+    case t_VEC:
+    case t_COL: return hamming_slice(n, 1, lg(n)-1);
+    case t_POL: return hamming_slice(n, 2, lg(n)-1);
+    case t_VECSMALL: return hamming_vecsmall(n);
+    case t_MAT: return hamming_mat(n);
+  }
+  pari_err_TYPE("hammingweight", n);
+  return 0;/*notreached*/
+}
diff --git a/src/basemath/buch1.c b/src/basemath/buch1.c
new file mode 100644
index 0000000..ff7c31e
--- /dev/null
+++ b/src/basemath/buch1.c
@@ -0,0 +1,1180 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/*******************************************************************/
+/*                                                                 */
+/*         CLASS GROUP AND REGULATOR (McCURLEY, BUCHMANN)          */
+/*                   QUADRATIC FIELDS                              */
+/*                                                                 */
+/*******************************************************************/
+/* For largeprime() hashtable. Note that hashed pseudoprimes are odd (unless
+ * 2 | index), hence the low order bit is not useful. So we hash
+ * HASHBITS bits starting at bit 1, not bit 0 */
+#define HASHBITS 10
+static const long HASHT = 1L << HASHBITS;
+
+static long
+hash(long q) { return (q & ((1L << (HASHBITS+1)) - 1)) >> 1; }
+#undef HASHBITS
+
+/* See buch2.c:
+ * B->subFB contains split p such that \prod p > sqrt(B->Disc)
+ * B->powsubFB contains powers of forms in B->subFB */
+#define RANDOM_BITS 4
+static const long CBUCH = (1L<<RANDOM_BITS)-1;
+
+struct buch_quad
+{
+  ulong limhash;
+  long KC, KC2, PRECREG;
+  long *primfact, *exprimfact, **hashtab;
+  GEN FB, numFB;
+  GEN powsubFB, vperm, subFB, badprim;
+  struct qfr_data *QFR;
+};
+
+/*******************************************************************/
+/*                                                                 */
+/*  Routines related to binary quadratic forms (for internal use)  */
+/*                                                                 */
+/*******************************************************************/
+/* output canonical representative wrt projection Cl^+ --> Cl (a > 0) */
+static GEN
+qfr3_canon(GEN x, struct qfr_data *S)
+{
+  GEN a = gel(x,1), c = gel(x,3);
+  if (signe(a) < 0) {
+    if (absi_equal(a,c)) return qfr3_rho(x, S);
+    setsigne(a, 1);
+    setsigne(c,-1);
+  }
+  return x;
+}
+static GEN
+qfr3_canon_safe(GEN x, struct qfr_data *S)
+{
+  GEN a = gel(x,1), c = gel(x,3);
+  if (signe(a) < 0) {
+    if (absi_equal(a,c)) return qfr3_rho(x, S);
+    gel(x,1) = negi(a);
+    gel(x,3) = negi(c);
+  }
+  return x;
+}
+static GEN
+qfr5_canon(GEN x, struct qfr_data *S)
+{
+  GEN a = gel(x,1), c = gel(x,3);
+  if (signe(a) < 0) {
+    if (absi_equal(a,c)) return qfr5_rho(x, S);
+    setsigne(a, 1);
+    setsigne(c,-1);
+  }
+  return x;
+}
+static GEN
+QFR5_comp(GEN x,GEN y, struct qfr_data *S)
+{ return qfr5_canon(qfr5_comp(x,y,S), S); }
+static GEN
+QFR3_comp(GEN x, GEN y, struct qfr_data *S)
+{ return qfr3_canon(qfr3_comp(x,y,S), S); }
+
+/* compute rho^n(x) */
+static GEN
+qfr5_rho_pow(GEN x, long n, struct qfr_data *S)
+{
+  long i;
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  for (i=1; i<=n; i++)
+  {
+    x = qfr5_rho(x,S);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"qfr5_rho_pow");
+      x = gerepilecopy(av, x);
+    }
+  }
+  return gerepilecopy(av, x);
+}
+
+static GEN
+qfr5_pf(struct qfr_data *S, long p, long prec)
+{
+  GEN y = primeform_u(S->D,p);
+  return qfr5_canon(qfr5_red(qfr_to_qfr5(y,prec), S), S);
+}
+
+static GEN
+qfr3_pf(struct qfr_data *S, long p)
+{
+  GEN y = primeform_u(S->D,p);
+  return qfr3_canon(qfr3_red(y, S), S);
+}
+
+#define qfi_pf primeform_u
+
+/* Warning: ex[0] not set in general */
+static GEN
+init_form(struct buch_quad *B, GEN ex,
+          GEN (*comp)(GEN,GEN,struct qfr_data *S))
+{
+  long i, l = lg(B->powsubFB);
+  GEN F = NULL;
+  for (i=1; i<l; i++)
+    if (ex[i])
+    {
+      GEN t = gmael(B->powsubFB,i,ex[i]);
+      F = F? comp(F, t, B->QFR): t;
+    }
+  return F;
+}
+static GEN
+qfr5_factorback(struct buch_quad *B, GEN ex) { return init_form(B, ex, &QFR5_comp); }
+
+static GEN
+QFI_comp(GEN x, GEN y, struct qfr_data *S) { (void)S; return qficomp(x,y); }
+
+static GEN
+qfi_factorback(struct buch_quad *B, GEN ex) { return init_form(B, ex, &QFI_comp); }
+
+static GEN
+random_form(struct buch_quad *B, GEN ex,
+            GEN (*comp)(GEN,GEN, struct qfr_data *S))
+{
+  long i, l = lg(ex);
+  pari_sp av = avma;
+  GEN F;
+  for(;;)
+  {
+    for (i=1; i<l; i++) ex[i] = random_bits(RANDOM_BITS);
+    if ((F = init_form(B, ex, comp))) return F;
+    avma = av;
+  }
+}
+static GEN
+qfr3_random(struct buch_quad *B,GEN ex){ return random_form(B, ex, &QFR3_comp); }
+static GEN
+qfi_random(struct buch_quad *B,GEN ex) { return random_form(B, ex, &QFI_comp); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                     Common subroutines                          */
+/*                                                                 */
+/*******************************************************************/
+long
+check_LIMC(long LIMC, long LIMCMAX)
+{
+  if (LIMC >= LIMCMAX) pari_err_BUG("Buchmann's algorithm");
+  if (LIMC <= LIMCMAX/40) /* cbach <= 0.3 */
+    LIMC *= 2;
+  else if (LIMCMAX < 60) /* \Delta_K <= 9 */
+    LIMC++;
+  else
+    LIMC += LIMCMAX / 60; /* cbach += 0.2 */
+  if (LIMC > LIMCMAX) LIMC = LIMCMAX;
+  return LIMC;
+}
+
+/* Is |q| <= p ? */
+static int
+isless_iu(GEN q, ulong p) {
+  long l = lgefint(q);
+  return l==2 || (l == 3 && (ulong)q[2] <= p);
+}
+
+static long
+factorquad(struct buch_quad *B, GEN f, long nFB, ulong limp)
+{
+  ulong X;
+  long i, lo = 0;
+  GEN x = gel(f,1), FB = B->FB, P = B->primfact, E = B->exprimfact;
+
+  for (i=1; lgefint(x) > 3; i++)
+  {
+    ulong p = (ulong)FB[i], r;
+    GEN q = diviu_rem(x, p, &r);
+    if (!r)
+    {
+      long k = 0;
+      do { k++; x = q; q = diviu_rem(x, p, &r); } while (!r);
+      lo++; P[lo] = p; E[lo] = k;
+    }
+    if (isless_iu(q,p)) {
+      if (lgefint(x) == 3) { X = (ulong)x[2]; goto END; }
+      return 0;
+    }
+    if (i == nFB) return 0;
+  }
+  X = (ulong)x[2];
+  if (X == 1) { P[0] = 0; return 1; }
+  for (;; i++)
+  { /* single precision affair, split for efficiency */
+    ulong p = (ulong)FB[i];
+    ulong q = X / p, r = X % p; /* gcc makes a single div */
+    if (!r)
+    {
+      long k = 0;
+      do { k++; X = q; q = X / p; r = X % p; } while (!r);
+      lo++; P[lo] = p; E[lo] = k;
+    }
+    if (q <= p) break;
+    if (i == nFB) return 0;
+  }
+END:
+  if (X > B->limhash) return 0;
+  if (X != 1 && X <= limp)
+  {
+    if (B->badprim && ugcd(X, umodiu(B->badprim,X)) > 1) return 0;
+    lo++; P[lo] = X; E[lo] = 1;
+    X = 1;
+  }
+  P[0] = lo; return X;
+}
+
+/* Check for a "large prime relation" involving q; q may not be prime */
+static long *
+largeprime(struct buch_quad *B, long q, GEN ex, long np, long nrho)
+{
+  const long hashv = hash(q);
+  long *pt, i, l = lg(B->subFB);
+
+  for (pt = B->hashtab[hashv]; ; pt = (long*) pt[0])
+  {
+    if (!pt)
+    {
+      pt = (long*) pari_malloc((l+3) * sizeof(long));
+      *pt++ = nrho; /* nrho = pt[-3] */
+      *pt++ = np;   /* np   = pt[-2] */
+      *pt++ = q;    /* q    = pt[-1] */
+      pt[0] = (long)B->hashtab[hashv];
+      for (i=1; i<l; i++) pt[i]=ex[i];
+      B->hashtab[hashv]=pt; return NULL;
+    }
+    if (pt[-1] == q) break;
+  }
+  for(i=1; i<l; i++)
+    if (pt[i] != ex[i]) return pt;
+  return (pt[-2]==np)? NULL: pt;
+}
+
+static void
+clearhash(long **hash)
+{
+  long *pt;
+  long i;
+  for (i=0; i<HASHT; i++) {
+    for (pt = hash[i]; pt; ) {
+      void *z = (void*)(pt - 3);
+      pt = (long*) pt[0]; pari_free(z);
+    }
+    hash[i] = NULL;
+  }
+}
+
+/* last prime stored */
+ulong
+GRH_last_prime(GRHcheck_t *S) { return (S->primes + S->nprimes-1)->p; }
+/* ensure that S->primes can hold at least nb primes */
+void
+GRH_ensure(GRHcheck_t *S, long nb)
+{
+  if (S->maxprimes <= nb)
+  {
+    do S->maxprimes *= 2; while (S->maxprimes <= nb);
+    S->primes = (GRHprime_t*)pari_realloc((void*)S->primes,
+                                          S->maxprimes*sizeof(*S->primes));
+  }
+}
+/* cache data for all primes up to the LIM */
+static void
+cache_prime_quad(GRHcheck_t *S, ulong LIM, GEN D)
+{
+  GRHprime_t *pr;
+  double nb;
+
+  if (S->limp >= LIM) return;
+  nb = primepi_upper_bound((double)LIM); /* #{p <= LIM} <= nb */
+  GRH_ensure(S, nb+1); /* room for one extra prime */
+  for (pr = S->primes + S->nprimes;;)
+  {
+    ulong p = u_forprime_next(&(S->P));
+    pr->p = p;
+    pr->logp = log((double)p);
+    pr->dec = (GEN)kroiu(D,p);
+    S->nprimes++;
+    pr++;
+    /* store up to nextprime(LIM) included */
+    if (p >= LIM) { S->limp = p; break; }
+  }
+}
+
+static GEN
+compute_invresquad(GRHcheck_t *S)
+{
+  pari_sp av = avma;
+  GEN invres = real_1(DEFAULTPREC);
+  GRHprime_t *pr = S->primes;
+  long i = S->nprimes, LIMC = GRH_last_prime(S)+diffptr[i]-1; /* nextprime(p+1)-1*/
+  double limp = log(LIMC) / 2;
+  for (; i > 0; pr++, i--)
+  {
+    long s = (long)pr->dec;
+    if (s)
+    {
+      ulong p = pr->p;
+      if (s>0 || pr->logp <= limp)
+        /* Both p and P contribute */
+        invres = mulur(p - s, divru(invres, p));
+      else if (s<0)
+        /* Only p contributes */
+        invres = mulur(p, divru(invres, p - 1));
+    }
+  }
+  return gerepileuptoleaf(av, invres);
+}
+
+/* p | conductor of order of disc D ? */
+static int
+is_bad(GEN D, ulong p)
+{
+  pari_sp av = avma;
+  int r;
+  if (p == 2)
+  {
+    r = mod16(D) >> 1;
+    if (r && signe(D) < 0) r = 8-r;
+    return (r < 4);
+  }
+  r = (remii(D, sqru(p)) == gen_0); /* p^2 | D ? */
+  avma = av; return r;
+}
+
+/* returns the n-th suitable ideal for the factorbase */
+static long
+nthidealquad(GEN D, long n)
+{
+  pari_sp av = avma;
+  forprime_t S;
+  ulong p;
+  (void)u_forprime_init(&S, 2, ULONG_MAX);
+  while (n > 0)
+  {
+    p = u_forprime_next(&S);
+    if (!is_bad(D, p) && kroiu(D, p) >= 0) n--;
+  }
+  avma = av; return p;
+}
+
+static int
+quadGRHchk(GEN D, GRHcheck_t *S, ulong LIMC)
+{
+  double logC = log((double)LIMC), SA = 0, SB = 0;
+  long i;
+
+  cache_prime_quad(S, LIMC, D);
+  for (i = 0;; i++)
+  {
+    GRHprime_t *pr = S->primes+i;
+    ulong p = pr->p;
+    long M;
+    double logNP, q, A, B;
+    if (p > LIMC) break;
+    if ((long)pr->dec < 0)
+    {
+      logNP = 2 * pr->logp;
+      q = 1/(double)p;
+    }
+    else
+    {
+      logNP = pr->logp;
+      q = 1/sqrt((double)p);
+    }
+    A = logNP * q; B = logNP * A; M = (long)(logC/logNP);
+    if (M > 1)
+    {
+      double inv1_q = 1 / (1-q);
+      A *= (1 - pow(q, M)) * inv1_q;
+      B *= (1 - pow(q, M)*(M+1 - M*q)) * inv1_q * inv1_q;
+    }
+    if ((long)pr->dec>0) { SA += 2*A;SB += 2*B; } else { SA += A; SB += B; }
+  }
+  return GRHok(S, logC, SA, SB);
+}
+
+/* create B->FB, B->numFB; set B->badprim. Return L(kro_D, 1) */
+static void
+FBquad(struct buch_quad *B, ulong C2, ulong C1, GRHcheck_t *S)
+{
+  GEN D = B->QFR->D;
+  long i;
+  pari_sp av;
+  GRHprime_t *pr;
+
+  cache_prime_quad(S, C2, D);
+  pr = S->primes;
+  B->numFB = cgetg(C2+1, t_VECSMALL);
+  B->FB    = cgetg(C2+1, t_VECSMALL);
+  av = avma;
+  B->KC = 0; i = 0;
+  B->badprim = gen_1;
+  for (;; pr++) /* p <= C2 */
+  {
+    ulong p = pr->p;
+    if (!B->KC && p > C1) B->KC = i;
+    if (p > C2) break;
+    switch ((long)pr->dec)
+    {
+      case -1: break; /* inert */
+      case  0: /* ramified */
+        if (is_bad(D, p)) { B->badprim = muliu(B->badprim, p); break; }
+        /* fall through */
+      default:  /* split */
+        i++; B->numFB[p] = i; B->FB[i] = p; break;
+    }
+  }
+  B->KC2 = i;
+  setlg(B->FB, B->KC2+1);
+  if (B->badprim != gen_1)
+    B->badprim = gerepileuptoint(av, B->badprim);
+  else
+  {
+    B->badprim = NULL; avma = av;
+  }
+}
+
+/* create B->vperm, return B->subFB */
+static GEN
+subFBquad(struct buch_quad *B, GEN D, double PROD, long minSFB)
+{
+  long i, j, lgsub = 1, ino = 1, lv = B->KC+1;
+  double prod = 1.;
+  pari_sp av;
+  GEN no;
+
+  B->vperm = cgetg(lv, t_VECSMALL);
+  av = avma;
+  no    = cgetg(lv, t_VECSMALL);
+  for (j = 1; j < lv; j++)
+  {
+    ulong p = B->FB[j];
+    if (!umodiu(D, p)) no[ino++] = j; /* ramified */
+    else
+    {
+      B->vperm[lgsub++] = j;
+      prod *= p;
+      if (lgsub > minSFB && prod > PROD) break;
+    }
+  }
+  /* lgsub >= 1 otherwise quadGRHchk is false */
+  i = lgsub;
+  for (j = 1; j < ino;i++,j++) B->vperm[i] = no[j];
+  for (     ; i < lv; i++)     B->vperm[i] = i;
+  no = gclone(vecslice(B->vperm, 1, lgsub-1));
+  avma = av; return no;
+}
+
+/* assume n >= 1, x[i][j] = B->subFB[i]^j, for j = 1..n */
+static GEN
+powsubFBquad(struct buch_quad *B, long n)
+{
+  pari_sp av = avma;
+  long i,j, l = lg(B->subFB);
+  GEN F, y, x = cgetg(l, t_VEC), D = B->QFR->D;
+
+  if (B->PRECREG) /* real */
+  {
+    for (i=1; i<l; i++)
+    {
+      F = qfr5_pf(B->QFR, B->FB[B->subFB[i]], B->PRECREG);
+      y = cgetg(n+1, t_VEC); gel(x,i) = y;
+      gel(y,1) = F;
+      for (j=2; j<=n; j++) gel(y,j) = QFR5_comp(gel(y,j-1), F, B->QFR);
+    }
+  }
+  else /* imaginary */
+  {
+    for (i=1; i<l; i++)
+    {
+      F = qfi_pf(D, B->FB[B->subFB[i]]);
+      y = cgetg(n+1, t_VEC); gel(x,i) = y;
+      gel(y,1) = F;
+      for (j=2; j<=n; j++) gel(y,j) = qficomp(gel(y,j-1), F);
+    }
+  }
+  x = gclone(x); avma = av; return x;
+}
+
+static void
+sub_fact(struct buch_quad *B, GEN col, GEN F)
+{
+  GEN b = gel(F,2);
+  long i;
+  for (i=1; i<=B->primfact[0]; i++)
+  {
+    ulong p = B->primfact[i], k = B->numFB[p];
+    long e = B->exprimfact[i];
+    if (umodiu(b, p<<1) > p) e = -e;
+    col[k] -= e;
+  }
+}
+static void
+add_fact(struct buch_quad *B, GEN col, GEN F)
+{
+  GEN b = gel(F,2);
+  long i;
+  for (i=1; i<=B->primfact[0]; i++)
+  {
+    ulong p = B->primfact[i], k = B->numFB[p];
+    long e = B->exprimfact[i];
+    if (umodiu(b, p<<1) > p) e = -e;
+    col[k] += e;
+  }
+}
+
+static GEN
+get_clgp(struct buch_quad *B, GEN W, GEN *ptD, long prec)
+{
+  GEN res, init, u1, D = ZM_snf_group(W,NULL,&u1), Z = prec? real_0(prec): NULL;
+  long i, j, l = lg(W), c = lg(D);
+
+  res=cgetg(c,t_VEC); init = cgetg(l,t_VEC);
+  for (i=1; i<l; i++) gel(init,i) = primeform_u(B->QFR->D, B->FB[B->vperm[i]]);
+  for (j=1; j<c; j++)
+  {
+    GEN g = NULL;
+    if (prec)
+    {
+      for (i=1; i<l; i++)
+      {
+        GEN t, u = gcoeff(u1,i,j);
+        if (!signe(u)) continue;
+        t = qfr3_pow(gel(init,i), u, B->QFR);
+        g = g? qfr3_comp(g, t, B->QFR): t;
+      }
+      g = qfr3_to_qfr(qfr3_canon_safe(qfr3_red(g, B->QFR), B->QFR), Z);
+    }
+    else
+    {
+      for (i=1; i<l; i++)
+      {
+        GEN t, u = gcoeff(u1,i,j);
+        if (!signe(u)) continue;
+        t = powgi(gel(init,i), u);
+        g = g? qficomp(g, t): t;
+      }
+    }
+    gel(res,j) = g;
+  }
+  *ptD = D; return res;
+}
+
+static long
+trivial_relations(struct buch_quad *B, GEN mat, GEN C)
+{
+  long i, j = 0;
+  GEN col, D = B->QFR->D;
+  for (i = 1; i <= B->KC; i++)
+  { /* ramified prime ==> trivial relation */
+    if (umodiu(D, B->FB[i])) continue;
+    col = zero_zv(B->KC);
+    col[i] = 2; j++;
+    gel(mat,j) = col;
+    gel(C,j) = gen_0;
+  }
+  return j;
+}
+
+static void
+dbg_all(pari_timer *T, const char *phase, long s, long n)
+{
+  err_printf("\n");
+  timer_printf(T, "%s rel [#rel/#test = %ld/%ld]", phase,s,n);
+}
+
+/* Imaginary Quadratic fields */
+
+static void
+imag_relations(struct buch_quad *B, long need, long *pc, ulong LIMC, GEN mat)
+{
+  pari_timer T;
+  long lgsub = lg(B->subFB), current = *pc, nbtest = 0, s = 0;
+  long i, fpc;
+  pari_sp av;
+  GEN col, form, ex = cgetg(lgsub, t_VECSMALL);
+
+  if (!current) current = 1;
+  if (DEBUGLEVEL) timer_start(&T);
+  av = avma;
+  for(;;)
+  {
+    if (s >= need) break;
+    avma = av;
+    form = qfi_random(B,ex);
+    form = qficomp(form, qfi_pf(B->QFR->D, B->FB[current]));
+    nbtest++; fpc = factorquad(B,form,B->KC,LIMC);
+    if (!fpc)
+    {
+      if (DEBUGLEVEL>1) err_printf(".");
+      if ((nbtest & 0xff) == 0 && ++current > B->KC) current = 1;
+      continue;
+    }
+    if (fpc > 1)
+    {
+      long *fpd = largeprime(B,fpc,ex,current,0);
+      ulong b1, b2, p;
+      GEN form2;
+      if (!fpd)
+      {
+        if (DEBUGLEVEL>1) err_printf(".");
+        continue;
+      }
+      form2 = qficomp(qfi_factorback(B,fpd), qfi_pf(B->QFR->D, B->FB[fpd[-2]]));
+      p = fpc << 1;
+      b1 = umodiu(gel(form2,2), p);
+      b2 = umodiu(gel(form,2),  p);
+      if (b1 != b2 && b1+b2 != p) continue;
+
+      col = gel(mat,++s);
+      add_fact(B,col, form);
+      (void)factorquad(B,form2,B->KC,LIMC);
+      if (b1==b2)
+      {
+        for (i=1; i<lgsub; i++) col[B->subFB[i]] += fpd[i]-ex[i];
+        sub_fact(B, col, form2); col[fpd[-2]]++;
+      }
+      else
+      {
+        for (i=1; i<lgsub; i++) col[B->subFB[i]] += -fpd[i]-ex[i];
+        add_fact(B, col, form2); col[fpd[-2]]--;
+      }
+      if (DEBUGLEVEL) err_printf(" %ldP",s);
+    }
+    else
+    {
+      col = gel(mat,++s);
+      for (i=1; i<lgsub; i++) col[B->subFB[i]] = -ex[i];
+      add_fact(B, col, form);
+      if (DEBUGLEVEL) err_printf(" %ld",s);
+    }
+    col[current]--;
+    if (++current > B->KC) current = 1;
+  }
+  if (DEBUGLEVEL) dbg_all(&T, "random", s, nbtest);
+  *pc = current;
+}
+
+static int
+imag_be_honest(struct buch_quad *B)
+{
+  long p, fpc, s = B->KC, nbtest = 0;
+  GEN F, ex = cgetg(lg(B->subFB), t_VECSMALL);
+  pari_sp av = avma;
+
+  while (s<B->KC2)
+  {
+    p = B->FB[s+1]; if (DEBUGLEVEL) err_printf(" %ld",p);
+    F = qficomp(qfi_pf(B->QFR->D, p), qfi_random(B, ex));
+    fpc = factorquad(B,F,s,p-1);
+    if (fpc == 1) { nbtest=0; s++; }
+    else
+      if (++nbtest > 40) return 0;
+    avma = av;
+  }
+  return 1;
+}
+
+/* Real Quadratic fields */
+
+static void
+real_relations(struct buch_quad *B, long need, long *pc, long lim, ulong LIMC, GEN mat, GEN C)
+{
+  pari_timer T;
+  long lgsub = lg(B->subFB), prec = B->PRECREG, current = *pc, nbtest=0, s=0;
+  long i, fpc, endcycle, rhoacc, rho;
+  /* in a 2nd phase, don't include FB[current] but run along the cyle
+   * ==> get more units */
+  int first = (current == 0);
+  pari_sp av, av1, limstack;
+  GEN d, col, form, form0, form1, ex = cgetg(lgsub, t_VECSMALL);
+
+  if (DEBUGLEVEL) timer_start(&T);
+  if (!current) current = 1;
+  if (lim > need) lim = need;
+  av = avma; limstack = stack_lim(av,1);
+  for(;;)
+  {
+    if (s >= need) break;
+    if (first && s >= lim) {
+      first = 0;
+      if (DEBUGLEVEL) dbg_all(&T, "initial", s, nbtest);
+    }
+    avma = av; form = qfr3_random(B, ex);
+    if (!first)
+      form = QFR3_comp(form, qfr3_pf(B->QFR, B->FB[current]), B->QFR);
+    av1 = avma;
+    form0 = form; form1 = NULL;
+    endcycle = rhoacc = 0;
+    rho = -1;
+
+CYCLE:
+    if (endcycle || rho > 5000)
+    {
+      if (++current > B->KC) current = 1;
+      continue;
+    }
+    if (low_stack(limstack, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"real_relations");
+      gerepileall(av1, form1? 2: 1, &form, &form1);
+    }
+    if (rho < 0) rho = 0; /* first time in */
+    else
+    {
+      form = qfr3_rho(form, B->QFR); rho++;
+      rhoacc++;
+      if (first)
+        endcycle = (absi_equal(gel(form,1),gel(form0,1))
+             && equalii(gel(form,2),gel(form0,2)));
+      else
+      {
+        if (absi_equal(gel(form,1), gel(form,3))) /* a = -c */
+        {
+          if (absi_equal(gel(form,1),gel(form0,1)) &&
+                  equalii(gel(form,2),gel(form0,2))) continue;
+          form = qfr3_rho(form, B->QFR); rho++;
+          rhoacc++;
+        }
+        else
+          { setsigne(form[1],1); setsigne(form[3],-1); }
+        if (equalii(gel(form,1),gel(form0,1)) &&
+            equalii(gel(form,2),gel(form0,2))) continue;
+      }
+    }
+    nbtest++; fpc = factorquad(B,form,B->KC,LIMC);
+    if (!fpc)
+    {
+      if (DEBUGLEVEL>1) err_printf(".");
+      goto CYCLE;
+    }
+    if (fpc > 1)
+    { /* look for Large Prime relation */
+      long *fpd = largeprime(B,fpc,ex,first? 0: current,rhoacc);
+      ulong b1, b2, p;
+      GEN form2;
+      if (!fpd)
+      {
+        if (DEBUGLEVEL>1) err_printf(".");
+        goto CYCLE;
+      }
+      if (!form1)
+      {
+        form1 = qfr5_factorback(B,ex);
+        if (!first)
+          form1 = QFR5_comp(form1, qfr5_pf(B->QFR, B->FB[current], prec), B->QFR);
+      }
+      form1 = qfr5_rho_pow(form1, rho, B->QFR);
+      rho = 0;
+
+      form2 = qfr5_factorback(B,fpd);
+      if (fpd[-2])
+        form2 = QFR5_comp(form2, qfr5_pf(B->QFR, B->FB[fpd[-2]], prec), B->QFR);
+      form2 = qfr5_rho_pow(form2, fpd[-3], B->QFR);
+      if (!absi_equal(gel(form2,1),gel(form2,3)))
+      {
+        setsigne(form2[1], 1);
+        setsigne(form2[3],-1);
+      }
+      p = fpc << 1;
+      b1 = umodiu(gel(form2,2), p);
+      b2 = umodiu(gel(form1,2), p);
+      if (b1 != b2 && b1+b2 != p) goto CYCLE;
+
+      col = gel(mat,++s);
+      add_fact(B, col, form1);
+      (void)factorquad(B,form2,B->KC,LIMC);
+      if (b1==b2)
+      {
+        for (i=1; i<lgsub; i++) col[B->subFB[i]] += fpd[i]-ex[i];
+        sub_fact(B,col, form2);
+        if (fpd[-2]) col[fpd[-2]]++;
+        d = qfr5_dist(subii(gel(form1,4),gel(form2,4)),
+                      divrr(gel(form1,5),gel(form2,5)), prec);
+      }
+      else
+      {
+        for (i=1; i<lgsub; i++) col[B->subFB[i]] += -fpd[i]-ex[i];
+        add_fact(B, col, form2);
+        if (fpd[-2]) col[fpd[-2]]--;
+        d = qfr5_dist(addii(gel(form1,4),gel(form2,4)),
+                      mulrr(gel(form1,5),gel(form2,5)), prec);
+      }
+      if (DEBUGLEVEL) err_printf(" %ldP",s);
+    }
+    else
+    { /* standard relation */
+      if (!form1)
+      {
+        form1 = qfr5_factorback(B, ex);
+        if (!first)
+          form1 = QFR5_comp(form1, qfr5_pf(B->QFR, B->FB[current], prec), B->QFR);
+      }
+      form1 = qfr5_rho_pow(form1, rho, B->QFR);
+      rho = 0;
+
+      col = gel(mat,++s);
+      for (i=1; i<lgsub; i++) col[B->subFB[i]] = -ex[i];
+      add_fact(B, col, form1);
+      d = qfr5_dist(gel(form1,4), gel(form1,5), prec);
+      if (DEBUGLEVEL) err_printf(" %ld",s);
+    }
+    affrr(d, gel(C,s));
+    if (first)
+    {
+      if (s >= lim) continue;
+      goto CYCLE;
+    }
+    else
+    {
+      col[current]--;
+      if (++current > B->KC) current = 1;
+    }
+  }
+  if (DEBUGLEVEL) dbg_all(&T, "random", s, nbtest);
+  *pc = current;
+}
+
+static int
+real_be_honest(struct buch_quad *B)
+{
+  long p, fpc, s = B->KC, nbtest = 0;
+  GEN F,F0, ex = cgetg(lg(B->subFB), t_VECSMALL);
+  pari_sp av = avma;
+
+  while (s<B->KC2)
+  {
+    p = B->FB[s+1]; if (DEBUGLEVEL) err_printf(" %ld",p);
+    F = QFR3_comp(qfr3_random(B, ex), qfr3_pf(B->QFR, p), B->QFR);
+    for (F0 = F;;)
+    {
+      fpc = factorquad(B,F,s,p-1);
+      if (fpc == 1) { nbtest=0; s++; break; }
+      if (++nbtest > 40) return 0;
+      F = qfr3_canon(qfr3_rho(F, B->QFR), B->QFR);
+      if (equalii(gel(F,1),gel(F0,1))
+       && equalii(gel(F,2),gel(F0,2))) break;
+    }
+    avma = av;
+  }
+  return 1;
+}
+
+static GEN
+gcdreal(GEN a,GEN b)
+{
+  if (!signe(a)) return mpabs(b);
+  if (!signe(b)) return mpabs(a);
+  if (typ(a)==t_INT)
+  {
+    if (typ(b)==t_INT) return gcdii(a,b);
+    a = itor(a, lg(b));
+  }
+  else if (typ(b)==t_INT)
+    b = itor(b, lg(a));
+  if (expo(a)<-5) return absr(b);
+  if (expo(b)<-5) return absr(a);
+  a = absr(a); b = absr(b);
+  while (expo(b) >= -5  && signe(b))
+  {
+    long e;
+    GEN r, q = gcvtoi(divrr(a,b),&e);
+    if (e > 0) return NULL;
+    r = subrr(a, mulir(q,b)); a = b; b = r;
+  }
+  return absr(a);
+}
+
+static int
+get_R(struct buch_quad *B, GEN C, long sreg, GEN z, GEN *ptR)
+{
+  GEN R = gen_1;
+  double c;
+  long i;
+
+  if (B->PRECREG)
+  {
+    R = mpabs(gel(C,1));
+    for (i=2; i<=sreg; i++)
+    {
+      R = gcdreal(gel(C,i), R);
+      if (!R) return fupb_PRECI;
+    }
+    if (gexpo(R) <= -3)
+    {
+      if (DEBUGLEVEL) err_printf("regulator is zero.\n");
+      return fupb_RELAT;
+    }
+    if (DEBUGLEVEL) err_printf("#### Tentative regulator: %Ps\n",R);
+  }
+  c = gtodouble(gmul(z, R));
+  if (c < 0.8 || c > 1.3) return fupb_RELAT;
+  *ptR = R; return fupb_NONE;
+}
+
+static int
+quad_be_honest(struct buch_quad *B)
+{
+  int r;
+  if (B->KC2 <= B->KC) return 1;
+  if (DEBUGLEVEL)
+    err_printf("be honest for primes from %ld to %ld\n", B->FB[B->KC+1],B->FB[B->KC2]);
+  r = B->PRECREG? real_be_honest(B): imag_be_honest(B);
+  if (DEBUGLEVEL) err_printf("\n");
+  return r;
+}
+
+
+GEN
+Buchquad(GEN D, double cbach, double cbach2, long prec)
+{
+  const long MAXRELSUP = 7, SFB_MAX = 3;
+  pari_timer T;
+  pari_sp av0 = avma, av, av2;
+  const long RELSUP = 5;
+  long i, s, current, triv, sfb_trials, nrelsup, nreldep, need, nsubFB, minSFB;
+  ulong low, high, LIMC0, LIMC, LIMC2, LIMCMAX, cp;
+  GEN W, cyc, res, gen, dep, mat, C, extraC, B, R, invhr, h = NULL; /*-Wall*/
+  double drc, sdrc, lim, LOGD, LOGD2;
+  GRHcheck_t GRHcheck;
+  struct qfr_data QFR;
+  struct buch_quad BQ;
+  int FIRST = 1;
+
+  check_quaddisc(D, &s, /*junk*/&i, "Buchquad");
+  R = NULL; /* -Wall */
+  BQ.QFR = &QFR;
+  QFR.D = D;
+  if (s < 0)
+  {
+    if (cmpiu(QFR.D,4) <= 0)
+    {
+      GEN z = cgetg(5,t_VEC);
+      gel(z,1) = gel(z,4) = gen_1; gel(z,2) = gel(z,3) = cgetg(1,t_VEC);
+      return z;
+    }
+    BQ.PRECREG = 0;
+  } else {
+    BQ.PRECREG = maxss(prec+EXTRAPRECWORD, nbits2prec(2*expi(QFR.D) + 128));
+  }
+  if (DEBUGLEVEL) timer_start(&T);
+  BQ.primfact   = new_chunk(100);
+  BQ.exprimfact = new_chunk(100);
+  BQ.hashtab = (long**) new_chunk(HASHT);
+  for (i=0; i<HASHT; i++) BQ.hashtab[i] = NULL;
+
+  drc = fabs(gtodouble(QFR.D));
+  LOGD = log(drc);
+  LOGD2 = LOGD * LOGD;
+
+  sdrc = lim = sqrt(drc);
+  if (!BQ.PRECREG) lim /= sqrt(3.);
+  cp = (ulong)exp(sqrt(LOGD*log(LOGD)/8.0));
+  if (cp < 20) cp = 20;
+  if (cbach > 6.) {
+    if (cbach2 < cbach) cbach2 = cbach;
+    cbach = 6.;
+  }
+  if (cbach < 0.)
+    pari_err_DOMAIN("Buchquad","Bach constant","<",gen_0,dbltor(cbach));
+  av = avma;
+  BQ.powsubFB = BQ.subFB = NULL;
+  minSFB = (expi(D) > 15)? 3: 2;
+  init_GRHcheck(&GRHcheck, 2, BQ.PRECREG? 2: 0, LOGD);
+  high = low = LIMC0 = maxss((long)(cbach2*LOGD2), 1);
+  LIMCMAX = (long)(6.*LOGD2);
+  /* 97/1223 below to ensure a good enough approximation of residue */
+  cache_prime_quad(&GRHcheck, expi(D) < 16 ? 97: 1223, D);
+  while (!quadGRHchk(D, &GRHcheck, high))
+  {
+    low = high;
+    high *= 2;
+  }
+  while (high - low > 1)
+  {
+    long test = (low+high)/2;
+    if (quadGRHchk(D, &GRHcheck, test))
+      high = test;
+    else
+      low = test;
+  }
+  if (high == LIMC0+1 && quadGRHchk(D, &GRHcheck, LIMC0))
+    LIMC2 = LIMC0;
+  else
+    LIMC2 = high;
+  if (LIMC2 > LIMCMAX) LIMC2 = LIMCMAX;
+  LIMC0 = (long)(cbach*LOGD2);
+  LIMC = cbach ? LIMC0 : LIMC2;
+  LIMC = maxss(LIMC, nthidealquad(D, 2));
+
+/* LIMC = Max(cbach*(log D)^2, exp(sqrt(log D loglog D) / 8)) */
+START:
+  do
+  {
+    if (!FIRST) LIMC = check_LIMC(LIMC,LIMCMAX);
+    if (DEBUGLEVEL && LIMC > LIMC0)
+      err_printf("%s*** Bach constant: %f\n", FIRST?"":"\n", LIMC/LOGD2);
+    FIRST = 0; avma = av;
+    if (BQ.subFB) gunclone(BQ.subFB);
+    if (BQ.powsubFB) gunclone(BQ.powsubFB);
+    clearhash(BQ.hashtab);
+    if (LIMC < cp) LIMC = cp;
+    if (LIMC2 < LIMC) LIMC2 = LIMC;
+    if (BQ.PRECREG) qfr_data_init(QFR.D, BQ.PRECREG, &QFR);
+
+    FBquad(&BQ, LIMC2, LIMC, &GRHcheck);
+    if (DEBUGLEVEL) timer_printf(&T, "factor base");
+    BQ.subFB = subFBquad(&BQ, QFR.D, lim + 0.5, minSFB);
+    if (DEBUGLEVEL) timer_printf(&T, "subFBquad = %Ps",
+                                 vecpermute(BQ.FB, BQ.subFB));
+    nsubFB = lg(BQ.subFB) - 1;
+  }
+  while (nsubFB < (expi(D) > 15 ? 3 : 2));
+  /* invhr = 2^r1 (2pi)^r2 / sqrt(D) w ~ L(chi,1) / hR */
+  invhr = gmul(dbltor((BQ.PRECREG?2.:PI)/sdrc), compute_invresquad(&GRHcheck));
+  BQ.powsubFB = powsubFBquad(&BQ,CBUCH+1);
+  if (DEBUGLEVEL) timer_printf(&T, "powsubFBquad");
+  BQ.limhash = (LIMC & HIGHMASK)? (HIGHBIT>>1): LIMC*LIMC;
+
+  need = BQ.KC + RELSUP - 2;
+  current = 0;
+  W = NULL;
+  sfb_trials = nreldep = nrelsup = 0;
+  s = nsubFB + RELSUP;
+  av2 = avma;
+
+  do
+  {
+    if ((nreldep & 3) == 1 || (nrelsup & 7) == 1) {
+      if (DEBUGLEVEL) err_printf("*** Changing sub factor base\n");
+      gunclone(BQ.subFB);
+      gunclone(BQ.powsubFB);
+      BQ.subFB = gclone(vecslice(BQ.vperm, 1, nsubFB));
+      BQ.powsubFB = powsubFBquad(&BQ,CBUCH+1);
+      if (DEBUGLEVEL) timer_printf(&T, "powsubFBquad");
+      clearhash(BQ.hashtab);
+    }
+    need += 2;
+    mat    = cgetg(need+1, t_MAT);
+    extraC = cgetg(need+1, t_VEC);
+    if (!W) { /* first time */
+      C = extraC;
+      triv = trivial_relations(&BQ, mat, C);
+      if (DEBUGLEVEL) err_printf("KC = %ld, need %ld relations\n", BQ.KC, need);
+    } else {
+      triv = 0;
+      if (DEBUGLEVEL) err_printf("...need %ld more relations\n", need);
+    }
+    if (BQ.PRECREG) {
+      for (i = triv+1; i<=need; i++) {
+        gel(mat,i) = zero_zv(BQ.KC);
+        gel(extraC,i) = cgetr(BQ.PRECREG);
+      }
+      real_relations(&BQ, need - triv, &current, s,LIMC,mat + triv,extraC + triv);
+    } else {
+      for (i = triv+1; i<=need; i++) {
+        gel(mat,i) = zero_zv(BQ.KC);
+        gel(extraC,i) = gen_0;
+      }
+      imag_relations(&BQ, need - triv, &current, LIMC,mat + triv);
+    }
+
+    if (!W)
+      W = hnfspec_i(mat,BQ.vperm,&dep,&B,&C,nsubFB);
+    else
+      W = hnfadd_i(W,BQ.vperm,&dep,&B,&C, mat,extraC);
+    gerepileall(av2, 4, &W,&C,&B,&dep);
+    need = BQ.KC - (lg(W)-1) - (lg(B)-1);
+    if (need)
+    {
+      if (++nreldep > 15 && cbach < 1) goto START;
+      continue;
+    }
+
+    h = ZM_det_triangular(W);
+    if (DEBUGLEVEL) err_printf("\n#### Tentative class number: %Ps\n", h);
+
+    switch(get_R(&BQ, C, (lg(C)-1) - (lg(B)-1) - (lg(W)-1), mulir(h,invhr), &R))
+    {
+      case fupb_PRECI:
+        BQ.PRECREG = precdbl(BQ.PRECREG);
+        FIRST = 1; goto START;
+
+      case fupb_RELAT:
+        if (++nrelsup > MAXRELSUP)
+        {
+          if (++sfb_trials > SFB_MAX && cbach <= 1) goto START;
+          if (nsubFB < minss(10,BQ.KC)) nsubFB++;
+        }
+        need = minss(BQ.KC, nrelsup);
+    }
+  }
+  while (need);
+  /* DONE */
+  if (!quad_be_honest(&BQ)) goto START;
+  if (DEBUGLEVEL) timer_printf(&T, "be honest");
+  clearhash(BQ.hashtab);
+  free_GRHcheck(&GRHcheck);
+
+  gen = get_clgp(&BQ,W,&cyc,BQ.PRECREG);
+  gunclone(BQ.subFB);
+  gunclone(BQ.powsubFB);
+  res = cgetg(5,t_VEC);
+  gel(res,1) = h;
+  gel(res,2) = cyc;
+  gel(res,3) = gen;
+  gel(res,4) = R; return gerepilecopy(av0,res);
+}
+
+GEN
+buchimag(GEN D, GEN c, GEN c2, GEN REL)
+{ (void)REL; return Buchquad(D,gtodouble(c),gtodouble(c2),0); }
+
+GEN
+buchreal(GEN D, GEN flag, GEN c, GEN c2, GEN REL, long prec) {
+  if (signe(flag)) pari_err_IMPL("narrow class group");
+  (void)REL; return Buchquad(D,gtodouble(c),gtodouble(c2),prec);
+}
+
+GEN
+quadclassunit0(GEN x, long flag, GEN data, long prec)
+{
+  long lx;
+  double c1 = 0.0, c2 = 0.0;
+
+  if (!data) lx=1;
+  else
+  {
+    lx = lg(data);
+    if (typ(data)!=t_VEC) pari_err_TYPE("quadclassunit", data);
+    if (lx > 7) pari_err_DIM("quadclassunit [tech vector]");
+    if (lx > 3) lx = 3;
+  }
+  switch(lx)
+  {
+    case 3: c2 = gtodouble(gel(data,2));
+    case 2: c1 = gtodouble(gel(data,1));
+  }
+  if (flag) pari_err_IMPL("narrow class group");
+  return Buchquad(x,c1,c2,prec);
+}
diff --git a/src/basemath/buch2.c b/src/basemath/buch2.c
new file mode 100644
index 0000000..87a354a
--- /dev/null
+++ b/src/basemath/buch2.c
@@ -0,0 +1,4227 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+/*******************************************************************/
+/*                                                                 */
+/*         CLASS GROUP AND REGULATOR (McCURLEY, BUCHMANN)          */
+/*                    GENERAL NUMBER FIELDS                        */
+/*                                                                 */
+/*******************************************************************/
+/* get_random_ideal */
+static const long RANDOM_BITS = 4;
+/* Buchall */
+static const double BNF_C1 = 0.0, BNF_C2 = 0.0;
+static const long RELSUP = 5;
+static const long FAIL_DIVISOR = 32;
+static const long MINFAIL = 10;
+/* small_norm */
+static const long BNF_RELPID = 4;
+static const long BMULT = 8;
+static const long maxtry_ELEMENT = 1000*1000;
+static const long maxtry_DEP = 20;
+static const long maxtry_FACT = 500;
+/* rnd_rel */
+static const long RND_REL_RELPID = 1;
+static const long PREVENT_LLL_IN_RND_REL = 1;
+/* random relations */
+static const long MINSFB = 3;
+static const long SFB_MAX = 3;
+static const long DEPSIZESFBMULT = 16;
+static const long DEPSFBDIV = 10;
+/* add_rel_i */
+static const ulong mod_p = 27449UL;
+/* be_honest */
+static const long maxtry_HONEST = 50;
+
+typedef struct FACT {
+    long pr, ex;
+} FACT;
+
+typedef struct subFB_t {
+  GEN subFB;
+  struct subFB_t *old;
+} subFB_t;
+
+/* a factor base contains only non-inert primes
+ * KC = # of P in factor base (p <= n, NP <= n2)
+ * KC2= # of P assumed to generate class group (NP <= n2)
+ *
+ * KCZ = # of rational primes under ideals counted by KC
+ * KCZ2= same for KC2 */
+
+typedef struct FB_t {
+  GEN FB; /* FB[i] = i-th rational prime used in factor base */
+  GEN LP; /* vector of all prime ideals in FB */
+  GEN *LV; /* LV[p] = vector of P|p, NP <= n2
+            * isclone() is set for LV[p] iff all P|p are in FB
+            * LV[i], i not prime or i > n2, is undefined! */
+  GEN iLP; /* iLP[p] = i such that LV[p] = [LP[i],...] */
+  GEN id2; /* id2[i] = powers of ideal i */
+  GEN L_jid; /* indexes of "useful" prime ideals for rnd_rel */
+  long KC, KCZ, KCZ2;
+  GEN subFB; /* LP o subFB =  part of FB used to build random relations */
+  int sfb_chg; /* need to change subFB ? */
+  int newpow; /* need to compute powFB */
+  int newarc; /* need to compute archimedean components */
+  GEN perm; /* permutation of LP used to represent relations [updated by
+               hnfspec/hnfadd: dense rows come first] */
+  GEN vecG, G0;
+  GEN idealperm; /* permutation of ideals under field automorphisms */
+  GEN minidx; /* minidx[i] min ideal in orbit of LP[i] under field autom */
+  long orbits; /* number of ideal orbits */
+  subFB_t *allsubFB; /* all subFB's used */
+  GEN embperm; /* permutations of the complex embeddings */
+  GEN invs; /* inverse of automorphism */
+} FB_t;
+
+enum { sfb_CHANGE = 1, sfb_INCREASE = 2 };
+
+typedef struct REL_t {
+  GEN R; /* relation vector as t_VECSMALL; clone */
+  long nz; /* index of first non-zero elt in R (hash) */
+  GEN m; /* pseudo-minimum yielding the relation; clone */
+  long relorig; /* relation this one is an image of */
+  long relaut; /* automorphim used to compute this relation from the original */
+  GEN junk[3]; /*make sure sizeof(struct) is a power of two.*/
+} REL_t;
+
+typedef struct RELCACHE_t {
+  REL_t *chk; /* last checkpoint */
+  REL_t *base; /* first rel found */
+  REL_t *last; /* last rel found so far */
+  REL_t *end; /* target for last relation. base <= last <= end */
+  size_t len; /* number of rels pre-allocated in base */
+  long relsup; /* how many linearly dependent relations to we allow */
+  GEN basis; /* mod p basis (generating family actually) */
+  ulong missing; /* missing vectors in generating family above */
+} RELCACHE_t;
+
+typedef struct FP_t {
+  double **q;
+  GEN x;
+  double *y;
+  double *z;
+  double *v;
+} FP_t;
+
+typedef struct RNDREL_t {
+  GEN Nideal;
+  long jid;
+  GEN ex;
+  GEN m1;
+} RNDREL_t;
+
+static void
+wr_rel(GEN col)
+{
+  long i, l = lg(col);
+  err_printf("\nrel = ");
+  for (i=1; i<l; i++)
+    if (col[i]) err_printf("%ld^%ld ",i,col[i]);
+  err_printf("\n");
+}
+static void
+dbg_newrel(RELCACHE_t *cache)
+{
+  if (DEBUGLEVEL > 1)
+  {
+    err_printf("\n++++ cglob = %ld", cache->last - cache->base);
+    wr_rel(cache->last->R);
+  }
+  else
+    err_printf("%ld ", cache->last - cache->base);
+}
+
+static void
+dbg_cancelrel(long jid, long jdir, GEN col)
+{
+  err_printf("relation cancelled: ");
+  if (DEBUGLEVEL>3) err_printf("(jid=%ld,jdir=%ld)",jid,jdir);
+  wr_rel(col); err_flush();
+}
+
+
+static void
+delete_cache(RELCACHE_t *M)
+{
+  REL_t *rel;
+  for (rel = M->base+1; rel <= M->last; rel++)
+  {
+    gunclone(rel->R);
+    if (!rel->m) continue;
+    gunclone(rel->m);
+  }
+  pari_free((void*)M->base); M->base = NULL;
+}
+
+static void
+unclone_subFB(FB_t *F)
+{
+  subFB_t *sub, *subold;
+  GEN id2 = F->id2;
+  long i;
+
+  for (sub = F->allsubFB; sub; sub = subold)
+  {
+    GEN subFB = sub->subFB;
+    for (i = 1; i < lg(subFB); i++)
+    {
+      long id = subFB[i];
+      if (gel(id2, id) == gen_0) continue;
+
+      gunclone(gel(id2, id));
+      gel(id2, id) = gen_0;
+    }
+    subold = sub->old;
+    pari_free(sub);
+  }
+}
+
+static void
+delete_FB(FB_t *F)
+{
+  unclone_subFB(F);
+  gunclone(F->minidx);
+  gunclone(F->idealperm);
+}
+
+static void
+reallocate(RELCACHE_t *M, long len)
+{
+  REL_t *old = M->base;
+  M->len = len;
+  M->base = (REL_t*)pari_realloc((void*)old, (len+1) * sizeof(REL_t));
+  if (old)
+  {
+    size_t last = M->last - old, chk = M->chk - old, end = M->end - old;
+    M->last = M->base + last;
+    M->chk  = M->base + chk;
+    M->end  = M->base + end;
+  }
+}
+
+#define pr_get_smallp(pr) gel(pr,1)[2]
+
+/* don't take P|p all other Q|p are already there */
+static int
+bad_subFB(FB_t *F, long t)
+{
+  GEN LP, P = gel(F->LP,t);
+  long p = pr_get_smallp(P);
+  LP = F->LV[p];
+  return (isclone(LP) && t == F->iLP[p] + lg(LP)-1);
+}
+
+static void
+assign_subFB(FB_t *F, GEN yes, long iyes)
+{
+  subFB_t *sub;
+  long i, lv;
+
+  /* single malloc for struct + GEN */
+  lv = sizeof(subFB_t) + iyes*sizeof(long);
+  sub = (subFB_t *)pari_malloc(lv);
+  sub->subFB = (GEN)&sub[1];
+  sub->old = F->allsubFB;
+  F->allsubFB = sub;
+  for (i = 0; i < iyes; i++) sub->subFB[i] = yes[i];
+  F->subFB = sub->subFB;
+  F->newpow = 1;
+  F->newarc = 1;
+}
+
+/*
+ * Determine the permutation of the ideals made by each field automorphism.
+ */
+static void
+FB_aut_perm(FB_t *F, GEN nf, GEN auts, GEN cyclic)
+{
+  pari_sp av0 = avma;
+  long i, KC = F->KC, nauts = lg(auts);
+  GEN minidx = zero_Flv(KC), perm = zero_Flm_copy(KC, nauts-1);
+
+  if (nauts == 1)
+  {
+    for (i = 1; i <= KC; i++) minidx[i] = i;
+    F->orbits = KC;
+  }
+  else
+  {
+    long j, m;
+    F->orbits = 0;
+    for (m = 1; m < lg(cyclic); m++)
+    {
+      GEN thiscyc = gel(cyclic, m);
+      long k0 = thiscyc[1];
+      GEN aut = gel(auts, k0), permk0 = gel(perm, k0), ppermk;
+      i = 1;
+      while (i <= KC)
+      {
+        pari_sp av2 = avma;
+        GEN seen = zero_Flv(KC), P = gel(F->LP, i);
+        long imin = i, p, f, l;
+        p = pr_get_p(P)[2];
+        f = pr_get_f(P);
+        do
+        {
+          if (++i > KC) break;
+          P = gel(F->LP, i);
+        }
+        while (p == pr_get_p(P)[2] && f == pr_get_f(P));
+        for (j = imin; j < i; j++)
+        {
+          GEN img = ZM_ZC_mul(aut, pr_get_gen(gel(F->LP, j)));
+          for (l = imin; l < i; l++)
+            if (!seen[l] && nfval(nf, img, gel(F->LP, l)))
+            {
+              seen[l] = 1; permk0[j] = l; break;
+            }
+        }
+        avma = av2;
+      }
+      for (ppermk = permk0, i = 2; i < lg(thiscyc); i++)
+      {
+        GEN permk = gel(perm, thiscyc[i]);
+        for (j = 1; j <= KC; j++) permk[j] = permk0[ppermk[j]];
+        ppermk = permk;
+      }
+    }
+    for (j = 1; j <= KC; j++)
+    {
+      if (minidx[j]) continue;
+      F->orbits++;
+      minidx[j] = j;
+      for (i = 1; i < nauts; i++) minidx[coeff(perm, j, i)] = j;
+    }
+  }
+  F->minidx = gclone(minidx);
+  F->idealperm = gclone(perm);
+  avma = av0;
+}
+
+/* set subFB.
+ * Fill F->perm (if != NULL): primes ideals sorted by increasing norm (except
+ * the ones in subFB come first [dense rows for hnfspec]) */
+static int
+subFBgen(FB_t *F, GEN nf, GEN auts, GEN cyclic, double PROD, long minsFB)
+{
+  GEN y, perm, yes, no;
+  long i, j, k, iyes, ino, lv = F->KC + 1;
+  double prod;
+  pari_sp av;
+
+  F->LP   = cgetg(lv, t_VEC);
+  F->L_jid = F->perm = cgetg(lv, t_VECSMALL);
+  av = avma;
+  y = cgetg(lv,t_COL); /* Norm P */
+  for (k=0, i=1; i <= F->KCZ; i++)
+  {
+    GEN LP = F->LV[F->FB[i]];
+    long l = lg(LP);
+    for (j = 1; j < l; j++)
+    {
+      GEN P = gel(LP,j);
+      k++;
+      gel(y,k) = pr_norm(P);
+      gel(F->LP,k) = P;
+    }
+  }
+  /* perm sorts LP by increasing norm */
+  perm = indexsort(y);
+  no  = cgetg(lv, t_VECSMALL); ino  = 1;
+  yes = cgetg(lv, t_VECSMALL); iyes = 1;
+  prod = 1.0;
+  for (i = 1; i < lv; i++)
+  {
+    long t = perm[i];
+    if (bad_subFB(F, t)) { no[ino++] = t; continue; }
+
+    yes[iyes++] = t;
+    prod *= (double)itos(gel(y,t));
+    if (iyes > minsFB && prod > PROD) break;
+  }
+  setlg(yes, iyes);
+  for (j=1; j<iyes; j++)     F->perm[j] = yes[j];
+  for (i=1; i<ino; i++, j++) F->perm[j] =  no[i];
+  for (   ; j<lv; j++)       F->perm[j] =  perm[j];
+  F->allsubFB = NULL;
+  FB_aut_perm(F, nf, auts, cyclic);
+  if (iyes) assign_subFB(F, yes, iyes);
+  avma = av; return 1;
+}
+static int
+subFB_change(FB_t *F)
+{
+  long i, iyes, minsFB, lv = F->KC + 1, l = lg(F->subFB)-1;
+  pari_sp av = avma;
+  GEN yes, L_jid = F->L_jid, present = zero_zv(lv-1);
+
+  switch (F->sfb_chg)
+  {
+    case sfb_INCREASE: minsFB = l + 1; break;
+    default: minsFB = l; break;
+  }
+
+  yes = cgetg(minsFB+1, t_VECSMALL); iyes = 1;
+  if (L_jid)
+  {
+    for (i = 1; i < lg(L_jid); i++)
+    {
+      long l = L_jid[i];
+      yes[iyes++] = l;
+      present[l] = 1;
+      if (iyes > minsFB) break;
+    }
+  }
+  else i = 1;
+  if (iyes <= minsFB)
+  {
+    for ( ; i < lv; i++)
+    {
+      long l = F->perm[i];
+      if (present[l]) continue;
+      yes[iyes++] = l;
+      if (iyes > minsFB) break;
+    }
+    if (i == lv) return 0;
+  }
+  if (zv_equal(F->subFB, yes))
+  {
+    if (DEBUGLEVEL) err_printf("\n*** NOT Changing sub factor base\n");
+  }
+  else
+  {
+    if (DEBUGLEVEL) err_printf("\n*** Changing sub factor base\n");
+    assign_subFB(F, yes, iyes);
+  }
+  F->sfb_chg = 0;
+  avma = av; return 1;
+}
+
+static GEN
+init_famat(GEN x) { return mkvec2(x, cgetg(1,t_MAT)); }
+
+static GEN
+red(GEN nf, GEN I, GEN G0, GEN *pm)
+{
+  GEN m, y, norm, norm2;
+  norm = typ(I) == t_MAT ? ZM_det_triangular(I) : idealnorm(nf, I);
+  y = idealred0(nf, init_famat(I), G0);
+  m = gel(y,2);
+  y = gel(y,1); *pm = lg(m)==1? gen_1: Q_primpart(gmael(m, 1, 1));
+  norm2 = typ(y) == t_MAT ? ZM_det_triangular(y) : idealnorm(nf, y);
+  if (gcmp(norm, norm2) < 0 || is_pm1(gcoeff(y,1,1)))
+  {
+    *pm = gen_1;
+    y = I;
+  }
+  return idealtwoelt(nf,y);
+}
+
+/* make sure enough room to store n more relations */
+static void
+pre_allocate(RELCACHE_t *cache, size_t n)
+{
+  size_t len = (cache->last - cache->base) + n;
+  if (len >= cache->len) reallocate(cache, len << 1);
+}
+
+void
+init_GRHcheck(GRHcheck_t *S, long N, long R1, double LOGD)
+{
+  const double c1 = PI*PI/2;
+  const double c2 = 3.663862376709;
+  const double c3 = 3.801387092431; /* Euler + log(8*Pi)*/
+  S->clone = 0;
+  S->cN = R1*c2 + N*c1;
+  S->cD = LOGD - N*c3 - R1*PI/2;
+  S->maxprimes = 16000; /* sufficient for LIMC=176081*/
+  S->primes = (GRHprime_t*)pari_malloc(S->maxprimes*sizeof(*S->primes));
+  S->nprimes = 0;
+  S->limp = 0;
+  u_forprime_init(&S->P, 2, ULONG_MAX);
+}
+
+void
+free_GRHcheck(GRHcheck_t *S)
+{
+  if (S->clone)
+  {
+    long i = S->nprimes;
+    GRHprime_t *pr;
+    for (pr = S->primes, i = S->nprimes; i > 0; pr++, i--) gunclone(pr->dec);
+  }
+  pari_free(S->primes);
+}
+
+int
+GRHok(GRHcheck_t *S, double L, double SA, double SB)
+{
+  return (S->cD + (S->cN + 2*SB) / L - 2*SA < -1e-8);
+}
+
+/* Return factorization pattern of p: [f,n], where n[i] primes of
+ * residue degree f[i] */
+static GEN
+get_fs(GEN nf, GEN P, GEN index, ulong p)
+{
+  long j, k, f, n, l;
+  GEN fs, ns;
+
+  if (umodiu(index, p))
+  { /* easy case: p does not divide index */
+    GEN F = Flx_degfact(ZX_to_Flx(P,p), p);
+    fs = gel(F,1); l = lg(fs);
+    ns = gel(F,2); /*to be overwritten*/
+  }
+  else
+  {
+    GEN F = idealprimedec(nf, utoipos(p));
+    l = lg(F);
+    fs = cgetg(l, t_VECSMALL);
+    for (j = 1; j < l; j++) fs[j] = pr_get_f(gel(F,j));
+    ns = cgetg(l, t_VECSMALL);
+  }
+  f = fs[1]; n = 1;
+  for (j = 2, k = 1; j < l; j++)
+    if (fs[j] == f)
+      n++;
+    else
+    {
+      ns[k] = n; fs[k] = f; k++;
+      f = fs[j]; n = 1;
+    }
+  ns[k] = n; fs[k] = f; k++;
+  setlg(fs, k);
+  setlg(ns, k); return mkvec2(fs,ns);
+}
+
+/* cache data for all rational primes up to the LIM */
+static void
+cache_prime_dec(GRHcheck_t *S, ulong LIM, GEN nf)
+{
+  pari_sp av = avma;
+  GRHprime_t *pr;
+  GEN index, P;
+  double nb;
+
+  if (S->limp >= LIM) return;
+  S->clone = 1;
+  nb = primepi_upper_bound((double)LIM); /* #{p <= LIM} <= nb */
+  GRH_ensure(S, nb+1); /* room for one extra prime */
+  P = nf_get_pol(nf);
+  index = nf_get_index(nf);
+  for (pr = S->primes + S->nprimes;;)
+  {
+    ulong p = u_forprime_next(&(S->P));
+    pr->p = p;
+    pr->logp = log((double)p);
+    pr->dec = gclone(get_fs(nf, P, index, p));
+    S->nprimes++;
+    pr++;
+    /* store up to nextprime(LIM) included */
+    if (p >= LIM) { S->limp = p; break; }
+  }
+  avma = av;
+}
+
+static GEN
+compute_invres(GRHcheck_t *S)
+{
+  pari_sp av = avma;
+  GEN invres = real_1(DEFAULTPREC);
+  GRHprime_t *pr = S->primes;
+  long i = S->nprimes, LIMC = GRH_last_prime(S)+diffptr[i]-1; /* nextprime(p+1)-1*/
+  double llimc = log(LIMC);
+  for (; i > 0; pr++, i--)
+  {
+    GEN dec, a = NULL, b = NULL, fs, ns;
+    long j, k, limp = llimc/pr->logp;
+    ulong p = pr->p;
+    dec = pr->dec;
+    fs = gel(dec, 1); ns = gel(dec, 2);
+    k = lg(fs);
+    for (j = 1; j < k; j++)
+    {
+      long f, nb;
+      GEN nor;
+      f = fs[j]; if (f > limp) continue;
+      nb = ns[j];
+      nor = powuu(p, f);
+      if (a)
+      {
+        a = mulii(a, powiu(nor, nb));
+        b = mulii(b, powiu(subii(nor, gen_1), nb));
+      }
+      else
+      {
+        a = powuu(p, f*nb-1);
+        b = diviuexact(powiu(subii(nor, gen_1), nb), p-1);
+      }
+    }
+    if (a)
+      invres = divri(mulir(b, invres), a);
+    else
+      invres = divru(mulur(p, invres), p-1);
+  }
+  return gerepileuptoleaf(av, invres);
+}
+
+static long
+nthideal(GRHcheck_t *S, GEN nf, long n)
+{
+  pari_sp av = avma;
+  GEN P = nf_get_pol(nf);
+  ulong p = 0, *vecN = (ulong*)const_vecsmall(n, LONG_MAX);
+  long i, res, N = poldegree(P, -1);
+  for (i = 0; ; i++)
+  {
+    GRHprime_t *pr;
+    GEN fs;
+    cache_prime_dec(S, p+1, nf);
+    pr = S->primes + i;
+    fs = gel(pr->dec, 1);
+    p = pr->p;
+    if (fs[1] != N)
+    {
+      GEN ns = gel(pr->dec, 2);
+      long k, l, j = lg(fs);
+      while (--j > 0)
+      {
+        ulong NP = upowuu(p, fs[j]);
+        long nf;
+        if (!NP) continue;
+        for (k = 1; k <= n; k++) if (vecN[k] > NP) break;
+        if (k > n) continue;
+        /* vecN[k] <= NP */
+        nf = ns[j]; /*#{primes of norme NP} = nf, insert them here*/
+        for (l = k+nf; l <= n; l++) vecN[l] = vecN[l-nf];
+        for (l = 0; l < nf && k+l <= n; l++) vecN[k+l] = NP;
+        while (l <= k) vecN[l++] = NP;
+      }
+    }
+    if (p > vecN[n]) break;
+  }
+  res = vecN[n]; avma = av; return res;
+}
+
+
+/* Compute FB, LV, iLP + KC*. Reset perm
+ * C2: bound for norm of tested prime ideals (includes be_honest())
+ * C1: bound for p, such that P|p (NP <= C2) used to build relations
+
+ * Return prod_{p<=C2} (1-1/p) / prod_{Norm(P)<=C2} (1-1/Norm(P)),
+ * close to residue of zeta_K at 1 = 2^r1 (2pi)^r2 h R / (w D) */
+static void
+FBgen(FB_t *F, GEN nf, long N, ulong C1, ulong C2, GRHcheck_t *S)
+{
+  GRHprime_t *pr;
+  long i, ip;
+  GEN prim;
+  const double L = log((double)C2 + 0.5);
+
+  cache_prime_dec(S, C2, nf);
+  pr = S->primes;
+  F->sfb_chg = 0;
+  F->FB  = cgetg(C2+1, t_VECSMALL);
+  F->iLP = cgetg(C2+1, t_VECSMALL);
+  F->LV = (GEN*)const_vec(C2, NULL);
+
+  prim = icopy(gen_1);
+  i = ip = 0;
+  F->KC = F->KCZ = 0;
+  for (;; pr++) /* p <= C2 */
+  {
+    ulong p = pr->p;
+    pari_sp av = avma;
+    long k, l, m;
+    GEN LP, nb, f;
+
+    if (!F->KC && p > C1) { F->KCZ = i; F->KC = ip; }
+    if (p > C2) break;
+
+    if (DEBUGLEVEL>1) { err_printf(" %ld",p); err_flush(); }
+
+    f = gel(pr->dec, 1); nb = gel(pr->dec, 2);
+    if (f[1] == N) continue; /* p inert */
+    /* compute l such that p^f <= C2  <=> f <= l */
+    l = (long)(L/pr->logp);
+    for (k=0, m=1; m < lg(f) && f[m]<=l; m++) k += nb[m];
+
+    prim[2] = p; LP = idealprimedec(nf,prim);
+    /* keep non-inert ideals with Norm <= C2 */
+    for (m = 1; m <= k; m++)
+    {
+      GEN t = gel(LP,m);
+      gel(t,5) = zk_scalar_or_multable(nf, gel(t,5));
+    }
+    if (m == lg(LP))
+      setisclone(LP); /* flag it: all prime divisors in FB */
+    else
+      { setlg(LP,k+1); LP = gerepilecopy(av,LP); }
+    F->FB[++i]= p;
+    F->LV[p]  = LP;
+    F->iLP[p] = ip; ip += k;
+    if (p == C2)
+    {
+      if (!F->KC) { F->KCZ = i; F->KC = ip; }
+      break;
+    }
+  }
+  /* Note F->KC > 0 otherwise GRHchk is false */
+  setlg(F->FB, F->KCZ+1); F->KCZ2 = i;
+  if (DEBUGLEVEL>1)
+  {
+    err_printf("\n");
+    if (DEBUGLEVEL>6)
+    {
+      err_printf("########## FACTORBASE ##########\n\n");
+      err_printf("KC2=%ld, KC=%ld, KCZ=%ld, KCZ2=%ld\n",
+                  ip, F->KC, F->KCZ, F->KCZ2);
+      for (i=1; i<=F->KCZ; i++) err_printf("++ LV[%ld] = %Ps",i,F->LV[F->FB[i]]);
+    }
+  }
+  F->perm = NULL; F->L_jid = NULL;
+}
+
+static int
+GRHchk(GEN nf, GRHcheck_t *S, ulong LIMC)
+{
+  double logC = log((ulong)LIMC), SA = 0, SB = 0;
+  GRHprime_t *pr = S->primes;
+
+  cache_prime_dec(S, LIMC, nf);
+  for (pr = S->primes;; pr++)
+  {
+    ulong p = pr->p;
+    GEN dec, fs, ns;
+    double logCslogp;
+    long j;
+
+    if (p > LIMC) break;
+    dec = pr->dec; fs = gel(dec, 1); ns = gel(dec,2);
+    logCslogp = logC/pr->logp;
+    for (j = 1; j < lg(fs); j++)
+    {
+      long f = fs[j], M, nb;
+      double logNP, q, A, B;
+      if (f > logCslogp) break;
+      logNP = f * pr->logp;
+      q = 1/sqrt(upowuu(p, f));
+      A = logNP * q; B = logNP * A; M = (long)(logCslogp/f);
+      if (M > 1)
+      {
+        double inv1_q = 1 / (1-q);
+        A *= (1 - pow(q, M)) * inv1_q;
+        B *= (1 - pow(q, M)*(M+1 - M*q)) * inv1_q * inv1_q;
+      }
+      nb = ns[j];
+      SA += nb * A;
+      SB += nb * B;
+    }
+    if (p == LIMC) break;
+  }
+  return GRHok(S, logC, SA, SB);
+}
+
+/*  SMOOTH IDEALS */
+static void
+store(long i, long e, FACT *fact)
+{
+  ++fact[0].pr;
+  fact[fact[0].pr].pr = i; /* index */
+  fact[fact[0].pr].ex = e; /* exponent */
+}
+
+/* divide out x by all P|p, where x as in can_factor().  k = v_p(Nx) */
+static int
+divide_p_elt(GEN LP, long ip, long k, GEN nf, GEN m, FACT *fact)
+{
+  long j, l = lg(LP);
+  for (j=1; j<l; j++)
+  {
+    GEN P = gel(LP,j);
+    long v = ZC_nfval(nf, m, P);
+    if (!v) continue;
+    store(ip + j, v, fact); /* v = v_P(m) > 0 */
+    k -= v * pr_get_f(P);
+    if (!k) return 1;
+  }
+  return 0;
+}
+static int
+divide_p_id(GEN LP, long ip, long k, GEN nf, GEN I, FACT *fact)
+{
+  long j, l = lg(LP);
+  for (j=1; j<l; j++)
+  {
+    GEN P = gel(LP,j);
+    long v = idealval(nf,I, P);
+    if (!v) continue;
+    store(ip + j, v, fact); /* v = v_P(I) > 0 */
+    k -= v * pr_get_f(P);
+    if (!k) return 1;
+  }
+  return 0;
+}
+static int
+divide_p_quo(GEN LP, long ip, long k, GEN nf, GEN I, GEN m, FACT *fact)
+{
+  long j, l = lg(LP);
+  for (j=1; j<l; j++)
+  {
+    GEN P = gel(LP,j);
+    long v = ZC_nfval(nf, m, P);
+    if (!v) continue;
+    v -= idealval(nf,I, P);
+    if (!v) continue;
+    store(ip + j, v, fact); /* v = v_P(m / I) > 0 */
+    k -= v * pr_get_f(P);
+    if (!k) return 1;
+  }
+  return 0;
+}
+
+/* |*N| != 0 is the norm of a primitive ideal, in particular not divisible by
+ * any inert prime. Is |*N| a smooth rational integer wrt F ? (put the
+ * exponents in *ex) */
+static int
+smooth_norm(FB_t *F, GEN *N, GEN *ex)
+{
+  GEN FB = F->FB;
+  const long KCZ = F->KCZ;
+  const ulong limp = (ulong)FB[KCZ]; /* last p in FB */
+  long i;
+
+  *ex = new_chunk(KCZ+1);
+  for (i=1; ; i++)
+  {
+    int stop;
+    (*ex)[i] = Z_lvalrem_stop(N, (ulong)FB[i], &stop);
+    if (stop) break;
+    if (i == KCZ) return 0;
+  }
+  (*ex)[0] = i;
+  return (cmpiu(*N,limp) <= 0);
+}
+
+static int
+divide_p(FB_t *F, long p, long k, GEN nf, GEN I, GEN m, FACT *fact)
+{
+  GEN LP = F->LV[p];
+  long ip = F->iLP[p];
+  if (!LP)
+  {
+    if (!I) pari_err_BUG("divide_p");
+    pari_err_TYPE("divide_p [not an ideal]", I);
+  }
+  if (!m) return divide_p_id (LP,ip,k,nf,I,fact);
+  if (!I) return divide_p_elt(LP,ip,k,nf,m,fact);
+  return divide_p_quo(LP,ip,k,nf,I,m,fact);
+}
+
+/* Let x = m if I == NULL,
+ *         I if m == NULL,
+ *         m/I otherwise.
+ * Can we factor the integral primitive ideal x ? |N| = Norm x > 0 */
+static long
+can_factor(FB_t *F, GEN nf, GEN I, GEN m, GEN N, FACT *fact)
+{
+  GEN ex;
+  long i, res = 0;
+  fact[0].pr = 0;
+  if (is_pm1(N)) return 1;
+  if (!smooth_norm(F, &N, &ex)) goto END;
+  for (i=1; i<=ex[0]; i++)
+    if (ex[i] && !divide_p(F, F->FB[i], ex[i], nf, I, m, fact)) goto END;
+  res = is_pm1(N) || divide_p(F, itou(N), 1, nf, I, m, fact);
+END:
+  if (!res && DEBUGLEVEL > 1) { err_printf("."); err_flush(); }
+  return res;
+}
+
+/* can we factor m/I ? [m in I from idealpseudomin_nonscalar], NI = norm I */
+static long
+factorgen(FB_t *F, GEN nf, GEN I, GEN NI, GEN m, FACT *fact)
+{
+  long e, r1 = nf_get_r1(nf);
+  GEN M = nf_get_M(nf);
+  GEN N = divri(embed_norm(RgM_RgC_mul(M,m), r1), NI); /* ~ N(m/I) */
+  N = grndtoi(N, &e);
+  if (e > -1)
+  {
+    if (DEBUGLEVEL > 1) { err_printf("+"); err_flush(); }
+    return 0;
+  }
+  return can_factor(F, nf, I, m, N, fact);
+}
+
+/*  FUNDAMENTAL UNITS */
+
+/* a, m real. Return  (Re(x) + a) + I * (Im(x) % m) */
+static GEN
+addRe_modIm(GEN x, GEN a, GEN m)
+{
+  GEN re, im, z;
+  if (typ(x) == t_COMPLEX)
+  {
+    im = modr_safe(gel(x,2), m);
+    if (!im) return NULL;
+    re = gadd(gel(x,1), a);
+    z = gequal0(im)? re: mkcomplex(re, im);
+  }
+  else
+    z = gadd(x, a);
+  return z;
+}
+
+/* clean archimedean components */
+static GEN
+cleanarch(GEN x, long N, long prec)
+{
+  long i, R1, RU, tx = typ(x);
+  GEN s, y, pi2;
+
+  if (tx == t_MAT)
+  {
+    y = cgetg(lg(x), tx);
+    for (i=1; i < lg(x); i++) {
+      gel(y,i) = cleanarch(gel(x,i), N, prec);
+      if (!gel(y,i)) return NULL;
+    }
+    return y;
+  }
+  if (!is_vec_t(tx)) pari_err_TYPE("cleanarch",x);
+  RU = lg(x)-1; R1 = (RU<<1)-N;
+  s = gdivgs(RgV_sum(real_i(x)), -N); /* -log |norm(x)| / N */
+  y = cgetg(RU+1,tx);
+  pi2 = Pi2n(1, prec);
+  for (i=1; i<=R1; i++) {
+    gel(y,i) = addRe_modIm(gel(x,i), s, pi2);
+    if (!gel(y,i)) return NULL;
+  }
+  if (i <= RU)
+  {
+    GEN pi4 = Pi2n(2, prec), s2 = gmul2n(s, 1);
+    for (   ; i<=RU; i++) {
+      gel(y,i) = addRe_modIm(gel(x,i), s2, pi4);
+      if (!gel(y,i)) return NULL;
+    }
+  }
+  return y;
+}
+
+static GEN
+not_given(long reason)
+{
+  if (DEBUGLEVEL)
+    switch(reason)
+    {
+      case fupb_LARGE:
+        pari_warn(warner,"fundamental units too large, not given");
+        break;
+      case fupb_PRECI:
+        pari_warn(warner,"insufficient precision for fundamental units, not given");
+        break;
+    }
+  return cgetg(1,t_MAT);
+}
+
+/* check whether exp(x) will 1) get too big (real(x) large), 2) require
+ * large accuracy for argument reduction (imag(x) large) */
+static int
+exp_OK(GEN x, long *pte)
+{
+  long i,I,j,J, e = - (long)HIGHEXPOBIT;
+  RgM_dimensions(x, &I,&J);
+  for (j=1; j<=J; j++)
+    for (i=1; i<=I; i++)
+    {
+      GEN c = gcoeff(x,i,j), re;
+      if (typ(c)!=t_COMPLEX) re = c;
+      else
+      {
+        GEN im = gel(c,2);
+        e = maxss(e, expo(im) + 5 - bit_prec(im));
+        re = gel(c,1);
+      }
+      if (expo(re) > 20) { *pte = LONG_MAX; return 0; }
+    }
+  *pte = -e; return (e < 0);
+}
+
+static GEN
+getfu(GEN nf, GEN *ptA, long *pte, long prec)
+{
+  GEN p1, p2, u, y, matep, A, vec, T = nf_get_pol(nf), M = nf_get_M(nf);
+  long e, i, j, R1, RU, N = degpol(T);
+
+  if (DEBUGLEVEL) err_printf("\n#### Computing fundamental units\n");
+  R1 = nf_get_r1(nf); RU = (N+R1)>>1;
+  if (RU==1) { *pte=LONG_MAX; return cgetg(1,t_VEC); }
+
+  *pte = 0; A = *ptA;
+  matep = cgetg(RU,t_MAT);
+  for (j=1; j<RU; j++)
+  {
+    GEN c = cgetg(RU+1,t_COL), Aj = gel(A,j);
+    GEN s = gdivgs(RgV_sum(real_i(Aj)), -N); /* -log |norm(Aj)| / N */
+    gel(matep,j) = c;
+    for (i=1; i<=R1; i++) gel(c,i) = gadd(s, gel(Aj,i));
+    for (   ; i<=RU; i++) gel(c,i) = gadd(s, gmul2n(gel(Aj,i),-1));
+  }
+  u = lll(real_i(matep));
+  if (lg(u) < RU) return not_given(fupb_PRECI);
+
+  y = RgM_mul(matep,u);
+  if (!exp_OK(y, pte))
+    return not_given(*pte == LONG_MAX? fupb_LARGE: fupb_PRECI);
+  if (prec <= 0) prec = gprecision(A);
+  y = RgM_solve_realimag(M, gexp(y,prec));
+  if (!y) return not_given(fupb_PRECI);
+  y = grndtoi(y, &e);
+  *pte = -e;
+  if (e >= 0) return not_given(fupb_PRECI);
+  for (j=1; j<RU; j++)
+    if (!gequal1(idealnorm(nf, gel(y,j)))) break;
+  if (j < RU) { *pte = 0; return not_given(fupb_PRECI); }
+  A = RgM_mul(A,u);
+
+  /* y[i] are unit generators. Normalize: smallest L2 norm + lead coeff > 0 */
+  y = coltoliftalg(nf, y);
+  vec = cgetg(RU+1,t_COL);
+  p1 = PiI2n(0,prec); for (i=1; i<=R1; i++) gel(vec,i) = p1;
+  p2 = PiI2n(1,prec); for (   ; i<=RU; i++) gel(vec,i) = p2;
+  for (j=1; j<RU; j++)
+  {
+    GEN u = gel(y,j), v = QXQ_inv(u, T);
+    if (gcmp(RgX_fpnorml2(v,DEFAULTPREC),
+             RgX_fpnorml2(u,DEFAULTPREC)) < 0)
+    {
+      gel(A,j) = RgC_neg(gel(A,j));
+      u = v;
+    }
+    if (gsigne(leading_term(u)) < 0)
+    {
+      gel(A,j) = RgC_add(gel(A,j), vec);
+      u = RgX_neg(u);
+    }
+    gel(y,j) = u;
+  }
+  *ptA = A; return y;
+}
+
+GEN
+init_units(GEN BNF)
+{
+  GEN bnf = checkbnf(BNF), funits = bnf_get_fu_nocheck(bnf), v;
+  long i, l;
+  if (typ(funits) == t_MAT)
+  {
+    pari_sp av = avma;
+    GEN nf = bnf_get_nf(bnf), A = bnf_get_logfu(bnf);
+    funits = gerepilecopy(av, getfu(nf, &A, &l, 0));
+    if (typ(funits) == t_MAT)
+      pari_err_PREC("init_units [can't compute units on the fly]");
+  }
+  l = lg(funits) + 1;
+  v = cgetg(l, t_VEC); gel(v,1) = bnf_get_tuU(bnf);
+  for (i = 2; i < l; i++) gel(v,i) = gel(funits,i-1);
+  return v;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*           PRINCIPAL IDEAL ALGORITHM (DISCRETE LOG)              */
+/*                                                                 */
+/*******************************************************************/
+
+/* G: prime ideals, E: vector of non-negative exponents.
+ * C = possible extra prime (^1) or NULL
+ * Return Norm (product) */
+static GEN
+get_norm_fact_primes(GEN G, GEN E, GEN C)
+{
+  pari_sp av=avma;
+  GEN N = gen_1, P, p;
+  long i, c = lg(E);
+  for (i=1; i<c; i++)
+  {
+    GEN ex = gel(E,i);
+    long s = signe(ex);
+    if (!s) continue;
+
+    P = gel(G,i); p = pr_get_p(P);
+    N = mulii(N, powii(p, mului(pr_get_f(P), ex)));
+  }
+  if (C) N = mulii(N, pr_norm(C));
+  return gerepileuptoint(av, N);
+}
+
+/* gen: HNF ideals */
+static GEN
+get_norm_fact(GEN gen, GEN ex, GEN *pd)
+{
+  long i, c = lg(ex);
+  GEN d,N,I,e,n,ne,de;
+  d = N = gen_1;
+  for (i=1; i<c; i++)
+    if (signe(gel(ex,i)))
+    {
+      I = gel(gen,i); e = gel(ex,i); n = ZM_det_triangular(I);
+      ne = powii(n,e);
+      de = equalii(n, gcoeff(I,1,1))? ne: powii(gcoeff(I,1,1), e);
+      N = mulii(N, ne);
+      d = mulii(d, de);
+    }
+  *pd = d; return N;
+}
+
+static GEN
+get_pr_lists(GEN FB, long N, int list_pr)
+{
+  GEN pr, L;
+  long i, l = lg(FB), p, pmax;
+
+  pmax = 0;
+  for (i=1; i<l; i++)
+  {
+    pr = gel(FB,i); p = pr_get_smallp(pr);
+    if (p > pmax) pmax = p;
+  }
+  L = const_vec(pmax, NULL);
+  if (list_pr)
+  {
+    for (i=1; i<l; i++)
+    {
+      pr = gel(FB,i); p = pr_get_smallp(pr);
+      if (!L[p]) gel(L,p) = vectrunc_init(N+1);
+      vectrunc_append(gel(L,p), pr);
+    }
+    for (p=1; p<=pmax; p++)
+      if (L[p]) gen_sort_inplace(gel(L,p), (void*)&cmp_prime_over_p,
+                                 &cmp_nodata, NULL);
+  }
+  else
+  {
+    for (i=1; i<l; i++)
+    {
+      pr = gel(FB,i); p = pr_get_smallp(pr);
+      if (!L[p]) gel(L,p) = vecsmalltrunc_init(N+1);
+      vecsmalltrunc_append(gel(L,p), i);
+    }
+  }
+  return L;
+}
+
+/* recover FB, LV, iLP, KCZ from Vbase */
+static GEN
+recover_partFB(FB_t *F, GEN Vbase, long N)
+{
+  GEN FB, LV, iLP, L = get_pr_lists(Vbase, N, 0);
+  long l = lg(L), p, ip, i;
+
+  i = ip = 0;
+  FB = cgetg(l, t_VECSMALL);
+  iLP= cgetg(l, t_VECSMALL);
+  LV = cgetg(l, t_VEC);
+  for (p = 2; p < l; p++)
+  {
+    if (!L[p]) continue;
+    FB[++i] = p;
+    gel(LV,p) = vecpermute(Vbase, gel(L,p));
+    iLP[p]= ip; ip += lg(gel(L,p))-1;
+  }
+  F->KCZ = i;
+  F->KC = ip;
+  F->FB = FB; setlg(FB, i+1);
+  F->LV = (GEN*)LV;
+  F->iLP= iLP; return L;
+}
+
+/* add v^e to factorization */
+static void
+add_to_fact(long v, long e, FACT *fact)
+{
+  long i, l = fact[0].pr;
+  for (i=1; i<=l && fact[i].pr < v; i++)/*empty*/;
+  if (i <= l && fact[i].pr == v) fact[i].ex += e; else store(v, e, fact);
+}
+
+/* L (small) list of primes above the same p including pr. Return pr index */
+static int
+pr_index(GEN L, GEN pr)
+{
+  long j, l = lg(L);
+  GEN al = pr_get_gen(pr);
+  for (j=1; j<l; j++)
+    if (ZV_equal(al, pr_get_gen(gel(L,j)))) return j;
+  pari_err_BUG("codeprime");
+  return 0; /* not reached */
+}
+
+static long
+Vbase_to_FB(FB_t *F, GEN pr)
+{
+  long p = pr_get_smallp(pr);
+  return F->iLP[p] + pr_index(F->LV[p], pr);
+}
+
+/* return famat y (principal ideal) such that y / x is smooth [wrt Vbase] */
+static GEN
+SPLIT(FB_t *F, GEN nf, GEN x, GEN Vbase, FACT *fact)
+{
+  GEN vecG, z, ex, y, x0, Nx = ZM_det_triangular(x);
+  long nbtest_lim, nbtest, i, j, ru, lgsub;
+  pari_sp av;
+
+  if (nf_get_degree(nf) != lg(x)-1)
+    pari_err_TYPE("idealtyp [dimension != degree]", x);
+
+  /* try without reduction if x is small */
+  if (gexpo(gcoeff(x,1,1)) < 100 &&
+      can_factor(F, nf, x, NULL, Nx, fact)) return NULL;
+
+  av = avma;
+  y = idealpseudomin_nonscalar(x, nf_get_roundG(nf));
+  if (factorgen(F, nf, x, Nx, y, fact)) return y;
+  avma = av;
+
+  /* reduce in various directions */
+  ru = lg(nf_get_roots(nf));
+  vecG = cgetg(ru, t_VEC);
+  for (j=1; j<ru; j++)
+  {
+    gel(vecG,j) = nf_get_Gtwist1(nf, j);
+    av = avma;
+    y = idealpseudomin_nonscalar(x, gel(vecG,j));
+    if (factorgen(F, nf, x, Nx, y, fact)) return y;
+    avma = av;
+  }
+
+  /* tough case, multiply by random products */
+  lgsub = 3;
+  ex = cgetg(lgsub, t_VECSMALL);
+  z  = init_famat(NULL);
+  x0 = init_famat(x);
+  nbtest = 1; nbtest_lim = 4;
+  for(;;)
+  {
+    GEN I, NI, id = x0;
+    av = avma;
+    if (DEBUGLEVEL>2) err_printf("# ideals tried = %ld\n",nbtest);
+    for (i=1; i<lgsub; i++)
+    {
+      ex[i] = random_bits(RANDOM_BITS);
+      if (ex[i])
+      { /* avoid prec pb: don't let id become too large as lgsub increases */
+        if (id != x0) id = idealred(nf,id);
+        z[1] = Vbase[i];
+        id = extideal_HNF_mul(nf, id, idealpowred(nf,z,utoipos(ex[i])));
+      }
+    }
+    if (id == x0) continue;
+
+    I = gel(id,1); NI = ZM_det_triangular(I);
+    for (j=1; j<ru; j++)
+    {
+      pari_sp av2 = avma;
+      y = idealpseudomin_nonscalar(I, gel(vecG,j));
+      if (factorgen(F, nf, I, NI, y, fact))
+      {
+        for (i=1; i<lgsub; i++)
+          if (ex[i]) add_to_fact(Vbase_to_FB(F,gel(Vbase,i)), ex[i], fact);
+        return famat_mul(gel(id,2), y);
+      }
+      avma = av2;
+    }
+    avma = av;
+    if (++nbtest > nbtest_lim)
+    {
+      nbtest = 0;
+      if (++lgsub < 7)
+      {
+        nbtest_lim <<= 1;
+        ex = cgetg(lgsub, t_VECSMALL);
+      }
+      else nbtest_lim = LONG_MAX; /* don't increase further */
+      if (DEBUGLEVEL>2) err_printf("SPLIT: increasing factor base [%ld]\n",lgsub);
+    }
+  }
+}
+
+/* return principal y such that y / x is smooth. Store factorization of latter*/
+static GEN
+split_ideal(GEN nf, FB_t *F, GEN x, GEN Vbase, GEN L, FACT *fact)
+{
+  GEN y = SPLIT(F, nf, x, Vbase, fact);
+  long p,j, i, l = lg(F->FB);
+
+  p = j = 0; /* -Wall */
+  for (i=1; i<=fact[0].pr; i++)
+  { /* decode index C = ip+j --> (p,j) */
+    long q,k,t, C = fact[i].pr;
+    for (t=1; t<l; t++)
+    {
+      q = F->FB[t];
+      k = C - F->iLP[q];
+      if (k <= 0) break;
+      p = q;
+      j = k;
+    }
+    fact[i].pr = gel(L, p)[j];
+  }
+  return y;
+}
+
+/* return sorted vectbase [sorted in bnf since version 2.2.4] */
+static GEN
+get_Vbase(GEN bnf)
+{
+  GEN vectbase = gel(bnf,5), perm = gel(bnf,6), Vbase;
+  long i, l, tx = typ(perm);
+
+  if (tx == t_INT) return vectbase;
+  /* old format */
+  l = lg(vectbase); Vbase = cgetg(l,t_VEC);
+  for (i=1; i<l; i++) Vbase[i] = vectbase[itos(gel(perm,i))];
+  return Vbase;
+}
+
+/* all primes up to Minkowski bound factor on factorbase ? */
+void
+testprimes(GEN bnf, GEN BOUND)
+{
+  pari_sp av0 = avma, av;
+  ulong pmax, count = 0;
+  GEN Vbase, fb, p, nf = bnf_get_nf(bnf), dK = nf_get_disc(nf);
+  forprime_t S;
+  FACT *fact;
+  FB_t F;
+
+  if (DEBUGLEVEL)
+  {
+    err_printf("PHASE 1 [CLASS GROUP]: are all primes good ?\n");
+    err_printf("  Testing primes <= %Ps\n", BOUND); err_flush();
+  }
+  if (is_bigint(BOUND))
+    pari_warn(warner,"Zimmert's bound is large (%Ps), certification will take a long time", BOUND);
+  if (!is_pm1(nf_get_index(nf)))
+  {
+    GEN D = nf_get_diff(nf), L;
+    if (DEBUGLEVEL>1) err_printf("**** Testing Different = %Ps\n",D);
+    L = bnfisprincipal0(bnf, D, nf_FORCE);
+    if (DEBUGLEVEL>1) err_printf("     is %Ps\n", L);
+  }
+  /* sort factorbase for tablesearch */
+  fb = gen_sort(gel(bnf,5), (void*)&cmp_prime_ideal, cmp_nodata);
+  pmax = itou( pr_get_p(gel(fb, lg(fb)-1)) ); /* largest p in factorbase */
+  Vbase = get_Vbase(bnf);
+  (void)recover_partFB(&F, Vbase, nf_get_degree(nf));
+  fact = (FACT*)stack_malloc((F.KC+1)*sizeof(FACT));
+  forprime_init(&S, gen_2, BOUND);
+  av = avma;
+  while (( p = forprime_next(&S) ))
+  {
+    GEN vP;
+    long i, l;
+
+    if (DEBUGLEVEL == 1 && ++count > 1000)
+    {
+      err_printf("passing p = %Ps / %Ps\n", p, BOUND);
+      count = 0;
+    }
+
+    avma = av;
+    vP = idealprimedec(bnf, p);
+    l = lg(vP);
+    if (DEBUGLEVEL>1) err_printf("*** p = %Ps\n",p);
+    /* loop through all P | p if ramified, all but one otherwise */
+    if (!dvdii(dK,p)) l--;
+    for (i=1; i<l; i++)
+    {
+      GEN P = gel(vP,i);
+      long k;
+      if (DEBUGLEVEL>1) err_printf("  Testing P = %Ps\n",P);
+      if (cmpii(pr_norm(P), BOUND) >= 0)
+      {
+        if (DEBUGLEVEL>1) err_printf("    Norm(P) > Zimmert bound\n");
+        break;
+      }
+      if (cmpiu(p, pmax) <= 0 && (k = tablesearch(fb, P, &cmp_prime_ideal)))
+      { if (DEBUGLEVEL>1) err_printf("    #%ld in factor base\n",k); }
+      else if (DEBUGLEVEL>1)
+        err_printf("    is %Ps\n", isprincipal(bnf,P));
+      else /* faster: don't compute result */
+        (void)SPLIT(&F, nf, idealhnf_two(nf,P), Vbase, fact);
+    }
+  }
+  avma = av0;
+}
+
+/**** logarithmic embeddings ****/
+static GEN famat_to_arch(GEN nf, GEN fa, long prec);
+static GEN
+triv_arch(GEN nf) { return zerovec(lg(nf_get_roots(nf))-1); }
+
+/* Get archimedean components: [e_i Log( sigma_i(X) )], where X = primpart(x),
+ * and e_i = 1 (resp 2.) for i <= R1 (resp. > R1) */
+static GEN
+get_arch(GEN nf, GEN x, long prec)
+{
+  long i, l, R1;
+  GEN v;
+  if (typ(x) == t_MAT) return famat_to_arch(nf,x,prec);
+  x = nf_to_scalar_or_basis(nf,x);
+  if (typ(x) != t_COL) return triv_arch(nf);
+  x = RgM_RgC_mul(nf_get_M(nf), Q_primpart(x));
+  l = lg(x);
+  for (i=1; i < l; i++) if (gequal0(gabs(gel(x,i),prec))) return NULL;
+  v = cgetg(l,t_VEC); R1 = nf_get_r1(nf);
+  for (i=1; i<=R1; i++) gel(v,i) = glog(gel(x,i),prec);
+  for (   ; i < l; i++) gel(v,i) = gmul2n(glog(gel(x,i),prec),1);
+  return v;
+}
+static GEN
+famat_to_arch(GEN nf, GEN fa, long prec)
+{
+  GEN g,e, y = NULL;
+  long i,l;
+
+  if (typ(fa) != t_MAT) pari_err_TYPE("famat_to_arch",fa);
+  if (lg(fa) == 1) return triv_arch(nf);
+  g = gel(fa,1);
+  e = gel(fa,2); l = lg(e);
+  for (i=1; i<l; i++)
+  {
+    GEN t, x = nf_to_scalar_or_basis(nf, gel(g,i));
+    /* multiplicative arch would be better (save logs), but exponents overflow
+     * [ could keep track of expo separately, but not worth it ] */
+    t = get_arch(nf,x,prec); if (!t) return NULL;
+    if (gel(t,1) == gen_0) continue; /* rational */
+    t = RgV_Rg_mul(t, gel(e,i));
+    y = y? RgV_add(y,t): t;
+  }
+  return y ? y: triv_arch(nf);
+}
+
+static GEN
+famat_get_arch_real(GEN nf,GEN x,GEN *emb,long prec)
+{
+  GEN A, T, a, t, g = gel(x,1), e = gel(x,2);
+  long i, l = lg(e);
+
+  if (l <= 1)
+    return get_arch_real(nf, gen_1, emb, prec);
+  A = T = NULL; /* -Wall */
+  for (i=1; i<l; i++)
+  {
+    a = get_arch_real(nf, gel(g,i), &t, prec);
+    if (!a) return NULL;
+    a = RgC_Rg_mul(a, gel(e,i));
+    t = vecpow(t, gel(e,i));
+    if (i == 1) { A = a;          T = t; }
+    else        { A = gadd(A, a); T = vecmul(T, t); }
+  }
+  *emb = T; return A;
+}
+
+static GEN
+scalar_get_arch_real(GEN nf, GEN u, GEN *emb)
+{
+  GEN v, logu;
+  long i, s = signe(u), RU = lg(nf_get_roots(nf))-1, R1 = nf_get_r1(nf);
+
+  if (!s) pari_err_DOMAIN("get_arch_real","argument","=",gen_0,u);
+  v = cgetg(RU+1, t_COL);
+  logu = logr_abs(u);
+  for (i=1; i<=R1; i++) gel(v,i) = logu;
+  if (i <= RU)
+  {
+    GEN logu2 = shiftr(logu,1);
+    for (   ; i<=RU; i++) gel(v,i) = logu2;
+  }
+  *emb = const_col(RU, u); return v;
+}
+
+static int
+low_prec(GEN x) { return gequal0(x) || (typ(x) == t_REAL && realprec(x) <= LOWDEFAULTPREC); }
+
+/* For internal use. Get archimedean components: [e_i log( | sigma_i(x) | )],
+ * with e_i = 1 (resp 2.) for i <= R1 (resp. > R1)
+ * Return NULL if precision problem, and set *emb to the embeddings of x */
+GEN
+get_arch_real(GEN nf, GEN x, GEN *emb, long prec)
+{
+  long i, lx, R1;
+  GEN v, t;
+
+  if (typ(x) == t_MAT) return famat_get_arch_real(nf,x,emb,prec);
+  x = nf_to_scalar_or_basis(nf,x);
+  if (typ(x) != t_COL) return scalar_get_arch_real(nf, gtofp(x,prec), emb);
+  R1 = nf_get_r1(nf);
+  x = RgM_RgC_mul(nf_get_M(nf), x);
+  lx = lg(x);
+  v = cgetg(lx,t_COL);
+  for (i=1; i<=R1; i++)
+  {
+    t = gabs(gel(x,i),prec); if (low_prec(t)) return NULL;
+    gel(v,i) = glog(t,prec);
+  }
+  for (   ; i< lx; i++)
+  {
+    t = gnorm(gel(x,i)); if (low_prec(t)) return NULL;
+    gel(v,i) = glog(t,prec);
+  }
+  *emb = x; return v;
+}
+
+
+GEN
+init_red_mod_units(GEN bnf, long prec)
+{
+  GEN s = gen_0, p1,s1,mat, logfu = bnf_get_logfu(bnf);
+  long i,j, RU = lg(logfu);
+
+  if (RU == 1) return NULL;
+  mat = cgetg(RU,t_MAT);
+  for (j=1; j<RU; j++)
+  {
+    p1 = cgetg(RU+1,t_COL); gel(mat,j) = p1;
+    s1 = gen_0;
+    for (i=1; i<RU; i++)
+    {
+      gel(p1,i) = real_i(gcoeff(logfu,i,j));
+      s1 = mpadd(s1, mpsqr(gel(p1,i)));
+    }
+    gel(p1,RU) = gen_0; if (mpcmp(s1,s) > 0) s = s1;
+  }
+  s = gsqrt(gmul2n(s,RU),prec);
+  if (expo(s) < 27) s = utoipos(1UL << 27);
+  return mkvec2(mat, s);
+}
+
+/* z computed above. Return unit exponents that would reduce col (arch) */
+GEN
+red_mod_units(GEN col, GEN z)
+{
+  long i,RU;
+  GEN x,mat,N2;
+
+  if (!z) return NULL;
+  mat= gel(z,1);
+  N2 = gel(z,2);
+  RU = lg(mat); x = cgetg(RU+1,t_COL);
+  for (i=1; i<RU; i++) gel(x,i) = real_i(gel(col,i));
+  gel(x,RU) = N2;
+  x = lll(shallowconcat(mat,x));
+  if (typ(x) != t_MAT) return NULL;
+  x = gel(x,RU);
+  if (signe(gel(x,RU)) < 0) x = gneg_i(x);
+  if (!gequal1(gel(x,RU))) pari_err_BUG("red_mod_units");
+  setlg(x,RU); return x;
+}
+
+/* [x] archimedian components, A column vector. return [x] A
+ * x may be a translated GEN (y + k) */
+static GEN
+act_arch(GEN A, GEN x)
+{
+  GEN a;
+  long i,l = lg(A), tA = typ(A);
+  if (tA == t_MAT)
+  { /* assume lg(x) >= l */
+    a = cgetg(l, t_VEC);
+    for (i=1; i<l; i++) gel(a,i) = act_arch(gel(A,i), x);
+    return a;
+  }
+  if (l==1) return cgetg(1, t_VEC);
+  if (tA == t_VECSMALL)
+  {
+    a = gmulsg(A[1], gel(x,1));
+    for (i=2; i<l; i++)
+      if (A[i]) a = gadd(a, gmulsg(A[i], gel(x,i)));
+  }
+  else
+  { /* A a t_COL of t_INT. Assume lg(A)==lg(x) */
+    a = gmul(gel(A,1), gel(x,1));
+    for (i=2; i<l; i++)
+      if (signe(gel(A,i))) a = gadd(a, gmul(gel(A,i), gel(x,i)));
+  }
+  settyp(a, t_VEC); return a;
+}
+
+static long
+prec_arch(GEN bnf)
+{
+  GEN a = gel(bnf,4);
+  long i, l = lg(a), prec;
+
+  for (i=1; i<l; i++)
+    if ( (prec = gprecision(gel(a,i))) ) return prec;
+  return DEFAULTPREC;
+}
+
+static long
+needed_bitprec(GEN x)
+{
+  long i, e = 0, l = lg(x);
+  for (i = 1; i < l; i++)
+  {
+    GEN c = gel(x,i);
+    long f = gexpo(c) - prec2nbits(gprecision(c));
+    if (f > e) e = f;
+  }
+  return e;
+}
+
+/* col = archimedian components of x, Nx = kNx^e its norm (e > 0, usually = 1),
+ * dx a bound for its denominator. Return x or NULL (fail) */
+GEN
+isprincipalarch(GEN bnf, GEN col, GEN kNx, GEN e, GEN dx, long *pe)
+{
+  GEN nf, x, y, logfu, s, M;
+  long N, R1, RU, i, prec = gprecision(col);
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf); M = nf_get_M(nf);
+  if (!prec) prec = prec_arch(bnf);
+  logfu = bnf_get_logfu(bnf);
+  N = nf_get_degree(nf);
+  R1 = nf_get_r1(nf);
+  RU = (N + R1)>>1;
+  col = cleanarch(col,N,prec); settyp(col, t_COL);
+  if (!col) pari_err_PREC( "isprincipalarch");
+  if (RU > 1)
+  { /* reduce mod units */
+    GEN u, z = init_red_mod_units(bnf,prec);
+    u = red_mod_units(col,z);
+    if (!u && z) return NULL;
+    if (u) col = RgC_add(col, RgM_RgC_mul(logfu, u));
+  }
+  s = divru(mulir(e, glog(kNx,prec)), N);
+  for (i=1; i<=R1; i++) gel(col,i) = gexp(gadd(s, gel(col,i)),prec);
+  for (   ; i<=RU; i++) gel(col,i) = gexp(gadd(s, gmul2n(gel(col,i),-1)),prec);
+  /* d.alpha such that x = alpha \prod gj^ej */
+  x = RgM_solve_realimag(M,col); if (!x) return NULL;
+  x = RgC_Rg_mul(x, dx);
+  y = grndtoi(x, pe);
+  if (*pe > -5)
+  {
+    *pe = needed_bitprec(x);
+    return NULL;
+  }
+  return RgC_Rg_div(y, dx);
+}
+
+/* y = C \prod g[i]^e[i] ? */
+static int
+fact_ok(GEN nf, GEN y, GEN C, GEN g, GEN e)
+{
+  pari_sp av = avma;
+  long i, c = lg(e);
+  GEN z = C? C: gen_1;
+  for (i=1; i<c; i++)
+    if (signe(gel(e,i))) z = idealmul(nf, z, idealpow(nf, gel(g,i), gel(e,i)));
+  if (typ(z) != t_MAT) z = idealhnf_shallow(nf,z);
+  if (typ(y) != t_MAT) y = idealhnf_shallow(nf,y);
+  i = ZM_equal(y, z); avma = av; return i;
+}
+
+/* assume x in HNF. cf class_group_gen for notations.
+ * Return NULL iff flag & nf_FORCE and computation of principal ideal generator
+ * fails */
+static GEN
+isprincipalall(GEN bnf, GEN x, long *ptprec, long flag)
+{
+  long i,nW,nB,e,c, prec = *ptprec;
+  GEN Q,xar,Wex,Bex,U,p1,gen,cyc,xc,ex,d,col,A;
+  GEN W    = gel(bnf,1);
+  GEN B    = gel(bnf,2);
+  GEN WB_C = gel(bnf,4);
+  GEN nf   = bnf_get_nf(bnf);
+  GEN clg2 = gel(bnf,9);
+  FB_t F;
+  GEN Vbase = get_Vbase(bnf);
+  GEN L = recover_partFB(&F, Vbase, lg(x)-1);
+  pari_sp av;
+  FACT *fact;
+
+  U = gel(clg2,1);
+  cyc = bnf_get_cyc(bnf); c = lg(cyc)-1;
+  gen = bnf_get_gen(bnf);
+  ex = cgetg(c+1,t_COL);
+  if (c == 0 && !(flag & (nf_GEN|nf_GENMAT|nf_GEN_IF_PRINCIPAL))) return ex;
+
+  /* factor x */
+  x = Q_primitive_part(x, &xc);
+  av = avma;
+
+  fact = (FACT*)stack_malloc((F.KC+1)*sizeof(FACT));
+  xar = split_ideal(nf, &F, x, Vbase, L, fact);
+  nW = lg(W)-1; Wex = zero_zv(nW);
+  nB = lg(B)-1; Bex = zero_zv(nB);
+  for (i=1; i<=fact[0].pr; i++)
+  {
+    long k = fact[i].pr;
+    long l = k - nW;
+    if (l <= 0) Wex[k] = fact[i].ex;
+    else        Bex[l] = fact[i].ex;
+  }
+
+  /* x = -g_W Wex - g_B Bex + [xar]  | x = g_W Wex + g_B Bex if xar = NULL
+   *   = g_W A + [xar] - [C_B]Bex    |   = g_W A + [C_B]Bex
+   * since g_W B + g_B = [C_B] */
+  if (xar)
+  {
+    if (!nB) /*treat specially B = matrix(n,0): PARI can't represent it*/
+      A = zc_to_ZC(zv_neg(Wex));
+    else
+    {
+      A = ZC_sub(ZM_zc_mul(B,Bex), zc_to_ZC(Wex));
+      Bex = zv_neg(Bex);
+    }
+  }
+  else
+  {
+    if (!nB)
+      A = zc_to_ZC(Wex);
+    else
+      A = ZC_sub(zc_to_ZC(Wex), ZM_zc_mul(B,Bex));
+  }
+  Q = ZM_ZC_mul(U, A);
+  for (i=1; i<=c; i++)
+    gel(Q,i) = truedvmdii(gel(Q,i), gel(cyc,i), (GEN*)(ex+i));
+  if ((flag & nf_GEN_IF_PRINCIPAL))
+    { if (!ZV_equal0(ex)) return gen_0; }
+  else if (!(flag & (nf_GEN|nf_GENMAT)))
+    return ZC_copy(ex);
+
+  /* compute arch component of the missing principal ideal */
+  { /* g A = G Ur A + [ga]A, Ur A = D Q + R as above (R = ex)
+           = G R + [GD]Q + [ga]A */
+    GEN ga = gel(clg2,2), GD = gel(clg2,3);
+    if (nB) col = act_arch(Bex, WB_C + nW); else col = triv_arch(nf);
+    if (nW) col = gadd(col, act_arch(A, ga));
+    if (c)  col = gadd(col, act_arch(Q, GD));
+  }
+  if (xar)
+  {
+    GEN t = get_arch(nf, xar, prec);
+    col = t? gadd(col, t):NULL;
+  }
+
+  /* find coords on Zk; Q = N (x / \prod gj^ej) = N(alpha), denom(alpha) | d */
+  Q = gdiv(ZM_det_triangular(x), get_norm_fact(gen, ex, &d));
+  col = col?isprincipalarch(bnf, col, Q, gen_1, d, &e):NULL;
+  if (col && !fact_ok(nf,x, col,gen,ex)) col = NULL;
+  if (!col && !ZV_equal0(ex))
+  {
+    /* in case isprincipalfact calls bnfinit() due to prec trouble...*/
+    ex = gerepilecopy(av, ex);
+    p1 = isprincipalfact(bnf, x, gen, ZC_neg(ex), flag);
+    if (typ(p1) != t_VEC) return p1;
+    col = gel(p1,2);
+  }
+  if (col)
+  { /* add back missing content */
+    if (xc) col = (typ(col)==t_MAT)? famat_mul(col,xc): RgC_Rg_mul(col,xc);
+  }
+  else
+  {
+    if (e < 0) e = 0;
+    *ptprec = prec + nbits2extraprec(e + 128);
+    if (flag & nf_FORCE)
+    {
+      if (DEBUGLEVEL) pari_warn(warner,"precision too low for generators, e = %ld",e);
+      return NULL;
+    }
+    pari_warn(warner,"precision too low for generators, not given");
+    col = cgetg(1, t_COL);
+  }
+  return (flag & nf_GEN_IF_PRINCIPAL)? col: mkvec2(ex, col);
+}
+
+static GEN
+triv_gen(GEN bnf, GEN x, long flag)
+{
+  GEN y, nf = bnf_get_nf(bnf);
+  long c;
+  if (flag & nf_GEN_IF_PRINCIPAL) return algtobasis(nf,x);
+  c = lg(bnf_get_cyc(bnf)) - 1;
+  if (!(flag & (nf_GEN|nf_GENMAT))) return zerocol(c);
+  y = cgetg(3,t_VEC);
+  gel(y,1) = zerocol(c);
+  gel(y,2) = algtobasis(nf,x); return y;
+}
+
+GEN
+bnfisprincipal0(GEN bnf,GEN x,long flag)
+{
+  GEN arch, c;
+  long pr;
+  pari_sp av = avma;
+
+  bnf = checkbnf(bnf);
+  switch( idealtyp(&x, &arch) )
+  {
+    case id_PRINCIPAL:
+      if (gequal0(x)) pari_err_DOMAIN("bnfisprincipal","ideal","=",gen_0,x);
+      return triv_gen(bnf, x, flag);
+    case id_PRIME:
+      if (pr_is_inert(x))
+        return gerepileupto(av, triv_gen(bnf, gel(x,1), flag));
+      x = idealhnf_two(bnf_get_nf(bnf), x);
+      break;
+    case id_MAT:
+      if (lg(x)==1) pari_err_DOMAIN("bnfisprincipal","ideal","=",gen_0,x);
+  }
+  pr = prec_arch(bnf); /* precision of unit matrix */
+  c = getrand();
+  for (;;)
+  {
+    pari_sp av1 = avma;
+    GEN y = isprincipalall(bnf,x,&pr,flag);
+    if (y) return gerepilecopy(av, y);
+
+    if (DEBUGLEVEL) pari_warn(warnprec,"isprincipal",pr);
+    avma = av1; bnf = bnfnewprec_shallow(bnf,pr); setrand(c);
+  }
+}
+GEN
+isprincipal(GEN bnf,GEN x) { return bnfisprincipal0(bnf,x,0); }
+
+/* FIXME: OBSOLETE */
+GEN
+isprincipalgen(GEN bnf,GEN x)
+{ return bnfisprincipal0(bnf,x,nf_GEN); }
+GEN
+isprincipalforce(GEN bnf,GEN x)
+{ return bnfisprincipal0(bnf,x,nf_FORCE); }
+GEN
+isprincipalgenforce(GEN bnf,GEN x)
+{ return bnfisprincipal0(bnf,x,nf_GEN | nf_FORCE); }
+
+static GEN
+add_principal_part(GEN nf, GEN u, GEN v, long flag)
+{
+  if (flag & nf_GENMAT)
+    return (typ(u) == t_COL && RgV_isscalar(u) && gequal1(gel(u,1)))? v: famat_mul(v,u);
+  else
+    return nfmul(nf, v, u);
+}
+
+#if 0
+/* compute C prod P[i]^e[i],  e[i] >=0 for all i. C may be NULL (omitted)
+ * e destroyed ! */
+static GEN
+expand(GEN nf, GEN C, GEN P, GEN e)
+{
+  long i, l = lg(e), done = 1;
+  GEN id = C;
+  for (i=1; i<l; i++)
+  {
+    GEN ei = gel(e,i);
+    if (signe(ei))
+    {
+      if (mod2(ei)) id = id? idealmul(nf, id, gel(P,i)): gel(P,i);
+      ei = shifti(ei,-1);
+      if (signe(ei)) done = 0;
+      gel(e,i) = ei;
+    }
+  }
+  if (id != C) id = idealred(nf, id);
+  if (done) return id;
+  return idealmulred(nf, id, idealsqr(nf, expand(nf,id,P,e)));
+}
+/* C is an extended ideal, possibly with C[1] = NULL */
+static GEN
+expandext(GEN nf, GEN C, GEN P, GEN e)
+{
+  long i, l = lg(e), done = 1;
+  GEN A = gel(C,1);
+  for (i=1; i<l; i++)
+  {
+    GEN ei = gel(e,i);
+    if (signe(ei))
+    {
+      if (mod2(ei)) A = A? idealmul(nf, A, gel(P,i)): gel(P,i);
+      ei = shifti(ei,-1);
+      if (signe(ei)) done = 0;
+      gel(e,i) = ei;
+    }
+  }
+  if (A == gel(C,1))
+    A = C;
+  else
+    A = idealred(nf, mkvec2(A, gel(C,2)));
+  if (done) return A;
+  return idealmulred(nf, A, idealsqr(nf, expand(nf,A,P,e)));
+}
+#endif
+
+static GEN
+expand(GEN nf, GEN C, GEN P, GEN e)
+{
+  long i, l = lg(e);
+  GEN B, A = C;
+  for (i=1; i<l; i++) /* compute prod P[i]^e[i] */
+    if (signe(gel(e,i)))
+    {
+      B = idealpowred(nf, gel(P,i), gel(e,i));
+      A = A? idealmulred(nf,A,B): B;
+    }
+  return A;
+}
+static GEN
+expandext(GEN nf, GEN C, GEN P, GEN e)
+{
+  long i, l = lg(e);
+  GEN B, A = gel(C,1), C1 = A;
+  for (i=1; i<l; i++) /* compute prod P[i]^e[i] */
+    if (signe(gel(e,i)))
+    {
+      gel(C,1) = gel(P,i);
+      B = idealpowred(nf, C, gel(e,i));
+      A = A? idealmulred(nf,A,B): B;
+    }
+  return A == C1? C: A;
+}
+
+/* isprincipal for C * \prod P[i]^e[i] (C omitted if NULL) */
+GEN
+isprincipalfact(GEN bnf, GEN C, GEN P, GEN e, long flag)
+{
+  const long gen = flag & (nf_GEN|nf_GENMAT|nf_GEN_IF_PRINCIPAL);
+  long prec;
+  pari_sp av = avma;
+  GEN C0, Cext, c, id, nf = checknf(bnf);
+
+  if (gen)
+  {
+    Cext = (flag & nf_GENMAT)? cgetg(1, t_MAT): mkpolmod(gen_1,nf_get_pol(nf));
+    C0 = mkvec2(C, Cext);
+    id = expandext(nf, C0, P, e);
+  } else {
+    Cext = NULL;
+    C0 = C;
+    id = expand(nf, C, P, e);
+  }
+  if (id == C0) /* e = 0 */
+  {
+    if (!C) return bnfisprincipal0(bnf, gen_1, flag);
+    C = idealhnf_shallow(nf,C);
+  }
+  else
+  {
+    if (gen) { C = gel(id,1); Cext = gel(id,2); } else C = id;
+  }
+  prec = prec_arch(bnf);
+  c = getrand();
+  for (;;)
+  {
+    pari_sp av1 = avma;
+    GEN y = isprincipalall(bnf, C, &prec, flag);
+    if (y)
+    {
+      if (flag & nf_GEN_IF_PRINCIPAL)
+      {
+        if (typ(y) == t_INT) { avma = av; return NULL; }
+        y = add_principal_part(nf, y, Cext, flag);
+      }
+      else
+      {
+        GEN u = gel(y,2);
+        if (!gen || typ(y) != t_VEC) return gerepileupto(av,y);
+        if (lg(u) != 1) gel(y,2) = add_principal_part(nf, u, Cext, flag);
+      }
+      return gerepilecopy(av, y);
+    }
+    if (DEBUGLEVEL) pari_warn(warnprec,"isprincipal",prec);
+    avma = av1; bnf = bnfnewprec_shallow(bnf,prec); setrand(c);
+  }
+}
+GEN
+isprincipalfact_or_fail(GEN bnf, GEN C, GEN P, GEN e)
+{
+  const long flag = nf_GENMAT|nf_FORCE;
+  long prec;
+  pari_sp av = avma;
+  GEN u, y, id, C0, Cext, nf = bnf_get_nf(bnf);
+
+  Cext = cgetg(1, t_MAT);
+  C0 = mkvec2(C, Cext);
+  id = expandext(nf, C0, P, e);
+  if (id == C0) /* e = 0 */
+    C = idealhnf_shallow(nf,C);
+  else {
+    C = gel(id,1); Cext = gel(id,2);
+  }
+  prec = prec_arch(bnf);
+  y = isprincipalall(bnf, C, &prec, flag);
+  if (!y) { avma = av; return utoipos(prec); }
+  u = gel(y,2);
+  if (lg(u) != 1) gel(y,2) = add_principal_part(nf, u, Cext, flag);
+  return gerepilecopy(av, y);
+}
+
+/* if x a famat, assume it is an algebraic integer (very costly to check) */
+GEN
+bnfisunit(GEN bnf,GEN x)
+{
+  long tx = typ(x), i, R1, RU, e, n, prec;
+  pari_sp av = avma;
+  GEN p1, v, rlog, logunit, ex, nf, pi2_sur_w, emb;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  logunit = bnf_get_logfu(bnf); RU = lg(logunit);
+  n = bnf_get_tuN(bnf); /* # { roots of 1 } */
+  if (tx == t_MAT)
+  { /* famat, assumed integral */
+    if (lg(x) != 3) pari_err_TYPE("bnfisunit [not a factorization]", x);
+  } else {
+    x = nf_to_scalar_or_basis(nf,x);
+    if (typ(x) != t_COL)
+    { /* rational unit ? */
+      long s;
+      if (typ(x) != t_INT || !is_pm1(x)) return cgetg(1,t_COL);
+      s = signe(x); avma = av; v = zerocol(RU);
+      gel(v,RU) = mkintmodu((s > 0)? 0: n>>1, n);
+      return v;
+    }
+    if (!isint1(Q_denom(x))) { avma = av; return cgetg(1,t_COL); }
+  }
+
+  R1 = nf_get_r1(nf); v = cgetg(RU+1,t_COL);
+  for (i=1; i<=R1; i++) gel(v,i) = gen_1;
+  for (   ; i<=RU; i++) gel(v,i) = gen_2;
+  logunit = shallowconcat(logunit, v);
+  /* ex = fundamental units exponents */
+  rlog = real_i(logunit);
+  prec = nf_get_prec(nf);
+  for (i=1;; i++)
+  {
+    GEN rx = get_arch_real(nf,x,&emb, MEDDEFAULTPREC);
+    if (rx)
+    {
+      GEN logN = RgV_sum(rx); /* log(Nx), should be ~ 0 */
+      if (gexpo(logN) > -20)
+      { /* precision problem ? */
+        if (typ(logN) != t_REAL) { avma = av; return cgetg(1,t_COL); } /*no*/
+        if (i == 1)
+        {
+          GEN N = nfnorm(nf, x);
+          if (!is_pm1(N)) { avma = av; return cgetg(1, t_COL); }
+        }
+      }
+      else
+      {
+        ex = RgM_solve(rlog, rx);
+        if (ex)
+        {
+          ex = grndtoi(ex, &e);
+          if (!signe(gel(ex,RU)) && e < -4) break;
+        }
+      }
+    }
+    if (i == 1)
+      prec = nbits2prec(gexpo(x) + 128);
+    else
+    {
+      if (i > 4) pari_err_PREC("bnfisunit");
+      prec = precdbl(prec);
+    }
+    if (DEBUGLEVEL) pari_warn(warnprec,"bnfisunit",prec);
+    nf = nfnewprec_shallow(nf, prec);
+  }
+
+  setlg(ex, RU); /* ZC */
+  p1 = imag_i( row_i(logunit,1, 1,RU-1) );
+  p1 = RgV_dotproduct(p1, ex); if (!R1) p1 = gmul2n(p1, -1);
+  p1 = gsub(garg(gel(emb,1),prec), p1);
+  /* p1 = arg(the missing root of 1) */
+
+  pi2_sur_w = divru(mppi(prec), n>>1); /* 2pi / n */
+  e = umodiu(roundr(divrr(p1, pi2_sur_w)), n);
+  if (n > 2)
+  {
+    GEN z = algtobasis(nf, bnf_get_tuU(bnf)); /* primitive root of 1 */
+    GEN ro = RgV_dotproduct(row(nf_get_M(nf), 1), z);
+    GEN p2 = roundr(divrr(garg(ro, prec), pi2_sur_w));
+    e *= Fl_inv(umodiu(p2,n), n);
+    e %= n;
+  }
+
+  gel(ex,RU) = mkintmodu(e, n);
+  setlg(ex, RU+1); return gerepilecopy(av, ex);
+}
+
+GEN
+nfsign_from_logarch(GEN LA, GEN invpi, GEN archp)
+{
+  long l = lg(archp), i;
+  GEN y = cgetg(l, t_VECSMALL);
+  pari_sp av = avma;
+
+  for (i=1; i<l; i++)
+  {
+    GEN c = ground( gmul(imag_i(gel(LA,archp[i])), invpi) );
+    y[i] = mpodd(c)? 1: 0;
+  }
+  avma = av; return y;
+}
+
+GEN
+nfsign_units(GEN bnf, GEN archp, int add_zu)
+{
+  GEN invpi, y, A = bnf_get_logfu(bnf), nf = bnf_get_nf(bnf);
+  long j = 1, RU = lg(A);
+
+  invpi = invr( mppi(nf_get_prec(nf)) );
+  if (!archp) archp = identity_perm( nf_get_r1(nf) );
+  if (add_zu) { RU++; A--; }
+  y = cgetg(RU,t_MAT);
+  if (add_zu)
+  {
+    long w = bnf_get_tuN(bnf);
+    gel(y, j++) = (w == 2)? const_vecsmall(lg(archp)-1, 1)
+                          : cgetg(1, t_VECSMALL);
+  }
+  for ( ; j < RU; j++) gel(y,j) = nfsign_from_logarch(gel(A,j), invpi, archp);
+  return y;
+}
+
+/* obsolete */
+GEN
+signunits(GEN bnf)
+{
+  pari_sp av;
+  GEN S, y, nf;
+  long i, j, r1, r2;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  nf_get_sign(nf, &r1,&r2);
+  S = zeromatcopy(r1, r1+r2-1); av = avma;
+  y = nfsign_units(bnf, NULL, 0);
+  for (j = 1; j < lg(y); j++)
+  {
+    GEN Sj = gel(S,j), yj = gel(y,j);
+    for (i = 1; i <= r1; i++) gel(Sj,i) = yj[i]? gen_m1: gen_1;
+  }
+  avma = av; return S;
+}
+
+static GEN
+get_log_embed(REL_t *rel, GEN M, long RU, long R1, long prec)
+{
+  GEN arch, C, z = rel->m;
+  long i;
+  if (!z) return zerocol(RU);
+  arch = typ(z) == t_COL? RgM_RgC_mul(M, z): RgC_Rg_mul(gel(M,1), z);
+  C = cgetg(RU+1, t_COL); arch = glog(arch, prec);
+  for (i=1; i<=R1; i++) gel(C,i) = gel(arch,i);
+  for (   ; i<=RU; i++) gel(C,i) = gmul2n(gel(arch,i), 1);
+  return C;
+}
+
+static GEN
+perm_log_embed(GEN C, GEN perm)
+{
+  long i, n;
+  GEN Cnew = cgetg_copy(C, &n);
+  for (i = 1; i < n; i++)
+  {
+    long v = perm[i];
+    if (v > 0)
+      gel(Cnew, i) = gel(C, v);
+    else
+      gel(Cnew, i) = gconj(gel(C, -v));
+  }
+  return Cnew;
+}
+
+static GEN
+set_fact(FB_t *F, FACT *fact, GEN ex, long *pnz)
+{
+  long i, n = fact[0].pr;
+  long nz;
+  GEN c = zero_Flv(F->KC);
+  if (!n) /* trivial factorization */
+    *pnz = F->KC;
+  else {
+    nz = fact[1].pr;
+    if (fact[n].pr < nz) /* Possible with jid in rnd_rel */
+      nz = fact[n].pr;
+    for (i=1; i<=n; i++) c[fact[i].pr] = fact[i].ex;
+    if (ex)
+    {
+      for (i=1; i<lg(ex); i++)
+        if (ex[i]) {
+          long v = F->subFB[i];
+          c[v] += ex[i];
+          if (v < nz) nz = v;
+        }
+    }
+    *pnz = nz;
+  }
+  return c;
+}
+
+/* Is cols already in the cache ? bs = index of first non zero coeff in cols
+ * General check for colinearity useless since exceedingly rare */
+static int
+already_known(RELCACHE_t *cache, long bs, GEN cols)
+{
+  REL_t *r;
+  long l = lg(cols);
+  for (r = cache->last; r > cache->base; r--)
+    if (bs == r->nz)
+    {
+      GEN coll = r->R;
+      long b = bs;
+      while (b < l && cols[b] == coll[b]) b++;
+      if (b == l) return 1;
+    }
+  return 0;
+}
+
+/* Add relation R to cache, nz = index of first non zero coeff in R.
+ * If relation is a linear combination of the previous ones, return 0.
+ * Otherwise, update basis and return > 0. Compute mod p (much faster)
+ * so some kernel vector might not be genuine. */
+static int
+add_rel_i(RELCACHE_t *cache, GEN R, long nz, GEN m, long orig, long aut, REL_t **relp, long in_rnd_rel)
+{
+  long i, k, n = lg(R)-1;
+
+  if (already_known(cache, nz, R)) return -1;
+  if (cache->last >= cache->base + cache->len) return 0;
+  if (DEBUGLEVEL>6)
+  {
+    err_printf("adding vector = %Ps\n",R);
+    err_printf("generators =\n%Ps\n", cache->basis);
+  }
+  if (cache->missing)
+  {
+    GEN a = leafcopy(R), basis = cache->basis;
+    k = lg(a);
+    do --k; while (!a[k]);
+    while (k)
+    {
+      GEN c = gel(basis, k);
+      if (c[k])
+      {
+        long ak = a[k];
+        for (i=1; i < k; i++) if (c[i]) a[i] = (a[i] + ak*(mod_p-c[i])) % mod_p;
+        a[k] = 0;
+        do --k; while (!a[k]); /* k cannot go below 0: codeword is a sentinel */
+      }
+      else
+      {
+        ulong invak = Fl_inv((ulong)a[k], mod_p);
+        /* Cleanup a */
+        for (i = k; i-- > 1; )
+        {
+          long j, ai = a[i];
+          c = gel(basis, i);
+          if (!ai || !c[i]) continue;
+          ai = mod_p-ai;
+          for (j = 1; j < i; j++) if (c[j]) a[j] = (a[j] + ai*c[j]) % mod_p;
+          a[i] = 0;
+        }
+        /* Insert a/a[k] as k-th column */
+        c = gel(basis, k);
+        for (i = 1; i<k; i++) if (a[i]) c[i] = (a[i] * invak) % mod_p;
+        c[k] = 1; a = c;
+        /* Cleanup above k */
+        for (i = k+1; i<n; i++)
+        {
+          long j, ck;
+          c = gel(basis, i);
+          ck = c[k];
+          if (!ck) continue;
+          ck = mod_p-ck;
+          for (j = 1; j < k; j++) if (a[j]) c[j] = (c[j] + ck*a[j]) % mod_p;
+          c[k] = 0;
+        }
+        cache->missing--;
+        break;
+      }
+    }
+  }
+  else
+    k = (cache->last - cache->base) + 1;
+  if (k || cache->relsup > 0 || (m && in_rnd_rel))
+  {
+    REL_t *rel;
+
+    rel = ++cache->last;
+    if (!k && cache->relsup)
+    {
+      cache->relsup--;
+      k = (rel - cache->base) + cache->missing;
+    }
+    rel->R  = gclone(R);
+    rel->m  =  m ? gclone(m) : NULL;
+    rel->nz = nz;
+    if (aut)
+    {
+      rel->relorig = (rel - cache->base) - orig;
+      rel->relaut = aut;
+    }
+    else
+      rel->relaut = 0;
+    if (relp) *relp = rel;
+    if (DEBUGLEVEL) dbg_newrel(cache);
+  }
+  return k;
+}
+
+static int
+add_rel(RELCACHE_t *cache, FB_t *F, GEN R, long nz, GEN m, long in_rnd_rel)
+{
+  REL_t *rel;
+  long k, l, reln;
+  const long nauts = lg(F->idealperm), KC = F->KC;
+
+  k = add_rel_i(cache, R, nz, m, 0, 0, &rel, in_rnd_rel);
+  if (k > 0 && m)
+  {
+    GEN Rl = cgetg(KC+1, t_VECSMALL);
+    reln = rel - cache->base;
+    for (l = 1; l < nauts; l++)
+    {
+      GEN perml = gel(F->idealperm, l);
+      long i, nzl = perml[nz];
+
+      for (i = 1; i <= KC; i++) Rl[i] = 0;
+      for (i = nz; i <= KC; i++)
+        if (R[i])
+        {
+          long v = perml[i];
+
+          if (v < nzl) nzl = v;
+          Rl[v] = R[i];
+        }
+      (void)add_rel_i(cache, Rl, nzl, NULL, reln, l, NULL, in_rnd_rel);
+    }
+  }
+  return k;
+}
+
+/* Compute powers of prime ideal (P^0,...,P^a) (a > 1) */
+static void
+powPgen(GEN nf, GEN vp, GEN *ppowP, long a)
+{
+  GEN id2, J;
+  long j;
+
+  id2 = cgetg(a+1,t_VEC);
+  J = mkvec2(pr_get_p(vp), zk_scalar_or_multable(nf,pr_get_gen(vp)));
+  gel(id2,1) = J;
+  vp = idealhnf_two(nf,vp);
+  for (j=2; j<=a; j++)
+  {
+    if (DEBUGLEVEL>1) err_printf(" %ld", j);
+    J = idealtwoelt(nf, idealmul_HNF(nf, vp, J));
+    gel(J, 2) = zk_scalar_or_multable(nf, gel(J,2));
+    gel(id2,j) = J;
+  }
+  setlg(id2, j);
+  *ppowP = id2;
+  if (DEBUGLEVEL>1) err_printf("\n");
+}
+
+
+/* Compute powers of prime ideals (P^0,...,P^a) in subFB (a > 1) */
+static void
+powFBgen(RELCACHE_t *cache, FB_t *F, GEN nf, GEN auts)
+{
+  const long a = 1L<<RANDOM_BITS;
+  pari_sp av = avma;
+  GEN subFB = F->subFB, idealperm = F->idealperm;
+  long i, k, l, id, n = lg(F->subFB), naut = lg(auts);
+
+  if (DEBUGLEVEL) err_printf("Computing powers for subFB: %Ps\n",subFB);
+  if (cache) pre_allocate(cache, n*naut);
+  for (i=1; i<n; i++)
+  {
+    id = subFB[i];
+    if (gel(F->id2, id) == gen_0)
+    {
+      GEN id2 = NULL;
+
+      for (k = 1; k < naut; k++)
+      {
+        long sigmaid = coeff(idealperm, id, k);
+        GEN sigmaid2 = gel(F->id2, sigmaid);
+        if (sigmaid2 != gen_0)
+        {
+          GEN aut = gel(auts, k), invaut = gel(auts, F->invs[k]);
+          long lid2;
+          id2 = cgetg_copy(sigmaid2, &lid2);
+          if (DEBUGLEVEL>1) err_printf("%ld: automorphism(%ld)\n", id,sigmaid);
+          for (l = 1; l < lid2; l++)
+          {
+            GEN id2l = gel(sigmaid2, l);
+            gel(id2, l) =
+              mkvec2(gel(id2l, 1), ZM_mul(ZM_mul(invaut, gel(id2l, 2)), aut));
+          }
+          break;
+        }
+      }
+      if (!id2)
+      {
+        if (DEBUGLEVEL>1) err_printf("%ld: 1", id);
+        powPgen(nf, gel(F->LP, id), &id2, a);
+      }
+      gel(F->id2, id) = gclone(id2);
+      avma = av;
+    }
+  }
+  F->sfb_chg = 0;
+  F->newpow = 0;
+}
+
+INLINE void
+step(GEN x, double *y, GEN inc, long k)
+{
+  if (!y[k])
+    x[k]++; /* leading coeff > 0 */
+  else
+  {
+    long i = inc[k];
+    x[k] += i;
+    inc[k] = (i > 0)? -1-i: 1-i;
+  }
+}
+
+INLINE long
+Fincke_Pohst_ideal(RELCACHE_t *cache, FB_t *F, GEN nf, GEN M,
+    GEN G, GEN ideal0, FACT *fact, long nbrelpid, FP_t *fp,
+    RNDREL_t *rr, long prec, long *nbsmallnorm, long *nbfact)
+{
+  pari_sp av;
+  const long N = nf_get_degree(nf), R1 = nf_get_r1(nf);
+  GEN r, u, gx, inc=const_vecsmall(N, 1), ideal;
+  GEN Nideal = nbrelpid ? NULL : idealnorm(nf, ideal0);
+  double BOUND;
+  long j, k, skipfirst, nbrelideal=0, dependent=0, try_elt=0,  try_factor=0;
+
+  u = ZM_lll(ZM_mul(F->G0, ideal0), 0.99, LLL_IM);
+  ideal = ZM_mul(ideal0,u); /* approximate T2-LLL reduction */
+  r = gaussred_from_QR(RgM_mul(G, ideal), prec); /* Cholesky for T2 | ideal */
+  if (!r) pari_err_BUG("small_norm (precision too low)");
+
+  skipfirst = ZV_isscalar(gel(ideal,1))? 1: 0; /* 1 probable */
+  for (k=1; k<=N; k++)
+  {
+    fp->v[k] = gtodouble(gcoeff(r,k,k));
+    for (j=1; j<k; j++) fp->q[j][k] = gtodouble(gcoeff(r,j,k));
+    if (DEBUGLEVEL>3) err_printf("fp->v[%ld]=%.4g ",k,fp->v[k]);
+  }
+  BOUND = mindd(BMULT*fp->v[1], 2*(fp->v[2]+fp->v[1]*fp->q[1][2]*fp->q[1][2]));
+  /* BOUND at most BMULT fp->x smallest known vector */
+  if (DEBUGLEVEL>1)
+  {
+    if (DEBUGLEVEL>3) err_printf("\n");
+    err_printf("BOUND = %.4g\n",BOUND); err_flush();
+  }
+  BOUND *= 1 + 1e-6;
+  k = N; fp->y[N] = fp->z[N] = 0; fp->x[N] = 0;
+  for (av = avma;; avma = av, step(fp->x,fp->y,inc,k))
+  {
+    GEN R;
+    long nz;
+    do
+    { /* look for primitive element of small norm, cf minim00 */
+      int fl = 0;
+      double p;
+      if (k > 1)
+      {
+        long l = k-1;
+        fp->z[l] = 0;
+        for (j=k; j<=N; j++) fp->z[l] += fp->q[l][j]*fp->x[j];
+        p = (double)fp->x[k] + fp->z[k];
+        fp->y[l] = fp->y[k] + p*p*fp->v[k];
+        if (l <= skipfirst && !fp->y[1]) fl = 1;
+        fp->x[l] = (long)floor(-fp->z[l] + 0.5);
+        k = l;
+      }
+      for(;; step(fp->x,fp->y,inc,k))
+      {
+        if (++try_elt > maxtry_ELEMENT) return 0;
+        if (!fl)
+        {
+          p = (double)fp->x[k] + fp->z[k];
+          if (fp->y[k] + p*p*fp->v[k] <= BOUND) break;
+
+          step(fp->x,fp->y,inc,k);
+
+          p = (double)fp->x[k] + fp->z[k];
+          if (fp->y[k] + p*p*fp->v[k] <= BOUND) break;
+        }
+        fl = 0; inc[k] = 1;
+        if (++k > N) return 0;
+      }
+    } while (k > 1);
+
+    /* element complete */
+    if (zv_content(fp->x) !=1) continue; /* not primitive */
+    gx = ZM_zc_mul(ideal,fp->x);
+    if (ZV_isscalar(gx)) continue;
+    if (++try_factor > maxtry_FACT) return 0;
+
+    if (!nbrelpid)
+    {
+      if (!factorgen(F,nf,ideal0,Nideal,gx,fact))
+         continue;
+      return 1;
+    }
+    else if (rr)
+    {
+      if (!factorgen(F,nf,ideal0,rr->Nideal,gx,fact))
+         continue;
+      add_to_fact(rr->jid, 1, fact);
+      gx = nfmul(nf, rr->m1, gx);
+    }
+    else
+    {
+      GEN Nx, xembed = RgM_RgC_mul(M, gx);
+      long e;
+      if (nbsmallnorm) (*nbsmallnorm)++;
+      Nx = grndtoi(embed_norm(xembed, R1), &e);
+      if (e >= 0) {
+        if (DEBUGLEVEL > 1) { err_printf("+"); err_flush(); }
+        continue;
+      }
+      if (!can_factor(F, nf, NULL, gx, Nx, fact)) continue;
+    }
+
+    /* smooth element */
+    R = set_fact(F, fact, rr ? rr->ex : NULL, &nz);
+    /* make sure we get maximal rank first, then allow all relations */
+    if (add_rel(cache, F, R, nz, gx, rr ? 1 : 0) <= 0)
+    { /* probably Q-dependent from previous ones: forget it */
+      if (DEBUGLEVEL>1) err_printf("*");
+      if (++dependent > maxtry_DEP) break;
+      continue;
+    }
+    dependent = 0;
+    if (DEBUGLEVEL && nbfact) (*nbfact)++;
+    if (cache->last >= cache->end) return 1; /* we have enough */
+    if (++nbrelideal == nbrelpid) break;
+  }
+  return 0;
+}
+
+static void
+small_norm(RELCACHE_t *cache, FB_t *F, GEN nf, long nbrelpid,
+           double LOGD, double LIMC2, FACT *fact, GEN p0)
+{
+  pari_timer T;
+  const long N = nf_get_degree(nf), prec = nf_get_prec(nf);
+  FP_t fp;
+  pari_sp av;
+  GEN M = nf_get_M(nf), G = nf_get_G(nf), L_jid = F->L_jid;
+  long nbsmallnorm, nbfact, precbound, noideal = lg(L_jid);
+  REL_t *last = cache->last;
+
+  if (DEBUGLEVEL)
+  {
+    timer_start(&T);
+    err_printf("\n#### Look for %ld relations in %ld ideals (small_norm)\n",
+               cache->end - last, lg(L_jid)-1);
+  }
+  nbsmallnorm = nbfact = 0;
+
+ /* LLL reduction produces v0 in I such that
+  *     T2(v0) <= (4/3)^((n-1)/2) NI^(2/n) disc(K)^(1/n)
+  * We consider v with T2(v) <= BMULT * T2(v0)
+  * Hence Nv <= ((4/3)^((n-1)/2) * BMULT / n)^(n/2) NI sqrt(disc(K)) */
+  precbound = nbits2prec( BITS_IN_LONG + (long)ceil(
+    (N/2. * ((N-1)/2.* log(4./3) + log(BMULT/(double)N)) + log(LIMC2) + LOGD/2)
+      / LOG2)); /* enough to compute norms */
+  if (precbound < prec) M = gprec_w(M, precbound);
+
+  minim_alloc(N+1, &fp.q, &fp.x, &fp.y, &fp.z, &fp.v);
+  for (av = avma; --noideal; avma = av)
+  {
+    GEN ideal=gel(F->LP,L_jid[noideal]);
+
+    if (DEBUGLEVEL>1)
+      err_printf("\n*** Ideal no %ld: %Ps\n", L_jid[noideal], vecslice(ideal,1,4));
+    else if (DEBUGLEVEL)
+      err_printf("(%ld) ", L_jid[noideal]);
+    if (p0)
+      ideal = idealmul(nf, p0, ideal);
+    else
+      ideal = idealhnf_two(nf, ideal);
+    if (Fincke_Pohst_ideal(cache, F, nf, M, G, ideal, fact,
+          nbrelpid, &fp, NULL, prec, &nbsmallnorm, &nbfact))
+      break;
+    if (DEBUGLEVEL>1) timer_printf(&T, "for this ideal");
+  }
+  if (DEBUGLEVEL)
+  {
+    err_printf("\n");
+    timer_printf(&T, "small norm relations");
+    if (nbsmallnorm && DEBUGLEVEL > 1)
+      err_printf("  nb. fact./nb. small norm = %ld/%ld = %.3f\n",
+                  nbfact,nbsmallnorm,((double)nbfact)/nbsmallnorm);
+  }
+}
+
+/* I integral ideal in HNF form */
+static GEN
+remove_content(GEN I)
+{
+  long N = lg(I)-1;
+  if (!is_pm1(gcoeff(I,N,N))) I = Q_primpart(I);
+  return I;
+}
+
+static GEN
+get_random_ideal(FB_t *F, GEN nf, GEN ex)
+{
+  long l = lg(ex);
+  for (;;) {
+    GEN ideal = NULL;
+    long i;
+    for (i=1; i<l; i++)
+    {
+      long id = F->subFB[i];
+      ex[i] = random_bits(RANDOM_BITS);
+      if (ex[i])
+      {
+        GEN a = gmael(F->id2,id,ex[i]);
+        ideal = ideal? idealmul_HNF(nf,ideal, a): idealhnf_two(nf,a);
+      }
+    }
+    if (ideal) { /* ex  != 0 */
+      ideal = remove_content(ideal);
+      if (!is_pm1(gcoeff(ideal,1,1))) return ideal; /* ideal != Z_K */
+    }
+  }
+}
+
+static void
+rnd_rel(RELCACHE_t *cache, FB_t *F, GEN nf, FACT *fact)
+{
+  pari_timer T;
+  const GEN L_jid = F->L_jid, M = nf_get_M(nf), G = F->G0;
+  GEN baseideal;
+  RNDREL_t rr;
+  FP_t fp;
+  const long nbG = lg(F->vecG)-1, lgsub = lg(F->subFB), l_jid = lg(L_jid);
+  const long N = nf_get_degree(nf), prec = nf_get_prec(nf);
+  long jlist;
+  pari_sp av;
+
+  /* will compute P[ L_jid[i] ] * (random product from subFB) */
+  if (DEBUGLEVEL) {
+    timer_start(&T);
+    err_printf("\n#### Look for %ld relations in %ld ideals (rnd_rel)\n",
+               cache->end - cache->last, lg(L_jid)-1);
+  }
+  rr.ex = cgetg(lgsub, t_VECSMALL);
+  baseideal = get_random_ideal(F, nf, rr.ex);
+  baseideal = red(nf, baseideal, F->G0, &rr.m1);
+  baseideal = idealhnf_two(nf, baseideal);
+  minim_alloc(N+1, &fp.q, &fp.x, &fp.y, &fp.z, &fp.v);
+  for (av = avma, jlist = 1; jlist < l_jid; jlist++, avma = av)
+  {
+    long j;
+    GEN ideal;
+    pari_sp av1;
+    REL_t *last = cache->last;
+
+    rr.jid = L_jid[jlist];
+    ideal = gel(F->LP,rr.jid);
+    if (DEBUGLEVEL>1)
+      err_printf("\n*** Ideal no %ld: %Ps\n", rr.jid, vecslice(ideal,1,4));
+    else if (DEBUGLEVEL)
+      err_printf("(%ld) ", jlist, rr.jid);
+    ideal = idealmul_HNF(nf, baseideal, ideal);
+    rr.Nideal = ZM_det_triangular(ideal);
+    if (Fincke_Pohst_ideal(cache, F, nf, M, G, ideal, fact,
+                           RND_REL_RELPID, &fp, &rr, prec, NULL, NULL))
+      break;
+    if (PREVENT_LLL_IN_RND_REL || cache->last != last) continue;
+    for (av1 = avma, j = 1; j <= nbG; j++, avma = av1)
+    { /* reduce along various directions */
+      GEN m = idealpseudomin_nonscalar(ideal, gel(F->vecG,j));
+      GEN R;
+      long nz;
+      if (!factorgen(F,nf,ideal,rr.Nideal,m,fact)) continue;
+      /* can factor ideal, record relation */
+      add_to_fact(rr.jid, 1, fact);
+      R = set_fact(F, fact, rr.ex, &nz);
+      switch (add_rel(cache, F, R, nz, nfmul(nf, m, rr.m1), 1))
+      {
+        case -1: /* forget it */
+          if (DEBUGLEVEL>1) dbg_cancelrel(rr.jid,j,R);
+          continue;
+      }
+      if (DEBUGLEVEL) timer_printf(&T, "for this relation");
+      /* Need more, try next prime ideal */
+      if (cache->last < cache->end) break;
+      /* We have found enough. Return */
+      avma = av; return;
+    }
+  }
+  if (DEBUGLEVEL)
+  {
+    err_printf("\n");
+    timer_printf(&T, "for remaining ideals");
+  }
+}
+
+/* remark: F->KCZ changes if be_honest() fails */
+static int
+be_honest(FB_t *F, GEN nf, GEN auts, FACT *fact)
+{
+  GEN P, done_by_autom;
+  long ex, i, j, J, iz, nbtest;
+  long lgsub = lg(F->subFB), KCZ0 = F->KCZ;
+  long N = nf_get_degree(nf), prec = nf_get_prec(nf);
+  GEN M = nf_get_M(nf), G = nf_get_G(nf);
+  FP_t fp;
+  pari_sp av;
+
+  if (DEBUGLEVEL) {
+    err_printf("Be honest for %ld primes from %ld to %ld\n", F->KCZ2 - F->KCZ,
+               F->FB[ F->KCZ+1 ], F->FB[ F->KCZ2 ]);
+  }
+  minim_alloc(N+1, &fp.q, &fp.x, &fp.y, &fp.z, &fp.v);
+  av = avma;
+  for (iz=F->KCZ+1; iz<=F->KCZ2; iz++, avma = av)
+  {
+    long p = F->FB[iz];
+    P = F->LV[p]; J = lg(P);
+    /* all P|p in FB + last is unramified --> check all but last */
+    if (isclone(P) && pr_get_e(gel(P,J-1)) == 1) J--;
+    if (DEBUGLEVEL>1) err_printf("%ld ", p);
+    done_by_autom = zero_zv(J);
+
+    for (j=1; j<J; j++)
+    {
+      GEN ideal0 = idealhnf_two(nf,gel(P,j)), ideal = ideal0;
+      GEN gen0 = gmael(P, j, 2);
+      pari_sp av2 = avma;
+      if (done_by_autom[j]) continue;
+      for (i = 1; i < lg(auts); i++)
+      {
+        GEN gen = gmul(gel(auts,i), gen0);
+        long k;
+        for (k = j; k < J; k++)
+          if (nfval(nf, gen, gel(P, k)))
+          {
+            done_by_autom[k] = 1;
+            break;
+          }
+      }
+      for(nbtest=0;;)
+      {
+        if (Fincke_Pohst_ideal(NULL, F, nf, M, G, ideal, fact, 0, &fp,
+              NULL, prec, NULL, NULL))
+          break;
+        avma = av2;
+        if (++nbtest > maxtry_HONEST)
+        {
+          if (DEBUGLEVEL)
+            pari_warn(warner,"be_honest() failure on prime %Ps\n", P[j]);
+          return 0;
+        }
+        ideal = ideal0;
+        if (F->newpow) powFBgen(NULL, F, nf, auts);
+        for (i=1; i<lgsub; i++)
+        {
+          long id = F->subFB[i];
+          ex = random_bits(RANDOM_BITS);
+          if (ex) ideal = idealmul_HNF(nf,ideal, gmael(F->id2,id,ex));
+        }
+        ideal = remove_content(ideal);
+      }
+      avma = av2;
+    }
+    F->KCZ++; /* SUCCESS, "enlarge" factorbase */
+  }
+  F->KCZ = KCZ0; avma = av; return 1;
+}
+
+/* A t_MAT of complex floats, in fact reals. Extract a submatrix B
+ * whose columns are definitely non-0, i.e. gexpo(A[j]) >= -2
+ *
+ * If possible precision problem (t_REAL 0 with large exponent), set
+ * *precpb to 1 */
+static GEN
+clean_cols(GEN A, int *precpb)
+{
+  long l = lg(A), h, i, j, k;
+  GEN B;
+  *precpb = 0;
+  if (l == 1) return A;
+  h = lgcols(A);;
+  B = cgetg(l, t_MAT);
+  for (i = k = 1; i < l; i++)
+  {
+    GEN Ai = gel(A,i);
+    int non0 = 0;
+    for (j = 1; j < h; j++)
+    {
+      GEN c = gel(Ai,j);
+      if (gexpo(c) >= -2)
+      {
+        if (gequal0(c)) *precpb = 1; else non0 = 1;
+      }
+    }
+    if (non0) gel(B, k++) = Ai;
+  }
+  setlg(B, k); return B;
+}
+
+static long
+compute_multiple_of_R_pivot(GEN X, GEN x0/*unused*/, long ix, GEN c)
+{
+  GEN x = gel(X,ix);
+  long i, k = 0, ex = - (long)HIGHEXPOBIT, lx = lg(x);
+  (void)x0;
+  for (i=1; i<lx; i++)
+    if (!c[i] && !gequal0(gel(x,i)))
+    {
+      long e = gexpo(gel(x,i));
+      if (e > ex) { ex = e; k = i; }
+    }
+  return (k && ex > -32)? k: lx;
+}
+
+/* A = complex logarithmic embeddings of units (u_j) found so far,
+ * RU = R1+R2 = unit rank, N = field degree
+ * need = unit rank defect
+ * L = NULL (prec problem) or B^(-1) * A with approximate rational entries
+ * (as t_REAL), B a submatrix of A, with (probably) maximal rank RU */
+static GEN
+compute_multiple_of_R(GEN A, long RU, long N, long *pneed, GEN *ptL)
+{
+  GEN T, d, mdet, Im_mdet, kR, xreal, L;
+  long i, j, r, R1 = 2*RU - N;
+  int precpb;
+  pari_sp av = avma;
+
+  if (RU == 1) { *ptL = zeromat(0, lg(A)-1); return gen_1; }
+
+  if (DEBUGLEVEL) err_printf("\n#### Computing regulator multiple\n");
+  xreal = real_i(A); /* = (log |sigma_i(u_j)|) */
+  mdet = clean_cols(xreal, &precpb);
+  /* will cause precision to increase on later failure, but we may succeed! */
+  *ptL = precpb? NULL: gen_1;
+  T = cgetg(RU+1,t_COL);
+  for (i=1; i<=R1; i++) gel(T,i) = gen_1;
+  for (   ; i<=RU; i++) gel(T,i) = gen_2;
+  mdet = shallowconcat(T, mdet); /* det(Span(mdet)) = N * R */
+
+  /* could be using indexrank(), but need custom "get_pivot" function */
+  d = RgM_pivots(mdet, NULL, &r, &compute_multiple_of_R_pivot);
+  /* # of independent columns == unit rank ? */
+  if (lg(mdet)-1 - r != RU)
+  {
+    if (DEBUGLEVEL)
+      err_printf("Unit group rank = %ld < %ld\n",lg(mdet)-1 - r, RU);
+    *pneed = RU - (lg(mdet)-1-r);
+    avma = av; return NULL;
+  }
+
+  Im_mdet = cgetg(RU+1, t_MAT); /* extract independent columns */
+  /* N.B: d[1] = 1, corresponding to T above */
+  gel(Im_mdet, 1) = T;
+  for (i = j = 2; i <= RU; j++)
+    if (d[j]) gel(Im_mdet, i++) = gel(mdet,j);
+
+  /* integral multiple of R: the cols we picked form a Q-basis, they have an
+   * index in the full lattice. First column is T */
+  kR = divru(det2(Im_mdet), N);
+  /* R > 0.2 uniformly */
+  if (!signe(kR) || expo(kR) < -3) { avma=av; *pneed = 0; return NULL; }
+
+  setabssign(kR);
+  L = RgM_inv(Im_mdet);
+  if (!L) { *ptL = NULL; return kR; }
+
+  L = rowslice(L, 2, RU); /* remove first line */
+  L = RgM_mul(L, xreal); /* approximate rational entries */
+  gerepileall(av,2, &L, &kR);
+  *ptL = L; return kR;
+}
+
+static GEN
+bestappr_noer(GEN x, GEN k)
+{
+  GEN y;
+  pari_CATCH(e_PREC) { y = NULL; }
+  pari_TRY { y = bestappr(x,k); } pari_ENDCATCH;
+  return y;
+}
+
+/* Input:
+ * lambda = approximate rational entries: coords of units found so far on a
+ * sublattice of maximal rank (sublambda)
+ * *ptkR = regulator of sublambda = multiple of regulator of lambda
+ * Compute R = true regulator of lambda.
+ *
+ * If c := Rz ~ 1, by Dirichlet's formula, then lambda is the full group of
+ * units AND the full set of relations for the class group has been computed.
+ *
+ * In fact z is a very rough approximation and we only expect 0.75 < Rz < 1.3
+ *
+ * Output: *ptkR = R, *ptU = basis of fundamental units (in terms lambda) */
+static int
+compute_R(GEN lambda, GEN z, GEN *ptL, GEN *ptkR, pari_timer *T)
+{
+  pari_sp av = avma;
+  long r, ec;
+  GEN L, H, D, den, R, c;
+
+  if (DEBUGLEVEL) { err_printf("\n#### Computing check\n"); err_flush(); }
+  D = gmul2n(mpmul(*ptkR,z), 1); /* bound for denom(lambda) */
+  if (expo(D) < 0 && rtodbl(D) < 0.95) return fupb_PRECI;
+  lambda = bestappr_noer(lambda,D);
+  if (!lambda)
+  {
+    if (DEBUGLEVEL) err_printf("truncation error in bestappr\n");
+    return fupb_PRECI;
+  }
+  den = Q_denom(lambda);
+  if (mpcmp(den,D) > 0)
+  {
+    if (DEBUGLEVEL) err_printf("D = %Ps\nden = %Ps\n",D,
+                    lgefint(den) <= DEFAULTPREC? den: itor(den,LOWDEFAULTPREC));
+    return fupb_PRECI;
+  }
+  L = Q_muli_to_int(lambda, den);
+  H = ZM_hnf(L); r = lg(H)-1;
+
+  /* tentative regulator */
+  R = gmul(*ptkR, gdiv(ZM_det_triangular(H), powiu(den, r)));
+  /* R > 0.2 uniformly */
+  if (gexpo(R) < -3) {
+    if (DEBUGLEVEL)
+    {
+      err_printf("\n#### Tentative regulator: %.28Pg\n", R);
+      timer_printf(T, "computing check");
+    }
+    avma = av; return fupb_PRECI;
+  }
+  c = gmul(R,z); /* should be n (= 1 if we are done) */
+  if (DEBUGLEVEL)
+  {
+    err_printf("\n#### Tentative regulator: %.28Pg\n", R);
+    err_printf("\n ***** check = %.28Pg\n",c);
+    timer_printf(T, "computing check");
+  }
+  ec = gexpo(c);
+  /* safe check for c < 0.75 : avoid underflow in gtodouble() */
+  if (ec < -1 || (ec == -1 && gtodouble(c) < 0.75)) {
+    avma = av; return fupb_PRECI;
+  }
+  /* safe check for c > 1.3 : avoid overflow */
+  if (ec > 0 || (ec == 0 && gtodouble(c) > 1.3)) {
+    avma = av; return fupb_RELAT;
+  }
+  *ptkR = R; *ptL = L; return fupb_NONE;
+}
+
+/* norm of an extended ideal I, whose 1st component is in integral HNF */
+static GEN
+idnorm(GEN I) { return ZM_det_triangular(gel(I,1)); }
+
+/* find the smallest (wrt norm) among I, I^-1 and red(I^-1) */
+static GEN
+inverse_if_smaller(GEN nf, GEN I)
+{
+  GEN d, dmin, I1;
+
+  dmin = idnorm(I);
+  I1 = idealinv(nf,I); gel(I1,1) = Q_remove_denom(gel(I1,1), NULL);
+  d = idnorm(I1); if (cmpii(d,dmin) < 0) {I=I1; dmin=d;}
+  /* try reducing (often _increases_ the norm) */
+  I1 = idealred(nf,I1);
+  d = idnorm(I1); if (cmpii(d,dmin) < 0) I=I1;
+  return I;
+}
+
+/* in place */
+static void
+neg_row(GEN U, long i)
+{
+  GEN c = U + lg(U)-1;
+  for (; c>U; c--) gcoeff(c,i,0) = negi(gcoeff(c,i,0));
+}
+
+static void
+setlg_col(GEN U, long l)
+{
+  GEN c = U + lg(U)-1;
+  for (; c>U; c--) setlg(*c, l);
+}
+
+/* compute class group (clg1) + data for isprincipal (clg2) */
+static void
+class_group_gen(GEN nf,GEN W,GEN C,GEN Vbase,long prec, GEN nf0,
+                GEN *ptclg1,GEN *ptclg2)
+{
+  pari_timer T;
+  GEN z,G,Ga,ga,GD,cyc,X,Y,D,U,V,Ur,Ui,Uir,I,J,arch;
+  long i,j,lo,lo0;
+
+  if (DEBUGLEVEL) timer_start(&T);
+  D = ZM_snfall(W,&U,&V); /* UWV = D, D diagonal, G = g Ui (G=new gens, g=old) */
+  Ui = RgM_inv(U);
+  lo0 = lo = lg(D);
+ /* we could set lo = lg(cyc) and truncate all matrices below
+  *   setlg_col(D && U && Y, lo) + setlg(D && V && X && Ui, lo)
+  * but it's not worth the complication:
+  * 1) gain is negligible (avoid computing z^0 if lo < lo0)
+  * 2) when computing ga, the products XU and VY use the original matrices
+  */
+  Ur  = ZM_hnfdivrem(U, D, &Y);
+  Uir = ZM_hnfdivrem(Ui,W, &X);
+ /* [x] = logarithmic embedding of x (arch. component)
+  * NB: z = idealred(I) --> I = y z[1], with [y] = - z[2]
+  * P invertible diagonal matrix (\pm 1) which is only implicitly defined
+  * G = g Uir P + [Ga],  Uir = Ui + WX
+  * g = G P Ur  + [ga],  Ur  = U + DY */
+  G = cgetg(lo,t_VEC);
+  Ga= cgetg(lo,t_VEC);
+  z = init_famat(NULL);
+  if (!nf0) nf0 = nf;
+  for (j=1; j<lo; j++)
+  {
+    GEN p1 = gcoeff(Uir,1,j);
+    z[1]=Vbase[1]; I = idealpowred(nf0,z,p1);
+    for (i=2; i<lo0; i++)
+    {
+      p1 = gcoeff(Uir,i,j);
+      if (signe(p1))
+      {
+        z[1]=Vbase[i];
+        I = extideal_HNF_mul(nf0, I, idealpowred(nf0,z,p1));
+        I = idealred(nf0,I);
+      }
+    }
+    J = inverse_if_smaller(nf0, I);
+    if (J != I)
+    { /* update wrt P */
+      neg_row(Y ,j); gel(V,j) = ZC_neg(gel(V,j));
+      neg_row(Ur,j); gel(X,j) = ZC_neg(gel(X,j));
+    }
+    G[j] = J[1]; /* generator, order cyc[j] */
+    arch = famat_to_arch(nf, gel(J,2), prec);
+    if (!arch) pari_err_PREC("class_group_gen");
+    gel(Ga,j) = gneg(arch);
+  }
+  /* at this point Y = PY, Ur = PUr, V = VP, X = XP */
+
+  /* G D =: [GD] = g (UiP + W XP) D + [Ga]D = g W (VP + XP D) + [Ga]D
+   * NB: DP = PD and Ui D = W V. gW is given by (first lo0-1 cols of) C
+   */
+  GD = gadd(act_arch(ZM_add(V, ZM_mul(X,D)), C),
+            act_arch(D, Ga));
+  /* -[ga] = [GD]PY + G PU - g = [GD]PY + [Ga] PU + gW XP PU
+                               = gW (XP PUr + VP PY) + [Ga]PUr */
+  ga = gadd(act_arch(ZM_add(ZM_mul(X,Ur), ZM_mul(V,Y)), C),
+            act_arch(Ur, Ga));
+  ga = gneg(ga);
+  /* TODO: could (LLL)reduce ga and GD mod units ? */
+
+  cyc = cgetg(lo,t_VEC); /* elementary divisors */
+  for (j=1; j<lo; j++)
+  {
+    gel(cyc,j) = gcoeff(D,j,j);
+    if (gequal1(gel(cyc,j)))
+    { /* strip useless components */
+      lo = j; setlg(cyc,lo); setlg_col(Ur,lo);
+      setlg(G,lo); setlg(Ga,lo); setlg(GD,lo); break;
+    }
+  }
+  *ptclg1 = mkvec3(ZM_det_triangular(W), cyc, G);
+  *ptclg2 = mkvec3(Ur, ga,GD);
+  if (DEBUGLEVEL) timer_printf(&T, "classgroup generators");
+}
+
+/* SMALLBUCHINIT */
+
+static GEN
+decode_pr_lists(GEN nf, GEN pfc)
+{
+  long i, p, pmax, n = nf_get_degree(nf), l = lg(pfc);
+  GEN t, L;
+
+  pmax = 0;
+  for (i=1; i<l; i++)
+  {
+    t = gel(pfc,i); p = itos(t) / n;
+    if (p > pmax) pmax = p;
+  }
+  L = const_vec(pmax, NULL);
+  for (i=1; i<l; i++)
+  {
+    t = gel(pfc,i); p = itos(t) / n;
+    if (!L[p]) gel(L,p) = idealprimedec(nf, utoipos(p));
+  }
+  return L;
+}
+
+static GEN
+decodeprime(GEN T, GEN L, long n)
+{
+  long t = itos(T);
+  return gmael(L, t/n, t%n + 1);
+}
+static GEN
+codeprime(GEN L, long N, GEN pr)
+{
+  long p = pr_get_smallp(pr);
+  return utoipos( N*p + pr_index(gel(L,p), pr)-1 );
+}
+
+static GEN
+codeprimes(GEN Vbase, long N)
+{
+  GEN v, L = get_pr_lists(Vbase, N, 1);
+  long i, l = lg(Vbase);
+  v = cgetg(l, t_VEC);
+  for (i=1; i<l; i++) gel(v,i) = codeprime(L, N, gel(Vbase,i));
+  return v;
+}
+
+/* compute principal ideals corresponding to (gen[i]^cyc[i]) */
+static GEN
+makecycgen(GEN bnf)
+{
+  GEN cyc,gen,h,nf,y,GD;
+  long e,i,l;
+
+  if (DEBUGLEVEL) pari_warn(warner,"completing bnf (building cycgen)");
+  nf = bnf_get_nf(bnf);
+  cyc = bnf_get_cyc(bnf);
+  gen = bnf_get_gen(bnf); GD = gmael(bnf,9,3);
+  h = cgetg_copy(gen, &l);
+  for (i=1; i<l; i++)
+  {
+    GEN gi = gel(gen,i), ci = gel(cyc,i);
+    if (cmpiu(ci, 5) < 0)
+    {
+      GEN N = ZM_det_triangular(gi);
+      y = isprincipalarch(bnf,gel(GD,i), N, ci, gen_1, &e);
+      if (y && fact_ok(nf,y,NULL,mkvec(gi),mkvec(ci)))
+      {
+        gel(h,i) = to_famat_shallow(y,gen_1);
+        continue;
+      }
+    }
+    y = isprincipalfact(bnf, NULL, mkvec(gi), mkvec(ci), nf_GENMAT|nf_FORCE);
+    h[i] = y[2];
+  }
+  return h;
+}
+
+static GEN
+get_y(GEN bnf, GEN pFB, long j)
+{
+  GEN W, B, nf, WB_C, ex, C, Nx, y;
+  long lW, e;
+
+  W   = gel(bnf,1);
+  B   = gel(bnf,2);
+  WB_C= gel(bnf,4);
+  nf  = bnf_get_nf(bnf);
+  lW=lg(W)-1;
+
+  ex = (j<=lW)? gel(W,j): gel(B,j-lW);
+  C = (j<=lW)? NULL: gel(pFB,j);
+  Nx = get_norm_fact_primes(pFB, ex, C);
+  y = isprincipalarch(bnf,gel(WB_C,j), Nx,gen_1, gen_1, &e);
+  if (y && fact_ok(nf,y,C,pFB,ex)) return y;
+  y = isprincipalfact_or_fail(bnf, C, pFB, ex);
+  return typ(y) == t_INT? y: gel(y,2);
+}
+/* compute principal ideals corresponding to bnf relations */
+static GEN
+makematal(GEN bnf)
+{
+  GEN W, B, pFB, ma, retry;
+  long lma, j, prec = 0;
+
+  if (DEBUGLEVEL) pari_warn(warner,"completing bnf (building matal)");
+  W   = gel(bnf,1);
+  B   = gel(bnf,2);
+  lma=lg(W)+lg(B)-1;
+  pFB = get_Vbase(bnf);
+  ma = cgetg(lma,t_VEC);
+  retry = vectrunc_init(lma);
+  for (j=lma-1; j>0; j--)
+  {
+    pari_sp av0 = avma, av;
+    GEN c = getrand(), y;
+    av = avma; y = get_y(bnf, pFB, j);
+    if (typ(y) == t_INT)
+    {
+      long E = itos(y);
+      if (DEBUGLEVEL>1) err_printf("\n%ld done later at prec %ld\n",j,E);
+      avma = av;
+      vectrunc_append(retry, mkvec2(c, (GEN)j));
+      if (E > prec) prec = E;
+    }
+    else
+    {
+      if (DEBUGLEVEL>1) err_printf("%ld ",j);
+      gel(ma,j) = gerepileupto(av0,y);
+    }
+  }
+  if (prec)
+  {
+    long k, l = lg(retry);
+    GEN y, nf = bnf_get_nf(bnf);
+    if (DEBUGLEVEL) pari_warn(warnprec,"makematal",prec);
+    nf = nfnewprec_shallow(nf,prec);
+    bnf = Buchall(nf, nf_FORCE, prec);
+    if (DEBUGLEVEL) err_printf("makematal, adding missing entries:");
+    for (k=1; k<l; k++)
+    {
+      pari_sp av = avma;
+      GEN S = gel(retry,k), c = gel(S,1);
+      long j = S[2];
+      setrand(c);
+      y = get_y(bnf, pFB, j);
+      if (typ(y) == t_INT) pari_err_PREC("makematal");
+      if (DEBUGLEVEL>1) err_printf("%ld ",j);
+      gel(ma,j) = gerepileupto(av,y);
+    }
+  }
+  if (DEBUGLEVEL>1) err_printf("\n");
+  return ma;
+}
+
+#define MATAL  1
+#define CYCGEN 2
+GEN
+check_and_build_cycgen(GEN bnf) {
+  return obj_checkbuild(bnf, CYCGEN, &makecycgen);
+}
+GEN
+check_and_build_matal(GEN bnf) {
+  return obj_checkbuild(bnf, MATAL, &makematal);
+}
+
+static GEN
+get_regulator(GEN mun)
+{
+  pari_sp av = avma;
+  GEN R;
+
+  if (lg(mun) == 1) return gen_1;
+  R = det( rowslice(real_i(mun), 1, lgcols(mun)-2) );
+  setabssign(R); return gerepileuptoleaf(av, R);
+}
+
+/* return corrected archimedian components for elts of x (vector)
+ * (= log(sigma_i(x)) - log(|Nx|) / [K:Q]) */
+static GEN
+get_archclean(GEN nf, GEN x, long prec, int units)
+{
+  long k,N, la = lg(x);
+  GEN M = cgetg(la,t_MAT);
+
+  if (la == 1) return M;
+  N = nf_get_degree(nf);
+  for (k=1; k<la; k++)
+  {
+    pari_sp av = avma;
+    GEN c = get_arch(nf, gel(x,k), prec);
+    if (!c) return NULL;
+    if (!units) {
+      c = cleanarch(c, N, prec);
+      if (!c) return NULL;
+    }
+    gel(M,k) = gerepilecopy(av, c);
+  }
+  return M;
+}
+
+static void
+my_class_group_gen(GEN bnf, long prec, GEN nf0, GEN *ptcl, GEN *ptcl2)
+{
+  GEN W = gel(bnf,1), C = gel(bnf,4), nf = bnf_get_nf(bnf);
+  class_group_gen(nf,W,C,get_Vbase(bnf),prec,nf0, ptcl,ptcl2);
+}
+
+GEN
+bnfnewprec_shallow(GEN bnf, long prec)
+{
+  GEN nf0 = bnf_get_nf(bnf), nf, res, funits, mun, gac, matal, clgp, clgp2, y;
+  long r1, r2, prec1;
+
+  nf_get_sign(nf0, &r1, &r2);
+  funits = matalgtobasis(nf0, bnf_get_fu(bnf));
+
+  prec1 = prec;
+  if (r1 + r2 > 1) {
+    long e = gexpo(bnf_get_logfu(bnf)) + 1 - TWOPOTBITS_IN_LONG;
+    if (e >= 0) prec += nbits2extraprec(e);
+  }
+  if (DEBUGLEVEL && prec1!=prec) pari_warn(warnprec,"bnfnewprec",prec);
+  matal = check_and_build_matal(bnf);
+  for(;;)
+  {
+    pari_sp av = avma;
+    nf = nfnewprec_shallow(nf0,prec);
+    mun = get_archclean(nf,funits,prec,1);
+    if (mun)
+    {
+      gac = get_archclean(nf,matal,prec,0);
+      if (gac) break;
+    }
+    avma = av; prec = precdbl(prec);
+    if (DEBUGLEVEL) pari_warn(warnprec,"bnfnewprec(extra)",prec);
+  }
+  y = leafcopy(bnf);
+  gel(y,3) = mun;
+  gel(y,4) = gac;
+  gel(y,7) = nf;
+  my_class_group_gen(y,prec,nf0, &clgp,&clgp2);
+  res = leafcopy(gel(bnf,8));
+  gel(res,1) = clgp;
+  gel(res,2) = get_regulator(mun);
+  gel(y,8) = res;
+  gel(y,9) = clgp2; return y;
+}
+GEN
+bnfnewprec(GEN bnf, long prec)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, bnfnewprec_shallow(checkbnf(bnf), prec));
+}
+
+GEN
+bnrnewprec_shallow(GEN bnr, long prec)
+{
+  GEN y = cgetg(7,t_VEC);
+  long i;
+  gel(y,1) = bnfnewprec_shallow(bnr_get_bnf(bnr), prec);
+  for (i=2; i<7; i++) gel(y,i) = gel(bnr,i);
+  return y;
+}
+GEN
+bnrnewprec(GEN bnr, long prec)
+{
+  GEN y = cgetg(7,t_VEC);
+  long i;
+  checkbnr(bnr);
+  gel(y,1) = bnfnewprec(bnr_get_bnf(bnr), prec);
+  for (i=2; i<7; i++) gel(y,i) = gcopy(gel(bnr,i));
+  return y;
+}
+
+static void
+nfbasic_from_sbnf(GEN sbnf, nfbasic_t *T)
+{
+  T->x0 = T->x    = gel(sbnf,1);
+  T->dK   = gel(sbnf,3);
+  T->bas  = gel(sbnf,4);
+  T->index= get_nfindex(T->bas);
+  T->r1   = itos(gel(sbnf,2));
+  T->dx   = NULL;
+  T->dKP  = NULL;
+  T->basden = NULL;
+}
+
+static GEN
+get_clfu(GEN clgp, GEN reg, GEN zu, GEN fu)
+{ return mkvec5(clgp, reg, gen_1/*DUMMY*/, zu, fu); }
+
+static GEN
+buchall_end(GEN nf,GEN res, GEN clg2, GEN W, GEN B, GEN A, GEN C,GEN Vbase)
+{
+  GEN z = cgetg(11,t_VEC);
+  gel(z,1) = W;
+  gel(z,2) = B;
+  gel(z,3) = A;
+  gel(z,4) = C;
+  gel(z,5) = Vbase;
+  gel(z,6) = gen_0;
+  gel(z,7) = nf;
+  gel(z,8) = res;
+  gel(z,9) = clg2;
+  gel(z,10) = zerovec(2);
+  return z;
+}
+
+static GEN
+bnftosbnf(GEN bnf)
+{
+  GEN nf = bnf_get_nf(bnf), T = nf_get_pol(nf);
+  GEN y = cgetg(13,t_VEC);
+
+  gel(y,1) = T;
+  gel(y,2) = gmael(nf,2,1);
+  gel(y,3) = nf_get_disc(nf);
+  gel(y,4) = nf_get_zk(nf);
+  gel(y,5) = nf_get_roots(nf);
+  gel(y,6) = gen_0; /* FIXME: unused */
+  gel(y,7) = gel(bnf,1);
+  gel(y,8) = gel(bnf,2);
+  gel(y,9) = codeprimes(gel(bnf,5), degpol(T));
+  gel(y,10) = mkvec2(utoipos(bnf_get_tuN(bnf)),
+                     nf_to_scalar_or_basis(nf, bnf_get_tuU(bnf)));
+  gel(y,11) = matalgtobasis(bnf, bnf_get_fu_nocheck(bnf));
+  (void)check_and_build_matal(bnf);
+  gel(y,12) = gel(bnf,10); return y;
+}
+GEN
+bnfcompress(GEN bnf)
+{
+  pari_sp av = avma;
+  bnf = checkbnf(bnf);
+  return gerepilecopy(av, bnftosbnf( checkbnf(bnf) ));
+}
+
+static GEN
+sbnf2bnf(GEN sbnf, long prec)
+{
+  long j, k, l, n;
+  pari_sp av = avma;
+  GEN ro, nf, A, fu, FU, L;
+  GEN pfc, C, clgp, clgp2, res, y, W, zu, matal, Vbase;
+  nfbasic_t T;
+
+  if (typ(sbnf) != t_VEC || lg(sbnf) != 13) pari_err_TYPE("bnfmake",sbnf);
+  if (prec < DEFAULTPREC) prec = DEFAULTPREC;
+
+  nfbasic_from_sbnf(sbnf, &T);
+  ro = gel(sbnf,5);
+  fu = gel(sbnf,11);
+  if (prec > gprecision(ro)) ro = get_roots(T.x,T.r1,prec);
+  nf = nfbasic_to_nf(&T, ro, prec);
+
+  A = get_archclean(nf, fu, prec, 1);
+  if (!A) pari_err_PREC( "bnfmake");
+
+  prec = gprecision(ro);
+  matal = check_and_build_matal(sbnf);
+  C = get_archclean(nf,matal,prec,0);
+  if (!C) pari_err_PREC( "bnfmake");
+
+  pfc = gel(sbnf,9);
+  l = lg(pfc);
+  Vbase = cgetg(l,t_COL);
+  L = decode_pr_lists(nf, pfc);
+  n = nf_get_degree(nf);
+  for (j=1; j<l; j++) gel(Vbase,j) = decodeprime(gel(pfc,j), L, n);
+  W = gel(sbnf,7);
+  class_group_gen(nf,W,C,Vbase,prec,NULL, &clgp,&clgp2);
+
+  zu = gel(sbnf,10);
+  zu = mkvec2(gel(zu,1), nf_to_scalar_or_alg(nf, gel(zu,2)));
+
+  FU = cgetg_copy(fu, &l);
+  for (k=1; k < l; k++) gel(FU,k) = coltoliftalg(nf, gel(fu,k));
+  res = get_clfu(clgp, get_regulator(A), zu, FU);
+  y = buchall_end(nf,res,clgp2,W,gel(sbnf,8),A,C,Vbase);
+  y[10] = sbnf[12]; return gerepilecopy(av,y);
+}
+
+GEN
+bnfinit0(GEN P, long flag, GEN data, long prec)
+{
+  double c1 = BNF_C1, c2 = BNF_C2;
+  long fl, relpid = BNF_RELPID;
+
+  if (typ(P) == t_VEC && lg(P) == 13) return sbnf2bnf(P, prec); /* sbnf */
+  if (data)
+  {
+    long lx = lg(data);
+    if (typ(data) != t_VEC || lx > 5) pari_err_TYPE("bnfinit",data);
+    switch(lx)
+    {
+      case 4: relpid = itos(gel(data,3));
+      case 3: c2 = gtodouble(gel(data,2));
+      case 2: c1 = gtodouble(gel(data,1));
+    }
+  }
+  switch(flag)
+  {
+    case 2:
+    case 0: fl = 0; break;
+    case 1: fl = nf_FORCE; break;
+    default: pari_err_FLAG("bnfinit");
+      return NULL; /* not reached */
+  }
+  return Buchall_param(P, c1, c2, relpid, fl, prec);
+}
+GEN
+Buchall(GEN P, long flag, long prec)
+{ return Buchall_param(P, BNF_C1, BNF_C2, BNF_RELPID, flag, prec); }
+
+static GEN
+Buchall_deg1(GEN nf)
+{
+  GEN v = cgetg(1,t_VEC), m = cgetg(1,t_MAT);
+  GEN W, A, B, C, Vbase, res;
+  GEN fu = v, R = gen_1, zu = mkvec2(gen_2, gen_m1);
+  GEN clg1 = mkvec3(gen_1,v,v), clg2 = mkvec3(m,v,v);
+
+  W = A = B = C = m;
+  Vbase = cgetg(1,t_COL);
+  res = get_clfu(clg1, R, zu, fu);
+  return buchall_end(nf,res,clg2,W,B,A,C,Vbase);
+}
+
+/* return (small set of) indices of columns generating the same lattice as x.
+ * Assume HNF(x) is inexpensive (few rows, many columns).
+ * Dichotomy approach since interesting columns may be at the very end */
+GEN
+extract_full_lattice(GEN x)
+{
+  long dj, j, k, l = lg(x);
+  GEN h, h2, H, v;
+
+  if (l < 200) return NULL; /* not worth it */
+
+  v = vecsmalltrunc_init(l);
+  H = ZM_hnf(x);
+  h = cgetg(1, t_MAT);
+  dj = 1;
+  for (j = 1; j < l; )
+  {
+    pari_sp av = avma;
+    long lv = lg(v);
+
+    for (k = 0; k < dj; k++) v[lv+k] = j+k;
+    setlg(v, lv + dj);
+    h2 = ZM_hnf(vecpermute(x, v));
+    if (ZM_equal(h, h2))
+    { /* these dj columns can be eliminated */
+      avma = av; setlg(v, lv);
+      j += dj;
+      if (j >= l) break;
+      dj <<= 1;
+      if (j + dj >= l) { dj = (l - j) >> 1; if (!dj) dj = 1; }
+    }
+    else if (dj > 1)
+    { /* at least one interesting column, try with first half of this set */
+      avma = av; setlg(v, lv);
+      dj >>= 1; /* > 0 */
+    }
+    else
+    { /* this column should be kept */
+      if (ZM_equal(h2, H)) break;
+      h = h2; j++;
+    }
+  }
+  return v;
+}
+
+static void
+init_rel(RELCACHE_t *cache, FB_t *F, long add_need)
+{
+  const long n = F->KC + add_need; /* expected # of needed relations */
+  long i, j, k, p;
+  GEN c, P;
+  GEN R;
+
+  if (DEBUGLEVEL) err_printf("KCZ = %ld, KC = %ld, n = %ld\n", F->KCZ,F->KC,n);
+  reallocate(cache, 10*n + 50); /* make room for lots of relations */
+  cache->chk = cache->base;
+  cache->end = cache->base + n;
+  cache->relsup = add_need;
+  cache->last = cache->base;
+  cache->missing = lg(cache->basis) - 1;
+  for (i = 1; i <= F->KCZ; i++)
+  { /* trivial relations (p) = prod P^e */
+    p = F->FB[i]; P = F->LV[p];
+    if (!isclone(P)) continue;
+
+    /* all prime divisors in FB */
+    c = zero_Flv(F->KC); k = F->iLP[p];
+    R = c; c += k;
+    for (j = lg(P)-1; j; j--) c[j] = pr_get_e(gel(P,j));
+    add_rel(cache, F, R, k+1, /*m*/NULL, 0);
+  }
+}
+
+static void
+shift_embed(GEN G, GEN Gtw, long a, long r1)
+{
+  long j, k, l = lg(G);
+  if (a <= r1)
+    for (j=1; j<l; j++) gcoeff(G,a,j) = gcoeff(Gtw,a,j);
+  else
+  {
+    k = (a<<1) - r1;
+    for (j=1; j<l; j++)
+    {
+      gcoeff(G,k-1,j) = gcoeff(Gtw,k-1,j);
+      gcoeff(G,k  ,j) = gcoeff(Gtw,k,  j);
+    }
+  }
+}
+
+/* G where embeddings a and b are multiplied by 2^10 */
+static GEN
+shift_G(GEN G, GEN Gtw, long a, long b, long r1)
+{
+  GEN g = RgM_shallowcopy(G);
+  if (a != b) shift_embed(g,Gtw,a,r1);
+  shift_embed(g,Gtw,b,r1); return g;
+}
+
+static void
+compute_vecG(GEN nf, FB_t *F, long n)
+{
+  GEN G0, Gtw0, vecG, G = nf_get_G(nf);
+  long e, i, j, ind, r1 = nf_get_r1(nf), r = lg(G)-1;
+  if (n == 1) { F->G0 = G0 = ground(G); F->vecG = mkvec( G0 ); return; }
+  for (e = 32;;)
+  {
+    G = gmul2n(G, e);
+    G0 = ground(G); if (ZM_rank(G0) == r) break; /* maximal rank ? */
+  }
+  Gtw0 = ground(gmul2n(G, 10));
+  vecG = cgetg(1 + n*(n+1)/2,t_VEC);
+  for (ind=j=1; j<=n; j++)
+    for (i=1; i<=j; i++) gel(vecG,ind++) = shift_G(G0,Gtw0,i,j,r1);
+  F->G0 = G0; F->vecG = vecG;
+}
+
+static GEN
+automorphism_perms(GEN M, GEN auts, GEN cyclic, long N)
+{
+  pari_sp av;
+  const long r1plusr2 = lgcols(M), r1 = 2*r1plusr2-N-2, r2 = r1plusr2-r1-1;
+  long nauts = lg(auts), ncyc = lg(cyclic), i, j, l, m;
+  GEN Mt, perms = cgetg(nauts, t_VEC);
+
+  for (l = 1; l < nauts; l++)
+    gel(perms, l) = cgetg(r1plusr2, t_VECSMALL);
+  av = avma;
+  Mt = shallowtrans(gprec_w(M, 3)); /* need little accuracy */
+  Mt = shallowconcat(Mt, gconj(vecslice(Mt, r1+1, r1+r2)));
+  for (l = 1; l < ncyc; l++)
+  {
+    GEN thiscyc = gel(cyclic, l);
+    long k = thiscyc[1];
+    GEN Nt = RgM_mul(shallowtrans(gel(auts, k)), Mt);
+    GEN perm = gel(perms, k), permprec;
+    pari_sp av2 = avma;
+    for (i = 1; i < r1plusr2; i++, avma = av2)
+    {
+      GEN vec = gel(Nt, i), minnorm;
+      minnorm = gnorml2(gsub(vec, gel(Mt, 1)));
+      perm[i] = 1;
+      for (j = 2; j <= N; j++)
+      {
+        GEN thisnorm = gnorml2(gsub(vec, gel(Mt, j)));
+        if (gcmp(thisnorm, minnorm) < 0)
+        {
+          minnorm = thisnorm;
+          perm[i] = j >= r1plusr2 ? r2-j : j;
+        }
+      }
+    }
+    for (permprec = perm, m = 2; m < lg(thiscyc); m++)
+    {
+      GEN thisperm = gel(perms, thiscyc[m]);
+      for (i = 1; i < r1plusr2; i++)
+      {
+        long pp = labs(permprec[i]);
+        thisperm[i] = permprec[i] < 0 ? -perm[pp] : perm[pp];
+      }
+      permprec = thisperm;
+    }
+  }
+  avma = av;
+  return perms;
+}
+
+/* Determine the field automorphisms and its matrix in the integral basis. */
+static GEN
+automorphism_matrices(GEN nf, GEN *invp, GEN *cycp)
+{
+  pari_sp av = avma;
+  GEN auts = galoisconj(nf, NULL), mats, cyclic, cyclicidx;
+  GEN invs;
+  long nauts = lg(auts)-1, i, j, k, l;
+
+  cyclic = cgetg(nauts+1, t_VEC);
+  cyclicidx = zero_Flv(nauts);
+  invs = zero_Flv(nauts-1);
+  for (l = 1; l <= nauts; l++)
+  {
+    GEN aut = gel(auts, l);
+    if (degpol(aut) == 1 && isint1(leading_term(aut)) &&
+        isintzero(constant_term(aut)))
+    {
+      swap(gel(auts, l), gel(auts, nauts));
+      break;
+    }
+  }
+  for (l = 1; l <= nauts; l++) gel(auts, l) = algtobasis(nf, gel(auts, l));
+  /* Compute maximal cyclic subgroups */
+  for (l = nauts; --l > 0; )
+    if (!cyclicidx[l])
+    {
+      GEN elt = gel(auts, l), aut = elt, cyc = cgetg(nauts+1, t_VECSMALL);
+      cyclicidx[l] = l;
+      cyc[1] = l;
+      j = 1;
+      do
+      {
+        elt = galoisapply(nf, elt, aut);
+        for (k = 1; k <= nauts; k++) if (gequal(elt, gel(auts, k))) break;
+        cyclicidx[k] = l;
+        cyc[++j] = k;
+      }
+      while (k != nauts);
+      setlg(cyc, j);
+      gel(cyclic, l) = cyc;
+      /* Store the inverses */
+      for (i = 1; i <= j/2; i++)
+      {
+        invs[cyc[i]] = cyc[j-i];
+        invs[cyc[j-i]] = cyc[i];
+      }
+    }
+  for (i = j = 1; i < nauts; i++)
+    if (cyclicidx[i] == i) cyclic[j++] = cyclic[i];
+  setlg(cyclic, j);
+  mats = cgetg(nauts, t_VEC);
+  while (--j > 0)
+  {
+    GEN cyc = gel(cyclic, j);
+    long id = cyc[1];
+    GEN M, Mi, aut = gel(auts, id);
+
+    gel(mats, id) = Mi = M = nfgaloismatrix(nf, aut);
+    for (i = 2; i < lg(cyc); i++)
+    {
+      Mi = ZM_mul(Mi, M);
+      gel(mats, cyc[i]) = Mi;
+    }
+  }
+  gerepileall(av, 3, &mats, &invs, &cyclic);
+  *invp = invs;
+  *cycp = cyclic;
+  return mats;
+}
+
+static GEN
+trim_list(FB_t *F)
+{
+  pari_sp av = avma;
+  GEN L_jid = F->L_jid, present = zero_Flv(F->KC);
+  long i, j, imax = minss(lg(L_jid), F->KC + 1);
+  GEN minidx = F->minidx, idx = cgetg(imax, t_VECSMALL);
+
+  for (i = j = 1; i < imax; i++)
+  {
+    long id = minidx[L_jid[i]];
+
+    if (!present[id])
+    {
+      idx[j++] = L_jid[i];
+      present[id] = 1;
+    }
+  }
+  setlg(idx, j);
+  return gerepileuptoleaf(av, idx);
+}
+
+static void
+try_elt(RELCACHE_t *cache, FB_t *F, GEN nf, GEN x, FACT *fact)
+{
+  pari_sp av = avma;
+  GEN R, Nx;
+  long nz, tx = typ(x);
+
+  if (tx == t_INT || tx == t_FRAC) return;
+  if (tx != t_COL) x = algtobasis(nf, x);
+  if (RgV_isscalar(x)) return;
+  x = Q_primpart(x);
+  Nx = nfnorm(nf, x);
+  if (!can_factor(F, nf, NULL, x, Nx, fact)) return;
+
+  /* smooth element */
+  R = set_fact(F, fact, NULL, &nz);
+  /* make sure we get maximal rank first, then allow all relations */
+  (void) add_rel(cache, F, R, nz, x, 0);
+  avma = av;
+}
+
+GEN
+Buchall_param(GEN P, double cbach, double cbach2, long nbrelpid, long flun, long prec)
+{
+  pari_timer T;
+  pari_sp av0 = avma, av, av2;
+  long PRECREG, N, R1, R2, RU, low, high, LIMC0, LIMC, LIMC2, LIMCMAX, zc, i;
+  long MAXDEPSIZESFB, MAXDEPSFB;
+  long nreldep, sfb_trials, need, old_need, precdouble = 0, precadd = 0;
+  long done_small, small_fail, fail_limit, squash_index;
+  double lim, drc, LOGD, LOGD2;
+  GEN computed = NULL, zu, nf, D, A, W, R, h, PERM, fu = NULL /*-Wall*/;
+  GEN small_multiplier;
+  GEN res, L, invhr, B, C, C0, lambda, dep, clg1, clg2, Vbase;
+  GEN auts, cyclic;
+  const char *precpb = NULL;
+  int FIRST = 1, class1 = 0;
+  RELCACHE_t cache;
+  FB_t F;
+  GRHcheck_t GRHcheck;
+  FACT *fact;
+
+  if (DEBUGLEVEL) timer_start(&T);
+  P = get_nfpol(P, &nf);
+  if (nf)
+    PRECREG = nf_get_prec(nf);
+  else
+  {
+    PRECREG = maxss(prec, MEDDEFAULTPREC);
+    nf = nfinit(P, PRECREG);
+    if (lg(nf)==3) { /* P non-monic and nfinit CHANGEd it ? */
+      pari_warn(warner,"non-monic polynomial. Change of variables discarded");
+      nf = gel(nf,1);
+      P = nf_get_pol(nf);
+    }
+  }
+  N = degpol(P);
+  if (N <= 1) return gerepilecopy(av0, Buchall_deg1(nf));
+  zu = rootsof1(nf);
+  gel(zu,2) = nf_to_scalar_or_alg(nf, gel(zu,2));
+  if (DEBUGLEVEL) timer_printf(&T, "nfinit & rootsof1");
+
+  auts = automorphism_matrices(nf, &F.invs, &cyclic);
+  if (DEBUGLEVEL) timer_printf(&T, "automorphisms");
+  F.embperm = automorphism_perms(nf_get_M(nf), auts, cyclic, N);
+  if (DEBUGLEVEL) timer_printf(&T, "complex embedding permutations");
+
+  nf_get_sign(nf, &R1, &R2); RU = R1+R2;
+  compute_vecG(nf, &F, minss(RU, 9));
+  if (DEBUGLEVEL) timer_printf(&T, "weighted G matrices");
+  D = absi(nf_get_disc(nf)); drc = gtodouble(D);
+  if (DEBUGLEVEL) err_printf("R1 = %ld, R2 = %ld\nD = %Ps\n",R1,R2, D);
+  LOGD = log(drc); LOGD2 = LOGD*LOGD;
+  lim = exp(-N + R2 * log(4/PI)) * sqrt(2*PI*N*drc);
+  if (lim < 3.) lim = 3.;
+  if (cbach > 12.) {
+    if (cbach2 < cbach) cbach2 = cbach;
+    cbach = 12.;
+  }
+  if (cbach < 0.)
+    pari_err_DOMAIN("Buchall","Bach constant","<",gen_0,dbltor(cbach));
+
+  cache.base = NULL; F.subFB = NULL; F.LP = NULL;
+  init_GRHcheck(&GRHcheck, N, R1, LOGD);
+  high = low = LIMC0 = maxss((long)(cbach2*LOGD2), 1);
+  LIMCMAX = (long)(12.*LOGD2);
+  /* 97/1223 below to ensure a good enough approximation of residue */
+  cache_prime_dec(&GRHcheck, expi(D) < 16 ? 97: 1223, nf);
+  while (!GRHchk(nf, &GRHcheck, high))
+  {
+    low = high;
+    high *= 2;
+  }
+  while (high - low > 1)
+  {
+    long test = (low+high)/2;
+    if (GRHchk(nf, &GRHcheck, test))
+      high = test;
+    else
+      low = test;
+  }
+  if (high == LIMC0+1 && GRHchk(nf, &GRHcheck, LIMC0))
+    LIMC2 = LIMC0;
+  else
+    LIMC2 = high;
+  if (LIMC2 > LIMCMAX) LIMC2 = LIMCMAX;
+  if (DEBUGLEVEL) err_printf("LIMC2 = %ld\n", LIMC2);
+  if (LIMC2 < nthideal(&GRHcheck, nf, 1)) class1 = 1;
+  if (DEBUGLEVEL && class1) err_printf("Class 1\n", LIMC2);
+  LIMC0 = (long)(cbach*LOGD2);
+  av = avma; LIMC = cbach ? LIMC0 : LIMC2;
+  LIMC = maxss(LIMC, nthideal(&GRHcheck, nf, N));
+  if (DEBUGLEVEL) timer_printf(&T, "computing Bach constant");
+
+START:
+  if (DEBUGLEVEL) timer_start(&T);
+  if (!FIRST) LIMC = check_LIMC(LIMC,LIMCMAX);
+  if (DEBUGLEVEL && LIMC > LIMC0)
+    err_printf("%s*** Bach constant: %f\n", FIRST?"":"\n", LIMC/LOGD2);
+  if (cache.base)
+  {
+    REL_t *rel;
+    for (i = 1, rel = cache.base + 1; rel < cache.last; rel++)
+      if (rel->m) i++;
+    computed = cgetg(i, t_VEC);
+    for (i = 1, rel = cache.base + 1; rel < cache.last; rel++)
+      if (rel->m) gel(computed, i++) = rel->m;
+    computed = gclone(computed);
+    delete_cache(&cache);
+  }
+  FIRST = 0; avma = av;
+  if (F.LP) delete_FB(&F);
+  if (LIMC2 < LIMC) LIMC2 = LIMC;
+  if (DEBUGLEVEL) { err_printf("LIMC = %ld, LIMC2 = %ld\n",LIMC,LIMC2); }
+
+  FBgen(&F, nf, N, LIMC, LIMC2, &GRHcheck);
+  if (!F.KC) goto START;
+  av = avma;
+  subFBgen(&F,nf,auts,cyclic,mindd(lim,LIMC2) + 0.5,MINSFB);
+  if (DEBUGLEVEL)
+  {
+    if (lg(F.subFB) > 1)
+      timer_printf(&T, "factorbase (#subFB = %ld) and ideal permutations",
+                       lg(F.subFB)-1);
+    else
+      timer_printf(&T, "factorbase (no subFB) and ideal permutations");
+  }
+  /* invhr ~ 2^r1 (2pi)^r2 / sqrt(D) w = Res(zeta_K, s=1) / hR */
+  invhr = gmul(gdiv(gmul2n(powru(mppi(DEFAULTPREC), R2), RU),
+              mulri(gsqrt(D,DEFAULTPREC),gel(zu,1))),compute_invres(&GRHcheck));
+  fact = (FACT*)stack_malloc((F.KC+1)*sizeof(FACT));
+  PERM = leafcopy(F.perm); /* to be restored in case of precision increase */
+  cache.basis = zero_Flm_copy(F.KC,F.KC);
+  small_multiplier = zero_Flv(F.KC);
+  F.id2 = zerovec(F.KC);
+  MAXDEPSIZESFB = (lg(F.subFB) - 1) * DEPSIZESFBMULT;
+  MAXDEPSFB = MAXDEPSIZESFB / DEPSFBDIV;
+  done_small = 0; small_fail = 0; squash_index = 0;
+  fail_limit = F.KC + 1;
+  R = NULL; A = NULL;
+  av2 = avma;
+  init_rel(&cache, &F, RELSUP + RU-1); /* trivial relations */
+  old_need = need = cache.end - cache.last;
+
+  W = NULL; zc = 0;
+  sfb_trials = nreldep = 0;
+
+  if (computed)
+  {
+    for (i = 1; i < lg(computed); i++)
+      try_elt(&cache, &F, nf, gel(computed, i), fact);
+    if (isclone(computed)) gunclone(computed);
+    if (DEBUGLEVEL && i > 1)
+    {
+      err_printf("\n");
+      timer_printf(&T, "including already computed relations");
+    }
+    need = 0;
+  }
+
+  do
+  {
+    do
+    {
+      pari_sp av4 = avma;
+      if (need > 0)
+      {
+        long oneed = cache.end - cache.last;
+        /* Test below can be true if small_norm did not find enough linearly
+         * dependent relations */
+        if (need < oneed) need = oneed;
+        pre_allocate(&cache, need+lg(auts)-1+(R ? lg(W)-1 : 0));
+        cache.end = cache.last + need;
+        F.L_jid = trim_list(&F);
+      }
+      if (need > 0 && nbrelpid > 0 && (done_small <= F.KC+1 || A) &&
+          small_fail <= fail_limit &&
+          cache.last < cache.base + 2*F.KC+2*RU+RELSUP /* heuristic */)
+      {
+        pari_sp av3 = avma;
+        GEN p0 = NULL;
+        long j, k;
+        REL_t *last = cache.last;
+        if (R && lg(W) > 1 && (done_small % 2))
+        {
+          /* We have full rank for class group and unit, however those
+           * lattices are too small. The following tries to improve the
+           * prime group lattice: it specifically looks for relations
+           * involving the primes generating the class group. */
+          long l = lg(W) - 1;
+          /* We need lg(W)-1 relations to squash the class group. */
+          F.L_jid = vecslice(F.perm, 1, l); cache.end = cache.last + l;
+          /* Lie to the add_rel subsystem: pretend we miss relations involving
+           * the primes generating the class group (and only those). */
+          cache.missing = l;
+          for ( ; l > 0; l--) mael(cache.basis, F.perm[l], F.perm[l]) = 0;
+        }
+        j = done_small % (F.KC+1);
+        if (j)
+        {
+          long mj = small_multiplier[j];
+          p0 = gel(F.LP, j);
+          if (!A)
+          {
+            /* Prevent considering both P_iP_j and P_jP_i in small_norm */
+            /* Since not all elements end up in F.L_jid (because they can
+             * be eliminated by hnfspec/add or by trim_list, keep track
+             * of which ideals are being considered at each run. */
+            for (i = k = 1; i < lg(F.L_jid); i++)
+              if (F.L_jid[i] > mj)
+              {
+                small_multiplier[F.L_jid[i]] = j;
+                F.L_jid[k++] = F.L_jid[i];
+              }
+            setlg(F.L_jid, k);
+          }
+        }
+        if (lg(F.L_jid) > 1)
+          small_norm(&cache, &F, nf, nbrelpid, LOGD, LIMC2, fact, p0);
+        avma = av3;
+        if (!A && cache.last != last)
+          small_fail = 0;
+        else
+          small_fail++;
+        if (R && lg(W) > 1 && (done_small % 2))
+        {
+          long l = lg(W) - 1;
+          for ( ; l > 0; l--) mael(cache.basis, F.perm[l], F.perm[l]) = 1;
+          cache.missing = 0;
+        }
+        F.L_jid = F.perm;
+        need = 0; cache.end = cache.last;
+        done_small++;
+        if (!need) F.sfb_chg = 0;
+      }
+      if (need > 0)
+      {
+        /* Random relations */
+        if (lg(F.subFB) == 1) goto START;
+        nreldep++;
+        if (nreldep > MAXDEPSIZESFB) {
+          if (++sfb_trials > SFB_MAX && LIMC < LIMCMAX/6) goto START;
+          F.sfb_chg = sfb_INCREASE;
+          nreldep = 0;
+        }
+        else if (!(nreldep % MAXDEPSFB))
+          F.sfb_chg = sfb_CHANGE;
+        if (F.newpow)
+        {
+          F.sfb_chg = 0;
+          if (DEBUGLEVEL) err_printf("\n");
+        }
+        if (F.sfb_chg && !subFB_change(&F)) goto START;
+        if (F.newpow) {
+          powFBgen(&cache, &F, nf, auts);
+          MAXDEPSIZESFB = (lg(F.subFB) - 1) * DEPSIZESFBMULT;
+          MAXDEPSFB = MAXDEPSIZESFB / DEPSFBDIV;
+          if (DEBUGLEVEL) timer_printf(&T, "powFBgen");
+        }
+        if (!F.sfb_chg) rnd_rel(&cache, &F, nf, fact);
+        F.L_jid = F.perm;
+      }
+      if (DEBUGLEVEL) timer_start(&T);
+      if (precpb)
+      {
+        GEN nf0 = nf;
+        if (precadd) { PRECREG += precadd; precadd = 0; }
+        else           PRECREG = precdbl(PRECREG);
+        if (DEBUGLEVEL)
+        {
+          char str[64]; sprintf(str,"Buchall_param (%s)",precpb);
+          pari_warn(warnprec,str,PRECREG);
+        }
+        nf = gclone( nfnewprec_shallow(nf, PRECREG) );
+        if (precdouble) gunclone(nf0);
+        precdouble++; precpb = NULL;
+
+        F.newarc = 1;
+        for (i = 1; i < lg(PERM); i++) F.perm[i] = PERM[i];
+        cache.chk = cache.base; W = NULL; /* recompute arch components+reduce */
+      }
+      avma = av4;
+      if (cache.chk != cache.last)
+      { /* Reduce relation matrices */
+        long l = cache.last - cache.chk + 1, j;
+        GEN M = nf_get_M(nf), mat = cgetg(l, t_MAT), emb = cgetg(l, t_MAT);
+        int first = (W == NULL); /* never reduced before */
+        REL_t *rel;
+
+        for (j=1,rel = cache.chk + 1; j < l; rel++,j++)
+        {
+          gel(mat,j) = rel->R;
+          if (!rel->relaut)
+            gel(emb,j) = get_log_embed(rel, M, RU, R1, PRECREG);
+          else
+            gel(emb,j) = perm_log_embed(gel(emb, j-rel->relorig),
+                                        gel(F.embperm, rel->relaut));
+        }
+        if (DEBUGLEVEL) timer_printf(&T, "floating point embeddings");
+        if (first) {
+          C = emb;
+          W = hnfspec_i(mat, F.perm, &dep, &B, &C, F.subFB ? lg(F.subFB)-1:0);
+        }
+        else
+          W = hnfadd_i(W, F.perm, &dep, &B, &C, mat, emb);
+        gerepileall(av2, 4, &W,&C,&B,&dep);
+        cache.chk = cache.last;
+        if (DEBUGLEVEL)
+        {
+          if (first)
+            timer_printf(&T, "hnfspec [%ld x %ld]", lg(F.perm)-1, l-1);
+          else
+            timer_printf(&T, "hnfadd (%ld + %ld)", l-1, lg(dep)-1);
+        }
+      }
+      else if (!W)
+      {
+        need = old_need;
+        F.L_jid = vecslice(F.perm, 1, need);
+        continue;
+      }
+      need = F.KC - (lg(W)-1) - (lg(B)-1);
+      /* FIXME: replace by err(e_BUG,"") */
+      if (!need && cache.missing)
+      { /* The test above will never be true except if 27449|class number,
+         * but the code implicitely assumes that if we have maximal rank
+         * for the ideal lattice, then cache.missing == 0. */
+        for (i = 1; cache.missing; i++)
+          if (!mael(cache.basis, i, i))
+          {
+            long j;
+            mael(cache.basis, i, i) = 1;
+            cache.missing--;
+            for (j = i+1; j <= F.KC; j++) mael(cache.basis, j, i) = 0;
+          }
+      }
+      zc = (lg(C)-1) - (lg(B)-1) - (lg(W)-1);
+      if (zc < RU-1)
+      {
+        /* need more columns for units */
+        need += RU-1 - zc;
+        if (need > F.KC) need = F.KC;
+      }
+      if (need)
+      { /* dependent rows */
+        F.L_jid = vecslice(F.perm, 1, need);
+        vecsmall_sort(F.L_jid);
+        if (need != old_need) nreldep = 0;
+        old_need = need;
+      }
+      else
+      {
+        /* If the relation lattice is too small, check will be > 1 and we
+         * will do a new run of small_norm/rnd_rel asking for 1 relation.
+         * However they tend to give a relation involving the first element
+         * of L_jid. We thus permute which element is the first of L_jid in
+         * order to increase the probability of finding a good relation, i.e.
+         * one that increases the relation lattice. */
+        if (lg(W) > 2 && squash_index % (lg(W) - 1))
+        {
+          long j, l = lg(W) - 1;
+          F.L_jid = leafcopy(F.perm);
+          for (j = 1; j <= l; j++)
+            F.L_jid[j] = F.perm[1 + (j + squash_index - 1) % l];
+        }
+        else
+          F.L_jid = F.perm;
+        squash_index++;
+      }
+    }
+    while (need);
+    if (!A)
+    {
+      small_fail = 0; fail_limit = maxss(F.KC / FAIL_DIVISOR, MINFAIL);
+      old_need = 0;
+    }
+    A = vecslice(C, 1, zc); /* cols corresponding to units */
+    R = compute_multiple_of_R(A, RU, N, &need, &lambda);
+    if (need < old_need) small_fail = 0;
+    old_need = need;
+    if (!lambda) { precpb = "bestappr"; continue; }
+    if (!R)
+    { /* not full rank for units */
+      if (DEBUGLEVEL) err_printf("regulator is zero.\n");
+      if (!need) precpb = "regulator";
+      continue;
+    }
+
+    h = ZM_det_triangular(W);
+    if (DEBUGLEVEL) err_printf("\n#### Tentative class number: %Ps\n", h);
+
+    switch (compute_R(lambda, mulir(h,invhr), &L, &R, &T))
+    {
+      case fupb_RELAT:
+        need = 1; /* not enough relations */
+        continue;
+      case fupb_PRECI: /* prec problem unless we cheat on Bach constant */
+        if ((precdouble&7) == 7 && LIMC<=LIMCMAX/6) goto START;
+        precpb = "compute_R";
+        continue;
+    }
+    /* DONE */
+
+    if (F.KCZ2 > F.KCZ)
+    {
+      if (F.sfb_chg && !subFB_change(&F)) goto START;
+      if (!be_honest(&F, nf, auts, fact)) goto START;
+      if (DEBUGLEVEL) timer_printf(&T, "to be honest");
+    }
+    F.KCZ2 = 0; /* be honest only once */
+
+    /* fundamental units */
+    {
+      pari_sp av3 = avma;
+      GEN AU, U, H, v = extract_full_lattice(L); /* L may be very large */
+      long e;
+      if (v)
+      {
+        A = vecpermute(A, v);
+        L = vecpermute(L, v);
+      }
+      /* arch. components of fund. units */
+      H = ZM_hnflll(L, &U, 1); U = vecslice(U, lg(U)-(RU-1), lg(U)-1);
+      U = ZM_mul(U, ZM_lll(H, 0.99, LLL_IM));
+      AU = RgM_mul(A, U);
+      A = cleanarch(AU, N, PRECREG);
+      if (DEBUGLEVEL) timer_printf(&T, "cleanarch");
+      if (!A) {
+        precadd = nbits2extraprec( gexpo(AU) + 64 ) - gprecision(AU);
+        if (precadd <= 0) precadd = 1;
+        precpb = "cleanarch"; continue;
+      }
+      fu = getfu(nf, &A, &e, PRECREG);
+      if (DEBUGLEVEL) timer_printf(&T, "getfu");
+      if ((flun & nf_FORCE) && typ(fu) == t_MAT)
+      { /* units not found but we want them */
+        if (e > 0) pari_err_OVERFLOW("bnfinit [fundamental units too large]");
+        if (e < 0) precadd = nbits2extraprec( (-e - (BITS_IN_LONG - 1)) + 64);
+        avma = av3; precpb = "getfu"; continue;
+      }
+    }
+    /* class group generators */
+    i = lg(C)-zc; C += zc; C[0] = evaltyp(t_MAT)|evallg(i);
+    C0 = C; C = cleanarch(C, N, PRECREG);
+    if (!C) {
+      precadd = nbits2extraprec( gexpo(C0) + 64 ) - gprecision(C0);
+      if (precadd <= 0) precadd = 1;
+      precpb = "cleanarch";
+    }
+  } while (need || precpb);
+
+  delete_cache(&cache); delete_FB(&F); free_GRHcheck(&GRHcheck);
+  Vbase = vecpermute(F.LP, F.perm);
+  class_group_gen(nf,W,C,Vbase,PRECREG,NULL, &clg1, &clg2);
+  res = get_clfu(clg1, R, zu, fu);
+  res = buchall_end(nf,res,clg2,W,B,A,C,Vbase);
+  res = gerepilecopy(av0, res); if (precdouble) gunclone(nf);
+  return res;
+}
diff --git a/src/basemath/buch3.c b/src/basemath/buch3.c
new file mode 100644
index 0000000..9af8d7d
--- /dev/null
+++ b/src/basemath/buch3.c
@@ -0,0 +1,2406 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                       RAY CLASS FIELDS                          */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* Faster than Buchray (because it can use nfsign_units: easier nfarchstar) */
+GEN
+buchnarrow(GEN bnf)
+{
+  GEN nf, cyc, gen, A, NO, GD, v, invpi, logs, R, basecl, met, u1, archp;
+  long r1, j, ngen, t, RU;
+  pari_sp av = avma;
+
+  bnf = checkbnf(bnf);
+  nf = bnf_get_nf(bnf); r1 = nf_get_r1(nf);
+
+  if (!r1) return gcopy( bnf_get_clgp(bnf) );
+
+  /* simplified version of nfsign_units; r1 > 0 so bnf.tu = -1 */
+  archp = identity_perm(r1);
+  A = bnf_get_logfu(bnf); RU = lg(A)+1;
+  invpi = invr( mppi(nf_get_prec(nf)) );
+  v = cgetg(RU,t_MAT); gel(v, 1) = const_vecsmall(r1, 1); /* nfsign(-1) */
+  for (j=2; j<RU; j++) gel(v,j) = nfsign_from_logarch(gel(A,j-1), invpi, archp);
+  /* up to here */
+
+  cyc = bnf_get_cyc(bnf);
+  gen = bnf_get_gen(bnf);
+  v = Flm_image(v, 2);
+  t = lg(v)-1;
+  if (t == r1) { avma = av; return gcopy( bnf_get_clgp(bnf) ); }
+  NO = shifti(bnf_get_no(bnf), r1-t);
+
+  ngen = lg(gen)-1;
+  gen = vec_lengthen(gen, r1 + (ngen-t));
+  v = archstar_full_rk(NULL, nf_get_M(nf), v, gen + (ngen-t));
+  v = rowslice(v, t+1, r1);
+
+  logs = cgetg(ngen+1,t_MAT); GD = gmael(bnf,9,3);
+  for (j=1; j<=ngen; j++)
+  {
+    GEN z = nfsign_from_logarch(gel(GD,j), invpi, archp);
+    gel(logs,j) = zc_to_ZC( Flm_Flc_mul(v, z, 2) );
+  }
+  /* [ cyc  0 ]
+   * [ logs 2 ] = relation matrix for Cl_f */
+  R = shallowconcat(
+    vconcat(diagonal_shallow(cyc), logs),
+    vconcat(zeromat(ngen, r1-t), scalarmat(gen_2,r1-t))
+  );
+  met = ZM_snf_group(R,NULL,&u1);
+  t = lg(met); basecl = cgetg(t,t_VEC);
+  for (j=1; j<t; j++)
+    gel(basecl,j) = Q_primpart( idealfactorback(nf,gen,gel(u1,j),0) );
+  return gerepilecopy(av, mkvec3(NO, met, basecl));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                  REDUCTION MOD IDELE                           **/
+/**                                                                **/
+/********************************************************************/
+
+static GEN
+compute_fact(GEN nf, GEN u1, GEN gen)
+{
+  GEN G, basecl;
+  long i, j, l = lg(u1), h = lgcols(u1); /* l > 1 */
+
+  basecl = cgetg(l,t_VEC);
+  G = cgetg(3,t_VEC);
+  gel(G,2) = cgetg(1,t_MAT);
+
+  for (j=1; j<l; j++)
+  {
+    GEN g,e, z = NULL;
+    for (i=1; i<h; i++)
+    {
+      e = gcoeff(u1,i,j); if (!signe(e)) continue;
+
+      g = gel(gen,i);
+      if (typ(g) != t_MAT)
+      {
+        if (z)
+          gel(z,2) = famat_mul(gel(z,2), to_famat_shallow(g, e));
+        else
+          z = mkvec2(NULL, to_famat_shallow(g, e));
+        continue;
+      }
+
+      gel(G,1) = g;
+      g = idealpowred(nf,G,e);
+      z = z? idealmulred(nf,z,g): g;
+    }
+    gel(z,2) = famat_reduce(gel(z,2));
+    gel(basecl,j) = z;
+  }
+  return basecl;
+}
+
+static int
+too_big(GEN nf, GEN bet)
+{
+  GEN x = gnorm(coltoalg(nf,bet));
+  switch (typ(x))
+  {
+    case t_INT: return absi_cmp(x, gen_1);
+    case t_FRAC: return absi_cmp(gel(x,1), gel(x,2));
+  }
+  pari_err_BUG("wrong type in too_big");
+  return 0; /* not reached */
+}
+
+/* GTM 193: Algo 4.3.4. Reduce x mod divisor */
+static GEN
+idealmoddivisor_aux(GEN nf, GEN x, GEN divisor, GEN sarch)
+{
+  pari_sp av = avma;
+  GEN a,A,D,G, f = gel(divisor,1);
+
+  if ( is_pm1(gcoeff(f,1,1)) ) /* f = 1 */
+  {
+    G = idealred_elt(nf, x);
+    D = idealred_elt(nf, idealdiv(nf,G,x));
+  }
+  else
+  {/* given coprime integral ideals x and f (f HNF), compute "small"
+    * G in x, such that G = 1 mod (f). GTM 193: Algo 4.3.3 */
+    G = idealaddtoone_i(nf, x, f);
+    D = idealaddtoone_i(nf, idealdiv(nf,G,x), f);
+  }
+  A = nfdiv(nf,D,G);
+  if (too_big(nf,A) > 0) { avma = av; return x; }
+  a = set_sign_mod_divisor(nf, NULL, A, divisor, sarch);
+  if (a != A && too_big(nf,A) > 0) { avma = av; return x; }
+  return idealmul(nf, a, x);
+}
+
+GEN
+idealmoddivisor(GEN bnr, GEN x)
+{
+  GEN bid = bnr_get_bid(bnr), fa2 = gel(bid,4);
+  GEN sarch = gel(fa2,lg(fa2)-1);
+  return idealmoddivisor_aux(checknf(bnr), x, bid_get_mod(bid), sarch);
+}
+
+/* v_pr(L0 * cx) */
+static long
+fast_val(GEN nf,GEN L0,GEN cx,GEN pr)
+{
+  pari_sp av = avma;
+  long v = typ(L0) == t_INT? 0: ZC_nfval(nf,L0,pr);
+  if (cx)
+  {
+    long w = Q_pval(cx, pr_get_p(pr));
+    if (w) v += w * pr_get_e(pr);
+  }
+  avma = av; return v;
+}
+
+/* x coprime to fZ, return y = x mod fZ, y integral */
+static GEN
+make_integral_Z(GEN x, GEN fZ)
+{
+  GEN d, y = Q_remove_denom(x, &d);
+  if (d) y = FpC_Fp_mul(y, Fp_inv(d, fZ), fZ);
+  return y;
+}
+
+/* p pi^(-1) mod f */
+static GEN
+get_pinvpi(GEN nf, GEN fZ, GEN p, GEN pi, GEN *v)
+{
+  if (!*v) {
+    GEN invpi = nfinv(nf, pi);
+    *v = make_integral_Z(RgC_Rg_mul(invpi, p), mulii(p, fZ));
+  }
+  return *v;
+}
+/* p pi^(-1) mod f */
+static GEN
+get_pi(GEN F, GEN pr, GEN *v)
+{
+  if (!*v) *v = unif_mod_fZ(pr, F);
+  return *v;
+}
+
+static GEN
+compute_raygen(GEN nf, GEN u1, GEN gen, GEN bid)
+{
+  GEN f, fZ, basecl, module, fa, fa2, pr, t, EX, sarch, cyc, F;
+  GEN listpr, vecpi, vecpinvpi;
+  long i,j,l,lp;
+
+  if (lg(u1) == 1) return cgetg(1, t_VEC);
+
+  /* basecl = generators in factored form */
+  basecl = compute_fact(nf,u1,gen);
+
+  module = bid_get_mod(bid);
+  cyc = bid_get_cyc(bid); EX = gel(cyc,1); /* exponent of (O/f)^* */
+  f   = gel(module,1); fZ = gcoeff(f,1,1);
+  fa  = gel(bid,3);
+  fa2 = gel(bid,4); sarch = gel(fa2, lg(fa2)-1);
+  listpr = gel(fa,1); F = init_unif_mod_fZ(listpr);
+
+  lp = lg(listpr);
+  vecpinvpi = cgetg(lp, t_VEC);
+  vecpi  = cgetg(lp, t_VEC);
+  for (i=1; i<lp; i++)
+  {
+    pr = gel(listpr,i);
+    gel(vecpi,i)    = NULL; /* to be computed if needed */
+    gel(vecpinvpi,i) = NULL; /* to be computed if needed */
+  }
+
+  l = lg(basecl);
+  for (i=1; i<l; i++)
+  {
+    GEN p, pi, pinvpi, dmulI, mulI, G, I, A, e, L, newL;
+    long la, v, k;
+    pari_sp av;
+    /* G = [I, A=famat(L,e)] is a generator, I integral */
+    G = gel(basecl,i);
+    I = gel(G,1);
+    A = gel(G,2);
+      L = gel(A,1);
+      e = gel(A,2);
+    /* if no reduction took place in compute_fact, everybody is still coprime
+     * to f + no denominators */
+    if (!I)
+    {
+      gel(basecl,i) = famat_to_nf_moddivisor(nf, L, e, bid);
+      continue;
+    }
+    if (lg(A) == 1)
+    {
+      gel(basecl,i) = I;
+      continue;
+    }
+
+    /* compute mulI so that mulI * I coprime to f
+     * FIXME: use idealcoprime ??? (Less efficient. Fix idealcoprime!) */
+    dmulI = mulI = NULL;
+    for (j=1; j<lp; j++)
+    {
+      pr = gel(listpr,j);
+      v  = idealval(nf, I, pr);
+      if (!v) continue;
+      p  = pr_get_p(pr);
+      pi = get_pi(F, pr, &gel(vecpi,j));
+      pinvpi = get_pinvpi(nf, fZ, p, pi, &gel(vecpinvpi,j));
+      t = nfpow_u(nf, pinvpi, (ulong)v);
+      mulI = mulI? nfmuli(nf, mulI, t): t;
+      t = powiu(p, v);
+      dmulI = dmulI? mulii(dmulI, t): t;
+    }
+
+    /* make all components of L coprime to f.
+     * Assuming (L^e * I, f) = 1, then newL^e * mulI = L^e */
+    la = lg(e); newL = cgetg(la, t_VEC);
+    for (k=1; k<la; k++)
+    {
+      GEN cx, LL = nf_to_scalar_or_basis(nf, gel(L,k));
+      GEN L0 = Q_primitive_part(LL, &cx); /* LL = L0*cx (faster nfval) */
+      for (j=1; j<lp; j++)
+      {
+        pr = gel(listpr,j);
+        v  = fast_val(nf, L0,cx, pr); /* = val_pr(LL) */
+        if (!v) continue;
+        p  = pr_get_p(pr);
+        pi = get_pi(F, pr, &gel(vecpi,j));
+        if (v > 0)
+        {
+          pinvpi = get_pinvpi(nf, fZ, p, pi, &gel(vecpinvpi,j));
+          t = nfpow_u(nf,pinvpi, (ulong)v);
+          LL = nfmul(nf, LL, t);
+          LL = RgC_Rg_div(LL, powiu(p, v));
+        }
+        else
+        {
+          t = nfpow_u(nf,pi,(ulong)(-v));
+          LL = nfmul(nf, LL, t);
+        }
+      }
+      LL = make_integral(nf,LL,f,listpr);
+      gel(newL,k) = typ(LL) == t_INT? LL: FpC_red(LL, fZ);
+    }
+
+    av = avma;
+    /* G in nf, = L^e mod f */
+    G = famat_to_nf_modideal_coprime(nf, newL, e, f, EX);
+    if (mulI)
+    {
+      G = nfmuli(nf, G, mulI);
+      G = ZC_hnfrem(G, ZM_Z_mul(f, dmulI));
+    }
+    G = set_sign_mod_divisor(nf,A,G,module,sarch);
+    I = idealmul(nf,I,G);
+    if (dmulI) I = ZM_Z_divexact(I, dmulI);
+    /* more or less useless, but cheap at this point */
+    I = idealmoddivisor_aux(nf,I,module,sarch);
+    gel(basecl,i) = gerepilecopy(av, I);
+  }
+  return basecl;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                   INIT RAY CLASS GROUP                         **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+check_subgroup(GEN bnr, GEN H, GEN *clhray, int triv_is_NULL)
+{
+  GEN h, cyc = bnr_get_cyc(bnr);
+  if (H && gequal0(H)) H = NULL;
+  if (H)
+  {
+    if (typ(H) != t_MAT) pari_err_TYPE("check_subgroup",H);
+    RgM_check_ZM(H, "check_subgroup");
+    H = ZM_hnfmodid(H, cyc);
+    h = ZM_det_triangular(H);
+    if (equalii(h, *clhray)) H = NULL; else *clhray = h;
+  }
+  if (!H && !triv_is_NULL) H = diagonal_shallow(cyc);
+  return H;
+}
+
+static GEN
+get_dataunit(GEN bnf, GEN bid)
+{
+  GEN D, cyc = bid_get_cyc(bid), U = init_units(bnf), nf = bnf_get_nf(bnf);
+  long i, l;
+  zlog_S S; init_zlog_bid(&S, bid);
+  D = nfsign_units(bnf, S.archp, 1); l = lg(D);
+  for (i = 1; i < l; i++)
+  {
+    GEN v = zlog(nf, gel(U,i),gel(D,i), &S);
+    gel(D,i) = vecmodii(ZM_ZC_mul(S.U, v), cyc);
+  }
+  return D;
+}
+
+GEN
+Buchray(GEN bnf, GEN module, long flag)
+{
+  GEN nf, cyc, gen, Gen, u, clg, logs, p1, h, met, u1, u2, U, cycgen;
+  GEN bid, cycbid, genbid, y, funits, H, Hi, c1, c2, El;
+  long RU, Ri, j, ngen, lh;
+  const long add_gen = flag & nf_GEN;
+  const long do_init = flag & nf_INIT;
+  pari_sp av = avma;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  funits = bnf_get_fu(bnf); RU = lg(funits);
+  El = Gen = NULL; /* gcc -Wall */
+  cyc = bnf_get_cyc(bnf);
+  gen = bnf_get_gen(bnf); ngen = lg(cyc)-1;
+
+  bid = checkbid_i(module);
+  if (!bid) bid = Idealstar(nf,module,nf_GEN|nf_INIT);
+  cycbid = bid_get_cyc(bid);
+  genbid = bid_get_gen(bid);
+  Ri = lg(cycbid)-1; lh = ngen+Ri;
+  if (Ri || add_gen || do_init)
+  {
+    GEN fx = gel(bid,3);
+    El = cgetg(ngen+1,t_VEC);
+    for (j=1; j<=ngen; j++)
+    {
+      p1 = idealcoprimefact(nf, gel(gen,j), fx);
+      if (RgV_isscalar(p1)) p1 = gel(p1,1);
+      gel(El,j) = p1;
+    }
+  }
+  if (add_gen)
+  {
+    Gen = cgetg(lh+1,t_VEC);
+    for (j=1; j<=ngen; j++) gel(Gen,j) = idealmul(nf, gel(El,j), gel(gen,j));
+    for (   ; j<=lh; j++)   gel(Gen,j) = gel(genbid, j-ngen);
+  }
+  if (!Ri)
+  {
+    clg = cgetg(add_gen? 4: 3,t_VEC);
+    if (add_gen) gel(clg,3) = Gen;
+    gel(clg,1) = bnf_get_no(bnf);
+    gel(clg,2) = cyc;
+    if (!do_init) return gerepilecopy(av,clg);
+    y = cgetg(7,t_VEC);
+    gel(y,1) = bnf;
+    gel(y,2) = bid;
+    gel(y,3) = El;
+    gel(y,4) = matid(ngen);
+    gel(y,5) = clg;
+    gel(y,6) = mkvec3(cgetg(1,t_MAT), matid(RU), gen_1);
+    return gerepilecopy(av,y);
+  }
+
+  cycgen = check_and_build_cycgen(bnf);
+  /* (log(Units)|D) * u = (0 | H) */
+  if (do_init)
+  {
+    GEN D = shallowconcat(get_dataunit(bnf, bid), diagonal_shallow(cycbid));
+    H = ZM_hnfall(D, do_init? &u: NULL, 1);
+  }
+  else
+    H = ZM_hnfmodid(get_dataunit(bnf, bid), cycbid);
+  logs = cgetg(ngen+1, t_MAT);
+  /* FIXME: cycgen[j] is not necessarily coprime to bid, but it is made coprime
+   * in famat_zlog using canonical uniformizers [from bid data]: no need to
+   * correct it here. The same ones will be used in bnrisprincipal. Hence
+   * modification by El is useless. */
+  for (j=1; j<=ngen; j++)
+  {
+    p1 = gel(cycgen,j);
+    if (typ(gel(El,j)) != t_INT) /* <==> != 1 */
+    {
+      GEN F = to_famat_shallow(gel(El,j), gel(cyc,j));
+      p1 = famat_mul(F, p1);
+    }
+    gel(logs,j) = ideallog(nf, p1, bid); /* = log(Gen[j]) */
+  }
+  /* [ cyc  0 ]
+   * [-logs H ] = relation matrix for Cl_f */
+  h = shallowconcat(
+    vconcat(diagonal_shallow(cyc), gneg_i(logs)),
+    vconcat(zeromat(ngen, Ri), H)
+  );
+  met = ZM_snf_group(ZM_hnf(h), &U, add_gen? &u1: NULL);
+  clg = cgetg(add_gen? 4: 3, t_VEC);
+  gel(clg,1) = detcyc(met, &j);
+  gel(clg,2) = met;
+  if (add_gen) gel(clg,3) = compute_raygen(nf,u1,Gen,bid);
+  if (!do_init) return gerepilecopy(av, clg);
+
+  u2 = cgetg(Ri+1,t_MAT);
+  u1 = cgetg(RU+1,t_MAT);
+  for (j=1; j<=RU; j++) { gel(u1,j) = gel(u,j); setlg(u[j],RU+1); }
+  u += RU;
+  for (j=1; j<=Ri; j++) { gel(u2,j) = gel(u,j); setlg(u[j],RU+1); }
+
+  /* log(Units) U2 = H (mod D)
+   * log(Units) U1 = 0 (mod D) */
+  u1 = ZM_lll(u1, 0.99, LLL_INPLACE);
+  Hi = Q_primitive_part(RgM_inv_upper(H), &c1);
+  u2 = Q_primitive_part(ZM_mul(ZM_reducemodmatrix(u2,u1), Hi), &c2);
+  c1 = mul_content(c1, c2);
+  if (!c1)
+    c2 = gen_1;
+  else if (typ(c1) == t_INT)
+  {
+    if (!is_pm1(c1)) u2 = ZM_Z_mul(u2, c1);
+    c2 = gen_1;
+  }
+  else /* t_FRAC */
+  {
+    c2 = gel(c1,2);
+    c1 = gel(c1,1);
+    if (!is_pm1(c1)) u2 = ZM_Z_mul(u2, c1);
+  }
+  y = cgetg(7,t_VEC);
+  gel(y,1) = bnf;
+  gel(y,2) = bid;
+  gel(y,3) = El;
+  gel(y,4) = U;
+  gel(y,5) = clg;
+  gel(y,6) = mkvec3(u2,u1,c2); /* u2/c2 = H^(-1) (mod Im u1) */
+  return gerepilecopy(av,y);
+}
+
+GEN
+bnrinit0(GEN bnf, GEN ideal, long flag)
+{
+  switch(flag)
+  {
+    case 0: flag = nf_INIT; break;
+    case 1: flag = nf_INIT | nf_GEN; break;
+    default: pari_err_FLAG("bnrinit");
+  }
+  return Buchray(bnf,ideal,flag);
+}
+
+GEN
+bnrclassno(GEN bnf,GEN ideal)
+{
+  GEN nf, h, D, bid, cycbid;
+  pari_sp av = avma;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  h = bnf_get_no(bnf); /* class number */
+  bid = checkbid_i(ideal);
+  if (!bid) bid = Idealstar(nf,ideal,nf_INIT);
+  cycbid = bid_get_cyc(bid);
+  if (lg(cycbid) == 1) { avma = av; return icopy(h); }
+  D = get_dataunit(bnf, bid); /* (Z_K/f)^* / units ~ Z^n / D */
+  D = ZM_hnfmodid(D,cycbid);
+  return gerepileuptoint(av, mulii(h, ZM_det_triangular(D)));
+}
+GEN
+bnrclassno0(GEN A, GEN B, GEN C)
+{
+  pari_sp av = avma;
+  GEN h, H = NULL;
+  /* adapted from ABC_to_bnr, avoid costly bnrinit if possible */
+  if (typ(A) == t_VEC)
+    switch(lg(A))
+    {
+      case 7: /* bnr */
+        checkbnr(A); H = B;
+        break;
+      case 11: /* bnf */
+        if (!B) pari_err_TYPE("bnrclassno [bnf+missing conductor]",A);
+        if (!C) return bnrclassno(A, B);
+        A = Buchray(A, B, nf_INIT); H = C;
+        break;
+      default: checkbnf(A);/*error*/
+    }
+  else checkbnf(A);/*error*/
+
+  h = bnr_get_no(A);
+  H = check_subgroup(A, H, &h, 1);
+  if (!H) { avma = av; return icopy(h); }
+  return gerepileuptoint(av, h);
+}
+
+GEN
+bnrisprincipal(GEN bnr, GEN x, long flag)
+{
+  pari_sp av = avma;
+  GEN bnf, nf, bid, U, El, ep, L, idep, ex, cycray, cycbid, alpha;
+
+  checkbnr(bnr);
+  cycray = bnr_get_cyc(bnr);
+  if (lg(cycray) == 1 && !(flag & nf_GEN)) return cgetg(1,t_COL);
+
+  bnf = bnr_get_bnf(bnr); nf = bnf_get_nf(bnf);
+  bid = bnr_get_bid(bnr);
+  cycbid = bid_get_cyc(bid);
+  El  = gel(bnr,3);
+  U   = gel(bnr,4);
+
+  if (typ(x) == t_VEC && lg(x) == 3)
+  { idep = gel(x,2); x = gel(x,1); }  /* precomputed */
+  else
+    idep = bnfisprincipal0(bnf, x, nf_FORCE|nf_GENMAT);
+  ep  = gel(idep,1);
+  if (lg(cycbid) > 1)
+  {
+    GEN beta = gel(idep,2);
+    long i, j = lg(ep);
+    for (i=1; i<j; i++) /* modify beta as if gen -> El.gen (coprime to bid) */
+      if (typ(gel(El,i)) != t_INT && signe(gel(ep,i))) /* <==> != 1 */
+        beta = famat_mul(to_famat_shallow(gel(El,i), negi(gel(ep,i))), beta);
+    ep = shallowconcat(ep, ideallog(nf,beta,bid));
+  }
+  ex = vecmodii(ZM_ZC_mul(U, ep), cycray);
+  if (!(flag & nf_GEN)) return gerepileupto(av, ex);
+
+  /* compute generator */
+  L = isprincipalfact(bnf, x, bnr_get_gen(bnr), ZC_neg(ex),
+                      nf_GENMAT|nf_GEN_IF_PRINCIPAL|nf_FORCE);
+  if (L == gen_0) pari_err_BUG("isprincipalray");
+  alpha = nffactorback(nf, L, NULL);
+  if (lg(cycbid) > 1)
+  {
+    GEN v = gel(bnr,6), u2 = gel(v,1), u1 = gel(v,2), du2 = gel(v,3);
+    GEN y = ZM_ZC_mul(u2, ideallog(nf, L, bid));
+    if (!is_pm1(du2)) y = ZC_Z_divexact(y,du2);
+    y = ZC_reducemodmatrix(y, u1);
+    alpha = nfdiv(nf, alpha, nffactorback(nf, init_units(bnf), y));
+  }
+  return gerepilecopy(av, mkvec2(ex,alpha));
+}
+
+GEN
+isprincipalray(GEN bnr, GEN x)
+{
+  return bnrisprincipal(bnr,x,0);
+}
+
+GEN
+isprincipalraygen(GEN bnr, GEN x)
+{
+  return bnrisprincipal(bnr,x,nf_GEN);
+}
+
+/* N! / N^N * (4/pi)^r2 * sqrt(|D|) */
+GEN
+minkowski_bound(GEN D, long N, long r2, long prec)
+{
+  pari_sp av = avma;
+  GEN c = divri(mpfactr(N,prec), powuu(N,N));
+  if (r2) c = mulrr(c, powru(divur(4,mppi(prec)), r2));
+  c = mulrr(c, gsqrt(absi(D),prec));
+  return gerepileuptoleaf(av, c);
+}
+
+/* DK = |dK| */
+static GEN
+zimmertbound(long N,long R2,GEN DK)
+{
+  pari_sp av = avma;
+  GEN w;
+
+  if (N < 2) return gen_1;
+  if (N < 21)
+  {
+    const double c[19][11] = {
+{/*2*/  0.6931,     0.45158},
+{/*3*/  1.71733859, 1.37420604},
+{/*4*/  2.91799837, 2.50091538, 2.11943331},
+{/*5*/  4.22701425, 3.75471588, 3.31196660},
+{/*6*/  5.61209925, 5.09730381, 4.60693851, 4.14303665},
+{/*7*/  7.05406203, 6.50550021, 5.97735406, 5.47145968},
+{/*8*/  8.54052636, 7.96438858, 7.40555445, 6.86558259, 6.34608077},
+{/*9*/ 10.0630022,  9.46382812, 8.87952524, 8.31139202, 7.76081149},
+{/*10*/11.6153797, 10.9966020, 10.3907654,  9.79895170, 9.22232770, 8.66213267},
+{/*11*/13.1930961, 12.5573772, 11.9330458, 11.3210061, 10.7222412, 10.1378082},
+{/*12*/14.7926394, 14.1420915, 13.5016616, 12.8721114, 12.2542699, 11.6490374,
+       11.0573775},
+{/*13*/16.4112395, 15.7475710, 15.0929680, 14.4480777, 13.8136054, 13.1903162,
+       12.5790381},
+{/*14*/18.0466672, 17.3712806, 16.7040780, 16.0456127, 15.3964878, 14.7573587,
+       14.1289364, 13.5119848},
+{/*15*/19.6970961, 19.0111606, 18.3326615, 17.6620757, 16.9999233, 16.3467686,
+       15.7032228, 15.0699480},
+{/*16*/21.3610081, 20.6655103, 19.9768082, 19.2953176, 18.6214885, 17.9558093,
+       17.2988108, 16.6510652, 16.0131906},
+
+{/*17*/23.0371259, 22.3329066, 21.6349299, 20.9435607, 20.2591899, 19.5822454,
+       18.9131878, 18.2525157, 17.6007672},
+
+{/*18*/24.7243611, 24.0121449, 23.3056902, 22.6053167, 21.9113705, 21.2242247,
+       20.5442836, 19.8719830, 19.2077941, 18.5522234},
+
+{/*19*/26.4217792, 25.7021950, 24.9879497, 24.2793271, 23.5766321, 22.8801952,
+       22.1903709, 21.5075437, 20.8321263, 20.1645647},
+{/*20*/28.1285704, 27.4021674, 26.6807314, 25.9645140, 25.2537867, 24.5488420,
+       23.8499943, 23.1575823, 22.4719720, 21.7935548, 21.1227537}
+    };
+    w = mulrr(dbltor(exp(-c[N-2][R2])), gsqrt(DK,DEFAULTPREC));
+  }
+  else
+  {
+    w = minkowski_bound(DK, N, R2, DEFAULTPREC);
+  }
+  return gerepileuptoint(av, ceil_safe(w));
+}
+
+/* return \gamma_n^n if known, an upper bound otherwise */
+static GEN
+hermiteconstant(long n)
+{
+  GEN h,h1;
+  pari_sp av;
+
+  switch(n)
+  {
+    case 1: return gen_1;
+    case 2: return mkfrac(utoipos(4), utoipos(3));
+    case 3: return gen_2;
+    case 4: return utoipos(4);
+    case 5: return utoipos(8);
+    case 6: return mkfrac(utoipos(64), utoipos(3));
+    case 7: return utoipos(64);
+    case 8: return utoipos(256);
+  }
+  av = avma;
+  h  = powru(divur(2,mppi(DEFAULTPREC)), n);
+  h1 = sqrr(ggamma(gdivgs(utoipos(n+4),2),DEFAULTPREC));
+  return gerepileuptoleaf(av, mulrr(h,h1));
+}
+
+/* 1 if L (= nf != Q) primitive for sure, 0 if MAYBE imprimitive (may have a
+ * subfield K) */
+static long
+isprimitive(GEN nf)
+{
+  long p, i, l, ep, N = nf_get_degree(nf);
+  GEN D, fa;
+
+  p = ucoeff(factoru(N), 1,1); /* smallest prime | N */
+  if (p == N) return 1; /* prime degree */
+
+  /* N = [L:Q] = product of primes >= p, same is true for [L:K]
+   * d_L = t d_K^[L:K] --> check that some q^p divides d_L */
+  D = nf_get_disc(nf);
+  fa = gel(absi_factor_limit(D,0),2); /* list of v_q(d_L). Don't check large primes */
+  if (mod2(D)) i = 1;
+  else
+  { /* q = 2 */
+    ep = itos(gel(fa,1));
+    if ((ep>>1) >= p) return 0; /* 2 | d_K ==> 4 | d_K */
+    i = 2;
+  }
+  l = lg(fa);
+  for ( ; i < l; i++)
+  {
+    ep = itos(gel(fa,i));
+    if (ep >= p) return 0;
+  }
+  return 1;
+}
+
+static GEN
+dft_bound(void)
+{
+  if (DEBUGLEVEL>1) err_printf("Default bound for regulator: 0.2\n");
+  return dbltor(0.2);
+}
+
+static GEN
+regulatorbound(GEN bnf)
+{
+  long N, R1, R2, R;
+  GEN nf, dK, p1, c1;
+
+  nf = bnf_get_nf(bnf); N = nf_get_degree(nf);
+  if (!isprimitive(nf)) return dft_bound();
+
+  dK = absi(nf_get_disc(nf));
+  nf_get_sign(nf, &R1, &R2); R = R1+R2-1;
+  c1 = (!R2 && N<12)? int2n(N & (~1UL)): powuu(N,N);
+  if (cmpii(dK,c1) <= 0) return dft_bound();
+
+  p1 = sqrr(glog(gdiv(dK,c1),DEFAULTPREC));
+  p1 = divru(gmul2n(powru(divru(mulru(p1,3),N*(N*N-1)-6*R2),R),R2), N);
+  p1 = sqrtr(gdiv(p1, hermiteconstant(R)));
+  if (DEBUGLEVEL>1) err_printf("Mahler bound for regulator: %Ps\n",p1);
+  return gmax(p1, dbltor(0.2));
+}
+
+static int
+is_unit(GEN M, long r1, GEN x)
+{
+  pari_sp av = avma;
+  GEN Nx = ground( embed_norm(RgM_zc_mul(M,x), r1) );
+  int ok = is_pm1(Nx);
+  avma = av; return ok;
+}
+
+/* FIXME: should use smallvectors */
+static GEN
+minimforunits(GEN nf, long BORNE, ulong w)
+{
+  const long prec = MEDDEFAULTPREC;
+  long n, r1, i, j, k, s, *x, cnt = 0;
+  pari_sp av = avma;
+  GEN u, r, M;
+  double p, norme, normin, normax;
+  double **q,*v,*y,*z;
+  double eps=0.000001, BOUND = BORNE * 1.00001;
+
+  if (DEBUGLEVEL>=2)
+  {
+    err_printf("Searching minimum of T2-form on units:\n");
+    if (DEBUGLEVEL>2) err_printf("   BOUND = %ld\n",BORNE);
+    err_flush();
+  }
+  n = nf_get_degree(nf); r1 = nf_get_r1(nf);
+  minim_alloc(n+1, &q, &x, &y, &z, &v);
+  M = gprec_w(nf_get_M(nf), prec);
+  r = gaussred_from_QR(nf_get_G(nf), prec);
+  for (j=1; j<=n; j++)
+  {
+    v[j] = gtodouble(gcoeff(r,j,j));
+    for (i=1; i<j; i++) q[i][j] = gtodouble(gcoeff(r,i,j));
+  }
+  normax = 0.; normin = (double)BOUND;
+  s=0; k=n; y[n]=z[n]=0;
+  x[n] = (long)(sqrt(BOUND/v[n]));
+
+  for(;;x[1]--)
+  {
+    do
+    {
+      if (k>1)
+      {
+        long l = k-1;
+        z[l] = 0;
+        for (j=k; j<=n; j++) z[l] += q[l][j]*x[j];
+        p = (double)x[k] + z[k];
+        y[l] = y[k] + p*p*v[k];
+        x[l] = (long)floor(sqrt((BOUND-y[l])/v[l])-z[l]);
+        k = l;
+      }
+      for(;;)
+      {
+        p = (double)x[k] + z[k];
+        if (y[k] + p*p*v[k] <= BOUND) break;
+        k++; x[k]--;
+      }
+    }
+    while (k>1);
+    if (!x[1] && y[1]<=eps) break;
+
+    if (DEBUGLEVEL>8){ err_printf("."); err_flush(); }
+    if (++cnt == 5000) return NULL; /* too expensive */
+
+    p = (double)x[1] + z[1]; norme = y[1] + p*p*v[1] + eps;
+    if (norme > normax) normax = norme;
+    if (is_unit(M, r1, x)
+    && (norme > 2*n  /* exclude roots of unity */
+        || !ZV_isscalar(nfpow_u(nf, zc_to_ZC(x), w))))
+    {
+      if (norme < normin) normin = norme;
+      if (DEBUGLEVEL>=2) { err_printf("*"); err_flush(); }
+    }
+
+  }
+  if (DEBUGLEVEL>=2){ err_printf("\n"); err_flush(); }
+  avma = av; u = cgetg(4,t_VEC);
+  gel(u,1) = stoi(s<<1);
+  gel(u,2) = dbltor(normax);
+  gel(u,3) = dbltor(normin);
+  return u;
+}
+
+#undef NBMAX
+static int
+is_zero(GEN x, long bitprec) { return (gexpo(x) < -bitprec); }
+
+static int
+is_complex(GEN x, long bitprec) { return !is_zero(imag_i(x), bitprec); }
+
+/* assume M_star t_REAL
+ * FIXME: what does this do ? To be rewritten */
+static GEN
+compute_M0(GEN M_star,long N)
+{
+  long m1,m2,n1,n2,n3,lr,lr1,lr2,i,j,l,vx,vy,vz,vM;
+  GEN pol,p1,p2,p3,p4,p5,p6,p7,p8,p9,u,v,w,r,r1,r2,M0,M0_pro,S,P,M;
+  GEN f1,f2,f3,g1,g2,g3,pg1,pg2,pg3,pf1,pf2,pf3,X,Y,Z;
+  long bitprec = 24;
+
+  if (N == 2) return gmul2n(sqrr(gacosh(gmul2n(M_star,-1),0)), -1);
+  vM = fetch_var(); M = pol_x(vM);
+  vz = fetch_var(); Z = pol_x(vz);
+  vy = fetch_var(); Y = pol_x(vy);
+  vx = fetch_var(); X = pol_x(vx);
+
+  M0 = NULL; m1 = N/3;
+  for (n1=1; n1<=m1; n1++) /* 1 <= n1 <= n2 <= n3 < N */
+  {
+    m2 = (N-n1)>>1;
+    for (n2=n1; n2<=m2; n2++)
+    {
+      pari_sp av = avma; n3=N-n1-n2;
+      if (n1==n2 && n1==n3) /* n1 = n2 = n3 = m1 = N/3 */
+      {
+        p1 = divru(M_star, m1);
+        p4 = sqrtr_abs( mulrr(addsr(1,p1),subrs(p1,3)) );
+        p5 = subrs(p1,1);
+        u = gen_1;
+        v = gmul2n(addrr(p5,p4),-1);
+        w = gmul2n(subrr(p5,p4),-1);
+        M0_pro=gmul2n(mulur(m1,addrr(sqrr(logr_abs(v)),sqrr(logr_abs(w)))), -2);
+        if (DEBUGLEVEL>2)
+        {
+          err_printf("[ %ld, %ld, %ld ]: %.28Pg\n",n1,n2,n3,M0_pro);
+          err_flush();
+        }
+        if (!M0 || gcmp(M0_pro,M0) < 0) M0 = M0_pro;
+      }
+      else if (n1==n2 || n2==n3)
+      { /* n3 > N/3 >= n1 */
+        long k = N - 2*n2;
+        p2 = deg1pol_shallow(stoi(-n2), M_star, vx); /* M* - n2 X */
+        p3 = gmul(powuu(k,k),
+                  gpowgs(gsubgs(RgX_Rg_mul(p2, M_star), k*k), n2));
+        pol = gsub(p3, RgX_mul(monomial(powuu(n2,n2), n2, vx),
+                               gpowgs(p2, N-n2)));
+        r = roots(pol, DEFAULTPREC); lr = lg(r);
+        for (i=1; i<lr; i++)
+        {
+          GEN n2S;
+          S = real_i(gel(r,i));
+          if (is_complex(gel(r,i), bitprec) || signe(S) <= 0) continue;
+
+          n2S = mulur(n2,S);
+          p4 = subrr(M_star, n2S);
+          P = divrr(mulrr(n2S,p4), subrs(mulrr(M_star,p4),k*k));
+          p5 = subrr(sqrr(S), gmul2n(P,2));
+          if (gsigne(p5) < 0) continue;
+
+          p6 = sqrtr(p5);
+          v = gmul2n(subrr(S,p6),-1);
+          if (gsigne(v) <= 0) continue;
+
+          u = gmul2n(addrr(S,p6),-1);
+          w = gpow(P, gdivgs(utoineg(n2),k), 0);
+          p6 = mulur(n2, addrr(sqrr(logr_abs(u)), sqrr(logr_abs(v))));
+          M0_pro = gmul2n(addrr(p6, mulur(k, sqrr(logr_abs(w)))),-2);
+          if (DEBUGLEVEL>2)
+          {
+            err_printf("[ %ld, %ld, %ld ]: %.28Pg\n",n1,n2,n3,M0_pro);
+            err_flush();
+          }
+          if (!M0 || gcmp(M0_pro,M0) < 0) M0 = M0_pro;
+        }
+      }
+      else
+      {
+        f1 = gsub(gadd(gmulsg(n1,X),gadd(gmulsg(n2,Y),gmulsg(n3,Z))), M);
+        f2 =         gmulsg(n1,gmul(Y,Z));
+        f2 = gadd(f2,gmulsg(n2,gmul(X,Z)));
+        f2 = gadd(f2,gmulsg(n3,gmul(X,Y)));
+        f2 = gsub(f2,gmul(M,gmul(X,gmul(Y,Z))));
+        f3 = gsub(gmul(gpowgs(X,n1),gmul(gpowgs(Y,n2),gpowgs(Z,n3))), gen_1);
+        /* f1 = n1 X + n2 Y + n3 Z - M */
+        /* f2 = n1 YZ + n2 XZ + n3 XY */
+        /* f3 = X^n1 Y^n2 Z^n3 - 1*/
+        g1=resultant(f1,f2); g1=primpart(g1);
+        g2=resultant(f1,f3); g2=primpart(g2);
+        g3=resultant(g1,g2); g3=primpart(g3);
+        pf1=gsubst(f1,vM,M_star); pg1=gsubst(g1,vM,M_star);
+        pf2=gsubst(f2,vM,M_star); pg2=gsubst(g2,vM,M_star);
+        pf3=gsubst(f3,vM,M_star); pg3=gsubst(g3,vM,M_star);
+        /* g3 = Res_Y,Z(f1,f2,f3) */
+        r = roots(pg3,DEFAULTPREC); lr = lg(r);
+        for (i=1; i<lr; i++)
+        {
+          w = real_i(gel(r,i));
+          if (is_complex(gel(r,i), bitprec) || signe(w) <= 0) continue;
+          p1=gsubst(pg1,vz,w);
+          p2=gsubst(pg2,vz,w);
+          p3=gsubst(pf1,vz,w);
+          p4=gsubst(pf2,vz,w);
+          p5=gsubst(pf3,vz,w);
+          r1 = roots(p1, DEFAULTPREC); lr1 = lg(r1);
+          for (j=1; j<lr1; j++)
+          {
+            v = real_i(gel(r1,j));
+            if (is_complex(gel(r1,j), bitprec) || signe(v) <= 0
+             || !is_zero(gsubst(p2,vy,v), bitprec)) continue;
+
+            p7=gsubst(p3,vy,v);
+            p8=gsubst(p4,vy,v);
+            p9=gsubst(p5,vy,v);
+            r2 = roots(p7, DEFAULTPREC); lr2 = lg(r2);
+            for (l=1; l<lr2; l++)
+            {
+              u = real_i(gel(r2,l));
+              if (is_complex(gel(r2,l), bitprec) || signe(u) <= 0
+               || !is_zero(gsubst(p8,vx,u), bitprec)
+               || !is_zero(gsubst(p9,vx,u), bitprec)) continue;
+
+              M0_pro =              mulur(n1, sqrr(logr_abs(u)));
+              M0_pro = gadd(M0_pro, mulur(n2, sqrr(logr_abs(v))));
+              M0_pro = gadd(M0_pro, mulur(n3, sqrr(logr_abs(w))));
+              M0_pro = gmul2n(M0_pro,-2);
+              if (DEBUGLEVEL>2)
+              {
+               err_printf("[ %ld, %ld, %ld ]: %.28Pg\n",n1,n2,n3,M0_pro);
+               err_flush();
+              }
+              if (!M0 || gcmp(M0_pro,M0) < 0) M0 = M0_pro;
+            }
+          }
+        }
+      }
+      if (!M0) avma = av; else M0 = gerepilecopy(av, M0);
+    }
+  }
+  for (i=1;i<=4;i++) (void)delete_var();
+  return M0? M0: gen_0;
+}
+
+static GEN
+lowerboundforregulator(GEN bnf, GEN units)
+{
+  long i, N, R2, RU = lg(units)-1;
+  GEN nf, M0, M, G, bound, minunit, vecminim;
+
+  if (!RU) return gen_1;
+  nf = bnf_get_nf(bnf);
+  N = nf_get_degree(nf);
+  R2 = nf_get_r2(nf);
+
+  G = nf_get_G(nf);
+  minunit = gnorml2(RgM_RgC_mul(G, gel(units,1))); /* T2(units[1]) */
+  for (i=2; i<=RU; i++)
+  {
+    GEN t = gnorml2(RgM_RgC_mul(G, gel(units,i)));
+    if (gcmp(t,minunit) < 0) minunit = t;
+  }
+  if (gexpo(minunit) > 30) return NULL;
+
+  vecminim = minimforunits(nf, itos(gceil(minunit)), bnf_get_tuN(bnf));
+  if (!vecminim) return NULL;
+  bound = gel(vecminim,3);
+  if (DEBUGLEVEL>1) err_printf("M* = %Ps\n", bound);
+  M0 = compute_M0(bound, N);
+  if (DEBUGLEVEL>1) { err_printf("M0 = %.28Pg\n",M0); err_flush(); }
+  M = gmul2n(divru(gdiv(powrs(M0,RU),hermiteconstant(RU)),N),R2);
+  if (cmprr(M, dbltor(0.04)) < 0) return NULL;
+  M = sqrtr(M);
+  if (DEBUGLEVEL>1)
+    err_printf("(lower bound for regulator) M = %.28Pg\n",M);
+  return M;
+}
+
+/* upper bound for the index of bnf.fu in the full unit group */
+static GEN
+bound_unit_index(GEN bnf, GEN units)
+{
+  pari_sp av = avma;
+  GEN x = lowerboundforregulator(bnf, units);
+  if (!x) { avma = av; x = regulatorbound(bnf); }
+  return gerepileuptoint(av, ground(gdiv(bnf_get_reg(bnf), x)));
+}
+
+/* Compute a square matrix of rank #beta associated to a family
+ * (P_i), 1<=i<=#beta, of primes s.t. N(P_i) = 1 mod p, and
+ * (P_i,beta[j]) = 1 for all i,j */
+static void
+primecertify(GEN bnf, GEN beta, ulong p, GEN bad)
+{
+  long i, j, nbcol, lb, nbqq, ra;
+  GEN nf,mat,gq,LQ,newcol,g,ord,modpr;
+  ulong q;
+
+  ord = NULL; /* gcc -Wall */
+  nbcol = 0; nf = bnf_get_nf(bnf);
+  lb = lg(beta)-1; mat = cgetg(1,t_MAT); q = 1UL;
+  for(;;)
+  {
+    q += 2*p;
+    if (!umodiu(bad,q) || !uisprime(q)) continue;
+
+    gq = utoipos(q);
+    LQ = idealprimedec(bnf,gq); nbqq = lg(LQ)-1;
+    g = NULL;
+    for (i=1; i<=nbqq; i++)
+    {
+      GEN mat1, Q = gel(LQ,i);
+
+      if (pr_get_f(Q) != 1) break;
+      /* Q has degree 1 */
+      if (!g)
+      {
+        g = gener_Flxq(pol_x(0), q, &ord);
+        g = utoipos(g[2]); /* from Flx of degree 0 to t_INT */
+      }
+      modpr = zkmodprinit(nf, Q);
+      newcol = cgetg(lb+1,t_COL);
+      for (j=1; j<=lb; j++)
+      {
+        GEN t = to_Fp_simple(nf, gel(beta,j), modpr);
+        gel(newcol,j) = Fp_log(t,g,ord,gq);
+      }
+      if (DEBUGLEVEL>3)
+      {
+        if (i==1) err_printf("       generator of (Zk/Q)^*: %Ps\n", g);
+        err_printf("       prime ideal Q: %Ps\n",Q);
+        err_printf("       column #%ld of the matrix log(b_j/Q): %Ps\n",
+                   nbcol, newcol);
+      }
+      mat1 = shallowconcat(mat,newcol); ra = ZM_rank(mat1);
+      if (ra==nbcol) continue;
+
+      if (DEBUGLEVEL>2) err_printf("       new rank: %ld\n",ra);
+      if (++nbcol == lb) return;
+      mat = mat1;
+    }
+  }
+}
+
+struct check_pr {
+  long w; /* #mu(K) */
+  GEN mu; /* generator of mu(K) */
+  GEN fu;
+  GEN cyc;
+  GEN cycgen;
+  GEN bad; /* p | bad <--> p | some element occurring in cycgen */
+};
+
+static void
+check_prime(ulong p, GEN bnf, struct check_pr *S)
+{
+  pari_sp av = avma;
+  long i,b, lc = lg(S->cyc), lf = lg(S->fu);
+  GEN beta = cgetg(lf+lc, t_VEC);
+
+  if (DEBUGLEVEL>1) err_printf("  *** testing p = %lu\n",p);
+  for (b=1; b<lc; b++)
+  {
+    if (umodiu(gel(S->cyc,b), p)) break; /* p \nmid cyc[b] */
+    if (b==1 && DEBUGLEVEL>2) err_printf("     p divides h(K)\n");
+    gel(beta,b) = gel(S->cycgen,b);
+  }
+  if (S->w % p == 0)
+  {
+    if (DEBUGLEVEL>2) err_printf("     p divides w(K)\n");
+    gel(beta,b++) = S->mu;
+  }
+  for (i=1; i<lf; i++) gel(beta,b++) = gel(S->fu,i);
+  setlg(beta, b); /* beta = [cycgen[i] if p|cyc[i], tu if p|w, fu] */
+  if (DEBUGLEVEL>3) {err_printf("     Beta list = %Ps\n",beta); err_flush();}
+  primecertify(bnf,beta,p,S->bad); avma = av;
+}
+
+static void
+init_bad(struct check_pr *S, GEN nf, GEN gen)
+{
+  long i, l = lg(gen);
+  GEN bad = gen_1;
+
+  for (i=1; i < l; i++)
+    bad = lcmii(bad, gcoeff(gel(gen,i),1,1));
+  for (i = 1; i < l; i++)
+  {
+    GEN c = gel(S->cycgen,i);
+    long j;
+    if (typ(c) == t_MAT)
+    {
+      GEN g = gel(c,1);
+      for (j = 1; j < lg(g); j++)
+      {
+        GEN h = idealhnf_shallow(nf, gel(g,j));
+        bad = lcmii(bad, gcoeff(h,1,1));
+      }
+    }
+  }
+  S->bad = bad;
+}
+
+long
+bnfcertify0(GEN bnf, long flag)
+{
+  pari_sp av = avma;
+  long i, N;
+  GEN nf, cyc, B;
+  ulong bound, p;
+  struct check_pr S;
+  forprime_t T;
+
+  bnf = checkbnf(bnf);
+  nf = bnf_get_nf(bnf);
+  N = nf_get_degree(nf); if (N==1) return 1;
+  testprimes(bnf, zimmertbound(N, nf_get_r2(nf), absi(nf_get_disc(nf))));
+  if (flag) return 1;
+
+  cyc = bnf_get_cyc(bnf);
+  S.w = bnf_get_tuN(bnf);
+  S.mu = nf_to_scalar_or_basis(nf, bnf_get_tuU(bnf));
+  S.fu= matalgtobasis(nf, bnf_get_fu(bnf));
+  S.cyc = cyc;
+  S.cycgen = check_and_build_cycgen(bnf);
+  init_bad(&S, nf, bnf_get_gen(bnf));
+
+  B = bound_unit_index(bnf, S.fu);
+  if (DEBUGLEVEL)
+  {
+    err_printf("PHASE 2 [UNITS]: are all primes good ?\n");
+    err_printf("  Testing primes <= %Ps\n", B); err_flush();
+  }
+  bound = itou_or_0(B);
+  if (!bound) pari_err_OVERFLOW("bnfcertify [too many primes to check]");
+  if (u_forprime_init(&T, 2, bound))
+    while ( (p = u_forprime_next(&T)) ) check_prime(p,bnf, &S);
+  if (lg(cyc) > 1)
+  {
+    GEN f = Z_factor(gel(cyc,1)), P = gel(f,1);
+    long l = lg(P);
+    if (DEBUGLEVEL>1) { err_printf("  Testing primes | h(K)\n\n"); err_flush(); }
+    for (i=1; i<l; i++)
+    {
+      p = itou(gel(P,i));
+      if (p > bound) check_prime(p,bnf, &S);
+    }
+  }
+  avma = av; return 1;
+}
+long
+bnfcertify(GEN bnf) { return bnfcertify0(bnf, 0); }
+
+/*******************************************************************/
+/*                                                                 */
+/*        RAY CLASS FIELDS: CONDUCTORS AND DISCRIMINANTS           */
+/*                                                                 */
+/*******************************************************************/
+/* Let bnr1 with generators, bnr2 be such that mod(bnr2) | mod(bnr1), compute
+ * the matrix of the surjective map Cl(bnr1) ->> Cl(bnr2) */
+GEN
+bnrsurjection(GEN bnr1, GEN bnr2)
+{
+  long l, i;
+  GEN M, gen = bnr_get_gen(bnr1);
+  l = lg(gen); M = cgetg(l, t_MAT);
+  for (i = 1; i < l; i++) gel(M,i) = isprincipalray(bnr2, gel(gen,i));
+  return M;
+}
+
+/* s: <gen> = Cl_f --> Cl_f2 --> 0, H subgroup of Cl_f (generators given as
+ * HNF on [gen]). Return subgroup s(H) in Cl_f2. bnr must include generators */
+static GEN
+imageofgroup(GEN bnr, GEN bnr2, GEN H)
+{
+  GEN H2, cyc2 = bnr_get_cyc(bnr2);
+  if (!H) return diagonal_shallow(cyc2);
+  H2 = ZM_mul(bnrsurjection(bnr, bnr2), H);
+  return ZM_hnfmodid(H2, cyc2); /* s(H) in Cl_n */
+}
+
+/* convert A,B,C to [bnr, H] */
+GEN
+ABC_to_bnr(GEN A, GEN B, GEN C, GEN *H, int gen)
+{
+  if (typ(A) == t_VEC)
+    switch(lg(A))
+    {
+      case 7: /* bnr */
+        *H = B; return A;
+      case 11: /* bnf */
+        if (!B) pari_err_TYPE("ABC_to_bnr [bnf+missing conductor]",A);
+        *H = C; return Buchray(A,B, gen? nf_INIT | nf_GEN: nf_INIT);
+    }
+  pari_err_TYPE("ABC_to_bnr",A);
+  *H = NULL; return NULL; /* not reached */
+}
+
+GEN
+bnrconductor0(GEN A, GEN B, GEN C, long flag)
+{
+  GEN H, bnr = ABC_to_bnr(A,B,C,&H, flag > 0);
+  return bnrconductor(bnr, H, flag);
+}
+
+long
+bnrisconductor0(GEN A,GEN B,GEN C)
+{
+  GEN H, bnr = ABC_to_bnr(A,B,C,&H, 0);
+  return bnrisconductor(bnr, H);
+}
+
+/* return bnrisprincipal(bnr, (x)), assuming z = ideallog(x) */
+static GEN
+ideallog_to_bnr(GEN bnr, GEN z)
+{
+  GEN U = gel(bnr,4), divray = bnr_get_cyc(bnr);
+  long j, l, lU, lz;
+  int col;
+
+  if (lg(z) == 1) return z;
+  col = (typ(z) == t_COL); /* else t_MAT */
+  lz = col? lg(z): lgcols(z);
+  lU = lg(U);
+  if (lz != lU)
+  {
+    if (lz == 1) return zerocol(nbrows(U)); /* lU != 1 */
+    U = vecslice(U, lU-lz+1, lU-1); /* remove Cl(K) part */
+  }
+  if (col) {
+    z = ZM_ZC_mul(U, z);
+    z = vecmodii(z, divray);
+  } else {
+    z = ZM_mul(U, z); l = lg(z);
+    for (j = 1; j < l; j++) gel(z,j) = vecmodii(gel(z,j), divray);
+  }
+  return z;
+}
+static GEN
+bnr_log_gen_pr(GEN bnr, zlog_S *S, GEN nf, long e, long index)
+{ return ideallog_to_bnr(bnr, log_gen_pr(S, index, nf, e)); }
+static GEN
+bnr_log_gen_arch(GEN bnr, zlog_S *S, long index)
+{ return ideallog_to_bnr(bnr, log_gen_arch(S, index)); }
+
+/* A \subset H ? Allow H = NULL = trivial subgroup */
+static int
+contains(GEN H, GEN A)
+{ return H? (hnf_solve(H, A) != NULL): gequal0(A); }
+
+/* (see also Discrayrel). Given a number field bnf=bnr[1], a ray class
+ * group structure bnr (with generators if flag > 0), and a subgroup H of the
+ * ray class group, compute the conductor of H if flag=0. If flag > 0, compute
+ * furthermore the corresponding H' and output
+ * if flag = 1: [[ideal,arch],[hm,cyc,gen],H']
+ * if flag = 2: [[ideal,arch],newbnr,H'] */
+GEN
+bnrconductor(GEN bnr, GEN H0, long flag)
+{
+  pari_sp av = avma;
+  long j, k, l;
+  GEN bnf, nf, bid, ideal, archp, clhray, bnr2, e2, e, mod, H;
+  int iscond0 = 1, iscondinf = 1;
+  zlog_S S;
+
+  checkbnr(bnr);
+  bnf = bnr_get_bnf(bnr);
+  bid = bnr_get_bid(bnr); init_zlog_bid(&S, bid);
+  clhray = bnr_get_no(bnr);
+  nf = bnf_get_nf(bnf);
+  H = check_subgroup(bnr, H0, &clhray, 1);
+
+  archp = S.archp;
+  e     = S.e; l = lg(e);
+  e2 = cgetg(l, t_COL);
+  for (k = 1; k < l; k++)
+  {
+    for (j = itos(gel(e,k)); j > 0; j--)
+    {
+      if (!contains(H, bnr_log_gen_pr(bnr, &S, nf, j, k))) break;
+      iscond0 = 0;
+    }
+    gel(e2,k) = stoi(j);
+  }
+  l = lg(archp);
+  for (k = 1; k < l; k++)
+  {
+    if (!contains(H, bnr_log_gen_arch(bnr, &S, k))) continue;
+    archp[k] = 0;
+    iscondinf = 0;
+  }
+  if (!iscondinf)
+  {
+    for (j = k = 1; k < l; k++)
+      if (archp[k]) archp[j++] = archp[k];
+    setlg(archp, j);
+  }
+  ideal = iscond0? bid_get_ideal(bid): factorbackprime(nf, S.P, e2);
+  mod = mkvec2(ideal, indices_to_vec01(archp, nf_get_r1(nf)));
+  if (!flag) return gerepilecopy(av, mod);
+
+  if (iscond0 && iscondinf)
+  {
+    bnr2 = bnr;
+    if (!H) H = diagonal_shallow(bnr_get_cyc(bnr));
+  }
+  else
+  {
+    bnr2 = Buchray(bnf, mod, nf_INIT | nf_GEN);
+    H = imageofgroup(bnr, bnr2, H);
+  }
+  return gerepilecopy(av, mkvec3(mod, (flag == 1)? gel(bnr2,5): bnr2, H));
+}
+long
+bnrisconductor(GEN bnr, GEN H0)
+{
+  pari_sp av = avma;
+  long j, k, l;
+  GEN bnf, nf, bid, archp, clhray, e, H;
+  zlog_S S;
+
+  checkbnr(bnr);
+  bnf = bnr_get_bnf(bnr);
+  bid = bnr_get_bid(bnr); init_zlog_bid(&S, bid);
+  clhray = bnr_get_no(bnr);
+  nf = bnf_get_nf(bnf);
+  H = check_subgroup(bnr, H0, &clhray, 1);
+
+  archp = S.archp;
+  e     = S.e; l = lg(e);
+  for (k = 1; k < l; k++)
+  {
+    j = itos(gel(e,k));
+    if (contains(H, bnr_log_gen_pr(bnr, &S, nf, j, k))) { avma = av; return 0; }
+  }
+  l = lg(archp);
+  for (k = 1; k < l; k++)
+    if (contains(H, bnr_log_gen_arch(bnr, &S, k))) { avma = av; return 0; }
+  avma = av; return 1;
+}
+
+static void
+err_rnfnormgroup(GEN T)
+{ pari_err_DOMAIN("rnfnormgroup","rnfisabelian(bnr,pol)","=", gen_0,T); }
+
+/* return the norm group corresponding to the relative extension given by
+ * polrel over bnr.bnf, assuming it is abelian and the modulus of bnr is a
+ * multiple of the conductor */
+GEN
+rnfnormgroup(GEN bnr, GEN polrel)
+{
+  long i, j, reldeg, nfac, k;
+  pari_sp av = avma;
+  GEN bnf, index, discnf, nf, group, detgroup, fa, greldeg;
+  GEN fac, col, cnd;
+  forprime_t S;
+  ulong p;
+
+  checkbnr(bnr); bnf = bnr_get_bnf(bnr);
+  nf = bnf_get_nf(bnf); cnd = gel(bnr_get_mod(bnr), 1);
+  polrel = RgX_nffix("rnfnormgroup", nf_get_pol(nf),polrel,1);
+  if (!gequal1(leading_term(polrel)))
+    pari_err_IMPL("rnfnormgroup for non-monic polynomials");
+
+  reldeg = degpol(polrel);
+  /* reldeg-th powers are in norm group */
+  greldeg = utoipos(reldeg);
+  group = FpC_red(bnr_get_cyc(bnr), greldeg);
+  for (i=1; i<lg(group); i++)
+    if (!signe(gel(group,i))) gel(group,i) = greldeg;
+  detgroup = ZV_prod(group);
+  group = diagonal_shallow(group);
+  k = cmpiu(detgroup,reldeg);
+  if (k < 0) err_rnfnormgroup(polrel);
+  if (!k) return gerepilecopy(av, group);
+
+  discnf = nf_get_disc(nf);
+  index  = nf_get_index(nf);
+  u_forprime_init(&S, 2, ULONG_MAX);
+  while ( (p = u_forprime_next(&S)) )
+  {
+    long oldf = -1, lfa;
+    /* If all pr are unramified and have the same residue degree, p =prod pr
+     * and including last pr^f or p^f is the same, but the last isprincipal
+     * is much easier! oldf is used to track this */
+
+    if (!umodiu(index, p)) continue; /* can't be treated efficiently */
+
+    fa = idealprimedec(nf, utoipos(p)); lfa = lg(fa)-1;
+    for (i=1; i<=lfa; i++)
+    {
+      GEN pr = gel(fa,i), pp, T, polr, modpr;
+      long f;
+
+      /* primes of degree 1 are enough, and simpler */
+      if (pr_get_f(pr) > 1) break;
+      /* if pr (probably) ramified, we have to use all (non-ram) P | pr */
+      if (idealval(nf,cnd,pr)) { oldf = 0; continue; }
+      modpr = zk_to_Fq_init(nf, &pr, &T, &pp); /* T = NULL, pp ignored */
+      polr = nfX_to_FqX(polrel, nf, modpr); /* in Fp[X] */
+      polr = ZX_to_Flx(polr, p);
+      if (!Flx_is_squarefree(polr, p)) { oldf = 0; continue; }
+
+      fac = gel(Flx_factor(polr, p), 1);
+      f = degpol(gel(fac,1));
+      nfac = lg(fac)-1;
+      /* check decomposition of pr has Galois type */
+      for (j=2; j<=nfac; j++)
+        if (degpol(gel(fac,j)) != f) err_rnfnormgroup(polrel);
+      if (oldf < 0) oldf = f; else if (oldf != f) oldf = 0;
+      if (f == reldeg) continue; /* reldeg-th powers already included */
+
+      if (oldf && i == lfa && !umodiu(discnf, p)) pr = utoipos(p);
+
+      /* pr^f = N P, P | pr, hence is in norm group */
+      col = bnrisprincipal(bnr,pr,0);
+      if (f > 1) col = ZC_z_mul(col, f);
+      group = ZM_hnf(shallowconcat(group, col));
+      detgroup = ZM_det_triangular(group);
+      k = cmpiu(detgroup,reldeg);
+      if (k < 0) err_rnfnormgroup(polrel);
+      if (!k) { cgiv(detgroup); return gerepileupto(av,group); }
+    }
+  }
+  return NULL;
+}
+
+GEN
+nf_deg1_prime(GEN nf)
+{
+  GEN z, T = nf_get_pol(nf), D = nf_get_disc(nf), f = nf_get_index(nf);
+  long degnf = degpol(T);
+  forprime_t S;
+  pari_sp av;
+  ulong p;
+  u_forprime_init(&S, degnf, ULONG_MAX);
+  av = avma;
+  while ( (p = u_forprime_next(&S)) )
+  {
+    ulong r;
+    if (!umodiu(D, p) || !umodiu(f, p)) continue;
+    r = Flx_oneroot(ZX_to_Flx(T,p), p);
+    if (r != p)
+    {
+      z = utoi(Fl_neg(r, p));
+      z = deg1pol_shallow(gen_1, z, varn(T));
+      return primedec_apply_kummer(nf, z, 1, utoipos(p));
+    }
+    avma = av;
+  }
+  return NULL;
+}
+
+long
+rnfisabelian(GEN nf, GEN pol)
+{
+  GEN modpr, pr, T, Tnf, pp, ro, nfL, C, z, a, sig, eq;
+  long i, j, l, v;
+  ulong p, k, ka;
+
+  if (typ(nf) == t_POL)
+    Tnf = nf;
+  else {
+    nf = checknf(nf);
+    Tnf = nf_get_pol(nf);
+  }
+  v = varn(Tnf);
+  pol = RgX_nffix("rnfisabelian",Tnf,pol,1);
+  eq = nf_rnfeq(nf,pol); /* init L := K[x]/(pol), nf associated to K */
+  C = gel(eq,1); setvarn(C, v); /* L = Q[t]/(C) */
+  a = gel(eq,2); setvarn(a, v); /* root of K.pol in L */
+  z = nfroots_split(C, QXX_QXQ_eval(pol, a, C));
+  if (!z) return 0;
+  ro = gel(z,1); l = lg(ro)-1;
+  /* small groups are abelian, as are groups of prime order */
+  if (l < 6 || uisprime(l)) return 1;
+
+  nfL = gel(z,2);
+  pr = nf_deg1_prime(nfL);
+  modpr = nf_to_Fq_init(nfL, &pr, &T, &pp);
+  p = itou(pp);
+  k = umodiu(gel(eq,3), p);
+  ka = (k * itou(nf_to_Fq(nfL, a, modpr))) % p;
+  sig= cgetg(l+1, t_VECSMALL);
+  /* image of c = ro[1] + k a [distinguished root of C] by the l automorphisms
+   * sig[i]: ro[1] -> ro[i] */
+  for (i = 1; i <= l; i++)
+    sig[i] = Fl_add(ka, itou(nf_to_Fq(nfL, gel(ro,i), modpr)), p);
+  ro = Q_primpart(ro);
+  for (i=2; i<=l; i++) { /* start at 2, since sig[1] = identity */
+    gel(ro,i) = ZX_to_Flx(gel(ro,i), p);
+    for (j=2; j<i; j++)
+      if (Flx_eval(gel(ro,j), sig[i], p)
+       != Flx_eval(gel(ro,i), sig[j], p)) return 0;
+  }
+  return 1;
+}
+
+/* Given bnf and polrel defining an abelian relative extension, compute the
+ * corresponding conductor and congruence subgroup. Return
+ * [[ideal,arch],[hm,cyc,gen],group] where [ideal,arch] is the conductor, and
+ * [hm,cyc,gen] is the corresponding ray class group. */
+GEN
+rnfconductor(GEN bnf, GEN polrel)
+{
+  pari_sp av = avma;
+  GEN nf, module, bnr, group, den, D;
+
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  if (typ(polrel) != t_POL) pari_err_TYPE("rnfconductor",polrel);
+  den = Q_denom( RgX_to_nfX(nf, polrel) );
+  if (!is_pm1(den)) polrel = RgX_rescale(polrel, den);
+  (void)rnfallbase(nf,&polrel, &D, NULL, NULL);
+  module = mkvec2(D, const_vec(nf_get_r1(nf), gen_1));
+  bnr   = Buchray(bnf,module,nf_INIT | nf_GEN);
+  group = rnfnormgroup(bnr,polrel);
+  if (!group) { avma = av; return gen_0; }
+  return gerepileupto(av, bnrconductor(bnr,group,1));
+}
+
+/* Given a number field bnf=bnr[1], a ray class group structure bnr, and a
+ * subgroup H (HNF form) of the ray class group, compute [n, r1, dk]
+ * associated to H (cf. discrayall). If flcond = 1, abort (return gen_0) if
+ * module is not the conductor If flrel = 0, compute only N(dk) instead of
+ * the ideal dk proper */
+static GEN
+Discrayrel(GEN bnr, GEN H0, long flag)
+{
+  pari_sp av = avma;
+  long j, k, l, nz, flrel = flag & rnf_REL, flcond = flag & rnf_COND;
+  GEN bnf, nf, bid, ideal, archp, clhray, clhss, P, e, dlk;
+  zlog_S S;
+
+  checkbnr(bnr);
+  bnf = bnr_get_bnf(bnr);
+  bid = bnr_get_bid(bnr); init_zlog_bid(&S, bid);
+  clhray = bnr_get_no(bnr);
+  nf = bnf_get_nf(bnf);
+  ideal= bid_get_ideal(bid);
+  H0 = check_subgroup(bnr, H0, &clhray, 0);
+  archp = S.archp;
+  P     = S.P;
+  e     = S.e; l = lg(e);
+  dlk = flrel? idealpow(nf,ideal,clhray)
+             : powii(ZM_det_triangular(ideal),clhray);
+  for (k = 1; k < l; k++)
+  {
+    GEN pr = gel(P,k), sum = gen_0, H = H0;
+    long ep = itos(gel(e,k));
+    for (j = ep; j > 0; j--)
+    {
+      GEN z = bnr_log_gen_pr(bnr, &S, nf, j, k);
+      H = ZM_hnf(shallowconcat(H, z));
+      clhss = ZM_det_triangular(H);
+      if (flcond && j==ep && equalii(clhss,clhray)) { avma = av; return gen_0; }
+      if (is_pm1(clhss)) { sum = addis(sum, j); break; }
+      sum = addii(sum, clhss);
+    }
+    dlk = flrel? idealdivpowprime(nf, dlk, pr, sum)
+               : diviiexact(dlk, powii(pr_norm(pr),sum));
+  }
+  l = lg(archp); nz = nf_get_r1(nf) - (l-1);
+  for (k = 1; k < l; k++)
+  {
+    if (!contains(H0, bnr_log_gen_arch(bnr, &S, k))) continue;
+    if (flcond) { avma = av; return gen_0; }
+    nz++;
+  }
+  return gerepilecopy(av, mkvec3(clhray, stoi(nz), dlk));
+}
+
+GEN
+bnrdisc(GEN bnr, GEN H, long flag)
+{
+  pari_sp av = avma;
+  long clhray, n, R1;
+  GEN z, p1, D, dk, nf, dkabs;
+
+  D = Discrayrel(bnr, H, flag);
+  if ((flag & rnf_REL) || D == gen_0) return D;
+
+  nf = checknf(bnr);
+  dkabs = absi(nf_get_disc(nf));
+  clhray = itos(gel(D,1)); p1 = powiu(dkabs, clhray);
+  n = clhray * nf_get_degree(nf);
+  R1= clhray * itos(gel(D,2));
+  dk = gel(D,3);
+  if (((n-R1)&3) == 2) dk = negi(dk); /* (2r2) mod 4 = 2 : r2(relext) is odd */
+  z = cgetg(4,t_VEC);
+  gel(z,1) = utoipos(n);
+  gel(z,2) = stoi(R1);
+  gel(z,3) = mulii(dk,p1); return gerepileupto(av, z);
+}
+
+GEN
+bnrdisc0(GEN A, GEN B, GEN C, long flag)
+{
+  GEN H, bnr = ABC_to_bnr(A,B,C,&H, 0);
+  return bnrdisc(bnr,H,flag);
+}
+GEN
+discrayrel(GEN bnr, GEN H)
+{ return bnrdisc(bnr,H,rnf_REL); }
+GEN
+discrayrelcond(GEN bnr, GEN H)
+{ return bnrdisc(bnr,H,rnf_REL | rnf_COND); }
+GEN
+discrayabs(GEN bnr, GEN H)
+{ return bnrdisc(bnr,H,0); }
+GEN
+discrayabscond(GEN bnr, GEN H)
+{ return bnrdisc(bnr,H,rnf_COND); }
+
+/* chi character of abelian G: chi[i] = chi(z_i), where G = \oplus Z/cyc[i] z_i.
+ * Return Ker chi [ NULL = trivial subgroup of G ] */
+static GEN
+KerChar(GEN chi, GEN cyc)
+{
+  long i, l = lg(cyc);
+  GEN m, U, d1;
+
+  if (typ(chi) != t_VEC) pari_err_TYPE("KerChar",chi);
+  if (lg(chi) != l) pari_err_DIM("KerChar [incorrect character length]");
+  if (l == 1) return NULL; /* trivial subgroup */
+  d1 = gel(cyc,1); m = cgetg(l+1,t_MAT);
+  for (i=1; i<l; i++)
+  {
+    GEN c = gel(chi,i);
+    if (typ(c) != t_INT) pari_err_TYPE("conductorofchar", c);
+    gel(m,i) = mkcol(mulii(c, diviiexact(d1, gel(cyc,i))));
+  }
+  gel(m,i) = mkcol(d1);
+  (void)ZM_hnfall(m, &U, 1);
+  for (i = 1; i < l; i++) setlg(U[i], l);
+  setlg(U,l); return U;
+}
+
+/* Given a number field bnf=bnr[1], a ray class group structure bnr and a
+ * vector chi representing a character on the generators bnr[2][3], compute
+ * the conductor of chi. */
+GEN
+bnrconductorofchar(GEN bnr, GEN chi)
+{
+  pari_sp av = avma; checkbnr(bnr);
+  return gerepileupto(av, bnrconductor(bnr, KerChar(chi, bnr_get_cyc(bnr)), 0));
+}
+
+/* t = [bid,U], h = #Cl(K) */
+static GEN
+get_classno(GEN t, GEN h)
+{
+  GEN bid = gel(t,1), m = gel(t,2), cyc = bid_get_cyc(bid);
+  return mulii(h, ZM_det_triangular(ZM_hnfmodid(m, cyc)));
+}
+
+static void
+chk_listBU(GEN L, const char *s) {
+  if (typ(L) != t_VEC) pari_err_TYPE(s,L);
+  if (lg(L) > 1) {
+    GEN z = gel(L,1);
+    if (typ(z) != t_VEC) pari_err_TYPE(s,z);
+    if (lg(z) == 1) return;
+    z = gel(z,1); /* [bid,U] */
+    if (typ(z) != t_VEC || lg(z) != 3) pari_err_TYPE(s,z);
+    checkbid(gel(z,1));
+  }
+}
+
+/* Given lists of [bid, unit ideallogs], return lists of ray class
+ * numbers */
+GEN
+bnrclassnolist(GEN bnf,GEN L)
+{
+  pari_sp av = avma;
+  long i, j, lz, l = lg(L);
+  GEN v, z, V, h;
+
+  chk_listBU(L, "bnrclassnolist");
+  if (l == 1) return cgetg(1, t_VEC);
+  bnf = checkbnf(bnf); h = bnf_get_no(bnf);
+  V = cgetg(l,t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    z = gel(L,i); lz = lg(z);
+    gel(V,i) = v = cgetg(lz,t_VEC);
+    for (j=1; j<lz; j++) gel(v,j) = get_classno(gel(z,j), h);
+  }
+  return gerepilecopy(av, V);
+}
+
+static GEN
+Lbnrclassno(GEN L, GEN fac)
+{
+  long i, l = lg(L);
+  for (i=1; i<l; i++)
+    if (gequal(gmael(L,i,1),fac)) return gmael(L,i,2);
+  pari_err_BUG("Lbnrclassno");
+  return NULL; /* not reached */
+}
+
+static GEN
+factordivexact(GEN fa1,GEN fa2)
+{
+  long i, j, k, c, l;
+  GEN P, E, P1, E1, P2, E2, p1;
+
+  P1 = gel(fa1,1); E1 = gel(fa1,2); l = lg(P1);
+  P2 = gel(fa2,1); E2 = gel(fa2,2);
+  P = cgetg(l,t_COL);
+  E = cgetg(l,t_COL);
+  for (c = i = 1; i < l; i++)
+  {
+    j = RgV_isin(P2,gel(P1,i));
+    if (!j) { gel(P,c) = gel(P1,i); gel(E,c) = gel(E1,i); c++; }
+    else
+    {
+      p1 = subii(gel(E1,i), gel(E2,j)); k = signe(p1);
+      if (k < 0) pari_err_BUG("factordivexact [not exact]");
+      if (k > 0) { gel(P,c) = gel(P1,i); gel(E,c) = p1; c++; }
+    }
+  }
+  setlg(P, c);
+  setlg(E, c); return mkmat2(P, E);
+}
+/* remove index k */
+static GEN
+factorsplice(GEN fa, long k)
+{
+  GEN p = gel(fa,1), e = gel(fa,2), P, E;
+  long i, l = lg(p) - 1;
+  P = cgetg(l, typ(p));
+  E = cgetg(l, typ(e));
+  for (i=1; i<k; i++) { P[i] = p[i]; E[i] = e[i]; }
+  p++; e++;
+  for (   ; i<l; i++) { P[i] = p[i]; E[i] = e[i]; }
+  return mkmat2(P,E);
+}
+static GEN
+factorpow(GEN fa, long n)
+{
+  if (!n) return trivial_fact();
+  return mkmat2(gel(fa,1), gmulsg(n, gel(fa,2)));
+}
+static GEN
+factormul(GEN fa1,GEN fa2)
+{
+  GEN p, pnew, e, enew, v, P, y = famat_mul_shallow(fa1,fa2);
+  long i, c, lx;
+
+  p = gel(y,1); v = indexsort(p); lx = lg(p);
+  e = gel(y,2);
+  pnew = vecpermute(p, v);
+  enew = vecpermute(e, v);
+  P = gen_0; c = 0;
+  for (i=1; i<lx; i++)
+  {
+    if (gequal(gel(pnew,i),P))
+      gel(e,c) = addii(gel(e,c),gel(enew,i));
+    else
+    {
+      c++; P = gel(pnew,i);
+      gel(p,c) = P;
+      gel(e,c) = gel(enew,i);
+    }
+  }
+  setlg(p, c+1);
+  setlg(e, c+1); return y;
+}
+
+
+static long
+get_nz(GEN bnf, GEN ideal, GEN arch, long clhray)
+{
+  GEN arch2, mod;
+  long nz = 0, l = lg(arch), k, clhss;
+  if (typ(arch) == t_VECSMALL)
+    arch2 = indices_to_vec01(arch,nf_get_r1(bnf_get_nf(bnf)));
+  else
+    arch2 = leafcopy(arch);
+  mod = mkvec2(ideal, arch2);
+  for (k = 1; k < l; k++)
+  { /* FIXME: this is wasteful. Use the same algorithm as bnrconductor */
+    if (signe(gel(arch2,k)))
+    {
+      gel(arch2,k) = gen_0; clhss = itos(bnrclassno(bnf,mod));
+      gel(arch2,k) = gen_1;
+      if (clhss == clhray) return -1;
+    }
+    else nz++;
+  }
+  return nz;
+}
+
+static GEN
+get_NR1D(long Nf, long clhray, long degk, long nz, GEN fadkabs, GEN idealrel)
+{
+  long n, R1;
+  GEN dlk;
+  if (nz < 0) mkvec3(gen_0,gen_0,gen_0); /*EMPTY*/
+  n  = clhray * degk;
+  R1 = clhray * nz;
+  dlk = factordivexact(factorpow(Z_factor(utoipos(Nf)),clhray), idealrel);
+  /* r2 odd, set dlk = -dlk */
+  if (((n-R1)&3)==2) dlk = factormul(to_famat_shallow(gen_m1,gen_1), dlk);
+  return mkvec3(utoipos(n),
+                stoi(R1),
+                factormul(dlk,factorpow(fadkabs,clhray)));
+}
+
+/* t = [bid,U], h = #Cl(K) */
+static GEN
+get_discdata(GEN t, GEN h)
+{
+  GEN bid = gel(t,1), fa = gel(bid,3);
+  return mkvec3(mkmat2(gel(fa,1), vec_to_vecsmall(gel(fa,2))),
+                (GEN)itou(get_classno(t, h)),
+                bid_get_mod(bid));
+}
+typedef struct _disc_data {
+  long degk;
+  GEN bnf, fadk, idealrelinit, V;
+} disc_data;
+
+static GEN
+get_discray(disc_data *D, GEN V, GEN z, long N)
+{
+  GEN idealrel = D->idealrelinit;
+  GEN mod = gel(z,3), Fa = gel(z,1);
+  GEN P = gel(Fa,1), E = gel(Fa,2);
+  long k, nz, clhray = z[2], lP = lg(P);
+  for (k=1; k<lP; k++)
+  {
+    GEN pr = gel(P,k), p = pr_get_p(pr);
+    long e, ep = E[k], f = pr_get_f(pr);
+    long S = 0, norm = N, Npr = upowuu(p[2],f), clhss;
+    for (e=1; e<=ep; e++)
+    {
+      GEN fad;
+      if (e < ep) { E[k] = ep-e; fad = Fa; }
+      else fad = factorsplice(Fa, k);
+      norm /= Npr;
+      clhss = (long)Lbnrclassno(gel(V,norm), fad);
+      if (e==1 && clhss==clhray) { E[k] = ep; return cgetg(1, t_VEC); }
+      if (clhss == 1) { S += ep-e+1; break; }
+      S += clhss;
+    }
+    E[k] = ep;
+    idealrel = factormul(idealrel, to_famat_shallow(p, utoi(f * S)));
+  }
+  nz = get_nz(D->bnf, gel(mod,1), gel(mod,2), clhray);
+  return get_NR1D(N, clhray, D->degk, nz, D->fadk, idealrel);
+}
+
+/* Given a list of bids and associated unit log matrices, return the
+ * list of discrayabs. Only keep moduli which are conductors. */
+GEN
+discrayabslist(GEN bnf, GEN L)
+{
+  pari_sp av = avma;
+  long i, l = lg(L);
+  GEN nf, V, D, h;
+  disc_data ID;
+
+  chk_listBU(L, "discrayabslist");
+  if (l == 1) return cgetg(1, t_VEC);
+  ID.bnf = bnf = checkbnf(bnf);
+  nf = bnf_get_nf(bnf);
+  h = bnf_get_no(bnf);
+  ID.degk = nf_get_degree(nf);
+  ID.fadk = absi_factor(nf_get_disc(nf));
+  ID.idealrelinit = trivial_fact();
+  V = cgetg(l, t_VEC);
+  D = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    GEN z = gel(L,i), v, d;
+    long j, lz = lg(z);
+    gel(V,i) = v = cgetg(lz,t_VEC);
+    gel(D,i) = d = cgetg(lz,t_VEC);
+    for (j=1; j<lz; j++) {
+      gel(d,j) = get_discdata(gel(z,j), h);
+      gel(v,j) = get_discray(&ID, D, gel(d,j), i);
+    }
+  }
+  return gerepilecopy(av, V);
+}
+
+/* BIG VECTOR:
+ * Interface: a container v whose length is arbitrary (< 2^30), bigel(v,i)
+ * refers to the i-th component. It is an lvalue.
+ *
+ * Implementation: a vector v whose components have exactly 2^LGVINT entries
+ * but for the last one which is allowed to be shorter. v[i][j]
+ * (where j<=2^LGVINT) is understood as component number I = (i-1)*2^LGVINT+j
+ * in a unique huge vector V. */
+static const int SHLGVINT = 15;
+static const long LGVINT = 1L << 15;
+INLINE long vext0(ulong i) { return ((i-1)>>SHLGVINT) + 1; }
+INLINE long vext1(ulong i) { return i & (LGVINT-1); }
+#define bigel(v,i) gmael((v), vext0(i), vext1(i))
+
+/* allocate an extended vector (t_VEC of t_VEC) for N _true_ components */
+static GEN
+bigcgetvec(long N)
+{
+  long i, nv = vext0(N);
+  GEN v = cgetg(nv+1,t_VEC);
+  for (i=1; i<nv; i++) gel(v,i) = cgetg(LGVINT+1,t_VEC);
+  gel(v,nv) = cgetg(vext1(N)+1,t_VEC); return v;
+}
+
+/* a zsimp is [fa, cyc, U, v]
+ * fa: vecsmall factorisation,
+ * cyc: ZV (abelian group)
+ * U: ZM (base change)
+ * v: ZV (log of units) */
+static GEN
+zsimp(GEN bid, GEN embunit)
+{
+  GEN empty = cgetg(1, t_VECSMALL);
+  return mkvec4(mkmat2(empty,empty), bid_get_cyc(bid), gel(bid,5), embunit);
+}
+
+/* fa a vecsmall factorization, append p^e */
+static GEN
+fasmall_append(GEN fa, long p, long e)
+{
+  GEN P = gel(fa,1), E = gel(fa,2);
+  retmkmat2(vecsmall_append(P,p), vecsmall_append(E,e));
+}
+
+static GEN
+zsimpjoin(GEN b, GEN bid, GEN embunit, long prcode, long e)
+{
+  long l1, l2, nbgen;
+  pari_sp av = avma;
+  GEN fa, U, U1, U2, cyc1, cyc2, cyc;
+
+  fa = gel(b,1);
+  U1 = gel(b,3);   cyc1 = gel(b,2);         l1 = lg(cyc1);
+  U2 = gel(bid,5); cyc2 = bid_get_cyc(bid); l2 = lg(cyc2);
+  nbgen = l1+l2-2;
+  if (nbgen)
+  {
+    GEN u1u2 = matsnf0(diagonal_shallow(shallowconcat(cyc1,cyc2)), 1|4); /* all && clean */
+    cyc = RgM_diagonal_shallow( gel(u1u2,3) );
+    U = gel(u1u2,1);
+    U = shallowconcat(
+      l1==1   ? zeromat(nbgen, lg(U1)-1): ZM_mul(vecslice(U, 1,   l1-1), U1),
+      l1>nbgen? zeromat(nbgen, lg(U2)-1): ZM_mul(vecslice(U, l1, nbgen), U2)
+    );
+  }
+  else
+  {
+    U = zeromat(0, lg(U1)+lg(U2)-2);
+    cyc = cgetg(1,t_VEC);
+  }
+  fa = fasmall_append(fa, prcode, e);
+  return gerepilecopy(av, mkvec4(fa, cyc, U, vconcat(gel(b,4),embunit)));
+}
+/* B a zsimp */
+static GEN
+bnrclassnointern(GEN B, GEN h)
+{
+  long lx = lg(B), j;
+  GEN L = cgetg(lx,t_VEC);
+  for (j=1; j<lx; j++)
+  {
+    GEN b = gel(B,j), qm = ZM_mul(gel(b,3),gel(b,4));
+    GEN m = ZM_det_triangular( ZM_hnfmodid(qm, gel(b,2)) );
+    gel(L,j) = mkvec2(gel(b,1), mkvecsmall( itou( mulii(h, m) ) ));
+  }
+  return L;
+}
+
+static void
+vecselect_p(GEN A, GEN B, GEN p, long init, long lB)
+{
+  long i; setlg(B, lB);
+  for (i=init; i<lB; i++) B[i] = A[p[i]];
+}
+/* B := p . A = row selection according to permutation p. Treat only lower
+ * right corner init x init */
+static void
+rowselect_p(GEN A, GEN B, GEN p, long init)
+{
+  long i, lB = lg(A), lp = lg(p);
+  for (i=1; i<init; i++) setlg(B[i],lp);
+  for (   ; i<lB;   i++) vecselect_p(gel(A,i),gel(B,i),p,init,lp);
+}
+
+static GEN
+bnrclassnointernarch(GEN B, GEN h, GEN matU)
+{
+  long lx, nc, k, kk, j, r1, jj, nba, nbarch;
+  GEN _2, b, qm, L, cyc, m, H, mm, rowsel;
+
+  if (!matU) return bnrclassnointern(B,h);
+  lx = lg(B); if (lx == 1) return B;
+
+  r1 = nbrows(matU); _2 = const_vec(r1, gen_2);
+  L = cgetg(lx,t_VEC); nbarch = 1L<<r1;
+  for (j=1; j<lx; j++)
+  {
+    b = gel(B,j); qm = ZM_mul(gel(b,3),gel(b,4));
+    cyc = gel(b,2); nc = lg(cyc)-1;
+    /* [ qm   cyc 0 ]
+     * [ matU  0  2 ] */
+    m = ZM_hnfmodid(vconcat(qm, matU), shallowconcat(cyc,_2));
+    mm = RgM_shallowcopy(m);
+    H = cgetg(nbarch+1,t_VECSMALL);
+    rowsel = cgetg(nc+r1+1,t_VECSMALL);
+    for (k = 0; k < nbarch; k++)
+    {
+      nba = nc+1;
+      for (kk=k,jj=1; jj<=r1; jj++,kk>>=1)
+        if (kk&1) rowsel[nba++] = nc + jj;
+      setlg(rowsel, nba);
+      rowselect_p(m, mm, rowsel, nc+1);
+      H[k+1] = itou( mulii(h, ZM_det_triangular(ZM_hnf(mm))) );
+    }
+    gel(L,j) = mkvec2(gel(b,1), H);
+  }
+  return L;
+}
+
+GEN
+decodemodule(GEN nf, GEN fa)
+{
+  long n, nn, k;
+  pari_sp av = avma;
+  GEN G, E, id, pr;
+
+  nf = checknf(nf);
+  if (typ(fa)!=t_MAT || lg(fa)!=3)
+    pari_err_TYPE("decodemodule [not a factorization]", fa);
+  n = nf_get_degree(nf); nn = n*n; id = NULL;
+  G = gel(fa,1);
+  E = gel(fa,2);
+  for (k=1; k<lg(G); k++)
+  {
+    long code = G[k], p = code / nn, j = (code%n)+1;
+    GEN P = idealprimedec(nf, utoipos(p)), e = stoi(E[k]);
+    if (lg(P) <= j) pari_err_BUG("decodemodule [incorrect hash code]");
+    pr = gel(P,j);
+    id = id? idealmulpowprime(nf,id, pr,e)
+           : idealpow(nf, pr,e);
+  }
+  if (!id) { avma = av; return matid(n); }
+  return gerepileupto(av,id);
+}
+
+/* List of ray class fields. Do all from scratch, bound < 2^30. No subgroups.
+ *
+ * Output: a "big vector" V (cf bigcgetvec). V[k] is a vector indexed by
+ * the ideals of norm k. Given such an ideal m, the component is as follows:
+ *
+ * + if arch = NULL, run through all possible archimedean parts; archs are
+ * ordered using inverse lexicographic order, [0,..,0], [1,0,..,0], [0,1,..,0],
+ * Component is [m,V] where V is a vector with 2^r1 entries, giving for each
+ * arch the triple [N,R1,D], with N, R1, D as in discrayabs; D is in factored
+ * form.
+ *
+ * + otherwise [m,N,R1,D] */
+GEN
+discrayabslistarch(GEN bnf, GEN arch, ulong bound)
+{
+  int allarch = (arch==NULL), flbou = 0;
+  long degk, j, k, l, nba, nbarch, r1, c;
+  pari_sp av0 = avma,  av,  av1,  lim;
+  GEN nf, p, Z, fa, ideal, bidp, matarchunit, Disc, U, sgnU, EMPTY, empty;
+  GEN res, embunit, h, Ray, discall, idealrel, idealrelinit, fadkabs;
+  ulong i, ii, sqbou;
+  forprime_t S;
+
+  if (bound == 0)
+    pari_err_DOMAIN("discrayabslistarch","bound","==",gen_0,utoi(bound));
+  res = discall = NULL; /* -Wall */
+
+  bnf = checkbnf(bnf);
+  nf = bnf_get_nf(bnf); r1 = nf_get_r1(nf);
+  degk = nf_get_degree(nf);
+  fadkabs = absi_factor(nf_get_disc(nf));
+  h = bnf_get_no(bnf);
+  U = init_units(bnf);
+  sgnU = nfsign_units(bnf, NULL, 1);
+
+  if (allarch) arch = const_vec(r1, gen_1);
+  bidp = Idealstar(nf, mkvec2(gen_1, arch), nf_INIT);
+  if (allarch) {
+    matarchunit = zlog_units(nf, U, sgnU, bidp);
+    bidp = Idealstar(nf,matid(degk), nf_INIT);
+    if (r1>15) pari_err_IMPL("r1>15 in discrayabslistarch");
+    nba = r1;
+  } else {
+    matarchunit = NULL;
+    for (nba=0,k=1; k<=r1; k++) if (signe(gel(arch,k))) nba++;
+  }
+
+  empty = cgetg(1,t_VEC);
+  /* what follows was rewritten from Ideallist */
+  p = cgetipos(3);
+  u_forprime_init(&S, 2, bound);
+  av = avma; lim = stack_lim(av,1);
+  sqbou = (ulong)sqrt((double)bound) + 1;
+  Z = bigcgetvec(bound);
+  for (i=2; i<=bound; i++) bigel(Z,i) = empty;
+  embunit = zlog_units(nf, U, sgnU, bidp);
+  bigel(Z,1) = mkvec(zsimp(bidp,embunit));
+  if (DEBUGLEVEL>1) err_printf("Starting zidealstarunits computations\n");
+  /* The goal is to compute Ray (lists of bnrclassno). Z contains "zsimps",
+   * simplified bid, from which bnrclassno is easy to compute.
+   * Once p > sqbou, delete Z[i] for i > sqbou and compute directly Ray */
+  Ray = Z;
+  while ((p[2] = u_forprime_next(&S)))
+  {
+    if (!flbou && (ulong)p[2] > sqbou)
+    {
+      GEN z;
+      flbou = 1;
+      if (DEBUGLEVEL>1) err_printf("\nStarting bnrclassno computations\n");
+      Z = gerepilecopy(av,Z); av1 = avma;
+      Ray = bigcgetvec(bound);
+      for (i=1; i<=bound; i++)
+        bigel(Ray,i) = bnrclassnointernarch(bigel(Z,i),h,matarchunit);
+      Ray = gerepilecopy(av1,Ray);
+      z = bigcgetvec(sqbou);
+      for (i=1; i<=sqbou; i++) bigel(z,i) = bigel(Z,i);
+      Z = z;
+    }
+    fa = idealprimedec(nf,p);
+    for (j=1; j<lg(fa); j++)
+    {
+      GEN pr = gel(fa,j);
+      long prcode, f = pr_get_f(pr);
+      ulong q, Q = upowuu(p[2], f);
+      if (!Q || Q > bound) break;
+
+      /* p, f-1, j-1 as a single integer in "base degk" (f,j <= degk)*/
+      prcode = (p[2]*degk + f-1)*degk + j-1;
+      q = Q; ideal = pr;
+      for (l=1;; l++) /* Q <= bound */
+      {
+        ulong iQ;
+        bidp = Idealstar(nf,ideal, nf_INIT);
+        embunit = zlog_units_noarch(nf, U, bidp);
+        for (iQ = Q, i = 1; iQ <= bound; iQ += Q, i++)
+        {
+          GEN pz, p2, p1 = bigel(Z,i);
+          long lz = lg(p1);
+          if (lz == 1) continue;
+
+          p2 = cgetg(lz,t_VEC); c = 0;
+          for (k=1; k<lz; k++)
+          {
+            GEN z = gel(p1,k), v = gmael(z,1,1); /* primes in zsimp's fact. */
+            long lv = lg(v);
+            /* If z has a power of pr in its modulus, skip it */
+            if (i != 1 && lv > 1 && v[lv-1] == prcode) break;
+            gel(p2,++c) = zsimpjoin(z,bidp,embunit,prcode,l);
+          }
+
+          setlg(p2, c+1);
+          pz = bigel(Ray,iQ);
+          if (flbou) p2 = bnrclassnointernarch(p2,h,matarchunit);
+          if (lg(pz) > 1) p2 = shallowconcat(pz,p2);
+          bigel(Ray,iQ) = p2;
+        }
+        Q = itou_or_0( muluu(Q, q) );
+        if (!Q || Q > bound) break;
+
+        ideal = idealmul(nf,ideal,pr);
+      }
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"[1]: discrayabslistarch");
+      gerepileall(av, flbou? 2: 1, &Z, &Ray);
+    }
+  }
+  if (!flbou) /* occurs iff bound = 1,2,4 */
+  {
+    if (DEBUGLEVEL>1) err_printf("\nStarting bnrclassno computations\n");
+    Ray = bigcgetvec(bound);
+    for (i=1; i<=bound; i++)
+      bigel(Ray,i) = bnrclassnointernarch(bigel(Z,i),h,matarchunit);
+  }
+  Ray = gerepilecopy(av, Ray);
+
+  if (DEBUGLEVEL>1) err_printf("Starting discrayabs computations\n");
+  if (allarch) nbarch = 1L<<r1;
+  else
+  {
+    nbarch = 1;
+    discall = cgetg(2,t_VEC);
+  }
+  EMPTY = mkvec3(gen_0,gen_0,gen_0);
+  idealrelinit = trivial_fact();
+  av1 = avma; lim = stack_lim(av1,1);
+  Disc = bigcgetvec(bound);
+  for (i=1; i<=bound; i++) bigel(Disc,i) = empty;
+  for (ii=1; ii<=bound; ii++)
+  {
+    GEN sous, sousdisc;
+    long ls;
+    i = ii;
+    sous = bigel(Ray,i);
+    ls = lg(sous); bigel(Disc,ii) = sousdisc = cgetg(ls,t_VEC);
+    for (j=1; j<ls; j++)
+    {
+      GEN b = gel(sous,j), clhrayall = gel(b,2), Fa = gel(b,1);
+      GEN P = gel(Fa,1), E = gel(Fa,2);
+      long lP = lg(P), karch;
+
+      if (allarch) discall = cgetg(nbarch+1,t_VEC);
+      for (karch=0; karch<nbarch; karch++)
+      {
+        long nz, clhray = clhrayall[karch+1];
+        if (allarch)
+        {
+          long ka, k2;
+          nba = 0;
+          for (ka=karch,k=1; k<=r1; k++,ka>>=1)
+            if (ka & 1) nba++;
+          for (k2=1,k=1; k<=r1; k++,k2<<=1)
+            if (karch&k2 && clhrayall[karch-k2+1] == clhray)
+              { res = EMPTY; goto STORE; }
+        }
+        idealrel = idealrelinit;
+        for (k=1; k<lP; k++) /* cf get_discray */
+        {
+          long e, ep = E[k], pf = P[k] / degk, f = (pf%degk) + 1, S = 0;
+          ulong normi = i, Npr;
+          p = utoipos(pf / degk);
+          Npr = upowuu(p[2],f);
+          for (e=1; e<=ep; e++)
+          {
+            long clhss;
+            GEN fad;
+            if (e < ep) { E[k] = ep-e; fad = Fa; }
+            else fad = factorsplice(Fa, k);
+            normi /= Npr;
+            clhss = Lbnrclassno(bigel(Ray,normi),fad)[karch+1];
+            if (e==1 && clhss==clhray) { E[k] = ep; res = EMPTY; goto STORE; }
+            if (clhss == 1) { S += ep-e+1; break; }
+            S += clhss;
+          }
+          E[k] = ep;
+          idealrel = factormul(idealrel, to_famat_shallow(p, utoi(f * S)));
+        }
+        if (!allarch && nba)
+          nz = get_nz(bnf, decodemodule(nf,Fa), arch, clhray);
+        else
+          nz = r1 - nba;
+        res = get_NR1D(i, clhray, degk, nz, fadkabs, idealrel);
+STORE:  gel(discall,karch+1) = res;
+      }
+      res = allarch? mkvec2(Fa, discall)
+                   : mkvec4(Fa, gel(res,1), gel(res,2), gel(res,3));
+      gel(sousdisc,j) = res;
+      if (low_stack(lim, stack_lim(av1,1)))
+      {
+        long jj;
+        if(DEBUGMEM>1) pari_warn(warnmem,"[2]: discrayabslistarch");
+        for (jj=j+1; jj<ls; jj++) gel(sousdisc,jj) = gen_0; /* dummy */
+        Disc = gerepilecopy(av1, Disc);
+        sousdisc = bigel(Disc,ii);
+      }
+    }
+  }
+  return gerepilecopy(av0, Disc);
+}
+GEN
+discrayabslistlong(GEN bnf, ulong bound) {
+  GEN nf = checknf(bnf);
+  long r1 = nf_get_r1(nf);
+  return discrayabslistarch(bnf,zerovec(r1),bound);
+}
+
+int
+subgroup_conductor_ok(GEN H, GEN L)
+{ /* test conductor */
+  long i, l = lg(L);
+  for (i = 1; i < l; i++)
+    if ( hnf_solve(H, gel(L,i)) ) return 0;
+  return 1;
+}
+static GEN
+conductor_elts(GEN bnr)
+{
+  GEN e, L, nf = bnf_get_nf( bnr_get_bnf(bnr) );
+  long le, la, i, k;
+  zlog_S S;
+
+  init_zlog_bid(&S, bnr_get_bid(bnr));
+  e = S.e; le = lg(e); la = lg(S.archp);
+  L = cgetg(le + la - 1, t_VEC);
+  i = 1;
+  for (k = 1; k < le; k++)
+    gel(L,i++) = bnr_log_gen_pr(bnr, &S, nf, itos(gel(e,k)), k);
+  for (k = 1; k < la; k++)
+    gel(L,i++) = bnr_log_gen_arch(bnr, &S, k);
+  return L;
+}
+
+/* Let C a congruence group in bnr, compute its subgroups whose index is
+ * described by bound (see subgrouplist) as subgroups of Clk(bnr).
+ * Restrict to subgroups having the same conductor as bnr */
+GEN
+subgrouplist_cond_sub(GEN bnr, GEN C, GEN bound)
+{
+  pari_sp av = avma;
+  long l, i, j;
+  GEN D, Mr, U, T, subgrp, L, cyc = bnr_get_cyc(bnr);
+
+  Mr = diagonal_shallow(cyc);
+  D = ZM_snfall_i(hnf_solve(C, Mr), &U, NULL, 1);
+  T = ZM_mul(C, RgM_inv(U));
+  L = conductor_elts(bnr);
+  subgrp  = subgrouplist(D, bound);
+  l = lg(subgrp);
+  for (i = j = 1; i < l; i++)
+  {
+    GEN H = ZM_hnfmodid(ZM_mul(T, gel(subgrp,i)), cyc);
+    if (subgroup_conductor_ok(H, L)) gel(subgrp, j++) = H;
+  }
+  setlg(subgrp, j);
+  return gerepilecopy(av, subgrp);
+}
+
+static GEN
+subgroupcond(GEN bnr, GEN indexbound)
+{
+  pari_sp av = avma;
+  GEN li = subgroupcondlist(bnr_get_cyc(bnr), indexbound, conductor_elts(bnr));
+  if (indexbound && typ(indexbound) != t_VEC)
+  { /* sort by increasing index if not single value */
+    long i, l = lg(li);
+    GEN p1, perm, lidet = cgetg(l,t_VEC);
+    for (i=1; i<l; i++) gel(lidet,i) = ZM_det_triangular(gel(li,i));
+    perm = indexsort(lidet); p1 = li; li = cgetg(l,t_VEC);
+    for (i=1; i<l; i++) li[i] = p1[perm[l-i]];
+  }
+  return gerepilecopy(av,li);
+}
+
+GEN
+subgrouplist0(GEN bnr, GEN indexbound, long all)
+{
+  if (typ(bnr)!=t_VEC) pari_err_TYPE("subgrouplist",bnr);
+  if (lg(bnr)!=1 && typ(gel(bnr,1))!=t_INT)
+  {
+    checkbnr(bnr);
+    if (!all) return subgroupcond(bnr,indexbound);
+    bnr = bnr_get_cyc(bnr);
+  }
+  return subgrouplist(bnr,indexbound);
+}
+
+GEN
+bnrdisclist0(GEN bnf, GEN L, GEN arch)
+{
+  if (typ(L)!=t_INT) return discrayabslist(bnf,L);
+  return discrayabslistarch(bnf,arch,itos(L));
+}
diff --git a/src/basemath/buch4.c b/src/basemath/buch4.c
new file mode 100644
index 0000000..1c2ce11
--- /dev/null
+++ b/src/basemath/buch4.c
@@ -0,0 +1,859 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*               S-CLASS GROUP AND NORM SYMBOLS                    */
+/*          (Denis Simon, desimon at math.u-bordeaux.fr)              */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* p > 2, T ZX, p prime, x t_INT */
+static long
+lemma6(GEN T, GEN p, long nu, GEN x)
+{
+  long la, mu;
+  pari_sp av = avma;
+  GEN gpx, gx = poleval(T, x);
+
+  if (Zp_issquare(gx, p)) { avma = av; return 1; }
+
+  la = Z_pval(gx, p);
+  gpx = poleval(ZX_deriv(T), x);
+  mu = signe(gpx)? Z_pval(gpx,p)
+                 : la+nu+1; /* mu = +oo */
+  avma = av;
+  if (la > mu<<1) return 1;
+  if (la >= nu<<1 && mu >= nu) return 0;
+  return -1;
+}
+/* p = 2, T ZX, x t_INT: return 1 = yes, -1 = no, 0 = inconclusive */
+static long
+lemma7(GEN T, long nu, GEN x)
+{
+  long odd4, la, mu;
+  pari_sp av = avma;
+  GEN gpx, oddgx, gx = poleval(T, x);
+
+  if (Zp_issquare(gx,gen_2)) return 1;
+
+  gpx = poleval(ZX_deriv(T), x);
+  la = Z_lvalrem(gx, 2, &oddgx);
+  odd4 = umodiu(oddgx,4); avma = av;
+
+  mu = vali(gpx);
+  if (mu < 0) mu = la+nu+1; /* mu = +oo */
+
+  if (la > mu<<1) return 1;
+  if (nu > mu)
+  {
+    long mnl = mu+nu-la;
+    if (odd(la)) return -1;
+    if (mnl==1) return 1;
+    if (mnl==2 && odd4==1) return 1;
+  }
+  else
+  {
+    long nu2 = nu << 1;
+    if (la >= nu2) return 0;
+    if (la == nu2 - 2 && odd4==1) return 0;
+  }
+  return -1;
+}
+
+/* T a ZX, p a prime, pnu = p^nu, x0 t_INT */
+static long
+zpsol(GEN T, GEN p, long nu, GEN pnu, GEN x0)
+{
+  long i, res;
+  pari_sp av = avma;
+  GEN x, pnup;
+
+  res = equaliu(p,2)? lemma7(T,nu,x0): lemma6(T,p,nu,x0);
+  if (res== 1) return 1;
+  if (res==-1) return 0;
+  x = x0; pnup = mulii(pnu,p);
+  for (i=0; i < itos(p); i++)
+  {
+    x = addii(x,pnu);
+    if (zpsol(T,p,nu+1,pnup,x)) { avma = av; return 1; }
+  }
+  avma = av; return 0;
+}
+
+/* return 1 if equation y^2=T(x) has a rational p-adic solution (possibly
+ * infinite), 0 otherwise. */
+long
+hyperell_locally_soluble(GEN T,GEN p)
+{
+  pari_sp av = avma;
+  long res;
+  if (typ(T)!=t_POL) pari_err_TYPE("zpsoluble",T);
+  if (typ(p)!=t_INT) pari_err_TYPE("zpsoluble",p);
+  RgX_check_ZX(T, "zpsoluble");
+  res = zpsol(T,p,0,gen_1,gen_0) || zpsol(RgX_recip_shallow(T), p, 1, p, gen_0);
+  avma = av; return res;
+}
+
+/* is t a square in (O_K/pr) ? Assume v_pr(t) = 0 */
+static long
+quad_char(GEN nf, GEN t, GEN pr)
+{
+  GEN ord, ordp, T, p, modpr = zk_to_Fq_init(nf, &pr,&T,&p);
+  t = nf_to_Fq(nf,t,modpr);
+  if (T)
+  {
+    ord = subis( pr_norm(pr), 1 ); /* |(O_K / pr)^*| */
+    ordp= subis( p, 1);            /* |F_p^*|        */
+    t = Fq_pow(t, diviiexact(ord, ordp), T,p); /* in F_p^* */
+    if (typ(t) == t_POL)
+    {
+      if (degpol(t)) pari_err_BUG("nfhilbertp");
+      t = gel(t,2);
+    }
+  }
+  return kronecker(t, p);
+}
+/* quad_char(x), x in Z, non-zero mod p */
+static long
+Z_quad_char(GEN x, GEN pr)
+{
+  long f = pr_get_f(pr);
+  if (!odd(f)) return 1;
+  return kronecker(x, pr_get_p(pr));
+}
+
+/* (pr,2) = 1. return 1 if x in Z_K is a square in Z_{K_pr}, 0 otherwise.
+ * modpr = zkmodprinit(nf,pr) */
+static long
+psquarenf(GEN nf,GEN x,GEN pr,GEN modpr)
+{
+  pari_sp av = avma;
+  GEN p = pr_get_p(pr);
+  long v;
+
+  x = nf_to_scalar_or_basis(nf, x);
+  if (typ(x) == t_INT) {
+    if (!signe(x)) return 1;
+    v = Z_pvalrem(x, p, &x) * pr_get_e(pr);
+    if (v&1) return 0;
+    v = (Z_quad_char(x, pr) == 1);
+  } else {
+    v = ZC_nfvalrem(nf, x, pr, &x);
+    if (v&1) return 0;
+    v = (quad_char(nf, x, modpr) == 1);
+  }
+  avma = av; return v;
+}
+
+/* Is  x a square in (ZK / pr^(1+2e))^* ?  pr | 2 */
+static long
+check2(GEN nf, GEN x, GEN zinit)
+{
+  GEN zlog = ideallog(nf, x, zinit);
+  long i, l = lg(zlog);
+  for (i=1; i<l; i++) /* all elementary divisors are even (1+2e > 1) */
+    if (mpodd(gel(zlog,i))) return 0;
+  return 1;
+}
+
+/* pr | 2. Return 1 if x in Z_K is square in Z_{K_pr}, 0 otherwise */
+static int
+psquare2nf_i(GEN nf,GEN x,GEN pr,GEN zinit)
+{
+  long v = nfvalrem(nf, x, pr, &x);
+  /* now (x,pr) = 1 */
+  return v == LONG_MAX || (!odd(v) && check2(nf,x,zinit));
+}
+static int
+psquare2nf(GEN nf,GEN x,GEN pr,GEN zinit)
+{
+  pari_sp av = avma;
+  long v = psquare2nf_i(nf,x,pr,zinit);
+  avma = av; return v;
+}
+
+/* pr above an odd prime */
+static long
+lemma6nf(GEN nf, GEN T, GEN pr, long nu, GEN x, GEN modpr)
+{
+  pari_sp av = avma;
+  long la, mu;
+  GEN gpx, gx = nfpoleval(nf, T, x);
+
+  if (psquarenf(nf,gx,pr,modpr)) return 1;
+
+  la = nfval(nf,gx,pr);
+  gpx = nfpoleval(nf, RgX_deriv(T), x);
+  mu = gequal0(gpx)? la+nu+1: nfval(nf,gpx,pr);
+  avma = av;
+  if (la > (mu<<1)) return 1;
+  if (la >= (nu<<1)  && mu >= nu) return 0;
+  return -1;
+}
+/* pr above 2 */
+static long
+lemma7nf(GEN nf, GEN T, GEN pr, long nu, GEN x, GEN zinit)
+{
+  long res, la, mu, q;
+  GEN gpx, gx = nfpoleval(nf, T, x);
+
+  if (psquare2nf(nf,gx,pr,zinit)) return 1;
+
+  gpx = nfpoleval(nf, RgX_deriv(T), x);
+  /* gx /= pi^la, pi a pr-uniformizer */
+  la = ZC_nfvalrem(nf, gx, pr, &gx);
+  mu = gequal0(gpx)? la+nu+1: nfval(nf,gpx,pr);
+
+  if (la > (mu<<1)) return 1;
+  if (nu > mu)
+  {
+    if (la&1) return -1;
+    q = mu+nu-la; res = 1;
+  }
+  else
+  {
+    long nu2 = nu<<1;
+    if (la >= nu2) return 0;
+    if (odd(la)) return -1;
+    q = nu2-la; res = 0;
+  }
+  if (q > pr_get_e(pr)<<1)  return -1;
+  if (q == 1) return res;
+
+  /* is gx a square mod pi^q ? FIXME : highly inefficient */
+  zinit = zidealstarinit(nf, idealpows(nf,pr,q));
+  if (!check2(nf, gx, zinit)) res = -1;
+  return res;
+}
+/* zinit either a bid (pr | 2) or a modpr structure (pr | p odd).
+   pnu = pi^nu, pi a uniformizer */
+static long
+zpsolnf(GEN nf,GEN T,GEN pr,long nu,GEN pnu,GEN x0,GEN repr,GEN zinit)
+{
+  long i, res;
+  pari_sp av = avma;
+  GEN pnup;
+
+  res = typ(zinit) == t_VEC? lemma7nf(nf,T,pr,nu,x0,zinit)
+                           : lemma6nf(nf,T,pr,nu,x0,zinit);
+  avma = av;
+  if (res== 1) return 1;
+  if (res==-1) return 0;
+  pnup = nfmul(nf, pnu, pr_get_gen(pr));
+  nu++;
+  for (i=1; i<lg(repr); i++)
+  {
+    GEN x = nfadd(nf, x0, nfmul(nf,pnu,gel(repr,i)));
+    if (zpsolnf(nf,T,pr,nu,pnup,x,repr,zinit)) { avma = av; return 1; }
+  }
+  avma = av; return 0;
+}
+
+/* Let y = copy(x); y[k] := j; return y */
+static GEN
+ZC_add_coeff(GEN x, long k, long j)
+{ GEN y = shallowcopy(x); gel(y, k) = utoi(j); return y; }
+
+/* system of representatives for Zk/pr */
+static GEN
+repres(GEN nf, GEN pr)
+{
+  long f = pr_get_f(pr), N = nf_get_degree(nf), p = itos(pr_get_p(pr));
+  long i, j, k, pi, pf = upowuu(p, f);
+  GEN rep, perm = cgetg(f+1, t_VECSMALL);
+
+  perm[1] = 1;
+  if (f > 1) {
+    GEN H = idealhnf_two(nf,pr);
+    for (i = k = 2; k <= f; i++)
+    {
+      if (is_pm1(gcoeff(H,i,i))) continue;
+      perm[k++] = i;
+    }
+  }
+  rep = cgetg(pf+1,t_VEC);
+  gel(rep,1) = zerocol(N);
+  for (pi=i=1; i<=f; i++,pi*=p)
+  {
+    long t = perm[i];
+    for (j=1; j<p; j++)
+      for (k=1; k<=pi; k++) gel(rep, j*pi+k) = ZC_add_coeff(gel(rep,k), t, j);
+  }
+  return rep;
+}
+
+/* = 1 if equation y^2 = z^deg(T) * T(x/z) has a pr-adic rational solution
+ * (possibly (1,y,0) = oo), 0 otherwise.
+ * coeffs of T are algebraic integers in nf */
+long
+nf_hyperell_locally_soluble(GEN nf,GEN T,GEN pr)
+{
+  GEN repr, zinit, p1;
+  pari_sp av = avma;
+
+  if (typ(T)!=t_POL) pari_err_TYPE("nf_hyperell_locally_soluble",T);
+  if (gequal0(T)) return 1;
+  checkprid(pr); nf = checknf(nf);
+  if (equaliu(pr_get_p(pr), 2))
+  { /* tough case */
+    zinit = Idealstar(nf, idealpows(nf,pr,1+2*pr_get_e(pr)), nf_INIT);
+    if (psquare2nf(nf,constant_term(T),pr,zinit)) return 1;
+    if (psquare2nf(nf, leading_term(T),pr,zinit)) return 1;
+  }
+  else
+  {
+    zinit = zkmodprinit(nf, pr);
+    if (psquarenf(nf,constant_term(T),pr,zinit)) return 1;
+    if (psquarenf(nf, leading_term(T),pr,zinit)) return 1;
+  }
+  repr = repres(nf,pr);
+  if (zpsolnf(nf,T,pr,0,gen_1,gen_0,repr,zinit)) { avma=av; return 1; }
+  p1 = pr_get_gen(pr);
+  if (zpsolnf(nf,RgX_recip_shallow(T),pr,1,p1,gen_0,repr,zinit)) { avma=av; return 1; }
+
+  avma = av; return 0;
+}
+
+/* return a * denom(a)^2, as an 'liftalg' */
+static GEN
+den_remove(GEN nf, GEN a)
+{
+  GEN da;
+  a = nf_to_scalar_or_basis(nf, a);
+  switch(typ(a))
+  {
+    case t_INT: return a;
+    case t_FRAC: return mulii(gel(a,1), gel(a,2));
+    case t_COL:
+      a = Q_remove_denom(a, &da);
+      if (da) a = ZC_Z_mul(a, da);
+      a = coltoliftalg(nf, a);
+      return a;
+    default: pari_err_TYPE("nfhilbert",a);
+      return NULL;/*not reached*/
+  }
+}
+
+static long
+hilb2nf(GEN nf,GEN a,GEN b,GEN p)
+{
+  pari_sp av = avma;
+  long rep;
+  GEN pol;
+
+  a = den_remove(nf, a);
+  b = den_remove(nf, b);
+  pol = mkpoln(3, a, gen_0, b);
+  /* varn(nf.pol) = 0, pol is not a valid GEN  [as in Pol([x,x], x)].
+   * But it is only used as a placeholder, hence it is not a problem */
+
+  rep = nf_hyperell_locally_soluble(nf,pol,p)? 1: -1;
+  avma = av; return rep;
+}
+
+/* local quadratic Hilbert symbol (a,b)_pr, for a,b (non-zero) in nf */
+static long
+nfhilbertp(GEN nf, GEN a, GEN b, GEN pr)
+{
+  GEN t;
+  long va, vb, rep;
+  pari_sp av = avma;
+
+  if (equaliu(pr_get_p(pr), 2)) return hilb2nf(nf,a,b,pr);
+
+  /* pr not above 2, compute t = tame symbol */
+  va = nfval(nf,a,pr);
+  vb = nfval(nf,b,pr);
+  if (!odd(va) && !odd(vb)) { avma = av; return 1; }
+  /* Trick: pretend the exponent is 2, result is OK up to squares ! */
+  t = famat_makecoprime(nf, mkvec2(a,b), mkvec2s(vb, -va),
+                        pr, idealhnf_two(nf, pr), gen_2);
+  if (typ(t) == t_INT) {
+    if (odd(va) && odd(vb)) t = negi(t);
+    /* t = (-1)^(v(a)v(b)) a^v(b) b^(-v(a)) */
+    rep = Z_quad_char(t, pr);
+  }
+  else if (ZV_isscalar(t)) {
+    t = gel(t,1);
+    if (odd(va) && odd(vb)) t = negi(t);
+    /* t = (-1)^(v(a)v(b)) a^v(b) b^(-v(a)) */
+    rep = Z_quad_char(t, pr);
+  } else {
+    if (odd(va) && odd(vb)) t = ZC_neg(t);
+    /* t = (-1)^(v(a)v(b)) a^v(b) b^(-v(a)) */
+    rep = quad_char(nf, t, pr);
+  }
+  /* quad. symbol is image of t by the quadratic character  */
+  avma = av; return rep;
+}
+
+/* Global quadratic Hilbert symbol (a,b):
+ *  =  1 if X^2 - aY^2 - bZ^2 has a point in projective plane
+ *  = -1 otherwise
+ * a, b should be non-zero */
+long
+nfhilbert(GEN nf, GEN a, GEN b)
+{
+  pari_sp av = avma;
+  long i, l;
+  GEN S, S2, Sa, Sb, sa, sb;
+
+  nf = checknf(nf);
+  a = nf_to_scalar_or_basis(nf, a);
+  b = nf_to_scalar_or_basis(nf, b);
+  /* local solutions in real completions ? [ error in nfsign if arg is 0 ]*/
+  sa = nfsign(nf, a);
+  sb = nfsign(nf, b); l = lg(sa);
+  for (i=1; i<l; i++)
+    if (sa[i] && sb[i])
+    {
+      if (DEBUGLEVEL>3)
+        err_printf("nfhilbert not soluble at real place %ld\n",i);
+      avma = av; return -1;
+    }
+
+  /* local solutions in finite completions ? (pr | 2ab)
+   * primes above 2 are toughest. Try the others first */
+  Sa = idealfactor(nf, a);
+  Sb = idealfactor(nf, b);
+  S2 = idealfactor(nf, gen_2);
+  S = merge_factor(Sa, Sb, (void*)&cmp_prime_ideal, &cmp_nodata);
+  S = merge_factor(S,  S2, (void*)&cmp_prime_ideal, &cmp_nodata);
+  S = gel(S,1);
+  /* product of all hilbertp is 1 ==> remove one prime (above 2!) */
+  for (i=lg(S)-1; i>1; i--)
+    if (nfhilbertp(nf,a,b,gel(S,i)) < 0)
+    {
+      if (DEBUGLEVEL>3)
+        err_printf("nfhilbert not soluble at finite place %Ps\n",S[i]);
+      avma = av; return -1;
+    }
+  avma = av; return 1;
+}
+
+long
+nfhilbert0(GEN nf,GEN a,GEN b,GEN p)
+{
+  nf = checknf(nf);
+  if (p) {
+    checkprid(p);
+    if (gequal0(a)) pari_err_DOMAIN("nfhilbert", "a", "=", gen_0, a);
+    if (gequal0(b)) pari_err_DOMAIN("nfhilbert", "b", "=", gen_0, b);
+    return nfhilbertp(nf,a,b,p);
+  }
+  return nfhilbert(nf,a,b);
+}
+
+/* S a list of prime ideal in idealprimedec format. Return res:
+ * res[1] = generators of (S-units / units), as polynomials
+ * res[2] = [perm, HB, den], for bnfissunit
+ * res[3] = [] (was: log. embeddings of res[1])
+ * res[4] = S-regulator ( = R * det(res[2]) * \prod log(Norm(S[i])))
+ * res[5] = S class group
+ * res[6] = S */
+GEN
+bnfsunit(GEN bnf,GEN S,long prec)
+{
+  pari_sp av = avma;
+  long i,j,ls;
+  GEN p1,nf,gen,M,U,H;
+  GEN sunit,card,sreg,res,pow;
+
+  if (!is_vec_t(typ(S))) pari_err_TYPE("bnfsunit",S);
+  bnf = checkbnf(bnf);
+  nf = bnf_get_nf(bnf);
+  gen = bnf_get_gen(bnf);
+
+  sreg = bnf_get_reg(bnf);
+  res=cgetg(7,t_VEC);
+  gel(res,1) = gel(res,2) = gel(res,3) = cgetg(1,t_VEC);
+  gel(res,4) = sreg;
+  gel(res,5) = bnf_get_clgp(bnf);
+  gel(res,6) = S; ls=lg(S);
+
+ /* M = relation matrix for the S class group (in terms of the class group
+  * generators given by gen)
+  * 1) ideals in S
+  */
+  M = cgetg(ls,t_MAT);
+  for (i=1; i<ls; i++)
+  {
+    p1 = gel(S,i); checkprid(p1);
+    gel(M,i) = isprincipal(bnf,p1);
+  }
+  /* 2) relations from bnf class group */
+  M = shallowconcat(M, diagonal_shallow(bnf_get_cyc(bnf)));
+
+  /* S class group */
+  H = ZM_hnfall(M, &U, 1);
+  card = gen_1;
+  if (lg(H) > 1)
+  { /* non trivial (rare!) */
+    GEN A, u, D = ZM_snfall_i(H, &u, NULL, 1);
+    card = detcyc(D, &i); setlg(D,i);
+    A = cgetg(i,t_VEC); pow = ZM_inv(u,gen_1);
+    for(i--; i; i--) gel(A,i) = idealfactorback(nf, gen, gel(pow,i), 1);
+    gel(res,5) = mkvec3(card, D, A);
+  }
+
+  /* S-units */
+  if (ls>1)
+  {
+    GEN den, Sperm, perm, dep, B, A, U1 = U;
+    long lH, lB;
+
+   /* U1 = upper left corner of U, invertible. S * U1 = principal ideals
+    * whose generators generate the S-units */
+    setlg(U1,ls); p1 = cgetg(ls, t_MAT); /* p1 is junk for mathnfspec */
+    for (i=1; i<ls; i++) { setlg(U1[i],ls); gel(p1,i) = cgetg(1,t_COL); }
+    H = mathnfspec(U1,&perm,&dep,&B,&p1);
+    lH = lg(H);
+    lB = lg(B);
+    if (lg(dep) > 1 && lgcols(dep) > 1) pari_err_BUG("bnfsunit");
+   /*                   [ H B  ]            [ H^-1   - H^-1 B ]
+    * perm o HNF(U1) =  [ 0 Id ], inverse = [  0         Id   ]
+    * (permute the rows)
+    * S * HNF(U1) = _integral_ generators for S-units  = sunit */
+    Sperm = cgetg(ls, t_VEC); sunit = cgetg(ls, t_VEC);
+    for (i=1; i<ls; i++) Sperm[i] = S[perm[i]]; /* S o perm */
+
+    setlg(Sperm, lH);
+    for (i=1; i<lH; i++)
+    {
+      GEN v = isprincipalfact(bnf, NULL,Sperm,gel(H,i), nf_GEN|nf_FORCE);
+      gel(sunit,i) = coltoliftalg(nf, gel(v,2));
+    }
+    for (j=1; j<lB; j++,i++)
+    {
+      GEN v = isprincipalfact(bnf, gel(Sperm,i),Sperm,gel(B,j),nf_GEN|nf_FORCE);
+      gel(sunit,i) = coltoliftalg(nf, gel(v,2));
+   }
+    den = ZM_det_triangular(H); H = ZM_inv(H,den);
+    A = shallowconcat(H, ZM_neg(ZM_mul(H,B))); /* top part of inverse * den */
+    /* HNF in split form perm + (H B) [0 Id missing] */
+    gel(res,1) = sunit;
+    gel(res,2) = mkvec3(perm,A,den);
+  }
+
+  /* S-regulator */
+  sreg = mpmul(sreg,card);
+  for (i=1; i<ls; i++)
+  {
+    GEN p = pr_get_p( gel(S,i) );
+    sreg = mpmul(sreg, logr_abs(itor(p,prec)));
+  }
+  gel(res,4) = sreg;
+  return gerepilecopy(av,res);
+}
+
+static GEN
+make_unit(GEN nf, GEN bnfS, GEN *px)
+{
+  long lB, cH, i, ls;
+  GEN den, gen, S, v, p1, xp, xb, N, N0, HB, perm;
+
+  if (gequal0(*px)) return NULL;
+  S = gel(bnfS,6); ls = lg(S);
+  if (ls==1) return cgetg(1, t_COL);
+
+  xb = nf_to_scalar_or_basis(nf,*px);
+  switch(typ(xb))
+  {
+    case t_INT:  N = xb; break;
+    case t_FRAC: N = mulii(gel(xb,1),gel(xb,2)); break;
+    default: { GEN d = Q_denom(xb); N = mulii(idealnorm(nf,gmul(*px,d)), d); }
+  } /* relevant primes divide N */
+  if (is_pm1(N)) return zerocol(ls -1);
+
+  p1 = gel(bnfS,2);
+  perm = gel(p1,1);
+  HB   = gel(p1,2);
+  den  = gel(p1,3);
+  cH = nbrows(HB);
+  lB = lg(HB) - cH;
+  v = zero_zv(ls-1);
+  N0 = N;
+  for (i=1; i<ls; i++)
+  {
+    GEN P = gel(S,i), p = pr_get_p(P);
+    if ( remii(N, p) == gen_0 )
+    {
+      v[i] = nfval(nf,xb,P);
+      (void)Z_pvalrem(N0, p, &N0);
+    }
+  }
+  if (!is_pm1(N0)) return NULL;
+  /* here, x = S v */
+  p1 = vecpermute(v, perm);
+  v = ZM_zc_mul(HB, p1);
+  for (i=1; i<=cH; i++)
+  {
+    GEN r, w = dvmdii(gel(v,i), den, &r);
+    if (r != gen_0) return NULL;
+    gel(v,i) = w;
+  }
+  p1 += cH; p1[0] = evaltyp(t_VECSMALL) | evallg(lB);
+  v = shallowconcat(v, zc_to_ZC(p1)); /* append bottom of p1 (= [0 Id] part) */
+
+  gen = gel(bnfS,1);
+  xp = cgetg(1, t_MAT);
+  for (i=1; i<ls; i++)
+  {
+    GEN e = gel(v,i);
+    if (!signe(e)) continue;
+    xp = famat_mul(xp, to_famat_shallow(gel(gen,i), negi(e)));
+  }
+  if (lg(xp) > 1) *px = famat_mul(xp, to_famat_shallow(xb, gen_1));
+  return v;
+}
+
+/* Analog to bnfisunit, for S-units. Let v the result
+ * If x not an S-unit, v = []~, else
+ * x = \prod_{i=0}^r e_i^v[i] * prod{i=r+1}^{r+s} s_i^v[i]
+ * where the e_i are the field units (cf bnfisunit), and the s_i are
+ * the S-units computed by bnfsunit (in the same order) */
+GEN
+bnfissunit(GEN bnf,GEN bnfS,GEN x)
+{
+  pari_sp av = avma;
+  GEN v, w, nf;
+
+  bnf = checkbnf(bnf);
+  nf = bnf_get_nf(bnf);
+  if (typ(bnfS)!=t_VEC || lg(bnfS)!=7) pari_err_TYPE("bnfissunit",bnfS);
+  x = nf_to_scalar_or_alg(nf,x);
+  v = NULL;
+  if ( (w = make_unit(nf, bnfS, &x)) ) v = bnfisunit(bnf, x);
+  if (!v || lg(v) == 1) { avma = av; return cgetg(1,t_COL); }
+  return gerepileupto(av, concat(v, w));
+}
+
+static void
+pr_append(GEN nf, GEN rel, GEN p, GEN *prod, GEN *S1, GEN *S2)
+{
+  if (dvdii(*prod, p)) return;
+  *prod = mulii(*prod, p);
+  *S1 = shallowconcat(*S1, idealprimedec(nf,p));
+  *S2 = shallowconcat(*S2, idealprimedec(rel,p));
+}
+
+/* N a t_INT */
+static void
+Zfa_pr_append(GEN nf,GEN rel,GEN N,GEN *prod,GEN *S1,GEN *S2)
+{
+  if (!is_pm1(N))
+  {
+    GEN v = gel(Z_factor(N),1);
+    long i, l = lg(v);
+    for (i=1; i<l; i++) pr_append(nf,rel,gel(v,i),prod,S1,S2);
+  }
+}
+/* N a t_INT or t_FRAC */
+static void
+fa_pr_append(GEN nf,GEN rel,GEN N,GEN *prod,GEN *S1,GEN *S2)
+{
+  if (typ(N) == t_FRAC)
+  {
+    Zfa_pr_append(nf,rel,gel(N,1),prod,S1,S2);
+    Zfa_pr_append(nf,rel,gel(N,2),prod,S1,S2);
+  }
+  else
+    Zfa_pr_append(nf,rel,N,prod,S1,S2);
+}
+
+/* apply lift(rnfeltup) to all coeffs, without rnf structure */
+static GEN
+nfX_eltup(GEN nf, GEN rnfeq, GEN x)
+{
+  long i, l;
+  GEN zknf, czknf, y = cgetg_copy(x, &l);
+  y[1] = x[1]; nf_nfzk(nf, rnfeq, &zknf, &czknf);
+  for (i=2; i<l; i++) gel(y,i) = nfeltup(nf, gel(x,i), zknf, czknf);
+  return y;
+}
+/* FIXME: remove this */
+static void
+nfX_fix_var(GEN P, long v)
+{
+  long i, l = lg(P);
+  for (i=2; i<l; i++)
+  {
+    GEN c = gel(P,i);
+    if (typ(c) == t_POL) setvarn(c, v);
+  }
+}
+
+GEN
+rnfisnorminit(GEN T, GEN relpol, int galois)
+{
+  pari_sp av = avma;
+  long i, l, drel;
+  GEN prod, S1, S2, gen, cyc, bnf, nf, nfabs, rnfeq, bnfabs, k, polabs;
+  GEN y = cgetg(9, t_VEC);
+
+  if (galois < 0 || galois > 2) pari_err_FLAG("rnfisnorminit");
+  T = get_bnfpol(T, &bnf, &nf);
+  if (!bnf) bnf = Buchall(nf? nf: T, nf_FORCE, DEFAULTPREC);
+  if (!nf) nf = bnf_get_nf(bnf);
+
+  relpol = get_bnfpol(relpol, &bnfabs, &nfabs);
+  if (!gequal1(leading_term(relpol))) pari_err_IMPL("non monic relative equation");
+  drel = degpol(relpol);
+  if (drel <= 2) galois = 1;
+
+  relpol = RgX_nffix("rnfisnorminit", T, relpol, 1);
+  if (nf_get_degree(nf) == 1) /* over Q */
+    rnfeq = mkvec5(relpol,gen_0,gen_0,T,relpol);
+  else if (galois == 2) /* needs eltup+abstorel */
+    rnfeq = nf_rnfeq(nf, relpol);
+  else /* needs abstorel */
+    rnfeq = nf_rnfeqsimple(nf, relpol);
+  polabs = gel(rnfeq,1);
+  k = gel(rnfeq,3);
+  if (!bnfabs || !gequal0(k))
+    bnfabs = Buchall(polabs, nf_FORCE, nf_get_prec(nf));
+  if (!nfabs) nfabs = bnf_get_nf(bnfabs);
+
+  if (galois == 2)
+  {
+    GEN P;
+    long v = varn(T);
+    if (polabs == relpol)
+      P = relpol;
+    else
+    { /* FIXME: don't mess with variables, use proper priorities in nfabs. */
+      P = nfX_eltup(nf, rnfeq, relpol);
+      nfX_fix_var(P, v);
+    }
+    /* FIXME */
+    galois = nfissplit(gsubst(nfabs, nf_get_varn(nfabs), pol_x(v)), P);
+  }
+
+  prod = gen_1; S1 = S2 = cgetg(1, t_VEC);
+  cyc = bnf_get_cyc(bnfabs);
+  gen = bnf_get_gen(bnfabs); l = lg(cyc);
+  for(i=1; i<l; i++)
+  {
+    GEN g = gel(gen,i);
+    if (ugcd(umodiu(gel(cyc,i), drel), drel) == 1) break;
+    Zfa_pr_append(nf,bnfabs,gcoeff(g,1,1),&prod,&S1,&S2);
+  }
+  if (!galois)
+  {
+    GEN Ndiscrel = diviiexact(nf_get_disc(nfabs), powiu(nf_get_disc(nf), drel));
+    Zfa_pr_append(nf,bnfabs,absi(Ndiscrel), &prod,&S1,&S2);
+  }
+
+  gel(y,1) = bnf;
+  gel(y,2) = bnfabs;
+  gel(y,3) = relpol;
+  gel(y,4) = rnfeq;
+  gel(y,5) = prod;
+  gel(y,6) = S1;
+  gel(y,7) = S2;
+  gel(y,8) = stoi(galois); return gerepilecopy(av, y);
+}
+
+/* T as output by rnfisnorminit
+ * if flag=0 assume extension is Galois (==> answer is unconditional)
+ * if flag>0 add to S all primes dividing p <= flag
+ * if flag<0 add to S all primes dividing abs(flag)
+
+ * answer is a vector v = [a,b] such that
+ * x = N(a)*b and x is a norm iff b = 1  [assuming S large enough] */
+GEN
+rnfisnorm(GEN T, GEN x, long flag)
+{
+  pari_sp av = avma;
+  GEN bnf, rel, relpol, rnfeq, nfpol;
+  GEN nf, aux, H, U, Y, M, A, bnfS, sunitrel, futu, prod, S1, S2;
+  long L, i, drel, itu;
+
+  if (typ(T) != t_VEC || lg(T) != 9)
+    pari_err_TYPE("rnfisnorm [please apply rnfisnorminit()]", T);
+  bnf = gel(T,1);
+  rel = gel(T,2);
+  relpol = gel(T,3);
+  rnfeq = gel(T,4);
+  drel = degpol(relpol);
+  bnf = checkbnf(bnf);
+  rel = checkbnf(rel);
+  nf = bnf_get_nf(bnf);
+  x = nf_to_scalar_or_alg(nf,x);
+  if (gequal0(x)) { avma = av; return mkvec2(gen_0, gen_1); }
+  if (gequal1(x)) { avma = av; return mkvec2(gen_1, gen_1); }
+  if (gequalm1(x) && odd(drel)) { avma = av; return mkvec2(gen_m1, gen_1); }
+
+  /* build set T of ideals involved in the solutions */
+  nfpol = nf_get_pol(nf);
+  prod = gel(T,5);
+  S1   = gel(T,6);
+  S2   = gel(T,7);
+  if (flag && !gequal0(gel(T,8)))
+    pari_warn(warner,"useless flag in rnfisnorm: the extension is Galois");
+  if (flag > 0)
+  {
+    forprime_t T;
+    ulong p;
+    u_forprime_init(&T, 2, flag);
+    while ( (p = u_forprime_next(&T)) )
+      pr_append(nf,rel, utoipos(p),&prod,&S1,&S2);
+  }
+  else if (flag < 0)
+    Zfa_pr_append(nf,rel, utoipos(-flag),&prod,&S1,&S2);
+  /* overkill: prime ideals dividing x would be enough */
+  fa_pr_append(nf,rel,idealnorm(nf,x), &prod,&S1,&S2);
+
+  /* computation on T-units */
+  futu = shallowconcat(bnf_get_fu(rel), bnf_get_tuU(rel));
+  bnfS = bnfsunit(bnf,S1,LOWDEFAULTPREC);
+  sunitrel = shallowconcat(futu, gel(bnfsunit(rel,S2,LOWDEFAULTPREC), 1));
+
+  A = lift_intern(bnfissunit(bnf,bnfS,x));
+  L = lg(sunitrel);
+  itu = lg(nf_get_roots(nf))-1; /* index of torsion unit in bnfsunit(nf) output */
+  M = cgetg(L+1,t_MAT);
+  for (i=1; i<L; i++)
+  {
+    GEN u = eltabstorel(rnfeq, gel(sunitrel,i));
+    gel(sunitrel,i) = u;
+    u = bnfissunit(bnf,bnfS, gnorm(u));
+    if (lg(u) == 1) pari_err_BUG("rnfisnorm");
+    gel(u,itu) = lift_intern(gel(u,itu)); /* lift root of 1 part */
+    gel(M,i) = u;
+  }
+  aux = zerocol(lg(A)-1); gel(aux,itu) = utoipos( bnf_get_tuN(rel) );
+  gel(M,L) = aux;
+  H = ZM_hnfall(M, &U, 2);
+  Y = RgM_RgC_mul(U, inverseimage(H,A));
+  /* Y: sols of MY = A over Q */
+  setlg(Y, L);
+  aux = factorback2(sunitrel, gfloor(Y));
+  x = mkpolmod(x,nfpol);
+  if (!gequal1(aux)) x = gdiv(x, gnorm(aux));
+  x = lift_if_rational(x);
+  if (typ(aux) == t_POLMOD && degpol(nfpol) == 1)
+    gel(aux,2) = lift_if_rational(gel(aux,2));
+  return gerepilecopy(av, mkvec2(aux, x));
+}
+
+GEN
+bnfisnorm(GEN bnf, GEN x, long flag)
+{
+  pari_sp av = avma;
+  GEN T = rnfisnorminit(pol_x(MAXVARN), bnf, flag == 0? 1: 2);
+  return gerepileupto(av, rnfisnorm(T, x, flag == 1? 0: flag));
+}
diff --git a/src/basemath/concat.c b/src/basemath/concat.c
new file mode 100644
index 0000000..38d285a
--- /dev/null
+++ b/src/basemath/concat.c
@@ -0,0 +1,576 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                          CONCATENATION                          */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* assume A or B is a t_LIST */
+static GEN
+listconcat(GEN A, GEN B)
+{
+  long i, l1, lx;
+  GEN L, z, L1, L2;
+
+  if (typ(A) != t_LIST) {
+    L2 = list_data(B);
+    if (!L2) return mklistcopy(A);
+    lx = lg(L2) + 1;
+    z = listcreate();
+    list_data(z) = L = cgetg(lx, t_VEC);
+    for (i = 2; i < lx; i++) gel(L,i) = gcopy(gel(L2,i-1));
+    gel(L,1) = gcopy(A); return z;
+  } else if (typ(B) != t_LIST) {
+    L1 = list_data(A);
+    if (!L1) return mklistcopy(B);
+    lx = lg(L1) + 1;
+    z = listcreate();
+    list_data(z) = L = cgetg(lx, t_VEC);
+    for (i = 1; i < lx-1; i++) gel(L,i) = gcopy(gel(L1,i));
+    gel(L,i) = gcopy(B); return z;
+  }
+  /* A, B both t_LISTs */
+  L1 = list_data(A); if (!L1) return listcopy(B);
+  L2 = list_data(B); if (!L2) return listcopy(A);
+
+  l1 = lg(L1);
+  lx = l1-1 + lg(L2);
+  z = cgetg(3, t_LIST);
+  list_nmax(z) = 0;
+  list_data(z) = L = cgetg(lx, t_VEC);
+  L2 -= l1-1;
+  for (i=1; i<l1; i++) gel(L,i) = gclone(gel(L1,i));
+  for (   ; i<lx; i++) gel(L,i) = gclone(gel(L2,i));
+  return z;
+}
+
+/* assume A or B is a t_STR */
+static GEN
+strconcat(GEN x, GEN y)
+{
+  size_t l, lx;
+  char *sx = GENtostr_unquoted(x);
+  char *sy = GENtostr_unquoted(y), *str;
+  lx = strlen(sx);
+  l = nchar2nlong(lx + strlen(sy) + 1);
+  x = cgetg(l + 1, t_STR); str = GSTR(x);
+  strcpy(str,   sx);
+  strcpy(str+lx,sy); return x;
+}
+
+/* concat A and B vertically. Internal */
+GEN
+vconcat(GEN A, GEN B)
+{
+  long la, ha, hb, hc, i, j, T;
+  GEN M, a, b, c;
+
+  if (!A) return B;
+  if (!B) return A;
+  la = lg(A); if (la==1) return A;
+  T = typ(gel(A,1)); /* t_COL or t_VECSMALL */
+  ha = lgcols(A); M = cgetg(la,t_MAT);
+  hb = lgcols(B); hc = ha+hb-1;
+  for (j=1; j<la; j++)
+  {
+    c = cgetg(hc, T); gel(M, j) = c;
+    a = gel(A,j);
+    b = gel(B,j);
+    for (i=1; i<ha; i++) *++c = *++a;
+    for (i=1; i<hb; i++) *++c = *++b;
+  }
+  return M;
+}
+
+static void
+err_cat(GEN x, GEN y) { pari_err_OP("concatenation",x,y); }
+
+GEN
+shallowconcat(GEN x, GEN y)
+{
+  long tx=typ(x),ty=typ(y),lx=lg(x),ly=lg(y),i;
+  GEN z,p1;
+
+  if (tx==t_STR  || ty==t_STR)  return strconcat(x,y);
+  if (tx==t_LIST || ty==t_LIST) return listconcat(x,y);
+
+  if (tx==t_MAT && lx==1)
+  {
+    if (ty!=t_VEC) return gtomat(y);
+    if (ly==1) return cgetg(1, t_MAT);
+    err_cat(x,y);
+  }
+  if (ty==t_MAT && ly==1)
+  {
+    if (tx!=t_VEC) return gtomat(x);
+    if (lx==1) return cgetg(1, t_MAT);
+    err_cat(x,y);
+  }
+
+  if (tx == ty)
+  {
+    if (tx == t_MAT)
+    { if (lgcols(x) != lgcols(y)) err_cat(x,y); }
+    else
+      if (!is_matvec_t(tx) && tx != t_VECSMALL) return mkvec2(x, y);
+    z=cgetg(lx+ly-1,tx);
+    for (i=1; i<lx; i++) z[i]     = x[i];
+    for (i=1; i<ly; i++) z[lx+i-1]= y[i];
+    return z;
+  }
+
+  if (! is_matvec_t(tx))
+  {
+    if (! is_matvec_t(ty)) return mkvec2(x, y);
+    z=cgetg(ly+1,ty);
+    if (ty != t_MAT) p1 = x;
+    else
+    {
+      if (lgcols(y)!=2) err_cat(x,y);
+      p1 = mkcol(x);
+    }
+    for (i=2; i<=ly; i++) z[i] = y[i-1];
+    gel(z, 1) = p1; return z;
+  }
+  if (! is_matvec_t(ty))
+  {
+    z=cgetg(lx+1,tx);
+    if (tx != t_MAT) p1 = y;
+    else
+    {
+      if (lgcols(x)!=2) err_cat(x,y);
+      p1 = mkcol(y);
+    }
+    for (i=1; i<lx; i++) z[i]=x[i];
+    gel(z, lx) = p1; return z;
+  }
+
+  switch(tx)
+  {
+    case t_VEC:
+      switch(ty)
+      {
+        case t_COL:
+          if (lx<=2) return (lx==1)? y: shallowconcat(gel(x,1),y);
+          if (ly>=3) break;
+          return (ly==1)? x: shallowconcat(x,gel(y,1));
+        case t_MAT:
+          z=cgetg(ly,t_MAT); if (lx != ly) break;
+          for (i=1; i<ly; i++) gel(z,i) = shallowconcat(gel(x,i),gel(y,i));
+          return z;
+      }
+      break;
+
+    case t_COL:
+      switch(ty)
+      {
+        case t_VEC:
+          if (lx<=2) return (lx==1)? y: shallowconcat(gel(x,1), y);
+          if (ly>=3) break;
+          return (ly==1)? x: shallowconcat(x, gel(y,1));
+        case t_MAT:
+          if (lx != lgcols(y)) break;
+          z=cgetg(ly+1,t_MAT);  gel(z,1) = x;
+          for (i=2; i<=ly; i++) gel(z,i) = gel(y,i-1);
+          return z;
+      }
+      break;
+
+    case t_MAT:
+      switch(ty)
+      {
+        case t_VEC:
+          z=cgetg(lx, t_MAT); if (ly != lx) break;
+          for (i=1; i<lx; i++) gel(z,i) = shallowconcat(gel(x,i), gel(y,i));
+          return z;
+        case t_COL:
+          if (ly != lgcols(x)) break;
+          z=cgetg(lx+1,t_MAT); gel(z,lx) = y;
+          for (i=1; i<lx; i++) z[i]=x[i];
+          return z;
+      }
+      break;
+  }
+  err_cat(x,y);
+  return NULL; /* not reached */
+}
+
+/* see catmany() */
+static GEN
+catmanyMAT(GEN y1, GEN y2)
+{
+  long i, h = 0, L = 1;
+  GEN z, y;
+  for (y = y2; y >= y1; y--)
+  {
+    GEN c = gel(y,0);
+    long nc = lg(c)-1;
+    if (nc == 0) continue;
+    if (h != lgcols(c))
+    {
+      if (h) err_cat(gel(y2,0), c);
+      h = lgcols(c);
+    }
+    L += nc;
+    z = new_chunk(nc) - 1;
+    for (i=1; i<=nc; i++) gel(z,i) = gel(c,i);
+  }
+  z = new_chunk(1);
+  *z = evaltyp(t_MAT) | evallg(L);
+  return z;
+}
+static GEN
+catmanySTR(GEN y1, GEN y2)
+{
+  long L = 1; /* final \0 */
+  GEN z, y;
+  char *s;
+  for (y = y1; y <= y2; y++)
+  {
+    char *c = GSTR( gel(y,0) );
+    L += strlen(c);
+  }
+  z = cgetg(nchar2nlong(L)+1, t_STR);
+  s = GSTR(z);
+  for (y = y1; y <= y2; y++)
+  {
+    char *c = GSTR( gel(y,0) );
+    long nc = strlen(c);
+    if (nc) { (void)strncpy(s, c, nc); s += nc; }
+  }
+  *s = 0; return z;
+}
+
+/* all entries in y have the same type t = t_VEC, COL, MAT or VECSMALL
+ * concatenate y[k1..k2], with yi = y + ki, k1 <= k2 */
+static GEN
+catmany(GEN y1, GEN y2, long t)
+{
+  long i, L;
+  GEN z, y;
+  if (y1 == y2) return gel(y1,0);
+  if (t == t_MAT) return catmanyMAT(y1, y2);
+  if (t == t_STR) return catmanySTR(y1, y2);
+  L = 1;
+  for (y = y2; y >= y1; y--)
+  {
+    GEN c = gel(y,0);
+    long nc = lg(c)-1;
+    if (nc == 0) continue;
+    L += nc;
+    z = new_chunk(nc) - 1;
+    for (i=1; i<=nc; i++) gel(z,i) = gel(c,i);
+  }
+  z = new_chunk(1);
+  *z = evaltyp(t) | evallg(L);
+  return z;
+}
+
+GEN
+shallowconcat1(GEN x)
+{
+  pari_sp av = avma, lim = stack_lim(av, 3);
+  long lx, t, i;
+  GEN z;
+  switch(typ(x))
+  {
+    case t_VEC:
+      lx = lg(x);
+      if (lx==1) pari_err_DOMAIN("concat","vector","=",x,x);
+      break;
+    case t_LIST:
+      if (!list_data(x)) pari_err_DOMAIN("concat","vector","=",x,x);
+      x = list_data(x); lx = lg(x);
+      break;
+    default:
+      pari_err_TYPE("concat",x);
+      return NULL; /* not reached */
+  }
+  if (lx==2) return gel(x,1);
+  z = gel(x,1); t = typ(z); i = 2;
+  if (is_matvec_t(t) || t == t_VECSMALL || t == t_STR)
+  { /* detect a "homogeneous" object: catmany is faster */
+    for (; i<lx; i++)
+      if (typ(gel(x,i)) != t) break;
+    z = catmany(x + 1, x + i-1, t);
+  }
+  for (; i<lx; i++) {
+    z = shallowconcat(z, gel(x,i));
+    if (low_stack(lim, stack_lim(av,3)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"concat: i = %ld", i);
+      z = gerepilecopy(av, z);
+    }
+  }
+  return z;
+}
+
+GEN
+concat1(GEN x)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, shallowconcat1(x));
+}
+
+/* fill M[xoff+i, yoff+j] with the contents of c ( c * Id_n if scalar ) */
+static void
+matfill(GEN M, GEN c, long xoff, long yoff, long n)
+{
+  long i, j, h, l;
+  l = lg(c); if (l == 1) return;
+  switch(typ(c))
+  {
+    case t_VEC:
+      for (i = 1; i < l; i++)
+        gcoeff(M,xoff+1,yoff+i) = gel(c,i);
+      break;
+    case t_COL:
+      for (i = 1; i < l; i++)
+        gcoeff(M,xoff+i,yoff+1) = gel(c,i);
+      break;
+    case t_MAT:
+      h = lgcols(c);
+      for (j = 1; j < l; j++)
+        for (i = 1; i < h; i++) gcoeff(M,xoff+i,yoff+j) = gcoeff(c,i,j);
+      break;
+    default:
+      for (i = 1; i <= n; i++)
+        gcoeff(M, xoff+i, yoff+i) = c;
+      break;
+  }
+}
+
+static GEN
+_matsize(GEN x)
+{
+  long t = typ(x), L = lg(x) - 1;
+  switch(t)
+  { /* matsize */
+    case t_VEC: return mkvecsmall2(1, L);
+    case t_COL: return mkvecsmall2(L, 1);
+    case t_MAT: return mkvecsmall2(L? nbrows(x): 0, L);
+    default:
+      if (is_noncalc_t(t)) pari_err_TYPE("_matsize", x);
+      return mkvecsmall2(1, 1);
+  }
+}
+
+GEN
+shallowmatconcat(GEN v)
+{
+  long i, j, h, l = lg(v), L = 0, H = 0;
+  GEN M, maxh, maxl;
+  if (l == 1) return cgetg(1,t_MAT);
+  switch(typ(v))
+  {
+    case t_VEC:
+      for (i = 1; i < l; i++)
+      {
+        GEN c = gel(v,i);
+        GEN s = _matsize(c);
+        H = maxss(H, s[1]);
+        L += s[2];
+      }
+      M = zeromatcopy(H, L);
+      L = 0;
+      for (i = 1; i < l; i++)
+      {
+        GEN c = gel(v,i);
+        GEN s = _matsize(c);
+        matfill(M, c, 0, L, 1);
+        L += s[2];
+      }
+      return M;
+
+    case t_COL:
+      for (i = 1; i < l; i++)
+      {
+        GEN c = gel(v,i);
+        GEN s = _matsize(c);
+        H += s[1];
+        L = maxss(L, s[2]);
+      }
+      M = zeromatcopy(H, L);
+      H = 0;
+      for (i = 1; i < l; i++)
+      {
+        GEN c = gel(v,i);
+        GEN s = _matsize(c);
+        matfill(M, c, H, 0, 1);
+        H += s[1];
+      }
+      return M;
+    case t_MAT:
+      h = lgcols(v);
+      maxh = zero_zv(h-1);
+      maxl = zero_zv(l-1);
+      for (j = 1; j < l; j++)
+        for (i = 1; i < h; i++)
+        {
+          GEN c = gcoeff(v,i,j);
+          GEN s = _matsize(c);
+          if (s[1] > maxh[i]) maxh[i] = s[1];
+          if (s[2] > maxl[j]) maxl[j] = s[2];
+        }
+      for (i = 1, H = 0; i < h; i++) H += maxh[i];
+      for (j = 1, L = 0; j < l; j++) L += maxl[j];
+      M = zeromatcopy(H, L);
+      for (j = 1, L = 0; j < l; j++)
+      {
+        for (i = 1, H = 0; i < h; i++)
+        {
+          GEN c = gcoeff(v,i,j);
+          matfill(M, c, H, L, minss(maxh[i], maxl[j]));
+          H += maxh[i];
+        }
+        L += maxl[j];
+      }
+      return M;
+    default:
+      pari_err_TYPE("shallowmatconcat", v);
+      return NULL;
+  }
+}
+GEN
+matconcat(GEN v)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, shallowmatconcat(v));
+}
+
+GEN
+concat(GEN x, GEN y)
+{
+  long tx, lx,ty,ly,i;
+  GEN z,p1;
+
+  if (!y) return concat1(x);
+  tx = typ(x);
+  ty = typ(y);
+  if (tx==t_STR  || ty==t_STR)
+  {
+    pari_sp av = avma;
+    return gerepileuptoleaf(av, strconcat(x,y));
+  }
+  if (tx==t_LIST || ty==t_LIST) return listconcat(x,y);
+  lx=lg(x); ly=lg(y);
+
+  if (tx==t_MAT && lx==1)
+  {
+    if (ty!=t_VEC) return gtomat(y);
+    if (ly==1) return cgetg(1, t_MAT);
+    err_cat(x,y);
+  }
+  if (ty==t_MAT && ly==1)
+  {
+    if (tx!=t_VEC) return gtomat(x);
+    if (lx==1) return cgetg(1, t_MAT);
+    err_cat(x,y);
+  }
+
+  if (tx == ty)
+  {
+    if (tx == t_MAT && lgcols(x) != lgcols(y)) err_cat(x,y);
+    if (!is_matvec_t(tx))
+    {
+      if (tx != t_VECSMALL) return mkvec2copy(x, y);
+      z = cgetg(lx+ly-1,t_VECSMALL);
+      for (i=1; i<lx; i++) z[i]     = x[i];
+      for (i=1; i<ly; i++) z[lx+i-1]= y[i];
+      return z;
+    }
+    z=cgetg(lx+ly-1,tx);
+    for (i=1; i<lx; i++) gel(z,i)     = gcopy(gel(x,i));
+    for (i=1; i<ly; i++) gel(z,lx+i-1)= gcopy(gel(y,i));
+    return z;
+  }
+
+  if (! is_matvec_t(tx))
+  {
+    if (! is_matvec_t(ty)) return mkvec2copy(x, y);
+    z=cgetg(ly+1,ty);
+    if (ty != t_MAT) p1 = gcopy(x);
+    else
+    {
+      if (lgcols(y)!=2) err_cat(x,y);
+      p1 = mkcolcopy(x);
+    }
+    for (i=2; i<=ly; i++) gel(z,i) = gcopy(gel(y,i-1));
+    gel(z,1) = p1; return z;
+  }
+  if (! is_matvec_t(ty))
+  {
+    z=cgetg(lx+1,tx);
+    if (tx != t_MAT) p1 = gcopy(y);
+    else
+    {
+      if (lgcols(x)!=2) err_cat(x,y);
+      p1 = mkcolcopy(y);
+    }
+    for (i=1; i<lx; i++) gel(z,i) = gcopy(gel(x,i));
+    gel(z,lx) = p1; return z;
+  }
+
+  switch(tx)
+  {
+    case t_VEC:
+      switch(ty)
+      {
+        case t_COL:
+          if (lx<=2) return (lx==1)? gcopy(y): concat(gel(x,1),y);
+          if (ly>=3) break;
+          return (ly==1)? gcopy(x): concat(x,gel(y,1));
+        case t_MAT:
+          z=cgetg(ly,t_MAT); if (lx != ly) break;
+          for (i=1; i<ly; i++) gel(z,i) = concat(gel(x,i),gel(y,i));
+          return z;
+      }
+      break;
+
+    case t_COL:
+      switch(ty)
+      {
+        case t_VEC:
+          if (lx<=2) return (lx==1)? gcopy(y): concat(gel(x,1),y);
+          if (ly>=3) break;
+          return (ly==1)? gcopy(x): concat(x,gel(y,1));
+        case t_MAT:
+          if (lx != lgcols(y)) break;
+          z=cgetg(ly+1,t_MAT); gel(z,1) = gcopy(x);
+          for (i=2; i<=ly; i++) gel(z,i) = gcopy(gel(y,i-1));
+          return z;
+      }
+      break;
+
+    case t_MAT:
+      switch(ty)
+      {
+        case t_VEC:
+          z=cgetg(lx,t_MAT); if (ly != lx) break;
+          for (i=1; i<lx; i++) gel(z,i) = concat(gel(x,i),gel(y,i));
+          return z;
+        case t_COL:
+          if (ly != lgcols(x)) break;
+          z=cgetg(lx+1,t_MAT); gel(z,lx) = gcopy(y);
+          for (i=1; i<lx; i++) gel(z,i) = gcopy(gel(x,i));
+          return z;
+      }
+      break;
+  }
+  err_cat(x,y);
+  return NULL; /* not reached */
+}
diff --git a/src/basemath/ellanal.c b/src/basemath/ellanal.c
new file mode 100644
index 0000000..e9f0860
--- /dev/null
+++ b/src/basemath/ellanal.c
@@ -0,0 +1,1343 @@
+/* Copyright (C) 2010  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                  L functions of elliptic curves                **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* Generic Buhler-Gross algorithm */
+
+struct bg_data
+{
+  GEN E, N; /* ell, conductor */
+  GEN bnd; /* t_INT; will need all an for n <= bnd */
+  ulong rootbnd; /* sqrt(bnd) */
+  GEN an; /* t_VECSMALL: cache of ap, n <= rootbnd */
+  GEN ap; /* t_VECSMALL: cache of ap, p <= rootbnd */
+  GEN p;  /* t_VECSMALL: primes <= rootbnd */
+};
+
+typedef void bg_fun(void*el, GEN *psum, GEN n, GEN a, long jmax);
+
+/* a = a_n, where p = bg->pp[i] divides n, and lasta = a_{n/p}.
+ * Call fun(E, psum, N, a_N, 0), for all N, n | N, P^+(N) <= p, a_N != 0,
+ * i.e. assumes that fun accumulates psum += a_N * w(N) */
+static void
+gen_BG_add(void *E, bg_fun *fun, struct bg_data *bg, GEN *psum, GEN n, long i, GEN a, GEN lasta)
+{
+  pari_sp av = avma, lim = stack_lim(av,2);
+  long j;
+  ulong nn = itou_or_0(n);
+  if (nn && nn <= bg->rootbnd) bg->an[nn] = itos(a);
+
+  if (signe(a))
+  {
+    fun(E, psum, n, a, 0);
+    j = 1;
+  }
+  else
+    j = i;
+  for(; j <= i; j++)
+  {
+    ulong p = bg->p[j];
+    GEN nexta, pn = mului(p, n);
+    if (cmpii(pn, bg->bnd) > 0) return;
+    nexta = mulis(a, bg->ap[j]);
+    if (i == j && umodiu(bg->N, p)) nexta = subii(nexta, mului(p, lasta));
+    gen_BG_add(E, fun, bg, psum, pn, j, nexta, a);
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"gen_BG_add, p=%lu",p);
+      *psum = gerepilecopy(av, *psum);
+    }
+  }
+}
+
+static void
+gen_BG_init(struct bg_data *bg, GEN E, GEN N, GEN bnd, GEN ap)
+{
+  pari_sp av;
+  long i = 1, l;
+  bg->E = E; bg->N = N;
+  bg->bnd = bnd;
+  bg->rootbnd = itou(sqrtint(bnd));
+  bg->p = primes_upto_zv(bg->rootbnd);
+  l = lg(bg->p);
+  if (ap)
+  { /* reuse known values */
+    i = lg(ap);
+    bg->ap = vecsmall_lengthen(ap, maxss(l,i)-1);
+  }
+  else bg->ap = cgetg(l, t_VECSMALL);
+  av = avma;
+  for (  ; i < l; i++, avma = av) bg->ap[i] = itos(ellap(E, utoipos(bg->p[i])));
+  avma = av;
+  bg->an = zero_zv(bg->rootbnd);
+  bg->an[1] = 1;
+}
+
+static GEN
+gen_BG_rec(void *E, bg_fun *fun, struct bg_data *bg, GEN sum0)
+{
+  long i, j, lp = lg(bg->p)-1, lim;
+  GEN bndov2 = shifti(bg->bnd, -1);
+  pari_sp av = avma, av2;
+  GEN sum, p;
+  forprime_t S;
+  (void)forprime_init(&S, utoipos(bg->p[lp]+1), bg->bnd);
+  av2 = avma; lim = stack_lim(av2,1);
+  if(DEBUGLEVEL)
+    err_printf("1st stage, using recursion for p <= %ld\n", bg->p[lp]);
+  sum = gcopy(sum0);
+  for (i = 1; i <= lp; i++)
+  {
+    ulong pp = bg->p[i];
+    long ap = bg->ap[i];
+    gen_BG_add(E, fun, bg, &sum, utoipos(pp), i, stoi(ap), gen_1);
+    if (low_stack(lim, stack_lim(av2,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ellL1, p=%lu",pp);
+      sum = gerepileupto(av2, sum);
+    }
+  }
+  if (DEBUGLEVEL) err_printf("2nd stage, looping for p <= %Ps\n", bndov2);
+  while ( (p = forprime_next(&S)) )
+  {
+    long jmax;
+    GEN ap = ellap(bg->E, p);
+    if (!signe(ap)) continue;
+
+    jmax = itou( divii(bg->bnd, p) ); /* 2 <= jmax <= el->rootbound */
+    fun(E, &sum, p, ap, -jmax); /*Beware: create a cache on the stack */
+    for (j = 2;  j <= jmax; j++)
+    {
+      long aj = bg->an[j];
+      GEN a, n;
+      if (!aj) continue;
+      a = mulis(ap, aj);
+      n = muliu(p, j);
+      fun(E, &sum, n, a, j);
+    }
+    if (low_stack(lim, stack_lim(av2,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ellL1, p=%Ps",p);
+      sum = gerepilecopy(av2, sum);
+    }
+    if (absi_cmp(p, bndov2) >= 0) break;
+  }
+  if (DEBUGLEVEL) err_printf("3nd stage, looping for p <= %Ps\n", bg->bnd);
+  while ( (p = forprime_next(&S)) )
+  {
+    GEN ap = ellap(bg->E, p);
+    if (!signe(ap)) continue;
+    fun(E, &sum, p, ap, 0);
+    if (low_stack(lim, stack_lim(av2,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ellL1, p=%Ps",p);
+      sum = gerepilecopy(av2, sum);
+    }
+  }
+  return gerepileupto(av, sum);
+}
+
+/* Computing L-series derivatives */
+
+/* Implementation by C. Delaunay and X.-F. Roblot
+   after a GP script of Tom Womack and John Cremona
+   and the corresponding section of Henri Cohen's book GTM 239
+   Generic Buhler-Gross iteration and baby-step-giant-step implementation
+   by Bill Allombert.
+*/
+
+/* used to compute exp((g*bgbnd + b) C) = baby[b] * giant[g] */
+struct babygiant
+{
+  GEN baby, giant;
+  ulong bgbnd;
+};
+
+struct ellld {
+  GEN E, N; /* ell, conductor */
+  GEN bnd; /* t_INT; will need all an for n <= bnd */
+  ulong rootbnd; /* floor(sqrt(bnd)) */
+  long r; /* we are computing L^{(r)}(1) */
+  GEN X; /* t_REAL, 2Pi / sqrt(N) */
+  GEN eX; /* t_REAL, exp(X) */
+  GEN emX; /* t_REAL, exp(-X) */
+  long epsbit;
+  /* only if r > 1 */
+  GEN alpha; /* t_VEC of t_REALs, except alpha[1] = gen_1 */
+  GEN A; /* t_VEC of t_REALs, A[1] = 1 */
+  /* only if r = 1 */
+  GEN gcache, gjcache; /* t_VEC of t_REALs */
+  /* only if r = 0 */
+  struct babygiant BG[1];
+};
+
+static GEN
+init_alpha(long m, long prec)
+{
+  GEN a, si, p1;
+  GEN s = gadd(pol_x(0), zeroser(0, m+1));
+  long i;
+
+  si = s;
+  p1 = gmul(mpeuler(prec), s);
+  for (i = 2; i <= m; i++)
+  {
+    si = gmul(si, s); /* = s^i */
+    p1 = gadd(p1, gmul(divru(szeta(i, prec), i), si));
+  }
+  p1 = gexp(p1, prec); /* t_SER of valuation = 0 */
+
+  a = cgetg(m+2, t_VEC);
+  for (i = 1; i <= m+1; i++) gel(a, i) = gel(p1, i+1);
+  return a;
+}
+
+/* assume r >= 2, return a t_VEC A of t_REALs of length > 2.
+ * NB: A[1] = 1 */
+static GEN
+init_A(long r, long m, long prec)
+{
+  const long l = m+1;
+  long j, s, n;
+  GEN A, B, ONE, fj;
+  pari_sp av0, av;
+
+  A = cgetg(l, t_VEC); /* will contain the final result */
+  gel(A,1) = real_1(prec);
+  for (j = 2; j < l; j++) gel(A,j) = cgetr(prec);
+  av0 = avma;
+  B = cgetg(l, t_VEC); /* scratch space */
+  for (j = 1; j < l; j++) gel(B,j) = cgetr(prec);
+  ONE = real_1(prec);
+  av = avma;
+
+  /* We alternate between two temp arrays A, B (initially virtually filled
+   * ONEs = 1.), which are swapped after each loop.
+   * After the last loop, we want A to contain the final array: make sure
+   * we swap an even number of times */
+  if (odd(r)) swap(A, B);
+
+  /* s = 1 */
+    for (n = 2; n <= m; n++)
+    {
+      GEN p3 = ONE; /* j = 1 */
+      for (j = 2; j <= n; j++) p3 = addrr(p3, divru(ONE, j));
+      affrr(p3, gel(B, n)); avma = av;
+    }
+  swap(A, B); /* B becomes the new A, old A becomes the new scratchspace */
+  for (s = 2; s <= r; s++)
+  {
+    for (n = 2; n <= m; n++)
+    {
+      GEN p3 = ONE; /* j = 1 */
+      for (j = 2; j <= n; j++) p3 = addrr(p3, divru(gel(A, j), j));
+      affrr(p3, gel(B, n)); avma = av;
+    }
+    swap(A, B); /* B becomes the new A, old A becomes the new scratchspace */
+  }
+
+  /* leave A[1] (division by 1) alone */
+  fj = ONE; /* will destroy ONE now */
+  for (j = 2; j < l; j++)
+  {
+    affrr(mulru(fj, j), fj);
+    affrr(divrr(gel(A,j), fj), gel(A,j));
+    avma = av;
+  }
+  avma = av0; return A;
+}
+
+/* x > 0 t_REAL, M >= 2 */
+static long
+estimate_prec_Sx(GEN x, long M)
+{
+  GEN p1, p2;
+  pari_sp av = avma;
+
+  x = rtor(x, DEFAULTPREC);
+  p1 = divri(powru(x, M-2), mpfact(M-1)); /* x^(M-2) / (M-1)! */
+  if (expo(x) < 0)
+  {
+    p2 = divrr(mulrr(p1, powru(x,3)), mulur(M,subsr(1,x)));/* x^(M+1)/(1-x)M! */
+    if (cmprr(p2,p1) < 0) p1 = p2;
+  }
+  avma = av; return expo(p1);
+}
+
+/* x a t_REAL */
+static long
+number_of_terms_Sx(GEN x, long epsbit)
+{
+  long M, M1, M2;
+  M1 = (long)(epsbit * 7.02901423262); /* epsbit * log(2) / (log(3) - 1) */
+  M2 = itos(ceil_safe(gmul2n(x,1))); /* >= 2x */
+  if (M2 < 2) M2 = 2;
+  M = M2;
+  for(;;)
+  {
+    if (estimate_prec_Sx(x, M) < -epsbit) M1 = M; else M2 = M;
+    M = (M1+M2+1) >> 1;
+    if (M >= M1) return M1;
+  }
+}
+
+/* X t_REAL, emX = exp(-X) t_REAL; return t_INT */
+static GEN
+cutoff_point(long r, GEN X, GEN emX, long epsbit, long prec)
+{
+  GEN M1 = ceil_safe(divsr(7*prec2nbits(prec)+1, X));
+  GEN M2 = gen_2, M = M1;
+  for(;;)
+  {
+    GEN c = divrr(powgi(emX, M), powru(mulri(X,M), r+1));
+    if (expo(c) < -epsbit) M1 = M; else M2 = M;
+    M = shifti(addii(M1, M2), -1);
+    if (cmpii(M2, M) >= 0) return M;
+  }
+}
+
+/* x "small" t_REAL, use power series expansion. Returns a t_REAL */
+static GEN
+compute_Gr_VSx(struct ellld *el, GEN x)
+{
+  pari_sp av = avma;
+  long r = el->r, n;
+  /* n = 2 */
+  GEN p1 = divrs(sqrr(x), -2); /* (-1)^(n-1) x^n / n! */
+  GEN p2 = x;
+  GEN p3 = shiftr(p1, -r);
+  for (n = 3; ; n++)
+  {
+    if (expo(p3) < -el->epsbit) return gerepilecopy(av, p2);
+    p2 = addrr(p2, p3);
+    p1 = divrs(mulrr(p1, x), -n); /* (-1)^(n-1) x^n / n! */
+    p3 = divri(p1, powuu(n, r));
+  }
+  /* sum_{n = 1}^{oo} (-1)^(n-1) x^n / (n! n^r) */
+}
+
+/* t_REAL, assume r >= 2. m t_INT or NULL; Returns a t_REAL */
+static GEN
+compute_Gr_Sx(struct ellld *el, GEN m, ulong sm)
+{
+  pari_sp av = avma;
+  const long thresh_SMALL = 5;
+  long i, r = el->r;
+  GEN x = m? mulir(m, el->X): mulur(sm, el->X);
+  GEN logx = mplog(x), p4;
+  /* i = 0 */
+  GEN p3 = gel(el->alpha, r+1);
+  GEN p2 = logx;
+  for (i = 1; i < r; i++)
+  { /* p2 = (logx)^i / i! */
+    p3 = addrr(p3, mulrr(gel(el->alpha, r-i+1), p2));
+    p2 = divru(mulrr(p2, logx), i+1);
+  }
+  /* i = r, use alpha[1] = 1 */
+  p3 = addrr(p3, p2);
+
+  if (cmprs(x, thresh_SMALL) < 0)
+    p4 = compute_Gr_VSx(el, x); /* x "small" use expansion near 0 */
+  else
+  { /* x "large" use expansion at infinity */
+    pari_sp av = avma, lim = stack_lim(av, 2);
+    long M = lg(el->A);
+    GEN xi = sqrr(x); /* x^2 */
+    p4 = x; /* i = 1. Uses A[1] = 1; NB: M > 1 */
+    for (i = 2; i < M; i++)
+    {
+      GEN p5 = mulrr(xi, gel(el->A, i));
+      if (expo(p5) < -el->epsbit) break;
+      p4 = addrr(p4, p5);
+      xi = mulrr(xi, x); /* = x^i */
+      if (low_stack(lim, stack_lim(av, 2)))
+      {
+        if (DEBUGMEM > 0) pari_warn(warnmem, "compute_Gr_Sx");
+        gerepileall(av, 2, &xi, &p4);
+      }
+    }
+    p4 = mulrr(p4, m? powgi(el->emX, m): powru(el->emX, sm));
+  }
+  return gerepileuptoleaf(av, odd(r)? subrr(p4, p3): subrr(p3, p4));
+}
+
+/* return G_r(X), cache values G(n*X), n < rootbnd.
+ * If r = 0, G(x) = exp(-x), cache Baby/Giant struct in el->BG
+ * If r >= 2, precompute the expansions at 0 and oo of G */
+static GEN
+init_G(struct ellld *el, long prec)
+{
+  if (el->r == 0)
+  {
+    ulong bnd = el->rootbnd+1;
+    el->BG->bgbnd = bnd;
+    el->BG->baby  = powruvec(el->emX, bnd);
+    el->BG->giant = powruvec(gel(el->BG->baby,bnd), bnd);
+    return gel(el->BG->baby, 1);
+  }
+  if (el->r == 1)
+    el->gcache = mpveceint1(el->X, el->eX, el->rootbnd);
+  else
+  {
+    long m, j, l = el->rootbnd;
+    GEN G;
+    m = number_of_terms_Sx(mulri(el->X, el->bnd), el->epsbit);
+    el->alpha = init_alpha(el->r, prec);
+    el->A = init_A(el->r, m, prec);
+    G = cgetg(l+1, t_VEC);
+    for (j = 1; j <= l; j++) gel(G,j) = compute_Gr_Sx(el, NULL, j);
+    el->gcache = G;
+  }
+  return gel(el->gcache, 1);
+}
+
+/* r >= 2; sum += G(n*X) * a_n / n */
+static void
+ellld_L1(void *E, GEN *psum, GEN n, GEN a, long j)
+{
+  struct ellld *el = (struct ellld *)E;
+  GEN G;
+  (void)j;
+  if (cmpiu(n, el->rootbnd) <= 0)
+    G = gel(el->gcache, itou(n));
+  else
+    G = compute_Gr_Sx(el, n, 0);
+  *psum = addrr(*psum, divri(mulir(a, G), n));
+}
+/* r = 1; sum += G(n*X) * a_n / n, where G = eint1.
+ * If j < 0, cache values G(n*a*X), 1 <= a <= |j| in gjcache
+ * If j > 0, assume n = N*j and retrieve G(N*j*X), from gjcache */
+static void
+ellld_L1r1(void *E, GEN *psum, GEN n, GEN a, long j)
+{
+  struct ellld *el = (struct ellld *)E;
+  GEN G;
+  if (j==0)
+  {
+    if (cmpiu(n, el->rootbnd) <= 0)
+      G = gel(el->gcache, itou(n));
+    else
+      G = mpeint1(mulir(n,el->X), powgi(el->eX,n));
+  }
+  else if (j < 0)
+  {
+    el->gjcache = mpveceint1(mulir(n,el->X), powgi(el->eX,n), -j);
+    G = gel(el->gjcache, 1);
+  }
+  else
+    G = gel(el->gjcache, j);
+  *psum = addrr(*psum, divri(mulir(a, G), n));
+}
+
+/* assume n / h->bgbnd fits in an ulong */
+static void
+get_baby_giant(struct babygiant *h, GEN n, GEN *b, GEN *g)
+{
+  ulong r, q = udiviu_rem(n, h->bgbnd, &r);
+  *b = r? gel(h->baby,r): NULL;
+  *g = q? gel(h->giant,q): NULL;
+}
+static void
+ellld_L1r0(void *E, GEN *psum, GEN n, GEN a, long j)
+{
+  GEN b, g, G;
+  get_baby_giant(((struct ellld*)E)->BG, n, &b, &g);
+  (void)j;
+  if (!b)      G = g;
+  else if (!g) G = b;
+  else         G = mulrr(b,g);
+  *psum = addrr(*psum, divri(mulir(a, G), n));
+}
+static void
+heegner_L1(void*E, GEN *psum, GEN n, GEN a, long jmax)
+{
+  long j, l = lg(*psum);
+  GEN b, g, sum = cgetg(l, t_VEC);
+  get_baby_giant((struct babygiant*)E, n, &b, &g);
+  (void)jmax;
+  for (j = 1; j < l; j++)
+  {
+    GEN G;
+    if (!b)      G = real_i(gel(g,j));
+    else if (!g) G = real_i(gel(b,j));
+    else         G = mulreal(gel(b,j), gel(g,j));
+    gel(sum, j) = addrr(gel(*psum,j), divri(mulir(a, G), n));
+  }
+  *psum = sum;
+}
+
+/* Basic data independent from r (E, N, X, eX, emX) already filled,
+ * Returns a t_REAL */
+static GEN
+ellL1_i(struct ellld *el, struct bg_data *bg, long r, GEN ap, long prec)
+{
+  GEN sum;
+  if (DEBUGLEVEL) err_printf("in ellL1 with r = %ld, prec = %ld\n", r, prec);
+  el->r = r;
+  el->bnd = cutoff_point(r, el->X, el->emX, el->epsbit, prec);
+  gen_BG_init(bg,el->E,el->N,el->bnd,ap);
+  el->rootbnd = bg->rootbnd;
+  sum = init_G(el, prec);
+  if (DEBUGLEVEL>=3) err_printf("el_bnd = %Ps, N=%Ps\n", el->bnd, el->N);
+  sum = gen_BG_rec(el, r>=2? ellld_L1: (r==1? ellld_L1r1: ellld_L1r0), bg, sum);
+  return mulri(shiftr(sum, 1), mpfact(r));
+}
+
+static void
+init_el(struct ellld *el, GEN E, long *parity, long bitprec)
+{
+  GEN eX;
+  long prec;
+
+  el->E = E = ellanal_globalred(E, NULL);
+  el->N = ellQ_get_N(E);
+  prec = nbits2prec(bitprec+(expi(el->N)>>1));
+  el->X = divrr(Pi2n(1, prec), sqrtr(itor(el->N, prec))); /* << 1 */
+  eX = mpexp(el->X);
+  if (realprec(eX) > prec) eX = rtor(eX, prec); /* avoid spurious accuracy increase */
+  el->eX = eX;
+  el->emX = invr(el->eX);
+  el->epsbit = bitprec+1;
+  *parity = (ellrootno_global(E) > 0)? 0: 1; /* rank parity */
+}
+
+static GEN
+ellL1_bitprec(GEN E, long r, long bitprec)
+{
+  pari_sp av = avma;
+  struct ellld el;
+  struct bg_data bg;
+  long parity;
+  long prec = nbits2prec(bitprec)+1;
+  if (r<0) pari_err_DOMAIN("ellL1","derivative order","<",gen_0,stoi(r));
+  init_el(&el, E, &parity, bitprec);
+  if (parity != (r & 1)) return gen_0;
+  return gerepileuptoleaf(av, ellL1_i(&el, &bg, r, NULL, prec));
+}
+
+GEN
+ellL1(GEN E, long r, long prec) { return ellL1_bitprec(E, r, prec2nbits(prec)); }
+
+GEN
+ellanalyticrank(GEN e, GEN eps, long prec)
+{
+  struct ellld el;
+  struct bg_data bg;
+  long rk;
+  pari_sp av = avma, av2;
+  GEN ap = NULL;
+  pari_timer T;
+
+  if (!eps)
+    eps = real2n(-prec2nbits(prec)/2+1, DEFAULTPREC);
+  else
+    if (typ(eps) != t_REAL) {
+      eps = gtofp(eps, DEFAULTPREC);
+      if (typ(eps) != t_REAL) pari_err_TYPE("ellanalyticrank", eps);
+    }
+  init_el(&el, e, &rk, prec2nbits(prec)); /* set rk to rank parity (0 or 1) */
+  if (DEBUGLEVEL) {
+    err_printf("ellanalyticrank: rank is %s, eps=%Ps\n", rk? "odd": "even",eps);
+    timer_start(&T);
+  }
+  av2 = avma;
+  for(;; rk += 2)
+  {
+    GEN Lr1 = ellL1_i(&el, &bg, rk, ap, prec);
+    if (DEBUGLEVEL) timer_printf(&T, "L^(%ld)=%Ps", rk, Lr1);
+    if (absr_cmp(Lr1, eps) > 0) return gerepilecopy(av, mkvec2(stoi(rk), Lr1));
+    ap = gerepilecopy(av2, bg.ap);
+  }
+}
+
+/* This file is a C version by Bill Allombert of a GP script by
+   Christophe Delaunay which was based on a GP script by John Cremona.
+   Reference: Henri Cohen's book GTM 239.
+*/
+
+/* Return C, C[i][j] = Q[j]^i, i = 1..nb */
+static GEN
+fillstep(GEN Q, long nb)
+{
+  long i, k, l = lg(Q);
+  GEN C = cgetg(nb+1,t_VEC);
+  gel(C,1) = Q;
+  for (i = 2; i<=nb; ++i)
+  {
+    gel(C,i) = cgetg(l, t_VEC);
+    for (k = 1; k<l; ++k) gmael(C,i,k) = gmul(gel(Q,k),gmael(C,i-1,k));
+  }
+  return C;
+}
+
+/* ymin a t_REAL */
+static GEN
+heegner_psi(GEN E, GEN N, GEN ymin, GEN points, long bitprec)
+{
+  pari_sp av = avma;
+  struct babygiant BG[1];
+  struct bg_data bg;
+  ulong bnd;
+  long k, np = lg(points), prec = nbits2prec(bitprec)+1;
+  GEN sum, Q, pi2 = Pi2n(1, prec);
+  GEN B = ceilr(divrr(mulur(bitprec,mplog2(DEFAULTPREC)), mulrr(pi2, ymin)));
+  gen_BG_init(&bg,E,N,B,NULL);
+  bnd = bg.rootbnd + 1;
+  BG->bgbnd = bnd;
+  Q = cgetg(np, t_VEC);
+  for (k = 1; k<np; ++k) gel(Q, k) = expIxy(pi2, gel(points, k), prec);
+  BG->baby  = fillstep(Q, bnd);
+  BG->giant = fillstep(gel(BG->baby, bnd), bnd);
+  sum = gen_BG_rec((void*)BG, heegner_L1, &bg, real_i(Q));
+  return gerepileupto(av, sum);
+}
+
+/*Returns lambda_bad list for one prime p, nv = localred(E, p) */
+static GEN
+lambda1(GEN E, GEN nv, GEN p, long prec)
+{
+  pari_sp av;
+  GEN res, lp;
+  long kod = itos(gel(nv, 2));
+  if (kod==2 || kod ==-2) return cgetg(1,t_VEC);
+  av = avma; lp = glog(p, prec);
+  if (kod > 4)
+  {
+    long n = Z_pval(ell_get_disc(E), p);
+    long j, m = kod - 4, nl = 1 + (m >> 1L);
+    res = cgetg(nl, t_VEC);
+    for (j = 1; j < nl; j++)
+      gel(res, j) = gmul(lp, gsubgs(gdivgs(sqru(j), n), j)); /* j^2/n - j */
+  }
+  else if (kod < -4)
+    res = mkvec2(negr(lp), divrs(mulrs(lp, kod), 4));
+  else
+  {
+    const long lam[] = {8,9,0,6,0,0,0,3,4};
+    long m = -lam[kod+4];
+    res = mkvec(divrs(mulrs(lp, m), 6));
+  }
+  return gerepilecopy(av, res);
+}
+
+static GEN
+lambdalist(GEN E, long prec)
+{
+  pari_sp ltop = avma;
+  GEN glob = ellglobalred(E), plist = gmael(glob,4,1), L = gel(glob,5);
+  GEN res, v, D = ell_get_disc(E);
+  long i, j, k, l, m, n, np = lg(plist), lr = 1;
+  v = cgetg(np, t_VEC);
+  for (j = 1, i = 1 ; j < np; ++j)
+  {
+    GEN p = gel(plist, j);
+    if (dvdii(D, sqri(p)))
+    {
+      GEN la = lambda1(E, gel(L,j), p, prec);
+      gel(v, i++) = la;
+      lr *= lg(la);
+    }
+  }
+  np = i;
+  res = cgetg(lr+1, t_VEC);
+  gel(res, 1) = gen_0; n = 1; m = 1;
+  for (j = 1; j < np; ++j)
+  {
+    GEN w = gel(v, j);
+    long lw = lg(w);
+    for (k = 1; k <= n; k++)
+    {
+      GEN t = gel(res, k);
+      for (l = 1, m = n; l < lw; l++, m+=n)
+        gel(res, k + m) = mpadd(t, gel(w, l));
+    }
+    n = m;
+  }
+  return gerepilecopy(ltop, res);
+}
+
+/* P a t_INT or t_FRAC, return its logarithmic height */
+static GEN
+heightQ(GEN P, long prec)
+{
+  long s;
+  if (typ(P) == t_FRAC)
+  {
+    GEN a = gel(P,1), b = gel(P,2);
+    P = absi_cmp(a,b) > 0 ? a: b;
+  }
+  s = signe(P);
+  if (!s) return real_0(prec);
+  if (s < 0) P = absi(P);
+  return glog(P, prec);
+}
+
+/* t a t_INT or t_FRAC, returns max(1, log |t|), returns a t_REAL */
+static GEN
+logplusQ(GEN t, long prec)
+{
+  if (typ(t) == t_INT)
+  {
+    if (!signe(t)) return real_1(prec);
+    if (signe(t) < 0) t = absi(t);
+  }
+  else
+  {
+    GEN a = gel(t,1), b = gel(t,2);
+    if (absi_cmp(a, b) < 0) return real_1(prec);
+    if (signe(a) < 0) t = gneg(t);
+  }
+  return glog(t, prec);
+}
+
+/* See GTM239, p532, Th 8.1.18
+ * Return M such that h_naive <= M */
+static GEN
+hnaive_max(GEN ell, GEN ht)
+{
+  const long prec = LOWDEFAULTPREC; /* minimal accuracy */
+  GEN b2     = ell_get_b2(ell), j = ell_get_j(ell);
+  GEN logd   = glog(absi(ell_get_disc(ell)), prec);
+  GEN logj   = logplusQ(j, prec);
+  GEN hj     = heightQ(j, prec);
+  GEN logb2p = signe(b2)? addrr(logplusQ(gdivgs(b2, 12),prec), mplog2(prec))
+                        : real_1(prec);
+  GEN mu     = addrr(divrs(addrr(logd, logj),6), logb2p);
+  return addrs(addrr(addrr(ht, divrs(hj,12)), mu), 2);
+}
+
+static GEN
+qfb_root(GEN Q, GEN vDi)
+{
+  GEN a2 = shifti(gel(Q, 1),1), b = gel(Q, 2);
+  return mkcomplex(gdiv(negi(b),a2),divri(vDi,a2));
+}
+
+static GEN
+qimag2(GEN Q)
+{
+  pari_sp av = avma;
+  GEN z = gdiv(negi(qfb_disc(Q)), shifti(sqri(gel(Q, 1)),2));
+  return gerepileupto(av, z);
+}
+
+/***************************************************/
+/*Routines for increasing the imaginary parts using*/
+/*Atkin-Lehner operators                           */
+/***************************************************/
+
+static GEN
+qfb_mult(GEN Q, GEN M)
+{
+  GEN A = gel(Q, 1) , B = gel(Q, 2) , C = gel(Q, 3);
+  GEN a = gcoeff(M, 1, 1), b = gcoeff(M, 1, 2);
+  GEN c = gcoeff(M, 2, 1), d = gcoeff(M, 2, 2);
+  GEN W1 = addii(addii(mulii(sqri(a), A), mulii(mulii(c, a), B)), mulii(sqri(c), C));
+  GEN W2 = addii(addii(mulii(mulii(shifti(b,1), a), A),
+                       mulii(addii(mulii(d, a), mulii(c, b)), B)),
+                 mulii(mulii(shifti(d,1), c), C));
+  GEN W3 = addii(addii(mulii(sqri(b), A), mulii(mulii(d, b), B)), mulii(sqri(d), C));
+  GEN D = gcdii(W1, gcdii(W2, W3));
+  if (!equali1(D)) {
+    W1 = diviiexact(W1,D);
+    W2 = diviiexact(W2,D);
+    W3 = diviiexact(W3,D);
+  }
+  return qfi(W1, W2, W3);
+}
+
+#ifdef DEBUG
+static void
+best_point_old(GEN Q, GEN NQ, GEN f, GEN *u, GEN *v)
+{
+  long n, k;
+  GEN U, c, d;
+  GEN A = gel(f, 1);
+  GEN B = gel(f, 2);
+  GEN C = gel(f, 3);
+  GEN q = qfi(mulii(NQ, C), negi(B), diviiexact(A, NQ));
+  redimagsl2(q, &U);
+  *u = c = gcoeff(U, 1, 1);
+  *v = d = gcoeff(U, 2, 1);
+  if (equali1(gcdii(mulii(*u, NQ), mulii(*v, Q))))
+    return;
+  for (n = 1; ; n++)
+  {
+    for (k = -n; k<=n; k++)
+    {
+      *u = addis(c, k); *v = addis(d, n);
+      if (equali1(ggcd(mulii(*u, NQ), mulii(*v, Q))))
+        return;
+      *v= subis(d, n);
+      if (equali1(ggcd(mulii(*u, NQ), mulii(*v, Q))))
+        return;
+      *u = addis(c, n); *v = addis(d, k);
+      if (equali1(ggcd(mulii(*u, NQ), mulii(*v, Q))))
+        return;
+      *u = subis(c, n);
+      if (equali1(ggcd(mulii(*u, NQ), mulii(*v, Q))))
+        return;
+    }
+  }
+}
+/* q(x,y) = ax^2 + bxy + cy^2 */
+static GEN
+qfb_eval(GEN q, GEN x, GEN y)
+{
+  GEN a = gel(q,1), b = gel(q,2), c = gel(q,3);
+  GEN x2 = sqri(x), y2 = sqri(y), xy = mulii(x,y);
+  return addii(addii(mulii(a, x2), mulii(b,xy)), mulii(c, y2));
+}
+#endif
+
+static long
+nexti(long i) { return i>0 ? -i : 1-i; }
+
+/* q0 + i q1 + i^2 q2 */
+static GEN
+qfmin_eval(GEN q0, GEN q1, GEN q2, long i)
+{ return addii(mulis(addii(mulis(q2, i), q1), i), q0); }
+
+/* assume a > 0, return gcd(a,b,c) */
+static ulong
+gcduii(ulong a, GEN b, GEN c)
+{
+  ulong d = a;
+  d = ugcd(umodiu(b, d), d );
+  if (d == 1) return 1;
+  d = ugcd(umodiu(c, d), d );
+  return d;
+}
+
+static void
+best_point(GEN Q, GEN NQ, GEN f, GEN *pu, GEN *pv)
+{
+  GEN a = mulii(NQ, gel(f,3)), b = negi(gel(f,2)), c = diviiexact(gel(f,1), NQ);
+  GEN D = absi( qfb_disc(f) );
+  GEN U, qr = redimagsl2(qfi(a,b,c), &U);
+  GEN A = gel(qr,1), B = gel(qr,2), A2 = shifti(A,1), AA4 = sqri(A2);
+  GEN V, best;
+  long y;
+
+  /* 4A qr(x,y) = (2A x + By)^2 + D y^2
+   * Write x = x0(y) + i, where x0 is an integer minimum
+   * (the smallest in case of tie) of x-> qr(x,y), for given y.
+   * 4A qr(x,y) = ((2A x0 + By)^2 + Dy^2) + 4A i (2A x0 + By) + 4A^2 i^2
+   *            = q0(y) + q1(y) i + q2 i^2
+   * Loop through (x,y), y>0 by (roughly) increasing values of qr(x,y) */
+
+  /* We must test whether [X,Y]~ := U * [x,y]~ satisfy (X NQ, Y Q) = 1
+   * This is equivalent to (X,Y) = 1 (note that (X,Y) = (x,y)), and
+   * (X, Q) = (Y, NQ) = 1.
+   * We have U * [x0+i, y]~ = U * [x0,y]~ + i U[,1] =: V0 + i U[,1] */
+
+  /* try [1,0]~ = first minimum */
+  V = gel(U,1); /* U *[1,0]~ */
+  *pu = gel(V,1);
+  *pv = gel(V,2);
+  if (is_pm1(gcdii(*pu, Q)) && is_pm1(gcdii(*pv, NQ))) return;
+
+  /* try [0,1]~ = second minimum */
+  V = gel(U,2); /* U *[0,1]~ */
+  *pu = gel(V,1);
+  *pv = gel(V,2);
+  if (is_pm1(gcdii(*pu, Q)) && is_pm1(gcdii(*pv, NQ))) return;
+
+  /* (X,Y) = (1, \pm1) always works. Try to do better now */
+  best = subii(addii(a, c), absi(b));
+  *pu = gen_1;
+  *pv = signe(b) < 0? gen_1: gen_m1;
+
+  for (y = 1;; y++)
+  {
+    GEN Dy2, r, By, x0, q0, q1, V0;
+    long i;
+    if (y > 1)
+    {
+      if (gcduii(y, gcoeff(U,1,1),  Q) != 1) continue;
+      if (gcduii(y, gcoeff(U,2,1), NQ) != 1) continue;
+    }
+    Dy2 = mulii(D, sqru(y));
+    if (cmpii(Dy2, best) >= 0) break; /* we won't improve. STOP */
+    By = muliu(B,y), x0 = truedvmdii(negi(By), A2, &r);
+    if (cmpii(r, A) >= 0) { x0 = subis(x0,1); r = subii(r, A2); }
+    /* (2A x + By)^2 + Dy^2, minimal at x = x0. Assume A2 > 0 */
+    /* r = 2A x0 + By */
+    q0 = addii(sqri(r), Dy2); /* minimal value for this y, at x0 */
+    if (cmpii(q0, best) >= 0) continue; /* we won't improve for this y */
+    q1 = shifti(mulii(A2, r), 1);
+
+    V0 = ZM_ZC_mul(U, mkcol2(x0, utoipos(y)));
+    for (i = 0;; i = nexti(i))
+    {
+      pari_sp av2 = avma;
+      GEN x, N = qfmin_eval(q0, q1, AA4, i);
+      if (cmpii(N , best) >= 0) break;
+      x = addis(x0, i);
+      if (ugcd(umodiu(x, y), y) == 1)
+      {
+        GEN u, v;
+        V = ZC_add(V0, ZC_z_mul(gel(U,1), i)); /* [X, Y] */
+        u = gel(V,1);
+        v = gel(V,2);
+        if (is_pm1(gcdii(u, Q)) && is_pm1(gcdii(v, NQ)))
+        {
+          *pu = u;
+          *pv = v;
+          best = N; break;
+        }
+      }
+      avma = av2;
+    }
+  }
+#ifdef DEBUG
+  {
+    GEN oldu, oldv, F = qfi(a,b,c);
+    best_point_old(Q, NQ, f, &oldu, &oldv);
+    if (!equalii(oldu, *pu) || !equalii(oldv, *pv))
+    {
+      if (!equali1(gcdii(mulii(*pu, NQ), mulii(*pv, Q))))
+        pari_err_BUG("best_point (gcd)");
+      if (cmpii(qfb_eval(F, *pu,*pv), qfb_eval(F, oldu, oldv)) > 0)
+      {
+        pari_warn(warner, "%Ps,%Ps,%Ps, %Ps > %Ps",
+                          Q,NQ,f, mkvec2(*pu,*pv), mkvec2(oldu,oldv));
+        pari_err_BUG("best_point (too large)");
+      }
+    }
+  }
+#endif
+}
+
+static GEN
+best_lift(GEN N, GEN Q, GEN NQ, GEN f)
+{
+  GEN a,b,c,d,M;
+  best_point(Q, NQ, f, &c, &d);
+  (void)bezout(mulii(d, Q), mulii(NQ, c), &a, &b);
+  M = mkmat2( mkcol2(mulii(d, Q), mulii(negi(N), c)),
+              mkcol2(b, mulii(a, Q)));
+  return qfb_mult(f, M);
+}
+
+static GEN
+lift_points(GEN N, GEN listQ, GEN f, GEN *pt, GEN *pQ)
+{
+  pari_sp av = avma;
+  GEN yf = gen_0, tf = NULL, Qf = NULL;
+  long k, l = lg(listQ);
+  for (k = 1; k < l; ++k)
+  {
+    GEN c = gel(listQ, k), Q = gel(c,1), NQ = gel(c,2);
+    GEN t = best_lift(N, Q, NQ, f), y = qimag2(t);
+    if (gcmp(y, yf) > 0) { yf = y; Qf = Q; tf = t; }
+  }
+  gerepileall(av, 3, &tf, &Qf, &yf);
+  *pt = tf; *pQ = Qf; return yf;
+}
+
+/***************************/
+/*         Twists          */
+/***************************/
+
+static GEN
+twistcurve(GEN e, GEN D)
+{
+  GEN D2 = sqri(D);
+  GEN a4 = mulii(mulsi(-27, D2), ell_get_c4(e));
+  GEN a6 = mulii(mulsi(-54, mulii(D, D2)), ell_get_c6(e));
+  return ellinit(mkvec2(a4,a6),NULL,0);
+}
+
+static GEN
+ltwist1(GEN E, GEN d, long bitprec)
+{
+  pari_sp av = avma;
+  GEN Ed = twistcurve(E, d);
+  GEN z = ellL1_bitprec(Ed, 0, bitprec);
+  obj_free(Ed); return gerepileuptoleaf(av, z);
+}
+
+/* Return O_re*c(E)/(4*O_vol*|E_t|^2) */
+
+static GEN
+heegner_indexmult(GEN om, long t, GEN tam, long prec)
+{
+  pari_sp av = avma;
+  GEN Ovr = gabs(gimag(gel(om, 2)), prec); /* O_vol/O_re, t_REAL */
+  return gerepileupto(av, divrs(divir(tam, Ovr), 4*t*t));
+}
+
+
+/* omega(gcd(D, N)), given faN = factor(N) */
+static long
+omega_N_D(GEN faN, ulong D)
+{
+  GEN P = gel(faN, 1);
+  long i, l = lg(P), w = 0;
+  for (i = 1; i < l; i++)
+    if (dvdui(D, gel(P,i))) w++;
+  return w;
+}
+
+static GEN
+heegner_indexmultD(GEN faN, GEN a, long D, GEN sqrtD)
+{
+  pari_sp av = avma;
+  GEN c;
+  long w;
+  switch(D)
+  {
+    case -3: w = 9; break;
+    case -4: w = 4; break;
+    default: w = 1;
+  }
+  c = shifti(stoi(w), omega_N_D(faN,-D)); /* (w(D)/2)^2 * 2^omega(gcd(D,N)) */
+  return gerepileupto(av, mulri(mulrr(a, sqrtD), c));
+}
+
+static GEN
+heegner_try_point(GEN E, GEN lambdas, GEN ht, GEN z, long prec)
+{
+  long l = lg(lambdas);
+  long i, eps;
+  GEN P = greal(pointell(E, z, prec)), x = gel(P,1);
+  GEN rh = subrr(ht, shiftr(ellheightoo(E, P, prec),1));
+  for (i = 1; i < l; ++i)
+  {
+    GEN logd = shiftr(gsub(rh, gel(lambdas, i)), -1);
+    GEN d, approxd = gexp(logd, prec);
+    if (DEBUGLEVEL > 1)
+      err_printf("Trying lambda number %ld, logd=%Ps, approxd=%Ps\n", i, logd, approxd);
+    d = grndtoi(approxd, &eps);
+    if (signe(d) > 0 && eps<-10)
+    {
+      GEN X, ylist, d2 = sqri(d), approxn = mulir(d2, x);
+      if (DEBUGLEVEL > 1) err_printf("approxn=%Ps  eps=%ld\n", approxn,eps);
+      X = gdiv(ground(approxn), d2);
+      ylist = ellordinate(E, X, prec);
+      if (lg(ylist) > 1)
+      {
+        GEN P = mkvec2(X, gel(ylist, 1));
+        GEN hp = ghell(E,P,prec);
+        if (cmprr(hp, shiftr(ht,1)) < 0 && cmprr(hp, shiftr(ht,-1)) > 0)
+          return P;
+        if (DEBUGLEVEL > 0)
+          err_printf("found non-Heegner point %Ps\n", P);
+      }
+    }
+  }
+  return NULL;
+}
+
+static GEN
+heegner_find_point(GEN e, GEN om, GEN ht, GEN z1, long k, long prec)
+{
+  GEN lambdas = lambdalist(e, prec);
+  pari_sp av = avma;
+  long m;
+  GEN Ore = gel(om, 1), Oim = gel(om, 2);
+  for (m = 0; m < k; m++)
+  {
+    GEN P, z2 = divrs(addrr(z1, mulsr(m, Ore)), k);
+    if (DEBUGLEVEL > 1)
+      err_printf("Trying multiplier %ld\n",m);
+    P = heegner_try_point(e, lambdas, ht, z2, prec);
+    if (P) return P;
+    if (signe(ell_get_disc(e)) > 0)
+    {
+      z2 = gadd(z2, gmul2n(Oim, -1));
+      P = heegner_try_point(e, lambdas, ht, z2, prec);
+      if (P) return P;
+    }
+    avma = av;
+  }
+  pari_err_BUG("ellheegner, point not found");
+  return NULL; /* NOT REACHED */
+}
+
+/* N > 1, fa = factor(N), return factor(4*N) */
+static GEN
+fa_shift2(GEN fa)
+{
+  GEN P = gel(fa,1), E = gel(fa,2);
+  if (equaliu(gcoeff(fa,1,1), 2))
+  {
+    E = shallowcopy(E);
+    gel(E,1) = addis(gel(E,1), 2);
+  }
+  else
+  {
+    P = shallowconcat(gen_2, P);
+    E = shallowconcat(gen_2, E);
+  }
+  return mkmat2(P, E);
+}
+
+/* P = prime divisors of N(E). Return the product of primes p in P, a_p != -1
+ * HACK: restrict to small primes since large ones won't divide our C-long
+ * discriminants */
+static GEN
+get_bad(GEN E, GEN P)
+{
+  long k, l = lg(P), ibad = 1;
+  GEN B = cgetg(l, t_VECSMALL);
+  for (k = 1; k < l; k++)
+  {
+    GEN p = gel(P,k);
+    long pp = itos_or_0(p);
+    if (!pp) break;
+    if (! equalim1(ellap(E,p))) B[ibad++] = pp;
+  }
+  setlg(B, ibad); return ibad == 1? NULL: zv_prod_Z(B);
+}
+
+/* list of pairs [Q,N/Q], where Q | N and gcd(Q,N/Q) = 1 */
+static GEN
+find_div(GEN N, GEN faN)
+{
+  GEN listQ = divisors(faN);
+  long j, k, l = lg(listQ);
+
+  gel(listQ, 1) = mkvec2(gen_1, N);
+  for (j = k = 2; k < l; ++k)
+  {
+    GEN Q = gel(listQ, k), NQ = diviiexact(N, Q);
+    if (is_pm1(gcdii(Q,NQ))) gel(listQ, j++) = mkvec2(Q,NQ);
+  }
+  setlg(listQ, j); return listQ;
+}
+
+static long
+testDisc(GEN bad, long d)
+{ return !bad || ugcd(umodiu(bad, -d), -d) == 1; }
+/* bad = product of bad primes. Return the NDISC largest fundamental
+ * discriminants D < d such that (D,bad) = 1 and d is a square mod 4N */
+static GEN
+listDisc(GEN fa4N, GEN bad, long d)
+{
+  const long NDISC = 10;
+  GEN v = cgetg(NDISC+1, t_VECSMALL);
+  pari_sp av = avma;
+  long j = 1;
+  for(;;)
+  {
+    d -= odd(d)? 1: 3;
+    if (testDisc(bad,d) && unegisfundamental(-d) && Zn_issquare(stoi(d), fa4N))
+    {
+      v[j++] = d;
+      if (j > NDISC) break;
+    }
+    avma = av;
+  }
+  avma = av; return v;
+}
+/* L = vector of [q1,q2] or [q1,q2,q2']
+ * cd = (b^2 - D)/(4N) */
+static void
+listfill(GEN N, GEN b, GEN c, GEN d, GEN L, long *s)
+{
+  long k, l = lg(L);
+  GEN add, frm2, a = mulii(d, N), V = mkqfi(a,b,c), frm = redimag(V);
+  for (k = 1; k < l; ++k)
+  { /* Lk = [v,frm] or [v,frm,frm2] */
+    GEN Lk = gel(L,k);
+    long i;
+    for (i = 2; i < lg(Lk); i++) /* 1 or 2 elements */
+      if (gequal(frm, gel(Lk,i)))
+      {
+        GEN v = gel(Lk, 1);
+        if (cmpii(a, gel(v,1)) < 0) gel(Lk,1) = V;
+        return;
+      }
+  }
+  frm2 = redimag( mkqfi(d, negi(b), mulii(c,N)) );
+  add = gequal(frm, frm2)? mkvec2(V,frm): mkvec3(V,frm,frm2);
+  vectrunc_append(L, add);
+  *s += lg(add) - 2;
+}
+/* faN4 = factor(4*N) */
+static GEN
+listheegner(GEN N, GEN faN4, GEN listQ, GEN D)
+{
+  const long kmin = 30;
+  long h = itos(gel(quadclassunit0(D, 0, NULL, DEFAULTPREC), 1));
+  GEN ymin, b = Zn_sqrt(D, faN4), L = vectrunc_init(h+1);
+  long l, k, s = 0;
+  for (k = 0; k < kmin || s < h; k++)
+  {
+    GEN bk = addii(b, mulsi(2*k, N));
+    GEN C = diviiexact(shifti(subii(sqri(bk), D), -2), N);
+    GEN div = divisors(C);
+    long i, l = lg(div);
+    for (i = 1; i < l; i++)
+    {
+      GEN d = gel(div, i), c = gel(div, l-i); /* cd = C */
+      listfill(N, bk, c, d, L, &s);
+    }
+  }
+  l = lg(L); ymin = NULL;
+  for (k = 1; k < l; k++)
+  {
+    GEN t, Q, Lk = gel(L,k), f = gel(Lk,1);
+    GEN y = lift_points(N, listQ, f, &t, &Q);
+    gel(L, k) = mkvec3(t, stoi(lg(Lk) - 2), Q);
+    if (!ymin || gcmp(y, ymin) < 0) ymin = y;
+  }
+  return mkvec3(ymin, L, D);
+}
+
+/* Q | N, P = prime divisors of N, R[i] = local epsilon-factor at P[i].
+ * Return \prod_{p | Q} R[i] */
+static long
+rootno(GEN Q, GEN P, GEN R)
+{
+  long s = 1, i, l = lg(P);
+  for (i = 1; i < l; i++)
+    if (dvdii(Q, gel(P,i))) s *= R[i];
+  return s;
+}
+
+static void
+heegner_find_disc(GEN *ymin, GEN *points, GEN *coefs, long *pind, GEN E,
+                  GEN indmult, long prec)
+{
+  long d = 0;
+  GEN faN4, bad, N, faN, listQ, listR;
+
+  ellQ_get_Nfa(E, &N, &faN);
+  faN4 = fa_shift2(faN);
+  listQ = find_div(N, faN);
+  bad = get_bad(E, gel(faN, 1));
+  listR = gel(obj_check(E, Q_ROOTNO), 2);
+  for(;;)
+  {
+    pari_sp av = avma;
+    GEN list, listD = listDisc(faN4, bad, d);
+    long k, l = lg(listD);
+    if (DEBUGLEVEL) err_printf("List of discriminants...%Ps\n", listD);
+    list = cgetg(l, t_VEC);
+    for (k = 1; k < l; ++k)
+      gel(list, k) = listheegner(N, faN4, listQ, stoi(listD[k]));
+    list = vecsort0(list, gen_1, 0);
+    for (k = l-1; k > 0; --k)
+    {
+      long bprec = 8;
+      GEN Lk = gel(list,k), D = gel(Lk,3);
+      GEN sqrtD = sqrtr_abs(itor(D, prec)); /* sqrt(|D|) */
+      GEN indmultD = heegner_indexmultD(faN, indmult, itos(D), sqrtD);
+      do
+      {
+        GEN mulf = ltwist1(E, D, bprec+expo(indmultD));
+        GEN indr = mulrr(indmultD, mulf);
+        if (DEBUGLEVEL>=1) err_printf("Disc = %Ps, Index^2 = %Ps\n", D, indr);
+        if (signe(indr)>0 && expo(indr) >= -1) /* indr >=.5 */
+        {
+          long e, i, l;
+          GEN pts, cfs, L, indi = grndtoi(sqrtr_abs(indr), &e);
+          if (e > expi(indi)-7)
+          {
+            bprec++;
+            pari_warn(warnprec, "ellL1",bprec);
+            continue;
+          }
+          *pind = itos(indi);
+          *ymin = gsqrt(gel(Lk, 1), prec);
+          L = gel(Lk, 2); l = lg(L);
+          pts = cgetg(l, t_VEC);
+          cfs = cgetg(l, t_VECSMALL);
+          for (i = 1; i < l; ++i)
+          {
+            GEN P = gel(L,i), z = gel(P,2), Q = gel(P,3); /* [1 or 2, Q] */
+            long c;
+            gel(pts, i) = qfb_root(gel(P,1), sqrtD);
+            c = rootno(Q, gel(faN,1), listR);
+            if (!equali1(z)) c *= 2;
+            cfs[i] = c;
+          }
+          *coefs = cfs; *points = pts; return;
+        }
+      } while(0);
+    }
+    d = listD[l-1]; avma = av;
+  }
+}
+
+static GEN
+ell_apply_globalred_all(GEN e, GEN *N, GEN *cb, GEN *tam)
+{
+  GEN E = ellanal_globalred(e, cb), red = obj_check(E, Q_GLOBALRED);
+  *N = gel(red, 1);
+  *tam = gel(red,2);
+  if (signe(ell_get_disc(E))>0) *tam = shifti(*tam,1);
+  return E;
+}
+
+GEN
+ellheegner(GEN E)
+{
+  pari_sp av = avma;
+  GEN z, P, ht, points, coefs, ymin, s, om, indmult;
+  long ind, lint, k, l, wtor, etor;
+  long bitprec = 16, prec = nbits2prec(bitprec)+1;
+  pari_timer T;
+  GEN N, cb, tam, torsion;
+
+  E = ell_apply_globalred_all(E, &N, &cb, &tam);
+  if (ellrootno_global(E) == 1)
+    pari_err_DOMAIN("ellheegner", "(analytic rank)%2","=",gen_0,E);
+  torsion = elltors(E);
+  wtor = itos( gel(torsion,1) ); /* #E(Q)_tor */
+  etor = wtor > 1? itos(gmael(torsion, 2, 1)): 1; /* exponent of E(Q)_tor */
+  while (1)
+  {
+    GEN hnaive, l1;
+    long bitneeded;
+    l1 = ellL1_bitprec(E, 1, bitprec);
+    if (expo(l1) < 1 - bitprec/2)
+      pari_err_DOMAIN("ellheegner", "analytic rank",">",gen_1,E);
+    om = ellR_omega(E,prec);
+    ht = divrr(mulru(l1, wtor * wtor), mulri(gel(om,1), tam));
+    if (DEBUGLEVEL) err_printf("Expected height=%Ps\n", ht);
+    hnaive = hnaive_max(E, ht);
+    if (DEBUGLEVEL) err_printf("Naive height <= %Ps\n", hnaive);
+    bitneeded = itos(gceil(divrr(hnaive, mplog2(prec)))) + 10;
+    if (DEBUGLEVEL) err_printf("precision = %ld\n", bitneeded);
+    if (bitprec>=bitneeded) break;
+    bitprec = bitneeded;
+    prec = nbits2prec(bitprec)+1;
+  }
+  indmult = heegner_indexmult(om, wtor, tam, prec);
+  heegner_find_disc(&ymin, &points, &coefs, &ind, E, indmult, prec);
+  if (DEBUGLEVEL == 1) err_printf("N = %Ps, ymin*N = %Ps\n",N,gmul(ymin,N));
+  if (DEBUGLEVEL) timer_start(&T);
+  s = heegner_psi(E, N, ymin, points, bitprec);
+  if (DEBUGLEVEL) timer_printf(&T,"heegner_psi");
+  l = lg(points);
+  z = mulsr(coefs[1], gel(s, 1));
+  for (k = 2; k < l; ++k) z = addrr(z, mulsr(coefs[k], gel(s, k)));
+  if (DEBUGLEVEL) err_printf("z=%Ps\n", z);
+  z = gsub(z, gmul(gel(om,1), ground(gdiv(z, gel(om,1)))));
+  lint = wtor > 1 ? cgcd(ind, etor): 1;
+  P = heegner_find_point(E, om, ht, gmulsg(2*lint, z), lint*2*ind, prec);
+  if (DEBUGLEVEL) timer_printf(&T,"heegner_find_point");
+  if (cb) P = ellchangepointinv(P, cb);
+  return gerepilecopy(av, P);
+}
diff --git a/src/basemath/elliptic.c b/src/basemath/elliptic.c
new file mode 100644
index 0000000..c68f47f
--- /dev/null
+++ b/src/basemath/elliptic.c
@@ -0,0 +1,5594 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                       ELLIPTIC CURVES                          **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#undef coordch
+
+/* Transforms a curve E into short Weierstrass form E' modulo p.
+   Returns a vector, the first two entries of which are a4' and a6'.
+   The third entry is a vector describing the isomorphism E' \to E.
+*/
+
+static void
+Fl_c4c6_to_a4a6(ulong c4, ulong c6, ulong p, ulong *a4, ulong *a6)
+{
+  *a4 = Fl_neg(Fl_mul(c4, 27, p), p);
+  *a6 = Fl_neg(Fl_mul(c6, 54, p), p);
+}
+static void
+c4c6_to_a4a6(GEN c4, GEN c6, GEN p, GEN *a4, GEN *a6)
+{
+  *a4 = Fp_neg(Fp_mulu(c4, 27, p), p);
+  *a6 = Fp_neg(Fp_mulu(c6, 54, p), p);
+}
+static void
+ell_to_a4a6(GEN E, GEN p, GEN *a4, GEN *a6)
+{
+  GEN c4 = Rg_to_Fp(ell_get_c4(E),p);
+  GEN c6 = Rg_to_Fp(ell_get_c6(E),p);
+  c4c6_to_a4a6(c4, c6, p, a4, a6);
+}
+static void
+Fl_ell_to_a4a6(GEN E, ulong p, ulong *a4, ulong *a6)
+{
+  ulong c4 = Rg_to_Fl(ell_get_c4(E),p);
+  ulong c6 = Rg_to_Fl(ell_get_c6(E),p);
+  Fl_c4c6_to_a4a6(c4, c6, p, a4, a6);
+}
+
+static GEN
+ell_to_a4a6_bc(GEN E, GEN p)
+{
+  GEN a1, a3, b2, c4, c6;
+  a1 = Rg_to_Fp(ell_get_a1(E),p);
+  a3 = Rg_to_Fp(ell_get_a3(E),p);
+  b2 = Rg_to_Fp(ell_get_b2(E),p);
+  c4 = Rg_to_Fp(ell_get_c4(E),p);
+  c6 = Rg_to_Fp(ell_get_c6(E),p);
+  /* [-27c4, -54c6, [6,3b2,3a1,108a3]] */
+  retmkvec3(Fp_neg(Fp_mulu(c4, 27, p), p), Fp_neg(Fp_mulu(c6, 54, p), p),
+            mkvec4(modsi(6,p),Fp_mulu(b2,3,p),Fp_mulu(a1,3,p),Fp_mulu(a3,108,p)));
+}
+
+void
+checkellpt(GEN z)
+{
+  if (typ(z)!=t_VEC) pari_err_TYPE("checkellpt", z);
+  switch(lg(z))
+  {
+    case 3: break;
+    case 2: if (isintzero(gel(z,1))) break;
+    /* fall through */
+    default: pari_err_TYPE("checkellpt", z);
+  }
+}
+void
+checkell5(GEN E)
+{
+  long l = lg(E);
+  if (typ(E)!=t_VEC || (l != 17 && l != 6)) pari_err_TYPE("checkell5",E);
+}
+void
+checkell(GEN E)
+{ if (typ(E)!=t_VEC || lg(E) != 17) pari_err_TYPE("checkell",E); }
+
+void
+checkell_Q(GEN E)
+{
+  if (typ(E)!=t_VEC || lg(E) != 17 || ell_get_type(E)!=t_ELL_Q)
+    pari_err_TYPE("checkell over Q",E);
+}
+
+void
+checkell_Qp(GEN E)
+{
+  if (typ(E)!=t_VEC || lg(E) != 17 || ell_get_type(E)!=t_ELL_Qp)
+    pari_err_TYPE("checkell over Qp",E);
+}
+
+static int
+ell_over_Fq(GEN E)
+{
+  long t = ell_get_type(E);
+  return t==t_ELL_Fp || t==t_ELL_Fq;
+}
+
+void
+checkell_Fq(GEN E)
+{
+  if (typ(E)!=t_VEC || lg(E) != 17 || !ell_over_Fq(E))
+  pari_err_TYPE("checkell over Fq", E);
+}
+
+GEN
+ellff_get_p(GEN E)
+{
+  GEN fg = ellff_get_field(E);
+  return typ(fg)==t_INT? fg: FF_p_i(fg);
+}
+
+static int
+ell_is_integral(GEN E)
+{
+  return typ(ell_get_a1(E)) == t_INT
+      && typ(ell_get_a2(E)) == t_INT
+      && typ(ell_get_a3(E)) == t_INT
+      && typ(ell_get_a4(E)) == t_INT
+      && typ(ell_get_a6(E)) == t_INT;
+}
+
+
+static void
+checkcoordch(GEN z)
+{ if (typ(z)!=t_VEC || lg(z) != 5) pari_err_TYPE("checkcoordch",z); }
+
+/* 4 X^3 + b2 X^2 + 2b4 X + b6, N is the characteristic of the base
+ * ring of NULL (char = 0) */
+static GEN
+RHSpol(GEN e, GEN N)
+{
+  GEN b2 = ell_get_b2(e), b6 = ell_get_b6(e), b42 = gmul2n(ell_get_b4(e),1);
+  return mkpoln(4, N? modsi(4,N): utoipos(4), b2, b42, b6);
+}
+
+static int
+invcmp(void *E, GEN x, GEN y) { (void)E; return -gcmp(x,y); }
+
+static GEN
+doellR_roots(GEN e, long prec)
+{
+  GEN R = roots(RHSpol(e,NULL), prec);
+  long s = ellR_get_sign(e);
+  if (s > 0)
+  { /* sort 3 real roots in decreasing order */
+    R = real_i(R);
+    gen_sort_inplace(R, NULL, &invcmp, NULL);
+  } else if (s < 0)
+  { /* make sure e1 is real, imag(e2) > 0 and imag(e3) < 0 */
+    gel(R,1) = real_i(gel(R,1));
+    if (signe(gmael(R,2,2)) < 0) swap(gel(R,2), gel(R,3));
+  }
+  return R;
+}
+static GEN
+ellR_root(GEN e, long prec) { return gel(ellR_roots(e,prec),1); }
+
+/* x^3 + a2 x^2 + a4 x + a6 */
+static GEN
+ellRHS(GEN e, GEN x)
+{
+  GEN z;
+  z = gadd(ell_get_a2(e),x);
+  z = gadd(ell_get_a4(e), gmul(x,z));
+  z = gadd(ell_get_a6(e), gmul(x,z));
+  return z;
+}
+
+/* a1 x + a3 */
+static GEN
+ellLHS0(GEN e, GEN x)
+{
+  GEN a1 = ell_get_a1(e);
+  GEN a3 = ell_get_a3(e);
+  return gequal0(a1)? a3: gadd(a3, gmul(x,a1));
+}
+
+static GEN
+ellLHS0_i(GEN e, GEN x)
+{
+  GEN a1 = ell_get_a1(e);
+  GEN a3 = ell_get_a3(e);
+  return signe(a1)? addii(a3, mulii(x, a1)): a3;
+}
+
+/* y^2 + a1 xy + a3 y */
+static GEN
+ellLHS(GEN e, GEN z)
+{
+  GEN y = gel(z,2);
+  return gmul(y, gadd(y, ellLHS0(e,gel(z,1))));
+}
+
+/* 2y + a1 x + a3 */
+static GEN
+d_ellLHS(GEN e, GEN z)
+{
+  return gadd(ellLHS0(e, gel(z,1)), gmul2n(gel(z,2),1));
+}
+
+/* return basic elliptic struct y[1..13], y[14] (domain type) and y[15]
+ * (domain-specific data) are left uninitialized, from x[1], ..., x[5].
+ * Also allocate room for n dynamic members (actually stored in the last
+ * component y[16])*/
+static GEN
+initsmall(GEN x, long n)
+{
+  GEN a1,a2,a3,a4,a6, b2,b4,b6,b8, c4,c6, D, j;
+  GEN y = obj_init(15, n);
+  switch(lg(x))
+  {
+    case 1:
+    case 2:
+    case 4:
+    case 5:
+      pari_err_TYPE("ellxxx [not an elliptic curve (ell5)]",x);
+      return NULL; break; /* not reached */
+    case 3:
+      a1 = a2 = a3 = gen_0;
+      a4 = gel(x,1);
+      a6 = gel(x,2);
+      b2 = gen_0;
+      b4 = gmul2n(a4,1);
+      b6 = gmul2n(a6,2);
+      b8 = gneg(gsqr(a4));
+      c4 = gmulgs(a4,-48);
+      c6 = gmulgs(a6,-864);
+      D = gadd(gmul(gmulgs(a4,-64), gsqr(a4)), gmulsg(-432,gsqr(a6)));
+      break;
+    default: /* l > 5 */
+    { GEN a11, a13, a33, b22;
+      a1 = gel(x,1);
+      a2 = gel(x,2);
+      a3 = gel(x,3);
+      a4 = gel(x,4);
+      a6 = gel(x,5);
+      a11= gsqr(a1);
+      b2 = gadd(a11, gmul2n(a2,2));
+      a13= gmul(a1, a3);
+      b4 = gadd(a13, gmul2n(a4,1));
+      a33= gsqr(a3);
+      b6 = gadd(a33, gmul2n(a6,2));
+      b8 = gsub(gadd(gmul(a11,a6), gmul(b6, a2)), gmul(a4, gadd(a4,a13)));
+      b22= gsqr(b2);
+      c4 = gadd(b22, gmulsg(-24,b4));
+      c6 = gadd(gmul(b2,gsub(gmulsg(36,b4),b22)), gmulsg(-216,b6));
+      D  = gsub(gmul(b4, gadd(gmulsg(9,gmul(b2,b6)),gmulsg(-8,gsqr(b4)))),
+                gadd(gmul(b22,b8),gmulsg(27,gsqr(b6))));
+      break;
+    }
+  }
+  gel(y,1) = a1;
+  gel(y,2) = a2;
+  gel(y,3) = a3;
+  gel(y,4) = a4;
+  gel(y,5) = a6;
+  gel(y,6) = b2; /* a1^2 + 4a2 */
+  gel(y,7) = b4; /* a1 a3 + 2a4 */
+  gel(y,8) = b6; /* a3^2 + 4 a6 */
+  gel(y,9) = b8; /* a1^2 a6 + 4a6 a2 + a2 a3^2 - a4(a4 + a1 a3) */
+  gel(y,10)= c4; /* b2^2 - 24 b4 */
+  gel(y,11)= c6; /* 36 b2 b4 - b2^3 - 216 b6 */
+  gel(y,12)= D;
+  if (gequal0(D)) { gel(y, 13) = gen_0; return NULL; }
+
+  if (typ(D) == t_POL && typ(c4) == t_POL && varn(D) == varn(c4))
+  { /* c4^3 / D, simplifying incrementally */
+    GEN g = RgX_gcd(D, c4);
+    if (degpol(g) == 0)
+      j = gred_rfrac_simple(gmul(gsqr(c4),c4), D);
+    else
+    {
+      GEN d, c = RgX_div(c4, g);
+      D = RgX_div(D, g);
+      g = RgX_gcd(D,c4);
+      if (degpol(g) == 0)
+        j = gred_rfrac_simple(gmul(gsqr(c4),c), D);
+      else
+      {
+        D = RgX_div(D, g);
+        d = RgX_div(c4, g);
+        g = RgX_gcd(D,c4);
+        if (degpol(g))
+        {
+          D = RgX_div(D, g);
+          c4 = RgX_div(c4, g);
+        }
+        j = gred_rfrac_simple(gmul(gmul(c4, d),c), D);
+      }
+    }
+  }
+  else
+    j = gdiv(gmul(gsqr(c4),c4), D);
+  gel(y,13) = j;
+  gel(y,16) = zerovec(n); return y;
+}
+
+void
+ellprint(GEN e)
+{
+  pari_sp av = avma;
+  long vx, vy;
+  GEN z;
+  checkell5(e);
+  vx = fetch_var(); name_var(vx, "X");
+  vy = fetch_var(); name_var(vy, "Y"); z = mkvec2(pol_x(vx), pol_x(vy));
+  err_printf("%Ps - (%Ps)\n", ellLHS(e, z), ellRHS(e, pol_x(vx)));
+  (void)delete_var();
+  (void)delete_var(); avma = av;
+}
+
+/* compute a,b such that E1: y^2 = x(x-a)(x-b) ~ E */
+static GEN
+doellR_ab(GEN E, long prec)
+{
+  GEN b2 = ell_get_b2(E), b4 = ell_get_b4(E), e1 = ellR_root(E, prec);
+  GEN a, b, t, w;
+
+  t = gmul2n(gadd(gmulsg(12,e1), b2), -2); /* = (12 e1 + b2) / 4 */
+  w = sqrtr( gmul2n(gadd(b4, gmul(e1,gadd(b2, mulur(6,e1)))),1) );
+  if (gsigne(t) > 0) setsigne(w, -1);
+  /* w^2 = 2b4 + 2b2 e1 + 12 e1^2 = 4(e1-e2)(e1-e3) */
+  a = gmul2n(gsub(w,t),-2);
+  b = gmul2n(w,-1); /* = sqrt( (e1 - e2)(e1 - e3) ) */
+  return mkvec2(a, b);
+}
+GEN
+ellR_ab(GEN E, long prec)
+{ return obj_checkbuild_prec(E, R_AB, &doellR_ab, prec); }
+
+/* return x mod p */
+static GEN
+padic_mod(GEN x) { return modii(gel(x,4), gel(x,2)); }
+
+/* a1, b1 are t_PADICs, a1/b1 = 1 (mod p) if p odd, (mod 2^4) otherwise.
+ * Return u^2 = 1 / 4M2(a1,b1), where M2(A,B) = B AGM(sqrt(A/B),1)^2; M2(A,B)
+ * is the common limit of (A_n, B_n), A_0 = A, B _0 = B;
+ *   A_{n+1} = (A_n + B_n + 2 B_{n+1}) / 4
+ *   B_{n+1} = B_n sqrt(A_n / B_n) = the square root of A_n B_n congruent to B_n
+ * Update (x,y) using p-adic Landen transform; if *pty = NULL, don't update y */
+static GEN
+do_padic_agm(GEN *ptx, GEN *pty, GEN a1, GEN b1)
+{
+  GEN bp = padic_mod(b1), x = *ptx;
+  for(;;)
+  {
+    GEN p1, d, a = a1, b = b1;
+    b1 = Qp_sqrt(gmul(a,b));
+    if (!b1) pari_err_PREC("p-adic AGM");
+    if (!equalii(padic_mod(b1), bp)) b1 = gneg_i(b1);
+    a1 = gmul2n(gadd(gadd(a,b),gmul2n(b1,1)),-2);
+    d = gsub(a1,b1);
+    if (gequal0(d)) { *ptx = x; return ginv(gmul2n(a1,2)); }
+    p1 = Qp_sqrt(gdiv(gadd(x,d),x)); /* = 1 (mod p) */
+    /* x_{n+1} = x_n  ((1 + sqrt(1 + r_n/x_n)) / 2)^2 */
+    x = gmul(x, gsqr(gmul2n(gaddsg(1,p1),-1)));
+    /* y_{n+1} = y_n / (1 - (r_n/4x_{n+1})^2) */
+    if (pty) *pty = gdiv(*pty, gsubsg(1, gsqr(gdiv(d,gmul2n(x,2)))));
+  }
+}
+
+/* q a t_REAL*/
+static long
+real_prec(GEN q)
+{ return signe(q)? realprec(q): LONG_MAX; }
+/* q a t_PADIC */
+static long
+padic_prec(GEN q)
+{ return signe(gel(q,4))? precp(q)+valp(q): valp(q); }
+
+/* check whether moduli are consistent */
+static void
+chk_p(GEN p, GEN p2)
+{ if (!equalii(p, p2)) pari_err_MODULUS("ellinit", p,p2); }
+
+static long
+base_ring(GEN x, GEN *pp, long *prec)
+{
+  long i, e = *prec, imax = minss(lg(x), 6);
+  GEN p = NULL;
+  long t = t_FRAC;
+  if (*pp) switch(t = typ(*pp))
+  {
+    case t_INT:
+      if (cmpis(*pp,2) < 0) { t = t_FRAC; p = NULL; break; }
+      p = *pp;
+      t = t_INTMOD;
+      break;
+    case t_INTMOD:
+      p = gel(*pp, 1);
+      break;
+    case t_REAL:
+      e = real_prec(*pp);
+      p = NULL;
+      break;
+    case t_PADIC:
+      e = padic_prec(*pp);
+      p = gel(*pp, 2);
+      break;
+    case t_FFELT:
+      p = *pp;
+      break;
+    default:
+      pari_err_TYPE("elliptic curve base_ring", *pp);
+      return 0;
+  }
+  /* Possible cases:
+   * t = t_FFELT (p t_FFELT)
+   * t = t_INTMOD (p a prime)
+   * t = t_PADIC (p a prime, e = padic prec)
+   * t = t_REAL (p = NULL, e = real prec)
+   * t = t_FRAC (p = NULL) */
+  for (i = 1; i < imax; i++)
+  {
+    GEN p2, q = gel(x,i);
+    switch(typ(q)) {
+      case t_PADIC:
+        p2 = gel(q,2);
+        switch(t)
+        {
+          case t_FRAC:  t = t_PADIC; p = p2; break;
+          case t_PADIC: chk_p(p,p2); break;
+          default: pari_err_TYPE("elliptic curve base_ring", x);
+        }
+        e = minss(e, padic_prec(q));
+        break;
+      case t_INTMOD:
+        p2 = gel(q,1);
+        switch(t)
+        {
+          case t_FRAC:  t = t_INTMOD; p = p2; break;
+          case t_FFELT: chk_p(FF_p_i(p),p2); break;
+          case t_INTMOD:chk_p(p,p2); break;
+          default: pari_err_TYPE("elliptic curve base_ring", x);
+        }
+        break;
+      case t_FFELT:
+        switch(t)
+        {
+          case t_INTMOD: chk_p(p, FF_p_i(q)); /* fall through */
+          case t_FRAC:   t = t_FFELT; p = q; break;
+          case t_FFELT:
+            if (!FF_samefield(p,q)) pari_err_MODULUS("ellinit", p,q);
+            break;
+          default: pari_err_TYPE("elliptic curve base_ring", x);
+        }
+        break;
+
+      case t_INT: case t_FRAC: break;
+      case t_REAL:
+        switch(t)
+        {
+          case t_REAL: e = minss(e, real_prec(q)); break;
+          case t_FRAC: e = real_prec(q); t = t_REAL; break;
+          default: pari_err_TYPE("elliptic curve base_ring", x);
+        }
+        break;
+      default: /* base ring too general */
+        return t_COMPLEX;
+    }
+  }
+  *pp = p; *prec = e; return t;
+}
+
+static GEN
+ellinit_Rg(GEN x, int real, long prec)
+{
+  pari_sp av=avma;
+  GEN y;
+  long s;
+  if (!(y = initsmall(x, 4))) return NULL;
+  s = real? gsigne( ell_get_disc(y) ): 0;
+  gel(y,14) = mkvecsmall(t_ELL_Rg);
+  gel(y,15) = mkvec(mkvecsmall2(prec2nbits(prec), s));
+  return gerepilecopy(av, y);
+}
+
+static GEN
+ellinit_Qp(GEN x, GEN p, long prec)
+{
+  pari_sp av=avma;
+  GEN y;
+  if (lg(x) > 6) x = vecslice(x,1,5);
+  x = QpV_to_QV(x); /* make entries rational */
+  if (!(y = initsmall(x, 2))) return NULL;
+  gel(y,14) = mkvecsmall(t_ELL_Qp);
+  gel(y,15) = mkvec(zeropadic(p, prec));
+  return gerepilecopy(av, y);
+}
+
+static GEN
+ellinit_Q(GEN x, long prec)
+{
+  pari_sp av=avma;
+  GEN y;
+  long s;
+  if (!(y = initsmall(x, 8))) return NULL;
+  s = gsigne( ell_get_disc(y) );
+  gel(y,14) = mkvecsmall(t_ELL_Q);
+  gel(y,15) = mkvec(mkvecsmall2(prec2nbits(prec), s));
+  return gerepilecopy(av, y);
+}
+
+static GEN
+ellinit_Fp(GEN x, GEN p)
+{
+  pari_sp av=avma;
+  long i;
+  GEN y, disc;
+  if (!(y = initsmall(x, 4))) return NULL;
+  if (cmpiu(p,3)<=0) /* ell_to_a4a6_bc does not handle p<=3 */
+  {
+    y = FF_ellinit(y,p_to_FF(p,0));
+    if (!y) return NULL;
+    return gerepilecopy(av, y);
+  }
+  disc = Rg_to_Fp(ell_get_disc(y),p);
+  if (!signe(disc)) return NULL;
+  for(i=1;i<=13;i++)
+    gel(y,i) = Fp_to_mod(Rg_to_Fp(gel(y,i),p),p);
+  gel(y,14) = mkvecsmall(t_ELL_Fp);
+  gel(y,15) = mkvec2(p, ell_to_a4a6_bc(y, p));
+  return gerepilecopy(av, y);
+}
+
+static GEN
+ellinit_Fq(GEN x, GEN fg)
+{
+  pari_sp av=avma;
+  GEN y;
+  if (!(y = initsmall(x, 4))) return NULL;
+  y = FF_ellinit(y,fg);
+  return y ? gerepilecopy(av, y): NULL;
+}
+
+GEN
+ellinit(GEN x, GEN p, long prec)
+{
+  pari_sp av = avma;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_STR: x = gel(ellsearchcurve(x),2); break;
+    case t_VEC: break;
+    default: pari_err_TYPE("ellxxx [not an elliptic curve (ell5)]",x);
+  }
+  switch (base_ring(x, &p, &prec))
+  {
+  case t_PADIC:
+    y = ellinit_Qp(x, p, prec);
+    break;
+  case t_INTMOD:
+    y = ellinit_Fp(x, p);
+    break;
+  case t_FFELT:
+    y = ellinit_Fq(x, p);
+    break;
+  case t_FRAC:
+    y = ellinit_Q(x, prec);
+    break;
+  case t_REAL:
+    y = ellinit_Rg(x, 1, prec);
+    break;
+  default:
+    y = ellinit_Rg(x, 0, prec);
+  }
+  if (!y) { avma = av; return cgetg(1,t_VEC); }
+  return y;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                     COORDINATE CHANGE                          **/
+/**  Apply [u,r,s,t]. All coordch_* functions update E[1..14] only **/
+/**  and copy E[15] (type-specific data), E[16] (dynamic data)     **/
+/**  verbatim                                                      **/
+/**                                                                **/
+/********************************************************************/
+/* [1,0,0,0] */
+static GEN
+init_ch(void) { return mkvec4(gen_1,gen_0,gen_0,gen_0); }
+static int
+is_trivial_change(GEN v)
+{
+  GEN u, r, s, t;
+  if (typ(v) == t_INT) return 1;
+  u = gel(v,1); r = gel(v,2); s = gel(v,3); t = gel(v,4);
+  return isint1(u) && isintzero(r) && isintzero(s) && isintzero(t);
+}
+
+/* compose coordinate changes, *vtotal = v is a ZV */
+/* v = [u,0,0,0] o v, u t_INT */
+static void
+composev_u(GEN *vtotal, GEN u)
+{
+  GEN v = *vtotal;
+  if (typ(v) == t_INT)
+    *vtotal = mkvec4(u,gen_0,gen_0,gen_0);
+  else if (!equali1(u))
+  {
+    GEN U = gel(v,1);
+    GEN uU = equali1(U)? u: mulii(u, U);
+    gel(v,1) = uU;
+  }
+}
+/* v = [1,r,0,0] o v, r t_INT */
+static void
+composev_r(GEN *vtotal, GEN r)
+{
+  GEN v = *vtotal;
+  if (typ(v) == t_INT)
+    *vtotal = mkvec4(gen_1,r,gen_0,gen_0);
+  else if (signe(r))
+  {
+    GEN U = gel(v,1), R = gel(v,2), S = gel(v,3), T = gel(v,4);
+    GEN rU2 = equali1(U)? r: mulii(r, sqri(U));
+    gel(v,2) = addii(R, rU2);
+    gel(v,4) = addii(T, mulii(rU2, S));
+  }
+}
+/* v = [1,0,s,0] o v, s t_INT */
+static void
+composev_s(GEN *vtotal, GEN s)
+{
+  GEN v = *vtotal;
+  if (typ(v) == t_INT)
+    *vtotal = mkvec4(gen_1,gen_0,s,gen_0);
+  else if (signe(s))
+  {
+    GEN U = gel(v,1), S = gel(v,3);
+    GEN sU = equali1(U)? s: mulii(s,U);
+    gel(v,3) = addii(S, sU);
+  }
+}
+/* v = [1,0,0,t] o v, t t_INT */
+static void
+composev_t(GEN *vtotal, GEN t)
+{
+  GEN v = *vtotal;
+  if (typ(v) == t_INT)
+    *vtotal = mkvec4(gen_1,gen_0,gen_0,t);
+  else if (signe(t))
+  {
+    GEN U = gel(v,1), T = gel(v,4);
+    GEN tU3 = equali1(U)? t: mulii(t, powiu(U,3));
+    gel(v,4) = addii(T, tU3);
+  }
+}
+/* v = [1,0,s,t] o v, s,t t_INT */
+static void
+composev_st(GEN *vtotal, GEN s, GEN t)
+{
+  GEN v = *vtotal;
+  if (typ(v) == t_INT)
+    *vtotal = mkvec4(gen_1,gen_0,s,t);
+  else if (!signe(t)) composev_s(vtotal, s);
+  else if (!signe(s)) composev_t(vtotal, t);
+  else
+  {
+    GEN U = gel(v,1), S = gel(v,3), T = gel(v,4);
+    if (!equali1(U))
+    {
+      GEN U3 = mulii(sqri(U), U);
+      t = mulii(U3, t);
+      s = mulii(U, s);
+    }
+    gel(v,3) = addii(S, s);
+    gel(v,4) = addii(T, t);
+  }
+}
+/* v = [1,r,s,t] o v, r,s,t t_INT */
+static void
+composev_rst(GEN *vtotal, GEN r, GEN s, GEN t)
+{
+  GEN v = *vtotal, U, R, S, T;
+  if (typ(v) == t_INT) { *vtotal = mkvec4(gen_1,r,s,t); return; }
+  if (!signe(r)) { composev_st(vtotal, s,t); return; }
+  v = *vtotal;
+  U = gel(v,1); R = gel(v,2); S = gel(v,3); T = gel(v,4);
+  if (equali1(U))
+    t = addii(t, mulii(S, r));
+  else
+  {
+    GEN U2 = sqri(U);
+    t = mulii(U2, addii(mulii(U, t), mulii(S, r)));
+    r = mulii(U2, r);
+    s = mulii(U, s);
+  }
+  gel(v,2) = addii(R, r);
+  gel(v,3) = addii(S, s);
+  gel(v,4) = addii(T, t);
+}
+
+/* Accumulate the effects of variable changes w o v, where
+ * w = [u,r,s,t], *vtotal = v = [U,R,S,T]. No assumption on types */
+static void
+gcomposev(GEN *vtotal, GEN w)
+{
+  GEN v = *vtotal;
+  GEN U2, U, R, S, T, u, r, s, t;
+
+  if (typ(v) == t_INT) { *vtotal = w; return; }
+  U = gel(v,1); R = gel(v,2); S = gel(v,3); T = gel(v,4);
+  u = gel(w,1); r = gel(w,2); s = gel(w,3); t = gel(w,4);
+  U2 = gsqr(U);
+  gel(v,1) = gmul(U, u);
+  gel(v,2) = gadd(R, gmul(U2, r));
+  gel(v,3) = gadd(S, gmul(U, s));
+  gel(v,4) = gadd(T, gmul(U2, gadd(gmul(U, t), gmul(S, r))));
+}
+
+/* [u,r,s,t]^-1 = [ 1/u,-r/u^2,-s/u, (rs-t)/u^3 ] */
+GEN
+ellchangeinvert(GEN w)
+{
+  GEN u,r,s,t, u2,u3, U,R,S,T;
+  if (typ(w) == t_INT) return w;
+  u = gel(w,1);
+  r = gel(w,2);
+  s = gel(w,3);
+  t = gel(w,4);
+  u2 = gsqr(u); u3 = gmul(u2,u);
+  U = ginv(u);
+  R = gdiv(gneg(r), u2);
+  S = gdiv(gneg(s), u);
+  T = gdiv(gsub(gmul(r,s), t), u3);
+  return mkvec4(U,R,S,T);
+}
+
+/* apply [u,0,0,0] */
+static GEN
+coordch_u(GEN e, GEN u)
+{
+  GEN y, u2, u3, u4, u6;
+  long lx;
+  if (gequal1(u)) return e;
+  y = cgetg_copy(e, &lx);
+  u2 = gsqr(u); u3 = gmul(u,u2); u4 = gsqr(u2); u6 = gsqr(u3);
+  gel(y,1) = gdiv(ell_get_a1(e),  u);
+  gel(y,2) = gdiv(ell_get_a2(e), u2);
+  gel(y,3) = gdiv(ell_get_a3(e), u3);
+  gel(y,4) = gdiv(ell_get_a4(e), u4);
+  gel(y,5) = gdiv(ell_get_a6(e), u6);
+  if (lx == 6) return y;
+  gel(y,6) = gdiv(ell_get_b2(e), u2);
+  gel(y,7) = gdiv(ell_get_b4(e), u4);
+  gel(y,8) = gdiv(ell_get_b6(e), u6);
+  gel(y,9) = gdiv(ell_get_b8(e), gsqr(u4));
+  gel(y,10)= gdiv(ell_get_c4(e), u4);
+  gel(y,11)= gdiv(ell_get_c6(e), u6);
+  gel(y,12)= gdiv(ell_get_disc(e), gsqr(u6));
+  gel(y,13)= ell_get_j(e);
+  gel(y,14)= gel(e,14);
+  gel(y,15)= gel(e,15);
+  gel(y,16)= gel(e,16);
+  return y;
+}
+/* apply [1,r,0,0] */
+static GEN
+coordch_r(GEN e, GEN r)
+{
+  GEN a2, b4, b6, y, p1, r2, b2r, rx3;
+  if (gequal0(r)) return e;
+  y = leafcopy(e);
+  a2 = ell_get_a2(e);
+  rx3 = gmulsg(3,r);
+
+  /* A2 = a2 + 3r */
+  gel(y,2) = gadd(a2,rx3);
+  /* A3 = a1 r + a3 */
+  gel(y,3) = ellLHS0(e,r);
+  /* A4 = 3r^2 + 2a2 r + a4 */
+  gel(y,4) = gadd(ell_get_a4(e), gmul(r,gadd(gmul2n(a2,1),rx3)));
+  /* A6 = r^3 + a2 r^2 + a4 r + a6 */
+  gel(y,5) = ellRHS(e,r);
+  if (lg(y) == 6) return y;
+
+  b4 = ell_get_b4(e);
+  b6 = ell_get_b6(e);
+  /* B2 = 12r + b2 */
+  gel(y,6) = gadd(ell_get_b2(e),gmul2n(rx3,2));
+  b2r = gmul(r, ell_get_b2(e));
+  r2 = gsqr(r);
+  /* B4 = 6r^2 + b2 r + b4 */
+  gel(y,7) = gadd(b4,gadd(b2r, gmulsg(6,r2)));
+  /* B6 = 4r^3 + 2b2 r^2 + 2b4 r + b6 */
+  gel(y,8) = gadd(b6,gmul(r,gadd(gmul2n(b4,1), gadd(b2r,gmul2n(r2,2)))));
+  /* B8 = 3r^4 + b2 r^3 + 3b4 r^2 + 3b6 r + b8 */
+  p1 = gadd(gmulsg(3,b4),gadd(b2r, gmulsg(3,r2)));
+  gel(y,9) = gadd(ell_get_b8(e), gmul(r,gadd(gmulsg(3,b6), gmul(r,p1))));
+  return y;
+}
+/* apply [1,0,s,0] */
+static GEN
+coordch_s(GEN e, GEN s)
+{
+  GEN a1, y;
+  if (gequal0(s)) return e;
+  a1 = ell_get_a1(e);
+  y = leafcopy(e);
+
+  /* A1 = a1 + 2s */
+  gel(y,1) = gadd(a1,gmul2n(s,1));
+  /* A2 = a2 - (a1 s + s^2) */
+  gel(y,2) = gsub(ell_get_a2(e),gmul(s,gadd(a1,s)));
+  /* A4 = a4 - s a3 */
+  gel(y,4) = gsub(ell_get_a4(e),gmul(s,ell_get_a3(e)));
+  return y;
+}
+/* apply [1,0,0,t] */
+static GEN
+coordch_t(GEN e, GEN t)
+{
+  GEN a1, a3, y;
+  if (gequal0(t)) return e;
+  a1 = ell_get_a1(e); a3 = ell_get_a3(e);
+  y = leafcopy(e);
+  /* A3 = 2t + a3 */
+  gel(y,3) = gadd(a3, gmul2n(t,1));
+  /* A4 = a4 - a1 t */
+  gel(y,4) = gsub(ell_get_a4(e), gmul(t,a1));
+  /* A6 = a6 - t(t + a3) */
+  gel(y,5) = gsub(ell_get_a6(e), gmul(t,gadd(t, a3)));
+  return y;
+}
+/* apply [1,0,s,t] */
+static GEN
+coordch_st(GEN e, GEN s, GEN t)
+{
+  GEN y, a1, a3;
+  if (gequal0(s)) return coordch_t(e, t);
+  if (gequal0(t)) return coordch_s(e, s);
+  a1 = ell_get_a1(e); a3 = ell_get_a3(e);
+  y = leafcopy(e);
+  /* A1 = a1 + 2s */
+  gel(y,1) = gadd(a1,gmul2n(s,1));
+  /* A2 = a2 - (a1 s + s^2) */
+  gel(y,2) = gsub(ell_get_a2(e),gmul(s,gadd(a1,s)));
+  /* A3 = 2t + a3 */
+  gel(y,3) = gadd(a3,gmul2n(t,1));
+  /* A4 = a4 - (a1 t + s (2t + a3)) */
+  gel(y,4) = gsub(ell_get_a4(e),gadd(gmul(t,a1),gmul(s,gel(y,3))));
+  /* A6 = a6 - t(t + a3) */
+  gel(y,5) = gsub(ell_get_a6(e), gmul(t,gadd(t, a3)));
+  return y;
+}
+/* apply [1,r,s,t] */
+static GEN
+coordch_rst(GEN e, GEN r, GEN s, GEN t)
+{
+  e = coordch_r(e, r);
+  return coordch_st(e, s, t);
+}
+/* apply w = [u,r,s,t] */
+static GEN
+coordch(GEN e, GEN w)
+{
+  if (typ(w) == t_INT) return e;
+  e = coordch_rst(e, gel(w,2), gel(w,3), gel(w,4));
+  return coordch_u(e, gel(w,1));
+}
+
+/* the ch_* routines update E[14] (type), E[15] (type specific data), E[16]
+ * (dynamic data) */
+static GEN
+ch_Qp(GEN E, GEN e, GEN w)
+{
+  GEN S, p = ellQp_get_zero(E), u2 = NULL, u = gel(w,1), r = gel(w,2);
+  long prec = valp(p);
+  if (base_ring(E, &p, &prec) != t_PADIC) return ellinit(E, p, prec);
+  if ((S = obj_check(e, Qp_ROOT)))
+  {
+    if (!u2) u2 = gsqr(u);
+    obj_insert(E, Qp_ROOT, gdiv(gsub(S, r), u2));
+  }
+  if ((S = obj_check(e, Qp_TATE)))
+  {
+    GEN U2 = gel(S,1), U = gel(S,2), Q = gel(S,3), AB = gel(S,4);
+    if (!u2) u2 = gsqr(u);
+    U2 = gmul(U2, u2);
+    U = gmul(U, u);
+    AB = gdiv(AB, u2);
+    obj_insert(E, Qp_TATE, mkvec4(U2,U,Q,AB));
+  }
+  return E;
+}
+
+/* common to Q and Rg */
+static GEN
+ch_R(GEN E, GEN e, GEN w)
+{
+  GEN S, u = gel(w,1), r = gel(w,2);
+  if ((S = obj_check(e, R_PERIODS)))
+    obj_insert(E, R_PERIODS, gmul(S, u));
+  if ((S = obj_check(e, R_ETA)))
+    obj_insert(E, R_ETA, gmul(S, u));
+  if ((S = obj_check(e, R_ROOTS)))
+  {
+    GEN ro = cgetg(4, t_VEC), u2 = gsqr(u);
+    long i;
+    for (i = 1; i <= 3; i++) gel(ro,i) = gdiv(gsub(gel(S,i), r), u2);
+    obj_insert(E, R_ROOTS, ro);
+  }
+  return E;
+}
+
+static GEN
+ch_Rg(GEN E, GEN e, GEN w)
+{
+  GEN p = NULL;
+  long prec = ellR_get_prec(E);
+  if (base_ring(E, &p, &prec) != t_REAL) return ellinit(E, p, prec);
+  ch_R(E, e, w); return E;
+}
+
+static GEN
+ch_Q(GEN E, GEN e, GEN w)
+{
+  long prec = ellR_get_prec(E);
+  GEN S, v = NULL, p = NULL;
+  if (base_ring(E, &p, &prec) != t_FRAC) return ellinit(E, p, prec);
+  ch_R(E, e, w);
+  if ((S = obj_check(e, Q_GROUPGEN)))
+    S = obj_insert(E, Q_GROUPGEN, ellchangepoint(S, w));
+  if ((S = obj_check(e, Q_MINIMALMODEL)))
+  {
+    if (lg(S) == 2)
+    { /* model was minimal */
+      if (!is_trivial_change(w)) /* no longer minimal */
+        S = mkvec3(gel(S,1), ellchangeinvert(w), e);
+      (void)obj_insert(E, Q_MINIMALMODEL, S);
+    }
+    else
+    {
+      v = gel(S,2);
+      if (gequal(v, w) || (is_trivial_change(v) && is_trivial_change(w)))
+        S = mkvec(gel(S,1)); /* now minimal */
+      else
+      {
+        w = ellchangeinvert(w);
+        gcomposev(&w, v); v = w;
+        S = leafcopy(S); /* don't modify S in place: would corrupt e */
+        gel(S,2) = v;
+      }
+      (void)obj_insert(E, Q_MINIMALMODEL, S);
+    }
+  }
+  if ((S = obj_check(e, Q_GLOBALRED)))
+    S = obj_insert(E, Q_GLOBALRED, S);
+  if ((S = obj_check(e, Q_ROOTNO)))
+    S = obj_insert(E, Q_ROOTNO, S);
+  return E;
+}
+
+static void
+ch_FF(GEN E, GEN e, GEN w)
+{
+  GEN S;
+  if ((S = obj_check(e, FF_CARD)))
+    S = obj_insert(E, FF_CARD, S);
+  if ((S = obj_check(e, FF_GROUP)))
+    S = obj_insert(E, FF_GROUP, S);
+  if ((S = obj_check(e, FF_GROUPGEN)))
+    S = obj_insert(E, FF_GROUPGEN, ellchangepoint(S, w));
+  if ((S = obj_check(e, FF_O)))
+    S = obj_insert(E, FF_O, S);
+}
+
+/* FF_CARD, FF_GROUP, FF_O are invariant */
+static GEN
+ch_Fp(GEN E, GEN e, GEN w)
+{
+  long prec = 0;
+  GEN p = ellff_get_field(E);
+  if (base_ring(E, &p, &prec) != t_INTMOD) return ellinit(E, p, prec);
+  gel(E,15) = mkvec2(p, ell_to_a4a6_bc(E, p));
+  ch_FF(E, e, w); return E;
+}
+static GEN
+ch_Fq(GEN E, GEN e, GEN w)
+{
+  long prec = 0;
+  GEN p = ellff_get_field(E);
+  if (base_ring(E, &p, &prec) != t_FFELT) return ellinit(E, p, prec);
+  gel(E,15) = FF_elldata(E, p);
+  ch_FF(E, e, w); return E;
+}
+
+static void
+ell_reset(GEN E)
+{ gel(E,16) = zerovec(lg(gel(E,16))-1); }
+
+GEN
+ellchangecurve(GEN e, GEN w)
+{
+  pari_sp av = avma;
+  GEN E;
+  checkell5(e);
+  if (equali1(w)) return gcopy(e);
+  checkcoordch(w);
+  E = coordch(leafcopy(e), w);
+  if (lg(E) == 6) return gerepilecopy(av, E);
+  ell_reset(E); E = gerepilecopy(av, E);
+  switch(ell_get_type(E))
+  {
+    case t_ELL_Qp: E = ch_Qp(E,e,w); break;
+    case t_ELL_Fp: E = ch_Fp(E,e,w); break;
+    case t_ELL_Fq: E = ch_Fq(E,e,w); break;
+    case t_ELL_Q:  E = ch_Q(E,e,w);  break;
+    case t_ELL_Rg: E = ch_Rg(E,e,w); break;
+  }
+  return E;
+}
+
+static void
+E_compose_u(GEN *vtotal, GEN *e, GEN u) {*e=coordch_u(*e,u); composev_u(vtotal,u);}
+static void
+E_compose_r(GEN *vtotal, GEN *e, GEN r) {*e=coordch_r(*e,r); composev_r(vtotal,r);}
+#if 0
+static void
+E_compose_s(GEN *vtotal, GEN *e, GEN s) {*e=coordch_s(*e,s); composev_s(vtotal,s);}
+#endif
+static void
+E_compose_t(GEN *vtotal, GEN *e, GEN t) {*e=coordch_t(*e,t); composev_t(vtotal,t);}
+static void
+E_compose_rst(GEN *vtotal, GEN *e, GEN r, GEN s, GEN t)
+{ *e=coordch_rst(*e,r,s,t); composev_rst(vtotal,r,s,t); }
+
+/* apply [1,r,0,0] to P */
+static GEN
+ellchangepoint_r(GEN P, GEN r)
+{
+  GEN x, y, a;
+  if (ell_is_inf(P)) return P;
+  x = gel(P,1); y = gel(P,2); a = gsub(x,r);
+  return mkvec2(a, y);
+}
+
+/* X = (x-r)/u^2
+ * Y = (y - s(x-r) - t) / u^3 */
+static GEN
+ellchangepoint0(GEN P, GEN v2, GEN v3, GEN r, GEN s, GEN t)
+{
+  GEN a, x, y;
+  if (ell_is_inf(P)) return P;
+  x = gel(P,1); y = gel(P,2); a = gsub(x,r);
+  retmkvec2(gmul(v2, a), gmul(v3, gsub(y, gadd(gmul(s,a),t))));
+}
+
+GEN
+ellchangepoint(GEN x, GEN ch)
+{
+  GEN y, v, v2, v3, r, s, t, u;
+  long tx, i, lx = lg(x);
+  pari_sp av = avma;
+
+  if (typ(x) != t_VEC) pari_err_TYPE("ellchangepoint",x);
+  if (equali1(ch)) return gcopy(x);
+  checkcoordch(ch);
+  if (lx == 1) return cgetg(1, t_VEC);
+  u = gel(ch,1); r = gel(ch,2); s = gel(ch,3); t = gel(ch,4);
+  v = ginv(u); v2 = gsqr(v); v3 = gmul(v,v2);
+  tx = typ(gel(x,1));
+  if (is_matvec_t(tx))
+  {
+    y = cgetg(lx,tx);
+    for (i=1; i<lx; i++)
+      gel(y,i) = ellchangepoint0(gel(x,i),v2,v3,r,s,t);
+  }
+  else
+    y = ellchangepoint0(x,v2,v3,r,s,t);
+  return gerepilecopy(av,y);
+}
+
+/* x = u^2*X + r
+ * y = u^3*Y + s*u^2*X + t */
+static GEN
+ellchangepointinv0(GEN P, GEN u2, GEN u3, GEN r, GEN s, GEN t)
+{
+  GEN a, X, Y;
+  if (ell_is_inf(P)) return P;
+  X = gel(P,1); Y = gel(P,2); a = gmul(u2,X);
+  return mkvec2(gadd(a, r), gadd(gmul(u3, Y), gadd(gmul(s, a), t)));
+}
+GEN
+ellchangepointinv(GEN x, GEN ch)
+{
+  GEN y, u, r, s, t, u2, u3;
+  long tx, i, lx = lg(x);
+  pari_sp av = avma;
+
+  if (typ(x) != t_VEC) pari_err_TYPE("ellchangepointinv",x);
+  if (equali1(ch)) return gcopy(x);
+  checkcoordch(ch);
+  if (lx == 1) return cgetg(1, t_VEC);
+  u = gel(ch,1); r = gel(ch,2); s = gel(ch,3); t = gel(ch,4);
+  u2 = gsqr(u); u3 = gmul(u,u2);
+  tx = typ(gel(x,1));
+  if (is_matvec_t(tx))
+  {
+    y = cgetg(lx,tx);
+    for (i=1; i<lx; i++)
+      gel(y,i) = ellchangepointinv0(gel(x,i),u2,u3,r,s,t);
+  }
+  else
+    y = ellchangepointinv0(x,u2,u3,r,s,t);
+  return gerepilecopy(av,y);
+}
+
+static long
+ellexpo(GEN E)
+{
+  long i, f, e = -(long)HIGHEXPOBIT;
+  for (i=1; i<=5; i++)
+  {
+    f = gexpo(gel(E,i));
+    if (f > e) e = f;
+  }
+  return e;
+}
+
+/* Exactness of lhs and rhs in the following depends in non-obvious ways
+ * on the coeffs of the curve as well as on the components of the point z.
+ * Thus if e is exact, with a1==0, and z has exact y coordinate only, the
+ * lhs will be exact but the rhs won't. */
+int
+oncurve(GEN e, GEN z)
+{
+  GEN LHS, RHS, x;
+  long pl, pr, ex, expx;
+  pari_sp av;
+
+  checkellpt(z); if (ell_is_inf(z)) return 1; /* oo */
+  av = avma;
+  LHS = ellLHS(e,z);
+  RHS = ellRHS(e,gel(z,1)); x = gsub(LHS,RHS);
+  if (gequal0(x)) { avma = av; return 1; }
+  pl = precision(LHS);
+  pr = precision(RHS);
+  if (!pl && !pr) { avma = av; return 0; } /* both of LHS, RHS are exact */
+  /* at least one of LHS,RHS is inexact */
+  ex = pr? gexpo(RHS): gexpo(LHS); /* don't take exponent of exact 0 */
+  if (!pr || (pl && pl < pr)) pr = pl; /* min among nonzero elts of {pl,pr} */
+  expx = gexpo(x);
+  pr = (expx < ex - prec2nbits(pr) + 15
+     || expx < ellexpo(e) - prec2nbits(pr) + 5);
+  avma = av; return pr;
+}
+
+GEN
+ellisoncurve(GEN e, GEN x)
+{
+  long i, tx = typ(x), lx;
+
+  checkell5(e);
+  if (!is_vec_t(tx)) pari_err_TYPE("ellisoncurve [point]", x);
+  lx = lg(x); if (lx==1) return cgetg(1,tx);
+  tx = typ(gel(x,1));
+  if (is_vec_t(tx))
+  {
+    GEN z = cgetg(lx,tx);
+    for (i=1; i<lx; i++) gel(z,i) = ellisoncurve(e,gel(x,i));
+    return z;
+  }
+  return oncurve(e, x)? gen_1: gen_0;
+}
+
+GEN
+elladd(GEN e, GEN z1, GEN z2)
+{
+  GEN p1, p2, x, y, x1, x2, y1, y2;
+  pari_sp av = avma, tetpil;
+
+  checkell5(e); checkellpt(z1); checkellpt(z2);
+  if (ell_is_inf(z1)) return gcopy(z2);
+  if (ell_is_inf(z2)) return gcopy(z1);
+
+  x1 = gel(z1,1); y1 = gel(z1,2);
+  x2 = gel(z2,1); y2 = gel(z2,2);
+  if (x1 == x2 || gequal(x1,x2))
+  { /* y1 = y2 or -LHS0-y2 */
+    if (y1 != y2)
+    {
+      int eq;
+      if (precision(y1) || precision(y2))
+        eq = (gexpo(gadd(ellLHS0(e,x1),gadd(y1,y2))) >= gexpo(y1));
+      else
+        eq = gequal(y1,y2);
+      if (!eq) { avma = av; return ellinf(); }
+    }
+    p2 = d_ellLHS(e,z1);
+    if (gequal0(p2)) { avma = av; return ellinf(); }
+    p1 = gadd(gsub(ell_get_a4(e),gmul(ell_get_a1(e),y1)),
+              gmul(x1,gadd(gmul2n(ell_get_a2(e),1),gmulsg(3,x1))));
+  }
+  else {
+    p1 = gsub(y2,y1);
+    p2 = gsub(x2,x1);
+  }
+  p1 = gdiv(p1,p2);
+  x = gsub(gmul(p1,gadd(p1,ell_get_a1(e))), gadd(gadd(x1,x2),ell_get_a2(e)));
+  y = gadd(gadd(y1, ellLHS0(e,x)), gmul(p1,gsub(x,x1)));
+  tetpil = avma; p1 = cgetg(3,t_VEC);
+  gel(p1,1) = gcopy(x);
+  gel(p1,2) = gneg(y); return gerepile(av,tetpil,p1);
+}
+
+static GEN
+ellneg_i(GEN e, GEN z)
+{
+  GEN t;
+  if (ell_is_inf(z)) return z;
+  t = cgetg(3,t_VEC);
+  gel(t,1) = gel(z,1);
+  gel(t,2) = gneg_i(gadd(gel(z,2), ellLHS0(e,gel(z,1))));
+  return t;
+}
+
+GEN
+ellneg(GEN e, GEN z)
+{
+  pari_sp av;
+  GEN t, y;
+  checkell5(e); checkellpt(z);
+  if (ell_is_inf(z)) return z;
+  t = cgetg(3,t_VEC);
+  gel(t,1) = gcopy(gel(z,1));
+  av = avma;
+  y = gneg(gadd(gel(z,2), ellLHS0(e,gel(z,1))));
+  gel(t,2) = gerepileupto(av, y);
+  return t;
+}
+
+GEN
+ellsub(GEN e, GEN z1, GEN z2)
+{
+  pari_sp av = avma;
+  checkell5(e); checkellpt(z2);
+  return gerepileupto(av, elladd(e, z1, ellneg_i(e,z2)));
+}
+
+/* E an ell, x a scalar */
+static GEN
+ellordinate_i(GEN E, GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN a = ellRHS(E,x), b = ellLHS0(E,x), D = gadd(gsqr(b), gmul2n(a,2));
+  GEN d, y, p;
+
+  /* solve y*(y+b) = a */
+  if (gequal0(D)) {
+    if (ell_get_type(E) == t_ELL_Fq && equaliu(ellff_get_p(E),2))
+      retmkvec( FF_sqrt(a) );
+    b = gneg_i(b); y = cgetg(2,t_VEC);
+    gel(y,1) = gmul2n(b,-1);
+    return gerepileupto(av,y);
+  }
+  /* D != 0 */
+  switch(ell_get_type(E))
+  {
+    case t_ELL_Fp: /* imply p!=2 */
+      p = ellff_get_p(E);
+      D = gel(D,2);
+      if (kronecker(D, p) < 0) { avma = av; return cgetg(1,t_VEC); }
+      d = Fp_sqrt(D, p);
+      break;
+    case t_ELL_Fq:
+      if (equaliu(ellff_get_p(E),2))
+      {
+        GEN F = FFX_roots(mkpoln(3, gen_1, b, a), D);
+        if (lg(F) == 1) { avma = av; return cgetg(1,t_VEC); }
+        return gerepileupto(av, F);
+      }
+      if (!FF_issquareall(D,&d)) { avma = av; return cgetg(1,t_VEC); }
+      break;
+    case t_ELL_Q:
+      if (typ(x) == t_COMPLEX) { d = gsqrt(D, prec); break; }
+      if (!issquareall(D,&d)) { avma = av; return cgetg(1,t_VEC); }
+      break;
+
+    case t_ELL_Qp:
+      p = ellQp_get_p(E);
+      D = cvtop(D, p, ellQp_get_prec(E));
+      if (!issquare(D)) { avma = av; return cgetg(1,t_VEC); }
+      d = Qp_sqrt(D);
+      break;
+
+    default:
+      d = gsqrt(D,prec);
+  }
+  a = gsub(d,b); y = cgetg(3,t_VEC);
+  gel(y,1) = gmul2n(a, -1);
+  gel(y,2) = gsub(gel(y,1),d);
+  return gerepileupto(av,y);
+}
+
+GEN
+ellordinate(GEN e, GEN x, long prec)
+{
+  checkell(e);
+  if (is_matvec_t(typ(x)))
+  {
+    long i, lx;
+    GEN v = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(v,i) = ellordinate(e,gel(x,i),prec);
+    return v;
+  }
+  return ellordinate_i(e, x, prec);
+}
+
+GEN
+ellrandom(GEN E)
+{
+  GEN fg;
+  checkell_Fq(E);
+  fg = ellff_get_field(E);
+  if (typ(fg)==t_FFELT)
+    return FF_ellrandom(E);
+  else
+  {
+    pari_sp av = avma;
+    GEN p = fg, e = ellff_get_a4a6(E);
+    GEN P = random_FpE(gel(e,1),gel(e,2),p);
+    P = FpE_to_mod(FpE_changepoint(P,gel(e,3),p),p);
+    return gerepileupto(av, P);
+  }
+}
+
+/* n t_QUAD or t_COMPLEX, z != [0] */
+static GEN
+ellmul_CM(GEN e, GEN z, GEN n)
+{
+  GEN p1p, q1p, x, y, p0, p1, q0, q1, z1, z2, grdx, b2ov12, N = gnorm(n);
+  long ln, ep, vn;
+
+  if (typ(N) != t_INT)
+    pari_err_TYPE("ellmul (non integral CM exponent)",N);
+  ln = itos_or_0(shifti(addsi(1, N), 3));
+  if (!ln) pari_err_OVERFLOW("ellmul_CM [norm too large]");
+  vn = ((ln>>1)-4)>>2;
+  z1 = ellwpseries(e, 0, ln);
+  z2 = gsubst(z1, 0, monomial(n, 1, 0));
+  p0 = gen_0; p1 = gen_1;
+  q0 = gen_1; q1 = gen_0;
+  do
+  {
+    GEN p2,q2, ss = gen_0;
+    do
+    {
+      ep = (-valp(z2)) >> 1;
+      ss = gadd(ss, gmul(gel(z2,2), monomial(gen_1, ep, 0)));
+      z2 = gsub(z2, gmul(gel(z2,2), gpowgs(z1, ep)));
+    }
+    while (valp(z2) <= 0);
+    p2 = gadd(p0, gmul(ss,p1)); p0 = p1; p1 = p2;
+    q2 = gadd(q0, gmul(ss,q1)); q0 = q1; q1 = q2;
+    if (!signe(z2)) break;
+    z2 = ginv(z2);
+  }
+  while (degpol(p1) < vn);
+  if (degpol(p1) > vn || signe(z2))
+    pari_err_TYPE("ellmul [not a complex multiplication]", n);
+  q1p = RgX_deriv(q1);
+  b2ov12 = gdivgs(ell_get_b2(e), 12); /* x - b2/12 */
+  grdx = gadd(gel(z,1), b2ov12);
+  q1 = poleval(q1, grdx);
+  if (gequal0(q1)) return ellinf();
+
+  p1p = RgX_deriv(p1);
+  p1 = poleval(p1, grdx);
+  p1p = poleval(p1p, grdx);
+  q1p = poleval(q1p, grdx);
+
+  x = gdiv(p1,q1);
+  y = gdiv(gsub(gmul(p1p,q1), gmul(p1,q1p)), gmul(n,gsqr(q1)));
+  x = gsub(x, b2ov12);
+  y = gsub( gmul(d_ellLHS(e,z), y), ellLHS0(e,x));
+  return mkvec2(x, gmul2n(y,-1));
+}
+
+static GEN
+_sqr(void *e, GEN x) { return elladd((GEN)e, x, x); }
+static GEN
+_mul(void *e, GEN x, GEN y) { return elladd((GEN)e, x, y); }
+
+static GEN
+ellffmul(GEN E, GEN P, GEN n)
+{
+  GEN fg = ellff_get_field(E);
+  if (typ(fg)==t_FFELT)
+    return FF_ellmul(E, P, n);
+  else
+  {
+    pari_sp av = avma;
+    GEN p = fg, e = ellff_get_a4a6(E), Q;
+    GEN Pp = FpE_changepointinv(RgE_to_FpE(P, p), gel(e,3), p);
+    GEN Qp = FpE_mul(Pp, n, gel(e,1), p);
+    Q = FpE_to_mod(FpE_changepoint(Qp, gel(e,3), p), p);
+    return gerepileupto(av, Q);
+  }
+}
+/* [n] z, n integral */
+static GEN
+ellmul_Z(GEN e, GEN z, GEN n)
+{
+  long s;
+  if (ell_is_inf(z)) return ellinf();
+  if (lg(e)==17 && ell_over_Fq(e)) return ellffmul(e,z,n);
+  s = signe(n);
+  if (!s) return ellinf();
+  if (s < 0) z = ellneg_i(e,z);
+  if (is_pm1(n)) return z;
+  return gen_pow(z, n, (void*)e, &_sqr, &_mul);
+}
+
+/* x a t_REAL, try to round it to an integer */
+enum { OK, LOW_PREC, NO };
+static long
+myroundr(GEN *px)
+{
+  GEN x = *px;
+  long e;
+  if (bit_prec(x) - expo(x) < 5) return LOW_PREC;
+  *px = grndtoi(x, &e);
+  if (e >= -5) return NO;
+  return OK;
+}
+
+/* E has CM by Q, t_COMPLEX or t_QUAD. Return q such that E has CM by Q/q
+ * or gen_1 (couldn't find q > 1)
+ * or NULL (doesn't have CM by Q) */
+static GEN
+CM_factor(GEN E, GEN Q)
+{
+  GEN w, tau, D, v, x, y, F, dF, q, r, fk, fkb, fkc;
+  long prec;
+
+  if (ell_get_type(E) != t_ELL_Q) return gen_1;
+  switch(typ(Q))
+  {
+    case t_COMPLEX:
+      D = utoineg(4);
+      v = gel(Q,2);
+      break;
+    case t_QUAD:
+      D = quad_disc(Q);
+      v = gel(Q,3);
+      break;
+    default:
+      return NULL; /*-Wall*/
+  }
+  /* disc Q = v^2 D, D < 0 fundamental */
+  w = ellR_omega(E, DEFAULTPREC + nbits2nlong(expi(D)));
+  tau = gdiv(gel(w,2), gel(w,1));
+  prec = precision(tau);
+  /* disc tau = -4 k^2 (Im tau)^2 for some integral k
+   * Assuming that E has CM by Q, then disc Q / disc tau = f^2 is a square.
+   * Compute f*k */
+  x = gel(tau,1);
+  y = gel(tau,2); /* tau = x + Iy */
+  fk = gmul(gdiv(v, gmul2n(y, 1)), sqrtr_abs(itor(D, prec)));
+  switch(myroundr(&fk))
+  {
+    case NO: return NULL;
+    case LOW_PREC: return gen_1;
+  }
+  fk = absi(fk);
+
+  fkb = gmul(fk, gmul2n(x,1));
+  switch(myroundr(&fkb))
+  {
+    case NO: return NULL;
+    case LOW_PREC: return gen_1;
+  }
+
+  fkc = gmul(fk, cxnorm(tau));
+  switch(myroundr(&fkc))
+  {
+    case NO: return NULL;
+    case LOW_PREC: return gen_1;
+  }
+
+  /* tau is a root of fk (X^2 - b X + c) \in Z[X],  */
+  F = Q_primpart(mkvec3(fk, fkb, fkc));
+  dF = qfb_disc(F); /* = disc tau, E has CM by orders of disc dF q^2, all q */
+  q = dvmdii(dF, D, &r);
+  if (r != gen_0 || !Z_issquareall(q, &q)) return NULL;
+  /* disc(Q) = disc(tau) (v / q)^2 */
+  v = dvmdii(absi(v), q, &r);
+  if (r != gen_0) return NULL;
+  return is_pm1(v)? gen_1: v; /* E has CM by Q/q: [Q] = [q] o [Q/q] */
+}
+
+/* [a + w] z, a integral, w pure imaginary */
+static GEN
+ellmul_CM_aux(GEN e, GEN z, GEN a, GEN w)
+{
+  GEN A, B, q;
+  checkell(e);
+  if (typ(a) != t_INT) pari_err_TYPE("ellmul_CM",a);
+  q = CM_factor(e, w);
+  if (!q) pari_err_TYPE("ellmul [not a complex multiplication]",w);
+  if (q != gen_1) w = gdiv(w, q);
+  /* compute [a + q w] z, z has CM by w */
+  if (typ(w) == t_QUAD && is_pm1(gel(gel(w,1), 3)))
+  { /* replace w by w - u, u in Z, so that N(w-u) is minimal
+     * N(w - u) = N w - Tr w u + u^2, minimal for u = Tr w / 2 */
+    GEN u = gtrace(w);
+    if (typ(u) != t_INT) pari_err_TYPE("ellmul_CM",w);
+    u = shifti(u, -1);
+    if (signe(u))
+    {
+      w = gsub(w, u);
+      a = addii(a, mulii(q,u));
+    }
+    /* [a + w]z = [(a + qu)] z + [q] [(w - u)] z */
+  }
+  A = ellmul_Z(e,z,a);
+  B = ellmul_CM(e,z,w);
+  if (q != gen_1) B = ellmul_Z(e, B, q);
+  return elladd(e, A, B);
+}
+GEN
+ellmul(GEN e, GEN z, GEN n)
+{
+  pari_sp av = avma;
+
+  checkell5(e); checkellpt(z);
+  if (ell_is_inf(z)) return ellinf();
+  switch(typ(n))
+  {
+    case t_INT: return gerepilecopy(av, ellmul_Z(e,z,n));
+    case t_QUAD: {
+      GEN pol = gel(n,1), a = gel(n,2), b = gel(n,3);
+      if (signe(gel(pol,2)) < 0) pari_err_TYPE("ellmul_CM",n); /* disc > 0 ? */
+      return gerepileupto(av, ellmul_CM_aux(e,z,a,mkquad(pol, gen_0,b)));
+    }
+    case t_COMPLEX: {
+      GEN a = gel(n,1), b = gel(n,2);
+      return gerepileupto(av, ellmul_CM_aux(e,z,a,mkcomplex(gen_0,b)));
+    }
+  }
+  pari_err_TYPE("ellmul (non integral, non CM exponent)",n);
+  return NULL; /* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       Periods                                  **/
+/**                                                                **/
+/********************************************************************/
+
+/* References:
+  The complex AGM, periods of elliptic curves over C and complex elliptic logarithms
+  John E. Cremona, Thotsaphon Thongjunthug, arXiv:1011.0914
+*/
+
+static GEN
+ellomega_agm(GEN a, GEN b, GEN c, long prec)
+{
+  GEN pi = mppi(prec), mIpi = mkcomplex(gen_0, negr(pi));
+  GEN Mac = agm(a,c,prec), Mbc = agm(b,c,prec);
+  retmkvec2(gdiv(pi, Mac), gdiv(mIpi, Mbc));
+}
+
+static GEN
+ellomega_cx(GEN E, long prec)
+{
+  pari_sp av = avma;
+  GEN roots = ellR_roots(E,prec);
+  GEN e1=gel(roots,1), e2=gel(roots,2), e3=gel(roots,3);
+  GEN a = gsqrt(gsub(e1,e2),prec);
+  GEN b = gsqrt(gsub(e2,e3),prec);
+  GEN c = gsqrt(gsub(e1,e3),prec);
+  return gerepileupto(av, ellomega_agm(a,b,c,prec));
+}
+
+/* return [w1,w2] for E / R; w1 > 0 is real.
+ * If e.disc > 0, w2 = -I r; else w2 = w1/2 - I r, for some real r > 0.
+ * => tau = w1/w2 is in upper half plane */
+static GEN
+doellR_omega(GEN E, long prec)
+{
+  pari_sp av = avma;
+  GEN roots, e1, e3, z, a, b, c;
+  if (ellR_get_sign(E) >= 0) return ellomega_cx(E,prec);
+  roots = ellR_roots(E,prec);
+  e1 = gel(roots,1);
+  e3 = gel(roots,3);
+  z = gsqrt(gsub(e1,e3),prec); /* imag(e1-e3) > 0, so that b > 0*/
+  a = gel(z,1); /* >= 0 */
+  b = gel(z,2);
+  c = gabs(z, prec);
+  z = ellomega_agm(a,b,c,prec);
+  return gerepilecopy(av, mkvec2(gel(z,1),gmul2n(gadd(gel(z,1),gel(z,2)),-1)));
+}
+static GEN
+doellR_eta(GEN E, long prec)
+{ GEN w = ellR_omega(E, prec); return elleta(w, prec); }
+
+GEN
+ellR_omega(GEN E, long prec)
+{ return obj_checkbuild_prec(E, R_PERIODS, &doellR_omega, prec); }
+GEN
+ellR_eta(GEN E, long prec)
+{ return obj_checkbuild_prec(E, R_ETA, &doellR_eta, prec); }
+GEN
+ellR_roots(GEN E, long prec)
+{ return obj_checkbuild_prec(E, R_ROOTS, &doellR_roots, prec); }
+
+/********************************************************************/
+/**                                                                **/
+/**                       ELLIPTIC FUNCTIONS                       **/
+/**                                                                **/
+/********************************************************************/
+/* P = [x,0] is 2-torsion on y^2 = g(x). Return w1/2, (w1+w2)/2, or w2/2
+ * depending on whether x is closest to e1,e2, or e3, the 3 complex root of g */
+static GEN
+zell_closest_0(GEN om, GEN x, GEN ro)
+{
+  GEN e1 = gel(ro,1), e2 = gel(ro,2), e3 = gel(ro,3);
+  GEN d1 = gnorm(gsub(x,e1));
+  GEN d2 = gnorm(gsub(x,e2));
+  GEN d3 = gnorm(gsub(x,e3));
+  GEN z = gel(om,2);
+  if (gcmp(d1, d2) <= 0)
+  { if (gcmp(d1, d3) <= 0) z = gel(om,1); }
+  else
+  { if (gcmp(d2, d3)<=0) z = gadd(gel(om,1),gel(om,2)); }
+  return gmul2n(z, -1);
+}
+
+static GEN
+zellcx(GEN E, GEN P, long prec)
+{
+  GEN roots = ellR_roots(E, prec+EXTRAPRECWORD);
+  GEN x0 = gel(P,1), y0 = d_ellLHS(E,P);
+  if (gequal0(y0))
+    return zell_closest_0(ellomega_cx(E,prec),x0,roots);
+  else
+  {
+    GEN e1 = gel(roots,1), e2 = gel(roots,2), e3 = gel(roots,3);
+    GEN a = gsqrt(gsub(e1,e3),prec), b = gsqrt(gsub(e1,e2),prec);
+    GEN r = gsqrt(gdiv(gsub(x0,e3), gsub(x0,e2)),prec);
+    GEN t = gdiv(gneg(y0), gmul2n(gmul(r,gsub(x0,e2)),1));
+    GEN ar = real_i(a), br = real_i(b), ai = imag_i(a), bi = imag_i(b);
+    /* |a+b| < |a-b| */
+    if (gcmp(gmul(ar,br), gneg(gmul(ai,bi))) < 0) b = gneg(b);
+    return zellagmcx(a,b,r,t,prec);
+  }
+}
+
+/* Assume E/R, disc E < 0, and P \in E(R) ==> z \in R */
+static GEN
+zellrealneg(GEN E, GEN P, long prec)
+{
+  GEN x0 = gel(P,1), y0 = d_ellLHS(E,P);
+  if (gequal0(y0)) return gmul2n(gel(ellR_omega(E,prec),1),-1);
+  else
+  {
+    GEN roots = ellR_roots(E, prec+EXTRAPRECWORD);
+    GEN e1 = gel(roots,1), e3 = gel(roots,3);
+    GEN a = gsqrt(gsub(e1,e3),prec);
+    GEN z = gsqrt(gsub(x0,e3), prec);
+    GEN ar = real_i(a), zr = real_i(z), ai = imag_i(a), zi = imag_i(z);
+    GEN t = gdiv(gneg(y0), gmul2n(gnorm(z),1));
+    GEN r2 = ginv(gsqrt(gaddsg(1,gdiv(gmul(ai,zi),gmul(ar,zr))),prec));
+    return zellagmcx(ar,gabs(a,prec),r2,gmul(t,r2),prec);
+  }
+}
+
+/* Assume E/R, disc E > 0, and P \in E(R) */
+static GEN
+zellrealpos(GEN E, GEN P, long prec)
+{
+  GEN roots = ellR_roots(E, prec+EXTRAPRECWORD);
+  GEN e1,e2,e3, a,b, x0 = gel(P,1), y0 = d_ellLHS(E,P);
+  if (gequal0(y0)) return zell_closest_0(ellR_omega(E,prec), x0,roots);
+  e1 = gel(roots,1);
+  e2 = gel(roots,2);
+  e3 = gel(roots,3);
+  a = gsqrt(gsub(e1,e3),prec);
+  b = gsqrt(gsub(e1,e2),prec);
+  if (gcmp(x0,e1)>0) {
+    GEN r = gsqrt(gdiv(gsub(x0,e3), gsub(x0,e2)),prec);
+    GEN t = gdiv(gneg(y0), gmul2n(gmul(r,gsub(x0,e2)),1));
+    return zellagmcx(a,b,r,t,prec);
+  } else {
+    GEN om = ellR_omega(E,prec);
+    GEN r = gdiv(a,gsqrt(gsub(e1,x0),prec));
+    GEN t = gdiv(gmul(r,y0),gmul2n(gsub(x0,e3),1));
+    return gsub(zellagmcx(a,b,r,t,prec),gmul2n(gel(om,2),-1));
+  }
+}
+
+/* Let T = 4x^3 + b2 x^2 + 2b4 x + b6, where T has a unique p-adic root 'a'.
+ * Return a lift of a to padic accuracy prec. We have
+ * 216 T = 864 X^3 - 18 c4X - c6, where X = x + b2/12 */
+static GEN
+doellQp_root(GEN E, long prec)
+{
+  GEN c4=ell_get_c4(E), c6=ell_get_c6(E), j=ell_get_j(E), p=ellQp_get_p(E);
+  GEN c4p, c6p, T, a, pe;
+  long alpha;
+  int pis2 = equaliu(p, 2);
+  if (Q_pval(j, p) >= 0) pari_err_DOMAIN(".root", "v_p(j)", ">=", gen_0, j);
+  /* v(j) < 0 => v(c4^3) = v(c6^2) = 2 alpha */
+  alpha = Q_pvalrem(ell_get_c4(E), p, &c4) >> 1;
+  if (alpha) (void)Q_pvalrem(ell_get_c6(E), p, &c6);
+  /* Renormalized so that v(c4) = v(c6) = 0; multiply by p^alpha at the end */
+  if (prec < 4 && pis2) prec = 4;
+  pe = powiu(p, prec);
+  c4 = Rg_to_Fp(c4, pe); c4p = remii(c4,p);
+  c6 = Rg_to_Fp(c6, pe); c6p = remii(c6,p);
+  if (pis2)
+  { /* Use 432T(X/4) = 27X^3 - 9c4 X - 2c6 to have integral root; a=0 mod 2 */
+    T = mkpoln(4, utoipos(27), gen_0, Fp_muls(c4, -9, pe), Fp_muls(c6, -2, pe));
+    a = ZpX_liftroot(T, gen_0, p, prec);
+    alpha -= 2;
+  }
+  else if (equaliu(p, 3))
+  { /* Use 216T(X/3) = 32X^3 - 6c4 X - c6 to have integral root; a=-c6 mod 3 */
+    a = Fp_neg(c6p, p);
+    T = mkpoln(4, utoipos(32), gen_0, Fp_muls(c4, -6, pe), Fp_neg(c6, pe));
+    a = ZX_Zp_root(T, a, p, prec);
+    switch(lg(a)-1)
+    {
+      case 1: /* single root */
+        a = gel(a,1); break;
+      case 3: /* three roots, e.g. "15a1", choose the right one */
+      {
+        GEN a1 = gel(a,1), a2 = gel(a,2), a3 = gel(a,3);
+        long v1 = Z_lval(subii(a2, a3), 3);
+        long v2 = Z_lval(subii(a1, a3), 3);
+        long v3 = Z_lval(subii(a1, a2), 3);
+        if      (v1 == v2) a = a3;
+        else if (v1 == v3) a = a2;
+        else a = a1;
+      }
+      break;
+    }
+    alpha--;
+  }
+  else
+  { /* p != 2,3: T = 4(x-a)(x-b)^2 = 4x^3 - 3a^2 x - a^3 when b = -a/2
+     * (so that the trace coefficient vanishes) => a = c6/6c4 (mod p)*/
+    a = Fp_div(c6p, Fp_mulu(c4p, 6, p), p);
+    T = mkpoln(4, utoipos(864), gen_0, Fp_muls(c4, -18, pe), Fp_neg(c6, pe));
+    a = ZpX_liftroot(T, a, p, prec);
+  }
+  a = cvtop(a, p, prec);
+  if (alpha) setvalp(a, valp(a)+alpha);
+  return gsub(a, gdivgs(ell_get_b2(E), 12));
+}
+GEN
+ellQp_root(GEN E, long prec)
+{ return obj_checkbuild_padicprec(E, Qp_ROOT, &doellQp_root, prec); }
+
+/* compute a,b such that E1: y^2 = x(x-a)(x-b) ~ E */
+static void
+doellQp_ab(GEN E, GEN *pta, GEN *ptb, long prec)
+{
+  GEN b2 = ell_get_b2(E), b4 = ell_get_b4(E), e1 = ellQp_root(E, prec);
+  GEN w, t = gadd(gdivgs(b2,4), gmulsg(3,e1));
+  w = Qp_sqrt(gmul2n(gadd(b4,gmul(e1,gadd(b2,gmulsg(6,e1)))),1));
+  if (valp(gadd(t,w)) <= valp(w)) w = gneg_i(w); /* <=> v(d) > v(w) */
+  /* w^2 = 2b4 + 2b2 e1 + 12 e1^2 = 4(e1-e2)(e1-e3) */
+  *pta = gmul2n(gsub(w,t),-2);
+  *ptb = gmul2n(w,-1);
+}
+
+static GEN
+doellQp_Tate_uniformization(GEN E, long prec0)
+{
+  GEN p = ellQp_get_p(E), j = ell_get_j(E);
+  GEN u, u2, q, x1, a, b, d, s, t;
+  long v, prec = prec0+2;
+
+  if (Q_pval(j, p) >= 0) pari_err_DOMAIN(".tate", "v_p(j)", ">=", gen_0, j);
+START:
+  doellQp_ab(E, &a, &b, prec);
+  d = gsub(a,b);
+  v = prec0 - precp(d);
+  if (v > 0) { prec += v; goto START; }
+  x1 = gmul2n(d,-2);
+  u2 = do_padic_agm(&x1,NULL,a,b);
+
+  t = gaddsg(1, ginv(gmul2n(gmul(u2,x1),1)));
+  s = Qp_sqrt(gsubgs(gsqr(t), 1));
+  q = gadd(t,s);
+  if (gequal0(q)) q = gsub(t,s);
+  v = prec0 - precp(q);
+  if (v > 0) { prec += v; goto START; }
+  if (valp(q) < 0) q = ginv(q);
+  if (issquare(u2))
+    u = Qp_sqrt(u2);
+  else
+  {
+    long v = fetch_user_var("u");
+    GEN T = mkpoln(3, gen_1, gen_0, gneg(u2));
+    setvarn(T, v); u = mkpolmod(pol_x(v), T);
+  }
+  return mkvec4(u2, u, q, mkvec2(a, b));
+}
+GEN
+ellQp_Tate_uniformization(GEN E, long prec)
+{return obj_checkbuild_padicprec(E,Qp_TATE,&doellQp_Tate_uniformization,prec);}
+GEN
+ellQp_u(GEN E, long prec)
+{ GEN T = ellQp_Tate_uniformization(E, prec); return gel(T,2); }
+GEN
+ellQp_u2(GEN E, long prec)
+{ GEN T = ellQp_Tate_uniformization(E, prec); return gel(T,1); }
+GEN
+ellQp_q(GEN E, long prec)
+{ GEN T = ellQp_Tate_uniformization(E, prec); return gel(T,3); }
+GEN
+ellQp_ab(GEN E, long prec)
+{ GEN T = ellQp_Tate_uniformization(E, prec); return gel(T,4); }
+
+static GEN
+zellQp(GEN E, GEN z, long prec)
+{
+  pari_sp av = avma;
+  GEN b2, a, b, ab, c0, r0, r1, ar1, e1, x, y, delta, x0,x1, y0,y1, t;
+  if (ell_is_inf(z)) return gen_1;
+  b2 = ell_get_b2(E);
+  e1 = ellQp_root(E, prec);
+  ab = ellQp_ab(E, prec);
+  a = gel(ab,1);
+  b = gel(ab,2); r1 = gsub(a,b);
+  x = gel(z,1);
+  y = gel(z,2);
+  r0 = gadd(e1,gmul2n(b2,-2));
+  c0 = gadd(x, gmul2n(r0,-1)); ar1 = gmul(a,r1);
+  delta = gdiv(ar1, gsqr(c0));
+  x0 = gmul2n(gmul(c0,gaddsg(1,Qp_sqrt(gsubsg(1,gmul2n(delta,2))))),-1);
+  y0 = gdiv(gadd(y, gmul2n(d_ellLHS(E,z), -1)), gsubsg(1, gdiv(ar1,gsqr(x0))));
+
+  x1 = gmul(x0, gsqr(gmul2n(gaddsg(1, Qp_sqrt(gdiv(gadd(x0,r1),x0))),-1)));
+  y1 = gdiv(y0, gsubsg(1, gsqr(gdiv(r1,gmul2n(x1,2)))));
+  if (gequal0(x1)) pari_err_PREC("ellpointtoz");
+
+  (void)do_padic_agm(&x1,&y1, a,b);
+  t = gmul(ellQp_u(E, prec), gmul2n(y1,1)); /* 2u y_oo */
+  t = gdiv(gsub(t, x1), gadd(t, x1));
+  return gerepileupto(av, t);
+}
+
+GEN
+zell(GEN e, GEN z, long prec)
+{
+  pari_sp av = avma;
+  GEN t;
+  long s;
+
+  checkell(e); checkellpt(z);
+  switch(ell_get_type(e))
+  {
+    case t_ELL_Qp:
+      prec = minss(ellQp_get_prec(e), padicprec_relative(z));
+      return zellQp(e, z, prec);
+    case t_ELL_Q: break;
+    case t_ELL_Rg: break;
+    default: pari_err_TYPE("ellpointtoz", e);
+  }
+  (void)ellR_omega(e, prec); /* type checking */
+  if (ell_is_inf(z)) return gen_0;
+  s = ellR_get_sign(e);
+  if (s && typ(gel(z,1))!=t_COMPLEX && typ(gel(z,2))!=t_COMPLEX)
+    t = (s < 0)? zellrealneg(e,z,prec): zellrealpos(e,z,prec);
+  else
+    t = zellcx(e,z,prec);
+  return gerepileupto(av,t);
+}
+
+enum period_type { t_PER_W, t_PER_WETA, t_PER_ELL };
+/* normalization / argument reduction for ellptic functions */
+typedef struct {
+  enum period_type type;
+  GEN in; /* original input */
+  GEN w1,w2,tau; /* original basis for L = <w1,w2> = w2 <1,tau> */
+  GEN W1,W2,Tau; /* new basis for L = <W1,W2> = W2 <1,tau> */
+  GEN a,b,c,d; /* t_INT; tau in F = h/Sl2, tau = g.t, g=[a,b;c,d] in SL(2,Z) */
+  GEN z,Z; /* z/w2 defined mod <1,tau>, Z = z + x*tau + y reduced mod <1,tau> */
+  GEN x,y; /* t_INT */
+  int swap; /* 1 if we swapped w1 and w2 */
+  int some_q_is_real; /* exp(2iPi g.tau) for some g \in SL(2,Z) */
+  int some_z_is_real; /* z + xw1 + yw2 is real for some x,y \in Z */
+  int some_z_is_pure_imag; /* z + xw1 + yw2 = it, t \in R */
+  int q_is_real; /* exp(2iPi tau) \in R */
+  int abs_u_is_1; /* |exp(2iPi Z)| = 1 */
+  long prec; /* precision(Z) */
+} ellred_t;
+
+/* compute g in SL_2(Z), g.t is in the usual
+   fundamental domain. Internal function no check, no garbage. */
+static void
+set_gamma(GEN t, GEN *pa, GEN *pb, GEN *pc, GEN *pd)
+{
+  GEN a, b, c, d, run = dbltor(1. - 1e-8);
+  pari_sp av = avma, lim = stack_lim(av, 1);
+
+  a = d = gen_1;
+  b = c = gen_0;
+  for(;;)
+  {
+    GEN m, n = ground(real_i(t));
+    if (signe(n))
+    { /* apply T^n */
+      t = gsub(t,n);
+      a = subii(a, mulii(n,c));
+      b = subii(b, mulii(n,d));
+    }
+    m = cxnorm(t); if (gcmp(m,run) > 0) break;
+    t = gneg_i(gdiv(gconj(t), m)); /* apply S */
+    togglesign_safe(&c); swap(a,c);
+    togglesign_safe(&d); swap(b,d);
+    if (low_stack(lim, stack_lim(av, 1))) {
+      if (DEBUGMEM>1) pari_warn(warnmem, "redimagsl2");
+      gerepileall(av, 5, &t, &a,&b,&c,&d);
+    }
+  }
+  *pa = a;
+  *pb = b;
+  *pc = c;
+  *pd = d;
+}
+/* Im t > 0. Return U.t in PSl2(Z)'s standard fundamental domain.
+ * Set *pU to U. */
+GEN
+redtausl2(GEN t, GEN *pU)
+{
+  pari_sp av = avma;
+  GEN U, a,b,c,d;
+  set_gamma(t, &a, &b, &c, &d);
+  U = mkmat2(mkcol2(a,c), mkcol2(b,d));
+  t = gdiv(gadd(gmul(a,t), b),
+           gadd(gmul(c,t), d));
+  gerepileall(av, 2, &t, &U);
+  *pU = U; return t;
+}
+
+/* swap w1, w2 so that Im(t := w1/w2) > 0. Set tau = representative of t in
+ * the standard fundamental domain, and g in Sl_2, such that tau = g.t */
+static void
+red_modSL2(ellred_t *T, long prec)
+{
+  long s, p;
+  T->tau = gdiv(T->w1,T->w2);
+  if (isexactzero(real_i(T->tau))) T->some_q_is_real = 1;
+  s = gsigne(imag_i(T->tau));
+  if (!s) pari_err_DOMAIN("elliptic function", "det(w1,w2)", "=", gen_0,
+                          mkvec2(T->w1,T->w2));
+  T->swap = (s < 0);
+  if (T->swap) { swap(T->w1, T->w2); T->tau = ginv(T->tau); }
+  set_gamma(T->tau, &T->a, &T->b, &T->c, &T->d);
+  /* update lattice */
+  T->W1 = gadd(gmul(T->a,T->w1), gmul(T->b,T->w2));
+  T->W2 = gadd(gmul(T->c,T->w1), gmul(T->d,T->w2));
+  T->Tau = gdiv(T->W1, T->W2);
+  if (isexactzero(real_i(T->Tau))) T->some_q_is_real = T->q_is_real = 1;
+  p = precision(T->Tau); if (!p) p = prec;
+  T->prec = p;
+}
+static void
+reduce_z(GEN z, ellred_t *T)
+{
+  long p;
+  GEN Z;
+  T->abs_u_is_1 = 0;
+  T->some_z_is_real = 0;
+  T->some_z_is_pure_imag = 0;
+  switch(typ(z))
+  {
+    case t_INT: case t_REAL: case t_FRAC: case t_COMPLEX: break;
+    case t_QUAD:
+      z = isexactzero(gel(z,2))? gel(z,1): quadtofp(z, T->prec);
+      break;
+    default: pari_err_TYPE("reduction mod 2-dim lattice (reduce_z)", z);
+  }
+  T->z = z;
+  Z = gdiv(z, T->W2);
+  T->x = ground(gdiv(imag_i(Z), imag_i(T->Tau)));
+  if (signe(T->x)) Z = gsub(Z, gmul(T->x,T->Tau));
+  T->y = ground(real_i(Z));
+  if (signe(T->y)) Z = gsub(Z, T->y);
+  if (typ(Z) != t_COMPLEX)
+    T->some_z_is_real = T->abs_u_is_1 = 1;
+  else if (typ(z) != t_COMPLEX)
+    T->some_z_is_real = 1;
+  else if (isexactzero(gel(z,1)) || isexactzero(gel(Z,1)))
+    T->some_z_is_pure_imag = 1;
+  p = precision(Z);
+  if (gequal0(Z) || (p && gexpo(Z) < 5 - prec2nbits(p)))
+    Z = NULL; /*z in L*/
+  if (p && p < T->prec) T->prec = p;
+  T->Z = Z;
+}
+/* return x.eta1 + y.eta2 */
+static GEN
+eta_correction(ellred_t *T, GEN eta)
+{
+  GEN y1 = NULL, y2 = NULL;
+  if (signe(T->x)) y1 = gmul(T->x, gel(eta,1));
+  if (signe(T->y)) y2 = gmul(T->y, gel(eta,2));
+  if (!y1) return y2? y2: gen_0;
+  return y2? gadd(y1, y2): y1;
+}
+/* e is either
+ * - [w1,w2]
+ * - [[w1,w2],[eta1,eta2]]
+ * - an ellinit structure */
+static void
+compute_periods(ellred_t *T, GEN z, long prec)
+{
+  GEN w, e;
+  T->q_is_real = 0;
+  T->some_q_is_real = 0;
+  switch(T->type)
+  {
+    case t_PER_ELL:
+    {
+      long pr, p = prec;
+      if (z && (pr = precision(z))) p = pr;
+      e = T->in;
+      w = ellR_omega(e, p);
+      T->some_q_is_real = T->q_is_real = 1;
+      break;
+    }
+    case t_PER_W:
+      w = T->in; break;
+    default: /*t_PER_WETA*/
+      w = gel(T->in,1); break;
+  }
+  T->w1 = gel(w,1);
+  T->w2 = gel(w,2);
+  red_modSL2(T, prec);
+  if (z) reduce_z(z, T);
+}
+static int
+check_periods(GEN e, ellred_t *T)
+{
+  GEN w1;
+  if (typ(e) != t_VEC) return 0;
+  T->in = e;
+  switch(lg(e))
+  {
+    case 17:
+      T->type = t_PER_ELL;
+      break;
+    case 3:
+      w1 = gel(e,1);
+      if (typ(w1) != t_VEC)
+        T->type = t_PER_W;
+      else
+      {
+        if (lg(w1) != 3) return 0;
+        T->type = t_PER_WETA;
+      }
+      break;
+    default: return 0;
+  }
+  return 1;
+}
+static int
+get_periods(GEN e, GEN z, ellred_t *T, long prec)
+{
+  if (!check_periods(e, T)) return 0;
+  compute_periods(T, z, prec); return 1;
+}
+
+/* 2iPi/x, more efficient when x pure imaginary */
+static GEN
+PiI2div(GEN x, long prec) { return gdiv(Pi2n(1, prec), mulcxmI(x)); }
+/* exp(I x y), more efficient for x in R, y pure imaginary */
+GEN
+expIxy(GEN x, GEN y, long prec) { return gexp(gmul(x, mulcxI(y)), prec); }
+
+static GEN
+check_real(GEN q)
+{ return (typ(q) == t_COMPLEX && gequal0(gel(q,2)))? gel(q,1): q; }
+
+/* Return E_k(tau). Slow if tau is not in standard fundamental domain */
+static GEN
+trueE(GEN tau, long k, long prec)
+{
+  pari_sp lim, av;
+  GEN p1, q, y, qn;
+  long n = 1;
+
+  if (k == 2) return trueE2(tau, prec);
+  q = expIxy(Pi2n(1, prec), tau, prec);
+  q = check_real(q);
+  y = gen_0;
+  av = avma; lim = stack_lim(av,2); qn = gen_1;
+  for(;; n++)
+  { /* compute y := sum_{n>0} n^(k-1) q^n / (1-q^n) */
+    qn = gmul(q,qn);
+    p1 = gdiv(gmul(powuu(n,k-1),qn), gsubsg(1,qn));
+    if (gequal0(p1) || gexpo(p1) <= - prec2nbits(prec) - 5) break;
+    y = gadd(y, p1);
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"elleisnum");
+      gerepileall(av, 2, &y,&qn);
+    }
+  }
+  return gadd(gen_1, gmul(y, gdiv(gen_2, szeta(1-k, prec))));
+}
+
+/* (2iPi/W2)^k E_k(W1/W2) */
+static GEN
+_elleisnum(ellred_t *T, long k)
+{
+  GEN y = trueE(T->Tau, k, T->prec);
+  y = gmul(y, gpowgs(mulcxI(gdiv(Pi2n(1,T->prec), T->W2)),k));
+  return check_real(y);
+}
+
+/* Return (2iPi)^k E_k(L) = (2iPi/w2)^k E_k(tau), with L = <w1,w2>, k > 0 even
+ * E_k(tau) = 1 + 2/zeta(1-k) * sum(n>=1, n^(k-1) q^n/(1-q^n))
+ * If flag is != 0 and k=4 or 6, compute g2 = E4/12 or g3 = -E6/216 resp. */
+GEN
+elleisnum(GEN om, long k, long flag, long prec)
+{
+  pari_sp av = avma;
+  GEN y;
+  ellred_t T;
+
+  if (k<=0) pari_err_DOMAIN("elleisnum", "k", "<=", gen_0, stoi(k));
+  if (k&1) pari_err_DOMAIN("elleisnum", "k % 2", "!=", gen_0, stoi(k));
+  if (!get_periods(om, NULL, &T, prec)) pari_err_TYPE("elleisnum",om);
+  y = _elleisnum(&T, k);
+  if (k==2 && signe(T.c))
+  {
+    GEN a = gmul(Pi2n(1,T.prec), mului(12, T.c));
+    y = gsub(y, mulcxI(gdiv(a, gmul(T.w2, T.W2))));
+  }
+  else if (k==4 && flag) y = gdivgs(y,  12);
+  else if (k==6 && flag) y = gdivgs(y,-216);
+  return gerepileupto(av,y);
+}
+
+/* return quasi-periods associated to [T->W1,T->W2] */
+static GEN
+_elleta(ellred_t *T)
+{
+  GEN y1, y2, e2 = gdivgs(_elleisnum(T,2), 12);
+  y2 = gmul(T->W2, e2);
+  y1 = gadd(PiI2div(T->W2, T->prec), gmul(T->W1,e2));
+  retmkvec2(gneg(y1), gneg(y2));
+}
+
+/* compute eta1, eta2 */
+GEN
+elleta(GEN om, long prec)
+{
+  pari_sp av = avma;
+  GEN y1, y2, E2, pi;
+  ellred_t T;
+
+  if (!check_periods(om, &T)) pari_err_TYPE("elleta",om);
+  if (T.type == t_PER_ELL) return ellR_eta(om, prec);
+
+  compute_periods(&T, NULL, prec);
+  prec = T.prec;
+  pi = mppi(prec);
+  E2 = trueE2(T.Tau, prec); /* E_2(Tau) */
+  if (signe(T.c))
+  {
+    GEN u = gdiv(T.w2, T.W2);
+    /* E2 := u^2 E2 + 6iuc/pi = E_2(tau) */
+    E2 = gadd(gmul(gsqr(u), E2), mulcxI(gdiv(gmul(mului(6,T.c), u), pi)));
+  }
+  y2 = gdiv(gmul(E2, sqrr(pi)), gmulsg(3, T.w2));
+  if (T.swap)
+  {
+    y1 = y2;
+    y2 = gadd(gmul(T.tau,y1), PiI2div(T.w2, prec));
+  }
+  else
+    y1 = gsub(gmul(T.tau,y2), PiI2div(T.w2, prec));
+  switch(typ(T.w1))
+  {
+    case t_INT: case t_FRAC: case t_REAL:
+      y1 = real_i(y1);
+  }
+  return gerepilecopy(av, mkvec2(y1,y2));
+}
+GEN
+ellperiods(GEN w, long flag, long prec)
+{
+  pari_sp av = avma;
+  ellred_t T;
+  if (!get_periods(w, NULL, &T, prec)) pari_err_TYPE("ellperiods",w);
+  switch(flag)
+  {
+    case 0: return gerepilecopy(av, mkvec2(T.W1, T.W2));
+    case 1: return gerepilecopy(av, mkvec2(mkvec2(T.W1, T.W2), _elleta(&T)));
+    default: pari_err_FLAG("ellperiods");
+             return NULL;/*not reached*/
+  }
+}
+
+/* 2Pi Im(z)/log(2) */
+static double
+get_toadd(GEN z) { return (2*PI/LOG2)*gtodouble(imag_i(z)); }
+
+/* computes the numerical value of wp(z | L), L = om1 Z + om2 Z
+ * return NULL if z in L.  If flall=1, compute also wp' */
+static GEN
+ellwpnum_all(GEN e, GEN z, long flall, long prec)
+{
+  long toadd;
+  pari_sp av = avma, lim, av1;
+  GEN pi2, q, u, y, yp, u1, u2, qn;
+  ellred_t T;
+  int simple_case;
+
+  if (!get_periods(e, z, &T, prec)) pari_err_TYPE("ellwp",e);
+  if (!T.Z) return NULL;
+  prec = T.prec;
+
+  /* Now L,Z normalized to <1,tau>. Z in fund. domain of <1, tau> */
+  pi2 = Pi2n(1, prec);
+  q = expIxy(pi2, T.Tau, prec);
+  u = expIxy(pi2, T.Z, prec);
+  u1 = gsubsg(1,u);
+  u2 = gsqr(u1); /* (1-u)^2 = -4u sin^2(Pi Z) */
+  if (gequal0(u2)) return NULL; /* possible if loss of accuracy */
+  y = gdiv(u,u2); /* -1/4(sin^2(Pi Z)) */
+  if (T.abs_u_is_1) y = real_i(y);
+  simple_case = T.abs_u_is_1 && T.q_is_real;
+  y = gadd(mkfrac(gen_1, utoipos(12)), y);
+  yp = flall? gdiv(gaddsg(1,u), gmul(u1,u2)): NULL;
+  toadd = (long)ceil(get_toadd(T.Z));
+
+  av1 = avma; lim = stack_lim(av1,1); qn = q;
+  for(;;)
+  { /* y += u q^n [ 1/(1-q^n u)^2 + 1/(q^n-u)^2 ] - 2q^n /(1-q^n)^2 */
+    /* analogous formula for yp */
+    GEN yadd, ypadd = NULL;
+    GEN qnu = gmul(qn,u); /* q^n u */
+    GEN a = gsubsg(1,qnu);/* 1 - q^n u */
+    GEN a2 = gsqr(a);     /* (1 - q^n u)^2 */
+    if (yp) ypadd = gdiv(gaddsg(1,qnu),gmul(a,a2));
+    if (simple_case)
+    { /* conj(u) = 1/u: formula simplifies */
+      yadd = gdiv(u, a2);
+      yadd = gmul2n(real_i(yadd), 1);
+      if (yp) ypadd = gmul2n(real_i(ypadd), 1);
+    }
+    else
+    {
+      GEN b = gsub(qn,u);/* q^n - u */
+      GEN b2 = gsqr(b);  /* (q^n - u)^2 */
+      yadd = gmul(u, gadd(ginv(a2),ginv(b2)));
+      if (yp) ypadd = gadd(ypadd, gdiv(gadd(qn,u),gmul(b,b2)));
+    }
+    yadd = gsub(yadd, gmul2n(ginv(gsqr(gsubsg(1,qn))), 1));
+    y = gadd(y, gmul(qn,yadd));
+    if (yp) yp = gadd(yp, gmul(qn,ypadd));
+
+    qn = gmul(q,qn);
+    if (gexpo(qn) <= - prec2nbits(prec) - 5 - toadd) break;
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"ellwp");
+      gerepileall(av1, flall? 3: 2, &y, &qn, &yp);
+    }
+  }
+
+  u1 = gdiv(pi2, mulcxmI(T.W2));
+  u2 = gsqr(u1);
+  y = gmul(u2,y); /* y *= (2i pi / w2)^2 */
+  if (T.some_q_is_real && (T.some_z_is_real || T.some_z_is_pure_imag))
+    y = real_i(y);
+  if (yp)
+  {
+    yp = gmul(u, gmul(gmul(u1,u2),yp));/* yp *= u (2i pi / w2)^3 */
+    if (T.some_q_is_real && T.some_z_is_real) yp = real_i(yp);
+    y = mkvec2(y, gmul2n(yp,-1));
+  }
+  return gerepilecopy(av, y);
+}
+static GEN
+ellwpseries_aux(GEN c4, GEN c6, long v, long PRECDL)
+{
+  long i, k, l;
+  pari_sp av;
+  GEN t, res = cgetg(PRECDL+2,t_SER), *P = (GEN*)(res + 2);
+
+  res[1] = evalsigne(1) | _evalvalp(-2) | evalvarn(v);
+  if (!PRECDL) { setsigne(res,0); return res; }
+
+  for (i=1; i<PRECDL; i+=2) P[i]= gen_0;
+  switch(PRECDL)
+  {
+    default:P[6] = gdivgs(c6,6048);
+    case 6:
+    case 5: P[4] = gdivgs(c4, 240);
+    case 4:
+    case 3: P[2] = gen_0;
+    case 2:
+    case 1: P[0] = gen_1;
+    case 0: break;
+  }
+  if (PRECDL <= 8) return res;
+  av = avma;
+  P[8] = gerepileupto(av, gdivgs(gsqr(P[4]), 3));
+  for (k=5; (k<<1) < PRECDL; k++)
+  {
+    av = avma;
+    t = gmul(P[4], P[(k-2)<<1]);
+    for (l=3; (l<<1) < k; l++) t = gadd(t, gmul(P[l<<1], P[(k-l)<<1]));
+    t = gmul2n(t, 1);
+    if ((k & 1) == 0) t = gadd(gsqr(P[k]), t);
+    if (k % 3 == 2)
+      t = gdivgs(gmulsg(3, t), (k-3)*(2*k+1));
+    else /* same value, more efficient */
+      t = gdivgs(t, ((k-3)*(2*k+1)) / 3);
+    P[k<<1] = gerepileupto(av, t);
+  }
+  return res;
+}
+
+static int
+get_c4c6(GEN w, GEN *c4, GEN *c6, long prec)
+{
+  if (typ(w) == t_VEC) switch(lg(w))
+  {
+    case 17:
+      *c4 = ell_get_c4(w);
+      *c6 = ell_get_c6(w);
+      return 1;
+    case 3:
+    {
+      ellred_t T;
+      if (!get_periods(w,NULL,&T, prec)) break;
+      *c4 = _elleisnum(&T, 4);
+      *c6 = gneg(_elleisnum(&T, 6));
+      return 1;
+    }
+  }
+  *c4 = *c6 = NULL;
+  return 0;
+}
+
+GEN
+ellwpseries(GEN e, long v, long PRECDL)
+{
+  GEN c4, c6;
+  checkell(e);
+  c4 = ell_get_c4(e);
+  c6 = ell_get_c6(e); return ellwpseries_aux(c4,c6,v,PRECDL);
+}
+
+GEN
+ellwp(GEN w, GEN z, long prec)
+{ return ellwp0(w,z,0,prec); }
+
+GEN
+ellwp0(GEN w, GEN z, long flag, long prec)
+{
+  pari_sp av = avma;
+  GEN y;
+
+  if (flag && flag != 1) pari_err_FLAG("ellwp");
+  if (!z) z = pol_x(0);
+  y = toser_i(z);
+  if (y)
+  {
+    long vy = varn(y), v = valp(y);
+    GEN P, Q, c4,c6;
+    if (!get_c4c6(w,&c4,&c6,prec)) pari_err_TYPE("ellwp",w);
+    if (v <= 0) pari_err(e_IMPL,"ellwp(t_SER) away from 0");
+    if (gequal0(y)) {
+      avma = av;
+      if (!flag) return zeroser(vy, -2*v);
+      retmkvec2(zeroser(vy, -2*v), zeroser(vy, -3*v));
+    }
+    P = ellwpseries_aux(c4,c6, vy, lg(y)-2);
+    Q = gsubst(P, varn(P), y);
+    if (!flag)
+      return gerepileupto(av, Q);
+    else
+    {
+      GEN R = mkvec2(Q, gdiv(derivser(Q), derivser(y)));
+      return gerepilecopy(av, R);
+    }
+  }
+  y = ellwpnum_all(w,z,flag,prec);
+  if (!y) pari_err_DOMAIN("ellwp", "argument","=", gen_0,z);
+  return gerepileupto(av, y);
+}
+
+GEN
+ellzeta(GEN w, GEN z, long prec0)
+{
+  long prec;
+  pari_sp av = avma;
+  GEN pi2, q, u, v, y, et = NULL;
+  ellred_t T;
+  int simple_case;
+
+  if (!z) z = pol_x(0);
+  y = toser_i(z);
+  if (y)
+  {
+    long vy = varn(y), v = valp(y);
+    GEN P, Q, c4,c6;
+    if (!get_c4c6(w,&c4,&c6,prec0)) pari_err_TYPE("ellzeta",w);
+    if (v <= 0) pari_err(e_IMPL,"ellzeta(t_SER) away from 0");
+    if (gequal0(y)) { avma = av; return zeroser(vy, -v); }
+    P = ellwpseries_aux(c4,c6, vy, lg(y)-2);
+    P = integser(gneg(P)); /* \zeta' = - \wp*/
+    Q = gsubst(P, varn(P), y);
+    return gerepileupto(av, Q);
+  }
+  if (!get_periods(w, z, &T, prec0)) pari_err_TYPE("ellzeta", w);
+  if (!T.Z) pari_err_DOMAIN("ellzeta", "z", "=", gen_0, z);
+  prec = T.prec;
+  if (signe(T.x) || signe(T.y)) et = eta_correction(&T, _elleta(&T));
+
+  pi2 = Pi2n(1, prec);
+  q = expIxy(pi2, T.Tau, prec);
+  u = expIxy(pi2, T.Z, prec);
+  simple_case = T.abs_u_is_1 && T.q_is_real;
+
+  y = mulcxI(gmul(trueE2(T.Tau,prec), gmul(T.Z,divrs(pi2,-12))));
+  v = gadd(ghalf, ginv(gsubgs(u, 1)));
+  if (T.abs_u_is_1) gel(v,1) = gen_0; /*v = (u+1)/2(u-1), pure imaginary*/
+  y = gadd(y, v);
+
+  if (!simple_case)/* otherwise |u|=1 and all terms in sum are 0 */
+  {
+    long toadd = (long)ceil(get_toadd(T.Z));
+    pari_sp av1 = avma, lim = stack_lim(av1,1);
+    GEN qn;
+    for (qn = q;;)
+    { /* y += sum q^n ( u/(u*q^n - 1) + 1/(u - q^n) ) */
+      GEN p1 = gadd(gdiv(u,gsubgs(gmul(qn,u),1)), ginv(gsub(u,qn)));
+      y = gadd(y, gmul(qn,p1));
+      qn = gmul(q,qn);
+      if (gexpo(qn) <= - prec2nbits(prec) - 5 - toadd) break;
+      if (low_stack(lim, stack_lim(av1,1)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"ellzeta");
+        gerepileall(av1,2, &y,&qn);
+      }
+    }
+  }
+  y = mulcxI(gmul(gdiv(pi2,T.W2), y));
+  if (et) y = gadd(y,et);
+  if (T.some_q_is_real)
+  {
+    if (T.some_z_is_real)
+      y = real_i(y);
+    else if (T.some_z_is_pure_imag)
+      gel(y,1) = gen_0;
+  }
+  return gerepilecopy(av, y);
+}
+
+/* if flag=0, return ellsigma, otherwise return log(ellsigma) */
+GEN
+ellsigma(GEN w, GEN z, long flag, long prec0)
+{
+  long toadd, prec, n;
+  pari_sp av = avma, lim, av1;
+  GEN zinit, pi, pi2, q, q8, qn2, qn, y, y1, uinv, et, etnew;
+  GEN u, uhalf, urn, urninv;
+  ellred_t T;
+
+  if (flag < 0 || flag > 1) pari_err_FLAG("ellsigma");
+
+  if (!z) z = pol_x(0);
+  y = toser_i(z);
+  if (y)
+  {
+    long vy = varn(y), v = valp(y);
+    GEN P, Q, c4,c6;
+    if (!get_c4c6(w,&c4,&c6,prec0)) pari_err_TYPE("ellsigma",w);
+    if (v <= 0) pari_err_IMPL("ellsigma(t_SER) away from 0");
+    if (flag) pari_err_TYPE("log(ellsigma)",y);
+    if (gequal0(y)) { avma = av; return zeroser(vy, -v); }
+    P = ellwpseries_aux(c4,c6, vy, lg(y)-2);
+    P = integser(gneg(P)); /* \zeta' = - \wp*/
+    /* (log \sigma)' = \zeta; remove log-singularity first */
+    P = integser(gsub(P, monomial(gen_1,-1,vy)));
+    P = gexp(P, prec0);
+    setvalp(P, valp(P)+1);
+    Q = gsubst(P, varn(P), y);
+    return gerepileupto(av, Q);
+  }
+  if (!get_periods(w, z, &T, prec0)) pari_err_TYPE("ellsigma",w);
+  if (!T.Z)
+  {
+    if (!flag) return gen_0;
+    pari_err_DOMAIN("log(ellsigma)", "argument","=",gen_0,z);
+  }
+  prec = T.prec;
+  pi2 = Pi2n(1,prec);
+  pi  = mppi(prec);
+
+  toadd = (long)ceil(fabs( get_toadd(T.Z) ));
+  uhalf = expIxy(pi, T.Z, prec); /* exp(i Pi Z) */
+  u = gsqr(uhalf);
+  q8 = expIxy(gmul2n(pi2,-3), T.Tau, prec);
+  q = gpowgs(q8,8);
+  u = gneg_i(u); uinv = ginv(u);
+  y = gen_0;
+  av1 = avma; lim = stack_lim(av1,1);
+  qn = q; qn2 = gen_1;
+  urn = uhalf; urninv = ginv(uhalf);
+  for(n=0;;n++)
+  {
+    y = gadd(y,gmul(qn2,gsub(urn,urninv)));
+    qn2 = gmul(qn,qn2);
+    if (gexpo(qn2) + n*toadd <= - prec2nbits(prec) - 5) break;
+    qn  = gmul(q,qn);
+    urn = gmul(urn,u);
+    urninv = gmul(urninv,uinv);
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"ellsigma");
+      gerepileall(av1,5, &y,&qn,&qn2,&urn,&urninv);
+    }
+  }
+  y = gmul(gmul(y,q8),
+           gdiv(mulcxmI(T.W2), gmul(pi2,gpowgs(trueeta(T.Tau,prec),3))));
+
+  et = _elleta(&T);
+  etnew = eta_correction(&T, et);
+  zinit = gmul(T.Z,T.W2);
+  etnew = gmul(etnew, gadd(zinit,
+                           gmul2n(gadd(gmul(T.x,T.W1), gmul(T.y,T.W2)),-1)));
+  if (mpodd(T.x) || mpodd(T.y)) etnew = gadd(etnew, mulcxI(pi));
+  y1 = gadd(etnew, gmul2n(gmul(gmul(T.Z,zinit),gel(et,2)),-1));
+  if (flag)
+  {
+    y = gadd(y1, glog(y,prec));
+    if (T.some_q_is_real && T.some_z_is_real)
+    { /* y = log(some real number): im(y) is 0 or Pi */
+      if (gexpo(imag_i(y)) < 1) y = real_i(y);
+    }
+  }
+  else
+  {
+    y = gmul(y, gexp(y1,prec));
+    if (T.some_q_is_real)
+    {
+      if (T.some_z_is_real)
+        y = real_i(y);
+      else if (T.some_z_is_pure_imag)
+        gel(y,1) = gen_0;
+    }
+  }
+  return gerepilecopy(av, y);
+}
+
+GEN
+pointell(GEN e, GEN z, long prec)
+{
+  pari_sp av = avma;
+  GEN v;
+
+  checkell(e);
+  v = ellwpnum_all(e,z,1,prec);
+  if (!v) { avma = av; return ellinf(); }
+  gel(v,1) = gsub(gel(v,1), gdivgs(ell_get_b2(e),12));
+  gel(v,2) = gsub(gel(v,2), gmul2n(ellLHS0(e,gel(v,1)),-1));
+  return gerepilecopy(av, v);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                 Tate's algorithm e (cf Anvers IV)              **/
+/**               Kodaira types, global minimal model              **/
+/**                                                                **/
+/********************************************************************/
+
+/* Given an integral elliptic curve in ellinit form, and a prime p, returns the
+  type of the fiber at p of the Neron model, as well as the change of variables
+  in the form [f, kod, v, c].
+
+  * The integer f is the conductor's exponent.
+
+  * The integer kod is the Kodaira type using the following notation:
+    II , III , IV  -->  2, 3, 4
+    I0  -->  1
+    Inu --> 4+nu for nu > 0
+  A '*' negates the code (e.g I* --> -2)
+
+  * v is a quadruple [u, r, s, t] yielding a minimal model
+
+  * c is the Tamagawa number.
+
+  Uses Tate's algorithm (Anvers IV). Given the remarks at the bottom of
+  page 46, the "long" algorithm is used for p = 2,3 only. */
+static GEN
+localred_result(long f, long kod, long c, GEN v)
+{
+  GEN z = cgetg(5, t_VEC);
+  gel(z,1) = stoi(f);
+  gel(z,2) = stoi(kod);
+  gel(z,3) = gcopy(v);
+  gel(z,4) = stoi(c); return z;
+}
+static GEN
+localredbug(GEN p, const char *s)
+{
+  if (BPSW_psp(p)) pari_err_BUG(s);
+  pari_err_PRIME("localred",p);
+  return NULL; /* not reached */
+}
+
+/* v_p( denom(j(E)) ) >= 0 */
+static long
+j_pval(GEN E, GEN p) { return Z_pval(Q_denom(ell_get_j(E)), p); }
+
+#if 0
+/* Here p > 3. e assumed integral, return v_p(N). Simplified version of
+ * localred_p */
+static long
+localred_p_get_f(GEN e, GEN p)
+{
+  long nuj, nuD;
+  GEN D = ell_get_disc(e);
+  nuj = j_pval(e, p);
+  nuD = Z_pval(D, p);
+  if (nuj == 0) return (nuD % 12)? 2 : 0;
+  return (nuD - nuj) % 12 ? 2: 1;
+}
+#endif
+/* Here p > 3. e assumed integral, minim = 1 if we only want a minimal model */
+static GEN
+localred_p(GEN e, GEN p)
+{
+  long k, f, kod, c, nuj, nuD;
+  GEN p2, v, tri, c4, c6, D = ell_get_disc(e);
+
+  c4 = ell_get_c4(e);
+  c6 = ell_get_c6(e);
+  nuj = j_pval(e, p);
+  nuD = Z_pval(D, p);
+  k = (nuD - nuj) / 12;
+  if (k <= 0) v = init_ch();
+  else
+  { /* model not minimal */
+    GEN pk = powiu(p,k), p2k = sqri(pk), p4k = sqri(p2k), p6k = mulii(p4k,p2k);
+    GEN r, s, t;
+
+    s = negi(ell_get_a1(e));
+    if (mpodd(s)) s = addii(s, pk);
+    s = shifti(s, -1);
+
+    r = subii(ell_get_a2(e), mulii(s, addii(ell_get_a1(e), s)));
+    switch(umodiu(r, 3))
+    {
+      default: break; /* 0 */
+      case 2: r = addii(r, p2k); break;
+      case 1: r = subii(r, p2k); break;
+    }
+    r = negi( diviuexact(r, 3) );
+
+    t = negi(ellLHS0_i(e,r));
+    if (mpodd(t)) t = addii(t, mulii(pk, p2k));
+    t = shifti(t, -1);
+
+    v = mkvec4(pk,r,s,t);
+    nuD -= 12 * k;
+    c4 = diviiexact(c4, p4k);
+    c6 = diviiexact(c6, p6k);
+    D = diviiexact(D, sqri(p6k));
+  }
+
+  if (nuj > 0) switch(nuD - nuj)
+  {
+    case 0: f = 1; kod = 4+nuj; /* Inu */
+      switch(kronecker(negi(c6),p))
+      {
+        case  1: c = nuD; break;
+        case -1: c = odd(nuD)? 1: 2; break;
+        default: return localredbug(p,"localred (p | c6)");
+      }
+      break;
+    case 6:
+    {
+      GEN d = Fp_red(diviiexact(D, powiu(p, 6+nuj)), p);
+      if (nuj & 1) d = Fp_mul(d, diviiexact(c6, powiu(p,3)), p);
+      f = 2; kod = -4-nuj; c = 3 + kronecker(d, p); /* Inu* */
+      break;
+    }
+    default: return localredbug(p,"localred (nu_D - nu_j != 0,6)");
+  }
+  else switch(nuD)
+  {
+    case  0: f = 0; kod = 1; c = 1; break; /* I0, regular */
+    case  2: f = 2; kod = 2; c = 1; break; /* II   */
+    case  3: f = 2; kod = 3; c = 2; break; /* III  */
+    case  4: f = 2; kod = 4; /* IV   */
+      c = 2 + krosi(-6,p) * kronecker(diviiexact(c6,sqri(p)), p);
+      break;
+    case  6: f = 2; kod = -1; /* I0*  */
+      p2 = sqri(p);
+      /* x^3 - 3c4/p^2 x - 2c6/p^3 */
+      tri = mkpoln(4, gen_1, gen_0,
+                            negi(mului(3, diviiexact(c4, p2))),
+                            negi(shifti(diviiexact(c6, mulii(p2,p)), 1)));
+      c = 1 + FpX_nbroots(tri, p);
+      break;
+    case  8: f = 2; kod = -4; /* IV*  */
+      c = 2 + krosi(-6,p) * kronecker(diviiexact(c6, sqri(sqri(p))), p);
+      break;
+    case  9: f = 2; kod = -3; c = 2; break; /* III* */
+    case 10: f = 2; kod = -2; c = 1; break; /* II*  */
+    default: return localredbug(p,"localred");
+  }
+  return localred_result(f, kod, c, v);
+}
+
+/* return a_{ k,l } in Tate's notation, pl = p^l */
+static ulong
+aux(GEN ak, ulong q, ulong pl)
+{
+  return umodiu(ak, q) / pl;
+}
+
+static ulong
+aux2(GEN ak, ulong p, GEN pl)
+{
+  pari_sp av = avma;
+  ulong res = umodiu(diviiexact(ak, pl), p);
+  avma = av; return res;
+}
+
+/* number of distinct roots of X^3 + aX^2 + bX + c modulo p = 2 or 3
+ * assume a,b,c in {0, 1} [ p = 2 ] or {0, 1, 2} [ p = 3 ]
+ * if there's a multiple root, put it in *mult */
+static long
+numroots3(long a, long b, long c, long p, long *mult)
+{
+  if (p == 2)
+  {
+    if ((c + a * b) & 1) return 3;
+    *mult = b; return (a + b) & 1 ? 2 : 1;
+  }
+  /* p = 3 */
+  if (!a) { *mult = -c; return b ? 3 : 1; }
+  *mult = a * b;
+  if (b == 2)
+    return (a + c) == 3 ? 2 : 3;
+  else
+    return c ? 3 : 2;
+}
+
+/* same for aX^2 +bX + c */
+static long
+numroots2(long a, long b, long c, long p, long *mult)
+{
+  if (p == 2) { *mult = c; return b & 1 ? 2 : 1; }
+  /* p = 3 */
+  *mult = a * b; return (b * b - a * c) % 3 ? 2 : 1;
+}
+
+/* p = 2 or 3 */
+static GEN
+localred_23(GEN e, long p)
+{
+  long c, nu, nuD, r, s, t;
+  long theroot, p2, p3, p4, p5, p6, a21, a42, a63, a32, a64;
+  GEN v;
+
+  nuD = Z_lval(ell_get_disc(e), (ulong)p);
+  v = init_ch();
+  if (p == 2) { p2 = 4; p3 = 8;  p4 = 16; p5 = 32; p6 = 64;}
+  else        { p2 = 9; p3 = 27; p4 = 81; p5 =243; p6 =729; }
+
+  for (;;)
+  {
+    if (!nuD) return localred_result(0, 1, 1, v);
+        /* I0   */
+    if (umodiu(ell_get_b2(e), p)) /* p \nmid b2 */
+    {
+      if (umodiu(negi(ell_get_c6(e)), p == 2 ? 8 : 3) == 1)
+        c = nuD;
+      else
+        c = 2 - (nuD & 1);
+      return localred_result(1, 4 + nuD, c, v);
+    }
+        /* Inu  */
+    if (p == 2)
+    {
+      r = umodiu(ell_get_a4(e), 2);
+      s = umodiu(ell_get_a2(e), 2);
+      t = umodiu(ell_get_a6(e), 2);
+      if (r) { t = (s + t) & 1; s = (s + 1) & 1; }
+    }
+    else /* p == 3 */
+    {
+      r = - umodiu(ell_get_b6(e), 3);
+      s = umodiu(ell_get_a1(e), 3);
+      t = umodiu(ell_get_a3(e), 3);
+      if (s) { t  = (t + r*s) % 3; if (t < 0) t += 3; }
+    }
+    /* p | (a1, a2, a3, a4, a6) */
+    if (r || s || t) E_compose_rst(&v, &e, stoi(r), stoi(s), stoi(t));
+    if (umodiu(ell_get_a6(e), p2))
+      return localred_result(nuD, 2, 1, v);
+        /* II   */
+    if (umodiu(ell_get_b8(e), p3))
+      return localred_result(nuD - 1, 3, 2, v);
+        /* III  */
+    if (umodiu(ell_get_b6(e), p3))
+    {
+      if (umodiu(ell_get_b6(e), (p==2)? 32: 27) == (ulong)p2)
+        c = 3;
+      else
+        c = 1;
+      return localred_result(nuD - 2, 4, c, v);
+    }
+        /* IV   */
+
+    if (umodiu(ell_get_a6(e), p3))
+      E_compose_t(&v, &e, p == 2? gen_2: modis(ell_get_a3(e), 9));
+        /* p | a1, a2; p^2  | a3, a4; p^3 | a6 */
+    a21 = aux(ell_get_a2(e), p2, p);
+    a42 = aux(ell_get_a4(e), p3, p2);
+    a63 = aux(ell_get_a6(e), p4, p3);
+    switch (numroots3(a21, a42, a63, p, &theroot))
+    {
+      case 3:
+        c = a63 ? 1: 2;
+        if (p == 2)
+          c += ((a21 + a42 + a63) & 1);
+        else {
+          if (((1 + a21 + a42 + a63) % 3) == 0) c++;
+          if (((1 - a21 + a42 - a63) % 3) == 0) c++;
+        }
+        return localred_result(nuD - 4, -1, c, v);
+      case 2: /* I0*  */
+      { /* compute nu */
+        GEN pk, pk1, p2k;
+        long al, be, ga;
+        if (theroot) E_compose_r(&v, &e, stoi(theroot * p));
+            /* p | a1; p^2  | a2, a3; p^3 | a4; p^4 | a6 */
+        nu = 1;
+        pk  = utoipos(p2);
+        p2k = utoipos(p4);
+        for(;;)
+        {
+          be =  aux2(ell_get_a3(e), p, pk);
+          ga = -aux2(ell_get_a6(e), p, p2k);
+          al = 1;
+          if (numroots2(al, be, ga, p, &theroot) == 2) break;
+          if (theroot) E_compose_t(&v, &e, mulsi(theroot,pk));
+          pk1 = pk;
+          pk  = mului(p, pk);
+          p2k = mului(p, p2k); nu++;
+
+          al = a21;
+          be = aux2(ell_get_a4(e), p, pk);
+          ga = aux2(ell_get_a6(e), p, p2k);
+          if (numroots2(al, be, ga, p, &theroot) == 2) break;
+          if (theroot) E_compose_r(&v, &e, mulsi(theroot, pk1));
+          p2k = mului(p, p2k); nu++;
+        }
+        if (p == 2)
+          c = 4 - 2 * (ga & 1);
+        else
+          c = 3 + kross(be * be - al * ga, 3);
+        return localred_result(nuD - 4 - nu, -4 - nu, c, v);
+      }
+      case 1: /* Inu* */
+        if (theroot) E_compose_r(&v, &e, stoi(theroot*p));
+            /* p | a1; p^2  | a2, a3; p^3 | a4; p^4 | a6 */
+        a32 = aux(ell_get_a3(e), p3, p2);
+        a64 = aux(ell_get_a6(e), p5, p4);
+        if (numroots2(1, a32, -a64, p, &theroot) == 2)
+        {
+          if (p == 2)
+            c = 3 - 2 * a64;
+          else
+            c = 2 + kross(a32 * a32 + a64, 3);
+          return localred_result(nuD - 6, -4, c, v);
+        }
+            /* IV*  */
+        if (theroot) E_compose_t(&v, &e, stoi(theroot*p2));
+            /* p | a1; p^2 | a2; p^3 | a3, a4; p^5 | a6 */
+        if (umodiu(ell_get_a4(e), p4))
+          return localred_result(nuD - 7, -3, 2, v);
+            /* III* */
+
+        if (umodiu(ell_get_a6(e), p6))
+          return localred_result(nuD - 8, -2, 1, v);
+            /* II*  */
+        E_compose_u(&v, &e, utoipos(p)); /* not minimal */
+        nuD -= 12;
+    }
+  }
+}
+
+static GEN
+localred(GEN e, GEN p)
+{
+  if (cmpiu(p, 3) > 0) /* p != 2,3 */
+    return localred_p(e,p);
+  else
+  {
+    long l = itos(p);
+    if (l < 2) pari_err_PRIME("localred",p);
+    return localred_23(e, l);
+  }
+}
+
+GEN
+elllocalred(GEN e, GEN p)
+{
+  pari_sp av = avma;
+  checkell_Q(e);
+  if (typ(ell_get_disc(e)) != t_INT)
+    pari_err_TYPE("elllocalred [not an integral curve]",e);
+  if (typ(p) != t_INT) pari_err_TYPE("elllocalred [prime]",p);
+  if (signe(p) <= 0) pari_err_PRIME("elllocalred",p);
+  return gerepileupto(av, localred(e, p));
+}
+
+/* Return an integral model for e / Q. Set v = NULL (already integral)
+ * or the variable change [u,0,0,0], u = 1/t, t > 1 integer making e integral */
+static GEN
+ellintegralmodel(GEN e, GEN *pv)
+{
+  GEN a = cgetg(6,t_VEC), t, u, L;
+  long i, l, k;
+
+  L = cgetg(1, t_VEC);
+  for (i = 1; i < 6; i++)
+  {
+    GEN c = gel(e,i);
+    gel(a,i) = c;
+    switch(typ(c))
+    {
+      case t_INT: break;
+      case t_FRAC: /* partial factorization */
+        L = shallowconcat(L, gel(Z_factor_limit(gel(c,2), 0),1));
+        break;
+      default: pari_err_TYPE("ellintegralmodel [not a rational curve]",e);
+    }
+  }
+  /* a = [a1, a2, a3, a4, a6] */
+  l = lg(L); if (l == 1) { if (pv) *pv = NULL; return e; }
+  L = ZV_sort_uniq(L);
+  l = lg(L);
+
+  t = gen_1;
+  for (k = 1; k < l; k++)
+  {
+    GEN p = gel(L,k);
+    long n = 0, m;
+    for (i = 1; i < 6; i++)
+      if (!gequal0(gel(a,i)))
+      {
+        long r = (i == 5)? 6: i; /* a5 is missing */
+        m = r * n + Q_pval(gel(a,i), p);
+        while (m < 0) { n++; m += r; }
+      }
+    t = mulii(t, powiu(p, n));
+  }
+  u = ginv(t);
+  if (pv) *pv = mkvec4(u,gen_0,gen_0,gen_0);
+  return coordch_u(e, u);
+}
+
+/* FIXME: export ? */
+static ulong
+Mod32(GEN x) {
+  long s = signe(x);
+  ulong m;
+  if (!s) return 0;
+  m = mod32(x); if (!m) return m;
+  if (s < 0) m = 32 - m;
+  return m;
+}
+#define Mod16(x) Mod32(x)&15
+#define Mod2(x) Mod32(x)&1
+
+/* structure to hold incremental computation of standard minimal model/Q */
+typedef struct {
+  long a1; /*{0,1}*/
+  long a2; /*{-1,0,1}*/
+  long a3; /*{0,1}*/
+  long b2; /* centermod(-c6, 12), in [-5,6] */
+  GEN u, u2, u3, u4, u6;
+  GEN a4, a6, b4, b6, b8, c4, c6, D;
+} ellmin_t;
+
+/* u from [u,r,s,t] */
+static void
+min_set_u(ellmin_t *M, GEN u)
+{
+  M->u = u;
+  if (is_pm1(u))
+    M->u2 = M->u3 = M->u4 = M->u6 = gen_1;
+  else
+  {
+    M->u2 = sqri(u);
+    M->u3 = mulii(M->u2, u);
+    M->u4 = sqri(M->u2);
+    M->u6 = sqri(M->u3);
+  }
+}
+/* E = original curve */
+static void
+min_set_c(ellmin_t *M, GEN E)
+{
+  GEN c4 = ell_get_c4(E), c6 = ell_get_c6(E);
+  if (!is_pm1(M->u4)) {
+    c4 = diviiexact(c4, M->u4);
+    c6 = diviiexact(c6, M->u6);
+  }
+  M->c4 = c4;
+  M->c6 = c6;
+}
+static void
+min_set_D(ellmin_t *M, GEN E)
+{
+  GEN D = ell_get_disc(E);
+  if (!is_pm1(M->u6)) D = diviiexact(D, sqri(M->u6));
+  M->D = D;
+}
+static void
+min_set_b(ellmin_t *M)
+{
+  long b2 = Fl_center(12 - umodiu(M->c6,12), 12, 6);
+  long b22 = b2*b2; /* in [0,36] */
+  M->b2 = b2;
+  M->b4 = diviuexact(subui(b22, M->c4), 24);
+  M->b6 = diviuexact(subii(mulsi(b2, subiu(mului(36,M->b4),b22)), M->c6), 216);
+}
+static void
+min_set_a(ellmin_t *M)
+{
+  long a1, a2, a3, a13, b2 = M->b2;
+  GEN b4 = M->b4, b6 = M->b6;
+  if (odd(b2))
+  {
+    a1 = 1;
+    a2 = (b2 - 1) >> 2;
+  }
+  else
+  {
+    a1 = 0;
+    a2 = b2 >> 2;
+  }
+  M->a1 = a1;
+  M->a2 = a2;
+  M->a3 = a3 = Mod2(b6)? 1: 0;
+  a13 = a1 & a3; /* a1 * a3 */
+  M->a4 = shifti(subiu(b4, a13), -1);
+  M->a6 = shifti(subiu(b6, a3), -2);
+}
+static GEN
+min_to_ell(ellmin_t *M, GEN E)
+{
+  GEN b8, y = obj_init(15, 8);
+  long a11, a13;
+  gel(y,1) = M->a1? gen_1: gen_0;
+  gel(y,2) = stoi(M->a2);
+  gel(y,3) = M->a3? gen_1: gen_0;
+  gel(y,4) = M->a4;
+  gel(y,5) = M->a6;
+  gel(y,6) = stoi(M->b2);
+  gel(y,7) = M->b4;
+  gel(y,8) = M->b6;
+  a11 = M->a1;
+  a13 = M->a1 & M->a3;
+  b8 = subii(addii(mului(a11,M->a6), mulis(M->b6, M->a2)),
+             mulii(M->a4, addiu(M->a4,a13)));
+  gel(y,9) = b8; /* a1^2 a6 + 4a6 a2 + a2 a3^2 - a4(a4 + a1 a3) */
+  gel(y,10)= M->c4;
+  gel(y,11)= M->c6;
+  gel(y,12)= M->D;
+  gel(y,13)= gel(E,13);
+  gel(y,14)= gel(E,14);
+  gel(y,15)= gel(E,15);
+  return y;
+}
+static GEN
+min_get_v(ellmin_t *M, GEN E)
+{
+  GEN r, s, t;
+  r = diviuexact(subii(mulis(M->u2,M->b2), ell_get_b2(E)), 12);
+  s = shifti(subii(M->a1? M->u: gen_0, ell_get_a1(E)), -1);
+  t = shifti(subii(M->a3? M->u3: gen_0, ellLHS0(E,r)), -1);
+  return mkvec4(M->u,r,s,t);
+}
+
+static long
+F2_card(ulong a1, ulong a2, ulong a3, ulong a4, ulong a6)
+{
+  long N = 1; /* oo */
+  if (!a3) N ++; /* x = 0, y=0 or 1 */
+  else if (!a6) N += 2; /* x = 0, y arbitrary */
+  if ((a3 ^ a1) == 0) N++; /* x = 1, y = 0 or 1 */
+  else if (a2 ^ a4 ^ a6) N += 2; /* x = 1, y arbitrary */
+  return N;
+}
+static long
+F3_card(ulong b2, ulong b4, ulong b6)
+{
+  ulong Po = 1+2*b4, Pe = b2+b6;
+  /* kro(x,3)+1 = (x+1)%3, N = 4 + sum(kro) = 1+ sum(1+kro) */
+  return 1+(b6+1)%3+(Po+Pe+1)%3+(2*Po+Pe+1)%3;
+}
+static long
+cardmod2(GEN e)
+{ /* solve y(1 + a1x + a3) = x (1 + a2 + a4) + a6 */
+  ulong a1 = Rg_to_F2(ell_get_a1(e));
+  ulong a2 = Rg_to_F2(ell_get_a2(e));
+  ulong a3 = Rg_to_F2(ell_get_a3(e));
+  ulong a4 = Rg_to_F2(ell_get_a4(e));
+  ulong a6 = Rg_to_F2(ell_get_a6(e));
+  return F2_card(a1,a2,a3,a4,a6);
+}
+static long
+cardmod3(GEN e)
+{
+  ulong b2 = Rg_to_Fl(ell_get_b2(e), 3);
+  ulong b4 = Rg_to_Fl(ell_get_b4(e), 3);
+  ulong b6 = Rg_to_Fl(ell_get_b6(e), 3);
+  return F3_card(b2,b4,b6);
+}
+
+/* return v_p(u), where [u,r,s,t] is the variable change to minimal model */
+static long
+get_vu_p_small(GEN E, ulong p, long *pv6, long *pvD)
+{
+  GEN c6 = ell_get_c6(E), D = ell_get_disc(E);
+  long d, v6, vD = Z_lval(D,p);
+  if (!signe(c6))
+  {
+    d = vD / 12;
+    if (d)
+    {
+      if (p == 2)
+      {
+        GEN c4 = ell_get_c4(E);
+        long a = Mod16( shifti(c4, -4*d) );
+        if (a) d--;
+      }
+      if (d) vD -= 12*d; /* non minimal model */
+    }
+    v6 = 12; /* +oo */
+  }
+  else
+  {
+    v6 = Z_lval(c6,p);
+    d = minss(2*v6, vD) / 12;
+    if (d) {
+      if (p == 2) {
+        GEN c4 = ell_get_c4(E);
+        long a = Mod16( shifti(c4, -4*d) );
+        long b = Mod32( shifti(c6, -6*d) );
+        if ((b & 3) != 3 && (a || (b && b!=8))) d--;
+      } else if (p == 3) {
+        if (v6 == 6*d+2) d--;
+      }
+      if (d) { v6 -= 6*d; vD -= 12*d; } /* non minimal model */
+    }
+  }
+  *pv6 = v6; *pvD = vD; return d;
+}
+
+static ulong
+ZtoF2(GEN x) { return (ulong)mpodd(x); }
+
+/* complete local reduction at 2, u = 2^d */
+static void
+min_set_2(ellmin_t *M, GEN E, long d)
+{
+  min_set_u(M, int2n(d));
+  min_set_c(M, E);
+  min_set_b(M);
+  min_set_a(M);
+}
+/* local reduction at 3, u = 3^d, don't compute the a_i */
+static void
+min_set_3(ellmin_t *M, GEN E, long d)
+{
+  min_set_u(M, powuu(3, d));
+  min_set_c(M, E);
+  min_set_b(M);
+}
+
+static long
+is_minimal_ap_small(GEN E, ulong p, int *good_red)
+{
+  long vc6, vD, d = get_vu_p_small(E, p, &vc6, &vD);
+  if (vD) /* bad reduction */
+  {
+    GEN c6;
+    long s;
+    *good_red = 0;
+    if (vc6) return 0;
+    c6 = ell_get_c6(E);
+    if (d) c6 = diviiexact(c6, powuu(p, 6*d));
+    s = kroiu(c6,p);
+    if ((p & 3) == 3) s = -s;
+    return s;
+  }
+  *good_red = 1;
+  if (p == 2)
+  {
+    ellmin_t M;
+    if (!d) return 3 - cardmod2(E);
+    min_set_2(&M, E, d);
+    return 3 - F2_card(M.a1, M.a2 & 1, M.a3, ZtoF2(M.a4), ZtoF2(M.a6));
+  }
+  else if (p == 3)
+  {
+    ellmin_t M;
+    if (!d) return 4 - cardmod3(E);
+    min_set_3(&M, E, d);
+    return 4 - F3_card(M.b2, umodiu(M.b4,3), umodiu(M.b6,3));
+  }
+  else
+  {
+    ellmin_t M;
+    GEN a4, a6, pp = utoipos(p);
+    min_set_u(&M, powuu(p,d));
+    min_set_c(&M, E);
+    c4c6_to_a4a6(M.c4, M.c6, pp, &a4,&a6);
+    return itos( subui(p+1, Fp_ellcard(a4, a6, pp)) );
+  }
+}
+
+static GEN
+is_minimal_ap(GEN E, GEN p, int *good_red)
+{
+  GEN a4,a6, c4, c6, D;
+  long vc6, vD, d;
+  if (lgefint(p) == 3) return stoi( is_minimal_ap_small(E, p[2], good_red) );
+  c6 = ell_get_c6(E);
+  D = ell_get_disc(E);
+  vc6 = Z_pval(c6,p); vD = Z_pval(D,p);
+  d = minss(2*vc6, vD) / 12;
+  if (d) { vc6 -= 6*d; vD -= 12*d; } /* non minimal model */
+  if (vD) /* bad reduction */
+  {
+    long s;
+    *good_red = 0;
+    if (vc6) return gen_0;
+    if (d) c6 = diviiexact(c6, powiu(p, 6*d));
+    s = kronecker(c6,p);
+    if (mod4(p) == 3) s = -s;
+    return s < 0? gen_m1: gen_1;
+  }
+  *good_red = 1;
+  c4 = ell_get_c4(E);
+  if (d)
+  {
+    GEN u2 = powiu(p, 2*d), u4 = sqri(u2), u6 = mulii(u2,u4);
+    c4 = diviiexact(c4, u4);
+    c6 = diviiexact(c6, u6);
+  }
+  c4c6_to_a4a6(c4, c6, p, &a4,&a6);
+  return subii(addiu(p,1), Fp_ellcard(a4, a6, p));
+}
+
+/* E/Q, integral model, Laska-Kraus-Connell algorithm */
+static GEN
+get_u(GEN E, GEN *pc4c6P, GEN P)
+{
+  pari_sp av;
+  GEN c4, c6, g, u, D, c4c6P;
+  long l, k;
+
+  D = ell_get_disc(E);
+  c4 = ell_get_c4(E);
+  c6 = ell_get_c6(E);
+  if (!P) P = gel(Z_factor(gcdii(c4,c6)),1); /* primes dividing gcd(c4,c6) */
+  l = lg(P);
+  c4c6P = vectrunc_init(l); settyp(c4c6P,t_COL);
+  av = avma;
+  g = gcdii(sqri(c6), D);
+  u = gen_1;
+  for (k = 1; k < l; k++)
+  {
+    GEN p = gel(P, k);
+    long vg = Z_pval(g, p), d = vg / 12, r = vg % 12;
+    if (!d) { vectrunc_append(c4c6P, p); continue; }
+    switch(itou_or_0(p))
+    {
+      case 2:
+      {
+        long a, b;
+        a = Mod16( shifti(c4, -4*d) );
+        b = Mod32( shifti(c6, -6*d) );
+        if ((b & 3) != 3 && (a || (b && b!=8))) { d--; r += 12; }
+        break;
+      }
+      case 3:
+        if (Z_lval(c6,3) == 6*d+2) { d--; r += 12; }
+        break;
+    }
+    if (r) vectrunc_append(c4c6P, p);
+    if (d) u = mulii(u, powiu(p, d));
+  }
+  *pc4c6P = c4c6P;
+  return gerepileuptoint(av, u);
+}
+
+/* update Q_MINIMALMODEL entry in E, but don't update type-specific data on
+ * ellminimalmodel(E) */
+static GEN
+ellminimalmodel_i(GEN E, GEN *ptv)
+{
+  pari_sp av = avma;
+  GEN S, y, e, v, v0, u;
+  GEN c4c6P;
+  ellmin_t M;
+  if ((S = obj_check(E, Q_MINIMALMODEL)))
+  {
+    if (lg(S) != 2)
+    {
+      E = gel(S,3);
+      v = gel(S,2);
+    }
+    else
+      v = init_ch();
+    if (ptv) *ptv = v;
+    return gcopy(E);
+  }
+  e = ellintegralmodel(E, &v0);
+  u = get_u(e, &c4c6P, NULL);
+  min_set_u(&M, u);
+  min_set_c(&M, e);
+  min_set_D(&M, e);
+  min_set_b(&M);
+  min_set_a(&M);
+  y = min_to_ell(&M, e);
+  v = min_get_v(&M, e);
+  if (v0) { gcomposev(&v0, v); v = v0; }
+  if (is_trivial_change(v))
+    S = mkvec(c4c6P);
+  else
+    S = mkvec3(c4c6P, v, y);
+  S = gclone(S);
+  y = gerepilecopy(av, y);
+  obj_insert_shallow(E, Q_MINIMALMODEL, S);
+  *ptv = lg(S) == 2? init_ch(): gel(S,2);
+  return y;
+}
+GEN
+ellminimalmodel(GEN E, GEN *ptv)
+{
+  GEN S, y, v;
+  checkell_Q(E);
+  y = ellminimalmodel_i(E, &v);
+  if (!is_trivial_change(v)) ch_Q(y, E, v);
+  if (ptv) *ptv = gcopy(v);
+  S = obj_check(E, Q_MINIMALMODEL);
+  obj_insert(y, Q_MINIMALMODEL, mkvec(gel(S,1)));
+  return y;
+}
+
+/* Reduction of a rational curve E to its standard minimal model, don't
+ * update type-dependant components.
+ * Set v = [u, r, s, t] = change of variable E -> minimal model, with u > 0
+ * Set gr = [N, [u,r,s,t], c, fa, L], where
+ *   N = arithmetic conductor of E
+ *   c = product of the local Tamagawa numbers cp
+ *   fa = factorization of N
+ *   L = list of localred(E,p) for p | N.
+ * Return standard minimal model (a1,a3 = 0 or 1, a2 = -1, 0 or 1) */
+static GEN
+ellglobalred_all(GEN e, GEN *pgr, GEN *pv)
+{
+  long k, l, iN;
+  GEN S, E, c, L, P, NP, NE, D;
+
+  E = ellminimalmodel_i(e, pv);
+  S = obj_check(e, Q_MINIMALMODEL);
+  P = gel(S,1); l = lg(P); /* prime divisors of (c4,c6) */
+  D  = ell_get_disc(E);
+  for (k = 1; k < l; k++) (void)Z_pvalrem(D, gel(P,k), &D);
+  if (!is_pm1(D)) P = ZV_sort( shallowconcat(P, gel(absi_factor(D),1)) );
+  l = lg(P); c = gen_1;
+  iN = 1;
+  NP = cgetg(l, t_COL);
+  NE = cgetg(l, t_COL);
+  L = cgetg(l, t_VEC);
+  for (k = 1; k < l; k++)
+  {
+    GEN p = gel(P,k), q = localred(E, p), ex = gel(q,1);
+    if (signe(ex))
+    {
+      gel(NP, iN) = p;
+      gel(NE, iN) = ex;
+      gel(L, iN) = q; iN++;
+      gel(q,3) = gen_0; /*delete variable change*/
+      c = mulii(c, gel(q,4));
+    }
+  }
+  setlg(L, iN);
+  setlg(NP, iN);
+  setlg(NE, iN);
+  *pgr = mkvec4(factorback2(NP,NE), c, mkmat2(NP,NE), L);
+  return E;
+}
+static GEN
+doellglobalred(GEN E)
+{
+  GEN v, gr;
+  E = ellglobalred_all(E, &gr, &v);
+  return gr;
+}
+static GEN
+ellglobalred_i(GEN E)
+{ return obj_checkbuild(E, Q_GLOBALRED, &doellglobalred); }
+GEN
+ellglobalred(GEN E)
+{
+  pari_sp av = avma;
+  GEN S, gr, v;
+  checkell_Q(E); gr = ellglobalred_i(E);
+  S = obj_check(E, Q_MINIMALMODEL);
+  v = (lg(S) == 2)? init_ch(): gel(S,2);
+  return gerepilecopy(av, mkvec5(gel(gr,1), v, gel(gr,2),gel(gr,3),gel(gr,4)));
+}
+
+static GEN doellrootno(GEN e);
+/* Return E = ellminimalmodel(e), but only update E[1..14].
+ * insert MINIMALMODEL, GLOBALRED, ROOTNO in both e (regular insertion)
+ * and E (shallow insert) */
+GEN
+ellanal_globalred(GEN e, GEN *ch)
+{
+  GEN E, S, v = NULL;
+  checkell_Q(e);
+  if (!(S = obj_check(e, Q_MINIMALMODEL)))
+  {
+    E = ellminimalmodel_i(e, &v);
+    S = obj_check(e, Q_MINIMALMODEL);
+    obj_insert_shallow(E, Q_MINIMALMODEL, mkvec(gel(S,1)));
+  }
+  else if (lg(S) == 2) /* trivial change */
+    E = e;
+  else
+  {
+    v = gel(S,2);
+    E = gcopy(gel(S,3));
+    obj_insert_shallow(E, Q_MINIMALMODEL, mkvec(gel(S,1)));
+  }
+  if (ch) *ch = v;
+  S = ellglobalred_i(e);
+  if (E != e) obj_insert_shallow(E, Q_GLOBALRED, S);
+  S = obj_check(e, Q_ROOTNO);
+  if (!S)
+  {
+    S = doellrootno(E);
+    obj_insert(e, Q_ROOTNO, S); /* insert in e */
+  }
+  if (E != e) obj_insert_shallow(E, Q_ROOTNO, S); /* ... and in E */
+  return E;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**           ROOT NUMBER (after Halberstadt at p = 2,3)           **/
+/**                                                                **/
+/********************************************************************/
+/* x a t_INT */
+static long
+val_aux(GEN x, long p, long pk, long *u)
+{
+  long v;
+  GEN z;
+  if (!signe(x)) { *u = 0; return 12; }
+  v = Z_lvalrem(x,p,&z);
+  *u = umodiu(z,pk); return v;
+}
+static void
+val_init(GEN e, long p, long pk,
+         long *v4, long *u, long *v6, long *v, long *vD, long *d1)
+{
+  GEN c4 = ell_get_c4(e), c6 = ell_get_c6(e), D = ell_get_disc(e);
+  pari_sp av = avma;
+  *v4 = val_aux(c4, p,pk, u);
+  *v6 = val_aux(c6, p,pk, v);
+  *vD = val_aux(D , p,pk, d1); avma = av;
+}
+
+static long
+kod_23(GEN e, long p)
+{
+  GEN S, nv;
+  if ((S = obj_check(e, Q_GLOBALRED)))
+  {
+    GEN NP = gmael(S,3,1), L = gel(S,4);
+    nv = equaliu(gel(NP,1), p)? gel(L,1): gel(L,2); /* localred(p) */
+  }
+  else
+    nv = localred_23(e, p);
+  return itos(gel(nv,2));
+}
+
+/* v(c4), v(c6), v(D) for minimal model, +oo is coded by 12 */
+static long
+neron_2(long v4, long v6, long vD, long kod)
+{
+  if (kod > 4) return 1;
+  switch(kod)
+  {
+    case 1: return (v6>0) ? 2 : 1;
+    case 2:
+      if (vD==4) return 1;
+      else
+      {
+        if (vD==7) return 3;
+        else return v4==4 ? 2 : 4;
+      }
+    case 3:
+      switch(vD)
+      {
+        case 6: return 3;
+        case 8: return 4;
+        case 9: return 5;
+        default: return v4==5 ? 2 : 1;
+      }
+    case 4: return v4>4 ? 2 : 1;
+    case -1:
+      switch(vD)
+      {
+        case 9: return 2;
+        case 10: return 4;
+        default: return v4>4 ? 3 : 1;
+      }
+    case -2:
+      switch(vD)
+      {
+        case 12: return 2;
+        case 14: return 3;
+        default: return 1;
+      }
+    case -3:
+      switch(vD)
+      {
+        case 12: return 2;
+        case 14: return 3;
+        case 15: return 4;
+        default: return 1;
+      }
+    case -4: return v6==7 ? 2 : 1;
+    case -5: return (v6==7 || v4==6) ? 2 : 1;
+    case -6:
+      switch(vD)
+      {
+        case 12: return 2;
+        case 13: return 3;
+        default: return v4==6 ? 2 : 1;
+      }
+    case -7: return (vD==12 || v4==6) ? 2 : 1;
+    default: return v4==6 ? 2 : 1;
+  }
+}
+/* p = 3; v(c4), v(c6), v(D) for minimal model, +oo is coded by 12 */
+static long
+neron_3(long v4, long v6, long vD, long kod)
+{
+  if (labs(kod) > 4) return 1;
+  switch(kod)
+  {
+    case -1: case 1: return v4&1 ? 2 : 1;
+    case -3: case 3: return (2*v6>vD+3) ? 2 : 1;
+    case -4: case 2:
+      switch (vD%6)
+      {
+        case 4: return 3;
+        case 5: return 4;
+        default: return v6%3==1 ? 2 : 1;
+      }
+    default: /* kod = -2 et 4 */
+      switch (vD%6)
+      {
+        case 0: return 2;
+        case 1: return 3;
+        default: return 1;
+      }
+  }
+}
+
+static long
+ellrootno_2(GEN e)
+{
+  long n2, kod, u, v, x1, y1, D1, vD, v4, v6;
+  long d = get_vu_p_small(e, 2, &v6, &vD);
+
+  if (!vD) return 1;
+  if (d) { /* not minimal */
+    ellmin_t M;
+    min_set_2(&M, e, d);
+    e = min_to_ell(&M, e);
+  }
+  val_init(e, 2,64,&v4,&u, &v6,&v, &vD,&D1);
+  kod = kod_23(e,2);
+  n2 = neron_2(v4,v6,vD, kod);
+  if (kod>=5)
+  {
+    long a2, a3;
+    a2 = ZtoF2(ell_get_a2(e));
+    a3 = ZtoF2(ell_get_a3(e));
+    return odd(a2 + a3) ? 1 : -1;
+  }
+  if (kod<-9) return (n2==2) ? -kross(-1,v) : -1;
+  x1 = u+v+v;
+  switch(kod)
+  {
+    case 1: return 1;
+    case 2:
+      switch(n2)
+      {
+        case 1:
+          switch(v4)
+          {
+            case 4: return kross(-1,u);
+            case 5: return 1;
+            default: return -1;
+          }
+        case 2: return (v6==7) ? 1 : -1;
+        case 3: return (v%8==5 || (u*v)%8==5) ? 1 : -1;
+        case 4: if (v4>5) return kross(-1,v);
+          return (v4==5) ? -kross(-1,u) : -1;
+      }
+    case 3:
+      switch(n2)
+      {
+        case 1: return -kross(2,u*v);
+        case 2: return -kross(2,v);
+        case 3: y1 = (u - (v << (v6-5))) & 15;
+          return (y1==7 || y1==11) ? 1 : -1;
+        case 4: return (v%8==3 || (2*u+v)%8==7) ? 1 : -1;
+        case 5: return v6==8 ? kross(2,x1) : kross(-2,u);
+      }
+    case -1:
+      switch(n2)
+      {
+        case 1: return -kross(2,x1);
+        case 2: return (v%8==7) || (x1%32==11) ? 1 : -1;
+        case 3: return v4==6 ? 1 : -1;
+        case 4: if (v4>6) return kross(-1,v);
+          return v4==6 ? -kross(-1,u*v) : -1;
+      }
+    case -2: return n2==1 ? kross(-2,v) : kross(-1,v);
+    case -3:
+      switch(n2)
+      {
+        case 1: y1=(u-2*v)%64; if (y1<0) y1+=64;
+          return (y1==3) || (y1==19) ? 1 : -1;
+        case 2: return kross(2*kross(-1,u),v);
+        case 3: return -kross(-1,u)*kross(-2*kross(-1,u),u*v);
+        case 4: return v6==11 ? kross(-2,x1) : -kross(-2,u);
+      }
+    case -5:
+      if (n2==1) return x1%32==23 ? 1 : -1;
+      else return -kross(2,2*u+v);
+    case -6:
+      switch(n2)
+      {
+        case 1: return 1;
+        case 2: return v6==10 ? 1 : -1;
+        case 3: return (u%16==11) || ((u+4*v)%16==3) ? 1 : -1;
+      }
+    case -7:
+      if (n2==1) return 1;
+      else
+      {
+        y1 = (u + (v << (v6-8))) & 15;
+        if (v6==10) return (y1==9 || y1==13) ? 1 : -1;
+        else return (y1==9 || y1==5) ? 1 : -1;
+      }
+    case -8: return n2==2 ? kross(-1,v*D1) : -1;
+    case -9: return n2==2 ? -kross(-1,D1) : -1;
+    default: return -1;
+  }
+}
+
+static long
+ellrootno_3(GEN e)
+{
+  long n2, kod, u, v, D1, r6, K4, K6, vD, v4, v6;
+  long d = get_vu_p_small(e, 3, &v6, &vD);
+
+  if (!vD) return 1;
+  if (d) { /* not minimal */
+    ellmin_t M;
+    min_set_3(&M, e, d);
+    min_set_a(&M);
+    e = min_to_ell(&M, e);
+  }
+  val_init(e, 3,81, &v4,&u, &v6,&v, &vD,&D1);
+  kod = kod_23(e,3);
+  K6 = kross(v,3); if (kod>4) return K6;
+  n2 = neron_3(v4,v6,vD,kod);
+  r6 = v%9; K4 = kross(u,3);
+  switch(kod)
+  {
+    case 1: case 3: case -3: return 1;
+    case 2:
+      switch(n2)
+      {
+        case 1: return (r6==4 || r6>6) ? 1 : -1;
+        case 2: return -K4*K6;
+        case 3: return 1;
+        case 4: return -K6;
+      }
+    case 4:
+      switch(n2)
+      {
+        case 1: return K6*kross(D1,3);
+        case 2: return -K4;
+        case 3: return -K6;
+      }
+    case -2: return n2==2 ? 1 : K6;
+    case -4:
+      switch(n2)
+      {
+        case 1:
+          if (v4==4) return (r6==4 || r6==8) ? 1 : -1;
+          else return (r6==1 || r6==2) ? 1 : -1;
+        case 2: return -K6;
+        case 3: return (r6==2 || r6==7) ? 1 : -1;
+        case 4: return K6;
+      }
+    default: return -1;
+  }
+}
+
+/* p > 3. Don't assume that e is minimal or even integral at p */
+static long
+ellrootno_p(GEN e, GEN p)
+{
+  long nuj, nuD, nu;
+  GEN D = ell_get_disc(e);
+  long ep, z;
+
+  nuD = Q_pval(D, p);
+  if (!nuD) return 1;
+  nuj = j_pval(e, p);
+  nu = (nuD - nuj) % 12;
+  if (nu == 0)
+  {
+    GEN c6;
+    long d, vg;
+    if (!nuj) return 1; /* good reduction */
+   /* p || N */
+    c6 = ell_get_c6(e); /* != 0 */
+    vg = minss(2*Q_pval(c6, p), nuD);
+    d = vg / 12;
+    if (d)
+    {
+      GEN q = powiu(p,6*d);
+      c6 = (typ(c6) == t_INT)? diviiexact(c6, q): gdiv(c6, q);
+    }
+    if (typ(c6) != t_INT) c6 = Rg_to_Fp(c6,p);
+    /* c6 in minimal model */
+    return -kronecker(negi(c6), p);
+  }
+  if (nuj) return krosi(-1,p);
+  ep = 12 / ugcd(12, nu);
+  if (ep==4) z = 2; else z = (ep&1) ? 3 : 1;
+  return krosi(-z, p);
+}
+
+static GEN
+doellrootno(GEN e)
+{
+  GEN S, V, v, P;
+  long i, l, s = -1;
+  if ((S = obj_check(e, Q_GLOBALRED)))
+  {
+    GEN S2 = obj_check(e, Q_MINIMALMODEL);
+    if (lg(S2) != 2) e = gel(S2,3);
+  }
+  else
+  {
+    GEN E = ellglobalred_all(e, &S, &v);
+    obj_insert(e, Q_GLOBALRED, S);
+    e = E;
+  }
+  P = gmael(S,3,1); l = lg(P);
+  V = cgetg(l, t_VECSMALL);
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(P,i);
+    long t;
+    switch(itou_or_0(p))
+    {
+      case 2: t = ellrootno_2(e); break;
+      case 3: t = ellrootno_3(e); break;
+      default:t = ellrootno_p(e, p);
+    }
+    V[i] = t; s *= t;
+  }
+  return mkvec2(stoi(s), V);
+}
+long
+ellrootno_global(GEN e)
+{
+  pari_sp av = avma;
+  GEN S = obj_checkbuild(e, Q_ROOTNO, &doellrootno);
+  avma = av; return itos(gel(S,1));
+}
+
+/* local epsilon factor at p (over Q), including p=0 for the infinite place.
+ * Global if p==1 or NULL. */
+long
+ellrootno(GEN e, GEN p)
+{
+  pari_sp av = avma;
+  GEN S;
+  long s;
+  checkell_Q(e);
+  if (!p || isint1(p)) return ellrootno_global(e);
+  if (typ(p) != t_INT) pari_err_TYPE("ellrootno", p);
+  if (signe(p) < 0) pari_err_PRIME("ellrootno",p);
+  if (!signe(p)) return -1; /* local factor at infinity */
+  if ( (S = obj_check(e, Q_ROOTNO)) )
+  {
+    GEN T = obj_check(e, Q_GLOBALRED), NP = gmael(T,3,1);
+    long i, l = lg(NP);
+    for (i = 1; i < l; i++)
+    {
+      GEN q = gel(NP,i);
+      if (equalii(p, q)) { GEN V = gel(S,2); return V[i]; }
+    }
+    return 1;
+  }
+  switch(itou_or_0(p))
+  {
+    case 2:
+      e = ellintegralmodel(e, NULL);
+      s = ellrootno_2(e); break;
+    case 3:
+      e = ellintegralmodel(e, NULL);
+      s = ellrootno_3(e); break;
+    default:
+      s = ellrootno_p(e,p); break;
+  }
+  avma = av; return s;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       TRACE OF FROBENIUS                       **/
+/**                                                                **/
+/********************************************************************/
+
+/* assume e has good reduction mod p */
+static long
+ellap_small_goodred(int CM, GEN E, ulong p)
+{
+  ulong a4, a6;
+  if (p == 2) return 3 - cardmod2(E);
+  if (p == 3) return 4 - cardmod3(E);
+  Fl_ell_to_a4a6(E, p, &a4, &a6);
+  return CM? Fl_elltrace_CM(CM, a4, a6, p): Fl_elltrace(a4, a6, p);
+}
+
+static void
+checkell_int(GEN e)
+{
+  checkell_Q(e);
+  if (typ(ell_get_a1(e)) != t_INT ||
+      typ(ell_get_a2(e)) != t_INT ||
+      typ(ell_get_a3(e)) != t_INT ||
+      typ(ell_get_a4(e)) != t_INT ||
+      typ(ell_get_a6(e)) != t_INT) pari_err_TYPE("anellsmall [not an integral model]",e);
+}
+
+static int
+ell_get_CM(GEN e)
+{
+  GEN j = ell_get_j(e);
+  int CM = 0;
+  if (typ(j) == t_INT) switch(itos_or_0(j))
+  {
+    case 0:
+      if (!signe(j)) CM = -3;
+      break;
+    case 1728: CM = -4; break;
+    case -3375: CM = -7; break;
+    case  8000: CM = -8; break;
+    case 54000: CM = -12; break;
+    case -32768: CM = -11; break;
+    case 287496: CM = -16; break;
+    case -884736: CM = -19; break;
+    case -12288000: CM = -27; break;
+    case  16581375: CM = -28; break;
+    case -884736000: CM = -43; break;
+#ifdef LONG_IS_64BIT
+    case -147197952000: CM = -67; break;
+    case -262537412640768000: CM = -163; break;
+#endif
+  }
+  return CM;
+}
+GEN
+anellsmall(GEN e, long n0)
+{
+  pari_sp av;
+  ulong p, m, SQRTn, n = (ulong)n0;
+  GEN an, D;
+  int CM;
+
+  checkell_int(e);
+  if (n0 <= 0) return cgetg(1,t_VEC);
+  if (n >= LGBITS)
+    pari_err_IMPL( stack_sprintf("ellan for n >= %lu", LGBITS) );
+  SQRTn = (ulong)sqrt(n);
+  D = ell_get_disc(e);
+  CM = ell_get_CM(e);
+
+  an = cgetg(n+1,t_VECSMALL); an[1] = 1;
+  av = avma;
+  for (p=2; p <= n; p++) an[p] = LONG_MAX; /* not computed yet */
+  for (p=2; p<=n; p++)
+  {
+    long ap;
+    if (an[p] != LONG_MAX) continue; /* p not prime */
+    if (!umodiu(D,p)) /* p | D, bad reduction or non-minimal model */
+    {
+      int good_red;
+      ap = is_minimal_ap_small(e, p, &good_red);
+      if (good_red) goto GOOD_RED;
+      switch (ap) /* (-c6/p) */
+      {
+        case -1: { /* non-split */
+          ulong N = n/p;
+          for (m=1; m<=N; m++)
+            if (an[m] != LONG_MAX) an[m*p] = -an[m];
+          break;
+        }
+        case 0: /* additive */
+          for (m=p; m<=n; m+=p) an[m] = 0;
+          break;
+        case 1: { /* split */
+          ulong N = n/p;
+          for (m=1; m<=N; m++)
+            if (an[m] != LONG_MAX) an[m*p] = an[m];
+          break;
+        }
+      }
+    }
+    else /* good reduction */
+    {
+      ap = ellap_small_goodred(CM, e, p);
+GOOD_RED:
+      if (p <= SQRTn) {
+        ulong pk, oldpk = 1;
+        for (pk=p; pk <= n; oldpk=pk, pk *= p)
+        {
+          if (pk == p)
+            an[pk] = ap;
+          else
+            an[pk] = ap * an[oldpk] - p * an[oldpk/p];
+          for (m = n/pk; m > 1; m--)
+            if (an[m] != LONG_MAX && m%p) an[m*pk] = an[m] * an[pk];
+        }
+      } else {
+        an[p] = ap;
+        for (m = n/p; m > 1; m--)
+          if (an[m] != LONG_MAX) an[m*p] = ap * an[m];
+      }
+    }
+  }
+  avma = av; return an;
+}
+
+GEN
+anell(GEN e, long n0)
+{
+  GEN v = anellsmall(e, n0);
+  long i;
+  for (i = 1; i <= n0; i++) gel(v,i) = stoi(v[i]);
+  settyp(v, t_VEC); return v;
+}
+
+static GEN
+apk_good(GEN ap, GEN p, long e)
+{
+  GEN u, v, w;
+  long j;
+  if (e == 1) return ap;
+  u = ap;
+  w = subii(sqri(ap), p);
+  for (j=3; j<=e; j++)
+  {
+    v = u; u = w;
+    w = subii(mulii(ap,u), mulii(p,v));
+  }
+  return w;
+}
+
+GEN
+akell(GEN e, GEN n)
+{
+  long i, j, s;
+  pari_sp av = avma;
+  GEN fa, P, E, D, u, y;
+
+  checkell_int(e);
+  if (typ(n) != t_INT) pari_err_TYPE("akell",n);
+  if (signe(n)<= 0) return gen_0;
+  if (gequal1(n)) return gen_1;
+  D = ell_get_disc(e);
+  u = coprime_part(n, D);
+  y = gen_1;
+  s = 1;
+  if (!equalii(u, n))
+  { /* bad reduction at primes dividing n/u */
+    fa = Z_factor(diviiexact(n, u));
+    P = gel(fa,1);
+    E = gel(fa,2);
+    for (i=1; i<lg(P); i++)
+    {
+      GEN p = gel(P,i);
+      long ex = itos(gel(E,i));
+      int good_red;
+      GEN ap = is_minimal_ap(e,p,&good_red);
+      if (good_red) { y = mulii(y, apk_good(ap, p, ex)); continue; }
+      j = signe(ap);
+      if (!j) { avma = av; return gen_0; }
+      if (odd(ex) && j < 0) s = -s;
+    }
+  }
+  if (s < 0) y = negi(y);
+  fa = Z_factor(u);
+  P = gel(fa,1);
+  E = gel(fa,2);
+  for (i=1; i<lg(P); i++)
+  { /* good reduction */
+    GEN p = gel(P,i);
+    GEN ap = ellap(e,p);
+    y = mulii(y, apk_good(ap, p, itos(gel(E,i))));
+  }
+  return gerepileuptoint(av,y);
+}
+
+GEN
+ellQ_get_N(GEN e)
+{ GEN v = ellglobalred_i(e); return gel(v,1); }
+void
+ellQ_get_Nfa(GEN e, GEN *N, GEN *faN)
+{ GEN v = ellglobalred_i(e); *N = gel(v,1); *faN = gel(v,3); }
+
+GEN
+elllseries(GEN e, GEN s, GEN A, long prec)
+{
+  pari_sp av = avma, av1, lim;
+  ulong l, n;
+  long eps, flun;
+  GEN z, cg, v, cga, cgb, s2, K, gs, N;
+
+  if (!A) A = gen_1;
+  else
+  {
+    if (gsigne(A)<=0)
+      pari_err_DOMAIN("elllseries", "cut-off point", "<=", gen_0,A);
+    if (gcmpgs(A,1) < 0) A = ginv(A);
+  }
+  if (isint(s, &s) && signe(s) <= 0) { avma = av; return gen_0; }
+  flun = gequal1(A) && gequal1(s);
+  checkell_Q(e);
+  e = ellanal_globalred(e, NULL);
+  N = ellQ_get_N(e);
+  eps = ellrootno_global(e);
+  if (flun && eps < 0) { avma = av; return real_0(prec); }
+
+  gs = ggamma(s, prec);
+  cg = divrr(Pi2n(1, prec), gsqrt(N,prec));
+  cga = gmul(cg, A);
+  cgb = gdiv(cg, A);
+  l = (ulong)((prec2nbits_mul(prec, LOG2) +
+              fabs(gtodouble(real_i(s))-1.) * log(rtodbl(cga)))
+            / rtodbl(cgb) + 1);
+  if ((long)l < 1) l = 1;
+  v = anellsmall(e, minss(l,LGBITS-1));
+  s2 = K = NULL; /* gcc -Wall */
+  if (!flun) { s2 = gsubsg(2,s); K = gpow(cg, gsubgs(gmul2n(s,1),2),prec); }
+  z = gen_0;
+  av1 = avma; lim = stack_lim(av1,1);
+  for (n = 1; n <= l; n++)
+  {
+    GEN p1, an, gn = utoipos(n), ns;
+    an = ((ulong)n<LGBITS)? stoi(v[n]): akell(e,gn);
+    if (!signe(an)) continue;
+
+    ns = gpow(gn,s,prec);
+    p1 = gdiv(incgam0(s,mulur(n,cga),gs,prec), ns);
+    if (flun)
+      p1 = gmul2n(p1, 1);
+    else
+    {
+      GEN p2 = gdiv(gmul(gmul(K,ns), incgam(s2,mulur(n,cgb),prec)), sqru(n));
+      if (eps < 0) p2 = gneg_i(p2);
+      p1 = gadd(p1, p2);
+    }
+    z = gadd(z, gmul(p1, an));
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"lseriesell");
+      z = gerepilecopy(av1,z);
+    }
+  }
+  return gerepileupto(av, gdiv(z,gs));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       CANONICAL HEIGHT                         **/
+/**                                                                **/
+/********************************************************************/
+
+/* h' := h_oo(a) + 1/2 log(denom(a)) */
+static GEN
+hell(GEN e, GEN a, long prec)
+{
+  long n;
+  pari_sp av = avma;
+  GEN pi2 = Pi2n(1, prec);
+  GEN om = ellR_omega(e,prec), w1 = gel(om,1), w2 = gel(om,2);
+  GEN p1, y, z, q, pi2surw, qn, ps;
+
+  pi2surw = gdiv(pi2, w1);
+  z = gmul(real_i(zell(e,a,prec)), pi2surw);
+  q = real_i( expIxy(mpneg(pi2surw), w2, prec) );
+  y = mpsin(z); qn = gen_1; ps = gneg_i(q);
+  for (n = 3; ; n += 2)
+  {
+    qn = gmul(qn, ps);
+    ps = gmul(ps, q);
+    y = gadd(y, gmul(qn, gsin(gmulsg(n,z),prec)));
+    if (gexpo(qn) < -prec2nbits(prec)) break;
+  }
+  p1 = gmul(gsqr(gdiv(gmul2n(y,1), d_ellLHS(e,a))), pi2surw);
+  p1 = gsqr(gsqr(gdiv(p1, gsqr(gsqr(denom(gel(a,1)))))));
+  p1 = gdiv(gmul(p1,q), ell_get_disc(e));
+  p1 = gmul2n(glog(gabs(p1,prec),prec), -5);
+  return gerepileupto(av, gneg(p1));
+}
+
+static GEN
+Q_numer(GEN x) { return typ(x) == t_INT? x: gel(x,1); }
+
+/* h' := h_oo(x) + 1/2 log(denom(x)) */
+static GEN
+hells(GEN e, GEN Q, long prec)
+{
+  GEN b2 = ell_get_b2(e);
+  GEN b4 = ell_get_b4(e);
+  GEN b6 = ell_get_b6(e);
+  GEN b8 = ell_get_b8(e);
+  GEN x = gel(Q,1), w, z, t, mu, b42, b62;
+  long n, lim;
+
+  mu = gmul2n(glog(Q_numer(x),prec),-1);
+  t = ginv(gtofp(x, prec));
+  b42 = gmul2n(b4,1);
+  b62 = gmul2n(b6,1);
+  lim = 15 + prec2nbits(prec);
+  for (n = 3; n < lim; n += 2)
+  {
+    /* 4 + b2 t + 2b4 t^2 + b6 t^3 */
+    w = gmul(t, gaddsg(4, gmul(t, gadd(b2, gmul(t, gadd(b42, gmul(t, b6)))))));
+    /* 1 - (b4 t^2 + 2b6 t^3 + b8 t^4) */
+    z = gsubsg(1, gmul(gsqr(t), gadd(b4, gmul(t, gadd(b62, gmul(t, b8))))));
+    mu = gadd(mu, gmul2n(glog(z,prec), -n));
+    t = gdiv(w, z);
+  }
+  return mu;
+}
+
+static GEN
+hell2(GEN e, GEN x, long prec)
+{
+  GEN e3, ro, r;
+  pari_sp av = avma;
+
+  if (ell_is_inf(x)) return gen_0;
+  ro= ellR_roots(e, prec);
+  e3 = (ellR_get_sign(e) < 0)? gel(ro,1): gel(ro,3);
+  r = addis(gfloor(e3),-1);
+  e = coordch_r(e, r);
+  x = ellchangepoint_r(x, r);
+  return gerepileupto(av, hells(e, x, prec));
+}
+
+/* one root of X^2 - t X + c */
+static GEN
+quad_root(GEN t, GEN c, long prec)
+{
+  return gmul2n(gadd(t, gsqrt(gsub(gsqr(t), gmul2n(c,2)),prec)), -1);
+}
+
+/* exp( h_oo(z) ), assume z on neutral component.
+ * If flag, return exp(4 h_oo(z)) instead */
+static GEN
+exphellagm(GEN e, GEN z, int flag, long prec)
+{
+  GEN x_a, a, b, e1, r, V = cgetg(1, t_VEC), x = gel(z,1);
+  long n, ex = 5-prec2nbits(prec), p = prec+EXTRAPRECWORD;
+
+  if (typ(x) == t_REAL && realprec(x) < p) x = gprec_w(x, p);
+  e1 = ellR_root(e, p);
+  {
+    GEN ab = ellR_ab(e, p);
+    a = gel(ab, 1);
+    b = gel(ab, 2);
+  }
+  x = gsub(x, e1);
+  x = quad_root(gadd(x,b), gmul(a,x), prec);
+
+  x_a = gsub(x, a);
+  if (gsigne(a) > 0)
+  {
+    GEN a0 = a;
+    x = gsub(x, b);
+    a = gneg(b);
+    b = gsub(a0, b);
+  }
+  a = gsqrt(gneg(a), prec);
+  b = gsqrt(gneg(b), prec);
+  /* compute height on isogenous curve E1 ~ E0 */
+  for(n=0; ; n++)
+  {
+    GEN p1, p2, ab, a0 = a;
+    a = gmul2n(gadd(a0,b), -1);
+    r = gsub(a, a0);
+    if (gequal0(r) || gexpo(r) < ex) break;
+    ab = gmul(a0, b);
+    b = gsqrt(ab, prec);
+
+    p1 = gmul2n(gsub(x, ab), -1);
+    p2 = gsqr(a);
+    x = gadd(p1, gsqrt(gadd(gsqr(p1), gmul(x, p2)), prec));
+    V = shallowconcat(V, gadd(x, p2));
+  }
+  if (n) {
+    x = gel(V,n);
+    while (--n > 0) x = gdiv(gsqr(x), gel(V,n));
+  } else {
+    x = gadd(x, gsqr(a));
+  }
+  /* height on E1 is log(x)/2. Go back to E0 */
+  return flag? gsqr( gdiv(gsqr(x), x_a) )
+             : gdiv(x, sqrtr( mpabs(x_a) ));
+}
+/* is P \in E(R)^0, the neutral component ? */
+static int
+ellR_on_neutral(GEN E, GEN P, long prec)
+{
+  GEN x = gel(P,1), e1 = ellR_root(E, prec);
+  return gcmp(x, e1) >= 0;
+}
+
+/* exp( 4h_oo(z) ) */
+static GEN
+exp4hellagm(GEN E, GEN z, long prec)
+{
+  if (!ellR_on_neutral(E, z, prec))
+  {
+    GEN eh = exphellagm(E, elladd(E, z,z), 0, prec);
+    /* h_oo(2P) = 4h_oo(P) - log |2y + a1x + a3| */
+    return gmul(eh, gabs(d_ellLHS(E, z), prec));
+  }
+  return exphellagm(E, z, 1, prec);
+}
+
+GEN
+ellheightoo(GEN E, GEN z, long prec)
+{
+  pari_sp av = avma;
+  GEN h;
+  checkell_Q(E);
+  if (!ellR_on_neutral(E, z, prec))
+  {
+    GEN eh = exphellagm(E, elladd(E, z,z), 0, prec);
+    /* h_oo(2P) = 4h_oo(P) - log |2y + a1x + a3| */
+    h = gmul(eh, gabs(d_ellLHS(E, z), prec));
+  }
+  else
+    h = exphellagm(E, z, 1, prec);
+  return gerepileuptoleaf(av, gmul2n(mplog(h), -2));
+}
+
+GEN
+ellheight0(GEN e, GEN a, long flag, long prec)
+{
+  long i, tx = typ(a), lx;
+  pari_sp av = avma;
+  GEN Lp, x, y, z, phi2, psi2, psi3;
+  GEN v, S, b2, b4, b6, b8, a1, a2, a4, c4, D;
+
+  if (flag > 2 || flag < 0) pari_err_FLAG("ellheight");
+  checkell_Q(e); if (!is_matvec_t(tx)) pari_err_TYPE("ellheight",a);
+  lx = lg(a); if (lx==1) return cgetg(1,tx);
+  tx = typ(gel(a,1));
+  if ((S = obj_check(e, Q_MINIMALMODEL)))
+  { /* switch to minimal model if needed */
+    if (lg(S) != 2)
+    {
+      v = gel(S,2);
+      e = gel(S,3);
+      a = ellchangepoint(a, v);
+    }
+  }
+  else
+  {
+    e = ellminimalmodel_i(e, &v);
+    a = ellchangepoint(a, v);
+  }
+  if (is_matvec_t(tx))
+  {
+    z = cgetg(lx,tx);
+    for (i=1; i<lx; i++) gel(z,i) = ellheight0(e,gel(a,i),flag,prec);
+    return z;
+  }
+  if (ell_is_inf(a)) return gen_0;
+  if (!oncurve(e,a))
+    pari_err_DOMAIN("ellheight", "point", "not on", strtoGENstr("E"),a);
+  psi2 = Q_numer(d_ellLHS(e,a));
+  if (!signe(psi2)) { avma = av; return gen_0; }
+  switch(flag)
+  {
+    case 0:  z = hell2(e,a,prec); break; /* Tate 4^n */
+    case 1:  z = hell(e,a,prec);  break; /* Silverman's log(sigma) */
+    default:
+    {
+      GEN d = denom(gel(a,1));
+      z = exp4hellagm(e,a,prec); /* = exp(4h_oo(a)), Mestre's AGM */
+      if (!is_pm1(d)) z = gmul(z, sqri(d));
+      z = gmul2n(mplog(z), -2); break;
+    }
+  }
+  x = gel(a,1);
+  y = gel(a,2);
+  b2 = ell_get_b2(e);
+  b4 = ell_get_b4(e);
+  b6 = ell_get_b6(e);
+  b8 = ell_get_b8(e);
+  psi3 = Q_numer( /* b8 + 3x b6 + 3x^2 b4 + x^3 b2 + 3 x^4 */
+    poleval(mkvec5(b8, mului(3,b6), mului(3,b4), b2, utoipos(3)), x)
+  );
+  if (!signe(psi3)) { avma=av; return gen_0; }
+  a1 = ell_get_a1(e);
+  a2 = ell_get_a2(e);
+  a4 = ell_get_a4(e);
+  phi2 = Q_numer( /* a4 + 2a2 x + 3x^2 - y a1*/
+    poleval(mkvec3(gsub(a4,gmul(a1,y)), shifti(a2,1), utoipos(3)), x)
+  );
+  c4 = ell_get_c4(e);
+  D = ell_get_disc(e);
+  Lp = gel(Z_factor(gcdii(psi2,phi2)),1);
+  lx = lg(Lp);
+  for (i=1; i<lx; i++)
+  {
+    GEN p = gel(Lp,i);
+    long u, v, n, n2;
+    if (signe(remii(c4,p)))
+    { /* p \nmid c4 */
+      long N = Z_pval(D,p);
+      if (!N) continue;
+      n2 = Z_pval(psi2,p); n = n2<<1;
+      if (n > N) n = N;
+      u = n * ((N<<1) - n);
+      v = N << 3;
+    }
+    else
+    {
+      n2 = Z_pval(psi2, p);
+      n  = Z_pval(psi3, p);
+      if (n >= 3*n2) { u = n2; v = 3; } else { u = n; v = 8; }
+    }
+    /* z -= u log(p) / v */
+    z = gsub(z, divru(mulur(u, logr_abs(itor(p,prec))), v));
+  }
+  return gerepileupto(av, gmul2n(z, 1));
+}
+
+GEN
+ghell(GEN e, GEN a, long prec) { return ellheight0(e,a,2,prec); }
+
+GEN
+mathell(GEN e, GEN x, long prec)
+{
+  GEN y, h, pdiag;
+  long lx = lg(x),i,j,tx=typ(x);
+  pari_sp av = avma;
+
+  if (!is_vec_t(tx)) pari_err_TYPE("ellheightmatrix",x);
+  y = cgetg(lx,t_MAT); pdiag = new_chunk(lx);
+  for (i=1; i<lx; i++)
+  {
+    gel(pdiag,i) = ghell(e,gel(x,i),prec);
+    gel(y,i) = cgetg(lx,t_COL);
+  }
+  for (i=1; i<lx; i++)
+  {
+    gcoeff(y,i,i) = gel(pdiag,i);
+    for (j=i+1; j<lx; j++)
+    {
+      h = ghell(e, elladd(e,gel(x,i),gel(x,j)), prec);
+      h = gsub(h, gadd(gel(pdiag,i),gel(pdiag,j)));
+      gcoeff(y,j,i) = gcoeff(y,i,j) = gmul2n(h, -1);
+    }
+  }
+  return gerepilecopy(av,y);
+}
+
+static GEN
+bilhells(GEN e, GEN z1, GEN z2, GEN h2, long prec)
+{
+  long lz1=lg(z1), tx, i;
+  pari_sp av = avma;
+  GEN y,p1,p2;
+
+  if (lz1==1) return cgetg(1,typ(z1));
+
+  tx = typ(gel(z1,1));
+  if (!is_matvec_t(tx))
+  {
+    p1 = ghell(e, elladd(e,z1,z2),prec);
+    p2 = gadd(h2, ghell(e,z1,prec));
+    return gerepileupto(av, gmul2n(gsub(p1,p2), -1));
+  }
+  y = cgetg(lz1, typ(z1));
+  for (i=1; i<lz1; i++) gel(y,i) = bilhells(e,gel(z1,i),z2,h2,prec);
+  return y;
+}
+
+GEN
+bilhell(GEN e, GEN z1, GEN z2, long prec)
+{
+  GEN p1, h2;
+  long tz1 = typ(z1), tz2 = typ(z2);
+  pari_sp av = avma;
+
+  if (!is_matvec_t(tz1)) pari_err_TYPE("ellbil",z1);
+  if (!is_matvec_t(tz2)) pari_err_TYPE("ellbil",z2);
+  if (lg(z1)==1) return cgetg(1,tz1);
+  if (lg(z2)==1) return cgetg(1,tz2);
+
+  tz1 = typ(gel(z1,1));
+  tz2 = typ(gel(z2,1));
+  if (is_matvec_t(tz2))
+  {
+    if (is_matvec_t(tz1)) pari_err_TYPE("bilhell",z1);
+    p1 = z1; z1 = z2; z2 = p1;
+  }
+  h2 = ghell(e,z2,prec);
+  return gerepileupto(av, bilhells(e,z1,z2,h2,prec));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                    Modular Parametrization                     **/
+/**                                                                **/
+/********************************************************************/
+/* t*x^v (1 + O(x)), t != 0 */
+static GEN
+triv_ser(GEN t, long v)
+{
+  GEN s = cgetg(3,t_SER);
+  s[1] = evalsigne(1) | _evalvalp(v) | evalvarn(0);
+  gel(s,2) = t; return s;
+}
+
+GEN
+elltaniyama(GEN e, long prec)
+{
+  GEN x, w, c, d, X, C, b2, b4;
+  long n, m;
+  pari_sp av = avma;
+
+  checkell_Q(e);
+  if (prec < 0) pari_err_DOMAIN("elltaniyama","precision","<",gen_0,stoi(prec));
+  if (!prec) retmkvec2(triv_ser(gen_1,-2), triv_ser(gen_m1,-3));
+
+  x = cgetg(prec+3,t_SER);
+  x[1] = evalsigne(1) | _evalvalp(-2) | evalvarn(0);
+  d = ginv(gtoser(anell(e,prec+1), 0, prec)); setvalp(d,-1);
+  /* 2y(q) + a1x + a3 = d qx'(q). Solve for x(q),y(q):
+   * 4y^2 = 4x^3 + b2 x^2 + 2b4 x + b6 */
+  c = gsqr(d);
+  /* solve 4x^3 + b2 x^2 + 2b4 x + b6 = c (q x'(q))^2; c = 1/q^2 + O(1/q)
+   * Take derivative then divide by 2x':
+   *  b2 x + b4 = (1/2) (q c')(q x') + c q (q x')' - 6x^2.
+   * Write X[i] = coeff(x, q^i), C[i] = coeff(c, q^i), we obtain for all n
+   *  ((n+1)(n+2)-12) X[n+2] =  b2 X[n] + b4 delta_{n = 0}
+   *   + 6    \sum_{m = -1}^{n+1} X[m] X[n-m]
+   *   - (1/2)\sum_{m = -2}^{n+1} (n+m) m C[n-m]X[m].
+   * */
+  C = c+4;
+  X = x+4;
+  gel(X,-2) = gen_1;
+  gel(X,-1) = gmul2n(gel(C,-1), -1); /* n = -3, X[-1] = C[-1] / 2 */
+  b2 = ell_get_b2(e);
+  b4 = ell_get_b4(e);
+  for (n=-2; n <= prec-4; n++)
+  {
+    pari_sp av2 = avma;
+    GEN s1, s2, s3;
+    if (n != 2)
+    {
+      s3 = gmul(b2, gel(X,n));
+      if (!n) s3 = gadd(s3, b4);
+      s2 = gen_0;
+      for (m=-2; m<=n+1; m++)
+        if (m) s2 = gadd(s2, gmulsg(m*(n+m), gmul(gel(X,m),gel(C,n-m))));
+      s2 = gmul2n(s2,-1);
+      s1 = gen_0;
+      for (m=-1; m+m < n; m++) s1 = gadd(s1, gmul(gel(X,m),gel(X,n-m)));
+      s1 = gmul2n(s1, 1);
+      if (m+m==n) s1 = gadd(s1, gsqr(gel(X,m)));
+      /* ( (n+1)(n+2) - 12 ) X[n+2] = (6 s1 + s3 - s2) */
+      s1 = gdivgs(gsub(gadd(gmulsg(6,s1),s3),s2), (n+2)*(n+1)-12);
+    }
+    else
+    {
+      GEN b6 = ell_get_b6(e);
+      GEN U = cgetg(9, t_SER);
+      U[1] = evalsigne(1) | _evalvalp(-2) | evalvarn(0);
+      gel(U,2) = gel(x,2);
+      gel(U,3) = gel(x,3);
+      gel(U,4) = gel(x,4);
+      gel(U,5) = gel(x,5);
+      gel(U,6) = gel(x,6);
+      gel(U,7) = gel(x,7);
+      gel(U,8) = gen_0; /* defined mod q^5 */
+      /* write x = U + x_4 q^4 + O(q^5) and expand original equation */
+      w = derivser(U); setvalp(w,-2); /* q X' */
+      /* 4X^3 + b2 U^2 + 2b4 U + b6 */
+      s1 = gadd(b6, gmul(U, gadd(gmul2n(b4,1), gmul(U,gadd(b2,gmul2n(U,2))))));
+      /* s2 = (qX')^2 - (4X^3 + b2 U^2 + 2b4 U + b6) = 28 x_4 + O(q) */
+      s2 = gsub(gmul(c,gsqr(w)), s1);
+      s1 = signe(s2)? gdivgs(gel(s2,2), 28): gen_0; /* = x_4 */
+    }
+    gel(X,n+2) = gerepileupto(av2, s1);
+  }
+  w = gmul(d,derivser(x)); setvalp(w, valp(w)+1);
+  w = gsub(w, ellLHS0(e,x));
+  c = cgetg(3,t_VEC);
+  gel(c,1) = gcopy(x);
+  gel(c,2) = gmul2n(w,-1); return gerepileupto(av, c);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       TORSION POINTS (over Q)                  **/
+/**                                                                **/
+/********************************************************************/
+static int
+smaller_x(GEN p, GEN q)
+{
+  int s = absi_cmp(denom(p), denom(q));
+  return (s<0 || (s==0 && absi_cmp(numer(p),numer(q)) < 0));
+}
+
+/* best generator in cycle of length k */
+static GEN
+best_in_cycle(GEN e, GEN p, long k)
+{
+  GEN p0 = p,q = p;
+  long i;
+
+  for (i=2; i+i<k; i++)
+  {
+    q = elladd(e,q,p0);
+    if (ugcd(i,k)==1 && smaller_x(gel(q,1), gel(p,1))) p = q;
+  }
+  return (gsigne(d_ellLHS(e,p)) < 0)? ellneg_i(e,p): p;
+}
+
+/* <p,q> = E_tors, possibly NULL (= oo), p,q independent unless NULL
+ * order p = k, order q = 2 unless NULL */
+static GEN
+tors(GEN e, long k, GEN p, GEN q, GEN v)
+{
+  GEN r;
+  if (q)
+  {
+    long n = k>>1;
+    GEN p1, best = q, np = ellmul_Z(e,p,utoipos(n));
+    if (n % 2 && smaller_x(gel(np,1), gel(best,1))) best = np;
+    p1 = elladd(e,q,np);
+    if (smaller_x(gel(p1,1), gel(best,1))) q = p1;
+    else if (best == np) { p = elladd(e,p,q); q = np; }
+    p = best_in_cycle(e,p,k);
+    if (v)
+    {
+      p = ellchangepointinv(p,v);
+      q = ellchangepointinv(q,v);
+    }
+    r = cgetg(4,t_VEC);
+    gel(r,1) = utoipos(2*k);
+    gel(r,2) = mkvec2(utoipos(k), gen_2);
+    gel(r,3) = mkvec2copy(p, q);
+  }
+  else
+  {
+    if (p)
+    {
+      p = best_in_cycle(e,p,k);
+      if (v) p = ellchangepointinv(p,v);
+      r = cgetg(4,t_VEC);
+      gel(r,1) = utoipos(k);
+      gel(r,2) = mkvec( gel(r,1) );
+      gel(r,3) = mkvec( gcopy(p) );
+    }
+    else
+    {
+      r = cgetg(4,t_VEC);
+      gel(r,1) = gen_1;
+      gel(r,2) = cgetg(1,t_VEC);
+      gel(r,3) = cgetg(1,t_VEC);
+    }
+  }
+  return r;
+}
+
+static GEN
+doellff_get_o(GEN E)
+{
+  GEN G = ellgroup(E, NULL), d1 = gel(G,1);
+  return mkvec2(d1, Z_factor(d1));
+}
+GEN
+ellff_get_o(GEN E)
+{ return obj_checkbuild(E, FF_O, &doellff_get_o); };
+
+GEN
+elllog(GEN E, GEN a, GEN g, GEN o)
+{
+  pari_sp av = avma;
+  GEN fg, r;
+  checkell_Fq(E); checkellpt(a); checkellpt(g);
+  fg = ellff_get_field(E);
+  if (!o) o = ellff_get_o(E);
+  if (typ(fg)==t_FFELT)
+    r = FF_elllog(E, a, g, o);
+  else
+  {
+    GEN p = fg, e = ellff_get_a4a6(E);
+    GEN Pp = FpE_changepointinv(RgE_to_FpE(a,p), gel(e,3), p);
+    GEN Qp = FpE_changepointinv(RgE_to_FpE(g,p), gel(e,3), p);
+    r = FpE_log(Pp, Qp, o, gel(e,1), p);
+  }
+  return gerepileuptoint(av, r);
+}
+
+/* assume e is defined over Q (use Mazur's theorem) */
+static long
+_orderell(GEN E, GEN P)
+{
+  pari_sp av = avma;
+  GEN tmp, p, a4, dx, dy, d4, d6, D, Pp, Q;
+  forprime_t T;
+  ulong pp;
+  long k;
+  if (ell_is_inf(P)) return 1;
+
+  dx = Q_denom(gel(P,1));
+  dy = Q_denom(gel(P,2));
+  if (ell_is_integral(E)) /* integral model, try Nagell Lutz */
+    if (cmpiu(dx, 4) > 0 || cmpiu(dy, 8) > 0) return 0;
+
+  d4 = Q_denom(ell_get_c4(E));
+  d6 = Q_denom(ell_get_c6(E));
+  D = ell_get_disc (E);
+  /* choose not too small prime p dividing neither a coefficient of the
+     short Weierstrass form nor of P and leading to good reduction      */
+
+  u_forprime_init(&T, 100003, ULONG_MAX);
+  while ( (pp = u_forprime_next(&T)) )
+    if (Rg_to_Fl(d4, pp) && Rg_to_Fl(d6, pp) && Rg_to_Fl(D, pp)
+     && Rg_to_Fl(dx, pp) && Rg_to_Fl(dy, pp)) break;
+
+  /* transform E into short Weierstrass form Ep modulo p
+     and P to Pp on Ep */
+  p = utoipos(pp);
+  tmp = ell_to_a4a6_bc(E, p);
+  a4 = gel(tmp, 1);
+  Pp = FpE_changepointinv(RgV_to_FpV(P, p), gel(tmp,3), p);
+
+  /* check whether the order of Pp on Ep is <= 12 */
+  for (Q = FpE_dbl(Pp, a4, p), k = 2;
+       !ell_is_inf(Q) && k <= 12;
+       Q = FpE_add(Q, Pp, a4, p), k++) /* empty */;
+
+  if (k != 13)
+    /* check over Q; one could also run more tests modulo primes */
+    for (Q = elladd(E, P, P), k = 2;
+        !ell_is_inf(Q) && k <= 12;
+        Q = elladd(E, Q, P), k++) /* empty */;
+
+  avma = av;
+  return (k == 13 ? 0 : k);
+}
+
+GEN
+ellorder(GEN E, GEN P, GEN o)
+{
+  pari_sp av = avma;
+  GEN fg, r, E0 = E;
+  checkell(E); checkellpt(P);
+  if (ell_is_inf(P)) return gen_1;
+  if (ell_get_type(E)==t_ELL_Q)
+  {
+    GEN p = NULL;
+    if (is_rational_t(typ(gel(P,1))) && is_rational_t(typ(gel(P,2))))
+      return utoi( _orderell(E, P) );
+    if (RgV_is_FpV(P,&p) && p)
+    {
+      E = ellinit(E,p,0);
+      if (lg(E)==1) pari_err_IMPL("ellorder for curve with singular reduction");
+    }
+  }
+  checkell_Fq(E);
+  fg = ellff_get_field(E);
+  if (!o) o = ellff_get_o(E);
+  if (typ(fg)==t_FFELT)
+    r = FF_ellorder(E, P, o);
+  else
+  {
+    GEN p = fg, e = ellff_get_a4a6(E);
+    GEN Pp = FpE_changepointinv(RgE_to_FpE(P,p), gel(e,3), p);
+    r = FpE_order(Pp, o, gel(e,1), p);
+  }
+  if (E != E0) obj_free(E);
+  return gerepileuptoint(av, r);
+}
+
+GEN
+orderell(GEN e, GEN z) { return ellorder(e,z,NULL); }
+
+/* Using Lutz-Nagell */
+
+/* p in Z[X] of degree 3. Return vector of x/4, x integral root of p */
+static GEN
+ratroot(GEN p)
+{
+  GEN L, a, ld;
+  long i, t, v = ZX_valrem(p, &p);
+
+  if (v == 3) return ellinf();
+  if (v == 2) return mkvec2(gen_0, gmul2n(negi(gel(p,2)), -2));
+
+  L = cgetg(4,t_VEC); t = 1;
+  if (v == 1) gel(L,t++) = gen_0;
+  ld = divisors(gel(p,2));
+  for (i=1; i<lg(ld); i++)
+  {
+    a = gel(ld,i);
+    if (!signe(poleval(p,a))) gel(L,t++) = gmul2n(a, -2);
+    a = negi(a);
+    if (!signe(poleval(p,a))) gel(L,t++) = gmul2n(a, -2);
+  }
+  setlg(L,t); return L;
+}
+
+static int
+is_new_torsion(GEN e, GEN v, GEN p, long t2) {
+  GEN pk = p, pkprec = NULL;
+  long k,l;
+
+  for (k=2; k<=6; k++)
+  {
+    pk = elladd(e,pk,p); /* = [k] p */
+    if (ell_is_inf(pk)) return 1;
+
+    for (l=2; l<=t2; l++)
+      if (gequal(gel(pk,1),gmael(v,l,1))) return 1;
+
+    if (pkprec && k<=5)
+      if (gequal(gel(pk,1),gel(pkprec,1))) return 1;
+    pkprec=pk;
+  }
+  return 0;
+}
+
+static GEN
+nagelllutz(GEN e)
+{
+  GEN ld, pol, p1, lr, r, v, w2, w3;
+  long i, j, nlr, t, t2, k, k2;
+  pari_sp av=avma;
+
+  e = ellintegralmodel(e, &v);
+  pol = RgX_rescale(RHSpol(e,NULL), utoipos(4));
+  r = cgetg(17, t_VEC);
+  gel(r,1) = ellinf();
+  lr = ratroot(pol); nlr=lg(lr)-1;
+  for (t=1,i=1; i<=nlr; i++)
+  {
+    GEN x = gel(lr,i), y = gmul2n(gneg(ellLHS0(e,x)), -1);
+    gel(r,++t) = mkvec2(x, y);
+  }
+  ld = absi_factor(gmul2n(ell_get_disc(e), 4));
+  p1 = gel(ld,2); k = lg(p1);
+  for (i=1; i<k; i++) gel(p1,i) = shifti(gel(p1,i), -1);
+  ld = divisors(ld);
+  for (t2=t,j=1; j<lg(ld); j++)
+  {
+    GEN d = gel(ld,j);
+    lr = ratroot(ZX_Z_sub(pol, shifti(sqri(d), 6)));
+    for (i=1; i<lg(lr); i++)
+    {
+      GEN x = gel(lr,i), y = gmul2n(gsub(d, ellLHS0(e,x)), -1);
+      p1 = mkvec2(x, y);
+      if (is_new_torsion(e,r,p1,t2))
+      {
+        gel(r,++t) = p1;
+        gel(r,++t) = mkvec2(x, gsub(y, d));
+      }
+    }
+  }
+  if (t == 1) { avma = av; return tors(e,1,NULL,NULL,v); }
+
+  if (nlr < 3)
+  {
+    w2 = mkvec( utoipos(t) );
+    for (k=2; k<=t; k++)
+      if (_orderell(e,gel(r,k)) == t) break;
+    if (k>t) pari_err_BUG("elltors (bug1)");
+
+    w3 = mkvec( gel(r,k) );
+  }
+  else
+  {
+    if (t&3) pari_err_BUG("elltors (bug2)");
+    t2 = t>>1;
+    w2 = mkvec2(utoipos(t2), gen_2);
+    for (k=2; k<=t; k++)
+      if (_orderell(e,gel(r,k)) == t2) break;
+    if (k>t) pari_err_BUG("elltors (bug3)");
+
+    p1 = ellmul_Z(e,gel(r,k),utoipos(t>>2));
+    k2 = (!ell_is_inf(p1) && gequal(gel(r,2),p1))? 3: 2;
+    w3 = mkvec2(gel(r,k), gel(r,k2));
+  }
+  if (v)
+  {
+    gel(v,1) = ginv(gel(v,1));
+    w3 = ellchangepoint(w3,v);
+  }
+  return gerepilecopy(av, mkvec3(utoipos(t), w2,w3));
+}
+
+/* Using Doud's algorithm */
+
+/* finds a bound for #E_tor */
+static long
+torsbound(GEN e)
+{
+  GEN D = ell_get_disc(e);
+  pari_sp av = avma, av2;
+  long m, b, bold, nb;
+  forprime_t S;
+  int CM = ell_get_CM(e);
+  nb = expi(D) >> 3;
+  /* nb = number of primes to try ~ 1 prime every 8 bits in D */
+  b = bold = 5040; /* = 2^4 * 3^2 * 5 * 7 */
+  m = 0;
+  (void)u_forprime_init(&S, 3, ULONG_MAX);
+  av2 = avma;
+  while (m < nb || (b > 12 && b != 16))
+  {
+    ulong p = u_forprime_next(&S);
+    if (!p) pari_err_BUG("torsbound [ran out of primes]");
+    if (!umodiu(D, p)) continue;
+
+    b = ugcd(b, p+1 - ellap_small_goodred(CM, e, p));
+    avma = av2;
+    if (b == 1) break;
+    if (b == bold) m++; else { bold = b; m = 0; }
+  }
+  avma = av; return b;
+}
+
+static GEN
+myround(GEN x, long *e)
+{
+  GEN y = grndtoi(x,e);
+  if (*e > -5 && prec2nbits(gprecision(x)) < gexpo(y) - 10)
+    pari_err_PREC("elltors");
+  return y;
+}
+
+/* E the curve, w in C/Lambda ~ E of order n, returns q = pointell(w) as a
+ * rational point on the curve, or NULL if q is not rational. */
+static GEN
+torspnt(GEN E, GEN w, long n, long prec)
+{
+  GEN p = cgetg(3,t_VEC), q = pointell(E, w, prec);
+  long e;
+  gel(p,1) = gmul2n(myround(gmul2n(gel(q,1),2), &e),-2);
+  if (e > -5 || typ(gel(p,1)) == t_COMPLEX) return NULL;
+  gel(p,2) = gmul2n(myround(gmul2n(gel(q,2),3), &e),-3);
+  if (e > -5 || typ(gel(p,2)) == t_COMPLEX) return NULL;
+  return (oncurve(E,p)
+      && ell_is_inf(ellmul_Z(E,p,utoipos(n)))
+      && _orderell(E,p) == n)? p: NULL;
+}
+
+static GEN
+elltors_doud(GEN e)
+{
+  long B, i, ord, prec, k = 1;
+  pari_sp av=avma;
+  GEN v,w,w1,w22,w1j,w12,p,tor1,tor2;
+  GEN om;
+
+  e = ellintegralmodel(e, &v);
+  B = torsbound(e); /* #E_tor | B */
+  if (B == 1) { avma = av; return tors(e,1,NULL,NULL, v); }
+
+  /* prec >= size of sqrt(D) */
+  prec = DEFAULTPREC + ((lgefint(ell_get_disc(e))-2) >> 1);
+  om = ellR_omega(e, prec);
+  w1 = gel(om,1);
+  w22 = gmul2n(gel(om,2),-1);
+  if (B % 4)
+  { /* cyclic of order 1, p, 2p, p <= 5 */
+    p = NULL;
+    for (i=10; i>1; i--)
+    {
+      if (B%i != 0) continue;
+      w1j = gdivgs(w1,i);
+      p = torspnt(e,w1j,i,prec);
+      if (!p && i%2==0)
+      {
+        p = torspnt(e,gadd(w22,w1j),i,prec);
+        if (!p) p = torspnt(e,gadd(w22,gmul2n(w1j,1)),i,prec);
+      }
+      if (p) { k = i; break; }
+    }
+    return gerepileupto(av, tors(e,k,p,NULL, v));
+  }
+
+  ord = 0; tor1 = tor2 = NULL;
+  w12 = gmul2n(w1,-1);
+  if ((p = torspnt(e,w12,2,prec))) { tor1 = p; ord++; }
+  w = w22;
+  if ((p = torspnt(e,w,2,prec))) { tor2 = p; ord += 2; }
+  if (!ord)
+  {
+    w = gadd(w12,w22);
+    if ((p = torspnt(e,w,2,prec))) { tor2 = p; ord += 2; }
+  }
+  p = NULL;
+  switch(ord)
+  {
+    case 0: /* no point of order 2 */
+      for (i=9; i>1; i-=2)
+      {
+        if (B%i != 0) continue;
+        w1j = gdivgs(w1,i);
+        p = torspnt(e,w1j,i,prec);
+        if (p) { k = i; break; }
+      }
+      break;
+
+    case 1: /* 1 point of order 2: w1 / 2 */
+      for (i=12; i>2; i-=2)
+      {
+        if (B%i != 0) continue;
+        w1j = gdivgs(w1,i);
+        p = torspnt(e,w1j,i,prec);
+        if (!p && i%4==0) p = torspnt(e,gadd(w22,w1j),i,prec);
+        if (p) { k = i; break; }
+      }
+      if (!p) { p = tor1; k = 2; }
+      break;
+
+    case 2: /* 1 point of order 2: w = w2/2 or (w1+w2)/2 */
+      for (i=5; i>1; i-=2)
+      {
+        if (B%i != 0) continue;
+        w1j = gdivgs(w1,i);
+        p = torspnt(e,gadd(w,w1j),2*i,prec);
+        if (p) { k = 2*i; break; }
+      }
+      if (!p) { p = tor2; k = 2; }
+      tor2 = NULL; break;
+
+    case 3: /* 2 points of order 2: w1/2 and w2/2 */
+      for (i=8; i>2; i-=2)
+      {
+        if (B%(2*i) != 0) continue;
+        w1j = gdivgs(w1,i);
+        p = torspnt(e,w1j,i,prec);
+        if (p) { k = i; break; }
+      }
+      if (!p) { p = tor1; k = 2; }
+      break;
+  }
+  return gerepileupto(av, tors(e,k,p,tor2, v));
+}
+
+/* return a rational point of order pk = p^k on E, or NULL if E(Q)[k] = O.
+ * *fk is either NULL (pk = 4 or prime) or elldivpol(p^(k-1)).
+ * Set *fk to elldivpol(p^k) */
+static GEN
+tpoint(GEN E, long pk, GEN *fk)
+{
+  GEN f = elldivpol(E,pk,0), g = *fk, v;
+  long i, l;
+  *fk = f;
+  if (g) f = RgX_div(f, g);
+  v = nfrootsQ(f); l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    GEN x = gel(v,i);
+    GEN y = ellordinate_i(E,x,0);
+    if (lg(y) != 1) return mkvec2(x,gel(y,1));
+  }
+  return NULL;
+}
+/* return E(Q)[2] */
+static GEN
+t2points(GEN E, GEN *f2)
+{
+  long i, l;
+  GEN v;
+  *f2 = RHSpol(E,NULL);
+  v = nfrootsQ(*f2); l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    GEN x = gel(v,i);
+    GEN y = ellordinate_i(E,x,0);
+    if (lg(y) != 1) gel(v,i) = mkvec2(x,gel(y,1));
+  }
+  return v;
+}
+
+static GEN
+elltors_divpol(GEN E)
+{
+  GEN T2 = NULL, p, P, Q, v;
+  long v2, r2, B;
+
+  E = ellintegralmodel(E, &v);
+  B = torsbound(E); /* #E_tor | B */
+  if (B == 1) return tors(E,1,NULL,NULL, v);
+  v2 = vals(B); /* bound for v_2(point order) */
+  B >>= v2;
+  p = const_vec(9, NULL);
+  r2 = 0;
+  if (v2) {
+    GEN f;
+    T2 = t2points(E, &f);
+    switch(lg(T2)-1)
+    {
+      case 0:  v2 = 0; break;
+      case 1:  r2 = 1; if (v2 == 4) v2 = 3; break;
+      default: r2 = 2; v2--; break; /* 3 */
+    }
+    if (v2) gel(p,2) = gel(T2,1);
+    /* f = f_2 */
+    if (v2 > 1) { gel(p,4) = tpoint(E,4, &f); if (!gel(p,4)) v2 = 1; }
+    /* if (v2>1) now f = f4 */
+    if (v2 > 2) { gel(p,8) = tpoint(E,8, &f); if (!gel(p,8)) v2 = 2; }
+  }
+  B <<= v2;
+  if (B % 3 == 0) {
+    GEN f3 = NULL;
+    gel(p,3) = tpoint(E,3,&f3);
+    if (!gel(p,3)) B /= (B%9)? 3: 9;
+    if (gel(p,3) && B % 9 == 0)
+    {
+      gel(p,9) = tpoint(E,9,&f3);
+      if (!gel(p,9)) B /= 3;
+    }
+  }
+  if (B % 5 == 0) {
+    GEN junk = NULL;
+    gel(p,5) = tpoint(E,5,&junk);
+    if (!gel(p,5)) B /= 5;
+  }
+  if (B % 7 == 0) {
+    GEN junk = NULL;
+    gel(p,7) = tpoint(E,7,&junk);
+    if (!gel(p,7)) B /= 7;
+  }
+  /* B is the exponent of E_tors(Q), r2 is the rank of its 2-Sylow,
+   * for i > 1, p[i] is a point of order i if one exists and i is a prime power
+   * and NULL otherwise */
+  if (r2 == 2) /* 2 cyclic factors */
+  { /* C2 x C2 */
+    if (B == 2) return tors(E,2, gel(T2,1), gel(T2,2), v);
+    else if (B == 6)
+    { /* C2 x C6 */
+      P = elladd(E, gel(p,3), gel(T2,1));
+      Q = gel(T2,2);
+    }
+    else
+    { /* C2 x C4 or C2 x C8 */
+      P = gel(p, B);
+      Q = gel(T2,2);
+      if (gequal(Q, ellmul(E, P, utoipos(B>>1)))) Q = gel(T2,1);
+    }
+  }
+  else /* cyclic */
+  {
+    Q = NULL;
+    if (v2)
+    {
+      if (B>>v2 == 1)
+        P = gel(p, B);
+      else
+        P = elladd(E, gel(p, B>>v2), gel(p,1<<v2));
+    }
+    else P = gel(p, B);
+  }
+  return tors(E,B, P, Q, v);
+}
+GEN
+elltors(GEN e)
+{
+  pari_sp av = avma;
+  checkell_Q(e);
+  return gerepileupto(av, elltors_divpol(e));
+}
+
+GEN
+elltors0(GEN e, long flag)
+{
+  checkell_Q(e);
+  switch(flag)
+  {
+    case 0: return elltors(e);
+    case 1: return nagelllutz(e);
+    case 2: return elltors_doud(e);
+    default: pari_err_FLAG("elltors");
+  }
+  return NULL; /* not reached */
+}
+
+GEN
+ellweilpairing(GEN E, GEN P, GEN Q, GEN m)
+{
+  GEN fg;
+  checkell_Fq(E); checkellpt(P); checkellpt(Q);
+  if (typ(m)!=t_INT) pari_err_TYPE("ellweilpairing",m);
+  fg = ellff_get_field(E);
+  if (typ(fg)==t_FFELT)
+    return FF_ellweilpairing(E, P, Q, m);
+  else
+  {
+    pari_sp av = avma;
+    GEN p = fg, e = ellff_get_a4a6(E);
+    GEN z = FpE_weilpairing(FpE_changepointinv(RgV_to_FpV(P,p),gel(e,3),p),
+                            FpE_changepointinv(RgV_to_FpV(Q,p),gel(e,3),p),m,gel(e,1),p);
+    return gerepileupto(av, Fp_to_mod(z, p));
+  }
+}
+
+GEN
+elltatepairing(GEN E, GEN P, GEN Q, GEN m)
+{
+  GEN fg;
+  checkell_Fq(E); checkellpt(P); checkellpt(Q);
+  if (typ(m)!=t_INT) pari_err_TYPE("elltatepairing",m);
+  fg = ellff_get_field(E);
+  if (typ(fg)==t_FFELT)
+    return FF_elltatepairing(E, P, Q, m);
+  else
+  {
+    pari_sp av = avma;
+    GEN p = fg, e = ellff_get_a4a6(E);
+    GEN z = FpE_tatepairing(FpE_changepointinv(RgV_to_FpV(P,p),gel(e,3),p),
+                            FpE_changepointinv(RgV_to_FpV(Q,p),gel(e,3),p),m,gel(e,1),p);
+    return gerepileupto(av, Fp_to_mod(z, p));
+  }
+}
+
+/* E/Q, return cardinal including the (possible) ramified point */
+static GEN
+ellcard_ram(GEN E, GEN p)
+{
+  GEN a4, a6, D = Rg_to_Fp(ell_get_disc(E), p);
+  if (!signe(D))
+  {
+    pari_sp av = avma;
+    int good_red;
+    GEN ap = is_minimal_ap(E, p, &good_red);
+    return gerepileuptoint(av, subii(addiu(p,1), ap));
+  }
+  if (equaliu(p,2)) return utoi(cardmod2(E));
+  if (equaliu(p,3)) return utoi(cardmod3(E));
+  ell_to_a4a6(E,p,&a4,&a6);
+  return Fp_ellcard(a4, a6, p);
+}
+
+static GEN
+checkellp(GEN E, GEN p, const char *s)
+{
+  GEN q;
+  if (p)
+  {
+    if (typ(p)!=t_INT) pari_err_TYPE(s,p);
+    if (cmpis(p, 2) < 0) pari_err_DOMAIN(s,"p", "<", gen_2, p);
+  }
+  checkell(E);
+  switch(ell_get_type(E))
+  {
+    case t_ELL_Qp:
+      q = ellQp_get_p(E);
+      if (p && !equalii(p, q)) pari_err_TYPE(s,p);
+      return q;
+
+    case t_ELL_Fp:
+    case t_ELL_Fq:
+      q = ellff_get_p(E);
+      if (p && !equalii(p, q)) pari_err_TYPE(s,p);
+      return q;
+    case t_ELL_Q:
+      if (p) return p;
+    default:
+      pari_err_TYPE(stack_strcat(s," [can't determine p]"), E);
+      return NULL;/*not reached*/
+  }
+}
+
+GEN
+ellap(GEN E, GEN p)
+{
+  pari_sp av = avma;
+  GEN q, card;
+  p = checkellp(E, p, "ellap");
+  switch(ell_get_type(E))
+  {
+  case t_ELL_Fp:
+    q = p; card = ellff_get_card(E);
+    break;
+  case t_ELL_Fq:
+    q = FF_q(ellff_get_field(E)); card = ellff_get_card(E);
+    break;
+  case t_ELL_Q:
+    q = p; card = ellcard_ram(E, p);
+    break;
+  default:
+    pari_err_TYPE("ellap",E);
+    return NULL; /*NOT REACHED*/
+  }
+  return gerepileuptoint(av, subii(addiu(q,1), card));
+}
+
+static GEN
+doellcard(GEN E)
+{
+  GEN fg = ellff_get_field(E);
+  if (typ(fg)==t_FFELT)
+    return FF_ellcard(E);
+  else
+  {
+    GEN e = ellff_get_a4a6(E);
+    return Fp_ellcard(gel(e,1),gel(e,2),fg);
+  }
+}
+
+GEN
+ellff_get_card(GEN E)
+{ return obj_checkbuild(E, FF_CARD, &doellcard); }
+
+GEN
+ellcard(GEN E, GEN p)
+{
+  p = checkellp(E, p, "ellcard");
+  switch(ell_get_type(E))
+  {
+  case t_ELL_Fp: case t_ELL_Fq:
+    return icopy(ellff_get_card(E));
+  case t_ELL_Q:
+    {
+      pari_sp av = avma;
+      GEN N = ellcard_ram(E, p);
+      GEN D = Rg_to_Fp(ell_get_disc(E), p);
+      if (!signe(D)) N = subis(N, 1); /* remove singular point */
+      return gerepileuptoint(av, N);
+    }
+  default:
+    pari_err_TYPE("ellcard",E);
+    return NULL; /*NOT REACHED*/
+  }
+}
+
+/* D = [d_1, ..., d_r ] the elementary divisors for E(Fp), r = 0,1,2.
+ * d_r | ... | d_1 */
+static GEN
+ellgen(GEN E, GEN D, GEN m, GEN p)
+{
+  pari_sp av = avma;
+  if (cmpiu(p, 3)<=0)
+  {
+    ulong l = itou(p), r = lg(D)-1;
+    long a1 = Rg_to_Fl(ell_get_a1(E),l);
+    long a3 = Rg_to_Fl(ell_get_a3(E),l);
+    if (r==0) return cgetg(1,t_VEC);
+    if (l==2)
+    {
+      long a2 = Rg_to_Fl(ell_get_a2(E),l);
+      long a4 = Rg_to_Fl(ell_get_a4(E),l);
+      long a6 = Rg_to_Fl(ell_get_a6(E),l);
+      switch(a1|(a2<<1)|(a3<<2)|(a4<<3)|(a6<<4))
+      { /* r==0 : 22, 23, 25, 28, 31 */
+        case 18: case 29:
+          retmkvec(mkvec2s(1,1));
+        case 19: case 24: case 26:
+          retmkvec(mkvec2s(0,1));
+        case 9: case 16: case 17: case 20: case 21: case 27: case 30:
+          retmkvec(mkvec2s(1,0));
+        default:
+          retmkvec(mkvec2s(0,0));
+      }
+    } else
+    { /* y^2 = 4x^3 + b2 x^2 + 2b4 x + b6 */
+      long b2 = Rg_to_Fl(ell_get_b2(E),l);
+      long b4 = Rg_to_Fl(ell_get_b4(E),l);
+      long b6 = Rg_to_Fl(ell_get_b6(E),l);
+      long T1 = (1+b2+2*b4+b6)%3; /* RHS(1) */
+      long x,y;
+      if (r==2) /* [2,2] */
+        retmkvec2(mkvec2s(0,a3),mkvec2s(1,Fl_add(a1,a3,3)));
+      /* cyclic, order d_1 */
+      y = equaliu(gel(D,1),2)? 0 : 1;
+      if (equaliu(gel(D,1),6)) /* [6] */
+      {
+        long b8 = Rg_to_Fl(ell_get_b8(E),l);
+        x = (b6==1 && b8!=0) ? 0 : (T1==1 && (b2+b8)%3!=0) ? 1 : 2;
+      }
+      else /* [2],[3],[4],[5],[7] */
+      { /* Avoid [x,y] singular, iff b2 x + b4 = 0 = y. */
+        if (y == 1)
+          x = (b6==1) ? 0 : (T1==1) ? 1 : 2;
+        else
+          x = (b6==0 && b4) ? 0 : (T1==0 && (b2 + b4) % 3) ? 1 : 2;
+      }
+      retmkvec(mkvec2s(x,(2*y+a1*x+a3)%3));
+    }
+  }
+  else
+  {
+    GEN e = ell_to_a4a6_bc(E, p), a4 = gel(e, 1), a6 = gel(e, 2);
+    return gerepileupto(av, Fp_ellgens(a4,a6,gel(e,3),D,m,p));
+  }
+}
+
+static GEN
+ellgroup_m(GEN E, GEN p)
+{
+  GEN a4, a6, G, m = gen_1, N = ellcard(E, p);
+  if (equali1(N)) { G = cgetg(1,t_VEC); goto END; }
+  if (equaliu(p, 2)) { G = mkvec(N); goto END; }
+  if (equaliu(p, 3))
+  { /* The only possible non-cyclic group is [2,2] which happens 9 times */
+    ulong b2, b4, b6;
+    if (!equaliu(N, 4)) { G = mkvec(N); goto END; }
+    /* If the group is not cyclic, T = 4x^3 + b2 x^2 + 2b4 x + b6
+     * must have 3 roots else 1 root. Test T(0) = T(1) = 0 mod 3 */
+    b6 = Rg_to_Fl(ell_get_b6(E), 3);
+    if (b6) { G = mkvec(N); goto END; }
+    /* b6 = T(0) = 0 mod 3. Test T(1) */
+    b2 = Rg_to_Fl(ell_get_b2(E), 3);
+    b4 = Rg_to_Fl(ell_get_b4(E), 3);
+    if ((1 + b2 + (b4<<1)) % 3) { G = mkvec(N); goto END; }
+    G = mkvec2s(2, 2); goto END;
+  } /* Now assume p > 3 */
+  ell_to_a4a6(E, p, &a4,&a6);
+  G = Fp_ellgroup(a4,a6,N,p, &m);
+END:
+  return mkvec2(G, m);
+}
+
+static GEN
+doellgroup(GEN E)
+{
+  GEN fg = ellff_get_field(E);
+  return typ(fg) == t_FFELT ? FF_ellgroup(E): ellgroup_m(E, fg);
+}
+
+GEN
+ellff_get_group(GEN E)
+{ return obj_checkbuild(E, FF_GROUP, &doellgroup); }
+
+/* E / Fp */
+static GEN
+doellgens(GEN E)
+{
+  GEN fg = ellff_get_field(E);
+  if (typ(fg)==t_FFELT)
+    return FF_ellgens(E);
+  else
+  {
+    GEN e, Gm, F, p = fg;
+    e = ellff_get_a4a6(E);
+    Gm = ellff_get_group(E);
+    F = Fp_ellgens(gel(e,1),gel(e,2),gel(e,3), gel(Gm,1),gel(Gm,2), p);
+    return FpVV_to_mod(F,p);
+  }
+}
+
+GEN
+ellff_get_gens(GEN E)
+{ return obj_checkbuild(E, FF_GROUPGEN, &doellgens); }
+
+GEN
+ellgroup(GEN E, GEN p)
+{
+  pari_sp av = avma;
+  GEN G;
+  p = checkellp(E,p, "ellgroup");
+  if (ell_over_Fq(E)) G = ellff_get_group(E);
+  else                G = ellgroup_m(E,p); /* t_ELL_Q */
+  return gerepilecopy(av, gel(G,1));
+}
+
+GEN
+ellgroup0(GEN E, GEN p, long flag)
+{
+  pari_sp av = avma;
+  GEN V;
+  if (flag==0) return ellgroup(E, p);
+  if (flag!=1) pari_err_FLAG("ellgroup");
+  p = checkellp(E, p, "ellgroup");
+  if (!ell_over_Fq(E))
+  { /* t_ELL_Q */
+    GEN Gm = ellgroup_m(E, p), G = gel(Gm,1), m = gel(Gm,2);
+    GEN F = FpVV_to_mod(ellgen(E,G,m,p), p);
+    return gerepilecopy(av, mkvec3(ZV_prod(G),G,F));
+  }
+  V = mkvec3(ellff_get_card(E), gel(ellff_get_group(E), 1), ellff_get_gens(E));
+  return gerepilecopy(av, V);
+}
+
+GEN
+ellgenerators(GEN E)
+{
+  checkell(E);
+  switch(ell_get_type(E))
+  {
+    case t_ELL_Q:
+      return obj_checkbuild(E, Q_GROUPGEN, &elldatagenerators);
+    case t_ELL_Fp: case t_ELL_Fq:
+      return gcopy(ellff_get_gens(E));
+    default:
+      pari_err_TYPE("ellgenerators",E);
+      return NULL;/*not reached*/
+  }
+}
+
+/* char != 2,3, j != 0, 1728 */
+static GEN
+ellfromj_simple(GEN j)
+{
+  pari_sp av = avma;
+  GEN k = gsubsg(1728,j), kj = gmul(k, j), k2j = gmul(kj, k);
+  GEN E = zerovec(5);
+  gel(E,4) = gmulsg(3,kj);
+  gel(E,5) = gmulsg(2,k2j); return gerepileupto(av, E);
+}
+GEN
+ellfromj(GEN j)
+{
+  GEN T = NULL, p = typ(j)==t_FFELT? FF_p_i(j): NULL;
+  /* trick: use j^0 to get 1 in the proper base field */
+  if ((p || (Rg_is_FpXQ(j,&T,&p) && p)) && lgefint(p) == 3) switch(p[2])
+  {
+    case 2:
+      if (gequal0(j))
+        retmkvec5(gen_0,gen_0, gpowgs(j,0), gen_0,gen_0);
+      else
+        retmkvec5(gpowgs(j,0),gen_0,gen_0, gen_0,ginv(j));
+    case 3:
+      if (gequal0(j))
+        retmkvec5(gen_0,gen_0,gen_0, gpowgs(j,0), gen_0);
+      else
+      {
+        GEN E = zerovec(5);
+        pari_sp av = avma;
+        gel(E,5) = gerepileupto(av, gneg(gsqr(j)));
+        gel(E,2) = gcopy(j);
+        return E;
+      }
+  }
+  if (gequal0(j)) retmkvec5(gen_0,gen_0,gen_0,gen_0, gpowgs(j,0));
+  if (gequalgs(j,1728)) retmkvec5(gen_0,gen_0,gen_0, gpowgs(j,0), gen_0);
+  return ellfromj_simple(j);
+}
+
+/* n <= 4, N is the characteristic of the base ring or NULL (char 0) */
+static GEN
+elldivpol4(GEN e, GEN N, long n, long v)
+{
+  GEN b2,b4,b6,b8, res;
+  if (n==0) return pol_0(v);
+  if (n<=2) return N? scalarpol_shallow(mkintmod(gen_1,N),v): pol_1(v);
+  b2 = ell_get_b2(e); b4 = ell_get_b4(e);
+  b6 = ell_get_b6(e); b8 = ell_get_b8(e);
+  if (n==3)
+    res = mkpoln(5, N? modsi(3,N): utoi(3),b2,gmulsg(3,b4),gmulsg(3,b6),b8);
+  else
+  {
+    GEN b10 = gsub(gmul(b2, b8), gmul(b4, b6));
+    GEN b12 = gsub(gmul(b8, b4), gsqr(b6));
+    res = mkpoln(7, N? modsi(2, N): gen_2,b2,gmulsg(5,b4),gmulsg(10,b6),gmulsg(10,b8),b10,b12);
+  }
+  setvarn(res, v); return res;
+}
+
+/* T = (2y + a1x + a3)^2 modulo the curve equation. Store elldivpol(e,n,v)
+ * in t[n]. N is the caracteristic of the base ring or NULL (char 0) */
+static GEN
+elldivpol0(GEN e, GEN t, GEN N, GEN T, long n, long v)
+{
+  GEN ret;
+  long m = n/2;
+  if (gel(t,n)) return gel(t,n);
+  if (n<=4) ret = elldivpol4(e, N, n, v);
+  else if (odd(n))
+  {
+    GEN t1 = RgX_mul(elldivpol0(e,t,N,T,m+2,v),
+                     gpowgs(elldivpol0(e,t,N,T,m,v),3));
+    GEN t2 = RgX_mul(elldivpol0(e,t,N,T,m-1,v),
+                     gpowgs(elldivpol0(e,t,N,T,m+1,v),3));
+    if (odd(m))/*f_{4l+3} = f_{2l+3}f_{2l+1}^3 - T f_{2l}f_{2l+2}^3, m=2l+1*/
+      ret = RgX_sub(t1, RgX_mul(T,t2));
+    else       /*f_{4l+1} = T f_{2l+2}f_{2l}^3 - f_{2l-1}f_{2l+1}^3, m=2l*/
+      ret = RgX_sub(RgX_mul(T,t1), t2);
+  }
+  else
+  { /* f_2m = f_m(f_{m+2}f_{m-1}^2 - f_{m-2}f_{m+1}^2) */
+    GEN t1 = RgX_mul(elldivpol0(e,t,N,T,m+2,v),
+                     RgX_sqr(elldivpol0(e,t,N,T,m-1,v)));
+    GEN t2 = RgX_mul(elldivpol0(e,t,N,T,m-2,v),
+                     RgX_sqr(elldivpol0(e,t,N,T,m+1,v)));
+    ret = RgX_mul(elldivpol0(e,t,N,T,m,v), RgX_sub(t1,t2));
+  }
+  gel(t,n) = ret;
+  return ret;
+}
+
+GEN
+elldivpol(GEN e, long n, long v)
+{
+  pari_sp av = avma;
+  GEN ret, D, N;
+  checkell(e); D = ell_get_disc(e);
+  if (v==-1) v = 0;
+  if (varncmp(gvar(D), v) <= 0) pari_err_PRIORITY("elldivpol", e, "<=", v);
+  N = characteristic(D);
+  if (!signe(N)) N = NULL;
+  if (n<0) n = -n;
+  if (n==1 || n==3)
+    ret = elldivpol4(e, N, n, v);
+  else
+  {
+    GEN d2 = RHSpol(e, N); /* (2y + a1x + 3)^2 mod E */
+    setvarn(d2,v);
+    if (n <= 4)
+      ret = elldivpol4(e, N, n, v);
+    else
+      ret = elldivpol0(e, const_vec(n,NULL), N,RgX_sqr(d2), n, v);
+    if (n%2==0) ret = RgX_mul(ret, d2);
+  }
+  return gerepilecopy(av, ret);
+}
diff --git a/src/basemath/galconj.c b/src/basemath/galconj.c
new file mode 100644
index 0000000..7452fcc
--- /dev/null
+++ b/src/basemath/galconj.c
@@ -0,0 +1,2637 @@
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+/*************************************************************************/
+/**                                                                     **/
+/**                           GALOIS CONJUGATES                         **/
+/**                                                                     **/
+/*************************************************************************/
+
+static int is2sparse(GEN x)
+{
+  long i, l = lg(x);
+  if (odd(l-3)) return 0;
+  for(i=3; i<l; i+=2)
+    if (signe(gel(x,i)))
+      return 0;
+  return 1;
+}
+
+static GEN
+galoisconj1(GEN nf)
+{
+  GEN x = get_nfpol(nf, &nf), y, z;
+  long i, lz, v = varn(x), nbmax;
+  pari_sp av = avma;
+  RgX_check_ZX(x, "nfgaloisconj");
+  nbmax = numberofconjugates(x, 2);
+  if (nbmax==1)
+  {
+    GEN res = cgetg(2,t_COL);
+    gel(res,1) = pol_x(v);
+    return res;
+  }
+  if (nbmax==2 && is2sparse(x))
+  {
+    GEN res = cgetg(3,t_COL);
+    gel(res,1) = deg1pol_shallow(gen_m1, gen_0, v);
+    gel(res,2) = pol_x(v);
+    return res;
+  }
+  if (v == 0)
+  {
+    long w = fetch_var();
+    if (nf) y = gsubst(nf, 0, pol_x(w));
+    else { y = leafcopy(x); setvarn(y, w); }
+  }
+  else
+  {
+    y = x;
+    x = leafcopy(x); setvarn(x, 0);
+  }
+  z = nfroots(y, x); lz = lg(z);
+  y = cgetg(lz, t_COL);
+  for (i = 1; i < lz; i++)
+  {
+    GEN t = lift(gel(z,i));
+    if (typ(t) == t_POL) setvarn(t, v);
+    gel(y,i) = t;
+  }
+  if (v == 0) delete_var();
+  return gerepileupto(av, y);
+}
+
+/* nbmax: bound for the number of conjugates */
+static GEN
+galoisconj2pol(GEN x, long prec)
+{
+  long i, n, v, nbauto, nbmax;
+  pari_sp av = avma;
+  GEN y, w, polr, p1, p2;
+  n = degpol(x);
+  if (n <= 0) return cgetg(1, t_VEC);
+  RgX_check_ZX(x, "galoisconj2pol");
+  nbmax = numberofconjugates(x, 2);
+  polr = QX_complex_roots(x, prec);
+  p1 = gel(polr,1);
+  /* accuracy in decimal digits */
+  prec = (long)prec2nbits_mul(prec, LOG10_2 * 0.75);
+  nbauto = 1;
+  w = cgetg(n + 2, t_VEC);
+  gel(w,1) = gen_1;
+  for (i = 2; i <= n; i++) gel(w,i) = gmul(p1, gel(w,i-1));
+  v = varn(x);
+  y = cgetg(nbmax + 1, t_COL);
+  gel(y,1) = pol_x(v);
+  for (i = 2; i <= n && nbauto < nbmax; i++)
+  {
+    gel(w,n+1) = gel(polr,i);
+    p1 = lindep2(w, prec);
+    if (signe(gel(p1,n+1)))
+    {
+      p1[0] = evallg(n+1) | evaltyp(t_COL);
+      p2 = gdiv(RgV_to_RgX(p1, v), negi(gel(p1,n+1)));
+      if (gdvd(poleval(x, p2), x))
+      {
+        gel(y,++nbauto) = p2;
+        if (DEBUGLEVEL > 1) err_printf("conjugate %ld: %Ps\n", i, y[nbauto]);
+      }
+    }
+  }
+  if (nbauto < nbmax)
+    pari_warn(warner, "conjugates list may be incomplete in nfgaloisconj");
+  setlg(y, 1 + nbauto);
+  return gerepileupto(av, gen_sort(y, (void*)&gcmp, &gen_cmp_RgX));
+}
+
+static GEN
+galoisconj2(GEN nf, long prec)
+{
+  long i, n, nbauto, nbmax;
+  pari_sp av = avma;
+  GEN T = get_nfpol(nf,&nf), y, w, polr, p1, p2;
+
+  if (!nf) return galoisconj2pol(T, prec);
+  n = degpol(T);
+  if (n <= 0) return cgetg(1, t_VEC);
+  nbmax = numberofconjugates(T, 2);
+  y = cgetg(nbmax + 1, t_COL);
+  gel(y,1) = pol_x(varn(T));
+  /* accuracy in decimal digits */
+  prec = (long)prec2nbits_mul(nf_get_prec(nf), LOG10_2 * 0.75);
+  nbauto = 1;
+  polr = nf_get_allroots(nf);
+  p2 = nf_get_M(nf);
+  w = cgetg(n + 2, t_VEC);
+  for (i = 1; i <= n; i++) gel(w,i) = gcoeff(p2, 1, i);
+  for (i = 2; i <= n && nbauto < nbmax; i++)
+  {
+    gel(w,n+1) = gel(polr,i);
+    p1 = lindep2(w, prec);
+    if (signe(gel(p1,n+1)))
+    {
+      p1[0] = evallg(n+1) | evaltyp(t_COL);
+      p2 = gdiv(coltoliftalg(nf, p1), negi(gel(p1, n+1)));
+      if (gdvd(poleval(T, p2), T))
+      {
+        gel(y,++nbauto) = p2;
+        if (DEBUGLEVEL > 1) err_printf("conjugate %ld: %Ps\n", i, y[nbauto]);
+      }
+    }
+  }
+  if (nbauto < nbmax)
+    pari_warn(warner, "conjugates list may be incomplete in nfgaloisconj");
+  setlg(y, 1 + nbauto);
+  return gerepileupto(av, gen_sort(y, (void*)&gcmp, &gen_cmp_RgX));
+}
+/*************************************************************************/
+/**                                                                     **/
+/**                           GALOISCONJ4                               **/
+/**                                                                     **/
+/*************************************************************************/
+/*DEBUGLEVEL:
+  1: timing
+  2: outline
+  4: complete outline
+  6: detail
+  7: memory
+  9: complete detail
+*/
+struct galois_borne {
+  GEN  l;
+  long valsol;
+  long valabs;
+  GEN  bornesol;
+  GEN  ladicsol;
+  GEN  ladicabs;
+};
+struct galois_lift {
+  GEN  T;
+  GEN  den;
+  GEN  p;
+  GEN  L;
+  GEN  Lden;
+  long e;
+  GEN  Q;
+  GEN  TQ;
+  struct galois_borne *gb;
+};
+struct galois_testlift {
+  long n;
+  long f;
+  long g;
+  GEN  bezoutcoeff;
+  GEN  pauto;
+  GEN  C;
+  GEN  Cd;
+};
+struct galois_test { /* data for permutation test */
+  GEN order; /* order of tests pour galois_test_perm */
+  GEN borne, lborne; /* coefficient bounds */
+  GEN ladic;
+  GEN PV; /* NULL or vector of test matrices (Vmatrix) */
+  GEN TM; /* transpose of M */
+  GEN L; /* p-adic roots, known mod ladic */
+  GEN M; /* vandermonde inverse */
+};
+/* result of the study of Frobenius degrees */
+enum ga_code {ga_all_normal=1,ga_ext_2=2,ga_non_wss=4};
+struct galois_analysis {
+  long p; /* prime to be lifted */
+  long deg; /* degree of the lift */
+  long ord;
+  long l; /* l: prime number such that T is totally split mod l */
+  long p4;
+  enum ga_code group;
+};
+struct galois_frobenius {
+  long p;
+  long fp;
+  long deg;
+  GEN Tmod;
+  GEN psi;
+};
+
+GEN
+vandermondeinverseprep(GEN L)
+{
+  long i, j, n = lg(L);
+  GEN V;
+  V = cgetg(n, t_VEC);
+  for (i = 1; i < n; i++)
+  {
+    pari_sp ltop = avma;
+    GEN W = cgetg(n-1,t_VEC);
+    long k = 1;
+    for (j = 1; j < n; j++)
+      if (i != j) gel(W, k++) = gsub(gel(L,i),gel(L,j));
+    gel(V,i) = gerepileupto(ltop,divide_conquer_prod(W,&gmul));
+  }
+  return V;
+}
+
+/* Compute the inverse of the van der Monde matrix of T multiplied by den */
+GEN
+vandermondeinverse(GEN L, GEN T, GEN den, GEN prep)
+{
+  pari_sp ltop = avma;
+  long i, n = lg(L)-1;
+  GEN M, P;
+  if (!prep) prep = vandermondeinverseprep(L);
+  M = cgetg(n+1, t_MAT);
+  for (i = 1; i <= n; i++)
+  {
+    P = RgX_Rg_div(RgX_div_by_X_x(T, gel(L,i), NULL), gel(prep,i));
+    gel(M,i) = RgX_to_RgV(P,n);
+  }
+  return den? gerepileupto(ltop, gmul(den, M)): gerepilecopy(ltop, M);
+}
+
+/* #r = r1 + r2 */
+GEN
+embed_roots(GEN ro, long r1)
+{
+  long r2 = lg(ro)-1-r1;
+  GEN L;
+  if (!r2) L = ro;
+  else
+  {
+    long i,j, N = r1+2*r2;
+    L = cgetg(N+1, t_VEC);
+    for (i = 1; i <= r1; i++) gel(L,i) = gel(ro,i);
+    for (j = i; j <= N; i++)
+    {
+      GEN z = gel(ro,i);
+      gel(L,j++) = z;
+      gel(L,j++) = mkcomplex(gel(z,1), gneg(gel(z,2)));
+    }
+  }
+  return L;
+}
+GEN
+embed_disc(GEN z, long r1, long prec)
+{
+  pari_sp av = avma;
+  GEN t = real_1(prec);
+  long i, j, n = lg(z)-1, r2 = n-r1;
+  for (i = 1; i < r1; i++)
+  {
+    GEN zi = gel(z,i);
+    for (j = i+1; j <= r1; j++) t = gmul(t, gsub(zi, gel(z,j)));
+  }
+  for (j = r1+1; j <= n; j++)
+  {
+    GEN zj = gel(z,j), a = gel(zj,1), b = gel(zj,2), b2 = gsqr(b);
+    for (i = 1; i <= r1; i++)
+    {
+      GEN zi = gel(z,i);
+      t = gmul(t, gadd(gsqr(gsub(zi, a)), b2));
+    }
+    t = gmul(t, b);
+  }
+  if (r2) t = gmul2n(t, r2);
+  if (r2 > 1)
+  {
+    GEN T = real_1(prec);
+    for (i = r1+1; i < n; i++)
+    {
+      GEN zi = gel(z,i), a = gel(zi,1), b = gel(zi,2);
+      for (j = i+1; j <= n; j++)
+      {
+        GEN zj = gel(z,j), c = gel(zj,1), d = gel(zj,2);
+        GEN f = gsqr(gsub(a,c)), g = gsqr(gsub(b,d)), h = gsqr(gadd(b,d));
+        T = gmul(T, gmul(gadd(f,g), gadd(f,h)));
+      }
+    }
+    t = gmul(t, T);
+  }
+  t = gsqr(t);
+  if (odd(r2)) t = gneg(t);
+  return gerepileupto(av, t);
+}
+
+/* Compute bound for the coefficients of automorphisms.
+ * T a ZX, dn a t_INT denominator or NULL */
+GEN
+initgaloisborne(GEN T, GEN dn, long prec, GEN *ptL, GEN *ptprep, GEN *ptdis)
+{
+  GEN L, prep, den, nf, r;
+  pari_timer ti;
+
+  if (DEBUGLEVEL>=4) timer_start(&ti);
+  T = get_nfpol(T, &nf);
+  r = nf ? nf_get_roots(nf) : NULL;
+  if (nf &&  precision(gel(r, 1)) >= prec)
+    L = embed_roots(r, nf_get_r1(nf));
+  else
+    L = QX_complex_roots(T, prec);
+  if (DEBUGLEVEL>=4) timer_printf(&ti,"roots");
+  prep = vandermondeinverseprep(L);
+  if (!dn)
+  {
+    GEN dis, res = divide_conquer_prod(gabs(prep,prec), mpmul);
+    dis = ZX_disc_all(T, expi(ceil_safe(res)));
+    den = indexpartial(T,dis);
+    if (ptdis) *ptdis = dis;
+  }
+  else
+  {
+    if (typ(dn) != t_INT || signe(dn) <= 0)
+      pari_err_TYPE("initgaloisborne [incorrect denominator]", dn);
+    den = dn;
+  }
+  if (ptprep) *ptprep = prep;
+  *ptL = L; return den;
+}
+
+/* ||| M ||| with respect to || x ||_oo. Assume M square t_MAT */
+GEN
+matrixnorm(GEN M, long prec)
+{
+  long i,j, n = lg(M);
+  GEN B = real_0(prec);
+
+  for (i = 1; i < n; i++)
+  {
+    GEN z = gabs(gcoeff(M,i,1), prec);
+    for (j = 2; j < n; j++) z = gadd(z, gabs(gcoeff(M,i,j), prec));
+    if (gcmp(z, B) > 0) B = z;
+  }
+  return B;
+}
+
+static GEN
+galoisborne(GEN T, GEN dn, struct galois_borne *gb, long d)
+{
+  pari_sp ltop = avma, av2;
+  GEN borne, borneroots, borneabs;
+  long prec;
+  GEN L, M, prep, den;
+  pari_timer ti;
+
+  prec = nbits2prec(bit_accuracy(ZX_max_lg(T)));
+  den = initgaloisborne(T,dn,prec, &L,&prep,NULL);
+  if (!dn) den = gclone(den);
+  if (DEBUGLEVEL>=4) timer_start(&ti);
+  M = vandermondeinverse(L, RgX_gtofp(T, prec), den, prep);
+  if (DEBUGLEVEL>=4) timer_printf(&ti,"vandermondeinverse");
+  borne = matrixnorm(M, prec);
+  borneroots = gsupnorm(L, prec); /*t_REAL*/
+  borneabs = ceil_safe(gmul(borne,gmulsg(d, powru(borneroots, d))));
+  borneroots = ceil_safe(gmul(borne, borneroots));
+  av2 = avma;
+  /*We use d-1 test, so we must overlift to 2^BITS_IN_LONG*/
+  gb->valsol = logint(shifti(borneroots,2+BITS_IN_LONG), gb->l,NULL);
+  gb->valabs = logint(shifti(borneabs,2), gb->l, NULL);
+  gb->valabs = maxss(gb->valsol, gb->valabs);
+  if (DEBUGLEVEL >= 4)
+    err_printf("GaloisConj:val1=%ld val2=%ld\n", gb->valsol, gb->valabs);
+  avma = av2;
+  gb->bornesol = gerepileuptoint(ltop, shifti(borneroots,1));
+  if (DEBUGLEVEL >= 9)
+    err_printf("GaloisConj: Bound %Ps\n",borneroots);
+  gb->ladicsol = powiu(gb->l, gb->valsol);
+  gb->ladicabs = powiu(gb->l, gb->valabs);
+  if (!dn) { dn = icopy(den); gunclone(den); }
+  return dn;
+}
+
+static GEN
+makeLden(GEN L,GEN den, struct galois_borne *gb)
+{ return FpC_Fp_mul(L, den, gb->ladicsol); }
+
+/* Initialize the galois_lift structure */
+static void
+initlift(GEN T, GEN den, GEN p, GEN L, GEN Lden, struct galois_borne *gb, struct galois_lift *gl)
+{
+  pari_sp av = avma;
+  long e;
+  gl->gb = gb;
+  gl->T = T;
+  gl->den = is_pm1(den)? gen_1: den;
+  gl->p = p;
+  gl->L = L;
+  gl->Lden = Lden;
+  e = logint(shifti(gb->bornesol, 2+BITS_IN_LONG),p,NULL);
+  avma = av;
+  if (e < 2) e = 2;
+  gl->e = e;
+  gl->Q = powiu(p, e);
+  gl->TQ = FpX_red(T,gl->Q);
+}
+
+/* Check whether f is (with high probability) a solution and compute its
+ * permutation */
+static int
+poltopermtest(GEN f, struct galois_lift *gl, GEN pf)
+{
+  pari_sp av;
+  GEN fx, fp, B = gl->gb->bornesol;
+  long i, j, ll;
+  for (i = 2; i < lg(f); i++)
+    if (absi_cmp(gel(f,i),B) > 0)
+    {
+      if (DEBUGLEVEL>=4) err_printf("GaloisConj: Solution too large.\n");
+      if (DEBUGLEVEL>=8) err_printf("f=%Ps\n borne=%Ps\n",f,B);
+      return 0;
+    }
+  ll = lg(gl->L);
+  fp = const_vecsmall(ll-1, 1); /* left on stack */
+  av = avma;
+  for (i = 1; i < ll; i++, avma = av)
+  {
+    fx = FpX_eval(f, gel(gl->L,i), gl->gb->ladicsol);
+    for (j = 1; j < ll; j++)
+      if (fp[j] && equalii(fx, gel(gl->Lden,j))) { pf[i]=j; fp[j]=0; break; }
+    if (j == ll) return 0;
+  }
+  return 1;
+}
+
+static long
+monoratlift(GEN S, GEN q, GEN qm1old,struct galois_lift *gl, GEN frob)
+{
+  GEN tlift = FpX_ratlift(S,q,qm1old,qm1old,gl->den);
+  if (tlift)
+  {
+    pari_sp ltop = avma;
+    if(DEBUGLEVEL>=4)
+      err_printf("MonomorphismLift: trying early solution %Ps\n",tlift);
+    if (gl->den != gen_1) {
+      GEN N = gl->gb->ladicsol, N2 = shifti(N,-1);
+      tlift = FpX_center(FpX_red(Q_muli_to_int(tlift, gl->den), N), N,N2);
+    }
+    if (poltopermtest(tlift, gl, frob))
+    {
+      if(DEBUGLEVEL>=4) err_printf("MonomorphismLift: true early solution.\n");
+      avma = ltop; return 1;
+    }
+    avma = ltop;
+    if(DEBUGLEVEL>=4) err_printf("MonomorphismLift: false early solution.\n");
+  }
+  return 0;
+}
+
+static GEN
+monomorphismratlift(GEN P, GEN S, struct galois_lift *gl, GEN frob)
+{
+  pari_sp ltop, lbot;
+  GEN Q = gl->T, p = gl->p, qold = NULL, q = p;
+  GEN Sr, Spow, Wr = NULL, W, Prold = NULL, Pr, Qrold = NULL, Qr;
+  long e = gl->e, level = 1, rt;
+  ulong mask;
+  GEN *gptr[2];
+  pari_timer ti;
+
+  if (DEBUGLEVEL == 1) timer_start(&ti);
+  rt = brent_kung_optpow(degpol(P), 4, 3);
+  mask = quadratic_prec_mask(e);
+  Pr = FpX_red(P,q);
+  Qr = (P==Q)? Pr: FpX_red(Q, q);/*A little speed up for automorphismlift*/
+  W = FpXQ_inv(FpX_FpXQ_eval(ZX_deriv(Pr),S, Qr,q), Qr,q);
+  gptr[0] = &S;
+  gptr[1] = &Wr;
+  for (;;)
+  {
+    if (DEBUGLEVEL>=2) { level = (level<<1) - (mask & 1); timer_start(&ti); }
+    q = sqri(q);
+    if (mask & 1) q = diviiexact(q, p);
+    mask >>= 1;
+    Pr = FpX_red(P, q);
+    Qr = (P==Q)? Pr: FpX_red(Q, q);/*A little speed up for automorphismlift*/
+    ltop = avma;
+    Sr = S;
+    Spow = FpXQ_powers(Sr, rt, Qr, q);
+    if (Wr)
+    {
+      W = FpXQ_mul(Wr, FpX_FpXQV_eval(ZX_deriv(Prold),FpXV_red(Spow,qold),Qrold,qold),
+                   Qrold,qold);
+      W = FpXQ_mul(Wr, Fp_FpX_sub(gen_2, W, qold), Qrold,qold);
+    }
+    Wr = W;
+    S = FpXQ_mul(Wr, FpX_FpXQV_eval(Pr, Spow, Qr, q),Qr,q);
+    lbot = avma;
+    S = FpX_sub(Sr, S, q);
+    if (DEBUGLEVEL >= 4) timer_printf(&ti, "MonomorphismLift: lift to prec %d",level);
+    if (mask == 1) break;
+    Wr = ZX_copy(Wr);
+    gerepilemanysp(ltop, lbot, gptr, 2);
+    if (qold && frob && monoratlift(S,q,diviiexact(qold,p),gl,frob)) return NULL;
+    qold = q; Prold = Pr; Qrold = Qr;
+  }
+  if (DEBUGLEVEL == 1) timer_printf(&ti, "monomorphismlift()");
+  return S;
+}
+
+/* Let T be a polynomial in Z[X] , p a prime number, S in Fp[X]/(T) so
+ * that T(S)=0 [p,T]. Lift S in S_0 so that T(S_0)=0 [T,p^e]
+ * Unclean stack */
+static GEN
+automorphismlift(GEN S, struct galois_lift *gl, GEN frob)
+{
+  return monomorphismratlift(gl->T, S, gl, frob);
+}
+
+/* Let P be a polynomial in Z[X] , p a prime number, S in Fp[X]/(Q) so
+ * that T(S)=0 [p,T]. Lift S in S_0 so that T(S_0)=0 [Q,p^e]
+ * Unclean stack */
+
+GEN
+monomorphismlift(GEN P, GEN S, GEN Q, GEN p, long e)
+{
+  struct galois_lift gl;
+  gl.T = Q;
+  gl.p = p;
+  gl.e = e;
+  return monomorphismratlift(P,S,&gl,NULL);
+}
+
+static GEN
+galoisdolift(struct galois_lift *gl, GEN frob)
+{
+  pari_sp av = avma;
+  GEN Tp = FpX_red(gl->T, gl->p);
+  GEN S = FpXQ_pow(pol_x(varn(Tp)), gl->p, Tp, gl->p);
+  return gerepileupto(av, automorphismlift(S, gl, frob));
+}
+
+static void
+inittestlift(GEN plift, GEN Tmod, struct galois_lift *gl,
+             struct galois_testlift *gt)
+{
+  pari_timer ti;
+  gt->n = lg(gl->L) - 1;
+  gt->g = lg(Tmod) - 1;
+  gt->f = gt->n / gt->g;
+  gt->bezoutcoeff = bezout_lift_fact(gl->T, Tmod, gl->p, gl->e);
+  if (DEBUGLEVEL >= 2) timer_start(&ti);
+  gt->pauto = FpXQ_autpowers(plift, gt->f-1, gl->TQ, gl->Q);
+  if (DEBUGLEVEL >= 2) timer_printf(&ti, "Frobenius power");
+}
+
+/* Explanation of the intheadlong technique:
+ * Let C be a bound, B = BITS_IN_LONG, M > C*2^B a modulus and 0 <= a_i < M for
+ * i=1,...,n where n < 2^B. We want to test if there exist k,l, |k| < C < M/2^B,
+ * such that sum a_i = k + l*M
+ * We write a_i*2^B/M = b_i+c_i with b_i integer and 0<=c_i<1, so that
+ *   sum b_i - l*2^B = k*2^B/M - sum c_i
+ * Since -1 < k*2^B/M < 1 and 0<=c_i<1, it follows that
+ *   -n-1 < sum b_i - l*2^B < 1  i.e.  -n <= sum b_i -l*2^B <= 0
+ * So we compute z = - sum b_i [mod 2^B] and check if 0 <= z <= n. */
+
+/* Assume 0 <= x < mod. */
+long
+intheadlong(GEN x, GEN mod)
+{
+  pari_sp av = avma;
+  long res = (long) itou(divii(shifti(x,BITS_IN_LONG),mod));
+  avma = av; return res;
+}
+static GEN
+vecheadlong(GEN W, GEN mod)
+{
+  long i, l = lg(W);
+  GEN V = cgetg(l, t_VECSMALL);
+  for(i=1; i<l; i++) V[i] = intheadlong(gel(W,i), mod);
+  return V;
+}
+GEN
+matheadlong(GEN W, GEN mod)
+{
+  long i, l = lg(W);
+  GEN V = cgetg(l,t_MAT);
+  for(i=1; i<l; i++) gel(V,i) = vecheadlong(gel(W,i), mod);
+  return V;
+}
+long
+polheadlong(GEN P, long n, GEN mod)
+{
+  return (lg(P)>n+2)? intheadlong(gel(P,n+2),mod): 0;
+}
+
+#define headlongisint(Z,N) (-(ulong)(Z)<=(ulong)(N))
+
+static long
+frobeniusliftall(GEN sg, long el, GEN *psi, struct galois_lift *gl,
+                 struct galois_testlift *gt, GEN frob)
+{
+  pari_sp av, ltop2, ltop = avma;
+  long i,j,k, c = lg(sg)-1, n = lg(gl->L)-1, m = gt->g, d = m / c;
+  GEN pf, u, v, C, Cd, SG, cache;
+  long N1, N2, R1, Ni, Z, ord = gt->f, c_idx = gt->g-1;
+  long hop = 0;
+  GEN NN, NQ;
+  pari_timer ti;
+
+  *psi = pf = cgetg(m, t_VECSMALL);
+  ltop2 = avma;
+  NN = diviiexact(mpfact(m), mului(c, powiu(mpfact(d), c)));
+  if (DEBUGLEVEL >= 4)
+    err_printf("GaloisConj:I will try %Ps permutations\n", NN);
+  N1=10000000;
+  NQ=divis_rem(NN,N1,&R1);
+  if (cmpiu(NQ,1000000000)>0)
+  {
+    pari_warn(warner,"Combinatorics too hard : would need %Ps tests!\n"
+        "I will skip it, but it may induce an infinite loop",NN);
+    avma = ltop; *psi = NULL; return 0;
+  }
+  N2=itos(NQ); if(!N2) N1=R1;
+  if (DEBUGLEVEL>=4) timer_start(&ti);
+  avma = ltop2;
+  C = gt->C;
+  Cd= gt->Cd;
+  v = FpXQ_mul(gel(gt->pauto, 1+el%ord), gel(gt->bezoutcoeff, m),gl->TQ,gl->Q);
+  if (gl->den != gen_1) v = FpX_Fp_mul(v, gl->den, gl->Q);
+  SG = cgetg(lg(sg),t_VECSMALL);
+  for(i=1; i<lg(SG); i++) SG[i] = (el*sg[i])%ord + 1;
+  cache = cgetg(m+1,t_VECSMALL); cache[m] = polheadlong(v,1,gl->Q);
+  Z = polheadlong(v,2,gl->Q);
+  for (i = 1; i < m; i++) pf[i] = 1 + i/d;
+  av = avma;
+  for (Ni = 0, i = 0; ;i++)
+  {
+    for (j = c_idx ; j > 0; j--)
+    {
+      long h = SG[pf[j]];
+      if (!mael(C,h,j))
+      {
+        pari_sp av3 = avma;
+        GEN r = FpXQ_mul(gel(gt->pauto,h), gel(gt->bezoutcoeff,j),gl->TQ,gl->Q);
+        if (gl->den != gen_1) r = FpX_Fp_mul(r, gl->den, gl->Q);
+        gmael(C,h,j) = gclone(r);
+        mael(Cd,h,j) = polheadlong(r,1,gl->Q);
+        avma = av3;
+      }
+      uel(cache,j) = uel(cache,j+1)+umael(Cd,h,j);
+    }
+    if (headlongisint(uel(cache,1),n))
+    {
+      long ZZ = Z;
+      for (j = 1; j < m; j++) ZZ += polheadlong(gmael(C,SG[pf[j]],j),2,gl->Q);
+      if (headlongisint(ZZ,n))
+      {
+        u = v;
+        for (j = 1; j < m; j++) u = ZX_add(u, gmael(C,SG[pf[j]],j));
+        u = FpX_center(FpX_red(u, gl->Q), gl->Q, shifti(gl->Q,-1));
+        if (poltopermtest(u, gl, frob))
+        {
+          if (DEBUGLEVEL >= 4)
+          {
+            timer_printf(&ti, "");
+            err_printf("GaloisConj: %d hops on %Ps tests\n",hop,addis(mulss(Ni,N1),i));
+          }
+          avma = ltop2; return 1;
+        }
+        if (DEBUGLEVEL >= 4) err_printf("M");
+      }
+      else hop++;
+    }
+    if (DEBUGLEVEL >= 4 && i % maxss(N1/20, 1) == 0)
+      timer_printf(&ti, "GaloisConj:Testing %Ps", addis(mulss(Ni,N1),i));
+    avma = av;
+    if (i == N1-1)
+    {
+      if (Ni==N2-1) N1 = R1;
+      if (Ni==N2) break;
+      Ni++; i = 0;
+      if (DEBUGLEVEL>=4) timer_start(&ti);
+    }
+    for (j = 2; j < m && pf[j-1] >= pf[j]; j++)
+      /*empty*/; /* to kill clang Warning */
+    for (k = 1; k < j-k && pf[k] != pf[j-k]; k++) { lswap(pf[k], pf[j-k]); }
+    for (k = j - 1; pf[k] >= pf[j]; k--)
+      /*empty*/;
+    lswap(pf[j], pf[k]); c_idx = j;
+  }
+  if (DEBUGLEVEL>=4) err_printf("GaloisConj: not found, %d hops \n",hop);
+  *psi = NULL; avma = ltop; return 0;
+}
+
+/* Compute the test matrix for the i-th line of V. Clone. */
+static GEN
+Vmatrix(long i, struct galois_test *td)
+{
+  pari_sp av = avma;
+  GEN m = gclone( matheadlong(FpC_FpV_mul(td->L, row(td->M,i), td->ladic), td->ladic));
+  avma = av; return m;
+}
+
+/* Initialize galois_test */
+static void
+inittest(GEN L, GEN M, GEN borne, GEN ladic, struct galois_test *td)
+{
+  long i, n = lg(L)-1;
+  GEN p = cgetg(n+1, t_VECSMALL);
+  if (DEBUGLEVEL >= 8) err_printf("GaloisConj:Init Test\n");
+  td->order = p;
+  for (i = 1; i <= n-2; i++) p[i] = i+2;
+  p[n-1] = 1; p[n] = 2;
+  td->borne = borne;
+  td->lborne = subii(ladic, borne);
+  td->ladic = ladic;
+  td->L = L;
+  td->M = M;
+  td->TM = shallowtrans(M);
+  td->PV = zero_zv(n);
+  gel(td->PV, 2) = Vmatrix(2, td);
+}
+
+/* Free clones stored inside galois_test */
+static void
+freetest(struct galois_test *td)
+{
+  long i;
+  for (i = 1; i < lg(td->PV); i++)
+    if (td->PV[i]) { gunclone(gel(td->PV,i)); td->PV[i] = 0; }
+}
+
+/* Check if the integer P seen as a p-adic number is close to an integer less
+ * than td->borne in absolute value */
+static long
+padicisint(GEN P, struct galois_test *td)
+{
+  pari_sp ltop = avma;
+  GEN U  = modii(P, td->ladic);
+  long r = cmpii(U, td->borne) <= 0 || cmpii(U, td->lborne) >= 0;
+  avma = ltop; return r;
+}
+
+/* Check if the permutation pf is valid according to td.
+ * If not, update td to make subsequent test faster (hopefully) */
+static long
+galois_test_perm(struct galois_test *td, GEN pf)
+{
+  pari_sp av = avma;
+  long i, j, n = lg(td->L)-1;
+  GEN V, P = NULL;
+  for (i = 1; i < n; i++)
+  {
+    long ord = td->order[i];
+    GEN PW = gel(td->PV, ord);
+    if (PW)
+    {
+      ulong Z = umael(PW,1,pf[1]);
+      for (j = 2; j <= n; j++) Z += umael(PW,j,pf[j]);
+      if (!headlongisint(Z,n)) break;
+    } else
+    {
+      if (!P) P = vecpermute(td->L, pf);
+      V = FpV_dotproduct(gel(td->TM,ord), P, td->ladic);
+      if (!padicisint(V, td)) {
+        gel(td->PV, ord) = Vmatrix(ord, td);
+        if (DEBUGLEVEL >= 4) err_printf("M");
+        break;
+      }
+    }
+  }
+  if (i == n) { avma = av; return 1; }
+  if (DEBUGLEVEL >= 4) err_printf("%d.", i);
+  if (i > 1)
+  {
+    long z = td->order[i];
+    for (j = i; j > 1; j--) td->order[j] = td->order[j-1];
+    td->order[1] = z;
+    if (DEBUGLEVEL >= 8) err_printf("%Ps", td->order);
+  }
+  avma = av; return 0;
+}
+/*Compute a*b/c when a*b will overflow*/
+static long
+muldiv(long a,long b,long c)
+{
+  return (long)((double)a*(double)b/c);
+}
+
+/* F = cycle decomposition of sigma,
+ * B = cycle decomposition of cl(tau).
+ * Check all permutations pf who can possibly correspond to tau, such that
+ * tau*sigma*tau^-1 = sigma^s and tau^d = sigma^t, where d = ord cl(tau)
+ * x: vector of choices,
+ * G: vector allowing linear access to elts of F.
+ * Choices multiple of e are not changed. */
+static GEN
+testpermutation(GEN F, GEN B, GEN x, long s, long e, long cut,
+                struct galois_test *td)
+{
+  pari_sp av, avm = avma;
+  long a, b, c, d, n, p1, p2, p3, p4, p5, p6, l1, l2, N1, N2, R1;
+  long i, j, cx, hop = 0, start = 0;
+  GEN pf, ar, G, W, NN, NQ;
+  pari_timer ti;
+  if (DEBUGLEVEL >= 1) timer_start(&ti);
+  a = lg(F)-1; b = lg(gel(F,1))-1;
+  c = lg(B)-1; d = lg(gel(B,1))-1;
+  n = a*b;
+  s = (b+s) % b;
+  pf = cgetg(n+1, t_VECSMALL);
+  av = avma;
+  ar = cgetg(a+2, t_VECSMALL); ar[a+1]=0;
+  G  = cgetg(a+1, t_VECSMALL);
+  W  = gel(td->PV, td->order[n]);
+  for (cx=1, i=1, j=1; cx <= a; cx++, i++)
+  {
+    gel(G,cx) = gel(F, coeff(B,i,j));
+    if (i == d) { i = 0; j++; }
+  }
+  NN = divis(powuu(b, c * (d - d/e)), cut);
+  if (DEBUGLEVEL>=4) err_printf("GaloisConj:I will try %Ps permutations\n", NN);
+  N1 = 1000000;
+  NQ = divis_rem(NN,N1,&R1);
+  if (cmpiu(NQ,100000000)>0)
+  {
+    avma = avm;
+    pari_warn(warner,"Combinatorics too hard : would need %Ps tests!\n I'll skip it but you will get a partial result...",NN);
+    return identity_perm(n);
+  }
+  N2 = itos(NQ);
+  for (l2 = 0; l2 <= N2; l2++)
+  {
+    long nbiter = (l2<N2) ? N1: R1;
+    if (DEBUGLEVEL >= 2 && N2) err_printf("%d%% ", muldiv(l2,100,N2));
+    for (l1 = 0; l1 < nbiter; l1++)
+    {
+      if (start)
+      {
+        for (i=1, j=e; i < a;)
+        {
+          if ((++(x[i])) != b) break;
+          x[i++] = 0;
+          if (i == j) { i++; j += e; }
+        }
+      }
+      else { start=1; i = a-1; }
+      /* intheadlong test: overflow in + is OK, we compute mod 2^BIL */
+      for (p1 = i+1, p5 = p1%d - 1 ; p1 >= 1; p1--, p5--) /* p5 = (p1%d) - 1 */
+      {
+        ulong V = 0;
+        if (p5 == - 1) { p5 = d - 1; p6 = p1 + 1 - d; } else p6 = p1 + 1;
+        p4 = p5 ? x[p1-1] : 0;
+        V = 0;
+        for (p2 = 1+p4, p3 = 1 + x[p1]; p2 <= b; p2++)
+        {
+          V += umael(W,mael(G,p6,p3),mael(G,p1,p2));
+          p3 += s; if (p3 > b) p3 -= b;
+        }
+        p3 = 1 + x[p1] - s; if (p3 <= 0) p3 += b;
+        for (p2 = p4; p2 >= 1; p2--)
+        {
+          V += umael(W,mael(G,p6,p3),mael(G,p1,p2));
+          p3 -= s; if (p3 <= 0) p3 += b;
+        }
+        ar[p1] = ar[p1+1] + V;
+      }
+      if (!headlongisint(uel(ar,1),n)) continue;
+
+      /* intheadlong succeeds. Full computation */
+      for (p1=1, p5=d; p1 <= a; p1++, p5++)
+      {
+        if (p5 == d) { p5 = 0; p4 = 0; } else p4 = x[p1-1];
+        if (p5 == d-1) p6 = p1+1-d; else p6 = p1+1;
+        for (p2 = 1+p4, p3 = 1 + x[p1]; p2 <= b; p2++)
+        {
+          pf[mael(G,p1,p2)] = mael(G,p6,p3);
+          p3 += s; if (p3 > b) p3 -= b;
+        }
+        p3 = 1 + x[p1] - s; if (p3 <= 0) p3 += b;
+        for (p2 = p4; p2 >= 1; p2--)
+        {
+          pf[mael(G,p1,p2)] = mael(G,p6,p3);
+          p3 -= s; if (p3 <= 0) p3 += b;
+        }
+      }
+      if (galois_test_perm(td, pf))
+      {
+        if (DEBUGLEVEL >= 1)
+        {
+          GEN nb = addis(mulss(l2,N1),l1);
+          timer_printf(&ti, "testpermutation(%Ps)", nb);
+          if (DEBUGLEVEL >= 2 && hop)
+            err_printf("GaloisConj:%d hop over %Ps iterations\n", hop, nb);
+        }
+        avma = av; return pf;
+      }
+      hop++;
+    }
+  }
+  if (DEBUGLEVEL >= 1)
+  {
+    timer_printf(&ti, "testpermutation(%Ps)", NN);
+    if (DEBUGLEVEL >= 2 && hop)
+      err_printf("GaloisConj:%d hop over %Ps iterations\n", hop, NN);
+  }
+  avma = avm; return NULL;
+}
+
+/* List of subgroups of (Z/mZ)^* whose order divide o, and return the list
+ * of their elements, sorted by increasing order */
+GEN
+listznstarelts(long m, long o)
+{
+  pari_sp av = avma;
+  GEN L, zn, zns, res;
+  long i, phi, ind, l;
+  if (m == 2)
+  {
+    res = cgetg(2, t_VEC);
+    gel(res,1) = mkvecsmall(1);
+    return res;
+  }
+  zn = znstar(stoi(m));
+  phi = itos(gel(zn,1));
+  o = ugcd(o, phi); /* do we impose this on input ? */
+  zns = znstar_small(zn);
+  L = cgetg(o+1, t_VEC);
+  for (i=1,ind = phi; ind; ind -= phi/o, i++) /* by *decreasing* exact index */
+    gel(L,i) = subgrouplist(gel(zn,2), mkvec(utoipos(ind)));
+  L = shallowconcat1(L); l = lg(L);
+  for (i = 1; i < l; i++) gel(L,i) = znstar_hnf_elts(zns, gel(L,i));
+  return gerepilecopy(av, L);
+}
+
+/* A sympol is a symmetric polynomial
+ *
+ * Currently sympol are couple of t_VECSMALL [v,w]
+ * v[1]...v[k], w[1]...w[k]  represent the polynomial sum(i=1,k,v[i]*s_w[i])
+ * where s_i(X_1,...,X_n) = sum(j=1,n,X_j^i) */
+
+/*Return s_e*/
+static GEN
+sympol_eval_newtonsum(long e, GEN O, GEN mod)
+{
+  long f = lg(O), g = lg(gel(O,1)), i, j;
+  GEN PL = cgetg(f, t_COL);
+  for(i=1; i<f; i++)
+  {
+    pari_sp av = avma;
+    GEN s = gen_0;
+    for(j=1; j<g; j++) s = addii(s, Fp_powu(gmael(O,i,j), (ulong)e, mod));
+    gel(PL,i) = gerepileuptoint(av, remii(s,mod));
+  }
+  return PL;
+}
+
+static GEN
+sympol_eval(GEN v, GEN NS)
+{
+  pari_sp av = avma;
+  long i;
+  GEN S = gen_0;
+  for (i=1; i<lg(v); i++)
+    if (v[i]) S = gadd(S, gmulsg(v[i], gel(NS,i)));
+  return gerepileupto(av, S);
+}
+
+/* Let sigma be an automorphism of L (as a polynomial with rational coefs)
+ * Let 'sym' be a symmetric polynomial defining alpha in L.
+ * We have alpha = sym(x,sigma(x),,,sigma^(g-1)(x)). Compute alpha mod p */
+static GEN
+sympol_aut_evalmod(GEN sym, long g, GEN sigma, GEN Tp, GEN p)
+{
+  pari_sp ltop=avma;
+  long i, j, npows = brent_kung_optpow(degpol(Tp)-1, g-1, 1);
+  GEN s, f, pows, v = zv_to_ZV(gel(sym,1)), w = zv_to_ZV(gel(sym,2));
+  sigma = RgX_to_FpX(sigma, p);
+  pows  = FpXQ_powers(sigma,npows,Tp,p);
+  f = pol_x(varn(sigma));
+  s = pol_0(varn(sigma));
+  for(i=1; i<=g;i++)
+  {
+    if (i > 1) f = FpX_FpXQV_eval(f,pows,Tp,p);
+    for(j=1; j<lg(v); j++)
+      s = FpX_add(s, FpX_Fp_mul(FpXQ_pow(f,gel(w,j),Tp,p),gel(v,j),p),p);
+  }
+  return gerepileupto(ltop, s);
+}
+
+/* Let Sp be as computed with sympol_aut_evalmod
+ * Let Tmod be the factorisation of T mod p.
+ * Return the factorisation of the minimal polynomial of S mod p w.r.t. Tmod */
+static GEN
+fixedfieldfactmod(GEN Sp, GEN p, GEN Tmod)
+{
+  long i, l = lg(Tmod);
+  GEN F = cgetg(l,t_VEC);
+  for(i=1; i<l; i++)
+  {
+    GEN Ti = gel(Tmod,i);
+    gel(F,i) = FpXQ_minpoly(FpX_rem(Sp,Ti,p), Ti,p);
+  }
+  return F;
+}
+
+static GEN
+fixedfieldsurmer(GEN mod, GEN l, GEN p, long v, GEN NS, GEN W)
+{
+  const long step=3;
+  long i, j, n = lg(W)-1, m = 1L<<((n-1)<<1);
+  GEN sym = cgetg(n+1,t_VECSMALL), mod2 = shifti(mod,-1);
+  for (j=1;j<n;j++) sym[j] = step;
+  sym[n] = 0;
+  if (DEBUGLEVEL>=4) err_printf("FixedField: Weight: %Ps\n",W);
+  for (i=0; i<m; i++)
+  {
+    pari_sp av = avma;
+    GEN L, P;
+    for (j=1; sym[j]==step; j++) sym[j]=0;
+    sym[j]++;
+    if (DEBUGLEVEL>=6) err_printf("FixedField: Sym: %Ps\n",sym);
+    L = sympol_eval(sym,NS);
+    if (!vec_is1to1(FpC_red(L,l))) continue;
+    P = FpX_center(FpV_roots_to_pol(L,mod,v),mod,mod2);
+    if (!p || FpX_is_squarefree(P,p)) return mkvec3(mkvec2(sym,W),L,P);
+    avma = av;
+  }
+  return NULL;
+}
+
+/*Check whether the line of NS are pair-wise distinct.*/
+static long
+sympol_is1to1_lg(GEN NS, long n)
+{
+  long i, j, k, l = lgcols(NS);
+  for (i=1; i<l; i++)
+    for(j=i+1; j<l; j++)
+    {
+      for(k=1; k<n; k++)
+        if (!equalii(gmael(NS,k,j),gmael(NS,k,i))) break;
+      if (k>=n) return 0;
+    }
+  return 1;
+}
+
+/* Let O a set of orbits of roots (see fixedfieldorbits) modulo mod,
+ * l | mod and p two prime numbers. Return a vector [sym,s,P] where:
+ * sym is a sympol, s is the set of images of sym on O and
+ * P is the polynomial with roots s. */
+static GEN
+fixedfieldsympol(GEN O, GEN mod, GEN l, GEN p, long v)
+{
+  pari_sp ltop=avma;
+  const long n=(BITS_IN_LONG>>1)-1;
+  GEN NS = cgetg(n+1,t_MAT), sym = NULL, W = cgetg(n+1,t_VECSMALL);
+  long i, e=1;
+  if (DEBUGLEVEL>=4)
+    err_printf("FixedField: Size: %ldx%ld\n",lg(O)-1,lg(gel(O,1))-1);
+  for (i=1; !sym && i<=n; i++)
+  {
+    GEN L = sympol_eval_newtonsum(e++, O, mod);
+    if (lg(O)>2)
+      while (vec_isconst(L)) L = sympol_eval_newtonsum(e++, O, mod);
+    W[i] = e-1; gel(NS,i) = L;
+    if (sympol_is1to1_lg(NS,i+1))
+      sym = fixedfieldsurmer(mod,l,p,v,NS,vecsmall_shorten(W,i));
+  }
+  if (!sym) pari_err_BUG("fixedfieldsympol [p too small]");
+  if (DEBUGLEVEL>=2) err_printf("FixedField: Found: %Ps\n",gel(sym,1));
+  return gerepilecopy(ltop,sym);
+}
+
+/* Let O a set of orbits as indices and L the corresponding roots.
+ * Return the set of orbits as roots. */
+static GEN
+fixedfieldorbits(GEN O, GEN L)
+{
+  GEN S = cgetg(lg(O), t_MAT);
+  long i;
+  for (i = 1; i < lg(O); i++) gel(S,i) = vecpermute(L, gel(O,i));
+  return S;
+}
+
+static GEN
+fixedfieldinclusion(GEN O, GEN PL)
+{
+  long i, j, f = lg(O)-1, g = lg(gel(O,1))-1;
+  GEN S = cgetg(f*g + 1, t_COL);
+  for (i = 1; i <= f; i++)
+  {
+    GEN Oi = gel(O,i);
+    for (j = 1; j <= g; j++) gel(S, Oi[j]) = gel(PL, i);
+  }
+  return S;
+}
+
+/*Usually mod > den so there is no need to reduce it.*/
+GEN
+vandermondeinversemod(GEN L, GEN T, GEN den, GEN mod)
+{
+  pari_sp av;
+  long i, n = lg(L);
+  GEN P, Tp, M = cgetg(n, t_MAT);
+  av = avma;
+  Tp = gclone(FpX_deriv(T,mod)); /*clone*/
+  avma = av;
+  for (i = 1; i < n; i++)
+  {
+    GEN z;
+    av = avma;
+    z = Fp_inv(FpX_eval(Tp, gel(L,i),mod),mod);
+    z = Fp_mul(den,z,mod);
+    P = FpX_Fp_mul(FpX_div_by_X_x(T, gel(L,i), mod, NULL), z, mod);
+    gel(M,i) = gerepilecopy(av, RgX_to_RgV(P, n-1));
+  }
+  gunclone(Tp); /*unclone*/
+  return M;
+}
+
+/* Polynomial associated to a vector of conjugates. Not stack clean */
+static GEN
+vectopol(GEN v, GEN M, GEN den , GEN mod, GEN mod2, long x)
+{
+  long l = lg(v)+1, i;
+  GEN z = cgetg(l,t_POL);
+  z[1] = evalsigne(1)|evalvarn(x);
+  for (i=2; i<l; i++)
+    gel(z,i) = gdiv(centermodii(ZMrow_ZC_mul(M,v,i-1), mod, mod2), den);
+  return normalizepol_lg(z, l);
+}
+
+/* Polynomial associate to a permutation of the roots. Not stack clean */
+static GEN
+permtopol(GEN p, GEN L, GEN M, GEN den, GEN mod, GEN mod2, long x)
+{
+  if (lg(p) != lg(L)) pari_err_TYPE("permtopol [permutation]", p);
+  return vectopol(vecpermute(L,p), M, den, mod, mod2, x);
+}
+
+static GEN
+galoisgrouptopol(GEN res, GEN L, GEN M, GEN den, GEN mod, long v)
+{
+  long i, l = lg(res);
+  GEN mod2 = shifti(mod,-1), aut = cgetg(l, t_COL);
+  for (i = 1; i < l; i++)
+  {
+    if (DEBUGLEVEL>=6) err_printf("%d ",i);
+    gel(aut,i) = permtopol(gel(res,i), L, M, den, mod, mod2, v);
+  }
+  return aut;
+}
+
+static void
+notgalois(long p, struct galois_analysis *ga)
+{
+  if (DEBUGLEVEL >= 2) err_printf("GaloisAnalysis:non Galois for p=%ld\n", p);
+  ga->p = p;
+  ga->deg = 0;
+}
+
+#define numberof(x) (long)(sizeof(x) / sizeof((x)[0]))
+
+/*Gather information about the group*/
+static long
+init_group(long n, long np, GEN Fp, GEN Fe, long *porder)
+{
+  /*TODO: complete the table to at least 200*/
+  const long prim_nonss_orders[] = { 36,48,56,60,72,75,80,96,108,120,132 };
+  long i, phi_order = 1, order = 1, group = 0;
+
+ /* non-WSS groups of this order? */
+  for (i=0; i < numberof(prim_nonss_orders); i++)
+    if (n % prim_nonss_orders[i] == 0) { group |= ga_non_wss; break; }
+  if (np == 2 && Fp[2] == 3 && Fe[2] == 1 && Fe[1] > 2) group |= ga_ext_2;
+
+  for (i = np; i > 0; i--)
+  {
+    long p = Fp[i];
+    if (phi_order % p == 0) { group |= ga_all_normal; break; }
+    order *= p; phi_order *= p-1;
+    if (Fe[i] > 1) break;
+  }
+  *porder = order; return group;
+}
+
+/*is a "better" than b ? (if so, update karma) */
+static int
+improves(long a, long b, long plift, long p, long n, long *karma)
+{
+  if (!plift || a > b) { *karma = ugcd(p-1,n); return 1; }
+  if (a == b) {
+    long k = ugcd(p-1,n);
+    if (k > *karma) { *karma = k; return 1; }
+  }
+  return 0; /* worse */
+}
+
+/* return 0 if not galois or not wss */
+static int
+galoisanalysis(GEN T, struct galois_analysis *ga, long calcul_l)
+{
+  pari_sp ltop = avma, av;
+  long group, linf, n, p, i, karma = 0;
+  GEN F, Fp, Fe, Fpe, O;
+  long np, order, plift, nbmax, nbtest, deg;
+  pari_timer ti;
+  forprime_t S;
+  if (DEBUGLEVEL >= 1) timer_start(&ti);
+  n = degpol(T);
+  O = zero_zv(n);
+  F = factoru_pow(n);
+  Fp = gel(F,1); np = lg(Fp)-1;
+  Fe = gel(F,2);
+  Fpe= gel(F,3);
+  group = init_group(n, np, Fp, Fe, &order);
+
+  /*Now we study the orders of the Frobenius elements*/
+  deg = Fp[np]; /* largest prime | n */
+  plift = 0;
+  nbtest = 0;
+  nbmax = 8+(n>>1);
+  u_forprime_init(&S, n*maxss(expu(n)-3, 2), ULONG_MAX);
+  av = avma;
+  while (!plift || (nbtest < nbmax && (nbtest <=8 || order < (n>>1)))
+                || (n == 24 && O[6] == 0 && O[4] == 0)
+                || ((group&ga_non_wss) && order == Fp[np]))
+  {
+    long d, o, norm_o = 1;
+    GEN D, Tp;
+
+    if ((group&ga_non_wss) && nbtest >= 3*nbmax) break; /* in all cases */
+    nbtest++; avma = av;
+    p = u_forprime_next(&S);
+    if (!p) pari_err_OVERFLOW("galoisanalysis [ran out of primes]");
+    Tp = ZX_to_Flx(T,p);
+    if (!Flx_is_squarefree(Tp,p)) { if (!--nbtest) nbtest = 1; continue; }
+
+    D = Flx_nbfact_by_degree(Tp, &d, p);
+    o = n / d; /* d factors, all should have degree o */
+    if (D[o] != d) { notgalois(p, ga); avma = ltop; return 0; }
+
+    if (!O[o]) O[o] = p;
+    if (o % deg) goto ga_end; /* NB: deg > 1 */
+    if ((group&ga_all_normal) && o < order) goto ga_end;
+
+    /*Frob_p has order o > 1, find a power which generates a normal subgroup*/
+    if (o * Fp[1] >= n)
+      norm_o = o; /*subgroups of smallest index are normal*/
+    else
+    {
+      for (i = np; i > 0; i--)
+      {
+        if (o % Fpe[i]) break;
+        norm_o *= Fpe[i];
+      }
+    }
+    /* Frob_p^(o/norm_o) generates a normal subgroup of order norm_o */
+    if (norm_o != 1)
+    {
+      if (!(group&ga_all_normal) || o > order)
+        karma = ugcd(p-1,n);
+      else if (!improves(norm_o, deg, plift,p,n, &karma)) goto ga_end;
+      /* karma0=0, deg0<=norm_o -> the first improves() returns 1 */
+      deg = norm_o; group |= ga_all_normal; /* STORE */
+    }
+    else if (group&ga_all_normal) goto ga_end;
+    else if (!improves(o, order, plift,p,n, &karma)) goto ga_end;
+
+    order = o; plift = p; /* STORE */
+    ga_end:
+    if (DEBUGLEVEL >= 5)
+      err_printf("GaloisAnalysis:Nbtest=%ld,p=%ld,o=%ld,n_o=%d,best p=%ld,ord=%ld,k=%ld\n", nbtest, p, o, norm_o, plift, order,karma);
+  }
+  /* To avoid looping on non-wss group.
+   * TODO: check for large groups. Would it be better to disable this check if
+   * we are in a good case (ga_all_normal && !(ga_ext_2) (e.g. 60)) ?*/
+  ga->p = plift;
+  if (!plift || ((group&ga_non_wss) && order == Fp[np]))
+  {
+    pari_warn(warner,"Galois group almost certainly not weakly super solvable");
+    return 0;
+  }
+  /*linf=(n*(n-1))>>2;*/
+  linf = n;
+  if (calcul_l && O[1] <= linf)
+  {
+    pari_sp av2;
+    forprime_t S2;
+    ulong p;
+    u_forprime_init(&S2, linf+1,ULONG_MAX);
+    av2 = avma;
+    while ((p = u_forprime_next(&S2)))
+    { /*find a totally split prime l > linf*/
+      GEN Tp = ZX_to_Flx(T, p);
+      long nb = Flx_nbroots(Tp, p);
+      if (nb == n) { O[1] = p; break; }
+      if (nb && Flx_is_squarefree(Tp,p)) {
+        notgalois(p,ga);
+        avma = ltop; return 0;
+      }
+      avma = av2;
+    }
+    if (!p) pari_err_OVERFLOW("galoisanalysis [ran out of primes]");
+  }
+  ga->group = (enum ga_code)group;
+  ga->deg = deg;
+  ga->ord = order;
+  ga->l  = O[1];
+  ga->p4 = n >= 4 ? O[4] : 0;
+  if (DEBUGLEVEL >= 4)
+    err_printf("GaloisAnalysis:p=%ld l=%ld group=%ld deg=%ld ord=%ld\n",
+               plift, O[1], group, deg, order);
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "galoisanalysis()");
+  avma = ltop; return 1;
+}
+
+static GEN
+a4galoisgen(struct galois_test *td)
+{
+  const long n = 12;
+  pari_sp ltop = avma, av, av2;
+  long i, j, k, N, hop = 0;
+  GEN MT, O,O1,O2,O3, ar, mt, t, u, res, orb, pft, pfu, pfv;
+
+  res = cgetg(3, t_VEC);
+  pft = cgetg(n+1, t_VECSMALL);
+  pfu = cgetg(n+1, t_VECSMALL);
+  pfv = cgetg(n+1, t_VECSMALL);
+  gel(res,1) = mkvec3(pft,pfu,pfv);
+  gel(res,2) = mkvecsmall3(2,2,3);
+  av = avma;
+  ar = cgetg(5, t_VECSMALL);
+  mt = gel(td->PV, td->order[n]);
+  t = identity_perm(n) + 1; /* Sorry for this hack */
+  u = cgetg(n+1, t_VECSMALL) + 1; /* too lazy to correct */
+  MT = cgetg(n+1, t_MAT);
+  for (j = 1; j <= n; j++) gel(MT,j) = cgetg(n+1, t_VECSMALL);
+  for (j = 1; j <= n; j++)
+    for (i = 1; i < j; i++)
+      ucoeff(MT,i,j) = ucoeff(MT,j,i) = ucoeff(mt,i,j)+ucoeff(mt,j,i);
+  /* MT(i,i) unused */
+
+  av2 = avma;
+  /* N = itos(gdiv(mpfact(n), mpfact(n >> 1))) >> (n >> 1); */
+  /* n = 2k = 12; N = (2k)! / (k! * 2^k) = 10395 */
+  N = 10395;
+  if (DEBUGLEVEL>=4) err_printf("A4GaloisConj:will test %ld permutations\n", N);
+  uel(ar,4) = umael(MT,11,12);
+  uel(ar,3) = uel(ar,4) + umael(MT,9,10);
+  uel(ar,2) = uel(ar,3) + umael(MT,7,8);
+  uel(ar,1) = uel(ar,2) + umael(MT,5,6);
+  for (i = 0; i < N; i++)
+  {
+    long g;
+    if (i)
+    {
+      long a, x = i, y = 1;
+      do { y += 2; a = x%y; x = x/y; } while (!a);
+      switch (y)
+      {
+      case 3:
+        lswap(t[2], t[2-a]);
+        break;
+      case 5:
+        x = t[0]; t[0] = t[2]; t[2] = t[1]; t[1] = x;
+        lswap(t[4], t[4-a]);
+        uel(ar,1) = uel(ar,2) + umael(MT,t[4],t[5]);
+        break;
+      case 7:
+        x = t[0]; t[0] = t[4]; t[4] = t[3]; t[3] = t[1]; t[1] = t[2]; t[2] = x;
+        lswap(t[6], t[6-a]);
+        uel(ar,2) = uel(ar,3) + umael(MT,t[6],t[7]);
+        uel(ar,1) = uel(ar,2) + umael(MT,t[4],t[5]);
+        break;
+      case 9:
+        x = t[0]; t[0] = t[6]; t[6] = t[5]; t[5] = t[3]; t[3] = x;
+        lswap(t[1], t[4]);
+        lswap(t[8], t[8-a]);
+        uel(ar,3) = uel(ar,4) + umael(MT,t[8],t[9]);
+        uel(ar,2) = uel(ar,3) + umael(MT,t[6],t[7]);
+        uel(ar,1) = uel(ar,2) + umael(MT,t[4],t[5]);
+        break;
+      case 11:
+        x = t[0]; t[0] = t[8]; t[8] = t[7]; t[7] = t[5]; t[5] = t[1];
+        t[1] = t[6]; t[6] = t[3]; t[3] = t[2]; t[2] = t[4]; t[4] = x;
+        lswap(t[10], t[10-a]);
+        uel(ar,4) = umael(MT,t[10],t[11]);
+        uel(ar,3) = uel(ar,4) + umael(MT,t[8],t[9]);
+        uel(ar,2) = uel(ar,3) + umael(MT,t[6],t[7]);
+        uel(ar,1) = uel(ar,2) + umael(MT,t[4],t[5]);
+      }
+    }
+    g = uel(ar,1)+umael(MT,t[0],t[1])+umael(MT,t[2],t[3]);
+    if (headlongisint(g,n))
+    {
+      for (k = 0; k < n; k += 2)
+      {
+        pft[t[k]] = t[k+1];
+        pft[t[k+1]] = t[k];
+      }
+      if (galois_test_perm(td, pft)) break;
+      hop++;
+    }
+    avma = av2;
+  }
+  if (DEBUGLEVEL >= 1 && hop)
+    err_printf("A4GaloisConj: %ld hop over %ld iterations\n", hop, N);
+  if (i == N) { avma = ltop; return gen_0; }
+  /* N = itos(gdiv(mpfact(n >> 1), mpfact(n >> 2))) >> 1; */
+  N = 60;
+  if (DEBUGLEVEL >= 4) err_printf("A4GaloisConj:sigma=%Ps \n", pft);
+  for (k = 0; k < n; k += 4)
+  {
+    u[k+3] = t[k+3];
+    u[k+2] = t[k+1];
+    u[k+1] = t[k+2];
+    u[k]   = t[k];
+  }
+  for (i = 0; i < N; i++)
+  {
+    ulong g = 0;
+    if (i)
+    {
+      long a, x = i, y = -2;
+      do { y += 4; a = x%y; x = x/y; } while (!a);
+      lswap(u[0],u[2]);
+      switch (y)
+      {
+      case 2:
+        break;
+      case 6:
+        lswap(u[4],u[6]);
+        if (!(a & 1))
+        {
+          a = 4 - (a>>1);
+          lswap(u[6], u[a]);
+          lswap(u[4], u[a-2]);
+        }
+        break;
+      case 10:
+        x = u[6];
+        u[6] = u[3];
+        u[3] = u[2];
+        u[2] = u[4];
+        u[4] = u[1];
+        u[1] = u[0];
+        u[0] = x;
+        if (a >= 3) a += 2;
+        a = 8 - a;
+        lswap(u[10],u[a]);
+        lswap(u[8], u[a-2]);
+        break;
+      }
+    }
+    for (k = 0; k < n; k += 2) g += mael(MT,u[k],u[k+1]);
+    if (headlongisint(g,n))
+    {
+      for (k = 0; k < n; k += 2)
+      {
+        pfu[u[k]] = u[k+1];
+        pfu[u[k+1]] = u[k];
+      }
+      if (galois_test_perm(td, pfu)) break;
+      hop++;
+    }
+    avma = av2;
+  }
+  if (i == N) { avma = ltop; return gen_0; }
+  if (DEBUGLEVEL >= 1 && hop)
+    err_printf("A4GaloisConj: %ld hop over %ld iterations\n", hop, N);
+  if (DEBUGLEVEL >= 4) err_printf("A4GaloisConj:tau=%Ps \n", pfu);
+  avma = av2;
+  orb = mkvec2(pft,pfu);
+  O = vecperm_orbits(orb, 12);
+  if (DEBUGLEVEL >= 4) {
+    err_printf("A4GaloisConj:orb=%Ps\n", orb);
+    err_printf("A4GaloisConj:O=%Ps \n", O);
+  }
+  av2 = avma;
+  O1 = gel(O,1); O2 = gel(O,2); O3 = gel(O,3);
+  for (j = 0; j < 2; j++)
+  {
+    pfv[O1[1]] = O2[1];
+    pfv[O1[2]] = O2[3+j];
+    pfv[O1[3]] = O2[4 - (j << 1)];
+    pfv[O1[4]] = O2[2+j];
+    for (i = 0; i < 4; i++)
+    {
+      ulong g = 0;
+      switch (i)
+      {
+      case 0: break;
+      case 1: lswap(O3[1], O3[2]); lswap(O3[3], O3[4]); break;
+      case 2: lswap(O3[1], O3[4]); lswap(O3[2], O3[3]); break;
+      case 3: lswap(O3[1], O3[2]); lswap(O3[3], O3[4]); break;
+      }
+      pfv[O2[1]]          = O3[1];
+      pfv[O2[3+j]]        = O3[4-j];
+      pfv[O2[4 - (j<<1)]] = O3[2 + (j<<1)];
+      pfv[O2[2+j]]        = O3[3-j];
+      pfv[O3[1]]          = O1[1];
+      pfv[O3[4-j]]        = O1[2];
+      pfv[O3[2 + (j<<1)]] = O1[3];
+      pfv[O3[3-j]]        = O1[4];
+      for (k = 1; k <= n; k++) g += mael(mt,k,pfv[k]);
+      if (headlongisint(g,n) && galois_test_perm(td, pfv))
+      {
+        avma = av;
+        if (DEBUGLEVEL >= 1)
+          err_printf("A4GaloisConj:%ld hop over %d iterations max\n",
+                     hop, 10395 + 68);
+        return res;
+      }
+      hop++; avma = av2;
+    }
+  }
+  avma = ltop; return gen_0; /* Fail */
+}
+
+/* S4 */
+static void
+s4makelift(GEN u, struct galois_lift *gl, GEN liftpow)
+{
+  GEN s = automorphismlift(u, gl, NULL);
+  long i;
+  gel(liftpow,1) = s;
+  for (i = 2; i < lg(liftpow); i++)
+    gel(liftpow,i) = FpXQ_mul(gel(liftpow,i-1), s, gl->TQ, gl->Q);
+}
+static long
+s4test(GEN u, GEN liftpow, struct galois_lift *gl, GEN phi)
+{
+  pari_sp av = avma;
+  GEN res, Q, Q2;
+  long bl, i, d = lg(u)-2;
+  pari_timer ti;
+  if (DEBUGLEVEL >= 6) timer_start(&ti);
+  if (!d) return 0;
+  Q = gl->Q; Q2 = shifti(Q,-1);
+  res = gel(u,2);
+  for (i = 1; i < d; i++)
+    if (lg(gel(liftpow,i))>2)
+      res = addii(res, mulii(gmael(liftpow,i,2), gel(u,i+2)));
+  res = remii(res,Q);
+  if (gl->den != gen_1) res = mulii(res, gl->den);
+  res = centermodii(res, Q,Q2);
+  if (absi_cmp(res, gl->gb->bornesol) > 0) { avma = av; return 0; }
+  res = scalar_ZX_shallow(gel(u,2),varn(u));
+  for (i = 1; i < d ; i++)
+    if (lg(gel(liftpow,i))>2)
+      res = ZX_add(res, ZX_Z_mul(gel(liftpow,i), gel(u,i+2)));
+  res = FpX_red(res, Q);
+  if (gl->den != gen_1) res = FpX_Fp_mul(res, gl->den, Q);
+  res = FpX_center(res, Q, shifti(Q,-1));
+  bl = poltopermtest(res, gl, phi);
+  if (DEBUGLEVEL >= 6) timer_printf(&ti, "s4test()");
+  avma = av; return bl;
+}
+
+static GEN
+aux(long a, long b, GEN T, GEN M, GEN p, GEN *pu)
+{
+  *pu = FpX_mul(gel(T,b), gel(T,a),p);
+  return FpX_chinese_coprime(gmael(M,a,b), gmael(M,b,a),
+                             gel(T,b), gel(T,a), *pu, p);
+}
+
+static GEN
+s4releveauto(GEN misom,GEN Tmod,GEN Tp,GEN p,long a1,long a2,long a3,long a4,long a5,long a6)
+{
+  pari_sp av = avma;
+  GEN u4,u5;
+  GEN pu1, pu2, pu3, pu4;
+  GEN u1 = aux(a1, a2, Tmod, misom, p, &pu1);
+  GEN u2 = aux(a3, a4, Tmod, misom, p, &pu2);
+  GEN u3 = aux(a5, a6, Tmod, misom, p, &pu3);
+  pu4 = FpX_mul(pu1,pu2,p);
+  u4 = FpX_chinese_coprime(u1,u2,pu1,pu2,pu4,p);
+  u5 = FpX_chinese_coprime(u4,u3,pu4,pu3,Tp,p);
+  return gerepileupto(av, u5);
+}
+static GEN
+lincomb(GEN A, GEN B, GEN pauto, long j)
+{
+  long k = (-j) & 3;
+  if (j == k) return ZX_mul(ZX_add(A,B), gel(pauto, j+1));
+  return ZX_add(ZX_mul(A, gel(pauto, j+1)), ZX_mul(B, gel(pauto, k+1)));
+}
+/* FIXME: could use the intheadlong technique */
+static GEN
+s4galoisgen(struct galois_lift *gl)
+{
+  const long n = 24;
+  struct galois_testlift gt;
+  pari_sp av, ltop2, ltop = avma;
+  long i, j;
+  GEN sigma, tau, phi, res, r1,r2,r3,r4, pj, p = gl->p, Q = gl->Q, TQ = gl->TQ;
+  GEN sg, Tp, Tmod, isom, isominv, misom, Bcoeff, pauto, liftpow, aut;
+
+  res = cgetg(3, t_VEC);
+  r1 = cgetg(n+1, t_VECSMALL);
+  r2 = cgetg(n+1, t_VECSMALL);
+  r3 = cgetg(n+1, t_VECSMALL);
+  r4 = cgetg(n+1, t_VECSMALL);
+  gel(res,1)= mkvec4(r1,r2,r3,r4);
+  gel(res,2) = mkvecsmall4(2,2,3,2);
+  ltop2 = avma;
+  sg = identity_perm(6);
+  pj = zero_zv(6);
+  sigma = cgetg(n+1, t_VECSMALL);
+  tau = cgetg(n+1, t_VECSMALL);
+  phi = cgetg(n+1, t_VECSMALL);
+  Tp = FpX_red(gl->T,p);
+  Tmod = gel(FpX_factor(Tp,p), 1);
+  isom    = cgetg(lg(Tmod), t_VEC);
+  isominv = cgetg(lg(Tmod), t_VEC);
+  misom   = cgetg(lg(Tmod), t_MAT);
+  aut = galoisdolift(gl, NULL);
+  inittestlift(aut, Tmod, gl, &gt);
+  Bcoeff = gt.bezoutcoeff;
+  pauto = gt.pauto;
+  for (i = 1; i < lg(isom); i++)
+  {
+    gel(misom,i) = cgetg(lg(Tmod), t_COL);
+    gel(isom,i) = FpX_ffisom(gel(Tmod,1), gel(Tmod,i), p);
+    if (DEBUGLEVEL >= 6)
+      err_printf("S4GaloisConj:Computing isomorphisms %d:%Ps\n", i,
+                 gel(isom,i));
+    gel(isominv,i) = FpXQ_ffisom_inv(gel(isom,i), gel(Tmod,i),p);
+  }
+  for (i = 1; i < lg(isom); i++)
+    for (j = 1; j < lg(isom); j++)
+      gmael(misom,i,j) = FpX_FpXQ_eval(gel(isominv,i),gel(isom,j),
+                                         gel(Tmod,j),p);
+  liftpow = cgetg(24, t_VEC);
+  av = avma;
+  for (i = 0; i < 3; i++, avma = av)
+  {
+    pari_sp av1, av2, av3;
+    GEN u, u1, u2, u3;
+    long j1, j2, j3;
+    if (i)
+    {
+      if (i == 1) { lswap(sg[2],sg[3]); }
+      else        { lswap(sg[1],sg[3]); }
+    }
+    u = s4releveauto(misom,Tmod,Tp,p,sg[1],sg[2],sg[3],sg[4],sg[5],sg[6]);
+    s4makelift(u, gl, liftpow);
+    av1 = avma;
+    for (j1 = 0; j1 < 4; j1++, avma = av1)
+    {
+      u1 = lincomb(gel(Bcoeff,sg[5]),gel(Bcoeff,sg[6]), pauto,j1);
+      u1 = FpX_rem(u1, TQ, Q); av2 = avma;
+      for (j2 = 0; j2 < 4; j2++, avma = av2)
+      {
+        u2 = lincomb(gel(Bcoeff,sg[3]),gel(Bcoeff,sg[4]), pauto,j2);
+        u2 = FpX_rem(FpX_add(u1, u2, Q), TQ,Q); av3 = avma;
+        for (j3 = 0; j3 < 4; j3++, avma = av3)
+        {
+          u3 = lincomb(gel(Bcoeff,sg[1]),gel(Bcoeff,sg[2]), pauto,j3);
+          u3 = FpX_rem(FpX_add(u2, u3, Q), TQ,Q);
+          if (DEBUGLEVEL >= 4)
+            err_printf("S4GaloisConj:Testing %d/3:%d/4:%d/4:%d/4:%Ps\n",
+                       i,j1,j2,j3, sg);
+          if (s4test(u3, liftpow, gl, sigma))
+          {
+            pj[1] = j3;
+            pj[2] = j2;
+            pj[3] = j1; goto suites4;
+          }
+        }
+      }
+    }
+  }
+  avma = ltop; return gen_0;
+suites4:
+  if (DEBUGLEVEL >= 4) err_printf("S4GaloisConj:sigma=%Ps\n", sigma);
+  if (DEBUGLEVEL >= 4) err_printf("S4GaloisConj:pj=%Ps\n", pj);
+  avma = av;
+  for (j = 1; j <= 3; j++)
+  {
+    pari_sp av2, av3;
+    GEN u;
+    long w, l, z;
+    z = sg[1]; sg[1] = sg[3]; sg[3] = sg[5]; sg[5] = z;
+    z = sg[2]; sg[2] = sg[4]; sg[4] = sg[6]; sg[6] = z;
+    z = pj[1]; pj[1] = pj[2]; pj[2] = pj[3]; pj[3] = z;
+    for (l = 0; l < 2; l++, avma = av)
+    {
+      u = s4releveauto(misom,Tmod,Tp,p,sg[1],sg[3],sg[2],sg[4],sg[5],sg[6]);
+      s4makelift(u, gl, liftpow);
+      av2 = avma;
+      for (w = 0; w < 4; w += 2, avma = av2)
+      {
+        GEN uu;
+        pj[6] = (w + pj[3]) & 3;
+        uu = lincomb(gel(Bcoeff,sg[5]),gel(Bcoeff,sg[6]), pauto, pj[6]);
+        uu = FpX_rem(FpX_red(uu,Q), TQ, Q);
+        av3 = avma;
+        for (i = 0; i < 4; i++, avma = av3)
+        {
+          GEN u;
+          pj[4] = i;
+          pj[5] = (i + pj[2] - pj[1]) & 3;
+          if (DEBUGLEVEL >= 4)
+            err_printf("S4GaloisConj:Testing %d/3:%d/2:%d/2:%d/4:%Ps:%Ps\n",
+                       j-1, w >> 1, l, i, sg, pj);
+          u = ZX_add(lincomb(gel(Bcoeff,sg[1]),gel(Bcoeff,sg[3]), pauto,pj[4]),
+                     lincomb(gel(Bcoeff,sg[2]),gel(Bcoeff,sg[4]), pauto,pj[5]));
+          u = FpX_rem(FpX_add(uu,u,Q), TQ, Q);
+          if (s4test(u, liftpow, gl, tau)) goto suites4_2;
+        }
+      }
+      lswap(sg[3], sg[4]);
+      pj[2] = (-pj[2]) & 3;
+    }
+  }
+  avma = ltop; return gen_0;
+suites4_2:
+  avma = av;
+  {
+    long abc = (pj[1] + pj[2] + pj[3]) & 3;
+    long abcdef = ((abc + pj[4] + pj[5] - pj[6]) & 3) >> 1;
+    GEN u;
+    pari_sp av2;
+    u = s4releveauto(misom,Tmod,Tp,p,sg[1],sg[4],sg[2],sg[5],sg[3],sg[6]);
+    s4makelift(u, gl, liftpow);
+    av2 = avma;
+    for (j = 0; j < 8; j++)
+    {
+      long h, g, i;
+      h = j & 3;
+      g = (abcdef + ((j & 4) >> 1)) & 3;
+      i = (h + abc - g) & 3;
+      u = ZX_add(   lincomb(gel(Bcoeff,sg[1]), gel(Bcoeff,sg[4]), pauto, g),
+                    lincomb(gel(Bcoeff,sg[2]), gel(Bcoeff,sg[5]), pauto, h));
+      u = FpX_add(u, lincomb(gel(Bcoeff,sg[3]), gel(Bcoeff,sg[6]), pauto, i),Q);
+      u = FpX_rem(u, TQ, Q);
+      if (DEBUGLEVEL >= 4)
+        err_printf("S4GaloisConj:Testing %d/8 %d:%d:%d\n", j,g,h,i);
+      if (s4test(u, liftpow, gl, phi)) break;
+      avma = av2;
+    }
+  }
+  if (j == 8) { avma = ltop; return gen_0; }
+  for (i = 1; i <= n; i++)
+  {
+    r1[i] = sigma[tau[i]];
+    r2[i] = phi[sigma[tau[phi[i]]]];
+    r3[i] = phi[sigma[i]];
+    r4[i] = sigma[i];
+  }
+  avma = ltop2; return res;
+}
+
+static GEN
+galoisfindgroups(GEN lo, GEN sg, long f)
+{
+  pari_sp ltop = avma;
+  long i, j, k;
+  GEN V = cgetg(lg(lo), t_VEC);
+  for(j=1,i=1; i<lg(lo); i++)
+  {
+    pari_sp av = avma;
+    GEN loi = gel(lo,i), W = cgetg(lg(loi),t_VECSMALL);
+    for (k=1; k<lg(loi); k++) W[k] = loi[k] % f;
+    W = vecsmall_uniq(W);
+    if (zv_equal(W, sg)) gel(V,j++) = loi;
+    avma = av;
+  }
+  setlg(V,j); return gerepilecopy(ltop,V);
+}
+
+static long
+galoisfrobeniustest(GEN aut, struct galois_lift *gl, GEN frob)
+{
+  pari_sp av = avma;
+  GEN tlift = aut;
+  long res;
+  if (gl->den != gen_1) tlift = FpX_Fp_mul(tlift, gl->den, gl->Q);
+  tlift = FpX_center(tlift, gl->Q, shifti(gl->Q,-1));
+  res = poltopermtest(tlift, gl, frob);
+  avma = av; return res;
+}
+
+static GEN
+galoismakepsi(long g, GEN sg, GEN pf)
+{
+  GEN psi=cgetg(g+1,t_VECSMALL);
+  long i;
+  for (i = 1; i < g; i++) psi[i] = sg[pf[i]];
+  psi[g] = sg[1]; return psi;
+}
+
+static GEN
+galoisfrobeniuslift(GEN T, GEN den, GEN L,  GEN Lden,
+    struct galois_frobenius *gf,  struct galois_borne *gb)
+{
+  pari_sp ltop=avma, av2;
+  struct galois_testlift gt;
+  struct galois_lift gl;
+  long i, j, k, n = lg(L)-1, deg = 1, g = lg(gf->Tmod)-1;
+  GEN F,Fp,Fe, aut, frob, ip = utoipos(gf->p), res = cgetg(lg(L), t_VECSMALL);
+  if (DEBUGLEVEL >= 4)
+    err_printf("GaloisConj:p=%ld deg=%ld fp=%ld\n", gf->p, deg, gf->fp);
+  gf->psi = const_vecsmall(g,1);
+  av2 = avma;
+  initlift(T, den, ip, L, Lden, gb, &gl);
+  aut = galoisdolift(&gl, res);
+  if (!aut || galoisfrobeniustest(aut,&gl,res))
+  {
+    avma = av2; gf->deg = gf->fp; return res;
+  }
+  inittestlift(aut,gf->Tmod, &gl, &gt);
+  gt.C = cgetg(gf->fp+1,t_VEC);
+  gt.Cd= cgetg(gf->fp+1,t_VEC);
+  for (i = 1; i <= gf->fp; i++) {
+    gel(gt.C,i)  = zero_zv(gt.g);
+    gel(gt.Cd,i) = zero_zv(gt.g);
+  }
+
+  F =factoru(gf->fp);
+  Fp = gel(F,1);
+  Fe = gel(F,2);
+  frob = cgetg(lg(L), t_VECSMALL);
+  for(k=lg(Fp)-1;k>=1;k--)
+  {
+    pari_sp btop=avma;
+    GEN psi=NULL, fres=NULL, sg = identity_perm(1);
+    long el=gf->fp, dg=1, dgf=1, e, pr;
+    for(e=1; e<=Fe[k]; e++)
+    {
+      GEN lo, pf;
+      long l;
+      dg *= Fp[k]; el /= Fp[k];
+      if (DEBUGLEVEL>=4) err_printf("Trying degre %d.\n",dg);
+      if (galoisfrobeniustest(gel(gt.pauto,el+1),&gl,frob))
+      {
+        psi = const_vecsmall(g,1);
+        dgf = dg; fres = gcopy(frob); continue;
+      }
+      lo = listznstarelts(dg, n / gf->fp);
+      if (e!=1) lo = galoisfindgroups(lo, sg, dgf);
+      if (DEBUGLEVEL>=4) err_printf("Galoisconj:Subgroups list:%Ps\n", lo);
+      for (l = 1; l < lg(lo); l++)
+        if (lg(gel(lo,l))>2 && frobeniusliftall(gel(lo,l), el, &pf,&gl,&gt, frob))
+        {
+          sg  = gcopy(gel(lo,l));
+          psi = galoismakepsi(g,sg,pf);
+          dgf = dg; fres = gcopy(frob); break;
+        }
+      if (l == lg(lo)) break;
+    }
+    if (dgf == 1) { avma = btop; continue; }
+    pr = deg*dgf;
+    if (deg == 1)
+    {
+      for(i=1;i<lg(res);i++) res[i]=fres[i];
+      for(i=1;i<lg(psi);i++) gf->psi[i]=psi[i];
+    }
+    else
+    {
+      GEN cp = perm_mul(res,fres);
+      for(i=1;i<lg(res);i++) res[i] = cp[i];
+      for(i=1;i<lg(psi);i++) gf->psi[i] = (dgf*gf->psi[i] + deg*psi[i]) % pr;
+    }
+    deg = pr; avma = btop;
+  }
+  for (i = 1; i <= gf->fp; i++)
+    for (j = 1; j <= gt.g; j++)
+      if (mael(gt.C,i,j)) gunclone(gmael(gt.C,i,j));
+  if (DEBUGLEVEL>=4 && res) err_printf("Best lift: %d\n",deg);
+  if (deg==1) { avma = ltop; return NULL; }
+  else
+  {
+    /* Normalize result so that psi[g]=1 */
+    long im = Fl_inv(gf->psi[g], deg);
+    GEN cp = perm_pow(res, im);
+    for(i=1;i<lg(res);i++) res[i] = cp[i];
+    for(i=1;i<lg(gf->psi);i++) gf->psi[i] = Fl_mul(im, gf->psi[i], deg);
+    avma = av2; gf->deg = deg; return res;
+  }
+}
+
+/* return NULL if not Galois */
+static GEN
+galoisfindfrobenius(GEN T, GEN L, GEN den, struct galois_frobenius *gf,
+    struct galois_borne *gb, const struct galois_analysis *ga)
+{
+  pari_sp ltop = avma, av;
+  long Try = 0, n = degpol(T), deg, gmask = (ga->group&ga_ext_2)? 3: 1;
+  GEN frob, Lden = makeLden(L,den,gb);
+  forprime_t S;
+
+  u_forprime_init(&S, ga->p, ULONG_MAX);
+  av = avma;
+  deg = gf->deg = ga->deg;
+  while ((gf->p = u_forprime_next(&S)))
+  {
+    pari_sp lbot;
+    GEN Ti, Tp;
+    long nb, d;
+    avma = av;
+    Tp = ZX_to_Flx(T, gf->p);
+    if (!Flx_is_squarefree(Tp, gf->p)) continue;
+    Ti = gel(Flx_factor(Tp, gf->p), 1);
+    nb = lg(Ti)-1; d = degpol(gel(Ti,1));
+    if (nb > 1 && degpol(gel(Ti,nb)) != d) { avma = ltop; return NULL; }
+    if (((gmask&1)==0 || d % deg) && ((gmask&2)==0 || odd(d))) continue;
+    if (DEBUGLEVEL >= 1) err_printf("GaloisConj: Trying p=%ld\n", gf->p);
+    FlxV_to_ZXV_inplace(Ti);
+    gf->fp = d;
+    gf->Tmod = Ti; lbot = avma;
+    frob = galoisfrobeniuslift(T, den, L, Lden, gf, gb);
+    if (frob)
+    {
+      GEN *gptr[3];
+      gf->Tmod = gcopy(Ti);
+      gptr[0]=&gf->Tmod; gptr[1]=&gf->psi; gptr[2]=&frob;
+      gerepilemanysp(ltop,lbot,gptr,3); return frob;
+    }
+    if ((ga->group&ga_all_normal) && d % deg == 0) gmask &= ~1;
+    /* The first prime degree is always divisible by deg, so we don't
+     * have to worry about ext_2 being used before regular supersolvable*/
+    if (!gmask) { avma = ltop; return NULL; }
+    if ((ga->group&ga_non_wss) && ++Try > ((3*n)>>1))
+    {
+      pari_warn(warner,"Galois group probably not weakly super solvable");
+      return NULL;
+    }
+  }
+  if (!gf->p) pari_err_OVERFLOW("galoisfindfrobenius [ran out of primes]");
+  return NULL;
+}
+
+/* compute g such that tau(Pmod[#])= tau(Pmod[g]) */
+
+static long
+get_image(GEN tau, GEN P, GEN Pmod, GEN p)
+{
+  pari_sp av = avma;
+  long g, gp = lg(Pmod)-1;
+  tau = RgX_to_FpX(tau, p);
+  tau = FpX_FpXQ_eval(gel(Pmod, gp), tau, P, p);
+  tau = FpX_normalize(FpX_gcd(P, tau, p), p);
+  for (g = 1; g <= gp; g++)
+    if (ZX_equal(tau, gel(Pmod,g))) { avma = av; return g; }
+  avma = av; return 0;
+}
+
+static GEN
+galoisgen(GEN T, GEN L, GEN M, GEN den, struct galois_borne *gb,
+          const struct galois_analysis *ga);
+static GEN
+galoisgenfixedfield(GEN Tp, GEN Pmod, GEN V, GEN ip, struct galois_borne *gb)
+{
+  GEN  P, PL, Pden, PM, Pp;
+  GEN  tau, PG, Pg;
+  long g, lP;
+  long x=varn(Tp);
+  P=gel(V,3);
+  PL=gel(V,2);
+  Pp = FpX_red(P,ip);
+  if (DEBUGLEVEL>=6)
+    err_printf("GaloisConj: Fixed field %Ps\n",P);
+  if (degpol(P)==2)
+  {
+    PG=cgetg(3,t_VEC);
+    gel(PG,1) = mkvec( mkvecsmall2(2,1) );
+    gel(PG,2) = mkvecsmall(2);
+    tau = deg1pol_shallow(gen_m1, negi(gel(P,3)), x);
+    g = get_image(tau, Pp, Pmod, ip);
+    if (!g) return NULL;
+    Pg = mkvecsmall(g);
+  }
+  else
+  {
+    struct galois_analysis Pga;
+    struct galois_borne Pgb;
+    GEN mod, mod2;
+    long j;
+    if (!galoisanalysis(P, &Pga, 0)) return NULL;
+    Pgb.l = gb->l;
+    Pden = galoisborne(P, NULL, &Pgb, degpol(P));
+
+    if (Pgb.valabs > gb->valabs)
+    {
+      if (DEBUGLEVEL>=4)
+        err_printf("GaloisConj:increase prec of p-adic roots of %ld.\n"
+            ,Pgb.valabs-gb->valabs);
+      PL = ZpX_liftroots(P,PL,gb->l,Pgb.valabs);
+    }
+    else if (Pgb.valabs < gb->valabs)
+      PL = FpC_red(PL, Pgb.ladicabs);
+    PM = vandermondeinversemod(PL, P, Pden, Pgb.ladicabs);
+    PG = galoisgen(P, PL, PM, Pden, &Pgb, &Pga);
+    if (PG == gen_0) return NULL;
+    lP = lg(gel(PG,1));
+    mod = Pgb.ladicabs; mod2 = shifti(mod, -1);
+    Pg = cgetg(lP, t_VECSMALL);
+    for (j = 1; j < lP; j++)
+    {
+      pari_sp btop=avma;
+      tau = permtopol(gmael(PG,1,j), PL, PM, Pden, mod, mod2, x);
+      g = get_image(tau, Pp, Pmod, ip);
+      if (!g) return NULL;
+      Pg[j] = g;
+      avma = btop;
+    }
+  }
+  return mkvec2(PG,Pg);
+}
+
+/* Let sigma^m=1,  tau*sigma*tau^-1=sigma^s.
+ * Compute n so that (sigma*tau)^e = sigma^n*tau^e
+ * We have n = sum_{k=0}^{e-1} s^k mod m.
+ * so n*(1-s) = 1-s^e mod m
+ * Unfortunately (1-s) might not invertible mod m.
+ */
+
+static long
+stpow(long s, long e, long m)
+{
+  long i;
+  long n = 1;
+  for (i = 1; i < e; i++)
+    n = (1 + n * s) % m;
+  return n;
+}
+
+static GEN
+wpow(long s, long m, long e, long n)
+{
+  GEN   w = cgetg(n+1,t_VECSMALL);
+  long si = s;
+  long i;
+  w[1] = 1;
+  for(i=2; i<=n; i++) w[i] = w[i-1]*e;
+  for(i=n; i>=1; i--)
+  {
+    si = Fl_powu(si,e,m);
+    w[i] = Fl_mul(s-1, stpow(si, w[i], m), m);
+  }
+  return w;
+}
+
+static GEN
+galoisgenliftauto(GEN O, GEN gj, long s, long n, struct galois_test *td)
+{
+  pari_sp av = avma;
+  long sr, k;
+  long deg = lg(gel(O,1))-1;
+  GEN  X  = cgetg(lg(O), t_VECSMALL);
+  GEN  oX = cgetg(lg(O), t_VECSMALL);
+  GEN  B  = perm_cycles(gj);
+  long oj = lg(gel(B,1)) - 1;
+  GEN  F  = factoru(oj);
+  GEN  Fp = gel(F,1);
+  GEN  Fe = gel(F,2);
+  GEN  pf = identity_perm(n);
+  if (DEBUGLEVEL >= 6)
+    err_printf("GaloisConj: %Ps of relative order %d\n", gj, oj);
+  for (k=lg(Fp)-1; k>=1; k--)
+  {
+    long f, dg = 1, el = oj, osel = 1, a = 0;
+    long p  = Fp[k], e  = Fe[k], op = oj / upowuu(p,e);
+    long i;
+    GEN  pf1 = NULL, w, wg, Be = cgetg(e+1,t_VEC);
+    gel(Be,e) = cyc_pow(B, op);
+    for(i=e-1; i>=1; i--) gel(Be,i) = cyc_pow(gel(Be,i+1), p);
+    w = wpow(Fl_powu(s,op,deg),deg,p,e);
+    wg = cgetg(e+2,t_VECSMALL);
+    wg[e+1] = deg;
+    for (i=e; i>=1; i--) wg[i] = ugcd(wg[i+1], w[i]);
+    for (i=1; i<lg(O); i++) oX[i] = 0;
+    for (f=1; f<=e; f++)
+    {
+      long sel, t;
+      GEN Bel = gel(Be,f);
+      dg *= p; el /= p;
+      sel = Fl_powu(s,el,deg);
+      if (DEBUGLEVEL >= 6) err_printf("GaloisConj: B=%Ps\n", Bel);
+      sr  = cgcd(stpow(sel,p,deg),deg);
+      if (DEBUGLEVEL >= 6)
+        err_printf("GaloisConj: exp %d: s=%ld [%ld] a=%ld w=%ld wg=%ld sr=%ld\n",
+            dg, sel, deg, a, w[f], wg[f+1], sr);
+      for (t = 0; t < sr; t++)
+        if ((a+t*w[f])%wg[f+1]==0)
+        {
+          long i, j, k, st;
+          for (i = 1; i < lg(X); i++) X[i] = 0;
+          for (i = 0; i < lg(X)-1; i+=dg)
+            for (j = 1, k = p, st = t; k <= dg; j++, k += p)
+            {
+              X[k+i] = (oX[j+i] + st)%deg;
+              st = (t + st*osel)%deg;
+            }
+          pf1 = testpermutation(O, Bel, X, sel, p, sr, td);
+          if (pf1) break;
+        }
+      if (!pf1) return NULL;
+      for (i=1; i<lg(O); i++) oX[i] = X[i];
+      osel = sel; a = (a+t*w[f])%deg;
+    }
+    pf = perm_mul(pf, perm_pow(pf1, el));
+  }
+  return gerepileuptoleaf(av, pf);
+}
+
+static GEN
+galoisgen(GEN T, GEN L, GEN M, GEN den, struct galois_borne *gb,
+          const struct galois_analysis *ga)
+{
+  struct galois_test td;
+  struct galois_frobenius gf;
+  pari_sp ltop = avma;
+  long p, deg, x, j, n = degpol(T), lP;
+  GEN sigma, Tmod, res, res1, ip, frob, O, PG, PG1, PG2, Pg;
+
+  if (!ga->deg) return gen_0;
+  x = varn(T);
+  if (DEBUGLEVEL >= 9) err_printf("GaloisConj:denominator:%Ps\n", den);
+  if (n == 12 && ga->ord==3 && !ga->p4)
+  { /* A4 is very probable: test it first */
+    pari_sp av = avma;
+    if (DEBUGLEVEL >= 4) err_printf("GaloisConj:Testing A4 first\n");
+    inittest(L, M, gb->bornesol, gb->ladicsol, &td);
+    PG = a4galoisgen(&td);
+    freetest(&td);
+    if (PG != gen_0) return gerepileupto(ltop, PG);
+    avma = av;
+  }
+  if (n == 24 && ga->ord==3)
+  { /* S4 is very probable: test it first */
+    pari_sp av = avma;
+    struct galois_lift gl;
+    if (DEBUGLEVEL >= 4) err_printf("GaloisConj:Testing S4 first\n");
+    initlift(T, den, stoi(ga->p4), L, makeLden(L,den,gb), gb, &gl);
+    PG = s4galoisgen(&gl);
+    if (PG != gen_0) return gerepileupto(ltop, PG);
+    avma = av;
+  }
+  frob = galoisfindfrobenius(T, L, den, &gf, gb, ga);
+  if (!frob) { avma=ltop; return gen_0; }
+  p = gf.p; ip = utoipos(p);
+  Tmod = gf.Tmod;
+  O = perm_cycles(frob);
+  deg = lg(gel(O,1))-1;
+  if (DEBUGLEVEL >= 9) err_printf("GaloisConj:Orbite:%Ps\n", O);
+  if (deg == n)        /* cyclic */
+    return gerepilecopy(ltop, mkvec2(mkvec(frob), mkvecsmall(deg)));
+  sigma = permtopol(frob, L, M, den, gb->ladicabs, shifti(gb->ladicabs,-1), x);
+  if (DEBUGLEVEL >= 9) err_printf("GaloisConj:Frobenius:%Ps\n", sigma);
+  {
+    pari_sp btop=avma;
+    GEN V, Tp, Sp, Pmod;
+    GEN OL = fixedfieldorbits(O,L);
+    V  = fixedfieldsympol(OL, gb->ladicabs, gb->l, ip, x);
+    Tp = FpX_red(T,ip);
+    Sp = sympol_aut_evalmod(gel(V,1),deg,sigma,Tp,ip);
+    Pmod = fixedfieldfactmod(Sp,ip,Tmod);
+    PG = galoisgenfixedfield(Tp, Pmod, V, ip, gb);
+    if (PG == NULL) { avma = ltop; return gen_0; }
+    if (DEBUGLEVEL >= 4) err_printf("GaloisConj:Back to Earth:%Ps\n", PG);
+    PG=gerepilecopy(btop, PG);
+  }
+  inittest(L, M, gb->bornesol, gb->ladicsol, &td);
+  PG1 = gmael(PG, 1, 1); lP = lg(PG1);
+  PG2 = gmael(PG, 1, 2);
+  Pg = gel(PG, 2);
+  res = cgetg(3, t_VEC);
+  gel(res,1) = res1 = cgetg(lP + 1, t_VEC);
+  gel(res,2) = vecsmall_prepend(PG2, deg);
+  gel(res1, 1) = vecsmall_copy(frob);
+  for (j = 1; j < lP; j++)
+  {
+    GEN pf = galoisgenliftauto(O, gel(PG1, j), gf.psi[Pg[j]], n, &td);
+    if (!pf) { freetest(&td); avma = ltop; return gen_0; }
+    gel(res1, j+1) = pf;
+  }
+  if (DEBUGLEVEL >= 4) err_printf("GaloisConj:Fini!\n");
+  freetest(&td);
+  return gerepileupto(ltop, res);
+}
+
+/* T = polcyclo(N) */
+static GEN
+conjcyclo(GEN T, long N)
+{
+  pari_sp av = avma;
+  long i, k = 1, d = eulerphiu(N), v = varn(T);
+  GEN L = cgetg(d+1,t_COL);
+  for (i=1; i<=N; i++)
+    if (ugcd(i, N)==1)
+    {
+      GEN s = monomial(gen_1, i, v);
+      if (i >= d) s = ZX_rem(s, T);
+      gel(L,k++) = s;
+    }
+  return gerepileupto(av, gen_sort(L, (void*)&gcmp, &gen_cmp_RgX));
+}
+
+/* T: polynomial or nf, den multiple of common denominator of solutions or
+ * NULL (unknown). If T is nf, and den unknown, use den = denom(nf.zk) */
+static GEN
+galoisconj4_main(GEN T, GEN den, long flag)
+{
+  pari_sp ltop = avma;
+  GEN nf, G, L, M, aut;
+  struct galois_analysis ga;
+  struct galois_borne gb;
+  long n;
+  pari_timer ti;
+
+  T = get_nfpol(T, &nf);
+  n = poliscyclo(T);
+  if (n) return flag? galoiscyclo(n, varn(T)): conjcyclo(T, n);
+  n = degpol(T);
+  if (nf)
+  { if (!den) den = Q_denom(nf_get_zk(nf)); }
+  else
+  {
+    if (n <= 0) pari_err_IRREDPOL("galoisinit",T);
+    RgX_check_ZX(T, "galoisinit");
+    if (!ZX_is_squarefree(T))
+      pari_err_DOMAIN("galoisinit","issquarefree(pol)","=",gen_0,T);
+    if (!gequal1(gel(T,n+2))) pari_err_IMPL("galoisinit(non-monic)");
+  }
+  if (n == 1)
+  {
+    if (!flag) { G = cgetg(2, t_COL); gel(G,1) = pol_x(varn(T)); return G;}
+    ga.l = 3;
+    ga.deg = 1;
+    den = gen_1;
+  }
+  else if (!galoisanalysis(T, &ga, 1)) { avma = ltop; return utoipos(ga.p); }
+
+  if (den)
+  {
+    if (typ(den) != t_INT) pari_err_TYPE("galoisconj4 [2nd arg integer]", den);
+    den = absi(den);
+  }
+  gb.l = utoipos(ga.l);
+  if (DEBUGLEVEL >= 1) timer_start(&ti);
+  den = galoisborne(T, den, &gb, degpol(T));
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "galoisborne()");
+  L = rootpadicfast(T, gb.l, gb.valabs);
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "rootpadicfast()");
+  M = vandermondeinversemod(L, T, den, gb.ladicabs);
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "vandermondeinversemod()");
+  if (n == 1)
+  {
+    G = cgetg(3, t_VEC);
+    gel(G,1) = cgetg(1, t_VEC);
+    gel(G,2) = cgetg(1, t_VECSMALL);
+  }
+  else
+    G = galoisgen(T, L, M, den, &gb, &ga);
+  if (DEBUGLEVEL >= 6) err_printf("GaloisConj:%Ps\n", G);
+  if (G == gen_0) { avma = ltop; return gen_0; }
+  if (DEBUGLEVEL >= 1) timer_start(&ti);
+  if (flag)
+  {
+    GEN grp = cgetg(9, t_VEC);
+    gel(grp,1) = ZX_copy(T);
+    gel(grp,2) = mkvec3(utoipos(ga.l), utoipos(gb.valabs), icopy(gb.ladicabs));
+    gel(grp,3) = ZC_copy(L);
+    gel(grp,4) = ZM_copy(M);
+    gel(grp,5) = icopy(den);
+    gel(grp,6) = group_elts(G,n);
+    gel(grp,7) = gcopy(gel(G,1));
+    gel(grp,8) = gcopy(gel(G,2)); return gerepileupto(ltop, grp);
+  }
+  aut = galoisgrouptopol(group_elts(G, n),L,M,den,gb.ladicsol, varn(T));
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "Computation of polynomials");
+  return gerepileupto(ltop, gen_sort(aut, (void*)&gcmp, &gen_cmp_RgX));
+}
+
+/* Heuristic computation of #Aut(T), pinit = first prime to be tested */
+long
+numberofconjugates(GEN T, long pinit)
+{
+  pari_sp av = avma;
+  long c, nbtest, nbmax, n = degpol(T);
+  ulong p;
+  forprime_t S;
+
+  if (n == 1) return 1;
+  nbmax = (n < 10)? 20: (n<<1) + 1;
+  nbtest = 0;
+#if 0
+  c = sturm(T); c = ugcd(c, n - c); /* too costly: finite primes are cheaper */
+#else
+  c = n;
+#endif
+  u_forprime_init(&S, pinit, ULONG_MAX);
+  while((p = u_forprime_next(&S)))
+  {
+    GEN L, Tp = ZX_to_Flx(T,p);
+    long i, nb;
+    if (!Flx_is_squarefree(Tp, p)) continue;
+    /* unramified */
+    nbtest++;
+    L = Flx_nbfact_by_degree(Tp, &nb, p); /* L[i] = #factors of degree i */
+    if (L[n/nb] == nb) {
+      if (c == n && nbtest > 10) break; /* probably Galois */
+    }
+    else
+    {
+      c = ugcd(c, L[1]);
+      for (i = 2; i <= n; i++)
+        if (L[i]) { c = ugcd(c, L[i]*i); if (c == 1) break; }
+      if (c == 1) break;
+    }
+    if (nbtest == nbmax) break;
+    if (DEBUGLEVEL >= 6)
+      err_printf("NumberOfConjugates [%ld]:c=%ld,p=%ld\n", nbtest,c,p);
+    avma = av;
+  }
+  if (DEBUGLEVEL >= 2) err_printf("NumberOfConjugates:c=%ld,p=%ld\n", c, p);
+  avma = av; return c;
+}
+static GEN
+galoisconj4(GEN nf, GEN d)
+{
+  pari_sp av = avma;
+  GEN G, T;
+  G = galoisconj4_main(nf, d, 0);
+  if (typ(G) != t_INT) return G; /* Success */
+  avma = av; T = get_nfpol(nf, &nf);
+  G = cgetg(2, t_COL); gel(G,1) = pol_x(varn(T)); return G; /* Fail */
+
+}
+
+/* d multipllicative bound for the automorphism's denominators */
+GEN
+galoisconj(GEN nf, GEN d)
+{
+  pari_sp av = avma;
+  GEN G = galoisconj4_main(nf, d, 0);
+  if (typ(G) != t_INT) return G; /* Success */
+  avma = av; return galoisconj1(nf);
+}
+
+GEN
+galoisconj0(GEN nf, long flag, GEN d, long prec)
+{
+  switch(flag) {
+    case 0: return galoisconj(nf, d);
+    case 1: return galoisconj1(nf);
+    case 2: return galoisconj2(nf, prec);
+    case 4: return galoisconj4(nf, d);
+  }
+  pari_err_FLAG("nfgaloisconj");
+  return NULL; /*not reached*/
+}
+
+/******************************************************************************/
+/* Galois theory related algorithms                                           */
+/******************************************************************************/
+GEN
+checkgal(GEN gal)
+{
+  if (typ(gal) == t_POL) pari_err_TYPE("checkgal [apply galoisinit first]",gal);
+  if (typ(gal) != t_VEC || lg(gal) != 9) pari_err_TYPE("checkgal",gal);
+  return gal;
+}
+
+GEN
+galoisinit(GEN nf, GEN den)
+{
+  GEN G = galoisconj4_main(nf, den, 1);
+  return (typ(G) == t_INT)? gen_0: G;
+}
+
+static GEN
+galoispermtopol_i(GEN gal, GEN perm, GEN mod, GEN mod2)
+{
+  long t = typ(perm), i;
+  GEN v;
+  switch (t)
+  {
+  case t_VECSMALL:
+  {
+    long v = varn(gel(gal,1));
+    return permtopol(perm, gal_get_roots(gal), gal_get_invvdm(gal),
+                           gal_get_den(gal), mod, mod2, v);
+  }
+  case t_VEC: case t_COL: case t_MAT:
+    v = cgetg(lg(perm), t);
+    if (DEBUGLEVEL>=4) err_printf("GaloisPermToPol:");
+    for (i = 1; i < lg(v); i++)
+    {
+      gel(v,i) = galoispermtopol_i(gal, gel(perm,i), mod, mod2);
+      if (DEBUGLEVEL>=4) err_printf("%ld ",i);
+    }
+    if (DEBUGLEVEL>=4) err_printf("\n");
+    return v;
+  }
+  pari_err_TYPE("galoispermtopol", perm);
+  return NULL; /* not reached */
+}
+
+GEN
+galoispermtopol(GEN gal, GEN perm)
+{
+  pari_sp av = avma;
+  GEN mod, mod2;
+  gal = checkgal(gal);
+  mod = gal_get_mod(gal);
+  mod2 = shifti(mod,-1);
+  return gerepilecopy(av, galoispermtopol_i(gal, perm, mod, mod2));
+}
+
+GEN
+galoiscosets(GEN O, GEN perm)
+{
+  long i, j, k, u, f, l = lg(O);
+  GEN RC, C = cgetg(l,t_VECSMALL), o = gel(O,1);
+  pari_sp av = avma;
+  f = lg(o); u = o[1]; RC = zero_zv(lg(perm)-1);
+  for(i=1,j=1; j<l; i++)
+  {
+    GEN p = gel(perm,i);
+    if (RC[ p[u] ]) continue;
+    for(k=1; k<f; k++) RC[ p[ o[k] ] ] = 1;
+    C[j++] = i;
+  }
+  avma = av; return C;
+}
+
+static GEN
+fixedfieldfactor(GEN L, GEN O, GEN perm, GEN M, GEN den, GEN mod, GEN mod2,
+                 long x,long y)
+{
+  pari_sp ltop = avma;
+  long i, j, k, l = lg(O), lo = lg(gel(O,1));
+  GEN V, res, cosets = galoiscosets(O,perm), F = cgetg(lo+1,t_COL);
+
+  gel(F, lo) = gen_1;
+  if (DEBUGLEVEL>=4) err_printf("GaloisFixedField:cosets=%Ps \n",cosets);
+  if (DEBUGLEVEL>=6) err_printf("GaloisFixedField:den=%Ps mod=%Ps \n",den,mod);
+  V = cgetg(l,t_COL); res = cgetg(l,t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    pari_sp av = avma;
+    GEN G = cgetg(l,t_VEC), Lp = vecpermute(L, gel(perm, cosets[i]));
+    for (k = 1; k < l; k++)
+      gel(G,k) = FpV_roots_to_pol(vecpermute(Lp, gel(O,k)), mod, x);
+    for (j = 1; j < lo; j++)
+    {
+      for(k = 1; k < l; k++) gel(V,k) = gmael(G,k,j+1);
+      gel(F,j) = vectopol(V, M, den, mod, mod2, y);
+    }
+    gel(res,i) = gerepileupto(av,gtopolyrev(F,x));
+  }
+  return gerepileupto(ltop,res);
+}
+
+static void
+chk_perm(GEN perm, long n)
+{
+  if (typ(perm) != t_VECSMALL || lg(perm)!=n+1)
+    pari_err_TYPE("galoisfixedfield", perm);
+}
+
+static int
+is_group(GEN g)
+{
+  return typ(g)==t_VEC && lg(g)==3 && typ(gel(g,1))==t_VEC
+      && typ(gel(g,2))==t_VECSMALL;
+}
+
+GEN
+galoisfixedfield(GEN gal, GEN perm, long flag, long y)
+{
+  pari_sp ltop = avma;
+  GEN T, L, P, S, PL, O, res, mod, mod2;
+  long vT, n, i;
+  if (flag<0 || flag>2) pari_err_FLAG("galoisfixedfield");
+  gal = checkgal(gal); T = gal_get_pol(gal);
+  vT = varn(T);
+  L = gal_get_roots(gal); n = lg(L)-1;
+  mod = gal_get_mod(gal);
+  if (typ(perm) == t_VEC)
+  {
+    if (is_group(perm)) perm = gel(perm, 1);
+    for (i = 1; i < lg(perm); i++) chk_perm(gel(perm,i), n);
+    O = vecperm_orbits(perm, n);
+  }
+  else
+  {
+    chk_perm(perm, n);
+    O = perm_cycles(perm);
+  }
+
+  {
+    GEN OL= fixedfieldorbits(O,L);
+    GEN V = fixedfieldsympol(OL, mod, gal_get_p(gal), NULL, vT);
+    PL= gel(V,2);
+    P = gel(V,3);
+  }
+  if (flag==1) return gerepileupto(ltop,P);
+  mod2 = shifti(mod,-1);
+  S = fixedfieldinclusion(O, PL);
+  S = vectopol(S, gal_get_invvdm(gal), gal_get_den(gal), mod, mod2, vT);
+  if (flag==0)
+    res = cgetg(3, t_VEC);
+  else
+  {
+    GEN PM, Pden;
+    struct galois_borne Pgb;
+    long val = itos(gal_get_e(gal));
+    Pgb.l = gal_get_p(gal);
+    Pden = galoisborne(P, NULL, &Pgb, degpol(T)/degpol(P));
+    if (Pgb.valabs > val)
+    {
+      if (DEBUGLEVEL>=4)
+        err_printf("GaloisConj:increase p-adic prec by %ld.\n", Pgb.valabs-val);
+      PL = ZpX_liftroots(P, PL, Pgb.l, Pgb.valabs);
+      L  = ZpX_liftroots(T, L, Pgb.l, Pgb.valabs);
+      mod = Pgb.ladicabs; mod2 = shifti(mod,-1);
+    }
+    PM = vandermondeinversemod(PL, P, Pden, mod);
+    if (y < 0) y = fetch_user_var("y");
+    if (varncmp(y, vT) <= 0)
+      pari_err_PRIORITY("galoisfixedfield", T, "<=", y);
+    res = cgetg(4, t_VEC);
+    gel(res,3) = fixedfieldfactor(L,O,gal_get_group(gal), PM,Pden,mod,mod2,vT,y);
+  }
+  gel(res,1) = gcopy(P);
+  gel(res,2) = gmodulo(S, T);
+  return gerepileupto(ltop, res);
+}
+
+/* gal a galois group output the underlying wss group */
+GEN
+galois_group(GEN gal) { return mkvec2(gal_get_gen(gal), gal_get_orders(gal)); }
+
+GEN
+checkgroup(GEN g, GEN *S)
+{
+  if (is_group(g)) { *S = NULL; return g; }
+  g  = checkgal(g);
+  *S = gal_get_group(g); return galois_group(g);
+}
+
+GEN
+galoisisabelian(GEN gal, long flag)
+{
+  pari_sp av = avma;
+  GEN S, G = checkgroup(gal,&S);
+  if (!group_isabelian(G)) { avma=av; return gen_0; }
+  switch(flag)
+  {
+    case 0: return gerepileupto(av, group_abelianHNF(G,S));
+    case 1: avma=av; return gen_1;
+    case 2: return gerepileupto(av, group_abelianSNF(G,S));
+    default: pari_err_FLAG("galoisisabelian");
+  }
+  return NULL; /* not reached */
+}
+
+long
+galoisisnormal(GEN gal, GEN sub)
+{
+  pari_sp av = avma;
+  GEN S, G = checkgroup(gal, &S), H = checkgroup(sub, &S);
+  long res = group_subgroup_isnormal(G, H);
+  avma = av;
+  return res;
+}
+
+GEN
+galoissubgroups(GEN gal)
+{
+  pari_sp av = avma;
+  GEN S, G = checkgroup(gal,&S);
+  return gerepileupto(av, group_subgroups(G));
+}
+
+GEN
+galoissubfields(GEN G, long flag, long v)
+{
+  pari_sp av = avma;
+  GEN L = galoissubgroups(G);
+  long i, l = lg(L);
+  GEN S = cgetg(l, t_VEC);
+  for (i = 1; i < l; ++i) gel(S,i) = galoisfixedfield(G, gmael(L,i,1), flag, v);
+  return gerepileupto(av, S);
+}
+
+GEN
+galoisexport(GEN gal, long format)
+{
+  pari_sp av = avma;
+  GEN S, G = checkgroup(gal,&S);
+  return gerepileupto(av, group_export(G,format));
+}
+
+GEN
+galoisidentify(GEN gal)
+{
+  pari_sp av = avma;
+  GEN S, G = checkgroup(gal,&S);
+  long idx = group_ident(G,S), card = group_order(G);
+  avma = av; return mkvec2s(card, idx);
+}
diff --git a/src/basemath/gen1.c b/src/basemath/gen1.c
new file mode 100644
index 0000000..b5e653f
--- /dev/null
+++ b/src/basemath/gen1.c
@@ -0,0 +1,3398 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                      GENERIC OPERATIONS                        **/
+/**                         (first part)                           **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/* assume z[1] was created last */
+#define fix_frac_if_int(z) if (is_pm1(gel(z,2)))\
+  z = gerepileupto((pari_sp)(z+3), gel(z,1));
+
+/* assume z[1] was created last */
+#define fix_frac_if_int_GC(z,tetpil) { if (is_pm1(gel(z,2)))\
+  z = gerepileupto((pari_sp)(z+3), gel(z,1));\
+else\
+  gerepilecoeffssp((pari_sp)z, tetpil, z+1, 2); }
+
+static long
+kro_quad(GEN x, GEN y)
+{
+  pari_sp av=avma;
+  long k = kronecker(quad_disc(x), y);
+  avma = av; return k;
+}
+
+/* is -1 not a square in Zp, assume p prime */
+INLINE int
+Zp_nosquare_m1(GEN p) { return (mod4(p) & 2); /* 2 or 3 mod 4 */ }
+
+static GEN addsub_pp(GEN x, GEN y, GEN(*op)(GEN,GEN));
+static GEN addsub_frac(GEN x, GEN y, GEN (*op)(GEN,GEN));
+static GEN mulpp(GEN x, GEN y);
+static GEN divpp(GEN x, GEN y);
+/* Argument codes for inline routines
+ * c: complex, p: padic, q: quad, f: floating point (REAL, some complex)
+ * R: without imaginary part (INT, REAL, INTMOD, FRAC, PADIC if -1 not square)
+ * T: some type (to be converted to PADIC)
+ */
+static GEN
+addRc(GEN x, GEN y) {
+  GEN z = cgetg(3,t_COMPLEX);
+  gel(z,1) = gadd(x,gel(y,1));
+  gel(z,2) = gcopy(gel(y,2)); return z;
+}
+static GEN
+mulRc(GEN x, GEN y) {
+  GEN z = cgetg(3,t_COMPLEX);
+  gel(z,1) = isintzero(gel(y,1))? gen_0: gmul(x,gel(y,1));
+  gel(z,2) = gmul(x,gel(y,2)); return z;
+}
+/* for INTMODs: can't simplify when Re(x) = gen_0 */
+static GEN
+mulRc_direct(GEN x, GEN y) {
+  GEN z = cgetg(3,t_COMPLEX);
+  gel(z,1) = gmul(x,gel(y,1));
+  gel(z,2) = gmul(x,gel(y,2)); return z;
+}
+static GEN
+divRc(GEN x, GEN y) {
+  GEN t = gdiv(x, cxnorm(y)), mt = gneg(t); /* left on stack for efficiency */
+  GEN z = cgetg(3,t_COMPLEX);
+  gel(z,1) = isintzero(gel(y,1))? gen_0: gmul(t, gel(y,1));
+  gel(z,2) = gmul(mt, gel(y,2));
+  return z;
+}
+static GEN
+divcR(GEN x, GEN y) {
+  GEN z = cgetg(3,t_COMPLEX);
+  gel(z,1) = isintzero(gel(x,1))? gen_0: gdiv(gel(x,1), y);
+  gel(z,2) = gdiv(gel(x,2), y); return z;
+}
+static GEN
+addRq(GEN x, GEN y) {
+  GEN z = cgetg(4,t_QUAD);
+  gel(z,1) = ZX_copy(gel(y,1));
+  gel(z,2) = gadd(x, gel(y,2));
+  gel(z,3) = gcopy(gel(y,3)); return z;
+}
+static GEN
+mulRq(GEN x, GEN y) {
+  GEN z = cgetg(4,t_QUAD);
+  gel(z,1) = ZX_copy(gel(y,1));
+  gel(z,2) = gmul(x,gel(y,2));
+  gel(z,3) = gmul(x,gel(y,3)); return z;
+}
+static GEN
+addqf(GEN x, GEN y, long prec) { pari_sp av = avma;
+  long i = gexpo(x) - gexpo(y);
+  if (i > 0) prec += nbits2extraprec( i );
+  return gerepileupto(av, gadd(y, quadtofp(x, prec)));
+}
+static GEN
+mulrfrac(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN z, a = gel(y,1), b = gel(y,2);
+  if (is_pm1(a)) /* frequent special case */
+  {
+    z = divri(x, b);
+    if (signe(a) < 0) togglesign(z);
+    return z;
+  }
+  return gerepileuptoleaf(av, divri(mulri(x,gel(y,1)), gel(y,2)));
+}
+static GEN
+mulqf(GEN x, GEN y, long prec) { pari_sp av = avma;
+  return gerepileupto(av, gmul(y, quadtofp(x, prec)));
+}
+static GEN
+divqf(GEN x, GEN y, long prec) { pari_sp av = avma;
+  return gerepileupto(av, gdiv(quadtofp(x,prec), y));
+}
+static GEN
+divfq(GEN x, GEN y, long prec) { pari_sp av = avma;
+  return gerepileupto(av, gdiv(x, quadtofp(y,prec)));
+}
+/* y PADIC, x + y by converting x to padic */
+static GEN
+addTp(GEN x, GEN y) { pari_sp av = avma; GEN z;
+
+  if (!valp(y)) z = cvtop2(x,y);
+  else {
+    long l = signe(gel(y,4))? valp(y) + precp(y): valp(y);
+    z  = cvtop(x, gel(y,2), l);
+  }
+  return gerepileupto(av, addsub_pp(z, y, addii));
+}
+/* y PADIC, x * y by converting x to padic */
+static GEN
+mulTp(GEN x, GEN y) { pari_sp av = avma;
+  return gerepileupto(av, mulpp(cvtop2(x,y), y));
+}
+/* y PADIC, non zero x / y by converting x to padic */
+static GEN
+divTp(GEN x, GEN y) { pari_sp av = avma;
+  return gerepileupto(av, divpp(cvtop2(x,y), y));
+}
+/* x PADIC, x / y by converting y to padic. Assume x != 0; otherwise y
+ * converted to O(p^e) and division by 0 */
+static GEN
+divpT(GEN x, GEN y) { pari_sp av = avma;
+  return gerepileupto(av, divpp(x, cvtop2(y,x)));
+}
+
+/* z := Mod(x,X) + Mod(y,X) [ t_INTMOD preallocated ], x,y,X INT, 0 <= x,y < X
+ * clean memory from z on */
+static GEN
+add_intmod_same(GEN z, GEN X, GEN x, GEN y) {
+  if (lgefint(X) == 3) {
+    ulong u = Fl_add(itou(x),itou(y), X[2]);
+    avma = (pari_sp)z; gel(z,2) = utoi(u);
+  }
+  else {
+    GEN u = addii(x,y); if (cmpii(u, X) >= 0) u = subii(u, X);
+    gel(z,2) = gerepileuptoint((pari_sp)z, u);
+  }
+  gel(z,1) = icopy(X); return z;
+}
+static GEN
+sub_intmod_same(GEN z, GEN X, GEN x, GEN y) {
+  if (lgefint(X) == 3) {
+    ulong u = Fl_sub(itou(x),itou(y), X[2]);
+    avma = (pari_sp)z; gel(z,2) = utoi(u);
+  }
+  else {
+    GEN u = subii(x,y); if (signe(u) < 0) u = addii(u, X);
+    gel(z,2) = gerepileuptoint((pari_sp)z, u);
+  }
+  gel(z,1) = icopy(X); return z;
+}
+/* cf add_intmod_same */
+static GEN
+mul_intmod_same(GEN z, GEN X, GEN x, GEN y) {
+  if (lgefint(X) == 3) {
+    ulong u = Fl_mul(itou(x),itou(y), X[2]);
+    avma = (pari_sp)z; gel(z,2) = utoi(u);
+  }
+  else
+    gel(z,2) = gerepileuptoint((pari_sp)z, remii(mulii(x,y), X) );
+  gel(z,1) = icopy(X); return z;
+}
+/* cf add_intmod_same */
+static GEN
+div_intmod_same(GEN z, GEN X, GEN x, GEN y)
+{
+  if (lgefint(X) == 3) {
+    ulong m = (ulong)X[2], u = Fl_div(itou(x), itou(y), m);
+    avma = (pari_sp)z; gel(z,2) = utoi(u);
+  }
+  else
+    gel(z,2) = gerepileuptoint((pari_sp)z, remii(mulii(x, Fp_inv(y,X)), X) );
+  gel(z,1) = icopy(X); return z;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*        REDUCTION to IRREDUCIBLE TERMS (t_FRAC/t_RFRAC)          */
+/*                                                                 */
+/* (static routines are not memory clean, but OK for gerepileupto) */
+/*******************************************************************/
+/* Compute the denominator of (1/y) * (n/d) = n/yd, y a "scalar".
+ * Sanity check : avoid (1/2) / (Mod(1,2)*x + 1) "=" 1 / (0 * x + 1) */
+static GEN
+rfrac_denom_mul_scal(GEN d, GEN y)
+{
+  GEN D = RgX_Rg_mul(d, y);
+  if (lg(D) != lg(d))
+  { /* try to generate a meaningful diagnostic */
+    D = gdiv(leading_term(d), y); /* should fail */
+    pari_err_INV("gred_rfrac", y); /* better than nothing */
+  }
+  return D;
+}
+
+/* d a t_POL, n a coprime t_POL of same var or "scalar". Not memory clean */
+GEN
+gred_rfrac_simple(GEN n, GEN d)
+{
+  GEN c, cn, cd, z;
+  long dd = degpol(d);
+
+  if (dd <= 0)
+  {
+    if (dd < 0) pari_err_INV("gred_rfrac_simple", d);
+    n = gdiv(n, gel(d,2));
+    if (typ(n) != t_POL || varn(n) != varn(d)) n = scalarpol(n, varn(d));
+    return n;
+  }
+
+  cd = content(d);
+  cn = (typ(n) == t_POL && varn(n) == varn(d))? content(n): n;
+  if (!gequal1(cd)) {
+    d = RgX_Rg_div(d,cd);
+    if (!gequal1(cn))
+    {
+      if (gequal0(cn)) {
+        if (isexactzero(cn)) return scalarpol(cn, varn(d));
+        n = (cn != n)? RgX_Rg_div(n,cd): gdiv(n, cd);
+        c = gen_1;
+      } else {
+        n = (cn != n)? RgX_Rg_div(n,cn): gen_1;
+        c = gdiv(cn,cd);
+      }
+    }
+    else
+      c = ginv(cd);
+  } else {
+    if (!gequal1(cn))
+    {
+      if (gequal0(cn)) {
+        if (isexactzero(cn)) return scalarpol(cn, varn(d));
+        c = gen_1;
+      } else {
+        n = (cn != n)? RgX_Rg_div(n,cn): gen_1;
+        c = cn;
+      }
+    } else {
+      GEN y = cgetg(3,t_RFRAC);
+      gel(y,1) = gcopy(n);
+      gel(y,2) = RgX_copy(d); return y;
+    }
+  }
+
+  if (typ(c) == t_POL)
+  {
+    z = c;
+    do { z = content(z); } while (typ(z) == t_POL);
+    cd = denom(z);
+    cn = gmul(c, cd);
+  }
+  else
+  {
+    cn = numer(c);
+    cd = denom(c);
+  }
+  z = cgetg(3,t_RFRAC);
+  gel(z,1) = gmul(n, cn);
+  gel(z,2) = rfrac_denom_mul_scal(d, cd);
+  return z;
+}
+
+/* in rare cases x may be a t_POL, after 0/x for instance -> pol_0() */
+static GEN
+fix_rfrac(GEN x, long d)
+{
+  GEN z, N, D;
+  if (!d || typ(x) == t_POL) return x;
+  z = cgetg(3, t_RFRAC);
+  N = gel(x,1);
+  D = gel(x,2);
+  if (d > 0) {
+    gel(z, 1) = (typ(N)==t_POL && varn(N)==varn(D))? RgX_shift(N,d)
+                                                   : monomialcopy(N,d,varn(D));
+    gel(z, 2) = RgX_copy(D);
+  } else {
+    gel(z, 1) = gcopy(N);
+    gel(z, 2) = RgX_shift(D, -d);
+  }
+  return z;
+}
+
+/* assume d != 0 */
+static GEN
+gred_rfrac2_i(GEN n, GEN d)
+{
+  GEN y, z;
+  long v, vd, vn;
+
+  n = simplify_shallow(n);
+  if (isrationalzero(n)) return gcopy(n);
+  d = simplify_shallow(d);
+  if (typ(d) != t_POL) return gdiv(n,d);
+  vd = varn(d);
+  if (typ(n) != t_POL)
+  {
+    if (varncmp(vd, gvar(n)) >= 0) return gdiv(n,d);
+    if (varncmp(vd, gvar2(n)) < 0) return gred_rfrac_simple(n,d);
+    pari_err_BUG("gred_rfrac2_i [incompatible variables]");
+  }
+  vn = varn(n);
+  if (varncmp(vd, vn) < 0) return gred_rfrac_simple(n,d);
+  if (varncmp(vd, vn) > 0) return RgX_Rg_div(n,d);
+
+  /* now n and d are t_POLs in the same variable */
+  v = RgX_valrem(n, &n) - RgX_valrem(d, &d);
+  if (!degpol(d))
+  {
+    n = RgX_Rg_div(n,gel(d,2));
+    return v? RgX_mulXn(n,v): n;
+  }
+
+  /* X does not divide gcd(n,d), deg(d) > 0 */
+  if (!isinexact(n) && !isinexact(d))
+  {
+    y = RgX_divrem(n, d, &z);
+    if (!signe(z)) { cgiv(z); return v? RgX_mulXn(y, v): y; }
+    z = RgX_gcd(d, z);
+    if (degpol(z)) { n = RgX_div(n,z); d = RgX_div(d,z); }
+  }
+  return fix_rfrac(gred_rfrac_simple(n,d), v);
+}
+
+GEN
+gred_rfrac2(GEN x1, GEN x2)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gred_rfrac2_i(x1, x2));
+}
+
+/* x1,x2 t_INT, return x1/x2 in reduced form */
+GEN
+gred_frac2(GEN x1, GEN x2)
+{
+  GEN r, y = dvmdii(x1,x2,&r);
+  pari_sp av;
+
+  if (r == gen_0) return y; /* gen_0 intended */
+  av = avma; r = gcdii(x2,r);
+  if (lgefint(r) == 3)
+  {
+    ulong rr = r[2];
+    avma = av;
+    if (rr == 1) y = mkfraccopy(x1, x2);
+    else
+    {
+      y = cgetg(3,t_FRAC);
+      gel(y,1) = diviuexact(x1, rr);
+      gel(y,2) = diviuexact(x2, rr);
+    }
+  }
+  else
+  { /* rare: r left on stack for efficiency */
+    y = cgetg(3,t_FRAC);
+    gel(y,1) = diviiexact(x1,r);
+    gel(y,2) = diviiexact(x2,r);
+  }
+  normalize_frac(y); return y;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                          CONJUGATION                            */
+/*                                                                 */
+/*******************************************************************/
+/* lift( conj(Mod(x, y)) ), assuming degpol(y) = 2, degpol(x) < 2 */
+static GEN
+quad_polmod_conj(GEN x, GEN y)
+{
+  GEN z, u, v, a, b;
+  pari_sp av;
+  if (typ(x) != t_POL) return gcopy(x);
+  if (varn(x) != varn(y) || degpol(x) <= 0) return RgX_copy(x);
+  a = gel(y,4); u = gel(x,3); /*Mod(ux + v, ax^2 + bx + c)*/
+  b = gel(y,3); v = gel(x,2);
+  z = cgetg(4, t_POL); z[1] = x[1]; av = avma;
+  gel(z,2) = gerepileupto(av, gsub(v, gdiv(gmul(u,b), a)));
+  gel(z,3) = gneg(u); return z;
+}
+static GEN
+quad_polmod_norm(GEN x, GEN y)
+{
+  GEN z, u, v, a, b, c;
+  pari_sp av;
+  if (typ(x) != t_POL || varn(x) != varn(y) || degpol(x) <= 0)
+    return gsqr(x);
+  a = gel(y,4); u = gel(x,3); /*Mod(ux + v, ax^2 + bx + c)*/
+  b = gel(y,3); v = gel(x,2);
+  c = gel(y,2); av = avma;
+  z = gmul(u, gsub(gmul(c,u), gmul(b,v)));
+  if (!gequal1(a)) z = gdiv(z, a);
+  return gerepileupto(av, gadd(z, gsqr(v)));
+}
+
+GEN
+gconj(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL:
+      return mpcopy(x);
+
+    case t_INTMOD: case t_FRAC: case t_PADIC:
+      return gcopy(x);
+
+    case t_COMPLEX:
+      y = cgetg(3,t_COMPLEX);
+      gel(y,1) = gcopy(gel(x,1));
+      gel(y,2) = gneg(gel(x,2));
+      break;
+
+    case t_QUAD:
+      y = cgetg(4,t_QUAD);
+      gel(y,1) = ZX_copy(gel(x,1));
+      gel(y,2) = gequal0(gmael(x,1,3))? gcopy(gel(x,2))
+                                    : gadd(gel(x,2), gel(x,3));
+      gel(y,3) = gneg(gel(x,3));
+      break;
+
+    case t_POL: case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gconj(gel(x,i));
+      break;
+
+    case t_RFRAC: case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gconj(gel(x,i));
+      break;
+
+    case t_POLMOD:
+    {
+      GEN y, X = gel(x,1);
+      long d = degpol(X);
+      if (d < 2) return RgX_copy(x);
+      if (d == 2) {
+        y = cgetg(3, t_POLMOD);
+        gel(y,1) = RgX_copy(X);
+        gel(y,2) = quad_polmod_conj(gel(x,2), X); return y;
+      }
+    }
+    default:
+      pari_err_TYPE("gconj",x);
+      return NULL; /* not reached */
+  }
+  return y;
+}
+
+GEN
+conjvec(GEN x,long prec)
+{
+  long lx, s, i;
+  GEN z;
+
+  switch(typ(x))
+  {
+    case t_INT: case t_INTMOD: case t_FRAC:
+      return mkcolcopy(x);
+
+    case t_COMPLEX: case t_QUAD:
+      z=cgetg(3,t_COL); gel(z,1) = gcopy(x); gel(z,2) = gconj(x); break;
+
+    case t_FFELT:
+      return FF_conjvec(x);
+
+    case t_VEC: case t_COL:
+      lx = lg(x); z = cgetg(lx,t_MAT);
+      if (lx == 1) return z;
+      gel(z,1) = conjvec(gel(x,1),prec);
+      s = lgcols(z);
+      for (i=2; i<lx; i++)
+      {
+        gel(z,i) = conjvec(gel(x,i),prec);
+        if (lg(gel(z,i)) != s) pari_err_OP("conjvec", gel(z,1), gel(z,i));
+      }
+      break;
+
+    case t_POLMOD: {
+      GEN T = gel(x,1), r;
+      pari_sp av;
+
+      lx = lg(T);
+      if (lx <= 3) return cgetg(1,t_COL);
+      x = gel(x,2);
+      for (i=2; i<lx; i++)
+      {
+        GEN c = gel(T,i);
+        switch(typ(c)) {
+          case t_INTMOD: {
+            GEN p = gel(c,1);
+            pari_sp av;
+            if (typ(x) != t_POL) retconst_col(lx-3, Rg_to_Fp(x, p));
+            av = avma;
+            T = RgX_to_FpX(T,p);
+            x = RgX_to_FpX(x, p);
+            if (varn(x) != varn(T)) pari_err_VAR("conjvec",x,T);
+            z = FpXQC_to_mod(FpXQ_conjvec(x, T , p), T, p);
+            return gerepileupto(av, z);
+          }
+          case t_INT:
+          case t_FRAC: break;
+          default: pari_err_TYPE("conjvec [not a rational t_POL]",T);
+        }
+      }
+      if (typ(x) != t_POL)
+      {
+        if (!is_rational_t(typ(x)))
+          pari_err_TYPE("conjvec [not a rational t_POL]",x);
+        retconst_col(lx-3, gcopy(x));
+      }
+      if (!RgX_is_QX(x)) pari_err_TYPE("conjvec", x);
+      av = avma;
+      if (varn(x) != varn(T)) pari_err_VAR("conjvec",x,T);
+      r = cleanroots(T,prec);
+      z = cgetg(lx-2,t_COL);
+      for (i=1; i<=lx-3; i++) gel(z,i) = poleval(x, gel(r,i));
+      return gerepileupto(av, z);
+    }
+
+    default:
+      pari_err_TYPE("conjvec",x);
+      return NULL; /* not reached */
+  }
+  return z;
+}
+
+
+/********************************************************************/
+/**                                                                **/
+/**                           ADDITION                             **/
+/**                                                                **/
+/********************************************************************/
+/* x, y compatible PADIC, op = add or sub */
+static GEN
+addsub_pp(GEN x, GEN y, GEN (*op)(GEN,GEN))
+{
+  pari_sp av = avma;
+  long d,e,r,rx,ry;
+  GEN u, z, mod, p = gel(x,2);
+  int swap;
+
+  (void)new_chunk(5 + lgefint(gel(x,3)) + lgefint(gel(y,3)));
+  e = valp(x);
+  r = valp(y); d = r-e;
+  if (d < 0) { swap = 1; swap(x,y); e = r; d = -d; } else swap = 0;
+  rx = precp(x);
+  ry = precp(y);
+  if (d) /* v(x) < v(y) */
+  {
+    r = d+ry; z = powiu(p,d);
+    if (r < rx) mod = mulii(z,gel(y,3)); else { r = rx; mod = gel(x,3); }
+    z = mulii(z,gel(y,4));
+    u = swap? op(z, gel(x,4)): op(gel(x,4), z);
+  }
+  else
+  {
+    long c;
+    if (ry < rx) { r=ry; mod = gel(y,3); } else { r=rx; mod = gel(x,3); }
+    u = swap? op(gel(y,4), gel(x,4)): op(gel(x,4), gel(y,4));
+    if (!signe(u) || (c = Z_pvalrem(u,p,&u)) >= r)
+    {
+      avma = av; return zeropadic(p, e+r);
+    }
+    if (c)
+    {
+      mod = diviiexact(mod, powiu(p,c));
+      r -= c;
+      e += c;
+    }
+  }
+  u = modii(u, mod);
+  avma = av; z = cgetg(5,t_PADIC);
+  z[1] = evalprecp(r) | evalvalp(e);
+  gel(z,2) = icopy(p);
+  gel(z,3) = icopy(mod);
+  gel(z,4) = icopy(u); return z;
+}
+
+/* return x + y, where y t_PADIC and x is a non-zero t_INT or t_FRAC */
+static GEN
+addQp(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long tx,d,r,e, vy = valp(y), py = precp(y);
+  GEN z,q,p1,p2,mod,u, p = gel(y,2);
+
+  tx = typ(x);
+  e = (tx == t_INT)? Z_pvalrem(x,p,&p1)
+                   : Z_pvalrem(gel(x,1),p,&p1) -
+                     Z_pvalrem(gel(x,2),p,&p2);
+  d = vy - e; r = d + py;
+  if (r <= 0) { avma = av; return gcopy(y); }
+  mod = gel(y,3);
+  u   = gel(y,4);
+  (void)new_chunk(5 + ((lgefint(mod) + lgefint(p)*labs(d)) << 1));
+
+  if (d > 0)
+  {
+    q = powiu(p,d);
+    mod = mulii(mod, q);
+    u   = mulii(u, q);
+    if (tx != t_INT && !is_pm1(p2)) p1 = mulii(p1, Fp_inv(p2,mod));
+    u = addii(u, p1);
+  }
+  else if (d < 0)
+  {
+    q = powiu(p,-d);
+    if (tx != t_INT && !is_pm1(p2)) p1 = mulii(p1, Fp_inv(p2,mod));
+    p1 = mulii(p1, q);
+    u = addii(u, p1);
+    r = py; e = vy;
+  }
+  else
+  {
+    long c;
+    if (tx != t_INT && !is_pm1(p2)) p1 = mulii(p1, Fp_inv(p2,mod));
+    u = addii(u, p1);
+    if (!signe(u) || (c = Z_pvalrem(u,p,&u)) >= r)
+    {
+      avma = av; return zeropadic(p,e+r);
+    }
+    if (c)
+    {
+      mod = diviiexact(mod, powiu(p,c));
+      r -= c;
+      e += c;
+    }
+  }
+  u = modii(u, mod);
+  avma = av; z = cgetg(5,t_PADIC);
+  z[1] = evalprecp(r) | evalvalp(e);
+  gel(z,2) = icopy(p);
+  gel(z,3) = icopy(mod);
+  gel(z,4) = icopy(u); return z;
+}
+
+/* Mod(x,X) + Mod(y,X) */
+#define addsub_polmod_same addsub_polmod_scal
+/* Mod(x,X) +/- Mod(y,Y) */
+static GEN
+addsub_polmod(GEN X, GEN Y, GEN x, GEN y, GEN(*op)(GEN,GEN))
+{
+  long T[3] = { evaltyp(t_POLMOD) | _evallg(3),0,0 };
+  GEN z = cgetg(3,t_POLMOD);
+  long vx = varn(X), vy = varn(Y);
+  if (vx==vy) {
+    pari_sp av;
+    gel(z,1) = RgX_gcd(X,Y); av = avma;
+    gel(z,2) = gerepileupto(av, gmod(op(x, y), gel(z,1))); return z;
+  }
+  if (varncmp(vx, vy) < 0)
+  { gel(z,1) = RgX_copy(X); gel(T,1) = Y; gel(T,2) = y; y = T; }
+  else
+  { gel(z,1) = RgX_copy(Y); gel(T,1) = X; gel(T,2) = x; x = T; }
+  gel(z,2) = op(x, y); return z;
+}
+/* Mod(y, Y) +/- x,  x scalar or polynomial in same var and reduced degree */
+static GEN
+addsub_polmod_scal(GEN Y, GEN y, GEN x, GEN(*op)(GEN,GEN))
+{
+  GEN z = cgetg(3,t_POLMOD);
+  gel(z,1) = RgX_copy(Y);
+  gel(z,2) = op(y, x); return z;
+}
+
+/* check y[a..b-1] and set signe to 1 if one coeff is non-0, 0 otherwise
+ * For t_POL and t_SER */
+static GEN
+NORMALIZE_i(GEN y, long a, long b)
+{
+  long i;
+  for (i = a; i < b; i++)
+    if (!gequal0(gel(y,i))) { setsigne(y, 1); return y; }
+  setsigne(y, 0); return y;
+}
+/* typ(y) == t_SER, x "scalar" [e.g object in lower variable] */
+static GEN
+add_ser_scal(GEN y, GEN x)
+{
+  long i, j, l, ly, vy;
+  pari_sp av;
+  GEN z, t;
+
+  if (isrationalzero(x)) return gcopy(y);
+  ly = lg(y);
+  l = valp(y);
+  if (l < 3-ly) return gcopy(y);
+  /* l + ly >= 3 */
+  if (l < 0)
+  {
+    z = cgetg(ly,t_SER); z[1] = y[1];
+    for (i = 2; i <= 1-l; i++) gel(z,i) = gcopy(gel(y,i));
+    gel(z,i) = gadd(x,gel(y,i)); i++;
+    for (     ; i < ly; i++)   gel(z,i) = gcopy(gel(y,i));
+    return NORMALIZE_i(z, 2, ly);
+  }
+  vy = varn(y);
+  if (l > 0)
+  {
+    ly += l; y -= l; z = cgetg(ly,t_SER);
+    z[1] = evalsigne(1) | _evalvalp(0) | evalvarn(vy);
+    gel(z,2) = gcopy(x);
+    for (i=3; i<=l+1; i++) gel(z,i) = gen_0;
+    for (   ; i < ly; i++) gel(z,i) = gcopy(gel(y,i));
+    if (gequal0(x)) return normalize(z);
+    return z;
+  }
+  /* l = 0, ly >= 3, !isrationalzero(x) */
+  av = avma; z = cgetg(ly,t_SER);
+  x = gadd(x, gel(y,2));
+  if (!isrationalzero(x))
+  {
+    z[1] = evalsigne(1) | _evalvalp(0) | evalvarn(vy);
+    gel(z,2) = x;
+    for (i=3; i<ly; i++) gel(z,i) = gcopy(gel(y,i));
+    if (gequal0(x)) return normalize(z);
+    return z;
+  }
+  avma = av; /* first coeff is rational 0 */
+  i = 3;
+  while (i<ly && isrationalzero(gel(y,i))) i++;
+  if (i == ly) return zeroser(vy, ly-2);
+  t = gel(y,i);
+  while (i<ly && isexactzero(gel(y,i))) i++;
+  if (i == ly)
+  {
+    z = cgetg(3, t_SER);
+    z[1] = evalsigne(0) | _evalvalp(i-2) | evalvarn(vy);
+    gel(z,2) = gcopy(t); return gerepileupto(av, z);
+  }
+
+  i -= 2; ly -= i; y += i;
+  z = cgetg(ly,t_SER); z[1] = evalvalp(i) | evalvarn(vy);
+  for (j = 2; j < ly; j++) gel(z,j) = gcopy(gel(y,j));
+  return NORMALIZE_i(z, 2, ly);
+}
+/* typ(y) == RFRAC, x polynomial in same variable or "scalar" */
+static GEN
+add_rfrac_scal(GEN y, GEN x)
+{
+  pari_sp av;
+  GEN n;
+
+  if (isintzero(x)) return gcopy(y); /* frequent special case */
+  av = avma; n = gadd(gmul(x, gel(y,2)), gel(y,1));
+  return gerepileupto(av, gred_rfrac_simple(n, gel(y,2)));
+}
+
+/* x "scalar", ty != t_MAT and non-scalar */
+static GEN
+add_scal(GEN y, GEN x, long ty)
+{
+  switch(ty)
+  {
+    case t_POL: return RgX_Rg_add(y, x);
+    case t_SER: return add_ser_scal(y, x);
+    case t_RFRAC: return add_rfrac_scal(y, x);
+    case t_COL: return RgC_Rg_add(y, x);
+    case t_VEC:
+      if (isintzero(x)) return gcopy(y);
+      break;
+  }
+  pari_err_TYPE2("+",x,y);
+  return NULL; /* not reached */
+}
+
+static GEN
+addsub_frac(GEN x, GEN y, GEN (*op)(GEN,GEN))
+{
+  pari_sp av = avma;
+  GEN x1 = gel(x,1), x2 = gel(x,2), z = cgetg(3,t_FRAC);
+  GEN y1 = gel(y,1), y2 = gel(y,2), q, r, n, d, delta;
+  int s = cmpii(x2, y2);
+
+  if (!s)
+  { /* common denominator: (x1 op y1) / x2 */
+    n = op(x1, y1);
+    if (!signe(n)) { avma = av; return gen_0; }
+    d = x2;
+    q = dvmdii(n, d, &r);
+    if (r == gen_0) { avma = av; return icopy(q); }
+    r = gcdii(d, r);
+    if (!is_pm1(r)) { n = diviiexact(n, r); d = diviiexact(d, r); }
+    gel(z,1) = icopy_avma(n, (pari_sp)z);
+    gel(z,2) = icopy_avma(d, (pari_sp)gel(z,1));
+    avma = (pari_sp)gel(z,2); return z;
+  }
+  if (s < 0)
+  {
+    GEN Q = dvmdii(y2, x2, &r);
+    if (r == gen_0)
+    { /* y2 = Q x2: 1/x2 . (Q x1 op y1)/Q, where latter is in coprime form */
+      pari_sp av = avma;
+      n = op(mulii(Q,x1), y1);
+      q = dvmdii(n, x2, &r);
+      if (r == gen_0)
+      {
+        gel(z,1) = gerepileuptoint(av, q);
+        gel(z,2) = Q; return z;
+      }
+      r = gcdii(x2, r);
+      if (!is_pm1(r)) { n = diviiexact(n, r); x2 = diviiexact(x2, r); }
+      d = mulii(x2,Q);
+      gel(z,1) = icopy_avma(n, (pari_sp)z);
+      gel(z,2) = icopy_avma(d, (pari_sp)gel(z,1));
+      avma = (pari_sp)gel(z,2); return z;
+    }
+    delta = gcdii(x2,r);
+  }
+  else
+  {
+    GEN Q = dvmdii(x2, y2, &r);
+    if (r == gen_0)
+    { /* x2 = Q y2: 1/y2 . (x1 op Q y1)/Q, where latter is in coprime form */
+      pari_sp av = avma;
+      n = op(x1, mulii(Q,y1));
+      q = dvmdii(n, y2, &r);
+      if (r == gen_0)
+      {
+        gel(z,1) = gerepileuptoint(av, q);
+        gel(z,2) = Q; return z;
+      }
+      r = gcdii(y2, r);
+      if (!is_pm1(r)) { n = diviiexact(n, r); y2 = diviiexact(y2, r); }
+      d = mulii(y2,Q);
+      gel(z,1) = icopy_avma(n, (pari_sp)z);
+      gel(z,2) = icopy_avma(d, (pari_sp)gel(z,1));
+      avma = (pari_sp)gel(z,2); return z;
+    }
+    delta = gcdii(y2,r);
+  }
+  /* delta = gcd(x2,y2) */
+  if (is_pm1(delta))
+  { /* numerator is non-zero */
+    gel(z,1) = gerepileuptoint((pari_sp)z, op(mulii(x1,y2), mulii(y1,x2)));
+    gel(z,2) = mulii(x2,y2); return z;
+  }
+  x2 = diviiexact(x2,delta);
+  y2 = diviiexact(y2,delta);
+  n = op(mulii(x1,y2), mulii(y1,x2));
+  if (!signe(n)) { avma = av; return gen_0; }
+  d = mulii(x2, y2);
+  q = dvmdii(n, delta, &r);
+  if (r == gen_0)
+  {
+    if (is_pm1(d)) { avma = av; return icopy(q); }
+    avma = (pari_sp)z;
+    gel(z,2) = icopy(d);
+    gel(z,1) = icopy(q); return z;
+  }
+  r = gcdii(delta, r);
+  if (!is_pm1(r))
+  {
+    n     = diviiexact(n, r);
+    delta = diviiexact(delta, r);
+  }
+  d = mulii(d,delta); avma = (pari_sp)z;
+  gel(z,1) = icopy(n);
+  gel(z,2) = icopy(d); return z;
+}
+
+/* assume x2, y2 are t_POLs in the same variable */
+static GEN
+add_rfrac(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN x1 = gel(x,1), x2 = gel(x,2);
+  GEN y1 = gel(y,1), y2 = gel(y,2), q, r, n, d, delta;
+
+  delta = RgX_gcd(x2,y2);
+  if (!degpol(delta))
+  {
+    n = simplify_shallow( gadd(gmul(x1,y2), gmul(y1,x2)) );
+    d = RgX_mul(x2, y2);
+    return gerepileupto(av, gred_rfrac_simple(n, d));
+  }
+  x2 = RgX_div(x2,delta);
+  y2 = RgX_div(y2,delta);
+  n = gadd(gmul(x1,y2), gmul(y1,x2));
+  if (!signe(n))
+  {
+    n = simplify_shallow(n);
+    if (isrationalzero(n)) return gerepileupto(av, n);
+    return gerepilecopy(av, mkrfrac(n, RgX_mul(gel(x,2),y2)));
+  }
+  if (degpol(n) == 0)
+    return gerepileupto(av, gred_rfrac_simple(gel(n,2), RgX_mul(gel(x,2),y2)));
+  q = RgX_divrem(n, delta, &r); /* we want gcd(n,delta) */
+  if (isexactzero(r))
+  {
+    GEN z;
+    d = RgX_mul(x2, y2);
+    /* "constant" denominator ? */
+    z = lg(d) == 3? RgX_Rg_div(q, gel(d,2)): gred_rfrac_simple(q, d);
+    return gerepileupto(av, z);
+  }
+  r = RgX_gcd(delta, r);
+  if (degpol(r))
+  {
+    n = RgX_div(n, r);
+    d = RgX_mul(RgX_mul(x2,y2), RgX_div(delta, r));
+  }
+  else
+    d = RgX_mul(gel(x,2), y2);
+  return gerepileupto(av, gred_rfrac_simple(n, d));
+}
+
+GEN
+gadd(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y), vx, vy, lx, ly, i, l;
+  pari_sp av, tetpil;
+  GEN z, p1;
+
+  if (tx == ty) switch(tx) /* shortcut to generic case */
+  {
+    case t_INT: return addii(x,y);
+    case t_REAL: return addrr(x,y);
+    case t_INTMOD:  { GEN X = gel(x,1), Y = gel(y,1);
+      z = cgetg(3,t_INTMOD);
+      if (X==Y || equalii(X,Y))
+        return add_intmod_same(z, X, gel(x,2), gel(y,2));
+      gel(z,1) = gcdii(X,Y);
+      av = avma; p1 = addii(gel(x,2),gel(y,2));
+      gel(z,2) = gerepileuptoint(av, remii(p1, gel(z,1))); return z;
+    }
+    case t_FRAC: return addsub_frac(x,y,addii);
+    case t_COMPLEX: z = cgetg(3,t_COMPLEX);
+      gel(z,2) = gadd(gel(x,2),gel(y,2));
+      if (isintzero(gel(z,2)))
+      {
+        avma = (pari_sp)(z+3);
+        return gadd(gel(x,1),gel(y,1));
+      }
+      gel(z,1) = gadd(gel(x,1),gel(y,1));
+      return z;
+    case t_PADIC:
+      if (!equalii(gel(x,2),gel(y,2))) pari_err_OP("+",x,y);
+      return addsub_pp(x,y, addii);
+    case t_QUAD: z = cgetg(4,t_QUAD);
+      if (!ZX_equal(gel(x,1),gel(y,1))) pari_err_OP("+",x,y);
+      gel(z,1) = ZX_copy(gel(x,1));
+      gel(z,2) = gadd(gel(x,2),gel(y,2));
+      gel(z,3) = gadd(gel(x,3),gel(y,3)); return z;
+    case t_POLMOD:
+      if (RgX_equal_var(gel(x,1), gel(y,1)))
+        return addsub_polmod_same(gel(x,1), gel(x,2), gel(y,2), &gadd);
+      return addsub_polmod(gel(x,1), gel(y,1), gel(x,2), gel(y,2), &gadd);
+    case t_FFELT: return FF_add(x,y);
+    case t_POL:
+      vx = varn(x);
+      vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return RgX_Rg_add(x, y);
+        else                     return RgX_Rg_add(y, x);
+      }
+      return RgX_add(x, y);
+    case t_SER:
+      vx = varn(x);
+      vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return add_ser_scal(x, y);
+        else                     return add_ser_scal(y, x);
+      }
+      l = valp(y) - valp(x);
+      if (l < 0) { l = -l; swap(x,y); }
+      /* valp(x) <= valp(y) */
+      lx = lg(x);
+      ly = lg(y) + l; if (lx < ly) ly = lx;
+      if (l)
+      {
+        if (l > lx-2) return gcopy(x);
+        z = cgetg(ly,t_SER);
+        for (i=2; i<=l+1; i++) gel(z,i) = gcopy(gel(x,i));
+        for (   ; i < ly; i++) gel(z,i) = gadd(gel(x,i),gel(y,i-l));
+      } else {
+        z = cgetg(ly,t_SER);
+        for (i=2; i < ly; i++) gel(z,i) = gadd(gel(x,i),gel(y,i));
+      }
+      z[1] = x[1]; return normalize(z);
+    case t_RFRAC:
+      vx = varn(gel(x,2));
+      vy = varn(gel(y,2));
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return add_rfrac_scal(x, y);
+        else                     return add_rfrac_scal(y, x);
+      }
+      return add_rfrac(x,y);
+    case t_VEC:
+      if (lg(y) != lg(x)) pari_err_OP("+",x,y);
+      return RgV_add(x,y);
+    case t_COL:
+      if (lg(y) != lg(x)) pari_err_OP("+",x,y);
+      return RgC_add(x,y);
+    case t_MAT:
+      lx = lg(x);
+      if (lg(y) != lx) pari_err_OP("+",x,y);
+      if (lx == 1) return cgetg(1, t_MAT);
+      if (lgcols(y) != lgcols(x)) pari_err_OP("+",x,y);
+      return RgM_add(x,y);
+
+    default: pari_err_TYPE2("+",x,y);
+  }
+  /* tx != ty */
+  if (tx > ty) { swap(x,y); lswap(tx,ty); }
+
+  if (is_const_t(ty)) switch(tx) /* tx < ty, is_const_t(tx) && is_const_t(ty) */
+  {
+    case t_INT:
+      switch(ty)
+      {
+        case t_REAL: return addir(x,y);
+        case t_INTMOD:
+          z = cgetg(3, t_INTMOD);
+          return add_intmod_same(z, gel(y,1), gel(y,2), modii(x, gel(y,1)));
+        case t_FRAC: z = cgetg(3,t_FRAC);
+          gel(z,1) = gerepileuptoint((pari_sp)z, addii(gel(y,1), mulii(gel(y,2),x)));
+          gel(z,2) = icopy(gel(y,2)); return z;
+        case t_COMPLEX: return addRc(x, y);
+        case t_PADIC:
+          if (!signe(x)) return gcopy(y);
+          return addQp(x,y);
+        case t_QUAD: return addRq(x, y);
+        case t_FFELT: return FF_Z_add(y,x);
+      }
+
+    case t_REAL:
+      switch(ty)
+      {
+        case t_FRAC:
+          if (!signe(gel(y,1))) return rcopy(x);
+          if (!signe(x))
+          {
+            lx = expi(gel(y,1)) - expi(gel(y,2)) - expo(x);
+            return lx <= 0? rcopy(x): fractor(y, nbits2prec(lx));
+          }
+          av=avma; z=addir(gel(y,1),mulir(gel(y,2),x)); tetpil=avma;
+          return gerepile(av,tetpil,divri(z,gel(y,2)));
+        case t_COMPLEX: return addRc(x, y);
+        case t_QUAD: return gequal0(y)? rcopy(x): addqf(y, x, lg(x));
+
+        default: pari_err_TYPE2("+",x,y);
+      }
+
+    case t_INTMOD:
+      switch(ty)
+      {
+        case t_FRAC: { GEN X = gel(x,1);
+          z = cgetg(3, t_INTMOD);
+          p1 = Fp_div(gel(y,1), gel(y,2), X);
+          return add_intmod_same(z, X, p1, gel(x,2));
+        }
+        case t_FFELT:
+          if (!equalii(gel(x,1),FF_p_i(y)))
+            pari_err_OP("+",x,y);
+          return FF_Z_add(y,gel(x,2));
+        case t_COMPLEX: return addRc(x, y);
+        case t_PADIC: { GEN X = gel(x,1);
+          z = cgetg(3, t_INTMOD);
+          return add_intmod_same(z, X, gel(x,2), padic_to_Fp(y, X));
+        }
+        case t_QUAD: return addRq(x, y);
+      }
+
+    case t_FRAC:
+      switch (ty)
+      {
+        case t_COMPLEX: return addRc(x, y);
+        case t_PADIC:
+          if (!signe(gel(x,1))) return gcopy(y);
+          return addQp(x,y);
+        case t_QUAD: return addRq(x, y);
+        case t_FFELT: return FF_Q_add(y, x);
+      }
+
+    case t_FFELT:
+      pari_err_TYPE2("+",x,y);
+
+    case t_COMPLEX:
+      switch(ty)
+      {
+        case t_PADIC:
+          return Zp_nosquare_m1(gel(y,2))? addRc(y, x): addTp(x, y);
+        case t_QUAD:
+          lx = precision(x); if (!lx) pari_err_OP("+",x,y);
+          return gequal0(y)? gcopy(x): addqf(y, x, lx);
+      }
+
+    case t_PADIC: /* ty == t_QUAD */
+      return (kro_quad(y,gel(x,2)) == -1)? addRq(x, y): addTp(y, x);
+  }
+  /* tx < ty, !is_const_t(y) */
+  switch(ty)
+  {
+    case t_MAT:
+      if (is_matvec_t(tx)) pari_err_TYPE2("+",x,y);
+      if (isrationalzero(x)) return gcopy(y);
+      return RgM_Rg_add(y, x);
+    case t_COL:
+      if (tx == t_VEC) pari_err_TYPE2("+",x,y);
+      return RgC_Rg_add(y, x);
+    case t_POLMOD: /* is_const_t(tx) in this case */
+      return addsub_polmod_scal(gel(y,1), gel(y,2), x, &gadd);
+  }
+  if (is_scalar_t(tx))  {
+    if (tx == t_POLMOD)
+    {
+      vx = varn(gel(x,1));
+      vy = gvar(y);
+      if (vx == vy) y = gmod(y, gel(x,1)); /* error if ty == t_SER */
+      else
+        if (varncmp(vx,vy) > 0) return add_scal(y, x, ty);
+      return addsub_polmod_scal(gel(x,1), gel(x,2), y, &gadd);
+    }
+    return add_scal(y, x, ty);
+  }
+  /* x and y are not scalars, ty != t_MAT */
+  vx = gvar(x);
+  vy = gvar(y);
+  if (vx != vy) { /* x or y is treated as a scalar */
+    if (is_vec_t(tx) || is_vec_t(ty)) pari_err_TYPE2("+",x,y);
+    return (varncmp(vx, vy) < 0)? add_scal(x, y, tx)
+                                : add_scal(y, x, ty);
+  }
+  /* vx = vy */
+  switch(tx)
+  {
+    case t_POL:
+      switch (ty)
+      {
+        case t_SER:
+          if (lg(x) == 2) return gcopy(y);
+          i = lg(y) + valp(y) - RgX_val(x);
+          if (i < 3) return gcopy(y);
+
+          p1 = RgX_to_ser(x,i); y = gadd(p1,y);
+          settyp(p1, t_VECSMALL); /* p1 left on stack */
+          return y;
+
+        case t_RFRAC: return add_rfrac_scal(y, x);
+      }
+      break;
+
+    case t_SER:
+      if (ty == t_RFRAC)
+      {
+        GEN n, d;
+        long vn, vd;
+        av = avma;
+        n = gel(y,1); vn = gval(n, vy);
+        d = gel(y,2); vd = RgX_valrem(d, &d);
+
+        l = lg(x) + valp(x) - (vn - vd);
+        if (l < 3) { avma = av; return gcopy(x); }
+
+        /* take advantage of y = t^n ! */
+        if (degpol(d))
+          y = gdiv(n, RgX_to_ser_inexact(d,l));
+        else {
+          y = gdiv(n, gel(d,2));
+          if (gvar(y) == vy) y = RgX_to_ser(y,l); else y = scalarser(y, vy, l);
+        }
+        setvalp(y, valp(y) - vd);
+        return gerepileupto(av, gadd(y, x));
+      }
+      break;
+  }
+  pari_err_TYPE2("+",x,y);
+  return NULL; /* not reached */
+}
+
+GEN
+gaddsg(long x, GEN y)
+{
+  long ty = typ(y);
+  GEN z;
+
+  switch(ty)
+  {
+    case t_INT:  return addsi(x,y);
+    case t_REAL: return addsr(x,y);
+    case t_INTMOD:
+      z = cgetg(3, t_INTMOD);
+      return add_intmod_same(z, gel(y,1), gel(y,2), modsi(x, gel(y,1)));
+    case t_FRAC: z = cgetg(3,t_FRAC);
+      gel(z,1) = gerepileuptoint((pari_sp)z, addii(gel(y,1), mulis(gel(y,2),x)));
+      gel(z,2) = icopy(gel(y,2)); return z;
+    case t_COMPLEX:
+      z = cgetg(3, t_COMPLEX);
+      gel(z,1) = gaddsg(x, gel(y,1));
+      gel(z,2) = gcopy(gel(y,2)); return z;
+
+    default: return gadd(stoi(x), y);
+  }
+}
+
+GEN
+gsubsg(long x, GEN y)
+{
+  GEN z, a, b;
+  pari_sp av;
+
+  switch(typ(y))
+  {
+    case t_INT:  return subsi(x,y);
+    case t_REAL: return subsr(x,y);
+    case t_INTMOD:
+      z = cgetg(3, t_INTMOD); a = gel(y,1); b = gel(y,2);
+      return add_intmod_same(z, a, Fp_neg(b,a), modsi(x, a));
+    case t_FRAC: z = cgetg(3,t_FRAC); a = gel(y,1); b = gel(y,2);
+      gel(z,1) = gerepileuptoint((pari_sp)z, subii(mulis(b,x), a));
+      gel(z,2) = icopy(gel(y,2)); return z;
+    case t_COMPLEX:
+      z = cgetg(3, t_COMPLEX);
+      gel(z,1) = gsubsg(x, gel(y,1));
+      gel(z,2) = gneg(gel(y,2)); return z;
+  }
+  av = avma;
+  return gerepileupto(av, gadd(stoi(x), gneg_i(y)));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                          SUBTRACTION                           **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+gsub(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y);
+  pari_sp av;
+  GEN z;
+  if (tx == ty) switch(tx) /* shortcut to generic case */
+  {
+    case t_INT: return subii(x,y);
+    case t_REAL: return subrr(x,y);
+    case t_INTMOD:  { GEN p1, X = gel(x,1), Y = gel(y,1);
+      z = cgetg(3,t_INTMOD);
+      if (X==Y || equalii(X,Y))
+        return sub_intmod_same(z, X, gel(x,2), gel(y,2));
+      gel(z,1) = gcdii(X,Y);
+      av = avma; p1 = subii(gel(x,2),gel(y,2));
+      gel(z,2) = gerepileuptoint(av, modii(p1, gel(z,1))); return z;
+    }
+    case t_FRAC: return addsub_frac(x,y, subii);
+    case t_COMPLEX: z = cgetg(3,t_COMPLEX);
+      gel(z,2) = gsub(gel(x,2),gel(y,2));
+      if (isintzero(gel(z,2)))
+      {
+        avma = (pari_sp)(z+3);
+        return gsub(gel(x,1),gel(y,1));
+      }
+      gel(z,1) = gsub(gel(x,1),gel(y,1));
+      return z;
+    case t_PADIC:
+      if (!equalii(gel(x,2),gel(y,2))) pari_err_OP("+",x,y);
+      return addsub_pp(x,y, subii);
+    case t_QUAD: z = cgetg(4,t_QUAD);
+      if (!ZX_equal(gel(x,1),gel(y,1))) pari_err_OP("+",x,y);
+      gel(z,1) = ZX_copy(gel(x,1));
+      gel(z,2) = gsub(gel(x,2),gel(y,2));
+      gel(z,3) = gsub(gel(x,3),gel(y,3)); return z;
+    case t_POLMOD:
+      if (RgX_equal_var(gel(x,1), gel(y,1)))
+        return addsub_polmod_same(gel(x,1), gel(x,2), gel(y,2), &gsub);
+      return addsub_polmod(gel(x,1), gel(y,1), gel(x,2), gel(y,2), &gsub);
+    case t_FFELT: return FF_sub(x,y);
+    case t_POL: {
+      long vx = varn(x);
+      long vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return RgX_Rg_sub(x, y);
+        else                     return Rg_RgX_sub(x, y);
+      }
+      return RgX_sub(x, y);
+    }
+    case t_VEC:
+      if (lg(y) != lg(x)) pari_err_OP("+",x,y);
+      return RgV_sub(x,y);
+    case t_COL:
+      if (lg(y) != lg(x)) pari_err_OP("+",x,y);
+      return RgC_sub(x,y);
+    case t_MAT: {
+      long lx = lg(x);
+      if (lg(y) != lx) pari_err_OP("+",x,y);
+      if (lx == 1) return cgetg(1, t_MAT);
+      if (lgcols(y) != lgcols(x)) pari_err_OP("+",x,y);
+      return RgM_sub(x,y);
+    }
+    case t_RFRAC: case t_SER: break;
+
+    default: pari_err_TYPE2("+",x,y);
+  }
+  av = avma;
+  return gerepileupto(av, gadd(x,gneg_i(y)));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        MULTIPLICATION                          **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+mul_ser_scal(GEN y, GEN x) {
+  long ly, i;
+  GEN z;
+  if (isrationalzero(x)) return zeropol(varn(y));
+  z = cgetg_copy(y, &ly); z[1] = y[1];
+  for (i = 2; i < ly; i++) gel(z,i) = gmul(x,gel(y,i));
+  return normalize(z);
+}
+/* (n/d) * x, x "scalar" or polynomial in the same variable as d
+ * [n/d a valid RFRAC]  */
+static GEN
+mul_rfrac_scal(GEN n, GEN d, GEN x)
+{
+  pari_sp av = avma;
+  GEN z;
+
+  switch(typ(x))
+  {
+    case t_PADIC:
+      n = gmul(n, x);
+      d = gcvtop(d, gel(x,2), signe(gel(x,4))? precp(x): 1);
+      return gerepileupto(av, gdiv(n,d));
+
+    case t_INTMOD: case t_POLMOD:
+      n = gmul(n, x);
+      d = gmul(d, gmodulo(gen_1, gel(x,1)));
+      return gerepileupto(av, gdiv(n,d));
+  }
+  z = gred_rfrac2_i(x, d);
+  n = simplify_shallow(n);
+  if (typ(z) == t_RFRAC)
+  {
+    n = gmul(gel(z,1), n);
+    d = gel(z,2);
+    if (typ(n) == t_POL && varncmp(varn(n), varn(d)) < 0)
+      z = RgX_Rg_div(n, d);
+    else
+      z = gred_rfrac_simple(n, d);
+  }
+  else
+    z = gmul(z, n);
+  return gerepileupto(av, z);
+}
+static GEN
+mul_scal(GEN y, GEN x, long ty)
+{
+  switch(ty)
+  {
+    case t_POL:
+      if (lg(y) == 2) return scalarpol(gmul(gen_0,x), varn(y));
+      return RgX_Rg_mul(y, x);
+    case t_SER: return mul_ser_scal(y, x);
+    case t_RFRAC: return mul_rfrac_scal(gel(y,1),gel(y,2), x);
+    case t_QFI: case t_QFR:
+      if (typ(x) == t_INT && gequal1(x)) return gcopy(y); /* fall through */
+  }
+  pari_err_TYPE2("*",x,y);
+  return NULL; /* not reached */
+}
+
+static GEN
+mul_gen_rfrac(GEN X, GEN Y)
+{
+  GEN y1 = gel(Y,1), y2 = gel(Y,2);
+  long vx = gvar(X), vy = varn(y2);
+  return (varncmp(vx, vy) <= 0)? mul_scal(Y, X, typ(Y)):
+                                 gred_rfrac_simple(gmul(y1,X), y2);
+}
+/* (x1/x2) * (y1/y2) */
+static GEN
+mul_rfrac(GEN x1, GEN x2, GEN y1, GEN y2)
+{
+  GEN z, X, Y;
+  pari_sp av = avma;
+
+  X = gred_rfrac2_i(x1, y2);
+  Y = gred_rfrac2_i(y1, x2);
+  if (typ(X) == t_RFRAC)
+  {
+    if (typ(Y) == t_RFRAC) {
+      x1 = gel(X,1);
+      x2 = gel(X,2);
+      y1 = gel(Y,1);
+      y2 = gel(Y,2);
+      z = gred_rfrac_simple(gmul(x1,y1), gmul(x2,y2));
+    } else
+      z = mul_gen_rfrac(Y, X);
+  }
+  else if (typ(Y) == t_RFRAC)
+    z = mul_gen_rfrac(X, Y);
+  else
+    z = gmul(X, Y);
+  return gerepileupto(av, z);
+}
+/* (x1/x2) /y2, x2 and y2 are t_POL in the same variable */
+static GEN
+div_rfrac_pol(GEN x1, GEN x2, GEN y2)
+{
+  pari_sp av = avma;
+  GEN X = gred_rfrac2_i(x1, y2);
+  if (typ(X) == t_RFRAC && varn(gel(X,2)) == varn(x2))
+  {
+    x2 = RgX_mul(gel(X,2), x2);
+    x1 = gel(X,1);
+  }
+  else
+    x1 = X;
+  return gerepileupto(av, gred_rfrac_simple(x1, x2));
+}
+
+/* Mod(y, Y) * x,  assuming x scalar */
+static GEN
+mul_polmod_scal(GEN Y, GEN y, GEN x)
+{
+  GEN z = cgetg(3,t_POLMOD);
+  gel(z,1) = RgX_copy(Y);
+  gel(z,2) = gmul(x,y); return z;
+}
+
+/* cf mulqq */
+static GEN
+quad_polmod_mul(GEN P, GEN x, GEN y)
+{
+  GEN T = cgetg(4, t_POL), b = gel(P,3), c = gel(P,2), p1, p2, p3, p4;
+  pari_sp tetpil, av = avma;
+  T[1] = x[1];
+  p2 = gmul(gel(x,2), gel(y,2));
+  p3 = gmul(gel(x,3), gel(y,3));
+  p1 = gmul(gneg_i(c),p3);
+  /* operands are usually small: gadd ~ gmul and Karatsuba is a waste */
+  if (typ(b) == t_INT)
+  {
+    if (signe(b))
+    {
+      p4 = gadd(gmul(gel(x,2), gel(y,3)), gmul(gel(x,3), gel(y,2)));
+      if (is_pm1(b))
+      {
+        if (signe(b) > 0) p3 = gneg(p3);
+      }
+      else
+        p3 = gmul(negi(b), p3);
+    }
+    else
+    {
+      p3 = gmul(gel(x,2),gel(y,3));
+      p4 = gmul(gel(x,3),gel(y,2));
+    }
+  }
+  else
+  {
+    p4 = gadd(gmul(gel(x,2), gel(y,3)), gmul(gel(x,3), gel(y,2)));
+    p3 = gmul(gneg_i(b), p3);
+  }
+  tetpil = avma;
+  gel(T,2) = gadd(p2, p1);
+  gel(T,3) = gadd(p4, p3);
+  gerepilecoeffssp(av,tetpil,T+2,2);
+  return normalizepol_lg(T,4);
+}
+/* Mod(x,T) * Mod(y,T) */
+static GEN
+mul_polmod_same(GEN T, GEN x, GEN y)
+{
+  GEN z = cgetg(3,t_POLMOD), a;
+  long v = varn(T), lx = lg(x), ly = lg(y);
+  gel(z,1) = RgX_copy(T);
+  /* x * y mod T optimised */
+  if (typ(x) != t_POL || varn(x) != v || lx <= 3
+   || typ(y) != t_POL || varn(y) != v || ly <= 3)
+    a = gmul(x, y);
+  else
+  {
+    if (lg(T) == 5 && isint1(gel(T,4))) /* quadratic fields */
+      a = quad_polmod_mul(T, x, y);
+    else
+    {
+      pari_sp av = avma;
+      GEN p = NULL;
+      if (RgX_is_FpX(T, &p) && RgX_is_FpX(x, &p) && RgX_is_FpX(y, &p) && p)
+      {
+        T = RgX_to_FpX(T, p); x = RgX_to_FpX(x, p); y = RgX_to_FpX(y, p);
+        if (lgefint(p) == 3)
+        {
+          ulong pp = p[2];
+          a = Flxq_mul(ZX_to_Flx(x, pp), ZX_to_Flx(y, pp), ZX_to_Flx(T, pp), pp);
+          a = Flx_to_ZX(a);
+        }
+        else
+          a = FpXQ_mul(x, y, T, p);
+        a = FpX_to_mod(a, p);
+      }
+      else
+        a = RgXQ_mul(x, y, gel(z,1));
+      a = gerepileupto(av, a);
+    }
+  }
+  gel(z,2) = a; return z;
+}
+static GEN
+sqr_polmod(GEN T, GEN x)
+{
+  GEN a, z = cgetg(3,t_POLMOD);
+  gel(z,1) = RgX_copy(T);
+  if (typ(x) != t_POL || varn(x) != varn(T) || lg(x) <= 3)
+    a = gsqr(x);
+  else
+  {
+    pari_sp av = avma;
+    GEN p = NULL;
+    if (RgX_is_FpX(T, &p) && RgX_is_FpX(x, &p) && p)
+    {
+      T = RgX_to_FpX(T, p); x = RgX_to_FpX(x, p);
+      if (lgefint(p) == 3)
+      {
+        ulong pp = p[2];
+        a = Flxq_sqr(ZX_to_Flx(x, pp), ZX_to_Flx(T, pp), pp);
+        a = Flx_to_ZX(a);
+      }
+      else
+        a = FpXQ_sqr(x, T, p);
+      a = FpX_to_mod(a, p);
+    }
+    else
+      a = RgXQ_sqr(x, gel(z,1));
+    a = gerepileupto(av, a);
+  }
+  gel(z,2) = a; return z;
+}
+/* Mod(x,X) * Mod(y,Y) */
+static GEN
+mul_polmod(GEN X, GEN Y, GEN x, GEN y)
+{
+  long T[3] = { evaltyp(t_POLMOD) | _evallg(3),0,0 };
+  long vx = varn(X), vy = varn(Y);
+  GEN z = cgetg(3,t_POLMOD);
+
+  if (vx==vy) {
+    pari_sp av;
+    gel(z,1) = RgX_gcd(X,Y); av = avma;
+    gel(z,2) = gerepileupto(av, gmod(gmul(x, y), gel(z,1)));
+    return z;
+  }
+  if (varncmp(vx, vy) < 0)
+  { gel(z,1) = RgX_copy(X); gel(T,1) = Y; gel(T,2) = y; y = T; }
+  else
+  { gel(z,1) = RgX_copy(Y); gel(T,1) = X; gel(T,2) = x; x = T; }
+  gel(z,2) = gmul(x, y); return z;
+}
+
+#if 0 /* used by 3M only */
+/* set z = x+y and return 1 if x,y have the same sign
+ * set z = x-y and return 0 otherwise */
+static int
+did_add(GEN x, GEN y, GEN *z)
+{
+  long tx = typ(x), ty = typ(y);
+  if (tx == ty) switch(tx)
+  {
+    case t_INT: *z = addii(x,y); return 1;
+    case t_FRAC: *z = addsub_frac(x,y,addii); return 1;
+    case t_REAL:
+      if (signe(x) == -signe(y))
+      { *z = subrr(x,y); return 0; }
+      else
+      { *z = addrr(x,y); return 1; }
+  }
+  if (tx == t_REAL) switch(ty)
+  {
+    case t_INT:
+      if (signe(x) == -signe(y))
+      { *z = subri(x,y); return 0; }
+      else
+      { *z = addri(x,y); return 1; }
+    case t_FRAC:
+      if (signe(x) == -signe(gel(y,1)))
+      { *z = gsub(x,y); return 0; }
+      else
+      { *z = gadd(x,y); return 1; }
+  }
+  else if (ty == t_REAL) switch(tx)
+  {
+    case t_INT:
+      if (signe(x) == -signe(y))
+      { *z = subir(x,y); return 0; }
+      else
+      { *z = addir(x,y); return 1; }
+    case t_FRAC:
+      if (signe(gel(x,1)) == -signe(y))
+      { *z = gsub(x,y); return 0; }
+      else
+      { *z = gadd(x,y); return 1; }
+  }
+  *z = gadd(x,y); return 1;
+}
+#endif
+/* x * I * y, x t_COMPLEX with non-intzero real part, y non-intzero "scalar" */
+static GEN
+mulcIR(GEN x, GEN y)
+{
+  GEN z = cgetg(3,t_COMPLEX);
+  pari_sp av = avma;
+  gel(z,1) = gerepileupto(av, gneg(gmul(y,gel(x,2))));
+  gel(z,2) = gmul(y, gel(x,1));
+  return z;
+
+}
+/* x,y COMPLEX */
+static GEN
+mulcc(GEN x, GEN y)
+{
+  GEN xr = gel(x,1), xi = gel(x,2);
+  GEN yr = gel(y,1), yi = gel(y,2);
+  GEN p1, p2, p3, p4, z;
+  pari_sp tetpil, av;
+
+  if (isintzero(xr))
+  {
+    if (isintzero(yr)) {
+      av = avma;
+      return gerepileupto(av, gneg(gmul(xi,yi)));
+    }
+    return mulcIR(y, xi);
+  }
+  if (isintzero(yr)) return mulcIR(x, yi);
+
+  z = cgetg(3,t_COMPLEX); av = avma;
+#if 0
+  /* 3M method avoiding catastrophic cancellation, BUT loses accuracy due to
+   * e.g. xr + xi if exponents differ */
+  if (did_add(xr, xi, &p3))
+  {
+    if (did_add(yr, yi, &p4)) {
+    /* R = xr*yr - xi*yi
+     * I = (xr+xi)(yr+yi) - xr*yr - xi*yi */
+      p1 = gmul(xr,yr);
+      p2 = gmul(xi,yi); p2 = gneg(p2);
+      p3 = gmul(p3, p4);
+      p4 = gsub(p2, p1);
+    } else {
+    /* R = (xr + xi) * (yr - yi) + (xr * yi - xi * yr)
+     * I = xr*yi + xi*yr */
+      p1 = gmul(p3,p4);
+      p3 = gmul(xr,yi);
+      p4 = gmul(xi,yr);
+      p2 = gsub(p3, p4);
+    }
+  } else {
+    if (did_add(yr, yi, &p4)) {
+     /* R = (xr - xi) * (yr + yi) + (xi * yr - xr * yi)
+      * I = xr*yi +xi*yr */
+      p1 = gmul(p3,p4);
+      p3 = gmul(xr,yi);
+      p4 = gmul(xi,yr);
+      p2 = gsub(p4, p3);
+    } else {
+    /* R = xr*yr - xi*yi
+     * I = -(xr-xi)(yr-yi) + xr*yr + xi*yi */
+      p3 = gneg( gmul(p3, p4) );
+      p1 = gmul(xr,yr);
+      p2 = gmul(xi,yi);
+      p4 = gadd(p1, p2);
+
+      p2 = gneg(p2);
+    }
+  }
+  tetpil = avma;
+  gel(z,1) = gadd(p1,p2);
+  gel(z,2) = gadd(p3,p4);
+#else
+  if (typ(xr)==t_INT && typ(yr)==t_INT && typ(xi)==t_INT && typ(yr)==t_INT)
+  { /* 3M formula */
+    p3 = addii(xr,xi);
+    p4 = addii(yr,yi);
+    p1 = mulii(xr,yr);
+    p2 = mulii(xi,yi);
+    p3 = mulii(p3,p4);
+    p4 = addii(p2,p1);
+    tetpil = avma;
+    gel(z,1) = subii(p1,p2);
+    gel(z,2) = subii(p3,p4);
+    if (!signe(gel(z,2)))
+      return gerepileuptoint((pari_sp)(z+3), gel(z,1));
+  }
+  else
+  { /* naive 4M formula: avoid all loss of accuracy */
+    p1 = gmul(xr,yr);
+    p2 = gmul(xi,yi);
+    p3 = gmul(xr,yi);
+    p4 = gmul(xi,yr);
+    tetpil = avma;
+    gel(z,1) = gsub(p1,p2);
+    gel(z,2) = gadd(p3,p4);
+    if (isintzero(gel(z,2)))
+    {
+      cgiv(gel(z,2));
+      return gerepileupto((pari_sp)(z+3), gel(z,1));
+    }
+  }
+#endif
+
+  gerepilecoeffssp(av,tetpil, z+1,2); return z;
+}
+/* x,y PADIC */
+static GEN
+mulpp(GEN x, GEN y) {
+  long l = valp(x) + valp(y);
+  pari_sp av;
+  GEN z, t;
+  if (!equalii(gel(x,2),gel(y,2))) pari_err_OP("*",x,y);
+  if (!signe(gel(x,4))) return zeropadic(gel(x,2), l);
+  if (!signe(gel(y,4))) return zeropadic(gel(x,2), l);
+
+  t = (precp(x) > precp(y))? y: x;
+  z = cgetp(t); setvalp(z,l); av = avma;
+  affii(remii(mulii(gel(x,4),gel(y,4)), gel(t,3)), gel(z,4));
+  avma = av; return z;
+}
+/* x,y QUAD */
+static GEN
+mulqq(GEN x, GEN y) {
+  GEN z = cgetg(4,t_QUAD);
+  GEN p1, p2, p3, p4, P = gel(x,1), b = gel(P,3), c = gel(P,2);
+  pari_sp av, tetpil;
+  if (!ZX_equal(P, gel(y,1))) pari_err_OP("*",x,y);
+
+  gel(z,1) = ZX_copy(P); av = avma;
+  p2 = gmul(gel(x,2),gel(y,2));
+  p3 = gmul(gel(x,3),gel(y,3));
+  p1 = gmul(gneg_i(c),p3);
+
+  if (signe(b))
+    p4 = gadd(gmul(gel(x,2),gel(y,3)), gmul(gel(x,3),gel(y,2)));
+  else
+  {
+    p3 = gmul(gel(x,2),gel(y,3));
+    p4 = gmul(gel(x,3),gel(y,2));
+  }
+  tetpil = avma;
+  gel(z,2) = gadd(p2,p1);
+  gel(z,3) = gadd(p4,p3);
+  gerepilecoeffssp(av,tetpil,z+2,2); return z;
+}
+
+GEN
+mulcxI(GEN x)
+{
+  GEN z;
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return mkcomplex(gen_0, x);
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) return gneg(gel(x,2));
+      z = cgetg(3,t_COMPLEX);
+      gel(z,1) = gneg(gel(x,2));
+      gel(z,2) = gel(x,1); return z;
+    default:
+      return gmul(gen_I(), x);
+  }
+}
+GEN
+mulcxmI(GEN x)
+{
+  GEN z;
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return mkcomplex(gen_0, gneg(x));
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) return gel(x,2);
+      z = cgetg(3,t_COMPLEX);
+      gel(z,1) = gel(x,2);
+      gel(z,2) = gneg(gel(x,1)); return z;
+    default:
+      return gmul(mkcomplex(gen_0, gen_m1), x);
+  }
+}
+
+/* fill in coefficients of t_SER z from coeffs of t_POL y */
+static GEN
+fill_ser(GEN z, GEN y)
+{
+  long i, lx = lg(z), ly = lg(y);
+  if (ly >= lx) {
+    for (i = 2; i < lx; i++) gel(z,i) = gel(y,i);
+  } else {
+    for (i = 2; i < ly; i++) gel(z,i) = gel(y,i);
+    for (     ; i < lx; i++) gel(z,i) = gen_0;
+  }
+  return normalize(z);
+}
+
+GEN
+gmul(GEN x, GEN y)
+{
+  long tx, ty, lx, ly, vx, vy, i, l;
+  pari_sp av, tetpil;
+  GEN z, p1, p2;
+
+  if (x == y) return gsqr(x);
+  tx = typ(x); ty = typ(y);
+  if (tx == ty) switch(tx)
+  {
+    case t_INT: return mulii(x,y);
+    case t_REAL: return mulrr(x,y);
+    case t_INTMOD: { GEN X = gel(x,1), Y = gel(y,1);
+      z = cgetg(3,t_INTMOD);
+      if (X==Y || equalii(X,Y))
+        return mul_intmod_same(z, X, gel(x,2), gel(y,2));
+      gel(z,1) = gcdii(X,Y); av = avma; p1 = mulii(gel(x,2),gel(y,2));
+      gel(z,2) = gerepileuptoint(av, remii(p1, gel(z,1))); return z;
+    }
+    case t_FRAC:
+    {
+      GEN x1 = gel(x,1), x2 = gel(x,2);
+      GEN y1 = gel(y,1), y2 = gel(y,2);
+      z=cgetg(3,t_FRAC);
+      p1 = gcdii(x1, y2);
+      if (!is_pm1(p1)) { x1 = diviiexact(x1,p1); y2 = diviiexact(y2,p1); }
+      p1 = gcdii(x2, y1);
+      if (!is_pm1(p1)) { x2 = diviiexact(x2,p1); y1 = diviiexact(y1,p1); }
+      tetpil = avma;
+      gel(z,2) = mulii(x2,y2);
+      gel(z,1) = mulii(x1,y1);
+      fix_frac_if_int_GC(z,tetpil); return z;
+    }
+    case t_COMPLEX: return mulcc(x, y);
+    case t_PADIC: return mulpp(x, y);
+    case t_QUAD: return mulqq(x, y);
+    case t_FFELT: return FF_mul(x, y);
+    case t_POLMOD:
+      if (RgX_equal_var(gel(x,1), gel(y,1)))
+        return mul_polmod_same(gel(x,1), gel(x,2), gel(y,2));
+      return mul_polmod(gel(x,1), gel(y,1), gel(x,2), gel(y,2));
+    case t_POL:
+      vx = varn(x);
+      vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return RgX_Rg_mul(x, y);
+        else                     return RgX_Rg_mul(y, x);
+      }
+      if (RgX_is_ZX(x) && RgX_is_ZX(y)) return ZX_mul(x,y);
+      return RgX_mul(x, y);
+
+    case t_SER: {
+      GEN p = NULL;
+      vx = varn(x);
+      vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return mul_ser_scal(x, y);
+        else                     return mul_ser_scal(y, x);
+      }
+      lx = lg(x);
+      ly = lg(y); if (lx > ly) { lx = ly; swap(x, y); }
+      if (lx == 2) return zeroser(vx, valp(x)+valp(y));
+      z = cgetg(lx,t_SER);
+      z[1] = evalvalp(valp(x)+valp(y)) | evalvarn(vx) | evalsigne(1);
+      x = ser2pol_i(x, lx);
+      y = ser2pol_i(y, lx);
+      if (RgX_is_FpX(x,&p) && RgX_is_FpX(y,&p))
+      {
+        if (!p) y = ZX_mul(x,y);
+        else
+        {
+          x = RgX_to_FpX(x, p);
+          y = RgX_to_FpX(y, p);
+          y = FpX_to_mod(ZX_mul(x,y), p);
+        }
+      }
+      else
+        y = RgX_mullow(x, y, lx-2);
+      z = fill_ser(z, y);
+      return gerepilecopy((pari_sp)(z + lx), z);
+    }
+    case t_QFI: return qficomp(x,y);
+    case t_QFR: return qfrcomp(x,y);
+    case t_RFRAC: return mul_rfrac(gel(x,1),gel(x,2), gel(y,1),gel(y,2));
+    case t_MAT: return RgM_mul(x, y);
+
+    case t_VECSMALL: /* multiply as permutation. cf perm_mul */
+      z = cgetg_copy(x, &l);
+      if (l != lg(y)) break;
+      for (i=1; i<l; i++)
+      {
+        long yi = y[i];
+        if (yi < 1 || yi >= l) pari_err_TYPE2("*",x,y);
+        z[i] = x[yi];
+      }
+      return z;
+
+
+    default:
+      pari_err_TYPE2("*",x,y);
+  }
+  /* tx != ty */
+  if (is_const_t(ty) && is_const_t(tx))  {
+    if (tx > ty) { swap(x,y); lswap(tx,ty); }
+    switch(tx) {
+    case t_INT:
+      switch(ty)
+      {
+        case t_REAL: return signe(x)? mulir(x,y): gen_0;
+        case t_INTMOD:
+          z = cgetg(3, t_INTMOD);
+          return mul_intmod_same(z, gel(y,1), gel(y,2), modii(x, gel(y,1)));
+        case t_FRAC:
+          if (!signe(x)) return gen_0;
+          z=cgetg(3,t_FRAC);
+          p1 = gcdii(x,gel(y,2));
+          if (is_pm1(p1))
+          {
+            avma = (pari_sp)z;
+            gel(z,2) = icopy(gel(y,2));
+            gel(z,1) = mulii(gel(y,1), x);
+          }
+          else
+          {
+            x = diviiexact(x,p1); tetpil = avma;
+            gel(z,2) = diviiexact(gel(y,2), p1);
+            gel(z,1) = mulii(gel(y,1), x);
+            fix_frac_if_int_GC(z,tetpil);
+          }
+          return z;
+        case t_COMPLEX: return signe(x)? mulRc(x, y): gen_0;
+        case t_PADIC: return signe(x)? mulTp(x, y): gen_0;
+        case t_QUAD: return mulRq(x,y);
+        case t_FFELT: return FF_Z_mul(y,x);
+      }
+
+    case t_REAL:
+      switch(ty)
+      {
+        case t_FRAC: return mulrfrac(x, y);
+        case t_COMPLEX: return mulRc(x, y);
+        case t_QUAD: return mulqf(y, x, lg(x));
+        default: pari_err_TYPE2("*",x,y);
+      }
+
+    case t_INTMOD:
+      switch(ty)
+      {
+        case t_FRAC: { GEN X = gel(x,1);
+          z = cgetg(3, t_INTMOD); p1 = Fp_mul(gel(y,1), gel(x,2), X);
+          return div_intmod_same(z, X, p1, remii(gel(y,2), X));
+        }
+        case t_COMPLEX: return mulRc_direct(x,y);
+        case t_PADIC: { GEN X = gel(x,1);
+          z = cgetg(3, t_INTMOD);
+          return mul_intmod_same(z, X, gel(x,2), padic_to_Fp(y, X));
+        }
+        case t_QUAD: return mulRq(x, y);
+        case t_FFELT:
+          if (!equalii(gel(x,1),FF_p_i(y)))
+            pari_err_OP("*",x,y);
+          return FF_Z_mul(y,gel(x,2));
+      }
+
+    case t_FRAC:
+      switch(ty)
+      {
+        case t_COMPLEX: return mulRc(x, y);
+        case t_PADIC: return signe(gel(x,1))? mulTp(x, y): gen_0;
+        case t_QUAD:  return mulRq(x, y);
+        case t_FFELT: return FF_Z_Z_muldiv(y, gel(x,1),gel(x,2));
+      }
+
+    case t_FFELT:
+      pari_err_TYPE2("*",x,y);
+
+    case t_COMPLEX:
+      switch(ty)
+      {
+        case t_PADIC:
+          return Zp_nosquare_m1(gel(y,2))? mulRc(y, x): mulTp(x, y);
+        case t_QUAD:
+          lx = precision(x); if (!lx) pari_err_OP("*",x,y);
+          return mulqf(y, x, lx);
+      }
+
+    case t_PADIC: /* ty == t_QUAD */
+      return (kro_quad(y,gel(x,2))== -1)? mulRq(x, y): mulTp(y, x);
+    }
+  }
+
+  if (is_matvec_t(ty))
+  {
+    if (!is_matvec_t(tx))
+    {
+      if (is_noncalc_t(tx)) pari_err_TYPE2( "*",x,y); /* necessary if ly = 1 */
+      z = cgetg_copy(y, &ly);
+      for (i=1; i<ly; i++) gel(z,i) = gmul(x,gel(y,i));
+      return z;
+    }
+    switch(tx)
+    {
+      case t_VEC:
+        switch(ty) {
+          case t_COL: return RgV_RgC_mul(x,y);
+          case t_MAT: return RgV_RgM_mul(x,y);
+        }
+        break;
+      case t_COL:
+        switch(ty) {
+          case t_VEC: return RgC_RgV_mul(x,y);
+          case t_MAT: return RgC_RgM_mul(x,y);
+        }
+        break;
+      case t_MAT:
+        switch(ty) {
+          case t_VEC: return RgM_RgV_mul(x,y);
+          case t_COL: return RgM_RgC_mul(x,y);
+        }
+    }
+  }
+  if (is_matvec_t(tx))
+  {
+    if (is_noncalc_t(ty)) pari_err_TYPE2( "*",x,y); /* necessary if lx = 1 */
+    z = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(z,i) = gmul(y,gel(x,i));
+    return z;
+  }
+  if (tx > ty) { swap(x,y); lswap(tx,ty); }
+  /* tx < ty, !ismatvec(x and y) */
+
+  if (ty == t_POLMOD) /* is_const_t(tx) in this case */
+    return mul_polmod_scal(gel(y,1), gel(y,2), x);
+  if (is_scalar_t(tx)) {
+    if (tx == t_POLMOD) {
+      vx = varn(gel(x,1));
+      vy = gvar(y);
+      if (vx != vy) {
+        if (varncmp(vx,vy) > 0) return mul_scal(y, x, ty);
+        return mul_polmod_scal(gel(x,1), gel(x,2), y);
+      }
+      /* error if ty == t_SER */
+      av = avma; y = gmod(y, gel(x,1));
+      return gerepileupto(av, mul_polmod_same(gel(x,1), gel(x,2), y));
+    }
+    return mul_scal(y, x, ty);
+  }
+
+  /* x and y are not scalars, nor matvec */
+  vx = gvar(x);
+  vy = gvar(y);
+  if (vx != vy) /* x or y is treated as a scalar */
+    return (varncmp(vx, vy) < 0)? mul_scal(x, y, tx)
+                                : mul_scal(y, x, ty);
+  /* vx = vy */
+  switch(tx)
+  {
+    case t_POL:
+      switch (ty)
+      {
+        case t_SER:
+        {
+          long vn;
+          if (lg(x) == 2) return zeropol(vx);
+          if (lg(y) == 2) return zeroser(vx, valp(y)+RgX_val(x));
+          av = avma;
+          vn = RgX_valrem(x, &x);
+          /* take advantage of x = t^n ! */
+          if (degpol(x)) {
+            p1 = RgX_to_ser(x,lg(y));
+            if (vn) settyp(x, t_VECSMALL); /* *new* x left on stack */
+            p2 = gmul(p1,y);
+            settyp(p1, t_VECSMALL); /* p1 left on stack */
+          } else {
+            avma = av;
+            p2 = mul_ser_scal(y, gel(x,2));
+          }
+          setvalp(p2, valp(p2) + vn);
+          return p2;
+        }
+
+        case t_RFRAC: return mul_rfrac_scal(gel(y,1),gel(y,2), x);
+      }
+      break;
+
+    case t_SER:
+      switch (ty)
+      {
+        case t_RFRAC:
+          av = avma;
+          return gerepileupto(av, gdiv(gmul(gel(y,1),x), gel(y,2)));
+      }
+      break;
+  }
+  pari_err_TYPE2("*",x,y);
+  return NULL; /* not reached */
+}
+
+int
+ff_poltype(GEN *x, GEN *p, GEN *pol)
+{
+  GEN Q, P = *x;
+  if (!signe(P)) return 0;
+  if (!RgX_is_FpXQX(P,pol,p) || !*p || !*pol)
+    return 0;
+  Q = RgX_to_FpX(*pol, *p);
+  P = RgX_to_FpXQX(P, Q, *p);
+  *x = ZXX_to_Kronecker(P, degpol(Q));
+  *pol = Q;
+  return 1;
+}
+
+GEN
+sqr_ser_part(GEN x, long l1, long l2)
+{
+  long i, j, l;
+  pari_sp av;
+  GEN Z, z, p1, p2;
+  long mi;
+  if (l2 < l1) return zeroser(varn(x), 2*valp(x));
+  p2 = cgetg(l2+2, t_VECSMALL)+1; /* left on stack on exit */
+  Z = cgetg(l2-l1+3, t_SER);
+  Z[1] = evalvalp(2*valp(x)) | evalvarn(varn(x));
+  z = Z + 2-l1;
+  x += 2; mi = 0;
+  for (i=0; i<l1; i++)
+  {
+    p2[i] = !isrationalzero(gel(x,i)); if (p2[i]) mi = i;
+  }
+
+  for (i=l1; i<=l2; i++)
+  {
+    p2[i] = !isrationalzero(gel(x,i)); if (p2[i]) mi = i;
+    p1=gen_0; av=avma; l=((i+1)>>1) - 1;
+    for (j=i-mi; j<=minss(l,mi); j++)
+      if (p2[j] && p2[i-j]) p1 = gadd(p1, gmul(gel(x,j),gel(x,i-j)));
+    p1 = gshift(p1,1);
+    if ((i&1) == 0 && p2[i>>1])
+      p1 = gadd(p1, gsqr(gel(x,i>>1)));
+    gel(z,i) = gerepileupto(av,p1);
+  }
+  return Z;
+}
+
+GEN
+gsqr(GEN x)
+{
+  long i, lx;
+  pari_sp av, tetpil;
+  GEN z, p1, p2, p3, p4;
+
+  switch(typ(x))
+  {
+    case t_INT: return sqri(x);
+    case t_REAL: return sqrr(x);
+    case t_INTMOD: { GEN X = gel(x,1);
+      z = cgetg(3,t_INTMOD);
+      gel(z,2) = gerepileuptoint((pari_sp)z, remii(sqri(gel(x,2)), X));
+      gel(z,1) = icopy(X); return z;
+    }
+    case t_FRAC: return sqrfrac(x);
+
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) {
+        av = avma;
+        return gerepileupto(av, gneg(gsqr(gel(x,2))));
+      }
+      z = cgetg(3,t_COMPLEX); av = avma;
+      p1 = gadd(gel(x,1),gel(x,2));
+      p2 = gsub(gel(x,1), gel(x,2));
+      p3 = gmul(gel(x,1),gel(x,2));
+      tetpil = avma;
+      gel(z,1) = gmul(p1,p2);
+      gel(z,2) = gshift(p3,1); gerepilecoeffssp(av,tetpil,z+1,2); return z;
+
+    case t_PADIC:
+      z = cgetg(5,t_PADIC);
+      i = (equaliu(gel(x,2), 2) && signe(gel(x,4)))? 1: 0;
+      if (i && precp(x) == 1) i = 2; /* (1 + O(2))^2 = 1 + O(2^3) */
+      z[1] = evalprecp(precp(x)+i) | evalvalp(valp(x) << 1);
+      gel(z,2) = icopy(gel(x,2));
+      gel(z,3) = shifti(gel(x,3), i); av = avma;
+      gel(z,4) = gerepileuptoint(av, remii(sqri(gel(x,4)), gel(z,3)));
+      return z;
+
+    case t_QUAD: z = cgetg(4,t_QUAD);
+      p1 = gel(x,1);
+      gel(z,1) = ZX_copy(p1); av = avma;
+      p2 = gsqr(gel(x,2));
+      p3 = gsqr(gel(x,3));
+      p4 = gmul(gneg_i(gel(p1,2)),p3);
+
+      if (gequal0(gel(p1,3)))
+      {
+        tetpil = avma;
+        gel(z,2) = gerepile(av,tetpil,gadd(p4,p2));
+        av = avma;
+        p2 = gmul(gel(x,2),gel(x,3)); tetpil = avma;
+        gel(z,3) = gerepile(av,tetpil,gmul2n(p2,1)); return z;
+      }
+
+      p1 = gmul2n(gmul(gel(x,2),gel(x,3)), 1);
+      tetpil = avma;
+      gel(z,2) = gadd(p2,p4);
+      gel(z,3) = gadd(p1,p3);
+      gerepilecoeffssp(av,tetpil,z+2,2); return z;
+
+    case t_POLMOD:
+      return sqr_polmod(gel(x,1), gel(x,2));
+
+    case t_FFELT: return FF_sqr(x);
+
+    case t_POL:
+    {
+      GEN a = x, p = NULL, pol = NULL;
+      av = avma;
+      if (RgX_is_ZX(x)) return ZX_sqr(x);
+      if (ff_poltype(&x,&p,&pol))
+      {
+        z = ZX_sqr(x);
+        if (p) z = FpX_to_mod(z,p);
+        if (pol) z = Kronecker_to_mod(z,pol);
+        z = gerepileupto(av, z);
+      }
+      else { avma = av; z = RgX_sqr(a); }
+      return z;
+    }
+
+    case t_SER:
+      lx = lg(x);
+      if (lx < 40)
+        return normalize( sqr_ser_part(x, 0, lx-3) );
+      else
+      {
+        pari_sp av = avma;
+        GEN z = cgetg(lx,t_SER), p = NULL;
+        z[1] = evalvalp(2*valp(x)) | evalvarn(varn(x)) | evalsigne(1);
+        x = ser2pol_i(x,lx);
+        if (RgX_is_FpX(x,&p))
+        {
+          if (!p) x = ZX_sqr(x);
+          else
+          {
+            x = RgX_to_FpX(x, p);
+            x = FpX_to_mod(ZX_sqr(x), p);
+          }
+        }
+        else
+          x = RgX_sqrlow(x, lx-2);
+        z = fill_ser(z, x);
+        return gerepilecopy(av, z);
+      }
+
+    case t_RFRAC: z = cgetg(3,t_RFRAC);
+      gel(z,1) = gsqr(gel(x,1));
+      gel(z,2) = gsqr(gel(x,2)); return z;
+
+    case t_MAT: return RgM_sqr(x);
+    case t_QFR: return qfrsqr(x);
+    case t_QFI: return qfisqr(x);
+    case t_VECSMALL:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++)
+      {
+        long xi = x[i];
+        if (xi < 1 || xi >= lx) pari_err_TYPE2("*",x,x);
+        z[i] = x[xi];
+      }
+      return z;
+  }
+  pari_err_TYPE2("*",x,x);
+  return NULL; /* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           DIVISION                             **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+div_rfrac_scal(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN d = rfrac_denom_mul_scal(gel(x,2), y);
+  return gerepileupto(av, gred_rfrac_simple(gel(x,1), d));
+}
+static GEN
+div_scal_rfrac(GEN x, GEN y)
+{
+  GEN y1 = gel(y,1), y2 = gel(y,2);
+  pari_sp av = avma;
+  if (typ(y1) == t_POL && varn(y2) == varn(y1) && degpol(y1) > 0)
+    return gerepileupto(av, gred_rfrac_simple(gmul(x, y2), y1));
+  return RgX_Rg_mul(y2, gdiv(x,y1));
+}
+static GEN
+div_rfrac(GEN x, GEN y)
+{ return mul_rfrac(gel(x,1),gel(x,2), gel(y,2),gel(y,1)); }
+
+static GEN
+div_ser_scal(GEN x, GEN y) {
+  long i, lx;
+  GEN z = cgetg_copy(x, &lx); z[1] = x[1];
+  for (i=2; i<lx; i++) gel(z,i) = gdiv(gel(x,i),y);
+  return normalize(z);
+}
+GEN
+ser_normalize(GEN x)
+{
+  long i, lx = lg(x);
+  GEN c, z;
+  if (lx == 2) return x;
+  c = gel(x,2); if (gequal1(c)) return x;
+  z = cgetg(lx, t_SER); z[1] = x[1]; gel(z,2) = gen_1;
+  for (i=3; i<lx; i++) gel(z,i) = gdiv(gel(x,i),c);
+  return z;
+}
+
+static GEN
+div_T_scal(GEN x, GEN y, long tx) {
+  switch(tx)
+  {
+    case t_POL: return RgX_Rg_div(x, y);
+    case t_SER: return div_ser_scal(x, y);
+    case t_RFRAC: return div_rfrac_scal(x,y);
+  }
+  pari_err_TYPE2("/",x,y);
+  return NULL; /* not reached */
+}
+
+static GEN
+div_scal_pol(GEN x, GEN y) {
+  long ly = lg(y);
+  pari_sp av;
+  if (ly == 3) return scalarpol(gdiv(x,gel(y,2)), varn(y));
+  if (isrationalzero(x)) return zeropol(varn(y));
+  av = avma;
+  return gerepileupto(av, gred_rfrac_simple(x,y));
+}
+static GEN
+div_scal_ser(GEN x, GEN y) { /* TODO: improve */
+  GEN z;
+  long ly, i;
+  if (gequal0(x)) { pari_sp av=avma; return gerepileupto(av, gmul(x, ginv(y))); }
+  ly = lg(y); z = (GEN)pari_malloc(ly*sizeof(long));
+  z[0] = evaltyp(t_SER) | evallg(ly);
+  z[1] = evalsigne(1) | _evalvalp(0) | evalvarn(varn(y));
+  gel(z,2) = x; for (i=3; i<ly; i++) gel(z,i) = gen_0;
+  y = gdiv(z,y); pari_free(z); return y;
+}
+static GEN
+div_scal_T(GEN x, GEN y, long ty) {
+  switch(ty)
+  {
+    case t_POL: return div_scal_pol(x, y);
+    case t_SER: return div_scal_ser(x, y);
+    case t_RFRAC: return div_scal_rfrac(x, y);
+  }
+  pari_err_TYPE2("/",x,y);
+  return NULL; /* not reached */
+}
+
+/* assume tx = ty = t_SER, same variable vx */
+static GEN
+div_ser(GEN x, GEN y, long vx)
+{
+  long i, j, l = valp(x) - valp(y), lx = lg(x), ly = lg(y);
+  GEN y_lead, p1, p2, z;
+
+  if (!signe(y)) pari_err_INV("div_ser", y);
+  if (lx == 2) return zeroser(vx, l);
+  y_lead = gel(y,2);
+  if (gequal0(y_lead)) /* normalize denominator if leading term is 0 */
+  {
+    pari_warn(warner,"normalizing a series with 0 leading term");
+    for (l--, ly--,y++; ly > 2; l--, ly--, y++)
+    {
+      y_lead = gel(y,2);
+      if (!gequal0(y_lead)) break;
+    }
+    if (ly <= 2) pari_err_INV("div_ser", y);
+  }
+  if (ly < lx) lx = ly;
+  p2 = cgetg(lx, t_VECSMALL); /* left on stack for efficiency */
+  for (i=3; i<lx; i++)
+  {
+    p1 = gel(y,i);
+    if (isrationalzero(p1)) p1 = NULL;
+    gel(p2,i) = p1;
+  }
+  z = cgetg(lx,t_SER);
+  z[1] = evalvalp(l) | evalvarn(vx) | evalsigne(1);
+  gel(z,2) = gdiv(gel(x,2), y_lead);
+  for (i=3; i<lx; i++)
+  {
+    pari_sp av = avma;
+    p1 = gel(x,i);
+    for (j=2, l=i; j<i; j++, l--)
+      if (p2[l]) p1 = gsub(p1, gmul(gel(z,j), gel(p2,l)));
+    gel(z,i) = gerepileupto(av, gdiv(p1, y_lead));
+  }
+  return normalize(z);
+}
+/* x,y compatible PADIC */
+static GEN
+divpp(GEN x, GEN y) {
+  pari_sp av;
+  long a, b;
+  GEN z, M;
+
+  if (!signe(gel(y,4))) pari_err_INV("divpp",y);
+  if (!signe(gel(x,4))) return zeropadic(gel(x,2), valp(x)-valp(y));
+  a = precp(x);
+  b = precp(y); if (a > b) { M = gel(y,3); } else { M = gel(x,3); b = a; }
+  z = cgetg(5, t_PADIC);
+  z[1] = _evalprecp(b) | evalvalp(valp(x) - valp(y));
+  gel(z,2) = icopy(gel(x,2));
+  gel(z,3) = icopy(M); av = avma;
+  gel(z,4) = gerepileuptoint(av, remii(mulii(gel(x,4), Fp_inv(gel(y,4), M)), M) );
+  return z;
+}
+static GEN
+div_polmod_same(GEN T, GEN x, GEN y)
+{
+  long v = varn(T);
+  GEN a, z = cgetg(3, t_POLMOD);
+  gel(z,1) = RgX_copy(T);
+  if (typ(y) != t_POL || varn(y) != v || lg(y) <= 3)
+    a = gdiv(x, y);
+  else if (typ(x) != t_POL || varn(x) != v || lg(x) <= 3)
+  {
+    pari_sp av = avma;
+    a = gerepileupto(av, gmul(x, RgXQ_inv(y, T)));
+  }
+  else if (degpol(T) == 2 && isint1(gel(T,4))) /* quadratic fields */
+  {
+    pari_sp av = avma;
+    a = quad_polmod_mul(T, x, quad_polmod_conj(y, T));
+    a = RgX_Rg_div(a, quad_polmod_norm(y, T));
+    a = gerepileupto(av, a);
+  }
+  else
+  {
+    pari_sp av = avma;
+    GEN p = NULL;
+    if (RgX_is_FpX(T, &p) && RgX_is_FpX(x, &p) && RgX_is_FpX(y, &p) && p)
+    {
+      T = RgX_to_FpX(T, p); x = RgX_to_FpX(x, p); y = RgX_to_FpX(y, p);
+      if (lgefint(p) == 3)
+      {
+        ulong pp = p[2];
+        x = ZX_to_Flx(x, pp);
+        y = ZX_to_Flx(y, pp);
+        T = ZX_to_Flx(T, pp);
+        a = Flxq_mul(x, Flxq_inv(y,T,pp),T,pp);
+        a = Flx_to_ZX(a);
+      }
+      else
+        a = FpXQ_div(x, y, T, p);
+      a = FpX_to_mod(a, p);
+    } else
+      a = RgXQ_mul(x, ginvmod(y, gel(z,1)), gel(z,1));
+    a = gerepileupto(av, a);
+  }
+  gel(z,2) = a; return z;
+}
+GEN
+gdiv(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y), lx, ly, vx, vy, i;
+  pari_sp av, tetpil;
+  GEN z, p1, p2;
+
+  if (tx == ty) switch(tx)
+  {
+    case t_INT:
+      if (is_pm1(y)) return (signe(y) < 0)? negi(x): icopy(x);
+      if (is_pm1(x)) {
+        long s = signe(y);
+        if (!s) pari_err_INV("gdiv",y);
+        if (signe(x) < 0) s = -s;
+        z = cgetg(3, t_FRAC);
+        gel(z,1) = s<0? gen_m1: gen_1;
+        gel(z,2) = absi(y); return z;
+      }
+      return gred_frac2(x,y);
+
+    case t_REAL: return divrr(x,y);
+    case t_INTMOD: { GEN X = gel(x,1), Y = gel(y,1);
+      z = cgetg(3,t_INTMOD);
+      if (X==Y || equalii(X,Y))
+        return div_intmod_same(z, X, gel(x,2), gel(y,2));
+      gel(z,1) = gcdii(X,Y); av = avma;
+      p1 = mulii(gel(x,2), Fp_inv(gel(y,2), gel(z,1)));
+      gel(z,2) = gerepileuptoint(av, remii(p1, gel(z,1))); return z;
+    }
+    case t_FRAC: {
+      GEN x1 = gel(x,1), x2 = gel(x,2);
+      GEN y1 = gel(y,1), y2 = gel(y,2);
+      z = cgetg(3, t_FRAC);
+      p1 = gcdii(x1, y1);
+      if (!is_pm1(p1)) { x1 = diviiexact(x1,p1); y1 = diviiexact(y1,p1); }
+      p1 = gcdii(x2, y2);
+      if (!is_pm1(p1)) { x2 = diviiexact(x2,p1); y2 = diviiexact(y2,p1); }
+      tetpil = avma;
+      gel(z,2) = mulii(x2,y1);
+      gel(z,1) = mulii(x1,y2);
+      normalize_frac(z);
+      fix_frac_if_int_GC(z,tetpil);
+      return z;
+    }
+    case t_COMPLEX:
+      if (isintzero(gel(y,1)))
+      {
+        y = gel(y,2);
+        if (isintzero(gel(x,1))) return gdiv(gel(x,2), y);
+        z = cgetg(3,t_COMPLEX);
+        gel(z,1) = gdiv(gel(x,2), y);
+        av = avma;
+        gel(z,2) = gerepileupto(av, gneg(gdiv(gel(x,1), y)));
+        return z;
+      }
+      av = avma; p1 = cxnorm(y); p2 = mulcc(x, gconj(y)); tetpil = avma;
+      return gerepile(av, tetpil, gdiv(p2,p1));
+
+    case t_PADIC:
+      if (!equalii(gel(x,2),gel(y,2))) pari_err_OP("/",x,y);
+      return divpp(x, y);
+
+    case t_QUAD:
+      if (!ZX_equal(gel(x,1),gel(y,1))) pari_err_OP("/",x,y);
+      av = avma; p1 = quadnorm(y); p2 = mulqq(x, gconj(y)); tetpil = avma;
+      return gerepile(av, tetpil, gdiv(p2,p1));
+
+    case t_FFELT: return FF_div(x,y);
+
+    case t_POLMOD:
+      if (RgX_equal_var(gel(x,1), gel(y,1)))
+        z = div_polmod_same(gel(x,1), gel(x,2), gel(y,2));
+      else {
+        av = avma;
+        z = gerepileupto(av, gmul(x, ginv(y)));
+      }
+      return z;
+
+    case t_POL:
+      vx = varn(x);
+      vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return RgX_Rg_div(x, y);
+                            else return div_scal_pol(x, y);
+      }
+      if (!signe(y)) pari_err_INV("gdiv",y);
+      if (lg(y) == 3) return RgX_Rg_div(x,gel(y,2));
+      return gred_rfrac2(x,y);
+
+    case t_SER:
+      vx = varn(x);
+      vy = varn(y);
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return div_ser_scal(x, y);
+                            else return div_scal_ser(x, y);
+      }
+      return div_ser(x, y, vx);
+    case t_RFRAC:
+      vx = varn(gel(x,2));
+      vy = varn(gel(y,2));
+      if (vx != vy) {
+        if (varncmp(vx, vy) < 0) return div_rfrac_scal(x, y);
+                            else return div_scal_rfrac(x, y);
+      }
+      return div_rfrac(x,y);
+
+    case t_QFI: av = avma; return gerepileupto(av, qficomp(x, ginv(y)));
+    case t_QFR: av = avma; return gerepileupto(av, qfrcomp(x, ginv(y)));
+
+    case t_MAT:
+      av = avma; p1 = RgM_inv(y);
+      if (!p1) pari_err_INV("gdiv",y);
+      return gerepileupto(av, RgM_mul(x, p1));
+
+    default: pari_err_TYPE2("/",x,y);
+  }
+
+  if (tx==t_INT && is_const_t(ty)) /* optimized for speed */
+  {
+    long s = signe(x);
+    if (!s) {
+      if (gequal0(y)) pari_err_INV("gdiv",y);
+      switch (ty)
+      {
+      default: return gen_0;
+      case t_INTMOD:
+        z = cgetg(3,t_INTMOD);
+        gel(z,1) = icopy(gel(y,1));
+        gel(z,2) = gen_0; return z;
+      case t_FFELT: return FF_zero(y);
+      }
+    }
+    if (is_pm1(x)) {
+      if (s > 0) return ginv(y);
+      av = avma; return gerepileupto(av, ginv(gneg(y)));
+    }
+    switch(ty)
+    {
+      case t_REAL: return divir(x,y);
+      case t_INTMOD:
+        z = cgetg(3, t_INTMOD);
+        return div_intmod_same(z, gel(y,1), modii(x, gel(y,1)), gel(y,2));
+      case t_FRAC:
+        z = cgetg(3,t_FRAC); p1 = gcdii(x,gel(y,1));
+        if (is_pm1(p1))
+        {
+          avma = (pari_sp)z;
+          gel(z,2) = icopy(gel(y,1));
+          gel(z,1) = mulii(gel(y,2), x);
+          normalize_frac(z);
+          fix_frac_if_int(z);
+        }
+        else
+        {
+          x = diviiexact(x,p1); tetpil = avma;
+          gel(z,2) = diviiexact(gel(y,1), p1);
+          gel(z,1) = mulii(gel(y,2), x);
+          normalize_frac(z);
+          fix_frac_if_int_GC(z,tetpil);
+        }
+        return z;
+
+      case t_FFELT: return Z_FF_div(x,y);
+      case t_COMPLEX: return divRc(x,y);
+      case t_PADIC: return divTp(x, y);
+      case t_QUAD:
+        av = avma; p1 = quadnorm(y); p2 = mulRq(x, gconj(y)); tetpil = avma;
+        return gerepile(av, tetpil, gdiv(p2,p1));
+    }
+  }
+  if (gequal0(y) && ty != t_MAT) pari_err_INV("gdiv",y);
+
+  if (is_const_t(tx) && is_const_t(ty)) switch(tx)
+  {
+    case t_REAL:
+      switch(ty)
+      {
+        case t_INT: return divri(x,y);
+        case t_FRAC:
+          av = avma; z = divri(mulri(x,gel(y,2)), gel(y,1));
+          return gerepileuptoleaf(av, z);
+        case t_COMPLEX: return divRc(x, y);
+        case t_QUAD: return divfq(x, y, lg(x));
+        default: pari_err_TYPE2("/",x,y);
+      }
+
+    case t_INTMOD:
+      switch(ty)
+      {
+        case t_INT:
+          z = cgetg(3, t_INTMOD);
+          return div_intmod_same(z, gel(x,1), gel(x,2), modii(y, gel(x,1)));
+        case t_FRAC: { GEN X = gel(x,1);
+          z = cgetg(3,t_INTMOD); p1 = remii(mulii(gel(y,2), gel(x,2)), X);
+          return div_intmod_same(z, X, p1, modii(gel(y,1), X));
+        }
+        case t_FFELT:
+          if (!equalii(gel(x,1),FF_p_i(y)))
+            pari_err_OP("/",x,y);
+          return Z_FF_div(gel(x,2),y);
+
+        case t_COMPLEX:
+          av = avma;
+          return gerepileupto(av, mulRc_direct(gdiv(x,cxnorm(y)), gconj(y)));
+
+        case t_QUAD:
+          av = avma; p1 = quadnorm(y); p2 = gmul(x,gconj(y)); tetpil = avma;
+          return gerepile(av,tetpil, gdiv(p2,p1));
+
+        case t_PADIC: { GEN X = gel(x,1);
+          z = cgetg(3, t_INTMOD);
+          return div_intmod_same(z, X, gel(x,2), padic_to_Fp(y, X));
+        }
+        case t_REAL: pari_err_TYPE2("/",x,y);
+      }
+
+    case t_FRAC:
+      switch(ty)
+      {
+        case t_INT: z = cgetg(3, t_FRAC);
+        p1 = gcdii(y,gel(x,1));
+        if (is_pm1(p1))
+        {
+          avma = (pari_sp)z; tetpil = 0;
+          gel(z,1) = icopy(gel(x,1));
+        }
+        else
+        {
+          y = diviiexact(y,p1); tetpil = avma;
+          gel(z,1) = diviiexact(gel(x,1), p1);
+        }
+        gel(z,2) = mulii(gel(x,2),y);
+        normalize_frac(z);
+        if (tetpil) fix_frac_if_int_GC(z,tetpil);
+        return z;
+
+        case t_REAL:
+          av=avma; p1=mulri(y,gel(x,2)); tetpil=avma;
+          return gerepile(av, tetpil, divir(gel(x,1), p1));
+
+        case t_INTMOD: { GEN Y = gel(y,1);
+          z = cgetg(3,t_INTMOD); p1 = remii(mulii(gel(y,2),gel(x,2)), Y);
+          return div_intmod_same(z, Y, modii(gel(x,1), Y), p1);
+        }
+
+        case t_FFELT: av=avma;
+          return gerepileupto(av,Z_FF_div(gel(x,1),FF_Z_mul(y,gel(x,2))));
+
+        case t_COMPLEX: return divRc(x, y);
+
+        case t_PADIC:
+          if (!signe(gel(x,1))) return gen_0;
+          return divTp(x, y);
+
+        case t_QUAD:
+          av=avma; p1=quadnorm(y); p2=gmul(x,gconj(y)); tetpil=avma;
+          return gerepile(av,tetpil,gdiv(p2,p1));
+      }
+
+    case t_FFELT:
+      switch (ty)
+      {
+        case t_INT: return FF_Z_Z_muldiv(x,gen_1,y);
+        case t_FRAC: return FF_Z_Z_muldiv(x,gel(y,2),gel(y,1));
+        case t_INTMOD:
+          if (!equalii(gel(y,1),FF_p_i(x)))
+            pari_err_OP("/",x,y);
+          return FF_Z_Z_muldiv(x,gen_1,gel(y,2));
+        default:
+        pari_err_TYPE2("/",x,y);
+      }
+      break;
+
+    case t_COMPLEX:
+      switch(ty)
+      {
+        case t_INT: case t_REAL: case t_FRAC: return divcR(x,y);
+        case t_INTMOD: return mulRc_direct(ginv(y), x);
+        case t_PADIC:
+          return Zp_nosquare_m1(gel(y,2))? divcR(x,y): divTp(x, y);
+        case t_QUAD:
+          lx = precision(x); if (!lx) pari_err_OP("/",x,y);
+          return divfq(x, y, lx);
+      }
+
+    case t_PADIC:
+      switch(ty)
+      {
+        case t_INT: case t_FRAC: { GEN p = gel(x,2);
+          return signe(gel(x,4))? divpT(x, y)
+                            : zeropadic(p, valp(x) - Q_pval(y,p));
+        }
+        case t_INTMOD: { GEN Y = gel(y,1);
+          z = cgetg(3, t_INTMOD);
+          return div_intmod_same(z, Y, padic_to_Fp(x, Y), gel(y,2));
+        }
+        case t_COMPLEX: case t_QUAD:
+          av=avma; p1=gmul(x,gconj(y)); p2=gnorm(y); tetpil=avma;
+          return gerepile(av,tetpil,gdiv(p1,p2));
+
+        case t_REAL: pari_err_TYPE2("/",x,y);
+      }
+
+    case t_QUAD:
+      switch (ty)
+      {
+        case t_INT: case t_INTMOD: case t_FRAC:
+          z = cgetg(4,t_QUAD);
+          gel(z,1) = ZX_copy(gel(x,1));
+          gel(z,2) = gdiv(gel(x,2), y);
+          gel(z,3) = gdiv(gel(x,3), y); return z;
+        case t_REAL: return divqf(x, y, lg(y));
+        case t_PADIC: return divTp(x, y);
+        case t_COMPLEX:
+          ly = precision(y); if (!ly) pari_err_OP("/",x,y);
+          return divqf(x, y, ly);
+      }
+  }
+  switch(ty) {
+    case t_REAL: case t_INTMOD: case t_PADIC: case t_POLMOD:
+      return gmul(x, ginv(y)); /* missing gerepile, for speed */
+    case t_MAT:
+      av = avma; p1 = RgM_inv(y);
+      if (!p1) pari_err_INV("gdiv",y);
+      return gerepileupto(av, gmul(x, p1));
+    case t_VEC: case t_COL:
+    case t_LIST: case t_STR: case t_VECSMALL: case t_CLOSURE:
+      pari_err_TYPE2("/",x,y);
+  }
+  switch(tx) {
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(z,i) = gdiv(gel(x,i),y);
+      return z;
+    case t_LIST: case t_STR: case t_VECSMALL: case t_CLOSURE:
+      pari_err_TYPE2("/",x,y);
+  }
+
+  vy = gvar(y);
+  if (tx == t_POLMOD) { GEN X = gel(x,1);
+    vx = varn(X);
+    if (vx != vy) {
+      if (varncmp(vx, vy) > 0) return div_scal_T(x, y, ty);
+      z = cgetg(3,t_POLMOD);
+      gel(z,1) = RgX_copy(X);
+      gel(z,2) = gdiv(gel(x,2), y); return z;
+    }
+    /* y is POL, SER or RFRAC */
+    av = avma;
+    switch(ty)
+    {
+      case t_RFRAC: y = gmod(ginv(y), X); break;
+      default: y = ginvmod(gmod(y,X), X);
+    }
+    return gerepileupto(av, mul_polmod_same(X, gel(x,2), y));
+  }
+  /* x and y are not both is_scalar_t. If one of them is scalar, it's not a
+   * POLMOD (done already), hence its variable is NO_VARIABLE. If the other has
+   * variable NO_VARIABLE, then the operation is incorrect */
+  vx = gvar(x);
+  if (vx != vy) { /* includes cases where one is scalar */
+    if (varncmp(vx, vy) < 0) return div_T_scal(x, y, tx);
+                        else return div_scal_T(x, y, ty);
+  }
+  switch(tx)
+  {
+    case t_POL:
+      switch(ty)
+      {
+        case t_SER:
+          if (lg(y) == 2)
+            return zeroser(vx, RgX_val(x) - valp(y));
+          p1 = RgX_to_ser(x,lg(y));
+          p2 = div_ser(p1, y, vx);
+          settyp(p1, t_VECSMALL); /* p1 left on stack */
+          return p2;
+
+        case t_RFRAC:
+        {
+          GEN y1 = gel(y,1), y2 = gel(y,2);
+          if (typ(y1) == t_POL && varn(y1) == vx)
+            return mul_rfrac_scal(y2, y1, x);
+          av = avma;
+          return gerepileupto(av, RgX_Rg_div(RgX_mul(y2, x), y1));
+        }
+      }
+      break;
+
+    case t_SER:
+      switch(ty)
+      {
+        case t_POL:
+          if (lg(x) == 2)
+            return zeroser(vx, valp(x) - RgX_val(y));
+          p1 = RgX_to_ser_inexact(y,lg(x));
+          p2 = div_ser(x, p1, vx);
+          settyp(p1, t_VECSMALL); /* p1 left on stack */
+          return p2;
+        case t_RFRAC:
+          av = avma;
+          return gerepileupto(av, gdiv(gmul(x,gel(y,2)), gel(y,1)));
+      }
+      break;
+
+    case t_RFRAC:
+      switch(ty)
+      {
+        case t_POL: return div_rfrac_pol(gel(x,1),gel(x,2), y);
+        case t_SER:
+          av = avma; z = RgX_to_ser_inexact(gel(x,2), lg(y));
+          return gerepileupto(av, gdiv(gel(x,1), gmul(z,y)));
+      }
+      break;
+  }
+  pari_err_TYPE2("/",x,y);
+  return NULL; /* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                     SIMPLE MULTIPLICATION                      **/
+/**                                                                **/
+/********************************************************************/
+GEN
+gmulsg(long s, GEN y)
+{
+  long ly, i;
+  pari_sp av;
+  GEN z;
+
+  switch(typ(y))
+  {
+    case t_INT:  return mulsi(s,y);
+    case t_REAL: return mulsr(s,y);
+    case t_INTMOD: { GEN p = gel(y,1);
+      z = cgetg(3,t_INTMOD);
+      gel(z,2) = gerepileuptoint((pari_sp)z, modii(mulsi(s,gel(y,2)), p));
+      gel(z,1) = icopy(p); return z;
+    }
+    case t_FFELT: return FF_Z_mul(y,stoi(s));
+    case t_FRAC:
+      if (!s) return gen_0;
+      z = cgetg(3,t_FRAC);
+      i = cgcd(s, smodis(gel(y,2), s));
+      if (i == 1)
+      {
+        gel(z,2) = icopy(gel(y,2));
+        gel(z,1) = mulis(gel(y,1), s);
+      }
+      else
+      {
+        gel(z,2) = divis(gel(y,2), i);
+        gel(z,1) = mulis(gel(y,1), s/i);
+        fix_frac_if_int(z);
+      }
+      return z;
+
+    case t_COMPLEX: z = cgetg(3, t_COMPLEX);
+      gel(z,1) = gmulsg(s,gel(y,1));
+      gel(z,2) = gmulsg(s,gel(y,2)); return z;
+
+    case t_PADIC:
+      if (!s) return gen_0;
+      av = avma; return gerepileupto(av, mulpp(cvtop2(stoi(s),y), y));
+
+    case t_QUAD: z = cgetg(4, t_QUAD);
+      gel(z,1) = ZX_copy(gel(y,1));
+      gel(z,2) = gmulsg(s,gel(y,2));
+      gel(z,3) = gmulsg(s,gel(y,3)); return z;
+
+    case t_POLMOD: z = cgetg(3, t_POLMOD);
+      gel(z,1) = RgX_copy(gel(y,1));
+      gel(z,2) = gmulsg(s,gel(y,2)); return z;
+
+    case t_POL:
+      if (!signe(y)) return RgX_copy(y);
+      if (!s) return scalarpol(RgX_get_0(y), varn(y));
+      z = cgetg_copy(y, &ly); z[1]=y[1];
+      for (i=2; i<ly; i++) gel(z,i) = gmulsg(s,gel(y,i));
+      return normalizepol_lg(z, ly);
+
+    case t_SER:
+      if (!s) return zeropol(varn(y));
+      z = cgetg_copy(y, &ly); z[1]=y[1];
+      for (i=2; i<ly; i++) gel(z,i) = gmulsg(s,gel(y,i));
+      return normalize(z);
+
+    case t_RFRAC:
+      if (!s) return zeropol(varn(gel(y,2)));
+      if (s == 1) return gcopy(y);
+      if (s == -1) return gneg(y);
+      return mul_rfrac_scal(gel(y,1), gel(y,2), stoi(s));
+
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(y, &ly);
+      for (i=1; i<ly; i++) gel(z,i) = gmulsg(s,gel(y,i));
+      return z;
+  }
+  pari_err_TYPE("gmulsg",y);
+  return NULL; /* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       SIMPLE DIVISION                          **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+gdivgs(GEN x, long s)
+{
+  long lx, i;
+  pari_sp av;
+  GEN z, y, p1;
+
+  if (!s) pari_err_INV("gdivgs",gen_0);
+  switch(typ(x))
+  {
+    case t_INT:
+      av = avma; z = divis_rem(x,s,&i);
+      if (!i) return z;
+
+      i = cgcd(s, i);
+      avma=av; z = cgetg(3,t_FRAC);
+      if (i == 1) y = icopy(x); else { s /= i; y = diviuexact(x, i); }
+      gel(z,1) = y;
+      gel(z,2) = stoi(s); normalize_frac(z); return z;
+
+    case t_REAL:
+      return divrs(x,s);
+
+    case t_INTMOD:
+      z = cgetg(3, t_INTMOD);
+      return div_intmod_same(z, gel(x,1), gel(x,2), modsi(s, gel(x,1)));
+
+    case t_FFELT: return FF_Z_Z_muldiv(x,gen_1,stoi(s));
+
+    case t_FRAC: z = cgetg(3, t_FRAC);
+      i = cgcd(s, smodis(gel(x,1), s));
+      if (i == 1)
+      {
+        gel(z,2) = mulsi(s, gel(x,2));
+        gel(z,1) = icopy(gel(x,1));
+      }
+      else
+      {
+        gel(z,2) = mulsi(s/i, gel(x,2));
+        gel(z,1) = divis(gel(x,1), i);
+      }
+      normalize_frac(z);
+      fix_frac_if_int(z); return z;
+
+    case t_COMPLEX: z = cgetg(3, t_COMPLEX);
+      gel(z,1) = gdivgs(gel(x,1),s);
+      gel(z,2) = gdivgs(gel(x,2),s); return z;
+
+    case t_PADIC: /* divpT */
+    {
+      GEN p = gel(x,2);
+      if (!signe(gel(x,4))) return zeropadic(p, valp(x) - u_pval(s,p));
+      av = avma;
+      return gerepileupto(av, divpp(x, cvtop2(stoi(s),x)));
+    }
+
+    case t_QUAD: z = cgetg(4, t_QUAD);
+      gel(z,1) = ZX_copy(gel(x,1));
+      gel(z,2) = gdivgs(gel(x,2),s);
+      gel(z,3) = gdivgs(gel(x,3),s); return z;
+
+    case t_POLMOD: z = cgetg(3, t_POLMOD);
+      gel(z,1) = RgX_copy(gel(x,1));
+      gel(z,2) = gdivgs(gel(x,2),s); return z;
+
+    case t_RFRAC:
+      av = avma;
+      p1 = ggcd(stoi(s),gel(x,1));
+      if (typ(p1) == t_INT)
+      {
+        avma = av;
+        z = cgetg(3, t_RFRAC);
+        i = p1[2];
+        if (i == 1)
+        {
+          gel(z,1) = gcopy(gel(x,1));
+          gel(z,2) = gmulsg(s,gel(x,2));
+        }
+        else
+        {
+          gel(z,1) = gdivgs(gel(x,1), i);
+          gel(z,2) = gmulgs(gel(x,2), s/i);
+        }
+      }
+      else /* t_FRAC */
+      {
+        z = cgetg(3, t_RFRAC);
+        gel(z,1) = gdiv(gel(x,1), p1);
+        gel(z,2) = RgX_Rg_mul(gel(x,2), gdivsg(s,p1));
+        z = gerepilecopy(av, z);
+      }
+      return z;
+
+    case t_POL: case t_SER:
+      z = cgetg_copy(x, &lx); z[1] = x[1];
+      for (i=2; i<lx; i++) gel(z,i) = gdivgs(gel(x,i),s);
+      return z;
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(z,i) = gdivgs(gel(x,i),s);
+      return z;
+
+  }
+  pari_err_TYPE2("/",x, stoi(s));
+  return NULL; /* not reached */
+}
+
+/* True shift (exact multiplication by 2^n) */
+GEN
+gmul2n(GEN x, long n)
+{
+  long lx, i, k, l;
+  GEN z, a, b;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      if (n>=0) return shifti(x,n);
+      if (!signe(x)) return gen_0;
+      l = vali(x); n = -n;
+      if (n<=l) return shifti(x,-n);
+      z = cgetg(3,t_FRAC);
+      gel(z,1) = shifti(x,-l);
+      gel(z,2) = int2n(n-l); return z;
+
+    case t_REAL:
+      return shiftr(x,n);
+
+    case t_INTMOD: b = gel(x,1); a = gel(x,2);
+      z = cgetg(3,t_INTMOD);
+      if (n <= 0) return div_intmod_same(z, b, a, modii(int2n(-n), b));
+      gel(z,2) = gerepileuptoint((pari_sp)z, modii(shifti(a,n), b));
+      gel(z,1) = icopy(b); return z;
+
+    case t_FFELT: return FF_mul2n(x,n);
+
+    case t_FRAC: a = gel(x,1); b = gel(x,2);
+      l = vali(a);
+      k = vali(b);
+      if (n+l >= k)
+      {
+        if (expi(b) == k) return shifti(a,n-k); /* b power of 2 */
+        l = n-k; k = -k;
+      }
+      else
+      {
+        k = -(l+n); l = -l;
+      }
+      z = cgetg(3,t_FRAC);
+      gel(z,1) = shifti(a,l);
+      gel(z,2) = shifti(b,k); return z;
+
+    case t_COMPLEX: z = cgetg(3,t_COMPLEX);
+      gel(z,1) = gmul2n(gel(x,1),n);
+      gel(z,2) = gmul2n(gel(x,2),n); return z;
+
+    case t_QUAD: z = cgetg(4,t_QUAD);
+      gel(z,1) = ZX_copy(gel(x,1));
+      gel(z,2) = gmul2n(gel(x,2),n);
+      gel(z,3) = gmul2n(gel(x,3),n); return z;
+
+    case t_POLMOD: z = cgetg(3,t_POLMOD);
+      gel(z,1) = RgX_copy(gel(x,1));
+      gel(z,2) = gmul2n(gel(x,2),n); return z;
+
+    case t_POL:
+      z = cgetg_copy(x, &lx); z[1] = x[1];
+      for (i=2; i<lx; i++) gel(z,i) = gmul2n(gel(x,i),n);
+      return normalizepol_lg(z, lx); /* needed if char = 2 */
+    case t_SER:
+      z = cgetg_copy(x, &lx); z[1] = x[1];
+      for (i=2; i<lx; i++) gel(z,i) = gmul2n(gel(x,i),n);
+      return normalize(z); /* needed if char = 2 */
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(z,i) = gmul2n(gel(x,i),n);
+      return z;
+
+    case t_RFRAC: /* int2n wrong if n < 0 */
+      return mul_rfrac_scal(gel(x,1),gel(x,2), gmul2n(gen_1,n));
+
+    case t_PADIC: /* int2n wrong if n < 0 */
+      return gmul(gmul2n(gen_1,n),x);
+  }
+  pari_err_TYPE("gmul2n",x);
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                              INVERSE                            */
+/*                                                                 */
+/*******************************************************************/
+GEN
+inv_ser(GEN b)
+{
+  pari_sp av = avma, av2, lim;
+  long j, lold, l = lg(b), e = valp(b), v = varn(b);
+  GEN y, x = cgetg(l, t_SER), a = leafcopy(b);
+  ulong mask = quadratic_prec_mask(l - 2);
+
+  if (!signe(b)) pari_err_INV("inv_ser",b);
+
+  for (j = 3; j < l; j++) gel(x,j) = gen_0;
+  gel(x,2) = ginv(gel(b,2));
+  a[1] = x[1] = _evalvalp(0) | evalvarn(v) | evalsigne(1);
+  av2 = avma; lim = stack_lim(av2, 2);
+  lold = 1;
+  while (mask > 1)
+  {
+    long lnew = lold << 1;
+    GEN z;
+
+    if (mask & 1) lnew--;
+    mask >>= 1;
+    setlg(a, lnew + 2);
+    setlg(x, lnew + 2);
+    /* TODO: gmul(a,x) should be a half product (the higher half is known) */
+    z = gmul(a,x); /* = 1 + O(t^lold) */
+    y = cgetg(lnew-lold + 2, t_SER);
+    y[1] = _evalvalp(lold) | evalvarn(v) | evalsigne(1);
+    for (j = 2; j < 2+lnew-lold; j++) gel(y,j) = gel(z,j+lold);
+    /* y = a*x - 1; */
+    y = gsub(x, gmul(x, y));
+    for (j = lold+2; j < lnew+2; j++) x[j] = y[j];
+    if (low_stack(lim, stack_lim(av2,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"inv_ser");
+      y = gerepilecopy(av2, x);
+      for (j = 2; j < lnew+2; j++) x[j] = y[j];
+    }
+    lold = lnew;
+  }
+  x[1] = evalvalp(valp(x)-e) | evalvarn(v) | evalsigne(1);
+  return gerepilecopy(av, x);
+}
+
+static GEN
+inv_polmod(GEN T, GEN x)
+{
+  GEN z = cgetg(3,t_POLMOD), a;
+  gel(z,1) = RgX_copy(T);
+  if (typ(x) != t_POL || varn(x) != varn(T) || lg(x) <= 3)
+    a = ginv(x);
+  else
+  {
+    pari_sp av = avma;
+    if (lg(T) == 5) /* quadratic fields */
+      a = RgX_Rg_div(quad_polmod_conj(x,T), quad_polmod_norm(x,T));
+    else
+    {
+      GEN p = NULL;
+      if (RgX_is_FpX(T, &p) && RgX_is_FpX(x, &p) && p)
+      {
+        T = RgX_to_FpX(T, p); x = RgX_to_FpX(x, p);
+        if (lgefint(p) == 3)
+        {
+          ulong pp = p[2];
+          a = Flxq_inv(ZX_to_Flx(x, pp), ZX_to_Flx(T, pp), pp);
+          a = Flx_to_ZX(a);
+        }
+        else
+          a = FpXQ_inv(x, T, p);
+        a = FpX_to_mod(a, p);
+        a = gerepileupto(av, a);
+      }
+      else {
+        avma = av;
+        a = RgXQ_inv(x, gel(z,1));
+      }
+    }
+  }
+  gel(z,2) = a; return z;
+}
+GEN
+ginv(GEN x)
+{
+  long s;
+  pari_sp av, tetpil;
+  GEN z, y, p1, p2;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      if (is_pm1(x)) return icopy(x);
+      s = signe(x); if (!s) pari_err_INV("ginv",gen_0);
+      z = cgetg(3,t_FRAC);
+      gel(z,1) = s<0? gen_m1: gen_1;
+      gel(z,2) = absi(x); return z;
+
+    case t_REAL: return invr(x);
+
+    case t_INTMOD: z=cgetg(3,t_INTMOD);
+      gel(z,1) = icopy(gel(x,1));
+      gel(z,2) = Fp_inv(gel(x,2),gel(x,1)); return z;
+
+    case t_FRAC: {
+      GEN a = gel(x,1), b = gel(x,2);
+      s = signe(a);
+      if (is_pm1(a)) return s > 0? icopy(b): negi(b);
+      z = cgetg(3,t_FRAC);
+      gel(z,1) = icopy(b);
+      gel(z,2) = icopy(a);
+      normalize_frac(z); return z;
+    }
+    case t_COMPLEX:
+      av=avma;
+      p1=cxnorm(x);
+      p2=mkcomplex(gel(x,1), gneg(gel(x,2)));
+      tetpil=avma;
+      return gerepile(av,tetpil,divcR(p2,p1));
+
+    case t_QUAD:
+      av=avma; p1=gnorm(x); p2=gconj(x); tetpil=avma;
+      return gerepile(av,tetpil,gdiv(p2,p1));
+
+    case t_PADIC: z = cgetg(5,t_PADIC);
+      if (!signe(gel(x,4))) pari_err_INV("ginv",x);
+      z[1] = _evalprecp(precp(x)) | evalvalp(-valp(x));
+      gel(z,2) = icopy(gel(x,2));
+      gel(z,3) = icopy(gel(x,3));
+      gel(z,4) = Fp_inv(gel(x,4),gel(z,3)); return z;
+
+    case t_POLMOD: return inv_polmod(gel(x,1), gel(x,2));
+    case t_FFELT: return FF_inv(x);
+    case t_POL: return gred_rfrac_simple(gen_1,x);
+    case t_SER: return gdiv(gen_1,x);
+
+    case t_RFRAC:
+    {
+      GEN n = gel(x,1), d = gel(x,2);
+      pari_sp av = avma, ltop;
+      if (gequal0(n)) pari_err_INV("ginv",x);
+
+      n = simplify_shallow(n);
+      if (typ(n) != t_POL || varn(n) != varn(d))
+      {
+        if (gequal1(n)) { avma = av; return RgX_copy(d); }
+        ltop = avma;
+        z = RgX_Rg_div(d,n);
+      } else {
+        ltop = avma;
+        z = cgetg(3,t_RFRAC);
+        gel(z,1) = RgX_copy(d);
+        gel(z,2) = RgX_copy(n);
+      }
+      stackdummy(av, ltop);
+      return z;
+    }
+
+    case t_QFR:
+      av = avma; z = cgetg(5, t_QFR);
+      gel(z,1) = gel(x,1);
+      gel(z,2) = negi( gel(x,2) );
+      gel(z,3) = gel(x,3);
+      gel(z,4) = negr( gel(x,4) );
+      return gerepileupto(av, redreal(z));
+
+    case t_QFI:
+      y = gcopy(x);
+      if (!equalii(gel(x,1),gel(x,2)) && !equalii(gel(x,1),gel(x,3)))
+        togglesign(gel(y,2));
+      return y;
+    case t_MAT:
+    {
+      GEN ff = NULL;
+      if (RgM_is_FFM(x,&ff))
+        y = FFM_inv(x, ff);
+      else
+        y = RgM_inv(x);
+      if (!y) pari_err_INV("ginv",x);
+      return y;
+    }
+    case t_VECSMALL:
+    {
+      long i, lx = lg(x)-1;
+      y = zero_zv(lx);
+      for (i=1; i<=lx; i++)
+      {
+        long xi = x[i];
+        if (xi<1 || xi>lx || y[xi])
+          pari_err_TYPE("ginv [not a permutation]", x);
+        y[xi] = i;
+      }
+      return y;
+    }
+  }
+  pari_err_TYPE("inverse",x);
+  return NULL; /* not reached */
+}
diff --git a/src/basemath/gen2.c b/src/basemath/gen2.c
new file mode 100644
index 0000000..75547fd
--- /dev/null
+++ b/src/basemath/gen2.c
@@ -0,0 +1,2721 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                      GENERIC OPERATIONS                        **/
+/**                        (second part)                           **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/*********************************************************************/
+/**                                                                 **/
+/**                MAP FUNCTIONS WITH GIVEN PROTOTYPES              **/
+/**                                                                 **/
+/*********************************************************************/
+GEN
+map_proto_G(GEN f(GEN), GEN x)
+{
+  if (is_matvec_t(typ(x)))
+  {
+    long lx, i;
+    GEN y = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(y,i) = map_proto_G(f, gel(x,i));
+    return y;
+  }
+  return f(x);
+}
+
+GEN
+map_proto_lG(long f(GEN), GEN x)
+{
+  if (is_matvec_t(typ(x)))
+  {
+    long lx, i;
+    GEN y = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(y,i) = map_proto_lG(f, gel(x,i));
+    return y;
+  }
+  return stoi(f(x));
+}
+
+GEN
+map_proto_lGL(long f(GEN,long), GEN x, long y)
+{
+  if (is_matvec_t(typ(x)))
+  {
+    long l, i;
+    GEN t = cgetg_copy(x, &l);
+    for (i=1; i<l; i++) gel(t,i) = map_proto_lGL(f,gel(x,i),y);
+    return t;
+  }
+  return stoi(f(x,y));
+}
+
+GEN
+gassoc_proto(GEN f(GEN,GEN), GEN x, GEN y)
+{
+  if (!y)
+  {
+    pari_sp av = avma;
+    switch(typ(x))
+    {
+      case t_LIST:
+        x = list_data(x); if (!x) return gen_1;
+      case t_VEC:
+      case t_COL: break;
+      default: pari_err_TYPE("association",x);
+    }
+    return gerepileupto(av, divide_conquer_prod(x,f));
+  }
+  return f(x,y);
+}
+/*******************************************************************/
+/*                                                                 */
+/*                    CREATION OF A P-ADIC GEN                     */
+/*                                                                 */
+/*******************************************************************/
+GEN
+cgetp(GEN x)
+{
+  GEN y = cgetg(5,t_PADIC);
+  y[1] = (x[1]&PRECPBITS) | _evalvalp(0);
+  gel(y,2) = icopy(gel(x,2));
+  gel(y,3) = icopy(gel(x,3));
+  gel(y,4) = cgeti(lgefint(gel(x,3))); return y;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            SIZES                                */
+/*                                                                 */
+/*******************************************************************/
+
+long
+glength(GEN x)
+{
+  long tx = typ(x);
+  switch(tx)
+  {
+    case t_INT:  return lgefint(x)-2;
+    case t_LIST: {
+      GEN L = list_data(x);
+      return L? lg(L)-1: 0;
+    }
+    case t_REAL: return signe(x)? lg(x)-2: 0;
+    case t_STR:  return strlen( GSTR(x) );
+    case t_VECSMALL: return lg(x)-1;
+  }
+  return lg(x) - lontyp[tx];
+}
+
+GEN
+matsize(GEN x)
+{
+  long L = lg(x) - 1;
+  switch(typ(x))
+  {
+    case t_VEC: return mkvec2s(1, L);
+    case t_COL: return mkvec2s(L, 1);
+    case t_MAT: return mkvec2s(L? nbrows(x): 0, L);
+  }
+  pari_err_TYPE("matsize",x);
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                  Conversion t_POL --> t_SER                     */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+greffe_aux(GEN x, long l, long lx, long v)
+{
+  GEN y = cgetg(l,t_SER);
+  long i;
+  if (l <= 2) pari_err_BUG("RgX_to_ser (l <= 2)");
+  y[1] = x[1]; setvalp(y, v);
+  x += v; lx -= v;
+  if (lx > l) {
+    for (i = 2; i < l; i++) gel(y,i) = gel(x,i);
+  } else {
+    for (i = 2; i <lx; i++) gel(y,i) = gel(x,i);
+    for (     ; i < l; i++) gel(y,i) = gen_0;
+  }
+  return normalize(y);
+}
+/* enlarge/truncate t_POL x to a t_SER with lg l */
+GEN
+RgX_to_ser(GEN x, long l)
+{
+  long i, lx = lg(x);
+  if (lx == 2) return zeroser(varn(x), l-2);
+  /* analogous to RgX_valrem + normalize */
+  i = 2; while (i<lx && isrationalzero(gel(x,i))) i++;
+  return greffe_aux(x, l, lx, i - 2);
+}
+GEN
+RgX_to_ser_inexact(GEN x, long l)
+{
+  long i, lx = lg(x);
+  int first = 1;
+  /* analogous to RgX_valrem + normalize */
+  if (lx == 2) return zeroser(varn(x), l-2);
+  i = 2;
+  while (i<lx && gequal0(gel(x,i))) {
+    if (first && !isexactzero(gel(x,i)))
+    {
+      pari_warn(warner,"normalizing a series with 0 leading term");
+      first = 0;
+    }
+    i++;
+  }
+  return greffe_aux(x, l, lx, i - 2);
+}
+GEN
+rfrac_to_ser(GEN x, long l)
+{
+  return gdiv(gel(x,1), RgX_to_ser(gel(x,2), l));
+}
+/*******************************************************************/
+/*                                                                 */
+/*                 CONVERSION GEN --> long                         */
+/*                                                                 */
+/*******************************************************************/
+
+long
+gtolong(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT:
+      return itos(x);
+    case t_REAL:
+      return (long)(rtodbl(x) + 0.5);
+    case t_FRAC: {
+      pari_sp av = avma;
+      long y = itos(ground(x));
+      avma = av; return y;
+    }
+    case t_COMPLEX:
+      if (gequal0(gel(x,2))) return gtolong(gel(x,1)); break;
+    case t_QUAD:
+      if (gequal0(gel(x,3))) return gtolong(gel(x,2)); break;
+  }
+  pari_err_TYPE("gtolong",x);
+  return 0; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         COMPARISONS                             */
+/*                                                                 */
+/*******************************************************************/
+int
+isexactzero(GEN g)
+{
+  long i, lx;
+  switch (typ(g))
+  {
+    case t_INT:
+      return !signe(g);
+    case t_INTMOD:
+      return !signe(gel(g,2));
+    case t_COMPLEX:
+      return isexactzero(gel(g,1)) && isexactzero(gel(g,2));
+    case t_FFELT:
+      return FF_equal0(g);
+    case t_QUAD:
+      return isexactzero(gel(g,2)) && isexactzero(gel(g,3));
+    case t_POLMOD:
+      return isexactzero(gel(g,2));
+    case t_POL:
+      lx = lg(g); /* cater for Mod(0,2)*x^0 */
+      return lx == 2 || (lx == 3 && isexactzero(gel(g,2)));
+    case t_RFRAC:
+      return isexactzero(gel(g,1)); /* may occur: Mod(0,2)/x */
+    case t_VEC: case t_COL: case t_MAT:
+      for (i=lg(g)-1; i; i--)
+        if (!isexactzero(gel(g,i))) return 0;
+      return 1;
+  }
+  return 0;
+}
+
+int
+isrationalzero(GEN g)
+{
+  long i;
+  switch (typ(g))
+  {
+    case t_INT:
+      return !signe(g);
+    case t_COMPLEX:
+      return isintzero(gel(g,1)) && isintzero(gel(g,2));
+    case t_QUAD:
+      return isintzero(gel(g,2)) && isintzero(gel(g,3));
+    case t_POLMOD:
+      return isrationalzero(gel(g,2));
+    case t_POL: return lg(g) == 2;
+    case t_VEC: case t_COL: case t_MAT:
+      for (i=lg(g)-1; i; i--)
+        if (!isrationalzero(gel(g,i))) return 0;
+      return 1;
+  }
+  return 0;
+}
+
+int
+gequal0(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_POL: case t_SER:
+      return !signe(x);
+
+    case t_INTMOD:
+      return !signe(gel(x,2));
+
+    case t_FFELT:
+      return FF_equal0(x);
+
+    case t_COMPLEX:
+     /* is 0 iff norm(x) would be 0 (can happen with Re(x) and Im(x) != 0
+      * only if Re(x) and Im(x) are of type t_REAL). See mp.c:addrr().
+      */
+      if (gequal0(gel(x,1)))
+      {
+        if (gequal0(gel(x,2))) return 1;
+        if (typ(gel(x,1))!=t_REAL || typ(gel(x,2))!=t_REAL) return 0;
+        return (expo(gel(x,1))>expo(gel(x,2)));
+      }
+      if (gequal0(gel(x,2)))
+      {
+        if (typ(gel(x,1))!=t_REAL || typ(gel(x,2))!=t_REAL) return 0;
+        return (expo(gel(x,2))>expo(gel(x,1)));
+      }
+      return 0;
+
+    case t_PADIC:
+      return !signe(gel(x,4));
+
+    case t_QUAD:
+      return gequal0(gel(x,2)) && gequal0(gel(x,3));
+
+    case t_POLMOD:
+      return gequal0(gel(x,2));
+
+    case t_RFRAC:
+      return gequal0(gel(x,1));
+
+    case t_VEC: case t_COL: case t_MAT:
+    {
+      long i;
+      for (i=lg(x)-1; i; i--)
+        if (!gequal0(gel(x,i))) return 0;
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/* x a t_POL or t_SER, considered as having valuation v; let X(t) = t^(-v) x(t)
+ * return 1 (true) if coeff(X,i) = 0 for all i != 0 and test(coeff(X, 0))
+ * is true. Return 0 (false) otherwise, or if x == 0 */
+static int
+is_monomial_test(GEN x, long v, int(*test)(GEN))
+{
+  long d, i, l;
+  if (!signe(x)) return 0;
+  if (v > 0) return 0;
+  l = lg(x); d = 2-v;
+  if (l <= d) return 0;
+  /* 2 <= d < l */
+  if (!test(gel(x,d))) return 0;
+  for (i = 2; i < d; i++)
+    if (!gequal0(gel(x,i))) return 0;
+  for (i = d+1; i < l; i++)
+    if (!gequal0(gel(x,i))) return 0;
+  return 1;
+}
+static int
+col_test(GEN x, int(*test)(GEN))
+{
+  long i, l = lg(x);
+  if (l == 1) return 0;
+  if (!test(gel(x,1))) return 0;
+  for (i = 2; i < l; i++)
+    if (!gequal0(gel(x,i))) return 0;
+  return 1;
+}
+static int
+mat_test(GEN x, int(*test)(GEN))
+{
+  long i, j, l = lg(x);
+  if (l == 1) return 1;
+  if (l != lgcols(x)) return 0;
+  for (i = 1; i < l; i++)
+    for (j = 1; j < l; j++)
+      if (i == j) {
+        if (!test(gcoeff(x,i,i))) return 0;
+      } else {
+        if (!gequal0(gcoeff(x,i,j))) return 0;
+      }
+  return 1;
+}
+
+/* returns 1 whenever x = 1, and 0 otherwise */
+int
+gequal1(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT:
+      return equali1(x);
+
+    case t_REAL:
+      return signe(x) > 0 ? absrnz_equal1(x): 0;
+
+    case t_INTMOD: case t_POLMOD:
+      return gequal1(gel(x,2));
+
+    case t_FFELT:
+      return FF_equal1(x);
+
+    case t_FRAC:
+      return 0;
+
+    case t_COMPLEX:
+      return gequal1(gel(x,1)) && gequal0(gel(x,2));
+
+    case t_PADIC:
+      return !valp(x) && gequal1(gel(x,4));
+
+    case t_QUAD:
+      return gequal1(gel(x,2)) && gequal0(gel(x,3));
+
+    case t_POL: return is_monomial_test(x, 0, &gequal1);
+    case t_SER: return is_monomial_test(x, valp(x), &gequal1);
+
+    case t_RFRAC: return gequal(gel(x,1), gel(x,2));
+    case t_COL: return col_test(x, &gequal1);
+    case t_MAT: return mat_test(x, &gequal1);
+  }
+  return 0;
+}
+
+/* returns 1 whenever the x = -1, 0 otherwise */
+int
+gequalm1(GEN x)
+{
+  pari_sp av;
+  long y;
+  GEN p1;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      return equalim1(x);
+
+    case t_REAL:
+      return signe(x) < 0 ? absrnz_equal1(x): 0;
+
+    case t_INTMOD:
+      av=avma; y=equalii(addsi(1,gel(x,2)), gel(x,1)); avma=av; return y;
+
+    case t_FRAC:
+      return 0;
+
+    case t_FFELT:
+      return FF_equalm1(x);
+
+    case t_COMPLEX:
+      return gequalm1(gel(x,1)) && gequal0(gel(x,2));
+
+    case t_QUAD:
+      return gequalm1(gel(x,2)) && gequal0(gel(x,3));
+
+    case t_PADIC:
+      av=avma; y=equalii(addsi(1,gel(x,4)), gel(x,3)); avma=av; return y;
+
+    case t_POLMOD:
+      av=avma; p1 = gaddgs(gel(x,2), 1);
+      y = signe(p1) && !gequal(p1,gel(x,1)); avma=av; return !y;
+
+    case t_POL: return is_monomial_test(x, 0, &gequalm1);
+    case t_SER: return is_monomial_test(x, valp(x), &gequalm1);
+
+    case t_RFRAC:
+      av=avma; y=gequal(gel(x,1), gneg_i(gel(x,2))); avma=av; return y;
+    case t_COL: return col_test(x, &gequalm1);
+    case t_MAT: return mat_test(x, &gequalm1);
+  }
+  return 0;
+}
+
+int
+gequalX(GEN x) { return typ(x) == t_POL && lg(x) == 4
+                      && isintzero(gel(x,2)) && isint1(gel(x,3)); }
+
+static int
+cmp_str(const char *x, const char *y)
+{
+  int f = strcmp(x, y);
+  return f > 0? 1
+              : f? -1: 0;
+}
+
+static int
+cmp_universal_rec(GEN x, GEN y, long i0)
+{
+  long i, lx = lg(x), ly = lg(y);
+  if (lx < ly) return -1;
+  if (lx > ly) return 1;
+  for (i = i0; i < lx; i++)
+  {
+    int f = cmp_universal(gel(x,i), gel(y,i));
+    if (f) return f;
+  }
+  return 0;
+}
+/* Universal "meaningless" comparison function. Transitive, returns 0 iff
+ * gidentical(x,y) */
+int
+cmp_universal(GEN x, GEN y)
+{
+  long lx, ly, i, tx = typ(x), ty = typ(y);
+
+  if (tx < ty) return -1;
+  if (ty < tx) return 1;
+  switch(tx)
+  {
+    case t_INT: return cmpii(x,y);
+    case t_STR: return cmp_str(GSTR(x),GSTR(y));
+    case t_REAL:
+    case t_VECSMALL:
+      lx = lg(x);
+      ly = lg(y);
+      if (lx < ly) return -1;
+      if (lx > ly) return 1;
+      for (i = 1; i < lx; i++)
+      {
+        if (x[i] < y[i]) return -1;
+        if (x[i] > y[i]) return 1;
+      }
+      return 0;
+
+    case t_POL:
+    case t_SER:
+    case t_FFELT:
+    case t_CLOSURE:
+      if (x[1] < y[1]) return -1;
+      if (x[1] > y[1]) return 1;
+      return cmp_universal_rec(x, y, 2);
+
+    case t_LIST:
+      x = list_data(x);
+      y = list_data(y);
+      if (!x) return y? -1: 0;
+      if (!y) return 1;
+      return cmp_universal_rec(x, y, 1);
+
+    default:
+      return cmp_universal_rec(x, y, lontyp[tx]);
+  }
+}
+
+static int
+cmpfrac(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN a = gel(x,1), b = gel(x,2);
+  GEN c = gel(y,1), d = gel(y,2);
+  int r = cmpii(mulii(a, d), mulii(b, c));
+  avma = av; return r;
+}
+static int
+cmpifrac(GEN a, GEN y)
+{
+  pari_sp av = avma;
+  GEN c = gel(y,1), d = gel(y,2);
+  int r = cmpii(mulii(a, d), c);
+  avma = av; return r;
+}
+static int
+cmprfrac(GEN a, GEN y)
+{
+  pari_sp av = avma;
+  GEN c = gel(y,1), d = gel(y,2);
+  int r = cmpri(mulri(a, d), c);
+  avma = av; return r;
+}
+
+/* returns the sign of x - y when it makes sense. 0 otherwise */
+int
+gcmp(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y);
+
+  if (tx == ty) /* generic case */
+    switch(tx)
+    {
+      case t_INT:  return cmpii(x, y);
+      case t_REAL: return cmprr(x, y);
+      case t_FRAC: return cmpfrac(x, y);
+      case t_STR:  return cmp_str(GSTR(x), GSTR(y));
+    }
+  switch(tx)
+  {
+    case t_INT:
+      switch(ty)
+      {
+        case t_REAL: return cmpir(x, y);
+        case t_FRAC: return cmpifrac(x, y);
+        case t_STR:  return -1;
+      }
+      break;
+    case t_REAL:
+      switch(ty)
+      {
+        case t_INT:  return cmpri(x, y);
+        case t_FRAC: return cmprfrac(x, y);
+        case t_STR:  return -1;
+      }
+      break;
+    case t_FRAC:
+      switch(ty)
+      {
+        case t_INT:  return -cmpifrac(y, x);
+        case t_REAL: return -cmprfrac(y, x);
+        case t_STR:  return -1;
+      }
+      break;
+    case t_STR: return 1;
+  }
+  pari_err_TYPE2("comparison",x,y);
+  return 0;/*not reached*/
+}
+
+int
+gcmpsg(long s, GEN y)
+{
+  long ty = typ(y);
+  switch(ty) {
+    case t_INT:  return cmpsi(s,y);
+    case t_REAL: return cmpsr(s,y);
+    case t_FRAC: {
+      pari_sp av = avma;
+      GEN n = gel(y,1), d = gel(y,2);
+      int f = cmpii(mulsi(s,d), n); avma = av; return f;
+    }
+    case t_STR: return -1;
+  }
+  pari_err_TYPE2("comparison",stoi(s),y);
+  return 0; /* not reached */
+}
+
+static long
+roughtype(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_MAT: return t_MAT;
+    case t_VEC: case t_COL: return t_VEC;
+    case t_VECSMALL: return t_VECSMALL;
+    default: return t_INT;
+  }
+}
+
+static int lexcmpsg(long x, GEN y);
+static int lexcmpgs(GEN x, long y) { return -lexcmpsg(y,x); }
+/* lexcmp(stoi(x),y), y t_VEC/t_COL/t_MAT */
+static int
+lexcmp_s_matvec(long x, GEN y)
+{
+  int fl;
+  if (lg(y)==1) return 1;
+  fl = lexcmpsg(x,gel(y,1));
+  if (fl) return fl;
+  return -1;
+}
+/* x a scalar, y a t_VEC/t_COL/t_MAT */
+static int
+lexcmp_scal_matvec(GEN x, GEN y)
+{
+  int fl;
+  if (lg(y)==1) return 1;
+  fl = lexcmp(x,gel(y,1));
+  if (fl) return fl;
+  return -1;
+}
+/* x a scalar, y a t_VECSMALL */
+static int
+lexcmp_scal_vecsmall(GEN x, GEN y)
+{
+  int fl;
+  if (lg(y)==1) return 1;
+  fl = lexcmpgs(x, y[1]);
+  if (fl) return fl;
+  return -1;
+}
+
+/* tx = ty = t_MAT, or x and y are both vect_t */
+static int
+lexcmp_similar(GEN x, GEN y)
+{
+  long i, lx = lg(x), ly = lg(y), l = minss(lx,ly);
+  for (i=1; i<l; i++)
+  {
+    int fl = lexcmp(gel(x,i),gel(y,i));
+    if (fl) return fl;
+  }
+  if (lx == ly) return 0;
+  return (lx < ly)? -1 : 1;
+}
+/* x a t_VECSMALL, y a t_VEC/t_COL ~ lexcmp_similar */
+static int
+lexcmp_vecsmall_vec(GEN x, GEN y)
+{
+  long i, lx = lg(x), ly = lg(y), l = minss(lx,ly);
+  for (i=1; i<l; i++)
+  {
+    int fl = lexcmpsg(x[i], gel(y,i));
+    if (fl) return fl;
+  }
+  if (lx == ly) return 0;
+  return (lx < ly)? -1 : 1;
+}
+
+/* x t_VEC/t_COL, y t_MAT */
+static int
+lexcmp_vec_mat(GEN x, GEN y)
+{
+  int fl;
+  if (lg(x)==1) return -1;
+  if (lg(y)==1) return 1;
+  fl = lexcmp_similar(x,gel(y,1));
+  if (fl) return fl;
+  return -1;
+}
+/* x t_VECSMALl, y t_MAT ~ lexcmp_vec_mat */
+static int
+lexcmp_vecsmall_mat(GEN x, GEN y)
+{
+  int fl;
+  if (lg(x)==1) return -1;
+  if (lg(y)==1) return 1;
+  fl = lexcmp_vecsmall_vec(x, gel(y,1));
+  if (fl) return fl;
+  return -1;
+}
+
+/* x a t_VECSMALL, not y */
+static int
+lexcmp_vecsmall_other(GEN x, GEN y, long ty)
+{
+  switch(ty)
+  {
+    case t_MAT: return lexcmp_vecsmall_mat(x, y);
+    case t_VEC: return lexcmp_vecsmall_vec(x, y);
+    default: return -lexcmp_scal_vecsmall(y, x); /*y scalar*/
+  }
+}
+
+/* lexcmp(stoi(s), y) */
+static int
+lexcmpsg(long x, GEN y)
+{
+  switch(roughtype(y))
+  {
+    case t_MAT:
+    case t_VEC:
+      return lexcmp_s_matvec(x,y);
+    case t_VECSMALL: /* ~ lexcmp_scal_matvec */
+      if (lg(y)==1) return 1;
+      return (x > y[1])? 1: -1;
+    default: return gcmpsg(x,y);
+  }
+}
+
+/* as gcmp for vector/matrices, using lexicographic ordering on components */
+int
+lexcmp(GEN x, GEN y)
+{
+  const long tx = roughtype(x), ty = roughtype(y);
+  if (tx == ty)
+    switch(tx)
+    {
+      case t_MAT:
+      case t_VEC:
+        return lexcmp_similar(x,y);
+      case t_VECSMALL:
+        return vecsmall_lexcmp(x,y);
+      default:
+        return gcmp(x,y);
+    }
+  if (tx == t_VECSMALL) return  lexcmp_vecsmall_other(x,y,ty);
+  if (ty == t_VECSMALL) return -lexcmp_vecsmall_other(y,x,tx);
+
+  if (tx == t_INT) return  lexcmp_scal_matvec(x,y); /*scalar*/
+  if (ty == t_INT) return -lexcmp_scal_matvec(y,x);
+
+  if (ty==t_MAT) return  lexcmp_vec_mat(x,y);
+  /*tx==t_MAT*/  return -lexcmp_vec_mat(y,x);
+}
+
+/*****************************************************************/
+/*                                                               */
+/*                          EQUALITY                             */
+/*                returns 1 if x == y, 0 otherwise               */
+/*                                                               */
+/*****************************************************************/
+/* x,y t_POL */
+static int
+polidentical(GEN x, GEN y)
+{
+  long lx;
+  if (x[1] != y[1]) return 0;
+  lx = lg(x); if (lg(y) != lg(x)) return 0;
+  for (lx--; lx >= 2; lx--) if (!gidentical(gel(x,lx), gel(y,lx))) return 0;
+  return 1;
+}
+/* x,y t_SER */
+static int
+seridentical(GEN x, GEN y) { return polidentical(x,y); }
+/* typ(x) = typ(y) = t_VEC/COL/MAT */
+static int
+vecidentical(GEN x, GEN y)
+{
+  long i;
+  if ((x[0] ^ y[0]) & (TYPBITS|LGBITS)) return 0;
+  for (i = lg(x)-1; i; i--)
+    if (! gidentical(gel(x,i),gel(y,i)) ) return 0;
+  return 1;
+}
+static int
+identicalrr(GEN x, GEN y)
+{
+  long i, lx = lg(x);
+  if (lg(y) != lx) return 0;
+  if (x[1] != y[1]) return 0;
+  i=2; while (i<lx && x[i]==y[i]) i++;
+  return (i == lx);
+}
+
+static int
+closure_identical(GEN x, GEN y)
+{
+  if (lg(x)!=lg(y) || x[1]!=y[1]) return 0;
+  if (!gidentical(gel(x,2),gel(y,2)) || !gidentical(gel(x,3),gel(y,3))
+   || !gidentical(gel(x,4),gel(y,4))) return 0;
+  if (lg(x)<8) return 1;
+  return gidentical(gel(x,7),gel(y,7));
+}
+
+int
+gidentical(GEN x, GEN y)
+{
+  long tx;
+
+  if (x == y) return 1;
+  tx = typ(x); if (typ(y) != tx) return 0;
+  switch(tx)
+  {
+    case t_INT:
+      return equalii(x,y);
+
+    case t_REAL:
+      return identicalrr(x,y);
+
+    case t_FRAC: case t_INTMOD:
+      return equalii(gel(x,2), gel(y,2)) && equalii(gel(x,1), gel(y,1));
+
+    case t_COMPLEX:
+      return gidentical(gel(x,2),gel(y,2)) && gidentical(gel(x,1),gel(y,1));
+    case t_PADIC:
+      return valp(x) == valp(y)
+        && equalii(gel(x,2),gel(y,2))
+        && equalii(gel(x,3),gel(y,3))
+        && equalii(gel(x,4),gel(y,4));
+    case t_POLMOD:
+      return gidentical(gel(x,2),gel(y,2)) && polidentical(gel(x,1),gel(y,1));
+    case t_POL:
+      return polidentical(x,y);
+    case t_SER:
+      return seridentical(x,y);
+    case t_FFELT:
+      return FF_equal(x,y);
+
+    case t_QFR:
+          if (!identicalrr(gel(x,4),gel(y,4))) return 0; /* fall through */
+    case t_QFI:
+      return equalii(gel(x,1),gel(y,1))
+          && equalii(gel(x,2),gel(y,2))
+          && equalii(gel(x,3),gel(y,3));
+
+    case t_QUAD:
+      return ZX_equal(gel(x,1),gel(y,1))
+          && gidentical(gel(x,2),gel(y,2))
+          && gidentical(gel(x,3),gel(y,3));
+
+    case t_RFRAC:
+      return gidentical(gel(x,1),gel(y,1)) && gidentical(gel(x,2),gel(y,2));
+
+    case t_STR:
+      return !strcmp(GSTR(x),GSTR(y));
+    case t_VEC: case t_COL: case t_MAT:
+      return vecidentical(x,y);
+    case t_VECSMALL:
+      return zv_equal(x,y);
+    case t_CLOSURE:
+      return closure_identical(x,y);
+    case t_LIST:
+      x = list_data(x);
+      y = list_data(y);
+      if (!x) return y? 0: 1;
+      if (!y) return 0;
+      return vecidentical(x, y);
+  }
+  return 0;
+}
+/* x,y t_POL */
+static int
+polequal(GEN x, GEN y)
+{
+  long lx, ly;
+  if ((x[1] ^ y[1]) & (VARNBITS | SIGNBITS)) return 0;
+  lx = lg(x); ly = lg(y);
+  while (lx > ly) if (!gequal0(gel(x,--lx))) return 0;
+  while (ly > lx) if (!gequal0(gel(y,--ly))) return 0;
+  for (lx--; lx >= 2; lx--) if (!gequal(gel(x,lx), gel(y,lx))) return 0;
+  return 1;
+}
+
+/* typ(x) = typ(y) = t_VEC/COL/MAT */
+static int
+vecequal(GEN x, GEN y)
+{
+  long i;
+  if ((x[0] ^ y[0]) & (TYPBITS|LGBITS)) return 0;
+  for (i = lg(x)-1; i; i--)
+    if (! gequal(gel(x,i),gel(y,i)) ) return 0;
+  return 1;
+}
+
+static int
+gequal_try(GEN x, GEN y)
+{
+  int i;
+  pari_CATCH(CATCH_ALL) {
+    GEN E = pari_err_last();
+    switch(err_get_num(E))
+    {
+      case e_STACK: case e_MEM: case e_ALARM:
+        pari_err(0, E); /* rethrow */
+    }
+    return 0;
+  } pari_TRY {
+    i = gequal0(gadd(x, gneg_i(y)));
+  } pari_ENDCATCH;
+  return i;
+}
+
+int
+gequal(GEN x, GEN y)
+{
+  pari_sp av;
+  long tx, ty;
+  long i;
+
+  if (x == y) return 1;
+  tx = typ(x);
+  ty = typ(y);
+  if (tx == ty)
+    switch(tx)
+    {
+      case t_INT:
+        return equalii(x,y);
+
+      case t_REAL:
+        return equalrr(x,y);
+
+      case t_FRAC: case t_INTMOD:
+        return equalii(gel(x,2), gel(y,2)) && equalii(gel(x,1), gel(y,1));
+
+      case t_COMPLEX:
+        return gequal(gel(x,2),gel(y,2)) && gequal(gel(x,1),gel(y,1));
+      case t_PADIC:
+        if (!equalii(gel(x,2),gel(y,2))) return 0;
+        av = avma; i = gequal0(gsub(x,y)); avma = av;
+        return i;
+      case t_POLMOD:
+        return gequal(gel(x,2),gel(y,2)) && RgX_equal_var(gel(x,1),gel(y,1));
+      case t_POL:
+        return polequal(x,y);
+
+      case t_FFELT:
+        return FF_equal(x,y);
+
+      case t_QFR:
+      case t_QFI:
+        return equalii(gel(x,1),gel(y,1))
+            && equalii(gel(x,2),gel(y,2))
+            && equalii(gel(x,3),gel(y,3));
+
+      case t_QUAD:
+        return ZX_equal(gel(x,1),gel(y,1))
+            && gequal(gel(x,2),gel(y,2))
+            && gequal(gel(x,3),gel(y,3));
+
+      case t_RFRAC:
+      {
+        GEN a = gel(x,1), b = gel(x,2), c = gel(y,1), d = gel(y,2);
+        if (gequal(b,d)) return gequal(a,c); /* simple case */
+        av = avma;
+        i = gequal(simplify_shallow(gmul(a,d)), simplify_shallow(gmul(b,c)));
+        avma = av; return i;
+      }
+
+      case t_STR:
+        return !strcmp(GSTR(x),GSTR(y));
+      case t_VEC: case t_COL: case t_MAT:
+        return vecequal(x,y);
+      case t_VECSMALL:
+        return zv_equal(x,y);
+      case t_LIST:
+        x = list_data(x);
+        y = list_data(y);
+        if (!x) return y? 0: 1;
+        if (!y) return 0;
+        return gequal(x, y);
+      case t_CLOSURE:
+        return closure_identical(x,y);
+    }
+  (void)&av; /* emulate volatile */
+  av = avma; i = gequal_try(x, y);
+  avma = av; return i;
+}
+
+int
+gequalsg(long s, GEN x)
+{
+  pari_sp av = avma;
+  int f = gequal(stoi(s), x);
+  avma = av; return f;
+}
+/*******************************************************************/
+/*                                                                 */
+/*                          VALUATION                              */
+/*             p is either a t_INT or a t_POL.                     */
+/*  returns the largest exponent of p dividing x when this makes   */
+/*  sense : error for types real, integermod and polymod if p does */
+/*  not divide the modulus, q-adic if q!=p.                        */
+/*                                                                 */
+/*******************************************************************/
+
+static long
+minval(GEN x, GEN p)
+{
+  long i,k, val = LONG_MAX, lx = lg(x);
+  for (i=lontyp[typ(x)]; i<lx; i++)
+  {
+    k = gvaluation(gel(x,i),p);
+    if (k < val) val = k;
+  }
+  return val;
+}
+
+static int
+intdvd(GEN x, GEN y, GEN *z) { GEN r; *z = dvmdii(x,y,&r); return (r==gen_0); }
+
+/* x t_FRAC, p t_INT, return v_p(x) */
+static long
+ratval(GEN x, GEN p) {
+  long v = Z_pval(gel(x,2),p);
+  if (v) return -v;
+  return Z_pval(gel(x,1),p);
+}
+
+long
+Q_pval(GEN x, GEN p) { return (typ(x) == t_INT)? Z_pval(x, p): ratval(x, p); }
+
+long
+Q_pvalrem(GEN x, GEN p, GEN *y)
+{
+  GEN a, b;
+  long v;
+  if (typ(x) == t_INT) return Z_pvalrem(x, p, y);
+  a = gel(x,1);
+  b = gel(x,2);
+  v = Z_pvalrem(b, p, &b);
+  if (v) { *y = isint1(b)? a: mkfrac(a, b); return -v; }
+  v = Z_pvalrem(a, p, &a);
+  *y = mkfrac(a, b); return v;
+}
+
+long
+gvaluation(GEN x, GEN p)
+{
+  long tx = typ(x), tp = typ(p);
+  pari_sp av, limit;
+
+  switch(tp)
+  {
+    case t_INT:
+      if (signe(p) && !is_pm1(p)) break;
+      pari_err_DOMAIN("gvaluation", "p", "=", p, p);
+    case t_POL:
+      if (degpol(p) > 0) break;
+    default:
+      pari_err_DOMAIN("gvaluation", "p", "=", p, p);
+  }
+
+  switch(tx)
+  {
+    case t_INT:
+      if (!signe(x)) return LONG_MAX;
+      if (tp == t_POL) return 0;
+      return Z_pval(x,p);
+
+    case t_REAL:
+      if (tp == t_POL) return 0;
+      break;
+
+    case t_FFELT:
+      if (tp == t_POL) return FF_equal0(x)? LONG_MAX: 0;
+      break;
+
+    case t_INTMOD: {
+      GEN a = gel(x,1), b = gel(x,2);
+      long val;
+      if (tp == t_POL) return signe(b)? 0: LONG_MAX;
+      av = avma;
+      if (!intdvd(a, p, &a)) break;
+      if (!intdvd(b, p, &b)) { avma = av; return 0; }
+      val = 1; while (intdvd(a,p,&a) && intdvd(b,p,&b)) val++;
+      avma = av; return val;
+    }
+
+    case t_FRAC:
+      if (tp == t_POL) return 0;
+      return ratval(x, p);
+
+    case t_PADIC:
+      if (tp == t_POL) return 0;
+      if (!equalii(p,gel(x,2))) break;
+      return valp(x);
+
+    case t_POLMOD: {
+      GEN a = gel(x,1), b = gel(x,2);
+      long v, val;
+      if (tp == t_INT) return gvaluation(b,p);
+      v = varn(p);
+      if (varn(a) != v) return 0;
+      av = avma;
+      a = RgX_divrem(a, p, ONLY_DIVIDES);
+      if (!a) break;
+      if (typ(b) != t_POL || varn(b) != v ||
+          !(b = RgX_divrem(b, p, ONLY_DIVIDES)) ) { avma = av; return 0; }
+      val = 1;
+      while ((a = RgX_divrem(a, p, ONLY_DIVIDES)) &&
+             (b = RgX_divrem(b, p, ONLY_DIVIDES)) ) val++;
+      avma = av; return val;
+    }
+    case t_POL: {
+      if (tp == t_POL) {
+        long vp = varn(p), vx = varn(x);
+        if (vp == vx)
+        {
+          long val;
+          if (RgX_is_monomial(p)) return RgX_val(x) / degpol(p);
+          av = avma; limit=stack_lim(av,1);
+          for (val=0; ; val++)
+          {
+            x = RgX_divrem(x,p,ONLY_DIVIDES);
+            if (!x) { avma = av; return val; }
+            if (low_stack(limit, stack_lim(av,1)))
+            {
+              if(DEBUGMEM>1) pari_warn(warnmem,"gvaluation");
+              x = gerepilecopy(av, x);
+            }
+          }
+        }
+        if (varncmp(vx, vp) > 0) return 0;
+      }
+      return minval(x,p);
+    }
+
+    case t_SER: {
+      if (tp == t_POL) {
+        long vp = varn(p), vx = varn(x);
+        if (vp == vx)
+        {
+          long val = RgX_val(p);
+          if (!val) pari_err_DOMAIN("gvaluation", "p", "=", p, p);
+          return (long)(valp(x) / val);
+        }
+        if (varncmp(vx, vp) > 0) return 0;
+      }
+      return minval(x,p);
+    }
+
+    case t_RFRAC:
+      return gvaluation(gel(x,1),p) - gvaluation(gel(x,2),p);
+
+    case t_COMPLEX: case t_QUAD: case t_VEC: case t_COL: case t_MAT:
+      return minval(x,p);
+  }
+  pari_err_OP("valuation", x,p);
+  return 0; /* not reached */
+}
+
+/* x is non-zero */
+long
+u_lvalrem(ulong x, ulong p, ulong *py)
+{
+  ulong vx;
+  if (p == 2) { vx = vals(x); *py = x >> vx; return vx; }
+  for(vx = 0;;)
+  {
+    if (x % p) { *py = x; return vx; }
+    x /= p; /* gcc is smart enough to make a single div */
+    vx++;
+  }
+}
+long
+u_lval(ulong x, ulong p)
+{
+  ulong vx;
+  if (p == 2) return vals(x);
+  for(vx = 0;;)
+  {
+    if (x % p) return vx;
+    x /= p; /* gcc is smart enough to make a single div */
+    vx++;
+  }
+}
+
+long
+z_lval(long s, ulong p) { return u_lval(labs(s), p); }
+long
+z_lvalrem(long s, ulong p, long *py)
+{
+  long v;
+  if (s < 0)
+  {
+    ulong u = (ulong)-s;
+    v = u_lvalrem(u, p, &u);
+    *py = -(long)u;
+  }
+  else
+  {
+    ulong u = (ulong)s;
+    v = u_lvalrem(u, p, &u);
+    *py = (long)u;
+  }
+  return v;
+}
+/* assume |p| > 1 */
+long
+z_pval(long s, GEN p)
+{
+  if (lgefint(p) > 3) return 0;
+  return z_lval(s, (ulong)p[2]);
+}
+/* assume |p| > 1 */
+long
+z_pvalrem(long s, GEN p, long *py)
+{
+  if (lgefint(p) > 3) { *py = s; return 0; }
+  return z_lvalrem(s, (ulong)p[2], py);
+}
+
+/* return v_q(x) and set *py = x / q^v_q(x), using divide & conquer */
+static long
+Z_pvalrem_DC(GEN x, GEN q, GEN *py)
+{
+  GEN r, z = dvmdii(x, q, &r);
+  long v;
+  if (r != gen_0) { *py = x; return 0; }
+  if (2 * lgefint(q) <= lgefint(z)+3) /* avoid squaring if pointless */
+    v = Z_pvalrem_DC(z, sqri(q), py) << 1;
+  else
+  { v = 0; *py = z; }
+  z = dvmdii(*py, q, &r);
+  if (r != gen_0) return v + 1;
+  *py = z; return v + 2;
+}
+
+static const long VAL_DC_THRESHOLD = 16;
+
+long
+Z_lval(GEN x, ulong p)
+{
+  long vx;
+  pari_sp av;
+  if (p == 2) return vali(x);
+  if (lgefint(x) == 3) return u_lval((ulong)x[2], p);
+  av = avma;
+  for(vx = 0;;)
+  {
+    ulong r;
+    GEN q = diviu_rem(x, p, &r);
+    if (r) break;
+    vx++; x = q;
+    if (vx == VAL_DC_THRESHOLD) {
+      if (p == 1) pari_err_DOMAIN("Z_lval", "p", "=", gen_1, gen_1);
+      vx += Z_pvalrem_DC(x, sqru(p), &x) << 1;
+      q = diviu_rem(x, p, &r); if (!r) vx++;
+      break;
+    }
+  }
+  avma = av; return vx;
+}
+long
+Z_lvalrem(GEN x, ulong p, GEN *py)
+{
+  long vx, sx;
+  pari_sp av;
+  if (p == 2) { vx = vali(x); *py = shifti(x, -vx); return vx; }
+  if (lgefint(x) == 3) {
+    ulong u;
+    vx = u_lvalrem((ulong)x[2], p, &u);
+    *py = signe(x) < 0? utoineg(u): utoipos(u);
+    return vx;
+  }
+  av = avma; (void)new_chunk(lgefint(x));
+  sx = signe(x);
+  for(vx = 0;;)
+  {
+    ulong r;
+    GEN q = diviu_rem(x, p, &r);
+    if (r) break;
+    vx++; x = q;
+    if (vx == VAL_DC_THRESHOLD) {
+      if (p == 1) pari_err_DOMAIN("Z_lvalrem", "p", "=", gen_1, gen_1);
+      vx += Z_pvalrem_DC(x, sqru(p), &x) << 1;
+      q = diviu_rem(x, p, &r); if (!r) { vx++; x = q; }
+      break;
+    }
+  }
+  avma = av; *py = icopy(x); setsigne(*py, sx); return vx;
+}
+
+/* Is |q| <= p ? */
+static int
+isless_iu(GEN q, ulong p) {
+  long l = lgefint(q);
+  return l==2 || (l == 3 && (ulong)q[2] <= p);
+}
+
+long
+u_lvalrem_stop(ulong *n, ulong p, int *stop)
+{
+  ulong N = *n, q = N / p, r = N % p; /* gcc makes a single div */
+  long v = 0;
+  if (!r)
+  {
+    do { v++; N = q; q = N / p; r = N % p; } while (!r);
+    *n = N;
+  }
+  *stop = q <= p; return v;
+}
+/* Assume n > 0. Return v_p(n), set *n := n/p^v_p(n). Set 'stop' if now
+ * n < p^2 [implies n prime if no prime < p divides n] */
+long
+Z_lvalrem_stop(GEN *n, ulong p, int *stop)
+{
+  pari_sp av;
+  long v;
+  ulong r;
+  GEN N, q;
+
+  if (lgefint(*n) == 3)
+  {
+    r = (*n)[2];
+    v = u_lvalrem_stop(&r, p, stop);
+    if (v) *n = utoipos(r);
+    return v;
+  }
+  av = avma; v = 0; q = diviu_rem(*n, p, &r);
+  if (r) avma = av;
+  else
+  {
+    do {
+      v++; N = q;
+      if (v == VAL_DC_THRESHOLD)
+      {
+        v += Z_pvalrem_DC(N,sqru(p),&N) << 1;
+        q = diviu_rem(N, p, &r); if (!r) { v++; N = q; }
+        break;
+      }
+      q = diviu_rem(N, p, &r);
+    } while (!r);
+    *n = N;
+  }
+  *stop = isless_iu(q,p); return v;
+}
+
+/* x is a non-zero integer, |p| > 1 */
+long
+Z_pvalrem(GEN x, GEN p, GEN *py)
+{
+  long vx;
+  pari_sp av;
+
+  if (lgefint(p) == 3) return Z_lvalrem(x, (ulong)p[2], py);
+  if (lgefint(x) == 3) { *py = icopy(x); return 0; }
+  av = avma; vx = 0; (void)new_chunk(lgefint(x));
+  for(;;)
+  {
+    GEN r, q = dvmdii(x,p,&r);
+    if (r != gen_0) { avma = av; *py = icopy(x); return vx; }
+    vx++; x = q;
+  }
+}
+long
+u_pvalrem(ulong x, GEN p, ulong *py)
+{
+  if (lgefint(p) == 3) return u_lvalrem(x, (ulong)p[2], py);
+  *py = x; return 0;
+}
+long
+u_pval(ulong x, GEN p)
+{
+  if (lgefint(p) == 3) return u_lval(x, (ulong)p[2]);
+  return 0;
+}
+long
+Z_pval(GEN x, GEN p) {
+  long vx;
+  pari_sp av;
+
+  if (lgefint(p) == 3) return Z_lval(x, (ulong)p[2]);
+  if (lgefint(x) == 3) return 0;
+  av = avma; vx = 0;
+  for(;;)
+  {
+    GEN r, q = dvmdii(x,p,&r);
+    if (r != gen_0) { avma = av; return vx; }
+    vx++; x = q;
+  }
+}
+
+/* return v_p(n!) = [n/p] + [n/p^2] + ... */
+long
+factorial_lval(ulong n, ulong p)
+{
+  ulong q = p, v = 0;
+  do { v += n/q; q *= p; } while (n >= q);
+  return (long)v;
+}
+
+/********** Same for "containers" ZX / ZV / ZC **********/
+
+/* If the t_INT q divides the ZX/ZV x, return the quotient. Otherwise NULL.
+ * Stack clean; assumes lg(x) > 1 */
+static GEN
+gen_Z_divides(GEN x, GEN q, long imin)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+
+  y[1] = x[1]; /* Needed for ZX; no-op if ZV, overwritten in first iteration */
+  for (i = imin; i < l; i++)
+  {
+    GEN r, xi = gel(x,i);
+    if (!signe(xi)) { gel(y,i) = xi; continue; }
+    gel(y,i) = dvmdii(xi, q, &r);
+    if (r != gen_0) { avma = (pari_sp)(y+l); return NULL; }
+  }
+  return y;
+}
+/* If q divides the ZX/ZV x, return the quotient. Otherwise NULL.
+ * Stack clean; assumes lg(x) > 1 */
+static GEN
+gen_z_divides(GEN x, ulong q, long imin)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+
+  y[1] = x[1]; /* Needed for ZX; no-op if ZV, overwritten in first iteration */
+  for (i = imin; i < l; i++)
+  {
+    ulong r;
+    GEN xi = gel(x,i);
+    if (!signe(xi)) { gel(y,i) = xi; continue; }
+    gel(y,i) = diviu_rem(xi, q, &r);
+    if (r) { avma = (pari_sp)(y+l); return NULL; }
+    affectsign_safe(xi, &gel(y,i));
+  }
+  return y;
+}
+
+/* return v_q(x) and set *py = x / q^v_q(x), using divide & conquer */
+static long
+gen_pvalrem_DC(GEN x, GEN q, GEN *py, long imin)
+{
+
+  pari_sp av = avma;
+  long v, i, l, lz = LONG_MAX;
+  GEN y = cgetg_copy(x, &l);
+
+  y[1] = x[1];
+  for (i = imin; i < l; i++)
+  {
+    GEN r, xi = gel(x,i);
+    if (!signe(xi)) { gel(y,i) = xi; continue; }
+    gel(y,i) = dvmdii(xi, q, &r);
+    if (r != gen_0) { avma = av; *py = x; return 0; }
+    lz = minss(lz, lgefint(gel(y,i)));
+  }
+  if (2 * lgefint(q) <= lz+3) /* avoid squaring if pointless */
+    v = gen_pvalrem_DC(y, sqri(q), py, imin) << 1;
+  else
+  { v = 0; *py = y; }
+
+  y = gen_Z_divides(*py, q, imin);
+  if (!y) return v+1;
+  *py = y; return v+2;
+}
+
+static long
+gen_2val(GEN x, long imin)
+{
+  long i, lx = lg(x), v = LONG_MAX;
+  for (i = imin; i < lx; i++)
+  {
+    GEN c = gel(x,i);
+    long w;
+    if (!signe(c)) continue;
+    w = vali(c);
+    if (w < v) { v = w; if (!v) break; }
+  }
+  return v;
+}
+static long
+gen_lval(GEN x, ulong p, long imin)
+{
+  long i, lx, v;
+  pari_sp av;
+  GEN y;
+  if (p == 2) return gen_2val(x, imin);
+  av = avma;
+  lx = lg(x); y = leafcopy(x);
+  for(v = 0;; v++)
+    for (i = imin; i < lx; i++)
+    {
+      ulong r; gel(y,i) = diviu_rem(gel(y,i), p, &r);
+      if (r) { avma = av; return v; }
+    }
+}
+long
+ZX_lval(GEN x, ulong p) { return gen_lval(x, p, 2); }
+long
+ZV_lval(GEN x, ulong p) { return gen_lval(x, p, 1); }
+
+static long
+gen_pval(GEN x, GEN p, long imin)
+{
+  long i, lx, v;
+  pari_sp av;
+  GEN y;
+  if (lgefint(p) == 3) return gen_lval(x, p[2], imin);
+  av = avma;
+  lx = lg(x); y = leafcopy(x);
+  for(v = 0;; v++)
+  {
+    if (v == VAL_DC_THRESHOLD)
+    {
+      if (is_pm1(p)) pari_err_DOMAIN("gen_pval", "p", "=", p, p);
+      v += gen_pvalrem_DC(y, p, &y, imin);
+      avma = av; return v;
+    }
+
+    for (i = imin; i < lx; i++)
+    {
+      GEN r; gel(y,i) = dvmdii(gel(y,i), p, &r);
+      if (r != gen_0) { avma = av; return v; }
+    }
+  }
+}
+long
+ZX_pval(GEN x, GEN p) { return gen_pval(x, p, 2); }
+long
+ZV_pval(GEN x, GEN p) { return gen_pval(x, p, 1); }
+/* v = 0 (mod p) */
+int
+ZV_Z_dvd(GEN v, GEN p)
+{
+  pari_sp av = avma;
+  long i, l = lg(v);
+  for (i=1; i<l; i++)
+    if (remii(gel(v,i), p) != gen_0) { avma = av; return 0; }
+  avma = av; return 1;
+}
+
+static long
+gen_2valrem(GEN x, GEN *px, long imin)
+{
+  long i, lx = lg(x), v = LONG_MAX;
+  GEN z;
+  for (i = imin; i < lx; i++)
+  {
+    GEN c = gel(x,i);
+    long w;
+    if (!signe(c)) continue;
+    w = vali(c);
+    if (w < v) {
+      v = w;
+      if (!v) { *px = x; return 0; } /* early abort */
+    }
+  }
+  z = cgetg_copy(x, &lx); z[1] = x[1];
+  for (i=imin; i<lx; i++) gel(z,i) = shifti(gel(x,i), -v);
+  *px = z; return v;
+}
+static long
+gen_lvalrem(GEN x, ulong p, GEN *px, long imin)
+{
+  long i, lx, v;
+  GEN y;
+  if (p == 2) return gen_2valrem(x, px, imin);
+  y = cgetg_copy(x, &lx);
+  y[1] = x[1];
+  x = leafcopy(x);
+  for(v = 0;; v++)
+  {
+    if (v == VAL_DC_THRESHOLD)
+    {
+      if (p == 1) pari_err_DOMAIN("gen_lvalrem", "p", "=", gen_1, gen_1);
+      v += gen_pvalrem_DC(x, sqru(p), px, imin) << 1;
+      x = gen_z_divides(*px, p, imin);
+      if (x) { *px = x; v++; }
+      return v;
+    }
+
+    for (i = imin; i < lx; i++)
+    {
+      ulong r; gel(y,i) = diviu_rem(gel(x,i), p, &r);
+      if (r) { *px = x; return v; }
+      affectsign_safe(gel(x,i), &gel(y,i));
+    }
+    swap(x, y);
+  }
+}
+long
+ZX_lvalrem(GEN x, ulong p, GEN *px) { return gen_lvalrem(x,p,px, 2); }
+long
+ZV_lvalrem(GEN x, ulong p, GEN *px) { return gen_lvalrem(x,p,px, 1); }
+
+static long
+gen_pvalrem(GEN x, GEN p, GEN *px, long imin)
+{
+  long i, lx, v;
+  GEN y;
+  if (lgefint(p) == 3) return gen_lvalrem(x, p[2], px, imin);
+  y = cgetg_copy(x, &lx);
+  y[1] = x[1];
+  x = leafcopy(x);
+  for(v = 0;; v++)
+  {
+    if (v == VAL_DC_THRESHOLD)
+    {
+      if (is_pm1(p)) pari_err_DOMAIN("gen_pvalrem", "p", "=", p, p);
+      return v + gen_pvalrem_DC(x, p, px, imin);
+    }
+
+    for (i = imin; i < lx; i++)
+    {
+      GEN r; gel(y,i) = dvmdii(gel(x,i), p, &r);
+      if (r != gen_0) { *px = x; return v; }
+    }
+    swap(x, y);
+  }
+}
+long
+ZX_pvalrem(GEN x, GEN p, GEN *px) { return gen_pvalrem(x,p,px, 2); }
+long
+ZV_pvalrem(GEN x, GEN p, GEN *px) { return gen_pvalrem(x,p,px, 1); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                       NEGATION: Create -x                       */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+gneg(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      return signe(x)? negi(x): gen_0;
+    case t_REAL:
+      return mpneg(x);
+
+    case t_INTMOD: y=cgetg(3,t_INTMOD);
+      gel(y,1) = icopy(gel(x,1));
+      gel(y,2) = signe(gel(x,2))? subii(gel(y,1),gel(x,2)): gen_0;
+      break;
+
+    case t_FRAC:
+      y = cgetg(3, t_FRAC);
+      gel(y,1) = negi(gel(x,1));
+      gel(y,2) = icopy(gel(x,2)); break;
+
+    case t_COMPLEX:
+      y=cgetg(3, t_COMPLEX);
+      gel(y,1) = gneg(gel(x,1));
+      gel(y,2) = gneg(gel(x,2));
+      break;
+
+    case t_POLMOD: y=cgetg(3,t_POLMOD);
+      gel(y,1) = RgX_copy(gel(x,1));
+      gel(y,2) = gneg(gel(x,2)); break;
+
+    case t_RFRAC:
+      y = cgetg(3, t_RFRAC);
+      gel(y,1) = gneg(gel(x,1));
+      gel(y,2) = RgX_copy(gel(x,2)); break;
+
+    case t_PADIC:
+      if (!signe(gel(x,4))) return gcopy(x);
+      y = cgetg(5, t_PADIC);
+      y[1] = x[1];
+      gel(y,2) = icopy(gel(x,2));
+      gel(y,3) = icopy(gel(x,3));
+      gel(y,4) = subii(gel(x,3),gel(x,4));
+      break;
+
+    case t_QUAD:
+      y=cgetg(4,t_QUAD);
+      gel(y,1) = ZX_copy(gel(x,1));
+      gel(y,2) = gneg(gel(x,2));
+      gel(y,3) = gneg(gel(x,3)); break;
+
+    case t_FFELT: return FF_neg(x);
+    case t_POL: return RgX_neg(x);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gneg(gel(x,i));
+      break;
+    case t_VEC: return RgV_neg(x);
+    case t_COL: return RgC_neg(x);
+    case t_MAT: return RgM_neg(x);
+    default:
+      pari_err_TYPE("gneg",x);
+      return NULL; /* not reached */
+  }
+  return y;
+}
+
+GEN
+gneg_i(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      return signe(x)? negi(x): gen_0;
+    case t_REAL:
+      return mpneg(x);
+
+    case t_INTMOD: y=cgetg(3,t_INTMOD);
+      gel(y,1) = gel(x,1);
+      gel(y,2) = signe(gel(x,2))? subii(gel(y,1),gel(x,2)): gen_0;
+      break;
+
+    case t_FRAC:
+      y = cgetg(3, t_FRAC);
+      gel(y,1) = negi(gel(x,1));
+      gel(y,2) = gel(x,2); break;
+
+    case t_COMPLEX:
+      y = cgetg(3, t_COMPLEX);
+      gel(y,1) = gneg_i(gel(x,1));
+      gel(y,2) = gneg_i(gel(x,2)); break;
+
+    case t_PADIC: y = cgetg(5,t_PADIC);
+      y[1] = x[1];
+      gel(y,2) = gel(x,2);
+      gel(y,3) = gel(x,3);
+      gel(y,4) = signe(gel(x,4))? subii(gel(x,3),gel(x,4)): gen_0; break;
+
+    case t_POLMOD: y=cgetg(3,t_POLMOD);
+      gel(y,1) = gel(x,1);
+      gel(y,2) = gneg_i(gel(x,2)); break;
+
+    case t_FFELT: return FF_neg_i(x);
+
+    case t_QUAD: y=cgetg(4,t_QUAD);
+      gel(y,1) = gel(x,1);
+      gel(y,2) = gneg_i(gel(x,2));
+      gel(y,3) = gneg_i(gel(x,3)); break;
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gneg_i(gel(x,i));
+      break;
+
+    case t_POL: case t_SER:
+      y = cgetg_copy(x, &lx); y[1]=x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gneg_i(gel(x,i));
+      break;
+
+    case t_RFRAC:
+      y = cgetg(3, t_RFRAC);
+      gel(y,1) = gneg_i(gel(x,1));
+      gel(y,2) = gel(x,2); break;
+
+    default:
+      pari_err_TYPE("gneg_i",x);
+      return NULL; /* not reached */
+  }
+  return y;
+}
+
+/******************************************************************/
+/*                                                                */
+/*                       ABSOLUTE VALUE                           */
+/*    Create abs(x) if x is integer, real, fraction or complex.   */
+/*                       Error otherwise.                         */
+/*                                                                */
+/******************************************************************/
+static int
+is_negative(GEN x) {
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL:
+      return (signe(x) < 0);
+    case t_FRAC:
+      return (signe(gel(x,1)) < 0);
+  }
+  return 0;
+}
+
+GEN
+gabs(GEN x, long prec)
+{
+  long lx, i;
+  pari_sp av, tetpil;
+  GEN y,p1;
+
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL:
+      return mpabs(x);
+
+    case t_FRAC:
+      return absfrac(x);
+
+    case t_COMPLEX:
+      av=avma; p1=cxnorm(x);
+      switch(typ(p1))
+      {
+        case t_INT:
+          if (!Z_issquareall(p1, &y)) break;
+          return gerepileupto(av, y);
+        case t_FRAC: {
+          GEN a,b;
+          if (!Z_issquareall(gel(p1,1), &a)) break;
+          if (!Z_issquareall(gel(p1,2), &b)) break;
+          return gerepileupto(av, gdiv(a,b));
+        }
+      }
+      tetpil=avma;
+      return gerepile(av,tetpil,gsqrt(p1,prec));
+
+    case t_QUAD:
+      av = avma;
+      return gerepileuptoleaf(av, gabs(quadtofp(x, prec), prec));
+
+    case t_POL:
+      lx = lg(x); if (lx<=2) return RgX_copy(x);
+      return is_negative(gel(x,lx-1))? gneg(x): RgX_copy(x);
+
+    case t_SER:
+     if (!signe(x)) pari_err_DOMAIN("abs", "argument", "=", gen_0, x);
+     if (valp(x)) pari_err_DOMAIN("abs", "series valuation", "!=", gen_0, x);
+     return is_negative(gel(x,2))? gneg(x): gcopy(x);
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gabs(gel(x,i),prec);
+      return y;
+  }
+  pari_err_TYPE("gabs",x);
+  return NULL; /* not reached */
+}
+
+GEN
+gmax(GEN x, GEN y) { return gcopy(gcmp(x,y)<0? y: x); }
+GEN
+gmaxgs(GEN x, long s) { return (gcmpsg(s,x)>=0)? stoi(s): gcopy(x); }
+
+GEN
+gmin(GEN x, GEN y) { return gcopy(gcmp(x,y)<0? x: y); }
+GEN
+gmings(GEN x, long s) { return (gcmpsg(s,x)>0)? gcopy(x): stoi(s); }
+
+long
+vecindexmax(GEN x)
+{
+  long lx = lg(x), i0, i;
+  GEN s;
+
+  if (lx==1) pari_err_DOMAIN("vecindexmax", "empty argument", "=", x,x);
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      s = gel(x,i0=1);
+      for (i=2; i<lx; i++)
+        if (gcmp(gel(x,i),s) > 0) s = gel(x,i0=i);
+      return i0;
+    case t_VECSMALL:
+      return vecsmall_indexmax(x);
+    default: pari_err_TYPE("vecindexmax",x);
+  }
+  /* NOT REACHED */
+  return 0;
+}
+long
+vecindexmin(GEN x)
+{
+  long lx = lg(x), i0, i;
+  GEN s;
+
+  if (lx==1) pari_err_DOMAIN("vecindexmin", "empty argument", "=", x,x);
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      s = gel(x,i0=1);
+      for (i=2; i<lx; i++)
+        if (gcmp(gel(x,i),s) < 0) s = gel(x,i0=i);
+      return i0;
+    case t_VECSMALL:
+      return vecsmall_indexmin(x);
+    default: pari_err_TYPE("vecindexmin",x);
+  }
+  /* NOT REACHED */
+  return 0;
+}
+
+
+GEN
+vecmax0(GEN x, GEN *pi)
+{
+  long i0, j0, i, j;
+  GEN s;
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      i = vecindexmax(x);
+      if (pi) *pi = utoipos(i);
+      return gcopy(gel(x, i));
+    case t_MAT: {
+      long lx2, lx = lg(x);
+      if (lx==1 || (lx2 = lgcols(x)) == 1)
+      {
+        pari_err_DOMAIN("vecmax", "empty argument", "=", x,x);
+        return NULL;/*not reached*/
+      }
+      s = gcoeff(x,i0=1,j0=1); i = 2;
+      for (j=1; j<lx; j++,i=1)
+      {
+        GEN c = gel(x,j);
+        for (; i<lx2; i++)
+          if (gcmp(gel(c,i),s) > 0) { s = gel(c,i); j0=j; i0=i; }
+      }
+      if (pi) *pi = mkvec2(utoipos(i0), utoipos(j0));
+      return gcopy(s);
+    }
+    case t_VECSMALL:
+      i = vecsmall_indexmax(x);
+      if (pi) *pi = utoipos(i);
+      return stoi(x[i]);
+    default:
+      return gcopy(x);
+  }
+}
+GEN
+vecmin0(GEN x, GEN *pi)
+{
+  long i0, j0, i, j;
+  GEN s;
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      i = vecindexmin(x);
+      if (pi) *pi = utoipos(i);
+      return gcopy(gel(x, i));
+    case t_MAT: {
+      long lx2, lx = lg(x);
+      if (lx==1 || (lx2 = lgcols(x)) == 1)
+      {
+        pari_err_DOMAIN("vecmin", "empty argument", "=", x,x);
+        return NULL;/*not reached*/
+      }
+      s = gcoeff(x,i0=1,j0=1); i = 2;
+      for (j=1; j<lx; j++,i=1)
+      {
+        GEN c = gel(x,j);
+        for (; i<lx2; i++)
+          if (gcmp(gel(c,i),s) < 0) { s = gel(c,i); j0=j; i0=i; }
+      }
+      if (pi) *pi = mkvec2(utoipos(i0), utoipos(j0));
+      return gcopy(s);
+    }
+    case t_VECSMALL:
+      i = vecsmall_indexmin(x);
+      if (pi) *pi = utoipos(i);
+      return stoi(x[i]);
+    default:
+      return gcopy(x);
+  }
+}
+
+GEN
+vecmax(GEN x) { return vecmax0(x, NULL); }
+GEN
+vecmin(GEN x) { return vecmin0(x, NULL); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                      AFFECT long --> GEN                        */
+/*         affect long s to GEN x. Useful for initialization.      */
+/*                                                                 */
+/*******************************************************************/
+
+static void
+padicaff0(GEN x)
+{
+  if (signe(gel(x,4)))
+  {
+    x[1] = evalvalp(valp(x)+precp(x));
+    affsi(0,gel(x,4));
+  }
+}
+
+void
+gaffsg(long s, GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: affsi(s,x); break;
+    case t_REAL: affsr(s,x); break;
+    case t_INTMOD: modsiz(s,gel(x,1),gel(x,2)); break;
+    case t_FRAC: affsi(s,gel(x,1)); affsi(1,gel(x,2)); break;
+    case t_COMPLEX: gaffsg(s,gel(x,1)); gaffsg(0,gel(x,2)); break;
+    case t_PADIC: {
+      long vx;
+      GEN y;
+      if (!s) { padicaff0(x); break; }
+      vx = Z_pvalrem(stoi(s), gel(x,2), &y);
+      setvalp(x,vx); modiiz(y,gel(x,3),gel(x,4));
+      break;
+    }
+    case t_QUAD: gaffsg(s,gel(x,2)); gaffsg(0,gel(x,3)); break;
+    default: pari_err_TYPE2("=",stoi(s),x);
+  }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     GENERIC AFFECTATION                         */
+/*         Affect the content of x to y, whenever possible         */
+/*                                                                 */
+/*******************************************************************/
+/* x PADIC, Y INT, return lift(x * Mod(1,Y)) */
+GEN
+padic_to_Fp(GEN x, GEN Y) {
+  pari_sp av = avma;
+  GEN p = gel(x,2), z;
+  long vy, vx = valp(x);
+  if (!signe(Y)) pari_err_INV("padic_to_Fp",Y);
+  vy = Z_pvalrem(Y,p, &z);
+  if (vx < 0 || !gequal1(z)) pari_err_OP("",x, mkintmod(gen_1,Y));
+  if (vx >= vy) { avma = av; return gen_0; }
+  z = gel(x,4);
+  if (!signe(z) || vy > vx + precp(x)) pari_err_OP("",x, mkintmod(gen_1,Y));
+  if (vx) z = mulii(z, powiu(p,vx));
+  return gerepileuptoint(av, remii(z, Y));
+}
+ulong
+padic_to_Fl(GEN x, ulong Y) {
+  GEN p = gel(x,2);
+  ulong u, z;
+  long vy, vx = valp(x);
+  vy = u_pvalrem(Y,p, &u);
+  if (vx < 0 || u != 1) pari_err_OP("",x, mkintmodu(1,Y));
+  /* Y = p^vy */
+  if (vx >= vy) return 0;
+  z = umodiu(gel(x,4), Y);
+  if (!z || vy > vx + precp(x)) pari_err_OP("",x, mkintmodu(1,Y));
+  if (vx) {
+    ulong pp = p[2];
+    z = Fl_mul(z, upowuu(pp,vx), Y); /* p^vx < p^vy = Y */
+  }
+  return z;
+}
+
+static void
+croak(const char *s) {
+  char *t;
+  t = stack_sprintf("gaffect [overwriting universal object: %s]",s);
+  pari_err_BUG(t);
+}
+
+void
+gaffect(GEN x, GEN y)
+{
+  long vx, i, lx, ly, tx = typ(x), ty = typ(y);
+  pari_sp av;
+  GEN p1, num, den;
+
+  if (tx == ty) switch(tx) {
+    case t_INT:
+      if (!is_universal_constant(y)) { affii(x,y); return; }
+      /* y = gen_0, gnil, gen_1 or gen_2 */
+      if (y==gen_0)  croak("gen_0");
+      if (y==gen_1)  croak("gen_1");
+      if (y==gen_m1) croak("gen_m1");
+      if (y==gen_m2) croak("gen_m2");
+      if (y==gen_2)  croak("gen_2");
+      croak("gnil)");
+    case t_REAL: affrr(x,y); return;
+    case t_INTMOD:
+      if (!dvdii(gel(x,1),gel(y,1))) pari_err_OP("",x,y);
+      modiiz(gel(x,2),gel(y,1),gel(y,2)); return;
+    case t_FRAC:
+      affii(gel(x,1),gel(y,1));
+      affii(gel(x,2),gel(y,2)); return;
+    case t_COMPLEX:
+      gaffect(gel(x,1),gel(y,1));
+      gaffect(gel(x,2),gel(y,2)); return;
+    case t_PADIC:
+      if (!equalii(gel(x,2),gel(y,2))) pari_err_OP("",x,y);
+      modiiz(gel(x,4),gel(y,3),gel(y,4));
+      setvalp(y,valp(x)); return;
+    case t_QUAD:
+      if (! ZX_equal(gel(x,1),gel(y,1))) pari_err_OP("",x,y);
+      affii(gel(x,2),gel(y,2));
+      affii(gel(x,3),gel(y,3)); return;
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x); if (lx != lg(y)) pari_err_DIM("gaffect");
+      for (i=1; i<lx; i++) gaffect(gel(x,i),gel(y,i));
+      return;
+  }
+
+  /* Various conversions. Avoid them, use specialized routines ! */
+
+  if (!is_const_t(ty)) pari_err_TYPE2("=",x,y);
+  switch(tx)
+  {
+    case t_INT:
+      switch(ty)
+      {
+        case t_REAL:
+          affir(x,y); break;
+
+        case t_INTMOD:
+          modiiz(x,gel(y,1),gel(y,2)); break;
+
+        case t_COMPLEX:
+          gaffect(x,gel(y,1)); gaffsg(0,gel(y,2)); break;
+
+        case t_PADIC:
+          if (!signe(x)) { padicaff0(y); break; }
+          av = avma;
+          setvalp(y, Z_pvalrem(x,gel(y,2),&p1));
+          affii(modii(p1,gel(y,3)), gel(y,4));
+          avma = av; break;
+
+        case t_QUAD: gaffect(x,gel(y,2)); gaffsg(0,gel(y,3)); break;
+        default: pari_err_TYPE2("=",x,y);
+      }
+      break;
+
+    case t_REAL:
+      switch(ty)
+      {
+        case t_COMPLEX: gaffect(x,gel(y,1)); gaffsg(0,gel(y,2)); break;
+        default: pari_err_TYPE2("=",x,y);
+      }
+      break;
+
+    case t_FRAC:
+      switch(ty)
+      {
+        case t_REAL: rdiviiz(gel(x,1),gel(x,2), y); break;
+        case t_INTMOD: av = avma;
+          p1 = Fp_inv(gel(x,2),gel(y,1));
+          affii(modii(mulii(gel(x,1),p1),gel(y,1)), gel(y,2));
+          avma = av; break;
+        case t_COMPLEX: gaffect(x,gel(y,1)); gaffsg(0,gel(y,2)); break;
+        case t_PADIC:
+          if (!signe(gel(x,1))) { padicaff0(y); break; }
+          num = gel(x,1);
+          den = gel(x,2);
+          av = avma; vx = Z_pvalrem(num, gel(y,2), &num);
+          if (!vx) vx = -Z_pvalrem(den,gel(y,2),&den);
+          setvalp(y,vx);
+          p1 = mulii(num,Fp_inv(den,gel(y,3)));
+          affii(modii(p1,gel(y,3)), gel(y,4)); avma = av; break;
+        case t_QUAD: gaffect(x,gel(y,2)); gaffsg(0,gel(y,3)); break;
+        default: pari_err_TYPE2("=",x,y);
+      }
+      break;
+
+    case t_COMPLEX:
+      if (!gequal0(gel(x,2))) pari_err_TYPE2("=",x,y);
+      gaffect(gel(x,1), y);
+      break;
+
+    case t_PADIC:
+      switch(ty)
+      {
+        case t_INTMOD:
+          av = avma; affii(padic_to_Fp(x, gel(y,1)), gel(y,2));
+          avma = av; break;
+        default: pari_err_TYPE2("=",x,y);
+      }
+      break;
+
+    case t_QUAD:
+      switch(ty)
+      {
+        case t_INT: case t_INTMOD: case t_FRAC: case t_PADIC:
+          pari_err_TYPE2("=",x,y);
+
+        case t_REAL:
+          av = avma; affgr(quadtofp(x,realprec(y)), y); avma = av; break;
+        case t_COMPLEX:
+          ly = precision(y); if (!ly) pari_err_TYPE2("=",x,y);
+          av = avma; gaffect(quadtofp(x,ly), y); avma = av; break;
+        default: pari_err_TYPE2("=",x,y);
+      }
+    default: pari_err_TYPE2("=",x,y);
+  }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*           CONVERSION QUAD --> REAL, COMPLEX OR P-ADIC           */
+/*                                                                 */
+/*******************************************************************/
+GEN
+quadtofp(GEN x, long prec)
+{
+  GEN z, Q, u = gel(x,2), v = gel(x,3);
+  pari_sp av;
+  if (prec < LOWDEFAULTPREC) prec = LOWDEFAULTPREC;
+  if (isintzero(v)) return cxcompotor(u, prec);
+  av = avma; Q = gel(x,1);
+  z = itor(quad_disc(x), prec);
+  if (signe(gel(Q,2)) < 0) /* Q[2] = -D/4 or (1-D)/4 */
+  {
+    z = subri(sqrtr(z), gel(Q,3));
+    shiftr_inplace(z, -1);
+  }
+  else
+  {
+    z = sqrtr_abs(z); shiftr_inplace(z, -1);
+    z = mkcomplex(gmul2n(negi(gel(Q,3)),-1), z);
+  }/* z = (-b + sqrt(D)) / 2 */
+  return gerepileupto(av, gadd(u, gmul(v,z)));
+}
+
+static GEN
+qtop(GEN x, GEN p, long d)
+{
+  GEN z, D, P, b, u = gel(x,2), v = gel(x,3);
+  pari_sp av;
+  if (gequal0(v)) return cvtop(u, p, d);
+  P = gel(x,1);
+  b = gel(P,3);
+  av = avma; D = quad_disc(x);
+  if (equaliu(p,2)) d += 2;
+  z = Qp_sqrt(cvtop(D,p,d));
+  if (!z) pari_err_SQRTN("Qp_sqrt",D);
+  z = gmul2n(gsub(z, b), -1);
+
+  z = gadd(u, gmul(v, z));
+  if (typ(z) != t_PADIC) /* t_INTMOD for t_QUAD of t_INTMODs... */
+    z = cvtop(z, p, d);
+  return gerepileupto(av, z);
+}
+static GEN
+ctop(GEN x, GEN p, long d)
+{
+  pari_sp av = avma;
+  GEN z, u = gel(x,1), v = gel(x,2);
+  if (isrationalzero(v)) return cvtop(u, p, d);
+  z = Qp_sqrt(cvtop(gen_m1, p, d - gvaluation(v, p))); /* = I */
+
+  z = gadd(u, gmul(v, z));
+  if (typ(z) != t_PADIC) /* t_INTMOD for t_COMPLEX of t_INTMODs... */
+    z = cvtop(z, p, d);
+  return gerepileupto(av, z);
+}
+
+/* cvtop2(stoi(s), y) */
+GEN
+cvstop2(long s, GEN y)
+{
+  GEN z, p = gel(y,2);
+  long v, d = signe(gel(y,4))? precp(y): 0;
+  if (!s) return zeropadic(p, d);
+  v = z_pvalrem(s, p, &s);
+  if (d <= 0) return zeropadic(p, v);
+  z = cgetg(5, t_PADIC);
+  z[1] = evalprecp(d) | evalvalp(v);
+  gel(z,2) = p;
+  gel(z,3) = gel(y,3);
+  gel(z,4) = modsi(s, gel(y,3)); return z;
+}
+
+/* cvtop(x, gel(y,2), precp(y)), internal, not memory-clean */
+GEN
+cvtop2(GEN x, GEN y)
+{
+  GEN z, p = gel(y,2);
+  long v, d = signe(gel(y,4))? precp(y): 0;
+  switch(typ(x))
+  {
+    case t_INT:
+      if (!signe(x)) return zeropadic(p, d);
+      v = Z_pvalrem(x, p, &x);
+      if (d <= 0) return zeropadic(p, v);
+      z = cgetg(5, t_PADIC);
+      z[1] = evalprecp(d) | evalvalp(v);
+      gel(z,2) = p;
+      gel(z,3) = gel(y,3);
+      gel(z,4) = modii(x, gel(y,3)); return z;
+
+    case t_INTMOD:
+      v = Z_pval(gel(x,1),p); if (v > d) v = d;
+      return cvtop(gel(x,2), p, v);
+
+    case t_FRAC: { GEN num = gel(x,1), den = gel(x,2);
+      if (!signe(num)) return zeropadic(p, d);
+      v = Z_pvalrem(num, p, &num);
+      if (!v) v = -Z_pvalrem(den, p, &den); /* assume (num,den) = 1 */
+      if (d <= 0) return zeropadic(p, v);
+      z = cgetg(5, t_PADIC);
+      z[1] = evalprecp(d) | evalvalp(v);
+      gel(z,2) = p;
+      gel(z,3) = gel(y,3);
+      if (!is_pm1(den)) num = mulii(num, Fp_inv(den, gel(y,3)));
+      gel(z,4) = modii(num, gel(y,3)); return z;
+    }
+    case t_COMPLEX: return ctop(x, p, d);
+    case t_QUAD:    return qtop(x, p, d);
+  }
+  pari_err_TYPE("cvtop2",x);
+  return NULL; /* not reached */
+}
+
+/* assume is_const_t(tx) */
+GEN
+cvtop(GEN x, GEN p, long d)
+{
+  GEN z;
+  long v;
+
+  if (typ(p) != t_INT) pari_err_TYPE("cvtop",p);
+  switch(typ(x))
+  {
+    case t_INT:
+      if (!signe(x)) return zeropadic(p, d);
+      v = Z_pvalrem(x, p, &x);
+      if (d <= 0) return zeropadic(p, v);
+      z = cgetg(5, t_PADIC);
+      z[1] = evalprecp(d) | evalvalp(v);
+      gel(z,2) = icopy(p);
+      gel(z,3) = powiu(p, d);
+      gel(z,4) = modii(x, gel(z,3)); return z; /* not memory-clean */
+
+    case t_INTMOD:
+      v = Z_pval(gel(x,1),p); if (v > d) v = d;
+      return cvtop(gel(x,2), p, v);
+
+    case t_FRAC: { GEN num = gel(x,1), den = gel(x,2);
+      if (!signe(num)) return zeropadic(p, d);
+      v = Z_pvalrem(num, p, &num);
+      if (!v) v = -Z_pvalrem(den, p, &den); /* assume (num,den) = 1 */
+      if (d <= 0) return zeropadic(p, v);
+      z = cgetg(5, t_PADIC);
+      z[1] = evalprecp(d) | evalvalp(v);
+      gel(z,2) = icopy(p);
+      gel(z,3) = powiu(p, d);
+      if (!is_pm1(den)) num = mulii(num, Fp_inv(den, gel(z,3)));
+      gel(z,4) = modii(num, gel(z,3)); return z; /* not memory-clean */
+    }
+    case t_COMPLEX: return ctop(x, p, d);
+    case t_PADIC: return gprec(x,d);
+    case t_QUAD: return qtop(x, p, d);
+  }
+  pari_err_TYPE("cvtop",x);
+  return NULL; /* not reached */
+}
+
+GEN
+gcvtop(GEN x, GEN p, long r)
+{
+  long i, lx;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_POL: case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = gcvtop(gel(x,i),p,r);
+      return y;
+    case t_POLMOD: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gcvtop(gel(x,i),p,r);
+      return y;
+  }
+  return cvtop(x,p,r);
+}
+
+long
+gexpo(GEN x)
+{
+  long tx = typ(x), lx, e, f, i;
+
+  switch(tx)
+  {
+    case t_INT:
+      return expi(x);
+
+    case t_FRAC:
+      return expi(gel(x,1)) - expi(gel(x,2));
+
+    case t_REAL:
+      return expo(x);
+
+    case t_COMPLEX:
+      e = gexpo(gel(x,1));
+      f = gexpo(gel(x,2)); return maxss(e, f);
+
+    case t_QUAD: {
+      GEN p = gel(x,1); /* mod = X^2 + {0,1}* X - {D/4, (1-D)/4})*/
+      long d = 1 + expi(gel(p,2))/2; /* ~ expo(sqrt(D)) */
+      e = gexpo(gel(x,2));
+      f = gexpo(gel(x,3)) + d; return maxss(e, f);
+    }
+    case t_POL: case t_SER:
+      lx = lg(x); f = -(long)HIGHEXPOBIT;
+      for (i=2; i<lx; i++) { e=gexpo(gel(x,i)); if (e>f) f=e; }
+      return f;
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x); f = -(long)HIGHEXPOBIT;
+      for (i=1; i<lx; i++) { e=gexpo(gel(x,i)); if (e>f) f=e; }
+      return f;
+  }
+  pari_err_TYPE("gexpo",x);
+  return 0; /* not reached */
+}
+
+long
+sizedigit(GEN x)
+{
+  return gequal0(x)? 0: (long) ((gexpo(x)+1) * LOG10_2) + 1;
+}
+
+/* normalize series. avma is not updated */
+GEN
+normalize(GEN x)
+{
+  long i, lx = lg(x), vx=varn(x), vp=valp(x);
+  GEN y, z;
+
+  if (typ(x) != t_SER) pari_err_TYPE("normalize",x);
+  if (lx==2) { setsigne(x,0); return x; }
+  for (i=2; i<lx; i++)
+    if (! isrationalzero(gel(x,i))) break;
+  if (i == lx) return zeroser(vx,lx-2+vp);
+  z = gel(x,i);
+  while (i<lx && isexactzero(gel(x,i))) i++;
+  if (i == lx)
+  {
+    i -= 3; y = x + i;
+    stackdummy((pari_sp)y, (pari_sp)x);
+    gel(y,2) = z;
+    y[1] = evalsigne(0) | evalvalp(lx-2+vp) | evalvarn(vx);
+    y[0] = evaltyp(t_SER) | _evallg(3);
+    return y;
+  }
+
+  i -= 2; y = x + i; lx -= i;
+  y[1] = evalsigne(1) | evalvalp(vp+i) | evalvarn(vx);
+  y[0] = evaltyp(t_SER) | evallg(lx);
+
+  stackdummy((pari_sp)y, (pari_sp)x);
+  for (i = 2; i < lx; i++)
+    if (!gequal0(gel(y, i))) return y;
+  setsigne(y, 0); return y;
+}
+
+GEN
+normalizepol_approx(GEN x, long lx)
+{
+  long i;
+  for (i = lx-1; i>1; i--)
+    if (! gequal0(gel(x,i))) break;
+  stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
+  setlg(x, i+1); setsigne(x, i!=1); return x;
+}
+
+GEN
+normalizepol_lg(GEN x, long lx)
+{
+  long i, LX = 0;
+  GEN KEEP = NULL;
+
+  for (i = lx-1; i>1; i--)
+  {
+    GEN z = gel(x,i);
+    if (! gequal0(z) ) {
+      if (!LX) LX = i+1;
+      stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + LX));
+      x[0] = evaltyp(t_POL) | evallg(LX);
+      setsigne(x,1); return x;
+    } else if (!isexactzero(z)) {
+      if (!LX) LX = i+1; /* to be kept as leading coeff */
+    } else if (!isrationalzero(z))
+      KEEP = z; /* to be kept iff all other coeffs are exact 0s */
+  }
+  if (!LX) {
+    if (KEEP) { /* e.g. Pol(Mod(0,2)) */
+      gel(x,2) = KEEP;
+      LX = 3;
+    } else
+      LX = 2; /* Pol(0) */
+  }
+  stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + LX));
+  x[0] = evaltyp(t_POL) | evallg(LX);
+  setsigne(x,0); return x;
+}
+
+/* normalize polynomial x in place */
+GEN
+normalizepol(GEN x)
+{
+  return normalizepol_lg(x, lg(x));
+}
+
+int
+gsigne(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: return signe(x);
+    case t_FRAC: return signe(gel(x,1));
+  }
+  pari_err_TYPE("gsigne",x);
+  return 0; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                              LISTS                              */
+/*                                                                 */
+/*******************************************************************/
+/* make sure L can hold l elements, at least doubling the previous max number
+ * of components. */
+static void
+ensure_nb(GEN L, long l)
+{
+  long nmax = list_nmax(L);
+  GEN v;
+  if (l <= nmax) return;
+  if (nmax)
+  {
+    nmax <<= 1;
+    if (l > nmax) nmax = l;
+    v = (GEN)pari_realloc(list_data(L), (nmax+1) * sizeof(long));
+  }
+  else /* unallocated */
+  {
+    nmax = 32;
+    if (list_data(L))
+      pari_err(e_MISC, "store list in variable before appending elements");
+    v = (GEN)pari_malloc((nmax+1) * sizeof(long));
+    v[0] = evaltyp(t_VEC) | _evallg(1);
+  }
+  list_data(L) = v;
+  list_nmax(L) = nmax;
+}
+
+void
+listkill(GEN L)
+{
+
+  if (typ(L) != t_LIST) pari_err_TYPE("listkill",L);
+  if (list_nmax(L)) {
+    GEN v = list_data(L);
+    long i, l = lg(v);
+    for (i=1; i<l; i++) gunclone_deep(gel(v,i));
+    pari_free(v);
+    list_nmax(L) = 0;
+    list_data(L) = NULL;
+  }
+}
+
+GEN
+listcreate(void)
+{
+  GEN L = cgetg(3,t_LIST);
+  list_nmax(L) = 0;
+  list_data(L) = NULL; return L;
+}
+
+GEN
+listput(GEN L, GEN x, long index)
+{
+  long l;
+  GEN z;
+
+  if (typ(L) != t_LIST) pari_err_TYPE("listput",L);
+  if (index < 0) pari_err_COMPONENT("listput", "<", gen_0, stoi(index));
+  z = list_data(L);
+  l = z? lg(z): 1;
+
+  if (!index || index >= l)
+  {
+    ensure_nb(L, l);
+    z = list_data(L); /* it may change ! */
+    index = l;
+    l++;
+  } else
+    gunclone_deep( gel(z, index) );
+  gel(z,index) = gclone(x);
+  z[0] = evaltyp(t_VEC) | evallg(l); /*must be after gel(z,index) is set*/
+  return gel(z,index);
+}
+
+GEN
+listinsert(GEN L, GEN x, long index)
+{
+  long l, i;
+  GEN z;
+
+  if (typ(L) != t_LIST) pari_err_TYPE("listinsert",L);
+  z = list_data(L); l = z? lg(z): 1;
+  if (index <= 0) pari_err_COMPONENT("listinsert", "<=", gen_0, stoi(index));
+  if (index > l) pari_err_COMPONENT("listinsert", ">", stoi(l), stoi(index));
+  ensure_nb(L, l);
+  z = list_data(L);
+  for (i=l; i > index; i--) gel(z,i) = gel(z,i-1);
+  z[0] = evaltyp(t_VEC) | evallg(l+1);
+  return gel(z,index) = gclone(x);
+}
+
+void
+listpop(GEN L, long index)
+{
+  long l, i;
+  GEN z;
+
+  if (typ(L) != t_LIST) pari_err_TYPE("listinsert",L);
+  if (index < 0) pari_err_COMPONENT("listpop", "<", gen_0, stoi(index));
+  z = list_data(L);
+  if (!z || (l = lg(z)-1) == 0) return;
+
+  if (!index || index > l) index = l;
+  gunclone_deep( gel(z, index) );
+  z[0] = evaltyp(t_VEC) | evallg(l);
+  for (i=index; i < l; i++) z[i] = z[i+1];
+}
+
+/* return a list with single element x, allocated on stack */
+GEN
+mklistcopy(GEN x)
+{
+  GEN y = listcreate();
+  list_data(y) = mkveccopy(x);
+  return y;
+}
+
+/* return a copy fully allocated on stack. gclone from changevalue is
+ * supposed to malloc() it */
+GEN
+gtolist(GEN x)
+{
+  GEN y;
+
+  if (!x) return listcreate();
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL:
+      y = listcreate();
+      if (lg(x) == 1) return y;
+      list_data(y) = gcopy(x);
+      settyp(list_data(y), t_VEC);
+      return y;
+    case t_LIST:
+      y = listcreate();
+      list_data(y) = list_data(x)? gcopy(list_data(x)): NULL;
+      return y;
+    default:
+      return mklistcopy(x);
+  }
+}
+
+void
+listsort(GEN L, long flag)
+{
+  long i, l;
+  pari_sp av = avma;
+  GEN perm, v, vnew;
+
+  if (typ(L) != t_LIST) pari_err_TYPE("listsort",L);
+  v = list_data(L); l = v? lg(v): 1;
+  if (l < 3) return;
+  if (flag)
+  {
+    long lnew;
+    perm = gen_indexsort_uniq(L, (void*)&cmp_universal, cmp_nodata);
+    lnew = lg(perm); /* may have changed since 'uniq' */
+    vnew = cgetg(lnew,t_VEC);
+    for (i=1; i<lnew; i++) {
+      long c = perm[i];
+      gel(vnew,i) = gel(v,c);
+      gel(v,c) = NULL;
+    }
+    if (l != lnew) { /* was shortened */
+      for (i=1; i<l; i++)
+        if (gel(v,i)) gunclone_deep(gel(v,i));
+      l = lnew;
+    }
+  }
+  else
+  {
+    perm = gen_indexsort(L, (void*)&cmp_universal, cmp_nodata);
+    vnew = cgetg(l,t_VEC);
+    for (i=1; i<l; i++) gel(vnew,i) = gel(v,perm[i]);
+  }
+  for (i=1; i<l; i++) gel(v,i) = gel(vnew,i);
+  v[0] = vnew[0]; avma = av;
+}
diff --git a/src/basemath/gen3.c b/src/basemath/gen3.c
new file mode 100644
index 0000000..d4f66b1
--- /dev/null
+++ b/src/basemath/gen3.c
@@ -0,0 +1,3939 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                      GENERIC OPERATIONS                        **/
+/**                         (third part)                           **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/**                                                                **/
+/**                 PRINCIPAL VARIABLE NUMBER                      **/
+/**                                                                **/
+/********************************************************************/
+long
+gvar(GEN x)
+{
+  long i, v, w, lx;
+  switch(typ(x))
+  {
+    case t_POL: case t_SER: return varn(x);
+    case t_POLMOD: return varn(gel(x,1));
+    case t_RFRAC:  return varn(gel(x,2));
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x); break;
+    case t_LIST:
+      x = list_data(x);
+      lx = x? lg(x): 1; break;
+    default:
+      return NO_VARIABLE;
+  }
+  v = NO_VARIABLE;
+  for (i=1; i < lx; i++) { w = gvar(gel(x,i)); if (varncmp(w,v) < 0) v = w; }
+  return v;
+}
+/* T main polynomial in R[X], A auxiliary in R[X] (possibly degree 0).
+ * Guess and return the main variable of R */
+static long
+var2_aux(GEN T, GEN A)
+{
+  long a = gvar2(T);
+  long b = (typ(A) == t_POL && varn(A) == varn(T))? gvar2(A): gvar(A);
+  if (varncmp(a, b) > 0) a = b;
+  return a;
+}
+static long
+var2_rfrac(GEN x)  { return var2_aux(gel(x,2), gel(x,1)); }
+static long
+var2_polmod(GEN x) { return var2_aux(gel(x,1), gel(x,2)); }
+
+/* main variable of x, with the convention that the "natural" main
+ * variable of a POLMOD is mute, so we want the next one. */
+static long
+gvar9(GEN x)
+{ return (typ(x) == t_POLMOD)? var2_polmod(x): gvar(x); }
+
+/* main variable of the ring over wich x is defined */
+long
+gvar2(GEN x)
+{
+  long i, v, w;
+  switch(typ(x))
+  {
+    case t_POLMOD:
+      return var2_polmod(x);
+    case t_POL: case t_SER:
+      v = NO_VARIABLE;
+      for (i=2; i < lg(x); i++) {
+        w = gvar9(gel(x,i));
+        if (varncmp(w,v) < 0) v=w;
+      }
+      return v;
+    case t_RFRAC:
+      return var2_rfrac(x);
+    case t_VEC: case t_COL: case t_MAT:
+      v = NO_VARIABLE;
+      for (i=1; i < lg(x); i++) {
+        w = gvar2(gel(x,i));
+        if (varncmp(w,v)<0) v=w;
+      }
+      return v;
+  }
+  return NO_VARIABLE;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    PRECISION OF SCALAR OBJECTS                  */
+/*                                                                 */
+/*******************************************************************/
+static long
+prec0(long e) { return (e < 0)? nbits2prec(-e): 2; }
+static long
+precREAL(GEN x) { return signe(x) ? realprec(x): prec0(expo(x)); }
+/* x t_REAL, y an exact non-complex type. Return precision(|x| + |y|) */
+static long
+precrealexact(GEN x, GEN y)
+{
+  long lx, ey = gexpo(y), ex, e;
+  if (ey == -(long)HIGHEXPOBIT) return precREAL(x);
+  ex = expo(x);
+  e = ey - ex;
+  if (!signe(x)) return prec0((e >= 0)? -e: ex);
+  lx = realprec(x);
+  return (e > 0)? lx + nbits2extraprec(e): lx;
+}
+static long
+precCOMPLEX(GEN z)
+{ /* ~ precision(|x| + |y|) */
+  GEN x = gel(z,1), y = gel(z,2);
+  long e, ex, ey, lz, lx, ly;
+  if (typ(x) != t_REAL) {
+    if (typ(y) != t_REAL) return 0;
+    return precrealexact(y, x);
+  }
+  if (typ(y) != t_REAL) return precrealexact(x, y);
+  /* x, y are t_REALs, cf addrr_sign */
+  ex = expo(x);
+  ey = expo(y);
+  e = ey - ex;
+  if (!signe(x)) {
+    if (!signe(y)) return prec0( minss(ex,ey) );
+    if (e <= 0) return prec0(ex);
+    lz = nbits2prec(e);
+    ly = realprec(y); if (lz > ly) lz = ly;
+    return lz;
+  }
+  if (!signe(y)) {
+    if (e >= 0) return prec0(ey);
+    lz = nbits2prec(-e);
+    lx = realprec(x); if (lz > lx) lz = lx;
+    return lz;
+  }
+  if (e < 0) { swap(x, y); e = -e; }
+  lx = realprec(x);
+  ly = realprec(y);
+  if (e) {
+    long d = nbits2extraprec(e), l = ly-d;
+    return (l > lx)? lx + d: ly;
+  }
+  return minss(lx, ly);
+}
+long
+precision(GEN z)
+{
+  switch(typ(z))
+  {
+    case t_REAL: return precREAL(z);
+    case t_COMPLEX: return precCOMPLEX(z);
+  }
+  return 0;
+}
+
+long
+gprecision(GEN x)
+{
+  long i, k, l;
+
+  switch(typ(x))
+  {
+    case t_REAL: return precREAL(x);
+    case t_COMPLEX: return precCOMPLEX(x);
+    case t_INT: case t_INTMOD: case t_FRAC: case t_FFELT:
+    case t_PADIC: case t_QUAD: case t_POLMOD:
+      return 0;
+
+    case t_POL:
+      k = LONG_MAX;
+      for (i=lg(x)-1; i>1; i--)
+      {
+        l = gprecision(gel(x,i));
+        if (l && l<k) k = l;
+      }
+      return (k==LONG_MAX)? 0: k;
+    case t_VEC: case t_COL: case t_MAT:
+      k = LONG_MAX;
+      for (i=lg(x)-1; i>0; i--)
+      {
+        l = gprecision(gel(x,i));
+        if (l && l<k) k = l;
+      }
+      return (k==LONG_MAX)? 0: k;
+
+    case t_RFRAC:
+    {
+      k=gprecision(gel(x,1));
+      l=gprecision(gel(x,2)); if (l && (!k || l<k)) k=l;
+      return k;
+    }
+    case t_QFR:
+      return gprecision(gel(x,4));
+  }
+  return 0;
+}
+
+GEN
+precision0(GEN x, long n)
+{
+  long a;
+  if (n) return gprec(x,n);
+  a = gprecision(x);
+  return utoi(a ? prec2ndec(a): LONG_MAX);
+}
+
+static long
+vec_padicprec_relative(GEN x, long imin)
+{
+  long s, t, i;
+  for (s=LONG_MAX, i=lg(x)-1; i>=imin; i--)
+  {
+    t = padicprec_relative(gel(x,i)); if (t<s) s = t;
+  }
+  return s;
+}
+/* RELATIVE padic precision. Only accept decent types: don't try to make sense
+ * of everything like padicprec */
+long
+padicprec_relative(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC:
+      return LONG_MAX;
+    case t_PADIC:
+      return signe(gel(x,4))? precp(x): 0;
+    case t_POLMOD: case t_VEC: case t_COL: case t_MAT:
+      return vec_padicprec_relative(x, 1);
+    case t_POL: case t_SER:
+      return vec_padicprec_relative(x, 2);
+  }
+  pari_err_TYPE("padicprec_relative",x);
+  return 0; /* not reached */
+}
+
+static long
+vec_padicprec(GEN x, GEN p, long imin)
+{
+  long s, t, i;
+  for (s=LONG_MAX, i=lg(x)-1; i>=imin; i--)
+  {
+    t = padicprec(gel(x,i),p); if (t<s) s = t;
+  }
+  return s;
+}
+
+/* ABSOLUTE padic precision */
+long
+padicprec(GEN x, GEN p)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC:
+      return LONG_MAX;
+
+    case t_INTMOD:
+      return Z_pval(gel(x,1),p);
+
+    case t_PADIC:
+      if (!equalii(gel(x,2),p)) pari_err_MODULUS("padicprec", gel(x,2), p);
+      return precp(x)+valp(x);
+
+    case t_POL: case t_SER:
+      return vec_padicprec(x, p, 2);
+    case t_COMPLEX: case t_QUAD: case t_POLMOD: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      return vec_padicprec(x, p, 1);
+  }
+  pari_err_TYPE("padicprec",x);
+  return 0; /* not reached */
+}
+
+/* Degree of x (scalar, t_POL, t_RFRAC) wrt variable v if v >= 0,
+ * wrt to main variable if v < 0. */
+long
+poldegree(GEN x, long v)
+{
+  const long DEGREE0 = -LONG_MAX;
+  long tx = typ(x), lx,w,i,d;
+
+  if (is_scalar_t(tx)) return gequal0(x)? DEGREE0: 0;
+  switch(tx)
+  {
+    case t_POL:
+      if (!signe(x)) return DEGREE0;
+      w = varn(x);
+      if (v < 0 || v == w) return degpol(x);
+      if (varncmp(v, w) < 0) return 0;
+      lx = lg(x); d = DEGREE0;
+      for (i=2; i<lx; i++)
+      {
+        long e = poldegree(gel(x,i), v);
+        if (e > d) d = e;
+      }
+      return d;
+
+    case t_RFRAC:
+      if (gequal0(gel(x,1))) return DEGREE0;
+      return poldegree(gel(x,1),v) - poldegree(gel(x,2),v);
+  }
+  pari_err_TYPE("degree",x);
+  return 0; /* not reached  */
+}
+
+/* assume v >= 0 and x is a POLYNOMIAL in v, return deg_v(x) */
+long
+RgX_degree(GEN x, long v)
+{
+  long tx = typ(x), lx, w, i, d;
+
+  if (is_scalar_t(tx)) return gequal0(x)? -1: 0;
+  switch(tx)
+  {
+    case t_POL:
+      if (!signe(x)) return -1;
+      w = varn(x);
+      if (v == w) return degpol(x);
+      if (varncmp(v, w) < 0) return 0;
+      lx = lg(x); d = -1;
+      for (i=2; i<lx; i++)
+      {
+        long e = RgX_degree(gel(x,i), v);
+        if (e > d) d = e;
+      }
+      return d;
+
+    case t_RFRAC:
+      w = varn(gel(x,2));
+      if (varncmp(v, w) < 0) return 0;
+      if (RgX_degree(gel(x,2),v)) pari_err_TYPE("RgX_degree", x);
+      return RgX_degree(gel(x,1),v);
+  }
+  pari_err_TYPE("RgX_degree",x);
+  return 0; /* not reached  */
+}
+
+long
+degree(GEN x)
+{
+  return poldegree(x,-1);
+}
+
+/* If v<0, leading coeff with respect to the main variable, otherwise wrt v. */
+GEN
+pollead(GEN x, long v)
+{
+  long l, tx = typ(x), w;
+  pari_sp av;
+  GEN xinit;
+
+  if (is_scalar_t(tx)) return gcopy(x);
+  w = varn(x);
+  switch(tx)
+  {
+    case t_POL:
+      if (v < 0 || v == w)
+      {
+        l=lg(x);
+        return (l==2)? gen_0: gcopy(gel(x,l-1));
+      }
+      break;
+
+    case t_SER:
+      if (v < 0 || v == w) return signe(x)? gcopy(gel(x,2)): gen_0;
+      break;
+
+    default:
+      pari_err_TYPE("pollead",x);
+      return NULL; /* not reached */
+  }
+  if (v < w) return gcopy(x);
+  av = avma; xinit = x;
+  x = gsubst(gsubst(x,w,pol_x(MAXVARN)),v,pol_x(0));
+  if (gvar(x)) { avma = av; return gcopy(xinit);}
+  tx = typ(x);
+  if (tx == t_POL) {
+    l = lg(x); if (l == 2) { avma = av; return gen_0; }
+    x = gel(x,l-1);
+  }
+  else if (tx == t_SER) {
+    if (!signe(x)) { avma = av; return gen_0;}
+    x = gel(x,2);
+  } else pari_err_TYPE("pollead",x);
+  return gerepileupto(av, gsubst(x,MAXVARN,pol_x(w)));
+}
+
+/* returns 1 if there's a real component in the structure, 0 otherwise */
+int
+isinexactreal(GEN x)
+{
+  long i;
+  switch(typ(x))
+  {
+    case t_REAL: return 1;
+    case t_COMPLEX: return (typ(gel(x,1))==t_REAL || typ(gel(x,2))==t_REAL);
+
+    case t_INT: case t_INTMOD: case t_FRAC:
+    case t_FFELT: case t_PADIC: case t_QUAD:
+    case t_QFR: case t_QFI: return 0;
+
+    case t_RFRAC: case t_POLMOD:
+      return isinexactreal(gel(x,1)) || isinexactreal(gel(x,2));
+
+    case t_POL: case t_SER:
+      for (i=lg(x)-1; i>1; i--)
+        if (isinexactreal(gel(x,i))) return 1;
+      return 0;
+
+    case t_VEC: case t_COL: case t_MAT:
+      for (i=lg(x)-1; i>0; i--)
+        if (isinexactreal(gel(x,i))) return 1;
+      return 0;
+    default: return 0;
+  }
+}
+/* Check if x is approximately real with precision e */
+int
+isrealappr(GEN x, long e)
+{
+  long i;
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return 1;
+    case t_COMPLEX:
+      return (gexpo(gel(x,2)) < e);
+
+    case t_POL: case t_SER:
+      for (i=lg(x)-1; i>1; i--)
+        if (! isrealappr(gel(x,i),e)) return 0;
+      return 1;
+
+    case t_RFRAC: case t_POLMOD:
+      return isrealappr(gel(x,1),e) && isrealappr(gel(x,2),e);
+
+    case t_VEC: case t_COL: case t_MAT:
+      for (i=lg(x)-1; i>0; i--)
+        if (! isrealappr(gel(x,i),e)) return 0;
+      return 1;
+    default: pari_err_TYPE("isrealappr",x); return 0;
+  }
+}
+
+/* returns 1 if there's an inexact component in the structure, and
+ * 0 otherwise. */
+int
+isinexact(GEN x)
+{
+  long lx, i;
+
+  switch(typ(x))
+  {
+    case t_REAL: case t_PADIC: case t_SER:
+      return 1;
+    case t_INT: case t_INTMOD: case t_FFELT: case t_FRAC:
+    case t_QFR: case t_QFI:
+      return 0;
+    case t_COMPLEX: case t_QUAD: case t_RFRAC: case t_POLMOD:
+      return isinexact(gel(x,1)) || isinexact(gel(x,2));
+    case t_POL:
+      for (i=lg(x)-1; i>1; i--)
+        if (isinexact(gel(x,i))) return 1;
+      return 0;
+    case t_VEC: case t_COL: case t_MAT:
+      for (i=lg(x)-1; i>0; i--)
+        if (isinexact(gel(x,i))) return 1;
+      return 0;
+    case t_LIST:
+      x = list_data(x); lx = x? lg(x): 1;
+      for (i=1; i<lx; i++)
+        if (isinexact(gel(x,i))) return 1;
+      return 0;
+  }
+  return 0;
+}
+
+int
+isrationalzeroscalar(GEN g)
+{
+  switch (typ(g))
+  {
+    case t_INT:     return !signe(g);
+    case t_COMPLEX: return isintzero(gel(g,1)) && isintzero(gel(g,2));
+    case t_QUAD:    return isintzero(gel(g,2)) && isintzero(gel(g,3));
+  }
+  return 0;
+}
+
+int
+iscomplex(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return 0;
+    case t_COMPLEX:
+      return !gequal0(gel(x,2));
+    case t_QUAD:
+      return signe(gmael(x,1,2)) > 0;
+  }
+  pari_err_TYPE("iscomplex",x);
+  return 0; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    GENERIC REMAINDER                            */
+/*                                                                 */
+/*******************************************************************/
+/* euclidean quotient for scalars of admissible types */
+static GEN
+_quot(GEN x, GEN y)
+{
+  GEN q = gdiv(x,y), f = gfloor(q);
+  if (gsigne(y) < 0 && !gequal(f,q)) f = gaddgs(f, 1);
+  return f;
+}
+/* y t_REAL, x \ y */
+static GEN
+_quotsr(long x, GEN y)
+{
+  GEN q, f;
+  if (!x) return gen_0;
+  q = divsr(x,y); f = floorr(q);
+  if (signe(y) < 0 && signe(subir(f,q))) f = addiu(f, 1);
+  return f;
+}
+/* x t_REAL, x \ y */
+static GEN
+_quotrs(GEN x, long y)
+{
+  GEN q = divrs(x,y), f = floorr(q);
+  if (y < 0 && signe(subir(f,q))) f = addiu(f, 1);
+  return f;
+}
+static GEN
+_quotri(GEN x, GEN y)
+{
+  GEN q = divri(x,y), f = floorr(q);
+  if (signe(y) < 0 && signe(subir(f,q))) f = addiu(f, 1);
+  return f;
+}
+
+/* y t_FRAC, x \ y */
+static GEN
+_quotsf(long x, GEN y)
+{ return truedivii(mulis(gel(y,2),x), gel(y,1)); }
+/* x t_FRAC, x \ y */
+static GEN
+_quotfs(GEN x, long y)
+{ return truedivii(gel(x,1),mulis(gel(x,2),y)); }
+/* x t_FRAC, y t_INT, x \ y */
+static GEN
+_quotfi(GEN x, GEN y)
+{ return truedivii(gel(x,1),mulii(gel(x,2),y)); }
+
+static GEN
+quot(GEN x, GEN y)
+{ pari_sp av = avma; return gerepileupto(av, _quot(x, y)); }
+static GEN
+quotrs(GEN x, long y)
+{ pari_sp av = avma; return gerepileuptoleaf(av, _quotrs(x,y)); }
+static GEN
+quotfs(GEN x, long s)
+{ pari_sp av = avma; return gerepileuptoleaf(av, _quotfs(x,s)); }
+static GEN
+quotsr(long x, GEN y)
+{ pari_sp av = avma; return gerepileuptoleaf(av, _quotsr(x, y)); }
+static GEN
+quotsf(long x, GEN y)
+{ pari_sp av = avma; return gerepileuptoleaf(av, _quotsf(x, y)); }
+static GEN
+quotfi(GEN x, GEN y)
+{ pari_sp av = avma; return gerepileuptoleaf(av, _quotfi(x, y)); }
+static GEN
+quotri(GEN x, GEN y)
+{ pari_sp av = avma; return gerepileuptoleaf(av, _quotri(x, y)); }
+
+static GEN
+modrs(GEN x, long y)
+{
+  pari_sp av = avma;
+  GEN q = _quotrs(x,y);
+  if (!signe(q)) { avma = av; return rcopy(x); }
+  return gerepileuptoleaf(av, subri(x, mulis(q,y)));
+}
+static GEN
+modsr(long x, GEN y)
+{
+  pari_sp av = avma;
+  GEN q = _quotsr(x,y);
+  if (!signe(q)) { avma = av; return stoi(x); }
+  return gerepileuptoleaf(av, subsr(x, mulir(q,y)));
+}
+static GEN
+modsf(long x, GEN y)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gred_frac2(modii(mulis(gel(y,2),x), gel(y,1)), gel(y,2)));
+}
+
+/* assume y a t_REAL, x a t_INT, t_FRAC or t_REAL.
+ * Return x mod y or NULL if accuracy error */
+GEN
+modr_safe(GEN x, GEN y)
+{
+  GEN q, f;
+  long e;
+  if (typ(x) == t_INT && !signe(x)) return gen_0;
+  q = gdiv(x,y); /* t_REAL */
+
+  e = expo(q);
+  if (e >= 0 && nbits2prec(e+1) > realprec(q)) return NULL;
+  f = floorr(q);
+  if (gsigne(y) < 0 && signe(subri(q,f))) f = addis(f, 1);
+  return signe(f)? gsub(x, mulir(f,y)): x;
+}
+
+GEN
+gmod(GEN x, GEN y)
+{
+  pari_sp av;
+  long i, lx, ty, tx;
+  GEN z;
+
+  tx = typ(x); if (tx == t_INT && !is_bigint(x)) return gmodsg(itos(x),y);
+  ty = typ(y); if (ty == t_INT && !is_bigint(y)) return gmodgs(x,itos(y));
+  if (is_matvec_t(tx))
+  {
+    z = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(z,i) = gmod(gel(x,i),y);
+    return z;
+  }
+  if (tx == t_POL || ty == t_POL) return grem(x,y);
+  if (!is_scalar_t(tx) || !is_scalar_t(ty)) pari_err_TYPE2("%",x,y);
+  switch(ty)
+  {
+    case t_INT:
+      switch(tx)
+      {
+        case t_INT: return modii(x,y);
+        case t_INTMOD: z=cgetg(3, t_INTMOD);
+          gel(z,1) = gcdii(gel(x,1),y);
+          gel(z,2) = modii(gel(x,2),gel(z,1)); return z;
+        case t_FRAC: return Fp_div(gel(x,1),gel(x,2),y);
+        case t_QUAD: z=cgetg(4,t_QUAD);
+          gel(z,1) = ZX_copy(gel(x,1));
+          gel(z,2) = gmod(gel(x,2),y);
+          gel(z,3) = gmod(gel(x,3),y); return z;
+        case t_PADIC: return padic_to_Fp(x, y);
+        case t_REAL: /* NB: conflicting semantic with lift(x * Mod(1,y)). */
+          av = avma;
+          return gerepileuptoleaf(av, mpsub(x, mpmul(_quot(x,y),y)));
+        default: pari_err_TYPE2("%",x,y);
+      }
+    case t_REAL: case t_FRAC:
+      switch(tx)
+      {
+        case t_INT: case t_REAL: case t_FRAC:
+          av = avma;
+          return gerepileupto(av, gadd(x, gneg(gmul(_quot(x,y),y))));
+        default: pari_err_TYPE2("%",x,y);
+      }
+  }
+  pari_err_TYPE2("%",x,y);
+  return NULL; /* not reached */
+}
+
+GEN
+gmodgs(GEN x, long y)
+{
+  ulong u;
+  long i, lx, tx = typ(x);
+  GEN z;
+  if (is_matvec_t(tx))
+  {
+    z = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(z,i) = gmodgs(gel(x,i),y);
+    return z;
+  }
+  if (!y) pari_err_INV("gmodgs",gen_0);
+  switch(tx)
+  {
+    case t_INT: return modis(x,y);
+    case t_REAL: return modrs(x,y);
+
+    case t_INTMOD: z=cgetg(3, t_INTMOD);
+      u = (ulong)labs(y);
+      i = ugcd(umodiu(gel(x,1), u), u);
+      gel(z,1) = utoi(i);
+      gel(z,2) = modis(gel(x,2), i); return z;
+
+    case t_FRAC:
+      u = (ulong)labs(y);
+      return utoi( Fl_div(umodiu(gel(x,1), u),
+                          umodiu(gel(x,2), u), u) );
+
+    case t_QUAD: z=cgetg(4,t_QUAD);
+      gel(z,1) = ZX_copy(gel(x,1));
+      gel(z,2) = gmodgs(gel(x,2),y);
+      gel(z,3) = gmodgs(gel(x,3),y); return z;
+
+    case t_PADIC: return padic_to_Fp(x, stoi(y));
+    case t_POL: return scalarpol(RgX_get_0(x), varn(x));
+    case t_POLMOD: return gmul(gen_0,x);
+  }
+  pari_err_TYPE2("%",x,stoi(y));
+  return NULL; /* not reached */
+}
+GEN
+gmodsg(long x, GEN y)
+{
+  switch(typ(y))
+  {
+    case t_INT: return modsi(x,y);
+    case t_REAL: return modsr(x,y);
+    case t_FRAC: return modsf(x,y);
+    case t_POL:
+      if (!signe(y)) pari_err_INV("gmodsg",y);
+      return degpol(y)? gmulsg(x, RgX_get_1(y)): RgX_get_0(y);
+  }
+  pari_err_TYPE2("%",stoi(x),y);
+  return NULL; /* not reached */
+}
+/* divisibility: return 1 if y | x, 0 otherwise */
+int
+gdvd(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  int t = gequal0( gmod(x,y) ); avma = av; return t;
+}
+
+GEN
+gmodulss(long x, long y)
+{
+  if (!y) pari_err_INV("%",gen_0);
+  retmkintmod(modss(x, y), utoi(labs(y)));
+}
+GEN
+gmodulsg(long x, GEN y)
+{
+  switch(typ(y))
+  {
+    case t_INT:
+      if (!is_bigint(y)) return gmodulss(x,itos(y));
+      retmkintmod(modsi(x,y), absi(y));
+    case t_POL:
+      if (!signe(y)) pari_err_INV("%", y);
+      retmkpolmod(stoi(x),RgX_copy(y));
+  }
+  pari_err_TYPE2("%",stoi(x),y); return NULL; /* not reached */
+}
+GEN
+gmodulo(GEN x,GEN y)
+{
+  long tx = typ(x), vx, vy;
+  if (tx == t_INT && !is_bigint(x)) return gmodulsg(itos(x), y);
+  if (is_matvec_t(tx))
+  {
+    long l, i;
+    GEN z = cgetg_copy(x, &l);
+    for (i=1; i<l; i++) gel(z,i) = gmodulo(gel(x,i),y);
+    return z;
+  }
+  switch(typ(y))
+  {
+    case t_INT:
+      if (!is_const_t(tx)) return gmul(x, gmodulsg(1,y));
+      if (tx == t_INTMOD) return gmod(x,y);
+      retmkintmod(Rg_to_Fp(x,y), absi(y));
+    case t_POL:
+      vx = gvar(x); vy = varn(y);
+      if (varncmp(vy, vx) > 0) return gmul(x, gmodulsg(1,y));
+      if (vx == vy && tx == t_POLMOD) return grem(x,y);
+      retmkpolmod(grem(x,y), RgX_copy(y));
+  }
+  pari_err_TYPE2("%",x,y); return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                 GENERIC EUCLIDEAN DIVISION                      */
+/*                                                                 */
+/*******************************************************************/
+GEN
+gdivent(GEN x, GEN y)
+{
+  long tx, ty;
+  tx = typ(x); if (tx == t_INT && !is_bigint(x)) return gdiventsg(itos(x),y);
+  ty = typ(y); if (ty == t_INT && !is_bigint(y)) return gdiventgs(x,itos(y));
+  if (is_matvec_t(tx))
+  {
+    long i, lx;
+    GEN z = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(z,i) = gdivent(gel(x,i),y);
+    return z;
+  }
+  if (tx == t_POL || ty == t_POL) return gdeuc(x,y);
+  switch(ty)
+  {
+    case t_INT:
+      switch(tx)
+      {
+        case t_INT: return truedivii(x,y);
+        case t_REAL: return quotri(x,y);
+        case t_FRAC: return quotfi(x,y);
+      }
+      break;
+    case t_REAL: case t_FRAC: return quot(x,y);
+  }
+  pari_err_TYPE2("\\",x,y);
+  return NULL; /* not reached */
+}
+
+GEN
+gdiventgs(GEN x, long y)
+{
+  long i, lx;
+  GEN z;
+  switch(typ(x))
+  {
+    case t_INT:  return truedivis(x,y);
+    case t_REAL: return quotrs(x,y);
+    case t_FRAC: return quotfs(x,y);
+    case t_POL:  return gdivgs(x,y);
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(z,i) = gdiventgs(gel(x,i),y);
+      return z;
+  }
+  pari_err_TYPE2("\\",x,stoi(y));
+  return NULL; /* not reached */
+}
+GEN
+gdiventsg(long x, GEN y)
+{
+  switch(typ(y))
+  {
+    case t_INT:  return truedivsi(x,y);
+    case t_REAL: return quotsr(x,y);
+    case t_FRAC: return quotsf(x,y);
+    case t_POL:
+      if (!signe(y)) pari_err_INV("gdiventsg",y);
+      return degpol(y)? RgX_get_0(y): gdivsg(x,gel(y,2));
+  }
+  pari_err_TYPE2("\\",stoi(x),y);
+  return NULL; /* not reached */
+}
+
+/* with remainder */
+static GEN
+quotrem(GEN x, GEN y, GEN *r)
+{
+  GEN q = quot(x,y);
+  pari_sp av = avma;
+  *r = gerepileupto(av, gsub(x, gmul(q,y)));
+  return q;
+}
+
+GEN
+gdiventres(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y);
+  GEN z,q,r;
+
+  if (is_matvec_t(tx))
+  {
+    long i, lx;
+    z = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(z,i) = gdiventres(gel(x,i),y);
+    return z;
+  }
+  z = cgetg(3,t_COL);
+  if (tx == t_POL || ty == t_POL)
+  {
+    gel(z,1) = poldivrem(x,y,(GEN*)(z+2));
+    return z;
+  }
+  switch(ty)
+  {
+    case t_INT:
+      switch(tx)
+      { /* equal to, but more efficient than next case */
+        case t_INT:
+          gel(z,1) = truedvmdii(x,y,(GEN*)(z+2));
+          return z;
+        case t_REAL: case t_FRAC:
+          q = quotrem(x,y,&r);
+          gel(z,1) = q;
+          gel(z,2) = r; return z;
+      }
+      break;
+    case t_REAL: case t_FRAC:
+          q = quotrem(x,y,&r);
+          gel(z,1) = q;
+          gel(z,2) = r; return z;
+  }
+  pari_err_TYPE2("\\",x,y);
+  return NULL; /* not reached */
+}
+
+GEN
+divrem(GEN x, GEN y, long v)
+{
+  pari_sp av = avma;
+  long vx, vy;
+  GEN q, r;
+  if (v < 0 || typ(y) != t_POL || typ(x) != t_POL) return gdiventres(x,y);
+  vx = varn(x); if (vx != v) x = swap_vars(x,v);
+  vy = varn(y); if (vy != v) y = swap_vars(y,v);
+  q = poldivrem(x,y, &r);
+  if (v && (vx != v || vy != v))
+  {
+    GEN X = pol_x(v);
+    q = gsubst(q, v, X); /* poleval broken for t_RFRAC, subst is safe */
+    r = gsubst(r, v, X);
+  }
+  return gerepilecopy(av, mkcol2(q, r));
+}
+
+GEN
+diviiround(GEN x, GEN y)
+{
+  pari_sp av1, av = avma;
+  GEN q,r;
+  int fl;
+
+  q = dvmdii(x,y,&r); /* q = x/y rounded towards 0, sgn(r)=sgn(x) */
+  if (r==gen_0) return q;
+  av1 = avma;
+  fl = absi_cmp(shifti(r,1),y);
+  avma = av1; cgiv(r);
+  if (fl >= 0) /* If 2*|r| >= |y| */
+  {
+    long sz = signe(x)*signe(y);
+    if (fl || sz > 0) q = gerepileuptoint(av, addis(q,sz));
+  }
+  return q;
+}
+
+/* If x and y are not both scalars, same as gdivent.
+ * Otherwise, compute the quotient x/y, rounded to the nearest integer
+ * (towards +oo in case of tie). */
+GEN
+gdivround(GEN x, GEN y)
+{
+  pari_sp av;
+  long tx=typ(x),ty=typ(y);
+  GEN q,r;
+
+  if (tx==t_INT && ty==t_INT) return diviiround(x,y);
+  av = avma;
+  if (is_rational_t(tx) && is_rational_t(ty))
+  { /* same as diviiround but less efficient */
+    pari_sp av1;
+    int fl;
+    q = quotrem(x,y,&r);
+    av1 = avma;
+    fl = gcmp(gmul2n(Q_abs(r),1), Q_abs(y));
+    avma = av1; cgiv(r);
+    if (fl >= 0) /* If 2*|r| >= |y| */
+    {
+      long sz = gsigne(y);
+      if (fl || sz > 0) q = gerepileupto(av, gaddgs(q, sz));
+    }
+    return q;
+  }
+  if (is_matvec_t(tx))
+  {
+    long i, lx;
+    GEN z = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(z,i) = gdivround(gel(x,i),y);
+    return z;
+  }
+  return gdivent(x,y);
+}
+
+GEN
+gdivmod(GEN x, GEN y, GEN *pr)
+{
+  switch(typ(x))
+  {
+    case t_INT:
+      switch(typ(y))
+      {
+        case t_INT: return dvmdii(x,y,pr);
+        case t_POL: *pr=icopy(x); return gen_0;
+      }
+      break;
+    case t_POL: return poldivrem(x,y,pr);
+  }
+  pari_err_TYPE2("gdivmod",x,y);
+  return NULL;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                               SHIFT                             */
+/*                                                                 */
+/*******************************************************************/
+
+/* Shift tronque si n<0 (multiplication tronquee par 2^n)  */
+
+GEN
+gshift(GEN x, long n)
+{
+  long i, lx;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT: return shifti(x,n);
+    case t_REAL:return shiftr(x,n);
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gshift(gel(x,i),n);
+      return y;
+  }
+  return gmul2n(x,n);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*           SUBSTITUTION DANS UN POLYNOME OU UNE SERIE            */
+/*                                                                 */
+/*******************************************************************/
+
+/* Convert t_SER --> t_POL, ignoring valp. INTERNAL ! */
+GEN
+ser2pol_i(GEN x, long lx)
+{
+  long i = lx-1;
+  GEN y;
+  while (i > 1 && isexactzero(gel(x,i))) i--;
+  y = cgetg(i+1, t_POL); y[1] = x[1] & ~VALPBITS;
+  for ( ; i > 1; i--) gel(y,i) = gel(x,i);
+  return y;
+}
+
+/* T t_POL in var v, mod out by T components of x which are
+ * t_POL/t_RFRAC in v. Recursively */
+static GEN
+mod_r(GEN x, long v, GEN T)
+{
+  long i, w, lx, tx = typ(x);
+  GEN y;
+
+  if (is_const_t(tx)) return x;
+  switch(tx)
+  {
+    case t_POLMOD:
+      w = varn(gel(x,1));
+      if (w == v) pari_err_PRIORITY("subst", gel(x,1), "=", v);
+      if (varncmp(v, w) < 0) return x;
+      return gmodulo(mod_r(gel(x,2),v,T), mod_r(gel(x,1),v,T));
+    case t_SER:
+      w = varn(x);
+      if (w == v) break; /* fail */
+      if (varncmp(v, w) < 0) return x;
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i = 2; i < lx; i++) gel(y,i) = mod_r(gel(x,i),v,T);
+      return normalize(y);
+    case t_POL:
+      w = varn(x);
+      if (w == v) return RgX_rem(x, T);
+      if (varncmp(v, w) < 0) return x;
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i = 2; i < lx; i++) gel(y,i) = mod_r(gel(x,i),v,T);
+      return normalizepol_lg(y, lx);
+    case t_RFRAC:
+      return gdiv(mod_r(gel(x,1),v,T), mod_r(gel(x,2),v,T));
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i = 1; i < lx; i++) gel(y,i) = mod_r(gel(x,i),v,T);
+      return y;
+    case t_LIST:
+      y = listcreate();
+      list_data(y) = list_data(x)? mod_r(list_data(x),v,T): NULL;
+      return y;
+  }
+  pari_err_TYPE("substpol",x);
+  return NULL;/*not reached*/
+}
+GEN
+gsubst_expr(GEN expr, GEN from, GEN to)
+{
+  pari_sp av = avma;
+  long w, v = fetch_var(); /* FIXME: Need fetch_var_low_priority() */
+  GEN y;
+
+  from = simplify_shallow(from);
+  switch (typ(from)) {
+    case t_RFRAC: /* M= numerator(from) - t * denominator(from) */
+      y = gsub(gel(from,1), gmul(pol_x(v), gel(from,2)));
+      break;
+    default:
+      y = gsub(from, pol_x(v));        /* M = from - t */
+  }
+  w = gvar(from);
+  if (varncmp(v,w) <= 0) pari_err_PRIORITY("subst", pol_x(v), "<=", w);
+  y = gsubst(mod_r(expr, w, y), v, to);
+  (void)delete_var(); return gerepileupto(av, y);
+}
+
+GEN
+gsubstpol(GEN x, GEN T, GEN y)
+{
+  if (typ(T) == t_POL && RgX_is_monomial(T) && gequal1(leading_term(T)))
+  { /* T = t^d */
+    long d = degpol(T), v = varn(T);
+    pari_sp av = avma;
+    GEN deflated = d == 1? x: gdeflate(x, v, d);
+    if (deflated) return gerepileupto(av, gsubst(deflated, v, y));
+    avma = av;
+  }
+  return gsubst_expr(x,T,y);
+}
+
+static long
+checkdeflate(GEN x)
+{
+  ulong d = 0, i, lx = (ulong)lg(x);
+  for (i=3; i<lx; i++)
+    if (!gequal0(gel(x,i))) { d = ugcd(d,i-2); if (d == 1) break; }
+  return (long)d;
+}
+
+/* deflate (non-leaf) x recursively */
+static GEN
+vdeflate(GEN x, long v, long d)
+{
+  long i = lontyp[typ(x)], lx;
+  GEN z = cgetg_copy(x, &lx);
+  if (i == 2) z[1] = x[1];
+  for (; i<lx; i++)
+  {
+    gel(z,i) = gdeflate(gel(x,i),v,d);
+    if (!z[i]) return NULL;
+  }
+  return z;
+
+}
+
+/* don't return NULL if substitution fails (fallback won't be able to handle
+ * t_SER anyway), fail with a meaningful message */
+static GEN
+serdeflate(GEN x, long v, long d)
+{
+  long V, lx, vx = varn(x);
+  pari_sp av;
+  GEN y;
+  if (varncmp(vx, v) < 0) return vdeflate(x,v,d);
+  if (varncmp(vx, v) > 0) return gcopy(x);
+  av = avma;
+  V = valp(x);
+  lx = lg(x);
+  if (lx == 2) return zeroser(v, V / d);
+  y = ser2pol_i(x, lx);
+  if (V % d != 0 || checkdeflate(y) % d != 0)
+  {
+    const char *s = stack_sprintf("valuation(x) %% %ld", d);
+    pari_err_DOMAIN("gdeflate", s, "!=", gen_0,x);
+  }
+  y = poltoser(RgX_deflate(y, d), v, 1 + (lx-3)/d);
+  setvalp(y, V/d); return gerepilecopy(av, y);
+}
+static GEN
+poldeflate(GEN x, long v, long d)
+{
+  long vx = varn(x);
+  pari_sp av;
+  if (varncmp(vx, v) < 0) return vdeflate(x,v,d);
+  if (varncmp(vx, v) > 0) return gcopy(x);
+  av = avma;
+  if (checkdeflate(x) % d != 0) return NULL;
+  return gerepilecopy(av, RgX_deflate(x,d));
+}
+static GEN
+listdeflate(GEN x, long v, long d)
+{
+  GEN y = NULL, z = listcreate();
+  if (list_data(x))
+  {
+    y = vdeflate(list_data(x),v,d);
+    if (!y) return NULL;
+  }
+  list_data(z) = y; return z;
+}
+/* return NULL if substitution fails */
+GEN
+gdeflate(GEN x, long v, long d)
+{
+  if (d <= 0) pari_err_DOMAIN("gdeflate", "degree", "<=", gen_0,stoi(d));
+  switch(typ(x))
+  {
+    case t_INT:
+    case t_REAL:
+    case t_INTMOD:
+    case t_FRAC:
+    case t_FFELT:
+    case t_COMPLEX:
+    case t_PADIC:
+    case t_QUAD: return gcopy(x);
+    case t_POL: return poldeflate(x,v,d);
+    case t_SER: return serdeflate(x,v,d);
+    case t_POLMOD:
+      if (varncmp(varn(gel(x,1)), v) >= 0) return gcopy(x);
+      /* fall through */
+    case t_RFRAC:
+    case t_VEC:
+    case t_COL:
+    case t_MAT: return vdeflate(x,v,d);
+    case t_LIST: return listdeflate(x,v,d);
+  }
+  pari_err_TYPE("gdeflate",x);
+  return NULL; /* not reached */
+}
+
+/* set *m to the largest d such that x0 = A(X^d); return A */
+GEN
+RgX_deflate_max(GEN x, long *m)
+{
+  *m = checkdeflate(x);
+  return RgX_deflate(x, *m);
+}
+
+GEN
+RgX_RgM_eval_col(GEN x, GEN M, long c)
+{
+  long i, n = lg(M)-1, lc = lg(x)-1;
+  GEN z;
+  if (signe(x)==0) return zerocol(n);
+  z = Rg_col_ei(gel(x, lc), n, c);
+  for (i=lc-1; i>=2; i--)
+  {
+    z = RgM_RgC_mul(M, z);
+    gel(z,c) = gadd(gel(z,c), gel(x, i));
+  }
+  return z;
+}
+
+GEN
+gsubst(GEN x, long v, GEN y)
+{
+  long tx = typ(x), ty = typ(y), lx = lg(x), ly = lg(y);
+  long l, vx, vy, ex, ey, i, j, k, jb;
+  pari_sp av, av2, lim;
+  GEN X, t, p1, p2, modp1, z;
+
+  switch(ty)
+  {
+    case t_MAT:
+      if (ly==1) return cgetg(1,t_MAT);
+      if (ly == lgcols(y)) break;
+      /* fall through */
+    case t_QFR: case t_QFI: case t_VEC: case t_COL:
+      pari_err_TYPE2("substitution",x,y);
+      break; /* not reached */
+  }
+
+  if (is_scalar_t(tx))
+  {
+    if (tx!=t_POLMOD || varncmp(v, varn(gel(x,1))) <= 0)
+    {
+      if (ty==t_MAT) return scalarmat(x,ly-1);
+      return gcopy(x);
+    }
+    av=avma;
+    p1=gsubst(gel(x,1),v,y); vx=varn(p1);
+    p2=gsubst(gel(x,2),v,y); vy=gvar(p2);
+    if (typ(p1)!=t_POL) pari_err_TYPE2("substitution",x,y);
+    if (varncmp(vy, vx) >= 0) return gerepileupto(av, gmodulo(p2,p1));
+    modp1 = mkpolmod(gen_1,p1);
+    lx = lg(p2);
+    z = cgetg(lx,t_POL); z[1] = p2[1];
+    for (i=2; i<lx; i++)
+    {
+      GEN c = gel(p2,i);
+      if (varncmp(vx, gvar(c)) <= 0)
+        c = gmodulo(c,p1);
+      else
+        c = gmul(c, modp1);
+      gel(z,i) = c;
+    }
+    return gerepileupto(av, normalizepol_lg(z,lx));
+  }
+
+  switch(tx)
+  {
+    case t_POL:
+      if (lx==2)
+        return ty == t_MAT? scalarmat(gen_0,ly-1): gen_0;
+
+      vx = varn(x);
+      if (varncmp(vx, v) > 0)
+        return ty == t_MAT? scalarmat(x,ly-1): RgX_copy(x);
+      if (varncmp(vx, v) < 0)
+      {
+        av = avma; z = cgetg(lx, t_POL); z[1] = x[1];
+        for (i=2; i<lx; i++) gel(z,i) = gsubst(gel(x,i),v,y);
+        return gerepileupto(av, poleval(z, pol_x(vx)));
+      }
+      return ty == t_MAT? RgX_RgM_eval(x, y): poleval(x,y);
+
+    case t_SER:
+      vx = varn(x);
+      if (varncmp(vx, v) > 0)
+        return (ty==t_MAT)? scalarmat(x,ly-1): gcopy(x);
+      ex = valp(x);
+      if (varncmp(vx, v) < 0)
+      {
+        if (lx == 2) return (ty==t_MAT)? scalarmat(x,ly-1): gcopy(x);
+        av = avma; X = pol_x(vx);
+        av2 = avma; lim = stack_lim(av2,1);
+        z = gadd(gsubst(gel(x,lx-1),v,y), zeroser(vx,1));
+        for (i = lx-2; i>=2; i--)
+        {
+          z = gadd(gmul(z,X), gsubst(gel(x,i),v,y));
+          if (low_stack(lim, stack_lim(av2,1)))
+          {
+            if(DEBUGMEM>1) pari_warn(warnmem,"gsubst (i = %ld)", i);
+            z = gerepileupto(av2, z);
+          }
+        }
+        if (ex) z = gmul(z, monomial(gen_1,ex,vx));
+        return gerepileupto(av, z);
+      }
+      switch(ty) /* here vx == v */
+      {
+        case t_SER:
+          vy = varn(y); ey = valp(y);
+          if (ey < 1 || lx == 2) return zeroser(vy, ey*(ex+lx-2));
+          if (vy != vx)
+          {
+            av = avma; lim = stack_lim(av,1); z = gel(x,lx-1);
+
+            for (i=lx-2; i>=2; i--)
+            {
+              z = gadd(gmul(y,z), gel(x,i));
+              if (low_stack(lim, stack_lim(av,1)))
+              {
+                if(DEBUGMEM>1) pari_warn(warnmem,"gsubst (i = %ld)", i);
+                z = gerepileupto(av, z);
+              }
+            }
+            if (ex) z = gmul(z, gpowgs(y,ex));
+            return gerepileupto(av,z);
+          }
+          l = (lx-2)*ey+2;
+          if (ex) { if (l>ly) l = ly; }
+          else if (lx != 3)
+          {
+            long l2;
+            for (i = 3; i < lx; i++)
+              if (!isexactzero(gel(x,i))) break;
+            l2 = (i-2)*ey + (gequal0(y)? 2 : ly);
+            if (l > l2) l = l2;
+          }
+          av = avma; lim=stack_lim(av,1);
+          t = leafcopy(y);
+          if (l < ly) setlg(t, l);
+          z = scalarser(gel(x,2),varn(y),l-2);
+          for (i=3,jb=ey; jb<=l-2; i++,jb+=ey)
+          {
+            if (i < lx) {
+              for (j=jb+2; j<minss(l, jb+ly); j++)
+                gel(z,j) = gadd(gel(z,j), gmul(gel(x,i),gel(t,j-jb)));
+            }
+            for (j=l-1-jb-ey; j>1; j--)
+            {
+              p1 = gen_0;
+              for (k=2; k<j; k++)
+                p1 = gadd(p1, gmul(gel(t,j-k+2),gel(y,k)));
+              gel(t,j) = gadd(p1, gmul(gel(t,2),gel(y,j)));
+            }
+            if (low_stack(lim, stack_lim(av,1)))
+            {
+              if(DEBUGMEM>1) pari_warn(warnmem,"gsubst");
+              gerepileall(av,2, &z,&t);
+            }
+          }
+          if (!ex) return gerepilecopy(av,z);
+          return gerepileupto(av, gmul(z,gpowgs(y, ex)));
+
+        case t_POL: case t_RFRAC:
+        {
+          long n = lx-2;
+          GEN cx;
+          vy = gvar(y); ey = gval(y,vy);
+          if (ey == LONG_MAX) return n? scalarser(gel(x,2),v,n): gcopy(x);
+          if (ey < 1 || n == 0) return zeroser(vy, ey*(ex+n));
+          av = avma;
+          n *= ey;
+          y = (ty == t_RFRAC)? rfractoser(y, vy, n): poltoser(y, vy, n);
+          x = ser2pol_i(x, lx);
+          x = primitive_part(x, &cx);
+          z = RgX_modXn_eval(x, ser2rfrac_i(y), n);
+          z = RgX_to_ser(z, n+2);
+          if (cx) z = gmul(z, cx);
+          if (!ex && !cx) return gerepilecopy(av, z);
+          if (ex) z = gmul(z, gpowgs(y,ex));
+          return gerepileupto(av, z);
+        }
+
+        default:
+          if (isexactzero(y))
+          {
+            if (ex < 0) pari_err_INV("gsubst",y);
+            if (ex > 0) return gcopy(y);
+            if (lx > 2) return gadd(gel(x,2), y); /*add maps to correct ring*/
+          }
+          pari_err_TYPE2("substitution",x,y);
+      }
+      break;
+
+    case t_RFRAC: av=avma;
+      p1=gsubst(gel(x,1),v,y);
+      p2=gsubst(gel(x,2),v,y); return gerepileupto(av, gdiv(p1,p2));
+
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(z,i) = gsubst(gel(x,i),v,y);
+      return z;
+    case t_LIST:
+      z = listcreate();
+      list_data(z) = list_data(x)? gsubst(list_data(x),v,y): NULL;
+      return z;
+  }
+  return gcopy(x);
+}
+
+/* Return P(x * h), not memory clean */
+GEN
+ser_unscale(GEN P, GEN h)
+{
+  long l = lg(P);
+  GEN Q = cgetg(l,t_SER);
+  Q[1] = P[1];
+  if (l != 2)
+  {
+    long i = 2;
+    GEN hi = gpowgs(h, valp(P));
+    gel(Q,i) = gmul(gel(P,i), hi);
+    for (i++; i<l; i++) { hi = gmul(hi,h); gel(Q,i) = gmul(gel(P,i), hi); }
+  }
+  return Q;
+}
+
+GEN
+gsubstvec(GEN e, GEN v, GEN r)
+{
+  pari_sp ltop=avma;
+  long i, j, l = lg(v);
+  GEN w, z, R;
+  if ( !is_vec_t(typ(v)) ) pari_err_TYPE("substvec",v);
+  if ( !is_vec_t(typ(r)) ) pari_err_TYPE("substvec",r);
+  if (lg(r)!=l) pari_err_DIM("substvec");
+  w = cgetg(l,t_VECSMALL);
+  z = cgetg(l,t_VECSMALL);
+  R = cgetg(l,t_VEC);
+  for(i=j=1;i<l;i++)
+  {
+    GEN T = gel(v,i), ri = gel(r,i);
+    if (!gequalX(T)) pari_err_TYPE("substvec [not a variable]", T);
+    if (gvar(ri) == NO_VARIABLE) /* no need to take precautions */
+      e = gsubst(e, varn(T), ri);
+    else
+    {
+      w[j] = varn(T);
+      z[j] = fetch_var();
+      gel(R,j) = ri;
+      j++;
+    }
+  }
+  for(i=1;i<j;i++) e = gsubst(e,w[i],pol_x(z[i]));
+  for(i=1;i<j;i++) e = gsubst(e,z[i],gel(R,i));
+  for(i=1;i<j;i++) (void)delete_var();
+  return gerepileupto(ltop,e);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                SERIE RECIPROQUE D'UNE SERIE                     */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+serreverse(GEN x)
+{
+  long v=varn(x), lx = lg(x), i, mi;
+  pari_sp av0 = avma, av, lim;
+  GEN a, y, u;
+
+  if (typ(x)!=t_SER) pari_err_TYPE("serreverse",x);
+  if (valp(x)!=1) pari_err_DOMAIN("serreverse", "valuation", "!=", gen_1,x);
+  if (lx < 3) pari_err_DOMAIN("serreverse", "x", "=", gen_0,x);
+  y = ser_normalize(x);
+  if (y == x) a = NULL; else { a = gel(x,2); x = y; }
+  av = avma; lim = stack_lim(av, 2);
+  mi = lx-1; while (mi>=3 && gequal0(gel(x,mi))) mi--;
+  u = cgetg(lx,t_SER);
+  y = cgetg(lx,t_SER);
+  u[1] = y[1] = evalsigne(1) | _evalvalp(1) | evalvarn(v);
+  gel(u,2) = gel(y,2) = gen_1;
+  if (lx > 3)
+  {
+    gel(u,3) = gmulsg(-2,gel(x,3));
+    gel(y,3) = gneg(gel(x,3));
+  }
+  for (i=3; i<lx-1; )
+  {
+    pari_sp av2;
+    GEN p1;
+    long j, k, K = minss(i,mi);
+    for (j=3; j<i+1; j++)
+    {
+      av2 = avma; p1 = gel(x,j);
+      for (k = maxss(3,j+2-mi); k < j; k++)
+        p1 = gadd(p1, gmul(gel(u,k),gel(x,j-k+2)));
+      p1 = gneg(p1);
+      gel(u,j) = gerepileupto(av2, gadd(gel(u,j), p1));
+    }
+    av2 = avma;
+    p1 = gmulsg(i,gel(x,i+1));
+    for (k = 2; k < K; k++)
+    {
+      GEN p2 = gmul(gel(x,k+1),gel(u,i-k+2));
+      p1 = gadd(p1, gmulsg(k,p2));
+    }
+    i++;
+    gel(u,i) = gerepileupto(av2, gneg(p1));
+    gel(y,i) = gdivgs(gel(u,i), i-1);
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      GEN dummy = cgetg(1,t_VEC);
+      if(DEBUGMEM>1) pari_warn(warnmem,"serreverse");
+      for(k=i+1; k<lx; k++) gel(u,k) = gel(y,k) = dummy;
+      gerepileall(av,2, &u,&y);
+    }
+  }
+  if (a) y = ser_unscale(y, ginv(a));
+  return gerepilecopy(av0,y);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    DERIVATION ET INTEGRATION                    */
+/*                                                                 */
+/*******************************************************************/
+GEN
+derivser(GEN x)
+{
+  long i, vx = varn(x), e = valp(x), lx = lg(x);
+  GEN y;
+  if (lx == 2) return zeroser(vx,e? e-1: 0);
+  if (e)
+  {
+    y = cgetg(lx,t_SER); y[1] = evalvalp(e-1) | evalvarn(vx);
+    for (i=2; i<lx; i++) gel(y,i) = gmulsg(i+e-2,gel(x,i));
+  } else {
+    if (lx == 3) return zeroser(vx, 0);
+    lx--;
+    y = cgetg(lx,t_SER); y[1] = _evalvalp(0) | evalvarn(vx);
+    for (i=2; i<lx; i++) gel(y,i) = gmulsg(i-1,gel(x,i+1));
+  }
+  return normalize(y);
+}
+
+GEN
+deriv(GEN x, long v)
+{
+  long lx, vx, tx, i, j;
+  pari_sp av;
+  GEN y;
+
+  tx = typ(x);
+  if (is_const_t(tx))
+    switch(tx)
+    {
+      case t_INTMOD: retmkintmod(gen_0, icopy(gel(x,1)));
+      case t_FFELT: return FF_zero(x);
+      default: return gen_0;
+    }
+  if (v < 0 && tx!=t_CLOSURE) v = gvar9(x);
+  switch(tx)
+  {
+    case t_POLMOD:
+    {
+      GEN a = gel(x,2), b = gel(x,1);
+      if (v == varn(b)) return RgX_get_0(b);
+      retmkpolmod(deriv(a,v), RgX_copy(b));
+    }
+    case t_POL:
+      vx = varn(x);
+      if (varncmp(vx, v) > 0) return RgX_get_0(x);
+      if (varncmp(vx, v) == 0) return RgX_deriv(x);
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = deriv(gel(x,i),v);
+      return normalizepol_lg(y,i);
+
+    case t_SER:
+      vx = varn(x);
+      if (varncmp(vx, v) > 0) return RgX_get_0(x);
+      if (varncmp(vx, v) == 0) return derivser(x);
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (j=2; j<lx; j++) gel(y,j) = deriv(gel(x,j),v);
+      return normalize(y);
+
+    case t_RFRAC: {
+      GEN a = gel(x,1), b = gel(x,2), bp, b0, d, t;
+      y = cgetg(3,t_RFRAC); av = avma;
+
+      bp = deriv(b, v);
+      d = RgX_gcd(bp, b);
+      if (gequal1(d)) {
+        d = gsub(gmul(b, deriv(a,v)), gmul(a, bp));
+        if (isexactzero(d)) return gerepileupto((pari_sp)(y+3), d);
+        gel(y,1) = gerepileupto(av, d);
+        gel(y,2) = gsqr(b); return y;
+      }
+      b0 = gdivexact(b, d);
+      bp = gdivexact(bp,d);
+      a = gsub(gmul(b0, deriv(a,v)), gmul(a, bp));
+      if (isexactzero(a)) return gerepileupto((pari_sp)(y+3), a);
+      t = ggcd(a, d);
+      if (!gequal1(t)) { a = gdivexact(a, t); d = gdivexact(d, t); }
+      gel(y,1) = a;
+      gel(y,2) = gmul(d, gsqr(b0));
+      return gerepilecopy((pari_sp)(y+3), y);
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = deriv(gel(x,i),v);
+      return y;
+
+    case t_CLOSURE:
+      if (v==-1) return closure_deriv(x);
+  }
+  pari_err_TYPE("deriv",x);
+  return NULL; /* not reached */
+}
+
+static long
+lookup(GEN v, long vx)
+{
+  long i ,l = lg(v);
+  for(i=1; i<l; i++)
+    if (varn(gel(v,i)) == vx) return i;
+  return 0;
+}
+
+GEN
+diffop(GEN x, GEN v, GEN dv)
+{
+  pari_sp av;
+  long i, idx, lx, tx = typ(x), vx;
+  GEN y;
+  if (!is_vec_t(typ(v))) pari_err_TYPE("diffop",v);
+  if (!is_vec_t(typ(dv))) pari_err_TYPE("diffop",dv);
+  if (lg(v)!=lg(dv)) pari_err_DIM("diffop");
+  if (is_const_t(tx)) return gen_0;
+  switch(tx)
+  {
+    case t_POLMOD:
+      av = avma;
+      vx  = varn(gel(x,1)); idx = lookup(v,vx);
+      if (idx) /*Assume the users now what they are doing */
+        y = gmodulo(diffop(gel(x,2),v,dv), gel(x,1));
+      else
+      {
+        GEN m = gel(x,1), pol=gel(x,2);
+        GEN u = gneg(gdiv(diffop(m,v,dv),RgX_deriv(m)));
+        y = diffop(pol,v,dv);
+        if (typ(pol)==t_POL && varn(pol)==varn(m))
+          y = gadd(y, gmul(u,RgX_deriv(pol)));
+        y = gmodulo(y, gel(x,1));
+      }
+      return gerepileupto(av, y);
+    case t_POL:
+      if (signe(x)==0) return gen_0;
+      vx  = varn(x); idx = lookup(v,vx);
+      av = avma; lx = lg(x);
+      y = diffop(gel(x,lx-1),v,dv);
+      for (i=lx-2; i>=2; i--) y = gadd(gmul(y,pol_x(vx)),diffop(gel(x,i),v,dv));
+      if (idx) y = gadd(y, gmul(gel(dv,idx),RgX_deriv(x)));
+      return gerepileupto(av, y);
+
+    case t_SER:
+      if (signe(x)==0) return gen_0;
+      vx  = varn(x); idx = lookup(v,vx);
+      if (!idx) return gen_0;
+      av = avma;
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = diffop(gel(x,i),v,dv);
+      y = normalize(y); /* y is probably invalid */
+      y = gsubst(y, vx, pol_x(vx)); /* Fix that */
+      y = gadd(y, gmul(gel(dv,idx),derivser(x)));
+      return gerepileupto(av, y);
+
+    case t_RFRAC: {
+      GEN a = gel(x,1), b = gel(x,2), ap, bp;
+      av = avma;
+      ap = diffop(a, v, dv); bp = diffop(b, v, dv);
+      y = gsub(gdiv(ap,b),gdiv(gmul(a,bp),gsqr(b)));
+      return gerepileupto(av, y);
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = diffop(gel(x,i),v,dv);
+      return y;
+
+  }
+  pari_err_TYPE("diffop",x);
+  return NULL; /* not reached */
+}
+
+GEN
+diffop0(GEN x, GEN v, GEN dv, long n)
+{
+  pari_sp av=avma;
+  long i;
+  for(i=1; i<=n; i++)
+    x = gerepileupto(av, diffop(x,v,dv));
+  return x;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         TAYLOR SERIES                          **/
+/**                                                                **/
+/********************************************************************/
+/* swap vars (vx,v) in x (assume vx < v, vx main variable in x), then call
+ * act(data, v, x). FIXME: use in other places */
+static GEN
+swapvar_act(GEN x, long vx, long v, GEN (*act)(void*, long, GEN), void *data)
+{
+  GEN y;
+  if (v != MAXVARN) { /* (vx,v) -> (MAXVARN, v) */
+    y = act(data, v, gsubst(x,vx,pol_x(MAXVARN)));
+    y = gsubst(y,MAXVARN,pol_x(vx));
+  } else if (vx != 0) { /* (vx,v) -> (vx, 0) */
+    y = act(data, 0, gsubst(x,v,pol_x(0)));
+    y = gsubst(y,0,pol_x(v));
+  } else { /* (0,MAXVARN) -> (w, 0) */
+    long w = fetch_var();
+    y = act(data, 0, gsubst(gsubst(x,0,pol_x(w)), MAXVARN,pol_x(0)));
+    y = gsubst(gsubst(y,0,pol_x(MAXVARN)), w,pol_x(0));
+    (void)delete_var();
+  }
+  return y;
+}
+/* x + O(v^data) */
+static GEN
+tayl_act(void *data, long v, GEN x) { return gadd(zeroser(v, (long)data), x); }
+static  GEN
+integ_act(void *data, long v, GEN x) { (void)data; return integ(x,v); }
+
+GEN
+tayl(GEN x, long v, long precS)
+{
+  long vx = gvar9(x);
+  pari_sp av;
+
+  if (varncmp(v, vx) <= 0) return gadd(zeroser(v,precS), x);
+  av = avma;
+  return gerepileupto(av, swapvar_act(x, vx, v, tayl_act, (void*)precS));
+}
+
+GEN
+ggrando(GEN x, long n)
+{
+  long m, v;
+
+  switch(typ(x))
+  {
+  case t_INT:/* bug 3 + O(1) */
+    if (signe(x) <= 0) pari_err_DOMAIN("O", "x", "<=", gen_0, x);
+    if (!is_pm1(x)) return zeropadic(x,n);
+    /* +/-1 = x^0 */
+    v = m = 0; break;
+  case t_POL:
+    if (!signe(x)) pari_err_DOMAIN("O", "x", "=", gen_0, x);
+    v = varn(x);
+    m = n * RgX_val(x); break;
+  case t_RFRAC:
+    if (gequal0(gel(x,1))) pari_err_DOMAIN("O", "x", "=", gen_0, x);
+    v = gvar(x);
+    m = n * gval(x,v); break;
+    default:  pari_err_TYPE("O", x);
+      v = m = 0; /* not reached */
+  }
+  return zeroser(v,m);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    FORMAL INTEGRATION                           */
+/*                                                                 */
+/*******************************************************************/
+
+static GEN
+triv_integ(GEN x, long v)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x, &lx); y[1] = x[1];
+  for (i=2; i<lx; i++) gel(y,i) = integ(gel(x,i),v);
+  return y;
+}
+
+GEN
+RgX_integ(GEN x)
+{
+  long i, lx = lg(x);
+  GEN y;
+  if (lx == 2) return RgX_copy(x);
+  y = cgetg(lx+1, t_POL); y[1] = x[1]; gel(y,2) = gen_0;
+  for (i=3; i<=lx; i++) gel(y,i) = gdivgs(gel(x,i-1),i-2);
+  return y;
+}
+
+static void
+err_intformal(GEN x)
+{ pari_err_DOMAIN("intformal", "residue(series, pole)", "!=", gen_0, x); }
+
+GEN
+integser(GEN x)
+{
+  long i, lx = lg(x), vx = varn(x), e = valp(x);
+  GEN y;
+  if (lx == 2) return zeroser(vx, e+1);
+  y = cgetg(lx, t_SER);
+  for (i=2; i<lx; i++)
+  {
+    long j = i+e-1;
+    GEN c = gel(x,i);
+    if (j)
+      c = gdivgs(c, j);
+    else
+    { /* should be isexactzero, but try to avoid error */
+      if (!gequal0(c)) err_intformal(x);
+      c = gen_0;
+    }
+    gel(y,i) = c;
+  }
+  y[1] = evalsigne(1) | evalvarn(vx) | evalvalp(e+1); return y;
+}
+
+GEN
+integ(GEN x, long v)
+{
+  long lx, tx, i, vx, n;
+  pari_sp av = avma;
+  GEN y,p1;
+
+  tx = typ(x);
+  if (v < 0) { v = gvar9(x); if (v == NO_VARIABLE) v = 0; }
+  if (is_scalar_t(tx))
+  {
+    if (tx == t_POLMOD)
+    {
+      GEN a = gel(x,2), b = gel(x,1);
+      vx = varn(b);
+      if (varncmp(v, vx) > 0) retmkpolmod(integ(a,v), RgX_copy(b));
+      if (v == vx) pari_err_PRIORITY("intformal",x,"=",v);
+    }
+    return deg1pol(x, gen_0, v);
+  }
+
+  switch(tx)
+  {
+    case t_POL:
+      vx = varn(x);
+      if (v == vx) return RgX_integ(x);
+      if (lg(x) == 2) {
+        if (varncmp(vx, v) < 0) v = vx;
+        return zeropol(v);
+      }
+      if (varncmp(vx, v) > 0) return deg1pol(x, gen_0, v);
+      return triv_integ(x,v);
+
+    case t_SER:
+      vx = varn(x);
+      if (v == vx) return integser(x);
+      if (lg(x) == 2) {
+        if (varncmp(vx, v) < 0) v = vx;
+        return zeroser(v, valp(x));
+      }
+      if (varncmp(vx, v) > 0) return deg1pol(x, gen_0, v);
+      return triv_integ(x,v);
+
+    case t_RFRAC:
+    {
+      GEN a = gel(x,1), b = gel(x,2), c, d, s;
+      vx = varn(b);
+      if (varncmp(vx, v) > 0) return deg1pol(x, gen_0, v);
+      if (varncmp(vx, v) < 0)
+        return gerepileupto(av, swapvar_act(x, vx, v, integ_act, NULL));
+
+      n = degpol(b);
+      if (typ(a) == t_POL && varn(a) == vx) n += degpol(a);
+      y = integ(gadd(x, zeroser(v,n + 2)), v);
+      y = gdiv(gtrunc(gmul(b, y)), b);
+      if (typ(y) != t_RFRAC) pari_err_BUG("intformal(t_RFRAC)");
+      c = gel(y,1); d = gel(y,2);
+      s = gsub(gmul(deriv(c,v),d), gmul(c,deriv(d,v)));
+      /* (c'd-cd')/d^2 = y' = x = a/b ? */
+      if (!gequal(gmul(s,b), gmul(a,gsqr(d)))) err_intformal(x);
+      if (typ(y)==t_RFRAC && lg(gel(y,1)) == lg(gel(y,2)))
+      {
+        GEN p2 = leading_term(gel(y,2));
+        p1 = gel(y,1);
+        if (typ(p1) == t_POL && varn(p1) == vx) p1 = leading_term(p1);
+        y = gsub(y, gdiv(p1,p2));
+      }
+      return gerepileupto(av,y);
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lg(x); i++) gel(y,i) = integ(gel(x,i),v);
+      return y;
+  }
+  pari_err_TYPE("integ",x);
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    PARTIES ENTIERES                             */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+gfloor(GEN x)
+{
+  GEN y;
+  long i, lx;
+
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_POL: return RgX_copy(x);
+    case t_REAL: return floorr(x);
+    case t_FRAC: return truedivii(gel(x,1),gel(x,2));
+    case t_RFRAC: return gdeuc(gel(x,1),gel(x,2));
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gfloor(gel(x,i));
+      return y;
+  }
+  pari_err_TYPE("gfloor",x);
+  return NULL; /* not reached */
+}
+
+GEN
+gfrac(GEN x)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gsub(x,gfloor(x)));
+}
+
+/* assume x t_REAL */
+GEN
+ceilr(GEN x) {
+  pari_sp av = avma;
+  GEN y = floorr(x);
+  if (cmpri(x, y)) return gerepileuptoint(av, addui(1,y));
+  return y;
+}
+
+GEN
+gceil(GEN x)
+{
+  GEN y;
+  long i, lx;
+  pari_sp av;
+
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_POL: return RgX_copy(x);
+    case t_REAL: return ceilr(x);
+    case t_FRAC:
+      av = avma; y = divii(gel(x,1),gel(x,2));
+      if (signe(gel(x,1)) > 0) y = gerepileuptoint(av, addui(1,y));
+      return y;
+
+    case t_RFRAC:
+      return gdeuc(gel(x,1),gel(x,2));
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gceil(gel(x,i));
+      return y;
+  }
+  pari_err_TYPE("gceil",x);
+  return NULL; /* not reached */
+}
+
+GEN
+round0(GEN x, GEN *pte)
+{
+  if (pte) { long e; x = grndtoi(x,&e); *pte = stoi(e); }
+  return ground(x);
+}
+
+/* assume x a t_REAL */
+GEN
+roundr(GEN x)
+{
+  long ex, s = signe(x);
+  pari_sp av;
+  GEN t;
+  if (!s || (ex=expo(x)) < -1) return gen_0;
+  if (ex == -1) return s>0? gen_1:
+                            absrnz_equal2n(x)? gen_0: gen_m1;
+  av = avma;
+  t = addrr(real2n(-1, nbits2prec(ex+1)), x); /* x + 0.5 */
+  return gerepileuptoint(av, floorr(t));
+}
+GEN
+roundr_safe(GEN x)
+{
+  long e1, ex, s = signe(x);
+  pari_sp av;
+  GEN t, y;
+
+  if (!s || (ex = expo(x)) < -1) return gen_0;
+  if (ex == -1) return s>0? gen_1:
+                            absrnz_equal2n(x)? gen_0: gen_m1;
+  av = avma;
+  t = addrr(real2n(-1,nbits2prec(ex+1)), x); /* x + 0.5 */
+
+  e1 = expo(t) - bit_prec(t) + 1;
+  y = mantissa2nr(t, e1);
+  if (signe(x) < 0) y = addsi(-1,y);
+  return gerepileuptoint(av,y);
+}
+
+GEN
+ground(GEN x)
+{
+  GEN y;
+  long i, lx;
+  pari_sp av;
+
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_INTMOD: case t_QUAD: return gcopy(x);
+    case t_REAL: return roundr(x);
+    case t_FRAC: return diviiround(gel(x,1), gel(x,2));
+    case t_POLMOD: y=cgetg(3,t_POLMOD);
+      gel(y,1) = RgX_copy(gel(x,1));
+      gel(y,2) = ground(gel(x,2)); return y;
+
+    case t_COMPLEX:
+      av = avma; y = cgetg(3, t_COMPLEX);
+      gel(y,2) = ground(gel(x,2));
+      if (!signe(gel(y,2))) { avma = av; return ground(gel(x,1)); }
+      gel(y,1) = ground(gel(x,1)); return y;
+
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = ground(gel(x,i));
+      return normalizepol_lg(y, lx);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = ground(gel(x,i));
+      return normalize(y);
+    case t_RFRAC:
+      av = avma;
+      return gerepileupto(av, gdiv(ground(gel(x,1)), ground(gel(x,2))));
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = ground(gel(x,i));
+      return y;
+  }
+  pari_err_TYPE("ground",x);
+  return NULL; /* not reached */
+}
+
+/* e = number of error bits on integral part */
+GEN
+grndtoi(GEN x, long *e)
+{
+  GEN y;
+  long i, lx, e1;
+  pari_sp av;
+
+  *e = -(long)HIGHEXPOBIT;
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_REAL: {
+      long ex = expo(x);
+      GEN t;
+      if (!signe(x) || ex < -1) { *e = ex; return gen_0; }
+      av = avma;
+      /* ex+2 since we may have ex = -1 */
+      t = addrr(real2n(-1,nbits2prec(ex+2)), x); e1 = expo(t);
+      if (e1 < 0)
+      {
+        if (signe(t) >= 0) { *e = ex; avma = av; return gen_0; }
+        *e = expo(addsr(1,x)); avma = av; return gen_m1;
+      }
+      e1 = e1 - bit_prec(t) + 1;
+      y = mantissa2nr(t, e1);
+      if (signe(x) < 0) y = addsi(-1,y);
+      y = gerepileuptoint(av,y);
+
+      if (e1 <= 0) { av = avma; e1 = expo(subri(x,y)); avma = av; }
+      *e = e1; return y;
+    }
+    case t_FRAC: return diviiround(gel(x,1), gel(x,2));
+    case t_INTMOD: case t_QUAD: return gcopy(x);
+    case t_COMPLEX:
+      av = avma; y = cgetg(3, t_COMPLEX);
+      gel(y,2) = grndtoi(gel(x,2), e);
+      if (!signe(gel(y,2))) {
+        avma = av;
+        y = grndtoi(gel(x,1), &e1);
+      }
+      else
+        gel(y,1) = grndtoi(gel(x,1), &e1);
+      if (e1 > *e) *e = e1;
+      return y;
+
+    case t_POLMOD: y = cgetg(3,t_POLMOD);
+      gel(y,1) = RgX_copy(gel(x,1));
+      gel(y,2) = grndtoi(gel(x,2), e); return y;
+
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++)
+      {
+        gel(y,i) = grndtoi(gel(x,i),&e1);
+        if (e1 > *e) *e = e1;
+      }
+      return normalizepol_lg(y, lx);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++)
+      {
+        gel(y,i) = grndtoi(gel(x,i),&e1);
+        if (e1 > *e) *e = e1;
+      }
+      return normalize(y);
+    case t_RFRAC:
+      y = cgetg(3,t_RFRAC);
+      gel(y,1) = grndtoi(gel(x,1),&e1); if (e1 > *e) *e = e1;
+      gel(y,2) = grndtoi(gel(x,2),&e1); if (e1 > *e) *e = e1;
+      return y;
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++)
+      {
+        gel(y,i) = grndtoi(gel(x,i),&e1);
+        if (e1 > *e) *e = e1;
+      }
+      return y;
+  }
+  pari_err_TYPE("grndtoi",x);
+  return NULL; /* not reached */
+}
+
+/* trunc(x * 2^s). lindep() sanity checks rely on this function to return a
+ * t_INT or fail when fed a non-t_COMPLEX input; so do not make this one
+ * recursive [ or change the lindep call ] */
+GEN
+gtrunc2n(GEN x, long s)
+{
+  GEN z;
+  switch(typ(x))
+  {
+    case t_INT:  return shifti(x, s);
+    case t_REAL: return trunc2nr(x, s);
+    case t_FRAC: {
+      pari_sp av;
+      GEN a = gel(x,1), b = gel(x,2), q;
+      if (s == 0) return divii(a, b);
+      av = avma;
+      if (s < 0) q = divii(shifti(a, s), b);
+      else {
+        GEN r;
+        q = dvmdii(a, b, &r);
+        q = addii(shifti(q,s), divii(shifti(r,s), b));
+      }
+      return gerepileuptoint(av, q);
+    }
+    case t_COMPLEX:
+      z = cgetg(3, t_COMPLEX);
+      gel(z,2) = gtrunc2n(gel(x,2), s);
+      if (!signe(gel(z,2))) {
+        avma = (pari_sp)(z + 3);
+        return gtrunc2n(gel(x,1), s);
+      }
+      gel(z,1) = gtrunc2n(gel(x,1), s);
+      return z;
+    default: pari_err_TYPE("gtrunc2n",x);
+      return NULL; /* not reached */
+  }
+}
+
+/* e = number of error bits on integral part */
+GEN
+gcvtoi(GEN x, long *e)
+{
+  long tx = typ(x), lx, e1;
+  GEN y;
+
+  if (tx == t_REAL)
+  {
+    long ex = expo(x); if (ex < 0) { *e = ex; return gen_0; }
+    e1 = ex - bit_prec(x) + 1;
+    y = mantissa2nr(x, e1);
+    if (e1 <= 0) { pari_sp av = avma; e1 = expo(subri(x,y)); avma = av; }
+    *e = e1; return y;
+  }
+  *e = -(long)HIGHEXPOBIT;
+  if (is_matvec_t(tx))
+  {
+    long i;
+    y = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++)
+    {
+      gel(y,i) = gcvtoi(gel(x,i),&e1);
+      if (e1 > *e) *e = e1;
+    }
+    return y;
+  }
+  return gtrunc(x);
+}
+
+int
+isint(GEN n, GEN *ptk)
+{
+  switch(typ(n))
+  {
+    case t_INT: *ptk = n; return 1;
+    case t_REAL: {
+      pari_sp av0 = avma;
+      GEN z = floorr(n);
+      pari_sp av = avma;
+      long s = signe(subri(n, z));
+      if (s) { avma = av0; return 0; }
+      *ptk = z; avma = av; return 1;
+    }
+    case t_FRAC:    return 0;
+    case t_COMPLEX: return gequal0(gel(n,2)) && isint(gel(n,1),ptk);
+    case t_QUAD:    return gequal0(gel(n,3)) && isint(gel(n,2),ptk);
+    default: pari_err_TYPE("isint",n); return 0; /* not reached */
+  }
+}
+
+int
+issmall(GEN n, long *ptk)
+{
+  pari_sp av = avma;
+  GEN z;
+  long k;
+  if (!isint(n, &z)) return 0;
+  k = itos_or_0(z); avma = av;
+  if (k || lgefint(z) == 2) { *ptk = k; return 1; }
+  return 0;
+}
+
+/* smallest integer greater than any incarnations of the real x
+ * Avoid "precision loss in truncation" */
+GEN
+ceil_safe(GEN x)
+{
+  pari_sp av = avma;
+  long e, tx = typ(x);
+  GEN y;
+
+  if (is_rational_t(tx)) return gceil(x);
+  y = gcvtoi(x,&e);
+  if (gsigne(x) >= 0)
+  {
+    if (e < 0) e = 0;
+    y = addii(y, int2n(e));
+  }
+  return gerepileuptoint(av, y);
+}
+/* largest integer smaller than any incarnations of the real x
+ * Avoid "precision loss in truncation" */
+GEN
+floor_safe(GEN x)
+{
+  pari_sp av = avma;
+  long e, tx = typ(x);
+  GEN y;
+
+  if (is_rational_t(tx)) return gfloor(x);
+  y = gcvtoi(x,&e);
+  if (gsigne(x) <= 0)
+  {
+    if (e < 0) e = 0;
+    y = subii(y, int2n(e));
+  }
+  return gerepileuptoint(av, y);
+}
+
+GEN
+ser2rfrac_i(GEN x)
+{
+  long e = valp(x);
+  GEN a = ser2pol_i(x, lg(x));
+  if (e) {
+    if (e > 0) a = RgX_shift_shallow(a, e);
+    else a = gred_rfrac_simple(a, monomial(gen_1, -e, varn(a)));
+  }
+  return a;
+}
+
+static GEN
+ser2rfrac(GEN x)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, ser2rfrac_i(x));
+}
+
+/* x t_PADIC, truncate to rational (t_INT/t_FRAC) */
+GEN
+padic_to_Q(GEN x)
+{
+  GEN u = gel(x,4), p;
+  long v;
+  if (!signe(u)) return gen_0;
+  v = valp(x);
+  if (!v) return icopy(u);
+  p = gel(x,2);
+  if (v>0)
+  {
+    pari_sp av = avma;
+    return gerepileuptoint(av, mulii(u, powiu(p,v)));
+  }
+  retmkfrac(icopy(u), powiu(p,-v));
+}
+GEN
+padic_to_Q_shallow(GEN x)
+{
+  GEN u = gel(x,4), p;
+  long v;
+  if (!signe(u)) return gen_0;
+  v = valp(x);
+  if (!v) return u;
+  p = gel(x,2);
+  if (v>0) return mulii(powiu(p,v), u);
+  return mkfrac(u, powiu(p,-v));
+}
+GEN
+QpV_to_QV(GEN v)
+{
+  long i, l;
+  GEN w = cgetg_copy(v, &l);
+  for (i = 1; i < l; i++)
+  {
+    GEN c = gel(v,i);
+    switch(typ(c))
+    {
+      case t_INT:
+      case t_FRAC: break;
+      case t_PADIC: c = padic_to_Q_shallow(c); break;
+      default: pari_err_TYPE("padic_to_Q", v);
+    }
+    gel(w,i) = c;
+  }
+  return w;
+}
+
+GEN
+gtrunc(GEN x)
+{
+  long i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_REAL: return truncr(x);
+    case t_FRAC: return divii(gel(x,1),gel(x,2));
+    case t_PADIC: return padic_to_Q(x);
+    case t_POL: return RgX_copy(x);
+    case t_RFRAC: return gdeuc(gel(x,1),gel(x,2));
+    case t_SER: return ser2rfrac(x);
+    case t_VEC: case t_COL: case t_MAT:
+    {
+      long lx;
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = gtrunc(gel(x,i));
+      return y;
+    }
+  }
+  pari_err_TYPE("gtrunc",x);
+  return NULL; /* not reached */
+}
+
+GEN
+trunc0(GEN x, GEN *pte)
+{
+  if (pte) { long e; x = gcvtoi(x,&e); *pte = stoi(e); }
+  return gtrunc(x);
+}
+/*******************************************************************/
+/*                                                                 */
+/*                  CONVERSIONS -->  INT, POL & SER                */
+/*                                                                 */
+/*******************************************************************/
+
+/* return a_(n-1) B^(n-1) + ... + a_0, where B = 2^32.
+ * The a_i are 32bits integers */
+GEN
+mkintn(long n, ...)
+{
+  va_list ap;
+  GEN x, y;
+  long i;
+#ifdef LONG_IS_64BIT
+  long e = (n&1);
+  n = (n+1) >> 1;
+#endif
+  va_start(ap,n);
+  x = cgetipos(n+2);
+  y = int_MSW(x);
+  for (i=0; i <n; i++)
+  {
+#ifdef LONG_IS_64BIT
+    ulong a = (e && !i)? 0: va_arg(ap, long);
+    ulong b = va_arg(ap, long);
+    *y = (a << 32) | b;
+#else
+    *y = va_arg(ap, long);
+#endif
+    y = int_precW(y);
+  }
+  va_end(ap);
+  return int_normalize(x, 0);
+}
+
+/* 2^32 a + b */
+GEN
+uu32toi(ulong a, ulong b)
+{
+#ifdef LONG_IS_64BIT
+  return utoi((a<<32) | b);
+#else
+  return uutoi(a, b);
+#endif
+}
+
+/* return a_(n-1) x^(n-1) + ... + a_0 */
+GEN
+mkpoln(long n, ...)
+{
+  va_list ap;
+  GEN x, y;
+  long i, l = n+2;
+  va_start(ap,n);
+  x = cgetg(l, t_POL); y = x + 2;
+  x[1] = evalvarn(0);
+  for (i=n-1; i >= 0; i--) gel(y,i) = va_arg(ap, GEN);
+  va_end(ap); return normalizepol_lg(x, l);
+}
+
+/* return [a_1, ..., a_n] */
+GEN
+mkvecn(long n, ...)
+{
+  va_list ap;
+  GEN x;
+  long i;
+  va_start(ap,n);
+  x = cgetg(n+1, t_VEC);
+  for (i=1; i <= n; i++) gel(x,i) = va_arg(ap, GEN);
+  va_end(ap); return x;
+}
+
+GEN
+mkcoln(long n, ...)
+{
+  va_list ap;
+  GEN x;
+  long i;
+  va_start(ap,n);
+  x = cgetg(n+1, t_COL);
+  for (i=1; i <= n; i++) gel(x,i) = va_arg(ap, GEN);
+  va_end(ap); return x;
+}
+
+GEN
+mkvecsmalln(long n, ...)
+{
+  va_list ap;
+  GEN x;
+  long i;
+  va_start(ap,n);
+  x = cgetg(n+1, t_VECSMALL);
+  for (i=1; i <= n; i++) gel(x,i) = va_arg(ap, GEN);
+  va_end(ap); return x;
+}
+
+GEN
+scalarpol(GEN x, long v)
+{
+  GEN y;
+  if (isrationalzero(x)) return zeropol(v);
+  y = cgetg(3,t_POL);
+  y[1] = gequal0(x)? evalvarn(v)
+                   : evalvarn(v) | evalsigne(1);
+  gel(y,2) = gcopy(x); return y;
+}
+GEN
+scalarpol_shallow(GEN x, long v)
+{
+  GEN y;
+  if (isrationalzero(x)) return zeropol(v);
+  y = cgetg(3,t_POL);
+  y[1] = gequal0(x)? evalvarn(v)
+                   : evalvarn(v) | evalsigne(1);
+  gel(y,2) = x; return y;
+}
+
+/* x0 + x1*T, do not assume x1 != 0 */
+GEN
+deg1pol(GEN x1, GEN x0,long v)
+{
+  GEN x = cgetg(4,t_POL);
+  x[1] = evalsigne(1) | evalvarn(v);
+  gel(x,2) = x0 == gen_0? x0: gcopy(x0); /* gen_0 frequent */
+  gel(x,3) = gcopy(x1); return normalizepol_lg(x,4);
+}
+
+/* same, no copy */
+GEN
+deg1pol_shallow(GEN x1, GEN x0,long v)
+{
+  GEN x = cgetg(4,t_POL);
+  x[1] = evalsigne(1) | evalvarn(v);
+  gel(x,2) = x0;
+  gel(x,3) = x1; return normalizepol_lg(x,4);
+}
+
+static GEN
+_gtopoly(GEN x, long v, int reverse)
+{
+  long tx = typ(x);
+  GEN y;
+
+  if (v<0) v = 0;
+  switch(tx)
+  {
+    case t_POL:
+      if (varncmp(varn(x), v) < 0) pari_err_PRIORITY("gtopoly", x, "<", v);
+      y = RgX_copy(x); break;
+    case t_SER:
+      if (varncmp(varn(x), v) < 0) pari_err_PRIORITY("gtopoly", x, "<", v);
+      y = ser2rfrac(x);
+      if (typ(y) != t_POL)
+        pari_err_DOMAIN("gtopoly", "valuation", "<", gen_0, x);
+      break;
+    case t_RFRAC:
+    {
+      GEN a = gel(x,1), b = gel(x,2);
+      long vb = varn(b);
+      if (varncmp(vb, v) < 0) pari_err_PRIORITY("gtopoly", x, "<", v);
+      if (typ(a) != t_POL || varn(a) != vb) return zeropol(v);
+      y = RgX_div(a,b); break;
+    }
+    case t_VECSMALL: x = zv_to_ZV(x); /* fall through */
+    case t_QFR: case t_QFI: case t_VEC: case t_COL: case t_MAT:
+    {
+      long j, k, lx = lg(x);
+      GEN z;
+      if (tx == t_QFR) lx--;
+      if (varncmp(gvar(x), v) <= 0) pari_err_PRIORITY("gtopoly", x, "<=", v);
+      y = cgetg(lx+1, t_POL);
+      y[1] = evalvarn(v);
+      if (reverse) {
+        x--;
+        for (j=2; j<=lx; j++) gel(y,j) = gel(x,j);
+      } else {
+        for (j=2, k=lx; k>=2; j++) gel(y,j) = gel(x,--k);
+      }
+      z = RgX_copy(normalizepol_lg(y,lx+1));
+      settyp(y, t_VECSMALL);/* left on stack */
+      return z;
+    }
+    default:
+      if (is_scalar_t(tx)) return scalarpol(x,v);
+      pari_err_TYPE("gtopoly",x);
+      return NULL; /* not reached */
+  }
+  setvarn(y,v); return y;
+}
+
+GEN
+gtopolyrev(GEN x, long v) { return _gtopoly(x,v,1); }
+
+GEN
+gtopoly(GEN x, long v) { return _gtopoly(x,v,0); }
+
+GEN
+scalarser(GEN x, long v, long prec)
+{
+  long i, l;
+  GEN y;
+
+  if (isrationalzero(x)) return zeroser(v, prec);
+  l = prec + 2; y = cgetg(l, t_SER);
+  y[1] = evalsigne(gequal0(x)? 0: 1) | _evalvalp(0) | evalvarn(v);
+  gel(y,2) = gcopy(x); for (i=3; i<l; i++) gel(y,i) = gen_0;
+  return y;
+}
+
+/* assume x a t_[SER|POL], apply gtoser to all coeffs */
+static GEN
+coefstoser(GEN x, long v, long prec)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x, &lx); y[1] = x[1];
+  for (i=2; i<lx; i++) gel(y,i) = gtoser(gel(x,i), v, prec);
+  return y;
+}
+
+/* x a t_POL. Not stack-clean */
+GEN
+poltoser(GEN x, long v, long prec)
+{
+  long vx = varn(x);
+  GEN y;
+
+  if (varncmp(vx, v) > 0) return scalarser(x, v, prec);
+  if (varncmp(vx, v) < 0) return coefstoser(x, v, prec);
+  y = RgX_to_ser(x, prec+2);
+  setvarn(y, v); return y;
+}
+
+/* x a t_RFRAC. Not stack-clean */
+GEN
+rfractoser(GEN x, long v, long prec)
+{
+  GEN n = gel(x,1);
+  if (is_scalar_t(typ(n)))
+    n = scalarser(n, v, prec);
+  else
+    n = poltoser(n, v, prec);
+  return gdiv(n, poltoser(gel(x,2), v, prec));
+}
+
+GEN
+toser_i(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_SER: return x;
+    case t_POL: return RgX_to_ser(x, precdl+2);
+    case t_RFRAC: return rfrac_to_ser(x, precdl+2);
+  }
+  return NULL;
+}
+
+GEN
+gtoser(GEN x, long v, long prec)
+{
+  long tx=typ(x), i, j, l;
+  pari_sp av;
+  GEN y;
+
+  if (v < 0) v = 0;
+  if (tx == t_SER)
+  {
+    long vx = varn(x);
+    if      (varncmp(vx, v) < 0) y = coefstoser(x, v, prec);
+    else if (varncmp(vx, v) > 0) y = scalarser(x, v, prec);
+    else y = gcopy(x);
+    return y;
+  }
+  if (is_scalar_t(tx)) return scalarser(x,v,prec);
+  switch(tx)
+  {
+    case t_POL:
+      if (varncmp(varn(x), v) < 0) pari_err_PRIORITY("gtoser", x, "<", v);
+      y = poltoser(x, v, prec); l = lg(y);
+      for (i=2; i<l; i++)
+        if (gel(y,i) != gen_0) gel(y,i) = gcopy(gel(y,i));
+      break;
+
+    case t_RFRAC:
+      if (varncmp(varn(gel(x,2)), v) < 0) pari_err_PRIORITY("gtoser",x,"<",v);
+      av = avma;
+      return gerepileupto(av, rfractoser(x, v, prec));
+
+    case t_VECSMALL: x = zv_to_ZV(x);/*fall through*/
+    case t_QFR: case t_QFI: case t_VEC: case t_COL:
+    {
+      GEN z;
+      long lx = lg(x); if (tx == t_QFR) lx--;
+      if (varncmp(gvar(x), v) <= 0) pari_err_PRIORITY("gtoser", x, "<=", v);
+      y = cgetg(lx+1, t_SER);
+      y[1] = evalvarn(v)|evalvalp(0);
+      x--;
+      for (j=2; j<=lx; j++) gel(y,j) = gel(x,j);
+      z = gcopy(normalize(y));
+      settyp(y, t_VECSMALL);/* left on stack */
+      return z;
+    }
+
+    default: pari_err_TYPE("gtoser",x);
+      return NULL; /* not reached */
+  }
+  return y;
+}
+
+static GEN
+gtovecpost(GEN x, long n)
+{
+  long i, imax, lx, tx = typ(x);
+  GEN y = zerovec(n);
+
+  if (is_scalar_t(tx) || tx == t_RFRAC) { gel(y,1) = gcopy(x); return y; }
+  switch(tx)
+  {
+    case t_POL:
+      lx=lg(x); imax = minss(lx-2, n);
+      for (i=1; i<=imax; i++) gel(y,i) = gcopy(gel(x,lx-i));
+      return y;
+    case t_SER:
+      lx=lg(x); imax = minss(lx-2, n); x++;
+      for (i=1; i<=imax; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    case t_QFR: case t_QFI: case t_VEC: case t_COL:
+      lx=lg(x); imax = minss(lx-1, n);
+      for (i=1; i<=imax; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    case t_LIST:
+      x = list_data(x); lx = x? lg(x): 1;
+      imax = minss(lx-1, n);
+      for (i=1; i<=imax; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    case t_VECSMALL:
+      lx=lg(x);
+      imax = minss(lx-1, n);
+      for (i=1; i<=imax; i++) gel(y,i) = stoi(x[i]);
+      return y;
+    default: pari_err_TYPE("gtovec",x);
+      return NULL; /*notreached*/
+  }
+}
+
+static GEN
+init_vectopre(long a, long n, GEN y, long *imax)
+{
+  *imax = minss(a, n);
+  return (n == *imax)?  y: y + n - a;
+}
+static GEN
+gtovecpre(GEN x, long n)
+{
+  long i, imax, lx, tx = typ(x);
+  GEN y = zerovec(n), y0;
+
+  if (is_scalar_t(tx) || tx == t_RFRAC) { gel(y,n) = gcopy(x); return y; }
+  switch(tx)
+  {
+    case t_POL:
+      lx=lg(x);
+      y0 = init_vectopre(lx-2, n, y, &imax);
+      for (i=1; i<=imax; i++) gel(y0,i) = gcopy(gel(x,lx-i));
+      return y;
+    case t_SER:
+      lx=lg(x); x++;
+      y0 = init_vectopre(lx-2, n, y, &imax);
+      for (i=1; i<=imax; i++) gel(y0,i) = gcopy(gel(x,i));
+      return y;
+    case t_QFR: case t_QFI: case t_VEC: case t_COL:
+      lx=lg(x);
+      y0 = init_vectopre(lx-1, n, y, &imax);
+      for (i=1; i<=imax; i++) gel(y0,i) = gcopy(gel(x,i));
+      return y;
+    case t_LIST:
+      x = list_data(x); lx = x? lg(x): 1;
+      y0 = init_vectopre(lx-1, n, y, &imax);
+      for (i=1; i<=imax; i++) gel(y0,i) = gcopy(gel(x,i));
+      return y;
+    case t_VECSMALL:
+      lx=lg(x);
+      y0 = init_vectopre(lx-1, n, y, &imax);
+      for (i=1; i<=imax; i++) gel(y0,i) = stoi(x[i]);
+      return y;
+    default: pari_err_TYPE("gtovec",x);
+      return NULL; /*notreached*/
+  }
+}
+GEN
+gtovec0(GEN x, long n)
+{
+  if (!n) return gtovec(x);
+  if (n > 0) return gtovecpost(x, n);
+  return gtovecpre(x, -n);
+}
+
+GEN
+gtovec(GEN x)
+{
+  long i, lx, tx = typ(x);
+  GEN y;
+
+  if (is_scalar_t(tx)) return mkveccopy(x);
+  switch(tx)
+  {
+    case t_POL:
+      lx=lg(x); y=cgetg(lx-1,t_VEC);
+      for (i=1; i<=lx-2; i++) gel(y,i) = gcopy(gel(x,lx-i));
+      return y;
+    case t_SER:
+      lx=lg(x); y=cgetg(lx-1,t_VEC); x++;
+      for (i=1; i<=lx-2; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    case t_RFRAC: return mkveccopy(x);
+    case t_QFR: case t_QFI: case t_VEC: case t_COL: case t_MAT:
+      lx=lg(x); y=cgetg(lx,t_VEC);
+      for (i=1; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    case t_LIST:
+      x = list_data(x); lx = x? lg(x): 1;
+      y = cgetg(lx, t_VEC);
+      for (i=1; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    case t_STR:
+    {
+      char *s = GSTR(x);
+      lx = strlen(s)+1; y = cgetg(lx, t_VEC);
+      s--;
+      for (i=1; i<lx; i++) gel(y,i) = chartoGENstr(s[i]);
+      return y;
+    }
+    case t_VECSMALL:
+      return vecsmall_to_vec(x);
+    case t_ERROR:
+      lx=lg(x); y = cgetg(lx,t_VEC);
+      gel(y,1) = errname(x);
+      for (i=2; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+      return y;
+    default: pari_err_TYPE("gtovec",x);
+      return NULL; /*notreached*/
+  }
+}
+
+GEN
+gtovecrev0(GEN x, long n)
+{
+  GEN y = gtovec0(x, n);
+  long ly = lg(y), lim = ly>>1, i;
+  for (i = 1; i <= lim; i++) swap(gel(y,i), gel(y,ly-i));
+  return y;
+}
+GEN
+gtovecrev(GEN x) { return gtovecrev0(x, 0); }
+
+GEN
+gtocol0(GEN x, long n)
+{
+  GEN y;
+  if (!n) return gtocol(x);
+  y = gtovec0(x, n);
+  settyp(y, t_COL); return y;
+}
+GEN
+gtocol(GEN x)
+{
+  long lx, tx, i, j, h;
+  GEN y;
+  tx = typ(x);
+  if (tx != t_MAT) { y = gtovec(x); settyp(y, t_COL); return y; }
+  lx = lg(x); if (lx == 1) return cgetg(1, t_COL);
+  h = lgcols(x); y = cgetg(h, t_COL);
+  for (i = 1 ; i < h; i++) {
+    gel(y,i) = cgetg(lx, t_VEC);
+    for (j = 1; j < lx; j++) gmael(y,i,j) = gcopy(gcoeff(x,i,j));
+  }
+  return y;
+}
+
+GEN
+gtocolrev0(GEN x, long n)
+{
+  GEN y = gtocol0(x, n);
+  long ly = lg(y), lim = ly>>1, i;
+  for (i = 1; i <= lim; i++) swap(gel(y,i), gel(y,ly-i));
+  return y;
+}
+GEN
+gtocolrev(GEN x) { return gtocolrev0(x, 0); }
+
+static long
+Itos(GEN x)
+{
+   if (typ(x) != t_INT) pari_err_TYPE("vectosmall",x);
+   return itos(x);
+}
+
+static GEN
+gtovecsmallpost(GEN x, long n)
+{
+  long i, imax, lx;
+  GEN y = zero_Flv(n);
+
+  switch(typ(x))
+  {
+    case t_INT:
+      y[1] = itos(x); return y;
+    case t_POL:
+      lx=lg(x); imax = minss(lx-2, n);
+      for (i=1; i<=imax; i++) y[i] = Itos(gel(x,lx-i));
+      return y;
+    case t_SER:
+      lx=lg(x); imax = minss(lx-2, n); x++;
+      for (i=1; i<=imax; i++) y[i] = Itos(gel(x,i));
+      return y;
+    case t_VEC: case t_COL:
+      lx=lg(x); imax = minss(lx-1, n);
+      for (i=1; i<=imax; i++) y[i] = Itos(gel(x,i));
+      return y;
+    case t_LIST:
+      x = list_data(x); lx = x? lg(x): 1;
+      imax = minss(lx-1, n);
+      for (i=1; i<=imax; i++) y[i] = Itos(gel(x,i));
+      return y;
+    case t_VECSMALL:
+      lx=lg(x);
+      imax = minss(lx-1, n);
+      for (i=1; i<=imax; i++) y[i] = x[i];
+      return y;
+    default: pari_err_TYPE("gtovecsmall",x);
+      return NULL; /*notreached*/
+  }
+}
+static GEN
+gtovecsmallpre(GEN x, long n)
+{
+  long i, imax, lx;
+  GEN y = zero_Flv(n), y0;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      y[n] = itos(x); return y;
+    case t_POL:
+      lx=lg(x);
+      y0 = init_vectopre(lx-2, n, y, &imax);
+      for (i=1; i<=imax; i++) y0[i] = Itos(gel(x,lx-i));
+      return y;
+    case t_SER:
+      lx=lg(x); x++;
+      y0 = init_vectopre(lx-2, n, y, &imax);
+      for (i=1; i<=imax; i++) y0[i] = Itos(gel(x,i));
+      return y;
+    case t_VEC: case t_COL:
+      lx=lg(x);
+      y0 = init_vectopre(lx-1, n, y, &imax);
+      for (i=1; i<=imax; i++) y0[i] = Itos(gel(x,i));
+      return y;
+    case t_LIST:
+      x = list_data(x); lx = x? lg(x): 1;
+      y0 = init_vectopre(lx-1, n, y, &imax);
+      for (i=1; i<=imax; i++) y0[i] = Itos(gel(x,i));
+      return y;
+    case t_VECSMALL:
+      lx=lg(x);
+      y0 = init_vectopre(lx-1, n, y, &imax);
+      for (i=1; i<=imax; i++) y0[i] = x[i];
+      return y;
+    default: pari_err_TYPE("gtovecsmall",x);
+      return NULL; /*notreached*/
+  }
+}
+
+GEN
+gtovecsmall0(GEN x, long n)
+{
+  if (!n) return gtovecsmall(x);
+  if (n > 0) return gtovecsmallpost(x, n);
+  return gtovecsmallpre(x, -n);
+}
+
+GEN
+gtovecsmall(GEN x)
+{
+  GEN V;
+  long l, i;
+
+  switch(typ(x))
+  {
+    case t_INT: return mkvecsmall(itos(x));
+    case t_STR:
+    {
+      char *s = GSTR(x);
+      l = strlen(s);
+      V = cgetg(l+1, t_VECSMALL);
+      s--;
+      for (i=1; i<=l; i++) V[i] = (long)s[i];
+      return V;
+    }
+    case t_VECSMALL: return leafcopy(x);
+    case t_LIST:
+      x = list_data(x);
+      if (!x) return cgetg(1, t_VECSMALL);
+      /* fall through */
+    case t_VEC: case t_COL:
+      l = lg(x); V = cgetg(l,t_VECSMALL);
+      for(i=1; i<l; i++) V[i] = Itos(gel(x,i));
+      return V;
+    case t_POL:
+      l = lg(x); V = cgetg(l-1,t_VECSMALL);
+      for (i=1; i<=l-2; i++) V[i] = Itos(gel(x,l-i));
+      return V;
+    case t_SER:
+      l = lg(x); V = cgetg(l-1,t_VECSMALL); x++;
+      for (i=1; i<=l-2; i++) V[i] = Itos(gel(x,i));
+      return V;
+    default:
+      pari_err_TYPE("vectosmall",x);
+      return NULL; /* not reached */
+  }
+}
+
+GEN
+compo(GEN x, long n)
+{
+  long tx = typ(x);
+  ulong l, lx = (ulong)lg(x);
+
+  if (!is_recursive_t(tx))
+  {
+    if (tx == t_VECSMALL)
+    {
+      if (n < 1) pari_err_COMPONENT("", "<", gen_1, stoi(n));
+      if ((ulong)n >= lx) pari_err_COMPONENT("", ">", utoi(lx-1), stoi(n));
+      return stoi(x[n]);
+    }
+    pari_err_TYPE("component [leaf]", x);
+  }
+  if (n < 1) pari_err_COMPONENT("", "<", gen_1, stoi(n));
+  if (tx == t_POL && (ulong)n+1 >= lx) return gen_0;
+  if (tx == t_LIST) {
+    tx = t_VEC;
+    x = list_data(x);
+    lx = (ulong)(x? lg(x): 1);
+  }
+  l = (ulong)lontyp[tx] + (ulong)n-1; /* beware overflow */
+  if (l >= lx) pari_err_COMPONENT("", ">", utoi(lx-1), utoi(l));
+  return gcopy(gel(x,l));
+}
+
+/* assume v > varn(x), extract coeff of pol_x(v)^n */
+static GEN
+multi_coeff(GEN x, long n, long v, long dx)
+{
+  long i, lx = dx+3;
+  GEN z = cgetg(lx, t_POL); z[1] = x[1];
+  for (i = 2; i < lx; i++) gel(z,i) = polcoeff_i(gel(x,i), n, v);
+  z = normalizepol_lg(z, lx);
+  switch(lg(z))
+  {
+    case 2: z = gen_0; break;
+    case 3: z = gel(z,2); break;
+  }
+  return z;
+}
+
+/* assume x a t_POL */
+static GEN
+_polcoeff(GEN x, long n, long v)
+{
+  long w, dx;
+  dx = degpol(x);
+  if (dx < 0) return gen_0;
+  if (v < 0 || v == (w=varn(x)))
+    return (n < 0 || n > dx)? gen_0: gel(x,n+2);
+  if (varncmp(w,v) > 0) return n? gen_0: x;
+  /* w < v */
+  return multi_coeff(x, n, v, dx);
+}
+
+/* assume x a t_SER */
+static GEN
+_sercoeff(GEN x, long n, long v)
+{
+  long w, dx = degpol(x), ex = valp(x), N = n - ex;
+  GEN z;
+  if (dx < 0)
+  {
+    if (N >= 0) pari_err_DOMAIN("polcoeff", "t_SER", "=", x, x);
+    return gen_0;
+  }
+  if (v < 0 || v == (w=varn(x)))
+  {
+    if (N > dx)
+      pari_err_DOMAIN("polcoeff", "degree", ">", stoi(dx+ex), stoi(n));
+    return (N < 0)? gen_0: gel(x,N+2);
+  }
+  if (varncmp(w,v) > 0) return N? gen_0: x;
+  /* w < v */
+  z = multi_coeff(x, n, v, dx);
+  if (ex) z = gmul(z, monomial(gen_1,ex, w));
+  return z;
+}
+
+/* assume x a t_RFRAC(n) */
+static GEN
+_rfraccoeff(GEN x, long n, long v)
+{
+  GEN P,Q, p = gel(x,1), q = gel(x,2);
+  long vp = gvar(p), vq = gvar(q);
+  if (v < 0) v = minss(vp, vq);
+  P = (vp == v)? p: swap_vars(p, v);
+  Q = (vq == v)? q: swap_vars(q, v);
+  if (!RgX_is_monomial(Q)) pari_err_TYPE("polcoeff", x);
+  n += degpol(Q);
+  return gdiv(_polcoeff(P, n, v), leading_term(Q));
+}
+
+GEN
+polcoeff_i(GEN x, long n, long v)
+{
+  switch(typ(x))
+  {
+    case t_POL: return _polcoeff(x,n,v);
+    case t_SER: return _sercoeff(x,n,v);
+    case t_RFRAC: return _rfraccoeff(x,n,v);
+    default: return n? gen_0: x;
+  }
+}
+
+/* with respect to the main variable if v<0, with respect to the variable v
+   otherwise. v ignored if x is not a polynomial/series. */
+GEN
+polcoeff0(GEN x, long n, long v)
+{
+  long lx, tx = typ(x);
+  pari_sp av;
+
+  if (is_scalar_t(tx)) return n? gen_0: gcopy(x);
+  av = avma;
+  switch(tx)
+  {
+    case t_POL: x = _polcoeff(x,n,v); break;
+    case t_SER: x = _sercoeff(x,n,v); break;
+    case t_RFRAC: x = _rfraccoeff(x,n,v); break;
+
+    case t_QFR: case t_QFI: case t_VEC: case t_COL: case t_MAT:
+      if (n < 1) pari_err_COMPONENT("polcoeff","<",gen_1,stoi(n));
+      lx = lg(x);
+      if (n >= lx) pari_err_COMPONENT("polcoeff",">",stoi(lx-1),stoi(n));
+      return gcopy(gel(x,n));
+
+    default: pari_err_TYPE("polcoeff", x);
+  }
+  if (x == gen_0) return x;
+  if (avma == av) return gcopy(x);
+  return gerepilecopy(av, x);
+}
+
+static GEN
+vecdenom(GEN v, long imin, long imax)
+{
+  pari_sp av = avma;
+  long i = imin;
+  GEN s;
+  if (imin > imax) return gen_1;
+  s = denom(gel(v,i));
+  for (i++; i<=imax; i++)
+  {
+    GEN t = denom(gel(v,i));
+    if (t != gen_1) s = glcm(s,t);
+  }
+  return gerepileupto(av, s);
+}
+GEN
+denom(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT:
+    case t_REAL:
+    case t_INTMOD:
+    case t_FFELT:
+    case t_PADIC:
+    case t_SER: return gen_1;
+    case t_FRAC: return icopy(gel(x,2));
+    case t_COMPLEX: return vecdenom(x,1,2);
+    case t_QUAD: return vecdenom(x,2,3);
+    case t_POLMOD: return denom(gel(x,2));
+    case t_RFRAC: return RgX_copy(gel(x,2));
+    case t_POL: return pol_1(varn(x));
+    case t_VEC: case t_COL: case t_MAT: return vecdenom(x, 1, lg(x)-1);
+  }
+  pari_err_TYPE("denom",x);
+  return NULL; /* not reached */
+}
+
+GEN
+numer(GEN x)
+{
+  pari_sp av;
+  switch(typ(x))
+  {
+    case t_INT:
+    case t_REAL: return mpcopy(x);
+    case t_INTMOD:
+    case t_FFELT:
+    case t_PADIC:
+    case t_SER: return gcopy(x);
+    case t_POL: return RgX_copy(x);
+    case t_FRAC: return icopy(gel(x,1));
+    case t_POLMOD:
+      av = avma; return gerepileupto(av, gmodulo(numer(gel(x,2)), gel(x,1)));
+    case t_RFRAC: return gcopy(gel(x,1));
+    case t_COMPLEX:
+    case t_QUAD:
+    case t_VEC:
+    case t_COL:
+    case t_MAT:
+      av = avma; return gerepileupto(av, gmul(denom(x),x));
+  }
+  pari_err_TYPE("numer",x);
+  return NULL; /* not reached */
+}
+
+/* Lift only intmods if v does not occur in x, lift with respect to main
+ * variable of x if v < 0, with respect to variable v otherwise.
+ */
+GEN
+lift0(GEN x, long v)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_INTMOD: return v < 0? icopy(gel(x,2)): gcopy(x);
+    case t_POLMOD:
+      if (v < 0 || v == varn(gel(x,1))) return gcopy(gel(x,2));
+      y = cgetg(3, t_POLMOD);
+      gel(y,1) = lift0(gel(x,1),v);
+      gel(y,2) = lift0(gel(x,2),v); return y;
+    case t_PADIC: return v < 0? padic_to_Q(x): gcopy(x);
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = lift0(gel(x,i), v);
+      return normalizepol_lg(y,lx);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = lift0(gel(x,i), v);
+      return normalize(y);
+    case t_COMPLEX: case t_QUAD: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = lift0(gel(x,i), v);
+      return y;
+    default: return gcopy(x);
+  }
+}
+GEN
+lift(GEN x) { return lift0(x,-1); }
+
+GEN
+liftall_shallow(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INTMOD: return gel(x,2);
+    case t_POLMOD:
+      return liftall_shallow(gel(x,2));
+    case t_PADIC: return padic_to_Q(x);
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = liftall_shallow(gel(x,i));
+      return normalizepol_lg(y,lx);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = liftall_shallow(gel(x,i));
+      return normalize(y);
+    case t_COMPLEX: case t_QUAD: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = liftall_shallow(gel(x,i));
+      return y;
+    default: return x;
+  }
+}
+GEN
+liftall(GEN x)
+{ pari_sp av = avma;
+  GEN z = liftall_shallow(x);
+  return z==x ? gcopy(z): gerepilecopy(av, z); }
+
+GEN
+liftint_shallow(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INTMOD: return gel(x,2);
+    case t_PADIC: return padic_to_Q(x);
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = liftint_shallow(gel(x,i));
+      return normalizepol_lg(y,lx);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = liftint_shallow(gel(x,i));
+      return normalize(y);
+    case t_POLMOD: case t_COMPLEX: case t_QUAD: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = liftint_shallow(gel(x,i));
+      return y;
+    default: return x;
+  }
+}
+GEN
+liftint(GEN x)
+{ pari_sp av = avma;
+  GEN z = liftint_shallow(x);
+  return z==x ? gcopy(z): gerepilecopy(av, z); }
+
+GEN
+liftpol_shallow(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_POLMOD:
+      return liftpol_shallow(gel(x,2));
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = liftpol_shallow(gel(x,i));
+      return normalizepol_lg(y,lx);
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = liftpol_shallow(gel(x,i));
+      return normalize(y);
+    case t_RFRAC: case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = liftpol_shallow(gel(x,i));
+      return y;
+    default: return x;
+  }
+}
+GEN
+liftpol(GEN x)
+{ pari_sp av = avma;
+  GEN z = liftpol_shallow(x);
+  return z==x ? gcopy(z): gerepilecopy(av, z); }
+
+/* same as lift, without copy. May DESTROY x. For internal use only */
+GEN
+lift_intern(GEN x)
+{
+  long i;
+  switch(typ(x))
+  {
+    case t_INTMOD: case t_POLMOD: return gel(x,2);
+    case t_PADIC: return padic_to_Q(x);
+    case t_SER:
+      for (i = lg(x)-1; i>=2; i--) gel(x,i) = lift_intern(gel(x,i));
+      return normalize(x);
+    case t_POL:
+      for (i = lg(x)-1; i>=2; i--) gel(x,i) = lift_intern(gel(x,i));
+      return normalizepol(x);
+    case t_COMPLEX: case t_QUAD: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      for (i = lg(x)-1; i>=1; i--) gel(x,i) = lift_intern(gel(x,i));
+      return x;
+    default: return x;
+  }
+}
+
+static GEN
+centerliftii(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long i = cmpii(shifti(x,1), y);
+  avma = av; return (i > 0)? subii(x,y): icopy(x);
+}
+
+/* see lift0 */
+GEN
+centerlift0(GEN x, long v)
+{ return v < 0? centerlift(x): lift0(x,v); }
+GEN
+centerlift(GEN x)
+{
+  long i, v, lx;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_INT: return icopy(x);
+    case t_INTMOD: return centerliftii(gel(x,2), gel(x,1));
+    case t_POLMOD: return gcopy(gel(x,2));
+   case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = centerlift(gel(x,i));
+      return normalizepol_lg(y,lx);
+   case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = centerlift(gel(x,i));
+      return normalize(y);
+   case t_COMPLEX: case t_QUAD: case t_RFRAC:
+   case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = centerlift(gel(x,i));
+      return y;
+    case t_PADIC:
+      if (!signe(gel(x,4))) return gen_0;
+      v = valp(x);
+      if (v>=0)
+      { /* here p^v is an integer */
+        GEN z =  centerliftii(gel(x,4), gel(x,3));
+        pari_sp av;
+        if (!v) return z;
+        av = avma; y = powiu(gel(x,2),v);
+        return gerepileuptoint(av, mulii(y,z));
+      }
+      y = cgetg(3,t_FRAC);
+      gel(y,1) = centerliftii(gel(x,4), gel(x,3));
+      gel(y,2) = powiu(gel(x,2),-v);
+      return y;
+    default: return gcopy(x);
+  }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                      REAL & IMAGINARY PARTS                     */
+/*                                                                 */
+/*******************************************************************/
+
+static GEN
+op_ReIm(GEN f(GEN), GEN x)
+{
+  long lx, i, j;
+  pari_sp av;
+  GEN z;
+
+  switch(typ(x))
+  {
+    case t_POL:
+      z = cgetg_copy(x, &lx); z[1] = x[1];
+      for (j=2; j<lx; j++) gel(z,j) = f(gel(x,j));
+      return normalizepol_lg(z, lx);
+
+    case t_SER:
+      z = cgetg_copy(x, &lx); z[1] = x[1];
+      for (j=2; j<lx; j++) gel(z,j) = f(gel(x,j));
+      return normalize(z);
+
+    case t_RFRAC: {
+      GEN dxb, n, d;
+      av = avma; dxb = gconj(gel(x,2));
+      n = gmul(gel(x,1), dxb);
+      d = gmul(gel(x,2), dxb);
+      return gerepileupto(av, gdiv(f(n), d));
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(z,i) = f(gel(x,i));
+      return z;
+  }
+  pari_err_TYPE("greal/gimag",x);
+  return NULL; /* not reached */
+}
+
+GEN
+real_i(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return x;
+    case t_COMPLEX:
+      return gel(x,1);
+    case t_QUAD:
+      return gel(x,2);
+  }
+  return op_ReIm(real_i,x);
+}
+GEN
+imag_i(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return gen_0;
+    case t_COMPLEX:
+      return gel(x,2);
+    case t_QUAD:
+      return gel(x,3);
+  }
+  return op_ReIm(imag_i,x);
+}
+GEN
+greal(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL:
+      return mpcopy(x);
+
+    case t_FRAC:
+      return gcopy(x);
+
+    case t_COMPLEX:
+      return gcopy(gel(x,1));
+
+    case t_QUAD:
+      return gcopy(gel(x,2));
+  }
+  return op_ReIm(greal,x);
+}
+GEN
+gimag(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC:
+      return gen_0;
+
+    case t_COMPLEX:
+      return gcopy(gel(x,2));
+
+    case t_QUAD:
+      return gcopy(gel(x,3));
+  }
+  return op_ReIm(gimag,x);
+}
+
+/* return Re(x * y), x and y scalars */
+GEN
+mulreal(GEN x, GEN y)
+{
+  if (typ(x) == t_COMPLEX)
+  {
+    if (typ(y) == t_COMPLEX)
+    {
+      pari_sp av = avma;
+      GEN a = gmul(gel(x,1), gel(y,1));
+      GEN b = gmul(gel(x,2), gel(y,2));
+      return gerepileupto(av, gsub(a, b));
+    }
+    x = gel(x,1);
+  }
+  else
+    if (typ(y) == t_COMPLEX) y = gel(y,1);
+  return gmul(x,y);
+}
+/* Compute Re(x * y), x and y matrices of compatible dimensions
+ * assume scalar entries */
+GEN
+RgM_mulreal(GEN x, GEN y)
+{
+  long i, j, k, l, lx = lg(x), ly = lg(y);
+  GEN z = cgetg(ly,t_MAT);
+  l = (lx == 1)? 1: lgcols(x);
+  for (j=1; j<ly; j++)
+  {
+    GEN zj = cgetg(l,t_COL), yj = gel(y,j);
+    gel(z,j) = zj;
+    for (i=1; i<l; i++)
+    {
+      pari_sp av = avma;
+      GEN c = mulreal(gcoeff(x,i,1),gel(yj,1));
+      for (k=2; k<lx; k++) c = gadd(c, mulreal(gcoeff(x,i,k),gel(yj,k)));
+      gel(zj, i) = gerepileupto(av, c);
+    }
+  }
+  return z;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     LOGICAL OPERATIONS                          */
+/*                                                                 */
+/*******************************************************************/
+static long
+_egal(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long r = gequal(simplify_shallow(x), simplify_shallow(y));
+  avma = av; return r;
+}
+
+GEN
+glt(GEN x, GEN y) { return gcmp(x,y)<0? gen_1: gen_0; }
+
+GEN
+gle(GEN x, GEN y) { return gcmp(x,y)<=0? gen_1: gen_0; }
+
+GEN
+gge(GEN x, GEN y) { return gcmp(x,y)>=0? gen_1: gen_0; }
+
+GEN
+ggt(GEN x, GEN y) { return gcmp(x,y)>0? gen_1: gen_0; }
+
+GEN
+geq(GEN x, GEN y) { return _egal(x,y)? gen_1: gen_0; }
+
+GEN
+gne(GEN x, GEN y) { return _egal(x,y)? gen_0: gen_1; }
+
+GEN
+gnot(GEN x) { return gequal0(x)? gen_1: gen_0; }
+
+/*******************************************************************/
+/*                                                                 */
+/*                      FORMAL SIMPLIFICATIONS                     */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+geval_gp(GEN x, GEN t)
+{
+  long lx, i, tx = typ(x);
+  pari_sp av;
+  GEN y, z;
+
+  if (is_const_t(tx)) return gcopy(x);
+  switch(tx)
+  {
+    case t_STR:
+      return localvars_read_str(GSTR(x),t);
+
+    case t_POLMOD:
+      av = avma;
+      return gerepileupto(av, gmodulo(geval_gp(gel(x,2),t),
+                                      geval_gp(gel(x,1),t)));
+
+    case t_POL:
+      lx=lg(x); if (lx==2) return gen_0;
+      z = fetch_var_value(varn(x),t);
+      if (!z) return RgX_copy(x);
+      av = avma; y = geval_gp(gel(x,lx-1),t);
+      for (i=lx-2; i>1; i--)
+        y = gadd(geval_gp(gel(x,i),t), gmul(z,y));
+      return gerepileupto(av, y);
+
+    case t_SER:
+      pari_err_IMPL( "evaluation of a power series");
+
+    case t_RFRAC:
+      av = avma;
+      return gerepileupto(av, gdiv(geval_gp(gel(x,1),t), geval_gp(gel(x,2),t)));
+
+    case t_QFR: case t_QFI: case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = geval_gp(gel(x,i),t);
+      return y;
+
+    case t_CLOSURE:
+      if (closure_arity(x)) pari_err_IMPL("eval on functions with parameters");
+      return closure_evalres(x);
+  }
+  pari_err_TYPE("geval",x);
+  return NULL; /* not reached */
+}
+GEN
+geval(GEN x) { return geval_gp(x,NULL); }
+
+GEN
+simplify_shallow(GEN x)
+{
+  long i, lx;
+  GEN y, z;
+  if (!x) pari_err_BUG("simplify, NULL input");
+
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_INTMOD: case t_FRAC: case t_FFELT:
+    case t_PADIC: case t_QFR: case t_QFI: case t_LIST: case t_STR: case t_VECSMALL:
+    case t_CLOSURE: case t_ERROR:
+      return x;
+    case t_COMPLEX: return isintzero(gel(x,2))? gel(x,1): x;
+    case t_QUAD:    return isintzero(gel(x,3))? gel(x,2): x;
+
+    case t_POLMOD: y = cgetg(3,t_POLMOD);
+      z = simplify_shallow(gel(x,1));
+      if (typ(z) != t_POL) z = scalarpol(z, varn(gel(x,1)));
+      gel(y,1) = z; /* z must be a t_POL: invalid object otherwise */
+      gel(y,2) = simplify_shallow(gel(x,2)); return y;
+
+    case t_POL:
+      lx = lg(x);
+      if (lx==2) return gen_0;
+      if (lx==3) return simplify_shallow(gel(x,2));
+      y = cgetg(lx,t_POL); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = simplify_shallow(gel(x,i));
+      return y;
+
+    case t_SER:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = simplify_shallow(gel(x,i));
+      return y;
+
+    case t_RFRAC: y = cgetg(3,t_RFRAC);
+      gel(y,1) = simplify_shallow(gel(x,1));
+      z = simplify_shallow(gel(x,2));
+      if (typ(z) != t_POL) return gdiv(gel(y,1), z);
+      gel(y,2) = z; return y;
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &lx);
+      for (i=1; i<lx; i++) gel(y,i) = simplify_shallow(gel(x,i));
+      return y;
+  }
+  pari_err_BUG("simplify_shallow, type unknown");
+  return NULL; /* not reached */
+}
+
+GEN
+simplify(GEN x)
+{
+  pari_sp av = avma;
+  GEN y = simplify_shallow(x);
+  return av == avma ? gcopy(y): gerepilecopy(av, y);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                EVALUATION OF SOME SIMPLE OBJECTS                */
+/*                                                                 */
+/*******************************************************************/
+/* q is a real symetric matrix, x a RgV. Horner-type evaluation of q(x)
+ * using (n^2+3n-2)/2 mul */
+GEN
+qfeval(GEN q, GEN x)
+{
+  pari_sp av = avma;
+  long i, j, l = lg(q);
+  GEN z;
+  if (lg(x) != l) pari_err_DIM("qfeval");
+  if (l==1) return gen_0;
+  if (lgcols(q) != l) pari_err_DIM("qfeval");
+  /* l = lg(x) = lg(q) > 1 */
+  z = gmul(gcoeff(q,1,1), gsqr(gel(x,1)));
+  for (i=2; i<l; i++)
+  {
+    GEN c = gel(q,i), s;
+    if (isintzero(gel(x,i))) continue;
+    s = gmul(gel(c,1), gel(x,1));
+    for (j=2; j<i; j++) s = gadd(s, gmul(gel(c,j),gel(x,j)));
+    s = gadd(gshift(s,1), gmul(gel(c,i),gel(x,i)));
+    z = gadd(z, gmul(gel(x,i), s));
+  }
+  return gerepileupto(av,z);
+}
+GEN
+qfnorm(GEN x, GEN q)
+{
+  if (!q) switch(typ(x))
+  {
+    case t_VEC: case t_COL: return RgV_dotsquare(x);
+    case t_MAT: return gram_matrix(x);
+    default: pari_err_TYPE("qfnorm",x);
+  }
+  if (typ(q) != t_MAT) pari_err_TYPE("qfnorm",q);
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL: break;
+    case t_MAT: return qf_apply_RgM(q, x);
+    default: pari_err_TYPE("qfnorm",x);
+  }
+  return qfeval(q,x);
+}
+/* assume q is a real symetric matrix, q(x,y) using n^2+n mul */
+GEN
+qfevalb(GEN q, GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long l = lg(q);
+  if (lg(x) != l || lg(y) != l) pari_err_DIM("qfevalb");
+  return gerepileupto(av, RgV_dotproduct(RgV_RgM_mul(x,q), y));
+}
+GEN
+qfbil(GEN x, GEN y, GEN q)
+{
+  if (!is_vec_t(typ(x))) pari_err_TYPE("qfbil",x);
+  if (!is_vec_t(typ(y))) pari_err_TYPE("qfbil",y);
+  if (!q) {
+    if (lg(x) != lg(y)) pari_err_DIM("qfbil");
+    return RgV_dotproduct(x,y);
+  }
+  if (typ(q) != t_MAT) pari_err_TYPE("qfbil",q);
+  return qfevalb(q,x,y);
+}
+
+/* q a hermitian complex matrix, x a RgV */
+GEN
+hqfeval(GEN q, GEN x)
+{
+  pari_sp av = avma;
+  long i, j, l = lg(q);
+  GEN z, xc;
+
+  if (lg(x) != l) pari_err_DIM("hqfeval");
+  if (l==1) return gen_0;
+  if (lgcols(q) != l) pari_err_DIM("hqfeval");
+  if (l == 2) return gerepileupto(av, gmul(gcoeff(q,1,1), gnorm(gel(x,1))));
+  /* l = lg(x) = lg(q) > 2 */
+  xc = gconj(x);
+  z = mulreal(gcoeff(q,2,1), gmul(gel(x,2),gel(xc,1)));
+  for (i=3;i<l;i++)
+    for (j=1;j<i;j++)
+      z = gadd(z, mulreal(gcoeff(q,i,j), gmul(gel(x,i),gel(xc,j))));
+  z = gshift(z,1);
+  for (i=1;i<l;i++) z = gadd(z, gmul(gcoeff(q,i,i), gnorm(gel(x,i))));
+  return gerepileupto(av,z);
+}
+
+static void
+init_qf_apply(GEN q, GEN M, long *l)
+{
+  long k = lg(M);
+  *l = lg(q);
+  if (*l == 1) { if (k == 1) return; }
+  else         { if (k != 1 && lgcols(M) == *l) return; }
+  pari_err_DIM("qf_apply_RgM");
+}
+/* Return X = M'.q.M, assuming q is a symetric matrix and M is a
+ * matrix of compatible dimensions. X_ij are X_ji identical, not copies */
+GEN
+qf_apply_RgM(GEN q, GEN M)
+{
+  pari_sp av = avma;
+  long l; init_qf_apply(q, M, &l); if (l == 1) return cgetg(1, t_MAT);
+  return gerepileupto(av, RgM_transmultosym(M, RgM_mul(q, M)));
+}
+GEN
+qf_apply_ZM(GEN q, GEN M)
+{
+  pari_sp av = avma;
+  long l; init_qf_apply(q, M, &l); if (l == 1) return cgetg(1, t_MAT);
+  return gerepileupto(av, ZM_transmultosym(M, ZM_mul(q, M)));
+}
+
+GEN
+poleval(GEN x, GEN y)
+{
+  long i, j, imin, tx = typ(x);
+  pari_sp av0 = avma, av, lim;
+  GEN p1, p2, r, s;
+
+  if (is_scalar_t(tx)) return gcopy(x);
+  switch(tx)
+  {
+    case t_POL:
+      i = lg(x)-1; imin = 2; break;
+
+    case t_RFRAC:
+      p1 = poleval(gel(x,1),y);
+      p2 = poleval(gel(x,2),y);
+      return gerepileupto(av0, gdiv(p1,p2));
+
+    case t_VEC: case t_COL:
+      i = lg(x)-1; imin = 1; break;
+    default: pari_err_TYPE("poleval",x);
+      return NULL; /* not reached */
+  }
+  if (i<=imin)
+    return (i==imin)? gcopy(gel(x,imin)): gen_0;
+
+  lim = stack_lim(av0,2);
+  p1 = gel(x,i); i--;
+  if (typ(y)!=t_COMPLEX)
+  {
+#if 0 /* standard Horner's rule */
+    for ( ; i>=imin; i--)
+      p1 = gadd(gmul(p1,y),gel(x,i));
+#endif
+    /* specific attention to sparse polynomials */
+    for ( ; i>=imin; i=j-1)
+    {
+      for (j=i; isexactzero(gel(x,j)); j--)
+        if (j==imin)
+        {
+          if (i!=j) y = gpowgs(y, i-j+1);
+          return gerepileupto(av0, gmul(p1,y));
+        }
+      r = (i==j)? y: gpowgs(y, i-j+1);
+      p1 = gadd(gmul(p1,r), gel(x,j));
+      if (low_stack(lim, stack_lim(av0,2)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"poleval: i = %ld",i);
+        p1 = gerepileupto(av0, p1);
+      }
+    }
+    return gerepileupto(av0,p1);
+  }
+
+  p2 = gel(x,i); i--; r = gtrace(y); s = gneg_i(gnorm(y));
+  av = avma;
+  for ( ; i>=imin; i--)
+  {
+    GEN p3 = gadd(p2, gmul(r, p1));
+    p2 = gadd(gel(x,i), gmul(s, p1)); p1 = p3;
+    if (low_stack(lim, stack_lim(av0,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"poleval: i = %ld",i);
+      gerepileall(av, 2, &p1, &p2);
+    }
+  }
+  return gerepileupto(av0, gadd(p2, gmul(y,p1)));
+}
diff --git a/src/basemath/hnf_snf.c b/src/basemath/hnf_snf.c
new file mode 100644
index 0000000..60a5610
--- /dev/null
+++ b/src/basemath/hnf_snf.c
@@ -0,0 +1,2698 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+/**************************************************************/
+/**                                                          **/
+/**                HERMITE NORMAL FORM REDUCTION             **/
+/**                                                          **/
+/**************************************************************/
+static GEN ZV_hnfgcdext(GEN A);
+static GEN
+hnfallgen(GEN x)
+{
+  GEN z = cgetg(3, t_VEC);
+  gel(z,1) = RgM_hnfall(x, (GEN*)(z+2), 1);
+  return z;
+}
+GEN
+mathnf0(GEN x, long flag)
+{
+  switch(typ(x))
+  {
+    case t_VEC:
+      if (RgV_is_ZV(x))
+        switch (flag)
+        {
+          case 0:
+            if (lg(x) == 1) return cgetg(1, t_MAT);
+            retmkmat(mkcol(ZV_content(x)));
+          case 1:
+          case 4:
+            return ZV_hnfgcdext(x);
+        }
+      x = gtomat(x); break;
+    case t_MAT: break;
+    default: pari_err_TYPE("mathnf0",x);
+  }
+
+  switch(flag)
+  {
+    case 0: return RgM_is_ZM(x)? ZM_hnf(x): RgM_hnfall(x,NULL,1);
+    case 1: return RgM_is_ZM(x)? hnfall(x): hnfallgen(x);
+    case 2: return RgM_hnfall(x, NULL, 1);
+    case 3: return hnfallgen(x);
+    case 4: RgM_check_ZM(x, "mathnf0"); return hnflll(x);
+    case 5: RgM_check_ZM(x, "mathnf0"); return hnfperm(x);
+    default: pari_err_FLAG("mathnf");
+  }
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                SPECIAL HNF (FOR INTERNAL USE !!!)               */
+/*                                                                 */
+/*******************************************************************/
+static int
+count(GEN mat, long row, long len, long *firstnonzero)
+{
+  long j, n = 0;
+
+  for (j=1; j<=len; j++)
+  {
+    long p = mael(mat,j,row);
+    if (p)
+    {
+      if (labs(p)!=1) return -1;
+      n++; *firstnonzero=j;
+    }
+  }
+  return n;
+}
+
+static int
+count2(GEN mat, long row, long len)
+{
+  long j;
+  for (j=len; j; j--)
+    if (labs(mael(mat,j,row)) == 1) return j;
+  return 0;
+}
+
+static GEN
+hnffinal(GEN matgen,GEN perm,GEN* ptdep,GEN* ptB,GEN* ptC)
+{
+  GEN p1,p2,U,H,Hnew,Bnew,Cnew,diagH1;
+  GEN B = *ptB, C = *ptC, dep = *ptdep, depnew;
+  pari_sp av, lim;
+  long i,j,k,s,i1,j1,zc;
+  long co = lg(C);
+  long col = lg(matgen)-1;
+  long lnz, nlze, lig;
+
+  if (col == 0) return matgen;
+  lnz = nbrows(matgen); /* was called lnz-1 - nr in hnfspec */
+  nlze = nbrows(dep);
+  lig = nlze + lnz;
+  /* H: lnz x lnz [disregarding initial 0 cols], U: col x col */
+  H = ZM_hnflll(matgen, &U, 0);
+  H += (lg(H)-1 - lnz); H[0] = evaltyp(t_MAT) | evallg(lnz+1);
+  /* Only keep the part above the H (above the 0s is 0 since the dep rows
+   * are dependent from the ones in matgen) */
+  zc = col - lnz; /* # of 0 columns, correspond to units */
+  if (nlze) { dep = ZM_mul(dep,U); dep += zc; }
+
+  diagH1 = new_chunk(lnz+1); /* diagH1[i] = 0 iff H[i,i] != 1 (set later) */
+
+  av = avma; lim = stack_lim(av,1);
+  Cnew = cgetg(co, typ(C));
+  setlg(C, col+1); p1 = gmul(C,U);
+  for (j=1; j<=col; j++) gel(Cnew,j) = gel(p1,j);
+  for (   ; j<co ; j++)  gel(Cnew,j) = gel(C,j);
+
+  /* Clean up B using new H */
+  for (s=0,i=lnz; i; i--)
+  {
+    GEN Di = gel(dep,i), Hi = gel(H,i);
+    GEN h = gel(Hi,i); /* H[i,i] */
+    if ( (diagH1[i] = is_pm1(h)) ) { h = NULL; s++; }
+    for (j=col+1; j<co; j++)
+    {
+      GEN z = gel(B,j-col);
+      p1 = gel(z,i+nlze);
+      if (h) p1 = truedivii(p1,h);
+      if (!signe(p1)) continue;
+      for (k=1; k<=nlze; k++) gel(z,k) = subii(gel(z,k), mulii(p1, gel(Di,k)));
+      for (   ; k<=lig;  k++) gel(z,k) = subii(gel(z,k), mulii(p1, gel(Hi,k-nlze)));
+      gel(Cnew,j) = gsub(gel(Cnew,j), gmul(p1, gel(Cnew,i+zc)));
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"hnffinal, i = %ld",i);
+      gerepileall(av, 2, &Cnew, &B);
+    }
+  }
+  p1 = cgetg(lnz+1,t_VEC); p2 = perm + nlze;
+  for (i1=0, j1=lnz-s, i=1; i<=lnz; i++) /* push the 1 rows down */
+    if (diagH1[i])
+      gel(p1,++j1) = gel(p2,i);
+    else
+      gel(p2,++i1) = gel(p2,i);
+  for (i=i1+1; i<=lnz; i++) gel(p2,i) = gel(p1,i);
+
+  /* s = # extra redundant generators taken from H
+   *          zc  col-s  co   zc = col - lnz
+   *       [ 0 |dep |     ]    i = nlze + lnz - s = lig - s
+   *  nlze [--------|  B' ]
+   *       [ 0 | H' |     ]    H' = H minus the s rows with a 1 on diagonal
+   *     i [--------|-----] lig-s           (= "1-rows")
+   *       [   0    | Id  ]
+   *       [        |     ] li */
+  lig -= s; col -= s; lnz -= s;
+  Hnew = cgetg(lnz+1,t_MAT);
+  depnew = cgetg(lnz+1,t_MAT); /* only used if nlze > 0 */
+  Bnew = cgetg(co-col,t_MAT);
+  C = shallowcopy(Cnew);
+  for (j=1,i1=j1=0; j<=lnz+s; j++)
+  {
+    GEN z = gel(H,j);
+    if (diagH1[j])
+    { /* hit exactly s times */
+      i1++; C[i1+col] = Cnew[j+zc];
+      p1 = cgetg(lig+1,t_COL); gel(Bnew,i1) = p1;
+      for (i=1; i<=nlze; i++) gel(p1,i) = gcoeff(dep,i,j);
+      p1 += nlze;
+    }
+    else
+    {
+      j1++; C[j1+zc] = Cnew[j+zc];
+      p1 = cgetg(lnz+1,t_COL); gel(Hnew,j1) = p1;
+      depnew[j1] = dep[j];
+    }
+    for (i=k=1; k<=lnz; i++)
+      if (!diagH1[i]) p1[k++] = z[i];
+  }
+  for (j=s+1; j<co-col; j++)
+  {
+    GEN z = gel(B,j-s);
+    p1 = cgetg(lig+1,t_COL); gel(Bnew,j) = p1;
+    for (i=1; i<=nlze; i++) gel(p1,i) = gel(z,i);
+    z += nlze; p1 += nlze;
+    for (i=k=1; k<=lnz; i++)
+      if (!diagH1[i]) gel(p1,k++) = gel(z,i);
+  }
+  *ptdep = depnew;
+  *ptC = C;
+  *ptB = Bnew; return Hnew;
+}
+
+/* for debugging */
+static void
+p_mat(GEN mat, GEN perm, long k)
+{
+  pari_sp av = avma;
+  perm = vecslice(perm, k+1, lg(perm)-1);
+  err_printf("Permutation: %Ps\n",perm);
+  if (DEBUGLEVEL > 6)
+    err_printf("matgen = %Ps\n", zm_to_ZM( rowpermute(mat, perm) ));
+  avma = av;
+}
+
+static GEN
+col_dup(long l, GEN col)
+{
+  GEN c = new_chunk(l);
+  memcpy(c,col,l * sizeof(long)); return c;
+}
+
+/* HNF reduce a relation matrix (column operations + row permutation)
+** Input:
+**   mat = (li-1) x (co-1) matrix of long
+**   C   = r x (co-1) matrix of GEN
+**   perm= permutation vector (length li-1), indexing the rows of mat: easier
+**     to maintain perm than to copy rows. For columns we can do it directly
+**     using e.g. swap(mat[i], mat[j])
+**   k0 = integer. The k0 first lines of mat are dense, the others are sparse.
+** Output: cf ASCII art in the function body
+**
+** row permutations applied to perm
+** column operations applied to C. IN PLACE
+**/
+GEN
+hnfspec_i(GEN mat0, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, long k0)
+{
+  pari_sp av, lim;
+  long co, n, s, nlze, lnz, nr, i, j, k, lk0, col, lig, *p;
+  GEN mat;
+  GEN p1, p2, matb, matbnew, vmax, matt, T, extramat, B, C, H, dep, permpro;
+  const long li = lg(perm); /* = lgcols(mat0) */
+  const long CO = lg(mat0);
+
+  n = 0; /* -Wall */
+
+  C = *ptC; co = CO;
+  if (co > 300 && co > 1.5 * li)
+  { /* treat the rest at the end */
+    co = (long)(1.2 * li);
+    setlg(C, co);
+  }
+
+  if (DEBUGLEVEL>5)
+  {
+    err_printf("Entering hnfspec\n");
+    p_mat(mat0,perm,0);
+  }
+  matt = cgetg(co, t_MAT); /* dense part of mat (top) */
+  mat = cgetg(co, t_MAT);
+  for (j = 1; j < co; j++)
+  {
+    GEN matj = col_dup(li, gel(mat0,j));
+    p1 = cgetg(k0+1,t_COL); gel(matt,j) = p1; gel(mat,j) = matj;
+    for (i=1; i<=k0; i++) gel(p1,i) = stoi(matj[perm[i]]);
+  }
+  av = avma; lim = stack_lim(av,1);
+
+  i = lig = li-1; col = co-1; lk0 = k0;
+  T = (k0 || (lg(C) > 1 && lgcols(C) > 1))? matid(col): NULL;
+  /* Look for lines with a single non-0 entry, equal to 1 in absolute value */
+  while (i > lk0 && col)
+    switch( count(mat,perm[i],col,&n) )
+    {
+      case 0: /* move zero lines between k0+1 and lk0 */
+        lk0++; lswap(perm[i], perm[lk0]);
+        i = lig; continue;
+
+      case 1: /* move trivial generator between lig+1 and li */
+        lswap(perm[i], perm[lig]);
+        if (T) swap(gel(T,n), gel(T,col));
+        swap(gel(mat,n), gel(mat,col)); p = gel(mat,col);
+        if (p[perm[lig]] < 0) /* = -1 */
+        { /* convert relation -g = 0 to g = 0 */
+          for (i=lk0+1; i<lig; i++) p[perm[i]] = -p[perm[i]];
+          if (T)
+          {
+            p1 = gel(T,col);
+            for (i=1; ; i++) /* T is a permuted identity: single non-0 entry */
+              if (signe(gel(p1,i))) { togglesign_safe(&gel(p1,i)); break; }
+          }
+        }
+        lig--; col--; i = lig; continue;
+
+      default: i--;
+    }
+  if (DEBUGLEVEL>5) { err_printf("    after phase1:\n"); p_mat(mat,perm,0); }
+
+#define absmax(s,z) {long _z; _z = labs(z); if (_z > s) s = _z;}
+  /* Get rid of all lines containing only 0 and +/- 1, keeping track of column
+   * operations in T. Leave the rows 1..lk0 alone [up to k0, coefficient
+   * explosion, between k0+1 and lk0, row is 0] */
+  s = 0;
+  while (lig > lk0 && col && s < (long)(HIGHBIT>>1))
+  {
+    for (i=lig; i>lk0; i--)
+      if (count(mat,perm[i],col,&n) > 0) break;
+    if (i == lk0) break;
+
+    /* only 0, +/- 1 entries, at least 2 of them non-zero */
+    lswap(perm[i], perm[lig]);
+    swap(gel(mat,n), gel(mat,col)); p = gel(mat,col);
+    if (T) swap(gel(T,n), gel(T,col));
+    if (p[perm[lig]] < 0)
+    {
+      for (i=lk0+1; i<=lig; i++) p[perm[i]] = -p[perm[i]];
+      if (T) ZV_togglesign(gel(T,col));
+    }
+    for (j=1; j<col; j++)
+    {
+      GEN matj = gel(mat,j);
+      long t;
+      if (! (t = matj[perm[lig]]) ) continue;
+      if (t == 1) {
+        for (i=lk0+1; i<=lig; i++) absmax(s, matj[perm[i]] -= p[perm[i]]);
+      }
+      else { /* t = -1 */
+        for (i=lk0+1; i<=lig; i++) absmax(s, matj[perm[i]] += p[perm[i]]);
+      }
+      if (T) ZC_lincomb1_inplace(gel(T,j), gel(T,col), stoi(-t));
+    }
+    lig--; col--;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"hnfspec[1]");
+      if (T) T = gerepilecopy(av, T); else avma = av;
+    }
+  }
+  /* As above with lines containing a +/- 1 (no other assumption).
+   * Stop when single precision becomes dangerous */
+  vmax = cgetg(co,t_VECSMALL);
+  for (j=1; j<=col; j++)
+  {
+    GEN matj = gel(mat,j);
+    for (s=0, i=lk0+1; i<=lig; i++) absmax(s, matj[i]);
+    vmax[j] = s;
+  }
+  while (lig > lk0 && col)
+  {
+    for (i=lig; i>lk0; i--)
+      if ( (n = count2(mat,perm[i],col)) ) break;
+    if (i == lk0) break;
+
+    lswap(vmax[n], vmax[col]);
+    lswap(perm[i], perm[lig]);
+    swap(gel(mat,n), gel(mat,col)); p = gel(mat,col);
+    if (T) swap(gel(T,n), gel(T,col));
+    if (p[perm[lig]] < 0)
+    {
+      for (i=lk0+1; i<=lig; i++) p[perm[i]] = -p[perm[i]];
+      if (T) ZV_togglesign(gel(T,col));
+    }
+    for (j=1; j<col; j++)
+    {
+      GEN matj = gel(mat,j);
+      long t;
+      if (! (t = matj[perm[lig]]) ) continue;
+      if (vmax[col] && (ulong)labs(t) >= (HIGHBIT-vmax[j]) / vmax[col])
+        goto END2;
+
+      for (s=0, i=lk0+1; i<=lig; i++) absmax(s, matj[perm[i]] -= t*p[perm[i]]);
+      vmax[j] = s;
+      if (T) ZC_lincomb1_inplace(gel(T,j), gel(T,col), stoi(-t));
+    }
+    lig--; col--;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"hnfspec[2]");
+      gerepileall(av, T? 2: 1, &vmax, &T);
+    }
+  }
+
+END2: /* clean up mat: remove everything to the right of the 1s on diagonal */
+  /* go multiprecision first */
+  matb = cgetg(co,t_MAT); /* bottom part (complement of matt) */
+  for (j=1; j<co; j++)
+  {
+    GEN matj = gel(mat,j);
+    p1 = cgetg(li-k0,t_COL); gel(matb,j) = p1;
+    p1 -= k0;
+    for (i=k0+1; i<li; i++) gel(p1,i) = stoi(matj[perm[i]]);
+  }
+  if (DEBUGLEVEL>5)
+  {
+    err_printf("    after phase2:\n");
+    p_mat(mat,perm,lk0);
+  }
+  for (i=li-2; i>lig; i--)
+  {
+    long h, i0 = i - k0, k = i + co-li;
+    GEN Bk = gel(matb,k);
+    for (j=k+1; j<co; j++)
+    {
+      GEN Bj = gel(matb,j), v = gel(Bj,i0);
+      s = signe(v); if (!s) continue;
+
+      gel(Bj,i0) = gen_0;
+      if (is_pm1(v))
+      {
+        if (s > 0) /* v = 1 */
+        { for (h=1; h<i0; h++) gel(Bj,h) = subii(gel(Bj,h), gel(Bk,h)); }
+        else /* v = -1 */
+        { for (h=1; h<i0; h++) gel(Bj,h) = addii(gel(Bj,h), gel(Bk,h)); }
+      }
+      else {
+        for (h=1; h<i0; h++) gel(Bj,h) = subii(gel(Bj,h), mulii(v,gel(Bk,h)));
+      }
+      if (T) ZC_lincomb1_inplace(gel(T,j), gel(T,k), negi(v));
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"hnfspec[3], (i,j) = %ld,%ld", i,j);
+        for (h=1; h<co; h++) setlg(matb[h], i0+1); /* bottom can be forgotten */
+        gerepileall(av, T? 2: 1, &matb, &T);
+        Bk = gel(matb,k);
+      }
+    }
+  }
+  for (j=1; j<co; j++) setlg(matb[j], lig-k0+1); /* bottom can be forgotten */
+  gerepileall(av, T? 2: 1, &matb, &T);
+  if (DEBUGLEVEL>5) err_printf("    matb cleaned up (using Id block)\n");
+
+  nlze = lk0 - k0;  /* # of 0 rows */
+  lnz = lig-nlze+1; /* 1 + # of non-0 rows (!= 0...0 1 0 ... 0) */
+  if (T) matt = ZM_mul(matt,T); /* update top rows */
+  extramat = cgetg(col+1,t_MAT); /* = new C minus the 0 rows */
+  for (j=1; j<=col; j++)
+  {
+    GEN z = gel(matt,j);
+    GEN t = (gel(matb,j)) + nlze - k0;
+    p2=cgetg(lnz,t_COL); gel(extramat,j) = p2;
+    for (i=1; i<=k0; i++) gel(p2,i) = gel(z,i); /* top k0 rows */
+    for (   ; i<lnz; i++) gel(p2,i) = gel(t,i); /* other non-0 rows */
+  }
+  if (!col) {
+    permpro = identity_perm(lnz);
+    nr = lnz;
+  }
+  else
+    permpro = ZM_imagecomplspec(extramat, &nr);
+  /* lnz = lg(permpro) */
+  if (nlze)
+  { /* put the nlze 0 rows (trivial generators) at the top */
+    p1 = new_chunk(lk0+1);
+    for (i=1; i<=nlze; i++) p1[i] = perm[i + k0];
+    for (   ; i<=lk0; i++)  p1[i] = perm[i - nlze];
+    for (i=1; i<=lk0; i++)  perm[i] = p1[i];
+  }
+  /* sort other rows according to permpro (nr redundant generators first) */
+  p1 = new_chunk(lnz); p2 = perm + nlze;
+  for (i=1; i<lnz; i++) p1[i] = p2[permpro[i]];
+  for (i=1; i<lnz; i++) p2[i] = p1[i];
+  /* perm indexes the rows of mat
+   *   |_0__|__redund__|__dense__|__too big__|_____done______|
+   *   0  nlze                              lig             li
+   *         \___nr___/ \___k0__/
+   *         \____________lnz ______________/
+   *
+   *               col   co
+   *       [dep     |     ]
+   *    i0 [--------|  B  ] (i0 = nlze + nr)
+   *       [matbnew |     ] matbnew has maximal rank = lnz-1 - nr
+   * mat = [--------|-----] lig
+   *       [   0    | Id  ]
+   *       [        |     ] li */
+
+  matbnew = cgetg(col+1,t_MAT); /* dense+toobig, maximal rank. For hnffinal */
+  dep    = cgetg(col+1,t_MAT); /* rows dependent from the ones in matbnew */
+  for (j=1; j<=col; j++)
+  {
+    GEN z = gel(extramat,j);
+    p1 = cgetg(nlze+nr+1,t_COL); gel(dep,j) = p1;
+    p2 = cgetg(lnz-nr,t_COL); gel(matbnew,j) = p2;
+    for (i=1; i<=nlze; i++) gel(p1,i) = gen_0;
+    p1 += nlze; for (i=1; i<=nr; i++) p1[i] = z[permpro[i]];
+    p2 -= nr;   for (   ; i<lnz; i++) p2[i] = z[permpro[i]];
+  }
+
+  /* redundant generators in terms of the genuine generators
+   * (x_i) = - (g_i) B */
+  B = cgetg(co-col,t_MAT);
+  for (j=col+1; j<co; j++)
+  {
+    GEN y = gel(matt,j);
+    GEN z = gel(matb,j);
+    p1=cgetg(lig+1,t_COL); gel(B,j-col) = p1;
+    for (i=1; i<=nlze; i++) gel(p1,i) = gel(z,i);
+    p1 += nlze; z += nlze-k0;
+    for (k=1; k<lnz; k++)
+    {
+      i = permpro[k];
+      gel(p1,k) = (i <= k0)? gel(y,i): gel(z,i);
+    }
+  }
+  if (T) C = typ(C)==t_MAT? RgM_mul(C,T): RgV_RgM_mul(C,T);
+  gerepileall(av, 4, &matbnew, &B, &dep, &C);
+  *ptdep = dep;
+  *ptB = B;
+  H = hnffinal(matbnew, perm, ptdep, ptB, &C);
+  if (CO > co)
+  { /* treat the rest, N cols at a time (hnflll slow otherwise) */
+    const long N = 300;
+    long a, L = CO - co, l = minss(L, N); /* L columns to add */
+    GEN CC = *ptC, m0 = mat0;
+    setlg(CC, CO); /* restore */
+    CC += co-1;
+    m0 += co-1;
+    for (a = l;;)
+    {
+      GEN MAT, emb;
+      gerepileall(av, 4, &H,&C,ptB,ptdep);
+      MAT = cgetg(l + 1, t_MAT);
+      emb = cgetg(l + 1, typ(C));
+      for (j = 1 ; j <= l; j++)
+      {
+        gel(MAT,j) = gel(m0,j);
+        emb[j] = CC[j];
+      }
+      H = hnfadd_i(H, perm, ptdep, ptB, &C, MAT, emb);
+      if (a == L) break;
+      CC += l;
+      m0 += l;
+      a += l; if (a > L) { l = L - (a - l); a = L; }
+    }
+  }
+  *ptC = C; return H;
+}
+
+GEN
+hnfspec(GEN mat, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, long k0)
+{
+  pari_sp av = avma;
+  GEN H = hnfspec_i(mat, perm, ptdep, ptB, ptC, k0);
+  gerepileall(av, 4, ptC, ptdep, ptB, &H); return H;
+}
+
+/* HNF reduce x, apply same transforms to C */
+GEN
+mathnfspec(GEN x, GEN *ptperm, GEN *ptdep, GEN *ptB, GEN *ptC)
+{
+  long i,j,k,ly,lx = lg(x);
+  GEN z, perm;
+  if (lx == 1) return cgetg(1, t_MAT);
+  ly = lgcols(x);
+  *ptperm = perm = identity_perm(ly-1);
+  z = cgetg(lx,t_MAT);
+  for (i=1; i<lx; i++)
+  {
+    GEN C = cgetg(ly,t_COL), D = gel(x,i);
+    gel(z,i) = C;
+    for (j=1; j<ly; j++)
+    {
+      GEN d = gel(D,j);
+      if (is_bigint(d)) goto TOOLARGE;
+      C[j] = itos(d);
+    }
+  }
+  /*  [ dep |     ]
+   *  [-----|  B  ]
+   *  [  H  |     ]
+   *  [-----|-----]
+   *  [  0  | Id  ] */
+  return hnfspec(z,perm, ptdep, ptB, ptC, 0);
+
+TOOLARGE:
+  if (lg(*ptC) > 1 && lgcols(*ptC) > 1)
+    pari_err_IMPL("mathnfspec with large entries");
+  x = ZM_hnf(x); lx = lg(x); j = ly; k = 0;
+  for (i=1; i<ly; i++)
+  {
+    if (equali1(gcoeff(x,i,i + lx-ly)))
+      perm[--j] = i;
+    else
+      perm[++k] = i;
+  }
+  setlg(perm,k+1);
+  x = rowpermute(x, perm); /* upper part */
+  setlg(perm,ly);
+  *ptB = vecslice(x, j+lx-ly, lx-1);
+  setlg(x, j);
+  *ptdep = rowslice(x, 1, lx-ly);
+  return rowslice(x, lx-ly+1, k); /* H */
+}
+
+/* add new relations to a matrix treated by hnfspec (extramat / extraC) */
+GEN
+hnfadd_i(GEN H, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, /* cf hnfspec */
+       GEN extramat,GEN extraC)
+{
+  GEN matb, extratop, Cnew, permpro, B = *ptB, C = *ptC, dep = *ptdep;
+  long i, lH, lB, li, lig, co, col, nlze;
+
+  if (lg(extramat) == 1) return H;
+  co = lg(C)-1;
+  lH = lg(H)-1;
+  lB = lg(B)-1;
+  li = lg(perm)-1;
+  lig = li - lB;
+  col = co - lB;
+  nlze = lig - lH;
+
+ /*               col    co
+  *       [ 0 |dep |     ]
+  *  nlze [--------|  B  ]
+  *       [ 0 | H  |     ]
+  *       [--------|-----] lig
+  *       [   0    | Id  ]
+  *       [        |     ] li */
+  extratop = zm_to_ZM( rowslicepermute(extramat, perm, 1, lig) );
+  if (li != lig)
+  { /* zero out bottom part, using the Id block */
+    GEN A = vecslice(C, col+1, co);
+    GEN c = rowslicepermute(extramat, perm, lig+1, li);
+    extraC   = gsub(extraC, typ(A)==t_MAT? RgM_zm_mul(A, c): RgV_zm_mul(A,c));
+    extratop = ZM_sub(extratop, ZM_zm_mul(B, c));
+  }
+
+  extramat = shallowconcat(extratop, vconcat(dep, H));
+  Cnew     = shallowconcat(extraC, vecslice(C, col-lH+1, co));
+  if (DEBUGLEVEL>5) err_printf("    1st phase done\n");
+  permpro = ZM_imagecomplspec(extramat, &nlze);
+  extramat = rowpermute(extramat, permpro);
+  *ptB     = rowpermute(B,        permpro);
+  permpro = vecpermute(perm, permpro);
+  for (i=1; i<=lig; i++) perm[i] = permpro[i]; /* perm o= permpro */
+
+  *ptdep  = rowslice(extramat, 1, nlze);
+  matb    = rowslice(extramat, nlze+1, lig);
+  if (DEBUGLEVEL>5) err_printf("    2nd phase done\n");
+  H = hnffinal(matb,perm,ptdep,ptB,&Cnew);
+  *ptC = shallowconcat(vecslice(C, 1, col-lH), Cnew);
+  return H;
+}
+
+GEN
+hnfadd(GEN H, GEN perm, GEN* ptdep, GEN* ptB, GEN* ptC, /* cf hnfspec */
+       GEN extramat,GEN extraC)
+{
+  pari_sp av = avma;
+  H = hnfadd_i(H, perm, ptdep, ptB, ptC, ZM_to_zm(extramat), extraC);
+  gerepileall(av, 4, ptC, ptdep, ptB, &H); return H;
+}
+
+/* zero aj = Aij (!= 0)  using  ak = Aik (maybe 0), via linear combination of
+ * A[j] and A[k] of determinant 1. If U != NULL, likewise update its columns */
+static void
+ZC_elem(GEN aj, GEN ak, GEN A, GEN U, long j, long k)
+{
+  GEN p1,u,v,d;
+
+  if (!signe(ak)) {
+    swap(gel(A,j), gel(A,k));
+    if (U) swap(gel(U,j), gel(U,k));
+    return;
+  }
+  d = bezout(aj,ak,&u,&v);
+  /* frequent special case (u,v) = (1,0) or (0,1) */
+  if (!signe(u))
+  { /* ak | aj */
+    p1 = diviiexact(aj,ak); togglesign(p1);
+    ZC_lincomb1_inplace(gel(A,j), gel(A,k), p1);
+    if (U)
+      ZC_lincomb1_inplace(gel(U,j), gel(U,k), p1);
+    return;
+  }
+  if (!signe(v))
+  { /* aj | ak */
+    p1 = diviiexact(ak,aj); togglesign(p1);
+    ZC_lincomb1_inplace(gel(A,k), gel(A,j), p1);
+    swap(gel(A,j), gel(A,k));
+    if (U) {
+      ZC_lincomb1_inplace(gel(U,k), gel(U,j), p1);
+      swap(gel(U,j), gel(U,k));
+    }
+    return;
+  }
+
+  if (!is_pm1(d)) { aj = diviiexact(aj, d); ak = diviiexact(ak, d); }
+  p1 = gel(A,k); aj = negi(aj); /* NOT togglesign */
+  gel(A,k) = ZC_lincomb(u,v, gel(A,j),p1);
+  gel(A,j) = ZC_lincomb(aj,ak, p1,gel(A,j));
+  if (U)
+  {
+    p1 = gel(U,k);
+    gel(U,k) = ZC_lincomb(u,v, gel(U,j),p1);
+    gel(U,j) = ZC_lincomb(aj,ak, p1,gel(U,j));
+  }
+}
+
+INLINE int
+is_RgX(GEN a, long v) { return typ(a) == t_POL && varn(a)==v; }
+/* set u,v such that au + bv = gcd(a,b), divide a,b by the gcd */
+static GEN
+gbezout_step(GEN *pa, GEN *pb, GEN *pu, GEN *pv, long vx)
+{
+  GEN a = *pa, b = *pb, d;
+  if (gequal0(a))
+  {
+    *pa = gen_0; *pu = gen_0;
+    *pb = gen_1; *pv = gen_1; return b;
+  }
+  a = is_RgX(a,vx)? RgX_renormalize(a): scalarpol(a, vx);
+  b = is_RgX(b,vx)? RgX_renormalize(b): scalarpol(b, vx);
+  d = RgX_extgcd(a,b, pu,pv);
+  if (degpol(d)) { a = RgX_div(a, d); b = RgX_div(b, d); }
+  else if (typ(gel(d,2)) == t_REAL && lg(gel(d,2)) <= 3)
+#if 1
+  { /* possible accuracy problem */
+    GEN D = RgX_gcd_simple(a,b);
+    if (degpol(D)) {
+      D = RgX_Rg_div(D, leading_term(D));
+      a = RgX_div(a, D);
+      b = RgX_div(b, D);
+      d = RgX_extgcd(a,b, pu,pv); /* retry now */
+      d = RgX_mul(d, D);
+    }
+  }
+#else
+  { /* less stable */
+    d = RgX_extgcd_simple(a,b, pu,pv);
+    if (degpol(d)) { a = RgX_div(a, d); b = RgX_div(b, d); }
+  }
+#endif
+  *pa = a;
+  *pb = b; return d;
+}
+static GEN
+col_mul(GEN x, GEN c)
+{
+  if (typ(x) == t_INT)
+  {
+    long s = signe(x);
+    if (!s) return NULL;
+    if (is_pm1(x)) return (s > 0)? c: RgC_neg(c);
+  }
+  return RgC_Rg_mul(c, x);
+}
+static void
+do_zero(GEN x)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++) gel(x,i) = gen_0;
+}
+
+/* (c1, c2) *= [u,-b; v,a] */
+static void
+update(GEN u, GEN v, GEN a, GEN b, GEN *c1, GEN *c2)
+{
+  GEN p1,p2;
+
+  u = col_mul(u,*c1);
+  v = col_mul(v,*c2);
+  if (u) p1 = v? gadd(u,v): u;
+  else   p1 = v? v: NULL;
+
+  a = col_mul(a,*c2);
+  b = col_mul(gneg_i(b),*c1);
+  if (a) p2 = b? RgC_add(a,b): a;
+  else   p2 = b? b: NULL;
+
+  if (!p1) do_zero(*c1); else *c1 = p1;
+  if (!p2) do_zero(*c2); else *c2 = p2;
+}
+
+/* zero aj = Aij (!= 0)  using  ak = Aik (maybe 0), via linear combination of
+ * A[j] and A[k] of determinant 1. If U != NULL, likewise update its columns */
+static void
+RgC_elem(GEN aj, GEN ak, GEN A, GEN V, long j, long k, long li, long vx)
+{
+  GEN u,v, d = gbezout_step(&aj, &ak, &u, &v, vx);
+  long l;
+  /* (A[,k], A[,j]) *= [v, -aj; u, ak ] */
+  for (l = 1; l < li; l++)
+  {
+    GEN t = gadd(gmul(u,gcoeff(A,l,j)), gmul(v,gcoeff(A,l,k)));
+    gcoeff(A,l,j) = gsub(gmul(ak,gcoeff(A,l,j)), gmul(aj,gcoeff(A,l,k)));
+    gcoeff(A,l,k) = t;
+  }
+  gcoeff(A,li,j) = gen_0;
+  gcoeff(A,li,k) = d;
+  if (V) update(v,u,ak,aj,(GEN*)(V+k),(GEN*)(V+j));
+}
+
+/* reduce A[i,j] mod A[i,j0] for j=j0+1... via column operations */
+static void
+ZM_reduce(GEN A, GEN U, long i, long j0)
+{
+  long j, lA = lg(A);
+  GEN d = gcoeff(A,i,j0);
+  if (signe(d) < 0)
+  {
+    ZV_neg_inplace(gel(A,j0));
+    if (U) ZV_togglesign(gel(U,j0));
+    d = gcoeff(A,i,j0);
+  }
+  for (j=j0+1; j<lA; j++)
+  {
+    GEN q = truedivii(gcoeff(A,i,j), d);
+    if (!signe(q)) continue;
+
+    togglesign(q);
+    ZC_lincomb1_inplace(gel(A,j), gel(A,j0), q);
+    if (U) ZC_lincomb1_inplace(gel(U,j), gel(U,j0), q);
+  }
+}
+
+static GEN
+RgX_normalize(GEN T, GEN *pd)
+{
+  GEN d = leading_term(T);
+  while (gequal0(d) || ( typ(d) == t_REAL && lg(d) == 3
+                       && gexpo(T) - expo(d) > (long)BITS_IN_LONG)) {
+     T = normalizepol_lg(T, lg(T)-1);
+     if (!signe(T)) { *pd = gen_1; return T; }
+     d = leading_term(T);
+  }
+  *pd = d;
+  return RgX_Rg_div(T, d);
+}
+/* reduce A[i,j] mod A[i,j0] for j=j0+1... via column operations */
+static void
+RgM_reduce(GEN A, GEN U, long i, long j0, long vx)
+{
+  long j, lA = lg(A);
+  GEN d, T = gcoeff(A,i,j0);
+  if (is_RgX(T,vx)) {
+    T = RgX_normalize(T, &d);
+    if (degpol(T) == 0) { d = gel(T,2); T = gen_1; }
+  } else {
+    d = T; T = gen_1;
+  }
+  if (U && !gequal1(d)) gel(U,j0) = RgC_Rg_div(gel(U,j0), d);
+  gcoeff(A,i,j0) = T;
+
+  for (j=j0+1; j<lA; j++)
+  {
+    GEN t = gcoeff(A,i,j), q;
+    if (gequal0(t)) continue;
+    if (T == gen_1)
+      q = t;
+    else if (is_RgX(t,vx))
+      q = RgX_div(t, T);
+    else continue;
+
+    if (gequal0(q)) continue;
+    gel(A,j) = RgC_sub(gel(A,j), RgC_Rg_mul(gel(A,j0), q));
+    if (U) gel(U,j) = RgC_sub(gel(U,j), RgC_Rg_mul(gel(U,j0), q));
+  }
+}
+
+/* A,B square integral in upper HNF, of the same dimension > 0. Return Au
+ * in Z^n (v in Z^n not computed), such that Au + Bv = [1, 0, ..., 0] */
+GEN
+hnfmerge_get_1(GEN A, GEN B)
+{
+  pari_sp av = avma;
+  long j, k, c, l = lg(A), lb;
+  GEN b, t, U = cgetg(l + 1, t_MAT), C = cgetg(l + 1, t_VEC);
+
+  t = NULL; /* -Wall */
+  b = gcoeff(B,1,1); lb = lgefint(b);
+  if (!signe(b)) {
+    if (!is_pm1(gcoeff(A,1,1))) return NULL;
+    return scalarcol_shallow(gen_1, l-1);
+  }
+  for (j = 1; j < l; j++)
+  {
+    c = j+1;
+    gel(U,j) = col_ei(l-1, j);
+    gel(U,c) = zerocol(l-1); /* dummy */
+    gel(C,j) = vecslice(gel(A,j), 1,j);
+    gel(C,c) = vecslice(gel(B,j), 1,j);
+    for (k = j; k > 0; k--)
+    {
+      t = gcoeff(C,k,c);
+      if (gequal0(t)) continue;
+      setlg(C[c], k+1);
+      ZC_elem(t, gcoeff(C,k,k), C, U, c, k);
+      if (lgefint(gcoeff(C,k,k)) > lb) gel(C,k) = FpC_red(gel(C,k), b);
+      if (j > 4)
+      {
+        GEN u = gel(U,k);
+        long h;
+        for (h=1; h<l; h++)
+          if (lgefint(gel(u,h)) > lb) gel(u,h) = remii(gel(u,h), b);
+      }
+    }
+    if (j == 1)
+      t = gcoeff(C,1,1);
+    else
+    {
+      GEN u;
+      t = bezout(gcoeff(C,1,1), b, &u, NULL); /* >= 0 */
+      if (signe(u) && !equali1(u)) gel(U,1) = ZC_Z_mul(gel(U,1), u);
+      gcoeff(C,1,1) = t;
+    }
+    if (equali1(t)) break;
+  }
+  if (j >= l) return NULL;
+  return gerepileupto(av, ZM_ZC_mul(A,gel(U,1)));
+}
+
+/* remove the first r columns */
+static void
+remove_0cols(long r, GEN *pA, GEN *pB, long remove)
+{
+  GEN A = *pA, B = *pB;
+  long l = lg(A);
+  A += r; A[0] = evaltyp(t_MAT) | evallg(l-r);
+  if (B && remove == 2) { B += r; B[0] = A[0]; }
+  *pA = A; *pB = B;
+}
+
+/* Inefficient compared to hnfall. 'remove' = throw away lin.dep columns */
+static GEN
+hnf_i(GEN A, int remove)
+{
+  pari_sp av0 = avma, av, lim;
+  long s, n, m, j, k, li, def, ldef;
+
+  RgM_dimensions(A, &m, &n);
+  if (!n) return cgetg(1,t_MAT);
+  av = avma; lim = stack_lim(av,1);
+  A = RgM_shallowcopy(A);
+  def = n; ldef = (m>n)? m-n: 0;
+  for (li=m; li>ldef; li--)
+  {
+    for (j=def-1; j; j--)
+    {
+      GEN a = gcoeff(A,li,j);
+      if (!signe(a)) continue;
+
+      /* zero a = Aij  using  b = Aik */
+      k = (j==1)? def: j-1;
+      ZC_elem(a,gcoeff(A,li,k), A,NULL, j,k);
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"ZM_hnf[1]. li=%ld",li);
+        A = gerepilecopy(av, A);
+      }
+    }
+    s = signe(gcoeff(A,li,def));
+    if (s)
+    {
+      if (s < 0) ZV_neg_inplace(gel(A,def));
+      ZM_reduce(A, NULL, li,def);
+      def--;
+    }
+    else
+      if (ldef) ldef--;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ZM_hnf[2]. li=%ld",li);
+      A = gerepilecopy(av, A);
+    }
+  }
+  /* rank A = n - def */
+  if (remove) { GEN B = NULL; remove_0cols(def, &A, &B, remove); }
+  return gerepileupto(av0, ZM_copy(A));
+}
+
+GEN
+ZM_hnf(GEN x) { return lg(x) > 8? ZM_hnfall(x, NULL, 1): hnf_i(x, 1); }
+
+/* u*z[1..k] mod p, in place */
+static void
+FpV_Fp_mul_part_ip(GEN z, GEN u, GEN p, long k)
+{
+  long i;
+  if (is_pm1(u)) {
+    if (signe(u) > 0) {
+      for (i = 1; i <= k; i++)
+        if (signe(gel(z,i))) gel(z,i) = modii(gel(z,i), p);
+    } else {
+      for (i = 1; i <= k; i++)
+        if (signe(gel(z,i))) gel(z,i) = modii(negi(gel(z,i)), p);
+    }
+  }
+  else {
+    for (i = 1; i <= k; i++)
+      if (signe(gel(z,i))) gel(z,i) = Fp_mul(u,gel(z,i), p);
+  }
+}
+static void
+FpV_red_part_ipvec(GEN z, GEN p, long k)
+{
+  long i;
+  for (i = 1; i <= k; i++) gel(z,i) = modii(gel(z,i), gel(p,i));
+}
+
+/* return x * U, in echelon form (mod p^m), where (det(U),p) = 1.
+ * If early_abort is set, return NULL as soon as one pivot is 0 (mod p^m) */
+GEN
+ZpM_echelon(GEN x, long early_abort, GEN p, GEN pm)
+{
+  pari_sp av0 = avma, av, lim;
+  long m, li, co, i, j, k, def, ldef;
+
+  co = lg(x); if (co == 1) return cgetg(1,t_MAT);
+  li = lgcols(x);
+  av = avma; lim = stack_lim(av,1);
+  x = RgM_shallowcopy(x);
+  m = Z_pval(pm, p);
+
+  ldef = (li > co)? li - co: 0;
+  for (def = co-1,i = li-1; i > ldef; i--)
+  {
+    long vmin = LONG_MAX, kmin = 0;
+    GEN umin = gen_0, pvmin, q;
+    for (k = 1; k <= def; k++)
+    {
+      GEN u = gcoeff(x,i,k);
+      long v;
+      if (!signe(u)) continue;
+      v = Z_pvalrem(u, p, &u);
+      if (v >= m) gcoeff(x,i,k) = gen_0;
+      else if (v < vmin) {
+        vmin = v; kmin = k; umin = u;
+        if (!vmin) break;
+      }
+    }
+    if (!kmin)
+    {
+      if (early_abort) return NULL;
+      gcoeff(x,i,def) = gen_0;
+      ldef--;
+      if (ldef < 0) ldef = 0;
+      continue;
+    }
+    if (kmin != def) swap(gel(x,def), gel(x,kmin));
+    q = vmin? powiu(p, m-vmin): pm;
+    /* pivot has valuation vmin */
+    umin = modii(umin, q);
+    if (!equali1(umin))
+      FpV_Fp_mul_part_ip(gel(x,def), Fp_inv(umin,q), pm, i-1);
+    gcoeff(x, i, def) = pvmin = powiu(p, vmin);
+    for (j = def-1; j; j--)
+    { /* zero x[i, 1..def-1] using x[i,def] = pvmin */
+      GEN t, a = gcoeff(x,i,j) = modii(gcoeff(x,i,j), pm);
+      if (!signe(a)) continue;
+
+      t = diviiexact(a, pvmin); togglesign(t);
+      ZC_lincomb1_inplace(gel(x,j), gel(x,def), t);
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"ZpM_echelon. i=%ld",i);
+        x = gerepilecopy(av, x); pvmin = gcoeff(x,i,def);
+      }
+    }
+    def--;
+  }
+  if (co > li)
+  {
+    x += co - li;
+    x[0] = evaltyp(t_MAT) | evallg(li);
+  }
+  return gerepilecopy(av0, x);
+}
+GEN
+zlm_echelon(GEN x, long early_abort, ulong p, ulong pm)
+{
+  pari_sp av0 = avma;
+  long li, co, i, j, k, def, ldef;
+  ulong m;
+
+  co = lg(x); if (co == 1) return cgetg(1,t_MAT);
+  li = lgcols(x);
+  x = Flm_copy(x);
+  m = u_lval(pm, p);
+
+  ldef = (li > co)? li - co: 0;
+  for (def = co-1,i = li-1; i > ldef; i--)
+  {
+    long vmin = LONG_MAX, kmin = 0;
+    ulong umin = 0, pvmin, q;
+    for (k = 1; k <= def; k++)
+    {
+      ulong u = ucoeff(x,i,k);
+      long v;
+      if (!u) continue;
+      v = u_lvalrem(u, p, &u);
+      if (v >= (long) m) ucoeff(x,i,k) = 0;
+      else if (v < vmin) {
+        vmin = v; kmin = k; umin = u;
+        if (!vmin) break;
+      }
+    }
+    if (!kmin)
+    {
+      if (early_abort) return NULL;
+      ucoeff(x,i,def) = 0;
+      ldef--;
+      if (ldef < 0) ldef = 0;
+      continue;
+    }
+    if (kmin != def) swap(gel(x,def), gel(x,kmin));
+    q = vmin? upowuu(p, m-vmin): pm;
+    /* pivot has valuation vmin */
+    umin %= q;
+    if (umin != 1)
+      Flc_Fl_mul_part_inplace(gel(x,def), Fl_inv(umin,q), pm, i-1);
+    ucoeff(x, i, def) = pvmin = upowuu(p, vmin);
+    for (j = def-1; j; j--)
+    { /* zero x[i, 1..def-1] using x[i,def] = pvmin */
+      ulong t, a = ucoeff(x,i,j);
+      if (!a) continue;
+
+      t = Fl_neg(a / pvmin, q);
+      Flc_lincomb1_inplace(gel(x,j), gel(x,def), t, pm);
+    }
+    def--;
+  }
+  if (co > li)
+  {
+    x += co - li;
+    x[0] = evaltyp(t_MAT) | evallg(li);
+  }
+  return gerepilecopy(av0, x);
+}
+
+/* dm = multiple of diag element (usually detint(x))
+ * flag & hnf_MODID: reduce mod dm * matid [ otherwise as above ].
+ * flag & hnf_PART: don't reduce once diagonal is known; */
+
+/* x a ZM, dm a t_INT */
+GEN
+ZM_hnfmodall_i(GEN x, GEN dm, long flag)
+{
+  pari_sp av, lim;
+  const long center = (flag & hnf_CENTER);
+  long moddiag = (flag & hnf_MODID);
+  long li, co, i, j, k, def, ldef;
+  GEN a, b, p1, p2, u, dm2, LDM;
+
+  co = lg(x); if (co == 1) return cgetg(1,t_MAT);
+  li = lgcols(x); if (li == 1) return cgetg(1,t_MAT);
+  if (typ(dm) == t_INT)
+  {
+    long ldm = lgefint(dm);
+    dm2 = shifti(dm, -1);
+    dm = const_vec(li-1,dm);
+    dm2= const_vec(li-1,dm2);
+    LDM= const_vecsmall(li-1,ldm);
+  }
+  else
+  {
+    if (lg(dm) != li) pari_err_DIM("ZM_hnfmod");
+    moddiag = 1;
+    dm2 = cgetg(li, t_VEC);
+    LDM = cgetg(li, t_VECSMALL);
+    for (i=1; i<li; i++)
+    {
+      gel(dm2,i) = shifti(gel(dm,i),-1);
+      LDM[i] = lgefint(gel(dm,i));
+    }
+  }
+  av = avma; lim = stack_lim(av,1);
+  x = RgM_shallowcopy(x);
+
+  ldef = 0;
+  if (li > co)
+  {
+    ldef = li - co;
+    if (!moddiag)
+      pari_err_DOMAIN("ZM_hnfmod","nb lines",">", strtoGENstr("nb columns"), x);
+  }
+  for (def = co-1,i = li-1; i > ldef; i--,def--)
+  {
+    GEN d = gel(dm,i), d2 = gel(dm2,i);
+    gcoeff(x,i,def) = centermodii(gcoeff(x,i,def), d,d2);
+    for (j = def-1; j; j--)
+    {
+      gcoeff(x,i,j) = centermodii(gcoeff(x,i,j), d,d2);
+      a = gcoeff(x,i,j);
+      if (!signe(a)) continue;
+
+      k = (j==1)? def: j-1;
+      gcoeff(x,i,k) = centermodii(gcoeff(x,i,k), d,d2);
+      ZC_elem(a,gcoeff(x,i,k), x,NULL, j,k);
+      p1 = gel(x,j);
+      p2 = gel(x,k);
+      /* prevent coeffs explosion: reduce mod dm when lg() > ldm */
+      for (k = 1; k < i; k++)
+      {
+        if (lgefint(gel(p1,k)) > LDM[k])
+          gel(p1,k) = centermodii(gel(p1,k), gel(dm,k),gel(dm2,k));
+        if (lgefint(gel(p2,k)) > LDM[k])
+          gel(p2,k) = centermodii(gel(p2,k), gel(dm,k),gel(dm2,k));
+      }
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"ZM_hnfmod[1]. i=%ld",i);
+        x = gerepilecopy(av, x);
+      }
+    }
+    if (moddiag && !signe(gcoeff(x,i,def)))
+    { /* missing pivot on line i, insert column */
+      GEN a = cgetg(co + 1, t_MAT);
+      for (k = 1; k <= def; k++) gel(a,k) = gel(x,k);
+      gel(a,k++) = Rg_col_ei(gel(dm,i), li-1, i);
+      for (     ; k <= co;  k++) gel(a,k) = gel(x,k-1);
+      ldef--; if (ldef < 0) ldef = 0;
+      co++; def++; x = a;
+    }
+  }
+  if (co < li)
+  { /* implies moddiag, add missing diag(dm) components */
+    GEN a = cgetg(li+1, t_MAT);
+    for (k = 1; k <= li-co; k++) gel(a,k) = Rg_col_ei(gel(dm,k), li-1, k);
+    for (i = 1; i < co; i++) gel(a,k-1+i) = gel(x,i);
+    gel(a,li) = zerocol(li-1); x = a;
+  }
+  else
+  {
+    x += co - li;
+    x[0] = evaltyp(t_MAT) | evallg(li); /* kill 0 columns */
+  }
+  if (moddiag)
+  { /* one column extra: an accumulator, discarded at the end */
+    if (lg(x) == li) x = shallowconcat(x, zerocol(li-1));
+    /* add up missing diag(dm) components */
+    for (i = li-1; i > 0; i--)
+    {
+      gcoeff(x, i, li) = gel(dm,i);
+      for (j = i; j > 0; j--)
+      {
+        GEN a = gcoeff(x, j, li);
+        if (!signe(a)) continue;
+        ZC_elem(a, gcoeff(x,j,j), x, NULL, li,j);
+        FpV_red_part_ipvec(gel(x,li), dm, j-1);
+        FpV_red_part_ipvec(gel(x,j),  dm, j-1);
+        if (low_stack(lim, stack_lim(av,1)))
+        {
+          if (DEBUGMEM>1) pari_warn(warnmem,"ZM_hnfmod[2]. i=%ld", i);
+          x = gerepilecopy(av, x);
+        }
+      }
+    }
+  }
+  else
+  {
+    b = gel(dm,1);
+    for (i = li-1; i > 0; i--)
+    {
+      GEN d = bezout(gcoeff(x,i,i),b, &u,NULL);
+      gcoeff(x,i,i) = d;
+      FpV_Fp_mul_part_ip(gel(x,i), u, b, i-1);
+      if (i > 1) b = diviiexact(b,d);
+    }
+  }
+  x[0] = evaltyp(t_MAT) | evallg(li); /* kill 0 columns / discard accumulator */
+  if (flag & hnf_PART) return x;
+
+  if (!moddiag)
+  { /* compute optimal value for dm */
+    b = cgetg(li, t_VEC); gel(b,1) = gcoeff(x,1,1);
+    for (i = 2; i < li; i++) gel(b,i) = mulii(gel(b,i-1), gcoeff(x,i,i));
+    dm = b;
+  }
+
+  for (i = li-1; i > 0; i--)
+  {
+    GEN diag = gcoeff(x,i,i);
+    if (signe(diag) < 0) { gel(x,i) = ZC_neg(gel(x,i)); diag = gcoeff(x,i,i); }
+    for (j = i+1; j < li; j++)
+    {
+      b = gcoeff(x,i,j);
+      b = center? diviiround(b,diag): truedivii(b, diag);
+      if (!signe(b)) continue;
+      togglesign(b);
+      ZC_lincomb1_inplace(gel(x,j), gel(x,i),b);
+      p1 = gel(x,j);
+      for (k=1; k<i; k++)
+        if (lgefint(gel(p1,k)) > LDM[k]) gel(p1,k) = remii(gel(p1,k), gel(dm,i));
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"ZM_hnfmod[3]. i=%ld", i);
+        gerepileall(av, 2, &x, &dm); diag = gcoeff(x,i,i);
+      }
+    }
+  }
+  return x;
+}
+GEN
+ZM_hnfmodall(GEN x, GEN dm, long flag)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, ZM_hnfmodall_i(x, dm, flag));
+}
+GEN
+ZM_hnfmod(GEN x, GEN d) { return ZM_hnfmodall(x,d,0); }
+GEN
+ZM_hnfmodid(GEN x, GEN d) { return ZM_hnfmodall(x,d,hnf_MODID); }
+
+static GEN
+allhnfmod(GEN x, GEN dm, int flag)
+{
+  if (typ(x)!=t_MAT) pari_err_TYPE("allhnfmod",x);
+  RgM_check_ZM(x, "allhnfmod");
+  if (isintzero(dm)) return ZM_hnf(x);
+  return ZM_hnfmodall(x, dm, flag);
+}
+GEN
+hnfmod(GEN x, GEN d)
+{
+  if (typ(d) != t_INT) pari_err_TYPE("mathnfmod",d);
+  return allhnfmod(x, d, 0);
+}
+GEN
+hnfmodid(GEN x, GEN d)
+{
+  switch(typ(d))
+  {
+    case t_INT: break;
+    case t_VEC: case t_COL:
+      if (RgV_is_ZV(d)) break;
+    default: pari_err_TYPE("mathnfmodid",d);
+  }
+  return allhnfmod(x, d, hnf_MODID);
+}
+
+/* M a ZM in HNF. Normalize with *centered* residues */
+GEN
+ZM_hnfcenter(GEN M)
+{
+  long i, j, k, N = lg(M)-1;
+  pari_sp av = avma, lim = stack_lim(av,1);
+
+  for (j=N-1; j>0; j--) /* skip last line */
+  {
+    GEN Mj = gel(M,j), a = gel(Mj,j);
+    for (k = j+1; k <= N; k++)
+    {
+      GEN Mk = gel(M,k), q = diviiround(gel(Mk,j), a);
+      long s = signe(q);
+      if (!s) continue;
+      if (is_pm1(q))
+      {
+        if (s < 0)
+          for (i = 1; i <= j; i++) gel(Mk,i) = addii(gel(Mk,i), gel(Mj,i));
+        else
+          for (i = 1; i <= j; i++) gel(Mk,i) = subii(gel(Mk,i), gel(Mj,i));
+      }
+      else
+        for (i = 1; i <= j; i++) gel(Mk,i) = subii(gel(Mk,i), mulii(q,gel(Mj,i)));
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM) pari_warn(warnmem,"ZM_hnfcenter, j = %ld",j);
+        M = gerepilecopy(av, M);
+      }
+    }
+  }
+  return M;
+}
+
+/***********************************************************************/
+/*                                                                     */
+/*                 HNFLLL (Havas, Majewski, Mathews)                   */
+/*                                                                     */
+/***********************************************************************/
+
+static void
+Minus(long j, GEN lambda)
+{
+  long k, n = lg(lambda);
+
+  for (k=1  ; k<j; k++) togglesign_safe(&gcoeff(lambda,k,j));
+  for (k=j+1; k<n; k++) togglesign_safe(&gcoeff(lambda,j,k));
+}
+
+/* index of first non-zero entry */
+static long
+findi(GEN M)
+{
+  long i, n = lg(M);
+  for (i=1; i<n; i++)
+    if (signe(gel(M,i))) return i;
+  return 0;
+}
+
+static long
+findi_normalize(GEN Aj, GEN B, long j, GEN lambda)
+{
+  long r = findi(Aj);
+  if (r && signe(gel(Aj,r)) < 0)
+  {
+    ZV_togglesign(Aj); if (B) ZV_togglesign(gel(B,j));
+    Minus(j,lambda);
+  }
+  return r;
+}
+
+static void
+reduce2(GEN A, GEN B, long k, long j, long *row0, long *row1, GEN lambda, GEN D)
+{
+  GEN q;
+  long i;
+
+  *row0 = findi_normalize(gel(A,j), B,j,lambda);
+  *row1 = findi_normalize(gel(A,k), B,k,lambda);
+  if (*row0)
+    q = truedivii(gcoeff(A,*row0,k), gcoeff(A,*row0,j));
+  else if (absi_cmp(shifti(gcoeff(lambda,j,k), 1), gel(D,j)) > 0)
+    q = diviiround(gcoeff(lambda,j,k), gel(D,j));
+  else
+    return;
+
+  if (signe(q))
+  {
+    GEN Lk = gel(lambda,k), Lj = gel(lambda,j);
+    togglesign_safe(&q);
+    if (*row0) ZC_lincomb1_inplace(gel(A,k),gel(A,j),q);
+    if (B) ZC_lincomb1_inplace(gel(B,k),gel(B,j),q);
+    gel(Lk,j) = addii(gel(Lk,j), mulii(q,gel(D,j)));
+    if (is_pm1(q))
+    {
+      if (signe(q) > 0)
+      {
+        for (i=1; i<j; i++)
+          if (signe(gel(Lj,i))) gel(Lk,i) = addii(gel(Lk,i), gel(Lj,i));
+      }
+      else
+      {
+        for (i=1; i<j; i++)
+          if (signe(gel(Lj,i))) gel(Lk,i) = subii(gel(Lk,i), gel(Lj,i));
+      }
+    }
+    else
+    {
+      for (i=1; i<j; i++)
+        if (signe(gel(Lj,i))) gel(Lk,i) = addii(gel(Lk,i), mulii(q,gel(Lj,i)));
+    }
+  }
+}
+
+static void
+hnfswap(GEN A, GEN B, long k, GEN lambda, GEN D)
+{
+  GEN t, p1, p2, Lk = gel(lambda,k);
+  long i,j,n = lg(A);
+
+  swap(gel(A,k), gel(A,k-1));
+  if (B) swap(gel(B,k), gel(B,k-1));
+  for (j=k-2; j; j--) swap(gcoeff(lambda,j,k-1), gel(Lk,j));
+  for (i=k+1; i<n; i++)
+  {
+    GEN Li = gel(lambda,i);
+    p1 = mulii(gel(Li,k-1), gel(D,k));
+    p2 = mulii(gel(Li,k), gel(Lk,k-1));
+    t = subii(p1,p2);
+
+    p1 = mulii(gel(Li,k), gel(D,k-2));
+    p2 = mulii(gel(Li,k-1), gel(Lk,k-1));
+    gel(Li,k-1) = diviiexact(addii(p1,p2), gel(D,k-1));
+    gel(Li,k) = diviiexact(t, gel(D,k-1));
+  }
+  p1 = mulii(gel(D,k-2), gel(D,k));
+  p2 = sqri(gel(Lk,k-1));
+  gel(D,k-1) = diviiexact(addii(p1,p2), gel(D,k-1));
+}
+
+/* reverse row order in matrix A, IN PLACE */
+static GEN
+reverse_rows(GEN A)
+{
+  long i, j, h, n = lg(A);
+  if (n == 1) return A;
+  h = lgcols(A);
+  for (j=1; j<n; j++)
+  {
+    GEN c = gel(A,j);
+    /* start at (h-1) >>1 : if h = 2i even, no need to swap c[i] and itself */
+    for (i=(h-1)>>1; i; i--) swap(gel(c,i), gel(c,h-i));
+  }
+  return A;
+}
+
+GEN
+ZM_hnflll(GEN A, GEN *ptB, int remove)
+{
+  pari_sp av = avma, lim = stack_lim(av,3);
+#ifdef HNFLLL_QUALITY
+  const long m1 = 1, n1 = 1; /* alpha = m1/n1. Maybe 3/4 here ? */
+#endif
+  long n, k, kmax;
+  GEN B, lambda, D;
+
+  n = lg(A);
+  A = reverse_rows(ZM_copy(A)); /* ZM_copy for in place findi_normalize() */
+  B = ptB? matid(n-1): NULL;
+  D = const_vec(n, gen_1) + 1;
+  lambda = zeromatcopy(n-1,n-1);
+  k = kmax = 2;
+  while (k < n)
+  {
+    long row0, row1;
+    int do_swap;
+    reduce2(A,B,k,k-1,&row0,&row1,lambda,D);
+    if (row0)
+      do_swap = (!row1 || row0 <= row1);
+    else if (!row1)
+    { /* row0 == row1 == 0 */
+      pari_sp av1 = avma;
+      GEN z = addii(mulii(gel(D,k-2),gel(D,k)), sqri(gcoeff(lambda,k-1,k)));
+#ifdef HNFLLL_QUALITY
+      do_swap = (cmpii(mului(n1,z), mului(m1,sqri(gel(D,k-1)))) < 0);
+#else /* assume m1 = n1 = 1 */
+      do_swap = (cmpii(z, sqri(gel(D,k-1))) < 0);
+#endif
+      avma = av1;
+    }
+    else
+      do_swap = 0;
+    if (do_swap)
+    {
+      hnfswap(A,B,k,lambda,D);
+      if (k > 2) k--;
+    }
+    else
+    {
+      long i;
+      for (i=k-2; i; i--)
+      {
+        long row0, row1;
+        reduce2(A,B,k,i,&row0,&row1,lambda,D);
+        if (low_stack(lim, stack_lim(av,3)))
+        {
+          GEN b = D-1;
+          if (DEBUGMEM) pari_warn(warnmem,"hnflll (reducing), kmax = %ld",kmax);
+          gerepileall(av, B? 4: 3, &A, &lambda, &b, &B);
+          D = b+1;
+        }
+      }
+      if (++k > kmax) kmax = k;
+    }
+    if (low_stack(lim, stack_lim(av,3)))
+    {
+      GEN b = D-1;
+      if (DEBUGMEM) pari_warn(warnmem,"hnflll, kmax = %ld / %ld",kmax,n-1);
+      gerepileall(av, B? 4: 3, &A, &lambda, &b, &B);
+      D = b+1;
+    }
+  }
+  /* handle trivial case: return negative diag coefficient otherwise */
+  if (n == 2) (void)findi_normalize(gel(A,1), B,1,lambda);
+  A = reverse_rows(A);
+  if (remove)
+  {
+    long i;
+    for (i = 1; i < n; i++)
+      if (!ZV_equal0(gel(A,i))) break;
+    remove_0cols(i-1, &A, &B, remove);
+  }
+  gerepileall(av, B? 2: 1, &A, &B);
+  if (B) *ptB = B;
+  return A;
+}
+
+GEN
+hnflll(GEN x)
+{
+  GEN z = cgetg(3, t_VEC);
+  gel(z,1) = ZM_hnflll(x, &gel(z,2), 1);
+  return z;
+}
+
+/* Variation on HNFLLL: Extended GCD */
+
+static void
+reduce1(GEN A, GEN B, long k, long j, GEN lambda, GEN D)
+{
+  GEN q;
+  long i;
+
+  if (signe(gel(A,j)))
+    q = diviiround(gel(A,k),gel(A,j));
+  else if (absi_cmp(shifti(gcoeff(lambda,j,k), 1), gel(D,j)) > 0)
+    q = diviiround(gcoeff(lambda,j,k), gel(D,j));
+  else
+    return;
+
+  if (signe(q))
+  {
+    GEN Lk = gel(lambda,k), Lj = gel(lambda,j);
+    togglesign_safe(&q);
+    gel(A,k) = addii(gel(A,k), mulii(q,gel(A,j)));
+    ZC_lincomb1_inplace(gel(B,k),gel(B,j),q);
+    gel(Lk,j) = addii(gel(Lk,j), mulii(q,gel(D,j)));
+    for (i=1; i<j; i++)
+      if (signe(gel(Lj,i))) gel(Lk,i) = addii(gel(Lk,i), mulii(q,gel(Lj,i)));
+  }
+}
+
+static GEN
+ZV_gcdext_i(GEN A)
+{
+#ifdef HNFLLL_QUALITY
+  const long m1 = 1, n1 = 1; /* alpha = m1/n1. Maybe 3/4 here ? */
+#endif
+  long k, n = lg(A);
+  GEN B, lambda, D;
+
+  if (n == 1) retmkvec2(gen_1, cgetg(1,t_MAT));
+  A = leafcopy(A);
+  B = matid(n-1);
+  lambda = zeromatcopy(n-1,n-1);
+  D = const_vec(n, gen_1) + 1;
+  k = 2;
+  while (k < n)
+  {
+    int do_swap;
+
+    reduce1(A,B,k,k-1,lambda,D);
+    if (signe(gel(A,k-1))) do_swap = 1;
+    else if (!signe(gel(A,k)))
+    {
+      pari_sp av1 = avma;
+      GEN z = addii(mulii(gel(D,k-2),gel(D,k)), sqri(gcoeff(lambda,k-1,k)));
+#ifdef HNFLLL_QUALITY
+      do_swap = (cmpii(mului(n1,z), mului(m1,sqri(gel(D,k-1)))) < 0);
+#else /* assume m1 = n1 = 1 */
+      do_swap = (cmpii(z, sqri(gel(D,k-1))) < 0);
+#endif
+      avma = av1;
+    }
+    else do_swap = 0;
+
+    if (do_swap)
+    {
+      hnfswap(A,B,k,lambda,D);
+      if (k > 2) k--;
+    }
+    else
+    {
+      long i;
+      for (i=k-2; i; i--) reduce1(A,B,k,i,lambda,D);
+      k++;
+    }
+  }
+  if (signe(gel(A,n-1)) < 0)
+  {
+    gel(A,n-1) = negi(gel(A,n-1));
+    ZV_togglesign(gel(B,n-1));
+  }
+  return mkvec2(gel(A,n-1), B);
+}
+GEN
+ZV_gcdext(GEN A)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, ZV_gcdext_i(A));
+}
+/* as ZV_gcdext, transforming the gcd into a t_MAT, for mathnf0 */
+static GEN
+ZV_hnfgcdext(GEN A)
+{
+  pari_sp av = avma;
+  GEN z;
+  if (lg(A) == 1) retmkvec2(cgetg(1,t_MAT),cgetg(1,t_MAT));
+  z = ZV_gcdext_i(A);
+  gel(z,1) = mkmat(mkcol(gel(z,1)));
+  return gerepilecopy(av, z);
+}
+
+/* HNF with permutation. */
+GEN
+ZM_hnfperm(GEN A, GEN *ptU, GEN *ptperm)
+{
+  GEN U, c, l, perm, d, p, q, b;
+  pari_sp av = avma, av1, lim;
+  long r, t, i, j, j1, k, m, n;
+
+  n = lg(A)-1;
+  if (!n)
+  {
+    if (ptU) *ptU = cgetg(1,t_MAT);
+    if (ptperm) *ptperm = cgetg(1,t_VEC);
+    return cgetg(1, t_MAT);
+  }
+  m = nbrows(A);
+  c = zero_zv(m);
+  l = zero_zv(n);
+  perm = cgetg(m+1, t_VECSMALL);
+  av1 = avma; lim = stack_lim(av1,1);
+  A = RgM_shallowcopy(A);
+  U = ptU? matid(n): NULL;
+  /* U base change matrix : A0*U = A all along */
+  for (r=0, k=1; k <= n; k++)
+  {
+    for (j=1; j<k; j++)
+    {
+      if (!l[j]) continue;
+      t=l[j]; b=gcoeff(A,t,k);
+      if (!signe(b)) continue;
+
+      ZC_elem(b,gcoeff(A,t,j), A,U,k,j);
+      d = gcoeff(A,t,j);
+      if (signe(d) < 0)
+      {
+        ZV_neg_inplace(gel(A,j));
+        if (U) ZV_togglesign(gel(U,j));
+        d = gcoeff(A,t,j);
+      }
+      for (j1=1; j1<j; j1++)
+      {
+        if (!l[j1]) continue;
+        q = truedivii(gcoeff(A,t,j1),d);
+        if (!signe(q)) continue;
+
+        togglesign(q);
+        ZC_lincomb1_inplace(gel(A,j1), gel(A,j), q);
+        if (U) ZC_lincomb1_inplace(gel(U,j1), gel(U,j), q);
+      }
+    }
+    t = m; while (t && (c[t] || !signe(gcoeff(A,t,k)))) t--;
+    if (t)
+    {
+      p = gcoeff(A,t,k);
+      for (i=t-1; i; i--)
+      {
+        q = gcoeff(A,i,k);
+        if (signe(q) && absi_cmp(p,q) > 0) { p = q; t = i; }
+      }
+      perm[++r] = l[k] = t; c[t] = k;
+      if (signe(p) < 0)
+      {
+        ZV_neg_inplace(gel(A,k));
+        if (U) ZV_togglesign(gel(U,k));
+        p = gcoeff(A,t,k);
+      }
+      /* p > 0 */
+      for (j=1; j<k; j++)
+      {
+        if (!l[j]) continue;
+        q = truedivii(gcoeff(A,t,j),p);
+        if (!signe(q)) continue;
+
+        togglesign(q);
+        ZC_lincomb1_inplace(gel(A,j), gel(A,k), q);
+        if (U) ZC_lincomb1_inplace(gel(U,j), gel(U,k), q);
+      }
+    }
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"hnfperm");
+      gerepileall(av1, U? 2: 1, &A, &U);
+    }
+  }
+  if (r < m)
+  {
+    for (i=1,k=r; i<=m; i++)
+      if (!c[i]) perm[++k] = i;
+  }
+
+/* We have A0*U=A, U in Gl(n,Z)
+ * basis for Im(A):  columns of A s.t l[j]>0 (r   cols)
+ * basis for Ker(A): columns of U s.t l[j]=0 (n-r cols) */
+  p = cgetg(r+1,t_MAT);
+  for (i=1; i<=m/2; i++) lswap(perm[i], perm[m+1-i]);
+  if (U)
+  {
+    GEN u = cgetg(n+1,t_MAT);
+    for (t=1,k=r,j=1; j<=n; j++)
+      if (l[j])
+      {
+        u[k + n-r] = U[j];
+        gel(p,k--) = vecpermute(gel(A,j), perm);
+      }
+      else
+        u[t++] = U[j];
+    *ptU = u;
+    if (ptperm) *ptperm = perm;
+    gerepileall(av, ptperm? 3: 2, &p, ptU, ptperm);
+  }
+  else
+  {
+    for (k=r,j=1; j<=n; j++)
+      if (l[j]) gel(p,k--) = vecpermute(gel(A,j), perm);
+    if (ptperm) *ptperm = perm;
+    gerepileall(av, ptperm? 2: 1, &p, ptperm);
+  }
+  return p;
+}
+
+GEN
+hnfperm(GEN A)
+{
+  GEN y = cgetg(4, t_VEC);
+  gel(y,1) = ZM_hnfperm(A, &gel(y,2), &gel(y,3));
+  return y;
+}
+
+/* Hermite Normal Form, with base change matrix if ptB != NULL.
+ * If 'remove' = 1, remove 0 columns (do NOT update *ptB accordingly)
+ * If 'remove' = 2, remove 0 columns and update *ptB accordingly */
+GEN
+ZM_hnfall(GEN A, GEN *ptB, long remove)
+{
+  pari_sp av = avma, av1, lim;
+  long m, n, r, i, j, k, li;
+  GEN B, c, h, a;
+
+  RgM_dimensions(A, &m,&n);
+  if (!n)
+  {
+    if (ptB) *ptB = cgetg(1,t_MAT);
+    return cgetg(1,t_MAT);
+  }
+  c = zero_zv(m);
+  h = const_vecsmall(n, m);
+  av1 = avma; lim = stack_lim(av1,1);
+  A = RgM_shallowcopy(A);
+  B = ptB? matid(n): NULL;
+  r = n+1;
+  for (li=m; li; li--)
+  {
+    for (j=1; j<r; j++)
+    {
+      for (i=h[j]; i>li; i--)
+      {
+        a = gcoeff(A,i,j);
+        k = c[i];
+        /* zero a = Aij  using  Aik */
+        if (signe(a)) ZC_elem(a,gcoeff(A,i,k), A,B,j,k);
+        ZM_reduce(A,B, i,k); /* ensure reduced entries */
+        if (low_stack(lim, stack_lim(av1,1)))
+        {
+          if (DEBUGMEM>1) pari_warn(warnmem,"hnfall[1], li = %ld", li);
+          gerepileall(av1, B? 2: 1, &A, &B);
+        }
+      }
+      if (signe( gcoeff(A,li,j) )) break;
+      h[j] = li-1;
+    }
+    if (j == r) continue;
+    r--;
+    if (j < r) /* A[j] != 0 */
+    {
+      swap(gel(A,j), gel(A,r));
+      if (B) swap(gel(B,j), gel(B,r));
+      h[j] = h[r]; h[r] = li; c[li] = r;
+    }
+    if (signe(gcoeff(A,li,r)) < 0)
+    {
+      ZV_neg_inplace(gel(A,r));
+      if (B) ZV_togglesign(gel(B,r));
+    }
+    ZM_reduce(A,B, li,r);
+    if (low_stack(lim, stack_lim(av1,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"hnfall[2], li = %ld", li);
+      gerepileall(av1, B? 2: 1, &A, &B);
+    }
+  }
+
+  if (DEBUGLEVEL>5) err_printf("\nhnfall, final phase: ");
+  r--; /* first r cols are in the image the n-r (independent) last ones */
+  for (j=1; j<=r; j++)
+    for (i=h[j]; i; i--)
+    {
+      a = gcoeff(A,i,j);
+      k = c[i];
+      if (signe(a)) ZC_elem(a,gcoeff(A,i,k), A,B, j,k);
+      ZM_reduce(A,B, i,k); /* ensure reduced entries, even if a = 0 */
+      if (low_stack(lim, stack_lim(av1,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"hnfall[3], j = %ld", j);
+        gerepileall(av1, B? 2: 1, &A, &B);
+      }
+    }
+  if (DEBUGLEVEL>5) err_printf("\n");
+  if (remove) remove_0cols(r, &A, &B, remove);
+  gerepileall(av, B? 2: 1, &A, &B);
+  if (B) *ptB = B;
+  return A;
+}
+
+GEN
+hnfall(GEN x)
+{
+  GEN z = cgetg(3, t_VEC);
+  gel(z,1) = ZM_hnfall(x, (GEN*)(z+2), 1);
+  return z;
+}
+GEN
+hnf(GEN x) { return mathnf0(x,0); }
+
+/* C = A^(-1)(tB) where A, B, C are integral, A is upper triangular, t t_INT */
+GEN
+hnf_divscale(GEN A, GEN B, GEN t)
+{
+  long n = lg(A)-1, i,j,k;
+  GEN m, c = cgetg(n+1,t_MAT);
+
+  if (!n) return c;
+  for (k=1; k<=n; k++)
+  {
+    GEN u = cgetg(n+1, t_COL), b = gel(B,k);
+    pari_sp av = avma;
+    gel(c,k) = u; m = mulii(gel(b,n),t);
+    gel(u,n) = gerepileuptoint(av, diviiexact(m, gcoeff(A,n,n)));
+    for (i=n-1; i>0; i--)
+    {
+      av = avma; m = mulii(gel(b,i),t);
+      for (j=i+1; j<=n; j++) m = subii(m, mulii(gcoeff(A,i,j),gel(u,j)));
+      gel(u,i) = gerepileuptoint(av, diviiexact(m, gcoeff(A,i,i)));
+    }
+  }
+  return c;
+}
+
+/* A, B integral upper HNF. A^(-1) B integral ? */
+int
+hnfdivide(GEN A, GEN B)
+{
+  pari_sp av = avma;
+  long n = lg(A)-1, i,j,k;
+  GEN u, b, m, r;
+
+  if (!n) return 1;
+  if (lg(B)-1 != n) pari_err_DIM("hnfdivide");
+  u = cgetg(n+1, t_COL);
+  for (k=1; k<=n; k++)
+  {
+    b = gel(B,k);
+    m = gel(b,k);
+    gel(u,k) = dvmdii(m, gcoeff(A,k,k), &r);
+    if (r != gen_0) { avma = av; return 0; }
+    for (i=k-1; i>0; i--)
+    {
+      m = gel(b,i);
+      for (j=i+1; j<=k; j++) m = subii(m, mulii(gcoeff(A,i,j),gel(u,j)));
+      m = dvmdii(m, gcoeff(A,i,i), &r);
+      if (r != gen_0) { avma = av; return 0; }
+      gel(u,i) = m;
+    }
+  }
+  avma = av; return 1;
+}
+
+/* A upper HNF, b integral vector. Return A^(-1) b if integral,
+ * NULL otherwise. Assume #A[,1] = #b. */
+GEN
+hnf_invimage(GEN A, GEN b)
+{
+  pari_sp av = avma;
+  long n = lg(A)-1, m, i, k;
+  GEN u, r;
+
+  if (!n) return NULL;
+  m = nbrows(A); /* m >= n */
+  u = cgetg(n+1, t_COL);
+  for (i = n, k = m; k > 0; k--)
+  {
+    pari_sp av2 = avma;
+    long j;
+    GEN t = gel(b,k), Aki = gcoeff(A,k,i);
+    if (typ(t) != t_INT) pari_err_TYPE("hnf_invimage",t);
+    for (j=i+1; j<=n; j++) t = subii(t, mulii(gcoeff(A,k,j),gel(u,j)));
+    if (!signe(Aki))
+    {
+      if (signe(t)) { avma = av;return NULL; }
+      avma = av2; gel(u,i) = gen_0; continue;
+    }
+    t = dvmdii(t, Aki, &r);
+    if (r != gen_0) { avma = av; return NULL; }
+    gel(u,i) = gerepileuptoint(av2, t);
+    if (--i == 0) break;
+  }
+  /* If there is a solution, it must be u. Check remaining equations */
+  for (; k > 0; k--)
+  {
+    pari_sp av2 = avma;
+    long j;
+    GEN t = gel(b,k);
+    if (typ(t) != t_INT) pari_err_TYPE("hnf_invimage",t);
+    for (j=1; j<=n; j++) t = subii(t, mulii(gcoeff(A,k,j),gel(u,j)));
+    if (signe(t)) { avma = av;return NULL; }
+    avma = av2;
+  }
+  return u;
+}
+
+/* A upper HNF, B integral matrix or column. Return A^(-1) B if integral,
+ * NULL otherwise */
+GEN
+hnf_solve(GEN A, GEN B)
+{
+  pari_sp av;
+  long i, l;
+  GEN C;
+
+  if (typ(B) == t_COL) return hnf_invimage(A, B);
+  av = avma; C = cgetg_copy(B, &l);
+  for (i = 1; i < l; i++) {
+    GEN c = hnf_invimage(A, gel(B,i));
+    if (!c) { avma = av; return NULL; }
+    gel(C,i) = c;
+  }
+  return C;
+}
+
+/***************************************************************/
+/**                                                           **/
+/**               SMITH NORMAL FORM REDUCTION                 **/
+/**                                                           **/
+/***************************************************************/
+
+static GEN
+trivsmith(long all)
+{
+  GEN z;
+  if (!all) return cgetg(1,t_VEC);
+  z=cgetg(4,t_VEC);
+  gel(z,1) = cgetg(1,t_MAT);
+  gel(z,2) = cgetg(1,t_MAT);
+  gel(z,3) = cgetg(1,t_MAT); return z;
+}
+
+static void
+snf_pile(pari_sp av, GEN *x, GEN *U, GEN *V)
+{
+  GEN *gptr[3];
+  int c = 1; gptr[0]=x;
+  if (*U) gptr[c++] = U;
+  if (*V) gptr[c++] = V;
+  gerepilemany(av,gptr,c);
+}
+
+static GEN
+bezout_step(GEN *pa, GEN *pb, GEN *pu, GEN *pv)
+{
+  GEN a = *pa, b = *pb, d;
+  if (absi_equal(a,b))
+  {
+    long sa = signe(a), sb = signe(b);
+    *pv = gen_0;
+    if (sb == sa) {
+      *pa = *pb = gen_1;
+      if (sa > 0) {*pu=gen_1; return a;} else {*pu=gen_m1; return absi(a);}
+    }
+    if (sa > 0) { *pa = *pu = gen_1; *pb = gen_m1; return a; }
+    *pa = *pu = gen_m1; *pb = gen_1; return b;
+  }
+  d = bezout(a,b, pu,pv);
+  *pa = diviiexact(a, d);
+  *pb = diviiexact(b, d); return d;
+}
+
+static int
+negcmpii(void *E, GEN x, GEN y) { (void)E; return -cmpii(x,y); }
+
+/* does b = x[i,i] divide all entries in x[1..i-1,1..i-1] ? If so, return 0;
+ * else return the index of a problematic row */
+static long
+ZM_snf_no_divide(GEN x, long i)
+{
+  GEN b = gcoeff(x,i,i);
+  long j, k;
+
+  if (!signe(b))
+  { /* impossible in the current implementation : x square of maximal rank */
+    for (k = 1; k < i; k++)
+      for (j = 1; j < i; j++)
+        if (signe(gcoeff(x,k,j))) return k;
+    return 0;
+  }
+  if (is_pm1(b)) return 0;
+  for (k=1; k<i; k++)
+  {
+    for (j=1; j<i; j++)
+      if (signe(remii(gcoeff(x,k,j),b))) return k;
+  }
+  return 0;
+}
+
+/* Return the SNF D of matrix X. If ptU/ptV non-NULL set them to U/V
+ * to that D = UXV */
+GEN
+ZM_snfall_i(GEN x, GEN *ptU, GEN *ptV, int return_vec)
+{
+  pari_sp av0 = avma, av, lim = stack_lim(av0,1);
+  long i, j, k, m0, m, n0, n;
+  GEN p1, u, v, U, V, V0, mdet, ys, perm = NULL;
+
+  n0 = n = lg(x)-1;
+  if (!n) {
+    if (ptU) *ptU = cgetg(1,t_MAT);
+    if (ptV) *ptV = cgetg(1,t_MAT);
+    return cgetg(1, return_vec? t_VEC: t_MAT);
+  }
+  av = avma;
+  m0 = m = nbrows(x);
+
+  U = ptU? gen_1: NULL; /* TRANSPOSE of row transform matrix [act on columns]*/
+  V = ptV? gen_1: NULL;
+  V0 = NULL;
+  x = RgM_shallowcopy(x);
+  if (m == n && ZM_ishnf(x))
+  {
+    mdet = ZM_det_triangular(x);
+    if (V) *ptV = matid(n);
+  }
+  else
+  {
+    mdet = ZM_detmult(x);
+    if (signe(mdet))
+    {
+      if (!V)
+        p1 = ZM_hnfmod(x,mdet);
+      else
+      {
+        if (m == n)
+        {
+          p1 = ZM_hnfmod(x,mdet);
+          *ptV = RgM_solve(x,p1);
+        }
+        else
+          p1 = ZM_hnfperm(x, ptV, ptU? &perm: NULL);
+      }
+      mdet = ZM_det_triangular(p1);
+    }
+    else
+      p1 = ZM_hnfperm(x, ptV, ptU? &perm: NULL);
+    x = p1;
+  }
+  n = lg(x)-1;
+  if (V)
+  {
+    V = *ptV;
+    if (n != n0)
+    {
+      V0 = vecslice(V, 1, n0 - n); /* kernel */
+      V  = vecslice(V, n0-n+1, n0);
+      av = avma;
+    }
+  }
+  /* independent columns */
+  if (!signe(mdet))
+  {
+    if (n)
+    {
+      x = ZM_snfall_i(shallowtrans(x), ptV, ptU, return_vec); /* swap ptV,ptU */
+      if (typ(x) == t_MAT && n != m) x = shallowtrans(x);
+      if (V) V = ZM_mul(V, shallowtrans(*ptV));
+      if (U) U = *ptU; /* TRANSPOSE */
+    }
+    else /* 0 matrix */
+    {
+      x = cgetg(1,t_MAT);
+      if (V) V = cgetg(1, t_MAT);
+      if (U) U = matid(m);
+    }
+    goto THEEND;
+  }
+  if (U) U = matid(n);
+
+  /* square, maximal rank n */
+  p1 = gen_indexsort(RgM_diagonal_shallow(x), NULL, &negcmpii);
+  ys = cgetg(n+1,t_MAT);
+  for (j=1; j<=n; j++) gel(ys,j) = vecpermute(gel(x, p1[j]), p1);
+  x = ys;
+  if (U) U = vecpermute(U, p1);
+  if (V) V = vecpermute(V, p1);
+
+  p1 = ZM_hnfmod(x, mdet);
+  if (V) V = ZM_mul(V, RgM_solve(x,p1));
+  x = p1;
+
+  if (DEBUGLEVEL>7) err_printf("starting SNF loop");
+  for (i=n; i>1; i--)
+  {
+    if (DEBUGLEVEL>7) err_printf("\ni = %ld: ",i);
+    for(;;)
+    {
+      int c = 0;
+      GEN a, b;
+      for (j=i-1; j>=1; j--)
+      {
+        b = gcoeff(x,i,j); if (!signe(b)) continue;
+        a = gcoeff(x,i,i);
+        ZC_elem(b, a, x,V, j,i);
+        if (low_stack(lim, stack_lim(av,1)))
+        {
+          if (DEBUGMEM>1) pari_warn(warnmem,"[1]: ZM_snfall i = %ld", i);
+          snf_pile(av, &x,&U,&V);
+        }
+      }
+      if (DEBUGLEVEL>7) err_printf("; ");
+      for (j=i-1; j>=1; j--)
+      {
+        GEN d;
+        b = gcoeff(x,j,i); if (!signe(b)) continue;
+        a = gcoeff(x,i,i);
+        d = bezout_step(&a, &b, &u, &v);
+        for (k = 1; k < i; k++)
+        {
+          GEN t = addii(mulii(u,gcoeff(x,i,k)),mulii(v,gcoeff(x,j,k)));
+          gcoeff(x,j,k) = subii(mulii(a,gcoeff(x,j,k)),
+                                mulii(b,gcoeff(x,i,k)));
+          gcoeff(x,i,k) = t;
+        }
+        gcoeff(x,j,i) = gen_0;
+        gcoeff(x,i,i) = d;
+        if (U) update(u,v,a,b,(GEN*)(U+i),(GEN*)(U+j));
+        if (low_stack(lim, stack_lim(av,1)))
+        {
+          if (DEBUGMEM>1) pari_warn(warnmem,"[2]: ZM_snfall, i = %ld", i);
+          snf_pile(av, &x,&U,&V);
+        }
+        c = 1;
+      }
+      if (!c)
+      {
+        k = ZM_snf_no_divide(x, i);
+        if (!k) break;
+
+        /* x[k,j] != 0 mod b */
+        for (j=1; j<=i; j++)
+          gcoeff(x,i,j) = addii(gcoeff(x,i,j),gcoeff(x,k,j));
+        if (U) gel(U,i) = gadd(gel(U,i),gel(U,k));
+      }
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"[3]: ZM_snfall");
+        snf_pile(av, &x,&U,&V);
+      }
+    }
+  }
+  if (DEBUGLEVEL>7) err_printf("\n");
+  for (k=1; k<=n; k++)
+    if (signe(gcoeff(x,k,k)) < 0)
+    {
+      if (V) ZV_togglesign(gel(V,k));
+      togglesign(gcoeff(x,k,k));
+    }
+THEEND:
+  if (return_vec)
+  {
+    long l = lg(x)-1;
+    if (typ(x) == t_MAT) x = RgM_diagonal_shallow(x);
+    if (m0 > l) x = shallowconcat(zerovec(m0-l), x);
+  }
+
+  if (V0)
+  {
+    if (!return_vec) x = shallowconcat(zeromat(m,n0-n), x);
+    if (V) V = shallowconcat(V0, V);
+  }
+  if (U)
+  {
+    U = shallowtrans(U);
+    if (perm) U = vecpermute(U, perm_inv(perm));
+  }
+  snf_pile(av0, &x,&U,&V);
+  if (ptU) *ptU = U;
+  if (ptV) *ptV = V;
+  return x;
+}
+GEN
+ZM_snfall(GEN x, GEN *U, GEN *V) { return ZM_snfall_i(x, U, V, 0); }
+GEN
+ZM_snf(GEN x) { return ZM_snfall_i(x, NULL,NULL, 1); }
+
+GEN
+smith(GEN x) {
+  if (typ(x)!=t_MAT) pari_err_TYPE("smith",x);
+  RgM_check_ZM(x, "smith");
+  return ZM_snfall_i(x, NULL,NULL, 1);
+}
+GEN
+smithall(GEN x)
+{
+  GEN z = cgetg(4, t_VEC);
+  if (typ(x)!=t_MAT) pari_err_TYPE("smithall",x);
+  RgM_check_ZM(x, "smithall");
+  gel(z,3) = ZM_snfall_i(x, (GEN*)(z+1),(GEN*)(z+2), 0);
+  return z;
+}
+
+void
+ZM_snfclean(GEN d, GEN u, GEN v)
+{
+  long i, c, l = lg(d);
+
+  if (typ(d) == t_VEC)
+    for (c=1; c<l; c++) { GEN t = gel(d,c); if (is_pm1(t)) break; }
+  else
+  {
+    for (c=1; c<l; c++) { GEN t = gcoeff(d,c,c); if (is_pm1(t)) break; }
+    if (c < l) for (i = 1; i < c; i++) setlg(gel(d,i), c);
+  }
+  setlg(d, c);
+  if (u) for (i=1; i<l; i++) setlg(gel(u,i), c);
+  if (v) setlg(v, c);
+}
+
+/* Assume z was computed by [g]smithall(). Remove the 1s on the diagonal */
+GEN
+smithclean(GEN z)
+{
+  long i, j, h, l, c, d;
+  GEN U, V, y, D, t;
+
+  if (typ(z) != t_VEC) pari_err_TYPE("smithclean",z);
+  l = lg(z); if (l == 1) return cgetg(1,t_VEC);
+  U = gel(z,1);
+  if (l != 4 || typ(U) != t_MAT)
+  { /* assume z = vector of elementary divisors */
+    for (c=1; c<l; c++)
+      if (gequal1(gel(z,c))) break;
+    return gcopy_lg(z, c);
+  }
+  V = gel(z,2);
+  D = gel(z,3);
+  l = lg(D);
+  if (l == 1) return gcopy(z);
+  h = lgcols(D);
+  if (h > l)
+  { /* D = vconcat(zero matrix, diagonal matrix) */
+    for (c=1+h-l, d=1; c<h; c++,d++)
+      if (gequal1(gcoeff(D,c,d))) break;
+  }
+  else if (h < l)
+  { /* D = concat(zero matrix, diagonal matrix) */
+    for (c=1, d=1+l-h; d<l; c++,d++)
+      if (gequal1(gcoeff(D,c,d))) break;
+  }
+  else
+  { /* D diagonal */
+    for (c=1; c<l; c++)
+      if (gequal1(gcoeff(D,c,c))) break;
+    d = c;
+  }
+  /* U was (h-1)x(h-1), V was (l-1)x(l-1), D was (h-1)x(l-1) */
+  y = cgetg(4,t_VEC);
+  /* truncate U to (c-1) x (h-1) */
+  gel(y,1) = t = cgetg(h,t_MAT);
+  for (j=1; j<h; j++) gel(t,j) = gcopy_lg(gel(U,j), c);
+  /* truncate V to (l-1) x (d-1) */
+  gel(y,2) = gcopy_lg(V, d);
+  gel(y,3) = t = zeromatcopy(c-1, d-1);
+  /* truncate D to a (c-1) x (d-1) matrix */
+  if (d > 1)
+  {
+    if (h > l)
+    {
+      for (i=1+h-l, j=1; i<h; i++,j++)
+        gcoeff(t,i,j) = gcopy(gcoeff(D,i,j));
+    }
+    else if (h < l)
+    {
+      for (i=1, j=1+l-h; j<d; i++,j++)
+        gcoeff(t,i,j) = gcopy(gcoeff(D,i,j));
+    }
+    else
+    {
+      for (j=1; j<d; j++)
+        gcoeff(t,j,j) = gcopy(gcoeff(D,j,j));
+    }
+  }
+  return y;
+}
+
+/* does b = x[i,i] divide all entries in x[1..i-1,1..i-1] ? If so, return 0;
+ * else return the index of a problematic row */
+static long
+gsnf_no_divide(GEN x, long i, long vx)
+{
+  GEN b = gcoeff(x,i,i);
+  long j, k;
+
+  if (gequal0(b))
+  {
+    for (k = 1; k < i; k++)
+      for (j = 1; j < i; j++)
+        if (!gequal0(gcoeff(x,k,j))) return k;
+    return 0;
+  }
+
+  if (!is_RgX(b,vx) || degpol(b)<=0) return 0;
+  for (k = 1; k < i; k++)
+    for (j = 1; j < i; j++)
+    {
+      GEN z = gcoeff(x,k,j), r;
+      if (!is_RgX(z,vx)) z = scalarpol(z, vx);
+      r = RgX_rem(z, b);
+      if (signe(r) && (! isinexactreal(r) ||
+             gexpo(r) > 16 + gexpo(b) - prec2nbits(gprecision(r)))
+         ) return k;
+    }
+  return 0;
+}
+
+/* Hermite Normal Form, with base change matrix if ptB != NULL.
+ * If 'remove' = 1, remove 0 columns (do NOT update *ptB accordingly)
+ * If 'remove' = 2, remove 0 columns and update *ptB accordingly */
+GEN
+RgM_hnfall(GEN A, GEN *pB, long remove)
+{
+  pari_sp av, lim;
+  long li, j, k, m, n, def, ldef;
+  GEN B;
+  long vx = gvar(A);
+
+  n = lg(A)-1;
+  if (vx==NO_VARIABLE || !n)
+  {
+    RgM_check_ZM(A, "mathnf0");
+    return ZM_hnfall(A, pB, remove);
+  }
+  m = nbrows(A);
+  av = avma; lim = stack_lim(av,1);
+  A = RgM_shallowcopy(A);
+  B = pB? matid(n): NULL;
+  def = n; ldef = (m>n)? m-n: 0;
+  for (li=m; li>ldef; li--)
+  {
+    GEN T;
+    for (j=def-1; j; j--)
+    {
+      GEN a = gcoeff(A,li,j);
+      if (gequal0(a)) continue;
+
+      k = (j==1)? def: j-1;
+      RgC_elem(a,gcoeff(A,li,k), A,B, j,k, li, vx);
+    }
+    T = gcoeff(A,li,def);
+    if (gequal0(T))
+    { if (ldef) ldef--; }
+    else
+    {
+      GEN d;
+      gcoeff(A,li,def) = RgX_normalize(T, &d);
+      if (B && !gequal1(d)) gel(B, def) = RgC_Rg_div(gel(B, def), d);
+      RgM_reduce(A, B, li, def, vx);
+      def--;
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ghnfall");
+      gerepileall(av, B? 2: 1, &A, &B);
+    }
+  }
+  /* rank A = n - def */
+  if (remove) remove_0cols(def, &A, &B, remove);
+  gerepileall(av, B? 2: 1, &A, &B);
+  if (B) *pB = B;
+  return A;
+}
+
+static GEN
+gsmithall_i(GEN x,long all)
+{
+  pari_sp av, lim;
+  long i, j, k, n;
+  GEN z, u, v, U, V;
+  long vx = gvar(x);
+  if (typ(x)!=t_MAT) pari_err_TYPE("gsmithall",x);
+  if (vx==NO_VARIABLE) return all? smithall(x): smith(x);
+  n = lg(x)-1;
+  if (!n) return trivsmith(all);
+  if (lgcols(x) != n+1) pari_err_DIM("gsmithall");
+  av = avma; lim = stack_lim(av,1);
+  x = RgM_shallowcopy(x);
+  if (all) { U = matid(n); V = matid(n); }
+  for (i=n; i>=2; i--)
+  {
+    for(;;)
+    {
+      GEN a, b, d;
+      int c = 0;
+      for (j=i-1; j>=1; j--)
+      {
+        b = gcoeff(x,i,j); if (gequal0(b)) continue;
+        a = gcoeff(x,i,i);
+        d = gbezout_step(&b, &a, &v, &u, vx);
+        for (k = 1; k < i; k++)
+        {
+          GEN t = gadd(gmul(u,gcoeff(x,k,i)),gmul(v,gcoeff(x,k,j)));
+          gcoeff(x,k,j) = gsub(gmul(a,gcoeff(x,k,j)),gmul(b,gcoeff(x,k,i)));
+          gcoeff(x,k,i) = t;
+        }
+        gcoeff(x,i,j) = gen_0;
+        gcoeff(x,i,i) = d;
+        if (all) update(u,v,a,b,(GEN*)(V+i),(GEN*)(V+j));
+      }
+      for (j=i-1; j>=1; j--)
+      {
+        b = gcoeff(x,j,i); if (gequal0(b)) continue;
+        a = gcoeff(x,i,i);
+        d = gbezout_step(&b, &a, &v, &u, vx);
+        for (k = 1; k < i; k++)
+        {
+          GEN t = gadd(gmul(u,gcoeff(x,i,k)),gmul(v,gcoeff(x,j,k)));
+          gcoeff(x,j,k) = gsub(gmul(a,gcoeff(x,j,k)),gmul(b,gcoeff(x,i,k)));
+          gcoeff(x,i,k) = t;
+        }
+        gcoeff(x,j,i) = gen_0;
+        gcoeff(x,i,i) = d;
+        if (all) update(u,v,a,b,(GEN*)(U+i),(GEN*)(U+j));
+        c = 1;
+      }
+      if (!c)
+      {
+        k = gsnf_no_divide(x, i, vx);
+        if (!k) break;
+
+        for (j=1; j<=i; j++)
+          gcoeff(x,i,j) = gadd(gcoeff(x,i,j),gcoeff(x,k,j));
+        if (all) gel(U,i) = gadd(gel(U,i),gel(U,k));
+      }
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"gsmithall");
+        gerepileall(av, all? 3: 1, &x, &U, &V);
+      }
+    }
+  }
+  for (k=1; k<=n; k++)
+  {
+    GEN d, T = gcoeff(x,k,k);
+    if (!signe(T)) continue;
+    if (is_RgX(T,vx)) T = RgX_normalize(T, &d); else { d = T; T = gen_1; }
+    if (all && !gequal1(d)) gel(V,k) = RgC_Rg_div(gel(V,k), d);
+    gcoeff(x,k,k) = T;
+  }
+  z = all? mkvec3(shallowtrans(U), V, x): RgM_diagonal_shallow(x);
+  return gerepilecopy(av, z);
+}
+
+GEN
+matsnf0(GEN x,long flag)
+{
+  pari_sp av = avma;
+  if (flag > 7) pari_err_FLAG("matsnf");
+  if (typ(x) == t_VEC && flag & 4) return smithclean(x);
+  if (flag & 2) x = flag&1 ? gsmithall(x): gsmith(x);
+  else          x = flag&1 ?  smithall(x):  smith(x);
+  if (flag & 4) x = gerepileupto(av, smithclean(x));
+  return x;
+}
+
+GEN
+gsmith(GEN x) { return gsmithall_i(x,0); }
+
+GEN
+gsmithall(GEN x) { return gsmithall_i(x,1); }
+
+/* H relation matrix among row of generators g in HNF.  Let URV = D its SNF,
+ * newU R newV = newD its clean SNF (no 1 in Dnew). Return the diagonal of
+ * newD, newU and newUi such that  1/U = (newUi, ?).
+ * Rationale: let (G,0) = g Ui be the new generators then
+ * 0 = G U R --> G D = 0,  g = G newU  and  G = g newUi */
+GEN
+ZM_snf_group(GEN H, GEN *newU, GEN *newUi)
+{
+  GEN D = ZM_snfall_i(H, newU, newUi, 1);
+  long i, j, l;
+
+  ZM_snfclean(D, newU? *newU: NULL, newUi? *newUi: NULL);
+  l = lg(D);
+  if (newU) {
+    GEN U = *newU;
+    for (i = 1; i < l; i++)
+    {
+      GEN d = gel(D,i), d2 = shifti(d, 1);
+      for (j = 1; j < lg(U); j++)
+        gcoeff(U,i,j) = centermodii(gcoeff(U,i,j), d, d2);
+    }
+    *newU = U;
+  }
+  if (newUi) { /* UHV=D -> U^-1 = HVD^-1 -> U^-1 = H(VD^-1 mod 1) mod H */
+    if (l > 1)
+    { /* Ui = ZM_inv(U, gen_1); setlg(Ui, l); */
+      GEN V = FpM_red(*newUi, gel(D,1));
+      GEN Ui = ZM_mul(H, V);
+      for (i = 1; i < l; i++) gel(Ui,i) = ZC_Z_divexact(gel(Ui,i), gel(D,i));
+      *newUi = ZM_hnfrem(Ui, H);
+    }
+  }
+  return D;
+}
+
+/***********************************************************************
+ ****                                                               ****
+ ****         Frobenius form and Jordan form of a matrix            ****
+ ****                                                               ****
+ ***********************************************************************/
+
+static long
+prod_degree(GEN V)
+{
+  long i, s=0, l = lg(V);
+  for (i=1; i<l; i++)
+  {
+    long d = degpol(gel(V,i));
+    if (d<0) return d;
+    s += d;
+  }
+  return s;
+}
+
+GEN
+Frobeniusform(GEN V, long n)
+{
+  long i, j, k;
+  GEN M = zeromatcopy(n,n);
+  for (k=1,i=1;i<lg(V);i++,k++)
+  {
+    GEN  P = gel(V,i);
+    long d = degpol(P);
+    if (k+d-1 > n) pari_err_PREC("matfrobenius");
+    for (j=0; j<d-1; j++, k++) gcoeff(M,k+1,k) = gen_1;
+    for (j=0; j<d; j++) gcoeff(M,k-j,k) = gneg(gel(P, 1+d-j));
+  }
+  return M;
+}
+
+static GEN
+build_frobeniusbc(GEN V, long n)
+{
+  long i, j, k, l, m = lg(V)-1;
+  GEN M = zeromatcopy(n,n), z = monomial(gen_m1, 1, 0); /* -x */
+  for (k=1,l=1+m,i=1;i<=m;i++,k++)
+  {
+    GEN  P = gel(V,i);
+    long d = degpol(P);
+    if (d <= 0) continue;
+    if (l+d-2 > n) pari_err_PREC("matfrobenius");
+    gcoeff(M,k,i) = gen_1;
+    for (j=1; j<d; j++,k++,l++)
+    {
+      gcoeff(M,k,l)   = z;
+      gcoeff(M,k+1,l) = gen_1;
+    }
+  }
+  return M;
+}
+
+static GEN
+build_basischange(GEN N, GEN U)
+{
+  long i, j, n = lg(N)-1;
+  GEN p2 = cgetg(n+1, t_MAT);
+  for (j = 1; j <= n; ++j)
+  {
+    pari_sp btop = avma;
+    GEN p3 = NULL;
+    for (i = 1; i <= n; ++i)
+    {
+      GEN Uij = gcoeff(U, i, j), z;
+      if (is_RgX(Uij, 0))
+        z = RgX_RgM_eval_col(Uij, N, i);
+      else
+        z = Rg_col_ei(Uij, n, i);
+      p3 = p3 ? RgC_add(p3, z):z;
+    }
+    gel(p2,j) = gerepileupto(btop, p3);
+  }
+  return p2;
+}
+
+GEN
+matfrobenius(GEN M, long flag, long v)
+{
+  pari_sp ltop=avma;
+  long n;
+  GEN D, A, N, B, R, M_x;
+  if (typ(M)!=t_MAT) pari_err_TYPE("matfrobenius",M);
+  if (v<0) v=0;
+  if (varncmp(gvar(M), v) <= 0) pari_err_PRIORITY("matfrobenius", M, "<=", v);
+  n = lg(M)-1;
+  if (n && lgcols(M)!=n+1) pari_err_DIM("matfrobenius");
+  M_x = RgM_Rg_add_shallow(M, monomial(gen_m1, 1, v));
+  if (flag<2)
+  {
+    D = matsnf0(M_x,6);
+    if (prod_degree(D) != n) pari_err_PREC("matfrobenius");
+    if (flag != 1) D = Frobeniusform(D, n);
+    return gerepileupto(ltop, D);
+  }
+  if (flag>2) pari_err_FLAG("matfrobenius");
+  A = matsnf0(M_x,3);
+  D = smithclean(RgM_diagonal_shallow(gel(A,3)));
+  if (prod_degree(D) != n) pari_err_PREC("matfrobenius");
+  N = Frobeniusform(D, n);
+  B = build_frobeniusbc(D, n);
+  R = build_basischange(N, RgM_mul(B,gel(A,1)));
+  return gerepilecopy(ltop, mkvec2(N,R));
+}
diff --git a/src/basemath/ifactor1.c b/src/basemath/ifactor1.c
new file mode 100644
index 0000000..e08a560
--- /dev/null
+++ b/src/basemath/ifactor1.c
@@ -0,0 +1,3831 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       PRIMES IN SUCCESSION                        **/
+/** (abstracted by GN 1998Aug21 mainly for use in ellfacteur() below) **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* map from prime residue classes mod 210 to their numbers in {0...47}.
+ * Subscripts into this array take the form ((k-1)%210)/2, ranging from
+ * 0 to 104.  Unused entries are */
+#define NPRC 128                /* non-prime residue class */
+
+static unsigned char prc210_no[] = {
+  0, NPRC, NPRC, NPRC, NPRC, 1, 2, NPRC, 3, 4, NPRC, /* 21 */
+  5, NPRC, NPRC, 6, 7, NPRC, NPRC, 8, NPRC, 9, /* 41 */
+  10, NPRC, 11, NPRC, NPRC, 12, NPRC, NPRC, 13, 14, NPRC, /* 63 */
+  NPRC, 15, NPRC, 16, 17, NPRC, NPRC, 18, NPRC, 19, /* 83 */
+  NPRC, NPRC, 20, NPRC, NPRC, NPRC, 21, NPRC, 22, 23, NPRC, /* 105 */
+  24, 25, NPRC, 26, NPRC, NPRC, NPRC, 27, NPRC, NPRC, /* 125 */
+  28, NPRC, 29, NPRC, NPRC, 30, 31, NPRC, 32, NPRC, NPRC, /* 147 */
+  33, 34, NPRC, NPRC, 35, NPRC, NPRC, 36, NPRC, 37, /* 167 */
+  38, NPRC, 39, NPRC, NPRC, 40, 41, NPRC, NPRC, 42, NPRC, /* 189 */
+  43, 44, NPRC, 45, 46, NPRC, NPRC, NPRC, NPRC, 47, /* 209 */
+};
+
+#if 0
+/* map from prime residue classes mod 210 (by number) to their smallest
+ * positive representatives */
+static unsigned char prc210_rp[] = { /* 19 + 15 + 14 = [0..47] */
+  1, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79,
+  83, 89, 97, 101, 103, 107, 109, 113, 121, 127, 131, 137, 139, 143, 149,
+  151, 157, 163, 167, 169, 173, 179, 181, 187, 191, 193, 197, 199, 209,
+};
+#endif
+
+/* first differences of the preceding */
+static unsigned char prc210_d1[] = {
+  10, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6,
+  4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6,
+  2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2,
+};
+
+/* return 0 for overflow */
+ulong
+unextprime(ulong n)
+{
+  long rc, rc0, rcd, rcn;
+
+  switch(n) {
+    case 0: case 1: case 2: return 2;
+    case 3: return 3;
+    case 4: case 5: return 5;
+    case 6: case 7: return 7;
+  }
+#ifdef LONG_IS_64BIT
+  if (n > (ulong)-59) return 0;
+#else
+  if (n > (ulong)-5) return 0;
+#endif
+  /* here n > 7 */
+  n |= 1; /* make it odd */
+  rc = rc0 = n % 210;
+  /* find next prime residue class mod 210 */
+  for(;;)
+  {
+    rcn = (long)(prc210_no[rc>>1]);
+    if (rcn != NPRC) break;
+    rc += 2; /* cannot wrap since 209 is coprime and rc odd */
+  }
+  if (rc > rc0) n += rc - rc0;
+  /* now find an actual (pseudo)prime */
+  for(;;)
+  {
+    if (uisprime(n)) break;
+    rcd = prc210_d1[rcn];
+    if (++rcn > 47) rcn = 0;
+    n += rcd;
+  }
+  return n;
+}
+
+GEN
+nextprime(GEN n)
+{
+  long rc, rc0, rcd, rcn;
+  pari_sp av = avma;
+
+  if (typ(n) != t_INT)
+  {
+    n = gceil(n);
+    if (typ(n) != t_INT) pari_err_TYPE("nextprime",n);
+  }
+  if (signe(n) <= 0) { avma = av; return gen_2; }
+  if (lgefint(n) == 3)
+  {
+    ulong k = unextprime(n[2]);
+    avma = av;
+    if (k) return utoipos(k);
+#ifdef LONG_IS_64BIT
+    return uutoi(1,13);
+#else
+    return uutoi(1,15);
+#endif
+  }
+  /* here n > 7 */
+  if (!mod2(n)) n = addsi(1,n);
+  rc = rc0 = smodis(n, 210);
+  /* find next prime residue class mod 210 */
+  for(;;)
+  {
+    rcn = (long)(prc210_no[rc>>1]);
+    if (rcn != NPRC) break;
+    rc += 2; /* cannot wrap since 209 is coprime and rc odd */
+  }
+  if (rc > rc0) n = addsi(rc - rc0, n);
+  /* now find an actual (pseudo)prime */
+  for(;;)
+  {
+    if (BPSW_psp(n)) break;
+    rcd = prc210_d1[rcn];
+    if (++rcn > 47) rcn = 0;
+    n = addsi(rcd, n);
+  }
+  if (avma == av) return icopy(n);
+  return gerepileuptoint(av, n);
+}
+
+ulong
+uprecprime(ulong n)
+{
+  long rc, rc0, rcd, rcn;
+  { /* check if n <= 10 */
+    if (n <= 1)  return 0;
+    if (n == 2)  return 2;
+    if (n <= 4)  return 3;
+    if (n <= 6)  return 5;
+    if (n <= 10) return 7;
+  }
+  /* here n >= 11 */
+  if (!(n % 2)) n--;
+  rc = rc0 = n % 210;
+  /* find previous prime residue class mod 210 */
+  for(;;)
+  {
+    rcn = (long)(prc210_no[rc>>1]);
+    if (rcn != NPRC) break;
+    rc -= 2; /* cannot wrap since 1 is coprime and rc odd */
+  }
+  if (rc < rc0) n += rc - rc0;
+  /* now find an actual (pseudo)prime */
+  for(;;)
+  {
+    if (uisprime(n)) break;
+    if (--rcn < 0) rcn = 47;
+    rcd = prc210_d1[rcn];
+    n -= rcd;
+  }
+  return n;
+}
+
+GEN
+precprime(GEN n)
+{
+  long rc, rc0, rcd, rcn;
+  pari_sp av = avma;
+
+  if (typ(n) != t_INT)
+  {
+    n = gfloor(n);
+    if (typ(n) != t_INT) pari_err_TYPE("nextprime",n);
+  }
+  if (signe(n) <= 0) { avma = av; return gen_0; }
+  if (lgefint(n) <= 3)
+  {
+    ulong k = (ulong)n[2];
+    avma = av;
+    return utoi(uprecprime(k));
+  }
+  if (!mod2(n)) n = addsi(-1,n);
+  rc = rc0 = smodis(n, 210);
+  /* find previous prime residue class mod 210 */
+  for(;;)
+  {
+    rcn = (long)(prc210_no[rc>>1]);
+    if (rcn != NPRC) break;
+    rc -= 2; /* cannot wrap since 1 is coprime and rc odd */
+  }
+  if (rc < rc0) n = addsi(rc - rc0, n);
+  /* now find an actual (pseudo)prime */
+  for(;;)
+  {
+    if (BPSW_psp(n)) break;
+    if (--rcn < 0) rcn = 47;
+    rcd = prc210_d1[rcn];
+    n = addsi(-rcd, n);
+  }
+  if (avma == av) return icopy(n);
+  return gerepileuptoint(av, n);
+}
+
+/* Find next single-word prime strictly larger than p.
+ * If **d is non-NULL (somewhere in a diffptr), this is p + *(*d)++;
+ * otherwise imitate nextprime().
+ * *rcn = NPRC or the correct residue class for the current p; we'll use this
+ * to track the current prime residue class mod 210 once we're out of range of
+ * the diffptr table, and we'll update it before that if it isn't NPRC.
+ *
+ * *q is incremented whenever q!=NULL and we wrap from 209 mod 210 to
+ * 1 mod 210
+ * k =  second argument for MR_Jaeschke(). --GN1998Aug22 */
+ulong
+snextpr(ulong p, byteptr *d, long *rcn, long *q, long k)
+{
+  ulong n;
+  if (**d)
+  {
+    byteptr dd = *d;
+    long d1 = 0;
+
+    NEXT_PRIME_VIADIFF(d1,dd);
+    /* d1 = nextprime(p+1) - p */
+    if (*rcn != NPRC)
+    {
+      long rcn0 = *rcn;
+      while (d1 > 0)
+      {
+        d1 -= prc210_d1[*rcn];
+        if (++*rcn > 47) { *rcn = 0; if (q) (*q)++; }
+      }
+      if (d1 < 0)
+      {
+        char *s=stack_sprintf("snextpr: %lu!=prc210_rp[%ld] mod 210\n",p,rcn0);
+        pari_err_BUG(s);
+      }
+    }
+    NEXT_PRIME_VIADIFF(p,*d);
+    return p;
+  }
+  /* we are beyond the diffptr table */
+  if (*rcn == NPRC)
+  { /* initialize */
+    *rcn = prc210_no[(p % 210) >> 1];
+    if (*rcn == NPRC)
+    {
+      char *s = stack_sprintf("snextpr: %lu should have been prime\n", p);
+      pari_err_BUG(s);
+    }
+  }
+  /* look for the next one */
+  n = p + prc210_d1[*rcn];
+  if (++*rcn > 47) *rcn = 0;
+  while (!Fl_MR_Jaeschke(n, k))
+  {
+    n += prc210_d1[*rcn];
+    if (n <= 11) pari_err_OVERFLOW("snextpr");
+    if (++*rcn > 47) { *rcn = 0; if (q) (*q)++; }
+  }
+  return n;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                     INTEGER FACTORIZATION                      **/
+/**                                                                **/
+/********************************************************************/
+int factor_add_primes = 0, factor_proven = 0;
+
+/***********************************************************************/
+/**                                                                   **/
+/**                 FACTORIZATION (ECM) -- GN Jul-Aug 1998            **/
+/**   Integer factorization using the elliptic curves method (ECM).   **/
+/**   ellfacteur() returns a non trivial factor of N, assuming N>0,   **/
+/**   is composite, and has no prime divisor below 2^14 or so.        **/
+/**   Thanks to Paul Zimmermann for much helpful advice and to        **/
+/**   Guillaume Hanrot and Igor Schein for intensive testing          **/
+/**                                                                   **/
+/***********************************************************************/
+#define nbcmax 64                /* max number of simultaneous curves */
+#define bstpmax 1024                /* max number of baby step table entries */
+
+/* addition/doubling/multiplication of a point on an 'elliptic curve mod N'
+ * may result in one of three things:
+ * - a new bona fide point
+ * - a point at infinity (denominator divisible by N)
+ * - a point at infinity mod some p | N but finite mod q | N betraying itself
+ *   by a denominator which has nontrivial gcd with N.
+ *
+ * In the second case, addition/doubling aborts, copying one of the summands
+ * to the destination array of points unless they coincide.
+ * Multiplication will stop at some unpredictable intermediate stage:  The
+ * destination will contain _some_ multiple of the input point, but not
+ * necessarily the desired one, which doesn't matter.  As long as we're
+ * multiplying (B1 phase) we simply carry on with the next multiplier.
+ * During the B2 phase, the only additions are the giant steps, and the
+ * worst that can happen here is that we lose one residue class mod 210
+ * of prime multipliers on 4 of the curves, so again, we ignore the problem
+ * and just carry on.)
+ *
+ * Idea: select nbc curves mod N and one point P on each of them. For each
+ * such P, compute [M]P = Q where M is the product of all powers <= B2 of
+ * primes <= nextprime(B1). Then check whether [p]Q for p < nextprime(B2)
+ * betrays a factor. This second stage looks separately at the primes in
+ * each residue class mod 210, four curves at a time, and steps additively
+ * to ever larger multipliers, by comparing X coordinates of points which we
+ * would need to add in order to reach another prime multiplier in the same
+ * residue class. 'Comparing' means that we accumulate a product of
+ * differences of X coordinates, and from time to time take a gcd of this
+ * product with N. Montgomery's multi-inverse trick is used heavily. */
+
+/* *** auxiliary functions for ellfacteur: *** */
+/* (Rx,Ry) <- (Px,Py)+(Qx,Qy) over Z/NZ, z=1/(Px-Qx). If Ry = NULL, don't set */
+static void
+FpE_add_i(GEN N, GEN z, GEN Px, GEN Py, GEN Qx, GEN Qy, GEN *Rx, GEN *Ry)
+{
+  GEN slope = modii(mulii(subii(Py, Qy), z), N);
+  GEN t = subii(sqri(slope), addii(Qx, Px));
+  affii(modii(t, N), *Rx);
+  if (Ry) {
+    t = subii(mulii(slope, subii(Px, *Rx)), Py);
+    affii(modii(t, N), *Ry);
+  }
+}
+/* X -> Z; cannot add on one of the curves: make sure Z contains
+ * something useful before letting caller proceed */
+static void
+ZV_aff(long n, GEN *X, GEN *Z)
+{
+  if (X != Z) {
+    long k;
+    for (k = n; k--; ) affii(X[k],Z[k]);
+  }
+}
+
+/* Parallel addition on nbc curves, assigning the result to locations at and
+ * following *X3, *Y3. (If Y-coords of result not desired, set Y=NULL.)
+ * Safe even if (X3,Y3) = (X2,Y2), _not_ if (X1,Y1). It is also safe to
+ * overwrite Y2 with X3. If nbc1 < nbc, the first summand is
+ * assumed to hold only nbc1 distinct points, repeated as often as we need
+ * them  (to add one point on each of a few curves to several other points on
+ * the same curves): only used with nbc1 = nbc or nbc1 = 4 | nbc.
+ *
+ * Return 0 [SUCCESS], 1 [N | den], 2 [gcd(den, N) is a factor of N, preserved
+ * in gl.
+ * Stack space is bounded by a constant multiple of lgefint(N)*nbc:
+ * - Phase 2 creates 12 items on the stack per iteration, of which 4 are twice
+ *   as long and 1 is thrice as long as N, i.e. 18 units per iteration.
+ * - Phase  1 creates 4 units.
+ * Total can be as large as 4*nbcmax + 18*8 units; ecm_elladd2() is
+ * just as bad, and elldouble() comes to 3*nbcmax + 29*8 units. */
+static int
+ecm_elladd0(GEN N, GEN *gl, long nbc, long nbc1,
+            GEN *X1, GEN *Y1, GEN *X2, GEN *Y2, GEN *X3, GEN *Y3)
+{
+  const ulong mask = (nbc1 == 4)? 3: ~0UL; /*nbc1 = 4 or nbc*/
+  GEN W[2*nbcmax], *A = W+nbc; /* W[0],A[0] unused */
+  long i;
+  pari_sp av = avma;
+
+  W[1] = subii(X1[0], X2[0]);
+  for (i=1; i<nbc; i++)
+  { /*prepare for multi-inverse*/
+    A[i] = subii(X1[i&mask], X2[i]); /* don't waste time reducing mod N */
+    W[i+1] = modii(mulii(A[i], W[i]), N);
+  }
+  if (!invmod(W[nbc], N, gl))
+  {
+    if (!equalii(N,*gl)) return 2;
+    ZV_aff(nbc, X2,X3);
+    if (Y3) ZV_aff(nbc, Y2,Y3);
+    avma = av; return 1;
+  }
+
+  while (i--) /* nbc times */
+  {
+    pari_sp av2 = avma;
+    GEN Px = X1[i&mask], Py = Y1[i&mask], Qx = X2[i], Qy = Y2[i];
+    GEN z = i? mulii(*gl,W[i]): *gl; /*1/(Px-Qx)*/
+    FpE_add_i(N,z,  Px,Py,Qx,Qy, X3+i, Y3? Y3+i: NULL);
+    if (!i) break;
+    avma = av2; *gl = modii(mulii(*gl, A[i]), N);
+  }
+  avma = av; return 0;
+}
+
+/* Shortcut, for use in cases where Y coordinates follow their corresponding
+ * X coordinates, and first summand doesn't need to be repeated */
+static int
+ecm_elladd(GEN N, GEN *gl, long nbc, GEN *X1, GEN *X2, GEN *X3) {
+  return ecm_elladd0(N, gl, nbc, nbc, X1, X1+nbc, X2, X2+nbc, X3, X3+nbc);
+}
+
+/* As ecm_elladd except it does twice as many additions (and hides even more
+ * of the cost of the modular inverse); the net effect is the same as
+ * ecm_elladd(nbc,X1,X2,X3) && ecm_elladd(nbc,X4,X5,X6). Safe to
+ * have X2=X3, X5=X6, or X1,X2 coincide with X4,X5 in any order. */
+static int
+ecm_elladd2(GEN N, GEN *gl, long nbc,
+            GEN *X1, GEN *X2, GEN *X3, GEN *X4, GEN *X5, GEN *X6)
+{
+  GEN *Y1 = X1+nbc, *Y2 = X2+nbc, *Y3 = X3+nbc;
+  GEN *Y4 = X4+nbc, *Y5 = X5+nbc, *Y6 = X6+nbc;
+  GEN W[4*nbcmax], *A = W+2*nbc; /* W[0],A[0] unused */
+  long i, j;
+  pari_sp av = avma;
+
+  W[1] = subii(X1[0], X2[0]);
+  for (i=1; i<nbc; i++)
+  {
+    A[i] = subii(X1[i], X2[i]); /* don't waste time reducing mod N here */
+    W[i+1] = modii(mulii(A[i], W[i]), N);
+  }
+  for (j=0; j<nbc; i++,j++)
+  {
+    A[i] = subii(X4[j], X5[j]);
+    W[i+1] = modii(mulii(A[i], W[i]), N);
+  }
+  if (!invmod(W[2*nbc], N, gl))
+  {
+    if (!equalii(N,*gl)) return 2;
+    ZV_aff(2*nbc, X2,X3); /* hack: 2*nbc => copy Y2->Y3 */
+    ZV_aff(2*nbc, X5,X6); /* also copy Y5->Y6 */
+    avma = av; return 1;
+  }
+
+  while (j--) /* nbc times */
+  {
+    pari_sp av2 = avma;
+    GEN Px = X4[j], Py = Y4[j], Qx = X5[j], Qy = Y5[j];
+    GEN z = mulii(*gl,W[--i]); /*1/(Px-Qx)*/
+    FpE_add_i(N,z, Px,Py, Qx,Qy, X6+j,Y6+j);
+    avma = av2; *gl = modii(mulii(*gl, A[i]), N);
+  }
+  while (i--) /* nbc times */
+  {
+    pari_sp av2 = avma;
+    GEN Px = X1[i], Py = Y1[i], Qx = X2[i], Qy = Y2[i];
+    GEN z = i? mulii(*gl, W[i]): *gl; /*1/(Px-Qx)*/
+    FpE_add_i(N,z, Px,Py, Qx,Qy, X3+i,Y3+i);
+    if (!i) break;
+    avma = av2; *gl = modii(mulii(*gl, A[i]), N);
+  }
+  avma = av; return 0;
+}
+
+/* Parallel doubling on nbc curves, assigning the result to locations at
+ * and following *X2.  Safe to be called with X2 equal to X1.  Return
+ * value as for ecm_elladd.  If we find a point at infinity mod N,
+ * and if X1 != X2, we copy the points at X1 to X2. */
+static int
+elldouble(GEN N, GEN *gl, long nbc, GEN *X1, GEN *X2)
+{
+  GEN *Y1 = X1+nbc, *Y2 = X2+nbc;
+  GEN W[nbcmax+1]; /* W[0] unused */
+  long i;
+  pari_sp av = avma;
+  /*W[0] = gen_1;*/ W[1] = Y1[0];
+  for (i=1; i<nbc; i++) W[i+1] = modii(mulii(Y1[i], W[i]), N);
+  if (!invmod(W[nbc], N, gl))
+  {
+    if (!equalii(N,*gl)) return 2;
+    ZV_aff(2*nbc,X1,X2); /* also copies Y1->Y2 */
+    avma = av; return 1;
+  }
+  while (i--) /* nbc times */
+  {
+    pari_sp av2;
+    GEN v, w, L, z = i? mulii(*gl,W[i]): *gl;
+    if (i) *gl = modii(mulii(*gl, Y1[i]), N);
+    av2 = avma;
+    L = modii(mulii(addsi(1, mului(3, Fp_sqr(X1[i],N))), z), N);
+    if (signe(L)) /* half of zero is still zero */
+      L = shifti(mod2(L)? addii(L, N): L, -1);
+    v = modii(subii(sqri(L), shifti(X1[i],1)), N);
+    w = modii(subii(mulii(L, subii(X1[i], v)), Y1[i]), N);
+    affii(v, X2[i]);
+    affii(w, Y2[i]);
+    avma = av2;
+  }
+  avma = av; return 0;
+}
+
+/* Parallel multiplication by an odd prime k on nbc curves, storing the
+ * result to locations at and following *X2. Safe to be called with X2 = X1.
+ * Return values as ecm_elladd. Uses (a simplified variant of) Montgomery's
+ * PRAC algorithm; see ftp://ftp.cwi.nl/pub/pmontgom/Lucas.ps.gz .
+ * With thanks to Paul Zimmermann for the reference.  --GN1998Aug13 */
+static int
+get_rule(ulong d, ulong e)
+{
+  if (d <= e + (e>>2)) /* floor(1.25*e) */
+  {
+    if ((d+e)%3 == 0) return 0; /* rule 1 */
+    if ((d-e)%6 == 0) return 1;  /* rule 2 */
+  }
+  /* d <= 4*e but no ofl */
+  if ((d+3)>>2 <= e) return 2; /* rule 3, common case */
+  if ((d&1)==(e&1))  return 1; /* rule 4 = rule 2 */
+  if (!(d&1))        return 3; /* rule 5 */
+  if (d%3 == 0)      return 4; /* rule 6 */
+  if ((d+e)%3 == 0)  return 5; /* rule 7 */
+  if ((d-e)%3 == 0)  return 6; /* rule 8 */
+  /* when we get here, e is even, otherwise one of rules 4,5 would apply */
+  return 7; /* rule 9 */
+}
+
+/* k>2 assumed prime, XAUX = scratchpad */
+static int
+ellmult(GEN N, GEN *gl, long nbc, ulong k, GEN *X1, GEN *X2, GEN *XAUX)
+{
+  ulong r, d, e, e1;
+  int res;
+  GEN *A = X2, *B = XAUX, *T = XAUX + 2*nbc;
+
+  ZV_aff(2*nbc,X1,XAUX);
+  /* first doubling picks up X1;  after this we'll be working in XAUX and
+   * X2 only, mostly via A and B and T */
+  if ((res = elldouble(N, gl, nbc, X1, X2)) != 0) return res;
+
+  /* split the work at the golden ratio */
+  r = (ulong)(k*0.61803398875 + .5);
+  d = k - r;
+  e = r - d; /* d+e == r, so no danger of ofl below */
+  while (d != e)
+  { /* apply one of the nine transformations from PM's Table 4. */
+    switch(get_rule(d,e))
+    {
+    case 0: /* rule 1 */
+      if ( (res = ecm_elladd(N, gl, nbc, A, B, T)) ) return res;
+      if ( (res = ecm_elladd2(N, gl, nbc, T, A, A, T, B, B)) != 0) return res;
+      e1 = d - e; d = (d + e1)/3; e = (e - e1)/3; break;
+    case 1: /* rules 2 and 4 */
+      if ( (res = ecm_elladd(N, gl, nbc, A, B, B)) ) return res;
+      if ( (res = elldouble(N, gl, nbc, A, A)) ) return res;
+      d = (d-e)>>1; break;
+    case 3: /* rule 5 */
+      if ( (res = elldouble(N, gl, nbc, A, A)) ) return res;
+      d >>= 1; break;
+    case 4: /* rule 6 */
+      if ( (res = elldouble(N, gl, nbc, A, T)) ) return res;
+      if ( (res = ecm_elladd(N, gl, nbc, T, A, A)) ) return res;
+      if ( (res = ecm_elladd(N, gl, nbc, A, B, B)) ) return res;
+      d = d/3 - e; break;
+    case 2: /* rule 3 */
+      if ( (res = ecm_elladd(N, gl, nbc, A, B, B)) ) return res;
+      d -= e; break;
+    case 5: /* rule 7 */
+      if ( (res = elldouble(N, gl, nbc, A, T)) ) return res;
+      if ( (res = ecm_elladd2(N, gl, nbc, T, A, A, T, B, B)) != 0) return res;
+      d = (d - 2*e)/3; break;
+    case 6: /* rule 8 */
+      if ( (res = ecm_elladd(N, gl, nbc, A, B, B)) ) return res;
+      if ( (res = elldouble(N, gl, nbc, A, T)) ) return res;
+      if ( (res = ecm_elladd(N, gl, nbc, T, A, A)) ) return res;
+      d = (d - e)/3; break;
+    case 7: /* rule 9 */
+      if ( (res = elldouble(N, gl, nbc, B, B)) ) return res;
+      e >>= 1; break;
+    }
+    /* swap d <-> e and A <-> B if necessary */
+    if (d < e) { lswap(d,e); pswap(A,B); }
+  }
+  return ecm_elladd(N, gl, nbc, XAUX, X2, X2);
+}
+
+/* Auxiliary routines need < (3*nbc+240)*tf words on the PARI stack, in
+ * addition to the spc*(tf+1) words occupied by our main table.
+ * If stack space is already tight, use the heap & newblock(). */
+static GEN*
+alloc_scratch(long nbc, long spc, long tf)
+{
+  long i, tw = evallg(tf) | evaltyp(t_INT), len = spc + 385 + spc*tf;
+  GEN *X, w;
+  if ((long)((GEN)avma - (GEN)bot) < len + (3*nbc + 240)*tf)
+  {
+    if (DEBUGLEVEL>4) err_printf("ECM: stack tight, using heap space\n");
+    X = (GEN*)newblock(len);
+  } else
+    X = (GEN*)new_chunk(len);
+  /* hack for X[i] = cgeti(tf). X = current point in B1 phase */
+  w = (GEN)(X + spc + 385);
+  for (i = spc-1; i >= 0; i--) { X[i] = w; *w = tw; w += tf; }
+  return X;
+}
+
+/* PRAC implementation notes - main changes against the paper version:
+ * (1) The general function [m+n]P = f([m]P,[n]P,[m-n]P) collapses (for m!=n)
+ * to an ecm_elladd() which does not depend on the third argument; thus
+ * references to the third variable (C in the paper) can be eliminated.
+ * (2) Since our multipliers are prime, the outer loop of the paper
+ * version executes only once, and thus is invisible above.
+ * (3) The first step in the inner loop of the paper version will always be
+ * rule 3, but the addition requested by this rule amounts to a doubling, and
+ * will always be followed by a swap, so we have unrolled this first iteration.
+ * (4) Simplifications in rules 6 and 7 are possible given the above, and we
+ * save one addition in each of the two cases.  NB none of the other
+ * ecm_elladd()s in the loop can ever degenerate into an elldouble.
+ * (5) I tried to optimize for rule 3, which is used more frequently than all
+ * others together, but it didn't improve things, so I removed the nested
+ * tight loop again.  --GN */
+
+/* The main loop body of ellfacteur() runs _slower_ under PRAC than under a
+ * straightforward left-shift binary multiplication when N has <30 digits and
+ * B1 is small;  PRAC wins when N and B1 get larger.  Weird. --GN */
+
+/* memory layout in ellfacteur():  a large array of GEN pointers, and one
+ * huge chunk of memory containing all the actual GEN (t_INT) objects.
+ * nbc is constant throughout the invocation:
+ * - The B1 stage of each iteration through the main loop needs little
+ * space:  enough for the X and Y coordinates of the current points,
+ * and twice as much again as scratchpad for ellmult().
+ * - The B2 stage, starting from some current set of points Q, needs, in
+ * succession:
+ *   + space for [2]Q, [4]Q, ..., [10]Q, and [p]Q for building the helix;
+ *   + space for 48*nbc X and Y coordinates to hold the helix.  This could
+ *   re-use [2]Q,...,[8]Q, but only with difficulty, since we don't
+ *   know in advance which residue class mod 210 our p is going to be in.
+ *   It can and should re-use [p]Q, though;
+ *   + space for (temporarily [30]Q and then) [210]Q, [420]Q, and several
+ *   further doublings until the giant step multiplier is reached.  This
+ *   can re-use the remaining cells from above.  The computation of [210]Q
+ *   will have been the last call to ellmult() within this iteration of the
+ *   main loop, so the scratchpad is now also free to be re-used. We also
+ *   compute [630]Q by a parallel addition;  we'll need it later to get the
+ *   baby-step table bootstrapped a little faster.
+ *   + Finally, for no more than 4 curves at a time, room for up to 1024 X
+ *   coordinates only: the Y coordinates needed whilst setting up this baby
+ *   step table are temporarily stored in the upper half, and overwritten
+ *   during the last series of additions.
+ *
+ * Graphically:  after end of B1 stage (X,Y are the coords of Q):
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--
+ * | X Y |  scratch  | [2]Q| [4]Q| [6]Q| [8]Q|[10]Q|    ...    | ...
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--
+ * *X    *XAUX *XT   *XD                                       *XB
+ *
+ * [30]Q is computed from [10]Q.  [210]Q can go into XY, etc:
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--
+ * |[210]|[420]|[630]|[840]|[1680,3360,6720,...,2048*210]      |bstp table...
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--
+ * *X    *XAUX *XT   *XD      [*XG, somewhere here]            *XB .... *XH
+ *
+ * So we need (13 + 48) * 2 * nbc slots here + 4096 slots for the baby step
+ * table (not all of which will be used when we start with a small B1, but
+ * better to allocate and initialize ahead of time all the slots that might
+ * be needed later).
+ *
+ * Note on memory locality:  During the B2 phase, accesses to the helix
+ * (once it is set up) will be clustered by curves (4 out of nbc at a time).
+ * Accesses to the baby steps table will wander from one end of the array to
+ * the other and back, one such cycle per giant step, and during a full cycle
+ * we would expect on the order of 2E4 accesses when using the largest giant
+ * step size.  Thus we shouldn't be doing too bad with respect to thrashing
+ * a 512KBy L2 cache.  However, we don't want the baby step table to grow
+ * larger than this, even if it would reduce the number of EC operations by a
+ * few more per cent for very large B2, lest cache thrashing slow down
+ * everything disproportionally. --GN */
+
+/* parameters for MR_Jaeschke() via snextpr(), for use by ellfacteur() */
+static const long MR_Jaeschke_k1 = 16;/* B1 phase, foolproof below 10^12 */
+static const long MR_Jaeschke_k2 = 1; /* B2 phase, not foolproof, 2xfaster */
+/* MR_Jaeschke_k2 will let thousands of composites slip through, which doesn't
+ * harm ECM, but ellmult() during the B1 phase should only be fed primes
+ * which really are prime */
+/* ellfacteur() has been re-tuned to be useful as a first stage before
+ * MPQS, especially for large arguments, when 'insist' is false, and now
+ * also for the case when 'insist' is true, vaguely following suggestions
+ * by Paul Zimmermann (http://www.loria.fr/~zimmerma/records/ecmnet.html).
+ * --GN 1998Jul,Aug */
+GEN
+ellfacteur(GEN N, int insist)
+{
+  const ulong TB1[] = {
+    142,172,208,252,305,370,450,545,661,801,972,1180,1430,
+    1735,2100,2550,3090,3745,4540,5505,6675,8090,9810,11900,
+    14420,17490,21200,25700,31160,37780UL,45810UL,55550UL,67350UL,
+    81660UL,99010UL,120050UL,145550UL,176475UL,213970UL,259430UL,
+    314550UL,381380UL,462415UL,560660UL,679780UL,824220UL,999340UL,
+    1211670UL,1469110UL,1781250UL,2159700UL,2618600UL,3175000UL,
+    3849600UL,4667500UL,5659200UL,6861600UL,8319500UL,10087100UL,
+    12230300UL,14828900UL,17979600UL,21799700UL,26431500UL,
+    32047300UL,38856400UL, /* 110 times that still fits into 32bits */
+#ifdef LONG_IS_64BIT
+    47112200UL,57122100UL,69258800UL,83974200UL,101816200UL,
+    123449000UL,149678200UL,181480300UL,220039400UL,266791100UL,
+    323476100UL,392204900UL,475536500UL,576573500UL,699077800UL,
+    847610500UL,1027701900UL,1246057200UL,1510806400UL,1831806700UL,
+    2221009800UL,2692906700UL,3265067200UL,3958794400UL,4799917500UL,
+    /* Someone can extend this table when the hardware gets faster */
+#endif
+    };
+  const ulong TB1_for_stage[] = {
+   /* Start below the optimal B1 for finding factors which would just have been
+    * missed by pollardbrent(), and escalate, changing curves to give good
+    * coverage of the small factor ranges. Entries grow faster than what would
+    * be optimal but a table instead of a 2D array keeps the code simple */
+    500,520,560,620,700,800,900,1000,1150,1300,1450,1600,1800,2000,
+    2200,2450,2700,2950,3250,3600,4000,4400,4850,5300,5800,6400,
+    7100,7850,8700,9600,10600,11700,12900,14200,15700,17300,
+    19000,21000,23200,25500,28000,31000,34500UL,38500UL,43000UL,
+    48000UL,53800UL,60400UL,67750UL,76000UL,85300UL,95700UL,
+    107400UL,120500UL,135400UL,152000UL,170800UL,191800UL,215400UL,
+    241800UL,271400UL,304500UL,341500UL,383100UL,429700UL,481900UL,
+    540400UL,606000UL,679500UL,761800UL,854100UL,957500UL,1073500UL,
+  };
+  long nbc,nbc2,dsn,dsnmax,rep,spc,gse,gss,rcn,rcn0,bstp,bstp0;
+  long a, i, j, k, size = expi(N) + 1, tf = lgefint(N);
+  ulong B1,B2,B2_p,B2_rt,m,p,p0,dp;
+  GEN *X,*XAUX,*XT,*XD,*XG,*YG,*XH,*XB,*XB2,*Xh,*Yh,*Xb;
+  GEN res = cgeti(tf), gl;
+  pari_sp av1, avtmp, av = avma;
+  pari_timer T;
+  int rflag;
+
+  /* Determine where to start, how long to persist, and how many curves to
+   * use in parallel */
+  if (insist)
+  {
+#ifdef LONG_IS_64BIT
+    const long DSNMAX = 90;
+#else
+    const long DSNMAX = 65;
+#endif
+    dsnmax = (size >> 2) - 10;
+    if (dsnmax < 0) dsnmax = 0;
+    else if (dsnmax > DSNMAX) dsnmax = DSNMAX;
+    dsn = (size >> 3) - 5;
+    if (dsn < 0) dsn = 0;
+    else if (dsn > 47) dsn = 47;
+    /* pick up the torch where non-insistent stage would have given up */
+    nbc = dsn + (dsn >> 2) + 9; /* 8 or more curves in parallel */
+    nbc &= ~3; /* 4 | nbc */
+    if (nbc > nbcmax) nbc = nbcmax;
+    a = 1 + (nbcmax<<7)*(size&0xffff); /* seed for choice of curves */
+    rep = 0; /* gcc -Wall */
+  }
+  else
+  {
+    dsn = (size - 140) >> 3;
+    if (dsn > 12) dsn = 12;
+    dsnmax = 72;
+    if (dsn < 0) /* < 140 bits: decline the task */
+    {
+#ifdef __EMX__
+      /* unless DOS/EMX: MPQS's disk access is abysmally slow */
+      dsn = 0; rep = 20; nbc = 8;
+#else
+      if (DEBUGLEVEL >= 4)
+        err_printf("ECM: number too small to justify this stage\n");
+      avma = av; return NULL;
+#endif
+    }
+    else
+    {
+      rep = (size <= 248 ?
+             (size <= 176 ? (size - 124) >> 4 : (size - 148) >> 3) :
+             (size - 224) >> 1);
+      nbc = ((size >> 3) << 2) - 80;
+      if (nbc < 8) nbc = 8;
+      else if (nbc > nbcmax) nbc = nbcmax;
+#ifdef __EMX__
+      rep += 20;
+#endif
+    }
+
+    /* Use disjoint sets of curves for non-insist and insist phases; moreover,
+     * repeated calls acting on factors of the same original number should try
+     * to use fresh curves. The following achieves this */
+    a = 1 + (nbcmax<<3)*(size & 0xf);
+  }
+  if (dsn > dsnmax) dsn = dsnmax;
+
+  if (DEBUGLEVEL >= 4)
+  {
+    timer_start(&T);
+    err_printf("ECM: working on %ld curves at a time; initializing", nbc);
+    if (!insist)
+    {
+      if (rep == 1) err_printf(" for one round");
+      else          err_printf(" for up to %ld rounds", rep);
+    }
+    err_printf("...\n");
+  }
+
+  nbc2 = nbc << 1;
+  spc = (13 + 48) * nbc2 + bstpmax * 4;
+  X = alloc_scratch(nbc, spc, tf);
+  XAUX = X    + nbc2; /* scratchpad for ellmult() */
+  XT   = XAUX + nbc2; /* ditto, will later hold [3*210]Q */
+  XD   = XT   + nbc2; /* room for various multiples */
+  XB   = XD   + 10*nbc2; /* start of baby steps table */
+  XB2  = XB   + 2 * bstpmax; /* middle of baby steps table */
+  XH   = XB2  + 2 * bstpmax; /* end of bstps table, start of helix */
+  Xh   = XH   + 48*nbc2; /* little helix, X coords */
+  Yh   = XH   + 192;     /* ditto, Y coords */
+  /* XG will be set inside the main loop, since it depends on B2 */
+
+  /* Xh range of 384 pointers not set; these will later duplicate the pointers
+   * in the XH range, 4 curves at a time. Some of the cells reserved here for
+   * the XB range will never be used, instead, we'll warp the pointers to
+   * connect to (read-only) GENs in the X/XD range */
+  for(;;)
+  {
+    byteptr d0, d = diffptr;
+
+    rcn = NPRC; /* multipliers begin at the beginning */
+    /* pick curves & bounds */
+    for (i = nbc2; i--; ) affui(a++, X[i]);
+    B1 = insist ? TB1[dsn] : TB1_for_stage[dsn];
+    B2 = 110*B1;
+    B2_rt = (ulong)(sqrt((double)B2));
+    /* pick giant step exponent and size.
+     * With 32 baby steps, a giant step corresponds to 32*420 = 13440,
+     * appropriate for the smallest B2s. With 1024, a giant step will be 430080;
+     * appropriate for B1 >~ 42000, where 512 baby steps would imply roughly
+     * the same number of E.C. additions. */
+    gse = B1 < 656
+            ? (B1 < 200? 5: 6)
+            : (B1 < 10500
+              ? (B1 < 2625? 7: 8)
+              : (B1 < 42000? 9: 10));
+    gss = 1UL << gse;
+    XG = XT + gse*nbc2; /* will later hold [2^(gse+1)*210]Q */
+    YG = XG + nbc;
+
+    if (DEBUGLEVEL >= 4) {
+      err_printf("ECM: time = %6ld ms\nECM: dsn = %2ld,\tB1 = %4lu,",
+                 timer_delay(&T), dsn, B1);
+      err_printf("\tB2 = %6lu,\tgss = %4ld*420\n", B2, gss);
+    }
+    p = 0;
+    NEXT_PRIME_VIADIFF(p,d);
+
+    /* ---B1 PHASE--- */
+    /* treat p=2 separately */
+    B2_p = B2 >> 1;
+    for (m=1; m<=B2_p; m<<=1)
+    {
+      if ((rflag = elldouble(N, &gl, nbc, X, X)) > 1) goto fin;
+      else if (rflag) break;
+    }
+    /* p=3,...,nextprime(B1) */
+    while (p < B1 && p <= B2_rt)
+    {
+      pari_sp av = avma;
+      p = snextpr(p, &d, &rcn, NULL, MR_Jaeschke_k1);
+      B2_p = B2/p; /* beware integer overflow on 32-bit CPUs */
+      for (m=1; m<=B2_p; m*=p)
+      {
+        if ((rflag = ellmult(N, &gl, nbc, p, X, X, XAUX)) > 1) goto fin;
+        else if (rflag) break;
+        avma = av;
+      }
+      avma = av;
+    }
+    /* primes p larger than sqrt(B2) appear only to the 1st power */
+    while (p < B1)
+    {
+      pari_sp av = avma;
+      p = snextpr(p, &d, &rcn, NULL, MR_Jaeschke_k1);
+      if (ellmult(N, &gl, nbc, p, X, X, XAUX) > 1) goto fin;
+      avma = av;
+    }
+    if (DEBUGLEVEL >= 4) {
+      err_printf("ECM: time = %6ld ms, B1 phase done, ", timer_delay(&T));
+      err_printf("p = %lu, setting up for B2\n", p);
+    }
+
+    /* ---B2 PHASE--- */
+    /* compute [2]Q,...,[10]Q, needed to build the helix */
+    if (elldouble(N, &gl, nbc, X, XD) > 1) goto fin; /* [2]Q */
+    if (elldouble(N, &gl, nbc, XD, XD + nbc2) > 1) goto fin; /* [4]Q */
+    if (ecm_elladd(N, &gl, nbc, XD, XD + nbc2, XD + (nbc<<2)) > 1)
+      goto fin; /* [6]Q */
+    if (ecm_elladd2(N, &gl, nbc,
+                XD, XD + (nbc<<2), XT + (nbc<<3),
+                XD + nbc2, XD + (nbc<<2), XD + (nbc<<3)) > 1)
+      goto fin; /* [8]Q and [10]Q */
+    if (DEBUGLEVEL >= 7) err_printf("\t(got [2]Q...[10]Q)\n");
+
+    /* get next prime (still using the foolproof test) */
+    p = snextpr(p, &d, &rcn, NULL, MR_Jaeschke_k1);
+    /* make sure we have the residue class number (mod 210) */
+    if (rcn == NPRC)
+    {
+      rcn = prc210_no[(p % 210) >> 1];
+      if (rcn == NPRC)
+      {
+        err_printf("ECM: %lu should have been prime but isn\'t\n", p);
+        pari_err_BUG("ellfacteur");
+      }
+    }
+
+    /* compute [p]Q and put it into its place in the helix */
+    if (ellmult(N, &gl, nbc, p, X, XH + rcn*nbc2, XAUX) > 1) goto fin;
+    if (DEBUGLEVEL >= 7)
+      err_printf("\t(got [p]Q, p = %lu = prc210_rp[%ld] mod 210)\n", p, rcn);
+
+    /* save current p, d, and rcn;  we'll need them more than once below */
+    p0 = p;
+    d0 = d;
+    rcn0 = rcn; /* remember where the helix wraps */
+    bstp0 = 0; /* p is at baby-step offset 0 from itself */
+
+    /* fill up the helix, stepping forward through the prime residue classes
+     * mod 210 until we're back at the r'class of p0.  Keep updating p so
+     * that we can print meaningful diagnostics if a factor shows up; don't
+     * bother checking which of these p's are in fact prime */
+    for (i = 47; i; i--) /* 47 iterations */
+    {
+      p += (dp = (ulong)prc210_d1[rcn]);
+      if (rcn == 47)
+      { /* wrap mod 210 */
+        if (ecm_elladd(N, &gl, nbc, XT+dp*nbc, XH+rcn*nbc2, XH) > 1) goto fin;
+        rcn = 0; continue;
+      }
+      if (ecm_elladd(N, &gl, nbc, XT+dp*nbc, XH+rcn*nbc2, XH+rcn*nbc2+nbc2) > 1)
+        goto fin;
+      rcn++;
+    }
+    if (DEBUGLEVEL >= 7) err_printf("\t(got initial helix)\n");
+    /* compute [210]Q etc, needed for the baby step table */
+    if (ellmult(N, &gl, nbc, 3, XD + (nbc<<3), X, XAUX) > 1) goto fin;
+    if (ellmult(N, &gl, nbc, 7, X, X, XAUX) > 1) goto fin; /* [210]Q */
+    /* this was the last call to ellmult() in the main loop body; may now
+     * overwrite XAUX and slots XD and following */
+    if (elldouble(N, &gl, nbc, X, XAUX) > 1) goto fin; /* [420]Q */
+    if (ecm_elladd(N, &gl, nbc, X, XAUX, XT) > 1) goto fin;/* [630]Q */
+    if (ecm_elladd(N, &gl, nbc, X, XT, XD) > 1) goto fin;  /* [840]Q */
+    for (i=1; i <= gse; i++)
+      if (elldouble(N, &gl, nbc, XT + i*nbc2, XD + i*nbc2) > 1) goto fin;
+    /* (the last iteration has initialized XG to [210*2^(gse+1)]Q) */
+
+    if (DEBUGLEVEL >= 4)
+      err_printf("ECM: time = %6ld ms, entering B2 phase, p = %lu\n",
+                 timer_delay(&T), p);
+
+    /* inner loop over small sets of 4 curves at a time */
+    for (i = nbc - 4; i >= 0; i -= 4)
+    {
+      if (DEBUGLEVEL >= 6)
+        err_printf("ECM: finishing curves %ld...%ld\n", i, i+3);
+      /* Copy relevant pointers from XH to Xh. Memory layout in XH:
+       * nbc X coordinates, nbc Y coordinates for residue class
+       * 1 mod 210, then the same for r.c. 11 mod 210, etc. Memory layout for
+       * Xh is: four X coords for 1 mod 210, four for 11 mod 210, ..., four
+       * for 209 mod 210, then the corresponding Y coordinates in the same
+       * order. This allows a giant step on Xh using just three calls to
+       * ecm_elladd0() each acting on 64 points in parallel */
+      for (j = 48; j--; )
+      {
+        k = nbc2*j + i;
+        m = j << 2; /* X coordinates */
+        Xh[m]   = XH[k];   Xh[m+1] = XH[k+1];
+        Xh[m+2] = XH[k+2]; Xh[m+3] = XH[k+3];
+        k += nbc; /* Y coordinates */
+        Yh[m]   = XH[k];   Yh[m+1] = XH[k+1];
+        Yh[m+2] = XH[k+2]; Yh[m+3] = XH[k+3];
+      }
+      /* Build baby step table of X coords of multiples of [210]Q.  XB[4*j]
+       * will point at X coords on four curves from [(j+1)*210]Q.  Until
+       * we're done, we need some Y coords as well, which we keep in the
+       * second half of the table, overwriting them at the end when gse=10.
+       * Multiples which we already have  (by 1,2,3,4,8,16,...,2^gse) are
+       * entered simply by copying the pointers, ignoring the few slots in w
+       * that were initially reserved for them. Here are the initial entries */
+      for (Xb=XB,k=2,j=i; k--; Xb=XB2,j+=nbc) /* do first X, then Y coords */
+      {
+        Xb[0]  = X[j];      Xb[1]  = X[j+1]; /* [210]Q */
+        Xb[2]  = X[j+2];    Xb[3]  = X[j+3];
+        Xb[4]  = XAUX[j];   Xb[5]  = XAUX[j+1]; /* [420]Q */
+        Xb[6]  = XAUX[j+2]; Xb[7]  = XAUX[j+3];
+        Xb[8]  = XT[j];     Xb[9]  = XT[j+1]; /* [630]Q */
+        Xb[10] = XT[j+2];   Xb[11] = XT[j+3];
+        Xb += 4; /* points at [420]Q */
+        /* ... entries at powers of 2 times 210 .... */
+        for (m = 2; m < (ulong)gse+k; m++) /* omit Y coords of [2^gse*210]Q */
+        {
+          long m2 = m*nbc2 + j;
+          Xb += (2UL<<m); /* points at [2^m*210]Q */
+          Xb[0] = XAUX[m2];   Xb[1] = XAUX[m2+1];
+          Xb[2] = XAUX[m2+2]; Xb[3] = XAUX[m2+3];
+        }
+      }
+      if (DEBUGLEVEL >= 7)
+        err_printf("\t(extracted precomputed helix / baby step entries)\n");
+      /* ... glue in between, up to 16*210 ... */
+      if (ecm_elladd0(N, &gl, 12, 4, /* 12 pts + (4 pts replicated thrice) */
+                  XB + 12, XB2 + 12,
+                  XB,      XB2,
+                  XB + 16, XB2 + 16) > 1) goto fin;  /*4+{1,2,3} = {5,6,7}*/
+      if (ecm_elladd0(N, &gl, 28, 4, /* 28 pts + (4 pts replicated 7fold) */
+                  XB + 28, XB2 + 28,
+                  XB,      XB2,
+                  XB + 32, XB2 + 32) > 1) goto fin; /*8+{1,...,7} = {9,...,15}*/
+      /* ... and the remainder of the lot */
+      for (m = 5; m <= (ulong)gse; m++)
+      { /* fill in from 2^(m-1)+1 to 2^m-1 in chunks of 64 and 60 points */
+        ulong m2 = 2UL << m; /* will point at 2^(m-1)+1 */
+        for (j = 0; (ulong)j < m2-64; j+=64) /* executed 0 times when m = 5 */
+        {
+          if (ecm_elladd0(N, &gl, 64, 4,
+                      XB + m2-4, XB2 + m2-4,
+                      XB + j,    XB2 + j,
+                      XB + m2+j, (m<(ulong)gse? XB2+m2+j: NULL)) > 1) goto fin;
+        } /* j = m2-64 here, 60 points left */
+        if (ecm_elladd0(N, &gl, 60, 4,
+                    XB + m2-4, XB2 + m2-4,
+                    XB + j,    XB2 + j,
+                    XB + m2+j, (m<(ulong)gse? XB2+m2+j: NULL)) > 1) goto fin;
+        /* when m=gse, drop Y coords of result, and when both equal 1024,
+         * overwrite Y coords of second argument with X coords of result */
+      }
+      if (DEBUGLEVEL >= 7) err_printf("\t(baby step table complete)\n");
+      /* initialize a few other things */
+      bstp = bstp0;
+      p = p0; d = d0; rcn = rcn0;
+      gl = gen_1; av1 = avma;
+      /* scratchspace for prod (x_i-x_j) */
+      avtmp = (pari_sp)new_chunk(8 * lgefint(N));
+      /* The correct entry in XB to use depends on bstp and on where we are
+       * on the helix. As we skip from prime to prime, bstp is incremented
+       * by snextpr each time we wrap around through residue class number 0
+       * (1 mod 210), but the baby step should not be taken until rcn>=rcn0,
+       * i.e. until we pass again the residue class of p0.
+       *
+       * The correct signed multiplier is thus k = bstp - (rcn < rcn0),
+       * and the offset from XB is four times (|k| - 1).  When k=0, we ignore
+       * the current prime: if it had led to a factorization, this
+       * would have been noted during the last giant step, or -- when we
+       * first get here -- whilst initializing the helix.  When k > gss,
+       * we must do a giant step and bump bstp back by -2*gss.
+       *
+       * The gcd of the product of X coord differences against N is taken just
+       * before we do a giant step. */
+      while (p < B2)
+      {/* loop over probable primes p0 < p <= nextprime(B2), inserting giant
+        * steps as necessary */
+        /* get next probable prime */
+        p = snextpr(p, &d, &rcn, &bstp, MR_Jaeschke_k2);
+        /* work out the corresponding baby-step multiplier */
+        k = bstp - (rcn < rcn0 ? 1 : 0);
+        /* check whether it's giant-step time */
+        if (k > gss)
+        { /* take gcd */
+          gl = gcdii(gl, N);
+          if (!is_pm1(gl) && !equalii(gl, N)) goto fin;
+          gl = gen_1; avma = av1;
+          while (k > gss)
+          { /* giant step */
+            if (DEBUGLEVEL >= 7) err_printf("\t(giant step at p = %lu)\n", p);
+            if (ecm_elladd0(N, &gl, 64, 4, XG + i, YG + i,
+                        Xh, Yh, Xh, Yh) > 1) goto fin;
+            if (ecm_elladd0(N, &gl, 64, 4, XG + i, YG + i,
+                        Xh + 64, Yh + 64, Xh + 64, Yh + 64) > 1) goto fin;
+            if (ecm_elladd0(N, &gl, 64, 4, XG + i, YG + i,
+                        Xh + 128, Yh + 128, Xh + 128, Yh + 128) > 1) goto fin;
+            bstp -= (gss << 1);
+            k = bstp - (rcn < rcn0? 1: 0); /* recompute multiplier */
+          }
+        }
+        if (!k) continue; /* point of interest is already in Xh */
+        if (k < 0) k = -k;
+        m = ((ulong)k - 1) << 2;
+        /* accumulate product of differences of X coordinates */
+        j = rcn<<2;
+        avma = avtmp; /* go to garbage zone */
+        gl = modii(mulii(gl, subii(XB[m],   Xh[j])), N);
+        gl = modii(mulii(gl, subii(XB[m+1], Xh[j+1])), N);
+        gl = modii(mulii(gl, subii(XB[m+2], Xh[j+2])), N);
+        gl = mulii(gl, subii(XB[m+3], Xh[j+3]));
+        avma = av1;
+        gl = modii(gl, N);
+      } /* loop over p */
+      avma = av1;
+    } /* for i (loop over sets of 4 curves) */
+
+    /* continuation part of main loop */
+    if (dsn < dsnmax)
+    {
+      dsn += insist ? 1 : 2;
+      if (dsn > dsnmax) dsn = dsnmax;
+    }
+
+    if (!insist && !--rep)
+    {
+      if (DEBUGLEVEL >= 4) {
+        err_printf("ECM: time = %6ld ms,\tellfacteur giving up.\n",
+                   timer_delay(&T));
+        err_flush();
+      }
+      res = NULL; goto ret;
+    }
+  }
+fin:
+  affii(gl, res);
+  if (DEBUGLEVEL >= 4) {
+    err_printf("ECM: time = %6ld ms,\tp <= %6lu,\n\tfound factor = %Ps\n",
+               timer_delay(&T), p, res);
+    err_flush();
+  }
+ret:
+  if (!isonstack((GEN)X)) killblock((GEN)X);
+  avma = av; return res;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                FACTORIZATION (Pollard-Brent rho) --GN1998Jun18-26 **/
+/**  pollardbrent() returns a nontrivial factor of n, assuming n is   **/
+/**  composite and has no small prime divisor, or NULL if going on    **/
+/**  would take more time than we want to spend.  Sometimes it finds  **/
+/**  more than one factor, and returns a structure suitable for       **/
+/**  interpretation by ifac_crack. (Cf Algo 8.5.2 in ACiCNT)          **/
+/**                                                                   **/
+/***********************************************************************/
+#define VALUE(x) gel(x,0)
+#define EXPON(x) gel(x,1)
+#define CLASS(x) gel(x,2)
+
+INLINE void
+INIT(GEN x, GEN v, GEN e, GEN c) {
+  VALUE(x) = v;
+  EXPON(x) = e;
+  CLASS(x) = c;
+}
+static void
+ifac_delete(GEN x) { INIT(x,NULL,NULL,NULL); }
+
+static void
+rho_dbg(pari_timer *T, long c, long msg_mask)
+{
+  if (c & msg_mask) return;
+  err_printf("Rho: time = %6ld ms,\t%3ld round%s\n",
+             timer_delay(T), c, (c==1?"":"s"));
+  err_flush();
+}
+
+/* Tuning parameter:  for input up to 64 bits long, we must not spend more
+ * than a very short time, for fear of slowing things down on average.
+ * With the current tuning formula, increase our efforts somewhat at 49 bit
+ * input (an extra round for each bit at first),  and go up more and more
+ * rapidly after we pass 80 bits.-- Changed this to adjust for the presence of
+ * squfof, which will finish input up to 59 bits quickly. */
+
+/* Return NULL when we run out of time, or a single t_INT containing a
+ * nontrivial factor of n, or a vector of t_INTs, each triple of successive
+ * entries containing a factor, an exponent (equal to one),  and a factor
+ * class (NULL for unknown or zero for known composite),  matching the
+ * internal representation used by the ifac_*() routines below.  Repeated
+ * factors may arise;  the caller will sort the factors anyway. */
+GEN
+pollardbrent(GEN n)
+{
+  const long tune_pb_min = 14; /* even 15 seems too much. */
+  long tf = lgefint(n), size = 0, delta, retries = 0, msg_mask;
+  long c0, c, k, k1, l;
+  pari_sp GGG, avP, avx, av = avma;
+  GEN x, x1, y, P, g, g1, res;
+  pari_timer T;
+
+  if (DEBUGLEVEL >= 4) timer_start(&T);
+
+  if (tf >= 4)
+    size = expi(n) + 1;
+  else if (tf == 3)                /* try to keep purify happy...  */
+    size = 1 + expu((ulong)n[2]);
+
+  if (size <= 28)
+    c0 = 32;/* amounts very nearly to 'insist'. Now that we have squfof(), we
+             * don't insist any more when input is 2^29 ... 2^32 */
+  else if (size <= 42)
+    c0 = tune_pb_min;
+  else if (size <= 59) /* match squfof() cutoff point */
+    c0 = tune_pb_min + ((size - 42)<<1);
+  else if (size <= 72)
+    c0 = tune_pb_min + size - 24;
+  else if (size <= 301)
+    /* nonlinear increase in effort, kicking in around 80 bits */
+    /* 301 gives 48121 + tune_pb_min */
+    c0 = tune_pb_min + size - 60 +
+      ((size-73)>>1)*((size-70)>>3)*((size-56)>>4);
+  else
+    c0 = 49152;        /* ECM is faster when it'd take longer */
+
+  c = c0 << 5; /* 2^5 iterations per round */
+  msg_mask = (size >= 448? 0x1fff:
+                           (size >= 192? (256L<<((size-128)>>6))-1: 0xff));
+PB_RETRY:
+ /* trick to make a 'random' choice determined by n.  Don't use x^2+0 or
+  * x^2-2, ever.  Don't use x^2-3 or x^2-7 with a starting value of 2.
+  * x^2+4, x^2+9 are affine conjugate to x^2+1, so don't use them either.
+  *
+  * (the point being that when we get called again on a composite cofactor
+  * of something we've already seen, we had better avoid the same delta) */
+  switch ((size + retries) & 7)
+  {
+    case 0:  delta=  1; break;
+    case 1:  delta= -1; break;
+    case 2:  delta=  3; break;
+    case 3:  delta=  5; break;
+    case 4:  delta= -5; break;
+    case 5:  delta=  7; break;
+    case 6:  delta= 11; break;
+    /* case 7: */
+    default: delta=-11; break;
+  }
+  if (DEBUGLEVEL >= 4)
+  {
+    if (!retries)
+      err_printf("Rho: searching small factor of %ld-bit integer\n", size);
+    else
+      err_printf("Rho: restarting for remaining rounds...\n");
+    err_printf("Rho: using X^2%+1ld for up to %ld rounds of 32 iterations\n",
+               delta, c >> 5);
+    err_flush();
+  }
+  x = gen_2; P = gen_1; g1 = NULL; k = 1; l = 1;
+  (void)new_chunk(10 + 6 * tf); /* enough for cgetg(10) + 3 modii */
+  y = cgeti(tf); affsi(2, y);
+  x1= cgeti(tf); affsi(2, x1);
+  avx = avma;
+  avP = (pari_sp)new_chunk(2 * tf); /* enough for x = addsi(tf+1) */
+  GGG = (pari_sp)new_chunk(4 * tf); /* enough for P = modii(2tf+1, tf) */
+
+  for (;;)                        /* terminated under the control of c */
+  {
+    /* use the polynomial  x^2 + delta */
+#define one_iter() STMT_START {\
+    avma = GGG; x = remii(sqri(x), n); /* to garbage zone */\
+    avma = avx; x = addsi(delta,x);    /* erase garbage */\
+    avma = GGG; P = mulii(P, subii(x1, x));\
+    avma = avP; P = modii(P,n); } STMT_END
+
+    one_iter();
+
+    if ((--c & 0x1f)==0)
+    { /* one round complete */
+      g = gcdii(n, P); if (!is_pm1(g)) goto fin;
+      if (c <= 0)
+      {        /* getting bored */
+        if (DEBUGLEVEL >= 4)
+        {
+          err_printf("Rho: time = %6ld ms,\tPollard-Brent giving up.\n",
+                     timer_delay(&T));
+          err_flush();
+        }
+        avma = av; return NULL;
+      }
+      P = gen_1;                        /* not necessary, but saves 1 mulii/round */
+      if (DEBUGLEVEL >= 4) rho_dbg(&T, c0-(c>>5), msg_mask);
+      affii(x,y);
+    }
+
+    if (--k) continue;                /* normal end of loop body */
+
+    if (c & 0x1f) /* otherwise, we already checked */
+    {
+      g = gcdii(n, P); if (!is_pm1(g)) goto fin;
+      P = gen_1;
+    }
+
+   /* Fast forward phase, doing l inner iterations without computing gcds.
+    * Check first whether it would take us beyond the alloted time.
+    * Fast forward rounds count only half (although they're taking
+    * more like 2/3 the time of normal rounds).  This to counteract the
+    * nuisance that all c0 between 4096 and 6144 would act exactly as
+    * 4096;  with the halving trick only the range 4096..5120 collapses
+    * (similarly for all other powers of two)
+    */
+    if ((c -= (l>>1)) <= 0)
+    {                                /* got bored */
+      if (DEBUGLEVEL >= 4)
+      {
+        err_printf("Rho: time = %6ld ms,\tPollard-Brent giving up.\n",
+                   timer_delay(&T));
+        err_flush();
+      }
+      avma = av; return NULL;
+    }
+    c &= ~0x1f;                        /* keep it on multiples of 32 */
+
+    /* Fast forward loop */
+    affii(x, x1); k = l; l <<= 1;
+    /* don't show this for the first several (short) fast forward phases. */
+    if (DEBUGLEVEL >= 4 && (l>>7) > msg_mask)
+    {
+      err_printf("Rho: fast forward phase (%ld rounds of 64)...\n", l>>7);
+      err_flush();
+    }
+    for (k1=k; k1; k1--) one_iter();
+    if (DEBUGLEVEL >= 4 && (l>>7) > msg_mask)
+    {
+      err_printf("Rho: time = %6ld ms,\t%3ld rounds, back to normal mode\n",
+                 timer_delay(&T), c0-(c>>5));
+      err_flush();
+    }
+
+    affii(x,y);
+  } /* forever */
+
+fin:
+  /* An accumulated gcd was > 1 */
+  /* if it isn't n, and looks prime, return it */
+  if  (!equalii(g,n))
+  {
+    if (MR_Jaeschke(g,17))
+    {
+      if (DEBUGLEVEL >= 4)
+      {
+        rho_dbg(&T, c0-(c>>5), 0);
+        err_printf("\tfound factor = %Ps\n",g);
+        err_flush();
+      }
+      avma = av; return icopy(g);
+    }
+    avma = avx; g1 = icopy(g);  /* known composite, keep it safe */
+    avx = avma;
+  }
+  else g1 = n;                        /* and work modulo g1 for backtracking */
+
+  /* Here g1 is known composite */
+  if (DEBUGLEVEL >= 4 && size > 192)
+  {
+    err_printf("Rho: hang on a second, we got something here...\n");
+    err_flush();
+  }
+  for(;;) /* backtrack until period recovered. Must terminate */
+  {
+    avma = GGG; y = remii(sqri(y), g1);
+    avma = avx; y = addsi(delta,y);
+    g = gcdii(subii(x1, y), g1); if (!is_pm1(g)) break;
+
+    if (DEBUGLEVEL >= 4 && (--c & 0x1f) == 0) rho_dbg(&T, c0-(c>>5), msg_mask);
+  }
+
+  avma = av; /* safe */
+  if (g1 == n || equalii(g,g1))
+  {
+    if (g1 == n && equalii(g,g1))
+    { /* out of luck */
+      if (DEBUGLEVEL >= 4)
+      {
+        rho_dbg(&T, c0-(c>>5), 0);
+        err_printf("\tPollard-Brent failed.\n"); err_flush();
+      }
+      if (++retries >= 4) return NULL;
+      goto PB_RETRY;
+    }
+    /* half lucky: we've split n, but g1 equals either g or n */
+    if (DEBUGLEVEL >= 4)
+    {
+      rho_dbg(&T, c0-(c>>5), 0);
+      err_printf("\tfound %sfactor = %Ps\n", (g1!=n ? "composite " : ""), g);
+      err_flush();
+    }
+    res = cgetg(7, t_VEC);
+    /* g^1: known composite when g1!=n */
+    INIT(res+1, icopy(g), gen_1, (g1!=n? gen_0: NULL));
+    /* cofactor^1: status unknown */
+    INIT(res+4, diviiexact(n,g), gen_1, NULL);
+    return res;
+  }
+  /* g < g1 < n : our lucky day -- we've split g1, too */
+  res = cgetg(10, t_VEC);
+  /* unknown status for all three factors */
+  INIT(res+1, icopy(g),         gen_1, NULL);
+  INIT(res+4, diviiexact(g1,g), gen_1, NULL);
+  INIT(res+7, diviiexact(n,g1), gen_1, NULL);
+  if (DEBUGLEVEL >= 4)
+  {
+    rho_dbg(&T, c0-(c>>5), 0);
+    err_printf("\tfound factors = %Ps, %Ps,\n\tand %Ps\n", res[1], res[4], res[7]);
+    err_flush();
+  }
+  return res;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**              FACTORIZATION (Shanks' SQUFOF) --GN2000Sep30-Oct01   **/
+/**  squfof() returns a nontrivial factor of n, assuming n is odd,    **/
+/**  composite, not a pure square, and has no small prime divisor,    **/
+/**  or NULL if it fails to find one.  It works on two discriminants  **/
+/**  simultaneously  (n and 5n for n=1(4), 3n and 4n for n=3(4)).     **/
+/**  Present implementation is limited to input <2^59, and works most **/
+/**  of the time in signed arithmetic on integers <2^31 in absolute   **/
+/**  size. (Cf. Algo 8.7.2 in ACiCNT)                                 **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* The following is invoked to walk back along the ambiguous cycle* until we
+ * hit an ambiguous form and thus the desired factor, which it returns.  If it
+ * fails for any reason, it returns 0.  It doesn't interfere with timing and
+ * diagnostics, which it leaves to squfof().
+ *
+ * Before we invoke this, we've found a form (A, B, -C) with A = a^2, where a
+ * isn't blacklisted and where gcd(a, B) = 1.  According to ACiCANT, we should
+ * now proceed reducing the form (a, -B, -aC), but it is easy to show that the
+ * first reduction step always sends this to (-aC, B, a), and the next one,
+ * with q computed as usual from B and a (occupying the c position), gives a
+ * reduced form, whose third member is easiest to recover by going back to D.
+ * From this point onwards, we're once again working with single-word numbers.
+ * No need to track signs, just work with the abs values of the coefficients. */
+static long
+squfof_ambig(long a, long B, long dd, GEN D)
+{
+  long b, c, q, qc, qcb, a0, b0, b1, c0;
+  long cnt = 0; /* count reduction steps on the cycle */
+
+  q = (dd + (B>>1)) / a;
+  b = ((q*a) << 1) - B;
+  {
+    pari_sp av = avma;
+    c = itos(divis(shifti(subii(D, sqrs(b)), -2), a));
+    avma = av;
+  }
+#ifdef DEBUG_SQUFOF
+  err_printf("SQUFOF: ambigous cycle of discriminant %Ps\n", D);
+  err_printf("SQUFOF: Form on ambigous cycle (%ld, %ld, %ld)\n", a, b, c);
+#endif
+
+  a0 = a; b0 = b1 = b;        /* end of loop detection and safeguard */
+
+  for (;;) /* reduced cycles are finite */
+  { /* reduction step */
+    c0 = c;
+    if (c0 > dd)
+      q = 1;
+    else
+      q = (dd + (b>>1)) / c0;
+    if (q == 1)
+    {
+      qcb = c0 - b; b = c0 + qcb; c = a - qcb;
+    }
+    else
+    {
+      qc = q*c0; qcb = qc - b; b = qc + qcb; c = a - q*qcb;
+    }
+    a = c0;
+
+    cnt++; if (b == b1) break;
+
+    /* safeguard against infinite loop: recognize when we've walked the entire
+     * cycle in vain. (I don't think this can actually happen -- exercise.) */
+    if (b == b0 && a == a0) return 0;
+
+    b1 = b;
+  }
+  q = a&1 ? a : a>>1;
+  if (DEBUGLEVEL >= 4)
+  {
+    if (q > 1)
+      err_printf("SQUFOF: found factor %ld from ambiguous form\n"
+                 "\tafter %ld steps on the ambiguous cycle\n",
+                 q / ugcd(q,15), cnt);
+    else
+      err_printf("SQUFOF: ...found nothing on the ambiguous cycle\n"
+                 "\tafter %ld steps there\n", cnt);
+    if (DEBUGLEVEL >= 6) err_printf("SQUFOF: squfof_ambig returned %ld\n", q);
+  }
+  return q;
+}
+
+#define SQUFOF_BLACKLIST_SZ 64
+
+/* assume 2,3,5 do not divide n */
+GEN
+squfof(GEN n)
+{
+  ulong d1, d2;
+  long tf = lgefint(n), nm4, cnt = 0;
+  long a1, b1, c1, dd1, L1, a2, b2, c2, dd2, L2, a, q, c, qc, qcb;
+  GEN D1, D2;
+  pari_sp av = avma;
+  long blacklist1[SQUFOF_BLACKLIST_SZ], blacklist2[SQUFOF_BLACKLIST_SZ];
+  long blp1 = 0, blp2 = 0;
+  int act1 = 1, act2 = 1;
+
+#ifdef LONG_IS_64BIT
+  if (tf > 3 || (tf == 3 && (ulong)n[2]          >= (1UL << (BITS_IN_LONG-5))))
+#else  /* 32 bits */
+  if (tf > 4 || (tf == 4 && (ulong)(*int_MSW(n)) >= (1UL << (BITS_IN_LONG-5))))
+#endif
+    return NULL; /* n too large */
+
+  /* now we have 5 < n < 2^59 */
+  nm4 = mod4(n);
+  if (nm4 == 1)
+  { /* n = 1 (mod4):  run one iteration on D1 = n, another on D2 = 5n */
+    D1 = n;
+    D2 = mului(5,n); d2 = itou(sqrti(D2)); dd2 = (long)((d2>>1) + (d2&1));
+    b2 = (long)((d2-1) | 1);        /* b1, b2 will always stay odd */
+  }
+  else
+  { /* n = 3 (mod4):  run one iteration on D1 = 3n, another on D2 = 4n */
+    D1 = mului(3,n);
+    D2 = shifti(n,2); dd2 = itou(sqrti(n)); d2 =  dd2 << 1;
+    b2 = (long)(d2 & (~1UL)); /* largest even below d2, will stay even */
+  }
+  d1 = itou(sqrti(D1));
+  b1 = (long)((d1-1) | 1); /* largest odd number not exceeding d1 */
+  c1 = itos(shifti(subii(D1, sqru((ulong)b1)), -2));
+  if (!c1) pari_err_BUG("squfof [caller of] (n or 3n is a square)");
+  c2 = itos(shifti(subii(D2, sqru((ulong)b2)), -2));
+  if (!c2) pari_err_BUG("squfof [caller of] (5n is a square)");
+  L1 = (long)usqrt(d1);
+  L2 = (long)usqrt(d2);
+  /* dd1 used to compute floor((d1+b1)/2) as dd1+floor(b1/2), without
+   * overflowing the 31bit signed integer size limit. Same for dd2. */
+  dd1 = (long) ((d1>>1) + (d1&1));
+  a1 = a2 = 1;
+
+  /* The two (identity) forms (a1,b1,-c1) and (a2,b2,-c2) are now set up.
+   *
+   * a1 and c1 represent the absolute values of the a,c coefficients; we keep
+   * track of the sign separately, via the iteration counter cnt: when cnt is
+   * even, c is understood to be negative, else c is positive and a < 0.
+   *
+   * L1, L2 are the limits for blacklisting small leading coefficients
+   * on the principal cycle, to guarantee that when we find a square form,
+   * its square root will belong to an ambiguous cycle  (i.e. won't be an
+   * earlier form on the principal cycle).
+   *
+   * When n = 3(mod 4), D2 = 12(mod 16), and b^2 is always 0 or 4 mod 16.
+   * It follows that 4*a*c must be 4 or 8 mod 16, respectively, so at most
+   * one of a,c can be divisible by 2 at most to the first power.  This fact
+   * is used a couple of times below.
+   *
+   * The flags act1, act2 remain true while the respective cycle is still
+   * active;  we drop them to false when we return to the identity form with-
+   * out having found a square form  (or when the blacklist overflows, which
+   * shouldn't happen). */
+  if (DEBUGLEVEL >= 4)
+    err_printf("SQUFOF: entering main loop with forms\n"
+               "\t(1, %ld, %ld) and (1, %ld, %ld)\n\tof discriminants\n"
+               "\t%Ps and %Ps, respectively\n", b1, -c1, b2, -c2, D1, D2);
+
+  /* MAIN LOOP: walk around the principal cycle looking for a square form.
+   * Blacklist small leading coefficients.
+   *
+   * The reduction operator can be computed entirely in 32-bit arithmetic:
+   * Let q = floor(floor((d1+b1)/2)/c1)  (when c1>dd1, q=1, which happens
+   * often enough to special-case it).  Then the new b1 = (q*c1-b1) + q*c1,
+   * which does not overflow, and the new c1 = a1 - q*(q*c1-b1), which is
+   * bounded by d1 in abs size since both the old and the new a1 are positive
+   * and bounded by d1. */
+  while (act1 || act2)
+  {
+    if (act1)
+    { /* send first form through reduction operator if active */
+      c = c1;
+      q = (c > dd1)? 1: (dd1 + (b1>>1)) / c;
+      if (q == 1)
+      { qcb = c - b1; b1 = c + qcb; c1 = a1 - qcb; }
+      else
+      { qc = q*c; qcb = qc - b1; b1 = qc + qcb; c1 = a1 - q*qcb; }
+      a1 = c;
+
+      if (a1 <= L1)
+      { /* blacklist this */
+        if (blp1 >= SQUFOF_BLACKLIST_SZ) /* overflows: shouldn't happen */
+          act1 = 0;                /* silently */
+        else
+        {
+          if (DEBUGLEVEL >= 6)
+            err_printf("SQUFOF: blacklisting a = %ld on first cycle\n", a1);
+          blacklist1[blp1++] = a1;
+        }
+      }
+    }
+    if (act2)
+    { /* send second form through reduction operator if active */
+      c = c2;
+      q = (c > dd2)? 1: (dd2 + (b2>>1)) / c;
+      if (q == 1)
+      { qcb = c - b2; b2 = c + qcb; c2 = a2 - qcb; }
+      else
+      { qc = q*c; qcb = qc - b2; b2 = qc + qcb; c2 = a2 - q*qcb; }
+      a2 = c;
+
+      if (a2 <= L2)
+      { /* blacklist this */
+        if (blp2 >= SQUFOF_BLACKLIST_SZ) /* overflows: shouldn't happen */
+          act2 = 0;                /* silently */
+        else
+        {
+          if (DEBUGLEVEL >= 6)
+            err_printf("SQUFOF: blacklisting a = %ld on second cycle\n", a2);
+          blacklist2[blp2++] = a2;
+        }
+      }
+    }
+
+    /* bump counter, loop if this is an odd iteration (i.e. if the real
+     * leading coefficients are negative) */
+    if (++cnt & 1) continue;
+
+    /* second half of main loop entered only when the leading coefficients
+     * are positive (i.e., during even-numbered iterations) */
+
+    /* examine first form if active */
+    if (act1 && a1 == 1) /* back to identity */
+    { /* drop this discriminant */
+      act1 = 0;
+      if (DEBUGLEVEL >= 4)
+        err_printf("SQUFOF: first cycle exhausted after %ld iterations,\n"
+                   "\tdropping it\n", cnt);
+    }
+    if (act1)
+    {
+      if (uissquareall((ulong)a1, (ulong*)&a))
+      { /* square form */
+        if (DEBUGLEVEL >= 4)
+          err_printf("SQUFOF: square form (%ld^2, %ld, %ld) on first cycle\n"
+                     "\tafter %ld iterations\n", a, b1, -c1, cnt);
+        if (a <= L1)
+        { /* blacklisted? */
+          long j;
+          for (j = 0; j < blp1; j++)
+            if (a == blacklist1[j]) { a = 0; break; }
+        }
+        if (a > 0)
+        { /* not blacklisted */
+          q = ugcd(a, b1); /* imprimitive form? */
+          if (q > 1)
+          { /* q^2 divides D1 hence n [ assuming n % 3 != 0 ] */
+            avma = av;
+            if (DEBUGLEVEL >= 4) err_printf("SQUFOF: found factor %ld^2\n", q);
+            return mkvec3(utoipos(q), gen_2, NULL);/* exponent 2, unknown status */
+          }
+          /* chase the inverse root form back along the ambiguous cycle */
+          q = squfof_ambig(a, b1, dd1, D1);
+          if (nm4 == 3 && q % 3 == 0) q /= 3;
+          if (q > 1) { avma = av; return utoipos(q); } /* SUCCESS! */
+        }
+        else if (DEBUGLEVEL >= 4) /* blacklisted */
+          err_printf("SQUFOF: ...but the root form seems to be on the "
+                     "principal cycle\n");
+      }
+    }
+
+    /* examine second form if active */
+    if (act2 && a2 == 1) /* back to identity form */
+    { /* drop this discriminant */
+      act2 = 0;
+      if (DEBUGLEVEL >= 4)
+        err_printf("SQUFOF: second cycle exhausted after %ld iterations,\n"
+                   "\tdropping it\n", cnt);
+    }
+    if (act2)
+    {
+      if (uissquareall((ulong)a2, (ulong*)&a))
+      { /* square form */
+        if (DEBUGLEVEL >= 4)
+          err_printf("SQUFOF: square form (%ld^2, %ld, %ld) on second cycle\n"
+                     "\tafter %ld iterations\n", a, b2, -c2, cnt);
+        if (a <= L2)
+        { /* blacklisted? */
+          long j;
+          for (j = 0; j < blp2; j++)
+            if (a == blacklist2[j]) { a = 0; break; }
+        }
+        if (a > 0)
+        { /* not blacklisted */
+          q = ugcd(a, b2); /* imprimitive form? */
+          /* NB if b2 is even, a is odd, so the gcd is always odd */
+          if (q > 1)
+          { /* q^2 divides D2 hence n [ assuming n % 5 != 0 ] */
+            avma = av;
+            if (DEBUGLEVEL >= 4) err_printf("SQUFOF: found factor %ld^2\n", q);
+            return mkvec3(utoipos(q), gen_2, NULL);/* exponent 2, unknown status */
+          }
+          /* chase the inverse root form along the ambiguous cycle */
+          q = squfof_ambig(a, b2, dd2, D2);
+          if (nm4 == 1 && q % 5 == 0) q /= 5;
+          if (q > 1) { avma = av; return utoipos(q); } /* SUCCESS! */
+        }
+        else if (DEBUGLEVEL >= 4)        /* blacklisted */
+          err_printf("SQUFOF: ...but the root form seems to be on the "
+                     "principal cycle\n");
+      }
+    }
+  } /* end main loop */
+
+  /* both discriminants turned out to be useless. */
+  if (DEBUGLEVEL>=4) err_printf("SQUFOF: giving up\n");
+  avma = av; return NULL;
+}
+
+/***********************************************************************/
+/*                                                                     */
+/*                    DETECTING ODD POWERS  --GN1998Jun28              */
+/*   Factoring engines like MPQS which ultimately rely on computing    */
+/*   gcd(N, x^2-y^2) to find a nontrivial factor of N can't split      */
+/*   N = p^k for an odd prime p, since (Z/p^k)^* is then cyclic. Here  */
+/*   is an analogue of Z_issquareall() for 3rd, 5th and 7th powers.    */
+/*   The general case is handled by is_kth_power                       */
+/*                                                                     */
+/***********************************************************************/
+
+/* Multistage sieve. First stages work mod 211, 209, 61, 203 in this order
+ * (first reduce mod the product of these and then take the remainder apart).
+ * Second stages use 117, 31, 43, 71. Moduli which are no longer interesting
+ * are skipped. Everything is encoded in a table of 106 24-bit masks. We only
+ * need the first half of the residues.  Three bits per modulus indicate which
+ * residues are 7th (bit 2), 5th (bit 1) or 3rd (bit 0) powers; the eight
+ * moduli above are assigned right-to-left. The table was generated using: */
+
+#if 0
+L = [71, 43, 31, [O(3^2),O(13)], [O(7),O(29)], 61, [O(11),O(19)], 211];
+ispow(x, N, k)=
+{
+  if (type(N) == "t_INT", return (ispower(Mod(x,N), k)));
+  for (i = 1, #N, if (!ispower(x + N[i], k), return (0))); 1
+}
+check(r) =
+{
+  print1("  0");
+  for (i=1,#L,
+    N = 0;
+    if (ispow(r, L[i], 3), N += 1);
+    if (ispow(r, L[i], 5), N += 2);
+    if (ispow(r, L[i], 7), N += 4);
+    print1(N);
+  ); print("ul,  /* ", r, " */")
+}
+for (r = 0, 105, check(r))
+#endif
+static ulong powersmod[106] = {
+  077777777ul,  /* 0 */
+  077777777ul,  /* 1 */
+  013562440ul,  /* 2 */
+  012402540ul,  /* 3 */
+  013562440ul,  /* 4 */
+  052662441ul,  /* 5 */
+  016603440ul,  /* 6 */
+  016463450ul,  /* 7 */
+  013573551ul,  /* 8 */
+  012462540ul,  /* 9 */
+  012462464ul,  /* 10 */
+  013462771ul,  /* 11 */
+  012406473ul,  /* 12 */
+  012463641ul,  /* 13 */
+  052463646ul,  /* 14 */
+  012503446ul,  /* 15 */
+  013562440ul,  /* 16 */
+  052466440ul,  /* 17 */
+  012472451ul,  /* 18 */
+  012462454ul,  /* 19 */
+  032463550ul,  /* 20 */
+  013403664ul,  /* 21 */
+  013463460ul,  /* 22 */
+  032562565ul,  /* 23 */
+  012402540ul,  /* 24 */
+  052662441ul,  /* 25 */
+  032672452ul,  /* 26 */
+  013573551ul,  /* 27 */
+  012467541ul,  /* 28 */
+  012567640ul,  /* 29 */
+  032706450ul,  /* 30 */
+  012762452ul,  /* 31 */
+  033762662ul,  /* 32 */
+  012502562ul,  /* 33 */
+  032463562ul,  /* 34 */
+  013563440ul,  /* 35 */
+  016663440ul,  /* 36 */
+  036662550ul,  /* 37 */
+  012462552ul,  /* 38 */
+  033502450ul,  /* 39 */
+  012462643ul,  /* 40 */
+  033467540ul,  /* 41 */
+  017403441ul,  /* 42 */
+  017463462ul,  /* 43 */
+  017472460ul,  /* 44 */
+  033462470ul,  /* 45 */
+  052566450ul,  /* 46 */
+  013562640ul,  /* 47 */
+  032403640ul,  /* 48 */
+  016463450ul,  /* 49 */
+  016463752ul,  /* 50 */
+  033402440ul,  /* 51 */
+  012462540ul,  /* 52 */
+  012472540ul,  /* 53 */
+  053562462ul,  /* 54 */
+  012463465ul,  /* 55 */
+  012663470ul,  /* 56 */
+  052607450ul,  /* 57 */
+  012566553ul,  /* 58 */
+  013466440ul,  /* 59 */
+  012502741ul,  /* 60 */
+  012762744ul,  /* 61 */
+  012763740ul,  /* 62 */
+  012763443ul,  /* 63 */
+  013573551ul,  /* 64 */
+  013462471ul,  /* 65 */
+  052502460ul,  /* 66 */
+  012662463ul,  /* 67 */
+  012662451ul,  /* 68 */
+  012403550ul,  /* 69 */
+  073567540ul,  /* 70 */
+  072463445ul,  /* 71 */
+  072462740ul,  /* 72 */
+  012472442ul,  /* 73 */
+  012462644ul,  /* 74 */
+  013406650ul,  /* 75 */
+  052463471ul,  /* 76 */
+  012563474ul,  /* 77 */
+  013503460ul,  /* 78 */
+  016462441ul,  /* 79 */
+  016462440ul,  /* 80 */
+  012462540ul,  /* 81 */
+  013462641ul,  /* 82 */
+  012463454ul,  /* 83 */
+  013403550ul,  /* 84 */
+  057563540ul,  /* 85 */
+  017466441ul,  /* 86 */
+  017606471ul,  /* 87 */
+  053666573ul,  /* 88 */
+  012562561ul,  /* 89 */
+  013473641ul,  /* 90 */
+  032573440ul,  /* 91 */
+  016763440ul,  /* 92 */
+  016702640ul,  /* 93 */
+  033762552ul,  /* 94 */
+  012562550ul,  /* 95 */
+  052402451ul,  /* 96 */
+  033563441ul,  /* 97 */
+  012663561ul,  /* 98 */
+  012677560ul,  /* 99 */
+  012462464ul,  /* 100 */
+  032562642ul,  /* 101 */
+  013402551ul,  /* 102 */
+  032462450ul,  /* 103 */
+  012467445ul,  /* 104 */
+  032403440ul,  /* 105 */
+};
+
+static int
+check_res(ulong x, ulong N, int shift, ulong *mask)
+{
+  long r = x%N; if ((ulong)r> (N>>1)) r = N - r;
+  *mask &= (powersmod[r] >> shift);
+  return *mask;
+}
+
+/* is x mod 211*209*61*203*117*31*43*71 a 3rd, 5th or 7th power ? */
+int
+uis_357_powermod(ulong x, ulong *mask)
+{
+  if (             !check_res(x, 211UL, 0, mask)) return 0;
+  if (*mask & 3 && !check_res(x, 209UL, 3, mask)) return 0;
+  if (*mask & 3 && !check_res(x,  61UL, 6, mask)) return 0;
+  if (*mask & 5 && !check_res(x, 203UL, 9, mask)) return 0;
+  if (*mask & 1 && !check_res(x, 117UL,12, mask)) return 0;
+  if (*mask & 3 && !check_res(x,  31UL,15, mask)) return 0;
+  if (*mask & 5 && !check_res(x,  43UL,18, mask)) return 0;
+  if (*mask & 6 && !check_res(x,  71UL,21, mask)) return 0;
+  return 1;
+}
+/* asume x > 0 and pt != NULL */
+int
+uis_357_power(ulong x, ulong *pt, ulong *mask)
+{
+  double logx;
+  if (!odd(x))
+  {
+    long v = vals(x);
+    if (v % 7) *mask &= ~4;
+    if (v % 5) *mask &= ~2;
+    if (v % 3) *mask &= ~1;
+    if (!*mask) return 0;
+  }
+  if (!uis_357_powermod(x, mask)) return 0;
+  logx = log((double)x);
+  while (*mask)
+  {
+    long e, b;
+    ulong y, ye;
+    if (*mask & 1)      { b = 1; e = 3; }
+    else if (*mask & 2) { b = 2; e = 5; }
+    else                { b = 4; e = 7; }
+    y = (ulong)(exp(logx / e) + 0.5);
+    ye = upowuu(y,e);
+    if (ye == x) { *pt = y; return e; }
+#ifdef LONG_IS_64BIT
+    if (ye > x) y--; else y++;
+    ye = upowuu(y,e);
+    if (ye == x) { *pt = y; return e; }
+#endif
+    *mask &= ~b; /* turn the bit off */
+  }
+  return 0;
+}
+
+#ifndef LONG_IS_64BIT
+/* as above, split in two functions */
+/* is x mod 211*209*61*203 a 3rd, 5th or 7th power ? */
+static int
+uis_357_powermod_32bit_1(ulong x, ulong *mask)
+{
+  if (             !check_res(x, 211UL, 0, mask)) return 0;
+  if (*mask & 3 && !check_res(x, 209UL, 3, mask)) return 0;
+  if (*mask & 3 && !check_res(x,  61UL, 6, mask)) return 0;
+  if (*mask & 5 && !check_res(x, 203UL, 9, mask)) return 0;
+  return 1;
+}
+/* is x mod 117*31*43*71 a 3rd, 5th or 7th power ? */
+static int
+uis_357_powermod_32bit_2(ulong x, ulong *mask)
+{
+  if (*mask & 1 && !check_res(x, 117UL,12, mask)) return 0;
+  if (*mask & 3 && !check_res(x,  31UL,15, mask)) return 0;
+  if (*mask & 5 && !check_res(x,  43UL,18, mask)) return 0;
+  if (*mask & 6 && !check_res(x,  71UL,21, mask)) return 0;
+  return 1;
+}
+#endif
+
+/* Returns 3, 5, or 7 if x is a cube (but not a 5th or 7th power),  a 5th
+ * power (but not a 7th),  or a 7th power, and in this case creates the
+ * base on the stack and assigns its address to *pt.  Otherwise returns 0.
+ * x must be of type t_INT and positive;  this is not checked.  The *mask
+ * argument tells us which things to check -- bit 0: 3rd, bit 1: 5th,
+ * bit 2: 7th pwr;  set a bit to have the corresponding power examined --
+ * and is updated appropriately for a possible follow-up call */
+int
+is_357_power(GEN x, GEN *pt, ulong *mask)
+{
+  long lx = lgefint(x);
+  ulong r;
+  pari_sp av;
+  GEN y;
+
+  if (!*mask) return 0; /* useful when running in a loop */
+  if (DEBUGLEVEL>4) err_printf("OddPwrs: examining %ld-bit integer\n", expi(x));
+  if (lgefint(x) == 3) {
+    ulong t;
+    long e = uis_357_power(x[2], &t, mask);
+    if (e)
+    {
+      if (pt) *pt = utoi(t);
+      return e;
+    }
+    return 0;
+  }
+#ifdef LONG_IS_64BIT
+  r = (lx == 3)? (ulong)x[2]: umodiu(x, 6046846918939827UL);
+  if (!uis_357_powermod(r, mask)) return 0;
+#else
+  r = (lx == 3)? (ulong)x[2]: umodiu(x, 211*209*61*203);
+  if (!uis_357_powermod_32bit_1(r, mask)) return 0;
+  r = (lx == 3)? (ulong)x[2]: umodiu(x, 117*31*43*71);
+  if (!uis_357_powermod_32bit_2(r, mask)) return 0;
+#endif
+  av = avma;
+  while (*mask)
+  {
+    long e, b;
+    /* priority to higher powers: if we have a 21st, it is easier to rediscover
+     * that its 7th root is a cube than that its cube root is a 7th power */
+         if (*mask & 4) { b = 4; e = 7; }
+    else if (*mask & 2) { b = 2; e = 5; }
+    else                { b = 1; e = 3; }
+    y = mpround( sqrtnr(itor(x, nbits2prec(64 + bit_accuracy(lx) / e)), e) );
+    if (equalii(powiu(y,e), x))
+    {
+      if (!pt) { avma = av; return e; }
+      avma = (pari_sp)y; *pt = gerepileuptoint(av, y);
+      return e;
+    }
+    if (DEBUGLEVEL>4)
+      err_printf("\tBut it nevertheless wasn't a %ld%s power.\n", e,eng_ord(e));
+    *mask &= ~b; /* turn the bit off */
+    avma = av;
+  }
+  return 0;
+}
+
+/* Is x a n-th power ?
+ * if d = NULL, n not necessarily prime, otherwise, n prime and d the
+ * corresponding diffptr to go on looping over primes.
+ * If pt != NULL, it receives the n-th root */
+ulong
+is_kth_power(GEN x, ulong n, GEN *pt)
+{
+  forprime_t T;
+  long j;
+  ulong q, residue;
+  GEN y;
+  pari_sp av = avma;
+
+  (void)u_forprime_arith_init(&T, odd(n)? 2*n+1: n+1, ULONG_MAX, 1,n);
+  /* we'll start at q, smallest prime >= n */
+
+  /* Modular checks, use small primes q congruent 1 mod n */
+  /* A non n-th power nevertheless passes the test with proba n^(-#checks),
+   * We'd like this < 1e-6 but let j = floor(log(1e-6) / log(n)) which
+   * ensures much less. */
+  if (n < 16)
+    j = 5;
+  else if (n < 32)
+    j = 4;
+  else if (n < 101)
+    j = 3;
+  else if (n < 1001)
+    j = 2;
+  else if (n < 17886697) /* smallest such that smallest suitable q is > 2^32 */
+    j = 1;
+  else
+    j = 0;
+  for (; j > 0; j--)
+  {
+    if (!(q = u_forprime_next(&T))) break;
+    /* q a prime = 1 mod n */
+    residue = umodiu(x, q);
+    if (residue == 0)
+    {
+      if (Z_lval(x,q) % n) { avma = av; return 0; }
+      continue;
+    }
+    /* n-th power mod q ? */
+    if (Fl_powu(residue, (q-1)/n, q) != 1) { avma = av; return 0; }
+  }
+  avma = av;
+
+  if (DEBUGLEVEL>4) err_printf("\nOddPwrs: [%lu] passed modular checks\n",n);
+  /* go to the horse's mouth... */
+  y = roundr( sqrtnr(itor(x, nbits2prec((expi(x)+16*n)/n)), n) );
+  if (!equalii(powiu(y, n), x)) {
+    if (DEBUGLEVEL>4) err_printf("\tBut it wasn't a pure power.\n");
+    avma = av; return 0;
+  }
+  if (!pt) avma = av; else { avma = (pari_sp)y; *pt = gerepileuptoint(av, y); }
+  return 1;
+}
+
+/* is x a p^i-th power, p >= 11 prime ? Similar to is_357_power(), but instead
+ * of the mask, we keep the current test exponent around. Cut off when
+ * log_2 x^(1/k) < cutoffbits since we would have found it by trial division.
+ * Everything needed here (primitive roots etc.) is computed from scratch on
+ * the fly; compared to the size of numbers under consideration, these
+ * word-sized computations take negligible time.
+ * Any cutoffbits > 0 is safe, but direct root extraction attempts are faster
+ * when trial division has been used to discover very small bases. We become
+ * competitive at cutoffbits ~ 10 */
+int
+is_pth_power(GEN x, GEN *pt, forprime_t *T, ulong cutoffbits)
+{
+  long cnt=0, size = expi(x) /* not +1 */;
+  ulong p;
+  pari_sp av = avma;
+  while ((p = u_forprime_next(T)) && size/p >= cutoffbits) {
+    long v = 1;
+    if (DEBUGLEVEL>5 && cnt++==2000)
+      { cnt=0; err_printf("%lu%% ", 100*p*cutoffbits/size); }
+    while (is_kth_power(x, p, pt)) {
+      v *= p; x = *pt;
+      size = expi(x);
+    }
+    if (v > 1)
+    {
+      if (DEBUGLEVEL>5) err_printf("\nOddPwrs: is a %ld power\n",v);
+      return v;
+    }
+  }
+  if (DEBUGLEVEL>5) err_printf("\nOddPwrs: not a power\n",p);
+  avma = av; return 0; /* give up */
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                FACTORIZATION  (master iteration)                  **/
+/**      Driver for the various methods of finding large factors      **/
+/**      (after trial division has cast out the very small ones).     **/
+/**                        GN1998Jun24--30                            **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* Direct use:
+ *  ifac_start_hint(n,moebius,hint) registers with the iterative factorizer
+ *  - an integer n (without prime factors  < tridiv_bound(n))
+ *  - registers whether or not we should terminate early if we find a square
+ *    factor,
+ *  - a hint about which method(s) to use.
+ *  This must always be called first. If input is not composite, oo loop.
+ *  The routine decomposes n nontrivially into a product of two factors except
+ *  in squarefreeness ('Moebius') mode.
+ *
+ *  ifac_start(n,moebius) same using default hint.
+ *
+ *  ifac_primary_factor()  returns a prime divisor (not necessarily the
+ *    smallest) and the corresponding exponent.
+ *
+ * Encapsulated user interface: Many arithmetic functions have a 'contributor'
+ * ifac_xxx, to be called on any large composite cofactor left over after trial
+ * division by small primes: xxx is one of moebius, issquarefree, totient, etc.
+ *
+ * We never test whether the input number is prime or composite, since
+ * presumably it will have come out of the small factors finder stage
+ * (which doesn't really exist yet but which will test the left-over
+ * cofactor for primality once it does). */
+
+/* The data structure in which we preserve whatever we know about our number N
+ * is kept on the PARI stack, and updated as needed.
+ * This makes the machinery re-entrant, and avoids memory leaks when a lengthy
+ * factorization is interrupted. We try to keep the whole affair connected,
+ * and the parent object is always older than its children.  This may in
+ * rare cases lead to some extra copying around, and knowing what is garbage
+ * at any given time is not trivial. See below for examples how to do it right.
+ * (Connectedness is destroyed if callers of ifac_main() create stuff on the
+ * stack in between calls. This is harmless as long as ifac_realloc() is used
+ * to re-create a connected object at the head of the stack just before
+ * collecting garbage.)
+ * A t_INT may well have > 10^6 distinct prime factors larger than 2^16. Since
+ * we need not find factors in order of increasing size, we must be prepared to
+ * drag a very large amount of data around.  We start with a small structure
+ * and extend it when necessary. */
+
+/* The idea of the algorithm is:
+ * Let N0 be whatever is currently left of N after dividing off all the
+ * prime powers we have already returned to the caller.  Then we maintain
+ * N0 as a product
+ * (1) N0 = \prod_i P_i^{e_i} * \prod_j Q_j^{f_j} * \prod_k C_k^{g_k}
+ * where the P_i and Q_j are distinct primes, each C_k is known composite,
+ * none of the P_i divides any C_k, and we also know the total ordering
+ * of all the P_i, Q_j and C_k; in particular, we will never try to divide
+ * a C_k by a larger Q_j.  Some of the C_k may have common factors.
+ *
+ * Caveat implementor:  Taking gcds among C_k's is very likely to cost at
+ * least as much time as dividing off any primes as we find them, and book-
+ * keeping would be tough (since D=gcd(C_1,C_2) can still have common factors
+ * with both C_1/D and C_2/D, and so on...).
+ *
+ * At startup, we just initialize the structure to
+ * (2) N = C_1^1   (composite).
+ *
+ * Whenever ifac_primary_factor() or one of the arithmetic user interface
+ * routines needs a primary factor, and the smallest thing in our list is P_1,
+ * we return that and its exponent, and remove it from our list. (When nothing
+ * is left, we return a sentinel value -- gen_1.  And in Moebius mode, when we
+ * see something with exponent > 1, whether prime or composite, we return gen_0
+ * or 0, depending on the function). In all other cases, ifac_main() iterates
+ * the following steps until we have a P_1 in the smallest position.
+ *
+ * When the smallest item is C_1, as it is initially:
+ * (3.1) Crack C_1 into a nontrivial product  U_1 * U_2  by whatever method
+ * comes to mind for this size. (U for 'unknown'.)  Cracking will detect
+ * perfect powers, so we may instead see a power of some U_1 here, or even
+ * something of the form U_1^k*U_2^k; of course the exponent already attached
+ * to C_1 is taken into account in the following.
+ * (3.2) If we have U_1*U_2, sort the two factors (distinct: squares are caught
+ * in stage 3.1). N.B. U_1 and U_2 are smaller than anything else in our list.
+ * (3.3) Check U_1 and U_2 for primality, and flag them accordingly.
+ * (3.4) Iterate.
+ *
+ * When the smallest item is Q_1:
+ * This is the unpleasant case.  We go through the entire list and try to
+ * divide Q_1 off each of the current C_k's, which usually fails, but may
+ * succeed several times. When a division was successful, the corresponding
+ * C_k is removed from our list, and the cofactor becomes a U_l for the moment
+ * unless it is 1 (which happens when C_k was a power of Q_1).  When we're
+ * through we upgrade Q_1 to P_1 status, then do a primality check on each U_l
+ * and sort it back into the list either as a Q_j or as a C_k.  If during the
+ * insertion sort we discover that some U_l equals some P_i or Q_j or C_k we
+ * already have, we just add U_l's exponent to that of its twin. (The sorting
+ * therefore happens before the primality test). Since this may produce one or
+ * more elements smaller than the P_1 we just confirmed, we may have to repeat
+ * the iteration.
+ * A trick avoids some Q_1 instances: just after the sweep classifying
+ * all current unknowns as either composites or primes, we do another downward
+ * sweep beginning with the largest current factor and stopping just above the
+ * largest current composite.  Every Q_j we pass is turned into a P_i.
+ * (Different primes are automatically coprime among each other, and primes do
+ * not divide smaller composites.)
+ * NB: We have no use for comparing the square of a prime to N0.  Normally
+ * we will get called after casting out only the smallest primes, and
+ * since we cannot guarantee that we see the large prime factors in as-
+ * cending order, we cannot stop when we find one larger than sqrt(N0). */
+
+/* Data structure: We keep everything in a single t_VEC of t_INTs.  The
+ * first 2 components are read-only:
+ * 1) the first records whether we're doing full (NULL) or Moebius (gen_1)
+ * factorization; in the latter case subroutines return a sentinel value as
+ * soon as they spot an exponent > 1.
+ * 2) the second records the hint from factorint()'s optional flag, for use by
+ * ifac_crack().
+ *
+ * The remaining components (initially 15) are used in groups of three:
+ * [ factor (t_INT), exponent (t_INT), factor class ], where factor class is
+ *  NULL : unknown
+ *  gen_0: known composite C_k
+ *  gen_1: known prime Q_j awaiting trial division
+ *  gen_2: finished prime P_i.
+ * When during the division stage we re-sort a C_k-turned-U_l to a lower
+ * position, we rotate any intervening material upward towards its old
+ * slot.  When a C_k was divided down to 1, its slot is left empty at
+ * first; similarly when the re-sorting detects a repeated factor.
+ * After the sorting phase, we de-fragment the list and squeeze all the
+ * occupied slots together to the high end, so that ifac_crack() has room
+ * for new factors.  When this doesn't suffice, we abandon the current vector
+ * and allocate a somewhat larger one, defragmenting again while copying.
+ *
+ * For internal use: note that all exponents will fit into C longs, given
+ * PARI's lgefint field size.  When we work with them, we sometimes read
+ * out the GEN pointer, and sometimes do an itos, whatever is more con-
+ * venient for the task at hand. */
+
+/*** Overview ***/
+
+/* The '*where' argument in the following points into *partial at the first of
+ * the three fields of the first occupied slot.  It's there because the caller
+ * would already know where 'here' is, so we don't want to search for it again.
+ * We do not preserve this from one user-interface call to the next. */
+
+/* In the most common cases, control flows from the user interface to
+ * ifac_main() and then to a succession of ifac_crack()s and ifac_divide()s,
+ * with (typically) none of the latter finding anything. */
+
+static long ifac_insert_multiplet(GEN *, GEN *, GEN, long);
+
+#define LAST(x) x+lg(x)-3
+#define FIRST(x) x+3
+
+#define MOEBIUS(x) gel(x,1)
+#define HINT(x) gel(x,2)
+
+/* y <- x */
+INLINE void
+SHALLOWCOPY(GEN x, GEN y) {
+  VALUE(y) = VALUE(x);
+  EXPON(y) = EXPON(x);
+  CLASS(y) = CLASS(x);
+}
+/* y <- x */
+INLINE void
+COPY(GEN x, GEN y) {
+  icopyifstack(VALUE(x), VALUE(y));
+  icopyifstack(EXPON(x), EXPON(y));
+  CLASS(y) = CLASS(x);
+}
+
+/* Diagnostics */
+static void
+ifac_factor_dbg(GEN x)
+{
+  GEN c = CLASS(x), v = VALUE(x);
+  if (c == gen_2) err_printf("IFAC: factor %Ps\n\tis prime (finished)\n", v);
+  else if (c == gen_1) err_printf("IFAC: factor %Ps\n\tis prime\n", v);
+  else if (c == gen_0) err_printf("IFAC: factor %Ps\n\tis composite\n", v);
+}
+static void
+ifac_check(GEN partial, GEN where)
+{
+  if (!where || where < FIRST(partial) || where > LAST(partial))
+    pari_err_BUG("ifac_check ['where' out of bounds]");
+}
+static void
+ifac_print(GEN part, GEN where)
+{
+  long l = lg(part);
+  GEN p;
+
+  err_printf("ifac partial factorization structure: %ld slots, ", (l-3)/3);
+  if (MOEBIUS(part)) err_printf("Moebius mode, ");
+  err_printf("hint = %ld\n", itos(HINT(part)));
+  ifac_check(part, where);
+  for (p = part+3; p < part + l; p += 3)
+  {
+    GEN v = VALUE(p), e = EXPON(p), c = CLASS(p);
+    const char *s = "";
+    if (!v) { err_printf("[empty slot]\n"); continue; }
+    if (c == NULL) s = "unknown";
+    else if (c == gen_0) s = "composite";
+    else if (c == gen_1) s = "unfinished prime";
+    else if (c == gen_2) s = "prime";
+    else pari_err_BUG("unknown factor class");
+    err_printf("[%Ps, %Ps, %s]\n", v, e, s);
+  }
+  err_printf("Done.\n");
+}
+
+static const long decomp_default_hint = 0;
+/* assume n a non-zero t_INT */
+/* return initial data structure, see ifac_crack() for the hint argument */
+static GEN
+ifac_start_hint(GEN n, int moebius, long hint)
+{
+  const long ifac_initial_length = 3 + 7*3;
+  /* codeword, moebius, hint, 7 slots -- a 512-bit product of distinct 8-bit
+   * primes needs at most 7 slots at a time) */
+  GEN here, part = cgetg(ifac_initial_length, t_VEC);
+
+  MOEBIUS(part) = moebius? gen_1 : NULL;
+  HINT(part) = stoi(hint);
+  if (isonstack(n)) n = absi(n);
+  /* make copy, because we'll later want to replace it in place.
+   * If it's not on stack, then we assume it is a clone made for us by
+   * ifactor, and we assume the sign has already been set positive */
+  /* fill first slot at the top end */
+  here = part + ifac_initial_length - 3; /* LAST(part) */
+  INIT(here, n,gen_1,gen_0); /* n^1: composite */
+  while ((here -= 3) > part) ifac_delete(here);
+  return part;
+}
+GEN
+ifac_start(GEN n, int moebius)
+{ return ifac_start_hint(n,moebius,decomp_default_hint); }
+
+/* Return next nonempty slot after 'here', NULL if none exist */
+static GEN
+ifac_find(GEN partial)
+{
+  GEN scan, end = partial + lg(partial);
+
+#ifdef IFAC_DEBUG
+  ifac_check(partial, partial);
+#endif
+  for (scan = partial+3; scan < end; scan += 3)
+    if (VALUE(scan)) return scan;
+  return NULL;
+}
+
+/* Defragment: squeeze out unoccupied slots above *where. Unoccupied slots
+ * arise when a composite factor dissolves completely whilst dividing off a
+ * prime, or when ifac_resort() spots a coincidence and merges two factors.
+ * Update *where */
+static void
+ifac_defrag(GEN *partial, GEN *where)
+{
+  GEN scan_new = LAST(*partial), scan_old;
+
+  for (scan_old = scan_new; scan_old >= *where; scan_old -= 3)
+  {
+    if (!VALUE(scan_old)) continue; /* empty slot */
+    if (scan_old < scan_new) SHALLOWCOPY(scan_old, scan_new);
+    scan_new -= 3; /* point at next slot to be written */
+  }
+  scan_new += 3; /* back up to last slot written */
+  *where = scan_new;
+  while ((scan_new -= 3) > *partial) ifac_delete(scan_new); /* erase junk */
+}
+
+/* Move to a larger main vector, updating *where if it points into it, and
+ * *partial in any case. Can be used as a specialized gcopy before
+ * a gerepileupto() (pass 0 as the new length). Normally, one would pass
+ * new_lg=1 to let this function guess the new size.  To be used sparingly.
+ * Complex version of ifac_defrag(), combined with reallocation.  If new_lg
+ * is 0, use the old length, so this acts just like gcopy except that the
+ * 'where' pointer is carried along; if it is 1, we make an educated guess.
+ * Exception:  If new_lg is 0, the vector is full to the brim, and the first
+ * entry is composite, we make it longer to avoid being called again a
+ * microsecond later. It is safe to call this with *where = NULL:
+ * if it doesn't point anywhere within the old structure, it is left alone */
+static void
+ifac_realloc(GEN *partial, GEN *where, long new_lg)
+{
+  long old_lg = lg(*partial);
+  GEN newpart, scan_new, scan_old;
+
+  if (new_lg == 1)
+    new_lg = 2*old_lg - 6;        /* from 7 slots to 13 to 25... */
+  else if (new_lg <= old_lg)        /* includes case new_lg == 0 */
+  {
+    GEN first = *partial + 3;
+    new_lg = old_lg;
+    /* structure full and first entry composite or unknown */
+    if (VALUE(first) && (CLASS(first) == gen_0 || CLASS(first)==NULL))
+      new_lg += 6; /* give it a little more breathing space */
+  }
+  newpart = cgetg(new_lg, t_VEC);
+  if (DEBUGMEM >= 3)
+    err_printf("IFAC: new partial factorization structure (%ld slots)\n",
+               (new_lg - 3)/3);
+  MOEBIUS(newpart) = MOEBIUS(*partial);
+  icopyifstack(HINT(*partial), HINT(newpart));
+  /* Downward sweep through the old *partial. Pick up 'where' and carry it
+   * over if we pass it. (Only useful if it pointed at a non-empty slot.)
+   * Factors are COPY'd so that we again have a nice object (parent older
+   * than children, connected), except the one factor that may still be living
+   * in a clone where n originally was; exponents are similarly copied if they
+   * aren't global constants; class-of-factor fields are global constants so we
+   * need only copy them as pointers. Caller may then do a gerepileupto() */
+  scan_new = newpart + new_lg - 3; /* LAST(newpart) */
+  scan_old = *partial + old_lg - 3; /* LAST(*partial) */
+  for (; scan_old > *partial + 2; scan_old -= 3)
+  {
+    if (*where == scan_old) *where = scan_new;
+    if (!VALUE(scan_old)) continue; /* skip empty slots */
+    COPY(scan_old, scan_new); scan_new -= 3;
+  }
+  scan_new += 3; /* back up to last slot written */
+  while ((scan_new -= 3) > newpart) ifac_delete(scan_new);
+  *partial = newpart;
+}
+
+/* Re-sort one (typically unknown) entry from washere to a new position,
+ * rotating intervening entries upward to fill the vacant space. If the new
+ * position is the same as the old one, or the new value of the entry coincides
+ * with a value already occupying a lower slot, then we just add exponents (and
+ * use the 'more known' class, and return 1 immediately when in Moebius mode).
+ * Slots between *where and washere must be in sorted order, so a sweep using
+ * this to re-sort several unknowns must proceed upward, see ifac_resort().
+ * Bubble-sort-of-thing sort. Won't be exercised frequently, so this is ok */
+static void
+ifac_sort_one(GEN *where, GEN washere)
+{
+  GEN old, scan = washere - 3;
+  GEN value, exponent, class0, class1;
+  long cmp_res;
+
+  if (scan < *where) return; /* nothing to do, washere==*where */
+  value    = VALUE(washere);
+  exponent = EXPON(washere);
+  class0 = CLASS(washere);
+  cmp_res = -1; /* sentinel */
+  while (scan >= *where) /* at least once */
+  {
+    if (VALUE(scan))
+    { /* current slot nonempty, check against where */
+      cmp_res = cmpii(value, VALUE(scan));
+      if (cmp_res >= 0) break; /* have found where to stop */
+    }
+    /* copy current slot upward by one position and move pointers down */
+    SHALLOWCOPY(scan, scan+3);
+    scan -= 3;
+  }
+  scan += 3;
+  /* At this point there are the following possibilities:
+   * 1) cmp_res == -1. Either value is less than that at *where, or *where was
+   * pointing at vacant slots and any factors we saw en route were larger than
+   * value. At any rate, scan == *where now, and scan is pointing at an empty
+   * slot, into which we'll stash our entry.
+   * 2) cmp_res == 0. The entry at scan-3 is the one, we compare class0
+   * fields and add exponents, and put it all into the vacated scan slot,
+   * NULLing the one at scan-3 (and possibly updating *where).
+   * 3) cmp_res == 1. The slot at scan is the one to store our entry into. */
+  if (cmp_res)
+  {
+    if (cmp_res < 0 && scan != *where)
+      pari_err_BUG("ifact_sort_one [misaligned partial]");
+    INIT(scan, value, exponent, class0); return;
+  }
+  /* case cmp_res == 0: repeated factor detected */
+  if (DEBUGLEVEL >= 4)
+    err_printf("IFAC: repeated factor %Ps\n\tin ifac_sort_one\n", value);
+  old = scan - 3;
+  /* if old class0 was composite and new is prime, or vice versa, complain
+   * (and if one class0 was unknown and the other wasn't, use the known one) */
+  class1 = CLASS(old);
+  if (class0) /* should never be used */
+  {
+    if (class1)
+    {
+      if (class0 == gen_0 && class1 != gen_0)
+        pari_err_BUG("ifac_sort_one (composite = prime)");
+      else if (class0 != gen_0 && class1 == gen_0)
+        pari_err_BUG("ifac_sort_one (prime = composite)");
+      else if (class0 == gen_2)
+        CLASS(scan) = class0;
+    }
+    else
+      CLASS(scan) = class0;
+  }
+  /* else stay with the existing known class0 */
+  CLASS(scan) = class1;
+  /* in any case, add exponents */
+  if (EXPON(old) == gen_1 && exponent == gen_1)
+    EXPON(scan) = gen_2;
+  else
+    EXPON(scan) = addii(EXPON(old), exponent);
+  /* move the value over and null out the vacated slot below */
+  old = scan - 3;
+  *scan = *old;
+  ifac_delete(old);
+  /* finally, see whether *where should be pulled in */
+  if (old == *where) *where += 3;
+}
+
+/* Sort all current unknowns downward to where they belong. Sweeps in the
+ * upward direction. Not needed after ifac_crack(), only when ifac_divide()
+ * returned true. Update *where. */
+static void
+ifac_resort(GEN *partial, GEN *where)
+{
+  GEN scan, end;
+  ifac_defrag(partial, where); end = LAST(*partial);
+  for (scan = *where; scan <= end; scan += 3)
+    if (VALUE(scan) && !CLASS(scan)) ifac_sort_one(where, scan); /*unknown*/
+  ifac_defrag(partial, where); /* remove newly created gaps */
+}
+
+/* Let x be a t_INT known not to have small divisors (< 2^14). Return 0 if x
+ * is a proven composite. Return 1 if we believe it to be prime (fully proven
+ * prime if factor_proven is set).  */
+int
+ifac_isprime(GEN x)
+{
+  if (!BPSW_psp_nosmalldiv(x)) return 0; /* composite */
+  if (factor_proven && ! BPSW_isprime(x))
+  {
+    pari_warn(warner,
+              "IFAC: pseudo-prime %Ps\n\tis not prime. PLEASE REPORT!\n", x);
+    return 0;
+  }
+  return 1;
+}
+
+static int
+ifac_checkprime(GEN x)
+{
+  int res = ifac_isprime(VALUE(x));
+  CLASS(x) = res? gen_1: gen_0;
+  if (DEBUGLEVEL>2) ifac_factor_dbg(x);
+  return res;
+}
+
+/* Determine primality or compositeness of all current unknowns, and set
+ * class Q primes to finished (class P) if everything larger is already
+ * known to be prime.  When after_crack >= 0, only look at the
+ * first after_crack things in the list (do nothing when it's 0) */
+static void
+ifac_whoiswho(GEN *partial, GEN *where, long after_crack)
+{
+  GEN scan, scan_end = LAST(*partial);
+
+#ifdef IFAC_DEBUG
+  ifac_check(*partial, *where);
+#endif
+  if (after_crack == 0) return;
+  if (after_crack > 0) /* check at most after_crack entries */
+    scan = *where + 3*(after_crack - 1); /* assert(scan <= scan_end) */
+  else
+    for (scan = scan_end; scan >= *where; scan -= 3)
+    {
+      if (CLASS(scan))
+      { /* known class of factor */
+        if (CLASS(scan) == gen_0) break;
+        if (CLASS(scan) == gen_1)
+        {
+          if (DEBUGLEVEL>=3)
+          {
+            err_printf("IFAC: factor %Ps\n\tis prime (no larger composite)\n",
+                       VALUE(*where));
+            err_printf("IFAC: prime %Ps\n\tappears with exponent = %ld\n",
+                       VALUE(*where), itos(EXPON(*where)));
+          }
+          CLASS(scan) = gen_2;
+        }
+        continue;
+      }
+      if (!ifac_checkprime(scan)) break; /* must disable Q-to-P */
+      CLASS(scan) = gen_2; /* P_i, finished prime */
+      if (DEBUGLEVEL>2) ifac_factor_dbg(scan);
+    }
+  /* go on, Q-to-P trick now disabled */
+  for (; scan >= *where; scan -= 3)
+  {
+    if (CLASS(scan)) continue;
+    (void)ifac_checkprime(scan); /* Qj | Ck */
+  }
+}
+
+/* Divide all current composites by first (prime, class Q) entry, updating its
+ * exponent, and turning it into a finished prime (class P).  Return 1 if any
+ * such divisions succeeded  (in Moebius mode, the update may then not have
+ * been completed), or 0 if none of them succeeded.  Doesn't modify *where.
+ * Here we normally do not check that the first entry is a not-finished
+ * prime.  Stack management: we may allocate a new exponent */
+static long
+ifac_divide(GEN *partial, GEN *where, long moebius_mode)
+{
+  GEN scan, scan_end = LAST(*partial);
+  long res = 0, exponent, newexp, otherexp;
+
+#ifdef IFAC_DEBUG
+  ifac_check(*partial, *where);
+  if (CLASS(*where) != gen_1)
+    pari_err_BUG("ifac_divide [division by composite or finished prime]");
+  if (!VALUE(*where)) pari_err_BUG("ifac_divide [division by nothing]");
+#endif
+  newexp = exponent = itos(EXPON(*where));
+  if (exponent > 1 && moebius_mode) return 1;
+  /* should've been caught by caller */
+
+  for (scan = *where+3; scan <= scan_end; scan += 3)
+  {
+    if (CLASS(scan) != gen_0) continue; /* the other thing ain't composite */
+    otherexp = 0;
+    /* divide in place to keep stack clutter minimal */
+    while (dvdiiz(VALUE(scan), VALUE(*where), VALUE(scan)))
+    {
+      if (moebius_mode) return 1; /* immediately */
+      if (!otherexp) otherexp = itos(EXPON(scan));
+      newexp += otherexp;
+    }
+    if (newexp > exponent)        /* did anything happen? */
+    {
+      EXPON(*where) = (newexp == 2 ? gen_2 : utoipos(newexp));
+      exponent = newexp;
+      if (is_pm1((GEN)*scan)) /* factor dissolved completely */
+      {
+        ifac_delete(scan);
+        if (DEBUGLEVEL >= 4)
+          err_printf("IFAC: a factor was a power of another prime factor\n");
+      } else {
+        CLASS(scan) = NULL;        /* at any rate it's Unknown now */
+        if (DEBUGLEVEL >= 4)
+          err_printf("IFAC: a factor was divisible by another prime factor,\n"
+                     "\tleaving a cofactor = %Ps\n", VALUE(scan));
+      }
+      res = 1;
+      if (DEBUGLEVEL >= 5)
+        err_printf("IFAC: prime %Ps\n\tappears at least to the power %ld\n",
+                   VALUE(*where), newexp);
+    }
+  } /* for */
+  CLASS(*where) = gen_2; /* make it a finished prime */
+  if (DEBUGLEVEL >= 3)
+    err_printf("IFAC: prime %Ps\n\tappears with exponent = %ld\n",
+               VALUE(*where), newexp);
+  return res;
+}
+
+/* found out our integer was factor^exp. Update */
+static void
+update_pow(GEN where, GEN factor, long exp, pari_sp *av)
+{
+  GEN ex = EXPON(where);
+  if (DEBUGLEVEL>3)
+    err_printf("IFAC: found %Ps =\n\t%Ps ^%ld\n", *where, factor, exp);
+  affii(factor, VALUE(where)); avma = *av;
+  if (ex == gen_1)
+  { EXPON(where) = exp == 2? gen_2: utoipos(exp); *av = avma; }
+  else if (ex == gen_2)
+  { EXPON(where) = utoipos(exp<<1); *av = avma; }
+  else
+    affsi(exp * itos(ex), EXPON(where));
+}
+/* hint == 0 : Use a default strategy
+ * hint & 1  : Avoid mpqs(), use ellfacteur() after pollardbrent()
+ * hint & 2  : Avoid first-stage ellfacteur() in favour of mpqs()
+ * (may still fall back to ellfacteur() if mpqs() is not installed or gives up)
+ * hint & 4  : Avoid even the pollardbrent() and squfof() stages. Put under
+ *  the same governing  bit, for no good reason other than avoiding a
+ *  proliferation of bits.
+ * hint & 8  : Avoid final ellfacteur(); this may declare a composite to be
+ *  prime.  */
+#define get_hint(partial) (itos(HINT(*partial)) & 15)
+
+/* Split the first (composite) entry.  There _must_ already be room for another
+ * factor below *where, and *where is updated. Two cases:
+ * - entry = factor^k is a pure power: factor^k is inserted, leaving *where
+ *   unchanged;
+ * - entry = factor * cofactor (not necessarily coprime): both factors are
+ *   inserted in the correct order, updating *where
+ * The inserted factors class is set to unknown, they inherit the exponent
+ * (or a multiple thereof) of their ancestor.
+ *
+ * Returns number of factors written into the structure, normally 2 (1 if pure
+ * power, maybe > 2 if a factoring engine returned a vector of factors instead
+ * of a single factor). Can reallocate the data structure in the
+ * vector-of-factors case, not in the most common single-factor case.
+ * Stack housekeeping:  this routine may create one or more objects  (a new
+ * factor, or possibly several, and perhaps one or more new exponents > 2) */
+static long
+ifac_crack(GEN *partial, GEN *where, long moebius_mode)
+{
+  long cmp_res, hint = get_hint(partial);
+  GEN factor, exponent;
+
+#ifdef IFAC_DEBUG
+  ifac_check(*partial, *where);
+  if (*where < *partial + 6)
+    pari_err_BUG("ifac_crack ['*where' out of bounds]");
+  if (!(VALUE(*where)) || typ(VALUE(*where)) != t_INT)
+    pari_err_BUG("ifac_crack [incorrect VALUE(*where)]");
+  if (CLASS(*where) != gen_0)
+    pari_err_BUG("ifac_crack [operand not known composite]");
+#endif
+
+  if (DEBUGLEVEL>2) {
+    err_printf("IFAC: cracking composite\n\t%Ps\n", **where);
+    if (DEBUGLEVEL>3) err_printf("IFAC: checking for pure square\n");
+  }
+  /* MPQS cannot factor prime powers. Look for pure powers even if MPQS is
+   * blocked by hint: fast and useful in bounded factorization */
+  {
+    forprime_t T;
+    ulong exp = 1, mask = 7;
+    long good = 0;
+    pari_sp av = avma;
+    (void)u_forprime_init(&T, 11, ULONG_MAX);
+    /* crack squares */
+    while (Z_issquareall(VALUE(*where), &factor))
+    {
+      good = 1; /* remember we succeeded once */
+      update_pow(*where, factor, 2, &av);
+      if (moebius_mode) return 0; /* no need to carry on */
+    }
+    while ( (exp = is_357_power(VALUE(*where), &factor, &mask)) )
+    {
+      good = 1; /* remember we succeeded once */
+      update_pow(*where, factor, exp, &av);
+      if (moebius_mode) return 0; /* no need to carry on */
+    }
+    /* cutoff at 14 bits as trial division must have found everything below */
+    while ( (exp = is_pth_power(VALUE(*where), &factor, &T, 15)) )
+    {
+      good = 1; /* remember we succeeded once */
+      update_pow(*where, factor, exp, &av);
+      if (moebius_mode) return 0; /* no need to carry on */
+    }
+
+    if (good && hint != 15 && ifac_checkprime(*where))
+    { /* our composite was a prime power */
+      if (DEBUGLEVEL>3)
+        err_printf("IFAC: factor %Ps\n\tis prime\n", VALUE(*where));
+      return 0; /* bypass subsequent ifac_whoiswho() call */
+    }
+  } /* pure power stage */
+
+  factor = NULL;
+  if (!(hint & 4))
+  { /* pollardbrent() Rho usually gets a first chance */
+    if (DEBUGLEVEL >= 4) err_printf("IFAC: trying Pollard-Brent rho method\n");
+    factor = pollardbrent(VALUE(*where));
+    if (!factor)
+    { /* Shanks' squfof() */
+      if (DEBUGLEVEL >= 4)
+        err_printf("IFAC: trying Shanks' SQUFOF, will fail silently if input\n"
+                   "      is too large for it.\n");
+      factor = squfof(VALUE(*where));
+    }
+  }
+  if (!factor && !(hint & 2))
+  { /* First ECM stage */
+    if (DEBUGLEVEL >= 4) err_printf("IFAC: trying Lenstra-Montgomery ECM\n");
+    factor = ellfacteur(VALUE(*where), 0); /* do not insist */
+  }
+  if (!factor && !(hint & 1))
+  { /* MPQS stage */
+    if (DEBUGLEVEL >= 4) err_printf("IFAC: trying MPQS\n");
+    factor = mpqs(VALUE(*where));
+  }
+  if (!factor)
+  {
+    if (!(hint & 8))
+    { /* still no luck? Final ECM stage, guaranteed to succeed */
+      if (DEBUGLEVEL >= 4)
+        err_printf("IFAC: forcing ECM, may take some time\n");
+      factor = ellfacteur(VALUE(*where), 1);
+    }
+    else
+    { /* limited factorization */
+      if (DEBUGLEVEL >= 2)
+      {
+        if (hint != 15)
+          pari_warn(warner, "IFAC: unfactored composite declared prime");
+        else
+          pari_warn(warner, "IFAC: untested integer declared prime");
+
+        /* don't print it out at level 3 or above, where it would appear
+         * several times before and after this message already */
+        if (DEBUGLEVEL == 2) err_printf("\t%Ps\n", VALUE(*where));
+      }
+      CLASS(*where) = gen_1; /* might as well trial-divide by it... */
+      return 1;
+    }
+  }
+  if (typ(factor) == t_VEC) /* delegate this case */
+    return ifac_insert_multiplet(partial, where, factor, moebius_mode);
+  /* typ(factor) == t_INT */
+  /* got single integer back:  work out the cofactor (in place) */
+  if (!dvdiiz(VALUE(*where), factor, VALUE(*where)))
+  {
+    err_printf("IFAC: factoring %Ps\n", VALUE(*where));
+    err_printf("\tyielded 'factor' %Ps\n\twhich isn't!\n", factor);
+    pari_err_BUG("factoring");
+  }
+  /* factoring engines report the factor found; tell about the cofactor */
+  if (DEBUGLEVEL >= 4) err_printf("IFAC: cofactor = %Ps\n", VALUE(*where));
+
+  /* The two factors are 'factor' and VALUE(*where), find out which is larger */
+  cmp_res = cmpii(factor, VALUE(*where));
+  CLASS(*where) = NULL; /* mark factor /cofactor 'unknown' */
+  exponent = EXPON(*where);
+  *where -= 3;
+  CLASS(*where) = NULL; /* mark factor /cofactor 'unknown' */
+  EXPON(*where) = isonstack(exponent)? icopy(exponent): exponent;
+  if (cmp_res < 0)
+    VALUE(*where) = factor; /* common case */
+  else if (cmp_res > 0)
+  { /* factor > cofactor, rearrange */
+    GEN old = *where + 3;
+    VALUE(*where) = VALUE(old); /* move cofactor pointer to lowest slot */
+    VALUE(old) = factor; /* save factor */
+  }
+  else pari_err_BUG("ifac_crack [Z_issquareall miss]");
+  return 2;
+}
+
+/* Gets called to complete ifac_crack's job when a factoring engine splits
+ * the current factor into a product of three or more new factors. Makes room
+ * for them if necessary, sorts them, gives them the right exponents and class.
+ * Also returns the number of factors actually written, which may be less than
+ * the number of components in facvec if there are duplicates.--- Vectors of
+ * factors  (cf pollardbrent()) actually contain 'slots' of three GENs per
+ * factor with the three fields interpreted as in our partial factorization
+ * data structure.  Thus 'engines' can tell us what they already happen to
+ * know about factors being prime or composite and/or appearing to a power
+ * larger than the first.
+ * Don't collect garbage.  No diagnostics: the factoring engine should have
+ * printed what it found. facvec contains slots of three components per factor;
+ * repeated factors are allowed  (and their classes shouldn't contradict each
+ * other whereas their exponents will be added up) */
+static long
+ifac_insert_multiplet(GEN *partial, GEN *where, GEN facvec, long moebius_mode)
+{
+  long j,k=1, lfv=lg(facvec)-1, nf=lfv/3, room=(long)(*where-*partial);
+  /* one of the factors will go into the *where slot, so room is now 3 times
+   * the number of slots we can use */
+  long needroom = lfv - room;
+  GEN e, newexp, cur, sorted, auxvec = cgetg(nf+1, t_VEC), factor;
+  long exponent = itos(EXPON(*where)); /* the old exponent */
+
+  if (DEBUGLEVEL >= 5) /* squfof may return a single squared factor as a set */
+    err_printf("IFAC: incorporating set of %ld factor(s)\n", nf);
+  if (needroom > 0) /* one extra slot for paranoia, errm, future use */
+    ifac_realloc(partial, where, lg(*partial) + needroom + 3);
+
+  /* create sort permutation from the values of the factors */
+  for (j=nf; j; j--) auxvec[j] = facvec[3*j-2]; /* just the pointers */
+  sorted = indexsort(auxvec);
+  /* and readjust the result for the triple spacing */
+  for (j=nf; j; j--) sorted[j] = 3*sorted[j]-2;
+
+  /* store factors, beginning at *where, and catching any duplicates */
+  cur = facvec + sorted[nf];
+  VALUE(*where) = VALUE(cur);
+  newexp = EXPON(cur);
+  if (newexp != gen_1) /* new exponent > 1 */
+  {
+    if (exponent == 1)
+      e = isonstack(newexp)? icopy(newexp): newexp;
+    else
+      e = mului(exponent, newexp);
+    EXPON(*where) = e;
+  } /* if new exponent is 1, the old exponent already in place will do */
+  CLASS(*where) = CLASS(cur);
+  if (DEBUGLEVEL >= 6) err_printf("\tstored (largest) factor no. %ld...\n", nf);
+
+  for (j=nf-1; j; j--)
+  {
+    cur = facvec + sorted[j];
+    factor = VALUE(cur);
+    if (equalii(factor, VALUE(*where)))
+    {
+      if (DEBUGLEVEL >= 6)
+        err_printf("\tfactor no. %ld is a duplicate%s\n", j, (j>1? "...": ""));
+      /* update exponent, ignore class which would already have been set,
+       * then forget current factor */
+      newexp = EXPON(cur);
+      if (newexp != gen_1) /* new exp > 1 */
+        e = addis(EXPON(*where), exponent * itos(newexp));
+      else if (EXPON(*where) == gen_1 && exponent == 1)
+        e = gen_2;
+      else
+        e = addis(EXPON(*where), exponent);
+      EXPON(*where) = e;
+
+      if (moebius_mode) return 0; /* stop now, but with exponent updated */
+      continue;
+    }
+
+    *where -= 3;
+    CLASS(*where) = CLASS(cur);        /* class as given */
+    newexp = EXPON(cur);
+    if (newexp != gen_1) /* new exp > 1 */
+    {
+      if (exponent == 1 && newexp == gen_2)
+        e = gen_2;
+      else /* exponent*newexp > 2 */
+        e = mului(exponent, newexp);
+    }
+    else
+      e = (exponent == 1 ? gen_1 :
+            (exponent == 2 ? gen_2 :
+               utoipos(exponent))); /* inherit parent's exponent */
+    EXPON(*where) = e;
+    /* keep components younger than *partial */
+    VALUE(*where) = isonstack(factor) ? icopy(factor) : factor;
+    k++;
+    if (DEBUGLEVEL >= 6)
+      err_printf("\tfactor no. %ld was unique%s\n", j, j>1? " (so far)...": "");
+  }
+  /* make the 'sorted' object safe for garbage collection (it should be in the
+   * garbage zone from everybody's perspective, but it's easy to do it) */
+  *sorted = evaltyp(t_INT) | evallg(nf+1);
+  return k;
+}
+
+/* main loop:  iterate until smallest entry is a finished prime;  returns
+ * a 'where' pointer, or NULL if nothing left, or gen_0 in Moebius mode if
+ * we aren't squarefree */
+static GEN
+ifac_main(GEN *partial)
+{
+  const long moebius_mode = !!MOEBIUS(*partial);
+  GEN here = ifac_find(*partial);
+  long nf;
+
+  if (!here) return NULL; /* nothing left */
+  /* loop until first entry is a finished prime.  May involve reallocations,
+   * thus updates of *partial */
+  while (CLASS(here) != gen_2)
+  {
+    if (CLASS(here) == gen_0) /* composite: crack it */
+    { /* make sure there's room for another factor */
+      if (here < *partial + 6)
+      {
+        ifac_defrag(partial, &here);
+        if (here < *partial + 6) ifac_realloc(partial, &here, 1); /* no luck */
+      }
+      nf = ifac_crack(partial, &here, moebius_mode);
+      if (moebius_mode && EXPON(here) != gen_1) /* that was a power */
+      {
+        if (DEBUGLEVEL >= 3)
+          err_printf("IFAC: main loop: repeated new factor\n\t%Ps\n", *here);
+        return gen_0;
+      }
+      /* deal with the new unknowns.  No sort: ifac_crack did it */
+      ifac_whoiswho(partial, &here, nf);
+      continue;
+    }
+    if (CLASS(here) == gen_1) /* prime but not yet finished: finish it */
+    {
+      if (ifac_divide(partial, &here, moebius_mode))
+      {
+        if (moebius_mode)
+        {
+          if (DEBUGLEVEL >= 3)
+            err_printf("IFAC: main loop: another factor was divisible by\n"
+                       "\t%Ps\n", *here);
+          return gen_0;
+        }
+        ifac_resort(partial, &here); /* sort new cofactors down */
+        ifac_whoiswho(partial, &here, -1);
+      }
+      continue;
+    }
+    pari_err_BUG("ifac_main [non-existent factor class]");
+  } /* while */
+  if (moebius_mode && EXPON(here) != gen_1)
+  {
+    if (DEBUGLEVEL >= 3)
+      err_printf("IFAC: after main loop: repeated old factor\n\t%Ps\n", *here);
+    return gen_0;
+  }
+  if (DEBUGLEVEL >= 4)
+  {
+    nf = (*partial + lg(*partial) - here - 3)/3;
+    if (nf)
+      err_printf("IFAC: main loop: %ld factor%s left\n", nf, (nf>1)? "s": "");
+    else
+      err_printf("IFAC: main loop: this was the last factor\n");
+  }
+  if (factor_add_primes && !(get_hint(partial) & 8))
+  {
+    GEN p = VALUE(here);
+    if (lgefint(p)>3 || (ulong)p[2] > 0x1000000UL) (void)addprimes(p);
+  }
+  return here;
+}
+
+/* Encapsulated routines */
+
+/* prime/exponent pairs need to appear contiguously on the stack, but we also
+ * need our data structure somewhere, and we don't know in advance how many
+ * primes will turn up.  The following discipline achieves this:  When
+ * ifac_decomp() is called, n should point at an object older than the oldest
+ * small prime/exponent pair  (ifactor() guarantees this).
+ * We allocate sufficient space to accommodate several pairs -- eleven pairs
+ * ought to fit in a space not much larger than n itself -- before calling
+ * ifac_start().  If we manage to complete the factorization before we run out
+ * of space, we free the data structure and cull the excess reserved space
+ * before returning.  When we do run out, we have to leapfrog to generate more
+ * (guesstimating the requirements from what is left in the partial
+ * factorization structure);  room for fresh pairs is allocated at the head of
+ * the stack, followed by an ifac_realloc() to reconnect the data structure and
+ * move it out of the way, followed by a few pointer tweaks to connect the new
+ * pairs space to the old one. This whole affair translates into a surprisingly
+ * compact routine. */
+
+/* find primary factors of n */
+static long
+ifac_decomp(GEN n, long hint)
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  long nb = 0;
+  GEN part, here, workspc, pairs = (GEN)av;
+
+  /* workspc will be doled out in pairs of smaller t_INTs. For n = prod p^{e_p}
+   * (p not necessarily prime), need room to store all p and e_p [ cgeti(3) ],
+   * bounded by
+   *    sum_{p | n} ( log_{2^BIL} (p) + 6 ) <= log_{2^BIL} n + 6 log_2 n */
+  workspc = new_chunk((expi(n) + 1) * 7);
+  part = ifac_start_hint(n, 0, hint);
+  for (;;)
+  {
+    here = ifac_main(&part);
+    if (!here) break;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      long offset;
+      if(DEBUGMEM>1)
+      {
+        pari_warn(warnmem,"[2] ifac_decomp");
+        ifac_print(part, here);
+      }
+      ifac_realloc(&part, &here, 0);
+      offset = here - part;
+      part = gerepileupto((pari_sp)workspc, part);
+      here = part + offset;
+    }
+    nb++;
+    pairs = icopy_avma(VALUE(here), (pari_sp)pairs);
+    pairs = icopy_avma(EXPON(here), (pari_sp)pairs);
+    ifac_delete(here);
+  }
+  avma = (pari_sp)pairs;
+  if (DEBUGLEVEL >= 3)
+    err_printf("IFAC: found %ld large prime (power) factor%s.\n",
+               nb, (nb>1? "s": ""));
+  return nb;
+}
+
+/***********************************************************************/
+/**            ARITHMETIC FUNCTIONS WITH EARLY-ABORT                  **/
+/**  needing direct access to the factoring machinery to avoid work:  **/
+/**  e.g. if we find a square factor, moebius returns 0, core doesn't **/
+/**  need to factor it, etc.                                          **/
+/***********************************************************************/
+/* memory management */
+static void
+ifac_GC(pari_sp av, GEN *part)
+{
+  GEN here = NULL;
+  if(DEBUGMEM>1) pari_warn(warnmem,"ifac_xxx");
+  ifac_realloc(part, &here, 0);
+  *part = gerepileupto(av, *part);
+}
+
+static long
+ifac_moebius(GEN n)
+{
+  long mu = 1;
+  pari_sp av = avma, lim = stack_lim(av,1);
+  GEN part = ifac_start(n, 1);
+  for(;;)
+  {
+    long v;
+    GEN p;
+    if (!ifac_next(&part,&p,&v)) return v? 0: mu;
+    mu = -mu;
+    if (low_stack(lim, stack_lim(av,1))) ifac_GC(av,&part);
+  }
+}
+
+int
+ifac_read(GEN part, GEN *p, long *e)
+{
+  GEN here = ifac_find(part);
+  if (!here) return 0;
+  *p = VALUE(here);
+  *e = EXPON(here)[2];
+  return 1;
+}
+void
+ifac_skip(GEN part)
+{
+  GEN here = ifac_find(part);
+  if (here) ifac_delete(here);
+}
+
+static int
+ifac_ispowerful(GEN n)
+{
+  pari_sp av = avma, lim = stack_lim(av,1);
+  GEN part = ifac_start(n, 0);
+  for(;;)
+  {
+    long e;
+    GEN p;
+    if (!ifac_read(part,&p,&e)) return 1;
+    /* power: skip */
+    if (e != 1 || Z_isanypower(p,NULL)) { ifac_skip(part); continue; }
+    if (!ifac_next(&part,&p,&e)) return 1;
+    if (e == 1) return 0;
+    if (low_stack(lim, stack_lim(av,1))) ifac_GC(av,&part);
+  }
+}
+static GEN
+ifac_core(GEN n)
+{
+  GEN m = gen_1, c = cgeti(lgefint(n));
+  pari_sp av = avma, lim = stack_lim(av,1);
+  GEN part = ifac_start(n, 0);
+  for(;;)
+  {
+    long e;
+    GEN p;
+    if (!ifac_read(part,&p,&e)) return m;
+    /* square: skip */
+    if (!odd(e) || Z_issquare(p)) { ifac_skip(part); continue; }
+    if (!ifac_next(&part,&p,&e)) return m;
+    if (odd(e)) m = mulii(m, p);
+    if (low_stack(lim, stack_lim(av,1))) { affii(m,c); m=c; ifac_GC(av,&part); }
+  }
+}
+
+/* Where to stop trial dividing in factorization. Guaranteed >= 2^14 */
+ulong
+tridiv_bound(GEN n)
+{
+  ulong l = (ulong)expi(n) + 1;
+  if (l <= 32)  return 1UL<<14;
+  if (l <= 512) return (l-16) << 10;
+  return 1UL<<19; /* Rho is generally faster above this */
+}
+
+/* return a value <= (48 << 10) = 49152 < primelinit */
+static ulong
+utridiv_bound(ulong n)
+{
+#ifdef LONG_IS_64BIT
+  if (n & HIGHMASK)
+    return ((ulong)expu(n) + 1 - 16) << 10;
+#else
+  (void)n;
+#endif
+  return 1UL<<14;
+}
+
+static void
+ifac_factoru(GEN n, GEN P, GEN E, long *pi)
+{
+  GEN part = ifac_start(n, 0);
+  for(;;)
+  {
+    long v;
+    GEN p;
+    if (!ifac_next(&part,&p,&v)) return;
+    P[*pi] = itou(p);
+    E[*pi] = v;
+    (*pi)++;
+  }
+}
+static long
+ifac_moebiusu(GEN n)
+{
+  GEN part = ifac_start(n, 1);
+  long s = 1;
+  for(;;)
+  {
+    long v;
+    GEN p;
+    if (!ifac_next(&part,&p,&v)) return v? 0: s;
+    s = -s;
+  }
+}
+
+INLINE ulong
+u_forprime_next_fast(forprime_t *T)
+{
+  if (*(T->d))
+  {
+    NEXT_PRIME_VIADIFF(T->p, T->d);
+    return T->p > T->b ? 0: T->p;
+  }
+  return u_forprime_next(T);
+}
+
+/* Factor n and output [p,e] where
+ * p, e are vecsmall with n = prod{p[i]^e[i]} */
+GEN
+factoru(ulong n)
+{
+  GEN f, E, E2, P, P2;
+  pari_sp av;
+  ulong p;
+  long v, i, oldi;
+  forprime_t S;
+
+  if (n == 0) retmkvec2(mkvecsmall(0), mkvecsmall(1));
+  if (n == 1) retmkvec2(cgetg(1,t_VECSMALL), cgetg(1,t_VECSMALL));
+
+  v = vals(n);
+  if (v)
+  {
+    n >>= v;
+    if (n == 1) retmkvec2(mkvecsmall(2), mkvecsmall(v));
+  }
+  f = cgetg(3,t_VEC); av = avma;
+  /* enough room to store <= 15 primes and exponents (OK if n < 2^64) */
+  (void)new_chunk((15 + 1)*2);
+  u_forprime_init(&S, 3, utridiv_bound(n));
+  P = cgetg(16, t_VECSMALL);
+  E = cgetg(16, t_VECSMALL);
+  if (v) { P[1] = 2; E[1] = v; i = 2; } else i = 1;
+  oldi = i;
+  while ( (p = u_forprime_next_fast(&S)) )
+  {
+    int stop;
+    /* tiny integers without small factors are often primes */
+    if (p == 673)
+    {
+      oldi = i;
+      if (uisprime_661(n)) { P[i] = n; E[i] = 1; i++; goto END; }
+    }
+    v = u_lvalrem_stop(&n, p, &stop);
+    if (v) {
+      P[i] = p;
+      E[i] = v; i++;
+    }
+    if (stop) {
+      if (n != 1) { P[i] = n; E[i] = 1; i++; }
+      goto END;
+    }
+  }
+  if (oldi != i && uisprime_661(n)) { P[i] = n; E[i] = 1; i++; }
+  else
+  {
+    GEN perm;
+    ifac_factoru(utoipos(n), P, E, &i);
+    setlg(P, i);
+    perm = vecsmall_indexsort(P);
+    P = vecpermute(P, perm);
+    E = vecpermute(E, perm);
+  }
+END:
+  avma = av;
+  P2 = cgetg(i, t_VECSMALL); gel(f,1) = P2;
+  E2 = cgetg(i, t_VECSMALL); gel(f,2) = E2;
+  while (--i >= 1) { P2[i] = P[i]; E2[i] = E[i]; }
+  return f;
+}
+
+long
+moebiusu(ulong n)
+{
+  pari_sp av;
+  ulong p;
+  long s, v, test_prime;
+  forprime_t S;
+
+  switch(n)
+  {
+    case 0: (void)check_arith_non0(gen_0,"moebius");/*error*/
+    case 1: return  1;
+    case 2: return -1;
+  }
+  v = vals(n);
+  if (v == 0)
+    s = 1;
+  else
+  {
+    if (v > 1) return 0;
+    n >>= 1;
+    s = -1;
+  }
+  av = avma;
+  u_forprime_init(&S, 3, utridiv_bound(n));
+  test_prime = 0;
+  while ((p = u_forprime_next_fast(&S)))
+  {
+    int stop;
+    /* tiny integers without small factors are often primes */
+    if (p == 673)
+    {
+      test_prime = 0;
+      if (uisprime_661(n)) { avma = av; return -s; }
+    }
+    v = u_lvalrem_stop(&n, p, &stop);
+    if (v) {
+      if (v > 1) { avma = av; return 0; }
+      test_prime = 1;
+      s = -s;
+    }
+    if (stop) { avma = av; return n == 1? s: -s; }
+  }
+  avma = av;
+  if (test_prime && uisprime_661(n)) return -s;
+  else
+  {
+    long t = ifac_moebiusu(utoipos(n));
+    avma = av;
+    if (t == 0) return 0;
+    return (s == t)? 1: -1;
+  }
+}
+
+long
+moebius(GEN n)
+{
+  pari_sp av = avma;
+  GEN F;
+  ulong p;
+  long i, l, s, v;
+  forprime_t S;
+
+  if ((F = check_arith_non0(n,"moebius")))
+  {
+    GEN E;
+    F = clean_Z_factor(F);
+    E = gel(F,2);
+    l = lg(E);
+    for(i = 1; i < l; i++)
+      if (!equali1(gel(E,1))) { avma = av; return 0; }
+    avma = av; return odd(l)? 1: -1;
+  }
+  if (lgefint(n) == 3) return moebiusu(n[2]);
+  p = mod4(n); if (!p) return 0;
+  if (p == 2) { s = -1; n = shifti(n, -1); } else { s = 1; n = icopy(n); }
+  setabssign(n);
+
+  u_forprime_init(&S, 3, tridiv_bound(n));
+  while ((p = u_forprime_next_fast(&S)))
+  {
+    int stop;
+    v = Z_lvalrem_stop(&n, p, &stop);
+    if (v)
+    {
+      if (v > 1) { avma = av; return 0; }
+      s = -s;
+      if (stop) { avma = av; return is_pm1(n)? s: -s; }
+    }
+  }
+  l = lg(primetab);
+  for (i = 1; i < l; i++)
+  {
+    v = Z_pvalrem(n, gel(primetab,i), &n);
+    if (v)
+    {
+      if (v > 1) { avma = av; return 0; }
+      s = -s;
+      if (is_pm1(n)) { avma = av; return s; }
+    }
+  }
+  if (ifac_isprime(n)) { avma = av; return -s; }
+  /* large composite without small factors */
+  v = ifac_moebius(n);
+  avma = av; return (s<0 ? -v : v); /* correct also if v==0 */
+}
+
+long
+ispowerful(GEN n)
+{
+  pari_sp av = avma;
+  GEN F;
+  ulong p, bound;
+  long i, l, v;
+  forprime_t S;
+
+  if ((F = check_arith_all(n, "ispowerful")))
+  {
+    GEN p, P = gel(F,1), E = gel(F,2);
+    if (lg(P) == 1) return 1; /* 1 */
+    p = gel(P,1);
+    if (!signe(p)) return 1; /* 0 */
+    l = lg(E);
+    for (i = 1; i < l; i++)
+      if (equali1(gel(E,i))) return 0;
+    return 1;
+  }
+  if (!signe(n)) return 1;
+
+  if (mod4(n) == 2) return 0;
+  n = shifti(n, -vali(n));
+  if (is_pm1(n)) return 1;
+  setabssign(n);
+  bound = tridiv_bound(n);
+  u_forprime_init(&S, 3, bound);
+  while ((p = u_forprime_next_fast(&S)))
+  {
+    int stop;
+    v = Z_lvalrem_stop(&n, p, &stop);
+    if (v)
+    {
+      if (v == 1) { avma = av; return 0; }
+      if (stop) { avma = av; return is_pm1(n); }
+    }
+  }
+  l = lg(primetab);
+  for (i = 1; i < l; i++)
+  {
+    v = Z_pvalrem(n, gel(primetab,i), &n);
+    if (v)
+    {
+      if (v == 1) { avma = av; return 0; }
+      if (is_pm1(n)) { avma = av; return 1; }
+    }
+  }
+  /* no need to factor: must be p^2 or not powerful */
+  if(cmpii(powuu(bound+1, 3), n) > 0) {
+    long res = Z_issquare(n);
+    avma = av; return res;
+  }
+
+  if (ifac_isprime(n)) { avma=av; return 0; }
+  /* large composite without small factors */
+  v = ifac_ispowerful(n);
+  avma = av; return v;
+}
+
+ulong
+coreu(ulong n)
+{
+  if (n == 0) return 0;
+  else
+  {
+    pari_sp av = avma;
+    GEN f = factoru(n), P = gel(f,1), E = gel(f,2);
+    long i, l = lg(P), m = 1;
+
+    avma = av;
+    for (i = 1; i < l; i++)
+    {
+      ulong p = P[i], e = E[i];
+      if (e & 1) m *= p;
+    }
+    return m;
+  }
+}
+GEN
+core(GEN n)
+{
+  pari_sp av = avma;
+  GEN m, F;
+  ulong p;
+  long i, l, v;
+  forprime_t S;
+
+  if ((F = check_arith_all(n, "core")))
+  {
+    GEN p, x, P = gel(F,1), E = gel(F,2);
+    long j = 1;
+    if (lg(P) == 1) return gen_1;
+    p = gel(P,1);
+    if (!signe(p)) return gen_0;
+    l = lg(P); x = cgetg(l, t_VEC);
+    for (i = 1; i < l; i++)
+      if (mpodd(gel(E,i))) gel(x,j++) = gel(P,i);
+    setlg(x, j); return ZV_prod(x);
+  }
+  switch(lgefint(n))
+  {
+    case 2: return gen_0;
+    case 3:
+      p = coreu(n[2]);
+      return signe(n) > 0? utoipos(p): utoineg(p);
+  }
+
+  m = signe(n) < 0? gen_m1: gen_1;
+  n = absi(n);
+  u_forprime_init(&S, 2, tridiv_bound(n));
+  while ((p = u_forprime_next_fast(&S)))
+  {
+    int stop;
+    v = Z_lvalrem_stop(&n, p, &stop);
+    if (v)
+    {
+      if (v & 1) m = muliu(m, p);
+      if (stop)
+      {
+        if (!is_pm1(n)) m = mulii(m, n);
+        return gerepileuptoint(av, m);
+      }
+    }
+  }
+  l = lg(primetab);
+  for (i = 1; i < l; i++)
+  {
+    GEN q = gel(primetab,i);
+    v = Z_pvalrem(n, q, &n);
+    if (v)
+    {
+      if (v & 1) m = mulii(m, q);
+      if (is_pm1(n)) return gerepileuptoint(av, m);
+    }
+  }
+  if (ifac_isprime(n)) { m = mulii(m, n); return gerepileuptoint(av, m); }
+  /* large composite without small factors */
+  return gerepileuptoint(av, mulii(m, ifac_core(n)));
+}
+
+long
+Z_issmooth(GEN m, ulong lim)
+{
+  pari_sp av=avma;
+  ulong p = 2;
+  forprime_t S;
+  u_forprime_init(&S, 2, lim);
+  while ((p = u_forprime_next_fast(&S)))
+  {
+    int stop;
+    (void)Z_lvalrem_stop(&m, p, &stop);
+    if (stop) { avma = av; return cmpiu(m,lim)<=0; }
+  }
+  avma = av; return 0;
+}
+
+GEN
+Z_issmooth_fact(GEN m, ulong lim)
+{
+  pari_sp av=avma;
+  GEN F, P, E;
+  ulong p;
+  long i = 1, l = expi(m)+1;
+  forprime_t S;
+  P = cgetg(l, t_VECSMALL);
+  E = cgetg(l, t_VECSMALL);
+  F = mkmat2(P,E);
+  u_forprime_init(&S, 2, lim);
+  while ((p = u_forprime_next_fast(&S)))
+  {
+    long v;
+    int stop;
+    if ((v = Z_lvalrem_stop(&m, p, &stop)))
+    {
+      P[i] = p;
+      E[i] = v; i++;
+      if (stop)
+      {
+        if (cmpiu(m,lim) > 0) break;
+        P[i] = m[2];
+        E[i] = 1; i++;
+        setlg(P, i);
+        setlg(E, i); avma = (pari_sp)F; return F;
+      }
+    }
+  }
+  avma = av; return NULL;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**       COMPUTING THE MATRIX OF PRIME DIVISORS AND EXPONENTS        **/
+/**                                                                   **/
+/***********************************************************************/
+static GEN
+aux_end(GEN M, GEN n, long nb)
+{
+  GEN P,E, z = (GEN)avma;
+  long i;
+
+  if (n) gunclone(n);
+  P = cgetg(nb+1,t_COL);
+  E = cgetg(nb+1,t_COL);
+  for (i=nb; i; i--)
+  { /* allow a stackdummy in the middle */
+    while (typ(z) != t_INT) z += lg(z);
+    gel(E,i) = z; z += lg(z);
+    gel(P,i) = z; z += lg(z);
+  }
+  gel(M,1) = P;
+  gel(M,2) = E;
+  return sort_factor(M, (void*)&absi_cmp, cmp_nodata);
+}
+
+static void
+STORE(long *nb, GEN x, long e) { (*nb)++; (void)x; (void)utoipos(e); }
+static void
+STOREu(long *nb, ulong x, long e) { STORE(nb, utoipos(x), e); }
+static void
+STOREi(long *nb, GEN x, long e) { STORE(nb, icopy(x), e); }
+/* no prime less than p divides n */
+static int
+special_primes(GEN n, ulong p, long *nb, GEN T)
+{
+  long i, l = lg(T);
+  if (l > 1)
+  { /* pp = square of biggest p tried so far */
+    long pp[] = { evaltyp(t_INT)|_evallg(4), 0,0,0 };
+    pari_sp av = avma; affii(sqru(p), pp); avma = av;
+
+    for (i = 1; i < l; i++)
+      if (dvdiiz(n,gel(T,i), n))
+      {
+        long k = 1; while (dvdiiz(n,gel(T,i), n)) k++;
+        STOREi(nb, gel(T,i), k);
+        if (absi_cmp(pp, n) > 0) return 1;
+      }
+  }
+  return 0;
+}
+
+/* factor(sn*|n|), where sn = -1,1 or 0.
+ * all != 0 : only look for prime divisors < all */
+static GEN
+ifactor_sign(GEN n, ulong all, long hint, long sn)
+{
+  GEN M, N;
+  pari_sp av;
+  long nb = 0, i;
+  ulong lim;
+  forprime_t T;
+
+  if (!sn) retmkmat2(mkcol(gen_0), mkcol(gen_1));
+  if (lgefint(n) == 3)
+  { /* small integer */
+    GEN f, Pf, Ef, P, E, F = cgetg(3, t_MAT);
+    long l;
+    av = avma;
+    /* enough room to store <= 15 primes and exponents (OK if n < 2^64) */
+    (void)new_chunk((15*3 + 15 + 1) * 2);
+    f = factoru(n[2]);
+    avma = av;
+    Pf = gel(f,1);
+    Ef = gel(f,2);
+    l = lg(Pf);
+    if (sn < 0)
+    { /* add sign */
+      long L = l+1;
+      gel(F,1) = P = cgetg(L, t_COL);
+      gel(F,2) = E = cgetg(L, t_COL);
+      gel(P,1) = gen_m1; P++;
+      gel(E,1) = gen_1;  E++;
+    }
+    else
+    {
+      gel(F,1) = P = cgetg(l, t_COL);
+      gel(F,2) = E = cgetg(l, t_COL);
+    }
+    for (i = 1; i < l; i++)
+    {
+      gel(P,i) = utoipos(Pf[i]);
+      gel(E,i) = utoipos(Ef[i]);
+    }
+    return F;
+  }
+  M = cgetg(3,t_MAT);
+  if (sn < 0) STORE(&nb, utoineg(1), 1);
+  if (is_pm1(n)) return aux_end(M,NULL,nb);
+
+  n = N = gclone(n); setabssign(n);
+  /* trial division bound */
+  lim = all; if (!lim) lim = tridiv_bound(n);
+  if (lim > 2)
+  {
+    ulong maxp, p;
+    pari_sp av2;
+    i = vali(n);
+    if (i)
+    {
+      STOREu(&nb, 2, i);
+      av = avma; affii(shifti(n,-i), n); avma = av;
+    }
+    if (is_pm1(n)) return aux_end(M,n,nb);
+    /* trial division */
+    maxp = maxprime();
+    av = avma; u_forprime_init(&T, 3, minss(lim, maxp)); av2 = avma;
+    /* first pass: known to fit in private prime table */
+    while ((p = u_forprime_next_fast(&T)))
+    {
+      pari_sp av3 = avma;
+      int stop;
+      long k = Z_lvalrem_stop(&n, p, &stop);
+      if (k)
+      {
+        affii(n, N); n = N; avma = av3;
+        STOREu(&nb, p, k);
+      }
+      if (stop)
+      {
+        if (!is_pm1(n)) STOREi(&nb, n, 1);
+        stackdummy(av, av2);
+        return aux_end(M,n,nb);
+      }
+    }
+    stackdummy(av, av2);
+    if (lim > maxp)
+    { /* second pass, usually empty: outside private prime table */
+      av = avma; u_forprime_init(&T, maxp+1, lim); av2 = avma;
+      while ((p = u_forprime_next(&T)))
+      {
+        pari_sp av3 = avma;
+        int stop;
+        long k = Z_lvalrem_stop(&n, p, &stop);
+        if (k)
+        {
+          affii(n, N); n = N; avma = av3;
+          STOREu(&nb, p, k);
+        }
+        if (stop)
+        {
+          if (!is_pm1(n)) STOREi(&nb, n, 1);
+          stackdummy(av, av2);
+          return aux_end(M,n,nb);
+        }
+      }
+      stackdummy(av, av2);
+    }
+  }
+  /* trial divide by the special primes */
+  if (special_primes(n, lim, &nb, primetab))
+  {
+    if (!is_pm1(n)) STOREi(&nb, n, 1);
+    return aux_end(M,n,nb);
+  }
+
+  if (all)
+  { /* smallfact: look for easy pure powers then stop. Cf Z_isanypower */
+    GEN x;
+    long k;
+    av = avma;
+    k = isanypower_nosmalldiv(n, &x);
+    if (k > 1) affii(x, n);
+    avma = av; STOREi(&nb, n, k);
+    if (DEBUGLEVEL >= 2) {
+      pari_warn(warner,
+        "IFAC: untested %ld-bit integer declared prime", expi(n));
+      if (expi(n) <= 256)
+        err_printf("\t%Ps\n", n);
+    }
+    return aux_end(M,n,nb);
+  }
+  if (ifac_isprime(n)) { STOREi(&nb, n, 1); return aux_end(M,n,nb); }
+  nb += ifac_decomp(n, hint);
+  return aux_end(M,n, nb);
+}
+
+static GEN
+ifactor(GEN n, ulong all, long hint)
+{ return ifactor_sign(n, all, hint, signe(n)); }
+
+int
+ifac_next(GEN *part, GEN *p, long *e)
+{
+  GEN here = ifac_main(part);
+  if (here == gen_0) { *p = NULL; *e = 1; return 0; }
+  if (!here) { *p = NULL; *e = 0; return 0; }
+  *p = VALUE(here);
+  *e = EXPON(here)[2];
+  ifac_delete(here); return 1;
+}
+
+/* see before ifac_crack for current semantics of 'hint' (factorint's 'flag') */
+GEN
+factorint(GEN n, long flag)
+{
+  GEN F;
+  if ((F = check_arith_all(n,"factorint"))) return gcopy(F);
+  return ifactor(n,0,flag);
+}
+
+GEN
+Z_factor_limit(GEN n, ulong all)
+{
+  if (!all) all = GP_DATA->primelimit + 1;
+  return ifactor(n,all,decomp_default_hint);
+}
+GEN
+absi_factor_limit(GEN n, ulong all)
+{
+  if (!all) all = GP_DATA->primelimit + 1;
+  return ifactor_sign(n,all,decomp_default_hint, signe(n)?1 : 0);
+}
+GEN
+Z_factor(GEN n)
+{ return ifactor(n,0,decomp_default_hint); }
+GEN
+absi_factor(GEN n)
+{ return ifactor_sign(n, 0, decomp_default_hint, signe(n)? 1: 0); }
+
+/* Factor until the unfactored part is smaller than limit. Return the
+ * factored part. Hence factorback(output) may be smaller than n */
+GEN
+Z_factor_until(GEN n, GEN limit)
+{
+  pari_sp av2, av = avma;
+  ulong B = tridiv_bound(n);
+  GEN q, part, F = ifactor(n, B, decomp_default_hint);
+  GEN P = gel(F,1), E = gel(F,2), P2, E2, F2;
+  long l = lg(P), l2;
+
+  av2 = avma;
+  q = gel(P,l-1);
+  if (cmpiu(q, B) <= 0 || cmpii(q, sqru(B)) < 0 || ifac_isprime(q))
+  {
+    avma = av2; return F;
+  }
+  /* q = composite unfactored part, remove from P/E */
+  setlg(E,l-1);
+  setlg(P,l-1);
+  if (cmpii(q, limit) > 0)
+  { /* factor further */
+    l2 = expi(q)+1;
+    P2 = vectrunc_init(l2);
+    E2 = vectrunc_init(l2);
+    F2 = mkmat2(P2,E2);
+    part = ifac_start(q, 0);
+    for(;;)
+    {
+      long e;
+      GEN p;
+      if (!ifac_next(&part,&p,&e)) break;
+      vectrunc_append(P2, p);
+      vectrunc_append(E2, utoipos(e));
+      q = diviiexact(q, powiu(p, e));
+      if (cmpii(q, limit) <= 0) break;
+    }
+    F2 = sort_factor(F2, (void*)&absi_cmp, cmp_nodata);
+    F = merge_factor(F, F2, (void*)&absi_cmp, cmp_nodata);
+  }
+  return gerepilecopy(av, F);
+}
diff --git a/src/basemath/lll.c b/src/basemath/lll.c
new file mode 100644
index 0000000..4c5f9f7
--- /dev/null
+++ b/src/basemath/lll.c
@@ -0,0 +1,826 @@
+/* Copyright (C) 2008  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/* default quality ratio for LLL */
+static const double LLLDFT = 0.99;
+
+/* assume flag & (LLL_KER|LLL_IM|LLL_ALL). LLL_INPLACE implies LLL_IM */
+static GEN
+lll_trivial(GEN x, long flag)
+{
+  GEN y;
+  if (lg(x) == 1)
+  { /* dim x = 0 */
+    if (! (flag & LLL_ALL)) return cgetg(1,t_MAT);
+    y=cgetg(3,t_VEC);
+    gel(y,1) = cgetg(1,t_MAT);
+    gel(y,2) = cgetg(1,t_MAT); return y;
+  }
+  /* dim x = 1 */
+  if (gequal0(gel(x,1)))
+  {
+    if (flag & LLL_KER) return matid(1);
+    if (flag & (LLL_IM|LLL_INPLACE)) return cgetg(1,t_MAT);
+    y = cgetg(3,t_VEC);
+    gel(y,1) = matid(1);
+    gel(y,2) = cgetg(1,t_MAT); return y;
+  }
+  if (flag & LLL_INPLACE) return gcopy(x);
+  if (flag & LLL_KER) return cgetg(1,t_MAT);
+  if (flag & LLL_IM)  return matid(1);
+  y=cgetg(3,t_VEC);
+  gel(y,1) = cgetg(1,t_MAT);
+  gel(y,2) = (flag & LLL_GRAM)? gcopy(x): matid(1);
+  return y;
+}
+
+/* vecslice(h,#h-k,#h) in place. Works for t_MAT, t_VEC/t_COL */
+static GEN
+lll_get_im(GEN h, long k)
+{
+  ulong mask = h[0] & ~LGBITS;
+  long l = lg(h) - k;
+  h += k; h[0] = mask | evallg(l);
+  return h;
+}
+
+/* k = dim Kernel */
+static GEN
+lll_finish(GEN h, long k, long flag)
+{
+  GEN g;
+  if (flag & LLL_KER) { setlg(h,k+1); return h; }
+  if (flag & LLL_IM) return lll_get_im(h, k);
+  g = vecslice(h,1,k);
+  return mkvec2(g, lll_get_im(h, k));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                   FPLLL (adapted from D. Stehle's code)        **/
+/**                                                                **/
+/********************************************************************/
+/* Babai() and fplll() are a conversion to libpari API and data types
+   of the file proved.c in fplll-1.3 by Damien Stehle'.
+
+  Copyright 2005, 2006 Damien Stehle'.
+
+  This program 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 2 of the License, or (at your
+  option) any later version.
+
+  This program implements ideas from the paper "Floating-point LLL Revisited",
+  by Phong Nguyen and Damien Stehle', in the Proceedings of Eurocrypt'2005,
+  Springer-Verlag; and was partly inspired by Shoup's NTL library:
+  http://www.shoup.net/ntl/
+*/
+
+/***********************************************/
+/* Babai's Nearest Plane algorithm (iterative) */
+/***********************************************/
+/* Size-reduces b_kappa using mu_{i,j} and r_{i,j} for j<=i <kappa
+Updates B (kappa); computes mu_{kappa,j}, r_{kappa,j} for j<=kappa, and s(kappa)
+mu, r, s updated in place (affrr).
+*/
+static long
+Babai(pari_sp av, long kappa, GEN *pG, GEN *pB, GEN *pU, GEN mu, GEN r, GEN s,
+      long a, long zeros, long maxG, long n, GEN eta, GEN halfplus1, long prec)
+{
+  const pari_sp lim = stack_lim(av,2);
+  GEN B = *pB, G = *pG, U = *pU, tmp, rtmp, ztmp;
+  long k, aa = (a > zeros)? a : zeros+1;
+  GEN maxmu = gen_0, max2mu = gen_0;
+  /* N.B: we set d = 0 (resp. n = 0) to avoid updating U (resp. B) */
+  const long d = U ? lg(U)-1: 0;
+
+  for (;;) {
+    int go_on = 0;
+    GEN max3mu;
+    long i, j;
+
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"Babai[1], a=%ld", aa);
+      gerepileall(av,U?5:4,&B,&G,&maxmu,&max2mu,&U);
+    }
+    /* Step2: compute the GSO for stage kappa */
+    max3mu = max2mu;
+    max2mu = maxmu;
+    maxmu = real_0(prec);
+    for (j=aa; j<kappa; j++)
+    {
+      pari_sp btop = avma;
+      k = zeros+1;
+      if (j > k)
+      {
+        tmp  = mulrr(gmael(mu,j,k), gmael(r,kappa,k));
+        rtmp = subir(gmael(G,kappa,j), tmp);
+        for (k++; k<j; k++)
+        {
+          tmp  = mulrr(gmael(mu,j,k), gmael(r,kappa,k));
+          rtmp = subrr(rtmp,tmp);
+        }
+        affrr(rtmp, gmael(r,kappa,j));
+      }
+      else
+        affir(gmael(G,kappa,j), gmael(r,kappa,j));
+      affrr(divrr(gmael(r,kappa,j), gmael(r,j,j)), gmael(mu,kappa,j));
+      if (absr_cmp(maxmu, gmael(mu,kappa,j))<0)
+        maxmu = gmael(mu,kappa,j);
+      avma = btop;
+    }
+    maxmu = absr(maxmu);
+    if (typ(max3mu)==t_REAL && absr_cmp(max3mu, shiftr(max2mu, 5))<=0)
+    {
+      *pB = B; *pG = G; *pU = U;
+      if (DEBUGLEVEL>5) err_printf("prec too low\n");
+      return kappa;
+    }
+
+    /* Step3--5: compute the X_j's  */
+    for (j=kappa-1; j>zeros; j--)
+    {
+      tmp = gmael(mu,kappa,j);
+      if (absr_cmp(tmp, eta) <= 0) continue; /* (essentially) size-reduced */
+
+      if (low_stack(lim, stack_lim(av,2)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"Babai[2], a=%ld, j=%ld", aa,j);
+        gerepileall(av,U?5:4,&B,&G,&maxmu,&max2mu,&U);
+      }
+      go_on = 1;
+      /* we consider separately the case |X| = 1 */
+      if (absr_cmp(tmp, halfplus1) <= 0)
+      {
+        if (signe(tmp) > 0) { /* in this case, X = 1 */
+          pari_sp btop = avma;
+          for (k=zeros+1; k<j; k++)
+            affrr(subrr(gmael(mu,kappa,k), gmael(mu,j,k)), gmael(mu,kappa,k));
+          avma = btop;
+
+          for (i=1; i<=n; i++)
+            gmael(B,kappa,i) = subii(gmael(B,kappa,i), gmael(B,j,i));
+          for (i=1; i<=d; i++)
+            gmael(U,kappa,i) = subii(gmael(U,kappa,i), gmael(U,j,i));
+          btop = avma;
+          ztmp = subii(gmael(G,j,j), shifti(gmael(G,kappa,j), 1));
+          ztmp = addii(gmael(G,kappa,kappa), ztmp);
+          gmael(G,kappa,kappa) = gerepileuptoint(btop, ztmp);
+          for (i=1; i<=j; i++)
+            gmael(G,kappa,i) = subii(gmael(G,kappa,i), gmael(G,j,i));
+          for (i=j+1; i<kappa; i++)
+            gmael(G,kappa,i) = subii(gmael(G,kappa,i), gmael(G,i,j));
+          for (i=kappa+1; i<=maxG; i++)
+            gmael(G,i,kappa) = subii(gmael(G,i,kappa), gmael(G,i,j));
+        } else { /* otherwise X = -1 */
+          pari_sp btop = avma;
+          for (k=zeros+1; k<j; k++)
+            affrr(addrr(gmael(mu,kappa,k), gmael(mu,j,k)), gmael(mu,kappa,k));
+          avma = btop;
+
+          for (i=1; i<=n; i++)
+            gmael(B,kappa,i) = addii(gmael(B,kappa,i), gmael(B,j,i));
+          for (i=1; i<=d; i++)
+            gmael(U,kappa,i) = addii(gmael(U,kappa,i),gmael(U,j,i));
+          btop = avma;
+          ztmp = addii(gmael(G,j,j), shifti(gmael(G,kappa,j), 1));
+          ztmp = addii(gmael(G,kappa,kappa), ztmp);
+          gmael(G,kappa,kappa) = gerepileuptoint(btop, ztmp);
+          for (i=1; i<=j; i++)
+            gmael(G,kappa,i) = addii(gmael(G,kappa,i), gmael(G,j,i));
+          for (i=j+1; i<kappa; i++)
+            gmael(G,kappa,i) = addii(gmael(G,kappa,i), gmael(G,i,j));
+          for (i=kappa+1; i<=maxG; i++)
+            gmael(G,i,kappa) = addii(gmael(G,i,kappa), gmael(G,i,j));
+        }
+        continue;
+      }
+      /* we have |X| >= 2 */
+      ztmp = roundr_safe(tmp);
+      if (lgefint(ztmp) == 3)
+      {
+        pari_sp btop = avma;
+        ulong xx = ztmp[2]; /* X fits in an ulong */
+        if (signe(ztmp) > 0) /* = xx */
+        {
+          for (k=zeros+1; k<j; k++)
+          {
+            rtmp = subrr(gmael(mu,kappa,k), mulur(xx, gmael(mu,j,k)));
+            affrr(rtmp, gmael(mu,kappa,k));
+          }
+          avma = btop;
+          for (i=1; i<=n; i++)
+            gmael(B,kappa,i) = submuliu_inplace(gmael(B,kappa,i), gmael(B,j,i), xx);
+          for (i=1; i<=d; i++)
+            gmael(U,kappa,i) = submuliu_inplace(gmael(U,kappa,i), gmael(U,j,i), xx);
+          btop = avma;
+          ztmp = shifti(muliu(gmael(G,kappa,j), xx), 1);
+          ztmp = subii(mulii(gmael(G,j,j), sqru(xx)), ztmp);
+          ztmp = addii(gmael(G,kappa,kappa), ztmp);
+          gmael(G,kappa,kappa) = gerepileuptoint(btop, ztmp);
+          for (i=1; i<=j; i++)
+            gmael(G,kappa,i) = submuliu_inplace(gmael(G,kappa,i), gmael(G,j,i), xx);
+          for (i=j+1; i<kappa; i++)
+            gmael(G,kappa,i) = submuliu_inplace(gmael(G,kappa,i), gmael(G,i,j), xx);
+          for (i=kappa+1; i<=maxG; i++)
+            gmael(G,i,kappa) = submuliu_inplace(gmael(G,i,kappa), gmael(G,i,j), xx);
+        }
+        else /* = -xx */
+        {
+          for (k=zeros+1; k<j; k++)
+          {
+            rtmp = addrr(gmael(mu,kappa,k), mulur(xx, gmael(mu,j,k)));
+            affrr(rtmp, gmael(mu,kappa,k));
+          }
+          avma = btop;
+          for (i=1; i<=n; i++)
+            gmael(B,kappa,i) = addmuliu_inplace(gmael(B,kappa,i), gmael(B,j,i), xx);
+          for (i=1; i<=d; i++)
+            gmael(U,kappa,i) = addmuliu_inplace(gmael(U,kappa,i), gmael(U,j,i), xx);
+          btop = avma;
+          ztmp = shifti(muliu(gmael(G,kappa,j), xx), 1);
+          ztmp = addii(mulii(gmael(G,j,j), sqru(xx)), ztmp);
+          ztmp = addii(gmael(G,kappa,kappa), ztmp);
+          gmael(G,kappa,kappa) = gerepileuptoint(btop, ztmp);
+          for (i=1; i<=j; i++)
+            gmael(G,kappa,i) = addmuliu_inplace(gmael(G,kappa,i), gmael(G,j,i), xx);
+          for (i=j+1; i<kappa; i++)
+            gmael(G,kappa,i) = addmuliu_inplace(gmael(G,kappa,i), gmael(G,i,j), xx);
+          for (i=kappa+1; i<=maxG; i++)
+            gmael(G,i,kappa) = addmuliu_inplace(gmael(G,i,kappa), gmael(G,i,j), xx);
+        }
+      }
+      else
+      {
+        GEN tmp2  = itor(ztmp,prec);
+        long e = expo(tmp2)-prec2nbits(prec);
+        GEN X = shifti(trunc2nr(tmp2, -e), e);
+        pari_sp btop = avma;
+
+        for (k=zeros+1; k<j; k++)
+        {
+          rtmp = subrr(gmael(mu,kappa,k), mulir(ztmp, gmael(mu,j,k)));
+          affrr(rtmp, gmael(mu,kappa,k));
+        }
+        avma = btop;
+        for (i=1; i<=n; i++)
+          gmael(B,kappa,i) = submulii(gmael(B,kappa,i), gmael(B,j,i), X);
+        for (i=1; i<=d; i++)
+          gmael(U,kappa,i) = submulii(gmael(U,kappa,i), gmael(U,j,i), X);
+        btop = avma;
+        ztmp = shifti(mulii(gmael(G,kappa,j), X), 1);
+        ztmp = subii(mulii(gmael(G,j,j), sqri(X)), ztmp);
+        ztmp = addii(gmael(G,kappa,kappa), ztmp);
+        gmael(G,kappa,kappa) = gerepileuptoint(btop, ztmp);
+        for (i=1; i<=j; i++)
+          gmael(G,kappa,i) = submulii(gmael(G,kappa,i), gmael(G,j,i), X);
+        for (   ; i<kappa; i++)
+          gmael(G,kappa,i) = submulii(gmael(G,kappa,i), gmael(G,i,j), X);
+        for (i=kappa+1; i<=maxG; i++)
+          gmael(G,i,kappa) = submulii(gmael(G,i,kappa), gmael(G,i,j), X);
+      }
+    }
+    if (!go_on) break; /* Anything happened? */
+    aa = zeros+1;
+  }
+
+  affir(gmael(G,kappa,kappa), gel(s,zeros+1));
+  /* the last s[kappa-1]=r[kappa][kappa] is computed only if kappa increases */
+  av = avma;
+  for (k=zeros+1; k<=kappa-2; k++)
+  {
+    tmp = subrr(gel(s,k), mulrr(gmael(mu,kappa,k), gmael(r,kappa,k)));
+    affrr(tmp, gel(s,k+1));
+  }
+  *pB = B; *pG = G; *pU = U; avma = av;
+  return 0;
+}
+
+static void
+rotate(GEN mu, long kappa2, long kappa, long d)
+{
+  long i, j;
+  pari_sp av = avma;
+  GEN mutmp = leafcopy(gel(mu,kappa2));
+  for (i=kappa2; i>kappa; i--)
+    for (j=1;j<=d;j++) gmael(mu,i,j) = gmael(mu,i-1,j);
+  for (j=1;j<=d;j++)   gmael(mu,kappa,j) = gel(mutmp,j);
+  avma = av;
+}
+
+/* ****************** */
+/* The LLL Algorithm  */
+/* ****************** */
+
+/* LLL-reduces the integer matrix(ces) (G,B,U)? "in place" */
+static GEN
+fplll(GEN *ptrB, GEN *ptrU, GEN *ptrr, double DELTA, double ETA, long flag, long prec)
+{
+  const long gram = flag & LLL_GRAM; /*Gram matrix*/
+  const long keepfirst = flag & LLL_KEEP_FIRST; /*never swap with first vector*/
+  pari_sp av, av2;
+  long kappa, kappa2, d, n, i, j, zeros, kappamax, maxG, bab;
+  GEN G, mu, r, s, tmp, SPtmp, alpha;
+  GEN delta = dbltor(DELTA), eta = dbltor(ETA), halfplus1 = dbltor(1.5);
+  const long triangular = 0;
+  pari_timer T;
+  GEN B = *ptrB, U;
+
+  d = lg(B)-1;
+  if (gram)
+  {
+    G = B;
+    n = d;
+    B = cgetg(1, t_VECSMALL); /* dummy */
+  }
+  else
+  {
+    G = zeromatcopy(d,d);
+    n = nbrows(B);
+  }
+  U = *ptrU; /* NULL if inplace */
+
+  if(DEBUGLEVEL>=4)
+  {
+    timer_start(&T);
+    err_printf("Entering L^2: LLL-parameters (%P.3f,%.3Pf), working precision %d words\n",delta,eta, prec);
+  }
+
+  mu = cgetg(d+1, t_MAT);
+  r  = cgetg(d+1, t_MAT);
+  s  = cgetg(d+1, t_VEC);
+  for (j = 1; j <= d; j++)
+  {
+    GEN M = cgetg(d+1, t_COL), R = cgetg(d+1, t_COL);
+    gel(mu,j)= M;
+    gel(r,j) = R;
+    gel(s,j) = cgetr(prec);
+    for (i = 1; i <= d; i++) {
+      gel(R,i) = cgetr(prec);
+      gel(M,i) = cgetr(prec);
+    }
+  }
+  SPtmp = zerovec(d+1);
+  alpha = cgetg(d+1, t_VECSMALL);
+  av = avma;
+
+  /* Step2: Initializing the main loop */
+  kappamax = 1;
+  i = 1;
+  maxG = d; /* later updated to kappamax if (!gram) */
+
+  do {
+    if (!gram) gmael(G,i,i) = ZV_dotsquare(gel(B,i));
+    affir(gmael(G,i,i), gmael(r,i,i));
+  } while (signe(gmael(G,i,i)) == 0 && (++i <=d));
+  zeros = i-1; /* all vectors B[i] with i <= zeros are zero vectors */
+  kappa = i;
+  if (zeros < d) affir(gmael(G,zeros+1,zeros+1), gmael(r,zeros+1,zeros+1));
+  for (i=zeros+1; i<=d; i++) alpha[i]=1;
+
+  while (++kappa <= d)
+  {
+    if (kappa>kappamax)
+    {
+      if (DEBUGLEVEL>=4) err_printf("K%ld ",kappa);
+      kappamax = kappa;
+      if (!gram) {
+        for (i=zeros+1; i<=kappa; i++)
+          gmael(G,kappa,i) = ZV_dotproduct(gel(B,kappa), gel(B,i));
+        maxG = kappamax;
+      }
+    }
+    /* Step3: Call to the Babai algorithm, mu,r,s updated in place */
+    bab = Babai(av, kappa, &G,&B,&U, mu,r,s, alpha[kappa], zeros, maxG,
+      gram? 0 : ((triangular && kappamax <= n) ? kappamax: n),
+      eta, halfplus1, prec);
+    if (bab) {*ptrB=(gram?G:B); *ptrU=U; return NULL; }
+
+    av2 = avma;
+    if ((keepfirst && kappa == 2) ||
+        cmprr(mulrr(gmael(r,kappa-1,kappa-1), delta), gel(s,kappa-1)) <= 0)
+    { /* Step4: Success of Lovasz's condition */
+      alpha[kappa] = kappa;
+      tmp = mulrr(gmael(mu,kappa,kappa-1), gmael(r,kappa,kappa-1));
+      affrr(subrr(gel(s,kappa-1), tmp), gmael(r,kappa,kappa));
+      avma = av2;
+    }
+    else
+    { /* Step5: Find the right insertion index kappa, kappa2 = initial kappa */
+      if (DEBUGLEVEL>=10 && kappa==kappamax && signe(gel(s,kappa-1)))
+      {
+        long e = expo(divrr(mulrr(gmael(r,kappa-1,kappa-1), delta),
+                            gel(s,kappa-1)));
+        err_printf("(%ld) ", e);
+      }
+      kappa2 = kappa;
+      do {
+        kappa--;
+        if (kappa<zeros+2 + (keepfirst ? 1: 0)) break;
+        tmp = mulrr(gmael(r,kappa-1,kappa-1), delta);
+      } while (cmprr(gel(s,kappa-1), tmp) <=0 );
+      avma = av2;
+
+      for (i=kappa; i<kappa2; i++)
+        if (kappa <= alpha[i]) alpha[i] = kappa;
+      for (i=kappa2; i>kappa; i--) alpha[i] = alpha[i-1];
+      for (i=kappa2+1; i<=kappamax; i++)
+        if (kappa < alpha[i]) alpha[i] = kappa;
+      alpha[kappa] = kappa;
+
+      /* Step6: Update the mu's and r's */
+      rotate(mu,kappa2,kappa,d);
+      rotate(r,kappa2,kappa,d);
+      affrr(gel(s,kappa), gmael(r,kappa,kappa));
+
+      /* Step7: Update B, G, U */
+      if (!gram) rotate(B,kappa2,kappa,n);
+      if (U) rotate(U,kappa2,kappa,d);
+
+      for (i=1; i<=kappa2; i++) gel(SPtmp,i) = gmael(G,kappa2,i);
+      for (i=kappa2+1; i<=maxG; i++) gel(SPtmp,i) = gmael(G,i,kappa2);
+      for (i=kappa2; i>kappa; i--)
+      {
+        for (j=1; j<kappa; j++) gmael(G,i,j) = gmael(G,i-1,j);
+        gmael(G,i,kappa) = gel(SPtmp,i-1);
+        for (j=kappa+1; j<=i; j++) gmael(G,i,j) = gmael(G,i-1,j-1);
+        for (j=kappa2+1; j<=maxG; j++) gmael(G,j,i) = gmael(G,j,i-1);
+      }
+      for (i=1; i<kappa; i++) gmael(G,kappa,i) = gel(SPtmp,i);
+      gmael(G,kappa,kappa) = gel(SPtmp,kappa2);
+      for (i=kappa2+1; i<=maxG; i++) gmael(G,i,kappa) = gel(SPtmp,i);
+
+      /* Step8: Prepare the next loop iteration */
+      if (kappa == zeros+1 && !signe(gmael(G,kappa,kappa)))
+      {
+        zeros++; kappa++;
+        affir(gmael(G,kappa,kappa), gmael(r,kappa,kappa));
+      }
+    }
+  }
+
+  if (DEBUGLEVEL>=4) timer_printf(&T,"LLL");
+  if (ptrr) *ptrr = RgM_diagonal_shallow(r);
+  if (!U)
+  {
+    if (zeros) {
+      if (gram) {
+        G = lll_get_im(G, zeros);
+        d -= zeros;
+        for (i = 1; i <= d; i++) gel(G,i) = lll_get_im(gel(G,i), zeros);
+      }
+      else
+        B = lll_get_im(B, zeros);
+    }
+  }
+  else if (flag & (LLL_IM|LLL_KER|LLL_ALL))
+    U = lll_finish(U, zeros, flag);
+  if (gram)
+  {
+    if (U) return U;
+    for (i = 1; i <= d; i++)
+      for (j = i+1; j <= d; j++) gmael(G,i,j) = gmael(G,j,i);
+    return G;
+  }
+  return U? U: B;
+}
+
+/* Assume x a ZM, if ptB != NULL, set it to Gram-Schmidt (squared) norms */
+GEN
+ZM_lll_norms(GEN x, double DELTA, long flag, GEN *B)
+{
+  pari_sp ltop = avma;
+  const double ETA = 0.51;
+  long p, n = lg(x)-1;
+  GEN U;
+  if (n <= 1) return lll_trivial(x, flag);
+  x = RgM_shallowcopy(x);
+  U = (flag & LLL_INPLACE)? NULL: matid(n);
+  for (p = LOWDEFAULTPREC; ; incrprec(p))
+  {
+    GEN m = fplll(&x, &U, B, DELTA, ETA, flag, p);
+    if (m) return m;
+    gerepileall(ltop, U? 2: 1, &x, &U);
+  }
+  return NULL; /* NOT REACHED */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        LLL OVER K[X]                           **/
+/**                                                                **/
+/********************************************************************/
+static int
+pslg(GEN x)
+{
+  long tx;
+  if (gequal0(x)) return 2;
+  tx = typ(x); return is_scalar_t(tx)? 3: lg(x);
+}
+
+static int
+REDgen(long k, long l, GEN h, GEN L, GEN B)
+{
+  GEN q, u = gcoeff(L,k,l);
+  long i;
+
+  if (pslg(u) < pslg(B)) return 0;
+
+  q = gneg(gdeuc(u,B));
+  gel(h,k) = gadd(gel(h,k), gmul(q,gel(h,l)));
+  for (i=1; i<l; i++) gcoeff(L,k,i) = gadd(gcoeff(L,k,i), gmul(q,gcoeff(L,l,i)));
+  gcoeff(L,k,l) = gadd(gcoeff(L,k,l), gmul(q,B)); return 1;
+}
+
+static int
+do_SWAPgen(GEN h, GEN L, GEN B, long k, GEN fl, int *flc)
+{
+  GEN p1, la, la2, Bk;
+  long ps1, ps2, i, j, lx;
+
+  if (!fl[k-1]) return 0;
+
+  la = gcoeff(L,k,k-1); la2 = gsqr(la);
+  Bk = gel(B,k);
+  if (fl[k])
+  {
+    GEN q = gadd(la2, gmul(gel(B,k-1),gel(B,k+1)));
+    ps1 = pslg(gsqr(Bk));
+    ps2 = pslg(q);
+    if (ps1 <= ps2 && (ps1 < ps2 || !*flc)) return 0;
+    *flc = (ps1 != ps2);
+    gel(B,k) = gdiv(q, Bk);
+  }
+
+  swap(gel(h,k-1), gel(h,k)); lx = lg(L);
+  for (j=1; j<k-1; j++) swap(gcoeff(L,k-1,j), gcoeff(L,k,j));
+  if (fl[k])
+  {
+    for (i=k+1; i<lx; i++)
+    {
+      GEN t = gcoeff(L,i,k);
+      p1 = gsub(gmul(gel(B,k+1),gcoeff(L,i,k-1)), gmul(la,t));
+      gcoeff(L,i,k) = gdiv(p1, Bk);
+      p1 = gadd(gmul(la,gcoeff(L,i,k-1)), gmul(gel(B,k-1),t));
+      gcoeff(L,i,k-1) = gdiv(p1, Bk);
+    }
+  }
+  else if (!gequal0(la))
+  {
+    p1 = gdiv(la2, Bk);
+    gel(B,k+1) = gel(B,k) = p1;
+    for (i=k+2; i<=lx; i++) gel(B,i) = gdiv(gmul(p1,gel(B,i)),Bk);
+    for (i=k+1; i<lx; i++)
+      gcoeff(L,i,k-1) = gdiv(gmul(la,gcoeff(L,i,k-1)), Bk);
+    for (j=k+1; j<lx-1; j++)
+      for (i=j+1; i<lx; i++)
+        gcoeff(L,i,j) = gdiv(gmul(p1,gcoeff(L,i,j)), Bk);
+  }
+  else
+  {
+    gcoeff(L,k,k-1) = gen_0;
+    for (i=k+1; i<lx; i++)
+    {
+      gcoeff(L,i,k) = gcoeff(L,i,k-1);
+      gcoeff(L,i,k-1) = gen_0;
+    }
+    B[k] = B[k-1]; fl[k] = 1; fl[k-1] = 0;
+  }
+  return 1;
+}
+
+static void
+incrementalGSgen(GEN x, GEN L, GEN B, long k, GEN fl)
+{
+  GEN u = NULL; /* gcc -Wall */
+  long i, j, tu;
+  for (j=1; j<=k; j++)
+    if (j==k || fl[j])
+    {
+      u = gcoeff(x,k,j); tu = typ(u);
+      if (! is_extscalar_t(tu)) pari_err_TYPE("incrementalGSgen",u);
+      for (i=1; i<j; i++)
+        if (fl[i])
+        {
+          u = gsub(gmul(gel(B,i+1),u), gmul(gcoeff(L,k,i),gcoeff(L,j,i)));
+          u = gdiv(u, gel(B,i));
+        }
+      gcoeff(L,k,j) = u;
+    }
+  if (gequal0(u)) B[k+1] = B[k];
+  else
+  {
+    gel(B,k+1) = gcoeff(L,k,k); gcoeff(L,k,k) = gen_1; fl[k] = 1;
+  }
+}
+
+static GEN
+lllgramallgen(GEN x, long flag)
+{
+  long lx = lg(x), i, j, k, l, n;
+  pari_sp av, lim;
+  GEN B, L, h, fl;
+  int flc;
+
+  n = lx-1; if (n<=1) return lll_trivial(x,flag);
+  if (lgcols(x) != lx) pari_err_DIM("lllgramallgen");
+
+  fl = cgetg(lx, t_VECSMALL);
+
+  av = avma; lim = stack_lim(av,1);
+  B = scalarcol_shallow(gen_1, lx);
+  L = cgetg(lx,t_MAT);
+  for (j=1; j<lx; j++) { gel(L,j) = zerocol(n); fl[j] = 0; }
+
+  h = matid(n);
+  for (i=1; i<lx; i++)
+    incrementalGSgen(x, L, B, i, fl);
+  flc = 0;
+  for(k=2;;)
+  {
+    if (REDgen(k, k-1, h, L, gel(B,k))) flc = 1;
+    if (do_SWAPgen(h, L, B, k, fl, &flc)) { if (k > 2) k--; }
+    else
+    {
+      for (l=k-2; l>=1; l--)
+        if (REDgen(k, l, h, L, gel(B,l+1))) flc = 1;
+      if (++k > n) break;
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"lllgramallgen");
+      gerepileall(av,3,&B,&L,&h);
+    }
+  }
+  k=1; while (k<lx && !fl[k]) k++;
+  return lll_finish(h,k-1,flag);
+}
+
+static GEN
+lllallgen(GEN x, long flag)
+{
+  pari_sp av = avma;
+  if ((flag & LLL_GRAM) == 0) x = gram_matrix(x);
+  return gerepilecopy(av, lllgramallgen(x, flag));
+}
+GEN
+lllgen(GEN x) { return lllallgen(x, LLL_IM); }
+GEN
+lllkerimgen(GEN x) { return lllallgen(x, LLL_ALL); }
+GEN
+lllgramgen(GEN x)  { return lllallgen(x, LLL_IM|LLL_GRAM); }
+GEN
+lllgramkerimgen(GEN x)  { return lllallgen(x, LLL_ALL|LLL_GRAM); }
+
+static GEN
+lllall(GEN x, long flag)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, ZM_lll(x, LLLDFT, flag));
+}
+GEN
+lllint(GEN x) { return lllall(x, LLL_IM); }
+GEN
+lllkerim(GEN x) { return lllall(x, LLL_ALL); }
+GEN
+lllgramint(GEN x) { return lllall(x, LLL_IM | LLL_GRAM); }
+GEN
+lllgramkerim(GEN x) { return lllall(x, LLL_ALL | LLL_GRAM); }
+
+static GEN
+rescale_to_int(GEN x)
+{
+  long e, i,j, lx, hx, emin;
+  GEN D = gen_1;
+  int exact = 1;
+
+  lx = lg(x); if (lx == 1) return x;
+  hx = lgcols(x);
+  emin = HIGHEXPOBIT;
+  for (j = 1; j < lx; j++)
+    for (i = 1; i < hx; i++)
+    {
+      GEN c = gcoeff(x,i,j);
+      switch(typ(c))
+      {
+        case t_REAL:
+          exact = 0;
+          if (!signe(c)) continue;
+          e = expo(c) - bit_prec(c);
+          break;
+        case t_INT:
+          if (!signe(c)) continue;
+          e = expi(c) + 32;
+          break;
+        case t_FRAC:
+          e = expi(gel(c,1)) - expi(gel(c,2)) + 32;
+          if (exact) D = lcmii(D, gel(c,2));
+          break;
+        default:
+          pari_err_TYPE("rescale_to_int",c);
+          return NULL; /* not reached */
+      }
+      if (e < emin) emin = e;
+    }
+  if (exact) return D == gen_1 ? x: Q_muli_to_int(x, D);
+  return grndtoi(gmul2n(x, -emin), &e);
+}
+
+GEN
+lllfp(GEN x, double D, long flag)
+{
+  long n = lg(x)-1;
+  pari_sp av = avma;
+  GEN h;
+  if (n <= 1) return lll_trivial(x,flag);
+  h = ZM_lll(rescale_to_int(x), D, flag);
+  return gerepilecopy(av, h);
+}
+
+GEN
+lllgram(GEN x) { return lllfp(x,LLLDFT,LLL_GRAM|LLL_IM); }
+GEN
+lll(GEN x) { return lllfp(x,LLLDFT,LLL_IM); }
+
+GEN
+qflll0(GEN x, long flag)
+{
+  if (typ(x) != t_MAT) pari_err_TYPE("qflll",x);
+  switch(flag)
+  {
+    case 0: return lll(x);
+    case 1: RgM_check_ZM(x,"qflll"); return lllint(x);
+    case 2: RgM_check_ZM(x,"qflll"); return lllintpartial(x);
+    case 4: RgM_check_ZM(x,"qflll"); return lllkerim(x);
+    case 5: return lllkerimgen(x);
+    case 8: return lllgen(x);
+    default: pari_err_FLAG("qflll");
+  }
+  return NULL; /* not reached */
+}
+
+GEN
+qflllgram0(GEN x, long flag)
+{
+  if (typ(x) != t_MAT) pari_err_TYPE("qflllgram",x);
+  switch(flag)
+  {
+    case 0: return lllgram(x);
+    case 1: RgM_check_ZM(x,"qflllgram"); return lllgramint(x);
+    case 4: RgM_check_ZM(x,"qflllgram"); return lllgramkerim(x);
+    case 5: return lllgramkerimgen(x);
+    case 8: return lllgramgen(x);
+    default: pari_err_FLAG("qflllgram");
+  }
+  return NULL; /* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                   INTEGRAL KERNEL (LLL REDUCED)                **/
+/**                                                                **/
+/********************************************************************/
+/* Horribly slow (coeff explosion in small dimension): never use this */
+static GEN
+kerint1(GEN x)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, ZM_lll(QM_ImQ_hnf(ker(x)), LLLDFT, LLL_INPLACE));
+}
+/* Mostly useless: use ZM_lll(x, 0.99, LLL_KER) directly */
+GEN
+kerint(GEN x)
+{
+  pari_sp av = avma;
+  GEN h = ZM_lll(x, LLLDFT, LLL_KER);
+  if (lg(h)==1) { avma = av; return cgetg(1, t_MAT); }
+  return gerepilecopy(av, ZM_lll(h, LLLDFT, LLL_INPLACE));
+}
+
+GEN
+matkerint0(GEN x, long flag)
+{
+  if (typ(x) != t_MAT) pari_err_TYPE("matkerint",x);
+  RgM_check_ZM(x, "kerint");
+  switch(flag)
+  {
+    case 0: return kerint(x);
+    case 1: return kerint1(x);
+    default: pari_err_FLAG("matkerint");
+  }
+  return NULL; /* not reached */
+}
diff --git a/src/basemath/nffactor.c b/src/basemath/nffactor.c
new file mode 100644
index 0000000..fa71439
--- /dev/null
+++ b/src/basemath/nffactor.c
@@ -0,0 +1,2220 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*            POLYNOMIAL FACTORIZATION IN A NUMBER FIELD           */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+static GEN nfsqff(GEN nf,GEN pol,long fl,GEN den);
+static int nfsqff_use_Trager(long n, long dpol);
+
+enum { FACTORS = 0, ROOTS, ROOTS_SPLIT };
+
+/* for nf_bestlift: reconstruction of algebraic integers known mod P^k,
+ * P maximal ideal above p */
+typedef struct {
+  long k;    /* input known mod P^k */
+  GEN p, pk; /* p^k */
+  GEN den;   /* denom(prk^-1) = p^k [ assume pr unramified ] */
+  GEN prk;   /* |.|^2 LLL-reduced basis (b_i) of P^k  (NOT T2-reduced) */
+  GEN prkHNF;/* HNF basis of P^k */
+  GEN iprk;  /* den * prk^-1 */
+  GEN GSmin; /* min |b_i^*|^2 */
+
+  GEN Tp; /* Tpk mod p */
+  GEN Tpk;
+  GEN ZqProj;/* projector to Zp / P^k = Z/p^k[X] / Tpk */
+
+  GEN tozk;
+  GEN topow;
+  GEN topowden; /* topow x / topowden = basistoalg(x) */
+  GEN dn; /* NULL (we trust nf.zk) or a t_INT > 1 (an alg. integer has
+             denominator dividing dn, when expressed on nf.zk */
+} nflift_t;
+
+typedef struct
+{
+  nflift_t *L;
+  GEN nf;
+  GEN pol, polbase; /* leading coeff is a t_INT */
+  GEN fact;
+  GEN pr;
+  GEN Br, bound, ZC, BS_2;
+} nfcmbf_t;
+
+/*******************************************************************/
+/*              RATIONAL RECONSTRUCTION (use ratlift)              */
+/*******************************************************************/
+/* NOT stack clean. a, b stay on the stack */
+static GEN
+lift_to_frac(GEN t, GEN mod, GEN amax, GEN bmax, GEN denom)
+{
+  GEN a, b;
+  if (signe(t) < 0) t = addii(t, mod); /* in case t is a centerlift */
+  if (!Fp_ratlift(t, mod, amax,bmax, &a,&b)
+     || (denom && !dvdii(denom,b))
+     || !is_pm1(gcdii(a,b))) return NULL;
+  if (is_pm1(b)) { cgiv(b); return a; }
+  return mkfrac(a, b);
+}
+
+/* Compute rational lifting for all the components of M modulo mod.
+ * Assume all Fp_ratlift preconditions are met; we allow centerlifts but in
+ * that case are no longer stack clean. If one component fails, return NULL.
+ * If denom != NULL, check that the denominators divide denom.
+ *
+ * We suppose gcd(mod, denom) = 1, then a and b are coprime; so we can use
+ * mkfrac rather than gdiv */
+GEN
+FpM_ratlift(GEN M, GEN mod, GEN amax, GEN bmax, GEN denom)
+{
+  pari_sp av = avma;
+  long i, j, h, l = lg(M);
+  GEN a, N = cgetg_copy(M, &l);
+  if (l == 1) return N;
+  h = lgcols(M);
+  for (j = 1; j < l; ++j)
+  {
+    gel(N,j) = cgetg(h, t_COL);
+    for (i = 1; i < h; ++i)
+    {
+      a = lift_to_frac(gcoeff(M,i,j), mod, amax,bmax,denom);
+      if (!a) { avma = av; return NULL; }
+      gcoeff(N,i,j) = a;
+    }
+  }
+  return N;
+}
+GEN
+FpC_ratlift(GEN P, GEN mod, GEN amax, GEN bmax, GEN denom)
+{
+  pari_sp ltop = avma;
+  long j, l;
+  GEN a, Q = cgetg_copy(P, &l);
+  for (j = 1; j < l; ++j)
+  {
+    a = lift_to_frac(gel(P,j), mod, amax,bmax,denom);
+    if (!a) { avma = ltop; return NULL; }
+    gel(Q,j) = a;
+  }
+  return Q;
+}
+GEN
+FpX_ratlift(GEN P, GEN mod, GEN amax, GEN bmax, GEN denom)
+{
+  pari_sp ltop = avma;
+  long j, l;
+  GEN a, Q = cgetg_copy(P, &l);
+  Q[1] = P[1];
+  for (j = 2; j < l; ++j)
+  {
+    a = lift_to_frac(gel(P,j), mod, amax,bmax,denom);
+    if (!a) { avma = ltop; return NULL; }
+    gel(Q,j) = a;
+  }
+  return Q;
+}
+
+/*******************************************************************/
+/*              GCD in K[X], K NUMBER FIELD                        */
+/*******************************************************************/
+/* P,Q in Z[X,Y], T in Z[Y] irreducible. compute GCD in Q[Y]/(T)[X].
+ *
+ * M. Encarnacion "On a modular Algorithm for computing GCDs of polynomials
+ * over number fields" (ISSAC'94).
+ *
+ * We procede as follows
+ *  1:compute the gcd modulo primes discarding bad primes as they are detected.
+ *  2:reconstruct the result via FpM_ratlift, stoping as soon as we get weird
+ *    denominators.
+ *  3:if FpM_ratlift succeeds, try the full division.
+ * Suppose accuracy is insufficient to get the result right: FpM_ratlift will
+ * rarely succeed, and even if it does the polynomial we get has sensible
+ * coefficients, so the full division will not be too costly.
+ *
+ * If not NULL, den must a a multiple of the denominator of the gcd,
+ * for example the discriminant of T.
+ *
+ * NOTE: if T is not irreducible, nfgcd may loop forever, esp. if gcd | T */
+GEN
+nfgcd_all(GEN P, GEN Q, GEN T, GEN den, GEN *Pnew)
+{
+  pari_sp btop, st_lim, ltop = avma;
+  GEN lP, lQ, M, dsol, R, bo, sol, mod = NULL;
+  long vP = varn(P), vT = varn(T), dT = degpol(T), dM = 0, dR;
+  forprime_t S;
+
+  if (!signe(P)) { if (Pnew) *Pnew = pol_0(vT); return gcopy(Q); }
+  if (!signe(Q)) { if (Pnew) *Pnew = pol_1(vT);   return gcopy(P); }
+  /*Compute denominators*/
+  if (!den) den = ZX_disc(T);
+  lP = leading_term(P);
+  lQ = leading_term(Q);
+  if ( !((typ(lP)==t_INT && is_pm1(lP)) || (typ(lQ)==t_INT && is_pm1(lQ))) )
+    den = mulii(den, gcdii(ZX_resultant(lP, T), ZX_resultant(lQ, T)));
+
+  init_modular(&S);
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for(;;)
+  {
+    ulong p = u_forprime_next(&S);
+    if (!p) pari_err_OVERFLOW("nfgcd [ran out of primes]");
+    /*Discard primes dividing disc(T) or lc(PQ) */
+    if (!umodiu(den, p)) continue;
+    if (DEBUGLEVEL>5) err_printf("nfgcd: p=%d\n",p);
+    /*Discard primes when modular gcd does not exist*/
+    if ((R = FlxqX_safegcd(ZXX_to_FlxX(P,p,vT),
+                           ZXX_to_FlxX(Q,p,vT),
+                           ZX_to_Flx(T,p), p)) == NULL) continue;
+    dR = degpol(R);
+    if (dR == 0) { avma = ltop; if (Pnew) *Pnew = P; return pol_1(vP); }
+    if (mod && dR > dM) continue; /* p divides Res(P/gcd, Q/gcd). Discard. */
+
+    R = FlxX_to_Flm(R, dT);
+    /* previous primes divided Res(P/gcd, Q/gcd)? Discard them. */
+    if (!mod || dR < dM) { M = ZM_init_CRT(R, p); mod = utoipos(p); dM = dR; continue; }
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"nfgcd");
+      gerepileall(btop, 2, &M, &mod);
+    }
+
+    (void)ZM_incremental_CRT(&M,R, &mod,p);
+    /* I suspect it must be better to take amax > bmax*/
+    bo = sqrti(shifti(mod, -1));
+    if ((sol = FpM_ratlift(M, mod, bo, bo, den)) == NULL) continue;
+    sol = RgM_to_RgXX(sol,vP,vT);
+    dsol = Q_primpart(sol);
+
+    if (!ZXQX_dvd(Q, dsol, T)) continue;
+    if (Pnew)
+    {
+      *Pnew = RgXQX_pseudodivrem(P, dsol, T, &R);
+      if (signe(R)) continue;
+    }
+    else
+    {
+      if (!ZXQX_dvd(P, dsol, T)) continue;
+    }
+    gerepileall(ltop, Pnew? 2: 1, &dsol, Pnew);
+    return dsol; /* both remainders are 0 */
+  }
+}
+GEN
+nfgcd(GEN P, GEN Q, GEN T, GEN den)
+{ return nfgcd_all(P,Q,T,den,NULL); }
+
+/*******************************************************************/
+/*             FACTOR OVER (Z_K/pr)[X] --> FqX_factor              */
+/*******************************************************************/
+GEN
+nffactormod(GEN nf, GEN x, GEN pr)
+{
+  long j, l, vx = varn(x), vn;
+  pari_sp av = avma;
+  GEN F, E, rep, xrd, modpr, T, p;
+
+  nf = checknf(nf);
+  vn = nf_get_varn(nf);
+  if (typ(x)!=t_POL) pari_err_TYPE("nffactormod",x);
+  if (varncmp(vx,vn) >= 0) pari_err_PRIORITY("nffactormod", x, ">=", vn);
+
+  modpr = nf_to_Fq_init(nf, &pr, &T, &p);
+  xrd = nfX_to_FqX(x, nf, modpr);
+  rep = FqX_factor(xrd,T,p);
+  settyp(rep, t_MAT);
+  F = gel(rep,1); l = lg(F);
+  E = gel(rep,2); settyp(E, t_COL);
+  for (j = 1; j < l; j++) {
+    gel(F,j) = FqX_to_nfX(gel(F,j), modpr);
+    gel(E,j) = stoi(E[j]);
+  }
+  return gerepilecopy(av, rep);
+}
+
+/*******************************************************************/
+/*               MAIN ROUTINES nfroots / nffactor                  */
+/*******************************************************************/
+static GEN
+QXQX_normalize(GEN P, GEN T)
+{
+  GEN P0 = leading_term(P);
+  long t = typ(P0);
+  if (t == t_POL)
+  {
+    if (degpol(P0)) return RgXQX_RgXQ_mul(P, QXQ_inv(P0,T), T);
+    P0 = gel(P0,2); t = typ(P0);
+  }
+  /* t = t_INT/t_FRAC */
+  if (t == t_INT && is_pm1(P0) && signe(P0) > 0) return P; /* monic */
+  return RgX_Rg_div(P, P0);
+}
+/* assume leading term of P is an integer */
+static GEN
+RgX_int_normalize(GEN P)
+{
+  GEN P0 = leading_term(P);
+  /* cater for t_POL */
+  if (typ(P0) == t_POL)
+  {
+    P0 = gel(P0,2); /* non-0 constant */
+    P = shallowcopy(P);
+    gel(P,lg(P)-1) = P0; /* now leading term is a t_INT */
+  }
+  if (typ(P0) != t_INT) pari_err_BUG("RgX_int_normalize");
+  if (is_pm1(P0)) return signe(P0) > 0? P: RgX_neg(P);
+  return RgX_Rg_div(P, P0);
+}
+
+/* discard change of variable if nf is of the form [nf,c] as return by nfinit
+ * for non-monic polynomials */
+static GEN
+proper_nf(GEN nf)
+{ return (lg(nf) == 3)? gel(nf,1): nf; }
+
+static GEN
+get_den(GEN *pnf, GEN T)
+{
+  GEN den = gen_1;
+  if (!*pnf)
+  {
+    GEN fa, P, q, D;
+    *pnf = nfinitall(T, nf_PARTIALFACT, DEFAULTPREC);
+    D = nf_get_disc(proper_nf(*pnf));
+    if (is_pm1(D)) return gen_1;
+    fa = absi_factor_limit(D, 0);
+    P = gel(fa,1); q = gel(P, lg(P)-1);
+    if (!BPSW_psp(q)) den = q; /* nf_get_disc(nf) may be incorrect */
+  }
+  return den;
+}
+
+/* set B = A/gcd(A,A'), squarefree */
+static GEN
+get_nfsqff_data(GEN *pnf, GEN *pT, GEN *pA, GEN *pB, GEN *ptbad)
+{
+  GEN den, bad, A = *pA, T = *pT;
+  long n = degpol(T);
+  if (nfsqff_use_Trager(n, degpol(A)))
+  {
+    *pnf = T; bad = den = ZX_disc(T);
+    if (is_pm1(leading_term(T))) den = indexpartial(T, den);
+  }
+  else
+  {
+    GEN nf;
+    den = get_den(pnf, T);
+    nf = proper_nf(*pnf);
+    bad = nf_get_index(nf);
+    if (den != gen_1) bad = mulii(bad, den);
+    if (nf != *pnf) { /* t_POL defining base field changed (not monic) */
+      long i, l;
+      GEN a = cgetg_copy(A, &l);
+      GEN rev = gel(*pnf,2), pow, dpow;
+
+      *pT = T = nf_get_pol(nf); /* need to update T */
+      pow = QXQ_powers(lift_intern(rev), n-1, T);
+      pow = Q_remove_denom(pow, &dpow);
+      a[1] = A[1];
+      for (i=2; i<l; i++) {
+        GEN c = gel(A,i);
+        if (typ(c) == t_POL) c = QX_ZXQV_eval(c, pow, dpow);
+        gel(a,i) = c;
+      }
+      *pA = A = Q_primpart(a); /* need to update A */
+      *pnf = nf; /* now discard change of variable */
+    }
+  }
+  (void)nfgcd_all(A, RgX_deriv(A), T, bad, pB);
+  if( ptbad) *ptbad = bad;
+  return den;
+}
+
+/* lt(A) is an integer; ensure it is not a constant t_POL. In place */
+static void
+ensure_lt_INT(GEN A)
+{
+  long n = lg(A)-1;
+  GEN lt = gel(A,n);
+  while (typ(lt) != t_INT) gel(A,n) = lt = gel(lt,2);
+}
+
+/* return the roots of pol in nf */
+GEN
+nfroots(GEN nf,GEN pol)
+{
+  pari_sp av = avma;
+  GEN z, A, B, T, den;
+  long d;
+
+  if (!nf) return nfrootsQ(pol);
+  T = get_nfpol(nf, &nf);
+  RgX_check_ZX(T,"nfroots");
+  A = RgX_nffix("nfroots", T,pol,1);
+  d = degpol(A);
+  if (d < 0) pari_err_ROOTS0("nfroots");
+  if (d == 0) return cgetg(1,t_VEC);
+  if (d == 1)
+  {
+    A = QXQX_normalize(A,T);
+    A = mkpolmod(gneg_i(gel(A,2)), T);
+    return gerepilecopy(av, mkvec(A));
+  }
+  if (degpol(T) == 1) return gerepileupto(av, nfrootsQ(simplify_shallow(A)));
+
+  A = Q_primpart(A);
+  den = get_nfsqff_data(&nf, &T, &A, &B, NULL);
+  if (degpol(B) != d) B = Q_primpart( QXQX_normalize(B, T) );
+  ensure_lt_INT(B);
+  z = nfsqff(nf,B, ROOTS, den);
+  z = gerepileupto(av, QXQV_to_mod(z, T));
+  gen_sort_inplace(z, (void*)&cmp_RgX, &cmp_nodata, NULL);
+  return z;
+}
+
+/* assume x is squarefree monic in nf.zk[X] */
+int
+nfissplit(GEN nf, GEN x)
+{
+  pari_sp av = avma;
+  long l;
+  nf = checknf(nf);
+  if (typ(x) != t_POL) pari_err_TYPE("nfissplit",x);
+  x = RgX_nffix("nfissplit", nf_get_pol(nf), x, 1);
+  l = lg(nfsqff(nf, x, ROOTS_SPLIT, gen_1));
+  avma = av; return l != 1;
+}
+
+/* return a minimal lift of elt modulo id, as a ZC */
+static GEN
+nf_bestlift(GEN elt, GEN bound, nflift_t *L)
+{
+  GEN u;
+  long i,l = lg(L->prk), t = typ(elt);
+  if (t != t_INT)
+  {
+    if (t == t_POL) elt = mulmat_pol(L->tozk, elt);
+    u = ZM_ZC_mul(L->iprk,elt);
+    for (i=1; i<l; i++) gel(u,i) = diviiround(gel(u,i), L->den);
+  }
+  else
+  {
+    u = ZC_Z_mul(gel(L->iprk,1), elt);
+    for (i=1; i<l; i++) gel(u,i) = diviiround(gel(u,i), L->den);
+    elt = scalarcol(elt, l-1);
+  }
+  u = ZC_sub(elt, ZM_ZC_mul(L->prk, u));
+  if (bound && gcmp(RgC_fpnorml2(u,DEFAULTPREC), bound) > 0) u = NULL;
+  return u;
+}
+
+/* Warning: return L->topowden * (best lift). */
+static GEN
+nf_bestlift_to_pol(GEN elt, GEN bound, nflift_t *L)
+{
+  pari_sp av = avma;
+  GEN u,v = nf_bestlift(elt,bound,L);
+  if (!v) return NULL;
+  if (ZV_isscalar(v))
+  {
+    if (L->topowden)
+      u = mulii(L->topowden, gel(v,1));
+    else
+      u = icopy(gel(v,1));
+    u = gerepileuptoint(av, u);
+  }
+  else
+  {
+    v = gclone(v); avma = av;
+    u = RgV_dotproduct(L->topow, v);
+    gunclone(v);
+  }
+  return u;
+}
+
+/* return the T->powden * (lift of pol with coefficients of T2-norm <= C)
+ * if it exists. */
+static GEN
+nf_pol_lift(GEN pol, GEN bound, nfcmbf_t *T)
+{
+  long i, l = lg(pol);
+  GEN t, x = cgetg(l,t_POL);
+
+  x[1] = pol[1];
+  gel(x,l-1) = mul_content(gel(pol,l-1), T->L->topowden);
+  for (i=l-2; i>1; i--)
+  {
+    t = nf_bestlift_to_pol(gel(pol,i), bound, T->L);
+    if (!t) return NULL;
+    gel(x,i) = t;
+  }
+  return x;
+}
+
+static GEN
+zerofact(long v)
+{
+  GEN z = cgetg(3, t_MAT);
+  gel(z,1) = mkcol(pol_0(v));
+  gel(z,2) = mkcol(gen_1); return z;
+}
+
+/* Return the factorization of A in Q[X]/(T) in rep [pre-allocated with
+ * cgetg(3,t_MAT)], reclaiming all memory between avma and rep.
+ * y is the vector of irreducible factors of B = Q_primpart( A/gcd(A,A') ).
+ * Bad primes divide 'bad' */
+static void
+fact_from_sqff(GEN rep, GEN A, GEN B, GEN y, GEN T, GEN bad)
+{
+  pari_sp av = (pari_sp)rep;
+  long n = lg(y)-1;
+  GEN ex;
+
+  if (A != B)
+  { /* not squarefree */
+    if (n == 1)
+    { /* perfect power, simple ! */
+      long e = degpol(A) / degpol(gel(y,1));
+      y = gerepileupto(av, QXQXV_to_mod(y, T));
+      ex = mkcol(utoipos(e));
+    }
+    else
+    { /* compute valuations mod a prime of degree 1 (avoid coeff explosion) */
+      GEN quo, p, r, Bp, lb = leading_term(B), E = cgetalloc(t_VECSMALL,n+1);
+      pari_sp av1 = avma;
+      ulong pp;
+      long j;
+      forprime_t S;
+      u_forprime_init(&S, degpol(T), ULONG_MAX);
+      for (; ; avma = av1)
+      {
+        pp = u_forprime_next(&S);
+        if (! umodiu(bad,pp) || !umodiu(lb, pp)) continue;
+        p = utoipos(pp);
+        r = FpX_oneroot(T, p);
+        if (!r) continue;
+        Bp = FpXY_evalx(B, r, p);
+        if (FpX_is_squarefree(Bp, p)) break;
+      }
+
+      quo = FpXY_evalx(Q_primpart(A), r, p);
+      for (j=n; j>=2; j--)
+      {
+        GEN junk, fact = Q_remove_denom(gel(y,j), &junk);
+        long e = 0;
+        fact = FpXY_evalx(fact, r, p);
+        for(;; e++)
+        {
+          GEN q = FpX_divrem(quo,fact,p,ONLY_DIVIDES);
+          if (!q) break;
+          quo = q;
+        }
+        E[j] = e;
+      }
+      E[1] = degpol(quo) / degpol(gel(y,1));
+      y = gerepileupto(av, QXQXV_to_mod(y, T));
+      ex = zc_to_ZC(E); pari_free((void*)E);
+    }
+  }
+  else
+  {
+    y = gerepileupto(av, QXQXV_to_mod(y, T));
+    ex = const_col(n, gen_1);
+  }
+  gel(rep,1) = y; settyp(y, t_COL);
+  gel(rep,2) = ex;
+}
+
+/* return the factorization of x in nf */
+GEN
+nffactor(GEN nf,GEN pol)
+{
+  GEN bad, A, B, y, T, den, rep = cgetg(3, t_MAT);
+  pari_sp av = avma;
+  long dA;
+  pari_timer ti;
+
+  if (DEBUGLEVEL>2) { timer_start(&ti); err_printf("\nEntering nffactor:\n"); }
+  T = get_nfpol(nf, &nf);
+  RgX_check_ZX(T,"nffactor");
+  A = RgX_nffix("nffactor",T,pol,1);
+  dA = degpol(A);
+  if (dA <= 0) {
+    avma = (pari_sp)(rep + 3);
+    return (dA == 0)? trivial_fact(): zerofact(varn(pol));
+  }
+  A = Q_primpart( QXQX_normalize(A, T) );
+  if (dA == 1) {
+    GEN c;
+    A = gerepilecopy(av, A); c = gel(A,2);
+    if (typ(c) == t_POL && degpol(c) > 0) gel(A,2) = mkpolmod(c, ZX_copy(T));
+    gel(rep,1) = mkcol(A);
+    gel(rep,2) = mkcol(gen_1); return rep;
+  }
+  if (degpol(T) == 1) return gerepileupto(av, QX_factor(simplify_shallow(A)));
+
+  den = get_nfsqff_data(&nf, &T, &A, &B, &bad);
+  if (DEBUGLEVEL>2) timer_printf(&ti, "squarefree test");
+  if (degpol(B) != dA) B = Q_primpart( QXQX_normalize(B, T) );
+  ensure_lt_INT(B);
+  y = nfsqff(nf,B, 0, den);
+  if (DEBUGLEVEL>3) err_printf("number of factor(s) found: %ld\n", lg(y)-1);
+
+  fact_from_sqff(rep, A, B, y, T, bad);
+  return sort_factor_pol(rep, cmp_RgX);
+}
+
+/* assume x scalar or t_COL, G t_MAT */
+static GEN
+arch_for_T2(GEN G, GEN x)
+{
+  return (typ(x) == t_COL)? RgM_RgC_mul(G,x)
+                          : RgC_Rg_mul(gel(G,1),x);
+}
+static GEN
+arch_for_T2_prec(GEN G, GEN x, long prec)
+{
+  return (typ(x) == t_COL)? RgM_RgC_mul(G, RgC_gtofp(x,prec))
+                          : RgC_Rg_mul(gel(G,1), gtofp(x, prec));
+}
+
+/* return a bound for T_2(P), P | polbase in C[X]
+ * NB: Mignotte bound: A | S ==>
+ *  |a_i| <= binom(d-1, i-1) || S ||_2 + binom(d-1, i) lc(S)
+ *
+ * Apply to sigma(S) for all embeddings sigma, then take the L_2 norm over
+ * sigma, then take the sup over i.
+ **/
+static GEN
+nf_Mignotte_bound(GEN nf, GEN polbase)
+{
+  GEN G = nf_get_G(nf), lS = leading_term(polbase); /* t_INT */
+  GEN p1, C, N2, matGS, binlS, bin;
+  long prec, i, j, d = degpol(polbase), n = nf_get_degree(nf), r1 = nf_get_r1(nf);
+
+  binlS = bin = vecbinome(d-1);
+  if (!isint1(lS)) binlS = ZC_Z_mul(bin,lS);
+
+  N2 = cgetg(n+1, t_VEC);
+  prec = gprecision(G);
+  for (;;)
+  {
+    nffp_t F;
+
+    matGS = cgetg(d+2, t_MAT);
+    for (j=0; j<=d; j++) gel(matGS,j+1) = arch_for_T2(G, gel(polbase,j+2));
+    matGS = shallowtrans(matGS);
+    for (j=1; j <= r1; j++) /* N2[j] = || sigma_j(S) ||_2 */
+    {
+      GEN c = sqrtr( RgC_fpnorml2(gel(matGS,j), DEFAULTPREC) );
+      gel(N2,j) = c; if (!signe(c)) goto PRECPB;
+    }
+    for (   ; j <= n; j+=2)
+    {
+      GEN q1 = RgC_fpnorml2(gel(matGS,j  ), DEFAULTPREC);
+      GEN q2 = RgC_fpnorml2(gel(matGS,j+1), DEFAULTPREC);
+      GEN c = sqrtr( gmul2n(addrr(q1, q2), -1) );
+      gel(N2,j) = gel(N2,j+1) = c; if (!signe(c)) goto PRECPB;
+    }
+    if (j > n) break; /* done */
+PRECPB:
+    prec = precdbl(prec);
+    remake_GM(nf, &F, prec); G = F.G;
+    if (DEBUGLEVEL>1) pari_warn(warnprec, "nf_factor_bound", prec);
+  }
+
+  /* Take sup over 0 <= i <= d of
+   * sum_j | binom(d-1, i-1) ||sigma_j(S)||_2 + binom(d-1,i) lc(S) |^2 */
+
+  /* i = 0: n lc(S)^2 */
+  C = mului(n, sqri(lS));
+  /* i = d: sum_sigma ||sigma(S)||_2^2 */
+  p1 = gnorml2(N2); if (gcmp(C, p1) < 0) C = p1;
+  for (i = 1; i < d; i++)
+  {
+    GEN B = gel(bin,i), L = gel(binlS,i+1);
+    GEN s = sqrr(addri(mulir(B, gel(N2,1)),  L)); /* j=1 */
+    for (j = 2; j <= n; j++) s = addrr(s, sqrr(addri(mulir(B, gel(N2,j)), L)));
+    if (mpcmp(C, s) < 0) C = s;
+  }
+  return C;
+}
+
+/* return a bound for T_2(P), P | polbase
+ * max |b_i|^2 <= 3^{3/2 + d} / (4 \pi d) [P]_2,
+ * where [P]_2 is Bombieri's 2-norm
+ * Sum over conjugates */
+static GEN
+nf_Beauzamy_bound(GEN nf, GEN polbase)
+{
+  GEN lt, C, s, G = nf_get_G(nf), POL, bin;
+  long d = degpol(polbase), n = nf_get_degree(nf), prec   = MEDDEFAULTPREC;
+  bin = vecbinome(d);
+  POL = polbase + 2;
+  /* compute [POL]_2 */
+  for (;;)
+  {
+    nffp_t F;
+    long i;
+
+    s = real_0(prec);
+    for (i=0; i<=d; i++)
+    {
+      GEN c = gel(POL,i);
+      if (gequal0(c)) continue;
+      c = gnorml2(arch_for_T2_prec(G, c, prec));
+      if (!signe(c)) goto PRECPB;
+      /* s += T2(POL[i]) / binomial(d,i) */
+      s = addrr(s, divri(c, gel(bin,i+1)));
+    }
+    break;
+
+PRECPB:
+    prec = precdbl(prec);
+    remake_GM(nf, &F, prec); G = F.G;
+    if (DEBUGLEVEL>1) pari_warn(warnprec, "nf_factor_bound", prec);
+  }
+  lt = leading_term(polbase);
+  s = mulri(s, muliu(sqri(lt), n));
+  C = powruhalf(stor(3,DEFAULTPREC), 3 + 2*d); /* 3^{3/2 + d} */
+  return divrr(mulrr(C, s), mulur(d, mppi(DEFAULTPREC)));
+}
+
+static GEN
+nf_factor_bound(GEN nf, GEN polbase)
+{
+  pari_sp av = avma;
+  GEN a = nf_Mignotte_bound(nf, polbase);
+  GEN b = nf_Beauzamy_bound(nf, polbase);
+  if (DEBUGLEVEL>2)
+  {
+    err_printf("Mignotte bound: %Ps\n",a);
+    err_printf("Beauzamy bound: %Ps\n",b);
+  }
+  return gerepileupto(av, gmin(a, b));
+}
+
+/* return Bs: if r a root of sigma_i(P), |r| < Bs[i] */
+static GEN
+nf_root_bounds(GEN P, GEN T)
+{
+  long lR, i, j, l, prec;
+  GEN Ps, R, V, nf;
+
+  if (RgX_is_rational(P)) return logmax_modulus_bound(P);
+  T = get_nfpol(T, &nf);
+
+  P = Q_primpart(P);
+  prec = ZXX_max_lg(P) + 1;
+  l = lg(P);
+  if (nf && nf_get_prec(nf) >= prec)
+    R = nf_get_roots(nf);
+  else
+    R = QX_complex_roots(T, prec);
+  lR = lg(R);
+  V = cgetg(lR, t_VEC);
+  Ps = cgetg(l, t_POL); /* sigma (P) */
+  Ps[1] = P[1];
+  for (j=1; j<lg(R); j++)
+  {
+    GEN r = gel(R,j);
+    for (i=2; i<l; i++) gel(Ps,i) = poleval(gel(P,i), r);
+    gel(V,j) = logmax_modulus_bound(Ps);
+  }
+  return V;
+}
+
+/* return B such that if x in O_K, K = Z[X]/(T), then the L2-norm of the
+ * coordinates of the numerator of x [on the power, resp. integral, basis if T
+ * is a polynomial, resp. an nf] is  <= B T_2(x)
+ * den = multiplicative bound for denom(x) */
+static GEN
+L2_bound(GEN nf, GEN den)
+{
+  GEN M, L, prep, T = nf_get_pol(nf), tozk = nf_get_invzk(nf);
+  long prec;
+
+  prec = nbits2prec(bit_accuracy(ZX_max_lg(T)) + bit_accuracy(ZM_max_lg(tozk)));
+  (void)initgaloisborne(nf, den, prec, &L, &prep, NULL);
+  M = vandermondeinverse(L, RgX_gtofp(T,prec), den, prep);
+  return RgM_fpnorml2(RgM_mul(tozk,M), DEFAULTPREC);
+}
+
+/* || L ||_p^p in dimension n (L may be a scalar) */
+static GEN
+normlp(GEN L, long p, long n)
+{
+  long i,l, t = typ(L);
+  GEN z;
+
+  if (!is_vec_t(t)) return gmulsg(n, gpowgs(L, p));
+
+  l = lg(L); z = gen_0;
+  /* assert(n == l-1); */
+  for (i=1; i<l; i++)
+    z = gadd(z, gpowgs(gel(L,i), p));
+  return z;
+}
+
+/* S = S0 + tS1, P = P0 + tP1 (Euclidean div. by t integer). For a true
+ * factor (vS, vP), we have:
+ *    | S vS + P vP |^2 < Btra
+ * This implies | S1 vS + P1 vP |^2 < Bhigh, assuming t > sqrt(Btra).
+ * d = dimension of low part (= [nf:Q])
+ * n0 = bound for |vS|^2
+ * */
+static double
+get_Bhigh(long n0, long d)
+{
+  double sqrtd = sqrt((double)d);
+  double z = n0*sqrtd + sqrtd/2 * (d * (n0+1));
+  z = 1. + 0.5 * z; return z * z;
+}
+
+typedef struct {
+  GEN d;
+  GEN dPinvS;   /* d P^(-1) S   [ integral ] */
+  double **PinvSdbl; /* P^(-1) S as double */
+  GEN S1, P1;   /* S = S0 + S1 q, idem P */
+} trace_data;
+
+/* S1 * u - P1 * round(P^-1 S u). K non-zero coords in u given by ind */
+static GEN
+get_trace(GEN ind, trace_data *T)
+{
+  long i, j, l, K = lg(ind)-1;
+  GEN z, s, v;
+
+  s = gel(T->S1, ind[1]);
+  if (K == 1) return s;
+
+  /* compute s = S1 u */
+  for (j=2; j<=K; j++) s = ZC_add(s, gel(T->S1, ind[j]));
+
+  /* compute v := - round(P^1 S u) */
+  l = lg(s);
+  v = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++)
+  {
+    double r, t = 0.;
+    /* quick approximate computation */
+    for (j=1; j<=K; j++) t += T->PinvSdbl[ ind[j] ][i];
+    r = floor(t + 0.5);
+    if (fabs(t + 0.5 - r) < 0.0001)
+    { /* dubious, compute exactly */
+      z = gen_0;
+      for (j=1; j<=K; j++) z = addii(z, ((GEN**)T->dPinvS)[ ind[j] ][i]);
+      v[i] = - itos( diviiround(z, T->d) );
+    }
+    else
+      v[i] = - (long)r;
+  }
+  return ZC_add(s, ZM_zc_mul(T->P1, v));
+}
+
+static trace_data *
+init_trace(trace_data *T, GEN S, nflift_t *L, GEN q)
+{
+  long e = gexpo(S), i,j, l,h;
+  GEN qgood, S1, invd;
+
+  if (e < 0) return NULL; /* S = 0 */
+
+  qgood = int2n(e - 32); /* single precision check */
+  if (cmpii(qgood, q) > 0) q = qgood;
+
+  S1 = gdivround(S, q);
+  if (gequal0(S1)) return NULL;
+
+  invd = invr(itor(L->den, DEFAULTPREC));
+
+  T->dPinvS = ZM_mul(L->iprk, S);
+  l = lg(S);
+  h = lgcols(T->dPinvS);
+  T->PinvSdbl = (double**)cgetg(l, t_MAT);
+  init_dalloc();
+  for (j = 1; j < l; j++)
+  {
+    double *t = dalloc(h * sizeof(double));
+    GEN c = gel(T->dPinvS,j);
+    pari_sp av = avma;
+    T->PinvSdbl[j] = t;
+    for (i=1; i < h; i++) t[i] = rtodbl(mulri(invd, gel(c,i)));
+    avma = av;
+  }
+
+  T->d  = L->den;
+  T->P1 = gdivround(L->prk, q);
+  T->S1 = S1; return T;
+}
+
+static void
+update_trace(trace_data *T, long k, long i)
+{
+  if (!T) return;
+  gel(T->S1,k)     = gel(T->S1,i);
+  gel(T->dPinvS,k) = gel(T->dPinvS,i);
+  T->PinvSdbl[k]   = T->PinvSdbl[i];
+}
+
+/* reduce coeffs mod (T,pk), then center mod pk */
+static GEN
+FqX_centermod(GEN z, GEN T, GEN pk, GEN pks2)
+{
+  long i, l;
+  GEN y;
+  if (!T) return centermod_i(z, pk, pks2);
+  y = FpXQX_red(z, T, pk); l = lg(y);
+  for (i = 2; i < l; i++)
+  {
+    GEN c = gel(y,i);
+    if (typ(c) == t_INT)
+      c = centermodii(c, pk, pks2);
+    else
+      c = FpX_center(c, pk, pks2);
+    gel(y,i) = c;
+  }
+  return y;
+}
+
+typedef struct {
+  GEN lt, C, Clt, C2lt, C2ltpol;
+} div_data;
+
+static void
+init_div_data(div_data *D, GEN pol, nflift_t *L)
+{
+  GEN C = mul_content(L->topowden, L->dn);
+  GEN C2lt, Clt, lc = leading_term(pol), lt = is_pm1(lc)? NULL: absi(lc);
+  if (C)
+  {
+    GEN C2 = sqri(C);
+    if (lt) {
+      C2lt = mulii(C2, lt);
+      Clt = mulii(C,lt);
+    } else {
+      C2lt = C2;
+      Clt = C;
+    }
+  }
+  else
+    C2lt = Clt = lt;
+  D->lt = lt;
+  D->C = C;
+  D->Clt = Clt;
+  D->C2lt = C2lt;
+  D->C2ltpol = C2lt? RgX_Rg_mul(pol, C2lt): pol;
+}
+static void
+update_target(div_data *D, GEN pol)
+{ D->C2ltpol = D->Clt? RgX_Rg_mul(pol, D->Clt): pol; }
+
+/* nb = number of modular factors; return a "good" K such that naive
+ * recombination of up to maxK modular factors is not too costly */
+long
+cmbf_maxK(long nb)
+{
+  if (nb >  10) return 3;
+  return nb-1;
+}
+/* Naive recombination of modular factors: combine up to maxK modular
+ * factors, degree <= klim
+ *
+ * target = polynomial we want to factor
+ * famod = array of modular factors.  Product should be congruent to
+ * target/lc(target) modulo p^a
+ * For true factors: S1,S2 <= p^b, with b <= a and p^(b-a) < 2^31 */
+/* set *done = 1 if factorisation is known to be complete */
+static GEN
+nfcmbf(nfcmbf_t *T, long klim, long *pmaxK, int *done)
+{
+  GEN nf = T->nf, famod = T->fact, bound = T->bound;
+  GEN ltdn, nfpol = nf_get_pol(nf);
+  long K = 1, cnt = 1, i,j,k, curdeg, lfamod = lg(famod)-1, dnf = degpol(nfpol);
+  pari_sp av0 = avma;
+  GEN Tpk = T->L->Tpk, pk = T->L->pk, pks2 = shifti(pk,-1);
+  GEN ind      = cgetg(lfamod+1, t_VECSMALL);
+  GEN deg      = cgetg(lfamod+1, t_VECSMALL);
+  GEN degsofar = cgetg(lfamod+1, t_VECSMALL);
+  GEN fa       = cgetg(lfamod+1, t_VEC);
+  const double Bhigh = get_Bhigh(lfamod, dnf);
+  trace_data _T1, _T2, *T1, *T2;
+  div_data D;
+  pari_timer ti;
+
+  timer_start(&ti);
+
+  *pmaxK = cmbf_maxK(lfamod);
+  init_div_data(&D, T->pol, T->L);
+  ltdn = mul_content(D.lt, T->L->dn);
+  {
+    GEN q = ceil_safe(sqrtr(T->BS_2));
+    GEN t1,t2, lt2dn = mul_content(ltdn, D.lt);
+    GEN trace1   = cgetg(lfamod+1, t_MAT);
+    GEN trace2   = cgetg(lfamod+1, t_MAT);
+    for (i=1; i <= lfamod; i++)
+    {
+      pari_sp av = avma;
+      GEN P = gel(famod,i);
+      long d = degpol(P);
+
+      deg[i] = d; P += 2;
+      t1 = gel(P,d-1);/* = - S_1 */
+      t2 = Fq_sqr(t1, Tpk, pk);
+      if (d > 1) t2 = Fq_sub(t2, gmul2n(gel(P,d-2), 1), Tpk, pk);
+      /* t2 = S_2 Newton sum */
+      if (ltdn)
+      {
+        t1 = Fq_Fp_mul(t1, ltdn, Tpk, pk);
+        t2 = Fq_Fp_mul(t2, lt2dn, Tpk, pk);
+      }
+      gel(trace1,i) = gclone( nf_bestlift(t1, NULL, T->L) );
+      gel(trace2,i) = gclone( nf_bestlift(t2, NULL, T->L) ); avma = av;
+    }
+    T1 = init_trace(&_T1, trace1, T->L, q);
+    T2 = init_trace(&_T2, trace2, T->L, q);
+    for (i=1; i <= lfamod; i++) {
+      gunclone(gel(trace1,i));
+      gunclone(gel(trace2,i));
+    }
+  }
+  degsofar[0] = 0; /* sentinel */
+
+  /* ind runs through strictly increasing sequences of length K,
+   * 1 <= ind[i] <= lfamod */
+nextK:
+  if (K > *pmaxK || 2*K > lfamod) goto END;
+  if (DEBUGLEVEL > 3)
+    err_printf("\n### K = %d, %Ps combinations\n", K,binomial(utoipos(lfamod), K));
+  setlg(ind, K+1); ind[1] = 1;
+  i = 1; curdeg = deg[ind[1]];
+  for(;;)
+  { /* try all combinations of K factors */
+    for (j = i; j < K; j++)
+    {
+      degsofar[j] = curdeg;
+      ind[j+1] = ind[j]+1; curdeg += deg[ind[j+1]];
+    }
+    if (curdeg <= klim) /* trial divide */
+    {
+      GEN t, y, q;
+      pari_sp av;
+
+      av = avma;
+      if (T1)
+      { /* d-1 test */
+        t = get_trace(ind, T1);
+        if (rtodbl(RgC_fpnorml2(t,DEFAULTPREC)) > Bhigh)
+        {
+          if (DEBUGLEVEL>6) err_printf(".");
+          avma = av; goto NEXT;
+        }
+      }
+      if (T2)
+      { /* d-2 test */
+        t = get_trace(ind, T2);
+        if (rtodbl(RgC_fpnorml2(t,DEFAULTPREC)) > Bhigh)
+        {
+          if (DEBUGLEVEL>3) err_printf("|");
+          avma = av; goto NEXT;
+        }
+      }
+      avma = av;
+      y = ltdn; /* full computation */
+      for (i=1; i<=K; i++)
+      {
+        GEN q = gel(famod, ind[i]);
+        if (y) q = gmul(y, q);
+        y = FqX_centermod(q, Tpk, pk, pks2);
+      }
+      y = nf_pol_lift(y, bound, T);
+      if (!y)
+      {
+        if (DEBUGLEVEL>3) err_printf("@");
+        avma = av; goto NEXT;
+      }
+      /* y = topowden*dn*lt*\prod_{i in ind} famod[i] is apparently in O_K[X],
+       * in fact in (Z[Y]/nf.pol)[X] due to multiplication by C = topowden*dn.
+       * Try out this candidate factor */
+      q = RgXQX_divrem(D.C2ltpol, y, nfpol, ONLY_DIVIDES);
+      if (!q)
+      {
+        if (DEBUGLEVEL>3) err_printf("*");
+        avma = av; goto NEXT;
+      }
+      /* Original T->pol in O_K[X] with leading coeff lt in Z,
+       * y = C*lt \prod famod[i] is in O_K[X] with leading coeff in Z
+       * q = C^2*lt*pol / y = C * (lt*pol) / (lt*\prod famod[i]) is a
+       * K-rational factor, in fact in Z[Y]/nf.pol)[X] as above, with
+       * leading term C*lt. */
+      update_target(&D, q);
+      gel(fa,cnt++) = D.C2lt? RgX_int_normalize(y): y; /* make monic */
+      for (i=j=k=1; i <= lfamod; i++)
+      { /* remove used factors */
+        if (j <= K && i == ind[j]) j++;
+        else
+        {
+          gel(famod,k) = gel(famod,i);
+          update_trace(T1, k, i);
+          update_trace(T2, k, i);
+          deg[k] = deg[i]; k++;
+        }
+      }
+      lfamod -= K;
+      *pmaxK = cmbf_maxK(lfamod);
+      if (lfamod < 2*K) goto END;
+      i = 1; curdeg = deg[ind[1]];
+      if (DEBUGLEVEL > 2)
+      {
+        err_printf("\n"); timer_printf(&ti, "to find factor %Ps",y);
+        err_printf("remaining modular factor(s): %ld\n", lfamod);
+      }
+      continue;
+    }
+
+NEXT:
+    for (i = K+1;;)
+    {
+      if (--i == 0) { K++; goto nextK; }
+      if (++ind[i] <= lfamod - K + i)
+      {
+        curdeg = degsofar[i-1] + deg[ind[i]];
+        if (curdeg <= klim) break;
+      }
+    }
+  }
+END:
+  *done = 1;
+  if (degpol(D.C2ltpol) > 0)
+  { /* leftover factor */
+    GEN q = D.C2ltpol;
+    if (D.C2lt) q = RgX_int_normalize(q);
+    if (lfamod >= 2*K)
+    { /* restore leading coefficient [#930] */
+      if (D.lt) q = RgX_Rg_mul(q, D.lt);
+      *done = 0; /* ... may still be reducible */
+    }
+    setlg(famod, lfamod+1);
+    gel(fa,cnt++) = q;
+  }
+  if (DEBUGLEVEL>6) err_printf("\n");
+  if (cnt == 2) {
+    avma = av0;
+    return mkvec(T->pol);
+  }
+  else
+  {
+    setlg(fa, cnt);
+    return gerepilecopy(av0, fa);
+  }
+}
+
+static GEN
+nf_chk_factors(nfcmbf_t *T, GEN P, GEN M_L, GEN famod, GEN pk)
+{
+  GEN nf = T->nf, bound = T->bound;
+  GEN nfT = nf_get_pol(nf);
+  long i, r;
+  GEN pol = P, list, piv, y;
+  GEN Tpk = T->L->Tpk;
+  div_data D;
+
+  piv = special_pivot(M_L);
+  if (!piv) return NULL;
+  if (DEBUGLEVEL>3) err_printf("special_pivot output:\n%Ps\n",piv);
+
+  r  = lg(piv)-1;
+  list = cgetg(r+1, t_VEC);
+  init_div_data(&D, pol, T->L);
+  for (i = 1;;)
+  {
+    pari_sp av = avma;
+    if (DEBUGLEVEL) err_printf("nf_LLL_cmbf: checking factor %ld\n", i);
+    y = chk_factors_get(D.lt, famod, gel(piv,i), Tpk, pk);
+
+    if (! (y = nf_pol_lift(y, bound, T)) ) return NULL;
+    y = gerepilecopy(av, y);
+    /* y is the candidate factor */
+    pol = RgXQX_divrem(D.C2ltpol, y, nfT, ONLY_DIVIDES);
+    if (!pol) return NULL;
+
+    if (D.C2lt) y = RgX_int_normalize(y);
+    gel(list,i) = y;
+    if (++i >= r) break;
+
+    update_target(&D, pol);
+  }
+  gel(list,i) = RgX_int_normalize(pol); return list;
+}
+
+static GEN
+nf_to_Zq(GEN x, GEN T, GEN pk, GEN pks2, GEN proj)
+{
+  GEN y;
+  if (typ(x) != t_COL) return centermodii(x, pk, pks2);
+  if (!T)
+  {
+    y = ZV_dotproduct(proj, x);
+    return centermodii(y, pk, pks2);
+  }
+  y = ZM_ZC_mul(proj, x);
+  y = RgV_to_RgX(y, varn(T));
+  return FpX_center(FpX_rem(y, T, pk), pk, pks2);
+}
+
+/* Assume P in nfX form, lc(P) != 0 mod p. Reduce P to Zp[X]/(T) mod p^a */
+static GEN
+ZqX(GEN P, GEN pk, GEN T, GEN proj)
+{
+  long i, l = lg(P);
+  GEN z, pks2 = shifti(pk,-1);
+
+  z = cgetg(l,t_POL); z[1] = P[1];
+  for (i=2; i<l; i++) gel(z,i) = nf_to_Zq(gel(P,i),T,pk,pks2,proj);
+  return normalizepol_lg(z, l);
+}
+
+static GEN
+ZqX_normalize(GEN P, GEN lt, nflift_t *L)
+{
+  GEN R = lt? RgX_Rg_mul(P, Fp_inv(lt, L->pk)): P;
+  return ZqX(R, L->pk, L->Tpk, L->ZqProj);
+}
+
+/* k allowing to reconstruct x, |x|^2 < C, from x mod pr^k */
+/* return log [  2sqrt(C/d) * ( (3/2)sqrt(gamma) )^(d-1) ] ^d / log N(pr)
+ * cf. Belabas relative van Hoeij algorithm, lemma 3.12 */
+static double
+bestlift_bound(GEN C, long d, double alpha, GEN Npr)
+{
+  const double y = 1 / (alpha - 0.25); /* = 2 if alpha = 3/4 */
+  double t;
+  C = gtofp(C,DEFAULTPREC);
+  /* (1/2)log (4C/d) + (d-1)(log 3/2 sqrt(gamma)) */
+  t = rtodbl(mplog(gmul2n(divru(C,d), 2))) * 0.5 + (d-1) * log(1.5 * sqrt(y));
+  return ceil((t * d) / log(gtodouble(Npr)));
+}
+
+static GEN
+get_R(GEN M)
+{
+  GEN R;
+  long i, l, prec = nbits2prec( gexpo(M) + 64 );
+
+  for(;;)
+  {
+    R = gaussred_from_QR(M, prec);
+    if (R) break;
+    prec = precdbl(prec);
+  }
+  l = lg(R);
+  for (i=1; i<l; i++) gcoeff(R,i,i) = gen_1;
+  return R;
+}
+
+static void
+init_proj(nflift_t *L, GEN nfT)
+{
+  if (L->Tp)
+  {
+    GEN coTp = FpX_div(FpX_red(nfT, L->p), L->Tp,  L->p); /* Tp's cofactor */
+    GEN z, proj;
+    z = ZpX_liftfact(nfT, mkvec2(L->Tp, coTp), NULL,  L->p, L->k, L->pk);
+    L->Tpk = gel(z,1);
+    proj = get_proj_modT(L->topow, L->Tpk, L->pk);
+    if (L->topowden)
+      proj = FpM_red(ZM_Z_mul(proj, Fp_inv(L->topowden, L->pk)), L->pk);
+    L->ZqProj = proj;
+  }
+  else
+  {
+    L->Tpk = NULL;
+    L->ZqProj = dim1proj(L->prkHNF);
+  }
+}
+
+/* Square of the radius of largest ball inscript in PRK's fundamental domain,
+ *   whose orthogonalized vector's norms are the Bi
+ * Rmax ^2 =  min 1/4T_i where T_i = sum ( s_ij^2 / B_j) */
+static GEN
+max_radius(GEN PRK, GEN B)
+{
+  GEN S, smax = gen_0;
+  pari_sp av = avma;
+  long i, j, d = lg(PRK)-1;
+
+  S = RgM_inv( get_R(PRK) ); if (!S) pari_err_PREC("max_radius");
+  for (i=1; i<=d; i++)
+  {
+    GEN s = gen_0;
+    for (j=1; j<=d; j++)
+      s = mpadd(s, mpdiv( mpsqr(gcoeff(S,i,j)), gel(B,j)));
+    if (mpcmp(s, smax) > 0) smax = s;
+  }
+  return gerepileupto(av, ginv(gmul2n(smax, 2)));
+}
+
+static void
+bestlift_init(long a, GEN nf, GEN pr, GEN C, nflift_t *L)
+{
+  const double alpha = 0.99; /* LLL parameter */
+  const long d = nf_get_degree(nf);
+  pari_sp av = avma, av2;
+  GEN prk, PRK, B, GSmin, pk;
+  pari_timer ti;
+
+  timer_start(&ti);
+  if (!a) a = (long)bestlift_bound(C, d, alpha, pr_norm(pr));
+
+  for (;; avma = av, a += (a==1)? 1: (a>>1)) /* roughly a *= 1.5 */
+  {
+    if (DEBUGLEVEL>2) err_printf("exponent %ld\n",a);
+    prk = idealpows(nf, pr, a);
+    av2 = avma;
+    pk = gcoeff(prk,1,1);
+    PRK = ZM_lll_norms(prk, alpha, LLL_INPLACE, &B);
+    GSmin = max_radius(PRK, B);
+    if (gcmp(GSmin, C) >= 0) break;
+  }
+  gerepileall(av2, 2, &PRK, &GSmin);
+  if (DEBUGLEVEL>2)
+    err_printf("for this exponent, GSmin = %Ps\nTime reduction: %ld\n",
+      GSmin, timer_delay(&ti));
+  L->k = a;
+  L->den = L->pk = pk;
+  L->prk = PRK;
+  L->iprk = ZM_inv(PRK, pk);
+  L->GSmin= GSmin;
+  L->prkHNF = prk;
+  init_proj(L, nf_get_pol(nf));
+}
+
+/* Let X = Tra * M_L, Y = bestlift(X) return V s.t Y = X - PRK V
+ * and set *eT2 = gexpo(Y)  [cf nf_bestlift, but memory efficient] */
+static GEN
+get_V(GEN Tra, GEN M_L, GEN PRK, GEN PRKinv, GEN pk, long *eT2)
+{
+  long i, e = 0, l = lg(M_L);
+  GEN V = cgetg(l, t_MAT);
+  *eT2 = 0;
+  for (i = 1; i < l; i++)
+  { /* cf nf_bestlift(Tra * c) */
+    pari_sp av = avma, av2;
+    GEN v, T2 = ZM_ZC_mul(Tra, gel(M_L,i));
+
+    v = gdivround(ZM_ZC_mul(PRKinv, T2), pk); /* small */
+    av2 = avma;
+    T2 = ZC_sub(T2, ZM_ZC_mul(PRK, v));
+    e = gexpo(T2); if (e > *eT2) *eT2 = e;
+    avma = av2;
+    gel(V,i) = gerepileupto(av, v); /* small */
+  }
+  return V;
+}
+
+static GEN
+nf_LLL_cmbf(nfcmbf_t *T, long rec)
+{
+  const double BitPerFactor = 0.4; /* nb bits / modular factor */
+  nflift_t *L = T->L;
+  GEN famod = T->fact, ZC = T->ZC, Br = T->Br, P = T->pol, dn = T->L->dn;
+  long dnf = nf_get_degree(T->nf), dP = degpol(P);
+  long i, C, tmax, n0;
+  GEN lP, Bnorm, Tra, T2, TT, CM_L, m, list, ZERO, Btra;
+  double Bhigh;
+  pari_sp av, av2, lim;
+  long ti_LLL = 0, ti_CF = 0;
+  pari_timer ti2, TI;
+
+  lP = absi(leading_term(P));
+  if (is_pm1(lP)) lP = NULL;
+
+  n0 = lg(famod) - 1;
+ /* Lattice: (S PRK), small vector (vS vP). To find k bound for the image,
+  * write S = S1 q + S0, P = P1 q + P0
+  * |S1 vS + P1 vP|^2 <= Bhigh for all (vS,vP) assoc. to true factors */
+  Btra = mulrr(ZC, mulur(dP*dP, normlp(Br, 2, dnf)));
+  Bhigh = get_Bhigh(n0, dnf);
+  C = (long)ceil(sqrt(Bhigh/n0)) + 1; /* C^2 n0 ~ Bhigh */
+  Bnorm = dbltor( n0 * C * C + Bhigh );
+  ZERO = zeromat(n0, dnf);
+
+  av = avma; lim = stack_lim(av, 1);
+  TT = cgetg(n0+1, t_VEC);
+  Tra  = cgetg(n0+1, t_MAT);
+  for (i=1; i<=n0; i++) TT[i] = 0;
+  CM_L = scalarmat_s(C, n0);
+  /* tmax = current number of traces used (and computed so far) */
+  for(tmax = 0;; tmax++)
+  {
+    long a, b, bmin, bgood, delta, tnew = tmax + 1, r = lg(CM_L)-1;
+    GEN M_L, q, CM_Lp, oldCM_L, S1, P1, VV;
+    int first = 1;
+
+    /* bound for f . S_k(genuine factor) = ZC * bound for T_2(S_tnew) */
+    Btra = mulrr(ZC, mulur(dP*dP, normlp(Br, 2*tnew, dnf)));
+    bmin = logint(ceil_safe(sqrtr(Btra)), gen_2, NULL);
+    if (DEBUGLEVEL>2)
+      err_printf("\nLLL_cmbf: %ld potential factors (tmax = %ld, bmin = %ld)\n",
+                 r, tmax, bmin);
+
+    /* compute Newton sums (possibly relifting first) */
+    if (gcmp(L->GSmin, Btra) < 0)
+    {
+      GEN polred;
+
+      bestlift_init((L->k)<<1, T->nf, T->pr, Btra, L);
+      polred = ZqX_normalize(T->polbase, lP, L);
+      famod = ZpX_liftfact(polred, famod, L->Tpk, L->p, L->k, L->pk);
+      for (i=1; i<=n0; i++) TT[i] = 0;
+    }
+    for (i=1; i<=n0; i++)
+    {
+      GEN h, lPpow = lP? powiu(lP, tnew): NULL;
+      GEN z = polsym_gen(gel(famod,i), gel(TT,i), tnew, L->Tpk, L->pk);
+      gel(TT,i) = z;
+      h = gel(z,tnew+1);
+      /* make Newton sums integral */
+      lPpow = mul_content(lPpow, dn);
+      if (lPpow)
+        h = (typ(h) == t_INT)? Fp_mul(h, lPpow, L->pk): FpX_Fp_mul(h, lPpow, L->pk);
+      gel(Tra,i) = nf_bestlift(h, NULL, L); /* S_tnew(famod) */
+    }
+
+    /* compute truncation parameter */
+    if (DEBUGLEVEL>2) { timer_start(&ti2); timer_start(&TI); }
+    oldCM_L = CM_L;
+    av2 = avma;
+    b = delta = 0; /* -Wall */
+AGAIN:
+    M_L = Q_div_to_int(CM_L, utoipos(C));
+    VV = get_V(Tra, M_L, L->prk, L->iprk, L->pk, &a);
+    if (first)
+    { /* initialize lattice, using few p-adic digits for traces */
+      bgood = (long)(a - maxss(32, (long)(BitPerFactor * r)));
+      b = maxss(bmin, bgood);
+      delta = a - b;
+    }
+    else
+    { /* add more p-adic digits and continue reduction */
+      if (a < b) b = a;
+      b = maxss(b-delta, bmin);
+      if (b - delta/2 < bmin) b = bmin; /* near there. Go all the way */
+    }
+
+    /* restart with truncated entries */
+    q = int2n(b);
+    P1 = gdivround(L->prk, q);
+    S1 = gdivround(Tra, q);
+    T2 = ZM_sub(ZM_mul(S1, M_L), ZM_mul(P1, VV));
+    m = vconcat( CM_L, T2 );
+    if (first)
+    {
+      first = 0;
+      m = shallowconcat( m, vconcat(ZERO, P1) );
+      /*     [ C M_L   0  ]
+       * m = [            ]   square matrix
+       *     [  T2'   PRK ]   T2' = Tra * M_L  truncated
+       */
+    }
+    CM_L = LLL_check_progress(Bnorm, n0, m, b == bmin, /*dbg:*/ &ti_LLL);
+    if (DEBUGLEVEL>2)
+      err_printf("LLL_cmbf: (a,b) =%4ld,%4ld; r =%3ld -->%3ld, time = %ld\n",
+                 a,b, lg(m)-1, CM_L? lg(CM_L)-1: 1, timer_delay(&TI));
+    if (!CM_L) { list = mkcol(RgX_int_normalize(P)); break; }
+    if (b > bmin)
+    {
+      CM_L = gerepilecopy(av2, CM_L);
+      goto AGAIN;
+    }
+    if (DEBUGLEVEL>2) timer_printf(&ti2, "for this trace");
+
+    i = lg(CM_L) - 1;
+    if (i == r && ZM_equal(CM_L, oldCM_L))
+    {
+      CM_L = oldCM_L;
+      avma = av2; continue;
+    }
+
+    CM_Lp = FpM_image(CM_L, utoipos(27449)); /* inexpensive test */
+    if (lg(CM_Lp) != lg(CM_L))
+    {
+      if (DEBUGLEVEL>2) err_printf("LLL_cmbf: rank decrease\n");
+      CM_L = ZM_hnf(CM_L);
+    }
+
+    if (i <= r && i*rec < n0)
+    {
+      pari_timer ti;
+      if (DEBUGLEVEL>2) timer_start(&ti);
+      list = nf_chk_factors(T, P, Q_div_to_int(CM_L,utoipos(C)), famod, L->pk);
+      if (DEBUGLEVEL>2) ti_CF += timer_delay(&ti);
+      if (list) break;
+    }
+    CM_L = gerepilecopy(av2, CM_L);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"nf_LLL_cmbf");
+      gerepileall(av, L->Tpk? 9: 8,
+                      &CM_L,&TT,&Tra,&famod,&L->pk,&L->GSmin,&L->prk,&L->iprk,&L->Tpk);
+    }
+  }
+  if (DEBUGLEVEL>2)
+    err_printf("* Time LLL: %ld\n* Time Check Factor: %ld\n",ti_LLL,ti_CF);
+  return list;
+}
+
+static GEN
+nf_combine_factors(nfcmbf_t *T, GEN polred, long klim)
+{
+  nflift_t *L = T->L;
+  GEN res;
+  long maxK;
+  int done;
+  pari_timer ti;
+
+  if (DEBUGLEVEL>2) timer_start(&ti);
+  T->fact = ZpX_liftfact(polred, T->fact, L->Tpk, L->p, L->k, L->pk);
+  if (DEBUGLEVEL>2) timer_printf(&ti, "Hensel lift");
+  res = nfcmbf(T, klim, &maxK, &done);
+  if (DEBUGLEVEL>2) timer_printf(&ti, "Naive recombination");
+  if (!done)
+  {
+    long l = lg(res)-1;
+    GEN v;
+    if (l > 1)
+    {
+      T->pol = gel(res,l);
+      T->polbase = RgX_to_nfX(T->nf, T->pol);
+    }
+    v = nf_LLL_cmbf(T, maxK);
+    /* remove last elt, possibly unfactored. Add all new ones. */
+    setlg(res, l); res = shallowconcat(res, v);
+  }
+  return res;
+}
+
+static GEN
+nf_DDF_roots(GEN pol, GEN polred, GEN nfpol, GEN init_fa, long nbf,
+              long fl, nflift_t *L)
+{
+  GEN z, Cltx_r, ltdn;
+  long i, m;
+  div_data D;
+
+  init_div_data(&D, pol, L);
+  ltdn = mul_content(D.lt, L->dn);
+  if (L->Tpk)
+  {
+    int cof = (degpol(pol) > nbf); /* non trivial cofactor ? */
+    z = FqX_split_roots(init_fa, L->Tp, L->p, cof? polred: NULL);
+    z = ZpX_liftfact(polred, z, L->Tpk, L->p, L->k, L->pk);
+    if (cof) setlg(z, lg(z)-1); /* remove cofactor */
+    z = roots_from_deg1(z);
+  }
+  else
+    z = rootpadicfast(polred, L->p, L->k);
+  Cltx_r = deg1pol_shallow(D.Clt? D.Clt: gen_1, NULL, varn(pol));
+  for (m=1,i=1; i<lg(z); i++)
+  {
+    GEN q, r = gel(z,i);
+    pari_sp av;
+    /* lt*dn*topowden * r = Clt * r */
+    r = nf_bestlift_to_pol(ltdn? gmul(ltdn,r): r, NULL, L);
+    av = avma;
+    gel(Cltx_r,2) = gneg(r); /* check P(r) == 0 */
+    q = RgXQX_divrem(D.C2ltpol, Cltx_r, nfpol, ONLY_DIVIDES); /* integral */
+    avma = av;
+    /* don't go on with q, usually much larger that C2ltpol */
+    if (q) {
+      if (D.Clt) r = gdiv(r, D.Clt);
+      gel(z,m++) = r;
+    }
+    else if (fl == ROOTS_SPLIT) return cgetg(1, t_VEC);
+  }
+  z[0] = evaltyp(t_VEC) | evallg(m);
+  return z;
+}
+
+/* returns a factor of T in Fp of degree <= maxf, NULL if none exist */
+static GEN
+get_good_factor(GEN T, ulong p, long maxf)
+{
+  pari_sp av = avma;
+  GEN r, list = gel(Flx_factor(ZX_to_Flx(T,p),p), 1);
+  if (maxf == 1)
+  { /* deg.1 factors are best */
+    r = gel(list,1);
+    if (degpol(r) == 1) return r;
+  }
+  else
+  { /* otherwise, pick factor of largish degree */
+    long i, dr;
+    for (i = lg(list)-1; i > 0; i--)
+    {
+      r = gel(list,i); dr = degpol(r);
+      if (dr <= maxf) return r;
+    }
+  }
+  avma = av; return NULL; /* failure */
+}
+
+/* Optimization problem: factorization of polynomials over large Fq is slow,
+ * BUT bestlift correspondingly faster.
+ * Return maximal residue degree to be considered when picking a prime ideal */
+static long
+get_maxf(long nfdeg)
+{
+  long maxf = 1;
+  if      (nfdeg >= 45) maxf =16;
+  else if (nfdeg >= 30) maxf = 8;
+  else if (nfdeg >= 15) maxf = 4;
+  return maxf;
+}
+
+/* Select a prime ideal pr over which to factor polbase.
+ * Return the number of factors (or roots, according to flag fl) mod pr,
+ * Input:
+ *   ct: number of attempts to find best
+ * Set:
+ *   lt: leading term of polbase (t_INT or NULL [ for 1 ])
+ *   pr: a suitable maximal ideal
+ *   Fa: factors found mod pr
+ *   Tp: polynomial defining Fq/Fp */
+static long
+nf_pick_prime(long ct, GEN nf, GEN polbase, long fl,
+              GEN *lt, GEN *Fa, GEN *pr, GEN *Tp)
+{
+  GEN nfpol = nf_get_pol(nf), bad = mulii(nf_get_disc(nf), nf_get_index(nf));
+  long maxf, nfdeg = degpol(nfpol), dpol = degpol(polbase), nbf = 0;
+  ulong pp;
+  forprime_t S;
+  pari_timer ti_pr;
+
+  if (DEBUGLEVEL>3) timer_start(&ti_pr);
+  *lt  = leading_term(polbase); /* t_INT */
+  if (gequal1(*lt)) *lt = NULL;
+  *pr = NULL;
+  *Fa = NULL;
+  *Tp = NULL;
+
+  maxf = get_maxf(nfdeg);
+  (void)u_forprime_init(&S, 2, ULONG_MAX);
+  /* select pr such that pol has the smallest number of factors, ct attempts */
+  while ((pp = u_forprime_next(&S)))
+  {
+    GEN aT, apr, ap, amodpr, red, r, fa = NULL;
+    long anbf;
+    ulong ltp = 0;
+    pari_sp av2 = avma;
+
+    /* first step : select prime of high inertia degree */
+    if (! umodiu(bad,pp)) continue;
+    if (*lt) { ltp = umodiu(*lt, pp); if (!ltp) continue; }
+    r = get_good_factor(nfpol, pp, maxf);
+    if (!r) continue;
+
+    ap = utoipos(pp);
+    apr = primedec_apply_kummer(nf, Flx_to_ZX(r), 1, ap);
+    amodpr = zk_to_Fq_init(nf,&apr,&aT,&ap);
+
+    /* second step : evaluate factorisation mod apr */
+    red = nfX_to_FqX(polbase, nf, amodpr);
+    if (!aT)
+    { /* degree 1 */
+      red = ZX_to_Flx(red, pp);
+      if (ltp) red = Flx_normalize(red, pp);
+      if (!Flx_is_squarefree(red, pp)) { avma = av2; continue; }
+      anbf = fl == FACTORS? Flx_nbfact(red, pp): Flx_nbroots(red, pp);
+    }
+    else
+    {
+      if (ltp) red = FqX_normalize(red, aT,ap);
+      if (!FqX_is_squarefree(red,aT,ap)) { avma = av2; continue; }
+      anbf = fl == FACTORS? FqX_split_by_degree(&fa, red, aT, ap)
+                          : FqX_split_deg1(&fa, red, aT, ap);
+    }
+    if (fl == ROOTS_SPLIT && anbf < dpol) return anbf;
+    if (anbf <= 1)
+    {
+      if (fl == FACTORS) return anbf; /* irreducible */
+      if (!anbf) return 0; /* no root */
+    }
+
+    if (!nbf || anbf < nbf
+             || (anbf == nbf && pr_get_f(apr) > pr_get_f(*pr)))
+    {
+      nbf = anbf;
+      *pr = apr;
+      *Tp = aT;
+      *Fa = fa;
+    }
+    else avma = av2;
+    if (DEBUGLEVEL>3)
+      err_printf("%3ld %s at prime\n  %Ps\nTime: %ld\n",
+                 anbf, fl == FACTORS?"factors": "roots", apr, timer_delay(&ti_pr));
+    if (--ct <= 0) break;
+  }
+  if (!nbf) pari_err_OVERFLOW("nf_pick_prime [ran out of primes]");
+  return nbf;
+}
+
+/* assume lt(T) is a t_INT and T square free */
+static GEN
+nfsqff_trager(GEN u, GEN T, GEN dent)
+{
+  long k = 0, i, lx;
+  GEN U, P, x0, mx0, fa, n = ZX_ZXY_rnfequation(T, u, &k);
+  int tmonic;
+  if (DEBUGLEVEL>4) err_printf("nfsqff_trager: choosing k = %ld\n",k);
+
+  /* n guaranteed to be squarefree */
+  fa = ZX_DDF(Q_primpart(n)); lx = lg(fa);
+  if (lx == 2) return mkcol(u);
+
+  tmonic = is_pm1(leading_term(T));
+  P = cgetg(lx,t_COL);
+  x0 = deg1pol_shallow(stoi(-k), gen_0, varn(T));
+  mx0 = deg1pol_shallow(stoi(k), gen_0, varn(T));
+  U = RgXQX_translate(u, mx0, T);
+  if (!tmonic) U = Q_primpart(U);
+  for (i=lx-1; i>0; i--)
+  {
+    GEN f = gel(fa,i), F = nfgcd(U, f, T, dent);
+    F = RgXQX_translate(F, x0, T);
+    /* F = gcd(f, u(t - x0)) [t + x0] = gcd(f(t + x0), u), more efficient */
+    if (typ(F) != t_POL || degpol(F) == 0)
+      pari_err_IRREDPOL("factornf [modulus]",T);
+    gel(P,i) = QXQX_normalize(F, T);
+  }
+  return P;
+}
+
+/* Factor polynomial a on the number field defined by polynomial T, using
+ * Trager's trick */
+GEN
+polfnf(GEN a, GEN T)
+{
+  GEN rep = cgetg(3, t_MAT), A, B, y, dent, bad;
+  long dA;
+  int tmonic;
+
+  if (typ(a)!=t_POL) pari_err_TYPE("polfnf",a);
+  if (typ(T)!=t_POL) pari_err_TYPE("polfnf",T);
+  T = Q_primpart(T); tmonic = is_pm1(leading_term(T));
+  RgX_check_ZX(T,"polfnf");
+  A = Q_primpart( QXQX_normalize(RgX_nffix("polfnf",T,a,1), T) );
+  dA = degpol(A);
+  if (dA <= 0)
+  {
+    avma = (pari_sp)(rep + 3);
+    return (dA == 0)? trivial_fact(): zerofact(varn(A));
+  }
+  bad = dent = ZX_disc(T);
+  if (tmonic) dent = indexpartial(T, dent);
+  (void)nfgcd_all(A,RgX_deriv(A), T, dent, &B);
+  if (degpol(B) != dA) B = Q_primpart( QXQX_normalize(B, T) );
+  ensure_lt_INT(B);
+  y = nfsqff_trager(B, T, dent);
+  fact_from_sqff(rep, A, B, y, T, bad);
+  return sort_factor_pol(rep, cmp_RgX);
+}
+
+static int
+nfsqff_use_Trager(long n, long dpol)
+{
+  return dpol*3<n;
+}
+
+/* return the factorization of the square-free polynomial pol. Not memory-clean
+   The coeffs of pol are in Z_nf and its leading term is a rational integer.
+   deg(pol) > 0, deg(nfpol) > 1
+   fl is either FACTORS (return factors), or ROOTS / ROOTS_SPLIT (return roots):
+     - ROOTS, return only the roots of x in nf
+     - ROOTS_SPLIT, as ROOTS if pol splits, [] otherwise
+   den is usually 1, otherwise nf.zk is doubtful, and den bounds the
+   denominator of an arbitrary element of Z_nf on nf.zk */
+static GEN
+nfsqff(GEN nf, GEN pol, long fl, GEN den)
+{
+  long n, nbf, dpol = degpol(pol);
+  GEN pr, C0, polbase, init_fa = NULL;
+  GEN N2, res, polred, lt, nfpol = typ(nf)==t_POL?nf:nf_get_pol(nf);
+  nfcmbf_t T;
+  nflift_t L;
+  pari_timer ti, ti_tot;
+
+  if (DEBUGLEVEL>2) { timer_start(&ti); timer_start(&ti_tot); }
+  n = degpol(nfpol);
+  /* deg = 1 => irreducible */
+  if (dpol == 1) {
+    if (fl == FACTORS) return mkvec(QXQX_normalize(pol, nfpol));
+    return mkvec(gneg(gdiv(gel(pol,2),gel(pol,3))));
+  }
+  if (typ(nf)==t_POL || nfsqff_use_Trager(n,dpol))
+  {
+    GEN z;
+    if (DEBUGLEVEL>2) err_printf("Using Trager's method\n");
+    if (typ(nf) != t_POL) den =  mulii(den, nf_get_index(nf));
+    z = nfsqff_trager(Q_primpart(pol), nfpol, den);
+    if (fl != FACTORS) {
+      long i, l = lg(z);
+      for (i = 1; i < l; i++)
+      {
+        GEN LT, t = gel(z,i); if (degpol(t) > 1) break;
+        LT = gel(t,3);
+        if (typ(LT) == t_POL) LT = gel(LT,2); /* constant */
+        gel(z,i) = gdiv(gel(t,2), negi(LT));
+      }
+      setlg(z, i);
+      if (fl == ROOTS_SPLIT && i != l) return cgetg(1,t_VEC);
+    }
+    return z;
+  }
+
+  polbase = RgX_to_nfX(nf, pol);
+  nbf = nf_pick_prime(5, nf, polbase, fl, &lt, &init_fa, &pr, &L.Tp);
+  if (fl == ROOTS_SPLIT && nbf < dpol) return cgetg(1,t_VEC);
+  if (nbf <= 1)
+  {
+    if (fl == FACTORS) return mkvec(QXQX_normalize(pol, nfpol)); /* irred. */
+    if (!nbf) return cgetg(1,t_VEC); /* no root */
+  }
+
+  if (DEBUGLEVEL>2) {
+    timer_printf(&ti, "choice of a prime ideal");
+    err_printf("Prime ideal chosen: %Ps\n", pr);
+  }
+  L.tozk = nf_get_invzk(nf);
+  L.topow= Q_remove_denom(nf_get_zk(nf), &L.topowden);
+  if (is_pm1(den)) den = NULL;
+  L.dn = den;
+  T.ZC = L2_bound(nf, den);
+  T.Br = nf_root_bounds(pol, nf); if (lt) T.Br = gmul(T.Br, lt);
+
+  /* C0 = bound for T_2(Q_i), Q | P */
+  if (fl != FACTORS) C0 = normlp(T.Br, 2, n);
+  else               C0 = nf_factor_bound(nf, polbase);
+  T.bound = mulrr(T.ZC, C0); /* bound for |Q_i|^2 in Z^n on chosen Z-basis */
+
+  N2 = mulur(dpol*dpol, normlp(T.Br, 4, n)); /* bound for T_2(lt * S_2) */
+  T.BS_2 = mulrr(T.ZC, N2); /* bound for |S_2|^2 on chosen Z-basis */
+
+  if (DEBUGLEVEL>2) {
+    timer_printf(&ti, "bound computation");
+    err_printf("  1) T_2 bound for %s: %Ps\n",
+               fl == FACTORS?"factor": "root", C0);
+    err_printf("  2) Conversion from T_2 --> | |^2 bound : %Ps\n", T.ZC);
+    err_printf("  3) Final bound: %Ps\n", T.bound);
+  }
+
+  L.p = pr_get_p(pr);
+  if (L.Tp && degpol(L.Tp) == 1) L.Tp = NULL;
+  bestlift_init(0, nf, pr, T.bound, &L);
+  if (DEBUGLEVEL>2) timer_start(&ti);
+  polred = ZqX_normalize(polbase, lt, &L); /* monic */
+
+  if (fl != FACTORS) {
+    GEN z = nf_DDF_roots(pol, polred, nfpol, init_fa, nbf, fl, &L);
+    if (lg(z) == 1) return cgetg(1, t_VEC);
+    return z;
+  }
+
+  {
+    pari_sp av = avma;
+    if (L.Tp)
+      res = FqX_split_all(init_fa, L.Tp, L.p);
+    else
+    {
+      long d;
+      res = cgetg(dpol + 1, t_VEC); gel(res,1) = FpX_red(polred,L.p);
+      d = FpX_split_Berlekamp((GEN*)(res + 1), L.p);
+      setlg(res, d + 1);
+    }
+    gen_sort_inplace(res, (void*)&cmp_RgX, &gen_cmp_RgX, NULL);
+    T.fact  = gerepilecopy(av, res);
+  }
+  if (DEBUGLEVEL>2) timer_printf(&ti, "splitting mod %Ps", pr);
+  T.pr = pr;
+  T.L  = &L;
+  T.polbase = polbase;
+  T.pol   = pol;
+  T.nf    = nf;
+  res = nf_combine_factors(&T, polred, dpol-1);
+  if (DEBUGLEVEL>2)
+    err_printf("Total Time: %ld\n===========\n", timer_delay(&ti_tot));
+  return res;
+}
+
+/* assume pol monic in nf.zk[X] */
+GEN
+nfroots_split(GEN nf, GEN pol)
+{
+  GEN T = get_nfpol(nf,&nf), den = get_den(&nf, T);
+  pari_sp av = avma;
+  GEN z = gerepilecopy(av, nfsqff(nf, pol, ROOTS_SPLIT, den));
+  return (lg(z) == 1)? NULL: mkvec2(z, nf);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*              Roots of unity in a number field                   */
+/*     (alternative to nfrootsof1 using factorization in K[X])     */
+/*                                                                 */
+/*******************************************************************/
+/* Code adapted from nffactor. Structure of the algorithm; only step 1 is
+ * specific to roots of unity.
+ *
+ * [Step 1]: guess roots via ramification. If trivial output this.
+ * [Step 2]: select prime [p] unramified and ideal [pr] above
+ * [Step 3]: evaluate the maximal exponent [k] such that the fondamental domain
+ *           of a LLL-reduction of [prk] = pr^k contains a ball of radius larger
+ *           than the norm of any root of unity.
+ * [Step 3]: select a heuristic exponent,
+ *           LLL reduce prk=pr^k and verify the exponent is sufficient,
+ *           otherwise try a larger one.
+ * [Step 4]: factor the cyclotomic polynomial mod [pr],
+ *           Hensel lift to pr^k and find the representative in the ball
+ *           If there is it is a primitive root */
+
+typedef struct {
+  GEN q;
+  GEN modpr;
+  GEN pr;
+  nflift_t *L;
+} prklift_t;
+
+/* FIXME: check that all primes dividing n are ramified ! */
+
+/* Choose prime ideal unramified with "large" inertia degree */
+static void
+nf_pick_prime_for_units(GEN nf, prklift_t *P)
+{
+  GEN nfpol = nf_get_pol(nf), bad = mulii(nf_get_disc(nf), nf_get_index(nf));
+  GEN aT, amodpr, apr, ap = NULL, r = NULL;
+  long nfdeg = degpol(nfpol), maxf = get_maxf(nfdeg);
+  ulong pp;
+  forprime_t S;
+
+  (void)u_forprime_init(&S, 2, ULONG_MAX);
+  while ( (pp = u_forprime_next(&S)) )
+  {
+    if (! umodiu(bad,pp)) continue;
+    r = get_good_factor(nfpol, pp, maxf);
+    if (r) break;
+  }
+  if (!r) pari_err_OVERFLOW("nf_pick_prime [ran out of primes]");
+  ap = utoipos(pp);
+  apr = primedec_apply_kummer(nf, Flx_to_ZX(r), 1, ap);
+  amodpr = zk_to_Fq_init(nf,&apr,&aT,&ap);
+  P->pr = apr;
+  P->q = pr_norm(apr);
+  P->modpr = amodpr;
+  P->L->p = ap;
+  P->L->Tp = aT;
+  P->L->tozk = nf_get_invzk(nf);
+  P->L->topow = Q_remove_denom(nf_get_zk(nf), &(P->L->topowden));
+}
+
+/* *Heuristic* exponent k such that the fundamental domain of pr^k
+ * should contain the ball of radius C */
+static double
+mybestlift_bound(GEN C)
+{
+  C = gtofp(C,DEFAULTPREC);
+#if 0 /* d = nf degree, Npr = Norm(pr) */
+  const double alpha = 0.99; /* LLL parameter */
+  const double y = 1 / (alpha - 0.25); /* = 2 if alpha = 3/4 */
+  double t;
+  t = rtodbl(mplog(gmul2n(divru(C,d), 4))) * 0.5 + (d-1) * log(1.5 * sqrt(y));
+  return ceil((t * d) / log(gtodouble(Npr))); /* proved upper bound */
+#endif
+  return ceil(log(gtodouble(C)) / 0.2) + 3;
+}
+
+/* Returns the roots of the n_cyclo-th cyclotomic polynomial
+ * if it splits, NULL otherwise */
+static GEN
+nfcyclo_root(GEN nf, long n_cyclo, prklift_t *P)
+{
+  pari_sp av = avma;
+  GEN init_fa = NULL; /* factors mod pr */
+  GEN z, nfpol = nf_get_pol(nf), pol = polcyclo(n_cyclo, MAXVARN);
+  long nbf, deg = degpol(pol); /* = eulerphi(n_cyclo) */
+  if (P->L->Tp)
+    nbf = FqX_split_deg1(&init_fa, pol, P->L->Tp, P->L->p);
+  else
+  {
+    ulong p = itou(P->L->p);
+    nbf = Flx_nbroots(ZX_to_Flx(pol,p), p);
+  }
+  if (nbf != deg) return NULL; /* no roots in residue field */
+  z = nf_DDF_roots(pol, pol, nfpol, init_fa, nbf, ROOTS_SPLIT, P->L);
+  if (lg(z) == 1) { avma = av; return NULL; } /* no roots */
+  return gel(z,1);
+}
+
+/* Guesses the number of roots of unity in number field [nf].
+ * Computes gcd of N(P)-1 for some primes. The value returned is a proven
+ * multiple of the correct value. */
+static long
+guess_roots(GEN nf)
+{
+  long c = 0, nfdegree = nf_get_degree(nf), B = nfdegree + 20, l;
+  ulong p = 2;
+  GEN T = nf_get_pol(nf), D = nf_get_disc(nf), index = nf_get_index(nf);
+  GEN nbroots = NULL;
+  forprime_t S;
+  pari_sp av;
+
+  (void)u_forprime_init(&S, 3, ULONG_MAX);
+  av = avma;
+  /* result must be stationnary (counter c) for at least B loops */
+  for (l=1; (p = u_forprime_next(&S)); l++)
+  {
+    GEN old, F, pf_1, Tp;
+    long i, nb, gcdf = 0;
+
+    if (!umodiu(D,p) || !umodiu(index,p)) continue;
+    Tp = ZX_to_Flx(T,p); /* squarefree */
+    F = Flx_nbfact_by_degree(Tp, &nb, p);
+    /* the gcd of the p^f - 1 is p^(gcd of the f's) - 1 */
+    for (i = 1; i <= nfdegree; i++)
+      if (F[i]) {
+        gcdf = gcdf? cgcd(gcdf, i): i;
+        if (gcdf == 1) break;
+      }
+    pf_1 = subis(powuu(p, gcdf), 1);
+    old = nbroots;
+    nbroots = nbroots? gcdii(pf_1, nbroots): pf_1;
+    if (DEBUGLEVEL>5)
+      err_printf("p=%ld; gcf(f(P/p))=%ld; nbroots | %Ps",p, gcdf, nbroots);
+    /* if same result go on else reset the stop counter [c] */
+    if (old && equalii(nbroots,old))
+    { if (!is_bigint(nbroots) && ++c > B) break; }
+    else
+      c = 0;
+  }
+  if (!nbroots) pari_err_OVERFLOW("guess_roots [ran out of primes]");
+  if (DEBUGLEVEL>5) err_printf("%ld loops\n",l);
+  avma = av; return itos(nbroots);
+}
+
+/* T(x) an irreducible ZX. Is it of the form Phi_n(c \pm x) ?
+ * Return NULL if not, and a root of 1 of maximal order in Z[x]/(T) otherwise
+ *
+ * N.B. Set n_squarefree = 1 if n is squarefree, and 0 otherwise.
+ * This last parameter is inconvenient, but it allows a cheap
+ * stringent test. (n guessed from guess_roots())*/
+static GEN
+ZXirred_is_cyclo_translate(GEN T, long n_squarefree)
+{
+  long r, m, d = degpol(T);
+  GEN T1, c = divis_rem(gel(T, d+1), d, &r); /* d-1 th coeff divisible by d ? */
+  /* The trace coefficient of polcyclo(n) is \pm1 if n is square free, and 0
+   * otherwise. */
+  if (!n_squarefree)
+  { if (r) return NULL; }
+  else
+  {
+    if (r < -1)
+    {
+      r += d;
+      c = subiu(c, 1);
+    }
+    else if (r == d-1)
+    {
+      r = -1;
+      c = addiu(c, 1);
+    }
+    if (r != 1 && r != -1) return NULL;
+  }
+  if (signe(c)) /* presumably Phi_guess(c \pm x) */
+    T = RgX_translate(T, negi(c));
+  if (!n_squarefree) T = RgX_deflate_max(T, &m);
+  /* presumably Phi_core(guess)(\pm x), cyclotomic iff original T was */
+  T1 = ZX_graeffe(T);
+  if (ZX_equal(T, T1)) /* T = Phi_n, n odd */
+    return deg1pol_shallow(gen_m1, negi(c), varn(T));
+  else if (ZX_equal(T1, ZX_unscale(T, gen_m1))) /* T = Phi_{2n}, nodd */
+    return deg1pol_shallow(gen_1, c, varn(T));
+  return NULL;
+}
+
+static GEN
+trivroots(void) { return mkvec2(gen_2, gen_m1); }
+/* Number of roots of unity in number field [nf]. */
+GEN
+rootsof1(GEN nf)
+{
+  prklift_t P;
+  nflift_t L;
+  GEN fa, LP, LE, C0, z, prim_root, disc;
+  pari_timer ti;
+  long i, l, nbguessed, nbroots, nfdegree;
+  pari_sp av;
+
+  nf = checknf(nf);
+  if (nf_get_r1(nf)) return trivroots();
+
+  /* Step 1 : guess number of roots and discard trivial case 2 */
+  if (DEBUGLEVEL>2) timer_start(&ti);
+  nbguessed = guess_roots(nf);
+  if (DEBUGLEVEL>2)
+    timer_printf(&ti, "guessing roots of 1 [guess = %ld]", nbguessed);
+  if (nbguessed == 2) return trivroots();
+
+  nfdegree = nf_get_degree(nf);
+  fa = factoru(nbguessed);
+  LP = gel(fa,1); l = lg(LP);
+  LE = gel(fa,2);
+  disc = nf_get_disc(nf);
+  for (i = 1; i < l; i++)
+  {
+    long p = LP[i];
+    /* Cheap test: can Q(zeta_{2p}) be a subset of K ? */
+    if (p == 2)
+    { /* 2 | n and v_p(disc K) >= n/2 ? */
+      if (LE[i] == 1) continue;
+      if (!odd(nfdegree) && vali(disc) >= nfdegree / 2) continue;
+    }
+    else
+    { /* p-1 | n and v_p(disc K) >= (p-2) n/(p-1) ? */
+      ulong r, q = udivuu_rem(nfdegree, p-1, &r);
+      if (r == 0 && (ulong)Z_lval(disc, p) >= q * (p-2)) continue;
+    }
+    nbguessed /= upowuu(p, LE[i]);
+    LE[i] = 0;
+  }
+  if (DEBUGLEVEL>2)
+    timer_printf(&ti, "after ramification conditions [guess = %ld]", nbguessed);
+  if (nbguessed == 2) return trivroots();
+  av = avma;
+
+  /* Step 1.5 : test if nf.pol == subst(polcyclo(nbguessed), x, \pm x+c) */
+  if (eulerphiu_fact(fa) == (ulong)nfdegree)
+  {
+    GEN elt = ZXirred_is_cyclo_translate(nf_get_pol(nf),
+                                         uissquarefree_fact(fa));
+    if (elt)
+    {
+      if (DEBUGLEVEL>2)
+        timer_printf(&ti, "checking for cyclotomic polynomial [yes]");
+      return gerepilecopy(av, mkvec2(utoipos(nbguessed), elt));
+    }
+    avma = av;
+  }
+  if (DEBUGLEVEL>2)
+    timer_printf(&ti, "checking for cyclotomic polynomial [no]");
+
+  /* Step 2 : choose a prime ideal for local lifting */
+  P.L = &L; nf_pick_prime_for_units(nf, &P);
+  if (DEBUGLEVEL>2)
+    timer_printf(&ti, "choosing prime %Ps, degree %ld",
+             P.L->p, P.L->Tp? degpol(P.L->Tp): 1);
+
+  /* Step 3 : compute a reduced pr^k allowing lifting of local solutions */
+  /* evaluate maximum L2 norm of a root of unity in nf */
+  C0 = gmulsg(nfdegree, L2_bound(nf, gen_1));
+  /* lift and reduce pr^k */
+  if (DEBUGLEVEL>2) err_printf("Lift pr^k; GSmin wanted: %Ps\n",C0);
+  bestlift_init((long)mybestlift_bound(C0), nf, P.pr, C0, P.L);
+  P.L->dn = NULL;
+  if (DEBUGLEVEL>2) timer_start(&ti);
+
+  /* Step 4 : actual computation of roots */
+  nbroots = 2; prim_root = gen_m1;
+  for (i = 1; i < l; i++)
+  { /* for all prime power factors of nbguessed, find a p^k-th root of unity */
+    long k, p = LP[i];
+    for (k = LE[i]; k > 0; k--)
+    { /* find p^k-th roots */
+      long pk = upowuu(p,k);
+      if (pk==2) continue; /* no need to test second roots ! */
+      z = nfcyclo_root(nf,pk,&P);
+      if (DEBUGLEVEL>2) timer_printf(&ti, "for factoring Phi_%ld^%ld", p,k);
+      if (z) {
+        if (DEBUGLEVEL>2) err_printf("  %ld-th root of unity found.\n", pk);
+        if (p==2) { nbroots = pk; prim_root = z; }
+        else     { nbroots *= pk; prim_root = nfmul(nf, prim_root,z); }
+        break;
+      }
+      if (DEBUGLEVEL) pari_warn(warner,"rootsof1: wrong guess");
+    }
+  }
+  return gerepilecopy(av, mkvec2(utoi(nbroots), prim_root));
+}
+
+static long
+nf_pm1(GEN y) {
+  GEN z = gel(y,1);
+  return (is_pm1(z) && ZV_isscalar(y))? signe(z): 0;
+}
+static GEN
+is_primitive_root(GEN nf, GEN fa, GEN x, long w)
+{
+  GEN y, exp = utoipos(2), pp = gel(fa,1);
+  long i,p, l = lg(pp);
+
+  for (i=1; i<l; i++)
+  {
+    p = itos(gel(pp,i));
+    exp[2] = w / p; y = nfpow(nf,x,exp);
+    if (nf_pm1(y) > 0) /* y = 1 */
+    {
+      if (p!=2 || !gequal1(gcoeff(fa,i,2))) return NULL;
+      x = gneg_i(x);
+    }
+  }
+  return x;
+}
+GEN
+rootsof1_kannan(GEN nf)
+{
+  pari_sp av = avma;
+  long N, k, i, ws, prec;
+  GEN z, y, d, list, w;
+
+  nf = checknf(nf);
+  if ( nf_get_r1(nf) ) return trivroots();
+
+  N = nf_get_degree(nf); prec = nf_get_prec(nf);
+  for (;;)
+  {
+    GEN R = R_from_QR(nf_get_G(nf), prec);
+    if (R)
+    {
+      y = fincke_pohst(mkvec(R), utoipos(N), N * N, 0, NULL);
+      if (y) break;
+    }
+    prec = precdbl(prec);
+    if (DEBUGLEVEL) pari_warn(warnprec,"rootsof1",prec);
+    nf = nfnewprec_shallow(nf,prec);
+  }
+  if (itos(ground(gel(y,2))) != N) pari_err_BUG("rootsof1 (bug1)");
+  w = gel(y,1); ws = itos(w);
+  if (ws == 2) { avma = av; return trivroots(); }
+
+  d = Z_factor(w); list = gel(y,3); k = lg(list);
+  for (i=1; i<k; i++)
+  {
+    z = is_primitive_root(nf, d, gel(list,i), ws);
+    if (z) return gerepilecopy(av, mkvec2(w, z));
+  }
+  pari_err_BUG("rootsof1");
+  return NULL; /* not reached */
+}
diff --git a/src/basemath/perm.c b/src/basemath/perm.c
new file mode 100644
index 0000000..eea2a55
--- /dev/null
+++ b/src/basemath/perm.c
@@ -0,0 +1,1053 @@
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*************************************************************************/
+/**                                                                     **/
+/**                   Routines for handling VEC/COL                     **/
+/**                                                                     **/
+/*************************************************************************/
+int
+vec_isconst(GEN v)
+{
+  long i, l = lg(v);
+  GEN w;
+  if (l==1) return 1;
+  w = gel(v,1);
+  for(i=2;i<l;i++)
+    if (!gequal(gel(v,i), w)) return 0;
+  return 1;
+}
+
+/* Check if all the elements of v are different.
+ * Use a quadratic algorithm. Could be done in n*log(n) by sorting. */
+int
+vec_is1to1(GEN v)
+{
+  long i, j, l = lg(v);
+  for (i=1; i<l; i++)
+  {
+    GEN w = gel(v,i);
+    for(j=i+1; j<l; j++)
+      if (gequal(gel(v,j), w)) return 0;
+  }
+  return 1;
+}
+
+GEN
+vec_insert(GEN v, long n, GEN x)
+{
+  long i, l=lg(v);
+  GEN V = cgetg(l+1,t_VEC);
+  for(i=1; i<n; i++) gel(V,i) = gel(v,i);
+  gel(V,n) = x;
+  for(i=n+1; i<=l; i++) gel(V,i) = gel(v,i-1);
+  return V;
+}
+/*************************************************************************/
+/**                                                                     **/
+/**                   Routines for handling VECSMALL                    **/
+/**                                                                     **/
+/*************************************************************************/
+/* Sort v[0]...v[n-1] and put result in w[0]...w[n-1].
+ * We accept v==w. w must be allocated. */
+static void
+vecsmall_sortspec(GEN v, long n, GEN w)
+{
+  pari_sp ltop=avma;
+  long nx=n>>1, ny=n-nx;
+  long m, ix, iy;
+  GEN x, y;
+  if (n<=2)
+  {
+    if (n==1)
+      w[0]=v[0];
+    else if (n==2)
+    {
+      long v0=v[0], v1=v[1];
+      if (v0<=v1) { w[0]=v0; w[1]=v1; }
+      else        { w[0]=v1; w[1]=v0; }
+    }
+    return;
+  }
+  x=new_chunk(nx); y=new_chunk(ny);
+  vecsmall_sortspec(v,nx,x);
+  vecsmall_sortspec(v+nx,ny,y);
+  for (m=0, ix=0, iy=0; ix<nx && iy<ny; )
+    if (x[ix]<=y[iy])
+      w[m++]=x[ix++];
+    else
+      w[m++]=y[iy++];
+  for(;ix<nx;) w[m++]=x[ix++];
+  for(;iy<ny;) w[m++]=y[iy++];
+  avma=ltop;
+}
+
+/*in place sort.*/
+void
+vecsmall_sort(GEN V)
+{
+  long l = lg(V)-1;
+  if (l<=1) return;
+  vecsmall_sortspec(V+1,l,V+1);
+}
+
+/* cf gen_sortspec */
+static GEN
+vecsmall_indexsortspec(GEN v, long n)
+{
+  long nx, ny, m, ix, iy;
+  GEN x, y, w;
+  switch(n)
+  {
+    case 1: return mkvecsmall(1);
+    case 2: return (v[1] <= v[2])? mkvecsmall2(1,2): mkvecsmall2(2,1);
+    case 3:
+      if (v[1] <= v[2]) {
+        if (v[2] <= v[3]) return mkvecsmall3(1,2,3);
+        return (v[1] <= v[3])? mkvecsmall3(1,3,2)
+                             : mkvecsmall3(3,1,2);
+      } else {
+        if (v[1] <= v[3]) return mkvecsmall3(2,1,3);
+        return (v[2] <= v[3])? mkvecsmall3(2,3,1)
+                             : mkvecsmall3(3,2,1);
+      }
+  }
+  nx = n>>1; ny = n-nx;
+  w = cgetg(n+1,t_VECSMALL);
+  x = vecsmall_indexsortspec(v,nx);
+  y = vecsmall_indexsortspec(v+nx,ny);
+  for (m=1, ix=1, iy=1; ix<=nx && iy<=ny; )
+    if (v[x[ix]] <= v[y[iy]+nx])
+      w[m++] = x[ix++];
+    else
+      w[m++] = y[iy++]+nx;
+  for(;ix<=nx;) w[m++] = x[ix++];
+  for(;iy<=ny;) w[m++] = y[iy++]+nx;
+  avma = (pari_sp)w; return w;
+}
+
+/*indirect sort.*/
+GEN
+vecsmall_indexsort(GEN V)
+{
+  long l=lg(V)-1;
+  if (l==0) return cgetg(1, t_VECSMALL);
+  return vecsmall_indexsortspec(V,l);
+}
+
+/* assume V sorted */
+GEN
+vecsmall_uniq_sorted(GEN V)
+{
+  GEN W;
+  long i,j, l = lg(V);
+  if (l == 1) return vecsmall_copy(V);
+  W = cgetg(l,t_VECSMALL);
+  W[1] = V[1];
+  for(i=j=2; i<l; i++)
+    if (V[i] != W[j-1]) W[j++] = V[i];
+  stackdummy((pari_sp)(W + l), (pari_sp)(W + j));
+  setlg(W, j); return W;
+}
+
+GEN
+vecsmall_uniq(GEN V)
+{
+  pari_sp av = avma;
+  V = zv_copy(V); vecsmall_sort(V);
+  return gerepileuptoleaf(av, vecsmall_uniq_sorted(V));
+}
+
+/* assume x sorted */
+long
+vecsmall_duplicate_sorted(GEN x)
+{
+  long i,k,l=lg(x);
+  if (l==1) return 0;
+  for (k=x[1],i=2; i<l; k=x[i++])
+    if (x[i] == k) return i;
+  return 0;
+}
+
+long
+vecsmall_duplicate(GEN x)
+{
+  pari_sp av=avma;
+  GEN p=vecsmall_indexsort(x);
+  long k,i,r=0,l=lg(x);
+  if (l==1) return 0;
+  for (k=x[p[1]],i=2; i<l; k=x[p[i++]])
+    if (x[p[i]] == k) { r=p[i]; break; }
+  avma=av;
+  return r;
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**             Routines for handling vectors of VECSMALL               **/
+/**                                                                     **/
+/*************************************************************************/
+
+GEN
+vecvecsmall_sort(GEN x)
+{
+  return gen_sort(x, (void*)&vecsmall_lexcmp, cmp_nodata);
+}
+
+GEN
+vecvecsmall_sort_uniq(GEN x)
+{
+  return gen_sort_uniq(x, (void*)&vecsmall_lexcmp, cmp_nodata);
+}
+
+GEN
+vecvecsmall_indexsort(GEN x)
+{
+  return gen_indexsort(x, (void*)&vecsmall_lexcmp, cmp_nodata);
+}
+
+long
+vecvecsmall_search(GEN x, GEN y, long flag)
+{
+  return gen_search(x,y,flag,(void*)vecsmall_prefixcmp, cmp_nodata);
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**                  Routines for handling permutations                 **/
+/**                                                                     **/
+/*************************************************************************/
+
+/* Permutations may be given by
+ * perm (VECSMALL): a bijection from 1...n to 1...n i-->perm[i]
+ * cyc (VEC of VECSMALL): a product of disjoint cycles. */
+
+/* Multiply (compose) two permutations, putting the result in the second one. */
+static void
+perm_mul_inplace2(GEN s, GEN t)
+{
+  long i, l = lg(s);
+  for (i = 1; i < l; i++) t[i] = s[t[i]];
+}
+
+/* Orbits of the subgroup generated by v on {1,..,n} */
+static GEN
+vecperm_orbits_i(GEN v, long n)
+{
+  long mj = 1, k, l, m;
+  GEN cy, cycle = cgetg(n+1, t_VEC), bit = zero_F2v(n);
+  for (k = 1, l = 1; k <= n;)
+  {
+    for (  ; F2v_coeff(bit,mj); mj++) /*empty*/;
+    cy = cgetg(n+1, t_VECSMALL);
+    m = 1; k++; cy[m++] = mj;
+    F2v_set(bit, mj++);
+    for(;;)
+    {
+      long o, mold = m;
+      for (o = 1; o < lg(v); o++)
+      {
+        GEN vo = gel(v,o);
+        long p;
+        for (p = 1; p < m; p++) /* m increases! */
+        {
+          long j = vo[ cy[p] ];
+          if (!F2v_coeff(bit,j)) cy[m++] = j;
+          F2v_set(bit,j);
+        }
+      }
+      if (m == mold) break;
+      k += m - mold;
+    }
+    setlg(cy, m); gel(cycle,l++) = cy;
+  }
+  setlg(cycle, l); return cycle;
+}
+/* memory clean version */
+GEN
+vecperm_orbits(GEN v, long n)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, vecperm_orbits_i(v, n));
+}
+
+/* Compute the cyclic decomposition of a permutation */
+GEN
+perm_cycles(GEN v)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, vecperm_orbits_i(mkvec(v), lg(v)-1));
+}
+
+/* Output the order of p */
+long
+perm_order(GEN v)
+{
+  pari_sp ltop = avma;
+  GEN c = vecperm_orbits_i(mkvec(v), lg(v)-1);
+  long i, d;
+  for(i=1, d=1; i<lg(c); i++) d = clcm(d, lg(gel(c,i))-1);
+  avma = ltop; return d;
+}
+
+GEN
+cyc_pow(GEN cyc, long exp)
+{
+  long i, j, k, l, r;
+  GEN c;
+  for (r = j = 1; j < lg(cyc); j++)
+  {
+    long n = lg(gel(cyc,j)) - 1;
+    r += cgcd(n, exp);
+  }
+  c = cgetg(r, t_VEC);
+  for (r = j = 1; j < lg(cyc); j++)
+  {
+    GEN v = gel(cyc,j);
+    long n = lg(v) - 1, e = smodss(exp,n), g = (long)ugcd(n, e), m = n / g;
+    for (i = 0; i < g; i++)
+    {
+      GEN p = cgetg(m+1, t_VECSMALL);
+      gel(c,r++) = p;
+      for (k = 1, l = i; k <= m; k++)
+      {
+        p[k] = v[l+1];
+        l += e; if (l >= n) l -= n;
+      }
+    }
+  }
+  return c;
+}
+
+/* Compute the power of a permutation given by product of cycles
+ * Ouput a perm, not a cyc */
+GEN
+cyc_pow_perm(GEN cyc, long exp)
+{
+  long e, j, k, l, n;
+  GEN p;
+  for (n = 0, j = 1; j < lg(cyc); j++) n += lg(gel(cyc,j))-1;
+  p = cgetg(n + 1, t_VECSMALL);
+  for (j = 1; j < lg(cyc); j++)
+  {
+    GEN v = gel(cyc,j);
+    n = lg(v) - 1; e = smodss(exp, n);
+    for (k = 1, l = e; k <= n; k++)
+    {
+      p[v[k]] = v[l+1];
+      if (++l == n) l = 0;
+    }
+  }
+  return p;
+}
+
+/* Compute the power of a permutation.
+ * TODO: make it more clever for small exp */
+GEN
+perm_pow(GEN perm, long exp)
+{
+  return cyc_pow_perm(perm_cycles(perm), exp);
+}
+
+GEN
+perm_to_GAP(GEN p)
+{
+  pari_sp ltop=avma;
+  GEN gap;
+  GEN x;
+  long i;
+  long nb, c=0;
+  char *s;
+  long sz;
+  long lp=lg(p)-1;
+  if (typ(p) != t_VECSMALL)  pari_err_TYPE("perm_to_GAP",p);
+  x = perm_cycles(p);
+  sz = (long) ((bfffo(lp)+1) * LOG10_2 + 1);
+  /*Dry run*/
+  for (i = 1, nb = 1; i < lg(x); ++i)
+  {
+    GEN z = gel(x,i);
+    long lz = lg(z)-1;
+    nb += 1+lz*(sz+2);
+  }
+  nb++;
+  /*Real run*/
+  gap = cgetg(nchar2nlong(nb) + 1, t_STR);
+  s = GSTR(gap);
+  for (i = 1; i < lg(x); ++i)
+  {
+    long j;
+    GEN z = gel(x,i);
+    if (lg(z) > 2)
+    {
+      s[c++] = '(';
+      for (j = 1; j < lg(z); ++j)
+      {
+        if (j > 1)
+        {
+          s[c++] = ','; s[c++] = ' ';
+        }
+        sprintf(s+c,"%ld",z[j]);
+        while(s[c++]) /* empty */;
+        c--;
+      }
+      s[c++] = ')';
+    }
+  }
+  if (!c) { s[c++]='('; s[c++]=')'; }
+  s[c] = '\0';
+  return gerepileupto(ltop,gap);
+}
+
+int
+perm_commute(GEN s, GEN t)
+{
+  long i, l = lg(t);
+  for (i = 1; i < l; i++)
+    if (t[ s[i] ] != s[ t[i] ]) return 0;
+  return 1;
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**                  Routines for handling groups                       **/
+/**                                                                     **/
+/*************************************************************************/
+/* A Group is a t_VEC [gen,orders]
+ * gen (vecvecsmall): list of generators given by permutations
+ * orders (vecsmall): relatives orders of generators. */
+INLINE GEN grp_get_gen(GEN G) { return gel(G,1); }
+INLINE GEN grp_get_ord(GEN G) { return gel(G,2); }
+
+/* A Quotient Group is a t_VEC [gen,coset]
+ * gen (vecvecsmall): coset generators
+ * coset (vecsmall): gen[coset[p[1]]] generate the p-coset.
+ */
+INLINE GEN quo_get_gen(GEN C) { return gel(C,1); }
+INLINE GEN quo_get_coset(GEN C) { return gel(C,2); }
+
+static GEN
+trivialsubgroups(void)
+{ GEN L = cgetg(2, t_VEC); gel(L,1) = trivialgroup(); return L; }
+
+/* Compute the order of p modulo the group given by a set */
+long
+perm_relorder(GEN p, GEN set)
+{
+  pari_sp ltop = avma;
+  long n = 1;
+  long q = p[1];
+  while (!F2v_coeff(set,q)) { q = p[q]; n++; }
+  avma = ltop; return n;
+}
+
+GEN
+perm_generate(GEN S, GEN H, long o)
+{
+  long i, n = lg(H)-1;
+  GEN L = cgetg(n*o + 1, t_VEC);
+  for(i=1; i<=n;     i++) gel(L,i) = vecsmall_copy(gel(H,i));
+  for(   ; i <= n*o; i++) gel(L,i) = perm_mul(gel(L,i-n), S);
+  return L;
+}
+
+/*Return the order (cardinal) of a group */
+long
+group_order(GEN G)
+{
+  return zv_prod(grp_get_ord(G));
+}
+
+/* G being a subgroup of S_n, output n */
+long
+group_domain(GEN G)
+{
+  GEN gen = grp_get_gen(G);
+  if (lg(gen) < 2) pari_err_DOMAIN("group_domain", "#G", "=", gen_1,G);
+  return lg(gel(gen,1)) - 1;
+}
+
+/*Left coset of g mod G: gG*/
+GEN
+group_leftcoset(GEN G, GEN g)
+{
+  GEN gen = grp_get_gen(G), ord = grp_get_ord(G);
+  GEN res = cgetg(group_order(G)+1, t_VEC);
+  long i, j, k;
+  gel(res,1) = vecsmall_copy(g);
+  k = 1;
+  for (i = 1; i < lg(gen); i++)
+  {
+    long c = k * (ord[i] - 1);
+    for (j = 1; j <= c; j++) gel(res,++k) = perm_mul(gel(res,j), gel(gen,i));
+  }
+  return res;
+}
+/*Right coset of g mod G: Gg*/
+GEN
+group_rightcoset(GEN G, GEN g)
+{
+  GEN gen = grp_get_gen(G), ord = grp_get_ord(G);
+  GEN res = cgetg(group_order(G)+1, t_VEC);
+  long i, j, k;
+  gel(res,1) = vecsmall_copy(g);
+  k = 1;
+  for (i = 1; i < lg(gen); i++)
+  {
+    long c = k * (ord[i] - 1);
+    for (j = 1; j <= c; j++) gel(res,++k) = perm_mul(gel(gen,i), gel(res,j));
+  }
+  return res;
+}
+/*Elements of a group from the generators, cf group_leftcoset*/
+GEN
+group_elts(GEN G, long n)
+{
+  GEN gen = grp_get_gen(G), ord = grp_get_ord(G);
+  GEN res = cgetg(group_order(G)+1, t_VEC);
+  long i, j, k;
+  gel(res,1) = identity_perm(n);
+  k = 1;
+  for (i = 1; i < lg(gen); i++)
+  {
+    long c = k * (ord[i] - 1);
+    /* j = 1, use res[1] = identity */
+    gel(res,++k) = vecsmall_copy(gel(gen,i));
+    for (j = 2; j <= c; j++) gel(res,++k) = perm_mul(gel(res,j), gel(gen,i));
+  }
+  return res;
+}
+
+GEN
+groupelts_set(GEN elts, long n)
+{
+  GEN res = zero_F2v(n);
+  long i, l = lg(elts);
+  for(i=1; i<l; i++)
+    F2v_set(res,mael(elts,i,1));
+  return res;
+}
+
+/*Elements of a group from the generators, returned as a set (bitmap)*/
+GEN
+group_set(GEN G, long n)
+{
+  GEN res = zero_F2v(n);
+  pari_sp av = avma;
+  GEN elts = group_elts(G, n);
+  long i, l = lg(elts);
+  for(i=1; i<l; i++)
+    F2v_set(res,mael(elts,i,1));
+  avma = av;
+  return res;
+}
+
+static int
+sgcmp(GEN a, GEN b) { return vecsmall_lexcmp(gel(a,1),gel(b,1)); }
+
+GEN
+subgroups_tableset(GEN S, long n)
+{
+  long i, l = lg(S);
+  GEN  v = cgetg(l, t_VEC);
+  for(i=1; i<l; i++)
+    gel(v,i) = mkvec2(group_set(gel(S,i), n), mkvecsmall(i));
+  gen_sort_inplace(v,(void*)sgcmp,cmp_nodata, NULL);
+  return v;
+}
+
+long
+tableset_find_index(GEN tbl, GEN set)
+{
+  long i = tablesearch(tbl,mkvec2(set,mkvecsmall(0)),sgcmp);
+  if (!i) return 0;
+  return mael3(tbl,i,2,1);
+}
+
+GEN
+trivialgroup(void) { retmkvec2(cgetg(1,t_VEC), cgetg(1,t_VECSMALL)); }
+/*Cyclic group generated by g of order s*/
+GEN
+cyclicgroup(GEN g, long s)
+{ retmkvec2(mkvec( vecsmall_copy(g) ),
+            mkvecsmall(s)); }
+/*Return the group generated by g1,g2 of relative orders s1,s2*/
+GEN
+dicyclicgroup(GEN g1, GEN g2, long s1, long s2)
+{ retmkvec2( mkvec2(vecsmall_copy(g1), vecsmall_copy(g2)),
+             mkvecsmall2(s1, s2) ); }
+
+/* return the quotient map G --> G/H */
+/*The ouput is [gen,hash]*/
+/* gen (vecvecsmall): coset generators
+ * coset (vecsmall): vecsmall of coset number) */
+GEN
+group_quotient(GEN G, GEN H)
+{
+  pari_sp ltop = avma;
+  GEN  p2, p3;
+  long i, j, a = 1;
+  long n = group_domain(G), o = group_order(H);
+  GEN  elt = group_elts(G,n), el;
+  long le = lg(elt)-1;
+  GEN used = zero_F2v(le+1);
+  long l = le/o;
+  p2 = cgetg(l+1, t_VEC);
+  p3 = zero_zv(n);
+  el = zero_zv(n);
+  for (i = 1; i<=le; i++)
+    el[mael(elt,i,1)]=i;
+  for (i = 1; i <= l; ++i)
+  {
+    GEN V;
+    while(F2v_coeff(used,a)) a++;
+    V = group_leftcoset(H,gel(elt,a));
+    gel(p2,i) = gel(V,1);
+    for(j=1;j<lg(V);j++)
+    {
+      long b = el[mael(V,j,1)];
+      if (b==0) pari_err_IMPL("group_quotient for a non-WSS group");
+      F2v_set(used,b);
+    }
+    for (j = 1; j <= o; j++)
+      p3[mael(V, j, 1)] = i;
+  }
+  return gerepilecopy(ltop,mkvec2(p2,p3));
+}
+
+/*Compute the image of a permutation by a quotient map.*/
+GEN
+quotient_perm(GEN C, GEN p)
+{
+  GEN gen = quo_get_gen(C);
+  GEN coset = quo_get_coset(C);
+  long j, l = lg(gen);
+  GEN p3 = cgetg(l, t_VECSMALL);
+  for (j = 1; j < l; ++j)
+  {
+    p3[j] = coset[p[mael(gen,j,1)]];
+    if (p3[j]==0) pari_err_IMPL("quotient_perm for a non-WSS group");
+  }
+  return p3;
+}
+
+/* H is a subgroup of G, C is the quotient map G --> G/H
+ *
+ * Lift a subgroup S of G/H to a subgroup of G containing H */
+GEN
+quotient_subgroup_lift(GEN C, GEN H, GEN S)
+{
+  GEN genH = grp_get_gen(H);
+  GEN genS = grp_get_gen(S);
+  GEN genC = quo_get_gen(C);
+  long l1 = lg(genH)-1;
+  long l2 = lg(genS)-1, j;
+  GEN p1 = cgetg(3, t_VEC), L = cgetg(l1+l2+1, t_VEC);
+  for (j = 1; j <= l1; ++j) gel(L,j) = gel(genH,j);
+  for (j = 1; j <= l2; ++j) gel(L,l1+j) = gel(genC, mael(genS,j,1));
+  gel(p1,1) = L;
+  gel(p1,2) = vecsmall_concat(grp_get_ord(H), grp_get_ord(S));
+  return p1;
+}
+
+/* Let G a group and C a quotient map G --> G/H
+ * Assume H is normal, return the group G/H */
+GEN
+quotient_group(GEN C, GEN G)
+{
+  pari_sp ltop = avma;
+  GEN Qgen, Qord, Qelt, Qset, Q;
+  GEN Cgen = quo_get_gen(C);
+  GEN Ggen = grp_get_gen(G);
+  long i,j, n = lg(Cgen)-1, l = lg(Ggen);
+  Qord = cgetg(l, t_VECSMALL);
+  Qgen = cgetg(l, t_VEC);
+  Qelt = mkvec(identity_perm(n));
+  Qset = groupelts_set(Qelt, n);
+  for (i = 1, j = 1; i < l; ++i)
+  {
+    GEN  g = quotient_perm(C, gel(Ggen,i));
+    long o = perm_relorder(g, Qset);
+    gel(Qgen,j) = g;
+    Qord[j] = o;
+    if (o != 1)
+    {
+      Qelt = perm_generate(g, Qelt, o);
+      Qset = groupelts_set(Qelt, n);
+      j++;
+    }
+  }
+  setlg(Qgen,j);
+  setlg(Qord,j); Q = mkvec2(Qgen, Qord);
+  return gerepilecopy(ltop,Q);
+}
+
+/* Return 1 if g normalizes N, 0 otherwise */
+long
+group_perm_normalize(GEN N, GEN g)
+{
+  pari_sp ltop = avma;
+  long r = gequal(vecvecsmall_sort(group_leftcoset(N, g)),
+                  vecvecsmall_sort(group_rightcoset(N, g)));
+  avma = ltop; return r;
+}
+
+/* L is a list of subgroups, C is a coset and r a relative order.*/
+static GEN
+liftlistsubgroups(GEN L, GEN C, long r)
+{
+  pari_sp ltop = avma;
+  long c = lg(C)-1, l = lg(L)-1, n = lg(gel(C,1))-1, i, k;
+  GEN R;
+  if (!l) return cgetg(1,t_VEC);
+  R = cgetg(l*c+1, t_VEC);
+  for (i = 1, k = 1; i <= l; ++i)
+  {
+    GEN S = gel(L,i), Selt = group_set(S,n);
+    GEN gen = grp_get_gen(S);
+    GEN ord = grp_get_ord(S);
+    long j;
+    for (j = 1; j <= c; ++j)
+    {
+      GEN p = gel(C,j);
+      if (perm_relorder(p, Selt) == r && group_perm_normalize(S, p))
+        gel(R,k++) = mkvec2(vecsmall_append(gen, (long)p),
+                            vecsmall_append(ord, r));
+    }
+  }
+  setlg(R, k);
+  return gerepilecopy(ltop, R);
+}
+
+/* H is a normal subgroup, C is the quotient map G -->G/H,
+ * S is a subgroup of G/H, and G is embedded in Sym(l)
+ * Return all the subgroups K of G such that
+ * S= K mod H and K inter H={1} */
+static GEN
+liftsubgroup(GEN C, GEN H, GEN S)
+{
+  pari_sp ltop = avma;
+  GEN V = trivialsubgroups();
+  GEN Sgen = grp_get_gen(S);
+  GEN Sord = grp_get_ord(S);
+  GEN Cgen = quo_get_gen(C);
+  long n = lg(Sgen), i;
+  for (i = 1; i < n; ++i)
+  { /*loop over generators of S*/
+    GEN W = group_leftcoset(H, gel(Cgen, mael(Sgen, i, 1)));
+    V = liftlistsubgroups(V, W, Sord[i]);
+  }
+  return gerepilecopy(ltop,V);
+}
+
+/* 1:A4 2:S4 0: other */
+long
+group_isA4S4(GEN G)
+{
+  GEN elt = grp_get_gen(G);
+  GEN ord = grp_get_ord(G);
+  long n = lg(ord);
+  if (n != 4 && n != 5) return 0;
+  if (ord[1]!=2 || ord[2]!=2 || ord[3]!=3) return 0;
+  if (perm_commute(gel(elt,1),gel(elt,3))) return 0;
+  if (n==4) return 1;
+  if (ord[4]!=2) return 0;
+  if (perm_commute(gel(elt,3),gel(elt,4))) return 0;
+  return 2;
+}
+/* compute all the subgroups of a group G */
+GEN
+group_subgroups(GEN G)
+{
+  pari_sp ltop = avma;
+  GEN p1, H, C, Q, M, sg1, sg2, sg3;
+  GEN gen = grp_get_gen(G);
+  GEN ord = grp_get_ord(G);
+  long lM, i, j, n = lg(gen);
+  if (n == 1) return trivialsubgroups();
+  if (group_isA4S4(G))
+  {
+    GEN s = gel(gen,1);       /*s = (1,2)(3,4) */
+    GEN t = gel(gen,2);       /*t = (1,3)(2,4) */
+    GEN st = perm_mul(s, t); /*st = (1,4)(2,3) */
+    H = dicyclicgroup(s, t, 2, 2);
+    /* sg3 is the list of subgroups intersecting only partially with H*/
+    sg3 = cgetg((n==4)?4: 10, t_VEC);
+    gel(sg3,1) = cyclicgroup(s, 2);
+    gel(sg3,2) = cyclicgroup(t, 2);
+    gel(sg3,3) = cyclicgroup(st, 2);
+    if (n==5)
+    {
+      GEN u = gel(gen,3);
+      GEN v = gel(gen,4), w, u2;
+      if (zv_equal(perm_conj(u,s), t)) /*u=(2,3,4)*/
+        u2 = perm_mul(u,u);
+      else
+      {
+        u2 = u;
+        u = perm_mul(u,u);
+      }
+      if (perm_order(v)==2)
+      {
+        if (!perm_commute(s,v)) /*v=(1,2)*/
+        {
+          v = perm_conj(u,v);
+          if (!perm_commute(s,v)) v = perm_conj(u,v);
+        }
+        w = perm_mul(v,t); /*w=(1,4,2,3)*/
+      }
+      else
+      {
+        w = v;
+        if (!zv_equal(perm_mul(w,w), s)) /*w=(1,4,2,3)*/
+        {
+          w = perm_conj(u,w);
+          if (!zv_equal(perm_mul(w,w), s)) w = perm_conj(u,w);
+        }
+        v = perm_mul(w,t); /*v=(1,2)*/
+      }
+      gel(sg3,4) = dicyclicgroup(s,v,2,2);
+      gel(sg3,5) = dicyclicgroup(t,perm_conj(u,v),2,2);
+      gel(sg3,6) = dicyclicgroup(st,perm_conj(u2,v),2,2);
+      gel(sg3,7) = dicyclicgroup(s,w,2,2);
+      gel(sg3,8) = dicyclicgroup(t,perm_conj(u,w),2,2);
+      gel(sg3,9) = dicyclicgroup(st,perm_conj(u2,w),2,2);
+    }
+  }
+  else
+  {
+    long osig = mael(factoru(ord[1]), 1, 1);
+    GEN sig = perm_pow(gel(gen,1), ord[1]/osig);
+    H = cyclicgroup(sig,osig);
+    sg3 = NULL;
+  }
+  C = group_quotient(G,H);
+  Q = quotient_group(C,G);
+  M = group_subgroups(Q); lM = lg(M);
+  /* sg1 is the list of subgroups containing H*/
+  sg1 = cgetg(lM, t_VEC);
+  for (i = 1; i < lM; ++i) gel(sg1,i) = quotient_subgroup_lift(C,H,gel(M,i));
+  /*sg2 is a list of lists of subgroups not intersecting with H*/
+  sg2 = cgetg(lM, t_VEC);
+  /* Loop over all subgroups of G/H */
+  for (j = 1; j < lM; ++j) gel(sg2,j) = liftsubgroup(C, H, gel(M,j));
+  p1 = concat(sg1, shallowconcat1(sg2));
+  if (sg3)
+  {
+    p1 = concat(p1, sg3);
+    if (n==5) /*ensure that the D4 subgroups of S4 are in supersolvable format*/
+      for(j = 3; j <= 5; j++)
+      {
+        GEN c = gmael(p1,j,1);
+        if (!perm_commute(gel(c,1),gel(c,3)))
+        {
+          if (perm_commute(gel(c,2),gel(c,3))) { swap(gel(c,1), gel(c,2)); }
+          else
+            perm_mul_inplace2(gel(c,2), gel(c,1));
+        }
+      }
+  }
+  return gerepileupto(ltop,p1);
+}
+
+/*return 1 if G is abelian, else 0*/
+long
+group_isabelian(GEN G)
+{
+  GEN g = grp_get_gen(G);
+  long i, j, n = lg(g);
+  for(i=2; i<n; i++)
+    for(j=1; j<i; j++)
+      if (!perm_commute(gel(g,i), gel(g,j))) return 0;
+  return 1;
+}
+
+/*If G is abelian, return its HNF matrix*/
+GEN
+group_abelianHNF(GEN G, GEN S)
+{
+  GEN M, g = grp_get_gen(G), o = grp_get_ord(G);
+  long i, j, k, n = lg(g);
+  if (!group_isabelian(G)) return NULL;
+  if (n==1) return cgetg(1,t_MAT);
+  if (!S) S = group_elts(G, group_domain(G));
+  M = cgetg(n,t_MAT);
+  for(i=1; i<n; i++)
+  {
+    GEN P, C = cgetg(n,t_COL);
+    pari_sp av = avma;
+    gel(M,i) = C;
+    P = perm_pow(gel(g,i), o[i]);
+    for(j=1; j<lg(S); j++)
+      if (zv_equal(P, gel(S,j))) break;
+    avma = av;
+    if (j==lg(S)) pari_err_BUG("galoisisabelian [inconsistent group]");
+    j--;
+    for(k=1; k<i; k++)
+    {
+      long q = j / o[k];
+      gel(C,k) = stoi(j - q*o[k]);
+      j = q;
+    }
+    gel(C,k) = stoi(o[i]);
+    for (k++; k<n; k++) gel(C,k) = gen_0;
+  }
+  return M;
+}
+
+/*If G is abelian, return its abstract SNF matrix*/
+GEN
+group_abelianSNF(GEN G, GEN L)
+{
+  pari_sp ltop = avma;
+  GEN H = group_abelianHNF(G,L);
+  if (!H) return NULL;
+  return gerepileupto(ltop, smithclean( ZM_snf(H) ));
+}
+
+GEN
+abelian_group(GEN v)
+{
+  long card = zv_prod(v), i, d = 1, l = lg(v);
+  GEN G = cgetg(3,t_VEC), gen = cgetg(l,t_VEC);
+  gel(G,1) = gen;
+  gel(G,2) = vecsmall_copy(v);
+  for(i=1; i<l; i++)
+  {
+    GEN p = cgetg(card+1, t_VECSMALL);
+    long o = v[i], u = d*(o-1), j, k, l;
+    gel(gen, i) = p;
+    /* The following loop is over-optimized. Remember that I wrote it for
+     * testpermutation. Something has survived... BA */
+    for(j=1;j<=card;)
+    {
+      for(k=1;k<o;k++)
+        for(l=1;l<=d; l++,j++) p[j] = j+d;
+      for (l=1; l<=d; l++,j++) p[j] = j-u;
+    }
+    d += u;
+  }
+  return G;
+}
+
+/*return 1 if H is a normal subgroup of G*/
+long
+group_subgroup_isnormal(GEN G, GEN H)
+{
+  GEN g = grp_get_gen(G);
+  long i, n = lg(g);
+  if (lg(grp_get_gen(H)) > 1 && group_domain(G) != group_domain(H))
+    pari_err_DOMAIN("group_subgroup_isnormal","domain(H)","!=",
+                    strtoGENstr("domain(G)"), H);
+  for(i=1; i<n; i++)
+    if (!group_perm_normalize(H, gel(g,i))) return 0;
+  return 1;
+}
+
+GEN
+groupelts_center(GEN S)
+{
+  pari_sp ltop = avma;
+  long i, j, n = lg(S)-1, l = n;
+  GEN V, elts = zero_F2v(n+1);
+  for(i=1; i<=n; i++)
+  {
+    if (F2v_coeff(elts,i)) { l--;  continue; }
+    for(j=1; j<=n; j++)
+      if (!perm_commute(gel(S,i),gel(S,j)))
+      {
+        F2v_set(elts,i);
+        F2v_set(elts,j); l--; break;
+      }
+  }
+  V = cgetg(l+1,t_VEC);
+  for (i=1, j=1; i<=n ;i++)
+    if (!F2v_coeff(elts,i)) gel(V,j++) = vecsmall_copy(gel(S,i));
+  return gerepileupto(ltop,V);
+}
+
+/* S a list of generators */
+GEN
+groupelts_abelian_group(GEN S)
+{
+  pari_sp ltop = avma;
+  GEN Qgen, Qord, Qelt;
+  long i, j, n = lg(gel(S,1))-1, l = lg(S);
+  Qord = cgetg(l, t_VECSMALL);
+  Qgen = cgetg(l, t_VEC);
+  Qelt = mkvec(identity_perm(n));
+  for (i = 1, j = 1; i < l; ++i)
+  {
+    GEN  g = gel(S,i);
+    long o = perm_relorder(g, groupelts_set(Qelt, n));
+    gel(Qgen,j) = g;
+    Qord[j] = o;
+    if (o != 1) { Qelt = perm_generate(g, Qelt, o); j++; }
+  }
+  setlg(Qgen,j);
+  setlg(Qord,j);
+  return gerepilecopy(ltop, mkvec2(Qgen, Qord));
+}
+
+GEN
+group_export_GAP(GEN G)
+{
+  pari_sp av = avma;
+  GEN s, comma, g = grp_get_gen(G);
+  long i, k, l = lg(g);
+  if (l == 1) return strtoGENstr("Group(())");
+  s = cgetg(2*l, t_VEC);
+  comma = strtoGENstr(", ");
+  gel(s,1) = strtoGENstr("Group(");
+  for (i=1, k=2; i < l; ++i)
+  {
+    if (i > 1) gel(s,k++) = comma;
+    gel(s,k++) = perm_to_GAP(gel(g,i));
+  }
+  gel(s,k++) = strtoGENstr(")");
+  return gerepilecopy(av, shallowconcat1(s));
+}
+
+GEN
+group_export_MAGMA(GEN G)
+{
+  pari_sp av = avma;
+  GEN s, comma, g = grp_get_gen(G);
+  long i, k, l = lg(g);
+  if (l == 1) return strtoGENstr("PermutationGroup<1|>");
+  s = cgetg(2*l, t_VEC);
+  comma = strtoGENstr(", ");
+  gel(s,1) = gsprintf("PermutationGroup<%ld|",group_domain(G));
+  for (i=1, k=2; i < l; ++i)
+  {
+    if (i > 1) gel(s,k++) = comma;
+    gel(s,k++) = GENtoGENstr( vecsmall_to_vec(gel(g,i)) );
+  }
+  gel(s,k++) = strtoGENstr(">");
+  return gerepilecopy(av, shallowconcat1(s));
+}
+
+GEN
+group_export(GEN G, long format)
+{
+  switch(format)
+  {
+  case 0: return group_export_GAP(G);
+  case 1: return group_export_MAGMA(G);
+  }
+  pari_err_FLAG("galoisexport");
+  return NULL; /*-Wall*/
+}
diff --git a/src/basemath/polarit1.c b/src/basemath/polarit1.c
new file mode 100644
index 0000000..8ecc9d8
--- /dev/null
+++ b/src/basemath/polarit1.c
@@ -0,0 +1,635 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**               ARITHMETIC OPERATIONS ON POLYNOMIALS                **/
+/**                         (first part)                              **/
+/**                                                                   **/
+/***********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+/*******************************************************************/
+/*                                                                 */
+/*                  POLYNOMIAL EUCLIDEAN DIVISION                  */
+/*                                                                 */
+/*******************************************************************/
+/* x t_POLMOD, y t_POL in the same variable as x[1], return x % y */
+static GEN
+polmod_mod(GEN x, GEN y)
+{
+  GEN z, a, T = gel(x,1);
+  if (RgX_equal(T, y)) return gcopy(x);
+  z = cgetg(3,t_POLMOD); T = RgX_gcd(T,y); a = gel(x,2);
+  gel(z,1) = T;
+  gel(z,2) = (typ(a)==t_POL && varn(a)==varn(T))? RgX_rem(a, T): gcopy(a);
+  return z;
+}
+/* x,y two "scalars", return 0 with type info */
+static GEN
+rem_scal_scal(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN z = gadd(gmul(gen_0,x), gmul(gen_0,y));
+  if (gequal0(y)) pari_err_INV("grem",y);
+  return gerepileupto(av, simplify(z));
+}
+/* x pol, y "scalar", return 0 with type info */
+static GEN
+rem_pol_scal(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  if (gequal0(y)) pari_err_INV("grem",y);
+  return gerepileupto(av, simplify(gmul(RgX_get_0(x),y)));
+}
+/* x "scalar", y pol, return x % y with type info */
+static GEN
+rem_scal_pol(GEN x, GEN y)
+{
+  if (degpol(y))
+  {
+    if (!signe(y)) pari_err_INV("grem",y);
+    return gmul(x, RgX_get_1(y));
+  }
+  y = gel(y,2); return rem_scal_scal(x,y);
+}
+GEN
+poldivrem(GEN x, GEN y, GEN *pr)
+{
+  const char *f = "euclidean division";
+  long tx = typ(x), ty = typ(y), vx = gvar(x), vy = gvar(y);
+  GEN z;
+
+  if (!is_extscalar_t(tx) || !is_extscalar_t(ty)) pari_err_TYPE2(f,x,y);
+  if (vx == vy && ((tx==t_POLMOD) ^ (ty==t_POLMOD))) pari_err_TYPE2(f,x,y);
+  if (ty != t_POL || varncmp(vx, vy) < 0) /* y "scalar" */
+  {
+    if (!pr || pr == ONLY_DIVIDES) return gdiv(x,y);
+    if (tx != t_POL || varncmp(vy, vx) < 0) /* x "scalar" */
+      z = rem_scal_scal(x,y);
+    else
+      z = rem_pol_scal(x,y);
+    if (pr == ONLY_REM) return z;
+    *pr = z; return gdiv(x,y);
+  }
+  if (tx != t_POL || varncmp(vx, vy) > 0) /* x "scalar" */
+  {
+    if (!degpol(y)) /* constant t_POL, treat as scalar */
+    {
+      y = gel(y,2);
+      if (!pr || pr == ONLY_DIVIDES) gdiv(x,y);
+      z = rem_scal_scal(x,y);
+      if (pr == ONLY_REM) return z;
+      *pr = z; return gdiv(x,y);
+    }
+    if (!signe(y)) pari_err_INV("poldivrem",y);
+    if (!pr || pr == ONLY_DIVIDES) return gequal0(x)? RgX_get_0(y): NULL;
+    z = gmul(x, RgX_get_1(y));
+    if (pr == ONLY_REM) return z;
+    *pr = z; return RgX_get_0(y);
+  }
+  return RgX_divrem(x,y,pr);
+}
+GEN
+gdeuc(GEN x, GEN y)
+{
+  const char *f = "euclidean division";
+  long tx = typ(x), ty = typ(y), vx = gvar(x), vy = gvar(y);
+  if (!is_extscalar_t(tx) || !is_extscalar_t(ty)) pari_err_TYPE2(f,x,y);
+  if (vx == vy && ((tx==t_POLMOD) ^ (ty==t_POLMOD))) pari_err_TYPE2(f,x,y);
+  if (ty != t_POL || varncmp(vx, vy) < 0) return gdiv(x,y); /* y "scalar" */
+  if (tx != t_POL || varncmp(vx, vy) > 0)
+  { /* x "scalar" */
+    if (!signe(y)) pari_err_INV("gdeuc",y);
+    if (!degpol(y)) return gdiv(x, gel(y,2)); /* constant */
+    return RgX_get_0(y);
+  }
+  return RgX_div(x,y);
+}
+GEN
+grem(GEN x, GEN y)
+{
+  const char *f = "euclidean division";
+  long tx = typ(x), ty = typ(y), vx = gvar(x), vy = gvar(y);
+
+  if (ty == t_POL)
+  {
+    if (varncmp(vx,vy) >= 0)
+    {
+      pari_sp av;
+      GEN z;
+      if (!signe(y)) pari_err_INV("grem",y);
+      if (vx != vy) return rem_scal_pol(x,y);
+      switch(tx)
+      {
+        case t_POLMOD: return polmod_mod(x,y);
+        case t_POL: return RgX_rem(x,y);
+        case t_RFRAC:
+          av = avma; z = gmul(gel(x,1), RgXQ_inv(gel(x,2),y));
+          return gerepileupto(av, grem(z,y));
+        case t_SER:
+          if (RgX_is_monomial(y))
+          {
+            if (lg(x)-2 + valp(x) < degpol(y)) pari_err_OP("%",x,y);
+            av = avma;
+            return gerepileupto(av, gmod(ser2rfrac_i(x), y));
+          }
+        default: pari_err_TYPE2("%",x,y);
+      }
+    }
+    else switch(tx)
+    {
+      case t_POL:
+      case t_RFRAC: return rem_pol_scal(x,y);
+      default: pari_err_TYPE2("%",x,y);
+    }
+  }
+  if (!is_extscalar_t(tx) || !is_extscalar_t(ty)) pari_err_TYPE2(f,x,y);
+  if (vx == vy && ty==t_POLMOD) pari_err_TYPE2(f,x,y);
+  if (tx != t_POL || varncmp(vx,vy) > 0)
+  { /* x a "scalar" */
+    if (ty != t_POL || varncmp(vx, vy) < 0) return rem_scal_scal(x,y);
+    return rem_scal_pol(x,y);
+  }
+  if (ty != t_POL || varncmp(vx, vy) < 0) /* y a "scalar" */
+    return rem_pol_scal(x,y);
+  return RgX_rem(x,y);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                  CONVERSIONS RELATED TO p-ADICS                 */
+/*                                                                 */
+/*******************************************************************/
+/* x t_PADIC, p a prime or NULL (unset). Consistency check */
+static void
+check_padic_p(GEN x, GEN p)
+{
+  GEN q = gel(x,2);
+  if (p && !equalii(p, q)) pari_err_MODULUS("Zp_to_Z", p,q);
+}
+/* shallow */
+static GEN
+Zp_to_Z(GEN x, GEN p) {
+  switch(typ(x))
+  {
+    case t_INT: break;
+    case t_PADIC:
+      check_padic_p(x, p);
+      x = gtrunc(x); break;
+    default: pari_err_TYPE("Zp_to_Z",x);
+  }
+  return x;
+}
+/* shallow */
+static GEN
+ZpX_to_ZX(GEN f, GEN p) {
+  long i, l = lg(f);
+  GEN F = cgetg_copy(f, &l); F[1] = f[1];
+  for (i=2; i<l; i++) gel(F,i) = Zp_to_Z(gel(f,i), p);
+  return F;
+}
+
+static GEN
+get_padic_content(GEN f, GEN p)
+{
+  GEN c = content(f);
+  if (gequal0(c)) /*  O(p^n) can occur */
+  {
+    if (typ(c) != t_PADIC) pari_err_TYPE("QpX_to_ZX",f);
+    check_padic_p(c, p);
+    c = powis(p, valp(c));
+  }
+  return c;
+}
+/* make f suitable for [root|factor]padic. Shallow */
+static GEN
+QpX_to_ZX(GEN f, GEN p)
+{
+  GEN c = get_padic_content(f, p);
+  f = RgX_Rg_div(f, c);
+  return ZpX_to_ZX(f, p);
+}
+
+/* x in Z return x + O(pr), pr = p^r. Shallow */
+static GEN
+Z_to_Zp(GEN x, GEN p, GEN pr, long r)
+{
+  GEN y;
+  long v, sx = signe(x);
+
+  if (!sx) return zeropadic_shallow(p,r);
+  v = Z_pvalrem(x,p,&x);
+  if (v) {
+    if (r <= v) return zeropadic_shallow(p,r);
+    r -= v;
+    pr = powiu(p,r);
+  }
+  y = cgetg(5,t_PADIC);
+  y[1] = evalprecp(r)|evalvalp(v);
+  gel(y,2) = p;
+  gel(y,3) = pr;
+  gel(y,4) = modii(x,pr); return y;
+}
+/* shallow */
+static GEN
+ZV_to_ZpV(GEN z, GEN p, long prec)
+{
+  long i, l = lg(z);
+  GEN Z = cgetg(l, typ(z)), q = powiu(p, prec);
+  for (i=1; i<lg(z); i++) gel(Z,i) = Z_to_Zp(gel(z,i),p,q,prec);
+  return Z;
+}
+/* shallow */
+static GEN
+ZX_to_ZpX(GEN z, GEN p, GEN q, long prec)
+{
+  long i, l = lg(z);
+  GEN Z = cgetg(l, t_POL); Z[1] = z[1];
+  for (i=2; i<lg(z); i++) gel(Z,i) = Z_to_Zp(gel(z,i),p,q,prec);
+  return Z;
+}
+/* return (x + O(p^r)) normalized (multiply by a unit such that leading coeff
+ * is a power of p), x in Z[X] (or Z_p[X]). Shallow */
+static GEN
+ZX_to_ZpX_normalized(GEN x, GEN p, GEN pr, long r)
+{
+  long i, lx = lg(x);
+  GEN z, lead = leading_term(x);
+
+  if (gequal1(lead)) return ZX_to_ZpX(x, p, pr, r);
+  (void)Z_pvalrem(lead, p, &lead); lead = Fp_inv(lead, pr);
+  z = cgetg(lx,t_POL);
+  for (i=2; i < lx; i++) gel(z,i) = Z_to_Zp(mulii(lead,gel(x,i)),p,pr,r);
+  z[1] = x[1]; return z;
+}
+static GEN
+ZXV_to_ZpXQV(GEN z, GEN T, GEN p, long prec)
+{
+  long i, l = lg(z);
+  GEN Z = cgetg(l, typ(z)), q = powiu(p, prec);
+  T = ZX_copy(T);
+  for (i=1; i<lg(z); i++) gel(Z,i) = mkpolmod(ZX_to_ZpX(gel(z,i),p,q,prec),T);
+  return Z;
+}
+/* shallow */
+static GEN
+QpXQX_to_ZXY(GEN f, GEN p)
+{
+  GEN c = get_padic_content(f, p);
+  long i, l = lg(f);
+  f = RgX_Rg_div(f,c);
+  for (i=2; i<l; i++)
+  {
+    GEN t = gel(f,i);
+    switch(typ(t))
+    {
+      case t_POLMOD:
+        t = gel(t,2);
+        t = (typ(t) == t_POL)? ZpX_to_ZX(t, p): Zp_to_Z(t, p);
+        break;
+      case t_POL: t = ZpX_to_ZX(t, p); break;
+      default: t = Zp_to_Z(t, p); break;
+    }
+    gel(f,i) = t;
+  }
+  return f;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         p-ADIC ROOTS                            */
+/*                                                                 */
+/*******************************************************************/
+
+/* f primitive ZX, squarefree, leading term prime to p; a in Z such that
+ * f(a) = 0 mod p. Return p-adic roots of f equal to a mod p, in
+ * precision >= prec */
+GEN
+ZX_Zp_root(GEN f, GEN a, GEN p, long prec)
+{
+  GEN z, R, a0 = modii(a, p);
+  long i, j, k, v;
+
+  if (signe(FpX_eval(FpX_deriv(f, p), a0, p)))
+  { /* simple zero mod p, go all the way to p^prec */
+    if (prec > 1) a0 = ZpX_liftroot(f, a0, p, prec);
+    return mkcol(a0);
+  }
+
+  f = ZX_unscale_div(RgX_translate(f,a), p); /* f(pX + a) / p */
+  v = ZX_pval(f,p);
+  if (v) f = ZX_Z_divexact(f, powiu(p,v));
+  z = cgetg(degpol(f)+1,t_COL);
+
+  R = FpX_roots(f, p);
+  for (j=i=1; i<lg(R); i++)
+  {
+    GEN u = ZX_Zp_root(f, gel(R,i), p, prec-1);
+    for (k=1; k<lg(u); k++) gel(z,j++) = addii(a, mulii(p, gel(u,k)));
+  }
+  setlg(z,j); return z;
+}
+
+/* a t_PADIC, return vector of p-adic roots of f equal to a (mod p)
+ * We assume 1) f(a) = 0 mod p,
+ *           2) leading coeff prime to p. */
+GEN
+Zp_appr(GEN f, GEN a)
+{
+  pari_sp av = avma;
+  long prec;
+  GEN z, p;
+  p = gel(a,2); prec = gequal0(a)? valp(a): precp(a);
+  f = QpX_to_ZX(f, p);
+  if (degpol(f) <= 0) pari_err_CONSTPOL("Zp_appr");
+  (void)ZX_gcd_all(f, ZX_deriv(f), &f);
+  z = ZX_Zp_root(f, gtrunc(a), p, prec);
+  return gerepilecopy(av, ZV_to_ZpV(z, p, prec));
+}
+/* vector of p-adic roots of the ZX f, leading term prime to p. Shallow */
+static GEN
+ZX_Zp_roots(GEN f, GEN p, long prec)
+{
+  GEN y, z, rac;
+  long lx, i, j, k;
+
+  (void)ZX_gcd_all(f, ZX_deriv(f), &f);
+  rac = FpX_roots(f, p);
+  lx = lg(rac); if (lx == 1) return rac;
+  y = cgetg(degpol(f)+1,t_COL);
+  for (j=i=1; i<lx; i++)
+  {
+    z = ZX_Zp_root(f, gel(rac,i), p, prec);
+    for (k=1; k<lg(z); k++,j++) gel(y,j) = gel(z,k);
+  }
+  setlg(y,j); return ZV_to_ZpV(y, p, prec);
+}
+
+/* f a ZX */
+static GEN
+pnormalize(GEN f, GEN p, long prec, long n, GEN *plead, long *pprec, int *prev)
+{
+  *plead = leading_term(f);
+  *pprec = prec;
+  *prev = 0;
+  if (!is_pm1(*plead))
+  {
+    long v = Z_pval(*plead,p), v1 = Z_pval(constant_term(f),p);
+    if (v1 < v)
+    {
+      *prev = 1; f = RgX_recip_shallow(f);
+     /* beware loss of precision from lc(factor), whose valuation is <= v */
+      *pprec += v; v = v1;
+    }
+    *pprec += v * n;
+  }
+  return ZX_Q_normalize(f, plead);
+}
+
+/* return p-adic roots of f, precision prec */
+GEN
+rootpadic(GEN f, GEN p, long prec)
+{
+  pari_sp av = avma;
+  GEN lead,y;
+  long PREC,i,k;
+  int reverse;
+
+  if (typ(p)!=t_INT) pari_err_TYPE("rootpadic",p);
+  if (typ(f)!=t_POL) pari_err_TYPE("rootpadic",f);
+  if (gequal0(f)) pari_err_ROOTS0("rootpadic");
+  if (prec <= 0)
+    pari_err_DOMAIN("rootpadic", "precision", "<=",gen_0,stoi(prec));
+  f = QpX_to_ZX(f, p);
+  f = pnormalize(f, p, prec, 1, &lead, &PREC, &reverse);
+  y = ZX_Zp_roots(f,p,PREC);
+  k = lg(y);
+  if (lead != gen_1)
+    for (i=1; i<k; i++) gel(y,i) = gdiv(gel(y,i), lead);
+  if (reverse)
+    for (i=1; i<k; i++) gel(y,i) = ginv(gel(y,i));
+  return gerepilecopy(av, y);
+}
+/* p is prime
+ * f in a ZX, with leading term prime to p.
+ * f must have no multiple roots mod p.
+ *
+ * return p-adics roots of f with prec p^e, as integers (implicitly mod p^e) */
+GEN
+rootpadicfast(GEN f, GEN p, long e)
+{
+  pari_sp av = avma;
+  GEN y, S = FpX_roots(f, p); /*no multiplicity*/
+  if (lg(S)==1) { avma = av; return cgetg(1,t_COL); }
+  S = gclone(S); avma = av;
+  y = ZpX_liftroots(f,S,p,e);
+  gunclone(S); return y;
+}
+/**************************************************************************/
+
+static void
+scalar_getprec(GEN x, long *pprec, GEN *pp)
+{
+  if (typ(x)==t_PADIC)
+  {
+    long e = valp(x); if (signe(gel(x,4))) e += precp(x);
+    if (e < *pprec) *pprec = e;
+    check_padic_p(x, *pp);
+    *pp = gel(x,2);
+  }
+}
+static void
+getprec(GEN x, long *pprec, GEN *pp)
+{
+  long i;
+  if (typ(x) != t_POL) scalar_getprec(x, pprec, pp);
+  else
+    for (i = lg(x)-1; i>1; i--) scalar_getprec(gel(x,i), pprec, pp);
+}
+
+static GEN
+ZXY_ZpQ_root(GEN f, GEN a, GEN T, GEN p, long prec)
+{
+  GEN z, R;
+  long i, j, k, lR;
+  if (signe(FqX_eval(FqX_deriv(f,T,p), a, T,p)))
+  { /* simple zero mod (T,p), go all the way to p^prec */
+    if (prec > 1) a = ZpXQX_liftroot(f, a, T, p, prec);
+    return mkcol(a);
+  }
+  f = RgX_unscale(RgXQX_translate(f, a, T), p);
+  f = RgX_Rg_div(f, powiu(p, gvaluation(f,p)));
+  z = cgetg(degpol(f)+1,t_COL);
+  R = FqX_roots(FqX_red(f,T,p), T, p); lR = lg(R);
+  for(j=i=1; i<lR; i++)
+  {
+    GEN u = ZXY_ZpQ_root(f, gel(R,i), T, p, prec-1);
+    for (k=1; k<lg(u); k++) gel(z,j++) = gadd(a, gmul(p, gel(u,k)));
+  }
+  setlg(z,j); return z;
+}
+
+/* a belongs to finite extension of Q_p, return all roots of f equal to a
+ * mod p. We assume f(a) = 0 (mod p) [mod 4 if p=2] */
+GEN
+padicappr(GEN f, GEN a)
+{
+  GEN p, z, T;
+  long prec;
+  pari_sp av = avma;
+
+  if (typ(f)!=t_POL) pari_err_TYPE("padicappr",f);
+  switch(typ(a)) {
+    case t_PADIC: return Zp_appr(f,a);
+    case t_POLMOD: break;
+    default: pari_err_TYPE("padicappr",a);
+  }
+  if (gequal0(f)) pari_err_ROOTS0("padicappr");
+  z = RgX_gcd(f, RgX_deriv(f));
+  if (degpol(z) > 0) f = RgX_div(f,z);
+  T = gel(a,1); a = gel(a,2);
+  p = NULL; prec = LONG_MAX;
+  getprec(a, &prec, &p);
+  getprec(T, &prec, &p); if (!p) pari_err_TYPE("padicappr",T);
+  f = QpXQX_to_ZXY(f, p);
+  a = QpX_to_ZX(a,p);
+  T = QpX_to_ZX(T,p);
+  z = ZXY_ZpQ_root(f, a, T, p, prec);
+  return gerepilecopy(av, ZXV_to_ZpXQV(z, T, p, prec));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*             FACTORIZATION in Zp[X], using ROUND4                */
+/*                                                                 */
+/*******************************************************************/
+
+int
+cmp_padic(GEN x, GEN y)
+{
+  long vx, vy;
+  if (x == gen_0) return -1;
+  if (y == gen_0) return  1;
+  vx = valp(x);
+  vy = valp(y);
+  if (vx < vy) return  1;
+  if (vx > vy) return -1;
+  return cmpii(gel(x,4), gel(y,4));
+}
+
+static int
+expo_is_squarefree(GEN e)
+{
+  long i, l = lg(e);
+  for (i=1; i<l; i++)
+    if (e[i] != 1) return 0;
+  return 1;
+}
+
+/* assume f a ZX with leading_term 1, degree > 0 */
+GEN
+ZX_monic_factorpadic(GEN f, GEN p, long prec)
+{
+  GEN w, poly, p1, p2, ex, P, E;
+  long n=degpol(f), i, k, j;
+
+  if (n==1) return mkmat2(mkcol(f), mkcol(gen_1));
+
+  poly = ZX_squff(f,&ex);
+  P = cgetg(n+1,t_COL);
+  E = cgetg(n+1,t_COL); n = lg(poly);
+  for (j=i=1; i<n; i++)
+  {
+    pari_sp av1 = avma;
+    GEN fx = gel(poly,i), fa = FpX_factor(fx,p);
+    w = gel(fa,1);
+    if (expo_is_squarefree(gel(fa,2)))
+    { /* no repeated factors: Hensel lift */
+      p1 = ZpX_liftfact(fx, w, NULL, p, prec, powiu(p,prec));
+      p2 = utoipos(ex[i]);
+      for (k=1; k<lg(p1); k++,j++)
+      {
+        gel(P,j) = gel(p1,k);
+        gel(E,j) = p2;
+      }
+      continue;
+    }
+    /* use Round 4 */
+    p2 = maxord_i(p, fx, ZpX_disc_val(fx,p), w, prec);
+    if (p2)
+    {
+      p2 = gerepilecopy(av1,p2);
+      p1 = gel(p2,1);
+      p2 = gel(p2,2);
+      for (k=1; k<lg(p1); k++,j++)
+      {
+        gel(P,j) = gel(p1,k);
+        gel(E,j) = muliu(gel(p2,k),ex[i]);
+      }
+    }
+    else
+    {
+      avma = av1;
+      gel(P,j) = fx;
+      gel(E,j) = utoipos(ex[i]); j++;
+    }
+  }
+  setlg(P,j);
+  setlg(E,j); return mkmat2(P, E);
+}
+
+GEN
+factorpadic(GEN f,GEN p,long prec)
+{
+  pari_sp av = avma;
+  GEN y, P, ppow, lead, lt;
+  long i, l, pr, n = degpol(f);
+  int reverse = 0;
+
+  if (n == 0) return trivial_fact();
+
+  f = QpX_to_ZX(f, p); (void)Z_pvalrem(leading_term(f), p, &lt);
+  f = pnormalize(f, p, prec, n-1, &lead, &pr, &reverse);
+  y = ZX_monic_factorpadic(f, p, pr);
+  P = gel(y,1); l = lg(P);
+  if (lead != gen_1)
+    for (i=1; i<l; i++) gel(P,i) = Q_primpart( RgX_unscale(gel(P,i), lead) );
+  ppow = powiu(p,prec);
+  for (i=1; i<l; i++)
+  {
+    GEN t = gel(P,i);
+    if (reverse) t = normalizepol(RgX_recip_shallow(t));
+    gel(P,i) = ZX_to_ZpX_normalized(t,p,ppow,prec);
+  }
+  if (!gequal1(lt)) gel(P,1) = gmul(gel(P,1), lt);
+  return gerepilecopy(av, sort_factor_pol(y, cmp_padic));
+}
+
+/* deprecated: backward compatibility */
+GEN
+factorpadic0(GEN f,GEN p,long r,long flag)
+{
+  if (typ(f)!=t_POL) pari_err_TYPE("factorpadic",f);
+  if (typ(p)!=t_INT) pari_err_TYPE("factorpadic",p);
+  if (!signe(f)) return prime_fact(f);
+  if (r <= 0)
+    pari_err_DOMAIN("factorpadic", "precision", "<=",gen_0,stoi(r));
+  switch(flag)
+  {
+    case 0: case 1:
+       return factorpadic(f,p,r);
+     default: pari_err_FLAG("factorpadic");
+  }
+  return NULL; /* not reached */
+}
diff --git a/src/basemath/polarit2.c b/src/basemath/polarit2.c
new file mode 100644
index 0000000..1c39a5a
--- /dev/null
+++ b/src/basemath/polarit2.c
@@ -0,0 +1,3137 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**               ARITHMETIC OPERATIONS ON POLYNOMIALS                **/
+/**                         (second part)                             **/
+/**                                                                   **/
+/***********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+#define addshift(x,y) addshiftpol((x),(y),1)
+
+/* compute Newton sums S_1(P), ... , S_n(P). S_k(P) = sum a_j^k, a_j root of P
+ * If N != NULL, assume p-adic roots and compute mod N [assume integer coeffs]
+ * If T != NULL, compute mod (T,N) [assume integer coeffs if N != NULL]
+ * If y0!= NULL, precomputed i-th powers, i=1..m, m = length(y0).
+ * Not memory clean in the latter case */
+GEN
+polsym_gen(GEN P, GEN y0, long n, GEN T, GEN N)
+{
+  long dP=degpol(P), i, k, m;
+  pari_sp av1, av2;
+  GEN s,y,P_lead;
+
+  if (n<0) pari_err_IMPL("polsym of a negative n");
+  if (typ(P) != t_POL) pari_err_TYPE("polsym",P);
+  if (!signe(P)) pari_err_ROOTS0("polsym");
+  y = cgetg(n+2,t_COL);
+  if (y0)
+  {
+    if (typ(y0) != t_COL) pari_err_TYPE("polsym_gen",y0);
+    m = lg(y0)-1;
+    for (i=1; i<=m; i++) gel(y,i) = gel(y0,i); /* not memory clean */
+  }
+  else
+  {
+    m = 1;
+    gel(y,1) = stoi(dP);
+  }
+  P += 2; /* strip codewords */
+
+  P_lead = gel(P,dP); if (gequal1(P_lead)) P_lead = NULL;
+  if (P_lead)
+  {
+    if (N) P_lead = Fq_inv(P_lead,T,N);
+    else if (T) P_lead = QXQ_inv(P_lead,T);
+  }
+  for (k=m; k<=n; k++)
+  {
+    av1 = avma; s = (dP>=k)? gmulsg(k,gel(P,dP-k)): gen_0;
+    for (i=1; i<k && i<=dP; i++)
+      s = gadd(s, gmul(gel(y,k-i+1),gel(P,dP-i)));
+    if (N)
+    {
+      s = Fq_red(s, T, N);
+      if (P_lead) s = Fq_mul(s, P_lead, T, N);
+    }
+    else if (T)
+    {
+      s = grem(s, T);
+      if (P_lead) s = grem(gmul(s, P_lead), T);
+    }
+    else
+      if (P_lead) s = gdiv(s, P_lead);
+    av2 = avma; gel(y,k+1) = gerepile(av1,av2, gneg(s));
+  }
+  return y;
+}
+
+GEN
+polsym(GEN x, long n)
+{
+  return polsym_gen(x, NULL, n, NULL,NULL);
+}
+
+/* centered residue x mod p. po2 = shifti(p, -1) or NULL (euclidean residue) */
+GEN
+centermodii(GEN x, GEN p, GEN po2)
+{
+  GEN y = remii(x, p);
+  switch(signe(y))
+  {
+    case 0: break;
+    case 1: if (po2 && absi_cmp(y,po2) > 0) y = subii(y, p);
+      break;
+    case -1: if (!po2 || absi_cmp(y,po2) > 0) y = addii(y, p);
+      break;
+  }
+  return y;
+}
+
+static long
+s_centermod(long x, ulong pp, ulong pps2)
+{
+  long y = x % (long)pp;
+  if (y < 0) y += pp;
+  return Fl_center(y, pp,pps2);
+}
+
+/* for internal use */
+GEN
+centermod_i(GEN x, GEN p, GEN ps2)
+{
+  long i, lx;
+  pari_sp av;
+  GEN y;
+
+  if (!ps2) ps2 = shifti(p,-1);
+  switch(typ(x))
+  {
+    case t_INT: return centermodii(x,p,ps2);
+
+    case t_POL: lx = lg(x);
+      y = cgetg(lx,t_POL); y[1] = x[1];
+      for (i=2; i<lx; i++)
+      {
+        av = avma;
+        gel(y,i) = gerepileuptoint(av, centermodii(gel(x,i),p,ps2));
+      }
+      return normalizepol_lg(y, lx);
+
+    case t_COL: lx = lg(x);
+      y = cgetg(lx,t_COL);
+      for (i=1; i<lx; i++) gel(y,i) = centermodii(gel(x,i),p,ps2);
+      return y;
+
+    case t_MAT: lx = lg(x);
+      y = cgetg(lx,t_MAT);
+      for (i=1; i<lx; i++) gel(y,i) = centermod_i(gel(x,i),p,ps2);
+      return y;
+
+    case t_VECSMALL: lx = lg(x);
+    {
+      ulong pp = itou(p), pps2 = itou(ps2);
+      y = cgetg(lx,t_VECSMALL);
+      for (i=1; i<lx; i++) y[i] = s_centermod(x[i], pp, pps2);
+      return y;
+    }
+  }
+  return x;
+}
+
+GEN
+centermod(GEN x, GEN p) { return centermod_i(x,p,NULL); }
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          FACTORIZATION                            **/
+/**                                                                   **/
+/***********************************************************************/
+#define assign_or_fail(x,y) { GEN __x = x;\
+  if (y==NULL) y=__x; else if (!gequal(__x,y)) return 0;\
+}
+
+static const long tsh = 6;
+static long
+RgX_type_code(long t1, long t2) { return (t1 << tsh) | t2; }
+void
+RgX_type_decode(long x, long *t1, long *t2)
+{
+  *t1 = x >> tsh;
+  *t2 = (x & ((1L<<tsh)-1));
+}
+int
+RgX_type_is_composite(long t) { return t >= tsh; }
+
+long
+RgX_type(GEN x, GEN *ptp, GEN *ptpol, long *ptpa)
+{
+  long t[16];
+  long tx = typ(x), lx, i, j, s, pa = LONG_MAX;
+  GEN pcx=NULL, p=NULL, pol=NULL, ff=NULL;
+
+  if (is_scalar_t(tx))
+  {
+    if (tx == t_POLMOD) return 0;
+    x = scalarpol(x,0);
+  }
+  for (i=2; i<16; i++) t[i]=0;
+  /* t[0..1] unused. Other values, if set, indicate a coefficient of type
+   * t[2] : t_REAL
+   * t[3] : t_INTMOD
+   * t[4] : t_COMPLEX of rationals (t_INT/t_FRAC)
+   * t[5] : t_COMPLEX of t_REAL
+   * t[6] : t_COMPLEX of t_INTMOD
+   * t[7] : t_COMPLEX of t_PADIC
+   * t[8] : t_PADIC
+   * t[9] : t_QUAD of rationals (t_INT/t_FRAC)
+   * t[10]: t_QUAD of t_INTMOD
+   * t[11]: t_QUAD of t_PADIC
+   * t[12]: t_POLMOD of rationals (t_INT/t_FRAC)
+   * t[13]: t_POLMOD of t_INTMOD
+   * t[14]: t_POLMOD of t_PADIC
+   * t[15]: t_FFELT */
+  lx = lg(x);
+  for (i=2; i<lx; i++)
+  {
+    GEN c = gel(x,i);
+    switch(typ(c))
+    {
+      case t_INT: case t_FRAC:
+        break;
+      case t_REAL:
+        s = precision(c); if (s < pa) pa = s;
+        t[2]=1; break;
+      case t_INTMOD:
+        assign_or_fail(gel(c,1),p);
+        t[3]=1; break;
+      case t_FFELT:
+        if (ff==NULL) ff=c;
+        else if (!FF_samefield(c,ff)) return 0;
+        assign_or_fail(FF_p_i(c),p);
+        t[15]=1; break;
+      case t_COMPLEX:
+        if (!pcx) pcx = mkpoln(3, gen_1,gen_0,gen_1); /* x^2 + 1 */
+        for (j=1; j<=2; j++)
+        {
+          GEN d = gel(c,j);
+          switch(typ(d))
+          {
+            case t_INT: case t_FRAC:
+              assign_or_fail(pcx,pol);
+              t[4]=1; break;
+            case t_REAL:
+              s = precision(d); if (s < pa) pa = s;
+              t[5]=1; break;
+            case t_INTMOD:
+              assign_or_fail(gel(d,1),p);
+              if (!signe(p) || mod4(p) != 3) return 0;
+              assign_or_fail(pcx,pol);
+              t[6]=1; break;
+            case t_PADIC:
+              s = precp(d) + valp(d); if (s < pa) pa = s;
+              assign_or_fail(gel(d,2),p);
+              assign_or_fail(pcx,pol);
+              t[7]=1; break;
+            default: return 0;
+          }
+        }
+        break;
+      case t_PADIC:
+        s = precp(c) + valp(c); if (s < pa) pa = s;
+        assign_or_fail(gel(c,2),p);
+        t[8]=1; break;
+      case t_QUAD:
+        for (j=2; j<=3; j++)
+        {
+          GEN d = gel(c,j);
+          switch(typ(d))
+          {
+            case t_INT: case t_FRAC:
+              assign_or_fail(gel(c,1),pol);
+              t[9]=1; break;
+            case t_INTMOD:
+              assign_or_fail(gel(d,1),p);
+              assign_or_fail(gel(c,1),pol);
+              t[10]=1; break;
+            case t_PADIC:
+              s = precp(d) + valp(d); if (s < pa) pa = s;
+              assign_or_fail(gel(d,2),p);
+              assign_or_fail(gel(c,1),pol);
+              t[11]=1; break;
+            default: return 0;
+          }
+        }
+        break;
+      case t_POLMOD:
+        assign_or_fail(gel(c,1),pol);
+        for (j=1; j<=2; j++)
+        {
+          GEN pbis = NULL, polbis = NULL;
+          long pabis;
+          switch(RgX_type(gel(c,j),&pbis,&polbis,&pabis))
+          {
+            case t_INT: t[12]=1; break;
+            case t_INTMOD: t[13]=1; break;
+            case t_PADIC: t[14]=1; if (pabis<pa) pa=pabis; break;
+            default: return 0;
+          }
+          if (pbis) assign_or_fail(pbis,p);
+          if (polbis) assign_or_fail(polbis,pol);
+        }
+        break;
+      default: return 0;
+    }
+  }
+  if (t[5])
+  {
+    if (t[3]||t[6]||t[7]||t[8]||t[10]||t[11]||t[12]||t[13]||t[14]) return 0;
+    *ptpa=pa; return t_COMPLEX;
+  }
+  if (t[2])
+  {
+    if (t[3]||t[6]||t[7]||t[8]||t[10]||t[11]||t[12]||t[13]||t[14]) return 0;
+    *ptpa=pa; return t[4]?t_COMPLEX:t_REAL;
+  }
+  if (t[6]||t[10]||t[13])
+  {
+    *ptpol=pol; *ptp=p;
+    i = t[13]? t_POLMOD: (t[10]? t_QUAD: t_COMPLEX);
+    return RgX_type_code(i, t_INTMOD);
+  }
+  if (t[7]||t[11]||t[14])
+  {
+    *ptpol=pol; *ptp=p; *ptpa=pa;
+    i = t[14]? t_POLMOD: (t[11]? t_QUAD: t_COMPLEX);
+    return RgX_type_code(i, t_PADIC);
+  }
+  if (t[4]||t[9]||t[12])
+  {
+    *ptpol=pol;
+    i = t[12]? t_POLMOD: (t[9]? t_QUAD: t_COMPLEX);
+    return RgX_type_code(i, t_INT);
+  }
+  if (t[15])
+  {
+    if (t[8]) return 0;
+    *ptp=p; *ptpol=ff;
+    return t_FFELT;
+  }
+  if (t[3]) { *ptp=p; return t_INTMOD; }
+  if (t[8]) { *ptp=p; *ptpa=pa; return t_PADIC; }
+  return t_INT;
+}
+
+GEN
+factor0(GEN x,long flag)
+{
+  return (flag<0)? factor(x): boundfact(x,flag);
+}
+
+/* only present for interface with GP */
+GEN
+gp_factor0(GEN x, GEN flag)
+{
+  ulong B;
+  if (!flag) return factor(x);
+  if (typ(flag) != t_INT || signe(flag) < 0) pari_err_FLAG("factor");
+  switch(lgefint(flag))
+  {
+    case 2: B = 0; break;
+    case 3: B = flag[2]; break;
+    default: pari_err_OVERFLOW("factor [large prime bound]");
+             return NULL; /*not reached*/
+  }
+  return boundfact(x, B);
+}
+
+GEN
+deg1_from_roots(GEN L, long v)
+{
+  long i, l = lg(L);
+  GEN z = cgetg(l,t_COL);
+  for (i=1; i<l; i++)
+    gel(z,i) = deg1pol_shallow(gen_1, gneg(gel(L,i)), v);
+  return z;
+}
+GEN
+roots_from_deg1(GEN x)
+{
+  long i,l = lg(x);
+  GEN r = cgetg(l,t_VEC);
+  for (i=1; i<l; i++) { GEN P = gel(x,i); gel(r,i) = gneg(gel(P,2)); }
+  return r;
+}
+
+static GEN
+gauss_factor_p(GEN p)
+{
+  GEN a, b; (void)cornacchia(gen_1, p, &a,&b);
+  return mkcomplex(a, b);
+}
+
+static GEN
+gauss_primpart(GEN x, GEN *c)
+{
+  GEN a = gel(x,1), b = gel(x,2), n = gcdii(a, b);
+  *c = n; if (n == gen_1) return x;
+  retmkcomplex(diviiexact(a,n), diviiexact(b,n));
+}
+
+static GEN
+gauss_primpart_try(GEN x, GEN c)
+{
+  GEN r, y;
+  if (typ(x) == t_INT)
+  {
+    y = dvmdii(x, c, &r); if (r != gen_0) return NULL;
+  }
+  else
+  {
+    GEN a = gel(x,1), b = gel(x,2); y = cgetg(3, t_COMPLEX);
+    gel(y,1) = dvmdii(a, c, &r); if (r != gen_0) return NULL;
+    gel(y,2) = dvmdii(b, c, &r); if (r != gen_0) return NULL;
+  }
+  return y;
+}
+
+static int
+gauss_cmp(GEN x, GEN y)
+{
+  int v;
+  if (typ(x) != t_COMPLEX)
+    return (typ(y) == t_COMPLEX)? -1: gcmp(x, y);
+  if (typ(y) != t_COMPLEX) return 1;
+  v = cmpii(gel(x,2), gel(y,2));
+  if (v) return v;
+  return gcmp(gel(x,1), gel(y,1));
+}
+
+/* 0 or canonical representative in Z[i]^* / <i> (impose imag(x) >= 0) */
+static GEN
+gauss_normal(GEN x)
+{
+  if (typ(x) != t_COMPLEX) return (signe(x) < 0)? absi(x): x;
+  if (signe(gel(x,1)) < 0) x = gneg(x);
+  if (signe(gel(x,2)) < 0) x = mulcxI(x);
+  return x;
+}
+
+static GEN
+gauss_factor(GEN x)
+{
+  pari_sp av = avma;
+  GEN a = gel(x,1), b = gel(x,2), d = gen_1, n, y, fa, P, E, P2, E2;
+  long t1 = typ(a);
+  long t2 = typ(b), i, j, l, exp = 0;
+  if (t1 == t_FRAC) d = gel(a,2);
+  if (t2 == t_FRAC) d = lcmii(d, gel(b,2));
+  if (d == gen_1) y = x;
+  else
+  {
+    y = gmul(x, d);
+    a = gel(y,1); t1 = typ(a);
+    b = gel(y,2); t2 = typ(b);
+  }
+  if (t1 != t_INT || t2 != t_INT) return NULL;
+  y = gauss_primpart(y, &n);
+  fa = factor(cxnorm(y));
+  P = gel(fa,1);
+  E = gel(fa,2); l = lg(P);
+  P2 = cgetg(l, t_COL);
+  E2 = cgetg(l, t_COL);
+  for (j = 1, i = l-1; i > 0; i--) /* remove largest factors first */
+  { /* either p = 2 (ramified) or those factors split in Q(i) */
+    GEN p = gel(P,i), w, w2, t, we, pe;
+    long v, e = itos(gel(E,i));
+    int is2 = equaliu(p, 2);
+    w = is2? mkcomplex(gen_1,gen_1): gauss_factor_p(p);
+    w2 = gauss_normal( gconj(w) );
+    /* w * w2 * I^3 = p, w2 = gconj(w) * I */
+    pe = powiu(p, e);
+    we = gpowgs(w, e);
+    t = gauss_primpart_try( gmul(y, gconj(we)), pe );
+    if (t) y = t; /* y /= w^e */
+    else {
+      /* y /= conj(w)^e, should be y /= w2^e */
+      y = gauss_primpart_try( gmul(y, we), pe );
+      swap(w, w2); exp -= e; /* += 3*e mod 4 */
+    }
+    gel(P,i) = w;
+    v = Z_pvalrem(n, p, &n);
+    if (v) {
+      exp -= v; /* += 3*v mod 4 */
+      if (is2) v <<= 1; /* 2 = w^2 I^3 */
+      else {
+        gel(P2,j) = w2;
+        gel(E2,j) = utoipos(v); j++;
+      }
+      gel(E,i) = stoi(e + v);
+    }
+    v = Z_pvalrem(d, p, &d);
+    if (v) {
+      exp += v; /* -= 3*v mod 4 */
+      if (is2) v <<= 1; /* 2 is ramified */
+      else {
+        gel(P2,j) = w2;
+        gel(E2,j) = utoineg(v); j++;
+      }
+      gel(E,i) = stoi(e - v);
+    }
+    exp &= 3;
+  }
+  if (j > 1) {
+    long k = 1;;
+    GEN P1 = cgetg(l, t_COL);
+    GEN E1 = cgetg(l, t_COL);
+    /* remove factors with exponent 0 */
+    for (i = 1; i < l; i++)
+      if (signe(gel(E,i)))
+      {
+        gel(P1,k) = gel(P,i);
+        gel(E1,k) = gel(E,i);
+        k++;
+      }
+    setlg(P1, k); setlg(E1, k);
+    setlg(P2, j); setlg(E2, j);
+    fa = famat_mul_shallow(mkmat2(P1,E1), mkmat2(P2,E2));
+  }
+  if (!is_pm1(n) || !is_pm1(d))
+  {
+    GEN Fa = factor(gdiv(n, d));
+    P = gel(Fa,1); l = lg(P);
+    E = gel(Fa,2);
+    for (i = 1; i < l; i++)
+    {
+      GEN w, p = gel(P,i);
+      long e;
+      int is2;
+      switch(mod4(p))
+      {
+        case 3: continue;
+        case 2: is2 = 1; break;
+        default:is2 = 0; break;
+      }
+      e = itos(gel(E,i));
+      w = is2? mkcomplex(gen_1,gen_1): gauss_factor_p(p);
+      gel(P,i) = w;
+      if (is2)
+        gel(E,i) = stoi(e << 1);
+      else
+      {
+        P = shallowconcat(P, gauss_normal( gconj(w) ));
+        E = shallowconcat(E, gel(E,i));
+      }
+      exp -= e; /* += 3*e mod 4 */
+      exp &= 3;
+    }
+    gel(Fa,1) = P;
+    gel(Fa,2) = E;
+    fa = famat_mul_shallow(fa, Fa);
+  }
+  fa = sort_factor(fa, (void*)&gauss_cmp, &cmp_nodata);
+
+  y = gmul(y, powIs(exp));
+  if (!gequal1(y)) {
+    gel(fa,1) = shallowconcat(mkcol(y), gel(fa,1));
+    gel(fa,2) = shallowconcat(gen_1,    gel(fa,2));
+  }
+  return gerepilecopy(av, fa);
+}
+
+GEN
+factor(GEN x)
+{
+  long tx=typ(x), lx, i, pa, v, r1;
+  pari_sp av, tetpil;
+  GEN  y, p, p1, p2, pol;
+
+  if (gequal0(x))
+    switch(tx)
+    {
+      case t_INT:
+      case t_COMPLEX:
+      case t_POL:
+      case t_RFRAC: return prime_fact(x);
+      default: pari_err_TYPE("factor",x);
+    }
+  av = avma;
+  switch(tx)
+  {
+    case t_INT: return Z_factor(x);
+
+    case t_FRAC:
+      p1 = Z_factor(gel(x,1));
+      p2 = Z_factor(gel(x,2)); gel(p2,2) = gneg_i(gel(p2,2));
+      return gerepilecopy(av, merge_factor_i(p1,p2));
+
+    case t_POL:
+      tx=RgX_type(x,&p,&pol,&pa);
+      switch(tx)
+      {
+        case 0: pari_err_IMPL("factor for general polynomials");
+        case t_INT: return QX_factor(x);
+        case t_INTMOD: return factmod(x,p);
+
+        case t_COMPLEX: y=cgetg(3,t_MAT); lx=lg(x)-2;
+          av = avma; p1 = roots(x,pa); tetpil = avma;
+          p1 = deg1_from_roots(p1, varn(x));
+          gel(y,1) = gerepile(av,tetpil,p1);
+          gel(y,2) = const_col(lx-1, gen_1); return y;
+
+        case t_REAL: y=cgetg(3,t_MAT); lx=lg(x)-2; v=varn(x);
+          av=avma; p1=cleanroots(x,pa); tetpil=avma;
+          for(r1=1; r1<lx; r1++)
+            if (typ(gel(p1,r1)) == t_COMPLEX) break;
+          lx=(r1+lx)>>1; p2=cgetg(lx,t_COL);
+          for(i=1; i<r1; i++)
+            gel(p2,i) = deg1pol_shallow(gen_1, negr(gel(p1,i)), v);
+          for(   ; i<lx; i++)
+          {
+            GEN a = gel(p1,2*i-r1);
+            p = cgetg(5, t_POL); gel(p2,i) = p;
+            p[1] = x[1];
+            gel(p,2) = gnorm(a);
+            gel(p,3) = gmul2n(gel(a,1),1); togglesign(gel(p,3));
+            gel(p,4) = gen_1;
+          }
+          gel(y,1) = gerepile(av,tetpil,p2);
+          gel(y,2) = const_col(lx-1, gen_1); return y;
+
+        case t_PADIC: return factorpadic(x,p,pa);
+
+        case t_FFELT: return FFX_factor(x,pol);
+
+        default:
+        {
+          GEN w;
+          long killv, t1, t2;
+          x = leafcopy(x); lx=lg(x);
+          pol = leafcopy(pol);
+          v = pari_var_next_temp();
+          for(i=2; i<lx; i++)
+          {
+            p1 = gel(x,i);
+            switch(typ(p1))
+            {
+              case t_QUAD: p1++;
+              case t_COMPLEX:
+                gel(x,i) = mkpolmod(deg1pol_shallow(gel(p1,2), gel(p1,1), v), pol);
+            }
+          }
+          killv = (avma != (pari_sp)pol);
+          if (killv) setvarn(pol, fetch_var());
+          RgX_type_decode(tx, &t1, &t2);
+          switch (t2)
+          {
+            case t_INT: p1 = polfnf(x,pol); break;
+            case t_INTMOD:
+              pol = RgX_to_FpX(pol, p);
+              if (FpX_is_squarefree(pol,p) && FpX_nbfact(pol, p) == 1)
+              {
+                p1 = factorff(x,p,pol); break;
+              }
+            /*fall through*/
+            default: pari_err_IMPL("factor for general polynomial");
+              return NULL; /* not reached */
+          }
+          switch (t1)
+          {
+            case t_POLMOD:
+              if (killv) (void)delete_var();
+              return gerepileupto(av,p1);
+            case t_COMPLEX: w = gen_I(); break;
+            case t_QUAD: w = mkquad(pol,gen_0,gen_1);
+              break;
+            default: pari_err_IMPL("factor for general polynomial");
+              return NULL; /* not reached */
+          }
+          p2=gel(p1,1);
+          for(i=1; i<lg(p2); i++)
+          {
+            GEN P = gel(p2,i);
+            long j;
+            for(j=2; j<lg(P); j++)
+            {
+              GEN p = gel(P,j);
+              if(typ(p)==t_POLMOD) gel(P,j) = gsubst(gel(p,2),v,w);
+            }
+          }
+          if (killv) (void)delete_var();
+          return gerepilecopy(av, p1);
+        }
+      }
+    case t_RFRAC: {
+      GEN a = gel(x,1), b = gel(x,2);
+      y = factor(b); gel(y,2) = gneg_i(gel(y,2));
+      if (typ(a)==t_POL && varn(a)==varn(b)) y = famat_mul_shallow(factor(a), y);
+      return gerepilecopy(av, y);
+    }
+
+    case t_COMPLEX:
+      y = gauss_factor(x);
+      if (y) return y;
+      /* fall through */
+  }
+  pari_err_TYPE("factor",x);
+  return NULL; /* not reached */
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     ROOTS --> MONIC POLYNOMIAL                  */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+normalized_mul(GEN x, GEN y)
+{
+  long a = gel(x,1)[1], b = gel(y,1)[1];
+  return mkvec2(mkvecsmall(a + b),
+                RgX_mul_normalized(gel(x,2),a, gel(y,2),b));
+}
+/* L = [Vecsmall([a]), A], with a > 0, A an RgX, deg(A) < a; return X^a + A */
+static GEN
+normalized_to_RgX(GEN L)
+{
+  long i, a = gel(L,1)[1];
+  GEN A = gel(L,2);
+  GEN z = cgetg(a + 3, t_POL);
+  z[1] = evalsigne(1) | evalvarn(varn(A));
+  for (i = 2; i < lg(A); i++) gel(z,i) = gcopy(gel(A,i));
+  for (     ; i < a+2;   i++) gel(z,i) = gen_0;
+  gel(z,i) = gen_1; return z;
+}
+
+/* compute prod (x - a[i]) */
+GEN
+roots_to_pol(GEN a, long v)
+{
+  pari_sp av = avma;
+  long i, k, lx = lg(a);
+  GEN L;
+  if (lx == 1) return pol_1(v);
+  L = cgetg(lx, t_VEC);
+  for (k=1,i=1; i<lx-1; i+=2)
+  {
+    GEN s = gel(a,i), t = gel(a,i+1);
+    GEN x0 = gmul(s,t);
+    GEN x1 = gneg(gadd(s,t));
+    gel(L,k++) = mkvec2(mkvecsmall(2), deg1pol_shallow(x1,x0,v));
+  }
+  if (i < lx) gel(L,k++) = mkvec2(mkvecsmall(1),
+                                  scalarpol_shallow(gneg(gel(a,i)), v));
+  setlg(L, k); L = divide_conquer_prod(L, normalized_mul);
+  return gerepileupto(av, normalized_to_RgX(L));
+}
+
+/* prod_{i=1..r1} (x - a[i]) prod_{i=1..r2} (x - a[i])(x - conj(a[i]))*/
+GEN
+roots_to_pol_r1(GEN a, long v, long r1)
+{
+  pari_sp av = avma;
+  long i, k, lx = lg(a);
+  GEN L;
+  if (lx == 1) return pol_1(v);
+  L = cgetg(lx, t_VEC);
+  for (k=1,i=1; i<r1; i+=2)
+  {
+    GEN s = gel(a,i), t = gel(a,i+1);
+    GEN x0 = gmul(s,t);
+    GEN x1 = gneg(gadd(s,t));
+    gel(L,k++) = mkvec2(mkvecsmall(2), deg1pol_shallow(x1,x0,v));
+  }
+  if (i < r1+1) gel(L,k++) = mkvec2(mkvecsmall(1),
+                                    scalarpol_shallow(gneg(gel(a,i)), v));
+  for (i=r1+1; i<lx; i++)
+  {
+    GEN s = gel(a,i);
+    GEN x0 = gnorm(s);
+    GEN x1 = gneg(gtrace(s));
+    gel(L,k++) = mkvec2(mkvecsmall(2), deg1pol_shallow(x1,x0,v));
+  }
+  setlg(L, k); L = divide_conquer_prod(L, normalized_mul);
+  return gerepileupto(av, normalized_to_RgX(L));
+}
+
+GEN
+divide_conquer_assoc(GEN x, void *data, GEN (*mul)(void *,GEN,GEN))
+{
+  pari_sp ltop, lim;
+  long i,k,lx = lg(x);
+
+  if (lx == 1) return gen_1;
+  if (lx == 2) return gcopy(gel(x,1));
+  x = leafcopy(x); k = lx;
+  ltop=avma; lim = stack_lim(ltop,1);
+  while (k > 2)
+  {
+    if (DEBUGLEVEL>7)
+      err_printf("prod: remaining objects %ld\n",k-1);
+    lx = k; k = 1;
+    for (i=1; i<lx-1; i+=2)
+      gel(x,k++) = mul(data,gel(x,i),gel(x,i+1));
+    if (i < lx) gel(x,k++) = gel(x,i);
+    if (low_stack(lim,stack_lim(ltop,1)))
+      gerepilecoeffs(ltop,x+1,k-1);
+  }
+  return gel(x,1);
+}
+
+static GEN
+_domul(void *data, GEN x, GEN y)
+{
+  GEN (*mul)(GEN,GEN)=(GEN (*)(GEN,GEN)) data;
+  return mul(x,y);
+}
+GEN
+divide_conquer_prod(GEN x, GEN (*mul)(GEN,GEN))
+{ return divide_conquer_assoc(x, (void *)mul, _domul); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                          FACTORBACK                             */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+idmulred(void *nf, GEN x, GEN y) { return idealmulred((GEN) nf, x, y); }
+static GEN
+idpowred(void *nf, GEN x, GEN n) { return idealpowred((GEN) nf, x, n); }
+static GEN
+idmul(void *nf, GEN x, GEN y) { return idealmul((GEN) nf, x, y); }
+static GEN
+idpow(void *nf, GEN x, GEN n) { return idealpow((GEN) nf, x, n); }
+static GEN
+eltmul(void *nf, GEN x, GEN y) { return nfmul((GEN) nf, x, y); }
+static GEN
+eltpow(void *nf, GEN x, GEN n) { return nfpow((GEN) nf, x, n); }
+static GEN
+mul(void *a, GEN x, GEN y) { (void)a; return gmul(x,y);}
+static GEN
+powi(void *a, GEN x, GEN y) { (void)a; return powgi(x,y);}
+
+#if 0
+static GEN
+_ellmul(void *ell, GEN x, GEN y) { return elladd((GEN) ell, x, y); }
+static GEN
+_ellpow(void *ell, GEN x, GEN n) { return ellmul((GEN) ell, x, n); }
+#endif
+
+/* [L,e] = [fa, NULL] or [elts, NULL] or [elts, exponents] */
+GEN
+gen_factorback(GEN L, GEN e, GEN (*_mul)(void*,GEN,GEN),
+               GEN (*_pow)(void*,GEN,GEN), void *data)
+{
+  pari_sp av = avma;
+  long k, l, lx;
+  GEN p,x;
+
+  if (e) /* supplied vector of exponents */
+    p = L;
+  else
+  {
+    switch(typ(L)) {
+      case t_VEC:
+      case t_COL: /* product of the L[i] */
+        return gerepileupto(av, divide_conquer_assoc(L, data, _mul));
+      case t_MAT: /* genuine factorization */
+        l = lg(L);
+        if (l == 1) return gen_1;
+        if (l == 3) break;
+        /*fall through*/
+      default:
+        pari_err_TYPE("factorback [not a factorization]", L);
+    }
+    p = gel(L,1);
+    e = gel(L,2);
+  }
+  /* p = elts, e = expo */
+  lx = lg(p);
+  /* check whether e is an integral vector of correct length */
+  if (!is_vec_t(typ(e)) || lx != lg(e) || !RgV_is_ZV(e))
+    pari_err_TYPE("factorback [not an exponent vector]", e);
+  if (lx == 1) return gen_1;
+  x = cgetg(lx,t_VEC);
+  for (l=1,k=1; k<lx; k++)
+    if (signe(gel(e,k))) gel(x,l++) = _pow(data, gel(p,k), gel(e,k));
+  x[0] = evaltyp(t_VEC) | _evallg(l);
+  return gerepileupto(av, divide_conquer_assoc(x, data, _mul));
+}
+
+GEN
+idealfactorback(GEN nf, GEN L, GEN e, int red)
+{
+  nf = checknf(nf);
+  if (red) return gen_factorback(L, e, &idmulred, &idpowred, (void*)nf);
+  else     return gen_factorback(L, e, &idmul, &idpow, (void*)nf);
+}
+
+GEN
+nffactorback(GEN nf, GEN L, GEN e)
+{ return gen_factorback(L, e, &eltmul, &eltpow, (void*)checknf(nf)); }
+
+GEN
+factorback2(GEN L, GEN e) { return gen_factorback(L, e, &mul, &powi, NULL); }
+GEN
+factorback(GEN fa) { return factorback2(fa, NULL); }
+
+static int
+RgX_is_irred_i(GEN x)
+{
+  GEN y, p, pol;
+  long l = lg(x), pa;
+
+  if (!signe(x) || l <= 3) return 0;
+  switch(RgX_type(x,&p,&pol,&pa))
+  {
+    case t_INTMOD: return FpX_is_irred(RgX_to_FpX(x,p), p);
+    case t_COMPLEX: return l == 4;
+    case t_REAL:
+      if (l == 4) return 1;
+      if (l > 5) return 0;
+      return gsigne(RgX_disc(x)) > 0;
+  }
+  y = factor(x);
+  return (lg(gcoeff(y,1,1))==l);
+}
+static int
+RgX_is_irred(GEN x)
+{
+  pari_sp av = avma;
+  int r = RgX_is_irred_i(x);
+  avma = av; return r;
+}
+long
+isirreducible(GEN x)
+{
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC: return 0;
+    case t_POL: return RgX_is_irred(x);
+  }
+  pari_err_TYPE("isirreducible",x);
+  return 0;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         GENERIC GCD                             */
+/*                                                                 */
+/*******************************************************************/
+/* x is a COMPLEX or a QUAD */
+static GEN
+triv_cont_gcd(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN c;
+  if (typ(x)==t_COMPLEX)
+  {
+    GEN a = gel(x,1), b = gel(x,2);
+    if (typ(a) == t_REAL || typ(b) == t_REAL) return gen_1;
+    c = ggcd(a,b);
+  }
+  else
+    c = ggcd(gel(x,2),gel(x,3));
+  return gerepileupto(av, ggcd(c,y));
+}
+
+/* y is a PADIC, x a rational number or an INTMOD */
+static GEN
+padic_gcd(GEN x, GEN y)
+{
+  GEN p = gel(y,2);
+  long v = gvaluation(x,p), w = valp(y);
+  if (w < v) v = w;
+  return powis(p, v);
+}
+
+/* x,y in Z[i], at least one of which is t_COMPLEX */
+static GEN
+gauss_gcd(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN dx, dy;
+  dx = denom(x); x = gmul(x, dx);
+  dy = denom(y); y = gmul(y, dy);
+  while (!gequal0(y))
+  {
+    GEN z = gsub(x, gmul(ground(gdiv(x,y)), y));
+    x = y; y = z;
+  }
+  x = gauss_normal(x);
+  if (typ(x) == t_COMPLEX)
+  {
+    if      (gequal0(gel(x,2))) x = gel(x,1);
+    else if (gequal0(gel(x,1))) x = gel(x,2);
+  }
+  return gerepileupto(av, gdiv(x, lcmii(dx, dy)));
+}
+
+static int
+is_rational(GEN x) { long t = typ(x); return is_rational_t(t); }
+static int
+c_is_rational(GEN x)
+{
+  return (is_rational(gel(x,1)) && is_rational(gel(x,2)));
+}
+static GEN
+c_zero_gcd(GEN c)
+{
+  GEN x = gel(c,1), y = gel(c,2);
+  long tx = typ(x), ty = typ(y);
+  if (tx == t_REAL || ty == t_REAL) return gen_1;
+  if (tx == t_PADIC || tx == t_INTMOD
+   || ty == t_PADIC || ty == t_INTMOD) return ggcd(x, y);
+  return gauss_gcd(c, gen_0);
+}
+
+/* y == 0 */
+static GEN
+zero_gcd(GEN x, long tx)
+{
+  pari_sp av;
+  switch(tx)
+  {
+    case t_INT: return absi(x);
+    case t_FRAC: return absfrac(x);
+    case t_COMPLEX: return c_zero_gcd(x);
+    case t_REAL: return gen_1;
+    case t_PADIC: return powis(gel(x,2), valp(x));
+    case t_SER: return monomial(gen_1, valp(x), varn(x));
+    case t_POLMOD: {
+      GEN d = gel(x,2);
+      if (typ(d) == t_POL && varn(d) == varn(gel(x,1))) return content(d);
+      return isinexact(d)? zero_gcd(d, typ(d)): gcopy(d);
+    }
+    case t_POL:
+      if (!isinexact(x)) break;
+      av = avma;
+      return gerepileupto(av,
+        monomialcopy(content(x), RgX_val(x), varn(x))
+      );
+
+    case t_RFRAC:
+      if (!isinexact(x)) break;
+      av = avma;
+      return gerepileupto(av,
+        gdiv(zero_gcd(gel(x,1), typ(gel(x,1))), gel(x,2))
+      );
+  }
+  return gcopy(x);
+}
+
+/* tx = t_RFRAC, y considered as constant */
+static GEN
+cont_gcd_rfrac(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  GEN cx; x = primitive_part(x, &cx);
+  return gerepileupto(av, gred_rfrac_simple(ggcd(cx? cx: gen_1, y), gel(x,2)));
+}
+/* tx = t_POL, y considered as constant */
+static GEN
+cont_gcd_pol(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, scalarpol(ggcd(content(x),y), varn(x)));
+}
+/* !is_const_t(tx), tx != t_POL,t_RFRAC, y considered as constant */
+static GEN
+cont_gcd_gen(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, ggcd(content(x),y));
+}
+/* !is_const(tx), y considered as constant */
+static GEN
+cont_gcd(GEN x, long tx, GEN y)
+{
+  switch(tx)
+  {
+    case t_RFRAC: return cont_gcd_rfrac(x,y);
+    case t_POL: return cont_gcd_pol(x,y);
+    default: return cont_gcd_gen(x,y);
+  }
+}
+static GEN
+gcdiq(GEN x, GEN y)
+{
+  GEN z;
+  if (!signe(x)) return Q_abs(y);
+  z = cgetg(3,t_FRAC);
+  gel(z,1) = gcdii(x,gel(y,1));
+  gel(z,2) = icopy(gel(y,2));
+  return z;
+}
+static GEN
+gcdqq(GEN x, GEN y)
+{
+  GEN z = cgetg(3,t_FRAC);
+  gel(z,1) = gcdii(gel(x,1), gel(y,1));
+  gel(z,2) = lcmii(gel(x,2), gel(y,2));
+  return z;
+}
+/* assume x,y t_INT or t_FRAC */
+GEN
+Q_gcd(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y);
+  if (tx == t_INT)
+  { return (ty == t_INT)? gcdii(x,y): gcdiq(x,y); }
+  else
+  { return (ty == t_INT)? gcdiq(y,x): gcdqq(x,y); }
+}
+
+GEN
+ggcd(GEN x, GEN y)
+{
+  long l, i, vx, vy, tx = typ(x), ty = typ(y);
+  pari_sp av, tetpil;
+  GEN p1,z;
+
+  if (is_noncalc_t(tx) || is_noncalc_t(ty)) pari_err_TYPE2("gcd",x,y);
+  if (is_matvec_t(ty))
+  {
+    z = cgetg_copy(y, &l);
+    for (i=1; i<l; i++) gel(z,i) = ggcd(x,gel(y,i));
+    return z;
+  }
+  if (is_matvec_t(tx))
+  {
+    z = cgetg_copy(x, &l);
+    for (i=1; i<l; i++) gel(z,i) = ggcd(gel(x,i),y);
+    return z;
+  }
+  if (tx>ty) { swap(x,y); lswap(tx,ty); }
+  /* tx <= ty */
+  if (isrationalzero(x)) return zero_gcd(y, ty);
+  if (isrationalzero(y)) return zero_gcd(x, tx);
+  if (is_const_t(tx))
+  {
+    if (ty == tx) switch(tx)
+    {
+      case t_INT:
+        return gcdii(x,y);
+
+      case t_INTMOD: z=cgetg(3,t_INTMOD);
+        if (equalii(gel(x,1),gel(y,1)))
+          gel(z,1) = icopy(gel(x,1));
+        else
+          gel(z,1) = gcdii(gel(x,1),gel(y,1));
+        if (gequal1(gel(z,1))) gel(z,2) = gen_0;
+        else
+        {
+          av = avma; p1 = gcdii(gel(z,1),gel(x,2));
+          if (!is_pm1(p1))
+          {
+            p1 = gcdii(p1,gel(y,2));
+            if (equalii(p1, gel(z,1))) { cgiv(p1); p1 = gen_0; }
+            else p1 = gerepileuptoint(av, p1);
+          }
+          gel(z,2) = p1;
+        }
+        return z;
+
+      case t_FRAC:
+        return gcdqq(x,y);
+
+      case t_FFELT:
+        if (!FF_samefield(x,y)) pari_err_OP("gcd",x,y);
+        return FF_equal0(x) && FF_equal0(y)? FF_zero(y): FF_1(y);
+
+      case t_COMPLEX:
+        if (c_is_rational(x) && c_is_rational(y)) return gauss_gcd(x,y);
+        return triv_cont_gcd(y,x);
+
+      case t_PADIC:
+        if (!equalii(gel(x,2),gel(y,2))) return gen_1;
+        return powis(gel(y,2), minss(valp(x), valp(y)));
+
+      case t_QUAD:
+        av=avma; p1=gdiv(x,y);
+        if (gequal0(gel(p1,3)))
+        {
+          p1=gel(p1,2);
+          if (typ(p1)==t_INT) { avma=av; return gcopy(y); }
+          tetpil=avma; return gerepile(av,tetpil, gdiv(y,gel(p1,2)));
+        }
+        if (typ(gel(p1,2))==t_INT && typ(gel(p1,3))==t_INT) {avma=av; return gcopy(y);}
+        p1 = ginv(p1); avma=av;
+        if (typ(gel(p1,2))==t_INT && typ(gel(p1,3))==t_INT) return gcopy(x);
+        return triv_cont_gcd(y,x);
+
+      default: return gen_1; /* t_REAL */
+    }
+    if (is_const_t(ty)) switch(tx)
+    {
+      case t_INT:
+        switch(ty)
+        {
+          case t_INTMOD: z = cgetg(3,t_INTMOD);
+            gel(z,1) = icopy(gel(y,1)); av = avma;
+            p1 = gcdii(gel(y,1),gel(y,2));
+            if (!is_pm1(p1)) {
+              p1 = gcdii(x,p1);
+              if (equalii(p1, gel(z,1))) { cgiv(p1); p1 = gen_0; }
+              else
+                p1 = gerepileuptoint(av, p1);
+            }
+            gel(z,2) = p1; return z;
+
+          case t_REAL: return gen_1;
+
+          case t_FRAC:
+            return gcdiq(x,y);
+
+          case t_COMPLEX:
+            if (c_is_rational(y)) return gauss_gcd(x,y);
+            return triv_cont_gcd(y,x);
+
+          case t_FFELT:
+            if (!FF_equal0(y)) return FF_1(y);
+            return dvdii(x, gel(y,4))? FF_zero(y): FF_1(y);
+
+          case t_PADIC:
+            return padic_gcd(x,y);
+
+          case t_QUAD:
+            return triv_cont_gcd(y,x);
+          default:
+            pari_err_TYPE2("gcd",x,y);
+        }
+
+      case t_REAL:
+        switch(ty)
+        {
+          case t_INTMOD:
+          case t_FFELT:
+          case t_PADIC: pari_err_TYPE2("gcd",x,y);
+          default: return gen_1;
+        }
+
+      case t_INTMOD:
+        switch(ty)
+        {
+          case t_FRAC:
+            av = avma; p1=gcdii(gel(x,1),gel(y,2)); avma = av;
+            if (!is_pm1(p1)) pari_err_OP("gcd",x,y);
+            return ggcd(gel(y,1), x);
+
+          case t_FFELT:
+          {
+            GEN p = gel(y,4);
+            if (!dvdii(gel(x,1), p)) pari_err_OP("gcd",x,y);
+            if (!FF_equal0(y)) return FF_1(y);
+            return dvdii(gel(x,2),p)? FF_zero(y): FF_1(y);
+          }
+
+          case t_COMPLEX: case t_QUAD:
+            return triv_cont_gcd(y,x);
+
+          case t_PADIC:
+            return padic_gcd(x,y);
+
+          default: pari_err_TYPE2("gcd",x,y);
+        }
+
+      case t_FRAC:
+        switch(ty)
+        {
+          case t_COMPLEX:
+            if (c_is_rational(y)) return gauss_gcd(x,y);
+          case t_QUAD:
+            return triv_cont_gcd(y,x);
+          case t_FFELT:
+          {
+            GEN p = gel(y,4);
+            if (dvdii(gel(x,2), p)) pari_err_OP("gcd",x,y);
+            if (!FF_equal0(y)) return FF_1(y);
+            return dvdii(gel(x,1),p)? FF_zero(y): FF_1(y);
+          }
+
+          case t_PADIC:
+            return padic_gcd(x,y);
+
+          default: pari_err_TYPE2("gcd",x,y);
+        }
+      case t_FFELT:
+        switch(ty)
+        {
+          case t_PADIC:
+          {
+            GEN p = gel(y,2);
+            long v = valp(y);
+            if (!equalii(p, gel(x,4)) || v < 0) pari_err_OP("gcd",x,y);
+            return (v && FF_equal0(x))? FF_zero(x): FF_1(x);
+          }
+          default: pari_err_TYPE2("gcd",x,y);
+        }
+
+      case t_COMPLEX:
+        switch(ty)
+        {
+          case t_PADIC:
+          case t_QUAD: return triv_cont_gcd(x,y);
+          default: pari_err_TYPE2("gcd",x,y);
+        }
+
+      case t_PADIC:
+        switch(ty)
+        {
+          case t_QUAD: return triv_cont_gcd(y,x);
+          default: pari_err_TYPE2("gcd",x,y);
+        }
+
+      default: return gen_1; /* tx = t_REAL */
+    }
+    return cont_gcd(y,ty, x);
+  }
+
+  if (tx == t_POLMOD)
+  {
+    if (ty == t_POLMOD)
+    {
+      GEN T = gel(x,1);
+      z = cgetg(3,t_POLMOD);
+      T = RgX_equal_var(T,gel(y,1))? RgX_copy(T): RgX_gcd(T, gel(y,1));
+      gel(z,1) = T;
+      if (degpol(T) <= 0) gel(z,2) = gen_0;
+      else
+      {
+        GEN X, Y, d;
+        av = avma; X = gel(x,2); Y = gel(y,2);
+        d = ggcd(content(X), content(Y));
+        if (!gequal1(d)) { X = gdiv(X,d); Y = gdiv(Y,d); }
+        p1 = ggcd(T, X);
+        gel(z,2) = gerepileupto(av, gmul(d, ggcd(p1, Y)));
+      }
+      return z;
+    }
+    vx = varn(gel(x,1));
+    switch(ty)
+    {
+      case t_POL:
+        vy = varn(y);
+        if (varncmp(vy,vx) < 0) return cont_gcd_pol(y, x);
+        z = cgetg(3,t_POLMOD);
+        gel(z,1) = RgX_copy(gel(x,1));
+        av = avma; p1 = ggcd(gel(x,1),gel(x,2));
+        gel(z,2) = gerepileupto(av, ggcd(p1,y));
+        return z;
+
+      case t_RFRAC:
+        vy = varn(gel(y,2));
+        if (varncmp(vy,vx) < 0) return cont_gcd_rfrac(y, x);
+        av = avma;
+        p1 = ggcd(gel(x,1),gel(y,2));
+        if (degpol(p1)) pari_err_OP("gcd",x,y);
+        avma = av; return gdiv(ggcd(gel(y,1),x), content(gel(y,2)));
+    }
+  }
+
+  vx = gvar(x);
+  vy = gvar(y);
+  if (varncmp(vy, vx) < 0) return cont_gcd(y,ty, x);
+  if (varncmp(vy, vx) > 0) return cont_gcd(x,tx, y);
+
+  /* vx = vy: same main variable */
+  switch(tx)
+  {
+    case t_POL:
+      switch(ty)
+      {
+        case t_POL: return RgX_gcd(x,y);
+        case t_SER:
+          z = ggcd(content(x), content(y));
+          return monomialcopy(z, minss(valp(y),gval(x,vx)), vx);
+        case t_RFRAC: return cont_gcd_rfrac(y, x);
+      }
+      break;
+
+    case t_SER:
+      z = ggcd(content(x), content(y));
+      switch(ty)
+      {
+        case t_SER:   return monomialcopy(z, minss(valp(x),valp(y)), vx);
+        case t_RFRAC: return monomialcopy(z, minss(valp(x),gval(y,vx)), vx);
+      }
+      break;
+
+    case t_RFRAC:
+    {
+      GEN xd = gel(x,2), yd = gel(y,2);
+      if (ty != t_RFRAC) pari_err_TYPE2("gcd",x,y);
+      z = cgetg(3,t_RFRAC); av = avma;
+      gel(z,2) = gerepileupto(av, RgX_mul(xd, RgX_div(yd, RgX_gcd(xd, yd))));
+      gel(z,1) = ggcd(gel(x,1), gel(y,1)); return z;
+    }
+  }
+  pari_err_TYPE2("gcd",x,y);
+  return NULL; /* not reached */
+}
+GEN
+ggcd0(GEN x, GEN y) { return y? ggcd(x,y): content(x); }
+
+/* x a t_VEC,t_COL or t_MAT */
+static GEN
+vec_lcm(GEN x)
+{
+  if (typ(x) == t_MAT)
+  {
+    long i, l = lg(x);
+    GEN z = cgetg(l, t_VEC);
+    for (i = 1; i < l; i++) gel(z,i) = glcm0(gel(x,i), NULL);
+    x = z;
+  }
+  return glcm0(x, NULL);
+}
+static GEN
+scal_lcm(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long tx = typ(x), ty = typ(y);
+  if (is_matvec_t(tx)) x = vec_lcm(x);
+  if (is_matvec_t(ty)) y = vec_lcm(y);
+  return gerepileupto(av, glcm(x, y));
+}
+
+static GEN
+fix_lcm(GEN x)
+{
+  GEN t;
+  switch(typ(x))
+  {
+    case t_INT: if (signe(x)<0) x = negi(x);
+      break;
+    case t_POL:
+      if (lg(x) <= 2) break;
+      t = leading_term(x);
+      if (typ(t) == t_INT && signe(t) < 0) x = gneg(x);
+  }
+  return x;
+}
+
+GEN
+glcm0(GEN x, GEN y) {
+  if (!y && lg(x) == 2)
+  {
+    long tx = typ(x);
+    if (is_vec_t(tx))
+    {
+      x = gel(x,1);
+      tx = typ(x);
+      return is_matvec_t(tx)? vec_lcm(x): fix_lcm(x);
+    }
+  }
+  return gassoc_proto(scal_lcm,x,y);
+}
+
+GEN
+glcm(GEN x, GEN y)
+{
+  long tx, ty, i, l;
+  pari_sp av;
+  GEN p1, z;
+
+  ty = typ(y);
+  if (is_matvec_t(ty))
+  {
+    z = cgetg_copy(y, &l);
+    for (i=1; i<l; i++) gel(z,i) = glcm(x,gel(y,i));
+    return z;
+  }
+  tx = typ(x);
+  if (is_matvec_t(tx))
+  {
+    z = cgetg_copy(x, &l);
+    for (i=1; i<l; i++) gel(z,i) = glcm(gel(x,i),y);
+    return z;
+  }
+  if (tx==t_INT && ty==t_INT) return lcmii(x,y);
+  if (gequal0(x)) return gen_0;
+
+  av = avma;
+  p1 = ggcd(x,y); if (!gequal1(p1)) y = gdiv(y,p1);
+  return gerepileupto(av, fix_lcm(gmul(x,y)));
+}
+
+/* x + r ~ x ? Assume x,r are t_POL, deg(r) <= deg(x) */
+static int
+pol_approx0(GEN r, GEN x, int exact)
+{
+  long i, lx,lr;
+  if (exact) return gequal0(r);
+  lx = lg(x);
+  lr = lg(r); if (lr < lx) lx = lr;
+  for (i=2; i<lx; i++)
+    if (!approx_0(gel(r,i), gel(x,i))) return 0;
+  return 1;
+}
+
+GEN
+RgX_gcd_simple(GEN x, GEN y)
+{
+  pari_sp av1, av = avma, lim = stack_lim(av, 1);
+  GEN r, yorig = y;
+  int exact = !(isinexactreal(x) || isinexactreal(y));
+
+  for(;;)
+  {
+    av1 = avma; r = RgX_rem(x,y);
+    if (pol_approx0(r, x, exact))
+    {
+      avma = av1;
+      if (y == yorig) return RgX_copy(y);
+      y = normalizepol_approx(y, lg(y));
+      if (lg(y) == 3) { avma = av; return pol_1(varn(x)); }
+      return gerepileupto(av,y);
+    }
+    x = y; y = r;
+    if (low_stack(lim,stack_lim(av,1))) {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgX_gcd_simple");
+      gerepileall(av,2, &x,&y);
+    }
+  }
+}
+GEN
+RgX_extgcd_simple(GEN a, GEN b, GEN *pu, GEN *pv)
+{
+  pari_sp av = avma;
+  GEN q, r, d, d1, u, v, v1;
+  int exact = !(isinexactreal(a) || isinexactreal(b));
+
+  d = a; d1 = b; v = gen_0; v1 = gen_1;
+  for(;;)
+  {
+    if (pol_approx0(d1, a, exact)) break;
+    q = poldivrem(d,d1, &r);
+    v = gsub(v, gmul(q,v1));
+    u=v; v=v1; v1=u;
+    u=r; d=d1; d1=u;
+  }
+  u = gsub(d, gmul(b,v));
+  u = RgX_div(u,a);
+
+  gerepileall(av, 3, &u,&v,&d);
+  *pu = u;
+  *pv = v; return d;
+}
+/*******************************************************************/
+/*                                                                 */
+/*                    CONTENT / PRIMITIVE PART                     */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+content(GEN x)
+{
+  long lx, i, t, tx = typ(x);
+  pari_sp av = avma;
+  GEN c;
+
+  if (is_scalar_t(tx)) return zero_gcd(x, tx);
+  switch(tx)
+  {
+    case t_RFRAC:
+    {
+      GEN n = gel(x,1), d = gel(x,2);
+      /* -- varncmp(vn, vd) < 0 can't happen
+       * -- if n is POLMOD, its main variable (in the sense of gvar2)
+       *    has lower priority than denominator */
+      if (typ(n) == t_POLMOD || varncmp(gvar(n), varn(d)) > 0)
+        n = isinexact(n)? zero_gcd(n, typ(n)): gcopy(n);
+      else
+        n = content(n);
+      return gerepileupto(av, gdiv(n, content(d)));
+    }
+
+    case t_VEC: case t_COL:
+      lx = lg(x); if (lx==1) return gen_1;
+      break;
+
+    case t_MAT:
+    {
+      long hx, j;
+      lx = lg(x);
+      if (lx == 1) return gen_1;
+      hx = lgcols(x);
+      if (hx == 1) return gen_1;
+      if (lx == 2) { x = gel(x,1); lx = lg(x); break; }
+      if (hx == 2) { x = row_i(x, 1, 1, lx-1); break; }
+      c = content(gel(x,1));
+      for (j=2; j<lx; j++)
+        for (i=1; i<hx; i++) c = ggcd(c,gcoeff(x,i,j));
+      if (typ(c) == t_INTMOD || isinexact(c)) { avma=av; return gen_1; }
+      return gerepileupto(av,c);
+    }
+
+    case t_POL: case t_SER:
+      lx = lg(x); if (lx == 2) return gen_0;
+      break;
+    case t_QFR: case t_QFI:
+      lx = 4; break;
+
+    default: pari_err_TYPE("content",x);
+      return NULL; /* not reached */
+  }
+  for (i=lontyp[tx]; i<lx; i++)
+    if (typ(gel(x,i)) != t_INT) break;
+  lx--; c = gel(x,lx);
+  t = typ(c); if (is_matvec_t(t)) c = content(c);
+  if (i > lx)
+  { /* integer coeffs */
+    while (lx-- > lontyp[tx])
+    {
+      c = gcdii(c, gel(x,lx));
+      if (is_pm1(c)) { avma=av; return gen_1; }
+    }
+  }
+  else
+  {
+    if (isinexact(c)) c = zero_gcd(c, typ(c));
+    while (lx-- > lontyp[tx])
+    {
+      GEN d = gel(x,lx);
+      t = typ(d); if (is_matvec_t(t)) d = content(d);
+      c = ggcd(c, d);
+    }
+    if (isinexact(c)) { avma=av; return gen_1; }
+  }
+  switch(typ(c))
+  {
+    case t_INT:
+      if (signe(c) < 0) c = negi(c);
+      break;
+    case t_VEC: case t_COL: case t_MAT:
+      pari_err_TYPE("content",x);
+  }
+
+  return av==avma? gcopy(c): gerepileupto(av,c);
+}
+
+GEN
+primitive_part(GEN x, GEN *ptc)
+{
+  pari_sp av = avma;
+  GEN c = content(x);
+  if (gequal1(c)) { avma = av; c = NULL; }
+  else if (!gequal0(c)) x = gdiv(x,c);
+  if (ptc) *ptc = c;
+  return x;
+}
+GEN
+primpart(GEN x) { return primitive_part(x, NULL); }
+
+/* As content(), but over Q. Treats polynomial as elts of Q[x1,...xn], instead
+ * of Q(x2,...,xn)[x1] */
+GEN
+Q_content(GEN x)
+{
+  long i, l;
+  GEN d;
+  pari_sp av;
+
+  switch(typ(x))
+  {
+    case t_INT:  return absi(x);
+    case t_FRAC: return absfrac(x);
+
+    case t_VEC: case t_COL: case t_MAT:
+      l = lg(x); if (l == 1) return gen_1;
+      av = avma; d = Q_content(gel(x,1));
+      for (i=2; i<l; i++)
+      {
+        d = Q_gcd(d, Q_content(gel(x,i)));
+        if ((i & 255) == 0) d = gerepileupto(av, d);
+      }
+      return gerepileupto(av, d);
+
+    case t_POL:
+      l = lg(x); if (l == 2) return gen_0;
+      av = avma; d = Q_content(gel(x,2));
+      for (i=3; i<l; i++) d = Q_gcd(d, Q_content(gel(x,i)));
+      return gerepileupto(av, d);
+    case t_POLMOD: return Q_content(gel(x,2));
+    case t_COMPLEX: return Q_gcd(Q_content(gel(x,1)), Q_content(gel(x,2)));
+  }
+  pari_err_TYPE("Q_content",x);
+  return NULL; /* not reached */
+}
+
+GEN
+ZX_content(GEN x)
+{
+  long i, l = lg(x);
+  GEN d;
+  pari_sp av;
+
+  if (l == 2) return gen_0;
+  d = gel(x,2);
+  if (l == 3) return absi(d);
+  av = avma;
+  for (i=3; !is_pm1(d) && i<l; i++) d = gcdii(d, gel(x,i));
+  if (signe(d) < 0) d = absi(d);
+  return gerepileuptoint(av, d);
+}
+
+/* NOT MEMORY CLEAN (because of t_FRAC).
+ * As denom(), but over Q. Treats polynomial as elts of Q[x1,...xn], instead
+ * of Q(x2,...,xn)[x1] */
+GEN
+Q_denom(GEN x)
+{
+  long i, l;
+  GEN d, D;
+  pari_sp av;
+
+  switch(typ(x))
+  {
+    case t_INT: return gen_1;
+    case t_FRAC: return gel(x,2);
+
+    case t_VEC: case t_COL: case t_MAT:
+      l = lg(x); if (l == 1) return gen_1;
+      av = avma; d = Q_denom(gel(x,1));
+      for (i=2; i<l; i++)
+      {
+        D = Q_denom(gel(x,i));
+        if (D != gen_1) d = lcmii(d, D);
+        if ((i & 255) == 0) d = gerepileuptoint(av, d);
+      }
+      return gerepileuptoint(av, d);
+
+    case t_POL:
+      l = lg(x); if (l == 2) return gen_1;
+      av = avma; d = Q_denom(gel(x,2));
+      for (i=3; i<l; i++)
+      {
+        D = Q_denom(gel(x,i));
+        if (D != gen_1) d = lcmii(d, D);
+      }
+      return gerepileuptoint(av, d);
+  }
+  pari_err_TYPE("Q_denom",x);
+  return NULL; /* not reached */
+}
+
+GEN
+Q_remove_denom(GEN x, GEN *ptd)
+{
+  GEN d = Q_denom(x);
+  if (d == gen_1) d = NULL; else x = Q_muli_to_int(x,d);
+  if (ptd) *ptd = d;
+  return x;
+}
+
+/* return y = x * d, assuming x rational, and d,y integral */
+GEN
+Q_muli_to_int(GEN x, GEN d)
+{
+  long i, l;
+  GEN y, xn, xd;
+  pari_sp av;
+
+  if (typ(d) != t_INT) pari_err_TYPE("Q_muli_to_int",d);
+  switch (typ(x))
+  {
+    case t_INT:
+      return mulii(x,d);
+
+    case t_FRAC:
+      xn = gel(x,1);
+      xd = gel(x,2); av = avma;
+      y = mulii(xn, diviiexact(d, xd));
+      return gerepileuptoint(av, y);
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &l);
+      for (i=1; i<l; i++) gel(y,i) = Q_muli_to_int(gel(x,i), d);
+      return y;
+
+    case t_POL:
+      y = cgetg_copy(x, &l); y[1] = x[1];
+      for (i=2; i<l; i++) gel(y,i) = Q_muli_to_int(gel(x,i), d);
+      return y;
+
+    case t_POLMOD:
+      y = cgetg(3, t_POLMOD);
+      gel(y,1) = RgX_copy(gel(x,1));
+      gel(y,2) = Q_muli_to_int(gel(x,2), d);
+      return y;
+  }
+  pari_err_TYPE("Q_muli_to_int",x);
+  return NULL; /* not reached */
+}
+
+/* return x * n/d. x: rational; d,n,result: integral; d,n coprime */
+static GEN
+Q_divmuli_to_int(GEN x, GEN d, GEN n)
+{
+  long i, l;
+  GEN y, xn, xd;
+  pari_sp av;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      av = avma; y = diviiexact(x,d);
+      return gerepileuptoint(av, mulii(y,n));
+
+    case t_FRAC:
+      xn = gel(x,1);
+      xd = gel(x,2); av = avma;
+      y = mulii(diviiexact(xn, d), diviiexact(n, xd));
+      return gerepileuptoint(av, y);
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &l);
+      for (i=1; i<l; i++) gel(y,i) = Q_divmuli_to_int(gel(x,i), d,n);
+      return y;
+
+    case t_POL:
+      y = cgetg_copy(x, &l); y[1] = x[1];
+      for (i=2; i<l; i++) gel(y,i) = Q_divmuli_to_int(gel(x,i), d,n);
+      return y;
+
+    case t_POLMOD:
+      y = cgetg(3, t_POLMOD);
+      gel(y,1) = RgX_copy(gel(x,1));
+      gel(y,2) = Q_divmuli_to_int(gel(x,2), d,n);
+      return y;
+  }
+  pari_err_TYPE("Q_divmuli_to_int",x);
+  return NULL; /* not reached */
+}
+
+/* return x / d. x: rational; d,result: integral. */
+static GEN
+Q_divi_to_int(GEN x, GEN d)
+{
+  long i, l;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      return diviiexact(x,d);
+
+    case t_VEC: case t_COL: case t_MAT:
+      y = cgetg_copy(x, &l);
+      for (i=1; i<l; i++) gel(y,i) = Q_divi_to_int(gel(x,i), d);
+      return y;
+
+    case t_POL:
+      y = cgetg_copy(x, &l); y[1] = x[1];
+      for (i=2; i<l; i++) gel(y,i) = Q_divi_to_int(gel(x,i), d);
+      return y;
+
+    case t_POLMOD:
+      y = cgetg(3, t_POLMOD);
+      gel(y,1) = RgX_copy(gel(x,1));
+      gel(y,2) = Q_divi_to_int(gel(x,2), d);
+      return y;
+  }
+  pari_err_TYPE("Q_divi_to_int",x);
+  return NULL; /* not reached */
+}
+/* c t_FRAC */
+static GEN
+Q_divq_to_int(GEN x, GEN c)
+{
+  GEN n = gel(c,1), d = gel(c,2);
+  if (is_pm1(n)) {
+    GEN y = Q_muli_to_int(x,d);
+    if (signe(n) < 0) y = gneg(y);
+    return y;
+  }
+  return Q_divmuli_to_int(x, n,d);
+}
+
+/* return y = x / c, assuming x,c rational, and y integral */
+GEN
+Q_div_to_int(GEN x, GEN c)
+{
+  switch(typ(c))
+  {
+    case t_INT:  return Q_divi_to_int(x, c);
+    case t_FRAC: return Q_divq_to_int(x, c);
+  }
+  pari_err_TYPE("Q_div_to_int",c);
+  return NULL; /* not reached */
+}
+/* return y = x * c, assuming x,c rational, and y integral */
+GEN
+Q_mul_to_int(GEN x, GEN c)
+{
+  GEN d, n;
+  switch(typ(c))
+  {
+    case t_INT: return Q_muli_to_int(x, c);
+    case t_FRAC:
+      n = gel(c,1);
+      d = gel(c,2);
+      return Q_divmuli_to_int(x, d,n);
+  }
+  pari_err_TYPE("Q_mul_to_int",c);
+  return NULL; /* not reached */
+}
+
+GEN
+Q_primitive_part(GEN x, GEN *ptc)
+{
+  pari_sp av = avma;
+  GEN c = Q_content(x);
+  if (typ(c) == t_INT)
+  {
+    if (is_pm1(c)) { avma = av; c = NULL; }
+    else if (signe(c)) x = Q_divi_to_int(x, c);
+  }
+  else x = Q_divq_to_int(x, c);
+  if (ptc) *ptc = c;
+  return x;
+}
+GEN
+Q_primpart(GEN x) { return Q_primitive_part(x, NULL); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                           SUBRESULTANT                          */
+/*                                                                 */
+/*******************************************************************/
+/* for internal use */
+GEN
+gdivexact(GEN x, GEN y)
+{
+  long i,lx;
+  GEN z;
+  if (gequal1(y)) return x;
+  switch(typ(x))
+  {
+    case t_INT:
+      if (typ(y)==t_INT) return diviiexact(x,y);
+      if (!signe(x)) return gen_0;
+      break;
+    case t_INTMOD:
+    case t_POLMOD: return gmul(x,ginv(y));
+    case t_POL:
+      switch(typ(y))
+      {
+        case t_INTMOD:
+        case t_POLMOD: return gmul(x,ginv(y));
+        case t_POL: { /* not stack-clean */
+          long v;
+          if (varn(x)!=varn(y)) break;
+          v = RgX_valrem(y,&y);
+          if (v) x = RgX_shift_shallow(x,-v);
+          if (!degpol(y)) { y = gel(y,2); break; }
+          return RgX_div(x,y);
+        }
+      }
+      return RgX_Rg_divexact(x, y);
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x); z = new_chunk(lx);
+      for (i=1; i<lx; i++) gel(z,i) = gdivexact(gel(x,i),y);
+      z[0] = x[0]; return z;
+  }
+  if (DEBUGLEVEL) pari_warn(warner,"missing case in gdivexact");
+  return gdiv(x,y);
+}
+
+static GEN
+init_resultant(GEN x, GEN y)
+{
+  long tx = typ(x), ty = typ(y), vx, vy;
+  if (is_scalar_t(tx) || is_scalar_t(ty))
+  {
+    if (gequal0(x) || gequal0(y)) return gmul(x,y); /* keep type info */
+    if (tx==t_POL) return gpowgs(y, degpol(x));
+    if (ty==t_POL) return gpowgs(x, degpol(y));
+    return gen_1;
+  }
+  if (tx!=t_POL) pari_err_TYPE("resultant_all",x);
+  if (ty!=t_POL) pari_err_TYPE("resultant_all",y);
+  if (!signe(x) || !signe(y)) return gmul(RgX_get_0(x),RgX_get_0(y)); /*type*/
+  vx = varn(x);
+  vy = varn(y); if (vx == vy) return NULL;
+  return (varncmp(vx,vy) < 0)? gpowgs(y,degpol(x)): gpowgs(x,degpol(y));
+}
+
+static long
+RgX_simpletype(GEN x)
+{
+  long T = t_INT, i, lx = lg(x);
+  for (i = 2; i < lx; i++)
+  {
+    GEN c = gel(x,i);
+    long tc = typ(c);
+    switch(tc) {
+      case t_INT:
+        break;
+      case t_FRAC:
+        if (T == t_INT) T = t_FRAC;
+        break;
+      default:
+        if (isinexact(c)) return t_REAL;
+        T = 0; break;
+    }
+  }
+  return T;
+}
+
+/* x an RgX, y a scalar */
+static GEN
+scalar_res(GEN x, GEN y, GEN *U, GEN *V)
+{
+  *V = gpowgs(y,degpol(x)-1);
+  *U = gen_0; return gmul(y, *V);
+}
+
+/* return 0 if the subresultant chain can be interrupted.
+ * Set u = NULL if the resultant is 0. */
+static int
+subres_step(GEN *u, GEN *v, GEN *g, GEN *h, GEN *uze, GEN *um1, long *signh)
+{
+  GEN u0, c, r, q = RgX_pseudodivrem(*u,*v, &r);
+  long du, dv, dr, degq;
+
+  dr = lg(r); if (!signe(r)) { *u = NULL; return 0; }
+  du = degpol(*u);
+  dv = degpol(*v);
+  degq = du - dv;
+  if (*um1 == gen_1)
+    u0 = gpowgs(gel(*v,dv+2),degq+1);
+  else if (*um1 == gen_0)
+    u0 = gen_0;
+  else /* except in those 2 cases, um1 is an RgX */
+    u0 = RgX_Rg_mul(*um1, gpowgs(gel(*v,dv+2),degq+1));
+
+  if (*uze == gen_0) /* except in that case, uze is an RgX */
+    u0 = scalarpol(u0, varn(*u)); /* now an RgX */
+  else
+    u0 = gsub(u0, gmul(q,*uze));
+
+  *um1 = *uze;
+  *uze = u0; /* uze <- lead(v)^(degq + 1) * um1 - q * uze */
+
+  *u = *v; c = *g; *g  = leading_term(*u);
+  switch(degq)
+  {
+    case 0: break;
+    case 1:
+      c = gmul(*h,c); *h = *g; break;
+    default:
+      c = gmul(gpowgs(*h,degq), c);
+      *h = gdivexact(gpowgs(*g,degq), gpowgs(*h,degq-1));
+  }
+  *v  = RgX_Rg_divexact(r,c);
+  *uze= RgX_Rg_divexact(*uze,c);
+  if (both_odd(du, dv)) *signh = -*signh;
+  return (dr > 3);
+}
+
+/* compute U, V s.t Ux + Vy = resultant(x,y) */
+static GEN
+subresext_i(GEN x, GEN y, GEN *U, GEN *V)
+{
+  pari_sp av, av2, lim;
+  long dx, dy, du, signh, tx = typ(x), ty = typ(y);
+  GEN r, z, g, h, p1, cu, cv, u, v, um1, uze, vze;
+
+  if (!is_extscalar_t(tx)) pari_err_TYPE("subresext",x);
+  if (!is_extscalar_t(ty)) pari_err_TYPE("subresext",y);
+  if (gequal0(x) || gequal0(y)) { *U = *V = gen_0; return gen_0; }
+  if (tx != t_POL) {
+    if (ty != t_POL) { *U = ginv(x); *V = gen_0; return gen_1; }
+    return scalar_res(y,x,V,U);
+  }
+  if (ty != t_POL) return scalar_res(x,y,U,V);
+  if (varn(x) != varn(y))
+    return varncmp(varn(x), varn(y)) < 0? scalar_res(x,y,U,V)
+                                        : scalar_res(y,x,V,U);
+  dx = degpol(x); dy = degpol(y); signh = 1;
+  if (dx < dy)
+  {
+    pswap(U,V); lswap(dx,dy); swap(x,y);
+    if (both_odd(dx, dy)) signh = -signh;
+  }
+  if (dy == 0)
+  {
+    *V = gpowgs(gel(y,2),dx-1);
+    *U = gen_0; return gmul(*V,gel(y,2));
+  }
+  av = avma;
+  u = x = primitive_part(x, &cu);
+  v = y = primitive_part(y, &cv);
+  g = h = gen_1; av2 = avma; lim = stack_lim(av2,1);
+  um1 = gen_1; uze = gen_0;
+  for(;;)
+  {
+    if (!subres_step(&u, &v, &g, &h, &uze, &um1, &signh)) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"subresext, dr = %ld", degpol(v));
+      gerepileall(av2,6, &u,&v,&g,&h,&uze,&um1);
+    }
+  }
+  /* uze an RgX */
+  if (!u) { *U = *V = gen_0; avma = av; return gen_0; }
+  z = gel(v,2); du = degpol(u);
+  if (du > 1)
+  { /* z = gdivexact(gpowgs(z,du), gpowgs(h,du-1)); */
+    p1 = gpowgs(gdiv(z,h),du-1);
+    z = gmul(z,p1);
+    uze = RgX_Rg_mul(uze, p1);
+  }
+  if (signh < 0) { z = gneg_i(z); uze = RgX_neg(uze); }
+
+  vze = RgX_divrem(Rg_RgX_sub(z, RgX_mul(uze,x)), y, &r);
+  if (signe(r)) pari_warn(warner,"inexact computation in subresext");
+  /* uze ppart(x) + vze ppart(y) = z = resultant(ppart(x), ppart(y)), */
+  p1 = gen_1;
+  if (cu) p1 = gmul(p1, gpowgs(cu,dy));
+  if (cv) p1 = gmul(p1, gpowgs(cv,dx));
+  cu = cu? gdiv(p1,cu): p1;
+  cv = cv? gdiv(p1,cv): p1;
+  z = gmul(z,p1);
+  *U = RgX_Rg_mul(uze,cu);
+  *V = RgX_Rg_mul(vze,cv);
+  return z;
+}
+GEN
+subresext(GEN x, GEN y, GEN *U, GEN *V)
+{
+  pari_sp av = avma;
+  GEN z = subresext_i(x, y, U, V);
+  gerepileall(av, 3, &z, U, V);
+  return z;
+}
+
+static GEN
+zero_extgcd(GEN y, GEN *U, GEN *V, long vx)
+{
+  GEN x=content(y);
+  *U=pol_0(vx); *V = scalarpol(ginv(x), vx); return gmul(y,*V);
+}
+
+static int
+must_negate(GEN x)
+{
+  GEN t = leading_term(x);
+  switch(typ(t))
+  {
+    case t_INT: case t_REAL:
+      return (signe(t) < 0);
+    case t_FRAC:
+      return (signe(gel(t,1)) < 0);
+  }
+  return 0;
+}
+
+/* compute U, V s.t Ux + Vy = GCD(x,y) using subresultant */
+GEN
+RgX_extgcd(GEN x, GEN y, GEN *U, GEN *V)
+{
+  pari_sp av, av2, tetpil, lim;
+  long signh; /* junk */
+  long dx, dy, vx, tx = typ(x), ty = typ(y);
+  GEN z, g, h, p1, cu, cv, u, v, um1, uze, vze, *gptr[3];
+
+  if (tx!=t_POL) pari_err_TYPE("RgX_extgcd",x);
+  if (ty!=t_POL) pari_err_TYPE("RgX_extgcd",y);
+  if ( varncmp(varn(x),varn(y))) pari_err_VAR("RgX_extgcd",x,y);
+  vx=varn(x);
+  if (!signe(x))
+  {
+    if (signe(y)) return zero_extgcd(y,U,V,vx);
+    *U = pol_0(vx); *V = pol_0(vx);
+    return pol_0(vx);
+  }
+  if (!signe(y)) return zero_extgcd(x,V,U,vx);
+  dx = degpol(x); dy = degpol(y);
+  if (dx < dy) { pswap(U,V); lswap(dx,dy); swap(x,y); }
+  if (dy==0) { *U=pol_0(vx); *V=ginv(y); return pol_1(vx); }
+
+  av = avma;
+  u = x = primitive_part(x, &cu);
+  v = y = primitive_part(y, &cv);
+  g = h = gen_1; av2 = avma; lim = stack_lim(av2,1);
+  um1 = gen_1; uze = gen_0;
+  for(;;)
+  {
+    if (!subres_step(&u, &v, &g, &h, &uze, &um1, &signh)) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgX_extgcd, dr = %ld",degpol(v));
+      gerepileall(av2,6,&u,&v,&g,&h,&uze,&um1);
+    }
+  }
+  if (uze != gen_0) {
+    GEN r;
+    vze = RgX_divrem(RgX_sub(v, RgX_mul(uze,x)), y, &r);
+    if (signe(r)) pari_warn(warner,"inexact computation in RgX_extgcd");
+    if (cu) uze = RgX_Rg_div(uze,cu);
+    if (cv) vze = RgX_Rg_div(vze,cv);
+    p1 = ginv(content(v));
+  }
+  else /* y | x */
+  {
+    vze = cv ? RgX_Rg_div(pol_1(vx),cv): pol_1(vx);
+    uze = pol_0(vx);
+    p1 = gen_1;
+  }
+  if (must_negate(v)) p1 = gneg(p1);
+  tetpil = avma;
+  z = RgX_Rg_mul(v,p1);
+  *U = RgX_Rg_mul(uze,p1);
+  *V = RgX_Rg_mul(vze,p1);
+  gptr[0] = &z;
+  gptr[1] = U;
+  gptr[2] = V;
+  gerepilemanysp(av,tetpil,gptr,3); return z;
+}
+
+int
+RgXQ_ratlift(GEN x, GEN T, long amax, long bmax, GEN *P, GEN *Q)
+{
+  pari_sp av = avma, av2, tetpil, lim;
+  long signh; /* junk */
+  long vx;
+  GEN g, h, p1, cu, cv, u, v, um1, uze, *gptr[2];
+
+  if (typ(x)!=t_POL) pari_err_TYPE("RgXQ_ratlift",x);
+  if (typ(T)!=t_POL) pari_err_TYPE("RgXQ_ratlift",T);
+  if ( varncmp(varn(x),varn(T)) ) pari_err_VAR("RgXQ_ratlift",x,T);
+  if (bmax < 0) pari_err_DOMAIN("ratlift", "bmax", "<", gen_0, stoi(bmax));
+  if (!signe(T)) {
+    if (degpol(x) <= amax) {
+      *P = RgX_copy(x);
+      *Q = pol_1(varn(x));
+      return 1;
+    }
+    return 0;
+  }
+  if (amax+bmax >= degpol(T))
+    pari_err_DOMAIN("ratlift", "amax+bmax", ">=", stoi(degpol(T)),
+                    mkvec3(stoi(amax), stoi(bmax), T));
+  vx = varn(T);
+  u = x = primitive_part(x, &cu);
+  v = T = primitive_part(T, &cv);
+  g = h = gen_1; av2 = avma; lim = stack_lim(av2,1);
+  um1 = gen_1; uze = gen_0;
+  for(;;)
+  {
+    (void) subres_step(&u, &v, &g, &h, &uze, &um1, &signh);
+    if (!u || (typ(uze)==t_POL && degpol(uze)>bmax)) { avma=av; return 0; }
+    if (typ(v)!=t_POL || degpol(v)<=amax) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"RgXQ_ratlift, dr = %ld", degpol(v));
+      gerepileall(av2,6,&u,&v,&g,&h,&uze,&um1);
+    }
+  }
+  if (uze == gen_0)
+  {
+    avma = av; *P = pol_0(vx); *Q = pol_1(vx);
+    return 1;
+  }
+  if (cu) uze = RgX_Rg_div(uze,cu);
+  p1 = ginv(content(v));
+  if (must_negate(v)) p1 = gneg(p1);
+  tetpil = avma;
+  *P = RgX_Rg_mul(v,p1);
+  *Q = RgX_Rg_mul(uze,p1);
+  gptr[0] = P;
+  gptr[1] = Q;
+  gerepilemanysp(av,tetpil,gptr,2); return 1;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                RESULTANT USING DUCOS VARIANT                    */
+/*                                                                 */
+/*******************************************************************/
+/* x^n / y^(n-1), assume n > 0 */
+static GEN
+Lazard(GEN x, GEN y, long n)
+{
+  long a;
+  GEN c;
+
+  if (n == 1) return x;
+  a = 1 << expu(n); /* a = 2^k <= n < 2^(k+1) */
+  c=x; n-=a;
+  while (a>1)
+  {
+    a>>=1; c=gdivexact(gsqr(c),y);
+    if (n>=a) { c=gdivexact(gmul(c,x),y); n -= a; }
+  }
+  return c;
+}
+
+/* F (x/y)^(n-1), assume n >= 1 */
+static GEN
+Lazard2(GEN F, GEN x, GEN y, long n)
+{
+  if (n == 1) return F;
+  return RgX_Rg_divexact(RgX_Rg_mul(F, Lazard(x,y,n-1)), y);
+}
+
+static GEN
+RgX_neg_i(GEN x, long lx)
+{
+  long i;
+  GEN y = cgetg(lx, t_POL); y[1] = x[1];
+  for (i=2; i<lx; i++) gel(y,i) = gneg(gel(x,i));
+  return y;
+}
+static GEN
+RgX_Rg_mul_i(GEN y, GEN x, long ly)
+{
+  long i;
+  GEN z;
+  if (isrationalzero(x)) return pol_0(varn(y));
+  z = cgetg(ly,t_POL); z[1] = y[1];
+  for (i = 2; i < ly; i++) gel(z,i) = gmul(x,gel(y,i));
+  return z;
+}
+static long
+reductum_lg(GEN x, long lx)
+{
+  long i = lx-2;
+  while (i > 1 && gequal0(gel(x,i))) i--;
+  return i+1;
+}
+
+/* delta = deg(P) - deg(Q) > 0, deg(Q) > 0, P,Q,Z t_POL in the same variable,
+ * s "scalar". Return prem(P, -Q) / s^delta lc(P) */
+static GEN
+nextSousResultant(GEN P, GEN Q, GEN Z, GEN s)
+{
+  GEN p0, q0, h0, TMP, H, A, z0 = leading_term(Z);
+  long p, q, j, lP, lQ;
+  pari_sp av, lim;
+
+  p = degpol(P); p0 = gel(P,p+2); lP = reductum_lg(P,lg(P));
+  q = degpol(Q); q0 = gel(Q,q+2); lQ = reductum_lg(Q,lg(Q));
+  /* p > q. Very often p - 1 = q */
+  av = avma; lim = stack_lim(av,1);
+  /* H = RgX_neg(reductum(Z)) optimized, using Q ~ Z */
+  H = RgX_neg_i(Z, lQ); /* deg H < q */
+
+  A = (q+2 < lP)? RgX_Rg_mul_i(H, gel(P,q+2), lQ): NULL;
+  for (j = q+1; j < p; j++)
+  {
+    if (degpol(H) == q-1)
+    { /* h0 = coeff of degree q-1 = leading coeff */
+      h0 = gel(H,q+1); (void)normalizepol_lg(H, q+1);
+      H = addshift(H, RgX_Rg_divexact(RgX_Rg_mul_i(Q, gneg(h0), lQ), q0));
+    }
+    else
+      H = RgX_shift_shallow(H, 1);
+    if (j+2 < lP)
+    {
+      TMP = RgX_Rg_mul(H, gel(P,j+2));
+      A = A? RgX_add(A, TMP): TMP;
+    }
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"nextSousResultant j = %ld/%ld",j,p);
+      gerepileall(av,A?2:1,&H,&A);
+    }
+  }
+  if (q+2 < lP) lP = reductum_lg(P, q+3);
+  TMP = RgX_Rg_mul_i(P, z0, lP);
+  A = A? RgX_add(A, TMP): TMP;
+  A = RgX_Rg_divexact(A, p0);
+  if (degpol(H) == q-1)
+  {
+    h0 = gel(H,q+1); (void)normalizepol_lg(H, q+1); /* destroy old H */
+    A = RgX_add(RgX_Rg_mul(addshift(H,A),q0), RgX_Rg_mul_i(Q, gneg(h0), lQ));
+  }
+  else
+    A = RgX_Rg_mul(addshift(H,A), q0);
+  return RgX_Rg_divexact(A, s);
+}
+
+/* Ducos's subresultant */
+GEN
+RgX_resultant_all(GEN P, GEN Q, GEN *sol)
+{
+  pari_sp av, av2, lim;
+  long dP, dQ, delta, sig = 1;
+  GEN cP, cQ, Z, s;
+
+  dP = degpol(P);
+  dQ = degpol(Q); delta = dP - dQ;
+  if (delta < 0)
+  {
+    if (both_odd(dP, dQ)) sig = -1;
+    swap(P,Q); lswap(dP, dQ); delta = -delta;
+  }
+  if (sol) *sol = gen_0;
+  av = avma;
+  if (dQ <= 0)
+  {
+    if (dQ < 0) return RgX_get_0(P);
+    s = gpowgs(gel(Q,2), dP);
+    if (sig == -1) s = gerepileupto(av, gneg(s));
+    return s;
+  }
+  P = primitive_part(P, &cP);
+  Q = primitive_part(Q, &cQ);
+  av2 = avma; lim = stack_lim(av2,1);
+  s = gpowgs(leading_term(Q),delta);
+  if (both_odd(dP, dQ)) sig = -sig;
+  Z = Q;
+  Q = RgX_pseudorem(P, Q);
+  P = Z;
+  while(degpol(Q) > 0)
+  {
+    delta = degpol(P) - degpol(Q); /* > 0 */
+    Z = Lazard2(Q, leading_term(Q), s, delta);
+    if (both_odd(degpol(P), degpol(Q))) sig = -sig;
+    Q = nextSousResultant(P, Q, Z, s);
+    P = Z;
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"resultant_all, degpol Q = %ld",degpol(Q));
+      gerepileall(av2,2,&P,&Q);
+    }
+    s = leading_term(P);
+  }
+  if (!signe(Q)) { avma = av; return RgX_get_0(Q); }
+  av2 = avma;
+  s = Lazard(leading_term(Q), s, degpol(P));
+  if (sig == -1) s = gneg(s);
+  if (cP) s = gmul(s, gpowgs(cP,dQ));
+  if (cQ) s = gmul(s, gpowgs(cQ,dP));
+  if (sol) { *sol = P; gerepileall(av, 2, &s, sol); return s; }
+  return (avma == av2)? gerepilecopy(av, s): gerepileupto(av, s);
+}
+/* Return resultant(P,Q). If sol != NULL: set *sol to the last non-constant
+ * polynomial in the prs IF the sequence was computed, and gen_0 otherwise.
+ * Uses Sylvester's matrix if P or Q inexact, a modular algorithm if they
+ * are in Q[X], and Ducos/Lazard optimization of the subresultant algorithm
+ * in the "generic" case. */
+GEN
+resultant_all(GEN P, GEN Q, GEN *sol)
+{
+  long TP, TQ;
+  GEN s;
+
+  if (sol) *sol = gen_0;
+  if ((s = init_resultant(P,Q))) return s;
+  if ((TP = RgX_simpletype(P)) == t_REAL || (TQ = RgX_simpletype(Q)) == t_REAL)
+    return resultant2(P,Q); /* inexact */
+  if (TP && TQ) /* rational */
+  {
+    if (TP == t_INT && TQ == t_INT) return ZX_resultant(P,Q);
+    return QX_resultant(P,Q);
+  }
+  return RgX_resultant_all(P, Q, sol);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*               RESULTANT USING SYLVESTER MATRIX                  */
+/*                                                                 */
+/*******************************************************************/
+static GEN
+_pol_0(void)
+{
+  GEN x = cgetg(3,t_POL);
+  x[1] = 0;
+  gel(x,2) = gen_0; return x;
+}
+
+static GEN
+sylvester_col(GEN x, long j, long d, long D)
+{
+  GEN c = cgetg(d+1,t_COL);
+  long i;
+  for (i=1; i< j; i++) gel(c,i) = gen_0;
+  for (   ; i<=D; i++) gel(c,i) = gel(x,D-i+2);
+  for (   ; i<=d; i++) gel(c,i) = gen_0;
+  return c;
+}
+
+GEN
+sylvestermatrix_i(GEN x, GEN y)
+{
+  long j,d,dx,dy;
+  GEN M;
+
+  dx = degpol(x); if (dx < 0) { dx = 0; x = _pol_0(); }
+  dy = degpol(y); if (dy < 0) { dy = 0; y = _pol_0(); }
+  d = dx+dy; M = cgetg(d+1,t_MAT);
+  for (j=1; j<=dy; j++) gel(M,j)    = sylvester_col(x,j,d,j+dx);
+  for (j=1; j<=dx; j++) gel(M,j+dy) = sylvester_col(y,j,d,j+dy);
+  return M;
+}
+
+GEN
+sylvestermatrix(GEN x, GEN y)
+{
+  long i,j,lx;
+  if (typ(x)!=t_POL) pari_err_TYPE("sylvestermatrix",x);
+  if (typ(y)!=t_POL) pari_err_TYPE("sylvestermatrix",y);
+  if (varn(x) != varn(y)) pari_err_VAR("sylvestermatrix",x,y);
+  x = sylvestermatrix_i(x,y); lx = lg(x);
+  for (i=1; i<lx; i++)
+    for (j=1; j<lx; j++) gcoeff(x,i,j) = gcopy(gcoeff(x,i,j));
+  return x;
+}
+
+GEN
+resultant2(GEN x, GEN y)
+{
+  pari_sp av;
+  GEN r;
+  if ((r = init_resultant(x,y))) return r;
+  av = avma; return gerepileupto(av,det(sylvestermatrix_i(x,y)));
+}
+
+/* Let vx = main variable of x. Return a polynomial in variable 0:
+ * if vx is 0 and v != 0, set *mx = 1 and replace vx by pol_x(MAXVARN)
+ * if vx = v, copy x, set its main variable to 0 and return
+ * if vx < v, return subst(x, v, pol_x(0))
+ * if vx > v, return scalarpol(x, 0) */
+static GEN
+fix_pol(GEN x, long v, long *mx)
+{
+  long vx;
+  if (typ(x) != t_POL) return x;
+  vx = varn(x);
+  if (v == vx)
+  {
+    if (v) { x = leafcopy(x); setvarn(x, 0); }
+    return x;
+  }
+  if (!vx)
+  {
+    *mx = 1;
+    x = poleval(x, pol_x(MAXVARN));
+    vx = varn(x);
+    if (v == vx) { setvarn(x, 0); return x; }
+  }
+  if (varncmp(v, vx) > 0)
+  {
+    x = gsubst(x,v,pol_x(0));
+    if (typ(x) == t_POL && varn(x) == 0) return x;
+  }
+  return scalarpol_shallow(x, 0);
+}
+
+/* resultant of x and y with respect to variable v, or with respect to their
+ * main variable if v < 0. */
+GEN
+polresultant0(GEN x, GEN y, long v, long flag)
+{
+  long m = 0;
+  pari_sp av = avma;
+
+  if (v >= 0)
+  {
+    x = fix_pol(x,v, &m);
+    y = fix_pol(y,v, &m);
+  }
+  switch(flag)
+  {
+    case 2:
+    case 0: x=resultant_all(x,y,NULL); break;
+    case 1: x=resultant2(x,y); break;
+    default: pari_err_FLAG("polresultant");
+  }
+  if (m) x = gsubst(x,MAXVARN,pol_x(0));
+  return gerepileupto(av,x);
+}
+GEN
+polresultantext0(GEN x, GEN y, long v)
+{
+  GEN R, U, V;
+  long m = 0;
+  pari_sp av = avma;
+
+  if (v >= 0)
+  {
+    x = fix_pol(x,v, &m);
+    y = fix_pol(y,v, &m);
+  }
+  R = subresext_i(x,y, &U,&V);
+  if (m)
+  {
+    U = gsubst(gsubst(U, 0, pol_x(v)), MAXVARN, pol_x(0));
+    V = gsubst(gsubst(V, 0, pol_x(v)), MAXVARN, pol_x(0));
+    R = gsubst(R,MAXVARN,pol_x(0));
+  }
+  else if (v >= 0)
+  {
+    if (typ(U) == t_POL && varn(U) != v) U = poleval(U, pol_x(v));
+    if (typ(V) == t_POL && varn(V) != v) V = poleval(V, pol_x(v));
+  }
+  return gerepilecopy(av, mkvec3(U,V,R));
+}
+GEN
+polresultantext(GEN x, GEN y) { return polresultantext0(x,y,-1); }
+
+/*******************************************************************/
+/*                                                                 */
+/*             CHARACTERISTIC POLYNOMIAL USING RESULTANT           */
+/*                                                                 */
+/*******************************************************************/
+
+/* (v - x)^d */
+static GEN
+caract_const(pari_sp av, GEN x, long v, long d)
+{ return gerepileupto(av, gpowgs(deg1pol_shallow(gen_1, gneg_i(x), v), d)); }
+
+/* return caract(Mod(x,T)) in variable v */
+GEN
+RgXQ_charpoly(GEN x, GEN T, long v)
+{
+  pari_sp av = avma;
+  long d = degpol(T), dx, vx, vp;
+  GEN ch, L;
+
+  if (typ(x) != t_POL) return caract_const(av, x, v, d);
+  vx = varn(x);
+  vp = varn(T);
+  if (varncmp(vx, vp) > 0) return caract_const(av, x, v, d);
+  if (varncmp(vx, vp) < 0) pari_err_PRIORITY("RgXQ_charpoly", x, "<", vp);
+  dx = degpol(x);
+  if (dx <= 0)
+    return dx? monomial(gen_1, d, v): caract_const(av, gel(x,2), v, d);
+
+  x = RgX_neg(x);
+  if (varn(x) == MAXVARN) { setvarn(x, 0); T = leafcopy(T); setvarn(T, 0); }
+  gel(x,2) = gadd(gel(x,2), pol_x(MAXVARN));
+  ch = resultant_all(T, x, NULL);
+  if (v != MAXVARN)
+  {
+    if (typ(ch) == t_POL && varn(ch) == MAXVARN)
+      setvarn(ch, v);
+    else
+      ch = gsubst(ch, MAXVARN, pol_x(v));
+  }
+  /* test for silly input: x mod (deg 0 polynomial) */
+  if (typ(ch) != t_POL) { avma = av; return pol_1(v); }
+
+  L = leading_term(ch);
+  if (!gequal1(L)) ch = RgX_Rg_div(ch, L);
+  return gerepileupto(av, ch);
+}
+
+/* characteristic polynomial (in v) of x over nf, where x is an element of the
+ * algebra nf[t]/(Q(t)) */
+GEN
+rnfcharpoly(GEN nf, GEN Q, GEN x, long v)
+{
+  const char *f = "rnfcharpoly";
+  long dQ = degpol(Q);
+  pari_sp av = avma;
+  GEN T;
+
+  if (v < 0) v = 0;
+  nf = checknf(nf); T = nf_get_pol(nf);
+  Q = RgX_nffix(f, T,Q,0);
+  switch(typ(x))
+  {
+    case t_INT:
+    case t_FRAC: return caract_const(av, x, v, dQ);
+    case t_POLMOD:
+      x = polmod_nffix2(f,T,Q, x,0);
+      break;
+    case t_POL:
+      x = varn(x) == varn(T)? Rg_nffix(f,T,x,0): RgX_nffix(f, T,x,0);
+      break;
+    default: pari_err_TYPE(f,x);
+  }
+  if (typ(x) != t_POL) return caract_const(av, x, v, dQ);
+  /* x a t_POL in variable vQ */
+  if (degpol(x) >= dQ) x = RgX_rem(x, Q);
+  if (dQ <= 1) return caract_const(av, constant_term(x), v, 1);
+  return gerepilecopy(av, lift_if_rational( RgXQ_charpoly(x, Q, v) ));
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                  GCD USING SUBRESULTANT                         */
+/*                                                                 */
+/*******************************************************************/
+static int inexact(GEN x, int *simple, int *rational);
+static int
+isinexactall(GEN x, int *simple, int *rational)
+{
+  long i, lx = lg(x);
+  for (i=2; i<lx; i++)
+    if (inexact(gel(x,i), simple, rational)) return 1;
+  return 0;
+}
+/* return 1 if coeff explosion is not possible */
+static int
+inexact(GEN x, int *simple, int *rational)
+{
+  int junk = 0;
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC: return 0;
+
+    case t_REAL: case t_PADIC: case t_SER: return 1;
+
+    case t_INTMOD:
+    case t_FFELT:
+      *rational = 0;
+      if (!*simple) *simple = 1;
+      return 0;
+
+    case t_COMPLEX:
+      *rational = 0;
+      return inexact(gel(x,1), simple, rational)
+          || inexact(gel(x,2), simple, rational);
+    case t_QUAD:
+      *rational = *simple = 0;
+      return inexact(gel(x,2), &junk, rational)
+          || inexact(gel(x,3), &junk, rational);
+
+    case t_POLMOD:
+      *rational = 0;
+      return isinexactall(gel(x,1), simple, rational);
+    case t_POL:
+      *rational = 0;
+      *simple = -1;
+      return isinexactall(x, &junk, rational);
+    case t_RFRAC:
+      *rational = 0;
+      *simple = -1;
+      return inexact(gel(x,1), &junk, rational)
+          || inexact(gel(x,2), &junk, rational);
+  }
+  *rational = 0;
+  *simple = -1; return 0;
+}
+
+/* x monomial, y t_POL in the same variable */
+static GEN
+gcdmonome(GEN x, GEN y)
+{
+  pari_sp av = avma;
+  long dx = degpol(x), e = RgX_valrem(y, &y);
+  long i, l = lg(y);
+  GEN t, v = cgetg(l, t_VEC);
+  gel(v,1) = gel(x,dx+2);
+  for (i = 2; i < l; i++) gel(v,i) = gel(y,i);
+  t = content(v); /* gcd(lc(x), cont(y)) */
+  t = simplify_shallow(t);
+  if (dx < e) e = dx;
+  return gerepileupto(av, monomialcopy(t, e, varn(x)));
+}
+
+/* x, y are t_POL in the same variable */
+GEN
+RgX_gcd(GEN x, GEN y)
+{
+  long dx, dy;
+  pari_sp av, av1, lim;
+  GEN d, g, h, p1, p2, u, v;
+  int simple = 0, rational = 1;
+
+  if (isexactzero(y)) return RgX_copy(x);
+  if (isexactzero(x)) return RgX_copy(y);
+  if (RgX_is_monomial(x)) return gcdmonome(x,y);
+  if (RgX_is_monomial(y)) return gcdmonome(y,x);
+  if (isinexactall(x,&simple,&rational) || isinexactall(y,&simple,&rational))
+  {
+    av = avma; u = ggcd(content(x), content(y));
+    return gerepileupto(av, scalarpol(u, varn(x)));
+  }
+  if (rational) return QX_gcd(x,y); /* Q[X] */
+
+  av = avma;
+  if (simple > 0) x = RgX_gcd_simple(x,y);
+  else
+  {
+    dx = lg(x); dy = lg(y);
+    if (dx < dy) { swap(x,y); lswap(dx,dy); }
+    if (dy==3)
+    {
+      d = ggcd(gel(y,2), content(x));
+      return gerepileupto(av, scalarpol(d, varn(x)));
+    }
+    u = primitive_part(x, &p1); if (!p1) p1 = gen_1;
+    v = primitive_part(y, &p2); if (!p2) p2 = gen_1;
+    d = ggcd(p1,p2);
+    av1 = avma; lim = stack_lim(av1,1);
+    g = h = gen_1;
+    for(;;)
+    {
+      GEN r = RgX_pseudorem(u,v);
+      long degq, du, dv, dr = lg(r);
+
+      if (!signe(r)) break;
+      if (dr <= 3)
+      {
+        avma = av1; return gerepileupto(av, scalarpol(d, varn(x)));
+      }
+      if (DEBUGLEVEL > 9) err_printf("RgX_gcd: dr = %ld\n", degpol(r));
+      du = lg(u); dv = lg(v); degq = du-dv;
+      u = v; p1 = g; g = leading_term(u);
+      switch(degq)
+      {
+        case 0: break;
+        case 1:
+          p1 = gmul(h,p1); h = g; break;
+        default:
+          p1 = gmul(gpowgs(h,degq), p1);
+          h = gdiv(gpowgs(g,degq), gpowgs(h,degq-1));
+      }
+      v = RgX_Rg_div(r,p1);
+      if (low_stack(lim, stack_lim(av1,1)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"RgX_gcd");
+        gerepileall(av1,4, &u,&v,&g,&h);
+      }
+    }
+    x = RgX_Rg_mul(primpart(v), d);
+  }
+  if (must_negate(x)) x = RgX_neg(x);
+  return gerepileupto(av,x);
+}
+
+static GEN
+RgX_disc_aux(GEN x)
+{
+  long dx = degpol(x), Tx;
+  GEN D, L, y, p;
+  if (!signe(x) || !dx) return RgX_get_0(x);
+  if (dx == 1) return RgX_get_1(x);
+  if (dx == 2) {
+    GEN a = gel(x,4), b = gel(x,3), c = gel(x,2);
+    return gsub(gsqr(b), gmul2n(gmul(a,c),2));
+  }
+  Tx = RgX_simpletype(x);
+  if (Tx == t_INT) return ZX_disc(x);
+  if (Tx == t_FRAC) return QX_disc(x);
+  p = NULL;
+  if (RgX_is_FpX(x, &p) && p)
+    return Fp_to_mod(FpX_disc(RgX_to_FpX(x,p), p), p);
+
+  y = RgX_deriv(x);
+  if (!signe(y)) return RgX_get_0(y);
+  if (Tx == t_REAL)
+    D = resultant2(x,y);
+  else
+  {
+    D = RgX_resultant_all(x, y, NULL);
+    if (D == gen_0) return RgX_get_0(y);
+  }
+  L = leading_term(x); if (!gequal1(L)) D = gdiv(D,L);
+  if (dx & 2) D = gneg(D);
+  return D;
+}
+GEN
+RgX_disc(GEN x) { pari_sp av = avma; return gerepileupto(av, RgX_disc_aux(x)); }
+
+GEN
+poldisc0(GEN x, long v)
+{
+  long i;
+  pari_sp av;
+  GEN z, D;
+
+  switch(typ(x))
+  {
+    case t_POL:
+      av = avma; i = 0;
+      if (v >= 0 && v != varn(x)) x = fix_pol(x,v, &i);
+      D = RgX_disc_aux(x);
+      if (i) D = gsubst(D, MAXVARN, pol_x(0));
+      return gerepileupto(av, D);
+
+    case t_COMPLEX:
+      return utoineg(4);
+
+    case t_QUAD:
+      return quad_disc(x);
+    case t_POLMOD:
+      return poldisc0(gel(x,1), v);
+
+    case t_QFR: case t_QFI:
+      av = avma; return gerepileuptoint(av, qfb_disc(x));
+
+    case t_VEC: case t_COL: case t_MAT:
+      z = cgetg_copy(x, &i);
+      for (i--; i; i--) gel(z,i) = poldisc0(gel(x,i), v);
+      return z;
+  }
+  pari_err_TYPE("poldisc",x);
+  return NULL; /* not reached */
+}
+
+GEN
+reduceddiscsmith(GEN x)
+{
+  long j, n = degpol(x);
+  pari_sp av = avma;
+  GEN xp, M;
+
+  if (typ(x) != t_POL) pari_err_TYPE("poldiscreduced",x);
+  if (n<=0) pari_err_CONSTPOL("poldiscreduced");
+  RgX_check_ZX(x,"poldiscreduced");
+  if (!gequal1(gel(x,n+2)))
+    pari_err_IMPL("non-monic polynomial in poldiscreduced");
+  M = cgetg(n+1,t_MAT);
+  xp = ZX_deriv(x);
+  for (j=1; j<=n; j++)
+  {
+    gel(M,j) = RgX_to_RgV(xp, n);
+    if (j<n) xp = RgX_rem(RgX_shift_shallow(xp, 1), x);
+  }
+  return gerepileupto(av, ZM_snf(M));
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       STURM ALGORITHM                             **/
+/**              (number of real roots of x in ]a,b])                 **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* if a (resp. b) is NULL, set it to -oo (resp. +oo) */
+long
+sturmpart(GEN x, GEN a, GEN b)
+{
+  long sl, sr, s, t, r1;
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN g,h,u,v;
+
+  if (gequal0(x)) pari_err_ROOTS0("sturm");
+  t = typ(x);
+  if (t != t_POL)
+  {
+    if (t == t_INT || t == t_REAL || t == t_FRAC) return 0;
+    pari_err_TYPE("sturm",x);
+  }
+  s=lg(x); if (s==3) return 0;
+
+  sl = gsigne(leading_term(x));
+  if (s==4)
+  {
+    t = a? gsigne(poleval(x,a)): -sl;
+    if (t == 0) { avma = av; return 0; }
+    s = b? gsigne(poleval(x,b)):  sl;
+    avma = av; return (s == t)? 0: 1;
+  }
+  u = primpart(x);
+  v = primpart(RgX_deriv(x));
+  g=gen_1; h=gen_1;
+  s = b? gsigne(poleval(u,b)): sl;
+  t = a? gsigne(poleval(u,a)): ((lg(u)&1)? sl: -sl);
+  r1=0;
+  sr = b? gsigne(poleval(v,b)): s;
+  if (sr)
+  {
+    if (!s) s=sr;
+    else if (sr!=s) { s= -s; r1--; }
+  }
+  sr = a? gsigne(poleval(v,a)): -t;
+  if (sr)
+  {
+    if (!t) t=sr;
+    else if (sr!=t) { t= -t; r1++; }
+  }
+  for(;;)
+  {
+    GEN p1, r = RgX_pseudorem(u,v);
+    long du=lg(u), dv=lg(v), dr=lg(r), degq=du-dv;
+
+    if (dr<=2) pari_err_DOMAIN("polsturm","issquarefree(pol)","=",gen_0,x);
+    if (gsigne(leading_term(v)) > 0 || degq&1) r=gneg_i(r);
+    sl = gsigne(gel(r,dr-1));
+    sr = b? gsigne(poleval(r,b)): sl;
+    if (sr)
+    {
+      if (!s) s=sr;
+      else if (sr!=s) { s= -s; r1--; }
+    }
+    sr = a? gsigne(poleval(r,a)): ((dr&1)? sl: -sl);
+    if (sr)
+    {
+      if (!t) t=sr;
+      else if (sr!=t) { t= -t; r1++; }
+    }
+    if (dr==3) { avma=av; return r1; }
+
+    u=v; p1 = g; g = gabs(leading_term(u),DEFAULTPREC);
+    switch(degq)
+    {
+      case 0: break;
+      case 1:
+        p1 = gmul(h,p1); h = g; break;
+      default:
+        p1 = gmul(gpowgs(h,degq),p1);
+        h = gdivexact(gpowgs(g,degq), gpowgs(h,degq-1));
+    }
+    v = RgX_Rg_divexact(r,p1);
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"polsturm, dr = %ld",dr);
+      gerepileall(av,4,&u,&v,&g,&h);
+    }
+  }
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                        GENERIC EXTENDED GCD                       **/
+/**                                                                   **/
+/***********************************************************************/
+/* assume typ(x) = typ(y) = t_POL */
+GEN
+RgXQ_inv(GEN x, GEN y)
+{
+  long vx=varn(x), vy=varn(y);
+  pari_sp av;
+  GEN u, v, d;
+
+  while (vx != vy)
+  {
+    if (varncmp(vx,vy) > 0)
+    {
+      d = (vx == NO_VARIABLE)? ginv(x): gred_rfrac_simple(gen_1, x);
+      return scalarpol(d, vy);
+    }
+    if (lg(x)!=3) pari_err_INV("RgXQ_inv",mkpolmod(x,y));
+    x = gel(x,2); vx = gvar(x);
+  }
+  av = avma; d = subresext_i(x,y,&u,&v/*junk*/);
+  if (gequal0(d)) pari_err_INV("RgXQ_inv",mkpolmod(x,y));
+  d = gdiv(u,d);
+  if (typ(d) != t_POL || varn(d) != vy) d = scalarpol(d, vy);
+  return gerepileupto(av, d);
+}
+
+/*Assume x is a polynomial and y is not */
+static GEN
+scalar_bezout(GEN x, GEN y, GEN *U, GEN *V)
+{
+  long vx = varn(x);
+  int xis0 = signe(x)==0, yis0 = gequal0(y);
+  if (xis0 && yis0) { *U = *V = pol_0(vx); return pol_0(vx); }
+  if (yis0) { *U=pol_1(vx); *V = pol_0(vx); return RgX_copy(x);}
+  *U=pol_0(vx); *V= ginv(y); return pol_1(vx);
+}
+/* Assume x==0, y!=0 */
+static GEN
+zero_bezout(GEN y, GEN *U, GEN *V)
+{
+  *U=gen_0; *V = ginv(y); return gen_1;
+}
+
+GEN
+gbezout(GEN x, GEN y, GEN *u, GEN *v)
+{
+  long tx=typ(x), ty=typ(y), vx;
+  if (tx == t_INT && ty == t_INT) return bezout(x,y,u,v);
+  if (tx != t_POL)
+  {
+    if (ty == t_POL)
+      return scalar_bezout(y,x,v,u);
+    else
+    {
+      int xis0 = gequal0(x), yis0 = gequal0(y);
+      if (xis0 && yis0) { *u = *v = gen_0; return gen_0; }
+      if (xis0) return zero_bezout(y,u,v);
+      else return zero_bezout(x,v,u);
+    }
+  }
+  else if (ty != t_POL) return scalar_bezout(x,y,u,v);
+  vx = varn(x);
+  if (vx != varn(y))
+    return varncmp(vx, varn(y)) < 0? scalar_bezout(x,y,u,v)
+                                   : scalar_bezout(y,x,v,u);
+  return RgX_extgcd(x,y,u,v);
+}
+
+GEN
+gcdext0(GEN x, GEN y)
+{
+  GEN z=cgetg(4,t_VEC);
+  gel(z,3) = gbezout(x,y,(GEN*)(z+1),(GEN*)(z+2));
+  return z;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                    GENERIC (modular) INVERSE                    */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+ginvmod(GEN x, GEN y)
+{
+  long tx=typ(x);
+
+  switch(typ(y))
+  {
+    case t_POL:
+      if (tx==t_POL) return RgXQ_inv(x,y);
+      if (is_scalar_t(tx)) return ginv(x);
+      break;
+    case t_INT:
+      if (tx==t_INT) return Fp_inv(x,y);
+      if (tx==t_POL) return gen_0;
+  }
+  pari_err_TYPE2("ginvmod",x,y);
+  return NULL; /* not reached */
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                         NEWTON POLYGON                            **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* assume leading coeff of x is non-zero */
+GEN
+newtonpoly(GEN x, GEN p)
+{
+  GEN y;
+  long n,ind,a,b,c,u1,u2,r1,r2;
+  long *vval, num[] = {evaltyp(t_INT) | _evallg(3), 0, 0};
+
+  if (typ(x)!=t_POL) pari_err_TYPE("newtonpoly",x);
+  n=degpol(x); if (n<=0) return cgetg(1,t_VEC);
+  y = cgetg(n+1,t_VEC); x += 2; /* now x[i] = term of degree i */
+  vval = (long *) pari_malloc(sizeof(long)*(n+1));
+  for (a=0; a<=n; a++) vval[a] = gvaluation(gel(x,a),p);
+  for (a=0, ind=1; a<n; a++)
+  {
+    if (vval[a] != LONG_MAX) break;
+    gel(y,ind++) = utoipos(LONG_MAX);
+  }
+  for (b=a+1; b<=n; a=b, b=a+1)
+  {
+    while (vval[b] == LONG_MAX) b++;
+    u1=vval[a]-vval[b]; u2=b-a;
+    for (c=b+1; c<=n; c++)
+    {
+      if (vval[c] == LONG_MAX) continue;
+      r1=vval[a]-vval[c]; r2=c-a;
+      if (u1*r2<=u2*r1) { u1=r1; u2=r2; b=c; }
+    }
+    while (ind<=b) { affsi(u1,num); gel(y,ind++) = gdivgs(num,u2); }
+  }
+  pari_free(vval); return y;
+}
diff --git a/src/basemath/polarit3.c b/src/basemath/polarit3.c
new file mode 100644
index 0000000..b41866d
--- /dev/null
+++ b/src/basemath/polarit3.c
@@ -0,0 +1,2927 @@
+/* Copyright (C) 2000-2005  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**               ARITHMETIC OPERATIONS ON POLYNOMIALS                **/
+/**                         (third part)                              **/
+/**                                                                   **/
+/***********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+/************************************************************************
+ **                                                                    **
+ **                      Ring membership                               **
+ **                                                                    **
+ ************************************************************************/
+struct charact {
+  GEN q;
+  int isprime;
+};
+static void
+char_update_prime(struct charact *S, GEN p)
+{
+  if (!S->isprime) { S->isprime = 1; S->q = p; }
+  if (!equalii(p, S->q)) pari_err_MODULUS("characteristic", S->q, p);
+}
+static void
+char_update_int(struct charact *S, GEN n)
+{
+  if (S->isprime)
+  {
+    if (dvdii(n, S->q)) return;
+    pari_err_MODULUS("characteristic", S->q, n);
+  }
+  S->q = gcdii(S->q, n);
+}
+static void
+charact(struct charact *S, GEN x)
+{
+  const long tx = typ(x);
+  long i, l;
+  switch(tx)
+  {
+    case t_INTMOD:char_update_int(S, gel(x,1)); break;
+    case t_FFELT: char_update_prime(S, gel(x,4)); break;
+    case t_COMPLEX: case t_QUAD:
+    case t_POLMOD: case t_POL: case t_SER: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      l = lg(x);
+      for (i=lontyp[tx]; i < l; i++) charact(S,gel(x,i));
+      break;
+    case t_LIST:
+      x = list_data(x);
+      if (x) charact(S, x);
+      break;
+  }
+}
+static void
+charact_res(struct charact *S, GEN x)
+{
+  const long tx = typ(x);
+  long i, l;
+  switch(tx)
+  {
+    case t_INTMOD:char_update_int(S, gel(x,1)); break;
+    case t_FFELT: char_update_prime(S, gel(x,4)); break;
+    case t_PADIC: char_update_prime(S, gel(x,2)); break;
+    case t_COMPLEX: case t_QUAD:
+    case t_POLMOD: case t_POL: case t_SER: case t_RFRAC:
+    case t_VEC: case t_COL: case t_MAT:
+      l = lg(x);
+      for (i=lontyp[tx]; i < l; i++) charact_res(S,gel(x,i));
+      break;
+    case t_LIST:
+      x = list_data(x);
+      if (x) charact_res(S, x);
+      break;
+  }
+}
+GEN
+characteristic(GEN x)
+{
+  struct charact S;
+  S.q = gen_0; S.isprime = 0;
+  charact(&S, x); return S.q;
+}
+GEN
+residual_characteristic(GEN x)
+{
+  struct charact S;
+  S.q = gen_0; S.isprime = 0;
+  charact_res(&S, x); return S.q;
+}
+
+int
+Rg_is_Fp(GEN x, GEN *pp)
+{
+  GEN mod;
+  switch(typ(x))
+  {
+  case t_INTMOD:
+    mod = gel(x,1);
+    if (!*pp) *pp = mod;
+    else if (mod != *pp && !equalii(mod, *pp))
+    {
+      if (DEBUGMEM) pari_warn(warner,"different moduli in Rg_is_Fp");
+      return 0;
+    }
+    return 1;
+  case t_INT:
+    return 1;
+  default: return 0;
+  }
+}
+
+int
+RgX_is_FpX(GEN x, GEN *pp)
+{
+  long i, lx = lg(x);
+  for (i=2; i<lx; i++)
+    if (!Rg_is_Fp(gel(x, i), pp))
+      return 0;
+  return 1;
+}
+
+int
+RgV_is_FpV(GEN x, GEN *pp)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++)
+    if (!Rg_is_Fp(gel(x,i), pp)) return 0;
+  return 1;
+}
+
+int
+RgM_is_FpM(GEN x, GEN *pp)
+{
+  long i, lx = lg(x);
+  for (i=1; i<lx; i++)
+    if (!RgV_is_FpV(gel(x, i), pp)) return 0;
+  return 1;
+}
+
+int
+Rg_is_FpXQ(GEN x, GEN *pT, GEN *pp)
+{
+  GEN pol, mod;
+  switch(typ(x))
+  {
+  case t_INTMOD:
+    return Rg_is_Fp(x, pp);
+  case t_INT:
+    return 1;
+  case t_POL:
+    return RgX_is_FpX(x, pp);
+  case t_POLMOD:
+    mod = gel(x,1); pol = gel(x, 2);
+    if (!RgX_is_FpX(mod, pp)) return 0;
+    if (typ(pol)==t_POL)
+    {
+      if (!RgX_is_FpX(pol, pp)) return 0;
+    }
+    else if (!Rg_is_Fp(pol, pp)) return 0;
+    if (!*pT) *pT = mod;
+    else if (mod != *pT && !gequal(mod, *pT))
+    {
+      if (DEBUGMEM) pari_warn(warner,"different moduli in Rg_is_FpXQ");
+      return 0;
+    }
+    return 1;
+
+  default: return 0;
+  }
+}
+
+int
+RgX_is_FpXQX(GEN x, GEN *pT, GEN *pp)
+{
+  long i, lx = lg(x);
+  for (i = 2; i < lx; i++)
+    if (!Rg_is_FpXQ(gel(x,i), pT, pp)) return 0;
+  return 1;
+}
+
+/************************************************************************
+ **                                                                    **
+ **                      Ring conversion                               **
+ **                                                                    **
+ ************************************************************************/
+
+/* p > 0 a t_INT, return lift(x * Mod(1,p)).
+ * If x is an INTMOD, assume modulus is a multiple of p. */
+GEN
+Rg_to_Fp(GEN x, GEN p)
+{
+  if (lgefint(p) == 3) return utoi(Rg_to_Fl(x, (ulong)p[2]));
+  switch(typ(x))
+  {
+    case t_INT: return modii(x, p);
+    case t_FRAC: {
+      pari_sp av = avma;
+      GEN z = modii(gel(x,1), p);
+      if (z == gen_0) return gen_0;
+      return gerepileuptoint(av, remii(mulii(z, Fp_inv(gel(x,2), p)), p));
+    }
+    case t_PADIC: return padic_to_Fp(x, p);
+    case t_INTMOD: {
+      GEN q = gel(x,1), a = gel(x,2);
+      if (equalii(q, p)) return icopy(a);
+      if (!dvdii(q,p)) pari_err_MODULUS("Rg_to_Fp", q, p);
+      return remii(a, p);
+    }
+    default: pari_err_TYPE("Rg_to_Fp",x);
+      return NULL; /* not reached */
+  }
+}
+/* If x is a POLMOD, assume modulus is a multiple of T. */
+GEN
+Rg_to_FpXQ(GEN x, GEN T, GEN p)
+{
+  long ta, tx = typ(x), v = varn(T);
+  GEN a, b;
+  if (is_const_t(tx))
+  {
+    if (tx == t_FFELT) return FF_to_FpXQ(x);
+    return scalar_ZX(Rg_to_Fp(x, p), v);
+  }
+  switch(tx)
+  {
+    case t_POLMOD:
+      b = gel(x,1);
+      a = gel(x,2); ta = typ(a);
+      if (is_const_t(ta)) return scalar_ZX(Rg_to_Fp(a, p), v);
+      b = RgX_to_FpX(b, p); if (varn(b) != v) break;
+      a = RgX_to_FpX(a, p); if (ZX_equal(b,T)) return a;
+      return FpX_rem(a, T, p);
+    case t_POL:
+      if (varn(x) != v) break;
+      return FpX_rem(RgX_to_FpX(x,p), T, p);
+    case t_RFRAC:
+      a = Rg_to_FpXQ(gel(x,1), T,p);
+      b = Rg_to_FpXQ(gel(x,2), T,p);
+      return FpXQ_div(a,b, T,p);
+  }
+  pari_err_TYPE("Rg_to_FpXQ",x);
+  return NULL; /* not reached */
+}
+GEN
+RgX_to_FpX(GEN x, GEN p)
+{
+  long i, l;
+  GEN z = cgetg_copy(x, &l); z[1] = x[1];
+  for (i = 2; i < l; i++) gel(z,i) = Rg_to_Fp(gel(x,i), p);
+  return FpX_renormalize(z, l);
+}
+
+GEN
+RgV_to_FpV(GEN x, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++) gel(z,i) = Rg_to_Fp(gel(x,i), p);
+  return z;
+}
+
+GEN
+RgC_to_FpC(GEN x, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_COL);
+  for (i = 1; i < l; i++) gel(z,i) = Rg_to_Fp(gel(x,i), p);
+  return z;
+}
+
+GEN
+RgM_to_FpM(GEN x, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_MAT);
+  for (i = 1; i < l; i++) gel(z,i) = RgC_to_FpC(gel(x,i), p);
+  return z;
+}
+GEN
+RgC_to_Flc(GEN x, ulong p)
+{
+  long l = lg(x), i;
+  GEN a = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) a[i] = Rg_to_Fl(gel(x,i), p);
+  return a;
+}
+GEN
+RgM_to_Flm(GEN x, ulong p)
+{
+  long l, i;
+  GEN a = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(a,i) = RgC_to_Flc(gel(x,i), p);
+  return a;
+}
+
+GEN
+RgX_to_FpXQX(GEN x, GEN T, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_POL); z[1] = x[1];
+  for (i = 2; i < l; i++) gel(z,i) = Rg_to_FpXQ(gel(x,i), T,p);
+  return FpXQX_renormalize(z, l);
+}
+GEN
+RgX_to_FqX(GEN x, GEN T, GEN p)
+{
+  long i, l = lg(x);
+  GEN z = cgetg(l, t_POL); z[1] = x[1];
+  for (i = 2; i < l; i++) gel(z,i) = simplify_shallow(Rg_to_FpXQ(gel(x,i), T,p));
+  return FpXQX_renormalize(z, l);
+}
+
+/* lg(V) > 1 */
+GEN
+FpXV_FpC_mul(GEN V, GEN W, GEN p)
+{
+  pari_sp av = avma;
+  long i, l = lg(V);
+  GEN z = ZX_Z_mul(gel(V,1),gel(W,1));
+  for(i=2; i<l; i++)
+  {
+    z = ZX_add(z, ZX_Z_mul(gel(V,i),gel(W,i)));
+    if ((i & 7) == 0) z = gerepileupto(av, z);
+  }
+  return gerepileupto(av, FpX_red(z,p));
+}
+
+GEN
+FqX_Fq_add(GEN y, GEN x, GEN T, GEN p)
+{
+  long i, lz = lg(y);
+  GEN z;
+  if (!T) return FpX_Fp_add(y, x, p);
+  if (lz == 2) return scalarpol(x, varn(y));
+  z = cgetg(lz,t_POL); z[1] = y[1];
+  gel(z,2) = Fq_add(gel(y,2),x, T, p);
+  if (lz == 3) z = FpXX_renormalize(z,lz);
+  else
+    for(i=3;i<lz;i++) gel(z,i) = gcopy(gel(y,i));
+  return z;
+}
+
+GEN
+FqX_Fq_mul_to_monic(GEN P, GEN U, GEN T, GEN p)
+{
+  long i, lP;
+  GEN res = cgetg_copy(P, &lP); res[1] = P[1];
+  for(i=2; i<lP-1; i++) gel(res,i) = Fq_mul(U,gel(P,i), T,p);
+  gel(res,lP-1) = gen_1; return res;
+}
+
+GEN
+FqX_normalize(GEN z, GEN T, GEN p)
+{
+  GEN lc;
+  if (!T) return FpX_normalize(z,p);
+  if (lg(z) == 2) return z;
+  lc = leading_term(z);
+  if (typ(lc) == t_POL)
+  {
+    if (lg(lc) > 3) /* non-constant */
+      return FqX_Fq_mul_to_monic(z, Fq_inv(lc,T,p), T,p);
+    /* constant */
+    lc = gel(lc,2);
+    z = shallowcopy(z);
+    gel(z, lg(z)-1) = lc;
+  }
+  /* lc a t_INT */
+  if (equali1(lc)) return z;
+  return FqX_Fq_mul_to_monic(z, Fp_inv(lc,p), T,p);
+}
+
+GEN
+FqX_eval(GEN x, GEN y, GEN T, GEN p)
+{
+  pari_sp av;
+  GEN p1, r;
+  long j, i=lg(x)-1;
+  if (i<=2)
+    return (i==2)? Fq_red(gel(x,2), T, p): gen_0;
+  av=avma; p1=gel(x,i);
+  /* specific attention to sparse polynomials (see poleval)*/
+  /*You've guessed it! It's a copy-paste(tm)*/
+  for (i--; i>=2; i=j-1)
+  {
+    for (j=i; !signe(gel(x,j)); j--)
+      if (j==2)
+      {
+        if (i!=j) y = Fq_pow(y,utoipos(i-j+1), T, p);
+        return gerepileupto(av, Fq_mul(p1,y, T, p));
+      }
+    r = (i==j)? y: Fq_pow(y, utoipos(i-j+1), T, p);
+    p1 = Fq_add(Fq_mul(p1,r,T,p), gel(x,j), T, p);
+  }
+  return gerepileupto(av, p1);
+}
+
+GEN
+FqXY_evalx(GEN Q, GEN x, GEN T, GEN p)
+{
+  long i, lb = lg(Q);
+  GEN z;
+  if (!T) return FpXY_evalx(Q, x, p);
+  z = cgetg(lb, t_POL); z[1] = Q[1];
+  for (i=2; i<lb; i++)
+  {
+    GEN q = gel(Q,i);
+    gel(z,i) = typ(q) == t_INT? modii(q,p): FqX_eval(q, x, T, p);
+  }
+  return FpXQX_renormalize(z, lb);
+}
+
+/* Q an FpXY, evaluate at (X,Y) = (x,y) */
+GEN
+FqXY_eval(GEN Q, GEN y, GEN x, GEN T, GEN p)
+{
+  pari_sp av = avma;
+  if (!T) return FpXY_eval(Q, y, x, p);
+  return gerepileupto(av, FqX_eval(FqXY_evalx(Q, x, T, p), y, T, p));
+}
+
+/* a X^d */
+GEN
+monomial(GEN a, long d, long v)
+{
+  long i, lP = d+3;
+  GEN P;
+  if (d < 0) {
+    if (isrationalzero(a)) return pol_0(v);
+    P = cgetg(3, t_RFRAC);
+    gel(P,1) = a;
+    gel(P,2) = monomial(gen_1, -d, v);
+  } else {
+    P = cgetg(lP, t_POL);
+    if (gequal0(a))
+    {
+      if (isexactzero(a)) return scalarpol_shallow(a,v);
+      P[1] = evalsigne(0) | evalvarn(v);
+    }
+    else
+      P[1] = evalsigne(1) | evalvarn(v);
+    lP--; gel(P,lP) = a;
+    for (i=2; i<lP; i++) gel(P,i) = gen_0;
+  }
+  return P;
+}
+GEN
+monomialcopy(GEN a, long d, long v)
+{
+  long i, lP = d+3;
+  GEN P;
+  if (d < 0) {
+    if (isrationalzero(a)) return pol_0(v);
+    P = cgetg(3, t_RFRAC);
+    gel(P,1) = gcopy(a);
+    gel(P,2) = monomial(gen_1, -d, v);
+  } else {
+    P = cgetg(lP, t_POL);
+    if (gequal0(a))
+    {
+      if (isexactzero(a)) return scalarpol(a,v);
+      P[1] = evalsigne(0) | evalvarn(v);
+    }
+    else
+      P[1] = evalsigne(1) | evalvarn(v);
+    lP--; gel(P,lP) = gcopy(a);
+    for (i=2; i<lP; i++) gel(P,i) = gen_0;
+  }
+  return P;
+}
+GEN
+pol_x_powers(long N, long v)
+{
+  GEN L = cgetg(N+1,t_VEC);
+  long i;
+  for (i=1; i<=N; i++) gel(L,i) = monomial(gen_1, i-1, v);
+  return L;
+}
+
+GEN
+FqXQ_powers(GEN x, long l, GEN S, GEN T, GEN p)
+{
+  return T ? FpXQXQ_powers(x, l, S, T, p): FpXQ_powers(x, l, S, p);
+}
+
+GEN
+FqXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, GEN p)
+{
+  return T ? FpXQXQ_matrix_pow(y, n, m, S, T, p): FpXQ_matrix_pow(y, n, m, S, p);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                             Fq                                  */
+/*                                                                 */
+/*******************************************************************/
+
+GEN
+Fq_add(GEN x, GEN y, GEN T/*unused*/, GEN p)
+{
+  (void)T;
+  switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
+  {
+    case 0: return Fp_add(x,y,p);
+    case 1: return FpX_Fp_add(x,y,p);
+    case 2: return FpX_Fp_add(y,x,p);
+    case 3: return FpX_add(x,y,p);
+  }
+  return NULL;
+}
+
+GEN
+Fq_sub(GEN x, GEN y, GEN T/*unused*/, GEN p)
+{
+  (void)T;
+  switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
+  {
+    case 0: return Fp_sub(x,y,p);
+    case 1: return FpX_Fp_sub(x,y,p);
+    case 2: return Fp_FpX_sub(x,y,p);
+    case 3: return FpX_sub(x,y,p);
+  }
+  return NULL;
+}
+
+GEN
+Fq_neg(GEN x, GEN T/*unused*/, GEN p)
+{
+  (void)T;
+  return (typ(x)==t_POL)? FpX_neg(x,p)
+                        : Fp_neg(x,p);
+}
+
+/* If T==NULL do not reduce*/
+GEN
+Fq_mul(GEN x, GEN y, GEN T, GEN p)
+{
+  switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
+  {
+    case 0: return Fp_mul(x,y,p);
+    case 1: return FpX_Fp_mul(x,y,p);
+    case 2: return FpX_Fp_mul(y,x,p);
+    case 3: if (T) return FpXQ_mul(x,y,T,p);
+            else return FpX_mul(x,y,p);
+  }
+  return NULL;
+}
+
+/* If T==NULL do not reduce*/
+GEN
+Fq_mulu(GEN x, ulong y, /*unused*/GEN T, GEN p)
+{
+  (void) T;
+  return typ(x)==t_POL ? FpX_Fp_mul(x,utoi(y),p): Fp_mulu(x, y, p);
+}
+
+/* y t_INT */
+GEN
+Fq_Fp_mul(GEN x, GEN y, GEN T/*unused*/, GEN p)
+{
+  (void)T;
+  return (typ(x) == t_POL)? FpX_Fp_mul(x,y,p)
+                          : Fp_mul(x,y,p);
+}
+/* If T==NULL do not reduce*/
+GEN
+Fq_sqr(GEN x, GEN T, GEN p)
+{
+  if (typ(x) == t_POL)
+  {
+    if (T) return FpXQ_sqr(x,T,p);
+    else return FpX_sqr(x,p);
+  }
+  else
+    return Fp_sqr(x,p);
+  return NULL;
+}
+
+GEN
+Fq_neg_inv(GEN x, GEN T, GEN p)
+{
+  if (typ(x) == t_INT) return Fp_inv(Fp_neg(x,p),p);
+  return FpXQ_inv(FpX_neg(x,p),T,p);
+}
+
+GEN
+Fq_invsafe(GEN x, GEN pol, GEN p)
+{
+  if (typ(x) == t_INT) return Fp_invsafe(x,p);
+  return FpXQ_invsafe(x,pol,p);
+}
+
+GEN
+Fq_inv(GEN x, GEN pol, GEN p)
+{
+  if (typ(x) == t_INT) return Fp_inv(x,p);
+  return FpXQ_inv(x,pol,p);
+}
+
+GEN
+Fq_div(GEN x, GEN y, GEN pol, GEN p)
+{
+  switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
+  {
+    case 0: return Fp_div(x,y,p);
+    case 1: return FpX_Fp_mul(x,Fp_inv(y,p),p);
+    case 2: return FpX_Fp_mul(FpXQ_inv(y,pol,p),x,p);
+    case 3: return FpXQ_div(x,y,pol,p);
+  }
+  return NULL;
+}
+
+GEN
+Fq_pow(GEN x, GEN n, GEN pol, GEN p)
+{
+  if (typ(x) == t_INT) return Fp_pow(x,n,p);
+  return FpXQ_pow(x,n,pol,p);
+}
+
+GEN
+Fq_powu(GEN x, ulong n, GEN pol, GEN p)
+{
+  if (typ(x) == t_INT) return Fp_powu(x,n,p);
+  return FpXQ_powu(x,n,pol,p);
+}
+
+GEN
+Fq_sqrt(GEN x, GEN T, GEN p)
+{
+  if (typ(x) == t_INT)
+  {
+    if (!T || odd(get_FpX_degree(T))) return Fp_sqrt(x,p);
+    x = scalarpol_shallow(x, get_FpX_var(T));
+  }
+  return FpXQ_sqrt(x,T,p);
+}
+GEN
+Fq_sqrtn(GEN x, GEN n, GEN T, GEN p, GEN *zeta)
+{
+  if (typ(x) == t_INT)
+  {
+    long d;
+    if (!T) return Fp_sqrtn(x,n,p,zeta);
+    d = get_FpX_degree(T);
+    if (ugcd(umodiu(n,d),d) == 1)
+    {
+      if (!zeta)
+        return Fp_sqrtn(x,n,p,NULL);
+      else
+      {
+        /* gcd(n,p-1)=gcd(n,p^d-1) <=> same number of solutions if Fp and F_{p^d} */
+        if (equalii(gcdii(subiu(p,1),n), gcdii(subiu(Fp_powu(p,d,n), 1), n)))
+          return Fp_sqrtn(x,n,p,zeta);
+      }
+    }
+    x = scalarpol_shallow(x, get_FpX_var(T));
+  }
+  return FpXQ_sqrtn(x,n,T,p,zeta);
+}
+
+struct _Fq_field
+{
+  GEN T, p;
+};
+
+static GEN
+_Fq_red(void *E, GEN x)
+{ struct _Fq_field *s = (struct _Fq_field *)E;
+  return Fq_red(x, s->T, s->p);
+}
+
+static GEN
+_Fq_add(void *E, GEN x, GEN y)
+{
+  (void) E;
+  switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
+  {
+    case 0: return addii(x,y);
+    case 1: return ZX_Z_add(x,y);
+    case 2: return ZX_Z_add(y,x);
+    default: return ZX_add(x,y);
+  }
+}
+
+static GEN
+_Fq_neg(void *E, GEN x) { (void) E; return typ(x)==t_POL?ZX_neg(x):negi(x); }
+
+static GEN
+_Fq_mul(void *E, GEN x, GEN y)
+{
+  (void) E;
+  switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
+  {
+    case 0: return mulii(x,y);
+    case 1: return ZX_Z_mul(x,y);
+    case 2: return ZX_Z_mul(y,x);
+    default: return ZX_mul(x,y);
+  }
+}
+
+static GEN
+_Fq_inv(void *E, GEN x)
+{ struct _Fq_field *s = (struct _Fq_field *)E;
+  return Fq_inv(x,s->T,s->p);
+}
+
+static int
+_Fq_equal0(GEN x) { return signe(x)==0; }
+
+static GEN
+_Fq_s(void *E, long x) { (void) E; return stoi(x); }
+
+static const struct bb_field Fq_field={_Fq_red,_Fq_add,_Fq_mul,_Fq_neg,
+                                       _Fq_inv,_Fq_equal0,_Fq_s};
+
+const struct bb_field *get_Fq_field(void **E, GEN T, GEN p)
+{
+  GEN z = new_chunk(sizeof(struct _Fq_field));
+  struct _Fq_field *e = (struct _Fq_field *) z;
+  e->T = T; e->p  = p; *E = (void*)e;
+  return &Fq_field;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                             Fq[X]                               */
+/*                                                                 */
+/*******************************************************************/
+/* P(X + c) */
+GEN
+FpX_translate(GEN P, GEN c, GEN p)
+{
+  pari_sp av = avma, lim;
+  GEN Q, *R;
+  long i, k, n;
+
+  if (!signe(P) || !signe(c)) return ZX_copy(P);
+  Q = leafcopy(P);
+  R = (GEN*)(Q+2); n = degpol(P);
+  lim = stack_lim(av, 2);
+  for (i=1; i<=n; i++)
+  {
+    for (k=n-i; k<n; k++)
+      R[k] = Fp_add(R[k], Fp_mul(c, R[k+1], p), p);
+
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"FpX_translate, i = %ld/%ld", i,n);
+      Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+    }
+  }
+  return gerepilecopy(av, FpX_renormalize(Q, lg(Q)));
+}
+/* P(X + c), c an Fq */
+GEN
+FqX_translate(GEN P, GEN c, GEN T, GEN p)
+{
+  pari_sp av = avma, lim;
+  GEN Q, *R;
+  long i, k, n;
+
+  /* signe works for t_(INT|POL) */
+  if (!signe(P) || !signe(c)) return RgX_copy(P);
+  Q = leafcopy(P);
+  R = (GEN*)(Q+2); n = degpol(P);
+  lim = stack_lim(av, 2);
+  for (i=1; i<=n; i++)
+  {
+    for (k=n-i; k<n; k++)
+      R[k] = Fq_add(R[k], Fq_mul(c, R[k+1], T, p), T, p);
+
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"FqX_translate, i = %ld/%ld", i,n);
+      Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
+    }
+  }
+  return gerepilecopy(av, FpXQX_renormalize(Q, lg(Q)));
+}
+
+GEN
+FqV_roots_to_pol(GEN V, GEN T, GEN p, long v)
+{
+  pari_sp ltop = avma;
+  long k;
+  GEN W;
+  if (lgefint(p) == 3)
+  {
+    ulong pp = p[2];
+    GEN Tl = ZX_to_Flx(T, pp);
+    GEN Vl = FqV_to_FlxV(V, T, p);
+    Tl = FlxqV_roots_to_pol(Vl, Tl, pp, v);
+    return gerepileupto(ltop, FlxX_to_ZXX(Tl));
+  }
+  W = cgetg(lg(V),t_VEC);
+  for(k=1; k < lg(V); k++)
+    gel(W,k) = deg1pol_shallow(gen_1,Fq_neg(gel(V,k),T,p),v);
+  return gerepileupto(ltop, FpXQXV_prod(W, T, p));
+}
+
+GEN
+FqV_red(GEN z, GEN T, GEN p)
+{
+  long i, l = lg(z);
+  GEN res = cgetg(l, typ(z));
+  for(i=1;i<l;i++) gel(res,i) = Fq_red(gel(z,i),T,p);
+  return res;
+}
+
+GEN
+FqC_add(GEN x, GEN y, GEN T, GEN p)
+{
+  long i, lx = lg(x);
+  GEN z;
+  if (!T) return FpC_add(x, y, p);
+  z = cgetg(lx, t_COL);
+  for (i = 1; i < lx; i++) gel(z, i) = Fq_add(gel(x, i), gel(y, i), T, p);
+  return z;
+}
+
+GEN
+FqC_sub(GEN x, GEN y, GEN T, GEN p)
+{
+  long i, lx = lg(x);
+  GEN z;
+  if (!T) return FpC_sub(x, y, p);
+  z = cgetg(lx, t_COL);
+  for (i = 1; i < lx; i++) gel(z, i) = Fq_sub(gel(x, i), gel(y, i), T, p);
+  return z;
+}
+
+GEN
+FqC_Fq_mul(GEN x, GEN y, GEN T, GEN p)
+{
+  long i, l = lg(x);
+  GEN z;
+  if (!T) return FpC_Fp_mul(x, y, p);
+  z = cgetg(l, t_COL);
+  for (i=1;i<l;i++) gel(z,i) = Fq_mul(gel(x,i),y,T,p);
+  return z;
+}
+
+GEN
+FqV_to_FlxV(GEN v, GEN T, GEN pp)
+{
+  long j, N = lg(v);
+  long vT = varn(T);
+  ulong p = pp[2];
+  GEN y = cgetg(N, t_VEC);
+  for (j=1; j<N; j++)
+    gel(y,j) = (typ(gel(v,j))==t_INT?  Z_to_Flx(gel(v,j), p, vT)
+                                    : ZX_to_Flx(gel(v,j), p));
+  return y;
+}
+
+GEN
+FqC_to_FlxC(GEN v, GEN T, GEN pp)
+{
+  long j, N = lg(v);
+  long vT = get_FpX_var(T);
+  ulong p = pp[2];
+  GEN y = cgetg(N, t_COL);
+  for (j=1; j<N; j++)
+    gel(y,j) = (typ(gel(v,j))==t_INT?  Z_to_Flx(gel(v,j), p, vT)
+                                    : ZX_to_Flx(gel(v,j), p));
+  return y;
+}
+
+GEN
+FqM_to_FlxM(GEN x, GEN T, GEN pp)
+{
+  long j, n = lg(x);
+  GEN y = cgetg(n,t_MAT);
+  if (n == 1) return y;
+  for (j=1; j<n; j++)
+    gel(y,j) = FqC_to_FlxC(gel(x,j), T, pp);
+  return y;
+}
+
+
+/*******************************************************************/
+/*  Isomorphisms between finite fields                             */
+/*******************************************************************/
+
+/* compute the reciprocical isomorphism of S mod T,p, i.e. V such that
+   V(S)=X  mod T,p*/
+
+GEN
+Flxq_ffisom_inv(GEN S,GEN T, ulong p)
+{
+  pari_sp ltop = avma;
+  long n = degpol(T);
+  GEN M = Flxq_matrix_pow(S,n,n,T,p);
+  GEN V = Flm_Flc_invimage(M, vecsmall_ei(n, 2), p);
+  return gerepileupto(ltop, Flv_to_Flx(V, T[1]));
+}
+
+GEN
+FpXQ_ffisom_inv(GEN S,GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  long n = degpol(T);
+  GEN V, M = FpXQ_matrix_pow(S,n,n,T,p);
+  V = FpM_FpC_invimage(M, col_ei(n, 2), p);
+  return gerepilecopy(ltop, RgV_to_RgX(V, varn(T)));
+}
+
+/* Let M the matrix of the x^p Frobenius automorphism.
+ * Compute x^(p^i) for i=0..r */
+static GEN
+FpM_Frobenius(GEN M, long r, GEN p, long v)
+{
+  GEN W, V = cgetg(r+2,t_VEC);
+  long i;
+  gel(V,1) = pol_x(v); if (!r) return V;
+  gel(V,2) = RgV_to_RgX(gel(M,2),v);
+  W = gel(M,2);
+  for (i = 3; i <= r+1; ++i)
+  {
+    W = FpM_FpC_mul(M,W,p);
+    gel(V,i) = RgV_to_RgX(W,v);
+  }
+  return V;
+}
+
+/* Let M the matrix of the x^p Frobenius automorphism.
+ * Compute x^(p^i) for i=0..r */
+static GEN
+Flm_Frobenius(GEN M, long r, ulong p, long v)
+{
+  GEN W, V = cgetg(r+2,t_VEC);
+  long i;
+  gel(V,1) = polx_Flx(v); if (!r) return V;
+  gel(V,2) = Flv_to_Flx(gel(M,2),v);
+  W = gel(M,2);
+  for (i = 3; i <= r+1; ++i)
+  {
+    W = Flm_Flc_mul(M,W,p);
+    gel(V,i) = Flv_to_Flx(W,v);
+  }
+  return V;
+}
+
+/* Let P a polynomial != 0 and M the matrix of the x^p Frobenius automorphism in
+ * FFp[X]/T. Compute P(M)
+ * V=FpM_Frobenius(M, p, degpol(P), v)
+ * not stack clean
+ */
+
+static GEN
+FpXQV_FpX_Frobenius(GEN V, GEN P, GEN T, GEN p)
+{
+  pari_sp btop;
+  long i;
+  long l = get_FpX_degree(T);
+  long v = get_FpX_var(T);
+  GEN M,W,Mi;
+  GEN *gptr[2];
+  long lV=lg(V);
+  GEN  PV=RgX_to_RgV(P, lgpol(P));
+  M=cgetg(l+1,t_VEC);
+  gel(M,1) = scalar_ZX_shallow(FpX_eval(P,gen_1,p),v);
+  gel(M,2) = FpXV_FpC_mul(V,PV,p);
+  btop=avma;
+  gptr[0]=&Mi;
+  gptr[1]=&W;
+  W = leafcopy(V);
+  for(i=3;i<=l;i++)
+  {
+    long j;
+    pari_sp bbot;
+    GEN W2=cgetg(lV,t_VEC);
+    for(j=1;j<lV;j++)
+      gel(W2,j) = FpXQ_mul(gel(W,j),gel(V,j),T,p);
+    bbot=avma;
+    Mi=FpXV_FpC_mul(W2,PV,p);
+    W=gcopy(W2);
+    gerepilemanysp(btop,bbot,gptr,2);
+    btop=(pari_sp)W;
+    gel(M,i) = Mi;
+  }
+  return RgXV_to_RgM(M,l);
+}
+
+static GEN
+FlxqV_Flx_Frobenius(GEN V, GEN P, GEN T, ulong p)
+{
+  pari_sp btop;
+  long i;
+  long l = get_Flx_degree(T);
+  long v = get_Flx_var(T);
+  GEN M,W,Mi;
+  GEN PV=Flx_to_Flv(P, lgpol(P));
+  GEN *gptr[2];
+  long lV=lg(V);
+  M=cgetg(l+1,t_VEC);
+  gel(M,1) = Fl_to_Flx(Flx_eval(P,1,p),v);
+  gel(M,2) = FlxV_Flc_mul(V,PV,p);
+  btop=avma;
+  gptr[0]=&Mi;
+  gptr[1]=&W;
+  W=gcopy(V);
+  for(i=3;i<=l;i++)
+  {
+    long j;
+    pari_sp bbot;
+    GEN W2=cgetg(lV,t_VEC);
+    for(j=1;j<lV;j++)
+      gel(W2,j) = Flxq_mul(gel(W,j),gel(V,j),T,p);
+    bbot=avma;
+    Mi=FlxV_Flc_mul(W2,PV,p);
+    W=gcopy(W2);
+    gerepilemanysp(btop,bbot,gptr,2);
+    btop=(pari_sp)W;
+    gel(M,i) = Mi;
+  }
+  return FlxV_to_Flm(M,l);
+}
+
+/* Let M the matrix of the Frobenius automorphism of Fp[X]/(T).
+ * Compute M^d
+ * TODO: use left-right binary (tricky!)
+ */
+GEN
+Flm_Frobenius_pow(GEN M, long d, GEN T, ulong p)
+{
+  pari_sp ltop=avma;
+  long i,l=degpol(T);
+  GEN R, W = gel(M,2);
+  for (i = 2; i <= d; ++i) W = Flm_Flc_mul(M,W,p);
+  R=Flxq_matrix_pow(Flv_to_Flx(W,T[2]),l,l,T,p);
+  return gerepileupto(ltop,R);
+}
+
+GEN
+FpM_Frobenius_pow(GEN M, long d, GEN T, GEN p)
+{
+  pari_sp ltop=avma;
+  long i,l=degpol(T);
+  GEN R, W = gel(M,2);
+  for (i = 2; i <= d; ++i) W = FpM_FpC_mul(M,W,p);
+  R=FpXQ_matrix_pow(RgV_to_RgX(W,varn(T)),l,l,T,p);
+  return gerepilecopy(ltop,R);
+}
+
+/* Essentially we want to compute
+ * FqM_ker(MA-pol_x(MAXVARN),U,l)
+ * To avoid use of matrix in Fq we procede as follows:
+ * We compute FpM_ker(U(MA),l) and then we recover
+ * the eigen value by Galois action, see formula.
+ */
+
+static GEN
+Flx_intersect_ker(GEN P, GEN MA, GEN U, ulong p)
+{
+  pari_sp ltop = avma;
+  long i, vp = P[1], vu = U[1], r = degpol(U);
+  GEN A, R;
+  ulong ib0;
+  pari_timer T;
+  GEN M, V;
+  if (DEBUGLEVEL>=4) timer_start(&T);
+  V = Flm_Frobenius(MA, r, p, U[1]);
+  if (DEBUGLEVEL>=4) timer_printf(&T,"pol[Frobenius]");
+  M = FlxqV_Flx_Frobenius(V, U, P, p);
+  if (p==2)
+    A = F2m_to_Flm(F2m_ker(Flm_to_F2m(M)));
+  else
+    A = Flm_ker(M,p);
+  if (DEBUGLEVEL>=4) timer_printf(&T,"matrix polcyclo");
+  if (lg(A)!=r+1) pari_err_IRREDPOL("FpX_ffintersect", Flx_to_ZX(P));
+  A = gerepileupto(ltop,A);
+  /*The formula is
+   * a_{r-1} = -\phi(a_0)/b_0
+   * a_{i-1} = \phi(a_i)+b_ia_{r-1}  i=r-1 to 1
+   * Where a_0=A[1] and b_i=U[i+2] */
+  ib0 = Fl_inv(Fl_neg(U[2], p), p);
+  R = cgetg(r+1,t_MAT);
+  gel(R,1) = gel(A,1);
+  gel(R,r) = Flm_Flc_mul(MA, Flc_Fl_mul(gel(A,1),ib0, p), p);
+  for(i=r-1; i>1; i--)
+    gel(R,i) = Flv_add(Flm_Flc_mul(MA,gel(R,i+1),p),
+                       Flc_Fl_mul(gel(R,r), U[i+2], p), p);
+  return gerepileupto(ltop, Flm_to_FlxX(Flm_transpose(R),vp,vu));
+}
+
+static GEN
+FpX_intersect_ker(GEN P, GEN MA, GEN U, GEN l)
+{
+  pari_sp ltop = avma;
+  long i, vp = varn(P), vu = varn(U), r = degpol(U);
+  GEN V, A, R, ib0;
+  pari_timer T;
+  if (lgefint(l)==3)
+  {
+    ulong p = l[2];
+    GEN res = Flx_intersect_ker(ZX_to_Flx(P,p), ZM_to_Flm(MA,p), ZX_to_Flx(U,p), p);
+    return gerepileupto(ltop, FlxX_to_ZXX(res));
+  }
+  if (DEBUGLEVEL>=4) timer_start(&T);
+  V = FpM_Frobenius(MA,r,l,vu);
+  if (DEBUGLEVEL>=4) timer_printf(&T,"pol[Frobenius]");
+  A = FpM_ker(FpXQV_FpX_Frobenius(V, U, P, l), l);
+  if (DEBUGLEVEL>=4) timer_printf(&T,"matrix polcyclo");
+  if (lg(A)!=r+1) pari_err_IRREDPOL("FpX_ffintersect", P);
+  A = gerepileupto(ltop,A);
+  /*The formula is
+   * a_{r-1} = -\phi(a_0)/b_0
+   * a_{i-1} = \phi(a_i)+b_ia_{r-1}  i=r-1 to 1
+   * Where a_0=A[1] and b_i=U[i+2] */
+  ib0 = Fp_inv(negi(gel(U,2)),l);
+  R = cgetg(r+1,t_MAT);
+  gel(R,1) = gel(A,1);
+  gel(R,r) = FpM_FpC_mul(MA, FpC_Fp_mul(gel(A,1),ib0,l), l);
+  for(i=r-1;i>1;i--)
+    gel(R,i) = FpC_add(FpM_FpC_mul(MA,gel(R,i+1),l),
+        FpC_Fp_mul(gel(R,r), gel(U,i+2), l),l);
+  return gerepilecopy(ltop,RgM_to_RgXX(shallowtrans(R),vp,vu));
+}
+
+/* n must divide both the degree of P and Q.  Compute SP and SQ such
+  that the subfield of FF_l[X]/(P) generated by SP and the subfield of
+  FF_l[X]/(Q) generated by SQ are isomorphic of degree n.  P and Q do
+  not need to be of the same variable.  if MA (resp. MB) is not NULL,
+  must be the matrix of the Frobenius map in FF_l[X]/(P) (resp.
+  FF_l[X]/(Q) ).  */
+/* Note on the implementation choice:
+ * We assume the prime p is very large
+ * so we handle Frobenius as matrices.
+ */
+
+void
+Flx_ffintersect(GEN P, GEN Q, long n, ulong l,GEN *SP, GEN *SQ, GEN MA, GEN MB)
+{
+  pari_sp ltop = avma;
+  long vp = P[1], vq = Q[1], np = degpol(P), nq = degpol(Q), e;
+  ulong pg;
+  GEN A, B, Ap, Bp;
+  if (np<=0) pari_err_IRREDPOL("FpX_ffintersect", P);
+  if (nq<=0) pari_err_IRREDPOL("FpX_ffintersect", Q);
+  if (n<=0 || np%n || nq%n)
+    pari_err_TYPE("FpX_ffintersect [bad degrees]",stoi(n));
+  e = u_lvalrem(n, l, &pg);
+  if(!MA) MA = Flxq_matrix_pow(Flxq_powu(polx_Flx(vp),l,P,l),np,np,P,l);
+  if(!MB) MB = Flxq_matrix_pow(Flxq_powu(polx_Flx(vq),l,Q,l),nq,nq,Q,l);
+  A = Ap = pol0_Flx(vp);
+  B = Bp = pol0_Flx(vq);
+  if (pg > 1)
+  {
+    pari_timer T;
+    GEN ipg = utoipos(pg);
+    if (l%pg == 1)
+    /* No need to use relative extension, so don't. (Well, now we don't
+     * in the other case either, but this special case is more efficient) */
+    {
+      GEN L;
+      ulong z, An, Bn;
+      z = Fl_neg(rootsof1_Fl(pg, l), l);
+      if (DEBUGLEVEL>=4) timer_start(&T);
+      A = Flm_ker(Flm_Fl_add(MA, z, l),l);
+      if (lg(A)!=2) pari_err_IRREDPOL("FpX_ffintersect",P);
+      A = Flv_to_Flx(gel(A,1),vp);
+
+      B = Flm_ker(Flm_Fl_add(MB, z, l),l);
+      if (lg(B)!=2) pari_err_IRREDPOL("FpX_ffintersect",Q);
+      B = Flv_to_Flx(gel(B,1),vq);
+
+      if (DEBUGLEVEL>=4) timer_printf(&T, "FpM_ker");
+      An = Flxq_powu(A,pg,P,l)[2];
+      Bn = Flxq_powu(B,pg,Q,l)[2];
+      if (!Bn) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      z = Fl_div(An,Bn,l);
+      L = Fp_sqrtn(utoi(z),ipg,utoipos(l),NULL);
+      if (!L) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      if (DEBUGLEVEL>=4) timer_printf(&T, "Fp_sqrtn");
+      B = Flx_Fl_mul(B,itou(L),l);
+    }
+    else
+    {
+      GEN L, An, Bn, z, U;
+      U = gmael(Flx_factor(ZX_to_Flx(polcyclo(pg,MAXVARN),l),l),1,1);
+      A = Flx_intersect_ker(P, MA, U, l);
+      B = Flx_intersect_ker(Q, MB, U, l);
+      if (DEBUGLEVEL>=4) timer_start(&T);
+      An = gel(FlxYqq_pow(A,ipg,P,U,l),2);
+      Bn = gel(FlxYqq_pow(B,ipg,Q,U,l),2);
+      if (DEBUGLEVEL>=4) timer_printf(&T,"pows [P,Q]");
+      z = Flxq_div(An,Bn,U,l);
+      L = Flxq_sqrtn(z,ipg,U,l,NULL);
+      if (!L) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      if (DEBUGLEVEL>=4) timer_printf(&T,"FpXQ_sqrtn");
+      B = FlxqX_Flxq_mul(B,L,U,l);
+      A = FlxY_evalx(A,0,l);
+      B = FlxY_evalx(B,0,l);
+    }
+  }
+  if (e)
+  {
+    GEN VP, VQ, Ay, By;
+    ulong lmun = l-1;
+    long j;
+    MA = Flm_Fl_add(MA,lmun,l);
+    MB = Flm_Fl_add(MB,lmun,l);
+    Ay = pol1_Flx(vp);
+    By = pol1_Flx(vq);
+    VP = vecsmall_ei(np, 1);
+    VQ = np == nq? VP: vecsmall_ei(nq, 1); /* save memory */
+    for(j=0;j<e;j++)
+    {
+      if (j)
+      {
+        Ay = Flxq_mul(Ay,Flxq_powu(Ap,lmun,P,l),P,l);
+        VP = Flx_to_Flv(Ay,np);
+      }
+      Ap = Flm_Flc_invimage(MA,VP,l);
+      Ap = Flv_to_Flx(Ap,vp);
+
+      if (j)
+      {
+        By = Flxq_mul(By,Flxq_powu(Bp,lmun,Q,l),Q,l);
+        VQ = Flx_to_Flv(By,nq);
+      }
+      Bp = Flm_Flc_invimage(MB,VQ,l);
+      Bp = Flv_to_Flx(Bp,vq);
+    }
+  }
+  *SP = Flx_add(A,Ap,l);
+  *SQ = Flx_add(B,Bp,l);
+  gerepileall(ltop,2,SP,SQ);
+}
+
+/* Let l be a prime number, P, Q in ZZ[X].  P and Q are both
+ * irreducible modulo l and degree(P) divides degree(Q).  Output a
+ * monomorphism between FF_l[X]/(P) and FF_l[X]/(Q) as a polynomial R such
+ * that Q | P(R) mod l.  If P and Q have the same degree, it is of course an
+ * isomorphism.  */
+GEN
+Flx_ffisom(GEN P,GEN Q,ulong l)
+{
+  pari_sp av = avma;
+  GEN SP, SQ, R;
+  Flx_ffintersect(P,Q,degpol(P),l,&SP,&SQ,NULL,NULL);
+  R = Flxq_ffisom_inv(SP,P,l);
+  return gerepileupto(av, Flx_Flxq_eval(R,SQ,Q,l));
+}
+
+void
+FpX_ffintersect(GEN P, GEN Q, long n, GEN l, GEN *SP, GEN *SQ, GEN MA, GEN MB)
+{
+  pari_sp ltop = avma;
+  long vp, vq, np, nq, e;
+  ulong pg;
+  GEN A, B, Ap, Bp;
+  vp = varn(P); np = degpol(P);
+  vq = varn(Q); nq = degpol(Q);
+  if (np<=0) pari_err_IRREDPOL("FpX_ffintersect", P);
+  if (nq<=0) pari_err_IRREDPOL("FpX_ffintersect", Q);
+  if (n<=0 || np%n || nq%n)
+    pari_err_TYPE("FpX_ffintersect [bad degrees]",stoi(n));
+  e = u_pvalrem(n, l, &pg);
+  if(!MA) MA = FpXQ_matrix_pow(FpXQ_pow(pol_x(vp),l,P,l),np,np,P,l);
+  if(!MB) MB = FpXQ_matrix_pow(FpXQ_pow(pol_x(vq),l,Q,l),nq,nq,Q,l);
+  A = Ap = pol_0(vp);
+  B = Bp = pol_0(vq);
+  if (pg > 1)
+  {
+    GEN ipg = utoipos(pg);
+    pari_timer T;
+    if (umodiu(l,pg) == 1)
+    /* No need to use relative extension, so don't. (Well, now we don't
+     * in the other case either, but this special case is more efficient) */
+    {
+      GEN L, An, Bn, z;
+      z = negi( rootsof1u_Fp(pg, l) );
+      if (DEBUGLEVEL>=4) timer_start(&T);
+      A = FpM_ker(RgM_Rg_add_shallow(MA, z),l);
+      if (lg(A)!=2) pari_err_IRREDPOL("FpX_ffintersect",P);
+      A = RgV_to_RgX(gel(A,1),vp);
+
+      B = FpM_ker(RgM_Rg_add_shallow(MB, z),l);
+      if (lg(B)!=2) pari_err_IRREDPOL("FpX_ffintersect",Q);
+      B = RgV_to_RgX(gel(B,1),vq);
+
+      if (DEBUGLEVEL>=4) timer_printf(&T, "FpM_ker");
+      An = gel(FpXQ_pow(A,ipg,P,l),2);
+      Bn = gel(FpXQ_pow(B,ipg,Q,l),2);
+      if (!signe(Bn)) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      z = Fp_div(An,Bn,l);
+      L = Fp_sqrtn(z,ipg,l,NULL);
+      if (!L) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      if (DEBUGLEVEL>=4) timer_printf(&T, "Fp_sqrtn");
+      B = FpX_Fp_mul(B,L,l);
+    }
+    else
+    {
+      GEN L, An, Bn, z, U;
+      U = gmael(FpX_factor(polcyclo(pg,MAXVARN),l),1,1);
+      A = FpX_intersect_ker(P, MA, U, l);
+      B = FpX_intersect_ker(Q, MB, U, l);
+      if (DEBUGLEVEL>=4) timer_start(&T);
+      An = gel(FpXYQQ_pow(A,ipg,P,U,l),2);
+      Bn = gel(FpXYQQ_pow(B,ipg,Q,U,l),2);
+      if (DEBUGLEVEL>=4) timer_printf(&T,"pows [P,Q]");
+      if (!signe(Bn)) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      z = Fq_div(An,Bn,U,l);
+      L = Fq_sqrtn(z,ipg,U,l,NULL);
+      if (!L) pari_err_IRREDPOL("FpX_ffintersect", mkvec2(P,Q));
+      if (DEBUGLEVEL>=4) timer_printf(&T,"FpXQ_sqrtn");
+      B = FqX_Fq_mul(B,L,U,l);
+      A = FpXY_evalx(A,gen_0,l);
+      B = FpXY_evalx(B,gen_0,l);
+    }
+  }
+  if (e)
+  {
+    GEN VP, VQ, Ay, By, lmun = addis(l,-1);
+    long j;
+    MA = RgM_Rg_add_shallow(MA,gen_m1);
+    MB = RgM_Rg_add_shallow(MB,gen_m1);
+    Ay = pol_1(vp);
+    By = pol_1(vq);
+    VP = col_ei(np, 1);
+    VQ = np == nq? VP: col_ei(nq, 1); /* save memory */
+    for(j=0;j<e;j++)
+    {
+      if (j)
+      {
+        Ay = FpXQ_mul(Ay,FpXQ_pow(Ap,lmun,P,l),P,l);
+        VP = RgX_to_RgV(Ay,np);
+      }
+      Ap = FpM_FpC_invimage(MA,VP,l);
+      Ap = RgV_to_RgX(Ap,vp);
+
+      if (j)
+      {
+        By = FpXQ_mul(By,FpXQ_pow(Bp,lmun,Q,l),Q,l);
+        VQ = RgX_to_RgV(By,nq);
+      }
+      Bp = FpM_FpC_invimage(MB,VQ,l);
+      Bp = RgV_to_RgX(Bp,vq);
+    }
+  }
+  *SP = FpX_add(A,Ap,l);
+  *SQ = FpX_add(B,Bp,l);
+  gerepileall(ltop,2,SP,SQ);
+}
+/* Let l be a prime number, P, Q in ZZ[X].  P and Q are both
+ * irreducible modulo l and degree(P) divides degree(Q).  Output a
+ * monomorphism between FF_l[X]/(P) and FF_l[X]/(Q) as a polynomial R such
+ * that Q | P(R) mod l.  If P and Q have the same degree, it is of course an
+ * isomorphism.  */
+GEN
+FpX_ffisom(GEN P,GEN Q,GEN l)
+{
+  pari_sp av = avma;
+  GEN SP, SQ, R;
+  FpX_ffintersect(P,Q,degpol(P),l,&SP,&SQ,NULL,NULL);
+  R = FpXQ_ffisom_inv(SP,P,l);
+  return gerepileupto(av, FpX_FpXQ_eval(R,SQ,Q,l));
+}
+
+/* Let l be a prime number, P a ZX irreducible modulo l, MP the matrix of the
+ * Frobenius automorphism of F_l[X]/(P).
+ * Factor P over the subfield of F_l[X]/(P) of index d. */
+static GEN
+FpX_factorgalois(GEN P, GEN l, long d, long w, GEN MP)
+{
+  pari_sp ltop = avma;
+  GEN R, V, Tl, z, M;
+  long k, n = degpol(P), m = n/d;
+  long v = varn(P);
+
+  /* x - y */
+  if (m == 1) return deg1pol_shallow(gen_1, deg1pol_shallow(subis(l,1), gen_0, w), v);
+  M = FpM_Frobenius_pow(MP,d,P,l);
+
+  Tl = leafcopy(P); setvarn(Tl,w);
+  V = cgetg(m+1,t_VEC);
+  gel(V,1) = pol_x(w);
+  z = gel(M,2);
+  gel(V,2) = RgV_to_RgX(z,w);
+  for(k=3;k<=m;k++)
+  {
+    z = FpM_FpC_mul(M,z,l);
+    gel(V,k) = RgV_to_RgX(z,w);
+  }
+  R = FqV_roots_to_pol(V,Tl,l,v);
+  return gerepileupto(ltop,R);
+}
+/* same: P is an Flx, MP an Flm */
+static GEN
+Flx_factorgalois(GEN P, ulong l, long d, long w, GEN MP)
+{
+  pari_sp ltop = avma;
+  GEN R, V, Tl, z, M;
+  long k, n = degpol(P), m = n/d;
+  long v = P[1];
+
+  if (m == 1) {
+    R = polx_Flx(v);
+    gel(R,2) = z = polx_Flx(w); z[3] = l - 1; /* - y */
+    gel(R,3) = pol1_Flx(w);
+    return R; /* x - y */
+  }
+  M = Flm_Frobenius_pow(MP,d,P,l);
+
+  Tl = leafcopy(P); setvarn(Tl,w);
+  V = cgetg(m+1,t_VEC);
+  gel(V,1) = polx_Flx(w);
+  z = gel(M,2);
+  gel(V,2) = Flv_to_Flx(z,w);
+  for(k=3;k<=m;k++)
+  {
+    z = Flm_Flc_mul(M,z,l);
+    gel(V,k) = Flv_to_Flx(z,w);
+  }
+  R = FlxqV_roots_to_pol(V,Tl,l,v);
+  return gerepileupto(ltop,R);
+}
+
+GEN
+Flx_factorff_irred(GEN P, GEN Q, ulong p)
+{
+  pari_sp ltop = avma, av;
+  GEN SP, SQ, MP, MQ, M, FP, FQ, E, V, IR, res;
+  long np = degpol(P), nq = degpol(Q), d = cgcd(np,nq);
+  long i, vp = P[1], vq = Q[1];
+  if (d==1) retmkcol(Flx_to_FlxX(P, vq));
+  FQ = Flxq_matrix_pow(Flxq_powu(polx_Flx(vq),p,Q,p),nq,nq,Q,p);
+  av = avma;
+  FP = Flxq_matrix_pow(Flxq_powu(polx_Flx(vp),p,P,p),np,np,P,p);
+  Flx_ffintersect(P,Q,d,p,&SP,&SQ, FP, FQ);
+  E = Flx_factorgalois(P,p,d,vq, FP);
+  E = FlxX_to_Flm(E,np);
+  MP= Flxq_matrix_pow(SP,np,d,P,p);
+  IR= gel(Flm_indexrank(MP,p),1);
+  E = rowpermute(E, IR);
+  M = rowpermute(MP,IR);
+  M = Flm_inv(M,p);
+  MQ= Flxq_matrix_pow(SQ,nq,d,Q,p);
+  M = Flm_mul(MQ,M,p);
+  M = Flm_mul(M,E,p);
+  M = gerepileupto(av,M);
+  V = cgetg(d+1,t_VEC);
+  gel(V,1) = M;
+  for(i=2;i<=d;i++)
+    gel(V,i) = Flm_mul(FQ,gel(V,i-1),p);
+  res = cgetg(d+1,t_COL);
+  for(i=1;i<=d;i++)
+    gel(res,i) = Flm_to_FlxX(gel(V,i),vp,vq);
+  return gerepileupto(ltop,res);
+}
+
+/* P,Q irreducible over F_p. Factor P over FF_p[X] / Q  [factors are ordered as
+ * a Frobenius cycle] */
+GEN
+FpX_factorff_irred(GEN P, GEN Q, GEN p)
+{
+  pari_sp ltop = avma, av;
+  GEN res;
+  long np = degpol(P), nq = degpol(Q), d = cgcd(np,nq);
+  if (d==1) return mkcolcopy(P);
+
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    GEN F = Flx_factorff_irred(ZX_to_Flx(P,pp), ZX_to_Flx(Q,pp), pp);
+    long i, lF = lg(F);
+    res = cgetg(lF, t_COL);
+    for(i=1; i<lF; i++)
+      gel(res,i) = FlxX_to_ZXX(gel(F,i));
+  }
+  else
+  {
+    GEN SP, SQ, MP, MQ, M, FP, FQ, E, V, IR;
+    long i, vp = varn(P), vq = varn(Q);
+    FQ = FpXQ_matrix_pow(FpXQ_pow(pol_x(vq),p,Q,p),nq,nq,Q,p);
+    av = avma;
+    FP = FpXQ_matrix_pow(FpXQ_pow(pol_x(vp),p,P,p),np,np,P,p);
+    FpX_ffintersect(P,Q,d,p,&SP,&SQ,FP,FQ);
+
+    E = FpX_factorgalois(P,p,d,vq,FP);
+    E = RgXX_to_RgM(E,np);
+    MP= FpXQ_matrix_pow(SP,np,d,P,p);
+    IR= gel(FpM_indexrank(MP,p),1);
+    E = rowpermute(E, IR);
+    M = rowpermute(MP,IR);
+    M = FpM_inv(M,p);
+    MQ= FpXQ_matrix_pow(SQ,nq,d,Q,p);
+    M = FpM_mul(MQ,M,p);
+    M = FpM_mul(M,E,p);
+    M = gerepileupto(av,M);
+    V = cgetg(d+1,t_VEC);
+    gel(V,1) = M;
+    for(i=2;i<=d;i++)
+      gel(V,i) = FpM_mul(FQ,gel(V,i-1),p);
+    res = cgetg(d+1,t_COL);
+    for(i=1;i<=d;i++)
+      gel(res,i) = RgM_to_RgXX(gel(V,i),vp,vq);
+  }
+  return gerepilecopy(ltop,res);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                          MODULAR GCD                            */
+/*                                                                 */
+/*******************************************************************/
+/* return z = a mod q, b mod p (p,q) = 1. qinv = 1/q mod p */
+static GEN
+Fl_chinese_coprime(GEN a, ulong b, GEN q, ulong p, ulong qinv, GEN pq)
+{
+  ulong d, amod = umodiu(a, p);
+  pari_sp av = avma;
+  GEN ax;
+
+  if (b == amod) return NULL;
+  d = (b > amod)? b - amod: p - (amod - b); /* (b - a) mod p */
+  (void)new_chunk(lgefint(pq)<<1); /* HACK */
+  ax = mului(Fl_mul(d,qinv,p), q); /* d mod p, 0 mod q */
+  avma = av; return addii(a, ax); /* in ]-q, pq[ assuming a in -]-q,q[ */
+}
+GEN
+Z_init_CRT(ulong Hp, ulong p) { return stoi(Fl_center(Hp, p, p>>1)); }
+GEN
+ZX_init_CRT(GEN Hp, ulong p, long v)
+{
+  long i, l = lg(Hp), lim = (long)(p>>1);
+  GEN H = cgetg(l, t_POL);
+  H[1] = evalsigne(1) | evalvarn(v);
+  for (i=2; i<l; i++)
+    gel(H,i) = stoi(Fl_center(Hp[i], p, lim));
+  return H;
+}
+
+/* assume lg(Hp) > 1 */
+GEN
+ZM_init_CRT(GEN Hp, ulong p)
+{
+  long i,j, m = lgcols(Hp), l = lg(Hp), lim = (long)(p>>1);
+  GEN c,cp,H = cgetg(l, t_MAT);
+  for (j=1; j<l; j++)
+  {
+    cp = gel(Hp,j);
+    c = cgetg(m, t_COL);
+    gel(H,j) = c;
+    for (i=1; i<m; i++) gel(c,i) = stoi(Fl_center(cp[i],p, lim));
+  }
+  return H;
+}
+
+int
+Z_incremental_CRT(GEN *H, ulong Hp, GEN *ptq, ulong p)
+{
+  GEN h, q = *ptq, qp = muliu(q,p), lim = shifti(qp,-1);
+  ulong qinv = Fl_inv(umodiu(q,p), p);
+  int stable = 1;
+  h = Fl_chinese_coprime(*H,Hp,q,p,qinv,qp);
+  if (h)
+  {
+    if (cmpii(h,lim) > 0) h = subii(h,qp);
+    *H = h; stable = 0;
+  }
+  *ptq = qp; return stable;
+}
+
+static int
+ZX_incremental_CRT_raw(GEN *ptH, GEN Hp, GEN q, GEN qp, ulong p)
+{
+  GEN H = *ptH, h, lim = shifti(qp,-1);
+  ulong qinv = Fl_inv(umodiu(q,p), p);
+  long i, l = lg(H), lp = lg(Hp);
+  int stable = 1;
+
+  if (l < lp)
+  { /* degree increases */
+    GEN x = cgetg(lp, t_POL);
+    for (i=1; i<l; i++)  x[i] = H[i];
+    for (   ; i<lp; i++) gel(x,i) = gen_0;
+    *ptH = H = x;
+    stable = 0;
+  } else if (l > lp)
+  { /* degree decreases */
+    GEN x = cgetg(l, t_VECSMALL);
+    for (i=1; i<lp; i++)  x[i] = Hp[i];
+    for (   ; i<l; i++) x[i] = 0;
+    Hp = x; lp = l;
+  }
+  for (i=2; i<lp; i++)
+  {
+    h = Fl_chinese_coprime(gel(H,i),Hp[i],q,p,qinv,qp);
+    if (h)
+    {
+      if (cmpii(h,lim) > 0) h = subii(h,qp);
+      gel(H,i) = h; stable = 0;
+    }
+  }
+  return stable;
+}
+
+int
+ZX_incremental_CRT(GEN *ptH, GEN Hp, GEN *ptq, ulong p)
+{
+  GEN q = *ptq, qp = muliu(q,p);
+  int stable = ZX_incremental_CRT_raw(ptH, Hp, q, qp, p);
+  *ptq = qp; return stable;
+}
+
+int
+ZM_incremental_CRT(GEN *pH, GEN Hp, GEN *ptq, ulong p)
+{
+  GEN h, H = *pH, q = *ptq, qp = muliu(q, p), lim = shifti(qp,-1);
+  ulong qinv = Fl_inv(umodiu(q,p), p);
+  long i,j, l = lg(H), m = lgcols(H);
+  int stable = 1;
+  for (j=1; j<l; j++)
+    for (i=1; i<m; i++)
+    {
+      h = Fl_chinese_coprime(gcoeff(H,i,j), coeff(Hp,i,j),q,p,qinv,qp);
+      if (h)
+      {
+        if (cmpii(h,lim) > 0) h = subii(h,qp);
+        gcoeff(H,i,j) = h; stable = 0;
+      }
+    }
+  *ptq = qp; return stable;
+}
+
+/* record the degrees of Euclidean remainders (make them as large as
+ * possible : smaller values correspond to a degenerate sequence) */
+static void
+Flx_resultant_set_dglist(GEN a, GEN b, GEN dglist, ulong p)
+{
+  long da,db,dc, ind;
+  pari_sp av = avma, lim = stack_lim(av, 2);
+
+  if (lgpol(a)==0 || lgpol(b)==0) return;
+  da = degpol(a);
+  db = degpol(b);
+  if (db > da)
+  { swapspec(a,b, da,db); }
+  else if (!da) return;
+  ind = 0;
+  while (db)
+  {
+    GEN c = Flx_rem(a,b, p);
+    a = b; b = c; dc = degpol(c);
+    if (dc < 0) break;
+
+    ind++;
+    if (dc > dglist[ind]) dglist[ind] = dc;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flx_resultant_all");
+      gerepileall(av, 2, &a,&b);
+    }
+    db = dc; /* = degpol(b) */
+  }
+  if (ind+1 > lg(dglist)) setlg(dglist,ind+1);
+  avma = av; return;
+}
+/* assuming the PRS finishes on a degree 1 polynomial C0 + C1X, with
+ * "generic" degree sequence as given by dglist, set *Ci and return
+ * resultant(a,b). Modular version of Collins's subresultant */
+static ulong
+Flx_resultant_all(GEN a, GEN b, long *C0, long *C1, GEN dglist, ulong p)
+{
+  long da,db,dc, ind;
+  ulong lb, res, g = 1UL, h = 1UL, ca = 1UL, cb = 1UL;
+  int s = 1;
+  pari_sp av = avma, lim = stack_lim(av,2);
+
+  *C0 = 1; *C1 = 0;
+  if (lgpol(a)==0 || lgpol(b)==0) return 0;
+  da = degpol(a);
+  db = degpol(b);
+  if (db > da)
+  {
+    swapspec(a,b, da,db);
+    if (both_odd(da,db)) s = -s;
+  }
+  else if (!da) return 1; /* = a[2] ^ db, since 0 <= db <= da = 0 */
+  ind = 0;
+  while (db)
+  { /* sub-resultant algo., applied to ca * a and cb * b, ca,cb scalars,
+     * da = deg a, db = deg b */
+    GEN c = Flx_rem(a,b, p);
+    long delta = da - db;
+
+    if (both_odd(da,db)) s = -s;
+    lb = Fl_mul(b[db+2], cb, p);
+    a = b; b = c; dc = degpol(c);
+    ind++;
+    if (dc != dglist[ind]) { avma = av; return 0; } /* degenerates */
+    if (g == h)
+    { /* frequent */
+      ulong cc = Fl_mul(ca, Fl_powu(Fl_div(lb,g,p), delta+1, p), p);
+      ca = cb;
+      cb = cc;
+    }
+    else
+    {
+      ulong cc = Fl_mul(ca, Fl_powu(lb, delta+1, p), p);
+      ulong ghdelta = Fl_mul(g, Fl_powu(h, delta, p), p);
+      ca = cb;
+      cb = Fl_div(cc, ghdelta, p);
+    }
+    da = db; /* = degpol(a) */
+    db = dc; /* = degpol(b) */
+
+    g = lb;
+    if (delta == 1)
+      h = g; /* frequent */
+    else
+      h = Fl_mul(h, Fl_powu(Fl_div(g,h,p), delta, p), p);
+
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"Flx_resultant_all");
+      gerepileall(av, 2, &a,&b);
+    }
+  }
+  if (da > 1) return 0; /* Failure */
+  /* last non-constant polynomial has degree 1 */
+  *C0 = Fl_mul(ca, a[2], p);
+  *C1 = Fl_mul(ca, a[3], p);
+  res = Fl_mul(cb, b[2], p);
+  if (s == -1) res = p - res;
+  avma = av; return res;
+}
+
+/* u P(X) + v P(-X) */
+static GEN
+pol_comp(GEN P, GEN u, GEN v)
+{
+  long i, l = lg(P);
+  GEN y = cgetg(l, t_POL);
+  for (i=2; i<l; i++)
+  {
+    GEN t = gel(P,i);
+    gel(y,i) = gequal0(t)? gen_0:
+                         (i&1)? gmul(t, gsub(u,v)) /*  odd degree */
+                              : gmul(t, gadd(u,v));/* even degree */
+  }
+  y[1] = P[1]; return normalizepol_lg(y,l);
+}
+
+GEN
+polint_triv(GEN xa, GEN ya)
+{
+  GEN P = NULL, Q = roots_to_pol(xa,0);
+  long i, n = lg(xa);
+  pari_sp av = avma, lim = stack_lim(av, 2);
+  for (i=1; i<n; i++)
+  {
+    GEN T, dP, r;
+    if (gequal0(gel(ya,i))) continue;
+    T = RgX_div_by_X_x(Q, gel(xa,i), NULL);
+    r = poleval(T, gel(xa,i));
+    if (i < n-1 && absi_equal(gel(xa,i), gel(xa,i+1)))
+    { /* x_i = -x_{i+1} */
+      dP = pol_comp(gdiv(T, r), gel(ya,i), gel(ya,i+1));
+      i++;
+    }
+    else
+      dP = gdiv(gmul(gel(ya,i), T), r);
+    P = P? gadd(P, dP): dP;
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"polint_triv2 (i = %ld)",i);
+      P = gerepileupto(av, P);
+    }
+  }
+  return P? P: pol_0(0);
+}
+
+GEN
+FpV_polint(GEN xa, GEN ya, GEN p, long v)
+{
+  GEN inv,T,dP, P = NULL, Q = FpV_roots_to_pol(xa, p, v);
+  long i, n = lg(xa);
+  pari_sp av, lim;
+  av = avma; lim = stack_lim(av,2);
+  for (i=1; i<n; i++)
+  {
+    if (!signe(gel(ya,i))) continue;
+    T = FpX_div_by_X_x(Q, gel(xa,i), p, NULL);
+    inv = Fp_inv(FpX_eval(T,gel(xa,i), p), p);
+    if (i < n-1 && equalii(addii(gel(xa,i), gel(xa,i+1)), p))
+    {
+      dP = pol_comp(T, Fp_mul(gel(ya,i),  inv,p),
+                       Fp_mul(gel(ya,i+1),inv,p));
+      i++; /* x_i = -x_{i+1} */
+    }
+    else
+      dP = FpX_Fp_mul(T, Fp_mul(gel(ya,i),inv,p), p);
+    P = P? FpX_add(P, dP, p): dP;
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"FpV_polint");
+      P = gerepileupto(av, P);
+    }
+  }
+  return P? P: pol_0(v);
+}
+
+static void
+Flv_polint_all(GEN xa, GEN ya, GEN C0, GEN C1, ulong p,
+               GEN *pHp, GEN *pH0p, GEN *pH1p)
+{
+  GEN T,Q = Flv_roots_to_pol(xa, p, 0);
+  GEN dP  = NULL,  P = NULL;
+  GEN dP0 = NULL, P0= NULL;
+  GEN dP1 = NULL, P1= NULL;
+  long i, n = lg(xa);
+  ulong inv;
+  for (i=1; i<n; i++)
+  {
+    T = Flx_div_by_X_x(Q, xa[i], p, NULL);
+    inv = Fl_inv(Flx_eval(T,xa[i], p), p);
+
+    if (ya[i])
+    {
+      dP = Flx_Fl_mul(T, Fl_mul(ya[i],inv,p), p);
+      P = P ? Flx_add(P , dP , p): dP;
+    }
+    if (C0[i])
+    {
+      dP0= Flx_Fl_mul(T, Fl_mul(C0[i],inv,p), p);
+      P0= P0? Flx_add(P0, dP0, p): dP0;
+    }
+    if (C1[i])
+    {
+      dP1= Flx_Fl_mul(T, Fl_mul(C1[i],inv,p), p);
+      P1= P1? Flx_add(P1, dP1, p): dP1;
+    }
+  }
+  *pHp  = (P ? P : zero_Flx(0));
+  *pH0p = (P0? P0: zero_Flx(0));
+  *pH1p = (P1? P1: zero_Flx(0));
+}
+
+/* Q a vector of polynomials representing B in Fp[X][Y], evaluate at X = x,
+ * Return 0 in case of degree drop. */
+static GEN
+FlxY_evalx_drop(GEN Q, ulong x, ulong p)
+{
+  GEN z;
+  long i, lb = lg(Q);
+  ulong leadz = Flx_eval(leading_term(Q), x, p);
+  long vs=mael(Q,2,1);
+  if (!leadz) return zero_Flx(vs);
+
+  z = cgetg(lb, t_VECSMALL); z[1] = vs;
+  for (i=2; i<lb-1; i++) z[i] = Flx_eval(gel(Q,i), x, p);
+  z[i] = leadz; return z;
+}
+
+GEN
+FpXY_Fq_evaly(GEN Q, GEN y, GEN T, GEN p, long vx)
+{
+  pari_sp av = avma;
+  long i, lb = lg(Q);
+  GEN z;
+  if (!T) return FpXY_evaly(Q, y, p, vx);
+  if (lb == 2) return pol_0(vx);
+  z = gel(Q, lb-1);
+  if (lb == 3 || !signe(y)) return typ(z)==t_INT? scalar_ZX(z, vx): ZX_copy(z);
+
+  if (typ(z) == t_INT) z = scalar_ZX_shallow(z, vx);
+  for (i=lb-2; i>=2; i--)
+  {
+    GEN c = gel(Q,i);
+    z = FqX_Fq_mul(z, y, T, p);
+    z = typ(c) == t_INT? FqX_Fq_add(z,c,T,p): FqX_add(z,c,T,p);
+  }
+  return gerepileupto(av, z);
+}
+
+static GEN
+ZX_norml1(GEN x)
+{
+  long i, l = lg(x);
+  GEN s;
+
+  if (l == 2) return gen_0;
+  s = gel(x, l-1); /* != 0 */
+  for (i = l-2; i > 1; i--) {
+    GEN xi = gel(x,i);
+    if (!signe(x)) continue;
+    s = addii_sign(s,1, xi,1);
+  }
+  return s;
+}
+
+/* Interpolate at roots of 1 and use Hadamard bound for univariate resultant:
+ *   bound = N_2(A)^degpol B N_2(B)^degpol(A),  where
+ *     N_2(A) = sqrt(sum (N_1(Ai))^2)
+ * Return e such that Res(A, B) < 2^e */
+ulong
+ZX_ZXY_ResBound(GEN A, GEN B, GEN dB)
+{
+  pari_sp av = avma;
+  GEN a = gen_0, b = gen_0;
+  long i , lA = lg(A), lB = lg(B);
+  double loga, logb;
+  for (i=2; i<lA; i++) a = addii(a, sqri(gel(A,i)));
+  for (i=2; i<lB; i++)
+  {
+    GEN t = gel(B,i);
+    if (typ(t) == t_POL) t = ZX_norml1(t);
+    b = addii(b, sqri(t));
+  }
+  loga = dbllog2(a);
+  logb = dbllog2(b); if (dB) logb -= 2 * dbllog2(dB);
+  i = (long)((degpol(B) * loga + degpol(A) * logb) / 2);
+  avma = av; return (i <= 0)? 1: 1 + (ulong)i;
+}
+
+/* return Res(a(Y), b(n,Y)) over Fp. la = leading_term(a) [for efficiency] */
+static ulong
+Flx_FlxY_eval_resultant(GEN a, GEN b, ulong n, ulong p, ulong la)
+{
+  GEN ev = FlxY_evalx(b, n, p);
+  long drop = lg(b) - lg(ev);
+  ulong r = Flx_resultant(a, ev, p);
+  if (drop && la != 1) r = Fl_mul(r, Fl_powu(la, drop,p),p);
+  return r;
+}
+static GEN
+FpX_FpXY_eval_resultant(GEN a, GEN b, GEN n, GEN p, GEN la)
+{
+  GEN ev = FpXY_evalx(b, n, p);
+  long drop=lg(b)-lg(ev);
+  GEN r = FpX_resultant(a, ev, p);
+  if (drop && !gequal1(la)) r = Fp_mul(r, Fp_powu(la, drop,p),p);
+  return r;
+}
+
+/* assume dres := deg(Res_X(a,b), Y) <= deg(a,X) * deg(b,Y) < p */
+/* Return a Fly */
+static GEN
+Flx_FlyX_resultant_polint(GEN a, GEN b, ulong p, ulong dres, long sx)
+{
+  ulong i, n, la = Flx_lead(a);
+  GEN  x = cgetg(dres+2, t_VECSMALL);
+  GEN  y = cgetg(dres+2, t_VECSMALL);
+ /* Evaluate at dres+ 1 points: 0 (if dres even) and +/- n, so that P_n(X) =
+  * P_{-n}(-X), where P_i is Lagrange polynomial: P_i(j) = delta_{i,j} */
+  for (i=0,n = 1; i < dres; n++)
+  {
+    x[++i] = n;   y[i] = Flx_FlxY_eval_resultant(a,b, x[i], p,la);
+    x[++i] = p-n; y[i] = Flx_FlxY_eval_resultant(a,b, x[i], p,la);
+  }
+  if (i == dres)
+  {
+    x[++i] = 0;   y[i] = Flx_FlxY_eval_resultant(a,b, x[i], p,la);
+  }
+  return Flv_polint(x,y, p, sx);
+}
+
+static GEN
+FlxX_pseudorem(GEN x, GEN y, ulong p)
+{
+  long vx = varn(x), dx, dy, dz, i, lx, dp;
+  pari_sp av = avma, av2, lim;
+
+  if (!signe(y)) pari_err_INV("FlxX_pseudorem",y);
+  (void)new_chunk(2);
+  dx=degpol(x); x = RgX_recip_shallow(x)+2;
+  dy=degpol(y); y = RgX_recip_shallow(y)+2; dz=dx-dy; dp = dz+1;
+  av2 = avma; lim = stack_lim(av2,1);
+  for (;;)
+  {
+    gel(x,0) = Flx_neg(gel(x,0), p); dp--;
+    for (i=1; i<=dy; i++)
+      gel(x,i) = Flx_add( Flx_mul(gel(y,0), gel(x,i), p),
+                              Flx_mul(gel(x,0), gel(y,i), p), p );
+    for (   ; i<=dx; i++)
+      gel(x,i) = Flx_mul(gel(y,0), gel(x,i), p);
+    do { x++; dx--; } while (dx >= 0 && lg(gel(x,0))==2);
+    if (dx < dy) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"FlxX_pseudorem dx = %ld >= %ld",dx,dy);
+      gerepilecoeffs(av2,x,dx+1);
+    }
+  }
+  if (dx < 0) return zero_Flx(0);
+  lx = dx+3; x -= 2;
+  x[0]=evaltyp(t_POL) | evallg(lx);
+  x[1]=evalsigne(1) | evalvarn(vx);
+  x = RgX_recip_shallow(x);
+  if (dp)
+  { /* multiply by y[0]^dp   [beware dummy vars from FpX_FpXY_resultant] */
+    GEN t = Flx_pow(gel(y,0), dp, p);
+    for (i=2; i<lx; i++)
+      gel(x,i) = Flx_mul(gel(x,i), t, p);
+  }
+  return gerepilecopy(av, x);
+}
+
+/* return a Flx */
+GEN
+FlxX_resultant(GEN u, GEN v, ulong p, long sx)
+{
+  pari_sp av = avma, av2, lim;
+  long degq,dx,dy,du,dv,dr,signh;
+  GEN z,g,h,r,p1;
+
+  dx=degpol(u); dy=degpol(v); signh=1;
+  if (dx < dy)
+  {
+    swap(u,v); lswap(dx,dy);
+    if (both_odd(dx, dy)) signh = -signh;
+  }
+  if (dy < 0) return zero_Flx(sx);
+  if (dy==0) return gerepileupto(av, Flx_pow(gel(v,2),dx,p));
+
+  g = h = pol1_Flx(sx); av2 = avma; lim = stack_lim(av2,1);
+  for(;;)
+  {
+    r = FlxX_pseudorem(u,v,p); dr = lg(r);
+    if (dr == 2) { avma = av; return zero_Flx(sx); }
+    du = degpol(u); dv = degpol(v); degq = du-dv;
+    u = v; p1 = g; g = leading_term(u);
+    switch(degq)
+    {
+      case 0: break;
+      case 1:
+        p1 = Flx_mul(h,p1, p); h = g; break;
+      default:
+        p1 = Flx_mul(Flx_pow(h,degq,p), p1, p);
+        h = Flx_div(Flx_pow(g,degq,p), Flx_pow(h,degq-1,p), p);
+    }
+    if (both_odd(du,dv)) signh = -signh;
+    v = FlxY_Flx_div(r, p1, p);
+    if (dr==3) break;
+    if (low_stack(lim,stack_lim(av2,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"resultant_all, dr = %ld",dr);
+      gerepileall(av2,4, &u, &v, &g, &h);
+    }
+  }
+  z = gel(v,2);
+  if (dv > 1) z = Flx_div(Flx_pow(z,dv,p), Flx_pow(h,dv-1,p), p);
+  if (signh < 0) z = Flx_neg(z,p);
+  return gerepileupto(av, z);
+}
+
+/* Warning:
+ * This function switches between valid and invalid variable ordering*/
+
+static GEN
+FlxY_to_FlyX(GEN b, long sv)
+{
+  long i, n=-1;
+  long sw = b[1]&VARNBITS;
+  for(i=2;i<lg(b);i++) n = maxss(n,lgpol(gel(b,i)));
+  return Flm_to_FlxX(Flm_transpose(FlxX_to_Flm(b,n)),sv,sw);
+}
+
+/* Return a Fly*/
+GEN
+Flx_FlxY_resultant(GEN a, GEN b, ulong pp)
+{
+  pari_sp ltop=avma;
+  long dres = degpol(a)*degpol(b);
+  long sx=a[1], sy=b[1]&VARNBITS;
+  GEN z;
+  b = FlxY_to_FlyX(b,sx);
+  if ((ulong)dres >= pp)
+    z = FlxX_resultant(Fly_to_FlxY(a, sy), b, pp, sx);
+  else
+    z = Flx_FlyX_resultant_polint(a, b, pp, (ulong)dres, sy);
+  return gerepileupto(ltop,z);
+}
+
+/* return a t_POL (in variable v >= 0) whose coeffs are the coeffs of b,
+ * in variable v. This is an incorrect PARI object if initially varn(b) << v.
+ * We could return a vector of coeffs, but it is convenient to have degpol()
+ * and friends available. Even in that case, it will behave nicely with all
+ * functions treating a polynomial as a vector of coeffs (eg poleval).
+ * FOR INTERNAL USE! */
+GEN
+swap_vars(GEN b0, long v)
+{
+  long i, n = RgX_degree(b0, v);
+  GEN b, x;
+  if (n < 0) return pol_0(v);
+  b = cgetg(n+3, t_POL); x = b + 2;
+  b[1] = evalsigne(1) | evalvarn(v);
+  for (i=0; i<=n; i++) gel(x,i) = polcoeff_i(b0, i, v);
+  return b;
+}
+
+/* assume varn(b) << varn(a) */
+/* return a FpY*/
+GEN
+FpX_FpXY_resultant(GEN a, GEN b, GEN p)
+{
+  long i,n,dres, vX = varn(b), vY = varn(a);
+  GEN la,x,y;
+
+  if (lgefint(p) == 3)
+  {
+    ulong pp = (ulong)p[2];
+    b = ZXX_to_FlxX(b, pp, vY);
+    a = ZX_to_Flx(a, pp);
+    x = Flx_FlxY_resultant(a, b, pp);
+    return Flx_to_ZX(x);
+  }
+  dres = degpol(a)*degpol(b);
+  b = swap_vars(b, vY);
+  la = leading_term(a);
+  x = cgetg(dres+2, t_VEC);
+  y = cgetg(dres+2, t_VEC);
+ /* Evaluate at dres+ 1 points: 0 (if dres even) and +/- n, so that P_n(X) =
+  * P_{-n}(-X), where P_i is Lagrange polynomial: P_i(j) = delta_{i,j} */
+  for (i=0,n = 1; i < dres; n++)
+  {
+    gel(x,++i) = utoipos(n);
+    gel(y,i) = FpX_FpXY_eval_resultant(a,b,gel(x,i),p,la);
+    gel(x,++i) = subis(p,n);
+    gel(y,i) = FpX_FpXY_eval_resultant(a,b,gel(x,i),p,la);
+  }
+  if (i == dres)
+  {
+    gel(x,++i) = gen_0;
+    gel(y,i) = FpX_FpXY_eval_resultant(a,b, gel(x,i), p,la);
+  }
+  return FpV_polint(x,y, p, vX);
+}
+
+GEN
+FpX_direct_compositum(GEN A, GEN B, GEN p)
+{
+  GEN a, b, x;
+  a = leafcopy(A); setvarn(a, MAXVARN);
+  b = leafcopy(B); setvarn(b, MAXVARN);
+  x = deg1pol_shallow(gen_1, pol_x(MAXVARN), 0); /* x + y */
+  return FpX_FpXY_resultant(a, poleval(b,x),p);
+}
+
+/* 0, 1, -1, 2, -2, ... */
+#define next_lambda(a) (a>0 ? -a : 1-a)
+GEN
+FpX_compositum(GEN A, GEN B, GEN p)
+{
+  GEN a, b;
+  long k;
+  a = leafcopy(A); setvarn(a, MAXVARN);
+  b = leafcopy(B); setvarn(b, MAXVARN);
+  for (k = 1;; k = next_lambda(k))
+  {
+    GEN x = deg1pol_shallow(gen_1, gmulsg(k, pol_x(MAXVARN)), 0); /* x + k y */
+    GEN C = FpX_FpXY_resultant(a, poleval(b,x),p);
+    if (FpX_is_squarefree(C, p)) return C;
+  }
+}
+
+#if 0
+/* Return x such that theta(x) - theta(27448) >= ln(2)*bound */
+/* NB: theta(27449) ~ 27225.387, theta(x) > 0.98 x for x>7481
+ * (Schoenfeld, 1976 for x > 1155901 + direct calculations) */
+static ulong
+get_theta_x(ulong bound)
+{ return (ulong)ceil((bound * LOG2 + 27225.388) / 0.98); }
+#endif
+/* 27449 = prime(3000) */
+void
+init_modular(forprime_t *S) { u_forprime_init(S, 27449, ULONG_MAX); }
+
+/* Assume A in Z[Y], B in Q[Y][X], and Res_Y(A, B) in Z[X].
+ * If lambda = NULL, return Res_Y(A,B).
+ * Otherwise, find a small lambda (start from *lambda, use the sequence above)
+ * such that R(X) = Res_Y(A(Y), B(X + lambda Y)) is squarefree, reset *lambda
+ * to the chosen value and return R
+ *
+ * If LERS is non-NULL, set it to the Last non-constant polynomial in the
+ * Euclidean Remainder Sequence */
+GEN
+ZX_ZXY_resultant_all(GEN A, GEN B0, long *plambda, GEN *LERS)
+{
+  int checksqfree = plambda? 1: 0, delvar = 0, stable;
+  long lambda = plambda? *plambda: 0, cnt = 0;
+  ulong bound, p, dp;
+  pari_sp av = avma, av2 = 0, lim;
+  long i,n, lb, degA = degpol(A), dres = degA*degpol(B0);
+  long vX = varn(B0), vY = varn(A); /* assume vX << vY */
+  long sX = evalvarn(vX);
+  GEN x, y, dglist, dB, B, q, a, b, ev, H, H0, H1, Hp, H0p, H1p, C0, C1, L;
+  forprime_t S;
+
+  dglist = Hp = H0p = H1p = C0 = C1 = NULL; /* gcc -Wall */
+  if (LERS)
+  {
+    if (!checksqfree)
+      pari_err_BUG("ZX_ZXY_resultant_all [LERS != NULL needs lambda]");
+    C0 = cgetg(dres+2, t_VECSMALL);
+    C1 = cgetg(dres+2, t_VECSMALL);
+    dglist = cgetg(dres+1, t_VECSMALL);
+  }
+  x = cgetg(dres+2, t_VECSMALL);
+  y = cgetg(dres+2, t_VECSMALL);
+  if (vY == MAXVARN)
+  {
+    vY = fetch_var(); delvar = 1;
+    B0 = gsubst(B0, MAXVARN, pol_x(vY));
+    A = leafcopy(A); setvarn(A, vY);
+  }
+  L = pol_x(MAXVARN);
+  B0 = Q_remove_denom(B0, &dB);
+  lim = stack_lim(av,2);
+
+  /* make sure p large enough */
+  u_forprime_init(&S, maxuu(dres << 1, 27499), ULONG_MAX);
+INIT:
+  /* allways except the first time */
+  if (av2) { avma = av2; lambda = next_lambda(lambda); }
+  if (checksqfree)
+  {
+    /* # + lambda */
+    L = deg1pol_shallow(stoi(lambda), pol_x(MAXVARN), vY);
+    if (DEBUGLEVEL>4) err_printf("Trying lambda = %ld\n", lambda);
+  }
+  B = poleval(B0, L); av2 = avma;
+
+  if (degA <= 3)
+  { /* sub-resultant faster for small degrees */
+    if (LERS)
+    { /* implies checksqfree */
+      H = resultant_all(A,B,&q);
+      if (typ(q) != t_POL || degpol(q)!=1) goto INIT;
+      H0 = gel(q,2);
+      if (typ(H0) == t_POL) setvarn(H0,vX); else H0 = scalarpol(H0,vX);
+      H1 = gel(q,3);
+      if (typ(H1) == t_POL) setvarn(H1,vX); else H1 = scalarpol(H1,vX);
+    }
+    else
+      H = resultant(A,B);
+    if (checksqfree && !ZX_is_squarefree(H)) goto INIT;
+    goto END;
+  }
+
+  H = H0 = H1 = NULL;
+  lb = lg(B);
+  bound = ZX_ZXY_ResBound(A, B, dB);
+  if (DEBUGLEVEL>4) err_printf("bound for resultant coeffs: 2^%ld\n",bound);
+  dp = 1;
+  while ((p = u_forprime_next(&S)))
+  {
+    if (dB) { dp = smodis(dB, p); if (!dp) continue; }
+
+    a = ZX_to_Flx(A, p);
+    b = ZXX_to_FlxX(B, p, varn(A));
+    if (LERS)
+    {
+      if (degpol(a) < degA || lg(b) < lb) continue; /* p | lc(A)lc(B) */
+      if (checksqfree)
+      { /* find degree list for generic Euclidean Remainder Sequence */
+        long goal = minss(degpol(a), degpol(b)); /* longest possible */
+        for (n=1; n <= goal; n++) dglist[n] = 0;
+        setlg(dglist, 1);
+        for (n=0; n <= dres; n++)
+        {
+          ev = FlxY_evalx_drop(b, n, p);
+          Flx_resultant_set_dglist(a, ev, dglist, p);
+          if (lg(dglist)-1 == goal) break;
+        }
+        /* last pol in ERS has degree > 1 ? */
+        goal = lg(dglist)-1;
+        if (degpol(B) == 1) { if (!goal) goto INIT; }
+        else
+        {
+          if (goal <= 1) goto INIT;
+          if (dglist[goal] != 0 || dglist[goal-1] != 1) goto INIT;
+        }
+        if (DEBUGLEVEL>4)
+          err_printf("Degree list for ERS (trials: %ld) = %Ps\n",n+1,dglist);
+      }
+
+      for (i=0,n = 0; i <= dres; n++)
+      {
+        ev = FlxY_evalx_drop(b, n, p);
+        x[++i] = n; y[i] = Flx_resultant_all(a, ev, C0+i, C1+i, dglist, p);
+        if (!C1[i]) i--; /* C1(i) = 0. No way to recover C0(i) */
+      }
+      Flv_polint_all(x,y,C0,C1, p, &Hp, &H0p, &H1p);
+    }
+    else
+    {
+      long dropa = degA - degpol(a), dropb = lb - lg(b);
+      Hp = Flx_FlyX_resultant_polint(a, b, p, (ulong)dres, sX);
+      if (dropa && dropb)
+        Hp = zero_Flx(sX);
+      else {
+        if (dropa)
+        { /* multiply by ((-1)^deg B lc(B))^(deg A - deg a) */
+          GEN c = gel(b,lb-1); /* lc(B) */
+          if (!odd(lb)) c = Flx_neg(c, p); /* deg B = lb - 3 */
+          if (!Flx_equal1(c)) {
+            c = Flx_pow(c, dropa, p);
+            if (!Flx_equal1(c)) Hp = Flx_mul(Hp, c, p);
+          }
+        }
+        else if (dropb)
+        { /* multiply by lc(A)^(deg B - deg b) */
+          ulong c = a[degA+2]; /* lc(A) */
+          c = Fl_powu(c, dropb, p);
+          if (c != 1) Hp = Flx_Fl_mul(Hp, c, p);
+        }
+      }
+    }
+    if (!H && degpol(Hp) != dres) continue;
+    if (dp != 1) Hp = Flx_Fl_mul(Hp, Fl_powu(Fl_inv(dp,p), degA, p), p);
+    if (checksqfree) {
+      if (!Flx_is_squarefree(Hp, p)) goto INIT;
+      if (DEBUGLEVEL>4) err_printf("Final lambda = %ld\n", lambda);
+      checksqfree = 0;
+    }
+
+    if (!H)
+    { /* initialize */
+      q = utoipos(p); stable = 0;
+      H = ZX_init_CRT(Hp, p,vX);
+      if (LERS) {
+        H0= ZX_init_CRT(H0p, p,vX);
+        H1= ZX_init_CRT(H1p, p,vX);
+      }
+    }
+    else
+    {
+      if (LERS) {
+        GEN qp = muliu(q,p);
+        stable  = ZX_incremental_CRT_raw(&H, Hp, q,qp, p)
+                & ZX_incremental_CRT_raw(&H0,H0p, q,qp, p)
+                & ZX_incremental_CRT_raw(&H1,H1p, q,qp, p);
+        q = qp;
+      }
+      else
+        stable = ZX_incremental_CRT(&H, Hp, &q, p);
+    }
+    /* could make it probabilistic for H ? [e.g if stable twice, etc]
+     * Probabilistic anyway for H0, H1 */
+    if (DEBUGLEVEL>5 && (stable ||  ++cnt==100))
+    { cnt=0; err_printf("%ld%%%s ",100*expi(q)/bound,stable?"s":""); }
+    if (stable && (ulong)expi(q) >= bound) break; /* DONE */
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ZX_ZXY_rnfequation");
+      gerepileall(av2, LERS? 4: 2, &H, &q, &H0, &H1);
+    }
+  }
+  if (!p) pari_err_OVERFLOW("ZX_ZXY_rnfequation [ran out of primes]");
+END:
+  if (DEBUGLEVEL>5) err_printf(" done\n");
+  setvarn(H, vX); if (delvar) (void)delete_var();
+  if (plambda) *plambda = lambda;
+  if (LERS)
+  {
+    *LERS = mkvec2(H0,H1);
+    gerepileall(av, 2, &H, LERS);
+    return H;
+  }
+  return gerepilecopy(av, H);
+}
+
+GEN
+ZX_ZXY_rnfequation(GEN A, GEN B, long *lambda)
+{
+  return ZX_ZXY_resultant_all(A, B, lambda, NULL);
+}
+
+/* If lambda = NULL, return caract(Mod(A, T)), T,A in Z[X].
+ * Otherwise find a small lambda such that caract (Mod(A + lambda X, T)) is
+ * squarefree */
+GEN
+ZXQ_charpoly_sqf(GEN A, GEN T, long *lambda, long v)
+{
+  pari_sp av = avma;
+  GEN R, a;
+  long dA;
+  int delvar;
+
+  if (v < 0) v = 0;
+  switch (typ(A))
+  {
+    case t_POL: dA = degpol(A); if (dA > 0) break;
+      A = dA? gel(A,2): gen_0; /* fall through */
+    default:
+      if (lambda) { A = scalar_ZX_shallow(A,varn(T)); dA = 0; break;}
+      return gerepileupto(av, gpowgs(gsub(pol_x(v), A), degpol(T)));
+  }
+  delvar = 0;
+  if (varn(T) == 0)
+  {
+    long v0 = fetch_var(); delvar = 1;
+    T = leafcopy(T); setvarn(T,v0);
+    A = leafcopy(A); setvarn(A,v0);
+  }
+  R = ZX_ZXY_rnfequation(T, deg1pol_shallow(gen_1, gneg_i(A), 0), lambda);
+  if (delvar) (void)delete_var();
+  setvarn(R, v); a = leading_term(T);
+  if (!gequal1(a)) R = gdiv(R, powiu(a, dA));
+  return gerepileupto(av, R);
+}
+
+
+/* charpoly(Mod(A,T)), A may be in Q[X], but assume T and result are integral */
+GEN
+ZXQ_charpoly(GEN A, GEN T, long v)
+{
+  return (degpol(T) < 16) ? RgXQ_charpoly(A,T,v): ZXQ_charpoly_sqf(A,T, NULL, v);
+}
+
+static GEN
+trivial_case(GEN A, GEN B)
+{
+  long d;
+  if (typ(A) == t_INT) return powiu(A, degpol(B));
+  d = degpol(A);
+  if (d == 0) return trivial_case(gel(A,2),B);
+  if (d < 0) return gen_0;
+  return NULL;
+}
+
+/* floating point resultant */
+static GEN
+fp_resultant(GEN a, GEN b)
+{
+  long da, db, dc;
+  GEN res = gen_1;
+  pari_sp av, lim;
+
+  if (lgpol(a)==0 || lgpol(b)==0) return gen_0;
+  da = degpol(a);
+  db = degpol(b);
+  if (db > da)
+  {
+    swapspec(a,b, da,db);
+    if (both_odd(da,db)) res = gneg(res);
+  }
+  else if (!da) return gen_1; /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
+  av = avma; lim = stack_lim(av, 1);
+  while (db)
+  {
+    GEN lb = gel(b,db+2), c = RgX_rem(a,b);
+    c = normalizepol_approx(c, lg(c)); /* kill leading zeroes without warning */
+    a = b; b = c; dc = degpol(c);
+    if (dc < 0) { avma = av; return gen_0; }
+
+    if (both_odd(da,db)) res = gneg(res);
+    res = gmul(res, gpowgs(lb, da - dc));
+    if (low_stack(lim, stack_lim(av,1))) {
+      if (DEBUGMEM>1) pari_warn(warnmem,"fp_resultant");
+      gerepileall(av, 3, &a,&b,&res);
+    }
+    da = db; /* = degpol(a) */
+    db = dc; /* = degpol(b) */
+  }
+  return gerepileupto(av, gmul(res, gpowgs(gel(b,2), da)));
+}
+
+/* Res(A, B/dB), assuming the A,B in Z[X] and result is integer */
+GEN
+ZX_resultant_all(GEN A, GEN B, GEN dB, ulong bound)
+{
+  ulong Hp, dp, p;
+  pari_sp av = avma, av2, lim;
+  long degA, degB, cnt=0;
+  int stable;
+  GEN q, a, b, H;
+  forprime_t S;
+
+  if ((H = trivial_case(A,B)) || (H = trivial_case(B,A))) return H;
+  q = H = NULL;
+  degA = degpol(A);
+  degB = degpol(B);
+  if (!bound)
+  {
+    bound = ZX_ZXY_ResBound(A, B, dB);
+    if (bound > 10000)
+    {
+      const long CNTMAX = 5; /* to avoid oo loops if R = 0 */
+      long bnd = 0, cnt;
+      long prec = nbits2prec( maxss(gexpo(A), gexpo(B)) + 1 );
+      long bndden = dB? (long)(dbllog2(dB)*degA): 0;
+      for(cnt = 1; cnt < CNTMAX; cnt++, prec = precdbl(prec))
+      {
+        GEN R = fp_resultant(RgX_gtofp(A, prec), RgX_gtofp(B, prec));
+        bnd = gexpo(R) - bndden + 1;
+        if (bnd >= 0 && bnd <= (long)bound && !gequal0(R))
+        {
+          bound = bnd; break;
+        }
+      }
+    }
+  }
+  if (DEBUGLEVEL>4) err_printf("bound for resultant: 2^%ld\n",bound);
+  init_modular(&S);
+  av2 = avma; lim = stack_lim(av,2);
+
+  dp = 1; /* denominator mod p */
+  while ((p = u_forprime_next(&S)))
+  {
+    long dropa, dropb;
+    if (dB) { dp = smodis(dB, p); if (!dp) continue; }
+
+    a = ZX_to_Flx(A, p); dropa = degA - degpol(a);
+    b = ZX_to_Flx(B, p); dropb = degB - degpol(b);
+    if (dropa && dropb) /* p | lc(A), p | lc(B) */
+      Hp = 0;
+    else
+    {
+      Hp = Flx_resultant(a, b, p);
+      if (dropa)
+      { /* multiply by ((-1)^deg B lc(B))^(deg A - deg a) */
+        ulong c = b[degB+2]; /* lc(B) */
+        if (odd(degB)) c = p - c;
+        c = Fl_powu(c, dropa, p);
+        if (c != 1) Hp = Fl_mul(Hp, c, p);
+      }
+      else if (dropb)
+      { /* multiply by lc(A)^(deg B - deg b) */
+        ulong c = a[degA+2]; /* lc(A) */
+        c = Fl_powu(c, dropb, p);
+        if (c != 1) Hp = Fl_mul(Hp, c, p);
+      }
+      if (dp != 1) Hp = Fl_mul(Hp, Fl_powu(Fl_inv(dp,p), degA, p), p);
+    }
+
+    if (!H)
+    {
+      stable = 0; q = utoipos(p);
+      H = Z_init_CRT(Hp, p);
+    }
+    else /* could make it probabilistic ??? [e.g if stable twice, etc] */
+      stable = Z_incremental_CRT(&H, Hp, &q, p);
+    if (DEBUGLEVEL>5 && (stable ||  cnt++==2000))
+    { cnt=0; err_printf("%ld%%%s ",100*expi(q)/bound,stable?"s":""); }
+    if (stable && (ulong)expi(q) >= bound) break; /* DONE */
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"ZX_resultant");
+      gerepileall(av2, 2, &H,&q);
+    }
+  }
+  if (DEBUGLEVEL>5) err_printf("done\n");
+  return gerepileuptoint(av, icopy(H));
+}
+
+/* A0 and B0 in Q[X] */
+GEN
+QX_resultant(GEN A0, GEN B0)
+{
+  GEN s, a, b, A, B;
+  pari_sp av = avma;
+
+  A = Q_primitive_part(A0, &a);
+  B = Q_primitive_part(B0, &b);
+  s = ZX_resultant(A, B);
+  if (!signe(s)) { avma = av; return gen_0; }
+  if (a) s = gmul(s, gpowgs(a,degpol(B)));
+  if (b) s = gmul(s, gpowgs(b,degpol(A)));
+  return gerepileupto(av, s);
+}
+
+GEN
+ZX_resultant(GEN A, GEN B) { return ZX_resultant_all(A,B,NULL,0); }
+
+GEN
+QXQ_intnorm(GEN A, GEN B)
+{
+  GEN c, n, R, lB;
+  long dA = degpol(A), dB = degpol(B);
+  pari_sp av = avma;
+  if (dA < 0) return gen_0;
+  A = Q_primitive_part(A, &c);
+  if (!c || typ(c) == t_INT) {
+    n = c;
+    R = ZX_resultant(B, A);
+  } else {
+    n = gel(c,1);
+    R = ZX_resultant_all(B, A, gel(c,2), 0);
+  }
+  if (n && !equali1(n)) R = mulii(R, powiu(n, dB));
+  lB = leading_term(B);
+  if (!equali1(lB)) R = diviiexact(R, powiu(lB, dA));
+  return gerepileuptoint(av, R);
+}
+
+GEN
+QXQ_norm(GEN A, GEN B)
+{
+  GEN c, R, lB;
+  long dA = degpol(A), dB = degpol(B);
+  pari_sp av = avma;
+  if (dA < 0) return gen_0;
+  A = Q_primitive_part(A, &c);
+  R = ZX_resultant(B, A);
+  if (c) R = gmul(R, gpowgs(c, dB));
+  lB = leading_term(B);
+  if (!equali1(lB)) R = gdiv(R, gpowgs(lB, dA));
+  return gerepileupto(av, R);
+}
+
+/* assume x has integral coefficients */
+GEN
+ZX_disc_all(GEN x, ulong bound)
+{
+  pari_sp av = avma;
+  GEN l, R;
+  long s, d = degpol(x);
+  if (d <= 1) return d ? gen_1: gen_0;
+  s = (d & 2) ? -1: 1;
+  l = leading_term(x);
+  R = ZX_resultant_all(x, ZX_deriv(x), NULL, bound);
+  if (is_pm1(l))
+  { if (signe(l) < 0) s = -s; }
+  else
+    R = diviiexact(R,l);
+  if (s == -1) togglesign_safe(&R);
+  return gerepileuptoint(av,R);
+}
+GEN ZX_disc(GEN x) { return ZX_disc_all(x,0); }
+
+GEN
+QX_disc(GEN x)
+{
+  pari_sp av = avma;
+  GEN c, d = ZX_disc( Q_primitive_part(x, &c) );
+  if (c) d = gmul(d, gpowgs(c, 2*degpol(x) - 2));
+  return gerepileupto(av, d);
+}
+
+/* lift(1 / Mod(A,B)). B a ZX, A a scalar or a QX */
+GEN
+QXQ_inv(GEN A, GEN B)
+{
+  GEN D, cU, q, U, V;
+  ulong p;
+  pari_sp av2, av = avma, avlim = stack_lim(av, 1);
+  forprime_t S;
+
+  if (is_scalar_t(typ(A))) return scalarpol(ginv(A), varn(B));
+  /* A a QX, B a ZX */
+  if (degpol(A) < 15) return RgXQ_inv(A,B);
+  A = Q_primitive_part(A, &D);
+  /* A, B in Z[X] */
+  init_modular(&S);
+  av2 = avma; U = NULL;
+  while ((p = u_forprime_next(&S)))
+  {
+    GEN a, b, qp, Up, Vp;
+    int stable;
+
+    a = ZX_to_Flx(A, p);
+    b = ZX_to_Flx(B, p);
+    /* if p | Res(A/G, B/G), discard */
+    if (!Flx_extresultant(b,a,p, &Vp,&Up)) continue;
+
+    if (!U)
+    { /* First time */
+      U = ZX_init_CRT(Up,p,varn(A));
+      V = ZX_init_CRT(Vp,p,varn(A));
+      q = utoipos(p); continue;
+    }
+    if (DEBUGLEVEL>5) err_printf("QXQ_inv: mod %ld (bound 2^%ld)", p,expi(q));
+    qp = muliu(q,p);
+    stable = ZX_incremental_CRT_raw(&U, Up, q,qp, p)
+           & ZX_incremental_CRT_raw(&V, Vp, q,qp, p);
+    if (stable)
+    { /* all stable: check divisibility */
+      GEN res = ZX_add(ZX_mul(A,U), ZX_mul(B,V));
+      if (degpol(res) == 0) {
+        res = gel(res,2);
+        D = D? gmul(D, res): res;
+        break;
+      } /* DONE */
+      if (DEBUGLEVEL) err_printf("QXQ_inv: char 0 check failed");
+    }
+    q = qp;
+    if (low_stack(avlim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"QXQ_inv");
+      gerepileall(av2, 3, &q,&U,&V);
+    }
+  }
+  if (!p) pari_err_OVERFLOW("QXQ_inv [ran out of primes]");
+  cU = ZX_content(U);
+  if (!is_pm1(cU)) { U = Q_div_to_int(U, cU); D = gdiv(D, cU); }
+  return gerepileupto(av, RgX_Rg_div(U, D));
+}
+
+/************************************************************************
+ *                                                                      *
+ *                   IRREDUCIBLE POLYNOMIAL / Fp                        *
+ *                                                                      *
+ ************************************************************************/
+
+/* irreducible (unitary) polynomial of degree n over Fp */
+GEN
+ffinit_rand(GEN p,long n)
+{
+  for(;;) {
+    pari_sp av = avma;
+    GEN pol = ZX_add(monomial(gen_1, n, 0), random_FpX(n-1,0, p));
+    if (FpX_is_irred(pol, p)) return pol;
+    avma = av;
+  }
+}
+
+/* return an extension of degree 2^l of F_2, assume l > 0
+ * Not stack clean. */
+static GEN
+f2init(long l)
+{
+  long i;
+  GEN Q, T, S;
+
+  if (l == 1) return polcyclo(3, MAXVARN);
+
+  S = mkpoln(4, gen_1,gen_1,gen_0,gen_0); /* y(y^2 + y) */
+  setvarn(S, MAXVARN);
+  Q = mkpoln(3, gen_1,gen_1, S); /* x^2 + x + y(y^2+y) */
+
+  /* x^4+x+1, irred over F_2, minimal polynomial of a root of Q */
+  T = mkpoln(5, gen_1,gen_0,gen_0,gen_1,gen_1);
+  for (i=2; i<l; i++)
+  { /* Q = x^2 + x + a(y) irred. over K = F2[y] / (T(y))
+     * ==> x^2 + x + a(y) b irred. over K for any root b of Q
+     * ==> x^2 + x + (b^2+b)b */
+    setvarn(T,MAXVARN);
+    T = FpX_FpXY_resultant(T, Q, gen_2); /* = minpoly of b over F2 */
+  }
+  return T;
+}
+
+/* return an extension of degree p^l of F_p, assume l > 0
+ * Not stack clean. */
+GEN
+ffinit_Artin_Shreier(GEN ip, long l)
+{
+  long i, p = itos(ip);
+  GEN T, Q, xp = monomial(gen_1,p,0); /* x^p */
+  T = ZX_sub(xp, deg1pol_shallow(gen_1,gen_1,0)); /* x^p - x - 1 */
+  if (l == 1) return T;
+
+  Q = ZX_sub(monomial(gen_1,2*p-1,MAXVARN), monomial(gen_1,p,MAXVARN));
+  Q = gsub(xp, deg1pol_shallow(gen_1, Q, 0)); /* x^p - x - (y^(2p-1)-y^p) */
+  for (i = 2; i <= l; ++i)
+  {
+    setvarn(T,MAXVARN);
+    T = FpX_FpXY_resultant(T, Q, ip);
+  }
+  return T;
+}
+
+/* check if polsubcyclo(n,l,0) is irreducible modulo p */
+static long
+fpinit_check(GEN p, long n, long l)
+{
+  ulong q;
+  if (!uisprime(n)) return 0;
+  q = umodiu(p,n); if (!q) return 0;
+  return cgcd((n-1)/Fl_order(q, n-1, n), l) == 1;
+}
+
+/* let k=2 if p%4==1, and k=4 else and assume k*p does not divide l.
+ * Return an irreducible polynomial of degree l over F_p.
+ * Variant of Adleman and Lenstra "Finding irreducible polynomials over
+ * finite fields", ACM, 1986 (5) 350--355.
+ * Not stack clean */
+static GEN
+fpinit(GEN p, long l)
+{
+  ulong n = 1+l;
+  while (!fpinit_check(p,n,l)) n += l;
+  if (DEBUGLEVEL>=4) err_printf("FFInit: using polsubcyclo(%ld, %ld)\n",n,l);
+  return FpX_red(polsubcyclo(n,l,0),p);
+}
+
+static GEN
+ffinit_fact(GEN p, long n)
+{
+  GEN P, F = gel(factoru_pow(n),3);
+  long i;
+  if (!odd(n) && equaliu(p, 2))
+    P = f2init(vals(n)); /* if n is even, F[1] = 2^vals(n)*/
+  else
+    P = fpinit(p, F[1]);
+  for (i = 2; i < lg(F); ++i)
+    P = FpX_direct_compositum(fpinit(p, F[i]), P, p);
+  return P;
+}
+
+static GEN
+ffinit_nofact(GEN p, long n)
+{
+  GEN P, Q = NULL;
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2], q;
+    long v = u_lvalrem(n,pp,&q);
+    if (v>0)
+    {
+      Q = (pp == 2)? f2init(v): fpinit(p,n/q);
+      n = q;
+    }
+  }
+  /* n coprime to p */
+  if (n==1) P = Q;
+  else
+  {
+    P = fpinit(p, n);
+    if (Q) P = FpX_direct_compositum(P, Q, p);
+  }
+  return P;
+}
+
+static GEN
+init_Fq_i(GEN p, long n, long v)
+{
+  GEN P;
+  if (n <= 0) pari_err_DOMAIN("ffinit", "degree", "<=", gen_0, stoi(n));
+  if (typ(p) != t_INT) pari_err_TYPE("ffinit",p);
+  if (signe(p) <= 0) pari_err_PRIME("ffinit",p);
+  if (v < 0) v = 0;
+  if (n == 1) return pol_x(v);
+  if (fpinit_check(p, n+1, n)) return polcyclo(n+1, v);
+  if (lgefint(p)-2 <= expu(n))
+    P = ffinit_fact(p,n);
+  else
+    P = ffinit_nofact(p,n);
+  setvarn(P, v); return P;
+}
+GEN
+init_Fq(GEN p, long n, long v)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, init_Fq_i(p, n, v));
+}
+GEN
+ffinit(GEN p, long n, long v)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, FpX_to_mod(init_Fq_i(p, n, v), p));
+}
+
+GEN
+ffnbirred(GEN p, long n)
+{
+  pari_sp av = avma;
+  long j;
+  GEN s = gen_0, dk, pd;
+  dk = divisorsu(n);
+  for (j = 1; j < lg(dk); ++j)
+  {
+    long d = dk[j];
+    long m = moebiusu(d);
+    if (!m) continue;
+    pd = powiu(p, n/d);
+    s = m>0 ? addii(s, pd): subii(s,pd);
+  }
+  return gerepileuptoint(av, divis(s, n));
+}
+
+GEN
+ffsumnbirred(GEN p, long n)
+{
+  pari_sp av = avma;
+  long i,j;
+  GEN v,q, t = gen_0;
+  v = cgetg(n+1,t_VECSMALL); v[1] = 1;
+  q = cgetg(n+1,t_VEC); gel(q,1) = p;
+  for(i=2; i<=n; i++)
+  {
+    v[i] = moebiusu(i);
+    gel(q,i) = mulii(gel(q,i-1), p);
+  }
+  for(i=1; i<=n; i++)
+  {
+    GEN s = gen_0;
+    GEN dk = divisorsu(i);
+    for (j = 1; j < lg(dk); ++j)
+    {
+      long d = dk[j], m = v[d];
+      if (!m) continue;
+      s = m>0 ? addii(s, gel(q, i/d)): subii(s, gel(q, i/d));
+    }
+    t = addii(t, divis(s, i));
+  }
+  return gerepileuptoint(av, t);
+}
+
+GEN
+ffnbirred0(GEN p, long n, long flag)
+{
+  if (typ(p) != t_INT) pari_err_TYPE("ffnbirred", p);
+  if (n <= 0) pari_err_DOMAIN("ffnbirred", "degree", "<=", gen_0, stoi(n));
+  switch(flag)
+  {
+    case 0:
+      return ffnbirred(p, n);
+    case 1:
+      return ffsumnbirred(p, n);
+    default:
+      pari_err_FLAG("ffnbirred");
+  }
+  return NULL; /* NOT REACHED */
+}
diff --git a/src/basemath/prime.c b/src/basemath/prime.c
new file mode 100644
index 0000000..931abf7
--- /dev/null
+++ b/src/basemath/prime.c
@@ -0,0 +1,1349 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+/*********************************************************************/
+/**                                                                 **/
+/**               PSEUDO PRIMALITY (MILLER-RABIN)                   **/
+/**                                                                 **/
+/*********************************************************************/
+typedef struct {
+  ulong n, sqrt1, sqrt2, t1, t;
+  long r1;
+} Fl_MR_Jaeschke_t;
+
+typedef struct {
+  GEN n, sqrt1, sqrt2, t1, t;
+  long r1;
+} MR_Jaeschke_t;
+
+static void
+init_MR_Jaeschke(MR_Jaeschke_t *S, GEN n)
+{
+  if (signe(n) < 0) n = absi(n);
+  S->n = n;
+  S->t = addsi(-1,n);
+  S->r1 = vali(S->t);
+  S->t1 = shifti(S->t, -S->r1);
+  S->sqrt1 = cgeti(lg(n)); S->sqrt1[1] = evalsigne(0)|evallgefint(2);
+  S->sqrt2 = cgeti(lg(n)); S->sqrt2[1] = evalsigne(0)|evallgefint(2);
+}
+static void
+Fl_init_MR_Jaeschke(Fl_MR_Jaeschke_t *S, ulong n)
+{
+  S->n = n;
+  S->t = n-1;
+  S->r1 = vals(S->t);
+  S->t1 = S->t >> S->r1;
+  S->sqrt1 = 0;
+  S->sqrt2 = 0;
+}
+
+/* c = sqrt(-1) seen in bad_for_base. End-matching: compare or remember
+ * If ends do mismatch, then we have factored n, and this information
+ * should somehow be made available to the factoring machinery. But so
+ * exceedingly rare... besides we use BSPW now. */
+static int
+MR_Jaeschke_ok(MR_Jaeschke_t *S, GEN c)
+{
+  if (signe(S->sqrt1))
+  { /* saw one earlier: compare */
+    if (!equalii(c, S->sqrt1) && !equalii(c, S->sqrt2))
+    { /* too many sqrt(-1)s mod n */
+      if (DEBUGLEVEL) {
+        GEN z = gcdii(addii(c, S->sqrt1), S->n);
+        pari_warn(warner,"found factor\n\t%Ps\ncurrently lost to the factoring machinery", z);
+      }
+      return 1;
+    }
+  } else { /* remember */
+    affii(c, S->sqrt1);
+    affii(subii(S->n, c), S->sqrt2);
+  }
+  return 0;
+}
+static int
+Fl_MR_Jaeschke_ok(Fl_MR_Jaeschke_t *S, ulong c)
+{
+  if (S->sqrt1)
+  { /* saw one earlier: compare */
+    if (c != S->sqrt1 && c != S->sqrt2) return 1;
+  } else { /* remember */
+    S->sqrt1 = c;
+    S->sqrt2 = S->n - c;
+  }
+  return 0;
+}
+
+/* is n strong pseudo-prime for base a ? 'End matching' (check for square
+ * roots of -1) added by GN */
+static int
+bad_for_base(MR_Jaeschke_t *S, GEN a)
+{
+  long r, lim, av = avma;
+  GEN c2, c = Fp_pow(a, S->t1, S->n);
+
+  if (is_pm1(c) || equalii(S->t, c)) return 0;
+
+  lim = stack_lim(av,1);
+  /* go fishing for -1, not for 1 (saves one squaring) */
+  for (r = S->r1 - 1; r; r--) /* r1 - 1 squarings */
+  {
+    c2 = c; c = remii(sqri(c), S->n);
+    if (equalii(S->t, c)) return MR_Jaeschke_ok(S, c2);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"Rabin-Miller");
+      c = gerepileuptoint(av, c);
+    }
+  }
+  return 1;
+}
+static int
+Fl_bad_for_base(Fl_MR_Jaeschke_t *S, ulong a)
+{
+  long r;
+  ulong c2, c = Fl_powu(a, S->t1, S->n);
+
+  if (c == 1 || c == S->t) return 0;
+
+  /* go fishing for -1, not for 1 (saves one squaring) */
+  for (r = S->r1 - 1; r; r--) /* r1 - 1 squarings */
+  {
+    c2 = c; c = Fl_sqr(c, S->n);
+    if (c == S->t) return Fl_MR_Jaeschke_ok(S, c2);
+  }
+  return 1;
+}
+
+/* Miller-Rabin test for k random bases */
+long
+millerrabin(GEN n, long k)
+{
+  pari_sp av2, av = avma;
+  ulong r;
+  long i;
+  MR_Jaeschke_t S;
+
+  if (typ(n) != t_INT) pari_err_TYPE("millerrabin",n);
+  if (signe(n)<=0) return 0;
+  /* If |n| <= 3, check if n = +- 1 */
+  if (lgefint(n)==3 && (ulong)(n[2])<=3) return (n[2] != 1);
+
+  if (!mod2(n)) return 0;
+  init_MR_Jaeschke(&S, n); av2 = avma;
+  for (i=1; i<=k; i++)
+  {
+    do r = umodui(pari_rand(), n); while (!r);
+    if (DEBUGLEVEL > 4) err_printf("Miller-Rabin: testing base %ld\n", r);
+    if (bad_for_base(&S, utoipos(r))) { avma = av; return 0; }
+    avma = av2;
+  }
+  avma = av; return 1;
+}
+
+GEN
+gispseudoprime(GEN x, long flag)
+{ return flag? map_proto_lGL(millerrabin, x, flag): map_proto_lG(BPSW_psp,x); }
+
+long
+ispseudoprime(GEN x, long flag)
+{ return flag? millerrabin(x, flag): BPSW_psp(x); }
+
+/* As above for k bases taken in pr (i.e not random). We must have |n|>2 and
+ * 1<=k<=11 (not checked) or k in {16,17} to select some special sets of bases.
+ *
+ * From Jaeschke, 'On strong pseudoprimes to several bases', Math.Comp. 61
+ * (1993), 915--926  (see also http://www.utm.edu/research/primes/prove2.html),
+ * we have:
+ *
+ * k == 4  (bases 2,3,5,7)  detects all composites
+ *    n <     118 670 087 467 == 172243 * 688969  with the single exception of
+ *    n ==      3 215 031 751 == 151 * 751 * 28351,
+ *
+ * k == 5  (bases 2,3,5,7,11)  detects all composites
+ *    n <   2 152 302 898 747 == 6763 * 10627 * 29947,
+ *
+ * k == 6  (bases 2,3,...,13)  detects all composites
+ *    n <   3 474 749 660 383 == 1303 * 16927 * 157543,
+ *
+ * k == 7  (bases 2,3,...,17)  detects all composites
+ *    n < 341 550 071 728 321 == 10670053 * 32010157,
+ * Even this limiting value is caught by an end mismatch between bases 5 and 17
+ *
+ * Moreover, the four bases chosen at
+ *
+ * k == 16  (2,13,23,1662803)  detects all composites up
+ * to at least 10^12, and the combination at
+ *
+ * k == 17  (31,73)  detects most odd composites without prime factors > 100
+ * in the range  n < 2^36  (with less than 250 exceptions, indeed with fewer
+ * than 1400 exceptions up to 2^42). --GN */
+int
+Fl_MR_Jaeschke(ulong n, long k)
+{
+  const ulong pr[] =
+    { 0, 2,3,5,7,11,13,17,19,23,29, 31,73, 2,13,23,1662803UL, };
+  const ulong *p;
+  ulong r;
+  long i;
+  Fl_MR_Jaeschke_t S;
+
+  if (!(n & 1)) return 0;
+  if (k == 16)
+  { /* use smaller (faster) bases if possible */
+    p = (n < 3215031751UL)? pr: pr+13;
+    k = 4;
+  }
+  else if (k == 17)
+  {
+    p = (n < 1373653UL)? pr: pr+11;
+    k = 2;
+  }
+  else p = pr; /* 2,3,5,... */
+  Fl_init_MR_Jaeschke(&S, n);
+  for (i=1; i<=k; i++)
+  {
+    r = p[i] % n; if (!r) break;
+    if (Fl_bad_for_base(&S, r)) return 0;
+  }
+  return 1;
+}
+
+int
+MR_Jaeschke(GEN n, long k)
+{
+  pari_sp av2, av = avma;
+  const ulong pr[] =
+    { 0, 2,3,5,7,11,13,17,19,23,29, 31,73, 2,13,23,1662803UL, };
+  const ulong *p;
+  long i;
+  MR_Jaeschke_t S;
+
+  if (lgefint(n) == 3) return Fl_MR_Jaeschke((ulong)n[2], k);
+
+  if (!mod2(n)) return 0;
+  if      (k == 16) { p = pr+13; k = 4; } /* 2,13,23,1662803 */
+  else if (k == 17) { p = pr+11; k = 2; } /* 31,73 */
+  else p = pr; /* 2,3,5,... */
+  init_MR_Jaeschke(&S, n); av2 = avma;
+  for (i=1; i<=k; i++)
+  {
+    if (bad_for_base(&S, utoipos(p[i]))) { avma = av; return 0; }
+    avma = av2;
+  }
+  avma = av; return 1;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                      PSEUDO PRIMALITY (LUCAS)                   **/
+/**                                                                 **/
+/*********************************************************************/
+/* compute n-th term of Lucas sequence modulo N.
+ * v_{k+2} = P v_{k+1} - v_k, v_0 = 2, v_1 = P.
+ * Assume n > 0 */
+static GEN
+LucasMod(GEN n, ulong P, GEN N)
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN nd = int_MSW(n);
+  long i, m = *nd, j = 1+bfffo((ulong)m);
+  GEN v = utoipos(P), v1 = utoipos(P*P - 2);
+
+  m <<= j; j = BITS_IN_LONG - j;
+  for (i=lgefint(n)-2;;) /* cf. leftright_pow */
+  {
+    for (; j; m<<=1,j--)
+    { /* v = v_k, v1 = v_{k+1} */
+      if (m < 0)
+      { /* set v = v_{2k+1}, v1 = v_{2k+2} */
+        v = subis(mulii(v,v1), (long)P);
+        v1= subis(sqri(v1), 2);
+      }
+      else
+      {/* set v = v_{2k}, v1 = v_{2k+1} */
+        v1= subis(mulii(v,v1), (long)P);
+        v = subis(sqri(v), 2);
+      }
+      v = modii(v, N);
+      v1= modii(v1,N);
+      if (low_stack(lim,stack_lim(av,1)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"LucasMod");
+        gerepileall(av, 2, &v,&v1);
+      }
+    }
+    if (--i == 0) return v;
+    j = BITS_IN_LONG;
+    nd=int_precW(nd);
+    m = *nd;
+  }
+}
+/* compute n-th term of Lucas sequence modulo N.
+ * v_{k+2} = P v_{k+1} - v_k, v_0 = 2, v_1 = P.
+ * Assume n > 0 */
+static ulong
+u_LucasMod(ulong n, ulong P, ulong N)
+{
+  long j = 1 + bfffo(n);
+  ulong v = P, v1 = P*P - 2, mP = N - P, m2 = N - 2, m = n << j;
+
+  j = BITS_IN_LONG - j;
+  for (; j; m<<=1,j--)
+  { /* v = v_k, v1 = v_{k+1} */
+    if (((long)m) < 0)
+    { /* set v = v_{2k+1}, v1 = v_{2k+2} */
+      v = Fl_add(Fl_mul(v,v1,N), mP, N);
+      v1= Fl_add(Fl_mul(v1,v1,N),m2, N);
+    }
+    else
+    {/* set v = v_{2k}, v1 = v_{2k+1} */
+      v1= Fl_add(Fl_mul(v,v1,N),mP, N);
+      v = Fl_add(Fl_mul(v,v,N), m2, N);
+    }
+  }
+  return v;
+}
+
+int
+uislucaspsp(ulong n)
+{
+  long i, v;
+  ulong b, z, m2, m = n + 1;
+
+  for (b=3, i=0;; b+=2, i++)
+  {
+    ulong c = b*b - 4; /* = 1 mod 4 */
+    if (krouu(n % c, c) < 0) break;
+    if (i == 64 && uissquareall(n, &c)) return 0; /* oo loop if N = m^2 */
+  }
+  if (!m) return 0; /* neither 2^32-1 nor 2^64-1 are Lucas-pp */
+  v = vals(m); m >>= v;
+  z = u_LucasMod(m, b, n);
+  if (z == 2) return 1;
+  m2 = n - 2;
+  if (z == m2) return 1;
+  for (i=1; i<v; i++)
+  {
+    if (!z) return 1;
+    z = Fl_add(Fl_mul(z,z, n), m2, n);
+    if (z == 2) return 0;
+  }
+  return 0;
+}
+/* check that N not a square first (taken care of here, but inefficient)
+ * assume N > 3 */
+static int
+IsLucasPsP(GEN N)
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN N_2, m, z;
+  long i, v;
+  ulong b;
+
+  for (b=3, i=0;; b+=2, i++)
+  {
+    ulong c = b*b - 4; /* = 1 mod 4 */
+    if (i == 64 && Z_issquare(N)) return 0; /* avoid oo loop if N = m^2 */
+    if (krouu(umodiu(N,c), c) < 0) break;
+  }
+  m = addis(N,1); v = vali(m); m = shifti(m,-v);
+  z = LucasMod(m, b, N);
+  if (equaliu(z, 2)) return 1;
+  N_2 = subis(N,2);
+  if (equalii(z, N_2)) return 1;
+  for (i=1; i<v; i++)
+  {
+    if (!signe(z)) return 1;
+    z = modii(subis(sqri(z), 2), N);
+    if (equaliu(z, 2)) return 0;
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"IsLucasPsP");
+      z = gerepileupto(av, z);
+    }
+  }
+  return 0;
+}
+
+/* assume u odd, u > 1 */
+static int
+iu_coprime(GEN N, ulong u)
+{
+  const ulong n = umodiu(N, u);
+  return (n == 1 || gcduodd(n, u) == 1);
+}
+/* assume u odd, u > 1 */
+static int
+uu_coprime(ulong n, ulong u)
+{
+  return gcduodd(n, u) == 1;
+}
+
+/* composite strong 2-pseudoprime < 1016801 whose prime divisors are > 101 */
+static int
+is_2_prp_101(ulong n)
+{
+  switch(n) {
+  case 42799:
+  case 49141:
+  case 88357:
+  case 90751:
+  case 104653:
+  case 130561:
+  case 196093:
+  case 220729:
+  case 253241:
+  case 256999:
+  case 271951:
+  case 280601:
+  case 357761:
+  case 390937:
+  case 458989:
+  case 486737:
+  case 489997:
+  case 514447:
+  case 580337:
+  case 741751:
+  case 838861:
+  case 873181:
+  case 877099:
+  case 916327:
+  case 976873:
+  case 983401: return 1;
+  } return 0;
+}
+
+static int
+u_2_prp(ulong n)
+{
+  Fl_MR_Jaeschke_t S;
+  Fl_init_MR_Jaeschke(&S, n);
+  return Fl_bad_for_base(&S, 2) == 0;
+}
+static int
+uBPSW_psp(ulong n) { return (u_2_prp(n) && uislucaspsp(n)); }
+
+int
+uisprime(ulong n)
+{
+  if (n < 103)
+    switch(n)
+    {
+      case 2:
+      case 3:
+      case 5:
+      case 7:
+      case 11:
+      case 13:
+      case 17:
+      case 19:
+      case 23:
+      case 29:
+      case 31:
+      case 37:
+      case 41:
+      case 43:
+      case 47:
+      case 53:
+      case 59:
+      case 61:
+      case 67:
+      case 71:
+      case 73:
+      case 79:
+      case 83:
+      case 89:
+      case 97:
+      case 101: return 1;
+      default: return 0;
+    }
+  if (!odd(n)) return 0;
+#ifdef LONG_IS_64BIT
+  /* 16294579238595022365 = 3*5*7*11*13*17*19*23*29*31*37*41*43*47*53
+   *  7145393598349078859 = 59*61*67*71*73*79*83*89*97*101 */
+  if (!uu_coprime(n, 16294579238595022365UL) ||
+      !uu_coprime(n,  7145393598349078859UL)) return 0;
+#else
+  /* 4127218095 = 3*5*7*11*13*17*19*23*37
+   * 3948078067 = 29*31*41*43*47*53
+   * 4269855901 = 59*83*89*97*101
+   * 1673450759 = 61*67*71*73*79 */
+  if (!uu_coprime(n, 4127218095UL) ||
+      !uu_coprime(n, 3948078067UL) ||
+      !uu_coprime(n, 1673450759UL) ||
+      !uu_coprime(n, 4269855901UL)) return 0;
+#endif
+  if (n < 10427) return 1;
+  if (n < 1016801) return !is_2_prp_101(n) && u_2_prp(n);
+  return uBPSW_psp(n);
+}
+
+/* assume no prime divisor <= 101 */
+int
+uisprime_101(ulong n)
+{
+  if (n < 10427) return 1;
+  if (n < 1016801) return !is_2_prp_101(n) && u_2_prp(n);
+  return uBPSW_psp(n);
+}
+
+/* assume no prime divisor <= 661 */
+int
+uisprime_661(ulong n) { return uBPSW_psp(n); }
+
+long
+BPSW_psp(GEN N)
+{
+  pari_sp av;
+  MR_Jaeschke_t S;
+  int k;
+
+  if (typ(N) != t_INT) pari_err_TYPE("BPSW_psp",N);
+  if (signe(N) <= 0) return 0;
+  if (lgefint(N) == 3) return uisprime((ulong)N[2]);
+  if (!mod2(N)) return 0;
+#ifdef LONG_IS_64BIT
+  /* 16294579238595022365 = 3*5*7*11*13*17*19*23*29*31*37*41*43*47*53
+   *  7145393598349078859 = 59*61*67*71*73*79*83*89*97*101 */
+  if (!iu_coprime(N, 16294579238595022365UL) ||
+      !iu_coprime(N,  7145393598349078859UL)) return 0;
+#else
+  /* 4127218095 = 3*5*7*11*13*17*19*23*37
+   * 3948078067 = 29*31*41*43*47*53
+   * 4269855901 = 59*83*89*97*101
+   * 1673450759 = 61*67*71*73*79 */
+  if (!iu_coprime(N, 4127218095UL) ||
+      !iu_coprime(N, 3948078067UL) ||
+      !iu_coprime(N, 1673450759UL) ||
+      !iu_coprime(N, 4269855901UL)) return 0;
+#endif
+  /* no prime divisor < 103 */
+  av = avma;
+  init_MR_Jaeschke(&S, N);
+  k = (!bad_for_base(&S, gen_2) && IsLucasPsP(N));
+  avma = av; return k;
+}
+
+/* can we write n = x^k ? Assume N has no prime divisor <= 2^14.
+ * Not memory clean */
+long
+isanypower_nosmalldiv(GEN N, GEN *px)
+{
+  GEN x = N, y;
+  ulong mask = 7;
+  long ex, k = 1;
+  forprime_t T;
+  while (Z_issquareall(x, &y)) { k <<= 1; x = y; }
+  while ( (ex = is_357_power(x, &y, &mask)) ) { k *= ex; x = y; }
+  (void)u_forprime_init(&T, 11, ULONG_MAX);
+  /* stop when x^(1/k) < 2^14 */
+  while ( (ex = is_pth_power(x, &y, &T, 15)) ) { k *= ex; x = y; }
+  *px = x; return k;
+}
+
+/* no prime divisor <= 2^14 (> 661) */
+long
+BPSW_psp_nosmalldiv(GEN N)
+{
+  pari_sp av;
+  MR_Jaeschke_t S;
+  long l = lgefint(N);
+  int k;
+
+  if (l == 3) return uisprime_661((ulong)N[2]);
+  av = avma;
+  /* N large: test for pure power, rarely succeeds, but requires < 1% of
+   * compositeness test times */
+  if (bit_accuracy(l) > 512 && isanypower_nosmalldiv(N, &N) != 1)
+  {
+    avma = av; return 0;
+  }
+  init_MR_Jaeschke(&S, N);
+  k = (!bad_for_base(&S, gen_2) && IsLucasPsP(N));
+  avma = av; return k;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       Pocklington-Lehmer                          **/
+/**                        P-1 primality test                         **/
+/**                                                                   **/
+/***********************************************************************/
+/* Assume x BPSW pseudoprime. Check whether it's small enough to be certified
+ * prime (< 2^64). Reference for strong 2-pseudoprimes:
+ *   http://www.cecm.sfu.ca/Pseudoprimes/index-2-to-64.html */
+static int
+BPSW_isprime_small(GEN x)
+{
+  long l = lgefint(x);
+#ifdef LONG_IS_64BIT
+  return (l == 3);
+#else
+  return (l <= 4);
+#endif
+}
+
+/* Brillhart, Lehmer, Selfridge test (Crandall & Pomerance, Th 4.1.5)
+ * N^(1/3) <= f fully factored, f | N-1. If pl831(p) is true for
+ * any prime divisor p of f, then any divisor of N is 1 mod f.
+ * In that case return 1 iff N is prime */
+static int
+BLS_test(GEN N, GEN f)
+{
+  GEN c1, c2, r, q;
+  q = dvmdii(N, f, &r);
+  if (!is_pm1(r)) return 0;
+  c2 = dvmdii(q, f, &c1);
+  /* N = 1 + f c1 + f^2 c2, 0 <= c_i < f; check whether it is of the form
+   * (1 + f a)(1 + fb) */
+  return ! Z_issquare(subii(sqri(c1), shifti(c2,2)));
+}
+
+/*assume N>1, p^e || N-1. Find a witness a(p) such that
+ * a^(N-1) = 1 (mod N)
+ * a^(N-1)/p - 1 invertible mod N.
+ * Proves that any divisor of N is 1 mod p^e */
+static ulong
+pl831(GEN N, GEN p)
+{
+  pari_sp ltop = avma, av;
+  ulong a;
+  GEN Nmunp = diviiexact(addis(N,-1), p);
+  av = avma;
+  for(a = 2;; a++, avma = av)
+  {
+    GEN b = Fp_pow(utoipos(a), Nmunp, N);
+    GEN c = Fp_pow(b,p,N), g = gcdii(addis(b,-1), N);
+    if (!is_pm1(c)) return 0;
+    if (is_pm1(g)) { avma=ltop; return a; }
+    if (!equalii(g,N)) return 0;
+  }
+}
+/* Assume x BPSW pseudoprime. Return gen_0 if not prime, and a primality
+ * certificate otherwise */
+static GEN isprimePL(GEN N);
+static GEN
+check_prime(GEN p)
+{
+  if (BPSW_isprime_small(p)) return gen_1;
+  if (expi(p) <= 250) return isprimePL(p);
+  return isprimeAPRCL(p)? gen_2: gen_0;
+}
+/* initialize Selfridge / Pocklington-Lehmer test, return 0
+ * if proven composite */
+static int
+selfridge_init(GEN N, GEN *pF, GEN *pf)
+{
+  GEN cbrtN = sqrtnint(N, 3);
+  GEN N_1 = addis(N,-1);
+  GEN F = Z_factor_until(N_1, sqri(cbrtN));
+  GEN f = factorback(F); /* factored part of N-1, f^3 > N */
+  *pF = gel(F,1);
+  *pf = f;
+  /* smooth or N^(1/2)-smooth (Pocklington) or N^(1/3)-smooth (BLS) */
+  return equalii(f, N_1) || cmpii(sqri(f), N) > 0 || BLS_test(N,f);
+}
+/* Assume N is a strong BPSW pseudoprime, Pocklington-Lehmer primality proof.
+ *
+ * return gen_0 (non-prime), gen_1 (small prime), matrix (large prime)
+ *
+ * The matrix has 3 columns, [a,b,c] with
+ * a[i] prime factor of N-1,
+ * b[i] witness for a[i] as in pl831
+ * c[i] isprimePL(a[i]) */
+static GEN
+isprimePL(GEN N)
+{
+  pari_sp ltop = avma;
+  long i, l;
+  int eps;
+  GEN C, P, W, R, F, f;
+
+  if (typ(N) != t_INT) pari_err_TYPE("isprimePL",N);
+  eps = cmpis(N,2);
+  if (eps <= 0) return eps? gen_0: gen_1;
+  /* N > 2 */
+  if (!selfridge_init(N, &F, &f)) { avma = ltop; return gen_0; }
+  if (DEBUGLEVEL>3)
+  {
+    err_printf("Pocklington-Lehmer: proving primality of N = %Ps\n", N);
+    err_printf("Pocklington-Lehmer: N-1 factored up to %Ps! (%.3Ps%%)\n", f, divri(itor(f,LOWDEFAULTPREC), N));
+    err_printf("Pocklington-Lehmer: N-1 smooth enough! Computing certificate\n");
+  }
+  C = cgetg(4,t_MAT); l = lg(F);
+  gel(C,1) = P = cgetg(l,t_COL);
+  gel(C,2) = W = cgetg(l,t_COL);
+  gel(C,3) = R = cgetg(l,t_COL);
+  for(i=1; i<l; i++)
+  {
+    GEN p = gel(F,i);
+    ulong witness = pl831(N,p);
+
+    if (!witness) { avma = ltop; return gen_0; }
+    gel(P,i) = icopy(p);
+    gel(W,i) = utoipos(witness);
+    gel(R,i) = check_prime(p);
+    if (gel(R,i) == gen_0)
+    { /* composite in prime factorisation ! */
+      err_printf("Not a prime: %Ps", p);
+      pari_err_BUG("isprimePL [false prime number]");
+    }
+  }
+  return gerepileupto(ltop,C);
+}
+
+/* F is a vector of BPSW pseudoprimes of N. For each of then, find a PL witness,
+ * then prove primality. If (avoid_last) do not apply test to last entry */
+static long
+isprimeSelfridge(GEN N, GEN F, int avoid_last)
+{
+  long i, l = lg(F);
+  if (avoid_last) l--;
+  for(i = 1; i < l; i++)
+  {
+    GEN p = gel(F,i);
+    if (! pl831(N, p)) return 0;
+    if (DEBUGLEVEL>3)
+      err_printf("Pocklington-Lehmer: recursively proving primality of p = %Ps\n", p);
+    if (!BPSW_isprime(p)) return 0;
+  }
+  return 1;
+}
+
+/* assume N a BPSW pseudoprime, in particular, it is odd > 2 */
+long
+BPSW_isprime(GEN N)
+{
+  pari_sp av = avma;
+  long l, res;
+  ulong B;
+  GEN fa, P, E, p, U, F, N_1;
+
+  if (BPSW_isprime_small(N)) return 1;
+  N_1 = subis(N,1);
+  B = minuu(1UL<<19, maxprime());
+  fa = Z_factor_limit(N_1, B);
+  P = gel(fa,1);
+  E = gel(fa,2);
+  l = lg(P)-1;
+  p = gel(P,l);
+  U = powii(p, gel(E,l)); /* (possibly) unfactored part of N-1 */
+  if (cmpiu(U, B) <= 0) { U = gen_1; F = N_1; }
+  else
+  { /* l >= 2, since 2 and U divide  N-1 */
+    if (l == 2)
+      F = powii(gel(P,1), gel(E,1));
+    else
+      F = diviiexact(N_1,  U);
+  }
+  /* N-1 = F U, F factored, U possibly composite */
+  if (U == gen_1) /* smooth */
+    res = isprimeSelfridge(N, P, 0);
+  else if (cmpii(F, U) >= 0) /* 1/2-smooth */
+    res = isprimeSelfridge(N, P, 1);
+  else if (cmpii(sqri(F), U) >= 0) /* 1/3-smooth */
+  {
+    res = BLS_test(N, F);
+    if (res) res = isprimeSelfridge(N, P, 1);
+  }
+  else if (BPSW_psp_nosmalldiv(p)) /* smooth after all */
+    res = isprimeSelfridge(N, P, 0);
+  else
+    res = isprimeAPRCL(N);
+  avma = av; return res;
+}
+
+GEN
+gisprime(GEN x, long flag)
+{
+  switch (flag)
+  {
+    case 0: return map_proto_lG(isprime,x);
+    case 1: return map_proto_G(isprimePL,x);
+    case 2: return map_proto_lG(isprimeAPRCL,x);
+  }
+  pari_err_FLAG("gisprime");
+  return NULL;
+}
+
+long
+isprime(GEN x) { return BPSW_psp(x) && BPSW_isprime(x); }
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          PRIME NUMBERS                            **/
+/**                                                                   **/
+/***********************************************************************/
+
+static struct {
+  ulong p;
+  long n;
+} prime_table[] = {
+  {           0,          0},
+  {        7919,       1000},
+  {       17389,       2000},
+  {       27449,       3000},
+  {       37813,       4000},
+  {       48611,       5000},
+  {       59359,       6000},
+  {       70657,       7000},
+  {       81799,       8000},
+  {       93179,       9000},
+  {      104729,      10000},
+  {      224737,      20000},
+  {      350377,      30000},
+  {      479909,      40000},
+  {      611953,      50000},
+  {      746773,      60000},
+  {      882377,      70000},
+  {     1020379,      80000},
+  {     1159523,      90000},
+  {     1299709,     100000},
+  {     2750159,     200000},
+  {     7368787,     500000},
+  {    15485863,    1000000},
+  {    32452843,    2000000},
+  {    86028121,    5000000},
+  {   179424673,   10000000},
+  {   373587883,   20000000},
+  {   982451653,   50000000},
+  {  2038074743,  100000000},
+  {  4000000483UL,189961831},
+  {  4222234741UL,200000000},
+#if BITS_IN_LONG == 64
+  { 11037271757,  500000000},
+  { 22801763489, 1000000000},
+  { 47055833459, 2000000000},
+  {122430513841, 5000000000},
+  {200000000507, 8007105083},
+#endif
+};
+static const int prime_table_len = sizeof(prime_table)/sizeof(prime_table[0]);
+
+/* find prime closest to n in prime_table. */
+static long
+prime_table_closest_p(ulong n)
+{
+  long i;
+  for (i = 1; i < prime_table_len; i++)
+  {
+    ulong p = prime_table[i].p;
+    if (p > n)
+    {
+      ulong u = n - prime_table[i-1].p;
+      if (p - n > u) i--;
+      break;
+    }
+  }
+  if (i == prime_table_len) i = prime_table_len - 1;
+  return i;
+}
+
+/* return the n-th successor of prime p > 2 */
+static GEN
+prime_successor(ulong p, ulong n)
+{
+  forprime_t S;
+  ulong i;
+  forprime_init(&S, utoipos(p+1), NULL);
+  for (i = 1; i < n; i++) (void)forprime_next(&S);
+  return forprime_next(&S);
+}
+/* find the N-th prime */
+static GEN
+prime_table_find_n(ulong N)
+{
+  byteptr d;
+  ulong n, p, maxp = maxprime();
+  long i;
+  for (i = 1; i < prime_table_len; i++)
+  {
+    n = prime_table[i].n;
+    if (n > N)
+    {
+      ulong u = N - prime_table[i-1].n;
+      if (n - N > u) i--;
+      break;
+    }
+  }
+  if (i == prime_table_len) i = prime_table_len - 1;
+  p = prime_table[i].p;
+  n = prime_table[i].n;
+  if (n > N && p > maxp)
+  {
+    i--;
+    p = prime_table[i].p;
+    n = prime_table[i].n;
+  }
+  /* if beyond prime table, then n <= N */
+  d = diffptr + n;
+  if (n > N)
+  {
+    n -= N;
+    do { n--; PREC_PRIME_VIADIFF(p,d); } while (n) ;
+  }
+  else if (n < N)
+  {
+    n = N-n;
+    if (p > maxp) return prime_successor(p, n);
+    do {
+      if (!*d) return prime_successor(p, n);
+      n--; NEXT_PRIME_VIADIFF(p,d);
+    } while (n) ;
+  }
+  return utoipos(p);
+}
+
+ulong
+uprime(long N)
+{
+  pari_sp av = avma;
+  GEN p;
+  if (N <= 0) pari_err_DOMAIN("prime", "n", "<=",gen_0, stoi(N));
+  p = prime_table_find_n(N);
+  if (lgefint(p) != 3) pari_err_OVERFLOW("uprime");
+  avma = av; return p[2];
+}
+GEN
+prime(long N)
+{
+  pari_sp av = avma;
+  GEN p;
+  if (N <= 0) pari_err_DOMAIN("prime", "n", "<=",gen_0, stoi(N));
+  new_chunk(4); /*HACK*/
+  p = prime_table_find_n(N);
+  avma = av; return icopy(p);
+}
+
+/* random b-bit prime */
+GEN
+randomprime(GEN N)
+{
+  pari_sp av = avma, av2;
+  GEN a, b, d;
+  if (!N)
+    for(;;)
+    {
+      ulong p = random_bits(31);
+      if (uisprime(p)) return utoipos(p);
+    }
+  switch(typ(N))
+  {
+    case t_INT:
+      a = gen_2;
+      b = subiu(N,1); /* between 2 and N-1 */
+      d = subiu(N,2);
+      if (signe(d) <= 0)
+        pari_err_DOMAIN("randomprime","N", "<", gen_2, N);
+      break;
+    case t_VEC:
+      if (lg(N) != 3) pari_err_TYPE("randomprime",N);
+      a = gel(N,1);
+      b = gel(N,2);
+      if (gcmp(b, a) < 0)
+        pari_err_DOMAIN("randomprime","b-a", "<", gen_0, mkvec2(a,b));
+      if (typ(a) != t_INT)
+      {
+        a = gceil(a);
+        if (typ(a) != t_INT) pari_err_TYPE("randomprime",a);
+      }
+      if (typ(b) != t_INT)
+      {
+        b = gfloor(b);
+        if (typ(b) != t_INT) pari_err_TYPE("randomprime",b);
+      }
+      if (cmpis(a, 2) < 0)
+      {
+        a = gen_2;
+        d = subiu(b,1);
+      }
+      else
+        d = addiu(subii(b,a), 1);
+      if (signe(d) <= 0)
+        pari_err_DOMAIN("randomprime","floor(b) - max(ceil(a),2)", "<",
+                        gen_0, mkvec2(a,b));
+      break;
+    default:
+      pari_err_TYPE("randomprime", N);
+      return NULL; /*notreached*/
+  }
+  av2 = avma;
+  for (;;)
+  {
+    GEN p = addii(a, randomi(d));
+    if (BPSW_psp(p)) return gerepileuptoint(av, p);
+    avma = av2;
+  }
+}
+
+/* set *pp = nextprime(a) = p
+ *     *pd so that NEXT_PRIME_VIADIFF(d, p) = nextprime(p+1)
+ *     *pn so that p = the n-th prime
+ * error if nextprime(a) is out of primetable bounds */
+void
+prime_table_next_p(ulong a, byteptr *pd, ulong *pp, ulong *pn)
+{
+  byteptr d;
+  ulong p, n, maxp = maxprime();
+  long i = prime_table_closest_p(a);
+  p = prime_table[i].p;
+  if (p > a && p > maxp)
+  {
+    i--;
+    p = prime_table[i].p;
+  }
+  /* if beyond prime table, then p <= a */
+  n = prime_table[i].n;
+  d = diffptr + n;
+  if (p < a)
+  {
+    if (a > maxp) pari_err_MAXPRIME(a);
+    do { n++; NEXT_PRIME_VIADIFF(p,d); } while (p < a);
+  }
+  else if (p != a)
+  {
+    do { n--; PREC_PRIME_VIADIFF(p,d); } while (p > a) ;
+    if (p < a) { NEXT_PRIME_VIADIFF(p,d); n++; }
+  }
+  *pn = n;
+  *pp = p;
+  *pd = d;
+}
+
+ulong
+uprimepi(ulong a)
+{
+  ulong p, n, maxp = maxprime();
+  if (a <= maxp)
+  {
+    byteptr d;
+    prime_table_next_p(a, &d, &p, &n);
+    return p == a? n: n-1;
+  }
+  else
+  {
+    long i = prime_table_closest_p(a);
+    forprime_t S;
+    p = prime_table[i].p;
+    if (p > a)
+    {
+      i--;
+      p = prime_table[i].p;
+    }
+    /* p = largest prime in table <= a */
+    n = prime_table[i].n;
+    (void)u_forprime_init(&S, p+1, a);
+    for (; p; n++) p = u_forprime_next(&S);
+    return n-1;
+  }
+}
+
+GEN
+primepi(GEN x)
+{
+  pari_sp av = avma;
+  GEN pp, nn, N = typ(x) == t_INT? x: gfloor(x);
+  forprime_t S;
+  ulong n, p;
+  long i, l;
+  if (typ(N) != t_INT) pari_err_TYPE("primepi",N);
+  if (signe(N) <= 0) return gen_0;
+  avma = av; l = lgefint(N);
+  if (l == 3) return utoi(uprimepi(N[2]));
+  i = prime_table_len-1;
+  p = prime_table[i].p;
+  n = prime_table[i].n;
+  (void)forprime_init(&S, utoipos(p+1), N);
+  nn = setloop(utoipos(n));
+  pp = gen_0;
+  for (; pp; incloop(nn)) pp = forprime_next(&S);
+  return gerepileuptoint(av, subiu(nn,1));
+}
+
+/* pi(x) < x/log x * (1 + 1/log x + 2.51/log^2 x)), x>=355991 [ Dusart ]
+ * pi(x) < x/(log x - 1.1), x >= 60184 [ Dusart ]
+ * ? \p9
+ * ? M = 0; for(x = 4, 60184, M = max(M, log(x) - x/primepi(x))); M
+ * %1 = 1.11196252 */
+double
+primepi_upper_bound(double x)
+{
+  if (x >= 355991)
+  {
+    double L = 1/log(x);
+    return x * L * (1 + L + 2.51*L*L);
+  }
+  if (x >= 60184) return x / (log(x) - 1.1);
+  if (x < 5) return 2; /* don't bother */
+  return x / (log(x) - 1.111963);
+}
+/* pi(x) > x/log x (1 + 1/log x), x >= 599 [ Dusart ]
+ * pi(x) > x / (log x + 2), x >= 55 [ Rosser ] */
+double
+primepi_lower_bound(double x)
+{
+  if (x >= 599)
+  {
+    double L = 1/log(x);
+    return x * L * (1 + L);
+  }
+  if (x < 55) return 0; /* don't bother */
+  return x / (log(x) + 2.);
+}
+GEN
+gprimepi_upper_bound(GEN x)
+{
+  pari_sp av = avma;
+  double L;
+  if (typ(x) != t_INT) x = gfloor(x);
+  if (expi(x) <= 1022)
+  {
+    avma = av;
+    return dbltor(primepi_upper_bound(gtodouble(x)));
+  }
+  x = itor(x, LOWDEFAULTPREC);
+  L = 1 / rtodbl(logr_abs(x));
+  x = mulrr(x, dbltor(L * (1 + L + 2.51*L*L)));
+  return gerepileuptoleaf(av, x);
+}
+GEN
+gprimepi_lower_bound(GEN x)
+{
+  pari_sp av = avma;
+  double L;
+  if (typ(x) != t_INT) x = gfloor(x);
+  if (cmpiu(x, 55) <= 0) return gen_0;
+  if (expi(x) <= 1022)
+  {
+    avma = av;
+    return dbltor(primepi_lower_bound(gtodouble(x)));
+  }
+  x = itor(x, LOWDEFAULTPREC);
+  L = 1 / rtodbl(logr_abs(x));
+  x = mulrr(x, dbltor(L * (1 + L)));
+  return gerepileuptoleaf(av, x);
+}
+
+GEN
+primes(long n)
+{
+  forprime_t S;
+  long i;
+  GEN y;
+  if (n <= 0) return cgetg(1, t_VEC);
+  y = cgetg(n+1, t_VEC);
+  (void)new_chunk(3*n); /*HACK*/
+  u_forprime_init(&S, 2, ULONG_MAX);
+  avma = (pari_sp)y;
+  for (i = 1; i <= n; i++) gel(y, i) = utoipos( u_forprime_next(&S) );
+  return y;
+}
+GEN
+primes_zv(long n)
+{
+  forprime_t S;
+  long i;
+  GEN y;
+  if (n <= 0) return cgetg(1, t_VECSMALL);
+  y = cgetg(n+1,t_VECSMALL);
+  u_forprime_init(&S, 2, ULONG_MAX);
+  for (i = 1; i <= n; i++) y[i] =  u_forprime_next(&S);
+  avma = (pari_sp)y; return y;
+}
+GEN
+primes0(GEN N)
+{
+  switch(typ(N))
+  {
+    case t_INT: return primes(itos(N));
+    case t_VEC:
+      if (lg(N) == 3) return primes_interval(gel(N,1),gel(N,2));
+  }
+  pari_err_TYPE("primes", N);
+  return NULL;
+}
+
+GEN
+primes_interval(GEN a, GEN b)
+{
+  pari_sp av = avma;
+  forprime_t S;
+  long i, n;
+  GEN y, d, p;
+  if (typ(a) != t_INT) a = gceil(a);
+  if (typ(b) != t_INT) b = gfloor(b);
+  d = subii(b, a);
+  if (signe(d) < 0 || signe(b) <= 0) { avma = av; return cgetg(1, t_VEC); }
+  if (lgefint(b) == 3)
+  {
+    avma = av;
+    y = primes_interval_zv(itou(a), itou(b));
+    n = lg(y); settyp(y, t_VEC);
+    for (i = 1; i < n; i++) gel(y,i) = utoipos(y[i]);
+    return y;
+  }
+  /* at most d+1 primes in [a,b]. If d large, try better bound to lower
+   * memory use */
+  if (cmpiu(d,100000) > 0)
+  {
+    GEN D = gsub(gprimepi_upper_bound(b), gprimepi_lower_bound(a));
+    D = ceil_safe(D);
+    if (cmpii(D, d) < 0) d = D;
+  }
+  n = itos(d)+1;
+  forprime_init(&S, a, b);
+  y = cgetg(n+1, t_VEC); i = 1;
+  while ((p = forprime_next(&S))) gel(y, i++) = icopy(p);
+  setlg(y, i); return gerepileupto(av, y);
+}
+
+/* a <= b, at most d primes in [a,b]. Return them */
+static GEN
+primes_interval_i(ulong a, ulong b, ulong d)
+{
+  ulong p, i = 1, n = d + 1;
+  forprime_t S;
+  GEN y = cgetg(n+1, t_VECSMALL);
+  pari_sp av = avma;
+  u_forprime_init(&S, a, b);
+  while ((p = u_forprime_next(&S))) y[i++] = p;
+  avma = av; setlg(y, i); stackdummy((pari_sp)(y + i), (pari_sp)(y + n+1));
+  return y;
+}
+GEN
+primes_interval_zv(ulong a, ulong b)
+{
+  ulong d;
+  if (!a) return primes_upto_zv(b);
+  if (b < a) return cgetg(1, t_VECSMALL);
+  d = b - a;
+  if (d > 100000UL) d = primepi_upper_bound(b) - primepi_lower_bound(a);
+  return primes_interval_i(a, b, d);
+}
+GEN
+primes_upto_zv(ulong b)
+{
+  ulong d;
+  if (b < 2) return cgetg(1, t_VECSMALL);
+  d = (b > 100000UL)? primepi_upper_bound(b): b;
+  return primes_interval_i(2, b, d);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       PRIVATE PRIME TABLE                         **/
+/**                                                                   **/
+/***********************************************************************/
+/* delete dummy NULL entries */
+static void
+cleanprimetab(GEN T)
+{
+  long i,j, l = lg(T);
+  for (i = j = 1; i < l; i++)
+    if (T[i]) T[j++] = T[i];
+  setlg(T,j);
+}
+/* remove p from T */
+static void
+rmprime(GEN T, GEN p)
+{
+  long i;
+  if (typ(p) != t_INT) pari_err_TYPE("removeprimes",p);
+  i = ZV_search(T, p);
+  if (!i)
+    pari_err_DOMAIN("removeprime","prime","not in",
+                    strtoGENstr("primetable"), p);
+  gunclone(gel(T,i)); gel(T,i) = NULL;
+  cleanprimetab(T);
+}
+
+/*stolen from ZV_union_shallow() : clone entries from y */
+static GEN
+addp_union(GEN x, GEN y)
+{
+  long i, j, k, lx = lg(x), ly = lg(y);
+  GEN z = cgetg(lx + ly - 1, t_VEC);
+  i = j = k = 1;
+  while (i<lx && j<ly)
+  {
+    int s = cmpii(gel(x,i), gel(y,j));
+    if (s < 0)
+      gel(z,k++) = gel(x,i++);
+    else if (s > 0)
+      gel(z,k++) = gclone(gel(y,j++));
+    else {
+      gel(z,k++) = gel(x,i++);
+      j++;
+    }
+  }
+  while (i<lx) gel(z,k++) = gel(x,i++);
+  while (j<ly) gel(z,k++) = gclone(gel(y,j++));
+  setlg(z, k); return z;
+}
+
+/* p is NULL, or a single element or a row vector with "primes" to add to
+ * prime table. */
+static GEN
+addp(GEN *T, GEN p)
+{
+  pari_sp av = avma;
+  long i, l;
+  GEN v;
+
+  if (!p || lg(p) == 1) return *T;
+  if (!is_vec_t(typ(p))) p = mkvec(p);
+
+  RgV_check_ZV(p, "addprimes");
+  v = gen_indexsort_uniq(p, (void*)&cmpii, &cmp_nodata);
+  p = vecpermute(p, v);
+  if (cmpiu(gel(p,1), 2) < 0) pari_err_DOMAIN("addprimes", "p", "<", gen_2,p);
+  p = addp_union(*T, p);
+  l = lg(p);
+  if (l != lg(*T))
+  {
+    GEN old = *T, t = cgetalloc(t_VEC, l);
+    for (i = 1; i < l; i++) gel(t,i) = gel(p,i);
+    *T = t; free(old);
+  }
+  avma = av; return *T;
+}
+GEN
+addprimes(GEN p) { return addp(&primetab, p); }
+
+static GEN
+rmprimes(GEN T, GEN prime)
+{
+  long i,tx;
+
+  if (!prime) return T;
+  tx = typ(prime);
+  if (is_vec_t(tx))
+  {
+    if (prime == T)
+    {
+      for (i=1; i < lg(prime); i++) gunclone(gel(prime,i));
+      setlg(prime, 1);
+    }
+    else
+    {
+      for (i=1; i < lg(prime); i++) rmprime(T, gel(prime,i));
+    }
+    return T;
+  }
+  rmprime(T, prime); return T;
+}
+GEN
+removeprimes(GEN prime) { return rmprimes(primetab, prime); }
diff --git a/src/basemath/qfisom.c b/src/basemath/qfisom.c
new file mode 100644
index 0000000..3d13487
--- /dev/null
+++ b/src/basemath/qfisom.c
@@ -0,0 +1,1796 @@
+/* Copyright (C) 2012  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*To be moved elsewhere */
+
+static long
+Z_trunc(GEN z)
+{
+  long s = signe(z);
+  return s==0 ? 0: (long)(s*mod2BIL(z));
+}
+
+static GEN
+ZV_trunc_to_zv(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) x[i] = Z_trunc(gel(z,i));
+  return x;
+}
+
+/* returns scalar product of vectors x and y with respect to Gram-matrix F */
+static long
+scp(GEN x, GEN F, GEN y)
+{
+  long i, j;
+  ulong sum = 0;
+  long n = lg(F)-1;
+  for (i = 1; i <= n; ++i)
+  {
+    ulong xi = uel(x,i);
+    if (xi)
+    {
+      GEN Fi = gel(F, i);
+      for (j = 1; j <= n; ++j)
+        sum += xi * uel(Fi,j) * uel(y,j);
+    }
+  }
+  return sum;
+}
+
+static GEN
+ZM_trunc_to_zm(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_MAT);
+  for (i=1; i<l; i++) gel(x,i) = ZV_trunc_to_zv(gel(z,i));
+  return x;
+}
+
+static GEN
+zm_divmod(GEN A, GEN B, ulong p)
+{
+  pari_sp av = avma;
+  GEN Ap = zm_to_Flm(A,p), Bp = zm_to_Flm(B,p);
+  GEN C = Flm_center(Flm_mul(Flm_inv(Ap, p), Bp, p), p, p>>1);
+  return gerepileupto(av, C);
+}
+
+
+static int
+zv_canon(GEN V)
+{
+  long l = lg(V), j, k;
+  for (j = 1; j < l  &&  V[j] == 0; ++j);
+  if (j < l  &&  V[j] < 0)
+  {
+    for (k = j; k < l; ++k)
+      V[k] = -V[k];
+    return -1;
+  }
+  return 1;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**      QFAUTO/QFISOM ported from B. Souvignier ISOM program      **/
+/**                                                                **/
+/********************************************************************/
+
+/* This is a port by Bill Allombert of the program ISOM by Bernt Souvignier
+which implement an algorithm published in
+W. PLESKEN, B. SOUVIGNIER, Computing Isometries of Lattices,
+Journal of Symbolic Computation, Volume 24, Issues 3-4, September 1997,
+Pages 327-334, ISSN 0747-7171, 10.1006/jsco.1996.0130.
+(http://www.sciencedirect.com/science/article/pii/S0747717196901303)
+
+We thanks Professor Souvignier for giving us permission to port his code.
+*/
+
+struct group
+{
+  GEN     ord;
+  GEN     ng;
+  GEN     nsg;
+  GEN     g;
+};
+
+struct fingerprint
+{
+  GEN diag;
+  GEN per;
+  GEN e;
+};
+
+struct qfauto
+{
+  long dim;
+  GEN F, V, W, v;
+  ulong p;
+};
+
+struct qfcand
+{
+  long cdep;
+  GEN comb;
+  GEN bacher_pol;
+};
+
+static long
+possible(GEN F, GEN Ftr, GEN V, GEN W, GEN per, long I, long J)
+{
+  long i, j, k, count;
+  long n = lg(W)-1, f = lg(F)-1;
+  count = 0;
+  for (j = 1; j <= n; ++j)
+  {
+    GEN Wj = gel(W,j), Vj = gel(V,j);
+    i = I+1;
+    /* check the length of the vector */
+    for (k = 1; k <= f  &&  i > I  &&  Wj[k] == mael3(F,k,J,J); ++k)
+      /* check the scalar products with the basis-vectors */
+      for (i = 1; i <= I; ++i)
+        if (zv_dotproduct(Vj,gmael(Ftr,k,per[i])) != coeff(gel(F,k),J,per[i]))
+          break;
+    if (k == f+1  &&  i > I)
+      ++count;
+    /* the same for the negative vector */
+    i = I+1;
+    for (k = 1; k <= f  &&  i > I  &&  Wj[k] == mael3(F,k,J,J); ++k)
+      for (i = 1; i <= I ; ++i)
+        if (zv_dotproduct(Vj,gmael(Ftr,k,per[i])) != -coeff(gel(F,k),J,per[i]))
+          break;
+    if (k == f+1  &&  i > I)
+      ++count;
+  }
+  return count;
+}
+
+static void
+fingerprint(struct fingerprint *fp, struct qfauto *qf)
+{
+  pari_sp av;
+  GEN V=qf->V, W=qf->W, F=qf->F;
+  GEN Mf;
+  long i, j, k, min;
+  long dim = qf->dim, n = lg(V)-1, f = lg(F)-1;
+  GEN Ftr;
+  fp->per = identity_perm(dim);
+  fp->e = cgetg(dim+1, t_VECSMALL);
+  fp->diag = cgetg(dim+1, t_VECSMALL);
+  av = avma;
+  Ftr = cgetg(f+1,t_VEC);
+  for (i = 1; i <= f; ++i)
+    gel(Ftr,i) = zm_transpose(gel(F,i));
+  Mf = zero_Flm_copy(dim, dim);
+  /* the first row of the fingerprint has as entry nr. i the number of
+   vectors, which have the same length as the i-th basis-vector with
+   respect to every invariant form */
+  for (j = 1; j <= n; ++j)
+  {
+    GEN Wj = gel(W,j);
+    for (i = 1; i <= dim; ++i)
+    {
+      for (k = 1; k <= f  &&  Wj[k] == mael3(F,k,i,i); ++k);
+      if (k == f+1)
+        mael(Mf,1,i) += 2;
+    }
+  }
+  for (i = 1; i <= dim-1; ++i)
+  {
+    /* a minimal entry != 0 in the i-th row is chosen */
+    min = i;
+    for (j = i+1; j <= dim; ++j)
+    {
+      if (mael(Mf,i,fp->per[j]) < mael(Mf,i,fp->per[min]))
+        min = j;
+    }
+    lswap(fp->per[i],fp->per[min]);
+    /* the column below the minimal entry is set to 0 */
+    for (j = i+1; j <= dim; ++j)
+      mael(Mf,j,fp->per[i]) = 0;
+    /* compute the row i+1 of the fingerprint */
+    for (j = i+1; j <= dim; ++j)
+      mael(Mf,i+1,fp->per[j]) = possible(F, Ftr, V, W, fp->per, i, fp->per[j]);
+  }
+  /* only the diagonal of f will be needed later */
+  for (i = 1; i <= dim; ++i)
+    fp->diag[i] = mael(Mf,i,fp->per[i]);
+  for (i = 1; i <= dim; ++i)
+  {
+    fp->e[i] = vecvecsmall_search(V,vecsmall_ei(dim,fp->per[i]),0);
+    if (!fp->e[i])
+      pari_err_BUG("qfisom, standard basis vector not found");
+  }
+  avma = av;
+}
+
+/* The Bacher-polynomial for v[I] with scalar product S is
+ * defined as follows: let list be the vectors which have the same length as
+ * v[I] and with scalar product S with v[I], for each vector w in list let n_w
+ * be the number of pairs (y,z) of vectors in list, such that all scalar
+ * products between w,y and z are S, then the Bacher-polynomial is the sum over
+ * the w in list of the monomials X^n_w  */
+
+static GEN
+bacher(long I, long S, struct qfauto *qf)
+{
+  pari_sp av=avma;
+  GEN V=qf->V, W=qf->W, Fv=gel(qf->v,1);
+  GEN  list, listxy, counts, vI;
+  long i, j, k, nlist, nxy;
+  long n = lg(V)-1;
+  long sum, mind, maxd;
+  GEN coef;
+
+  /* the Bacher-polynomials of v[I] and -v[I] are equal */
+  I = labs(I);
+  vI = gel(V,I);
+  /* list of vectors that have scalar product S with v[I] */
+  list = zero_Flv(2*n);
+  nlist = 0;
+  for (i = 1; i <= n; ++i)
+    if (mael(W,i,1) == mael(W,I,1))
+    {
+      long s = zv_dotproduct(vI, gel(Fv,i));
+      if (s == S)
+        list[++nlist] = i;
+      if (-s == S)
+        list[++nlist] = -i;
+    }
+  /* there are nlist vectors that have scalar product S with v[I] */
+  sum = nlist;
+  if (nlist==0) retmkvec2(mkvecsmall3(0,0,0),cgetg(1,t_VEC));
+  counts = cgetg(nlist+1, t_VECSMALL);
+  listxy = cgetg(nlist+1, t_VECSMALL);
+  for (i = 1; i <= nlist; ++i)
+  {
+    long S1;
+    /* listxy is the list of the nxy vectors from list that have scalar
+       product S with v[list[i]] */
+    for (j = 1; j <= nlist; ++j)
+      listxy[j] = 0;
+    nxy = 0;
+    S1 = list[i] > 0 ? S : -S;
+    for (j = 1; j <= nlist; ++j)
+    {
+      long S2 = list[j] > 0 ? S1 : -S1;
+      /* note: for i > 0 is v[-i] = -v[i] */
+      if (zv_dotproduct(gel(V,labs(list[i])), gel(Fv,labs(list[j]))) == S2)
+        listxy[++nxy] = list[j];
+    }
+    /* counts[i] is the number of pairs for the vector v[list[i]] */
+    counts[i] = 0;
+    for (j = 1; j <= nxy; ++j)
+    {
+      long S1 = listxy[j] > 0 ? S : -S;
+      for (k = j+1; k <= nxy; ++k)
+      {
+        long S2 = listxy[k] > 0 ? S1 : -S1;
+        long lj = labs(listxy[j]), lk = labs(listxy[k]);
+        if (zv_dotproduct(gel(V,lj), gel(Fv,lk)) == S2)
+          counts[i] += 1;
+      }
+    }
+  }
+   /* maxd is the maximal degree of the Bacher-polynomial,
+      mind the minimal degree */
+  maxd = counts[1];
+  mind = counts[1];
+  for (i = 2; i <= nlist; ++i)
+  {
+    if (counts[i] > maxd)
+      maxd = counts[i];
+    else if (counts[i] < mind)
+      mind = counts[i];
+  }
+  coef = zero_Flv(maxd - mind + 1);
+  for (i = 1; i <= nlist; ++i)
+    coef[1+counts[i] - mind] += 1;
+  if (DEBUGLEVEL)
+    err_printf("mind=%ld maxd=%ld sum=%ld\n",mind,maxd,sum);
+  /* the Bacher-polynomial is now: sum from i=mind to maxd over
+     coef[i - mind] * X^i */
+  return gerepilecopy(av, mkvec2(mkvecsmall3(sum, mind, maxd),coef));
+}
+
+static GEN
+init_bacher(long bachdep, struct fingerprint *fp, struct qfauto *qf)
+{
+  GEN z = cgetg(bachdep+1,t_VEC);
+  long i;
+  for (i=1;i<=bachdep;i++)
+  {
+    long bachscp = mael(qf->W,fp->e[i],1) / 2;
+    gel(z,i) = bacher(fp->e[i], bachscp, qf);
+  }
+  return z;
+}
+
+/* checks, whether the vector v[I] has the Bacher-polynomial pol  */
+static long
+bachcomp(GEN pol, long I, long S, GEN V, GEN W, GEN Fv)
+{
+  pari_sp av = avma;
+  GEN co, list, listxy, vI;
+  long i, j, k;
+  long nlist, nxy, count;
+  long n = lg(V)-1;
+  long sum = mael(pol,1,1), mind = mael(pol,1,2), maxd = mael(pol,1,3);
+  GEN coef = gel(pol,2);
+  I = labs(I);
+  vI = gel(V,I);
+  list = zero_Flv(sum);
+  /* nlist should be equal to pol.sum */
+  nlist = 0;
+  for (i = 1; i <= n  &&  nlist <= sum; ++i)
+  {
+    if (mael(W,i,1) == mael(W,I,1))
+    {
+      long s = zv_dotproduct(vI, gel(Fv,i));
+      if (s == S)
+      {
+        if (nlist < sum)
+          list[nlist+1] = i;
+        nlist++;
+      }
+      if (-s == S)
+      {
+        if (nlist < sum)
+          list[nlist+1] = -i;
+        nlist++;
+      }
+    }
+  }
+  if (nlist != sum)
+  {
+    /* the number of vectors with scalar product S is already different */
+    avma=av; return 0;
+  }
+  if (nlist == 0) { avma=av; return 1; }
+  /* listxy is the list of the nxy vectors from list that have scalar product S
+     with v[list[i]] */
+  listxy = cgetg(nlist+1,t_VECSMALL);
+  co = zero_Flv(maxd - mind + 1);
+  for (i = 1; i <= nlist; ++i)
+  {
+    long S1 = list[i] > 0 ? S : -S;
+    for (j = 1; j <= nlist; ++j)
+      listxy[j] = 0;
+    nxy = 0;
+    for (j = 1; j <= nlist; ++j)
+    {
+      long S2 = list[j] > 0 ? S1 : -S1;
+      if (zv_dotproduct(gel(V,labs(list[i])), gel(Fv,labs(list[j]))) == S2)
+        listxy[++nxy] = list[j];
+    }
+    /* count is the number of pairs */
+    count = 0;
+    for (j = 1; j <= nxy  &&  count <= maxd; ++j)
+    {
+      long S1 = listxy[j] > 0 ? S : -S;
+      for (k = j+1; k <= nxy  &&  count <= maxd; ++k)
+      {
+        long S2 = listxy[k] > 0 ? S1 : -S1;
+        long lj = labs(listxy[j]), lk = labs(listxy[k]);
+        if (zv_dotproduct(gel(V,lj), gel(Fv,lk)) == S2)
+          count++;
+      }
+    }
+    if (count < mind  ||  count > maxd  ||
+        co[count-mind+1] >= coef[count-mind+1])
+    /* if the number of pairs is smaller than pol.mind or larger than pol.maxd
+       or if the coefficient of X^count becomes now larger than the one in pol,
+       then the Bacher-polynomials can not be equal */
+    {
+      avma = av;
+      return 0;
+    }
+    else
+      co[count-mind+1]++;
+  }
+  /* the Bacher-polynomials are equal */
+  avma = av;
+  return 1;
+}
+
+static GEN
+checkvecs(GEN V, GEN F, GEN norm)
+{
+  long i, j, k;
+  long n = lg(V)-1, f = lg(F)-1;
+  GEN W = cgetg(n+1, t_MAT);
+  j = 0;
+  for (i = 1; i <= n; ++i)
+  {
+    GEN normvec = cgetg(f+1, t_VECSMALL);
+    GEN Vi = gel(V,i);
+    for (k = 1; k <= f; ++k)
+      normvec[k] = scp(Vi, gel(F,k), Vi);
+    if (!vecvecsmall_search(norm,normvec,0))
+      ++j;
+    else
+    {
+      gel(V,i-j) = Vi;
+      gel(W,i-j) = normvec;
+    }
+  }
+  setlg(V, n+1-j);
+  setlg(W, n+1-j);
+  return W;
+}
+
+static long
+operate(long nr, GEN A, GEN V)
+{
+  pari_sp av = avma;
+  long im,eps;
+  GEN w = zm_zc_mul(A,gel(V,labs(nr)));
+  eps = zv_canon(w);
+  if (nr < 0) eps = -eps; /* -w */
+  im = vecvecsmall_search(V,w,0);
+  if (!im) pari_err_BUG("qfauto, image of vector not found");
+  avma = av;
+  return eps*im;
+}
+
+static GEN
+orbit(GEN pt, long ipt, long npt, GEN H, GEN V)
+{
+  long i, cnd, im;
+  long n = lg(V)-1, nH = lg(H)-1, norb = npt+16, no = npt;
+  GEN flag = zero_Flv(2*n+1)+n+1; /*We need negative indices*/
+  pari_sp av = avma;
+  GEN orb = cgetg(norb+1,t_VECSMALL);
+  for (i = 1; i <= npt; ++i)
+  {
+    orb[i] = pt[ipt+i];
+    flag[pt[ipt+i]] = 1;
+  }
+  for (cnd=1; cnd <= no; ++cnd)
+    for (i = 1; i <= nH; ++i)
+    {
+      im = operate(orb[cnd], gel(H,i), V);
+      if (flag[im] == 0)
+        /* the image is a new point in the orbit */
+      {
+        if (no==norb)
+        {
+          norb<<=1;
+          orb = gerepileuptoleaf(av, vecsmall_lengthen(orb, norb));
+        }
+        orb[++no] = im;
+        flag[im] = 1;
+      }
+    }
+  setlg(orb,no+1); return orb;
+}
+
+/* return the length of the orbit of pt under the first nG matrices in G */
+
+static long
+orbitlen(long pt, long orblen, GEN G, long nG, GEN V)
+{
+  pari_sp av = avma;
+  long i, len, cnd;
+  long n = lg(V)-1;
+  GEN orb, flag;
+  /* if flag[i + n+1] = 1, -n <= i <= n, then the point i is already in the
+     orbit */
+  flag = zero_Flv(2*n + 1)+n+1;
+  orb  = zero_Flv(orblen);
+  orb[1] = pt;
+  flag[pt] = 1;
+  len = 1;
+  for(cnd = 1; cnd <= len  &&  len < orblen; ++cnd)
+    for (i = 1; i <= nG  &&  len < orblen; ++i)
+    {
+      long im = operate(orb[cnd], gel(G,i), V);
+      if (flag[im] == 0)
+        /* the image is a new point in the orbit */
+      {
+        orb[++len] = im;
+        flag[im] = 1;
+      }
+    }
+  avma = av;
+  return len;
+}
+
+/* delete the elements in orb2 from orb1, an entry 0 marks the end of the
+ * list, returns the length of orb1 */
+
+static long
+orbdelete(GEN orb1, GEN orb2)
+{
+  long i, j, len;
+  long l1 = lg(orb1)-1;
+  long l2 = lg(orb2)-1;
+  for (i = 1; i <= l1  &&  orb1[i] != 0; ++i);
+  len = i - 1;
+  for (i = 1; i <= l2  &&  orb2[i] != 0; ++i)
+  {
+    long o2i = orb2[i];
+    for (j = 1; j <= len  &&  orb1[j] != o2i; ++j);
+    /* orb1[j] = orb2[i], hence delete orb1[j] from orb1 */
+    if (j <= len)
+    {
+      orb1[j] = orb1[len];
+      orb1[len--] = 0;
+    }
+  }
+  return len;
+}
+
+static long
+orbsubtract(GEN Cs, GEN pt, long ipt, long npt, GEN H, GEN V, long *len)
+{
+  pari_sp av = avma;
+  long nC;
+  GEN orb = orbit(pt, ipt, npt, H, V);
+  if (len) *len = lg(orb)-1;
+  nC = orbdelete(Cs, orb);
+  avma = av; return nC;
+}
+
+/* Generates the matrix X which has as row per[i] the vector nr. x[i] from the
+ * list V */
+
+static GEN
+matgen(GEN x, GEN per, GEN V)
+{
+  long i, j;
+  long dim = lg(x)-1;
+  GEN X = cgetg(dim+1,t_MAT);
+  for (i = 1; i <= dim; ++i)
+  {
+    long xi = x[i];
+    GEN Xp = cgetg(dim+1,t_VECSMALL);
+    for (j = 1; j <= dim; ++j)
+      Xp[j] = xi > 0? mael(V,xi,j): -mael(V,-xi,j);
+    gel(X,per[i]) = Xp;
+  }
+  return X;
+}
+/* x1 corresponds to an element X1 mapping some vector e on p1, x2 to an
+ * element X2 mapping e on p2 and G is a generator mapping p1 on p2, then
+ * S = X1*G*X2^-1 stabilizes e
+ */
+
+static GEN
+stabil(GEN x1, GEN x2, GEN per, GEN G, GEN V, ulong p)
+{
+  pari_sp av = avma;
+  long i;
+  GEN x, XG, X2;
+  long dim = lg(x1)-1;
+  x = cgetg(dim+1,t_VECSMALL);
+  for (i = 1; i <= dim; ++i)
+    x[i] = operate(x1[i], G, V);
+  /* XG is the composite mapping of the matrix corresponding to x1 and G */
+  XG = matgen(x, per, V);
+  X2 = matgen(x2, per, V);
+  return gerepileupto(av,zm_divmod(X2,XG,p));
+}
+
+/* computes the orbit of fp.e[I] under the generators in G->g[I]...G->g[n-1]
+ * and elements stabilizing fp.e[I], has some heuristic break conditions, the
+ * generators in G->g[i] stabilize fp.e[0]...fp.e[i-1] but not fp.e[i],
+ * G->ng[i] is the number of generators in G->g[i], the first G->nsg[i] of
+ * which are elements which are obtained as stabilizer elements in
+ * <G->g[0],...,G->g[i-1]>, G->ord[i] is the orbit length of fp.e[i] under
+ * <G->g[i],...,G->g[n-1]> */
+
+static void
+stab(long I, struct group *G, struct fingerprint *fp, GEN V, ulong p)
+{
+  long len, cnd, tmplen;
+  GEN orb, w, flag, H, Hj, S;
+  long i, j, k, l, im, nH, nHj, fail;
+  long Maxfail, Rest;
+  long dim = lg(fp->diag)-1, n = lg(V)-1;
+  /* Some heuristic break conditions for the computation of stabilizer elements:
+     it would be too expensive to calculate all the stabilizer generators,
+     which are obtained from the orbit, since this is highly redundant, on the
+     other hand every new generator which enlarges the group is much cheaper
+     than one obtained from the backtrack, after Maxfail subsequent stabilizer
+     elements, that do not enlarge the group, Rest more elements are calculated
+     even if they leave the group unchanged, since it turned out that this is
+     often useful in the following steps, increasing the parameters will
+     possibly decrease the number of generators for the group, but will
+     increase the running time, there is no magic behind this heuristic, tuning
+     might be appropriate */
+
+  for (Rest = 0, i = I; i <= dim; ++i)
+    if (fp->diag[i] > 1  &&  G->ord[i] < fp->diag[i])
+      ++Rest;
+  for (Maxfail = Rest, i = 1; i <= dim; ++i)
+    if (fp->diag[i] > 1)
+      ++Maxfail;
+  for (nH = 0, i = I; i <= dim; ++i)
+    nH += G->ng[i];
+  /* H are the generators of the group in which the stabilizer is computed */
+  H = cgetg(nH+1,t_MAT);
+  Hj = cgetg(nH+2,t_MAT);
+  k = 0;
+  for (i = I; i <= dim; ++i)
+    for (j = 1; j <= G->ng[i]; ++j)
+      gel(H,++k) = gmael(G->g,i,j);
+  /* in w[V.n+i] an element is stored that maps fp.e[I] on v[i] */
+  w = cgetg(2*n+2,t_VEC);
+  /* orb contains the orbit of fp.e[I] */
+  orb = zero_Flv(2*n);
+  /* if flag[i + V.n] = 1, then the point i is already in the orbit */
+  flag = zero_Flv(2*n+1);
+  orb[1] = fp->e[I];
+  flag[orb[1]+n+1] = 1;
+  gel(w,orb[1]+n+1) = cgetg(dim+1,t_VECSMALL);
+  for (i = 1; i <= dim; ++i)
+    mael(w,orb[1]+n+1,i) = fp->e[i];
+  cnd = 1;
+  len = 1;
+  /* fail is the number of successive failures */
+  fail = 0;
+  while (cnd <= len && fail < Maxfail+Rest)
+  {
+    for (i = 1; i <= nH  &&  fail < Maxfail+Rest; ++i)
+    {
+      if (fail >= Maxfail)
+        /* there have already been Maxfail successive failures, now a random
+           generator is applied to a random point of the orbit to get Rest more
+           stabilizer elements */
+      {
+        cnd = 1+(long)(pari_rand() % len);
+        i = 1+(long)(pari_rand() % nH);
+      }
+      im = operate(orb[cnd], gel(H,i), V);
+      if (flag[im+n+1] == 0)
+        /* a new element is found, appended to the orbit and an element mapping
+           fp.e[I] to im is stored in w[im+V.n] */
+      {
+        GEN wim;
+        orb[++len] = im;
+        flag[im+n+1] = 1;
+        wim = cgetg(dim+1,t_VECSMALL);
+        gel(w,im+n+1) = wim;
+        for (j = 1; j <= dim; ++j)
+          wim[j] = operate(mael(w,orb[cnd]+n+1,j), gel(H,i), V);
+      }
+      else
+        /* the image was already in the orbit */
+      {
+        /* j is the first index where the images of the old and the new element
+           mapping e[I] on im differ */
+        for (j = I; j <= dim; j++)
+          if (operate(mael(w,orb[cnd]+n+1,j), gel(H,i), V) != mael(w,im+n+1,j))
+            break;
+        if (j <= dim  &&
+            (G->ord[j] < fp->diag[j] || fail >= Maxfail))
+        {
+          GEN wo = gel(w,orb[cnd]+n+1);
+      /* new stabilizer element S = w[orb[cnd]+V.n] * H[i] * (w[im+V.n])^-1 */
+          S = stabil(wo, gel(w,im+n+1), fp->per, gel(H,i), V, p);
+          gel(Hj,1) = S;
+          nHj = 1;
+          for (k = j; k <= dim; ++k)
+            for (l = 1; l <= G->ng[k]; ++l)
+              gel(Hj,++nHj) = gmael(G->g,k,l);
+          tmplen = orbitlen(fp->e[j], fp->diag[j], Hj, nHj, V);
+          if (tmplen > G->ord[j]  ||  fail >= Maxfail)
+            /* the new stabilizer element S either enlarges the orbit of e[j]
+               or it is one of the additional elements after MAXFAIL failures */
+          {
+            GEN Ggj;
+            G->ord[j] = tmplen;
+            G->ng[j]++;
+            G->nsg[j]++;
+            /* allocate memory for the new generator */
+            gel(G->g,j) = vec_lengthen(gel(G->g,j),G->ng[j]);
+            Ggj = gel(G->g,j);
+            /* the new generator is inserted as stabilizer element
+               nr. nsg[j]-1 */
+            for (k = G->ng[j]; k > G->nsg[j]; --k)
+              gel(Ggj,k) = gel(Ggj,k-1);
+            gel(Ggj,G->nsg[j]) = S;
+            nH++;
+            H = vec_lengthen(H, nH);
+            Hj = vec_lengthen(Hj, nH+1);
+            /* the new generator is appended to H */
+            gel(H,nH) = gel(Ggj,G->nsg[j]);
+            /* the number of failures is reset to 0 */
+            if (fail < Maxfail)
+              fail = 0;
+            else
+              ++fail;
+          }
+          else
+            /*the new stabilizer element S does not enlarge the orbit of e[j]*/
+            ++fail;
+        }
+        else if ((j < dim  &&  fail < Maxfail)  ||
+            (j == dim  &&  fail >= Maxfail))
+          ++fail;
+        /* if S is the identity and fail < Maxfail, nothing is done */
+      }
+    }
+    if (fail < Maxfail)
+      ++cnd;
+  }
+}
+
+/* tests, whether x[1],...,x[I-1] is a partial automorphism, using scalar
+ * product combinations and Bacher-polynomials depending on the chosen options,
+ * puts the candidates for x[I] (i.e. the vectors vec such that the scalar
+ * products of x[1],...,x[I-1],vec are correct) on CI, returns their number
+ * (should be fp.diag[I]) */
+
+static long
+qfisom_candidates_novec(GEN CI, long I, GEN x, struct qfauto *qf,
+       struct qfauto *qff, struct fingerprint *fp)
+{
+  pari_sp av = avma;
+  long i, j, k, okp, okm, nr, fail;
+  GEN vec;
+  GEN F =qf->F, V=qff->V, W=qff->W, v=qff->v;
+  long n = lg(V)-1, f = lg(F)-1;
+  vec = cgetg(I,t_VECSMALL);
+  /* CI is the list for the candidates */
+  for (i = 1; i <= fp->diag[I]; ++i)
+    CI[i] = 0;
+  nr = 0;
+  fail = 0;
+  for (j = 1; j <= n  &&  fail == 0; ++j)
+  {
+    GEN Vj = gel(V,j), Wj = gel(W, j);
+    okp = 0;
+    okm = 0;
+    for (i = 1; i <= f; ++i)
+    {
+      GEN FAiI = gmael(F,i,fp->per[I]);
+      GEN FFvi = gel(v,i);
+      /* vec is the vector of scalar products of V.v[j] with the first I base
+         vectors x[0]...x[I-1] */
+      for (k = 1; k < I; ++k)
+      {
+        long xk = x[k];
+        if (xk > 0)
+          vec[k] = zv_dotproduct(Vj, gel(FFvi,xk));
+        else
+          vec[k] = -zv_dotproduct(Vj, gel(FFvi,-xk));
+      }
+      for (k = 1; k < I  &&  vec[k] == FAiI[fp->per[k]]; ++k);
+      if (k == I  &&  Wj[i] == FAiI[fp->per[I]])
+        /* V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
+        ++okp;
+      for (k = 1; k < I  &&  vec[k] == -FAiI[fp->per[k]]; ++k);
+      if (k == I  &&  Wj[i] == FAiI[fp->per[I]])
+        /* -V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
+        ++okm;
+    }
+    if (okp == f)
+      /* V.v[j] is a candidate for x[I] */
+    {
+      if (nr < fp->diag[I])
+        CI[++nr] = j;
+      else
+        /* there are too many candidates */
+        fail = 1;
+    }
+    if (okm == f)
+      /* -V.v[j] is a candidate for x[I] */
+    {
+      if (nr < fp->diag[I])
+        CI[++nr] = -j;
+      else
+        /* there are too many candidates */
+        fail = 1;
+    }
+  }
+  if (fail == 1)
+    nr = 0;
+  avma = av;
+  return nr;
+}
+
+static long
+qfisom_candidates(GEN CI, long I, GEN x, struct qfauto *qf,
+      struct qfauto *qff, struct fingerprint *fp, struct qfcand *qfcand)
+{
+  pari_sp av = avma;
+  long i, j, k, okp, okm, nr, fail;
+  GEN vec;
+  GEN xvec, xbase, Fxbase, scpvec;
+  long vj, rank, num;
+  GEN com, list, trans, ccoef, cF;
+  GEN F =qf->F, V=qff->V, W=qff->W, v=qff->v, FF= qff->F;
+  long dim = qf->dim, n = lg(V)-1, f = lg(F)-1;
+  long nc;
+  long DEP = qfcand->cdep, len = f * DEP;
+  if (I >= 2  &&  I <= lg(qfcand->bacher_pol))
+  {
+    long BACHSCP = mael(W,labs(x[I-1]),1) / 2;
+    GEN bpolI = gel(qfcand->bacher_pol,I-1);
+    if (bachcomp(bpolI, x[I-1], BACHSCP, V, W, gel(v,1)) == 0)
+      return 0;
+  }
+  if (I==1 || DEP ==0)
+    return qfisom_candidates_novec(CI,I,x,qf,qff,fp);
+  vec = cgetg(I,t_VECSMALL);
+  scpvec = cgetg(len+1,t_VECSMALL);
+  com = gel(qfcand->comb,I-1);
+  list=gel(com,1); trans = gel(com,2); ccoef = gel(com,3); cF=gel(com,4);
+  rank = lg(trans)-1;
+  nc = lg(list)-2;
+  /* xvec is the list of vector sums which are computed with respect to the
+     partial basis in x */
+  xvec = zero_Flm_copy(dim,nc+1);
+  /* xbase should be a basis for the lattice generated by the vectors in xvec,
+     it is obtained via the transformation matrix comb[I-1].trans */
+  xbase = cgetg(rank+1,t_MAT);
+  for (i = 1; i <= rank; ++i)
+    gel(xbase,i) = cgetg(dim+1,t_VECSMALL);
+  /* Fxbase is the product of a form F with the base xbase */
+  Fxbase = cgetg(rank+1,t_MAT);
+  for (i = 1; i <= rank; ++i)
+    gel(Fxbase,i) = cgetg(dim+1,t_VECSMALL);
+  /* CI is the list for the candidates */
+  for (i = 1; i <= fp->diag[I]; ++i)
+    CI[i] = 0;
+  nr = 0;
+  fail = 0;
+  for (j = 1; j <= n  &&  fail == 0; ++j)
+  {
+    long sign;
+    GEN Vj = gel(V,j), Wj = gel(W, j);
+    okp = 0;
+    okm = 0;
+    for (k = 1; k <= len; ++k)
+      scpvec[k] = 0;
+    for (i = 1; i <= f; ++i)
+    {
+      GEN FAiI = gmael(F,i,fp->per[I]);
+      GEN FFvi = gel(v,i);
+      /* vec is the vector of scalar products of V.v[j] with the first I base
+         vectors x[0]...x[I-1] */
+      for (k = 1; k < I; ++k)
+      {
+        long xk = x[k];
+        if (xk > 0)
+          vec[k] = zv_dotproduct(Vj, gel(FFvi,xk));
+        else
+          vec[k] = -zv_dotproduct(Vj, gel(FFvi,-xk));
+      }
+      for (k = 1; k < I  &&  vec[k] == FAiI[fp->per[k]]; ++k);
+      if (k == I  &&  Wj[i] == FAiI[fp->per[I]])
+        /* V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
+        ++okp;
+      for (k = 1; k < I  &&  vec[k] == -FAiI[fp->per[k]]; ++k);
+      if (k == I  &&  Wj[i] == FAiI[fp->per[I]])
+        /* -V.v[j] is a candidate for x[I] with respect to the form F.A[i] */
+        ++okm;
+      for (k = I-1; k >= 1  &&  k > I-1-DEP; --k)
+        scpvec[(i-1)*DEP+I-k] = vec[k];
+    }
+    /* check, whether the scalar product combination scpvec is contained in the
+       list comb[I-1].list */
+    if (!zv_equal0(scpvec))
+    {
+      sign = zv_canon(scpvec);
+      num = vecvecsmall_search(list,scpvec,0);
+      if (!num)
+        /* scpvec is not found, hence x[0]...x[I-1] is not a partial
+           automorphism */
+        fail = 1;
+      else
+        /* scpvec is found and the vector is added to the corresponding
+           vector sum */
+      {
+        GEN xnum = gel(xvec,num);
+        for (k = 1; k <= dim; ++k)
+          xnum[k] += sign * Vj[k];
+      }
+    }
+    if (okp == f)
+      /* V.v[j] is a candidate for x[I] */
+    {
+      if (nr < fp->diag[I])
+        CI[++nr] = j;
+      else
+        /* there are too many candidates */
+        fail = 1;
+    }
+    if (okm == f)
+      /* -V.v[j] is a candidate for x[I] */
+    {
+      if (nr < fp->diag[I])
+        CI[++nr] = -j;
+      else
+        /* there are too many candidates */
+        fail = 1;
+    }
+  }
+  if (fail == 1)
+    nr = 0;
+  if (nr == fp->diag[I])
+  {
+    /* compute the basis of the lattice generated by the vectors in xvec via
+       the transformation matrix comb[I-1].trans */
+    for (i = 1; i <= rank; ++i)
+    {
+      GEN comtri = gel(trans,i);
+      for (j = 1; j <= dim; ++j)
+      {
+        long xbij = 0;
+        for (k = 1; k <= nc+1; ++k)
+          xbij += comtri[k] * mael(xvec,k,j);
+        mael(xbase,i,j) = xbij;
+      }
+    }
+    /* check, whether the base xbase has the right scalar products */
+    for (i = 1; i <= f &&  nr > 0; ++i)
+    {
+      for (j = 1; j <= rank; ++j)
+        for (k = 1; k <= dim; ++k)
+          mael(Fxbase,j,k) = zv_dotproduct(gmael(FF,i,k), gel(xbase,j));
+      for (j = 1; j <= rank  &&  nr > 0; ++j)
+        for (k = 1; k <= j  &&  nr > 0; ++k)
+        {
+          if (zv_dotproduct(gel(xbase,j), gel(Fxbase,k)) != mael3(cF,i,j,k))
+            /* a scalar product is wrong */
+            nr = 0;
+        }
+    }
+    for (i = 1; i <= nc+1  &&  nr > 0; ++i)
+    {
+      GEN comcoi = gel(ccoef,i);
+      for (j = 1; j <= dim && nr > 0; ++j)
+      {
+        vj = 0;
+        for (k = 1; k <= rank; ++k)
+          vj += comcoi[k] * mael(xbase,k,j);
+        if (vj != mael(xvec,i,j))
+          /* an entry is wrong */
+          nr = 0;
+      }
+    }
+  }
+  avma = av;
+  return nr;
+}
+
+static long
+aut(long step, GEN x, GEN C, struct group *G, struct qfauto *qf,
+                             struct fingerprint *fp, struct qfcand *cand)
+{
+  long dim = qf->dim;
+  GEN orb = cgetg(2,t_VECSMALL);
+  while (mael(C,step,1) != 0)
+  {
+    if (step < dim)
+    {
+      long nbc;
+      /* choose the image of the base-vector nr. step */
+      gel(x,step) = gmael(C,step,1);
+      /* check, whether x[0]...x[step] is a partial automorphism and compute
+         the candidates for x[step+1] */
+      nbc = qfisom_candidates(gel(C,step+1), step+1, x, qf, qf, fp, cand);
+      if (nbc == fp->diag[step+1])
+        /* go deeper into the recursion */
+        if (aut(step+1, x, C, G, qf, fp, cand))
+          return 1;
+      orb[1] = x[step];
+      /* delete the chosen vector from the list of candidates */
+      (void)orbdelete(gel(C,step), orb);
+    }
+    else
+    {
+      /* a new automorphism is found */
+      gel(x,dim) = gmael(C,dim,1);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/* search new automorphisms until on all levels representatives for all orbits
+ * have been tested */
+
+static void
+autom(struct group *G, struct qfauto *qf, struct fingerprint *fp,
+                                          struct qfcand *cand)
+{
+  long i, j, step, im, nC, nH, found, tries;
+  GEN  x, bad, H;
+  long nbad;
+  GEN V = qf->V;
+  long dim = qf->dim, n = lg(V)-1;
+  long STAB = 1;
+  GEN C = cgetg(dim+1,t_VEC);
+  /* C[i] is the list of candidates for the image of the i-th base-vector */
+  for (i = 1; i <= dim; ++i)
+    gel(C,i) = cgetg(fp->diag[i]+1, t_VECSMALL);
+  /* x is the new base i.e. x[i] is the index in V.v of the i-th base-vector */
+  x = cgetg(dim+1, t_VECSMALL);
+  for (step = STAB; step <= dim; ++step)
+  {
+    if(DEBUGLEVEL) err_printf("QFAuto: Step %ld/%ld\n",step,dim);
+    nH = 0;
+    for (nH = 0, i = step; i <= dim; ++i)
+      nH += G->ng[i];
+    H = cgetg(nH+1,t_VEC);
+    for (nH = 0, i = step; i <= dim; ++i)
+      for (j = 1; j <= G->ng[i]; ++j)
+        gel(H,++nH) = gmael(G->g,i,j);
+    bad = zero_Flv(2*n);
+    nbad = 0;
+    /* the first step base-vectors are fixed */
+    for (i = 1; i < step; ++i)
+      x[i] = fp->e[i];
+    /* compute the candidates for x[step] */
+    if (fp->diag[step] > 1)
+      /* if fp.diag[step] > 1 compute the candidates for x[step] */
+      nC = qfisom_candidates(gel(C,step), step, x, qf, qf, fp, cand);
+    else
+      /* if fp.diag[step] == 1, fp.e[step] is the only candidate */
+    {
+      mael(C,step,1) = fp->e[step];
+      nC = 1;
+    }
+    /* delete the orbit of the step-th base-vector from the candidates */
+    nC = orbsubtract(gel(C,step), fp->e, step-1, 1, H, V, &(G->ord[step]));
+    while (nC > 0  &&  (im = mael(C,step,1)) != 0)
+    {
+      found = 0;
+      /* tries vector V.v[im] as image of the step-th base-vector */
+      x[step] = im;
+      if (step < dim)
+      {
+        long nbc;
+        /* check, whether x[0]...x[step] is a partial basis and compute the
+           candidates for x[step+1] */
+        nbc = qfisom_candidates(gel(C,step+1), step+1, x, qf, qf, fp, cand);
+        if (nbc == fp->diag[step+1])
+          /* go into the recursion */
+          found = aut(step+1, x, C, G, qf, fp, cand);
+        else
+          found = 0;
+      }
+      else
+        found = 1;
+      if (found == 0)
+        /* x[0]...x[step] can not be continued to an automorphism */
+      {
+        /* delete the orbit of im from the candidates for x[step] */
+        nC = orbsubtract(gel(C,step),mkvecsmall(im), 0, 1, H, V, NULL);
+        bad[++nbad] = im;
+      }
+      else
+        /* a new generator has been found */
+      {
+        GEN Gstep;
+        ++G->ng[step];
+        /* append the new generator to G->g[step] */
+        Gstep = vec_lengthen(gel(G->g,step),G->ng[step]);
+        gel(Gstep,G->ng[step]) = matgen(x, fp->per, V);
+        gel(G->g,step) = Gstep;
+        ++nH;
+        H = cgetg(nH+1, t_VEC);
+        for (nH = 0, i = step; i <= dim; ++i)
+          for (j = 1; j <= G->ng[i]; ++j)
+            gel(H,++nH) = gmael(G->g,i,j);
+        nC = orbsubtract(gel(C,step), fp->e, step-1, 1, H, V, &(G->ord[step]));
+        nC = orbsubtract(gel(C,step), bad, 0, nbad, H, V, NULL);
+      }
+    }
+    /* test, whether on step STAB some generators may be omitted */
+    if (step == STAB)
+      for (tries = G->nsg[step]; tries <= G->ng[step]; ++tries)
+      {
+        nH = 0;
+        for (j = 1; j < tries; ++j)
+          gel(H,++nH) = gmael(G->g,step,j);
+        for (j = tries+1; j < G->ng[step]; ++j)
+          gel(H,++nH) = gmael(G->g,step,j);
+        for (i = step+1; i <= dim; ++i)
+          for (j = 1; j <= G->ng[i]; ++j)
+            gel(H,++nH) = gmael(G->g,i,j);
+        if (orbitlen(fp->e[step], G->ord[step], H, nH, V) == G->ord[step])
+          /* the generator g[step][tries] can be omitted */
+        {
+          G->ng[step]--;
+          for (i = tries; i < G->ng[step]; ++i)
+            gmael(G->g,step,i) = gmael(G->g,step,i+1);
+          tries--;
+        }
+      }
+    if (step < dim  &&  G->ord[step] > 1)
+      /* calculate stabilizer elements fixing the basis-vectors
+         fp.e[0]...fp.e[step] */
+      stab(step, G, fp, V, qf->p);
+  }
+}
+
+#define MAXENTRY (1L<<((BITS_IN_LONG-2)>>1))
+#define MAXNORM (1L<<(BITS_IN_LONG-2))
+
+static long
+zm_maxdiag(GEN A)
+{
+  long dim = lg(A)-1;
+  long max = coeff(A,1,1);
+  long i;
+  for (i = 2; i <= dim; ++i)
+    if (coeff(A,i,i) > max)
+      max = coeff(A,i,i);
+  return max;
+}
+
+static GEN
+init_qfauto(GEN F, long max, struct qfauto *qf, GEN norm)
+{
+  long i, j, k;
+  GEN W, v;
+  GEN M = minim(zm_to_ZM(gel(F,1)), stoi(max), NULL);
+  GEN V = ZM_to_zm(gel(M, 3));
+  long n = lg(V)-1, f = lg(F)-1, dim = lg(gel(F,1))-1;
+  for (i = 1; i <= n; ++i)
+  {
+    GEN Vi = gel(V,i);
+    if (typ(Vi)!=t_VECSMALL)
+      pari_err_TYPE("init_qfauto",Vi);
+    zv_canon(Vi);
+    for (k = 1; k <= dim; ++k)
+    {
+      long l = labs(Vi[k]);
+      if (l > max)
+        max = l;
+    }
+  }
+  if (max > MAXENTRY) pari_err_OVERFLOW("qfisom [lattice too large]");
+  qf->p = unextprime(2*max+1);
+  V = vecvecsmall_sort_uniq(V);
+  if (!norm)
+  {
+    norm = cgetg(dim+1,t_VEC);
+    for (i = 1; i <= dim; ++i)
+    {
+      GEN Ni = cgetg(f+1,t_VECSMALL);
+      for (k = 1; k <= f; ++k)
+        Ni[k] = mael3(F,k,i,i);
+      gel(norm,i) = Ni;
+    }
+    norm = vecvecsmall_sort_uniq(norm);
+  }
+  W = checkvecs(V, F, norm);
+  v = cgetg(f+1,t_VEC);
+  /* the product of the maximal entry in the short vectors with the maximal
+     entry in v[i] should not exceed MAXNORM to avoid overflow */
+  max = MAXNORM / max;
+  for (i = 1; i <= f; ++i)
+  {
+    GEN Fi = gel(F,i), vi;
+    vi = cgetg(n+1,t_MAT);
+    gel(v,i) = vi;
+    for (j = 1; j <= n; ++j)
+    {
+      GEN Vj = gel(V,j);
+      GEN vij = cgetg(dim+1, t_VECSMALL);
+      gel(vi,j) = vij;
+      for (k = 1; k <= dim; ++k)
+      {
+        vij[k] = zv_dotproduct(gel(Fi,k), Vj);
+        if (labs(vij[k]) > max) pari_err_OVERFLOW("qfisom [lattice too large]");
+      }
+    }
+  }
+  qf->dim = dim; qf->F = F; qf->V = V; qf->W = W; qf->v = v;
+  return norm;
+}
+
+static void
+init_qfgroup(struct group *G, struct fingerprint *fp, struct qfauto *qf)
+{
+  GEN H, M, V = qf->V;
+  long nH;
+  long i, j, k;
+  long dim = qf->dim;
+  G->ng  = zero_Flv(dim+1);
+  G->nsg = zero_Flv(dim+1);
+  G->ord = cgetg(dim+1,t_VECSMALL);
+  G->g = cgetg(dim+1,t_VEC);
+  for (i = 1; i <= dim; ++i)
+    gel(G->g,i) = mkvec(gen_0);
+  M = matid_Flm(dim);
+  gmael(G->g,1,1) = M;
+  G->ng[1] = 1;
+  /* -Id is always an automorphism */
+  for (i = 1; i <= dim; ++i)
+    mael(M,i,i) = -1;
+  nH = 0;
+  for (i = 1; i <= dim; ++i)
+    nH += G->ng[i];
+  H = cgetg(nH+1,t_MAT);
+  /* calculate the orbit lengths under the automorphisms known so far */
+  for (i = 1; i <= dim; ++i)
+  {
+    if (G->ng[i] > 0)
+    {
+      nH = 0;
+      for (j = i; j <= dim; ++j)
+        for (k = 1; k <= G->ng[j]; ++k)
+          gel(H,++nH) = gmael(G->g,j,k);
+      G->ord[i] = orbitlen(fp->e[i], fp->diag[i], H, nH, V);
+    }
+    else
+      G->ord[i] = 1;
+  }
+}
+
+/* calculates the scalar products of the vector w with the base vectors
+ * v[b[I]] down to v[b[I-dep+1]] with respect to all invariant forms and puts
+ * them on scpvec  */
+static GEN
+scpvector(GEN w, GEN b, long I, long dep, GEN v)
+{
+  long  i, j, n = lg(v)-1;
+  GEN scpvec = zero_Flv(dep*n);
+  for (i = I; i >= 1  &&  i > I-dep; --i)
+  {
+    long bi = b[i];
+    if (bi > 0)
+      for (j = 1; j <= n; ++j)
+        scpvec[1+(j-1)*dep + I-i] = zv_dotproduct(w, gmael(v,j,bi));
+    else
+      for (j = 1; j <= n; ++j)
+        scpvec[1+(j-1)*dep + I-i] = -zv_dotproduct(w, gmael(v,j,-bi));
+  }
+  return scpvec;
+}
+
+/* computes the list of scalar product combinations of the vectors
+ * in V.v with the basis-vectors in b */
+static GEN
+scpvecs(GEN *pt_vec, long I, GEN b, long dep, struct qfauto *qf)
+{
+  long  i, j, nr, sign;
+  GEN list, vec;
+  GEN vecnr;
+  GEN V = qf->V, F = qf->F, v = qf->v;
+  long n = lg(V)-1;
+  long dim = lg(gel(F,1))-1;
+  long len = (lg(F)-1)*dep;
+  /* the first vector in the list is the 0-vector and is not counted */
+  list = mkmat(zero_Flv(len));
+  vec  = mkmat(zero_Flv(dim));
+  for (j = 1; j <= n; ++j)
+  {
+    GEN Vvj = gel(V,j);
+    GEN scpvec = scpvector(Vvj, b, I, dep, v);
+    if (zv_equal0(scpvec))
+      nr = -1;
+    else
+    {
+      sign = zv_canon(scpvec);
+      nr = vecvecsmall_search(list,scpvec,0);
+    }
+    /* scpvec is already in list */
+    if (nr > 0)
+    {
+      vecnr = gel(vec,nr);
+      for (i = 1; i <= dim; ++i)
+        vecnr[i] += sign * Vvj[i];
+    }
+    /* scpvec is a new scalar product combination */
+    else if (nr==0)
+    {
+      nr = vecvecsmall_search(list,scpvec,1);
+      list=vec_insert(list,nr,scpvec);
+      vec=vec_insert(vec,nr,sign < 0 ? zv_neg(Vvj) : zv_copy(Vvj));
+    }
+  }
+  settyp(list,t_MAT);
+  *pt_vec = vec;
+  return list;
+}
+
+/* com->F[i] is the Gram-matrix of the basis b with respect to F.A[i] */
+static GEN
+scpforms(GEN b, struct qfauto *qf)
+{
+  long i, j, k;
+  GEN F = qf->F;
+  long n = lg(F)-1, dim = lg(gel(F,1))-1;
+  long nb = lg(b)-1;
+  GEN gram = cgetg(n+1, t_VEC);
+  /* Fbi is the list of products of F.A[i] with the vectors in b */
+  GEN Fbi = cgetg(nb+1, t_MAT);
+  for (j = 1; j <= nb; ++j)
+    gel(Fbi, j) = cgetg(dim+1, t_VECSMALL);
+  for (i = 1; i <= n; ++i)
+  {
+    GEN FAi = gel(F,i);
+    gel(gram, i) = cgetg(nb+1, t_MAT);
+    for (j = 1; j <= nb; ++j)
+      for (k = 1; k <= dim; ++k)
+        mael(Fbi,j,k) = zv_dotproduct(gel(FAi,k), gel(b,j));
+    for (j = 1; j <= nb; ++j)
+    {
+      GEN comFij = cgetg(nb+1, t_VECSMALL);
+      for (k = 1; k <= nb; ++k)
+        comFij[k] = zv_dotproduct(gel(b,j), gel(Fbi,k));
+      gmael(gram,i,j) = comFij;
+    }
+  }
+  return gram;
+}
+
+static GEN
+gen_comb(long cdep, GEN A, GEN e, struct qfauto *qf, long lim)
+{
+  long i, dim = lg(A)-1;
+  GEN comb = cgetg(dim+1,t_VEC);
+  for (i = 1; i <= dim; ++i)
+  {
+    pari_sp av = avma;
+    GEN trans, ccoef, cF, B, BI;
+    GEN sumveclist, sumvecbase;
+    GEN list = scpvecs(&sumveclist, i, e, cdep, qf);
+    GEN M = zm_to_ZM(sumveclist);
+    GEN T = lllgramint(qf_apply_ZM(A,M));
+    if (lim && lg(T)-1>=lim) return NULL;
+    B = ZM_mul(M,T);
+    BI = RgM_solve(B,NULL);
+    sumvecbase = ZM_trunc_to_zm(B);
+    trans = ZM_trunc_to_zm(T);
+    ccoef = ZM_trunc_to_zm(RgM_mul(BI,M));
+    cF = scpforms(sumvecbase, qf);
+    gel(comb,i) = gerepilecopy(av, mkvec4(list, trans, ccoef, cF));
+  }
+  return comb;
+}
+
+static void
+init_comb(struct qfcand *cand, GEN A, GEN e, struct qfauto *qf)
+{
+  long dim = lg(A)-1;
+  GEN Am = zm_to_ZM(A);
+  for (cand->cdep = 1; ; cand->cdep++)
+  {
+    cand->comb = gen_comb(cand->cdep, Am, e, qf, (dim+1)>>1);
+    if (!cand->comb) break;
+  }
+  cand->cdep= maxss(1, cand->cdep-1);
+  cand->comb = gen_comb(cand->cdep, Am, e, qf, 0);
+}
+
+static void
+init_flags(struct qfcand *cand, GEN A, struct fingerprint *fp,
+                                       struct qfauto *qf, GEN flags)
+{
+  if (!flags)
+  {
+    init_comb(cand, A, fp->e, qf);
+    cand->bacher_pol = init_bacher(0, fp, qf);
+  }
+  else
+  {
+    long cdep, bach;
+    if (typ(flags)!=t_VEC || lg(flags)!=3)
+      pari_err_TYPE("qfisominit",flags);
+    cdep = gtos(gel(flags,1));
+    bach = minss(gtos(gel(flags,2)),lg(fp->e)-1);
+    if (cdep<0 || bach<0) pari_err_FLAG("qfisom");
+    cand->cdep = cdep;
+    cand->comb = cdep ? gen_comb(cdep, zm_to_ZM(A), fp->e, qf, 0): NULL;
+    cand->bacher_pol = init_bacher(bach, fp, qf);
+  }
+}
+
+static GEN
+gen_group(struct group *G)
+{
+  GEN V;
+  long i, j, n=1, dim = lg(G->ord)-1;
+  GEN o = gen_1;
+  for (i = 1; i <= dim; ++i)
+    o = muliu(o, G->ord[i]);
+  for (i = 1; i <= dim; ++i)
+    n += G->ng[i]-G->nsg[i];
+  V = cgetg(n, t_VEC); n = 1;
+  for (i = 1; i <= dim; ++i)
+    for (j=G->nsg[i]+1; j<=G->ng[i]; j++)
+      gel(V,n++) = gmael(G->g,i,j);
+  return mkvec2(o, V);
+}
+
+static long
+is_qfisom(GEN F)
+{
+  return (lg(F)==6 && typ(F)==t_VEC && typ(gel(F,1))==t_VEC
+                   && typ(gel(F,3))==t_VEC && typ(gel(F,4))==t_VEC);
+}
+
+static GEN
+unpack_qfisominit(GEN F, GEN *norm, struct qfauto *qf,
+      struct fingerprint *fp, struct qfcand *cand)
+{
+  GEN QF = gel(F,3);
+  qf->F = gel(QF,1);
+  qf->V = gel(QF,2);
+  qf->W = gel(QF,3);
+  qf->v = gel(QF,4);
+  qf->p = itou(gel(QF,5));
+  QF = gel(F,4);
+  fp->diag = gel(QF,1);
+  fp->per  = gel(QF,2);
+  fp->e    = gel(QF,3);
+  QF = gel(F,5);
+  cand->cdep =itos(gel(QF,1));
+  cand->comb = gel(QF,2);
+  cand->bacher_pol = gel(QF,3);
+  *norm = gel(F,2);
+  qf->dim = lg(gmael(F,1,1))-1;
+  return gel(F,1);
+}
+
+static GEN
+init_qfisom(GEN F, struct fingerprint *fp, struct qfcand *cand,
+                   struct qfauto *qf, GEN flags, long *max)
+{
+  GEN A, norm;
+  if (is_qfisom(F))
+  {
+    F = unpack_qfisominit(F, &norm, qf, fp, cand);
+    A = gel(F,1);
+    *max = zm_maxdiag(A);
+    if (flags)
+      init_flags(cand, A, fp, qf, flags);
+  }
+  else
+  {
+    if (lg(F)<2) pari_err_TYPE("qfisom",F);
+    A = gel(F,1);
+    if (lg(A)<2) pari_err_TYPE("qfisom",A);
+    *max = zm_maxdiag(A);
+    if (DEBUGLEVEL) err_printf("max=%ld\n",*max);
+    norm=init_qfauto(F, *max, qf, NULL);
+    fingerprint(fp, qf);
+    if (DEBUGLEVEL) err_printf("fp=%Ps\n",fp->diag);
+    init_flags(cand, A, fp, qf, flags);
+  }
+  return norm;
+}
+
+GEN
+qfauto(GEN F, GEN flags)
+{
+  pari_sp av = avma;
+  struct fingerprint fp;
+  struct group G;
+  struct qfcand cand;
+  struct qfauto qf;
+  long max;
+  (void)init_qfisom(F, &fp, &cand, &qf, flags, &max);
+  init_qfgroup(&G, &fp, &qf);
+  autom(&G, &qf, &fp, &cand);
+  return gerepilecopy(av, gen_group(&G));
+}
+
+static GEN
+qf_to_zmV(GEN F)
+{
+  return typ(F)==t_MAT ?
+     (RgM_is_ZM(F) ? mkvec(ZM_to_zm(F)): NULL)
+     : typ(F)==t_VEC ? (RgV_is_ZMV(F) ? ZMV_to_zmV(F): NULL)
+     : NULL;
+}
+
+GEN
+qfauto0(GEN x, GEN flags)
+{
+  pari_sp av = avma;
+  GEN F, G;
+  if (is_qfisom(x))
+    F = x;
+  else
+  {
+    F = qf_to_zmV(x);
+    if (!F) pari_err_TYPE("qfauto",x);
+  }
+  G = qfauto(F, flags);
+  return gerepilecopy(av, mkvec2(gel(G,1), zmV_to_ZMV(gel(G,2))));
+}
+/* computes the orbit of V.v[pt] under the generators G[0],...,G[nG-1] and
+ * elements stabilizing V.v[pt], which are stored in H, returns the number of
+ * generators in H */
+
+static GEN
+isostab(long pt, GEN G, GEN V, long Maxfail, ulong p)
+{
+  pari_sp av = avma;
+  long  len, cnd, orblen, tmplen, rpt;
+  GEN  w, flag, orb;
+  long  i, im, nH, fail;
+  long dim = lg(gel(V,1))-1, n = lg(V)-1, nG = lg(G)-1;
+  GEN H;
+  /* a heuristic break condition for the computation of stabilizer elements:
+     it would be too expensive to calculate all the stabilizer generators,
+     which are obtained from the orbit, since this is highly redundant,
+     on the other hand every new generator which enlarges the group reduces the
+     number of orbits and hence the number of candidates to be tested, after
+     Maxfail subsequent stabilizer elements, that do not enlarge the group, the
+     procedure stops, increasing Maxfail will possibly decrease the number of
+     tests, but will increase the running time of the stabilizer computation
+     there is no magic behind the heuristic, tuning might be appropriate */
+  /* H are the generators of the stabilizer of V.v[pt] */
+  H = cgetg(2,t_VEC);
+  nH = 0;
+  /* w[i+V.n] is a matrix that maps V.v[pt] on V.v[i] */
+  w = cgetg(2*n+2,t_MAT);
+  orb = zero_Flv(2*n);
+  /* orblen is the length of the orbit of a random vector in V.v */
+  orblen = 1;
+  /* if flag[i+V.n] = 1, then the point i is already in the orbit */
+  flag = zero_Flv(2*n+1);
+  orb[1] = pt;
+  flag[orb[1]+n+1] = 1;
+  /* w[pt+V.n] is the Identity */
+  gel(w,orb[1]+n+1) = matid_Flm(dim);
+  cnd = 1;
+  len = 1;
+  /* fail is the number of successive failures */
+  fail = 0;
+  while (cnd <= len &&  fail < Maxfail)
+  {
+    for (i = 1; i <= nG  &&  fail < Maxfail; ++i)
+    {
+      im = operate(orb[cnd], gel(G,i), V);
+      if (flag[im+n+1] == 0)
+        /* a new element is found, appended to the orbit and an element mapping
+           V.v[pt] to im is stored in w[im+V.n] */
+      {
+        orb[++len] = im;
+        flag[im+n+1] = 1;
+        gel(w,im+n+1)= zm_mul(gel(G,i), gel(w,orb[cnd]+n+1));
+      }
+      else /* the image was already in the orbit */
+      {
+        GEN B = zm_mul(gel(G,i), gel(w,orb[cnd]+n+1));
+        /* check whether the old and the new element mapping pt on im differ */
+        if (!zvV_equal(B, gel(w,im+n+1)))
+        {
+          gel(H,nH+1) = zm_divmod(gel(w,im+n+1),B,p);
+          rpt = 1+(long)(pari_rand() % n);
+          tmplen = orbitlen(rpt, 2*n, H, nH+1, V);
+          while (tmplen < orblen)
+            /* the orbit of this vector is shorter than a previous one, hence
+               choose a new random vector */
+          {
+            rpt = 1+(long)(pari_rand() % n);
+            tmplen = orbitlen(rpt, 2*n, H, nH+1, V);
+          }
+          if (tmplen > orblen)
+            /* the new stabilizer element H[nH] enlarges the group generated by
+               H */
+          {
+            orblen = tmplen;
+            /* allocate memory for the new generator */
+            H = vec_lengthen(H, (++nH)+1);
+            fail = 0;
+          }
+          else
+            /* the new stabilizer element does not enlarge the orbit of a
+               random vector */
+            ++fail;
+        }
+        /* if H[nH] is the identity, nothing is done */
+      }
+    }
+    ++cnd;
+  }
+  setlg(H,nH+1);
+  return gerepilecopy(av, H);
+}
+
+/* the heart of the program: the recursion */
+
+static long
+iso(long step, GEN x, GEN C, struct qfauto *qf, struct qfauto *qff,
+    struct fingerprint *fp, GEN G, struct qfcand *cand)
+{
+  int  i, Maxfail;
+  GEN H;
+  long dim = qf->dim;
+  long found = 0;
+  while (mael(C,step,1) != 0  &&  found == 0)
+  {
+    if (step < dim)
+    {
+      long nbc;
+      /* choose the image of the base-vector nr. step */
+      x[step] = mael(C,step,1);
+      /* check whether x[0]...x[step] is a partial automorphism and compute the
+         candidates for x[step+1] */
+      nbc = qfisom_candidates(gel(C,step+1), step+1, x, qf, qff, fp, cand);
+      if (nbc == fp->diag[step+1])
+      {
+        /* go deeper into the recursion */
+        Maxfail = 0;
+        /* determine the heuristic value of Maxfail for the break condition in
+           isostab */
+        for (i = 1; i <= step; ++i)
+          if (fp->diag[i] > 1)
+            Maxfail += 1;
+        for (i = step+1; i <= dim; ++i)
+          if (fp->diag[i] > 1)
+            Maxfail += 2;
+        /* compute the stabilizer H of x[step] in G */
+        H = isostab(x[step], G, qff->V, Maxfail,qff->p);
+        found = iso(step+1, x, C, qf, qff, fp, H, cand);
+      }
+      if (found == 1)
+        return 1;
+      /* delete the orbit of the chosen vector from the list of candidates */
+      orbsubtract(gel(C,step), x, step-1, 1, G, qff->V, NULL);
+    }
+    else
+    {
+      /* an isomorphism is found */
+      x[dim] = mael(C,dim,1);
+      found = 1;
+    }
+  }
+  return found;
+}
+
+/* search for an isometry */
+
+static GEN
+isometry(struct qfauto *qf, struct qfauto *qff, struct fingerprint *fp, GEN G,
+                                                struct qfcand *cand)
+
+{
+  long i, found;
+  GEN x;
+  long dim = qf->dim;
+  GEN C = cgetg(dim+1,t_VEC);
+  /* C[i] is the list of candidates for the image of the i-th base-vector */
+  for (i = 1; i <= dim; ++i)
+    gel(C,i) = cgetg(fp->diag[i]+1, t_VECSMALL);
+  x = cgetg(dim+1, t_VECSMALL);
+  /* compute the candidates for x[1] */
+  qfisom_candidates(gel(C,1), 1, x, qf, qff, fp, cand);
+  found = iso(1, x, C, qf, qff, fp, G, cand);
+  return found ? matgen(x, fp->per, qff->V): NULL;
+}
+
+GEN
+qfisominit(GEN F, GEN flags)
+{
+  pari_sp av = avma;
+  struct fingerprint fp;
+  struct qfauto qf;
+  struct qfcand cand;
+  long max;
+  GEN norm = init_qfisom(F, &fp, &cand, &qf, flags, &max);
+  return gerepilecopy(av, mkvec5(F, norm, mkvec5(qf.F, qf.V, qf.W, qf.v, utoi(qf.p)),
+                          mkvec3(fp.diag, fp.per, fp.e),
+                          mkvec3(stoi(cand.cdep),cand.comb?cand.comb:cgetg(1,t_VEC),
+                                 cand.bacher_pol)));
+}
+
+GEN
+qfisominit0(GEN x, GEN flags)
+{
+  pari_sp av = avma;
+  GEN F = qf_to_zmV(x);
+  if (!F) pari_err_TYPE("qfisom",x);
+  return gerepileupto(av, qfisominit(F, flags));
+}
+
+GEN
+qfisom(GEN F, GEN FF, GEN flags)
+{
+  pari_sp av = avma;
+  struct fingerprint fp;
+  GEN G, res;
+  struct qfauto qf, qff;
+  struct qfcand cand;
+  long max;
+  GEN norm = init_qfisom(F, &fp, &cand, &qf, flags, &max);
+  init_qfauto(FF, max, &qff, norm);
+  if (lg(qf.W)!=lg(qff.W)
+      || !zvV_equal(vecvecsmall_sort(qf.W), vecvecsmall_sort(qff.W)))
+    { avma=av; return gen_0; }
+  G = mkvec(scalar_Flm(-1, qff.dim));
+  res = isometry(&qf, &qff, &fp, G, &cand);
+  if (!res) { avma=av; return gen_0; }
+  return gerepilecopy(av, zm_to_ZM(res));
+}
+
+GEN
+qfisom0(GEN x, GEN y, GEN flags)
+{
+  pari_sp av = avma;
+  GEN F, FF;
+  if (is_qfisom(x))
+    F = x;
+  else
+  {
+    F = qf_to_zmV(x);
+    if (!F) pari_err_TYPE("qfisom",x);
+  }
+  FF = qf_to_zmV(y);
+  if (!FF) pari_err_TYPE("qfisom",y);
+  return gerepileupto(av, qfisom(F, FF, flags));
+}
+
+static GEN
+ZM_to_GAP(GEN M)
+{
+  pari_sp ltop=avma;
+  long rows = nbrows(M), cols = lg(M)-1;
+  long i, j, c;
+  GEN comma = strtoGENstr(", ");
+  GEN bra = strtoGENstr("[");
+  GEN ket = strtoGENstr("]");
+  GEN s = cgetg(2*rows*cols+2*rows+2,t_VEC);
+  gel(s,1) = bra; c=2;
+  for (i = 1; i <= rows; ++i)
+  {
+    if (i > 1)
+      gel(s,c++) = comma;
+    gel(s,c++) = bra;
+    for (j = 1; j <= cols; ++j)
+    {
+      if (j > 1)
+        gel(s,c++) = comma;
+      gel(s,c++) = GENtoGENstr(gcoeff(M,i,j));
+    }
+    gel(s,c++) = ket;
+  }
+  gel(s,c++) = ket;
+  return gerepilecopy(ltop,shallowconcat1(s));
+}
+
+GEN
+qfautoexport(GEN G, long flag)
+{
+  pari_sp av = avma;
+  long i, lgen,  c = 2;
+  GEN gen, str, comma = strtoGENstr(", ");
+  if (typ(G)!=t_VEC || lg(G)!=3) pari_err_TYPE("qfautoexport", G);
+  if (flag!=0 && flag!=1) pari_err_FLAG("qfautoexport");
+  gen = gel(G,2); lgen = lg(gen)-1;
+  str = cgetg(2+2*lgen,t_VEC);
+  /* in GAP or MAGMA the matrix group is called BG */
+  if (flag == 0)
+    gel(str,1) = strtoGENstr("Group(");
+  else
+  {
+    long dim = lg(gmael(gen,1,1))-1;
+    gel(str,1) = gsprintf("MatrixGroup<%d, Integers() |",dim);
+  }
+  for(i = 1; i <= lgen; i++)
+  {
+    if (i!=1)
+      gel(str,c++) = comma;
+    gel(str,c++) = ZM_to_GAP(gel(gen,i));
+  }
+  gel(str,c++) = strtoGENstr(flag ? ">":")");
+  return gerepilecopy(av, shallowconcat1(str));
+}
diff --git a/src/basemath/random.c b/src/basemath/random.c
new file mode 100644
index 0000000..969013c
--- /dev/null
+++ b/src/basemath/random.c
@@ -0,0 +1,227 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/********************************************************************/
+/*                                                                  */
+/*                      PSEUDO-RANDOM INTEGERS                      */
+/*                                                                  */
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+/********************************************************************/
+/*                    XORGEN (Richard P. Brent)                     */
+/*          http://wwwmaths.anu.edu.au/~brent/random.html           */
+/*        (initial adaptation to PARI/GP by Randall Rathbun)        */
+/********************************************************************/
+/* Adapted from xorgens.c version 3.04, Richard P. Brent, 20060628 (GPL).
+ * 32-bit or 64-bit integer random number generator with period at
+ * least 2**4096-1. It is assumed that "ulong" is a 32-bit or 64-bit integer */
+static THREAD ulong xorgen_w;
+static THREAD int xorgen_i;
+
+#ifdef LONG_IS_64BIT /* weyl = odd approximation to 2^BIL*(sqrt(5)-1)/2. */
+  static const ulong weyl = 0x61c8864680b583ebUL;
+  static const int ws = 27, r = 64,  s = 53, a = 33, b = 26, c = 27, d = 29;
+  static THREAD ulong state[64]; /* size r */
+#else
+  static const ulong weyl = 0x61c88647UL;
+  static const int ws = 16, r = 128, s = 95, a = 17, b = 12, c = 13, d = 15;
+  static THREAD ulong state[128]; /* size r */
+#endif
+
+static void
+init_xor4096i(ulong seed)
+{
+  ulong t, v = seed;  /* v must be nonzero */
+  int k;
+
+  for (k = BITS_IN_LONG; k > 0; k--) {/* Avoid correlations for close seeds */
+    v ^= v<<10; v ^= v>>15;      /* Recurrence has period 2**BIL -1 */
+    v ^= v<<4;  v ^= v>>13;
+  }
+  for (xorgen_w = v, k = 0; k < r; k++) { /* Initialise circular array */
+    v ^= v<<10; v ^= v>>15;
+    v ^= v<<4;  v ^= v>>13;
+    state[k] = v + (xorgen_w+=weyl);
+  }
+  for (xorgen_i = r-1, k = 4*r; k > 0; k--) { /* Discard first 4*r results */
+    t = state[xorgen_i = (xorgen_i+1)&(r-1)]; t ^= t<<a;  t ^= t>>b;
+    v = state[(xorgen_i+(r-s))&(r-1)];        v ^= v<<c;  v ^= v>>d;
+    state[xorgen_i] = t^v;
+  }
+}
+
+void pari_init_rand(void) { init_xor4096i(1); }
+
+/* One random number uniformly distributed in [0..2**BIL) is returned, where
+ * BIL = 8*sizeof(ulong) = 32 or 64. */
+ulong
+pari_rand(void)
+{
+  ulong t, v;
+
+  t = state[xorgen_i = (xorgen_i+1)&(r-1)];
+  v = state[(xorgen_i+(r-s))&(r-1)];   /* Index is (xorgen_i-s) mod r */
+  t ^= t<<a;  t ^= t>>b;               /* (I + L^a)(I + R^b) */
+  v ^= v<<c;  v ^= v>>d;               /* (I + L^c)(I + R^d) */
+  state[xorgen_i] = (v ^= t);          /* Update circular array */
+  xorgen_w += weyl;                    /* Update Weyl generator */
+  return v + (xorgen_w ^ (xorgen_w>>ws));
+}
+
+void
+setrand(GEN seed) {
+  switch (typ(seed))
+  {
+    case t_VECSMALL:
+    {
+      GEN xd = seed+1;
+      long i;
+      if (lg(seed) != r+2 + 1) break;
+      for (i = 0; i < r; i++) state[i] = xd[i];
+      xorgen_i = xd[i++];
+      xorgen_w = xd[i++];
+      return;
+    }
+    case t_INT: if (signe(seed) > 0) { init_xor4096i( itou(seed) ); return; }
+  }
+  pari_err_TYPE("setrand",seed);
+}
+GEN
+getrand(void) {
+  GEN x, xd;
+  long i;
+  if (xorgen_i < 0) init_xor4096i(1);
+
+  x = cgetg(r+2 + 1, t_VECSMALL); xd = x+1;
+  for (i = 0; i < r; i++) xd[i] = state[i];
+  xd[i++] = xorgen_i;
+  xd[i++] = xorgen_w;
+  return x;
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                         GENERIC ROUTINES                         */
+/*                                                                  */
+/********************************************************************/
+
+/* assume n > 0 */
+ulong
+random_Fl(ulong n)
+{
+  ulong d;
+  int shift;
+
+  if (n == 1) return 0;
+
+  shift = bfffo(n); /* 2^(BIL-shift) > n >= 2^(BIL-shift-1)*/
+  /* if N a power of 2, increment shift. No reject */
+  if ((n << shift) == HIGHBIT) return pari_rand() >> (shift+1);
+  for (;;) {
+    d = pari_rand() >> shift; /* d < 2^(BIL-shift) uniformly distributed */
+    /* reject strategy: proba success = n 2^(shift-BIL), in [1/2, 1[ */
+    if (d < n) return d;
+  }
+}
+
+/* assume N > 0, see random_Fl() for algorithm */
+GEN
+randomi(GEN N)
+{
+  long lx = lgefint(N);
+  GEN d, NMSW;
+  pari_sp av;
+  int shift;
+
+  if (lx == 3) return utoi( random_Fl(N[2]) );
+
+  NMSW = int_MSW(N); shift = bfffo(*NMSW);
+  if (((ulong)*NMSW << shift) == HIGHBIT)
+  { /* if N a power of 2, increment shift */
+    for (d = int_LSW(N); !*d; d = int_nextW(d)) /* empty */;
+    if (d == NMSW && ++shift == BITS_IN_LONG) { shift = 0; lx--; }
+  }
+  for (av = avma;; avma = av) {
+    GEN x = cgetipos(lx), xMSW = int_MSW(x);
+    for (d = int_LSW(x); d != xMSW; d = int_nextW(d)) *d = pari_rand();
+    *d = pari_rand() >> shift;
+    x = int_normalize(x, 0);
+    if (absi_cmp(x, N) < 0) return x;
+  }
+}
+
+GEN
+randomr(long prec)
+{
+  pari_sp av;
+  long b;
+  GEN x, y;
+  if (prec <= 2) return real_0_bit(0);
+  x = cgetr(prec); av = avma;
+  b = prec2nbits(prec);
+  y = randomi(int2n(b));
+  if (!signe(y)) return real_0_bit(b);
+  affir(y, x); shiftr_inplace(x, - b);
+  avma = av; return x;
+}
+
+static GEN
+polrandom(GEN N) /* assume N!=0 */
+{
+  long i, d = lg(N);
+  GEN z = leading_term(N);
+  GEN y = cgetg(d,t_POL);
+  y[1] = evalsigne(1) | evalvarn(varn(N));
+  for (i=2; i<d; i++) gel(y,i) = genrand(z);
+  return normalizepol_lg(y,d);
+}
+
+GEN
+genrand(GEN N)
+{
+  GEN z;
+  if (!N) return utoi( random_bits(31) );
+  switch(typ(N))
+  {
+    case t_INT:
+      if (signe(N)<=0) pari_err_DOMAIN("random","N","<=",gen_0,gen_0);
+      return randomi(N);
+    case t_REAL:
+      return randomr(realprec(N));
+    case t_INTMOD:
+      z = cgetg(3, t_INTMOD);
+      gel(z,1) = icopy(gel(N,1));
+      gel(z,2) = randomi(gel(N,1)); return z;
+    case t_FFELT:
+      return ffrandom(N);
+    case t_POL:
+      if (signe(N)==0) return pol_0(varn(N));
+      return polrandom(N);
+    case t_VEC:
+      if (lg(N) == 3)
+      {
+        pari_sp av = avma;
+        GEN a = gel(N,1), b = gel(N,2), d;
+        if (typ(a) != t_INT) a = gceil(a);
+        if (typ(b) != t_INT) b = gfloor(b);
+        if (typ(a) != t_INT || typ(b) != t_INT) pari_err_TYPE("random", N);
+        d = subii(b,a);
+        if (signe(d) < 0) pari_err_TYPE("random([a,b]) (a > b)", N);
+        return gerepileuptoint(av, addii(a, randomi(addis(d,1))));
+      }
+      return ellrandom(N);
+    default:
+      pari_err_TYPE("genrand",N);
+      return NULL;/*not reached*/
+  }
+}
diff --git a/src/basemath/rootpol.c b/src/basemath/rootpol.c
new file mode 100644
index 0000000..3694d5b
--- /dev/null
+++ b/src/basemath/rootpol.c
@@ -0,0 +1,2165 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                ROOTS OF COMPLEX POLYNOMIALS                     */
+/*  (original code contributed by Xavier Gourdon, INRIA RR 1852)   */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+static const double pariINFINITY = 100000.;
+
+/********************************************************************/
+/**                                                                **/
+/**                   FAST ARITHMETIC over Z[i]                    **/
+/**                                                                **/
+/********************************************************************/
+static THREAD long KARASQUARE_LIMIT, COOKSQUARE_LIMIT;
+
+/* fast sum of x,y: t_INT or t_COMPLEX(t_INT) */
+static GEN
+addCC(GEN x, GEN y)
+{
+  GEN z;
+
+  if (typ(x) == t_INT)
+  {
+    if (typ(y) == t_INT) return addii(x,y);
+    /* ty == t_COMPLEX */
+    z = cgetg(3,t_COMPLEX);
+    gel(z,1) = addii(x, gel(y,1));
+    gel(z,2) = icopy(gel(y,2)); return z;
+  }
+  /* tx == t_COMPLEX */
+  z = cgetg(3,t_COMPLEX);
+  if (typ(y) == t_INT)
+  {
+    gel(z,1) = addii(gel(x,1),y);
+    gel(z,2) = icopy(gel(x,2)); return z;
+  }
+  /* ty == t_COMPLEX */
+  gel(z,1) = addii(gel(x,1),gel(y,1));
+  gel(z,2) = addii(gel(x,2),gel(y,2)); return z;
+}
+/* fast product of x,y: t_INT or t_COMPLEX(t_INT) */
+static GEN
+mulCC(GEN x, GEN y)
+{
+  GEN z;
+
+  if (typ(x) == t_INT)
+  {
+    if (typ(y) == t_INT) return mulii(x,y);
+    /* ty == t_COMPLEX */
+    z = cgetg(3,t_COMPLEX);
+    gel(z,1) = mulii(x, gel(y,1));
+    gel(z,2) = mulii(x, gel(y,2)); return z;
+  }
+  /* tx == t_COMPLEX */
+  z = cgetg(3,t_COMPLEX);
+  if (typ(y) == t_INT)
+  {
+    gel(z,1) = mulii(gel(x,1),y);
+    gel(z,2) = mulii(gel(x,2),y); return z;
+  }
+  /* ty == t_COMPLEX */
+  {
+    pari_sp av = avma, tetpil;
+    GEN p1, p2;
+
+    p1 = mulii(gel(x,1),gel(y,1));
+    p2 = mulii(gel(x,2),gel(y,2));
+    y = mulii(addii(gel(x,1),gel(x,2)),
+              addii(gel(y,1),gel(y,2)));
+    x = addii(p1,p2); tetpil = avma;
+    gel(z,1) = subii(p1,p2);
+    gel(z,2) = subii(y,x); gerepilecoeffssp(av,tetpil,z+1,2);
+    return z;
+  }
+}
+/* fast squaring x: t_INT or t_COMPLEX(t_INT) */
+static GEN
+sqrCC(GEN x)
+{
+  GEN z;
+
+  if (typ(x) == t_INT) return sqri(x);
+  /* tx == t_COMPLEX */
+  z = cgetg(3,t_COMPLEX);
+  {
+    pari_sp av = avma, tetpil;
+    GEN y, p1, p2;
+
+    p1 = sqri(gel(x,1));
+    p2 = sqri(gel(x,2));
+    y = sqri(addii(gel(x,1),gel(x,2)));
+    x = addii(p1,p2); tetpil = avma;
+    gel(z,1) = subii(p1,p2);
+    gel(z,2) = subii(y,x); gerepilecoeffssp(av,tetpil,z+1,2);
+    return z;
+  }
+}
+
+static void
+set_karasquare_limit(long bit)
+{
+  if (bit<600)       { KARASQUARE_LIMIT=8; COOKSQUARE_LIMIT=400; }
+  else if (bit<2000) { KARASQUARE_LIMIT=4; COOKSQUARE_LIMIT=200; }
+  else if (bit<3000) { KARASQUARE_LIMIT=4; COOKSQUARE_LIMIT=125; }
+  else if (bit<5000) { KARASQUARE_LIMIT=2; COOKSQUARE_LIMIT= 75; }
+  else               { KARASQUARE_LIMIT=1; COOKSQUARE_LIMIT= 50; }
+}
+
+/* assume lP > 0, lP = lgpol(P) */
+static GEN
+CX_square_spec(GEN P, long lP)
+{
+  GEN s, t;
+  long i, j, l, nn, n = lP - 1;
+  pari_sp av;
+
+  nn = n<<1; s = cgetg(nn+3,t_POL); s[1] = evalsigne(1)|evalvarn(0);
+  gel(s,2) = sqrCC(gel(P,0)); /* i = 0 */
+  for (i=1; i<=n; i++)
+  {
+    av = avma; l = (i+1)>>1;
+    t = mulCC(gel(P,0), gel(P,i)); /* j = 0 */
+    for (j=1; j<l; j++) t = addCC(t, mulCC(gel(P,j), gel(P,i-j)));
+    t = gmul2n(t,1);
+    if ((i & 1) == 0) t = addCC(t, sqrCC(gel(P,i>>1)));
+    gel(s,i+2) = gerepileupto(av, t);
+  }
+  gel(s,nn+2) = sqrCC(gel(P,n)); /* i = nn */
+  for (   ; i<nn; i++)
+  {
+    av = avma; l = (i+1)>>1;
+    t = mulCC(gel(P,i-n),gel(P,n)); /* j = i-n */
+    for (j=i-n+1; j<l; j++) t = addCC(t, mulCC(gel(P,j),gel(P,i-j)));
+    t = gmul2n(t,1);
+    if ((i & 1) == 0) t = addCC(t, sqrCC(gel(P,i>>1)));
+    gel(s,i+2) = gerepileupto(av, t);
+  }
+  return normalizepol_lg(s, nn+3);
+}
+/* not stack clean */
+static GEN
+RgX_addspec(GEN x, long nx, GEN y, long ny)
+{
+  GEN z, t;
+  long i;
+  if (nx == ny) {
+    z = cgetg(nx+2,t_POL); z[1] = evalsigne(1)|evalvarn(0); t = z+2;
+    for (i=0; i < nx; i++) gel(t,i) = gadd(gel(x,i),gel(y,i));
+    return normalizepol_lg(z, nx+2);
+  }
+  if (ny < nx) {
+    z = cgetg(nx+2,t_POL); z[1] = evalsigne(1)|evalvarn(0); t = z+2;
+    for (i=0; i < ny; i++) gel(t,i) = gadd(gel(x,i),gel(y,i));
+    for (   ; i < nx; i++) gel(t,i) = gel(x,i);
+    return normalizepol_lg(z, nx+2);
+  } else {
+    z = cgetg(ny+2,t_POL); z[1] = evalsigne(1)|evalvarn(0); t = z+2;
+    for (i=0; i < nx; i++) gel(t,i) = gadd(gel(x,i),gel(y,i));
+    for (   ; i < ny; i++) gel(t,i) = gel(y,i);
+    return normalizepol_lg(z, ny+2);
+  }
+}
+/* nx = lgpol(x) */
+static GEN
+RgX_s_mulspec(GEN x, long nx, long s)
+{
+  GEN z, t;
+  long i;
+  if (!s || !nx) return pol_0(0);
+  z = cgetg(nx+2, t_POL); z[1] = evalsigne(1)|evalvarn(0); t = z + 2;
+  for (i=0; i < nx; i++) gel(t,i) = gmulgs(gel(x,i), s);
+  return z;
+}
+/* nx = lgpol(x), return x << s. Inefficient if s = 0... */
+static GEN
+RgX_shiftspec(GEN x, long nx, long s)
+{
+  GEN z, t;
+  long i;
+  if (!nx) return pol_0(0);
+  z = cgetg(nx+2, t_POL); z[1] = evalsigne(1)|evalvarn(0); t = z + 2;
+  for (i=0; i < nx; i++) gel(t,i) = gmul2n(gel(x,i), s);
+  return z;
+}
+
+/* spec function. nP = lgpol(P) */
+static GEN
+karasquare(GEN P, long nP)
+{
+  GEN Q, s0, s1, s2, a, t;
+  long n0, n1, i, l, N, N0, N1, n = nP - 1; /* degree(P) */
+  pari_sp av;
+
+  if (n <= KARASQUARE_LIMIT) return nP? CX_square_spec(P, nP): pol_0(0);
+  av = avma;
+  n0 = (n>>1) + 1; n1 = nP - n0;
+  s0 = karasquare(P, n0); Q = P + n0;
+  s2 = karasquare(Q, n1);
+  s1 = RgX_addspec(P, n0, Q, n1);
+  s1 = RgX_sub(karasquare(s1+2, lgpol(s1)), RgX_add(s0,s2));
+  N = (n<<1) + 1;
+  a = cgetg(N + 2, t_POL); a[1] = evalsigne(1)|evalvarn(0);
+  t = a+2; l = lgpol(s0); s0 += 2; N0 = n0<<1;
+  for (i=0; i < l;  i++) gel(t,i) = gel(s0,i);
+  for (   ; i < N0; i++) gel(t,i) = gen_0;
+  t = a+2 + N0; l = lgpol(s2); s2 += 2; N1 = N - N0;
+  for (i=0; i < l;  i++) gel(t,i) = gel(s2,i);
+  for (   ; i < N1; i++) gel(t,i) = gen_0;
+  t = a+2 + n0; l = lgpol(s1); s1 += 2;
+  for (i=0; i < l; i++)  gel(t,i) = gadd(gel(t,i), gel(s1,i));
+  return gerepilecopy(av, normalizepol_lg(a, N+2));
+}
+/* spec function. nP = lgpol(P) */
+static GEN
+cook_square(GEN P, long nP)
+{
+  GEN Q, p0, p1, p2, p3, q, r, t, vp, vm;
+  long n0, n3, i, j, n = nP - 1;
+  pari_sp av;
+
+  if (n <= COOKSQUARE_LIMIT) return  nP? karasquare(P, nP): pol_0(0);
+  av = avma;
+
+  n0 = (n+1) >> 2; n3 = n+1 - 3*n0;
+  p0 = P;
+  p1 = p0+n0;
+  p2 = p1+n0;
+  p3 = p2+n0; /* lgpol(p0,p1,p2) = n0, lgpol(p3) = n3 */
+
+  q = cgetg(8,t_VEC) + 4;
+  Q = cook_square(p0, n0);
+  r = RgX_addspec(p0,n0, p2,n0);
+  t = RgX_addspec(p1,n0, p3,n3);
+  gel(q,-1) = RgX_sub(r,t);
+  gel(q,1)  = RgX_add(r,t);
+  r = RgX_addspec(p0,n0, RgX_shiftspec(p2,n0, 2)+2,n0);
+  t = gmul2n(RgX_addspec(p1,n0, RgX_shiftspec(p3,n3, 2)+2,n3), 1);
+  gel(q,-2) = RgX_sub(r,t);
+  gel(q,2)  = RgX_add(r,t);
+  r = RgX_addspec(p0,n0, RgX_s_mulspec(p2,n0, 9)+2,n0);
+  t = gmulsg(3, RgX_addspec(p1,n0, RgX_s_mulspec(p3,n3, 9)+2,n3));
+  gel(q,-3) = RgX_sub(r,t);
+  gel(q,3)  = RgX_add(r,t);
+
+  r = new_chunk(7);
+  vp = cgetg(4,t_VEC);
+  vm = cgetg(4,t_VEC);
+  for (i=1; i<=3; i++)
+  {
+    GEN a = gel(q,i), b = gel(q,-i);
+    a = cook_square(a+2, lgpol(a));
+    b = cook_square(b+2, lgpol(b));
+    gel(vp,i) = RgX_add(b, a);
+    gel(vm,i) = RgX_sub(b, a);
+  }
+  gel(r,0) = Q;
+  gel(r,1) = gdivgs(gsub(gsub(gmulgs(gel(vm,2),9),gel(vm,3)),
+                     gmulgs(gel(vm,1),45)),
+                60);
+  gel(r,2) = gdivgs(gadd(gadd(gmulgs(gel(vp,1),270),gmulgs(Q,-490)),
+                     gadd(gmulgs(gel(vp,2),-27),gmulgs(gel(vp,3),2))),
+                360);
+  gel(r,3) = gdivgs(gadd(gadd(gmulgs(gel(vm,1),13),gmulgs(gel(vm,2),-8)),
+                    gel(vm,3)),
+                48);
+  gel(r,4) = gdivgs(gadd(gadd(gmulgs(Q,56),gmulgs(gel(vp,1),-39)),
+                     gsub(gmulgs(gel(vp,2),12),gel(vp,3))),
+                144);
+  gel(r,5) = gdivgs(gsub(gadd(gmulgs(gel(vm,1),-5),gmulgs(gel(vm,2),4)),
+                     gel(vm,3)),
+                240);
+  gel(r,6) = gdivgs(gadd(gadd(gmulgs(Q,-20),gmulgs(gel(vp,1),15)),
+                     gadd(gmulgs(gel(vp,2),-6),gel(vp,3))),
+                720);
+  q = cgetg(2*n+3,t_POL); q[1] = evalsigne(1)|evalvarn(0);
+  t = q+2;
+  for (i=0; i<=2*n; i++) gel(t,i) = gen_0;
+  for (i=0; i<=6; i++,t += n0)
+  {
+    GEN h = gel(r,i);
+    long d = lgpol(h);
+    h += 2;
+    for (j=0; j<d; j++) gel(t,j) = gadd(gel(t,j), gel(h,j));
+  }
+  return gerepilecopy(av, normalizepol_lg(q, 2*n+3));
+}
+
+static GEN
+graeffe(GEN p)
+{
+  GEN p0, p1, s0, s1;
+  long n = degpol(p), n0, n1, i;
+
+  if (!n) return gcopy(p);
+  n0 = (n>>1)+1; n1 = n+1 - n0; /* n1 <= n0 <= n1+1 */
+  p0 = new_chunk(n0);
+  p1 = new_chunk(n1);
+  for (i=0; i<n1; i++)
+  {
+    p0[i] = p[2+(i<<1)];
+    p1[i] = p[3+(i<<1)];
+  }
+  if (n1 != n0)
+    p0[i] = p[2+(i<<1)];
+  s0 = cook_square(p0, n0);
+  s1 = cook_square(p1, n1);
+  return RgX_sub(s0, RgX_shift_shallow(s1,1));
+}
+GEN
+ZX_graeffe(GEN p)
+{
+  pari_sp av = avma;
+  GEN p0, p1, s0, s1;
+  long n = degpol(p);
+
+  if (!n) return ZX_copy(p);
+  RgX_even_odd(p, &p0, &p1);
+  /* p = p0(x^2) + x p1(x^2) */
+  s0 = ZX_sqr(p0);
+  s1 = ZX_sqr(p1);
+  return gerepileupto(av, ZX_sub(s0, RgX_shift_shallow(s1,1)));
+}
+GEN
+polgraeffe(GEN p)
+{
+  pari_sp av = avma;
+  GEN p0, p1, s0, s1;
+  long n = degpol(p);
+
+  if (typ(p) != t_POL) pari_err_TYPE("polgraeffe",p);
+  n = degpol(p);
+  if (!n) return gcopy(p);
+  RgX_even_odd(p, &p0, &p1);
+  /* p = p0(x^2) + x p1(x^2) */
+  s0 = RgX_sqr(p0);
+  s1 = RgX_sqr(p1);
+  return gerepileupto(av, RgX_sub(s0, RgX_shift_shallow(s1,1)));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       MODULUS OF ROOTS                         **/
+/**                                                                **/
+/********************************************************************/
+
+/* Quick approximation to log2(|x); first define y s.t. |y-x| < 2^-32 then
+ * return y rounded to 2 ulp. In particular, if result < 2^21, absolute error
+ * is bounded by 2^-31. If result > 2^21, it is correct to 2 ulp */
+static double
+mydbllog2i(GEN x)
+{
+#ifdef LONG_IS_64BIT
+  const double W = 1/(4294967296. * 4294967296.); /* 2^-64 */
+#else
+  const double W = 1/4294967296.; /*2^-32*/
+#endif
+  GEN m;
+  long lx = lgefint(x);
+  double l;
+  if (lx == 2) return -pariINFINITY;
+  m = int_MSW(x);
+  l = (double)(ulong)*m;
+  if (lx == 3) return log2(l);
+  l += ((double)(ulong)*int_precW(m)) * W;
+  /* at least m = min(53,BIL) bits are correct in the mantissa, thus log2
+   * is correct with error < log(1 + 2^-m) ~ 2^-m. Adding the correct
+   * exponent BIL(lx-3) causes 1ulp further round-off error */
+  return log2(l) + (double)(BITS_IN_LONG*(lx-3));
+}
+
+/* return log(|x|) or -pariINFINITY */
+static double
+mydbllogr(GEN x) {
+  if (!signe(x)) return -pariINFINITY;
+  return LOG2*dbllog2r(x);
+}
+
+/* return log2(|x|) or -pariINFINITY */
+static double
+mydbllog2r(GEN x) {
+  if (!signe(x)) return -pariINFINITY;
+  return dbllog2r(x);
+}
+static double
+dbllog2mp(GEN x) { return typ(x) == t_INT? mydbllog2i(x): mydbllog2r(x); }
+double
+dbllog2(GEN z)
+{
+  double x, y;
+  switch(typ(z))
+  {
+    case t_INT: return mydbllog2i(z);
+    case t_REAL: return mydbllog2r(z);
+    default: /*t_COMPLEX*/
+      x = dbllog2mp(gel(z,1));
+      y = dbllog2mp(gel(z,2));
+      if (fabs(x-y) > 10) return maxdd(x,y);
+      return x + 0.5*log2(1 + exp2(2*(y-x)));
+  }
+}
+static GEN /* beware overflow */
+dblexp(double x) { return fabs(x) < 100.? dbltor(exp(x)): mpexp(dbltor(x)); }
+
+/* find s such that  A_h <= 2^s <= 2 A_i  for one h and all i < n = deg(p),
+ * with  A_i := (binom(n,i) lc(p) / p_i) ^ 1/(n-i), and  p = sum p_i X^i */
+static long
+findpower(GEN p)
+{
+  double x, L, mins = pariINFINITY;
+  long n = degpol(p),i;
+
+  L = dbllog2(gel(p,n+2)); /* log2(lc * binom(n,i)) */
+  for (i=n-1; i>=0; i--)
+  {
+    L += log2((double)(i+1) / (double)(n-i));
+    x = dbllog2(gel(p,i+2));
+    if (x != -pariINFINITY)
+    {
+      double s = (L - x) / (double)(n-i);
+      if (s < mins) mins = s;
+    }
+  }
+  i = (long)ceil(mins);
+  if (i - mins > 1 - 1e-12) i--;
+  return i;
+}
+
+/* returns the exponent for logmodulus(), from the Newton diagram */
+static long
+newton_polygon(GEN p, long k)
+{
+  pari_sp av = avma;
+  double *logcoef, slope;
+  long n = degpol(p), i, j, h, l, *vertex;
+
+  init_dalloc();
+  logcoef = (double*)stack_malloc((n+1)*sizeof(double));
+  vertex = (long*)new_chunk(n+1);
+
+  /* vertex[i] = 1 if i a vertex of convex hull, 0 otherwise */
+  for (i=0; i<=n; i++) { logcoef[i] = dbllog2(gel(p,2+i)); vertex[i] = 0; }
+  vertex[0] = 1; /* sentinel */
+  for (i=0; i < n; i=h)
+  {
+    slope = logcoef[i+1]-logcoef[i];
+    for (j = h = i+1; j<=n; j++)
+    {
+      double pij = (logcoef[j]-logcoef[i])/(double)(j-i);
+      if (slope < pij) { slope = pij; h = j; }
+    }
+    vertex[h] = 1;
+  }
+  h = k;   while (!vertex[h]) h++;
+  l = k-1; while (!vertex[l]) l--;
+  avma = av;
+  return (long)floor((logcoef[h]-logcoef[l])/(double)(h-l) + 0.5);
+}
+
+/* change z into z*2^e, where z is real or complex of real */
+static void
+myshiftrc(GEN z, long e)
+{
+  if (typ(z)==t_COMPLEX)
+  {
+    if (signe(gel(z,1))) shiftr_inplace(gel(z,1), e);
+    if (signe(gel(z,2))) shiftr_inplace(gel(z,2), e);
+  }
+  else
+    if (signe(z)) shiftr_inplace(z, e);
+}
+
+/* return z*2^e, where z is integer or complex of integer (destroy z) */
+static GEN
+myshiftic(GEN z, long e)
+{
+  if (typ(z)==t_COMPLEX)
+  {
+    gel(z,1) = signe(gel(z,1))? mpshift(gel(z,1),e): gen_0;
+    gel(z,2) = mpshift(gel(z,2),e);
+    return z;
+  }
+  return signe(z)? mpshift(z,e): gen_0;
+}
+
+/* as real_1 with precision in bits, not in words */
+static GEN
+myreal_1(long bit)
+{
+  if (bit < 0) bit = 0;
+  return real_1(nbits2prec(bit));
+}
+static GEN
+RgX_gtofp_bit(GEN q, long bit)
+{
+  if (bit < 0) bit = 0;
+  return RgX_gtofp(q, nbits2prec(bit));
+}
+
+static GEN
+mygprecrc(GEN x, long prec, long e)
+{
+  GEN y;
+  switch(typ(x))
+  {
+    case t_REAL: return signe(x)? rtor(x, prec): real_0_bit(e);
+    case t_COMPLEX:
+      y = cgetg(3,t_COMPLEX);
+      gel(y,1) = mygprecrc(gel(x,1),prec,e);
+      gel(y,2) = mygprecrc(gel(x,2),prec,e);
+      return y;
+    default: return gcopy(x);
+  }
+}
+
+/* gprec behaves badly with the zero for polynomials.
+The second parameter in mygprec is the precision in base 2 */
+static GEN
+mygprec(GEN x, long bit)
+{
+  long lx, i, e, prec;
+  GEN y;
+
+  if (bit < 0) bit = 0; /* should rarely happen */
+  e = gexpo(x) - bit;
+  prec = nbits2prec(bit);
+  switch(typ(x))
+  {
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = mygprecrc(gel(x,i),prec,e);
+      break;
+
+    default: y = mygprecrc(x,prec,e);
+  }
+  return y;
+}
+
+/* normalize a polynomial p, that is change it with coefficients in Z[i],
+after making product by 2^shift */
+static GEN
+pol_to_gaussint(GEN p, long shift)
+{
+  long i, l = lg(p);
+  GEN q = cgetg(l, t_POL); q[1] = p[1];
+  for (i=2; i<l; i++) gel(q,i) = gtrunc2n(gel(p,i), shift);
+  return q;
+}
+
+/* returns a polynomial q in Z[i][x] keeping bit bits of p */
+static GEN
+eval_rel_pol(GEN p, long bit)
+{
+  long i;
+  for (i = 2; i < lg(p); i++)
+    if (gequal0(gel(p,i))) gel(p,i) = gen_0; /* bad behaviour of gexpo */
+  return pol_to_gaussint(p, bit-gexpo(p)+1);
+}
+
+/* returns p(R*x)/R^n (in R or R[i]), R = exp(lrho), bit bits of precision */
+static GEN
+homothetie(GEN p, double lrho, long bit)
+{
+  GEN q, r, t, iR;
+  long n = degpol(p), i;
+
+  iR = mygprec(dblexp(-lrho),bit);
+  q = mygprec(p, bit);
+  r = cgetg(n+3,t_POL); r[1] = p[1];
+  t = iR; r[n+2] = q[n+2];
+  for (i=n-1; i>0; i--)
+  {
+    gel(r,i+2) = gmul(t, gel(q,i+2));
+    t = mulrr(t, iR);
+  }
+  gel(r,2) = gmul(t, gel(q,2)); return r;
+}
+
+/* change q in 2^(n*e) p(x*2^(-e)), n=deg(q)  [ ~as above with R = 2^-e ]*/
+static void
+homothetie2n(GEN p, long e)
+{
+  if (e)
+  {
+    long i,n = lg(p)-1;
+    for (i=2; i<=n; i++) myshiftrc(gel(p,i), (n-i)*e);
+  }
+}
+
+/* return 2^f * 2^(n*e) p(x*2^(-e)), n=deg(q) */
+static void
+homothetie_gauss(GEN p, long e, long f)
+{
+  if (e || f)
+  {
+    long i, n = lg(p)-1;
+    for (i=2; i<=n; i++) gel(p,i) = myshiftic(gel(p,i), f+(n-i)*e);
+  }
+}
+
+/* Lower bound on the modulus of the largest root z_0
+ * k is set to an upper bound for #{z roots, |z-z_0| < eps} */
+static double
+lower_bound(GEN p, long *k, double eps)
+{
+  long n = degpol(p), i, j;
+  pari_sp ltop = avma;
+  GEN a, s, S, ilc;
+  double r, R, rho;
+
+  if (n < 4) { *k = n; return 0.; }
+  S = cgetg(5,t_VEC);
+  a = cgetg(5,t_VEC); ilc = gdiv(real_1(DEFAULTPREC), gel(p,n+2));
+  for (i=1; i<=4; i++) gel(a,i) = gmul(ilc,gel(p,n+2-i));
+  /* i = 1 split out from next loop for efficiency and initialization */
+  s = gel(a,1);
+  gel(S,1) = gneg(s); /* Newton sum S_i */
+  rho = r = gtodouble(gabs(s,3));
+  R = r / n;
+  for (i=2; i<=4; i++)
+  {
+    s = gmulsg(i,gel(a,i));
+    for (j=1; j<i; j++) s = gadd(s, gmul(gel(S,j),gel(a,i-j)));
+    gel(S,i) = gneg(s); /* Newton sum S_i */
+    r = gtodouble(gabs(s,3));
+    if (r > 0.)
+    {
+      r = exp(log(r/n) / (double)i);
+      if (r > R) R = r;
+    }
+  }
+  if (R > 0. && eps < 1.2)
+    *k = (long)floor((rho/R + n) / (1 + exp(-eps)*cos(eps)));
+  else
+    *k = n;
+  avma = ltop; return R;
+}
+
+/* log of modulus of the largest root of p with relative error tau. Assume
+ * P(0) != 0 and P non constant */
+static double
+logmax_modulus(GEN p, double tau)
+{
+  GEN r,q,aux,gunr;
+  pari_sp av, ltop = avma;
+  long i,k,n=degpol(p),nn,bit,M,e;
+  double rho,eps, tau2 = (tau > 3.0)? 0.5: tau/6.;
+
+  r = cgeti(BIGDEFAULTPREC);
+  av = avma;
+
+  eps = - 1/log(1.5*tau2); /* > 0 */
+  bit = (long) ((double) n*log2(1./tau2)+3*log2((double) n))+1;
+  gunr = myreal_1(bit+2*n);
+  aux = gdiv(gunr, gel(p,2+n));
+  q = RgX_Rg_mul(p, aux); gel(q,2+n) = gunr;
+  e = findpower(q);
+  homothetie2n(q,e);
+  affsi(e, r);
+  q = pol_to_gaussint(q, bit);
+  M = (long) (log2( log(4.*n) / (2*tau2) )) + 2;
+  nn = n;
+  for (i=0,e=0;;)
+  { /* nn = deg(q) */
+    rho = lower_bound(q, &k, eps);
+    if (rho > exp2(-(double)e)) e = (long)-floor(log2(rho));
+    affii(shifti(addis(r,e), 1), r);
+    if (++i == M) break;
+
+    bit = (long) ((double)k * log2(1./tau2) +
+                     (double)(nn-k)*log2(1./eps) + 3*log2((double)nn)) + 1;
+    homothetie_gauss(q, e, bit-(long)floor(dbllog2(gel(q,2+nn))+0.5));
+    nn -= RgX_valrem(q, &q);
+    set_karasquare_limit(gexpo(q));
+    q = gerepileupto(av, graeffe(q));
+    tau2 *= 1.5; if (tau2 > 0.9) tau2 = 0.5;
+    eps = -1/log(tau2); /* > 0 */
+    e = findpower(q);
+  }
+  if (!signe(r)) { avma = ltop; return 0.; }
+  r = itor(r, DEFAULTPREC); shiftr_inplace(r, -M);
+  avma = ltop; return -rtodbl(r) * LOG2; /* -log(2) sum e_i 2^-i */
+}
+/* assume P non constant */
+GEN
+logmax_modulus_bound(GEN P)
+{
+  (void)RgX_valrem_inexact(P,&P);
+  return lg(P)==3? gen_0: dblexp(logmax_modulus(P, 0.01) + 0.01);
+}
+
+/* log of modulus of the smallest root of p, with relative error tau */
+static double
+logmin_modulus(GEN p, double tau)
+{
+  pari_sp av = avma;
+  double r;
+
+  if (gequal0(gel(p,2))) return -pariINFINITY;
+  r = - logmax_modulus(RgX_recip_shallow(p),tau);
+  avma = av; return r;
+}
+
+/* return the log of the k-th modulus (ascending order) of p, rel. error tau*/
+static double
+logmodulus(GEN p, long k, double tau)
+{
+  GEN q;
+  long i, kk = k, imax, n = degpol(p), nn, bit, e;
+  pari_sp av, ltop=avma;
+  double r, tau2 = tau/6;
+
+  bit = (long)(n * (2. + log2(3.*n/tau2)));
+  av = avma;
+  q = gprec_w(p, nbits2prec(bit));
+  q = RgX_gtofp_bit(q, bit);
+  e = newton_polygon(q,k);
+  r = (double)e;
+  homothetie2n(q,e);
+  imax = (long)(log2(3./tau) + log2(log(4.*n)))+1;
+  for (i=1; i<imax; i++)
+  {
+    q = eval_rel_pol(q,bit);
+    kk -= RgX_valrem(q, &q);
+    nn = degpol(q);
+
+    set_karasquare_limit(bit);
+    q = gerepileupto(av, graeffe(q));
+    e = newton_polygon(q,kk);
+    r += e / exp2((double)i);
+    q = RgX_gtofp_bit(q, bit);
+    homothetie2n(q,e);
+
+    tau2 *= 1.5; if (tau2 > 1.) tau2 = 1.;
+    bit = 1 + (long)(nn*(2. + log2(3.*nn/tau2)));
+  }
+  avma = ltop; return -r * LOG2;
+}
+
+/* return the log of the k-th modulus r_k of p, rel. error tau, knowing that
+ * rmin < r_k < rmax. This information helps because we may reduce precision
+ * quicker */
+static double
+logpre_modulus(GEN p, long k, double tau, double lrmin, double lrmax)
+{
+  GEN q;
+  long n = degpol(p), i, imax, imax2, bit;
+  pari_sp ltop = avma, av;
+  double lrho, aux, tau2 = tau/6.;
+
+  aux = (lrmax - lrmin) / 2. + 4*tau2;
+  imax = (long) log2(log((double)n)/ aux);
+  if (imax <= 0) return logmodulus(p,k,tau);
+
+  lrho  = (lrmin + lrmax) / 2;
+  av = avma;
+  bit = (long)(n*(2. + aux / LOG2 - log2(tau2)));
+  q = homothetie(p, lrho, bit);
+  imax2 = (long)(log2(3./tau * log(4.*n))) + 1;
+  if (imax > imax2) imax = imax2;
+
+  for (i=0; i<imax; i++)
+  {
+    q = eval_rel_pol(q,bit);
+    set_karasquare_limit(bit);
+    q = gerepileupto(av, graeffe(q));
+    aux = 2*aux + 2*tau2;
+    tau2 *= 1.5;
+    bit = (long)(n*(2. + aux / LOG2 - log2(1-exp(-tau2))));
+    q = RgX_gtofp_bit(q, bit);
+  }
+  aux = exp2((double)imax);
+  aux = logmodulus(q,k, aux*tau/3.) / aux;
+  avma = ltop; return lrho + aux;
+}
+
+static double
+ind_maxlog2(GEN q)
+{
+  long i, k = -1;
+  double L = - pariINFINITY;
+  for (i=0; i<=degpol(q); i++)
+  {
+    double d = dbllog2(gel(q,2+i));
+    if (d > L) { L = d; k = i; }
+  }
+  return k;
+}
+
+/* Returns k such that r_k e^(-tau) < R < r_{k+1} e^tau.
+ * Assume that l <= k <= n-l */
+static long
+dual_modulus(GEN p, double lrho, double tau, long l)
+{
+  long i, imax, delta_k = 0, n = degpol(p), nn, v2, v, bit, ll = l;
+  double tau2 = tau * 7./8.;
+  pari_sp av = avma;
+  GEN q;
+
+  bit = 6*n - 5*l + (long)(n*(-log2(tau2) + tau2 * 8./7.));
+  q = homothetie(p, lrho, bit);
+  imax = (long)(log(log(2.*n)/tau2)/log(7./4.)+1);
+
+  for (i=0; i<imax; i++)
+  {
+    q = eval_rel_pol(q,bit); v2 = n - degpol(q);
+    v = RgX_valrem(q, &q);
+    ll -= maxss(v, v2); if (ll < 0) ll = 0;
+
+    nn = degpol(q); delta_k += v;
+    if (!nn) return delta_k;
+
+    set_karasquare_limit(bit);
+    q = gerepileupto(av, graeffe(q));
+    tau2 *= 7./4.;
+    bit = 6*nn - 5*ll + (long)(nn*(-log2(tau2) + tau2 * 8./7.));
+  }
+  avma = av; return delta_k + (long)ind_maxlog2(q);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**              FACTORS THROUGH CIRCLE INTEGRATION                **/
+/**                                                                **/
+/********************************************************************/
+/* l power of 2 */
+static void
+fft(GEN Omega, GEN p, GEN f, long step, long l)
+{
+  pari_sp ltop;
+  long i, l1, l2, l3, rapi, step4;
+  GEN f1, f2, f3, f02, f13, g02, g13, ff;
+
+  if (l == 2)
+  {
+    gel(f,0) = gadd(gel(p,0),gel(p,step));
+    gel(f,1) = gsub(gel(p,0),gel(p,step)); return;
+  }
+  if (l == 4)
+  {
+    f1 = gadd(gel(p,0),   gel(p,step<<1));
+    f2 = gsub(gel(p,0),   gel(p,step<<1));
+    f3 = gadd(gel(p,step),gel(p,3*step));
+    f02= gsub(gel(p,step),gel(p,3*step));
+    f02 = mulcxI(f02);
+    gel(f,0) = gadd(f1, f3);
+    gel(f,1) = gadd(f2, f02);
+    gel(f,2) = gsub(f1, f3);
+    gel(f,3) = gsub(f2, f02); return;
+  }
+
+  ltop = avma;
+  l1 = l>>2; l2 = 2*l1; l3 = l1+l2; step4 = step<<2;
+  fft(Omega,p,          f,   step4,l1);
+  fft(Omega,p+step,     f+l1,step4,l1);
+  fft(Omega,p+(step<<1),f+l2,step4,l1);
+  fft(Omega,p+3*step,   f+l3,step4,l1);
+
+  ff = cgetg(l+1,t_VEC);
+  for (i=0; i<l1; i++)
+  {
+    rapi = step*i;
+    f1 = gmul(gel(Omega,rapi),    gel(f,i+l1));
+    f2 = gmul(gel(Omega,rapi<<1), gel(f,i+l2));
+    f3 = gmul(gel(Omega,3*rapi),  gel(f,i+l3));
+
+    f02 = gadd(gel(f,i),f2);
+    g02 = gsub(gel(f,i),f2);
+    f13 = gadd(f1,f3);
+    g13 = mulcxI(gsub(f1,f3));
+
+    gel(ff,i+1)    = gadd(f02, f13);
+    gel(ff,i+l1+1) = gadd(g02, g13);
+    gel(ff,i+l2+1) = gsub(f02, f13);
+    gel(ff,i+l3+1) = gsub(g02, g13);
+  }
+  ff = gerepilecopy(ltop,ff);
+  for (i=0; i<l; i++) f[i] = ff[i+1];
+}
+
+/* e(1/N) */
+static GEN
+RUgen(long N, long bit)
+{
+  if (N == 2) return real_m1(nbits2prec(bit));
+  if (N == 4) return gen_I();
+  return expIr(divru(Pi2n(1, nbits2prec(bit)), N));
+}
+
+/* N=2^k. returns a vector RU which contains exp(2*i*k*Pi/N), k=0..N-1 */
+static GEN
+initRU(long N, long bit)
+{
+  GEN *RU, z = RUgen(N, bit);
+  long i, N2 = (N>>1), N4 = (N>>2), N8 = (N>>3);
+
+  RU = (GEN*)cgetg(N+1,t_VEC); RU++;
+
+  RU[0] = myreal_1(bit);
+  RU[1] = z;
+  for (i=1; i<N8; i++)
+  {
+    GEN t = RU[i];
+    RU[i+1] = gmul(z, t);
+    RU[N4-i] = mkcomplex(gel(t,2), gel(t,1));
+  }
+  for (i=0; i<N4; i++) RU[i+N4] = mulcxI(RU[i]);
+  for (i=0; i<N2; i++) RU[i+N2] = gneg(RU[i]);
+  return (GEN)RU;
+}
+
+/* as above, N arbitrary */
+static GEN
+initRUgen(long N, long bit)
+{
+  GEN *RU = (GEN*)cgetg(N+1,t_VEC), z = RUgen(N,bit);
+  long i, k = (N+3)>>1;
+  RU[0] = gen_1;
+  RU[1] = z;
+  for (i=2; i<k; i++) RU[i] = gmul(z, RU[i-1]);
+  for (   ; i<N; i++) RU[i] = gconj(RU[N-i]);
+  return (GEN)RU;
+}
+
+GEN
+FFTinit(long k, long prec)
+{
+  if (k <= 0) pari_err_DOMAIN("FFTinit", "k", "<=", gen_0, stoi(k));
+  return initRU(1L << k, prec2nbits(prec)) - 1;
+}
+
+GEN
+FFT(GEN x, GEN Omega)
+{
+  long i, l = lg(Omega), n = lg(x);
+  GEN y, z;
+  if (!is_vec_t(typ(x))) pari_err_TYPE("FFT",x);
+  if (typ(Omega) != t_VEC) pari_err_TYPE("FFT",Omega);
+  if (n > l) pari_err_DIM("FFT");
+
+  if (n < l) {
+    z = cgetg(l, t_VECSMALL); /* cf stackdummy */
+    for (i = 1; i < n; i++) z[i] = x[i];
+    for (     ; i < l; i++) gel(z,i) = gen_0;
+  }
+  else z = x;
+  y = cgetg(l, t_VEC);
+  fft(Omega+1, z+1, y+1, 1, l-1);
+  return y;
+}
+
+/* returns 1 if p has only real coefficients, 0 else */
+static int
+isreal(GEN p)
+{
+  long i;
+  for (i = lg(p)-1; i > 1; i--)
+    if (typ(gel(p,i)) == t_COMPLEX) return 0;
+  return 1;
+}
+
+/* x non complex */
+static GEN
+abs_update_r(GEN x, double *mu) {
+  GEN y = gtofp(x, DEFAULTPREC);
+  double ly = mydbllogr(y); if (ly < *mu) *mu = ly;
+  setabssign(y); return y;
+}
+/* return |x|, low accuracy. Set *mu = min(log(y), *mu) */
+static GEN
+abs_update(GEN x, double *mu) {
+  GEN y, xr, yr;
+  double ly;
+  if (typ(x) != t_COMPLEX) return abs_update_r(x, mu);
+  xr = gel(x,1);
+  yr = gel(x,2);
+  if (gequal0(xr)) return abs_update_r(yr,mu);
+  if (gequal0(yr)) return abs_update_r(xr,mu);
+  /* have to treat 0 specially: 0E-10 + 1e-20 = 0E-10 */
+  xr = gtofp(xr, DEFAULTPREC);
+  yr = gtofp(yr, DEFAULTPREC);
+  y = sqrtr(addrr(sqrr(xr), sqrr(yr)));
+  ly = mydbllogr(y); if (ly < *mu) *mu = ly;
+  return y;
+}
+
+static void
+parameters(GEN p, long *LMAX, double *mu, double *gamma,
+           int polreal, double param, double param2)
+{
+  GEN q, pc, Omega, A, RU, prim, g, ONE,TWO;
+  long n = degpol(p), bit, NN, K, i, j, Lmax;
+  pari_sp av2, av = avma, lim = stack_lim(av, 1);
+
+  bit = gexpo(p) + (long)param2+8;
+  Lmax = 4; while (Lmax <= n) Lmax <<= 1;
+  NN = (long)(param*3.14)+1; if (NN < Lmax) NN = Lmax;
+  K = NN/Lmax; if (K & 1) K++;
+  NN = Lmax*K;
+  if (polreal) K = K/2+1;
+
+  Omega = initRU(Lmax,bit);
+  prim = RUgen(NN, bit);
+
+  q = mygprec(p,bit) + 2;
+  A = cgetg(Lmax+1,t_VEC); A++;
+  pc= cgetg(Lmax+1,t_VEC); pc++;
+  for (i=0; i <= n; i++) gel(pc,i)= gel(q,i);
+  for (   ; i<Lmax; i++) gel(pc,i) = gen_0;
+
+  *mu = pariINFINITY;
+  g = real_0_bit(-bit);
+  ONE = real_1(DEFAULTPREC);
+  TWO = real2n(1, DEFAULTPREC);
+  av2 = avma;
+  RU = myreal_1(bit);
+  for (i=0; i<K; i++)
+  {
+    if (i) {
+      GEN z = RU;
+      for (j=1; j<n; j++)
+      {
+        gel(pc,j) = gmul(gel(q,j),z);
+        z = gmul(z,RU); /* RU = prim^i, z=prim^(ij) */
+      }
+      gel(pc,n) = gmul(gel(q,n),z);
+    }
+
+    fft(Omega,pc,A,1,Lmax);
+    if (polreal && i>0 && i<K-1)
+      for (j=0; j<Lmax; j++) g = addrr(g, divrr(TWO, abs_update(gel(A,j),mu)));
+    else
+      for (j=0; j<Lmax; j++) g = addrr(g, divrr(ONE, abs_update(gel(A,j),mu)));
+    RU = gmul(RU, prim);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"parameters");
+      gerepileall(av2,2, &g,&RU);
+    }
+  }
+  *gamma = mydbllog2r(divru(g,NN));
+  *LMAX = Lmax; avma = av;
+}
+
+/* NN is a multiple of Lmax */
+static void
+dft(GEN p, long k, long NN, long Lmax, long bit, GEN F, GEN H, long polreal)
+{
+  GEN Omega, q, qd, pc, pd, A, B, C, RU, aux, U, W, prim, prim2;
+  long n = degpol(p), i, j, K;
+  pari_sp ltop;
+
+  Omega = initRU(Lmax,bit);
+  prim = RUgen(NN, bit);
+  RU = cgetg(n+2,t_VEC); RU++;
+
+  K = NN/Lmax; if (polreal) K = K/2+1;
+  q = mygprec(p,bit);
+  qd = RgX_deriv(q);
+
+  A = cgetg(Lmax+1,t_VEC); A++;
+  B = cgetg(Lmax+1,t_VEC); B++;
+  C = cgetg(Lmax+1,t_VEC); C++;
+  pc = cgetg(Lmax+1,t_VEC); pc++;
+  pd = cgetg(Lmax+1,t_VEC); pd++;
+  pc[0] = q[2];  for (i=n+1; i<Lmax; i++) gel(pc,i) = gen_0;
+  pd[0] = qd[2]; for (i=n;   i<Lmax; i++) gel(pd,i) = gen_0;
+
+  ltop = avma;
+  W = cgetg(k+1,t_VEC);
+  U = cgetg(k+1,t_VEC);
+  for (i=1; i<=k; i++) gel(W,i) = gel(U,i) = gen_0;
+
+  gel(RU,0) = gen_1;
+  prim2 = myreal_1(bit);
+  for (i=0; i<K; i++)
+  {
+    gel(RU,1) = prim2;
+    for (j=1; j<n; j++) gel(RU,j+1) = gmul(gel(RU,j),prim2);
+    /* RU[j] = prim^(ij)= prim2^j */
+
+    for (j=1; j<n; j++) gel(pd,j) = gmul(gel(qd,j+2),gel(RU,j));
+    fft(Omega,pd,A,1,Lmax);
+    for (j=1; j<=n; j++) gel(pc,j) = gmul(gel(q,j+2),gel(RU,j));
+    fft(Omega,pc,B,1,Lmax);
+    for (j=0; j<Lmax; j++) gel(C,j) = ginv(gel(B,j));
+    for (j=0; j<Lmax; j++) gel(B,j) = gmul(gel(A,j),gel(C,j));
+    fft(Omega,B,A,1,Lmax);
+    fft(Omega,C,B,1,Lmax);
+
+    if (polreal) /* p has real coefficients */
+    {
+      if (i>0 && i<K-1)
+      {
+        for (j=1; j<=k; j++)
+        {
+          gel(W,j) = gadd(gel(W,j), gshift(real_i(gmul(gel(A,j+1),gel(RU,j+1))),1));
+          gel(U,j) = gadd(gel(U,j), gshift(real_i(gmul(gel(B,j),gel(RU,j))),1));
+        }
+      }
+      else
+      {
+        for (j=1; j<=k; j++)
+        {
+          gel(W,j) = gadd(gel(W,j), real_i(gmul(gel(A,j+1),gel(RU,j+1))));
+          gel(U,j) = gadd(gel(U,j), real_i(gmul(gel(B,j),gel(RU,j))));
+        }
+      }
+    }
+    else
+    {
+      for (j=1; j<=k; j++)
+      {
+        gel(W,j) = gadd(gel(W,j), gmul(gel(A,j+1),gel(RU,j+1)));
+        gel(U,j) = gadd(gel(U,j), gmul(gel(B,j),gel(RU,j)));
+      }
+    }
+    prim2 = gmul(prim2,prim);
+    gerepileall(ltop,3, &W,&U,&prim2);
+  }
+
+  for (i=1; i<=k; i++)
+  {
+    aux=gel(W,i);
+    for (j=1; j<i; j++) aux = gadd(aux, gmul(gel(W,i-j),gel(F,k+2-j)));
+    gel(F,k+2-i) = gdivgs(aux,-i*NN);
+  }
+  for (i=0; i<k; i++)
+  {
+    aux=gel(U,k-i);
+    for (j=1+i; j<k; j++) aux = gadd(aux,gmul(gel(F,2+j),gel(U,j-i)));
+    gel(H,i+2) = gdivgs(aux,NN);
+  }
+}
+
+#define NEWTON_MAX 10
+static GEN
+refine_H(GEN F, GEN G, GEN HH, long bit, long Sbit)
+{
+  GEN H = HH, D, aux;
+  pari_sp ltop = avma, lim = stack_lim(ltop, 1);
+  long error, i, bit1, bit2;
+
+  D = Rg_RgX_sub(gen_1, RgX_rem(RgX_mul(H,G),F)); error = gexpo(D);
+  bit2 = bit + Sbit;
+  for (i=0; error>-bit && i<NEWTON_MAX && error<=0; i++)
+  {
+    if (low_stack(lim, stack_lim(ltop,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"refine_H");
+      gerepileall(ltop,2, &D,&H);
+    }
+    bit1 = -error + Sbit;
+    aux = RgX_mul(mygprec(H,bit1), mygprec(D,bit1));
+    aux = RgX_rem(mygprec(aux,bit1), mygprec(F,bit1));
+
+    bit1 = -error*2 + Sbit; if (bit1 > bit2) bit1 = bit2;
+    H = RgX_add(mygprec(H,bit1), aux);
+    D = Rg_RgX_sub(gen_1, RgX_rem(RgX_mul(H,G),F));
+    error = gexpo(D); if (error < -bit1) error = -bit1;
+  }
+  if (error > -bit/2) return NULL; /* FAIL */
+  return gerepilecopy(ltop,H);
+}
+
+/* return 0 if fails, 1 else */
+static long
+refine_F(GEN p, GEN *F, GEN *G, GEN H, long bit, double gamma)
+{
+  GEN f0, FF, GG, r, HH = H;
+  long error, i, bit1 = 0, bit2, Sbit, Sbit2,  enh, normF, normG, n = degpol(p);
+  pari_sp av = avma, lim = stack_lim(av, 1);
+
+  FF = *F; GG = RgX_divrem(p, FF, &r);
+  error = gexpo(r); if (error <= -bit) error = 1-bit;
+  normF = gexpo(FF);
+  normG = gexpo(GG);
+  enh = gexpo(H); if (enh < 0) enh = 0;
+  Sbit = normF + 2*normG + enh + (long)(4.*log2((double)n)+gamma) + 1;
+  Sbit2 = enh + 2*(normF+normG) + (long)(2.*gamma+5.*log2((double)n)) + 1;
+  bit2 = bit + Sbit;
+  for (i=0; error>-bit && i<NEWTON_MAX && error<=0; i++)
+  {
+    if (bit1 == bit2 && i >= 2) { Sbit += n; Sbit2 += n; bit2 += n; }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"refine_F");
+      gerepileall(av,4, &FF,&GG,&r,&HH);
+    }
+
+    bit1 = -error + Sbit2;
+    HH = refine_H(mygprec(FF,bit1), mygprec(GG,bit1), mygprec(HH,bit1),
+                  1-error, Sbit2);
+    if (!HH) return 0; /* FAIL */
+
+    bit1 = -error + Sbit;
+    r = RgX_mul(mygprec(HH,bit1), mygprec(r,bit1));
+    f0 = RgX_rem(mygprec(r,bit1), mygprec(FF,bit1));
+
+    bit1 = -2*error + Sbit; if (bit1 > bit2) bit1 = bit2;
+    FF = gadd(mygprec(FF,bit1),f0);
+
+    bit1 = -3*error + Sbit; if (bit1 > bit2) bit1 = bit2;
+    GG = RgX_divrem(mygprec(p,bit1), mygprec(FF,bit1), &r);
+    error = gexpo(r); if (error < -bit1) error = -bit1;
+  }
+  if (error>-bit) return 0; /* FAIL */
+  *F = FF; *G = GG; return 1;
+}
+
+/* returns F and G from the unit circle U such that |p-FG|<2^(-bit) |cd|,
+where cd is the leading coefficient of p */
+static void
+split_fromU(GEN p, long k, double delta, long bit,
+            GEN *F, GEN *G, double param, double param2)
+{
+  GEN pp, FF, GG, H;
+  long n = degpol(p), NN, bit2, Lmax;
+  int polreal = isreal(p);
+  pari_sp ltop;
+  double mu, gamma;
+
+  pp = gdiv(p, gel(p,2+n));
+  parameters(pp, &Lmax,&mu,&gamma, polreal,param,param2);
+
+  H  = cgetg(k+2,t_POL); H[1] = p[1];
+  FF = cgetg(k+3,t_POL); FF[1]= p[1];
+  gel(FF,k+2) = gen_1;
+
+  NN = (long)(0.5/delta); NN |= 1; if (NN < 2) NN = 2;
+  NN *= Lmax; ltop = avma;
+  for(;;)
+  {
+    bit2 = (long)(((double)NN*delta-mu)/LOG2) + gexpo(pp) + 8;
+    dft(pp, k, NN, Lmax, bit2, FF, H, polreal);
+    if (refine_F(pp,&FF,&GG,H,bit,gamma)) break;
+    NN <<= 1; avma = ltop;
+  }
+  *G = gmul(GG,gel(p,2+n)); *F = FF;
+}
+
+static void
+optimize_split(GEN p, long k, double delta, long bit,
+            GEN *F, GEN *G, double param, double param2)
+{
+  long n = degpol(p);
+  GEN FF, GG;
+
+  if (k <= n/2)
+    split_fromU(p,k,delta,bit,F,G,param,param2);
+  else
+  {
+    split_fromU(RgX_recip_shallow(p),n-k,delta,bit,&FF,&GG,param,param2);
+    *F = RgX_recip_shallow(GG);
+    *G = RgX_recip_shallow(FF);
+  }
+}
+
+/********************************************************************/
+/**                                                                **/
+/**               SEARCH FOR SEPARATING CIRCLE                     **/
+/**                                                                **/
+/********************************************************************/
+
+/* return p(2^e*x) *2^(-n*e) */
+static void
+scalepol2n(GEN p, long e)
+{
+  long i,n=lg(p)-1;
+  for (i=2; i<=n; i++) gel(p,i) = gmul2n(gel(p,i),(i-n)*e);
+}
+
+/* returns p(x/R)*R^n */
+static GEN
+scalepol(GEN p, GEN R, long bit)
+{
+  GEN q,aux,gR;
+  long i;
+
+  aux = gR = mygprec(R,bit); q = mygprec(p,bit);
+  for (i=lg(p)-2; i>=2; i--)
+  {
+    gel(q,i) = gmul(aux,gel(q,i));
+    aux = gmul(aux,gR);
+  }
+  return q;
+}
+
+/* return (conj(a)X-1)^n * p[ (X-a) / (conj(a)X-1) ] */
+static GEN
+conformal_pol(GEN p, GEN a, long bit)
+{
+  GEN z, r, ma = gneg(a), ca = gconj(a);
+  long n = degpol(p), i;
+  pari_sp av = avma, lim = stack_lim(av,2);
+
+  z = mkpoln(2, ca, negr(myreal_1(bit)));
+  r = scalarpol(gel(p,2+n), 0);
+  for (i=n-1; ; i--)
+  {
+    r = addmulXn(r, gmul(ma,r), 1); /* r *= (X - a) */
+    r = gadd(r, gmul(z, gel(p,2+i)));
+    if (i == 0) return gerepileupto(av, r);
+    z = addmulXn(gmul(z,ca), gneg(z), 1); /* z *= conj(a)X - 1 */
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"conformal_pol");
+      gerepileall(av,2, &r,&z);
+    }
+  }
+}
+
+static const double UNDEF = -100000.;
+
+static double
+logradius(double *radii, GEN p, long k, double aux, double *delta)
+{
+  long i, n = degpol(p);
+  double lrho, lrmin, lrmax;
+  if (k > 1)
+  {
+    i = k-1; while (i>0 && radii[i] == UNDEF) i--;
+    lrmin = logpre_modulus(p,k,aux, radii[i], radii[k]);
+  }
+  else /* k=1 */
+    lrmin = logmin_modulus(p,aux);
+  radii[k] = lrmin;
+
+  if (k+1<n)
+  {
+    i = k+2; while (i<=n && radii[i] == UNDEF) i++;
+    lrmax = logpre_modulus(p,k+1,aux, radii[k+1], radii[i]);
+  }
+  else /* k+1=n */
+    lrmax = logmax_modulus(p,aux);
+  radii[k+1] = lrmax;
+
+  lrho = radii[k];
+  for (i=k-1; i>=1; i--)
+  {
+    if (radii[i] == UNDEF || radii[i] > lrho)
+      radii[i] = lrho;
+    else
+      lrho = radii[i];
+  }
+  lrho = radii[k+1];
+  for (i=k+1; i<=n; i++)
+  {
+    if (radii[i] == UNDEF || radii[i] < lrho)
+      radii[i] = lrho;
+    else
+      lrho = radii[i];
+  }
+  *delta = (lrmax - lrmin) / 2;
+  if (*delta > 1.) *delta = 1.;
+  return (lrmin + lrmax) / 2;
+}
+
+static void
+update_radius(long n, double *radii, double lrho, double *par, double *par2)
+{
+  double t, param = 0., param2 = 0.;
+  long i;
+  for (i=1; i<=n; i++)
+  {
+    radii[i] -= lrho;
+    t = fabs(rtodbl( invr(subsr(1, dblexp(radii[i]))) ));
+    param += t; if (t > 1.) param2 += log2(t);
+  }
+  *par = param; *par2 = param2;
+}
+
+/* apply the conformal mapping then split from U */
+static void
+conformal_mapping(double *radii, GEN ctr, GEN p, long k, long bit,
+                  double aux, GEN *F,GEN *G)
+{
+  long bit2, n = degpol(p), i;
+  pari_sp ltop = avma, av;
+  GEN q, FF, GG, a, R;
+  double lrho, delta, param, param2;
+  /* n * (2.*log2(2.732)+log2(1.5)) + 1 */
+  bit2 = bit + (long)(n*3.4848775) + 1;
+  a = sqrtr_abs( stor(3, 2*MEDDEFAULTPREC - 2) );
+  a = divrs(a, -6);
+  a = gmul(mygprec(a,bit2), mygprec(ctr,bit2)); /* a = -ctr/2sqrt(3) */
+
+  av = avma;
+  q = conformal_pol(mygprec(p,bit2), a, bit2);
+  for (i=1; i<=n; i++)
+    if (radii[i] != UNDEF) /* update array radii */
+    {
+      pari_sp av2 = avma;
+      GEN t, r = dblexp(radii[i]), r2 = sqrr(r);
+      /* 2(r^2 - 1) / (r^2 - 3(r-1)) */
+      t = divrr(shiftr((subrs(r2,1)),1), subrr(r2, mulur(3,subrs(r,1))));
+      radii[i] = mydbllogr(addsr(1,t)) / 2;
+      avma = av2;
+    }
+  lrho = logradius(radii, q,k,aux/10., &delta);
+  update_radius(n, radii, lrho, &param, &param2);
+
+  bit2 += (long)(n * fabs(lrho)/LOG2 + 1.);
+  R = mygprec(dblexp(-lrho), bit2);
+  q = scalepol(q,R,bit2);
+  gerepileall(av,2, &q,&R);
+
+  optimize_split(q,k,delta,bit2,&FF,&GG,param,param2);
+  bit2 += n; R = invr(R);
+  FF = scalepol(FF,R,bit2);
+  GG = scalepol(GG,R,bit2);
+
+  a = mygprec(a,bit2);
+  FF = conformal_pol(FF,a,bit2);
+  GG = conformal_pol(GG,a,bit2);
+
+  a = invr(subsr(1, gnorm(a)));
+  FF = RgX_Rg_mul(FF, powru(a,k));
+  GG = RgX_Rg_mul(GG, powru(a,n-k));
+
+  *F = mygprec(FF,bit+n);
+  *G = mygprec(GG,bit+n); gerepileall(ltop,2, F,G);
+}
+
+/* split p, this time without scaling. returns in F and G two polynomials
+ * such that |p-FG|< 2^(-bit)|p| */
+static void
+split_2(GEN p, long bit, GEN ctr, double thickness, GEN *F, GEN *G)
+{
+  GEN q, FF, GG, R;
+  double aux, delta, param, param2;
+  long n = degpol(p), i, j, k, bit2;
+  double lrmin, lrmax, lrho, *radii;
+
+  init_dalloc();
+  radii = (double*) stack_malloc((n+1) * sizeof(double));
+
+  for (i=2; i<n; i++) radii[i] = UNDEF;
+  aux = thickness/(double)(4 * n);
+  lrmin = logmin_modulus(p, aux);
+  lrmax = logmax_modulus(p, aux);
+  radii[1] = lrmin;
+  radii[n] = lrmax;
+  i = 1; j = n;
+  lrho = (lrmin + lrmax) / 2;
+  k = dual_modulus(p, lrho, aux, 1);
+  if (5*k < n || (n < 2*k && 5*k < 4*n))
+    { lrmax = lrho; j=k+1; radii[j] = lrho; }
+  else
+    { lrmin = lrho; i=k;   radii[i] = lrho; }
+  while (j > i+1)
+  {
+    if (i+j == n+1)
+      lrho = (lrmin + lrmax) / 2;
+    else
+    {
+      double kappa = 2. - log(1. + minss(i,n-j)) / log(1. + minss(j,n-i));
+      if (i+j < n+1) lrho = lrmax * kappa + lrmin;
+      else           lrho = lrmin * kappa + lrmax;
+      lrho /= 1+kappa;
+    }
+    aux = (lrmax - lrmin) / (4*(j-i));
+    k = dual_modulus(p, lrho, aux, minss(i,n+1-j));
+    if (k-i < j-k-1 || (k-i == j-k-1 && 2*k > n))
+      { lrmax = lrho; j=k+1; radii[j] = lrho - aux; }
+    else
+      { lrmin = lrho; i=k;   radii[i] = lrho + aux; }
+  }
+  aux = lrmax - lrmin;
+
+  if (ctr)
+  {
+    lrho = (lrmax + lrmin) / 2;
+    for (i=1; i<=n; i++)
+      if (radii[i] != UNDEF) radii[i] -= lrho;
+
+    bit2 = bit + (long)(n * fabs(lrho)/LOG2 + 1.);
+    R = mygprec(dblexp(-lrho), bit2);
+    q = scalepol(p,R,bit2);
+    conformal_mapping(radii, ctr, q, k, bit2, aux, &FF, &GG);
+  }
+  else
+  {
+    lrho = logradius(radii, p, k, aux/10., &delta);
+    update_radius(n, radii, lrho, &param, &param2);
+
+    bit2 = bit + (long)(n * fabs(lrho)/LOG2 + 1.);
+    R = mygprec(dblexp(-lrho), bit2);
+    q = scalepol(p,R,bit2);
+    optimize_split(q, k, delta, bit2, &FF, &GG, param, param2);
+  }
+  bit  += n;
+  bit2 += n; R = invr(mygprec(R,bit2));
+  *F = mygprec(scalepol(FF,R,bit2), bit);
+  *G = mygprec(scalepol(GG,R,bit2), bit);
+}
+
+/* procedure corresponding to steps 5,6,.. page 44 in RR n. 1852 */
+/* put in F and G two polynomial such that |p-FG|<2^(-bit)|p|
+ * where the maximum modulus of the roots of p is <=1.
+ * Assume sum of roots is 0. */
+static void
+split_1(GEN p, long bit, GEN *F, GEN *G)
+{
+  long i, imax, n = degpol(p), polreal = isreal(p), ep = gexpo(p), bit2 = bit+n;
+  GEN TWO, ctr, q, qq, FF, GG, v, gr, r, newq;
+  double lrmin, lrmax, lthick;
+  const double LOG3 = 1.098613;
+
+  lrmax = logmax_modulus(p, 0.01);
+  gr = mygprec(dblexp(-lrmax), bit2);
+  q = scalepol(p,gr,bit2);
+
+  bit2 = bit + gexpo(q) - ep + (long)((double)n*2.*log2(3.)+1);
+  TWO = myreal_1(bit2); setexpo(TWO,1);
+  v = cgetg(5,t_VEC);
+  gel(v,1) = TWO;
+  gel(v,2) = negr(TWO);
+  gel(v,3) = mkcomplex(gen_0, gel(v,1));
+  gel(v,4) = mkcomplex(gen_0, gel(v,2));
+  q = mygprec(q,bit2); lthick = 0;
+  newq = ctr = NULL; /* -Wall */
+  imax = polreal? 3: 4;
+  for (i=1; i<=imax; i++)
+  {
+    qq = RgX_translate(q, gel(v,i));
+    lrmin = logmin_modulus(qq,0.05);
+    if (LOG3 > lrmin + lthick)
+    {
+      double lquo = logmax_modulus(qq,0.05) - lrmin;
+      if (lquo > lthick) { lthick = lquo; newq = qq; ctr = gel(v,i); }
+    }
+    if (lthick > LOG2) break;
+    if (polreal && i==2 && lthick > LOG3 - LOG2) break;
+  }
+  bit2 = bit + gexpo(newq) - ep + (long)(n*LOG3/LOG2 + 1);
+  split_2(newq, bit2, ctr, lthick, &FF, &GG);
+  r = gneg(mygprec(ctr,bit2));
+  FF = RgX_translate(FF,r);
+  GG = RgX_translate(GG,r);
+
+  gr = invr(gr); bit2 = bit - ep + gexpo(FF)+gexpo(GG);
+  *F = scalepol(FF,gr,bit2);
+  *G = scalepol(GG,gr,bit2);
+}
+
+/* put in F and G two polynomials such that |P-FG|<2^(-bit)|P|,
+where the maximum modulus of the roots of p is < 0.5 */
+static int
+split_0_2(GEN p, long bit, GEN *F, GEN *G)
+{
+  GEN q, b, FF, GG;
+  long n = degpol(p), k, bit2, eq;
+  double aux = dbllog2(gel(p,n+1)) - dbllog2(gel(p,n+2));
+
+  /* beware double overflow */
+  if (aux >= 0 && (aux > 1e4 || exp2(aux) > 2.5*n)) return 0;
+
+  aux = (aux < -300)? 0.: n*log2(1 + exp2(aux)/(double)n);
+  bit2 = bit+1 + (long)(log2((double)n) + aux);
+
+  q = mygprec(p,bit2);
+  b = gdivgs(gdiv(gel(q,n+1),gel(q,n+2)),-n);
+  q = RgX_translate(q,b); gel(q,n+1) = gen_0; eq = gexpo(q);
+  k = 0;
+  while (k <= n/2 && (- gexpo(gel(q,k+2)) > bit2 + 2*(n-k) + eq
+                      || gequal0(gel(q,k+2)))) k++;
+  if (k > 0)
+  {
+    if (k > n/2) k = n/2;
+    bit2 += k<<1;
+    FF = monomial(myreal_1(bit2), k, 0);
+    GG = RgX_shift_shallow(q, -k);
+  }
+  else
+  {
+    split_1(q,bit2,&FF,&GG);
+    bit2 = bit + gexpo(FF) + gexpo(GG) - gexpo(p) + (long)aux+1;
+    FF = mygprec(FF,bit2);
+  }
+  GG = mygprec(GG,bit2); b = mygprec(gneg(b),bit2);
+  *F = RgX_translate(FF, b);
+  *G = RgX_translate(GG, b); return 1;
+}
+
+/* put in F and G two polynomials such that |P-FG|<2^(-bit)|P|.
+ * Assume max_modulus(p) < 2 */
+static void
+split_0_1(GEN p, long bit, GEN *F, GEN *G)
+{
+  GEN FF, GG;
+  long n, bit2, normp;
+
+  if  (split_0_2(p,bit,F,G)) return;
+
+  normp = gexpo(p);
+  scalepol2n(p,2); /* p := 4^(-n) p(4*x) */
+  n = degpol(p); bit2 = bit + 2*n + gexpo(p) - normp;
+  split_1(mygprec(p,bit2), bit2,&FF,&GG);
+  scalepol2n(FF,-2);
+  scalepol2n(GG,-2); bit2 = bit + gexpo(FF) + gexpo(GG) - normp;
+  *F = mygprec(FF,bit2);
+  *G = mygprec(GG,bit2);
+}
+
+/* put in F and G two polynomials such that |P-FG|<2^(-bit)|P| */
+static void
+split_0(GEN p, long bit, GEN *F, GEN *G)
+{
+  const double LOG1_9 = 0.6418539;
+  long n = degpol(p), k = 0;
+  GEN q;
+
+  while (gexpo(gel(p,k+2)) < -bit && k <= n/2) k++;
+  if (k > 0)
+  {
+    if (k > n/2) k = n/2;
+    *F = monomial(myreal_1(bit), k, 0);
+    *G = RgX_shift_shallow(p, -k);
+  }
+  else
+  {
+    double lr = logmax_modulus(p, 0.05);
+    if (lr < LOG1_9) split_0_1(p, bit, F, G);
+    else
+    {
+      q = RgX_recip_shallow(p);
+      lr = logmax_modulus(q,0.05);
+      if (lr < LOG1_9)
+      {
+        split_0_1(q, bit, F, G);
+        *F = RgX_recip_shallow(*F);
+        *G = RgX_recip_shallow(*G);
+      }
+      else
+        split_2(p,bit,NULL, 1.2837,F,G);
+    }
+  }
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                ERROR ESTIMATE FOR THE ROOTS                    **/
+/**                                                                **/
+/********************************************************************/
+
+static GEN
+root_error(long n, long k, GEN roots_pol, long err, GEN shatzle)
+{
+  GEN rho, d, eps, epsbis, eps2, aux, rap = NULL;
+  long i, j;
+
+  d = cgetg(n+1,t_VEC);
+  for (i=1; i<=n; i++)
+  {
+    if (i!=k)
+    {
+      aux = gsub(gel(roots_pol,i), gel(roots_pol,k));
+      gel(d,i) = gabs(mygprec(aux,31), DEFAULTPREC);
+    }
+  }
+  rho = gabs(mygprec(gel(roots_pol,k),31), DEFAULTPREC);
+  if (expo(rho) < 0) rho = real_1(DEFAULTPREC);
+  eps = mulrr(rho, shatzle);
+  aux = shiftr(powru(rho,n), err);
+
+  for (j=1; j<=2 || (j<=5 && cmprr(rap, dbltor(1.2)) > 0); j++)
+  {
+    GEN prod = NULL; /* 1. */
+    long m = n;
+    epsbis = mulrr(eps, dbltor(1.25));
+    for (i=1; i<=n; i++)
+    {
+      if (i != k && cmprr(gel(d,i),epsbis) > 0)
+      {
+        GEN dif = subrr(gel(d,i),eps);
+        prod = prod? mulrr(prod, dif): dif;
+        m--;
+      }
+    }
+    eps2 = prod? divrr(aux, prod): aux;
+    if (m > 1) eps2 = sqrtnr(shiftr(eps2, 2*m-2), m);
+    rap = divrr(eps,eps2); eps = eps2;
+  }
+  return eps;
+}
+
+/* round a complex or real number x to an absolute value of 2^(-bit) */
+static GEN
+mygprec_absolute(GEN x, long bit)
+{
+  long e;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      e = expo(x) + bit;
+      return (e <= 0 || !signe(x))? real_0_bit(-bit): rtor(x, nbits2prec(e));
+    case t_COMPLEX:
+      if (gexpo(gel(x,2)) < -bit) return mygprec_absolute(gel(x,1),bit);
+      y = cgetg(3,t_COMPLEX);
+      gel(y,1) = mygprec_absolute(gel(x,1),bit);
+      gel(y,2) = mygprec_absolute(gel(x,2),bit);
+      return y;
+    default: return x;
+  }
+}
+
+static long
+a_posteriori_errors(GEN p, GEN roots_pol, long err)
+{
+  long i, n = degpol(p), e_max = -(long)EXPOBITS;
+  GEN sigma, shatzle;
+
+  err += (long)log2((double)n) + 1;
+  if (err > -2) return 0;
+  sigma = real2n(-err, LOWDEFAULTPREC);
+  /*  2 / ((s - 1)^(1/n) - 1) */
+  shatzle = divur(2, subrs(sqrtnr(subrs(sigma,1),n), 1));
+  for (i=1; i<=n; i++)
+  {
+    pari_sp av = avma;
+    GEN x = root_error(n,i,roots_pol,err,shatzle);
+    long e = gexpo(x);
+    avma = av; if (e > e_max) e_max = e;
+    gel(roots_pol,i) = mygprec_absolute(gel(roots_pol,i), -e);
+  }
+  return e_max;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           MAIN                                 **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+append_clone(GEN r, GEN a) { a = gclone(a); vectrunc_append(r, a); return a; }
+
+/* put roots in placeholder roots_pol so that |P - L_1...L_n| < 2^(-bit)|P|
+ * returns prod (x-roots_pol[i]) */
+static GEN
+split_complete(GEN p, long bit, GEN roots_pol)
+{
+  long n = degpol(p);
+  pari_sp ltop;
+  GEN p1, F, G, a, b, m1, m2;
+
+  if (n == 1)
+  {
+    a = gneg_i(gdiv(gel(p,2), gel(p,3)));
+    (void)append_clone(roots_pol,a); return p;
+  }
+  ltop = avma;
+  if (n == 2)
+  {
+    F = gsub(gsqr(gel(p,3)), gmul2n(gmul(gel(p,2),gel(p,4)), 2));
+    F = gsqrt(F, nbits2prec(bit));
+    p1 = ginv(gmul2n(gel(p,4),1));
+    a = gneg_i(gmul(gadd(F,gel(p,3)), p1));
+    b =        gmul(gsub(F,gel(p,3)), p1);
+    a = append_clone(roots_pol,a);
+    b = append_clone(roots_pol,b); avma = ltop;
+    a = mygprec(a, 3*bit);
+    b = mygprec(b, 3*bit);
+    return gmul(gel(p,4), mkpoln(3, gen_1, gneg(gadd(a,b)), gmul(a,b)));
+  }
+  split_0(p,bit,&F,&G);
+  m1 = split_complete(F,bit,roots_pol);
+  m2 = split_complete(G,bit,roots_pol);
+  return gerepileupto(ltop, gmul(m1,m2));
+}
+
+static GEN
+quickabs(GEN x)
+{
+  const long prec = DEFAULTPREC;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_INT: y = itor(x, prec); setabssign(y); return y;
+    case t_REAL: y = rtor(x, prec); setabssign(y); return y;
+    case t_FRAC: y = fractor(x, prec); setabssign(y); return y;
+    case t_COMPLEX: {
+      GEN a = gel(x,1), b = gel(x,2);
+      /* avoid problem with 0, e.g. x = 0 + I*1e-100. We don't want |x| = 0. */
+      if (isintzero(a)) return cxcompotor(b, prec);
+      if (isintzero(b)) return cxcompotor(a, prec);
+      a = cxcompotor(a, prec);
+      b = cxcompotor(b, prec); return sqrtr(addrr(sqrr(a), sqrr(b)));
+    }
+    default: pari_err_TYPE("quickabs",x);
+      return NULL;/*not reached*/
+  }
+
+}
+
+/* bound ln |largest root of p| */
+double
+cauchy_bound(GEN p)
+{
+  pari_sp av = avma;
+  long i, n = degpol(p);
+  GEN invlc;
+  double Lmax = -pariINFINITY;
+
+  if (n <= 0) pari_err_CONSTPOL("cauchy_bound");
+  invlc = invr( quickabs(gel(p,n+2)) ); /* 1 / |lc(p)| */
+  for (i = 0; i < n; i++)
+  {
+    GEN y = gel(p,i+2);
+    double L;
+    if (gequal0(y)) continue;
+    L = mydbllogr(mulrr(quickabs(y), invlc)) / (n-i);
+    if (L > Lmax) Lmax = L;
+  }
+  avma = av; return Lmax + LOG2;
+}
+
+static GEN
+mygprecrc_special(GEN x, long prec, long e)
+{
+  GEN y;
+  switch(typ(x))
+  {
+    case t_REAL:
+      if (!signe(x)) return real_0_bit(minss(e, expo(x)));
+      return (prec > realprec(x))? rtor(x, prec): x;
+    case t_COMPLEX:
+      y = cgetg(3,t_COMPLEX);
+      gel(y,1) = mygprecrc_special(gel(x,1),prec,e);
+      gel(y,2) = mygprecrc_special(gel(x,2),prec,e);
+      return y;
+    default: return x;
+  }
+}
+
+/* like mygprec but keep at least the same precision as before */
+static GEN
+mygprec_special(GEN x, long bit)
+{
+  long lx, i, e, prec;
+  GEN y;
+
+  if (bit < 0) bit = 0; /* should not happen */
+  e = gexpo(x) - bit;
+  prec = nbits2prec(bit);
+  switch(typ(x))
+  {
+    case t_POL:
+      y = cgetg_copy(x, &lx); y[1] = x[1];
+      for (i=2; i<lx; i++) gel(y,i) = mygprecrc_special(gel(x,i),prec,e);
+      break;
+
+    default: y = mygprecrc_special(x,prec,e);
+  }
+  return y;
+}
+
+static GEN
+fix_roots1(GEN r)
+{
+  long i, l = lg(r);
+  GEN allr = cgetg(l, t_VEC);
+  for (i=1; i<l; i++)
+  {
+    GEN t = gel(r,i);
+    gel(allr,i) = gcopy(t); gunclone(t);
+  }
+  return allr;
+}
+static GEN
+fix_roots(GEN r, GEN *m, long h, long bit)
+{
+  long i, j, k, l, prec;
+  GEN allr, ro1;
+  if (h == 1) return fix_roots1(r);
+  ro1 = initRUgen(h, bit);
+  prec = nbits2prec(bit);
+  l = lg(r)-1;
+  allr = cgetg(h*l+1, t_VEC);
+  for (k=1,i=1; i<=l; i++)
+  {
+    GEN p2, p1 = gel(r,i);
+    p2 = (h == 2)? gsqrt(p1, prec): gsqrtn(p1, utoipos(h), NULL, prec);
+    for (j=0; j<h; j++) gel(allr,k++) = gmul(p2, gel(ro1,j));
+    gunclone(p1);
+  }
+  *m = roots_to_pol(allr, 0);
+  return allr;
+}
+
+static GEN
+all_roots(GEN p, long bit)
+{
+  GEN lc, pd, q, roots_pol, m;
+  long bit0,  bit2, n = degpol(p), i, e, h;
+  pari_sp av;
+
+  pd = RgX_deflate_max(p, &h); lc = leading_term(pd);
+  e = (long)((2/LOG2) * cauchy_bound(pd)); if (e < 0) e = 0;
+  bit0 = bit + gexpo(pd) - gexpo(lc) + (long)log2(n/h)+1+e;
+  bit2 = bit0; e = 0;
+  for (av=avma,i=1;; i++,avma=av)
+  {
+    roots_pol = vectrunc_init(n+1);
+    bit2 += e + (n << i);
+    q = RgX_gtofp_bit(mygprec(pd,bit2), bit2);
+    q[1] = evalsigne(1)|evalvarn(0);
+    m = split_complete(q,bit2,roots_pol);
+    roots_pol = fix_roots(roots_pol, &m, h, bit2);
+    q = mygprec_special(p,bit2); lc = leading_term(q);
+    q[1] = evalsigne(1)|evalvarn(0);
+    if (h > 1) m = gmul(m,lc);
+
+    e = gexpo(gsub(q, m)) - gexpo(lc) + (long)log2((double)n) + 1;
+    if (e < -2*bit2) e = -2*bit2; /* avoid e = -pariINFINITY */
+    if (e < 0)
+    {
+      e = bit + a_posteriori_errors(p,roots_pol,e);
+      if (e < 0) return roots_pol;
+    }
+    if (DEBUGLEVEL > 7)
+      err_printf("all_roots: restarting, i = %ld, e = %ld\n", i,e);
+  }
+}
+
+INLINE int
+isexactscalar(GEN x) { long tx = typ(x); return is_rational_t(tx); }
+
+static int
+isexactpol(GEN p)
+{
+  long i,n = degpol(p);
+  for (i=0; i<=n; i++)
+    if (!isexactscalar(gel(p,i+2))) return 0;
+  return 1;
+}
+
+static long
+isvalidcoeff(GEN x)
+{
+  switch (typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC: return 1;
+    case t_COMPLEX: return isvalidcoeff(gel(x,1)) && isvalidcoeff(gel(x,2));
+  }
+  return 0;
+}
+
+static void
+checkvalidpol(GEN p)
+{
+  long i,n = lg(p);
+  for (i=2; i<n; i++)
+    if (!isvalidcoeff(gel(p,i))) pari_err_TYPE("roots", gel(p,i));
+}
+
+static GEN
+solve_exact_pol(GEN p, long bit)
+{
+  long i, j, k, m, n = degpol(p), iroots = 0;
+  GEN ex, factors, v = zerovec(n);
+
+  factors = ZX_squff(Q_primpart(p), &ex);
+  for (i=1; i<lg(factors); i++)
+  {
+    GEN roots_fact = all_roots(gel(factors,i), bit);
+    n = degpol(gel(factors,i));
+    m = ex[i];
+    for (j=1; j<=n; j++)
+      for (k=1; k<=m; k++) v[++iroots] = roots_fact[j];
+  }
+  return v;
+}
+
+/* return the roots of p with absolute error bit */
+static GEN
+roots_com(GEN q, long bit)
+{
+  GEN L, p;
+  long v = RgX_valrem_inexact(q, &p);
+  if (lg(p) == 3) L = cgetg(1,t_VEC); /* constant polynomial */
+  else L = isexactpol(p)? solve_exact_pol(p,bit): all_roots(p,bit);
+  if (v)
+  {
+    GEN M, z, t = gel(q,2);
+    long i, x, y, l, n;
+
+    if (isrationalzero(t)) x = -bit;
+    else
+    {
+      n = gexpo(t);
+      x = n / v; l = degpol(q);
+      for (i = v; i <= l; i++)
+      {
+        t  = gel(q,i+2);
+        if (isrationalzero(t)) continue;
+        y = (n - gexpo(t)) / i;
+        if (y < x) x = y;
+      }
+    }
+    z = real_0_bit(x); l = v + lg(L);
+    M = cgetg(l, t_VEC); L -= v;
+    for (i = 1; i <= v; i++) gel(M,i) = z;
+    for (     ; i <  l; i++) gel(M,i) = gel(L,i);
+    L = M;
+  }
+  return L;
+}
+
+static GEN
+tocomplex(GEN x, long l, long bit)
+{
+  GEN y;
+  if (typ(x) == t_COMPLEX)
+  {
+    if (signe(gel(x,1))) return mygprecrc(x, l, -bit);
+    x = gel(x,2);
+    y = cgetg(3,t_COMPLEX);
+    gel(y,1) = real_0_bit(-bit);
+    gel(y,2) = mygprecrc(x, l, -bit);
+  }
+  else
+  {
+    y = cgetg(3,t_COMPLEX);
+    gel(y,1) = mygprecrc(x, l, -bit);
+    gel(y,2) = real_0_bit(-bit);
+  }
+  return y;
+}
+
+/* x,y are t_COMPLEX of t_REALs or t_REAL, compare lexicographically,
+ * up to 2^-e absolute error */
+static int
+cmp_complex_appr(void *E, GEN x, GEN y)
+{
+  long e = (long)E;
+  GEN z, xi, yi, xr, yr;
+  long sxi, syi;
+  if (typ(x) == t_COMPLEX) { xr = gel(x,1); xi = gel(x,2); sxi = signe(xi); }
+  else { xr = x; xi = NULL; sxi = 0; }
+  if (typ(y) == t_COMPLEX) { yr = gel(y,1); yi = gel(y,2); syi = signe(yi); }
+  else { yr = y; yi = NULL; syi = 0; }
+  /* Compare absolute values of imaginary parts */
+  if (!sxi)
+  {
+    if (syi && expo(yi) >= e) return -1;
+    /* |Im x| ~ |Im y| ~ 0 */
+  }
+  else if (!syi)
+  {
+    if (sxi && expo(xi) >= e) return 1;
+    /* |Im x| ~ |Im y| ~ 0 */
+  }
+  else
+  {
+    long sz;
+    z = addrr_sign(xi, 1, yi, -1);
+    sz = signe(z);
+    if (sz && expo(z) >= e) return (int)sz;
+  }
+  /* |Im x| ~ |Im y|, sort according to real parts */
+  z = subrr(xr, yr);
+  if (expo(z) >= e) return (int)signe(z);
+  /* Re x ~ Re y. Place negative absolute value before positive */
+  return (int) (sxi - syi);
+}
+
+static GEN
+clean_roots(GEN L, long l, long bit, long clean)
+{
+  long i, n = lg(L), ex = 5 - bit;
+  GEN res = cgetg(n,t_COL);
+  for (i=1; i<n; i++)
+  {
+    GEN c = gel(L,i);
+    if (clean && isrealappr(c,ex))
+    {
+      if (typ(c) == t_COMPLEX) c = gel(c,1);
+      c = mygprecrc(c, l, -bit);
+    }
+    else
+      c = tocomplex(c, l, bit);
+    gel(res,i) = c;
+  }
+  gen_sort_inplace(res, (void*)ex, &cmp_complex_appr, NULL);
+  return res;
+}
+
+/* the vector of roots of p, with absolute error 2^(- prec2nbits(l)) */
+static GEN
+roots_aux(GEN p, long l, long clean)
+{
+  pari_sp av = avma;
+  long bit;
+  GEN L;
+
+  if (typ(p) != t_POL)
+  {
+    if (gequal0(p)) pari_err_ROOTS0("roots");
+    if (!isvalidcoeff(p)) pari_err_TYPE("roots",p);
+    return cgetg(1,t_COL); /* constant polynomial */
+  }
+  if (!signe(p)) pari_err_ROOTS0("roots");
+  checkvalidpol(p);
+  if (lg(p) == 3) return cgetg(1,t_COL); /* constant polynomial */
+  if (l < LOWDEFAULTPREC) l = LOWDEFAULTPREC;
+  bit = prec2nbits(l);
+  L = roots_com(p, bit);
+  return gerepileupto(av, clean_roots(L, l, bit, clean));
+}
+GEN
+roots(GEN p, long l) { return roots_aux(p,l, 0); }
+/* clean up roots. If root is real replace it by its real part */
+GEN
+cleanroots(GEN p, long l) { return roots_aux(p,l, 1); }
+
+/* private variant of conjvec. Allow non rational coefficients, shallow
+ * function. */
+GEN
+polmod_to_embed(GEN x, long prec)
+{
+  GEN v, T = gel(x,1), A = gel(x,2);
+  long i, l;
+  if (typ(A) != t_POL || varn(A) != varn(T))
+  {
+    checkvalidpol(T);
+    return const_col(degpol(T), A);
+  }
+  v = cleanroots(T,prec); l = lg(v);
+  for (i=1; i<l; i++) gel(v,i) = poleval(A,gel(v,i));
+  return v;
+}
+
+GEN
+QX_complex_roots(GEN p, long l)
+{
+  pari_sp av = avma;
+  long bit;
+  GEN L;
+
+  if (!signe(p)) pari_err_ROOTS0("QX_complex_roots");
+  if (lg(p) == 3) return cgetg(1,t_COL); /* constant polynomial */
+  if (l < LOWDEFAULTPREC) l = LOWDEFAULTPREC;
+  bit = prec2nbits(l);
+  L = all_roots(Q_primpart(p), bit);
+  return gerepileupto(av, clean_roots(L, l, bit, 1));
+}
diff --git a/src/basemath/subcyclo.c b/src/basemath/subcyclo.c
new file mode 100644
index 0000000..c91b24b
--- /dev/null
+++ b/src/basemath/subcyclo.c
@@ -0,0 +1,993 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/*************************************************************************/
+/**                                                                     **/
+/**              Routines for handling subgroups of (Z/nZ)^*            **/
+/**              without requiring discrete logarithms.                 **/
+/**                                                                     **/
+/*************************************************************************/
+/* Subgroups are [gen,ord,bits] where
+ * gen is a vecsmall of generators
+ * ord is theirs relative orders
+ * bits is a bit vector of the elements, of length(n). */
+
+/*The algorithm is similar to testpermutation*/
+static void
+znstar_partial_coset_func(long n, GEN H, void (*func)(void *data,long c)
+    , void *data, long d, long c)
+{
+  GEN gen, ord, cache;
+  long i, j, card;
+
+  if (!d) { (*func)(data,c); return; }
+
+  cache = const_vecsmall(d,c);
+  (*func)(data,c);  /* AFTER cache: may contain gerepileupto statement */
+  gen = gel(H,1);
+  ord = gel(H,2);
+  card = ord[1]; for (i = 2; i <= d; i++) card *= ord[i];
+  for(i=1; i<card; i++)
+  {
+    long k, m = i;
+    for(j=1; j<d && m%ord[j]==0 ;j++) m /= ord[j];
+    cache[j] = Fl_mul(cache[j],gen[j],n);
+    for (k=1; k<j; k++) cache[k] = cache[j];
+    (*func)(data, cache[j]);
+  }
+}
+
+static void
+znstar_coset_func(long n, GEN H, void (*func)(void *data,long c)
+    , void *data, long c)
+{
+  znstar_partial_coset_func(n, H, func,data, lg(gel(H,1))-1, c);
+}
+
+/* Add the element of the bitvec of the coset c modulo the subgroup of H
+ * generated by the first d generators to the bitvec bits.*/
+
+static void
+znstar_partial_coset_bits_inplace(long n, GEN H, GEN bits, long d, long c)
+{
+  pari_sp av = avma;
+  znstar_partial_coset_func(n,H, (void (*)(void *,long)) &F2v_set,
+      (void *) bits, d, c);
+  avma = av;
+}
+
+static void
+znstar_coset_bits_inplace(long n, GEN H, GEN bits, long c)
+{
+  znstar_partial_coset_bits_inplace(n, H, bits, lg(gel(H,1))-1, c);
+}
+
+static GEN
+znstar_partial_coset_bits(long n, GEN H, long d, long c)
+{
+  GEN bits = zero_F2v(n);
+  znstar_partial_coset_bits_inplace(n,H,bits,d,c);
+  return bits;
+}
+
+/* Compute the bitvec of the elements of the subgroup of H generated by the
+ * first d generators.*/
+static GEN
+znstar_partial_bits(long n, GEN H, long d)
+{
+  return znstar_partial_coset_bits(n, H, d, 1);
+}
+
+/* Compute the bitvec of the elements of H. */
+GEN
+znstar_bits(long n, GEN H)
+{
+  return znstar_partial_bits(n,H,lg(gel(H,1))-1);
+}
+
+/* Compute the subgroup of (Z/nZ)^* generated by the elements of
+ * the vecsmall V */
+GEN
+znstar_generate(long n, GEN V)
+{
+  pari_sp av = avma;
+  GEN gen = cgetg(lg(V),t_VECSMALL);
+  GEN ord = cgetg(lg(V),t_VECSMALL), res = mkvec2(gen,ord);
+  GEN bits = znstar_partial_bits(n,NULL,0);
+  long i, r = 0;
+  for(i=1; i<lg(V); i++)
+  {
+    ulong v = (ulong)V[i], g = v;
+    long o = 0;
+    while (!F2v_coeff(bits, (long)g)) { g = Fl_mul(g, v, (ulong)n); o++; }
+    if (!o) continue;
+    r++;
+    gen[r] = v;
+    ord[r] = o+1;
+    cgiv(bits); bits = znstar_partial_bits(n,res,r);
+  }
+  setlg(gen,r+1);
+  setlg(ord,r+1); return gerepilecopy(av, mkvec3(gen,ord,bits));
+}
+
+static ulong
+znstar_order(GEN H) { return zv_prod(gel(H,2)); }
+
+/* Return the lists of element of H.
+ * This can be implemented with znstar_coset_func instead. */
+GEN
+znstar_elts(long n, GEN H)
+{
+  long card = znstar_order(H);
+  GEN gen = gel(H,1), ord = gel(H,2);
+  GEN sg = cgetg(1 + card, t_VECSMALL);
+  long k, j, l;
+  sg[1] = 1;
+  for (j = 1, l = 1; j < lg(gen); j++)
+  {
+    long c = l * (ord[j]-1);
+    for (k = 1; k <= c; k++) sg[++l] = Fl_mul(sg[k], gen[j], n);
+  }
+  vecsmall_sort(sg); return sg;
+}
+
+/* Take a znstar H and n dividing the modulus of H.
+ * Output H reduced to modulus n */
+GEN
+znstar_reduce_modulus(GEN H, long n)
+{
+  pari_sp ltop=avma;
+  GEN gen=cgetg(lgcols(H),t_VECSMALL);
+  long i;
+  for(i=1; i < lg(gen); i++)
+    gen[i] = mael(H,1,i)%n;
+  return gerepileupto(ltop, znstar_generate(n,gen));
+}
+
+/* Compute conductor of H */
+long
+znstar_conductor(long n, GEN H)
+{
+  pari_sp ltop=avma;
+  long i,j;
+  GEN F = factoru(n), P = gel(F,1), E = gel(F,2);
+  long cnd=n;
+  for(i=nbrows(F);i>0;i--)
+  {
+    long p = P[i], e = E[i], q = n;
+    if (DEBUGLEVEL>=4)
+      err_printf("SubCyclo: testing %ld^%ld\n",p,e);
+    for (  ; e>=1; e--)
+    {
+      long z = 1;
+      q /= p;
+      for (j = 1; j < p; j++)
+      {
+        z += q;
+        if (!F2v_coeff(gel(H,3),z) && ugcd(z,n)==1)
+          break;
+      }
+      if ( j < p )
+      {
+        if (DEBUGLEVEL>=4)
+          err_printf("SubCyclo: %ld not found\n",z);
+        break;
+      }
+      cnd /= p;
+      if (DEBUGLEVEL>=4)
+        err_printf("SubCyclo: new conductor:%ld\n",cnd);
+    }
+  }
+  if (DEBUGLEVEL>=6)
+    err_printf("SubCyclo: conductor:%ld\n",cnd);
+  avma=ltop;
+  return cnd;
+}
+
+/* Compute the orbits of a subgroups of Z/nZ given by a generator
+ * or a set of generators given as a vector.
+ */
+GEN
+znstar_cosets(long n, long phi_n, GEN H)
+{
+  long    k;
+  long    c = 0;
+  long    card   = znstar_order(H);
+  long    index  = phi_n/card;
+  GEN     cosets = cgetg(index+1,t_VECSMALL);
+  pari_sp ltop = avma;
+  GEN     bits   = zero_F2v(n);
+  for (k = 1; k <= index; k++)
+  {
+    for (c++ ; F2v_coeff(bits,c) || ugcd(c,n)!=1; c++);
+    cosets[k]=c;
+    znstar_coset_bits_inplace(n, H, bits, c);
+  }
+  avma=ltop;
+  return cosets;
+}
+
+
+/*************************************************************************/
+/**                                                                     **/
+/**                     znstar/HNF interface                            **/
+/**                                                                     **/
+/*************************************************************************/
+static GEN
+vecmod_to_vecsmall(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) {
+    GEN c = gel(z,i);
+    if (typ(c) == t_INTMOD) c = gel(c,2);
+    x[i] = itos(c);
+  }
+  return x;
+}
+/* Convert a true znstar output by znstar to a `small znstar' */
+GEN
+znstar_small(GEN zn)
+{
+  GEN Z = cgetg(4,t_VEC);
+  gel(Z,1) = icopy(gmael3(zn,3,1,1));
+  gel(Z,2) = vec_to_vecsmall(gel(zn,2));
+  gel(Z,3) = vecmod_to_vecsmall(gel(zn,3)); return Z;
+}
+
+/* Compute generators for the subgroup of (Z/nZ)* given in HNF. */
+GEN
+znstar_hnf_generators(GEN Z, GEN M)
+{
+  long j, h, l = lg(M);
+  GEN gen = cgetg(l, t_VECSMALL);
+  pari_sp ltop = avma;
+  GEN zgen = gel(Z,3);
+  ulong n = itou(gel(Z,1));
+  for (j = 1; j < l; j++)
+  {
+    GEN Mj = gel(M,j);
+    gen[j] = 1;
+    for (h = 1; h < l; h++)
+    {
+      ulong u = itou(gel(Mj,h));
+      if (!u) continue;
+      gen[j] = Fl_mul((ulong)gen[j], Fl_powu(zgen[h], u, n), n);
+    }
+  }
+  avma = ltop; return gen;
+}
+
+GEN
+znstar_hnf(GEN Z, GEN M)
+{
+  return znstar_generate(itos(gel(Z,1)),znstar_hnf_generators(Z,M));
+}
+
+GEN
+znstar_hnf_elts(GEN Z, GEN H)
+{
+  pari_sp ltop = avma;
+  GEN G = znstar_hnf(Z,H);
+  long n = itos(gel(Z,1));
+  return gerepileupto(ltop, znstar_elts(n,G));
+}
+
+/*************************************************************************/
+/**                                                                     **/
+/**                     polsubcyclo                                     **/
+/**                                                                     **/
+/*************************************************************************/
+
+static GEN
+gscycloconductor(GEN g, long n, long flag)
+{
+  if (flag==2)
+  {
+    GEN V = cgetg(3,t_VEC);
+    gel(V,1) = gcopy(g);
+    gel(V,2) = stoi(n); return V;
+  }
+  return g;
+}
+
+static long
+lift_check_modulus(GEN H, long n)
+{
+  long h;
+  switch(typ(H))
+  {
+    case t_INTMOD:
+      if (!equalsi(n, gel(H,1)))
+        pari_err_MODULUS("galoissubcyclo", stoi(n), gel(H,1));
+      H = gel(H,2);
+    case t_INT:
+      h = smodis(H,n);
+      if (ugcd(h,n) != 1) pari_err_COPRIME("galoissubcyclo", H,stoi(n));
+      return h;
+  }
+  pari_err_TYPE("galoissubcyclo [subgroup]", H);
+  return 0;/*not reached*/
+}
+
+/* Compute z^ex using the baby-step/giant-step table powz
+ * with only one multiply.
+ * In the modular case, the result is not reduced. */
+static GEN
+polsubcyclo_powz(GEN powz, long ex)
+{
+  long m = lg(gel(powz,1))-1, q = ex/m, r = ex%m; /*ex=m*q+r*/
+  GEN z = gmul(gmael(powz,1,r+1), gmael(powz,2,q+1));
+  if (lg(powz) == 4) z = greal(z);
+  return z;
+}
+
+static GEN
+polsubcyclo_complex_bound(pari_sp av, GEN V, long prec)
+{
+  GEN pol = real_i(roots_to_pol(V,0));
+  return gerepileuptoint(av, ceil_safe(gsupnorm(pol,prec)));
+}
+
+/* Newton sums mod le. if le==NULL, works with complex instead */
+static GEN
+polsubcyclo_cyclic(long n, long d, long m ,long z, long g, GEN powz, GEN le)
+{
+  GEN V = cgetg(d+1,t_VEC);
+  ulong base = 1;
+  long i,k;
+  pari_timer ti;
+  if (DEBUGLEVEL >= 6) timer_start(&ti);
+  for (i=1; i<=d; i++, base = Fl_mul(base,z,n))
+  {
+    pari_sp av = avma;
+    long ex = base;
+    GEN s = gen_0;
+    for (k=0; k<m; k++, ex = Fl_mul(ex,g,n))
+    {
+      s = gadd(s, polsubcyclo_powz(powz,ex));
+      if ((k&0xff)==0) s = gerepileupto(av,s);
+    }
+    if (le) s = modii(s, le);
+    gel(V,i) = gerepileupto(av, s);
+  }
+  if (DEBUGLEVEL >= 6) timer_printf(&ti, "polsubcyclo_cyclic");
+  return V;
+}
+
+struct _subcyclo_orbits_s
+{
+  GEN powz;
+  GEN *s;
+  ulong count;
+  pari_sp ltop;
+};
+
+static void
+_subcyclo_orbits(struct _subcyclo_orbits_s *data, long k)
+{
+  GEN powz = data->powz;
+  GEN *s = data->s;
+
+  if (!data->count) data->ltop = avma;
+  *s = gadd(*s, polsubcyclo_powz(powz,k));
+  data->count++;
+  if ((data->count & 0xffUL) == 0) *s = gerepileupto(data->ltop, *s);
+}
+
+/* Newton sums mod le. if le==NULL, works with complex instead */
+static GEN
+polsubcyclo_orbits(long n, GEN H, GEN O, GEN powz, GEN le)
+{
+  long i, d = lg(O);
+  GEN V = cgetg(d,t_VEC);
+  struct _subcyclo_orbits_s data;
+  long lle = le?lg(le)*2+1: 2*lg(gmael(powz,1,2))+3;/*dvmdii uses lx+ly space*/
+  data.powz = powz;
+  for(i=1; i<d; i++)
+  {
+    GEN s = gen_0;
+    pari_sp av = avma;
+    (void)new_chunk(lle);
+    data.count = 0;
+    data.s     = &s;
+    znstar_coset_func(n, H, (void (*)(void *,long)) _subcyclo_orbits,
+      (void *) &data, O[i]);
+    avma = av; /* HACK */
+    gel(V,i) = le? modii(s,le): gcopy(s);
+  }
+  return V;
+}
+
+static GEN
+polsubcyclo_start(long n, long d, long o, GEN borne, long *ptr_val,long *ptr_l)
+{
+  pari_sp av;
+  GEN le, z, gl;
+  long i, l, e, val;
+  l = n+1; e = 1;
+  while(!uisprime(l)) { l += n; e++; }
+  if (DEBUGLEVEL >= 4) err_printf("Subcyclo: prime l=%ld\n",l);
+  gl = utoipos(l); av = avma;
+  if (!borne)
+  { /* Use vecmax(Vec((x+o)^d)) = max{binomial(d,i)*o^i ;1<=i<=d} */
+    i = d-(1+d)/(1+o);
+    borne = mulii(binomial(utoipos(d),i),powuu(o,i));
+  }
+  if (DEBUGLEVEL >= 4) err_printf("Subcyclo: bound=2^%ld\n",expi(borne));
+  val = logint(shifti(borne,2), gl, NULL);
+  avma = av;
+  if (DEBUGLEVEL >= 4) err_printf("Subcyclo: val=%ld\n",val);
+  le = powiu(gl,val);
+  z = utoipos( Fl_powu(pgener_Fl(l), e, l) );
+  z = Zp_sqrtnlift(gen_1,utoipos(n),z,gl,val);
+  *ptr_val = val;
+  *ptr_l = l;
+  return gmodulo(z,le);
+}
+
+/*Fill in the powz table:
+ *  powz[1]: baby-step
+ *  powz[2]: giant-step
+ *  powz[3] exists only if the field is real (value is ignored). */
+static GEN
+polsubcyclo_complex_roots(long n, long real, long prec)
+{
+  long i, m = (long)(1+sqrt((double) n));
+  GEN bab, gig, powz = cgetg(real?4:3, t_VEC);
+
+  bab = cgetg(m+1,t_VEC);
+  gel(bab,1) = gen_1;
+  gel(bab,2) = expIr(divru(Pi2n(1, prec), n)); /* = e_n(1) */
+  for (i=3; i<=m; i++) gel(bab,i) = gmul(gel(bab,2),gel(bab,i-1));
+  gig = cgetg(m+1,t_VEC);
+  gel(gig,1) = gen_1;
+  gel(gig,2) = gmul(gel(bab,2),gel(bab,m));;
+  for (i=3; i<=m; i++) gel(gig,i) = gmul(gel(gig,2),gel(gig,i-1));
+  gel(powz,1) = bab;
+  gel(powz,2) = gig;
+  if (real) gel(powz,3) = gen_0;
+  return powz;
+}
+
+static GEN
+muliimod_sz(GEN x, GEN y, GEN l, long siz)
+{
+  pari_sp av = avma;
+  GEN p1;
+  (void)new_chunk(siz); /* HACK */
+  p1 = mulii(x,y);
+  avma = av; return modii(p1,l);
+}
+
+static GEN
+polsubcyclo_roots(long n, GEN zl)
+{
+  GEN le = gel(zl,1), z = gel(zl,2);
+  long i, lle = lg(le)*3; /*Assume dvmdii use lx+ly space*/
+  long m = (long)(1+sqrt((double) n));
+  GEN bab, gig, powz = cgetg(3,t_VEC);
+  pari_timer ti;
+  if (DEBUGLEVEL >= 6) timer_start(&ti);
+  bab = cgetg(m+1,t_VEC);
+  gel(bab,1) = gen_1;
+  gel(bab,2) = icopy(z);
+  for (i=3; i<=m; i++) gel(bab,i) = muliimod_sz(z,gel(bab,i-1),le,lle);
+  gig = cgetg(m+1,t_VEC);
+  gel(gig,1) = gen_1;
+  gel(gig,2) = muliimod_sz(z,gel(bab,m),le,lle);;
+  for (i=3; i<=m; i++) gel(gig,i) = muliimod_sz(gel(gig,2),gel(gig,i-1),le,lle);
+  if (DEBUGLEVEL >= 6) timer_printf(&ti, "polsubcyclo_roots");
+  gel(powz,1) = bab;
+  gel(powz,2) = gig; return powz;
+}
+
+GEN
+galoiscyclo(long n, long v)
+{
+  ulong av = avma;
+  GEN grp, G, z, le, L, elts;
+  long val, l, i, j, k;
+  GEN zn = znstar(stoi(n));
+  long card = itos(gel(zn,1));
+  GEN gen = vec_to_vecsmall(lift(gel(zn,3)));
+  GEN ord = gtovecsmall(gel(zn,2));
+
+  z = polsubcyclo_start(n,card/2,2,NULL,&val,&l);
+  le = gel(z,1); z = gel(z,2);
+  L = cgetg(1+card,t_VEC);
+  gel(L,1) = z;
+  for (j = 1, i = 1; j < lg(gen); j++)
+  {
+    long c = i * (ord[j]-1);
+    for (k = 1; k <= c; k++) gel(L,++i) = Fp_powu(gel(L,k), gen[j], le);
+  }
+  G = abelian_group(ord);
+  elts = group_elts(G, card); /*not stack clean*/
+  grp = cgetg(9, t_VEC);
+  gel(grp,1) = polcyclo(n,v);
+  gel(grp,2) = mkvec3(stoi(l), stoi(val), icopy(le));
+  gel(grp,3) = gcopy(L);
+  gel(grp,4) = vandermondeinversemod(L, gel(grp,1), gen_1, le);
+  gel(grp,5) = gen_1;
+  gel(grp,6) = gcopy(elts);
+  gel(grp,7) = gcopy(gel(G,1));
+  gel(grp,8) = gcopy(gel(G,2));
+  return gerepileupto(av, grp);
+}
+
+/* Convert a bnrinit(Q,n) to a znstar(n)
+ * complex is set to 0 if the bnr is real and to 1 if it is complex.
+ * Not stack clean */
+GEN
+bnr_to_znstar(GEN bnr, long *complex)
+{
+  GEN gen, cond, v, bid;
+  long l2, i;
+  checkbnr(bnr);
+  bid = bnr_get_bid(bnr);
+  gen = bnr_get_gen(bnr);
+  if (nf_get_degree(bnr_get_nf(bnr)) != 1)
+    pari_err_DOMAIN("bnr_to_znstar", "bnr", "!=", strtoGENstr("Q"), bnr);
+  /* cond is the finite part of the conductor,
+   * complex is the infinite part*/
+  cond = gcoeff(bid_get_ideal(bid), 1, 1);
+  *complex = signe(gel(bid_get_arch(bid), 1));
+  l2 = lg(gen);
+  v = cgetg(l2, t_VEC);
+  for (i = 1; i < l2; ++i)
+  {
+    GEN x = gel(gen,i);
+    switch(typ(x))
+    {
+      case t_MAT: x = gcoeff(x,1,1); break;
+      case t_COL: x = gel(x,1); break;
+    }
+    gel(v,i) = gmodulo(absi(x), cond);
+  }
+  return mkvec3(bnr_get_no(bnr), bnr_get_cyc(bnr), v);
+}
+
+GEN
+galoissubcyclo(GEN N, GEN sg, long flag, long v)
+{
+  pari_sp ltop= avma, av;
+  GEN H, V, B, zl, L, T, le, powz, O, Z = NULL;
+  long i, card, phi_n, val,l, n, cnd, complex=1;
+  pari_timer ti;
+
+  if (flag<0 || flag>2) pari_err_FLAG("galoissubcyclo");
+  if (v < 0) v = 0;
+  if (!sg) sg = gen_1;
+  switch(typ(N))
+  {
+    case t_INT:
+      n = itos(N);
+      if (n < 1)
+        pari_err_DOMAIN("galoissubcyclo", "degree", "<=", gen_0, stoi(n));
+      break;
+    case t_VEC:
+      if (lg(N)==7) N = bnr_to_znstar(N,&complex);
+      if (lg(N)==4)
+      { /* znstar */
+        GEN gen = gel(N,3);
+        Z = N;
+        if (typ(gen)!=t_VEC) pari_err_TYPE("galoissubcyclo",gen);
+        if (lg(gen) == 1) n = 1;
+        else
+        {
+          GEN z = gel(gen,1);
+          n = itos(gel(z,1));
+        }
+        break;
+      }
+    default: /*fall through*/
+      pari_err_TYPE("galoissubcyclo",N);
+      return NULL;/*Not reached*/
+  }
+  if (n==1) { avma = ltop; return deg1pol_shallow(gen_1,gen_m1,v); }
+
+  switch(typ(sg))
+  {
+     case t_INTMOD: case t_INT:
+      V = mkvecsmall( lift_check_modulus(sg,n) );
+      break;
+    case t_VECSMALL:
+      V = gcopy(sg);
+      for (i=1; i<lg(V); i++) { V[i] %= n; if (V[i] < 0) V[i] += n; }
+      break;
+    case t_VEC:
+    case t_COL:
+      V = cgetg(lg(sg),t_VECSMALL);
+      for(i=1;i<lg(sg);i++) V[i] = lift_check_modulus(gel(sg,i),n);
+      break;
+    case t_MAT:
+      if (lg(sg) == 1 || lg(sg) != lgcols(sg))
+        pari_err_TYPE("galoissubcyclo [H not in HNF]", sg);
+      if (!Z) pari_err_TYPE("galoissubcyclo [N not a bnrinit or znstar]", sg);
+      if ( lg(gel(Z,2)) != lg(sg) ) pari_err_DIM("galoissubcyclo");
+      V = znstar_hnf_generators(znstar_small(Z),sg);
+      break;
+    default:
+      pari_err_TYPE("galoissubcyclo",sg);
+      return NULL;/*Not reached*/
+  }
+  if (!complex) V = vecsmall_append(V,n-1); /*add complex conjugation*/
+  H = znstar_generate(n,V);
+  if (DEBUGLEVEL >= 6)
+  {
+    err_printf("Subcyclo: elements:");
+    for (i=1;i<n;i++)
+      if (F2v_coeff(gel(H,3),i)) err_printf(" %ld",i);
+    err_printf("\n");
+  }
+  /* field is real iff z -> conj(z) = z^-1 = z^(n-1) is in H */
+  complex = !F2v_coeff(gel(H,3),n-1);
+  if (DEBUGLEVEL >= 6) err_printf("Subcyclo: complex=%ld\n",complex);
+  if (DEBUGLEVEL >= 1) timer_start(&ti);
+  cnd = znstar_conductor(n,H);
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "znstar_conductor");
+  if (flag == 1)  { avma=ltop; return stoi(cnd); }
+  if (cnd == 1)
+  {
+    avma=  ltop;
+    return gscycloconductor(deg1pol_shallow(gen_1,gen_m1,v),1,flag);
+  }
+  if (n != cnd)
+  {
+    H = znstar_reduce_modulus(H, cnd);
+    n = cnd;
+  }
+  card = znstar_order(H);
+  phi_n = eulerphiu(n);
+  if (card == phi_n)
+  {
+    avma = ltop;
+    return gscycloconductor(polcyclo(n,v),n,flag);
+  }
+  O = znstar_cosets(n, phi_n, H);
+  if (DEBUGLEVEL >= 1) timer_printf(&ti, "znstar_cosets");
+  if (DEBUGLEVEL >= 6) err_printf("Subcyclo: orbits=%Ps\n",O);
+  if (DEBUGLEVEL >= 4)
+    err_printf("Subcyclo: %ld orbits with %ld elements each\n",phi_n/card,card);
+  av = avma;
+  powz = polsubcyclo_complex_roots(n,!complex,LOWDEFAULTPREC);
+  L = polsubcyclo_orbits(n,H,O,powz,NULL);
+  B = polsubcyclo_complex_bound(av,L,LOWDEFAULTPREC);
+  zl = polsubcyclo_start(n,phi_n/card,card,B,&val,&l);
+  powz = polsubcyclo_roots(n,zl);
+  le = gel(zl,1);
+  L = polsubcyclo_orbits(n,H,O,powz,le);
+  if (DEBUGLEVEL >= 6) timer_start(&ti);
+  T = FpV_roots_to_pol(L,le,v);
+  if (DEBUGLEVEL >= 6) timer_printf(&ti, "roots_to_pol");
+  T = FpX_center(T,le,shifti(le,-1));
+  return gerepileupto(ltop, gscycloconductor(T,n,flag));
+}
+
+/* Z = znstar(n) cyclic. n = 1,2,4,p^a or 2p^a,
+ * and d | phi(n) = 1,1,2,(p-1)p^(a-1) */
+static GEN
+polsubcyclo_g(long n, long d, GEN Z, long v)
+{
+  pari_sp ltop = avma;
+  long o, p, r, g, gd, l , val;
+  GEN zl, L, T, le, B, powz;
+  pari_timer ti;
+  if (d==1) return deg1pol_shallow(gen_1,gen_m1,v); /* get rid of n=1,2 */
+  if ((n & 3) == 2) n >>= 1;
+  /* n = 4 or p^a, p odd */
+  o = itos(gel(Z,1));
+  g = itos(gmael3(Z,3,1,2));
+  p = n / ugcd(n,o); /* p^a / gcd(p^a,phi(p^a)) = p*/
+  r = ugcd(d,n); /* = p^(v_p(d)) < n */
+  n = r*p; /* n is now the conductor */
+  o = n-r; /* = phi(n) */
+  if (o == d) return polcyclo(n,v);
+  o /= d;
+  gd = Fl_powu(g%n, d, n);
+  /*FIXME: If degree is small, the computation of B is a waste of time*/
+  powz = polsubcyclo_complex_roots(n,(o&1)==0,LOWDEFAULTPREC);
+  L = polsubcyclo_cyclic(n,d,o,g,gd,powz,NULL);
+  B = polsubcyclo_complex_bound(ltop,L,LOWDEFAULTPREC);
+  zl = polsubcyclo_start(n,d,o,B,&val,&l);
+  le = gel(zl,1);
+  powz = polsubcyclo_roots(n,zl);
+  L = polsubcyclo_cyclic(n,d,o,g,gd,powz,le);
+  if (DEBUGLEVEL >= 6) timer_start(&ti);
+  T = FpV_roots_to_pol(L,le,v);
+  if (DEBUGLEVEL >= 6) timer_printf(&ti, "roots_to_pol");
+  return gerepileupto(ltop, FpX_center(T,le,shifti(le,-1)));
+}
+
+GEN
+polsubcyclo(long n, long d, long v)
+{
+  pari_sp ltop = avma;
+  GEN L, Z;
+  if (v<0) v = 0;
+  if (d<=0) pari_err_DOMAIN("polsubcyclo","d","<=",gen_0,stoi(d));
+  if (n<=0) pari_err_DOMAIN("polsubcyclo","n","<=",gen_0,stoi(n));
+  Z = znstar(stoi(n));
+  if (!dvdis(gel(Z,1), d)) { avma = ltop; return cgetg(1, t_VEC); }
+  if (lg(gel(Z,2)) == 2)
+  { /* faster but Z must be cyclic */
+    avma = ltop;
+    return polsubcyclo_g(n, d, Z, v);
+  }
+  L = subgrouplist(gel(Z,2), mkvec(stoi(d)));
+  if (lg(L) == 2)
+    return gerepileupto(ltop, galoissubcyclo(Z, gel(L,1), 0, v));
+  else
+  {
+    GEN V = cgetg(lg(L),t_VEC);
+    long i;
+    for (i=1; i< lg(V); i++) gel(V,i) = galoissubcyclo(Z, gel(L,i), 0, v);
+    return gerepileupto(ltop, V);
+  }
+}
+
+struct aurifeuille_t {
+  GEN z, le;
+  ulong l;
+  long e;
+};
+
+/* Let z a primitive n-th root of 1, n > 1, A an integer such that
+ * Aurifeuillian factorization of Phi_n(A) exists ( z.A is a square in Q(z) ).
+ * Let G(p) the Gauss sum mod p prime:
+ *      sum_x (x|p) z^(xn/p) for p odd,  i - 1 for p = 2 [ i := z^(n/4) ]
+ * We have N(-1) = Nz = 1 (n != 1,2), and
+ *      G^2 = (-1|p) p for p odd,  G^2 = -2i for p = 2
+ * In particular, for odd A, (-1|A) A = g^2 is a square. If A = prod p^{e_p},
+ * sigma_j(g) = \prod_p (sigma_j G(p)))^e_p = \prod_p (j|p)^e_p g = (j|A) g
+ * n odd  : z^2 is a primitive root, A = g^2
+ *   Phi_n(A) = N(A - z^2) = N(g - z) N(g + z)
+ *
+ * n = 2 (4) : -z^2 is a primitive root, -A = g^2
+ *   Phi_n(A) = N(A - (-z^2)) = N(g^2 - z^2)  [ N(-1) = 1 ]
+ *                            = N(g - z) N(g + z)
+ *
+ * n = 4 (8) : i z^2 primitive root, -Ai = g^2
+ *   Phi_n(A) = N(A - i z^2) = N(-Ai -  z^2) = N(g - z) N(g + z)
+ * sigma_j(g) / g =  (j|A)  if j = 1 (4)
+ *                  (-j|A)i if j = 3 (4)
+ *   */
+/* factor Phi_n(A), Astar: A* = squarefree kernel of A, P = odd prime divisors
+ * of n */
+static GEN
+factor_Aurifeuille_aux(GEN A, long Astar, long n, GEN P,
+                       struct aurifeuille_t *S)
+{
+  pari_sp av;
+  GEN f, a, b, s, powers, z = S->z, le = S->le;
+  long j, k, maxjump, lastj, e = S->e;
+  ulong l = S->l;
+  char *invertible;
+
+  if ((n & 7) == 4)
+  { /* A^* even */
+    GEN i = Fp_powu(z, n>>2, le), z2 = Fp_sqr(z, le);
+
+    invertible = stack_malloc(n); /* even indices unused */
+    for (j = 1; j < n; j+=2) invertible[j] = 1;
+    for (k = 1; k < lg(P); k++)
+    {
+      long p = P[k];
+      for (j = p; j < n; j += 2*p) invertible[j] = 0;
+    }
+    lastj = 1; maxjump = 2;
+    for (j= 3; j < n; j+=2)
+      if (invertible[j]) {
+        long jump = j - lastj;
+        if (jump > maxjump) maxjump = jump;
+        lastj = j;
+      }
+    powers = cgetg(maxjump+1, t_VEC); /* powers[k] = z^k, odd indices unused */
+    gel(powers,2) = z2;
+    for (k = 4; k <= maxjump; k+=2)
+      gel(powers,k) = odd(k>>1)? Fp_mul(gel(powers, k-2), z2, le)
+                               : Fp_sqr(gel(powers, k>>1), le);
+
+    if (Astar == 2)
+    { /* important special case (includes A=2), split for efficiency */
+      if (!equalis(A, 2))
+      {
+        GEN f = sqrti(shifti(A,-1)), mf = Fp_neg(f,le), fi = Fp_mul(f,i,le);
+        a = Fp_add(mf, fi, le);
+        b = Fp_sub(mf, fi, le);
+      }
+      else
+      {
+        a = addsi(-1,i);
+        b = subsi(-1,i);
+      }
+      av = avma;
+      s = z; f = subii(a, s); lastj = 1;
+      for (j = 3, k = 0; j < n; j+=2)
+        if (invertible[j])
+        {
+          s = Fp_mul(gel(powers, j-lastj), s, le); /* z^j */
+          lastj = j;
+          f = Fp_mul(f, subii((j & 3) == 1? a: b, s), le);
+          if (++k == 0x1ff) { gerepileall(av, 2, &s, &f); k = 0; }
+        }
+    }
+    else
+    {
+      GEN ma, mb, B = Fp_mul(A, i, le), gl = utoipos(l);
+      long t;
+      Astar >>= 1;
+      t = Astar & 3; if (Astar < 0) t = 4-t; /* t = 1 or 3 */
+      if (t == 1) B = Fp_neg(B, le);
+      a = Zp_sqrtlift(B, Fp_sqrt(B, gl), gl, e);
+      b = Fp_mul(a, i, le);
+      ma = Fp_neg(a, le);
+      mb = Fp_neg(b, le);
+      av = avma;
+      s = z; f = subii(a, s); lastj = 1;
+      for (j = 3, k = 0; j<n; j+=2)
+        if (invertible[j])
+        {
+          GEN t;
+          if ((j & 3) == 1) t = (kross(j, Astar) < 0)? ma: a;
+          else              t = (kross(j, Astar) < 0)? mb: b;
+          s = Fp_mul(gel(powers, j-lastj), s, le); /* z^j */
+          lastj = j;
+          f = Fp_mul(f, subii(t, s), le);
+          if (++k == 0x1ff) { gerepileall(av, 2, &s, &f); k = 0; }
+        }
+    }
+  }
+  else /* A^* odd */
+  {
+    ulong g;
+    if ((n & 3) == 2)
+    { /* A^* = 3 (mod 4) */
+      A = negi(A); Astar = -Astar;
+      z = Fp_neg(z, le);
+      n >>= 1;
+    }
+    /* A^* = 1 (mod 4) */
+    g = Fl_sqrt(umodiu(A,l), l);
+    a = Zp_sqrtlift(A, utoipos(g), utoipos(l), e);
+    b = negi(a);
+
+    invertible = stack_malloc(n);
+    for (j = 1; j < n; j++) invertible[j] = 1;
+    for (k = 1; k < lg(P); k++)
+    {
+      long p = P[k];
+      for (j = p; j < n; j += p) invertible[j] = 0;
+    }
+    lastj = 2; maxjump = 1;
+    for (j= 3; j < n; j++)
+      if (invertible[j]) {
+        long jump = j - lastj;
+        if (jump > maxjump) maxjump = jump;
+        lastj = j;
+      }
+    powers = cgetg(maxjump+1, t_VEC); /* powers[k] = z^k */
+    gel(powers,1) = z;
+    for (k = 2; k <= maxjump; k++)
+      gel(powers,k) = odd(k)? Fp_mul(gel(powers, k-1), z, le)
+                            : Fp_sqr(gel(powers, k>>1), le);
+    av = avma;
+    s = z; f = subii(a, s); lastj = 1;
+    for(j = 2, k = 0; j < n; j++)
+      if (invertible[j])
+      {
+        s = Fp_mul(gel(powers, j-lastj), s, le);
+        lastj = j;
+        f = Fp_mul(f, subii(kross(j,Astar)==1? a: b, s), le);
+        if (++k == 0x1ff) { gerepileall(av, 2, &s, &f); k = 0; }
+      }
+  }
+  return f;
+}
+
+/* fd = factoru(odd part of d = d or d/4). Return eulerphi(d) */
+static ulong
+phi(long d, GEN fd)
+{
+  GEN P = gel(fd,1), E = gel(fd,2);
+  long i, l = lg(P);
+  ulong phi = 1;
+  for (i = 1; i < l; i++)
+  {
+    ulong p = P[i], e = E[i];
+    phi *= upowuu(p, e-1)*(p-1);
+  }
+  if (!odd(d)) phi <<= 1;
+  return phi;
+}
+
+static void
+Aurifeuille_init(GEN a, long d, GEN fd, struct aurifeuille_t *S)
+{
+  GEN sqrta = sqrtr_abs(itor(a, LOWDEFAULTPREC));
+  GEN bound = ceil_safe(powru(addrs(sqrta,1), phi(d, fd)));
+  GEN zl = polsubcyclo_start(d, 0, 0, bound, &(S->e), (long*)&(S->l));
+  S->le = gel(zl,1);
+  S->z  = gel(zl,2);
+}
+
+GEN
+factor_Aurifeuille_prime(GEN p, long d)
+{
+  pari_sp av = avma;
+  struct aurifeuille_t S;
+  GEN fd;
+  long pp;
+  if ((d & 3) == 2) { d >>= 1; p = negi(p); }
+  fd = factoru(odd(d)? d: d>>2);
+  pp = itos(p);
+  Aurifeuille_init(p, d, fd, &S);
+  return gerepileuptoint(av, factor_Aurifeuille_aux(p, pp, d, gel(fd,1), &S));
+}
+
+/* an algebraic factor of Phi_d(a), a != 0 */
+GEN
+factor_Aurifeuille(GEN a, long d)
+{
+  pari_sp av = avma;
+  GEN fd, P, A;
+  long i, lP, va = vali(a), sa, astar, D;
+  struct aurifeuille_t S;
+
+  if (d <= 0)
+    pari_err_DOMAIN("factor_Aurifeuille", "degre", "<=",gen_0,stoi(d));
+  if ((d & 3) == 2) { d >>= 1; a = negi(a); }
+  if ((va & 1) == (d & 1)) { avma = av; return gen_1; }
+  sa = signe(a);
+  if (odd(d))
+  {
+    long a4;
+    if (d == 1)
+    {
+      if (!Z_issquareall(a, &A)) return gen_1;
+      return gerepileuptoint(av, addis(A,1));
+    }
+    A = va? shifti(a, -va): a;
+    a4 = mod4(A); if (sa < 0) a4 = 4 - a4;
+    if (a4 != 1) { avma = av; return gen_1; }
+  }
+  else if ((d & 7) == 4)
+    A = shifti(a, -va);
+  else
+  {
+    avma = av; return gen_1;
+  }
+  /* v_2(d) = 0 or 2. Kill 2 from factorization (minor efficiency gain) */
+  fd = factoru(odd(d)? d: d>>2); P = gel(fd,1); lP = lg(P);
+  astar = sa;
+  if (odd(va)) astar <<= 1;
+  for (i = 1; i < lP; i++)
+    if (odd( (Z_lvalrem(A, P[i], &A)) ) ) astar *= P[i];
+  if (sa < 0)
+  { /* negate in place if possible */
+    if (A == a) A = icopy(A);
+    setabssign(A);
+  }
+  if (!Z_issquare(A)) { avma = av; return gen_1; }
+
+  D = odd(d)? 1: 4;
+  for (i = 1; i < lP; i++) D *= P[i];
+  if (D != d) { a = powiu(a, d/D); d = D; }
+
+  Aurifeuille_init(a, d, fd, &S);
+  return gerepileuptoint(av, factor_Aurifeuille_aux(a, astar, d, P, &S));
+}
diff --git a/src/basemath/subgroup.c b/src/basemath/subgroup.c
new file mode 100644
index 0000000..cf455a1
--- /dev/null
+++ b/src/basemath/subgroup.c
@@ -0,0 +1,648 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+typedef struct slist {
+  struct slist *next;
+  long *data;
+  long prec;
+} slist;
+
+typedef struct {
+  GEN cyc, gen;
+  ulong count;
+  slist *list;
+} sublist_t;
+
+/* SUBGROUPS
+ * G = Gp x I, with Gp a p-Sylow (I assumed small).
+ * Compute subgroups of I by recursive calls
+ * Loop through subgroups Hp of Gp using Birkhoff's algorithm.
+ * If (I is non trivial)
+ *   lift Hp to G (mul by exponent of I)
+ *   for each subgp of I, lift it to G (mult by exponent of Gp)
+ *   consider the group generated by the two subgroups (concat)
+ *
+ * type(H) = mu --> H = Z/p^mu[1] x ... x Z/p^mu[len(mu)] */
+typedef struct subgp_iter {
+  long *M, *L; /* mu = p-subgroup type, lambda = p-group type */
+  GEN *powlist; /* [i] = p^i, i = 0.. */
+  long *c, *maxc;
+  GEN *a, *maxa, **g, **maxg;
+  long *available;
+  GEN **H; /* p-subgroup of type mu, in matrix form */
+  GEN cyc; /* cyclic factors of G */
+  GEN subq;/* subgrouplist(I) */
+  GEN subqpart; /* J in subq s.t [I:J][Gp:Hp] <= indexbound */
+  GEN bound; /* if != NULL, impose a "bound" on [G:H] (see boundtype) */
+  long boundtype;
+  long countsub; /* number of subgroups of type M (so far) */
+  long count; /* number of p-subgroups so far [updated when M completed] */
+  GEN expoI; /* exponent of I */
+
+  long(*fun)(void*, GEN); /* callback applied to each subgroup */
+  void *fundata; /* data for fun */
+  long stop;
+} subgp_iter;
+
+/* MAX: [G:H] <= bound, EXACT: [G:H] = bound, TYPE: type(H) = bound */
+enum { b_NONE, b_MAX, b_EXACT, b_TYPE };
+
+#define len(x)      (x)[0]
+#define setlen(x,l) len(x)=(l)
+
+static void
+printtyp(const long *typ) /*Used only for ddebugging */
+{
+  long i, l = len(typ);
+  for (i=1; i<=l; i++) err_printf(" %ld ",typ[i]);
+  err_printf("\n");
+}
+
+/* compute conjugate partition of typ */
+static long*
+conjugate(long *typ)
+{
+  long *t, i, k = len(typ), l, last;
+
+  if (!k) { t = new_chunk(1); setlen(t,0); return t; }
+  l = typ[1]; t = new_chunk(l+2);
+  t[1] = k; last = k;
+  for (i=2; i<=l; i++)
+  {
+    while (typ[last] < i) last--;
+    t[i] = last;
+  }
+  t[i] = 0; setlen(t,l);
+  return t;
+}
+/* ----subgp_iter 'fun' associated to subgrouplist ------------- */
+static void
+addcell(sublist_t *S, GEN H)
+{
+  long *pt,i,j,L, n = lg(H)-1;
+  slist *cell;
+
+  L = 3;
+  for (j=1; j<=n; j++)
+  { /* H in HNF, largest entries are on diagonal */
+    long l = lgefint(gcoeff(H,j,j));
+    if (l > L) L = l;
+  }
+  L -= 2;
+  cell = (slist*) pari_malloc(sizeof(slist)
+                              + ((n*(n+1)) >> 1) * sizeof(long) * L);
+  S->list->next = cell; cell->data = pt = (long*) (cell + 1);
+  cell->prec = L;
+  for (j=1; j<=n; j++)
+    for(i=1; i<=j; i++) {
+      GEN z = gcoeff(H,i,j);
+      long h, lz = lgefint(z) - 2;
+      for (h = 0; h < L - lz; h++) *pt++ = 0;
+      for (h = 0; h < lz; h++) *pt++ = z[h+2];
+    }
+  S->list = cell;
+  S->count++;
+}
+
+static long
+list_fun(void *E, GEN x)
+{
+  sublist_t *S = (sublist_t*)E;
+  GEN H = ZM_hnfmodid(x, S->cyc);
+  if (!S->gen || subgroup_conductor_ok(H, S->gen)) addcell(S, H);
+  return 0;
+}
+/* -------------------------------------------------------------- */
+
+/* treat subgroup Hp (not in HNF, T->fun should do it if desired) */
+static void
+treatsub(subgp_iter *T, GEN H)
+{
+  long i;
+  if (!T->subq) {T->stop = T->fun(T->fundata, H); T->countsub++; }
+  else
+  { /* not a p group, add the trivial part */
+    GEN Hp = gmul(T->expoI, H); /* lift H to G */
+    long n = lg(T->subqpart)-1;
+    for (i=1; i<=n; i++)
+      if (T->fun(T->fundata, shallowconcat(Hp, gel(T->subqpart,i))))
+        { T->stop = 1; break; }
+    T->countsub += n;
+  }
+}
+
+/* x a t_INT, x++. Could be optimized... */
+static void
+inc(GEN x) { affii(addis(x,1), x); }
+
+/* assume t>0 and l>1 */
+static void
+dogroup(subgp_iter *T)
+{
+  const GEN *powlist = T->powlist;
+  long *M = T->M;
+  long *L = T->L;
+  long *c = T->c;
+  GEN *a = T->a,  *maxa = T->maxa;
+  GEN **g = T->g, **maxg = T->maxg;
+  GEN **H = T->H;
+  pari_sp av;
+  long i,j,k,r,n,t2,ind, t = len(M), l = len(L);
+
+  t2 = (l==t)? t-1: t;
+  n = t2 * l - (t2*(t2+1))/2; /* number of gamma_ij */
+  for (i=1, r=t+1; ; i++)
+  {
+    if (T->available[i]) c[r++] = i;
+    if (r > l) break;
+  }
+  if (DEBUGLEVEL>6) { err_printf("    column selection:"); printtyp(c); }
+  /* a/g and maxa/maxg access the same data indexed differently */
+  for (ind=0,i=1; i<=t; ind+=(l-i),i++)
+  {
+    maxg[i] = maxa + (ind - (i+1)); /* only access maxg[i][i+1..l] */
+    g[i] = a + (ind - (i+1));
+    for (r=i+1; r<=l; r++)
+      if (c[r] < c[i])         maxg[i][r] = powlist[M[i]-M[r]-1];
+      else if (L[c[r]] < M[i]) maxg[i][r] = powlist[L[c[r]]-M[r]];
+      else                     maxg[i][r] = powlist[M[i]-M[r]];
+  }
+  /* allocate correct lg */
+  for (i = 0; i<= n-1; i++) a[i] = icopy(maxa[i]);
+  affui(0, a[n-1]); for (i=0; i<n-1; i++) affui(1, a[i]);
+  av = avma;
+  for(;!T->stop;)
+  {
+    inc(a[n-1]);
+    if (cmpii(a[n-1], maxa[n-1]) > 0)
+    {
+      j=n-2; while (j>=0 && equalii(a[j], maxa[j])) j--;
+      if (j < 0) return;
+
+      inc(a[j]);
+      for (k=j+1; k<n; k++) affsi(1, a[k]);
+    }
+    for (i=1; i<=t; i++)
+    {
+      for (r=1; r<i; r++) H[i][c[r]] = gen_0;
+      H[i][c[r]] = powlist[L[c[r]] - M[r]];
+      for (r=i+1; r<=l; r++)
+      {
+        GEN e = g[i][r];
+        long d = L[c[r]] - M[i];
+        if (c[r] < c[i])
+          e = mulii(e, powlist[d+1]);
+        else if (d > 0)
+          e = mulii(e, powlist[d]);
+        H[i][c[r]] = e;
+      }
+    }
+    treatsub(T, (GEN)H); avma = av;
+  }
+}
+
+/* T->c[1],...,T->c[r-1] filled */
+static void
+loop(subgp_iter *T, long r)
+{
+  long j;
+
+  if (r > len(T->M)) {
+    pari_sp av = avma; dogroup(T); avma = av;
+    return;
+  }
+
+  if (r!=1 && (T->M[r-1] == T->M[r])) j = T->c[r-1]+1; else j = 1;
+  for (  ; j<=T->maxc[r]; j++)
+    if (T->available[j])
+    {
+      T->c[r] = j;  T->available[j] = 0;
+      loop(T, r+1); T->available[j] = 1;
+    }
+}
+
+static void
+dopsubtyp(subgp_iter *T)
+{
+  pari_sp av = avma;
+  long i,r, l = len(T->L), t = len(T->M);
+
+  if (!t) { treatsub(T, mkmat( zerocol(l) )); avma = av; return; }
+  if (l==1) /* imply t = 1 */
+  {
+    GEN p1 = gtomat(T->powlist[T->L[1]-T->M[1]]);
+    treatsub(T, p1); avma = av; return;
+  }
+  T->c         = new_chunk(l+1); setlen(T->c, l);
+  T->maxc      = new_chunk(l+1);
+  T->available = new_chunk(l+1);
+  T->a   = (GEN*)new_chunk(l*(t+1));
+  T->maxa= (GEN*)new_chunk(l*(t+1));
+  T->g    = (GEN**)new_chunk(t+1);
+  T->maxg = (GEN**)new_chunk(t+1);
+
+  if (DEBUGLEVEL>4) { err_printf("  subgroup:"); printtyp(T->M); }
+  for (i=1; i<=t; i++)
+  {
+    for (r=1; r<=l; r++)
+      if (T->M[i] > T->L[r]) break;
+    T->maxc[i] = r-1;
+  }
+  T->H = (GEN**)cgetg(t+1, t_MAT);
+  for (i=1; i<=t; i++) T->H[i] = (GEN*)cgetg(l+1, t_COL);
+  for (i=1; i<=l; i++) T->available[i]=1;
+  for (i=1; i<=t; i++) T->c[i]=0;
+  /* go through all column selections */
+  loop(T, 1); avma = av; return;
+}
+
+static long
+weight(long *typ)
+{
+  long i, l = len(typ), w = 0;
+  for (i=1; i<=l; i++) w += typ[i];
+  return w;
+}
+
+static void
+dopsub(subgp_iter *T, GEN p, GEN indexsubq)
+{
+  long *M, *L = T->L;
+  long w,i,j,k,lsubq, wG = weight(L), wmin = 0, wmax = wG, n = len(L);
+
+  if (DEBUGLEVEL>4) { err_printf("\ngroup:"); printtyp(L); }
+  T->count = 0;
+  switch(T->boundtype)
+  {
+  case b_MAX: /* upper bound */
+    wmin = (long) (wG - (log(gtodouble(T->bound)) / log(gtodouble(p))));
+    if (cmpii(powiu(p, wG - wmin), T->bound) > 0) wmin++;
+    break;
+  case b_EXACT: /* exact value */
+    wmin = wmax = wG - Z_pval(T->bound, p);
+    break;
+  }
+  T->M = M = new_chunk(n+1);
+  if (T->subq)
+  {
+    lsubq = lg(T->subq);
+    T->subqpart = T->bound? cgetg(lsubq, t_VEC): T->subq;
+  }
+  else
+    lsubq = 0; /* -Wall */
+  M[1] = -1; for (i=2; i<=n; i++) M[i]=0;
+  for(;!T->stop;) /* go through all vectors mu_{i+1} <= mu_i <= lam_i */
+  {
+    M[1]++;
+    if (M[1] > L[1])
+    {
+      for (j=2; j<=n; j++)
+        if (M[j] < L[j] && M[j] < M[j-1]) break;
+      if (j > n) return;
+
+      M[j]++; for (k=1; k<j; k++) M[k]=M[j];
+    }
+    for (j=1; j<=n; j++)
+      if (!M[j]) break;
+    setlen(M, j-1); w = weight(M);
+    if (w >= wmin && w <= wmax)
+    {
+      GEN p1 = gen_1;
+
+      if (T->subq && T->bound) /* G not a p-group */
+      {
+        pari_sp av = avma;
+        GEN indexH = powiu(p, wG - w);
+        GEN B = divii(T->bound, indexH);
+        k = 1;
+        for (i=1; i<lsubq; i++)
+          if (cmpii(gel(indexsubq,i), B) <= 0)
+            T->subqpart[k++] = T->subq[i];
+        setlg(T->subqpart, k); avma = av;
+      }
+      if (DEBUGLEVEL>4)
+      {
+        long *Lp = conjugate(L);
+        long *Mp = conjugate(M);
+        GEN BINMAT = matqpascal(len(L)+1, p);
+
+        if (DEBUGLEVEL>7)
+        {
+          err_printf("    lambda = "); printtyp(L);
+          err_printf("    lambda'= "); printtyp(Lp);
+          err_printf("    mu = "); printtyp(M);
+          err_printf("    mu'= "); printtyp(Mp);
+        }
+        for (j=1; j<=len(Mp); j++)
+        {
+          p1 = mulii(p1, powiu(p, Mp[j+1]*(Lp[j]-Mp[j])));
+          p1 = mulii(p1, gcoeff(BINMAT, Lp[j]-Mp[j+1]+1, Mp[j]-Mp[j+1]+1));
+        }
+        err_printf("  alpha_lambda(mu,p) = %Ps\n",p1);
+      }
+
+      T->countsub = 0; dopsubtyp(T);
+      T->count += T->countsub;
+
+      if (DEBUGLEVEL>4)
+      {
+        err_printf("  countsub = %ld\n", T->countsub);
+        if (T->subq) p1 = muliu(p1,lg(T->subqpart)-1);
+        if (!equaliu(p1,T->countsub))
+        {
+          err_printf("  alpha = %Ps\n",p1);
+          pari_err_BUG("forsubgroup (alpha != countsub)");
+        }
+      }
+    }
+  }
+}
+
+static void
+parse_bound(subgp_iter *T)
+{
+  GEN b, B = T->bound;
+  if (!B) { T->boundtype = b_NONE; return; }
+  switch(typ(B))
+  {
+  case t_INT: /* upper bound */
+    T->boundtype = b_MAX;
+    break;
+  case t_VEC: /* exact value */
+    b = gel(B,1);
+    if (lg(B) != 2 || typ(b) != t_INT) pari_err_TYPE("subgroup", B);
+    T->boundtype = b_EXACT;
+    T->bound = b;
+    break;
+  case t_COL: /* exact type */
+    pari_err_IMPL("exact type in subgrouplist");
+    if (lg(B) > len(T->L)+1) pari_err_TYPE("subgroup",B);
+    T->boundtype = b_TYPE;
+    break;
+  default: pari_err_TYPE("subgroup",B);
+  }
+  if (signe(T->bound) <= 0)
+    pari_err_DOMAIN("subgroup", "index bound", "<=", gen_0, T->bound);
+}
+
+static GEN
+expand_sub(GEN x, long n)
+{
+  long i,j, m = lg(x);
+  GEN p = matid(n-1), q,c;
+
+  for (i=1; i<m; i++)
+  {
+    q = gel(p,i); c = gel(x,i);
+    for (j=1; j<m; j++) gel(q,j) = gel(c,j);
+    for (   ; j<n; j++) gel(q,j) = gen_0;
+  }
+  return p;
+}
+
+static GEN
+init_powlist(long k, GEN p)
+{
+  GEN z = new_chunk(k+1);
+  long i;
+  gel(z,0) = gen_1; gel(z,1) = p;
+  for (i=2; i<=k; i++) gel(z,i) = mulii(p, gel(z,i-1));
+  return z;
+}
+
+static GEN subgrouplist_i(GEN cyc, GEN bound, GEN expoI, GEN gen);
+static void
+subgroup_engine(subgp_iter *T)
+{
+  pari_sp av = avma;
+  GEN B,L,fa,primlist,p,listL,indexsubq = NULL;
+  GEN cyc = T->cyc;
+  long i,j,k,imax,lprim, n = lg(cyc);
+
+  if (typ(cyc) != t_VEC)
+  {
+    if (typ(cyc) != t_MAT) pari_err_TYPE("forsubgroup",cyc);
+    cyc = RgM_diagonal_shallow(cyc);
+  }
+  for (i=1; i<n-1; i++)
+    if (!dvdii(gel(cyc,i), gel(cyc,i+1)))
+      pari_err_TYPE("forsubgroup [not a group]", cyc);
+  if (n == 1) {
+    parse_bound(T);
+    switch(T->boundtype)
+    {
+      case b_EXACT: if (!is_pm1(T->bound)) break;
+      default: T->fun(T->fundata, cyc);
+    }
+    avma = av; return;
+  }
+  if (!signe(gel(cyc,1))) pari_err_TYPE("forsubgroup [infinite group]", cyc);
+  fa = Z_factor(gel(cyc,1)); primlist = gel(fa,1);
+  listL = cgetg_copy(primlist, &lprim);
+  imax = k = 0;
+  for (i=1; i<lprim; i++)
+  {
+    L = new_chunk(n); p = gel(primlist,i);
+    for (j=1; j<n; j++)
+    {
+      L[j] = Z_pval(gel(cyc,j), p);
+      if (!L[j]) break;
+    }
+    j--; setlen(L, j);
+    if (j > k) { k = j; imax = i; }
+    gel(listL,i) = L;
+  }
+  L = gel(listL,imax); p = gel(primlist,imax);
+  k = L[1];
+  T->L = L;
+  T->powlist = (GEN*)init_powlist(k, p);
+  B = T->bound;
+  parse_bound(T);
+
+  if (lprim == 2)
+  {
+    T->subq = NULL;
+    if (T->boundtype == b_EXACT)
+    {
+      (void)Z_pvalrem(T->bound,p,&B);
+      if (!is_pm1(B)) { avma = av; return; }
+    }
+  }
+  else
+  { /* not a p-group */
+    GEN cycI = leafcopy(cyc);
+    long lsubq;
+    for (i=1; i<n; i++)
+    {
+      gel(cycI,i) = divii(gel(cycI,i), T->powlist[L[i]]);
+      if (is_pm1(gel(cycI,i))) break;
+    }
+    setlg(cycI, i); /* cyclic factors of I */
+    if (T->boundtype == b_EXACT)
+    {
+      (void)Z_pvalrem(T->bound,p,&B);
+      B = mkvec(B);
+    }
+    T->expoI = gel(cycI,1);
+    T->subq = subgrouplist_i(cycI, B, T->expoI, NULL);
+
+    lsubq = lg(T->subq);
+    for (i=1; i<lsubq; i++)
+      gel(T->subq,i) = expand_sub(gel(T->subq,i), n);
+    if (T->bound)
+    {
+      indexsubq = cgetg(lsubq,t_VEC);
+      for (i=1; i<lsubq; i++)
+        gel(indexsubq,i) = ZM_det_triangular(gel(T->subq,i));
+    }
+    /* lift subgroups of I to G */
+    for (i=1; i<lsubq; i++)
+      gel(T->subq,i) = gmul(T->powlist[k],gel(T->subq,i));
+    if (DEBUGLEVEL>6)
+      err_printf("(lifted) subgp of prime to %Ps part:\n%Ps\n",p, T->subq);
+  }
+  dopsub(T, p,indexsubq);
+  if (DEBUGLEVEL>4) err_printf("nb subgroup = %ld\n",T->count);
+  avma = av;
+}
+
+static GEN
+get_snf(GEN x, long *N)
+{
+  GEN cyc;
+  long n;
+  switch(typ(x))
+  {
+    case t_MAT:
+      if (!RgM_isdiagonal(x)) return NULL;
+      cyc = RgM_diagonal_shallow(x); break;
+    case t_VEC: if (lg(x) == 4 && typ(gel(x,2)) == t_VEC) x = gel(x,2);
+    case t_COL: cyc = leafcopy(x); break;
+    default: return NULL;
+  }
+  *N = lg(cyc)-1;
+  for (n = *N; n > 0; n--) /* take care of trailing 1s */
+  {
+    GEN c = gel(cyc,n);
+    if (typ(c) != t_INT || signe(c) <= 0) return NULL;
+    if (!is_pm1(c)) break;
+  }
+  setlg(cyc, n+1);
+  for ( ; n > 0; n--)
+  {
+    GEN c = gel(cyc,n);
+    if (typ(c) != t_INT || signe(c) <= 0) return NULL;
+  }
+  return cyc;
+}
+
+void
+forsubgroup(void *E, long call(void*, GEN), GEN cyc, GEN bound)
+{
+  subgp_iter T;
+  long N;
+
+  T.fun = call;
+  T.cyc = get_snf(cyc,&N); if (!T.cyc) pari_err_TYPE("forsubgroup",cyc);
+  T.bound = bound;
+  T.fundata = E;
+  T.stop = 0;
+  subgroup_engine(&T);
+}
+
+void
+forsubgroup0(GEN cyc, GEN bound, GEN code)
+{
+  push_lex(gen_0, code);
+  forsubgroup((void*)code, &gp_evalvoid, cyc, bound);
+  pop_lex(1);
+}
+
+static GEN
+packtoi(long *pt, long L)
+{
+  long i, l;
+  GEN z;
+  for (i=0; i<L; i++, pt++)
+    if (*pt) break;
+  L -= i;
+  if (!L) return gen_0;
+  l = L+2; z = cgeti(l);
+  z[1] = evalsigne(1) | evallgefint(l);
+  for (i = 2; i<l; i++) z[i] = *pt++;
+  return z;
+}
+
+static GEN
+subgrouplist_i(GEN CYC, GEN bound, GEN expoI, GEN gen)
+{
+  pari_sp av = avma;
+  subgp_iter T;
+  sublist_t S;
+  slist *list, *sublist;
+  long ii,i,j,nbsub,n,N = 0; /* -Wall */
+  GEN z, H, cyc;
+
+  cyc = get_snf(CYC, &N);
+  if (!cyc) pari_err_TYPE("subgrouplist",CYC);
+  n = lg(cyc)-1; /* not necessarily = N */
+
+  S.list = sublist = (slist*) pari_malloc(sizeof(slist));
+  S.cyc = cyc;
+  S.gen = gen;
+  S.count = 0;
+  T.fun = &list_fun;
+  T.fundata = (void*)&S;
+  T.stop = 0;
+
+  T.cyc = cyc;
+  T.bound = bound;
+  T.expoI = expoI;
+
+  subgroup_engine(&T);
+
+  nbsub = (long)S.count;
+  avma = av;
+  z = cgetg(nbsub+1,t_VEC);
+  for (ii=1; ii<=nbsub; ii++)
+  {
+    long *pt, L;
+    list = sublist; sublist = list->next; pari_free(list);
+    pt = sublist->data;
+    L = sublist->prec;
+    H = cgetg(N+1,t_MAT); gel(z,ii) = H;
+    for (j=1; j<=n; j++)
+    {
+      gel(H,j) = cgetg(N+1, t_COL);
+      for (i=1; i<=j; i++) { gcoeff(H,i,j) = packtoi(pt, L); pt += L; }
+      for (   ; i<=N; i++) gcoeff(H,i,j) = gen_0;
+    }
+    for (   ; j<=N; j++) gel(H,j) = col_ei(N, j);
+  }
+  pari_free(sublist); return z;
+}
+
+GEN
+subgrouplist(GEN cyc, GEN bound)
+{
+  return subgrouplist_i(cyc,bound,NULL,NULL);
+}
+
+GEN
+subgroupcondlist(GEN cyc, GEN bound, GEN L)
+{
+  return subgrouplist_i(cyc,bound,NULL,L);
+}
diff --git a/src/basemath/trans1.c b/src/basemath/trans1.c
new file mode 100644
index 0000000..76efdec
--- /dev/null
+++ b/src/basemath/trans1.c
@@ -0,0 +1,3170 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                   TRANSCENDENTAL FUNCTIONS                     **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+#ifdef LONG_IS_64BIT
+static const long SQRTVERYBIGINT = 3037000500L; /* ceil(sqrt(LONG_MAX)) */
+#else
+static const long SQRTVERYBIGINT = 46341L;
+#endif
+
+static THREAD GEN gcatalan, geuler, glog2, gpi;
+void
+pari_init_floats(void)
+{
+  gcatalan = geuler = gpi = bernzone = glog2 = NULL;
+}
+
+void
+pari_close_floats(void)
+{
+  if (gcatalan) gunclone(gcatalan);
+  if (geuler) gunclone(geuler);
+  if (gpi) gunclone(gpi);
+  if (bernzone) gunclone(bernzone);
+  if (glog2) gunclone(glog2);
+}
+
+/********************************************************************/
+/**                   GENERIC BINARY SPLITTING                     **/
+/**                    (Haible, Papanikolaou)                      **/
+/********************************************************************/
+void
+abpq_init(struct abpq *A, long n)
+{
+  A->a = (GEN*)new_chunk(n+1);
+  A->b = (GEN*)new_chunk(n+1);
+  A->p = (GEN*)new_chunk(n+1);
+  A->q = (GEN*)new_chunk(n+1);
+}
+static GEN
+mulii3(GEN a, GEN b, GEN c) { return mulii(mulii(a,b),c); }
+static GEN
+mulii4(GEN a, GEN b, GEN c, GEN d) { return mulii(mulii(a,b),mulii(c,d)); }
+
+/* T_{n1,n1+1}, given P = p[n1]p[n1+1] */
+static GEN
+T2(struct abpq *A, long n1, GEN P)
+{
+  GEN u1 = mulii4(A->a[n1], A->p[n1], A->b[n1+1], A->q[n1+1]);
+  GEN u2 = mulii3(A->b[n1],A->a[n1+1], P);
+  return addii(u1, u2);
+}
+
+/* assume n2 > n1. Compute sum_{n1 <= n < n2} a/b(n) p/q(n1)... p/q(n) */
+void
+abpq_sum(struct abpq_res *r, long n1, long n2, struct abpq *A)
+{
+  struct abpq_res L, R;
+  GEN u1, u2;
+  pari_sp av;
+  long n;
+  switch(n2 - n1)
+  {
+    GEN b, p, q;
+    case 1:
+      r->P = A->p[n1];
+      r->Q = A->q[n1];
+      r->B = A->b[n1];
+      r->T = mulii(A->a[n1], A->p[n1]);
+      return;
+    case 2:
+      r->P = mulii(A->p[n1], A->p[n1+1]);
+      r->Q = mulii(A->q[n1], A->q[n1+1]);
+      r->B = mulii(A->b[n1], A->b[n1+1]);
+      av = avma;
+      r->T = gerepileuptoint(av, T2(A, n1, r->P));
+      return;
+
+    case 3:
+      p = mulii(A->p[n1+1], A->p[n1+2]);
+      q = mulii(A->q[n1+1], A->q[n1+2]);
+      b = mulii(A->b[n1+1], A->b[n1+2]);
+      r->P = mulii(A->p[n1], p);
+      r->Q = mulii(A->q[n1], q);
+      r->B = mulii(A->b[n1], b);
+      av = avma;
+      u1 = mulii3(b, q, A->a[n1]);
+      u2 = mulii(A->b[n1], T2(A, n1+1, p));
+      r->T = gerepileuptoint(av, mulii(A->p[n1], addii(u1, u2)));
+      return;
+  }
+
+  av = avma;
+  n = (n1 + n2) >> 1;
+  abpq_sum(&L, n1, n, A);
+  abpq_sum(&R, n, n2, A);
+
+  r->P = mulii(L.P, R.P);
+  r->Q = mulii(L.Q, R.Q);
+  r->B = mulii(L.B, R.B);
+  u1 = mulii3(R.B,R.Q,L.T);
+  u2 = mulii3(L.B,L.P,R.T);
+  r->T = addii(u1,u2);
+  avma = av;
+  r->P = icopy(r->P);
+  r->Q = icopy(r->Q);
+  r->B = icopy(r->B);
+  r->T = icopy(r->T);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                               PI                               **/
+/**                                                                **/
+/********************************************************************/
+/* replace *old clone by c. Protect against SIGINT */
+static void
+swap_clone(GEN *old, GEN c)
+{
+  GEN tmp = *old;
+  *old = c;
+  if (tmp) gunclone(tmp);
+}
+
+/*                         ----
+ *  53360 (640320)^(1/2)   \    (6n)! (545140134 n + 13591409)
+ *  -------------------- = /    ------------------------------
+ *        Pi               ----   (n!)^3 (3n)! (-640320)^(3n)
+ *                         n>=0
+ *
+ * Ramanujan's formula + binary splitting */
+static GEN
+pi_ramanujan(long prec)
+{
+  const ulong B = 545140134, A = 13591409, C = 640320;
+  const double alpha2 = 47.11041314; /* 3log(C/12) / log(2) */
+  long n, nmax, prec2;
+  struct abpq_res R;
+  struct abpq S;
+  GEN D, u;
+
+  nmax = (long)(1 + prec2nbits(prec)/alpha2);
+#ifdef LONG_IS_64BIT
+  D = utoipos(10939058860032000); /* C^3/24 */
+#else
+  D = uutoi(2546948,495419392);
+#endif
+  abpq_init(&S, nmax);
+  S.a[0] = utoipos(A);
+  S.b[0] = S.p[0] = S.q[0] = gen_1;
+  for (n = 1; n <= nmax; n++)
+  {
+    S.a[n] = addiu(muluu(B, n), A);
+    S.b[n] = gen_1;
+    S.p[n] = mulis(muluu(6*n-5, 2*n-1), 1-6*n);
+    S.q[n] = mulii(sqru(n), muliu(D,n));
+  }
+  abpq_sum(&R, 0, nmax, &S); prec2 = prec+EXTRAPRECWORD;
+  u = itor(muliu(R.Q,C/12), prec2);
+  return rtor(mulrr(divri(u, R.T), sqrtr_abs(utor(C,prec2))), prec);
+}
+
+#if 0 /* Much slower than binary splitting at least up to prec = 10^8 */
+/* Gauss - Brent-Salamin AGM iteration */
+static GEN
+pi_brent_salamin(long prec)
+{
+  GEN A, B, C;
+  pari_sp av2;
+  long i, G;
+
+  G = - prec2nbits(prec);
+  incrprec(prec);
+
+  A = real2n(-1, prec);
+  B = sqrtr_abs(A); /* = 1/sqrt(2) */
+  setexpo(A, 0);
+  C = real2n(-2, prec); av2 = avma;
+  for (i = 0;; i++)
+  {
+    GEN y, a, b, B_A = subrr(B, A);
+    pari_sp av3 = avma;
+    if (expo(B_A) < G) break;
+    a = addrr(A,B); shiftr_inplace(a, -1);
+    b = mulrr(A,B);
+    affrr(a, A);
+    affrr(sqrtr_abs(b), B); avma = av3;
+    y = sqrr(B_A); shiftr_inplace(y, i - 2);
+    affrr(subrr(C, y), C); avma = av2;
+  }
+  shiftr_inplace(C, 2);
+  return divrr(sqrr(addrr(A,B)), C);
+}
+GEN
+constpi(long prec)
+{
+  pari_sp av;
+  GEN tmp;
+  if (gpi && realprec(gpi) >= prec) return gpi;
+
+  tmp = cgetr_block(prec);
+  av = avma;
+  affrr(pi_brent_salamin(prec), tmp);
+  swap_clone(&gpi, tmp);
+  avma = av;  return gpi;
+}
+#endif
+
+GEN
+constpi(long prec)
+{
+  pari_sp av;
+  GEN tmp;
+  if (gpi && realprec(gpi) >= prec) return gpi;
+
+  av = avma;
+  tmp = gclone(pi_ramanujan(prec));
+  swap_clone(&gpi,tmp);
+  avma = av; return gpi;
+}
+
+GEN
+mppi(long prec) { return rtor(constpi(prec), prec); }
+
+/* Pi * 2^n */
+GEN
+Pi2n(long n, long prec)
+{
+  GEN x = mppi(prec); shiftr_inplace(x, n);
+  return x;
+}
+
+/* I * Pi * 2^n */
+GEN
+PiI2n(long n, long prec) { retmkcomplex(gen_0, Pi2n(n, prec)); }
+
+/* 2I * Pi */
+GEN
+PiI2(long prec) { return PiI2n(1, prec); }
+
+/********************************************************************/
+/**                                                                **/
+/**                       EULER CONSTANT                           **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+consteuler(long prec)
+{
+  GEN u,v,a,b,tmpeuler;
+  long l, n1, n, k, x;
+  pari_sp av1, av2;
+
+  if (geuler && realprec(geuler) >= prec) return geuler;
+
+  av1 = avma; tmpeuler = cgetr_block(prec);
+
+  incrprec(prec);
+
+  l = prec+EXTRAPRECWORD; x = (long) (1 + prec2nbits_mul(l, LOG2/4));
+  a = utor(x,l); u=logr_abs(a); setsigne(u,-1); affrr(u,a);
+  b = real_1(l);
+  v = real_1(l);
+  n = (long)(1+3.591*x); /* z=3.591: z*[ ln(z)-1 ]=1 */
+  n1 = minss(n, SQRTVERYBIGINT);
+  if (x < SQRTVERYBIGINT)
+  {
+    ulong xx = x*x;
+    av2 = avma;
+    for (k=1; k<n1; k++)
+    {
+      affrr(divru(mulur(xx,b),k*k), b);
+      affrr(divru(addrr(divru(mulur(xx,a),k),b),k), a);
+      affrr(addrr(u,a), u);
+      affrr(addrr(v,b), v); avma = av2;
+    }
+    for (   ; k<=n; k++)
+    {
+      affrr(divru(divru(mulur(xx,b),k),k), b);
+      affrr(divru(addrr(divru(mulur(xx,a),k),b),k), a);
+      affrr(addrr(u,a), u);
+      affrr(addrr(v,b), v); avma = av2;
+    }
+  }
+  else
+  {
+    GEN xx = sqru(x);
+    av2 = avma;
+    for (k=1; k<n1; k++)
+    {
+      affrr(divru(mulir(xx,b),k*k), b);
+      affrr(divru(addrr(divru(mulir(xx,a),k),b),k), a);
+      affrr(addrr(u,a), u);
+      affrr(addrr(v,b), v); avma = av2;
+    }
+    for (   ; k<=n; k++)
+    {
+      affrr(divru(divru(mulir(xx,b),k),k), b);
+      affrr(divru(addrr(divru(mulir(xx,a),k),b),k), a);
+      affrr(addrr(u,a), u);
+      affrr(addrr(v,b), v); avma = av2;
+    }
+  }
+  divrrz(u,v,tmpeuler);
+  swap_clone(&geuler,tmpeuler);
+  avma = av1; return geuler;
+}
+
+GEN
+mpeuler(long prec) { return rtor(consteuler(prec), prec); }
+
+/********************************************************************/
+/**                                                                **/
+/**                       CATALAN CONSTANT                         **/
+/**                                                                **/
+/********************************************************************/
+/* 8G = 3\sum_{n>=0} 1/(binomial(2n,n)(2n+1)^2) + Pi log(2+sqrt(3)) */
+static GEN
+catalan(long prec)
+{
+  long i, nmax = bit_accuracy(prec) >> 1;
+  struct abpq_res R;
+  struct abpq A;
+  GEN u, v;
+  abpq_init(&A, nmax);
+  A.a[0] = A.b[0] = A.p[0] = A.q[0] = gen_1;
+  for (i = 1; i <= nmax; i++)
+  {
+    A.a[i] = gen_1;
+    A.b[i] = utoipos((i<<1)+1);
+    A.p[i] = utoipos(i);
+    A.q[i] = utoipos((i<<2)+2);
+  }
+  abpq_sum(&R, 0, nmax, &A);
+  u = mulur(3, rdivii(R.T, mulii(R.B,R.Q),prec));
+  v = mulrr(mppi(prec), logr_abs(addrs(sqrtr_abs(utor(3,prec)), 2)));
+  u = addrr(u, v); shiftr_inplace(u, -3);
+  return u;
+}
+
+GEN
+constcatalan(long prec)
+{
+  pari_sp av = avma;
+  GEN tmp;
+  if (gcatalan && realprec(gcatalan) >= prec) return gcatalan;
+  tmp = gclone(catalan(prec));
+  swap_clone(&gcatalan,tmp);
+  avma = av; return gcatalan;
+}
+
+GEN
+mpcatalan(long prec) { return rtor(constcatalan(prec), prec); }
+
+/********************************************************************/
+/**                                                                **/
+/**          TYPE CONVERSION FOR TRANSCENDENTAL FUNCTIONS          **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+transvec(GEN (*f)(GEN,long), GEN x, long prec)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(y,i) = f(gel(x,i),prec);
+  return y;
+}
+
+GEN
+trans_eval(const char *fun, GEN (*f)(GEN,long), GEN x, long prec)
+{
+  pari_sp av = avma;
+  if (prec < 3) pari_err_BUG("trans_eval [prec < 3]");
+  switch(typ(x))
+  {
+    case t_INT:    x = f(itor(x,prec),prec); break;
+    case t_FRAC:   x = f(fractor(x, prec),prec); break;
+    case t_QUAD:   x = f(quadtofp(x,prec),prec); break;
+    case t_POLMOD: x = transvec(f, polmod_to_embed(x,prec), prec); break;
+    case t_VEC:
+    case t_COL:
+    case t_MAT: return transvec(f, x, prec);
+    default: pari_err_TYPE(fun,x); return NULL;
+  }
+  return gerepileupto(av, x);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            POWERING                             */
+/*                                                                 */
+/*******************************************************************/
+/* x a t_REAL 0, return exp(x) */
+static GEN
+mpexp0(GEN x)
+{
+  long e = expo(x);
+  return e >= 0? real_0_bit(e): real_1(nbits2prec(-e));
+}
+static GEN
+powr0(GEN x)
+{ return signe(x)? real_1(realprec(x)): mpexp0(x); }
+
+/* x t_POL or t_SER, return scalarpol(RgX_get_1(x)) */
+static GEN
+scalarpol_get_1(GEN x)
+{
+  GEN y = cgetg(3,t_POL);
+  y[1] = evalvarn(varn(x)) | evalsigne(1);
+  gel(y,2) = RgX_get_1(x); return y;
+}
+/* to be called by the generic function gpowgs(x,s) when s = 0 */
+static GEN
+gpowg0(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC: case t_PADIC:
+      return gen_1;
+
+    case t_QUAD: x++; /*fall through*/
+    case t_COMPLEX: {
+      pari_sp av = avma;
+      GEN a = gpowg0(gel(x,1));
+      GEN b = gpowg0(gel(x,2));
+      if (a == gen_1) return b;
+      if (b == gen_1) return a;
+      return gerepileupto(av, gmul(a,b));
+    }
+    case t_INTMOD:
+      y = cgetg(3,t_INTMOD);
+      gel(y,1) = icopy(gel(x,1));
+      gel(y,2) = gen_1; return y;
+
+    case t_FFELT: return FF_1(x);
+
+    case t_POLMOD:
+      y = cgetg(3,t_POLMOD);
+      gel(y,1) = gcopy(gel(x,1));
+      gel(y,2) = scalarpol_get_1(gel(x,1)); return y;
+
+    case t_RFRAC:
+      return scalarpol_get_1(gel(x,2));
+    case t_POL: case t_SER:
+      return scalarpol_get_1(x);
+
+    case t_MAT:
+      lx=lg(x); if (lx==1) return cgetg(1,t_MAT);
+      if (lx != lgcols(x)) pari_err_DIM("gpow");
+      y = matid(lx-1);
+      for (i=1; i<lx; i++) gcoeff(y,i,i) = gpowg0(gcoeff(x,i,i));
+      return y;
+    case t_QFR: return qfr_1(x);
+    case t_QFI: return qfi_1(x);
+    case t_VECSMALL: return identity_perm(lg(x) - 1);
+  }
+  pari_err_TYPE("gpow",x);
+  return NULL; /* not reached */
+}
+
+static GEN
+_sqr(void *data /* ignored */, GEN x) { (void)data; return gsqr(x); }
+static GEN
+_mul(void *data /* ignored */, GEN x, GEN y) { (void)data; return gmul(x,y); }
+static GEN
+_sqri(void *data /* ignored */, GEN x) { (void)data; return sqri(x); }
+static GEN
+_muli(void *data /* ignored */, GEN x, GEN y) { (void)data; return mulii(x,y); }
+static GEN
+_sqrr(void *data /* ignored */, GEN x) { (void)data; return sqrr(x); }
+static GEN
+_mulr(void *data /* ignored */, GEN x, GEN y) { (void)data; return mulrr(x,y); }
+
+/* INTEGER POWERING (a^n for integer a != 0 and integer n > 0)
+ *
+ * Use left shift binary algorithm (RS is wasteful: multiplies big numbers,
+ * with LS one of them is the base, hence small). Sign of result is set
+ * to s (= 1,-1). Makes life easier for caller, which otherwise might do a
+ * setsigne(gen_1 / gen_m1) */
+static GEN
+powiu_sign(GEN a, ulong N, long s)
+{
+  pari_sp av;
+  GEN y;
+
+  if (lgefint(a) == 3)
+  { /* easy if |a| < 3 */
+    ulong q = a[2];
+    if (q == 1) return (s>0)? gen_1: gen_m1;
+    if (q == 2) { a = int2u(N); setsigne(a,s); return a; }
+    q = upowuu(q, N);
+    if (q) return s>0? utoipos(q): utoineg(q);
+  }
+  if (N <= 2) {
+    if (N == 2) return sqri(a);
+    a = icopy(a); setsigne(a,s); return a;
+  }
+  av = avma;
+  y = gen_powu_i(a, N, NULL, &_sqri, &_muli);
+  setsigne(y,s); return gerepileuptoint(av, y);
+}
+/* a^n */
+GEN
+powiu(GEN a, ulong n)
+{
+  long s;
+  if (!n) return gen_1;
+  s = signe(a);
+  if (!s) return gen_0;
+  return powiu_sign(a, n, (s < 0 && odd(n))? -1: 1);
+}
+GEN
+powis(GEN a, long n)
+{
+  long s;
+  GEN t, y;
+  if (n >= 0) return powiu(a, n);
+  s = signe(a);
+  if (!s) pari_err_INV("powis",gen_0);
+  t = (s < 0 && odd(n))? gen_m1: gen_1;
+  if (is_pm1(a)) return t;
+  /* n < 0, |a| > 1 */
+  y = cgetg(3,t_FRAC);
+  gel(y,1) = t;
+  gel(y,2) = powiu_sign(a, -n, 1); /* force denominator > 0 */
+  return y;
+}
+GEN
+powuu(ulong p, ulong N)
+{
+  pari_sp av = avma;
+  long P[] = {evaltyp(t_INT)|_evallg(3), evalsigne(1)|evallgefint(3),0};
+  ulong pN;
+  GEN y;
+  if (N <= 2)
+  {
+    if (N == 2) return sqru(p);
+    if (N == 1) return utoipos(p);
+    return gen_1;
+  }
+  if (!p) return gen_0;
+  pN = upowuu(p, N);
+  if (pN) return utoipos(pN);
+  if (p == 2) return int2u(N);
+  P[2] = p; av = avma;
+  y = gen_powu_i(P, N, NULL, &_sqri, &_muli);
+  return gerepileuptoint(av, y);
+}
+
+/* return 0 if overflow */
+static ulong
+usqru(ulong p) { return p & HIGHMASK? 0: p*p; }
+ulong
+upowuu(ulong p, ulong k)
+{
+#ifdef LONG_IS_64BIT
+  const ulong CUTOFF3 = 2642245;
+  const ulong CUTOFF4 = 65535;
+  const ulong CUTOFF5 = 7131;
+  const ulong CUTOFF6 = 1625;
+  const ulong CUTOFF7 = 565;
+  const ulong CUTOFF8 = 255;
+  const ulong CUTOFF9 = 138;
+  const ulong CUTOFF10 = 84;
+  const ulong CUTOFF11 = 56;
+  const ulong CUTOFF12 = 40;
+  const ulong CUTOFF13 = 30;
+  const ulong CUTOFF14 = 23;
+  const ulong CUTOFF15 = 19;
+  const ulong CUTOFF16 = 15;
+  const ulong CUTOFF17 = 13;
+  const ulong CUTOFF18 = 11;
+  const ulong CUTOFF19 = 10;
+  const ulong CUTOFF20 =  9;
+#else
+  const ulong CUTOFF3 = 1625;
+  const ulong CUTOFF4 =  255;
+  const ulong CUTOFF5 =   84;
+  const ulong CUTOFF6 =   40;
+  const ulong CUTOFF7 =   23;
+  const ulong CUTOFF8 =   15;
+  const ulong CUTOFF9 =   11;
+  const ulong CUTOFF10 =   9;
+  const ulong CUTOFF11 =   7;
+  const ulong CUTOFF12 =   6;
+  const ulong CUTOFF13 =   5;
+  const ulong CUTOFF14 =   4;
+  const ulong CUTOFF15 =   4;
+  const ulong CUTOFF16 =   3;
+  const ulong CUTOFF17 =   3;
+  const ulong CUTOFF18 =   3;
+  const ulong CUTOFF19 =   3;
+  const ulong CUTOFF20 =   3;
+#endif
+
+  if (p <= 2)
+  {
+    if (p < 2) return p;
+    return k < BITS_IN_LONG? 1UL<<k: 0;
+  }
+  switch(k)
+  {
+    ulong p2, p3, p4, p5, p8;
+    case 0:  return 1;
+    case 1:  return p;
+    case 2:  return usqru(p);
+    case 3:  if (p > CUTOFF3) return 0; return p*p*p;
+    case 4:  if (p > CUTOFF4) return 0; p2=p*p; return p2*p2;
+    case 5:  if (p > CUTOFF5) return 0; p2=p*p; return p2*p2*p;
+    case 6:  if (p > CUTOFF6) return 0; p2=p*p; return p2*p2*p2;
+    case 7:  if (p > CUTOFF7) return 0; p2=p*p; return p2*p2*p2*p;
+    case 8:  if (p > CUTOFF8) return 0; p2=p*p; p4=p2*p2; return p4*p4;
+    case 9:  if (p > CUTOFF9) return 0; p2=p*p; p4=p2*p2; return p4*p4*p;
+    case 10: if (p > CUTOFF10)return 0; p2=p*p; p4=p2*p2; return p4*p4*p2;
+    case 11: if (p > CUTOFF11)return 0; p2=p*p; p4=p2*p2; return p4*p4*p2*p;
+    case 12: if (p > CUTOFF12)return 0; p2=p*p; p4=p2*p2; return p4*p4*p4;
+    case 13: if (p > CUTOFF13)return 0; p2=p*p; p4=p2*p2; return p4*p4*p4*p;
+    case 14: if (p > CUTOFF14)return 0; p2=p*p; p4=p2*p2; return p4*p4*p4*p2;
+    case 15: if (p > CUTOFF15)return 0;
+      p2=p*p; p3=p2*p; p5=p3*p2; return p5*p5*p5;
+    case 16: if (p > CUTOFF16)return 0;
+      p2=p*p; p4=p2*p2; p8=p4*p4; return p8*p8;
+    case 17: if (p > CUTOFF17)return 0;
+      p2=p*p; p4=p2*p2; p8=p4*p4; return p*p8*p8;
+    case 18: if (p > CUTOFF18)return 0;
+      p2=p*p; p4=p2*p2; p8=p4*p4; return p2*p8*p8;
+    case 19: if (p > CUTOFF19)return 0;
+      p2=p*p; p4=p2*p2; p8=p4*p4; return p*p2*p8*p8;
+    case 20: if (p > CUTOFF20)return 0;
+      p2=p*p; p4=p2*p2; p8=p4*p4; return p4*p8*p8;
+  }
+#ifdef LONG_IS_64BIT
+  switch(p)
+  {
+    case 3: if (k > 40) return 0;
+      break;
+    case 4: if (k > 31) return 0;
+      return 1UL<<(2*k);
+    case 5: if (k > 27) return 0;
+      break;
+    case 6: if (k > 24) return 0;
+      break;
+    case 7: if (k > 22) return 0;
+      break;
+    default: return 0;
+  }
+  /* no overflow */
+  {
+    ulong q = upowuu(p, k >> 1);
+    q *= q ;
+    return odd(k)? q*p: q;
+  }
+#endif
+  return 0;
+}
+
+typedef struct {
+  long prec, a;
+  GEN (*sqr)(GEN);
+  GEN (*mulug)(ulong,GEN);
+} sr_muldata;
+
+static GEN
+_rpowuu_sqr(void *data, GEN x)
+{
+  sr_muldata *D = (sr_muldata *)data;
+  if (typ(x) == t_INT && lgefint(x) >= D->prec)
+  { /* switch to t_REAL */
+    D->sqr   = &sqrr;
+    D->mulug = &mulur; x = itor(x, D->prec);
+  }
+  return D->sqr(x);
+}
+
+static GEN
+_rpowuu_msqr(void *data, GEN x)
+{
+  GEN x2 = _rpowuu_sqr(data, x);
+  sr_muldata *D = (sr_muldata *)data;
+  return D->mulug(D->a, x2);
+}
+
+/* return a^n as a t_REAL of precision prec. Assume a > 0, n > 0 */
+GEN
+rpowuu(ulong a, ulong n, long prec)
+{
+  pari_sp av;
+  GEN y, z;
+  sr_muldata D;
+
+  if (a == 1) return real_1(prec);
+  if (a == 2) return real2n(n, prec);
+  if (n == 1) return utor(a, prec);
+  z = cgetr(prec);
+  av = avma;
+  D.sqr   = &sqri;
+  D.mulug = &mului;
+  D.prec = prec;
+  D.a = (long)a;
+  y = gen_powu_fold_i(utoipos(a), n, (void*)&D, &_rpowuu_sqr, &_rpowuu_msqr);
+  mpaff(y, z); avma = av; return z;
+}
+
+GEN
+powrs(GEN x, long n)
+{
+  pari_sp av = avma;
+  GEN y;
+  if (!n) return powr0(x);
+  y = gen_powu_i(x, (ulong)labs(n), NULL, &_sqrr, &_mulr);
+  if (n < 0) y = invr(y);
+  return gerepileuptoleaf(av,y);
+}
+GEN
+powru(GEN x, ulong n)
+{
+  pari_sp av = avma;
+  GEN y;
+  if (!n) return powr0(x);
+  y = gen_powu_i(x, n, NULL, &_sqrr, &_mulr);
+  return gerepileuptoleaf(av,y);
+}
+
+/* x^(s/2), assume x t_REAL */
+GEN
+powrshalf(GEN x, long s)
+{
+  if (s & 1) return sqrtr(powrs(x, s));
+  return powrs(x, s>>1);
+}
+/* x^(s/2), assume x t_REAL */
+GEN
+powruhalf(GEN x, ulong s)
+{
+  if (s & 1) return sqrtr(powru(x, s));
+  return powru(x, s>>1);
+}
+/* x^(n/d), assume x t_REAL, return t_REAL */
+GEN
+powrfrac(GEN x, long n, long d)
+{
+  long z;
+  if (!n) return powr0(x);
+  z = cgcd(n, d); if (z > 1) { n /= z; d /= z; }
+  if (d == 1) return powrs(x, n);
+  x = powrs(x, n);
+  if (d == 2) return sqrtr(x);
+  return sqrtnr(x, d);
+}
+
+/* assume x != 0 */
+static GEN
+pow_monome(GEN x, long n)
+{
+  long i, d, dx = degpol(x);
+  GEN A, b, y;
+
+  if (n < 0) { n = -n; y = cgetg(3, t_RFRAC); } else y = NULL;
+
+  if (HIGHWORD(dx) || HIGHWORD(n))
+  {
+    LOCAL_HIREMAINDER;
+    d = (long)mulll((ulong)dx, (ulong)n);
+    if (hiremainder || (d &~ LGBITS)) d = LGBITS; /* overflow */
+    d += 2;
+  }
+  else
+    d = dx*n + 2;
+  if ((d + 1) & ~LGBITS) pari_err(e_OVERFLOW,"pow_monome [degree]");
+  A = cgetg(d+1, t_POL); A[1] = x[1];
+  for (i=2; i < d; i++) gel(A,i) = gen_0;
+  b = gpowgs(gel(x,dx+2), n); /* not memory clean if (n < 0) */
+  if (!y) y = A;
+  else {
+    GEN c = denom(b);
+    gel(y,1) = c; if (c != gen_1) b = gmul(b,c);
+    gel(y,2) = A;
+  }
+  gel(A,d) = b; return y;
+}
+
+/* x t_PADIC */
+static GEN
+powps(GEN x, long n)
+{
+  long e = n*valp(x), v;
+  GEN t, y, mod, p = gel(x,2);
+  pari_sp av;
+
+  if (!signe(gel(x,4))) {
+    if (n < 0) pari_err_INV("powps",x);
+    return zeropadic(p, e);
+  }
+  v = z_pval(n, p);
+
+  y = cgetg(5,t_PADIC);
+  mod = gel(x,3);
+  if (v == 0) mod = icopy(mod);
+  else
+  {
+    if (precp(x) == 1 && equaliu(p, 2)) v++;
+    mod = mulii(mod, powiu(p,v));
+    mod = gerepileuptoint((pari_sp)y, mod);
+  }
+  y[1] = evalprecp(precp(x) + v) | evalvalp(e);
+  gel(y,2) = icopy(p);
+  gel(y,3) = mod;
+
+  av = avma; t = gel(x,4);
+  if (n < 0) { t = Fp_inv(t, mod); n = -n; }
+  t = Fp_powu(t, n, mod);
+  gel(y,4) = gerepileuptoint(av, t);
+  return y;
+}
+/* x t_PADIC */
+static GEN
+powp(GEN x, GEN n)
+{
+  long v;
+  GEN y, mod, p = gel(x,2);
+
+  if (valp(x)) pari_err_OVERFLOW("valp()");
+
+  if (!signe(gel(x,4))) {
+    if (signe(n) < 0) pari_err_INV("powp",x);
+    return zeropadic(p, 0);
+  }
+  v = Z_pval(n, p);
+
+  y = cgetg(5,t_PADIC);
+  mod = gel(x,3);
+  if (v == 0) mod = icopy(mod);
+  else
+  {
+    mod = mulii(mod, powiu(p,v));
+    mod = gerepileuptoint((pari_sp)y, mod);
+  }
+  y[1] = evalprecp(precp(x) + v) | _evalvalp(0);
+  gel(y,2) = icopy(p);
+  gel(y,3) = mod;
+  gel(y,4) = Fp_pow(gel(x,4), n, mod);
+  return y;
+}
+static GEN
+pow_polmod(GEN x, GEN n)
+{
+  GEN z = cgetg(3, t_POLMOD), a = gel(x,2), T = gel(x,1);
+  gel(z,1) = gcopy(T);
+  if (typ(a) != t_POL || varn(a) != varn(T) || lg(a) <= 3)
+    a = powgi(a, n);
+  else {
+    pari_sp av = avma;
+    GEN p = NULL;
+    if (RgX_is_FpX(T, &p) && RgX_is_FpX(a, &p) && p)
+    {
+      T = RgX_to_FpX(T, p); a = RgX_to_FpX(a, p);
+      if (lgefint(p) == 3)
+      {
+        ulong pp = p[2];
+        a = Flxq_pow(ZX_to_Flx(a, pp), n, ZX_to_Flx(T, pp), pp);
+        a = Flx_to_ZX(a);
+      }
+      else
+        a = FpXQ_pow(a, n, T, p);
+      a = FpX_to_mod(a, p);
+      a = gerepileupto(av, a);
+    }
+    else
+    {
+      avma = av;
+      a = RgXQ_pow(a, n, gel(z,1));
+    }
+  }
+  gel(z,2) = a; return z;
+}
+
+GEN
+gpowgs(GEN x, long n)
+{
+  long m;
+  pari_sp av;
+  GEN y;
+
+  if (n == 0) return gpowg0(x);
+  if (n == 1)
+    switch (typ(x)) {
+      case t_QFI: return redimag(x);
+      case t_QFR: return redreal(x);
+      default: return gcopy(x);
+    }
+  if (n ==-1) return ginv(x);
+  switch(typ(x))
+  {
+    case t_INT: return powis(x,n);
+    case t_REAL: return powrs(x,n);
+    case t_INTMOD:
+      y = cgetg(3,t_INTMOD); gel(y,1) = icopy(gel(x,1));
+      gel(y,2) = Fp_pows(gel(x,2), n, gel(x,1));
+      return y;
+    case t_FRAC:
+    {
+      GEN a = gel(x,1), b = gel(x,2);
+      long s = (signe(a) < 0 && odd(n))? -1: 1;
+      if (n < 0) {
+        n = -n;
+        if (is_pm1(a)) return powiu_sign(b, n, s); /* +-1/x[2] inverts to t_INT */
+        swap(a, b);
+      }
+      y = cgetg(3, t_FRAC);
+      gel(y,1) = powiu_sign(a, n, s);
+      gel(y,2) = powiu_sign(b, n, 1);
+      return y;
+    }
+    case t_PADIC: return powps(x, n);
+    case t_RFRAC:
+    {
+      av = avma; y = cgetg(3, t_RFRAC); m = labs(n);
+      gel(y,1) = gpowgs(gel(x,1),m);
+      gel(y,2) = gpowgs(gel(x,2),m);
+      if (n < 0) y = ginv(y);
+      return gerepileupto(av,y);
+    }
+    case t_POLMOD: {
+      long N[] = {evaltyp(t_INT) | _evallg(3),0,0};
+      affsi(n,N); return pow_polmod(x, N);
+    }
+    case t_POL:
+      if (RgX_is_monomial(x)) return pow_monome(x, n);
+    default: {
+      pari_sp av = avma;
+      y = gen_powu_i(x, (ulong)labs(n), NULL, &_sqr, &_mul);
+      if (n < 0) y = ginv(y);
+      return gerepileupto(av,y);
+    }
+  }
+}
+
+/* n a t_INT */
+GEN
+powgi(GEN x, GEN n)
+{
+  GEN y;
+
+  if (!is_bigint(n)) return gpowgs(x, itos(n));
+  /* probable overflow for non-modular types (typical exception: (X^0)^N) */
+  switch(typ(x))
+  {
+    case t_INTMOD:
+      y = cgetg(3,t_INTMOD); gel(y,1) = icopy(gel(x,1));
+      gel(y,2) = Fp_pow(gel(x,2), n, gel(x,1));
+      return y;
+    case t_FFELT: return FF_pow(x,n);
+    case t_PADIC: return powp(x, n);
+
+    case t_INT:
+      if (is_pm1(x)) return (signe(x) < 0 && mpodd(n))? gen_m1: gen_1;
+      if (signe(x)) pari_err_OVERFLOW("lg()");
+      if (signe(n) < 0) pari_err_INV("powgi",gen_0);
+      return gen_0;
+    case t_FRAC:
+      pari_err_OVERFLOW("lg()");
+
+    case t_QFR: return qfrpow(x, n);
+    case t_POLMOD: return pow_polmod(x, n);
+    default: {
+      pari_sp av = avma;
+      y = gen_pow(x, n, NULL, &_sqr, &_mul);
+      if (signe(n) < 0) y = ginv(y);
+      return gerepileupto(av,y);
+    }
+  }
+}
+
+/* Assume x = 1 + O(t), n a scalar. Return x^n */
+static GEN
+ser_pow_1(GEN x, GEN n)
+{
+  long lx, mi, i, j, d;
+  GEN y = cgetg_copy(x, &lx), X = x+2, Y = y + 2;
+  y[1] = evalsigne(1) | _evalvalp(0) | evalvarn(varn(x));
+  d = mi = lx-3; while (mi>=1 && isrationalzero(gel(X,mi))) mi--;
+  gel(Y,0) = gen_1;
+  for (i=1; i<=d; i++)
+  {
+    pari_sp av = avma;
+    GEN s = gen_0;
+    for (j=1; j<=minss(i,mi); j++)
+    {
+      GEN t = gsubgs(gmulgs(n,j),i-j);
+      s = gadd(s, gmul(gmul(t, gel(X,j)), gel(Y,i-j)));
+    }
+    gel(Y,i) = gerepileupto(av, gdivgs(s,i));
+  }
+  return y;
+}
+
+/* we suppose n != 0, valp(x) = 0 and leading-term(x) != 0. Not stack clean */
+static GEN
+ser_pow(GEN x, GEN n, long prec)
+{
+  GEN y, c, lead;
+  if (varncmp(gvar(n), varn(x)) <= 0) return gexp(gmul(n, glog(x,prec)), prec);
+  lead = gel(x,2);
+  if (gequal1(lead)) return ser_pow_1(x, n);
+  x = ser_normalize(x);
+  if (typ(n) == t_FRAC && !isinexact(lead) && ispower(lead, gel(n,2), &c))
+    c = powgi(c, gel(n,1));
+  else
+    c = gpow(lead,n, prec);
+  y = gmul(c, ser_pow_1(x, n));
+  /* gpow(t_POLMOD,n) can be a t_COL [conjvec] */
+  if (typ(y) != t_SER) pari_err_TYPE("gpow", y);
+  return y;
+}
+
+static long
+val_from_i(GEN E)
+{
+  if (is_bigint(E)) pari_err_OVERFLOW("sqrtn [valuation]");
+  return itos(E);
+}
+
+/* return x^q, assume typ(x) = t_SER, typ(q) = t_INT/t_FRAC and q != 0 */
+static GEN
+ser_powfrac(GEN x, GEN q, long prec)
+{
+  GEN y, E = gmulsg(valp(x), q);
+  long e;
+
+  if (!signe(x))
+  {
+    if (gsigne(q) < 0) pari_err_INV("gpow", x);
+    return zeroser(varn(x), val_from_i(gfloor(E)));
+  }
+  if (typ(E) != t_INT)
+    pari_err_DOMAIN("sqrtn", "valuation", "!=", mkintmod(gen_0, gel(q,2)), x);
+  e = val_from_i(E);
+  y = leafcopy(x); setvalp(y, 0);
+  y = ser_pow(y, q, prec);
+  setvalp(y, e); return y;
+}
+
+GEN
+gpow(GEN x, GEN n, long prec)
+{
+  long i, lx, tx, tn = typ(n);
+  pari_sp av;
+  GEN y;
+
+  if (tn == t_INT) return powgi(x,n);
+  tx = typ(x);
+  if (is_matvec_t(tx))
+  {
+    y = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(y,i) = gpow(gel(x,i),n,prec);
+    return y;
+  }
+  av = avma;
+  switch (tx)
+  {
+    case t_POL: case t_RFRAC: x = toser_i(x); /* fall through */
+    case t_SER:
+      if (tn == t_FRAC) return gerepileupto(av, ser_powfrac(x, n, prec));
+      if (valp(x))
+        pari_err_DOMAIN("gpow [irrational exponent]",
+                        "valuation", "!=", gen_0, x);
+      if (lg(x) == 2) return gerepilecopy(av, x); /* O(1) */
+      return gerepileupto(av, ser_pow(x, n, prec));
+  }
+  if (gequal0(x))
+  {
+    switch(tn)
+    {
+      case t_REAL: case t_FRAC: case t_COMPLEX: case t_QUAD:
+        break;
+      default: pari_err_TYPE("gpow(0,n)", n);
+    }
+    n = real_i(n);
+    if (gsigne(n) <= 0) pari_err_DOMAIN("gpow(0,n)", "n", "<=", gen_0, n);
+    if (!precision(x)) return gcopy(x);
+
+    x = ground(gmulsg(gexpo(x),n));
+    if (is_bigint(x) || (ulong)x[2] >= HIGHEXPOBIT)
+      pari_err_OVERFLOW("gpow");
+    avma = av; return real_0_bit(itos(x));
+  }
+  if (tn == t_FRAC)
+  {
+    GEN z, d = gel(n,2), a = gel(n,1);
+    switch (tx)
+    {
+    case t_INTMOD:
+      {
+        GEN p = gel(x,1);
+        if (!BPSW_psp(p)) pari_err_PRIME("gpow",p);
+        y = cgetg(3,t_INTMOD); gel(y,1) = icopy(p);
+        av = avma;
+        z = Fp_sqrtn(gel(x,2), d, p, NULL);
+        if (!z) pari_err_SQRTN("gpow",x);
+        gel(y,2) = gerepileuptoint(av, Fp_pow(z, a, p));
+        return y;
+      }
+
+    case t_PADIC:
+      z = Qp_sqrtn(x, d, NULL);
+      if (!z) pari_err_SQRTN("gpow",x);
+      return gerepileupto(av, powgi(z, a));
+
+    case t_FFELT:
+      return gerepileupto(av,FF_pow(FF_sqrtn(x,d,NULL),a));
+    }
+  }
+  i = (long) precision(n); if (i) prec=i;
+  y = gmul(n, glog(x,prec));
+  return gerepileupto(av, gexp(y,prec));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        RACINE CARREE                           **/
+/**                                                                **/
+/********************************************************************/
+/* assume x unit, precp(x) = pp > 3 */
+static GEN
+sqrt_2adic(GEN x, long pp)
+{
+  GEN z = mod16(x)==(signe(x)>=0?1:15)?gen_1: utoipos(3);
+  long zp;
+  pari_sp av, lim;
+
+  if (pp == 4) return z;
+  zp = 3; /* number of correct bits in z (compared to sqrt(x)) */
+
+  av = avma; lim = stack_lim(av,2);
+  for(;;)
+  {
+    GEN mod;
+    zp = (zp<<1) - 1;
+    if (zp > pp) zp = pp;
+    mod = int2n(zp);
+    z = addii(z, remi2n(mulii(x, Fp_inv(z,mod)), zp));
+    z = shifti(z, -1); /* (z + x/z) / 2 */
+    if (pp == zp) return z;
+
+    if (zp < pp) zp--;
+
+    if (low_stack(lim,stack_lim(av,2)))
+    {
+      if (DEBUGMEM > 1) pari_warn(warnmem,"Qp_sqrt");
+      z = gerepileuptoint(av,z);
+    }
+  }
+}
+
+/* x unit defined modulo p^e, e > 0 */
+static GEN
+Up_sqrt(GEN x, GEN p, long e)
+{
+  pari_sp av = avma;
+  if (equaliu(p,2))
+  {
+    long r = signe(x)>=0?mod8(x):8-mod8(x);
+    if (e <= 3)
+    {
+      switch(e) {
+      case 1: break;
+      case 2: if ((r&3) == 1) break;
+              return NULL;
+      case 3: if (r == 1) break;
+              return NULL;
+      }
+      return gen_1;
+    }
+    else
+    {
+      if (r != 1) return NULL;
+      return gerepileuptoint(av, sqrt_2adic(x, e));
+    }
+  }
+  else
+  {
+    GEN z = Fp_sqrt(x, p);
+    if (!z) return NULL;
+    if (e <= 1) return z;
+    return gerepileuptoint(av, Zp_sqrtlift(x, z, p, e));
+  }
+}
+
+GEN
+Qp_sqrt(GEN x)
+{
+  long pp, e = valp(x);
+  GEN z,y,mod, p = gel(x,2);
+
+  if (gequal0(x)) return zeropadic(p, (e+1) >> 1);
+  if (e & 1) return NULL;
+
+  y = cgetg(5,t_PADIC);
+  pp = precp(x);
+  mod = gel(x,3);
+  z   = gel(x,4); /* lift to t_INT */
+  e >>= 1;
+  z = Up_sqrt(z, p, pp);
+  if (!z) return NULL;
+  if (equaliu(p,2))
+  {
+    pp  = (pp <= 3) ? 1 : pp-1;
+    mod = int2n(pp);
+  }
+  else mod = icopy(mod);
+  y[1] = evalprecp(pp) | evalvalp(e);
+  gel(y,2) = icopy(p);
+  gel(y,3) = mod;
+  gel(y,4) = z; return y;
+}
+
+GEN
+Zn_sqrt(GEN d, GEN fn)
+{
+  pari_sp ltop = avma, btop, st_lim;
+  GEN b = gen_0, m = gen_1;
+  long j, np;
+  if (typ(d) != t_INT) pari_err_TYPE("Zn_sqrt",d);
+  if (typ(fn) == t_INT)
+    fn = absi_factor(fn);
+  else if (!is_Z_factorpos(fn))
+    pari_err_TYPE("Zn_sqrt",fn);
+  np = nbrows(fn);
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for (j = 1; j <= np; ++j)
+  {
+    GEN  bp, mp, pr, r;
+    GEN  p = gcoeff(fn, j, 1);
+    long e = itos(gcoeff(fn, j, 2));
+    long v = Z_pvalrem(d,p,&r);
+    if (v >= e) bp =gen_0;
+    else
+    {
+      if (odd(v)) return NULL;
+      bp = Up_sqrt(r, p, e-v);
+      if (!bp)    return NULL;
+      if (v) bp = mulii(bp, powiu(p, v>>1L));
+    }
+    mp = powiu(p, e);
+    pr = mulii(m, mp);
+    b = Z_chinese_coprime(b, bp, m, mp, pr);
+    m = pr;
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+      gerepileall(btop, 2, &b, &m);
+  }
+  return gerepileupto(ltop, b);
+}
+
+static GEN
+sqrt_ser(GEN b, long prec)
+{
+  long e = valp(b), vx = varn(b), lx, lold, j;
+  ulong mask;
+  GEN a, x, lta, ltx;
+
+  if (!signe(b)) return zeroser(vx, e>>1);
+  a = leafcopy(b);
+  x = cgetg_copy(b, &lx);
+  if (e & 1)
+    pari_err_DOMAIN("sqrtn", "valuation", "!=", mkintmod(gen_0, gen_2), x);
+  a[1] = x[1] = evalsigne(1) | evalvarn(0) | _evalvalp(0);
+  lta = gel(a,2);
+  if (gequal1(lta)) ltx = lta;
+  else if (!issquareall(lta,&ltx)) ltx = gsqrt(lta,prec);
+  gel(x,2) = ltx;
+  for (j = 3; j < lx; j++) gel(x,j) = gen_0;
+  setlg(x,3);
+  mask = quadratic_prec_mask(lx - 2);
+  lold = 1;
+  while (mask > 1)
+  {
+    GEN y, x2 = gmul2n(x,1);
+    long l = lold << 1;
+
+    if (mask & 1) l--;
+    mask >>= 1;
+    setlg(a, l + 2);
+    setlg(x, l + 2);
+    y = sqr_ser_part(x, lold, l-1) - lold;
+    for (j = lold+2; j < l+2; j++) gel(y,j) = gsub(gel(y,j), gel(a,j));
+    y += lold; setvalp(y, lold);
+    y = gsub(x, gdiv(y, x2)); /* = gmuloldn(gadd(x, gdiv(a,x)), -1); */
+    for (j = lold+2; j < l+2; j++) gel(x,j) = gel(y,j);
+    lold = l;
+  }
+  x[1] = evalsigne(1) | evalvarn(vx) | _evalvalp(e >> 1);
+  return x;
+}
+
+GEN
+gsqrt(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y;
+
+  switch(typ(x))
+  {
+    case t_REAL: return sqrtr(x);
+
+    case t_INTMOD:
+    {
+      GEN p = gel(x,1), a;
+      y = cgetg(3,t_INTMOD); gel(y,1) = icopy(p);
+      a = Fp_sqrt(gel(x,2),p);
+      if (!a)
+      {
+        if (!BPSW_psp(p)) pari_err_PRIME("sqrt [modulus]",p);
+        pari_err_SQRTN("gsqrt",x);
+      }
+      gel(y,2) = a; return y;
+    }
+
+    case t_COMPLEX:
+    { /* (u+iv)^2 = a+ib <=> u^2+v^2 = sqrt(a^2+b^2), u^2-v^2=a, 2uv=b */
+      GEN a = gel(x,1), b = gel(x,2), r, u, v;
+      if (isrationalzero(b)) return gsqrt(a, prec);
+      y = cgetg(3,t_COMPLEX); av = avma;
+
+      r = cxnorm(x);
+      if (typ(r) == t_INTMOD) pari_err_IMPL("sqrt(complex of t_INTMODs)");
+      r = gsqrt(r, prec); /* t_REAL, |a+Ib| */
+      if (!signe(r))
+        u = v = gerepileuptoleaf(av, sqrtr(r));
+      else if (gsigne(a) < 0)
+      {
+        /* v > 0 since r > 0, a < 0, rounding errors can't make the sum of two
+         * positive numbers = 0 */
+        v = sqrtr( gmul2n(gsub(r,a), -1) );
+        if (gsigne(b) < 0) togglesign(v);
+        v = gerepileuptoleaf(av, v); av = avma;
+        /* v = 0 is impossible */
+        u = gerepileuptoleaf(av, gdiv(b, shiftr(v,1)));
+      } else {
+        u = sqrtr( gmul2n(gadd(r,a), -1) );
+        u = gerepileuptoleaf(av, u); av = avma;
+        if (!signe(u)) /* possible if a = 0.0, e.g. sqrt(0.e-10+1e-10*I) */
+          v = u;
+        else
+          v = gerepileuptoleaf(av, gdiv(b, shiftr(u,1)));
+      }
+      gel(y,1) = u;
+      gel(y,2) = v; return y;
+    }
+
+    case t_PADIC:
+      y = Qp_sqrt(x);
+      if (!y) pari_err_SQRTN("Qp_sqrt",x);
+      return y;
+
+    case t_FFELT: return FF_sqrt(x);
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      return gerepilecopy(av, sqrt_ser(y, prec));
+  }
+  return trans_eval("sqrt",gsqrt,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                          N-th ROOT                             **/
+/**                                                                **/
+/********************************************************************/
+/* exp(2Ipi/n), assume n positive t_INT */
+GEN
+rootsof1complex(GEN n, long prec)
+{
+  pari_sp av = avma;
+  if (is_pm1(n)) return real_1(prec);
+  if (equaliu(n, 2)) return stor(-1, prec);
+  return gerepileupto(av, expIr( divri(Pi2n(1, prec), n) ));
+}
+
+/*Only the O() of y is used*/
+GEN
+rootsof1padic(GEN n, GEN y)
+{
+  GEN z, r = cgetp(y), p = gel(y,2);
+  pari_sp av = avma;
+  z = rootsof1_Fp(n, p);
+  z = Zp_sqrtnlift(gen_1, n, z, p, precp(y));
+  affii(z, gel(r,4)); avma = av; return r;
+}
+
+/* Let x = 1 mod p and y := (x-1)/(x+1) = 0 (p). Then
+ * log(x) = log(1+y) - log(1-y) = 2 \sum_{k odd} y^k / k.
+ * palogaux(x) returns the last sum (not multiplied by 2) */
+static GEN
+palogaux(GEN x)
+{
+  long k,e,pp;
+  GEN y,s,y2, p = gel(x,2);
+
+  if (equalii(gen_1, gel(x,4)))
+  {
+    long v = valp(x)+precp(x);
+    if (equaliu(p,2)) v--;
+    return zeropadic(p, v);
+  }
+  y = gdiv(gaddgs(x,-1), gaddgs(x,1));
+  e = valp(y); /* > 0 */
+  if (e <= 0) {
+    if (!BPSW_psp(p)) pari_err_PRIME("p-adic log",p);
+    pari_err_BUG("log_p");
+  }
+  pp = e+precp(y);
+  if (equaliu(p,2)) pp--;
+  else
+  {
+    GEN p1;
+    for (p1=utoipos(e); cmpui(pp,p1) > 0; pp++) p1 = mulii(p1, p);
+    pp -= 2;
+  }
+  k = pp/e; if (!odd(k)) k--;
+  y2 = gsqr(y); s = gdivgs(gen_1,k);
+  while (k > 2)
+  {
+    k -= 2; s = gadd(gmul(y2,s), gdivgs(gen_1,k));
+  }
+  return gmul(s,y);
+}
+
+GEN
+Qp_log(GEN x)
+{
+  pari_sp av = avma;
+  GEN y, p = gel(x,2), a = gel(x,4);
+
+  if (!signe(a)) pari_err_DOMAIN("Qp_log", "argument", "=", gen_0, x);
+  y = leafcopy(x); setvalp(y,0);
+  if (equaliu(p,2))
+    y = palogaux(gsqr(y));
+  else if (gequal1(modii(a, p)))
+    y = gmul2n(palogaux(y), 1);
+  else
+  { /* compute log(x^(p-1)) / (p-1) */
+    GEN mod = gel(y,3), p1 = subis(p,1);
+    gel(y,4) = Fp_pow(a, p1, mod);
+    p1 = diviiexact(subsi(1,mod), p1); /* 1/(p-1) */
+    y = gmul(palogaux(y), shifti(p1,1));
+  }
+  return gerepileupto(av,y);
+}
+
+static GEN Qp_exp_safe(GEN x);
+
+/*compute the p^e th root of x p-adic, assume x != 0 */
+static GEN
+Qp_sqrtn_ram(GEN x, long e)
+{
+  pari_sp ltop=avma;
+  GEN a, p = gel(x,2), n = powiu(p,e);
+  long v = valp(x), va;
+  if (v)
+  {
+    long z;
+    v = sdivsi_rem(v, n, &z);
+    if (z) return NULL;
+    x = leafcopy(x);
+    setvalp(x,0);
+  }
+  /*If p = 2, -1 is a root of 1 in U1: need extra check*/
+  if (equaliu(p, 2) && mod8(gel(x,4)) != 1) return NULL;
+  a = Qp_log(x);
+  va = valp(a) - e;
+  if (va <= 0)
+  {
+    if (signe(gel(a,4))) return NULL;
+    /* all accuracy lost */
+    a = cvtop(remii(gel(x,4),p), p, 1);
+  }
+  else
+  {
+    setvalp(a, va); /* divide by p^e */
+    a = Qp_exp_safe(a);
+    if (!a) return NULL;
+    /* n=p^e and a^n=z*x where z is a (p-1)th-root of 1.
+     * Since z^n=z, we have (a/z)^n = x. */
+    a = gdiv(x, powgi(a,addis(n,-1))); /* = a/z = x/a^(n-1)*/
+    if (v) setvalp(a,v);
+  }
+  return gerepileupto(ltop,a);
+}
+
+/*compute the nth root of x p-adic p prime with n*/
+static GEN
+Qp_sqrtn_unram(GEN x, GEN n, GEN *zetan)
+{
+  pari_sp av;
+  GEN Z, a, r, p = gel(x,2);
+  long v = valp(x);
+  if (v)
+  {
+    long z;
+    v = sdivsi_rem(v,n,&z);
+    if (z) return NULL;
+  }
+  r = cgetp(x); setvalp(r,v);
+  Z = NULL; /* -Wall */
+  if (zetan) Z = cgetp(x);
+  av = avma; a = Fp_sqrtn(gel(x,4), n, p, zetan);
+  if (!a) return NULL;
+  affii(Zp_sqrtnlift(gel(x,4), n, a, p, precp(x)), gel(r,4));
+  if (zetan)
+  {
+    affii(Zp_sqrtnlift(gen_1, n, *zetan, p, precp(x)), gel(Z,4));
+    *zetan = Z;
+  }
+  avma = av; return r;
+}
+
+GEN
+Qp_sqrtn(GEN x, GEN n, GEN *zetan)
+{
+  pari_sp av, tetpil;
+  GEN q, p;
+  long e;
+  if (equaliu(n, 2))
+  {
+    if (zetan) *zetan = gen_m1;
+    if (signe(n) < 0) x = ginv(x);
+    return Qp_sqrt(x);
+  }
+  av = avma; p = gel(x,2);
+  if (!signe(gel(x,4)))
+  {
+    if (signe(n) < 0) pari_err_INV("Qp_sqrtn", x);
+    q = divii(addis(n, valp(x)-1), n);
+    if (zetan) *zetan = gen_1;
+    avma = av; return zeropadic(p, itos(q));
+  }
+  /* treat the ramified part using logarithms */
+  e = Z_pvalrem(n, p, &q);
+  if (e) { x = Qp_sqrtn_ram(x,e); if (!x) return NULL; }
+  if (is_pm1(q))
+  { /* finished */
+    if (signe(q) < 0) x = ginv(x);
+    x = gerepileupto(av, x);
+    if (zetan)
+      *zetan = (e && equaliu(p, 2))? gen_m1 /*-1 in Q_2*/
+                                   : gen_1;
+    return x;
+  }
+  tetpil = avma;
+  /* use hensel lift for unramified case */
+  x = Qp_sqrtn_unram(x, q, zetan);
+  if (!x) return NULL;
+  if (zetan)
+  {
+    GEN *gptr[2];
+    if (e && equaliu(p, 2))/*-1 in Q_2*/
+    {
+      tetpil = avma; x = gcopy(x); *zetan = gneg(*zetan);
+    }
+    gptr[0] = &x; gptr[1] = zetan;
+    gerepilemanysp(av,tetpil,gptr,2);
+    return x;
+  }
+  return gerepile(av,tetpil,x);
+}
+
+GEN
+sqrtnint(GEN a, long n)
+{
+  pari_sp ltop = avma;
+  GEN x, b, q;
+  long s, k, e;
+  const ulong nm1 = n - 1;
+  if (typ(a) != t_INT) pari_err_TYPE("sqrtnint",a);
+  if (n <= 0) pari_err_DOMAIN("sqrtnint", "n", "<=", gen_0, stoi(n));
+  if (n == 1) return icopy(a);
+  s = signe(a);
+  if (s < 0) pari_err_DOMAIN("sqrtnint", "x", "<", gen_0, a);
+  if (!s) return gen_0;
+  if (lgefint(a) == 3) return utoi(usqrtn(itou(a), n));
+  e = expi(a); k = e/(2*n);
+  if (k == 0)
+  {
+    long flag;
+    if (n > e) {avma = ltop; return gen_1;}
+    flag = cmpii(a, powuu(3, n)); avma = ltop;
+    return (flag < 0) ? gen_2: stoi(3);
+  }
+  if (e < n*(BITS_IN_LONG - 1))
+  {
+    ulong s, xs, qs;
+    s = 1 + e/n; xs = 1UL << s;
+    qs = itou(shifti(a, -nm1*s));
+    while (qs < xs) {
+      xs -= (xs - qs + nm1)/n;
+      q = divii(a, powuu(xs, nm1));
+      if (lgefint(q) > 3) break;
+      qs = itou(q);
+    }
+    return utoi(xs);
+  }
+  b = addsi(1, shifti(a, -n*k));
+  x = shifti(addsi(1, sqrtnint(b, n)), k);
+  q = divii(a, powiu(x, nm1));
+  while (cmpii(q, x) < 0) /* a priori one iteration, no GC necessary */
+  {
+    x = subii(x, divis(addsi(nm1, subii(x, q)), n));
+    q = divii(a, powiu(x, nm1));
+  }
+  return gerepileuptoleaf(ltop, x);
+}
+
+ulong
+usqrtn(ulong a, ulong n)
+{
+  ulong x, s, q;
+  const ulong nm1 = n - 1;
+  if (!n) pari_err_DOMAIN("sqrtnint", "n", "=", gen_0, utoi(n));
+  if (n == 1 || a == 0) return a;
+  s = 1 + expu(a)/n; x = 1UL << s;
+  q = (nm1*s >= BITS_IN_LONG)? 0: a >> (nm1*s);
+  while (q < x) {
+    ulong X;
+    x -= (x - q + nm1)/n;
+    X = upowuu(x, nm1);
+    q = X? a/X: 0;
+  }
+  return x;
+}
+
+GEN
+gsqrtn(GEN x, GEN n, GEN *zetan, long prec)
+{
+  long i, lx, tx;
+  pari_sp av;
+  GEN y, z;
+  if (typ(n)!=t_INT) pari_err_TYPE("sqrtn",n);
+  if (!signe(n)) pari_err_DOMAIN("sqrtn", "n", "=", gen_0, n);
+  if (is_pm1(n))
+  {
+    if (zetan) *zetan = gen_1;
+    return (signe(n) > 0)? gcopy(x): ginv(x);
+  }
+  if (zetan) *zetan = gen_0;
+  tx = typ(x);
+  if (is_matvec_t(tx))
+  {
+    y = cgetg_copy(x, &lx);
+    for (i=1; i<lx; i++) gel(y,i) = gsqrtn(gel(x,i),n,NULL,prec);
+    return y;
+  }
+  av = avma;
+  switch(tx)
+  {
+  case t_INTMOD:
+    {
+      GEN p = gel(x,1), s;
+      z = gen_0;
+      y = cgetg(3,t_INTMOD);  gel(y,1) = icopy(p);
+      if (zetan) { z = cgetg(3,t_INTMOD); gel(z,1) = gel(y,1); }
+      s = Fp_sqrtn(gel(x,2),n,p,zetan);
+      if (!s) {
+        if (zetan) {avma=av; return gen_0;}
+        if (!BPSW_psp(p)) pari_err_PRIME("sqrtn [modulus]",p);
+        pari_err_SQRTN("gsqrtn",x);
+      }
+      gel(y,2) = s;
+      if (zetan) { gel(z,2) = *zetan; *zetan = z; }
+      return y;
+    }
+
+  case t_PADIC:
+    y = Qp_sqrtn(x,n,zetan);
+    if (!y) {
+      if (zetan) return gen_0;
+      pari_err_SQRTN("gsqrtn",x);
+    }
+    return y;
+
+  case t_FFELT: return FF_sqrtn(x,n,zetan);
+
+  case t_INT: case t_FRAC: case t_REAL: case t_COMPLEX:
+    i = precision(x); if (i) prec = i;
+    if (isint1(x))
+      y = real_1(prec);
+    else if (gequal0(x))
+    {
+      long b;
+      if (signe(n) < 0) pari_err_INV("gsqrtn",x);
+      if (isinexactreal(x))
+        b = sdivsi(gexpo(x), n);
+      else
+        b = -prec2nbits(prec);
+      if (typ(x) == t_COMPLEX)
+      {
+        y = cgetg(3,t_COMPLEX);
+        gel(y,1) = gel(y,2) = real_0_bit(b);
+      }
+      else
+        y = real_0_bit(b);
+    }
+    else
+      y = gerepileupto(av, gexp(gdiv(glog(x,prec), n), prec));
+    if (zetan) *zetan = rootsof1complex(n,prec);
+    return y;
+
+  case t_QUAD:
+    return gsqrtn(quadtofp(x, prec), n, zetan, prec);
+
+  default:
+    av = avma; if (!(y = toser_i(x))) break;
+    return gerepileupto(av, ser_powfrac(y, ginv(n), prec));
+  }
+  pari_err_TYPE("sqrtn",x);
+  return NULL;/* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                             EXP(X) - 1                         **/
+/**                                                                **/
+/********************************************************************/
+/* exp(|x|) - 1, assume x != 0.
+ * For efficiency, x should be reduced mod log(2): if so, we have a < 0 */
+GEN
+exp1r_abs(GEN x)
+{
+  long l = realprec(x), a = expo(x), b = prec2nbits(l), L, i, n, m, B;
+  GEN y, p2, X;
+  pari_sp av;
+  double d;
+
+  if (b + a <= 0) return mpabs(x);
+
+  y = cgetr(l); av = avma;
+  B = b/3 + BITS_IN_LONG + (BITS_IN_LONG*BITS_IN_LONG)/ b;
+  d = a/2.; m = (long)(d + sqrt(d*d + B)); /* >= 0 */
+  if (m < (-a) * 0.1) m = 0; /* not worth it */
+  L = l + nbits2extraprec(m);
+ /* Multiplication is quadratic in this range (l is small, otherwise we
+  * use logAGM + Newton). Set Y = 2^(-e-a) x, compute truncated series
+  * sum x^k/k!: this costs roughly
+  *    m b^2 + sum_{k <= n} (k e + BITS_IN_LONG)^2
+  * bit operations with |x| <  2^(1+a), |Y| < 2^(1-e) , m = e+a and b bits of
+  * accuracy needed, so
+  *    B := (b / 3 + BITS_IN_LONG + BITS_IN_LONG^2 / b) ~ m(m-a)
+  * we want b ~ 3 m (m-a) or m~b+a hence
+  *     m = min( a/2 + sqrt(a^2/4 + B),  b + a )
+  * NB: e ~ (b/3)^(1/2) as b -> oo
+  *
+  * Truncate the sum at k = n (>= 1), the remainder is
+  *   sum_{k >= n+1} Y^k / k! < Y^(n+1) / (n+1)! (1-Y) < Y^(n+1) / n!
+  * We want Y^(n+1) / n! <= Y 2^-b, hence -n log_2 |Y| + log_2 n! >= b
+  *   log n! ~ (n + 1/2) log(n+1) - (n+1) + log(2Pi)/2,
+  * error bounded by 1/6(n+1) <= 1/12. Finally, we want
+  * n (-1/log(2) -log_2 |Y| + log_2(n+1)) >= b  */
+  b += m;
+  d = m-dbllog2(x)-1/LOG2; /* ~ -log_2 Y - 1/log(2) */
+  n = (long)(b / d);
+  if (n > 1)
+    n = (long)(b / (d + log2((double)n+1))); /* log~constant in small ranges */
+  while (n*(d+log2((double)n+1)) < b) n++; /* expect few corrections */
+
+  X = rtor(x,L); shiftr_inplace(X, -m); setsigne(X, 1);
+  if (n == 1) p2 = X;
+  else
+  {
+    long s = 0, l1 = nbits2prec((long)(d + n + 16));
+    GEN unr = real_1(L);
+    pari_sp av2;
+
+    p2 = cgetr(L); av2 = avma;
+    for (i=n; i>=2; i--, avma = av2)
+    { /* compute X^(n-1)/n! + ... + X/2 + 1 */
+      GEN p1, p3;
+      setprec(X,l1); p3 = divru(X,i);
+      l1 += dvmdsBIL(s - expo(p3), &s); if (l1>L) l1=L;
+      setprec(unr,l1); p1 = addrr_sign(unr,1, i == n? p3: mulrr(p3,p2),1);
+      setprec(p2,l1); affrr(p1,p2); /* p2 <- 1 + (X/i)*p2 */
+    }
+    setprec(X,L); p2 = mulrr(X,p2);
+  }
+
+  for (i=1; i<=m; i++)
+  {
+    if (realprec(p2) > L) setprec(p2,L);
+    p2 = mulrr(p2, addsr(2,p2));
+  }
+  affrr_fixlg(p2,y); avma = av; return y;
+}
+
+GEN
+mpexpm1(GEN x)
+{
+  long sx = signe(x);
+  GEN y, z;
+  pari_sp av;
+  if (!sx) return real_0_bit(expo(x));
+  if (sx > 0) return exp1r_abs(x);
+  /* compute exp(x) * (1 - exp(-x)) */
+  av = avma; y = exp1r_abs(x);
+  z = addsr(1, y); setsigne(z, -1);
+  return gerepileupto(av, divrr(y, z));
+}
+
+GEN
+gexpm1(GEN x, long prec)
+{
+  switch(typ(x))
+  {
+    case t_REAL: return mpexpm1(x);
+    case t_COMPLEX: return cxexpm1(x,prec);
+  }
+  return trans_eval("expm1",gexpm1,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                             EXP(X)                             **/
+/**                                                                **/
+/********************************************************************/
+
+/* centermod(x, log(2)), set *sh to the quotient */
+static GEN
+modlog2(GEN x, long *sh)
+{
+  double d = rtodbl(x);
+  long q = (long) ((fabs(d) + (LOG2/2))/LOG2);
+  if (d > LOG2 * LONG_MAX)
+    pari_err_OVERFLOW("expo()"); /* avoid overflow in  q */
+  if (d < 0) q = -q;
+  *sh = q;
+  if (q) {
+    long l = realprec(x) + 1;
+    x = subrr(rtor(x,l), mulsr(q, mplog2(l)));
+    if (!signe(x)) return NULL;
+  }
+  return x;
+}
+
+static GEN
+mpexp_basecase(GEN x)
+{
+  pari_sp av = avma;
+  long sh, l = realprec(x);
+  GEN y, z;
+
+  y = modlog2(x, &sh);
+  if (!y) { avma = av; return real2n(sh, l); }
+  z = addsr(1, exp1r_abs(y));
+  if (signe(y) < 0) z = invr(z);
+  if (sh) {
+    shiftr_inplace(z, sh);
+    if (realprec(z) > l) z = rtor(z, l); /* spurious precision increase */
+  }
+#ifdef DEBUG
+{
+  GEN t = mplog(z), u = divrr(subrr(x, t),x);
+  if (signe(u) && expo(u) > 5-prec2nbits(minss(l,realprec(t))))
+    pari_err_BUG("exp");
+}
+#endif
+  return gerepileuptoleaf(av, z); /* NOT affrr, precision often increases */
+}
+
+GEN
+mpexp(GEN x)
+{
+  const long s = 6; /*Initial steps using basecase*/
+  long i, p, l = realprec(x), sh;
+  GEN a, t, z;
+  ulong mask;
+
+  if (l <= maxss(EXPNEWTON_LIMIT, (1L<<s) + 2))
+  {
+    if (!signe(x)) return mpexp0(x);
+    return mpexp_basecase(x);
+  }
+  z = cgetr(l); /* room for result */
+  x = modlog2(x, &sh);
+  if (!x) { avma = (pari_sp)(z+lg(z)); return real2n(sh, l); }
+  constpi(l); /* precompute for later logr_abs() */
+  mask = quadratic_prec_mask(prec2nbits(l)+BITS_IN_LONG);
+  for(i=0, p=1; i<s+TWOPOTBITS_IN_LONG; i++) { p <<= 1; if (mask & 1) p-=1; mask >>= 1; }
+  a = mpexp_basecase(rtor(x, nbits2prec(p)));
+  x = addrs(x,1);
+  if (realprec(x) < l+EXTRAPRECWORD) x = rtor(x, l+EXTRAPRECWORD);
+  a = rtor(a, l+EXTRAPRECWORD); /*append 0s */
+  t = NULL;
+  for(;;)
+  {
+    p <<= 1; if (mask & 1) p--;
+    mask >>= 1;
+    setprec(x, nbits2prec(p));
+    setprec(a, nbits2prec(p));
+    t = mulrr(a, subrr(x, logr_abs(a))); /* a (x - log(a)) */
+    if (mask == 1) break;
+    affrr(t, a); avma = (pari_sp)a;
+  }
+  affrr(t,z);
+  if (sh) shiftr_inplace(z, sh);
+  avma = (pari_sp)z; return z;
+}
+
+static long
+Qp_exp_prec(GEN x)
+{
+  long k, e = valp(x), n = e + precp(x);
+  GEN p = gel(x,2);
+  int is2 = equaliu(p,2);
+  if (e < 1 || (e == 1 && is2)) return -1;
+  if (is2)
+  {
+    n--; e--; k = n/e;
+    if (n%e == 0) k--;
+  }
+  else
+  { /* e > 0, n > 0 */
+    GEN r, t = subis(p, 1);
+    k = itos(dvmdii(subis(muliu(t,n), 1), subis(muliu(t,e), 1), &r));
+    if (!signe(r)) k--;
+  }
+  return k;
+}
+
+static GEN
+Qp_exp_safe(GEN x)
+{
+  long k;
+  pari_sp av;
+  GEN y;
+
+  if (gequal0(x)) return gaddgs(x,1);
+  k = Qp_exp_prec(x);
+  if (k < 0) return NULL;
+  av = avma;
+  for (y=gen_1; k; k--) y = gaddsg(1, gdivgs(gmul(y,x), k));
+  return gerepileupto(av, y);
+}
+
+GEN
+Qp_exp(GEN x)
+{
+  GEN y = Qp_exp_safe(x);
+  if (!y) pari_err_DOMAIN("gexp(t_PADIC)","argument","",gen_0,x);
+  return y;
+}
+
+static GEN
+cos_p(GEN x)
+{
+  long k;
+  pari_sp av;
+  GEN x2, y;
+
+  if (gequal0(x)) return gaddgs(x,1);
+  k = Qp_exp_prec(x);
+  if (k < 0) return NULL;
+  av = avma; x2 = gsqr(x);
+  if (k & 1) k--;
+  for (y=gen_1; k; k-=2)
+  {
+    GEN t = gdiv(gmul(y,x2), muluu(k, k-1));
+    y = gsubsg(1, t);
+  }
+  return gerepileupto(av, y);
+}
+static GEN
+sin_p(GEN x)
+{
+  long k;
+  pari_sp av;
+  GEN x2, y;
+
+  if (gequal0(x)) return gcopy(x);
+  k = Qp_exp_prec(x);
+  if (k < 0) return NULL;
+  av = avma; x2 = gsqr(x);
+  if (k & 1) k--;
+  for (y=gen_1; k; k-=2)
+  {
+    GEN t = gdiv(gmul(y,x2), muluu(k, k+1));
+    y = gsubsg(1, t);
+  }
+  return gerepileupto(av, gmul(y, x));
+}
+
+static GEN
+cxexp(GEN x, long prec)
+{
+  GEN r,p1,p2, y = cgetg(3,t_COMPLEX);
+  pari_sp av = avma, tetpil;
+  r = gexp(gel(x,1),prec);
+  if (gequal0(r)) { gel(y,1) = r; gel(y,2) = r; return y; }
+  gsincos(gel(x,2),&p2,&p1,prec);
+  tetpil = avma;
+  gel(y,1) = gmul(r,p1);
+  gel(y,2) = gmul(r,p2);
+  gerepilecoeffssp(av,tetpil,y+1,2);
+  return y;
+}
+
+/* given a t_SER x^v s(x), with s(0) != 0, return x^v(s - s(0)), shallow */
+GEN
+serchop0(GEN s)
+{
+  long i, l = lg(s);
+  GEN y;
+  if (l == 2) return s;
+  y = cgetg(l, t_SER); y[1] = s[1];
+  gel(y,2) = gen_0; for (i=3; i <l; i++) gel(y,i) = gel(s,i);
+  return normalize(y);
+}
+
+static GEN
+serexp(GEN x, long prec)
+{
+  pari_sp av;
+  long i,j,lx,ly,ex,mi;
+  GEN p1,y,xd,yd;
+
+  ex = valp(x);
+  if (ex < 0) pari_err_DOMAIN("exp","valuation", "<", gen_0, x);
+  if (gequal0(x)) return gaddsg(1,x);
+  lx = lg(x);
+  if (ex)
+  {
+    ly = lx+ex; y = cgetg(ly,t_SER);
+    mi = lx-1; while (mi>=3 && isrationalzero(gel(x,mi))) mi--;
+    mi += ex-2;
+    y[1] = evalsigne(1) | _evalvalp(0) | evalvarn(varn(x));
+    /* zd[i] = coefficient of X^i in z */
+    xd = x+2-ex; yd = y+2; ly -= 2;
+    gel(yd,0) = gen_1;
+    for (i=1; i<ex; i++) gel(yd,i) = gen_0;
+    for (   ; i<ly; i++)
+    {
+      av = avma; p1 = gen_0;
+      for (j=ex; j<=minss(i,mi); j++)
+        p1 = gadd(p1, gmulgs(gmul(gel(xd,j),gel(yd,i-j)),j));
+      gel(yd,i) = gerepileupto(av, gdivgs(p1,i));
+    }
+    return y;
+  }
+  av = avma;
+  return gerepileupto(av, gmul(gexp(gel(x,2),prec), serexp(serchop0(x),prec)));
+}
+
+GEN
+gexp(GEN x, long prec)
+{
+  switch(typ(x))
+  {
+    case t_REAL: return mpexp(x);
+    case t_COMPLEX: return cxexp(x,prec);
+    case t_PADIC: return Qp_exp(x);
+    default:
+    {
+      pari_sp av = avma;
+      GEN y;
+      if (!(y = toser_i(x))) break;
+      return gerepileupto(av, serexp(y,prec));
+    }
+  }
+  return trans_eval("exp",gexp,x,prec);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           AGM(X, Y)                            **/
+/**                                                                **/
+/********************************************************************/
+static int
+agmr_gap(GEN a, GEN b, long L)
+{
+  GEN d = subrr(b, a);
+  return (signe(d) && expo(d) - expo(b) >= L);
+}
+/* assume x > 0 */
+static GEN
+agm1r_abs(GEN x)
+{
+  long l = realprec(x), L = 5-prec2nbits(l);
+  GEN a1, b1, y = cgetr(l);
+  pari_sp av = avma;
+
+  a1 = addrr(real_1(l), x); shiftr_inplace(a1, -1);
+  b1 = sqrtr_abs(x);
+  while (agmr_gap(a1,b1,L))
+  {
+    GEN a = a1;
+    a1 = addrr(a,b1); shiftr_inplace(a1, -1);
+    b1 = sqrtr_abs(mulrr(a,b1));
+  }
+  affrr_fixlg(a1,y); avma = av; return y;
+}
+
+struct agmcx_gap_t { long L, ex, cnt; };
+
+static void
+agmcx_init(GEN x, long *prec, struct agmcx_gap_t *S)
+{
+  long l = precision(x);
+  if (l) *prec = l;
+  S->L = 1-prec2nbits(*prec);
+  S->cnt = 0;
+  S->ex = LONG_MAX;
+}
+
+static long
+agmcx_a_b(GEN x, GEN *a1, GEN *b1, long prec)
+{
+  long rotate = 0;
+  if (gsigne(real_i(x))<0)
+  { /* Rotate by +/-Pi/2, so that the choice of the principal square
+     * root gives the optimal AGM. So a1 = +/-I*a1, b1=sqrt(-x). */
+    if (gsigne(imag_i(x))<0) { *a1=mulcxI(*a1);  rotate=-1; }
+    else                     { *a1=mulcxmI(*a1); rotate=1; }
+    x = gneg(x);
+  }
+  *b1 = gsqrt(x, prec);
+  return rotate;
+}
+/* return 0 if we must stop the AGM loop (a=b or a ~ b), 1 otherwise */
+static int
+agmcx_gap(GEN a, GEN b, struct agmcx_gap_t *S)
+{
+  GEN d = gsub(b, a);
+  long ex = S->ex;
+  S->ex = gexpo(d);
+  if (gequal0(d) || S->ex - gexpo(b) < S->L) return 0;
+  /* if (S->ex >= ex) we're no longer making progress; twice in a row */
+  if (S->ex < ex) S->cnt = 0;
+  else
+    if (S->cnt++) return 0;
+  return 1;
+}
+static GEN
+agm1cx(GEN x, long prec)
+{
+  struct agmcx_gap_t S;
+  GEN a1, b1;
+  pari_sp av = avma;
+  long rotate;
+  agmcx_init(x, &prec, &S);
+  a1 = gtofp(gmul2n(gadd(real_1(prec), x), -1), prec);
+  rotate = agmcx_a_b(x, &a1, &b1, prec);
+  while (agmcx_gap(a1,b1,&S))
+  {
+    GEN a = a1;
+    a1 = gmul2n(gadd(a,b1),-1);
+    b1 = gsqrt(gmul(a,b1), prec);
+  }
+  if (rotate) a1 = rotate>0 ? mulcxI(a1):mulcxmI(a1);
+  return gerepilecopy(av,a1);
+}
+
+GEN
+zellagmcx(GEN a0, GEN b0, GEN r, GEN t, long prec)
+{
+  struct agmcx_gap_t S;
+  pari_sp av = avma;
+  GEN x = gdiv(a0, b0), a1, b1;
+  long rotate;
+  agmcx_init(x, &prec, &S);
+  a1 = gtofp(gmul2n(gadd(real_1(prec), x), -1), prec);
+  r = gsqrt(gdiv(gmul(a1,gaddgs(r, 1)),gadd(r, x)), prec);
+  t = gmul(r, t);
+  rotate = agmcx_a_b(x, &a1, &b1, prec);
+  while (agmcx_gap(a1,b1,&S))
+  {
+    GEN a = a1, b = b1;
+    a1 = gmul2n(gadd(a,b),-1);
+    b1 = gsqrt(gmul(a,b), prec);
+    r = gsqrt(gdiv(gmul(a1,gaddgs(r, 1)),gadd(gmul(b, r), a )), prec);
+    t = gmul(r, t);
+  }
+  if (rotate) a1 = rotate>0 ? mulcxI(a1):mulcxmI(a1);
+  a1 = gmul(a1, b0);
+  t = gatan(gdiv(a1,t), prec);
+  /* send t to the fundamental domain if necessary */
+  if (gsigne(real_i(t))<0) t = gadd(t, mppi(prec));
+  return gerepileupto(av,gdiv(t,a1));
+}
+
+/* agm(1,x) */
+static GEN
+agm1(GEN x, long prec)
+{
+  GEN p1, a, a1, b1, y;
+  long l, l2, ep;
+  pari_sp av;
+
+  if (gequal0(x)) return gcopy(x);
+  switch(typ(x))
+  {
+    case t_INT:
+      if (!is_pm1(x)) break;
+      return (signe(x) > 0)? real_1(prec): real_0(prec);
+
+    case t_REAL: return signe(x) > 0? agm1r_abs(x): agm1cx(x, prec);
+
+    case t_COMPLEX:
+      if (gequal0(gel(x,2))) return agm1(gel(x,1), prec);
+      return agm1cx(x, prec);
+
+    case t_PADIC:
+      av = avma;
+      a1 = x; b1 = gen_1; l = precp(x);
+      do
+      {
+        a = a1;
+        a1 = gmul2n(gadd(a,b1),-1);
+        b1 = Qp_sqrt(gmul(a,b1));
+        p1 = gsub(b1,a1); ep = valp(p1)-valp(b1);
+        if (ep<=0) { b1 = gneg_i(b1); p1 = gsub(b1,a1); ep=valp(p1)-valp(b1); }
+      }
+      while (ep<l && !gequal0(p1));
+      return gerepilecopy(av,a1);
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      a1 = y; b1 = gen_1; l = lg(y)-2;
+      l2 = 5-prec2nbits(prec);
+      do
+      {
+        a = a1;
+        a1 = gmul2n(gadd(a,b1),-1);
+        b1 = gsqrt(gmul(a,b1), prec);
+        p1 = gsub(b1,a1); ep = valp(p1)-valp(b1);
+      }
+      while (ep<l && !gequal0(p1)
+                  && (!isinexactreal(p1) || gexpo(p1) - gexpo(b1) >= l2));
+      return gerepilecopy(av,a1);
+  }
+  return trans_eval("agm",agm1,x,prec);
+}
+
+GEN
+agm(GEN x, GEN y, long prec)
+{
+  pari_sp av;
+  if (is_matvec_t(typ(y)))
+  {
+    if (is_matvec_t(typ(x))) pari_err_TYPE2("agm",x,y);
+    swap(x, y);
+  }
+  if (gequal0(y)) return gcopy(y);
+  av = avma;
+  return gerepileupto(av, gmul(y, agm1(gdiv(x,y), prec)));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                             LOG(X)                             **/
+/**                                                                **/
+/********************************************************************/
+/* atanh(u/v) using binary splitting */
+static GEN
+atanhQ_split(ulong u, ulong v, long prec)
+{
+  long i, nmax;
+  GEN u2 = sqru(u), v2 = sqru(v);
+  double d = ((double)v) / u;
+  struct abpq_res R;
+  struct abpq A;
+  /* satisfies (2n+1) (v/u)^2n > 2^bitprec */
+  nmax = bit_accuracy(prec) / (2*log2(d));
+  abpq_init(&A, nmax);
+  A.a[0] = A.b[0] = gen_1;
+  A.p[0] = utoipos(u);
+  A.q[0] = utoipos(v);
+  for (i = 1; i <= nmax; i++)
+  {
+    A.a[i] = gen_1;
+    A.b[i] = utoipos((i<<1)+1);
+    A.p[i] = u2;
+    A.q[i] = v2;
+  }
+  abpq_sum(&R, 0, nmax, &A);
+  return rdivii(R.T, mulii(R.B,R.Q),prec);
+}
+/* log(2) = 10*atanh(1/17)+4*atanh(13/499) */
+static GEN
+log2_split(long prec)
+{
+  GEN u = atanhQ_split(1, 17, prec);
+  GEN v = atanhQ_split(13, 499, prec);
+  shiftr_inplace(v, 2);
+  return addrr(mulur(10, u), v);
+}
+#if 0 /* slower ! */
+/* cf logagmr_abs(). Compute Pi/2agm(1, 4/2^n) ~ log(2^n) = n log(2) */
+static GEN
+log2_agm(long prec)
+{
+  long n = prec2nbits(prec) >> 1;
+  GEN y = divrr(Pi2n(-1, prec), agm1r_abs( real2n(2 - n, prec) ));
+  return divru(y, n);
+}
+#endif
+GEN
+constlog2(long prec)
+{
+  pari_sp av;
+  GEN tmp;
+  if (glog2 && realprec(glog2) >= prec) return glog2;
+
+  tmp = cgetr_block(prec);
+  av = avma;
+  affrr(log2_split(prec+EXTRAPRECWORD), tmp);
+  swap_clone(&glog2,tmp);
+  avma = av; return glog2;
+}
+
+GEN
+mplog2(long prec) { return rtor(constlog2(prec), prec); }
+
+static GEN
+logagmr_abs(GEN q)
+{
+  long prec = realprec(q), lim, e = expo(q);
+  GEN z, y, Q, _4ovQ;
+  pari_sp av;
+
+  if (absrnz_equal2n(q)) return e? mulsr(e, mplog2(prec)): real_0(prec);
+  z = cgetr(prec); av = avma; incrprec(prec);
+  lim = prec2nbits(prec) >> 1;
+  Q = rtor(q,prec);
+  shiftr_inplace(Q,lim-e); setsigne(Q,1);
+
+  _4ovQ = invr(Q); shiftr_inplace(_4ovQ, 2); /* 4/Q */
+  /* Pi / 2agm(1, 4/Q) ~ log(Q), q = Q * 2^(e-lim) */
+  y = divrr(Pi2n(-1, prec), agm1r_abs(_4ovQ));
+  y = addrr(y, mulsr(e - lim, mplog2(prec)));
+  affrr_fixlg(y, z); avma = av; return z;
+}
+/*return log(|x|), assuming x != 0 */
+GEN
+logr_abs(GEN X)
+{
+  pari_sp ltop;
+  long EX, L, m, k, a, b, l = realprec(X);
+  GEN z, x, y;
+  ulong u;
+  double d;
+
+  if (l > LOGAGM_LIMIT) return logagmr_abs(X);
+
+ /* Assuming 1 < x < 2, we want delta = x-1, 1-x/2, 1-1/x, or 2/x-1 small.
+  * We have 2/x-1 > 1-x/2, 1-1/x < x-1. So one should be choosing between
+  * 1-1/x and 1-x/2 ( crossover sqrt(2), worse ~ 0.29 ). To avoid an inverse,
+  * we choose between x-1 and 1-x/2 ( crossover 4/3, worse ~ 0.33 ) */
+  EX = expo(X);
+  u = (ulong)X[2];
+  k = 2;
+  if (u > (~0UL / 3) * 2) { /* choose 1-x/2 */
+    EX++; u = ~u;
+    while (!u && ++k < l) { u = (ulong)X[k]; u = ~u; }
+  } else { /* choose x - 1 */
+    u &= ~HIGHBIT; /* u - HIGHBIT, assuming HIGHBIT set */
+    while (!u && ++k < l) u = (ulong)X[k];
+  }
+  if (k == l) return EX? mulsr(EX, mplog2(l)): real_0(l);
+  z = cgetr(EX? l: l - (k-2)); ltop = avma;
+
+  a = prec2nbits(k) + bfffo(u); /* ~ -log2 |1-x| */
+ /* Multiplication is quadratic in this range (l is small, otherwise we
+  * use AGM). Set Y = x^(1/2^m), y = (Y - 1) / (Y + 1) and compute truncated
+  * series sum y^(2k+1)/(2k+1): the costs is less than
+  *    m b^2 + sum_{k <= n} ((2k+1) e + BITS_IN_LONG)^2
+  * bit operations with |x-1| <  2^(1-a), |Y| < 2^(1-e) , m = e-a and b bits of
+  * accuracy needed (+ BITS_IN_LONG since bit accuracies increase by
+  * increments of BITS_IN_LONG), so
+  * 4n^3/3 e^2 + n^2 2e BITS_IN_LONG+ n BITS_IN_LONG ~ m b^2, with n ~ b/2e
+  * or b/6e + BITS_IN_LONG/2e + BITS_IN_LONG/2be ~ m
+  *    B := (b / 6 + BITS_IN_LONG/2 + BITS_IN_LONG^2 / 2b) ~ m(m+a)
+  *     m = min( -a/2 + sqrt(a^2/4 + B),  b - a )
+  * NB: e ~ (b/6)^(1/2) as b -> oo */
+  L = l+1;
+  b = prec2nbits(L - (k-2)); /* take loss of accuracy into account */
+  /* instead of the above pessimistic estimate for the cost of the sum, use
+   * optimistic estimate (BITS_IN_LONG -> 0) */
+  d = -a/2.; m = (long)(d + sqrt(d*d + b/6)); /* >= 0 */
+
+  if (m > b-a) m = b-a;
+  if (m < 0.2*a) m = 0; else L += nbits2extraprec(m);
+  x = rtor(X,L);
+  setsigne(x,1); shiftr_inplace(x,-EX);
+  /* 2/3 < x < 4/3 */
+  for (k=1; k<=m; k++) x = sqrtr_abs(x);
+
+  y = divrr(subrs(x,1), addrs(x,1)); /* = (x-1) / (x+1), close to 0 */
+  L = realprec(y); /* should be ~ l+1 - (k-2) */
+  /* log(x) = log(1+y) - log(1-y) = 2 sum_{k odd} y^k / k
+   * Truncate the sum at k = 2n+1, the remainder is
+   *   2 sum_{k >= 2n+3} y^k / k < 2y^(2n+3) / (2n+3)(1-y) < y^(2n+3)
+   * We want y^(2n+3) < y 2^(-prec2nbits(L)), hence
+   *   n+1 > -prec2nbits(L) /-log_2(y^2) */
+  d = -2*dbllog2r(y); /* ~ -log_2(y^2) */
+  k = (long)(2*(prec2nbits(L) / d));
+  k |= 1;
+  if (k >= 3)
+  {
+    GEN S, T, y2 = sqrr(y), unr = real_1(L);
+    pari_sp av = avma;
+    long s = 0, incs = (long)d, l1 = nbits2prec((long)d);
+    S = x;
+    setprec(S,  l1);
+    setprec(unr,l1); affrr(divru(unr,k), S); /* destroy x, not needed anymore */
+    for (k -= 2;; k -= 2) /* k = 2n+1, ..., 1 */
+    { /* S = y^(2n+1-k)/(2n+1) + ... + 1 / k */
+      setprec(y2, l1); T = mulrr(S,y2);
+      if (k == 1) break;
+
+      l1 += dvmdsBIL(s + incs, &s); if (l1>L) l1=L;
+      setprec(S, l1);
+      setprec(unr,l1);
+      affrr(addrr(divru(unr, k), T), S); avma = av;
+    }
+    /* k = 1 special-cased for eficiency */
+    y = mulrr(y, addsr(1,T)); /* = log(X)/2 */
+  }
+  shiftr_inplace(y, m + 1);
+  if (EX) y = addrr(y, mulsr(EX, mplog2(l+1)));
+  affrr_fixlg(y, z); avma = ltop; return z;
+}
+
+/* assume Im(q) != 0 and precision(q) >= prec. Compute log(q) with accuracy
+ * prec [disregard input accuracy] */
+GEN
+logagmcx(GEN q, long prec)
+{
+  GEN z = cgetc(prec), y, Q, a, b;
+  long lim, e, ea, eb;
+  pari_sp av = avma;
+  int neg = 0;
+
+  incrprec(prec);
+  if (gsigne(gel(q,1)) < 0) { q = gneg(q); neg = 1; }
+  lim = prec2nbits(prec) >> 1;
+  Q = gtofp(q, prec);
+  a = gel(Q,1);
+  b = gel(Q,2);
+  if (gequal0(a)) {
+    affrr_fixlg(logr_abs(b), gel(z,1));
+    y = Pi2n(-1, prec);
+    if (signe(b) < 0) setsigne(y, -1);
+    affrr_fixlg(y, gel(z,2)); avma = av; return z;
+  }
+  ea = expo(a);
+  eb = expo(b);
+  e = ea <= eb ? lim - eb : lim - ea;
+  shiftr_inplace(a, e);
+  shiftr_inplace(b, e);
+
+  /* Pi / 2agm(1, 4/Q) ~ log(Q), q = Q * 2^e */
+  y = gdiv(Pi2n(-1, prec), agm1cx( gdivsg(4, Q), prec ));
+  a = gel(y,1);
+  b = gel(y,2);
+  a = addrr(a, mulsr(-e, mplog2(prec)));
+  if (realprec(a) <= LOWDEFAULTPREC) a = real_0_bit(expo(a));
+  if (neg) b = gsigne(b) <= 0? gadd(b, mppi(prec))
+                             : gsub(b, mppi(prec));
+  affrr_fixlg(a, gel(z,1));
+  affrr_fixlg(b, gel(z,2)); avma = av; return z;
+}
+
+GEN
+mplog(GEN x)
+{
+  if (signe(x)<=0) pari_err_DOMAIN("mplog", "argument", "<=", gen_0, x);
+  return logr_abs(x);
+}
+
+/* pe = p^e, p prime, 0 < x < pe a t_INT coprime to p. Return the (p-1)-th
+ * root of 1 in (Z/pe)^* congruent to x mod p, resp x mod 4 if p = 2.
+ * Simplified form of Zp_sqrtnlift: 1/(p-1) is trivial to compute */
+GEN
+Zp_teichmuller(GEN x, GEN p, long e, GEN pe)
+{
+  GEN q, z, p1;
+  pari_sp av;
+  ulong mask;
+  if (equaliu(p,2)) return (mod4(x) & 2)? subiu(pe,1): gen_1;
+  if (e == 1) return icopy(x);
+  av = avma;
+  p1 = subiu(p, 1);
+  mask = quadratic_prec_mask(e);
+  q = p; z = remii(x, p);
+  while (mask > 1)
+  { /* Newton iteration solving z^{1 - p} = 1, z = x (mod p) */
+    GEN w, t, qold = q;
+    if (mask <= 3) /* last iteration */
+      q = pe;
+    else
+    {
+      q = sqri(q);
+      if (mask & 1) q = diviiexact(q, p);
+    }
+    mask >>= 1;
+    /* q <= qold^2 */
+    if (lgefint(q) == 3)
+    {
+      ulong Z = (ulong)z[2], Q = (ulong)q[2], P1 = (ulong)p1[2];
+      ulong W = (Q-1) / P1; /* -1/(p-1) + O(qold) */
+      ulong T = Fl_mul(W, Fl_powu(Z,P1,Q) - 1, Q);
+      Z = Fl_mul(Z, 1 + T, Q);
+      z = utoi(Z);
+    }
+    else
+    {
+      w = diviiexact(addsi(-1,qold),p1); /* -1/(p-1) + O(qold) */
+      t = Fp_mul(w, subis(Fp_pow(z,p1,q), 1), q);
+      z = Fp_mul(z, addsi(1,t), q);
+    }
+  }
+  return gerepileuptoint(av, z);
+}
+
+GEN
+teich(GEN x)
+{
+  GEN p, q, y, z;
+  long n;
+
+  if (typ(x)!=t_PADIC) pari_err_TYPE("teichmuller",x);
+  z = gel(x,4);
+  if (!signe(z)) return gcopy(x);
+  p = gel(x,2);
+  q = gel(x,3);
+  n = precp(x);
+  y = cgetg(5,t_PADIC);
+  y[1] = evalprecp(n) | _evalvalp(0);
+  gel(y,2) = icopy(p);
+  gel(y,3) = icopy(q);
+  gel(y,4) = Zp_teichmuller(z, p, n, q);
+  return y;
+}
+
+GEN
+glog(GEN x, long prec)
+{
+  pari_sp av, tetpil;
+  GEN y, p1;
+  long l;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      if (signe(x) >= 0)
+      {
+        if (!signe(x)) pari_err_DOMAIN("log", "argument", "=", gen_0, x);
+        return logr_abs(x);
+      }
+      retmkcomplex(logr_abs(x), mppi(realprec(x)));
+
+    case t_FRAC:
+    {
+      GEN a, b;
+      long e1, e2;
+      av = avma;
+      a = gel(x,1);
+      b = gel(x,2);
+      e1 = expi(subii(a,b)); e2 = expi(b);
+      if (e2 > e1) prec += nbits2nlong(e2 - e1);
+      x = fractor(x, prec);
+      return gerepileupto(av, glog(x, prec));
+    }
+    case t_COMPLEX:
+      if (ismpzero(gel(x,2))) return glog(gel(x,1), prec);
+      l = precision(x); if (l > prec) prec = l;
+      if (prec >= LOGAGMCX_LIMIT) return logagmcx(x, prec);
+      y = cgetg(3,t_COMPLEX);
+      gel(y,2) = garg(x,prec);
+      av = avma; p1 = glog(cxnorm(x),prec); tetpil = avma;
+      gel(y,1) = gerepile(av,tetpil,gmul2n(p1,-1)); return y;
+
+    case t_PADIC: return Qp_log(x);
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (!signe(y)) pari_err_DOMAIN("log", "argument", "=", gen_0, x);
+      if (valp(y)) pari_err_DOMAIN("log", "series valuation", "!=", gen_0, x);
+      p1 = integser(gdiv(derivser(y), y)); /* log(y)' = y'/y */
+      if (!gequal1(gel(y,2))) p1 = gadd(p1, glog(gel(y,2),prec));
+      return gerepileupto(av, p1);
+  }
+  return trans_eval("log",glog,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                        SINE, COSINE                            **/
+/**                                                                **/
+/********************************************************************/
+
+/* Reduce x0 mod Pi/2 to x in [-Pi/4, Pi/4]. Return cos(x)-1 */
+static GEN
+mpcosm1(GEN x, long *ptmod8)
+{
+  long a = expo(x), l = realprec(x), b, L, i, n, m, B;
+  GEN y, p2, x2;
+  double d;
+
+  n = 0;
+  if (a >= 0)
+  {
+    long p;
+    GEN q;
+    if (a > 30)
+    {
+      GEN z, pitemp = Pi2n(-2, nbits2prec(a + 32));
+      z = addrr(x,pitemp); /* = x + Pi/4 */
+      if (expo(z) >= bit_prec(z) + 3) pari_err_PREC("mpcosm1");
+      shiftr_inplace(pitemp, 1);
+      q = floorr( divrr(z,pitemp) ); /* round ( x / (Pi/2) ) */
+      p = l+EXTRAPRECWORD; x = rtor(x,p);
+    } else {
+      q = stoi((long)floor(rtodbl(x) / (PI/2) + 0.5));
+      p = l;
+    }
+    if (signe(q))
+    {
+      x = subrr(x, mulir(q, Pi2n(-1,p))); /* x mod Pi/2  */
+      a = expo(x);
+      if (!signe(x) && a >= 0) pari_err_PREC("mpcosm1");
+      n = mod4(q); if (n && signe(q) < 0) n = 4 - n;
+    }
+  }
+  /* a < 0 */
+  b = signe(x); *ptmod8 = (b < 0)? 4 + n: n;
+  if (!b) return real_0_bit((expo(x)<<1) - 1);
+
+  b = prec2nbits(l);
+  if (b + (a<<1) <= 0) {
+    y = sqrr(x); shiftr_inplace(y, -1); setsigne(y, -1);
+    return y;
+  }
+
+  y = cgetr(l);
+  B = b/6 + BITS_IN_LONG + (BITS_IN_LONG*BITS_IN_LONG/2)/ b;
+  d = a/2.; m = (long)(d + sqrt(d*d + B)); /* >= 0 ,*/
+  if (m < (-a) * 0.1) m = 0; /* not worth it */
+  L = l + nbits2extraprec(m);
+
+  b += m;
+  d = 2.0 * (m-dbllog2r(x)-1/LOG2); /* ~ 2( - log_2 Y - 1/log(2) ) */
+  n = (long)(b / d);
+  if (n > 1)
+    n = (long)(b / (d + log2((double)n+1))); /* log~constant in small ranges */
+  while (n*(d+log2((double)n+1)) < b) n++; /* expect few corrections */
+
+ /* Multiplication is quadratic in this range (l is small, otherwise we
+  * use logAGM + Newton). Set Y = 2^(-e-a) x, compute truncated series
+  * sum Y^2k/(2k)!: this costs roughly
+  *   m b^2 + sum_{k <= n} (2k e + BITS_IN_LONG)^2
+  *   ~ floor(b/2e) b^2 / 3  + m b^2
+  * bit operations with |x| <  2^(1+a), |Y| < 2^(1-e) , m = e+a and b bits of
+  * accuracy needed, so
+  *    B := ( b / 6 + BITS_IN_LONG + BITS_IN_LONG^2 / 2b) ~ m(m-a)
+  * we want b ~ 6 m (m-a) or m~b+a hence
+  *     m = min( a/2 + sqrt(a^2/4 + b/6),  b/2 + a )
+  * NB1: e ~ (b/6)^(1/2) or b/2.
+  * NB2: We use b/4 instead of b/6 in the formula above: hand-optimized...
+  *
+  * Truncate the sum at k = n (>= 1), the remainder is
+  * < sum_{k >= n+1} Y^2k / 2k! < Y^(2n+2) / (2n+2)!(1-Y^2) < Y^(2n+2)/(2n+1)!
+  * We want ... <= Y^2 2^-b, hence -2n log_2 |Y| + log_2 (2n+1)! >= b
+  *   log n! ~ (n + 1/2) log(n+1) - (n+1) + log(2Pi)/2,
+  * error bounded by 1/6(n+1) <= 1/12. Finally, we want
+  * 2n (-1/log(2) - log_2 |Y| + log_2(2n+2)) >= b  */
+  x = rtor(x, L); shiftr_inplace(x, -m); setsigne(x, 1);
+  x2 = sqrr(x);
+  if (n == 1) { p2 = x2; shiftr_inplace(p2, -1); setsigne(p2, -1); } /*-Y^2/2*/
+  else
+  {
+    GEN unr = real_1(L);
+    pari_sp av;
+    long s = 0, l1 = nbits2prec((long)(d + n + 16));
+
+    p2 = cgetr(L); av = avma;
+    for (i=n; i>=2; i--)
+    {
+      GEN p1;
+      setprec(x2,l1); p1 = divrunu(x2, 2*i-1);
+      l1 += dvmdsBIL(s - expo(p1), &s); if (l1>L) l1=L;
+      if (i != n) p1 = mulrr(p1,p2);
+      setprec(unr,l1); p1 = addrr_sign(unr,1, p1,-signe(p1));
+      setprec(p2,l1); affrr(p1,p2); avma = av;
+    }
+    shiftr_inplace(p2, -1); togglesign(p2); /* p2 := -p2/2 */
+    setprec(x2,L); p2 = mulrr(x2,p2);
+  }
+  /* Now p2 = sum {1<= i <=n} (-1)^i x^(2i) / (2i)! ~ cos(x) - 1 */
+  for (i=1; i<=m; i++)
+  { /* p2 = cos(x)-1 --> cos(2x)-1 */
+    p2 = mulrr(p2, addsr(2,p2));
+    shiftr_inplace(p2, 1);
+    if ((i & 31) == 0) p2 = gerepileuptoleaf((pari_sp)y, p2);
+  }
+  affrr_fixlg(p2,y); return y;
+}
+
+/* sqrt (|1 - (1+x)^2|) = sqrt(|x*(x+2)|). Sends cos(x)-1 to |sin(x)| */
+static GEN
+mpaut(GEN x)
+{
+  pari_sp av = avma;
+  GEN t = mulrr(x, addsr(2,x)); /* != 0 */
+  if (!signe(t)) return real_0_bit(expo(t) >> 1);
+  return gerepileuptoleaf(av, sqrtr_abs(t));
+}
+
+/********************************************************************/
+/**                            COSINE                              **/
+/********************************************************************/
+
+GEN
+mpcos(GEN x)
+{
+  long mod8;
+  pari_sp av;
+  GEN y,p1;
+
+  if (!signe(x)) {
+    long l = nbits2prec(-expo(x));
+    if (l < LOWDEFAULTPREC) l = LOWDEFAULTPREC;
+    return real_1(l);
+  }
+
+  av = avma; p1 = mpcosm1(x,&mod8);
+  switch(mod8)
+  {
+    case 0: case 4: y = addsr(1,p1); break;
+    case 1: case 7: y = mpaut(p1); togglesign(y); break;
+    case 2: case 6: y = subsr(-1,p1); break;
+    default:        y = mpaut(p1); break; /* case 3: case 5: */
+  }
+  return gerepileuptoleaf(av, y);
+}
+
+/* convert INT or FRAC to REAL, which is later reduced mod 2Pi : avoid
+ * cancellation */
+static GEN
+tofp_safe(GEN x, long prec)
+{
+  return (typ(x) == t_INT || gexpo(x) > 0)? gadd(x, real_0(prec))
+                                          : fractor(x, prec);
+}
+
+GEN
+gcos(GEN x, long prec)
+{
+  pari_sp av;
+  GEN r, u, v, y, u1, v1;
+  long i;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mpcos(x);
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) return gcosh(gel(x,2), prec);
+      i = precision(x); if (!i) i = prec;
+      y = cgetc(i); av = avma;
+      r = gexp(gel(x,2),prec);
+      v1 = gmul2n(addrr(invr(r),r), -1); /* = cos(I*Im(x)) */
+      u1 = subrr(v1, r); /* = - I*sin(I*Im(x)) */
+      gsincos(gel(x,1),&u,&v,prec);
+      affrr_fixlg(gmul(v1,v), gel(y,1));
+      affrr_fixlg(gmul(u1,u), gel(y,2)); avma = av; return y;
+
+    case t_INT: case t_FRAC:
+      y = cgetr(prec); av = avma;
+      affrr_fixlg(mpcos(tofp_safe(x,prec)), y); avma = av; return y;
+
+    case t_PADIC: y = cos_p(x);
+      if (!y) pari_err_DOMAIN("gcos(t_PADIC)","argument","",gen_0,x);
+      return y;
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) return gerepileupto(av, gaddsg(1,y));
+      if (valp(y) < 0)
+        pari_err_DOMAIN("cos","valuation", "<", gen_0, x);
+      gsincos(y,&u,&v,prec);
+      return gerepilecopy(av,v);
+  }
+  return trans_eval("cos",gcos,x,prec);
+}
+/********************************************************************/
+/**                             SINE                               **/
+/********************************************************************/
+
+GEN
+mpsin(GEN x)
+{
+  long mod8;
+  pari_sp av;
+  GEN y,p1;
+
+  if (!signe(x)) return real_0_bit(expo(x));
+
+  av = avma; p1 = mpcosm1(x,&mod8);
+  switch(mod8)
+  {
+    case 0: case 6: y=mpaut(p1); break;
+    case 1: case 5: y=addsr(1,p1); break;
+    case 2: case 4: y=mpaut(p1); togglesign(y); break;
+    default:        y=subsr(-1,p1); break; /* case 3: case 7: */
+  }
+  return gerepileuptoleaf(av, y);
+}
+
+GEN
+gsin(GEN x, long prec)
+{
+  pari_sp av;
+  GEN r, u, v, y, v1, u1;
+  long i;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mpsin(x);
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) retmkcomplex(gen_0,gsinh(gel(x,2),prec));
+      i = precision(x); if (!i) i = prec;
+      y = cgetc(i); av = avma;
+      r = gexp(gel(x,2),prec);
+      v1 = gmul2n(addrr(invr(r),r), -1); /* = cos(I*Im(x)) */
+      u1 = subrr(r, v1); /* = I*sin(I*Im(x)) */
+      gsincos(gel(x,1),&u,&v,prec);
+      affrr_fixlg(gmul(v1,u), gel(y,1));
+      affrr_fixlg(gmul(u1,v), gel(y,2)); avma = av; return y;
+
+    case t_INT: case t_FRAC:
+      y = cgetr(prec); av = avma;
+      affrr_fixlg(mpsin(tofp_safe(x,prec)), y); avma = av; return y;
+
+    case t_PADIC: y = sin_p(x);
+      if (!y) pari_err_DOMAIN("gsin(t_PADIC)","argument","",gen_0,x);
+      return y;
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) return gerepilecopy(av, y);
+      if (valp(y) < 0)
+        pari_err_DOMAIN("sin","valuation", "<", gen_0, x);
+      gsincos(y,&u,&v,prec);
+      return gerepilecopy(av,u);
+  }
+  return trans_eval("sin",gsin,x,prec);
+}
+/********************************************************************/
+/**                       SINE, COSINE together                    **/
+/********************************************************************/
+
+void
+mpsincos(GEN x, GEN *s, GEN *c)
+{
+  long mod8;
+  pari_sp av, tetpil;
+  GEN p1, *gptr[2];
+
+  if (!signe(x))
+  {
+    long e = expo(x);
+    *s = real_0_bit(e);
+    *c = e >= 0? real_0_bit(e): real_1(nbits2prec(-e));
+    return;
+  }
+
+  av=avma; p1=mpcosm1(x,&mod8); tetpil=avma;
+  switch(mod8)
+  {
+    case 0: *c=addsr( 1,p1); *s=mpaut(p1); break;
+    case 1: *s=addsr( 1,p1); *c=mpaut(p1); togglesign(*c); break;
+    case 2: *c=subsr(-1,p1); *s=mpaut(p1); togglesign(*s); break;
+    case 3: *s=subsr(-1,p1); *c=mpaut(p1); break;
+    case 4: *c=addsr( 1,p1); *s=mpaut(p1); togglesign(*s); break;
+    case 5: *s=addsr( 1,p1); *c=mpaut(p1); break;
+    case 6: *c=subsr(-1,p1); *s=mpaut(p1); break;
+    case 7: *s=subsr(-1,p1); *c=mpaut(p1); togglesign(*c); break;
+  }
+  gptr[0]=s; gptr[1]=c;
+  gerepilemanysp(av,tetpil,gptr,2);
+}
+
+/* SINE and COSINE - 1 */
+void
+mpsincosm1(GEN x, GEN *s, GEN *c)
+{
+  long mod8;
+  pari_sp av, tetpil;
+  GEN p1, *gptr[2];
+
+  if (!signe(x))
+  {
+    long e = expo(x);
+    *s = real_0_bit(e);
+    *c = real_0_bit(2*e-1);
+    return;
+  }
+  av=avma; p1=mpcosm1(x,&mod8); tetpil=avma;
+  switch(mod8)
+  {
+    case 0: *c=rcopy(p1); *s=mpaut(p1); break;
+    case 1: *s=addsr(1,p1); *c=subrs(mpaut(p1),1); togglesign(*c); break;
+    case 2: *c=subsr(-2,p1); *s=mpaut(p1); togglesign(*s); break;
+    case 3: *s=subsr(-1,p1); *c=subrs(mpaut(p1),1); break;
+    case 4: *c=rcopy(p1); *s=mpaut(p1); togglesign(*s); break;
+    case 5: *s=addsr( 1,p1); *c=subrs(mpaut(p1),1); break;
+    case 6: *c=subsr(-2,p1); *s=mpaut(p1); break;
+    case 7: *s=subsr(-1,p1); *c=subsr(-1,mpaut(p1)); break;
+  }
+  gptr[0]=s; gptr[1]=c;
+  gerepilemanysp(av,tetpil,gptr,2);
+}
+
+/* return exp(ix), x a t_REAL */
+GEN
+expIr(GEN x)
+{
+  pari_sp av = avma;
+  GEN v = cgetg(3,t_COMPLEX);
+  mpsincos(x, (GEN*)(v+2), (GEN*)(v+1));
+  if (!signe(gel(v,2))) return gerepilecopy(av, gel(v,1));
+  return v;
+}
+
+/* return exp(ix)-1, x a t_REAL */
+static GEN
+expm1_Ir(GEN x)
+{
+  pari_sp av = avma;
+  GEN v = cgetg(3,t_COMPLEX);
+  mpsincosm1(x, (GEN*)(v+2), (GEN*)(v+1));
+  if (!signe(gel(v,2))) return gerepilecopy(av, gel(v,1));
+  return v;
+}
+
+/* return exp(z)-1, z complex */
+GEN
+cxexpm1(GEN z, long prec)
+{
+  pari_sp av = avma;
+  GEN X, Y, x = real_i(z), y = imag_i(z);
+  if (typ(x) != t_REAL) x = gtofp(x, prec);
+  if (typ(y) != t_REAL) y = gtofp(y, prec);
+  if (gequal0(y)) return mpexpm1(x);
+  if (gequal0(x)) return expm1_Ir(y);
+  X = mpexpm1(x); /* t_REAL */
+  Y = expm1_Ir(y);
+  /* exp(x+iy) - 1 = (exp(x)-1)(exp(iy)-1) + exp(x)-1 + exp(iy)-1 */
+  return gerepileupto(av, gadd(gadd(X,Y), gmul(X,Y)));
+}
+
+void
+gsincos(GEN x, GEN *s, GEN *c, long prec)
+{
+  long i, j, ex, ex2, lx, ly, mi;
+  pari_sp av, tetpil;
+  GEN y, r, u, v, u1, v1, p1, p2, p3, p4, ps, pc;
+  GEN *gptr[4];
+
+  switch(typ(x))
+  {
+    case t_INT: case t_FRAC:
+      *s = cgetr(prec);
+      *c = cgetr(prec); av = avma;
+      mpsincos(tofp_safe(x, prec), &ps, &pc);
+      affrr_fixlg(ps,*s);
+      affrr_fixlg(pc,*c); avma = av; return;
+
+    case t_REAL:
+      mpsincos(x,s,c); return;
+
+    case t_COMPLEX:
+      i = precision(x); if (!i) i = prec;
+      ps = cgetc(i); *s = ps;
+      pc = cgetc(i); *c = pc; av = avma;
+      r = gexp(gel(x,2),prec);
+      v1 = gmul2n(addrr(invr(r),r), -1); /* = cos(I*Im(x)) */
+      u1 = subrr(r, v1); /* = I*sin(I*Im(x)) */
+      gsincos(gel(x,1), &u,&v, prec);
+      affrr_fixlg(mulrr(v1,u), gel(ps,1));
+      affrr_fixlg(mulrr(u1,v), gel(ps,2));
+      affrr_fixlg(mulrr(v1,v), gel(pc,1));
+      affrr_fixlg(mulrr(u1,u), gel(pc,2)); togglesign(gel(pc,2));
+      avma = av; return;
+
+    case t_QUAD:
+      av = avma; gsincos(quadtofp(x, prec), s, c, prec);
+      gerepileall(av, 2, s, c); return;
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) { *s = gerepilecopy(av,y); *c = gaddsg(1,*s); return; }
+
+      ex = valp(y); lx = lg(y); ex2 = 2*ex+2;
+      if (ex < 0) pari_err_DOMAIN("gsincos","valuation", "<", gen_0, x);
+      if (ex2 > lx)
+      {
+        *s = x == y? gcopy(y): gerepilecopy(av, y); av = avma;
+        *c = gerepileupto(av, gsubsg(1, gdivgs(gsqr(y),2)));
+        return;
+      }
+      if (!ex)
+      {
+        gsincos(serchop0(y),&u,&v,prec);
+        gsincos(gel(y,2),&u1,&v1,prec);
+        p1 = gmul(v1,v);
+        p2 = gmul(u1,u);
+        p3 = gmul(v1,u);
+        p4 = gmul(u1,v); tetpil = avma;
+        *c = gsub(p1,p2);
+        *s = gadd(p3,p4);
+        gptr[0]=s; gptr[1]=c;
+        gerepilemanysp(av,tetpil,gptr,2);
+        return;
+      }
+
+      ly = lx+2*ex;
+      mi = lx-1; while (mi>=3 && isrationalzero(gel(y,mi))) mi--;
+      mi += ex-2;
+      pc = cgetg(ly,t_SER); *c = pc;
+      ps = cgetg(lx,t_SER); *s = ps;
+      pc[1] = evalsigne(1) | _evalvalp(0) | evalvarn(varn(y));
+      gel(pc,2) = gen_1; ps[1] = y[1];
+      for (i=2; i<ex+2; i++) gel(ps,i) = gcopy(gel(y,i));
+      for (i=3; i< ex2; i++) gel(pc,i) = gen_0;
+      for (i=ex2; i<ly; i++)
+      {
+        long ii = i-ex;
+        av = avma; p1 = gen_0;
+        for (j=ex; j<=minss(ii-2,mi); j++)
+          p1 = gadd(p1, gmulgs(gmul(gel(y,j-ex+2),gel(ps,ii-j)),j));
+        gel(pc,i) = gerepileupto(av, gdivgs(p1,2-i));
+        if (ii < lx)
+        {
+          av = avma; p1 = gen_0;
+          for (j=ex; j<=minss(i-ex2,mi); j++)
+            p1 = gadd(p1,gmulgs(gmul(gel(y,j-ex+2),gel(pc,i-j)),j));
+          p1 = gdivgs(p1,i-2);
+          gel(ps,ii) = gerepileupto(av, gadd(p1,gel(y,ii)));
+        }
+      }
+      return;
+  }
+  pari_err_TYPE("gsincos",x);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                     TANGENT and COTANGENT                      **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+mptan(GEN x)
+{
+  pari_sp av = avma;
+  GEN s, c;
+
+  mpsincos(x,&s,&c);
+  if (!signe(c))
+    pari_err_DOMAIN("tan", "argument", "=", strtoGENstr("Pi/2 + kPi"),x);
+  return gerepileuptoleaf(av, divrr(s,c));
+}
+
+GEN
+gtan(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, s, c;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mptan(x);
+
+    case t_COMPLEX: {
+      if (isintzero(gel(x,1))) retmkcomplex(gen_0,gtanh(gel(x,2),prec));
+      av = avma; y = mulcxmI(gtanh(mulcxI(x), prec)); /* tan x = -I th(I x) */
+      gel(y,1) = gcopy(gel(y,1));
+      return gerepileupto(av, y);
+    }
+    case t_INT: case t_FRAC:
+      y = cgetr(prec); av = avma;
+      affrr_fixlg(mptan(tofp_safe(x,prec)), y); avma = av; return y;
+
+    case t_PADIC:
+      av = avma;
+      return gerepileupto(av, gdiv(gsin(x,prec), gcos(x,prec)));
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) return gerepilecopy(av, y);
+      if (valp(y) < 0)
+        pari_err_DOMAIN("tan","valuation", "<", gen_0, x);
+      gsincos(y,&s,&c,prec);
+      return gerepileupto(av, gdiv(s,c));
+  }
+  return trans_eval("tan",gtan,x,prec);
+}
+
+static GEN
+mpcotan(GEN x)
+{
+  pari_sp av=avma, tetpil;
+  GEN s,c;
+
+  mpsincos(x,&s,&c); tetpil=avma;
+  return gerepile(av,tetpil,divrr(c,s));
+}
+
+GEN
+gcotan(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, s, c;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      return mpcotan(x);
+
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) {
+        GEN z = cgetg(3, t_COMPLEX);
+        gel(z,1) = gen_0;
+        av = avma;
+        gel(z,2) = gerepileupto(av, gneg(ginv(gtanh(gel(x,2),prec))));
+        return z;
+      }
+      av = avma;
+      gsincos(x,&s,&c,prec);
+      return gerepileupto(av, gdiv(c,s));
+
+    case t_INT: case t_FRAC:
+      y = cgetr(prec); av = avma;
+      affrr_fixlg(mpcotan(tofp_safe(x,prec)), y); avma = av; return y;
+
+    case t_PADIC:
+      av = avma;
+      return gerepileupto(av, gdiv(gcos(x,prec), gsin(x,prec)));
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) pari_err_DOMAIN("cotan", "argument", "=", gen_0, y);
+      if (valp(y) < 0) pari_err_DOMAIN("cotan","valuation", "<", gen_0, x);
+      gsincos(y,&s,&c,prec);
+      return gerepileupto(av, gdiv(c,s));
+  }
+  return trans_eval("cotan",gcotan,x,prec);
+}
diff --git a/src/basemath/trans2.c b/src/basemath/trans2.c
new file mode 100644
index 0000000..64b1a46
--- /dev/null
+++ b/src/basemath/trans2.c
@@ -0,0 +1,1742 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                   TRANSCENDENTAL FUNCTIONS                     **/
+/**                          (part 2)                              **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+GEN
+trans_fix_arg(long *prec, GEN *s0, GEN *sig, GEN *tau, pari_sp *av, GEN *res)
+{
+  GEN s, p1;
+  long l;
+  if (typ(*s0)==t_COMPLEX && gequal0(gel(*s0,2))) *s0 = gel(*s0,1);
+  s = *s0;
+  l = precision(s); if (!l) l = *prec;
+  if (l < LOWDEFAULTPREC) l = LOWDEFAULTPREC;
+  *res = cgetc(l); *av = avma;
+  if (typ(s) == t_COMPLEX)
+  { /* s = sig + i t */
+    s = cxtofp(s, l+EXTRAPRECWORD);
+    *sig = gel(s,1);
+    *tau = gel(s,2);
+  }
+  else /* real number */
+  {
+    *sig = s = gtofp(s, l+EXTRAPRECWORD);
+    *tau = gen_0;
+    p1 = trunc2nr(s, 0);
+    if (!signe(subri(s,p1))) *s0 = p1;
+  }
+  *prec = l; return s;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                          ARCTANGENT                            **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+mpatan(GEN x)
+{
+  long l, l1, l2, n, m, i, lp, e, s, sx = signe(x);
+  pari_sp av0, av;
+  double alpha, beta, delta;
+  GEN y, p1, p2, p3, p4, p5, unr;
+  int inv;
+
+  if (!sx) return real_0_bit(expo(x));
+  l = lp = realprec(x);
+  if (absrnz_equal1(x)) { /* |x| = 1 */
+    y = Pi2n(-2, l+EXTRAPRECWORD); if (sx < 0) setsigne(y,-1);
+    return y;
+  }
+  if (l > AGM_ATAN_LIMIT)
+  {
+    av = avma; y = logagmcx(mkcomplex(gen_1, x), l);
+    return gerepileuptoleaf(av, gel(y,2));
+  }
+
+  e = expo(x); inv = (e >= 0); /* = (|x| > 1 ) */
+  if (e > 0) lp += nbits2extraprec(e);
+
+  y = cgetr(lp); av0 = avma;
+  p1 = rtor(x, l+EXTRAPRECWORD); setabssign(p1); /* p1 = |x| */
+  if (inv) p1 = invr(p1);
+  e = expo(p1);
+  if (e < -100)
+    alpha = 1.65149612947 - e; /* log_2(Pi) - e */
+  else
+    alpha = log2(PI / atan(rtodbl(p1)));
+  beta = (double)(prec2nbits(l)>>1);
+  delta = 1 + beta - alpha/2;
+  if (delta <= 0) { n = 1; m = 0; }
+  else
+  {
+    double fi = alpha-2;
+    if (delta >= fi*fi)
+    {
+      double t = 1 + sqrt(delta);
+      n = (long)t;
+      m = (long)(t - fi);
+    }
+    else
+    {
+      n = (long)(1+beta/fi);
+      m = 0;
+    }
+  }
+  l2 = l + nbits2extraprec(m);
+  p2 = rtor(p1, l2); av = avma;
+  for (i=1; i<=m; i++)
+  {
+    p5 = addsr(1, sqrr(p2)); setprec(p5,l2);
+    p5 = addsr(1, sqrtr_abs(p5)); setprec(p5,l2);
+    affrr(divrr(p2,p5), p2); avma = av;
+  }
+  p3 = sqrr(p2); l1 = minss(LOWDEFAULTPREC+EXTRAPRECWORD, l2); /* l1 increases to l2 */;
+  unr = real_1(l2); setprec(unr,l1);
+  p4 = cgetr(l2); setprec(p4,l1);
+  affrr(divru(unr,2*n+1), p4);
+  s = 0; e = expo(p3); av = avma;
+  for (i = n; i > 1; i--) /* n >= 1. i = 1 done outside for efficiency */
+  {
+    setprec(p3,l1); p5 = mulrr(p4,p3);
+    l1 += dvmdsBIL(s - e, &s); if (l1 > l2) l1 = l2;
+    setprec(unr,l1); p5 = subrr(divru(unr,2*i-1), p5);
+    setprec(p4,l1); affrr(p5,p4); avma = av;
+  }
+  setprec(p3, l2); p5 = mulrr(p4,p3); /* i = 1 */
+  setprec(unr,l2); p4 = subrr(unr, p5);
+
+  p4 = mulrr(p2,p4); shiftr_inplace(p4, m);
+  if (inv) p4 = subrr(Pi2n(-1, lp), p4);
+  if (sx < 0) togglesign(p4);
+  affrr_fixlg(p4,y); avma = av0; return y;
+}
+
+GEN
+gatan(GEN x, long prec)
+{
+  pari_sp av;
+  GEN a, y;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mpatan(x);
+    case t_COMPLEX: /* atan(x) = -i atanh(ix) */
+      if (ismpzero(gel(x,2))) return gatan(gel(x,1), prec);
+      av = avma; return gerepilecopy(av, mulcxmI(gatanh(mulcxI(x),prec)));
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (valp(y) < 0) pari_err_DOMAIN("atan","valuation", "<", gen_0, x);
+      if (lg(y)==2) return gerepilecopy(av, y);
+      /* lg(y) > 2 */
+      a = integser(gdiv(derivser(y), gaddsg(1,gsqr(y))));
+      if (!valp(y)) a = gadd(a, gatan(gel(y,2),prec));
+      return gerepileupto(av, a);
+  }
+  return trans_eval("atan",gatan,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                             ARCSINE                            **/
+/**                                                                **/
+/********************************************************************/
+/* |x| < 1, x != 0 */
+static GEN
+mpasin(GEN x) {
+  pari_sp av = avma;
+  GEN z, a = sqrtr(subsr(1, sqrr(x)));
+  if (realprec(x) > AGM_ATAN_LIMIT)
+  {
+    z = logagmcx(mkcomplex(a,x), realprec(x));
+    z = gel(z,2);
+  }
+  else
+    z = mpatan(divrr(x, a));
+  return gerepileuptoleaf(av, z);
+}
+
+static GEN mpacosh(GEN x);
+GEN
+gasin(GEN x, long prec)
+{
+  long sx;
+  pari_sp av;
+  GEN a, y, p1;
+
+  switch(typ(x))
+  {
+    case t_REAL: sx = signe(x);
+      if (!sx) return real_0_bit(expo(x));
+      if (absrnz_equal1(x)) { /* |x| = 1 */
+        if (sx > 0) return Pi2n(-1, realprec(x)); /* 1 */
+        y = Pi2n(-1, realprec(x)); setsigne(y, -1); return y; /* -1 */
+      }
+      if (expo(x) < 0) return mpasin(x);
+      y = cgetg(3,t_COMPLEX);
+      gel(y,1) = Pi2n(-1, realprec(x));
+      gel(y,2) = mpacosh(x);
+      if (sx < 0) togglesign(gel(y,1)); else togglesign(gel(y,2));
+      return y;
+
+    case t_COMPLEX: /* asin(z) = -i asinh(iz) */
+      if (ismpzero(gel(x,2))) return gasin(gel(x,1), prec);
+      av = avma;
+      return gerepilecopy(av, mulcxmI(gasinh(mulcxI(x), prec)));
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) return gerepilecopy(av, y);
+      /* lg(y) > 2*/
+      if (valp(y) < 0) pari_err_DOMAIN("asin","valuation", "<", gen_0, x);
+      p1 = gsubsg(1,gsqr(y));
+      if (gequal0(p1))
+      {
+        GEN t = Pi2n(-1,prec);
+        if (gsigne(gel(y,2)) < 0) setsigne(t, -1);
+        return gerepileupto(av, scalarser(t, varn(y), valp(p1)>>1));
+      }
+      p1 = gdiv(derivser(y), gsqrt(p1,prec));
+      a = integser(p1);
+      if (!valp(y)) a = gadd(a, gasin(gel(y,2),prec));
+      return gerepileupto(av, a);
+  }
+  return trans_eval("asin",gasin,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                             ARCCOSINE                          **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+acos0(long e) { return Pi2n(-1, nbits2prec(e<0? -e: 1)); }
+
+/* |x| < 1, x != 0 */
+static GEN
+mpacos(GEN x)
+{
+  pari_sp av = avma;
+  GEN z, a = sqrtr(subsr(1, sqrr(x)));
+  if (realprec(x) > AGM_ATAN_LIMIT)
+  {
+    z = logagmcx(mkcomplex(x,a), realprec(x));
+    z = gel(z,2);
+  }
+  else {
+    z = mpatan(divrr(a, x));
+    if (signe(x) < 0) z = addrr(mppi(realprec(z)), z);
+  }
+  return gerepileuptoleaf(av, z);
+}
+
+GEN
+gacos(GEN x, long prec)
+{
+  long sx;
+  pari_sp av;
+  GEN a, y, p1;
+
+  switch(typ(x))
+  {
+    case t_REAL: sx = signe(x);
+      if (!sx) return acos0(expo(x));
+      if (absrnz_equal1(x)) /* |x| = 1 */
+        return sx > 0? real_0_bit( -(bit_prec(x)>>1) ) : mppi(realprec(x));
+      if (expo(x) < 0) return mpacos(x);
+
+      y = cgetg(3,t_COMPLEX); p1 = mpacosh(x);
+      if (sx < 0) { gel(y,1) = mppi(realprec(x)); togglesign(p1); }
+      else gel(y,1) = gen_0;
+      gel(y,2) = p1; return y;
+
+    case t_COMPLEX:
+      if (ismpzero(gel(x,2))) return gacos(gel(x,1), prec);
+      av = avma;
+      p1 = gadd(x, mulcxI(gsqrt(gsubsg(1,gsqr(x)), prec)));
+      y = glog(p1,prec); /* log(x + I*sqrt(1-x^2)) */
+      return gerepilecopy(av, mulcxmI(y));
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (valp(y) < 0) pari_err_DOMAIN("acos","valuation", "<", gen_0, x);
+      if (lg(y) > 2)
+      {
+        p1 = gsubsg(1,gsqr(y));
+        if (gequal0(p1)) return zeroser(varn(y), valp(p1)>>1);
+        p1 = integser(gdiv(gneg(derivser(y)), gsqrt(p1,prec)));
+        /*y(t) = 1+O(t)*/
+        if (gequal1(gel(y,2)) && !valp(y)) return gerepileupto(av, p1);
+      }
+      else p1 = y;
+      a = (lg(y)==2 || valp(y))? Pi2n(-1, prec): gacos(gel(y,2),prec);
+      return gerepileupto(av, gadd(a,p1));
+  }
+  return trans_eval("acos",gacos,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                            ARGUMENT                            **/
+/**                                                                **/
+/********************************************************************/
+
+/* we know that x and y are not both 0 */
+static GEN
+mparg(GEN x, GEN y)
+{
+  long prec, sx = signe(x), sy = signe(y);
+  GEN z;
+
+  if (!sy)
+  {
+    if (sx > 0) return real_0_bit(expo(y) - expo(x));
+    return mppi(realprec(x));
+  }
+  prec = realprec(y); if (prec < realprec(x)) prec = realprec(x);
+  if (!sx)
+  {
+    z = Pi2n(-1, prec); if (sy < 0) setsigne(z,-1);
+    return z;
+  }
+
+  if (expo(x)-expo(y) > -2)
+  {
+    z = mpatan(divrr(y,x)); if (sx > 0) return z;
+    return addrr_sign(z, signe(z), mppi(prec), sy);
+  }
+  z = mpatan(divrr(x,y));
+  return addrr_sign(z, -signe(z), Pi2n(-1, prec), sy);
+}
+
+static GEN
+rfix(GEN x,long prec)
+{
+  switch(typ(x))
+  {
+    case t_INT: return itor(x, prec);
+    case t_FRAC: return fractor(x, prec);
+    case t_REAL: break;
+    default: pari_err_TYPE("rfix (conversion to t_REAL)",x);
+  }
+  return x;
+}
+
+static GEN
+cxarg(GEN x, GEN y, long prec)
+{
+  pari_sp av = avma;
+  x = rfix(x,prec);
+  y = rfix(y,prec); return gerepileuptoleaf(av, mparg(x,y));
+}
+
+GEN
+garg(GEN x, long prec)
+{
+  if (gequal0(x)) pari_err_DOMAIN("arg", "argument", "=", gen_0, x);
+  switch(typ(x))
+  {
+    case t_REAL: prec = realprec(x); /* fall through */
+    case t_INT: case t_FRAC: return (gsigne(x)>0)? real_0(prec): mppi(prec);
+    case t_COMPLEX: return cxarg(gel(x,1),gel(x,2),prec);
+  }
+  return trans_eval("arg",garg,x,prec);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                      HYPERBOLIC COSINE                         **/
+/**                                                                **/
+/********************************************************************/
+
+static GEN
+mpcosh(GEN x)
+{
+  pari_sp av;
+  GEN z;
+
+  if (!signe(x)) { /* 1 + x */
+    long e = expo(x);
+    return e >= 0? real_0_bit(e): real_1(nbits2prec(-e));
+  }
+  av = avma;
+  z = mpexp(x); z = addrr(z, invr(z)); shiftr_inplace(z, -1);
+  return gerepileuptoleaf(av, z);
+}
+
+GEN
+gcosh(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, p1;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mpcosh(x);
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) return gcos(gel(x,2),prec);
+      /* fall through */
+    case t_PADIC:
+      av = avma; p1 = gexp(x,prec); p1 = gadd(p1, ginv(p1));
+      return gerepileupto(av, gmul2n(p1,-1));
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y) && valp(y) == 0) return gerepilecopy(av, y);
+      p1 = gexp(y,prec); p1 = gadd(p1, ginv(p1));
+      return gerepileupto(av, gmul2n(p1,-1));
+  }
+  return trans_eval("cosh",gcosh,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                       HYPERBOLIC SINE                          **/
+/**                                                                **/
+/********************************************************************/
+
+static GEN
+mpsinh(GEN x)
+{
+  pari_sp av;
+  long ex = expo(x), lx;
+  GEN z, res;
+
+  if (!signe(x)) return real_0_bit(ex);
+  lx = realprec(x); res = cgetr(lx); av = avma;
+  if (ex < 1 - BITS_IN_LONG) x = rtor(x, lx + nbits2extraprec(-ex)-1);
+  z = mpexp(x); z = subrr(z, invr(z)); shiftr_inplace(z, -1);
+  affrr(z, res); avma = av; return res;
+}
+
+GEN
+gsinh(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, p1;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mpsinh(x);
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) retmkcomplex(gen_0, gsin(gel(x,2),prec));
+      /* fall through */
+    case t_PADIC:
+      av = avma; p1 = gexp(x,prec); p1 = gsub(p1, ginv(p1));
+      return gerepileupto(av, gmul2n(p1,-1));
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y) && valp(y) == 0) return gerepilecopy(av, y);
+      p1 = gexp(y, prec); p1 = gsub(p1, ginv(p1));
+      return gerepileupto(av, gmul2n(p1,-1));
+  }
+  return trans_eval("sinh",gsinh,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                      HYPERBOLIC TANGENT                        **/
+/**                                                                **/
+/********************************************************************/
+
+static GEN
+mptanh(GEN x)
+{
+  long lx, s = signe(x);
+  GEN y;
+
+  if (!s) return real_0_bit(expo(x));
+  lx = realprec(x);
+  if (absr_cmp(x, stor(prec2nbits(lx), LOWDEFAULTPREC)) >= 0) {
+    y = real_1(lx);
+  } else {
+    pari_sp av = avma;
+    long ex = expo(x);
+    GEN t;
+    if (ex < 1 - BITS_IN_LONG) x = rtor(x, lx + nbits2extraprec(-ex)-1);
+    t = exp1r_abs(gmul2n(x,1)); /* exp(|2x|) - 1 */
+    y = gerepileuptoleaf(av, divrr(t, addsr(2,t)));
+  }
+  if (s < 0) togglesign(y); /* tanh is odd */
+  return y;
+}
+
+GEN
+gtanh(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, t;
+
+  switch(typ(x))
+  {
+    case t_REAL: return mptanh(x);
+    case t_COMPLEX:
+      if (isintzero(gel(x,1))) retmkcomplex(gen_0, gtan(gel(x,2),prec));
+      /* fall through */
+    case t_PADIC:
+      av = avma;
+      t = gexp(gmul2n(x,1),prec);
+      t = gdivsg(-2, gaddgs(t,1));
+      return gerepileupto(av, gaddsg(1,t));
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) return gerepilecopy(av, y);
+      t = gexp(gmul2n(y, 1),prec);
+      t = gdivsg(-2, gaddgs(t,1));
+      return gerepileupto(av, gaddsg(1,t));
+  }
+  return trans_eval("tanh",gtanh,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                     AREA HYPERBOLIC SINE                       **/
+/**                                                                **/
+/********************************************************************/
+
+/* x != 0 */
+static GEN
+mpasinh(GEN x)
+{
+  GEN z, res;
+  pari_sp av;
+  long lx = realprec(x), ex = expo(x);
+
+  res = cgetr(lx); av = avma;
+  if (ex < 1 - BITS_IN_LONG) x = rtor(x, lx + nbits2extraprec(-ex)-1);
+  z = logr_abs( addrr_sign(x,1, sqrtr_abs( addrs(sqrr(x), 1) ), 1) );
+  if (signe(x) < 0) togglesign(z);
+  affrr(z, res); avma = av; return res;
+}
+
+GEN
+gasinh(GEN x, long prec)
+{
+  pari_sp av;
+  GEN a, y, p1;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      if (!signe(x)) return rcopy(x);
+      return mpasinh(x);
+
+    case t_COMPLEX:
+      if (ismpzero(gel(x,2))) return gasinh(gel(x,1), prec);
+      av = avma;
+      if (ismpzero(gel(x,1))) /* avoid cancellation */
+        return gerepilecopy(av, mulcxI(gasin(gel(x,2), prec)));
+      p1 = gadd(x, gsqrt(gaddsg(1,gsqr(x)), prec));
+      y = glog(p1,prec); /* log (x + sqrt(1+x^2)) */
+      return gerepileupto(av, y);
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (gequal0(y)) return gerepilecopy(av, y);
+      if (valp(y) < 0) pari_err_DOMAIN("asinh","valuation", "<", gen_0, x);
+      p1 = gaddsg(1,gsqr(y));
+      if (gequal0(p1))
+      {
+        GEN t = PiI2n(-1,prec);
+        if ( gsigne(imag_i(gel(y,2))) < 0 ) setsigne(gel(t,2), -1);
+        return gerepileupto(av, scalarser(t, varn(y), valp(p1)>>1));
+      }
+      p1 = gdiv(derivser(y), gsqrt(p1,prec));
+      a = integser(p1);
+      if (!valp(y)) a = gadd(a, gasinh(gel(y,2),prec));
+      return gerepileupto(av, a);
+  }
+  return trans_eval("asinh",gasinh,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                     AREA HYPERBOLIC COSINE                     **/
+/**                                                                **/
+/********************************************************************/
+
+/* |x| >= 1, return ach(|x|) */
+static GEN
+mpacosh(GEN x)
+{
+  pari_sp av = avma;
+  GEN z;
+  if (absrnz_equal1(x)) return real_0_bit(- bit_prec(x) >> 1);
+  z = logr_abs( addrr_sign(x, 1, sqrtr( subrs(sqrr(x), 1) ), 1) );
+  return gerepileuptoleaf(av, z);
+}
+
+GEN
+gacosh(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, p1;
+
+  switch(typ(x))
+  {
+    case t_REAL: {
+      long s = signe(x), e = expo(x);
+      GEN a, b;
+      if (s > 0 && e >= 0) return mpacosh(x);
+      /* x < 1 */
+      y = cgetg(3,t_COMPLEX); a = gen_0;
+      if (s == 0) b = acos0(e);
+      else if (e < 0) b = mpacos(x); /* -1 < x < 1 */
+      else {
+        if (!absrnz_equal1(x)) a = mpacosh(x);
+        b = mppi(realprec(x));
+      }
+      gel(y,1) = a;
+      gel(y,2) = b; return y;
+    }
+    case t_COMPLEX:
+      if (ismpzero(gel(x,2))) return gacosh(gel(x,1), prec);
+      av = avma;
+      p1 = gadd(x, gsqrt(gaddsg(-1,gsqr(x)), prec));
+      y = glog(p1,prec); /* log(x + sqrt(x^2-1)) */
+      if (signe(real_i(y)) < 0) y = gneg(y);
+      return gerepileupto(av, y);
+    default: {
+      GEN a;
+      long v;
+      av = avma; if (!(y = toser_i(x))) break;
+      v = valp(y);
+      if (v < 0) pari_err_DOMAIN("acosh","valuation", "<", gen_0, x);
+      if (gequal0(y))
+      {
+        if (!v) return gerepilecopy(av, y);
+        return gerepileupto(av, gadd(y, PiI2n(-1, prec)));
+      }
+      p1 = gsubgs(gsqr(y),1);
+      if (gequal0(p1)) { avma = av; return zeroser(varn(y), valp(p1)>>1); }
+      p1 = gdiv(derivser(y), gsqrt(p1,prec));
+      a = integser(p1);
+      if (v)
+        p1 = PiI2n(-1, prec); /* I Pi/2 */
+      else
+      {
+        p1 = gel(y,2); if (gequal1(p1)) return gerepileupto(av,a);
+        p1 = gacosh(p1, prec);
+      }
+      return gerepileupto(av, gadd(p1,a));
+    }
+  }
+  return trans_eval("acosh",gacosh,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                     AREA HYPERBOLIC TANGENT                    **/
+/**                                                                **/
+/********************************************************************/
+
+/* |x| < 1, x != 0 */
+static GEN
+mpatanh(GEN x)
+{
+  pari_sp av = avma;
+  long ex = expo(x);
+  GEN z;
+  if (ex < 1 - BITS_IN_LONG) x = rtor(x, realprec(x) + nbits2extraprec(-ex)-1);
+  z = invr( subsr(1,x) ); shiftr_inplace(z, 1); /* 2/(1-x)*/
+  z = logr_abs( addrs(z,-1) );
+  shiftr_inplace(z, -1); return gerepileuptoleaf(av, z);
+}
+
+GEN
+gatanh(GEN x, long prec)
+{
+  long sx;
+  pari_sp av;
+  GEN a, y, z;
+
+  switch(typ(x))
+  {
+    case t_REAL:
+      sx = signe(x);
+      if (!sx) return real_0_bit(expo(x));
+      if (expo(x) < 0) return mpatanh(x);
+
+      y = cgetg(3,t_COMPLEX);
+      av = avma;
+      z = subrs(x,1);
+      if (!signe(z)) pari_err_DOMAIN("atanh", "argument", "=", gen_1, x);
+      z = invr(z); shiftr_inplace(z, 1); /* 2/(x-1)*/
+      z = addrs(z,1);
+      if (!signe(z)) pari_err_DOMAIN("atanh", "argument", "=", gen_m1, x);
+      z = logr_abs(z);
+      shiftr_inplace(z, -1); /* (1/2)log((1+x)/(x-1)) */
+      gel(y,1) = gerepileuptoleaf(av, z);
+      gel(y,2) = Pi2n(-1, realprec(x));
+      if (sx > 0) togglesign(gel(y,2));
+      return y;
+
+    case t_COMPLEX: /* 2/(1-z) - 1 = (1+z) / (1-z) */
+      if (ismpzero(gel(x,2))) return gatanh(gel(x,1), prec);
+      av = avma; z = glog( gaddgs(gdivsg(2,gsubsg(1,x)),-1), prec );
+      return gerepileupto(av, gmul2n(z,-1));
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (valp(y) < 0) pari_err_DOMAIN("atanh","valuation", "<", gen_0, x);
+      z = gdiv(derivser(y), gsubsg(1,gsqr(y)));
+      a = integser(z);
+      if (!valp(y)) a = gadd(a, gatanh(gel(y,2),prec));
+      return gerepileupto(av, a);
+  }
+  return trans_eval("atanh",gatanh,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**               CACHE BERNOULLI NUMBERS B_2k                     **/
+/**                                                                **/
+/********************************************************************/
+static GEN
+bern(GEN B, long pr)
+{
+  if (typ(B) != t_REAL) return fractor(B, pr);
+  if (realprec(B) < pr) return rtor(B,pr);
+  return B;
+}
+static const long BERN_MINNB = 5;
+/* need B[2..2*nb] at least prec accuracy. If prec = 0, compute exactly */
+void
+mpbern(long nb, long prec)
+{
+  const pari_sp av = avma;
+  long n, pr, n_is_small = 1, lbern = 0;
+  GEN B;
+  pari_timer T;
+
+  /* pr = accuracy for computation, prec = required accuracy for result */
+  if (prec)
+  {
+    pr = prec;
+    incrprec(pr);
+  }
+  else
+    pr = prec = LONG_MAX; /* oo */
+  if (nb < BERN_MINNB) nb = BERN_MINNB;
+  if (bernzone)
+  { /* don't recompute known Bernoulli */
+    long i, min, max;
+    lbern = lg(bernzone);
+    if (lbern-1 < nb)
+    { min = lbern-1; max = nb; }
+    else
+    { min = nb; max = lbern-1; }
+    /* skip B_2, ..., B_{2*MINNB}, always included as t_FRAC */
+    for (n = BERN_MINNB+1; n <= min; n++)
+    {
+      GEN c = gel(bernzone,n);
+      /* also stop if prec = 0 (compute exactly) */
+      if (typ(c) == t_REAL && realprec(c) < prec) break;
+    }
+    /* B[1..n-1] are OK */
+    if (n > nb) return;
+    B = cgetg_block(max+1, t_VEC);
+    for (i = 1; i < n; i++) gel(B,i) = gel(bernzone,i);
+    /* keep B[nb+1..max] */
+    for (i = nb+1; i <= max; i++) gel(B,i) = gel(bernzone,i);
+  }
+  else
+  {
+    B = cgetg_block(nb+1, t_VEC);
+    gel(B,1) = gclone(mkfrac(gen_1, utoipos(6)));
+    gel(B,2) = gclone(mkfrac(gen_m1, utoipos(30)));
+    gel(B,3) = gclone(mkfrac(gen_1, utoipos(42)));
+    gel(B,4) = gel(B,2);
+    gel(B,5) = gclone(mkfrac(utoipos(5), utoipos(66)));
+    n = BERN_MINNB+1;
+  }
+  avma = av;
+  if (DEBUGLEVEL) {
+    err_printf("caching Bernoulli numbers 2 to 2*%ld, prec = %ld\n",
+               nb, prec == LONG_MAX? 0: prec);
+    timer_start(&T);
+  }
+
+  /* B_{2n} = (2n-1) / (4n+2) -
+   * sum_{a = 1}^{n-1} (2n)...(2n+2-2a) / (2...(2a-1)2a) B_{2a} */
+  n_is_small = 1;
+  for (; n <= nb; n++, avma = av)
+  { /* compute and store B[n] = B_{2n} */
+    GEN S;
+    if (n < lbern)
+    {
+      GEN b = gel(bernzone,n);
+      if (typ(b)!=t_REAL || realprec(b)>=prec) { gel(B,n) = b; continue; }
+    }
+    /* Not cached, must compute */
+    /* huge accuracy ? May as well compute exactly */
+    if (n_is_small && (prec == LONG_MAX ||
+                       2*n * log(2*n) < prec2nbits_mul(prec, LOG2)))
+      S = bernfrac_using_zeta(2*n);
+    else
+    {
+#ifdef LONG_IS_64BIT
+      const ulong mul_overflow = 3037000500;
+#else
+      const ulong mul_overflow = 46341;
+#endif
+      ulong u = 8, v = 5, a = n-1, b = 2*n-3;
+      n_is_small = 0;
+      S = bern(gel(B,a), pr); /* B_2a */
+      for (;;)
+      { /* b = 2a-1, u = 2v-2, 2a + v = 2n+3 */
+        if (a == 1) { S = mulri(S, muluu(u,v)); break; } /* a=b=1, v=2n+1, u=4n */
+        /* beware overflow */
+        S = (v <= mul_overflow)? mulru(S, u*v): mulri(S, muluu(u,v));
+        S = (a <= mul_overflow)? divru(S, a*b): divri(S, muluu(a,b));
+        u += 4; v += 2; a--; b -= 2;
+        S = addrr(bern(gel(B,a), pr), S);
+        if ((a & 127) == 0) S = gerepileuptoleaf(av, S);
+      }
+      S = divru(subsr(2*n, S), 2*n+1);
+      shiftr_inplace(S, -2*n);
+      if (realprec(S) != prec) S = rtor(S, prec);
+    }
+    gel(B,n) = gclone(S); /* S = B_2n */
+  }
+  if (DEBUGLEVEL) timer_printf(&T, "Bernoulli");
+  swap(B, bernzone);
+  if (B)
+  { /* kill old non-reused values */
+    for (n = lbern-1; n; n--)
+    {
+      if (gel(B,n) != gel(bernzone,n)) gunclone(gel(B,n));
+    }
+    killblock(B);
+  }
+  avma = av;
+}
+
+GEN
+bernfrac(long n)
+{
+  long k;
+  if (n < 0) pari_err_DOMAIN("bernfrac", "index", "<", gen_0, stoi(n));
+  if (n == 0) return gen_1;
+  if (n == 1) return mkfrac(gen_m1,gen_2);
+  if (odd(n)) return gen_0;
+  k = n >> 1;
+  if (!bernzone && k <= BERN_MINNB) mpbern(BERN_MINNB, 0);
+  if (bernzone && k < lg(bernzone))
+  {
+    GEN B = gel(bernzone, k), C;
+    if (typ(B) != t_REAL) return B;
+    C = bernfrac_using_zeta(n);
+    gel(bernzone, k) = gclone(C);
+    gunclone(B); return C;
+  }
+  return bernfrac_using_zeta(n);
+}
+
+/* mpbern as exact fractions */
+static GEN
+bernvec_old(long nb)
+{
+  long n, i;
+  GEN y;
+
+  if (nb < 0) return cgetg(1, t_VEC);
+  if (nb > 46340 && BITS_IN_LONG == 32) pari_err_IMPL( "bernvec for n > 46340");
+
+  y = cgetg(nb+2, t_VEC); gel(y,1) = gen_1;
+  for (n = 1; n <= nb; n++)
+  { /* compute y[n+1] = B_{2n} */
+    pari_sp av = avma;
+    GEN b = gmul2n(utoineg(2*n - 1), -1); /* 1 + (2n+1)B_1 = -(2n-1) /2 */
+    GEN c = gen_1;
+    ulong u1 = 2*n + 1, u2 = n, d1 = 1, d2 = 1;
+
+    for (i = 1; i < n; i++)
+    {
+      c = diviiexact(muliu(c, u1*u2), utoipos(d1*d2));/*= binomial(2n+1, 2*i) */
+      b = gadd(b, gmul(c, gel(y,i+1)));
+      u1 -= 2; u2--; d1++; d2 += 2;
+    }
+    gel(y,n+1) = gerepileupto(av, gdivgs(b, -(1+2*n)));
+  }
+  return y;
+}
+GEN
+bernvec(long nb)
+{
+  long i, l = nb+2;
+  GEN y = cgetg(l, t_VEC);
+  if (nb < 20) return bernvec_old(nb);
+  for (i = 1; i < l; i++) gel(y,i) = bernfrac((i-1) << 1);
+  return y;
+}
+
+/* x := pol_x(v); B_k(x) = \sum_{i=0}^k binomial(k, i) B_i x^{k-i} */
+static GEN
+bernpol_i(long k, long v)
+{
+  GEN B, C;
+  long i;
+  if (v < 0) v = 0;
+  if (k < 0) pari_err_DOMAIN("bernpol", "index", "<", gen_0, stoi(k));
+  mpbern(k >> 1, 0); /* cache B_2, ..., B_2[k/2] */
+  C = vecbinome(k);
+  B = cgetg(k + 3, t_POL);
+  for (i = 0; i <= k; ++i) gel(B, k-i+2) = gmul(gel(C,i+1), bernfrac(i));
+  B[1] = evalsigne(1) | evalvarn(v);
+  return B;
+}
+GEN
+bernpol(long k, long v) {
+  pari_sp av = avma;
+  return gerepileupto(av, bernpol_i(k, v));
+}
+/* x := pol_x(v); return 1^e + ... + x^e = x^e + (B_{e+1}(x) - B_{e+1})/(e+1) */
+static GEN
+faulhaber(long e, long v)
+{
+  GEN B;
+  if (e == 0) return pol_x(v);
+  B = RgX_integ(bernpol_i(e, v)); /* (B_{e+1}(x) - B_{e+1}) / (e+1) */
+  gel(B,e+2) = gaddgs(gel(B,e+2), 1); /* add x^e, in place */
+  return B;
+}
+/* sum_v T(v), T a polynomial expression in v */
+GEN
+sumformal(GEN T, long v)
+{
+  pari_sp av = avma, av2, lim;
+  long i, t, d;
+  GEN R;
+
+  T = simplify_shallow(T);
+  t = typ(T);
+  if (is_scalar_t(t))
+    return gerepileupto(av, monomialcopy(T, 1, v < 0? 0: v));
+  if (t != t_POL) pari_err_TYPE("sumformal [not a t_POL]", T);
+  if (v < 0) v = varn(T);
+  av2 = avma; lim = stack_lim(av2,3);
+  R = gen_0;
+  d = poldegree(T,v);
+  for (i = d; i >= 0; i--)
+  {
+    GEN c = polcoeff0(T, i, v);
+    if (gequal0(c)) continue;
+    R = gadd(R, gmul(c, faulhaber(i, v)));
+    if (low_stack(lim,stack_lim(av2,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"sumformal, i = %ld/%ld", i,d);
+      R = gerepileupto(av2, R);
+    }
+  }
+  return gerepileupto(av, R);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         EULER'S GAMMA                          **/
+/**                                                                **/
+/********************************************************************/
+
+/* x / (i*(i+1)) */
+GEN
+divrunu(GEN x, ulong i)
+{
+  if (i <= LOWMASK) /* i(i+1) < 2^BITS_IN_LONG*/
+    return divru(x, i*(i+1));
+  else
+    return divru(divru(x, i), i+1);
+}
+/* x / (i*(i+1)) */
+GEN
+divgunu(GEN x, ulong i)
+{
+#ifdef LONG_IS_64BIT
+  if (i < 3037000500L) /* i(i+1) < 2^63 */
+#else
+  if (i < 46341L) /* i(i+1) < 2^31 */
+#endif
+    return gdivgs(x, i*(i+1));
+  else
+    return gdivgs(gdivgs(x, i), i+1);
+}
+
+/* arg(s+it) */
+double
+darg(double s, double t)
+{
+  double x;
+  if (!t) return (s>0)? 0.: PI;
+  if (!s) return (t>0)? PI/2: -PI/2;
+  x = atan(t/s);
+  return (s>0)? x
+              : ((t>0)? x+PI : x-PI);
+}
+
+void
+dcxlog(double s, double t, double *a, double *b)
+{
+  *a = log(s*s + t*t) / 2; /* log |s| = Re(log(s)) */
+  *b = darg(s,t);          /* Im(log(s)) */
+}
+
+double
+dabs(double s, double t) { return sqrt( s*s + t*t ); }
+double
+dnorm(double s, double t) { return s*s + t*t; }
+
+#if 0
+/* x, z t_REAL. Compute unique x in ]-z,z] congruent to x mod 2z */
+static GEN
+red_mod_2z(GEN x, GEN z)
+{
+  GEN Z = gmul2n(z, 1), d = subrr(z, x);
+  /* require little accuracy */
+  if (!signe(d)) return x;
+  setprec(d, nbits2prec(expo(d) - expo(Z)));
+  return addrr(mulir(floorr(divrr(d, Z)), Z), x);
+}
+#endif
+
+static GEN
+cxgamma(GEN s0, int dolog, long prec)
+{
+  GEN s, u, a, y, res, tes, sig, tau, invn2, p1, nnx, pi, pi2, sqrtpi2;
+  long i, lim, nn, esig, et;
+  pari_sp av, av2, avlim;
+  int funeq = 0;
+  pari_timer T;
+
+  if (DEBUGLEVEL>5) timer_start(&T);
+  s = trans_fix_arg(&prec,&s0,&sig,&tau,&av,&res);
+
+  esig = expo(sig);
+  et = signe(tau)? expo(tau): 0;
+  if ((signe(sig) <= 0 || esig < -1) && et <= 16)
+  { /* s <--> 1-s */
+    funeq = 1; s = gsubsg(1, s); sig = real_i(s);
+  }
+
+  /* find "optimal" parameters [lim, nn] */
+  if (esig > 300 || et > 300)
+  { /* |s| is HUGE ! Play safe and avoid inf / NaN */
+    GEN S, iS, l2, la, u;
+    double logla, l;
+
+    S = gprec_w(s,LOWDEFAULTPREC);
+    /* l2 ~ |lngamma(s))|^2 */
+    l2 = gnorm(gmul(S, glog(S, LOWDEFAULTPREC)));
+    l = (prec2nbits_mul(prec, LOG2) - rtodbl(glog(l2,LOWDEFAULTPREC))/2) / 2.;
+    if (l < 0) l = 0.;
+
+    iS = imag_i(S);
+    if (et > 0 && l > 0)
+    {
+      GEN t = gmul(iS, dbltor(PI / l)), logt = glog(t,LOWDEFAULTPREC);
+      la = gmul(t, logt);
+      if      (gcmpgs(la, 3) < 0)   { logla = log(3.); la = stoi(3); }
+      else if (gcmpgs(la, 150) > 0) { logla = rtodbl(logt); la = t; }
+      else logla = rtodbl(mplog(la));
+    }
+    else
+    {
+      logla = log(3.); la = stoi(3);
+    }
+    lim = (long)ceil(l / (1.+ logla));
+    if (lim == 0) lim = 1;
+
+    u = gmul(la, dbltor((lim-0.5)/PI));
+    l2 = gsub(gsqr(u), gsqr(iS));
+    if (signe(l2) > 0)
+    {
+      l2 = gsub(gsqrt(l2,3), sig);
+      if (signe(l2) > 0) nn = itos( gceil(l2) ); else nn = 1;
+    }
+    else
+      nn = 1;
+  }
+  else
+  { /* |s| is moderate. Use floats  */
+    double ssig = rtodbl(sig);
+    double st = typ(s) == t_REAL? 0.0: rtodbl(imag_i(s));
+    double la, l,l2,u,v, rlogs, ilogs;
+
+    if (dolog)
+    {
+      /* loggamma(1+u) ~ - Euler * u: cancellation if u is small */
+      if (fabs(ssig-1) + fabs(st) < 0.0001)
+      { /* s ~ 1, take care */
+        long e = gexpo(gsubgs(s,1));
+        prec += nbits2nlong(-e);
+        s = gprec_w(s, prec);
+      }
+    }
+    dcxlog(ssig,st, &rlogs,&ilogs);
+    /* Re (s - 1/2) log(s) */
+    u = (ssig - 0.5)*rlogs - st * ilogs;
+    /* Im (s - 1/2) log(s) */
+    v = (ssig - 0.5)*ilogs + st * rlogs;
+    /* l2 = | (s - 1/2) log(s) - s + log(2Pi)/2 |^2 ~ |lngamma(s))|^2 */
+    u = u - ssig + log(2.*PI)/2;
+    v = v - st;
+    l2 = u*u + v*v;
+    if (l2 < 0.000001) l2 = 0.000001;
+    l = (prec2nbits_mul(prec, LOG2) - log(l2)/2) / 2.;
+    if (l < 0) l = 0.;
+
+    la = 3.; /* FIXME: heuristic... */
+    if (st > 1 && l > 0)
+    {
+      double t = st * PI / l;
+      la = t * log(t);
+      if (la < 3) la = 3.;
+      if (la > 150) la = t;
+    }
+    lim = (long)ceil(l / (1.+ log(la)));
+    if (lim == 0) lim = 1;
+
+    u = (lim-0.5) * la / PI;
+    l2 = u*u - st*st;
+    if (l2 > 0)
+    {
+      nn = (long)ceil(sqrt(l2) - ssig);
+      if (nn < 1) nn = 1;
+    }
+    else
+      nn = 1;
+    if (DEBUGLEVEL>5) err_printf("lim, nn: [%ld, %ld], la = %lf\n",lim,nn,la);
+  }
+  incrprec(prec);
+
+  av2 = avma; avlim = stack_lim(av2,3);
+  y = s;
+  if (typ(s0) == t_INT)
+  {
+    if (signe(s0) <= 0)
+      pari_err_DOMAIN("gamma","argument", "=",
+                       strtoGENstr("non-positive integer"), s0);
+    if (is_bigint(s0)) {
+      for (i=1; i < nn; i++)
+      {
+        y = mulri(y, addis(s0, i));
+        if (low_stack(avlim,stack_lim(av2,3)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"gamma");
+          y = gerepileuptoleaf(av2, y);
+        }
+      }
+    } else {
+      ulong ss = itou(s0);
+      for (i=1; i < nn; i++)
+      {
+        y = mulru(y, ss + i);
+        if (low_stack(avlim,stack_lim(av2,3)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"gamma");
+          y = gerepileuptoleaf(av2, y);
+        }
+      }
+    }
+    if (dolog) y = logr_abs(y);
+  }
+  else if (!dolog || typ(s) == t_REAL)
+  { /* Compute lngamma mod 2 I Pi */
+    for (i=1; i < nn; i++)
+    {
+      y = gmul(y, gaddgs(s,i));
+      if (low_stack(avlim,stack_lim(av2,3)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"gamma");
+        y = gerepileupto(av2, y);
+      }
+    }
+    if (dolog) y = logr_abs(y);
+  }
+  else
+  { /* dolog && complex s: be careful with imaginary part */
+    y = glog(y, prec);
+    for (i=1; i < nn; i++)
+    {
+      y = gadd(y, glog(gaddgs(s,i), prec));
+      if (low_stack(avlim,stack_lim(av2,3)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"gamma");
+        y = gerepileupto(av2, y);
+      }
+    }
+  }
+  if (DEBUGLEVEL>5) timer_printf(&T,"product from 0 to N-1");
+
+  nnx = gaddgs(s, nn);
+  a = ginv(nnx); invn2 = gsqr(a);
+  av2 = avma; avlim = stack_lim(av2,3);
+  mpbern(lim,prec);
+  tes = divrunu(bernreal(2*lim,prec), 2*lim-1); /* B2l / (2l-1) 2l*/
+  if (DEBUGLEVEL>5) timer_printf(&T,"Bernoullis");
+  for (i = 2*lim-2; i > 1; i -= 2)
+  {
+    u = divrunu(bernreal(i,prec), i-1); /* Bi / i(i-1) */
+    tes = gadd(u, gmul(invn2,tes));
+    if (low_stack(avlim,stack_lim(av2,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"gamma");
+      tes = gerepileupto(av2, tes);
+    }
+  }
+  if (DEBUGLEVEL>5) timer_printf(&T,"Bernoulli sum");
+
+  p1 = gsub(gmul(gsub(nnx, ghalf), glog(nnx,prec)), nnx);
+  p1 = gadd(p1, gmul(tes, a));
+
+  pi = mppi(prec); pi2 = shiftr(pi, 1); sqrtpi2 = sqrtr(pi2);
+
+  if (dolog)
+  {
+    if (funeq)
+    { /* (recall that s = 1 - s0) */
+
+      /* We compute log(sin(Pi s0)) so that it has branch cuts along
+      * (-oo, 0] and [1, oo).  To do this in a numerically stable way
+      * we must compute the log first then mangle its imaginary part.
+      * The rounding operation below is stable because we're rounding
+      * a number which is already within 1/4 of an integer. */
+
+      /* z = log( sin(Pi s0) / (sqrt(2Pi)/2) ) */
+      GEN z = glog(gdiv(gsin(gmul(pi,s0),prec), shiftr(sqrtpi2,-1)), prec);
+      /* b = (2 Re(s) - 1) / 4 */
+      GEN b = shiftr(subrs(shiftr(sig, 1), 1), -2);
+      y = gsub(y, z);
+      if (gsigne(imag_i(s)) > 0) togglesign(b);
+      /* z = 2Pi round( Im(z)/2Pi - b ) */
+      z = gmul(roundr(gsub(gdiv(imag_i(z), pi2), b)), pi2);
+      if (signe(z)) { /* y += I*z */
+        if (typ(y) == t_COMPLEX)
+          gel(y,2) = gadd(gel(y,2), z);
+        else
+          y = gadd(y, mkcomplex(gen_0, z));
+      }
+      p1 = gneg(p1);
+    }
+    else /* y --> sqrt(2Pi) / y */
+      y = gsub(logr_abs(sqrtpi2), y);
+    y = gadd(p1, y);
+  }
+  else
+  {
+    if (funeq)
+    { /* y --> y Pi/(sin(Pi s) * sqrt(2Pi)) = y sqrt(Pi/2)/sin(Pi s) */
+      y = gdiv(gmul(shiftr(sqrtpi2,-1),y), gsin(gmul(pi,s0), prec));
+      /* don't use s above: sin(pi s0) = sin(pi s) and the former is
+       * more accurate, esp. if s0 ~ 0 */
+      p1 = gneg(p1);
+    }
+    else /* y --> sqrt(2Pi) / y */
+      y = gdiv(sqrtpi2, y);
+    y = gmul(gexp(p1, prec), y);
+  }
+  avma = av; return affc_fixlg(y, res);
+}
+
+/* Gamma((m+1) / 2) */
+static GEN
+gammahs(long m, long prec)
+{
+  GEN y = cgetr(prec), z;
+  pari_sp av = avma;
+  long ma = labs(m);
+
+  if (ma > 200 + 50*(prec-2)) /* heuristic */
+  {
+    z = stor(m + 1, prec); shiftr_inplace(z, -1);
+    affrr(cxgamma(z,0,prec), y);
+    avma = av; return y;
+  }
+  z = sqrtr( mppi(prec) );
+  if (m)
+  {
+    GEN p1 = mulu_interval(ma/2 + 1, ma);
+    long v = vali(p1);
+    p1 = shifti(p1, -v); v -= ma;
+    if (m >= 0) z = mulri(z,p1);
+    else
+    {
+      z = divri(z,p1); v = -v;
+      if ((m&3) == 2) setsigne(z,-1);
+    }
+    shiftr_inplace(z, v);
+  }
+  affrr(z, y); avma = av; return y;
+}
+GEN
+ggammah(GEN x, long prec)
+{
+  switch(typ(x))
+  {
+    case t_INT:
+    {
+      long k = itos(x);
+      if (labs(k) > 962353) pari_err_OVERFLOW("gammah");
+      return gammahs(k<<1, prec);
+    }
+    case t_REAL: case t_COMPLEX: case t_PADIC: case t_SER: {
+      pari_sp av = avma;
+      return gerepileupto(av, ggamma(gadd(x,ghalf), prec));
+    }
+  }
+  return trans_eval("gammah",ggammah,x,prec);
+}
+
+/* find n such that n+v_p(n!)>=k p^2/(p-1)^2 */
+static long
+nboft(long k, long p)
+{
+  pari_sp av = avma;
+  long s, n;
+
+  if (k <= 0) return 0;
+  k = itou( gceil(gdiv(mului(k, sqru(p)), sqru(p-1))) );
+  avma = av;
+  for (s=0, n=0; n+s < k; n++, s += u_lval(n, p));
+  return n;
+}
+
+/* Using Dwork's expansion, compute \Gamma(px+1)=-\Gamma(px) with x a unit.
+ * See p-Adic Gamma Functions and Dwork Cohomology, Maurizio Boyarsky
+ * Transactions of the AMS, Vol. 257, No. 2. (Feb., 1980), pp. 359-369.
+ * Inspired by a GP script by Fernando Rodriguez-Villegas */
+static GEN
+gadw(GEN x, long p)
+{
+  pari_sp ltop = avma;
+  GEN s, t, u = cgetg(p+1, t_VEC);
+  long j, k, kp, n = nboft(precp(x)+valp(x)+1, p);
+
+  t = s = gaddsg(1, zeropadic(gel(x,2), n));
+  gel(u, 1) = s;
+  gel(u, 2) = s;
+  for (j = 2; j < p; ++j)
+    gel(u, j+1) = gdivgs(gel(u, j), j);
+  for (k = 1, kp = p; k < n; ++k, kp += p) /* kp = k*p */
+  {
+    GEN c;
+    gel(u, 1) = gdivgs(gadd(gel(u, 1), gel(u, p)), kp);
+    for (j = 1; j < p; ++j)
+      gel(u, j+1) = gdivgs(gadd(gel(u, j), gel(u, j+1)), kp + j);
+
+    t = gmul(t, gaddgs(x, k-1));
+    c = leafcopy(gel(u,1)); setvalp(c, valp(c) + k); /* c = u[1] * p^k */
+    s = gadd(s, gmul(c, t));
+    if ((k&0xFL)==0) gerepileall(ltop, 3, &u,&s,&t);
+  }
+  return gneg(s);
+}
+
+/*Use Dwork expansion*/
+/*This is a O(p*e*log(pe)) algorithm, should be used when p small
+ * If p==2 this is a O(pe) algorithm. */
+static GEN
+Qp_gamma_Dwork(GEN x, long p)
+{
+  pari_sp ltop = avma;
+  long k = padic_to_Fl(x, p);
+  GEN p1;
+  long j;
+  if (k)
+  {
+    GEN x_k = gsubgs(x,k);
+    x = gdivgs(x_k, p);
+    p1 = gadw(x, p); if (!odd(k)) p1 = gneg(p1);
+    for (j = 1; j < k; ++j) p1 = gmul(p1, gaddgs(x_k, j));
+  }
+  else
+    p1 = gneg(gadw(gdivgs(x, p), p));
+  return gerepileupto(ltop, p1);
+}
+
+/* Compute Qp_gamma using the definition. This is a O(x*M(log(pe))) algorithm.
+ * This should be used if x is very small. */
+static GEN
+Qp_gamma_Morita(long n, GEN p, long e)
+{
+  pari_sp ltop=avma;
+  GEN p2 = gaddsg((n&1)?-1:1, zeropadic(p, e));
+  long i;
+  long pp=is_bigint(p)? 0: itos(p);
+  for (i = 2; i < n; i++)
+    if (!pp || i%pp)
+    {
+      p2 = gmulgs(p2, i);
+      if ((i&0xFL) == 0xFL)
+        p2 = gerepileupto(ltop, p2);
+    }
+  return gerepileupto(ltop, p2);
+}
+
+/* x\in\N: Gamma(-x)=(-1)^(1+x+x\p)*Gamma(1+x) */
+static GEN
+Qp_gamma_neg_Morita(long n, GEN p, long e)
+{
+  GEN g = ginv(Qp_gamma_Morita(n+1, p, e));
+  return ((n^sdivsi(n,p)) & 1)? g: gneg(g);
+}
+
+/* p-adic Gamma function for x a p-adic integer */
+/* If n < p*e : use Morita's definition.
+ * Else : use Dwork's expansion.
+ * If both n and p are big : itos(p) will fail.
+ * TODO: handle p=2 better (Qp_gamma_Dwork is slow for p=2). */
+GEN
+Qp_gamma(GEN x)
+{
+  GEN n, m, N, p = gel(x,2);
+  long s, e = precp(x);
+  if (valp(x) < 0) pari_err_DOMAIN("gamma","v_p(x)", "<", gen_0, x);
+  n = gtrunc(x);
+  m = gtrunc(gneg(x));
+  N = cmpii(n,m)<=0?n:m;
+  s = itos_or_0(N);
+  if (s && cmpsi(s, muliu(p,e)) < 0) /* s < p*e */
+    return (N == n) ? Qp_gamma_Morita(s,p,e): Qp_gamma_neg_Morita(s,p,e);
+  return Qp_gamma_Dwork(x, itos(p));
+}
+
+GEN
+ggamma(GEN x, long prec)
+{
+  pari_sp av;
+  long m;
+  GEN y, z;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      if (signe(x) <= 0)
+        pari_err_DOMAIN("gamma","argument", "=",
+                         strtoGENstr("non-positive integer"), x);
+      if (cmpiu(x,481177) > 0) pari_err_OVERFLOW("gamma");
+      return mpfactr(itos(x) - 1, prec);
+
+    case t_REAL: case t_COMPLEX:
+      return cxgamma(x, 0, prec);
+
+    case t_FRAC:
+      if (!equaliu(gel(x,2),2)) break;
+      z = gel(x,1); /* true argument is z/2 */
+      if (is_bigint(z) || labs(m = itos(z)) > 962354)
+      {
+        pari_err_OVERFLOW("gamma");
+        return NULL; /* not reached */
+      }
+      return gammahs(m-1, prec);
+
+    case t_PADIC: return Qp_gamma(x);
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      /* exp(lngamma) */
+      if (valp(y)>0 || lg(y) == 2)
+        z = gdiv(gexp(glngamma(gaddgs(y,1),prec),prec),y);
+      else
+      { /* use fun eq. to avoid log singularity of lngamma at negative ints */
+        GEN Y = y, y0 = gel(y,2), t = ground(y0), pi = NULL;
+        if (gequal(y0, t) && typ(t) == t_INT && signe(t) < 0)
+        {
+          pi = mppi(prec);
+          Y = gsubsg(1, y);
+        }
+        z = gexp(glngamma(Y,prec),prec);
+        if (pi)
+          z = gdiv(mpodd(t)? negr(pi): pi,
+                   gmul(z, gsin(gmul(pi,serchop0(y)), prec)));
+      }
+      return gerepileupto(av, z);
+  }
+  return trans_eval("gamma",ggamma,x,prec);
+}
+
+GEN
+mpfactr(long n, long prec)
+{
+  GEN f = cgetr(prec);
+  pari_sp av = avma;
+
+  if (n+1 > 350 + 70*(prec-2)) /* heuristic */
+    affrr(cxgamma(stor(n+1, prec), 0, prec), f);
+  else
+    affir(mpfact(n), f);
+  avma = av; return f;
+}
+
+GEN
+glngamma(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y, p1;
+
+  switch(typ(x))
+  {
+    case t_INT:
+      if (signe(x) <= 0)
+        pari_err_DOMAIN("lngamma","argument", "=",
+                         strtoGENstr("non-positive integer"), x);
+      if (cmpiu(x,200 + 50*(prec-2)) > 0) /* heuristic */
+        return cxgamma(x, 1, prec);
+      av = avma;
+      return gerepileuptoleaf(av, logr_abs( itor(mpfact(itos(x) - 1), prec) ));
+    case t_FRAC:
+    {
+      GEN a, b;
+      long e1, e2;
+      av = avma;
+      a = gel(x,1);
+      b = gel(x,2);
+      e1 = expi(subii(a,b)); e2 = expi(b);
+      if (e2 > e1) prec += nbits2nlong(e2 - e1);
+      x = fractor(x, prec);
+      return gerepileupto(av, cxgamma(x, 1, prec));
+    }
+
+    case t_REAL: case t_COMPLEX:
+      return cxgamma(x, 1, prec);
+
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (valp(y)) pari_err_DOMAIN("lngamma","valuation", "!=", gen_0, x);
+      /* (lngamma y)' = y' psi(y) */
+      p1 = integser(gmul(derivser(y), gpsi(y, prec)));
+      if (!gequal1(gel(y,2))) p1 = gadd(p1, glngamma(gel(y,2),prec));
+      return gerepileupto(av, p1);
+
+    case t_PADIC: av = avma; return gerepileupto(av, Qp_log(Qp_gamma(x)));
+  }
+  return trans_eval("lngamma",glngamma,x,prec);
+}
+/********************************************************************/
+/**                                                                **/
+/**                  PSI(x) = GAMMA'(x)/GAMMA(x)                   **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+cxpsi(GEN s0, long prec)
+{
+  pari_sp av, av2;
+  GEN sum,z,a,res,tes,in2,sig,tau,s,unr;
+  long lim,nn,k;
+  const long la = 3;
+  int funeq = 0;
+  pari_timer T;
+
+  if (DEBUGLEVEL>2) timer_start(&T);
+  s = trans_fix_arg(&prec,&s0,&sig,&tau,&av,&res);
+  if (signe(sig) <= 0) { funeq = 1; s = gsub(gen_1, s); sig = real_i(s); }
+  if (typ(s0) == t_INT && signe(s0) <= 0)
+    pari_err_DOMAIN("psi","argument", "=",
+                    strtoGENstr("non-positive integer"), s0);
+
+  if (expo(sig) > 300 || (typ(s) == t_COMPLEX && gexpo(gel(s,2)) > 300))
+  { /* |s| is HUGE. Play safe */
+    GEN L, S = gprec_w(s,LOWDEFAULTPREC), rS = real_i(S), iS = imag_i(S);
+    double l;
+
+    l = rtodbl( gnorm(glog(S, 3)) );
+    l = log(l) / 2.;
+    lim = 2 + (long)ceil((prec2nbits_mul(prec, LOG2) - l) / (2*(1+log((double)la))));
+    if (lim < 2) lim = 2;
+
+    l = (2*lim-1)*la / (2.*PI);
+    L = gsub(dbltor(l*l), gsqr(iS));
+    if (signe(L) < 0) L = gen_0;
+
+    L = gsub(gsqrt(L, 3), rS);
+    if (signe(L) > 0) nn = (long)ceil(rtodbl(L)); else nn = 1;
+    if (DEBUGLEVEL>2) err_printf("lim, nn: [%ld, %ld]\n",lim,nn);
+  }
+  else
+  {
+    double ssig = rtodbl(sig);
+    double st = typ(s) == t_REAL? 0.0: rtodbl(imag_i(s));
+    double l;
+    {
+      double rlog, ilog; /* log (s - Euler) */
+      dcxlog(ssig - 0.57721566, st, &rlog,&ilog);
+      l = dnorm(rlog,ilog);
+    }
+    if (l < 0.000001) l = 0.000001;
+    l = log(l) / 2.;
+    lim = 2 + (long)ceil((prec2nbits_mul(prec, LOG2) - l) / (2*(1+log((double)la))));
+    if (lim < 2) lim = 2;
+
+    l = (2*lim-1)*la / (2.*PI);
+    l = l*l - st*st;
+    if (l < 0.) l = 0.;
+    nn = (long)ceil( sqrt(l) - ssig );
+    if (nn < 1) nn = 1;
+    if (DEBUGLEVEL>2) err_printf("lim, nn: [%ld, %ld]\n",lim,nn);
+  }
+  incrprec(prec); unr = real_1(prec); /* one extra word of precision */
+
+  a = gdiv(unr, gaddgs(s, nn)); /* 1 / (s+n) */
+  av2 = avma; sum = gmul2n(a,-1);
+  for (k = 0; k < nn; k++)
+  {
+    sum = gadd(sum, gdiv(unr, gaddgs(s, k)));
+    if ((k & 127) == 0) sum = gerepileupto(av2, sum);
+  }
+  z = gsub(glog(gaddgs(s, nn), prec), sum);
+  if (DEBUGLEVEL>2) timer_printf(&T,"sum from 0 to N-1");
+
+  in2 = gsqr(a);
+  mpbern(lim,prec);
+  av2 = avma; tes = divru(bernreal(2*lim, prec), 2*lim);
+  for (k=2*lim-2; k>=2; k-=2)
+  {
+    tes = gadd(gmul(in2,tes), divru(bernreal(k, prec), k));
+    if ((k & 255) == 0) tes = gerepileupto(av2, tes);
+  }
+  if (DEBUGLEVEL>2) timer_printf(&T,"Bernoulli sum");
+  z = gsub(z, gmul(in2,tes));
+  if (funeq)
+  {
+    GEN pi = mppi(prec);
+    z = gadd(z, gmul(pi, gcotan(gmul(pi,s), prec)));
+  }
+  avma = av; return affc_fixlg(z, res);
+}
+
+/* psi(1+x) + O(x^n), x = pol_x(v) */
+static GEN
+serpsi1(long n, long v, long prec)
+{
+  long i, l = n+3;
+  GEN g, s = cgetg(l, t_SER);
+  s[1] = evalsigne(1)|evalvalp(0)|evalvarn(v);
+  g = mpeuler(prec); setsigne(g, -1);
+  gel(s,2) = g;
+  for (i = 2; i < l-1; i++)
+  {
+    GEN c = szeta(i, prec);
+    if (odd(i)) setsigne(c, -1);
+    gel(s,i+1) = c;
+  }
+  return s;
+}
+/* T an RgX, return T(X + z0) + O(X^L) */
+static GEN
+tr(GEN T, GEN z0, long L)
+{
+  GEN s = RgX_to_ser(RgX_translate(T, z0), L+3);
+  setvarn(s, 0); return s;
+}
+/* psi(z0+x) + O(x^n) */
+static GEN
+serpsiz0(GEN z0, long L, long v, long prec)
+{
+  pari_sp av, lim;
+  GEN A,A1,A2, B,B1,B2, Q;
+  long n;
+
+  if (equali1(z0)) return serpsi1(L, v, prec);
+  /* otherwise use Luke's rational approximations for psi(x) */
+  n = gprecision(z0); if (n) prec = n;
+  z0 = gtofp(z0, prec + EXTRAPRECWORD);
+  /* Start from n = 3; in Luke's notation, A2 := A_{n-2}, A1 := A_{n-1},
+   * A := A_n. Same for B */
+  av = avma; lim = stack_lim(av,1);
+  A2= gdivgs(mkpoln(2, gen_1, utoipos(6)), 2);
+  B2 = scalarpol_shallow(utoipos(4), 0);
+  A1= gdivgs(mkpoln(3, gen_1, utoipos(82), utoipos(96)), 6);
+  B1 = mkpoln(2, utoipos(8), utoipos(28));
+  A = gdivgs(mkpoln(4, gen_1, utoipos(387), utoipos(2906), utoipos(1920)), 12);
+  B = mkpoln(3, utoipos(14), utoipos(204), utoipos(310));
+  A2= tr(A2,z0, L);
+  B2= tr(B2,z0, L);
+  A1= tr(A1,z0, L);
+  B1= tr(B1,z0, L);
+  A = tr(A, z0, L);
+  B = tr(B, z0, L); Q = gdiv(A, B);
+  /* work with z0+x as a variable */
+  for (n = 4;; n++)
+  {
+    GEN Q0 = Q, a, b, r, c3,c2,c1,c0 = muluu(2*n-3, n+1);
+    GEN u = subiu(muluu(n, 7*n-9), 6);
+    GEN t = addiu(muluu(n, 7*n-19), 4);
+    /* c1=(2*n-1)*(3*(n-1)*z+7*n^2-9*n-6);
+     * c2=(2*n-3)*(z-n-1)*(-3*(n-1)*z+7*n^2-19*n+4);
+     * c3=(2*n-1)*(n-3)*(z-n)*(z-(n+1))*(z+(n-4)); */
+    c1 = deg1pol_shallow(muluu(3*(n-1),2*n-1), muliu(u,2*n-1), 0);
+    c2 = ZX_mul(deg1pol_shallow(utoipos(2*n-3), negi(muluu(2*n-3,n+1)), 0),
+                deg1pol_shallow(utoineg(3*(n-1)), t, 0));
+    r = mkvec3(utoipos(n), utoipos(n+1), stoi(4-n));
+    c3 = ZX_Z_mul(roots_to_pol(r,0), muluu(2*n-1,n-3));
+    c1 = tr(c1, z0, L+3);
+    c2 = tr(c2, z0, L+3);
+    c3 = tr(c3, z0, L+3);
+
+    /* A_{n+1}, B_{n+1} */
+    a = gdiv(gadd(gadd(gmul(c1,A),gmul(c2,A1)),gmul(c3,A2)), c0);
+    b = gdiv(gadd(gadd(gmul(c1,B),gmul(c2,B1)),gmul(c3,B2)), c0);
+    Q = gdiv(a,b);
+    if (gexpo(gsub(Q,Q0)) < -bit_accuracy(prec)) break;
+    A2 = A1; A1 = A; A = a;
+    B2 = B1; B1 = B; B = b;
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"serpsiz0, n = %ld", n);
+      gerepileall(av, 7, &A,&A1,&A2, &B,&B1,&B2, &Q);
+    }
+  }
+  Q = gmul(Q, gmul2n(gsubsg(1, ginv(tr(pol_x(v),z0, L))), 1));
+  setvarn(Q, v);
+  return gadd(negr(mpeuler(prec)), Q);
+}
+static GEN
+serpsi(GEN y, long prec)
+{
+  GEN Q, z0, Y;
+  long L = lg(y)-2, v  = varn(y), vy = valp(y);
+  int reflect;
+
+  if (!L) pari_err_DOMAIN("psi", "argument", "=", gen_0,y);
+  if (vy < 0) pari_err_DOMAIN("psi", "series valuation", "<", gen_0,y);
+  if (vy)
+    z0 = gen_0;
+  else
+  {
+    GEN t;
+    z0 = gel(y,2); t = ground(z0);
+    if (gequal(t,z0)) z0 = t;
+  }
+  reflect = (gcmp(real_i(z0),ghalf) < 0); /* use reflection formula */
+  if (reflect) { z0 = gsubsg(1,z0); Y = gsubsg(1,y); } else Y = y;
+  Q = serpsiz0(z0, L, v, prec);
+  Q = gsubst(Q, v, serchop0(Y)); /* psi(Y) */
+  if (reflect)
+  { /* psi(y) = psi(Y) + Pi cotan(Pi Y) = psi(Y) - Pi cotan(Pi y) */
+    GEN pi = mppi(prec);
+    if (equali1(z0))
+      Q = gsub(Q, gmul(pi, gcotan(gmul(pi,y), prec)));
+    else
+      Q = gadd(Q, gmul(pi, gcotan(gmul(pi,Y), prec)));
+  }
+  return Q;
+}
+
+GEN
+gpsi(GEN x, long prec)
+{
+  pari_sp av;
+  GEN y;
+  switch(typ(x))
+  {
+    case t_REAL: case t_COMPLEX: return cxpsi(x,prec);
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      return gerepileupto(av, serpsi(y,prec));
+  }
+  return trans_eval("psi",gpsi,x,prec);
+}
diff --git a/src/basemath/trans3.c b/src/basemath/trans3.c
new file mode 100644
index 0000000..3f23115
--- /dev/null
+++ b/src/basemath/trans3.c
@@ -0,0 +1,3312 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**                   TRANSCENDENTAL FONCTIONS                     **/
+/**                          (part 3)                              **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+#define HALF_E 1.3591409 /* Exponential / 2 */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       BESSEL FUNCTIONS                            **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* n! sum_{k=0}^m Z^k / (k!*(k+n)!), with Z := (-1)^flag*z^2/4 */
+static GEN
+_jbessel(GEN n, GEN z, long flag, long m)
+{
+  pari_sp av, lim;
+  GEN Z,s;
+  long k;
+
+  Z = gmul2n(gsqr(z),-2); if (flag & 1) Z = gneg(Z);
+  if (typ(z) == t_SER)
+  {
+    long v = valp(z);
+    if (v < 0) pari_err_DOMAIN("besselj","valuation", "<", gen_0, z);
+    k = lg(Z)-2 - v;
+    if (v == 0) pari_err_IMPL("besselj around a!=0");
+    if (k <= 0) return scalarser(gen_1, varn(z), 2*v);
+    setlg(Z, k+2);
+  }
+  s = gen_1;
+  av = avma; lim = stack_lim(av,1);
+  for (k=m; k>=1; k--)
+  {
+    s = gaddsg(1, gdiv(gmul(Z,s), gmulgs(gaddgs(n,k),k)));
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"besselj");
+      s = gerepileupto(av, s);
+    }
+  }
+  return s;
+}
+
+/* return L * approximate solution to x log x = B */
+static long
+bessel_get_lim(double B, double L)
+{
+  long lim;
+  double x = 1 + B; /* 3 iterations are enough except in pathological cases */
+  x = (x + B)/(log(x)+1);
+  x = (x + B)/(log(x)+1);
+  x = (x + B)/(log(x)+1); x = L*x;
+  lim = (long)x; if (lim < 2) lim = 2;
+  return lim;
+}
+
+static GEN jbesselintern(GEN n, GEN z, long flag, long prec);
+static GEN kbesselintern(GEN n, GEN z, long flag, long prec);
+static GEN
+jbesselvec(GEN n, GEN x, long fl, long prec)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(y,i) = jbesselintern(n,gel(x,i),fl,prec);
+  return y;
+}
+static GEN
+jbesselhvec(GEN n, GEN x, long prec)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(y,i) = jbesselh(n,gel(x,i),prec);
+  return y;
+}
+static GEN
+polylogvec(long m, GEN x, long prec)
+{
+  long l, i;
+  GEN y = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(y,i) = gpolylog(m,gel(x,i),prec);
+  return y;
+}
+static GEN
+kbesselvec(GEN n, GEN x, long fl, long prec)
+{
+  long i, l;
+  GEN y = cgetg_copy(x, &l);
+  for (i=1; i<l; i++) gel(y,i) = kbesselintern(n,gel(x,i),fl,prec);
+  return y;
+}
+
+static GEN
+jbesselintern(GEN n, GEN z, long flag, long prec)
+{
+  long i, ki;
+  pari_sp av = avma;
+  GEN y;
+
+  switch(typ(z))
+  {
+    case t_INT: case t_FRAC: case t_QUAD:
+    case t_REAL: case t_COMPLEX:
+    {
+      int flz0 = gequal0(z);
+      long lim, k, precnew;
+      GEN p1, p2;
+      double B, L;
+
+      i = precision(z); if (i) prec = i;
+      if (flz0 && gequal0(n)) return real_1(prec);
+      p2 = gdiv(gpow(gmul2n(z,-1),n,prec), ggamma(gaddgs(n,1),prec));
+      if (flz0) return gerepileupto(av, p2);
+      L = HALF_E * gtodouble(gabs(gtofp(z,LOWDEFAULTPREC),prec));
+      precnew = prec;
+      if (L >= 1.0)
+        precnew += nbits2extraprec((long)(L/(HALF_E*LOG2) + BITS_IN_LONG));
+      if (issmall(n,&ki)) {
+        k = labs(ki);
+        n = utoi(k);
+      } else {
+        i = precision(n);
+        if (i && i < precnew) n = gtofp(n,precnew);
+      }
+      z = gtofp(z,precnew);
+      B = prec2nbits_mul(prec, LOG2/2) / L;
+      lim = bessel_get_lim(B, L);
+      p1 = gprec_wtrunc(_jbessel(n,z,flag,lim), prec);
+      return gerepileupto(av, gmul(p2,p1));
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      return jbesselvec(n, z, flag, prec);
+
+    case t_POLMOD:
+      y = jbesselvec(n, polmod_to_embed(z, prec), flag, prec);
+      return gerepileupto(av,y);
+
+    case t_PADIC: pari_err_IMPL("p-adic jbessel function");
+    default:
+      if (!(y = toser_i(z))) break;
+      if (issmall(n,&ki)) n = utoi(labs(ki));
+      return gerepileupto(av, _jbessel(n,y,flag,lg(y)-2));
+  }
+  pari_err_TYPE("jbessel",z);
+  return NULL; /* not reached */
+}
+GEN
+jbessel(GEN n, GEN z, long prec) { return jbesselintern(n,z,1,prec); }
+GEN
+ibessel(GEN n, GEN z, long prec) { return jbesselintern(n,z,0,prec); }
+
+static GEN
+_jbesselh(long k, GEN z, long prec)
+{
+  GEN s,c,p0,p1,p2, zinv = ginv(z);
+  long i;
+
+  gsincos(z,&s,&c,prec);
+  p1 = gmul(zinv,s);
+  if (k)
+  {
+    p0 = p1; p1 = gmul(zinv,gsub(p0,c));
+    for (i=2; i<=k; i++)
+    {
+      p2 = gsub(gmul(gmulsg(2*i-1,zinv),p1), p0);
+      p0 = p1; p1 = p2;
+    }
+  }
+  return p1;
+}
+
+/* J_{n+1/2}(z) */
+GEN
+jbesselh(GEN n, GEN z, long prec)
+{
+  long k, i;
+  pari_sp av;
+  GEN y;
+
+  if (typ(n)!=t_INT) pari_err_TYPE("jbesselh",n);
+  k = itos(n);
+  if (k < 0) return jbessel(gadd(ghalf,n), z, prec);
+
+  switch(typ(z))
+  {
+    case t_INT: case t_FRAC: case t_QUAD:
+    case t_REAL: case t_COMPLEX:
+    {
+      long bits, precnew, gz, pr;
+      GEN p1;
+      if (gequal0(z))
+      {
+        av = avma;
+        p1 = gmul(gsqrt(gdiv(z,mppi(prec)),prec),gpowgs(z,k));
+        p1 = gdiv(p1, mulu_interval(k+1, 2*k+1)); /* x k! / (2k+1)! */
+        return gerepileupto(av, gmul2n(p1,2*k));
+      }
+      gz = gexpo(z);
+      if ( (pr = precision(z)) ) prec = pr;
+      y = cgetc(prec);
+      bits = -2*k*gz + BITS_IN_LONG;
+      av = avma;
+      if (bits <= 0)
+        precnew = prec;
+      else
+      {
+        precnew = prec + nbits2extraprec(bits);
+        if (pr) z = gtofp(z, precnew);
+      }
+      p1 = gmul(_jbesselh(k,z,prec), gsqrt(gdiv(z,Pi2n(-1,prec)),prec));
+      avma = av; return affc_fixlg(p1, y);
+    }
+
+    case t_VEC: case t_COL: case t_MAT:
+      return jbesselhvec(n, z, prec);
+
+    case t_POLMOD:
+      av = avma;
+      return gerepileupto(av, jbesselhvec(n, polmod_to_embed(z,prec), prec));
+
+    case t_PADIC: pari_err_IMPL("p-adic jbesselh function");
+    default:
+    {
+      GEN p1, v;
+      av = avma; if (!(y = toser_i(z))) break;
+      if (gequal0(y)) return gerepileupto(av, gpowgs(y,k));
+      if (valp(y) < 0) pari_err_DOMAIN("besseljh","valuation", "<", gen_0, y);
+      y = gprec(y, lg(y)-2 + (2*k+1)*valp(y));
+      p1 = gdiv(_jbesselh(k,y,prec),gpowgs(y,k));
+      v = cgetg(k+1, t_VECSMALL);
+      for (i=1; i<=k; i++) v[i] = 2*i+1;
+      p1 = gmul(p1, zv_prod_Z(v));
+      return gerepilecopy(av,p1);
+    }
+  }
+  pari_err_TYPE("besseljh",z);
+  return NULL; /* not reached */
+}
+
+static GEN
+kbessel2(GEN nu, GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN p1, x2, a;
+
+  if (typ(x)==t_REAL) prec = realprec(x);
+  x2 = gshift(x,1);
+  a = gtofp(gaddgs(gshift(nu,1), 1), prec);
+  p1 = hyperu(gshift(a,-1),a,x2,prec);
+  p1 = gmul(gmul(p1,gpow(x2,nu,prec)), sqrtr(mppi(prec)));
+  return gerepileupto(av, gmul(p1,gexp(gneg(x),prec)));
+}
+
+static GEN
+kbessel1(GEN nu, GEN gx, long prec)
+{
+  GEN x, y, p1, zf, zz, r, s, t, u, ak, pitemp, nu2;
+  long l, lnew, k, k2, l1, n2, n, ex;
+  pari_sp av;
+
+  if (typ(nu)==t_COMPLEX) return kbessel2(nu,gx,prec);
+  l = (typ(gx)==t_REAL)? realprec(gx): prec;
+  ex = gexpo(gx);
+  if (ex < 0)
+  {
+    long rab = nbits2extraprec(-ex);
+    lnew = l + rab; prec += rab;
+  }
+  else lnew = l;
+  y = cgetr(l); l1=lnew+1;
+  av = avma; x = gtofp(gx, lnew); nu = gtofp(nu, lnew);
+  nu2 = gmul2n(sqrr(nu), 2); togglesign(nu2);
+  n = (long) (prec2nbits_mul(l,LOG2) + PI*fabs(rtodbl(nu))) / 2;
+  n2 = n<<1; pitemp=mppi(l1);
+  r = gmul2n(x,1);
+  if (cmprs(x, n) < 0)
+  {
+    GEN q = stor(n2, l1), v, c, e, f;
+    pari_sp av1, av2;
+    u=cgetr(l1); v=cgetr(l1); e=cgetr(l1); f=cgetr(l1);
+    av1 = avma;
+    zf = sqrtr(divru(pitemp,n2));
+    zz = invr(stor(n2<<2, prec));
+    s = real_1(prec); t = real_0(prec);
+    for (k=n2,k2=2*n2-1; k > 0; k--,k2-=2)
+    {
+      p1 = addri(nu2, sqrs(k2));
+      ak = divrs(mulrr(p1,zz),-k);
+      s = addsr(1, mulrr(ak,s));
+      t = addsr(k2,mulrr(ak,t));
+    }
+    mulrrz(zf, s, u); shiftr_inplace(t, -1);
+    divrsz(addrr(mulrr(t,zf),mulrr(u,nu)),-n2,v);
+    for(;; avma = av1)
+    {
+      GEN d = real_1(l1);
+      c = divur(5,q); if (expo(c) >= -1) c = real2n(-1,l1);
+      p1 = subsr(1,divrr(r,q)); if (cmprr(c,p1)>0) c = p1;
+      togglesign(c);
+      affrr(u,e);
+      affrr(v,f); av2 = avma;
+      for (k=1;; k++, avma=av2)
+      {
+        GEN w = addrr(gmul2n(mulur(2*k-1,u), -1), mulrr(subrs(q,k),v));
+        w = addrr(w, mulrr(nu, subrr(u,gmul2n(v,1))));
+        divrsz(mulrr(q,v),k,u);
+        divrsz(w,k,v);
+        mulrrz(d,c,d);
+        addrrz(e,mulrr(d,u),e); p1=mulrr(d,v);
+        addrrz(f,p1,f);
+        if (gexpo(p1) - gexpo(f) <= 1-prec2nbits(precision(p1))) break;
+
+      }
+      swap(e, u);
+      swap(f, v);
+      affrr(mulrr(q,addrs(c,1)), q);
+      if (expo(subrr(q,r)) - expo(r) <= 1-prec2nbits(lnew)) break;
+    }
+    u = mulrr(u, gpow(divru(x,n),nu,prec));
+  }
+  else
+  {
+    zf = sqrtr(divrr(pitemp,r));
+    zz = ginv(gmul2n(r,2)); s = real_1(prec);
+    for (k=n2,k2=2*n2-1; k > 0; k--,k2-=2)
+    {
+      p1 = addri(nu2, sqrs(k2));
+      ak = divru(mulrr(p1,zz), k);
+      s = subsr(1, mulrr(ak,s));
+    }
+    u = mulrr(s, zf);
+  }
+  affrr(mulrr(u, mpexp(mpneg(x))), y);
+  avma = av; return y;
+}
+
+/*   sum_{k=0}^m Z^k (H(k)+H(k+n)) / (k! (k+n)!)
+ * + sum_{k=0}^{n-1} (-Z)^(k-n) (n-k-1)!/k!   with Z := (-1)^flag*z^2/4.
+ * Warning: contrary to _jbessel, no n! in front.
+ * When flag > 1, compute exactly the H(k) and factorials (slow) */
+static GEN
+_kbessel1(long n, GEN z, long flag, long m, long prec)
+{
+  GEN Z, p1, p2, s, H;
+  pari_sp av, lim;
+  long k;
+
+  Z = gmul2n(gsqr(z),-2); if (flag & 1) Z = gneg(Z);
+  if (typ(z) == t_SER)
+  {
+    long v = valp(z);
+    if (v < 0) pari_err_DOMAIN("_kbessel1","valuation", "<", gen_0, z);
+    k = lg(Z)-2 - v;
+    if (v == 0) pari_err_IMPL("Bessel K around a!=0");
+    if (k <= 0) return gadd(gen_1, zeroser(varn(z), 2*v));
+    setlg(Z, k+2);
+  }
+  H = cgetg(m+n+2,t_VEC); gel(H,1) = gen_0;
+  if (flag <= 1)
+  {
+    gel(H,2) = s = real_1(prec);
+    for (k=2; k<=m+n; k++) gel(H,k+1) = s = divru(addsr(1,mulur(k,s)),k);
+  }
+  else
+  {
+    gel(H,2) = s = gen_1;
+    for (k=2; k<=m+n; k++) gel(H,k+1) = s = gdivgs(gaddsg(1,gmulsg(k,s)),k);
+  }
+  s = gadd(gel(H,m+1), gel(H,m+n+1));
+  av = avma; lim = stack_lim(av,1);
+  for (k=m; k>0; k--)
+  {
+    s = gadd(gadd(gel(H,k),gel(H,k+n)),gdiv(gmul(Z,s),mulss(k,k+n)));
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"_kbessel1");
+      s = gerepileupto(av, s);
+    }
+  }
+  p1 = (flag <= 1) ? mpfactr(n,prec) : mpfact(n);
+  s = gdiv(s,p1);
+  if (n)
+  {
+    Z = gneg(ginv(Z));
+    p2 = gmulsg(n, gdiv(Z,p1));
+    s = gadd(s,p2);
+    for (k=n-1; k>0; k--)
+    {
+      p2 = gmul(p2, gmul(mulss(k,n-k),Z));
+      s = gadd(s,p2);
+    }
+  }
+  return s;
+}
+
+/* flag = 0: K / flag = 1: N */
+static GEN
+kbesselintern(GEN n, GEN z, long flag, long prec)
+{
+  const char *f = flag? "besseln": "besselk";
+  const long flK = (flag == 0);
+  long i, k, ki, lim, precnew, fl2, ex;
+  pari_sp av = avma;
+  GEN p1, p2, y, p3, pp, pm, s, c;
+  double B, L;
+
+  switch(typ(z))
+  {
+    case t_INT: case t_FRAC: case t_QUAD:
+    case t_REAL: case t_COMPLEX:
+      if (gequal0(z)) pari_err_DOMAIN(f, "argument", "=", gen_0, z);
+      i = precision(z); if (i) prec = i;
+      i = precision(n); if (i && prec > i) prec = i;
+      ex = gexpo(z);
+      /* experimental */
+      if (!flag && !gequal0(n) && ex > prec2nbits(prec)/16 + gexpo(n))
+        return kbessel1(n,z,prec);
+      L = HALF_E * gtodouble(gabs(z,prec));
+      precnew = prec;
+      if (L >= HALF_E) {
+        long rab = nbits2extraprec((long) (L/(HALF_E*LOG2)));
+        if (flK) rab *= 2;
+         precnew += 1 + rab;
+      }
+      z = gtofp(z, precnew);
+      if (issmall(n,&ki))
+      {
+        GEN z2 = gmul2n(z, -1);
+        k = labs(ki);
+        B = prec2nbits_mul(prec,LOG2/2) / L;
+        if (flK) B += 0.367879;
+        lim = bessel_get_lim(B, L);
+        p1 = gmul(gpowgs(z2,k), _kbessel1(k,z,flag,lim,precnew));
+        p2 = gadd(mpeuler(precnew), glog(z2,precnew));
+        p3 = jbesselintern(stoi(k),z,flag,precnew);
+        p2 = gsub(gmul2n(p1,-1),gmul(p2,p3));
+        p2 = gprec_wtrunc(p2, prec);
+        if (flK) {
+          if (k & 1) p2 = gneg(p2);
+        }
+        else
+        {
+          p2 = gdiv(p2, Pi2n(-1,prec));
+          if (ki >= 0 || (k&1)==0) p2 = gneg(p2);
+        }
+        return gerepilecopy(av, p2);
+      }
+
+      n = gtofp(n, precnew);
+      gsincos(gmul(n,mppi(precnew)), &s,&c,precnew);
+      ex = gexpo(s);
+      if (ex < 0)
+      {
+        long rab = nbits2extraprec(-ex);
+        if (flK) rab *= 2;
+        precnew += rab;
+      }
+      if (i && i < precnew) {
+        n = gtofp(n,precnew);
+        z = gtofp(z,precnew);
+        gsincos(gmul(n,mppi(precnew)), &s,&c,precnew);
+      }
+
+      pp = jbesselintern(n,      z,flag,precnew);
+      pm = jbesselintern(gneg(n),z,flag,precnew);
+      if (flK)
+        p1 = gmul(gsub(pm,pp), Pi2n(-1,precnew));
+      else
+        p1 = gsub(gmul(c,pp),pm);
+      p1 = gdiv(p1, s);
+      return gerepilecopy(av, gprec_wtrunc(p1,prec));
+
+    case t_VEC: case t_COL: case t_MAT:
+      return kbesselvec(n,z,flag,prec);
+
+    case t_POLMOD:
+      y = kbesselvec(n,polmod_to_embed(z,prec),flag,prec);
+      return gerepileupto(av, y);
+
+    case t_PADIC: pari_err_IMPL(stack_strcat("p-adic ",f));
+    default:
+      if (!(y = toser_i(z))) break;
+      if (issmall(n,&ki))
+      {
+        k = labs(ki);
+        return gerepilecopy(av, _kbessel1(k,y,flag+2,lg(y)-2,prec));
+      }
+      if (!issmall(gmul2n(n,1),&ki))
+        pari_err_DOMAIN(f, "2n mod Z", "!=", gen_0,n);
+      k = labs(ki); n = gmul2n(stoi(k),-1);
+      fl2 = (k&3)==1;
+      pm = jbesselintern(gneg(n),y,flag,prec);
+      if (flK)
+      {
+        pp = jbesselintern(n,y,flag,prec);
+        p2 = gpowgs(y,-k); if (fl2 == 0) p2 = gneg(p2);
+        p3 = gmul2n(diviiexact(mpfact(k + 1),mpfact((k + 1) >> 1)),-(k + 1));
+        p3 = gdivgs(gmul2n(gsqr(p3),1),k);
+        p2 = gmul(p2,p3);
+        p1 = gsub(pp,gmul(p2,pm));
+      }
+      else p1 = pm;
+      return gerepileupto(av, fl2? gneg(p1): gcopy(p1));
+  }
+  pari_err_TYPE(f,z);
+  return NULL; /* not reached */
+}
+
+GEN
+kbessel(GEN n, GEN z, long prec) { return kbesselintern(n,z,0,prec); }
+GEN
+nbessel(GEN n, GEN z, long prec) { return kbesselintern(n,z,1,prec); }
+/* J + iN */
+GEN
+hbessel1(GEN n, GEN z, long prec)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gadd(jbessel(n,z,prec), mulcxI(nbessel(n,z,prec))));
+}
+/* J - iN */
+GEN
+hbessel2(GEN n, GEN z, long prec)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gadd(jbessel(n,z,prec), mulcxmI(nbessel(n,z,prec))));
+}
+
+/***********************************************************************/
+/*                                                                    **/
+/**                    FONCTION U(a,b,z) GENERALE                     **/
+/**                         ET CAS PARTICULIERS                       **/
+/**                                                                   **/
+/***********************************************************************/
+/* Assume gx > 0 and a,b complex */
+/* This might one day be extended to handle complex gx */
+/* see Temme, N. M. "The numerical computation of the confluent        */
+/* hypergeometric function U(a,b,z)" in Numer. Math. 41 (1983),        */
+/* no. 1, 63--82.                                                      */
+GEN
+hyperu(GEN a, GEN b, GEN gx, long prec)
+{
+  GEN S, P, T, x, p1, zf, u, a1, mb = gneg(b);
+  const int ex = iscomplex(a) || iscomplex(b);
+  long k, n, l = (typ(gx)==t_REAL)? realprec(gx): prec, l1 = l+EXTRAPRECWORD;
+  GEN y = ex? cgetc(l): cgetr(l);
+  pari_sp av = avma;
+
+  if (gsigne(gx) <= 0) pari_err_IMPL("non-positive third argument in hyperu");
+  x = gtofp(gx, l);
+  a1 = gaddsg(1, gadd(a,mb)); P = gmul(a1, a);
+  p1 = gabs(gtofp(P,LOWDEFAULTPREC), LOWDEFAULTPREC);
+  n = (long)(prec2nbits_mul(l, LOG2) + PI*sqrt(gtodouble(p1)));
+  S = gadd(a1, a);
+  if (cmprs(x,n) < 0)
+  {
+    GEN q = stor(n, l1), s = gen_1, t = gen_0, v, c, e, f;
+    pari_sp av1, av2;
+
+    if (ex) { u=cgetc(l1); v=cgetc(l1); e=cgetc(l1); f=cgetc(l1); }
+    else    { u=cgetr(l1); v=cgetr(l1); e=cgetr(l1); f=cgetr(l1); }
+    av1 = avma;
+    zf = gpow(stoi(n),gneg_i(a),l1);
+    T = gadd(gadd(P, gmulsg(n-1, S)), sqrs(n-1));
+    for (k=n-1; k>=0; k--)
+    {
+      /* T = (a+k)*(a1+k) = a*a1 + k(a+a1) + k^2 = previous(T) - S - 2k + 1 */
+      p1 = gdiv(T, mulss(-n, k+1));
+      s = gaddgs(gmul(p1,s), 1);
+      t = gadd(  gmul(p1,t), gaddgs(a,k));
+      if (!k) break;
+      T = gsubgs(gsub(T, S), 2*k-1);
+    }
+    gmulz(zf, s, u);
+    gmulz(zf, gdivgs(t,-n), v);
+    for(;; avma = av1)
+    {
+      GEN d = real_1(l1), p3 = gadd(q,mb);
+      c = divur(5,q); if (expo(c)>= -1) c = real2n(-1, l1);
+      p1 = subsr(1,divrr(x,q)); if (cmprr(c,p1)>0) c = p1;
+      togglesign(c);
+      gaffect(u,e);
+      gaffect(v,f); av2 = avma;
+      for(k=1;;k++, avma = av2)
+      {
+        GEN w = gadd(gmul(gaddgs(a,k-1),u), gmul(gaddgs(p3,1-k),v));
+        gmulz(divru(q,k),v, u);
+        gaffect(gdivgs(w,k), v);
+        mulrrz(d,c,d);
+        gaddz(e,gmul(d,u),e); p1=gmul(d,v);
+        gaddz(f,p1,f);
+        if (gequal0(p1) || gexpo(p1) - gexpo(f) <= 1-prec2nbits(precision(p1)))
+          break;
+      }
+      swap(e, u);
+      swap(f, v);
+      affrr(mulrr(q, addrs(c,1)), q);
+      if (expo(subrr(q,x)) - expo(x) <= 1-prec2nbits(l)) break;
+    }
+  }
+  else
+  {
+    GEN zz = invr(x), s = gen_1;
+    togglesign(zz); /* -1/x */
+    zf = gpow(x,gneg_i(a),l1);
+    T = gadd(gadd(P, gmulsg(n-1, S)), sqrs(n-1));
+    for (k=n-1; k>=0; k--)
+    {
+      p1 = gmul(T,divru(zz,k+1));
+      s = gaddsg(1, gmul(p1,s));
+      if (!k) break;
+      T = gsubgs(gsub(T, S), 2*k-1);
+    }
+    u = gmul(s,zf);
+  }
+  gaffect(u,y); avma = av; return y;
+}
+
+/* incgam(0, x, prec) = eint1(x); typ(x) = t_REAL, x > 0 */
+static GEN
+incgam_0(GEN x, GEN expx)
+{
+  pari_sp av;
+  long l = realprec(x), n, i;
+  double mx = rtodbl(x), L = prec2nbits_mul(l,LOG2);
+  GEN z;
+
+  if (!mx) pari_err_DOMAIN("eint1", "x","=",gen_0, x);
+  if (mx > L)
+  {
+    double m = (L + mx)/4;
+    n = (long)(1+m*m/mx);
+    av = avma;
+    z = divsr(-n, addsr(n<<1,x));
+    for (i=n-1; i >= 1; i--)
+    {
+      z = divsr(-i, addrr(addsr(i<<1,x), mulur(i,z))); /* -1 / (2 + z + x/i) */
+      if ((i & 0x1ff) == 0) z = gerepileuptoleaf(av, z);
+    }
+    return divrr(addrr(real_1(l),z), mulrr(expx? expx: mpexp(x), x));
+  }
+  else
+  {
+    long prec = l + nbits2extraprec((mx+log(mx))/LOG2);
+    GEN S, t, H, run = real_1(prec);
+    n = -prec2nbits(prec);
+    x = rtor(x, prec);
+    av = avma;
+    S = z = t = H = run;
+    for (i = 2; expo(t) - expo(S) >= n; i++)
+    {
+      H = addrr(H, divru(run,i)); /* H = sum_{k<=i} 1/k */
+      z = divru(mulrr(x,z), i);   /* z = x^(i-1)/i! */
+      t = mulrr(z, H); S = addrr(S, t);
+      if ((i & 0x1ff) == 0) gerepileall(av, 4, &z,&t,&S,&H);
+    }
+    return subrr(mulrr(x, divrr(S,expx? expx: mpexp(x))),
+                 addrr(mplog(x), mpeuler(prec)));
+  }
+}
+
+/* incgam using the continued fraction. x a t_REAL or t_COMPLEX, mx ~ |x|.
+ * Assume precision(s), precision(x) >= prec */
+static GEN
+incgam_cf(GEN s, GEN x, double mx, long prec)
+{
+  GEN x_s, S, y;
+  long n, i;
+  pari_sp av = avma, av2, avlim;
+  double m;
+
+  m = (prec2nbits_mul(prec,LOG2) + mx)/4;
+  n = (long)(1+m*m/mx);
+  if (typ(s) == t_INT) /* y = x^(s-1) exp(-x) */
+    y = gmul(gexp(gneg(x), prec), powgi(x,subis(s,1)));
+  else
+    y = gexp(gsub(gmul(gsubgs(s,1), glog(x, prec)), x), prec);
+  x_s = gsub(x, s);
+  av2 = avma; avlim = stack_lim(av2,3);
+  S = gdiv(gsubgs(s,n), gaddgs(x_s,n<<1));
+  for (i=n-1; i >= 1; i--)
+  {
+    S = gdiv(gsubgs(s,i), gadd(gaddgs(x_s,i<<1),gmulsg(i,S)));
+    if (low_stack(avlim,stack_lim(av2,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"incgam_cf");
+      S = gerepileupto(av2, S);
+    }
+  }
+  return gerepileupto(av, gmul(y, gaddsg(1,S)));
+}
+
+/* use exp(-x) * (x^s/s) * sum_{k >= 0} x^k / prod(i=1, k, s+i) */
+GEN
+incgamc(GEN s, GEN x, long prec)
+{
+  GEN S, t, y;
+  long l, n, i, ex;
+  pari_sp av = avma, av2, avlim;
+
+  if (typ(x) != t_REAL) x = gtofp(x, prec);
+  if (gequal0(x)) return gcopy(x);
+
+  l = precision(x); n = -prec2nbits(l)-1;
+  ex = gexpo(x);
+  if (ex > 0 && ex > gexpo(s))
+  { /* take cancellation into account */
+    long p = LOWDEFAULTPREC;
+    double X = rtodbl(gabs(gtofp(x, p), p));
+    p = l + nbits2extraprec(X*log(X));
+    x = gtofp(x, p);
+    if (isinexactreal(s)) s = gtofp(s, p);
+  }
+  av2 = avma; avlim = stack_lim(av2,3);
+  S = gdiv(x, gaddsg(1,s));
+  t = gaddsg(1, S);
+  for (i=2; gexpo(S) >= n; i++)
+  {
+    S = gdiv(gmul(x,S), gaddsg(i,s)); /* x^i / ((s+1)...(s+i)) */
+    t = gadd(S,t);
+    if (low_stack(avlim,stack_lim(av2,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"incgamc");
+      gerepileall(av2, 2, &S, &t);
+    }
+  }
+  if (typ(s)==t_INT)
+    y = gmul(gexp(gneg(x), prec), powgi(x,s));
+  else
+    y = gexp(gsub(gmul(s, glog(x, prec)), x), prec);
+  return gerepileupto(av, gmul(gdiv(y,s), t));
+}
+
+/* incgamma using asymptotic expansion:
+ *   exp(-x)x^(s-1)(1 + (s-1)/x + (s-1)(s-2)/x^2 + ...) */
+static GEN
+incgam_asymp(GEN s, GEN x, long prec)
+{
+  pari_sp av = avma, av2, lim;
+  GEN S, q, cox, invx;
+  long oldeq = LONG_MAX, eq, esx, j;
+  invx = ginv(x);
+  esx = -prec2nbits(prec);
+  av2 = avma; lim = stack_lim(av2, 1);
+  q = gmul(gsubgs(s, 1), invx);
+  S = gaddgs(q, 1);
+  for (j = 2;; j++)
+  {
+    eq = gexpo(q); if (eq < esx) break;
+    if ((j & 3) == 0)
+    { /* guard against divergence */
+      if (eq > oldeq) { avma = av; return NULL; } /* regressing, abort */
+      oldeq = eq;
+    }
+    q = gmul(q, gmul(gsubgs(s, j), invx));
+    S = gadd(S, q);
+    if (low_stack(lim, stack_lim(av2, 1))) gerepileall(av2, 2, &S, &q);
+  }
+  if (typ(s) == t_INT) /* exp(-x) x^(s-1) */
+    cox = gmul(gexp(gneg(x), prec), powgi(x, subis(s, 1)));
+  else
+    cox = gexp(gsub(gmul(gsubgs(s, 1), glog(x,prec)), x), prec);
+  return gerepileupto(av, gmul(cox, S));
+}
+
+static GEN
+gexp1(GEN x, long prec)
+{
+  if (typ(x) != t_REAL) x = gtofp(x, prec);
+  return mpexpm1(x);
+}
+
+/* assume s != 0 */
+static GEN
+incgamspec(GEN s, GEN x, GEN g, long prec)
+{
+  pari_sp av = avma;
+  GEN q, S, cox, P, sk, S1, S2, S3, F2, F3, logx, mx;
+  long esk, n, k = itos(ground(gneg(real_i(s))));
+
+  sk = gaddgs(s, k);
+  logx = glog(x, prec);
+  mx = gneg(x);
+  cox = gexp(gadd(mx, gmul(s, logx)), prec); /* x^s exp(-x) */
+  if (k == 0) { S = gen_0; P = gen_1; }
+  else
+  {
+    long j;
+    q = ginv(s); S = q; P = s;
+    for (j = 1; j < k; j++)
+    {
+      GEN sj = gaddgs(s, j);
+      q = gmul(q, gdiv(x, sj));
+      S = gadd(S, q);
+      P = gmul(P, sj);
+    }
+    S = gmul(S, gneg(cox));
+  }
+  if (k && gequal0(sk))
+    return gerepileupto(av, gadd(S, gdiv(eint1(x, prec), P)));
+
+  esk = gexpo(sk);
+  if (esk > -7)
+  {
+    GEN a, b, PG = gmul(sk, P);
+    if (g) g = gmul(g, PG);
+    a = incgam0(gaddgs(sk,1), x, g, prec);
+    b = gmul(gpowgs(x, k), cox);
+    return gerepileupto(av, gadd(S, gdiv(gsub(a, b), PG)));
+  }
+  else if (2*esk > -prec2nbits(prec) - 4)
+  {
+    if (typ(sk) != t_REAL) sk = gtofp(sk, prec);
+    S1 = gdiv(gexp1(glngamma(gaddgs(sk, 1), prec), prec), sk);
+    F3 = gexp1(gmul(sk, logx), prec);
+    F2 = gneg(gdiv(F3, sk));
+    F3 = gaddsg(1, F3);
+  }
+  else
+  {
+    GEN EUL = mpeuler(prec);
+    S1 = gadd(negr(EUL), gmul(gdivgs(sk, 2), addrr(szeta(2,prec), sqrr(EUL))));
+    F2 = gmul(gneg(logx), gaddsg(1, gmul(gdivgs(sk, 2), logx)));
+    F3 = gexp(gmul(sk, logx), prec);
+  }
+  S2 = gmul(F2, gadd(gexp(mx, prec),
+                     gdiv(incgamc(gaddgs(sk,1), x, prec), F3)));
+  S3 = gdiv(x, gaddsg(1,sk));
+  q = x; n = 1;
+  while (gexpo(q) > -prec2nbits(prec))
+  {
+    n++; q = gmul(q, gdivgs(mx, n));
+    S3 = gadd(S3, gdiv(q, gaddsg(n, sk)));
+  }
+  return gerepileupto(av, gadd(S, gdiv(gadd(gadd(S1, S2), S3), P)));
+}
+
+#if 0
+static long
+precision2(GEN x, GEN y)
+{
+  long px = precision(x), py = precision(y);
+  if (!px) return py;
+  if (!py) return px;
+  return minss(px, py);
+}
+#endif
+
+/* return |x| */
+static double
+dblmodulus(GEN x)
+{
+  if (typ(x) == t_COMPLEX)
+  {
+    double a = gtodouble(gel(x,1));
+    double b = gtodouble(gel(x,2));
+    return sqrt(a*a + b*b);
+  }
+  else
+    return fabs(gtodouble(x));
+}
+
+/* Driver routine. If g != NULL, assume that g=gamma(s,prec). */
+GEN
+incgam0(GEN s, GEN x, GEN g, long prec)
+{
+  pari_sp av;
+  long E, es, l;
+  double mx;
+  GEN z, rs;
+
+  if (gequal0(x)) return g? gcopy(g): ggamma(s,prec);
+  if (gequal0(s)) return eint1(x, prec);
+  av = avma;
+  l = precision(s);
+  if (!l) l = prec;
+  E = prec2nbits(l) + 1;
+  if (typ(x) != t_REAL) x = gtofp(x, l);
+  /* avoid overflow in dblmodulus */
+  if (gexpo(x) > E) mx = E; else mx = dblmodulus(x);
+  /* use asymptotic expansion */
+  if (2*mx > E)
+  {
+    z = incgam_asymp(s, x, l);
+    if (z) return z;
+  }
+  /* use continued fraction */
+  if (12.1*mx > E) return incgam_cf(s, x, mx, l);
+  rs = real_i(s);
+  if (gsigne(rs) > 0 && gexpo(rs) >= -1)
+  { /* use complementary incomplete gamma */
+    es = gexpo(s);
+    if (es < 0) {
+      l += nbits2extraprec(-es) + 1;
+      s = gtofp(s, l);
+      x = gtofp(x, l);
+    }
+    if (!g) g = ggamma(s,l);
+    z = gsub(g, incgamc(s,x,l));
+    return gerepileupto(av, z);
+  }
+  /* use power series */
+  return gerepilecopy(av, incgamspec(s, x, g, l));
+}
+
+GEN
+incgam(GEN s, GEN x, long prec) { return incgam0(s, x, NULL, prec); }
+
+/* x >= 0 a t_REAL */
+GEN
+mpeint1(GEN x, GEN expx)
+{
+  GEN z = cgetr(lg(x));
+  pari_sp av = avma;
+  affrr(incgam_0(x, expx), z);
+  avma = av; return z;
+}
+
+static GEN
+cxeint1(GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN q, S1, S2, S3, logx, mx;
+  long n, E = prec2nbits(prec), ex = gexpo(x);
+
+  if (ex > E || dblmodulus(x) > 3*E/4)
+  {
+    GEN z = incgam_asymp(gen_0, x, prec);
+    if (z) return z;
+  }
+  logx = glog(x, prec);
+  S1 = negr(mpeuler(prec));
+  S2 = gneg(logx);
+  if (ex > 0)
+  { /* take cancellation into account, log2(\sum |x|^n / n!) = |x| / log(2) */
+    long p = LOWDEFAULTPREC;
+    double X = rtodbl(gabs(gtofp(x, p), p));
+    x = gtofp(x, prec + nbits2extraprec(X / LOG2));
+  }
+  q = S3 = x; n = 1; mx = gneg(x);
+  while (gexpo(q) > -E)
+  {
+    n++; q = gmul(q, gdivgs(mx, n));
+    S3 = gadd(S3, gdivgs(q, n));
+  }
+  return gerepileupto(av, gadd(gadd(S1, S2), S3));
+}
+
+GEN
+eint1(GEN x, long prec)
+{
+  long l, n, i;
+  pari_sp av;
+  GEN p1, t, S, y, res;
+
+  if (typ(x) != t_REAL) {
+    x = gtofp(x, prec);
+    if (typ(x) != t_REAL) return cxeint1(x, prec);
+  }
+  if (signe(x) >= 0) return mpeint1(x,NULL);
+  /* rewritten from code contributed by Manfred Radimersky */
+  res = cgetg(3, t_COMPLEX);
+  av = avma;
+  l  = lg(x);
+  n  = prec2nbits(l);
+  y  = negr(x);
+  if (cmprs(y, (3*n)/4) < 0) {
+    p1 = t = S = y;
+    for (i = 2; expo(t) - expo(S) >= -n; i++) {
+      p1 = mulrr(y, divru(p1, i)); /* (-x)^i/i! */
+      t = divru(p1, i);
+      S = addrr(S, t);
+    }
+    y  = addrr(S, addrr(logr_abs(x), mpeuler(l)));
+  } else { /* asymptotic expansion */
+    p1 = t = invr(y);
+    S = addrs(t, 1);
+    for (i = 2; expo(t) - expo(S) >= -n; i++) {
+      t = mulrr(p1, mulru(t, i));
+      S = addrr(S, t);
+    }
+    y  = mulrr(S, mulrr(p1, mpexp(y)));
+  }
+  gel(res, 1) = gerepileuptoleaf(av, negr(y));
+  y = mppi(prec); setsigne(y, -1);
+  gel(res, 2) = y; return res;
+}
+
+GEN
+veceint1(GEN C, GEN nmax, long prec)
+{
+  if (!nmax) return eint1(C,prec);
+  if (typ(nmax) != t_INT) pari_err_TYPE("veceint1",nmax);
+  if (typ(C) != t_REAL) {
+    C = gtofp(C, prec);
+    if (typ(C) != t_REAL) pari_err_TYPE("veceint1",C);
+  }
+  if (signe(C) <= 0) pari_err_DOMAIN("veceint1", "argument", "<=", gen_0,C);
+  return mpveceint1(C, NULL, itos(nmax));
+}
+
+/* j > 0, a t_REAL. Return sum_{m >= 0} a^m / j(j+1)...(j+m)).
+ * Stop when expo(summand) < E; note that s(j-1) = (a s(j) + 1) / (j-1). */
+static GEN
+mp_sum_j(GEN a, long j, long E, long prec)
+{
+  pari_sp av = avma;
+  GEN q = divru(real_1(prec), j), s = q;
+  long m;
+  for (m = 0;; m++)
+  {
+    if (expo(q) < E) break;
+    q = mulrr(q, divru(a, m+j));
+    s = addrr(s, q);
+  }
+  return gerepileuptoleaf(av, s);
+}
+/* Return the s_a(j), j <= J */
+static GEN
+sum_jall(GEN a, long J, long prec)
+{
+  GEN s = cgetg(J+1, t_VEC);
+  long j, E = -bit_accuracy(prec) - 5;
+  gel(s, J) = mp_sum_j(a, J, E, prec);
+  for (j = J-1; j; j--)
+    gel(s,j) = divru(addrs(mulrr(a, gel(s,j+1)), 1), j);
+  return s;
+}
+
+/* T a dense t_POL with t_REAL coeffs. Return T(n) [faster than poleval] */
+static GEN
+rX_s_eval(GEN T, long n)
+{
+  long i = lg(T)-1;
+  GEN c = gel(T,i);
+  for (i--; i>=2; i--) c = gadd(mulrs(c,n),gel(T,i));
+  return c;
+}
+
+/* C>0 t_REAL, eC = exp(C). Return eint1(n*C) for 1<=n<=N. Absolute accuracy */
+GEN
+mpveceint1(GEN C, GEN eC, long N)
+{
+  const long prec = realprec(C);
+  long Nmin = 15; /* >= 1. E.g. between 10 and 30, but little effect */
+  GEN en, v, w = cgetg(N+1, t_VEC);
+  pari_sp av0;
+  double DL;
+  long n, j, jmax, jmin;
+  if (!N) return w;
+  for (n = 1; n <= N; n++) gel(w,n) = cgetr(prec);
+  av0 = avma;
+  if (N < Nmin) Nmin = N;
+  if (!eC) eC = mpexp(C);
+  en = eC; affrr(incgam_0(C, en), gel(w,1));
+  for (n = 2; n <= Nmin; n++)
+  {
+    pari_sp av2;
+    en = mulrr(en,eC); /* exp(n C) */
+    av2 = avma;
+    affrr(incgam_0(mulru(C,n), en), gel(w,n));
+    avma = av2;
+  }
+  if (Nmin == N) { avma = av0; return w; }
+
+  DL = bit_accuracy_mul(prec, LOG2) + 5;
+  jmin = ceil(DL/log(N)) + 1;
+  jmax = ceil(DL/log(Nmin)) + 1;
+  v = sum_jall(C, jmax, prec);
+  en = powrs(eC, -N); /* exp(-N C) */
+  affrr(incgam_0(mulru(C,N), invr(en)), gel(w,N));
+  for (j = jmin, n = N-1; j <= jmax; j++)
+  {
+    long limN = maxss((long)ceil(exp(DL/j)), Nmin);
+    GEN polsh;
+    setlg(v, j+1);
+    polsh = RgV_to_RgX_reverse(v, 0);
+    for (; n >= limN; n--)
+    {
+      pari_sp av2 = avma;
+      GEN S = divri(mulrr(en, rX_s_eval(polsh, -n)), powuu(n,j));
+      /* w[n+1] - exp(-n C) * polsh(-n) / (-n)^j */
+      GEN c = odd(j)? addrr(gel(w,n+1), S) : subrr(gel(w,n+1), S);
+      affrr(c, gel(w,n)); avma = av2;
+      en = mulrr(en,eC); /* exp(-n C) */
+    }
+  }
+  avma = av0; return w;
+}
+
+/* e t_REAL, vector of e^i, 1 <= i <= n */
+GEN
+powruvec(GEN e, ulong n)
+{
+  GEN G = cgetg(n+1, t_VEC);
+  ulong j;
+  if (n)
+  {
+    gel(G, 1) = e;
+    for (j = 2; j <= n; j++) gel(G,j) = mulrr(gel(G,j-1), e);
+  }
+  return G;
+}
+
+/* erfc via numerical integration : assume real(x)>=1 */
+static GEN
+cxerfc_r1(GEN x, long prec)
+{
+  GEN h, h2, eh2, denom, res, lambda;
+  long u, v;
+  const double D = prec2nbits_mul(prec, LOG2);
+  const long npoints = (long)ceil(D/PI)+1;
+  pari_sp av = avma;
+  {
+    double t = exp(-2*PI*PI/D); /* ~exp(-2*h^2) */
+    v = 30; /* bits that fit in both long and double mantissa */
+    u = (long)floor(t*(1L<<v));
+    /* define exp(-2*h^2) to be u*2^(-v) */
+  }
+  incrprec(prec);
+  x = gtofp(x,prec);
+  eh2 = sqrtr_abs(rtor(shiftr(dbltor(u),-v),prec));
+  h2 = negr(logr_abs(eh2));
+  h = sqrtr_abs(h2);
+  lambda = gdiv(x,h);
+  denom = gsqr(lambda);
+  { /* res = h/x + 2*x*h*sum(k=1,npoints,exp(-(k*h)^2)/(lambda^2+k^2)); */
+    GEN Uk; /* = exp(-(kh)^2) */
+    GEN Vk = eh2;/* = exp(-(2k+1)h^2) */
+    pari_sp av2 = avma;
+    long k;
+    /* k = 0 moved out for efficiency */
+    denom = gaddsg(1,denom);
+    Uk = Vk;
+    Vk = mulur(u,Vk); shiftr_inplace(Vk, -v);
+    res = gdiv(Uk, denom);
+    for (k = 1; k < npoints; k++)
+    {
+      if ((k & 255) == 0) gerepileall(av2,4,&denom,&Uk,&Vk,&res);
+      denom = gaddsg(2*k+1,denom);
+      Uk = mpmul(Uk,Vk);
+      Vk = mulur(u,Vk); shiftr_inplace(Vk, -v);
+      res = gadd(res, gdiv(Uk, denom));
+    }
+  }
+  res = gmul(res, gshift(lambda,1));
+  /* 0 term : */
+  res = gadd(res, ginv(lambda));
+  res = gmul(res, gdiv(gexp(gneg(gsqr(x)), prec), mppi(prec)));
+  if (rtodbl(real_i(x)) < sqrt(D))
+  {
+    GEN t = gmul(divrr(Pi2n(1,prec),h), x);
+    res = gsub(res, gdivsg(2, cxexpm1(t, prec)));
+  }
+  return gerepileupto(av,res);
+}
+
+GEN
+gerfc(GEN x, long prec)
+{
+  GEN z, xr, xi, res;
+  pari_sp av;
+
+  x = trans_fix_arg(&prec,&x,&xr,&xi, &av,&res);
+  if (signe(xr) >= 0) {
+    if (cmprs(xr, 1) > 0) /* use numerical integration */
+      z = cxerfc_r1(x, prec);
+    else
+    { /* erfc(x) = incgam(1/2,x^2)/sqrt(Pi) */
+      GEN sqrtpi = sqrtr(mppi(prec));
+      z = incgam0(ghalf, gsqr(x), sqrtpi, prec);
+      z = gdiv(z, sqrtpi);
+    }
+  }
+  else
+  { /* erfc(-x)=2-erfc(x) */
+    /* FIXME could decrease prec
+    long size = nbits2extraprec(ceil((pow(rtodbl(gimag(x)),2)-pow(rtodbl(greal(x)),2))/LOG2)));
+    prec = size > 0 ? prec : prec + size;
+    */
+    /* NOT gsubsg(2, ...) : would create a result of
+     * huge accuracy if re(x)>>1, rounded to 2 by subsequent affc_fixlg... */
+    z = gsub(real2n(1,prec+EXTRAPRECWORD), gerfc(gneg(x), prec));
+  }
+  avma = av; return affc_fixlg(z, res);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                     FONCTION ZETA DE RIEMANN                      **/
+/**                                                                   **/
+/***********************************************************************/
+static const double log2PI = 1.83787706641;
+
+static double
+get_xinf(double beta)
+{
+  const double maxbeta = 0.06415003; /* 3^(-2.5) */
+  double x0, y0, x1;
+
+  if (beta < maxbeta) return beta + pow(3*beta, 1.0/3.0);
+  x0 = beta + PI/2.0;
+  for(;;)
+  {
+    y0 = x0*x0;
+    x1 = (beta+atan(x0)) * (1+y0) / y0 - 1/x0;
+    if (0.99*x0 < x1) return x1;
+    x0 = x1;
+  }
+}
+/* optimize for zeta( s + it, prec ), assume
+ * 1) |s-1| > 0.1 (guaranteed since if gexpo(u = s-1) < -5, we use the
+ * functional equation s->1-s)
+ * 2) s > 0 if t = 0 [ we use the functional equation otherwise ] */
+static void
+optim_zeta(GEN S, long prec, long *pp, long *pn)
+{
+  double s, t, alpha, beta, n, B;
+  long p;
+  if (typ(S) == t_REAL) {
+    s = rtodbl(S);
+    t = 0.;
+  } else {
+    s = rtodbl(gel(S,1));
+    t = fabs( rtodbl(gel(S,2)) );
+  }
+
+  B = prec2nbits_mul(prec, LOG2);
+  if (!t) /* real input */
+  {
+    double sn = fabs(s);
+    beta = B + 0.61 + s*(log2PI - log(s));
+    if (beta > 0)
+    {
+      p = (long)ceil(beta / 2.0);
+      n = fabs(s + 2*p-1)/(2*PI);
+    }
+    else
+    {
+      p = 0;
+      n = exp((B - LOG2 + log(sn/s)) / s);
+    }
+  }
+  else if (s <= 0 || t < 0.01) /* s < 0 may occur if s ~ 0 */
+  { /* TODO: the crude bounds below are generally valid. Optimize ? */
+    double l,l2, la = 1.; /* heuristic */
+    double rlog, ilog; dcxlog(s-1,t, &rlog,&ilog);
+    l2 = (s - 0.5)*rlog - t*ilog; /* = Re( (S - 1/2) log (S-1) ) */
+    l = (B - l2 + s*log2PI) / (2. * (1.+ log((double)la)));
+    l2 = dabs(s, t)/2;
+    if (l < l2) l = l2;
+    p = (long) ceil(l); if (p < 2) p = 2;
+    n = 1 + dabs(p+s/2.-.25, t/2) * la / PI;
+  }
+  else
+  {
+    double sn = dabs(s, t), L = log(sn/s);
+    alpha = B - 0.39 + L + s*(log2PI - log(sn));
+    beta = (alpha+s)/t - atan(s/t);
+    p = 0;
+    if (beta > 0)
+    {
+      beta = 1.0 - s + t * get_xinf(beta);
+      if (beta > 0) p = (long)ceil(beta / 2.0);
+    }
+    else
+      if (s < 1.0) p = 1;
+    n = p? dabs(s + 2*p-1, t) / (2*PI) : exp((B-LOG2+L) / s);
+  }
+  *pp = p;
+  *pn = (long)ceil(n);
+  if (*pp < 0 || *pn < 0) pari_err_OVERFLOW("zeta");
+  if (DEBUGLEVEL) err_printf("lim, nn: [%ld, %ld]\n", *pp, *pn);
+}
+
+/* 1/zeta(n) using Euler product. Assume n > 0.
+ * if (lba != 0) it is log(prec2nbits) we _really_ require */
+GEN
+inv_szeta_euler(long n, double lba, long prec)
+{
+  GEN z, res;
+  pari_sp av, av2, avlim;
+  double A, D;
+  ulong p, lim;
+  forprime_t S;
+
+  if (n > prec2nbits(prec)) return real_1(prec);
+
+  if (!lba) lba = prec2nbits_mul(prec, LOG2);
+  D = exp((lba - log(n-1)) / (n-1));
+  lim = 1 + (ulong)ceil(D);
+  if (lim < 3) return subir(gen_1,real2n(-n,prec));
+  res = cgetr(prec); incrprec(prec);
+  av = avma; avlim = stack_lim(av, 1);
+  z = subir(gen_1, real2n(-n, prec));
+
+  (void)u_forprime_init(&S, 3, lim);
+  av2 = avma; A = n / LOG2;
+  while ((p = u_forprime_next(&S)))
+  {
+    long l = prec - nbits2extraprec((long)floor(A * log(p)) - BITS_IN_LONG);
+    GEN h;
+
+    if (l < 3)         l = 3;
+    else if (l > prec) l = prec;
+    h = divrr(z, rpowuu(p, (ulong)n, l));
+    z = subrr(z, h);
+    if (low_stack(avlim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"inv_szeta_euler, p = %lu/%lu", p,lim);
+      z = gerepileuptoleaf(av2, z);
+    }
+  }
+  affrr(z, res); avma = av; return res;
+}
+
+/* assume n even > 0, if iz != NULL, assume iz = 1/zeta(n) */
+GEN
+bernreal_using_zeta(long n, GEN iz, long prec)
+{
+  long l = prec+EXTRAPRECWORD;
+  GEN z;
+
+  if (!iz) iz = inv_szeta_euler(n, 0., l);
+  z = divrr(mpfactr(n, l), mulrr(powru(Pi2n(1, l), n), iz));
+  shiftr_inplace(z, 1); /* 2 * n! * zeta(n) / (2Pi)^n */
+  if ((n & 3) == 0) setsigne(z, -1);
+  return z;
+}
+
+/* assume n even > 0. Faster than standard bernfrac for n >= 6 */
+GEN
+bernfrac_using_zeta(long n)
+{
+  pari_sp av = avma;
+  GEN iz, a, d, D = divisorsu(n >> 1);
+  long i, prec, l = lg(D);
+  double t, u;
+
+  d = utoipos(6); /* 2 * 3 */
+  for (i = 2; i < l; i++) /* skip 1 */
+  { /* Clausen - von Staudt */
+    ulong p = 2*D[i] + 1;
+    if (uisprime(p)) d = muliu(d, p);
+  }
+  /* 1.712086 = ??? */
+  t = log( gtodouble(d) ) + (n + 0.5) * log(n) - n*(1+log2PI) + 1.712086;
+  u = t / LOG2; prec = nbits2prec((long)ceil(u) + BITS_IN_LONG);
+  iz = inv_szeta_euler(n, t, prec);
+  a = roundr( mulir(d, bernreal_using_zeta(n, iz, prec)) );
+  return gerepilecopy(av, mkfrac(a, d));
+}
+
+/* n >= k >= 2, y = binomial(n,k-2), as a t_REAL. Return binomial(n,k) */
+static GEN
+next_bin(GEN y, long n, long k)
+{
+  if (n & HIGHMASK)
+  {
+    y = divru(mulru(y, n-k+2), k-1);
+    return divru(mulru(y, n-k+1), k);
+  }
+  return divru(mulru(y, (n-k+2)*(n-k+1)), (k-1)*k);
+}
+
+/* assume k > 1 odd */
+static GEN
+szeta_odd(long k, long prec)
+{
+  long kk, n, li = -(1+prec2nbits(prec));
+  pari_sp av = avma, av2, limit;
+  GEN y, p1, qn, z, q, pi2 = Pi2n(1, prec), binom= real_1(prec+EXTRAPRECWORD);
+
+  q = mpexp(pi2); kk = k+1; /* >= 4 */
+  qn = sqrr(q);
+  y = NULL; /* gcc -Wall */
+  mpbern(kk>>1,prec);
+  if ((k&3)==3)
+  {
+    for (n=0; n <= kk>>1; n+=2)
+    {
+      p1 = mulrr(bernreal(kk-n,prec),bernreal(n,prec));
+      if (n) binom = next_bin(binom,kk,n);
+      p1 = mulrr(binom,p1);
+      if (n == kk>>1) shiftr_inplace(p1, -1);
+      if ((n>>1)&1) togglesign(p1);
+      y = n? addrr(y,p1): p1;
+    }
+    y = mulrr(divrr(powru(pi2,k),mpfactr(kk,prec)),y);
+
+    av2 = avma; limit = stack_lim(av2,1);
+    z = invr( addrs(q,-1) );
+    for (n=2;; n++)
+    {
+      p1 = invr( mulir(powuu(n,k),addrs(qn,-1)) );
+      z = addrr(z,p1); if (expo(p1) < li) break;
+      qn = mulrr(qn,q);
+      if (low_stack(limit,stack_lim(av2,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"szeta, delta = %ld", expo(p1)-li);
+        gerepileall(av2,2, &z,&qn);
+      }
+    }
+    shiftr_inplace(z, 1);
+    y = addrr(y,z); togglesign(y);
+  }
+  else
+  {
+    GEN p2 = divru(pi2, k-1);
+    for (n=0; n <= k>>1; n+=2)
+    {
+      p1 = mulrr(bernreal(kk-n,prec),bernreal(n,prec));
+      if (n) binom = next_bin(binom,kk,n);
+      p1 = mulrr(binom,p1);
+      p1 = mulur(kk-(n<<1),p1);
+      if ((n>>1)&1) togglesign(p1);
+      y = n? addrr(y,p1): p1;
+    }
+    y = mulrr(divrr(powru(pi2,k),mpfactr(kk,prec)),y);
+    y = divru(y,k-1);
+
+    av2 = avma; limit = stack_lim(av2,1);
+    p1 = sqrr(addrs(q,-1));
+    z = divrr(addrs(mulrr(q,addsr(1,mulur(2,p2))),-1),p1);
+    for (n=2;; n++)
+    {
+      p1 = mulir(powuu(n,k),sqrr(addrs(qn,-1)));
+      p1 = divrr(addrs(mulrr(qn,addsr(1,mulur(n<<1,p2))),-1),p1);
+      z = addrr(z,p1); if (expo(p1) < li) break;
+      qn = mulrr(qn,q);
+      if (low_stack(limit,stack_lim(av2,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"szeta, delta = %ld", expo(p1)-li);
+        gerepileall(av2,2, &z,&qn);
+      }
+    }
+    shiftr_inplace(z, 1);
+    y = subrr(y,z);
+  }
+  return gerepileuptoleaf(av, y);
+}
+
+static int
+bernreal_use_zeta(long k, long prec)
+{
+  if (bernzone && (k>>1)+1 < lg(bernzone))
+  {
+    GEN B = gel(bernzone,(k>>1)+1);
+    if (typ(B) != t_REAL || realprec(B) >= prec) return 0;
+  }
+  return (k * (log(k) - 2.83) > prec2nbits_mul(prec, LOG2));
+}
+
+/* Return B_n */
+GEN
+bernreal(long n, long prec)
+{
+  GEN B, storeB;
+  long k, lbern;
+  if (n < 0) pari_err_DOMAIN("bernreal", "index", "<", gen_0, stoi(n));
+  if (n == 0) return real_1(prec);
+  if (n == 1) return real_m2n(-1,prec); /*-1/2*/
+  if (odd(n)) return real_0(prec);
+
+  k = n >> 1;
+  if (!bernzone && k < 100) mpbern(k, prec);
+  lbern = bernzone? lg(bernzone): 0;
+  if (k < lbern)
+  {
+    B = gel(bernzone,k);
+    if (typ(B) != t_REAL) return fractor(B, prec);
+    if (realprec(B) >= prec) return rtor(B, prec);
+  }
+  /* not cached, must compute */
+  if (n * log(n) > prec2nbits_mul(prec, LOG2))
+    B = storeB = bernreal_using_zeta(n, NULL, prec);
+  else
+  {
+    storeB = bernfrac_using_zeta(n);
+    B = fractor(storeB, prec);
+  }
+  if (k < lbern)
+  {
+    GEN old = gel(bernzone, k);
+    gel(bernzone, k) = gclone(storeB);
+    gunclone(old);
+  }
+  return B;
+}
+
+/* assume k != 1 */
+GEN
+szeta(long k, long prec)
+{
+  pari_sp av = avma;
+  GEN y;
+
+  /* treat trivial cases */
+  if (!k) { y = real2n(-1, prec); setsigne(y,-1); return y; }
+  if (k < 0)
+  {
+    if ((k&1) == 0) return gen_0;
+    /* the one value such that k < 0 and 1 - k < 0, due to overflow */
+    if ((ulong)k == (HIGHBIT | 1))
+      pari_err_OVERFLOW("zeta [large negative argument]");
+    k = 1-k;
+    y = bernreal(k, prec); togglesign(y);
+    return gerepileuptoleaf(av, divru(y, k));
+  }
+  if (k > prec2nbits(prec)+1) return real_1(prec);
+  if ((k&1) == 0)
+  {
+    if (bernreal_use_zeta(k, prec))
+      y = invr( inv_szeta_euler(k, 0, prec) );
+    else
+    {
+      y = mulrr(powru(Pi2n(1, prec), k), bernreal(k, prec));
+      y = divrr(y, mpfactr(k,prec));
+      setsigne(y, 1);
+      shiftr_inplace(y, -1);
+    }
+    return gerepileuptoleaf(av, y);
+  }
+  /* k > 1 odd */
+  if (k * log(k) > prec2nbits_mul(prec, LOG2)) /* heuristic */
+    return gerepileuptoleaf(av, invr( inv_szeta_euler(k, 0, prec) ));
+  return szeta_odd(k, prec);
+}
+
+/* return n^-s, n > 1 odd. tab[q] := q^-s, q prime power */
+static GEN
+n_s(ulong n, GEN *tab)
+{
+  GEN x, f = factoru(n), P = gel(f,1), E = gel(f,2);
+  long i, l = lg(P);
+
+  x = tab[ upowuu(P[1], E[1]) ];
+  for (i = 2; i < l; i++) x = gmul(x, tab[ upowuu(P[i], E[i]) ]);
+  return x;
+}
+
+/* s0 a t_INT, t_REAL or t_COMPLEX.
+ * If a t_INT, assume it's not a trivial case (i.e we have s0 > 1, odd)
+ * */
+GEN
+czeta(GEN s0, long prec)
+{
+  GEN s, u, a, y, res, tes, sig, tau, invn2, unr;
+  GEN sim, *tab, tabn, funeq_factor = NULL;
+  ulong p, sqn;
+  long i, nn, lim, lim2, ct;
+  pari_sp av0 = avma, av, av2, avlim;
+  pari_timer T;
+  forprime_t S;
+
+  if (DEBUGLEVEL>2) timer_start(&T);
+  s = trans_fix_arg(&prec,&s0,&sig,&tau,&av,&res);
+  if (typ(s0) == t_INT) return gerepileupto(av, gzeta(s0, prec));
+  if (!signe(tau)) /* real */
+  {
+    if (signe(sig) <= 0 || expo(sig) < -1)
+    { /* s < 1/2 */
+      s = subsr(1, s);
+      funeq_factor = gen_1;
+    }
+  }
+  else
+  {
+    u = gsubsg(1, s); /* temp */
+    if (gexpo(u) < -5 || ((signe(sig) <= 0 || expo(sig) < -1) && gexpo(s) > -5))
+    { /* |1-s| < 1/32  || (real(s) < 1/2 && |imag(s)| > 1/32) */
+      s = u;
+      funeq_factor = gen_1;
+    }
+  }
+
+  if (funeq_factor)
+  { /* s <--> 1-s */
+    GEN t;
+    sig = real_i(s);
+    /* Gamma(s) (2Pi)^-s 2 cos(Pi s/2) */
+    t = gmul(ggamma(gprec_w(s,prec),prec), gpow(Pi2n(1,prec), gneg(s), prec));
+    funeq_factor = gmul2n(gmul(t, gcos(gmul(Pi2n(-1,prec),s), prec)), 1);
+  }
+  if (gcmpgs(sig, prec2nbits(prec) + 1) > 0) { /* zeta(s) = 1 */
+    if (!funeq_factor) { avma = av0; return real_1(prec); }
+    return gerepileupto(av0, funeq_factor);
+  }
+  optim_zeta(s, prec, &lim, &nn);
+  u_forprime_init(&S, 2, nn-1);
+  incrprec(prec); unr = real_1(prec); /* one extra word of precision */
+
+  tab = (GEN*)cgetg(nn, t_VEC); /* table of q^(-s), q = p^e */
+  { /* general case */
+    GEN ms = gneg(s), rp = cgetr(prec);
+    while ((p = u_forprime_next(&S)))
+    {
+      affur(p, rp);
+      tab[p] = gexp(gmul(ms, mplog(rp)), prec);
+    }
+    affsr(nn, rp);
+    a = gexp(gmul(ms, mplog(rp)), prec);
+  }
+  sqn = (ulong)sqrt(nn-1.);
+  u_forprime_init(&S, 3, sqn); /* fill in odd prime powers */
+  while ((p = u_forprime_next(&S)))
+  {
+    ulong oldq = p, q = p*p;
+    while (q<(ulong)nn) { tab[q] = gmul(tab[p], tab[oldq]); oldq = q; q *= p; }
+  }
+  if (DEBUGLEVEL>2) timer_printf(&T,"tab[q^-s] from 1 to N-1");
+
+  tabn = cgetg(nn, t_VECSMALL); ct = 0;
+  for (i = nn-1; i; i>>=1) tabn[++ct] = (i-1)>>1;
+  sim = y = unr;
+  /* compute 1 + 2^-s + ... + n^-s = P(2^-s) using Horner's scheme */
+  for (i=ct; i > 1; i--)
+  {
+    long j;
+    av2 = avma;
+    for (j=tabn[i]+1; j<=tabn[i-1]; j++)
+      sim = gadd(sim, n_s(2*j+1, tab));
+    sim = gerepileupto(av2, sim);
+    y = gadd(sim, gmul(tab[2],y));
+  }
+  y = gadd(y, gmul2n(a,-1));
+  if (DEBUGLEVEL>2) timer_printf(&T,"sum from 1 to N-1");
+
+  invn2 = divri(unr, mulss(nn,nn)); lim2 = lim<<1;
+  mpbern(lim,prec);
+  tes = bernreal(lim2, prec);
+  {
+    GEN s1, s2, s3, s4, s5;
+    s1 = gsub(gmul2n(s,1), unr);
+    s2 = gmul(s, gsub(s,unr));
+    s3 = gmul2n(invn2,3);
+    av2 = avma; avlim = stack_lim(av2,3);
+    s4 = gmul(invn2, gmul2n(gaddsg(4*lim-2,s1),1));
+    s5 = gmul(invn2, gadd(s2, gmulsg(lim2, gaddgs(s1, lim2))));
+    for (i = lim2-2; i>=2; i -= 2)
+    {
+      s5 = gsub(s5, s4);
+      s4 = gsub(s4, s3);
+      tes = gadd(bernreal(i,prec), divgunu(gmul(s5,tes), i+1));
+      if (low_stack(avlim,stack_lim(av2,3)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"czeta");
+        gerepileall(av2,3, &tes,&s5,&s4);
+      }
+    }
+    u = gmul(gmul(tes,invn2), gmul2n(s2, -1));
+    tes = gmulsg(nn, gaddsg(1, u));
+  }
+  if (DEBUGLEVEL>2) timer_printf(&T,"Bernoulli sum");
+  /* y += tes n^(-s) / (s-1) */
+  y = gadd(y, gmul(tes, gdiv(a, gsub(s, unr))));
+  if (funeq_factor) y = gmul(y, funeq_factor);
+  avma = av; return affc_fixlg(y,res);
+}
+
+/* return P mod x^n where P is polynomial in x */
+static GEN
+pol_mod_xn(GEN P, long n)
+{
+  long j, l = lg(P), N = n+2;
+  GEN R;
+  if (l > N) l = N;
+  R = cgetg(N, t_POL); R[1] = evalvarn(0);
+  for (j = 2; j < l; j++) gel(R,j) = gel(P,j);
+  return normalizepol_lg(R, n+2);
+}
+
+/* compute the values of the twisted partial
+   zeta function Z_f(a, c, s) for a in va */
+GEN
+twistpartialzeta(GEN q, long f, long c, GEN va, GEN cff)
+{
+  long j, k, lva = lg(va)-1, N = lg(cff)-1;
+  pari_sp av, av2, lim;
+  GEN Ax, Cx, Bx, Dx, x = pol_x(0), y = pol_x(fetch_user_var("y"));
+  GEN cyc, psm, rep, eta, etaf;
+
+  cyc = gdiv(gsubgs(gpowgs(y, c), 1), gsubgs(y, 1));
+  psm = polsym(cyc, degpol(cyc) - 1);
+  eta = mkpolmod(y, cyc);
+  etaf = gpowgs(eta,f);
+  av = avma;
+  Ax  = gsubgs(gpowgs(gaddgs(x, 1), f), 1);
+  Ax  = gdiv(gmul(Ax, etaf), gsubsg(1, etaf));
+  Ax  = gerepileupto(av, RgX_to_FqX(Ax, cyc, q));
+  Cx  = Ax;
+  Bx  = gen_1;
+  av  = avma; lim = stack_lim(av, 1);
+  for (j = 2; j <= N; j++)
+  {
+    Bx = gadd(Bx, Cx);
+    Bx = FpXQX_red(Bx, cyc, q);
+    Cx = FpXQX_mul(Cx, Ax, cyc, q);
+    Cx = pol_mod_xn(Cx, N);
+    if (gequal0(Cx)) break;
+    if (low_stack(lim, stack_lim(av, 1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem, "twistpartialzeta (1), j = %ld/%ld", j, N);
+      gerepileall(av, 2, &Cx, &Bx);
+    }
+  }
+  Bx  = lift(gmul(ginv(gsubsg(1, etaf)), Bx));
+  Bx  = gerepileupto(av, RgX_to_FqX(Bx, cyc, q));
+  Cx = lift(gmul(eta, gaddsg(1, x)));
+  Dx = pol_1(varn(x));
+  av2 = avma; lim = stack_lim(av2, 1);
+  for (j = lva; j > 1; j--)
+  {
+    GEN Ex;
+    long e = va[j] - va[j-1];
+    if (e == 1)
+      Ex = Cx;
+    else
+      /* e is very small in general and actually very rarely different
+         to 1, it is always 1 for zetap (so it should be OK not to store
+         them or to compute them in a smart way) */
+      Ex = gpowgs(Cx, e);
+    Dx = gaddsg(1, FpXQX_mul(Dx, Ex, cyc, q));
+    if (low_stack(lim, stack_lim(av2, 1)))
+    {
+      if(DEBUGMEM>1)
+        pari_warn(warnmem, "twistpartialzeta (2), j = %ld/%ld", lva-j, lva);
+      Dx = gerepileupto(av2, FpXQX_red(Dx, cyc, q));
+    }
+  }
+  Dx = FpXQX_mul(Dx, Cx, cyc, q); /* va[1] = 1 */
+  Bx = gerepileupto(av, FpXQX_mul(Dx, Bx, cyc, q));
+  rep = gen_0;
+  av2 = avma; lim = stack_lim(av2, 1);
+  for (k = 1; k <= N; k++)
+  {
+    GEN p2, ak = polcoeff_i(Bx, k, 0);
+    p2  = quicktrace(ak, psm);
+    rep = modii(addii(rep, mulii(gel(cff, k), p2)), q);
+    if (low_stack(lim, stack_lim(av2, 1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem, "twistpartialzeta (3), j = %ld/%ld", k, N);
+      rep = gerepileupto(av2, rep);
+    }
+  }
+  return rep;
+}
+
+#if 0
+/* initialize the roots of unity for the computation
+   of the Teichmuller character (also the values of f and c) */
+GEN
+init_teich(ulong p, GEN q, long prec)
+{
+  GEN vz, gp = utoipos(p);
+  pari_sp av = avma;
+  long j;
+
+  if (p == 2UL)
+    return NULL;
+  else
+  { /* primitive (p-1)-th root of 1 */
+    GEN z, z0 = Zp_sqrtnlift(gen_1, utoipos(p-1), pgener_Fp(gp), gp, prec);
+    z = z0;
+    vz = cgetg(p, t_VEC);
+    for (j = 1; j < (long)p-2; j++)
+    {
+      gel(vz, umodiu(z, p)) = z; /* z = z0^i */
+      z = modii(mulii(z, z0), q);
+    }
+    gel(vz, umodiu(z, p)) = z; /* z = z0^(p-2) */
+    gel(vz,1) = gen_1; /* z0^(p-1) */
+  }
+  return gerepileupto(av, gcopy(vz));
+}
+
+/* compute phi^(m)_s(x); s must be an integer */
+GEN
+phi_ms(ulong p, GEN q, long m, GEN s, long x, GEN vz)
+{
+  long xp = x % p;
+  GEN p1, p2;
+
+  if (!xp) return gen_0;
+  if (vz)
+    p1 =gel(vz,xp); /* vz[x] = Teichmuller(x) */
+  else
+    p1 = (x & 2)? gen_m1: gen_1;
+  p1 = Fp_pow(p1, addis(s, m), q);
+  p2 = Fp_pow(stoi(x), negi(s), q);
+  return modii(mulii(p1,p2), q);
+}
+
+/* compute the first N coefficients of the Mahler expansion
+   of phi^m_s skipping the first one (which is zero) */
+GEN
+coeff_of_phi_ms(ulong p, GEN q, long m, GEN s, long N, GEN vz)
+{
+  GEN qs2 = shifti(q, -1), cff = zerovec(N);
+  pari_sp av, lim;
+  long k, j;
+
+  av = avma; lim = stack_lim(av, 2);
+  for (k = 1; k <= N; k++)
+  {
+    gel(cff, k) = phi_ms(p, q, m, s, k, vz);
+    if (low_stack(lim, stack_lim(av, 2)))
+    {
+      if(DEBUGMEM>1)
+        pari_warn(warnmem, "coeff_of_phi_ms (1), k = %ld/%ld", N-k, N);
+      cff = gerepileupto(av, gcopy(cff));
+    }
+  }
+  for (j = N; j > 1; j--)
+  {
+    GEN b = subii(gel(cff, j), gel(cff, j-1));
+    gel(cff, j) = centermodii(b, q, qs2);
+    if (low_stack(lim, stack_lim(av, 2)))
+    {
+      if(DEBUGMEM>1)
+        pari_warn(warnmem, "coeff_of_phi_ms (2), j = %ld/%ld", N-j, N);
+      cff = gerepileupto(av, gcopy(cff));
+    }
+  }
+  for (k = 1; k < N; k++)
+    for (j = N; j > k; j--)
+    {
+      GEN b = subii(gel(cff, j), gel(cff, j-1));
+      gel(cff, j) = centermodii(b, q, qs2);
+      if (low_stack(lim, stack_lim(av, 2)))
+      {
+        if(DEBUGMEM>1)
+          pari_warn(warnmem, "coeff_of_phi_ms (3), (k,j) = (%ld,%ld)/%ld",
+              k, N-j, N);
+        cff = gerepileupto(av, gcopy(cff));
+      }
+    }
+  k = N; while(gequal0(gel(cff, k))) k--;
+  setlg(cff, k+1);
+  if (DEBUGLEVEL > 2)
+    err_printf("  coeff_of_phi_ms: %ld coefficients kept out of %ld\n",
+               k, N);
+  return gerepileupto(av, cff);
+}
+
+static long
+valfact(long N, ulong p)
+{
+  long f = 0;
+  while (N > 1)
+  {
+    N /= p;
+    f += N;
+  }
+  return f;
+}
+
+static long
+number_of_terms(ulong p, long prec)
+{
+  long N, f;
+
+  if (prec == 0) return p;
+  N = (long)((p-1)*prec + (p>>1)*(log2(prec)/log2(p)));
+  N = p*(N/p);
+  f = valfact(N, p);
+  while (f > prec)
+  {
+    N = p*(N/p) - 1;
+    f -= u_lval(N+1, p);
+  }
+  while (f < prec)
+  {
+    N = p*(N/p+1);
+    f += u_lval(N, p);
+  }
+  return N;
+}
+
+static GEN
+zetap(GEN s)
+{
+  ulong p;
+  long N, f, c, prec = precp(s);
+  pari_sp av = avma;
+  GEN gp, q, vz, is, cff, val, va, cft;
+
+  if (valp(s) < 0) pari_err_DOMAIN("zetap", "v_p(s)", "<", gen_0, s);
+  if (!prec) prec = 1;
+
+  gp = gel(s,2); p = itou(gp);
+  is = gtrunc(s);  /* make s an integer */
+
+  N  = number_of_terms(p, prec);
+  q  = powiu(gp, prec);
+
+  /* initialize the roots of unity for the computation
+     of the Teichmuller character (also the values of f and c) */
+  if (DEBUGLEVEL > 1) err_printf("zetap: computing (p-1)th roots of 1\n");
+  vz = init_teich(p, q, prec);
+  if (p == 2UL) {  f = 4; c = 3; } else { f = (long)p; c = 2; }
+
+  /* compute the first N coefficients of the Mahler expansion
+     of phi^(-1)_s skipping the first one (which is zero) */
+  if (DEBUGLEVEL > 1)
+    err_printf("zetap: computing Mahler expansion of phi^(-1)_s\n");
+  cff = coeff_of_phi_ms(p, q, -1, is, N, vz);
+
+  /* compute the coefficients of the power series corresponding
+     to the twisted partial zeta function Z_f(a, c, s) for a in va */
+  /* The line below looks a bit stupid but it is to keep the
+     possibility of later adding p-adic Dirichlet L-functions */
+  va = identity_perm(f - 1);
+  if (DEBUGLEVEL > 1)
+    err_printf("zetap: computing values of twisted partial zeta functions\n");
+  val = twistpartialzeta(q, f, c, va, cff);
+
+  /* sum over all a's the coefficients of the twisted
+     partial zeta functions and integrate */
+  if (DEBUGLEVEL > 1)
+    err_printf("zetap: multiplying by correcting factor\n");
+
+  /* multiply by the corrective factor */
+  cft = gsubgs(gmulsg(c, phi_ms(p, q, -1, is, c, vz)), 1);
+  val = gdiv(val, cft);
+
+  /* adjust the precision and return */
+  return gerepileupto(av, cvtop(val, gp, prec));
+}
+#else
+static GEN
+hurwitz_p(GEN cache, GEN s, GEN x, GEN p, long prec)
+{
+  GEN S, x2, x2j, s_1 = gsubgs(s,1);
+  long j, J = lg(cache)-2;
+  x = ginv(gadd(x, zeropadic(p, prec)));
+  x2 = gsqr(x);
+  S = gmul2n(gmul(s_1, x), -1);
+  x2j = gen_1;
+  for (j = 0;; j++)
+  {
+    S = gadd(S, gmul(gel(cache, j+1), x2j));
+    if (j == J) break;
+    x2j = gmul(x2, x2j);
+  }
+  return gmul(gdiv(S, s_1), Qp_exp(gmul(s_1, Qp_log(x))));
+}
+
+static GEN
+init_cache(long J, GEN s)
+{
+  GEN C = gen_1, cache = bernvec(J);
+  long j;
+
+  for (j = 1; j <= J; j++)
+  { /* B_{2j} * binomial(1-s, 2j) */
+    GEN t = gmul(gaddgs(s, 2*j-3), gaddgs(s, 2*j-2));
+    C = gdiv(gmul(C, t), mulss(2*j, 2*j-1));
+    gel(cache, j+1) = gmul(gel(cache, j+1), C);
+  }
+  return cache;
+}
+
+static GEN
+zetap(GEN s)
+{
+  pari_sp av = avma;
+  GEN cache, S, gp = gel(s,2);
+  ulong a, p = itou(gp);
+  long J, prec = valp(s) + precp(s);
+
+  if (prec <= 0) prec = 1;
+  if (p == 2) {
+    J = ((long)(1+ceil((prec+1.)/2))) >> 1;
+    cache = init_cache(J, s);
+    S = gmul2n(hurwitz_p(cache, s, gmul2n(gen_1, -2), gen_2, prec), -1);
+  } else {
+    J = (prec+2) >> 1;
+    cache = init_cache(J, s);
+    S = gen_0;
+    for (a = 1; a <= (p-1)>>1; a++)
+      S = gadd(S, hurwitz_p(cache, s, gdivsg(a, gp), gp, prec));
+    S = gdiv(gmul2n(S, 1), gp);
+  }
+  return gerepileupto(av, S);
+}
+#endif
+
+GEN
+gzeta(GEN x, long prec)
+{
+  if (gequal1(x)) pari_err_DOMAIN("zeta", "argument", "=", gen_1, x);
+  switch(typ(x))
+  {
+    case t_INT:
+      if (is_bigint(x))
+      {
+        if (signe(x) > 0) return real_1(prec);
+        if (signe(x) < 0 && mod2(x) == 0) return real_0(prec);
+        pari_err_OVERFLOW("zeta [large negative argument]");
+      }
+      return szeta(itos(x),prec);
+    case t_REAL: case t_COMPLEX: return czeta(x,prec);
+    case t_PADIC: return zetap(x);
+    case t_SER: pari_err_IMPL("zeta(t_SER)");
+  }
+  return trans_eval("zeta",gzeta,x,prec);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                    FONCTIONS POLYLOGARITHME                       **/
+/**                                                                   **/
+/***********************************************************************/
+
+/* returns H_n = 1 + 1/2 + ... + 1/n, as a rational number (n "small") */
+static GEN
+Harmonic(long n)
+{
+  GEN h = gen_1;
+  long i;
+  for (i=2; i<=n; i++) h = gadd(h, mkfrac(gen_1, utoipos(i)));
+  return h;
+}
+
+/* m >= 2. Validity domain contains | log |x| | < 5, best for |x| ~ 1.
+ * Li_m(x = e^z) = sum_{n >= 0} zeta(m-n) z^n / n!
+ *    with zeta(1) := H_m - log(-z) */
+static GEN
+cxpolylog(long m, GEN x, long prec)
+{
+  long li, n;
+  GEN z, h, q, s;
+  int real;
+
+  if (gequal1(x)) return szeta(m,prec);
+  /* x real <= 1 ==> Li_m(x) real */
+  real = (typ(x) == t_REAL && (expo(x) < 0 || signe(x) <= 0));
+
+  z = glog(x,prec);
+  /* n = 0 */
+  q = gen_1;
+  s = szeta(m,prec);
+  for (n=1; n < m-1; n++)
+  {
+    q = gdivgs(gmul(q,z),n);
+    s = gadd(s, gmul(szeta(m-n,prec), real? real_i(q): q));
+  }
+  /* n = m-1 */
+    q = gdivgs(gmul(q,z),n); /* multiply by "zeta(1)" */
+    h = gmul(q, gsub(Harmonic(m-1), glog(gneg_i(z),prec)));
+    s = gadd(s, real? real_i(h): h);
+  /* n = m */
+    q = gdivgs(gmul(q,z),m);
+    s = gadd(s, gmul(szeta(0,prec), real? real_i(q): q));
+  /* n = m+1 */
+    q = gdivgs(gmul(q,z),m+1);
+    s = gadd(s, gmul(szeta(-1,prec), real? real_i(q): q));
+
+  z = gsqr(z); li = -(prec2nbits(prec)+1);
+  /* n = m+3, m+5, ...; note that zeta(- even integer) = 0 */
+  for(n = m+3;; n += 2)
+  {
+    GEN zet = szeta(m-n,prec);
+    q = divgunu(gmul(q,z), n-1);
+    s = gadd(s, gmul(zet, real? real_i(q): q));
+    if (gexpo(q) + expo(zet) < li) break;
+  }
+  return s;
+}
+
+static GEN
+polylog(long m, GEN x, long prec)
+{
+  long l, e, i, G, sx;
+  pari_sp av, av1, limpile;
+  GEN X, Xn, z, p1, p2, y, res;
+
+  if (m < 0) pari_err_DOMAIN("polylog", "index", "<", gen_0, stoi(m));
+  if (!m) return mkfrac(gen_m1,gen_2);
+  if (gequal0(x)) return gcopy(x);
+  if (m==1)
+  {
+    av = avma;
+    return gerepileupto(av, gneg(glog(gsub(gen_1,x), prec)));
+  }
+
+  l = precision(x); if (!l) l = prec;
+  res = cgetc(l); av = avma;
+  x = gtofp(x, l+EXTRAPRECWORD);
+  e = gexpo(gnorm(x));
+  if (!e || e == -1) {
+    y = cxpolylog(m,x,prec);
+    avma = av; return affc_fixlg(y, res);
+  }
+  X = (e > 0)? ginv(x): x;
+  G = -prec2nbits(l);
+  av1 = avma; limpile = stack_lim(av1,1);
+  y = Xn = X;
+  for (i=2; ; i++)
+  {
+    Xn = gmul(X,Xn); p2 = gdiv(Xn,powuu(i,m));
+    y = gadd(y,p2);
+    if (gexpo(p2) <= G) break;
+
+    if (low_stack(limpile, stack_lim(av1,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"polylog");
+      gerepileall(av1,2, &y, &Xn);
+    }
+  }
+  if (e < 0) { avma = av; return affc_fixlg(y, res); }
+
+  sx = gsigne(imag_i(x));
+  if (!sx)
+  {
+    if (m&1) sx = gsigne(gsub(gen_1, real_i(x)));
+    else     sx = - gsigne(real_i(x));
+  }
+  z = divri(mppi(l), mpfact(m-1)); setsigne(z, sx);
+  z = mkcomplex(gen_0, z);
+
+  if (m == 2)
+  { /* same formula as below, written more efficiently */
+    y = gneg_i(y);
+    if (typ(x) == t_REAL && signe(x) < 0)
+      p1 = logr_abs(x);
+    else
+      p1 = gsub(glog(x,l), z);
+    p1 = gmul2n(gsqr(p1), -1); /* = (log(-x))^2 / 2 */
+
+    p1 = gadd(p1, divru(sqrr(mppi(l)), 6));
+    p1 = gneg_i(p1);
+  }
+  else
+  {
+    GEN logx = glog(x,l), logx2 = gsqr(logx);
+    p1 = mkfrac(gen_m1,gen_2);
+    for (i=m-2; i>=0; i-=2)
+      p1 = gadd(szeta(m-i,l), gmul(p1,gdivgs(logx2,(i+1)*(i+2))));
+    if (m&1) p1 = gmul(logx,p1); else y = gneg_i(y);
+    p1 = gadd(gmul2n(p1,1), gmul(z,gpowgs(logx,m-1)));
+    if (typ(x) == t_REAL && signe(x) < 0) p1 = real_i(p1);
+  }
+  y = gadd(y,p1);
+  avma = av; return affc_fixlg(y, res);
+}
+
+GEN
+dilog(GEN x, long prec)
+{
+  return gpolylog(2, x, prec);
+}
+
+/* x a floating point number, t_REAL or t_COMPLEX of t_REAL */
+static GEN
+logabs(GEN x)
+{
+  GEN y;
+  if (typ(x) == t_COMPLEX)
+  {
+    y = logr_abs( cxnorm(x) );
+    shiftr_inplace(y, -1);
+  } else
+    y = logr_abs(x);
+  return y;
+}
+
+static GEN
+polylogD(long m, GEN x, long flag, long prec)
+{
+  long k, l, fl, m2;
+  pari_sp av;
+  GEN p1, p2, y;
+
+  if (gequal0(x)) return gcopy(x);
+  m2 = m&1;
+  if (gequal1(x) && m>=2) return m2? szeta(m,prec): gen_0;
+  av = avma; l = precision(x);
+  if (!l) { l = prec; x = gtofp(x,l); }
+  p1 = logabs(x);
+  k = signe(p1);
+  if (k > 0) { x = ginv(x); fl = !m2; } else { setabssign(p1); fl = 0; }
+  /* |x| <= 1, p1 = - log|x| >= 0 */
+  p2 = gen_1;
+  y = polylog(m,x,l);
+  y = m2? real_i(y): imag_i(y);
+  for (k=1; k<m; k++)
+  {
+    GEN t = polylog(m-k,x,l);
+    p2 = gdivgs(gmul(p2,p1), k); /* (-log|x|)^k / k! */
+    y = gadd(y, gmul(p2, m2? real_i(t): imag_i(t)));
+  }
+  if (m2)
+  {
+    if (!flag) p1 = negr( logabs(gsubsg(1,x)) ); else p1 = shiftr(p1,-1);
+    p2 = gdivgs(gmul(p2,p1), -m);
+    y = gadd(y, p2);
+  }
+  if (fl) y = gneg(y);
+  return gerepileupto(av, y);
+}
+
+static GEN
+polylogP(long m, GEN x, long prec)
+{
+  long k, l, fl, m2;
+  pari_sp av;
+  GEN p1,y;
+
+  if (gequal0(x)) return gcopy(x);
+  m2 = m&1;
+  if (gequal1(x) && m>=2) return m2? szeta(m,prec): gen_0;
+  av = avma; l = precision(x);
+  if (!l) { l = prec; x = gtofp(x,l); }
+  p1 = logabs(x);
+  if (signe(p1) > 0) { x = ginv(x); fl = !m2; setsigne(p1, -1); } else fl = 0;
+  /* |x| <= 1 */
+  y = polylog(m,x,l);
+  y = m2? real_i(y): imag_i(y);
+  if (m==1)
+  {
+    shiftr_inplace(p1, -1); /* log |x| / 2 */
+    y = gadd(y, p1);
+  }
+  else
+  { /* m >= 2, \sum_{0 <= k <= m} 2^k B_k/k! (log |x|)^k Li_{m-k}(x),
+       with Li_0(x) := -1/2 */
+    GEN u, t;
+    t = polylog(m-1,x,l);
+    u = gneg_i(p1); /* u = 2 B1 log |x| */
+    y = gadd(y, gmul(u, m2?real_i(t):imag_i(t)));
+    if (m > 2)
+    {
+      GEN p2;
+      shiftr_inplace(p1, 1); /* p1 = 2log|x| <= 0 */
+      mpbern(m>>1, l);
+      p1 = sqrr(p1);
+      p2 = shiftr(p1,-1);
+      for (k=2; k<m; k+=2)
+      {
+        if (k > 2) p2 = divgunu(gmul(p2,p1),k-1);
+        /* p2 = 2^k/k! log^k |x|*/
+        t = polylog(m-k,x,l);
+        u = gmul(p2, bernreal(k, prec));
+        y = gadd(y, gmul(u, m2?real_i(t):imag_i(t)));
+      }
+    }
+  }
+  if (fl) y = gneg(y);
+  return gerepileupto(av, y);
+}
+
+GEN
+gpolylog(long m, GEN x, long prec)
+{
+  long i, n, v;
+  pari_sp av = avma;
+  GEN a, y, p1;
+
+  if (m <= 0)
+  {
+    GEN t = mkpoln(2, gen_m1, gen_1); /* 1 - X */
+    p1 = pol_x(0);
+    for (i=2; i <= -m; i++)
+      p1 = RgX_shift_shallow(gadd(gmul(t,ZX_deriv(p1)), gmulsg(i,p1)), 1);
+    p1 = gdiv(p1, gpowgs(t,1-m));
+    return gerepileupto(av, poleval(p1,x));
+  }
+
+  switch(typ(x))
+  {
+    case t_INT: case t_REAL: case t_FRAC: case t_COMPLEX: case t_QUAD:
+      return polylog(m,x,prec);
+    case t_POLMOD:
+      return gerepileupto(av, polylogvec(m, polmod_to_embed(x, prec), prec));
+    case t_INTMOD: case t_PADIC: pari_err_IMPL( "padic polylogarithm");
+    case t_VEC: case t_COL: case t_MAT:
+      return polylogvec(m, x, prec);
+    default:
+      av = avma; if (!(y = toser_i(x))) break;
+      if (!m) { avma = av; return mkfrac(gen_m1,gen_2); }
+      if (m==1) return gerepileupto(av, gneg( glog(gsub(gen_1,y),prec) ));
+      if (gequal0(y)) return gerepilecopy(av, y);
+      v = valp(y);
+      if (v < 0) pari_err_DOMAIN("polylog","valuation", "<", gen_0, x);
+      if (v > 0) {
+        n = (lg(y)-3 + v) / v;
+        a = zeroser(varn(y), lg(y)-2);
+        for (i=n; i>=1; i--)
+          a = gmul(y, gadd(a, powis(utoipos(i),-m)));
+      } else { /* v == 0 */
+        long vy = varn(y);
+        GEN a0 = polcoeff0(y, 0, -1), yprimeovery = gdiv(derivser(y), y);
+        a = gneg( glog(gsub(gen_1,y), prec) );
+        for (i=2; i<=m; i++)
+          a = gadd(gpolylog(i, a0, prec), integ(gmul(yprimeovery, a), vy));
+      }
+      return gerepileupto(av, a);
+  }
+  pari_err_TYPE("gpolylog",x);
+  return NULL; /* not reached */
+}
+
+GEN
+polylog0(long m, GEN x, long flag, long prec)
+{
+  switch(flag)
+  {
+    case 0: return gpolylog(m,x,prec);
+    case 1: return polylogD(m,x,0,prec);
+    case 2: return polylogD(m,x,1,prec);
+    case 3: return polylogP(m,x,prec);
+    default: pari_err_FLAG("polylog");
+  }
+  return NULL; /* not reached */
+}
+
+static GEN
+upper_half(GEN x, long *prec)
+{
+  long tx = typ(x), l;
+  if (tx == t_QUAD) { x = quadtofp(x, *prec); tx = typ(x); }
+  switch(tx)
+  {
+    case t_COMPLEX:
+      if (gsigne(gel(x,2)) > 0) break; /*fall through*/
+    case t_REAL: case t_INT: case t_FRAC:
+      pari_err_DOMAIN("modular function", "Im(argument)", "<=", gen_0, x);
+    default:
+      pari_err_TYPE("modular function", x);
+  }
+  l = precision(x); if (l) *prec = l;
+  return x;
+}
+
+/* sqrt(3)/2 */
+static GEN
+sqrt32(long prec) { GEN z = sqrtr_abs(stor(3,prec)); setexpo(z, -1); return z; }
+/* exp(i x), x = k pi/12 */
+static GEN
+e12(ulong k, long prec)
+{
+  int s, sPi, sPiov2;
+  GEN z, t;
+  k %= 24;
+  if (!k) return gen_1;
+  if (k == 12) return gen_m1;
+  if (k >12) { s = 1; k = 24 - k; } else s = 0; /* x -> 2pi - x */
+  if (k > 6) { sPi = 1; k = 12 - k; } else sPi = 0; /* x -> pi  - x */
+  if (k > 3) { sPiov2 = 1; k = 6 - k; } else sPiov2 = 0; /* x -> pi/2 - x */
+  z = cgetg(3, t_COMPLEX);
+  switch(k)
+  {
+    case 0: gel(z,1) = icopy(gen_1); gel(z,2) = gen_0; break;
+    case 1: t = gmul2n(addrs(sqrt32(prec), 1), -1);
+      gel(z,1) = sqrtr(t);
+      gel(z,2) = gmul2n(invr(gel(z,1)), -2); break;
+
+    case 2: gel(z,1) = sqrt32(prec);
+            gel(z,2) = real2n(-1, prec); break;
+
+    case 3: gel(z,1) = sqrtr_abs(real2n(-1,prec));
+            gel(z,2) = rcopy(gel(z,1)); break;
+  }
+  if (sPiov2) swap(gel(z,1), gel(z,2));
+  if (sPi) togglesign(gel(z,1));
+  if (s)   togglesign(gel(z,2));
+  return z;
+}
+/* z a t_FRAC */
+static GEN
+eiPi_frac(GEN z, long prec)
+{
+  GEN n, d;
+  ulong q, r;
+  n = gel(z,1);
+  d = gel(z,2);
+  q = udivui_rem(12, d, &r);
+  if (!r) /* relatively frequent case */
+    return e12(q * umodiu(n, 24), prec);
+  n = centermodii(n, shifti(d,1), d);
+  return expIr(divri(mulri(mppi(prec), n), d));
+}
+/* exp(i Pi z), z a t_INT or t_FRAC */
+static GEN
+exp_IPiQ(GEN z, long prec)
+{
+  if (typ(z) == t_INT) return mpodd(z)? gen_m1: gen_1;
+  return eiPi_frac(z, prec);
+}
+/* z a t_COMPLEX */
+static GEN
+exp_IPiC(GEN z, long prec)
+{
+  GEN r, x = gel(z,1), y = gel(z,2);
+  GEN pi, mpi = mppi(prec);
+  togglesign(mpi); /* mpi = -Pi */
+  r = gexp(gmul(mpi, y), prec);
+  switch(typ(x))
+  {
+    case t_INT:
+      if (mpodd(x)) togglesign(r);
+      return r;
+    case t_FRAC:
+      return gmul(r, eiPi_frac(x, prec));
+    default:
+      pi = mpi; togglesign(mpi); /* pi = Pi */
+      return gmul(r, expIr(gmul(pi, x)));
+  }
+}
+
+static GEN
+qq(GEN x, long prec)
+{
+  long tx = typ(x);
+  GEN y;
+
+  if (is_scalar_t(tx))
+  {
+    if (tx == t_PADIC) return x;
+    x = upper_half(x, &prec);
+    return exp_IPiC(gmul2n(x,1), prec); /* e(x) */
+  }
+  if (! ( y = toser_i(x)) ) pari_err_TYPE("modular function", x);
+  return y;
+}
+
+/* return (y * X^d) + x. Assume d > 0, x != 0, valp(x) = 0 */
+static GEN
+ser_addmulXn(GEN y, GEN x, long d)
+{
+  long i, lx, ly, l = valp(y) + d; /* > 0 */
+  GEN z;
+
+  lx = lg(x);
+  ly = lg(y) + l; if (lx < ly) ly = lx;
+  if (l > lx-2) return gcopy(x);
+  z = cgetg(ly,t_SER);
+  for (i=2; i<=l+1; i++) gel(z,i) = gel(x,i);
+  for (   ; i < ly; i++) gel(z,i) = gadd(gel(x,i),gel(y,i-l));
+  z[1] = x[1]; return z;
+}
+
+/* q a t_POL */
+static GEN
+inteta_pol(GEN q, long v, long l)
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN qn, ps, y;
+  ulong vps, vqn, n;
+
+  y = qn = ps = pol_1(0);
+  vps = vqn = 0;
+  for(n = 0;; n++)
+  { /* qn = q^n,  ps = (-1)^n q^(n(3n+1)/2),
+     * vps, vqn valuation of ps, qn HERE */
+    pari_sp av2 = avma;
+    ulong vt = vps + 2*vqn + v; /* valuation of t at END of loop body */
+    long k1, k2;
+    GEN t;
+    vqn += v; vps = vt + vqn; /* valuation of qn, ps at END of body */
+    k1 = l-2 + v - vt + 1;
+    k2 = k1 - vqn; /* = l-2 + v - vps + 1 */
+    if (k1 <= 0) break;
+    t = RgX_mul(q, RgX_sqr(qn));
+    t = RgX_modXn_shallow(t, k1);
+    t = RgX_mul(ps,t);
+    t = RgX_modXn_shallow(t, k1);
+    t = RgX_neg(t); /* t = (-1)^(n+1) q^(n(3n+1)/2 + 2n+1) */
+    t = gerepileupto(av2, t);
+    y = addmulXn(t, y, vt);
+    if (k2 <= 0) break;
+
+    qn = RgX_mul(qn,q);
+    ps = RgX_mul(t,qn);
+    ps = RgX_modXn_shallow(ps, k2);
+    y = addmulXn(ps, y, vps);
+
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"eta, n = %ld", n);
+      gerepileall(av, 3, &y, &qn, &ps);
+    }
+  }
+  setvarn(y, varn(q)); return RgX_to_ser(y, l+v);
+}
+
+static GEN
+inteta(GEN q)
+{
+  long tx = typ(q);
+  GEN ps, qn, y;
+
+  y = gen_1; qn = gen_1; ps = gen_1;
+  if (tx==t_PADIC)
+  {
+    if (valp(q) <= 0) pari_err_DOMAIN("eta", "v_p(q)", "<=",gen_0,q);
+    for(;;)
+    {
+      GEN t = gneg_i(gmul(ps,gmul(q,gsqr(qn))));
+      y = gadd(y,t); qn = gmul(qn,q); ps = gmul(t,qn);
+      t = y;
+      y = gadd(y,ps); if (gequal(t,y)) return y;
+    }
+  }
+
+  if (tx == t_SER)
+  {
+    ulong vps, vqn;
+    long l = lg(q), v, n;
+    pari_sp av, lim;
+
+    v = valp(q); /* handle valuation separately to avoid overflow */
+    if (v <= 0) pari_err_DOMAIN("eta", "v_p(q)", "<=",gen_0,q);
+    y = ser2pol_i(q, l); /* t_SER inefficient when input has low degree */
+    n = degpol(y);
+    if (n == 1 || n < (l>>2)) return inteta_pol(y, v, l);
+
+    q = leafcopy(q); av = avma; lim = stack_lim(av, 3);
+    setvalp(q, 0);
+    y = scalarser(gen_1, varn(q), l+v);
+    vps = vqn = 0;
+    for(n = 0;; n++)
+    { /* qn = q^n,  ps = (-1)^n q^(n(3n+1)/2) */
+      ulong vt = vps + 2*vqn + v;
+      long k;
+      GEN t;
+      t = gneg_i(gmul(ps,gmul(q,gsqr(qn))));
+      /* t = (-1)^(n+1) q^(n(3n+1)/2 + 2n+1) */
+      y = ser_addmulXn(t, y, vt);
+      qn = gmul(qn,q); ps = gmul(t,qn);
+      vqn += v; vps = vt + vqn;
+      k = l+v - vps; if (k <= 2) return y;
+
+      y = ser_addmulXn(ps, y, vps);
+      setlg(q, k);
+      setlg(qn, k);
+      setlg(ps, k);
+      if (low_stack(lim, stack_lim(av,3)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"eta");
+        gerepileall(av, 3, &y, &qn, &ps);
+      }
+    }
+  }
+  {
+    long l; /* gcc -Wall */
+    pari_sp av = avma, lim = stack_lim(av, 3);
+
+    l = -prec2nbits(precision(q));
+    for(;;)
+    {
+      GEN t = gneg_i(gmul(ps,gmul(q,gsqr(qn))));
+      /* qn = q^n
+       * ps = (-1)^n q^(n(3n+1)/2)
+       * t = (-1)^(n+1) q^(n(3n+1)/2 + 2n+1) */
+      y = gadd(y,t); qn = gmul(qn,q); ps = gmul(t,qn);
+      y = gadd(y,ps);
+      if (gexpo(ps)-gexpo(y) < l) return y;
+      if (low_stack(lim, stack_lim(av,3)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"eta");
+        gerepileall(av, 3, &y, &qn, &ps);
+      }
+    }
+  }
+}
+
+GEN
+eta(GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN z = inteta( qq(x,prec) );
+  if (typ(z) == t_SER) return gerepilecopy(av, z);
+  return gerepileupto(av, z);
+}
+
+/* s(h,k) = sum(n = 1, k-1, (n/k)*(frac(h*n/k) - 1/2))
+ * Knuth's algorithm. h integer, k integer > 0, (h,k) = 1 */
+GEN
+sumdedekind_coprime(GEN h, GEN k)
+{
+  pari_sp av = avma;
+  GEN s2, s1, p, pp;
+  long s;
+  if (lgefint(k) == 3 && (ulong)k[2] <= (2*(ulong)LONG_MAX) / 3)
+  {
+    ulong kk = k[2], hh = umodiu(h, kk);
+    long s1, s2;
+    GEN v;
+    if (signe(k) < 0) { k = negi(k); hh = Fl_neg(hh, kk); }
+    v = u_sumdedekind_coprime(hh, kk);
+    s1 = v[1]; s2 = v[2];
+    return gerepileupto(av, gdiv(addis(mulis(k,s1), s2), muluu(12, kk)));
+  }
+  s = 1;
+  s1 = gen_0; p = gen_1; pp = gen_0;
+  s2 = h = modii(h, k);
+  while (signe(h)) {
+    GEN r, nexth, a = dvmdii(k, h, &nexth);
+    if (is_pm1(h)) s2 = s == 1? addii(s2, p): subii(s2, p);
+    s1 = s == 1? addii(s1, a): subii(s1, a);
+    s = -s;
+    k = h; h = nexth;
+    r = addii(mulii(a,p), pp); pp = p; p = r;
+  }
+  /* at this point p = original k */
+  if (s == -1) s1 = subis(s1, 3);
+  return gerepileupto(av, gdiv(addii(mulii(p,s1), s2), muliu(p,12)));
+}
+/* as above, for ulong arguments.
+ * k integer > 0, 0 <= h < k, (h,k) = 1. Returns [s1,s2] such that
+ * s(h,k) = (s2 + k s1) / (12k). Requires max(h + k/2, k) < LONG_MAX
+ * to avoid overflow, in particular k <= LONG_MAX * 2/3 is fine */
+GEN
+u_sumdedekind_coprime(long h, long k)
+{
+  long s = 1, s1 = 0, s2 = h, p = 1, pp = 0;
+  while (h) {
+    long r, nexth = k % h, a = k / h; /* a >= 1, a >= 2 if h = 1 */
+    if (h == 1) s2 += p * s; /* occurs exactly once, last step */
+    s1 += a * s;
+    s = -s;
+    k = h; h = nexth;
+    r = a*p + pp; pp = p; p = r; /* p >= pp >= 0 */
+  }
+  /* in the above computation, p increases from 1 to original k,
+   * -k/2 <= s2 <= h + k/2, and |s1| <= k */
+  if (s < 0) s1 -= 3; /* |s1| <= k+3 ? */
+  /* But in fact, |s2 + p s1| <= k^2 + 1/2 - 3k; if (s < 0), we have
+   * |s2| <= k/2 and it follows that |s1| < k here as well */
+  /* p = k; s(h,k) = (s2 + p s1)/12p. */
+  return mkvecsmall2(s1, s2);
+}
+GEN
+sumdedekind(GEN h, GEN k)
+{
+  pari_sp av = avma;
+  GEN d;
+  if (typ(h) != t_INT) pari_err_TYPE("sumdedekind",h);
+  if (typ(k) != t_INT) pari_err_TYPE("sumdedekind",k);
+  d = gcdii(h,k);
+  if (!is_pm1(d))
+  {
+    h = diviiexact(h, d);
+    k = diviiexact(k, d);
+  }
+  return gerepileupto(av, sumdedekind_coprime(h,k));
+}
+
+/* eta(x); assume Im x >> 0 (e.g. x in SL2's standard fundamental domain) */
+static GEN
+eta_reduced(GEN x, long prec)
+{
+  GEN z = exp_IPiC(gdivgs(x, 12), prec); /* e(x/24) */
+  if (24 * gexpo(z) >= -prec2nbits(prec))
+    z = gmul(z, inteta( gpowgs(z,24) ));
+  return z;
+}
+
+/* x = U.z (flag = 1), or x = U^(-1).z (flag = 0)
+ * Return [s,t] such that eta(z) = eta(x) * sqrt(s) * exp(I Pi t) */
+static GEN
+eta_correction(GEN x, GEN U, long flag)
+{
+  GEN a,b,c,d, s,t;
+  long sc;
+  a = gcoeff(U,1,1);
+  b = gcoeff(U,1,2);
+  c = gcoeff(U,2,1);
+  d = gcoeff(U,2,2);
+  /* replace U by U^(-1) */
+  if (flag) {
+    swap(a,d);
+    togglesign_safe(&b);
+    togglesign_safe(&c);
+  }
+  sc = signe(c);
+  if (!sc) {
+    if (signe(d) < 0) togglesign_safe(&b);
+    s = gen_1;
+    t = gdivgs(utoi(umodiu(b, 24)), 12);
+  } else {
+    if (sc < 0) {
+      togglesign_safe(&a);
+      togglesign_safe(&b);
+      togglesign_safe(&c);
+      togglesign_safe(&d);
+    } /* now c > 0 */
+    s = mulcxmI(gadd(gmul(c,x), d));
+    t = gadd(gdiv(addii(a,d),muliu(c,12)), sumdedekind_coprime(negi(d),c));
+    /* correction : exp(I Pi (((a+d)/12c) + s(-d,c)) ) sqrt(-i(cx+d))  */
+  }
+  return mkvec2(s, t);
+}
+
+/* returns the true value of eta(x) for Im(x) > 0, using reduction to
+ * standard fundamental domain */
+GEN
+trueeta(GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN U, st, s, t;
+
+  if (!is_scalar_t(typ(x))) pari_err_TYPE("trueeta",x);
+  x = upper_half(x, &prec);
+  x = redtausl2(x, &U);
+  st = eta_correction(x, U, 1);
+  x = eta_reduced(x, prec);
+  s = gel(st, 1);
+  t = gel(st, 2);
+  x = gmul(x, exp_IPiQ(t, prec));
+  if (s != gen_1) x = gmul(x, gsqrt(s, prec));
+  return gerepileupto(av, x);
+}
+
+GEN
+eta0(GEN x, long flag,long prec)
+{ return flag? trueeta(x,prec): eta(x,prec); }
+
+#if 0
+/* U = [a,b;c,d], return c*z +d */
+static GEN
+aut_factor(GEN U, GEN z)
+{
+  GEN c = gcoeff(U,2,1), d = gcoeff(U,2,2);
+  return signe(c)? gadd(gmul(c,z), d): d;
+}
+#endif
+
+/* j(q) = \sum_{n >= -1} c(n)q^n,
+ * \sum_{n = -1}^{N-1} c(n) (-10n \sigma_3(N-n) + 21 \sigma_5(N-n))
+ * = c(N) (N+1)/24 */
+static GEN
+ser_j(long prec)
+{
+  GEN j, J, K = mkvecsmall2(3,5), S = cgetg(prec+1, t_VEC);
+  long i, n;
+  for (n = 1; n <= prec; n++)
+  {
+    GEN s = usumdivkvec(n, K);
+    gel(s,2) = mului(21, gel(s,2));
+    gel(S,n) = s;
+  }
+  J = cgetg(prec+2, t_SER),
+  J[1] = evalvarn(0)|evalsigne(1)|evalvalp(-1);
+  j = J+3;
+  gel(j,-1) = gen_1;
+  gel(j,0) = utoipos(744);
+  gel(j,1) = utoipos(196884);
+  for (n = 2; n < prec; n++)
+  {
+    pari_sp av = avma;
+    GEN c, s = gel(S,n+1), s3 = gel(s,1), s5 = gel(s,2);
+    c = addii(mului(10, s3), s5);
+    for (i = 0; i < n; i++)
+    {
+      s = gel(S,n-i); s3 = gel(s,1); s5 = gel(s,2);
+      c = addii(c, mulii(gel(j,i), addii(mulsi(-10*i,s3), s5)));
+    }
+    gel(j,n) = gerepileuptoint(av, diviuexact(muliu(c,24), n+1));
+  }
+  return J;
+}
+
+GEN
+jell(GEN x, long prec)
+{
+  long tx = typ(x);
+  pari_sp av = avma;
+  GEN q, h, U;
+
+  if (!is_scalar_t(tx))
+  {
+    if (gequalX(x)) { h = ser_j(precdl); setvarn(h, varn(x)); return h; }
+    q = toser_i(x);
+    if (!q) pari_err_TYPE("ellj",x);
+    h = ser_j(lg(q) - 2);
+    return gerepileupto(av, gsubst(h, 0, q));
+  }
+  if (tx == t_PADIC)
+  {
+    GEN p2, p1 = gdiv(inteta(gsqr(x)), inteta(x));
+    p1 = gmul2n(gsqr(p1),1);
+    p1 = gmul(x,gpowgs(p1,12));
+    p2 = gaddsg(768,gadd(gsqr(p1),gdivsg(4096,p1)));
+    p1 = gmulsg(48,p1);
+    return gerepileupto(av, gadd(p2,p1));
+  }
+  /* Let h = Delta(2x) / Delta(x), then j(x) = (1 + 256h)^3 / h */
+  x = upper_half(x, &prec);
+  x = redtausl2(x, &U); /* forget about Ua : j has weight 0 */
+  { /* cf eta_reduced, raised to power 24
+     * Compute
+     *   t = (inteta(q(2x)) / inteta(q(x))) ^ 24;
+     * then
+     *   h = t * (q(2x) / q(x) = t * q(x);
+     * but inteta(q) costly and useless if expo(q) << 1  => inteta(q) = 1.
+     * log_2 ( exp(-2Pi Im tau) ) < -prec2nbits(prec)
+     * <=> Im tau > prec2nbits(prec) * log(2) / 2Pi */
+    long C = (long)prec2nbits_mul(prec, LOG2/(2*PI));
+    q = exp_IPiC(gmul2n(x,1), prec); /* e(x) */
+    if (gcmpgs(gel(x,2), C) > 0) /* eta(q(x)) = 1 : no need to compute q(2x) */
+      h = q;
+    else
+    {
+      GEN t = gdiv(inteta(gsqr(q)), inteta(q));
+      h = gmul(q, gpowgs(t, 24));
+    }
+  }
+  /* real_1 important ! gaddgs(, 1) could increase the accuracy ! */
+  return gerepileupto(av, gdiv(gpowgs(gadd(gmul2n(h,8), real_1(prec)), 3), h));
+}
+
+static GEN
+to_form(GEN a, GEN w, GEN C)
+{ return mkvec3(a, w, diviiexact(C, a)); }
+static GEN
+form_to_quad(GEN f, GEN sqrtD)
+{
+  long a = itos(gel(f,1)), a2 = a << 1;
+  GEN b = gel(f,2);
+  return mkcomplex(gdivgs(b, -a2), gdivgs(sqrtD, a2));
+}
+static GEN
+eta_form(GEN f, GEN sqrtD, GEN *s_t, long prec)
+{
+  GEN U, t = form_to_quad(redimagsl2(f, &U), sqrtD);
+  *s_t = eta_correction(t, U, 0);
+  return eta_reduced(t, prec);
+}
+
+/* eta(t/p)eta(t/q) / (eta(t)eta(t/pq)), t = (-w + sqrt(D)) / 2a */
+GEN
+double_eta_quotient(GEN a, GEN w, GEN D, long p, long q, GEN pq, GEN sqrtD)
+{
+  GEN C = shifti(subii(sqri(w), D), -2);
+  GEN d, t, z, zp, zq, zpq, s_t, s_tp, s_tpq, s, sp, spq;
+  long prec = realprec(sqrtD);
+
+  z = eta_form(to_form(a, w, C), sqrtD, &s_t, prec);
+  s = gel(s_t, 1);
+  zp = eta_form(to_form(mului(p, a), w, C), sqrtD, &s_tp, prec);
+  sp = gel(s_tp, 1);
+  zpq = eta_form(to_form(mulii(pq, a), w, C), sqrtD, &s_tpq, prec);
+  spq = gel(s_tpq, 1);
+  if (p == q) {
+    z = gdiv(gsqr(zp), gmul(z, zpq));
+    t = gsub(gmul2n(gel(s_tp,2), 1),
+             gadd(gel(s_t,2), gel(s_tpq,2)));
+    if (sp != gen_1) z = gmul(z, sp);
+  } else {
+    GEN s_tq, sq;
+    zq = eta_form(to_form(mului(q, a), w, C), sqrtD, &s_tq, prec);
+    sq = gel(s_tq, 1);
+    z = gdiv(gmul(zp, zq), gmul(z, zpq));
+    t = gsub(gadd(gel(s_tp,2), gel(s_tq,2)),
+             gadd(gel(s_t,2), gel(s_tpq,2)));
+    if (sp != gen_1) z = gmul(z, gsqrt(sp, prec));
+    if (sq != gen_1) z = gmul(z, gsqrt(sq, prec));
+  }
+  d = NULL;
+  if (s != gen_1) d = gsqrt(s, prec);
+  if (spq != gen_1) {
+    GEN x = gsqrt(spq, prec);
+    d = d? gmul(d, x): x;
+  }
+  if (d) z = gdiv(z, d);
+  return gmul(z, exp_IPiQ(t, prec));
+}
+
+typedef struct { GEN u; long v, t; } cxanalyze_t;
+
+/* typ(x) = t_INT, t_FRAC or t_REAL */
+INLINE GEN
+R_abs_shallow(GEN x)
+{ return (typ(x) == t_FRAC)? absfrac_shallow(x): mpabs_shallow(x); }
+
+/* Check whether a t_COMPLEX, t_REAL or t_INT z != 0 can be written as
+ * z = u * 2^(v/2) * exp(I Pi/4 t), u > 0, v = 0,1 and -3 <= t <= 4.
+ * Allow z t_INT/t_REAL to simplify handling of eta_correction() output */
+static int
+cxanalyze(cxanalyze_t *T, GEN z)
+{
+  GEN a, b;
+  long ta, tb;
+
+  T->v = 0;
+  if (is_intreal_t(typ(z)))
+  {
+    T->u = mpabs_shallow(z);
+    T->t = signe(z) < 0? 4: 0;
+    return 1;
+  }
+  a = gel(z,1); ta = typ(a);
+  b = gel(z,2); tb = typ(b);
+
+  T->t = 0;
+  if (ta == t_INT && !signe(a))
+  {
+    T->u = R_abs_shallow(b);
+    T->t = gsigne(b) < 0? -2: 2;
+    return 1;
+  }
+  if (tb == t_INT && !signe(b))
+  {
+    T->u = R_abs_shallow(a);
+    T->t = gsigne(a) < 0? 4: 0;
+    return 1;
+  }
+  if (ta != tb || ta == t_REAL) { T->u = z; return 0; }
+  /* a,b both non zero, both t_INT or t_FRAC */
+  if (ta == t_INT)
+  {
+    if (!absi_equal(a, b)) return 0;
+    T->u = absi_shallow(a);
+    T->v = 1;
+    if (signe(a) == signe(b))
+    { T->t = signe(a) < 0? -3: 1; }
+    else
+    { T->t = signe(a) < 0? 3: -1; }
+  }
+  else
+  {
+    if (!absi_equal(gel(a,2), gel(b,2)) || !absi_equal(gel(a,1),gel(b,1)))
+      return 0;
+    T->u = absfrac_shallow(a);
+    T->v = 1;
+    a = gel(a,1);
+    b = gel(b,1);
+    if (signe(a) == signe(b))
+    { T->t = signe(a) < 0? -3: 1; }
+    else
+    { T->t = signe(a) < 0? 3: -1; }
+  }
+  return 1;
+}
+
+/* z * sqrt(st_b) / sqrt(st_a) exp(I Pi (t + t0)). Assume that
+ * sqrt2 = gsqrt(gen_2, prec) or NULL */
+static GEN
+apply_eta_correction(GEN z, GEN st_a, GEN st_b, GEN t0, GEN sqrt2, long prec)
+{
+  GEN t, s_a = gel(st_a, 1), s_b = gel(st_b, 1);
+  cxanalyze_t Ta, Tb;
+  int ca, cb;
+
+  t = gsub(gel(st_b,2), gel(st_a,2));
+  if (t0 != gen_0) t = gadd(t, t0);
+  ca = cxanalyze(&Ta, s_a);
+  cb = cxanalyze(&Tb, s_b);
+  if (ca || cb)
+  { /* compute sqrt(s_b) / sqrt(s_a) in a more efficient way:
+     * sb = ub sqrt(2)^vb exp(i Pi/4 tb) */
+    GEN u = gdiv(Tb.u,Ta.u);
+    switch(Tb.v - Ta.v)
+    {
+      case -1: u = gmul2n(u,-1); /* fall through: write 1/sqrt2 = sqrt2/2 */
+      case 1: u = gmul(u, sqrt2? sqrt2: sqrtr_abs(real2n(1, prec)));
+    }
+    if (!isint1(u)) z = gmul(z, gsqrt(u, prec));
+    t = gadd(t, gmul2n(stoi(Tb.t - Ta.t), -3));
+  }
+  else
+  {
+    z = gmul(z, gsqrt(s_b, prec));
+    z = gdiv(z, gsqrt(s_a, prec));
+  }
+  return gmul(z, exp_IPiQ(t, prec));
+}
+
+/* sqrt(2) eta(2x) / eta(x) */
+GEN
+weberf2(GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN z, sqrt2, a,b, Ua,Ub, st_a,st_b;
+
+  x = upper_half(x, &prec);
+  a = redtausl2(x, &Ua);
+  b = redtausl2(gmul2n(x,1), &Ub);
+  if (gequal(a,b)) /* not infrequent */
+    z = gen_1;
+  else
+    z = gdiv(eta_reduced(b,prec), eta_reduced(a,prec));
+  st_a = eta_correction(a, Ua, 1);
+  st_b = eta_correction(b, Ub, 1);
+  sqrt2 = sqrtr_abs(real2n(1, prec));
+  z = apply_eta_correction(z, st_a, st_b, gen_0, sqrt2, prec);
+  return gerepileupto(av, gmul(z, sqrt2));
+}
+
+/* eta(x/2) / eta(x) */
+GEN
+weberf1(GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN z, a,b, Ua,Ub, st_a,st_b;
+
+  x = upper_half(x, &prec);
+  a = redtausl2(x, &Ua);
+  b = redtausl2(gmul2n(x,-1), &Ub);
+  if (gequal(a,b)) /* not infrequent */
+    z = gen_1;
+  else
+    z = gdiv(eta_reduced(b,prec), eta_reduced(a,prec));
+  st_a = eta_correction(a, Ua, 1);
+  st_b = eta_correction(b, Ub, 1);
+  z = apply_eta_correction(z, st_a, st_b, gen_0, NULL, prec);
+  return gerepileupto(av, z);
+}
+/* e(-1/24) * eta((x+1)/2) / eta(x) */
+GEN
+weberf(GEN x, long prec)
+{
+  pari_sp av = avma;
+  GEN z, t0, a,b, Ua,Ub, st_a,st_b;
+  x = upper_half(x, &prec);
+  a = redtausl2(x, &Ua);
+  b = redtausl2(gmul2n(gaddgs(x,1),-1), &Ub);
+  if (gequal(a,b)) /* not infrequent */
+    z = gen_1;
+  else
+    z = gdiv(eta_reduced(b,prec), eta_reduced(a,prec));
+  st_a = eta_correction(a, Ua, 1);
+  st_b = eta_correction(b, Ub, 1);
+  t0 = mkfrac(gen_m1, utoipos(24));
+  z = apply_eta_correction(z, st_a, st_b, t0, NULL, prec);
+  if (typ(z) == t_COMPLEX && isexactzero(real_i(a)))
+    z = gerepilecopy(av, gel(z,1));
+  else
+    z = gerepileupto(av, z);
+  return z;
+}
+GEN
+weber0(GEN x, long flag,long prec)
+{
+  switch(flag)
+  {
+    case 0: return weberf(x,prec);
+    case 1: return weberf1(x,prec);
+    case 2: return weberf2(x,prec);
+    default: pari_err_FLAG("weber");
+  }
+  return NULL; /* not reached */
+}
+
+/* check |q| < 1 */
+static GEN
+check_unit_disc(const char *fun, GEN q, long prec)
+{
+  GEN Q = gtofp(q, prec), Qlow;
+  Qlow = (prec > LOWDEFAULTPREC)? gtofp(Q,LOWDEFAULTPREC): Q;
+  if (gcmp(gnorm(Qlow), gen_1) >= 0)
+    pari_err_DOMAIN(fun, "abs(q)", ">=", gen_1, q);
+  return Q;
+}
+
+GEN
+theta(GEN q, GEN z, long prec)
+{
+  long l, n;
+  pari_sp av = avma, av2, lim;
+  GEN s, c, snz, cnz, s2z, c2z, ps, qn, y, zy, ps2, k, zold;
+
+  l = precision(q);
+  n = precision(z); if (n && n < l) l = n;
+  if (l) prec = l;
+  z = gtofp(z, prec);
+  q = check_unit_disc("theta", q, prec);
+  zold = NULL; /* gcc -Wall */
+  zy = imag_i(z);
+  if (gequal0(zy)) k = gen_0;
+  else
+  {
+    GEN lq = glog(q,prec); k = roundr(divrr(zy, real_i(lq)));
+    if (signe(k)) { zold = z; z = gadd(z, mulcxmI(gmul(lq,k))); }
+  }
+  qn = gen_1;
+  ps2 = gsqr(q);
+  ps = gneg_i(ps2);
+  gsincos(z, &s, &c, prec);
+  s2z = gmul2n(gmul(s,c),1); /* sin 2z */
+  c2z = gsubgs(gmul2n(gsqr(c),1), 1); /* cos 2z */
+  snz = s;
+  cnz = c; y = s;
+  av2 = avma; lim = stack_lim(av2,2);
+  for (n = 3;; n += 2)
+  {
+    long e;
+    s = gadd(gmul(snz, c2z), gmul(cnz,s2z));
+    qn = gmul(qn,ps);
+    y = gadd(y, gmul(qn, s));
+    e = gexpo(s); if (e < 0) e = 0;
+    if (gexpo(qn) + e < -prec2nbits(prec)) break;
+
+    ps = gmul(ps,ps2);
+    c = gsub(gmul(cnz, c2z), gmul(snz,s2z));
+    snz = s; /* sin nz */
+    cnz = c; /* cos nz */
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"theta (n = %ld)", n);
+      gerepileall(av2, 5, &snz, &cnz, &ps, &qn, &y);
+    }
+  }
+  if (signe(k))
+  {
+    y = gmul(y, gmul(powgi(q,sqri(k)),
+                     gexp(gmul(mulcxI(zold),shifti(k,1)), prec)));
+    if (mod2(k)) y = gneg_i(y);
+  }
+  return gerepileupto(av, gmul(y, gmul2n(gsqrt(gsqrt(q,prec),prec),1)));
+}
+
+GEN
+thetanullk(GEN q, long k, long prec)
+{
+  long l, n;
+  pari_sp av = avma;
+  GEN p1, ps, qn, y, ps2;
+
+  if (k < 0)
+    pari_err_DOMAIN("thetanullk", "k", "<", gen_0, stoi(k));
+  l = precision(q);
+  if (l) prec = l;
+  q = check_unit_disc("thetanullk", q, prec);
+
+  if (!(k&1)) { avma = av; return gen_0; }
+  qn = gen_1;
+  ps2 = gsqr(q);
+  ps = gneg_i(ps2);
+  y = gen_1;
+  for (n = 3;; n += 2)
+  {
+    GEN t;
+    qn = gmul(qn,ps);
+    ps = gmul(ps,ps2);
+    t = gmul(qn, powuu(n, k)); y = gadd(y, t);
+    if (gexpo(t) < -prec2nbits(prec)) break;
+  }
+  p1 = gmul2n(gsqrt(gsqrt(q,prec),prec),1);
+  if (k&2) y = gneg_i(y);
+  return gerepileupto(av, gmul(p1, y));
+}
+
+/* q2 = q^2 */
+static GEN
+vecthetanullk_loop(GEN q2, long k, long prec)
+{
+  GEN ps, qn = gen_1, y = const_vec(k, gen_1);
+  pari_sp av = avma, lim = stack_lim(av,2);
+  const long bit = prec2nbits(prec);
+  long i, n;
+
+  ps = gneg_i(q2);
+  for (n = 3;; n += 2)
+  {
+    GEN t = NULL/*-Wall*/, P = utoipos(n), N2 = sqru(n);
+    qn = gmul(qn,ps);
+    ps = gmul(ps,q2);
+    for (i = 1; i <= k; i++)
+    {
+      t = gmul(qn, P); gel(y,i) = gadd(gel(y,i), t);
+      P = mulii(P, N2);
+    }
+    if (gexpo(t) < -bit) return y;
+    if (low_stack(lim, stack_lim(av,2)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"vecthetanullk_loop, n = %ld",n);
+      gerepileall(av, 3, &qn, &ps, &y);
+    }
+  }
+}
+/* [d^i theta/dz^i(q, 0), i = 1, 3, .., 2*k - 1] */
+GEN
+vecthetanullk(GEN q, long k, long prec)
+{
+  long i, l = precision(q);
+  pari_sp av = avma;
+  GEN p1, y;
+
+  if (l) prec = l;
+  q = check_unit_disc("vecthetanullk", q, prec);
+  y = vecthetanullk_loop(gsqr(q), k, prec);
+
+  p1 = gmul2n(gsqrt(gsqrt(q,prec),prec),1);
+  for (i = 2; i <= k; i+=2) gel(y,i) = gneg_i(gel(y,i));
+  return gerepileupto(av, gmul(p1, y));
+}
+
+/* [d^i theta/dz^i(q, 0), i = 1, 3, .., 2*k - 1], q = exp(2iPi tau) */
+GEN
+vecthetanullk_tau(GEN tau, long k, long prec)
+{
+  long i, l = precision(tau);
+  pari_sp av = avma;
+  GEN p1, q4, y;
+
+  if (l) prec = l;
+  if (typ(tau) != t_COMPLEX || gsigne(gel(tau,2)) <= 0)
+    pari_err_DOMAIN("vecthetanullk_tau", "imag(tau)", "<=", gen_0, tau);
+
+  q4 = expIxy(Pi2n(-1, prec), tau, prec); /* q^(1/4) */
+  y = vecthetanullk_loop(gpowgs(q4,8), k, prec);
+  p1 = gmul2n(q4,1);
+  for (i = 2; i <= k; i+=2) gel(y,i) = gneg_i(gel(y,i));
+  return gerepileupto(av, gmul(p1, y));
+}
+
+/* -theta^(3)(tau/2) / theta^(1)(tau/2). Assume that Im tau > 0 */
+GEN
+trueE2(GEN tau, long prec)
+{
+  long l = precision(tau);
+  pari_sp av = avma;
+  GEN q2, y;
+  if (l) prec = l;
+  q2 = expIxy(Pi2n(1, prec), tau, prec);
+  y = vecthetanullk_loop(q2, 3, prec);
+  return gerepileupto(av, gdiv(gel(y,2), gel(y,1)));
+}
+
+/* Lambert W function : solution x of x*exp(x)=y, using Newton. y >= 0 t_REAL.
+ * Good for low accuracy: the precision remains constant. Not memory-clean */
+static GEN
+mplambertW0(GEN y)
+{
+  long bitprec = bit_prec(y) - 2;
+  GEN x, tmp;
+
+  x = mplog(addrs(y,1));
+  do {
+   tmp = x;
+   /* f(x) = log(x)+x-log(y), f'(x) = (1+1/x)
+    * x *= (1-log(x/y))/(x+1) */
+    x = mulrr(x, divrr(subsr(1, mplog(divrr(x,y))), addrs(x,1)));
+  } while (expo(tmp) - expo(subrr(x,tmp)) < bitprec);
+  return x;
+};
+
+/* Lambert W function using Newton, increasing prec */
+GEN
+mplambertW(GEN y)
+{
+  pari_sp av = avma;
+  GEN x;
+  long p = 1, s = signe(y);
+  ulong mask = quadratic_prec_mask(lg(y)-1);
+
+  if (s<0) pari_err_DOMAIN("Lw", "y", "<", gen_0, y);
+  if(s==0) return rcopy(y);
+  x = mplambertW0(rtor(y, LOWDEFAULTPREC));
+  while (mask!=1)
+  {
+    p <<= 1; if (mask & 1) p--;
+    mask >>= 1;
+    x = rtor(x, p+2);
+    x = mulrr(x, divrr(subsr(1, mplog(divrr(x,y))),addrs(x,1)));
+  }
+  return gerepileuptoleaf(av,x);
+}
+
+GEN
+glambertW(GEN y, long prec)
+{
+  switch(typ(y))
+  {
+    case t_REAL: return mplambertW(y);
+    case t_COMPLEX: pari_err_IMPL("lambert(t_COMPLEX)");
+  }
+  return trans_eval("lambert",glambertW,y,prec);
+}
+
+#if 0
+/* Another Lambert-like function: solution of exp(x)/x=y, y >= e t_REAL,
+ * using Newton with constant prec. Good for low accuracy */
+GEN
+mplambertX(GEN y)
+{
+  long bitprec = bit_prec(y)-2;
+  GEN tmp, x = mplog(y);
+  if (typ(x) != t_REAL || signe(subrs(x,1))<0)
+    pari_err(e_MISC,"Lx : argument less than e");
+  do {
+    tmp = x;
+   /* f(x)=x-log(xy), f'(x)=1-1/x */
+   /* x *= (log(x*y)-1)/(x-1) */
+    x = mulrr(x, divrr(subrs(mplog(mulrr(x,y)),1), subrs(x,1)));
+  } while(expo(tmp)-expo(subrr(x,tmp)) < bitprec);
+  return x;
+}
+#endif
diff --git a/src/desc/PARI/822.pm b/src/desc/PARI/822.pm
new file mode 100755
index 0000000..ccbdbfe
--- /dev/null
+++ b/src/desc/PARI/822.pm
@@ -0,0 +1,141 @@
+#!/usr/bin/perl -w
+#Copyright (C) 2003  The PARI group.
+#
+#This file is part of the GP2C package.
+#
+#PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+#ANY WARRANTY WHATSOEVER.
+#
+#Check the License for details. You should have received a copy of it, along
+#with the package; see the file 'COPYING'. If not, write to the Free Software
+#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+#Based on Debconf::Format::822 by Joey Hess <joey at kitenet.net>.
+
+package PARI::822;
+use strict;
+
+sub new { bless {} }
+
+=d1 NAME
+
+PARI::822::read -- Read Description files.
+
+=head1 SYNOPSIS
+
+$database->PARI::822::new();
+$database->read($filename,$mode)
+
+PARI::822::read(\%database,$filename,$mode)
+
+=head1 DESCRIPTION
+
+read the database file $filename and merge the information in the database.
+
+Mode is a bitmap flag
+mode&1: new values cannot overwrite old ones.
+mode&2: new functions are not allowed.
+
+=cut
+
+sub read
+{
+
+	local $/="\n";
+        local *FILE;
+
+	my ($ret,$file,$check)=@_;
+        $check=0 if (!defined($check));
+	my $invars=0;
+	my ($key, $value);
+	my $last_was_void=0;
+        my $entry;
+        my ($store) = sub {
+                $value =~ s/\s*$//;
+                if (!defined($ret->{$entry}->{$key}))
+                {
+                        $ret->{$entry}->{$key}=$value;
+                }
+                elsif (($check&1) and $ret->{$entry}->{$key} ne $value)
+                {
+                        die "Unmatched data: $entry: $key: $ret->{$entry}->{$key} ne $value";
+                }
+        };
+
+        open FILE,"<$file";
+	while (my $line = <FILE>)
+        {
+		chomp $line;
+                if ($invars && $line =~ /^\s/)
+                {
+                        $line =~ s/^\s//;
+                        $value.= ($last_was_void?"\n\n$line":"\n$line");
+                        $last_was_void = 0; next;
+                }
+                $last_was_void = ($line =~ /^\s*$/);
+                next if ($last_was_void);
+
+                $store->() if ($invars);
+
+		($key, $value)=split(/:\s*/, $line, 2);
+                die("Bad entry in $file: $key") if (!defined($value));
+		if ($key eq 'Function')
+                {
+                        $entry=$value;
+                        die("New function $value") if (($check&2) and !defined($ret->{$entry}));
+		}
+                $invars=1;
+	}
+        $store->() if ($invars);
+        return 0;
+}
+
+=d1 NAME
+
+PARI::822::write -- Write Description files.
+
+=head1 SYNOPSIS
+
+$database->PARI::822::new();
+$database->write($filename)
+
+PARI::822::write(\%database,STREAM)
+
+
+=head1 DESCRIPTION
+
+output a database to STREAM in canonical 822 format.
+
+=cut
+
+
+sub write
+{
+        my @order=("Function","Class","Section","C-Name","Prototype","Help",
+                   "Iterator","Wrapper","Description","Doc");
+        my %knowfields=map {$_ => 1}  @order;
+	my %data=%{shift()};
+        my $STREAM=shift;
+        defined($STREAM) or $STREAM=*STDOUT;
+	foreach my $func (sort keys %data)
+        {
+	        foreach my $field (@order)
+                {
+		        my $val=$data{$func}->{$field};
+                        next if (!defined($val));
+                        $val =~ s/\n/\n /g;
+		        print $STREAM $field.": $val\n";
+                }
+	        foreach my $field (sort keys %{$data{$func}})
+                {
+                        next if ($knowfields{$field});
+		        my $val=$data{$func}->{$field};
+                        $val =~ s/\n/\n /g;
+		        print $STREAM $field.": $val\n";
+                }
+                print $STREAM "\n";
+	}
+}
+1;
diff --git a/src/desc/doc_make b/src/desc/doc_make
new file mode 100755
index 0000000..91b894e
--- /dev/null
+++ b/src/desc/doc_make
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+use warnings FATAL => 'all';
+use strict;
+use PARI::822;
+
+my (%funcs, %Fun_by_sec);
+PARI::822::read(\%funcs, "pari.desc");
+
+open (FILE, "../../doc/usersFUNCS.tex") || die;
+for (keys %funcs)
+{ 
+  my ($s) = $funcs{$_}->{Section};
+  next if (!$s);
+  push(@{$Fun_by_sec{$s}}, $_);
+}
+while (<FILE>)
+{
+  if (/^% *SECTION *: *(.*) */)
+  {
+    my ($sec) = $1;
+    next if (!$Fun_by_sec{$sec});
+    for ( sort @{$Fun_by_sec{$sec}} ) {
+      my ($fun) = $funcs{$_};
+      my ($doc) = $fun->{Doc};
+      next if (!defined($doc));
+
+      my ($args)  = $fun->{Help};
+      my ($v);
+      $doc =~ s/^[\n\t ]*(.)/uc($1)/e;
+      $args =~ s/ *:.*//s;
+      if (!$args || $args =~ /^\w+=\w+\(\)/) { $args = $v = ''; }
+      else
+      {
+        $args =~ s/^[^(]*\((.*)\)/$1/; # args proper
+        $v = $args;
+        $v =~ s/([{}&])/\\$1/g;
+        $v =~ s/\^(\d+)/^{$1}/g;
+        $v =~ s/\[\]/[\\,]/g;
+        $v =~ s/(\w\w+)/\\var{$1}/g;
+        $v =~ s/\^([a-z])/\\hbox{\\kbd{\\pow}}$1/g;
+        $v =~ s/\\var{flag}/\\fl/g;
+        $v =~ s/\\var{(\d+)}/{$1}/g;
+
+        $v = "\$($v)\$";
+      }
+      if ($doc !~ /\\syn\w*{/ && $sec !~ /programming\/control/) {
+        $doc .= library_syntax($fun, $args);
+      }
+      s/_def_//;
+      my ($secname) = $_;
+      my ($l) = ($fun->{Section} =~ 'default')? "def,$_": $_;
+      my ($idx) = ($secname =~ s/_/\\_/g)? $l: $secname;
+      print "\n\\subsec{$secname$v}\\kbdsidx{$idx}\\label{se:$l}\n$doc\n";
+    }
+  }
+  print;
+}
+
+sub library_syntax { my ($fun, $args) = @_;
+  return '' if ($fun->{Class} =~ /^(highlevel|gp|default|gp_default)$/);
+  my ($Cname) = $fun->{'C-Name'};
+  return '' if (!$Cname);
+  my ($Variant) = $fun->{Variant};
+  my (@proto) = split(//, $fun->{Prototype});
+  $args =~ s/[{}&]//g;
+  $args =~ s/=[^,\)]*//g; # delete default values
+  my (@ARGS) = split(/[,^] */, $args); # ^ for O(p^e)
+  my ($type) = "GEN";
+  my (@vars)=();
+  $args = '';
+  for (my $i = 0; $i <= $#proto; )
+  {
+    my ($c) = $proto[$i++];
+    if ($c eq 'l') { $type = "long"; next; }
+    if ($c eq 'v') { $type = "void"; next; }
+
+    if ($c =~ /^[GWIJE]$/) {$args .= ", GEN " . shift(@ARGS); next;}
+    if ($c eq 'L') {$args .= ", long " . shift(@ARGS); next;}
+    if ($c eq 'n') {my ($v) = shift(@ARGS); push @vars,"\\kbd{$v}";
+                    $args .= ", long " . $v; next;}
+    if ($c =~ /^[rs]$/) {$args .= ", const char *" . shift(@ARGS); next;}
+
+    if ($c eq 'p') {$args .= ", long prec"; next;}
+    if ($c eq 'P') {$args .= ", long precdl"; next;}
+    if ($c eq 'C') {$args .= ", GEN ctx"; next;}
+    if ($c eq '') { next; }
+    if ($c eq 'D') {
+      $c = $proto[$i++];
+      if ($c eq 'G') {$args .= ", GEN " . shift(@ARGS) ." = NULL"; next;}
+      if ($c =~ /^[rs]$/) {$args .= ", const char *" . shift(@ARGS) ." = NULL"; next;}
+      if ($c eq '&') {$args .= ", GEN *". shift(@ARGS) ." = NULL"; next;}
+      if ($c eq 'P') {$args .= ", long precdl"; next;}
+      if ($c eq 'n') {
+        my ($v) = shift(@ARGS);
+        $args .= ", long $v = -1";
+        push @vars,"\\kbd{$v}";
+        next;
+      }
+      if ($c eq 'V') {
+        next;
+      }
+      if ($c =~ /^[EI]$/) {
+        $args .= ", GEN ". shift(@ARGS) ." = NULL"; next;
+      }
+      while (($c = $proto[$i++]) ne ',') {}
+    }
+  }
+  $args =~ s/^, //;
+
+  my ($post);
+  my ($l)=scalar @vars;
+  if    ($l==0) { $post=''; }
+  elsif ($l==1)
+  {
+    $post = ", where $vars[0] is a variable number";
+  }
+  else
+  {
+    my ($vl)=join(", ", at vars);
+    $post = ", where $vl are variable numbers";
+  }
+  my ($txt) = "\n\nThe library syntax is \\fun{$type}{$Cname}{$args}$post.";
+  $txt .= "\n$Variant" if ($Variant);
+  return $txt;
+}
diff --git a/src/desc/gen_proto b/src/desc/gen_proto
new file mode 100755
index 0000000..ee55449
--- /dev/null
+++ b/src/desc/gen_proto
@@ -0,0 +1,67 @@
+#!/usr/bin/perl -w
+use PARI::822;
+
+$class=$ARGV[0];
+
+%secnumber=(
+  'operators'             =>  1,
+  'conversions'           =>  2,
+  'transcendental'        =>  3,
+  'number_theoretical'    =>  4,
+  'elliptic_curves'       =>  5,
+  'number_fields'         =>  6,
+  'polynomials'           =>  7,
+  'linear_algebra'        =>  8,
+  'sums'                  =>  9,
+  'graphic'               => 10,
+  'programming/control'   => 11,
+  'programming/specific'  => 11,
+  'programming/parallel'  => 11,
+  'symbolic_operators'    => 13,
+  'member_functions'      => 14,
+  'programming/internals' => 15,
+  'default'               => 16,
+);
+
+print <<EOF;
+/* This file is autogenerated from the database. */
+/* See src/desc/gen_proto */
+/* Do not edit*/
+entree functions_${class}[]={
+EOF
+PARI::822::read(\%funcs,$ARGV[1]);
+foreach my $name (sort keys %funcs)
+{
+      my %func=%{$funcs{$name}};
+      next if ($func{'Class'} ne $class);
+      my $gpname=$func{'Function'};
+      $gpname =~ s/\\/\\\\/g;
+      $gpname =~ s/^_def_//;
+      defined($func{'Section'})
+        or die("$name: mandatory field Section is missing");
+      my $sec=$secnumber{$func{'Section'}};
+      defined($sec)
+        or die("$name: Section field has unknown value $func{'Section'}");
+      my $proto=$func{'Prototype'};
+      my $cname=$func{'C-Name'};
+      my $help=$func{'Help'};
+      defined($help) or die("$name: mandatory field Help is missing");
+      $help =~ s/\n/ /g;
+      $help =~ s/\\/\\\\/g;
+      $help =~ s/"/\\"/g;
+      $help = "\"$help\"";
+
+      if (defined($cname))
+      {
+        $proto =~ s/"/\\"/g;
+        print "{\"$gpname\",0,(void*)$cname,$sec,\"$proto\",$help},\n";
+      }
+      else
+      {
+        print "{\"$gpname\",0,NULL,$sec,NULL,$help},\n";
+      }
+}
+print <<'EOF';
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
+EOF
diff --git a/src/desc/merge_822 b/src/desc/merge_822
new file mode 100755
index 0000000..4c9217b
--- /dev/null
+++ b/src/desc/merge_822
@@ -0,0 +1,11 @@
+#!/usr/bin/perl -w
+use PARI::822;
+
+open(IN, $ARGV[0]) || die "cannot find $ARGV[0]";
+for (<IN>) { s/^\d+\s+\d+\s+//; PARI::822::read(\%funcs,$_,1) }
+
+for (keys %funcs)
+{
+  $funcs{$_}->{'Class'} = 'basic' if (!defined($funcs{$_}->{'Class'}));
+}
+PARI::822::write(\%funcs);
diff --git a/src/desc/whatnow b/src/desc/whatnow
new file mode 100755
index 0000000..7c4186c
--- /dev/null
+++ b/src/desc/whatnow
@@ -0,0 +1,81 @@
+#!/usr/bin/perl -w
+##################################################
+#
+# Outputs the whatnow_list to be included in gp.c
+#
+##################################################
+$src = "..";
+$special = "\@";
+
+%funold = read_oldfun("$src/language/compat.c");
+%gpold  = read_oldfun("$src/gp/gp_init.c");
+for ( read_dico() )
+{ my ($oldname,$name,$arg,$oldarg) = split(/$special/);
+  if ($funold{$oldname}) { push(@funres, get_new($name,$oldarg,$arg)); next; }
+  if ($gpold{$oldname})  { push(@gpres,  get_new($name,$oldarg,$arg)); next; }
+  bug("5: $oldname\n");
+}
+print "/* generated by the perl script 'whatnow' */\n"
+      . "static const whatnow_t whatnowlist[]={\n"
+      . join(",\n", at funres) . ",\n\n" . join(",\n", at gpres) . "\n};\n";
+
+sub get_new { my($name, $oldarg,$arg) = @_;
+  return "_SAME" if ($name eq "=");
+  return "_REMOV" if ($name eq "");
+  return "{\"$name\",\"$oldarg\",\"$arg\"}";
+}
+
+sub bug { die "BUG$_[0]\n"; }
+sub o_r { my($f) = $_[0]; open(IN,"<$f") || die "can't open $f\n"; }
+
+sub read_oldfun {
+  o_r($_[0]);
+  my(%fun);
+  while(<IN>) {
+    if (/^entree.*old/../^$/) {
+      next if (/^entree/ || /^$/);
+      my(@line) = split(/\"/);
+      my($first) = $line[1]; bug("1: $_") if (!$first);
+      $fun{$first} = 1;
+    }
+  }
+  close(IN); return %fun;
+}
+
+sub read_dico {
+  o_r("$src/whatnow");
+  my(@diclist);
+  while(<IN>)
+  {
+    chop; if (/_/ || !/=/) { push(@diclist,"$_$special="); next; }
+    print bug("2: $_") if (! /; *$/);
+
+    chop;
+    my(@tab) = split(//);
+    my($paren) = 0;
+    my($pre, $post) = ("","");
+    my($c) = 0;
+    for (@tab)
+    {
+      if ($_ eq '(') { $paren++; }
+      elsif ($_ eq ')') { $paren--; }
+      elsif ($_ eq '=')
+      {
+        if (!$paren)
+        {
+          $pre = join("", at tab[0..$c-1]);
+          $post = join("", at tab[$c+1..$#tab]);
+          last;
+        }
+      }
+      $c++;
+    }
+    $old = $pre;
+    bug("3: $_") if ($post !~ /([^(]*)(.*)/);
+    $name = $1; $arg = $2;
+    bug("4: $_") if ($old !~ /([^(]*)(.*)/);
+    $oldname = $1; $oldarg = $2;
+    push(@diclist, "$oldname$special$name$special$arg$special$oldarg");
+  }
+  close(IN); return @diclist;
+}
diff --git a/src/funclist b/src/funclist
new file mode 100644
index 0000000..e53f083
--- /dev/null
+++ b/src/funclist
@@ -0,0 +1,825 @@
+1557685643 1558 ../functions/conversions/Col
+3234299882 400 ../functions/conversions/Colrev
+4195552700 684 ../functions/conversions/List
+981428093 924 ../functions/conversions/Mat
+2678939693 1487 ../functions/conversions/Mod
+3703587249 1772 ../functions/conversions/Pol
+812817715 914 ../functions/conversions/Polrev
+2844602529 620 ../functions/conversions/Qfb
+2571513257 1826 ../functions/conversions/Ser
+1244914425 745 ../functions/conversions/Set
+2425486384 993 ../functions/conversions/Str
+1983631051 398 ../functions/conversions/Strchr
+3668703910 592 ../functions/conversions/Strexpand
+1117955044 413 ../functions/conversions/Strprintf
+2125909664 423 ../functions/conversions/Strtex
+1466495582 1560 ../functions/conversions/Vec
+2107834942 595 ../functions/conversions/Vecrev
+1108364263 729 ../functions/conversions/Vecsmall
+1275004057 370 ../functions/conversions/binary
+130657493 883 ../functions/conversions/bitand
+972947861 613 ../functions/conversions/bitneg
+1138344667 706 ../functions/conversions/bitnegimply
+2059074700 616 ../functions/conversions/bitor
+2769387502 923 ../functions/conversions/bittest
+351827735 633 ../functions/conversions/bitxor
+1456424929 513 ../functions/conversions/ceil
+276292424 1030 ../functions/conversions/centerlift
+4171436333 477 ../functions/conversions/characteristic
+1580484749 1749 ../functions/conversions/component
+2027282389 401 ../functions/conversions/conj
+4003200502 838 ../functions/conversions/conjvec
+2494482002 1105 ../functions/conversions/denominator
+863736833 259 ../functions/conversions/digits
+2072628265 514 ../functions/conversions/floor
+689569802 220 ../functions/conversions/frac
+958959570 550 ../functions/conversions/hammingweight
+293423956 246 ../functions/conversions/imag
+1061890972 1035 ../functions/conversions/length
+1580940764 1934 ../functions/conversions/lift
+2551633371 773 ../functions/conversions/liftall
+1700859792 785 ../functions/conversions/liftint
+2145004205 717 ../functions/conversions/liftpol
+4165166886 433 ../functions/conversions/norm
+765810884 922 ../functions/conversions/numerator
+1895271770 411 ../functions/conversions/numtoperm
+622159595 369 ../functions/conversions/padicprec
+137224884 360 ../functions/conversions/permtonum
+3807185038 2929 ../functions/conversions/precision
+3713049196 3331 ../functions/conversions/random
+1868514297 244 ../functions/conversions/real
+1814815920 1559 ../functions/conversions/round
+1581212202 1010 ../functions/conversions/simplify
+2803792228 343 ../functions/conversions/sizebyte
+2662166564 350 ../functions/conversions/sizedigit
+1113868494 1709 ../functions/conversions/truncate
+3403630479 953 ../functions/conversions/valuation
+3384175128 1425 ../functions/conversions/variable
+2091712008 516 ../functions/default/TeXstyle
+1101063589 291 ../functions/default/breakloop
+2501181600 2179 ../functions/default/colors
+45066311 2219 ../functions/default/compatible
+1698176216 380 ../functions/default/datadir
+845501215 264 ../functions/default/debug
+1705281810 307 ../functions/default/debugfiles
+2013652512 591 ../functions/default/debugmem
+3848717427 468 ../functions/default/echo
+3483503147 774 ../functions/default/factor_add_primes
+3740185017 672 ../functions/default/factor_proven
+3860891309 1410 ../functions/default/format
+2809565288 1006 ../functions/default/graphcolormap
+3446126174 403 ../functions/default/graphcolors
+398680676 391 ../functions/default/help
+1229391942 562 ../functions/default/histfile
+3654358528 443 ../functions/default/histsize
+220523559 556 ../functions/default/lines
+1161338771 240 ../functions/default/linewrap
+2950182014 774 ../functions/default/log
+4131263332 249 ../functions/default/logfile
+281989783 482 ../functions/default/nbthreads
+3548328759 468 ../functions/default/new_galois_format
+852419531 1403 ../functions/default/output
+140801756 712 ../functions/default/parisize
+2366699578 712 ../functions/default/path
+1991406625 395 ../functions/default/prettyprinter
+4106670828 1738 ../functions/default/primelimit
+4187737678 1452 ../functions/default/prompt
+673656302 350 ../functions/default/prompt_cont
+3343395058 317 ../functions/default/psfile
+3790396319 438 ../functions/default/readline
+712378547 1306 ../functions/default/realprecision
+312510814 312 ../functions/default/recover
+1228873450 456 ../functions/default/secure
+699347451 257 ../functions/default/seriesprecision
+4068861417 868 ../functions/default/simplify
+3166076333 826 ../functions/default/sopath
+3837614417 832 ../functions/default/strictargs
+2150187344 472 ../functions/default/strictmatch
+563829741 477 ../functions/default/threadsize
+4240558956 932 ../functions/default/timer
+4197571579 1431 ../functions/elliptic_curves/ellL1
+1093349276 227 ../functions/elliptic_curves/elladd
+288388107 1283 ../functions/elliptic_curves/ellak
+994306239 581 ../functions/elliptic_curves/ellan
+1664571559 1126 ../functions/elliptic_curves/ellanalyticrank
+1413240744 2280 ../functions/elliptic_curves/ellap
+74509841 536 ../functions/elliptic_curves/ellbil
+515089431 958 ../functions/elliptic_curves/ellcard
+400654891 599 ../functions/elliptic_curves/ellchangecurve
+286283106 745 ../functions/elliptic_curves/ellchangepoint
+3036975212 727 ../functions/elliptic_curves/ellchangepointinv
+1622337703 620 ../functions/elliptic_curves/ellconvertname
+959851128 885 ../functions/elliptic_curves/elldivpol
+2935267458 1270 ../functions/elliptic_curves/elleisnum
+1336341633 601 ../functions/elliptic_curves/elleta
+2802844850 287 ../functions/elliptic_curves/ellfromj
+1592776600 870 ../functions/elliptic_curves/ellgenerators
+2881662361 1249 ../functions/elliptic_curves/ellglobalred
+1057201550 2793 ../functions/elliptic_curves/ellgroup
+2445648106 1082 ../functions/elliptic_curves/ellheegner
+3354629609 1166 ../functions/elliptic_curves/ellheight
+170338517 805 ../functions/elliptic_curves/ellheightmatrix
+1035396547 705 ../functions/elliptic_curves/ellidentify
+1730986466 4261 ../functions/elliptic_curves/ellinit
+240812548 602 ../functions/elliptic_curves/ellisoncurve
+3064568940 281 ../functions/elliptic_curves/ellj
+1723653689 1242 ../functions/elliptic_curves/elllocalred
+2579301528 1166 ../functions/elliptic_curves/elllog
+1621101605 661 ../functions/elliptic_curves/elllseries
+904157268 782 ../functions/elliptic_curves/ellminimalmodel
+1775278007 2358 ../functions/elliptic_curves/ellmodulareqn
+2371457085 1054 ../functions/elliptic_curves/ellmul
+2235217918 190 ../functions/elliptic_curves/ellneg
+3036504182 1786 ../functions/elliptic_curves/ellorder
+712202039 307 ../functions/elliptic_curves/ellordinate
+722101610 1172 ../functions/elliptic_curves/ellperiods
+1794297368 2153 ../functions/elliptic_curves/ellpointtoz
+731455575 162 ../functions/elliptic_curves/ellpow
+1097874931 763 ../functions/elliptic_curves/ellrootno
+1004846089 1809 ../functions/elliptic_curves/ellsearch
+2855813971 1140 ../functions/elliptic_curves/ellsigma
+3595576828 241 ../functions/elliptic_curves/ellsub
+3847829045 1085 ../functions/elliptic_curves/elltaniyama
+463127701 362 ../functions/elliptic_curves/elltatepairing
+4013058914 1362 ../functions/elliptic_curves/elltors
+2219345950 318 ../functions/elliptic_curves/ellweilpairing
+2456249932 1334 ../functions/elliptic_curves/ellwp
+36981238 1480 ../functions/elliptic_curves/ellzeta
+1222568733 758 ../functions/elliptic_curves/ellztopoint
+1815276599 5553 ../functions/elliptic_curves/genus2red
+1668730040 123 ../functions/gp2c/DEBUGLEVEL
+977116536 393 ../functions/gp2c/clone
+4282475994 385 ../functions/gp2c/copy
+3783251634 108 ../functions/gp2c/unclone
+1891743718 82 ../functions/gp2c_internal/_avma
+2668997632 759 ../functions/gp2c_internal/_badtype
+118976974 2945 ../functions/gp2c_internal/_cast
+2617906549 160 ../functions/gp2c_internal/_cgetg
+1229532743 478 ../functions/gp2c_internal/_const
+1013746615 244 ../functions/gp2c_internal/_formatcode
+2727635652 193 ../functions/gp2c_internal/_gerepileall
+1155278703 344 ../functions/gp2c_internal/_gerepileupto
+2948757155 90 ../functions/gp2c_internal/_maxprime
+1600216421 227 ../functions/gp2c_internal/_stack_lim
+667601403 171 ../functions/gp2c_internal/_strtoclosure
+445566870 730 ../functions/gp2c_internal/_tovec
+918090833 2108 ../functions/gp2c_internal/_typedef
+2426792003 312 ../functions/gp2c_internal/_wrap
+1104882680 596 ../functions/graphic/plot
+165946816 531 ../functions/graphic/plotbox
+2126934426 451 ../functions/graphic/plotclip
+3520235070 631 ../functions/graphic/plotcolor
+610963136 883 ../functions/graphic/plotcopy
+663081319 276 ../functions/graphic/plotcursor
+2244201152 896 ../functions/graphic/plotdraw
+11351953 5248 ../functions/graphic/ploth
+755446296 758 ../functions/graphic/plothraw
+2127523847 763 ../functions/graphic/plothsizes
+917159818 1275 ../functions/graphic/plotinit
+1507686506 430 ../functions/graphic/plotkill
+2696127875 1249 ../functions/graphic/plotlines
+2711875301 574 ../functions/graphic/plotlinetype
+2326865526 226 ../functions/graphic/plotmove
+3539834349 912 ../functions/graphic/plotpoints
+607956083 364 ../functions/graphic/plotpointsize
+774343927 553 ../functions/graphic/plotpointtype
+1344949581 527 ../functions/graphic/plotrbox
+2776236072 577 ../functions/graphic/plotrecth
+1464208282 948 ../functions/graphic/plotrecthraw
+140592524 479 ../functions/graphic/plotrline
+1506919208 398 ../functions/graphic/plotrmove
+1566746629 434 ../functions/graphic/plotrpoint
+1103920392 743 ../functions/graphic/plotscale
+2541689096 920 ../functions/graphic/plotstring
+3542751079 547 ../functions/graphic/psdraw
+3906960811 340 ../functions/graphic/psploth
+1699338007 340 ../functions/graphic/psplothraw
+1413077018 2361 ../functions/linear_algebra/algdep
+1562845233 2766 ../functions/linear_algebra/charpoly
+3926371483 2388 ../functions/linear_algebra/concat
+1496930793 912 ../functions/linear_algebra/forqfvec
+1135559220 2367 ../functions/linear_algebra/lindep
+1885213010 438 ../functions/linear_algebra/listcreate
+3413287768 475 ../functions/linear_algebra/listinsert
+371937160 368 ../functions/linear_algebra/listkill
+2352976546 526 ../functions/linear_algebra/listpop
+1191932916 818 ../functions/linear_algebra/listput
+884227144 781 ../functions/linear_algebra/listsort
+4182706108 1104 ../functions/linear_algebra/matadjoint
+2523670318 202 ../functions/linear_algebra/matcompanion
+3032582234 2098 ../functions/linear_algebra/matconcat
+486768866 1184 ../functions/linear_algebra/matdet
+4230110372 1044 ../functions/linear_algebra/matdetint
+3118559761 594 ../functions/linear_algebra/matdiagonal
+2742450656 1427 ../functions/linear_algebra/mateigen
+2715004006 710 ../functions/linear_algebra/matfrobenius
+1006778180 243 ../functions/linear_algebra/mathess
+1459166046 263 ../functions/linear_algebra/mathilbert
+2893696448 4357 ../functions/linear_algebra/mathnf
+3195530342 794 ../functions/linear_algebra/mathnfmod
+3545461775 636 ../functions/linear_algebra/mathnfmodid
+1353085894 335 ../functions/linear_algebra/mathouseholder
+229723112 196 ../functions/linear_algebra/matid
+3939387398 716 ../functions/linear_algebra/matimage
+1659013314 551 ../functions/linear_algebra/matimagecompl
+2979739119 519 ../functions/linear_algebra/matindexrank
+372964243 648 ../functions/linear_algebra/matintersect
+1920196350 820 ../functions/linear_algebra/matinverseimage
+543305113 230 ../functions/linear_algebra/matisdiagonal
+3529997100 775 ../functions/linear_algebra/matker
+3907458254 868 ../functions/linear_algebra/matkerint
+1800646688 424 ../functions/linear_algebra/matmuldiagonal
+2929869545 432 ../functions/linear_algebra/matmultodiagonal
+3703545803 453 ../functions/linear_algebra/matpascal
+3982066657 941 ../functions/linear_algebra/matqr
+4135328056 138 ../functions/linear_algebra/matrank
+2190495821 621 ../functions/linear_algebra/matrix
+4051332933 1509 ../functions/linear_algebra/matrixqz
+2001534037 349 ../functions/linear_algebra/matsize
+217283128 1574 ../functions/linear_algebra/matsnf
+1385960588 493 ../functions/linear_algebra/matsolve
+997433783 1442 ../functions/linear_algebra/matsolvemod
+3658421602 866 ../functions/linear_algebra/matsupplement
+4081660555 207 ../functions/linear_algebra/mattranspose
+833948487 310 ../functions/linear_algebra/minpoly
+1331290943 858 ../functions/linear_algebra/norml2
+1423063517 1198 ../functions/linear_algebra/normlp
+306041510 1038 ../functions/linear_algebra/qfauto
+2469955322 783 ../functions/linear_algebra/qfautoexport
+3205643715 970 ../functions/linear_algebra/qfbil
+2663304455 1022 ../functions/linear_algebra/qfgaussred
+2089217354 1080 ../functions/linear_algebra/qfisom
+1265479323 1173 ../functions/linear_algebra/qfisominit
+4164538816 878 ../functions/linear_algebra/qfjacobi
+2738765226 3148 ../functions/linear_algebra/qflll
+2571047474 1825 ../functions/linear_algebra/qflllgram
+1151173386 5570 ../functions/linear_algebra/qfminim
+1309940214 1092 ../functions/linear_algebra/qfnorm
+884649199 631 ../functions/linear_algebra/qfperfection
+3388145892 1110 ../functions/linear_algebra/qfrep
+182597421 360 ../functions/linear_algebra/qfsign
+3023808062 820 ../functions/linear_algebra/seralgdep
+3338722377 634 ../functions/linear_algebra/setbinop
+2553744756 320 ../functions/linear_algebra/setintersect
+2037144827 574 ../functions/linear_algebra/setisset
+2943622176 366 ../functions/linear_algebra/setminus
+3876473219 1671 ../functions/linear_algebra/setsearch
+1510185044 290 ../functions/linear_algebra/setunion
+2343012690 397 ../functions/linear_algebra/trace
+1009255636 2824 ../functions/linear_algebra/vecextract
+4066532264 1527 ../functions/linear_algebra/vecsearch
+2127481713 4246 ../functions/linear_algebra/vecsort
+975438261 189 ../functions/linear_algebra/vecsum
+3969360427 869 ../functions/linear_algebra/vector
+2820295330 518 ../functions/linear_algebra/vectorsmall
+864292486 288 ../functions/linear_algebra/vectorv
+2163373123 136 ../functions/member_functions/a1
+3140155039 136 ../functions/member_functions/a2
+2912778027 136 ../functions/member_functions/a3
+3432772391 136 ../functions/member_functions/a4
+1592124589 137 ../functions/member_functions/a6
+3865780186 90 ../functions/member_functions/area
+2455216452 137 ../functions/member_functions/b2
+3948972502 136 ../functions/member_functions/b4
+1332019359 143 ../functions/member_functions/b6
+67273382 136 ../functions/member_functions/b8
+3822771829 195 ../functions/member_functions/bid
+2143476547 221 ../functions/member_functions/bnf
+175549204 136 ../functions/member_functions/c4
+655992956 136 ../functions/member_functions/c6
+2702710967 266 ../functions/member_functions/clgp
+1828862026 96 ../functions/member_functions/codiff
+3653427790 286 ../functions/member_functions/cyc
+71312838 199 ../functions/member_functions/diff
+3827599499 249 ../functions/member_functions/disc
+979383943 128 ../functions/member_functions/e
+958863332 87 ../functions/member_functions/eta
+1589707435 127 ../functions/member_functions/f
+3436115540 229 ../functions/member_functions/fu
+2707998417 90 ../functions/member_functions/futu
+2100492099 327 ../functions/member_functions/gen
+778199454 187 ../functions/member_functions/group
+1590134045 204 ../functions/member_functions/index
+788494505 132 ../functions/member_functions/j
+2438121695 87 ../functions/member_functions/mod
+2859016160 176 ../functions/member_functions/nf
+3500876418 280 ../functions/member_functions/no
+22232489 93 ../functions/member_functions/omega
+3263892110 151 ../functions/member_functions/orders
+3555377828 230 ../functions/member_functions/p
+1896741290 242 ../functions/member_functions/pol
+1365247405 96 ../functions/member_functions/polabs
+2389381105 189 ../functions/member_functions/r1
+2681820181 189 ../functions/member_functions/r2
+3310137925 236 ../functions/member_functions/reg
+4264971167 254 ../functions/member_functions/roots
+3731349347 194 ../functions/member_functions/sign
+131338638 142 ../functions/member_functions/t2
+482533715 90 ../functions/member_functions/tate
+2008126926 133 ../functions/member_functions/tu
+2066290395 90 ../functions/member_functions/tufu
+1665968088 171 ../functions/member_functions/zk
+2924685257 142 ../functions/member_functions/zkst
+817575908 1451 ../functions/number_fields/bnfcertify
+654416520 1799 ../functions/number_fields/bnfcompress
+1086935842 498 ../functions/number_fields/bnfdecodemodule
+43800451 5200 ../functions/number_fields/bnfinit
+476507862 895 ../functions/number_fields/bnfisintnorm
+2795798373 1520 ../functions/number_fields/bnfisnorm
+2303255407 2749 ../functions/number_fields/bnfisprincipal
+1025672292 530 ../functions/number_fields/bnfissunit
+3277737232 1587 ../functions/number_fields/bnfisunit
+3303533017 819 ../functions/number_fields/bnfnarrow
+30185140 954 ../functions/number_fields/bnfsignunit
+2219572014 1154 ../functions/number_fields/bnfsunit
+228098176 2890 ../functions/number_fields/bnrL1
+2672702749 1215 ../functions/number_fields/bnrclassno
+826486578 1183 ../functions/number_fields/bnrclassnolist
+1390393093 1044 ../functions/number_fields/bnrconductor
+2388880686 434 ../functions/number_fields/bnrconductorofchar
+4134011564 1456 ../functions/number_fields/bnrdisc
+3292276537 2779 ../functions/number_fields/bnrdisclist
+299142429 2215 ../functions/number_fields/bnrinit
+1297085953 549 ../functions/number_fields/bnrisconductor
+4098267331 1306 ../functions/number_fields/bnrisprincipal
+1309169618 1461 ../functions/number_fields/bnrrootnumber
+1162900897 2256 ../functions/number_fields/bnrstark
+653819746 346 ../functions/number_fields/dirzetak
+1635227253 1321 ../functions/number_fields/factornf
+1651383721 1335 ../functions/number_fields/galoisexport
+1065509842 1919 ../functions/number_fields/galoisfixedfield
+2282821753 1518 ../functions/number_fields/galoisgetpol
+690345336 1105 ../functions/number_fields/galoisidentify
+1065659861 3871 ../functions/number_fields/galoisinit
+474428460 542 ../functions/number_fields/galoisisabelian
+3491431523 559 ../functions/number_fields/galoisisnormal
+999481307 869 ../functions/number_fields/galoispermtopol
+415883189 2573 ../functions/number_fields/galoissubcyclo
+1739582821 432 ../functions/number_fields/galoissubfields
+1103908484 810 ../functions/number_fields/galoissubgroups
+4041888397 1330 ../functions/number_fields/idealadd
+2595771256 868 ../functions/number_fields/idealaddtoone
+988771264 1290 ../functions/number_fields/idealappr
+819005822 685 ../functions/number_fields/idealchinese
+2736419638 376 ../functions/number_fields/idealcoprime
+1528977577 990 ../functions/number_fields/idealdiv
+133283560 454 ../functions/number_fields/idealfactor
+706829025 1831 ../functions/number_fields/idealfactorback
+901201287 1024 ../functions/number_fields/idealfrobenius
+3001711766 2079 ../functions/number_fields/idealhnf
+2234579433 753 ../functions/number_fields/idealintersect
+3755090162 414 ../functions/number_fields/idealinv
+4061325327 2136 ../functions/number_fields/ideallist
+3313133009 1746 ../functions/number_fields/ideallistarch
+1430471263 1066 ../functions/number_fields/ideallog
+1577350712 385 ../functions/number_fields/idealmin
+392441553 1275 ../functions/number_fields/idealmul
+2213617432 214 ../functions/number_fields/idealnorm
+3621386739 392 ../functions/number_fields/idealnumden
+590254486 900 ../functions/number_fields/idealpow
+4185462448 1955 ../functions/number_fields/idealprimedec
+1421736270 775 ../functions/number_fields/idealprincipalunits
+1446079677 1715 ../functions/number_fields/idealramgroups
+477008975 2910 ../functions/number_fields/idealred
+2275219186 2198 ../functions/number_fields/idealstar
+2606691333 1306 ../functions/number_fields/idealtwoelt
+1614130949 338 ../functions/number_fields/idealval
+2450987406 330 ../functions/number_fields/matalgtobasis
+4263225345 330 ../functions/number_fields/matbasistoalg
+370962582 1543 ../functions/number_fields/modreverse
+923642746 645 ../functions/number_fields/newtonpoly
+74387094 580 ../functions/number_fields/nfalgtobasis
+1254909946 5186 ../functions/number_fields/nfbasis
+794927693 569 ../functions/number_fields/nfbasistoalg
+1941191205 887 ../functions/number_fields/nfcertify
+1793444342 342 ../functions/number_fields/nfdetint
+3831355376 1546 ../functions/number_fields/nfdisc
+2893425002 219 ../functions/number_fields/nfeltadd
+3704437998 223 ../functions/number_fields/nfeltdiv
+64595969 403 ../functions/number_fields/nfeltdiveuc
+2739572936 524 ../functions/number_fields/nfeltdivmodpr
+2441182165 345 ../functions/number_fields/nfeltdivrem
+1301142194 423 ../functions/number_fields/nfeltmod
+1895398557 223 ../functions/number_fields/nfeltmul
+366144574 521 ../functions/number_fields/nfeltmulmodpr
+1405889410 145 ../functions/number_fields/nfeltnorm
+3943733724 354 ../functions/number_fields/nfeltpow
+3035074752 512 ../functions/number_fields/nfeltpowmodpr
+3760170244 359 ../functions/number_fields/nfeltreduce
+3125536475 519 ../functions/number_fields/nfeltreducemodpr
+2932345806 150 ../functions/number_fields/nfelttrace
+3293746003 460 ../functions/number_fields/nfeltval
+1589700260 1566 ../functions/number_fields/nffactor
+973653093 818 ../functions/number_fields/nffactorback
+488334419 1043 ../functions/number_fields/nffactormod
+973104481 2219 ../functions/number_fields/nfgaloisapply
+3907195035 2623 ../functions/number_fields/nfgaloisconj
+624126709 736 ../functions/number_fields/nfhilbert
+2072610218 507 ../functions/number_fields/nfhnf
+868333029 539 ../functions/number_fields/nfhnfmod
+1082558102 7702 ../functions/number_fields/nfinit
+1575169407 243 ../functions/number_fields/nfisideal
+196768795 980 ../functions/number_fields/nfisincl
+1085970401 276 ../functions/number_fields/nfisisom
+2783715111 448 ../functions/number_fields/nfkermodpr
+3646082924 433 ../functions/number_fields/nfmodprinit
+987402003 612 ../functions/number_fields/nfnewprec
+770708172 1418 ../functions/number_fields/nfroots
+967762132 1587 ../functions/number_fields/nfrootsof1
+503060488 1181 ../functions/number_fields/nfsnf
+1005488227 839 ../functions/number_fields/nfsolvemodpr
+2732061843 972 ../functions/number_fields/nfsubfields
+1252868226 3254 ../functions/number_fields/polcompositum
+1565615061 4266 ../functions/number_fields/polgalois
+754868682 1743 ../functions/number_fields/polred
+1630097175 4003 ../functions/number_fields/polredabs
+2588514985 2291 ../functions/number_fields/polredbest
+2107242093 512 ../functions/number_fields/polredord
+2575474898 466 ../functions/number_fields/poltschirnhaus
+963192925 481 ../functions/number_fields/rnfalgtobasis
+2597801200 778 ../functions/number_fields/rnfbasis
+2039453614 422 ../functions/number_fields/rnfbasistoalg
+1430816044 577 ../functions/number_fields/rnfcharpoly
+332875321 1089 ../functions/number_fields/rnfconductor
+493784289 3512 ../functions/number_fields/rnfdedekind
+2250737713 235 ../functions/number_fields/rnfdet
+3226307783 779 ../functions/number_fields/rnfdisc
+2002861846 882 ../functions/number_fields/rnfeltabstorel
+3119689782 909 ../functions/number_fields/rnfeltdown
+2065307501 537 ../functions/number_fields/rnfeltnorm
+4001592359 757 ../functions/number_fields/rnfeltreltoabs
+1118338923 516 ../functions/number_fields/rnfelttrace
+205183268 667 ../functions/number_fields/rnfeltup
+4138499114 2180 ../functions/number_fields/rnfequation
+3457630168 562 ../functions/number_fields/rnfhnfbasis
+3710921157 1501 ../functions/number_fields/rnfidealabstorel
+3524519381 482 ../functions/number_fields/rnfidealdown
+2124882618 471 ../functions/number_fields/rnfidealhnf
+2972910198 410 ../functions/number_fields/rnfidealmul
+1270402441 572 ../functions/number_fields/rnfidealnormabs
+190446714 445 ../functions/number_fields/rnfidealnormrel
+295860721 993 ../functions/number_fields/rnfidealreltoabs
+3572608050 463 ../functions/number_fields/rnfidealtwoelt
+460503870 915 ../functions/number_fields/rnfidealup
+890141568 3847 ../functions/number_fields/rnfinit
+3000009520 440 ../functions/number_fields/rnfisabelian
+301910451 511 ../functions/number_fields/rnfisfree
+788695769 2247 ../functions/number_fields/rnfisnorm
+2615116728 1060 ../functions/number_fields/rnfisnorminit
+1544630009 1346 ../functions/number_fields/rnfkummer
+1089289418 648 ../functions/number_fields/rnflllgram
+2727680788 1102 ../functions/number_fields/rnfnormgroup
+1425929762 773 ../functions/number_fields/rnfpolred
+2338701732 1880 ../functions/number_fields/rnfpolredabs
+2550913263 1973 ../functions/number_fields/rnfpolredbest
+2300324313 858 ../functions/number_fields/rnfpseudobasis
+4120760786 981 ../functions/number_fields/rnfsteinitz
+3857332923 2021 ../functions/number_fields/subgrouplist
+3758453012 2039 ../functions/number_fields/zetak
+4294924339 1394 ../functions/number_fields/zetakinit
+215042210 749 ../functions/number_theoretical/addprimes
+143929946 2147 ../functions/number_theoretical/bestappr
+3704641807 1922 ../functions/number_theoretical/bestapprPade
+371659487 161 ../functions/number_theoretical/bezout
+3857964875 371 ../functions/number_theoretical/bigomega
+590656338 526 ../functions/number_theoretical/binomial
+3062219339 1967 ../functions/number_theoretical/chinese
+686535871 1031 ../functions/number_theoretical/content
+1721333311 2932 ../functions/number_theoretical/contfrac
+2762680277 1347 ../functions/number_theoretical/contfracpnqn
+1215033860 661 ../functions/number_theoretical/core
+498340475 1129 ../functions/number_theoretical/coredisc
+4003493065 334 ../functions/number_theoretical/dirdiv
+2123078772 1158 ../functions/number_theoretical/direuler
+418145044 689 ../functions/number_theoretical/dirmul
+3382938550 627 ../functions/number_theoretical/divisors
+3233091324 525 ../functions/number_theoretical/eulerphi
+3437351073 6295 ../functions/number_theoretical/factor
+3273804125 1262 ../functions/number_theoretical/factorback
+1023766457 729 ../functions/number_theoretical/factorcantor
+3573143606 1568 ../functions/number_theoretical/factorff
+4089779703 347 ../functions/number_theoretical/factorial
+3706897439 2084 ../functions/number_theoretical/factorint
+4224377394 813 ../functions/number_theoretical/factormod
+2120464340 1834 ../functions/number_theoretical/ffgen
+4176254427 703 ../functions/number_theoretical/ffinit
+3488041316 1370 ../functions/number_theoretical/fflog
+3597614832 652 ../functions/number_theoretical/ffnbirred
+3573775947 860 ../functions/number_theoretical/fforder
+1640799602 1120 ../functions/number_theoretical/ffprimroot
+641658465 176 ../functions/number_theoretical/fibonacci
+1168948070 2303 ../functions/number_theoretical/gcd
+80527233 1123 ../functions/number_theoretical/gcdext
+514753190 536 ../functions/number_theoretical/hilbert
+1861985508 377 ../functions/number_theoretical/isfundamental
+4121111175 479 ../functions/number_theoretical/ispolygonal
+3013969901 1221 ../functions/number_theoretical/ispower
+2938323692 460 ../functions/number_theoretical/ispowerful
+3017578398 2646 ../functions/number_theoretical/isprime
+1951470040 472 ../functions/number_theoretical/isprimepower
+2569893220 1562 ../functions/number_theoretical/ispseudoprime
+3271459303 1518 ../functions/number_theoretical/issquare
+389293029 301 ../functions/number_theoretical/issquarefree
+3038253040 442 ../functions/number_theoretical/istotient
+756622456 758 ../functions/number_theoretical/kronecker
+3492197658 1446 ../functions/number_theoretical/lcm
+2023281030 968 ../functions/number_theoretical/logint
+2160606288 190 ../functions/number_theoretical/moebius
+4254054828 481 ../functions/number_theoretical/nextprime
+2039825979 524 ../functions/number_theoretical/numbpart
+1077510865 221 ../functions/number_theoretical/numdiv
+3812027075 362 ../functions/number_theoretical/omega
+2724935271 2116 ../functions/number_theoretical/partitions
+3412010667 1219 ../functions/number_theoretical/polrootsff
+805028202 497 ../functions/number_theoretical/precprime
+3693765799 270 ../functions/number_theoretical/prime
+429279699 442 ../functions/number_theoretical/primepi
+530053972 619 ../functions/number_theoretical/primes
+2798669539 2553 ../functions/number_theoretical/qfbclassno
+3695050800 431 ../functions/number_theoretical/qfbcompraw
+1940983238 339 ../functions/number_theoretical/qfbhclassno
+973965715 902 ../functions/number_theoretical/qfbnucomp
+2697822130 368 ../functions/number_theoretical/qfbnupow
+3393949890 338 ../functions/number_theoretical/qfbpowraw
+3859788640 639 ../functions/number_theoretical/qfbprimeform
+760904670 1434 ../functions/number_theoretical/qfbred
+2106514910 916 ../functions/number_theoretical/qfbsolve
+3231541093 2938 ../functions/number_theoretical/quadclassunit
+411309927 217 ../functions/number_theoretical/quaddisc
+4137872200 445 ../functions/number_theoretical/quadgen
+1926712746 577 ../functions/number_theoretical/quadhilbert
+1522706503 423 ../functions/number_theoretical/quadpoly
+376107392 612 ../functions/number_theoretical/quadray
+3392391708 376 ../functions/number_theoretical/quadregulator
+426124966 575 ../functions/number_theoretical/quadunit
+1182031813 521 ../functions/number_theoretical/randomprime
+3397278150 476 ../functions/number_theoretical/removeprimes
+405726717 467 ../functions/number_theoretical/sigma
+2721939469 441 ../functions/number_theoretical/sqrtint
+504793252 509 ../functions/number_theoretical/sqrtnint
+3464175627 1336 ../functions/number_theoretical/stirling
+1102505332 332 ../functions/number_theoretical/sumdedekind
+1060747982 357 ../functions/number_theoretical/sumdigits
+3270801584 2239 ../functions/number_theoretical/zncoppersmith
+581186171 2248 ../functions/number_theoretical/znlog
+2737149070 769 ../functions/number_theoretical/znorder
+247007143 690 ../functions/number_theoretical/znprimroot
+3219363957 1133 ../functions/number_theoretical/znstar
+280573390 1171 ../functions/operators/cmp
+2333676171 1120 ../functions/operators/divrem
+1311333090 875 ../functions/operators/lex
+3424443404 548 ../functions/operators/max
+1305994321 548 ../functions/operators/min
+2356147055 550 ../functions/operators/shift
+1941910361 424 ../functions/operators/shiftmul
+2474408050 291 ../functions/operators/sign
+2122378256 865 ../functions/operators/vecmax
+3459801581 867 ../functions/operators/vecmin
+3728482663 863 ../functions/polynomials/O
+3840401116 192 ../functions/polynomials/bezoutres
+542241653 848 ../functions/polynomials/deriv
+3783180479 1942 ../functions/polynomials/diffop
+2644110396 1484 ../functions/polynomials/eval
+997399994 1498 ../functions/polynomials/factorpadic
+1249194359 1397 ../functions/polynomials/intformal
+986511628 730 ../functions/polynomials/padicappr
+2143739774 1841 ../functions/polynomials/padicfields
+2394852716 1226 ../functions/polynomials/polchebyshev
+791830338 1027 ../functions/polynomials/polcoeff
+3865942159 896 ../functions/polynomials/polcyclo
+3980671021 912 ../functions/polynomials/polcyclofactors
+2540383469 797 ../functions/polynomials/poldegree
+1629390302 482 ../functions/polynomials/poldisc
+1885087402 528 ../functions/polynomials/poldiscreduced
+838056086 250 ../functions/polynomials/polgraeffe
+2695444232 1133 ../functions/polynomials/polhensellift
+2042242352 534 ../functions/polynomials/polhermite
+3350225564 749 ../functions/polynomials/polinterpolate
+1007436899 437 ../functions/polynomials/poliscyclo
+4075425120 497 ../functions/polynomials/poliscycloprod
+2559408188 460 ../functions/polynomials/polisirreducible
+4266292747 609 ../functions/polynomials/pollead
+693228044 460 ../functions/polynomials/pollegendre
+803988097 240 ../functions/polynomials/polrecip
+1033208739 1192 ../functions/polynomials/polresultant
+3717931312 827 ../functions/polynomials/polresultantext
+2456631457 760 ../functions/polynomials/polroots
+3629305914 706 ../functions/polynomials/polrootsmod
+943111125 1016 ../functions/polynomials/polrootspadic
+3644766318 523 ../functions/polynomials/polsturm
+2495767096 741 ../functions/polynomials/polsubcyclo
+1076716398 656 ../functions/polynomials/polsylvestermatrix
+368684839 285 ../functions/polynomials/polsym
+1218299547 184 ../functions/polynomials/poltchebi
+2974829760 714 ../functions/polynomials/polzagier
+3084901600 333 ../functions/polynomials/serconvol
+678736819 330 ../functions/polynomials/serlaplace
+1323530505 398 ../functions/polynomials/serreverse
+1472934463 1070 ../functions/polynomials/subst
+1374933613 1103 ../functions/polynomials/substpol
+2817986031 662 ../functions/polynomials/substvec
+2581224437 1040 ../functions/polynomials/sumformal
+226826326 702 ../functions/polynomials/taylor
+1270801577 2607 ../functions/polynomials/thue
+3735388021 992 ../functions/polynomials/thueinit
+3300553752 136 ../functions/programming/_eval_mnemonic
+3140964717 1110 ../functions/programming/addhelp
+983874885 1936 ../functions/programming/alarm
+164541180 1843 ../functions/programming/alias
+2897255199 2301 ../functions/programming/allocatemem
+3731392400 1596 ../functions/programming/apply
+1649867724 490 ../functions/programming/break
+3619971697 685 ../functions/programming/breakpoint
+1655863837 273 ../functions/programming/dbg_down
+2271483609 697 ../functions/programming/dbg_err
+4050270931 329 ../functions/programming/dbg_up
+902450922 470 ../functions/programming/dbg_x
+673969152 1794 ../functions/programming/default
+3384828003 243 ../functions/programming/errname
+2772696481 674 ../functions/programming/error
+1852833943 400 ../functions/programming/extern
+2658094063 458 ../functions/programming/externstr
+1154955545 285 ../functions/programming/for
+1548115268 1490 ../functions/programming/forcomposite
+1930174191 1067 ../functions/programming/fordiv
+1011325593 915 ../functions/programming/forell
+3742891725 2400 ../functions/programming/forpart
+523389040 2355 ../functions/programming/forprime
+4097689279 583 ../functions/programming/forstep
+3286177718 2061 ../functions/programming/forsubgroup
+1526035725 1339 ../functions/programming/forvec
+1575287630 347 ../functions/programming/getabstime
+748638207 252 ../functions/programming/getenv
+4278770330 361 ../functions/programming/getheap
+3575456435 442 ../functions/programming/getrand
+4109487323 285 ../functions/programming/getstack
+3623950775 385 ../functions/programming/gettime
+551521830 165 ../functions/programming/global
+1141761031 2392 ../functions/programming/if
+3720775545 10465 ../functions/programming/iferr
+2051796286 494 ../functions/programming/inline
+1949270646 754 ../functions/programming/input
+490792963 3434 ../functions/programming/install
+2467288771 1469 ../functions/programming/kill
+3065531026 125 ../functions/programming/local
+2557836032 115 ../functions/programming/my
+1218387207 521 ../functions/programming/next
+315903861 794 ../functions/programming/parapply
+407023495 541 ../functions/programming/pareval
+168871054 1080 ../functions/programming/parfor
+2197030888 896 ../functions/programming/parforprime
+325469141 592 ../functions/programming/parselect
+2300142208 700 ../functions/programming/parsum
+3003736710 744 ../functions/programming/parvector
+1811616303 333 ../functions/programming/print
+3073867138 437 ../functions/programming/print1
+3605500215 7805 ../functions/programming/printf
+3800076878 384 ../functions/programming/printsep
+2686332207 415 ../functions/programming/printsep1
+1208643084 604 ../functions/programming/printtex
+203110110 447 ../functions/programming/quit
+1497408310 835 ../functions/programming/read
+2733156182 394 ../functions/programming/readstr
+2278186691 967 ../functions/programming/readvec
+1182439920 287 ../functions/programming/return
+1550019273 2768 ../functions/programming/select
+3452942428 504 ../functions/programming/setrand
+3720197410 447 ../functions/programming/system
+3717803513 2317 ../functions/programming/trap
+722695522 608 ../functions/programming/type
+419976951 184 ../functions/programming/uninline
+3467212281 421 ../functions/programming/until
+2538936335 2381 ../functions/programming/version
+3287864719 509 ../functions/programming/warning
+1594674442 434 ../functions/programming/whatnow
+3282345452 395 ../functions/programming/while
+559370645 305 ../functions/programming/write
+3083331806 313 ../functions/programming/write1
+1788791396 1947 ../functions/programming/writebin
+3395311360 248 ../functions/programming/writetex
+4162161133 1023 ../functions/sums/derivnum
+847633432 836 ../functions/sums/intcirc
+675879057 862 ../functions/sums/intfouriercos
+3481939193 904 ../functions/sums/intfourierexp
+3947097796 857 ../functions/sums/intfouriersin
+3096883269 1556 ../functions/sums/intfuncinit
+3456264235 2300 ../functions/sums/intlaplaceinv
+2519244525 2071 ../functions/sums/intmellininv
+1440289081 2077 ../functions/sums/intmellininvshort
+528809183 12017 ../functions/sums/intnum
+1762228811 1941 ../functions/sums/intnuminit
+3061973932 975 ../functions/sums/intnuminitgen
+2361868629 2774 ../functions/sums/intnumromb
+400964355 342 ../functions/sums/intnumstep
+3675594259 1432 ../functions/sums/prod
+238922829 499 ../functions/sums/prodeuler
+4010920763 901 ../functions/sums/prodinf
+3627520822 712 ../functions/sums/solve
+1209900028 648 ../functions/sums/sum
+2063081340 2085 ../functions/sums/sumalt
+2396474070 511 ../functions/sums/sumdiv
+3260846671 413 ../functions/sums/sumdivmult
+4053719439 1152 ../functions/sums/suminf
+2896834138 4953 ../functions/sums/sumnum
+1929035087 2463 ../functions/sums/sumnumalt
+763341576 559 ../functions/sums/sumnuminit
+233495912 1413 ../functions/sums/sumpos
+3764491866 847 ../functions/symbolic_operators/add
+3353925834 892 ../functions/symbolic_operators/adde
+2224582694 150 ../functions/symbolic_operators/and
+807393942 311 ../functions/symbolic_operators/call
+2207444170 1092 ../functions/symbolic_operators/coeff
+2209173128 720 ../functions/symbolic_operators/compr
+1114647704 290 ../functions/symbolic_operators/concat
+3074747166 189 ../functions/symbolic_operators/deriv
+1369992067 682 ../functions/symbolic_operators/div
+2294767635 742 ../functions/symbolic_operators/dive
+1770025917 530 ../functions/symbolic_operators/divent
+534012629 385 ../functions/symbolic_operators/divente
+1255088825 253 ../functions/symbolic_operators/divround
+857501574 337 ../functions/symbolic_operators/divrounde
+1716660983 1368 ../functions/symbolic_operators/eq
+57471919 155 ../functions/symbolic_operators/fact
+2828464303 790 ../functions/symbolic_operators/ge
+4062103410 781 ../functions/symbolic_operators/gt
+928796544 231 ../functions/symbolic_operators/hist
+4154293089 124 ../functions/symbolic_operators/id
+541828836 846 ../functions/symbolic_operators/le
+551395660 778 ../functions/symbolic_operators/lt
+4095498863 516 ../functions/symbolic_operators/mm
+1103955639 457 ../functions/symbolic_operators/mod
+481421093 476 ../functions/symbolic_operators/mode
+3747397195 784 ../functions/symbolic_operators/mul
+328650357 839 ../functions/symbolic_operators/mule
+1979227420 1160 ../functions/symbolic_operators/ne
+3811200823 292 ../functions/symbolic_operators/neg
+4083690638 171 ../functions/symbolic_operators/not
+4250259204 164 ../functions/symbolic_operators/or
+331309622 293 ../functions/symbolic_operators/pl
+3007025652 276 ../functions/symbolic_operators/pound
+3891617857 897 ../functions/symbolic_operators/pow
+3484290447 532 ../functions/symbolic_operators/pp
+350299153 195 ../functions/symbolic_operators/range
+1327651723 238 ../functions/symbolic_operators/shiftl
+2222950945 363 ../functions/symbolic_operators/shiftle
+540758560 300 ../functions/symbolic_operators/shiftr
+3408057925 372 ../functions/symbolic_operators/shiftre
+3157788552 440 ../functions/symbolic_operators/slice
+1170897255 771 ../functions/symbolic_operators/sub
+3687286192 892 ../functions/symbolic_operators/sube
+3333748126 198 ../functions/symbolic_operators/trans
+278423019 370 ../functions/transcendental/Catalan
+3203114013 323 ../functions/transcendental/Euler
+4128767667 143 ../functions/transcendental/I
+835661978 301 ../functions/transcendental/Pi
+3536365179 841 ../functions/transcendental/abs
+2484349986 482 ../functions/transcendental/acos
+2034584490 370 ../functions/transcendental/acosh
+186570540 526 ../functions/transcendental/agm
+2673702082 198 ../functions/transcendental/arg
+2799632495 484 ../functions/transcendental/asin
+3069830700 366 ../functions/transcendental/asinh
+1571898858 473 ../functions/transcendental/atan
+2789708507 335 ../functions/transcendental/atanh
+3784737751 311 ../functions/transcendental/bernfrac
+2362022768 283 ../functions/transcendental/bernpol
+1190142878 304 ../functions/transcendental/bernreal
+1159767886 728 ../functions/transcendental/bernvec
+2423860598 208 ../functions/transcendental/besselh1
+2824405762 208 ../functions/transcendental/besselh2
+391768912 364 ../functions/transcendental/besseli
+1918729913 364 ../functions/transcendental/besselj
+52495517 446 ../functions/transcendental/besseljh
+2577688153 201 ../functions/transcendental/besselk
+3275725244 201 ../functions/transcendental/besseln
+2865291733 112 ../functions/transcendental/cos
+3610629014 137 ../functions/transcendental/cosh
+3039924894 124 ../functions/transcendental/cotan
+3749138764 232 ../functions/transcendental/dilog
+1358921222 1230 ../functions/transcendental/eint1
+2742925928 351 ../functions/transcendental/erfc
+1932581236 1106 ../functions/transcendental/eta
+4186273876 387 ../functions/transcendental/exp
+3219425904 1030 ../functions/transcendental/expm1
+283840075 815 ../functions/transcendental/gamma
+2489052783 171 ../functions/transcendental/gammah
+1703809654 306 ../functions/transcendental/hyperu
+2351714423 626 ../functions/transcendental/incgam
+1394040053 409 ../functions/transcendental/incgamc
+2417873690 225 ../functions/transcendental/lambertw
+2152402485 1030 ../functions/transcendental/lngamma
+3393608858 1090 ../functions/transcendental/log
+2637087612 1600 ../functions/transcendental/polylog
+1987478852 188 ../functions/transcendental/psi
+1520878203 108 ../functions/transcendental/sin
+2704877408 133 ../functions/transcendental/sinh
+473305427 963 ../functions/transcendental/sqr
+685184966 819 ../functions/transcendental/sqrt
+213571604 1522 ../functions/transcendental/sqrtn
+1255385460 114 ../functions/transcendental/tan
+1579606196 139 ../functions/transcendental/tanh
+4246144284 278 ../functions/transcendental/teichmuller
+2713807692 227 ../functions/transcendental/theta
+1059709706 476 ../functions/transcendental/thetanullk
+2007207279 1064 ../functions/transcendental/weber
+298482603 822 ../functions/transcendental/zeta
diff --git a/src/functions/conversions/Col b/src/functions/conversions/Col
new file mode 100644
index 0000000..fdb4499
--- /dev/null
+++ b/src/functions/conversions/Col
@@ -0,0 +1,35 @@
+Function: Col
+Section: conversions
+C-Name: gtocol0
+Prototype: GD0,L,
+Help: Col(x, {n}): transforms the object x into a column vector of dimension n.
+Description:
+ (gen):vec     gtocol($1)
+Doc:
+ transforms the object $x$ into a column vector. The dimension of the
+ resulting vector can be optionally specified via the extra parameter $n$.
+
+ If $n$ is omitted or $0$, the dimension depends on the type of $x$; the
+ vector has a single component, except when $x$ is
+
+ \item a vector or a quadratic form (in which case the resulting vector
+ is simply the initial object considered as a row vector),
+
+ \item a polynomial or a power series. In the case of a polynomial, the
+ coefficients of the vector start with the leading coefficient of the
+ polynomial, while for power series only the significant coefficients are
+ taken into account, but this time by increasing order of degree.
+ In this last case, \kbd{Vec} is the reciprocal function of \kbd{Pol} and
+ \kbd{Ser} respectively,
+
+ \item a matrix (the column of row vector comprising the matrix is returned),
+
+ \item a character string (a vector of individual characters is returned).
+
+ In the last two cases (matrix and character string), $n$ is meaningless and
+ must be omitted or an error is raised. Otherwise, if $n$ is given, $0$
+ entries are appended at the end of the vector if $n > 0$, and prepended at
+ the beginning if $n < 0$. The dimension of the resulting vector is $|n|$.
+
+ Note that the function \kbd{Colrev} does not exist, use \kbd{Vecrev}.
+Variant: \fun{GEN}{gtocol}{GEN x} is also available.
diff --git a/src/functions/conversions/Colrev b/src/functions/conversions/Colrev
new file mode 100644
index 0000000..3a1b421
--- /dev/null
+++ b/src/functions/conversions/Colrev
@@ -0,0 +1,12 @@
+Function: Colrev
+Section: conversions
+C-Name: gtocolrev0
+Prototype: GD0,L,
+Help: Colrev(x, {n}): transforms the object x into a column vector of
+ dimension n in reverse order with respect to Col(x, {n}). Empty vector if x
+ is omitted.
+Description:
+ (gen):vec     gtocolrev($1)
+Doc:
+ as $\kbd{Col}(x, n)$, then reverse the result. In particular
+Variant: \fun{GEN}{gtocolrev}{GEN x} is also available.
diff --git a/src/functions/conversions/List b/src/functions/conversions/List
new file mode 100644
index 0000000..1c0387e
--- /dev/null
+++ b/src/functions/conversions/List
@@ -0,0 +1,16 @@
+Function: List
+Section: conversions
+C-Name: gtolist
+Prototype: DG
+Help: List({x=[]}): transforms the vector or list x into a list. Empty list
+ if x is omitted.
+Description:
+ ():list           listcreate()
+ (gen):list        gtolist($1)
+Doc:
+ transforms a (row or column) vector $x$ into a list, whose components are
+ the entries of $x$. Similarly for a list, but rather useless in this case.
+ For other types, creates a list with the single element $x$. Note that,
+ except when $x$ is omitted, this function creates a small memory leak; so,
+ either initialize all lists to the empty list, or use them sparingly.
+Variant: The variant \fun{GEN}{listcreate}{void} creates an empty list.
diff --git a/src/functions/conversions/Mat b/src/functions/conversions/Mat
new file mode 100644
index 0000000..049fdd4
--- /dev/null
+++ b/src/functions/conversions/Mat
@@ -0,0 +1,42 @@
+Function: Mat
+Section: conversions
+C-Name: gtomat
+Prototype: DG
+Help: Mat({x=[]}): transforms any GEN x into a matrix. Empty matrix if x is
+ omitted.
+Doc:
+ transforms the object $x$ into a matrix.
+ If $x$ is already a matrix, a copy of $x$ is created.
+ If $x$ is a row (resp. column) vector, this creates a 1-row (resp.
+ 1-column) matrix, \emph{unless} all elements are column (resp.~row) vectors
+ of the same length, in which case the vectors are concatenated sideways
+ and the associated big matrix is returned.
+ If $x$ is a binary quadratic form, creates the associated $2\times 2$
+ matrix. Otherwise, this creates a $1\times 1$ matrix containing $x$.
+
+ \bprog
+ ? Mat(x + 1)
+ %1 =
+ [x + 1]
+ ? Vec( matid(3) )
+ %2 = [[1, 0, 0]~, [0, 1, 0]~, [0, 0, 1]~]
+ ? Mat(%)
+ %3 =
+ [1 0 0]
+
+ [0 1 0]
+
+ [0 0 1]
+ ? Col( [1,2; 3,4] )
+ %4 = [[1, 2], [3, 4]]~
+ ? Mat(%)
+ %5 =
+ [1 2]
+
+ [3 4]
+ ? Mat(Qfb(1,2,3))
+ %6 =
+ [1 1]
+
+ [1 3]
+ @eprog
diff --git a/src/functions/conversions/Mod b/src/functions/conversions/Mod
new file mode 100644
index 0000000..f32ac05
--- /dev/null
+++ b/src/functions/conversions/Mod
@@ -0,0 +1,54 @@
+Function: Mod
+Section: conversions
+C-Name: gmodulo
+Prototype: GG
+Help: Mod(a,b): creates 'a modulo b'.
+Description:
+ (small, small):gen         gmodulss($1, $2)
+ (small, gen):gen           gmodulsg($1, $2)
+ (gen, gen):gen             gmodulo($1, $2)
+Doc: in its basic form, creates an intmod or a polmod $(a \mod b)$; $b$ must
+ be an integer or a polynomial. We then obtain a \typ{INTMOD} and a
+ \typ{POLMOD} respectively:
+ \bprog
+ ? t = Mod(2,17); t^8
+ %1 = Mod(1, 17)
+ ? t = Mod(x,x^2+1); t^2
+ %2 = Mod(-1, x^2+1)
+ @eprog\noindent If $a \% b$ makes sense and yields a result of the
+ appropriate type (\typ{INT} or scalar/\typ{POL}), the operation succeeds as
+ well:
+ \bprog
+ ? Mod(1/2, 5)
+ %3 = Mod(3, 5)
+ ? Mod(7 + O(3^6), 3)
+ %4 = Mod(1, 3)
+ ? Mod(Mod(1,12), 9)
+ %5 = Mod(1, 3)
+ ? Mod(1/x, x^2+1)
+ %6 = Mod(-1, x^2+1)
+ ? Mod(exp(x), x^4)
+ %7 = Mod(1/6*x^3 + 1/2*x^2 + x + 1, x^4)
+ @eprog
+ If $a$ is a complex object, ``base change'' it to $\Z/b\Z$ or $K[x]/(b)$,
+ which is equivalent to, but faster than, multiplying it by \kbd{Mod(1,b)}:
+ \bprog
+ ? Mod([1,2;3,4], 2)
+ %8 =
+ [Mod(1, 2) Mod(0, 2)]
+
+ [Mod(1, 2) Mod(0, 2)]
+ ? Mod(3*x+5, 2)
+ %9 = Mod(1, 2)*x + Mod(1, 2)
+ ? Mod(x^2 + y*x + y^3, y^2+1)
+ %10 = Mod(1, y^2 + 1)*x^2 + Mod(y, y^2 + 1)*x + Mod(-y, y^2 + 1)
+ @eprog
+
+ This function is not the same as $x$ \kbd{\%} $y$, the result of which
+ has no knowledge of the intended modulus $y$. Compare
+ \bprog
+ ? x = 4 % 5; x + 1
+ %1 = 5
+ ? x = Mod(4,5); x + 1
+ %2 = Mod(0,5)
+ @eprog
diff --git a/src/functions/conversions/Pol b/src/functions/conversions/Pol
new file mode 100644
index 0000000..f8ac736
--- /dev/null
+++ b/src/functions/conversions/Pol
@@ -0,0 +1,46 @@
+Function: Pol
+Section: conversions
+C-Name: gtopoly
+Prototype: GDn
+Description:
+ (gen,?var):pol  gtopoly($1, $2)
+Help: Pol(t,{v='x}): convert t (usually a vector or a power series) into a
+ polynomial with variable v, starting with the leading coefficient.
+Doc:
+ transforms the object $t$ into a polynomial with main variable $v$. If $t$
+ is a scalar, this gives a constant polynomial. If $t$ is a power series with
+ non-negative valuation or a rational function, the effect is similar to
+ \kbd{truncate}, i.e.~we chop off the $O(X^k)$ or compute the Euclidean
+ quotient of the numerator by the denominator, then change the main variable
+ of the result to $v$.
+
+ The main use of this function is when $t$ is a vector: it creates the
+ polynomial whose coefficients are given by $t$, with $t[1]$ being the leading
+ coefficient (which can be zero). It is much faster to evaluate
+ \kbd{Pol} on a vector of coefficients in this way, than the corresponding
+ formal expression $a_n X^n + \dots + a_0$, which is evaluated naively exactly
+ as written (linear versus quadratic time in $n$). \tet{Polrev} can be used if
+ one wants $x[1]$ to be the constant coefficient:
+ \bprog
+ ? Pol([1,2,3])
+ %1 = x^2 + 2*x + 3
+ ? Polrev([1,2,3])
+ %2 = 3*x^2 + 2*x + 1
+ @eprog\noindent
+ The reciprocal function of \kbd{Pol} (resp.~\kbd{Polrev}) is \kbd{Vec} (resp.~
+ \kbd{Vecrev}).
+ \bprog
+ ? Vec(Pol([1,2,3]))
+ %1 = [1, 2, 3]
+ ? Vecrev( Polrev([1,2,3]) )
+ %2 = [1, 2, 3]
+ @eprog\noindent
+
+ \misctitle{Warning} This is \emph{not} a substitution function. It will not
+ transform an object containing variables of higher priority than~$v$.
+ \bprog
+ ? Pol(x + y, y)
+   ***   at top-level: Pol(x+y,y)
+   ***                 ^----------
+   *** Pol: variable must have higher priority in gtopoly.
+ @eprog
diff --git a/src/functions/conversions/Polrev b/src/functions/conversions/Polrev
new file mode 100644
index 0000000..6d00394
--- /dev/null
+++ b/src/functions/conversions/Polrev
@@ -0,0 +1,26 @@
+Function: Polrev
+Section: conversions
+C-Name: gtopolyrev
+Prototype: GDn
+Description:
+ (gen,?var):pol  gtopolyrev($1, $2)
+Help: Polrev(t,{v='x}): convert t (usually a vector or a power series) into a
+ polynomial with variable v, starting with the constant term.
+Doc:
+ transform the object $t$ into a polynomial
+ with main variable $v$. If $t$ is a scalar, this gives a constant polynomial.
+ If $t$ is a power series, the effect is identical to \kbd{truncate}, i.e.~it
+ chops off the $O(X^k)$.
+
+ The main use of this function is when $t$ is a vector: it creates the
+ polynomial whose coefficients are given by $t$, with $t[1]$ being the
+ constant term. \tet{Pol} can be used if one wants $t[1]$ to be the leading
+ coefficient:
+ \bprog
+ ? Polrev([1,2,3])
+ %1 = 3*x^2 + 2*x + 1
+ ? Pol([1,2,3])
+ %2 = x^2 + 2*x + 3
+ @eprog
+ The reciprocal function of \kbd{Pol} (resp.~\kbd{Polrev}) is \kbd{Vec} (resp.~
+ \kbd{Vecrev}).
diff --git a/src/functions/conversions/Qfb b/src/functions/conversions/Qfb
new file mode 100644
index 0000000..1f80f1f
--- /dev/null
+++ b/src/functions/conversions/Qfb
@@ -0,0 +1,13 @@
+Function: Qfb
+Section: conversions
+C-Name: Qfb0
+Prototype: GGGDGp
+Help: Qfb(a,b,c,{D=0.}): binary quadratic form a*x^2+b*x*y+c*y^2. D is
+ optional (0.0 by default) and initializes Shanks's distance if b^2-4*a*c>0.
+Doc: creates the binary quadratic form\sidx{binary quadratic form}
+ $ax^2+bxy+cy^2$. If $b^2-4ac>0$, initialize \idx{Shanks}' distance
+ function to $D$. Negative definite forms are not implemented,
+ use their positive definite counterpart instead.
+Variant: Also available are
+ \fun{GEN}{qfi}{GEN a, GEN b, GEN c} (assumes $b^2-4ac<0$) and
+ \fun{GEN}{qfr}{GEN a, GEN b, GEN c, GEN D} (assumes $b^2-4ac>0$).
diff --git a/src/functions/conversions/Ser b/src/functions/conversions/Ser
new file mode 100644
index 0000000..1e43e68
--- /dev/null
+++ b/src/functions/conversions/Ser
@@ -0,0 +1,43 @@
+Function: Ser
+Section: conversions
+C-Name: gtoser
+Prototype: GDnDP
+Help: Ser(s,{v='x},{d=seriesprecision}): convert s into a power series with
+ variable v and precision d, starting with the constant coefficient.
+Doc: transforms the object $s$ into a power series with main variable $v$
+ ($x$ by default) and precision (number of significant terms) equal to
+ $d$ (= the default \kbd{seriesprecision} by default). If $s$ is a
+ scalar, this gives a constant power series in $v$ with precision \kbd{d}.
+ If $s$ is a polynomial, the polynomial is truncated to $d$ terms if needed
+ \bprog
+ ? Ser(1, 'y, 5)
+ %1 = 1 + O(y^5)
+ ? Ser(x^2,, 5)
+ %2 = x^2 + O(x^7)
+ ? T = polcyclo(100)
+ %3 = x^40 - x^30 + x^20 - x^10 + 1
+ ? Ser(T, 'x, 11)
+ %4 = 1 - x^10 + O(x^11)
+ @eprog\noindent The function is more or less equivalent with multiplication by
+ $1 + O(v^d)$ in theses cases, only faster.
+
+ If $s$ is a vector, on the other hand, the coefficients of the vector are
+ understood to be the coefficients of the power series starting from the
+ constant term (as in \tet{Polrev}$(x)$), and the precision $d$ is ignored:
+ in other words, in this case, we convert \typ{VEC} / \typ{COL} to the power
+ series whose significant terms are exactly given by the vector entries.
+ Finally, if $s$ is already a power series in $v$, we return it verbatim,
+ ignoring $d$ again. If $d$ significant terms are desired in the last two
+ cases, convert/truncate to \typ{POL} first.
+ \bprog
+ ? v = [1,2,3]; Ser(v, t, 7)
+ %5 = 1 + 2*t + 3*t^2 + O(t^3)  \\ 3 terms: 7 is ignored!
+ ? Ser(Polrev(v,t), t, 7)
+ %6 = 1 + 2*t + 3*t^2 + O(t^7)
+ ? s = 1+x+O(x^2); Ser(s, x, 7)
+ %7 = 1 + x + O(x^2)  \\ 2 terms: 7 ignored
+ ? Ser(truncate(s), x, 7)
+ %8 = 1 + x + O(x^7)
+ @eprog\noindent
+ The warning given for \kbd{Pol} also applies here: this is not a substitution
+ function.
diff --git a/src/functions/conversions/Set b/src/functions/conversions/Set
new file mode 100644
index 0000000..a136ef3
--- /dev/null
+++ b/src/functions/conversions/Set
@@ -0,0 +1,23 @@
+Function: Set
+Section: conversions
+C-Name: gtoset
+Prototype: DG
+Help: Set({x=[]}): convert x into a set, i.e. a row vector with strictly
+ increasing coefficients. Empty set if x is omitted.
+Description:
+ ():vec           cgetg(1,t_VEC)
+ (gen):vec        gtoset($1)
+Doc:
+ converts $x$ into a set, i.e.~into a row vector, with strictly increasing
+ entries with respect to the (somewhat arbitrary) universal comparison function
+ \tet{cmp}. Standard container types \typ{VEC}, \typ{COL}, \typ{LIST} and
+ \typ{VECSMALL} are converted to the set with corresponding elements. All
+ others are converted to a set with one element.
+ \bprog
+ ? Set([1,2,4,2,1,3])
+ %1 = [1, 2, 3, 4]
+ ? Set(x)
+ %2 = [x]
+ ? Set(Vecsmall([1,3,2,1,3]))
+ %3 = [1, 2, 3]
+ @eprog
diff --git a/src/functions/conversions/Str b/src/functions/conversions/Str
new file mode 100644
index 0000000..5df2597
--- /dev/null
+++ b/src/functions/conversions/Str
@@ -0,0 +1,28 @@
+Function: Str
+Section: conversions
+C-Name: Str
+Prototype: s*
+Help: Str({x}*): concatenates its (string) argument into a single string.
+Description:
+ (gen):genstr:copy:parens      $genstr:1
+ (gen,gen):genstr              Str(mkvec2($1, $2))
+ (gen,gen,gen):genstr          Str(mkvec3($1, $2, $3))
+ (gen,gen,gen,gen):genstr      Str(mkvec4($1, $2, $3, $4))
+ (gen,...):genstr              Str(mkvecn($#, $2))
+
+Doc:
+ converts its argument list into a
+ single character string (type \typ{STR}, the empty string if $x$ is omitted).
+ To recover an ordinary \kbd{GEN} from a string, apply \kbd{eval} to it. The
+ arguments of \kbd{Str} are evaluated in string context, see \secref{se:strings}.
+
+ \bprog
+ ? x2 = 0; i = 2; Str(x, i)
+ %1 = "x2"
+ ? eval(%)
+ %2 = 0
+ @eprog\noindent
+ This function is mostly useless in library mode. Use the pair
+ \tet{strtoGEN}/\tet{GENtostr} to convert between \kbd{GEN} and \kbd{char*}.
+ The latter returns a malloced string, which should be freed after usage.
+ %\syn{NO}
diff --git a/src/functions/conversions/Strchr b/src/functions/conversions/Strchr
new file mode 100644
index 0000000..5f87879
--- /dev/null
+++ b/src/functions/conversions/Strchr
@@ -0,0 +1,17 @@
+Function: Strchr
+Section: conversions
+C-Name: Strchr
+Prototype: G
+Help: Strchr(x): converts x to a string, translating each integer into a
+ character.
+Doc:
+ converts $x$ to a string, translating each integer
+ into a character.
+ \bprog
+ ? Strchr(97)
+ %1 = "a"
+ ? Vecsmall("hello world")
+ %2 = Vecsmall([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])
+ ? Strchr(%)
+ %3 = "hello world"
+ @eprog
diff --git a/src/functions/conversions/Strexpand b/src/functions/conversions/Strexpand
new file mode 100644
index 0000000..a503a64
--- /dev/null
+++ b/src/functions/conversions/Strexpand
@@ -0,0 +1,18 @@
+Function: Strexpand
+Section: conversions
+C-Name: Strexpand
+Prototype: s*
+Help: Strexpand({x}*): concatenates its (string) argument into a single
+ string, performing tilde expansion.
+Doc:
+ converts its argument list into a
+ single character string (type \typ{STR}, the empty string if $x$ is omitted).
+ Then perform \idx{environment expansion}, see \secref{se:envir}.
+ This feature can be used to read \idx{environment variable} values.
+ \bprog
+ ? Strexpand("$HOME/doc")
+ %1 = "/home/pari/doc"
+ @eprog
+
+ The individual arguments are read in string context, see \secref{se:strings}.
+ %\syn{NO}
diff --git a/src/functions/conversions/Strprintf b/src/functions/conversions/Strprintf
new file mode 100644
index 0000000..649f0cb
--- /dev/null
+++ b/src/functions/conversions/Strprintf
@@ -0,0 +1,10 @@
+Function: Strprintf
+Section: programming/specific
+C-Name: Strprintf
+Prototype: ss*
+Help: Strprintf(fmt,{x}*): returns a string built from the remaining
+ arguments according to the format fmt.
+Doc: returns a string built from the remaining arguments according to the
+ format fmt. The format consists of ordinary characters (not \%), printed
+ unchanged, and conversions specifications. See \kbd{printf}.
+ %\syn{NO}
diff --git a/src/functions/conversions/Strtex b/src/functions/conversions/Strtex
new file mode 100644
index 0000000..16fcaa5
--- /dev/null
+++ b/src/functions/conversions/Strtex
@@ -0,0 +1,13 @@
+Function: Strtex
+Section: conversions
+C-Name: Strtex
+Prototype: s*
+Help: Strtex({x}*): translates its (string) arguments to TeX format and
+ returns the resulting string.
+Doc:
+ translates its arguments to TeX
+ format, and concatenates the results into a single character string (type
+ \typ{STR}, the empty string if $x$ is omitted).
+
+ The individual arguments are read in string context, see \secref{se:strings}.
+ %\syn{NO}
diff --git a/src/functions/conversions/Vec b/src/functions/conversions/Vec
new file mode 100644
index 0000000..f99fcb2
--- /dev/null
+++ b/src/functions/conversions/Vec
@@ -0,0 +1,36 @@
+Function: Vec
+Section: conversions
+C-Name: gtovec0
+Prototype: GD0,L,
+Help: Vec(x, {n}): transforms the object x into a vector of dimension n.
+Description:
+ (gen):vec        gtovec($1)
+Doc:
+ transforms the object $x$ into a row vector. The dimension of the
+ resulting vector can be optionally specified via the extra parameter $n$.
+
+ If $n$ is omitted or $0$, the dimension depends on the type of $x$; the
+ vector has a single component, except when $x$ is
+
+ \item a vector or a quadratic form (in which case the resulting vector
+ is simply the initial object considered as a row vector),
+
+ \item a polynomial or a power series. In the case of a polynomial, the
+ coefficients of the vector start with the leading coefficient of the
+ polynomial, while for power series only the significant coefficients are
+ taken into account, but this time by increasing order of degree.
+ In this last case, \kbd{Vec} is the reciprocal function of \kbd{Pol} and
+ \kbd{Ser} respectively,
+
+ \item a matrix: return the vector of columns comprising the matrix.
+
+ \item a character string: return the vector of individual characters.
+
+ \item an error context (\typ{ERROR}): return the error components, see
+ \tet{iferr}.
+
+ In the last three cases (matrix, character string, error), $n$ is
+ meaningless and must be omitted or an error is raised. Otherwise, if $n$ is
+ given, $0$ entries are appended at the end of the vector if $n > 0$, and
+ prepended at the beginning if $n < 0$. The dimension of the resulting vector
+ is $|n|$. Variant: \fun{GEN}{gtovec}{GEN x} is also available.
diff --git a/src/functions/conversions/Vecrev b/src/functions/conversions/Vecrev
new file mode 100644
index 0000000..cd3f277
--- /dev/null
+++ b/src/functions/conversions/Vecrev
@@ -0,0 +1,14 @@
+Function: Vecrev
+Section: conversions
+C-Name: gtovecrev0
+Prototype: GD0,L,
+Help: Vecrev(x, {n}): transforms the object x into a vector of dimension n
+ in reverse order with respect to Vec(x, {n}). Empty vector if x is omitted.
+Description:
+ (gen):vec     gtovecrev($1)
+Doc:
+ as $\kbd{Vec}(x, n)$, then reverse the result. In particular
+ In this case, \kbd{Vecrev} is the reciprocal function of \kbd{Polrev}: the
+ coefficients of the vector start with the constant coefficient of the
+ polynomial and the others follow by increasing degree.
+Variant: \fun{GEN}{gtovecrev}{GEN x} is also available.
diff --git a/src/functions/conversions/Vecsmall b/src/functions/conversions/Vecsmall
new file mode 100644
index 0000000..32e817a
--- /dev/null
+++ b/src/functions/conversions/Vecsmall
@@ -0,0 +1,17 @@
+Function: Vecsmall
+Section: conversions
+C-Name: gtovecsmall0
+Prototype: GD0,L,
+Help: Vecsmall(x, {n}): transforms the object x into a VECSMALL of dimension n.
+Description:
+ (gen):vecsmall                gtovecsmall($1)
+Doc:
+ transforms the object $x$ into a row vector of type \typ{VECSMALL}. The
+ dimension of the resulting vector can be optionally specified via the extra
+ parameter $n$.
+
+ This acts as \kbd{Vec}$(x,n)$, but only on a limited set of objects:
+ the result must be representable as a vector of small integers.
+ If $x$ is a character string, a vector of individual characters in ASCII
+ encoding is returned (\tet{Strchr} yields back the character string).
+Variant: \fun{GEN}{gtovecsmall}{GEN x} is also available.
diff --git a/src/functions/conversions/binary b/src/functions/conversions/binary
new file mode 100644
index 0000000..32a3254
--- /dev/null
+++ b/src/functions/conversions/binary
@@ -0,0 +1,11 @@
+Function: binary
+Section: conversions
+C-Name: binaire
+Prototype: G
+Help: binary(x): gives the vector formed by the binary digits of x (x
+ integer).
+Doc:
+ outputs the vector of the binary digits of $|x|$.
+ Here $x$ can be an integer, a real number (in which case the result has two
+ components, one for the integer part, one for the fractional part) or a
+ vector/matrix.
diff --git a/src/functions/conversions/bitand b/src/functions/conversions/bitand
new file mode 100644
index 0000000..d9fd7b0
--- /dev/null
+++ b/src/functions/conversions/bitand
@@ -0,0 +1,30 @@
+Function: bitand
+Section: conversions
+C-Name: gbitand
+Prototype: GG
+Help: bitand(x,y): bitwise "and" of two integers x and y. Negative numbers
+ behave as if modulo big power of 2.
+Description:
+ (small, small):small:parens        $(1)&$(2)
+ (gen, gen):int        gbitand($1, $2)
+Doc:
+ bitwise \tet{and}
+ \sidx{bitwise and}of two integers $x$ and $y$, that is the integer
+ $$\sum_i (x_i~\kbd{and}~y_i) 2^i$$
+
+ Negative numbers behave $2$-adically, i.e.~the result is the $2$-adic limit
+ of \kbd{bitand}$(x_n,y_n)$, where $x_n$ and $y_n$ are non-negative integers
+ tending to $x$ and $y$ respectively. (The result is an ordinary integer,
+ possibly negative.)
+
+ \bprog
+ ? bitand(5, 3)
+ %1 = 1
+ ? bitand(-5, 3)
+ %2 = 3
+ ? bitand(-5, -3)
+ %3 = -7
+ @eprog
+Variant: Also available is
+ \fun{GEN}{ibitand}{GEN x, GEN y}, which returns the bitwise \emph{and}
+ of $|x|$ and $|y|$, two integers.
diff --git a/src/functions/conversions/bitneg b/src/functions/conversions/bitneg
new file mode 100644
index 0000000..839df0b
--- /dev/null
+++ b/src/functions/conversions/bitneg
@@ -0,0 +1,15 @@
+Function: bitneg
+Section: conversions
+C-Name: gbitneg
+Prototype: GD-1,L,
+Help: bitneg(x,{n=-1}): bitwise negation of an integers x truncated to n
+ bits. n=-1 means represent infinite sequences of bit 1 as negative numbers.
+ Negative numbers behave as if modulo big power of 2.
+Doc:
+ \idx{bitwise negation} of an integer $x$,
+ truncated to $n$ bits, $n\geq 0$, that is the integer
+ $$\sum_{i=0}^{n-1} \kbd{not}(x_i) 2^i.$$
+ The special case $n=-1$ means no truncation: an infinite sequence of
+ leading $1$ is then represented as a negative number.
+
+ See \secref{se:bitand} for the behavior for negative arguments.
diff --git a/src/functions/conversions/bitnegimply b/src/functions/conversions/bitnegimply
new file mode 100644
index 0000000..2acb43f
--- /dev/null
+++ b/src/functions/conversions/bitnegimply
@@ -0,0 +1,19 @@
+Function: bitnegimply
+Section: conversions
+C-Name: gbitnegimply
+Prototype: GG
+Help: bitnegimply(x,y): bitwise "negated imply" of two integers x and y,
+ in other words, x BITAND BITNEG(y). Negative numbers behave as if modulo big
+ power of 2.
+Description:
+ (small, small):small:parens        $(1)&~$(2)
+ (gen, gen):int        gbitnegimply($1, $2)
+Doc:
+ bitwise negated imply of two integers $x$ and
+ $y$ (or \kbd{not} $(x \Rightarrow y)$), that is the integer $$\sum
+ (x_i~\kbd{and not}(y_i)) 2^i$$
+
+ See \secref{se:bitand} for the behavior for negative arguments.
+Variant: Also available is
+ \fun{GEN}{ibitnegimply}{GEN x, GEN y}, which returns the bitwise negated
+ imply of $|x|$ and $|y|$, two integers.
diff --git a/src/functions/conversions/bitor b/src/functions/conversions/bitor
new file mode 100644
index 0000000..10c088e
--- /dev/null
+++ b/src/functions/conversions/bitor
@@ -0,0 +1,18 @@
+Function: bitor
+Section: conversions
+C-Name: gbitor
+Prototype: GG
+Help: bitor(x,y): bitwise "or" of two integers x and y. Negative numbers
+ behave as if modulo big power of 2.
+Description:
+ (small, small):small:parens        $(1)|$(2)
+ (gen, gen):int        gbitor($1, $2)
+Doc:
+ \sidx{bitwise inclusive or}bitwise (inclusive)
+ \tet{or} of two integers $x$ and $y$, that is the integer $$\sum
+ (x_i~\kbd{or}~y_i) 2^i$$
+
+ See \secref{se:bitand} for the behavior for negative arguments.
+Variant: Also available is
+ \fun{GEN}{ibitor}{GEN x, GEN y}, which returns the bitwise \emph{ir}
+ of $|x|$ and $|y|$, two integers.
diff --git a/src/functions/conversions/bittest b/src/functions/conversions/bittest
new file mode 100644
index 0000000..f286a69
--- /dev/null
+++ b/src/functions/conversions/bittest
@@ -0,0 +1,25 @@
+Function: bittest
+Section: conversions
+C-Name: gbittest
+Prototype: GL
+Help: bittest(x,n): gives bit number n (coefficient of 2^n) of the integer x.
+ Negative numbers behave as if modulo big power of 2.
+Description:
+ (small, small):bool:parens     ($(1)>>$(2))&1
+ (int, small):bool              bittest($1, $2)
+ (gen, small):gen               gbittest($1, $2)
+Doc:
+ outputs the $n^{\text{th}}$ bit of $x$ starting
+ from the right (i.e.~the coefficient of $2^n$ in the binary expansion of $x$).
+ The result is 0 or 1.
+ \bprog
+ ? bittest(7, 3)
+ %1 = 1 \\ the 3rd bit is 1
+ ? bittest(7, 4)
+ %2 = 0 \\ the 4th bit is 0
+ @eprog\noindent
+ See \secref{se:bitand} for the behavior at negative arguments.
+Variant: For a \typ{INT} $x$, the variant \fun{long}{bittest}{GEN x, long n} is
+ generally easier to use, and if furthermore $n\ge 0$ the low-level function
+ \fun{ulong}{int_bit}{GEN x, long n} returns \kbd{bittest(abs(x),n)}.
+
diff --git a/src/functions/conversions/bitxor b/src/functions/conversions/bitxor
new file mode 100644
index 0000000..bbb4fd8
--- /dev/null
+++ b/src/functions/conversions/bitxor
@@ -0,0 +1,18 @@
+Function: bitxor
+Section: conversions
+C-Name: gbitxor
+Prototype: GG
+Help: bitxor(x,y): bitwise "exclusive or" of two integers x and y.
+ Negative numbers behave as if modulo big power of 2.
+Description:
+ (small, small):small:parens        $(1)^$(2)
+ (gen, gen):int        gbitxor($1, $2)
+Doc:
+ bitwise (exclusive) \tet{or}
+ \sidx{bitwise exclusive or}of two integers $x$ and $y$, that is the integer
+ $$\sum (x_i~\kbd{xor}~y_i) 2^i$$
+
+ See \secref{se:bitand} for the behavior for negative arguments.
+Variant: Also available is
+ \fun{GEN}{ibitxor}{GEN x, GEN y}, which returns the bitwise \emph{xor}
+ of $|x|$ and $|y|$, two integers.
diff --git a/src/functions/conversions/ceil b/src/functions/conversions/ceil
new file mode 100644
index 0000000..d0c7852
--- /dev/null
+++ b/src/functions/conversions/ceil
@@ -0,0 +1,16 @@
+Function: ceil
+Section: conversions
+C-Name: gceil
+Prototype: G
+Help: ceil(x): ceiling of x = smallest integer >= x.
+Description:
+ (small):small:parens   $1
+ (int):int:copy:parens  $1
+ (real):int             ceilr($1)
+ (mp):int               mpceil($1)
+ (gen):gen              gceil($1)
+Doc:
+ ceiling of $x$. When $x$ is in $\R$, the result is the
+ smallest integer greater than or equal to $x$. Applied to a rational
+ function, $\kbd{ceil}(x)$ returns the Euclidean quotient of the numerator by
+ the denominator.
diff --git a/src/functions/conversions/centerlift b/src/functions/conversions/centerlift
new file mode 100644
index 0000000..c7b4aaa
--- /dev/null
+++ b/src/functions/conversions/centerlift
@@ -0,0 +1,28 @@
+Function: centerlift
+Section: conversions
+C-Name: centerlift0
+Prototype: GDn
+Help: centerlift(x,{v}): centered lift of x. Same as lift except for
+ intmod and padic components.
+Description:
+ (pol):pol        centerlift($1)
+ (vec):vec        centerlift($1)
+ (gen):gen        centerlift($1)
+ (pol, var):pol        centerlift0($1, $2)
+ (vec, var):vec        centerlift0($1, $2)
+ (gen, var):gen        centerlift0($1, $2)
+Doc: Same as \tet{lift}, except that \typ{INTMOD} and \typ{PADIC} components
+ are lifted using centered residues:
+
+ \item for a \typ{INTMOD} $x\in \Z/n\Z$, the lift $y$ is such that
+ $-n/2<y\le n/2$.
+
+ \item  a \typ{PADIC} $x$ is lifted in the same way as above (modulo
+ $p^\kbd{padicprec(x)}$) if its valuation $v$ is non-negative; if not, returns
+ the fraction $p^v$ \kbd{centerlift}$(x p^{-v})$; in particular, rational
+ reconstruction is not attempted. Use \tet{bestappr} for this.
+
+ For backward compatibility, \kbd{centerlift(x,'v)} is allowed as an alias
+ for \kbd{lift(x,'v)}.
+
+ \synt{centerlift}{GEN x}.
diff --git a/src/functions/conversions/characteristic b/src/functions/conversions/characteristic
new file mode 100644
index 0000000..b621c67
--- /dev/null
+++ b/src/functions/conversions/characteristic
@@ -0,0 +1,15 @@
+Function: characteristic
+Section: conversions
+C-Name: characteristic
+Prototype: mG
+Help: characteristic(x): characteristic of the base ring over which x is
+ defined
+Doc:
+ returns the characteristic of the base ring over which $x$ is defined (as
+ defined by \typ{INTMOD} and \typ{FFELT} components). The function raises an
+ exception if incompatible primes arise from \typ{FFELT} and \typ{PADIC}
+ components.
+ \bprog
+ ? characteristic(Mod(1,24)*x + Mod(1,18)*y)
+ %1 = 6
+ @eprog
diff --git a/src/functions/conversions/component b/src/functions/conversions/component
new file mode 100644
index 0000000..0f9200c
--- /dev/null
+++ b/src/functions/conversions/component
@@ -0,0 +1,40 @@
+Function: component
+Section: conversions
+C-Name: compo
+Prototype: GL
+Help: component(x,n): the n'th component of the internal representation of
+ x. For vectors or matrices, it is simpler to use x[]. For list objects such
+ as nf, bnf, bnr or ell, it is much easier to use member functions starting
+ with ".".
+Description:
+ (error,small):gen     err_get_compo($1, $2)
+ (gen,small):gen       compo($1,$2)
+Doc: extracts the $n^{\text{th}}$-component of $x$. This is to be understood
+ as follows: every PARI type has one or two initial \idx{code words}. The
+ components are counted, starting at 1, after these code words. In particular
+ if $x$ is a vector, this is indeed the $n^{\text{th}}$-component of $x$, if
+ $x$ is a matrix, the $n^{\text{th}}$ column, if $x$ is a polynomial, the
+ $n^{\text{th}}$ coefficient (i.e.~of degree $n-1$), and for power series,
+ the $n^{\text{th}}$ significant coefficient.
+
+ For polynomials and power series, one should rather use \tet{polcoeff}, and
+ for vectors and matrices, the \kbd{[$\,$]} operator. Namely, if $x$ is a
+ vector, then \tet{x[n]} represents the $n^{\text{th}}$ component of $x$. If
+ $x$ is a matrix, \tet{x[m,n]} represents the coefficient of row \kbd{m} and
+ column \kbd{n} of the matrix, \tet{x[m,]} represents the $m^{\text{th}}$
+ \emph{row} of $x$, and \tet{x[,n]} represents the $n^{\text{th}}$
+ \emph{column} of $x$.
+
+ Using of this function requires detailed knowledge of the structure of the
+ different PARI types, and thus it should almost never be used directly.
+ Some useful exceptions:
+ \bprog
+     ? x = 3 + O(3^5);
+     ? component(x, 2)
+     %2 = 81   \\ p^(p-adic accuracy)
+     ? component(x, 1)
+     %3 = 3    \\ p
+     ? q = Qfb(1,2,3);
+     ? component(q, 1)
+     %5 = 1
+ @eprog
diff --git a/src/functions/conversions/conj b/src/functions/conversions/conj
new file mode 100644
index 0000000..dff7013
--- /dev/null
+++ b/src/functions/conversions/conj
@@ -0,0 +1,11 @@
+Function: conj
+Section: conversions
+C-Name: gconj
+Prototype: G
+Help: conj(x): the algebraic conjugate of x.
+Doc:
+ conjugate of $x$. The meaning of this
+ is clear, except that for real quadratic numbers, it means conjugation in the
+ real quadratic field. This function has no effect on integers, reals,
+ intmods, fractions or $p$-adics. The only forbidden type is polmod
+ (see \kbd{conjvec} for this).
diff --git a/src/functions/conversions/conjvec b/src/functions/conversions/conjvec
new file mode 100644
index 0000000..a18874b
--- /dev/null
+++ b/src/functions/conversions/conjvec
@@ -0,0 +1,21 @@
+Function: conjvec
+Section: conversions
+C-Name: conjvec
+Prototype: Gp
+Help: conjvec(z): conjugate vector of the algebraic number z.
+Doc:
+ conjugate vector representation of $z$. If $z$ is a
+ polmod, equal to \kbd{Mod}$(a,T)$, this gives a vector of length
+ $\text{degree}(T)$ containing:
+
+ \item the complex embeddings of $z$ if $T$ has rational coefficients,
+ i.e.~the $a(r[i])$ where $r = \kbd{polroots}(T)$;
+
+ \item the conjugates of $z$ if $T$ has some intmod coefficients;
+
+ \noindent if $z$ is a finite field element, the result is the vector of
+ conjugates $[z,z^p,z^{p^2},\ldots,z^{p^{n-1}}]$ where $n=\text{degree}(T)$.
+
+ \noindent If $z$ is an integer or a rational number, the result is~$z$. If
+ $z$ is a (row or column) vector, the result is a matrix whose columns are
+ the conjugate vectors of the individual elements of $z$.
diff --git a/src/functions/conversions/denominator b/src/functions/conversions/denominator
new file mode 100644
index 0000000..38ca274
--- /dev/null
+++ b/src/functions/conversions/denominator
@@ -0,0 +1,26 @@
+Function: denominator
+Section: conversions
+C-Name: denom
+Prototype: G
+Help: denominator(x): denominator of x (or lowest common denominator in case
+ of an array).
+Doc:
+ denominator of $x$. The meaning of this
+ is clear when $x$ is a rational number or function. If $x$ is an integer
+ or a polynomial, it is treated as a rational number or function,
+ respectively, and the result is equal to $1$. For polynomials, you
+ probably want to use
+ \bprog
+ denominator( content(x) )
+ @eprog\noindent
+ instead. As for modular objects, \typ{INTMOD} and \typ{PADIC} have
+ denominator $1$, and the denominator of a \typ{POLMOD} is the denominator
+ of its (minimal degree) polynomial representative.
+
+ If $x$ is a recursive structure, for instance a vector or matrix, the lcm
+ of the denominators of its components (a common denominator) is computed.
+ This also applies for \typ{COMPLEX}s and \typ{QUAD}s.
+
+ \misctitle{Warning} Multivariate objects are created according to variable
+ priorities, with possibly surprising side effects ($x/y$ is a polynomial, but
+ $y/x$ is a rational function). See \secref{se:priority}.
diff --git a/src/functions/conversions/digits b/src/functions/conversions/digits
new file mode 100644
index 0000000..1cca009
--- /dev/null
+++ b/src/functions/conversions/digits
@@ -0,0 +1,8 @@
+Function: digits
+Section: conversions
+C-Name: digits
+Prototype: GDG
+Help: digits(x,{b=10}): gives the vector formed by the digits of x in base b (x and b
+ integers).
+Doc:
+ outputs the vector of the digits of $|x|$ in base $b$, where $x$ and $b$ are integers.
diff --git a/src/functions/conversions/floor b/src/functions/conversions/floor
new file mode 100644
index 0000000..6b6dc5b
--- /dev/null
+++ b/src/functions/conversions/floor
@@ -0,0 +1,16 @@
+Function: floor
+Section: conversions
+C-Name: gfloor
+Prototype: G
+Help: floor(x): floor of x = largest integer <= x.
+Description:
+ (small):small:parens   $1
+ (int):int:copy:parens  $1
+ (real):int             floorr($1)
+ (mp):int               mpfloor($1)
+ (gen):gen              gfloor($1)
+Doc:
+ floor of $x$. When $x$ is in $\R$, the result is the
+ largest integer smaller than or equal to $x$. Applied to a rational function,
+ $\kbd{floor}(x)$ returns the Euclidean quotient of the numerator by the
+ denominator.
diff --git a/src/functions/conversions/frac b/src/functions/conversions/frac
new file mode 100644
index 0000000..2852846
--- /dev/null
+++ b/src/functions/conversions/frac
@@ -0,0 +1,8 @@
+Function: frac
+Section: conversions
+C-Name: gfrac
+Prototype: G
+Help: frac(x): fractional part of x = x-floor(x).
+Doc:
+ fractional part of $x$. Identical to
+ $x-\text{floor}(x)$. If $x$ is real, the result is in $[0,1[$.
diff --git a/src/functions/conversions/hammingweight b/src/functions/conversions/hammingweight
new file mode 100644
index 0000000..69c8bdc
--- /dev/null
+++ b/src/functions/conversions/hammingweight
@@ -0,0 +1,20 @@
+Function: hammingweight
+Section: conversions
+C-Name: hammingweight
+Prototype: lG
+Help: hammingweight(x): returns the Hamming weight of x.
+Doc:
+ If $x$ is a \typ{INT}, return the binary Hamming weight of $|x|$. Otherwise
+ $x$ must be of type \typ{POL}, \typ{VEC}, \typ{COL}, \typ{VECSMALL}, or
+ \typ{MAT} and the function returns the number of non-zero coefficients of
+ $x$.
+ \bprog
+ ? hammingweight(15)
+ %1 = 4
+ ? hammingweight(x^100 + 2*x + 1)
+ %2 = 3
+ ? hammingweight([Mod(1,2), 2, Mod(0,3)])
+ %3 = 2
+ ? hammingweight(matid(100))
+ %4 = 100
+ @eprog
diff --git a/src/functions/conversions/imag b/src/functions/conversions/imag
new file mode 100644
index 0000000..dfbdfe3
--- /dev/null
+++ b/src/functions/conversions/imag
@@ -0,0 +1,7 @@
+Function: imag
+Section: conversions
+C-Name: gimag
+Prototype: G
+Help: imag(x): imaginary part of x.
+Doc: imaginary part of $x$. When $x$ is a quadratic number, this is the
+ coefficient of $\omega$ in the ``canonical'' integral basis $(1,\omega)$.
diff --git a/src/functions/conversions/length b/src/functions/conversions/length
new file mode 100644
index 0000000..9b94916
--- /dev/null
+++ b/src/functions/conversions/length
@@ -0,0 +1,39 @@
+Function: length
+Section: conversions
+C-Name: glength
+Prototype: lG
+Help: length(x): number of non code words in x, number of characters for a
+ string.
+Description:
+ (vecsmall):lg      lg($1)
+ (vec):lg           lg($1)
+ (pol):small        lgpol($1)
+ (gen):small        glength($1)
+Doc: length of $x$; \kbd{\#}$x$ is a shortcut for \kbd{length}$(x)$.
+ This is mostly useful for
+
+ \item vectors: dimension (0 for empty vectors),
+
+ \item lists: number of entries (0 for empty lists),
+
+ \item matrices: number of columns,
+
+ \item character strings: number of actual characters (without
+ trailing \kbd{\bs 0}, should you expect it from $C$ \kbd{char*}).
+ \bprog
+  ? #"a string"
+  %1 = 8
+  ? #[3,2,1]
+  %2 = 3
+  ? #[]
+  %3 = 0
+  ? #matrix(2,5)
+  %4 = 5
+  ? L = List([1,2,3,4]); #L
+  %5 = 4
+ @eprog
+
+ The routine is in fact defined for arbitrary GP types, but is awkward and
+ useless in other cases: it returns the number of non-code words in $x$, e.g.
+ the effective length minus 2 for integers since the \typ{INT} type has two code
+ words.
diff --git a/src/functions/conversions/lift b/src/functions/conversions/lift
new file mode 100644
index 0000000..175b49b
--- /dev/null
+++ b/src/functions/conversions/lift
@@ -0,0 +1,52 @@
+Function: lift
+Section: conversions
+C-Name: lift0
+Prototype: GDn
+Help: lift(x,{v}):
+ if v is omitted, lifts elements of Z/nZ to Z, of Qp to Q, and of K[x]/(P) to
+ K[x]. Otherwise lift only polmods with main variable v.
+Description:
+ (pol):pol        lift($1)
+ (vec):vec        lift($1)
+ (gen):gen        lift($1)
+ (pol, var):pol        lift0($1, $2)
+ (vec, var):vec        lift0($1, $2)
+ (gen, var):gen        lift0($1, $2)
+Doc:
+ if $v$ is omitted, lifts intmods from $\Z/n\Z$ in $\Z$,
+ $p$-adics from $\Q_p$ to $\Q$ (as \tet{truncate}), and polmods to
+ polynomials. Otherwise, lifts only polmods whose modulus has main
+ variable~$v$. \typ{FFELT} are not lifted, nor are List elements: you may
+ convert the latter to vectors first, or use \kbd{apply(lift,L)}. More
+ generally, components for which such lifts are meaningless (e.g. character
+ strings) are copied verbatim.
+ \bprog
+ ? lift(Mod(5,3))
+ %1 = 2
+ ? lift(3 + O(3^9))
+ %2 = 3
+ ? lift(Mod(x,x^2+1))
+ %3 = x
+ ? lift(Mod(x,x^2+1))
+ %4 = x
+ @eprog
+ Lifts are performed recursively on an object components, but only
+ by \emph{one level}: once a \typ{POLMOD} is lifted, the components of
+ the result are \emph{not} lifted further.
+ \bprog
+ ? lift(x * Mod(1,3) + Mod(2,3))
+ %4 = x + 2
+ ? lift(x * Mod(y,y^2+1) + Mod(2,3))
+ %5 = y*x + Mod(2, 3)   \\@com do you understand this one?
+ ? lift(x * Mod(y,y^2+1) + Mod(2,3), 'x)
+ %6 = Mod(y, y^2 + 1)*x + Mod(Mod(2, 3), y^2 + 1)
+ ? lift(%, y)
+ %7 = y*x + Mod(2, 3)
+ @eprog\noindent To recursively lift all components not only by one level,
+ but as long as possible, use \kbd{liftall}. To lift only \typ{INTMOD}s and
+ \typ{PADIC}s components, use \tet{liftint}. To lift only \typ{POLMOD}s
+ components, use \tet{liftpol}. Finally, \tet{centerlift} allows to lift
+ \typ{INTMOD}s and \typ{PADIC}s using centered residues (lift of smallest
+ absolute value).
+Variant: Also available is \fun{GEN}{lift}{GEN x} corresponding to
+ \kbd{lift0(x,-1)}.
diff --git a/src/functions/conversions/liftall b/src/functions/conversions/liftall
new file mode 100644
index 0000000..6eb0323
--- /dev/null
+++ b/src/functions/conversions/liftall
@@ -0,0 +1,23 @@
+Function: liftall
+Section: conversions
+C-Name: liftall
+Prototype: G
+Help: liftall(x): lifts every element of Z/nZ to Z, of Qp to Q, and of
+ K[x]/(P) to K[x].
+Description:
+ (pol):pol        liftall($1)
+ (vec):vec        liftall($1)
+ (gen):gen        liftall($1)
+Doc:
+ recursively lift all components of $x$ from $\Z/n\Z$ to $\Z$,
+ from $\Q_p$ to $\Q$ (as \tet{truncate}), and polmods to
+ polynomials. \typ{FFELT} are not lifted, nor are List elements: you may
+ convert the latter to vectors first, or use \kbd{apply(liftall,L)}. More
+ generally, components for which such lifts are meaningless (e.g. character
+ strings) are copied verbatim.
+ \bprog
+ ? liftall(x * (1 + O(3)) + Mod(2,3))
+ %1 = x + 2
+ ? liftall(x * Mod(y,y^2+1) + Mod(2,3)*Mod(z,z^2))
+ %2 = y*x + 2*z
+ @eprog
diff --git a/src/functions/conversions/liftint b/src/functions/conversions/liftint
new file mode 100644
index 0000000..a080282
--- /dev/null
+++ b/src/functions/conversions/liftint
@@ -0,0 +1,22 @@
+Function: liftint
+Section: conversions
+C-Name: liftint
+Prototype: G
+Help: liftint(x): lifts every element of Z/nZ to Z, of Qp to Q, and of
+ K[x]/(P) to K[x].
+Description:
+ (pol):pol        liftint($1)
+ (vec):vec        liftint($1)
+ (gen):gen        liftint($1)
+Doc: recursively lift all components of $x$ from $\Z/n\Z$ to $\Z$ and
+ from $\Q_p$ to $\Q$ (as \tet{truncate}).
+ \typ{FFELT} are not lifted, nor are List elements: you may
+ convert the latter to vectors first, or use \kbd{apply(liftint,L)}. More
+ generally, components for which such lifts are meaningless (e.g. character
+ strings) are copied verbatim.
+ \bprog
+ ? liftint(x * (1 + O(3)) + Mod(2,3))
+ %1 = x + 2
+ ? liftint(x * Mod(y,y^2+1) + Mod(2,3)*Mod(z,z^2))
+ %2 = Mod(y, y^2 + 1)*x + Mod(Mod(2*z, z^2), y^2 + 1)
+ @eprog
diff --git a/src/functions/conversions/liftpol b/src/functions/conversions/liftpol
new file mode 100644
index 0000000..faeba6e
--- /dev/null
+++ b/src/functions/conversions/liftpol
@@ -0,0 +1,20 @@
+Function: liftpol
+Section: conversions
+C-Name: liftpol
+Prototype: G
+Help: liftpol(x): lifts every polmod component of x to polynomials
+Description:
+ (pol):pol        liftpol($1)
+ (vec):vec        liftpol($1)
+ (gen):gen        liftpol($1)
+Doc: recursively lift all components of $x$ which are polmods to
+ polynomials. \typ{FFELT} are not lifted, nor are List elements: you may
+ convert the latter to vectors first, or use \kbd{apply(liftpol,L)}. More
+ generally, components for which such lifts are meaningless (e.g. character
+ strings) are copied verbatim.
+ \bprog
+ ? liftpol(x * (1 + O(3)) + Mod(2,3))
+ %1 = (1 + O(3))*x + Mod(2, 3)
+ ? liftpol(x * Mod(y,y^2+1) + Mod(2,3)*Mod(z,z^2))
+ %2 = y*x + Mod(2, 3)*z
+ @eprog
diff --git a/src/functions/conversions/norm b/src/functions/conversions/norm
new file mode 100644
index 0000000..b52ffed
--- /dev/null
+++ b/src/functions/conversions/norm
@@ -0,0 +1,11 @@
+Function: norm
+Section: conversions
+C-Name: gnorm
+Prototype: G
+Help: norm(x): norm of x.
+Doc:
+ algebraic norm of $x$, i.e.~the product of $x$ with
+ its conjugate (no square roots are taken), or conjugates for polmods. For
+ vectors and matrices, the norm is taken componentwise and hence is not the
+ $L^2$-norm (see \kbd{norml2}). Note that the norm of an element of
+ $\R$ is its square, so as to be compatible with the complex norm.
diff --git a/src/functions/conversions/numerator b/src/functions/conversions/numerator
new file mode 100644
index 0000000..dc775c8
--- /dev/null
+++ b/src/functions/conversions/numerator
@@ -0,0 +1,25 @@
+Function: numerator
+Section: conversions
+C-Name: numer
+Prototype: G
+Help: numerator(x): numerator of x.
+Doc:
+ numerator of $x$. The meaning of this
+ is clear when $x$ is a rational number or function. If $x$ is an integer
+ or a polynomial, it is treated as a rational number or function,
+ respectively, and the result is $x$ itself. For polynomials, you
+ probably want to use
+ \bprog
+ numerator( content(x) )
+ @eprog\noindent
+ instead.
+
+ In other cases, \kbd{numerator(x)} is defined to be
+ \kbd{denominator(x)*x}. This is the case when $x$ is a vector or a
+ matrix, but also for \typ{COMPLEX} or \typ{QUAD}. In particular since a
+ \typ{PADIC} or \typ{INTMOD} has  denominator $1$, its numerator is
+ itself.
+
+ \misctitle{Warning} Multivariate objects are created according to variable
+ priorities, with possibly surprising side effects ($x/y$ is a polynomial, but
+ $y/x$ is a rational function). See \secref{se:priority}.
diff --git a/src/functions/conversions/numtoperm b/src/functions/conversions/numtoperm
new file mode 100644
index 0000000..31e19fc
--- /dev/null
+++ b/src/functions/conversions/numtoperm
@@ -0,0 +1,10 @@
+Function: numtoperm
+Section: conversions
+C-Name: numtoperm
+Prototype: LG
+Help: numtoperm(n,k): permutation number k (mod n!) of n letters (n
+ C-integer).
+Doc: generates the $k$-th permutation (as a row vector of length $n$) of the
+ numbers $1$ to $n$. The number $k$ is taken modulo $n!\,$, i.e.~inverse
+ function of \tet{permtonum}. The numbering used is the standard lexicographic
+ ordering, starting at $0$.
diff --git a/src/functions/conversions/padicprec b/src/functions/conversions/padicprec
new file mode 100644
index 0000000..514bff3
--- /dev/null
+++ b/src/functions/conversions/padicprec
@@ -0,0 +1,9 @@
+Function: padicprec
+Section: conversions
+C-Name: padicprec
+Prototype: lGG
+Help: padicprec(x,p): absolute p-adic precision of object x.
+Doc: absolute $p$-adic precision of the object $x$. This is the minimum
+ precision of the components of $x$. The result is \tet{LONG_MAX}
+ ($2^{31}-1$ for 32-bit machines or $2^{63}-1$ for 64-bit machines) if $x$ is
+ an exact object.
diff --git a/src/functions/conversions/permtonum b/src/functions/conversions/permtonum
new file mode 100644
index 0000000..3f8faca
--- /dev/null
+++ b/src/functions/conversions/permtonum
@@ -0,0 +1,8 @@
+Function: permtonum
+Section: conversions
+C-Name: permtonum
+Prototype: G
+Help: permtonum(x): ordinal (between 1 and n!) of permutation x.
+Doc: given a permutation $x$ on $n$ elements, gives the number $k$ such that
+ $x=\kbd{numtoperm(n,k)}$, i.e.~inverse function of \tet{numtoperm}.
+ The numbering used is the standard lexicographic ordering, starting at $0$.
diff --git a/src/functions/conversions/precision b/src/functions/conversions/precision
new file mode 100644
index 0000000..5d657ec
--- /dev/null
+++ b/src/functions/conversions/precision
@@ -0,0 +1,72 @@
+Function: precision
+Section: conversions
+C-Name: precision0
+Prototype: GD0,L,
+Help: precision(x,{n}): if n is present, return x at precision n. If n is omitted, return real precision of object x.
+Description:
+ (real):small          prec2ndec(gprecision($1))
+ (gen):int             precision0($1, 0)
+ (real,0):small        prec2ndec(gprecision($1))
+ (gen,0):int           precision0($1, 0)
+ (real,#small):real    rtor($1, ndec2prec($2))
+ (gen,#small):gen      gprec($1, $2)
+ (real,small):real     precision0($1, $2)
+ (gen,small):gen       precision0($1, $2)
+
+Doc: the function has two different behaviors according to whether $n$ is present or not.
+
+ If $n$ is missing, the function returns the precision in decimal digits of the
+ PARI object $x$. If $x$ is
+ an exact object, the largest single precision integer is returned.
+ \bprog
+ ? precision(exp(1e-100))
+ %1 = 134                \\ 134 significant decimal digits
+ ? precision(2 + x)
+ %2 = 2147483647         \\ exact object
+ ? precision(0.5 + O(x))
+ %3 = 28                 \\ floating point accuracy, NOT series precision
+ ? precision( [ exp(1e-100), 0.5 ] )
+ %4 = 28                 \\ minimal accuracy among components
+ @eprog\noindent
+ The return value for exact objects is meaningless since it is not even the
+ same on 32 and 64-bit machines. The proper way to test whether an object is
+ exact is
+ \bprog
+ ? isexact(x) = precision(x) == precision(0)
+ @eprog
+
+ If $n$ is present, the function creates a new object equal to $x$ with a new
+ ``precision'' $n$. (This never changes the type of the result. In particular
+ it is not possible to use it to obtain a polynomial from a power series; for
+ that, see \tet{truncate}.) Now the meaning of precision is different from the
+ above (floating point accuracy), and depends on the type of $x$:
+
+ For exact types, no change. For $x$ a vector or a matrix, the operation is
+ done componentwise.
+
+ For real $x$, $n$ is the number of desired significant \emph{decimal}
+ digits. If $n$ is smaller than the precision of $x$, $x$ is truncated,
+ otherwise $x$ is extended with zeros.
+
+ For $x$ a $p$-adic or a power series, $n$ is the desired number of
+ \emph{significant} $p$-adic or $X$-adic digits, where $X$ is the main
+ variable of $x$. (Note: yes, this is inconsistent.)
+ Note that the precision is a priori distinct from the exponent $k$ appearing
+ in $O(*^k)$; it is indeed equal to $k$ if and only if $x$ is a $p$-adic
+ or $X$-adic \emph{unit}.
+ \bprog
+ ? precision(1 + O(x), 10)
+ %1 = 1 + O(x^10)
+ ? precision(x^2 + O(x^10), 3)
+ %2 = x^2 + O(x^5)
+ ? precision(7^2 + O(7^10), 3)
+ %3 = 7^2 + O(7^5)
+ @eprog\noindent
+ For the last two examples, note that $x^2 + O(x^5) = x^2(1 + O(x^3))$
+ indeed has 3 significant coefficients
+
+
+
+Variant: Also available are \fun{GEN}{gprec}{GEN x, long n} and
+ \fun{long}{precision}{GEN x}. In both, the accuracy is expressed in
+ \emph{words} (32-bit or 64-bit depending on the architecture).
diff --git a/src/functions/conversions/random b/src/functions/conversions/random
new file mode 100644
index 0000000..b805707
--- /dev/null
+++ b/src/functions/conversions/random
@@ -0,0 +1,76 @@
+Function: random
+Section: conversions
+C-Name: genrand
+Prototype: DG
+Help: random({N=2^31}): random object, depending on the type of N.
+ Integer between 0 and N-1 (t_INT), int mod N (t_INTMOD), element in a finite
+ field (t_FFELT), point on an elliptic curve (ellinit mod p or over a finite
+ field).
+Doc:
+ returns a random element in various natural sets depending on the
+ argument $N$.
+
+ \item \typ{INT}: returns an integer
+ uniformly distributed between $0$ and $N-1$. Omitting the argument
+ is equivalent to \kbd{random(2\pow31)}.
+
+ \item \typ{REAL}: returns a real number in $[0,1[$ with the same accuracy as
+ $N$ (whose mantissa has the same number of significant words).
+
+ \item \typ{INTMOD}: returns a random intmod for the same modulus.
+
+ \item \typ{FFELT}: returns a random element in the same finite field.
+
+ \item \typ{VEC} of length $2$, $N = [a,b]$: returns an integer uniformly
+ distributed between $a$ and $b$.
+
+ \item \typ{VEC} generated by \kbd{ellinit} over a finite field $k$
+ (coefficients are \typ{INTMOD}s modulo a prime or \typ{FFELT}s): returns a
+ ``random'' $k$-rational \emph{affine} point on the curve. More precisely
+ if the curve has a single point (at infinity!) we return it; otherwise
+ we return an affine point by drawing an abscissa uniformly at
+ random until \tet{ellordinate} succeeds. Note that this is definitely not a
+ uniform distribution over $E(k)$, but it should be good enough for
+ applications.
+
+ \item \typ{POL} return a random polynomial of degree at most the degree of $N$.
+ The coefficients are drawn by applying \kbd{random} to the leading
+ coefficient of $N$.
+
+ \bprog
+ ? random(10)
+ %1 = 9
+ ? random(Mod(0,7))
+ %2 = Mod(1, 7)
+ ? a = ffgen(ffinit(3,7), 'a); random(a)
+ %3 = a^6 + 2*a^5 + a^4 + a^3 + a^2 + 2*a
+ ? E = ellinit([3,7]*Mod(1,109)); random(E)
+ %4 = [Mod(103, 109), Mod(10, 109)]
+ ? E = ellinit([1,7]*a^0); random(E)
+ %5 = [a^6 + a^5 + 2*a^4 + 2*a^2, 2*a^6 + 2*a^4 + 2*a^3 + a^2 + 2*a]
+ ? random(Mod(1,7)*x^4)
+ %6 = Mod(5, 7)*x^4 + Mod(6, 7)*x^3 + Mod(2, 7)*x^2 + Mod(2, 7)*x + Mod(5, 7)
+
+ @eprog
+ These variants all depend on a single internal generator, and are
+ independent from your operating system's random number generators.
+ A random seed may be obtained via \tet{getrand}, and reset
+ using \tet{setrand}: from a given seed, and given sequence of \kbd{random}s,
+ the exact same values will be generated. The same seed is used at each
+ startup, reseed the generator yourself if this is a problem. Note that
+ internal functions also call the random number generator; adding such a
+ function call in the middle of your code will change the numbers produced.
+
+ \misctitle{Technical note}
+ Up to
+ version 2.4 included, the internal generator produced pseudo-random numbers
+ by means of linear congruences, which were not well distributed in arithmetic
+ progressions. We now
+ use Brent's XORGEN algorithm, based on Feedback Shift Registers, see
+ \kbd{http://wwwmaths.anu.edu.au/\til{}brent/random.html}. The generator has period
+ $2^{4096}-1$, passes the Crush battery of statistical tests of L'Ecuyer and
+ Simard, but is not suitable for cryptographic purposes: one can reconstruct
+ the state vector from a small sample of consecutive values, thus predicting
+ the entire sequence.
+Variant:
+  Also available: \fun{GEN}{ellrandom}{GEN E} and \fun{GEN}{ffrandom}{GEN a}.
diff --git a/src/functions/conversions/real b/src/functions/conversions/real
new file mode 100644
index 0000000..7ce1d70
--- /dev/null
+++ b/src/functions/conversions/real
@@ -0,0 +1,7 @@
+Function: real
+Section: conversions
+C-Name: greal
+Prototype: G
+Help: real(x): real part of x.
+Doc: real part of $x$. In the case where $x$ is a quadratic number, this is the
+ coefficient of $1$ in the ``canonical'' integral basis $(1,\omega)$.
diff --git a/src/functions/conversions/round b/src/functions/conversions/round
new file mode 100644
index 0000000..0f3bd76
--- /dev/null
+++ b/src/functions/conversions/round
@@ -0,0 +1,35 @@
+Function: round
+Section: conversions
+C-Name: round0
+Prototype: GD&
+Help: round(x,{&e}): take the nearest integer to all the coefficients of x.
+ If e is present, do not take into account loss of integer part precision,
+ and set e = error estimate in bits.
+Description:
+ (small):small:parens   $1
+ (int):int:copy:parens  $1
+ (real):int             roundr($1)
+ (mp):int               mpround($1)
+ (mp, &small):int       grndtoi($1, &$2)
+ (mp, &int):int         round0($1, &$2)
+ (gen):gen              ground($1)
+ (gen, &small):gen      grndtoi($1, &$2)
+ (gen, &int):gen        round0($1, &$2)
+Doc: If $x$ is in $\R$, rounds $x$ to the nearest integer (rounding to
+ $+\infty$ in case of ties), then and sets $e$ to the number of error bits,
+ that is the binary exponent of the difference between the original and the
+ rounded value (the ``fractional part''). If the exponent of $x$ is too large
+ compared to its precision (i.e.~$e>0$), the result is undefined and an error
+ occurs if $e$ was not given.
+
+ \misctitle{Important remark} Contrary to the other truncation functions,
+ this function operates on every coefficient at every level of a PARI object.
+ For example
+ $$\text{truncate}\left(\dfrac{2.4*X^2-1.7}{X}\right)=2.4*X,$$
+ whereas
+ $$\text{round}\left(\dfrac{2.4*X^2-1.7}{X}\right)=\dfrac{2*X^2-2}{X}.$$
+ An important use of \kbd{round} is to get exact results after an approximate
+ computation, when theory tells you that the coefficients must be integers.
+
+Variant: Also available are \fun{GEN}{grndtoi}{GEN x, long *e} and
+ \fun{GEN}{ground}{GEN x}.
diff --git a/src/functions/conversions/simplify b/src/functions/conversions/simplify
new file mode 100644
index 0000000..9794e8d
--- /dev/null
+++ b/src/functions/conversions/simplify
@@ -0,0 +1,32 @@
+Function: simplify
+Section: conversions
+C-Name: simplify
+Prototype: G
+Help: simplify(x): simplify the object x as much as possible.
+Doc:
+ this function simplifies $x$ as much as it can. Specifically, a complex or
+ quadratic number whose imaginary part is the integer 0 (i.e.~not \kbd{Mod(0,2)}
+ or \kbd{0.E-28}) is converted to its real part, and a polynomial of degree $0$
+ is converted to its constant term. Simplifications occur recursively.
+
+ This function is especially useful before using arithmetic functions,
+ which expect integer arguments:
+ \bprog
+ ? x = 2 + y - y
+ %1 = 2
+ ? isprime(x)
+   ***   at top-level: isprime(x)
+   ***                 ^----------
+   *** isprime: not an integer argument in an arithmetic function
+ ? type(x)
+ %2 = "t_POL"
+ ? type(simplify(x))
+ %3 = "t_INT"
+ @eprog
+ Note that GP results are simplified as above before they are stored in the
+ history. (Unless you disable automatic simplification with \b{y}, that is.)
+ In particular
+ \bprog
+ ? type(%1)
+ %4 = "t_INT"
+ @eprog
diff --git a/src/functions/conversions/sizebyte b/src/functions/conversions/sizebyte
new file mode 100644
index 0000000..7aea0fe
--- /dev/null
+++ b/src/functions/conversions/sizebyte
@@ -0,0 +1,10 @@
+Function: sizebyte
+Section: conversions
+C-Name: gsizebyte
+Prototype: lG
+Help: sizebyte(x): number of bytes occupied by the complete tree of the
+ object x.
+Doc: outputs the total number of bytes occupied by the tree representing the
+ PARI object $x$.
+Variant: Also available is \fun{long}{gsizeword}{GEN x} returning a
+ number of \emph{words}.
diff --git a/src/functions/conversions/sizedigit b/src/functions/conversions/sizedigit
new file mode 100644
index 0000000..4d2dbca
--- /dev/null
+++ b/src/functions/conversions/sizedigit
@@ -0,0 +1,10 @@
+Function: sizedigit
+Section: conversions
+C-Name: sizedigit
+Prototype: lG
+Help: sizedigit(x): maximum number of decimal digits minus one of (the
+ coefficients of) x.
+Doc:
+ outputs a quick bound for the number of decimal
+ digits of (the components of) $x$, off by at most $1$. If you want the
+ exact value, you can use \kbd{\#Str(x)}, which is slower.
diff --git a/src/functions/conversions/truncate b/src/functions/conversions/truncate
new file mode 100644
index 0000000..9bb6284
--- /dev/null
+++ b/src/functions/conversions/truncate
@@ -0,0 +1,36 @@
+Function: truncate
+Section: conversions
+C-Name: trunc0
+Prototype: GD&
+Help: truncate(x,{&e}): truncation of x; when x is a power series,take away
+ the O(X^). If e is present, do not take into account loss of integer part
+ precision, and set e = error estimate in bits.
+Description:
+ (small):small:parens   $1
+ (int):int:copy:parens  $1
+ (real):int             truncr($1)
+ (mp):int               mptrunc($1)
+ (mp, &small):int       gcvtoi($1, &$2)
+ (mp, &int):int         trunc0($1, &$2)
+ (gen):gen              gtrunc($1)
+ (gen, &small):gen      gcvtoi($1, &$2)
+ (gen, &int):gen        trunc0($1, &$2)
+Doc: truncates $x$ and sets $e$ to the number of
+ error bits. When $x$ is in $\R$, this means that the part after the decimal
+ point is chopped away, $e$ is the binary exponent of the difference between
+ the original and the truncated value (the ``fractional part''). If the
+ exponent of $x$ is too large compared to its precision (i.e.~$e>0$), the
+ result is undefined and an error occurs if $e$ was not given. The function
+ applies componentwise on vector / matrices; $e$ is then the maximal number of
+ error bits. If $x$ is a rational function, the result is the ``integer part''
+ (Euclidean quotient of numerator by denominator) and $e$ is not set.
+
+ Note a very special use of \kbd{truncate}: when applied to a power series, it
+ transforms it into a polynomial or a rational function with denominator
+ a power of $X$, by chopping away the $O(X^k)$. Similarly, when applied to
+ a $p$-adic number, it transforms it into an integer or a rational number
+ by chopping away the $O(p^k)$.
+
+Variant: The following functions are also available: \fun{GEN}{gtrunc}{GEN x}
+ and \fun{GEN}{gcvtoi}{GEN x, long *e}.
+
diff --git a/src/functions/conversions/valuation b/src/functions/conversions/valuation
new file mode 100644
index 0000000..313dd89
--- /dev/null
+++ b/src/functions/conversions/valuation
@@ -0,0 +1,21 @@
+Function: valuation
+Section: conversions
+C-Name: gvaluation
+Prototype: lGG
+Help: valuation(x,p): valuation of x with respect to p.
+Doc:
+ computes the highest
+ exponent of $p$ dividing $x$. If $p$ is of type integer, $x$ must be an
+ integer, an intmod whose modulus is divisible by $p$, a fraction, a
+ $q$-adic number with $q=p$, or a polynomial or power series in which case the
+ valuation is the minimum of the valuation of the coefficients.
+
+ If $p$ is of type polynomial, $x$ must be of type polynomial or rational
+ function, and also a power series if $x$ is a monomial. Finally, the
+ valuation of a vector, complex or quadratic number is the minimum of the
+ component valuations.
+
+ If $x=0$, the result is \tet{LONG_MAX} ($2^{31}-1$ for 32-bit machines or
+ $2^{63}-1$ for 64-bit machines) if $x$ is an exact object. If $x$ is a
+ $p$-adic numbers or power series, the result is the exponent of the zero.
+ Any other type combinations gives an error.
diff --git a/src/functions/conversions/variable b/src/functions/conversions/variable
new file mode 100644
index 0000000..42fb81d
--- /dev/null
+++ b/src/functions/conversions/variable
@@ -0,0 +1,39 @@
+Function: variable
+Section: conversions
+C-Name: gpolvar
+Prototype: DG
+Help: variable({x}): main variable of object x. Gives p for p-adic x, 0
+ if no variable can be associated to x. Returns the list of user variables if
+ x is omitted.
+Description:
+ (pol):var:parens:copy        $var:1
+ (gen):gen        gpolvar($1)
+Doc:
+ gives the main variable of the object $x$ (the variable with the highest
+ priority used in $x$), and $p$ if $x$ is a $p$-adic number. Return $0$ if
+ $x$ has no variable associated to it.
+ \bprog
+ ? variable(x^2 + y)
+ %1 = x
+ ? variable(1 + O(5^2))
+ %2 = 5
+ ? variable([x,y,z,t])
+ %3 = x
+ ? variable(1)
+ %4 = 0
+ @eprog\noindent The construction
+ \bprog
+    if (!variable(x),...)
+ @eprog\noindent can be used to test whether a variable is attached to $x$.
+
+ If $x$ is omitted, returns the list of user variables known to the
+ interpreter, by order of decreasing priority. (Highest priority is $x$,
+ which always come first.)
+
+Variant: However, in library mode, this function should not be used for $x$
+ non-\kbd{NULL}, since \tet{gvar} is more appropriate. Instead, for
+ $x$ a $p$-adic (type \typ{PADIC}), $p$ is $gel(x,2)$; otherwise, use
+ \fun{long}{gvar}{GEN x} which returns the variable number of $x$ if
+ it exists, \kbd{NO\_VARIABLE} otherwise, which satisfies the property
+ $\kbd{varncmp}(\kbd{NO\_VARIABLE}, v) > 0$ for all valid variable number
+ $v$, i.e. it has lower priority than any variable.
diff --git a/src/functions/default/TeXstyle b/src/functions/default/TeXstyle
new file mode 100644
index 0000000..b1a2630
--- /dev/null
+++ b/src/functions/default/TeXstyle
@@ -0,0 +1,17 @@
+Function: _def_TeXstyle
+Class: default
+Section: default
+C-Name: sd_TeXstyle
+Prototype:
+Help:
+Doc: the bits of this default allow
+ \kbd{gp} to use less rigid TeX formatting commands in the logfile. This
+ default is only taken into account when $\kbd{log} = 3$. The bits of
+ \kbd{TeXstyle} have the following meaning
+
+ 2: insert \kbd{\bs right} / \kbd{\bs left} pairs where appropriate.
+
+ 4: insert discretionary breaks in polynomials, to enhance the probability of
+ a good line break.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/breakloop b/src/functions/default/breakloop
new file mode 100644
index 0000000..7b418fa
--- /dev/null
+++ b/src/functions/default/breakloop
@@ -0,0 +1,12 @@
+Function: _def_breakloop
+Class: gp_default
+Section: default
+C-Name: sd_breakloop
+Prototype:
+Help:
+Doc: if true, enables the ``break loop'' debugging mode, see
+ \secref{se:break_loop}.
+
+ The default value is \kbd{1} if we are running an interactive \kbd{gp}
+ session, and \kbd{0} otherwise.
+
diff --git a/src/functions/default/colors b/src/functions/default/colors
new file mode 100644
index 0000000..459efc7
--- /dev/null
+++ b/src/functions/default/colors
@@ -0,0 +1,52 @@
+Function: _def_colors
+Class: default
+Section: default
+C-Name: sd_colors
+Prototype:
+Help:
+Doc: this default is only usable if \kbd{gp}
+ is running within certain color-capable terminals. For instance \kbd{rxvt},
+ \kbd{color\_xterm} and modern versions of \kbd{xterm} under X Windows, or
+ standard Linux/DOS text consoles. It causes \kbd{gp} to use a small palette of
+ colors for its output. With xterms, the colormap used corresponds to the
+ resources \kbd{Xterm*color$n$} where $n$ ranges from $0$ to $15$ (see the
+ file \kbd{misc/color.dft} for an example). Accepted values for this
+ default are strings \kbd{"$a_1$,\dots,$a_k$"} where $k\le7$ and each
+ $a_i$ is either
+
+ \noindent\item the keyword \kbd{no} (use the default color, usually
+ black on transparent background)
+
+ \noindent\item an integer between 0 and 15 corresponding to the
+ aforementioned colormap
+
+ \noindent\item a triple $[c_0,c_1,c_2]$ where $c_0$ stands for foreground
+ color, $c_1$ for background color, and $c_2$ for attributes (0 is default, 1
+ is bold, 4 is underline).
+
+ The output objects thus affected are respectively error messages,
+ history numbers, prompt, input line, output, help messages, timer (that's
+ seven of them). If $k < 7$, the remaining $a_i$ are assumed to be $no$. For
+ instance
+ %
+ \bprog
+ default(colors, "9, 5, no, no, 4")
+ @eprog
+ \noindent
+ typesets error messages in color $9$, history numbers in color $5$, output in
+ color $4$, and does not affect the rest.
+
+ A set of default colors for dark (reverse video or PC console) and light
+ backgrounds respectively is activated when \kbd{colors} is set to
+ \kbd{darkbg}, resp.~\kbd{lightbg} (or any proper prefix: \kbd{d} is
+ recognized as an abbreviation for \kbd{darkbg}). A bold variant of
+ \kbd{darkbg}, called \kbd{boldfg}, is provided if you find the former too
+ pale.
+
+ \emacs In the present version, this default is incompatible with PariEmacs.
+ Changing it will just fail silently (the alternative would be to display
+ escape sequences as is, since Emacs will refuse to interpret them).
+ You must customize color highlighting from the PariEmacs side, see its
+ documentation.
+
+ The default value is \kbd{""} (no colors).
diff --git a/src/functions/default/compatible b/src/functions/default/compatible
new file mode 100644
index 0000000..d80ab34
--- /dev/null
+++ b/src/functions/default/compatible
@@ -0,0 +1,45 @@
+Function: _def_compatible
+Class: default
+Section: default
+C-Name: sd_compatible
+Prototype:
+Help:
+Doc: The GP function names and syntax
+ have changed tremendously between versions 1.xx and 2.00. To help you cope
+ with this we provide some kind of backward compatibility, depending on the
+ value of this default:
+
+ \quad \kbd{compatible} = 0: no backward compatibility. In this mode, a very
+ handy function, to be described in \secref{se:whatnow}, is \kbd{whatnow},
+ which tells you what has become of your favorite functions, which \kbd{gp}
+ suddenly can't seem to remember.
+
+ \quad \kbd{compatible} = 1: warn when using obsolete functions, but
+ otherwise accept them. The output uses the new conventions though, and
+ there may be subtle incompatibilities between the behavior of former and
+ current functions, even when they share the same name (the current function
+ is used in such cases, of course!). We thought of this one as a transitory
+ help for \kbd{gp} old-timers. Thus, to encourage switching to \kbd{compatible}=0,
+ it is not possible to disable the warning.
+
+ \quad \kbd{compatible} = 2: use only the old function naming scheme (as
+ used up to version 1.39.15), but \emph{taking case into account}. Thus
+ \kbd{I} (${}=\sqrt{-1}$) is not the same as \kbd{i} (user variable, unbound
+ by default), and you won't get an error message using \kbd{i} as a loop
+ index as used to be the case.
+
+ \quad \kbd{compatible} = 3: try to mimic exactly the former behavior. This
+ is not always possible when functions have changed in a fundamental way.
+ But these differences are usually for the better (they were meant to,
+ anyway), and will probably not be discovered by the casual user.
+
+ One adverse side effect is that any user functions and aliases that have
+ been defined \emph{before} changing \kbd{compatible} will get erased if this
+ change modifies the function list, i.e.~if you move between groups
+ $\{0,1\}$ and $\{2,3\}$ (variables are unaffected). We of course strongly
+ encourage you to try and get used to the setting \kbd{compatible}=0.
+
+ Note that the default \tet{new_galois_format} is another compatibility setting,
+ which is completely independent of \kbd{compatible}.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/datadir b/src/functions/default/datadir
new file mode 100644
index 0000000..659fe16
--- /dev/null
+++ b/src/functions/default/datadir
@@ -0,0 +1,13 @@
+Function: _def_datadir
+Class: default
+Section: default
+C-Name: sd_datadir
+Prototype:
+Help:
+Doc: the name of directory containing the optional data files. For now,
+ this includes the \kbd{elldata}, \kbd{galdata}, \kbd{galpol}, \kbd{seadata}
+ packages.
+
+ The default value is \datadir (the location of installed precomputed data,
+ can be specified via \kbd{Configure --datadir=}).
+
diff --git a/src/functions/default/debug b/src/functions/default/debug
new file mode 100644
index 0000000..71eb4da
--- /dev/null
+++ b/src/functions/default/debug
@@ -0,0 +1,10 @@
+Function: _def_debug
+Class: default
+Section: default
+C-Name: sd_debug
+Prototype:
+Help:
+Doc: debugging level. If it is non-zero, some extra messages may be printed,
+ according to what is going on (see~\b{g}).
+
+ The default value is \kbd{0} (no debugging messages).
diff --git a/src/functions/default/debugfiles b/src/functions/default/debugfiles
new file mode 100644
index 0000000..f77c2bd
--- /dev/null
+++ b/src/functions/default/debugfiles
@@ -0,0 +1,11 @@
+Function: _def_debugfiles
+Class: default
+Section: default
+C-Name: sd_debugfiles
+Prototype:
+Help:
+Doc: file usage debugging level. If it is non-zero, \kbd{gp} will print
+ information on file descriptors in use, from PARI's point of view
+ (see~\b{gf}).
+
+ The default value is \kbd{0} (no debugging messages).
diff --git a/src/functions/default/debugmem b/src/functions/default/debugmem
new file mode 100644
index 0000000..a7dfcfe
--- /dev/null
+++ b/src/functions/default/debugmem
@@ -0,0 +1,16 @@
+Function: _def_debugmem
+Class: default
+Section: default
+C-Name: sd_debugmem
+Prototype:
+Help:
+Doc: memory debugging level. If it is non-zero, \kbd{gp} will regularly print
+ information on memory usage. If it's greater than 2, it will indicate any
+ important garbage collecting and the function it is taking place in
+ (see~\b{gm}).
+
+ \noindent {\bf Important Note:} As it noticeably slows down the performance,
+ the first functionality (memory usage) is disabled if you're not running a
+ version compiled for debugging (see Appendix~A).
+
+ The default value is \kbd{0} (no debugging messages).
diff --git a/src/functions/default/echo b/src/functions/default/echo
new file mode 100644
index 0000000..e6c1cae
--- /dev/null
+++ b/src/functions/default/echo
@@ -0,0 +1,13 @@
+Function: _def_echo
+Class: gp_default
+Section: default
+C-Name: sd_echo
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). When \kbd{echo}
+ mode is on, each command is reprinted before being executed. This can be
+ useful when reading a file with the \b{r} or \kbd{read} commands. For
+ example, it is turned on at the beginning of the test files used to check
+ whether \kbd{gp} has been built correctly (see \b{e}).
+
+ The default value is \kbd{0} (no echo).
diff --git a/src/functions/default/factor_add_primes b/src/functions/default/factor_add_primes
new file mode 100644
index 0000000..2050596
--- /dev/null
+++ b/src/functions/default/factor_add_primes
@@ -0,0 +1,17 @@
+Function: _def_factor_add_primes
+Class: default
+Section: default
+C-Name: sd_factor_add_primes
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). If on,
+ the integer factorization machinery calls \tet{addprimes} on primes
+ factor that were difficult to find (larger than $2^24$), so they are
+ automatically tried first in other factorizations. If a routine is performing
+ (or has performed) a factorization and is interrupted by an error or via
+ Control-C, this lets you recover the prime factors already found. The
+ downside is that a huge \kbd{addprimes} table unrelated to the current
+ computations will slow down arithmetic functions relying on integer
+ factorization; one should then empty the table using \tet{removeprimes}.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/factor_proven b/src/functions/default/factor_proven
new file mode 100644
index 0000000..eb25061
--- /dev/null
+++ b/src/functions/default/factor_proven
@@ -0,0 +1,16 @@
+Function: _def_factor_proven
+Class: default
+Section: default
+C-Name: sd_factor_proven
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). By
+ default, the factors output by the integer factorization machinery are
+ only pseudo-primes, not proven primes. If this toggle is
+ set, a primality proof is done for each factor and all results depending on
+ integer factorization are fully proven. This flag does not affect partial
+ factorization when it is explicitly requested. It also does not affect the
+ private table managed by \tet{addprimes}: its entries are included as is in
+ factorizations, without being tested for primality.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/format b/src/functions/default/format
new file mode 100644
index 0000000..95fdb01
--- /dev/null
+++ b/src/functions/default/format
@@ -0,0 +1,32 @@
+Function: _def_format
+Class: default
+Section: default
+C-Name: sd_format
+Prototype:
+Help:
+Doc: of the form x$.n$, where x (conversion style)
+ is a letter in $\{\kbd{e},\kbd{f},\kbd{g}\}$, and $n$ (precision) is an
+ integer; this affects the way real numbers are printed:
+
+ \item If the conversion style is \kbd{e}, real numbers are printed in
+ \idx{scientific format}, always with an explicit exponent,
+ e.g.~\kbd{3.3 E-5}.
+
+ \item In style \kbd{f}, real numbers are generally printed in \idx{fixed
+ floating point format} without exponent, e.g.~\kbd{0.000033}. A large
+ real number, whose integer part is not well defined (not enough significant
+ digits), is printed in style~\kbd{e}. For instance \kbd{10.\pow 100} known to
+ ten significant digits is always printed in style \kbd{e}.
+
+ \item In style \kbd{g}, non-zero real numbers are printed in \kbd{f} format,
+ except when their decimal exponent is $< -4$, in which case they are printed in
+ \kbd{e} format. Real zeroes (of arbitrary exponent) are printed in \kbd{e}
+ format.
+
+ The precision $n$ is the number of significant digits printed for real
+ numbers, except if $n<0$ where all the significant digits will be printed
+ (initial default 28, or 38 for 64-bit machines). For more powerful formatting
+ possibilities, see \tet{printf} and \tet{Strprintf}.
+
+ The default value is \kbd{"g.28"} and \kbd{"g.38"} on 32-bit and
+ 64-bit machines, respectively.
diff --git a/src/functions/default/graphcolormap b/src/functions/default/graphcolormap
new file mode 100644
index 0000000..011012a
--- /dev/null
+++ b/src/functions/default/graphcolormap
@@ -0,0 +1,23 @@
+Function: _def_graphcolormap
+Class: gp_default
+Section: default
+C-Name: sd_graphcolormap
+Prototype:
+Help:
+Doc: a vector of colors, to be
+ used by hi-res graphing routines. Its length is arbitrary, but it must
+ contain at least 3 entries: the first 3 colors are used for background,
+ frame/ticks and axes respectively. All colors in the colormap may be freely
+ used in \tet{plotcolor} calls.
+
+ A color is either given as in the default by character strings or by an RGB
+ code. For valid character strings, see the standard \kbd{rgb.txt} file in X11
+ distributions, where we restrict to lowercase letters and remove all
+ whitespace from color names. An RGB code is a vector with 3 integer entries
+ between 0 and 255. For instance \kbd{[250, 235, 215]} and
+ \kbd{"antiquewhite"} represent the same color. RGB codes are cryptic but
+ often easier to generate.
+
+ The default value is [\kbd{"white"}, \kbd{"black"}, \kbd{"blue"},
+ \kbd{"violetred"}, \kbd{"red"}, \kbd{"green"}, \kbd{"grey"},
+ \kbd{"gainsboro"}].
diff --git a/src/functions/default/graphcolors b/src/functions/default/graphcolors
new file mode 100644
index 0000000..4b490b3
--- /dev/null
+++ b/src/functions/default/graphcolors
@@ -0,0 +1,16 @@
+Function: _def_graphcolors
+Class: gp_default
+Section: default
+C-Name: sd_graphcolors
+Prototype:
+Help:
+Doc: entries in the
+ \tet{graphcolormap} that will be used to plot multi-curves. The successive
+ curves are drawn in colors
+
+ \kbd{graphcolormap[graphcolors[1]]}, \kbd{graphcolormap[graphcolors[2]]},
+   \dots
+
+ cycling when the \kbd{graphcolors} list is exhausted.
+
+ The default value is \kbd{[4,5]}.
diff --git a/src/functions/default/help b/src/functions/default/help
new file mode 100644
index 0000000..b25aaeb
--- /dev/null
+++ b/src/functions/default/help
@@ -0,0 +1,12 @@
+Function: _def_help
+Class: gp_default
+Section: default
+C-Name: sd_help
+Prototype:
+Help:
+Doc: name of the external help program to use from within \kbd{gp} when
+ extended help is invoked, usually through a \kbd{??} or \kbd{???} request
+ (see \secref{se:exthelp}), or \kbd{M-H} under readline (see
+ \secref{se:readline}).
+
+ The default value is the path to the \kbd{gphelp} script we install.
diff --git a/src/functions/default/histfile b/src/functions/default/histfile
new file mode 100644
index 0000000..74be8bc
--- /dev/null
+++ b/src/functions/default/histfile
@@ -0,0 +1,15 @@
+Function: _def_histfile
+Class: gp_default
+Section: default
+C-Name: sd_histfile
+Prototype:
+Help:
+Doc: name of a file where
+ \kbd{gp} will keep a history of all \emph{input} commands (results are
+ omitted). If this file exists when the value of \kbd{histfile} changes,
+ it is read in and becomes part of the session history. Thus, setting this
+ default in your gprc saves your readline history between sessions. Setting
+ this default to the empty string \kbd{""} changes it to
+ \kbd{$<$undefined$>$}
+
+ The default value is \kbd{$<$undefined$>$} (no history file).
diff --git a/src/functions/default/histsize b/src/functions/default/histsize
new file mode 100644
index 0000000..74dda82
--- /dev/null
+++ b/src/functions/default/histsize
@@ -0,0 +1,13 @@
+Function: _def_histsize
+Class: default
+Section: default
+C-Name: sd_histsize
+Prototype:
+Help:
+Doc: \kbd{gp} keeps a history of the last
+ \kbd{histsize} results computed so far, which you can recover using the
+ \kbd{\%} notation (see \secref{se:history}). When this number is exceeded,
+ the oldest values are erased. Tampering with this default is the only way to
+ get rid of the ones you do not need anymore.
+
+ The default value is \kbd{5000}.
diff --git a/src/functions/default/lines b/src/functions/default/lines
new file mode 100644
index 0000000..f21615d
--- /dev/null
+++ b/src/functions/default/lines
@@ -0,0 +1,14 @@
+Function: _def_lines
+Class: gp_default
+Section: default
+C-Name: sd_lines
+Prototype:
+Help:
+Doc: if set to a positive value, \kbd{gp} prints at
+ most that many lines from each result, terminating the last line shown with
+ \kbd{[+++]} if further material has been suppressed. The various \kbd{print}
+ commands (see \secref{se:gp_program}) are unaffected, so you can always type
+ \kbd{print(\%)} or \b{a} to view the full result. If the actual screen width
+ cannot be determined, a ``line'' is assumed to be 80 characters long.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/linewrap b/src/functions/default/linewrap
new file mode 100644
index 0000000..0050eb2
--- /dev/null
+++ b/src/functions/default/linewrap
@@ -0,0 +1,10 @@
+Function: _def_linewrap
+Class: gp_default
+Section: default
+C-Name: sd_linewrap
+Prototype:
+Help:
+Doc: if set to a positive value, \kbd{gp} wraps every single line after
+ printing that many characters.
+
+ The default value is \kbd{0} (unset).
diff --git a/src/functions/default/log b/src/functions/default/log
new file mode 100644
index 0000000..6279d22
--- /dev/null
+++ b/src/functions/default/log
@@ -0,0 +1,23 @@
+Function: _def_log
+Class: default
+Section: default
+C-Name: sd_log
+Prototype:
+Help:
+Doc: this can be either 0 (off) or 1, 2, 3
+ (on, see below for the various modes). When logging mode is turned on, \kbd{gp}
+ opens a log file, whose exact name is determined by the \kbd{logfile}
+ default. Subsequently, all the commands and results will be written to that
+ file (see \b{l}). In case a file with this precise name already existed, it
+ will not be erased: your data will be \emph{appended} at the end.
+
+ The specific positive values of \kbd{log} have the following meaning
+
+ 1: plain logfile
+
+ 2: emit color codes to the logfile (if \kbd{colors} is set).
+
+ 3: write LaTeX output to the logfile (can be further customized using
+ \tet{TeXstyle}).
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/logfile b/src/functions/default/logfile
new file mode 100644
index 0000000..9bd7ec9
--- /dev/null
+++ b/src/functions/default/logfile
@@ -0,0 +1,10 @@
+Function: _def_logfile
+Class: default
+Section: default
+C-Name: sd_logfile
+Prototype:
+Help:
+Doc: name of the log file to be used when the \kbd{log} toggle is on.
+ Environment and time expansion are performed.
+
+ The default value is \kbd{"pari.log"}.
diff --git a/src/functions/default/nbthreads b/src/functions/default/nbthreads
new file mode 100644
index 0000000..e5b84aa
--- /dev/null
+++ b/src/functions/default/nbthreads
@@ -0,0 +1,16 @@
+Function: _def_nbthreads
+Class: default
+Section: default
+C-Name: sd_nbthreads
+Prototype:
+Help:
+Doc: Number of threads to use for parallel computing.
+ The exact meaning an default depend on the \kbd{mt} engine used:
+
+ \item \kbd{single}: not used (always one thread).
+
+ \item \kbd{pthread}: number of threads (unlimited, default: number of core)
+
+ \item \kbd{mpi}: number of MPI process to use (limited to the number allocated by \kbd{mpirun},
+ default: use all allocated process).
+
diff --git a/src/functions/default/new_galois_format b/src/functions/default/new_galois_format
new file mode 100644
index 0000000..aa6631b
--- /dev/null
+++ b/src/functions/default/new_galois_format
@@ -0,0 +1,13 @@
+Function: _def_new_galois_format
+Class: default
+Section: default
+C-Name: sd_new_galois_format
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). If on,
+ the \tet{polgalois} command will use a different, more
+ consistent, naming scheme for Galois groups. This default is provided to
+ ensure that scripts can control this behavior and do not break unexpectedly.
+
+ The default value is \kbd{0}. This value will change to $1$ (set) in the next
+ major version.
diff --git a/src/functions/default/output b/src/functions/default/output
new file mode 100644
index 0000000..0079f40
--- /dev/null
+++ b/src/functions/default/output
@@ -0,0 +1,33 @@
+Function: _def_output
+Class: default
+Section: default
+C-Name: sd_output
+Prototype:
+Help:
+Doc: there are three possible values: 0
+ (=~\var{raw}), 1 (=~\var{prettymatrix}), or 3
+ (=~\var{external} \var{prettyprint}). This
+ means that, independently of the default \kbd{format} for reals which we
+ explained above, you can print results in three ways:
+
+ \item \tev{raw format}, i.e.~a format which is equivalent to what you
+ input, including explicit multiplication signs, and everything typed on a
+ line instead of two dimensional boxes. This can have several advantages, for
+ instance it allows you to pick the result with a mouse or an editor, and to
+ paste it somewhere else.
+
+ \item \tev{prettymatrix format}: this is identical to raw format, except
+ that matrices are printed as boxes instead of horizontally. This is
+ prettier, but takes more space and cannot be used for input. Column vectors
+ are still printed horizontally.
+
+ \item \tev{external prettyprint}: pipes all \kbd{gp}
+ output in TeX format to an external prettyprinter, according to the value of
+ \tet{prettyprinter}. The default script (\tet{tex2mail}) converts its input
+ to readable two-dimensional text.
+
+ Independently of the setting of this default, an object can be printed
+ in any of the three formats at any time using the commands \b{a} and \b{m}
+ and \b{B} respectively.
+
+ The default value is \kbd{1} (\var{prettymatrix}).
diff --git a/src/functions/default/parisize b/src/functions/default/parisize
new file mode 100644
index 0000000..0aceeb1
--- /dev/null
+++ b/src/functions/default/parisize
@@ -0,0 +1,17 @@
+Function: _def_parisize
+Class: default
+Section: default
+C-Name: sd_parisize
+Prototype:
+Help:
+Doc: \kbd{gp}, and in fact any program using the PARI
+ library, needs a \tev{stack} in which to do its computations. \kbd{parisize}
+ is the stack size, in bytes. It is strongly recommended you increase this
+ default (using the \kbd{-s} command-line switch, or a \tet{gprc}) if you can
+ afford it. Don't increase it beyond the actual amount of RAM installed on
+ your computer or \kbd{gp} will spend most of its time paging.
+
+ In case of emergency, you can use the \tet{allocatemem} function to
+ increase \kbd{parisize}, once the session is started.
+
+ The default value is 4M, resp.~8M on a 32-bit, resp.~64-bit machine.
diff --git a/src/functions/default/path b/src/functions/default/path
new file mode 100644
index 0000000..d0882fc
--- /dev/null
+++ b/src/functions/default/path
@@ -0,0 +1,16 @@
+Function: _def_path
+Class: default
+Section: default
+C-Name: sd_path
+Prototype:
+Help:
+Doc: this is a list of directories, separated by colons ':'
+ (semicolons ';' in the DOS world, since colons are preempted for drive names).
+ When asked to read a file whose name is not given by an absolute path
+ (does not start with \kbd{/}, \kbd{./} or \kbd{../}), \kbd{gp} will look for
+ it in these directories, in the order they were written in \kbd{path}. Here,
+ as usual, \kbd{.} means the current directory, and \kbd{..} its immediate
+ parent. Environment expansion is performed.
+
+ The default value is \kbd{".:\til:\til/gp"} on UNIX systems,
+ \kbd{".;C:\bs;C:\bs GP"} on DOS, OS/2 and Windows, and \kbd{"."} otherwise.
diff --git a/src/functions/default/prettyprinter b/src/functions/default/prettyprinter
new file mode 100644
index 0000000..ab8a264
--- /dev/null
+++ b/src/functions/default/prettyprinter
@@ -0,0 +1,12 @@
+Function: _def_prettyprinter
+Class: default
+Section: default
+C-Name: sd_prettyprinter
+Prototype:
+Help:
+Doc: the name of an external prettyprinter to use when
+ \kbd{output} is~3 (alternate prettyprinter). Note that the default
+ \tet{tex2mail} looks much nicer than the built-in ``beautified
+ format'' ($\kbd{output} = 2$).
+
+ The default value is \kbd{"tex2mail -TeX -noindent -ragged -by\_par"}.
diff --git a/src/functions/default/primelimit b/src/functions/default/primelimit
new file mode 100644
index 0000000..fd7a71a
--- /dev/null
+++ b/src/functions/default/primelimit
@@ -0,0 +1,36 @@
+Function: _def_primelimit
+Class: default
+Section: default
+C-Name: sd_primelimit
+Prototype:
+Help:
+Doc: \kbd{gp} precomputes a list of
+ all primes less than \kbd{primelimit} at initialization time, and can build
+ fast sieves on demand to quickly iterate over primes up to the \emph{square}
+ of \kbd{primelimit}. These are used by many arithmetic functions, usually for
+ trial division purposes. The maximal value is $2^{32} - 2049$ (resp $2^{64} -
+ 2049$) on a 32-bit (resp.~64-bit) machine, but values beyond $10^8$,
+ allowing to iterate over primes up to $10^{16}$, do not seem useful.
+
+ Since almost all arithmetic functions eventually require some table of prime
+ numbers, PARI guarantees that the first 6547 primes, up to and
+ including 65557, are precomputed, even if \kbd{primelimit} is $1$.
+
+ This default is only used on startup: changing it will not recompute a new
+ table.
+
+ \misctitle{Deprecated feature} \kbd{primelimit} was used in some
+ situations by algebraic number theory functions using the
+ \tet{nf_PARTIALFACT} flag (\tet{nfbasis}, \tet{nfdisc}, \tet{nfinit}, \dots):
+ this assumes that all primes $p > \kbd{primelimit}$ have a certain
+ property (the equation order is $p$-maximal). This is never done by default,
+ and must be explicitly set by the user of such functions. Nevertheless,
+ these functions now provide a more flexible interface, and their use
+ of the global default \kbd{primelimit} is deprecated.
+
+ \misctitle{Deprecated feature} \kbd{factor(N, 0)} was used to partially
+ factor integers by removing all prime factors $\leq$ \kbd{primelimit}.
+ Don't use this, supply an explicit bound: \kbd{factor(N, bound)},
+ which avoids relying on an unpredictable global variable.
+
+ The default value is \kbd{500k}.
diff --git a/src/functions/default/prompt b/src/functions/default/prompt
new file mode 100644
index 0000000..2e5b016
--- /dev/null
+++ b/src/functions/default/prompt
@@ -0,0 +1,31 @@
+Function: _def_prompt
+Class: gp_default
+Section: default
+C-Name: sd_prompt
+Prototype:
+Help:
+Doc: a string that will be printed as
+ prompt. Note that most usual escape sequences are available there: \b{e} for
+ Esc, \b{n} for Newline, \dots, \kbd{\bs\bs} for \kbd{\bs}. Time expansion is
+ performed.
+
+ This string is sent through the library function \tet{strftime} (on a
+ Unix system, you can try \kbd{man strftime} at your shell prompt). This means
+ that \kbd{\%} constructs have a special meaning, usually related to the time
+ and date. For instance, \kbd{\%H} = hour (24-hour clock) and \kbd{\%M} =
+ minute [00,59] (use \kbd{\%\%} to get a real \kbd{\%}).
+
+ If you use \kbd{readline}, escape sequences in your prompt will result in
+ display bugs. If you have a relatively recent \kbd{readline} (see the comment
+ at the end of \secref{se:def,colors}), you can brace them with special sequences
+ (\kbd{\bs[} and \kbd{\bs]}), and you will be safe. If these just result in
+ extra spaces in your prompt, then you'll have to get a more recent
+ \kbd{readline}. See the file \kbd{misc/gprc.dft} for an example.
+
+ \emacs {\bf Caution}: PariEmacs needs to know about the prompt pattern to
+ separate your input from previous \kbd{gp} results, without ambiguity. It is
+ not a trivial problem to adapt automatically this regular expression to an
+ arbitrary prompt (which can be self-modifying!). See PariEmacs's
+ documentation.
+
+ The default value is \kbd{"? "}.
diff --git a/src/functions/default/prompt_cont b/src/functions/default/prompt_cont
new file mode 100644
index 0000000..2af6aa2
--- /dev/null
+++ b/src/functions/default/prompt_cont
@@ -0,0 +1,12 @@
+Function: _def_prompt_cont
+Class: gp_default
+Section: default
+C-Name: sd_prompt_cont
+Prototype:
+Help:
+Doc: a string that will be printed
+ to prompt for continuation lines (e.g. in between braces, or after a
+ line-terminating backslash). Everything that applies to \kbd{prompt}
+ applies to \kbd{prompt\_cont} as well.
+
+ The default value is \kbd{""}.
diff --git a/src/functions/default/psfile b/src/functions/default/psfile
new file mode 100644
index 0000000..207ec91
--- /dev/null
+++ b/src/functions/default/psfile
@@ -0,0 +1,11 @@
+Function: _def_psfile
+Class: gp_default
+Section: default
+C-Name: sd_psfile
+Prototype:
+Help:
+Doc: name of the default file where
+ \kbd{gp} is to dump its PostScript drawings (these are appended, so that no
+ previous data are lost). Environment and time expansion are performed.
+
+ The default value is \kbd{"pari.ps"}.
diff --git a/src/functions/default/readline b/src/functions/default/readline
new file mode 100644
index 0000000..20d0b20
--- /dev/null
+++ b/src/functions/default/readline
@@ -0,0 +1,13 @@
+Function: _def_readline
+Class: gp_default
+Section: default
+C-Name: sd_readline
+Prototype:
+Help:
+Doc: switches readline line-editing
+ facilities on and off. This may be useful if you are running \kbd{gp} in a Sun
+ \tet{cmdtool}, which interacts badly with readline. Of course, until readline
+ is switched on again, advanced editing features like automatic completion
+ and editing history are not available.
+
+ The default value is \kbd{1}.
diff --git a/src/functions/default/realprecision b/src/functions/default/realprecision
new file mode 100644
index 0000000..07575fb
--- /dev/null
+++ b/src/functions/default/realprecision
@@ -0,0 +1,29 @@
+Function: _def_realprecision
+Class: default
+Section: default
+C-Name: sd_realprecision
+Prototype:
+Help:
+Doc: the number of significant digits used to convert exact inputs given to
+ transcendental functions (see \secref{se:trans}), or to create
+ absolute floating point constants (input as \kbd{1.0} or \kbd{Pi} for
+ instance). Unless you tamper with the \tet{format} default, this is also
+ the number of significant digits used to print a \typ{REAL} number;
+ \kbd{format} will override this latter behaviour, and allow you to have a
+ large internal precision while outputting few digits for instance.
+
+ Note that PARI's internal precision works on a word basis (by increments of
+ 32 or 64 bits), hence may be a little larger than the number of decimal
+ digits you expected. For instance to get 2 decimal digits you need one word
+ of precision which, on a 64-bit machine, actually gives you 19 digits ($19 <
+ \log_{10}(2^{64}) < 20$). The value returned when typing
+ \kbd{default(realprecision)} is the internal number of significant digits,
+ not the number of printed digits:
+ \bprog
+ ? default(realprecision, 2)
+       realprecision = 19 significant digits (2 digits displayed)
+ ? default(realprecision)
+ %1 = 19
+ @eprog
+ The default value is \kbd{38}, resp.~\kbd{28}, on a 64-bit, resp~.32-bit,
+ machine.
diff --git a/src/functions/default/recover b/src/functions/default/recover
new file mode 100644
index 0000000..e9694ad
--- /dev/null
+++ b/src/functions/default/recover
@@ -0,0 +1,11 @@
+Function: _def_recover
+Class: gp_default
+Section: default
+C-Name: sd_recover
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). If you change this to $0$, any
+ error becomes fatal and causes the gp interpreter to exit immediately. Can be
+ useful in batch job scripts.
+
+ The default value is \kbd{1}.
diff --git a/src/functions/default/secure b/src/functions/default/secure
new file mode 100644
index 0000000..d678a57
--- /dev/null
+++ b/src/functions/default/secure
@@ -0,0 +1,13 @@
+Function: _def_secure
+Class: default
+Section: default
+C-Name: sd_secure
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). If on, the \tet{system} and
+ \tet{extern} command are disabled. These two commands are potentially
+ dangerous when you execute foreign scripts since they let \kbd{gp} execute
+ arbitrary UNIX commands. \kbd{gp} will ask for confirmation before letting
+ you (or a script) unset this toggle.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/seriesprecision b/src/functions/default/seriesprecision
new file mode 100644
index 0000000..65617ce
--- /dev/null
+++ b/src/functions/default/seriesprecision
@@ -0,0 +1,11 @@
+Function: _def_seriesprecision
+Class: default
+Section: default
+C-Name: sd_seriesprecision
+Prototype:
+Help:
+Doc: number of significant terms
+ when converting a polynomial or rational function to a power series
+ (see~\b{ps}).
+
+ The default value is \kbd{16}.
diff --git a/src/functions/default/simplify b/src/functions/default/simplify
new file mode 100644
index 0000000..1c532a6
--- /dev/null
+++ b/src/functions/default/simplify
@@ -0,0 +1,19 @@
+Function: _def_simplify
+Class: default
+Section: default
+C-Name: sd_simplify
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). When the PARI library computes
+ something, the type of the
+ result is not always the simplest possible. The only type conversions which
+ the PARI library does automatically are rational numbers to integers (when
+ they are of type \typ{FRAC} and equal to integers), and similarly rational
+ functions to polynomials (when they are of type \typ{RFRAC} and equal to
+ polynomials). This feature is useful in many cases, and saves time, but can
+ be annoying at times. Hence you can disable this and, whenever you feel like
+ it, use the function \kbd{simplify} (see Chapter 3) which allows you to
+ simplify objects to the simplest possible types recursively (see~\b{y}).
+ \sidx{automatic simplification}
+
+ The default value is \kbd{1}.
diff --git a/src/functions/default/sopath b/src/functions/default/sopath
new file mode 100644
index 0000000..b3e6514
--- /dev/null
+++ b/src/functions/default/sopath
@@ -0,0 +1,18 @@
+Function: _def_sopath
+Class: default
+Section: default
+C-Name: sd_sopath
+Prototype:
+Help:
+Doc: this is a list of directories, separated by colons ':'
+ (semicolons ';' in the DOS world, since colons are preempted for drive names).
+ When asked to \tet{install} an external symbol from a shared library whose
+ name is not given by an absolute path (does not start with \kbd{/}, \kbd{./}
+ or \kbd{../}), \kbd{gp} will look for it in these directories, in the order
+ they were written in \kbd{sopath}. Here, as usual, \kbd{.} means the current
+ directory, and \kbd{..} its immediate parent. Environment expansion is
+ performed.
+
+ The default value is \kbd{""}, corresponding to an empty list of
+ directories: \tet{install} will use the library name as input (and look in
+ the current directory if the name is not an absolute path).
diff --git a/src/functions/default/strictargs b/src/functions/default/strictargs
new file mode 100644
index 0000000..0be8d2a
--- /dev/null
+++ b/src/functions/default/strictargs
@@ -0,0 +1,28 @@
+Function: _def_strictargs
+Class: default
+Section: default
+C-Name: sd_strictargs
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). If on, all arguments to \emph{new}
+ user functions are mandatory unless the function supplies an explicit default
+ value.
+ Otherwise arguments have the default value $0$.
+
+ In this example,
+ \bprog
+   fun(a,b=2)=a+b
+ @eprog
+ \kbd{a} is mandatory, while \kbd{b} is optional. If \kbd{strictargs} is on:
+ \bprog
+ ? fun()
+  ***   at top-level: fun()
+  ***                 ^-----
+  ***   in function fun: a,b=2
+  ***                    ^-----
+  ***   missing mandatory argument 'a' in user function.
+ @eprog
+ This applies to functions defined while \kbd{strictargs} is on. Changing \kbd{strictargs}
+ does not affect the behavior of previously defined functions.
+
+ The default value is \kbd{0}.
diff --git a/src/functions/default/strictmatch b/src/functions/default/strictmatch
new file mode 100644
index 0000000..7ec1d9b
--- /dev/null
+++ b/src/functions/default/strictmatch
@@ -0,0 +1,14 @@
+Function: _def_strictmatch
+Class: default
+Section: default
+C-Name: sd_strictmatch
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). If on, unused characters after a
+ sequence has been
+ processed will produce an error. Otherwise just a warning is printed. This
+ can be useful when you are unsure how many parentheses you have to close
+ after complicated nested loops. Please do not use this; find a decent
+ text-editor instead.
+
+ The default value is \kbd{1}.
diff --git a/src/functions/default/threadsize b/src/functions/default/threadsize
new file mode 100644
index 0000000..7f0b239
--- /dev/null
+++ b/src/functions/default/threadsize
@@ -0,0 +1,14 @@
+Function: _def_threadsize
+Class: default
+Section: default
+C-Name: sd_threadsize
+Prototype:
+Help:
+Doc: In parallel mode, each thread needs its own private \tev{stack} in which
+ to do its computations, see \kbd{parisize}. This value determines the size
+ in bytes of the stacks of each thread, so the total memory allocated will be
+ $\kbd{parisize}+\kbd{nbthreads}\times\kbd{threadsize}$.
+
+ If set to $0$, the value used is the same as \kbd{parisize}.
+
+ The default value is $0$.
diff --git a/src/functions/default/timer b/src/functions/default/timer
new file mode 100644
index 0000000..513f4ba
--- /dev/null
+++ b/src/functions/default/timer
@@ -0,0 +1,26 @@
+Function: _def_timer
+Class: gp_default
+Section: default
+C-Name: sd_timer
+Prototype:
+Help:
+Doc: this toggle is either 1 (on) or 0 (off). Every instruction sequence
+ in the gp calculator (anything ended by a newline in your input) is timed,
+ to some accuracy depending on the hardware and operating system. When
+ \tet{timer} is on, each such timing is printed immediately before the
+ output as follows:
+ \bprog
+ ? factor(2^2^7+1)
+ time = 108 ms.     \\ this line omitted if 'timer' is 0
+ %1 =
+ [     59649589127497217 1]
+
+ [5704689200685129054721 1]
+ @eprog\noindent (See also \kbd{\#} and \kbd{\#\#}.)
+
+ The time measured is the user \idx{CPU time}, \emph{not} including the time
+ for printing the results. If the time is negligible ($< 1$ ms.), nothing is
+ printed: in particular, no timing should be printed when defining a user
+ function or an alias, or installing a symbol from the library.
+
+ The default value is \kbd{0} (off).
diff --git a/src/functions/elliptic_curves/ellL1 b/src/functions/elliptic_curves/ellL1
new file mode 100644
index 0000000..18bb491
--- /dev/null
+++ b/src/functions/elliptic_curves/ellL1
@@ -0,0 +1,37 @@
+Function: ellL1
+Section: elliptic_curves
+C-Name: ellL1
+Prototype: GLp
+Help: ellL1(e, r): returns the value at s=1 of the derivative of order r of the L-function of the elliptic curve e assuming that r is at most the order of vanishing of the function at s=1.
+Doc: returns the value at $s=1$ of the derivative of order $r$ of the
+ $L$-function of the elliptic curve $e$ assuming that $r$ is at most the order
+ of vanishing of the $L$-function at $s=1$. (The result is wrong if $r$ is
+ strictly larger than the order of vanishing at 1.)
+ \bprog
+ ? e = ellinit("11a1"); \\ order of vanishing is 0
+ ? ellL1(e, 0)
+ %2 = 0.2538418608559106843377589233
+ ? e = ellinit("389a1");  \\ order of vanishing is 2
+ ? ellL1(e, 0)
+ %4 = -5.384067311837218089235032414 E-29
+ ? ellL1(e, 1)
+ %5 = 0
+ ? ellL1(e, 2)
+ %6 = 1.518633000576853540460385214
+ @eprog\noindent
+ The main use of this function, after computing at \emph{low} accuracy the
+ order of vanishing using \tet{ellanalyticrank}, is to compute the
+ leading term at \emph{high} accuracy to check (or use) the Birch and
+ Swinnerton-Dyer conjecture:
+ \bprog
+ ? \p18
+   realprecision = 18 significant digits
+ ? ellanalyticrank(ellinit([0, 0, 1, -7, 6]))
+ time = 32 ms.
+ %1 = [3, 10.3910994007158041]
+ ? \p200
+   realprecision = 202 significant digits (200 digits displayed)
+ ? ellL1(e, 3)
+ time = 23,113 ms.
+ %3 = 10.3910994007158041387518505103609170697263563756570092797 at com$[\dots]$
+ @eprog
diff --git a/src/functions/elliptic_curves/elladd b/src/functions/elliptic_curves/elladd
new file mode 100644
index 0000000..9ba307b
--- /dev/null
+++ b/src/functions/elliptic_curves/elladd
@@ -0,0 +1,8 @@
+Function: elladd
+Section: elliptic_curves
+C-Name: elladd
+Prototype: GGG
+Help: elladd(E,z1,z2): sum of the points z1 and z2 on elliptic curve E.
+Doc:
+ sum of the points $z1$ and $z2$ on the
+ elliptic curve corresponding to $E$.
diff --git a/src/functions/elliptic_curves/ellak b/src/functions/elliptic_curves/ellak
new file mode 100644
index 0000000..9e35370
--- /dev/null
+++ b/src/functions/elliptic_curves/ellak
@@ -0,0 +1,37 @@
+Function: ellak
+Section: elliptic_curves
+C-Name: akell
+Prototype: GG
+Help: ellak(E,n): computes the n-th Fourier coefficient of the L-function of
+ the elliptic curve E (assumed E is an integral model).
+Doc:
+ computes the coefficient $a_n$ of the $L$-function of the elliptic curve
+ $E/\Q$, i.e.~coefficients of a newform of weight 2 by the modularity theorem
+ (\idx{Taniyama-Shimura-Weil conjecture}). $E$ must be an \var{ell} structure
+ over $\Q$ as output by \kbd{ellinit}. $E$ must be given by an integral model,
+ not necessarily minimal, although a minimal model will make the function
+ faster.
+ \bprog
+ ? E = ellinit([0,1]);
+ ? ellak(E, 10)
+ %2 = 0
+ ? e = ellinit([5^4,5^6]); \\ not minimal at 5
+ ? ellak(e, 5) \\ wasteful but works
+ %3 = -3
+ ? E = ellminimalmodel(e); \\ now minimal
+ ? ellak(E, 5)
+ %5 = -3
+ @eprog\noindent If the model is not minimal at a number of bad primes, then
+ the function will be slower on those $n$ divisible by the bad primes.
+ The speed should be comparable for other $n$:
+ \bprog
+ ? for(i=1,10^6, ellak(E,5))
+ time = 820 ms.
+ ? for(i=1,10^6, ellak(e,5)) \\ 5 is bad, markedly slower
+ time = 1,249 ms.
+
+ ? for(i=1,10^5,ellak(E,5*i))
+ time = 977 ms.
+ ? for(i=1,10^5,ellak(e,5*i)) \\ still slower but not so much on average
+ time = 1,008 ms.
+ @eprog
diff --git a/src/functions/elliptic_curves/ellan b/src/functions/elliptic_curves/ellan
new file mode 100644
index 0000000..9b53263
--- /dev/null
+++ b/src/functions/elliptic_curves/ellan
@@ -0,0 +1,12 @@
+Function: ellan
+Section: elliptic_curves
+C-Name: anell
+Prototype: GL
+Help: ellan(E,n): computes the first n Fourier coefficients of the
+ L-function of the elliptic curve E (n<2^24 on a 32-bit machine).
+Doc: computes the vector of the first $n$ Fourier coefficients $a_k$
+ corresponding to the elliptic curve $E$. The curve must be given by an
+ integral model, not necessarily minimal, although a minimal model will make
+ the function faster.
+Variant: Also available is \fun{GEN}{anellsmall}{GEN e, long n}, which
+ returns a \typ{VECSMALL} instead of a \typ{VEC}, saving on memory.
diff --git a/src/functions/elliptic_curves/ellanalyticrank b/src/functions/elliptic_curves/ellanalyticrank
new file mode 100644
index 0000000..ae736ac
--- /dev/null
+++ b/src/functions/elliptic_curves/ellanalyticrank
@@ -0,0 +1,28 @@
+Function: ellanalyticrank
+Section: elliptic_curves
+C-Name: ellanalyticrank
+Prototype: GDGp
+Help: ellanalyticrank(e, {eps}): returns the order of vanishing at s=1
+ of the L-function of the elliptic curve e and the value of the first
+ non-zero derivative. To determine this order, it is assumed that any
+ value less than eps is zero. If no value of eps is given, a value of
+ half the current precision is used.
+Doc: returns the order of vanishing at $s=1$ of the $L$-function of the
+ elliptic curve $e$ and the value of the first non-zero derivative. To
+ determine this order, it is assumed that any value less than \kbd{eps} is
+ zero. If no value of \kbd{eps} is given, a value of half the current
+ precision is used.
+ \bprog
+ ? e = ellinit("11a1"); \\ rank 0
+ ? ellanalyticrank(e)
+ %2 = [0, 0.2538418608559106843377589233]
+ ? e = ellinit("37a1"); \\ rank 1
+ ? ellanalyticrank(e)
+ %4 = [1, 0.3059997738340523018204836835]
+ ? e = ellinit("389a1"); \\ rank 2
+ ? ellanalyticrank(e)
+ %6 = [2, 1.518633000576853540460385214]
+ ? e = ellinit("5077a1"); \\ rank 3
+ ? ellanalyticrank(e)
+ %8 = [3, 10.39109940071580413875185035]
+ @eprog
diff --git a/src/functions/elliptic_curves/ellap b/src/functions/elliptic_curves/ellap
new file mode 100644
index 0000000..81aaeed
--- /dev/null
+++ b/src/functions/elliptic_curves/ellap
@@ -0,0 +1,53 @@
+Function: ellap
+Section: elliptic_curves
+C-Name: ellap
+Prototype: GDG
+Help: ellap(E,{p}): computes the trace of Frobenius a_p for the elliptic
+ curve E, defined over Q or a finite field.
+Doc:
+ Let $E$ be an \var{ell} structure as output by \kbd{ellinit}, defined over
+ $\Q$ or a finite field $\F_q$. The argument $p$ is best left omitted if the
+ curve is defined over a finite field, and must be a prime number otherwise.
+ This function computes the trace of Frobenius $t$ for the elliptic curve $E$,
+ defined by the equation $\#E(\F_q) = q+1 - t$.
+
+ If the curve is defined over $\Q$, $p$ must be explicitly given and the
+ function computes the trace of the reduction over $\F_p$.
+ The trace of Frobenius is also the $a_p$ coefficient in the curve $L$-series
+ $L(E,s) = \sum_n a_n n^{-s}$, whence the function name. The equation must be
+ integral at $p$ but need not be minimal at $p$; of course, a minimal model
+ will be more efficient.
+ \bprog
+ ? E = ellinit([0,1]);  \\ y^2 = x^3 + 0.x + 1, defined over Q
+ ? ellap(E, 7) \\ 7 necessary here
+ %2 = -4       \\ #E(F_7) = 7+1-(-4) = 12
+ ? ellcard(E, 7)
+ %3 = 12       \\ OK
+
+ ? E = ellinit([0,1], 11);  \\ defined over F_11
+ ? ellap(E)       \\ no need to repeat 11
+ %4 = 0
+ ? ellap(E, 11)   \\ ... but it also works
+ %5 = 0
+ ? ellgroup(E, 13) \\ ouch, inconsistent input!
+    ***   at top-level: ellap(E,13)
+    ***                 ^-----------
+    *** ellap: inconsistent moduli in Rg_to_Fp:
+      11
+      13
+
+ ? Fq = ffgen(ffinit(11,3), 'a); \\ defines F_q := F_{11^3}
+ ? E = ellinit([a+1,a], Fq);  \\ y^2 = x^3 + (a+1)x + a, defined over F_q
+ ? ellap(E)
+ %8 = -3
+ @eprog
+
+ \misctitle{Algorithms used} If $E/\F_q$ has CM by a principal imaginary
+ quadratic order we use a fast explicit formula (involving essentially Kronecker
+ symbols and Cornacchia's algorithm), in $O(\log q)^2$.
+ Otherwise, we use Shanks-Mestre's baby-step/giant-step method, which runs in
+ time $q(p^{1/4})$ using $O(q^{1/4})$ storage, hence becomes unreasonable when
+ $q$ has about 30~digits. If the \tet{seadata} package is installed, the
+ \tet{SEA} algorithm becomes available, heuristically in $\tilde{O}(\log
+ q)^4$, and primes of the order of 200~digits become feasible. In very small
+ characteristic (2,3,5,7 or $13$), we use Harley's algorithm.
diff --git a/src/functions/elliptic_curves/ellbil b/src/functions/elliptic_curves/ellbil
new file mode 100644
index 0000000..3821feb
--- /dev/null
+++ b/src/functions/elliptic_curves/ellbil
@@ -0,0 +1,13 @@
+Function: ellbil
+Section: elliptic_curves
+C-Name: bilhell
+Prototype: GGGp
+Help: ellbil(E,z1,z2): canonical bilinear form for the points z1,z2 on the
+ elliptic curve E. Either z1 or z2 can also be a vector/matrix of points.
+Doc:
+ if $z1$ and $z2$ are points on the elliptic
+ curve $E$ this function
+ computes the value of the canonical bilinear form on $z1$, $z2$:
+ $$ ( h(E,z1\kbd{+}z2) - h(E,z1) - h(E,z2) ) / 2 $$
+ where \kbd{+} denotes of course addition on $E$. In addition, $z1$ or $z2$
+ (but not both) can be vectors or matrices.
diff --git a/src/functions/elliptic_curves/ellcard b/src/functions/elliptic_curves/ellcard
new file mode 100644
index 0000000..d830502
--- /dev/null
+++ b/src/functions/elliptic_curves/ellcard
@@ -0,0 +1,19 @@
+Function: ellcard
+Section: elliptic_curves
+C-Name: ellcard
+Prototype: GDG
+Help: ellcard(E,{p}): computes the order of the group E(Fp)
+ for the elliptic curve E, defined over Q or a finite field.
+Doc: Let $E$ be an \var{ell} structure as output by \kbd{ellinit}, defined over
+ $\Q$ or a finite field $\F_q$. The argument $p$ is best left omitted if the
+ curve is defined over a finite field, and must be a prime number otherwise.
+ This function computes the order of the group $E(\F_q)$ (as would be
+ computed by \tet{ellgroup}).
+
+ If the curve is defined over $\Q$, $p$ must be explicitly given and the
+ function computes the cardinal of the reduction over $\F_p$; the
+ equation need not be minimal at $p$, but a minimal model will be more
+ efficient. The reduction is allowed to be singular, and we return the order
+ of the group of non-singular points in this case.
+Variant: Also available is \fun{GEN}{ellcard}{GEN E, GEN p} where $p$ is not
+ \kbd{NULL}.
diff --git a/src/functions/elliptic_curves/ellchangecurve b/src/functions/elliptic_curves/ellchangecurve
new file mode 100644
index 0000000..bce5d79
--- /dev/null
+++ b/src/functions/elliptic_curves/ellchangecurve
@@ -0,0 +1,15 @@
+Function: ellchangecurve
+Section: elliptic_curves
+C-Name: ellchangecurve
+Prototype: GG
+Help: ellchangecurve(E,v): change data on elliptic curve according to
+ v=[u,r,s,t].
+Description:
+ (gen, gen):ell        ellchangecurve($1, $2)
+Doc:
+ changes the data for the elliptic curve $E$
+ by changing the coordinates using the vector \kbd{v=[u,r,s,t]}, i.e.~if $x'$
+ and $y'$ are the new coordinates, then $x=u^2x'+r$, $y=u^3y'+su^2x'+t$.
+ $E$ must be an \var{ell} structure as output by \kbd{ellinit}. The special
+ case $v = 1$ is also used instead of $[1,0,0,0]$ to denote the
+ trivial coordinate change.
diff --git a/src/functions/elliptic_curves/ellchangepoint b/src/functions/elliptic_curves/ellchangepoint
new file mode 100644
index 0000000..cb17490
--- /dev/null
+++ b/src/functions/elliptic_curves/ellchangepoint
@@ -0,0 +1,23 @@
+Function: ellchangepoint
+Section: elliptic_curves
+C-Name: ellchangepoint
+Prototype: GG
+Help: ellchangepoint(x,v): change data on point or vector of points x on an
+ elliptic curve according to v=[u,r,s,t].
+Doc:
+ changes the coordinates of the point or
+ vector of points $x$ using the vector \kbd{v=[u,r,s,t]}, i.e.~if $x'$ and
+ $y'$ are the new coordinates, then $x=u^2x'+r$, $y=u^3y'+su^2x'+t$ (see also
+ \kbd{ellchangecurve}).
+ \bprog
+ ? E0 = ellinit([1,1]); P0 = [0,1]; v = [1,2,3,4];
+ ? E = ellchangecurve(E0, v);
+ ? P = ellchangepoint(P0,v)
+ %3 = [-2, 3]
+ ? ellisoncurve(E, P)
+ %4 = 1
+ ? ellchangepointinv(P,v)
+ %5 = [0, 1]
+ @eprog
+Variant: The reciprocal function \fun{GEN}{ellchangepointinv}{GEN x, GEN ch}
+ inverts the coordinate change.
diff --git a/src/functions/elliptic_curves/ellchangepointinv b/src/functions/elliptic_curves/ellchangepointinv
new file mode 100644
index 0000000..dff67b0
--- /dev/null
+++ b/src/functions/elliptic_curves/ellchangepointinv
@@ -0,0 +1,21 @@
+Function: ellchangepointinv
+Section: elliptic_curves
+C-Name: ellchangepointinv
+Prototype: GG
+Help: ellchangepointinv(x,v): change data on point or vector of points x on an
+ elliptic curve according to v=[u,r,s,t], inverse of ellchangepoint.
+Doc:
+ changes the coordinates of the point or vector of points $x$ using
+ the inverse of the isomorphism associated to \kbd{v=[u,r,s,t]},
+ i.e.~if $x'$ and $y'$ are the old coordinates, then $x=u^2x'+r$,
+ $y=u^3y'+su^2x'+t$ (inverse of \kbd{ellchangepoint}).
+ \bprog
+ ? E0 = ellinit([1,1]); P0 = [0,1]; v = [1,2,3,4];
+ ? E = ellchangecurve(E0, v);
+ ? P = ellchangepoint(P0,v)
+ %3 = [-2, 3]
+ ? ellisoncurve(E, P)
+ %4 = 1
+ ? ellchangepointinv(P,v)
+ %5 = [0, 1]  \\ we get back P0
+ @eprog
diff --git a/src/functions/elliptic_curves/ellconvertname b/src/functions/elliptic_curves/ellconvertname
new file mode 100644
index 0000000..2790c76
--- /dev/null
+++ b/src/functions/elliptic_curves/ellconvertname
@@ -0,0 +1,18 @@
+Function: ellconvertname
+Section: elliptic_curves
+C-Name: ellconvertname
+Prototype: G
+Help: ellconvertname(name): convert an elliptic curve name (as found in
+ the elldata database) from a string to a triplet [conductor, isogeny class,
+ index]. It will also convert a triplet back to a curve name.
+Doc:
+ converts an elliptic curve name, as found in the \tet{elldata} database,
+ from a string to a triplet $[\var{conductor}, \var{isogeny class},
+ \var{index}]$. It will also convert a triplet back to a curve name.
+ Examples:
+ \bprog
+ ? ellconvertname("123b1")
+ %1 = [123, 1, 1]
+ ? ellconvertname(%)
+ %2 = "123b1"
+ @eprog
diff --git a/src/functions/elliptic_curves/elldivpol b/src/functions/elliptic_curves/elldivpol
new file mode 100644
index 0000000..58120f8
--- /dev/null
+++ b/src/functions/elliptic_curves/elldivpol
@@ -0,0 +1,17 @@
+Function: elldivpol
+Section: elliptic_curves
+C-Name: elldivpol
+Prototype: GLDn
+Help: elldivpol(E,n,{v='x}): n-division polynomial f_n for the curve E in the
+ variable v.
+Doc: $n$-division polynomial $f_n$ for the curve $E$ in the
+ variable $v$. In standard notation, for any affine point $P = (X,Y)$ on the
+ curve, we have
+ $$[n]P = (\phi_n(P)\psi_n(P) : \omega_n(P) : \psi_n(P)^3)$$
+ for some polynomials $\phi_n,\omega_n,\psi_n$ in
+ $\Z[a_1,a_2,a_3,a_4,a_6][X,Y]$. We have $f_n(X) = \psi_n(X)$ for $n$ odd, and
+ $f_n(X) = \psi_n(X,Y) (2Y + a_1X+a_3)$ for $n$ even. We have
+ $$ f_1  = 1,\quad f_2 = 4X^3 + b_2X^2 + 2b_4 X + b_6, \quad f_3 = 3 X^4 + b_2 X^3 + 3b_4 X^2 + 3 b_6 X + b8, $$
+ $$ f_4 = f_2(2X^6 + b_2 X^5 + 5b_4 X^4 + 10 b_6 X^3 + 10 b_8 X^2 +
+ (b_2b_8-b_4b_6)X + (b_8b_4 - b_6^2)), \dots $$
+ For $n \geq 2$, the roots of $f_n$ are the $X$-coordinates of points in $E[n]$.
diff --git a/src/functions/elliptic_curves/elleisnum b/src/functions/elliptic_curves/elleisnum
new file mode 100644
index 0000000..c8c3282
--- /dev/null
+++ b/src/functions/elliptic_curves/elleisnum
@@ -0,0 +1,33 @@
+Function: elleisnum
+Section: elliptic_curves
+C-Name: elleisnum
+Prototype: GLD0,L,p
+Help: elleisnum(w,k,{flag=0}): k being an even positive integer, computes the
+ numerical value of the Eisenstein series of weight k at the lattice
+ w, as given by ellperiods. When flag is non-zero and k=4 or 6, this gives the
+ elliptic invariants g2 or g3 with the correct normalization.
+Doc: $k$ being an even positive integer, computes the numerical value of the
+ Eisenstein series of weight $k$ at the lattice $w$, as given by
+ \tet{ellperiods}, namely
+ $$
+ (2i \pi/\omega_2)^k
+ \Big(1 + 2/\zeta(1-k) \sum_{n\geq 0} n^{k-1}q^n / (1-q^n)\Big),
+ $$
+ where $q = \exp(2i\pi \tau)$ and $\tau:=\omega_1/\omega_2$ belongs to the
+ complex upper half-plane. It is also possible to directly input $w =
+ [\omega_1,\omega_2]$, or an elliptic curve $E$ as given by \kbd{ellinit}.
+ \bprog
+ ? w = ellperiods([1,I]);
+ ? elleisnum(w, 4)
+ %2 = 2268.8726415508062275167367584190557607
+ ? elleisnum(w, 6)
+ %3 = -3.977978632282564763 E-33
+ ? E = ellinit([1, 0]);
+ ? elleisnum(E, 4, 1)
+ %5 = -47.999999999999999999999999999999999998
+ @eprog
+
+ When \fl\ is non-zero and $k=4$ or 6, returns the elliptic invariants $g_2$
+ or $g_3$, such that
+ $$y^2 = 4x^3 - g_2 x - g_3$$
+ is a Weierstrass equation for $E$.
diff --git a/src/functions/elliptic_curves/elleta b/src/functions/elliptic_curves/elleta
new file mode 100644
index 0000000..d9b0eae
--- /dev/null
+++ b/src/functions/elliptic_curves/elleta
@@ -0,0 +1,15 @@
+Function: elleta
+Section: elliptic_curves
+C-Name: elleta
+Prototype: Gp
+Help: elleta(w): w=[w1,w2], returns the vector [eta1,eta2] of quasi-periods
+ associated to [w1,w2].
+Doc: returns the quasi-periods $[\eta_1,\eta_2]$
+ associated to the lattice basis $\var{w} = [\omega_1, \omega_2]$.
+ Alternatively, \var{w} can be an elliptic curve $E$ as output by
+ \kbd{ellinit}, in which case, the quasi periods associated to the period
+ lattice basis \kbd{$E$.omega} (namely, \kbd{$E$.eta}) are returned.
+ \bprog
+ ? elleta([1, I])
+ %1 = [3.141592653589793238462643383, 9.424777960769379715387930149*I]
+ @eprog
diff --git a/src/functions/elliptic_curves/ellfromj b/src/functions/elliptic_curves/ellfromj
new file mode 100644
index 0000000..72eaa53
--- /dev/null
+++ b/src/functions/elliptic_curves/ellfromj
@@ -0,0 +1,8 @@
+Function: ellfromj
+Section: elliptic_curves
+C-Name: ellfromj
+Prototype: G
+Help: ellfromj(j): returns the coefficients [a1,a2,a3,a4,a6] of a fixed
+ elliptic curve with j-invariant j.
+Doc: returns the coefficients $[a_1,a_2,a_3,a_4,a_6]$ of a fixed elliptic curve
+ with $j$-invariant $j$.
diff --git a/src/functions/elliptic_curves/ellgenerators b/src/functions/elliptic_curves/ellgenerators
new file mode 100644
index 0000000..33b1518
--- /dev/null
+++ b/src/functions/elliptic_curves/ellgenerators
@@ -0,0 +1,16 @@
+Function: ellgenerators
+Section: elliptic_curves
+C-Name: ellgenerators
+Prototype: G
+Help: ellgenerators(E): If E is an elliptic curve over the rationals,
+ return the generators of the Mordell-Weil group associated to the curve.
+ This relies on the curve being referenced in the elldata database.
+ If E is an elliptic curve over a finite field Fq as output by ellinit(),
+ return a minimal set of generators for the group E(Fq).
+Doc:
+ If $E$ is an elliptic curve over the rationals, return a $\Z$-basis of the
+ free part of the \idx{Mordell-Weil group} associated to $E$.  This relies on
+ the \tet{elldata} database being installed and referencing the curve, and so
+ is only available for curves over $\Z$ of small conductors.
+ If $E$ is an elliptic curve over a finite field $\F_q$ as output by
+ \tet{ellinit}, return a minimal set of generators for the group $E(\F_q)$.
diff --git a/src/functions/elliptic_curves/ellglobalred b/src/functions/elliptic_curves/ellglobalred
new file mode 100644
index 0000000..90e86bd
--- /dev/null
+++ b/src/functions/elliptic_curves/ellglobalred
@@ -0,0 +1,29 @@
+Function: ellglobalred
+Section: elliptic_curves
+C-Name: ellglobalred
+Prototype: G
+Help: ellglobalred(E): E being an elliptic curve, returns [N,[u,r,s,t],c,
+ faN,L], where N is the conductor of E, [u,r,s,t] leads to the standard model
+ for E, c is the product of the local Tamagawa numbers c_p, faN is factor(N)
+ and L[i] is elllocalred(E, faN[i,1]).
+Description:
+ (gen):gen        ellglobalred($1)
+Doc:
+ calculates the arithmetic conductor, the global
+ minimal model of $E$ and the global \idx{Tamagawa number} $c$.
+ $E$ must be an \var{ell} structure as output by \kbd{ellinit}, defined over
+ $\Q$. The result is a vector $[N,v,c,F,L]$, where
+
+ \item $N$ is the arithmetic conductor of the curve,
+
+ \item $v$ gives the coordinate change for $E$ over $\Q$ to the minimal
+ integral model (see \tet{ellminimalmodel}),
+
+ \item $c$ is the product of the local Tamagawa numbers $c_p$, a quantity
+ which enters in the \idx{Birch and Swinnerton-Dyer conjecture},\sidx{minimal model}
+
+ \item $F$ is the factorization of $N$ over $\Z$.
+
+ \item $L$ is a vector, whose $i$-th entry contains the local data
+ at the $i$-th prime divisor of $N$, i.e. \kbd{L[i] = elllocalred(E,F[i,1])},
+ where the local coordinate change has been deleted, and replaced by a $0$.
diff --git a/src/functions/elliptic_curves/ellgroup b/src/functions/elliptic_curves/ellgroup
new file mode 100644
index 0000000..337109c
--- /dev/null
+++ b/src/functions/elliptic_curves/ellgroup
@@ -0,0 +1,67 @@
+Function: ellgroup
+Section: elliptic_curves
+C-Name: ellgroup0
+Prototype: GDGD0,L,
+Help: ellgroup(E,{p},{flag}): computes the structure of the group E(Fp)
+ If flag is 1, return also generators.
+Doc: Let $E$ be an \var{ell} structure as output by \kbd{ellinit}, defined over
+ $\Q$ or a finite field $\F_q$. The argument $p$ is best left omitted if the
+ curve is defined over a finite field, and must be a prime number otherwise.
+ This function computes the structure of the group $E(\F_q) \sim \Z/d_1\Z
+ \times \Z/d_2\Z$, with $d_2\mid d_1$.
+
+ If the curve is defined over $\Q$, $p$ must be explicitly given and the
+ function computes the structure of the reduction over $\F_p$; the
+ equation need not be minimal at $p$, but a minimal model will be more
+ efficient. The reduction is allowed to be singular, and we return the
+ structure of the (cyclic) group of non-singular points in this case.
+
+ If the flag is $0$ (default), return $[d_1]$ or $[d_1, d_2]$, if $d_2>1$.
+ If the flag is $1$, return a triple $[h,\var{cyc},\var{gen}]$, where
+ $h$ is the curve cardinality, \var{cyc} gives the group structure as a
+ product of cyclic groups (as per $\fl = 0$). More precisely, if $d_2 > 1$,
+ the output is $[d_1d_2, [d_1,d_2],[P,Q]]$ where $P$ is
+ of order $d_1$ and $[P,Q]$ generates the curve.
+ \misctitle{Caution} It is not guaranteed that $Q$ has order $d_2$, which in
+ the worst case requires an expensive discrete log computation. Only that
+ \kbd{ellweilpairing(E, P, Q, d1)} has order $d_2$.
+ \bprog
+ ? E = ellinit([0,1]);  \\ y^2 = x^3 + 0.x + 1, defined over Q
+ ? ellgroup(E, 7)
+ %2 = [6, 2] \\ Z/6 x Z/2, non-cyclic
+ ? E = ellinit([0,1] * Mod(1,11));  \\ defined over F_11
+ ? ellgroup(E)   \\ no need to repeat 11
+ %4 = [12]
+ ? ellgroup(E, 11)   \\ ... but it also works
+ %5 = [12]
+ ? ellgroup(E, 13) \\ ouch, inconsistent input!
+    ***   at top-level: ellgroup(E,13)
+    ***                 ^--------------
+    *** ellgroup: inconsistent moduli in Rg_to_Fp:
+      11
+      13
+ ? ellgroup(E, 7, 1)
+ %6 = [12, [6, 2], [[Mod(2, 7), Mod(4, 7)], [Mod(4, 7), Mod(4, 7)]]]
+ @eprog\noindent
+ If $E$ is defined over $\Q$, we allow singular reduction and in this case we
+ return the structure of the group of non-singular points, satisfying
+ $\#E_{ns}(\F_p) = p - a_p$.
+ \bprog
+ ? E = ellinit([0,5]);
+ ? ellgroup(E, 5, 1)
+ %2 = [5, [5], [[Mod(4, 5), Mod(2, 5)]]]
+ ? ellap(E, 5)
+ %3 = 0 \\ additive reduction at 5
+ ? E = ellinit([0,-1,0,35,0]);
+ ? ellgroup(E, 5, 1)
+ %5 = [4, [4], [[Mod(2, 5), Mod(2, 5)]]]
+ ? ellap(E, 5)
+ %6 = 1 \\ split multiplicative reduction at 5
+ ? ellgroup(E, 7, 1)
+ %7 = [8, [8], [[Mod(3, 7), Mod(5, 7)]]]
+ ? ellap(E, 7)
+ %8 = -1 \\ non-split multiplicative reduction at 7
+ @eprog
+
+Variant: Also available is \fun{GEN}{ellgroup}{GEN E, GEN p}, corresponding
+ to \fl = 0.
diff --git a/src/functions/elliptic_curves/ellheegner b/src/functions/elliptic_curves/ellheegner
new file mode 100644
index 0000000..7bbff3e
--- /dev/null
+++ b/src/functions/elliptic_curves/ellheegner
@@ -0,0 +1,24 @@
+Function: ellheegner
+Section: elliptic_curves
+C-Name: ellheegner
+Prototype: G
+Help: ellheegner(E): return a rational non-torsion point on the elliptic curve E
+ assumed to be of rank 1
+Doc: Let $E$ be an elliptic curve over the rationals, assumed to be of
+ (analytic) rank $1$. This returns a non-torsion rational point on the curve,
+ whose canonical height is equal to the product of the elliptic regulator by the
+ analytic Sha.
+
+ This uses the Heegner point method, described in Cohen GTM 239; the complexity
+ is proportional to the product of the square root of the conductor and the
+ height of the point (thus, it is preferable to apply it to strong Weil curves).
+ \bprog
+ ? E = ellinit([-157^2,0]);
+ ? u = ellheegner(E); print(u[1], "\n", u[2])
+ 69648970982596494254458225/166136231668185267540804
+ 538962435089604615078004307258785218335/67716816556077455999228495435742408
+ ? ellheegner(ellinit([0,1]))         \\ E has rank 0 !
+  ***   at top-level: ellheegner(E=ellinit
+  ***                 ^--------------------
+  *** ellheegner: The curve has even analytic rank.
+ @eprog
diff --git a/src/functions/elliptic_curves/ellheight b/src/functions/elliptic_curves/ellheight
new file mode 100644
index 0000000..f046bf3
--- /dev/null
+++ b/src/functions/elliptic_curves/ellheight
@@ -0,0 +1,21 @@
+Function: ellheight
+Section: elliptic_curves
+C-Name: ellheight0
+Prototype: GGD2,L,p
+Help: ellheight(E,x,{flag=2}): canonical height of point x on elliptic curve
+ E. flag is optional and selects the algorithm
+ used to compute the Archimedean local height. Its meaning is 0: use
+ theta-functions, 1: use Tate's method, 2: use Mestre's AGM.
+Doc: global N\'eron-Tate height of the point $z$ on the elliptic curve
+ $E$ (defined over $\Q$), using the normalization in Cremona's
+ \emph{Algorithms for modular elliptic curves}. $E$
+ must be an \kbd{ell} as output by \kbd{ellinit}; it needs not be given by a
+ minimal model although the computation will be faster if it is. \fl\ selects
+ the algorithm used to compute the Archimedean local height. If $\fl=0$,
+ we use sigma and theta-functions and Silverman's trick (Computing
+ heights on elliptic curves, \emph{Math.~Comp.} {\bf 51}; note that
+ our height is twice Silverman's height). If
+ $\fl=1$, use Tate's $4^n$ algorithm. If $\fl=2$, use Mestre's AGM algorithm.
+ The latter converges quadratically and is much faster than the other two.
+Variant: Also available is \fun{GEN}{ghell}{GEN E, GEN x, long prec}
+ ($\fl=2$).
diff --git a/src/functions/elliptic_curves/ellheightmatrix b/src/functions/elliptic_curves/ellheightmatrix
new file mode 100644
index 0000000..fd3f3e1
--- /dev/null
+++ b/src/functions/elliptic_curves/ellheightmatrix
@@ -0,0 +1,15 @@
+Function: ellheightmatrix
+Section: elliptic_curves
+C-Name: mathell
+Prototype: GGp
+Help: ellheightmatrix(E,x): gives the height matrix for vector of points x
+ on elliptic curve E.
+Doc: $x$ being a vector of points, this
+ function outputs the Gram matrix of $x$ with respect to the N\'eron-Tate
+ height, in other words, the $(i,j)$ component of the matrix is equal to
+ \kbd{ellbil($E$,x[$i$],x[$j$])}. The rank of this matrix, at least in some
+ approximate sense, gives the rank of the set of points, and if $x$ is a
+ basis of the \idx{Mordell-Weil group} of $E$, its determinant is equal to
+ the regulator of $E$. Note our height normalization follows Cremona's
+ \emph{Algorithms for modular elliptic curves}: this matrix should be divided
+ by 2 to be in accordance with, e.g., Silverman's normalizations.
diff --git a/src/functions/elliptic_curves/ellidentify b/src/functions/elliptic_curves/ellidentify
new file mode 100644
index 0000000..9f90d3c
--- /dev/null
+++ b/src/functions/elliptic_curves/ellidentify
@@ -0,0 +1,14 @@
+Function: ellidentify
+Section: elliptic_curves
+C-Name: ellidentify
+Prototype: G
+Help: ellidentify(E): look up the elliptic curve E in the elldata database and
+ return [[N, M, ...], C] where N is the name of the curve in Cremona's
+ database, M the minimal model and C the coordinates change (see
+ ellchangecurve).
+Doc: look up the elliptic curve $E$, defined by an arbitrary model over $\Q$,
+ in the \tet{elldata} database.
+ Return \kbd{[[N, M, G], C]}  where $N$ is the curve name in Cremona's
+ elliptic curve database, $M$ is the minimal model, $G$ is a $\Z$-basis of
+ the free part of the \idx{Mordell-Weil group} $E(\Q)$ and $C$ is the
+ change of coordinates change, suitable for \kbd{ellchangecurve}.
diff --git a/src/functions/elliptic_curves/ellinit b/src/functions/elliptic_curves/ellinit
new file mode 100644
index 0000000..5984394
--- /dev/null
+++ b/src/functions/elliptic_curves/ellinit
@@ -0,0 +1,106 @@
+Function: ellinit
+Section: elliptic_curves
+C-Name: ellinit
+Prototype: GDGp
+Help: ellinit(x,{D=1}): let x be a vector [a1,a2,a3,a4,a6], or [a4,a6] if
+ a1=a2=a3=0, defining the curve Y^2 + a1.XY + a3.Y = X^3 + a2.X^2 + a4.X +
+ a6; x can also be a string, in which case the curve with matching name is
+ retrieved from the elldata database, if available. This function initializes
+ an elliptic curve over the domain D (inferred from coefficients if omitted).
+Description:
+ (gen, gen, small):ell:prec  ellinit($1, $2, prec)
+Doc:
+ initialize an \tet{ell} structure, associated to the elliptic curve $E$.
+ $E$ is either
+
+ \item a $5$-component vector $[a_1,a_2,a_3,a_4,a_6]$ defining the elliptic
+ curve with Weierstrass equation
+ $$ Y^2 + a_1 XY + a_3 Y = X^3 + a_2 X^2 + a_4 X + a_6, $$
+
+ \item a $2$-component vector $[a_4,a_6]$ defining the elliptic
+ curve with short Weierstrass equation
+ $$ Y^2 = X^3 + a_4 X + a_6, $$
+
+ \item a character string in Cremona's notation, e.g. \kbd{"11a1"}, in which
+ case the curve is retrieved from the \tet{elldata} database if available.
+
+ The optional argument $D$ describes the domain over which the curve is
+ defined:
+
+ \item the \typ{INT} $1$ (default): the field of rational numbers $\Q$.
+
+ \item a \typ{INT} $p$, where $p$ is a prime number: the prime finite field
+ $\F_p$.
+
+ \item an \typ{INTMOD} \kbd{Mod(a, p)}, where $p$ is a prime number: the
+ prime finite field $\F_p$.
+
+ \item a \typ{FFELT}, as returned by \tet{ffgen}: the corresponding finite
+ field $\F_q$.
+
+ \item a \typ{PADIC}, $O(p^n)$: the field $\Q_p$, where $p$-adic quantities
+ will be computed to a relative accuracy of $n$ digits. We advise to input a
+ model defined over $\Q$ for such curves. In any case, if you input an
+ approximate model with \typ{PADIC} coefficients, it will be replaced by a lift
+ to $\Q$ (an exact model ``close'' to the one that was input) and all quantities
+ will then be computed in terms of this lifted model, at the given accuracy.
+
+ \item a \typ{REAL} $x$: the field $\C$ of complex numbers, where floating
+ point quantities are by default computed to a relative accuracy of
+ \kbd{precision}$(x)$. If no such argument is given, the value of
+ \kbd{realprecision} at the time \kbd{ellinit} is called will be used.
+
+ This argument $D$ is indicative: the curve coefficients are checked for
+ compatibility, possibly changing $D$; for instance if $D = 1$ and
+ an \typ{INTMOD} is found. If inconsistencies are detected, an error is
+ raised:
+ \bprog
+ ? ellinit([1 + O(5), 1], O(7));
+  ***   at top-level: ellinit([1+O(5),1],O
+  ***                 ^--------------------
+  *** ellinit: inconsistent moduli in ellinit: 7 != 5
+ @eprog\noindent If the curve coefficients are too general to fit any of the
+ above domain categories, only basic operations, such as point addition, will
+ be supported later.
+
+ If the curve (seen over the domain $D$) is singular, fail and return an
+ empty vector $[]$.
+ \bprog
+ ? E = ellinit([0,0,0,0,1]); \\ y^2 = x^3 + 1, over Q
+ ? E = ellinit([0,1]);       \\ the same curve, short form
+ ? E = ellinit("36a1");      \\ sill the same curve, Cremona's notations
+ ? E = ellinit([0,1], 2)     \\ over F2: singular curve
+ %4 = []
+ ? E = ellinit(['a4,'a6] * Mod(1,5));  \\ over F_5[a4,a6], basic support !
+ @eprog\noindent
+
+ The result of \tet{ellinit} is an \tev{ell} structure. It contains at least
+ the following information in its components:
+ %
+ $$ a_1,a_2,a_3,a_4,a_6,b_2,b_4,b_6,b_8,c_4,c_6,\Delta,j.$$
+ %
+ All are accessible via member functions. In particular, the discriminant is
+ \kbd{$E$.disc}, and the $j$-invariant is \kbd{$E$.j}.
+ \bprog
+ ? E = ellinit([a4, a6]);
+ ? E.disc
+ %2 = -64*a4^3 - 432*a6^2
+ ? E.j
+ %3 = -6912*a4^3/(-4*a4^3 - 27*a6^2)
+ @eprog
+ Further components contain domain-specific data, which are in general dynamic:
+ only computed when needed, and then cached in the structure.
+ \bprog
+ ? E = ellinit([2,3], 10^60+7);  \\ E over F_p, p large
+ ? ellap(E)
+ time = 4,440 ms.
+ %2 = -1376268269510579884904540406082
+ ? ellcard(E);  \\ now instantaneous !
+ time = 0 ms.
+ ? ellgenerators(E);
+ time = 5,965 ms.
+ ? ellgenerators(E); \\ second time instantaneous
+ time = 0 ms.
+ @eprog
+ See the description of member functions related to elliptic curves at the
+ beginning of this section.
diff --git a/src/functions/elliptic_curves/ellisoncurve b/src/functions/elliptic_curves/ellisoncurve
new file mode 100644
index 0000000..4bb7bdd
--- /dev/null
+++ b/src/functions/elliptic_curves/ellisoncurve
@@ -0,0 +1,12 @@
+Function: ellisoncurve
+Section: elliptic_curves
+C-Name: ellisoncurve
+Prototype: GG
+Help: ellisoncurve(E,z): true(1) if z is on elliptic curve E, false(0) if not.
+Doc: gives 1 (i.e.~true) if the point $z$ is on the elliptic curve $E$, 0
+ otherwise. If $E$ or $z$ have imprecise coefficients, an attempt is made to
+ take this into account, i.e.~an imprecise equality is checked, not a precise
+ one. It is allowed for $z$ to be a vector of points in which case a vector
+ (of the same type) is returned.
+Variant: Also available is \fun{int}{oncurve}{GEN E, GEN z} which does not
+ accept vectors of points.
diff --git a/src/functions/elliptic_curves/ellj b/src/functions/elliptic_curves/ellj
new file mode 100644
index 0000000..21a0892
--- /dev/null
+++ b/src/functions/elliptic_curves/ellj
@@ -0,0 +1,9 @@
+Function: ellj
+Section: elliptic_curves
+C-Name: jell
+Prototype: Gp
+Help: ellj(x): elliptic j invariant of x.
+Doc:
+ elliptic $j$-invariant. $x$ must be a complex number
+ with positive imaginary part, or convertible into a power series or a
+ $p$-adic number with positive valuation.
diff --git a/src/functions/elliptic_curves/elllocalred b/src/functions/elliptic_curves/elllocalred
new file mode 100644
index 0000000..66155de
--- /dev/null
+++ b/src/functions/elliptic_curves/elllocalred
@@ -0,0 +1,23 @@
+Function: elllocalred
+Section: elliptic_curves
+C-Name: elllocalred
+Prototype: GG
+Help: elllocalred(E,p): E being an elliptic curve, returns
+ [f,kod,[u,r,s,t],c], where f is the conductor's exponent, kod is the Kodaira
+ type for E at p, [u,r,s,t] is the change of variable needed to make E
+ minimal at p, and c is the local Tamagawa number c_p.
+Doc:
+ calculates the \idx{Kodaira} type of the local fiber of the elliptic curve
+ $E$ at the prime $p$. $E$ must be an \var{ell} structure as output by
+ \kbd{ellinit}, and is assumed to have all its coefficients $a_i$ in $\Z$.
+ The result is a 4-component vector $[f,kod,v,c]$. Here $f$ is the exponent of
+ $p$ in the arithmetic conductor of $E$, and $kod$ is the Kodaira type which
+ is coded as follows:
+
+ 1 means good reduction (type I$_0$), 2, 3 and 4 mean types II, III and IV
+ respectively, $4+\nu$ with $\nu>0$ means type I$_\nu$;
+ finally the opposite values $-1$, $-2$, etc.~refer to the starred types
+ I$_0^*$, II$^*$, etc. The third component $v$ is itself a vector $[u,r,s,t]$
+ giving the coordinate changes done during the local reduction;
+ $u = 1$ if and only if the given equation was already minimal at $p$.
+ Finally, the last component $c$ is the local \idx{Tamagawa number} $c_p$.
diff --git a/src/functions/elliptic_curves/elllog b/src/functions/elliptic_curves/elllog
new file mode 100644
index 0000000..f36d478
--- /dev/null
+++ b/src/functions/elliptic_curves/elllog
@@ -0,0 +1,30 @@
+Function: elllog
+Section: elliptic_curves
+C-Name: elllog
+Prototype: GGGDG
+Help: elllog(E,P,G,{o}): return the discrete logarithm of the point P of
+ the elliptic curve E in base G. If present, o represents the order of G.
+ If not present, assume that G generates the curve.
+Doc: given two points $P$ and $G$ on the elliptic curve $E/\F_q$, returns the
+ discrete logarithm of $P$ in base $G$, i.e. the smallest non-negative
+ integer $n$ such that $P = [n]G$.
+ See \tet{znlog} for the limitations of the underlying discrete log algorithms.
+ If present, $o$ represents the order of $G$, see \secref{se:DLfun};
+ the preferred format for this parameter is \kbd{[N, factor(N)]}, where $N$
+ is  the order of $G$.
+
+ If no $o$ is given, assume that $G$ generates the curve.
+ The function also assumes that $P$ is a multiple of $G$.
+ \bprog
+ ? a = ffgen(ffinit(2,8),'a);
+ ? E = ellinit([a,1,0,0,1]);  \\ over F_{2^8}
+ ? x = a^3; y = ellordinate(E,x)[1];
+ ? P = [x,y]; G = ellmul(E, P, 113);
+ ? ord = [242, factor(242)]; \\ P generates a group of order 242. Initialize.
+ ? ellorder(E, G, ord)
+ %4 = 242
+ ? e = elllog(E, P, G, ord)
+ %5 = 15
+ ? ellmul(E,G,e) == P
+ %6 = 1
+ @eprog
diff --git a/src/functions/elliptic_curves/elllseries b/src/functions/elliptic_curves/elllseries
new file mode 100644
index 0000000..a4925c1
--- /dev/null
+++ b/src/functions/elliptic_curves/elllseries
@@ -0,0 +1,15 @@
+Function: elllseries
+Section: elliptic_curves
+C-Name: elllseries
+Prototype: GGDGp
+Help: elllseries(E,s,{A=1}): L-series at s of the elliptic curve E, where A
+ a cut-off point close to 1.
+Doc:
+ $E$ being an elliptic curve, given by an arbitrary model over $\Q$ as output
+ by \kbd{ellinit}, this function computes the value of the $L$-series of $E$ at
+ the (complex) point $s$. This function uses an $O(N^{1/2})$ algorithm, where
+ $N$ is the conductor.
+
+ The optional parameter $A$ fixes a cutoff point for the integral and is best
+ left omitted; the result must be independent of $A$, up to
+ \kbd{realprecision}, so this allows to check the function's accuracy.
diff --git a/src/functions/elliptic_curves/ellminimalmodel b/src/functions/elliptic_curves/ellminimalmodel
new file mode 100644
index 0000000..7b3837c
--- /dev/null
+++ b/src/functions/elliptic_curves/ellminimalmodel
@@ -0,0 +1,16 @@
+Function: ellminimalmodel
+Section: elliptic_curves
+C-Name: ellminimalmodel
+Prototype: GD&
+Help: ellminimalmodel(E,{&v}): return the standard minimal integral model of
+ the rational elliptic curve E. Sets v to the corresponding change of
+ variables.
+Doc: return the standard minimal integral model of the rational elliptic
+ curve $E$. If present, sets $v$ to the corresponding change of variables,
+ which is a vector $[u,r,s,t]$ with rational components. The return value is
+ identical to that of \kbd{ellchangecurve(E, v)}.
+
+ The resulting model has integral coefficients, is everywhere minimal, $a_1$
+ is 0 or 1, $a_2$ is 0, 1 or $-1$ and $a_3$ is 0 or 1. Such a model is
+ unique, and the vector $v$ is unique if we specify that $u$ is positive,
+ which we do. \sidx{minimal model}
diff --git a/src/functions/elliptic_curves/ellmodulareqn b/src/functions/elliptic_curves/ellmodulareqn
new file mode 100644
index 0000000..7b53c0a
--- /dev/null
+++ b/src/functions/elliptic_curves/ellmodulareqn
@@ -0,0 +1,63 @@
+Function: ellmodulareqn
+Section: elliptic_curves
+C-Name: ellmodulareqn
+Prototype: LDnDn
+Help: ellmodulareqn(N,{x},{y}): return a vector [eqn, t] where eqn is a modular
+ equation of level N, for N<500, N prime. This requires the package seadata to
+ be installed.  The equation is either of canonical type (t=0) or of Atkin type
+ (t=1)
+Doc: return a vector [\kbd{eqn},$t$] where \kbd{eqn} is a modular equation of
+ level $N$, i.e.~a bivariate polynomial with integer coefficients; $t$
+ indicates the type of this equation: either \emph{canonical} ($t = 0$) or
+ \emph{Atkin} ($t = 1$). This function currently requires the package
+ \kbd{seadata} to be installed and is limited to $N<500$, $N$ prime.
+
+ Let $j$ be the $j$-invariant function. The polynomial \kbd{eqn} satisfies
+ the following functional equation, which allows to compute the values of the
+ classical modular polynomial $\Phi_N$ of prime level $N$, such that
+ $\Phi_N(j(\tau), j(N\tau)) = 0$, while being much smaller than the latter:
+
+ \item for canonical type:
+  $P(f(\tau),j(\tau)) = P(N^s/f(\tau),j(N\*\tau)) = 0$,
+  where $s = 12/\gcd(12,N-1)$;
+
+ \item for Atkin type:
+  $P(f(\tau),j(\tau)) = P(f(\tau),j(N\*\tau)) = 0$.
+
+ \noindent In both cases, $f$ is a suitable modular function (see below).
+
+ The following GP function returns values of the classical modular polynomial
+ by eliminating $f(\tau)$ in the above two equations, for $N\leq 31$ or
+ $N\in\{41,47,59,71\}$.
+
+ \bprog
+ classicaleqn(N, X='X, Y='Y)=
+ {
+   my(E=ellmodulareqn(N), P=E[1], t=E[2], Q, d);
+   if(poldegree(P,'y)>2,error("level unavailable in classicaleqn"));
+   if (t == 0,
+     my(s = 12/gcd(12,N-1));
+     Q = 'x^(N+1) * substvec(P,['x,'y],[N^s/'x,Y]);
+     d = N^(s*(2*N+1)) * (-1)^(N+1);
+   ,
+     Q = subst(P,'y,Y);
+     d = (X-Y)^(N+1));
+   polresultant(subst(P,'y,X), Q) / d;
+ }
+ @eprog
+
+ More precisely, let $W_N(\tau)={{-1}\over{N\*\tau}}$ be the Atkin-Lehner
+ involution; we have $j(W_N(\tau)) = j(N\*\tau)$ and the function $f$ also
+ satisfies:
+
+ \item for canonical type:
+    $f(W_N(\tau)) = N^s/f(\tau)$;
+
+ \item for Atkin type:
+    $f(W_N(\tau)) = f(\tau)$.
+
+ \noindent Furthermore, for an equation of canonical type, $f$ is the standard
+ $\eta$-quotient
+ $$f(\tau) = N^s \* \big(\eta(N\*\tau) / \eta(\tau) \big)^{2\*s},$$
+ where $\eta$ is Dedekind's eta function, which is invariant under
+ $\Gamma_0(N)$.
diff --git a/src/functions/elliptic_curves/ellmul b/src/functions/elliptic_curves/ellmul
new file mode 100644
index 0000000..242255a
--- /dev/null
+++ b/src/functions/elliptic_curves/ellmul
@@ -0,0 +1,28 @@
+Function: ellmul
+Section: elliptic_curves
+C-Name: ellmul
+Prototype: GGG
+Help: ellmul(E,z,n): n times the point z on elliptic curve E (n in Z).
+Doc:
+ computes $[n]z$, where $z$ is a point on the elliptic curve $E$. The
+ exponent $n$ is in $\Z$, or may be a complex quadratic integer if the curve $E$
+ has complex multiplication by $n$ (if not, an error message is issued).
+ \bprog
+ ? Ei = ellinit([1,0]); z = [0,0];
+ ? ellmul(Ei, z, 10)
+ %2 = [0]     \\ unsurprising: z has order 2
+ ? ellmul(Ei, z, I)
+ %3 = [0, 0]  \\ Ei has complex multiplication by Z[i]
+ ? ellmul(Ei, z, quadgen(-4))
+ %4 = [0, 0]  \\ an alternative syntax for the same query
+ ? Ej  = ellinit([0,1]); z = [-1,0];
+ ? ellmul(Ej, z, I)
+   ***   at top-level: ellmul(Ej,z,I)
+   ***                 ^--------------
+   *** ellmul: not a complex multiplication in ellmul.
+ ? ellmul(Ej, z, 1+quadgen(-3))
+ %6 = [1 - w, 0]
+ @eprog
+ The simple-minded algorithm for the CM case assumes that we are in
+ characteristic $0$, and that the quadratic order to which $n$ belongs has
+ small discriminant.
diff --git a/src/functions/elliptic_curves/ellneg b/src/functions/elliptic_curves/ellneg
new file mode 100644
index 0000000..c8788ac
--- /dev/null
+++ b/src/functions/elliptic_curves/ellneg
@@ -0,0 +1,7 @@
+Function: ellneg
+Section: elliptic_curves
+C-Name: ellneg
+Prototype: GG
+Help: ellneg(E,z): opposite of the point z on elliptic curve E.
+Doc:
+ Opposite of the point $z$ on elliptic curve $E$.
diff --git a/src/functions/elliptic_curves/ellorder b/src/functions/elliptic_curves/ellorder
new file mode 100644
index 0000000..0607d71
--- /dev/null
+++ b/src/functions/elliptic_curves/ellorder
@@ -0,0 +1,45 @@
+Function: ellorder
+Section: elliptic_curves
+C-Name: ellorder
+Prototype: GGDG
+Help: ellorder(E,z,{o}): order of the point z on the elliptic curve E over Q
+ or a finite field, 0 if non-torsion. The parameter o, if present,
+ represents a non-zero multiple of the order of z.
+Doc: gives the order of the point $z$ on the elliptic
+ curve $E$, defined over $\Q$ or a finite field.
+ If the curve is defined over $\Q$, return (the impossible value) zero if the
+ point has infinite order.
+ \bprog
+ ? E = ellinit([-157^2,0]);  \\ the "157-is-congruent" curve
+ ? P = [2,2]; ellorder(E, P)
+ %2 = 2
+ ? P = ellheegner(E); ellorder(E, P) \\ infinite order
+ %3 = 0
+ ? E = ellinit(ellfromj(ffgen(5^10)));
+ ? ellcard(E)
+ %5 = 9762580
+ ? P = random(E); ellorder(E, P)
+ %6 = 4881290
+ ? p = 2^160+7; E = ellinit([1,2], p);
+ ? N = ellcard(E)
+ %8 = 1461501637330902918203686560289225285992592471152
+ ? o = [N, factor(N)];
+ ? for(i=1,100, ellorder(E,random(E)))
+ time = 260 ms.
+ @eprog
+ The parameter $o$, is now mostly useless, and kept for backward
+ compatibility. If present, it represents a non-zero multiple of the order
+ of $z$, see \secref{se:DLfun}; the preferred format for this parameter is
+ \kbd{[ord, factor(ord)]}, where \kbd{ord} is the cardinality of the curve.
+ It is no longer needed since PARI is now able to compute it over large
+ finite fields (was restricted to small prime fields at the time this feature
+ was introduced), \emph{and} caches the result in $E$ so that it is computed
+ and factored only once. Modifying the last example, we see that including
+ this extra parameter provides no improvement:
+ \bprog
+ ? o = [N, factor(N)];
+ ? for(i=1,100, ellorder(E,random(E),o))
+ time = 260 ms.
+ @eprog
+Variant: The obsolete form \fun{GEN}{orderell}{GEN e, GEN z} should no longer be
+ used.
diff --git a/src/functions/elliptic_curves/ellordinate b/src/functions/elliptic_curves/ellordinate
new file mode 100644
index 0000000..207e28f
--- /dev/null
+++ b/src/functions/elliptic_curves/ellordinate
@@ -0,0 +1,10 @@
+Function: ellordinate
+Section: elliptic_curves
+C-Name: ellordinate
+Prototype: GGp
+Help: ellordinate(E,x): y-coordinates corresponding to x-ordinate x on
+ elliptic curve E.
+Doc:
+ gives a 0, 1 or 2-component vector containing
+ the $y$-coordinates of the points of the curve $E$ having $x$ as
+ $x$-coordinate.
diff --git a/src/functions/elliptic_curves/ellperiods b/src/functions/elliptic_curves/ellperiods
new file mode 100644
index 0000000..46df46d
--- /dev/null
+++ b/src/functions/elliptic_curves/ellperiods
@@ -0,0 +1,24 @@
+Function: ellperiods
+Section: elliptic_curves
+C-Name: ellperiods
+Prototype: GD0,L,p
+Help: ellperiods(w, {flag = 0}): w describes a complex period lattice ([w1,w2]
+ or an ellinit structure). Returns normalized periods [W1,W2] generating the
+ same lattice such that tau := W1/W2 satisfies Im(tau) > 0 and lies in the
+ standard fundamental domain for SL2. If flag is 1, the return value is
+ [[W1,W2], [eta1,eta2]], where eta1, eta2 are the quasi-periods associated to
+ [W1,W2], satisfying eta1 W2 - eta2 W1 = 2 I Pi.
+Doc: Let $w$ describe a complex period lattice ($w = [w_1,w_2]$
+ or an ellinit structure). Returns normalized periods $[W_1,W_2]$ generating
+ the same lattice such that $\tau := W_1/W_2$ has positive imaginary part
+ and lies in the standard fundamental domain for $\text{SL}_2(\Z)$.
+
+ If $\fl = 1$, the function returns $[[W_1,W_2], [\eta_1,\eta_2]]$, where
+ $\eta_1$ and $\eta_2$ are the quasi-periods associated to
+ $[W_1,W_2]$, satisfying $\eta_1 W_2 - \eta_2 W_1 = 2 i \pi$.
+
+ The output of this function is meant to be used as the first argument
+ given to ellwp, ellzeta, ellsigma or elleisnum. Quasi-periods are
+ needed by ellzeta and ellsigma only.
+
+
diff --git a/src/functions/elliptic_curves/ellpointtoz b/src/functions/elliptic_curves/ellpointtoz
new file mode 100644
index 0000000..7e1f679
--- /dev/null
+++ b/src/functions/elliptic_curves/ellpointtoz
@@ -0,0 +1,58 @@
+Function: ellpointtoz
+Section: elliptic_curves
+C-Name: zell
+Prototype: GGp
+Help: ellpointtoz(E,P): lattice point z corresponding to the point P on the
+ elliptic curve E.
+Doc:
+ if $E/\C \simeq \C/\Lambda$ is a complex elliptic curve ($\Lambda =
+ \kbd{E.omega}$),
+ computes a complex number $z$, well-defined modulo the lattice $\Lambda$,
+ corresponding to the point $P$; i.e.~such that
+  $P = [\wp_\Lambda(z),\wp'_\Lambda(z)]$
+ satisfies the equation
+ $$y^2 = 4x^3 - g_2 x - g_3,$$
+ where $g_2$, $g_3$ are the elliptic invariants.
+
+ If $E$ is defined over $\R$ and $P\in E(\R)$, we have more precisely, $0 \leq
+ \Re(t) < w1$ and $0 \leq \Im(t) < \Im(w2)$, where $(w1,w2)$ are the real and
+ complex periods of $E$.
+ \bprog
+ ? E = ellinit([0,1]); P = [2,3];
+ ? z = ellpointtoz(E, P)
+ %2 = 3.5054552633136356529375476976257353387
+ ? ellwp(E, z)
+ %3 = 2.0000000000000000000000000000000000000
+ ? ellztopoint(E, z) - P
+ %4 = [6.372367644529809109 E-58, 7.646841173435770930 E-57]
+ ? ellpointtoz(E, [0]) \\ the point at infinity
+ %5 = 0
+ @eprog
+
+ If $E/\Q_p$ has multiplicative reduction, then $E/\bar{\Q_p}$ is analytically
+ isomorphic to $\bar{\Q}_p^*/q^\Z$ (Tate curve) for some $p$-adic integer $q$.
+ The behaviour is then as follows:
+
+ \item If the reduction is split ($E.\kbd{tate[2]}$ is a \typ{PADIC}), we have
+ an isomorphism $\phi: E(\Q_p) \simeq \Q_p^*/q^\Z$ and the function returns
+ $\phi(P)\in \Q_p$.
+
+ \item If the reduction is \emph{not} split ($E.\kbd{tate[2]}$ is a
+ \typ{POLMOD}), we only have an isomorphism $\phi: E(K) \simeq K^*/q^\Z$ over
+ the unramified quadratic extension $K/\Q_p$. In this case, the output
+ $\phi(P)\in K$ is a \typ{POLMOD}.
+ \bprog
+ ? E = ellinit([0,-1,1,0,0], O(11^5)); P = [0,0];
+ ? [u2,u,q] = E.tate; type(u) \\ split multiplicative reduction
+ %2 = "t_PADIC"
+ ? ellmul(E, P, 5)  \\ P has order 5
+ %3 = [0]
+ ? z = ellpointtoz(E, [0,0])
+ %4 = 3 + 11^2 + 2*11^3 + 3*11^4 + O(11^5)
+ ? z^5
+ %5 = 1 + O(11^5)
+ ? E = ellinit(ellfromj(1/4), O(2^6)); x=1/2; y=ellordinate(E,x)[1];
+ ? z = ellpointtoz(E,[x,y]); \\ t_POLMOD of t_POL with t_PADIC coeffs
+ ? liftint(z) \\ lift all p-adics
+ %8 = Mod(8*u + 7, u^2 + 437)
+ @eprog
diff --git a/src/functions/elliptic_curves/ellpow b/src/functions/elliptic_curves/ellpow
new file mode 100644
index 0000000..63720a9
--- /dev/null
+++ b/src/functions/elliptic_curves/ellpow
@@ -0,0 +1,6 @@
+Function: ellpow
+Section: elliptic_curves
+C-Name: ellmul
+Prototype: GGG
+Help: ellpow(E,z,n): deprecated alias for ellmul.
+Doc: deprecated alias for \kbd{ellmul}.
diff --git a/src/functions/elliptic_curves/ellrootno b/src/functions/elliptic_curves/ellrootno
new file mode 100644
index 0000000..4ffae9e
--- /dev/null
+++ b/src/functions/elliptic_curves/ellrootno
@@ -0,0 +1,14 @@
+Function: ellrootno
+Section: elliptic_curves
+C-Name: ellrootno
+Prototype: lGDG
+Help: ellrootno(E,{p}): root number for the L-function of the elliptic
+ curve E/Q at a prime p (including 0, for the infinite place); global root
+ number if p is omitted.
+Doc: $E$ being an \var{ell} structure over $\Q$ as output by \kbd{ellinit},
+ this function computes the local root number of its $L$-series at the place
+ $p$ (at the infinite place if $p = 0$). If $p$ is omitted, return the global
+ root number. Note that the global root number is the sign of the functional
+ equation and conjecturally is the parity of the rank of the \idx{Mordell-Weil
+ group}. The equation for $E$ needs not be minimal at $p$, but if the model
+ is already minimal the function will run faster.
diff --git a/src/functions/elliptic_curves/ellsearch b/src/functions/elliptic_curves/ellsearch
new file mode 100644
index 0000000..3a240e5
--- /dev/null
+++ b/src/functions/elliptic_curves/ellsearch
@@ -0,0 +1,47 @@
+Function: ellsearch
+Section: elliptic_curves
+C-Name: ellsearch
+Prototype: G
+Help: ellsearch(N): returns all curves in the elldata database matching
+ constraint N:  given name (N = "11a1" or [11,0,1]),
+ given isogeny class (N = "11a" or [11,0]), or
+ given conductor (N = 11, "11", or [11]).
+Doc: This function finds all curves in the \tet{elldata} database satisfying
+ the constraint defined by the argument $N$:
+
+ \item if $N$ is a character string, it selects a given curve, e.g.
+ \kbd{"11a1"}, or curves in the given isogeny class, e.g. \kbd{"11a"}, or
+ curves with given conductor, e.g. \kbd{"11"};
+
+ \item if $N$ is a vector of integers, it encodes the same constraints
+ as the character string above, according to the \tet{ellconvertname}
+ correspondance, e.g. \kbd{[11,0,1]} for \kbd{"11a1"}, \kbd{[11,0]} for
+ \kbd{"11a"} and \kbd{[11]} for \kbd{"11"};
+
+ \item if $N$ is an integer, curves with conductor $N$ are selected.
+
+ If $N$ is a full curve name, e.g. \kbd{"11a1"} or \kbd{[11,0,1]},
+ the output format is $[N, [a_1,a_2,a_3,a_4,a_6], G]$ where
+ $[a_1,a_2,a_3,a_4,a_6]$ are the coefficients of the Weierstrass equation of
+ the curve and $G$ is a $\Z$-basis of the free part of the \idx{Mordell-Weil
+ group} associated to the curve.
+ \bprog
+ ? ellsearch("11a3")
+ %1 = ["11a3", [0, -1, 1, 0, 0], []]
+ ? ellsearch([11,0,3])
+ %2 = ["11a3", [0, -1, 1, 0, 0], []]
+ @eprog\noindent
+
+ If $N$ is not a full curve name, then the output is a vector of all matching
+ curves in the above format:
+ \bprog
+ ? ellsearch("11a")
+ %1 = [["11a1", [0, -1, 1, -10, -20], []],
+       ["11a2", [0, -1, 1, -7820, -263580], []],
+       ["11a3", [0, -1, 1, 0, 0], []]]
+ ? ellsearch("11b")
+ %2 = []
+ @eprog
+
+Variant: Also available is \fun{GEN}{ellsearchcurve}{GEN N} that only
+ accepts complete curve names (as \typ{STR}).
diff --git a/src/functions/elliptic_curves/ellsigma b/src/functions/elliptic_curves/ellsigma
new file mode 100644
index 0000000..565c04a
--- /dev/null
+++ b/src/functions/elliptic_curves/ellsigma
@@ -0,0 +1,24 @@
+Function: ellsigma
+Section: elliptic_curves
+C-Name: ellsigma
+Prototype: GDGD0,L,p
+Help: ellsigma(L,{z='x},{flag=0}): computes the value at z of the Weierstrass
+ sigma function attached to the lattice w, as given by ellperiods(,1).
+ If flag = 1, returns an arbitrary determination of the logarithm of sigma.
+Doc: Computes the value at $z$ of the Weierstrass $\sigma$ function attached to
+ the lattice $L$ as given by \tet{ellperiods}$(,1)$: including quasi-periods
+ is useful, otherwise there are recomputed from scratch for each new $z$.
+ $$ \sigma(z, L) = z \prod_{\omega\in L^*} \left(1 -
+ \dfrac{z}{\omega}\right)e^{\dfrac{z}{\omega} + \dfrac{z^2}{2\omega^2}}.$$
+ It is also possible to directly input $L = [\omega_1,\omega_2]$,
+ or an elliptic curve $E$ as given by \kbd{ellinit} ($L = \kbd{E.omega}$).
+ \bprog
+ ? w = ellperiods([1,I], 1);
+ ? ellsigma(w, 1/2)
+ %2 = 0.47494937998792065033250463632798296855
+ ? E = ellinit([1,0]);
+ ? ellsigma(E) \\ at 'x, implicitly at default seriesprecision
+ %4 = x + 1/60*x^5 - 1/10080*x^9 - 23/259459200*x^13 + O(x^17)
+ @eprog
+
+ If $\fl=1$, computes an arbitrary determination of $\log(\sigma(z))$.
diff --git a/src/functions/elliptic_curves/ellsub b/src/functions/elliptic_curves/ellsub
new file mode 100644
index 0000000..30b5e67
--- /dev/null
+++ b/src/functions/elliptic_curves/ellsub
@@ -0,0 +1,8 @@
+Function: ellsub
+Section: elliptic_curves
+C-Name: ellsub
+Prototype: GGG
+Help: ellsub(E,z1,z2): difference of the points z1 and z2 on elliptic curve E.
+Doc:
+ difference of the points $z1$ and $z2$ on the
+ elliptic curve corresponding to $E$.
diff --git a/src/functions/elliptic_curves/elltaniyama b/src/functions/elliptic_curves/elltaniyama
new file mode 100644
index 0000000..985154e
--- /dev/null
+++ b/src/functions/elliptic_curves/elltaniyama
@@ -0,0 +1,22 @@
+Function: elltaniyama
+Section: elliptic_curves
+C-Name: elltaniyama
+Prototype: GDP
+Help: elltaniyama(E, {d = seriesprecision}): modular parametrization of
+ elliptic curve E/Q.
+Doc:
+ computes the modular parametrization of the elliptic curve $E/\Q$,
+ where $E$ is an \var{ell} structure as output by \kbd{ellinit}. This returns
+ a two-component vector $[u,v]$ of power series, given to $d$ significant
+ terms (\tet{seriesprecision} by default), characterized by the following two
+ properties. First the point $(u,v)$ satisfies the equation of the elliptic
+ curve. Second, let $N$ be the conductor of $E$ and $\Phi: X_0(N)\to E$
+ be a modular parametrization; the pullback by $\Phi$ of the
+ N\'eron differential $du/(2v+a_1u+a_3)$ is equal to $2i\pi
+ f(z)dz$, a holomorphic differential form. The variable used in the power
+ series for $u$ and $v$ is $x$, which is implicitly understood to be equal to
+ $\exp(2i\pi z)$.
+
+ The algorithm assumes that $E$ is a \emph{strong} \idx{Weil curve}
+ and that the Manin constant is equal to 1: in fact, $f(x) = \sum_{n > 0}
+ \kbd{ellan}(E, n) x^n$.
diff --git a/src/functions/elliptic_curves/elltatepairing b/src/functions/elliptic_curves/elltatepairing
new file mode 100644
index 0000000..261b5c0
--- /dev/null
+++ b/src/functions/elliptic_curves/elltatepairing
@@ -0,0 +1,8 @@
+Function: elltatepairing
+Section: elliptic_curves
+C-Name: elltatepairing
+Prototype: GGGG
+Help: elltatepairing(E, P, Q, m): Computes the Tate pairing of the two points
+ P and Q on the elliptic curve E. The point P must be of m-torsion.
+Doc: Computes the Tate pairing of the two points $P$ and $Q$ on the elliptic
+ curve $E$. The point $P$ must be of $m$-torsion.
diff --git a/src/functions/elliptic_curves/elltors b/src/functions/elliptic_curves/elltors
new file mode 100644
index 0000000..63c5fb5
--- /dev/null
+++ b/src/functions/elliptic_curves/elltors
@@ -0,0 +1,32 @@
+Function: elltors
+Section: elliptic_curves
+C-Name: elltors0
+Prototype: GD0,L,
+Help: elltors(E,{flag=0}): torsion subgroup of elliptic curve E: order,
+ structure, generators. If flag = 0, use division polynomials; if flag = 1, use
+ Lutz-Nagell; if flag = 2, use Doud's algorithm.
+Doc:
+ if $E$ is an elliptic curve \emph{defined over $\Q$}, outputs the torsion
+ subgroup of $E$ as a 3-component vector \kbd{[t,v1,v2]}, where \kbd{t} is the
+ order of the torsion group, \kbd{v1} gives the structure of the torsion group
+ as a product of cyclic groups (sorted by decreasing order), and \kbd{v2}
+ gives generators for these cyclic groups. $E$ must be an \var{ell} structure
+ as output by \kbd{ellinit}, defined over $\Q$.
+
+ \bprog
+ ?  E = ellinit([-1,0]);
+ ?  elltors(E)
+ %1 = [4, [2, 2], [[0, 0], [1, 0]]]
+ @eprog
+ Here, the torsion subgroup is isomorphic to $\Z/2\Z \times \Z/2\Z$, with
+ generators $[0,0]$ and $[1,0]$.
+
+ If $\fl = 0$, find rational roots of division polynomials.
+
+ If $\fl = 1$, use Lutz-Nagell (\emph{much} slower).
+
+ If $\fl = 2$, use Doud's algorithm: bound torsion by computing $\#E(\F_p)$
+ for small primes of good reduction, then look for torsion points using
+ Weierstrass $\wp$ function (and Mazur's classification). For this variant,
+ $E$ must be an \var{ell}.
+Variant: Also available is \fun{GEN}{elltors}{GEN E} for \kbd{elltors(E, 0)}.
diff --git a/src/functions/elliptic_curves/ellweilpairing b/src/functions/elliptic_curves/ellweilpairing
new file mode 100644
index 0000000..ff2b16a
--- /dev/null
+++ b/src/functions/elliptic_curves/ellweilpairing
@@ -0,0 +1,8 @@
+Function: ellweilpairing
+Section: elliptic_curves
+C-Name: ellweilpairing
+Prototype: GGGG
+Help: ellweilpairing(E, P, Q, m): Computes the Weil pairing of the two points
+ of m-torsion P and Q on the elliptic curve E.
+Doc: Computes the Weil pairing of the two points of $m$-torsion $P$ and $Q$
+ on the elliptic curve $E$.
diff --git a/src/functions/elliptic_curves/ellwp b/src/functions/elliptic_curves/ellwp
new file mode 100644
index 0000000..9dfebe0
--- /dev/null
+++ b/src/functions/elliptic_curves/ellwp
@@ -0,0 +1,34 @@
+Function: ellwp
+Section: elliptic_curves
+C-Name: ellwp0
+Prototype: GDGD0,L,p
+Help: ellwp(w,{z='x},{flag=0}): computes the value at z of the Weierstrass P
+ function attached to the lattice w, as given by ellperiods. Optional flag
+ means 0 (default), compute only P(z), 1 compute [P(z),P'(z)].
+Doc: Computes the value at $z$ of the Weierstrass $\wp$ function attached to
+ the lattice $w$ as given by \tet{ellperiods}. It is also possible to
+ directly input $w = [\omega_1,\omega_2]$, or an elliptic curve $E$ as given
+ by \kbd{ellinit} ($w = \kbd{E.omega}$).
+ \bprog
+ ? w = ellperiods([1,I]);
+ ? ellwp(w, 1/2)
+ %2 = 6.8751858180203728274900957798105571978
+ ? E = ellinit([1,1]);
+ ? ellwp(E, 1/2)
+ %4 = 3.9413112427016474646048282462709151389
+ @eprog\noindent One can also compute the series expansion around $z = 0$:
+ \bprog
+ ? E = ellinit([1,0]);
+ ? ellwp(E)              \\ 'x implicitly at default seriesprecision
+ %5 = x^-2 - 1/5*x^2 + 1/75*x^6 - 2/4875*x^10 + O(x^14)
+ ? ellwp(E, x + O(x^12)) \\ explicit precision
+ %6 = x^-2 - 1/5*x^2 + 1/75*x^6 + O(x^9)
+ @eprog
+
+ Optional \fl\ means 0 (default): compute only $\wp(z)$, 1: compute
+ $[\wp(z),\wp'(z)]$.
+
+Variant: For $\fl = 0$, we also have
+ \fun{GEN}{ellwp}{GEN w, GEN z, long prec}, and
+ \fun{GEN}{ellwpseries}{GEN E, long v, long precdl} for the power series in
+ variable $v$.
diff --git a/src/functions/elliptic_curves/ellzeta b/src/functions/elliptic_curves/ellzeta
new file mode 100644
index 0000000..1a7d2ca
--- /dev/null
+++ b/src/functions/elliptic_curves/ellzeta
@@ -0,0 +1,33 @@
+Function: ellzeta
+Section: elliptic_curves
+C-Name: ellzeta
+Prototype: GDGp
+Help: ellzeta(w,{z='x}): computes the value at z of the Weierstrass Zeta
+ function attached to the lattice w, as given by ellperiods(,1).
+Doc: Computes the value at $z$ of the Weierstrass $\zeta$ function attached to
+ the lattice $w$ as given by \tet{ellperiods}$(,1)$: including quasi-periods
+ is useful, otherwise there are recomputed from scratch for each new $z$.
+ $$ \zeta(z, L) = \dfrac{1}{z} + z^2\sum_{\omega\in L^*}
+ \dfrac{1}{\omega^2(z-\omega)}.$$
+ It is also possible to directly input $w = [\omega_1,\omega_2]$,
+ or an elliptic curve $E$ as given by \kbd{ellinit} ($w = \kbd{E.omega}$).
+ The quasi-periods of $\zeta$, such that
+ $$\zeta(z + a\omega_1 + b\omega_2) = \zeta(z) + a\eta_1 + b\eta_2 $$
+ for integers $a$ and $b$ are obtained as $\eta_i = 2\zeta(\omega_i/2)$.
+ Or using directly \tet{elleta}.
+ \bprog
+ ? w = ellperiods([1,I],1);
+ ? ellzeta(w, 1/2)
+ %2 = 1.5707963267948966192313216916397514421
+ ? E = ellinit([1,0]);
+ ? ellzeta(E, E.omega[1]/2)
+ %4 = 0.84721308479397908660649912348219163647
+ @eprog\noindent One can also compute the series expansion around $z = 0$
+ (the quasi-periods are useless in this case):
+ \bprog
+ ? E = ellinit([0,1]);
+ ? ellzeta(E) \\ at 'x, implicitly at default seriesprecision
+ %4 = x^-1 + 1/35*x^5 - 1/7007*x^11 + O(x^15)
+ ? ellzeta(E, x + O(x^20)) \\ explicit precision
+ %5 = x^-1 + 1/35*x^5 - 1/7007*x^11 + 1/1440257*x^17 + O(x^18)
+ @eprog\noindent
diff --git a/src/functions/elliptic_curves/ellztopoint b/src/functions/elliptic_curves/ellztopoint
new file mode 100644
index 0000000..60cccd1
--- /dev/null
+++ b/src/functions/elliptic_curves/ellztopoint
@@ -0,0 +1,17 @@
+Function: ellztopoint
+Section: elliptic_curves
+C-Name: pointell
+Prototype: GGp
+Help: ellztopoint(E,z): coordinates of point P on the curve E corresponding
+ to the complex number z.
+Doc:
+ $E$ being an \var{ell} as output by
+ \kbd{ellinit}, computes the coordinates $[x,y]$ on the curve $E$
+ corresponding to the complex number $z$. Hence this is the inverse function
+ of \kbd{ellpointtoz}. In other words, if the curve is put in Weierstrass
+ form $y^2 = 4x^3 - g_2x - g_3$, $[x,y]$ represents the Weierstrass
+ $\wp$-function\sidx{Weierstrass $\wp$-function} and its derivative. More
+ precisely, we have
+ $$x = \wp(z) - b_2/12,\quad y = \wp'(z) - (a_1 x + a_3)/2.$$
+ If $z$ is in the lattice defining $E$ over $\C$, the result is the point at
+ infinity $[0]$.
diff --git a/src/functions/elliptic_curves/genus2red b/src/functions/elliptic_curves/genus2red
new file mode 100644
index 0000000..5d713c9
--- /dev/null
+++ b/src/functions/elliptic_curves/genus2red
@@ -0,0 +1,116 @@
+Function: genus2red
+Section: elliptic_curves
+C-Name: genus2red
+Prototype: GGDG
+Help: genus2red(Q,P,{p}): let Q,P be polynomials with integer coefficients.
+ Determines the reduction at p > 2 of the
+ (proper, smooth) hyperelliptic curve C/Q: y^2+Qy = P, of genus 2.
+ (The special fiber X_p of the minimal regular model X of C over Z.)
+Doc: Let $Q,P$ be polynomials with integer coefficients.
+ Determines the reduction at $p > 2$ of the (proper, smooth) genus~2
+ curve $C/\Q$, defined by the hyperelliptic equation $y^2+Qy = P$. (The
+ special fiber $X_p$ of the minimal regular model $X$ of $C$ over $\Z$.)
+ If $p$ is omitted, determines the reduction type for all (odd) prime
+ divisors of the discriminant.
+
+ \noindent This function rewritten from an implementation of Liu's algorithm by
+ Cohen and Liu (1994), \kbd{genus2reduction-0.3}, see
+ \kbd{http://www.math.u-bordeaux1.fr/\til liu/G2R/}.
+
+ \misctitle{CAVEAT} The function interface may change: for the
+ time being, it returns $[N,\var{FaN}, T, V]$
+ where $N$ is either the local conductor at $p$ or the
+ global conductor, \var{FaN} is its factorization, $y^2 = T$ defines a
+ minimal model over $\Z[1/2]$ and $V$ describes the reduction type at the
+ various considered~$p$. Unfortunately, the program is not complete for
+ $p = 2$, and we may return the odd part of the conductor only: this is the
+ case if the factorization includes the (impossible) term $2^{-1}$; if the
+ factorization contains another power of $2$, then this is the exact local
+ conductor at $2$ and $N$ is the global conductor.
+
+ \bprog
+ ? default(debuglevel, 1);
+ ? genus2red(0,x^6 + 3*x^3 + 63, 3)
+ (potential) stable reduction: [1, []]
+ reduction at p: [III{9}] page 184, [3, 3], f = 10
+ %1 = [59049, Mat([3, 10]), x^6 + 3*x^3 + 63, [3, [1, []],
+        ["[III{9}] page 184", [3, 3]]]]
+ ? [N, FaN, T, V] = genus2red(x^3-x^2-1, x^2-x);  \\ X_1(13), global reduction
+ p = 13
+ (potential) stable reduction: [5, [Mod(0, 13), Mod(0, 13)]]
+ reduction at p: [I{0}-II-0] page 159, [], f = 2
+ ? N
+ %3 = 169
+ ? FaN
+ %4 = Mat([13, 2])   \\ in particular, good reduction at 2 !
+ ? T
+ %5 = x^6 + 58*x^5 + 1401*x^4 + 18038*x^3 + 130546*x^2 + 503516*x + 808561
+ ? V
+ %6 = [[13, [5, [Mod(0, 13), Mod(0, 13)]], ["[I{0}-II-0] page 159", []]]]
+ @eprog\noindent
+ We now first describe the format of the vector $V = V_p$ in the case where
+ $p$ was specified (local reduction at~$p$): it is a triple $[p, \var{stable},
+ \var{red}]$. The component $\var{stable} = [\var{type}, \var{vecj}]$ contains
+ information about the stable reduction after a field extension;
+ depending on \var{type}s, the stable reduction is
+
+ \item 1: smooth (i.e. the curve has potentially good reduction). The
+       Jacobian $J(C)$ has potentially good reduction.
+
+ \item 2: an elliptic curve $E$ with an ordinary double point; \var{vecj}
+ contains $j$ mod $p$, the modular invariant of $E$. The (potential)
+ semi-abelian reduction of $J(C)$ is the extension of an elliptic curve (with
+ modular invariant $j$ mod $p$) by a torus.
+
+ \item 3: a projective line with two ordinary double points. The Jacobian
+ $J(C)$ has potentially multiplicative reduction.
+
+ \item 4: the union of two projective lines crossing transversally at three
+ points. The Jacobian $J(C)$ has potentially multiplicative reduction.
+
+ \item 5: the union of two elliptic curves $E_1$ and $E_2$ intersecting
+ transversally at one point; \var{vecj} contains their modular invariants
+ $j_1$ and $j_2$, which may live in a quadratic extension of $\F_p$ are need
+ not be distinct. The Jacobian $J(C)$ has potentially good reduction,
+ isomorphic to the product of the reductions of $E_1$ and $E_2$.
+
+ \item 6: the union of an elliptic curve $E$ and a projective line which has
+ an ordinary double point, and these two components intersect transversally
+ at one point; \var{vecj} contains $j$ mod $p$, the modular invariant of $E$.
+ The (potential) semi-abelian reduction of $J(C)$ is the extension of an
+ elliptic curve (with modular invariant $j$ mod $p$) by a torus.
+
+ \item 7: as in type 6, but the two components are both singular. The
+ Jacobian $J(C)$ has potentially multiplicative reduction.
+
+ The component $\var{red} = [\var{NUtype}, \var{neron}]$ contains two data
+ concerning the reduction at $p$ without any ramified field extension.
+
+ The \var{NUtype} is a \typ{STR} describing the reduction at $p$ of $C$,
+ following Namikawa-Ueno, \emph{The complete classification of fibers in
+ pencils of curves of genus two}, Manuscripta Math., vol. 9, (1973), pages
+ 143-186. The reduction symbol is followed by the corresponding page number in
+ this article.
+
+ The second datum \var{neron} is the group of connected components (over an
+ algebraic closure of $\F_p$) of the N\'eron model of $J(C)$, given as a
+ finite abelian group (vector of elementary divisors).
+ \smallskip
+ If $p = 2$, the \var{red} component may be omitted altogether (and
+ replaced by \kbd{[]}, in the case where the program could not compute it.
+ When $p$ was not specified, $V$ is the vector of all $V_p$, for all
+ considered $p$.
+
+ \misctitle{Notes about Namikawa-Ueno types}
+
+ \item A lower index is denoted between braces: for instance, \kbd{[I\obr
+  2\cbr-II-5]} means \kbd{[I\_2-II-5]}.
+
+ \item If $K$ and $K'$ are Kodaira symbols for singular fibers of elliptic
+ curves, \kbd{[$K$-$K'$-m]} and \kbd{[$K'$-$K$-m]} are the same.
+
+ \item \kbd{[$K$-$K'$-$-1$]}  is \kbd{[$K'$-$K$-$\alpha$]} in the notation of
+ Namikawa-Ueno.
+
+ \item The figure \kbd{[2I\_0-m]} in Namikawa-Ueno, page 159, must be denoted
+ by \kbd{[2I\_0-(m+1)]}.
diff --git a/src/functions/gp2c/DEBUGLEVEL b/src/functions/gp2c/DEBUGLEVEL
new file mode 100644
index 0000000..450cd48
--- /dev/null
+++ b/src/functions/gp2c/DEBUGLEVEL
@@ -0,0 +1,6 @@
+Function: DEBUGLEVEL
+C-Name: DEBUGLEVEL
+Prototype: v
+Class: gp2c
+Description:
+ ():small                         DEBUGLEVEL
diff --git a/src/functions/gp2c/clone b/src/functions/gp2c/clone
new file mode 100644
index 0000000..cb0418d
--- /dev/null
+++ b/src/functions/gp2c/clone
@@ -0,0 +1,11 @@
+Function: clone
+Class: gp2c
+Description:
+ (small):small:parens             $1
+ (int):int                        gclone($1)
+ (real):real                      gclone($1)
+ (mp):mp                          gclone($1)
+ (vecsmall):vecsmall              gclone($1)
+ (vec):vec                        gclone($1)
+ (pol):pol                        gclone($1)
+ (gen):gen                        gclone($1)
diff --git a/src/functions/gp2c/copy b/src/functions/gp2c/copy
new file mode 100644
index 0000000..95ab98b
--- /dev/null
+++ b/src/functions/gp2c/copy
@@ -0,0 +1,11 @@
+Function: copy
+Class: gp2c
+Description:
+ (small):small:parens             $1
+ (int):int                        icopy($1)
+ (real):real                      gcopy($1)
+ (mp):mp                          gcopy($1)
+ (vecsmall):vecsmall              gcopy($1)
+ (vec):vec                        gcopy($1)
+ (pol):pol                        gcopy($1)
+ (gen):gen                        gcopy($1)
diff --git a/src/functions/gp2c/unclone b/src/functions/gp2c/unclone
new file mode 100644
index 0000000..f88a7df
--- /dev/null
+++ b/src/functions/gp2c/unclone
@@ -0,0 +1,5 @@
+Function: unclone
+Class: gp2c
+Description:
+ (small):void   (void)0 /*unclone*/
+ (gen):void     gunclone($1)
diff --git a/src/functions/gp2c_internal/_avma b/src/functions/gp2c_internal/_avma
new file mode 100644
index 0000000..7843d60
--- /dev/null
+++ b/src/functions/gp2c_internal/_avma
@@ -0,0 +1,4 @@
+Function: _avma
+Class: gp2c_internal
+Description:
+ ():pari_sp                avma
diff --git a/src/functions/gp2c_internal/_badtype b/src/functions/gp2c_internal/_badtype
new file mode 100644
index 0000000..8d04e5f
--- /dev/null
+++ b/src/functions/gp2c_internal/_badtype
@@ -0,0 +1,17 @@
+Function: _badtype
+Class: gp2c_internal
+Help: Code to check types. If not void, will be used as if(...).
+Description:
+ (int):bool:parens              typ($1) != t_INT
+ (real):bool:parens             typ($1) != t_REAL
+ (mp):negbool                   is_intreal_t(typ($1))
+ (vec):negbool                  is_matvec_t(typ($1))
+ (vecsmall):bool:parens         typ($1) != t_VECSMALL
+ (pol):bool:parens              typ($1) != t_POL
+ (*nf):void:parens              $1 = checknf($1)
+ (*bnf):void:parens             $1 = checkbnf($1)
+ (bnr):void                     checkbnr($1)
+ (prid):void                    checkprid($1)
+ (clgp):void                    checkabgrp($1)
+ (ell):void                     checkell($1)
+ (*gal):gal:parens              $1 = checkgal($1)
diff --git a/src/functions/gp2c_internal/_cast b/src/functions/gp2c_internal/_cast
new file mode 100644
index 0000000..77ba135
--- /dev/null
+++ b/src/functions/gp2c_internal/_cast
@@ -0,0 +1,141 @@
+Function: _type_preorder
+Class: gp2c_internal
+Help: List of chains of type preorder.
+Description:
+ (empty, void, bool, small, int, mp, gen)
+ (empty, real, mp)
+ (empty, bptr, small)
+ (empty, bool, lg, small)
+ (empty, bool, small_int, small)
+ (empty, void, negbool, bool)
+ (empty, typ, str, genstr,gen)
+ (empty, errtyp, str)
+ (empty, vecsmall, gen)
+ (empty, vec, gen)
+ (empty, list, gen)
+ (empty, closure, gen)
+ (empty, error, gen)
+ (empty, bnr, bnf, nf, vec)
+ (empty, bnr, bnf, clgp, vec)
+ (empty, ell, vec)
+ (empty, prid, vec)
+ (empty, gal, vec)
+ (empty, var, pol, gen)
+
+Function: _cast
+Class: gp2c_internal
+Help: (type1):type2 : cast expression of type1 to type2
+Description:
+ (void):bool           0
+ (#negbool):bool       ${1 value not}
+ (negbool):bool        !$(1)
+ (small_int):bool
+ (small):bool
+ (lg):bool:parens      $(1)!=1
+ (bptr):bool           *$(1)
+ (gen):bool            !gequal0($1)
+ (real):bool           signe($1)
+ (int):bool            signe($1)
+ (mp):bool             signe($1)
+ (pol):bool            signe($1)
+
+ (void):negbool        1
+ (#bool):negbool       ${1 value not}
+ (bool):negbool        !$(1)
+ (lg):negbool:parens   $(1)==1
+ (bptr):negbool        !*$(1)
+ (gen):negbool         gequal0($1)
+ (int):negbool         !signe($1)
+ (real):negbool        !signe($1)
+ (mp):negbool          !signe($1)
+ (pol):negbool         !signe($1)
+
+ (bool):small_int
+ (typ):small_int
+ (small):small_int
+
+ (bool):small
+ (typ):small
+ (small_int):small
+ (bptr):small           *$(1)
+ (int):small            itos($1)
+ (#lg):small:parens     ${1 value 1 sub}
+ (lg):small:parens      $(1)-1
+ (gen):small            gtos($1)
+
+ (void):int             gen_0
+ (-2):int               gen_m2
+ (-1):int               gen_m1
+ (0):int                gen_0
+ (1):int                gen_1
+ (2):int                gen_2
+ (bool):int             stoi($1)
+ (small):int            stoi($1)
+ (mp):int
+ (gen):int
+
+ (mp):real
+ (gen):real
+
+ (int):mp
+ (real):mp
+ (gen):mp
+
+ (#bool):lg:parens             ${1 1 value add}
+ (bool):lg:parens              $(1)+1
+ (#small):lg:parens            ${1 1 value add}
+ (small):lg:parens             $(1)+1
+
+ (gen):error
+ (gen):closure
+ (gen):vecsmall
+
+ (nf):vec
+ (bnf):vec
+ (bnr):vec
+ (ell):vec
+ (clgp):vec
+ (prid):vec
+ (gal):vec
+ (gen):vec
+
+ (gen):list
+
+ (pol):var      varn($1)
+ (gen):var      gvar($1)
+
+ (var):pol      pol_x($1)
+ (gen):pol
+
+ (int):gen
+ (mp):gen
+ (vecsmall):gen
+ (vec):gen
+ (list):gen
+ (pol):gen
+ (genstr):gen
+ (error):gen
+ (closure):gen
+
+ (gen):genstr GENtoGENstr($1)
+ (str):genstr strtoGENstr($1)
+
+ (gen):str GENtostr_unquoted($1)
+ (genstr):str GSTR($1)
+ (typ):str type_name($1)
+ (errtyp):str numerr_name($1)
+
+ (#str):typ  ${1 str_format}
+ (#str):errtyp  ${1 str_format}
+
+ (bnf):nf              bnf_get_nf($1)
+ (gen):nf
+ (bnr):bnf             bnr_get_bnf($1)
+ (gen):bnf
+ (gen):bnr
+ (bnf):clgp            bnf_get_clgp($1)
+ (bnr):clgp            bnr_get_clgp($1)
+ (gen):clgp
+ (gen):ell
+ (gen):gal
+ (gen):prid
diff --git a/src/functions/gp2c_internal/_cgetg b/src/functions/gp2c_internal/_cgetg
new file mode 100644
index 0000000..3c19c15
--- /dev/null
+++ b/src/functions/gp2c_internal/_cgetg
@@ -0,0 +1,5 @@
+Function: _cgetg
+Class: gp2c_internal
+Description:
+ (lg,#str):gen              cgetg($1, ${2 str_raw})
+ (gen,lg,#str):gen          $1 = cgetg($2, ${3 str_raw})
diff --git a/src/functions/gp2c_internal/_const b/src/functions/gp2c_internal/_const
new file mode 100644
index 0000000..5ba2fe6
--- /dev/null
+++ b/src/functions/gp2c_internal/_const
@@ -0,0 +1,23 @@
+Function: _const_smallreal
+Class: gp2c_internal
+Description:
+ (0):real:prec       real_0(prec)
+ (1):real:prec       real_1(prec)
+ (-1):real:prec      real_m1(prec)
+ (small):real:prec   stor($1, prec)
+
+Function: _const_quote
+Class: gp2c_internal
+Description:
+ (str):var       fetch_user_var($1)
+
+Function: _const_expr
+Class: gp2c_internal
+Description:
+ (str):gen       readseq($1)
+
+Function: _const_real
+Class: gp2c_internal
+Description:
+ (str):real:prec       strtor($1, prec)
+
diff --git a/src/functions/gp2c_internal/_formatcode b/src/functions/gp2c_internal/_formatcode
new file mode 100644
index 0000000..a4164fb
--- /dev/null
+++ b/src/functions/gp2c_internal/_formatcode
@@ -0,0 +1,8 @@
+Function: _formatcode
+Class: gp2c_internal
+Description:
+ (#small):void                    $1
+ (small):small                    %ld
+ (#str):void                      $%1
+ (str):str                        %s
+ (gen):gen                        %Ps
diff --git a/src/functions/gp2c_internal/_gerepileall b/src/functions/gp2c_internal/_gerepileall
new file mode 100644
index 0000000..3e0f81e
--- /dev/null
+++ b/src/functions/gp2c_internal/_gerepileall
@@ -0,0 +1,5 @@
+Function: _gerepileall
+Class: gp2c_internal
+Description:
+ (pari_sp,gen):void:parens    $2 = gerepilecopy($1, $2)
+ (pari_sp,gen,...):void       gerepileall($1, ${nbarg 1 sub}, ${stdref 3 code})
diff --git a/src/functions/gp2c_internal/_gerepileupto b/src/functions/gp2c_internal/_gerepileupto
new file mode 100644
index 0000000..b7c7812
--- /dev/null
+++ b/src/functions/gp2c_internal/_gerepileupto
@@ -0,0 +1,8 @@
+Function: _gerepileupto
+Class: gp2c_internal
+Description:
+ (pari_sp, int):int               gerepileuptoint($1, $2)
+ (pari_sp, mp):mp                 gerepileuptoleaf($1, $2)
+ (pari_sp, vecsmall):vecsmall     gerepileuptoleaf($1, $2)
+ (pari_sp, vec):vec               gerepileupto($1, $2)
+ (pari_sp, gen):gen               gerepileupto($1, $2)
diff --git a/src/functions/gp2c_internal/_maxprime b/src/functions/gp2c_internal/_maxprime
new file mode 100644
index 0000000..1b0636a
--- /dev/null
+++ b/src/functions/gp2c_internal/_maxprime
@@ -0,0 +1,4 @@
+Function: _maxprime
+Class: gp2c_internal
+Description:
+ ():small                maxprime()
diff --git a/src/functions/gp2c_internal/_stack_lim b/src/functions/gp2c_internal/_stack_lim
new file mode 100644
index 0000000..313fd33
--- /dev/null
+++ b/src/functions/gp2c_internal/_stack_lim
@@ -0,0 +1,9 @@
+Function: _stack_lim
+Class: gp2c_internal
+Description:
+ (pari_sp,small):pari_sp       stack_lim($1, $2)
+
+Function: _low_stack_lim
+Class: gp2c_internal
+Description:
+ (pari_sp,pari_sp):bool        low_stack($1, stack_lim($2, 1))
diff --git a/src/functions/gp2c_internal/_strtoclosure b/src/functions/gp2c_internal/_strtoclosure
new file mode 100644
index 0000000..ad422c4
--- /dev/null
+++ b/src/functions/gp2c_internal/_strtoclosure
@@ -0,0 +1,5 @@
+Function: _strtoclosure
+Class: gp2c_internal
+Description:
+ (str):closure               strtofunction($1)
+ (str,gen,...):closure       strtoclosure($1, ${nbarg 1 sub}, $3)
diff --git a/src/functions/gp2c_internal/_tovec b/src/functions/gp2c_internal/_tovec
new file mode 100644
index 0000000..3eab566
--- /dev/null
+++ b/src/functions/gp2c_internal/_tovec
@@ -0,0 +1,20 @@
+Function: _tovec
+Class: gp2c_internal
+Help: Create a vector holding the arguments (shallow)
+Description:
+ ():vec                  cgetg(1, t_VEC)
+ (gen):vec               mkvec($1)
+ (gen,gen):vec           mkvec2($1, $2)
+ (gen,gen,gen):vec       mkvec3($1, $2, $3)
+ (gen,gen,gen,gen):vec   mkvec4($1, $2, $3, $4)
+ (gen,...):vec           mkvecn($#, $2)
+
+Function: _tovecprec
+Class: gp2c_internal
+Help: Create a vector holding the arguments and prec (shallow)
+Description:
+ ():vec:prec             mkvecs(prec)
+ (gen):vec:prec          mkvec2($1, stoi(prec))
+ (gen,gen):vec:prec      mkvec3($1, $2, stoi(prec))
+ (gen,gen,gen):vec:prec  mkvec4($1, $2, $3, stoi(prec))
+ (gen,...):vec:prec      mkvecn(${nbarg 1 add}, $2, stoi(prec))
diff --git a/src/functions/gp2c_internal/_typedef b/src/functions/gp2c_internal/_typedef
new file mode 100644
index 0000000..2fe0e0b
--- /dev/null
+++ b/src/functions/gp2c_internal/_typedef
@@ -0,0 +1,101 @@
+Function: _decl_base
+Class: gp2c_internal
+Description:
+ (C!void)            void
+ (C!long)            long
+ (C!int)             int
+ (C!GEN)             GEN
+ (C!char*)           char
+ (C!byteptr)         byteptr
+ (C!pari_sp)         pari_sp
+ (C!func_GG)         GEN
+ (C!forprime_t)      forprime_t
+ (C!forcomposite_t)  forcomposite_t
+ (C!forpart_t)       forpart_t
+ (C!forvec_t)        forvec_t
+
+Function: _decl_ext
+Class: gp2c_internal
+Description:
+ (C!char*)         *$1
+ (C!func_GG)       (*$1)(GEN, GEN)
+
+Function: _typedef
+Class: gp2c_internal
+Description:
+ (empty)        void
+ (void)         void
+ (negbool)      long
+ (bool)         long
+ (small_int)    int
+ (small)        long
+ (int)          GEN
+ (real)         GEN
+ (mp)           GEN
+ (lg)           long
+ (vecsmall)     GEN
+ (vec)          GEN
+ (list)         GEN
+ (var)          long
+ (pol)          GEN
+ (gen)          GEN
+ (closure)      GEN
+ (error)        GEN
+ (genstr)       GEN
+ (str)          char*
+ (bptr)         byteptr
+ (forcomposite) forcomposite_t
+ (forpart)      forpart_t
+ (forprime)     forprime_t
+ (forvec)       forvec_t
+ (func_GG)      func_GG
+ (pari_sp)      pari_sp
+ (typ)          long
+ (errtyp)       long
+ (nf)           GEN
+ (bnf)          GEN
+ (bnr)          GEN
+ (ell)          GEN
+ (clgp)         GEN
+ (prid)         GEN
+ (gal)          GEN
+
+Function: _proto_ret
+Class: gp2c_internal
+Help: Code for return value of functions
+Description:
+ (C!void)       v
+ (C!int)        i
+ (C!long)       l
+ (C!GEN)
+
+Function: _proto_max_args
+Class: gp2c_internal
+Help: Max number of arguments supported by install.
+Description:
+ (20)
+
+Function: _proto_code
+Class: gp2c_internal
+Help: Code for argument of a function
+Description:
+ (var)          n
+ (C!long)       L
+ (C!GEN)        G
+ (C!char*)      s
+
+Function: _default_marker
+Class: gp2c_internal
+Help: Code for default value of GP function
+Description:
+ (C!GEN)      NULL
+ (var)        -1
+ (small)      0
+ (str)        ""
+
+Function: _default_check
+Class: gp2c_internal
+Help: Code to check for the default marker
+Description:
+ (C!GEN):bool    !$(1)
+ (var):bool      $(1) == -1
diff --git a/src/functions/gp2c_internal/_wrap b/src/functions/gp2c_internal/_wrap
new file mode 100644
index 0000000..43f9a19
--- /dev/null
+++ b/src/functions/gp2c_internal/_wrap
@@ -0,0 +1,20 @@
+Function: _wrap_G
+Class: gp2c_internal
+C-Name: gp_call
+Prototype: G
+Description:
+  (gen):gen    $1
+
+Function: _wrap_vG
+Class: gp2c_internal
+C-Name: gp_callvoid
+Prototype: lG
+Description:
+  (void):small  0
+
+Function: _wrap_bG
+Class: gp2c_internal
+C-Name: gp_callbool
+Prototype: lG
+Description:
+  (bool):bool   $1
diff --git a/src/functions/graphic/plot b/src/functions/graphic/plot
new file mode 100644
index 0000000..2bb6b3a
--- /dev/null
+++ b/src/functions/graphic/plot
@@ -0,0 +1,12 @@
+Function: plot
+Class: highlevel
+Section: graphic
+C-Name: plot
+Prototype: vV=GGEDGDGp
+Help: plot(X=a,b,expr,{Ymin},{Ymax}): crude plot of expression expr, X goes
+ from a to b, with Y ranging from Ymin to Ymax. If Ymin (resp. Ymax) is not
+ given, the minimum (resp. the maximum) of the expression is used instead.
+Doc: crude ASCII plot of the function represented by expression \var{expr}
+ from $a$ to $b$, with \var{Y} ranging from \var{Ymin} to \var{Ymax}. If
+ \var{Ymin} (resp. \var{Ymax}) is not given, the minimum (resp. the maximum)
+ of the computed values of the expression is used instead.
diff --git a/src/functions/graphic/plotbox b/src/functions/graphic/plotbox
new file mode 100644
index 0000000..23622f3
--- /dev/null
+++ b/src/functions/graphic/plotbox
@@ -0,0 +1,11 @@
+Function: plotbox
+Class: highlevel
+Section: graphic
+C-Name: rectbox
+Prototype: vLGG
+Help: plotbox(w,x2,y2): if the cursor is at position (x1,y1), draw a box
+ with diagonal (x1,y1) and (x2,y2) in rectwindow w (cursor does not move).
+Doc: let $(x1,y1)$ be the current position of the virtual cursor. Draw in the
+ rectwindow $w$ the outline of the rectangle which is such that the points
+ $(x1,y1)$ and $(x2,y2)$ are opposite corners. Only the part of the rectangle
+ which is in $w$ is drawn. The virtual cursor does \emph{not} move.
diff --git a/src/functions/graphic/plotclip b/src/functions/graphic/plotclip
new file mode 100644
index 0000000..367f25f
--- /dev/null
+++ b/src/functions/graphic/plotclip
@@ -0,0 +1,11 @@
+Function: plotclip
+Class: highlevel
+Section: graphic
+C-Name: rectclip
+Prototype: vL
+Help: plotclip(w): clip the contents of the rectwindow to the bounding box
+ (except strings).
+Doc: `clips' the content of rectwindow $w$, i.e remove all parts of the
+ drawing that would not be visible on the screen. Together with
+ \tet{plotcopy} this function enables you to draw on a scratchpad before
+ committing the part you're interested in to the final picture.
diff --git a/src/functions/graphic/plotcolor b/src/functions/graphic/plotcolor
new file mode 100644
index 0000000..3754445
--- /dev/null
+++ b/src/functions/graphic/plotcolor
@@ -0,0 +1,16 @@
+Function: plotcolor
+Class: highlevel
+Section: graphic
+C-Name: rectcolor
+Prototype: vLL
+Help: plotcolor(w,c): in rectwindow w, set default color to c. Possible
+ values for c are given by the graphcolormap default: factory settings
+ are 1=black, 2=blue, 3=sienna, 4=red, 5=green, 6=grey, 7=gainsborough.
+Doc: set default color to $c$ in rectwindow $w$.
+ This is only implemented for the X-windows, fltk and Qt graphing engines.
+ Possible values for $c$ are given by the \tet{graphcolormap} default,
+ factory setting are
+
+ 1=black, 2=blue, 3=violetred, 4=red, 5=green, 6=grey, 7=gainsborough.
+
+ but this can be considerably extended.
diff --git a/src/functions/graphic/plotcopy b/src/functions/graphic/plotcopy
new file mode 100644
index 0000000..2f32c64
--- /dev/null
+++ b/src/functions/graphic/plotcopy
@@ -0,0 +1,16 @@
+Function: plotcopy
+Class: highlevel
+Section: graphic
+C-Name: rectcopy_gen
+Prototype: vLLGGD0,L,
+Help: plotcopy(sourcew,destw,dx,dy,{flag=0}): copy the contents of
+ rectwindow sourcew to rectwindow destw with offset (dx,dy). If flag's bit 1
+ is set, dx and dy express fractions of the size of the current output
+ device, otherwise dx and dy are in pixels. dx and dy are relative positions
+ of northwest corners if other bits of flag vanish, otherwise of: 2:
+ southwest, 4: southeast, 6: northeast corners.
+Doc: copy the contents of rectwindow \var{sourcew} to rectwindow \var{destw}
+ with offset (dx,dy). If flag's bit 1 is set, dx and dy express fractions of
+ the size of the current output device, otherwise dx and dy are in pixels. dx
+ and dy are relative positions of northwest corners if other bits of flag
+ vanish, otherwise of: 2: southwest, 4: southeast, 6: northeast corners
diff --git a/src/functions/graphic/plotcursor b/src/functions/graphic/plotcursor
new file mode 100644
index 0000000..b685729
--- /dev/null
+++ b/src/functions/graphic/plotcursor
@@ -0,0 +1,8 @@
+Function: plotcursor
+Class: highlevel
+Section: graphic
+C-Name: rectcursor
+Prototype: L
+Help: plotcursor(w): current position of cursor in rectwindow w.
+Doc: give as a 2-component vector the current
+ (scaled) position of the virtual cursor corresponding to the rectwindow $w$.
diff --git a/src/functions/graphic/plotdraw b/src/functions/graphic/plotdraw
new file mode 100644
index 0000000..0026d33
--- /dev/null
+++ b/src/functions/graphic/plotdraw
@@ -0,0 +1,17 @@
+Function: plotdraw
+Class: highlevel
+Section: graphic
+C-Name: rectdraw_flag
+Prototype: vGD0,L,
+Help: plotdraw(list, {flag=0}): draw vector of rectwindows list at indicated
+ x,y positions; list is a vector w1,x1,y1,w2,x2,y2,etc. If flag!=0, x1, y1
+ etc. express fractions of the size of the current output device.
+Doc: physically draw the rectwindows given in $list$
+ which must be a vector whose number of components is divisible by 3. If
+ $list=[w1,x1,y1,w2,x2,y2,\dots]$, the windows $w1$, $w2$, etc.~are
+ physically placed with their upper left corner at physical position
+ $(x1,y1)$, $(x2,y2)$,\dots\ respectively, and are then drawn together.
+ Overlapping regions will thus be drawn twice, and the windows are considered
+ transparent. Then display the whole drawing in a special window on your
+ screen. If $\fl \neq 0$, x1, y1 etc. express fractions of the size of the
+ current output device
diff --git a/src/functions/graphic/ploth b/src/functions/graphic/ploth
new file mode 100644
index 0000000..63d4eca
--- /dev/null
+++ b/src/functions/graphic/ploth
@@ -0,0 +1,114 @@
+Function: ploth
+Class: highlevel
+Section: graphic
+C-Name: ploth
+Prototype: V=GGEpD0,M,D0,L,\nParametric|1; Recursive|2; no_Rescale|4; no_X_axis|8; no_Y_axis|16; no_Frame|32; no_Lines|64; Points_too|128; Splines|256; no_X_ticks|512; no_Y_ticks|1024; Same_ticks|2048; Complex|4096
+Help: ploth(X=a,b,expr,{flags=0},{n=0}): plot of expression expr, X goes
+ from a to b in high resolution. Both flags and n are optional. Binary digits
+ of flags mean: 1=Parametric, 2=Recursive, 4=no_Rescale, 8=no_X_axis,
+ 16=no_Y_axis, 32=no_Frame, 64=no_Lines (do not join points), 128=Points_too
+ (plot both lines and points), 256=Splines (use cubic splines),
+ 512=no_X_ticks, 1024= no_Y_ticks, 2048=Same_ticks (plot all ticks with the
+ same length), 4096=Complex (the two coordinates of each point are encoded
+ as a complex number). n specifies number of reference points on the graph
+ (0=use default value). Returns a vector for the bounding box.
+Doc: high precision plot of the function $y=f(x)$ represented by the expression
+ \var{expr}, $x$ going from $a$ to $b$. This opens a specific window (which is
+ killed whenever you click on it), and returns a four-component vector giving
+ the coordinates of the bounding box in the form
+ $[\var{xmin},\var{xmax},\var{ymin},\var{ymax}]$.
+
+ \misctitle{Important note} \kbd{ploth} may evaluate \kbd{expr} thousands of
+ times; given the relatively low resolution of plotting devices, few
+ significant digits of the result will be meaningful. Hence you should keep
+ the current precision to a minimum (e.g.~9) before calling this function.
+
+ $n$ specifies the number of reference point on the graph, where a value of 0
+ means we use the hardwired default values (1000 for general plot, 1500 for
+ parametric plot, and 8 for recursive plot).
+
+ If no $\fl$ is given, \var{expr} is either a scalar expression $f(X)$, in which
+ case the plane curve $y=f(X)$ will be drawn, or a vector
+ $[f_1(X),\dots,f_k(X)]$, and then all the curves $y=f_i(X)$ will be drawn in
+ the same window.
+
+ \noindent The binary digits of $\fl$ mean:
+
+ \item $1 = \kbd{Parametric}$: \tev{parametric plot}. Here \var{expr} must
+ be a vector with an even number of components. Successive pairs are then
+ understood as the parametric coordinates of a plane curve. Each of these are
+ then drawn.
+
+ For instance:
+ \bprog
+ ploth(X=0,2*Pi,[sin(X),cos(X)], "Parametric")
+ ploth(X=0,2*Pi,[sin(X),cos(X)])
+ ploth(X=0,2*Pi,[X,X,sin(X),cos(X)], "Parametric")
+ @eprog\noindent draw successively a circle, two entwined sinusoidal curves
+ and a circle cut by the line $y=x$.
+
+ \item $2 = \kbd{Recursive}$: \tev{recursive plot}. If this flag is set,
+ only \emph{one} curve can be drawn at a time, i.e.~\var{expr} must be either a
+ two-component vector (for a single parametric curve, and the parametric flag
+ \emph{has} to be set), or a scalar function. The idea is to choose pairs of
+ successive reference points, and if their middle point is not too far away
+ from the segment joining them, draw this as a local approximation to the
+ curve. Otherwise, add the middle point to the reference points. This is
+ fast, and usually more precise than usual plot. Compare the results of
+ \bprog
+ ploth(X=-1,1, sin(1/X), "Recursive")
+ ploth(X=-1,1, sin(1/X))
+ @eprog\noindent
+ for instance. But beware that if you are extremely unlucky, or choose too few
+ reference points, you may draw some nice polygon bearing little resemblance
+ to the original curve. For instance you should \emph{never} plot recursively
+ an odd function in a symmetric interval around 0. Try
+ \bprog
+ ploth(x = -20, 20, sin(x), "Recursive")
+ @eprog\noindent
+ to see why. Hence, it's usually a good idea to try and plot the same curve
+ with slightly different parameters.
+
+ The other values toggle various display options:
+
+ \item $4 = \kbd{no\_Rescale}$: do not rescale plot according to the
+ computed extrema. This is used in conjunction with \tet{plotscale} when
+ graphing multiple functions on a rectwindow (as a \tet{plotrecth} call):
+ \bprog
+   s = plothsizes();
+   plotinit(0, s[2]-1, s[2]-1);
+   plotscale(0, -1,1, -1,1);
+   plotrecth(0, t=0,2*Pi, [cos(t),sin(t)], "Parametric|no_Rescale")
+   plotdraw([0, -1,1]);
+ @eprog\noindent
+ This way we get a proper circle instead of the distorted ellipse produced by
+ \bprog
+   ploth(t=0,2*Pi, [cos(t),sin(t)], "Parametric")
+ @eprog
+
+ \item $8 = \kbd{no\_X\_axis}$: do not print the $x$-axis.
+
+ \item $16 = \kbd{no\_Y\_axis}$: do not print the $y$-axis.
+
+ \item $32 = \kbd{no\_Frame}$: do not print frame.
+
+ \item $64 = \kbd{no\_Lines}$: only plot reference points, do not join them.
+
+ \item $128 = \kbd{Points\_too}$: plot both lines and points.
+
+ \item $256 = \kbd{Splines}$: use splines to interpolate the points.
+
+ \item $512 = \kbd{no\_X\_ticks}$: plot no $x$-ticks.
+
+ \item $1024 = \kbd{no\_Y\_ticks}$: plot no $y$-ticks.
+
+ \item $2048 = \kbd{Same\_ticks}$: plot all ticks with the same length.
+
+ \item $4096 = \kbd{Complex}$: is a parametric plot but where each member of
+ \kbd{expr} is considered a complex number encoding the two coordinates of a
+ point. For instance:
+ \bprog
+ ploth(X=0,2*Pi,exp(I*X), "Complex")
+ ploth(X=0,2*Pi,[(1+I)*X,exp(I*X)], "Complex")
+ @eprog\noindent will draw respectively a circle and a circle cut by the line
+ $y=x$.
diff --git a/src/functions/graphic/plothraw b/src/functions/graphic/plothraw
new file mode 100644
index 0000000..03ceccd
--- /dev/null
+++ b/src/functions/graphic/plothraw
@@ -0,0 +1,15 @@
+Function: plothraw
+Class: highlevel
+Section: graphic
+C-Name: plothraw
+Prototype: GGD0,L,
+Help: plothraw(listx,listy,{flag=0}): plot in high resolution points whose x
+ (resp. y) coordinates are in listx (resp. listy). If flag is 1, join points,
+ other non-0 flags should be combinations of bits 8,16,32,64,128,256 meaning
+ the same as for ploth().
+Doc: given \var{listx} and \var{listy} two vectors of equal length, plots (in
+ high precision) the points whose $(x,y)$-coordinates are given in
+ \var{listx} and \var{listy}. Automatic positioning and scaling is done, but
+ with the same scaling factor on $x$ and $y$. If $\fl$ is 1, join points,
+ other non-0 flags toggle display options and should be combinations of bits
+ $2^k$, $k \geq 3$ as in \kbd{ploth}.
diff --git a/src/functions/graphic/plothsizes b/src/functions/graphic/plothsizes
new file mode 100644
index 0000000..8d96047
--- /dev/null
+++ b/src/functions/graphic/plothsizes
@@ -0,0 +1,16 @@
+Function: plothsizes
+Class: highlevel
+Section: graphic
+C-Name: plothsizes_flag
+Prototype: D0,L,
+Help: plothsizes({flag=0}): returns array of 6 elements: terminal width and
+ height, sizes for ticks in horizontal and vertical directions, width and
+ height of characters. If flag=0, sizes of ticks and characters are in
+ pixels, otherwise are fractions of the screen size.
+Doc: return data corresponding to the output window
+ in the form of a 6-component vector: window width and height, sizes for ticks
+ in horizontal and vertical directions (this is intended for the \kbd{gnuplot}
+ interface and is currently not significant), width and height of characters.
+
+ If $\fl = 0$, sizes of ticks and characters are in
+ pixels, otherwise are fractions of the screen size
diff --git a/src/functions/graphic/plotinit b/src/functions/graphic/plotinit
new file mode 100644
index 0000000..0c6c709
--- /dev/null
+++ b/src/functions/graphic/plotinit
@@ -0,0 +1,28 @@
+Function: plotinit
+Class: highlevel
+Section: graphic
+C-Name: initrect_gen
+Prototype: vLDGDGD0,L,
+Help: plotinit(w,{x},{y},{flag=0}): initialize rectwindow w to size x,y.
+ If flag!=0, x and y express fractions of the size of the current output
+ device. Omitting x or y means use the full size of the device.
+Doc: initialize the rectwindow $w$,
+ destroying any rect objects you may have already drawn in $w$. The virtual
+ cursor is set to $(0,0)$. The rectwindow size is set to width $x$ and height
+ $y$; omitting either $x$ or $y$ means we use the full size of the device
+ in that direction.
+ If $\fl=0$, $x$ and $y$ represent pixel units. Otherwise, $x$ and $y$
+ are understood as fractions of the size of the current output device (hence
+ must be between $0$ and $1$) and internally converted to pixels.
+
+ The plotting device imposes an upper bound for $x$ and $y$, for instance the
+ number of pixels for screen output. These bounds are available through the
+ \tet{plothsizes} function. The following sequence initializes in a portable
+ way (i.e independent of the output device) a window of maximal size, accessed
+ through coordinates in the $[0,1000] \times [0,1000]$ range:
+
+ \bprog
+ s = plothsizes();
+ plotinit(0, s[1]-1, s[2]-1);
+ plotscale(0, 0,1000, 0,1000);
+ @eprog
diff --git a/src/functions/graphic/plotkill b/src/functions/graphic/plotkill
new file mode 100644
index 0000000..182b53b
--- /dev/null
+++ b/src/functions/graphic/plotkill
@@ -0,0 +1,10 @@
+Function: plotkill
+Class: highlevel
+Section: graphic
+C-Name: killrect
+Prototype: vL
+Help: plotkill(w): erase the rectwindow w.
+Doc: erase rectwindow $w$ and free the corresponding memory. Note that if you
+ want to use the rectwindow $w$ again, you have to use \kbd{plotinit} first
+ to specify the new size. So it's better in this case to use \kbd{plotinit}
+ directly as this throws away any previous work in the given rectwindow.
diff --git a/src/functions/graphic/plotlines b/src/functions/graphic/plotlines
new file mode 100644
index 0000000..903b50c
--- /dev/null
+++ b/src/functions/graphic/plotlines
@@ -0,0 +1,25 @@
+Function: plotlines
+Class: highlevel
+Section: graphic
+C-Name: rectlines
+Prototype: vLGGD0,L,
+Help: plotlines(w,X,Y,{flag=0}): draws an open polygon in rectwindow
+ w where X and Y contain the x (resp. y) coordinates of the vertices.
+ If X and Y are both single values (i.e not vectors), draw the
+ corresponding line (and move cursor). If (optional) flag is non-zero, close
+ the polygon.
+Doc: draw on the rectwindow $w$
+ the polygon such that the (x,y)-coordinates of the vertices are in the
+ vectors of equal length $X$ and $Y$. For simplicity, the whole
+ polygon is drawn, not only the part of the polygon which is inside the
+ rectwindow. If $\fl$ is non-zero, close the polygon. In any case, the
+ virtual cursor does not move.
+
+ $X$ and $Y$ are allowed to be scalars (in this case, both have to).
+ There, a single segment will be drawn, between the virtual cursor current
+ position and the point $(X,Y)$. And only the part thereof which
+ actually lies within the boundary of $w$. Then \emph{move} the virtual cursor
+ to $(X,Y)$, even if it is outside the window. If you want to draw a
+ line from $(x1,y1)$ to $(x2,y2)$ where $(x1,y1)$ is not necessarily the
+ position of the virtual cursor, use \kbd{plotmove(w,x1,y1)} before using this
+ function.
diff --git a/src/functions/graphic/plotlinetype b/src/functions/graphic/plotlinetype
new file mode 100644
index 0000000..d7bb565
--- /dev/null
+++ b/src/functions/graphic/plotlinetype
@@ -0,0 +1,12 @@
+Function: plotlinetype
+Class: highlevel
+Section: graphic
+C-Name: rectlinetype
+Prototype: vLL
+Help: plotlinetype(w,type): change the type of following lines in rectwindow
+ w. type -2 corresponds to frames, -1 to axes, larger values may correspond
+ to something else. w=-1 changes highlevel plotting.
+Doc: change the type of lines subsequently plotted in rectwindow $w$.
+ \var{type} $-2$ corresponds to frames, $-1$ to axes, larger values may
+ correspond to something else. $w = -1$ changes highlevel plotting. This is
+ only taken into account by the \kbd{gnuplot} interface.
diff --git a/src/functions/graphic/plotmove b/src/functions/graphic/plotmove
new file mode 100644
index 0000000..6bdd6a6
--- /dev/null
+++ b/src/functions/graphic/plotmove
@@ -0,0 +1,7 @@
+Function: plotmove
+Class: highlevel
+Section: graphic
+C-Name: rectmove
+Prototype: vLGG
+Help: plotmove(w,x,y): move cursor to position x,y in rectwindow w.
+Doc: move the virtual cursor of the rectwindow $w$ to position $(x,y)$.
diff --git a/src/functions/graphic/plotpoints b/src/functions/graphic/plotpoints
new file mode 100644
index 0000000..cc6f01d
--- /dev/null
+++ b/src/functions/graphic/plotpoints
@@ -0,0 +1,19 @@
+Function: plotpoints
+Class: highlevel
+Section: graphic
+C-Name: rectpoints
+Prototype: vLGG
+Help: plotpoints(w,X,Y): draws in rectwindow w the points whose x
+ (resp y) coordinates are in X (resp Y). If X and Y are both
+ single values (i.e not vectors), draw the corresponding point (and move
+ cursor).
+Doc: draw on the rectwindow $w$ the
+ points whose $(x,y)$-coordinates are in the vectors of equal length $X$ and
+ $Y$ and which are inside $w$. The virtual cursor does \emph{not} move. This
+ is basically the same function as \kbd{plothraw}, but either with no scaling
+ factor or with a scale chosen using the function \kbd{plotscale}.
+
+ As was the case with the \kbd{plotlines} function, $X$ and $Y$ are allowed to
+ be (simultaneously) scalar. In this case, draw the single point $(X,Y)$ on
+ the rectwindow $w$ (if it is actually inside $w$), and in any case
+ \emph{move} the virtual cursor to position $(x,y)$.
diff --git a/src/functions/graphic/plotpointsize b/src/functions/graphic/plotpointsize
new file mode 100644
index 0000000..c280f4c
--- /dev/null
+++ b/src/functions/graphic/plotpointsize
@@ -0,0 +1,9 @@
+Function: plotpointsize
+Class: highlevel
+Section: graphic
+C-Name: rectpointsize
+Prototype: vLG
+Help: plotpointsize(w,size): change the "size" of following points in
+ rectwindow w. w=-1 changes global value.
+Doc: changes the ``size'' of following points in rectwindow $w$. If $w = -1$,
+ change it in all rectwindows. This only works in the \kbd{gnuplot} interface.
diff --git a/src/functions/graphic/plotpointtype b/src/functions/graphic/plotpointtype
new file mode 100644
index 0000000..f9ce1e3
--- /dev/null
+++ b/src/functions/graphic/plotpointtype
@@ -0,0 +1,12 @@
+Function: plotpointtype
+Class: highlevel
+Section: graphic
+C-Name: rectpointtype
+Prototype: vLL
+Help: plotpointtype(w,type): change the type of following points in
+ rectwindow w. type -1 corresponds to a dot, larger values may correspond to
+ something else. w=-1 changes highlevel plotting.
+Doc: change the type of points subsequently plotted in rectwindow $w$.
+ $\var{type} = -1$ corresponds to a dot, larger values may correspond to
+ something else. $w = -1$ changes highlevel plotting. This is only taken into
+ account by the \kbd{gnuplot} interface.
diff --git a/src/functions/graphic/plotrbox b/src/functions/graphic/plotrbox
new file mode 100644
index 0000000..503695e
--- /dev/null
+++ b/src/functions/graphic/plotrbox
@@ -0,0 +1,11 @@
+Function: plotrbox
+Class: highlevel
+Section: graphic
+C-Name: rectrbox
+Prototype: vLGG
+Help: plotrbox(w,dx,dy): if the cursor is at (x1,y1), draw a box with
+ diagonal (x1,y1)-(x1+dx,y1+dy) in rectwindow w (cursor does not move).
+Doc: draw in the rectwindow $w$ the outline of the rectangle which is such
+ that the points $(x1,y1)$ and $(x1+dx,y1+dy)$ are opposite corners, where
+ $(x1,y1)$ is the current position of the cursor. Only the part of the
+ rectangle which is in $w$ is drawn. The virtual cursor does \emph{not} move.
diff --git a/src/functions/graphic/plotrecth b/src/functions/graphic/plotrecth
new file mode 100644
index 0000000..dcb1528
--- /dev/null
+++ b/src/functions/graphic/plotrecth
@@ -0,0 +1,10 @@
+Function: plotrecth
+Class: highlevel
+Section: graphic
+C-Name: rectploth
+Prototype: LV=GGEpD0,M,D0,L,\nParametric|1; Recursive|2; no_Rescale|4; no_X_axis|8; no_Y_axis|16; no_Frame|32; no_Lines|64; Points_too|128; Splines|256; no_X_ticks|512; no_Y_ticks|1024; Same_ticks|2048; Complex|4096
+Help: plotrecth(w,X=a,b,expr,{flag=0},{n=0}):
+ writes to rectwindow w the curve output of
+ ploth(w,X=a,b,expr,flag,n). Returns a vector for the bounding box.
+Doc: writes to rectwindow $w$ the curve output of
+ \kbd{ploth}$(w,X=a,b,\var{expr},\fl,n)$. Returns a vector for the bounding box.
diff --git a/src/functions/graphic/plotrecthraw b/src/functions/graphic/plotrecthraw
new file mode 100644
index 0000000..305456c
--- /dev/null
+++ b/src/functions/graphic/plotrecthraw
@@ -0,0 +1,19 @@
+Function: plotrecthraw
+Class: highlevel
+Section: graphic
+C-Name: rectplothraw
+Prototype: LGD0,L,
+Help: plotrecthraw(w,data,{flags=0}): plot graph(s) for data in rectwindow
+ w, where data is a vector of vectors. If plot is parametric, length of data
+ should be even, and pairs of entries give curves to plot. If not, first
+ entry gives x-coordinate, and the other ones y-coordinates. Admits the same
+ optional flags as plotrecth, save that recursive plot is meaningless.
+Doc: plot graph(s) for
+ \var{data} in rectwindow $w$. $\fl$ has the same significance here as in
+ \kbd{ploth}, though recursive plot is no more significant.
+
+ \var{data} is a vector of vectors, each corresponding to a list a coordinates.
+ If parametric plot is set, there must be an even number of vectors, each
+ successive pair corresponding to a curve. Otherwise, the first one contains
+ the $x$ coordinates, and the other ones contain the $y$-coordinates
+ of curves to plot.
diff --git a/src/functions/graphic/plotrline b/src/functions/graphic/plotrline
new file mode 100644
index 0000000..0720718
--- /dev/null
+++ b/src/functions/graphic/plotrline
@@ -0,0 +1,11 @@
+Function: plotrline
+Class: highlevel
+Section: graphic
+C-Name: rectrline
+Prototype: vLGG
+Help: plotrline(w,dx,dy): if the cursor is at (x1,y1), draw a line from
+ (x1,y1) to (x1+dx,y1+dy) (and move the cursor) in the rectwindow w.
+Doc: draw in the rectwindow $w$ the part of the segment
+ $(x1,y1)-(x1+dx,y1+dy)$ which is inside $w$, where $(x1,y1)$ is the current
+ position of the virtual cursor, and move the virtual cursor to
+ $(x1+dx,y1+dy)$ (even if it is outside the window).
diff --git a/src/functions/graphic/plotrmove b/src/functions/graphic/plotrmove
new file mode 100644
index 0000000..b83c493
--- /dev/null
+++ b/src/functions/graphic/plotrmove
@@ -0,0 +1,10 @@
+Function: plotrmove
+Class: highlevel
+Section: graphic
+C-Name: rectrmove
+Prototype: vLGG
+Help: plotrmove(w,dx,dy): move cursor to position (dx,dy) relative to the
+ present position in the rectwindow w.
+Doc: move the virtual cursor of the rectwindow $w$ to position
+ $(x1+dx,y1+dy)$, where $(x1,y1)$ is the initial position of the cursor
+ (i.e.~to position $(dx,dy)$ relative to the initial cursor).
diff --git a/src/functions/graphic/plotrpoint b/src/functions/graphic/plotrpoint
new file mode 100644
index 0000000..37648ff
--- /dev/null
+++ b/src/functions/graphic/plotrpoint
@@ -0,0 +1,10 @@
+Function: plotrpoint
+Class: highlevel
+Section: graphic
+C-Name: rectrpoint
+Prototype: vLGG
+Help: plotrpoint(w,dx,dy): draw a point (and move cursor) at position dx,dy
+ relative to present position of the cursor in rectwindow w.
+Doc: draw the point $(x1+dx,y1+dy)$ on the rectwindow $w$ (if it is inside
+ $w$), where $(x1,y1)$ is the current position of the cursor, and in any case
+ move the virtual cursor to position $(x1+dx,y1+dy)$.
diff --git a/src/functions/graphic/plotscale b/src/functions/graphic/plotscale
new file mode 100644
index 0000000..3c81e3d
--- /dev/null
+++ b/src/functions/graphic/plotscale
@@ -0,0 +1,14 @@
+Function: plotscale
+Class: highlevel
+Section: graphic
+C-Name: rectscale
+Prototype: vLGGGG
+Help: plotscale(w,x1,x2,y1,y2): scale the coordinates in rectwindow w so
+ that x goes from x1 to x2 and y from y1 to y2 (y2<y1 is allowed).
+Doc: scale the local coordinates of the rectwindow $w$ so that $x$ goes from
+ $x1$ to $x2$ and $y$ goes from $y1$ to $y2$ ($x2<x1$ and $y2<y1$ being
+ allowed). Initially, after the initialization of the rectwindow $w$ using
+ the function \kbd{plotinit}, the default scaling is the graphic pixel count,
+ and in particular the $y$ axis is oriented downwards since the origin is at
+ the upper left. The function \kbd{plotscale} allows to change all these
+ defaults and should be used whenever functions are graphed.
diff --git a/src/functions/graphic/plotstring b/src/functions/graphic/plotstring
new file mode 100644
index 0000000..54792dd
--- /dev/null
+++ b/src/functions/graphic/plotstring
@@ -0,0 +1,18 @@
+Function: plotstring
+Class: highlevel
+Section: graphic
+C-Name: rectstring3
+Prototype: vLsD0,L,
+Help: plotstring(w,x,{flags=0}): draw in rectwindow w the string
+ corresponding to x. Bits 1 and 2 of flag regulate horizontal alignment: left
+ if 0, right if 2, center if 1. Bits 4 and 8 regulate vertical alignment:
+ bottom if 0, top if 8, v-center if 4. Can insert additional gap between
+ point and string: horizontal if bit 16 is set, vertical if bit 32 is set.
+Doc: draw on the rectwindow $w$ the String $x$ (see \secref{se:strings}), at
+ the current position of the cursor.
+
+ \fl\ is used for justification: bits 1 and 2 regulate horizontal alignment:
+ left if 0, right if 2, center if 1. Bits 4 and 8 regulate vertical
+ alignment: bottom if 0, top if 8, v-center if 4. Can insert additional small
+ gap between point and string: horizontal if bit 16 is set, vertical if bit
+ 32 is set (see the tutorial for an example).
diff --git a/src/functions/graphic/psdraw b/src/functions/graphic/psdraw
new file mode 100644
index 0000000..5d3f937
--- /dev/null
+++ b/src/functions/graphic/psdraw
@@ -0,0 +1,12 @@
+Function: psdraw
+Class: highlevel
+Section: graphic
+C-Name: postdraw_flag
+Prototype: vGD0,L,
+Help: psdraw(list, {flag=0}): same as plotdraw, except that the output is a
+ PostScript program in psfile (pari.ps by default), and flag!=0 scales the
+ plot from size of the current output device to the standard PostScript
+ plotting size.
+Doc: same as \kbd{plotdraw}, except that the output is a PostScript program
+ appended to the \kbd{psfile}, and flag!=0 scales the plot from size of the
+ current output device to the standard PostScript plotting size
diff --git a/src/functions/graphic/psploth b/src/functions/graphic/psploth
new file mode 100644
index 0000000..0911e26
--- /dev/null
+++ b/src/functions/graphic/psploth
@@ -0,0 +1,9 @@
+Function: psploth
+Class: highlevel
+Section: graphic
+C-Name: postploth
+Prototype: V=GGEpD0,L,D0,L,
+Help: psploth(X=a,b,expr,{flags=0},{n=0}): same as ploth, except that the
+ output is a PostScript program in psfile (pari.ps by default).
+Doc: same as \kbd{ploth}, except that the output is a PostScript program
+ appended to the \kbd{psfile}.
diff --git a/src/functions/graphic/psplothraw b/src/functions/graphic/psplothraw
new file mode 100644
index 0000000..e9b153d
--- /dev/null
+++ b/src/functions/graphic/psplothraw
@@ -0,0 +1,9 @@
+Function: psplothraw
+Class: highlevel
+Section: graphic
+C-Name: postplothraw
+Prototype: GGD0,L,
+Help: psplothraw(listx,listy,{flag=0}): same as plothraw, except that the
+ output is a postscript program in psfile (pari.ps by default).
+Doc: same as \kbd{plothraw}, except that the output is a PostScript program
+ appended to the \kbd{psfile}.
diff --git a/src/functions/linear_algebra/algdep b/src/functions/linear_algebra/algdep
new file mode 100644
index 0000000..3b3bb57
--- /dev/null
+++ b/src/functions/linear_algebra/algdep
@@ -0,0 +1,49 @@
+Function: algdep
+Section: linear_algebra
+C-Name: algdep0
+Prototype: GLD0,L,
+Help: algdep(x,k,{flag=0}): algebraic relations up to degree n of x, using
+ lindep([1,x,...,x^(k-1)], flag).
+Doc: \sidx{algebraic dependence}
+ $x$ being real/complex, or $p$-adic, finds a polynomial of degree at most
+ $k$ with integer coefficients having $x$ as approximate root. Note that the
+ polynomial which is obtained is not necessarily the ``correct'' one. In fact
+ it is not even guaranteed to be irreducible. One can check the closeness
+ either by a polynomial evaluation (use \tet{subst}), or by computing the
+ roots of the polynomial given by \kbd{algdep} (use \tet{polroots}).
+
+ Internally, \tet{lindep}$([1,x,\ldots,x^k], \fl)$ is used.
+ A non-zero value of $\fl$ may improve on the default behavior
+ if the input number is known to a \emph{huge} accuracy, and you suspect the
+ last bits are incorrect  (this truncates the number, throwing away the least
+ significant bits), but default values are usually sufficient:
+ \bprog
+ ? \p200
+ ? algdep(2^(1/6)+3^(1/5), 30);      \\ wrong in 0.8s
+ ? algdep(2^(1/6)+3^(1/5), 30, 100); \\ wrong in 0.4s
+ ? algdep(2^(1/6)+3^(1/5), 30, 170); \\ right in 0.8s
+ ? algdep(2^(1/6)+3^(1/5), 30, 200); \\ wrong in 1.0s
+ ? \p250
+ ? algdep(2^(1/6)+3^(1/5), 30);      \\ right in 1.0s
+ ? algdep(2^(1/6)+3^(1/5), 30, 200); \\ right in 1.0s
+ ? \p500
+ ? algdep(2^(1/6)+3^(1/5), 30);      \\ right in 2.9s
+ ? \p1000
+ ? algdep(2^(1/6)+3^(1/5), 30);      \\ right in 10.6s
+ @eprog\noindent
+ The changes in \kbd{defaultprecision} only affect the quality of the
+ initial approximation to $2^{1/6} + 3^{1/5}$, \kbd{algdep} itself uses
+ exact operations (the size of its operands depend on the accuracy of the
+ input of course: more accurate input means slower operations).
+
+ Proceeding by increments of 5 digits of accuracy, \kbd{algdep} with default
+ flag produces its first correct result at 205 digits, and from then on a
+ steady stream of correct results.
+
+ The above example is the test case studied in a 2000 paper by Borwein and
+ Lisonek: Applications of integer relation algorithms, \emph{Discrete Math.},
+ {\bf 217}, p.~65--82. The version of PARI tested there was 1.39, which
+ succeeded reliably from precision 265 on, in about 200 as much time as the
+ current version.
+
+Variant: Also available is \fun{GEN}{algdep}{GEN x, long k} ($\fl=0$).
diff --git a/src/functions/linear_algebra/charpoly b/src/functions/linear_algebra/charpoly
new file mode 100644
index 0000000..9742ce7
--- /dev/null
+++ b/src/functions/linear_algebra/charpoly
@@ -0,0 +1,68 @@
+Function: charpoly
+Section: linear_algebra
+C-Name: charpoly0
+Prototype: GDnD5,L,
+Help: charpoly(A,{v='x},{flag=5}): det(v*Id-A)=characteristic polynomial of
+ the matrix or polmod A. flag is optional and ignored unless A is a matrix;
+ it may be set to 0 (Le Verrier), 1 (Lagrange interpolation),
+ 2 (Hessenberg form), 3 (Berkowitz), 4 (modular) if A is integral,
+ or 5 (default, choose best method).
+ Algorithms 0 (Le Verrier) and 1 (Lagrange) assume that n! is invertible,
+ where n is the dimension of the matrix.
+Doc:
+ \idx{characteristic polynomial}
+ of $A$ with respect to the variable $v$, i.e.~determinant of $v*I-A$ if $A$
+ is a square matrix.
+ \bprog
+ ? charpoly([1,2;3,4]);
+ %1 = x^2 - 5*x - 2
+ ? charpoly([1,2;3,4],, 't)
+ %2 = t^2 - 5*t - 2
+ @eprog\noindent
+ If $A$ is not a square matrix, the function returns the characteristic
+ polynomial of the map ``multiplication by $A$'' if $A$ is a scalar:
+ \bprog
+ ? charpoly(Mod(x+2, x^3-2))
+ %1 = x^3 - 6*x^2 + 12*x - 10
+ ? charpoly(I)
+ %2 = x^2 + 1
+ ? charpoly(quadgen(5))
+ %3 = x^2 - x - 1
+ ? charpoly(ffgen(ffinit(2,4)))
+ %4 = Mod(1, 2)*x^4 + Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(1, 2)*x + Mod(1, 2)
+ @eprog
+
+ The value of $\fl$ is only significant for matrices, and we advise to stick
+ to the default value. Let $n$ be the dimension of $A$.
+
+ If $\fl=0$, same method (Le Verrier's) as for computing the adjoint matrix,
+ i.e.~using the traces of the powers of $A$. Assumes that $n!$ is
+ invertible; uses $O(n^4)$ scalar operations.
+
+ If $\fl=1$, uses Lagrange interpolation which is usually the slowest method.
+ Assumes that $n!$ is invertible; uses $O(n^4)$ scalar operations.
+
+ If $\fl=2$, uses the Hessenberg form. Assumes that the base ring is a field.
+ Uses $O(n^3)$ scalar operations, but suffers from coefficient explosion
+ unless the base field is finite or $\R$.
+
+ If $\fl=3$, uses Berkowitz's division free algorithm, valid over any
+ ring (commutative, with unit). Uses $O(n^4)$ scalar operations.
+
+ If $\fl=4$, $x$ must be integral. Uses a modular algorithm: Hessenberg form
+ for various small primes, then Chinese remainders.
+
+ If $\fl=5$ (default), uses the ``best'' method given $x$.
+ This means we use Berkowitz unless the base ring is $\Z$ (use $\fl=4$)
+ or a field where coefficient explosion does not occur,
+ e.g.~a finite field or the reals (use $\fl=2$).
+
+Variant: Also available are
+ \fun{GEN}{charpoly}{GEN x, long v} ($\fl=5$),
+ \fun{GEN}{caract}{GEN A, long v} ($\fl=1$),
+ \fun{GEN}{carhess}{GEN A, long v} ($\fl=2$),
+ \fun{GEN}{carberkowitz}{GEN A, long v} ($\fl=3$) and
+ \fun{GEN}{caradj}{GEN A, long v, GEN *pt}. In this
+ last case, if \var{pt} is not \kbd{NULL}, \kbd{*pt} receives the address of
+ the adjoint matrix of $A$ (see \tet{matadjoint}), so both can be obtained at
+ once.
diff --git a/src/functions/linear_algebra/concat b/src/functions/linear_algebra/concat
new file mode 100644
index 0000000..54a93a2
--- /dev/null
+++ b/src/functions/linear_algebra/concat
@@ -0,0 +1,101 @@
+Function: concat
+Section: linear_algebra
+C-Name: concat
+Prototype: GDG
+Help: concat(x,{y}): concatenation of x and y, which can be scalars, vectors
+ or matrices, or lists (in this last case, both x and y have to be lists). If
+ y is omitted, x has to be a list or row vector and its elements are
+ concatenated.
+Description:
+ (mp,mp):vec           concat($1, $2)
+ (vec,mp):vec          concat($1, $2)
+ (mp,vec):vec          concat($1, $2)
+ (vec,vec):vec         concat($1, $2)
+ (list,list):list      concat($1, $2)
+ (genstr,gen):genstr   concat($1, $2)
+ (gen,genstr):genstr   concat($1, $2)
+ (gen,?gen):gen        concat($1, $2)
+Doc: concatenation of $x$ and $y$. If $x$ or $y$ is
+ not a vector or matrix, it is considered as a one-dimensional vector. All
+ types are allowed for $x$ and $y$, but the sizes must be compatible. Note
+ that matrices are concatenated horizontally, i.e.~the number of rows stays
+ the same. Using transpositions, one can concatenate them vertically,
+ but it is often simpler to use \tet{matconcat}.
+ \bprog
+ ? x = matid(2); y = 2*matid(2);
+ ? concat(x,y)
+ %2 =
+ [1 0 2 0]
+
+ [0 1 0 2]
+ ? concat(x~,y~)~
+ %3 =
+ [1 0]
+
+ [0 1]
+
+ [2 0]
+
+ [0 2]
+ ? matconcat([x;y])
+ %4 =
+ [1 0]
+
+ [0 1]
+
+ [2 0]
+
+ [0 2]
+ @eprog\noindent
+ To concatenate vectors sideways (i.e.~to obtain a two-row or two-column
+ matrix), use \tet{Mat} instead, or \tet{matconcat}:
+ \bprog
+ ? x = [1,2];
+ ? y = [3,4];
+ ? concat(x,y)
+ %3 = [1, 2, 3, 4]
+
+ ? Mat([x,y]~)
+ %4 =
+ [1 2]
+
+ [3 4]
+ ? matconcat([x;y])
+ %5 =
+ [1 2]
+
+ [3 4]
+ @eprog
+ Concatenating a row vector to a matrix having the same number of columns will
+ add the row to the matrix (top row if the vector is $x$, i.e.~comes first, and
+ bottom row otherwise).
+
+ The empty matrix \kbd{[;]} is considered to have a number of rows compatible
+ with any operation, in particular concatenation. (Note that this is
+ \emph{not} the case for empty vectors \kbd{[~]} or \kbd{[~]\til}.)
+
+ If $y$ is omitted, $x$ has to be a row vector or a list, in which case its
+ elements are concatenated, from left to right, using the above rules.
+ \bprog
+ ? concat([1,2], [3,4])
+ %1 = [1, 2, 3, 4]
+ ? a = [[1,2]~, [3,4]~]; concat(a)
+ %2 =
+ [1 3]
+
+ [2 4]
+
+ ? concat([1,2; 3,4], [5,6]~)
+ %3 =
+ [1 2 5]
+
+ [3 4 6]
+ ? concat([%, [7,8]~, [1,2,3,4]])
+ %5 =
+ [1 2 5 7]
+
+ [3 4 6 8]
+
+ [1 2 3 4]
+ @eprog
+Variant: \fun{GEN}{concat1}{GEN x} is a shortcut for \kbd{concat(x,NULL)}.
diff --git a/src/functions/linear_algebra/forqfvec b/src/functions/linear_algebra/forqfvec
new file mode 100644
index 0000000..888c22e
--- /dev/null
+++ b/src/functions/linear_algebra/forqfvec
@@ -0,0 +1,22 @@
+Function: forqfvec
+Section: linear_algebra
+C-Name: forqfvec0
+Prototype: vVGDGI
+Help:forqfvec(v,q,b,expr): q being a square and symmetric matrix
+ representing a positive definite quadratic form, evaluate expr for all
+ vector v such that q(v)<=b.
+Doc: $q$ being a square and symmetric matrix representing a positive definite
+ quadratic form, evaluate \kbd{expr} for all vector $v$ such that $q(v)\leq b$.
+ The formal variable $v$ runs through all such vectors in turn.
+ \bprog
+ ? forqfvec(v, [3,2;2,3], 3, print(v))
+ [0, 1]~
+ [1, 0]~
+ [-1, 1]~
+ @eprog
+Variant: The following function is also available:
+ \fun{void}{forqfvec}{void *E, long (*fun)(void *, GEN, double), GEN q, GEN b}:
+ Evaluate \kbd{fun(E,v,m)} on all $v$ such that $q(v)<b$, where $v$ is a
+ \typ{VECSMALL} and $m=q(v)$ is a C double. The function \kbd{fun} must
+ return $0$, unless \kbd{forqfvec} should stop, in which case, it should
+ return $1$.
diff --git a/src/functions/linear_algebra/lindep b/src/functions/linear_algebra/lindep
new file mode 100644
index 0000000..81cc794
--- /dev/null
+++ b/src/functions/linear_algebra/lindep
@@ -0,0 +1,62 @@
+Function: lindep
+Section: linear_algebra
+C-Name: lindep0
+Prototype: GD0,L,
+Help: lindep(v,{flag=0}): integral linear dependencies between components of v.
+ flag is optional, and can be 0: default, guess a suitable
+ accuracy, or positive: accuracy to use for the computation, in decimal
+ digits.
+Doc: \sidx{linear dependence} finds a small non-trivial integral linear
+ combination between components of $v$. If none can be found return an empty
+ vector.
+
+ If $v$ is a vector with real/complex entries we use a floating point
+ (variable precision) LLL algorithm. If $\fl = 0$ the accuracy is chosen
+ internally using a crude heuristic. If $\fl > 0$ the computation is done with
+ an accuracy of $\fl$ decimal digits. To get meaningful results in the latter
+ case, the parameter $\fl$ should be smaller than the number of correct
+ decimal digits in the input.
+
+ \bprog
+ ? lindep([sqrt(2), sqrt(3), sqrt(2)+sqrt(3)])
+ %1 = [-1, -1, 1]~
+ @eprog
+
+ If $v$ is $p$-adic, $\fl$ is ignored and the algorithm LLL-reduces a
+ suitable (dual) lattice.
+ \bprog
+ ? lindep([1, 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5)])
+ %2 = [1, -2]~
+ @eprog
+
+ If $v$ is a matrix, $\fl$ is ignored and the function returns a non trivial
+ kernel vector (combination of the columns).
+ \bprog
+ ? lindep([1,2,3;4,5,6;7,8,9])
+ %3 = [1, -2, 1]~
+ @eprog
+
+ If $v$ contains polynomials or power series over some base field, finds a
+ linear relation with coefficients in the field.
+ \bprog
+ ? lindep([x*y, x^2 + y, x^2*y + x*y^2, 1])
+ %4 = [y, y, -1, -y^2]~
+ @eprog\noindent For better control, it is preferable to use \typ{POL} rather
+ than \typ{SER} in the input, otherwise one gets a linear combination which is
+ $t$-adically small, but not necessarily $0$. Indeed, power series are first
+ converted to the minimal absolute accuracy occurring among the entries of $v$
+ (which can cause some coefficients to be ignored), then truncated to
+ polynomials:
+ \bprog
+ ? v = [t^2+O(t^4), 1+O(t^2)]; L=lindep(v)
+ %1 = [1, 0]~
+ ? v*L
+ %2 = t^2+O(t^4)  \\ small but not 0
+ @eprog
+
+Variant: Also available are \fun{GEN}{lindep}{GEN v} (real/complex entries,
+ $\fl=0$), \fun{GEN}{lindep2}{GEN v, long flag} (real/complex entries)
+ \fun{GEN}{padic_lindep}{GEN v} ($p$-adic entries) and
+ \fun{GEN}{Xadic_lindep}{GEN v} (polynomial entries).
+ Finally \fun{GEN}{deplin}{GEN v} returns a non-zero kernel vector for a
+ \typ{MAT} input.
diff --git a/src/functions/linear_algebra/listcreate b/src/functions/linear_algebra/listcreate
new file mode 100644
index 0000000..e5868a4
--- /dev/null
+++ b/src/functions/linear_algebra/listcreate
@@ -0,0 +1,12 @@
+Function: listcreate
+Section: linear_algebra
+C-Name: listcreate
+Prototype: D0,L,
+Help: listcreate(): creates an empty list.
+Description:
+ (?gen):list        listcreate()
+Doc: creates an empty list. This routine used to have a mandatory argument,
+ which is now ignored (for backward compatibility). In fact, this function
+ has become redundant and obsolete; it will disappear in future versions of
+ PARI: just use \kbd{List()}
+ % \syn{NO}
diff --git a/src/functions/linear_algebra/listinsert b/src/functions/linear_algebra/listinsert
new file mode 100644
index 0000000..971f4fc
--- /dev/null
+++ b/src/functions/linear_algebra/listinsert
@@ -0,0 +1,13 @@
+Function: listinsert
+Section: linear_algebra
+C-Name: listinsert
+Prototype: WGL
+Help: listinsert(L,x,n): insert x at index n in list L, shifting the
+ remaining elements to the right.
+Description:
+ (list, gen, small):gen        listinsert($1, $2, $3)
+Doc: inserts the object $x$ at
+ position $n$ in $L$ (which must be of type \typ{LIST}). This has
+ complexity $O(\#L - n + 1)$: all the
+ remaining elements of \var{list} (from position $n+1$ onwards) are shifted
+ to the right.
diff --git a/src/functions/linear_algebra/listkill b/src/functions/linear_algebra/listkill
new file mode 100644
index 0000000..c83a362
--- /dev/null
+++ b/src/functions/linear_algebra/listkill
@@ -0,0 +1,8 @@
+Function: listkill
+Section: linear_algebra
+C-Name: listkill
+Prototype: vG
+Help: listkill(L): obsolete, retained for backward compatibility.
+Doc: obsolete, retained for backward compatibility. Just use \kbd{L = List()}
+ instead of \kbd{listkill(L)}. In most cases, you won't even need that, e.g.
+ local variables are automatically cleared when a user function returns.
diff --git a/src/functions/linear_algebra/listpop b/src/functions/linear_algebra/listpop
new file mode 100644
index 0000000..5246a6c
--- /dev/null
+++ b/src/functions/linear_algebra/listpop
@@ -0,0 +1,13 @@
+Function: listpop
+Section: linear_algebra
+C-Name: listpop
+Prototype: vWD0,L,
+Help: listpop(list,{n}): removes n-th element from list. If n is
+ omitted or greater than the current list length, removes last element.
+Description:
+ (list, small):void     listpop($1, $2)
+Doc:
+ removes the $n$-th element of the list
+ \var{list} (which must be of type \typ{LIST}). If $n$ is omitted,
+ or greater than the list current length, removes the last element.
+ If the list is already empty, do nothing. This runs in time $O(\#L - n + 1)$.
diff --git a/src/functions/linear_algebra/listput b/src/functions/linear_algebra/listput
new file mode 100644
index 0000000..6e3a457
--- /dev/null
+++ b/src/functions/linear_algebra/listput
@@ -0,0 +1,18 @@
+Function: listput
+Section: linear_algebra
+C-Name: listput
+Prototype: WGD0,L,
+Help: listput(list,x,{n}): sets n-th element of list equal to x. If n is
+ omitted or greater than the current list length, appends x.
+Description:
+ (list, gen, small):gen        listput($1, $2, $3)
+Doc:
+ sets the $n$-th element of the list
+ \var{list} (which must be of type \typ{LIST}) equal to $x$. If $n$ is omitted,
+ or greater than the list length, appends $x$.
+ You may put an element into an occupied cell (not changing the
+ list length), but it is easier to use the standard \kbd{list[n] = x}
+ construct. This runs in time $O(\#L)$ in the worst case (when the list must
+ be reallocated), but in time $O(1)$ on average: any number of successive
+ \kbd{listput}s run in time $O(\#L)$, where $\#L$ denotes the list
+ \emph{final} length.
diff --git a/src/functions/linear_algebra/listsort b/src/functions/linear_algebra/listsort
new file mode 100644
index 0000000..c6f8e66
--- /dev/null
+++ b/src/functions/linear_algebra/listsort
@@ -0,0 +1,21 @@
+Function: listsort
+Section: linear_algebra
+C-Name: listsort
+Prototype: vWD0,L,
+Help: listsort(L,{flag=0}): sort the list L in place. If flag is non-zero,
+ suppress all but one occurence of each element in list.
+Doc: sorts the \typ{LIST} \var{list} in place, with respect to the (somewhat
+ arbitrary) universal comparison function \tet{cmp}. In particular, the
+ ordering is the same as for sets and \tet{setsearch} can be used on a sorted
+ list.
+ \bprog
+ ? L = List([1,2,4,1,3,-1]); listsort(L); L
+ %1 = List([-1, 1, 1, 2, 3, 4])
+ ? setsearch(L, 4)
+ %2 = 6
+ ? setsearch(L, -2)
+ %3 = 0
+ @eprog\noindent This is faster than the \kbd{vecsort} command since the list
+ is sorted in place: no copy is made. No value returned.
+
+ If $\fl$ is non-zero, suppresses all repeated coefficients.
diff --git a/src/functions/linear_algebra/matadjoint b/src/functions/linear_algebra/matadjoint
new file mode 100644
index 0000000..48683ec
--- /dev/null
+++ b/src/functions/linear_algebra/matadjoint
@@ -0,0 +1,32 @@
+Function: matadjoint
+Section: linear_algebra
+C-Name: matadjoint0
+Prototype: GD0,L,
+Help: matadjoint(M,{flag=0}): adjoint matrix of M using Leverrier-Faddeev's
+ algorithm. If flag is 1, compute the characteristic polynomial independently
+ first.
+Doc:
+ \idx{adjoint matrix} of $M$, i.e.~a matrix $N$
+ of cofactors of $M$, satisfying $M*N=\det(M)*\Id$. $M$ must be a
+ (non-necessarily invertible) square matrix of dimension $n$.
+ If $\fl$ is 0 or omitted, we try to use Leverrier-Faddeev's algorithm,
+ which assumes that $n!$ invertible. If it fails or $\fl = 1$,
+ compute $T = \kbd{charpoly}(M)$ independently first and return
+ $(-1)^{n-1} (T(x)-T(0))/x$ evaluated at $M$.
+ \bprog
+ ? a = [1,2,3;3,4,5;6,7,8] * Mod(1,4);
+ %2 =
+ [Mod(1, 4) Mod(2, 4) Mod(3, 4)]
+
+ [Mod(3, 4) Mod(0, 4) Mod(1, 4)]
+
+ [Mod(2, 4) Mod(3, 4) Mod(0, 4)]
+ @eprog\noindent
+ Both algorithms use $O(n^4)$ operations in the base ring, and are usually
+ slower than computing the characteristic polynomial or the inverse of $M$
+ directly.
+Variant: Also available are
+ \fun{GEN}{adj}{GEN x} (\fl=0) and
+ \fun{GEN}{adjsafe}{GEN x} (\fl=1).
+
+
diff --git a/src/functions/linear_algebra/matcompanion b/src/functions/linear_algebra/matcompanion
new file mode 100644
index 0000000..0f3ffb3
--- /dev/null
+++ b/src/functions/linear_algebra/matcompanion
@@ -0,0 +1,7 @@
+Function: matcompanion
+Section: linear_algebra
+C-Name: matcompanion
+Prototype: G
+Help: matcompanion(x): companion matrix to polynomial x.
+Doc:
+ the left companion matrix to the non-zero polynomial $x$.
diff --git a/src/functions/linear_algebra/matconcat b/src/functions/linear_algebra/matconcat
new file mode 100644
index 0000000..1ca5211
--- /dev/null
+++ b/src/functions/linear_algebra/matconcat
@@ -0,0 +1,89 @@
+Function: matconcat
+Section: linear_algebra
+C-Name: matconcat
+Prototype: G
+Help: matconcat(v): concatenate the entries of v and return the resulting matrix
+Doc: returns a \typ{MAT} built from the entries of $v$, which may
+ be a \typ{VEC} (concatenate horizontally), a \typ{COL} (concatenate
+ vertically), or a \typ{MAT} (concatenate vertically each column, and
+ concatenate vertically the resulting matrices). The entries of $v$ are always
+ considered as matrices: they can themselves be \typ{VEC} (seen as a row
+ matrix), a \typ{COL} seen as a column matrix), a \typ{MAT}, or a scalar (seen
+ as an $1 \times 1$ matrix).
+ \bprog
+ ? A=[1,2;3,4]; B=[5,6]~; C=[7,8]; D=9;
+ ? matconcat([A, B]) \\ horizontal
+ %1 =
+ [1 2 5]
+
+ [3 4 6]
+ ? matconcat([A, C]~) \\ vertical
+ %2 =
+ [1 2]
+
+ [3 4]
+
+ [7 8]
+ ? matconcat([A, B; C, D]) \\ block matrix
+ %3 =
+ [1 2 5]
+
+ [3 4 6]
+
+ [7 8 9]
+ @eprog\noindent
+ If the dimensions of the entries to concatenate do not match up, the above
+ rules are extended as follows:
+
+ \item each entry $v_{i,j}$ of $v$ has a natural length and height: $1 \times
+ 1$ for a scalar, $1 \times n$ for a \typ{VEC} of length $n$, $n \times 1$
+ for a \typ{COL}, $m \times n$ for an $m\times n$ \typ{MAT}
+
+ \item let $H_i$ be the maximum over $j$ of the lengths of the $v_{i,j}$,
+ let $L_j$ be the maximum over $i$ of the heights of the $v_{i,j}$.
+ The dimensions of the $(i,j)$-th block in the concatenated matrix are
+ $H_i \times L_j$.
+
+ \item a scalar $s = v_{i,j}$ is considered as $s$ times an identity matrix
+ of the block dimension $\min (H_i,L_j)$
+
+ \item blocks are extended by 0 columns on the right and 0 rows at the
+ bottom, as needed.
+
+ \bprog
+ ? matconcat([1, [2,3]~, [4,5,6]~]) \\ horizontal
+ %4 =
+ [1 2 4]
+
+ [0 3 5]
+
+ [0 0 6]
+ ? matconcat([1, [2,3], [4,5,6]]~) \\ vertical
+ %5 =
+ [1 0 0]
+
+ [2 3 0]
+
+ [4 5 6]
+ ? matconcat([B, C; A, D]) \\ block matrix
+ %6 =
+ [5 0 7 8]
+
+ [6 0 0 0]
+
+ [1 2 9 0]
+
+ [3 4 0 9]
+ ? U=[1,2;3,4]; V=[1,2,3;4,5,6;7,8,9];
+ ? matconcat(matdiagonal([U, V])) \\ block diagonal
+ %7 =
+ [1 2 0 0 0]
+
+ [3 4 0 0 0]
+
+ [0 0 1 2 3]
+
+ [0 0 4 5 6]
+
+ [0 0 7 8 9]
+ @eprog
diff --git a/src/functions/linear_algebra/matdet b/src/functions/linear_algebra/matdet
new file mode 100644
index 0000000..635fa36
--- /dev/null
+++ b/src/functions/linear_algebra/matdet
@@ -0,0 +1,33 @@
+Function: matdet
+Section: linear_algebra
+C-Name: det0
+Prototype: GD0,L,
+Help: matdet(x,{flag=0}): determinant of the matrix x using an appropriate
+ algorithm depending on the coefficients. If (optional) flag is set to 1, use
+ classical Gaussian elimination (usually worse than the default).
+Description:
+ (gen, ?0):gen           det($1)
+ (gen, 1):gen            det2($1)
+ (gen, #small):gen       $"incorrect flag in matdet"
+ (gen, small):gen        det0($1, $2)
+Doc: determinant of the square matrix $x$.
+
+ If $\fl=0$, uses an appropriate algorithm depending on the coefficients:
+
+ \item integer entries: modular method due to Dixon, Pernet and Stein.
+
+ \item real or $p$-adic entries: classical Gaussian elimination using maximal
+ pivot.
+
+ \item intmod entries: classical Gaussian elimination using first non-zero
+ pivot.
+
+ \item other cases: Gauss-Bareiss.
+
+ If $\fl=1$, uses classical Gaussian elimination with appropriate pivoting
+ strategy (maximal pivot for real or $p$-adic coefficients). This is usually
+ worse than the default.
+
+Variant: Also available are \fun{GEN}{det}{GEN x} ($\fl=0$),
+ \fun{GEN}{det2}{GEN x} ($\fl=1$) and \fun{GEN}{ZM_det}{GEN x} for integer
+ entries.
diff --git a/src/functions/linear_algebra/matdetint b/src/functions/linear_algebra/matdetint
new file mode 100644
index 0000000..1c23e69
--- /dev/null
+++ b/src/functions/linear_algebra/matdetint
@@ -0,0 +1,26 @@
+Function: matdetint
+Section: linear_algebra
+C-Name: detint
+Prototype: G
+Help: matdetint(B): some multiple of the determinant of the lattice
+ generated by the columns of B (0 if not of maximal rank). Useful with
+ mathnfmod.
+Doc:
+ Let $B$ be an $m\times n$ matrix with integer coefficients. The
+ \emph{determinant} $D$ of the lattice generated by the columns of $B$ is
+ the square root of $\det(B^T B)$ if $B$ has maximal rank $m$, and $0$
+ otherwise.
+
+ This function uses the Gauss-Bareiss algorithm to compute a positive
+ \emph{multiple} of $D$. When $B$ is square, the function actually returns
+ $D = |\det B|$.
+
+ This function is useful in conjunction with \kbd{mathnfmod}, which needs to
+ know such a multiple. If the rank is maximal and the matrix non-square,
+ you can obtain $D$ exactly using
+ \bprog
+   matdet( mathnfmod(B, matdetint(B)) )
+ @eprog\noindent
+ Note that as soon as one of the dimensions gets large ($m$ or $n$ is larger
+ than 20, say), it will often be much faster to use \kbd{mathnf(B, 1)} or
+ \kbd{mathnf(B, 4)} directly.
diff --git a/src/functions/linear_algebra/matdiagonal b/src/functions/linear_algebra/matdiagonal
new file mode 100644
index 0000000..7780a9b
--- /dev/null
+++ b/src/functions/linear_algebra/matdiagonal
@@ -0,0 +1,32 @@
+Function: matdiagonal
+Section: linear_algebra
+C-Name: diagonal
+Prototype: G
+Help: matdiagonal(x): creates the diagonal matrix whose diagonal entries are
+ the entries of the vector x.
+Doc: $x$ being a vector, creates the diagonal matrix
+ whose diagonal entries are those of $x$.
+ \bprog
+ ? matdiagonal([1,2,3]);
+ %1 =
+ [1 0 0]
+
+ [0 2 0]
+
+ [0 0 3]
+ @eprog\noindent Block diagonal matrices are easily created using
+ \tet{matconcat}:
+ \bprog
+ ? U=[1,2;3,4]; V=[1,2,3;4,5,6;7,8,9];
+ ? matconcat(matdiagonal([U, V]))
+ %1 =
+ [1 2 0 0 0]
+
+ [3 4 0 0 0]
+
+ [0 0 1 2 3]
+
+ [0 0 4 5 6]
+
+ [0 0 7 8 9]
+ @eprog
diff --git a/src/functions/linear_algebra/mateigen b/src/functions/linear_algebra/mateigen
new file mode 100644
index 0000000..64b6c44
--- /dev/null
+++ b/src/functions/linear_algebra/mateigen
@@ -0,0 +1,41 @@
+Function: mateigen
+Section: linear_algebra
+C-Name: mateigen
+Prototype: GD0,L,p
+Help: mateigen(x,{flag=0}): complex eigenvectors of the matrix x given as
+ columns of a matrix H. If flag=1, return [L,H], where L contains the
+ eigenvalues and H the corresponding eigenvectors.
+Doc: returns the (complex) eigenvectors of $x$ as columns of a matrix.
+ If $\fl=1$, return $[L,H]$, where $L$ contains the
+ eigenvalues and $H$ the corresponding eigenvectors; multiple eigenvalues are
+ repeated according to the eigenspace dimension (which may be less
+ than the eigenvalue multiplicity in the characteristic polynomial).
+
+ This function first computes the characteristic polynomial of $x$ and
+ approximates its complex roots $(\lambda_i)$, then tries to compute the
+ eigenspaces as kernels of the $x - \lambda_i$. This algorithm is
+ ill-conditioned and is likely to miss kernel vectors if some roots of the
+ characteristic polynomial are close, in particular if it has multiple roots.
+ \bprog
+ ? A = [13,2; 10,14]; mateigen(A)
+ %1 =
+ [-1/2 2/5]
+
+ [   1   1]
+ ? [L,H] = mateigen(A, 1);
+ ? L
+ %3 = [9, 18]
+ ? H
+ %4 =
+ [-1/2 2/5]
+
+ [   1   1]
+ @eprog\noindent
+ For symmetric matrices, use \tet{qfjacobi} instead; for Hermitian matrices,
+ compute
+ \bprog
+  A = real(x);
+  B = imag(x);
+  y = matconcat([A, -B; B, A]);
+ @eprog\noindent and apply \kbd{qfjacobi} to $y$.
+Variant: Also available is \fun{GEN}{eigen}{GEN x, long prec} ($\fl = 0$)
diff --git a/src/functions/linear_algebra/matfrobenius b/src/functions/linear_algebra/matfrobenius
new file mode 100644
index 0000000..532cdd4
--- /dev/null
+++ b/src/functions/linear_algebra/matfrobenius
@@ -0,0 +1,14 @@
+Function: matfrobenius
+Section: linear_algebra
+C-Name: matfrobenius
+Prototype: GD0,L,Dn
+Help: matfrobenius(M,{flag},{v='x}): Return the Frobenius form of the square
+ matrix M. If flag is 1, return only the elementary divisors as a vector of
+ polynomials in the variable v. If flag is 2, return a two-components vector
+ [F,B] where F is the Frobenius form and B is the basis change so that
+ M=B^-1*F*B.
+Doc: returns the Frobenius form of
+ the square matrix \kbd{M}. If $\fl=1$, returns only the elementary divisors as
+ a vector of polynomials in the variable \kbd{v}.  If $\fl=2$, returns a
+ two-components vector [F,B] where \kbd{F} is the Frobenius form and \kbd{B} is
+ the basis change so that $M=B^{-1}FB$.
diff --git a/src/functions/linear_algebra/mathess b/src/functions/linear_algebra/mathess
new file mode 100644
index 0000000..bce9961
--- /dev/null
+++ b/src/functions/linear_algebra/mathess
@@ -0,0 +1,7 @@
+Function: mathess
+Section: linear_algebra
+C-Name: hess
+Prototype: G
+Help: mathess(x): Hessenberg form of x.
+Doc: returns a matrix similar to the square matrix $x$, which is in upper Hessenberg
+ form (zero entries below the first subdiagonal).
diff --git a/src/functions/linear_algebra/mathilbert b/src/functions/linear_algebra/mathilbert
new file mode 100644
index 0000000..7b18df5
--- /dev/null
+++ b/src/functions/linear_algebra/mathilbert
@@ -0,0 +1,8 @@
+Function: mathilbert
+Section: linear_algebra
+C-Name: mathilbert
+Prototype: L
+Help: mathilbert(n): Hilbert matrix of order n.
+Doc: $x$ being a \kbd{long}, creates the
+ \idx{Hilbert matrix}of order $x$, i.e.~the matrix whose coefficient
+ ($i$,$j$) is $1/ (i+j-1)$.
diff --git a/src/functions/linear_algebra/mathnf b/src/functions/linear_algebra/mathnf
new file mode 100644
index 0000000..fe13c67
--- /dev/null
+++ b/src/functions/linear_algebra/mathnf
@@ -0,0 +1,127 @@
+Function: mathnf
+Section: linear_algebra
+C-Name: mathnf0
+Prototype: GD0,L,
+Help: mathnf(M,{flag=0}): (upper triangular) Hermite normal form of M, basis
+ for the lattice formed by the columns of M. flag is optional whose value
+ range from 0 to 3 have a binary meaning. Bit 1: complete output, returns
+ a 2-component vector [H,U] such that H is the HNF of M, and U is an
+ invertible matrix such that MU=H. Bit 2: allow polynomial entries, otherwise
+ assume that M is integral. These use a naive algorithm; larger values
+ correspond to more involved algorithms and are restricted to integer
+ matrices; flag = 4: returns [H,U] using LLL reduction along the way;
+ flag = 5: return [H,U,P] where P is a permutation of row indices such that
+ P applied to M U is H.
+Doc: let $R$ be a Euclidean ring, equal to $\Z$ or to $K[X]$ for some field
+ $K$. If $M$ is a (not necessarily square) matrix with entries in $R$, this
+ routine finds the \emph{upper triangular} \idx{Hermite normal form} of $M$.
+ If the rank of $M$ is equal to its number of rows, this is a square
+ matrix. In general, the columns of the result form a basis of the $R$-module
+ spanned by the columns of $M$.
+
+ The values $0,1,2,3$ of $\fl$ have a binary meaning, analogous to the one
+ in \tet{matsnf}; in this case, binary digits of $\fl$ mean:
+
+ \item 1 (complete output): if set, outputs $[H,U]$, where $H$ is the Hermite
+ normal form of $M$, and $U$ is a transformation matrix such that $MU=[0|H]$.
+ The matrix $U$ belongs to $\text{GL}(R)$. When $M$ has a large kernel, the
+ entries of $U$ are in general huge.
+
+ \item 2 (generic input): \emph{Deprecated}. If set, assume that $R = K[X]$ is
+ a polynomial ring; otherwise, assume that $R = \Z$. This flag is now useless
+ since the routine always checks whether the matrix has integral entries.
+
+ \noindent For these 4 values, we use a naive algorithm, which behaves well
+ in small dimension only. Larger values correspond to different algorithms,
+ are restricted to \emph{integer} matrices, and all output the unimodular
+ matrix $U$. From now on all matrices have integral entries.
+
+ \item $\fl=4$, returns $[H,U]$ as in ``complete output'' above, using a
+ variant of \idx{LLL} reduction along the way. The matrix $U$ is provably
+ small in the $L_2$ sense, and in general close to optimal; but the
+ reduction is in general slow, although provably polynomial-time.
+
+ If $\fl=5$, uses Batut's algorithm and output $[H,U,P]$, such that $H$ and
+ $U$ are as before and $P$ is a permutation of the rows such that $P$ applied
+ to $MU$ gives $H$. This is in general faster than $\fl=4$ but the matrix $U$
+ is usually worse; it is heuristically smaller than with the default algorithm.
+
+ When the matrix is dense and the dimension is large (bigger than 100, say),
+ $\fl = 4$ will be fastest. When $M$ has maximal rank, then
+ \bprog
+   H = mathnfmod(M, matdetint(M))
+ @eprog\noindent will be even faster. You can then recover $U$ as $M^{-1}H$.
+
+ \bprog
+ ? M = matrix(3,4,i,j,random([-5,5]))
+ %1 =
+ [ 0 2  3  0]
+
+ [-5 3 -5 -5]
+
+ [ 4 3 -5  4]
+
+ ? [H,U] = mathnf(M, 1);
+ ? U
+ %3 =
+ [-1 0 -1 0]
+
+ [ 0 5  3 2]
+
+ [ 0 3  1 1]
+
+ [ 1 0  0 0]
+
+ ? H
+ %5 =
+ [19 9 7]
+
+ [ 0 9 1]
+
+ [ 0 0 1]
+
+ ? M*U
+ %6 =
+ [0 19 9 7]
+
+ [0  0 9 1]
+
+ [0  0 0 1]
+ @eprog
+
+ For convenience, $M$ is allowed to be a \typ{VEC}, which is then
+ automatically converted to a \typ{MAT}, as per the \tet{Mat} function.
+ For instance to solve the generalized extended gcd problem, one may use
+ \bprog
+ ? v = [116085838, 181081878, 314252913,10346840];
+ ? [H,U] = mathnf(v, 1);
+ ? U
+ %2 =
+ [ 103 -603    15  -88]
+
+ [-146   13 -1208  352]
+
+ [  58  220   678 -167]
+
+ [-362 -144   381 -101]
+ ? v*U
+ %3 = [0, 0, 0, 1]
+ @eprog\noindent This also allows to input a matrix as a \typ{VEC} of
+ \typ{COL}s of the same length (which \kbd{Mat} would concatenate to
+ the \typ{MAT} having those columns):
+ \bprog
+ ? v = [[1,0,4]~, [3,3,4]~, [0,-4,-5]~]; mathnf(v)
+ %1 =
+ [47 32 12]
+
+ [ 0  1  0]
+
+ [ 0  0  1]
+ @eprog
+
+Variant: Also available are \fun{GEN}{hnf}{GEN M} ($\fl=0$) and
+ \fun{GEN}{hnfall}{GEN M} ($\fl=1$). To reduce \emph{huge} relation matrices
+ (sparse with small entries, say dimension $400$ or more), you can use the
+ pair \kbd{hnfspec} / \kbd{hnfadd}. Since this is quite technical and the
+ calling interface may change, they are not documented yet. Look at the code
+ in \kbd{basemath/hnf\_snf.c}.
diff --git a/src/functions/linear_algebra/mathnfmod b/src/functions/linear_algebra/mathnfmod
new file mode 100644
index 0000000..f929059
--- /dev/null
+++ b/src/functions/linear_algebra/mathnfmod
@@ -0,0 +1,16 @@
+Function: mathnfmod
+Section: linear_algebra
+C-Name: hnfmod
+Prototype: GG
+Help: mathnfmod(x,d): (upper triangular) Hermite normal form of x, basis for
+ the lattice formed by the columns of x, where d is a multiple of the
+ non-zero determinant of this lattice.
+Doc: if $x$ is a (not necessarily square) matrix of
+ maximal rank with integer entries, and $d$ is a multiple of the (non-zero)
+ determinant of the lattice spanned by the columns of $x$, finds the
+ \emph{upper triangular} \idx{Hermite normal form} of $x$.
+
+ If the rank of $x$ is equal to its number of rows, the result is a square
+ matrix. In general, the columns of the result form a basis of the lattice
+ spanned by the columns of $x$. Even when $d$ is known, this is in general
+ slower than \kbd{mathnf} but uses much less memory.
diff --git a/src/functions/linear_algebra/mathnfmodid b/src/functions/linear_algebra/mathnfmodid
new file mode 100644
index 0000000..27bb1ca
--- /dev/null
+++ b/src/functions/linear_algebra/mathnfmodid
@@ -0,0 +1,34 @@
+Function: mathnfmodid
+Section: linear_algebra
+C-Name: hnfmodid
+Prototype: GG
+Help: mathnfmodid(x,d): (upper triangular) Hermite normal form of x
+ concatenated with matdiagonal(d)
+Doc: outputs the (upper triangular)
+ \idx{Hermite normal form} of $x$ concatenated with the diagonal
+ matrix with diagonal $d$. Assumes that $x$ has integer entries.
+ Variant: if $d$ is an integer instead of a vector, concatenate $d$ times the
+ identity matrix.
+ \bprog
+ ? m=[0,7;-1,0;-1,-1]
+ %1 =
+ [ 0  7]
+
+ [-1  0]
+
+ [-1 -1]
+ ? mathnfmodid(m, [6,2,2])
+ %2 =
+ [2 1 1]
+
+ [0 1 0]
+
+ [0 0 1]
+ ? mathnfmodid(m, 10)
+ %3 =
+ [10 7 3]
+
+ [ 0 1 0]
+
+ [ 0 0 1]
+ @eprog
diff --git a/src/functions/linear_algebra/mathouseholder b/src/functions/linear_algebra/mathouseholder
new file mode 100644
index 0000000..770f330
--- /dev/null
+++ b/src/functions/linear_algebra/mathouseholder
@@ -0,0 +1,8 @@
+Function: mathouseholder
+Section: linear_algebra
+C-Name: mathouseholder
+Prototype: GG
+Help: mathouseholder(Q,v): applies a sequence Q of Householder transforms
+ to the vector or matrix v.
+Doc: \sidx{Householder transform}applies a sequence $Q$ of Householder
+ transforms, as returned by \kbd{matqr}$(M,1)$ to the vector or matrix $v$.
diff --git a/src/functions/linear_algebra/matid b/src/functions/linear_algebra/matid
new file mode 100644
index 0000000..3910bad
--- /dev/null
+++ b/src/functions/linear_algebra/matid
@@ -0,0 +1,8 @@
+Function: matid
+Section: linear_algebra
+C-Name: matid
+Prototype: L
+Help: matid(n): identity matrix of order n.
+Description:
+ (small):vec    matid($1)
+Doc: creates the $n\times n$ identity matrix.
diff --git a/src/functions/linear_algebra/matimage b/src/functions/linear_algebra/matimage
new file mode 100644
index 0000000..247e98d
--- /dev/null
+++ b/src/functions/linear_algebra/matimage
@@ -0,0 +1,17 @@
+Function: matimage
+Section: linear_algebra
+C-Name: matimage0
+Prototype: GD0,L,
+Help: matimage(x,{flag=0}): basis of the image of the matrix x. flag is
+ optional and can be set to 0 or 1, corresponding to two different algorithms.
+Description:
+ (gen, ?0):vec           image($1)
+ (gen, 1):vec            image2($1)
+ (gen, #small)           $"incorrect flag in matimage"
+ (gen, small):vec        matimage0($1, $2)
+Doc: gives a basis for the image of the
+ matrix $x$ as columns of a matrix. A priori the matrix can have entries of
+ any type. If $\fl=0$, use standard Gauss pivot. If $\fl=1$, use
+ \kbd{matsupplement} (much slower: keep the default flag!).
+
+Variant: Also available is \fun{GEN}{image}{GEN x} ($\fl=0$).
diff --git a/src/functions/linear_algebra/matimagecompl b/src/functions/linear_algebra/matimagecompl
new file mode 100644
index 0000000..4297fbe
--- /dev/null
+++ b/src/functions/linear_algebra/matimagecompl
@@ -0,0 +1,13 @@
+Function: matimagecompl
+Section: linear_algebra
+C-Name: imagecompl
+Prototype: G
+Description:
+ (gen):vecsmall                imagecompl($1)
+Help: matimagecompl(x): vector of column indices not corresponding to the
+ indices given by the function matimage.
+Doc: gives the vector of the column indices which
+ are not extracted by the function \kbd{matimage}, as a permutation
+ (\typ{VECSMALL}). Hence the number of
+ components of \kbd{matimagecompl(x)} plus the number of columns of
+ \kbd{matimage(x)} is equal to the number of columns of the matrix $x$.
diff --git a/src/functions/linear_algebra/matindexrank b/src/functions/linear_algebra/matindexrank
new file mode 100644
index 0000000..6e06b0e
--- /dev/null
+++ b/src/functions/linear_algebra/matindexrank
@@ -0,0 +1,11 @@
+Function: matindexrank
+Section: linear_algebra
+C-Name: indexrank
+Prototype: G
+Help: matindexrank(x): gives two extraction vectors (rows and columns) for
+ the matrix x such that the extracted matrix is square of maximal rank.
+Doc: $x$ being a matrix of rank $r$, returns a vector with two
+ \typ{VECSMALL} components $y$ and $z$ of length $r$ giving a list of rows
+ and columns respectively (starting from 1) such that the extracted matrix
+ obtained from these two vectors using $\tet{vecextract}(x,y,z)$ is
+ invertible.
diff --git a/src/functions/linear_algebra/matintersect b/src/functions/linear_algebra/matintersect
new file mode 100644
index 0000000..bfc0c29
--- /dev/null
+++ b/src/functions/linear_algebra/matintersect
@@ -0,0 +1,13 @@
+Function: matintersect
+Section: linear_algebra
+C-Name: intersect
+Prototype: GG
+Help: matintersect(x,y): intersection of the vector spaces whose bases are
+ the columns of x and y.
+Doc: $x$ and $y$ being two matrices with the same
+ number of rows each of whose columns are independent, finds a basis of the
+ $\Q$-vector space equal to the intersection of the spaces spanned by the
+ columns of $x$ and $y$ respectively. The faster function
+ \tet{idealintersect} can be used to intersect fractional ideals (projective
+ $\Z_K$ modules of rank $1$); the slower but much more general function
+ \tet{nfhnf} can be used to intersect general $\Z_K$-modules.
diff --git a/src/functions/linear_algebra/matinverseimage b/src/functions/linear_algebra/matinverseimage
new file mode 100644
index 0000000..caa534f
--- /dev/null
+++ b/src/functions/linear_algebra/matinverseimage
@@ -0,0 +1,30 @@
+Function: matinverseimage
+Section: linear_algebra
+C-Name: inverseimage
+Prototype: GG
+Help: matinverseimage(x,y): an element of the inverse image of the vector y
+ by the matrix x if one exists, the empty vector otherwise.
+Doc: given a matrix $x$ and
+ a column vector or matrix $y$, returns a preimage $z$ of $y$ by $x$ if one
+ exists (i.e such that $x z = y$), an empty vector or matrix otherwise. The
+ complete inverse image is $z + \text{Ker} x$, where a basis of the kernel of
+ $x$ may be obtained by \kbd{matker}.
+ \bprog
+ ? M = [1,2;2,4];
+ ? matinverseimage(M, [1,2]~)
+ %2 = [1, 0]~
+ ? matinverseimage(M, [3,4]~)
+ %3 = []~    \\@com no solution
+ ? matinverseimage(M, [1,3,6;2,6,12])
+ %4 =
+ [1 3 6]
+
+ [0 0 0]
+ ? matinverseimage(M, [1,2;3,4])
+ %5 = [;]    \\@com no solution
+ ? K = matker(M)
+ %6 =
+ [-2]
+
+ [1]
+ @eprog
diff --git a/src/functions/linear_algebra/matisdiagonal b/src/functions/linear_algebra/matisdiagonal
new file mode 100644
index 0000000..ccc3226
--- /dev/null
+++ b/src/functions/linear_algebra/matisdiagonal
@@ -0,0 +1,7 @@
+Function: matisdiagonal
+Section: linear_algebra
+C-Name: isdiagonal
+Prototype: iG
+Help: matisdiagonal(x): true(1) if x is a diagonal matrix, false(0)
+ otherwise.
+Doc: returns true (1) if $x$ is a diagonal matrix, false (0) if not.
diff --git a/src/functions/linear_algebra/matker b/src/functions/linear_algebra/matker
new file mode 100644
index 0000000..d26e4f7
--- /dev/null
+++ b/src/functions/linear_algebra/matker
@@ -0,0 +1,20 @@
+Function: matker
+Section: linear_algebra
+C-Name: matker0
+Prototype: GD0,L,
+Help: matker(x,{flag=0}): basis of the kernel of the matrix x. flag is
+ optional, and may be set to 0: default; non-zero: x is known to have
+ integral entries.
+Description:
+ (gen, ?0):vec           ker($1)
+ (gen, 1):vec            keri($1)
+ (gen, #small)           $"incorrect flag in matker"
+ (gen, small):vec        matker0($1, $2)
+Doc: gives a basis for the kernel of the matrix $x$ as columns of a matrix.
+ The matrix can have entries of any type, provided they are compatible with
+ the generic arithmetic operations ($+$, $\times$ and $/$).
+
+ If $x$ is known to have integral entries, set $\fl=1$.
+
+Variant: Also available are \fun{GEN}{ker}{GEN x} ($\fl=0$),
+ \fun{GEN}{keri}{GEN x} ($\fl=1$).
diff --git a/src/functions/linear_algebra/matkerint b/src/functions/linear_algebra/matkerint
new file mode 100644
index 0000000..3c0cde8
--- /dev/null
+++ b/src/functions/linear_algebra/matkerint
@@ -0,0 +1,22 @@
+Function: matkerint
+Section: linear_algebra
+C-Name: matkerint0
+Prototype: GD0,L,
+Help: matkerint(x,{flag=0}): LLL-reduced Z-basis of the kernel of the matrix
+ x with integral entries. flag is optional, and may be set to 0: default,
+ uses LLL, 1: uses matrixqz (much slower).
+Doc: gives an \idx{LLL}-reduced $\Z$-basis
+ for the lattice equal to the kernel of the matrix $x$ as columns of the
+ matrix $x$ with integer entries (rational entries are not permitted).
+
+ If $\fl=0$, uses an integer LLL algorithm.
+
+ If $\fl=1$, uses $\kbd{matrixqz}(x,-2)$. Many orders of magnitude slower
+ than the default: never use this.
+
+Variant: See also \fun{GEN}{kerint}{GEN x} ($\fl=0$), which is a trivial
+ wrapper around
+ \bprog
+ ZM_lll(ZM_lll(x, 0.99, LLL_KER), 0.99, LLL_INPLACE);
+ @eprog\noindent Remove the outermost \kbd{ZM\_lll} if LLL-reduction is not
+ desired (saves time).
diff --git a/src/functions/linear_algebra/matmuldiagonal b/src/functions/linear_algebra/matmuldiagonal
new file mode 100644
index 0000000..67e9244
--- /dev/null
+++ b/src/functions/linear_algebra/matmuldiagonal
@@ -0,0 +1,10 @@
+Function: matmuldiagonal
+Section: linear_algebra
+C-Name: matmuldiagonal
+Prototype: GG
+Help: matmuldiagonal(x,d): product of matrix x by diagonal matrix whose
+ diagonal coefficients are those of the vector d, equivalent but faster than
+ x*matdiagonal(d).
+Doc: product of the matrix $x$ by the diagonal
+ matrix whose diagonal entries are those of the vector $d$. Equivalent to,
+ but much faster than $x*\kbd{matdiagonal}(d)$.
diff --git a/src/functions/linear_algebra/matmultodiagonal b/src/functions/linear_algebra/matmultodiagonal
new file mode 100644
index 0000000..b17d5a5
--- /dev/null
+++ b/src/functions/linear_algebra/matmultodiagonal
@@ -0,0 +1,10 @@
+Function: matmultodiagonal
+Section: linear_algebra
+C-Name: matmultodiagonal
+Prototype: GG
+Help: matmultodiagonal(x,y): product of matrices x and y, knowing that the
+ result will be a diagonal matrix. Much faster than general multiplication in
+ that case.
+Doc: product of the matrices $x$ and $y$ assuming that the result is a
+ diagonal matrix. Much faster than $x*y$ in that case. The result is
+ undefined if $x*y$ is not diagonal.
diff --git a/src/functions/linear_algebra/matpascal b/src/functions/linear_algebra/matpascal
new file mode 100644
index 0000000..09141cf
--- /dev/null
+++ b/src/functions/linear_algebra/matpascal
@@ -0,0 +1,12 @@
+Function: matpascal
+Section: linear_algebra
+C-Name: matqpascal
+Prototype: LDG
+Help: matpascal(n,{q}): Pascal triangle of order n if q is omitted. q-Pascal
+ triangle otherwise.
+Doc: creates as a matrix the lower triangular
+ \idx{Pascal triangle} of order $x+1$ (i.e.~with binomial coefficients
+ up to $x$). If $q$ is given, compute the $q$-Pascal triangle (i.e.~using
+ $q$-binomial coefficients).
+
+Variant: Also available is \fun{GEN}{matpascal}{GEN x}.
diff --git a/src/functions/linear_algebra/matqr b/src/functions/linear_algebra/matqr
new file mode 100644
index 0000000..1bea007
--- /dev/null
+++ b/src/functions/linear_algebra/matqr
@@ -0,0 +1,22 @@
+Function: matqr
+Section: linear_algebra
+C-Name: matqr
+Prototype: GD0,L,p
+Help: matqr(M,{flag=0}): returns [Q,R], the QR-decomposition of the square
+ invertible matrix M. If flag=1, Q is given as a sequence of Householder
+ transforms (faster and stabler).
+Doc: returns $[Q,R]$, the \idx{QR-decomposition} of the square invertible
+ matrix $M$ with real entries: $Q$ is orthogonal and $R$ upper triangular. If
+ $\fl=1$, the orthogonal matrix is returned as a sequence of Householder
+ transforms: applying such a sequence is stabler and faster than
+ multiplication by the corresponding $Q$ matrix.\sidx{Householder transform}
+ More precisely, if
+ \bprog
+   [Q,R] = matqr(M);
+   [q,r] = matqr(M, 1);
+ @eprog\noindent then $r = R$ and \kbd{mathouseholder}$(q, M)$ is $R$;
+ furthermore
+ \bprog
+   mathouseholder(q, matid(#M)) == Q~
+ @eprog\noindent the inverse of $Q$. This function raises an error if the
+ precision is too low or $x$ is singular.
diff --git a/src/functions/linear_algebra/matrank b/src/functions/linear_algebra/matrank
new file mode 100644
index 0000000..7dbb72d
--- /dev/null
+++ b/src/functions/linear_algebra/matrank
@@ -0,0 +1,6 @@
+Function: matrank
+Section: linear_algebra
+C-Name: rank
+Prototype: lG
+Help: matrank(x): rank of the matrix x.
+Doc: rank of the matrix $x$.
diff --git a/src/functions/linear_algebra/matrix b/src/functions/linear_algebra/matrix
new file mode 100644
index 0000000..4e3f679
--- /dev/null
+++ b/src/functions/linear_algebra/matrix
@@ -0,0 +1,14 @@
+Function: matrix
+Section: linear_algebra
+C-Name: matrice
+Prototype: GGDVDVDE
+Help: matrix(m,n,{X},{Y},{expr=0}): mXn matrix of expression expr, the row
+ variable X going from 1 to m and the column variable Y going from 1 to n. By
+ default, fill with 0s.
+Doc: creation of the
+ $m\times n$ matrix whose coefficients are given by the expression
+ \var{expr}. There are two formal parameters in \var{expr}, the first one
+ ($X$) corresponding to the rows, the second ($Y$) to the columns, and $X$
+ goes from 1 to $m$, $Y$ goes from 1 to $n$. If one of the last 3 parameters
+ is omitted, fill the matrix with zeroes.
+ %\syn{NO}
diff --git a/src/functions/linear_algebra/matrixqz b/src/functions/linear_algebra/matrixqz
new file mode 100644
index 0000000..b3ba8c5
--- /dev/null
+++ b/src/functions/linear_algebra/matrixqz
@@ -0,0 +1,53 @@
+Function: matrixqz
+Section: linear_algebra
+C-Name: matrixqz0
+Prototype: GDG
+Help: matrixqz(A,{p=0}): if p>=0, transforms the rational or integral mxn (m>=n)
+ matrix A into an integral matrix with gcd of maximal determinants coprime to
+ p. If p=-1, finds a basis of the intersection with Z^n of the lattice spanned
+ by the columns of A. If p=-2, finds a basis of the intersection with Z^n of
+ the Q-vector space spanned by the columns of A.
+Doc: $A$ being an $m\times n$ matrix in $M_{m,n}(\Q)$, let
+ $\text{Im}_\Q A$ (resp.~$\text{Im}_\Z A$) the $\Q$-vector space
+ (resp.~the $\Z$-module) spanned by the columns of $A$. This function has
+ varying behavior depending on the sign of $p$:
+
+ If $p \geq 0$, $A$ is assumed to have maximal rank $n\leq m$. The function
+ returns a matrix $B\in M_{m,n}(\Z)$, with $\text{Im}_\Q B = \text{Im}_\Q A$,
+ such that the GCD of all its $n\times n$ minors is coprime to
+ $p$; in particular, if $p = 0$ (default), this GCD is $1$.
+ \bprog
+ ? minors(x) = vector(#x[,1], i, matdet(x[^i,]));
+ ? A = [3,1/7; 5,3/7; 7,5/7]; minors(A)
+ %1 = [4/7, 8/7, 4/7]   \\ determinants of all 2x2 minors
+ ? B = matrixqz(A)
+ %2 =
+ [3 1]
+
+ [5 2]
+
+ [7 3]
+ ? minors(%)
+ %3 = [1, 2, 1]   \\ B integral with coprime minors
+ @eprog
+
+ If $p=-1$, returns the HNF basis of the lattice $\Z^n \cap \text{Im}_\Z A$.
+
+ If $p=-2$, returns the HNF basis of the lattice $\Z^n \cap \text{Im}_\Q A$.
+ \bprog
+ ? matrixqz(A,-1)
+ %4 =
+ [8 5]
+
+ [4 3]
+
+ [0 1]
+
+ ? matrixqz(A,-2)
+ %5 =
+ [2 -1]
+
+ [1 0]
+
+ [0 1]
+ @eprog
diff --git a/src/functions/linear_algebra/matsize b/src/functions/linear_algebra/matsize
new file mode 100644
index 0000000..f90f851
--- /dev/null
+++ b/src/functions/linear_algebra/matsize
@@ -0,0 +1,9 @@
+Function: matsize
+Section: linear_algebra
+C-Name: matsize
+Prototype: G
+Help: matsize(x): number of rows and columns of the vector/matrix x as a
+ 2-vector.
+Doc: $x$ being a vector or matrix, returns a row vector
+ with two components, the first being the number of rows (1 for a row vector),
+ the second the number of columns (1 for a column vector).
diff --git a/src/functions/linear_algebra/matsnf b/src/functions/linear_algebra/matsnf
new file mode 100644
index 0000000..13b950f
--- /dev/null
+++ b/src/functions/linear_algebra/matsnf
@@ -0,0 +1,31 @@
+Function: matsnf
+Section: linear_algebra
+C-Name: matsnf0
+Prototype: GD0,L,
+Help: matsnf(X,{flag=0}): Smith normal form (i.e. elementary divisors) of
+ the matrix X, expressed as a vector d. Binary digits of flag mean 1: returns
+ [u,v,d] where d=u*X*v, otherwise only the diagonal d is returned, 2: allow
+ polynomial entries, otherwise assume X is integral, 4: removes all
+ information corresponding to entries equal to 1 in d.
+Doc: if $X$ is a (singular or non-singular) matrix outputs the vector of
+ \idx{elementary divisors} of $X$, i.e.~the diagonal of the
+ \idx{Smith normal form} of $X$, normalized so that $d_n \mid d_{n-1} \mid
+ \ldots \mid d_1$.
+
+ The binary digits of \fl\ mean:
+
+ 1 (complete output): if set, outputs $[U,V,D]$, where $U$ and $V$ are two
+ unimodular matrices such that $UXV$ is the diagonal matrix $D$. Otherwise
+ output only the diagonal of $D$. If $X$ is not a square matrix, then $D$
+ will be a square diagonal matrix padded with zeros on the left or the top.
+
+ 2 (generic input): if set, allows polynomial entries, in which case the
+ input matrix must be square. Otherwise, assume that $X$ has integer
+ coefficients with arbitrary shape.
+
+ 4 (cleanup): if set, cleans up the output. This means that elementary
+ divisors equal to $1$ will be deleted, i.e.~outputs a shortened vector $D'$
+ instead of $D$. If complete output was required, returns $[U',V',D']$ so
+ that $U'XV' = D'$ holds. If this flag is set, $X$ is allowed to be of the
+ form `vector of elementary divisors' or $[U,V,D]$ as would normally be output with the cleanup flag
+ unset.
diff --git a/src/functions/linear_algebra/matsolve b/src/functions/linear_algebra/matsolve
new file mode 100644
index 0000000..24e6f2b
--- /dev/null
+++ b/src/functions/linear_algebra/matsolve
@@ -0,0 +1,12 @@
+Function: matsolve
+Section: linear_algebra
+C-Name: gauss
+Prototype: GG
+Help: matsolve(M,B): solution of MX=B (M matrix, B column vector).
+Doc: $M$ being an invertible matrix and $B$ a column
+ vector, finds the solution $X$ of $MX=B$, using Dixon $p$-adic lifting method
+ if $M$ and $B$ are integral and Gaussian elimination otherwise. This
+ has the same effect as, but is faster, than $M^{-1}*B$.
+
+Variant: For integral input, the function
+ \fun{GEN}{ZM_gauss}{GEN M,GEN B} is also available.
diff --git a/src/functions/linear_algebra/matsolvemod b/src/functions/linear_algebra/matsolvemod
new file mode 100644
index 0000000..dc22677
--- /dev/null
+++ b/src/functions/linear_algebra/matsolvemod
@@ -0,0 +1,30 @@
+Function: matsolvemod
+Section: linear_algebra
+C-Name: matsolvemod0
+Prototype: GGGD0,L,
+Help: matsolvemod(M,D,B,{flag=0}): one solution of system of congruences
+ MX=B mod D (M matrix, B and D column vectors). If (optional) flag is
+ non-null return all solutions.
+Doc: $M$ being any integral matrix,
+ $D$ a column vector of non-negative integer moduli, and $B$ an integral
+ column vector, gives a small integer solution to the system of congruences
+ $\sum_i m_{i,j}x_j\equiv b_i\pmod{d_i}$ if one exists, otherwise returns
+ zero. Shorthand notation: $B$ (resp.~$D$) can be given as a single integer,
+ in which case all the $b_i$ (resp.~$d_i$) above are taken to be equal to $B$
+ (resp.~$D$).
+ \bprog
+ ? M = [1,2;3,4];
+ ? matsolvemod(M, [3,4]~, [1,2]~)
+ %2 = [-2, 0]~
+ ? matsolvemod(M, 3, 1) \\ M X = [1,1]~ over F_3
+ %3 = [-1, 1]~
+ ? matsolvemod(M, [3,0]~, [1,2]~) \\ x + 2y = 1 (mod 3), 3x + 4y = 2 (in Z)
+ %4 = [6, -4]~
+ @eprog
+ If $\fl=1$, all solutions are returned in the form of a two-component row
+ vector $[x,u]$, where $x$ is a small integer solution to the system of
+ congruences and $u$ is a matrix whose columns give a basis of the homogeneous
+ system (so that all solutions can be obtained by adding $x$ to any linear
+ combination of columns of $u$). If no solution exists, returns zero.
+Variant: Also available are \fun{GEN}{gaussmodulo}{GEN M, GEN D, GEN B}
+ ($\fl=0$) and \fun{GEN}{gaussmodulo2}{GEN M, GEN D, GEN B} ($\fl=1$).
diff --git a/src/functions/linear_algebra/matsupplement b/src/functions/linear_algebra/matsupplement
new file mode 100644
index 0000000..b247603
--- /dev/null
+++ b/src/functions/linear_algebra/matsupplement
@@ -0,0 +1,26 @@
+Function: matsupplement
+Section: linear_algebra
+C-Name: suppl
+Prototype: G
+Help: matsupplement(x): supplement the columns of the matrix x to an
+ invertible matrix.
+Doc: assuming that the columns of the matrix $x$
+ are linearly independent (if they are not, an error message is issued), finds
+ a square invertible matrix whose first columns are the columns of $x$,
+ i.e.~supplement the columns of $x$ to a basis of the whole space.
+ \bprog
+ ? matsupplement([1;2])
+ %1 =
+ [1 0]
+
+ [2 1]
+ @eprog
+ Raises an error if $x$ has 0 columns, since (due to a long standing design
+ bug), the dimension of the ambient space (the number of rows) is unknown in
+ this case:
+ \bprog
+ ? matsupplement(matrix(2,0))
+   ***   at top-level: matsupplement(matrix
+   ***                 ^--------------------
+   *** matsupplement: sorry, suppl [empty matrix] is not yet implemented.
+ @eprog
diff --git a/src/functions/linear_algebra/mattranspose b/src/functions/linear_algebra/mattranspose
new file mode 100644
index 0000000..d08633c
--- /dev/null
+++ b/src/functions/linear_algebra/mattranspose
@@ -0,0 +1,7 @@
+Function: mattranspose
+Section: linear_algebra
+C-Name: gtrans
+Prototype: G
+Help: mattranspose(x): x~ = transpose of x.
+Doc: transpose of $x$ (also $x\til$).
+ This has an effect only on vectors and matrices.
diff --git a/src/functions/linear_algebra/minpoly b/src/functions/linear_algebra/minpoly
new file mode 100644
index 0000000..09a8059
--- /dev/null
+++ b/src/functions/linear_algebra/minpoly
@@ -0,0 +1,8 @@
+Function: minpoly
+Section: linear_algebra
+C-Name: minpoly
+Prototype: GDn
+Help: minpoly(A,{v='x}): minimal polynomial of the matrix or polmod A.
+Doc: \idx{minimal polynomial}
+ of $A$ with respect to the variable $v$., i.e. the monic polynomial $P$
+ of minimal degree (in the variable $v$) such that $P(A) = 0$.
diff --git a/src/functions/linear_algebra/norml2 b/src/functions/linear_algebra/norml2
new file mode 100644
index 0000000..376c767
--- /dev/null
+++ b/src/functions/linear_algebra/norml2
@@ -0,0 +1,24 @@
+Function: norml2
+Section: linear_algebra
+C-Name: gnorml2
+Prototype: G
+Help: norml2(x): square of the L2-norm of x.
+Doc: square of the $L^2$-norm of $x$. More precisely,
+ if $x$ is a scalar, $\kbd{norml2}(x)$ is defined to be the square
+ of the complex modulus of $x$ (real \typ{QUAD}s are not supported).
+ If $x$ is a polynomial, a (row or column) vector or a matrix, \kbd{norml2($x$)} is
+ defined recursively as $\sum_i \kbd{norml2}(x_i)$, where $(x_i)$ run through
+ the components of $x$. In particular, this yields the usual $\sum |x_i|^2$
+ (resp.~$\sum |x_{i,j}|^2$) if $x$ is a polynomial or vector (resp.~matrix) with
+ complex components.
+
+ \bprog
+ ? norml2( [ 1, 2, 3 ] )      \\ vector
+ %1 = 14
+ ? norml2( [ 1, 2; 3, 4] )   \\ matrix
+ %2 = 30
+ ? norml2( 2*I + x )
+ %3 = 5
+ ? norml2( [ [1,2], [3,4], 5, 6 ] )   \\ recursively defined
+ %4 = 91
+ @eprog
diff --git a/src/functions/linear_algebra/normlp b/src/functions/linear_algebra/normlp
new file mode 100644
index 0000000..2f66e64
--- /dev/null
+++ b/src/functions/linear_algebra/normlp
@@ -0,0 +1,36 @@
+Function: normlp
+Section: linear_algebra
+C-Name: gnormlp
+Prototype: GDGp
+Help: normlp(x,{p}): Lp-norm of x; sup norm if p is omitted.
+Doc:
+ $L^p$-norm of $x$; sup norm if $p$ is omitted. More precisely,
+ if $x$ is a scalar, \kbd{normlp}$(x, p)$ is defined to be \kbd{abs}$(x)$.
+ If $x$ is a polynomial, a (row or column) vector or a matrix:
+
+ \item  if $p$ is omitted, \kbd{normlp($x$)} is defined recursively as
+ $\max_i \kbd{normlp}(x_i))$, where $(x_i)$ run through the components of~$x$.
+ In particular, this yields the usual sup norm if $x$ is a polynomial or
+ vector with complex components.
+
+ \item otherwise, \kbd{normlp($x$, $p$)} is defined recursively as $(\sum_i
+ \kbd{normlp}^p(x_i,p))^{1/p}$. In particular, this yields the usual $(\sum
+ |x_i|^p)^{1/p}$ if $x$ is a polynomial or vector with complex components.
+
+ \bprog
+ ? v = [1,-2,3]; normlp(v)      \\ vector
+ %1 = 3
+ ? M = [1,-2;-3,4]; normlp(M)   \\ matrix
+ %2 = 4
+ ? T = (1+I) + I*x^2; normlp(T)
+ %3 = 1.4142135623730950488016887242096980786
+ ? normlp([[1,2], [3,4], 5, 6])   \\ recursively defined
+ %4 = 6
+
+ ? normlp(v, 1)
+ %5 = 6
+ ? normlp(M, 1)
+ %6 = 10
+ ? normlp(T, 1)
+ %7 = 2.4142135623730950488016887242096980786
+ @eprog
diff --git a/src/functions/linear_algebra/qfauto b/src/functions/linear_algebra/qfauto
new file mode 100644
index 0000000..77cd4ca
--- /dev/null
+++ b/src/functions/linear_algebra/qfauto
@@ -0,0 +1,27 @@
+Function: qfauto
+Section: linear_algebra
+C-Name: qfauto0
+Prototype: GDG
+Help: qfauto(G,{fl}): automorphism group of the positive definite quadratic form
+ G.
+Doc:
+ $G$ being a square and symmetric matrix with integer entries representing a
+ positive definite quadratic form, outputs the automorphism group of the
+ associate lattice.
+ Since this requires computing the minimal vectors, the computations can
+ become very lengthy as the dimension grows. $G$ can also be given by an
+ \kbd{qfisominit} structure.
+ See \kbd{qfisominit} for the meaning of \var{fl}.
+
+ The output is a two-components vector $[o,g]$ where $o$ is the group order
+ and $g$ is the list of generators (as a vector). For each generator $H$,
+ the equality $G={^t}H\*G\*H$ holds.
+
+ The interface of this function is experimental and will likely change in the
+ future.
+
+ This function implements an algorithm of Plesken and Souvignier, following
+ Souvignier's implementation.
+
+Variant: Also available is \fun{GEN}{qfauto}{GEN G, GEN fl}
+ where $G$ is a vector of \kbd{zm}.
diff --git a/src/functions/linear_algebra/qfautoexport b/src/functions/linear_algebra/qfautoexport
new file mode 100644
index 0000000..b0dc448
--- /dev/null
+++ b/src/functions/linear_algebra/qfautoexport
@@ -0,0 +1,19 @@
+Function: qfautoexport
+Section: linear_algebra
+C-Name: qfautoexport
+Prototype: GD0,L,
+Help: qfautoexport(qfa,{flag}): qfa being an automorphism group as output by
+ qfauto, output a string representing the underlying matrix group in
+ GAP notation (default) or Magma notation (flag = 1).
+Doc: \var{qfa} being an automorphism group as output by
+ \tet{qfauto}, export the underlying matrix group as a string suitable
+ for (no flags or $\fl=0$) GAP or ($\fl=1$) Magma. The following example
+ computes the size of the matrix group using GAP:
+ \bprog
+ ? G = qfauto([2,1;1,2])
+ %1 = [12, [[-1, 0; 0, -1], [0, -1; 1, 1], [1, 1; 0, -1]]]
+ ? s = qfautoexport(G)
+ %2 = "Group([[-1, 0], [0, -1]], [[0, -1], [1, 1]], [[1, 1], [0, -1]])"
+ ? extern("echo \"Order("s");\" | gap -q")
+ %3 = 12
+ @eprog
diff --git a/src/functions/linear_algebra/qfbil b/src/functions/linear_algebra/qfbil
new file mode 100644
index 0000000..e4355ee
--- /dev/null
+++ b/src/functions/linear_algebra/qfbil
@@ -0,0 +1,31 @@
+Function: qfbil
+Section: linear_algebra
+C-Name: qfbil
+Prototype: GGDG
+Help: qfbil(x,y,{q}): evaluate the bilinear form q (symmetric matrix)
+ at (x,y); if q omitted, use the standard Euclidean scalar product.
+Doc: evaluate the bilinear form $q$ (symmetric matrix)
+ at the vectors $(x,y)$; if $q$ omitted, use the standard Euclidean scalar
+ product, corresponding to the identity matrix.
+
+ Roughly equivalent to \kbd{x\til * q * y}, but a little faster and
+ more convenient (does not distinguish between column and row vectors):
+ \bprog
+ ? x = [1,2,3]~; y = [-1,0,1]~; qfbil(x,y)
+ %1 = 2
+ ? q = [1,2,3;2,2,-1;3,-1,0]; qfbil(x,y, q)
+ %2 = -13
+ ? for(i=1,10^6, qfbil(x,y,q))
+ %3 = 568ms
+ ? for(i=1,10^6, x~*q*y)
+ %4 = 717ms
+ @eprog\noindent The associated quadratic form is also available, as
+ \tet{qfnorm}, slightly faster:
+ \bprog
+ ? for(i=1,10^6, qfnorm(x,q))
+ time = 444ms
+ ? for(i=1,10^6, qfnorm(x))
+ time = 176 ms.
+ ? for(i=1,10^6, qfbil(x,y))
+ time = 208 ms.
+ @eprog
diff --git a/src/functions/linear_algebra/qfgaussred b/src/functions/linear_algebra/qfgaussred
new file mode 100644
index 0000000..dbcc2c1
--- /dev/null
+++ b/src/functions/linear_algebra/qfgaussred
@@ -0,0 +1,25 @@
+Function: qfgaussred
+Section: linear_algebra
+C-Name: qfgaussred
+Prototype: G
+Help: qfgaussred(q): square reduction of the (symmetric) matrix q (returns a
+ square matrix whose i-th diagonal term is the coefficient of the i-th square
+ in which the coefficient of the i-th variable is 1).
+Doc:
+ \idx{decomposition into squares} of the
+ quadratic form represented by the symmetric matrix $q$. The result is a
+ matrix whose diagonal entries are the coefficients of the squares, and the
+ off-diagonal entries on each line represent the bilinear forms. More
+ precisely, if $(a_{ij})$ denotes the output, one has
+ $$ q(x) = \sum_i a_{ii} (x_i + \sum_{j \neq i} a_{ij} x_j)^2 $$
+ \bprog
+ ? qfgaussred([0,1;1,0])
+ %1 =
+ [1/2 1]
+
+ [-1 -1/2]
+ @eprog\noindent This means that $2xy = (1/2)(x+y)^2 - (1/2)(x-y)^2$.
+
+Variant: \fun{GEN}{qfgaussred_positive}{GEN q} assumes that $q$ is
+  positive definite and is a little faster; returns \kbd{NULL} if a vector
+  with negative norm occurs (non positive matrix or too many rounding errors).
diff --git a/src/functions/linear_algebra/qfisom b/src/functions/linear_algebra/qfisom
new file mode 100644
index 0000000..f79f811
--- /dev/null
+++ b/src/functions/linear_algebra/qfisom
@@ -0,0 +1,24 @@
+Function: qfisom
+Section: linear_algebra
+C-Name: qfisom0
+Prototype: GGDG
+Help: qfisom(G,H,{fl}): find an isomorphism between the integral positive
+ definite quadratic forms G and H if it exists. G can also be given by a
+ qfisominit structure which is preferable if several forms need to be compared
+ to G.
+Doc:
+ $G$, $H$ being square and symmetric matrices with integer entries representing
+ positive definite quadratic forms, return an invertible matrix $S$ such that
+ $G={^t}S\*H\*S$. This defines a isomorphism between the corresponding lattices.
+ Since this requires computing the minimal vectors, the computations can
+ become very lengthy as the dimension grows.
+ See \kbd{qfisominit} for the meaning of \var{fl}.
+
+ $G$ can also be given by an \kbd{qfisominit} structure which is preferable if
+ several forms $H$ need to be compared to $G$.
+
+ This function implements an algorithm of Plesken and Souvignier, following
+ Souvignier's implementation.
+
+Variant: Also available is \fun{GEN}{qfisom}{GEN G, GEN H, GEN fl}
+ where $G$ is a vector of \kbd{zm}, and $H$ is a \kbd{zm}.
diff --git a/src/functions/linear_algebra/qfisominit b/src/functions/linear_algebra/qfisominit
new file mode 100644
index 0000000..46a791d
--- /dev/null
+++ b/src/functions/linear_algebra/qfisominit
@@ -0,0 +1,28 @@
+Function: qfisominit
+Section: linear_algebra
+C-Name: qfisominit0
+Prototype: GDG
+Help: qfisominit(G,{fl}): G being a square and symmetric matrix representing an
+ integral positive definite quadratic form, this function return a structure
+ allowing to compute isomorphisms between G and other quadratic form faster.
+Doc:
+ $G$ being a square and symmetric matrix with integer entries representing a
+ positive definite quadratic form, return an \kbd{isom} structure allowing to
+ compute isomorphisms between $G$ and other quadratic forms faster.
+
+ The interface of this function is experimental and will likely change in future
+ release.
+
+ If present, the optional parameter \var{fl} must be a \typ{VEC} with two
+ components. It allows to specify the invariants used, which can make the
+ computation faster or slower. The components are
+
+ \item \kbd{fl[1]} Depth of scalar product combination to use.
+
+ \item \kbd{fl[2]} Maximum level of Bacher polynomials to use.
+
+ Since this function computes the minimal vectors, it can become very lengthy
+ as the dimension of $G$ grows.
+Variant: Also available is
+ \fun{GEN}{qfisominit}{GEN F, GEN fl}
+ where $F$ is a vector of \kbd{zm}.
diff --git a/src/functions/linear_algebra/qfjacobi b/src/functions/linear_algebra/qfjacobi
new file mode 100644
index 0000000..238dd8d
--- /dev/null
+++ b/src/functions/linear_algebra/qfjacobi
@@ -0,0 +1,34 @@
+Function: qfjacobi
+Section: linear_algebra
+C-Name: jacobi
+Prototype: Gp
+Help: qfjacobi(A): eigenvalues and orthogonal matrix of eigenvectors of the
+ real symmetric matrix A.
+Doc: apply Jacobi's eigenvalue algorithm to the real symmetric matrix $A$.
+ This returns $[L, V]$, where
+
+ \item $L$ is the vector of (real) eigenvalues of $A$, sorted in increasing
+ order,
+
+ \item $V$ is the corresponding orthogonal matrix of eigenvectors of $A$.
+
+ \bprog
+ ? \p19
+ ? A = [1,2;2,1]; mateigen(A)
+ %1 =
+ [-1 1]
+
+ [ 1 1]
+ ? [L, H] = qfjacobi(A);
+ ? L
+ %3 = [-1.000000000000000000, 3.000000000000000000]~
+ ? H
+ %4 =
+ [ 0.7071067811865475245 0.7071067811865475244]
+
+ [-0.7071067811865475244 0.7071067811865475245]
+ ? norml2( (A-L[1])*H[,1] )       \\ approximate eigenvector
+ %5 = 9.403954806578300064 E-38
+ ? norml2(H*H~ - 1)
+ %6 = 2.350988701644575016 E-38   \\ close to orthogonal
+ @eprog
diff --git a/src/functions/linear_algebra/qflll b/src/functions/linear_algebra/qflll
new file mode 100644
index 0000000..72456f9
--- /dev/null
+++ b/src/functions/linear_algebra/qflll
@@ -0,0 +1,63 @@
+Function: qflll
+Section: linear_algebra
+C-Name: qflll0
+Prototype: GD0,L,
+Help: qflll(x,{flag=0}): LLL reduction of the vectors forming the matrix x
+ (gives the unimodular transformation matrix T such that x*T is LLL-reduced). flag is
+ optional, and can be 0: default, 1: assumes x is integral, 2: assumes x is
+ integral, returns a partially reduced basis,
+ 4: assumes x is integral, returns [K,T] where K is the integer kernel of x
+ and T the LLL reduced image, 5: same as 4 but x may have polynomial
+ coefficients, 8: same as 0 but x may have polynomial coefficients.
+Description:
+ (vec, ?0):vec       lll($1)
+ (vec, 1):vec        lllint($1)
+ (vec, 2):vec        lllintpartial($1)
+ (vec, 4):vec        lllkerim($1)
+ (vec, 5):vec        lllkerimgen($1)
+ (vec, 8):vec        lllgen($1)
+ (vec, #small):vec   $"Bad flag in qflll"
+ (vec, small):vec    qflll0($1, $2)
+Doc: \idx{LLL} algorithm applied to the
+ \emph{columns} of the matrix $x$. The columns of $x$ may be linearly
+ dependent. The result is a unimodular transformation matrix $T$ such that $x
+ \cdot T$ is an LLL-reduced basis of the lattice generated by the column
+ vectors of $x$. Note that if $x$ is not of maximal rank $T$ will not be
+ square. The LLL parameters are $(0.51,0.99)$, meaning that the Gram-Schmidt
+ coefficients for the final basis satisfy $\mu_{i,j} \leq |0.51|$, and the
+ Lov\'{a}sz's constant is $0.99$.
+
+ If $\fl=0$ (default), assume that $x$ has either exact (integral or
+ rational) or real floating point entries. The matrix is rescaled, converted
+ to integers and the behavior is then as in $\fl = 1$.
+
+ If $\fl=1$, assume that $x$ is integral. Computations involving Gram-Schmidt
+ vectors are approximate, with precision varying as needed (Lehmer's trick,
+ as generalized by Schnorr). Adapted from Nguyen and Stehl\'e's algorithm
+ and Stehl\'e's code (\kbd{fplll-1.3}).
+
+ If $\fl=2$, $x$ should be an integer matrix whose columns are linearly
+ independent. Returns a partially reduced basis for $x$, using an unpublished
+ algorithm by Peter Montgomery: a basis is said to be \emph{partially reduced}
+ if $|v_i \pm v_j| \geq |v_i|$ for any two distinct basis vectors $v_i, \,
+ v_j$.
+
+ This is faster than $\fl=1$, esp. when one row is huge compared
+ to the other rows (knapsack-style), and should quickly produce relatively
+ short vectors. The resulting basis is \emph{not} LLL-reduced in general.
+ If LLL reduction is eventually desired, avoid this partial reduction:
+ applying LLL to the partially reduced matrix is significantly \emph{slower}
+ than starting from a knapsack-type lattice.
+
+ If $\fl=4$, as $\fl=1$, returning a vector $[K, T]$ of matrices: the
+ columns of $K$ represent a basis of the integer kernel of $x$
+ (not LLL-reduced in general) and $T$ is the transformation
+ matrix such that $x\cdot T$ is an LLL-reduced $\Z$-basis of the image
+ of the matrix $x$.
+
+ If $\fl=5$, case as case $4$, but $x$ may have polynomial coefficients.
+
+ If $\fl=8$, same as case $0$, but $x$ may have polynomial coefficients.
+
+Variant: Also available are \fun{GEN}{lll}{GEN x} ($\fl=0$),
+ \fun{GEN}{lllint}{GEN x} ($\fl=1$), and \fun{GEN}{lllkerim}{GEN x} ($\fl=4$).
diff --git a/src/functions/linear_algebra/qflllgram b/src/functions/linear_algebra/qflllgram
new file mode 100644
index 0000000..b67d8d2
--- /dev/null
+++ b/src/functions/linear_algebra/qflllgram
@@ -0,0 +1,36 @@
+Function: qflllgram
+Section: linear_algebra
+C-Name: qflllgram0
+Prototype: GD0,L,
+Help: qflllgram(G,{flag=0}): LLL reduction of the lattice whose gram matrix
+ is G (gives the unimodular transformation matrix). flag is optional and can
+ be 0: default,1: assumes x is integral, 4: assumes x is integral,
+ returns [K,T],  where K is the integer kernel of x
+ and T the LLL reduced image, 5: same as 4 but x may have polynomial
+ coefficients, 8: same as 0 but x may have polynomial coefficients.
+Doc: same as \kbd{qflll}, except that the
+ matrix $G = \kbd{x\til * x}$ is the Gram matrix of some lattice vectors $x$,
+ and not the coordinates of the vectors themselves. In particular, $G$ must
+ now be a square symmetric real matrix, corresponding to a positive
+ quadratic form (not necessarily definite: $x$ needs not have maximal rank).
+ The result is a unimodular
+ transformation matrix $T$ such that $x \cdot T$ is an LLL-reduced basis of
+ the lattice generated by the column vectors of $x$. See \tet{qflll} for
+ further details about the LLL implementation.
+
+ If $\fl=0$ (default), assume that $G$ has either exact (integral or
+ rational) or real floating point entries. The matrix is rescaled, converted
+ to integers and the behavior is then as in $\fl = 1$.
+
+ If $\fl=1$, assume that $G$ is integral. Computations involving Gram-Schmidt
+ vectors are approximate, with precision varying as needed (Lehmer's trick,
+ as generalized by Schnorr). Adapted from Nguyen and Stehl\'e's algorithm
+ and Stehl\'e's code (\kbd{fplll-1.3}).
+
+ $\fl=4$: $G$ has integer entries, gives the kernel and reduced image of $x$.
+
+ $\fl=5$: same as $4$, but $G$ may have polynomial coefficients.
+
+Variant: Also available are \fun{GEN}{lllgram}{GEN G} ($\fl=0$),
+ \fun{GEN}{lllgramint}{GEN G} ($\fl=1$), and \fun{GEN}{lllgramkerim}{GEN G}
+ ($\fl=4$).
diff --git a/src/functions/linear_algebra/qfminim b/src/functions/linear_algebra/qfminim
new file mode 100644
index 0000000..5c51959
--- /dev/null
+++ b/src/functions/linear_algebra/qfminim
@@ -0,0 +1,97 @@
+Function: qfminim
+Section: linear_algebra
+C-Name: qfminim0
+Prototype: GDGDGD0,L,p
+Help: qfminim(x,{b},{m},{flag=0}): x being a square and symmetric
+ matrix representing a positive definite quadratic form, this function
+ deals with the vectors of x whose norm is less than or equal to b,
+ enumerated using the Fincke-Pohst algorithm, storing at most m vectors (no
+ limit if m is omitted). The function searches for
+ the minimal non-zero vectors if b is omitted. The precise behavior
+ depends on flag. 0: seeks at most 2m vectors (unless m omitted), returns
+ [N,M,mat] where N is the number of vectors found, M the maximum norm among
+ these, and mat lists half the vectors (the other half is given by -mat). 1:
+ ignores m and returns the first vector whose norm is less than b. 2: as 0
+ but uses a more robust, slower implementation, valid for non integral
+ quadratic forms.
+Doc: $x$ being a square and symmetric matrix representing a positive definite
+ quadratic form, this function deals with the vectors of $x$ whose norm is
+ less than or equal to $b$, enumerated using the Fincke-Pohst algorithm,
+ storing at most $m$ vectors (no limit if $m$ is omitted). The function
+ searches for the minimal non-zero vectors if $b$ is omitted. The behavior is
+ undefined if $x$ is not positive definite (a ``precision too low'' error is
+ most likely, although more precise error messages are possible). The precise
+ behavior depends on $\fl$.
+
+ If $\fl=0$ (default), seeks at most $2m$ vectors. The result is a
+ three-component vector, the first component being the number of vectors
+ found, the second being the maximum norm found, and the last vector is a
+ matrix whose columns are the vectors found, only one being given for each
+ pair $\pm v$ (at most $m$ such pairs, unless $m$ was omitted). The vectors
+ are returned in no particular order.
+
+ If $\fl=1$, ignores $m$ and returns $[N,v]$, where $v$ is a non-zero vector
+ of length $N \leq b$, or $[]$ if no non-zero vector has length $\leq b$.
+ If no explicit $b$ is provided, return a vector of smallish norm
+ (smallest vector in an LLL-reduced basis).
+
+ In these two cases, $x$ must have \emph{integral} entries. The
+ implementation uses low precision floating point computations for maximal
+ speed, which gives incorrect result when $x$ has large entries. (The
+ condition is checked in the code and the routine raises an error if
+ large rounding errors occur.) A more robust, but much slower,
+ implementation is chosen if the following flag is used:
+
+ If $\fl=2$, $x$ can have non integral real entries. In this case, if $b$
+ is omitted, the ``minimal'' vectors only have approximately the same norm.
+ If $b$ is omitted, $m$ is an upper bound for the number of vectors that
+ will be stored and returned, but all minimal vectors are nevertheless
+ enumerated. If $m$ is omitted, all vectors found are stored and returned;
+ note that this may be a huge vector!
+
+ \bprog
+ ? x = matid(2);
+ ? qfminim(x)  \\@com 4 minimal vectors of norm 1: $\pm[0,1]$, $\pm[1,0]$
+ %2 = [4, 1, [0, 1; 1, 0]]
+ ? { x =
+ [4, 2, 0, 0, 0,-2, 0, 0, 0, 0, 0, 0, 1,-1, 0, 0, 0, 1, 0,-1, 0, 0, 0,-2;
+  2, 4,-2,-2, 0,-2, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0,-1, 0, 1,-1,-1;
+  0,-2, 4, 0,-2, 0, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1, 0, 0, 1,-1,-1, 0, 0;
+  0,-2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1,-1, 0, 0, 0, 1,-1, 0, 1,-1, 1, 0;
+  0, 0,-2, 0, 4, 0, 0, 0, 1,-1, 0, 0, 1, 0, 0, 0,-2, 0, 0,-1, 1, 1, 0, 0;
+ -2, -2,0, 0, 0, 4,-2, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,-1, 1, 1;
+  0, 0, 0, 0, 0,-2, 4,-2, 0, 0, 0, 0, 0, 1, 0, 0, 0,-1, 0, 0, 0, 1,-1, 0;
+  0, 0, 0, 0, 0, 0,-2, 4, 0, 0, 0, 0,-1, 0, 0, 0, 0, 0,-1,-1,-1, 0, 1, 0;
+  0, 0, 0, 0, 1,-1, 0, 0, 4, 0,-2, 0, 1, 1, 0,-1, 0, 1, 0, 0, 0, 0, 0, 0;
+  0, 0, 0, 0,-1, 0, 0, 0, 0, 4, 0, 0, 1, 1,-1, 1, 0, 0, 0, 1, 0, 0, 1, 0;
+  0, 0, 0, 0, 0, 0, 0, 0,-2, 0, 4,-2, 0,-1, 0, 0, 0,-1, 0,-1, 0, 0, 0, 0;
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-2, 4,-1, 1, 0, 0,-1, 1, 0, 1, 1, 1,-1, 0;
+  1, 0,-1, 1, 1, 0, 0,-1, 1, 1, 0,-1, 4, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1,-1;
+ -1,-1, 1,-1, 0, 0, 1, 0, 1, 1,-1, 1, 0, 4, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1;
+  0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 1, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0;
+  0, 0, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1, 1, 0, 4, 0, 0, 0, 0, 1, 1, 0, 0;
+  0, 0, 1, 0,-2, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 4, 1, 1, 1, 0, 0, 1, 1;
+  1, 0, 0, 1, 0, 0,-1, 0, 1, 0,-1, 1, 1, 0, 0, 0, 1, 4, 0, 1, 1, 0, 1, 0;
+  0, 0, 0,-1, 0, 1, 0,-1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 4, 0, 1, 1, 0, 1;
+ -1, -1,1, 0,-1, 1, 0,-1, 0, 1,-1, 1, 0, 1, 0, 0, 1, 1, 0, 4, 0, 0, 1, 1;
+  0, 0,-1, 1, 1, 0, 0,-1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 4, 1, 0, 1;
+  0, 1,-1,-1, 1,-1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 4, 0, 1;
+  0,-1, 0, 1, 0, 1,-1, 1, 0, 1, 0,-1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 4, 1;
+ -2,-1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 4]; }
+ ? qfminim(x,,0)  \\ the Leech lattice has 196560 minimal vectors of norm 4
+ time = 648 ms.
+ %4 = [196560, 4, [;]]
+ ? qfminim(x,,0,2); \\ safe algorithm. Slower and unnecessary here.
+ time = 18,161 ms.
+ %5 = [196560, 4.000061035156250000, [;]]
+ @eprog\noindent\sidx{Leech lattice}\sidx{minimal vector}
+ In the last example, we store 0 vectors to limit memory use. All minimal
+ vectors are nevertheless enumerated. Provided \kbd{parisize} is about 50MB,
+ \kbd{qfminim(x)} succeeds in 2.5 seconds.
+
+Variant: Also available are
+ \fun{GEN}{minim}{GEN x, GEN b = NULL, GEN m = NULL} ($\fl=0$),
+ \fun{GEN}{minim2}{GEN x, GEN b = NULL, GEN m = NULL} ($\fl=1$).
+ \fun{GEN}{minim_raw}{GEN x, GEN b = NULL, GEN m = NULL} (do not perform LLL
+ reduction on x).
+
diff --git a/src/functions/linear_algebra/qfnorm b/src/functions/linear_algebra/qfnorm
new file mode 100644
index 0000000..1ae36d0
--- /dev/null
+++ b/src/functions/linear_algebra/qfnorm
@@ -0,0 +1,38 @@
+Function: qfnorm
+Section: linear_algebra
+C-Name: qfnorm
+Prototype: GDG
+Help: qfnorm(x,{q}): evaluate the binary quadratic form q (symmetric matrix)
+ at x; if q omitted, use the standard Euclidean form.
+Doc: evaluate the binary quadratic form $q$ (symmetric matrix)
+ at the vector $x$. If $q$ omitted, use the standard Euclidean form,
+ corresponding to the identity matrix.
+
+ Equivalent to \kbd{x\til * q * x}, but about twice faster and
+ more convenient (does not distinguish between column and row vectors):
+ \bprog
+ ? x = [1,2,3]~; qfnorm(x)
+ %1 = 14
+ ? q = [1,2,3;2,2,-1;3,-1,0]; qfnorm(x, q)
+ %2 = 23
+ ? for(i=1,10^6, qfnorm(x,q))
+ time = 384ms.
+ ? for(i=1,10^6, x~*q*x)
+ time = 729ms.
+ @eprog\noindent We also allow \typ{MAT}s of compatible dimensions for $x$,
+ and return \kbd{x\til * q * x} in this case as well:
+ \bprog
+ ? M = [1,2,3;4,5,6;7,8,9]; qfnorm(M) \\ Gram matrix
+ %5 =
+ [66  78  90]
+
+ [78  93 108]
+
+ [90 108 126]
+
+ ? for(i=1,10^6, qfnorm(M,q))
+ time = 2,144 ms.
+ ? for(i=1,10^6, M~*q*M)
+ time = 2,793 ms.
+ @eprog
+ \noindent The polar form is also available, as \tet{qfbil}.
diff --git a/src/functions/linear_algebra/qfperfection b/src/functions/linear_algebra/qfperfection
new file mode 100644
index 0000000..47ae267
--- /dev/null
+++ b/src/functions/linear_algebra/qfperfection
@@ -0,0 +1,15 @@
+Function: qfperfection
+Section: linear_algebra
+C-Name: perf
+Prototype: G
+Help: qfperfection(G): rank of matrix of xx~ for x minimal vectors of a gram
+ matrix G.
+Doc:
+ $G$ being a square and symmetric matrix with
+ integer entries representing a positive definite quadratic form, outputs the
+ perfection rank of the form. That is, gives the rank of the family of the $s$
+ symmetric matrices $v_iv_i^t$, where $s$ is half the number of minimal
+ vectors and the $v_i$ ($1\le i\le s$) are the minimal vectors.
+
+ Since this requires computing the minimal vectors, the computations can
+ become very lengthy as the dimension of $x$ grows.
diff --git a/src/functions/linear_algebra/qfrep b/src/functions/linear_algebra/qfrep
new file mode 100644
index 0000000..cb9c700
--- /dev/null
+++ b/src/functions/linear_algebra/qfrep
@@ -0,0 +1,28 @@
+Function: qfrep
+Section: linear_algebra
+C-Name: qfrep0
+Prototype: GGD0,L,
+Help: qfrep(q,B,{flag=0}): vector of (half) the number of vectors of norms
+ from 1 to B for the integral and definite quadratic form q. If flag is 1,
+ count vectors of even norm from 1 to 2B.
+Doc:
+ $q$ being a square and symmetric matrix with integer entries representing a
+ positive definite quadratic form, count the vectors representing successive
+ integers.
+
+ \item If $\fl = 0$, count all vectors. Outputs the vector whose $i$-th
+ entry, $1 \leq i \leq B$ is half the number of vectors $v$ such that $q(v)=i$.
+
+ \item If $\fl = 1$, count vectors of even norm. Outputs the vector
+ whose $i$-th entry, $1 \leq i \leq B$ is half the number of vectors such
+ that $q(v) = 2i$.
+
+ \bprog
+ ? q = [2, 1; 1, 3];
+ ? qfrep(q, 5)
+ %2 = Vecsmall([0, 1, 2, 0, 0]) \\ 1 vector of norm 2, 2 of norm 3, etc.
+ ? qfrep(q, 5, 1)
+ %3 = Vecsmall([1, 0, 0, 1, 0]) \\ 1 vector of norm 2, 0 of norm 4, etc.
+ @eprog\noindent
+ This routine uses a naive algorithm based on \tet{qfminim}, and
+ will fail if any entry becomes larger than $2^{31}$ (or $2^{63}$).
diff --git a/src/functions/linear_algebra/qfsign b/src/functions/linear_algebra/qfsign
new file mode 100644
index 0000000..bc06dbb
--- /dev/null
+++ b/src/functions/linear_algebra/qfsign
@@ -0,0 +1,10 @@
+Function: qfsign
+Section: linear_algebra
+C-Name: qfsign
+Prototype: G
+Help: qfsign(x): signature of the symmetric matrix x.
+Doc:
+ returns $[p,m]$ the signature of the quadratic form represented by the
+ symmetric matrix $x$. Namely, $p$ (resp.~$m$) is the number of positive
+ (resp.~negative) eigenvalues of $x$.The result is computed using Gaussian
+ reduction.
diff --git a/src/functions/linear_algebra/seralgdep b/src/functions/linear_algebra/seralgdep
new file mode 100644
index 0000000..a7207b0
--- /dev/null
+++ b/src/functions/linear_algebra/seralgdep
@@ -0,0 +1,21 @@
+Function: seralgdep
+Section: linear_algebra
+C-Name: seralgdep
+Prototype: GLL
+Help: seralgdep(s,p,r): find a linear relation between powers (1,s, ..., s^p)
+ of the series s, with polynomial coefficients of degree <= r.
+Doc: \sidx{algebraic dependence} finds a linear relation between powers $(1,s,
+ \dots, s^p)$ of the series $s$, with polynomial coefficients of degree
+ $\leq r$. In case no relation is found, return $0$.
+ \bprog
+ ? s = 1 + 10*y - 46*y^2 + 460*y^3 - 5658*y^4 + 77740*y^5 + O(y^6);
+ ? seralgdep(s, 2, 2)
+ %2 = -x^2 + (8*y^2 + 20*y + 1)
+ ? subst(%, x, s)
+ %3 = O(y^6)
+ ? seralgdep(s, 1, 3)
+ %4 = (-77*y^2 - 20*y - 1)*x + (310*y^3 + 231*y^2 + 30*y + 1)
+ ? seralgdep(s, 1, 2)
+ %5 = 0
+ @eprog\noindent The series main variable must not be $x$, so as to be able
+ to express the result as a polynomial in $x$.
diff --git a/src/functions/linear_algebra/setbinop b/src/functions/linear_algebra/setbinop
new file mode 100644
index 0000000..455ad18
--- /dev/null
+++ b/src/functions/linear_algebra/setbinop
@@ -0,0 +1,18 @@
+Function: setbinop
+Section: linear_algebra
+C-Name: setbinop
+Prototype: GGDG
+Help: setbinop(f,X,{Y}): the set {f(x,y), x in X, y in Y}. If Y is omitted,
+ assume that X = Y and that f is symmetric.
+Doc: the set whose elements are the f(x,y), where x,y run through X,Y.
+ respectively. If $Y$ is omitted, assume that $X = Y$ and that $f$ is symmetric:
+ $f(x,y) = f(y,x)$ for all $x,y$ in $X$.
+ \bprog
+ ? X = [1,2,3]; Y = [2,3,4];
+ ? setbinop((x,y)->x+y, X,Y) \\ set X + Y
+ %2 = [3, 4, 5, 6, 7]
+ ? setbinop((x,y)->x-y, X,Y) \\ set X - Y
+ %3 = [-3, -2, -1, 0, 1]
+ ? setbinop((x,y)->x+y, X)   \\ set 2X = X + X
+ %2 = [2, 3, 4, 5, 6]
+ @eprog
diff --git a/src/functions/linear_algebra/setintersect b/src/functions/linear_algebra/setintersect
new file mode 100644
index 0000000..14713d1
--- /dev/null
+++ b/src/functions/linear_algebra/setintersect
@@ -0,0 +1,9 @@
+Function: setintersect
+Section: linear_algebra
+C-Name: setintersect
+Prototype: GG
+Help: setintersect(x,y): intersection of the sets x and y.
+Description:
+ (vec, vec):vec        setintersect($1, $2)
+Doc: intersection of the two sets $x$ and $y$ (see \kbd{setisset}).
+ If $x$ or $y$ is not a set, the result is undefined.
diff --git a/src/functions/linear_algebra/setisset b/src/functions/linear_algebra/setisset
new file mode 100644
index 0000000..df96394
--- /dev/null
+++ b/src/functions/linear_algebra/setisset
@@ -0,0 +1,19 @@
+Function: setisset
+Section: linear_algebra
+C-Name: setisset
+Prototype: lG
+Help: setisset(x): true(1) if x is a set (row vector with strictly
+ increasing entries), false(0) if not.
+Doc:
+ returns true (1) if $x$ is a set, false (0) if
+ not. In PARI, a set is a row vector whose entries are strictly
+ increasing with respect to a (somewhat arbitrary) universal comparison
+ function. To convert any object into a set (this is most useful for
+ vectors, of course), use the function \kbd{Set}.
+ \bprog
+ ? a = [3, 1, 1, 2];
+ ? setisset(a)
+ %2 = 0
+ ? Set(a)
+ %3 = [1, 2, 3]
+ @eprog
diff --git a/src/functions/linear_algebra/setminus b/src/functions/linear_algebra/setminus
new file mode 100644
index 0000000..d30502c
--- /dev/null
+++ b/src/functions/linear_algebra/setminus
@@ -0,0 +1,10 @@
+Function: setminus
+Section: linear_algebra
+C-Name: setminus
+Prototype: GG
+Help: setminus(x,y): set of elements of x not belonging to y.
+Description:
+ (vec, vec):vec        setminus($1, $2)
+Doc: difference of the two sets $x$ and $y$ (see \kbd{setisset}),
+ i.e.~set of elements of $x$ which do not belong to $y$.
+ If $x$ or $y$ is not a set, the result is undefined.
diff --git a/src/functions/linear_algebra/setsearch b/src/functions/linear_algebra/setsearch
new file mode 100644
index 0000000..f15f3c9
--- /dev/null
+++ b/src/functions/linear_algebra/setsearch
@@ -0,0 +1,45 @@
+Function: setsearch
+Section: linear_algebra
+C-Name: setsearch
+Prototype: lGGD0,L,
+Help: setsearch(S,x,{flag=0}): determines whether x belongs to the set (or
+ sorted list) S.
+ If flag is 0 or omitted, returns 0 if it does not, otherwise returns the index
+ j such that x==S[j]. If flag is non-zero, return 0 if x belongs to S,
+ otherwise the index j where it should be inserted.
+Doc: determines whether $x$ belongs to the set $S$ (see \kbd{setisset}).
+
+ We first describe the default behaviour, when $\fl$ is zero or omitted. If $x$
+ belongs to the set $S$, returns the index $j$ such that $S[j]=x$, otherwise
+ returns 0.
+ \bprog
+ ? T = [7,2,3,5]; S = Set(T);
+ ? setsearch(S, 2)
+ %2 = 1
+ ? setsearch(S, 4)      \\ not found
+ %3 = 0
+ ? setsearch(T, 7)      \\ search in a randomly sorted vector
+ %4 = 0 \\ WRONG !
+ @eprog\noindent
+ If $S$ is not a set, we also allow sorted lists with
+ respect to the \tet{cmp} sorting function, without repeated entries,
+ as per \tet{listsort}$(L,1)$; otherwise the result is undefined.
+ \bprog
+ ? L = List([1,4,2,3,2]); setsearch(L, 4)
+ %1 = 0 \\ WRONG !
+ ? listsort(L, 1); L    \\ sort L first
+ %2 = List([1, 2, 3, 4])
+ ? setsearch(L, 4)
+ %3 = 4                 \\ now correct
+ @eprog\noindent
+ If $\fl$ is non-zero, this function returns the index $j$ where $x$ should be
+ inserted, and $0$ if it already belongs to $S$. This is meant to be used for
+ dynamically growing (sorted) lists, in conjunction with \kbd{listinsert}.
+ \bprog
+ ? L = List([1,5,2,3,2]); listsort(L,1); L
+ %1 = List([1,2,3,5])
+ ? j = setsearch(L, 4, 1)  \\ 4 should have been inserted at index j
+ %2 = 4
+ ? listinsert(L, 4, j); L
+ %3 = List([1, 2, 3, 4, 5])
+ @eprog
diff --git a/src/functions/linear_algebra/setunion b/src/functions/linear_algebra/setunion
new file mode 100644
index 0000000..a2cc92a
--- /dev/null
+++ b/src/functions/linear_algebra/setunion
@@ -0,0 +1,9 @@
+Function: setunion
+Section: linear_algebra
+C-Name: setunion
+Prototype: GG
+Help: setunion(x,y): union of the sets x and y.
+Description:
+ (vec, vec):vec        setunion($1, $2)
+Doc: union of the two sets $x$ and $y$ (see \kbd{setisset}).
+ If $x$ or $y$ is not a set, the result is undefined.
diff --git a/src/functions/linear_algebra/trace b/src/functions/linear_algebra/trace
new file mode 100644
index 0000000..ef23a63
--- /dev/null
+++ b/src/functions/linear_algebra/trace
@@ -0,0 +1,11 @@
+Function: trace
+Section: linear_algebra
+C-Name: gtrace
+Prototype: G
+Help: trace(x): trace of x.
+Doc: this applies to quite general $x$. If $x$ is not a
+ matrix, it is equal to the sum of $x$ and its conjugate, except for polmods
+ where it is the trace as an algebraic number.
+
+ For $x$ a square matrix, it is the ordinary trace. If $x$ is a
+ non-square matrix (but not a vector), an error occurs.
diff --git a/src/functions/linear_algebra/vecextract b/src/functions/linear_algebra/vecextract
new file mode 100644
index 0000000..11f38ae
--- /dev/null
+++ b/src/functions/linear_algebra/vecextract
@@ -0,0 +1,71 @@
+Function: vecextract
+Section: linear_algebra
+C-Name: extract0
+Prototype: GGDG
+Description:
+ (vec,gen,?gen):vec  extract0($1, $2, $3)
+Help: vecextract(x,y,{z}): extraction of the components of the matrix or
+ vector x according to y and z. If z is omitted, y represents columns, otherwise
+ y corresponds to rows and z to columns. y and z can be vectors (of indices),
+ strings (indicating ranges as in "1..10") or masks (integers whose binary
+ representation indicates the indices to extract, from left to right 1, 2, 4,
+ 8, etc.).
+Doc: extraction of components of the vector or matrix $x$ according to $y$.
+ In case $x$ is a matrix, its components are the \emph{columns} of $x$. The
+ parameter $y$ is a component specifier, which is either an integer, a string
+ describing a range, or a vector.
+
+ If $y$ is an integer, it is considered as a mask: the binary bits of $y$ are
+ read from right to left, but correspond to taking the components from left to
+ right. For example, if $y=13=(1101)_2$ then the components 1,3 and 4 are
+ extracted.
+
+ If $y$ is a vector (\typ{VEC}, \typ{COL} or \typ{VECSMALL}), which must have
+ integer entries, these entries correspond to the component numbers to be
+ extracted, in the order specified.
+
+ If $y$ is a string, it can be
+
+ \item a single (non-zero) index giving a component number (a negative
+ index means we start counting from the end).
+
+ \item a range of the form \kbd{"$a$..$b$"}, where $a$ and $b$ are
+ indexes as above. Any of $a$ and $b$ can be omitted; in this case, we take
+ as default values $a = 1$ and $b = -1$, i.e.~ the first and last components
+ respectively. We then extract all components in the interval $[a,b]$, in
+ reverse order if $b < a$.
+
+ In addition, if the first character in the string is \kbd{\pow}, the
+ complement of the given set of indices is taken.
+
+ If $z$ is not omitted, $x$ must be a matrix. $y$ is then the \emph{row}
+ specifier, and $z$ the \emph{column} specifier, where the component specifier
+ is as explained above.
+
+ \bprog
+ ? v = [a, b, c, d, e];
+ ? vecextract(v, 5)         \\@com mask
+ %1 = [a, c]
+ ? vecextract(v, [4, 2, 1]) \\@com component list
+ %2 = [d, b, a]
+ ? vecextract(v, "2..4")    \\@com interval
+ %3 = [b, c, d]
+ ? vecextract(v, "-1..-3")  \\@com interval + reverse order
+ %4 = [e, d, c]
+ ? vecextract(v, "^2")      \\@com complement
+ %5 = [a, c, d, e]
+ ? vecextract(matid(3), "2..", "..")
+ %6 =
+ [0 1 0]
+
+ [0 0 1]
+ @eprog
+ The range notations \kbd{v[i..j]} and \kbd{v[\pow i]} (for \typ{VEC} or
+ \typ{COL}) and \kbd{M[i..j, k..l]} and friends (for \typ{MAT}) implement a
+ subset of the above, in a simpler and \emph{faster} way, hence should be
+ preferred in most common situations. The following features are not
+ implemented in the range notation:
+
+ \item reverse order,
+
+ \item omitting either $a$ or $b$ in \kbd{$a$..$b$}.
diff --git a/src/functions/linear_algebra/vecsearch b/src/functions/linear_algebra/vecsearch
new file mode 100644
index 0000000..7eb6510
--- /dev/null
+++ b/src/functions/linear_algebra/vecsearch
@@ -0,0 +1,45 @@
+Function: vecsearch
+Section: linear_algebra
+C-Name: vecsearch
+Prototype: lGGDG
+Help: vecsearch(v,x,{cmpf}): determines whether x belongs to the sorted
+ vector v. If the comparison function cmpf is explicitly given, assume
+ that v was sorted according to vecsort(, cmpf).
+Doc: determines whether $x$ belongs to the sorted vector or list $v$: return
+ the (positive) index where $x$ was found, or $0$ if it does not belong to
+ $v$.
+
+ If the comparison function cmpf is omitted, we assume that $v$ is sorted in
+ increasing order, according to the standard comparison function $<$, thereby
+ restricting the possible types for $x$ and the elements of $v$ (integers,
+ fractions or reals).
+
+ If \kbd{cmpf} is present, it is understood as a comparison function and we
+ assume that $v$ is sorted according to it, see \tet{vecsort} for how to
+ encode comparison functions.
+ \bprog
+ ? v = [1,3,4,5,7];
+ ? vecsearch(v, 3)
+ %2 = 2
+ ? vecsearch(v, 6)
+ %3 = 0 \\ not in the list
+ ? vecsearch([7,6,5], 5) \\ unsorted vector: result undefined
+ %4 = 0
+ @eprog
+
+ By abuse of notation, $x$ is also allowed to be a matrix, seen as a vector
+ of its columns; again by abuse of notation, a \typ{VEC} is considered
+ as part of the matrix, if its transpose is one of the matrix columns.
+ \bprog
+ ? v = vecsort([3,0,2; 1,0,2]) \\ sort matrix columns according to lex order
+ %1 =
+ [0 2 3]
+
+ [0 2 1]
+ ? vecsearch(v, [3,1]~)
+ %2 = 3
+ ? vecsearch(v, [3,1])  \\ can search for x or x~
+ %3 = 3
+ ? vecsearch(v, [1,2])
+ %4 = 0 \\ not in the list
+ @eprog\noindent
diff --git a/src/functions/linear_algebra/vecsort b/src/functions/linear_algebra/vecsort
new file mode 100644
index 0000000..4408b3a
--- /dev/null
+++ b/src/functions/linear_algebra/vecsort
@@ -0,0 +1,90 @@
+Function: vecsort
+Section: linear_algebra
+C-Name: vecsort0
+Prototype: GDGD0,L,
+Help: vecsort(x,{cmpf},{flag=0}): sorts the vector of vectors (or matrix) x in
+ ascending order, according to the comparison function cmpf, if not omitted.
+ (If cmpf is an integer, sort according to the value of the k-th component
+ of each entry.) Binary digits of flag (if present) mean: 1: indirect sorting,
+ return the permutation instead of the permuted vector, 2: sort using
+ lexicographic order, 4: use descending instead of ascending order, 8: remove
+ duplicate entries.
+Description:
+ (vecsmall,?gen):vecsmall       vecsort0($1, $2, 0)
+ (vecsmall,?gen,small):vecsmall vecsort0($1, $2, $3)
+ (vec, , ?0):vec                sort($1)
+ (vec, , 1):vecsmall            indexsort($1)
+ (vec, , 2):vec                 lexsort($1)
+ (vec, gen):vec                 vecsort0($1, $2, 0)
+ (vec, ?gen, 1):vecsmall        vecsort0($1, $2, 1)
+ (vec, ?gen, 3):vecsmall        vecsort0($1, $2, 3)
+ (vec, ?gen, 5):vecsmall        vecsort0($1, $2, 5)
+ (vec, ?gen, 7):vecsmall        vecsort0($1, $2, 7)
+ (vec, ?gen, 9):vecsmall        vecsort0($1, $2, 9)
+ (vec, ?gen, 11):vecsmall       vecsort0($1, $2, 11)
+ (vec, ?gen, 13):vecsmall       vecsort0($1, $2, 13)
+ (vec, ?gen, 15):vecsmall       vecsort0($1, $2, 15)
+ (vec, ?gen, #small):vec        vecsort0($1, $2, $3)
+ (vec, ?gen, small):gen         vecsort0($1, $2, $3)
+Doc: sorts the vector $x$ in ascending order, using a mergesort method.
+ $x$ must be a list, vector or matrix (seen as a vector of its columns).
+ Note that mergesort is stable, hence the initial ordering of ``equal''
+ entries (with respect to the sorting criterion) is not changed.
+
+ If \kbd{cmpf} is omitted, we use the standard comparison function
+ \kbd{lex}, thereby restricting the possible types for the elements of $x$
+ (integers, fractions or reals and vectors of those). If \kbd{cmpf} is
+ present, it is understood as a comparison function and we sort according to
+ it. The following possibilities exist:
+
+ \item an integer $k$: sort according to the value of the $k$-th
+ subcomponents of the components of~$x$.
+
+ \item a vector: sort lexicographically according to the components listed in
+ the vector. For example, if $\kbd{cmpf}=\kbd{[2,1,3]}$, sort with respect to
+ the second component, and when these are equal, with respect to the first,
+ and when these are equal, with respect to the third.
+
+ \item a comparison function (\typ{CLOSURE}), with two arguments $x$ and $y$,
+ and returning an integer which is $<0$, $>0$ or $=0$ if $x<y$, $x>y$ or
+ $x=y$ respectively. The \tet{sign} function is very useful in this context:
+ \bprog
+ ? vecsort([3,0,2; 1,0,2]) \\ sort columns according to lex order
+ %1 =
+ [0 2 3]
+
+ [0 2 1]
+ ? vecsort(v, (x,y)->sign(y-x))            \\@com reverse sort
+ ? vecsort(v, (x,y)->sign(abs(x)-abs(y)))  \\@com sort by increasing absolute value
+ ? cmpf(x,y) = my(dx = poldisc(x), dy = poldisc(y)); sign(abs(dx) - abs(dy))
+ ? vecsort([x^2+1, x^3-2, x^4+5*x+1], cmpf)
+ @eprog\noindent
+ The last example used the named \kbd{cmpf} instead of an anonymous function,
+ and sorts polynomials with respect to the absolute value of their
+ discriminant. A more efficient approach would use precomputations to ensure
+ a given discriminant is computed only once:
+ \bprog
+ ? DISC = vector(#v, i, abs(poldisc(v[i])));
+ ? perm = vecsort(vector(#v,i,i), (x,y)->sign(DISC[x]-DISC[y]))
+ ? vecextract(v, perm)
+ @eprog\noindent Similar ideas apply whenever we sort according to the values
+ of a function which is expensive to compute.
+
+ \noindent The binary digits of \fl\ mean:
+
+ \item 1: indirect sorting of the vector $x$, i.e.~if $x$ is an
+ $n$-component vector, returns a permutation of $[1,2,\dots,n]$ which
+ applied to the components of $x$ sorts $x$ in increasing order.
+ For example, \kbd{vecextract(x, vecsort(x,,1))} is equivalent to
+ \kbd{vecsort(x)}.
+
+ \item 4: use descending instead of ascending order.
+
+ \item 8: remove ``duplicate'' entries with respect to the sorting function
+ (keep the first occurring entry).  For example:
+ \bprog
+   ? vecsort([Pi,Mod(1,2),z], (x,y)->0, 8)   \\@com make everything compare equal
+   %1 = [3.141592653589793238462643383]
+   ? vecsort([[2,3],[0,1],[0,3]], 2, 8)
+   %2 = [[0, 1], [2, 3]]
+ @eprog
diff --git a/src/functions/linear_algebra/vecsum b/src/functions/linear_algebra/vecsum
new file mode 100644
index 0000000..0aed1f5
--- /dev/null
+++ b/src/functions/linear_algebra/vecsum
@@ -0,0 +1,6 @@
+Function: vecsum
+Section: linear_algebra
+C-Name: vecsum
+Prototype: G
+Help: vecsum(v): return the sum of the component of the vector v
+Doc: return the sum of the component of the vector $v$
diff --git a/src/functions/linear_algebra/vector b/src/functions/linear_algebra/vector
new file mode 100644
index 0000000..fcd0f63
--- /dev/null
+++ b/src/functions/linear_algebra/vector
@@ -0,0 +1,25 @@
+Function: vector
+Section: linear_algebra
+C-Name: vecteur
+Prototype: GDVDE
+Help: vector(n,{X},{expr=0}): row vector with n components of expression
+ expr (X ranges from 1 to n). By default, fill with 0s.
+Doc: creates a row vector (type
+ \typ{VEC}) with $n$ components whose components are the expression
+ \var{expr} evaluated at the integer points between 1 and $n$. If one of the
+ last two arguments is omitted, fill the vector with zeroes.
+
+ Avoid modifying $X$ within \var{expr}; if you do, the formal variable
+ still runs from $1$ to $n$. In particular, \kbd{vector(n,i,expr)} is not
+ equivalent to
+ \bprog
+ v = vector(n)
+ for (i = 1, n, v[i] = expr)
+ @eprog\noindent
+ as the following example shows:
+ \bprog
+ n = 3
+ v = vector(n); vector(n, i, i++)            ----> [2, 3, 4]
+ v = vector(n); for (i = 1, n, v[i] = i++)   ----> [2, 0, 4]
+ @eprog\noindent
+ %\syn{NO}
diff --git a/src/functions/linear_algebra/vectorsmall b/src/functions/linear_algebra/vectorsmall
new file mode 100644
index 0000000..1c2d967
--- /dev/null
+++ b/src/functions/linear_algebra/vectorsmall
@@ -0,0 +1,12 @@
+Function: vectorsmall
+Section: linear_algebra
+C-Name: vecteursmall
+Prototype: GDVDE
+Help: vectorsmall(n,{X},{expr=0}): VECSMALL with n components of expression
+ expr (X ranges from 1 to n) which must be small integers. By default, fill
+ with 0s.
+Doc: creates a row vector of small integers (type
+ \typ{VECSMALL}) with $n$ components whose components are the expression
+ \var{expr} evaluated at the integer points between 1 and $n$. If one of the
+ last two arguments is omitted, fill the vector with zeroes.
+ %\syn{NO}
diff --git a/src/functions/linear_algebra/vectorv b/src/functions/linear_algebra/vectorv
new file mode 100644
index 0000000..0df7d02
--- /dev/null
+++ b/src/functions/linear_algebra/vectorv
@@ -0,0 +1,8 @@
+Function: vectorv
+Section: linear_algebra
+C-Name: vvecteur
+Prototype: GDVDE
+Help: vectorv(n,{X},{expr=0}): column vector with n components of expression
+ expr (X ranges from 1 to n). By default, fill with 0s.
+Doc: as \tet{vector}, but returns a column vector (type \typ{COL}).
+ %\syn{NO}
diff --git a/src/functions/member_functions/a1 b/src/functions/member_functions/a1
new file mode 100644
index 0000000..4058ec6
--- /dev/null
+++ b/src/functions/member_functions/a1
@@ -0,0 +1,8 @@
+Function: _.a1
+Section: member_functions
+C-Name: member_a1
+Prototype: mG
+Help: _.a1
+Description:
+ (ell):gen:copy        ell_get_a1($1)
+
diff --git a/src/functions/member_functions/a2 b/src/functions/member_functions/a2
new file mode 100644
index 0000000..d1034f0
--- /dev/null
+++ b/src/functions/member_functions/a2
@@ -0,0 +1,8 @@
+Function: _.a2
+Section: member_functions
+C-Name: member_a2
+Prototype: mG
+Help: _.a2
+Description:
+ (ell):gen:copy        ell_get_a2($1)
+
diff --git a/src/functions/member_functions/a3 b/src/functions/member_functions/a3
new file mode 100644
index 0000000..7f7bee2
--- /dev/null
+++ b/src/functions/member_functions/a3
@@ -0,0 +1,8 @@
+Function: _.a3
+Section: member_functions
+C-Name: member_a3
+Prototype: mG
+Help: _.a3
+Description:
+ (ell):gen:copy        ell_get_a3($1)
+
diff --git a/src/functions/member_functions/a4 b/src/functions/member_functions/a4
new file mode 100644
index 0000000..e4d21ec
--- /dev/null
+++ b/src/functions/member_functions/a4
@@ -0,0 +1,8 @@
+Function: _.a4
+Section: member_functions
+C-Name: member_a4
+Prototype: mG
+Help: _.a4
+Description:
+ (ell):gen:copy        ell_get_a4($1)
+
diff --git a/src/functions/member_functions/a6 b/src/functions/member_functions/a6
new file mode 100644
index 0000000..725ddda
--- /dev/null
+++ b/src/functions/member_functions/a6
@@ -0,0 +1,8 @@
+Function: _.a6
+Section: member_functions
+C-Name: member_a6
+Prototype: mG
+Help: _.a6
+Description:
+ (ell):gen:copy         ell_get_a6($1)
+
diff --git a/src/functions/member_functions/area b/src/functions/member_functions/area
new file mode 100644
index 0000000..8b0e416
--- /dev/null
+++ b/src/functions/member_functions/area
@@ -0,0 +1,5 @@
+Function: _.area
+Help: _.area
+Section: member_functions
+C-Name: member_area
+Prototype: mG
diff --git a/src/functions/member_functions/b2 b/src/functions/member_functions/b2
new file mode 100644
index 0000000..8024938
--- /dev/null
+++ b/src/functions/member_functions/b2
@@ -0,0 +1,8 @@
+Function: _.b2
+Section: member_functions
+C-Name: member_b2
+Prototype: mG
+Help: _.b2
+Description:
+ (ell):gen:copy         ell_get_b2($1)
+
diff --git a/src/functions/member_functions/b4 b/src/functions/member_functions/b4
new file mode 100644
index 0000000..8b97453
--- /dev/null
+++ b/src/functions/member_functions/b4
@@ -0,0 +1,8 @@
+Function: _.b4
+Section: member_functions
+C-Name: member_b4
+Prototype: mG
+Help: _.b4
+Description:
+ (ell):gen:copy        ell_get_b4($1)
+
diff --git a/src/functions/member_functions/b6 b/src/functions/member_functions/b6
new file mode 100644
index 0000000..34ee28e
--- /dev/null
+++ b/src/functions/member_functions/b6
@@ -0,0 +1,8 @@
+Function: _.b6
+Section: member_functions
+C-Name: member_b6
+Prototype: mG
+Help: _.b6
+Description:
+ (ell):gen:copy               ell_get_b6($1)
+
diff --git a/src/functions/member_functions/b8 b/src/functions/member_functions/b8
new file mode 100644
index 0000000..4bedfb0
--- /dev/null
+++ b/src/functions/member_functions/b8
@@ -0,0 +1,8 @@
+Function: _.b8
+Section: member_functions
+C-Name: member_b8
+Prototype: mG
+Help: _.b8
+Description:
+ (ell):gen:copy        ell_get_b8($1)
+
diff --git a/src/functions/member_functions/bid b/src/functions/member_functions/bid
new file mode 100644
index 0000000..62ad433
--- /dev/null
+++ b/src/functions/member_functions/bid
@@ -0,0 +1,8 @@
+Function: _.bid
+Section: member_functions
+C-Name: member_bid
+Prototype: mG
+Help: _.bid
+Description:
+ (bnr):gen:copy                 bnr_get_bid($1)
+ (gen):gen:copy                 member_bid($1)
diff --git a/src/functions/member_functions/bnf b/src/functions/member_functions/bnf
new file mode 100644
index 0000000..e321877
--- /dev/null
+++ b/src/functions/member_functions/bnf
@@ -0,0 +1,9 @@
+Function: _.bnf
+Help: _.bnf
+Section: member_functions
+C-Name: member_bnf
+Prototype: mG
+Description:
+ (bnf):bnf:parens               $1
+ (bnr):bnf:copy:parens          $bnf:1
+ (gen):bnf:copy                 member_bnf($1)
diff --git a/src/functions/member_functions/c4 b/src/functions/member_functions/c4
new file mode 100644
index 0000000..d6bb4f8
--- /dev/null
+++ b/src/functions/member_functions/c4
@@ -0,0 +1,8 @@
+Function: _.c4
+Section: member_functions
+C-Name: member_c4
+Prototype: mG
+Help: _.c4
+Description:
+ (ell):gen:copy        ell_get_c4($1)
+
diff --git a/src/functions/member_functions/c6 b/src/functions/member_functions/c6
new file mode 100644
index 0000000..2eeea25
--- /dev/null
+++ b/src/functions/member_functions/c6
@@ -0,0 +1,8 @@
+Function: _.c6
+Section: member_functions
+C-Name: member_c6
+Prototype: mG
+Help: _.c6
+Description:
+ (ell):gen:copy        ell_get_c6($1)
+
diff --git a/src/functions/member_functions/clgp b/src/functions/member_functions/clgp
new file mode 100644
index 0000000..6e0720f
--- /dev/null
+++ b/src/functions/member_functions/clgp
@@ -0,0 +1,10 @@
+Function: _.clgp
+Help: _.clgp
+Section: member_functions
+C-Name: member_clgp
+Prototype: mG
+Description:
+ (bnf):clgp:copy:parens         $clgp:1
+ (bnr):clgp:copy:parens         $clgp:1
+ (clgp):clgp:parens             $1
+ (gen):clgp:copy                member_clgp($1)
diff --git a/src/functions/member_functions/codiff b/src/functions/member_functions/codiff
new file mode 100644
index 0000000..5dddd04
--- /dev/null
+++ b/src/functions/member_functions/codiff
@@ -0,0 +1,5 @@
+Function: _.codiff
+Help: _.codiff
+Section: member_functions
+C-Name: member_codiff
+Prototype: mG
diff --git a/src/functions/member_functions/cyc b/src/functions/member_functions/cyc
new file mode 100644
index 0000000..9105da1
--- /dev/null
+++ b/src/functions/member_functions/cyc
@@ -0,0 +1,10 @@
+Function: _.cyc
+Help: _.cyc
+Section: member_functions
+C-Name: member_cyc
+Prototype: mG
+Description:
+ (bnr):vec:copy                 bnr_get_cyc($1)
+ (bnf):vec:copy                 bnf_get_cyc($1)
+ (clgp):vec:copy                gel($1, 2)
+ (gen):vec:copy                 member_cyc($1)
diff --git a/src/functions/member_functions/diff b/src/functions/member_functions/diff
new file mode 100644
index 0000000..efa3552
--- /dev/null
+++ b/src/functions/member_functions/diff
@@ -0,0 +1,8 @@
+Function: _.diff
+Help: _.diff
+Section: member_functions
+C-Name: member_diff
+Prototype: mG
+Description:
+ (nf):gen:copy                  nf_get_diff($1)
+ (gen):gen:copy                 member_diff($1)
diff --git a/src/functions/member_functions/disc b/src/functions/member_functions/disc
new file mode 100644
index 0000000..8b267cd
--- /dev/null
+++ b/src/functions/member_functions/disc
@@ -0,0 +1,10 @@
+Function: _.disc
+Section: member_functions
+C-Name: member_disc
+Prototype: mG
+Help: _.disc
+Description:
+ (nf):int:copy                  nf_get_disc($1)
+ (ell):gen:copy                 ell_get_disc($1)
+ (gen):gen:copy                 member_disc($1)
+
diff --git a/src/functions/member_functions/e b/src/functions/member_functions/e
new file mode 100644
index 0000000..b6b8ee9
--- /dev/null
+++ b/src/functions/member_functions/e
@@ -0,0 +1,7 @@
+Function: _.e
+Help: _.e
+Section: member_functions
+C-Name: member_e
+Prototype: mG
+Description:
+ (prid):small        pr_get_e($1)
diff --git a/src/functions/member_functions/eta b/src/functions/member_functions/eta
new file mode 100644
index 0000000..303dac0
--- /dev/null
+++ b/src/functions/member_functions/eta
@@ -0,0 +1,5 @@
+Function: _.eta
+Help: _.eta
+Section: member_functions
+C-Name: member_eta
+Prototype: mG
diff --git a/src/functions/member_functions/f b/src/functions/member_functions/f
new file mode 100644
index 0000000..cf0d5e2
--- /dev/null
+++ b/src/functions/member_functions/f
@@ -0,0 +1,7 @@
+Function: _.f
+Help: _.f
+Section: member_functions
+C-Name: member_f
+Prototype: mG
+Description:
+ (prid):small       pr_get_f($1)
diff --git a/src/functions/member_functions/fu b/src/functions/member_functions/fu
new file mode 100644
index 0000000..97f4aa7
--- /dev/null
+++ b/src/functions/member_functions/fu
@@ -0,0 +1,9 @@
+Function: _.fu
+Help: _.fu
+Section: member_functions
+C-Name: member_fu
+Prototype: G
+Description:
+ (bnr):void                $"ray units not implemented"
+ (bnf):gen:copy         bnf_get_fu($1)
+ (gen):gen              member_fu($1)
diff --git a/src/functions/member_functions/futu b/src/functions/member_functions/futu
new file mode 100644
index 0000000..5403590
--- /dev/null
+++ b/src/functions/member_functions/futu
@@ -0,0 +1,5 @@
+Function: _.futu
+Help: _.futu
+Section: member_functions
+C-Name: member_futu
+Prototype: mG
diff --git a/src/functions/member_functions/gen b/src/functions/member_functions/gen
new file mode 100644
index 0000000..f7a634f
--- /dev/null
+++ b/src/functions/member_functions/gen
@@ -0,0 +1,12 @@
+Function: _.gen
+Help: _.gen
+Section: member_functions
+C-Name: member_gen
+Prototype: mG
+Description:
+ (bnr):vec:copy        bnr_get_gen($1)
+ (bnf):vec:copy        bnf_get_gen($1)
+ (gal):vec:copy        gal_get_gen($1)
+ (clgp):vec:copy       gel($1, 3)
+ (prid):gen:copy       pr_get_gen($1)
+ (gen):gen:copy        member_gen($1)
diff --git a/src/functions/member_functions/group b/src/functions/member_functions/group
new file mode 100644
index 0000000..41f5316
--- /dev/null
+++ b/src/functions/member_functions/group
@@ -0,0 +1,8 @@
+Function: _.group
+Help: _.group
+Section: member_functions
+C-Name: member_group
+Prototype: mG
+Description:
+ (gal):vec:copy        gal_get_group($1)
+ (gen):vec:copy        member_group($1)
diff --git a/src/functions/member_functions/index b/src/functions/member_functions/index
new file mode 100644
index 0000000..e1e0631
--- /dev/null
+++ b/src/functions/member_functions/index
@@ -0,0 +1,8 @@
+Function: _.index
+Section: member_functions
+C-Name: member_index
+Prototype: mG
+Help: _.index
+Description:
+ (nf):int:copy                  nf_get_index($1)
+ (gen):int:copy                 member_index($1)
diff --git a/src/functions/member_functions/j b/src/functions/member_functions/j
new file mode 100644
index 0000000..1da36c3
--- /dev/null
+++ b/src/functions/member_functions/j
@@ -0,0 +1,8 @@
+Function: _.j
+Section: member_functions
+C-Name: member_j
+Prototype: mG
+Help: _.j
+Description:
+ (ell):gen:copy        ell_get_j($1)
+
diff --git a/src/functions/member_functions/mod b/src/functions/member_functions/mod
new file mode 100644
index 0000000..8b78f15
--- /dev/null
+++ b/src/functions/member_functions/mod
@@ -0,0 +1,5 @@
+Function: _.mod
+Help: _.mod
+Section: member_functions
+C-Name: member_mod
+Prototype: mG
diff --git a/src/functions/member_functions/nf b/src/functions/member_functions/nf
new file mode 100644
index 0000000..853f2c4
--- /dev/null
+++ b/src/functions/member_functions/nf
@@ -0,0 +1,8 @@
+Function: _.nf
+Help: _.nf
+Section: member_functions
+C-Name: member_nf
+Prototype: mG
+Description:
+ (nf):nf:parens                $1
+ (gen):nf:copy                 member_nf($1)
diff --git a/src/functions/member_functions/no b/src/functions/member_functions/no
new file mode 100644
index 0000000..809d44d
--- /dev/null
+++ b/src/functions/member_functions/no
@@ -0,0 +1,10 @@
+Function: _.no
+Help: _.no
+Section: member_functions
+C-Name: member_no
+Prototype: mG
+Description:
+ (bnr):int:copy                 bnr_get_no($1)
+ (bnf):int:copy                 bnf_get_no($1)
+ (clgp):int:copy                gel($1, 1)
+ (gen):int:copy                 member_no($1)
diff --git a/src/functions/member_functions/omega b/src/functions/member_functions/omega
new file mode 100644
index 0000000..e3a9e1c
--- /dev/null
+++ b/src/functions/member_functions/omega
@@ -0,0 +1,5 @@
+Function: _.omega
+Help: _.omega
+Section: member_functions
+C-Name: member_omega
+Prototype: mG
diff --git a/src/functions/member_functions/orders b/src/functions/member_functions/orders
new file mode 100644
index 0000000..bdc3fff
--- /dev/null
+++ b/src/functions/member_functions/orders
@@ -0,0 +1,7 @@
+Function: _.orders
+Help: _.orders
+Section: member_functions
+C-Name: member_orders
+Prototype: mG
+Description:
+ (gal):vecsmall:copy   gal_get_orders($1)
diff --git a/src/functions/member_functions/p b/src/functions/member_functions/p
new file mode 100644
index 0000000..f3697cd
--- /dev/null
+++ b/src/functions/member_functions/p
@@ -0,0 +1,9 @@
+Function: _.p
+Help: _.p
+Section: member_functions
+C-Name: member_p
+Prototype: mG
+Description:
+ (gal):int:copy                 gal_get_p($1)
+ (prid):int:copy                pr_get_p($1)
+ (gen):int:copy                 member_p($1)
diff --git a/src/functions/member_functions/pol b/src/functions/member_functions/pol
new file mode 100644
index 0000000..529cb94
--- /dev/null
+++ b/src/functions/member_functions/pol
@@ -0,0 +1,9 @@
+Function: _.pol
+Help: _.pol
+Section: member_functions
+C-Name: member_pol
+Prototype: mG
+Description:
+ (gal):gen:copy                 gal_get_pol($1)
+ (nf):gen:copy                  nf_get_pol($1)
+ (gen):gen:copy                 member_pol($1)
diff --git a/src/functions/member_functions/polabs b/src/functions/member_functions/polabs
new file mode 100644
index 0000000..5be2825
--- /dev/null
+++ b/src/functions/member_functions/polabs
@@ -0,0 +1,5 @@
+Function: _.polabs
+Help: _.polabs
+Section: member_functions
+C-Name: member_polabs
+Prototype: mG
diff --git a/src/functions/member_functions/r1 b/src/functions/member_functions/r1
new file mode 100644
index 0000000..cc61e08
--- /dev/null
+++ b/src/functions/member_functions/r1
@@ -0,0 +1,8 @@
+Function: _.r1
+Section: member_functions
+C-Name: member_r1
+Prototype: mG
+Help: _.r1
+Description:
+ (nf):small                     nf_get_r1($1)
+ (gen):int:copy                 member_r1($1)
diff --git a/src/functions/member_functions/r2 b/src/functions/member_functions/r2
new file mode 100644
index 0000000..abd98f1
--- /dev/null
+++ b/src/functions/member_functions/r2
@@ -0,0 +1,8 @@
+Function: _.r2
+Section: member_functions
+C-Name: member_r2
+Prototype: mG
+Help: _.r2
+Description:
+ (nf):small                     nf_get_r2($1)
+ (gen):int:copy                 member_r2($1)
diff --git a/src/functions/member_functions/reg b/src/functions/member_functions/reg
new file mode 100644
index 0000000..77a3c3e
--- /dev/null
+++ b/src/functions/member_functions/reg
@@ -0,0 +1,9 @@
+Function: _.reg
+Help: _.reg
+Section: member_functions
+C-Name: member_reg
+Prototype: mG
+Description:
+ (bnr):real             $"ray regulator not implemented"
+ (bnf):real:copy        bnf_get_reg($1)
+ (gen):real:copy        member_reg($1)
diff --git a/src/functions/member_functions/roots b/src/functions/member_functions/roots
new file mode 100644
index 0000000..0fe865c
--- /dev/null
+++ b/src/functions/member_functions/roots
@@ -0,0 +1,9 @@
+Function: _.roots
+Section: member_functions
+C-Name: member_roots
+Prototype: mG
+Help: _.roots
+Description:
+ (gal):vec:copy                 gal_get_roots($1)
+ (nf):vec:copy                  nf_get_roots($1)
+ (gen):vec:copy                 member_roots($1)
diff --git a/src/functions/member_functions/sign b/src/functions/member_functions/sign
new file mode 100644
index 0000000..8890862
--- /dev/null
+++ b/src/functions/member_functions/sign
@@ -0,0 +1,8 @@
+Function: _.sign
+Help: _.sign
+Section: member_functions
+C-Name: member_sign
+Prototype: mG
+Description:
+ (nf):vec:copy                  gel($1, 2)
+ (gen):vec:copy                 member_sign($1)
diff --git a/src/functions/member_functions/t2 b/src/functions/member_functions/t2
new file mode 100644
index 0000000..89f055d
--- /dev/null
+++ b/src/functions/member_functions/t2
@@ -0,0 +1,7 @@
+Function: _.t2
+Help: _.t2
+Section: member_functions
+C-Name: member_t2
+Prototype: G
+Description:
+ (gen):vec                      member_t2($1)
diff --git a/src/functions/member_functions/tate b/src/functions/member_functions/tate
new file mode 100644
index 0000000..dbb6b44
--- /dev/null
+++ b/src/functions/member_functions/tate
@@ -0,0 +1,5 @@
+Function: _.tate
+Help: _.tate
+Section: member_functions
+C-Name: member_tate
+Prototype: mG
diff --git a/src/functions/member_functions/tu b/src/functions/member_functions/tu
new file mode 100644
index 0000000..c583c28
--- /dev/null
+++ b/src/functions/member_functions/tu
@@ -0,0 +1,7 @@
+Function: _.tu
+Help: _.tu
+Section: member_functions
+C-Name: member_tu
+Prototype: G
+Description:
+ (gen):gen:copy        member_tu($1)
diff --git a/src/functions/member_functions/tufu b/src/functions/member_functions/tufu
new file mode 100644
index 0000000..bf53751
--- /dev/null
+++ b/src/functions/member_functions/tufu
@@ -0,0 +1,5 @@
+Function: _.tufu
+Help: _.tufu
+Section: member_functions
+C-Name: member_tufu
+Prototype: mG
diff --git a/src/functions/member_functions/zk b/src/functions/member_functions/zk
new file mode 100644
index 0000000..6da9a9d
--- /dev/null
+++ b/src/functions/member_functions/zk
@@ -0,0 +1,8 @@
+Function: _.zk
+Help: _.zk
+Section: member_functions
+C-Name: member_zk
+Prototype: mG
+Description:
+ (nf):vec:copy         nf_get_zk($1)
+ (gen):vec:copy        member_zk($1)
diff --git a/src/functions/member_functions/zkst b/src/functions/member_functions/zkst
new file mode 100644
index 0000000..e26295e
--- /dev/null
+++ b/src/functions/member_functions/zkst
@@ -0,0 +1,7 @@
+Function: _.zkst
+Help: _.zkst
+Section: member_functions
+C-Name: member_zkst
+Prototype: mG
+Description:
+ (bnr):gen:copy        bnr_get_bid($1)
diff --git a/src/functions/number_fields/bnfcertify b/src/functions/number_fields/bnfcertify
new file mode 100644
index 0000000..f9dd9ab
--- /dev/null
+++ b/src/functions/number_fields/bnfcertify
@@ -0,0 +1,32 @@
+Function: bnfcertify
+Section: number_fields
+C-Name: bnfcertify0
+Prototype: lGD0,L,
+Help: bnfcertify(bnf,{flag = 0}): certify the correctness (i.e. remove the GRH) of the bnf data output by bnfinit. If flag is present, only certify that the class group is a quotient of the one computed in bnf (much simpler in general).
+Doc: $\var{bnf}$ being as output by
+ \kbd{bnfinit}, checks whether the result is correct, i.e.~whether it is
+ possible to remove the assumption of the Generalized Riemann
+ Hypothesis\sidx{GRH}. It is correct if and only if the answer is 1. If it is
+ incorrect, the program may output some error message, or loop indefinitely.
+ You can check its progress by increasing the debug level. The \var{bnf}
+ structure must contain the fundamental units:
+ \bprog
+ ? K = bnfinit(x^3+2^2^3+1); bnfcertify(K)
+   ***   at top-level: K=bnfinit(x^3+2^2^3+1);bnfcertify(K)
+   ***                                        ^-------------
+   *** bnfcertify: missing units in bnf.
+ ? K = bnfinit(x^3+2^2^3+1, 1); \\ include units
+ ? bnfcertify(K)
+ %3 = 1
+ @eprog
+
+ If flag is present, only certify that the class group is a quotient of the
+ one computed in bnf (much simpler in general); likewise, the computed units
+ may form a subgroup of the full unit group. In this variant, the units are
+ no longer needed:
+ \bprog
+ ? K = bnfinit(x^3+2^2^3+1); bnfcertify(K, 1)
+ %4 = 1
+ @eprog
+
+Variant: Also available is  \fun{GEN}{bnfcertify}{GEN bnf} ($\fl=0$).
diff --git a/src/functions/number_fields/bnfcompress b/src/functions/number_fields/bnfcompress
new file mode 100644
index 0000000..df63ede
--- /dev/null
+++ b/src/functions/number_fields/bnfcompress
@@ -0,0 +1,32 @@
+Function: bnfcompress
+Section: number_fields
+C-Name: bnfcompress
+Prototype: G
+Help: bnfcompress(bnf): converts bnf to a much smaller sbnf, containing the
+ same information. Use bnfinit(sbnf) to recover a true bnf.
+Doc: computes a compressed version of \var{bnf} (from \tet{bnfinit}), a
+ ``small Buchmann's number field'' (or \var{sbnf} for short) which contains
+ enough information to recover a full $\var{bnf}$ vector very rapidly, but
+ which is much smaller and hence easy to store and print. Calling
+ \kbd{bnfinit} on the result recovers a true \kbd{bnf}, in general different
+ from the original. Note that an \tev{snbf} is useless for almost all
+ purposes besides storage, and must be converted back to \tev{bnf} form
+ before use; for instance, no \kbd{nf*}, \kbd{bnf*} or member function
+ accepts them.
+
+ An \var{sbnf} is a 12 component vector $v$, as follows. Let \kbd{bnf} be
+ the result of a full \kbd{bnfinit}, complete with units. Then $v[1]$ is
+ \kbd{bnf.pol}, $v[2]$ is the number of real embeddings \kbd{bnf.sign[1]},
+ $v[3]$ is \kbd{bnf.disc}, $v[4]$ is \kbd{bnf.zk}, $v[5]$ is the list of roots
+ \kbd{bnf.roots}, $v[7]$ is the matrix $\kbd{W} = \kbd{bnf[1]}$,
+ $v[8]$ is the matrix $\kbd{matalpha}=\kbd{bnf[2]}$,
+ $v[9]$ is the prime ideal factor base \kbd{bnf[5]} coded in a compact way,
+ and ordered according to the permutation \kbd{bnf[6]}, $v[10]$ is the
+ 2-component vector giving the number of roots of unity and a generator,
+ expressed on the integral basis, $v[11]$ is the list of fundamental units,
+ expressed on the integral basis, $v[12]$ is a vector containing the algebraic
+ numbers alpha corresponding to the columns of the matrix \kbd{matalpha},
+ expressed on the integral basis.
+
+ All the components are exact (integral or rational), except for the roots in
+ $v[5]$.
diff --git a/src/functions/number_fields/bnfdecodemodule b/src/functions/number_fields/bnfdecodemodule
new file mode 100644
index 0000000..1d3328e
--- /dev/null
+++ b/src/functions/number_fields/bnfdecodemodule
@@ -0,0 +1,18 @@
+Function: bnfdecodemodule
+Section: number_fields
+C-Name: decodemodule
+Prototype: GG
+Help: bnfdecodemodule(nf,m): given a coded module m as in bnrdisclist,
+ gives the true module.
+Doc: if $m$ is a module as output in the
+ first component of an extension given by \kbd{bnrdisclist}, outputs the
+ true module.
+ \bprog
+ ? K = bnfinit(x^2+23); L = bnrdisclist(K, 10); s = L[1][2]
+ %1 = [[Mat([8, 1]), [[0, 0, 0]]], [Mat([9, 1]), [[0, 0, 0]]]]
+ ? bnfdecodemodule(K, s[1][1])
+ %2 =
+ [2 0]
+
+ [0 1]
+ @eprog
diff --git a/src/functions/number_fields/bnfinit b/src/functions/number_fields/bnfinit
new file mode 100644
index 0000000..beb82a6
--- /dev/null
+++ b/src/functions/number_fields/bnfinit
@@ -0,0 +1,101 @@
+Function: bnfinit
+Section: number_fields
+C-Name: bnfinit0
+Prototype: GD0,L,DGp
+Help: bnfinit(P,{flag=0},{tech=[]}): compute the necessary data for future
+ use in ideal and unit group computations, including fundamental units if
+ they are not too large. flag and tech are both optional. flag can be any of
+ 0: default, 1: insist on having fundamental units.
+ See manual for details about tech.
+Description:
+ (gen):bnf:prec           Buchall($1, 0, prec)
+ (gen, 0):bnf:prec        Buchall($1, 0, prec)
+ (gen, 1):bnf:prec        Buchall($1, nf_FORCE, prec)
+ (gen, ?small, ?gen):bnf:prec        bnfinit0($1, $2, $3, prec)
+Doc: initializes a
+ \var{bnf} structure. Used in programs such as \kbd{bnfisprincipal},
+ \kbd{bnfisunit} or \kbd{bnfnarrow}. By default, the results are conditional
+ on the GRH, see \ref{se:GRHbnf}. The result is a
+ 10-component vector \var{bnf}.
+
+ This implements \idx{Buchmann}'s sub-exponential algorithm for computing the
+ class group, the regulator and a system of \idx{fundamental units} of the
+ general algebraic number field $K$ defined by the irreducible polynomial $P$
+ with integer coefficients.
+
+ If the precision becomes insufficient, \kbd{gp} does not strive to compute
+ the units by default ($\fl=0$).
+
+ When $\fl=1$, we insist on finding the fundamental units exactly. Be
+ warned that this can take a very long time when the coefficients of the
+ fundamental units on the integral basis are very large. If the fundamental
+ units are simply too large to be represented in this form, an error message
+ is issued. They could be obtained using the so-called compact representation
+ of algebraic numbers as a formal product of algebraic integers. The latter is
+ implemented internally but not publicly accessible yet.
+
+ $\var{tech}$ is a technical vector (empty by default, see \ref{se:GRHbnf}).
+ Careful use of this parameter may speed up your computations,
+ but it is mostly obsolete and you should leave it alone.
+
+ \smallskip
+
+ The components of a \var{bnf} or \var{sbnf} are technical and never used by
+ the casual user. In fact: \emph{never access a component directly, always use
+ a proper member function.} However, for the sake of completeness and internal
+ documentation, their description is as follows. We use the notations
+ explained in the book by H. Cohen, \emph{A Course in Computational Algebraic
+ Number Theory}, Graduate Texts in Maths \key{138}, Springer-Verlag, 1993,
+ Section 6.5, and subsection 6.5.5 in particular.
+
+ $\var{bnf}[1]$ contains the matrix $W$, i.e.~the matrix in Hermite normal
+ form giving relations for the class group on prime ideal generators
+ $(\goth{p}_i)_{1\le i\le r}$.
+
+ $\var{bnf}[2]$ contains the matrix $B$, i.e.~the matrix containing the
+ expressions of the prime ideal factorbase in terms of the $\goth{p}_i$.
+ It is an $r\times c$ matrix.
+
+ $\var{bnf}[3]$ contains the complex logarithmic embeddings of the system of
+ fundamental units which has been found. It is an $(r_1+r_2)\times(r_1+r_2-1)$
+ matrix.
+
+ $\var{bnf}[4]$ contains the matrix $M''_C$ of Archimedean components of the
+ relations of the matrix $(W|B)$.
+
+ $\var{bnf}[5]$ contains the prime factor base, i.e.~the list of prime
+ ideals used in finding the relations.
+
+ $\var{bnf}[6]$ used to contain a permutation of the prime factor base, but
+ has been obsoleted. It contains a dummy $0$.
+
+ $\var{bnf}[7]$ or \kbd{\var{bnf}.nf} is equal to the number field data
+ $\var{nf}$ as would be given by \kbd{nfinit}.
+
+ $\var{bnf}[8]$ is a vector containing the classgroup \kbd{\var{bnf}.clgp}
+ as a finite abelian group, the regulator \kbd{\var{bnf}.reg}, a $1$ (used to
+ contain an obsolete ``check number''), the number of roots of unity and a
+ generator \kbd{\var{bnf}.tu}, the fundamental units \kbd{\var{bnf}.fu}.
+
+ $\var{bnf}[9]$ is a 3-element row vector used in \tet{bnfisprincipal} only
+ and obtained as follows. Let $D = U W V$ obtained by applying the
+ \idx{Smith normal form} algorithm to the matrix $W$ (= $\var{bnf}[1]$) and
+ let $U_r$ be the reduction of $U$ modulo $D$. The first elements of the
+ factorbase are given (in terms of \kbd{bnf.gen}) by the columns of $U_r$,
+ with Archimedean component $g_a$; let also $GD_a$ be the Archimedean
+ components of the generators of the (principal) ideals defined by the
+ \kbd{bnf.gen[i]\pow bnf.cyc[i]}. Then $\var{bnf}[9]=[U_r, g_a, GD_a]$.
+
+ $\var{bnf}[10]$ is by default unused and set equal to 0. This field is used
+ to store further information about the field as it becomes available, which
+ is rarely needed, hence would be too expensive to compute during the initial
+ \kbd{bnfinit} call. For instance, the generators of the principal ideals
+ \kbd{bnf.gen[i]\pow bnf.cyc[i]} (during a call to \tet{bnrisprincipal}), or
+ those corresponding to the relations in $W$ and $B$ (when the \kbd{bnf}
+ internal precision needs to be increased).
+Variant:
+ Also available is \fun{GEN}{Buchall}{GEN P, long flag, long prec},
+ corresponding to \kbd{tech = NULL}, where
+ \kbd{flag} is either $0$ (default) or \tet{nf_FORCE} (insist on finding
+ fundamental units). The function
+ \fun{GEN}{Buchall_param}{GEN P, double c1, double c2, long nrpid, long flag, long prec} gives direct access to the technical parameters.
diff --git a/src/functions/number_fields/bnfisintnorm b/src/functions/number_fields/bnfisintnorm
new file mode 100644
index 0000000..38b7a4b
--- /dev/null
+++ b/src/functions/number_fields/bnfisintnorm
@@ -0,0 +1,20 @@
+Function: bnfisintnorm
+Section: number_fields
+C-Name: bnfisintnorm
+Prototype: GG
+Help: bnfisintnorm(bnf,x): compute a complete system of solutions (modulo
+ units of positive norm) of the absolute norm equation N(a)=x, where a
+ belongs to the maximal order of big number field bnf (if bnf is not
+ certified, this depends on GRH).
+Doc: computes a complete system of
+ solutions (modulo units of positive norm) of the absolute norm equation
+ $\Norm(a)=x$,
+ where $a$ is an integer in $\var{bnf}$. If $\var{bnf}$ has not been certified,
+ the correctness of the result depends on the validity of \idx{GRH}.
+
+ See also \tet{bnfisnorm}.
+Variant: The function \fun{GEN}{bnfisintnormabs}{GEN bnf, GEN a}
+ returns a complete system of solutions modulo units of the absolute norm
+ equation $|\Norm(x)| = |a|$. As fast as \kbd{bnfisintnorm}, but solves
+ the two equations $\Norm(x) = \pm a$ simultaneously.
+
diff --git a/src/functions/number_fields/bnfisnorm b/src/functions/number_fields/bnfisnorm
new file mode 100644
index 0000000..6769e73
--- /dev/null
+++ b/src/functions/number_fields/bnfisnorm
@@ -0,0 +1,27 @@
+Function: bnfisnorm
+Section: number_fields
+C-Name: bnfisnorm
+Prototype: GGD1,L,
+Help: bnfisnorm(bnf,x,{flag=1}): Tries to tell whether x (in Q) is the norm
+ of some fractional y (in bnf). Returns a vector [a,b] where x=Norm(a)*b.
+ Looks for a solution which is a S-unit, with S a certain list of primes (in
+ bnf) containing (among others) all primes dividing x. If bnf is known to be
+ Galois, set flag=0 (in this case, x is a norm iff b=1). If flag is non zero
+ the program adds to S all the primes: dividing flag if flag<0, or less than
+ flag if flag>0. The answer is guaranteed (i.e x norm iff b=1) under GRH, if
+ S contains all primes less than 12.log(disc(Bnf))^2, where Bnf is the Galois
+ closure of bnf.
+Doc: tries to tell whether the
+ rational number $x$ is the norm of some element y in $\var{bnf}$. Returns a
+ vector $[a,b]$ where $x=Norm(a)*b$. Looks for a solution which is an $S$-unit,
+ with $S$ a certain set of prime ideals containing (among others) all primes
+ dividing $x$. If $\var{bnf}$ is known to be \idx{Galois}, set $\fl=0$ (in
+ this case, $x$ is a norm iff $b=1$). If $\fl$ is non zero the program adds to
+ $S$ the following prime ideals, depending on the sign of $\fl$. If $\fl>0$,
+ the ideals of norm less than $\fl$. And if $\fl<0$ the ideals dividing $\fl$.
+
+ Assuming \idx{GRH}, the answer is guaranteed (i.e.~$x$ is a norm iff $b=1$),
+ if $S$ contains all primes less than $12\log(\disc(\var{Bnf}))^2$, where
+ $\var{Bnf}$ is the Galois closure of $\var{bnf}$.
+
+ See also \tet{bnfisintnorm}.
diff --git a/src/functions/number_fields/bnfisprincipal b/src/functions/number_fields/bnfisprincipal
new file mode 100644
index 0000000..37768fa
--- /dev/null
+++ b/src/functions/number_fields/bnfisprincipal
@@ -0,0 +1,64 @@
+Function: bnfisprincipal
+Section: number_fields
+C-Name: bnfisprincipal0
+Prototype: GGD1,L,
+Help: bnfisprincipal(bnf,x,{flag=1}): bnf being output by bnfinit (with
+ flag<=2), gives [v,alpha], where v is the vector of exponents on
+ the class group generators and alpha is the generator of the resulting
+ principal ideal. In particular x is principal if and only if v is the zero
+ vector. flag is optional, whose binary digits mean 1: output [v,alpha] (only v
+ if unset); 2: increase precision until alpha can be computed (do not insist
+ if unset).
+Doc: $\var{bnf}$ being the \sidx{principal ideal}
+ number field data output by \kbd{bnfinit}, and $x$ being an ideal, this
+ function tests whether the ideal is principal or not. The result is more
+ complete than a simple true/false answer and solves general discrete
+ logarithm problem. Assume the class group is $\oplus (\Z/d_i\Z)g_i$
+ (where the generators $g_i$ and their orders $d_i$ are respectively given by
+ \kbd{bnf.gen} and \kbd{bnf.cyc}). The routine returns a row vector $[e,t]$,
+ where $e$ is a vector of exponents $0 \leq e_i < d_i$, and $t$ is a number
+ field element such that
+ $$ x = (t) \prod_i  g_i^{e_i}.$$
+ For \emph{given} $g_i$ (i.e. for a given \kbd{bnf}), the $e_i$ are unique,
+ and $t$ is unique modulo units.
+
+ In particular, $x$ is principal if and only if $e$ is the zero vector. Note
+ that the empty vector, which is returned when the class number is $1$, is
+ considered to be a zero vector (of dimension $0$).
+ \bprog
+ ? K = bnfinit(y^2+23);
+ ? K.cyc
+ %2 = [3]
+ ? K.gen
+ %3 = [[2, 0; 0, 1]]          \\ a prime ideal above 2
+ ? P = idealprimedec(K,3)[1]; \\ a prime ideal above 3
+ ? v = bnfisprincipal(K, P)
+ %5 = [[2]~, [3/4, 1/4]~]
+ ? idealmul(K, v[2], idealfactorback(K, K.gen, v[1]))
+ %6 =
+ [3 0]
+
+ [0 1]
+ ? % == idealhnf(K, P)
+ %7 = 1
+ @eprog
+
+ \noindent The binary digits of \fl mean:
+
+ \item $1$: If set, outputs $[e,t]$ as explained above, otherwise returns
+ only $e$, which is much easier to compute. The following idiom only tests
+ whether an ideal is principal:
+ \bprog
+   is_principal(bnf, x) = !bnfisprincipal(bnf,x,0);
+ @eprog
+
+ \item $2$: It may not be possible to recover $t$, given the initial accuracy
+ to which \kbd{bnf} was computed. In that case, a warning is printed and $t$ is
+ set equal to the empty vector \kbd{[]\til}. If this bit is set,
+ increase the precision and recompute needed quantities until $t$ can be
+ computed. Warning: setting this may induce \emph{very} lengthy computations.
+Variant: Instead of the above hardcoded numerical flags, one should
+ rather use an or-ed combination of the symbolic flags \tet{nf_GEN} (include
+ generators, possibly a place holder if too difficult) and \tet{nf_FORCE}
+ (insist on finding the generators).
+
diff --git a/src/functions/number_fields/bnfissunit b/src/functions/number_fields/bnfissunit
new file mode 100644
index 0000000..1c34840
--- /dev/null
+++ b/src/functions/number_fields/bnfissunit
@@ -0,0 +1,11 @@
+Function: bnfissunit
+Section: number_fields
+C-Name: bnfissunit
+Prototype: GGG
+Help: bnfissunit(bnf,sfu,x): bnf being output by bnfinit (with flag<=2), sfu
+ by bnfsunit, gives the column vector of exponents of x on the fundamental
+ S-units and the roots of unity if x is a unit, the empty vector otherwise.
+Doc: $\var{bnf}$ being output by
+ \kbd{bnfinit}, \var{sfu} by \kbd{bnfsunit}, gives the column vector of
+ exponents of $x$ on the fundamental $S$-units and the roots of unity.
+ If $x$ is not a unit, outputs an empty vector.
diff --git a/src/functions/number_fields/bnfisunit b/src/functions/number_fields/bnfisunit
new file mode 100644
index 0000000..66d164d
--- /dev/null
+++ b/src/functions/number_fields/bnfisunit
@@ -0,0 +1,32 @@
+Function: bnfisunit
+Section: number_fields
+C-Name: bnfisunit
+Prototype: GG
+Help: bnfisunit(bnf,x): bnf being output by bnfinit, gives
+ the column vector of exponents of x on the fundamental units and the roots
+ of unity if x is a unit, the empty vector otherwise.
+Doc: \var{bnf} being the number field data
+ output by \kbd{bnfinit} and $x$ being an algebraic number (type integer,
+ rational or polmod), this outputs the decomposition of $x$ on the fundamental
+ units and the roots of unity if $x$ is a unit, the empty vector otherwise.
+ More precisely, if $u_1$,\dots,$u_r$ are the fundamental units, and $\zeta$
+ is the generator of the group of roots of unity (\kbd{bnf.tu}), the output is
+ a vector $[x_1,\dots,x_r,x_{r+1}]$ such that $x=u_1^{x_1}\cdots
+ u_r^{x_r}\cdot\zeta^{x_{r+1}}$. The $x_i$ are integers for $i\le r$ and is an
+ integer modulo the order of $\zeta$ for $i=r+1$.
+
+ Note that \var{bnf} need not contain the fundamental unit explicitly:
+ \bprog
+ ? setrand(1); bnf = bnfinit(x^2-x-100000);
+ ? bnf.fu
+   ***   at top-level: bnf.fu
+   ***                     ^--
+   *** _.fu: missing units in .fu.
+ ? u = [119836165644250789990462835950022871665178127611316131167, \
+        379554884019013781006303254896369154068336082609238336]~;
+ ? bnfisunit(bnf, u)
+ %3 = [-1, Mod(0, 2)]~
+ @eprog\noindent The given $u$ is the inverse of the fundamental unit
+ implicitly stored in \var{bnf}. In this case, the fundamental unit was not
+ computed and stored in algebraic form since the default accuracy was too
+ low. (Re-run the command at \bs g1 or higher to see such diagnostics.)
diff --git a/src/functions/number_fields/bnfnarrow b/src/functions/number_fields/bnfnarrow
new file mode 100644
index 0000000..c0c7a80
--- /dev/null
+++ b/src/functions/number_fields/bnfnarrow
@@ -0,0 +1,15 @@
+Function: bnfnarrow
+Section: number_fields
+C-Name: buchnarrow
+Prototype: G
+Help: bnfnarrow(bnf): given a big number field as output by bnfinit, gives
+ as a 3-component vector the structure of the narrow class group.
+Doc: $\var{bnf}$ being as output by
+ \kbd{bnfinit}, computes the narrow class group of $\var{bnf}$. The output is
+ a 3-component row vector $v$ analogous to the corresponding class group
+ component \kbd{\var{bnf}.clgp} (\kbd{\var{bnf}[8][1]}): the first component
+ is the narrow class number \kbd{$v$.no}, the second component is a vector
+ containing the SNF\sidx{Smith normal form} cyclic components \kbd{$v$.cyc} of
+ the narrow class group, and the third is a vector giving the generators of
+ the corresponding \kbd{$v$.gen} cyclic groups. Note that this function is a
+ special case of \kbd{bnrinit}.
diff --git a/src/functions/number_fields/bnfsignunit b/src/functions/number_fields/bnfsignunit
new file mode 100644
index 0000000..0f83eb8
--- /dev/null
+++ b/src/functions/number_fields/bnfsignunit
@@ -0,0 +1,30 @@
+Function: bnfsignunit
+Section: number_fields
+C-Name: signunits
+Prototype: G
+Help: bnfsignunit(bnf): matrix of signs of the real embeddings of the system
+ of fundamental units found by bnfinit.
+Doc: $\var{bnf}$ being as output by
+ \kbd{bnfinit}, this computes an $r_1\times(r_1+r_2-1)$ matrix having $\pm1$
+ components, giving the signs of the real embeddings of the fundamental units.
+ The following functions compute generators for the totally positive units:
+
+ \bprog
+ /* exponents of totally positive units generators on bnf.tufu */
+ tpuexpo(bnf)=
+ { my(S,d,K);
+
+   S = bnfsignunit(bnf); d = matsize(S);
+   S = matrix(d[1],d[2], i,j, if (S[i,j] < 0, 1,0));
+   S = concat(vectorv(d[1],i,1), S);   \\ add sign(-1)
+   K = lift(matker(S * Mod(1,2)));
+   if (K, mathnfmodid(K, 2), 2*matid(d[1]))
+ }
+
+ /* totally positive units */
+ tpu(bnf)=
+ { my(vu = bnf.tufu, ex = tpuexpo(bnf));
+
+   vector(#ex-1, i, factorback(vu, ex[,i+1]))  \\ ex[,1] is 1
+ }
+ @eprog
diff --git a/src/functions/number_fields/bnfsunit b/src/functions/number_fields/bnfsunit
new file mode 100644
index 0000000..a0511b7
--- /dev/null
+++ b/src/functions/number_fields/bnfsunit
@@ -0,0 +1,29 @@
+Function: bnfsunit
+Section: number_fields
+C-Name: bnfsunit
+Prototype: GGp
+Help: bnfsunit(bnf,S): compute the fundamental S-units of the number field
+ bnf output by bnfinit, S being a list of prime ideals. res[1] contains the
+ S-units, res[5] the S-classgroup. See manual for details.
+Doc: computes the fundamental $S$-units of the
+ number field $\var{bnf}$ (output by \kbd{bnfinit}), where $S$ is a list of
+ prime ideals (output by \kbd{idealprimedec}). The output is a vector $v$ with
+ 6 components.
+
+ $v[1]$ gives a minimal system of (integral) generators of the $S$-unit group
+ modulo the unit group.
+
+ $v[2]$ contains technical data needed by \kbd{bnfissunit}.
+
+ $v[3]$ is an empty vector (used to give the logarithmic embeddings of the
+ generators in $v[1]$ in version 2.0.16).
+
+ $v[4]$ is the $S$-regulator (this is the product of the regulator, the
+ determinant of $v[2]$ and the natural logarithms of the norms of the ideals
+ in $S$).
+
+ $v[5]$ gives the $S$-class group structure, in the usual format
+ (a row vector whose three components give in order the $S$-class number,
+ the cyclic components and the generators).
+
+ $v[6]$ is a copy of $S$.
diff --git a/src/functions/number_fields/bnrL1 b/src/functions/number_fields/bnrL1
new file mode 100644
index 0000000..66236ce
--- /dev/null
+++ b/src/functions/number_fields/bnrL1
@@ -0,0 +1,56 @@
+Function: bnrL1
+Section: number_fields
+C-Name: bnrL1
+Prototype: GDGD0,L,p
+Help: bnrL1(bnr, {H}, {flag=0}): bnr being output by bnrinit(,,1) and
+ H being a square matrix defining a congruence subgroup of bnr (the
+ trivial subgroup if omitted), for each character of bnr trivial on this
+ subgroup, compute L(1, chi) (or equivalently the first non-zero term c(chi)
+ of the expansion at s = 0). The binary digits of flag mean 1: if 0 then
+ compute the term c(chi) and return [r(chi), c(chi)] where r(chi) is the
+ order of L(s, chi) at s = 0, or if 1 then compute the value at s = 1 (and in
+ this case, only for non-trivial characters), 2: if 0 then compute the value
+ of the primitive L-function associated to chi, if 1 then compute the value
+ of the L-function L_S(s, chi) where S is the set of places dividing the
+ modulus of bnr (and the infinite places), 3: return also the characters.
+Doc: let \var{bnr} be the number field data output by \kbd{bnrinit(,,1)} and
+ \var{H} be a square matrix defining a congruence subgroup of the
+ ray class group corresponding to \var{bnr} (the trivial congruence subgroup
+ if omitted). This function returns, for each \idx{character} $\chi$ of the ray
+ class group which is trivial on $H$, the value at $s = 1$ (or $s = 0$) of the
+ abelian $L$-function associated to $\chi$. For the value at $s = 0$, the
+ function returns in fact for each $\chi$ a vector $[r_\chi, c_\chi]$ where
+ $$L(s, \chi) = c \cdot s^r + O(s^{r + 1})$$
+ \noindent near $0$.
+
+ The argument \fl\ is optional, its binary digits
+ mean 1: compute at $s = 0$ if unset or $s = 1$ if set, 2: compute the
+ primitive $L$-function associated to $\chi$ if unset or the $L$-function
+ with Euler factors at prime ideals dividing the modulus of \var{bnr} removed
+ if set (that is $L_S(s, \chi)$, where $S$ is the
+ set of infinite places of the number field together with the finite prime
+ ideals dividing the modulus of \var{bnr}), 3: return also the character if
+ set.
+ \bprog
+ K = bnfinit(x^2-229);
+ bnr = bnrinit(K,1,1);
+ bnrL1(bnr)
+ @eprog\noindent
+ returns the order and the first non-zero term of $L(s, \chi)$ at $s = 0$
+ where $\chi$ runs through the characters of the class group of
+ $K = \Q(\sqrt{229})$. Then
+ \bprog
+ bnr2 = bnrinit(K,2,1);
+ bnrL1(bnr2,,2)
+ @eprog\noindent
+ returns the order and the first non-zero terms of $L_S(s, \chi)$ at $s = 0$
+ where $\chi$ runs through the characters of the class group of $K$ and $S$ is
+ the set of infinite places of $K$ together with the finite prime $2$. Note
+ that the ray class group modulo $2$ is in fact the class group, so
+ \kbd{bnrL1(bnr2,0)} returns the same answer as \kbd{bnrL1(bnr,0)}.
+
+ This function will fail with the message
+ \bprog
+  *** bnrL1: overflow in zeta_get_N0 [need too many primes].
+ @eprog\noindent if the approximate functional equation requires us to sum
+ too many terms (if the discriminant of $K$ is too large).
diff --git a/src/functions/number_fields/bnrclassno b/src/functions/number_fields/bnrclassno
new file mode 100644
index 0000000..0baa43a
--- /dev/null
+++ b/src/functions/number_fields/bnrclassno
@@ -0,0 +1,28 @@
+Function: bnrclassno
+Section: number_fields
+C-Name: bnrclassno0
+Prototype: GDGDG
+Help: bnrclassno(A,{B},{C}): relative degree of the class field defined by
+ A,B,C. [A,{B},{C}] is of type [bnr], [bnr,subgroup], [bnf,modulus],
+ or [bnf,modulus,subgroup].
+ Faster than bnrinit if only the ray class number is wanted.
+Doc:
+  let $A$, $B$, $C$ define a class field $L$ over a ground field $K$
+ (of type \kbd{[\var{bnr}]},
+ \kbd{[\var{bnr}, \var{subgroup}]},
+ or \kbd{[\var{bnf}, \var{modulus}]},
+ or \kbd{[\var{bnf}, \var{modulus},\var{subgroup}]},
+ \secref{se:CFT}); this function returns the relative degree $[L:K]$.
+
+ In particular if $A$ is a \var{bnf} (with units), and $B$ a modulus,
+ this function returns the corresponding ray class number modulo $B$.
+ One can input the associated \var{bid} (with generators if the subgroup
+ $C$ is non trivial) for $B$ instead of the module itself, saving some time.
+
+ This function is faster than \kbd{bnrinit} and should be used if only the
+ ray class number is desired. See \tet{bnrclassnolist} if you need ray class
+ numbers for all moduli less than some bound.
+
+Variant: Also available is
+ \fun{GEN}{bnrclassno}{GEN bnf,GEN f} to compute the ray class number
+ modulo~$f$.
diff --git a/src/functions/number_fields/bnrclassnolist b/src/functions/number_fields/bnrclassnolist
new file mode 100644
index 0000000..5d7c76e
--- /dev/null
+++ b/src/functions/number_fields/bnrclassnolist
@@ -0,0 +1,30 @@
+Function: bnrclassnolist
+Section: number_fields
+C-Name: bnrclassnolist
+Prototype: GG
+Help: bnrclassnolist(bnf,list): if list is as output by ideallist or
+ similar, gives list of corresponding ray class numbers.
+Doc: $\var{bnf}$ being as
+ output by \kbd{bnfinit}, and \var{list} being a list of moduli (with units) as
+ output by \kbd{ideallist} or \kbd{ideallistarch}, outputs the list of the
+ class numbers of the corresponding ray class groups. To compute a single
+ class number, \tet{bnrclassno} is more efficient.
+
+ \bprog
+ ? bnf = bnfinit(x^2 - 2);
+ ? L = ideallist(bnf, 100, 2);
+ ? H = bnrclassnolist(bnf, L);
+ ? H[98]
+ %4 = [1, 3, 1]
+ ? l = L[1][98]; ids = vector(#l, i, l[i].mod[1])
+ %5 = [[98, 88; 0, 1], [14, 0; 0, 7], [98, 10; 0, 1]]
+ @eprog
+ The weird \kbd{l[i].mod[1]}, is the first component of \kbd{l[i].mod}, i.e.
+ the finite part of the conductor. (This is cosmetic: since by construction
+ the Archimedean part is trivial, I do not want to see it). This tells us that
+ the ray class groups modulo the ideals of norm 98 (printed as \kbd{\%5}) have
+ respectively order $1$, $3$ and $1$. Indeed, we may check directly:
+ \bprog
+ ? bnrclassno(bnf, ids[2])
+ %6 = 3
+ @eprog
diff --git a/src/functions/number_fields/bnrconductor b/src/functions/number_fields/bnrconductor
new file mode 100644
index 0000000..ea1e2ad
--- /dev/null
+++ b/src/functions/number_fields/bnrconductor
@@ -0,0 +1,26 @@
+Function: bnrconductor
+Section: number_fields
+C-Name: bnrconductor0
+Prototype: GDGDGD0,L,
+Help: bnrconductor(A,{B},{C},{flag=0}): conductor f of the subfield of
+ the ray class field given by A,B,C. flag is optional and
+ can be 0: default, 1: returns [f, Cl_f, H], H subgroup of the ray class
+ group modulo f defining the extension, 2: returns [f, bnr(f), H].
+Doc: conductor $f$ of the subfield of a ray class field as defined by $[A,B,C]$
+ (of type \kbd{[\var{bnr}]},
+ \kbd{[\var{bnr}, \var{subgroup}]},
+ \kbd{[\var{bnf}, \var{modulus}]} or
+ \kbd{[\var{bnf}, \var{modulus}, \var{subgroup}]},
+ \secref{se:CFT})
+
+ If $\fl = 0$, returns $f$.
+
+ If $\fl = 1$, returns $[f, Cl_f, H]$, where $Cl_f$ is the ray class group
+ modulo $f$, as a finite abelian group; finally $H$ is the subgroup of $Cl_f$
+ defining the extension.
+
+ If $\fl = 2$, returns $[f, \var{bnr}(f), H]$, as above except $Cl_f$ is
+ replaced by a \kbd{bnr} structure, as output by $\tet{bnrinit}(,f,1)$.
+Variant:
+ Also available is \fun{GEN}{bnrconductor}{GEN bnr, GEN H, long flag}
+
diff --git a/src/functions/number_fields/bnrconductorofchar b/src/functions/number_fields/bnrconductorofchar
new file mode 100644
index 0000000..10ba92a
--- /dev/null
+++ b/src/functions/number_fields/bnrconductorofchar
@@ -0,0 +1,10 @@
+Function: bnrconductorofchar
+Section: number_fields
+C-Name: bnrconductorofchar
+Prototype: GG
+Help: bnrconductorofchar(bnr,chi): conductor of the character chi on the ray
+ class group bnr.
+Doc: \var{bnr} being a big
+ ray number field as output by \kbd{bnrinit}, and \var{chi} being a row vector
+ representing a \idx{character} as expressed on the generators of the ray
+ class group, gives the conductor of this character as a modulus.
diff --git a/src/functions/number_fields/bnrdisc b/src/functions/number_fields/bnrdisc
new file mode 100644
index 0000000..9da2903
--- /dev/null
+++ b/src/functions/number_fields/bnrdisc
@@ -0,0 +1,29 @@
+Function: bnrdisc
+Section: number_fields
+C-Name: bnrdisc0
+Prototype: GDGDGD0,L,
+Help: bnrdisc(A,{B},{C},{flag=0}): absolute or relative [N,R1,discf] of
+ the field defined by A,B,C. [A,{B},{C}] is of type [bnr],
+ [bnr,subgroup], [bnf, modulus] or [bnf,modulus,subgroup], where bnf is as
+ output by bnfinit, bnr by bnrinit, and
+ subgroup is the HNF matrix of a subgroup of the corresponding ray class
+ group (if omitted, the trivial subgroup). flag is optional whose binary
+ digits mean 1: give relative data; 2: return 0 if modulus is not the
+ conductor.
+Doc: $A$, $B$, $C$ defining a class field $L$ over a ground field $K$
+ (of type \kbd{[\var{bnr}]},
+ \kbd{[\var{bnr}, \var{subgroup}]},
+ \kbd{[\var{bnf}, \var{modulus}]} or
+ \kbd{[\var{bnf}, \var{modulus}, \var{subgroup}]},
+ \secref{se:CFT}), outputs data $[N,r_1,D]$ giving the discriminant and
+ signature of $L$, depending on the binary digits of \fl:
+
+ \item 1: if this bit is unset, output absolute data related to $L/\Q$:
+ $N$ is the absolute degree $[L:\Q]$, $r_1$ the number of real places of $L$,
+ and $D$ the discriminant of $L/\Q$. Otherwise, output relative data for $L/K$:
+ $N$ is the relative degree $[L:K]$, $r_1$ is the number of real places of $K$
+ unramified in $L$ (so that the number of real places of $L$ is equal to $r_1$
+ times $N$), and $D$ is the relative discriminant ideal of $L/K$.
+
+ \item 2: if this bit is set and if the modulus is not the conductor of $L$,
+ only return 0.
diff --git a/src/functions/number_fields/bnrdisclist b/src/functions/number_fields/bnrdisclist
new file mode 100644
index 0000000..e1dd899
--- /dev/null
+++ b/src/functions/number_fields/bnrdisclist
@@ -0,0 +1,51 @@
+Function: bnrdisclist
+Section: number_fields
+C-Name: bnrdisclist0
+Prototype: GGDG
+Help: bnrdisclist(bnf,bound,{arch}): gives list of discriminants of
+ ray class fields of all conductors up to norm bound, in a long vector
+ The ramified Archimedean places are given by arch; all possible values are
+ taken if arch is omitted. Supports the alternative syntax
+ bnrdisclist(bnf,list), where list is as output by ideallist or ideallistarch
+ (with units).
+Doc: $\var{bnf}$ being as output by \kbd{bnfinit} (with units), computes a
+ list of discriminants of Abelian extensions of the number field by increasing
+ modulus norm up to bound \var{bound}. The ramified Archimedean places are
+ given by \var{arch}; all possible values are taken if \var{arch} is omitted.
+
+ The alternative syntax $\kbd{bnrdisclist}(\var{bnf},\var{list})$ is
+ supported, where \var{list} is as output by \kbd{ideallist} or
+ \kbd{ideallistarch} (with units), in which case \var{arch} is disregarded.
+
+ The output $v$ is a vector of vectors, where $v[i][j]$ is understood to be in
+ fact $V[2^{15}(i-1)+j]$ of a unique big vector $V$. (This awkward scheme
+ allows for larger vectors than could be otherwise represented.)
+
+ $V[k]$ is itself a vector $W$, whose length is the number of ideals of norm
+ $k$. We consider first the case where \var{arch} was specified. Each
+ component of $W$ corresponds to an ideal $m$ of norm $k$, and
+ gives invariants associated to the ray class field $L$ of $\var{bnf}$ of
+ conductor $[m, \var{arch}]$. Namely, each contains a vector $[m,d,r,D]$ with
+ the following meaning: $m$ is the prime ideal factorization of the modulus,
+ $d = [L:\Q]$ is the absolute degree of $L$, $r$ is the number of real places
+ of $L$, and $D$ is the factorization of its absolute discriminant. We set $d
+ = r = D = 0$ if $m$ is not the finite part of a conductor.
+
+ If \var{arch} was omitted, all $t = 2^{r_1}$ possible values are taken and a
+ component of $W$ has the form $[m, [[d_1,r_1,D_1], \dots, [d_t,r_t,D_t]]]$,
+ where $m$ is the finite part of the conductor as above, and
+ $[d_i,r_i,D_i]$ are the invariants of the ray class field of conductor
+ $[m,v_i]$, where $v_i$ is the $i$-th Archimedean component, ordered by
+ inverse lexicographic order; so $v_1 = [0,\dots,0]$, $v_2 = [1,0\dots,0]$,
+ etc. Again, we set $d_i = r_i = D_i = 0$ if $[m,v_i]$ is not a conductor.
+
+ Finally, each prime ideal $pr = [p,\alpha,e,f,\beta]$ in the prime
+ factorization $m$ is coded as the integer $p\cdot n^2+(f-1)\cdot n+(j-1)$,
+ where $n$ is the degree of the base field and $j$ is such that
+
+ \kbd{pr = idealprimedec(\var{nf},p)[j]}.
+
+ \noindent $m$ can be decoded using \tet{bnfdecodemodule}.
+
+ Note that to compute such data for a single field, either \tet{bnrclassno}
+ or \tet{bnrdisc} is more efficient.
diff --git a/src/functions/number_fields/bnrinit b/src/functions/number_fields/bnrinit
new file mode 100644
index 0000000..89e6429
--- /dev/null
+++ b/src/functions/number_fields/bnrinit
@@ -0,0 +1,43 @@
+Function: bnrinit
+Section: number_fields
+C-Name: bnrinit0
+Prototype: GGD0,L,
+Help: bnrinit(bnf,f,{flag=0}): given a bnf as output by
+ bnfinit and a modulus f, initializes data
+ linked to the ray class group structure corresponding to this module. flag
+ is optional, and can be 0: default, 1: compute also the generators.
+Description:
+ (gen,gen,?small):bnr       bnrinit0($1, $2, $3)
+Doc: $\var{bnf}$ is as
+ output by \kbd{bnfinit}, $f$ is a modulus, initializes data linked to
+ the ray class group structure corresponding to this module, a so-called
+ \var{bnr} structure. One can input the associated \var{bid} with generators
+ for $f$ instead of the module itself, saving some time.
+ (As in \tet{idealstar}, the finite part of the conductor may be given
+ by a factorization into prime ideals, as produced by \tet{idealfactor}.)
+
+ The following member functions are available
+ on the result: \kbd{.bnf} is the underlying \var{bnf},
+ \kbd{.mod} the modulus, \kbd{.bid} the \var{bid} structure associated to the
+ modulus; finally, \kbd{.clgp}, \kbd{.no}, \kbd{.cyc}, \kbd{.gen} refer to the
+ ray class group (as a finite abelian group), its cardinality, its elementary
+ divisors, its generators (only computed if $\fl = 1$).
+
+ The last group of functions are different from the members of the underlying
+ \var{bnf}, which refer to the class group; use \kbd{\var{bnr}.bnf.\var{xxx}}
+ to access these, e.g.~\kbd{\var{bnr}.bnf.cyc} to get the cyclic decomposition
+ of the class group.
+
+ They are also different from the members of the underlying \var{bid}, which
+ refer to $(\Z_K/f)^*$; use \kbd{\var{bnr}.bid.\var{xxx}} to access these,
+ e.g.~\kbd{\var{bnr}.bid.no} to get $\phi(f)$.
+
+ If $\fl=0$ (default), the generators of the ray class group are not computed,
+ which saves time. Hence \kbd{\var{bnr}.gen} would produce an error.
+
+ If $\fl=1$, as the default, except that generators are computed.
+Variant: Instead the above  hardcoded  numerical flags,  one should rather use
+ \fun{GEN}{Buchray}{GEN bnf, GEN module, long flag}
+ where flag is an or-ed combination of \kbd{nf\_GEN} (include generators)
+ and \kbd{nf\_INIT} (if omitted, return just the cardinal of the ray class group
+ and its structure), possibly 0.
diff --git a/src/functions/number_fields/bnrisconductor b/src/functions/number_fields/bnrisconductor
new file mode 100644
index 0000000..7174b06
--- /dev/null
+++ b/src/functions/number_fields/bnrisconductor
@@ -0,0 +1,12 @@
+Function: bnrisconductor
+Section: number_fields
+C-Name: bnrisconductor0
+Prototype: lGDGDG
+Help: bnrisconductor(A,{B},{C}): returns 1 if the modulus is the
+ conductor of the subfield of the ray class field given by A,B,C (see
+ bnrdisc), and 0 otherwise. Slightly faster than bnrconductor if this is the
+ only desired result.
+Doc: $A$, $B$, $C$ represent
+ an extension of the base field, given by class field theory
+ (see~\secref{se:CFT}). Outputs 1 if this modulus is the conductor, and 0
+ otherwise. This is slightly faster than \kbd{bnrconductor}.
diff --git a/src/functions/number_fields/bnrisprincipal b/src/functions/number_fields/bnrisprincipal
new file mode 100644
index 0000000..979bb1d
--- /dev/null
+++ b/src/functions/number_fields/bnrisprincipal
@@ -0,0 +1,27 @@
+Function: bnrisprincipal
+Section: number_fields
+C-Name: bnrisprincipal
+Prototype: GGD1,L,
+Help: bnrisprincipal(bnr,x,{flag=1}): bnr being output by bnrinit, gives
+ [v,alpha], where v is the vector of exponents on the class group
+ generators and alpha is the generator of the resulting principal ideal. In
+ particular x is principal if and only if v is the zero vector. If (optional)
+ flag is set to 0, output only v.
+Doc: \var{bnr} being the
+ number field data which is output by \kbd{bnrinit}$(,,1)$ and $x$ being an
+ ideal in any form, outputs the components of $x$ on the ray class group
+ generators in a way similar to \kbd{bnfisprincipal}. That is a 2-component
+ vector $v$ where $v[1]$ is the vector of components of $x$ on the ray class
+ group generators, $v[2]$ gives on the integral basis an element $\alpha$ such
+ that $x=\alpha\prod_ig_i^{x_i}$.
+
+ If $\fl=0$, outputs only $v_1$. In that case, \var{bnr} need not contain the
+ ray class group generators, i.e.~it may be created with \kbd{bnrinit}$(,,0)$
+ If $x$ is not coprime to the modulus of \var{bnr} the result is undefined.
+Variant: Instead of hardcoded  numerical flags,  one should rather
+ use
+ \fun{GEN}{isprincipalray}{GEN bnr, GEN x} for $\kbd{flag} = 0$, and if you
+ want generators:
+ \bprog
+   bnrisprincipal(bnr, x, nf_GEN)
+ @eprog
diff --git a/src/functions/number_fields/bnrrootnumber b/src/functions/number_fields/bnrrootnumber
new file mode 100644
index 0000000..898114a
--- /dev/null
+++ b/src/functions/number_fields/bnrrootnumber
@@ -0,0 +1,33 @@
+Function: bnrrootnumber
+Section: number_fields
+C-Name: bnrrootnumber
+Prototype: GGD0,L,p
+Help: bnrrootnumber(bnr,chi,{flag=0}): returns the so-called Artin Root
+ Number, i.e. the constant W appearing in the functional equation of the
+ Hecke L-function associated to chi. Set flag = 1 if the character is known
+ to be primitive.
+Doc: if $\chi=\var{chi}$ is a
+ \idx{character} over \var{bnr}, not necessarily primitive, let
+ $L(s,\chi) = \sum_{id} \chi(id) N(id)^{-s}$ be the associated
+ \idx{Artin L-function}. Returns the so-called \idx{Artin root number}, i.e.~the
+ complex number $W(\chi)$ of modulus 1 such that
+ %
+ $$\Lambda(1-s,\chi) = W(\chi) \Lambda(s,\overline{\chi})$$
+ %
+ \noindent where $\Lambda(s,\chi) = A(\chi)^{s/2}\gamma_\chi(s) L(s,\chi)$ is
+ the enlarged L-function associated to $L$.
+
+ The generators of the ray class group are needed, and you can set $\fl=1$ if
+ the character is known to be primitive. Example:
+
+ \bprog
+ bnf = bnfinit(x^2 - x - 57);
+ bnr = bnrinit(bnf, [7,[1,1]], 1);
+ bnrrootnumber(bnr, [2,1])
+ @eprog\noindent
+ returns the root number of the character $\chi$ of
+ $\Cl_{7\infty_1\infty_2}(\Q(\sqrt{229}))$ defined by $\chi(g_1^ag_2^b)
+ = \zeta_1^{2a}\zeta_2^b$. Here $g_1, g_2$ are the generators of the
+ ray-class group given by \kbd{bnr.gen} and $\zeta_1 = e^{2i\pi/N_1},
+ \zeta_2 = e^{2i\pi/N_2}$ where $N_1, N_2$ are the orders of $g_1$ and
+ $g_2$ respectively ($N_1=6$ and $N_2=3$ as \kbd{bnr.cyc} readily tells us).
diff --git a/src/functions/number_fields/bnrstark b/src/functions/number_fields/bnrstark
new file mode 100644
index 0000000..3a4e360
--- /dev/null
+++ b/src/functions/number_fields/bnrstark
@@ -0,0 +1,51 @@
+Function: bnrstark
+Section: number_fields
+C-Name: bnrstark
+Prototype: GDGp
+Help: bnrstark(bnr,{subgroup}): bnr being as output by
+ bnrinit(,,1), finds a relative equation for the class field corresponding to
+ the module in bnr and the given congruence subgroup (the trivial subgroup if
+ omitted) using Stark's units. The ground field and the class field must be
+ totally real.
+Doc: \var{bnr} being as output by \kbd{bnrinit(,,1)}, finds a relative equation
+ for the class field corresponding to the modulus in \var{bnr} and the given
+ congruence subgroup (as usual, omit $\var{subgroup}$ if you want the whole ray
+ class group).
+
+ The main variable of \var{bnr} must not be $x$, and the ground field and the
+ class field must be totally real. When the base field is $\Q$, the vastly
+ simpler \tet{galoissubcyclo} is used instead. Here is an example:
+ \bprog
+ bnf = bnfinit(y^2 - 3);
+ bnr = bnrinit(bnf, 5, 1);
+ bnrstark(bnr)
+ @eprog\noindent
+ returns the ray class field of $\Q(\sqrt{3})$ modulo $5$. Usually, one wants
+ to apply to the result one of
+ \bprog
+ rnfpolredabs(bnf, pol, 16)     \\@com compute a reduced relative polynomial
+ rnfpolredabs(bnf, pol, 16 + 2) \\@com compute a reduced absolute polynomial
+ @eprog
+
+ The routine uses \idx{Stark units} and needs to find a suitable auxiliary
+ conductor, which may not exist when the class field is not cyclic over the
+ base. In this case \kbd{bnrstark} is allowed to return a vector of
+ polynomials defining \emph{independent} relative extensions, whose compositum
+ is the requested class field. It was decided that it was more useful
+ to keep the extra information thus made available, hence the user has to take
+ the compositum herself.
+
+ Even if it exists, the auxiliary conductor may be so large that later
+ computations become unfeasible. (And of course, Stark's conjecture may simply
+ be wrong.) In case of difficulties, try \tet{rnfkummer}:
+ \bprog
+ ? bnr = bnrinit(bnfinit(y^8-12*y^6+36*y^4-36*y^2+9,1), 2, 1);
+ ? bnrstark(bnr)
+   ***   at top-level: bnrstark(bnr)
+   ***                 ^-------------
+   *** bnrstark: need 3919350809720744 coefficients in initzeta.
+   *** Computation impossible.
+ ? lift( rnfkummer(bnr) )
+ time = 24 ms.
+ %2 = x^2 + (1/3*y^6 - 11/3*y^4 + 8*y^2 - 5)
+ @eprog
diff --git a/src/functions/number_fields/dirzetak b/src/functions/number_fields/dirzetak
new file mode 100644
index 0000000..abb2930
--- /dev/null
+++ b/src/functions/number_fields/dirzetak
@@ -0,0 +1,9 @@
+Function: dirzetak
+Section: number_fields
+C-Name: dirzetak
+Prototype: GG
+Help: dirzetak(nf,b): Dirichlet series of the Dedekind zeta function of the
+ number field nf up to the bound b-1.
+Doc: gives as a vector the first $b$
+ coefficients of the \idx{Dedekind} zeta function of the number field $\var{nf}$
+ considered as a \idx{Dirichlet series}.
diff --git a/src/functions/number_fields/factornf b/src/functions/number_fields/factornf
new file mode 100644
index 0000000..892c6fe
--- /dev/null
+++ b/src/functions/number_fields/factornf
@@ -0,0 +1,31 @@
+Function: factornf
+Section: number_fields
+C-Name: polfnf
+Prototype: GG
+Help: factornf(x,t): factorization of the polynomial x over the number field
+ defined by the polynomial t.
+Doc: factorization of the univariate polynomial $x$
+ over the number field defined by the (univariate) polynomial $t$. $x$ may
+ have coefficients in $\Q$ or in the number field. The algorithm reduces to
+ factorization over $\Q$ (\idx{Trager}'s trick). The direct approach of
+ \tet{nffactor}, which uses \idx{van Hoeij}'s method in a relative setting, is
+ in general faster.
+
+ The main variable of $t$ must be of \emph{lower} priority than that of $x$
+ (see \secref{se:priority}). However if non-rational number field elements
+ occur (as polmods or polynomials) as coefficients of $x$, the variable of
+ these polmods \emph{must} be the same as the main variable of $t$. For
+ example
+
+ \bprog
+ ? factornf(x^2 + Mod(y, y^2+1), y^2+1);
+ ? factornf(x^2 + y, y^2+1); \\@com these two are OK
+ ? factornf(x^2 + Mod(z,z^2+1), y^2+1)
+   ***   at top-level: factornf(x^2+Mod(z,z
+   ***                 ^--------------------
+   *** factornf: inconsistent data in rnf function.
+ ? factornf(x^2 + z, y^2+1)
+   ***   at top-level: factornf(x^2+z,y^2+1
+   ***                 ^--------------------
+   *** factornf: incorrect variable in rnf function.
+ @eprog
diff --git a/src/functions/number_fields/galoisexport b/src/functions/number_fields/galoisexport
new file mode 100644
index 0000000..be21cb6
--- /dev/null
+++ b/src/functions/number_fields/galoisexport
@@ -0,0 +1,35 @@
+Function: galoisexport
+Section: number_fields
+C-Name: galoisexport
+Prototype: GD0,L,
+Help: galoisexport(gal,{flag}): gal being a Galois group as output by
+ galoisinit, output a string representing the underlying permutation group in
+ GAP notation (default) or Magma notation (flag = 1).
+Doc: \var{gal} being be a Galois group as output by \tet{galoisinit},
+ export the underlying permutation group as a string suitable
+ for (no flags or $\fl=0$) GAP or ($\fl=1$) Magma. The following example
+ compute the index of the underlying abstract group in the GAP library:
+ \bprog
+ ? G = galoisinit(x^6+108);
+ ? s = galoisexport(G)
+ %2 = "Group((1, 2, 3)(4, 5, 6), (1, 4)(2, 6)(3, 5))"
+ ? extern("echo \"IdGroup("s");\" | gap -q")
+ %3 = [6, 1]
+ ? galoisidentify(G)
+ %4 = [6, 1]
+ @eprog\noindent
+ This command also accepts subgroups returned by \kbd{galoissubgroups}.
+
+ To \emph{import} a GAP permutation into gp (for \tet{galoissubfields} for
+ instance), the following GAP function may be useful:
+ \bprog
+ PermToGP := function(p, n)
+   return Permuted([1..n],p);
+ end;
+
+ gap> p:= (1,26)(2,5)(3,17)(4,32)(6,9)(7,11)(8,24)(10,13)(12,15)(14,27)
+   (16,22)(18,28)(19,20)(21,29)(23,31)(25,30)
+ gap> PermToGP(p,32);
+ [ 26, 5, 17, 32, 2, 9, 11, 24, 6, 13, 7, 15, 10, 27, 12, 22, 3, 28, 20, 19,
+   29, 16, 31, 8, 30, 1, 14, 18, 21, 25, 23, 4 ]
+ @eprog
diff --git a/src/functions/number_fields/galoisfixedfield b/src/functions/number_fields/galoisfixedfield
new file mode 100644
index 0000000..63ece1e
--- /dev/null
+++ b/src/functions/number_fields/galoisfixedfield
@@ -0,0 +1,38 @@
+Function: galoisfixedfield
+Section: number_fields
+C-Name: galoisfixedfield
+Prototype: GGD0,L,Dn
+Help: galoisfixedfield(gal,perm,{flag},{v=y}): gal being a Galois group as
+ output by galoisinit and perm a subgroup, an element of gal.group or a vector
+ of such elements, return [P,x] such that P is a polynomial defining the fixed
+ field of gal[1] by the subgroup generated by perm, and x is a root of P in gal
+ expressed as a polmod in gal.pol. If flag is 1 return only P. If flag is 2
+ return [P,x,F] where F is the factorization of gal.pol over the field
+ defined by P, where the variable v stands for a root of P.
+Description:
+ (gen, gen, ?small, ?var):vec        galoisfixedfield($1, $2, $3, $4)
+Doc: \var{gal} being be a Galois group as output by \tet{galoisinit} and
+ \var{perm} an element of $\var{gal}.group$, a vector of such elements
+ or a subgroup of \var{gal} as returned by galoissubgroups,
+ computes the fixed field of \var{gal} by the automorphism defined by the
+ permutations \var{perm} of the roots $\var{gal}.roots$. $P$ is guaranteed to
+ be squarefree modulo $\var{gal}.p$.
+
+ If no flags or $\fl=0$, output format is the same as for \tet{nfsubfield},
+ returning $[P,x]$ such that $P$ is a polynomial defining the fixed field, and
+ $x$ is a root of $P$ expressed as a polmod in $\var{gal}.pol$.
+
+ If $\fl=1$ return only the polynomial $P$.
+
+ If $\fl=2$ return $[P,x,F]$ where $P$ and $x$ are as above and $F$ is the
+ factorization of $\var{gal}.pol$ over the field defined by $P$, where
+ variable $v$ ($y$ by default) stands for a root of $P$. The priority of $v$
+ must be less than the priority of the variable of $\var{gal}.pol$ (see
+ \secref{se:priority}). Example:
+
+ \bprog
+ ? G = galoisinit(x^4+1);
+ ? galoisfixedfield(G,G.group[2],2)
+ %2 = [x^2 + 2, Mod(x^3 + x, x^4 + 1), [x^2 - y*x - 1, x^2 + y*x - 1]]
+ @eprog\noindent
+ computes the factorization  $x^4+1=(x^2-\sqrt{-2}x-1)(x^2+\sqrt{-2}x-1)$
diff --git a/src/functions/number_fields/galoisgetpol b/src/functions/number_fields/galoisgetpol
new file mode 100644
index 0000000..e047887
--- /dev/null
+++ b/src/functions/number_fields/galoisgetpol
@@ -0,0 +1,37 @@
+Function: galoisgetpol
+Section: number_fields
+C-Name: galoisgetpol
+Prototype: LD0,L,D1,L,
+Description:
+ (small):int               galoisnbpol($1)
+ (small,):int              galoisnbpol($1)
+ (small,,):int             galoisnbpol($1)
+ (small,small,small):vec   galoisgetpol($1, $2 ,$3)
+Help: galoisgetpol(a,{b},{s}): Query the galpol package for a polynomial with
+ Galois group isomorphic to GAP4(a,b), totally real if s=1 (default) and
+ totally complex if s=2.  The output is a vector [pol, den] where pol is the
+ polynomial and den is the common denominator of the conjugates expressed
+ as a polynomial in a root of pol. If b and s are omitted, return the number of
+ isomorphism classes of groups of order a.
+Doc: Query the galpol package for a polynomial with Galois group isomorphic to
+ GAP4(a,b), totally real if $s=1$ (default) and totally complex if $s=2$. The
+ output is a vector [\kbd{pol}, \kbd{den}] where
+
+ \item  \kbd{pol} is the polynomial of degree $a$
+
+ \item \kbd{den} is the denominator of \kbd{nfgaloisconj(pol)}.
+ Pass it as an optional argument to \tet{galoisinit} or \tet{nfgaloisconj} to
+ speed them up:
+ \bprog
+ ? [pol,den] = galoisgetpol(64,4,1);
+ ? G = galoisinit(pol);
+ time = 352ms
+ ? galoisinit(pol, den);  \\ passing 'den' speeds up the computation
+ time = 264ms
+ ? % == %`
+ %4 = 1  \\ same answer
+ @eprog
+ If $b$ and $s$ are omitted, return the number of isomorphism classes of
+ groups of order $a$.
+Variant: Also available is \fun{GEN}{galoisnbpol}{long a} when $b$ and $s$
+ are omitted.
diff --git a/src/functions/number_fields/galoisidentify b/src/functions/number_fields/galoisidentify
new file mode 100644
index 0000000..e26177e
--- /dev/null
+++ b/src/functions/number_fields/galoisidentify
@@ -0,0 +1,23 @@
+Function: galoisidentify
+Section: number_fields
+C-Name: galoisidentify
+Prototype: G
+Help: galoisidentify(gal): gal being a Galois group as output by galoisinit,
+ output the isomorphism class of the underlying abstract group as a
+ two-components vector [o,i], where o is the group order, and i is the group
+ index in the GAP4 small group library.
+Doc: \var{gal} being be a Galois group as output by \tet{galoisinit},
+ output the isomorphism class of the underlying abstract group as a
+ two-components vector $[o,i]$, where $o$ is the group order, and $i$ is the
+ group index in the GAP4 Small Group library, by Hans Ulrich Besche, Bettina
+ Eick and Eamonn O'Brien.
+
+ This command also accepts subgroups returned by \kbd{galoissubgroups}.
+
+ The current implementation is limited to degree less or equal to $127$.
+ Some larger ``easy'' orders are also supported.
+
+ The output is similar to the output of the function \kbd{IdGroup} in GAP4.
+ Note that GAP4 \kbd{IdGroup} handles all groups of order less than $2000$
+ except $1024$, so you can use \tet{galoisexport} and GAP4 to identify large
+ Galois groups.
diff --git a/src/functions/number_fields/galoisinit b/src/functions/number_fields/galoisinit
new file mode 100644
index 0000000..194b37f
--- /dev/null
+++ b/src/functions/number_fields/galoisinit
@@ -0,0 +1,101 @@
+Function: galoisinit
+Section: number_fields
+C-Name: galoisinit
+Prototype: GDG
+Help: galoisinit(pol,{den}): pol being a polynomial or a number field as
+ output by nfinit defining a Galois extension of Q, compute the Galois group
+ and all necessary information for computing fixed fields. den is optional
+ and has the same meaning as in nfgaloisconj(,4)(see manual).
+Description:
+ (gen, ?int):gal        galoisinit($1, $2)
+Doc: computes the Galois group
+ and all necessary information for computing the fixed fields of the
+ Galois extension $K/\Q$ where $K$ is the number field defined by
+ $\var{pol}$ (monic irreducible polynomial in $\Z[X]$ or
+ a number field as output by \tet{nfinit}). The extension $K/\Q$ must be
+ Galois with Galois group ``weakly'' super-solvable, see below;
+ returns 0 otherwise. Hence this permits to quickly check whether a polynomial
+ of order strictly less than $36$ is Galois or not.
+
+ The algorithm used is an improved version of the paper
+ ``An efficient algorithm for the computation of Galois automorphisms'',
+ Bill Allombert, Math.~Comp, vol.~73, 245, 2001, pp.~359--375.
+
+ A group $G$ is said to be ``weakly'' super-solvable if there exists a
+ normal series
+
+ $\{1\} = H_0 \triangleleft H_1 \triangleleft \cdots \triangleleft H_{n-1}
+ \triangleleft H_n$
+
+ such that each $H_i$ is normal in $G$ and for $i<n$, each quotient group
+ $H_{i+1}/H_i$ is cyclic, and either $H_n=G$ (then $G$ is super-solvable) or
+ $G/H_n$ is isomorphic to either $A_4$ or $S_4$.
+
+ In practice, almost all small groups are WKSS, the exceptions having order
+ 36(1 exception), 48(2), 56(1), 60(1), 72(5), 75(1), 80(1), 96(10) and $\geq
+ 108$.
+
+ This function is a prerequisite for most of the \kbd{galois}$xxx$ routines.
+ For instance:
+
+ \bprog
+ P = x^6 + 108;
+ G = galoisinit(P);
+ L = galoissubgroups(G);
+ vector(#L, i, galoisisabelian(L[i],1))
+ vector(#L, i, galoisidentify(L[i]))
+ @eprog
+
+ The output is an 8-component vector \var{gal}.
+
+ $\var{gal}[1]$ contains the polynomial \var{pol}
+ (\kbd{\var{gal}.pol}).
+
+ $\var{gal}[2]$ is a three-components vector $[p,e,q]$ where $p$ is a
+ prime number (\kbd{\var{gal}.p}) such that \var{pol} totally split
+ modulo $p$ , $e$ is an integer and $q=p^e$ (\kbd{\var{gal}.mod}) is the
+ modulus of the roots in \kbd{\var{gal}.roots}.
+
+ $\var{gal}[3]$ is a vector $L$ containing the $p$-adic roots of
+ \var{pol} as integers implicitly modulo \kbd{\var{gal}.mod}.
+ (\kbd{\var{gal}.roots}).
+
+ $\var{gal}[4]$ is the inverse of the Vandermonde matrix of the
+ $p$-adic roots of \var{pol}, multiplied by $\var{gal}[5]$.
+
+ $\var{gal}[5]$ is a multiple of the least common denominator of the
+ automorphisms expressed as polynomial in a root of \var{pol}.
+
+ $\var{gal}[6]$ is the Galois group $G$ expressed as a vector of
+ permutations of $L$ (\kbd{\var{gal}.group}).
+
+ $\var{gal}[7]$ is a generating subset $S=[s_1,\ldots,s_g]$ of $G$
+ expressed as a vector of permutations of $L$ (\kbd{\var{gal}.gen}).
+
+ $\var{gal}[8]$ contains the relative orders $[o_1,\ldots,o_g]$ of
+ the generators of $S$ (\kbd{\var{gal}.orders}).
+
+ Let $H_n$ be as above, we have the following properties:
+
+ \quad\item if $G/H_n\simeq A_4$ then $[o_1,\ldots,o_g]$ ends by
+ $[2,2,3]$.
+
+ \quad\item if $G/H_n\simeq S_4$ then $[o_1,\ldots,o_g]$ ends by
+ $[2,2,3,2]$.
+
+ \quad\item for $1\leq i \leq g$ the subgroup of $G$ generated by
+ $[s_1,\ldots,s_g]$ is normal, with the exception of $i=g-2$ in the
+ $A_4$ case and of $i=g-3$ in the $S_A$ case.
+
+ \quad\item the relative order $o_i$ of $s_i$ is its order in the
+ quotient group $G/\langle s_1,\ldots,s_{i-1}\rangle$, with the same
+ exceptions.
+
+ \quad\item for any $x\in G$ there exists a unique family
+ $[e_1,\ldots,e_g]$ such that (no exceptions):
+
+ -- for $1\leq i \leq g$ we have $0\leq e_i<o_i$
+
+ -- $x=g_1^{e_1}g_2^{e_2}\ldots g_n^{e_n}$
+
+ If present $den$ must be a suitable value for $\var{gal}[5]$.
diff --git a/src/functions/number_fields/galoisisabelian b/src/functions/number_fields/galoisisabelian
new file mode 100644
index 0000000..921e2e2
--- /dev/null
+++ b/src/functions/number_fields/galoisisabelian
@@ -0,0 +1,12 @@
+Function: galoisisabelian
+Section: number_fields
+C-Name: galoisisabelian
+Prototype: GD0,L,
+Help: galoisisabelian(gal,{flag=0}): gal being as output by galoisinit,
+ return 0 if gal is not abelian, the HNF matrix of gal over gal.gen if
+ flag=0, 1 if flag is 1, and the SNF of gal is flag=2.
+Doc: \var{gal} being as output by \kbd{galoisinit}, return $0$ if
+ \var{gal} is not an abelian group, and the HNF matrix of \var{gal} over
+ \kbd{gal.gen} if $fl=0$, $1$ if $fl=1$.
+
+ This command also accepts subgroups returned by \kbd{galoissubgroups}.
diff --git a/src/functions/number_fields/galoisisnormal b/src/functions/number_fields/galoisisnormal
new file mode 100644
index 0000000..fdacc02
--- /dev/null
+++ b/src/functions/number_fields/galoisisnormal
@@ -0,0 +1,12 @@
+Function: galoisisnormal
+Section: number_fields
+C-Name: galoisisnormal
+Prototype: lGG
+Help: galoisisnormal(gal,subgrp): gal being as output by galoisinit,
+ and subgrp a subgroup of gal as output by galoissubgroups,
+ return 1 if subgrp is a normal subgroup of gal, else return 0.
+Doc: \var{gal} being as output by \kbd{galoisinit}, and \var{subgrp} a subgroup
+ of \var{gal} as output by \kbd{galoissubgroups},return $1$ if \var{subgrp} is a
+ normal subgroup of \var{gal}, else return 0.
+
+ This command also accepts subgroups returned by \kbd{galoissubgroups}.
diff --git a/src/functions/number_fields/galoispermtopol b/src/functions/number_fields/galoispermtopol
new file mode 100644
index 0000000..f91b03e
--- /dev/null
+++ b/src/functions/number_fields/galoispermtopol
@@ -0,0 +1,22 @@
+Function: galoispermtopol
+Section: number_fields
+C-Name: galoispermtopol
+Prototype: GG
+Help: galoispermtopol(gal,perm): gal being a Galois group as output by
+ galoisinit and perm a element of gal.group, return the polynomial defining
+ the corresponding Galois automorphism.
+Doc: \var{gal} being a
+ Galois group as output by \kbd{galoisinit} and \var{perm} a element of
+ $\var{gal}.group$, return the polynomial defining the Galois
+ automorphism, as output by \kbd{nfgaloisconj}, associated with the
+ permutation \var{perm} of the roots $\var{gal}.roots$. \var{perm} can
+ also be a vector or matrix, in this case, \kbd{galoispermtopol} is
+ applied to all components recursively.
+
+ \noindent Note that
+ \bprog
+ G = galoisinit(pol);
+ galoispermtopol(G, G[6])~
+ @eprog\noindent
+ is equivalent to \kbd{nfgaloisconj(pol)}, if degree of \var{pol} is greater
+ or equal to $2$.
diff --git a/src/functions/number_fields/galoissubcyclo b/src/functions/number_fields/galoissubcyclo
new file mode 100644
index 0000000..2d7caf7
--- /dev/null
+++ b/src/functions/number_fields/galoissubcyclo
@@ -0,0 +1,55 @@
+Function: galoissubcyclo
+Section: number_fields
+C-Name: galoissubcyclo
+Prototype: GDGD0,L,Dn
+Help: galoissubcyclo(N,H,{fl=0},{v}):Compute a polynomial (in variable v)
+ defining the subfield of Q(zeta_n) fixed by the subgroup H of (Z/nZ)*. N can
+ be an integer n, znstar(n) or bnrinit(bnfinit(y),[n,[1]],1). H can be given
+ by a generator, a set of generator given by a vector or a HNF matrix (see
+ manual). If flag is 1, output only the conductor of the abelian extension.
+ If flag is 2 output [pol,f] where pol is the polynomial and f the conductor.
+Doc: computes the subextension
+ of $\Q(\zeta_n)$ fixed by the subgroup $H \subset (\Z/n\Z)^*$. By the
+ Kronecker-Weber theorem, all abelian number fields can be generated in this
+ way (uniquely if $n$ is taken to be minimal).
+
+ \noindent The pair $(n, H)$ is deduced from the parameters $(N, H)$ as follows
+
+ \item $N$ an integer: then $n = N$; $H$ is a generator, i.e. an
+ integer or an integer modulo $n$; or a vector of generators.
+
+ \item $N$ the output of \kbd{znstar($n$)}. $H$ as in the first case
+ above, or a matrix, taken to be a HNF left divisor of the SNF for $(\Z/n\Z)^*$
+ (of type \kbd{$N$.cyc}), giving the generators of $H$ in terms of \kbd{$N$.gen}.
+
+ \item $N$ the output of \kbd{bnrinit(bnfinit(y), $m$, 1)} where $m$ is a
+ module. $H$ as in the first case, or a matrix taken to be a HNF left
+ divisor of the SNF for the ray class group modulo $m$
+ (of type \kbd{$N$.cyc}), giving the generators of $H$ in terms of \kbd{$N$.gen}.
+
+ In this last case, beware that $H$ is understood relatively to $N$; in
+ particular, if the infinite place does not divide the module, e.g if $m$ is
+ an integer, then it is not a subgroup of $(\Z/n\Z)^*$, but of its quotient by
+ $\{\pm 1\}$.
+
+ If $fl=0$, compute a polynomial (in the variable \var{v}) defining the
+ the subfield of $\Q(\zeta_n)$ fixed by the subgroup \var{H} of $(\Z/n\Z)^*$.
+
+ If $fl=1$, compute only the conductor of the abelian extension, as a module.
+
+ If $fl=2$, output $[pol, N]$, where $pol$ is the polynomial as output when
+ $fl=0$ and $N$ the conductor as output when $fl=1$.
+
+ The following function can be used to compute all subfields of
+ $\Q(\zeta_n)$ (of exact degree \kbd{d}, if \kbd{d} is set):
+ \bprog
+ polsubcyclo(n, d = -1)=
+ { my(bnr,L,IndexBound);
+   IndexBound = if (d < 0, n, [d]);
+   bnr = bnrinit(bnfinit(y), [n,[1]], 1);
+   L = subgrouplist(bnr, IndexBound, 1);
+   vector(#L,i, galoissubcyclo(bnr,L[i]));
+ }
+ @eprog\noindent
+ Setting \kbd{L = subgrouplist(bnr, IndexBound)} would produce subfields of exact
+ conductor $n\infty$.
diff --git a/src/functions/number_fields/galoissubfields b/src/functions/number_fields/galoissubfields
new file mode 100644
index 0000000..0368767
--- /dev/null
+++ b/src/functions/number_fields/galoissubfields
@@ -0,0 +1,9 @@
+Function: galoissubfields
+Section: number_fields
+C-Name: galoissubfields
+Prototype: GD0,L,Dn
+Help: galoissubfields(G,{flags=0},{v}):Output all the subfields of G. flags
+ have the same meaning as for galoisfixedfield.
+Doc: outputs all the subfields of the Galois group \var{G}, as a vector.
+ This works by applying \kbd{galoisfixedfield} to all subgroups. The meaning of
+ the flag \var{fl} is the same as for \kbd{galoisfixedfield}.
diff --git a/src/functions/number_fields/galoissubgroups b/src/functions/number_fields/galoissubgroups
new file mode 100644
index 0000000..31fcbe0
--- /dev/null
+++ b/src/functions/number_fields/galoissubgroups
@@ -0,0 +1,18 @@
+Function: galoissubgroups
+Section: number_fields
+C-Name: galoissubgroups
+Prototype: G
+Help: galoissubgroups(G):Output all the subgroups of G.
+Doc: outputs all the subgroups of the Galois group \kbd{gal}. A subgroup is a
+ vector [\var{gen}, \var{orders}], with the same meaning
+ as for $\var{gal}.gen$ and $\var{gal}.orders$. Hence \var{gen} is a vector of
+ permutations generating the subgroup, and \var{orders} is the relatives
+ orders of the generators. The cardinal of a subgroup is the product of the
+ relative orders. Such subgroup can be used instead of a Galois group in the
+ following command: \kbd{galoisisabelian}, \kbd{galoissubgroups},
+ \kbd{galoisexport} and \kbd{galoisidentify}.
+
+ To get the subfield fixed by a subgroup \var{sub} of \var{gal}, use
+ \bprog
+ galoisfixedfield(gal,sub[1])
+ @eprog
diff --git a/src/functions/number_fields/idealadd b/src/functions/number_fields/idealadd
new file mode 100644
index 0000000..5f176fb
--- /dev/null
+++ b/src/functions/number_fields/idealadd
@@ -0,0 +1,44 @@
+Function: idealadd
+Section: number_fields
+C-Name: idealadd
+Prototype: GGG
+Help: idealadd(nf,x,y): sum of two ideals x and y in the number field
+ defined by nf.
+Doc: sum of the two ideals $x$ and $y$ in the number field $\var{nf}$. The
+ result is given in HNF.
+ \bprog
+  ? K = nfinit(x^2 + 1);
+  ? a = idealadd(K, 2, x + 1)  \\ ideal generated by 2 and 1+I
+  %2 =
+  [2 1]
+
+  [0 1]
+  ? pr = idealprimedec(K, 5)[1];  \\ a prime ideal above 5
+  ? idealadd(K, a, pr)     \\ coprime, as expected
+  %4 =
+  [1 0]
+
+  [0 1]
+ @eprog\noindent
+ This function cannot be used to add arbitrary $\Z$-modules, since it assumes
+ that its arguments are ideals:
+ \bprog
+   ? b = Mat([1,0]~);
+   ? idealadd(K, b, b)     \\ only square t_MATs represent ideals
+   *** idealadd: non-square t_MAT in idealtyp.
+   ? c = [2, 0; 2, 0]; idealadd(K, c, c)   \\ non-sense
+   %6 =
+   [2 0]
+
+   [0 2]
+   ? d = [1, 0; 0, 2]; idealadd(K, d, d)   \\ non-sense
+   %7 =
+   [1 0]
+
+   [0 1]
+
+ @eprog\noindent In the last two examples, we get wrong results since the
+ matrices $c$ and $d$ do not correspond to an ideal: the $\Z$-span of their
+ columns (as usual interpreted as coordinates with respect to the integer basis
+ \kbd{K.zk}) is not an $O_K$-module. To add arbitrary $\Z$-modules generated
+ by the columns of matrices $A$ and $B$, use \kbd{mathnf(concat(A,B))}.
diff --git a/src/functions/number_fields/idealaddtoone b/src/functions/number_fields/idealaddtoone
new file mode 100644
index 0000000..ae836b7
--- /dev/null
+++ b/src/functions/number_fields/idealaddtoone
@@ -0,0 +1,17 @@
+Function: idealaddtoone
+Section: number_fields
+C-Name: idealaddtoone0
+Prototype: GGDG
+Help: idealaddtoone(nf,x,{y}): if y is omitted, when the sum of the ideals
+ in the number field K defined by nf and given in the vector x is equal to
+ Z_K, gives a vector of elements of the corresponding ideals who sum to 1.
+ Otherwise, x and y are ideals, and if they sum up to 1, find one element in
+ each of them such that the sum is 1.
+Doc: $x$ and $y$ being two co-prime
+ integral ideals (given in any form), this gives a two-component row vector
+ $[a,b]$ such that $a\in x$, $b\in y$ and $a+b=1$.
+
+ The alternative syntax $\kbd{idealaddtoone}(\var{nf},v)$, is supported, where
+ $v$ is a $k$-component vector of ideals (given in any form) which sum to
+ $\Z_K$. This outputs a $k$-component vector $e$ such that $e[i]\in x[i]$ for
+ $1\le i\le k$ and $\sum_{1\le i\le k}e[i]=1$.
diff --git a/src/functions/number_fields/idealappr b/src/functions/number_fields/idealappr
new file mode 100644
index 0000000..0b952bd
--- /dev/null
+++ b/src/functions/number_fields/idealappr
@@ -0,0 +1,26 @@
+Function: idealappr
+Section: number_fields
+C-Name: idealappr0
+Prototype: GGD0,L,
+Help: idealappr(nf,x,{flag=0}): x being a fractional ideal, gives an element
+ b such that v_p(b)=v_p(x) for all prime ideals p dividing x, and v_p(b)>=0
+ for all other p. If (optional) flag is non-null x must be a prime ideal
+ factorization with possibly zero exponents.
+Doc: if $x$ is a fractional ideal
+ (given in any form), gives an element $\alpha$ in $\var{nf}$ such that for
+ all prime ideals $\goth{p}$ such that the valuation of $x$ at $\goth{p}$ is
+ non-zero, we have $v_{\goth{p}}(\alpha)=v_{\goth{p}}(x)$, and
+ $v_{\goth{p}}(\alpha)\ge0$ for all other $\goth{p}$.
+
+ If $\fl$ is non-zero, $x$ must be given as a prime ideal factorization, as
+ output by \kbd{idealfactor}, but possibly with zero or negative exponents.
+ This yields an element $\alpha$ such that for all prime ideals $\goth{p}$
+ occurring in $x$, $v_{\goth{p}}(\alpha)$ is equal to the exponent of
+ $\goth{p}$ in $x$, and for all other prime ideals,
+ $v_{\goth{p}}(\alpha)\ge0$. This generalizes $\kbd{idealappr}(\var{nf},x,0)$
+ since zero exponents are allowed. Note that the algorithm used is slightly
+ different, so that
+ \bprog
+   idealappr(nf, idealfactor(nf,x))
+ @eprog\noindent
+ may not be the same as \kbd{idealappr(nf,x,1)}.
diff --git a/src/functions/number_fields/idealchinese b/src/functions/number_fields/idealchinese
new file mode 100644
index 0000000..2d2938a
--- /dev/null
+++ b/src/functions/number_fields/idealchinese
@@ -0,0 +1,14 @@
+Function: idealchinese
+Section: number_fields
+C-Name: idealchinese
+Prototype: GGG
+Help: idealchinese(nf,x,y): x being a prime ideal factorization and y a
+ vector of elements, gives an element b such that v_p(b-y_p)>=v_p(x) for all
+ prime ideals p dividing x, and v_p(b)>=0 for all other p.
+Doc: $x$ being a prime ideal factorization
+ (i.e.~a 2 by 2 matrix whose first column contains prime ideals, and the second
+ column integral exponents), $y$ a vector of elements in $\var{nf}$ indexed by
+ the ideals in $x$, computes an element $b$ such that
+
+ $v_{\goth{p}}(b - y_{\goth{p}}) \geq v_{\goth{p}}(x)$ for all prime ideals
+ in $x$ and $v_{\goth{p}}(b)\geq 0$ for all other $\goth{p}$.
diff --git a/src/functions/number_fields/idealcoprime b/src/functions/number_fields/idealcoprime
new file mode 100644
index 0000000..9e07444
--- /dev/null
+++ b/src/functions/number_fields/idealcoprime
@@ -0,0 +1,9 @@
+Function: idealcoprime
+Section: number_fields
+C-Name: idealcoprime
+Prototype: GGG
+Help: idealcoprime(nf,x,y): gives an element b in nf such that b. x is an
+ integral ideal coprime to the integral ideal y.
+Doc: given two integral ideals $x$ and $y$
+ in the number field $\var{nf}$, returns a $\beta$ in the field,
+ such that $\beta\cdot x$ is an integral ideal coprime to $y$.
diff --git a/src/functions/number_fields/idealdiv b/src/functions/number_fields/idealdiv
new file mode 100644
index 0000000..8c99bb0
--- /dev/null
+++ b/src/functions/number_fields/idealdiv
@@ -0,0 +1,20 @@
+Function: idealdiv
+Section: number_fields
+C-Name: idealdiv0
+Prototype: GGGD0,L,
+Help: idealdiv(nf,x,y,{flag=0}): quotient x/y of two ideals x and y in HNF
+ in the number field nf. If (optional) flag is non-null, the quotient is
+ supposed to be an integral ideal (slightly faster).
+Description:
+ (gen, gen, gen, ?0):gen        idealdiv($1, $2, $3)
+ (gen, gen, gen, 1):gen         idealdivexact($1, $2, $3)
+ (gen, gen, gen, #small):gen    $"invalid flag in idealdiv"
+ (gen, gen, gen, small):gen     idealdiv0($1, $2, $3, $4)
+Doc: quotient $x\cdot y^{-1}$ of the two ideals $x$ and $y$ in the number
+ field $\var{nf}$. The result is given in HNF.
+
+ If $\fl$ is non-zero, the quotient $x \cdot y^{-1}$ is assumed to be an
+ integral ideal. This can be much faster when the norm of the quotient is
+ small even though the norms of $x$ and $y$ are large.
+Variant: Also available are \fun{GEN}{idealdiv}{GEN nf, GEN x, GEN y}
+ ($\fl=0$) and \fun{GEN}{idealdivexact}{GEN nf, GEN x, GEN y} ($\fl=1$).
diff --git a/src/functions/number_fields/idealfactor b/src/functions/number_fields/idealfactor
new file mode 100644
index 0000000..eb2720e
--- /dev/null
+++ b/src/functions/number_fields/idealfactor
@@ -0,0 +1,10 @@
+Function: idealfactor
+Section: number_fields
+C-Name: idealfactor
+Prototype: GG
+Help: idealfactor(nf,x): factorization of the ideal x given in HNF into
+ prime ideals in the number field nf.
+Doc: factors into prime ideal powers the
+ ideal $x$ in the number field $\var{nf}$. The output format is similar to the
+ \kbd{factor} function, and the prime ideals are represented in the form
+ output by the \kbd{idealprimedec} function, i.e.~as 5-element vectors.
diff --git a/src/functions/number_fields/idealfactorback b/src/functions/number_fields/idealfactorback
new file mode 100644
index 0000000..e649a78
--- /dev/null
+++ b/src/functions/number_fields/idealfactorback
@@ -0,0 +1,53 @@
+Function: idealfactorback
+Section: number_fields
+C-Name: idealfactorback
+Prototype: GGDGD0,L,
+Help: idealfactorback(nf,f,{e},{flag = 0}): given a factorisation f, gives the
+ ideal product back. If e is present, f has to be a
+ vector of the same length, and we return the product of the f[i]^e[i]. If
+ flag is non-zero, perform idealred along the way.
+Doc: gives back the ideal corresponding to a factorization. The integer $1$
+ corresponds to the empty factorization.
+ If $e$ is present, $e$ and $f$ must be vectors of the same length ($e$ being
+ integral), and the corresponding factorization is the product of the
+ $f[i]^{e[i]}$.
+
+ If not, and $f$ is vector, it is understood as in the preceding case with $e$
+ a vector of 1s: we return the product of the $f[i]$. Finally, $f$ can be a
+ regular factorization, as produced by \kbd{idealfactor}.
+ \bprog
+ ? nf = nfinit(y^2+1); idealfactor(nf, 4 + 2*y)
+ %1 =
+ [[2, [1, 1]~, 2, 1, [1, 1]~] 2]
+
+ [[5, [2, 1]~, 1, 1, [-2, 1]~] 1]
+
+ ? idealfactorback(nf, %)
+ %2 =
+ [10 4]
+
+ [0  2]
+
+ ? f = %1[,1]; e = %1[,2]; idealfactorback(nf, f, e)
+ %3 =
+ [10 4]
+
+ [0  2]
+
+ ? % == idealhnf(nf, 4 + 2*y)
+ %4 = 1
+ @eprog
+ If \kbd{flag} is non-zero, perform ideal reductions (\tet{idealred}) along the
+ way. This is most useful if the ideals involved are all \emph{extended}
+ ideals (for instance with trivial principal part), so that the principal parts
+ extracted by \kbd{idealred} are not lost. Here is an example:
+ \bprog
+ ? f = vector(#f, i, [f[i], [;]]);  \\ transform to extended ideals
+ ? idealfactorback(nf, f, e, 1)
+ %6 = [[1, 0; 0, 1], [2, 1; [2, 1]~, 1]]
+ ? nffactorback(nf, %[2])
+ %7 = [4, 2]~
+ @eprog
+ The extended ideal returned in \kbd{\%6} is the trivial ideal $1$, extended
+ with a principal generator given in factored form. We use \tet{nffactorback}
+ to recover it in standard form.
diff --git a/src/functions/number_fields/idealfrobenius b/src/functions/number_fields/idealfrobenius
new file mode 100644
index 0000000..410f061
--- /dev/null
+++ b/src/functions/number_fields/idealfrobenius
@@ -0,0 +1,23 @@
+Function: idealfrobenius
+Section: number_fields
+C-Name: idealfrobenius
+Prototype: GGG
+Help: idealfrobenius(nf,gal,pr): Returns the Frobenius element (pr|nf/Q)
+ associated with the unramified prime ideal pr in prid format, in the Galois
+ group gal of the number field nf.
+Doc: Let $K$ be the number field defined by $nf$ and assume $K/\Q$ be a
+ Galois extension with Galois group given \kbd{gal=galoisinit(nf)},
+ and that $pr$ is the prime ideal $\goth{P}$ in prid format, and that
+ $\goth{P}$ is unramified.
+ This function returns a permutation of \kbd{gal.group} which defines the
+ automorphism $\sigma=\left(\goth{P}\over K/\Q \right)$, i.e the Frobenius
+ element associated to $\goth{P}$. If $p$ is the unique prime number
+ in $\goth{P}$, then $\sigma(x)\equiv x^p\mod\P$ for all $x\in\Z_K$.
+ \bprog
+ ? nf = nfinit(polcyclo(31));
+ ? gal = galoisinit(nf);
+ ? pr = idealprimedec(nf,101)[1];
+ ? g = idealfrobenius(nf,gal,pr);
+ ? galoispermtopol(gal,g)
+ %5 = x^8
+ @eprog\noindent This is correct since $101\equiv 8\mod{31}$.
diff --git a/src/functions/number_fields/idealhnf b/src/functions/number_fields/idealhnf
new file mode 100644
index 0000000..34c44bb
--- /dev/null
+++ b/src/functions/number_fields/idealhnf
@@ -0,0 +1,73 @@
+Function: idealhnf
+Section: number_fields
+C-Name: idealhnf0
+Prototype: GGDG
+Help: idealhnf(nf,u,{v}): hermite normal form of the ideal u in the number
+ field nf if v is omitted. If called as idealhnf(nf,u,v), the ideal
+ is given as uZ_K + vZ_K in the number field K defined by nf.
+Doc: gives the \idx{Hermite normal form} of the ideal $u\Z_K+v\Z_K$, where $u$
+ and $v$ are elements of the number field $K$ defined by \kbd{nf}.
+ \bprog
+ ? nf = nfinit(y^3 - 2);
+ ? idealhnf(nf, 2, y+1)
+ %2 =
+ [1 0 0]
+
+ [0 1 0]
+
+ [0 0 1]
+ ? idealhnf(nf, y/2, [0,0,1/3]~)
+ %3 =
+ [1/3 0 0]
+
+ [0 1/6 0]
+
+ [0 0 1/6]
+ @eprog
+
+ If $b$ is omitted, returns the HNF of the ideal defined by $u$: $u$ may be an
+ algebraic number (defining a principal ideal), a maximal ideal (as given by
+ \kbd{idealprimedec} or \kbd{idealfactor}), or a matrix whose columns give
+ generators for the ideal. This last format is a little complicated, but
+ useful to reduce general modules to the canonical form once in a while:
+
+ \item if strictly less than $N = [K:\Q]$ generators are given, $u$
+ is the $\Z_K$-module they generate,
+
+ \item if $N$ or more are given, it is \emph{assumed} that they form a
+ $\Z$-basis of the ideal, in particular that the matrix has maximal rank $N$.
+ This acts as \kbd{mathnf} since the $\Z_K$-module structure is (taken for
+ granted hence) not taken into account in this case.
+ \bprog
+ ? idealhnf(nf, idealprimedec(nf,2)[1])
+ %4 =
+ [2 0 0]
+
+ [0 1 0]
+
+ [0 0 1]
+ ? idealhnf(nf, [1,2;2,3;3,4])
+ %5 =
+ [1 0 0]
+
+ [0 1 0]
+
+ [0 0 1]
+ @eprog\noindent Finally, when $K$ is quadratic with discriminant $D_K$, we
+ allow $u =$ \kbd{Qfb(a,b,c)}, provided $b^2 - 4ac = D_K$. As usual,
+ this represents the ideal $a \Z + (1/2)(-b + \sqrt{D_K}) \Z$.
+ \bprog
+ ? K = nfinit(x^2 - 60); K.disc
+ %1 = 60
+ ? idealhnf(K, qfbprimeform(60,2))
+ %2 =
+ [2 1]
+
+ [0 1]
+ ? idealhnf(K, Qfb(1,2,3))
+   ***   at top-level: idealhnf(K,Qfb(1,2,3
+   ***                 ^--------------------
+   *** idealhnf: Qfb(1, 2, 3) has discriminant != 60 in idealhnf.
+ @eprog
+
+Variant: Also available is \fun{GEN}{idealhnf}{GEN nf, GEN a}.
diff --git a/src/functions/number_fields/idealintersect b/src/functions/number_fields/idealintersect
new file mode 100644
index 0000000..8e91a24
--- /dev/null
+++ b/src/functions/number_fields/idealintersect
@@ -0,0 +1,27 @@
+Function: idealintersect
+Section: number_fields
+C-Name: idealintersect
+Prototype: GGG
+Help: idealintersect(nf,A,B): intersection of two ideals A and B in the
+ number field defined by nf.
+Doc: intersection of the two ideals
+ $A$ and $B$ in the number field $\var{nf}$. The result is given in HNF.
+ \bprog
+ ? nf = nfinit(x^2+1);
+ ? idealintersect(nf, 2, x+1)
+ %2 =
+ [2 0]
+
+ [0 2]
+ @eprog
+
+ This function does not apply to general $\Z$-modules, e.g.~orders, since its
+ arguments are replaced by the ideals they generate. The following script
+ intersects $\Z$-modules $A$ and $B$ given by matrices of compatible
+ dimensions with integer coefficients:
+ \bprog
+ ZM_intersect(A,B) =
+ { my(Ker = matkerint(concat(A,B)));
+   mathnf( A * Ker[1..#A,] )
+ }
+ @eprog
diff --git a/src/functions/number_fields/idealinv b/src/functions/number_fields/idealinv
new file mode 100644
index 0000000..990811c
--- /dev/null
+++ b/src/functions/number_fields/idealinv
@@ -0,0 +1,11 @@
+Function: idealinv
+Section: number_fields
+C-Name: idealinv
+Prototype: GG
+Help: idealinv(nf,x): inverse of the ideal x in the number field nf.
+Description:
+ (gen, gen):gen        idealinv($1, $2)
+Doc: inverse of the ideal $x$ in the
+ number field $\var{nf}$, given in HNF. If $x$ is an extended
+ ideal\sidx{ideal (extended)}, its principal part is suitably
+ updated: i.e. inverting $[I,t]$, yields $[I^{-1}, 1/t]$.
diff --git a/src/functions/number_fields/ideallist b/src/functions/number_fields/ideallist
new file mode 100644
index 0000000..6b975dd
--- /dev/null
+++ b/src/functions/number_fields/ideallist
@@ -0,0 +1,56 @@
+Function: ideallist
+Section: number_fields
+C-Name: ideallist0
+Prototype: GLD4,L,
+Help: ideallist(nf,bound,{flag=4}): vector of vectors L of all idealstar of
+ all ideals of norm<=bound. If (optional) flag is present, its binary digits
+ are toggles meaning 1: give generators; 2: add units; 4: give only the
+ ideals and not the bid.
+Doc: computes the list
+ of all ideals of norm less or equal to \var{bound} in the number field
+ \var{nf}. The result is a row vector with exactly \var{bound} components.
+ Each component is itself a row vector containing the information about
+ ideals of a given norm, in no specific order, depending on the value of
+ $\fl$:
+
+ The possible values of $\fl$ are:
+
+ \quad 0: give the \var{bid} associated to the ideals, without generators.
+
+ \quad 1: as 0, but include the generators in the \var{bid}.
+
+ \quad 2: in this case, \var{nf} must be a \var{bnf} with units. Each
+ component is of the form $[\var{bid},U]$, where \var{bid} is as case 0
+ and $U$ is a vector of discrete logarithms of the units. More precisely, it
+ gives the \kbd{ideallog}s with respect to \var{bid} of \kbd{bnf.tufu}.
+ This structure is technical, and only meant to be used in conjunction with
+ \tet{bnrclassnolist} or \tet{bnrdisclist}.
+
+ \quad 3: as 2, but include the generators in the \var{bid}.
+
+ \quad 4: give only the HNF of the ideal.
+
+ \bprog
+ ? nf = nfinit(x^2+1);
+ ? L = ideallist(nf, 100);
+ ? L[1]
+ %3 = [[1, 0; 0, 1]]  \\@com A single ideal of norm 1
+ ? #L[65]
+ %4 = 4               \\@com There are 4 ideals of norm 4 in $\Z[i]$
+ @eprog
+ If one wants more information, one could do instead:
+ \bprog
+ ? nf = nfinit(x^2+1);
+ ? L = ideallist(nf, 100, 0);
+ ? l = L[25]; vector(#l, i, l[i].clgp)
+ %3 = [[20, [20]], [16, [4, 4]], [20, [20]]]
+ ? l[1].mod
+ %4 = [[25, 18; 0, 1], []]
+ ? l[2].mod
+ %5 = [[5, 0; 0, 5], []]
+ ? l[3].mod
+ %6 = [[25, 7; 0, 1], []]
+ @eprog\noindent where we ask for the structures of the $(\Z[i]/I)^*$ for all
+ three ideals of norm $25$. In fact, for all moduli with finite part of norm
+ $25$ and trivial Archimedean part, as the last 3 commands show. See
+ \tet{ideallistarch} to treat general moduli.
diff --git a/src/functions/number_fields/ideallistarch b/src/functions/number_fields/ideallistarch
new file mode 100644
index 0000000..d2aba67
--- /dev/null
+++ b/src/functions/number_fields/ideallistarch
@@ -0,0 +1,40 @@
+Function: ideallistarch
+Section: number_fields
+C-Name: ideallistarch
+Prototype: GGG
+Help: ideallistarch(nf,list,arch): list is a vector of vectors of of bid's as
+ output by ideallist. Return a vector of vectors with the same number of
+ components as the original list. The leaves give information about
+ moduli whose finite part is as in original list, in the same order, and
+ Archimedean part is now arch. The information contained is of the same kind
+ as was present in the input.
+Doc:
+ \var{list} is a vector of vectors of bid's, as output by \tet{ideallist} with
+ flag $0$ to $3$. Return a vector of vectors with the same number of
+ components as the original \var{list}. The leaves give information about
+ moduli whose finite part is as in original list, in the same order, and
+ Archimedean part is now \var{arch} (it was originally trivial). The
+ information contained is of the same kind as was present in the input; see
+ \tet{ideallist}, in particular the meaning of \fl.
+
+ \bprog
+ ? bnf = bnfinit(x^2-2);
+ ? bnf.sign
+ %2 = [2, 0]                         \\@com two places at infinity
+ ? L = ideallist(bnf, 100, 0);
+ ? l = L[98]; vector(#l, i, l[i].clgp)
+ %4 = [[42, [42]], [36, [6, 6]], [42, [42]]]
+ ? La = ideallistarch(bnf, L, [1,1]); \\@com add them to the modulus
+ ? l = La[98]; vector(#l, i, l[i].clgp)
+ %6 = [[168, [42, 2, 2]], [144, [6, 6, 2, 2]], [168, [42, 2, 2]]]
+ @eprog
+ Of course, the results above are obvious: adding $t$ places at infinity will
+ add $t$ copies of $\Z/2\Z$ to the ray class group. The following application
+ is more typical:
+ \bprog
+ ? L = ideallist(bnf, 100, 2);        \\@com units are required now
+ ? La = ideallistarch(bnf, L, [1,1]);
+ ? H = bnrclassnolist(bnf, La);
+ ? H[98];
+ %6 = [2, 12, 2]
+ @eprog
diff --git a/src/functions/number_fields/ideallog b/src/functions/number_fields/ideallog
new file mode 100644
index 0000000..ac08ed7
--- /dev/null
+++ b/src/functions/number_fields/ideallog
@@ -0,0 +1,20 @@
+Function: ideallog
+Section: number_fields
+C-Name: ideallog
+Prototype: GGG
+Help: ideallog(nf,x,bid): if bid is a big ideal, as given by
+ idealstar(nf,I,1) or idealstar(nf,I,2), gives the vector of exponents on the
+ generators bid[2][3] (even if these generators have not been computed).
+Doc: $\var{nf}$ is a number field,
+ \var{bid} is as output by \kbd{idealstar(nf, D, \dots)} and $x$ a
+ non-necessarily integral element of \var{nf} which must have valuation
+ equal to 0 at all prime ideals in the support of $\kbd{D}$. This function
+ computes the discrete logarithm of $x$ on the generators given in
+ \kbd{\var{bid}.gen}. In other words, if $g_i$ are these generators, of orders
+ $d_i$ respectively, the result is a column vector of integers $(x_i)$ such
+ that $0\le x_i<d_i$ and
+ $$x \equiv \prod_i g_i^{x_i} \pmod{\ ^*D}\enspace.$$
+ Note that when the support of \kbd{D} contains places at infinity, this
+ congruence implies also sign conditions on the associated real embeddings.
+ See \tet{znlog} for the limitations of the underlying discrete log algorithms.
+
diff --git a/src/functions/number_fields/idealmin b/src/functions/number_fields/idealmin
new file mode 100644
index 0000000..cf0e9fd
--- /dev/null
+++ b/src/functions/number_fields/idealmin
@@ -0,0 +1,9 @@
+Function: idealmin
+Section: number_fields
+C-Name: idealmin
+Prototype: GGDG
+Help: idealmin(nf,ix,{vdir}): pseudo-minimum of the ideal ix in the direction
+ vdir in the number field nf.
+Doc: \emph{This function is useless and kept for backward compatibility only,
+ use \kbd{idealred}}. Computes a pseudo-minimum of the ideal $x$ in the
+ direction \var{vdir} in the number field \var{nf}.
diff --git a/src/functions/number_fields/idealmul b/src/functions/number_fields/idealmul
new file mode 100644
index 0000000..24e9db0
--- /dev/null
+++ b/src/functions/number_fields/idealmul
@@ -0,0 +1,33 @@
+Function: idealmul
+Section: number_fields
+C-Name: idealmul0
+Prototype: GGGD0,L,
+Help: idealmul(nf,x,y,{flag=0}): product of the two ideals x and y in the
+ number field nf. If (optional) flag is non-nul, reduce the result.
+Description:
+ (gen, gen, gen, ?0):gen        idealmul($1, $2, $3)
+ (gen, gen, gen, 1):gen         idealmulred($1, $2, $3)
+ (gen, gen, gen, #small):gen    $"invalid flag in idealmul"
+ (gen, gen, gen, small):gen     idealmul0($1, $2, $3, $4)
+Doc: ideal multiplication of the ideals $x$ and $y$ in the number field
+ \var{nf}; the result is the ideal product in HNF. If either $x$ or $y$
+ are extended ideals\sidx{ideal (extended)}, their principal part is suitably
+ updated: i.e. multiplying $[I,t]$, $[J,u]$ yields $[IJ, tu]$; multiplying
+ $I$ and $[J, u]$ yields $[IJ, u]$.
+ \bprog
+ ? nf = nfinit(x^2 + 1);
+ ? idealmul(nf, 2, x+1)
+ %2 =
+ [4 2]
+
+ [0 2]
+ ? idealmul(nf, [2, x], x+1)        \\ extended ideal * ideal
+ %4 = [[4, 2; 0, 2], x]
+ ? idealmul(nf, [2, x], [x+1, x])   \\ two extended ideals
+ %5 = [[4, 2; 0, 2], [-1, 0]~]
+ @eprog\noindent
+ If $\fl$ is non-zero, reduce the result using \kbd{idealred}.
+Variant:
+ \noindent See also
+ \fun{GEN}{idealmul}{GEN nf, GEN x, GEN y} ($\fl=0$) and
+ \fun{GEN}{idealmulred}{GEN nf, GEN x, GEN y} ($\fl\neq0$).
diff --git a/src/functions/number_fields/idealnorm b/src/functions/number_fields/idealnorm
new file mode 100644
index 0000000..af0324e
--- /dev/null
+++ b/src/functions/number_fields/idealnorm
@@ -0,0 +1,6 @@
+Function: idealnorm
+Section: number_fields
+C-Name: idealnorm
+Prototype: GG
+Help: idealnorm(nf,x): norm of the ideal x in the number field nf.
+Doc: computes the norm of the ideal~$x$ in the number field~$\var{nf}$.
diff --git a/src/functions/number_fields/idealnumden b/src/functions/number_fields/idealnumden
new file mode 100644
index 0000000..ed12ce3
--- /dev/null
+++ b/src/functions/number_fields/idealnumden
@@ -0,0 +1,13 @@
+Function: idealnumden
+Section: number_fields
+C-Name: idealnumden
+Prototype: GG
+Help: idealnumden(nf,x): returns [A,B], where A,B are coprime integer ideals
+ such that x = A/B
+Doc: returns $[A,B]$, where $A,B$ are coprime integer ideals
+ such that $x = A/B$, in the number field $\var{nf}$.
+ \bprog
+ ? nf = nfinit(x^2+1);
+ ? idealnumden(nf, (x+1)/2)
+ %2 = [[1, 0; 0, 1], [2, 1; 0, 1]]
+ @eprog
diff --git a/src/functions/number_fields/idealpow b/src/functions/number_fields/idealpow
new file mode 100644
index 0000000..1b978a0
--- /dev/null
+++ b/src/functions/number_fields/idealpow
@@ -0,0 +1,21 @@
+Function: idealpow
+Section: number_fields
+C-Name: idealpow0
+Prototype: GGGD0,L,
+Help: idealpow(nf,x,k,{flag=0}): k-th power of the ideal x in HNF in the
+ number field nf. If (optional) flag is non-null, reduce the result.
+Doc: computes the $k$-th power of
+ the ideal $x$ in the number field $\var{nf}$; $k\in\Z$.
+ If $x$ is an extended
+ ideal\sidx{ideal (extended)}, its principal part is suitably
+ updated: i.e. raising $[I,t]$ to the $k$-th power, yields $[I^k, t^k]$.
+
+ If $\fl$ is non-zero, reduce the result using \kbd{idealred}, \emph{throughout
+ the (binary) powering process}; in particular, this is \emph{not} the same as
+ as $\kbd{idealpow}(\var{nf},x,k)$ followed by reduction.
+
+Variant:
+ \noindent See also
+ \fun{GEN}{idealpow}{GEN nf, GEN x, GEN k} and
+ \fun{GEN}{idealpows}{GEN nf, GEN x, long k} ($\fl = 0$).
+ Corresponding to $\fl=1$ is \fun{GEN}{idealpowred}{GEN nf, GEN vp, GEN k}.
diff --git a/src/functions/number_fields/idealprimedec b/src/functions/number_fields/idealprimedec
new file mode 100644
index 0000000..7f3695e
--- /dev/null
+++ b/src/functions/number_fields/idealprimedec
@@ -0,0 +1,44 @@
+Function: idealprimedec
+Section: number_fields
+C-Name: idealprimedec
+Prototype: GG
+Help: idealprimedec(nf,p): prime ideal decomposition of the prime number p
+ in the number field nf as a vector of 5 component vectors [p,a,e,f,b]
+ representing the prime ideals pZ_K+a. Z_K, e,f as usual, a as vector of
+ components on the integral basis, b Lenstra's constant.
+Doc: computes the prime ideal
+ decomposition of the (positive) prime number $p$ in the number field $K$
+ represented by \var{nf}. If a non-prime $p$ is given the result is undefined.
+
+ The result is a vector of \tev{prid} structures, each representing one of the
+ prime ideals above $p$ in the number field $\var{nf}$. The representation
+ $\kbd{pr}=[p,a,e,f,\var{mb}]$ of a prime ideal means the following: $a$ and
+ is an algebraic integer in the maximal order $\Z_K$ and the prime ideal is
+ equal to $\goth{p} = p\Z_K + a\Z_K$;
+ $e$ is the ramification index; $f$ is the residual index;
+ finally, \var{mb} is the multiplication table associated to the algebraic
+ integer $b$ is such that $\goth{p}^{-1}=\Z_K+ b/ p\Z_K$, which is used
+ internally to compute valuations. In other words if $p$ is inert,
+ then \var{mb} is the integer $1$, and otherwise it's a square \typ{MAT}
+ whose $j$-th column is $b \cdot \kbd{nf.zk[j]}$.
+
+ The algebraic number $a$ is guaranteed to have a
+ valuation equal to 1 at the prime ideal (this is automatic if $e>1$).
+
+ The components of \kbd{pr} should be accessed by member functions: \kbd{pr.p},
+ \kbd{pr.e}, \kbd{pr.f}, and \kbd{pr.gen} (returns the vector $[p,a]$):
+ \bprog
+ ? K = nfinit(x^3-2);
+ ? L = idealprimedec(K, 5);
+ ? #L       \\ 2 primes above 5 in Q(2^(1/3))
+ %3 = 2
+ ? p1 = L[1]; p2 = L[2];
+ ? [p1.e, p1.f]    \\ the first is unramified of degree 1
+ %4 = [1, 1]
+ ? [p2.e, p2.f]    \\ the second is unramified of degree 2
+ %5 = [1, 2]
+ ? p1.gen
+ %6 = [5, [2, 1, 0]~]
+ ? nfbasistoalg(K, %[2])  \\ a uniformizer for p1
+ %7 = Mod(x + 2, x^3 - 2)
+ @eprog
diff --git a/src/functions/number_fields/idealprincipalunits b/src/functions/number_fields/idealprincipalunits
new file mode 100644
index 0000000..f51e6fc
--- /dev/null
+++ b/src/functions/number_fields/idealprincipalunits
@@ -0,0 +1,20 @@
+Function: idealprincipalunits
+Section: number_fields
+C-Name: idealprincipalunits
+Prototype: GGL
+Help: idealprincipalunits(nf,pr,k): returns the structure [no, cyc, gen]
+ of the multiplicative group (1 + pr) / (1 + pr^k)^*.
+Doc: given a prime ideal in \tet{idealprimedec} format,
+ returns the multiplicative group $(1 + \var{pr}) / (1 + \var{pr}^k)$ as an
+ abelian group. This function is much faster than \tet{idealstar} when the
+ norm of \var{pr} is large, since it avoids (useless) work in the
+ multiplicative group of the residue field.
+ \bprog
+ ? K = nfinit(y^2+1);
+ ? P = idealprimedec(K,2)[1];
+ ? G = idealprincipalunits(K, P, 20);
+ ? G.cyc
+ [512, 256, 4]   \\ Z/512 x Z/256 x Z/4
+ ? G.gen
+ %5 = [[-1, -2]~, 1021, [0, -1]~] \\ minimal generators of given order
+ @eprog
diff --git a/src/functions/number_fields/idealramgroups b/src/functions/number_fields/idealramgroups
new file mode 100644
index 0000000..bf67970
--- /dev/null
+++ b/src/functions/number_fields/idealramgroups
@@ -0,0 +1,45 @@
+Function: idealramgroups
+Section: number_fields
+C-Name: idealramgroups
+Prototype: GGG
+Help: idealramgroups(nf,gal,pr): let pr be a prime ideal in prid format, and
+ gal the Galois group of the number field nf, return a vector g such that g[1]
+ is the decomposition group of pr, g[2] is the inertia group, g[i] is the
+ (i-2)th ramification group of pr, all trivial subgroups being omitted.
+Doc: Let $K$ be the number field defined by \var{nf} and assume that $K/\Q$ is
+ Galois with Galois group $G$ given by \kbd{gal=galoisinit(nf)}.
+ Let \var{pr} be the prime ideal $\goth{P}$ in prid format.
+ This function returns a vector $g$ of subgroups of \kbd{gal}
+ as follow:
+
+ \item \kbd{g[1]} is the decomposition group of $\goth{P}$,
+
+ \item \kbd{g[2]} is $G_0(\goth{P})$, the inertia group of $\goth{P}$,
+
+ and for $i\geq 2$,
+
+ \item \kbd{g[i]} is $G_{i-2}(\goth{P})$, the $i-2$-th \idx{ramification
+ group} of $\goth{P}$.
+
+ \noindent The length of $g$ is the number of non-trivial groups in the
+ sequence, thus is $0$ if $e=1$ and $f=1$, and $1$ if $f>1$ and $e=1$.
+ The following function computes the cardinality of a subgroup of $G$,
+ as given by the components of $g$:
+ \bprog
+ card(H) =my(o=H[2]); prod(i=1,#o,o[i]);
+ @eprog
+ \bprog
+ ? nf=nfinit(x^6+3); gal=galoisinit(nf); pr=idealprimedec(nf,3)[1];
+ ? g = idealramgroups(nf, gal, pr);
+ ? apply(card,g)
+ %4 = [6, 6, 3, 3, 3] \\ cardinalities of the G_i
+ @eprog
+
+ \bprog
+ ? nf=nfinit(x^6+108); gal=galoisinit(nf); pr=idealprimedec(nf,2)[1];
+ ? iso=idealramgroups(nf,gal,pr)[2]
+ %4 = [[Vecsmall([2, 3, 1, 5, 6, 4])], Vecsmall([3])]
+ ? nfdisc(galoisfixedfield(gal,iso,1))
+ %5 = -3
+ @eprog\noindent The field fixed by the inertia group of $2$ is not ramified at
+ $2$.
diff --git a/src/functions/number_fields/idealred b/src/functions/number_fields/idealred
new file mode 100644
index 0000000..b93b57a
--- /dev/null
+++ b/src/functions/number_fields/idealred
@@ -0,0 +1,59 @@
+Function: idealred
+Section: number_fields
+C-Name: idealred0
+Prototype: GGDG
+Help: idealred(nf,I,{v=0}): LLL reduction of the ideal I in the number
+ field nf along direction v, in HNF.
+Doc: \idx{LLL} reduction of
+ the ideal $I$ in the number field \var{nf}, along the direction $v$.
+ The $v$ parameter is best left omitted, but if it is present, it must
+ be an $\kbd{nf.r1} + \kbd{nf.r2}$-component vector of \emph{non-negative}
+ integers. (What counts is the relative magnitude of the entries: if all
+ entries are equal, the effect is the same as if the vector had been omitted.)
+
+ This function finds a ``small'' $a$ in $I$ (see the end for technical details).
+ The result is the Hermite normal form of
+ the ``reduced'' ideal $J = r I/a$, where $r$ is the unique rational number such
+ that $J$ is integral and primitive. (This is usually not a reduced ideal in
+ the sense of \idx{Buchmann}.)
+ \bprog
+ ? K = nfinit(y^2+1);
+ ? P = idealprimedec(K,5)[1];
+ ? idealred(K, P)
+ %3 =
+ [1 0]
+
+ [0 1]
+ @eprog\noindent More often than not, a \idx{principal ideal} yields the unit
+ ideal as above. This is a quick and dirty way to check if ideals are principal,
+ but it is not a necessary condition: a non-trivial result does not prove that
+ the ideal is non-principal. For guaranteed results, see \kbd{bnfisprincipal},
+ which requires the computation of a full \kbd{bnf} structure.
+
+ If the input is an extended ideal $[I,s]$, the output is $[J,sa/r]$; this way,
+ one can keep track of the principal ideal part:
+ \bprog
+ ? idealred(K, [P, 1])
+ %5 = [[1, 0; 0, 1], [-2, 1]~]
+ @eprog\noindent
+ meaning that $P$ is generated by $[-2, 1]~$. The number field element in the
+ extended part is an algebraic number in any form \emph{or} a factorization
+ matrix (in terms of number field elements, not ideals!). In the latter case,
+ elements stay in factored form, which is a convenient way to avoid
+ coefficient explosion; see also \tet{idealpow}.
+
+ \misctitle{Technical note} The routine computes an LLL-reduced
+ basis for the lattice $I$ equipped with the quadratic form
+ $$|| x ||_v^2 = \sum_{i=1}^{r_1+r_2} 2^{v_i}\varepsilon_i|\sigma_i(x)|^2,$$
+ where as usual the $\sigma_i$ are the (real and) complex embeddings and
+ $\varepsilon_i = 1$, resp.~$2$, for a real, resp.~complex place. The element
+ $a$ is simply the first vector in the LLL basis. The only reason you may want
+ to try to change some directions and set some $v_i\neq 0$ is to randomize
+ the elements found for a fixed ideal, which is heuristically useful in index
+ calculus algorithms like \tet{bnfinit} and \tet{bnfisprincipal}.
+
+ \misctitle{Even more technical note} In fact, the above is a white lie.
+ We do not use $||\cdot||_v$ exactly but a rescaled rounded variant which
+ gets us faster and simpler LLLs. There's no harm since we are not using any
+ theoretical property of $a$ after all, except that it belongs to $I$ and is
+ ``expected to be small''.
diff --git a/src/functions/number_fields/idealstar b/src/functions/number_fields/idealstar
new file mode 100644
index 0000000..592441a
--- /dev/null
+++ b/src/functions/number_fields/idealstar
@@ -0,0 +1,43 @@
+Function: idealstar
+Section: number_fields
+C-Name: idealstar0
+Prototype: GGD1,L,
+Help: idealstar(nf,I,{flag=1}): gives the structure of (Z_K/I)^*. flag is
+ optional, and can be 0: simply gives the structure as a 3-component vector v
+ such that v[1] is the order (i.e. eulerphi(I)), v[2] is a vector of cyclic
+ components, and v[3] is a vector giving the corresponding generators. If
+ flag=1 (default), gives idealstarinit, i.e. a 6-component vector
+ [I,v,fa,f2,U,V] where v is as above without the generators, fa is the prime
+ ideal factorisation of I and f2, U and V are technical but essential to work
+ in (Z_K/I)^*. Finally if flag=2, same as with flag=1 except that the
+ generators are also given.
+Doc: outputs a \var{bid} structure,
+ necessary for computing in the finite abelian group $G = (\Z_K/I)^*$. Here,
+ \var{nf} is a number field and $I$ is a \var{modulus}: either an ideal in any
+ form, or a row vector whose first component is an ideal and whose second
+ component is a row vector of $r_1$ 0 or 1. Ideals can also be given
+ by a factorization into prime ideals, as produced by \tet{idealfactor}.
+
+ This \var{bid} is used in \tet{ideallog} to compute discrete logarithms. It
+ also contains useful information which can be conveniently retrieved as
+ \kbd{\var{bid}.mod} (the modulus),
+ \kbd{\var{bid}.clgp} ($G$ as a finite abelian group),
+ \kbd{\var{bid}.no} (the cardinality of $G$),
+ \kbd{\var{bid}.cyc} (elementary divisors) and
+ \kbd{\var{bid}.gen} (generators).
+
+ If $\fl=1$ (default), the result is a \var{bid} structure without
+ generators.
+
+ If $\fl=2$, as $\fl=1$, but including generators, which wastes some time.
+
+ If $\fl=0$, only outputs $(\Z_K/I)^*$ as an abelian group,
+ i.e as a 3-component vector $[h,d,g]$: $h$ is the order, $d$ is the vector of
+ SNF\sidx{Smith normal form} cyclic components and $g$ the corresponding
+ generators.
+Variant: Instead the above hardcoded numerical flags, one should rather use
+ \fun{GEN}{Idealstar}{GEN nf, GEN ideal, long flag}, where \kbd{flag} is
+ an or-ed combination of \tet{nf_GEN} (include generators) and \tet{nf_INIT}
+ (return a full \kbd{bid}, not a group), possibly $0$. This offers
+ one more combination: gen, but no init.
+
diff --git a/src/functions/number_fields/idealtwoelt b/src/functions/number_fields/idealtwoelt
new file mode 100644
index 0000000..d3e93c5
--- /dev/null
+++ b/src/functions/number_fields/idealtwoelt
@@ -0,0 +1,26 @@
+Function: idealtwoelt
+Section: number_fields
+C-Name: idealtwoelt0
+Prototype: GGDG
+Help: idealtwoelt(nf,x,{a}): two-element representation of an ideal x in the
+ number field nf. If (optional) a is non-zero, first element will be equal to a.
+Doc: computes a two-element
+ representation of the ideal $x$ in the number field $\var{nf}$, combining a
+ random search and an approximation theorem; $x$ is an ideal
+ in any form (possibly an extended ideal, whose principal part is ignored)
+
+ \item When called as \kbd{idealtwoelt(nf,x)}, the result is a row vector
+ $[a,\alpha]$ with two components such that $x=a\Z_K+\alpha\Z_K$ and $a$ is
+ chosen to be the positive generator of $x\cap\Z$, unless $x$ was given as a
+ principal ideal (in which case we may choose $a = 0$). The algorithm
+ uses a fast lazy factorization of $x\cap \Z$ and runs in randomized
+ polynomial time.
+
+ \item When called as \kbd{idealtwoelt(nf,x,a)} with an explicit non-zero $a$
+ supplied as third argument, the function assumes that $a \in x$ and returns
+ $\alpha\in x$ such that $x = a\Z_K + \alpha\Z_K$. Note that we must factor
+ $a$ in this case, and the algorithm is generally much slower than the
+ default variant.
+Variant: Also available are
+ \fun{GEN}{idealtwoelt}{GEN nf, GEN x} and
+ \fun{GEN}{idealtwoelt2}{GEN nf, GEN x, GEN a}.
diff --git a/src/functions/number_fields/idealval b/src/functions/number_fields/idealval
new file mode 100644
index 0000000..9d654ac
--- /dev/null
+++ b/src/functions/number_fields/idealval
@@ -0,0 +1,8 @@
+Function: idealval
+Section: number_fields
+C-Name: idealval
+Prototype: lGGG
+Help: idealval(nf,x,pr): valuation at pr given in idealprimedec format of the
+ ideal x in the number field nf.
+Doc: gives the valuation of the ideal $x$ at the prime ideal \var{pr} in the
+ number field $\var{nf}$, where \var{pr} is in \kbd{idealprimedec} format.
diff --git a/src/functions/number_fields/matalgtobasis b/src/functions/number_fields/matalgtobasis
new file mode 100644
index 0000000..1023094
--- /dev/null
+++ b/src/functions/number_fields/matalgtobasis
@@ -0,0 +1,9 @@
+Function: matalgtobasis
+Section: number_fields
+C-Name: matalgtobasis
+Prototype: GG
+Help: matalgtobasis(nf,x): nfalgtobasis applied to every element of the
+ vector or matrix x.
+Doc: $\var{nf}$ being a number field in \kbd{nfinit} format, and $x$ a
+ (row or column) vector or matrix, apply \tet{nfalgtobasis} to each entry
+ of $x$.
diff --git a/src/functions/number_fields/matbasistoalg b/src/functions/number_fields/matbasistoalg
new file mode 100644
index 0000000..65b8eb6
--- /dev/null
+++ b/src/functions/number_fields/matbasistoalg
@@ -0,0 +1,9 @@
+Function: matbasistoalg
+Section: number_fields
+C-Name: matbasistoalg
+Prototype: GG
+Help: matbasistoalg(nf,x): nfbasistoalg applied to every element of the
+ matrix or vector x.
+Doc: $\var{nf}$ being a number field in \kbd{nfinit} format, and $x$ a
+ (row or column) vector or matrix, apply \tet{nfbasistoalg} to each entry
+ of $x$.
diff --git a/src/functions/number_fields/modreverse b/src/functions/number_fields/modreverse
new file mode 100644
index 0000000..b50e9a9
--- /dev/null
+++ b/src/functions/number_fields/modreverse
@@ -0,0 +1,41 @@
+Function: modreverse
+Section: number_fields
+C-Name: modreverse
+Prototype: G
+Help: modreverse(z): reverse polmod of the polmod z, if it exists.
+Doc: let $z = \kbd{Mod(A, T)}$ be a polmod, and $Q$ be its minimal
+ polynomial, which must satisfy $\text{deg}(Q) = \text{deg}(T)$.
+ Returns a ``reverse polmod'' \kbd{Mod(B, Q)}, which is a root of $T$.
+
+ This is quite useful when one changes the generating element in algebraic
+ extensions:
+ \bprog
+ ? u = Mod(x, x^3 - x -1); v = u^5;
+ ? w = modreverse(v)
+ %2 = Mod(x^2 - 4*x + 1, x^3 - 5*x^2 + 4*x - 1)
+ @eprog\noindent
+ which means that $x^3 - 5x^2 + 4x -1$ is another defining polynomial for the
+ cubic field
+ $$\Q(u) = \Q[x]/(x^3 - x - 1) = \Q[x]/(x^3 - 5x^2 + 4x - 1) = \Q(v),$$
+ and that $u \to v^2 - 4v + 1$ gives an explicit isomorphism. From this, it is
+ easy to convert elements between the $A(u)\in \Q(u)$ and $B(v)\in \Q(v)$
+ representations:
+ \bprog
+ ? A = u^2 + 2*u + 3; subst(lift(A), 'x, w)
+ %3 = Mod(x^2 - 3*x + 3, x^3 - 5*x^2 + 4*x - 1)
+ ? B = v^2 + v + 1;   subst(lift(B), 'x, v)
+ %4 = Mod(26*x^2 + 31*x + 26, x^3 - x - 1)
+ @eprog
+ If the minimal polynomial of $z$ has lower degree than expected, the routine
+ fails
+ \bprog
+ ? u = Mod(-x^3 + 9*x, x^4 - 10*x^2 + 1)
+ ? modreverse(u)
+  *** modreverse: domain error in modreverse: deg(minpoly(z)) < 4
+  ***   Break loop: type 'break' to go back to GP prompt
+ break> Vec( dbg_err() ) \\ ask for more info
+ ["e_DOMAIN", "modreverse", "deg(minpoly(z))", "<", 4,
+   Mod(-x^3 + 9*x, x^4 - 10*x^2 + 1)]
+ break> minpoly(u)
+ x^2 - 8
+ @eprog
diff --git a/src/functions/number_fields/newtonpoly b/src/functions/number_fields/newtonpoly
new file mode 100644
index 0000000..951e531
--- /dev/null
+++ b/src/functions/number_fields/newtonpoly
@@ -0,0 +1,13 @@
+Function: newtonpoly
+Section: number_fields
+C-Name: newtonpoly
+Prototype: GG
+Help: newtonpoly(x,p): Newton polygon of polynomial x with respect to the
+ prime p.
+Doc: gives the vector of the slopes of the Newton
+ polygon of the polynomial $x$ with respect to the prime number $p$. The $n$
+ components of the vector are in decreasing order, where $n$ is equal to the
+ degree of $x$. Vertical slopes occur iff the constant coefficient of $x$ is
+ zero and are denoted by \tet{LONG_MAX}, the biggest single precision
+ integer representable on the machine ($2^{31}-1$ (resp.~$2^{63}-1$) on 32-bit
+ (resp.~64-bit) machines), see \secref{se:valuation}.
diff --git a/src/functions/number_fields/nfalgtobasis b/src/functions/number_fields/nfalgtobasis
new file mode 100644
index 0000000..ae79b86
--- /dev/null
+++ b/src/functions/number_fields/nfalgtobasis
@@ -0,0 +1,20 @@
+Function: nfalgtobasis
+Section: number_fields
+C-Name: algtobasis
+Prototype: GG
+Help: nfalgtobasis(nf,x): transforms the algebraic number x into a column
+ vector on the integral basis nf.zk.
+Doc: Given an algebraic number $x$ in the number field $\var{nf}$,
+ transforms it to a column vector on the integral basis \kbd{\var{nf}.zk}.
+ \bprog
+ ? nf = nfinit(y^2 + 4);
+ ? nf.zk
+ %2 = [1, 1/2*y]
+ ? nfalgtobasis(nf, [1,1]~)
+ %3 = [1, 1]~
+ ? nfalgtobasis(nf, y)
+ %4 = [0, 2]~
+ ? nfalgtobasis(nf, Mod(y, y^2+4))
+ %4 = [0, 2]~
+ @eprog
+ This is the inverse function of \kbd{nfbasistoalg}.
diff --git a/src/functions/number_fields/nfbasis b/src/functions/number_fields/nfbasis
new file mode 100644
index 0000000..227260f
--- /dev/null
+++ b/src/functions/number_fields/nfbasis
@@ -0,0 +1,116 @@
+Function: nfbasis
+Section: number_fields
+C-Name: nfbasis_gp
+Prototype: GDGDG
+Help: nfbasis(T): integral basis of the field Q[a], where a is
+ a root of the polynomial T, using the round 4 algorithm. An argument
+ [T,listP] is possible, where listP is a list of primes (to get an
+ order which is maximal at certain primes only) or a prime bound.
+Doc:
+ Let $T(X)$ be an irreducible polynomial with integral coefficients. This
+ function returns an \idx{integral basis} of the number field defined by $T$,
+ that is a $\Z$-basis of its maximal order. The basis elements are given as
+ elements in $\Q[X]/(T)$:
+ \bprog
+ ? nfbasis(x^2 + 1)
+ %1 = [1, x]
+ @eprog
+ This function uses a modified version of the \idx{round 4} algorithm,
+ due to David \idx{Ford}, Sebastian \idx{Pauli} and Xavier \idx{Roblot}.
+
+ \misctitle{Local basis, orders maximal at certain primes}
+
+ Obtaining the maximal order is hard: it requires factoring the discriminant
+ $D$ of $T$. Obtaining an order which is maximal at a finite explicit set of
+ primes is easy, but if may then be a strict suborder of the maximal order. To
+ specify that we are interested in a given set of places only, we can replace
+ the argument $T$ by an argument $[T,\var{listP}]$, where \var{listP} encodes
+ the primes we are interested in: it must be a factorization matrix, a vector
+ of integers or a single integer.
+
+ \item Vector: we assume that it contains distinct \emph{prime} numbers.
+
+ \item Matrix: we assume that it is a two-column matrix of a
+ (partial) factorization of $D$; namely the first column contains
+ \emph{primes} and the second one the valuation of $D$ at each of these
+ primes.
+
+ \item Integer $B$: this is replaced by the vector of primes up to $B$. Note
+ that the function will use at least $O(B)$ time: a small value, about
+ $10^5$, should be enough for most applications. Values larger than $2^{32}$
+ are not supported.
+
+ In all these cases, the primes may or may not divide the discriminant $D$
+ of $T$. The function then returns a $\Z$-basis of an order whose index is
+ not divisible by any of these prime numbers. The result is actually a global
+ integral basis if all prime divisors of the \emph{field} discriminant are
+ included! Note that \kbd{nfinit} has built-in support for such
+ a check:
+ \bprog
+ ? K = nfinit([T, listP]);
+ ? nfcertify(K)   \\ we computed an actual maximal order
+ %2 = [];
+ @eprog\noindent The first line initializes a number field structure
+ incorporating \kbd{nfbasis([T, listP]} in place of a proven integral basis.
+ The second line certifies that the resulting structure is correct. This
+ allows to create an \kbd{nf} structure associated to the number field $K =
+ \Q[X]/(T)$, when the discriminant of $T$ cannot be factored completely,
+ whereas the prime divisors of $\disc K$ are known.
+
+ Of course, if \var{listP} contains a single prime number $p$,
+ the function returns a local integral basis for $\Z_p[X]/(T)$:
+ \bprog
+ ? nfbasis(x^2+x-1001)
+ %1 = [1, 1/3*x - 1/3]
+ ? nfbasis( [x^2+x-1001, [2]] )
+ %2 = [1, x]
+ @eprog
+
+ \misctitle{The Buchmann-Lenstra algorithm}
+
+ We now complicate the picture: it is in fact allowed to include
+ \emph{composite} numbers instead of primes
+ in \kbd{listP} (Vector or Matrix case), provided they are pairwise coprime.
+ The result will still be a correct integral basis \emph{if}
+ the field discriminant factors completely over the actual primes in the list.
+ Adding a composite $C$ such that $C^2$ \emph{divides} $D$ may help because
+ when we consider $C$ as a prime and run the algorithm, two good things can
+ happen: either we
+ succeed in proving that no prime dividing $C$ can divide the index
+ (without actually needing to find those primes), or the computation
+ exhibits a non-trivial zero divisor, thereby factoring $C$ and
+ we go on with the refined factorization. (Note that including a $C$
+ such that $C^2$ does not divide $D$ is useless.) If neither happen, then the
+ computed basis need not generate the maximal order. Here is an example:
+ \bprog
+ ? B = 10^5;
+ ? P = factor(poldisc(T), B)[,1]; \\ primes <= B dividing D + cofactor
+ ? basis = nfbasis([T, listP])
+ ? disc = nfdisc([T, listP])
+ @eprog\noindent We obtain the maximal order and its discriminant if the
+ field discriminant factors
+ completely over the primes less than $B$ (together with the primes
+ contained in the \tet{addprimes} table). This can be tested as follows:
+ \bprog
+   check = factor(disc, B);
+   lastp = check[-1..-1,1];
+   if (lastp > B && !setsearch(addprimes(), lastp),
+     warning("nf may be incorrect!"))
+ @eprog\noindent
+ This is a sufficient but not a necessary condition, hence the warning,
+ instead of an error. N.B. \kbd{lastp} is the last entry
+ in the first column of the \kbd{check} matrix, i.e. the largest prime
+ dividing \kbd{nf.disc} if $\leq B$ or if it belongs to the prime table.
+
+ The function \tet{nfcertify} speeds up and automates the above process:
+ \bprog
+ ? B = 10^5;
+ ? nf = nfinit([T, B]);
+ ? nfcertify(nf)
+ %3 = []      \\ nf is unconditionally correct
+ ? basis = nf.zk;
+ ? disc = nf.disc;
+ @eprog
+
+ \synt{nfbasis}{GEN T, GEN *d, GEN listP = NULL}, which returns the order
+ basis, and where \kbd{*d} receives the order discriminant.
diff --git a/src/functions/number_fields/nfbasistoalg b/src/functions/number_fields/nfbasistoalg
new file mode 100644
index 0000000..22f4177
--- /dev/null
+++ b/src/functions/number_fields/nfbasistoalg
@@ -0,0 +1,20 @@
+Function: nfbasistoalg
+Section: number_fields
+C-Name: basistoalg
+Prototype: GG
+Help: nfbasistoalg(nf,x): transforms the column vector x on the integral
+ basis into an algebraic number.
+Doc: Given an algebraic number $x$ in the number field \kbd{nf}, transforms it
+ into \typ{POLMOD} form.
+ \bprog
+ ? nf = nfinit(y^2 + 4);
+ ? nf.zk
+ %2 = [1, 1/2*y]
+ ? nfbasistoalg(nf, [1,1]~)
+ %3 = Mod(1/2*y + 1, y^2 + 4)
+ ? nfbasistoalg(nf, y)
+ %4 = Mod(y, y^2 + 4)
+ ? nfbasistoalg(nf, Mod(y, y^2+4))
+ %4 = Mod(y, y^2 + 4)
+ @eprog
+ This is the inverse function of \kbd{nfalgtobasis}.
diff --git a/src/functions/number_fields/nfcertify b/src/functions/number_fields/nfcertify
new file mode 100644
index 0000000..a157826
--- /dev/null
+++ b/src/functions/number_fields/nfcertify
@@ -0,0 +1,18 @@
+Function: nfcertify
+Section: number_fields
+C-Name: nfcertify
+Prototype: G
+Help: nfcertify(nf): returns a vector of composite integers used to certify
+ nf.zk and nf.disc unconditionally (both are correct when the output
+ is the empty vector).
+Doc: $\var{nf}$ being as output by
+ \kbd{nfinit}, checks whether the integer basis is known unconditionally.
+ This is in particular useful when the argument to \kbd{nfinit} was of the
+ form $[T, \kbd{listP}]$, specifying a finite list of primes when
+ $p$-maximality had to be proven.
+
+ The function returns a vector of composite integers. If this vector is
+ empty, then \kbd{nf.zk} and \kbd{nf.disc} are correct. Otherwise, the
+ result is dubious. In order to obtain a certified result, one must
+ completely factor each of the given integers, then \kbd{addprime} each of
+ them, then check whether \kbd{nfdisc(nf.pol)} is equal to \kbd{nf.disc}.
diff --git a/src/functions/number_fields/nfdetint b/src/functions/number_fields/nfdetint
new file mode 100644
index 0000000..e4e6ef8
--- /dev/null
+++ b/src/functions/number_fields/nfdetint
@@ -0,0 +1,9 @@
+Function: nfdetint
+Section: number_fields
+C-Name: nfdetint
+Prototype: GG
+Help: nfdetint(nf,x): multiple of the ideal determinant of the pseudo
+ generating set x.
+Doc: given a pseudo-matrix $x$, computes a
+ non-zero ideal contained in (i.e.~multiple of) the determinant of $x$. This
+ is particularly useful in conjunction with \kbd{nfhnfmod}.
diff --git a/src/functions/number_fields/nfdisc b/src/functions/number_fields/nfdisc
new file mode 100644
index 0000000..ec0bcc8
--- /dev/null
+++ b/src/functions/number_fields/nfdisc
@@ -0,0 +1,37 @@
+Function: nfdisc
+Section: number_fields
+C-Name: nfdisc_gp
+Prototype: GDGDG
+Help: nfdisc(T): discriminant of the number field defined by
+ the polynomial T. An argument [T,listP] is possible, where listP is a list
+ of primes or a prime bound.
+Doc: \idx{field discriminant} of the number field defined by the integral,
+ preferably monic, irreducible polynomial $T(X)$. Returns the discriminant of
+ the number field $\Q[X]/(T)$, using the Round $4$ algorithm.
+
+ \misctitle{Local discriminants, valuations at certain primes}
+
+ As in \kbd{nfbasis}, the argument $T$ can be replaced by $[T,\var{listP}]$,
+ where \kbd{listP} is as in \kbd{nfbasis}: a vector of
+ pairwise coprime integers (usually distinct primes), a factorization matrix,
+ or a single integer. In that case, the function returns the discriminant of
+ an order whose basis is given by \kbd{nfbasis(T,listP)}, which need not be
+ the maximal order, and whose valuation at a prime entry in \kbd{listP} is the
+ same as the valuation of the field discriminant.
+
+ In particular, if \kbd{listP} is $[p]$ for a prime $p$, we can
+ return the $p$-adic discriminant of the maximal order of $\Z_p[X]/(T)$,
+ as a power of $p$, as follows:
+ \bprog
+ ? padicdisc(T,p) = p^valuation(nfdisc(T,[p]), p);
+ ? nfdisc(x^2 + 6)
+ %1 = -24
+ ? padicdisc(x^2 + 6, 2)
+ %2 = 8
+ ? padicdisc(x^2 + 6, 3)
+ %3 = 3
+ @eprog
+
+ \synt{nfdisc}{GEN T} (\kbd{listP = NULL}). Also available is
+ \fun{GEN}{nfbasis}{GEN T, GEN *d, GEN listP = NULL}, which returns the order
+ basis, and where \kbd{*d} receives the order discriminant.
diff --git a/src/functions/number_fields/nfeltadd b/src/functions/number_fields/nfeltadd
new file mode 100644
index 0000000..a527e23
--- /dev/null
+++ b/src/functions/number_fields/nfeltadd
@@ -0,0 +1,8 @@
+Function: nfeltadd
+Section: number_fields
+C-Name: nfadd
+Prototype: GGG
+Help: nfadd(nf,x,y): element x+y in nf.
+Doc:
+ given two elements $x$ and $y$ in
+ \var{nf}, computes their sum $x+y$ in the number field $\var{nf}$.
diff --git a/src/functions/number_fields/nfeltdiv b/src/functions/number_fields/nfeltdiv
new file mode 100644
index 0000000..b1544f9
--- /dev/null
+++ b/src/functions/number_fields/nfeltdiv
@@ -0,0 +1,7 @@
+Function: nfeltdiv
+Section: number_fields
+C-Name: nfdiv
+Prototype: GGG
+Help: nfdiv(nf,x,y): element x/y in nf.
+Doc: given two elements $x$ and $y$ in
+ \var{nf}, computes their quotient $x/y$ in the number field $\var{nf}$.
diff --git a/src/functions/number_fields/nfeltdiveuc b/src/functions/number_fields/nfeltdiveuc
new file mode 100644
index 0000000..a3a9902
--- /dev/null
+++ b/src/functions/number_fields/nfeltdiveuc
@@ -0,0 +1,9 @@
+Function: nfeltdiveuc
+Section: number_fields
+C-Name: nfdiveuc
+Prototype: GGG
+Help: nfdiveuc(nf,x,y): gives algebraic integer q such that x-by is small.
+Doc: given two elements $x$ and $y$ in
+ \var{nf}, computes an algebraic integer $q$ in the number field $\var{nf}$
+ such that the components of $x-qy$ are reasonably small. In fact, this is
+ functionally identical to \kbd{round(nfdiv(\var{nf},x,y))}.
diff --git a/src/functions/number_fields/nfeltdivmodpr b/src/functions/number_fields/nfeltdivmodpr
new file mode 100644
index 0000000..092a043
--- /dev/null
+++ b/src/functions/number_fields/nfeltdivmodpr
@@ -0,0 +1,12 @@
+Function: nfeltdivmodpr
+Section: number_fields
+C-Name: nfdivmodpr
+Prototype: GGGG
+Help: nfeltdivmodpr(nf,x,y,pr): element x/y modulo pr in nf, where pr is in
+ modpr format (see nfmodprinit).
+Doc: given two elements $x$
+ and $y$ in \var{nf} and \var{pr} a prime ideal in \kbd{modpr} format (see
+ \tet{nfmodprinit}), computes their quotient $x / y$ modulo the prime ideal
+ \var{pr}.
+Variant: This function is normally useless in library mode. Project your
+ inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
diff --git a/src/functions/number_fields/nfeltdivrem b/src/functions/number_fields/nfeltdivrem
new file mode 100644
index 0000000..f2d5b6b
--- /dev/null
+++ b/src/functions/number_fields/nfeltdivrem
@@ -0,0 +1,9 @@
+Function: nfeltdivrem
+Section: number_fields
+C-Name: nfdivrem
+Prototype: GGG
+Help: nfeltdivrem(nf,x,y): gives [q,r] such that r=x-by is small.
+Doc: given two elements $x$ and $y$ in
+ \var{nf}, gives a two-element row vector $[q,r]$ such that $x=qy+r$, $q$ is
+ an algebraic integer in $\var{nf}$, and the components of $r$ are
+ reasonably small.
diff --git a/src/functions/number_fields/nfeltmod b/src/functions/number_fields/nfeltmod
new file mode 100644
index 0000000..e88c41f
--- /dev/null
+++ b/src/functions/number_fields/nfeltmod
@@ -0,0 +1,12 @@
+Function: nfeltmod
+Section: number_fields
+C-Name: nfmod
+Prototype: GGG
+Help: nfeltmod(nf,x,y): gives r such that r=x-by is small with q algebraic
+ integer.
+Doc:
+ given two elements $x$ and $y$ in
+ \var{nf}, computes an element $r$ of $\var{nf}$ of the form $r=x-qy$ with
+ $q$ and algebraic integer, and such that $r$ is small. This is functionally
+ identical to
+ $$\kbd{x - nfmul(\var{nf},round(nfdiv(\var{nf},x,y)),y)}.$$
diff --git a/src/functions/number_fields/nfeltmul b/src/functions/number_fields/nfeltmul
new file mode 100644
index 0000000..f70d933
--- /dev/null
+++ b/src/functions/number_fields/nfeltmul
@@ -0,0 +1,8 @@
+Function: nfeltmul
+Section: number_fields
+C-Name: nfmul
+Prototype: GGG
+Help: nfmul(nf,x,y): element x.y in nf.
+Doc:
+ given two elements $x$ and $y$ in
+ \var{nf}, computes their product $x*y$ in the number field $\var{nf}$.
diff --git a/src/functions/number_fields/nfeltmulmodpr b/src/functions/number_fields/nfeltmulmodpr
new file mode 100644
index 0000000..ede272e
--- /dev/null
+++ b/src/functions/number_fields/nfeltmulmodpr
@@ -0,0 +1,12 @@
+Function: nfeltmulmodpr
+Section: number_fields
+C-Name: nfmulmodpr
+Prototype: GGGG
+Help: nfeltmulmodpr(nf,x,y,pr): element x.y modulo pr in nf, where pr is in
+ modpr format (see nfmodprinit).
+Doc: given two elements $x$ and
+ $y$ in \var{nf} and \var{pr} a prime ideal in \kbd{modpr} format (see
+ \tet{nfmodprinit}), computes their product $x*y$ modulo the prime ideal
+ \var{pr}.
+Variant: This function is normally useless in library mode. Project your
+ inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
diff --git a/src/functions/number_fields/nfeltnorm b/src/functions/number_fields/nfeltnorm
new file mode 100644
index 0000000..1ec4a72
--- /dev/null
+++ b/src/functions/number_fields/nfeltnorm
@@ -0,0 +1,6 @@
+Function: nfeltnorm
+Section: number_fields
+C-Name: nfnorm
+Prototype: GG
+Help: nfeltnorm(nf,x): norm of x.
+Doc: returns the absolute norm of $x$.
diff --git a/src/functions/number_fields/nfeltpow b/src/functions/number_fields/nfeltpow
new file mode 100644
index 0000000..c3706d0
--- /dev/null
+++ b/src/functions/number_fields/nfeltpow
@@ -0,0 +1,9 @@
+Function: nfeltpow
+Section: number_fields
+C-Name: nfpow
+Prototype: GGG
+Help: nfeltpow(nf,x,k): element x^k in nf.
+Doc: given an element $x$ in \var{nf}, and a positive or negative integer $k$,
+ computes $x^k$ in the number field $\var{nf}$.
+Variant: \fun{GEN}{nfinv}{GEN nf, GEN x} correspond to $k = -1$, and
+ \fun{GEN}{nfsqr}{GEN nf,GEN x} to $k = 2$.
diff --git a/src/functions/number_fields/nfeltpowmodpr b/src/functions/number_fields/nfeltpowmodpr
new file mode 100644
index 0000000..7654560
--- /dev/null
+++ b/src/functions/number_fields/nfeltpowmodpr
@@ -0,0 +1,11 @@
+Function: nfeltpowmodpr
+Section: number_fields
+C-Name: nfpowmodpr
+Prototype: GGGG
+Help: nfeltpowmodpr(nf,x,k,pr): element x^k modulo pr in nf, where pr is in
+ modpr format (see nfmodprinit).
+Doc: given an element $x$ in \var{nf}, an integer $k$ and a prime ideal
+ \var{pr} in \kbd{modpr} format
+ (see \tet{nfmodprinit}), computes $x^k$ modulo the prime ideal \var{pr}.
+Variant: This function is normally useless in library mode. Project your
+ inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
diff --git a/src/functions/number_fields/nfeltreduce b/src/functions/number_fields/nfeltreduce
new file mode 100644
index 0000000..cb81346
--- /dev/null
+++ b/src/functions/number_fields/nfeltreduce
@@ -0,0 +1,10 @@
+Function: nfeltreduce
+Section: number_fields
+C-Name: nfreduce
+Prototype: GGG
+Help: nfeltreduce(nf,a,id): gives r such that a-r is in the ideal id and r
+ is small.
+Doc: given an ideal \var{id} in
+ Hermite normal form and an element $a$ of the number field $\var{nf}$,
+ finds an element $r$ in $\var{nf}$ such that $a-r$ belongs to the ideal
+ and $r$ is small.
diff --git a/src/functions/number_fields/nfeltreducemodpr b/src/functions/number_fields/nfeltreducemodpr
new file mode 100644
index 0000000..22c0c6e
--- /dev/null
+++ b/src/functions/number_fields/nfeltreducemodpr
@@ -0,0 +1,11 @@
+Function: nfeltreducemodpr
+Section: number_fields
+C-Name: nfreducemodpr
+Prototype: GGG
+Help: nfeltreducemodpr(nf,x,pr): element x modulo pr in nf, where pr is in
+ modpr format (see nfmodprinit).
+Doc: given an element $x$ of the number field $\var{nf}$ and a prime ideal
+ \var{pr} in \kbd{modpr} format compute a canonical representative for the
+ class of $x$ modulo \var{pr}.
+Variant: This function is normally useless in library mode. Project your
+ inputs to the residue field using \kbd{nf\_to\_Fq}, then work there.
diff --git a/src/functions/number_fields/nfelttrace b/src/functions/number_fields/nfelttrace
new file mode 100644
index 0000000..97f5704
--- /dev/null
+++ b/src/functions/number_fields/nfelttrace
@@ -0,0 +1,6 @@
+Function: nfelttrace
+Section: number_fields
+C-Name: nftrace
+Prototype: GG
+Help: nfelttrace(nf,x): trace of x.
+Doc: returns the absolute trace of $x$.
diff --git a/src/functions/number_fields/nfeltval b/src/functions/number_fields/nfeltval
new file mode 100644
index 0000000..62100e0
--- /dev/null
+++ b/src/functions/number_fields/nfeltval
@@ -0,0 +1,12 @@
+Function: nfeltval
+Section: number_fields
+C-Name: nfval
+Prototype: lGGG
+Help: nfeltval(nf,x,pr): valuation of element x at the prime pr as output by
+ idealprimedec.
+Doc: given an element $x$ in
+ \var{nf} and a prime ideal \var{pr} in the format output by
+ \kbd{idealprimedec}, computes the valuation at \var{pr} of the
+ element $x$. The same result can be obtained using
+ \kbd{idealval(\var{nf},x,\var{pr})}, since $x$ is then converted to a
+ principal ideal.
diff --git a/src/functions/number_fields/nffactor b/src/functions/number_fields/nffactor
new file mode 100644
index 0000000..b01a6de
--- /dev/null
+++ b/src/functions/number_fields/nffactor
@@ -0,0 +1,32 @@
+Function: nffactor
+Section: number_fields
+C-Name: nffactor
+Prototype: GG
+Help: nffactor(nf,T): factor polynomial T in number field nf.
+Doc: factorization of the univariate
+ polynomial $T$ over the number field $\var{nf}$ given by \kbd{nfinit}; $T$
+ has coefficients in $\var{nf}$ (i.e.~either scalar, polmod, polynomial or
+ column vector). The factors are sorted by increasing degree.
+
+ The main variable of $\var{nf}$ must be of \emph{lower}
+ priority than that of $T$, see \secref{se:priority}. However if
+ the polynomial defining the number field occurs explicitly  in the
+ coefficients of $T$ as modulus of a \typ{POLMOD} or as a \typ{POL}
+ coefficient, its main variable must be \emph{the same} as the main variable
+ of $T$. For example,
+ \bprog
+ ? nf = nfinit(y^2 + 1);
+ ? nffactor(nf, x^2 + y); \\@com OK
+ ? nffactor(nf, x^2 + Mod(y, y^2+1)); \\ @com OK
+ ? nffactor(nf, x^2 + Mod(z, z^2+1)); \\ @com WRONG
+ @eprog
+
+ It is possible to input a defining polynomial for \var{nf}
+ instead, but this is in general less efficient since parts of an \kbd{nf}
+ structure will then be computed internally. This is useful in two
+ situations: when you do not need the \kbd{nf} elsewhere, or when you cannot
+ compute the field discriminant due to integer factorization difficulties. In
+ the latter case, if you must use a partial discriminant factorization (as
+ allowed by both \tet{nfdisc} or \tet{nfbasis}) to build a partially correct
+ \var{nf} structure, always input \kbd{nf.pol} to \kbd{nffactor}, and not your
+ makeshift \var{nf}: otherwise factors could be missed.
diff --git a/src/functions/number_fields/nffactorback b/src/functions/number_fields/nffactorback
new file mode 100644
index 0000000..a8451ab
--- /dev/null
+++ b/src/functions/number_fields/nffactorback
@@ -0,0 +1,23 @@
+Function: nffactorback
+Section: number_fields
+C-Name: nffactorback
+Prototype: GGDG
+Help: nffactorback(nf,f,{e}): given a factorisation f, returns
+ the factored object back as an nf element.
+Doc: gives back the \kbd{nf} element corresponding to a factorization.
+ The integer $1$ corresponds to the empty factorization.
+
+ If $e$ is present, $e$ and $f$ must be vectors of the same length ($e$ being
+ integral), and the corresponding factorization is the product of the
+ $f[i]^{e[i]}$.
+
+ If not, and $f$ is vector, it is understood as in the preceding case with $e$
+ a vector of 1s: we return the product of the $f[i]$. Finally, $f$ can be a
+ regular factorization matrix.
+ \bprog
+ ? nf = nfinit(y^2+1);
+ ? nffactorback(nf, [3, y+1, [1,2]~], [1, 2, 3])
+ %2 = [12, -66]~
+ ? 3 * (I+1)^2 * (1+2*I)^3
+ %3 = 12 - 66*I
+ @eprog
diff --git a/src/functions/number_fields/nffactormod b/src/functions/number_fields/nffactormod
new file mode 100644
index 0000000..4eb6176
--- /dev/null
+++ b/src/functions/number_fields/nffactormod
@@ -0,0 +1,28 @@
+Function: nffactormod
+Section: number_fields
+C-Name: nffactormod
+Prototype: GGG
+Help: nffactormod(nf,Q,pr): factor polynomial Q modulo prime ideal pr
+ in number field nf.
+Doc: factors the univariate polynomial $Q$ modulo the prime ideal \var{pr} in
+ the number field $\var{nf}$. The coefficients of $Q$ belong to the number
+ field (scalar, polmod, polynomial, even column vector) and the main variable
+ of $\var{nf}$ must be of lower priority than that of $Q$ (see
+ \secref{se:priority}). The prime ideal \var{pr} is either in
+ \tet{idealprimedec} or (preferred) \tet{modprinit} format. The coefficients
+ of the polynomial factors are lifted to elements of \var{nf}:
+ \bprog
+ ? K = nfinit(y^2+1);
+ ? P = idealprimedec(K, 3)[1];
+ ? nffactormod(K, x^2 + y*x + 18*y+1, P)
+ %3 =
+ [x + (2*y + 1) 1]
+
+ [x + (2*y + 2) 1]
+ ? P = nfmodprinit(K, P);  \\ convert to nfmodprinit format
+ ? nffactormod(K, x^2 + y*x + 18*y+1)
+ [x + (2*y + 1) 1]
+
+ [x + (2*y + 2) 1]
+ @eprog\noindent Same result, of course, here about 10\% faster due to the
+ precomputation.
diff --git a/src/functions/number_fields/nfgaloisapply b/src/functions/number_fields/nfgaloisapply
new file mode 100644
index 0000000..5cab8dc
--- /dev/null
+++ b/src/functions/number_fields/nfgaloisapply
@@ -0,0 +1,49 @@
+Function: nfgaloisapply
+Section: number_fields
+C-Name: galoisapply
+Prototype: GGG
+Help: nfgaloisapply(nf,aut,x): Apply the Galois automorphism aut to the object
+ x (element or ideal) in the number field nf.
+Doc: let $\var{nf}$ be a
+ number field as output by \kbd{nfinit}, and let \var{aut} be a \idx{Galois}
+ automorphism of $\var{nf}$ expressed by its image on the field generator
+ (such automorphisms can be found using \kbd{nfgaloisconj}). The function
+ computes the action of the automorphism \var{aut} on the object $x$ in the
+ number field; $x$ can be a number field element, or an ideal (possibly
+ extended). Because of possible confusion with elements and ideals, other
+ vector or matrix arguments are forbidden.
+  \bprog
+  ? nf = nfinit(x^2+1);
+  ? L = nfgaloisconj(nf)
+  %2 = [-x, x]~
+  ? aut = L[1]; /* the non-trivial automorphism */
+  ? nfgaloisapply(nf, aut, x)
+  %4 = Mod(-x, x^2 + 1)
+  ? P = idealprimedec(nf,5); /* prime ideals above 5 */
+  ? nfgaloisapply(nf, aut, P[2]) == P[1]
+  %7 = 0 \\ !!!!
+  ? idealval(nf, nfgaloisapply(nf, aut, P[2]), P[1])
+  %8 = 1
+ @eprog\noindent The surprising failure of the equality test (\kbd{\%7}) is
+ due to the fact that although the corresponding prime ideals are equal, their
+ representations are not. (A prime ideal is specified by a uniformizer, and
+ there is no guarantee that applying automorphisms yields the same elements
+ as a direct \kbd{idealprimedec} call.)
+
+ The automorphism can also be given as a column vector, representing the
+ image of \kbd{Mod(x, nf.pol)} as an algebraic number. This last
+ representation is more efficient and should be preferred if a given
+ automorphism must be used in many such calls.
+ \bprog
+  ? nf = nfinit(x^3 - 37*x^2 + 74*x - 37);
+  ? l = nfgaloisconj(nf); aut = l[2] \\ @com automorphisms in basistoalg form
+  %2 = -31/11*x^2 + 1109/11*x - 925/11
+  ? L = matalgtobasis(nf, l); AUT = L[2] \\ @com same in algtobasis form
+  %3 = [16, -6, 5]~
+  ? v = [1, 2, 3]~; nfgaloisapply(nf, aut, v) == nfgaloisapply(nf, AUT, v)
+  %4 = 1 \\ @com same result...
+  ? for (i=1,10^5, nfgaloisapply(nf, aut, v))
+  time = 1,451 ms.
+  ? for (i=1,10^5, nfgaloisapply(nf, AUT, v))
+  time = 1,045 ms.  \\ @com but the latter is faster
+ @eprog
diff --git a/src/functions/number_fields/nfgaloisconj b/src/functions/number_fields/nfgaloisconj
new file mode 100644
index 0000000..2bf4d66
--- /dev/null
+++ b/src/functions/number_fields/nfgaloisconj
@@ -0,0 +1,54 @@
+Function: nfgaloisconj
+Section: number_fields
+C-Name: galoisconj0
+Prototype: GD0,L,DGp
+Help: nfgaloisconj(nf,{flag=0},{d}): list of conjugates of a root of the
+ polynomial x=nf.pol in the same number field. flag is optional (set to 0 by
+ default), meaning 0: use combination of flag 4 and 1, always complete; 1:
+ use nfroots; 2 : use complex numbers, LLL on integral basis (not always
+ complete); 4: use Allombert's algorithm, complete if the field is Galois of
+ degree <= 35 (see manual for details). nf can be simply a polynomial.
+Doc: $\var{nf}$ being a number field as output by \kbd{nfinit}, computes the
+ conjugates of a root $r$ of the non-constant polynomial $x=\var{nf}[1]$
+ expressed as polynomials in $r$. This also makes sense when the number field
+ is not \idx{Galois} since some conjugates may lie in the field.
+ $\var{nf}$ can simply be a polynomial.
+
+ If no flags or $\fl=0$, use a combination of flag $4$ and $1$ and the result
+ is always complete. There is no point whatsoever in using the other flags.
+
+ If $\fl=1$, use \kbd{nfroots}: a little slow, but guaranteed to work in
+ polynomial time.
+
+ If $\fl=2$ (OBSOLETE), use complex approximations to the roots and an integral
+ \idx{LLL}. The result is not guaranteed to be complete: some
+ conjugates may be missing (a warning is issued if the result is not proved
+ complete), especially so if the corresponding polynomial has a huge index,
+ and increasing the default precision may help. This variant is slow and
+ unreliable: don't use it.
+
+ If $\fl=4$, use \kbd{galoisinit}: very fast, but only applies to (most) Galois
+ fields. If the field is Galois with weakly
+ super-solvable Galois group (see \tet{galoisinit}), return the complete list
+ of automorphisms, else only the identity element. If present, $d$ is assumed to
+ be a multiple of the least common denominator of the conjugates expressed as
+ polynomial in a root of \var{pol}.
+
+ This routine can only compute $\Q$-automorphisms, but it may be used to get
+ $K$-automorphism for any base field $K$ as follows:
+ \bprog
+ rnfgaloisconj(nfK, R) = \\ K-automorphisms of L = K[X] / (R)
+ { my(polabs, N);
+   R *= Mod(1, nfK.pol);             \\ convert coeffs to polmod elts of K
+   polabs = rnfequation(nfK, R);
+   N = nfgaloisconj(polabs) % R;     \\ Q-automorphisms of L
+   \\ select the ones that fix K
+   select(s->subst(R, variable(R), Mod(s,R)) == 0, N);
+ }
+ K  = nfinit(y^2 + 7);
+ rnfgaloisconj(K, x^4 - y*x^3 - 3*x^2 + y*x + 1)  \\ K-automorphisms of L
+ @eprog
+
+Variant: Use directly
+ \fun{GEN}{galoisconj}{GEN nf, GEN d}, corresponding to $\fl = 0$, the others
+ only have historical interest.
diff --git a/src/functions/number_fields/nfhilbert b/src/functions/number_fields/nfhilbert
new file mode 100644
index 0000000..f8b599b
--- /dev/null
+++ b/src/functions/number_fields/nfhilbert
@@ -0,0 +1,17 @@
+Function: nfhilbert
+Section: number_fields
+C-Name: nfhilbert0
+Prototype: lGGGDG
+Help: nfhilbert(nf,a,b,{pr}): if pr is omitted, global Hilbert symbol (a,b) in
+ nf, that is 1 if X^2-aY^2-bZ^2 has a non-trivial solution (X,Y,Z) in nf, -1
+ otherwise. Otherwise compute the local symbol modulo the prime ideal pr.
+Doc: if \var{pr} is omitted,
+ compute the global quadratic \idx{Hilbert symbol} $(a,b)$ in $\var{nf}$, that
+ is $1$ if $x^2 - a y^2 - b z^2$ has a non trivial solution $(x,y,z)$ in
+ $\var{nf}$, and $-1$ otherwise. Otherwise compute the local symbol modulo
+ the prime ideal \var{pr}, as output by \kbd{idealprimedec}.
+Variant:
+ Also available is \fun{long}{nfhilbert}{GEN bnf,GEN a,GEN b} (global
+ quadratic Hilbert symbol).
+
+
diff --git a/src/functions/number_fields/nfhnf b/src/functions/number_fields/nfhnf
new file mode 100644
index 0000000..b92a451
--- /dev/null
+++ b/src/functions/number_fields/nfhnf
@@ -0,0 +1,13 @@
+Function: nfhnf
+Section: number_fields
+C-Name: nfhnf
+Prototype: GG
+Help: nfhnf(nf,x): if x=[A,I], gives a pseudo-basis of the module sum A_jI_j
+Doc: given a pseudo-matrix $(A,I)$, finds a
+ pseudo-basis in \idx{Hermite normal form} of the module it generates.
+Variant: Also available:
+
+ \fun{GEN}{rnfsimplifybasis}{GEN bnf, GEN x} simplifies the pseudo-basis
+ given by $x = (A,I)$. The ideals in the list $I$ are integral, primitive and
+ either trivial (equal to the full ring of integer) or non-principal.
+
diff --git a/src/functions/number_fields/nfhnfmod b/src/functions/number_fields/nfhnfmod
new file mode 100644
index 0000000..b190185
--- /dev/null
+++ b/src/functions/number_fields/nfhnfmod
@@ -0,0 +1,11 @@
+Function: nfhnfmod
+Section: number_fields
+C-Name: nfhnfmod
+Prototype: GGG
+Help: nfhnfmod(nf,x,detx): if x=[A,I], and detx is a multiple of the ideal
+ determinant of x, gives a pseudo-basis of the module sum A_jI_j.
+Doc: given a pseudo-matrix $(A,I)$
+ and an ideal \var{detx} which is contained in (read integral multiple of) the
+ determinant of $(A,I)$, finds a pseudo-basis in \idx{Hermite normal form}
+ of the module generated by $(A,I)$. This avoids coefficient explosion.
+ \var{detx} can be computed using the function \kbd{nfdetint}.
diff --git a/src/functions/number_fields/nfinit b/src/functions/number_fields/nfinit
new file mode 100644
index 0000000..b510c7a
--- /dev/null
+++ b/src/functions/number_fields/nfinit
@@ -0,0 +1,175 @@
+Function: nfinit
+Section: number_fields
+C-Name: nfinit0
+Prototype: GD0,L,p
+Help: nfinit(pol,{flag=0}): pol being a nonconstant irreducible polynomial,
+ gives the vector: [pol,[r1,r2],discf,index,[M,MC,T2,T,different] (see
+ manual),r1+r2 first roots, integral basis, matrix of power basis in terms of
+ integral basis, multiplication table of basis]. flag is optional and can be
+ set to 0: default; 1: do not compute different; 2: first use polred to find
+ a simpler polynomial; 3: outputs a two-element vector [nf,Mod(a,P)], where
+ nf is as in 2 and Mod(a,P) is a polmod equal to Mod(x,pol) and P=nf.pol.
+Description:
+ (gen, ?0):nf:prec       nfinit0($1, 0, prec)
+ (gen, 1):nf:prec        nfinit0($1, 1, prec)
+ (gen, 2):nf:prec        nfinit0($1, 2, prec)
+ (gen, 3):gen:prec       nfinit0($1, 3, prec)
+ (gen, 4):nf:prec        nfinit0($1, 4, prec)
+ (gen, 5):gen:prec       nfinit0($1, 5, prec)
+ (gen, #small):void      $"incorrect flag in nfinit"
+ (gen, small):gen:prec   nfinit0($1, $2, prec)
+Doc: \var{pol} being a non-constant,
+ preferably monic, irreducible polynomial in $\Z[X]$, initializes a
+ \emph{number field} structure (\kbd{nf}) associated to the field $K$ defined
+ by \var{pol}. As such, it's a technical object passed as the first argument
+ to most \kbd{nf}\var{xxx} functions, but it contains some information which
+ may be directly useful. Access to this information via \emph{member
+ functions} is preferred since the specific data organization specified below
+ may change in the future. Currently, \kbd{nf} is a row vector with 9
+ components:
+
+ $\var{nf}[1]$ contains the polynomial \var{pol} (\kbd{\var{nf}.pol}).
+
+ $\var{nf}[2]$ contains $[r1,r2]$ (\kbd{\var{nf}.sign}, \kbd{\var{nf}.r1},
+ \kbd{\var{nf}.r2}), the number of real and complex places of $K$.
+
+ $\var{nf}[3]$ contains the discriminant $d(K)$ (\kbd{\var{nf}.disc}) of $K$.
+
+ $\var{nf}[4]$ contains the index of $\var{nf}[1]$ (\kbd{\var{nf}.index}),
+ i.e.~$[\Z_K : \Z[\theta]]$, where $\theta$ is any root of $\var{nf}[1]$.
+
+ $\var{nf}[5]$ is a vector containing 7 matrices $M$, $G$, \var{roundG}, $T$,
+ $MD$, $TI$, $MDI$ useful for certain computations in the number field $K$.
+
+ \quad\item $M$ is the $(r1+r2)\times n$ matrix whose columns represent
+ the numerical values of the conjugates of the elements of the integral
+ basis.
+
+ \quad\item $G$ is an $n\times n$ matrix such that $T2 = {}^t G G$,
+ where $T2$ is the quadratic form $T_2(x) = \sum |\sigma(x)|^2$, $\sigma$
+ running over the embeddings of $K$ into $\C$.
+
+ \quad\item \var{roundG} is a rescaled copy of $G$, rounded to nearest
+ integers.
+
+ \quad\item $T$ is the $n\times n$ matrix whose coefficients are
+ $\text{Tr}(\omega_i\omega_j)$ where the $\omega_i$ are the elements of the
+ integral basis. Note also that $\det(T)$ is equal to the discriminant of the
+ field $K$. Also, when understood as an ideal, the matrix $T^{-1}$
+ generates the codifferent ideal.
+
+ \quad\item The columns of $MD$ (\kbd{\var{nf}.diff}) express a $\Z$-basis
+ of the different of $K$ on the integral basis.
+
+ \quad\item $TI$ is equal to the primitive part of $T^{-1}$, which has integral
+ coefficients.
+
+ \quad\item Finally, $MDI$ is a two-element representation (for faster
+ ideal product) of $d(K)$ times the codifferent ideal
+ (\kbd{\var{nf}.disc$*$\var{nf}.codiff}, which is an integral ideal). $MDI$
+ is only used in \tet{idealinv}.
+
+ $\var{nf}[6]$ is the vector containing the $r1+r2$ roots
+ (\kbd{\var{nf}.roots}) of $\var{nf}[1]$ corresponding to the $r1+r2$
+ embeddings of the number field into $\C$ (the first $r1$ components are real,
+ the next $r2$ have positive imaginary part).
+
+ $\var{nf}[7]$ is an integral basis for $\Z_K$ (\kbd{\var{nf}.zk}) expressed
+ on the powers of~$\theta$. Its first element is guaranteed to be $1$. This
+ basis is LLL-reduced with respect to $T_2$ (strictly speaking, it is a
+ permutation of such a basis, due to the condition that the first element be
+ $1$).
+
+ $\var{nf}[8]$ is the $n\times n$ integral matrix expressing the power
+ basis in terms of the integral basis, and finally
+
+ $\var{nf}[9]$ is the $n\times n^2$ matrix giving the multiplication table
+ of the integral basis.
+
+ If a non monic polynomial is input, \kbd{nfinit} will transform it into a
+ monic one, then reduce it (see $\fl=3$). It is allowed, though not very
+ useful given the existence of \tet{nfnewprec}, to input a \kbd{nf} or a
+ \kbd{bnf} instead of a polynomial.
+
+ \bprog
+ ? nf = nfinit(x^3 - 12); \\ initialize number field Q[X] / (X^3 - 12)
+ ? nf.pol   \\ defining polynomial
+ %2 = x^3 - 12
+ ? nf.disc  \\ field discriminant
+ %3 = -972
+ ? nf.index \\ index of power basis order in maximal order
+ %4 = 2
+ ? nf.zk    \\ integer basis, lifted to Q[X]
+ %5 = [1, x, 1/2*x^2]
+ ? nf.sign  \\ signature
+ %6 = [1, 1]
+ ? factor(abs(nf.disc ))  \\ determines ramified primes
+ %7 =
+ [2 2]
+
+ [3 5]
+ ? idealfactor(nf, 2)
+ %8 =
+ [[2, [0, 0, -1]~, 3, 1, [0, 1, 0]~] 3]  \\ @com $\goth{p}_2^3$
+ @eprog
+
+ \misctitle{Huge discriminants, helping nfdisc}
+
+ In case \var{pol} has a huge discriminant which is difficult to factor,
+ it is hard to compute from scratch the maximal order. The special input
+ format $[\var{pol}, B]$ is also accepted where \var{pol} is a polynomial as
+ above and $B$ has one of the following forms
+
+ \item an integer basis, as would be computed by \tet{nfbasis}: a vector of
+ polynomials with first element $1$. This is useful if the maximal order is
+ known in advance.
+
+ \item an argument \kbd{listP} which specifies a list of primes (see
+ \tet{nfbasis}). Instead of the maximal order, \kbd{nfinit} then computes an
+ order which is maximal at these particular primes as well as the primes
+ contained in the private prime table (see \tet{addprimes}). The result is
+ unconditionaly correct when the discriminant \kbd{nf.disc} factors
+ completely over this set of primes. The function \tet{nfcertify} automates
+ this:
+ \bprog
+ ? pol = polcompositum(x^5 - 101, polcyclo(7))[1];
+ ? nf = nfinit( [pol, 10^3] );
+ ? nfcertify(nf)
+ %3 = []
+ @eprog\noindent A priori, \kbd{nf.zk} defines an order which is only known
+ to be maximal at all primes $\leq 10^3$ (no prime $\leq 10^3$ divides
+ \kbd{nf.index}). The certification step proves the correctness of the
+ computation.
+ \medskip
+
+ If $\fl=2$: \var{pol} is changed into another polynomial $P$ defining the same
+ number field, which is as simple as can easily be found using the
+ \tet{polredbest} algorithm, and all the subsequent computations are done
+ using this new polynomial. In particular, the first component of the result
+ is the modified polynomial.
+
+ If $\fl=3$, apply \kbd{polredbest} as in case 2, but outputs
+ $[\var{nf},\kbd{Mod}(a,P)]$, where $\var{nf}$ is as before and
+ $\kbd{Mod}(a,P)=\kbd{Mod}(x,\var{pol})$ gives the change of
+ variables. This is implicit when \var{pol} is not monic: first a linear change
+ of variables is performed, to get a monic polynomial, then \kbd{polredbest}.
+Variant: Also available are
+ \fun{GEN}{nfinit}{GEN x, long prec} ($\fl = 0$),
+ \fun{GEN}{nfinitred}{GEN x, long prec} ($\fl = 2$),
+ \fun{GEN}{nfinitred2}{GEN x, long prec} ($\fl = 3$).
+ Instead of the above hardcoded numerical flags in \kbd{nfinit0}, one should
+ rather use
+
+ \fun{GEN}{nfinitall}{GEN x, long flag, long prec}, where \fl\ is an
+ or-ed combination of
+
+ \item \tet{nf_RED}: find a simpler defining polynomial,
+
+ \item \tet{nf_ORIG}: if \tet{nf_RED} set, also return the change of variable,
+
+ \item \tet{nf_ROUND2}: \emph{Deprecated}. Slow down the routine by using an
+ obsolete normalization algorithm (do not use this one!),
+
+ \item \tet{nf_PARTIALFACT}: \emph{Deprecated}. Lazy factorization of the
+ polynomial discriminant. Result is conditional unless \kbd{nfcertify}
+ can certify it.
diff --git a/src/functions/number_fields/nfisideal b/src/functions/number_fields/nfisideal
new file mode 100644
index 0000000..8601fad
--- /dev/null
+++ b/src/functions/number_fields/nfisideal
@@ -0,0 +1,7 @@
+Function: nfisideal
+Section: number_fields
+C-Name: isideal
+Prototype: lGG
+Help: nfisideal(nf,x): true(1) if x is an ideal in the number field nf,
+ false(0) if not.
+Doc: returns 1 if $x$ is an ideal in the number field $\var{nf}$, 0 otherwise.
diff --git a/src/functions/number_fields/nfisincl b/src/functions/number_fields/nfisincl
new file mode 100644
index 0000000..ae5b914
--- /dev/null
+++ b/src/functions/number_fields/nfisincl
@@ -0,0 +1,19 @@
+Function: nfisincl
+Section: number_fields
+C-Name: nfisincl
+Prototype: GG
+Help: nfisincl(x,y): tests whether the number field x is isomorphic to a
+ subfield of y (where x and y are either polynomials or number fields as
+ output by nfinit). Return 0 if not, and otherwise all the isomorphisms. If y
+ is a number field, a faster algorithm is used.
+Doc: tests whether the number field $K$ defined
+ by the polynomial $x$ is conjugate to a subfield of the field $L$ defined
+ by $y$ (where $x$ and $y$ must be in $\Q[X]$). If they are not, the output
+ is the number 0. If they are, the output is a vector of polynomials, each
+ polynomial $a$ representing an embedding of $K$ into $L$, i.e.~being such
+ that $y\mid x\circ a$.
+
+ If $y$ is a number field (\var{nf}), a much faster algorithm is used
+ (factoring $x$ over $y$ using \tet{nffactor}). Before version 2.0.14, this
+ wasn't guaranteed to return all the embeddings, hence was triggered by a
+ special flag. This is no more the case.
diff --git a/src/functions/number_fields/nfisisom b/src/functions/number_fields/nfisisom
new file mode 100644
index 0000000..56fafee
--- /dev/null
+++ b/src/functions/number_fields/nfisisom
@@ -0,0 +1,7 @@
+Function: nfisisom
+Section: number_fields
+C-Name: nfisisom
+Prototype: GG
+Help: nfisisom(x,y): as nfisincl but tests whether x is isomorphic to y.
+Doc: as \tet{nfisincl}, but tests for isomorphism. If either $x$ or $y$ is a
+ number field, a much faster algorithm will be used.
diff --git a/src/functions/number_fields/nfkermodpr b/src/functions/number_fields/nfkermodpr
new file mode 100644
index 0000000..6223c11
--- /dev/null
+++ b/src/functions/number_fields/nfkermodpr
@@ -0,0 +1,10 @@
+Function: nfkermodpr
+Section: number_fields
+C-Name: nfkermodpr
+Prototype: GGG
+Help: nfkermodpr(nf,x,pr): kernel of the matrix x in Z_K/pr, where pr is in
+ modpr format (see nfmodprinit).
+Doc: kernel of the matrix $a$ in $\Z_K/\var{pr}$, where \var{pr} is in
+ \key{modpr} format (see \kbd{nfmodprinit}).
+Variant: This function is normally useless in library mode. Project your
+ inputs to the residue field using \kbd{nfM\_to\_FqM}, then work there.
diff --git a/src/functions/number_fields/nfmodprinit b/src/functions/number_fields/nfmodprinit
new file mode 100644
index 0000000..5b57f78
--- /dev/null
+++ b/src/functions/number_fields/nfmodprinit
@@ -0,0 +1,9 @@
+Function: nfmodprinit
+Section: number_fields
+C-Name: nfmodprinit
+Prototype: GG
+Help: nfmodprinit(nf,pr): transform the 5 element row vector pr representing
+ a prime ideal into modpr format necessary for all operations mod pr in the
+ number field nf (see manual for details about the format).
+Doc: transforms the prime ideal \var{pr} into \tet{modpr} format necessary
+ for all operations modulo \var{pr} in the number field \var{nf}.
diff --git a/src/functions/number_fields/nfnewprec b/src/functions/number_fields/nfnewprec
new file mode 100644
index 0000000..1ccf2bc
--- /dev/null
+++ b/src/functions/number_fields/nfnewprec
@@ -0,0 +1,13 @@
+Function: nfnewprec
+Section: number_fields
+C-Name: nfnewprec
+Prototype: Gp
+Help: nfnewprec(nf): transform the number field data nf into new data using
+ the current (usually larger) precision.
+Doc: transforms the number field $\var{nf}$
+ into the corresponding data using current (usually larger) precision. This
+ function works as expected if $\var{nf}$ is in fact a $\var{bnf}$ (update
+ $\var{bnf}$ to current precision) but may be quite slow (many generators of
+ principal ideals have to be computed).
+Variant: See also \fun{GEN}{bnfnewprec}{GEN bnf, long prec}
+ and \fun{GEN}{bnrnewprec}{GEN bnr, long prec}.
diff --git a/src/functions/number_fields/nfroots b/src/functions/number_fields/nfroots
new file mode 100644
index 0000000..189f0d2
--- /dev/null
+++ b/src/functions/number_fields/nfroots
@@ -0,0 +1,27 @@
+Function: nfroots
+Section: number_fields
+C-Name: nfroots
+Prototype: DGG
+Help: nfroots({nf},x): roots of polynomial x belonging to nf (Q if
+ omitted) without multiplicity.
+Doc: roots of the polynomial $x$ in the
+ number field $\var{nf}$ given by \kbd{nfinit} without multiplicity (in $\Q$
+ if $\var{nf}$ is omitted). $x$ has coefficients in the number field (scalar,
+ polmod, polynomial, column vector). The main variable of $\var{nf}$ must be
+ of lower priority than that of $x$ (see \secref{se:priority}). However if the
+ coefficients of the number field occur explicitly (as polmods) as
+ coefficients of $x$, the variable of these polmods \emph{must} be the same as
+ the main variable of $t$ (see \kbd{nffactor}).
+
+ It is possible to input a defining polynomial for \var{nf}
+ instead, but this is in general less efficient since parts of an \kbd{nf}
+ structure will be computed internally. This is useful in two situations: when
+ you don't need the \kbd{nf}, or when you can't compute its discriminant due
+ to integer factorization difficulties. In the latter case, \tet{addprimes} is
+ a possibility but a dangerous one: roots will probably be missed if the
+ (true) field discriminant and an \kbd{addprimes} entry are strictly divisible
+ by some prime. If you have such an unsafe \var{nf}, it is safer to input
+ \kbd{nf.pol}.
+Variant: See also \fun{GEN}{nfrootsQ}{GEN x},
+ corresponding to $\kbd{nf} = \kbd{NULL}$.
+
diff --git a/src/functions/number_fields/nfrootsof1 b/src/functions/number_fields/nfrootsof1
new file mode 100644
index 0000000..9837e65
--- /dev/null
+++ b/src/functions/number_fields/nfrootsof1
@@ -0,0 +1,31 @@
+Function: nfrootsof1
+Section: number_fields
+C-Name: rootsof1
+Prototype: G
+Help: nfrootsof1(nf): number of roots of unity and primitive root of unity
+ in the number field nf.
+Doc: Returns a two-component vector $[w,z]$ where $w$ is the number of roots of
+ unity in the number field \var{nf}, and $z$ is a primitive $w$-th root
+ of unity.
+ \bprog
+ ? K = nfinit(polcyclo(11));
+ ? nfrootsof1(K)
+ %2 = [22, [0, 0, 0, 0, 0, -1, 0, 0, 0, 0]~]
+ ? z = nfbasistoalg(K, %[2])   \\ in algebraic form
+ %3 = Mod(-x^5, x^10 + x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)
+ ? [lift(z^11), lift(z^2)]     \\ proves that the order of z is 22
+ %4 = [-1, -x^9 - x^8 - x^7 - x^6 - x^5 - x^4 - x^3 - x^2 - x - 1]
+ @eprog
+ This function guesses the number $w$ as the gcd of the $\#k(v)^*$ for
+ unramified $v$ above odd primes, then computes the roots in \var{nf}
+ of the $w$-th cyclotomic polynomial: the algorithm is polynomial time with
+ respect to the field degree and the bitsize of the multiplication table in
+ \var{nf} (both of them polynomially bounded in terms of the size of the
+ discriminant). Fields of degree up to $100$ or so should require less than
+ one minute.
+Variant: Also available is \fun{GEN}{rootsof1_kannan}{GEN nf}, that computes
+ all algebraic integers of $T_2$ norm equal to the field degree
+ (all roots of $1$, by Kronecker's theorem). This is in general a little
+ faster than the default when there \emph{are} roots of $1$ in the field
+ (say twice faster), but can be much slower (say, \emph{days} slower), since
+ the algorithm is a priori exponential in the field degree.
diff --git a/src/functions/number_fields/nfsnf b/src/functions/number_fields/nfsnf
new file mode 100644
index 0000000..46dfc6f
--- /dev/null
+++ b/src/functions/number_fields/nfsnf
@@ -0,0 +1,23 @@
+Function: nfsnf
+Section: number_fields
+C-Name: nfsnf
+Prototype: GG
+Help: nfsnf(nf,x): if x=[A,I,J], outputs [c_1,...c_n] Smith normal form of x.
+Doc: given a $\Z_K$-module $x$ associated to the integral pseudo-matrix
+ $(A,I,J)$, returns an ideal list $d_1,\dots,d_n$ which is the \idx{Smith
+ normal form} of $x$. In other words, $x$ is isomorphic to
+ $\Z_K/d_1\oplus\cdots\oplus\Z_K/d_n$ and $d_i$ divides $d_{i-1}$ for $i\ge2$.
+
+ See \secref{se:ZKmodules} for the definition of integral pseudo-matrix;
+ briefly, it is input as a 3-component row vector $[A,I,J]$ where
+ $I = [b_1,\dots,b_n]$ and $J = [a_1,\dots,a_n]$ are two ideal lists,
+ and $A$ is a square $n\times n$ matrix with columns $(A_1,\dots,A_n)$,
+ seen as elements in $K^n$ (with canonical basis $(e_1,\dots,e_n)$).
+ This data defines the $\Z_K$ module $x$ given by
+ $$ (b_1e_1\oplus\cdots\oplus b_ne_n) / (a_1A_1\oplus\cdots\oplus a_nA_n)
+ \enspace, $$
+ The integrality condition is $a_{i,j} \in b_i a_j^{-1}$ for all $i,j$. If it
+ is not satisfied, then the $d_i$ will not be integral. Note that every
+ finitely generated torsion module is isomorphic to a module of this form and
+ even with $b_i=Z_K$ for all $i$.
+
diff --git a/src/functions/number_fields/nfsolvemodpr b/src/functions/number_fields/nfsolvemodpr
new file mode 100644
index 0000000..36c270e
--- /dev/null
+++ b/src/functions/number_fields/nfsolvemodpr
@@ -0,0 +1,21 @@
+Function: nfsolvemodpr
+Section: number_fields
+C-Name: nfsolvemodpr
+Prototype: GGGG
+Help: nfsolvemodpr(nf,a,b,P): solution of a*x=b in Z_K/P, where a is a
+ matrix and b a column vector, and where P is in modpr format (see
+ nfmodprinit).
+Doc: let $P$ be a prime ideal in \key{modpr} format (see \kbd{nfmodprinit}),
+ let $a$ be a matrix, invertible over the residue field, and let $b$ be
+ a column vector or matrix. This function returns a solution of $a\cdot x =
+ b$; the coefficients of $x$ are lifted to \var{nf} elements.
+ \bprog
+ ? K = nfinit(y^2+1);
+ ? P = idealprimedec(K, 3)[1];
+ ? P = nfmodprinit(K, P);
+ ? a = [y+1, y; y, 0]; b = [1, y]~
+ ? nfsolvemodpr(K, a,b, P)
+ %5 = [1, 2]~
+ @eprog
+Variant: This function is normally useless in library mode. Project your
+ inputs to the residue field using \kbd{nfM\_to\_FqM}, then work there.
diff --git a/src/functions/number_fields/nfsubfields b/src/functions/number_fields/nfsubfields
new file mode 100644
index 0000000..5b2b6bf
--- /dev/null
+++ b/src/functions/number_fields/nfsubfields
@@ -0,0 +1,17 @@
+Function: nfsubfields
+Section: number_fields
+C-Name: nfsubfields
+Prototype: GD0,L,
+Help: nfsubfields(pol,{d=0}): find all subfields of degree d of number field
+ defined by pol (all subfields if d is null or omitted). Result is a vector of
+ subfields, each being given by [g,h], where g is an absolute equation and h
+ expresses one of the roots of g in terms of the root x of the polynomial
+ defining nf.
+Doc: finds all subfields of degree
+ $d$ of the number field defined by the (monic, integral) polynomial
+ \var{pol} (all subfields if $d$ is null or omitted). The result is a vector
+ of subfields, each being given by $[g,h]$, where $g$ is an absolute equation
+ and $h$ expresses one of the roots of $g$ in terms of the root $x$ of the
+ polynomial defining $\var{nf}$. This routine uses J.~Kl\"uners's algorithm
+ in the general case, and B.~Allombert's \tet{galoissubfields} when \var{nf}
+ is Galois (with weakly supersolvable Galois group).\sidx{Galois}\sidx{subfield}
diff --git a/src/functions/number_fields/polcompositum b/src/functions/number_fields/polcompositum
new file mode 100644
index 0000000..e239d63
--- /dev/null
+++ b/src/functions/number_fields/polcompositum
@@ -0,0 +1,69 @@
+Function: polcompositum
+Section: number_fields
+C-Name: polcompositum0
+Prototype: GGD0,L,
+Help: polcompositum(P,Q,{flag=0}): vector of all possible compositums
+ of the number fields defined by the polynomials P and Q. If (optional)
+ flag is set (i.e non-null), output for each compositum, not only the
+ compositum polynomial pol, but a vector [R,a,b,k] where a (resp. b) is a root
+ of P (resp. Q) expressed as a polynomial modulo R,
+ and a small integer k such that al2+k*al1 is the chosen root of R.
+Doc: \sidx{compositum} $P$ and $Q$
+ being squarefree polynomials in $\Z[X]$ in the same variable, outputs
+ the simple factors of the \'etale $\Q$-algebra $A = \Q(X, Y) / (P(X), Q(Y))$.
+ The factors are given by a list of polynomials $R$ in $\Z[X]$, associated to
+ the number field $\Q(X)/ (R)$, and sorted by increasing degree (with respect
+ to lexicographic ordering for factors of equal degrees). Returns an error if
+ one of the polynomials is not squarefree.
+
+ Note that it is more efficient to reduce to the case where $P$ and $Q$ are
+ irreducible first. The routine will not perform this for you, since it may be
+ expensive, and the inputs are irreducible in most applications anyway. In
+ this case, there will be a single factor $R$ if and only if the number
+ fields defined by $P$ and $Q$ are disjoint.
+
+ Assuming $P$ is irreducible (of smaller degree than $Q$ for efficiency), it
+ is in general much faster to proceed as follows
+ \bprog
+ nf = nfinit(P); L = nffactor(nf, Q)[,1];
+ vector(#L, i, rnfequation(nf, L[i]))
+ @eprog\noindent
+ to obtain the same result. If you are only interested in the degrees of the
+ simple factors, the \kbd{rnfequation} instruction can be replaced by a
+ trivial \kbd{poldegree(P) * poldegree(L[i])}.
+
+ If $\fl=1$, outputs a vector of 4-component vectors $[R,a,b,k]$, where $R$
+ ranges through the list of all possible compositums as above, and $a$
+ (resp. $b$) expresses the root of $P$ (resp. $Q$) as an element of
+ $\Q(X)/(R)$. Finally, $k$ is a small integer such that $b + ka = X$ modulo
+ $R$.
+
+ A compositum is often defined by a complicated polynomial, which it is
+ advisable to reduce before further work. Here is an example involving
+ the field $\Q(\zeta_5, 5^{1/5})$:
+ \bprog
+ ? L = polcompositum(x^5 - 5, polcyclo(5), 1); \\@com list of $[R,a,b,k]$
+ ? [R, a] = L[1];  \\@com pick the single factor, extract $R,a$ (ignore $b,k$)
+ ? R               \\@com defines the compositum
+ %3 = x^20 + 5*x^19 + 15*x^18 + 35*x^17 + 70*x^16 + 141*x^15 + 260*x^14\
+ + 355*x^13 + 95*x^12 - 1460*x^11 - 3279*x^10 - 3660*x^9 - 2005*x^8    \
+ + 705*x^7 + 9210*x^6 + 13506*x^5 + 7145*x^4 - 2740*x^3 + 1040*x^2     \
+ - 320*x + 256
+ ? a^5 - 5         \\@com a fifth root of $5$
+ %4 = 0
+ ? [T, X] = polredbest(R, 1);
+ ? T     \\@com simpler defining polynomial for $\Q[x]/(R)$
+ %6 = x^20 + 25*x^10 + 5
+ ? X     \\ @com root of $R$ in $\Q[y]/(T(y))$
+ %7 = Mod(-1/11*x^15 - 1/11*x^14 + 1/22*x^10 - 47/22*x^5 - 29/11*x^4 + 7/22,\
+ x^20 + 25*x^10 + 5)
+ ? a = subst(a.pol, 'x, X)  \\@com \kbd{a} in the new coordinates
+ %8 = Mod(1/11*x^14 + 29/11*x^4, x^20 + 25*x^10 + 5)
+ ? a^5 - 5
+ %9 = 0
+ @eprog
+Variant: Also available are
+ \fun{GEN}{compositum}{GEN P, GEN Q} ($\fl = 0$) and
+ \fun{GEN}{compositum2}{GEN P, GEN Q} ($\fl = 1$).
+
+
diff --git a/src/functions/number_fields/polgalois b/src/functions/number_fields/polgalois
new file mode 100644
index 0000000..933d082
--- /dev/null
+++ b/src/functions/number_fields/polgalois
@@ -0,0 +1,88 @@
+Function: polgalois
+Section: number_fields
+C-Name: polgalois
+Prototype: Gp
+Help: polgalois(T): Galois group of the polynomial T (see manual for group
+ coding). Return [n, s, k, name] where n is the group order, s the signature,
+ k the index and name is the GAP4 name of the transitive group.
+Doc: \idx{Galois} group of the non-constant
+ polynomial $T\in\Q[X]$. In the present version \vers, $T$ must be irreducible
+ and the degree $d$ of $T$ must be less than or equal to 7. If the
+ \tet{galdata} package has been installed, degrees 8, 9, 10 and 11 are also
+ implemented. By definition, if $K = \Q[x]/(T)$, this computes the action of
+ the Galois group of the Galois closure of $K$ on the $d$ distinct roots of
+ $T$, up to conjugacy (corresponding to different root orderings).
+
+ The output is a 4-component vector $[n,s,k,name]$ with the
+ following meaning: $n$ is the cardinality of the group, $s$ is its signature
+ ($s=1$ if the group is a subgroup of the alternating group $A_d$, $s=-1$
+ otherwise) and name is a character string containing name of the transitive
+ group according to the GAP 4 transitive groups library by Alexander Hulpke.
+
+ $k$ is more arbitrary and the choice made up to version~2.2.3 of PARI is rather
+ unfortunate: for $d > 7$, $k$ is the numbering of the group among all
+ transitive subgroups of $S_d$, as given in ``The transitive groups of degree up
+ to eleven'', G.~Butler and J.~McKay, \emph{Communications in Algebra}, vol.~11,
+ 1983,
+ pp.~863--911 (group $k$ is denoted $T_k$ there). And for $d \leq 7$, it was ad
+ hoc, so as to ensure that a given triple would denote a unique group.
+ Specifically, for polynomials of degree $d\leq 7$, the groups are coded as
+ follows, using standard notations
+ \smallskip
+ In degree 1: $S_1=[1,1,1]$.
+ \smallskip
+ In degree 2: $S_2=[2,-1,1]$.
+ \smallskip
+ In degree 3: $A_3=C_3=[3,1,1]$, $S_3=[6,-1,1]$.
+ \smallskip
+ In degree 4: $C_4=[4,-1,1]$, $V_4=[4,1,1]$, $D_4=[8,-1,1]$, $A_4=[12,1,1]$,
+ $S_4=[24,-1,1]$.
+ \smallskip
+ In degree 5: $C_5=[5,1,1]$, $D_5=[10,1,1]$, $M_{20}=[20,-1,1]$,
+ $A_5=[60,1,1]$, $S_5=[120,-1,1]$.
+ \smallskip
+ In degree 6: $C_6=[6,-1,1]$, $S_3=[6,-1,2]$, $D_6=[12,-1,1]$, $A_4=[12,1,1]$,
+ $G_{18}=[18,-1,1]$, $S_4^-=[24,-1,1]$, $A_4\times C_2=[24,-1,2]$,
+ $S_4^+=[24,1,1]$, $G_{36}^-=[36,-1,1]$, $G_{36}^+=[36,1,1]$,
+ $S_4\times C_2=[48,-1,1]$, $A_5=PSL_2(5)=[60,1,1]$, $G_{72}=[72,-1,1]$,
+ $S_5=PGL_2(5)=[120,-1,1]$, $A_6=[360,1,1]$, $S_6=[720,-1,1]$.
+ \smallskip
+ In degree 7: $C_7=[7,1,1]$, $D_7=[14,-1,1]$, $M_{21}=[21,1,1]$,
+ $M_{42}=[42,-1,1]$, $PSL_2(7)=PSL_3(2)=[168,1,1]$, $A_7=[2520,1,1]$,
+ $S_7=[5040,-1,1]$.
+ \smallskip
+ This is deprecated and obsolete, but for reasons of backward compatibility,
+ we cannot change this behavior yet. So you can use the default
+ \tet{new_galois_format} to switch to a consistent naming scheme, namely $k$ is
+ always the standard numbering of the group among all transitive subgroups of
+ $S_n$. If this default is in effect, the above groups will be coded as:
+ \smallskip
+ In degree 1: $S_1=[1,1,1]$.
+ \smallskip
+ In degree 2: $S_2=[2,-1,1]$.
+ \smallskip
+ In degree 3: $A_3=C_3=[3,1,1]$, $S_3=[6,-1,2]$.
+ \smallskip
+ In degree 4: $C_4=[4,-1,1]$, $V_4=[4,1,2]$, $D_4=[8,-1,3]$, $A_4=[12,1,4]$,
+ $S_4=[24,-1,5]$.
+ \smallskip
+ In degree 5: $C_5=[5,1,1]$, $D_5=[10,1,2]$, $M_{20}=[20,-1,3]$,
+ $A_5=[60,1,4]$, $S_5=[120,-1,5]$.
+ \smallskip
+ In degree 6: $C_6=[6,-1,1]$, $S_3=[6,-1,2]$, $D_6=[12,-1,3]$, $A_4=[12,1,4]$,
+ $G_{18}=[18,-1,5]$, $A_4\times C_2=[24,-1,6]$, $S_4^+=[24,1,7]$,
+ $S_4^-=[24,-1,8]$, $G_{36}^-=[36,-1,9]$, $G_{36}^+=[36,1,10]$,
+ $S_4\times C_2=[48,-1,11]$, $A_5=PSL_2(5)=[60,1,12]$, $G_{72}=[72,-1,13]$,
+ $S_5=PGL_2(5)=[120,-1,14]$, $A_6=[360,1,15]$, $S_6=[720,-1,16]$.
+ \smallskip
+ In degree 7: $C_7=[7,1,1]$, $D_7=[14,-1,2]$, $M_{21}=[21,1,3]$,
+ $M_{42}=[42,-1,4]$, $PSL_2(7)=PSL_3(2)=[168,1,5]$, $A_7=[2520,1,6]$,
+ $S_7=[5040,-1,7]$.
+ \smallskip
+
+ \misctitle{Warning} The method used is that of resolvent polynomials and is
+ sensitive to the current precision. The precision is updated internally but,
+ in very rare cases, a wrong result may be returned if the initial precision
+ was not sufficient.
+Variant: To enable the new format in library mode,
+ set the global variable \tet{new_galois_format} to $1$.
diff --git a/src/functions/number_fields/polred b/src/functions/number_fields/polred
new file mode 100644
index 0000000..9f20ba8
--- /dev/null
+++ b/src/functions/number_fields/polred
@@ -0,0 +1,46 @@
+Function: polred
+Section: number_fields
+C-Name: polred0
+Prototype: GD0,L,DG
+Help: polred(T,{flag=0}): Deprecated, use polredbest. Reduction of the
+ polynomial T (gives minimal polynomials only). The following binary digits of
+ (optional) flag are significant 1: partial reduction, 2: gives also elements.
+Doc: This function is \emph{deprecated}, use \tet{polredbest} instead.
+ Finds polynomials with reasonably small coefficients defining subfields of
+ the number field defined by $T$. One of the polynomials always defines $\Q$
+ (hence is equal to $x-1$), and another always defines the same number field
+ as $T$ if $T$ is irreducible.
+
+ All $T$ accepted by \tet{nfinit} are also allowed here;
+ in particular, the format \kbd{[T, listP]} is recommended, e.g. with
+ $\kbd{listP} = 10^5$ or a vector containing all ramified primes. Otherwise,
+ the maximal order of $\Q[x]/(T)$ must be computed.
+
+ The following binary digits of $\fl$ are significant:
+
+ 1: Possibly use a suborder of the maximal order. The
+ primes dividing the index of the order chosen are larger than
+ \tet{primelimit} or divide integers stored in the \tet{addprimes} table.
+ This flag is \emph{deprecated}, the \kbd{[T, listP]} format is more
+ flexible.
+
+ 2: gives also elements. The result is a two-column matrix, the first column
+ giving primitive elements defining these subfields, the second giving the
+ corresponding minimal polynomials.
+ \bprog
+ ? M = polred(x^4 + 8, 2)
+ %1 =
+ [1 x - 1]
+
+ [1/2*x^2 x^2 + 2]
+
+ [1/4*x^3 x^4 + 2]
+
+ [x x^4 + 8]
+ ? minpoly(Mod(M[2,1], x^4+8))
+ %2 = x^2 + 2
+ @eprog
+
+ \synt{polred}{GEN T} ($\fl = 0$). Also available is
+ \fun{GEN}{polred2}{GEN T} ($\fl = 2$). The function \kbd{polred0} is
+ deprecated, provided for backward compatibility.
diff --git a/src/functions/number_fields/polredabs b/src/functions/number_fields/polredabs
new file mode 100644
index 0000000..9fae0be
--- /dev/null
+++ b/src/functions/number_fields/polredabs
@@ -0,0 +1,81 @@
+Function: polredabs
+Section: number_fields
+C-Name: polredabs0
+Prototype: GD0,L,
+Help: polredabs(T,{flag=0}): a smallest generating polynomial of the number
+ field for the T2 norm on the roots, with smallest index for the minimal T2
+ norm. flag is optional, whose binary digit mean 1: give the element whose
+ characteristic polynomial is the given polynomial. 4: give all polynomials
+ of minimal T2 norm (give only one of P(x) and P(-x)).
+Doc: returns a canonical defining polynomial $P$ for the number field
+ $\Q[X]/(T)$ defined by $T$, such that the sum of the squares of the modulus
+ of the roots (i.e.~the $T_2$-norm) is minimal. Different $T$ defining
+ isomorphic number fields will yield the same $P$. All $T$ accepted by
+ \tet{nfinit} are also allowed here, e.g. non-monic polynomials, or pairs
+ \kbd{[T, listP]} specifying that a non-maximal order may be used.
+
+ \misctitle{Warning 1} Using a \typ{POL} $T$ requires fully factoring the
+ discriminant of $T$, which may be very hard. The format \kbd{[T, listP]}
+ computes only a suborder of the maximal order and replaces this part of the
+ algorithm by a polynomial time computation. In that case the polynomial $P$
+ is a priori no longer canonical, and it may happen that it does not have
+ minimal $T_2$ norm. The routine attempts to certify the result independently
+ of this order computation (as per \tet{nfcertify}: we try to prove that the
+ order is maximal); if it fails, the routine returns $0$ instead of $P$.
+ In order to force an output in that case as well, you may either use
+ \tet{polredbest}, or \kbd{polredabs(,16)}, or
+ \bprog
+   polredabs([T, nfbasis([T, listP])])
+ @eprog\noindent (In all three cases, the result is no longer canonical.)
+
+ \misctitle{Warning 2} Apart from the factorization of the discriminant of
+ $T$, this routine runs in polynomial time for a \emph{fixed} degree.
+ But the complexity is exponential in the degree: this routine
+ may be exceedingly slow when the number field has many subfields, hence a
+ lot of elements of small $T_2$-norm. If you do not need a canonical
+ polynomial, the function \tet{polredbest} is in general much faster (it runs
+ in polynomial time), and tends to return polynomials with smaller
+ discriminants.
+
+ The binary digits of $\fl$ mean
+
+ 1: outputs a two-component row vector $[P,a]$, where $P$ is the default
+ output and \kbd{Mod(a, P)} is a root of the original $T$.
+
+ 4: gives \emph{all} polynomials of minimal $T_2$ norm; of the two polynomials
+ $P(x)$ and $\pm P(-x)$, only one is given.
+
+ 16: Possibly use a suborder of the maximal order, \emph{without} attempting to
+ certify the result as in Warning 1: we always return a polynomial and never
+ $0$. The result is a priori not canonical.
+
+ \bprog
+ ? T = x^16 - 136*x^14 + 6476*x^12 - 141912*x^10 + 1513334*x^8 \
+       - 7453176*x^6 + 13950764*x^4 - 5596840*x^2 + 46225
+ ? T1 = polredabs(T); T2 = polredbest(T);
+ ? [ norml2(polroots(T1)), norml2(polroots(T2)) ]
+ %3 = [88.0000000, 120.000000]
+ ? [ sizedigit(poldisc(T1)), sizedigit(poldisc(T2)) ]
+ %4 = [75, 67]
+ @eprog
+
+Variant: Instead of the above hardcoded numerical flags, one should use an
+ or-ed combination of
+
+ \item \tet{nf_PARTIALFACT}: possibly use a suborder of the maximal order,
+ \emph{without} attempting to certify the result.
+
+ \item \tet{nf_ORIG}: return $[P, a]$, where \kbd{Mod(a, P)} is a root of $T$.
+
+ \item \tet{nf_RAW}: return $[P, b]$, where \kbd{Mod(b, T)} is a root of $P$.
+ The algebraic integer $b$ is the raw result produced by the small vectors
+ enumeration in the maximal order; $P$ was computed as the characteristic
+ polynomial of \kbd{Mod(b, T)}. \kbd{Mod(a, P)} as in \tet{nf_ORIG}
+ is obtained with \tet{modreverse}.
+
+ \item \tet{nf_ADDZK}: if $r$ is the result produced with some of the above
+ flags (of the form $P$ or $[P,c]$), return \kbd{[r,zk]}, where \kbd{zk} is a
+ $\Z$-basis for the maximal order of $\Q[X]/(P)$.
+
+ \item \tet{nf_ALL}: return a vector of results of the above form, for all
+ polynomials of minimal $T_2$-norm.
diff --git a/src/functions/number_fields/polredbest b/src/functions/number_fields/polredbest
new file mode 100644
index 0000000..fbc0b80
--- /dev/null
+++ b/src/functions/number_fields/polredbest
@@ -0,0 +1,54 @@
+Function: polredbest
+Section: number_fields
+C-Name: polredbest
+Prototype: GD0,L,
+Help: polredbest(T,{flag=0}): reduction of the polynomial T (gives minimal
+ polynomials only). If flag=1, gives also elements.
+Doc: finds a polynomial with reasonably
+ small coefficients defining the same number field as $T$.
+ All $T$ accepted by \tet{nfinit} are also allowed here (e.g. non-monic
+ polynomials, \kbd{nf}, \kbd{bnf}, \kbd{[T,Z\_K\_basis]}). Contrary to
+ \tet{polredabs}, this routine runs in polynomial time, but it offers no
+ guarantee as to the minimality of its result.
+
+ This routine computes an LLL-reduced basis for the ring of integers of
+ $\Q[X]/(T)$, then examines small linear combinations of the basis vectors,
+ computing their characteristic polynomials. It returns the \emph{separable}
+ $P$ polynomial of smallest discriminant (the one with lexicographically
+ smallest \kbd{abs(Vec(P))} in case of ties). This is a good candidate
+ for subsequent number field computations, since it guarantees that
+ the denominators of algebraic integers, when expressed in the power basis,
+ are reasonably small. With no claim of minimality, though.
+
+ It can happen that iterating this functions yields better and better
+ polynomials, until it stabilizes:
+ \bprog
+ ? \p5
+ ? P = X^12+8*X^8-50*X^6+16*X^4-3069*X^2+625;
+ ? poldisc(P)*1.
+ %2 = 1.2622 E55
+ ? P = polredbest(P);
+ ? poldisc(P)*1.
+ %4 = 2.9012 E51
+ ? P = polredbest(P);
+ ? poldisc(P)*1.
+ %6 = 8.8704 E44
+ @eprog\noindent In this example, the initial polynomial $P$ is the one
+ returned by \tet{polredabs}, and the last one is stable.
+
+ If $\fl = 1$: outputs a two-component row vector $[P,a]$,  where $P$ is the
+ default output and \kbd{Mod(a, P)} is a root of the original $T$.
+ \bprog
+ ? [P,a] = polredbest(x^4 + 8, 1)
+ %1 = [x^4 + 2, Mod(x^3, x^4 + 2)]
+ ? charpoly(a)
+ %2 = x^4 + 8
+ @eprog\noindent In particular, the map $\Q[x]/(T) \to \Q[x]/(P)$,
+ $x\mapsto \kbd{Mod(a,P)}$ defines an isomorphism of number fields, which can
+ be computed as
+ \bprog
+   subst(lift(Q), 'x, a)
+ @eprog\noindent if $Q$ is a \typ{POLMOD} modulo $T$; \kbd{b = modreverse(a)}
+ returns a \typ{POLMOD} giving the inverse of the above map (which should be
+ useless since $\Q[x]/(P)$ is a priori a better representation for the number
+ field and its elements).
diff --git a/src/functions/number_fields/polredord b/src/functions/number_fields/polredord
new file mode 100644
index 0000000..370e63d
--- /dev/null
+++ b/src/functions/number_fields/polredord
@@ -0,0 +1,11 @@
+Function: polredord
+Section: number_fields
+C-Name: polredord
+Prototype: G
+Help: polredord(x): reduction of the polynomial x, staying in the same order.
+Doc: finds polynomials with reasonably small
+ coefficients and of the same degree as that of $x$ defining suborders of the
+ order defined by $x$. One of the polynomials always defines $\Q$ (hence
+ is equal to $(x-1)^n$, where $n$ is the degree), and another always defines
+ the same order as $x$ if $x$ is irreducible. Useless function: try
+ \kbd{polredbest}.
diff --git a/src/functions/number_fields/poltschirnhaus b/src/functions/number_fields/poltschirnhaus
new file mode 100644
index 0000000..7e67430
--- /dev/null
+++ b/src/functions/number_fields/poltschirnhaus
@@ -0,0 +1,11 @@
+Function: poltschirnhaus
+Section: number_fields
+C-Name: tschirnhaus
+Prototype: G
+Help: poltschirnhaus(x): random Tschirnhausen transformation of the
+ polynomial x.
+Doc: applies a random Tschirnhausen
+ transformation to the polynomial $x$, which is assumed to be non-constant
+ and separable, so as to obtain a new equation for the \'etale algebra
+ defined by $x$. This is for instance useful when computing resolvents,
+ hence is used by the \kbd{polgalois} function.
diff --git a/src/functions/number_fields/rnfalgtobasis b/src/functions/number_fields/rnfalgtobasis
new file mode 100644
index 0000000..05fb0ee
--- /dev/null
+++ b/src/functions/number_fields/rnfalgtobasis
@@ -0,0 +1,11 @@
+Function: rnfalgtobasis
+Section: number_fields
+C-Name: rnfalgtobasis
+Prototype: GG
+Help: rnfalgtobasis(rnf,x): relative version of nfalgtobasis, where rnf is a
+ relative numberfield.
+Doc: expresses $x$ on the relative
+ integral basis. Here, $\var{rnf}$ is a relative number field extension $L/K$
+ as output by \kbd{rnfinit}, and $x$ an element of $L$ in absolute form, i.e.
+ expressed as a polynomial or polmod with polmod coefficients, \emph{not} on
+ the relative integral basis.
diff --git a/src/functions/number_fields/rnfbasis b/src/functions/number_fields/rnfbasis
new file mode 100644
index 0000000..b0043ed
--- /dev/null
+++ b/src/functions/number_fields/rnfbasis
@@ -0,0 +1,17 @@
+Function: rnfbasis
+Section: number_fields
+C-Name: rnfbasis
+Prototype: GG
+Help: rnfbasis(bnf,M): given a projective Z_K-module M as output by
+ rnfpseudobasis or rnfsteinitz, gives either a basis of M if it is free, or an
+ n+1-element generating set.
+Doc: let $K$ the field represented by
+ \var{bnf}, as output by \kbd{bnfinit}. $M$ is a projective $\Z_K$-module
+ of rank $n$ ($M\otimes K$ is an $n$-dimensional $K$-vector space), given by a
+ pseudo-basis of size $n$. The routine returns either a true $\Z_K$-basis of
+ $M$ (of size $n$) if it exists, or an $n+1$-element generating set of $M$ if
+ not.
+
+ It is allowed to use an irreducible polynomial $P$ in $K[X]$ instead of $M$,
+ in which case, $M$ is defined as the ring of integers of $K[X]/(P)$, viewed
+ as a $\Z_K$-module.
diff --git a/src/functions/number_fields/rnfbasistoalg b/src/functions/number_fields/rnfbasistoalg
new file mode 100644
index 0000000..b1fca77
--- /dev/null
+++ b/src/functions/number_fields/rnfbasistoalg
@@ -0,0 +1,10 @@
+Function: rnfbasistoalg
+Section: number_fields
+C-Name: rnfbasistoalg
+Prototype: GG
+Help: rnfbasistoalg(rnf,x): relative version of nfbasistoalg, where rnf is a
+ relative numberfield.
+Doc: computes the representation of $x$
+ as a polmod with polmods coefficients. Here, $\var{rnf}$ is a relative number
+ field extension $L/K$ as output by \kbd{rnfinit}, and $x$ an element of
+ $L$ expressed on the relative integral basis.
diff --git a/src/functions/number_fields/rnfcharpoly b/src/functions/number_fields/rnfcharpoly
new file mode 100644
index 0000000..1a89454
--- /dev/null
+++ b/src/functions/number_fields/rnfcharpoly
@@ -0,0 +1,16 @@
+Function: rnfcharpoly
+Section: number_fields
+C-Name: rnfcharpoly
+Prototype: GGGDn
+Help: rnfcharpoly(nf,T,a,{var='x}): characteristic polynomial of a
+ over nf, where a belongs to the algebra defined by T over nf. Returns a
+ polynomial in variable var (x by default).
+Doc: characteristic polynomial of
+ $a$ over $\var{nf}$, where $a$ belongs to the algebra defined by $T$ over
+ $\var{nf}$, i.e.~$\var{nf}[X]/(T)$. Returns a polynomial in variable $v$
+ ($x$ by default).
+ \bprog
+ ? nf = nfinit(y^2+1);
+ ? rnfcharpoly(nf, x^2+y*x+1, x+y)
+ %2 = x^2 + Mod(-y, y^2 + 1)*x + 1
+ @eprog
diff --git a/src/functions/number_fields/rnfconductor b/src/functions/number_fields/rnfconductor
new file mode 100644
index 0000000..b4a0e10
--- /dev/null
+++ b/src/functions/number_fields/rnfconductor
@@ -0,0 +1,19 @@
+Function: rnfconductor
+Section: number_fields
+C-Name: rnfconductor
+Prototype: GG
+Help: rnfconductor(bnf,pol): conductor of the Abelian extension
+ of bnf defined by pol. The result is [conductor,rayclassgroup,subgroup],
+ where conductor is the conductor itself, rayclassgroup the structure of the
+ corresponding full ray class group, and subgroup the HNF defining the norm
+ group (Artin or Takagi group) on the given generators rayclassgroup[3].
+Doc: given $\var{bnf}$
+ as output by \kbd{bnfinit}, and \var{pol} a relative polynomial defining an
+ \idx{Abelian extension}, computes the class field theory conductor of this
+ Abelian extension. The result is a 3-component vector
+ $[\var{conductor},\var{rayclgp},\var{subgroup}]$, where \var{conductor} is
+ the conductor of the extension given as a 2-component row vector
+ $[f_0,f_\infty]$, \var{rayclgp} is the full ray class group corresponding to
+ the conductor given as a 3-component vector [h,cyc,gen] as usual for a group,
+ and \var{subgroup} is a matrix in HNF defining the subgroup of the ray class
+ group on the given generators gen.
diff --git a/src/functions/number_fields/rnfdedekind b/src/functions/number_fields/rnfdedekind
new file mode 100644
index 0000000..2c17dfe
--- /dev/null
+++ b/src/functions/number_fields/rnfdedekind
@@ -0,0 +1,75 @@
+Function: rnfdedekind
+Section: number_fields
+C-Name: rnfdedekind
+Prototype: GGDGD0,L,
+Help: rnfdedekind(nf,pol,{pr},{flag=0}): relative Dedekind criterion over the
+ number field K, represented by nf, applied to the order O_K[X]/(P),
+ modulo the prime ideal pr (at all primes if pr omitted, in which case
+ flag is automatically set to 1).
+ P is assumed to be monic, irreducible, in O_K[X].
+ Returns [max,basis,v], where basis is a pseudo-basis of the
+ enlarged order, max is 1 iff this order is pr-maximal, and v is the
+ valuation at pr of the order discriminant. If flag is set, just return 1 if
+ the order is maximal, and 0 if not.
+Doc: given a number field $K$ coded by $\var{nf}$ and a monic
+ polynomial $P\in \Z_K[X]$, irreducible over $K$ and thus defining a relative
+ extension $L$ of $K$, applies \idx{Dedekind}'s criterion to the order
+ $\Z_K[X]/(P)$, at the prime ideal \var{pr}. It is possible to set \var{pr}
+ to a vector of prime ideals (test maximality at all primes in the vector),
+ or to omit altogether, in which case maximality at \emph{all} primes is tested;
+ in this situation \fl\ is automatically set to $1$.
+
+ The default historic behavior (\fl\ is 0 or omitted and \var{pr} is a
+ single prime ideal) is not so useful since
+ \kbd{rnfpseudobasis} gives more information and is generally not that
+ much slower. It returns a 3-component vector $[\var{max}, \var{basis}, v]$:
+
+ \item \var{basis} is a pseudo-basis of an enlarged order $O$ produced by
+ Dedekind's criterion, containing the original order $\Z_K[X]/(P)$
+ with index a power of \var{pr}. Possibly equal to the original order.
+
+ \item \var{max} is a flag equal to 1 if the enlarged order $O$
+ could be proven to be \var{pr}-maximal and to 0 otherwise; it may still be
+ maximal in the latter case if \var{pr} is ramified in $L$,
+
+ \item $v$ is the valuation at \var{pr} of the order discriminant.
+
+ If \fl\ is non-zero, on the other hand, we just return $1$ if the order
+ $\Z_K[X]/(P)$ is \var{pr}-maximal (resp.~maximal at all relevant primes, as
+ described above), and $0$ if not. This is much faster than the default,
+ since the enlarged order is not computed.
+ \bprog
+ ? nf = nfinit(y^2-3); P = x^3 - 2*y;
+ ? pr3 = idealprimedec(nf,3)[1];
+ ? rnfdedekind(nf, P, pr3)
+ %2 = [1, [[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, 1]], 8]
+ ? rnfdedekind(nf, P, pr3, 1)
+ %3 = 1
+ @eprog\noindent In this example, \kbd{pr3} is the ramified ideal above $3$,
+ and the order generated by the cube roots of $y$ is already
+ \kbd{pr3}-maximal. The order-discriminant has valuation $8$. On the other
+ hand, the order is not maximal at the prime above 2:
+ \bprog
+ ? pr2 = idealprimedec(nf,2)[1];
+ ? rnfdedekind(nf, P, pr2, 1)
+ %5 = 0
+ ? rnfdedekind(nf, P, pr2)
+ %6 = [0, [[2, 0, 0; 0, 1, 0; 0, 0, 1], [[1, 0; 0, 1], [1, 0; 0, 1],
+      [1, 1/2; 0, 1/2]]], 2]
+ @eprog
+ The enlarged order is not proven to be \kbd{pr2}-maximal yet. In fact, it
+ is; it is in fact the maximal order:
+ \bprog
+ ? B = rnfpseudobasis(nf, P)
+ %7 = [[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, [1, 1/2; 0, 1/2]],
+      [162, 0; 0, 162], -1]
+ ? idealval(nf,B[3], pr2)
+ %4 = 2
+ @eprog\noindent
+ It is possible to use this routine with non-monic
+ $P = \sum_{i\leq n} a_i X^i \in \Z_K[X]$ if $\fl = 1$;
+ in this case, we test maximality of Dedekind's order generated by
+ $$1, a_n \alpha, a_n\alpha^2 + a_{n-1}\alpha, \dots,
+ a_n\alpha^{n-1} + a_{n-1}\alpha^{n-2} + \cdots + a_1\alpha.$$
+ The routine will fail if $P$ is $0$ on the projective line over the residue
+ field $\Z_K/\kbd{pr}$ (FIXME).
diff --git a/src/functions/number_fields/rnfdet b/src/functions/number_fields/rnfdet
new file mode 100644
index 0000000..0c125fa
--- /dev/null
+++ b/src/functions/number_fields/rnfdet
@@ -0,0 +1,7 @@
+Function: rnfdet
+Section: number_fields
+C-Name: rnfdet
+Prototype: GG
+Help: rnfdet(nf,M): given a pseudo-matrix M, compute its determinant.
+Doc: given a pseudo-matrix $M$ over the maximal
+ order of $\var{nf}$, computes its determinant.
diff --git a/src/functions/number_fields/rnfdisc b/src/functions/number_fields/rnfdisc
new file mode 100644
index 0000000..b773eba
--- /dev/null
+++ b/src/functions/number_fields/rnfdisc
@@ -0,0 +1,15 @@
+Function: rnfdisc
+Section: number_fields
+C-Name: rnfdiscf
+Prototype: GG
+Help: rnfdisc(nf,pol): given a pol with coefficients in nf, gives a
+ 2-component vector [D,d], where D is the relative ideal discriminant, and d
+ is the relative discriminant in nf^*/nf*^2.
+Doc: given a number field $\var{nf}$ as
+ output by \kbd{nfinit} and a polynomial \var{pol} with coefficients in
+ $\var{nf}$ defining a relative extension $L$ of $\var{nf}$, computes the
+ relative discriminant of $L$. This is a two-element row vector $[D,d]$, where
+ $D$ is the relative ideal discriminant and $d$ is the relative discriminant
+ considered as an element of $\var{nf}^*/{\var{nf}^*}^2$. The main variable of
+ $\var{nf}$ \emph{must} be of lower priority than that of \var{pol}, see
+ \secref{se:priority}.
diff --git a/src/functions/number_fields/rnfeltabstorel b/src/functions/number_fields/rnfeltabstorel
new file mode 100644
index 0000000..e1773bf
--- /dev/null
+++ b/src/functions/number_fields/rnfeltabstorel
@@ -0,0 +1,25 @@
+Function: rnfeltabstorel
+Section: number_fields
+C-Name: rnfeltabstorel
+Prototype: GG
+Help: rnfeltabstorel(rnf,x): transforms the element x from absolute to
+ relative representation.
+Doc: $\var{rnf}$ being a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an
+ element of $L$ expressed as a polynomial modulo the absolute equation
+ \kbd{\var{rnf}.pol}, computes $x$ as an element of the relative extension
+ $L/K$ as a polmod with polmod coefficients.
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+ ? L.pol
+ %2 = x^4 + 1
+ ? rnfeltabstorel(L, Mod(x, L.pol))
+ %3 = Mod(x, x^2 + Mod(-y, y^2 + 1))
+ ? rnfeltabstorel(L, Mod(2, L.pol))
+ %4 = 2
+ ? rnfeltabstorel(L, Mod(x, x^2-y))
+  ***   at top-level: rnfeltabstorel(L,Mod
+  ***                 ^--------------------
+  *** rnfeltabstorel: inconsistent moduli in rnfeltabstorel: x^2-y != x^4+1
+ @eprog
+
diff --git a/src/functions/number_fields/rnfeltdown b/src/functions/number_fields/rnfeltdown
new file mode 100644
index 0000000..c0081e1
--- /dev/null
+++ b/src/functions/number_fields/rnfeltdown
@@ -0,0 +1,26 @@
+Function: rnfeltdown
+Section: number_fields
+C-Name: rnfeltdown
+Prototype: GG
+Help: rnfeltdown(rnf,x): expresses x on the base field if possible; returns
+ an error otherwise.
+Doc: $\var{rnf}$ being a relative number
+ field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an element of
+ $L$ expressed as a polynomial or polmod with polmod coefficients, computes
+ $x$ as an element of $K$ as a polmod, assuming $x$ is in $K$ (otherwise a
+ domain error occurs).
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+ ? L.pol
+ %2 = x^4 + 1
+ ? rnfeltdown(L, Mod(x^2, L.pol))
+ %3 = Mod(y, y^2 + 1)
+ ? rnfeltdown(L, Mod(y, x^2-y))
+ %4 = Mod(y, y^2 + 1)
+ ? rnfeltdown(L, Mod(y,K.pol))
+ %5 = Mod(y, y^2 + 1)
+ ? rnfeltdown(L, Mod(x, L.pol))
+  ***   at top-level: rnfeltdown(L,Mod(x,x
+  ***                 ^--------------------
+  *** rnfeltdown: domain error in rnfeltdown: element not in the base field
+ @eprog
diff --git a/src/functions/number_fields/rnfeltnorm b/src/functions/number_fields/rnfeltnorm
new file mode 100644
index 0000000..96009ad
--- /dev/null
+++ b/src/functions/number_fields/rnfeltnorm
@@ -0,0 +1,18 @@
+Function: rnfeltnorm
+Section: number_fields
+C-Name: rnfeltnorm
+Prototype: GG
+Help: rnfeltnorm(rnf,x): returns the relative norm N_{L/K}(x), as an element
+ of K
+Doc: $\var{rnf}$ being a relative number field extension $L/K$ as output by
+ \kbd{rnfinit} and $x$ being an element of $L$, returns the relative norm
+ $N_{L/K}(x)$ as an element of $K$.
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+ ? rnfeltnorm(L, Mod(x, L.pol))
+ %2 = Mod(x, x^2 + Mod(-y, y^2 + 1))
+ ? rnfeltnorm(L, 2)
+ %3 = 4
+ ? rnfeltnorm(L, Mod(x, x^2-y))
+ @eprog
+
diff --git a/src/functions/number_fields/rnfeltreltoabs b/src/functions/number_fields/rnfeltreltoabs
new file mode 100644
index 0000000..0088094
--- /dev/null
+++ b/src/functions/number_fields/rnfeltreltoabs
@@ -0,0 +1,22 @@
+Function: rnfeltreltoabs
+Section: number_fields
+C-Name: rnfeltreltoabs
+Prototype: GG
+Help: rnfeltreltoabs(rnf,x): transforms the element x from relative to
+ absolute representation.
+Doc: $\var{rnf}$ being a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an
+ element of $L$ expressed as a polynomial or polmod with polmod
+ coefficients, computes $x$ as an element of the absolute extension $L/\Q$ as
+ a polynomial modulo the absolute equation \kbd{\var{rnf}.pol}.
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+ ? L.pol
+ %2 = x^4 + 1
+ ? rnfeltreltoabs(L, Mod(x, L.pol))
+ %3 = Mod(x, x^4 + 1)
+ ? rnfeltreltoabs(L, Mod(y, x^2-y))
+ %4 = Mod(x^2, x^4 + 1)
+ ? rnfeltreltoabs(L, Mod(y,K.pol))
+ %5 = Mod(x^2, x^4 + 1)
+ @eprog
diff --git a/src/functions/number_fields/rnfelttrace b/src/functions/number_fields/rnfelttrace
new file mode 100644
index 0000000..bba92de
--- /dev/null
+++ b/src/functions/number_fields/rnfelttrace
@@ -0,0 +1,18 @@
+Function: rnfelttrace
+Section: number_fields
+C-Name: rnfelttrace
+Prototype: GG
+Help: rnfelttrace(rnf,x): returns the relative trace N_{L/K}(x), as an element
+ of K
+Doc: $\var{rnf}$ being a relative number field extension $L/K$ as output by
+ \kbd{rnfinit} and $x$ being an element of $L$, returns the relative trace
+ $N_{L/K}(x)$ as an element of $K$.
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+ ? rnfelttrace(L, Mod(x, L.pol))
+ %2 = 0
+ ? rnfelttrace(L, 2)
+ %3 = 4
+ ? rnfelttrace(L, Mod(x, x^2-y))
+ @eprog
+
diff --git a/src/functions/number_fields/rnfeltup b/src/functions/number_fields/rnfeltup
new file mode 100644
index 0000000..85d7e72
--- /dev/null
+++ b/src/functions/number_fields/rnfeltup
@@ -0,0 +1,21 @@
+Function: rnfeltup
+Section: number_fields
+C-Name: rnfeltup
+Prototype: GG
+Help: rnfeltup(rnf,x): expresses x (belonging to the base field) on the
+ relative field.
+Doc: $\var{rnf}$ being a relative number field extension $L/K$ as output by
+ \kbd{rnfinit} and $x$ being an element of $K$, computes $x$ as an element of
+ the absolute extension $L/\Q$ as a polynomial modulo the absolute equation
+ \kbd{\var{rnf}.pol}.
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y);
+ ? L.pol
+ %2 = x^4 + 1
+ ? rnfeltup(L, Mod(y, K.pol))
+ %4 = Mod(x^2, x^4 + 1)
+ ? rnfeltup(L, y)
+ %5 = Mod(x^2, x^4 + 1)
+ ? rnfeltup(L, [1,2]~) \\ in terms of K.zk
+ %6 = Mod(2*x^2 + 1, x^4 + 1)
+ @eprog
diff --git a/src/functions/number_fields/rnfequation b/src/functions/number_fields/rnfequation
new file mode 100644
index 0000000..fdbef1a
--- /dev/null
+++ b/src/functions/number_fields/rnfequation
@@ -0,0 +1,56 @@
+Function: rnfequation
+Section: number_fields
+C-Name: rnfequation0
+Prototype: GGD0,L,
+Help: rnfequation(nf,pol,{flag=0}): given a pol with coefficients in nf,
+ gives an absolute equation z of the number field defined by pol. flag is
+ optional, and can be 0: default, or non-zero, gives [z,al,k], where
+ z defines the absolute equation L/Q as in the default behavior,
+ al expresses as an element of L a root of the polynomial
+ defining the base field nf, and k is a small integer such that
+ t = b + k al is a root of z, for b a root of pol.
+Doc: given a number field
+ $\var{nf}$ as output by \kbd{nfinit} (or simply a polynomial) and a
+ polynomial \var{pol} with coefficients in $\var{nf}$ defining a relative
+ extension $L$ of $\var{nf}$, computes an absolute equation of $L$ over
+ $\Q$.
+
+ The main variable of $\var{nf}$ \emph{must} be of lower priority than that
+ of \var{pol} (see \secref{se:priority}). Note that for efficiency, this does
+ not check whether the relative equation is irreducible over $\var{nf}$, but
+ only if it is squarefree. If it is reducible but squarefree, the result will
+ be the absolute equation of the \'etale algebra defined by \var{pol}. If
+ \var{pol} is not squarefree, raise an \kbd{e\_DOMAIN} exception.
+ \bprog
+ ? rnfequation(y^2+1, x^2 - y)
+ %1 = x^4 + 1
+ ? T = y^3-2; rnfequation(nfinit(T), (x^3-2)/(x-Mod(y,T)))
+ %2 = x^6 + 108  \\ Galois closure of Q(2^(1/3))
+ @eprog
+
+ If $\fl$ is non-zero, outputs a 3-component row vector $[z,a,k]$, where
+
+ \item $z$ is the absolute equation of $L$ over $\Q$, as in the default
+ behavior,
+
+ \item $a$ expresses as a \typ{POLMOD} modulo $z$ a root $\alpha$ of the
+ polynomial defining the base field $\var{nf}$,
+
+ \item $k$ is a small integer such that $\theta = \beta+k\alpha$
+ is a root of $z$, where $\beta$ is a root of $\var{pol}$.
+ \bprog
+ ? T = y^3-2; pol = x^2 +x*y + y^2;
+ ? [z,a,k] = rnfequation(T, pol, 1);
+ ? z
+ %4 = x^6 + 108
+ ? subst(T, y, a)
+ %5 = 0
+ ? alpha= Mod(y, T);
+ ? beta = Mod(x*Mod(1,T), pol);
+ ? subst(z, x, beta + k*alpha)
+ %8 = 0
+ @eprog
+
+Variant: Also available are
+ \fun{GEN}{rnfequation}{GEN nf, GEN pol} ($\fl = 0$) and
+ \fun{GEN}{rnfequation2}{GEN nf, GEN pol} ($\fl = 1$).
diff --git a/src/functions/number_fields/rnfhnfbasis b/src/functions/number_fields/rnfhnfbasis
new file mode 100644
index 0000000..bb741f1
--- /dev/null
+++ b/src/functions/number_fields/rnfhnfbasis
@@ -0,0 +1,11 @@
+Function: rnfhnfbasis
+Section: number_fields
+C-Name: rnfhnfbasis
+Prototype: GG
+Help: rnfhnfbasis(bnf,x): given an order x as output by rnfpseudobasis,
+ gives either a true HNF basis of the order if it exists, zero otherwise.
+Doc: given $\var{bnf}$ as output by
+ \kbd{bnfinit}, and either a polynomial $x$ with coefficients in $\var{bnf}$
+ defining a relative extension $L$ of $\var{bnf}$, or a pseudo-basis $x$ of
+ such an extension, gives either a true $\var{bnf}$-basis of $L$ in upper
+ triangular Hermite normal form, if it exists, and returns $0$ otherwise.
diff --git a/src/functions/number_fields/rnfidealabstorel b/src/functions/number_fields/rnfidealabstorel
new file mode 100644
index 0000000..6c46d1d
--- /dev/null
+++ b/src/functions/number_fields/rnfidealabstorel
@@ -0,0 +1,40 @@
+Function: rnfidealabstorel
+Section: number_fields
+C-Name: rnfidealabstorel
+Prototype: GG
+Help: rnfidealabstorel(rnf,x): transforms the ideal x from absolute to
+ relative representation.
+Doc: let $\var{rnf}$ be a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and $x$ be an ideal of
+ the absolute extension $L/\Q$ given by a $\Z$-basis of elements of $L$.
+ Returns the relative pseudo-matrix in HNF giving the ideal $x$ considered as
+ an ideal of the relative extension $L/K$, i.e.~as a $\Z_K$-module.
+
+ The reason why the input does not use the customary HNF in terms of a fixed
+ $\Z$-basis for $\Z_L$ is precisely that no such basis has been explicitly
+ specified. On the other hand, if you already computed an (absolute) \var{nf}
+ structure \kbd{Labs} associated to $L$, and $m$ is in HNF, defining
+ an (absolute) ideal with respect to the $\Z$-basis \kbd{Labs.zk}, then
+ \kbd{Labs.zk * m} is a suitable $\Z$-basis for the ideal, and
+ \bprog
+   rnfidealabstorel(rnf, Labs.zk * m)
+ @eprog\noindent converts $m$ to a relative ideal.
+ \bprog
+ ? K = nfinit(y^2+1); L = rnfinit(K, x^2-y); Labs = nfinit(L.pol);
+ ? m = idealhnf(Labs, 17, x^3+2);
+ ? B = rnfidealabstorel(L, Labs.zk * m)
+ %3 = [[1, 8; 0, 1], [[17, 4; 0, 1], 1]]  \\ pseudo-basis for m as Z_K-module
+ ? A = rnfidealreltoabs(L, B)
+ %4 = [17, x^2 + 4, x + 8, x^3 + 8*x^2]   \\ Z-basis for m in Q[x]/(L.pol)
+ ? mathnf(matalgtobasis(Labs, A))
+ %5 =
+ [17 8 4 2]
+
+ [ 0 1 0 0]
+
+ [ 0 0 1 0]
+
+ [ 0 0 0 1]
+ ? % == m
+ %6 = 1
+ @eprog
diff --git a/src/functions/number_fields/rnfidealdown b/src/functions/number_fields/rnfidealdown
new file mode 100644
index 0000000..22669d9
--- /dev/null
+++ b/src/functions/number_fields/rnfidealdown
@@ -0,0 +1,11 @@
+Function: rnfidealdown
+Section: number_fields
+C-Name: rnfidealdown
+Prototype: GG
+Help: rnfidealdown(rnf,x): finds the intersection of the ideal x with the
+ base field.
+Doc: let $\var{rnf}$ be a relative number
+ field extension $L/K$ as output by \kbd{rnfinit}, and $x$ an ideal of
+ $L$, given either in relative form or by a $\Z$-basis of elements of $L$
+ (see \secref{se:rnfidealabstorel}). This function returns the ideal of $K$
+ below $x$, i.e.~the intersection of $x$ with $K$.
diff --git a/src/functions/number_fields/rnfidealhnf b/src/functions/number_fields/rnfidealhnf
new file mode 100644
index 0000000..45c6ddd
--- /dev/null
+++ b/src/functions/number_fields/rnfidealhnf
@@ -0,0 +1,11 @@
+Function: rnfidealhnf
+Section: number_fields
+C-Name: rnfidealhnf
+Prototype: GG
+Help: rnfidealhnf(rnf,x): relative version of idealhnf, where rnf is a
+ relative numberfield.
+Doc: $\var{rnf}$ being a relative number
+ field extension $L/K$ as output by \kbd{rnfinit} and $x$ being a relative
+ ideal (which can be, as in the absolute case, of many different types,
+ including of course elements), computes the HNF pseudo-matrix associated to
+ $x$, viewed as a $\Z_K$-module.
diff --git a/src/functions/number_fields/rnfidealmul b/src/functions/number_fields/rnfidealmul
new file mode 100644
index 0000000..ad5ad02
--- /dev/null
+++ b/src/functions/number_fields/rnfidealmul
@@ -0,0 +1,10 @@
+Function: rnfidealmul
+Section: number_fields
+C-Name: rnfidealmul
+Prototype: GGG
+Help: rnfidealmul(rnf,x,y): relative version of idealmul, where rnf is a
+ relative numberfield.
+Doc: $\var{rnf}$ being a relative number
+ field extension $L/K$ as output by \kbd{rnfinit} and $x$ and $y$ being ideals
+ of the relative extension $L/K$ given by pseudo-matrices, outputs the ideal
+ product, again as a relative ideal.
diff --git a/src/functions/number_fields/rnfidealnormabs b/src/functions/number_fields/rnfidealnormabs
new file mode 100644
index 0000000..dc3c6eb
--- /dev/null
+++ b/src/functions/number_fields/rnfidealnormabs
@@ -0,0 +1,14 @@
+Function: rnfidealnormabs
+Section: number_fields
+C-Name: rnfidealnormabs
+Prototype: GG
+Help: rnfidealnormabs(rnf,x): absolute norm of the ideal x.
+Doc: let $\var{rnf}$ be a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be a
+ relative ideal (which can be, as in the absolute case, of many different
+ types, including of course elements). This function computes the norm of the
+ $x$ considered as an ideal of the absolute extension $L/\Q$. This is
+ identical to
+ \bprog
+    idealnorm(rnf, rnfidealnormrel(rnf,x))
+ @eprog\noindent but faster.
diff --git a/src/functions/number_fields/rnfidealnormrel b/src/functions/number_fields/rnfidealnormrel
new file mode 100644
index 0000000..70be1a6
--- /dev/null
+++ b/src/functions/number_fields/rnfidealnormrel
@@ -0,0 +1,10 @@
+Function: rnfidealnormrel
+Section: number_fields
+C-Name: rnfidealnormrel
+Prototype: GG
+Help: rnfidealnormrel(rnf,x): relative norm of the ideal x.
+Doc: let $\var{rnf}$ be a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be a
+ relative ideal (which can be, as in the absolute case, of many different
+ types, including of course elements). This function computes the relative
+ norm of $x$ as an ideal of $K$ in HNF.
diff --git a/src/functions/number_fields/rnfidealreltoabs b/src/functions/number_fields/rnfidealreltoabs
new file mode 100644
index 0000000..f8c2304
--- /dev/null
+++ b/src/functions/number_fields/rnfidealreltoabs
@@ -0,0 +1,22 @@
+Function: rnfidealreltoabs
+Section: number_fields
+C-Name: rnfidealreltoabs
+Prototype: GG
+Help: rnfidealreltoabs(rnf,x): transforms the ideal x from relative to
+ absolute representation.
+Doc: let $\var{rnf}$ be a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be a
+ relative ideal, given as a $\Z_K$-module by a pseudo matrix $[A,I]$.
+ This function returns the ideal $x$ as an absolute ideal of $L/\Q$ in
+ the form of a $\Z$-basis, given by a vector of polynomials (modulo
+ \kbd{rnf.pol}).
+
+ The reason why we do not return the customary HNF in terms of a fixed
+ $\Z$-basis for $\Z_L$ is precisely that no such basis has been explicitly
+ specified. On the other hand, if you already computed an (absolute) \var{nf}
+ structure \kbd{Labs} associated to $L$, then
+ \bprog
+   xabs = rnfidealreltoabs(L, x);
+   xLabs = mathnf(matalgtobasis(Labs, xabs));
+ @eprog\noindent computes a traditional HNF \kbd{xLabs} for $x$ in terms of
+ the fixed $\Z$-basis \kbd{Labs.zk}.
diff --git a/src/functions/number_fields/rnfidealtwoelt b/src/functions/number_fields/rnfidealtwoelt
new file mode 100644
index 0000000..6c1f5af
--- /dev/null
+++ b/src/functions/number_fields/rnfidealtwoelt
@@ -0,0 +1,11 @@
+Function: rnfidealtwoelt
+Section: number_fields
+C-Name: rnfidealtwoelement
+Prototype: GG
+Help: rnfidealtwoelt(rnf,x): relative version of idealtwoelt, where rnf
+ is a relative numberfield.
+Doc: $\var{rnf}$ being a relative
+ number field extension $L/K$ as output by \kbd{rnfinit} and $x$ being an
+ ideal of the relative extension $L/K$ given by a pseudo-matrix, gives a
+ vector of two generators of $x$ over $\Z_L$ expressed as polmods with polmod
+ coefficients.
diff --git a/src/functions/number_fields/rnfidealup b/src/functions/number_fields/rnfidealup
new file mode 100644
index 0000000..6ad0f4e
--- /dev/null
+++ b/src/functions/number_fields/rnfidealup
@@ -0,0 +1,21 @@
+Function: rnfidealup
+Section: number_fields
+C-Name: rnfidealup
+Prototype: GG
+Help: rnfidealup(rnf,x): lifts the ideal x (of the base field) to the
+ relative field.
+Doc: let $\var{rnf}$ be a relative number
+ field extension $L/K$ as output by \kbd{rnfinit} and let $x$ be an ideal of
+ $K$. This function returns the ideal $x\Z_L$ as an absolute ideal of $L/\Q$,
+ in the form of a $\Z$-basis, given by a vector of polynomials (modulo
+ \kbd{rnf.pol}).
+
+ The reason why we do not return the customary HNF in terms of a fixed
+ $\Z$-basis for $\Z_L$ is precisely that no such basis has been explicitly
+ specified. On the other hand, if you already computed an (absolute) \var{nf}
+ structure \kbd{Labs} associated to $L$, then
+ \bprog
+   xabs = rnfidealup(L, x);
+   xLabs = mathnf(matalgtobasis(Labs, xabs));
+ @eprog\noindent computes a traditional HNF \kbd{xLabs} for $x$ in terms of
+ the fixed $\Z$-basis \kbd{Labs.zk}.
diff --git a/src/functions/number_fields/rnfinit b/src/functions/number_fields/rnfinit
new file mode 100644
index 0000000..b4acb03
--- /dev/null
+++ b/src/functions/number_fields/rnfinit
@@ -0,0 +1,81 @@
+Function: rnfinit
+Section: number_fields
+C-Name: rnfinit
+Prototype: GG
+Help: rnfinit(nf,pol): pol being an irreducible polynomial
+ defined over the number field nf, initializes a vector of data necessary for
+ working in relative number fields (rnf functions). See manual for technical
+ details.
+Doc: $\var{nf}$ being a number field in \kbd{nfinit}
+ format considered as base field, and \var{pol} a polynomial defining a relative
+ extension over $\var{nf}$, this computes data to work in the
+ relative extension. The main variable of \var{pol} must be of higher priority
+ (see \secref{se:priority}) than that of $\var{nf}$, and the coefficients of
+ \var{pol} must be in $\var{nf}$.
+
+ The result is a row vector, whose components are technical. In the following
+ description, we let $K$ be the base field defined by $\var{nf}$ and $L/K$
+ the large field associated to the \var{rnf}. Furthermore, we let
+ $m = [K:\Q]$ the degree of the base field, $n = [L:K]$ the relative degree,
+ $r_1$ and $r_2$ the number of real and complex places of $K$. Access to this
+ information via \emph{member functions} is preferred since the specific
+ data organization specified below will change in the future.
+
+ $\var{rnf}[1]$(\kbd{rnf.pol}) contains the relative polynomial \var{pol}.
+
+ $\var{rnf}[2]$ contains the integer basis $[A,d]$ of $K$, as
+ (integral) elements of $L/\Q$. More precisely, $A$ is a vector of
+ polynomial with integer coefficients, $d$ is a denominator, and the integer
+ basis is given by $A/d$.
+
+ $\var{rnf}[3]$ (\kbd{rnf.disc}) is a two-component row vector
+ $[\goth{d}(L/K),s]$ where $\goth{d}(L/K)$ is the relative ideal discriminant
+ of $L/K$ and $s$ is the discriminant of $L/K$ viewed as an element of
+ $K^*/(K^*)^2$, in other words it is the output of \kbd{rnfdisc}.
+
+ $\var{rnf}[4]$(\kbd{rnf.index}) is the ideal index $\goth{f}$, i.e.~such
+ that $d(pol)\Z_K=\goth{f}^2\goth{d}(L/K)$.
+
+ $\var{rnf}[5]$ is currently unused.
+
+ $\var{rnf}[6]$ is currently unused.
+
+ $\var{rnf}[7]$ (\kbd{rnf.zk}) is the pseudo-basis $(A,I)$ for the maximal
+ order $\Z_L$ as a $\Z_K$-module: $A$ is the relative integral pseudo basis
+ expressed as polynomials (in the variable of $pol$) with polmod coefficients
+ in $\var{nf}$, and the second component $I$ is the ideal list of the
+ pseudobasis in HNF.
+
+ $\var{rnf}[8]$ is the inverse matrix of the integral basis matrix, with
+ coefficients polmods in $\var{nf}$.
+
+ $\var{rnf}[9]$ is currently unused.
+
+ $\var{rnf}[10]$ (\kbd{rnf.nf}) is $\var{nf}$.
+
+ $\var{rnf}[11]$ is the output of \kbd{rnfequation(K, pol, 1)}. Namely, a
+ vector $[P, a, k]$ describing the \emph{absolute} extension
+ $L/\Q$: $P$ is an absolute equation, more conveniently obtained
+ as \kbd{rnf.polabs}; $a$ expresses the generator $\alpha = y \mod \kbd{K.pol}$
+ of the number field $K$ as an element of $L$, i.e.~a polynomial modulo the
+ absolute equation $P$;
+
+ $k$ is a small integer such that, if $\beta$ is an abstract root of \var{pol}
+ and $\alpha$ the generator of $K$ given above, then $P(\beta + k\alpha) = 0$.
+
+ \misctitle{Caveat.} Be careful if $k\neq0$ when dealing simultaneously with
+ absolute and relative quantities since $L = \Q(\beta + k\alpha) =
+ K(\alpha)$, and the generator chosen for the absolute extension is not the
+ same as for the relative one. If this happens, one can of course go on
+ working, but we advise to change the relative polynomial so that its root
+ becomes $\beta + k \alpha$. Typical GP instructions would be
+ \bprog
+   [P,a,k] = rnfequation(K, pol, 1);
+   if (k, pol = subst(pol, x, x - k*Mod(y, K.pol)));
+   L = rnfinit(K, pol);
+ @eprog
+
+ $\var{rnf}[12]$ is by default unused and set equal to 0. This field is used
+ to store further information about the field as it becomes available (which
+ is rarely needed, hence would be too expensive to compute during the initial
+ \kbd{rnfinit} call).
diff --git a/src/functions/number_fields/rnfisabelian b/src/functions/number_fields/rnfisabelian
new file mode 100644
index 0000000..d8b911a
--- /dev/null
+++ b/src/functions/number_fields/rnfisabelian
@@ -0,0 +1,13 @@
+Function: rnfisabelian
+Section: number_fields
+C-Name: rnfisabelian
+Prototype: lGG
+Help: rnfisabelian(nf,T): T being a relative polynomial with coefficients
+ in nf, return 1 if it defines an abelian extension, and 0 otherwise.
+Doc: $T$ being a relative polynomial with coefficients
+ in \var{nf}, return 1 if it defines an abelian extension, and 0 otherwise.
+ \bprog
+ ? K = nfinit(y^2 + 23);
+ ? rnfisabelian(K, x^3 - 3*x - y)
+ %2 = 1
+ @eprog
diff --git a/src/functions/number_fields/rnfisfree b/src/functions/number_fields/rnfisfree
new file mode 100644
index 0000000..eed3ef3
--- /dev/null
+++ b/src/functions/number_fields/rnfisfree
@@ -0,0 +1,12 @@
+Function: rnfisfree
+Section: number_fields
+C-Name: rnfisfree
+Prototype: lGG
+Help: rnfisfree(bnf,x): given an order x as output by rnfpseudobasis or
+ rnfsteinitz, outputs true (1) or false (0) according to whether the order is
+ free or not.
+Doc: given $\var{bnf}$ as output by
+ \kbd{bnfinit}, and either a polynomial $x$ with coefficients in $\var{bnf}$
+ defining a relative extension $L$ of $\var{bnf}$, or a pseudo-basis $x$ of
+ such an extension, returns true (1) if $L/\var{bnf}$ is free, false (0) if
+ not.
diff --git a/src/functions/number_fields/rnfisnorm b/src/functions/number_fields/rnfisnorm
new file mode 100644
index 0000000..119102c
--- /dev/null
+++ b/src/functions/number_fields/rnfisnorm
@@ -0,0 +1,47 @@
+Function: rnfisnorm
+Section: number_fields
+C-Name: rnfisnorm
+Prototype: GGD0,L,
+Help: rnfisnorm(T,a,{flag=0}): T is as output by rnfisnorminit applied to
+ L/K. Tries to tell whether a is a norm from L/K. Returns a vector [x,q]
+ where a=Norm(x)*q. Looks for a solution which is a S-integer, with S a list
+ of places in K containing the ramified primes, generators of the class group
+ of ext, as well as those primes dividing a. If L/K is Galois, omit flag,
+ otherwise it is used to add more places to S: all the places above the
+ primes p <= flag (resp. p | flag) if flag > 0 (resp. flag < 0). The answer
+ is guaranteed (i.e a is a norm iff q=1) if L/K is Galois or, under GRH, if S
+ contains all primes less than 12.log(disc(M))^2, where M is the normal
+ closure of L/K.
+Doc: similar to
+ \kbd{bnfisnorm} but in the relative case. $T$ is as output by
+ \tet{rnfisnorminit} applied to the extension $L/K$. This tries to decide
+ whether the element $a$ in $K$ is the norm of some $x$ in the extension
+ $L/K$.
+
+ The output is a vector $[x,q]$, where $a = \Norm(x)*q$. The
+ algorithm looks for a solution $x$ which is an $S$-integer, with $S$ a list
+ of places of $K$ containing at least the ramified primes, the generators of
+ the class group of $L$, as well as those primes dividing $a$. If $L/K$ is
+ Galois, then this is enough; otherwise, $\fl$ is used to add more primes to
+ $S$: all the places above the primes $p \leq \fl$ (resp.~$p|\fl$) if $\fl>0$
+ (resp.~$\fl<0$).
+
+ The answer is guaranteed (i.e.~$a$ is a norm iff $q = 1$) if the field is
+ Galois, or, under \idx{GRH}, if $S$ contains all primes less than
+ $12\log^2\left|\disc(M)\right|$, where $M$ is the normal
+ closure of $L/K$.
+
+ If \tet{rnfisnorminit} has determined (or was told) that $L/K$ is
+ \idx{Galois}, and $\fl \neq 0$, a Warning is issued (so that you can set
+ $\fl = 1$ to check whether $L/K$ is known to be Galois, according to $T$).
+ Example:
+
+ \bprog
+ bnf = bnfinit(y^3 + y^2 - 2*y - 1);
+ p = x^2 + Mod(y^2 + 2*y + 1, bnf.pol);
+ T = rnfisnorminit(bnf, p);
+ rnfisnorm(T, 17)
+ @eprog\noindent
+ checks whether $17$ is a norm in the Galois extension $\Q(\beta) /
+ \Q(\alpha)$, where $\alpha^3 + \alpha^2 - 2\alpha - 1 = 0$ and $\beta^2 +
+ \alpha^2 + 2\alpha + 1 = 0$ (it is).
diff --git a/src/functions/number_fields/rnfisnorminit b/src/functions/number_fields/rnfisnorminit
new file mode 100644
index 0000000..89970b4
--- /dev/null
+++ b/src/functions/number_fields/rnfisnorminit
@@ -0,0 +1,22 @@
+Function: rnfisnorminit
+Section: number_fields
+C-Name: rnfisnorminit
+Prototype: GGD2,L,
+Help: rnfisnorminit(pol,polrel,{flag=2}): let K be defined by a root of pol,
+ L/K the extension defined by polrel. Compute technical data needed by
+ rnfisnorm to solve norm equations Nx = a, for x in L, and a in K. If flag=0,
+ do not care whether L/K is Galois or not; if flag = 1, assume L/K is Galois;
+ if flag = 2, determine whether L/K is Galois.
+Doc: let $K$ be defined by a root of \var{pol}, and $L/K$ the extension defined
+ by the polynomial \var{polrel}. As usual, \var{pol} can in fact be an \var{nf},
+ or \var{bnf}, etc; if \var{pol} has degree $1$ (the base field is $\Q$),
+ polrel is also allowed to be an \var{nf}, etc. Computes technical data needed
+ by \tet{rnfisnorm} to solve norm equations $Nx = a$, for $x$ in $L$, and $a$
+ in $K$.
+
+ If $\fl = 0$, do not care whether $L/K$ is Galois or not.
+
+ If $\fl = 1$, $L/K$ is assumed to be Galois (unchecked), which speeds up
+ \tet{rnfisnorm}.
+
+ If $\fl = 2$, let the routine determine whether $L/K$ is Galois.
diff --git a/src/functions/number_fields/rnfkummer b/src/functions/number_fields/rnfkummer
new file mode 100644
index 0000000..a09b3f6
--- /dev/null
+++ b/src/functions/number_fields/rnfkummer
@@ -0,0 +1,24 @@
+Function: rnfkummer
+Section: number_fields
+C-Name: rnfkummer
+Prototype: GDGD0,L,p
+Help: rnfkummer(bnr,{subgp},{d=0}): bnr being as output by bnrinit,
+ finds a relative equation for the class field corresponding to the module in
+ bnr and the given congruence subgroup (the ray class field if subgp is
+ omitted). d can be zero (default), or positive, and in this case the
+ output is the list of all relative equations of degree d for the given bnr,
+ with the same conductor as (bnr, subgp).
+Doc: \var{bnr}
+ being as output by \kbd{bnrinit}, finds a relative equation for the
+ class field corresponding to the module in \var{bnr} and the given
+ congruence subgroup (the full ray class field if \var{subgp} is omitted).
+ If $d$ is positive, outputs the list of all relative equations of
+ degree $d$ contained in the ray class field defined by \var{bnr}, with
+ the \emph{same} conductor as $(\var{bnr}, \var{subgp})$.
+
+ \misctitle{Warning} This routine only works for subgroups of prime index. It
+ uses Kummer theory, adjoining necessary roots of unity (it needs to compute a
+ tough \kbd{bnfinit} here), and finds a generator via Hecke's characterization
+ of ramification in Kummer extensions of prime degree. If your extension does
+ not have prime degree, for the time being, you have to split it by hand as a
+ tower / compositum of such extensions.
diff --git a/src/functions/number_fields/rnflllgram b/src/functions/number_fields/rnflllgram
new file mode 100644
index 0000000..fa9bf9b
--- /dev/null
+++ b/src/functions/number_fields/rnflllgram
@@ -0,0 +1,13 @@
+Function: rnflllgram
+Section: number_fields
+C-Name: rnflllgram
+Prototype: GGGp
+Help: rnflllgram(nf,pol,order): given a pol with coefficients in nf and an
+ order as output by rnfpseudobasis or similar, gives [[neworder],U], where
+ neworder is a reduced order and U is the unimodular transformation matrix.
+Doc: given a polynomial
+ \var{pol} with coefficients in \var{nf} defining a relative extension $L$ and
+ a suborder \var{order} of $L$ (of maximal rank), as output by
+ \kbd{rnfpseudobasis}$(\var{nf},\var{pol})$ or similar, gives
+ $[[\var{neworder}],U]$, where \var{neworder} is a reduced order and $U$ is
+ the unimodular transformation matrix.
diff --git a/src/functions/number_fields/rnfnormgroup b/src/functions/number_fields/rnfnormgroup
new file mode 100644
index 0000000..2ea3fda
--- /dev/null
+++ b/src/functions/number_fields/rnfnormgroup
@@ -0,0 +1,21 @@
+Function: rnfnormgroup
+Section: number_fields
+C-Name: rnfnormgroup
+Prototype: GG
+Help: rnfnormgroup(bnr,pol): norm group (or Artin or Takagi group)
+ corresponding to the Abelian extension of bnr.bnf defined by pol, where
+ the module corresponding to bnr is assumed to be a multiple of the
+ conductor. The result is the HNF defining the norm group on the
+ generators in bnr.gen.
+Doc:
+ \var{bnr} being a big ray
+ class field as output by \kbd{bnrinit} and \var{pol} a relative polynomial
+ defining an \idx{Abelian extension}, computes the norm group (alias Artin
+ or Takagi group) corresponding to the Abelian extension of
+ $\var{bnf}=$\kbd{bnr.bnf}
+ defined by \var{pol}, where the module corresponding to \var{bnr} is assumed
+ to be a multiple of the conductor (i.e.~\var{pol} defines a subextension of
+ bnr). The result is the HNF defining the norm group on the given generators
+ of \kbd{bnr.gen}. Note that neither the fact that \var{pol} defines an
+ Abelian extension nor the fact that the module is a multiple of the conductor
+ is checked. The result is undefined if the assumption is not correct.
diff --git a/src/functions/number_fields/rnfpolred b/src/functions/number_fields/rnfpolred
new file mode 100644
index 0000000..176fd7f
--- /dev/null
+++ b/src/functions/number_fields/rnfpolred
@@ -0,0 +1,15 @@
+Function: rnfpolred
+Section: number_fields
+C-Name: rnfpolred
+Prototype: GGp
+Help: rnfpolred(nf,pol): given a pol with coefficients in nf, finds a list
+ of relative polynomials defining some subfields, hopefully simpler.
+Doc:  THIS FUNCTION IS OBSOLETE: use \tet{rnfpolredbest} instead.
+ Relative version of \kbd{polred}. Given a monic polynomial \var{pol} with
+ coefficients in $\var{nf}$, finds a list of relative polynomials defining some
+ subfields, hopefully simpler and containing the original field. In the present
+ version \vers, this is slower and less efficient than \kbd{rnfpolredbest}.
+
+ \misctitle{Remark} this function is based on an incomplete reduction
+ theory of lattices over number fields, implemented by \kbd{rnflllgram}, which
+ deserves to be improved.
diff --git a/src/functions/number_fields/rnfpolredabs b/src/functions/number_fields/rnfpolredabs
new file mode 100644
index 0000000..6ff6b20
--- /dev/null
+++ b/src/functions/number_fields/rnfpolredabs
@@ -0,0 +1,42 @@
+Function: rnfpolredabs
+Section: number_fields
+C-Name: rnfpolredabs
+Prototype: GGD0,L,
+Help: rnfpolredabs(nf,pol,{flag=0}): given a pol with coefficients in nf,
+ finds a relative simpler polynomial defining the same field. Binary digits
+ of flag mean: 1: return also the element whose characteristic polynomial is
+ the given polynomial, 2: return an absolute polynomial, 16: partial
+ reduction.
+Doc: THIS FUNCTION IS OBSOLETE: use \tet{rnfpolredbest} instead.
+ Relative version of \kbd{polredabs}. Given a monic polynomial \var{pol}
+ with coefficients in $\var{nf}$, finds a simpler relative polynomial defining
+ the same field. The binary digits of $\fl$ mean
+
+ The binary digits of $\fl$ correspond to $1$: add information to convert
+ elements to the new representation, $2$: absolute polynomial, instead of
+ relative, $16$: possibly use a suborder of the maximal order. More precisely:
+
+ 0: default, return $P$
+
+ 1: returns $[P,a]$ where $P$ is the default output and $a$,
+ a \typ{POLMOD} modulo $P$, is a root of \var{pol}.
+
+ 2: returns \var{Pabs}, an absolute, instead of a relative, polynomial.
+ Same as but faster than
+ \bprog
+   rnfequation(nf, rnfpolredabs(nf,pol))
+ @eprog
+
+ 3: returns $[\var{Pabs},a,b]$, where \var{Pabs} is an absolute polynomial
+ as above, $a$, $b$ are \typ{POLMOD} modulo \var{Pabs}, roots of \kbd{nf.pol}
+ and \var{pol} respectively.
+
+ 16: possibly use a suborder of the maximal order. This is slower than the
+ default when the relative discriminant is smooth, and much faster otherwise.
+ See \secref{se:polredabs}.
+
+ \misctitle{Warning} In the present implementation, \kbd{rnfpolredabs}
+ produces smaller polynomials than \kbd{rnfpolred} and is usually
+ faster, but its complexity is still exponential in the absolute degree.
+ The function \tet{rnfpolredbest} runs in polynomial time, and  tends  to
+ return polynomials with smaller discriminants.
diff --git a/src/functions/number_fields/rnfpolredbest b/src/functions/number_fields/rnfpolredbest
new file mode 100644
index 0000000..a9f4670
--- /dev/null
+++ b/src/functions/number_fields/rnfpolredbest
@@ -0,0 +1,57 @@
+Function: rnfpolredbest
+Section: number_fields
+C-Name: rnfpolredbest
+Prototype: GGD0,L,
+Help: rnfpolredbest(nf,pol,{flag=0}): given a pol with coefficients in nf,
+ finds a relative polynomial P defining the same field, hopefully simpler
+ than pol; flag
+ can be 0: default, 1: return [P,a], where a is a root of pol
+ 2: return an absolute polynomial Pabs, 3:
+ return [Pabs, a,b], where a is a root of nf.pol and b is a root of pol.
+Doc: relative version of \kbd{polredbest}. Given a monic polynomial \var{pol}
+ with coefficients in $\var{nf}$, finds a simpler relative polynomial $P$
+ defining the same field. As opposed to \tet{rnfpolredabs} this function does
+ not return a \emph{smallest} (canonical) polynomial with respect to some
+ measure, but it does run in polynomial time.
+
+ The binary digits of $\fl$ correspond to $1$: add information to convert
+ elements to the new representation, $2$: absolute polynomial, instead of
+ relative. More precisely:
+
+ 0: default, return $P$
+
+ 1: returns $[P,a]$ where $P$ is the default output and $a$,
+ a \typ{POLMOD} modulo $P$, is a root of \var{pol}.
+
+ 2: returns \var{Pabs}, an absolute, instead of a relative, polynomial.
+ Same as but faster than
+ \bprog
+   rnfequation(nf, rnfpolredbest(nf,pol))
+ @eprog
+
+ 3: returns $[\var{Pabs},a,b]$, where \var{Pabs} is an absolute polynomial
+ as above, $a$, $b$ are \typ{POLMOD} modulo \var{Pabs}, roots of \kbd{nf.pol}
+ and \var{pol} respectively.
+
+ \bprog
+ ? K = nfinit(y^3-2); pol = x^2 +x*y + y^2;
+ ? [P, a] = rnfpolredbest(K,pol,1);
+ ? P
+ %3 = x^2 - x + Mod(y - 1, y^3 - 2)
+ ? a
+ %4 = Mod(Mod(2*y^2+3*y+4,y^3-2)*x + Mod(-y^2-2*y-2,y^3-2),
+          x^2 - x + Mod(y-1,y^3-2))
+ ? subst(K.pol,y,a)
+ %5 = 0
+ ? [Pabs, a, b] = rnfpolredbest(K,pol,3);
+ ? Pabs
+ %7 = x^6 - 3*x^5 + 5*x^3 - 3*x + 1
+ ? a
+ %8 = Mod(-x^2+x+1, x^6-3*x^5+5*x^3-3*x+1)
+ ? b
+ %9 = Mod(2*x^5-5*x^4-3*x^3+10*x^2+5*x-5, x^6-3*x^5+5*x^3-3*x+1)
+ ? subst(K.pol,y,a)
+ %10 = 0
+ ? substvec(pol,[x,y],[a,b])
+ %11 = 0
+ @eprog
diff --git a/src/functions/number_fields/rnfpseudobasis b/src/functions/number_fields/rnfpseudobasis
new file mode 100644
index 0000000..4c8bd52
--- /dev/null
+++ b/src/functions/number_fields/rnfpseudobasis
@@ -0,0 +1,16 @@
+Function: rnfpseudobasis
+Section: number_fields
+C-Name: rnfpseudobasis
+Prototype: GG
+Help: rnfpseudobasis(nf,pol): given a pol with coefficients in nf, gives a
+ 4-component vector [A,I,D,d] where [A,I] is a pseudo basis of the maximal
+ order in HNF on the power basis, D is the relative ideal discriminant, and d
+ is the relative discriminant in nf^*/nf*^2.
+Doc: given a number field
+ $\var{nf}$ as output by \kbd{nfinit} and a polynomial \var{pol} with
+ coefficients in $\var{nf}$ defining a relative extension $L$ of $\var{nf}$,
+ computes a pseudo-basis $(A,I)$ for the maximal order $\Z_L$ viewed as a
+ $\Z_K$-module, and the relative discriminant of $L$. This is output as a
+ four-element row vector $[A,I,D,d]$, where $D$ is the relative ideal
+ discriminant and $d$ is the relative discriminant considered as an element of
+ $\var{nf}^*/{\var{nf}^*}^2$.
diff --git a/src/functions/number_fields/rnfsteinitz b/src/functions/number_fields/rnfsteinitz
new file mode 100644
index 0000000..20ea7c4
--- /dev/null
+++ b/src/functions/number_fields/rnfsteinitz
@@ -0,0 +1,17 @@
+Function: rnfsteinitz
+Section: number_fields
+C-Name: rnfsteinitz
+Prototype: GG
+Help: rnfsteinitz(nf,x): given an order x as output by rnfpseudobasis,
+ gives [A,I,D,d] where (A,I) is a pseudo basis where all the ideals except
+ perhaps the last are trivial.
+Doc: given a number field $\var{nf}$ as
+ output by \kbd{nfinit} and either a polynomial $x$ with coefficients in
+ $\var{nf}$ defining a relative extension $L$ of $\var{nf}$, or a pseudo-basis
+ $x$ of such an extension as output for example by \kbd{rnfpseudobasis},
+ computes another pseudo-basis $(A,I)$ (not in HNF in general) such that all
+ the ideals of $I$ except perhaps the last one are equal to the ring of
+ integers of $\var{nf}$, and outputs the four-component row vector $[A,I,D,d]$
+ as in \kbd{rnfpseudobasis}. The name of this function comes from the fact
+ that the ideal class of the last ideal of $I$, which is well defined, is the
+ \idx{Steinitz class} of the $\Z_K$-module $\Z_L$ (its image in $SK_0(\Z_K)$).
diff --git a/src/functions/number_fields/subgrouplist b/src/functions/number_fields/subgrouplist
new file mode 100644
index 0000000..3369cef
--- /dev/null
+++ b/src/functions/number_fields/subgrouplist
@@ -0,0 +1,41 @@
+Function: subgrouplist
+Section: number_fields
+C-Name: subgrouplist0
+Prototype: GDGD0,L,
+Help: subgrouplist(bnr,{bound},{flag=0}): bnr being as output by bnrinit or
+ a list of cyclic components of a finite Abelian group G, outputs the list of
+ subgroups of G (of index bounded by bound, if not omitted), given as HNF
+ left divisors of the SNF matrix corresponding to G. If flag=0 (default) and
+ bnr is as output by bnrinit, gives only the subgroups for which the modulus
+ is the conductor.
+Doc: \var{bnr} being as output by \kbd{bnrinit} or a list of cyclic components
+ of a finite Abelian group $G$, outputs the list of subgroups of $G$. Subgroups
+ are given as HNF left divisors of the SNF matrix corresponding to $G$.
+
+ If $\fl=0$ (default) and \var{bnr} is as output by \kbd{bnrinit}, gives
+ only the subgroups whose modulus is the conductor. Otherwise, the modulus is
+ not taken into account.
+
+ If \var{bound} is present, and is a positive integer, restrict the output to
+ subgroups of index less than \var{bound}. If \var{bound} is a vector
+ containing a single positive integer $B$, then only subgroups of index
+ exactly equal to $B$ are computed. For instance
+ \bprog
+ ? subgrouplist([6,2])
+ %1 = [[6, 0; 0, 2], [2, 0; 0, 2], [6, 3; 0, 1], [2, 1; 0, 1], [3, 0; 0, 2],
+ [1, 0; 0, 2], [6, 0; 0, 1], [2, 0; 0, 1], [3, 0; 0, 1], [1, 0; 0, 1]]
+ ? subgrouplist([6,2],3)    \\@com index less than 3
+ %2 = [[2, 1; 0, 1], [1, 0; 0, 2], [2, 0; 0, 1], [3, 0; 0, 1], [1, 0; 0, 1]]
+ ? subgrouplist([6,2],[3])  \\@com index 3
+ %3 = [[3, 0; 0, 1]]
+ ? bnr = bnrinit(bnfinit(x), [120,[1]], 1);
+ ? L = subgrouplist(bnr, [8]);
+ @eprog\noindent
+ In the last example, $L$ corresponds to the 24 subfields of
+ $\Q(\zeta_{120})$, of degree $8$ and conductor $120\infty$ (by setting \fl,
+ we see there are a total of $43$ subgroups of degree $8$).
+ \bprog
+ ? vector(#L, i, galoissubcyclo(bnr, L[i]))
+ @eprog\noindent
+ will produce their equations. (For a general base field, you would
+ have to rely on \tet{bnrstark}, or \tet{rnfkummer}.)
diff --git a/src/functions/number_fields/zetak b/src/functions/number_fields/zetak
new file mode 100644
index 0000000..ed34f70
--- /dev/null
+++ b/src/functions/number_fields/zetak
@@ -0,0 +1,54 @@
+Function: zetak
+Section: number_fields
+C-Name: gzetakall
+Prototype: GGD0,L,p
+Help: zetak(nfz,x,{flag=0}): Dedekind zeta function of the number field nfz
+ at x, where nfz is the vector computed by zetakinit (NOT by nfinit); flag is
+ optional, and can be 0: default, compute zetak, or non-zero: compute the
+ lambdak function, i.e. with the gamma factors.
+Doc: \var{znf} being a number
+ field initialized by \kbd{zetakinit} (\emph{not} by \kbd{nfinit}),
+ computes the value of the \idx{Dedekind} zeta function of the number
+ field at the complex number $x$. If $\fl=1$ computes Dedekind $\Lambda$
+ function instead (i.e.~the product of the Dedekind zeta function by its gamma
+ and exponential factors).
+
+ \misctitle{CAVEAT} This implementation is not satisfactory and must be
+ rewritten. In particular
+
+ \item The accuracy of the result depends in an essential way on the
+ accuracy of both the \kbd{zetakinit} program and the current accuracy.
+ Be wary in particular that $x$ of large imaginary part or, on the
+ contrary, very close to an ordinary integer will suffer from precision
+ loss, yielding fewer significant digits than expected. Computing with 28
+ digits of relative accuracy, we have
+ \bprog
+ ? zeta(3)
+ %1 = 1.202056903159594285399738161
+ ? zeta(3-1e-20)
+ %2 = 1.202056903159594285401719424
+ ? zetak(zetakinit(x), 3-1e-20)
+ %3 = 1.2020569031595952919  \\ 5 digits are wrong
+ ? zetak(zetakinit(x), 3-1e-28)
+ %4 = -25.33411749           \\ junk
+ @eprog
+
+ \item As the precision increases, results become unexpectedly
+ completely wrong:
+ \bprog
+ ? \p100
+ ? zetak(zetakinit(x^2-5), -1) - 1/30
+ %1 = 7.26691813 E-108    \\ perfect
+ ? \p150
+ ? zetak(zetakinit(x^2-5), -1) - 1/30
+ %2 = -2.486113578 E-156  \\ perfect
+ ? \p200
+ ? zetak(zetakinit(x^2-5), -1) - 1/30
+ %3 = 4.47... E-75        \\ more than half of the digits are wrong
+ ? \p250
+ ? zetak(zetakinit(x^2-5), -1) - 1/30
+ %4 = 1.6 E43             \\ junk
+ @eprog
+
+Variant: See also \fun{GEN}{glambdak}{GEN znf, GEN x, long prec} or
+ \fun{GEN}{gzetak}{GEN znf, GEN x, long prec}.
diff --git a/src/functions/number_fields/zetakinit b/src/functions/number_fields/zetakinit
new file mode 100644
index 0000000..067d1e2
--- /dev/null
+++ b/src/functions/number_fields/zetakinit
@@ -0,0 +1,28 @@
+Function: zetakinit
+Section: number_fields
+C-Name: initzeta
+Prototype: Gp
+Help: zetakinit(bnf): compute number field information necessary to use zetak.
+ bnf may also be an irreducible polynomial.
+Doc: computes a number of initialization data
+ concerning the number field associated to \kbd{bnf} so as to be able
+ to compute the \idx{Dedekind} zeta and lambda functions, respectively
+ $\kbd{zetak}(x)$ and $\kbd{zetak}(x,1)$, at the current real precision. If
+ you do not need the \kbd{bnfinit} data somewhere else, you may call it
+ with an irreducible polynomial instead of a \var{bnf}: it will call
+ \kbd{bnfinit} itself.
+
+ The result is a 9-component vector $v$ whose components are very technical
+ and cannot really be used except through the \kbd{zetak} function.
+
+ This function is very inefficient and should be rewritten. It needs to
+ computes millions of coefficients of the corresponding Dirichlet series if
+ the precision is big. Unless the discriminant is small it will not be able
+ to handle more than 9 digits of relative precision. For instance,
+ \kbd{zetakinit(x\pow 8 - 2)} needs 440MB of memory at default precision.
+
+ This function will fail with the message
+ \bprog
+  *** bnrL1: overflow in zeta_get_N0 [need too many primes].
+ @eprog\noindent if the approximate functional equation requires us to sum
+ too many terms (if the discriminant of the number field is too large).
diff --git a/src/functions/number_theoretical/addprimes b/src/functions/number_theoretical/addprimes
new file mode 100644
index 0000000..326b7a7
--- /dev/null
+++ b/src/functions/number_theoretical/addprimes
@@ -0,0 +1,17 @@
+Function: addprimes
+Section: number_theoretical
+C-Name: addprimes
+Prototype: DG
+Help: addprimes({x=[]}): add primes in the vector x to the prime table to
+ be used in trial division. x may also be a single integer. Composite
+ "primes" are NOT allowed!
+Doc: adds the integers contained in the
+ vector $x$ (or the single integer $x$) to a special table of
+ ``user-defined primes'', and returns that table. Whenever \kbd{factor} is
+ subsequently called, it will trial divide by the elements in this table.
+ If $x$ is empty or omitted, just returns the current list of extra
+ primes.
+
+ The entries in $x$ must be primes: there is no internal check, even if
+ the \tet{factor_proven} default is set. To remove primes from the list use
+ \kbd{removeprimes}.
diff --git a/src/functions/number_theoretical/bestappr b/src/functions/number_theoretical/bestappr
new file mode 100644
index 0000000..7874a24
--- /dev/null
+++ b/src/functions/number_theoretical/bestappr
@@ -0,0 +1,50 @@
+Function: bestappr
+Section: number_theoretical
+C-Name: bestappr
+Prototype: GDG
+Help: bestappr(x, {B}): returns a rational approximation to x, whose
+ denominator is limited by B, if present. This function applies to reals,
+ intmods, p-adics, and rationals of course. Otherwise it applies recursively
+ to all components.
+
+Doc: using variants of the extended Euclidean algorithm, returns a rational
+ approximation $a/b$ to $x$, whose denominator is limited
+ by $B$, if present. If $B$ is omitted, return the best approximation
+ affordable given the input accuracy; if you are looking for true rational
+ numbers, presumably approximated to sufficient accuracy, you should first
+ try that option. Otherwise, $B$ must be a positive real scalar (impose
+ $0 < b \leq B$).
+
+ \item If $x$ is a \typ{REAL} or a \typ{FRAC}, this function uses continued
+ fractions.
+ \bprog
+ ? bestappr(Pi, 100)
+ %1 = 22/7
+ ? bestappr(0.1428571428571428571428571429)
+ %2 = 1/7
+ ? bestappr([Pi, sqrt(2) + 'x], 10^3)
+ %3 = [355/113, x + 1393/985]
+ @eprog
+ By definition, $a/b$ is the best rational approximation to $x$ if
+ $|b x - a| < |v x - u|$ for all integers $(u,v)$ with $0 < v \leq B$.
+ (Which implies that $n/d$ is a convergent of the continued fraction of $x$.)
+
+ \item If $x$ is a \typ{INTMOD} modulo $N$ or a \typ{PADIC} of precision $N =
+ p^k$, this function performs rational modular reconstruction modulo $N$. The
+ routine then returns the unique rational number $a/b$ in coprime integers
+ $|a| < N/2B$ and $b\leq B$ which is congruent to $x$ modulo $N$. Omitting
+ $B$ amounts to choosing it of the order of $\sqrt{N/2}$. If rational
+ reconstruction is not possible (no suitable $a/b$ exists), returns $[]$.
+ \bprog
+ ? bestappr(Mod(18526731858, 11^10))
+ %1 = 1/7
+ ? bestappr(Mod(18526731858, 11^20))
+ %2 = []
+ ? bestappr(3 + 5 + 3*5^2 + 5^3 + 3*5^4 + 5^5 + 3*5^6 + O(5^7))
+ %2 = -1/3
+ @eprog\noindent In most concrete uses, $B$ is a prime power and we performed
+ Hensel lifting to obtain $x$.
+
+ The function applies recursively to components of complex objects
+ (polynomials, vectors, \dots). If rational reconstruction fails for even a
+ single entry, return $[]$.
diff --git a/src/functions/number_theoretical/bestapprPade b/src/functions/number_theoretical/bestapprPade
new file mode 100644
index 0000000..39f6114
--- /dev/null
+++ b/src/functions/number_theoretical/bestapprPade
@@ -0,0 +1,43 @@
+Function: bestapprPade
+Section: number_theoretical
+C-Name: bestapprPade
+Prototype: GD-1,L,
+Help: bestappr(x, {B}): returns a rational function approximation to x.
+ This function applies to series, polmods, and rational functions of course.
+ Otherwise it applies recursively to all components.
+Doc: using variants of the extended Euclidean algorithm, returns a rational
+ function approximation $a/b$ to $x$, whose denominator is limited
+ by $B$, if present. If $B$ is omitted, return the best approximation
+ affordable given the input accuracy; if you are looking for true rational
+ functions, presumably approximated to sufficient accuracy, you should first
+ try that option. Otherwise, $B$ must be a non-negative real (impose
+ $0 \leq \text{degree}(b) \leq B$).
+
+ \item If $x$ is a \typ{RFRAC} or \typ{SER}, this function uses continued
+ fractions.
+ \bprog
+ ? bestapprPade((1-x^11)/(1-x)+O(x^11))
+ %1 = 1/(-x + 1)
+ ? bestapprPade([1/(1+x+O(x^10)), (x^3-2)/(x^3+1)], 1)
+ %2 =  [1/(x + 1), -2]
+ @eprog
+
+ \item If $x$ is a \typ{POLMOD} modulo $N$ or a \typ{SER} of precision $N =
+ t^k$, this function performs rational modular reconstruction modulo $N$. The
+ routine then returns the unique rational function $a/b$ in coprime
+ polynomials, with $\text{degree}(b)\leq B$ which is congruent to $x$ modulo
+ $N$. Omitting $B$ amounts to choosing it of the order of $N/2$. If rational
+ reconstruction is not possible (no suitable $a/b$ exists), returns $[]$.
+ \bprog
+ ? bestapprPade(Mod(1+x+x^2+x^3+x^4, x^4-2))
+ %1 = (2*x - 1)/(x - 1)
+ ? % * Mod(1,x^4-2)
+ %2 = Mod(x^3 + x^2 + x + 3, x^4 - 2)
+ ? bestapprPade(Mod(1+x+x^2+x^3+x^5, x^9))
+ %2 = []
+ ? bestapprPade(Mod(1+x+x^2+x^3+x^5, x^10))
+ %3 = (2*x^4 + x^3 - x - 1)/(-x^5 + x^3 + x^2 - 1)
+ @eprog\noindent
+ The function applies recursively to components of complex objects
+ (polynomials, vectors, \dots). If rational reconstruction fails for even a
+ single entry, return $[]$.
diff --git a/src/functions/number_theoretical/bezout b/src/functions/number_theoretical/bezout
new file mode 100644
index 0000000..4b6b06e
--- /dev/null
+++ b/src/functions/number_theoretical/bezout
@@ -0,0 +1,6 @@
+Function: bezout
+Section: number_theoretical
+C-Name: gcdext0
+Prototype: GG
+Help: bezout(x,y): deprecated alias for gcdext
+Doc: deprecated alias for \kbd{gcdext}
diff --git a/src/functions/number_theoretical/bigomega b/src/functions/number_theoretical/bigomega
new file mode 100644
index 0000000..64f575c
--- /dev/null
+++ b/src/functions/number_theoretical/bigomega
@@ -0,0 +1,19 @@
+Function: bigomega
+Section: number_theoretical
+C-Name: bigomega
+Prototype: lG
+Help: bigomega(x): number of prime divisors of x, counted with multiplicity.
+Doc: number of prime divisors of the integer $|x|$ counted with
+ multiplicity:
+ \bprog
+ ? factor(392)
+ %1 =
+ [2 3]
+
+ [7 2]
+
+ ? bigomega(392)
+ %2 = 5;  \\ = 3+2
+ ? omega(392)
+ %3 = 2;  \\ without multiplicity
+ @eprog
diff --git a/src/functions/number_theoretical/binomial b/src/functions/number_theoretical/binomial
new file mode 100644
index 0000000..661ca3b
--- /dev/null
+++ b/src/functions/number_theoretical/binomial
@@ -0,0 +1,13 @@
+Function: binomial
+Section: number_theoretical
+C-Name: binomial
+Prototype: GL
+Help: binomial(x,y): binomial coefficient x*(x-1)...*(x-y+1)/y! defined for
+ y in Z and any x.
+Doc: \idx{binomial coefficient} $\binom{x}{y}$.
+ Here $y$ must be an integer, but $x$ can be any PARI object.
+Variant: The function
+ \fun{GEN}{binomialuu}{ulong n, ulong k} is also available, and so is
+ \fun{GEN}{vecbinome}{long n}, which returns a vector $v$
+ with $n+1$ components such that $v[k+1] = \kbd{binomial}(n,k)$ for $k$ from
+ $0$ up to $n$.
diff --git a/src/functions/number_theoretical/chinese b/src/functions/number_theoretical/chinese
new file mode 100644
index 0000000..c6671ed
--- /dev/null
+++ b/src/functions/number_theoretical/chinese
@@ -0,0 +1,48 @@
+Function: chinese
+Section: number_theoretical
+C-Name: chinese
+Prototype: GDG
+Help: chinese(x,{y}): x,y being both intmods (or polmods) computes z in the
+ same residue classes as x and y.
+Description:
+ (gen):gen      chinese1($1)
+ (gen, gen):gen chinese($1, $2)
+Doc: if $x$ and $y$ are both intmods or both polmods, creates (with the same
+ type) a $z$ in the same residue class as $x$ and in the same residue class as
+ $y$, if it is possible.
+ \bprog
+ ? chinese(Mod(1,2), Mod(2,3))
+ %1 = Mod(5, 6)
+ ? chinese(Mod(x,x^2-1), Mod(x+1,x^2+1))
+ %2 = Mod(-1/2*x^2 + x + 1/2, x^4 - 1)
+ @eprog\noindent
+ This function also allows vector and matrix arguments, in which case the
+ operation is recursively applied to each component of the vector or matrix.
+ \bprog
+ ? chinese([Mod(1,2),Mod(1,3)], [Mod(1,5),Mod(2,7)])
+ %3 = [Mod(1, 10), Mod(16, 21)]
+ @eprog\noindent
+ For polynomial arguments in the same variable, the function is applied to each
+ coefficient; if the polynomials have different degrees, the high degree terms
+ are copied verbatim in the result, as if the missing high degree terms in the
+ polynomial of lowest degree had been \kbd{Mod(0,1)}. Since the latter
+ behavior is usually \emph{not} the desired one, we propose to convert the
+ polynomials to vectors of the same length first:
+ \bprog
+  ? P = x+1; Q = x^2+2*x+1;
+  ? chinese(P*Mod(1,2), Q*Mod(1,3))
+  %4 = Mod(1, 3)*x^2 + Mod(5, 6)*x + Mod(3, 6)
+  ? chinese(Vec(P,3)*Mod(1,2), Vec(Q,3)*Mod(1,3))
+  %5 = [Mod(1, 6), Mod(5, 6), Mod(4, 6)]
+  ? Pol(%)
+  %6 = Mod(1, 6)*x^2 + Mod(5, 6)*x + Mod(4, 6)
+ @eprog
+
+ If $y$ is omitted, and $x$ is a vector, \kbd{chinese} is applied recursively
+ to the components of $x$, yielding a residue belonging to the same class as all
+ components of $x$.
+
+ Finally $\kbd{chinese}(x,x) = x$ regardless of the type of $x$; this allows
+ vector arguments to contain other data, so long as they are identical in both
+ vectors.
+Variant: \fun{GEN}{chinese1}{GEN x} is also available.
diff --git a/src/functions/number_theoretical/content b/src/functions/number_theoretical/content
new file mode 100644
index 0000000..a7e1e44
--- /dev/null
+++ b/src/functions/number_theoretical/content
@@ -0,0 +1,27 @@
+Function: content
+Section: number_theoretical
+C-Name: content
+Prototype: G
+Help: content(x): gcd of all the components of x, when this makes sense.
+Doc: computes the gcd of all the coefficients of $x$,
+ when this gcd makes sense. This is the natural definition
+ if $x$ is a polynomial (and by extension a power series) or a
+ vector/matrix. This is in general a weaker notion than the \emph{ideal}
+ generated by the coefficients:
+ \bprog
+ ? content(2*x+y)
+ %1 = 1            \\ = gcd(2,y) over Q[y]
+ @eprog
+
+ If $x$ is a scalar, this simply returns the absolute value of $x$ if $x$ is
+ rational (\typ{INT} or \typ{FRAC}), and either $1$ (inexact input) or $x$
+ (exact input) otherwise; the result should be identical to \kbd{gcd(x, 0)}.
+
+ The content of a rational function is the ratio of the contents of the
+ numerator and the denominator. In recursive structures, if a
+ matrix or vector \emph{coefficient} $x$ appears, the gcd is taken
+ not with $x$, but with its content:
+ \bprog
+ ? content([ [2], 4*matid(3) ])
+ %1 = 2
+ @eprog
diff --git a/src/functions/number_theoretical/contfrac b/src/functions/number_theoretical/contfrac
new file mode 100644
index 0000000..923ea57
--- /dev/null
+++ b/src/functions/number_theoretical/contfrac
@@ -0,0 +1,72 @@
+Function: contfrac
+Section: number_theoretical
+C-Name: contfrac0
+Prototype: GDGD0,L,
+Help: contfrac(x,{b},{nmax}): continued fraction expansion of x (x
+ rational,real or rational function). b and nmax are both optional, where b
+ is the vector of numerators of the continued fraction, and nmax is a bound
+ for the number of terms in the continued fraction expansion.
+Doc: returns the row vector whose components are the partial quotients of the
+ \idx{continued fraction} expansion of $x$. In other words, a result
+ $[a_0,\dots,a_n]$ means that $x \approx a_0+1/(a_1+\dots+1/a_n)$. The
+ output is normalized so that $a_n \neq 1$ (unless we also have $n = 0$).
+
+ The number of partial quotients $n+1$ is limited by \kbd{nmax}. If
+ \kbd{nmax} is omitted, the expansion stops at the last significant partial
+ quotient.
+ \bprog
+ ? \p19
+   realprecision = 19 significant digits
+ ? contfrac(Pi)
+ %1 = [3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2]
+ ? contfrac(Pi,, 3)  \\ n = 2
+ %2 = [3, 7, 15]
+ @eprog\noindent
+ $x$ can also be a rational function or a power series.
+
+ If a vector $b$ is supplied, the numerators are equal to the coefficients
+ of $b$, instead of all equal to $1$ as above; more precisely, $x \approx
+ (1/b_0)(a_0+b_1/(a_1+\dots+b_n/a_n))$; for a numerical continued fraction
+ ($x$ real), the $a_i$ are integers, as large as possible; if $x$ is a
+ rational function, they are polynomials with $\deg a_i = \deg b_i + 1$.
+ The length of the result is then equal to the length of $b$, unless the next
+ partial quotient cannot be reliably computed, in which case the expansion
+ stops. This happens when a partial remainder is equal to zero (or too small
+ compared to the available significant digits for $x$ a \typ{REAL}).
+
+ A direct implementation of the numerical continued fraction
+ \kbd{contfrac(x,b)} described above would be
+ \bprog
+ \\ "greedy" generalized continued fraction
+ cf(x, b) =
+ { my( a= vector(#b), t );
+
+   x *= b[1];
+   for (i = 1, #b,
+     a[i] = floor(x);
+     t = x - a[i]; if (!t || i == #b, break);
+     x = b[i+1] / t;
+   ); a;
+ }
+ @eprog\noindent There is some degree of freedom when choosing the $a_i$; the
+ program above can easily be modified to derive variants of the standard
+ algorithm. In the same vein, although no builtin
+ function implements the related \idx{Engel expansion} (a special kind of
+ \idx{Egyptian fraction} decomposition: $x = 1/a_1 + 1/(a_1a_2) + \dots$ ),
+ it can be obtained as follows:
+ \bprog
+ \\ n terms of the Engel expansion of x
+ engel(x, n = 10) =
+ { my( u = x, a = vector(n) );
+   for (k = 1, n,
+     a[k] = ceil(1/u);
+     u = u*a[k] - 1;
+     if (!u, break);
+   ); a
+ }
+ @eprog
+
+ \misctitle{Obsolete hack} (don't use this): If $b$ is an integer, \var{nmax}
+ is ignored and the command is understood as \kbd{contfrac($x,, b$)}.
+Variant: Also available are \fun{GEN}{gboundcf}{GEN x, long nmax},
+ \fun{GEN}{gcf}{GEN x} and \fun{GEN}{gcf2}{GEN b, GEN x}.
diff --git a/src/functions/number_theoretical/contfracpnqn b/src/functions/number_theoretical/contfracpnqn
new file mode 100644
index 0000000..902f458
--- /dev/null
+++ b/src/functions/number_theoretical/contfracpnqn
@@ -0,0 +1,37 @@
+Function: contfracpnqn
+Section: number_theoretical
+C-Name: contfracpnqn
+Prototype: GD-1,L,
+Help: contfracpnqn(x, {n=-1}): [p_n,p_{n-1}; q_n,q_{n-1}] corresponding to the
+ continued fraction x. If n >= 0 is present, returns all convergents from
+ p_0/q_0 up to p_n/q_n.
+Doc: when $x$ is a vector or a one-row matrix, $x$
+ is considered as the list of partial quotients $[a_0,a_1,\dots,a_n]$ of a
+ rational number, and the result is the 2 by 2 matrix
+ $[p_n,p_{n-1};q_n,q_{n-1}]$ in the standard notation of continued fractions,
+ so $p_n/q_n=a_0+1/(a_1+\dots+1/a_n)$. If $x$ is a matrix with two rows
+ $[b_0,b_1,\dots,b_n]$ and $[a_0,a_1,\dots,a_n]$, this is then considered as a
+ generalized continued fraction and we have similarly
+ $p_n/q_n=(1/b_0)(a_0+b_1/(a_1+\dots+b_n/a_n))$. Note that in this case one
+ usually has $b_0=1$.
+
+ If $n \geq 0$ is present, returns all convergents from $p_0/q_0$ up to
+ $p_n/q_n$. (All convergents if $x$ is too small to compute the $n+1$
+ requested convergents.)
+ \bprog
+ ? a=contfrac(Pi,20)
+ %1 = [3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2]
+ ? contfracpnqn(a,3)
+ %2 =
+ [3 22 333 355]
+
+ [1  7 106 113]
+
+ ? contfracpnqn(a,7)
+ %3 =
+ [3 22 333 355 103993 104348 208341 312689]
+
+ [1  7 106 113  33102  33215  66317  99532]
+ @eprog
+
+Variant: also available is \fun{GEN}{pnqn}{GEN x} for $n = -1$.
diff --git a/src/functions/number_theoretical/core b/src/functions/number_theoretical/core
new file mode 100644
index 0000000..d39f96e
--- /dev/null
+++ b/src/functions/number_theoretical/core
@@ -0,0 +1,15 @@
+Function: core
+Section: number_theoretical
+C-Name: core0
+Prototype: GD0,L,
+Help: core(n,{flag=0}): unique squarefree integer d
+ dividing n such that n/d is a square. If (optional) flag is non-null, output
+ the two-component row vector [d,f], where d is the unique squarefree integer
+ dividing n such that n/d=f^2 is a square.
+Doc: if $n$ is an integer written as
+ $n=df^2$ with $d$ squarefree, returns $d$. If $\fl$ is non-zero,
+ returns the two-element row vector $[d,f]$. By convention, we write $0 = 0
+ \times 1^2$, so \kbd{core(0, 1)} returns $[0,1]$.
+
+Variant: Also available are \fun{GEN}{core}{GEN n} ($\fl = 0$) and
+ \fun{GEN}{core2}{GEN n} ($\fl = 1$)
diff --git a/src/functions/number_theoretical/coredisc b/src/functions/number_theoretical/coredisc
new file mode 100644
index 0000000..c20c538
--- /dev/null
+++ b/src/functions/number_theoretical/coredisc
@@ -0,0 +1,22 @@
+Function: coredisc
+Section: number_theoretical
+C-Name: coredisc0
+Prototype: GD0,L,
+Help: coredisc(n,{flag=0}): discriminant of the quadratic field Q(sqrt(n)).
+ If (optional) flag is non-null, output a two-component row vector [d,f],
+ where d is the discriminant of the quadratic field Q(sqrt(n)) and n=df^2. f
+ may be a half integer.
+Doc: a \emph{fundamental discriminant} is an integer of the form $t\equiv 1
+ \mod 4$ or $4t \equiv 8,12 \mod 16$, with $t$ squarefree (i.e.~$1$ or the
+ discriminant of a quadratic number field). Given a non-zero integer
+ $n$, this routine returns the (unique) fundamental discriminant $d$
+ such that $n=df^2$, $f$ a positive rational number. If $\fl$ is non-zero,
+ returns the two-element row vector $[d,f]$. If $n$ is congruent to
+ 0 or 1 modulo 4, $f$ is an integer, and a half-integer otherwise.
+
+ By convention, \kbd{coredisc(0, 1))} returns $[0,1]$.
+
+ Note that \tet{quaddisc}$(n)$ returns the same value as \kbd{coredisc}$(n)$,
+ and also works with rational inputs $n\in\Q^*$.
+Variant: Also available are \fun{GEN}{coredisc}{GEN n} ($\fl = 0$) and
+ \fun{GEN}{coredisc2}{GEN n} ($\fl = 1$)
diff --git a/src/functions/number_theoretical/dirdiv b/src/functions/number_theoretical/dirdiv
new file mode 100644
index 0000000..e925fa6
--- /dev/null
+++ b/src/functions/number_theoretical/dirdiv
@@ -0,0 +1,9 @@
+Function: dirdiv
+Section: number_theoretical
+C-Name: dirdiv
+Prototype: GG
+Help: dirdiv(x,y): division of the Dirichlet series x by the Dirichlet
+ series y.
+Doc: $x$ and $y$ being vectors of perhaps different
+ lengths but with $y[1]\neq 0$ considered as \idx{Dirichlet series}, computes
+ the quotient of $x$ by $y$, again as a vector.
diff --git a/src/functions/number_theoretical/direuler b/src/functions/number_theoretical/direuler
new file mode 100644
index 0000000..3aff952
--- /dev/null
+++ b/src/functions/number_theoretical/direuler
@@ -0,0 +1,27 @@
+Function: direuler
+Section: number_theoretical
+C-Name: direuler0
+Prototype: V=GGEDG
+Help: direuler(p=a,b,expr,{c}): Dirichlet Euler product of expression expr
+ from p=a to p=b, limited to b terms. Expr should be a polynomial or rational
+ function in p and X, and X is understood to mean p^(-s). If c is present,
+ output only the first c terms.
+Wrapper: (,,G)
+Description:
+  (gen,gen,closure,?gen):gen direuler(${3 cookie}, ${3 wrapper}, $1, $2, $4)
+Doc: computes the \idx{Dirichlet series} associated to the
+ \idx{Euler product} of expression \var{expr} as $p$ ranges through the primes
+ from $a$
+ to $b$. \var{expr} must be a polynomial or rational function in another
+ variable than $p$ (say $X$) and $\var{expr}(X)$ is understood as the local
+ factor $\var{expr}(p^{-s})$.
+
+ The series is output as a vector of coefficients. If $c$ is present, output
+ only the first $c$ coefficients in the series. The following command computes
+ the \teb{sigma} function, associated to $\zeta(s)\zeta(s-1)$:
+ \bprog
+ ? direuler(p=2, 10, 1/((1-X)*(1-p*X)))
+ %1 = [1, 3, 4, 7, 6, 12, 8, 15, 13, 18]
+ @eprog
+
+ \synt{direuler}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b}
diff --git a/src/functions/number_theoretical/dirmul b/src/functions/number_theoretical/dirmul
new file mode 100644
index 0000000..ad206c8
--- /dev/null
+++ b/src/functions/number_theoretical/dirmul
@@ -0,0 +1,20 @@
+Function: dirmul
+Section: number_theoretical
+C-Name: dirmul
+Prototype: GG
+Help: dirmul(x,y): multiplication of the Dirichlet series x by the Dirichlet
+ series y.
+Doc: $x$ and $y$ being vectors of perhaps different lengths representing
+ the \idx{Dirichlet series} $\sum_n x_n n^{-s}$ and $\sum_n y_n n^{-s}$,
+ computes the product of $x$ by $y$, again as a vector.
+ \bprog
+ ? dirmul(vector(10,n,1), vector(10,n,moebius(n)))
+ %1 = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+ @eprog\noindent
+ The product
+ length is the minimum of $\kbd{\#}x\kbd{*}v(y)$ and $\kbd{\#}y\kbd{*}v(x)$,
+ where $v(x)$ is the index of the first non-zero coefficient.
+ \bprog
+ ? dirmul([0,1], [0,1]);
+ %2 = [0, 0, 0, 1]
+ @eprog
diff --git a/src/functions/number_theoretical/divisors b/src/functions/number_theoretical/divisors
new file mode 100644
index 0000000..2089d2f
--- /dev/null
+++ b/src/functions/number_theoretical/divisors
@@ -0,0 +1,16 @@
+Function: divisors
+Section: number_theoretical
+C-Name: divisors
+Prototype: G
+Help: divisors(x): gives a vector formed by the divisors of x in increasing
+ order.
+Description:
+ (gen):vec        divisors($1)
+Doc: creates a row vector whose components are the
+ divisors of $x$. The factorization of $x$ (as output by \tet{factor}) can
+ be used instead.
+
+ By definition, these divisors are the products of the irreducible
+ factors of $n$, as produced by \kbd{factor(n)}, raised to appropriate
+ powers (no negative exponent may occur in the factorization). If $n$ is
+ an integer, they are the positive divisors, in increasing order.
diff --git a/src/functions/number_theoretical/eulerphi b/src/functions/number_theoretical/eulerphi
new file mode 100644
index 0000000..d36df6a
--- /dev/null
+++ b/src/functions/number_theoretical/eulerphi
@@ -0,0 +1,16 @@
+Function: eulerphi
+Section: number_theoretical
+C-Name: eulerphi
+Prototype: G
+Help: eulerphi(x): Euler's totient function of x.
+Description:
+ (gen):int        eulerphi($1)
+Doc: Euler's $\phi$ (totient)\sidx{Euler totient function} function of the
+ integer $|x|$, in other words $|(\Z/x\Z)^*|$.
+ \bprog
+ ? eulerphi(40)
+ %1 = 16
+ @eprog\noindent
+ According to this definition we let $\phi(0) := 2$, since $\Z^* = \{-1,1\}$;
+ this is consistent with \kbd{znstar(0)}: we have \kbd{znstar$(n)$.no =
+ eulerphi(n)} for all $n\in\Z$.
diff --git a/src/functions/number_theoretical/factor b/src/functions/number_theoretical/factor
new file mode 100644
index 0000000..99c4eb4
--- /dev/null
+++ b/src/functions/number_theoretical/factor
@@ -0,0 +1,168 @@
+Function: factor
+Section: number_theoretical
+C-Name: gp_factor0
+Prototype: GDG
+Help: factor(x,{lim}): factorization of x. lim is optional and can be set
+ whenever x is of (possibly recursive) rational type. If lim is set return
+ partial factorization, using primes < lim.
+Description:
+ (int, ?-1):vec        Z_factor($1)
+ (gen, ?-1):vec        factor($1)
+ (gen, small):vec      factor0($1, $2)
+Doc: general factorization function, where $x$ is a
+ rational (including integers), a complex number with rational
+ real and imaginary parts, or a rational function (including polynomials).
+ The result is a two-column matrix: the first contains the irreducibles
+ dividing $x$ (rational or Gaussian primes, irreducible polynomials),
+ and the second the exponents. By convention, $0$ is factored as $0^1$.
+
+ \misctitle{$\Q$ and $\Q(i)$}
+ See \tet{factorint} for more information about the algorithms used.
+ The rational or Gaussian primes are in fact \var{pseudoprimes}
+ (see \kbd{ispseudoprime}), a priori not rigorously proven primes. In fact,
+ any factor which is $\leq 2^{64}$ (whose norm is $\leq 2^{64}$ for an
+ irrational Gaussian prime) is a genuine prime. Use \kbd{isprime} to prove
+ primality of other factors, as in
+ \bprog
+ ? fa = factor(2^2^7 + 1)
+ %1 =
+ [59649589127497217 1]
+
+ [5704689200685129054721 1]
+
+ ? isprime( fa[,1] )
+ %2 = [1, 1]~   \\ both entries are proven primes
+ @eprog\noindent
+ Another possibility is to set the global default \tet{factor_proven}, which
+ will perform a rigorous primality proof for each pseudoprime factor.
+
+ A \typ{INT} argument \var{lim} can be added, meaning that we look only for
+ prime factors $p < \var{lim}$. The limit \var{lim} must be non-negative.
+ In this case, all but the last factor are proven primes, but the remaining
+ factor may actually be a proven composite! If the remaining factor is less
+ than $\var{lim}^2$, then it is prime.
+ \bprog
+ ? factor(2^2^7 +1, 10^5)
+ %3 =
+ [340282366920938463463374607431768211457 1]
+ @eprog\noindent
+ \misctitle{Deprecated feature} Setting $\var{lim}=0$ is the same
+ as setting it to $\kbd{primelimit} + 1$. Don't use this: it is unwise to
+ rely on global variables when you can specify an explicit argument.
+ \smallskip
+
+ This routine uses trial division and perfect power tests, and should not be
+ used for huge values of \var{lim} (at most $10^9$, say):
+ \kbd{factorint(, 1 + 8)} will in general be faster. The latter does not
+ guarantee that all small
+ prime factors are found, but it also finds larger factors, and in a much more
+ efficient way.
+ \bprog
+ ? F = (2^2^7 + 1) * 1009 * 100003; factor(F, 10^5)  \\ fast, incomplete
+ time = 0 ms.
+ %4 =
+ [1009 1]
+
+ [34029257539194609161727850866999116450334371 1]
+
+ ? factor(F, 10^9)    \\ very slow
+ time = 6,892 ms.
+ %6 =
+ [1009 1]
+
+ [100003 1]
+
+ [340282366920938463463374607431768211457 1]
+
+ ? factorint(F, 1+8)  \\ much faster, all small primes were found
+ time = 12 ms.
+ %7 =
+ [1009 1]
+
+ [100003 1]
+
+ [340282366920938463463374607431768211457 1]
+
+ ? factor(F)   \\ complete factorisation
+ time = 112 ms.
+ %8 =
+ [1009 1]
+
+ [100003 1]
+
+ [59649589127497217 1]
+
+ [5704689200685129054721 1]
+ @eprog\noindent Over $\Q$, the prime factors are sorted in increasing order.
+
+ \misctitle{Rational functions}
+ The polynomials or rational functions to be factored must have scalar
+ coefficients. In particular PARI does not know how to factor
+ \emph{multivariate} polynomials. The following domains are currently
+ supported: $\Q$, $\R$, $\C$, $\Q_p$, finite fields and number fields.
+ See \tet{factormod} and \tet{factorff} for
+ the algorithms used over finite fields, \tet{factornf} for the algorithms
+ over number fields. Over $\Q$, \idx{van Hoeij}'s method is used, which is
+ able to cope with hundreds of modular factors.
+
+ The routine guesses a sensible ring over which to factor: the
+ smallest ring containing all coefficients, taking into account quotient
+ structures induced by \typ{INTMOD}s and \typ{POLMOD}s (e.g.~if a coefficient
+ in $\Z/n\Z$ is known, all rational numbers encountered are first mapped to
+ $\Z/n\Z$; different moduli will produce an error). Factoring modulo a
+ non-prime number is not supported; to factor in $\Q_p$, use \typ{PADIC}
+ coefficients not \typ{INTMOD} modulo $p^n$.
+ \bprog
+ ? T = x^2+1;
+ ? factor(T);                         \\ over Q
+ ? factor(T*Mod(1,3))                 \\ over F_3
+ ? factor(T*ffgen(ffinit(3,2,'t))^0)  \\ over F_{3^2}
+ ? factor(T*Mod(Mod(1,3), t^2+t+2))   \\ over F_{3^2}, again
+ ? factor(T*(1 + O(3^6))              \\ over Q_3, precision 6
+ ? factor(T*1.)                       \\ over R, current precision
+ ? factor(T*(1.+0.*I))                \\ over C
+ ? factor(T*Mod(1, y^3-2))            \\ over Q(2^{1/3})
+ @eprog\noindent In most cases, it is clearer and simpler to call an
+ explicit variant than to rely on the generic \kbd{factor} function and
+ the above detection mechanism:
+ \bprog
+ ? factormod(T, 3)           \\ over F_3
+ ? factorff(T, 3, t^2+t+2))  \\ over F_{3^2}
+ ? factorpadic(T, 3,6)       \\ over Q_3, precision 6
+ ? nffactor(y^3-2, T)        \\ over Q(2^{1/3})
+ ? polroots(T)               \\ over C
+ @eprog
+
+ Note that factorization of polynomials is done up to
+ multiplication by a constant. In particular, the factors of rational
+ polynomials will have integer coefficients, and the content of a polynomial
+ or rational function is discarded and not included in the factorization. If
+ needed, you can always ask for the content explicitly:
+ \bprog
+ ? factor(t^2 + 5/2*t + 1)
+ %1 =
+ [2*t + 1 1]
+
+ [t + 2 1]
+
+ ? content(t^2 + 5/2*t + 1)
+ %2 = 1/2
+ @eprog\noindent
+ The irreducible factors are sorted by increasing degree.
+ See also \tet{nffactor}.
+Variant: This function should only be used by the \kbd{gp} interface. Use
+ directly \fun{GEN}{factor}{GEN x} or \fun{GEN}{boundfact}{GEN x, ulong lim}.
+ The obsolete function \fun{GEN}{factor0}{GEN x, long lim} is kept for
+ backward compatibility.
+
+Function: _factor_Aurifeuille
+Section: programming/internals
+C-Name: factor_Aurifeuille
+Prototype: GL
+Help: _factor_Aurifeuille(a,d): return an algebraic factor of Phi_d(a), a != 0
+
+Function: _factor_Aurifeuille_prime
+Section: programming/internals
+C-Name: factor_Aurifeuille_prime
+Prototype: GL
+Help: _factor_Aurifeuille_prime(p,d): return an algebraic factor of Phi_d(p), p prime
diff --git a/src/functions/number_theoretical/factorback b/src/functions/number_theoretical/factorback
new file mode 100644
index 0000000..1acfb96
--- /dev/null
+++ b/src/functions/number_theoretical/factorback
@@ -0,0 +1,39 @@
+Function: factorback
+Section: number_theoretical
+C-Name: factorback2
+Prototype: GDG
+Description:
+ (gen):gen      factorback($1)
+ (gen,):gen     factorback($1)
+ (gen,gen):gen  factorback2($1, $2)
+Help: factorback(f,{e}): given a factorisation f, gives the factored
+ object back. If this is a prime ideal factorisation you must supply the
+ corresponding number field as last argument. If e is present, f has to be a
+ vector of the same length, and we return the product of the f[i]^e[i].
+Doc: gives back the factored object
+ corresponding to a factorization. The integer $1$ corresponds to the empty
+ factorization.
+
+ If $e$ is present, $e$ and $f$ must be vectors of the same length ($e$ being
+ integral), and the corresponding factorization is the product of the
+ $f[i]^{e[i]}$.
+
+ If not, and $f$ is vector, it is understood as in the preceding case with $e$
+ a vector of 1s: we return the product of the $f[i]$. Finally, $f$ can be a
+ regular factorization, as produced with any \kbd{factor} command. A few
+ examples:
+ \bprog
+ ? factor(12)
+ %1 =
+ [2 2]
+
+ [3 1]
+
+ ? factorback(%)
+ %2 = 12
+ ? factorback([2,3], [2,1])   \\ 2^3 * 3^1
+ %3 = 12
+ ? factorback([5,2,3])
+ %4 = 30
+ @eprog
+Variant: Also available is \fun{GEN}{factorback}{GEN f} (case $e = \kbd{NULL}$).
diff --git a/src/functions/number_theoretical/factorcantor b/src/functions/number_theoretical/factorcantor
new file mode 100644
index 0000000..95519e3
--- /dev/null
+++ b/src/functions/number_theoretical/factorcantor
@@ -0,0 +1,15 @@
+Function: factorcantor
+Section: number_theoretical
+C-Name: factcantor
+Prototype: GG
+Help: factorcantor(x,p): factorization mod p of the polynomial x using
+ Cantor-Zassenhaus.
+Doc: factors the polynomial $x$ modulo the
+ prime $p$, using distinct degree plus
+ \idx{Cantor-Zassenhaus}\sidx{Zassenhaus}. The coefficients of $x$ must be
+ operation-compatible with $\Z/p\Z$. The result is a two-column matrix, the
+ first column being the irreducible polynomials dividing $x$, and the second
+ the exponents. If you want only the \emph{degrees} of the irreducible
+ polynomials (for example for computing an $L$-function), use
+ $\kbd{factormod}(x,p,1)$. Note that the \kbd{factormod} algorithm is
+ usually faster than \kbd{factorcantor}.
diff --git a/src/functions/number_theoretical/factorff b/src/functions/number_theoretical/factorff
new file mode 100644
index 0000000..2ae4462
--- /dev/null
+++ b/src/functions/number_theoretical/factorff
@@ -0,0 +1,41 @@
+Function: factorff
+Section: number_theoretical
+C-Name: factorff
+Prototype: GDGDG
+Help: factorff(x,{p},{a}): factorization of the polynomial x in the finite field
+ F_p[X]/a(X)F_p[X].
+Doc: factors the polynomial $x$ in the field
+ $\F_q$ defined by the irreducible polynomial $a$ over $\F_p$. The
+ coefficients of $x$ must be operation-compatible with $\Z/p\Z$. The result
+ is a two-column matrix: the first column contains the irreducible factors of
+ $x$, and the second their exponents. If all the coefficients of $x$ are in
+ $\F_p$, a much faster algorithm is applied, using the computation of
+ isomorphisms between finite fields.
+
+ Either $a$ or $p$ can omitted (in which case both are ignored) if x has
+ \typ{FFELT} coefficients; the function then becomes identical to \kbd{factor}:
+ \bprog
+ ? factorff(x^2 + 1, 5, y^2+3)  \\ over F_5[y]/(y^2+3) ~ F_25
+ %1 =
+ [Mod(Mod(1, 5), Mod(1, 5)*y^2 + Mod(3, 5))*x
+  + Mod(Mod(2, 5), Mod(1, 5)*y^2 + Mod(3, 5)) 1]
+
+ [Mod(Mod(1, 5), Mod(1, 5)*y^2 + Mod(3, 5))*x
+  + Mod(Mod(3, 5), Mod(1, 5)*y^2 + Mod(3, 5)) 1]
+ ? t = ffgen(y^2 + Mod(3,5), 't); \\ a generator for F_25 as a t_FFELT
+ ? factorff(x^2 + 1)   \\ not enough information to determine the base field
+  ***   at top-level: factorff(x^2+1)
+  ***                 ^---------------
+  *** factorff: incorrect type in factorff.
+ ? factorff(x^2 + t^0) \\ make sure a coeff. is a t_FFELT
+ %3 =
+ [x + 2 1]
+
+ [x + 3 1]
+ ? factorff(x^2 + t + 1)
+ %11 =
+ [x + (2*t + 1) 1]
+
+ [x + (3*t + 4) 1]
+ @eprog\noindent
+ Notice that the second syntax is easier to use and much more readable.
diff --git a/src/functions/number_theoretical/factorial b/src/functions/number_theoretical/factorial
new file mode 100644
index 0000000..691942c
--- /dev/null
+++ b/src/functions/number_theoretical/factorial
@@ -0,0 +1,8 @@
+Function: factorial
+Section: number_theoretical
+C-Name: mpfactr
+Prototype: Lp
+Help: factorial(x): factorial of x, the result being given as a real number.
+Doc: factorial of $x$. The expression $x!$ gives a result which is an integer,
+ while $\kbd{factorial}(x)$ gives a real number.
+Variant: \fun{GEN}{mpfact}{long x} returns $x!$ as a \typ{INT}.
diff --git a/src/functions/number_theoretical/factorint b/src/functions/number_theoretical/factorint
new file mode 100644
index 0000000..70f9154
--- /dev/null
+++ b/src/functions/number_theoretical/factorint
@@ -0,0 +1,37 @@
+Function: factorint
+Section: number_theoretical
+C-Name: factorint
+Prototype: GD0,L,
+Help: factorint(x,{flag=0}): factor the integer x. flag is optional, whose
+ binary digits mean 1: avoid MPQS, 2: avoid first-stage ECM (may fall back on
+ it later), 4: avoid Pollard-Brent Rho and Shanks SQUFOF, 8: skip final ECM
+ (huge composites will be declared prime).
+Doc: factors the integer $n$ into a product of
+ pseudoprimes (see \kbd{ispseudoprime}), using a combination of the
+ \idx{Shanks SQUFOF} and \idx{Pollard Rho} method (with modifications due to
+ Brent), \idx{Lenstra}'s \idx{ECM} (with modifications by Montgomery), and
+ \idx{MPQS} (the latter adapted from the \idx{LiDIA} code with the kind
+ permission of the LiDIA maintainers), as well as a search for pure powers.
+ The output is a two-column matrix as for \kbd{factor}: the first column
+ contains the ``prime'' divisors of $n$, the second one contains the
+ (positive) exponents.
+
+ By convention $0$ is factored as $0^1$, and $1$ as the empty factorization;
+ also the divisors are by default not proven primes is they are larger than
+ $2^{64}$, they only failed the BPSW compositeness test (see
+ \tet{ispseudoprime}). Use \kbd{isprime} on the result if you want to
+ guarantee primality or set the \tet{factor_proven} default to $1$.
+ Entries of the private prime tables (see \tet{addprimes}) are also included
+ as is.
+
+ This gives direct access to the integer factoring engine called by most
+ arithmetical functions. \fl\ is optional; its binary digits mean 1: avoid
+ MPQS, 2: skip first stage ECM (we may still fall back to it later), 4: avoid
+ Rho and SQUFOF, 8: don't run final ECM (as a result, a huge composite may be
+ declared to be prime). Note that a (strong) probabilistic primality test is
+ used; thus composites might not be detected, although no example is known.
+
+ You are invited to play with the flag settings and watch the internals at
+ work by using \kbd{gp}'s \tet{debug} default parameter (level 3 shows
+ just the outline, 4 turns on time keeping, 5 and above show an increasing
+ amount of internal details).
diff --git a/src/functions/number_theoretical/factormod b/src/functions/number_theoretical/factormod
new file mode 100644
index 0000000..28364a2
--- /dev/null
+++ b/src/functions/number_theoretical/factormod
@@ -0,0 +1,14 @@
+Function: factormod
+Section: number_theoretical
+C-Name: factormod0
+Prototype: GGD0,L,
+Help: factormod(x,p,{flag=0}): factors the polynomial x modulo the prime p, using Berlekamp. flag is optional, and can be 0: default or 1:
+ only the degrees of the irreducible factors are given.
+Doc: factors the polynomial $x$ modulo the prime integer $p$, using
+ \idx{Berlekamp}. The coefficients of $x$ must be operation-compatible with
+ $\Z/p\Z$. The result is a two-column matrix, the first column being the
+ irreducible polynomials dividing $x$, and the second the exponents. If $\fl$
+ is non-zero, outputs only the \emph{degrees} of the irreducible polynomials
+ (for example, for computing an $L$-function). A different algorithm for
+ computing the mod $p$ factorization is \kbd{factorcantor} which is sometimes
+ faster.
diff --git a/src/functions/number_theoretical/ffgen b/src/functions/number_theoretical/ffgen
new file mode 100644
index 0000000..ff58509
--- /dev/null
+++ b/src/functions/number_theoretical/ffgen
@@ -0,0 +1,41 @@
+Function: ffgen
+Section: number_theoretical
+C-Name: ffgen
+Prototype: GDn
+Help: ffgen(q,{v}): return a generator X mod P(X) for the finite field with
+ q elements. If v is given, the variable name is used to display g, else the
+ variable 'x' is used. Alternative syntax, q = P(X) an irreducible
+ polynomial with t_INTMOD
+ coefficients, return the generator X mod P(X) of the finite field defined
+ by P. If v is given, the variable name is used to display g, else the
+ variable of the polynomial P is used.
+
+Doc: return a \typ{FFELT} generator for the finite field with $q$ elements;
+ $q = p^f$ must be a prime power. This functions computes an irreducible
+ monic polynomial $P\in\F_p[X]$ of degree~$f$ (via \tet{ffinit}) and
+ returns $g = X \pmod{P(X)}$. If \kbd{v} is given, the variable name is used
+ to display $g$, else the variable $x$ is used.
+ \bprog
+ ? g = ffgen(8, 't);
+ ? g.mod
+ %2 = t^3 + t^2 + 1
+ ? g.p
+ %3 = 2
+ ? g.f
+ %4 = 3
+ ? ffgen(6)
+  ***   at top-level: ffgen(6)
+  ***                 ^--------
+  *** ffgen: not a prime number in ffgen: 6.
+ @eprog\noindent Alternative syntax: instead of a prime power $q$, one may
+ input directly the polynomial $P$ (monic, irreducible, with \typ{INTMOD}
+ coefficients), and the function returns the generator $g = X \pmod{P(X)}$,
+ inferring $p$ from the coefficients of $P$. If \kbd{v} is given, the
+ variable name is used to display $g$, else the variable of the polynomial
+ $P$ is used. If $P$ is not irreducible, we create an invalid object and
+ behaviour of functions dealing with the resulting \typ{FFELT}
+ is undefined; in fact, it is much more costly to test $P$ for
+ irreducibility than it would be to produce it via \kbd{ffinit}.
+Variant:
+ To create a generator for a prime finite field, the function
+ \fun{GEN}{p_to_GEN}{GEN p, long v} returns \kbd{1+ffgen(x*Mod(1,p),v)}.
diff --git a/src/functions/number_theoretical/ffinit b/src/functions/number_theoretical/ffinit
new file mode 100644
index 0000000..e099da1
--- /dev/null
+++ b/src/functions/number_theoretical/ffinit
@@ -0,0 +1,16 @@
+Function: ffinit
+Section: number_theoretical
+C-Name: ffinit
+Prototype: GLDn
+Help: ffinit(p,n,{v='x}): monic irreducible polynomial of degree n over F_p[v].
+Description:
+ (int, small, ?var):pol        ffinit($1, $2, $3)
+Doc: computes a monic polynomial of degree $n$ which is irreducible over
+  $\F_p$, where $p$ is assumed to be prime. This function uses a fast variant
+  of Adleman and Lenstra's algorithm.
+
+ It is useful in conjunction with \tet{ffgen}; for instance if
+ \kbd{P = ffinit(3,2)}, you can represent elements in $\F_{3^2}$ in term of
+ \kbd{g = ffgen(P,'t)}. This can be abbreviated as
+ \kbd{g = ffgen(3\pow2, 't)}, where the defining polynomial $P$ can be later
+ recovered as \kbd{g.mod}.
diff --git a/src/functions/number_theoretical/fflog b/src/functions/number_theoretical/fflog
new file mode 100644
index 0000000..0aee200
--- /dev/null
+++ b/src/functions/number_theoretical/fflog
@@ -0,0 +1,40 @@
+Function: fflog
+Section: number_theoretical
+C-Name: fflog
+Prototype: GGDG
+Help: fflog(x,g,{o}): return the discrete logarithm of the finite field
+ element x in base g. If present, o must represents the multiplicative
+ order of g. If no o is given, assume that g is a primitive root.
+Doc: discrete logarithm of the finite field element $x$ in base $g$, i.e.~
+ an $e$ in $\Z$ such that $g^e = o$. If
+ present, $o$ represents the multiplicative order of $g$, see
+ \secref{se:DLfun}; the preferred format for
+ this parameter is \kbd{[ord, factor(ord)]}, where \kbd{ord} is the
+ order of $g$. It may be set as a side effect of calling \tet{ffprimroot}.
+
+ If no $o$ is given, assume that $g$ is a primitive root. The result is
+ undefined if $e$ does not exist. This function uses
+
+ \item a combination of generic discrete log algorithms (see \tet{znlog})
+
+ \item a cubic sieve index calculus algorithm for large fields of degree at
+ least $5$.
+
+ \item Coppersmith's algorithm for fields of characteristic at most $5$.
+
+ \bprog
+ ? t = ffgen(ffinit(7,5));
+ ? o = fforder(t)
+ %2 = 5602   \\@com \emph{not} a primitive root.
+ ? fflog(t^10,t)
+ %3 = 10
+ ? fflog(t^10,t, o)
+ %4 = 10
+ ? g = ffprimroot(t, &o);
+ ? o   \\ order is 16806, bundled with its factorization matrix
+ %6 = [16806, [2, 1; 3, 1; 2801, 1]]
+ ? fforder(g, o)
+ %7 = 16806
+ ? fflog(g^10000, g, o)
+ %8 = 10000
+ @eprog
diff --git a/src/functions/number_theoretical/ffnbirred b/src/functions/number_theoretical/ffnbirred
new file mode 100644
index 0000000..117af0d
--- /dev/null
+++ b/src/functions/number_theoretical/ffnbirred
@@ -0,0 +1,15 @@
+Function: ffnbirred
+Section: number_theoretical
+C-Name: ffnbirred0
+Prototype: GLD0,L,
+Help: ffnbirred(q,n{,fl=0}): number of monic irreducible polynomials over F_q, of
+ degree n (fl=0, default) or at most n (fl=1).
+Description:
+ (int, small, ?0):int      ffnbirred($1, $2)
+ (int, small, 1):int       ffsumnbirred($1, $2)
+ (int, small, ?small):int  ffnbirred0($1, $2, $3)
+Doc: computes the number of monic irreducible polynomials over $\F_q$ of degree exactly $n$,
+ ($\fl=0$ or omitted) or at most $n$ ($\fl=1$).
+Variant: Also available are
+  \fun{GEN}{ffnbirred}{GEN q, long n} (for $\fl=0$)
+  and \fun{GEN}{ffsumnbirred}{GEN q, long n} (for $\fl=1$).
diff --git a/src/functions/number_theoretical/fforder b/src/functions/number_theoretical/fforder
new file mode 100644
index 0000000..3b94881
--- /dev/null
+++ b/src/functions/number_theoretical/fforder
@@ -0,0 +1,21 @@
+Function: fforder
+Section: number_theoretical
+C-Name: fforder
+Prototype: GDG
+Help: fforder(x,{o}): multiplicative order of the finite field element x.
+ Optional o represents a multiple of the order of the element.
+Doc: multiplicative order of the finite field element $x$.  If $o$ is
+ present, it represents a multiple of the order of the element,
+ see \secref{se:DLfun}; the preferred format for
+ this parameter is \kbd{[N, factor(N)]}, where \kbd{N} is the cardinality
+ of the multiplicative group of the underlying finite field.
+ \bprog
+ ? t = ffgen(ffinit(nextprime(10^8), 5));
+ ? g = ffprimroot(t, &o);  \\@com o will be useful!
+ ? fforder(g^1000000, o)
+ time = 0 ms.
+ %5 = 5000001750000245000017150000600250008403
+ ? fforder(g^1000000)
+ time = 16 ms. \\@com noticeably slower, same result of course
+ %6 = 5000001750000245000017150000600250008403
+ @eprog
diff --git a/src/functions/number_theoretical/ffprimroot b/src/functions/number_theoretical/ffprimroot
new file mode 100644
index 0000000..53a2cf1
--- /dev/null
+++ b/src/functions/number_theoretical/ffprimroot
@@ -0,0 +1,44 @@
+Function: ffprimroot
+Section: number_theoretical
+C-Name: ffprimroot
+Prototype: GD&
+Help: ffprimroot(x, {&o}): return a primitive root of the multiplicative group
+ of the definition field of the finite field element x (not necessarily the
+ same as the field generated by x). If present, o is set to [ord, fa], where
+ ord is the order of the group, and fa its factorization
+ (useful in fflog and fforder).
+Doc: return a primitive root of the multiplicative
+ group of the definition field of the finite field element $x$ (not necessarily
+ the same as the field generated by $x$). If present, $o$ is set to
+ a vector \kbd{[ord, fa]}, where \kbd{ord} is the order of the group
+ and \kbd{fa} its factorisation \kbd{factor(ord)}. This last parameter is
+ useful in \tet{fflog} and \tet{fforder}, see \secref{se:DLfun}.
+ \bprog
+ ? t = ffgen(ffinit(nextprime(10^7), 5));
+ ? g = ffprimroot(t, &o);
+ ? o[1]
+ %3 = 100000950003610006859006516052476098
+ ? o[2]
+ %4 =
+ [2 1]
+
+ [7 2]
+
+ [31 1]
+
+ [41 1]
+
+ [67 1]
+
+ [1523 1]
+
+ [10498781 1]
+
+ [15992881 1]
+
+ [46858913131 1]
+
+ ? fflog(g^1000000, g, o)
+ time = 1,312 ms.
+ %5 = 1000000
+ @eprog
diff --git a/src/functions/number_theoretical/fibonacci b/src/functions/number_theoretical/fibonacci
new file mode 100644
index 0000000..586c7f9
--- /dev/null
+++ b/src/functions/number_theoretical/fibonacci
@@ -0,0 +1,6 @@
+Function: fibonacci
+Section: number_theoretical
+C-Name: fibo
+Prototype: L
+Help: fibonacci(x): fibonacci number of index x (x C-integer).
+Doc: $x^{\text{th}}$ Fibonacci number.
diff --git a/src/functions/number_theoretical/gcd b/src/functions/number_theoretical/gcd
new file mode 100644
index 0000000..8f5906e
--- /dev/null
+++ b/src/functions/number_theoretical/gcd
@@ -0,0 +1,53 @@
+Function: gcd
+Section: number_theoretical
+C-Name: ggcd0
+Prototype: GDG
+Help: gcd(x,{y}): greatest common divisor of x and y.
+Description:
+ (small, small):small   cgcd($1, $2)
+ (int, int):int         gcdii($1, $2)
+ (gen):gen              content($1)
+ (gen, gen):gen         ggcd($1, $2)
+Doc: creates the greatest common divisor of $x$ and $y$.
+ If you also need the $u$ and $v$ such that $x*u + y*v = \gcd(x,y)$,
+ use the \tet{bezout} function. $x$ and $y$ can have rather quite general
+ types, for instance both rational numbers. If $y$ is omitted and $x$ is a
+ vector, returns the $\text{gcd}$ of all components of $x$, i.e.~this is
+ equivalent to \kbd{content(x)}.
+
+ When $x$ and $y$ are both given and one of them is a vector/matrix type,
+ the GCD is again taken recursively on each component, but in a different way.
+ If $y$ is a vector, resp.~matrix, then the result has the same type as $y$,
+ and components equal to \kbd{gcd(x, y[i])}, resp.~\kbd{gcd(x, y[,i])}. Else
+ if $x$ is a vector/matrix the result has the same type as $x$ and an
+ analogous definition. Note that for these types, \kbd{gcd} is not
+ commutative.
+
+ The algorithm used is a naive \idx{Euclid} except for the following inputs:
+
+ \item integers: use modified right-shift binary (``plus-minus''
+ variant).
+
+ \item univariate polynomials with coefficients in the same number
+ field (in particular rational): use modular gcd algorithm.
+
+ \item general polynomials: use the \idx{subresultant algorithm} if
+ coefficient explosion is likely (non modular coefficients).
+
+ If $u$ and $v$ are polynomials in the same variable with \emph{inexact}
+ coefficients, their gcd is defined to be scalar, so that
+ \bprog
+ ? a = x + 0.0; gcd(a,a)
+ %1 = 1
+ ? b = y*x + O(y); gcd(b,b)
+ %2 = y
+ ? c = 4*x + O(2^3); gcd(c,c)
+ %3 = 4
+ @eprog\noindent A good quantitative check to decide whether such a
+ gcd ``should be'' non-trivial, is to use \tet{polresultant}: a value
+ close to $0$ means that a small deformation of the inputs has non-trivial gcd.
+ You may also use \tet{gcdext}, which does try to compute an approximate gcd
+ $d$ and provides $u$, $v$ to check whether $u x + v y$ is close to $d$.
+
+Variant: Also available are \fun{GEN}{ggcd}{GEN x, GEN y}, if \kbd{y} is not
+ \kbd{NULL}, and \fun{GEN}{content}{GEN x}, if $\kbd{y} = \kbd{NULL}$.
diff --git a/src/functions/number_theoretical/gcdext b/src/functions/number_theoretical/gcdext
new file mode 100644
index 0000000..872c1c6
--- /dev/null
+++ b/src/functions/number_theoretical/gcdext
@@ -0,0 +1,34 @@
+Function: gcdext
+Section: number_theoretical
+C-Name: gcdext0
+Prototype: GG
+Help: gcdext(x,y): returns [u,v,d] such that d=gcd(x,y) and u*x+v*y=d.
+Doc: Returns $[u,v,d]$ such that $d$ is the gcd of $x,y$,
+ $x*u+y*v=\gcd(x,y)$, and $u$ and $v$ minimal in a natural sense.
+ The arguments must be integers or polynomials. \sidx{extended gcd}
+ \sidx{Bezout relation}
+ \bprog
+ ? [u, v, d] = gcdext(32,102)
+ %1 = [16, -5, 2]
+ ? d
+ %2 = 2
+ ? gcdext(x^2-x, x^2+x-2)
+ %3 = [-1/2, 1/2, x - 1]
+ @eprog
+
+ If $x,y$ are polynomials in the same variable and \emph{inexact}
+ coefficients, then compute $u,v,d$ such that $x*u+y*v = d$, where $d$
+ approximately divides both and $x$ and $y$; in particular, we do not obtain
+ \kbd{gcd(x,y)} which is \emph{defined} to be a scalar in this case:
+ \bprog
+ ? a = x + 0.0; gcd(a,a)
+ %1 = 1
+
+ ? gcdext(a,a)
+ %2 = [0, 1, x + 0.E-28]
+
+ ? gcdext(x-Pi, 6*x^2-zeta(2))
+ %3 = [-6*x - 18.8495559, 1, 57.5726923]
+ @eprog\noindent For inexact inputs, the output is thus not well defined
+ mathematically, but you obtain explicit polynomials to check whether the
+ approximation is close enough for your needs.
diff --git a/src/functions/number_theoretical/hilbert b/src/functions/number_theoretical/hilbert
new file mode 100644
index 0000000..c1520f5
--- /dev/null
+++ b/src/functions/number_theoretical/hilbert
@@ -0,0 +1,12 @@
+Function: hilbert
+Section: number_theoretical
+C-Name: hilbert
+Prototype: lGGDG
+Help: hilbert(x,y,{p}): Hilbert symbol at p of x,y.
+Doc: \idx{Hilbert symbol} of $x$ and $y$ modulo the prime $p$, $p=0$ meaning
+ the place at infinity (the result is undefined if $p\neq 0$ is not prime).
+
+ It is possible to omit $p$, in which case we take $p = 0$ if both $x$
+ and $y$ are rational, or one of them is a real number. And take $p = q$
+ if one of $x$, $y$ is a \typ{INTMOD} modulo $q$ or a $q$-adic. (Incompatible
+ types will raise an error.)
diff --git a/src/functions/number_theoretical/isfundamental b/src/functions/number_theoretical/isfundamental
new file mode 100644
index 0000000..0fc45cd
--- /dev/null
+++ b/src/functions/number_theoretical/isfundamental
@@ -0,0 +1,11 @@
+Function: isfundamental
+Section: number_theoretical
+C-Name: isfundamental
+Prototype: lG
+Help: isfundamental(x): true(1) if x is a fundamental discriminant
+ (including 1), false(0) if not.
+Description:
+ (int):bool       Z_isfundamental($1)
+ (gen):bool       isfundamental($1)
+Doc: true (1) if $x$ is equal to 1 or to the discriminant of a quadratic
+ field, false (0) otherwise.
diff --git a/src/functions/number_theoretical/ispolygonal b/src/functions/number_theoretical/ispolygonal
new file mode 100644
index 0000000..a028768
--- /dev/null
+++ b/src/functions/number_theoretical/ispolygonal
@@ -0,0 +1,14 @@
+Function: ispolygonal
+Section: number_theoretical
+C-Name: ispolygonal
+Prototype: lGGD&
+Help: ispolygonal(x,s,{&N}): true(1) if x is an s-gonal number, false(0) if
+ not (s > 2). If N is given set it to n if x is the n-th s-gonal number.
+Doc: true (1) if the integer $x$ is an s-gonal number, false (0) if not.
+ The parameter $s > 2$ must be a \typ{INT}. If $N$ is given, set it to $n$
+ if $x$ is the $n$-th $s$-gonal number.
+ \bprog
+ ? ispolygonal(36, 3, &N)
+ %1 = 1
+ ? N
+ @eprog
diff --git a/src/functions/number_theoretical/ispower b/src/functions/number_theoretical/ispower
new file mode 100644
index 0000000..de363bd
--- /dev/null
+++ b/src/functions/number_theoretical/ispower
@@ -0,0 +1,30 @@
+Function: ispower
+Section: number_theoretical
+C-Name: ispower
+Prototype: lGDGD&
+Help: ispower(x,{k},{&n}): if k > 0 is given, return true (1) if x is a k-th
+ power, false (0) if not. If k is omitted, return the maximal k >= 2 such
+ that x = n^k is a perfect power, or 0 if no such k exist.
+ If n is present, and the function returns a non-zero result, set n to the
+ k-th root of x.
+Description:
+ (int):small       Z_isanypower($1, NULL)
+ (int, &int):small Z_isanypower($1, &$2)
+Doc: if $k$ is given, returns true (1) if $x$ is a $k$-th power, false
+ (0) if not.
+
+ If $k$ is omitted, only integers and fractions are allowed for $x$ and the
+ function returns the maximal $k \geq 2$ such that $x = n^k$ is a perfect
+ power, or 0 if no such $k$ exist; in particular \kbd{ispower(-1)},
+ \kbd{ispower(0)}, and \kbd{ispower(1)} all return $0$.
+
+ If a third argument $\&n$ is given and $x$ is indeed a $k$-th power, sets
+ $n$ to a $k$-th root of $x$.
+
+ \noindent For a \typ{FFELT} \kbd{x}, instead of omitting \kbd{k} (which is
+ not allowed for this type), it may be natural to set
+ \bprog
+ k = (x.p ^ poldegree(x.pol) - 1) / fforder(x)
+ @eprog
+Variant: Also available is
+ \fun{long}{gisanypower}{GEN x, GEN *pty} ($k$ omitted).
diff --git a/src/functions/number_theoretical/ispowerful b/src/functions/number_theoretical/ispowerful
new file mode 100644
index 0000000..39e056a
--- /dev/null
+++ b/src/functions/number_theoretical/ispowerful
@@ -0,0 +1,17 @@
+Function: ispowerful
+Section: number_theoretical
+C-Name: ispowerful
+Prototype: lG
+Help: ispowerful(x): true(1) if x is a powerful integer (valuation at all
+ primes is greater than 1), false(0) if not.
+Doc: true (1) if $x$ is a powerful integer, false (0) if not;
+ an integer is powerful if and only if its valuation at all primes is
+ greater than 1.
+ \bprog
+ ? ispowerful(50)
+ %1 = 0
+ ? ispowerful(100)
+ %2 = 1
+ ? ispowerful(5^3*(10^1000+1)^2)
+ %3 = 1
+ @eprog
diff --git a/src/functions/number_theoretical/isprime b/src/functions/number_theoretical/isprime
new file mode 100644
index 0000000..0f60e71
--- /dev/null
+++ b/src/functions/number_theoretical/isprime
@@ -0,0 +1,55 @@
+Function: isprime
+Section: number_theoretical
+C-Name: gisprime
+Prototype: GD0,L,
+Help: isprime(x,{flag=0}): true(1) if x is a (proven) prime number, false(0)
+ if not. If flag is 0 or omitted, use a combination of algorithms. If flag is
+ 1, the primality is certified by the Pocklington-Lehmer Test. If flag is 2,
+ the primality is certified using the APRCL test.
+Description:
+ (int, ?0):bool        isprime($1)
+ (int, 1):bool         plisprime($1, 0)
+ (int, 2):gen          plisprime($1, 1)
+ (gen, ?small):gen     gisprime($1, $2)
+Doc: true (1) if $x$ is a prime
+ number, false (0) otherwise. A prime number is a positive integer having
+ exactly two distinct divisors among the natural numbers, namely 1 and
+ itself.
+
+ This routine proves or disproves rigorously that a number is prime, which can
+ be very slow when $x$ is indeed prime and has more than $1000$ digits, say.
+ Use \tet{ispseudoprime} to quickly check for compositeness. See also
+ \kbd{factor}. It accepts vector/matrices arguments, and is then applied
+ componentwise.
+
+ If $\fl=0$, use a combination of Baillie-PSW pseudo primality test (see
+ \tet{ispseudoprime}), Selfridge ``$p-1$'' test if $x-1$ is smooth enough, and
+ Adleman-Pomerance-Rumely-Cohen-Lenstra (APRCL) for general $x$.
+
+ If $\fl=1$, use Selfridge-Pocklington-Lehmer ``$p-1$'' test and output a
+ primality certificate as follows: return
+
+ \item 0 if $x$ is composite,
+
+ \item 1 if $x$ is small enough that passing Baillie-PSW test guarantees
+ its primality (currently $x < 2^{64}$, as checked by Jan Feitsma),
+
+ \item $2$ if $x$ is a large prime whose primality could only sensibly be
+ proven (given the algorithms implemented in PARI) using the APRCL test.
+
+ \item Otherwise ($x$ is large and $x-1$ is smooth) output a three column
+ matrix as a primality certificate. The first column contains prime
+ divisors $p$ of $x-1$ (such that $\prod p^{v_p(x-1)} > x^{1/3}$), the second
+ the corresponding elements $a_p$ as in Proposition~8.3.1 in GTM~138 , and the
+ third the output of isprime(p,1).
+
+ The algorithm fails if one of the pseudo-prime factors is not prime, which is
+ exceedingly unlikely and well worth a bug report. Note that if you monitor
+ \kbd{isprime} at a high enough debug level, you may see warnings about
+ untested integers being declared primes. This is normal: we ask for partial
+ factorisations (sufficient to prove primality if the unfactored part is not
+ too large), and \kbd{factor} warns us that the cofactor hasn't been tested.
+ It may or may not be tested later, and may or may not be prime. This does
+ not affect the validity of the whole \kbd{isprime} procedure.
+
+ If $\fl=2$, use APRCL.
diff --git a/src/functions/number_theoretical/isprimepower b/src/functions/number_theoretical/isprimepower
new file mode 100644
index 0000000..b9f6c0a
--- /dev/null
+++ b/src/functions/number_theoretical/isprimepower
@@ -0,0 +1,10 @@
+Function: isprimepower
+Section: number_theoretical
+C-Name: isprimepower
+Prototype: lGD&
+Help: isprimepower(x,{&n}): if x = p^k is a prime power (p prime, k > 0),
+ return k, else return 0. If n is present, and the function returns a non-zero
+ result, set n to p, the k-th root of x.
+Doc: if $x = p^k$ is a prime power ($p$ prime, $k > 0$), return $k$, else
+ return 0. If a second argument $\&n$ is given and $x$ is indeed
+ the $k$-th power of a prime $p$, sets $n$ to $p$.
diff --git a/src/functions/number_theoretical/ispseudoprime b/src/functions/number_theoretical/ispseudoprime
new file mode 100644
index 0000000..0f26792
--- /dev/null
+++ b/src/functions/number_theoretical/ispseudoprime
@@ -0,0 +1,32 @@
+Function: ispseudoprime
+Section: number_theoretical
+C-Name: gispseudoprime
+Prototype: GD0,L,
+Help: ispseudoprime(x,{flag}): true(1) if x is a strong pseudoprime, false(0)
+ if not. If flag is 0 or omitted, use BPSW test, otherwise use strong
+ Rabin-Miller test for flag randomly chosen bases.
+Description:
+ (int,?0):bool      BPSW_psp($1)
+ (int,#small):bool  millerrabin($1,$2)
+ (int,small):bool   ispseudoprime($1, $2)
+ (gen,?small):bool  gispseudoprime($1, $2)
+Doc: true (1) if $x$ is a strong pseudo
+ prime (see below), false (0) otherwise. If this function returns false, $x$
+ is not prime; if, on the other hand it returns true, it is only highly likely
+ that $x$ is a prime number. Use \tet{isprime} (which is of course much
+ slower) to prove that $x$ is indeed prime.
+ The function accepts vector/matrices arguments, and is then applied
+ componentwise.
+
+ If $\fl = 0$, checks whether $x$ is a Baillie-Pomerance-Selfridge-Wagstaff
+ pseudo prime (strong Rabin-Miller pseudo prime for base $2$, followed by
+ strong Lucas test for the sequence $(P,-1)$, $P$ smallest positive integer
+ such that $P^2 - 4$ is not a square mod $x$).
+
+ There are no known composite numbers passing this test, although it is
+ expected that infinitely many such numbers exist. In particular, all
+ composites $\leq 2^{64}$ are correctly detected (checked using
+ \kbd{http://www.cecm.sfu.ca/Pseudoprimes/index-2-to-64.html}).
+
+ If $\fl > 0$, checks whether $x$ is a strong Miller-Rabin pseudo prime  for
+ $\fl$ randomly chosen bases (with end-matching to catch square roots of $-1$).
diff --git a/src/functions/number_theoretical/issquare b/src/functions/number_theoretical/issquare
new file mode 100644
index 0000000..14aeeda
--- /dev/null
+++ b/src/functions/number_theoretical/issquare
@@ -0,0 +1,41 @@
+Function: issquare
+Section: number_theoretical
+C-Name: issquareall
+Prototype: lGD&
+Help: issquare(x,{&n}): true(1) if x is a square, false(0) if not. If n is
+ given puts the exact square root there if it was computed.
+Description:
+ (int):bool        Z_issquare($1)
+ (gen):bool        issquare($1)
+ (int, &int):bool  Z_issquarerem($1, &$2)
+ (gen, &gen):bool  issquareall($1, &$2)
+Doc: true (1) if $x$ is a square, false (0)
+ if not. What ``being a square'' means depends on the type of $x$: all
+ \typ{COMPLEX} are squares, as well as all non-negative \typ{REAL}; for
+ exact types such as \typ{INT}, \typ{FRAC} and \typ{INTMOD}, squares are
+ numbers of the form $s^2$ with $s$ in $\Z$, $\Q$ and $\Z/N\Z$ respectively.
+ \bprog
+ ? issquare(3)          \\ as an integer
+ %1 = 0
+ ? issquare(3.)         \\ as a real number
+ %2 = 1
+ ? issquare(Mod(7, 8))  \\ in Z/8Z
+ %3 = 0
+ ? issquare( 5 + O(13^4) )  \\ in Q_13
+ %4 = 0
+ @eprog
+ If $n$ is given, a square root of $x$ is put into $n$.
+ \bprog
+ ? issquare(4, &n)
+ %1 = 1
+ ? n
+ %2 = 2
+ @eprog
+ For polynomials, either we detect that the characteristic is 2 (and check
+ directly odd and even-power monomials) or we assume that $2$ is invertible
+ and check whether squaring the truncated power series for the square root
+ yields the original input.
+Variant: Also available is \fun{long}{issquare}{GEN x}. Deprecated
+ GP-specific functions \fun{GEN}{gissquare}{GEN x} and
+ \fun{GEN}{gissquareall}{GEN x, GEN *pt} return \kbd{gen\_0} and \kbd{gen\_1}
+ instead of a boolean value.
diff --git a/src/functions/number_theoretical/issquarefree b/src/functions/number_theoretical/issquarefree
new file mode 100644
index 0000000..cf851d3
--- /dev/null
+++ b/src/functions/number_theoretical/issquarefree
@@ -0,0 +1,9 @@
+Function: issquarefree
+Section: number_theoretical
+C-Name: issquarefree
+Prototype: lG
+Help: issquarefree(x): true(1) if x is squarefree, false(0) if not.
+Description:
+ (gen):bool       issquarefree($1)
+Doc: true (1) if $x$ is squarefree, false (0) if not. Here $x$ can be an
+ integer or a polynomial.
diff --git a/src/functions/number_theoretical/istotient b/src/functions/number_theoretical/istotient
new file mode 100644
index 0000000..431968e
--- /dev/null
+++ b/src/functions/number_theoretical/istotient
@@ -0,0 +1,21 @@
+Function: istotient
+Section: number_theoretical
+C-Name: istotient
+Prototype: lGD&
+Help: istotient(x,{&N}): true(1) if x = eulerphi(n) for some integer n,
+ false(0) if not. If N is given, set N = n as well.
+Doc: true (1) if $x = \phi(n)$ for some integer $n$, false (0)
+ if not.
+ \bprog
+ ? istotient(14)
+ %1 = 0
+ ? istotient(100)
+ %2 = 0
+ @eprog
+ If $N$ is given, set $N = n$ as well.
+ \bprog
+ ? istotient(4, &n)
+ %1 = 1
+ ? n
+ %2 = 10
+ @eprog
diff --git a/src/functions/number_theoretical/kronecker b/src/functions/number_theoretical/kronecker
new file mode 100644
index 0000000..3689960
--- /dev/null
+++ b/src/functions/number_theoretical/kronecker
@@ -0,0 +1,22 @@
+Function: kronecker
+Section: number_theoretical
+C-Name: kronecker
+Prototype: lGG
+Help: kronecker(x,y): kronecker symbol (x/y).
+Description:
+ (small, small):small  kross($1, $2)
+ (int, small):small    krois($1, $2)
+ (small, int):small    krosi($1, $2)
+ (gen, gen):small      kronecker($1, $2)
+Doc:
+ \idx{Kronecker symbol} $(x|y)$, where $x$ and $y$ must be of type integer. By
+ definition, this is the extension of \idx{Legendre symbol} to $\Z \times \Z$
+ by total multiplicativity in both arguments with the following special rules
+ for $y = 0, -1$ or $2$:
+
+ \item $(x|0) = 1$ if $|x| = 1$ and $0$ otherwise.
+
+ \item $(x|-1) = 1$ if $x \geq 0$ and $-1$ otherwise.
+
+ \item $(x|2) = 0$ if $x$ is even and $1$ if $x = 1,-1 \mod 8$ and $-1$
+ if $x=3,-3 \mod 8$.
diff --git a/src/functions/number_theoretical/lcm b/src/functions/number_theoretical/lcm
new file mode 100644
index 0000000..eab7e1d
--- /dev/null
+++ b/src/functions/number_theoretical/lcm
@@ -0,0 +1,36 @@
+Function: lcm
+Section: number_theoretical
+C-Name: glcm0
+Prototype: GDG
+Help: lcm(x,{y}): least common multiple of x and y, i.e. x*y / gcd(x,y).
+Description:
+ (int, int):int lcmii($1, $2)
+ (gen):gen      glcm0($1, NULL)
+ (gen, gen):gen glcm($1, $2)
+Doc: least common multiple of $x$ and $y$, i.e.~such
+ that $\lcm(x,y)*\gcd(x,y) = \text{abs}(x*y)$. If $y$ is omitted and $x$
+ is a vector, returns the $\text{lcm}$ of all components of $x$.
+
+ When $x$ and $y$ are both given and one of them is a vector/matrix type,
+ the LCM is again taken recursively on each component, but in a different way.
+ If $y$ is a vector, resp.~matrix, then the result has the same type as $y$,
+ and components equal to \kbd{lcm(x, y[i])}, resp.~\kbd{lcm(x, y[,i])}. Else
+ if $x$ is a vector/matrix the result has the same type as $x$ and an
+ analogous definition. Note that for these types, \kbd{lcm} is not
+ commutative.
+
+ Note that \kbd{lcm(v)} is quite different from
+ \bprog
+ l = v[1]; for (i = 1, #v, l = lcm(l, v[i]))
+ @eprog\noindent
+ Indeed, \kbd{lcm(v)} is a scalar, but \kbd{l} may not be (if one of
+ the \kbd{v[i]} is a vector/matrix). The computation uses a divide-conquer tree
+ and should be much more efficient, especially when using the GMP
+ multiprecision kernel (and more subquadratic algorithms become available):
+ \bprog
+ ? v = vector(10^4, i, random);
+ ? lcm(v);
+ time = 323 ms.
+ ? l = v[1]; for (i = 1, #v, l = lcm(l, v[i]))
+ time = 833 ms.
+ @eprog
diff --git a/src/functions/number_theoretical/logint b/src/functions/number_theoretical/logint
new file mode 100644
index 0000000..3ce94f0
--- /dev/null
+++ b/src/functions/number_theoretical/logint
@@ -0,0 +1,34 @@
+Function: logint
+Section: number_theoretical
+C-Name: logint0
+Prototype: lGGD&
+Help: logint(x,b,&z): return the largest integer e so that b^e <= x, where the
+ parameters b > 1 and x > 0 are both integers. If the parameter z is present,
+ set it to b^e.
+Description:
+ (gen,2):small        expi($1)
+ (gen,gen,&int):small logint0($1, $2, &$3)
+Doc: Return the largest integer $e$ so that $b^e \leq x$, where the
+ parameters $b > 1$ and $x > 0$ are both integers. If the parameter $z$ is
+ present, set it to $b^e$.
+ \bprog
+ ? logint(1000, 2)
+ %1 = 9
+ ? 2^9
+ %2 = 512
+ ? logint(1000, 2, &z)
+ %3 = 9
+ ? z
+ %4 = 512
+ @eprog\noindent The number of digits used to write $b$ in base $x$ is
+ \kbd{1 + logint(x,b)}:
+ \bprog
+ ? #digits(1000!, 10)
+ %5 = 2568
+ ? logint(1000!, 10)
+ %6 = 2567
+ @eprog\noindent This function may conveniently replace
+ \bprog
+   floor( log(x) / log(b) )
+ @eprog\noindent which may not give the correct answer since PARI
+ does not guarantee exact rounding.
diff --git a/src/functions/number_theoretical/moebius b/src/functions/number_theoretical/moebius
new file mode 100644
index 0000000..352c55c
--- /dev/null
+++ b/src/functions/number_theoretical/moebius
@@ -0,0 +1,6 @@
+Function: moebius
+Section: number_theoretical
+C-Name: moebius
+Prototype: lG
+Help: moebius(x): Moebius function of x.
+Doc: \idx{Moebius} $\mu$-function of $|x|$. $x$ must be of type integer.
diff --git a/src/functions/number_theoretical/nextprime b/src/functions/number_theoretical/nextprime
new file mode 100644
index 0000000..67c05b0
--- /dev/null
+++ b/src/functions/number_theoretical/nextprime
@@ -0,0 +1,12 @@
+Function: nextprime
+Section: number_theoretical
+C-Name: nextprime
+Prototype: G
+Help: nextprime(x): smallest pseudoprime >= x.
+Description:
+ (gen):int        nextprime($1)
+Doc: finds the smallest pseudoprime (see
+ \tet{ispseudoprime}) greater than or equal to $x$. $x$ can be of any real
+ type. Note that if $x$ is a pseudoprime, this function returns $x$ and not
+ the smallest pseudoprime strictly larger than $x$. To rigorously prove that
+ the result is prime, use \kbd{isprime}.
diff --git a/src/functions/number_theoretical/numbpart b/src/functions/number_theoretical/numbpart
new file mode 100644
index 0000000..8a8568d
--- /dev/null
+++ b/src/functions/number_theoretical/numbpart
@@ -0,0 +1,11 @@
+Function: numbpart
+Section: number_theoretical
+C-Name: numbpart
+Prototype: G
+Help: numbpart(n): number of partitions of n.
+Doc: gives the number of unrestricted partitions of
+ $n$, usually called $p(n)$ in the literature; in other words the number of
+ nonnegative integer solutions to $a+2b+3c+\cdots=n$. $n$ must be of type
+ integer and $n<10^{15}$ (with trivial values $p(n) = 0$ for $n < 0$ and
+ $p(0) = 1$). The algorithm uses the Hardy-Ramanujan-Rademacher formula.
+ To explicitly enumerate them, see \tet{partitions}.
diff --git a/src/functions/number_theoretical/numdiv b/src/functions/number_theoretical/numdiv
new file mode 100644
index 0000000..2106f7a
--- /dev/null
+++ b/src/functions/number_theoretical/numdiv
@@ -0,0 +1,9 @@
+Function: numdiv
+Section: number_theoretical
+C-Name: numdiv
+Prototype: G
+Help: numdiv(x): number of divisors of x.
+Description:
+ (gen):int        numdiv($1)
+Doc: number of divisors of $|x|$. $x$ must be of type integer.
+
diff --git a/src/functions/number_theoretical/omega b/src/functions/number_theoretical/omega
new file mode 100644
index 0000000..162a12f
--- /dev/null
+++ b/src/functions/number_theoretical/omega
@@ -0,0 +1,18 @@
+Function: omega
+Section: number_theoretical
+C-Name: omega
+Prototype: lG
+Help: omega(x): number of distinct prime divisors of x.
+Doc: number of distinct prime divisors of $|x|$. $x$ must be of type integer.
+ \bprog
+ ? factor(392)
+ %1 =
+ [2 3]
+
+ [7 2]
+
+ ? omega(392)
+ %2 = 2;  \\ without multiplicity
+ ? bigomega(392)
+ %3 = 5;  \\ = 3+2, with multiplicity
+ @eprog
diff --git a/src/functions/number_theoretical/partitions b/src/functions/number_theoretical/partitions
new file mode 100644
index 0000000..280a841
--- /dev/null
+++ b/src/functions/number_theoretical/partitions
@@ -0,0 +1,52 @@
+Function: partitions
+Section: number_theoretical
+C-Name: partitions
+Prototype: LDGDG
+Help: partitions(k,{a=k},{n=k})): vector of partitions of the integer k.
+ You can restrict the length of the partitions with parameter n (n=nmax or
+ n=[nmin,nmax]), or the range of the parts with parameter a (a=amax
+ or a=[amin,amax]). By default remove zeros, but one can set amin=0 to get X of
+ fixed length nmax (=k by default).
+Doc: returns the vector of partitions of the integer $k$ as a sum of positive
+ integers (parts); for $k < 0$, it returns the empty set \kbd{[]}, and for $k
+ = 0$ the trivial partition (no parts). A partition is given by a
+ \typ{VECSMALL}, where parts are sorted in nondecreasing order:
+ \bprog
+ ? partitions(3)
+ %1 = [Vecsmall([3]), Vecsmall([1, 2]), Vecsmall([1, 1, 1])]
+ @eprog\noindent correspond to $3$, $1+2$ and $1+1+1$. The number
+ of (unrestricted) partitions of $k$ is given
+ by \tet{numbpart}:
+ \bprog
+ ? #partitions(50)
+ %1 = 204226
+ ? numbpart(50)
+ %2 = 204226
+ @eprog
+
+ \noindent Optional parameters $n$ and $a$ are as follows:
+
+ \item $n=\var{nmax}$ (resp. $n=[\var{nmin},\var{nmax}]$) restricts
+ partitions to length less than $\var{nmax}$ (resp. length between
+ $\var{nmin}$ and $nmax$), where the \emph{length} is the number of nonzero
+ entries.
+
+ \item $a=\var{amax}$ (resp. $a=[\var{amin},\var{amax}]$) restricts the parts
+ to integers less than $\var{amax}$ (resp. between $\var{amin}$ and
+ $\var{amax}$).
+ \bprog
+ ? partitions(4, 2)  \\ parts bounded by 2
+ %1 = [Vecsmall([2, 2]), Vecsmall([1, 1, 2]), Vecsmall([1, 1, 1, 1])]
+ ? partitions(4,, 2) \\ at most 2 parts
+ %2 = [Vecsmall([4]), Vecsmall([1, 3]), Vecsmall([2, 2])]
+ ? partitions(4,[0,3], 2) \\ at most 2 parts
+ %3 = [Vecsmall([4]), Vecsmall([1, 3]), Vecsmall([2, 2])]
+ @eprog\noindent
+ By default, parts are positive and we remove zero entries unless
+ $amin\leq0$, in which case $nmin$ is ignored and $X$ is of constant length
+ $\var{nmax}$:
+ \bprog
+ ? partitions(4, [0,3])  \\ parts between 0 and 3
+ %1 = [Vecsmall([0, 0, 1, 3]), Vecsmall([0, 0, 2, 2]),\
+       Vecsmall([0, 1, 1, 2]), Vecsmall([1, 1, 1, 1])]
+ @eprog
diff --git a/src/functions/number_theoretical/polrootsff b/src/functions/number_theoretical/polrootsff
new file mode 100644
index 0000000..f64737d
--- /dev/null
+++ b/src/functions/number_theoretical/polrootsff
@@ -0,0 +1,26 @@
+Function: polrootsff
+Section: number_theoretical
+C-Name: polrootsff
+Prototype: GDGDG
+Help: polrootsff(x,{p},{a}): returns the roots of the polynomial x in the finite
+ field F_p[X]/a(X)F_p[X]. a or p can be omitted if x has t_FFELT coefficients.
+Doc: returns the vector of distinct roots of the polynomial $x$ in the field
+ $\F_q$ defined by the irreducible polynomial $a$ over $\F_p$. The
+ coefficients of $x$ must be operation-compatible with $\Z/p\Z$.
+ Either $a$ or $p$ can omitted (in which case both are ignored) if x has
+ \typ{FFELT} coefficients:
+ \bprog
+ ? polrootsff(x^2 + 1, 5, y^2+3)  \\ over F_5[y]/(y^2+3) ~ F_25
+ %1 = [Mod(Mod(3, 5), Mod(1, 5)*y^2 + Mod(3, 5)),
+       Mod(Mod(2, 5), Mod(1, 5)*y^2 + Mod(3, 5))]
+ ? t = ffgen(y^2 + Mod(3,5), 't); \\ a generator for F_25 as a t_FFELT
+ ? polrootsff(x^2 + 1)   \\ not enough information to determine the base field
+  ***   at top-level: polrootsff(x^2+1)
+  ***                 ^-----------------
+  *** polrootsff: incorrect type in factorff.
+ ? polrootsff(x^2 + t^0) \\ make sure one coeff. is a t_FFELT
+ %3 = [3, 2]
+ ? polrootsff(x^2 + t + 1)
+ %4 = [2*t + 1, 3*t + 4]
+ @eprog\noindent
+ Notice that the second syntax is easier to use and much more readable.
diff --git a/src/functions/number_theoretical/precprime b/src/functions/number_theoretical/precprime
new file mode 100644
index 0000000..d79bae7
--- /dev/null
+++ b/src/functions/number_theoretical/precprime
@@ -0,0 +1,12 @@
+Function: precprime
+Section: number_theoretical
+C-Name: precprime
+Prototype: G
+Help: precprime(x): largest pseudoprime <= x, 0 if x<=1.
+Description:
+ (gen):int        precprime($1)
+Doc: finds the largest pseudoprime (see
+ \tet{ispseudoprime}) less than or equal to $x$. $x$ can be of any real type.
+ Returns 0 if $x\le1$. Note that if $x$ is a prime, this function returns $x$
+ and not the largest prime strictly smaller than $x$. To rigorously prove that
+ the result is prime, use \kbd{isprime}.
diff --git a/src/functions/number_theoretical/prime b/src/functions/number_theoretical/prime
new file mode 100644
index 0000000..ebebdc6
--- /dev/null
+++ b/src/functions/number_theoretical/prime
@@ -0,0 +1,10 @@
+Function: prime
+Section: number_theoretical
+C-Name: prime
+Prototype: L
+Help: prime(n): returns the n-th prime (n C-integer).
+Doc: the $n^{\text{th}}$ prime number
+ \bprog
+ ? prime(10^9)
+ %1 = 22801763489
+ @eprog\noindent Uses checkpointing and a naive $O(n)$ algorithm.
diff --git a/src/functions/number_theoretical/primepi b/src/functions/number_theoretical/primepi
new file mode 100644
index 0000000..a6ea13b
--- /dev/null
+++ b/src/functions/number_theoretical/primepi
@@ -0,0 +1,17 @@
+Function: primepi
+Section: number_theoretical
+C-Name: primepi
+Prototype: G
+Help: primepi(x): the prime counting function pi(x) = #{p <= x, p prime}.
+Description:
+ (gen):int        primepi($1)
+Doc: the prime counting function. Returns the number of
+ primes $p$, $p \leq x$.
+ \bprog
+ ? primepi(10)
+ %1 = 4;
+ ? primes(5)
+ %2 = [2, 3, 5, 7, 11]
+ ? primepi(10^11)
+ %3 = 4118054813
+ @eprog\noindent Uses checkpointing and a naive $O(x)$ algorithm.
diff --git a/src/functions/number_theoretical/primes b/src/functions/number_theoretical/primes
new file mode 100644
index 0000000..c4edef3
--- /dev/null
+++ b/src/functions/number_theoretical/primes
@@ -0,0 +1,17 @@
+Function: primes
+Section: number_theoretical
+C-Name: primes0
+Prototype: G
+Help: primes(n): returns the vector of the first n primes (integer), or the
+ primes in interval n = [a,b].
+Doc: creates a row vector whose components are the first $n$ prime numbers.
+ (Returns the empty vector for $n \leq 0$.) A \typ{VEC} $n = [a,b]$ is also
+ allowed, in which case the primes in $[a,b]$ are returned
+ \bprog
+ ? primes(10)     \\ the first 10 primes
+ %1 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+ ? primes([0,29])  \\ the primes up to 29
+ %2 = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+ ? primes([15,30])
+ %3 = [17, 19, 23, 29]
+ @eprog
diff --git a/src/functions/number_theoretical/qfbclassno b/src/functions/number_theoretical/qfbclassno
new file mode 100644
index 0000000..c3df308
--- /dev/null
+++ b/src/functions/number_theoretical/qfbclassno
@@ -0,0 +1,62 @@
+Function: qfbclassno
+Section: number_theoretical
+C-Name: qfbclassno0
+Prototype: GD0,L,
+Help: qfbclassno(D,{flag=0}): class number of discriminant D using Shanks's
+ method by default. If (optional) flag is set to 1, use Euler products.
+Doc: ordinary class number of the quadratic
+ order of discriminant $D$. In the present version \vers, a $O(D^{1/2})$
+ algorithm is used for $D > 0$ (using Euler product and the functional
+ equation) so $D$ should not be too large, say $D < 10^8$, for the time to be
+ reasonable. On the other hand, for $D < 0$ one can reasonably compute
+ \kbd{qfbclassno($D$)} for $|D|<10^{25}$, since the routine uses
+ \idx{Shanks}'s method which is in $O(|D|^{1/4})$. For larger values of $|D|$,
+ see \kbd{quadclassunit}.
+
+ If $\fl=1$, compute the class number using \idx{Euler product}s and the
+ functional equation. However, it is in $O(|D|^{1/2})$.
+
+ \misctitle{Important warning} For $D < 0$, this function may give incorrect
+ results when the class group has many cyclic factors,
+ because implementing \idx{Shanks}'s method in full generality slows it down
+ immensely. It is therefore strongly recommended to double-check results using
+ either the version with $\fl = 1$ or the function \kbd{quadclassunit}.
+
+ \misctitle{Warning} Contrary to what its name implies, this routine does not
+ compute the number of classes of binary primitive forms of discriminant $D$,
+ which is equal to the \emph{narrow} class number. The two notions are the same
+ when $D < 0$ or the fundamental unit $\varepsilon$ has negative norm; when $D
+ > 0$ and $N\varepsilon > 0$, the number of classes of forms is twice the
+ ordinary class number. This is a problem which we cannot fix for backward
+ compatibility reasons. Use the following routine if you are only interested
+ in the number of classes of forms:
+ \bprog
+ QFBclassno(D) =
+ qfbclassno(D) * if (D < 0 || norm(quadunit(D)) < 0, 1, 2)
+ @eprog\noindent
+ Here are a few examples:
+ \bprog
+ ? qfbclassno(400000028)
+ time = 3,140 ms.
+ %1 = 1
+ ? quadclassunit(400000028).no
+ time = 20 ms. \\@com{ much faster}
+ %2 = 1
+ ? qfbclassno(-400000028)
+ time = 0 ms.
+ %3 = 7253 \\@com{ correct, and fast enough}
+ ? quadclassunit(-400000028).no
+ time = 0 ms.
+ %4 = 7253
+ @eprog\noindent
+ See also \kbd{qfbhclassno}.
+Variant: The following functions are also available:
+
+ \fun{GEN}{classno}{GEN D} ($\fl = 0$)
+
+ \fun{GEN}{classno2}{GEN D} ($\fl = 1$).
+
+ \noindent Finally
+
+ \fun{GEN}{hclassno}{GEN D} computes the class number of an imaginary
+ quadratic field by counting reduced forms, an $O(|D|)$ algorithm.
diff --git a/src/functions/number_theoretical/qfbcompraw b/src/functions/number_theoretical/qfbcompraw
new file mode 100644
index 0000000..0400240
--- /dev/null
+++ b/src/functions/number_theoretical/qfbcompraw
@@ -0,0 +1,10 @@
+Function: qfbcompraw
+Section: number_theoretical
+C-Name: qfbcompraw
+Prototype: GG
+Help: qfbcompraw(x,y): Gaussian composition without reduction of the binary
+ quadratic forms x and y.
+Doc: \idx{composition} of the binary quadratic forms $x$ and $y$, without
+ \idx{reduction} of the result. This is useful e.g.~to compute a generating
+ element of an ideal. The result is undefined if $x$ and $y$ do not have the
+ same discriminant.
diff --git a/src/functions/number_theoretical/qfbhclassno b/src/functions/number_theoretical/qfbhclassno
new file mode 100644
index 0000000..8a80e47
--- /dev/null
+++ b/src/functions/number_theoretical/qfbhclassno
@@ -0,0 +1,9 @@
+Function: qfbhclassno
+Section: number_theoretical
+C-Name: hclassno
+Prototype: G
+Help: qfbhclassno(x): Hurwitz-Kronecker class number of x>0.
+Doc: \idx{Hurwitz class number} of $x$, where
+ $x$ is non-negative and congruent to 0 or 3 modulo 4. For $x > 5\cdot
+ 10^5$, we assume the GRH, and use \kbd{quadclassunit} with default
+ parameters.
diff --git a/src/functions/number_theoretical/qfbnucomp b/src/functions/number_theoretical/qfbnucomp
new file mode 100644
index 0000000..bc9db59
--- /dev/null
+++ b/src/functions/number_theoretical/qfbnucomp
@@ -0,0 +1,18 @@
+Function: qfbnucomp
+Section: number_theoretical
+C-Name: nucomp
+Prototype: GGG
+Help: qfbnucomp(x,y,L): composite of primitive positive definite quadratic
+ forms x and y using nucomp and nudupl, where L=[|D/4|^(1/4)] is precomputed.
+Doc: \idx{composition} of the primitive positive
+ definite binary quadratic forms $x$ and $y$ (type \typ{QFI}) using the NUCOMP
+ and NUDUPL algorithms of \idx{Shanks}, \`a la Atkin. $L$ is any positive
+ constant, but for optimal speed, one should take $L=|D|^{1/4}$, where $D$ is
+ the common discriminant of $x$ and $y$. When $x$ and $y$ do not have the same
+ discriminant, the result is undefined.
+
+ The current implementation is straightforward and in general \emph{slower}
+ than the generic routine (since the latter takes advantage of asymptotically
+ fast operations and careful optimizations).
+
+Variant: Also available is \fun{GEN}{nudupl}{GEN x, GEN L} when $x=y$.
diff --git a/src/functions/number_theoretical/qfbnupow b/src/functions/number_theoretical/qfbnupow
new file mode 100644
index 0000000..caaf02f
--- /dev/null
+++ b/src/functions/number_theoretical/qfbnupow
@@ -0,0 +1,9 @@
+Function: qfbnupow
+Section: number_theoretical
+C-Name: nupow
+Prototype: GG
+Help: qfbnupow(x,n): n-th power of primitive positive definite quadratic
+ form x using nucomp and nudupl.
+Doc: $n$-th power of the primitive positive definite
+ binary quadratic form $x$ using \idx{Shanks}'s NUCOMP and NUDUPL algorithms
+ (see \kbd{qfbnucomp}, in particular the final warning).
diff --git a/src/functions/number_theoretical/qfbpowraw b/src/functions/number_theoretical/qfbpowraw
new file mode 100644
index 0000000..2937033
--- /dev/null
+++ b/src/functions/number_theoretical/qfbpowraw
@@ -0,0 +1,9 @@
+Function: qfbpowraw
+Section: number_theoretical
+C-Name: qfbpowraw
+Prototype: GL
+Help: qfbpowraw(x,n): n-th power without reduction of the binary quadratic
+ form x.
+Doc: $n$-th power of the binary quadratic form
+ $x$, computed without doing any \idx{reduction} (i.e.~using \kbd{qfbcompraw}).
+ Here $n$ must be non-negative and $n<2^{31}$.
diff --git a/src/functions/number_theoretical/qfbprimeform b/src/functions/number_theoretical/qfbprimeform
new file mode 100644
index 0000000..d3cad55
--- /dev/null
+++ b/src/functions/number_theoretical/qfbprimeform
@@ -0,0 +1,14 @@
+Function: qfbprimeform
+Section: number_theoretical
+C-Name: primeform
+Prototype: GGp
+Help: qfbprimeform(x,p): returns the prime form of discriminant x, whose
+ first coefficient is p.
+Doc: prime binary quadratic form of discriminant
+ $x$ whose first coefficient is $p$, where $|p|$ is a prime number.
+ By abuse of notation,
+ $p = \pm 1$ is also valid and returns the unit form. Returns an
+ error if $x$ is not a quadratic residue mod $p$, or if $x < 0$ and $p < 0$.
+ (Negative definite \typ{QFI} are not implemented.) In the case where $x>0$,
+ the ``distance'' component of the form is set equal to zero according to the
+ current precision.
diff --git a/src/functions/number_theoretical/qfbred b/src/functions/number_theoretical/qfbred
new file mode 100644
index 0000000..a65cd70
--- /dev/null
+++ b/src/functions/number_theoretical/qfbred
@@ -0,0 +1,36 @@
+Function: qfbred
+Section: number_theoretical
+C-Name: qfbred0
+Prototype: GD0,L,DGDGDG
+Help: qfbred(x,{flag=0},{d},{isd},{sd}): reduction of the binary
+ quadratic form x. All other args. are optional. The arguments d, isd and
+ sd, if
+ present, supply the values of the discriminant, floor(sqrt(d)) and sqrt(d)
+ respectively. If d<0, its value is not used and all references to Shanks's
+ distance hereafter are meaningless. flag can be any of 0: default, uses
+ Shanks's distance function d; 1: use d, do a single reduction step; 2: do
+ not use d; 3: do not use d, single reduction step.
+Doc: reduces the binary quadratic form $x$ (updating Shanks's distance function
+ if $x$ is indefinite). The binary digits of $\fl$ are toggles meaning
+
+ \quad 1: perform a single \idx{reduction} step
+
+ \quad 2: don't update \idx{Shanks}'s distance
+
+ The arguments $d$, \var{isd}, \var{sd}, if present, supply the values of the
+ discriminant, $\floor{\sqrt{d}}$, and $\sqrt{d}$ respectively
+ (no checking is done of these facts). If $d<0$ these values are useless,
+ and all references to Shanks's distance are irrelevant.
+Variant: Also available are
+
+ \fun{GEN}{redimag}{GEN x} (for definite $x$),
+
+ \noindent and for indefinite forms:
+
+ \fun{GEN}{redreal}{GEN x}
+
+ \fun{GEN}{rhoreal}{GEN x} (= \kbd{qfbred(x,1)}),
+
+ \fun{GEN}{redrealnod}{GEN x, GEN isd} (= \kbd{qfbred(x,2,,isd)}),
+
+ \fun{GEN}{rhorealnod}{GEN x, GEN isd} (= \kbd{qfbred(x,3,,isd)}).
diff --git a/src/functions/number_theoretical/qfbsolve b/src/functions/number_theoretical/qfbsolve
new file mode 100644
index 0000000..11585fc
--- /dev/null
+++ b/src/functions/number_theoretical/qfbsolve
@@ -0,0 +1,18 @@
+Function: qfbsolve
+Section: number_theoretical
+C-Name: qfbsolve
+Prototype: GG
+Help: qfbsolve(Q,p): Return [x,y] so that Q(x,y)=p where Q is a binary
+ quadratic form and p a prime number, or 0 if there is no solution.
+Doc: Solve the equation $Q(x,y)=p$ over the integers,
+ where $Q$ is a binary quadratic form and $p$ a prime number.
+
+ Return $[x,y]$ as a two-components vector, or zero if there is no solution.
+ Note that this function returns only one solution and not all the solutions.
+
+ Let $D = \disc Q$. The algorithm used runs in probabilistic polynomial time
+ in $p$ (through the computation of a square root of $D$ modulo $p$); it is
+ polynomial time in $D$ if $Q$ is imaginary, but exponential time if $Q$ is
+ real (through the computation of a full cycle of reduced forms). In the
+ latter case, note that \tet{bnfisprincipal} provides a solution in heuristic
+ subexponential time in $D$ assuming the GRH.
diff --git a/src/functions/number_theoretical/quadclassunit b/src/functions/number_theoretical/quadclassunit
new file mode 100644
index 0000000..9d8fa3a
--- /dev/null
+++ b/src/functions/number_theoretical/quadclassunit
@@ -0,0 +1,57 @@
+Function: quadclassunit
+Section: number_theoretical
+C-Name: quadclassunit0
+Prototype: GD0,L,DGp
+Help: quadclassunit(D,{flag=0},{tech=[]}): compute the structure of the
+ class group and the regulator of the quadratic field of discriminant D.
+ See manual for the optional technical parameters.
+Doc: \idx{Buchmann-McCurley}'s sub-exponential algorithm for computing the
+ class group of a quadratic order of discriminant $D$.
+
+ This function should be used instead of \tet{qfbclassno} or \tet{quadregula}
+ when $D<-10^{25}$, $D>10^{10}$, or when the \emph{structure} is wanted. It
+ is a special case of \tet{bnfinit}, which is slower, but more robust.
+
+ The result is a vector $v$ whose components should be accessed using member
+ functions:
+
+ \item \kbd{$v$.no}: the class number
+
+ \item \kbd{$v$.cyc}: a vector giving the structure of the class group as a
+ product of cyclic groups;
+
+ \item \kbd{$v$.gen}: a vector giving generators of those cyclic groups (as
+ binary quadratic forms).
+
+ \item \kbd{$v$.reg}: the regulator, computed to an accuracy which is the
+ maximum of an internal accuracy determined by the program and the current
+ default (note that once the regulator is known to a small accuracy it is
+ trivial to compute it to very high accuracy, see the tutorial).
+
+ The $\fl$ is obsolete and should be left alone. In older versions,
+ it supposedly computed the narrow class group when $D>0$, but this did not
+ work at all; use the general function \tet{bnfnarrow}.
+
+ Optional parameter \var{tech} is a row vector of the form $[c_1, c_2]$,
+ where $c_1 \leq c_2$ are non-negative real numbers which control the execution
+ time and the stack size, see \ref{se:GRHbnf}. The parameter is used as a
+ threshold to balance the relation finding phase against the final linear
+ algebra. Increasing the default $c_1$ means that relations are easier
+ to find, but more relations are needed and the linear algebra will be
+ harder. The default value for $c_1$ is $0$ and means that it is taken equal
+ to $c_2$. The parameter $c_2$ is mostly obsolete and should not be changed,
+ but we still document it for completeness: we compute a tentative class
+ group by generators and relations using a factorbase of prime ideals
+ $\leq c_1 (\log |D|)^2$, then prove that ideals of norm
+ $\leq c_2 (\log |D|)^2$ do
+ not generate a larger group. By default an optimal $c_2$ is chosen, so that
+ the result is provably correct under the GRH --- a famous result of Bach
+ states that $c_2 = 6$ is fine, but it is possible to improve on this
+ algorithmically. You may provide a smaller $c_2$, it will be ignored
+ (we use the provably correct
+ one); you may provide a larger $c_2$ than the default value, which results
+ in longer computing times for equally correct outputs (under GRH).
+Variant: If you really need to experiment with the \var{tech} parameter, it is
+ usually more convenient to use
+ \fun{GEN}{Buchquad}{GEN D, double c1, double c2, long prec}
+
diff --git a/src/functions/number_theoretical/quaddisc b/src/functions/number_theoretical/quaddisc
new file mode 100644
index 0000000..7b776ff
--- /dev/null
+++ b/src/functions/number_theoretical/quaddisc
@@ -0,0 +1,6 @@
+Function: quaddisc
+Section: number_theoretical
+C-Name: quaddisc
+Prototype: G
+Help: quaddisc(x): discriminant of the quadratic field Q(sqrt(x)).
+Doc: discriminant of the quadratic field $\Q(\sqrt{x})$, where $x\in\Q$.
diff --git a/src/functions/number_theoretical/quadgen b/src/functions/number_theoretical/quadgen
new file mode 100644
index 0000000..4a23dec
--- /dev/null
+++ b/src/functions/number_theoretical/quadgen
@@ -0,0 +1,10 @@
+Function: quadgen
+Section: number_theoretical
+C-Name: quadgen
+Prototype: G
+Help: quadgen(D): standard generator of quadratic order of discriminant D.
+Doc: creates the quadratic
+ number\sidx{omega} $\omega=(a+\sqrt{D})/2$ where $a=0$ if $D\equiv0\mod4$,
+ $a=1$ if $D\equiv1\mod4$, so that $(1,\omega)$ is an integral basis for the
+ quadratic order of discriminant $D$. $D$ must be an integer congruent to 0 or
+ 1 modulo 4, which is not a square.
diff --git a/src/functions/number_theoretical/quadhilbert b/src/functions/number_theoretical/quadhilbert
new file mode 100644
index 0000000..7a97b50
--- /dev/null
+++ b/src/functions/number_theoretical/quadhilbert
@@ -0,0 +1,14 @@
+Function: quadhilbert
+Section: number_theoretical
+C-Name: quadhilbert
+Prototype: Gp
+Help: quadhilbert(D): relative equation for the Hilbert class field
+ of the quadratic field of discriminant D (which can also be a bnf).
+Doc: relative equation defining the
+ \idx{Hilbert class field} of the quadratic field of discriminant $D$.
+
+ If $D < 0$, uses complex multiplication (\idx{Schertz}'s variant).
+
+ If $D > 0$ \idx{Stark units} are used and (in rare cases) a
+ vector of extensions may be returned whose compositum is the requested class
+ field. See \kbd{bnrstark} for details.
diff --git a/src/functions/number_theoretical/quadpoly b/src/functions/number_theoretical/quadpoly
new file mode 100644
index 0000000..498a114
--- /dev/null
+++ b/src/functions/number_theoretical/quadpoly
@@ -0,0 +1,10 @@
+Function: quadpoly
+Section: number_theoretical
+C-Name: quadpoly0
+Prototype: GDn
+Help: quadpoly(D,{v='x}): quadratic polynomial corresponding to the
+ discriminant D, in variable v.
+Doc: creates the ``canonical'' quadratic
+ polynomial (in the variable $v$) corresponding to the discriminant $D$,
+ i.e.~the minimal polynomial of $\kbd{quadgen}(D)$. $D$ must be an integer
+ congruent to 0 or 1 modulo 4, which is not a square.
diff --git a/src/functions/number_theoretical/quadray b/src/functions/number_theoretical/quadray
new file mode 100644
index 0000000..07f40aa
--- /dev/null
+++ b/src/functions/number_theoretical/quadray
@@ -0,0 +1,16 @@
+Function: quadray
+Section: number_theoretical
+C-Name: quadray
+Prototype: GGp
+Help: quadray(D,f): relative equation for the ray class field of
+ conductor f for the quadratic field of discriminant D (which can also be a
+ bnf).
+Doc: relative equation for the ray
+ class field of conductor $f$ for the quadratic field of discriminant $D$
+ using analytic methods. A \kbd{bnf} for $x^2 - D$ is also accepted in place
+ of $D$.
+
+ For $D < 0$, uses the $\sigma$ function and Schertz's method.
+
+ For $D>0$, uses Stark's conjecture, and a vector of relative equations may be
+ returned. See \tet{bnrstark} for more details.
diff --git a/src/functions/number_theoretical/quadregulator b/src/functions/number_theoretical/quadregulator
new file mode 100644
index 0000000..1f37a66
--- /dev/null
+++ b/src/functions/number_theoretical/quadregulator
@@ -0,0 +1,9 @@
+Function: quadregulator
+Section: number_theoretical
+C-Name: quadregulator
+Prototype: Gp
+Help: quadregulator(x): regulator of the real quadratic field of
+ discriminant x.
+Doc: regulator of the quadratic field of positive discriminant $x$. Returns
+ an error if $x$ is not a discriminant (fundamental or not) or if $x$ is a
+ square. See also \kbd{quadclassunit} if $x$ is large.
diff --git a/src/functions/number_theoretical/quadunit b/src/functions/number_theoretical/quadunit
new file mode 100644
index 0000000..9c7d2e2
--- /dev/null
+++ b/src/functions/number_theoretical/quadunit
@@ -0,0 +1,12 @@
+Function: quadunit
+Section: number_theoretical
+C-Name: quadunit
+Prototype: G
+Help: quadunit(D): fundamental unit of the quadratic field of discriminant D
+ where D must be positive.
+Doc: fundamental unit\sidx{fundamental units} of the
+ real quadratic field $\Q(\sqrt D)$ where  $D$ is the positive discriminant
+ of the field. If $D$ is not a fundamental discriminant, this probably gives
+ the fundamental unit of the corresponding order. $D$ must be an integer
+ congruent to 0 or 1 modulo 4, which is not a square; the result is a
+ quadratic number (see \secref{se:quadgen}).
diff --git a/src/functions/number_theoretical/randomprime b/src/functions/number_theoretical/randomprime
new file mode 100644
index 0000000..bb69eb5
--- /dev/null
+++ b/src/functions/number_theoretical/randomprime
@@ -0,0 +1,10 @@
+Function: randomprime
+Section: number_theoretical
+C-Name: randomprime
+Prototype: DG
+Help: randomprime({N = 2^31}): returns a strong pseudo prime in [2, N-1].
+Doc: returns a strong pseudo prime (see \tet{ispseudoprime}) in $[2,N-1]$.
+ A \typ{VEC} $N = [a,b]$ is also allowed, with $a \leq b$ in which case a
+ pseudo prime $a \leq p \leq b$ is returned; if no prime exists in the
+ interval, the function will run into an infinite loop. If the upper bound
+ is less than $2^{64}$ the pseudo prime returned is a proven prime.
diff --git a/src/functions/number_theoretical/removeprimes b/src/functions/number_theoretical/removeprimes
new file mode 100644
index 0000000..bd186cd
--- /dev/null
+++ b/src/functions/number_theoretical/removeprimes
@@ -0,0 +1,10 @@
+Function: removeprimes
+Section: number_theoretical
+C-Name: removeprimes
+Prototype: DG
+Help: removeprimes({x=[]}): remove primes in the vector x from the prime table.
+ x can also be a single integer. List the current extra primes if x is omitted.
+Doc: removes the primes listed in $x$ from
+ the prime number table. In particular \kbd{removeprimes(addprimes())} empties
+ the extra prime table. $x$ can also be a single integer. List the current
+ extra primes if $x$ is omitted.
diff --git a/src/functions/number_theoretical/sigma b/src/functions/number_theoretical/sigma
new file mode 100644
index 0000000..e30002e
--- /dev/null
+++ b/src/functions/number_theoretical/sigma
@@ -0,0 +1,12 @@
+Function: sigma
+Section: number_theoretical
+C-Name: sumdivk
+Prototype: GD1,L,
+Help: sigma(x,{k=1}): sum of the k-th powers of the divisors of x. k is
+ optional and if omitted is assumed to be equal to 1.
+Description:
+ (gen, ?1):int           sumdiv($1)
+ (gen, 0):int            numdiv($1)
+Doc: sum of the $k^{\text{th}}$ powers of the positive divisors of $|x|$. $x$
+ and $k$ must be of type integer.
+Variant: Also available is \fun{GEN}{sumdiv}{GEN n}, for $k = 1$.
diff --git a/src/functions/number_theoretical/sqrtint b/src/functions/number_theoretical/sqrtint
new file mode 100644
index 0000000..12d0e2e
--- /dev/null
+++ b/src/functions/number_theoretical/sqrtint
@@ -0,0 +1,15 @@
+Function: sqrtint
+Section: number_theoretical
+C-Name: sqrtint
+Prototype: G
+Help: sqrtint(x): integer square root of x, where x is a non-negative integer.
+Description:
+ (gen):int sqrtint($1)
+Doc: returns the integer square root of $x$, i.e. the largest integer $y$
+ such that $y^2 \leq x$, where $x$ a non-negative integer.
+ \bprog
+ ? N = 120938191237; sqrtint(N)
+ %1 = 347761
+ ? sqrt(N)
+ %2 = 347761.68741970412747602130964414095216
+ @eprog
diff --git a/src/functions/number_theoretical/sqrtnint b/src/functions/number_theoretical/sqrtnint
new file mode 100644
index 0000000..e1b7a5e
--- /dev/null
+++ b/src/functions/number_theoretical/sqrtnint
@@ -0,0 +1,15 @@
+Function: sqrtnint
+Section: number_theoretical
+C-Name: sqrtnint
+Prototype: GL
+Help: sqrtnint(x,n): integer n-th root of x, where x is non-negative integer.
+Description:
+ (gen,small):int sqrtnint($1, $2)
+Doc: returns the integer $n$-th root of $x$, i.e. the largest integer $y$ such
+ that $y^n \leq x$, where $x$ is a non-negative integer.
+ \bprog
+ ? N = 120938191237; sqrtnint(N, 5)
+ %1 = 164
+ ? N^(1/5)
+ %2 = 164.63140849829660842958614676939677391
+ @eprog\noindent The special case $n = 2$ is \tet{sqrtint}
diff --git a/src/functions/number_theoretical/stirling b/src/functions/number_theoretical/stirling
new file mode 100644
index 0000000..6f1792e
--- /dev/null
+++ b/src/functions/number_theoretical/stirling
@@ -0,0 +1,31 @@
+Function: stirling
+Section: number_theoretical
+C-Name: stirling
+Prototype: LLD1,L,
+Help: stirling(n,k,{flag=1}): If flag=1 (default) return the Stirling number
+ of the first kind s(n,k), if flag=2, return the Stirling number of the second
+ kind S(n,k).
+Doc: \idx{Stirling number} of the first kind $s(n,k)$ ($\fl=1$, default) or
+ of the second kind $S(n,k)$ (\fl=2), where $n$, $k$ are non-negative
+ integers. The former is $(-1)^{n-k}$ times the
+ number of permutations of $n$ symbols with exactly $k$ cycles; the latter is
+ the number of ways of partitioning a set of $n$ elements into $k$ non-empty
+ subsets. Note that if all $s(n,k)$ are needed, it is much faster to compute
+ $$\sum_k s(n,k) x^k = x(x-1)\dots(x-n+1).$$
+ Similarly, if a large number of $S(n,k)$ are needed for the same $k$,
+ one should use
+ $$\sum_n S(n,k) x^n = \dfrac{x^k}{(1-x)\dots(1-kx)}.$$
+ (Should be implemented using a divide and conquer product.) Here are
+ simple variants for $n$ fixed:
+ \bprog
+ /* list of s(n,k), k = 1..n */
+ vecstirling(n) = Vec( factorback(vector(n-1,i,1-i*'x)) )
+
+ /* list of S(n,k), k = 1..n */
+ vecstirling2(n) =
+ { my(Q = x^(n-1), t);
+   vector(n, i, t = divrem(Q, x-i); Q=t[1]; t[2]);
+ }
+ @eprog
+Variant: Also available are \fun{GEN}{stirling1}{ulong n, ulong k}
+ ($\fl=1$) and \fun{GEN}{stirling2}{ulong n, ulong k} ($\fl=2$).
diff --git a/src/functions/number_theoretical/sumdedekind b/src/functions/number_theoretical/sumdedekind
new file mode 100644
index 0000000..3f6fb72
--- /dev/null
+++ b/src/functions/number_theoretical/sumdedekind
@@ -0,0 +1,10 @@
+Function: sumdedekind
+Section: number_theoretical
+C-Name: sumdedekind
+Prototype: GG
+Help: sumdedekind(h,k): Dedekind sum associated to h,k
+Doc: returns the \idx{Dedekind sum} associated to the integers $h$ and $k$,
+  corresponding to a fast implementation of
+  \bprog
+   s(h,k) = sum(n = 1, k-1, (n/k)*(frac(h*n/k) - 1/2))
+  @eprog
diff --git a/src/functions/number_theoretical/sumdigits b/src/functions/number_theoretical/sumdigits
new file mode 100644
index 0000000..c9257de
--- /dev/null
+++ b/src/functions/number_theoretical/sumdigits
@@ -0,0 +1,11 @@
+Function: sumdigits
+Section: number_theoretical
+C-Name: sumdigits
+Prototype: G
+Help: sumdigits(n): sum of (decimal) digits in the integer n.
+Doc: sum of (decimal) digits in the integer $n$.
+ \bprog
+ ? sumdigits(123456789)
+ %1 = 45
+ @eprog\noindent Other bases that 10 are not supported. Note that the sum of
+ bits in $n$ is returned by \tet{hammingweight}.
diff --git a/src/functions/number_theoretical/zncoppersmith b/src/functions/number_theoretical/zncoppersmith
new file mode 100644
index 0000000..8be0865
--- /dev/null
+++ b/src/functions/number_theoretical/zncoppersmith
@@ -0,0 +1,55 @@
+Function: zncoppersmith
+Section: number_theoretical
+C-Name: zncoppersmith
+Prototype: GGGDG
+Help: zncoppersmith(P, N, X, {B=N}): finds all integers x
+ with |x| <= X such that  gcd(N, P(x)) >= B. X should be smaller than
+ exp((log B)^2 / (deg(P) log N)).
+Doc: $N$ being an integer and $P\in \Z[X]$, finds all integers $x$ with
+ $|x| \leq X$ such that
+ $$\gcd(N, P(x)) \geq B,$$
+ using \idx{Coppersmith}'s algorithm (a famous application of the \idx{LLL}
+ algorithm). $X$ must be smaller than $\exp(\log^2 B / (\deg(P) \log N))$:
+ for $B = N$, this means $X < N^{1/\deg(P)}$. Some $x$ larger than $X$ may
+ be returned if you are very lucky. The smaller $B$ (or the larger $X$), the
+ slower the routine will be. The strength of Coppersmith method is the
+ ability to find roots modulo a general \emph{composite} $N$: if $N$ is a prime
+ or a prime power, \tet{polrootsmod} or \tet{polrootspadic} will be much
+ faster.
+
+ We shall now present two simple applications. The first one is
+ finding non-trivial factors of $N$, given some partial information on the
+ factors; in that case $B$ must obviously be smaller than the largest
+ non-trivial divisor of $N$.
+ \bprog
+ setrand(1); \\ to make the example reproducible
+ p = nextprime(random(10^30));
+ q = nextprime(random(10^30)); N = p*q;
+ p0 = p % 10^20; \\ assume we know 1) p > 10^29, 2) the last 19 digits of p
+ p1 = zncoppersmith(10^19*x + p0, N, 10^12, 10^29)
+
+ \\ result in 10ms.
+ %1 = [35023733690]
+ ? gcd(p1[1] * 10^19 + p0, N) == p
+ %2 = 1
+ @eprog\noindent and we recovered $p$, faster than by trying all
+ possibilities $ < 10^{12}$.
+
+ The second application is an attack on RSA with low exponent, when the
+ message $x$ is short and the padding $P$ is known to the attacker. We use
+ the same RSA modulus $N$ as in the first example:
+ \bprog
+ setrand(1);
+ P = random(N);    \\ known padding
+ e = 3;            \\ small public encryption exponent
+ X = floor(N^0.3); \\ N^(1/e - epsilon)
+ x0 = random(X);   \\ unknown short message
+ C = lift( (Mod(x0,N) + P)^e ); \\ known ciphertext, with padding P
+ zncoppersmith((P + x)^3 - C, N, X)
+
+ \\ result in 244ms.
+ %3 = [265174753892462432]
+ ? %[1] == x0
+ %4 = 1
+ @eprog\noindent
+ We guessed an integer of the order of $10^{18}$, almost instantly.
diff --git a/src/functions/number_theoretical/znlog b/src/functions/number_theoretical/znlog
new file mode 100644
index 0000000..ce9c2c5
--- /dev/null
+++ b/src/functions/number_theoretical/znlog
@@ -0,0 +1,70 @@
+Function: znlog
+Section: number_theoretical
+C-Name: znlog
+Prototype: GGDG
+Help: znlog(x,g,{o}): return the discrete logarithm of x in
+ (Z/nZ)* in base g. If present, o represents the multiplicative
+ order of g. Return [] if no solution exist.
+Doc: discrete logarithm of $x$ in $(\Z/N\Z)^*$ in base $g$.
+ The result is $[]$ when $x$ is not a power of $g$.
+ If present, $o$ represents the multiplicative order of $g$, see
+ \secref{se:DLfun}; the preferred format for this parameter is
+ \kbd{[ord, factor(ord)]}, where \kbd{ord} is the order of $g$.
+ This provides a definite speedup when the discrete log problem is simple:
+ \bprog
+ ? p = nextprime(10^4); g = znprimroot(p); o = [p-1, factor(p-1)];
+ ? for(i=1,10^4, znlog(i, g, o))
+ time = 205 ms.
+ ? for(i=1,10^4, znlog(i, g))
+ time = 244 ms. \\ a little slower
+ @eprog
+
+ The result is undefined if $g$ is not invertible mod $N$ or if the supplied
+ order is incorrect.
+
+ This function uses
+
+ \item a combination of generic discrete log algorithms (see below).
+
+ \item in $(\Z/N\Z)^*$ when $N$ is prime: a linear sieve index calculus
+ method, suitable for $N < 10^{50}$, say, is used for large prime divisors of
+ the order.
+
+ The generic discrete log algorithms are:
+
+ \item Pohlig-Hellman algorithm, to reduce to groups of prime order $q$,
+ where $q | p-1$ and $p$ is an odd prime divisor of $N$,
+
+ \item Shanks baby-step/giant-step ($q < 2^{32}$ is small),
+
+ \item Pollard rho method ($q > 2^{32}$).
+
+ The latter two algorithms require $O(\sqrt{q})$ operations in the group on
+ average, hence will not be able to treat cases where $q > 10^{30}$, say.
+ In addition, Pollard rho is not able to handle the case where there are no
+ solutions: it will enter an infinite loop.
+ \bprog
+ ? g = znprimroot(101)
+ %1 = Mod(2,101)
+ ? znlog(5, g)
+ %2 = 24
+ ? g^24
+ %3 = Mod(5, 101)
+
+ ? G = znprimroot(2 * 101^10)
+ %4 = Mod(110462212541120451003, 220924425082240902002)
+ ? znlog(5, G)
+ %5 = 76210072736547066624
+ ? G^% == 5
+ %6 = 1
+ ? N = 2^4*3^2*5^3*7^4*11; g = Mod(13, N); znlog(g^110, g)
+ %7 = 110
+ ? znlog(6, Mod(2,3))  \\ no solution
+ %8 = []
+ @eprog\noindent For convenience, $g$ is also allowed to be a $p$-adic number:
+ \bprog
+ ? g = 3+O(5^10); znlog(2, g)
+ %1 = 1015243
+ ? g^%
+ %2 = 2 + O(5^10)
+ @eprog
diff --git a/src/functions/number_theoretical/znorder b/src/functions/number_theoretical/znorder
new file mode 100644
index 0000000..b440475
--- /dev/null
+++ b/src/functions/number_theoretical/znorder
@@ -0,0 +1,18 @@
+Function: znorder
+Section: number_theoretical
+C-Name: znorder
+Prototype: GDG
+Help: znorder(x,{o}): order of the integermod x in (Z/nZ)*.
+ Optional o represents a multiple of the order of the element.
+Description:
+ (gen):int             order($1)
+ (gen,):int            order($1)
+ (gen,int):int         znorder($1, $2)
+Doc: $x$ must be an integer mod $n$, and the
+ result is the order of $x$ in the multiplicative group $(\Z/n\Z)^*$. Returns
+ an error if $x$ is not invertible.
+ The parameter o, if present, represents a non-zero
+ multiple of the order of $x$, see \secref{se:DLfun}; the preferred format for
+ this parameter is \kbd{[ord, factor(ord)]}, where \kbd{ord = eulerphi(n)}
+ is the cardinality of the group.
+Variant: Also available is \fun{GEN}{order}{GEN x}.
diff --git a/src/functions/number_theoretical/znprimroot b/src/functions/number_theoretical/znprimroot
new file mode 100644
index 0000000..09ebdcd
--- /dev/null
+++ b/src/functions/number_theoretical/znprimroot
@@ -0,0 +1,14 @@
+Function: znprimroot
+Section: number_theoretical
+C-Name: znprimroot
+Prototype: G
+Help: znprimroot(n): returns a primitive root of n when it exists.
+Doc: returns a primitive root (generator) of $(\Z/n\Z)^*$, whenever this
+ latter group is cyclic ($n = 4$ or $n = 2p^k$ or $n = p^k$, where $p$ is an
+ odd prime and $k \geq 0$). If the group is not cyclic, the result is
+ undefined. If $n$ is a prime power, then the smallest positive primitive
+ root is returned. This may not be true for $n = 2p^k$, $p$ odd.
+
+ Note that this function requires factoring $p-1$ for $p$ as above,
+ in order to determine the exact order of elements in
+ $(\Z/n\Z)^*$: this is likely to be costly if $p$ is large.
diff --git a/src/functions/number_theoretical/znstar b/src/functions/number_theoretical/znstar
new file mode 100644
index 0000000..cfc245e
--- /dev/null
+++ b/src/functions/number_theoretical/znstar
@@ -0,0 +1,26 @@
+Function: znstar
+Section: number_theoretical
+C-Name: znstar
+Prototype: G
+Help: znstar(n): 3-component vector v, giving the structure of (Z/nZ)^*.
+ v[1] is the order (i.e. eulerphi(n)), v[2] is a vector of cyclic components,
+ and v[3] is a vector giving the corresponding generators.
+Doc: gives the structure of the multiplicative group
+ $(\Z/n\Z)^*$ as a 3-component row vector $v$, where $v[1]=\phi(n)$ is the
+ order of that group, $v[2]$ is a $k$-component row-vector $d$ of integers
+ $d[i]$ such that $d[i]>1$ and $d[i]\mid d[i-1]$ for $i \ge 2$ and
+ $(\Z/n\Z)^* \simeq \prod_{i=1}^k(\Z/d[i]\Z)$, and $v[3]$ is a $k$-component row
+ vector giving generators of the image of the cyclic groups $\Z/d[i]\Z$.
+ \bprog
+ ? G = znstar(40)
+ %1 = [16, [4, 2, 2], [Mod(17, 40), Mod(21, 40), Mod(11, 40)]]
+ ? G.no   \\ eulerphi(40)
+ %2 = 16
+ ? G.cyc  \\ cycle structure
+ %3 = [4, 2, 2]
+ ? G.gen  \\ generators for the cyclic components
+ %4 = [Mod(17, 40), Mod(21, 40), Mod(11, 40)]
+ ? apply(znorder, G.gen)
+ %5 = [4, 2, 2]
+ @eprog\noindent According to the above definitions, \kbd{znstar(0)} is
+ \kbd{[2, [2], [-1]]}, corresponding to $\Z^*$.
diff --git a/src/functions/operators/cmp b/src/functions/operators/cmp
new file mode 100644
index 0000000..eb65f94
--- /dev/null
+++ b/src/functions/operators/cmp
@@ -0,0 +1,30 @@
+Function: cmp
+Section: operators
+C-Name: cmp_universal
+Prototype: iGG
+Help: cmp(x,y): compare two arbitrary objects x and y (1 if x>y, 0 if x=y, -1
+ if x<y). The function is used to implement sets, and has no useful
+ mathematical meaning.
+Doc: gives the result of a comparison between arbitrary objects $x$ and $y$
+ (as $-1$, $0$ or $1$). The underlying order relation is transitive,
+ the function returns $0$ if and only if $x~\kbd{===}~y$, and its
+ restriction to integers coincides with the customary one. Besides that,
+ it has no useful mathematical meaning.
+
+ In case all components are equal up to the smallest length of the operands,
+ the more complex is considered to be larger. More precisely, the longest is
+ the largest; when lengths are equal, we have matrix $>$ vector $>$ scalar.
+ For example:
+ \bprog
+ ? cmp(1, 2)
+ %1 = -1
+ ? cmp(2, 1)
+ %2 = 1
+ ? cmp(1, 1.0)   \\ note that 1 == 1.0, but (1===1.0) is false.
+ %3 = -1
+ ? cmp(x + Pi, [])
+ %4 = -1
+ @eprog\noindent This function is mostly useful to handle sorted lists or
+ vectors of arbitrary objects. For instance, if $v$ is a vector, the
+ construction \kbd{vecsort(v, cmp)} is equivalent to \kbd{Set(v)}.
+
diff --git a/src/functions/operators/divrem b/src/functions/operators/divrem
new file mode 100644
index 0000000..e3caefb
--- /dev/null
+++ b/src/functions/operators/divrem
@@ -0,0 +1,30 @@
+Function: divrem
+Section: operators
+C-Name: divrem
+Prototype: GGDn
+Help: divrem(x,y,{v}): euclidean division of x by y giving as a
+ 2-dimensional column vector the quotient and the remainder, with respect to
+ v (to main variable if v is omitted)
+Doc: creates a column vector with two components, the first being the Euclidean
+ quotient (\kbd{$x$ \bs\ $y$}), the second the Euclidean remainder
+ (\kbd{$x$ - ($x$\bs$y$)*$y$}), of the division of $x$ by $y$. This avoids the
+ need to do two divisions if one needs both the quotient and the remainder.
+ If $v$ is present, and $x$, $y$ are multivariate
+ polynomials, divide with respect to the variable $v$.
+
+ Beware that \kbd{divrem($x$,$y$)[2]} is in general not the same as
+ \kbd{$x$ \% $y$}; no GP operator corresponds to it:
+ \bprog
+ ? divrem(1/2, 3)[2]
+ %1 = 1/2
+ ? (1/2) % 3
+ %2 = 2
+ ? divrem(Mod(2,9), 3)[2]
+  ***   at top-level: divrem(Mod(2,9),3)[2
+  ***                 ^--------------------
+  ***   forbidden division t_INTMOD \ t_INT.
+ ? Mod(2,9) % 6
+ %3 = Mod(2,3)
+ @eprog
+Variant: Also available is \fun{GEN}{gdiventres}{GEN x, GEN y} when $v$ is
+ not needed.
diff --git a/src/functions/operators/lex b/src/functions/operators/lex
new file mode 100644
index 0000000..655304c
--- /dev/null
+++ b/src/functions/operators/lex
@@ -0,0 +1,26 @@
+Function: lex
+Section: operators
+C-Name: lexcmp
+Prototype: iGG
+Help: lex(x,y): compare x and y lexicographically (1 if x>y, 0 if x=y, -1 if
+ x<y)
+Doc: gives the result of a lexicographic comparison
+ between $x$ and $y$ (as $-1$, $0$ or $1$). This is to be interpreted in quite
+ a wide sense: It is admissible to compare objects of different types
+ (scalars, vectors, matrices), provided the scalars can be compared, as well
+ as vectors/matrices of different lengths. The comparison is recursive.
+
+ In case all components are equal up to the smallest length of the operands,
+ the more complex is considered to be larger. More precisely, the longest is
+ the largest; when lengths are equal, we have matrix $>$ vector $>$ scalar.
+ For example:
+ \bprog
+ ? lex([1,3], [1,2,5])
+ %1 = 1
+ ? lex([1,3], [1,3,-1])
+ %2 = -1
+ ? lex([1], [[1]])
+ %3 = -1
+ ? lex([1], [1]~)
+ %4 = 0
+ @eprog
diff --git a/src/functions/operators/max b/src/functions/operators/max
new file mode 100644
index 0000000..e545fe0
--- /dev/null
+++ b/src/functions/operators/max
@@ -0,0 +1,17 @@
+Function: max
+Section: operators
+C-Name: gmax
+Prototype: GG
+Help: max(x,y): maximum of x and y
+Description:
+ (small, small):small  maxss($1, $2)
+ (small, int):int      gmaxsg($1, $2)
+ (int, small):int      gmaxgs($1, $2)
+ (int, int):int        gmax($1, $2)
+ (small, mp):mp        gmaxsg($1, $2)
+ (mp, small):mp        gmaxgs($1, $2)
+ (mp, mp):mp           gmax($1, $2)
+ (small, gen):gen      gmaxsg($1, $2)
+ (gen, small):gen      gmaxgs($1, $2)
+ (gen, gen):gen        gmax($1, $2)
+Doc: creates the maximum of $x$ and $y$ when they can be compared.
diff --git a/src/functions/operators/min b/src/functions/operators/min
new file mode 100644
index 0000000..fa87519
--- /dev/null
+++ b/src/functions/operators/min
@@ -0,0 +1,17 @@
+Function: min
+Section: operators
+C-Name: gmin
+Prototype: GG
+Help: min(x,y): minimum of x and y
+Description:
+ (small, small):small  minss($1, $2)
+ (small, int):int      gminsg($1, $2)
+ (int, small):int      gmings($1, $2)
+ (int, int):int        gmin($1, $2)
+ (small, mp):mp        gminsg($1, $2)
+ (mp, small):mp        gmings($1, $2)
+ (mp, mp):mp           gmin($1, $2)
+ (small, gen):gen      gminsg($1, $2)
+ (gen, small):gen      gmings($1, $2)
+ (gen, gen):gen        gmin($1, $2)
+Doc: creates the minimum of $x$ and $y$ when they can be compared.
diff --git a/src/functions/operators/shift b/src/functions/operators/shift
new file mode 100644
index 0000000..ca9e8f1
--- /dev/null
+++ b/src/functions/operators/shift
@@ -0,0 +1,12 @@
+Function: shift
+Section: operators
+C-Name: gshift
+Prototype: GL
+Help: shift(x,n): shift x left n bits if n>=0, right -n bits if
+ n<0.
+Doc: shifts $x$ componentwise left by $n$ bits if $n\ge0$ and right by $|n|$
+ bits if $n<0$. May be abbreviated as $x$ \kbd{<<} $n$ or $x$ \kbd{>>} $(-n)$.
+ A left shift by $n$ corresponds to multiplication by $2^n$. A right shift of an
+ integer $x$ by $|n|$ corresponds to a Euclidean division of $x$ by $2^{|n|}$
+ with a remainder of the same sign as $x$, hence is not the same (in general) as
+ $x \kbd{\bs} 2^n$.
diff --git a/src/functions/operators/shiftmul b/src/functions/operators/shiftmul
new file mode 100644
index 0000000..1c46c54
--- /dev/null
+++ b/src/functions/operators/shiftmul
@@ -0,0 +1,10 @@
+Function: shiftmul
+Section: operators
+C-Name: gmul2n
+Prototype: GL
+Help: shiftmul(x,n): multiply x by 2^n (n>=0 or n<0)
+Doc: multiplies $x$ by $2^n$. The difference with
+ \kbd{shift} is that when $n<0$, ordinary division takes place, hence for
+ example if $x$ is an integer the result may be a fraction, while for shifts
+ Euclidean division takes place when $n<0$ hence if $x$ is an integer the result
+ is still an integer.
diff --git a/src/functions/operators/sign b/src/functions/operators/sign
new file mode 100644
index 0000000..f36b3a4
--- /dev/null
+++ b/src/functions/operators/sign
@@ -0,0 +1,10 @@
+Function: sign
+Section: operators
+C-Name: gsigne
+Prototype: iG
+Help: sign(x): sign of x, of type integer, real or fraction
+Description:
+ (mp):small          signe($1)
+ (gen):small        gsigne($1)
+Doc: \idx{sign} ($0$, $1$ or $-1$) of $x$, which must be of
+ type integer, real or fraction.
diff --git a/src/functions/operators/vecmax b/src/functions/operators/vecmax
new file mode 100644
index 0000000..48a5516
--- /dev/null
+++ b/src/functions/operators/vecmax
@@ -0,0 +1,27 @@
+Function: vecmax
+Section: operators
+C-Name: vecmax0
+Prototype: GD&
+Help: vecmax(x,{&v}): largest entry in the vector/matrix x. If v
+ is present, set it to the index of a largest entry (indirect max).
+Description:
+  (gen):gen            vecmax($1)
+  (gen, &gen):gen      vecmax0($1, &$2)
+Doc: if $x$ is a vector or a matrix, returns the largest entry of $x$,
+ otherwise returns a copy of $x$. Error if $x$ is empty.
+
+ If $v$ is given, set it to the index of a largest entry (indirect maximum),
+ when $x$ is a vector. If $x$ is a matrix, set $v$ to coordinates $[i,j]$
+ such that $x[i,j]$ is a largest entry. This flag is ignored if $x$ is not a
+ vector or matrix.
+
+ \bprog
+ ? vecmax([10, 20, -30, 40])
+ %1 = 40
+ ? vecmax([10, 20, -30, 40], &v); v
+ %2 = 4
+ ? vecmax([10, 20; -30, 40], &v); v
+ %3 = [2, 2]
+ @eprog
+
+Variant: Also available is \fun{GEN}{vecmax}{GEN x}.
diff --git a/src/functions/operators/vecmin b/src/functions/operators/vecmin
new file mode 100644
index 0000000..4d456c1
--- /dev/null
+++ b/src/functions/operators/vecmin
@@ -0,0 +1,28 @@
+Function: vecmin
+Section: operators
+C-Name: vecmin0
+Prototype: GD&
+Help: vecmin(x,{&v}): smallest entry in the vector/matrix x. If v is
+ present, set it to the index of a smallest
+ entry (indirect min).
+Description:
+  (gen):gen            vecmin($1)
+  (gen, &gen):gen      vecmin0($1, &$2)
+Doc: if $x$ is a vector or a matrix, returns the smallest entry of $x$,
+ otherwise returns a copy of $x$. Error if $x$ is empty.
+
+ If $v$ is given, set it to the index of a smallest entry (indirect minimum),
+ when $x$ is a vector. If $x$ is a matrix, set $v$ to coordinates $[i,j]$ such
+ that $x[i,j]$ is a smallest entry. This is ignored if $x$ is not a vector or
+ matrix.
+
+ \bprog
+ ? vecmin([10, 20, -30, 40])
+ %1 = -30
+ ? vecmin([10, 20, -30, 40], &v); v
+ %2 = 3
+ ? vecmin([10, 20; -30, 40], &v); v
+ %3 = [2, 1]
+ @eprog
+
+Variant: Also available is \fun{GEN}{vecmin}{GEN x}.
diff --git a/src/functions/polynomials/O b/src/functions/polynomials/O
new file mode 100644
index 0000000..90b38c4
--- /dev/null
+++ b/src/functions/polynomials/O
@@ -0,0 +1,23 @@
+Function: O(_^_)
+Section: programming/internals
+C-Name: ggrando
+Prototype: GD1,L,
+Help: O(p^e): p-adic or power series zero with precision given by e.
+Description:
+ (gen):gen          ggrando($1, 1)
+ (1,small):gen      ggrando(gen_1, $2)
+ (int,small):gen    zeropadic($1, $2)
+ (gen,small):gen    ggrando($1, $2)
+ (var,small):gen    zeroser($1, $2)
+
+Function: O
+Section: polynomials
+C-Name: ggrando
+Prototype:
+Help: O(p^e): p-adic or power series zero with precision given by e
+Doc: if $p$ is an integer
+ greater than $2$, returns a $p$-adic $0$ of precision $e$. In all other
+ cases, returns a power series zero with precision given by $e v$, where $v$
+ is the $X$-adic valuation of $p$ with respect to its main variable.
+Variant: \fun{GEN}{zeropadic}{GEN p, long e} for a $p$-adic and
+ \fun{GEN}{zeroser}{long v, long e} for a power series zero in variable $v$.
diff --git a/src/functions/polynomials/bezoutres b/src/functions/polynomials/bezoutres
new file mode 100644
index 0000000..79b467e
--- /dev/null
+++ b/src/functions/polynomials/bezoutres
@@ -0,0 +1,6 @@
+Function: bezoutres
+Section: polynomials
+C-Name: polresultantext0
+Prototype: GGDn
+Help: bezoutre(A,B,{v}): deprecated alias for polresultantext
+Doc: deprecated alias for \kbd{polresultantext}
diff --git a/src/functions/polynomials/deriv b/src/functions/polynomials/deriv
new file mode 100644
index 0000000..2dda633
--- /dev/null
+++ b/src/functions/polynomials/deriv
@@ -0,0 +1,18 @@
+Function: deriv
+Section: polynomials
+C-Name: deriv
+Prototype: GDn
+Help: deriv(x,{v}): derivative of x with respect to v, or to the main
+ variable of x if v is omitted.
+Doc:
+ derivative of $x$ with respect to the main
+ variable if $v$ is omitted, and with respect to $v$ otherwise. The derivative
+ of a scalar type is zero, and the derivative of a vector or matrix is done
+ componentwise. One can use $x'$ as a shortcut if the derivative is with
+ respect to the main variable of $x$.
+
+ By definition, the main variable of a \typ{POLMOD} is the main variable among
+ the coefficients from its two polynomial components (representative and
+ modulus); in other words, assuming a polmod represents an element of
+ $R[X]/(T(X))$, the variable $X$ is a mute variable and the derivative is
+ taken with respect to the main variable used in the base ring $R$.
diff --git a/src/functions/polynomials/diffop b/src/functions/polynomials/diffop
new file mode 100644
index 0000000..c6ee983
--- /dev/null
+++ b/src/functions/polynomials/diffop
@@ -0,0 +1,48 @@
+Function: diffop
+Section: polynomials
+C-Name: diffop0
+Prototype: GGGD1,L,
+Description:
+ (gen,gen,gen,?1):gen    diffop($1, $2, $3)
+ (gen,gen,gen,small):gen diffop0($1, $2, $3, $4)
+Help: diffop(x,v,d,{n=1}): apply the differential operator D to x, where D is defined
+ by D(v[i])=d[i], where v is a vector of variable names. D is 0 for variables
+ outside of v unless they appear as modulus of a POLMOD. If the optional parameter n
+ is given, return D^n(x) instead.
+Doc:
+ Let $v$ be a vector of variables, and $d$ a vector of the same length,
+ return the image of $x$ by the $n$-power ($1$ if n is not given) of the differential
+ operator $D$ that assumes the value \kbd{d[i]} on the variable \kbd{v[i]}.
+ The value of $D$ on a scalar type is zero, and $D$ applies componentwise to a vector
+ or matrix. When applied to a \typ{POLMOD}, if no value is provided for the variable
+ of the modulus, such value is derived using the implicit function theorem.
+
+ Some examples:
+ This function can be used to differentiate formal expressions:
+ If $E=\exp(X^2)$ then we have $E'=2*X*E$. We can derivate $X*exp(X^2)$ as follow:
+ \bprog
+ ? diffop(E*X,[X,E],[1,2*X*E])
+ %1 = (2*X^2 + 1)*E
+ @eprog
+ Let \kbd{Sin} and \kbd{Cos} be two function such that $\kbd{Sin}^2+\kbd{Cos}^2=1$
+ and $\kbd{Cos}'=-\kbd{Sin}$. We can differentiate $\kbd{Sin}/\kbd{Cos}$ as follow,
+ PARI inferring the value of $\kbd{Sin}'$ from the equation:
+ \bprog
+ ? diffop(Mod('Sin/'Cos,'Sin^2+'Cos^2-1),['Cos],[-'Sin])
+ %1 = Mod(1/Cos^2, Sin^2 + (Cos^2 - 1))
+
+ @eprog
+ Compute the Bell polynomials (both complete and partial) via the Faa di Bruno formula:
+ \bprog
+ Bell(k,n=-1)=
+ {
+   my(var(i)=eval(Str("X",i)));
+   my(x,v,dv);
+   v=vector(k,i,if(i==1,'E,var(i-1)));
+   dv=vector(k,i,if(i==1,'X*var(1)*'E,var(i)));
+   x=diffop('E,v,dv,k)/'E;
+   if(n<0,subst(x,'X,1),polcoeff(x,n,'X))
+ }
+ @eprog
+Variant:
+ For $n=1$, the function \fun{GEN}{diffop}{GEN x, GEN v, GEN d} is also available.
diff --git a/src/functions/polynomials/eval b/src/functions/polynomials/eval
new file mode 100644
index 0000000..609f25a
--- /dev/null
+++ b/src/functions/polynomials/eval
@@ -0,0 +1,52 @@
+Function: eval
+Section: polynomials
+C-Name: geval_gp
+Prototype: GC
+Help: eval(x): evaluation of x, replacing variables by their value.
+Description:
+ (gen):gen      geval($1)
+Doc: replaces in $x$ the formal variables by the values that
+ have been assigned to them after the creation of $x$. This is mainly useful
+ in GP, and not in library mode. Do not confuse this with substitution (see
+ \kbd{subst}).
+
+ If $x$ is a character string, \kbd{eval($x$)} executes $x$ as a GP
+ command, as if directly input from the keyboard, and returns its
+ output.
+ \bprog
+ ? x1 = "one"; x2 = "two";
+ ? n = 1; eval(Str("x", n))
+ %2 = "one"
+ ? f = "exp"; v = 1;
+ ? eval(Str(f, "(", v, ")"))
+ %4 = 2.7182818284590452353602874713526624978
+ @eprog\noindent Note that the first construct could be implemented in a
+ simpler way by using a vector \kbd{x = ["one","two"]; x[n]}, and the second
+ by using a closure \kbd{f = exp; f(v)}. The final example is more interesting:
+ \bprog
+ ? genmat(u,v) = matrix(u,v,i,j, eval( Str("x",i,j) ));
+ ? genmat(2,3)   \\ generic 2 x 3 matrix
+ %2 =
+ [x11 x12 x13]
+
+ [x21 x22 x23]
+ @eprog
+
+ A syntax error in the evaluation expression raises an \kbd{e\_SYNTAX}
+ exception, which can be trapped as usual:
+ \bprog
+ ? 1a
+  ***   unused characters: 1a
+  ***                       ^-
+ ? E(expr) =
+   {
+     iferr(eval(expr),
+           e, print("syntax error"),
+           errname(e) == "e_SYNTAX");
+   }
+ ? E("1+1")
+ %1 = 2
+ ? E("1a")
+ syntax error
+ @eprog
+ \synt{geval}{GEN x}.
diff --git a/src/functions/polynomials/factorpadic b/src/functions/polynomials/factorpadic
new file mode 100644
index 0000000..d7429e8
--- /dev/null
+++ b/src/functions/polynomials/factorpadic
@@ -0,0 +1,37 @@
+Function: factorpadic
+Section: polynomials
+C-Name: factorpadic0
+Prototype: GGLD0,L,
+Help: factorpadic(pol,p,r): p-adic factorization of the polynomial pol
+ to precision r.
+Doc: $p$-adic factorization
+ of the polynomial \var{pol} to precision $r$, the result being a
+ two-column matrix as in \kbd{factor}. Note that this is not the same
+ as a factorization over $\Z/p^r\Z$ (polynomials over that ring do not form a
+ unique factorization domain, anyway), but approximations in $\Q/p^r\Z$ of
+ the true factorization in $\Q_p[X]$.
+ \bprog
+ ? factorpadic(x^2 + 9, 3,5)
+ %1 =
+ [(1 + O(3^5))*x^2 + O(3^5)*x + (3^2 + O(3^5)) 1]
+ ? factorpadic(x^2 + 1, 5,3)
+ %2 =
+ [  (1 + O(5^3))*x + (2 + 5 + 2*5^2 + O(5^3)) 1]
+
+ [(1 + O(5^3))*x + (3 + 3*5 + 2*5^2 + O(5^3)) 1]
+ @eprog\noindent
+ The factors are normalized so that their leading coefficient is a power of
+ $p$. The method used is a modified version of the \idx{round 4} algorithm of
+ \idx{Zassenhaus}.
+
+ If \var{pol} has inexact \typ{PADIC} coefficients, this is not always
+ well-defined; in this case, the polynomial is first made integral by dividing
+ out the $p$-adic content,  then lifted to $\Z$ using \tet{truncate}
+ coefficientwise.
+ Hence we actually factor exactly a polynomial which is only $p$-adically
+ close to the input. To avoid pitfalls, we advise to only factor polynomials
+ with exact rational coefficients.
+
+ \synt{factorpadic}{GEN f,GEN p, long r} . The function \kbd{factorpadic0} is
+ deprecated, provided for backward compatibility.
+
diff --git a/src/functions/polynomials/intformal b/src/functions/polynomials/intformal
new file mode 100644
index 0000000..9ab0d15
--- /dev/null
+++ b/src/functions/polynomials/intformal
@@ -0,0 +1,34 @@
+Function: intformal
+Section: polynomials
+C-Name: integ
+Prototype: GDn
+Help: intformal(x,{v}): formal integration of x with respect to v, or to the
+ main variable of x if v is omitted.
+Doc: \idx{formal integration} of $x$ with respect to the variable $v$ (wrt.
+ the main variable if $v$ is omitted). Since PARI cannot represent
+ logarithmic or arctangent terms, any such term in the result will yield an
+ error:
+ \bprog
+  ? intformal(x^2)
+  %1 = 1/3*x^3
+  ? intformal(x^2, y)
+  %2 = y*x^2
+  ? intformal(1/x)
+    ***   at top-level: intformal(1/x)
+    ***                 ^--------------
+    *** intformal: domain error in intformal: residue(series, pole) != 0
+ @eprog
+ The argument $x$ can be of any type. When $x$ is a rational function, we
+ assume that the base ring is an integral domain of characteristic zero.
+
+   By  definition,   the main variable of a \typ{POLMOD} is the main variable
+ among the  coefficients  from  its  two  polynomial  components
+ (representative and modulus); in other words, assuming a polmod represents an
+ element of $R[X]/(T(X))$, the variable $X$ is a mute variable and the
+ integral is taken with respect to the main variable used in the base ring $R$.
+ In particular, it is meaningless to integrate with respect to the main
+ variable of \kbd{x.mod}:
+ \bprog
+ ? intformal(Mod(1,x^2+1), 'x)
+ *** intformal: incorrect priority in intformal: variable x = x
+ @eprog
diff --git a/src/functions/polynomials/padicappr b/src/functions/polynomials/padicappr
new file mode 100644
index 0000000..8d25a2a
--- /dev/null
+++ b/src/functions/polynomials/padicappr
@@ -0,0 +1,14 @@
+Function: padicappr
+Section: polynomials
+C-Name: padicappr
+Prototype: GG
+Help: padicappr(pol,a): p-adic roots of the polynomial pol congruent to a mod p.
+Doc: vector of $p$-adic roots of the
+ polynomial $pol$ congruent to the $p$-adic number $a$ modulo $p$, and with
+ the same $p$-adic precision as $a$. The number $a$ can be an ordinary
+ $p$-adic number (type \typ{PADIC}, i.e.~an element of $\Z_p$) or can be an
+ integral element of a finite extension of $\Q_p$, given as a \typ{POLMOD}
+ at least one of whose coefficients is a \typ{PADIC}. In this case, the result
+ is the vector of roots belonging to the same extension of $\Q_p$ as $a$.
+Variant: Also available is \fun{GEN}{Zp_appr}{GEN f, GEN a} when $a$ is a
+ \typ{PADIC}.
diff --git a/src/functions/polynomials/padicfields b/src/functions/polynomials/padicfields
new file mode 100644
index 0000000..238ec6d
--- /dev/null
+++ b/src/functions/polynomials/padicfields
@@ -0,0 +1,34 @@
+Function: padicfields
+Section: polynomials
+C-Name: padicfields0
+Prototype: GGD0,L,
+Help: padicfields(p, N, {flag=0}): returns polynomials generating all
+ the extensions of degree N of the field of p-adic rational numbers; N is
+ allowed to be a 2-component vector [n,d], in which case, returns the
+ extensions of degree n and discriminant p^d. flag is optional,
+ and can be 0: default, 1: return also the ramification index, the residual
+ degree, the valuation of the discriminant and the number of conjugate fields,
+ or 2: return only the number of extensions in a fixed algebraic closure.
+
+Doc: returns a vector of polynomials generating all the extensions of degree
+ $N$ of the field $\Q_p$ of $p$-adic rational numbers; $N$ is
+ allowed to be a 2-component vector $[n,d]$, in which case we return the
+ extensions of degree $n$ and discriminant $p^d$.
+
+ The list is minimal in the sense that two different polynomials generate
+ non-isomorphic extensions; in particular, the number of polynomials is the
+ number of classes of non-isomorphic extensions. If $P$ is a polynomial in this
+ list, $\alpha$ is any root of $P$ and $K = \Q_p(\alpha)$, then $\alpha$
+ is the sum of a uniformizer and a (lift of a) generator of the residue field
+ of $K$; in particular, the powers of $\alpha$ generate the ring of $p$-adic
+ integers of $K$.
+
+ If $\fl = 1$, replace each polynomial $P$ by a vector $[P, e, f, d, c]$
+ where $e$ is the ramification index, $f$ the residual degree, $d$ the
+ valuation of the discriminant, and $c$ the number of conjugate fields.
+ If $\fl = 2$, only return the \emph{number} of extensions in a fixed
+ algebraic closure (Krasner's formula), which is much faster.
+
+Variant: Also available is
+ \fun{GEN}{padicfields}{GEN p, long n, long d, long flag}, which computes
+ extensions of $\Q_p$ of degree $n$ and discriminant $p^d$.
diff --git a/src/functions/polynomials/polchebyshev b/src/functions/polynomials/polchebyshev
new file mode 100644
index 0000000..a6a478f
--- /dev/null
+++ b/src/functions/polynomials/polchebyshev
@@ -0,0 +1,25 @@
+Function: polchebyshev
+Section: polynomials
+C-Name: polchebyshev_eval
+Prototype: LD1,L,DG
+Help: polchebyshev(n,{flag=1},{a='x}): Chebychev polynomial of the first (flag
+ = 1) or second (flag = 2) kind, of degree n, evaluated at a.
+Description:
+ (small,?1,?var):gen polchebyshev1($1,$3)
+ (small,2,?var):gen  polchebyshev2($1,$3)
+ (small,small,?var):gen polchebyshev($1,$2,$3)
+Doc: returns the $n^{\text{th}}$
+ \idx{Chebyshev} polynomial of the first kind $T_n$ ($\fl=1$) or the second
+ kind $U_n$ ($\fl=2$), evaluated at $a$ (\kbd{'x} by default). Both series of
+ polynomials satisfy the 3-term relation
+ $$ P_{n+1} = 2xP_n - P_{n-1}, $$
+ and are determined by the initial conditions $U_0 = T_0 = 1$, $T_1 = x$,
+ $U_1 = 2x$. In fact $T_n' = n U_{n-1}$ and, for all complex numbers $z$, we
+ have $T_n(\cos z) = \cos (nz)$ and $U_{n-1}(\cos z) = \sin(nz)/\sin z$.
+ If $n \geq 0$, then these polynomials have degree $n$.  For $n < 0$,
+ $T_n$ is equal to $T_{-n}$ and $U_n$ is equal to $-U_{-2-n}$.
+ In particular, $U_{-1} = 0$.
+Variant: Also available are
+ \fun{GEN}{polchebyshev}{long n, long \fl, long v},
+ \fun{GEN}{polchebyshev1}{long n, long v} and
+ \fun{GEN}{polchebyshev2}{long n, long v} for $T_n$ and $U_n$ respectively.
diff --git a/src/functions/polynomials/polcoeff b/src/functions/polynomials/polcoeff
new file mode 100644
index 0000000..28fdd5f
--- /dev/null
+++ b/src/functions/polynomials/polcoeff
@@ -0,0 +1,23 @@
+Function: polcoeff
+Section: polynomials
+C-Name: polcoeff0
+Prototype: GLDn
+Help: polcoeff(x,n,{v}): coefficient of degree n of x, or the n-th component
+ for vectors or matrices (for which it is simpler to use x[]). With respect
+ to the main variable if v is omitted, with respect to the variable v
+ otherwise.
+Description:
+ (pol, 0):gen:copy      constant_term($1)
+ (gen, small, ?var):gen polcoeff0($1, $2, $3)
+Doc: coefficient of degree $n$ of the polynomial $x$, with respect to the
+ main variable if $v$ is omitted, with respect to $v$ otherwise.  If $n$
+ is greater than the degree, the result is zero.
+
+ Naturally applies to scalars (polynomial of degree $0$), as well as to
+ rational functions whose denominator is a monomial.
+ It also applies to power series: if $n$ is less than the valuation, the result
+ is zero. If it is greater than the largest significant degree, then an error
+ message is issued.
+
+  For greater flexibility, $x$ can be a vector or matrix type and the
+  function then returns \kbd{component(x,n)}.
diff --git a/src/functions/polynomials/polcyclo b/src/functions/polynomials/polcyclo
new file mode 100644
index 0000000..88f9917
--- /dev/null
+++ b/src/functions/polynomials/polcyclo
@@ -0,0 +1,20 @@
+Function: polcyclo
+Section: polynomials
+C-Name: polcyclo_eval
+Prototype: LDG
+Help: polcyclo(n,{a = 'x}): n-th cyclotomic polynomial evaluated at a.
+Description:
+  (small,?var):gen     polcyclo($1,$2)
+  (small,gen):gen      polcyclo_eval($1,$2)
+Doc: $n$-th cyclotomic polynomial, evaluated at $a$ (\kbd{'x} by default). The
+ integer $n$ must be positive.
+
+ Algorithm used: reduce to the case where $n$ is squarefree; to compute the
+ cyclotomic polynomial, use $\Phi_{np}(x)=\Phi_n(x^p)/\Phi(x)$; to compute
+ it evaluated, use $\Phi_n(x) = \prod_{d\mid n} (x^d-1)^{\mu(n/d)}$. In the
+ evaluated case, the algorithm assumes that $a^d - 1$ is either $0$ or
+ invertible, for all $d\mid n$. If this is not the case (the base ring has
+ zero divisors), use \kbd{subst(polcyclo(n),x,a)}.
+
+Variant: The variant \fun{GEN}{polcyclo}{long n, long v} returns the $n$-th
+ cyclotomic polynomial in variable $v$.
diff --git a/src/functions/polynomials/polcyclofactors b/src/functions/polynomials/polcyclofactors
new file mode 100644
index 0000000..aa22706
--- /dev/null
+++ b/src/functions/polynomials/polcyclofactors
@@ -0,0 +1,28 @@
+Function: polcyclofactors
+Section: polynomials
+C-Name: polcyclofactors
+Prototype: G
+Help: polcyclofactors(f): returns a vector of polynomials, whose product is
+ the product of distinct cyclotomic polynomials dividing f.
+Doc: returns a vector of polynomials, whose product is the product of
+ distinct cyclotomic polynomials dividing $f$.
+ \bprog
+ ? f = x^10+5*x^8-x^7+8*x^6-4*x^5+8*x^4-3*x^3+7*x^2+3;
+ ? v = polcyclofactors(f)
+ %2 = [x^2 + 1, x^2 + x + 1, x^4 - x^3 + x^2 - x + 1]
+ ? apply(poliscycloprod, v)
+ %3 = [1, 1, 1]
+ ? apply(poliscyclo, v)
+ %4 = [4, 3, 10]
+ @eprog\noindent In general, the polynomials are products of cyclotomic
+ polynomials and not themselves irreducible:
+ \bprog
+ ? g = x^8+2*x^7+6*x^6+9*x^5+12*x^4+11*x^3+10*x^2+6*x+3;
+ ? polcyclofactors(g)
+ %2 = [x^6 + 2*x^5 + 3*x^4 + 3*x^3 + 3*x^2 + 2*x + 1]
+ ? factor(%[1])
+ %3 =
+ [            x^2 + x + 1 1]
+
+ [x^4 + x^3 + x^2 + x + 1 1]
+ @eprog
diff --git a/src/functions/polynomials/poldegree b/src/functions/polynomials/poldegree
new file mode 100644
index 0000000..7b1101a
--- /dev/null
+++ b/src/functions/polynomials/poldegree
@@ -0,0 +1,18 @@
+Function: poldegree
+Section: polynomials
+C-Name: poldegree
+Prototype: lGDn
+Help: poldegree(x,{v}): degree of the polynomial or rational function x with
+ respect to main variable if v is omitted, with respect to v otherwise.
+ For scalar x, return 0 is x is non-zero and a negative number otherwise.
+Description:
+ (pol):small                degpol($1)
+ (gen):small                degree($1)
+ (gen, var):small           poldegree($1, $2)
+Doc: degree of the polynomial $x$ in the main variable if $v$ is omitted, in
+ the variable $v$ otherwise.
+
+ The degree of $0$ is a fixed negative number, whose exact value should not
+ be used. The degree of a non-zero scalar is $0$. Finally, when $x$ is a
+ non-zero polynomial or rational function, returns the ordinary degree of
+ $x$. Raise an error otherwise.
diff --git a/src/functions/polynomials/poldisc b/src/functions/polynomials/poldisc
new file mode 100644
index 0000000..7b503c5
--- /dev/null
+++ b/src/functions/polynomials/poldisc
@@ -0,0 +1,13 @@
+Function: poldisc
+Section: polynomials
+C-Name: poldisc0
+Prototype: GDn
+Help: poldisc(pol,{v}): discriminant of the polynomial pol, with respect to main
+ variable if v is omitted, with respect to v otherwise.
+Description:
+ (pol):gen        discsr($1)
+ (gen):gen        poldisc0($1, -1)
+ (gen, var):gen   poldisc0($1, $2)
+Doc: discriminant of the polynomial
+ \var{pol} in the main variable if $v$ is omitted, in $v$ otherwise. The
+ algorithm used is the \idx{subresultant algorithm}.
diff --git a/src/functions/polynomials/poldiscreduced b/src/functions/polynomials/poldiscreduced
new file mode 100644
index 0000000..93665f6
--- /dev/null
+++ b/src/functions/polynomials/poldiscreduced
@@ -0,0 +1,11 @@
+Function: poldiscreduced
+Section: polynomials
+C-Name: reduceddiscsmith
+Prototype: G
+Help: poldiscreduced(f): vector of elementary divisors of Z[a]/f'(a)Z[a],
+ where a is a root of the polynomial f.
+Doc: reduced discriminant vector of the
+ (integral, monic) polynomial $f$. This is the vector of elementary divisors
+ of $\Z[\alpha]/f'(\alpha)\Z[\alpha]$, where $\alpha$ is a root of the
+ polynomial $f$. The components of the result are all positive, and their
+ product is equal to the absolute value of the discriminant of~$f$.
diff --git a/src/functions/polynomials/polgraeffe b/src/functions/polynomials/polgraeffe
new file mode 100644
index 0000000..efc61ea
--- /dev/null
+++ b/src/functions/polynomials/polgraeffe
@@ -0,0 +1,8 @@
+Function: polgraeffe
+Section: polynomials
+C-Name: polgraeffe
+Prototype: G
+Help: polgraeffe(f): returns the Graeffe transform g of f, such that
+ g(x^2) = f(x)f(-x)
+Doc: returns the \idx{Graeffe} transform $g$ of $f$, such that $g(x^2) = f(x)
+ f(-x)$.
diff --git a/src/functions/polynomials/polhensellift b/src/functions/polynomials/polhensellift
new file mode 100644
index 0000000..79e5868
--- /dev/null
+++ b/src/functions/polynomials/polhensellift
@@ -0,0 +1,24 @@
+Function: polhensellift
+Section: polynomials
+C-Name: polhensellift
+Prototype: GGGL
+Help: polhensellift(A, B, p, e): lift the factorization B of A modulo p to a
+ factorization modulo p^e using Hensel lift. The factors in B must be
+ pairwise relatively prime modulo p.
+Doc: given a prime $p$, an integral polynomial $A$ whose leading coefficient
+ is a $p$-unit, a vector $B$ of integral polynomials that are monic and
+ pairwise relatively prime modulo $p$, and whose product is congruent to
+ $A/\text{lc}(A)$ modulo $p$, lift the elements of $B$ to polynomials whose
+ product is congruent to $A$ modulo $p^e$.
+
+ More generally, if $T$ is an integral polynomial irreducible mod $p$, and
+ $B$ is a factorization of $A$ over the finite field $\F_p[t]/(T)$, you can
+ lift it to $\Z_p[t]/(T, p^e)$ by replacing the $p$ argument with $[p,T]$:
+ \bprog
+ ? { T = t^3 - 2; p = 7; A = x^2 + t + 1;
+     B = [x + (3*t^2 + t + 1), x + (4*t^2 + 6*t + 6)];
+     r = polhensellift(A, B, [p, T], 6) }
+ %1 = [x + (20191*t^2 + 50604*t + 75783), x + (97458*t^2 + 67045*t + 41866)]
+ ? liftall( r[1] * r[2] * Mod(Mod(1,p^6),T) )
+ %2 = x^2 + (t + 1)
+ @eprog
diff --git a/src/functions/polynomials/polhermite b/src/functions/polynomials/polhermite
new file mode 100644
index 0000000..3280b3a
--- /dev/null
+++ b/src/functions/polynomials/polhermite
@@ -0,0 +1,14 @@
+Function: polhermite
+Section: polynomials
+C-Name: polhermite_eval
+Prototype: LDG
+Help: polhermite(n,{a='x}): Hermite polynomial H(n,v) of degree n, evaluated
+ at a.
+Description:
+  (small,?var):gen    polhermite($1,$2)
+  (small,gen):gen     polhermite_eval($1,$2)
+Doc: $n^{\text{th}}$ \idx{Hermite} polynomial $H_n$ evaluated at $a$
+ (\kbd{'x} by default), i.e.
+ $$ H_n(x) = (-1)^n\*e^{x^2} \dfrac{d^n}{dx^n}e^{-x^2}.$$
+Variant: The variant \fun{GEN}{polhermite}{long n, long v} returns the $n$-th
+ Hermite polynomial in variable $v$.
diff --git a/src/functions/polynomials/polinterpolate b/src/functions/polynomials/polinterpolate
new file mode 100644
index 0000000..c2004b5
--- /dev/null
+++ b/src/functions/polynomials/polinterpolate
@@ -0,0 +1,15 @@
+Function: polinterpolate
+Section: polynomials
+C-Name: polint
+Prototype: GDGDGD&
+Help: polinterpolate(X,{Y},{x},{&e}): polynomial interpolation at x
+ according to data vectors X, Y (ie return P such that P(X[i]) = Y[i] for
+ all i). If Y is omitted, return P such that P(i) = X[i]. If present, e
+ will contain an error estimate on the returned value.
+Doc: given the data vectors
+ $X$ and $Y$ of the same length $n$ ($X$ containing the $x$-coordinates,
+ and $Y$ the corresponding $y$-coordinates), this function finds the
+ \idx{interpolating polynomial} passing through these points and evaluates it
+ at~$x$. If $Y$ is omitted, return the polynomial interpolating the
+ $(i,X[i])$. If present, $e$ will contain an error estimate on the returned
+ value.
diff --git a/src/functions/polynomials/poliscyclo b/src/functions/polynomials/poliscyclo
new file mode 100644
index 0000000..5b40d92
--- /dev/null
+++ b/src/functions/polynomials/poliscyclo
@@ -0,0 +1,16 @@
+Function: poliscyclo
+Section: polynomials
+C-Name: poliscyclo
+Prototype: lG
+Help: poliscyclo(f): returns 0 if f is not a cyclotomic polynomial, and n
+ > 0 if f = Phi_n, the n-th cyclotomic polynomial.
+Doc: returns 0 if $f$ is not a cyclotomic polynomial, and $n > 0$ if $f =
+ \Phi_n$, the $n$-th cyclotomic polynomial.
+ \bprog
+ ? poliscyclo(x^4-x^2+1)
+ %1 = 12
+ ? polcyclo(12)
+ %2 = x^4 - x^2 + 1
+ ? poliscyclo(x^4-x^2-1)
+ %3 = 0
+ @eprog
diff --git a/src/functions/polynomials/poliscycloprod b/src/functions/polynomials/poliscycloprod
new file mode 100644
index 0000000..d03efd0
--- /dev/null
+++ b/src/functions/polynomials/poliscycloprod
@@ -0,0 +1,22 @@
+Function: poliscycloprod
+Section: polynomials
+C-Name: poliscycloprod
+Prototype: lG
+Help: poliscycloprod(f): returns 1 if f is a product of cyclotomic
+ polynonials, and 0 otherwise.
+Doc: returns 1 if $f$ is a product of cyclotomic polynomial, and $0$
+ otherwise.
+ \bprog
+ ? f = x^6+x^5-x^3+x+1;
+ ? poliscycloprod(f)
+ %2 = 1
+ ? factor(f)
+ %3 =
+ [  x^2 + x + 1 1]
+
+ [x^4 - x^2 + 1 1]
+ ? [ poliscyclo(T) | T <- %[,1] ]
+ %4 = [3, 12]
+ ? polcyclo(3) * polcyclo(12)
+ %5 = x^6 + x^5 - x^3 + x + 1
+ @eprog
diff --git a/src/functions/polynomials/polisirreducible b/src/functions/polynomials/polisirreducible
new file mode 100644
index 0000000..139f64a
--- /dev/null
+++ b/src/functions/polynomials/polisirreducible
@@ -0,0 +1,10 @@
+Function: polisirreducible
+Section: polynomials
+C-Name: isirreducible
+Prototype: lG
+Help: polisirreducible(pol): true(1) if pol is an irreducible non-constant
+ polynomial, false(0) if pol is reducible or constant.
+Doc: \var{pol} being a polynomial (univariate in the present version \vers),
+ returns 1 if \var{pol} is non-constant and irreducible, 0 otherwise.
+ Irreducibility is checked over the smallest base field over which \var{pol}
+ seems to be defined.
diff --git a/src/functions/polynomials/pollead b/src/functions/polynomials/pollead
new file mode 100644
index 0000000..b7fc2a6
--- /dev/null
+++ b/src/functions/polynomials/pollead
@@ -0,0 +1,14 @@
+Function: pollead
+Section: polynomials
+C-Name: pollead
+Prototype: GDn
+Help: pollead(x,{v}): leading coefficient of polynomial or series x, or x
+ itself if x is a scalar. Error otherwise. With respect to the main variable
+ of x if v is omitted, with respect to the variable v otherwise.
+Description:
+ (pol):gen:copy         leading_term($1)
+ (gen):gen              pollead($1, -1)
+ (gen, var):gen         pollead($1, $2)
+Doc: leading coefficient of the polynomial or power series $x$. This is
+  computed with respect to the main variable of $x$ if $v$ is omitted, with
+  respect to the variable $v$ otherwise.
diff --git a/src/functions/polynomials/pollegendre b/src/functions/polynomials/pollegendre
new file mode 100644
index 0000000..2163650
--- /dev/null
+++ b/src/functions/polynomials/pollegendre
@@ -0,0 +1,12 @@
+Function: pollegendre
+Section: polynomials
+C-Name: pollegendre_eval
+Prototype: LDG
+Help: pollegendre(n,{a='x}): legendre polynomial of degree n evaluated at a.
+Description:
+  (small,?var):gen    pollegendre($1,$2)
+  (small,gen):gen     pollegendre_eval($1,$2)
+Doc: $n^{\text{th}}$ \idx{Legendre polynomial} evaluated at $a$ (\kbd{'x} by
+ default).
+Variant: To obtain the $n$-th Legendre polynomial in variable $v$,
+ use \fun{GEN}{pollegendre}{long n, long v}.
diff --git a/src/functions/polynomials/polrecip b/src/functions/polynomials/polrecip
new file mode 100644
index 0000000..572a7e5
--- /dev/null
+++ b/src/functions/polynomials/polrecip
@@ -0,0 +1,7 @@
+Function: polrecip
+Section: polynomials
+C-Name: polrecip
+Prototype: G
+Help: polrecip(pol): reciprocal polynomial of pol.
+Doc:  reciprocal polynomial of \var{pol}, i.e.~the coefficients are in
+ reverse order. \var{pol} must be a polynomial.
diff --git a/src/functions/polynomials/polresultant b/src/functions/polynomials/polresultant
new file mode 100644
index 0000000..de0422b
--- /dev/null
+++ b/src/functions/polynomials/polresultant
@@ -0,0 +1,24 @@
+Function: polresultant
+Section: polynomials
+C-Name: polresultant0
+Prototype: GGDnD0,L,
+Help: polresultant(x,y,{v},{flag=0}): resultant of the polynomials x and y,
+ with respect to the main variables of x and y if v is omitted, with respect
+ to the variable v otherwise. flag is optional, and can be 0: default,
+ uses either the subresultant algorithm, a modular algorithm or Sylvester's
+ matrix, depending on the inputs; 1 uses Sylvester's matrix (should always be
+ slower than the default).
+Doc: resultant of the two
+ polynomials $x$ and $y$ with exact entries, with respect to the main
+ variables of $x$ and $y$ if $v$ is omitted, with respect to the variable $v$
+ otherwise. The algorithm assumes the base ring is a domain. If you also need
+ the $u$ and $v$ such that $x*u + y*v = \text{Res}(x,y)$, use the
+ \tet{polresultantext} function.
+
+ If $\fl=0$ (default), uses the the algorithm best suited to the inputs,
+ either the \idx{subresultant algorithm} (Lazard/Ducos variant, generic case),
+ a modular algorithm (inputs in $\Q[X]$) or Sylvester's matrix (inexact
+ inputs).
+
+ If $\fl=1$, uses the determinant of Sylvester's matrix instead; this should
+ always be slower than the default.
diff --git a/src/functions/polynomials/polresultantext b/src/functions/polynomials/polresultantext
new file mode 100644
index 0000000..ce4799c
--- /dev/null
+++ b/src/functions/polynomials/polresultantext
@@ -0,0 +1,25 @@
+Function: polresultantext
+Section: polynomials
+C-Name: polresultantext0
+Prototype: GGDn
+Help: polresultantext(A,B,{v}): return [U,V,R] such that
+ R=polresultant(A,B,v) and U*A+V*B = R, where A and B are polynomials.
+Doc: finds polynomials $U$ and $V$ such that $A*U + B*V = R$, where $R$ is
+ the resultant of $U$ and $V$ with respect to the main variables of $A$ and
+ $B$ if $v$ is omitted, and with respect to $v$ otherwise. Returns the row
+ vector $[U,V,R]$. The algorithm used (subresultant) assumes that the base
+ ring is a domain.
+ \bprog
+ ? A = x*y; B = (x+y)^2;
+ ? [U,V,R] = polresultantext(A, B)
+ %2 = [-y*x - 2*y^2, y^2, y^4]
+ ? A*U + B*V
+ %3 = y^4
+ ? [U,V,R] = polresultantext(A, B, y)
+ %4 = [-2*x^2 - y*x, x^2, x^4]
+ ? A*U+B*V
+ %5 = x^4
+ @eprog
+Variant: Also available is
+ \fun{GEN}{polresultantext}{GEN x, GEN y}.
+
diff --git a/src/functions/polynomials/polroots b/src/functions/polynomials/polroots
new file mode 100644
index 0000000..c3bfdd6
--- /dev/null
+++ b/src/functions/polynomials/polroots
@@ -0,0 +1,16 @@
+Function: polroots
+Section: polynomials
+C-Name: roots
+Prototype: Gp
+Help: polroots(x): complex roots of the polynomial x using
+ Schonhage's method, as modified by Gourdon.
+Doc: complex roots of the polynomial
+ \var{x}, given as a column vector where each root is repeated according to
+ its multiplicity. The precision is given as for transcendental functions: in
+ GP it is kept in the variable \kbd{realprecision} and is transparent to the
+ user, but it must be explicitly given as a second argument in library mode.
+
+ The algorithm used is a modification of A.~Sch\"onhage\sidx{Sch\"onage}'s
+ root-finding algorithm, due to and originally implemented by X.~Gourdon.
+ Barring bugs, it is guaranteed to converge and to give the roots to the
+ required accuracy.
diff --git a/src/functions/polynomials/polrootsmod b/src/functions/polynomials/polrootsmod
new file mode 100644
index 0000000..5881cc0
--- /dev/null
+++ b/src/functions/polynomials/polrootsmod
@@ -0,0 +1,18 @@
+Function: polrootsmod
+Section: polynomials
+C-Name: rootmod0
+Prototype: GGD0,L,
+Help: polrootsmod(pol,p,{flag=0}): roots mod the prime p of the polynomial pol. flag is
+ optional, and can be 0: default, or 1: use a naive search, useful for small p.
+Description:
+ (pol, int, ?0):vec           rootmod($1, $2)
+ (pol, int, 1):vec            rootmod2($1, $2)
+ (pol, int, #small):vec       $"Bad flag in polrootsmod"
+ (pol, int, small):vec        rootmod0($1, $2, $3)
+Doc: row vector of roots modulo $p$ of the polynomial \var{pol}.
+ Multiple roots are \emph{not} repeated.
+ \bprog
+ ? polrootsmod(x^2-1,2)
+ %1 = [Mod(1, 2)]~
+ @eprog\noindent
+ If $p$ is very small, you may set $\fl=1$, which uses a naive search.
diff --git a/src/functions/polynomials/polrootspadic b/src/functions/polynomials/polrootspadic
new file mode 100644
index 0000000..8d18cb3
--- /dev/null
+++ b/src/functions/polynomials/polrootspadic
@@ -0,0 +1,21 @@
+Function: polrootspadic
+Section: polynomials
+C-Name: rootpadic
+Prototype: GGL
+Help: polrootspadic(x,p,r): p-adic roots of the polynomial x to precision r.
+Doc: vector of $p$-adic roots of the polynomial \var{pol}, given to
+ $p$-adic precision $r$ $p$ is assumed to be a prime. Multiple roots are
+ \emph{not} repeated. Note that this is not the same as the roots in
+ $\Z/p^r\Z$, rather it gives approximations in $\Z/p^r\Z$ of the true roots
+ living in $\Q_p$.
+ \bprog
+ ? polrootspadic(x^3 - x^2 + 64, 2, 5)
+ %1 = [2^3 + O(2^5), 2^3 + 2^4 + O(2^5), 1 + O(2^5)]~
+ @eprog
+ If \var{pol} has inexact \typ{PADIC} coefficients, this is not always
+ well-defined; in this case, the polynomial is first made integral by dividing
+ out the $p$-adic content, then lifted
+ to $\Z$ using \tet{truncate} coefficientwise. Hence the roots given are
+ approximations of the roots of an exact polynomial which is $p$-adically
+ close to the input. To avoid pitfalls, we advise to only factor polynomials
+ with eact rational coefficients.
diff --git a/src/functions/polynomials/polsturm b/src/functions/polynomials/polsturm
new file mode 100644
index 0000000..6c2a598
--- /dev/null
+++ b/src/functions/polynomials/polsturm
@@ -0,0 +1,12 @@
+Function: polsturm
+Section: polynomials
+C-Name: sturmpart
+Prototype: lGDGDG
+Help: polsturm(pol,{a},{b}): number of real roots of the squarefree polynomial
+ pol in the interval ]a,b] (which are respectively taken to be -oo or +oo when
+ omitted).
+Doc: number of real roots of the real squarefree polynomial \var{pol} in the
+ interval $]a,b]$, using Sturm's algorithm. $a$ (resp.~$b$) is taken to be
+ $-\infty$ (resp.~$+\infty$) if omitted.
+Variant: Also available is \fun{long}{sturm}{GEN pol} (total number of real
+ roots).
diff --git a/src/functions/polynomials/polsubcyclo b/src/functions/polynomials/polsubcyclo
new file mode 100644
index 0000000..095223b
--- /dev/null
+++ b/src/functions/polynomials/polsubcyclo
@@ -0,0 +1,16 @@
+Function: polsubcyclo
+Section: polynomials
+C-Name: polsubcyclo
+Prototype: LLDn
+Help: polsubcyclo(n,d,{v='x}): finds an equation (in variable v) for the d-th
+ degree subfields of Q(zeta_n). Output is a polynomial, or a vector of
+ polynomials if there are several such fields or none.
+Doc: gives polynomials (in variable $v$) defining the sub-Abelian extensions
+ of degree $d$ of the cyclotomic field $\Q(\zeta_n)$, where $d\mid \phi(n)$.
+
+ If there is exactly one such extension the output is a polynomial, else it is
+ a vector of polynomials, possibly empty. To get a vector in all cases,
+ use \kbd{concat([], polsubcyclo(n,d))}.
+
+ The function \tet{galoissubcyclo} allows to specify exactly which
+ sub-Abelian extension should be computed.
diff --git a/src/functions/polynomials/polsylvestermatrix b/src/functions/polynomials/polsylvestermatrix
new file mode 100644
index 0000000..034e2c7
--- /dev/null
+++ b/src/functions/polynomials/polsylvestermatrix
@@ -0,0 +1,13 @@
+Function: polsylvestermatrix
+Section: polynomials
+C-Name: sylvestermatrix
+Prototype: GG
+Help: polsylvestermatrix(x,y): forms the sylvester matrix associated to the
+ two polynomials x and y. Warning: the polynomial coefficients are in
+ columns, not in rows.
+Doc: forms the Sylvester matrix
+ corresponding to the two polynomials $x$ and $y$, where the coefficients of
+ the polynomials are put in the columns of the matrix (which is the natural
+ direction for solving equations afterwards). The use of this matrix can be
+ essential when dealing with polynomials with inexact entries, since
+ polynomial Euclidean division doesn't make much sense in this case.
diff --git a/src/functions/polynomials/polsym b/src/functions/polynomials/polsym
new file mode 100644
index 0000000..fe79d3a
--- /dev/null
+++ b/src/functions/polynomials/polsym
@@ -0,0 +1,7 @@
+Function: polsym
+Section: polynomials
+C-Name: polsym
+Prototype: GL
+Help: polsym(x,n): column vector of symmetric powers of the roots of x up to n.
+Doc: creates the column vector of the \idx{symmetric powers} of the roots of the
+ polynomial $x$ up to power $n$, using Newton's formula.
diff --git a/src/functions/polynomials/poltchebi b/src/functions/polynomials/poltchebi
new file mode 100644
index 0000000..6898df9
--- /dev/null
+++ b/src/functions/polynomials/poltchebi
@@ -0,0 +1,6 @@
+Function: poltchebi
+Section: polynomials
+C-Name: polchebyshev1
+Prototype: LDn
+Help: poltchebi(n,{v='x}): deprecated alias for polchebyshev
+Doc: deprecated alias for \kbd{polchebyshev}
diff --git a/src/functions/polynomials/polzagier b/src/functions/polynomials/polzagier
new file mode 100644
index 0000000..503f45c
--- /dev/null
+++ b/src/functions/polynomials/polzagier
@@ -0,0 +1,19 @@
+Function: polzagier
+Section: polynomials
+C-Name: polzag
+Prototype: LL
+Help: polzagier(n,m): Zagier's polynomials of index n,m.
+Doc: creates Zagier's polynomial $P_n^{(m)}$ used in
+ the functions \kbd{sumalt} and \kbd{sumpos} (with $\fl=1$). One must have $m\le
+ n$. The exact definition can be found in ``Convergence acceleration of
+ alternating series'', Cohen et al., Experiment.~Math., vol.~9, 2000, pp.~3--12.
+
+ %@article {MR2001m:11222,
+ %    AUTHOR = {Cohen, Henri and Rodriguez Villegas, Fernando and Zagier, Don},
+ %     TITLE = {Convergence acceleration of alternating series},
+ %   JOURNAL = {Experiment. Math.},
+ %    VOLUME = {9},
+ %      YEAR = {2000},
+ %    NUMBER = {1},
+ %     PAGES = {3--12},
+ %}
diff --git a/src/functions/polynomials/serconvol b/src/functions/polynomials/serconvol
new file mode 100644
index 0000000..968c5ad
--- /dev/null
+++ b/src/functions/polynomials/serconvol
@@ -0,0 +1,8 @@
+Function: serconvol
+Section: polynomials
+C-Name: convol
+Prototype: GG
+Help: serconvol(x,y): convolution (or Hadamard product) of two power series.
+Doc: convolution (or \idx{Hadamard product}) of the
+ two power series $x$ and $y$; in other words if $x=\sum a_k*X^k$ and $y=\sum
+ b_k*X^k$ then $\kbd{serconvol}(x,y)=\sum a_k*b_k*X^k$.
diff --git a/src/functions/polynomials/serlaplace b/src/functions/polynomials/serlaplace
new file mode 100644
index 0000000..6a983b0
--- /dev/null
+++ b/src/functions/polynomials/serlaplace
@@ -0,0 +1,8 @@
+Function: serlaplace
+Section: polynomials
+C-Name: laplace
+Prototype: G
+Help: serlaplace(x): replaces the power series sum of a_n*x^n/n! by sum of
+ a_n*x^n. For the reverse operation, use serconvol(x,exp(X)).
+Doc: $x$ must be a power series with non-negative
+ exponents. If $x=\sum (a_k/k!)*X^k$ then the result is $\sum a_k*X^k$.
diff --git a/src/functions/polynomials/serreverse b/src/functions/polynomials/serreverse
new file mode 100644
index 0000000..c4ca94a
--- /dev/null
+++ b/src/functions/polynomials/serreverse
@@ -0,0 +1,14 @@
+Function: serreverse
+Section: polynomials
+C-Name: serreverse
+Prototype: G
+Help: serreverse(s): reversion of the power series s.
+Doc: reverse power series of $s$, i.e. the series $t$ such that $t(s) = x$;
+ $s$ must be a power series whose valuation is exactly equal to one.
+ \bprog
+ ? \ps 8
+ ? t = serreverse(tan(x))
+ %2 = x - 1/3*x^3 + 1/5*x^5 - 1/7*x^7 + O(x^8)
+ ? tan(t)
+ %3 = x + O(x^8)
+ @eprog
diff --git a/src/functions/polynomials/subst b/src/functions/polynomials/subst
new file mode 100644
index 0000000..fdd06ef
--- /dev/null
+++ b/src/functions/polynomials/subst
@@ -0,0 +1,30 @@
+Function: subst
+Section: polynomials
+C-Name: gsubst
+Prototype: GnG
+Help: subst(x,y,z): in expression x, replace the variable y by the
+ expression z.
+Doc: replace the simple variable $y$ by the argument $z$ in the ``polynomial''
+ expression $x$. Every type is allowed for $x$, but if it is not a genuine
+ polynomial (or power series, or rational function), the substitution will be
+ done as if the scalar components were polynomials of degree zero. In
+ particular, beware that:
+
+ \bprog
+ ? subst(1, x, [1,2; 3,4])
+ %1 =
+ [1 0]
+
+ [0 1]
+
+ ? subst(1, x, Mat([0,1]))
+   ***   at top-level: subst(1,x,Mat([0,1])
+   ***                 ^--------------------
+   *** subst: forbidden substitution by a non square matrix.
+ @eprog\noindent
+ If $x$ is a power series, $z$ must be either a polynomial, a power
+ series, or a rational function. Finally, if $x$ is a vector,
+ matrix or list, the substitution is applied to each individual entry.
+
+ Use the function \kbd{substvec} to replace several variables at once,
+ or the function \kbd{substpol} to replace a polynomial expression.
diff --git a/src/functions/polynomials/substpol b/src/functions/polynomials/substpol
new file mode 100644
index 0000000..a8371ef
--- /dev/null
+++ b/src/functions/polynomials/substpol
@@ -0,0 +1,27 @@
+Function: substpol
+Section: polynomials
+C-Name: gsubstpol
+Prototype: GGG
+Help: substpol(x,y,z): in expression x, replace the polynomial y by the
+ expression z, using remainder decomposition of x.
+Doc: replace the ``variable'' $y$ by the argument $z$ in the ``polynomial''
+ expression $x$. Every type is allowed for $x$, but the same behavior
+ as \kbd{subst} above apply.
+
+ The difference with \kbd{subst} is that $y$ is allowed to be any polynomial
+ here. The substitution is done moding out all components of $x$
+ (recursively) by $y - t$, where $t$ is a new free variable of lowest
+ priority. Then substituting $t$ by $z$ in the resulting expression. For
+ instance
+ \bprog
+ ? substpol(x^4 + x^2 + 1, x^2, y)
+ %1 = y^2 + y + 1
+ ? substpol(x^4 + x^2 + 1, x^3, y)
+ %2 = x^2 + y*x + 1
+ ? substpol(x^4 + x^2 + 1, (x+1)^2, y)
+ %3 = (-4*y - 6)*x + (y^2 + 3*y - 3)
+ @eprog
+Variant: Further, \fun{GEN}{gdeflate}{GEN T, long v, long d} attempts to
+ write $T(x)$ in the form $t(x^d)$, where $x=$\kbd{pol\_x}$(v)$, and returns
+ \kbd{NULL} if the substitution fails (for instance in the example \kbd{\%2}
+ above).
diff --git a/src/functions/polynomials/substvec b/src/functions/polynomials/substvec
new file mode 100644
index 0000000..8304984
--- /dev/null
+++ b/src/functions/polynomials/substvec
@@ -0,0 +1,18 @@
+Function: substvec
+Section: polynomials
+C-Name: gsubstvec
+Prototype: GGG
+Help: substvec(x,v,w): in expression x, make a best effort to replace the
+ variables v1,...,vn by the expression w1,...,wn.
+Doc: $v$ being a vector of monomials of degree 1 (variables),
+ $w$ a vector of expressions of the same length, replace in the expression
+ $x$ all occurrences of $v_i$ by $w_i$. The substitutions are done
+ simultaneously; more precisely, the $v_i$ are first replaced by new
+ variables in $x$, then these are replaced by the $w_i$:
+
+ \bprog
+ ? substvec([x,y], [x,y], [y,x])
+ %1 = [y, x]
+ ? substvec([x,y], [x,y], [y,x+y])
+ %2 = [y, x + y]     \\ not [y, 2*y]
+ @eprog
diff --git a/src/functions/polynomials/sumformal b/src/functions/polynomials/sumformal
new file mode 100644
index 0000000..9c8d658
--- /dev/null
+++ b/src/functions/polynomials/sumformal
@@ -0,0 +1,27 @@
+Function: sumformal
+Section: polynomials
+C-Name: sumformal
+Prototype: GDn
+Help: sumformal(f,{v}): formal sum of f with respect to v, or to the
+ main variable of f if v is omitted.
+Doc: \idx{formal sum} of the polynomial expression $f$ with respect to the
+ main variable if $v$ is omitted, with respect to the variable $v$ otherwise;
+ it is assumed that the base ring has characteristic zero. In other words,
+ considering $f$ as a polynomial function in the variable $v$,
+ returns $F$, a polynomial in $v$ vanishing at $0$, such that $F(b) - F(a)
+ = sum_{v = a+1}^b f(v)$:
+ \bprog
+ ? sumformal(n)  \\ 1 + ... + n
+ %1 = 1/2*n^2 + 1/2*n
+ ? f(n) = n^3+n^2+1;
+ ? F = sumformal(f(n))  \\ f(1) + ... + f(n)
+ %3 = 1/4*n^4 + 5/6*n^3 + 3/4*n^2 + 7/6*n
+ ? sum(n = 1, 2000, f(n)) == subst(F, n, 2000)
+ %4 = 1
+ ? sum(n = 1001, 2000, f(n)) == subst(F, n, 2000) - subst(F, n, 1000)
+ %5 = 1
+ ? sumformal(x^2 + x*y + y^2, y)
+ %6 = y*x^2 + (1/2*y^2 + 1/2*y)*x + (1/3*y^3 + 1/2*y^2 + 1/6*y)
+ ? x^2 * y + x * sumformal(y) + sumformal(y^2) == %
+ %7 = 1
+ @eprog
diff --git a/src/functions/polynomials/taylor b/src/functions/polynomials/taylor
new file mode 100644
index 0000000..6958483
--- /dev/null
+++ b/src/functions/polynomials/taylor
@@ -0,0 +1,18 @@
+Function: taylor
+Section: polynomials
+C-Name: tayl
+Prototype: GnDP
+Help: taylor(x,t,{d=seriesprecision}): taylor expansion of x with respect to
+ t, adding O(t^d) to all components of x.
+Doc: Taylor expansion around $0$ of $x$ with respect to
+ the simple variable $t$. $x$ can be of any reasonable type, for example a
+ rational function. Contrary to \tet{Ser}, which takes the valuation into
+ account, this function adds $O(t^d)$ to all components of $x$.
+ \bprog
+ ? taylor(x/(1+y), y, 5)
+ %1 = (y^4 - y^3 + y^2 - y + 1)*x + O(y^5)
+ ? Ser(x/(1+y), y, 5)
+  ***   at top-level: Ser(x/(1+y),y,5)
+  ***                 ^----------------
+  *** Ser: main variable must have higher priority in gtoser.
+ @eprog
diff --git a/src/functions/polynomials/thue b/src/functions/polynomials/thue
new file mode 100644
index 0000000..9c0a106
--- /dev/null
+++ b/src/functions/polynomials/thue
@@ -0,0 +1,64 @@
+Function: thue
+Section: polynomials
+C-Name: thue
+Prototype: GGDG
+Help: thue(tnf,a,{sol}): solve the equation P(x,y)=a, where tnf was created
+ with thueinit(P), and sol, if present, contains the solutions of Norm(x)=a
+ modulo units in the number field defined by P. If tnf was computed without
+ assuming GRH (flag 1 in thueinit), the result is unconditional. If tnf is a
+ polynomial, compute thue(thueinit(P,0), a).
+Doc: returns all solutions of the equation
+ $P(x,y)=a$ in integers $x$ and $y$, where \var{tnf} was created with
+ $\kbd{thueinit}(P)$. If present, \var{sol} must contain the solutions of
+ $\Norm(x)=a$ modulo units of positive norm in the number field
+ defined by $P$ (as computed by \kbd{bnfisintnorm}). If there are infinitely
+ many solutions, an error will be issued.
+
+ It is allowed to input directly the polynomial $P$ instead of a \var{tnf},
+ in which case, the function first performs \kbd{thueinit(P,0)}. This is
+ very wasteful if more than one value of $a$ is required.
+
+ If \var{tnf} was computed without assuming GRH (flag $1$ in \tet{thueinit}),
+ then the result is unconditional. Otherwise, it depends in principle of the
+ truth of the GRH, but may still be unconditionally correct in some
+ favorable cases. The result is conditional on the GRH if
+ $a\neq \pm 1$ and, $P$ has a single irreducible rational factor, whose
+ associated tentative class number $h$ and regulator $R$ (as computed
+ assuming the GRH) satisfy
+
+ \item $h > 1$,
+
+ \item $R/0.2 > 1.5$.
+
+ Here's how to solve the Thue equation $x^{13} - 5y^{13} = - 4$:
+ \bprog
+ ? tnf = thueinit(x^13 - 5);
+ ? thue(tnf, -4)
+ %1 = [[1, 1]]
+ @eprog\noindent In this case, one checks that \kbd{bnfinit(x\pow13 -5).no}
+ is $1$. Hence, the only solution is $(x,y) = (1,1)$, and the result is
+ unconditional. On the other hand:
+ \bprog
+ ? P = x^3-2*x^2+3*x-17; tnf = thueinit(P);
+ ? thue(tnf, -15)
+ %2 = [[1, 1]]  \\ a priori conditional on the GRH.
+ ? K = bnfinit(P); K.no
+ %3 = 3
+ ? K.reg
+ %4 = 2.8682185139262873674706034475498755834
+ @eprog
+ This time the result is conditional. All results computed using this
+ particular \var{tnf} are likewise conditional, \emph{except} for a right-hand
+ side of $\pm 1$.
+ The above result is in fact correct, so we did not just disprove the GRH:
+ \bprog
+ ? tnf = thueinit(x^3-2*x^2+3*x-17, 1 /*unconditional*/);
+ ? thue(tnf, -15)
+ %4 = [[1, 1]]
+ @eprog
+ Note that reducible or non-monic polynomials are allowed:
+ \bprog
+ ? tnf = thueinit((2*x+1)^5 * (4*x^3-2*x^2+3*x-17), 1);
+ ? thue(tnf, 128)
+ %2 = [[-1, 0], [1, 0]]
+ @eprog\noindent Reducible polynomials are in fact much easier to handle.
diff --git a/src/functions/polynomials/thueinit b/src/functions/polynomials/thueinit
new file mode 100644
index 0000000..1835e9b
--- /dev/null
+++ b/src/functions/polynomials/thueinit
@@ -0,0 +1,19 @@
+Function: thueinit
+Section: polynomials
+C-Name: thueinit
+Prototype: GD0,L,p
+Help: thueinit(P,{flag=0}): initialize the tnf corresponding to P, that will
+ be used to solve Thue equations P(x,y) = some-integer. If flag is non-zero,
+ certify the result unconditionaly. Otherwise, assume GRH (much faster of
+ course).
+Doc: initializes the \var{tnf} corresponding to $P$, a univariate polynomial
+ with integer coefficients. The result is meant to be used in conjunction with
+ \tet{thue} to solve Thue equations $P(X / Y)Y^{\deg P} = a$, where $a$ is an
+ integer.
+
+ If $\fl$ is non-zero, certify results unconditionally. Otherwise, assume
+ \idx{GRH}, this being much faster of course. In the latter case, the result
+ may still be unconditionally correct, see \tet{thue}. For instance in most
+ cases where $P$ is reducible (not a pure power of an irreducible), \emph{or}
+ conditional computed class groups are trivial \emph{or} the right hand side
+ is $\pm1$, then results are always unconditional.
diff --git a/src/functions/programming/_eval_mnemonic b/src/functions/programming/_eval_mnemonic
new file mode 100644
index 0000000..c348049
--- /dev/null
+++ b/src/functions/programming/_eval_mnemonic
@@ -0,0 +1,5 @@
+Function: _eval_mnemonic
+C-Name: eval_mnemonic
+Section: programming/internals
+Prototype: lGs
+Help: Convert a mnemonic string to a flag.
diff --git a/src/functions/programming/addhelp b/src/functions/programming/addhelp
new file mode 100644
index 0000000..cb6c14f
--- /dev/null
+++ b/src/functions/programming/addhelp
@@ -0,0 +1,34 @@
+Function: addhelp
+Section: programming/specific
+C-Name: addhelp
+Prototype: vrs
+Help: addhelp(sym,str): add/change help message for the symbol sym.
+Doc: changes the help message for the symbol \kbd{sym}. The string \var{str}
+ is expanded on the spot and stored as the online help for \kbd{sym}. It is
+ recommended to document global variables and user functions in this way,
+ although \kbd{gp} will not protest if you don't.
+
+ You can attach a help text to an alias, but it will never be
+ shown: aliases are expanded by the \kbd{?} help operator and we get the help
+ of the symbol the alias points to. Nothing prevents you from modifying the
+ help of built-in PARI functions. But if you do, we would like to hear why you
+ needed it!
+
+ Without \tet{addhelp}, the standard help for user functions consists of its
+ name and definition.
+ \bprog
+ gp> f(x) = x^2;
+ gp> ?f
+ f =
+   (x)->x^2
+
+ @eprog\noindent Once addhelp is applied to $f$, the function code is no
+ longer included. It can still be consulted by typing the function name:
+ \bprog
+ gp> addhelp(f, "Square")
+ gp> ?f
+ Square
+
+ gp> f
+ %2 = (x)->x^2
+ @eprog
diff --git a/src/functions/programming/alarm b/src/functions/programming/alarm
new file mode 100644
index 0000000..f9172da
--- /dev/null
+++ b/src/functions/programming/alarm
@@ -0,0 +1,49 @@
+Function: alarm
+Class: gp
+Section: programming/specific
+C-Name: gp_alarm
+Prototype: D0,L,DE
+Help: alarm({s = 0},{code}): if code is omitted, trigger an "e_ALARM"
+ exception after s seconds, cancelling any previously set alarm; stop a pending
+ alarm if s = 0 or is omitted. Otherwise, evaluate code, aborting after s
+ seconds.
+Doc: if \var{code} is omitted, trigger an \var{e\_ALARM} exception after $s$
+ seconds, cancelling any previously set alarm; stop a pending alarm if $s =
+ 0$ or is omitted.
+
+ Otherwise, if $s$ is positive, the function evaluates \var{code},
+ aborting after $s$ seconds. The return value is the value of \var{code} if
+ it ran to completion before the alarm timeout, and a \typ{ERROR} object
+ otherwise.
+ \bprog
+   ? p = nextprime(10^25); q = nextprime(10^26); N = p*q;
+   ? E = alarm(1, factor(N));
+   ? type(E)
+   %3 = "t_ERROR"
+   ? print(E)
+   %4 = error("alarm interrupt after 964 ms.")
+   ? alarm(10, factor(N));   \\ enough time
+   %5 =
+   [ 10000000000000000000000013 1]
+
+   [100000000000000000000000067 1]
+ @eprog\noindent Here is a more involved example: the function
+ \kbd{timefact(N,sec)} below tries to factor $N$ and gives up after \var{sec}
+ seconds, returning a partial factorisation.
+ \bprog
+ \\ Time-bounded partial factorization
+ default(factor_add_primes,1);
+ timefact(N,sec)=
+ {
+   F = alarm(sec, factor(N));
+   if (type(F) == "t_ERROR", factor(N, 2^24), F);
+ }
+ @eprog\noindent We either return the factorization directly, or replace the
+ \typ{ERROR} result by a simple bounded factorization \kbd{factor(N, 2\pow 24)}.
+ Note the \tet{factor_add_primes} trick: any prime larger than $2^{24}$
+ discovered while attempting the initial factorization is stored and
+ remembered. When the alarm rings, the subsequent bounded factorization finds
+ it right away.
+
+ \misctitle{Caveat} It is not possible to set a new alarm \emph{within}
+ another \kbd{alarm} code: the new timer erases the parent one.
diff --git a/src/functions/programming/alias b/src/functions/programming/alias
new file mode 100644
index 0000000..c90bdad
--- /dev/null
+++ b/src/functions/programming/alias
@@ -0,0 +1,59 @@
+Function: alias
+Section: programming/specific
+C-Name: alias0
+Prototype: vrr
+Help: alias(newsym,sym): defines the symbol newsym as an alias for the symbol
+ sym.
+Doc: defines the symbol \var{newsym} as an alias for the the symbol \var{sym}:
+ \bprog
+ ? alias("det", "matdet");
+ ? det([1,2;3,4])
+ %1 = -2
+ @eprog\noindent
+ You are not restricted to ordinary functions, as in the above example:
+ to alias (from/to) member functions, prefix them with `\kbd{\_.}';
+ to alias operators, use their internal name, obtained by writing
+ \kbd{\_} in lieu of the operators argument: for instance, \kbd{\_!} and
+ \kbd{!\_} are the internal names of the factorial and the
+ logical negation, respectively.
+ \bprog
+ ? alias("mod", "_.mod");
+ ? alias("add", "_+_");
+ ? alias("_.sin", "sin");
+ ? mod(Mod(x,x^4+1))
+ %2 = x^4 + 1
+ ? add(4,6)
+ %3 = 10
+ ? Pi.sin
+ %4 = 0.E-37
+ @eprog
+ Alias expansion is performed directly by the internal GP compiler.
+ Note that since alias is performed at compilation-time, it does not
+ require any run-time processing, however it only affects GP code
+ compiled \emph{after} the alias command is evaluated. A slower but more
+ flexible alternative is to use variables. Compare
+ \bprog
+ ? fun = sin;
+ ? g(a,b) = intnum(t=a,b,fun(t));
+ ? g(0, Pi)
+ %3 = 2.0000000000000000000000000000000000000
+ ? fun = cos;
+ ? g(0, Pi)
+ %5 = 1.8830410776607851098 E-39
+ @eprog\noindent
+ with
+ \bprog
+ ? alias(fun, sin);
+ ? g(a,b) = intnum(t=a,b,fun(t));
+ ? g(0,Pi)
+ %2 = 2.0000000000000000000000000000000000000
+ ? alias(fun, cos);  \\ Oops. Does not affect *previous* definition!
+ ? g(0,Pi)
+ %3 = 2.0000000000000000000000000000000000000
+ ? g(a,b) = intnum(t=a,b,fun(t)); \\ Redefine, taking new alias into account
+ ? g(0,Pi)
+ %5 = 1.8830410776607851098 E-39
+ @eprog
+
+ A sample alias file \kbd{misc/gpalias} is provided with
+ the standard distribution.
diff --git a/src/functions/programming/allocatemem b/src/functions/programming/allocatemem
new file mode 100644
index 0000000..fb3cc1e
--- /dev/null
+++ b/src/functions/programming/allocatemem
@@ -0,0 +1,49 @@
+Function: allocatemem
+Class: gp
+Section: programming/specific
+C-Name: allocatemem0
+Prototype: vDG
+Help: allocatemem({s=0}): allocates a new stack of s bytes. doubles the
+ stack if s is omitted.
+Doc: this special operation changes the stack size \emph{after}
+ initialization. $x$ must be a non-negative integer. If $x > 0$, a new stack
+ of at least $x$ bytes is allocated. We may allocate more than $x$ bytes if
+ $x$ is way too small, or for alignment reasons: the current formula is
+ $\max(16*\ceil{x/16}, 500032)$ bytes.
+
+ If $x=0$, the size of the new stack is twice the size of the old one. The
+ old stack is discarded.
+
+ \misctitle{Warning} This function should be typed at the \kbd{gp} prompt in
+ interactive usage, or left by itself at the start of batch files.
+ It cannot be used meaningfully in loop-like constructs, or as part of a
+ larger expression sequence, e.g
+ \bprog
+    allocatemem(); x = 1;   \\@com This will not set \kbd{x}!
+ @eprog\noindent
+ In fact, all loops are immediately exited, user functions terminated, and
+ the rest of the sequence following \kbd{allocatemem()} is silently
+ discarded, as well as all pending sequences of instructions. We just go on
+ reading the next instruction sequence from the file we're in (or from the
+ user). In particular, we have the following possibly unexpected behavior: in
+ \bprog
+    read("file.gp"); x = 1
+ @eprog\noindent were \kbd{file.gp} contains an \kbd{allocatemem} statement,
+ the \kbd{x = 1} is never executed, since all pending instructions in the
+ current sequence are discarded.
+
+ The technical reason is that this routine moves the stack, so temporary
+ objects created during the current expression evaluation are not correct
+ anymore. (In particular byte-compiled expressions, which are allocated on
+ the stack.) To avoid accessing obsolete pointers to the old stack, this
+ routine ends by a \kbd{longjmp}.
+
+ \misctitle{Remark} If the operating system cannot allocate the desired
+ $x$ bytes, a loop halves the allocation size until it succeeds:
+ \bprog
+ ? allocatemem(5*10^10)
+  ***   Warning: not enough memory, new stack 50000000000.
+  ***   Warning: not enough memory, new stack 25000000000.
+  ***   Warning: not enough memory, new stack 12500000000.
+  ***   Warning: new stack size = 6250000000 (5960.464 Mbytes).
+ @eprog
diff --git a/src/functions/programming/apply b/src/functions/programming/apply
new file mode 100644
index 0000000..b753783
--- /dev/null
+++ b/src/functions/programming/apply
@@ -0,0 +1,55 @@
+Function: apply
+Section: programming/specific
+C-Name: apply0
+Prototype: GG
+Help: apply(f, A): apply function f to each entry in A.
+Wrapper: (G)
+Description:
+  (closure,gen):gen    genapply(${1 cookie}, ${1 wrapper}, $2)
+Doc: Apply the \typ{CLOSURE} \kbd{f} to the entries of \kbd{A}. If \kbd{A}
+ is a scalar, return \kbd{f(A)}. If \kbd{A} is a polynomial or power series,
+ apply \kbd{f} on all coefficients. If \kbd{A} is a vector or list, return
+ the elements $f(x)$ where $x$ runs through \kbd{A}. If \kbd{A} is a matrix,
+ return the matrix whose entries are the $f(\kbd{A[i,j]})$.
+ \bprog
+ ? apply(x->x^2, [1,2,3,4])
+ %1 = [1, 4, 9, 16]
+ ? apply(x->x^2, [1,2;3,4])
+ %2 =
+ [1 4]
+
+ [9 16]
+ ? apply(x->x^2, 4*x^2 + 3*x+ 2)
+ %3 = 16*x^2 + 9*x + 4
+ @eprog\noindent Note that many functions already act componentwise on
+ vectors or matrices, but they almost never act on lists; in this
+ case, \kbd{apply} is a good solution:
+ \bprog
+ ? L = List([Mod(1,3), Mod(2,4)]);
+ ? lift(L)
+   ***   at top-level: lift(L)
+   ***                 ^-------
+   *** lift: incorrect type in lift.
+ ? apply(lift, L);
+ %2 = List([1, 2])
+ @eprog
+ \misctitle{Remark} For $v$ a \typ{VEC}, \typ{COL}, \typ{LIST} or \typ{MAT},
+ the alternative set-notations
+ \bprog
+ [g(x) | x <- v, f(x)]
+ [x | x <- v, f(x)]
+ [g(x) | x <- v]
+ @eprog\noindent
+ are available as shortcuts for
+ \bprog
+ apply(g, select(f, Vec(v)))
+ select(f, Vec(v))
+ apply(g, Vec(v))
+ @eprog\noindent respectively:
+ \bprog
+ ? L = List([Mod(1,3), Mod(2,4)]);
+ ? [ lift(x) | x<-L ]
+ %2 = [1, 2]
+ @eprog
+
+ \synt{genapply}{void *E, GEN (*fun)(void*,GEN), GEN a}.
diff --git a/src/functions/programming/break b/src/functions/programming/break
new file mode 100644
index 0000000..ccb8c50
--- /dev/null
+++ b/src/functions/programming/break
@@ -0,0 +1,11 @@
+Function: break
+Section: programming/control
+C-Name: break0
+Prototype: D1,L,
+Help: break({n=1}): interrupt execution of current instruction sequence, and
+ exit from the n innermost enclosing loops.
+Doc: interrupts execution of current \var{seq}, and
+ immediately exits from the $n$ innermost enclosing loops, within the
+ current function call (or the top level loop); the integer $n$ must be
+ positive. If $n$ is greater than the number of enclosing loops, all
+ enclosing loops are exited.
diff --git a/src/functions/programming/breakpoint b/src/functions/programming/breakpoint
new file mode 100644
index 0000000..f4c160b
--- /dev/null
+++ b/src/functions/programming/breakpoint
@@ -0,0 +1,22 @@
+Function: breakpoint
+Class: gp
+Section: programming/control
+C-Name: pari_breakpoint
+Prototype: v
+Help: breakpoint(): interrupt the program and enter the breakloop. The program
+ continues when the breakloop is exited.
+Doc: Interrupt the program and enter the breakloop. The program continues when
+ the breakloop is exited.
+ \bprog
+ ? f(N,x)=my(z=x^2+1);breakpoint();gcd(N,z^2+1-z);
+ ? f(221,3)
+   ***   at top-level: f(221,3)
+   ***                 ^--------
+   ***   in function f: my(z=x^2+1);breakpoint();gcd(N,z
+   ***                              ^--------------------
+   ***   Break loop: type <Return> to continue; 'break' to go back to GP
+ break> z
+ 10
+ break>
+ %2 = 13
+ @eprog
diff --git a/src/functions/programming/dbg_down b/src/functions/programming/dbg_down
new file mode 100644
index 0000000..25a07f3
--- /dev/null
+++ b/src/functions/programming/dbg_down
@@ -0,0 +1,8 @@
+Function: dbg_down
+Class: gp
+Section: programming/control
+C-Name: dbg_down
+Prototype: vD1,L,
+Help: dbg_down({n=1}): (break loop) go down n frames. Cancel a previous dbg_up.
+Doc: (In the break loop) go down n frames. This allows to cancel a previous call to
+ \kbd{dbg\_up}.
diff --git a/src/functions/programming/dbg_err b/src/functions/programming/dbg_err
new file mode 100644
index 0000000..3117dab
--- /dev/null
+++ b/src/functions/programming/dbg_err
@@ -0,0 +1,19 @@
+Function: dbg_err
+Class: gp
+Section: programming/control
+C-Name: dbg_err
+Prototype:
+Help: dbg_err(): (break loop) return the error data of the current error, if any.
+Doc: In the break loop, return the error data of the current error, if any.
+ See \tet{iferr} for details about error data.  Compare:
+ \bprog
+ ? iferr(1/(Mod(2,12019)^(6!)-1),E,Vec(E))
+ %1 = ["e_INV", "Fp_inv", Mod(119, 12019)]
+ ? 1/(Mod(2,12019)^(6!)-1)
+   ***   at top-level: 1/(Mod(2,12019)^(6!)-
+   ***                  ^--------------------
+   *** _/_: impossible inverse in Fp_inv: Mod(119, 12019).
+   ***   Break loop: type 'break' to go back to GP prompt
+ break> Vec(dbg_err())
+ ["e_INV", "Fp_inv", Mod(119, 12019)]
+ @eprog
diff --git a/src/functions/programming/dbg_up b/src/functions/programming/dbg_up
new file mode 100644
index 0000000..56d7f22
--- /dev/null
+++ b/src/functions/programming/dbg_up
@@ -0,0 +1,8 @@
+Function: dbg_up
+Class: gp
+Section: programming/control
+C-Name: dbg_up
+Prototype: vD1,L,
+Help: dbg_up({n=1}): (break loop) go up n frames. Allow to inspect data of the parent function.
+Doc: (In the break loop) go up n frames. This allows to inspect data of the
+ parent function. To cancel a \tet{dbg_up} call, use \tet{dbg_down}
diff --git a/src/functions/programming/dbg_x b/src/functions/programming/dbg_x
new file mode 100644
index 0000000..08d6880
--- /dev/null
+++ b/src/functions/programming/dbg_x
@@ -0,0 +1,10 @@
+Function: dbg_x
+Section: programming/control
+C-Name: dbgGEN
+Prototype: vGD-1,L,
+Help: dbg_x(A{,n}): print inner structure of A, complete if n is omitted, up to
+ level n otherwise. Intended for debugging.
+Doc: Print the inner structure of \kbd{A}, complete if \kbd{n} is omitted, up
+ to level \kbd{n} otherwise. This is useful for debugging. This is similar to
+ \b{x} but does not require \kbd{A} to be an history entry. In particular,
+ it can be used in the break loop.
diff --git a/src/functions/programming/default b/src/functions/programming/default
new file mode 100644
index 0000000..1f289f1
--- /dev/null
+++ b/src/functions/programming/default
@@ -0,0 +1,32 @@
+Function: default
+Section: programming/specific
+C-Name: default0
+Prototype: DrDs
+Help: default({key},{val}): returns the current value of the
+ default key. If val is present, set opt to val first. If no argument is
+ given, print a list of all defaults as well as their values.
+Description:
+ ("realprecision"):small:prec              getrealprecision()
+ ("realprecision",small):small:prec        setrealprecision($2, &prec)
+ ("seriesprecision"):small                 precdl
+ ("seriesprecision",small):small:parens    precdl = $2
+ ("debug"):small                           DEBUGLEVEL
+ ("debug",small):small:parens              DEBUGLEVEL = $2
+ ("debugmem"):small                        DEBUGMEM
+ ("debugmem",small):small:parens           DEBUGMEM = $2
+ ("debugfiles"):small                      DEBUGFILES
+ ("debugfiles",small):small:parens         DEBUGFILES = $2
+ ("factor_add_primes"):small               factor_add_primes
+ ("factor_add_primes",small):small         factor_add_primes = $2
+ ("factor_proven"):small                   factor_proven
+ ("factor_proven",small):small             factor_proven = $2
+ ("new_galois_format"):small               new_galois_format
+ ("new_galois_format",small):small         new_galois_format = $2
+Doc: returns the default corresponding to keyword \var{key}. If \var{val} is
+ present, sets the default to \var{val} first (which is subject to string
+ expansion first). Typing \kbd{default()} (or \b{d}) yields the complete
+ default list as well as their current values. See \secref{se:defaults} for an
+ introduction to GP defaults, \secref{se:gp_defaults} for a
+ list of available defaults, and \secref{se:meta} for some shortcut
+ alternatives. Note that the shortcuts are meant for interactive use and
+ usually display more information than \kbd{default}.
diff --git a/src/functions/programming/errname b/src/functions/programming/errname
new file mode 100644
index 0000000..2440cb9
--- /dev/null
+++ b/src/functions/programming/errname
@@ -0,0 +1,8 @@
+Function: errname
+Section: programming/specific
+C-Name: errname
+Prototype: G
+Help: errname(E): returns the type of the error message E.
+Doc: returns the type of the error message \kbd{E} as a string.
+Description:
+ (gen):errtyp err_get_num($1)
diff --git a/src/functions/programming/error b/src/functions/programming/error
new file mode 100644
index 0000000..fe7e78c
--- /dev/null
+++ b/src/functions/programming/error
@@ -0,0 +1,25 @@
+Function: _err_primes
+Class: gp2c_internal
+Description:
+ ():void  pari_err(e_MAXPRIME)
+
+Function: _err_type
+Class: gp2c_internal
+Description:
+ (str,gen):void  pari_err_TYPE($1,$2)
+
+Function: error
+Section: programming/specific
+C-Name: error0
+Prototype: vs*
+Help: error({str}*): abort script with error message str.
+Description:
+ (error):void  pari_err(0, $1)
+ (?gen,...):void  pari_err(e_MISC, "${2 format_string}"${2 format_args})
+Doc: outputs its argument list (each of
+ them interpreted as a string), then interrupts the running \kbd{gp} program,
+ returning to the input prompt. For instance
+ \bprog
+ error("n = ", n, " is not squarefree!")
+ @eprog\noindent
+  % \syn{NO}
diff --git a/src/functions/programming/extern b/src/functions/programming/extern
new file mode 100644
index 0000000..5bbd46e
--- /dev/null
+++ b/src/functions/programming/extern
@@ -0,0 +1,10 @@
+Function: extern
+Class: gp
+Section: programming/specific
+C-Name: extern0
+Prototype: s
+Help: extern(str): execute shell command str, and feeds the result to GP (as
+ if loading from file).
+Doc: the string \var{str} is the name of an external command (i.e.~one you
+ would type from your UNIX shell prompt). This command is immediately run and
+ its output fed into \kbd{gp}, just as if read from a file.
diff --git a/src/functions/programming/externstr b/src/functions/programming/externstr
new file mode 100644
index 0000000..3fcf10c
--- /dev/null
+++ b/src/functions/programming/externstr
@@ -0,0 +1,11 @@
+Function: externstr
+Class: gp
+Section: programming/specific
+C-Name: externstr
+Prototype: s
+Help: externstr(str): execute shell command str, and returns the result as a
+ vector of GP strings, one component per output line.
+Doc: the string \var{str} is the name of an external command (i.e.~one you
+ would type from your UNIX shell prompt). This command is immediately run and
+ its output is returned as a vector of GP strings, one component per output
+ line.
diff --git a/src/functions/programming/for b/src/functions/programming/for
new file mode 100644
index 0000000..291bfb6
--- /dev/null
+++ b/src/functions/programming/for
@@ -0,0 +1,8 @@
+Function: for
+Section: programming/control
+C-Name: forpari
+Prototype: vV=GGI
+Help: for(X=a,b,seq): the sequence is evaluated, X going from a up to b.
+Doc: evaluates \var{seq}, where
+ the formal variable $X$ goes from $a$ to $b$. Nothing is done if $a>b$.
+ $a$ and $b$ must be in $\R$.
diff --git a/src/functions/programming/forcomposite b/src/functions/programming/forcomposite
new file mode 100644
index 0000000..c5969ba
--- /dev/null
+++ b/src/functions/programming/forcomposite
@@ -0,0 +1,43 @@
+Function: _forcomposite_init
+Class: gp2c_internal
+Help: Initialize forcomposite_t
+Description:
+ (forcomposite,int):void                  forcomposite_init(&$1, $2, NULL)
+ (forcomposite,int,int):void              forcomposite_init(&$1, $2, $3)
+
+Function: _forcomposite_next
+Class: gp2c_internal
+Help: Compute the next composite
+Description:
+ (forcomposite):int                       forcomposite_next(&$1)
+
+Function: forcomposite
+Section: programming/control
+C-Name: forcomposite
+Prototype: vV=GDGI
+Help: forcomposite(n=a,{b},seq): the sequence is evaluated, n running over the
+ composite numbers between a and b. Omitting b runs through composites >= a
+Iterator:
+ (gen,gen,?gen) (forcomposite, _forcomposite_init, _forcomposite_next)
+Doc: evaluates \var{seq},
+ where the formal variable $n$ ranges over the composite numbers between the
+ non-negative real numbers $a$ to $b$, including $a$ and $b$ if they are
+ composite. Nothing is done if $a>b$.
+ \bprog
+ ? forcomposite(n = 0, 10, print(n))
+ 4
+ 6
+ 8
+ 9
+ 10
+ @eprog\noindent Omitting $b$ means we will run through all composites $\geq a$,
+ starting an infinite loop; it is expected that the user will break out of
+ the loop himself at some point, using \kbd{break} or \kbd{return}.
+
+ Note that the value of $n$ cannot be modified within \var{seq}:
+ \bprog
+ ? forcomposite(n = 2, 10, n = [])
+  ***   at top-level: forcomposite(n=2,10,n=[])
+  ***                                      ^---
+  ***   index read-only: was changed to [].
+ @eprog
diff --git a/src/functions/programming/fordiv b/src/functions/programming/fordiv
new file mode 100644
index 0000000..97db240
--- /dev/null
+++ b/src/functions/programming/fordiv
@@ -0,0 +1,35 @@
+Function: fordiv
+Section: programming/control
+C-Name: fordiv
+Prototype: vGVI
+Help: fordiv(n,X,seq): the sequence is evaluated, X running over the
+ divisors of n.
+Doc: evaluates \var{seq}, where
+ the formal variable $X$ ranges through the divisors of $n$
+ (see \tet{divisors}, which is used as a subroutine). It is assumed that
+ \kbd{factor} can handle $n$, without negative exponents. Instead of $n$,
+ it is possible to input a factorization matrix, i.e. the output of
+ \kbd{factor(n)}.
+
+ This routine uses \kbd{divisors} as a subroutine, then loops over the
+ divisors. In particular, if $n$ is an integer, divisors are sorted by
+ increasing size.
+
+ To avoid storing all divisors, possibly using a lot of memory, the following
+ (much slower) routine loops over the divisors using essentially constant
+ space:
+ \bprog
+ FORDIV(N)=
+ { my(P, E);
+
+   P = factor(N); E = P[,2]; P = P[,1];
+   forvec( v = vector(#E, i, [0,E[i]]),
+   X = factorback(P, v)
+   \\ ...
+ );
+ }
+ ? for(i=1,10^5, FORDIV(i))
+ time = 3,445 ms.
+ ? for(i=1,10^5, fordiv(i, d, ))
+ time = 490 ms.
+ @eprog
diff --git a/src/functions/programming/forell b/src/functions/programming/forell
new file mode 100644
index 0000000..f5a470b
--- /dev/null
+++ b/src/functions/programming/forell
@@ -0,0 +1,25 @@
+Function: forell
+Section: programming/control
+C-Name: forell0
+Prototype: vVLLI
+Help: forell(E,a,b,seq): execute seq for each elliptic curves E of conductor
+ between a and b in the elldata database.
+Wrapper: (,,,vG)
+Description:
+ (,small,small,closure):void  forell(${4 cookie}, ${4 wrapper}, $2, $3)
+Doc: evaluates \var{seq}, where the formal variable $E = [\var{name}, M, G]$
+ ranges through all elliptic curves of conductors from $a$ to $b$. In this
+ notation \var{name} is the curve name in Cremona's elliptic  curve  database,
+ $M$ is the minimal model, $G$ is a $\Z$-basis of the free part of the
+ Mordell-Weil group $E(\Q)$.
+ \bprog
+ ? forell(E, 1, 500, my([name,M,G] = E); \
+     if (#G > 1, print(name)))
+ 389a1
+ 433a1
+ 446d1
+ @eprog\noindent
+ The \tet{elldata} database must be installed and contain data for the
+ specified conductors.
+
+ \synt{forell}{void *data, long (*call)(void*,GEN), long a, long b}.
diff --git a/src/functions/programming/forpart b/src/functions/programming/forpart
new file mode 100644
index 0000000..5b7a815
--- /dev/null
+++ b/src/functions/programming/forpart
@@ -0,0 +1,68 @@
+Function: _forpart_init
+Class: gp2c_internal
+Help: Initialize forpart_t
+Description:
+ (forpart,small,?gen,?gen):void      forpart_init(&$1, $2, $3, $4)
+
+Function: _forpart_next
+Class: gp2c_internal
+Help: Compute the next part
+Description:
+ (forpart):vecsmall                  forpart_next(&$1)
+
+Function: forpart
+Section: programming/control
+C-Name: forpart0
+Prototype: vV=GIDGDG
+Iterator:
+ (gen,small,?gen,?gen)         (forpart, _forpart_init, _forpart_next)
+Wrapper: (,vG,,)
+Description:
+ (small,closure,?gen,?gen):void forpart(${2 cookie}, ${2 wrapper}, $1, $3, $4)
+Help: forpart(X=k,seq,{a=k},{n=k}): evaluate seq where the Vecsmall X
+ goes over the partitions of k. Optional parameter n (n=nmax or n=[nmin,nmax])
+ restricts the length of the partition. Optional parameter a (a=amax or
+ a=[amin,amax]) restricts the range of the parts. Zeros are removed unless one
+ sets amin=0 to get X of fixed length nmax (=k by default).
+Doc: evaluate \var{seq} over the partitions $X=[x_1,\dots x_n]$ of the
+ integer $k$, i.e.~increasing sequences $x_1\leq x_2\dots \leq x_n$ of sum
+ $x_1+\dots + x_n=k$. By convention, $0$ admits only the empty partition and
+ negative numbers have no partitions. A partition is given by a
+ \typ{VECSMALL}, where parts are sorted in nondecreasing order:
+ \bprog
+ ? forpart(X=3, print(X))
+ Vecsmall([3])
+ Vecsmall([1, 2])
+ Vecsmall([1, 1, 1])
+ @eprog\noindent Optional parameters $n$ and $a$ are as follows:
+
+ \item $n=\var{nmax}$ (resp. $n=[\var{nmin},\var{nmax}]$) restricts
+ partitions to length less than $\var{nmax}$ (resp. length between
+ $\var{nmin}$ and $nmax$), where the \emph{length} is the number of nonzero
+ entries.
+
+ \item $a=\var{amax}$ (resp. $a=[\var{amin},\var{amax}]$) restricts the parts
+ to integers less than $\var{amax}$ (resp. between $\var{amin}$ and
+ $\var{amax}$).
+
+ By default, parts are positive and we remove zero entries unless $amin\leq0$,
+ in which case $X$ is of constant length $\var{nmax}$.
+ \bprog
+ \\ at most 3 non-zero parts, all <= 4
+ ? forpart(v=5,print(Vec(v)),4,3)
+ [1, 4]
+ [2, 3]
+ [1, 1, 3]
+ [1, 2, 2]
+
+ \\ between 2 and 4 parts less than 5, fill with zeros
+ ? forpart(v=5,print(Vec(v)),[0,5],[2,4])
+ [0, 0, 1, 4]
+ [0, 0, 2, 3]
+ [0, 1, 1, 3]
+ [0, 1, 2, 2]
+ [1, 1, 1, 2]
+ @eprog\noindent
+ The behavior is unspecified if $X$ is modified inside the loop.
+
+ \synt{forpart}{void *data, long (*call)(void*,GEN), long k, GEN a, GEN n}.
diff --git a/src/functions/programming/forprime b/src/functions/programming/forprime
new file mode 100644
index 0000000..4164267
--- /dev/null
+++ b/src/functions/programming/forprime
@@ -0,0 +1,69 @@
+Function: _diffptr
+Class: gp2c_internal
+Help: Table of difference of primes.
+Description:
+ ():bptr        diffptr
+
+Function: _forprime_next
+Class: gp2c_internal
+Help: Compute the next prime from the diffptr table.
+Description:
+ (*small,*bptr):void  NEXT_PRIME_VIADIFF($1, $2)
+
+Function: _forprime_init
+Class: gp2c_internal
+Help: Initialize forprime_t
+Description:
+ (forprime,int,?int):void             forprime_init(&$1, $2, $3);
+
+Function: _forprime_next_
+Class: gp2c_internal
+Help: Compute the next prime
+Description:
+ (forprime):int                       forprime_next(&$1)
+
+Function: _u_forprime_init
+Class: gp2c_internal
+Help: Initialize forprime_t (ulong version)
+Description:
+ (forprime,small,):void              u_forprime_init(&$1, $2, LONG_MAX);
+ (forprime,small,small):void         u_forprime_init(&$1, $2, $3);
+
+Function: _u_forprime_next
+Class: gp2c_internal
+Help: Compute the next prime (ulong version)
+Description:
+ (forprime):small                   u_forprime_next(&$1)
+
+Function: forprime
+Section: programming/control
+C-Name: forprime
+Prototype: vV=GDGI
+Help: forprime(p=a,{b},seq): the sequence is evaluated, p running over the
+ primes between a and b. Omitting b runs through primes >= a
+Iterator:
+ (*notype,small,small) (forprime, _u_forprime_init, _u_forprime_next)
+ (*small,gen,?gen)    (forprime, _u_forprime_init, _u_forprime_next)
+ (*int,gen,?gen)      (forprime, _forprime_init, _forprime_next_)
+ (gen,gen,?gen)       (forprime, _forprime_init, _forprime_next_)
+Doc: evaluates \var{seq},
+ where the formal variable $p$ ranges over the prime numbers between the real
+ numbers $a$ to $b$, including $a$ and $b$ if they are prime. More precisely,
+ the value of
+ $p$ is incremented to \kbd{nextprime($p$ + 1)}, the smallest prime strictly
+ larger than $p$, at the end of each iteration. Nothing is done if $a>b$.
+ \bprog
+ ? forprime(p = 4, 10, print(p))
+ 5
+ 7
+ @eprog\noindent Omitting $b$ means we will run through all primes $\geq a$,
+ starting an infinite loop; it is expected that the user will break out of
+ the loop himself at some point, using \kbd{break} or \kbd{return}.
+
+ Note that the value of $p$ cannot be modified within \var{seq}:
+ \bprog
+ ? forprime(p = 2, 10, p = [])
+  ***   at top-level: forprime(p=2,10,p=[])
+  ***                                   ^---
+  ***   prime index read-only: was changed to [].
+ @eprog
diff --git a/src/functions/programming/forstep b/src/functions/programming/forstep
new file mode 100644
index 0000000..809ebe2
--- /dev/null
+++ b/src/functions/programming/forstep
@@ -0,0 +1,21 @@
+Function: forstep
+Section: programming/control
+C-Name: forstep
+Prototype: vV=GGGI
+Help: forstep(X=a,b,s,seq): the sequence is evaluated, X going from a to b
+ in steps of s (can be a vector of steps).
+Doc: evaluates \var{seq},
+ where the formal variable $X$ goes from $a$ to $b$, in increments of $s$.
+ Nothing is done if $s>0$ and $a>b$ or if $s<0$ and $a<b$. $s$ must be in
+ $\R^*$ or a vector of steps $[s_1,\dots,s_n]$. In the latter case, the
+ successive steps are used in the order they appear in $s$.
+
+ \bprog
+ ? forstep(x=5, 20, [2,4], print(x))
+ 5
+ 7
+ 11
+ 13
+ 17
+ 19
+ @eprog
diff --git a/src/functions/programming/forsubgroup b/src/functions/programming/forsubgroup
new file mode 100644
index 0000000..d23316c
--- /dev/null
+++ b/src/functions/programming/forsubgroup
@@ -0,0 +1,52 @@
+Function: forsubgroup
+Section: programming/control
+C-Name: forsubgroup0
+Prototype: vV=GDGI
+Help: forsubgroup(H=G,{bound},seq): execute seq for each subgroup H of the
+ abelian group G, whose index is bounded by bound if not omitted. H is given
+ as a left divisor of G in HNF form.
+Wrapper: (,,vG)
+Description:
+ (gen,?gen,closure):void  forsubgroup(${3 cookie}, ${3 wrapper}, $1, $2)
+Doc: evaluates \var{seq} for
+ each subgroup $H$ of the \emph{abelian} group $G$ (given in
+ SNF\sidx{Smith normal form} form or as a vector of elementary divisors).
+
+ If \var{bound} is present, and is a positive integer, restrict the output to
+ subgroups of index less than \var{bound}. If \var{bound} is a vector
+ containing a single positive integer $B$, then only subgroups of index
+ exactly equal to $B$ are computed
+
+ The subgroups are not ordered in any
+ obvious way, unless $G$ is a $p$-group in which case Birkhoff's algorithm
+ produces them by decreasing index. A \idx{subgroup} is given as a matrix
+ whose columns give its generators on the implicit generators of $G$. For
+ example, the following prints all subgroups of index less than 2 in $G =
+ \Z/2\Z g_1 \times \Z/2\Z g_2$:
+
+ \bprog
+ ? G = [2,2]; forsubgroup(H=G, 2, print(H))
+ [1; 1]
+ [1; 2]
+ [2; 1]
+ [1, 0; 1, 1]
+ @eprog\noindent
+ The last one, for instance is generated by $(g_1, g_1 + g_2)$. This
+ routine is intended to treat huge groups, when \tet{subgrouplist} is not an
+ option due to the sheer size of the output.
+
+ For maximal speed the subgroups have been left as produced by the algorithm.
+ To print them in canonical form (as left divisors of $G$ in HNF form), one
+ can for instance use
+ \bprog
+ ? G = matdiagonal([2,2]); forsubgroup(H=G, 2, print(mathnf(concat(G,H))))
+ [2, 1; 0, 1]
+ [1, 0; 0, 2]
+ [2, 0; 0, 1]
+ [1, 0; 0, 1]
+ @eprog\noindent
+ Note that in this last representation, the index $[G:H]$ is given by the
+ determinant. See \tet{galoissubcyclo} and \tet{galoisfixedfield} for
+ applications to \idx{Galois} theory.
+
+ \synt{forsubgroup}{void *data, long (*call)(void*,GEN), GEN G, GEN bound}.
diff --git a/src/functions/programming/forvec b/src/functions/programming/forvec
new file mode 100644
index 0000000..3ba624f
--- /dev/null
+++ b/src/functions/programming/forvec
@@ -0,0 +1,31 @@
+Function: _forvec_init
+Class: gp2c_internal
+Help: Initializes parameters for forvec.
+Description:
+ (forvec, gen, ?small):void    forvec_init(&$1, $2, $3)
+
+Function: _forvec_next
+Class: gp2c_internal
+Help: Initializes parameters for forvec.
+Description:
+ (forvec):vec    forvec_next(&$1)
+
+Function: forvec
+Section: programming/control
+C-Name: forvec
+Prototype: vV=GID0,L,
+Iterator: (gen,gen,?small) (forvec, _forvec_init, _forvec_next)
+Help: forvec(X=v,seq,{flag=0}): v being a vector of two-component vectors of
+ length n, the sequence is evaluated with X[i] going from v[i][1] to v[i][2]
+ for i=n,..,1 if flag is zero or omitted. If flag = 1 (resp. flag = 2),
+ restrict to increasing (resp. strictly increasing) sequences.
+Doc: Let $v$ be an $n$-component
+ vector (where $n$ is arbitrary) of two-component vectors $[a_i,b_i]$
+ for $1\le i\le n$. This routine evaluates \var{seq}, where the formal
+ variables $X[1],\dots, X[n]$ go from $a_1$ to $b_1$,\dots, from $a_n$ to
+ $b_n$, i.e.~$X$ goes from $[a_1,\dots,a_n]$ to $[b_1,\dots,b_n]$ with respect
+ to the lexicographic ordering. (The formal variable with the highest index
+ moves the fastest.) If $\fl=1$, generate only nondecreasing vectors $X$, and
+ if $\fl=2$, generate only strictly increasing vectors $X$.
+
+ The type of $X$ is the same as the type of $v$: \typ{VEC} or \typ{COL}.
diff --git a/src/functions/programming/getabstime b/src/functions/programming/getabstime
new file mode 100644
index 0000000..15d800f
--- /dev/null
+++ b/src/functions/programming/getabstime
@@ -0,0 +1,12 @@
+Function: getabstime
+Section: programming/specific
+C-Name: getabstime
+Prototype: l
+Help: getabstime(): time (in milliseconds) since startup.
+Doc: returns the time (in milliseconds) elapsed since \kbd{gp} startup. This
+ provides a reentrant version of \kbd{gettime}:
+ \bprog
+ my (t = getabstime());
+ ...
+ print("Time: ", getabstime() - t);
+ @eprog
diff --git a/src/functions/programming/getenv b/src/functions/programming/getenv
new file mode 100644
index 0000000..cb97821
--- /dev/null
+++ b/src/functions/programming/getenv
@@ -0,0 +1,6 @@
+Function: getenv
+Section: programming/specific
+C-Name: gp_getenv
+Prototype: s
+Help: getenv(s): value of the environment variable s, 0 if it is not defined.
+Doc: return the value of the environment variable \kbd{s} if it is defined, otherwise return 0.
diff --git a/src/functions/programming/getheap b/src/functions/programming/getheap
new file mode 100644
index 0000000..a83ea59
--- /dev/null
+++ b/src/functions/programming/getheap
@@ -0,0 +1,9 @@
+Function: getheap
+Section: programming/specific
+C-Name: getheap
+Prototype:
+Help: getheap(): 2-component vector giving the current number of objects in
+ the heap and the space they occupy.
+Doc: returns a two-component row vector giving the
+ number of objects on the heap and the amount of memory they occupy in long
+ words. Useful mainly for debugging purposes.
diff --git a/src/functions/programming/getrand b/src/functions/programming/getrand
new file mode 100644
index 0000000..cb307c7
--- /dev/null
+++ b/src/functions/programming/getrand
@@ -0,0 +1,10 @@
+Function: getrand
+Section: programming/specific
+C-Name: getrand
+Prototype:
+Help: getrand(): current value of random number seed.
+Doc: returns the current value of the seed used by the
+ pseudo-random number generator \tet{random}. Useful mainly for debugging
+ purposes, to reproduce a specific chain of computations. The returned value
+ is technical (reproduces an internal state array), and can only be used as an
+ argument to \tet{setrand}.
diff --git a/src/functions/programming/getstack b/src/functions/programming/getstack
new file mode 100644
index 0000000..0cfda43
--- /dev/null
+++ b/src/functions/programming/getstack
@@ -0,0 +1,7 @@
+Function: getstack
+Section: programming/specific
+C-Name: getstack
+Prototype: l
+Help: getstack(): current value of stack pointer avma.
+Doc: returns the current value of $\kbd{top}-\kbd{avma}$, i.e.~the number of
+ bytes used up to now on the stack. Useful mainly for debugging purposes.
diff --git a/src/functions/programming/gettime b/src/functions/programming/gettime
new file mode 100644
index 0000000..a1b79f6
--- /dev/null
+++ b/src/functions/programming/gettime
@@ -0,0 +1,10 @@
+Function: gettime
+Section: programming/specific
+C-Name: gettime
+Prototype: l
+Help: gettime(): time (in milliseconds) since last call to gettime.
+Doc: returns the time (in milliseconds) elapsed since either the last call to
+ \kbd{gettime}, or to the beginning of the containing GP instruction (if
+ inside \kbd{gp}), whichever came last.
+
+ For a reentrant version, see \tet{getabstime}.
diff --git a/src/functions/programming/global b/src/functions/programming/global
new file mode 100644
index 0000000..d75ba28
--- /dev/null
+++ b/src/functions/programming/global
@@ -0,0 +1,5 @@
+Function: global
+Section: programming/specific
+Help: global(list of variables): obsolete. Scheduled for deletion.
+Doc: obsolete. Scheduled for deletion.
+ % \syn{NO}
diff --git a/src/functions/programming/if b/src/functions/programming/if
new file mode 100644
index 0000000..d473a3a
--- /dev/null
+++ b/src/functions/programming/if
@@ -0,0 +1,72 @@
+Function: if
+Section: programming/control
+C-Name: ifpari
+Prototype: GDEDE
+Help: if(a,{seq1},{seq2}): if a is nonzero, seq1 is evaluated, otherwise seq2.
+ seq1 and seq2 are optional, and if seq2 is omitted, the preceding comma can
+ be omitted also.
+Doc: evaluates the expression sequence \var{seq1} if $a$ is non-zero, otherwise
+ the expression \var{seq2}. Of course, \var{seq1} or \var{seq2} may be empty:
+
+ \kbd{if ($a$,\var{seq})} evaluates \var{seq} if $a$ is not equal to zero
+ (you don't have to write the second comma), and does nothing otherwise,
+
+ \kbd{if ($a$,,\var{seq})} evaluates \var{seq} if $a$ is equal to zero, and
+ does nothing otherwise. You could get the same result using the \kbd{!}
+ (\kbd{not}) operator: \kbd{if (!$a$,\var{seq})}.
+
+ The value of an \kbd{if} statement is the value of the branch that gets
+ evaluated: for instance
+ \bprog
+ x = if(n % 4 == 1, y, z);
+ @eprog\noindent sets $x$ to $y$ if $n$ is $1$ modulo $4$, and to $z$
+ otherwise.
+
+ Successive 'else' blocks can be abbreviated in a single compound \kbd{if}
+ as follows:
+ \bprog
+ if (test1, seq1,
+     test2, seq2,
+     ...
+     testn, seqn,
+     seqdefault);
+ @eprog\noindent is equivalent to
+ \bprog
+ if (test1, seq1
+          , if (test2, seq2
+                     , ...
+                       if (testn, seqn, seqdefault)...));
+ @eprog For instance, this allows to write traditional switch / case
+ constructions:
+ \bprog
+ if (x == 0, do0(),
+     x == 1, do1(),
+     x == 2, do2(),
+     dodefault());
+ @eprog
+
+ \misctitle{Remark}
+ The boolean operators \kbd{\&\&} and \kbd{||} are evaluated
+ according to operator precedence as explained in \secref{se:operators}, but,
+ contrary to other operators, the evaluation of the arguments is stopped
+ as soon as the final truth value has been determined. For instance
+ \bprog
+ if (x != 0 && f(1/x), ...)
+ @eprog
+ \noindent is a perfectly safe statement.
+
+ \misctitle{Remark} Functions such as \kbd{break} and \kbd{next} operate on
+ \emph{loops}, such as \kbd{for$xxx$}, \kbd{while}, \kbd{until}. The \kbd{if}
+ statement is \emph{not} a loop. (Obviously!)
+
+Function: _void_if
+C-Name: ifpari_void
+Section: programming/internals
+Prototype: vGDIDI
+Help: internal variant of if() that does not return a value.
+
+Function: _multi_if
+C-Name: ifpari_multi
+Section: programming/internals
+Prototype: GE*
+Help: internal variant of if() that allows more than 3 arguments.
diff --git a/src/functions/programming/iferr b/src/functions/programming/iferr
new file mode 100644
index 0000000..e8d8058
--- /dev/null
+++ b/src/functions/programming/iferr
@@ -0,0 +1,236 @@
+Function: _iferr_CATCH
+Class: gp2c_internal
+Description:
+  (0)               pari_CATCH(CATCH_ALL)
+  (small)           pari_CATCH2(__iferr_old$1, CATCH_ALL)
+
+Function: _iferr_ENDCATCH
+Class: gp2c_internal
+Description:
+  (0)        pari_ENDCATCH
+  (small)    pari_ENDCATCH2(__iferr_old$1)
+
+Function: _iferr_CATCH_reset
+Class: gp2c_internal
+Description:
+  (0):void      pari_CATCH_reset()
+  (small):void  pari_CATCH2_reset(__iferr_old$1)
+
+Function: _iferr_error
+Class: gp2c_internal
+Description:
+  ():error pari_err_last()
+
+Function: _iferr_rethrow
+Class: gp2c_internal
+Description:
+  (error):void    pari_err(0, $1)
+
+Function: iferr
+Section: programming/control
+C-Name: iferrpari
+Prototype: EVEDE
+Help: iferr(seq1,E,seq2{,pred}): evaluates the expression sequence seq1. If
+ an error occurs, set the formal parameter E set to the error data.
+ If pred is not present or evaluates to true, catch the error and evaluate
+ seq2. Both pred and seq2 can reference E.
+Doc: evaluates the expression sequence \var{seq1}. If an error occurs,
+ set the formal parameter \var{E} set to the error data.
+ If \var{pred} is not present or evaluates to true, catch the error
+ and evaluate \var{seq2}. Both \var{pred} and \var{seq2} can reference \var{E}.
+ The error type is given by \kbd{errname(E)}, and other data can be
+ accessed using the \tet{component} function. The code \var{seq2} should check
+ whether the error is the one expected. In the negative the error can be
+ rethrown using \tet{error(E)} (and possibly caught by an higher \kbd{iferr}
+ instance). The following uses \kbd{iferr} to implement Lenstra's ECM factoring
+  method
+ \bprog
+ ? ecm(N, B = 1000!, nb = 100)=
+   {
+     for(a = 1, nb,
+       iferr(ellmul(ellinit([a,1]*Mod(1,N)), [0,1]*Mod(1,N), B),
+         E, return(gcd(lift(component(E,2)),N)),
+         errname(E)=="e_INV" && type(component(E,2)) == "t_INTMOD"))
+   }
+ ? ecm(2^101-1)
+ %2 = 7432339208719
+ @eprog
+ The return value of \kbd{iferr} itself is the value of \var{seq2} if an
+ error occurs, and the value of \var{seq1} otherwise. We now describe the
+ list of valid error types, and the associated error data \var{E}; in each
+ case, we list in order the components of \var{E}, accessed via
+ \kbd{component(E,1)}, \kbd{component(E,2)}, etc.
+
+  \misctitle{Internal errors, ``system'' errors}
+
+  \item \kbd{"e\_ARCH"}. A requested feature $s$ is not available on this
+  architecture or operating system.
+  \var{E} has one component (\typ{STR}): the missing feature name $s$.
+
+  \item \kbd{"e\_BUG"}. A bug in the PARI library, in function $s$.
+  \var{E} has one component (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_FILE"}. Error while trying to open a file.
+  \var{E} has two components, 1 (\typ{STR}): the file type (input, output,
+  etc.), 2 (\typ{STR}): the file name.
+
+  \item \kbd{"e\_IMPL"}. A requested feature $s$ is not implemented.
+  \var{E} has one component, 1 (\typ{STR}): the feature name $s$.
+
+  \item \kbd{"e\_PACKAGE"}. Missing optional package $s$.
+  \var{E} has one component, 1 (\typ{STR}): the package name $s$.
+
+  \misctitle{Syntax errors, type errors}
+
+  \item \kbd{"e\_DIM"}. The dimensions of arguments $x$ and $y$ submitted
+  to function $s$ does not match up.
+  E.g., multiplying matrices of inconsistent dimension, adding vectors of
+  different lengths,\dots
+  \var{E} has three component, 1 (\typ{STR}): the function name $s$, 2: the
+  argument $x$, 3: the argument $y$.
+
+  \item \kbd{"e\_FLAG"}. A flag argument is out of bounds in function $s$.
+  \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_NOTFUNC"}. Generated by the PARI evaluator; tried to use a
+ \kbd{GEN} $x$ which is not a \typ{CLOSURE} in a function call syntax (as in
+ \kbd{f = 1; f(2);}).
+  \var{E} has one component, 1: the offending \kbd{GEN} $x$.
+
+  \item \kbd{"e\_OP"}. Impossible operation between two objects than cannot
+  be typecast to a sensible common domain for deeper reasons than a type
+  mismatch, usually for arithmetic reasons. As in \kbd{O(2) + O(3)}: it is
+  valid to add two \typ{PADIC}s, provided the underlying prime is the same; so
+  the addition is not forbidden a priori for type reasons, it only becomes so
+  when inspecting the objects and trying to perform the operation.
+  \var{E} has three components, 1 (\typ{STR}): the operator name \var{op},
+  2: first argument, 3: second argument.
+
+  \item \kbd{"e\_TYPE"}. An argument $x$ of function $s$ had an unexpected type.
+  (As in \kbd{factor("blah")}.)
+  \var{E} has two components, 1 (\typ{STR}): the function name $s$,
+  2: the offending argument $x$.
+
+  \item \kbd{"e\_TYPE2"}. Forbidden operation between two objects than cannot be
+  typecast to a sensible common domain, because their types do not match up.
+  (As in \kbd{Mod(1,2) + Pi}.)
+  \var{E} has three components, 1 (\typ{STR}): the operator name \var{op},
+  2: first argument, 3: second argument.
+
+  \item \kbd{"e\_PRIORITY"}. Object $o$ in function $s$ contains
+  variables whose priority is incompatible with the expected operation.
+  E.g.~\kbd{Pol([x,1], 'y)}: this raises an error because it's not possible to
+  create a polynomial whose coefficients involve variables with higher priority
+  than the main variable. $E$ has four components: 1 (\typ{STR}): the function
+  name $s$, 2: the offending argument $o$, 3 (\typ{STR}): an operator
+  $\var{op}$ describing the priority error, 4 (\typ{POL}):
+  the variable $v$ describing the priority error. The argument
+  satisfies $\kbd{variable}(x)~\var{op} \kbd{variable}(v)$.
+
+  \item \kbd{"e\_VAR"}. The variables of arguments $x$ and $y$ submitted
+  to function $s$ does not match up. E.g., considering the algebraic number
+  \kbd{Mod(t,t\pow2+1)} in \kbd{nfinit(x\pow2+1)}.
+  \var{E} has three component, 1 (\typ{STR}): the function name $s$, 2
+  (\typ{POL}): the argument $x$, 3 (\typ{POL}): the argument $y$.
+
+  \misctitle{Overflows}
+
+  \item \kbd{"e\_COMPONENT"}. Trying to access an inexistent component in a
+  vector/matrix/list in a function: the index is less than $1$ or greater
+  than the allowed length.
+  \var{E} has four components,
+  1 (\typ{STR}): the function name
+  2 (\typ{STR}): an operator $\var{op}$ ($<$ or $>$),
+  2 (\typ{GEN}): a numerical limit $l$ bounding the allowed range,
+  3 (\kbd{GEN}): the index $x$. It satisfies $x$ \var{op} $l$.
+
+  \item \kbd{"e\_DOMAIN"}. An argument is not in the function's domain.
+  \var{E} has five components, 1 (\typ{STR}): the function name,
+  2 (\typ{STR}): the mathematical name of the out-of-domain argument
+  3 (\typ{STR}): an operator $\var{op}$ describing the domain error,
+  4 (\typ{GEN}): the numerical limit $l$ describing the domain error,
+  5 (\kbd{GEN}): the out-of-domain argument $x$. The argument satisfies $x$
+  \var{op} $l$, which prevents it from belonging to the function's domain.
+
+  \item \kbd{"e\_MAXPRIME"}. A function using the precomputed list of prime
+  numbers ran out of primes.
+  \var{E} has one component, 1 (\typ{INT}): the requested prime bound, which
+  overflowed \kbd{primelimit} or $0$ (bound is unknown).
+
+  \item \kbd{"e\_MEM"}. A call to \tet{pari_malloc} or \tet{pari_realloc}
+  failed. \var{E} has no component.
+
+  \item \kbd{"e\_OVERFLOW"}. An object in function $s$ becomes too large to be
+  represented within PARI's hardcoded limits. (As in \kbd{2\pow2\pow2\pow10} or
+  \kbd{exp(1e100)}, which overflow in \kbd{lg} and \kbd{expo}.)
+  \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_PREC"}. Function $s$ fails because input accuracy is too low.
+  (As in \kbd{floor(1e100)} at default accuracy.)
+  \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_STACK"}. The PARI stack overflows.
+  \var{E} has no component.
+
+  \misctitle{Errors triggered intentionally}
+
+  \item \kbd{"e\_ALARM"}. A timeout, generated by the \tet{alarm} function.
+  \var{E} has one component (\typ{STR}): the error message to print.
+
+  \item \kbd{"e\_USER"}. A user error, as triggered by
+  \tet{error}($g_1,\dots,g_n)$.
+  \var{E} has one component, 1 (\typ{VEC}): the vector of $n$ arguments given
+  to \kbd{error}.
+
+  \misctitle{Mathematical errors}
+
+  \item \kbd{"e\_CONSTPOL"}. An argument of function $s$ is a constant
+  polynomial, which does not make sense. (As in \kbd{galoisinit(Pol(1))}.)
+  \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_COPRIME"}. Function $s$ expected coprime arguments,
+  and did receive $x,y$, which were not.
+  \var{E} has three component, 1 (\typ{STR}): the function name $s$,
+  2: the argument $x$, 3: the argument $y$.
+
+  \item \kbd{"e\_INV"}. Tried to invert a non-invertible object $x$ in
+  function $s$.
+  \var{E} has two components, 1 (\typ{STR}): the function name $s$,
+  2: the non-invertible $x$. If $x = \kbd{Mod}(a,b)$
+  is a \typ{INTMOD} and $a$ is not $0$ mod $b$, this allows to factor
+  the modulus, as \kbd{gcd}$(a,b)$ is a non-trivial divisor of $b$.
+
+  \item \kbd{"e\_IRREDPOL"}. Function $s$ expected an irreducible polynomial,
+  and did receive $T$, which was not. (As in \kbd{nfinit(x\pow2-1)}.)
+  \var{E} has two component, 1 (\typ{STR}): the function name $s$,
+  2 (\typ{POL}): the polynomial $x$.
+
+  \item \kbd{"e\_MISC"}. Generic uncategorized error.
+  \var{E} has one component (\typ{STR}): the error message to print.
+
+  \item \kbd{"e\_MODULUS"}. moduli $x$ and $y$ submitted to function $s$ are
+  inconsistent. As in
+  \bprog
+    nfalgtobasis(nfinit(t^3-2), Mod(t,t^2+1)
+  @eprog\noindent
+  \var{E} has three component, 1 (\typ{STR}): the function $s$,
+  2: the argument $x$, 3: the argument $x$.
+
+  \item \kbd{"e\_NEGVAL"}. An argument of function $s$ is a power series with
+  negative valuation, which does not make sense. (As in \kbd{cos(1/x)}.)
+  \var{E} has one component, 1 (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_PRIME"}. Function $s$ expected a prime number,
+  and did receive $p$, which was not. (As in \kbd{idealprimedec(nf, 4)}.)
+  \var{E} has two component, 1 (\typ{STR}): the function name $s$,
+  2: the argument $p$.
+
+  \item \kbd{"e\_ROOTS0"}. An argument of function $s$ is a zero polynomial,
+  and we need to consider its roots. (As in \kbd{polroots(0)}.) \var{E} has
+  one component, 1 (\typ{STR}): the function name $s$.
+
+  \item \kbd{"e\_SQRTN"}. Trying to compute an $n$-th root of $x$, which does
+  not exist, in function $s$. (As in \kbd{sqrt(Mod(-1,3))}.)
+  \var{E} has two components, 1 (\typ{STR}): the function name $s$,
+  2: the argument $x$.
+
diff --git a/src/functions/programming/inline b/src/functions/programming/inline
new file mode 100644
index 0000000..3cc4b7c
--- /dev/null
+++ b/src/functions/programming/inline
@@ -0,0 +1,8 @@
+Function: inline
+Section: programming/specific
+Help: inline(x,...,z): declares x,...,z as inline variables [EXPERIMENTAL]
+Doc: (Experimental) declare $x,\ldots, z$ as inline variables. Such variables
+ behave like lexically scoped variable (see my()) but with unlimited scope.
+ It is however possible to exit the scope by using \kbd{uninline()}.
+ When used in a GP script, it is recommended to call \kbd{uninline()} before
+ the script's end to avoid inline variables leaking outside the script.
diff --git a/src/functions/programming/input b/src/functions/programming/input
new file mode 100644
index 0000000..67df986
--- /dev/null
+++ b/src/functions/programming/input
@@ -0,0 +1,15 @@
+Function: input
+Class: gp
+Section: programming/specific
+C-Name: input0
+Prototype:
+Help: input(): read an expression from the input file or standard input.
+Doc: reads a string, interpreted as a GP expression,
+ from the input file, usually standard input (i.e.~the keyboard). If a
+ sequence of expressions is given, the result is the result of the last
+ expression of the sequence. When using this instruction, it is useful to
+ prompt for the string by using the \kbd{print1} function. Note that in the
+ present version 2.19 of \kbd{pari.el}, when using \kbd{gp} under GNU Emacs (see
+ \secref{se:emacs}) one \emph{must} prompt for the string, with a string
+ which ends with the same prompt as any of the previous ones (a \kbd{"? "}
+ will do for instance).
diff --git a/src/functions/programming/install b/src/functions/programming/install
new file mode 100644
index 0000000..83525ce
--- /dev/null
+++ b/src/functions/programming/install
@@ -0,0 +1,79 @@
+Function: install
+Section: programming/specific
+C-Name: gpinstall
+Prototype: vrrD"",r,D"",s,
+Help: install(name,code,{gpname},{lib}): load from dynamic library 'lib' the
+ function 'name'. Assign to it the name 'gpname' in this GP session, with
+ prototype 'code'. If 'lib' is omitted, all symbols known to gp
+ (includes the whole 'libpari.so' and possibly others) are available.
+ If 'gpname' is omitted, use 'name'.
+Doc: loads from dynamic library \var{lib} the function \var{name}. Assigns to it
+ the name \var{gpname} in this \kbd{gp} session, with \emph{prototype}
+ \var{code} (see below). If \var{gpname} is omitted, uses \var{name}.
+ If \var{lib} is omitted, all symbols known to \kbd{gp} are available: this
+ includes the whole of \kbd{libpari.so} and possibly others (such as
+ \kbd{libc.so}).
+
+ Most importantly, \kbd{install} gives you access to all non-static functions
+ defined in the PARI library. For instance, the function \kbd{GEN addii(GEN
+ x, GEN y)} adds two PARI integers, and is not directly accessible under
+ \kbd{gp} (it is eventually called by the \kbd{+} operator of course):
+ \bprog
+ ? install("addii", "GG")
+ ? addii(1, 2)
+ %1 = 3
+ @eprog\noindent
+ It also allows to add external functions to the \kbd{gp} interpreter.
+ For instance, it makes the function \tet{system} obsolete:
+ \bprog
+ ? install(system, vs, sys,/*omitted*/)
+ ? sys("ls gp*")
+ gp.c            gp.h            gp_rl.c
+ @eprog\noindent This works because \kbd{system} is part of \kbd{libc.so},
+ which is linked to \kbd{gp}. It is also possible to compile a shared library
+ yourself and provide it to gp in this way: use \kbd{gp2c}, or do it manually
+ (see the \kbd{modules\_build} variable in \kbd{pari.cfg} for hints).
+
+ Re-installing a function will print a warning and update the prototype code
+ if needed. However, it will not reload a symbol from the library, even if the
+ latter has been recompiled.
+
+ \misctitle{Prototype} We only give a simplified description here, covering
+ most functions, but there are many more possibilities. The full documentation
+ is available in \kbd{libpari.dvi}, see
+ \bprog
+   ??prototype
+ @eprog
+
+ \item First character \kbd{i}, \kbd{l}, \kbd{v} : return type int / long /
+ void. (Default: \kbd{GEN})
+
+ \item One letter for each mandatory argument, in the same order as they appear
+ in the argument list: \kbd{G} (\kbd{GEN}), \kbd{\&}
+ (\kbd{GEN*}), \kbd{L} (\kbd{long}), \kbd{s} (\kbd{char *}), \kbd{n}
+ (variable).
+
+  \item \kbd{p} to supply \kbd{realprecision} (usually \kbd{long prec} in the
+  argument list), \kbd{P} to supply \kbd{seriesprecision} (usually \kbd{long
+  precdl}).
+
+  \noindent We also have special constructs for optional arguments and default
+  values:
+
+  \item \kbd{DG} (optional \kbd{GEN}, \kbd{NULL} if omitted),
+
+  \item \kbd{D\&} (optional \kbd{GEN*}, \kbd{NULL} if omitted),
+
+  \item \kbd{Dn} (optional variable, $-1$ if omitted),
+
+ For instance the prototype corresponding to
+ \bprog
+   long issquareall(GEN x, GEN *n = NULL)
+ @eprog\noindent is \kbd{lGD\&}.
+
+ \misctitle{Caution} This function may not work on all systems, especially
+ when \kbd{gp} has been compiled statically. In that case, the first use of an
+ installed function will provoke a Segmentation Fault (this should never
+ happen with a dynamically linked executable). If you intend to use this
+ function, please check first on some harmless example such as the one above
+ that it works properly on your machine.
diff --git a/src/functions/programming/kill b/src/functions/programming/kill
new file mode 100644
index 0000000..9bda287
--- /dev/null
+++ b/src/functions/programming/kill
@@ -0,0 +1,45 @@
+Function: kill
+Section: programming/specific
+C-Name: kill0
+Prototype: vr
+Help: kill(sym): restores the symbol sym to its ``undefined'' status and kill
+ associated help messages.
+Doc: restores the symbol \kbd{sym} to its ``undefined'' status, and deletes any
+ help messages associated to \kbd{sym} using \kbd{addhelp}. Variable names
+ remain known to the interpreter and keep their former priority: you cannot
+ make a variable ``less important" by killing it!
+ \bprog
+ ? z = y = 1; y
+ %1 = 1
+ ? kill(y)
+ ? y            \\ restored to ``undefined'' status
+ %2 = y
+ ? variable()
+ %3 = [x, y, z] \\ but the variable name y is still known, with y > z !
+ @eprog\noindent
+ For the same reason, killing a user function (which is an ordinary
+ variable holding a \typ{CLOSURE}) does not remove its name from the list of
+ variable names.
+
+ If the symbol is associated to a variable --- user functions being an
+ important special case ---, one may use the \idx{quote} operator
+ \kbd{a = 'a} to reset variables to their starting values. However, this
+ will not delete a help message associated to \kbd{a}, and is also slightly
+ slower than \kbd{kill(a)}.
+ \bprog
+ ? x = 1; addhelp(x, "foo"); x
+ %1 = 1
+ ? x = 'x; x   \\ same as 'kill', except we don't delete help.
+ %2 = x
+ ? ?x
+ foo
+ @eprog\noindent
+ On the other hand, \kbd{kill} is the only way to remove aliases and installed
+ functions.
+ \bprog
+ ? alias(fun, sin);
+ ? kill(fun);
+
+ ? install(addii, GG);
+ ? kill(addii);
+ @eprog
diff --git a/src/functions/programming/local b/src/functions/programming/local
new file mode 100644
index 0000000..2c1bbaa
--- /dev/null
+++ b/src/functions/programming/local
@@ -0,0 +1,3 @@
+Function: local
+Section: programming/specific
+Help: local(x,...,z): declare x,...,z as (dynamically scoped) local variables.
diff --git a/src/functions/programming/my b/src/functions/programming/my
new file mode 100644
index 0000000..cb51a7a
--- /dev/null
+++ b/src/functions/programming/my
@@ -0,0 +1,3 @@
+Function: my
+Section: programming/specific
+Help: my(x,...,z): declare x,...,z as lexically-scoped local variables.
diff --git a/src/functions/programming/next b/src/functions/programming/next
new file mode 100644
index 0000000..6fb58fe
--- /dev/null
+++ b/src/functions/programming/next
@@ -0,0 +1,11 @@
+Function: next
+Section: programming/control
+C-Name: next0
+Prototype: D1,L,
+Help: next({n=1}): interrupt execution of current instruction sequence, and
+ start another iteration from the n-th innermost enclosing loops.
+Doc: interrupts execution of current $seq$,
+ resume the next iteration of the innermost enclosing loop, within the
+ current function call (or top level loop). If $n$ is specified, resume at
+ the $n$-th enclosing loop. If $n$ is bigger than the number of enclosing
+ loops, all enclosing loops are exited.
diff --git a/src/functions/programming/parapply b/src/functions/programming/parapply
new file mode 100644
index 0000000..3494d4b
--- /dev/null
+++ b/src/functions/programming/parapply
@@ -0,0 +1,25 @@
+Function: parapply
+Section: programming/parallel
+C-Name: parapply
+Prototype: GG
+Help: parapply(f, x): parallel evaluation of f on the elements of x.
+Doc: parallel evaluation of \kbd{f} on the elements of \kbd{x}.
+ The function \kbd{f} must not access global variables or variables
+ declared with local(), and must be free of side effects.
+ \bprog
+ parapply(factor,[2^256 + 1, 2^193 - 1])
+ @eprog
+ factors $2^{256} + 1$ and $2^{193} - 1$ in parallel.
+ \bprog
+ {
+   my(E = ellinit([1,3]), V = vector(12,i,randomprime(2^200)));
+   parapply(p->ellcard(E,p), V)
+ }
+ @eprog
+ computes the order of $E(\F_p)$ for $12$ random primes of $200$ bits.
+
+Function: _parapply_worker
+Section: programming/internals
+C-Name: parapply_worker
+Prototype: GG
+Help: _parapply_worker(d,C): evaluate the closure C on d.
diff --git a/src/functions/programming/pareval b/src/functions/programming/pareval
new file mode 100644
index 0000000..410d1d2
--- /dev/null
+++ b/src/functions/programming/pareval
@@ -0,0 +1,16 @@
+Function: pareval
+Section: programming/parallel
+C-Name: pareval
+Prototype: G
+Help: pareval(x): parallel evaluation of the elements of the vector of
+ closures x.
+Doc: parallel evaluation of the elements of \kbd{x}, where \kbd{x} is a
+ vector of closures. The closures must be of arity $0$, must not access
+ global variables or variables declared with \kbd{local} and must be
+ free of side effects.
+
+Function: _pareval_worker
+Section: programming/internals
+C-Name: pareval_worker
+Prototype: G
+Help: _pareval_worker(C): evaluate the closure C.
diff --git a/src/functions/programming/parfor b/src/functions/programming/parfor
new file mode 100644
index 0000000..856ebb5
--- /dev/null
+++ b/src/functions/programming/parfor
@@ -0,0 +1,24 @@
+Function: parfor
+Section: programming/parallel
+C-Name: parfor
+Prototype: vV=GDGJDVDI
+Help: parfor(i=a,{b},expr1,{j},{expr2}): evaluates the sequence expr2
+ (dependent on i and j) for i between a and b, in random order, computed
+ in parallel. Substitute for j the value of expr1 (dependent on i).
+ If b is omitted, the loop will not stop.
+Doc: evaluates the sequence \kbd{expr2} (dependent on $i$ and $j$) for $i$
+ between $a$ and $b$, in random order, computed in parallel; in this sequence
+ \kbd{expr2}, substitute the variable $j$ by the value of \kbd{expr1}
+ (dependent on $i$). If $b$ is omitted, the loop will not stop.
+
+ It is allowed for \kbd{expr2} to exit the loop using
+ \kbd{break}/\kbd{next}/\kbd{return}; however in that case, \kbd{expr2} will
+ still be evaluated for all remaining value of $i$ less than the current one,
+ unless a subsequent \kbd{break}/\kbd{next}/\kbd{return} happens.
+ %\syn{NO}
+
+Function: _parfor_worker
+Section: programming/internals
+C-Name: parfor_worker
+Prototype: GG
+Help: _parfor_worker(i,C): evaluate the closure C on i and return [i,C(i)]
diff --git a/src/functions/programming/parforprime b/src/functions/programming/parforprime
new file mode 100644
index 0000000..82d2e61
--- /dev/null
+++ b/src/functions/programming/parforprime
@@ -0,0 +1,18 @@
+Function: parforprime
+Section: programming/parallel
+C-Name: parforprime
+Prototype: vV=GDGJDVDI
+Help: parforprime(p=a,{b},expr1,{j},{expr2}): evaluates the sequence expr2
+ (dependent on p and j) for p prime between a and b, in random order,
+ computed in parallel. Substitute for j the value of expr1 (dependent on i).
+ If b is omitted, the loop will not stop.
+Doc: evaluates the sequence \kbd{expr2} (dependent on $p$ and $j$) for $p$
+ prime between $a$ and $b$, in random order, computed in parallel. Substitute
+ for $j$ the value of \kbd{expr1} (dependent on $p$).
+ If $b$ is omitted, the loop will not stop.
+
+ It is allowed fo \kbd{expr2} to exit the loop using
+ \kbd{break}/\kbd{next}/\kbd{return}, however in that case, \kbd{expr2} will
+ still be evaluated for all remaining value of $p$ less than the current one,
+ unless a subsequent \kbd{break}/\kbd{next}/\kbd{return} happens.
+ %\syn{NO}
diff --git a/src/functions/programming/parselect b/src/functions/programming/parselect
new file mode 100644
index 0000000..65e7602
--- /dev/null
+++ b/src/functions/programming/parselect
@@ -0,0 +1,11 @@
+Function: parselect
+Section: programming/parallel
+C-Name: parselect
+Prototype: GGD0,L,
+Help: parselect(f, A, {flag = 0}): (parallel select) selects elements of A
+ according to the selection function f which is tested in parallel. If flag
+ is 1, return the indices of those elements (indirect selection)
+Doc: selects elements of $A$ according to the selection function $f$, done in
+ parallel.  If \fl is $1$, return the indices of those elements (indirect
+ selection) The function \kbd{f} must not access global variables or
+ variables declared with local(), and must be free of side effects.
diff --git a/src/functions/programming/parsum b/src/functions/programming/parsum
new file mode 100644
index 0000000..f576269
--- /dev/null
+++ b/src/functions/programming/parsum
@@ -0,0 +1,17 @@
+Function: parsum
+Section: programming/parallel
+C-Name: parsum
+Prototype: V=GGJDG
+Description:
+ (gen,gen,closure,?gen):gen parsum($1, $2, $3, $4)
+Help: parsum(i=a,b,expr,{x}): x plus the sum (X goes from a to b) of
+ expression expr, evaluated in parallel (in random order)
+Doc: sum of expression \var{expr}, initialized at $x$, the formal parameter
+ going from $a$ to $b$, evaluated in parallel in random order.
+ The expression \kbd{expr} must not access global variables or
+ variables declared with \kbd{local()}, and must be free of side effects.
+ \bprog
+ parsum(i=1,1000,ispseudoprime(2^prime(i)-1))
+ @eprog
+ returns the numbers of prime numbers among the first $1000$ Mersenne numbers.
+ %\syn{NO}
diff --git a/src/functions/programming/parvector b/src/functions/programming/parvector
new file mode 100644
index 0000000..fbf0ce7
--- /dev/null
+++ b/src/functions/programming/parvector
@@ -0,0 +1,22 @@
+Function: parvector
+Section: programming/parallel
+C-Name: parvector
+Prototype: LVJ
+Description:
+  (small,,closure):vec    parvector($1, $3)
+Help: parvector(N,i,expr): as vector(N,i,expr) but the evaluations of expr are
+ done in parallel.
+Doc: As \kbd{vector(N,i,expr)} but the evaluations of \kbd{expr} are done in
+ parallel. The expression \kbd{expr} must not access global variables or
+ variables declared with \kbd{local()}, and must be free of side effects.
+ \bprog
+ parvector(10,i,quadclassunit(2^(100+i)+1).no)
+ @eprog\noindent
+ computes the class numbers in parallel.
+ %\syn{NO}
+
+Function: _parvector_worker
+Section: programming/internals
+C-Name: parvector_worker
+Prototype: GG
+Help: _parvector_worker(i,C): evaluate the closure C on i.
diff --git a/src/functions/programming/print b/src/functions/programming/print
new file mode 100644
index 0000000..be17b93
--- /dev/null
+++ b/src/functions/programming/print
@@ -0,0 +1,10 @@
+Function: print
+Section: programming/specific
+C-Name: print
+Prototype: vs*
+Help: print({str}*): outputs its string arguments (in raw format) ending with
+ a newline.
+Description:
+ (?gen,...):void  pari_printf("${2 format_string}\n"${2 format_args})
+Doc: outputs its (string) arguments in raw format, ending with a newline.
+ %\syn{NO}
diff --git a/src/functions/programming/print1 b/src/functions/programming/print1
new file mode 100644
index 0000000..0412780
--- /dev/null
+++ b/src/functions/programming/print1
@@ -0,0 +1,12 @@
+Function: print1
+Section: programming/specific
+C-Name: print1
+Prototype: vs*
+Help: print1({str}*): outputs its string arguments (in raw format) without
+ ending with newline.
+Description:
+ (?gen,...):void  pari_printf("${2 format_string}"${2 format_args})
+Doc: outputs its (string) arguments in raw
+ format, without ending with a newline. Note that you can still embed newlines
+ within your strings, using the \b{n} notation~!
+ %\syn{NO}
diff --git a/src/functions/programming/printf b/src/functions/programming/printf
new file mode 100644
index 0000000..ee39fb4
--- /dev/null
+++ b/src/functions/programming/printf
@@ -0,0 +1,185 @@
+Function: printf
+Section: programming/specific
+C-Name: printf0
+Prototype: vss*
+Help: printf(fmt,{x}*): prints its arguments according to the format fmt.
+Doc: This function is based on the C library command of the same name.
+ It prints its arguments according to the format \var{fmt}, which specifies how
+ subsequent arguments are converted for output. The format is a
+ character string composed of zero or more directives:
+
+ \item ordinary characters (not \kbd{\%}), printed unchanged,
+
+ \item conversions specifications (\kbd{\%} followed by some characters)
+ which fetch one argument from the list and prints it according to the
+ specification.
+
+ More precisely, a conversion specification consists in a \kbd{\%}, one or more
+ optional flags (among \kbd{\#}, \kbd{0}, \kbd{-}, \kbd{+}, ` '), an optional
+ decimal digit string specifying a minimal field width, an optional precision
+ in the form of a period (`\kbd{.}') followed by a decimal digit string, and
+ the conversion specifier (among \kbd{d},\kbd{i}, \kbd{o}, \kbd{u},
+ \kbd{x},\kbd{X}, \kbd{p}, \kbd{e},\kbd{E}, \kbd{f}, \kbd{g},\kbd{G}, \kbd{s}).
+
+ \misctitle{The flag characters} The character \kbd{\%} is followed by zero or
+ more of the following flags:
+
+ \item \kbd{\#}: The value is converted to an ``alternate form''. For
+ \kbd{o} conversion (octal), a \kbd{0} is prefixed to the string. For \kbd{x}
+ and \kbd{X} conversions (hexa), respectively \kbd{0x} and \kbd{0X} are
+ prepended. For other conversions, the flag is ignored.
+
+ \item \kbd{0}: The value should be zero padded. For
+ \kbd{d},
+ \kbd{i},
+ \kbd{o},
+ \kbd{u},
+ \kbd{x},
+ \kbd{X}
+ \kbd{e},
+ \kbd{E},
+ \kbd{f},
+ \kbd{F},
+ \kbd{g}, and
+ \kbd{G} conversions, the value is padded on the left with zeros rather than
+ blanks. (If the \kbd{0} and \kbd{-} flags both appear, the \kbd{0} flag is
+ ignored.)
+
+ \item \kbd{-}: The value is left adjusted on the field boundary. (The
+ default is right justification.) The value is padded on the right with
+ blanks, rather than on the left with blanks or zeros. A \kbd{-} overrides a
+ \kbd{0} if both are given.
+
+ \item \kbd{` '} (a space): A blank is left before a positive number
+ produced by a signed conversion.
+
+ \item \kbd{+}: A sign (+ or -) is placed before a number produced by a
+ signed conversion. A \kbd{+} overrides a space if both are used.
+
+ \misctitle{The field width} An optional decimal digit string (whose first
+ digit is non-zero) specifying a \emph{minimum} field width. If the value has
+ fewer characters than the field width, it is padded with spaces on the left
+ (or right, if the left-adjustment flag has been given). In no case does a
+ small field width cause truncation of a field; if the value is wider than
+ the field width, the field is expanded to contain the conversion result.
+ Instead of a decimal digit string, one may write \kbd{*} to specify that the
+ field width is given in the next argument.
+
+ \misctitle{The precision} An optional precision in the form of a period
+ (`\kbd{.}') followed by a decimal digit string. This gives
+ the number of digits to appear after the radix character for \kbd{e},
+ \kbd{E}, \kbd{f}, and \kbd{F} conversions, the maximum number of significant
+ digits for \kbd{g} and \kbd{G} conversions, and the maximum number of
+ characters to be printed from an \kbd{s} conversion.
+ Instead of a decimal digit string, one may write \kbd{*} to specify that the
+ field width is given in the next argument.
+
+ \misctitle{The length modifier} This is ignored under \kbd{gp}, but
+ necessary for \kbd{libpari} programming. Description given here for
+ completeness:
+
+ \item \kbd{l}: argument is a \kbd{long} integer.
+
+ \item \kbd{P}: argument is a \kbd{GEN}.
+
+ \misctitle{The conversion specifier} A character that specifies the type of
+ conversion to be applied.
+
+ \item \kbd{d}, \kbd{i}: A signed integer.
+
+ \item \kbd{o}, \kbd{u}, \kbd{x}, \kbd{X}: An unsigned integer, converted
+ to unsigned octal (\kbd{o}), decimal (\kbd{u}) or hexadecimal (\kbd{x} or
+ \kbd{X}) notation. The letters \kbd{abcdef} are used for \kbd{x}
+ conversions;  the letters \kbd{ABCDEF} are used for \kbd{X} conversions.
+
+ \item \kbd{e}, \kbd{E}: The (real) argument is converted in the style
+ \kbd{[ -]d.ddd e[ -]dd}, where there is one digit before the decimal point,
+ and the number of digits after it is equal to the precision; if the
+ precision is missing, use the current \kbd{realprecision} for the total
+ number of printed digits. If the precision is explicitly 0, no decimal-point
+ character appears. An \kbd{E} conversion uses the letter \kbd{E} rather
+ than \kbd{e} to introduce the exponent.
+
+ \item \kbd{f}, \kbd{F}: The (real) argument is converted in the style
+ \kbd{[ -]ddd.ddd}, where the number of digits after the decimal point
+ is equal to the precision; if the precision is missing, use the current
+ \kbd{realprecision} for the total number of printed digits. If the precision
+ is explicitly 0, no decimal-point character appears. If a decimal point
+ appears, at least one digit appears before it.
+
+ \item \kbd{g}, \kbd{G}: The (real) argument is converted in style
+ \kbd{e} or \kbd{f} (or \kbd{E} or \kbd{F} for \kbd{G} conversions)
+ \kbd{[ -]ddd.ddd}, where the total number of digits printed
+ is equal to the precision; if the precision is missing, use the current
+ \kbd{realprecision}. If the precision is explicitly 0, it is treated as 1.
+ Style \kbd{e} is used when
+ the decimal exponent is $< -4$, to print \kbd{0.}, or when the integer
+ part cannot be decided given the known significant digits, and the \kbd{f}
+ format otherwise.
+
+ \item \kbd{c}: The integer argument is converted to an unsigned char, and the
+ resulting character is written.
+
+ \item \kbd{s}: Convert to a character string. If a precision is given, no
+ more than the specified number of characters are written.
+
+ \item \kbd{p}: Print the address of the argument in hexadecimal (as if by
+ \kbd{\%\#x}).
+
+ \item \kbd{\%}: A \kbd{\%} is written. No argument is converted. The complete
+ conversion specification is \kbd{\%\%}.
+
+ \noindent Examples:
+
+ \bprog
+ ? printf("floor: %d, field width 3: %3d, with sign: %+3d\n", Pi, 1, 2);
+ floor: 3, field width 3:   1, with sign:  +2
+
+ ? printf("%.5g %.5g %.5g\n",123,123/456,123456789);
+ 123.00 0.26974 1.2346 e8
+
+ ? printf("%-2.5s:%2.5s:%2.5s\n", "P", "PARI", "PARIGP");
+ P :PARI:PARIG
+
+ \\ min field width and precision given by arguments
+ ? x = 23; y=-1/x; printf("x=%+06.2f y=%+0*.*f\n", x, 6, 2, y);
+ x=+23.00 y=-00.04
+
+ \\ minimum fields width 5, pad left with zeroes
+ ? for (i = 2, 5, printf("%05d\n", 10^i))
+ 00100
+ 01000
+ 10000
+ 100000  \\@com don't truncate fields whose length is larger than the minimum width
+ ? printf("%.2f  |%06.2f|", Pi,Pi)
+ 3.14  |  3.14|
+ @eprog\noindent All numerical conversions apply recursively to the entries
+ of vectors and matrices:
+ \bprog
+ ? printf("%4d", [1,2,3]);
+ [   1,   2,   3]
+ ? printf("%5.2f", mathilbert(3));
+ [ 1.00  0.50  0.33]
+
+ [ 0.50  0.33  0.25]
+
+ [ 0.33  0.25  0.20]
+ @eprog
+ \misctitle{Technical note} Our implementation of \tet{printf}
+ deviates from the C89 and C99 standards in a few places:
+
+ \item whenever a precision is missing, the current \kbd{realprecision} is
+ used to determine the number of printed digits (C89: use 6 decimals after
+ the radix character).
+
+ \item in conversion style \kbd{e}, we do not impose that the
+ exponent has at least two digits; we never write a \kbd{+} sign in the
+ exponent; 0 is printed in a special way, always as \kbd{0.E\var{exp}}.
+
+ \item in conversion style \kbd{f}, we switch to style \kbd{e} if the
+ exponent is greater or equal to the precision.
+
+ \item in conversion \kbd{g} and \kbd{G}, we do not remove trailing zeros
+  from the fractional part of the result; nor a trailing decimal point;
+  0 is printed in a special way, always as \kbd{0.E\var{exp}}.
+ %\syn{NO}
diff --git a/src/functions/programming/printsep b/src/functions/programming/printsep
new file mode 100644
index 0000000..7976ed0
--- /dev/null
+++ b/src/functions/programming/printsep
@@ -0,0 +1,13 @@
+Function: printsep
+Section: programming/specific
+C-Name: printsep
+Prototype: vss*
+Help: printsep(sep,{str}*): outputs its string arguments (in raw format),
+ separated by 'sep', ending with a newline.
+Doc: outputs its (string) arguments in raw format, ending with a newline.
+ Successive entries are separated by \var{sep}:
+ \bprog
+ ? printsep(":", 1,2,3,4)
+ 1:2:3:4
+ @eprog
+ %\syn{NO}
diff --git a/src/functions/programming/printsep1 b/src/functions/programming/printsep1
new file mode 100644
index 0000000..2a4698e
--- /dev/null
+++ b/src/functions/programming/printsep1
@@ -0,0 +1,13 @@
+Function: printsep1
+Section: programming/specific
+C-Name: printsep1
+Prototype: vss*
+Help: printsep(sep,{str}*): outputs its string arguments (in raw format),
+ separated by 'sep', without ending with a newline.
+Doc: outputs its (string) arguments in raw format, without ending with a
+ newline.  Successive entries are separated by \var{sep}:
+ \bprog
+ ? printsep1(":", 1,2,3,4);print("|")
+ 1:2:3:4
+ @eprog
+ %\syn{NO}
diff --git a/src/functions/programming/printtex b/src/functions/programming/printtex
new file mode 100644
index 0000000..1dfcc79
--- /dev/null
+++ b/src/functions/programming/printtex
@@ -0,0 +1,20 @@
+Function: printtex
+Section: programming/specific
+C-Name: printtex
+Prototype: vs*
+Help: printtex({str}*): outputs its string arguments in TeX format.
+Doc: outputs its (string) arguments in \TeX\ format. This output can then be
+ used in a \TeX\ manuscript.
+ The printing is done on the standard output. If you want to print it to a
+ file you should use \kbd{writetex} (see there).
+
+ Another possibility is to enable the \tet{log} default
+ (see~\secref{se:defaults}).
+ You could for instance do:\sidx{logfile}
+ %
+ \bprog
+ default(logfile, "new.tex");
+ default(log, 1);
+ printtex(result);
+ @eprog
+ %\syn{NO}
diff --git a/src/functions/programming/quit b/src/functions/programming/quit
new file mode 100644
index 0000000..12e7f06
--- /dev/null
+++ b/src/functions/programming/quit
@@ -0,0 +1,11 @@
+Function: quit
+Class: gp
+Section: programming/specific
+C-Name: gp_quit
+Prototype: vD0,L,
+Help: quit({status = 0}): quit, return to the system with exit status
+ 'status'.
+Doc: exits \kbd{gp} and return to the system with exit status
+ \kbd{status}, a small integer. A non-zero exit status normally indicates
+ abnormal termination. (Note: the system actually sees only
+ \kbd{status} mod $256$, see your man pages for \kbd{exit(3)} or \kbd{wait(2)}).
diff --git a/src/functions/programming/read b/src/functions/programming/read
new file mode 100644
index 0000000..63ef580
--- /dev/null
+++ b/src/functions/programming/read
@@ -0,0 +1,21 @@
+Function: read
+Class: gp
+Section: programming/specific
+C-Name: read0
+Prototype: D"",s,
+Help: read({filename}): read from the input file filename. If filename is
+ omitted, reread last input file, be it from read() or \r.
+Description:
+ (str):gen      gp_read_file($1)
+Doc: reads in the file
+ \var{filename} (subject to string expansion). If \var{filename} is
+ omitted, re-reads the last file that was fed into \kbd{gp}. The return
+ value is the result of the last expression evaluated.
+
+ If a GP \tet{binary file} is read using this command (see
+ \secref{se:writebin}), the file is loaded and the last object in the file
+ is returned.
+
+ In case the file you read in contains an \tet{allocatemem} statement (to be
+ generally avoided), you should leave \kbd{read} instructions by themselves,
+ and not part of larger instruction sequences.
diff --git a/src/functions/programming/readstr b/src/functions/programming/readstr
new file mode 100644
index 0000000..c96d393
--- /dev/null
+++ b/src/functions/programming/readstr
@@ -0,0 +1,11 @@
+Function: readstr
+Class: gp
+Section: programming/specific
+C-Name: readstr
+Prototype: D"",s,
+Help: readstr({filename}): returns the vector of GP strings containing
+ the lines in filename.
+
+Doc: Reads in the file \var{filename} and return a vector of GP strings,
+ each component containing one line from the file. If \var{filename} is
+ omitted, re-reads the last file that was fed into \kbd{gp}.
diff --git a/src/functions/programming/readvec b/src/functions/programming/readvec
new file mode 100644
index 0000000..8b938e5
--- /dev/null
+++ b/src/functions/programming/readvec
@@ -0,0 +1,33 @@
+Function: readvec
+Section: programming/specific
+C-Name: gp_readvec_file
+Prototype: D"",s,
+Help: readvec({filename}): create a vector whose components are the evaluation
+ of all the expressions found in the input file filename.
+Description:
+ (str):gen      gp_readvec_file($1)
+Doc: reads in the file
+ \var{filename} (subject to string expansion). If \var{filename} is
+ omitted, re-reads the last file that was fed into \kbd{gp}. The return
+ value is a vector whose components are the evaluation of all sequences
+ of instructions contained in the file. For instance, if \var{file} contains
+ \bprog
+ 1
+ 2
+ 3
+ @eprog\noindent
+ then we will get:
+ \bprog
+ ? \r a
+ %1 = 1
+ %2 = 2
+ %3 = 3
+ ? read(a)
+ %4 = 3
+ ? readvec(a)
+ %5 = [1, 2, 3]
+ @eprog
+ In general a sequence is just a single line, but as usual braces and
+ \kbd{\bs} may be used to enter multiline sequences.
+Variant: The underlying library function
+ \fun{GEN}{gp_readvec_stream}{FILE *f} is usually more flexible.
diff --git a/src/functions/programming/return b/src/functions/programming/return
new file mode 100644
index 0000000..a081bc3
--- /dev/null
+++ b/src/functions/programming/return
@@ -0,0 +1,8 @@
+Function: return
+Section: programming/control
+C-Name: return0
+Prototype: DG
+Help: return({x=0}): return from current subroutine with result x.
+Doc: returns from current subroutine, with
+ result $x$. If $x$ is omitted, return the \kbd{(void)} value (return no
+ result, like \kbd{print}).
diff --git a/src/functions/programming/select b/src/functions/programming/select
new file mode 100644
index 0000000..4ca6e00
--- /dev/null
+++ b/src/functions/programming/select
@@ -0,0 +1,73 @@
+Function: select
+Section: programming/specific
+C-Name: select0
+Prototype: GGD0,L,
+Help: select(f, A, {flag = 0}): selects elements of A according to the selection
+ function f. If flag is 1, return the indices of those elements (indirect
+ selection)
+Wrapper: (bG)
+Description:
+  (gen,gen):gen    genselect(${1 cookie}, ${1 wrapper}, $2)
+  (gen,gen,0):gen  genselect(${1 cookie}, ${1 wrapper}, $2)
+  (gen,gen,1):gen  genindexselect(${1 cookie}, ${1 wrapper}, $2)
+Doc: We first describe the default behavior, when $\fl$ is 0 or omitted.
+ Given a vector or list \kbd{A} and a \typ{CLOSURE} \kbd{f}, \kbd{select}
+ returns the elements $x$ of \kbd{A} such that $f(x)$ is non-zero. In other
+ words, \kbd{f} is seen as a selection function returning a boolean value.
+ \bprog
+ ? select(x->isprime(x), vector(50,i,i^2+1))
+ %1 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+ ? select(x->(x<100), %)
+ %2 = [2, 5, 17, 37]
+ @eprog\noindent returns the primes of the form $i^2+1$ for some $i\leq 50$,
+ then the elements less than 100 in the preceding result. The \kbd{select}
+ function also applies to a matrix \kbd{A}, seen as a vector of columns, i.e. it
+ selects columns instead of entries, and returns the matrix whose columns are
+ the selected ones.
+
+ \misctitle{Remark} For $v$ a \typ{VEC}, \typ{COL}, \typ{LIST} or \typ{MAT},
+ the alternative set-notations
+ \bprog
+ [g(x) | x <- v, f(x)]
+ [x | x <- v, f(x)]
+ [g(x) | x <- v]
+ @eprog\noindent
+ are available as shortcuts for
+ \bprog
+ apply(g, select(f, Vec(v)))
+ select(f, Vec(v))
+ apply(g, Vec(v))
+ @eprog\noindent respectively:
+ \bprog
+ ? [ x | x <- vector(50,i,i^2+1), isprime(x) ]
+ %1 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+ @eprog
+
+ \noindent If $\fl = 1$, this function returns instead the \emph{indices} of
+ the selected elements, and not the elements themselves (indirect selection):
+ \bprog
+ ? V = vector(50,i,i^2+1);
+ ? select(x->isprime(x), V, 1)
+ %2 = Vecsmall([1, 2, 4, 6, 10, 14, 16, 20, 24, 26, 36, 40])
+ ? vecextract(V, %)
+ %3 = [2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+ @eprog\noindent
+ The following function lists the elements in $(\Z/N\Z)^*$:
+ \bprog
+ ? invertibles(N) = select(x->gcd(x,N) == 1, [1..N])
+ @eprog
+
+ \noindent Finally
+ \bprog
+ ? select(x->x, M)
+ @eprog\noindent selects the non-0 entries in \kbd{M}. If the latter is a
+ \typ{MAT}, we extract the matrix of non-0 columns. Note that \emph{removing}
+ entries instead of selecting them just involves replacing the selection
+ function \kbd{f} with its negation:
+ \bprog
+ ? select(x->!isprime(x), vector(50,i,i^2+1))
+ @eprog
+
+ \synt{genselect}{void *E, long (*fun)(void*,GEN), GEN a}. Also available
+ is \fun{GEN}{genindexselect}{void *E, long (*fun)(void*, GEN), GEN a},
+ corresponding to $\fl = 1$.
diff --git a/src/functions/programming/setrand b/src/functions/programming/setrand
new file mode 100644
index 0000000..a525ad8
--- /dev/null
+++ b/src/functions/programming/setrand
@@ -0,0 +1,10 @@
+Function: setrand
+Section: programming/specific
+C-Name: setrand
+Prototype: vG
+Help: setrand(n): reset the seed of the random number generator to n.
+Doc: reseeds the random number generator using the seed $n$. No value is
+ returned. The seed is either a technical array output by \kbd{getrand}, or a
+ small positive integer, used to generate deterministically a suitable state
+ array. For instance, running a randomized computation starting by
+ \kbd{setrand(1)} twice will generate the exact same output.
diff --git a/src/functions/programming/system b/src/functions/programming/system
new file mode 100644
index 0000000..a80e820
--- /dev/null
+++ b/src/functions/programming/system
@@ -0,0 +1,12 @@
+Function: system
+Class: gp
+Section: programming/specific
+C-Name: system0
+Prototype: vs
+Description:
+ (str):void   system($1)
+Help: system(str): str being a string, execute the system command str.
+Doc: \var{str} is a string representing a system command. This command is
+ executed, its output written to the standard output (this won't get into your
+ logfile), and control returns to the PARI system. This simply calls the C
+ \kbd{system} command.
diff --git a/src/functions/programming/trap b/src/functions/programming/trap
new file mode 100644
index 0000000..3a87694
--- /dev/null
+++ b/src/functions/programming/trap
@@ -0,0 +1,66 @@
+Function: trap
+Section: programming/specific
+C-Name: trap0
+Prototype: DrDEDE
+Help: trap({e}, {rec}, seq): try to execute seq, trapping runtime error e (all
+ of them if e omitted); sequence rec is executed if the error occurs and
+ is the result of the command. THIS FUNCTION IS OBSOLETE: use "IFERR"
+Wrapper: (,_,_)
+Description:
+ (?str,?closure,?closure):gen trap0($1, $2, $3)
+Doc: THIS FUNCTION IS OBSOLETE: use \tet{iferr}, which has a nicer and much
+ more powerful interface. For compatibility's sake we now describe the
+ \emph{obsolete} function \tet{trap}.
+
+ This function tries to
+ evaluate \var{seq}, trapping runtime error $e$, that is effectively preventing
+ it from aborting computations in the usual way; the recovery sequence
+ \var{rec} is executed if the error occurs and the evaluation of \var{rec}
+ becomes the result of the command. If $e$ is omitted, all exceptions are
+ trapped. See \secref{se:errorrec} for an introduction to error recovery
+ under \kbd{gp}.
+
+ \bprog
+ ? \\@com trap division by 0
+ ? inv(x) = trap (e_INV, INFINITY, 1/x)
+ ? inv(2)
+ %1 = 1/2
+ ? inv(0)
+ %2 = INFINITY
+ @eprog\noindent
+ Note that \var{seq} is effectively evaluated up to the point that produced
+ the error, and the recovery sequence is evaluated starting from that same
+ context, it does not "undo" whatever happened in the other branch (restore
+ the evaluation context):
+ \bprog
+ ? x = 1; trap (, /* recover: */ x, /* try: */ x = 0; 1/x)
+ %1 = 0
+ @eprog
+
+ \misctitle{Note} The interface is currently not adequate for trapping
+ individual exceptions. In the current version \vers, the following keywords
+ are recognized, but the name list will be expanded and changed in the
+ future (all library mode errors can be trapped: it's a matter of defining
+ the keywords to \kbd{gp}):
+
+ \kbd{e\_ALARM}: alarm time-out
+
+ \kbd{e\_ARCH}: not available on this architecture or operating system
+
+ \kbd{e\_STACK}: the PARI stack overflows
+
+ \kbd{e\_INV}: impossible inverse
+
+ \kbd{e\_IMPL}: not yet implemented
+
+ \kbd{e\_OVERFLOW}: all forms of arithmetic overflow, including length
+ or exponent overflow (when a larger value is supplied than the
+ implementation can handle).
+
+ \kbd{e\_SYNTAX}: syntax error
+
+ \kbd{e\_MISC}: miscellaneous error
+
+ \kbd{e\_TYPE}: wrong type
+
+ \kbd{e\_USER}: user error (from the \kbd{error} function)
diff --git a/src/functions/programming/type b/src/functions/programming/type
new file mode 100644
index 0000000..e488ca6
--- /dev/null
+++ b/src/functions/programming/type
@@ -0,0 +1,13 @@
+Function: type
+Section: programming/specific
+C-Name: type0
+Prototype: G
+Help: type(x): return the type of the GEN x.
+Description:
+ (gen):typ              typ($1)
+Doc: this is useful only under \kbd{gp}. Returns the internal type name of
+ the PARI object $x$ as a  string. Check out existing type names with the
+ metacommand \b{t}. For example \kbd{type(1)} will return "\typ{INT}".
+Variant: The macro \kbd{typ} is usually simpler to use since it returns a
+ \kbd{long} that can easily be matched with the symbols \typ{*}. The name
+ \kbd{type} was avoided since it is a reserved identifier for some compilers.
diff --git a/src/functions/programming/uninline b/src/functions/programming/uninline
new file mode 100644
index 0000000..a6a05db
--- /dev/null
+++ b/src/functions/programming/uninline
@@ -0,0 +1,4 @@
+Function: uninline
+Section: programming/specific
+Help: uninline(): forget all inline variables [EXPERIMENTAL]
+Doc: (Experimental) Exit the scope of all current \kbd{inline} variables.
diff --git a/src/functions/programming/until b/src/functions/programming/until
new file mode 100644
index 0000000..85205b9
--- /dev/null
+++ b/src/functions/programming/until
@@ -0,0 +1,9 @@
+Function: until
+Section: programming/control
+C-Name: untilpari
+Prototype: vEI
+Help: until(a,seq): evaluate the expression sequence seq until a is nonzero.
+Doc: evaluates \var{seq} until $a$ is not
+ equal to 0 (i.e.~until $a$ is true). If $a$ is initially not equal to 0,
+ \var{seq} is evaluated once (more generally, the condition on $a$ is tested
+ \emph{after} execution of the \var{seq}, not before as in \kbd{while}).
diff --git a/src/functions/programming/version b/src/functions/programming/version
new file mode 100644
index 0000000..23d6a45
--- /dev/null
+++ b/src/functions/programming/version
@@ -0,0 +1,49 @@
+Function: version
+Section: programming/specific
+C-Name: pari_version
+Prototype:
+Help: version(): returns the PARI version as [major,minor,patch] or [major,minor,patch,VCSversion].
+Doc: returns the current version number as a \typ{VEC} with three integer
+ components (major version number, minor version number and patchlevel);
+ if your sources were obtained through our version control system, this will
+ be followed by further more precise arguments, including
+ e.g.~a~\kbd{git} \emph{commit hash}.
+
+ This function is present in all versions of PARI following releases 2.3.4
+ (stable) and 2.4.3 (testing).
+
+ Unless you are working with multiple development versions, you probably only
+ care about the 3 first numeric components. In any case, the \kbd{lex} function
+ offers a clever way to check against a particular version number, since it will
+ compare each successive vector entry, numerically or as strings, and will not
+ mind if the vectors it compares have different lengths:
+ \bprog
+    if (lex(version(), [2,3,5]) >= 0,
+      \\ code to be executed if we are running 2.3.5 or more recent.
+    ,
+      \\ compatibility code
+    );
+ @eprog\noindent On a number of different machines, \kbd{version()} could return either of
+ \bprog
+  %1 = [2, 3, 4]    \\ released version, stable branch
+  %1 = [2, 4, 3]    \\ released version, testing branch
+  %1 = [2, 6, 1, 15174, ""505ab9b"] \\ development
+ @eprog
+
+ In particular, if you are only working with released versions, the first
+ line of the gp introductory message can be emulated by
+ \bprog
+    [M,m,p] = version();
+    printf("GP/PARI CALCULATOR Version %s.%s.%s", M,m,p);
+  @eprog\noindent If you \emph{are} working with many development versions of
+  PARI/GP, the 4th and/or 5th components can be profitably included in the
+  name of your logfiles, for instance.
+
+  \misctitle{Technical note} For development versions obtained via \kbd{git},
+  the 4th and 5th components are liable to change eventually, but we document
+  their current meaning for completeness. The 4th component counts the number
+  of reachable commits in the branch (analogous to \kbd{svn}'s revision
+  number), and the 5th is the \kbd{git} commit hash. In particular, \kbd{lex}
+  comparison still orders correctly development versions with respect to each
+  others or to released versions (provided we stay within a given branch,
+  e.g. \kbd{master})!
diff --git a/src/functions/programming/warning b/src/functions/programming/warning
new file mode 100644
index 0000000..3fc3be8
--- /dev/null
+++ b/src/functions/programming/warning
@@ -0,0 +1,16 @@
+Function: warning
+Section: programming/specific
+C-Name: warning0
+Prototype: vs*
+Help: warning({str}*): display warning message str
+Description:
+ (?gen,...):void  pari_warn(warnuser, "${2 format_string}"${2 format_args})
+Doc: outputs the message ``user warning''
+ and the argument list (each of them interpreted as a string).
+ If colors are enabled, this warning will be in a different color,
+ making it easy to distinguish.
+ \bprog
+ warning(n, " is very large, this might take a while.")
+ @eprog
+ % \syn{NO}
+
diff --git a/src/functions/programming/whatnow b/src/functions/programming/whatnow
new file mode 100644
index 0000000..a688966
--- /dev/null
+++ b/src/functions/programming/whatnow
@@ -0,0 +1,12 @@
+Function: whatnow
+Class: gp
+Section: programming/specific
+C-Name: whatnow0
+Prototype: vr
+Help: whatnow(key): if key was present in GP version 1.39.15 or lower, gives
+ the new function name.
+Description:
+ (str):void             whatnow($1, 0)
+Doc: if keyword \var{key} is the name of a function that was present in GP
+ version 1.39.15 or lower, outputs the new function name and syntax, if it
+ changed at all ($387$ out of $560$ did).
diff --git a/src/functions/programming/while b/src/functions/programming/while
new file mode 100644
index 0000000..05873fb
--- /dev/null
+++ b/src/functions/programming/while
@@ -0,0 +1,9 @@
+Function: while
+Section: programming/control
+C-Name: whilepari
+Prototype: vEI
+Help: while(a,seq): while a is nonzero evaluate the expression sequence seq.
+ Otherwise 0.
+Doc: while $a$ is non-zero, evaluates the expression sequence \var{seq}. The
+ test is made \emph{before} evaluating the $seq$, hence in particular if $a$
+ is initially equal to zero the \var{seq} will not be evaluated at all.
diff --git a/src/functions/programming/write b/src/functions/programming/write
new file mode 100644
index 0000000..3c58bf8
--- /dev/null
+++ b/src/functions/programming/write
@@ -0,0 +1,9 @@
+Function: write
+Section: programming/specific
+C-Name: write0
+Prototype: vss*
+Help: write(filename,{str}*): appends the remaining arguments (same output as
+ print) to filename.
+Doc: writes (appends) to \var{filename} the remaining arguments, and appends a
+ newline (same output as \kbd{print}).
+ %\syn{NO}
diff --git a/src/functions/programming/write1 b/src/functions/programming/write1
new file mode 100644
index 0000000..da9bca2
--- /dev/null
+++ b/src/functions/programming/write1
@@ -0,0 +1,9 @@
+Function: write1
+Section: programming/specific
+C-Name: write1
+Prototype: vss*
+Help: write1(filename,{str}*): appends the remaining arguments (same output as
+ print1) to filename.
+Doc: writes (appends) to \var{filename} the remaining arguments without a
+ trailing newline (same output as \kbd{print1}).
+ %\syn{NO}
diff --git a/src/functions/programming/writebin b/src/functions/programming/writebin
new file mode 100644
index 0000000..1450b8c
--- /dev/null
+++ b/src/functions/programming/writebin
@@ -0,0 +1,38 @@
+Function: writebin
+Section: programming/specific
+C-Name: gpwritebin
+Prototype: vsDG
+Help: writebin(filename,{x}): write x as a binary object to file filename.
+ If x is omitted, write all session variables.
+Doc: writes (appends) to
+ \var{filename} the object $x$ in binary format. This format is not human
+ readable, but contains the exact internal structure of $x$, and is much
+ faster to save/load than a string expression, as would be produced by
+ \tet{write}. The binary file format includes a magic number, so that such a
+ file can be recognized and correctly input by the regular \tet{read} or \b{r}
+ function. If saved objects refer to (polynomial) variables that are not
+ defined in the new session, they will be displayed in a funny way (see
+ \secref{se:kill}). Installed functions and history objects can not be saved
+ via this function.
+
+ If $x$ is omitted, saves all user variables from the session, together with
+ their names. Reading such a ``named object'' back in a \kbd{gp} session will set
+ the corresponding user variable to the saved value. E.g after
+ \bprog
+ x = 1; writebin("log")
+ @eprog\noindent
+ reading \kbd{log} into a clean session will set \kbd{x} to $1$.
+ The relative variables priorities (see \secref{se:priority}) of new variables
+ set in this way remain the same (preset variables retain their former
+ priority, but are set to the new value). In particular, reading such a
+ session log into a clean session will restore all variables exactly as they
+ were in the original one.
+
+ Just as a regular input file, a binary file can be compressed
+ using \tet{gzip}, provided the file name has the standard \kbd{.gz}
+ extension.\sidx{binary file}
+
+ In the present implementation, the binary files are architecture dependent
+ and compatibility with future versions of \kbd{gp} is not guaranteed. Hence
+ binary files should not be used for long term storage (also, they are
+ larger and harder to compress than text files).
diff --git a/src/functions/programming/writetex b/src/functions/programming/writetex
new file mode 100644
index 0000000..b0c014d
--- /dev/null
+++ b/src/functions/programming/writetex
@@ -0,0 +1,8 @@
+Function: writetex
+Section: programming/specific
+C-Name: writetex
+Prototype: vss*
+Help: writetex(filename,{str}*): appends the remaining arguments (same format as
+ print) to filename, in TeX format.
+Doc: as \kbd{write}, in \TeX\ format.
+ %\syn{NO}
diff --git a/src/functions/sums/derivnum b/src/functions/sums/derivnum
new file mode 100644
index 0000000..bc55706
--- /dev/null
+++ b/src/functions/sums/derivnum
@@ -0,0 +1,34 @@
+Function: derivnum
+Section: sums
+C-Name: derivnum0
+Prototype: V=GEp
+Help: derivnum(X=a,expr): numerical derivation of expr with respect to
+ X at X = a.
+Wrapper: (,G)
+Description:
+  (gen,gen):gen:prec derivnum(${2 cookie}, ${2 wrapper}, $1, prec)
+Doc: numerical derivation of \var{expr} with respect to $X$ at $X=a$.
+
+ \bprog
+ ? derivnum(x=0,sin(exp(x))) - cos(1)
+ %1 = -1.262177448 E-29
+ @eprog
+ A clumsier approach, which would not work in library mode, is
+ \bprog
+ ? f(x) = sin(exp(x))
+ ? f'(0) - cos(1)
+ %1 = -1.262177448 E-29
+ @eprog
+ When $a$ is a power series, compute \kbd{derivnum(t=a,f)} as $f'(a) =
+ (f(a))'/a'$.
+
+ \synt{derivnum}{void *E, GEN (*eval)(void*,GEN), GEN a, long prec}. Also
+ available is \fun{GEN}{derivfun}{void *E, GEN (*eval)(void *, GEN), GEN a, long prec}, which also allows power series for $a$.
+
+Function: _derivfun
+Section: programming/internals
+C-Name: derivfun0
+Prototype: GGp
+Help: _derivfun(closure,[args]) numerical derivation of closure with respect to
+ the first variable at (args).
+
diff --git a/src/functions/sums/intcirc b/src/functions/sums/intcirc
new file mode 100644
index 0000000..ab91b07
--- /dev/null
+++ b/src/functions/sums/intcirc
@@ -0,0 +1,24 @@
+Function: intcirc
+Section: sums
+C-Name: intcirc0
+Prototype: V=GGEDGp
+Help: intcirc(X=a,R,expr,{tab}): numerical integration of expr on the circle
+ |z-a|=R, divided by 2*I*Pi. tab is as in intnum.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?gen):gen:prec intcirc(${3 cookie}, ${3 wrapper}, $1, $2, $4, prec)
+Doc: numerical
+ integration of $(2i\pi)^{-1}\var{expr}$ with respect to $X$ on the circle
+ $|X-a| = R$.
+ In other words, when \var{expr} is a meromorphic
+ function, sum of the residues in the corresponding disk. \var{tab} is as in
+ \kbd{intnum}, except that if computed with \kbd{intnuminit} it should be with
+ the endpoints \kbd{[-1, 1]}.
+
+ \bprog
+ ? \p105
+ ? intcirc(s=1, 0.5, zeta(s)) - 1
+ %1 = -2.398082982 E-104 - 7.94487211 E-107*I
+ @eprog
+
+ \synt{intcirc}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN R,GEN tab, long prec}.
diff --git a/src/functions/sums/intfouriercos b/src/functions/sums/intfouriercos
new file mode 100644
index 0000000..87c403b
--- /dev/null
+++ b/src/functions/sums/intfouriercos
@@ -0,0 +1,18 @@
+Function: intfouriercos
+Section: sums
+C-Name: intfourcos0
+Prototype: V=GGGEDGp
+Help: intfouriercos(X=a,b,z,expr,{tab}): numerical integration from a to b
+ of cos(2*Pi*z*X)*expr(X) from a to b, where a, b, and tab are as in intnum.
+ This is the cosine-Fourier transform if a=-infty and b=+infty.
+Wrapper: (,,,G)
+Description:
+  (gen,gen,gen,gen,?gen):gen:prec intfouriercos(${4 cookie}, ${4 wrapper}, $1, $2, $3, $5, prec)
+Doc: numerical
+ integration of $\var{expr}(X)\cos(2\pi zX)$ from $a$ to $b$, in other words
+ Fourier cosine transform (from $a$ to $b$) of the function represented by
+ \var{expr}. Endpoints $a$ and $b$ are coded as in \kbd{intnum}, and are not
+ necessarily at infinity, but if they are, oscillations (i.e. $[[\pm1],\alpha
+ I]$) are forbidden.
+
+ \synt{intfouriercos}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, GEN z, GEN tab, long prec}.
diff --git a/src/functions/sums/intfourierexp b/src/functions/sums/intfourierexp
new file mode 100644
index 0000000..ac895d8
--- /dev/null
+++ b/src/functions/sums/intfourierexp
@@ -0,0 +1,19 @@
+Function: intfourierexp
+Section: sums
+C-Name: intfourexp0
+Prototype: V=GGGEDGp
+Help: intfourierexp(X=a,b,z,expr,{tab}): numerical integration from a to b
+ of exp(-2*I*Pi*z*X)*expr(X) from a to b, where a, b, and tab are as in intnum.
+ This is the ordinary Fourier transform if a=-infty and b=+infty. Note the
+ minus sign.
+Wrapper: (,,,G)
+Description:
+  (gen,gen,gen,gen,?gen):gen:prec intfourierexp(${4 cookie}, ${4 wrapper}, $1, $2, $3, $5, prec)
+Doc: numerical
+ integration of $\var{expr}(X)\exp(-2i\pi zX)$ from $a$ to $b$, in other words
+ Fourier transform (from $a$ to $b$) of the function represented by
+ \var{expr}. Note the minus sign. Endpoints $a$ and $b$ are coded as in
+ \kbd{intnum}, and are not necessarily at infinity but if they are,
+ oscillations (i.e. $[[\pm1],\alpha I]$) are forbidden.
+
+ \synt{intfourierexp}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, GEN z, GEN tab, long prec}.
diff --git a/src/functions/sums/intfouriersin b/src/functions/sums/intfouriersin
new file mode 100644
index 0000000..4fb8fea
--- /dev/null
+++ b/src/functions/sums/intfouriersin
@@ -0,0 +1,18 @@
+Function: intfouriersin
+Section: sums
+C-Name: intfoursin0
+Prototype: V=GGGEDGp
+Help: intfouriersin(X=a,b,z,expr,{tab}): numerical integration from a to b
+ of sin(2*Pi*z*X)*expr(X) from a to b, where a, b, and tab are as in intnum.
+ This is the sine-Fourier transform if a=-infty and b=+infty.
+Wrapper: (,,,G)
+Description:
+  (gen,gen,gen,gen,?gen):gen:prec intfouriercos(${4 cookie}, ${4 wrapper}, $1, $2, $3, $5, prec)
+Doc: numerical
+ integration of $\var{expr}(X)\sin(2\pi zX)$ from $a$ to $b$, in other words
+ Fourier sine transform (from $a$ to $b$) of the function represented by
+ \var{expr}. Endpoints $a$ and $b$ are coded as in \kbd{intnum}, and are not
+ necessarily at infinity but if they are, oscillations (i.e. $[[\pm1],\alpha
+ I]$) are forbidden.
+
+ \synt{intfouriersin}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, GEN z, GEN tab, long prec}.
diff --git a/src/functions/sums/intfuncinit b/src/functions/sums/intfuncinit
new file mode 100644
index 0000000..727a815
--- /dev/null
+++ b/src/functions/sums/intfuncinit
@@ -0,0 +1,26 @@
+Function: intfuncinit
+Section: sums
+C-Name: intfuncinit0
+Prototype: V=GGED0,L,D0,L,p
+Help: intfuncinit(X=a,b,expr,{flag=0},{m=0}): initialize tables for integrations
+ from a to b using a weight expr(X). Essential for integral transforms such
+ as intmellininv, intlaplaceinv and intfourier, since it avoids recomputing
+ all the time the same quantities. Must then be used with intmellininvshort
+ (for intmellininv) and directly with intnum and not with the corresponding
+ integral transforms for the others. See help for intnum for coding of a
+ and b, and m is as in intnuminit. If flag is nonzero, assumes that
+ expr(-X)=conj(expr(X)), which is twice faster.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?small,?small):gen:prec intfuncinit(${3 cookie}, ${3 wrapper}, $1, $2, $4, $5, prec)
+Doc: initialize tables for use with integral transforms such as \kbd{intmellininv},
+ etc., where $a$ and $b$ are coded as in \kbd{intnum}, $\var{expr}$ is the
+ function $s(X)$ to which the integral transform is to be applied (which will
+ multiply the weights of integration) and $m$ is as in \kbd{intnuminit}. If
+ $\fl$ is nonzero, assumes that $s(-X)=\overline{s(X)}$, which makes the
+ computation twice as fast. See \kbd{intmellininvshort} for examples of the
+ use of this function, which is particularly useful when the function $s(X)$
+ is lengthy to compute, such as a gamma product.
+
+ \synt{intfuncinit}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN b,long m, long flag, long prec}. Note that the order of $m$ and $\fl$ are reversed compared
+ to the \kbd{GP} syntax.
diff --git a/src/functions/sums/intlaplaceinv b/src/functions/sums/intlaplaceinv
new file mode 100644
index 0000000..34ce596
--- /dev/null
+++ b/src/functions/sums/intlaplaceinv
@@ -0,0 +1,56 @@
+Function: intlaplaceinv
+Section: sums
+C-Name: intlaplaceinv0
+Prototype: V=GGEDGp
+Help: intlaplaceinv(X=sig,z,expr,{tab}): numerical integration on the line
+ real(X) = sig of expr(X)exp(zX)dz/(2*I*Pi), i.e. inverse Laplace transform of
+ expr at z. tab is as in intnum.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?gen):gen:prec intlaplaceinv(${3 cookie}, ${3 wrapper}, $1, $2, $4, prec)
+Doc: numerical integration of $(2i\pi)^{-1}\var{expr}(X)e^{Xz}$ with respect
+ to $X$ on the line $\Re(X)=sig$. In other words, inverse Laplace transform
+ of the function corresponding to \var{expr} at the value $z$.
+
+ $sig$ is coded as follows. Either it is a real number $\sigma$, equal to the
+ abscissa of integration, and then the integrand is assumed to
+ be slowly decreasing when the imaginary part of the variable tends to
+ $\pm\infty$. Or it is a two component vector $[\sigma,\alpha]$, where
+ $\sigma$ is as before, and either $\alpha=0$ for slowly decreasing functions,
+ or $\alpha>0$ for functions decreasing like $\exp(-\alpha t)$. Note that it
+ is not necessary to choose the exact value of $\alpha$. \var{tab} is as in
+ \kbd{intnum}.
+
+ It is often a good idea to use this function with a value of $m$ one or two
+ higher than the one chosen by default (which can be viewed thanks to the
+ function \kbd{intnumstep}), or to increase the abscissa of integration
+ $\sigma$. For example:
+
+ \bprog
+ ? \p 105
+ ? intlaplaceinv(x=2, 1, 1/x) - 1
+ time = 350 ms.
+ %1 = 7.37... E-55 + 1.72... E-54*I \\@com not so good
+ ? m = intnumstep()
+ %2 = 7
+ ? intlaplaceinv(x=2, 1, 1/x, m+1) - 1
+ time = 700 ms.
+ %3 = 3.95... E-97 + 4.76... E-98*I \\@com better
+ ? intlaplaceinv(x=2, 1, 1/x, m+2) - 1
+ time = 1400 ms.
+ %4 = 0.E-105 + 0.E-106*I \\@com perfect but slow.
+ ? intlaplaceinv(x=5, 1, 1/x) - 1
+ time = 340 ms.
+ %5 = -5.98... E-85 + 8.08... E-85*I \\@com better than \%1
+ ? intlaplaceinv(x=5, 1, 1/x, m+1) - 1
+ time = 680 ms.
+ %6 = -1.09... E-106 + 0.E-104*I \\@com perfect, fast.
+ ? intlaplaceinv(x=10, 1, 1/x) - 1
+ time = 340 ms.
+ %7 = -4.36... E-106 + 0.E-102*I \\@com perfect, fastest, but why $sig=10$?
+ ? intlaplaceinv(x=100, 1, 1/x) - 1
+ time = 330 ms.
+ %7 = 1.07... E-72 + 3.2... E-72*I \\@com too far now...
+ @eprog
+
+ \synt{intlaplaceinv}{void *E, GEN (*eval)(void*,GEN), GEN sig,GEN z, GEN tab, long prec}.
diff --git a/src/functions/sums/intmellininv b/src/functions/sums/intmellininv
new file mode 100644
index 0000000..6994e64
--- /dev/null
+++ b/src/functions/sums/intmellininv
@@ -0,0 +1,43 @@
+Function: intmellininv
+Section: sums
+C-Name: intmellininv0
+Prototype: V=GGEDGp
+Help: intmellininv(X=sig,z,expr,{tab}): numerical integration on the
+ line real(X) = sig (or sig[1]) of expr(X)z^(-X)dX/(2*I*Pi), i.e. inverse Mellin
+ transform of s at x. sig is coded as follows: either it is real, and then
+ by default assume s(z) decreases like exp(-z). Or sig = [sigR, al], sigR is
+ the abscissa of integration, and al = 0 for slowly decreasing functions, or
+ al > 0 if s(z) decreases like exp(-al*z). tab is as in intnum. Use
+ intmellininvshort if several values must be computed.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?gen):gen:prec intmellininv(${3 cookie}, ${3 wrapper}, $1, $2, $4, prec)
+Doc: numerical
+ integration of $(2i\pi)^{-1}\var{expr}(X)z^{-X}$ with respect to $X$ on the
+ line $\Re(X)=sig$,  in other words, inverse Mellin transform of
+ the function corresponding to \var{expr} at the value $z$.
+
+ $sig$ is coded as follows. Either it is a real number $\sigma$, equal to the
+ abscissa of integration, and then the integrated is assumed to decrease
+ exponentially fast, of the order of $\exp(-t)$ when the imaginary part of the
+ variable tends to $\pm\infty$. Or it is a two component vector
+ $[\sigma,\alpha]$, where $\sigma$ is as before, and either $\alpha=0$ for
+ slowly decreasing functions, or $\alpha>0$ for functions decreasing like
+ $\exp(-\alpha t)$, such as gamma products. Note that it is not necessary to
+ choose the exact value of $\alpha$, and that $\alpha=1$ (equivalent to $sig$
+ alone) is usually sufficient. \var{tab} is as in \kbd{intnum}.
+
+ As all similar functions, this function is provided for the convenience of
+ the user, who could use \kbd{intnum} directly. However it is in general
+ better to use \kbd{intmellininvshort}.
+
+ \bprog
+ ? \p 105
+ ? intmellininv(s=2,4, gamma(s)^3);
+ time = 1,190 ms. \\@com reasonable.
+ ? \p 308
+ ? intmellininv(s=2,4, gamma(s)^3);
+ time = 51,300 ms. \\@com slow because of $\Gamma(s)^3$.
+ @eprog\noindent
+
+ \synt{intmellininv}{void *E, GEN (*eval)(void*,GEN), GEN sig, GEN z, GEN tab, long prec}.
diff --git a/src/functions/sums/intmellininvshort b/src/functions/sums/intmellininvshort
new file mode 100644
index 0000000..6083611
--- /dev/null
+++ b/src/functions/sums/intmellininvshort
@@ -0,0 +1,43 @@
+Function: intmellininvshort
+Section: sums
+C-Name: intmellininvshort
+Prototype: GGGp
+Help: intmellininvshort(sig,z,tab): numerical integration on the
+ line real(X) = sig (or sig[1]) of s(X)z^(-X)dX/(2*I*Pi), i.e. inverse Mellin
+ transform of s at z. sig is coded as follows: either it is real, and then
+ by default assume s(X) decreases like exp(-X). Or sig = [sigR, al], sigR is
+ the abscissa of integration, and al = 0 for slowly decreasing functions, or
+ al > 0 if s(X) decreases like exp(-al*X). Compulsory table tab has been
+ precomputed using the command intfuncinit(t=[[-1],sig[2]],[[1],sig[2]],s)
+ (with possibly its two optional additional parameters), where sig[2] = 1
+ if not given. Orders of magnitude faster than intmellininv.
+Doc: numerical integration
+ of $(2i\pi)^{-1}s(X)z^{-X}$ with respect to $X$ on the line $\Re(X)=sig$.
+ In other words, inverse Mellin transform of $s(X)$ at the value $z$.
+ Here $s(X)$ is implicitly contained in \var{tab} in \kbd{intfuncinit} format,
+ typically
+ \bprog
+ tab = intfuncinit(T = [-1], [1], s(sig + I*T))
+ @eprog\noindent
+ or similar commands. Take the example of the inverse Mellin transform of
+ $\Gamma(s)^3$ given in \kbd{intmellininv}:
+
+ \bprog
+ ? \p 105
+ ? oo = [1]; \\@com for clarity
+ ? A = intmellininv(s=2,4, gamma(s)^3);
+ time = 2,500 ms. \\@com not too fast because of $\Gamma(s)^3$.
+ \\ @com function of real type, decreasing as $\exp(-3\pi/2\cdot |t|)$
+ ? tab = intfuncinit(t=[-oo, 3*Pi/2],[oo, 3*Pi/2], gamma(2+I*t)^3, 1);
+ time = 1,370 ms.
+ ? intmellininvshort(2,4, tab) - A
+ time = 50 ms.
+ %4 = -1.26... - 3.25...E-109*I \\@com 50 times faster than \kbd{A} and perfect.
+ ? tab2 = intfuncinit(t=-oo, oo, gamma(2+I*t)^3, 1);
+ ? intmellininvshort(2,4, tab2)
+ %6 = -1.2...E-42 - 3.2...E-109*I  \\@com 63 digits lost
+ @eprog\noindent
+ In the computation of \var{tab}, it was not essential to include the
+ \emph{exact} exponential decrease of $\Gamma(2+it)^3$. But as the last
+ example shows, a rough indication \emph{must} be given, otherwise slow
+ decrease is assumed, resulting in catastrophic loss of accuracy.
diff --git a/src/functions/sums/intnum b/src/functions/sums/intnum
new file mode 100644
index 0000000..5b89b99
--- /dev/null
+++ b/src/functions/sums/intnum
@@ -0,0 +1,287 @@
+Function: intnum
+Section: sums
+C-Name: intnum0
+Prototype: V=GGEDGp
+Help: intnum(X=a,b,expr,{tab}): numerical integration of expr from a to b with
+ respect to X. Plus/minus infinity is coded as [+1]/ [-1]. Finally tab is
+ either omitted (let the program choose the integration step), a positive
+ integer m (choose integration step 1/2^m), or data precomputed with intnuminit.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?gen):gen:prec intnum(${3 cookie}, ${3 wrapper}, $1, $2, $4, prec)
+Doc: numerical integration
+ of \var{expr} on $]a,b[$ with respect to $X$. The integrand may have values
+ belonging to a vector space over the real numbers; in particular, it can be
+ complex-valued or vector-valued. But it is assumed that the function is regular
+ on $]a,b[$. If the endpoints $a$ and $b$ are finite and the function is regular
+ there, the situation is simple:
+ \bprog
+ ? intnum(x = 0,1, x^2)
+ %1 = 0.3333333333333333333333333333
+ ? intnum(x = 0,Pi/2, [cos(x), sin(x)])
+ %2 = [1.000000000000000000000000000, 1.000000000000000000000000000]
+ @eprog\noindent
+ An endpoint equal to $\pm\infty$ is coded as the single-component vector
+ $[\pm1]$. You are welcome to set, e.g \kbd{oo = [1]} or \kbd{INFINITY = [1]},
+ then using \kbd{+oo}, \kbd{-oo}, \kbd{-INFINITY}, etc. will have the expected
+ behavior.
+ \bprog
+ ? oo = [1];  \\@com for clarity
+ ? intnum(x = 1,+oo, 1/x^2)
+ %2 = 1.000000000000000000000000000
+ @eprog\noindent
+ In basic usage, it is assumed that the function does not decrease
+ exponentially fast at infinity:
+ \bprog
+ ? intnum(x=0,+oo, exp(-x))
+   ***   at top-level: intnum(x=0,+oo,exp(-
+   ***                 ^--------------------
+   *** exp: exponent (expo) overflow
+ @eprog\noindent
+ We shall see in a moment how to avoid the last problem, after describing
+ the last argument \var{tab}, which is both optional and technical. The
+ routine uses weights, which are mostly independent of the function being
+ integrated, evaluated at many sampling points. If \var{tab} is
+
+ \item a positive integer $m$, we use $2^m$ sampling points, hopefully
+ increasing accuracy. But note that the running time is roughly proportional
+ to $2^m$. One may try consecutive values of $m$ until they give the same
+ value up to an accepted error. If \var{tab} is omitted, the algorithm guesses
+ a reasonable value for $m$ depending on the current precision only, which
+ should be sufficient for regular functions. That value may be obtained from
+ \tet{intnumstep}, and increased in case of difficulties.
+
+ \item a set of integration tables as output by \tet{intnuminit},
+ they are used directly. This is useful if several integrations of the same
+ type are performed (on the same kind of interval and functions, for a given
+ accuracy), in particular for multivariate integrals, since we then skip
+ expensive precomputations.
+
+ \misctitle{Specifying the behavior at endpoints}
+ This is done as follows. An endpoint $a$ is either given as such (a scalar,
+ real or complex, or $[\pm1]$ for $\pm\infty$), or as a two component vector
+ $[a,\alpha]$, to indicate the behavior of the integrand in a neighborhood
+ of $a$.
+
+ If $a$ is finite, the code $[a,\alpha]$ means the function has a
+ singularity of the form $(x-a)^{\alpha}$, up to logarithms. (If $\alpha \ge
+ 0$, we only assume the function is regular, which is the default assumption.)
+ If a wrong singularity exponent is used, the result will lose a catastrophic
+ number of decimals:
+ \bprog
+ ? intnum(x=0, 1, x^(-1/2))         \\@com assume $x^{-1/2}$ is regular at 0
+ %1 = 1.999999999999999999990291881
+ ? intnum(x=[0,-1/2], 1, x^(-1/2))  \\@com no, it's not
+ %2 = 2.000000000000000000000000000
+ ? intnum(x=[0,-1/10], 1, x^(-1/2))
+ %3 = 1.999999999999999999999946438 \\@com using a wrong exponent is bad
+ @eprog
+
+ If $a$ is $\pm\infty$, which is coded as $[\pm 1]$, the situation is more
+ complicated, and $[[\pm1],\alpha]$ means:
+
+ \item $\alpha=0$ (or no $\alpha$ at all, i.e. simply $[\pm1]$) assumes that the
+ integrand tends to zero, but not exponentially fast, and not
+ oscillating such as $\sin(x)/x$.
+
+ \item $\alpha>0$ assumes that the function tends to zero exponentially fast
+ approximately as $\exp(-\alpha x)$. This includes oscillating but quickly
+ decreasing functions such as $\exp(-x)\sin(x)$.
+ \bprog
+ ? oo = [1];
+ ? intnum(x=0, +oo, exp(-2*x))
+   ***   at top-level: intnum(x=0,+oo,exp(-
+   ***                 ^--------------------
+   *** exp: exponent (expo) overflow
+ ? intnum(x=0, [+oo, 2], exp(-2*x))
+ %1 = 0.5000000000000000000000000000 \\@com OK!
+ ? intnum(x=0, [+oo, 4], exp(-2*x))
+ %2 = 0.4999999999999999999961990984 \\@com wrong exponent $\Rightarrow$ imprecise result
+ ? intnum(x=0, [+oo, 20], exp(-2*x))
+ %2 = 0.4999524997739071283804510227 \\@com disaster
+ @eprog
+
+ \item $\alpha<-1$ assumes that the function tends to $0$ slowly, like
+ $x^{\alpha}$. Here it is essential to give the correct $\alpha$, if possible,
+ but on the other hand $\alpha\le -2$ is equivalent to $\alpha=0$, in other
+ words to no $\alpha$ at all.
+
+ \smallskip The last two codes are reserved for oscillating functions.
+ Let $k > 0$ real, and $g(x)$ a non-oscillating function tending slowly to $0$
+ (e.g. like a negative power of $x$), then
+
+ \item $\alpha=k * I$ assumes that the function behaves like $\cos(kx)g(x)$.
+
+ \item $\alpha=-k* I$ assumes that the function behaves like $\sin(kx)g(x)$.
+
+ \noindent Here it is critical to give the exact value of $k$. If the
+ oscillating part is not a pure sine or cosine, one must expand it into a
+ Fourier series, use the above codings, and sum the resulting contributions.
+ Otherwise you will get nonsense. Note that $\cos(kx)$, and similarly
+ $\sin(kx)$, means that very function, and not a translated version such as
+ $\cos(kx+a)$.
+
+ \misctitle{Note} If $f(x)=\cos(kx)g(x)$ where $g(x)$ tends to zero
+ exponentially fast as $\exp(-\alpha x)$, it is up to the user to choose
+ between $[[\pm1],\alpha]$ and $[[\pm1],k* I]$, but a good rule of thumb is that
+ if the oscillations are much weaker than the exponential decrease, choose
+ $[[\pm1],\alpha]$, otherwise choose $[[\pm1],k* I]$, although the latter can
+ reasonably be used in all cases, while the former cannot. To take a specific
+ example, in the inverse Mellin transform, the integrand is almost always a
+ product of an exponentially decreasing and an oscillating factor. If we
+ choose the oscillating type of integral we perhaps obtain the best results,
+ at the expense of having to recompute our functions for a different value of
+ the variable $z$ giving the transform, preventing us to use a function such
+ as \kbd{intmellininvshort}. On the other hand using the exponential type of
+ integral, we obtain less accurate results, but we skip expensive
+ recomputations. See \kbd{intmellininvshort} and \kbd{intfuncinit} for more
+ explanations.
+
+ \smallskip
+
+ We shall now see many examples to get a feeling for what the various
+ parameters achieve. All examples below assume precision is set to $105$
+ decimal digits. We first type
+ \bprog
+ ? \p 105
+ ? oo = [1]  \\@com for clarity
+ @eprog
+
+ \misctitle{Apparent singularities} Even if the function $f(x)$ represented
+ by \var{expr} has no singularities, it may be important to define the
+ function differently near special points. For instance, if $f(x) = 1
+ /(\exp(x)-1) - \exp(-x)/x$, then $\int_0^\infty f(x)\,dx=\gamma$, Euler's
+ constant \kbd{Euler}. But
+
+ \bprog
+ ? f(x) = 1/(exp(x)-1) - exp(-x)/x
+ ? intnum(x = 0, [oo,1],  f(x)) - Euler
+ %1 = 6.00... E-67
+ @eprog\noindent
+ thus only correct to $67$ decimal digits. This is because close to $0$ the
+ function $f$ is computed with an enormous loss of accuracy.
+ A better solution is
+
+ \bprog
+ ? f(x) = 1/(exp(x)-1)-exp(-x)/x
+ ? F = truncate( f(t + O(t^7)) ); \\@com expansion around t = 0
+ ? g(x) = if (x > 1e-18, f(x), subst(F,t,x))  \\@com note that $6 \cdot 18 > 105$
+ ? intnum(x = 0, [oo,1],  g(x)) - Euler
+ %2 = 0.E-106 \\@com perfect
+ @eprog\noindent
+ It is up to the user to determine constants such as the $10^{-18}$ and $7$
+ used above.
+
+ \misctitle{True singularities} With true singularities the result is worse.
+ For instance
+
+ \bprog
+ ? intnum(x = 0, 1,  1/sqrt(x)) - 2
+ %1 = -1.92... E-59 \\@com only $59$ correct decimals
+
+ ? intnum(x = [0,-1/2], 1,  1/sqrt(x)) - 2
+ %2 = 0.E-105 \\@com better
+ @eprog
+
+ \misctitle{Oscillating functions}
+
+ \bprog
+ ? intnum(x = 0, oo, sin(x) / x) - Pi/2
+ %1 = 20.78.. \\@com nonsense
+ ? intnum(x = 0, [oo,1], sin(x)/x) - Pi/2
+ %2 = 0.004.. \\@com bad
+ ? intnum(x = 0, [oo,-I], sin(x)/x) - Pi/2
+ %3 = 0.E-105 \\@com perfect
+ ? intnum(x = 0, [oo,-I], sin(2*x)/x) - Pi/2  \\@com oops, wrong $k$
+ %4 = 0.07...
+ ? intnum(x = 0, [oo,-2*I], sin(2*x)/x) - Pi/2
+ %5 = 0.E-105 \\@com perfect
+
+ ? intnum(x = 0, [oo,-I], sin(x)^3/x) - Pi/4
+ %6 = 0.0092... \\@com bad
+ ? sin(x)^3 - (3*sin(x)-sin(3*x))/4
+ %7 = O(x^17)
+ @eprog\noindent
+ We may use the above linearization and compute two oscillating integrals with
+ ``infinite endpoints'' \kbd{[oo, -I]} and \kbd{[oo, -3*I]} respectively, or
+ notice the obvious change of variable, and reduce to the single integral
+ ${1\over 2}\int_0^\infty \sin(x)/x\,dx$. We finish with some more complicated
+ examples:
+
+ \bprog
+ ? intnum(x = 0, [oo,-I], (1-cos(x))/x^2) - Pi/2
+ %1 = -0.0004... \\@com bad
+ ? intnum(x = 0, 1, (1-cos(x))/x^2) \
+ + intnum(x = 1, oo, 1/x^2) - intnum(x = 1, [oo,I], cos(x)/x^2) - Pi/2
+ %2 = -2.18... E-106 \\@com OK
+
+ ? intnum(x = 0, [oo, 1], sin(x)^3*exp(-x)) - 0.3
+ %3 = 5.45... E-107 \\@com OK
+ ? intnum(x = 0, [oo,-I], sin(x)^3*exp(-x)) - 0.3
+ %4 = -1.33... E-89 \\@com lost 16 decimals. Try higher $m$:
+ ? m = intnumstep()
+ %5 = 7 \\@com the value of $m$ actually used above.
+ ? tab = intnuminit(0,[oo,-I], m+1); \\@com try $m$ one higher.
+ ? intnum(x = 0, oo, sin(x)^3*exp(-x), tab) - 0.3
+ %6 = 5.45... E-107 \\@com OK this time.
+ @eprog
+
+ \misctitle{Warning} Like \tet{sumalt}, \kbd{intnum} often assigns a
+ reasonable value to diverging integrals. Use these values at your own risk!
+ For example:
+
+ \bprog
+ ? intnum(x = 0, [oo, -I], x^2*sin(x))
+ %1 = -2.0000000000...
+ @eprog\noindent
+ Note the formula
+ $$ \int_0^\infty \sin(x)/x^s\,dx = \cos(\pi s/2) \Gamma(1-s)\;, $$
+ a priori valid only for $0 < \Re(s) < 2$, but the right hand side provides an
+ analytic continuation which may be evaluated at $s = -2$\dots
+
+ \misctitle{Multivariate integration}
+ Using successive univariate integration with respect to different formal
+ parameters, it is immediate to do naive multivariate integration. But it is
+ important to use a suitable \kbd{intnuminit} to precompute data for the
+ \emph{internal} integrations at least!
+
+ For example, to compute the double integral on the unit disc $x^2+y^2\le1$
+ of the function $x^2+y^2$, we can write
+ \bprog
+ ? tab = intnuminit(-1,1);
+ ? intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2, tab), tab)
+ @eprog\noindent
+ The first \var{tab} is essential, the second optional. Compare:
+
+ \bprog
+ ? tab = intnuminit(-1,1);
+ time = 30 ms.
+ ? intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2));
+ time = 54,410 ms. \\@com slow
+ ? intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2, tab), tab);
+ time = 7,210 ms.  \\@com faster
+ @eprog\noindent
+ However, the \kbd{intnuminit} program is usually pessimistic when it comes to
+ choosing the integration step $2^{-m}$. It is often possible to improve the
+ speed by trial and error. Continuing the above example:
+ \bprog
+ ? test(M) =
+ {
+ tab = intnuminit(-1,1, M);
+ intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2), x^2+y^2,tab), tab) - Pi/2
+ }
+ ? m = intnumstep() \\@com what value of $m$ did it take?
+ %1 = 7
+ ? test(m - 1)
+ time = 1,790 ms.
+ %2 = -2.05... E-104 \\@com $4 = 2^2$ times faster and still OK.
+ ? test(m - 2)
+ time = 430 ms.
+ %3 = -1.11... E-104 \\@com $16 = 2^4$ times faster and still OK.
+ ? test(m - 3)
+ time = 120 ms.
+ %3 = -7.23... E-60 \\@com $64 = 2^6$ times faster, lost $45$ decimals.
+ @eprog
+
+ \synt{intnum}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN b,GEN tab, long prec},
+ where an omitted \var{tab} is coded as \kbd{NULL}.
diff --git a/src/functions/sums/intnuminit b/src/functions/sums/intnuminit
new file mode 100644
index 0000000..57d734c
--- /dev/null
+++ b/src/functions/sums/intnuminit
@@ -0,0 +1,31 @@
+Function: intnuminit
+Section: sums
+C-Name: intnuminit
+Prototype: GGD0,L,p
+Help: intnuminit(a,b,{m=0}): initialize tables for integrations from a to b.
+ See help for intnum for coding of a and b. Possible types: compact interval,
+ semi-compact (one extremity at + or - infinity) or R, and very slowly, slowly
+ or exponentially decreasing, or sine or cosine oscillating at infinities.
+Doc: initialize tables for integration from
+ $a$ to $b$, where $a$ and $b$ are coded as in \kbd{intnum}. Only the
+ compactness, the possible existence of singularities, the speed of decrease
+ or the oscillations at infinity are taken into account, and not the values.
+ For instance {\tt intnuminit(-1,1)} is equivalent to {\tt intnuminit(0,Pi)},
+ and {\tt intnuminit([0,-1/2],[1])} is equivalent to {\tt
+ intnuminit([-1],[-1,-1/2])}. If $m$ is not given, it is computed according to
+ the current precision. Otherwise the integration step is $1/2^m$. Reasonable
+ values of $m$ are $m=6$ or $m=7$ for $100$ decimal digits, and $m=9$ for
+ $1000$ decimal digits.
+
+ The result is technical, but in some cases it is useful to know the output.
+ Let $x=\phi(t)$ be the change of variable which is used. \var{tab}[1] contains
+ the integer $m$ as above, either given by the user or computed from the default
+ precision, and can be recomputed directly using the function \kbd{intnumstep}.
+ \var{tab}[2] and \var{tab}[3] contain respectively the abscissa and weight
+ corresponding to $t=0$ ($\phi(0)$ and $\phi'(0)$). \var{tab}[4] and
+ \var{tab}[5] contain the abscissas and weights corresponding to positive
+ $t=nh$ for $1\le n\le N$ and $h=1/2^m$ ($\phi(nh)$ and $\phi'(nh)$). Finally
+ \var{tab}[6] and \var{tab}[7] contain either the abscissas and weights
+ corresponding to negative $t=nh$ for $-N\le n\le -1$, or may be empty (but
+ not always) if $\phi(t)$ is an odd function (implicitly we would have
+ $\var{tab}[6]=-\var{tab}[4]$ and $\var{tab}[7]=\var{tab}[5]$).
diff --git a/src/functions/sums/intnuminitgen b/src/functions/sums/intnuminitgen
new file mode 100644
index 0000000..f4a91f1
--- /dev/null
+++ b/src/functions/sums/intnuminitgen
@@ -0,0 +1,18 @@
+Function: intnuminitgen
+Section: sums
+C-Name: intnuminitgen0
+Prototype: VGGED0,L,D0,L,p
+Help: intnuminitgen(t,a,b,ph,{m=0},{flag=0}): initialize tables for
+ integrations from a to b using abscissas ph(t) and weights ph'(t). Note that
+ there is no equal sign after the variable name t since t always goes from
+ -infty to +infty, but it is ph(t) which goes from a to b, and this is not
+ checked. If flag = 1 or 2, multiply the reserved table length by 4^flag, to
+ avoid corresponding error.
+Doc: initialize tables for integrations from $a$ to $b$ using abscissas
+ $ph(t)$ and weights $ph'(t)$. Note that there is no equal sign after the
+ variable name $t$ since $t$ always goes from $-\infty$ to $+\infty$, but it
+ is $ph(t)$ which goes from $a$ to $b$, and this is not checked. If \fl = 1
+ or 2, multiply the reserved table length by $4^{\fl}$, to avoid corresponding
+ error.
+
+ \synt{intnuminitgen}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, long m, long flag, long prec}
diff --git a/src/functions/sums/intnumromb b/src/functions/sums/intnumromb
new file mode 100644
index 0000000..43ca3ff
--- /dev/null
+++ b/src/functions/sums/intnumromb
@@ -0,0 +1,51 @@
+Function: intnumromb
+Section: sums
+C-Name: intnumromb0
+Prototype: V=GGED0,L,p
+Help: intnumromb(X=a,b,expr,{flag=0}): numerical integration of expr (smooth in
+ ]a,b[) from a to b with respect to X. flag is optional and mean 0: default.
+ expr can be evaluated exactly on [a,b]; 1: general function; 2: a or b can be
+ plus or minus infinity (chosen suitably), but of same sign; 3: expr has only
+ limits at a or b.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?small):gen:prec intnumromb(${3 cookie}, ${3 wrapper}, $1, $2, $4, prec)
+Doc: numerical integration of \var{expr} (smooth in $]a,b[$), with respect to
+ $X$. Suitable for low accuracy; if \var{expr} is very regular (e.g. analytic
+ in a large region) and high accuracy is desired, try \tet{intnum} first.
+
+ Set $\fl=0$ (or omit it altogether) when $a$ and $b$ are not too large, the
+ function is smooth, and can be evaluated exactly everywhere on the interval
+ $[a,b]$.
+
+ If $\fl=1$, uses a general driver routine for doing numerical integration,
+ making no particular assumption (slow).
+
+ $\fl=2$ is tailored for being used when $a$ or $b$ are infinite. One
+ \emph{must} have $ab>0$, and in fact if for example $b=+\infty$, then it is
+ preferable to have $a$ as large as possible, at least $a\ge1$.
+
+ If $\fl=3$, the function is allowed to be undefined (but continuous) at $a$
+ or $b$, for example the function $\sin(x)/x$ at $x=0$.
+
+ The user should not require too much accuracy: 18 or 28 decimal digits is OK,
+ but not much more. In addition, analytical cleanup of the integral must have
+ been done: there must be no singularities in the interval or at the
+ boundaries. In practice this can be accomplished with a simple change of
+ variable. Furthermore, for improper integrals, where one or both of the
+ limits of integration are plus or minus infinity, the function must decrease
+ sufficiently rapidly at infinity. This can often be accomplished through
+ integration by parts. Finally, the function to be integrated should not be
+ very small (compared to the current precision) on the entire interval. This
+ can of course be accomplished by just multiplying by an appropriate constant.
+
+ Note that \idx{infinity} can be represented with essentially no loss of
+ accuracy by an appropriate huge number. However beware of real underflow
+ when dealing with rapidly decreasing functions. For example, in order to
+ compute the $\int_0^\infty e^{-x^2}\,dx$ to 28 decimal digits, then one can
+ set infinity equal to 10 for example, and certainly not to \kbd{1e1000}.
+
+ \synt{intnumromb}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN b, long flag, long prec},
+ where $\kbd{eval}(x, E)$ returns the value of the function at $x$.
+ You may store any additional information required by \kbd{eval} in $E$, or set
+ it to \kbd{NULL}.
diff --git a/src/functions/sums/intnumstep b/src/functions/sums/intnumstep
new file mode 100644
index 0000000..1c82d3d
--- /dev/null
+++ b/src/functions/sums/intnumstep
@@ -0,0 +1,9 @@
+Function: intnumstep
+Section: sums
+C-Name: intnumstep
+Prototype: lp
+Help: intnumstep(): gives the default value of m used by all intnum and sumnum
+ routines, such that the integration step is 1/2^m.
+Doc: give the value of $m$ used in all the
+ \kbd{intnum} and \kbd{sumnum} programs, hence such that the integration
+ step is equal to $1/2^m$.
diff --git a/src/functions/sums/prod b/src/functions/sums/prod
new file mode 100644
index 0000000..d69ad2c
--- /dev/null
+++ b/src/functions/sums/prod
@@ -0,0 +1,39 @@
+Function: prod
+Section: sums
+C-Name: produit
+Prototype: V=GGEDG
+Help: prod(X=a,b,expr,{x=1}): x times the product (X runs from a to b) of
+ expression.
+Doc: product of expression
+ \var{expr}, initialized at $x$, the formal parameter $X$ going from $a$ to
+ $b$. As for \kbd{sum}, the main purpose of the initialization parameter $x$
+ is to force the type of the operations being performed. For example if it is
+ set equal to the integer 1, operations will start being done exactly. If it
+ is set equal to the real $1.$, they will be done using real numbers having
+ the default precision. If it is set equal to the power series $1+O(X^k)$ for
+ a certain $k$, they will be done using power series of precision at most $k$.
+ These are the three most common initializations.
+
+ \noindent As an extreme example, compare
+
+ \bprog
+ ? prod(i=1, 100, 1 - X^i);  \\@com this has degree $5050$ !!
+ time = 128 ms.
+ ? prod(i=1, 100, 1 - X^i, 1 + O(X^101))
+ time = 8 ms.
+ %2 = 1 - X - X^2 + X^5 + X^7 - X^12 - X^15 + X^22 + X^26 - X^35 - X^40 + \
+ X^51 + X^57 - X^70 - X^77 + X^92 + X^100 + O(X^101)
+ @eprog\noindent
+ Of course, in  this specific case, it is faster to use \tet{eta},
+ which is computed using Euler's formula.
+ \bprog
+ ? prod(i=1, 1000, 1 - X^i, 1 + O(X^1001));
+ time = 589 ms.
+ ? \ps1000
+ seriesprecision = 1000 significant terms
+ ? eta(X) - %
+ time = 8ms.
+ %4 = O(X^1001)
+ @eprog
+
+ \synt{produit}{GEN a, GEN b, char *expr, GEN x}.
diff --git a/src/functions/sums/prodeuler b/src/functions/sums/prodeuler
new file mode 100644
index 0000000..976b4ef
--- /dev/null
+++ b/src/functions/sums/prodeuler
@@ -0,0 +1,12 @@
+Function: prodeuler
+Section: sums
+C-Name: prodeuler0
+Prototype: V=GGEp
+Help: prodeuler(X=a,b,expr): Euler product (X runs over the primes between a
+ and b) of real or complex expression.
+Doc: product of expression \var{expr},
+ initialized at 1. (i.e.~to a \emph{real} number equal to 1 to the current
+ \kbd{realprecision}), the formal parameter $X$ ranging over the prime numbers
+ between $a$ and $b$.\sidx{Euler product}
+
+ \synt{prodeuler}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN b, long prec}.
diff --git a/src/functions/sums/prodinf b/src/functions/sums/prodinf
new file mode 100644
index 0000000..3b2bb4a
--- /dev/null
+++ b/src/functions/sums/prodinf
@@ -0,0 +1,20 @@
+Function: prodinf
+Section: sums
+C-Name: prodinf0
+Prototype: V=GED0,L,p
+Help: prodinf(X=a,expr,{flag=0}): infinite product (X goes from a to
+ infinity) of real or complex expression. flag can be 0 (default) or 1, in
+ which case compute the product of the 1+expr instead.
+Wrapper: (,G)
+Description:
+  (gen,gen,?small):gen:prec prodinf(${2 cookie}, ${2 wrapper}, $1, $3, prec)
+Doc: \idx{infinite product} of
+ expression \var{expr}, the formal parameter $X$ starting at $a$. The evaluation
+ stops when the relative error of the expression minus 1 is less than the
+ default precision. In particular, non-convergent products result in infinite
+ loops. The expressions must always evaluate to an element of $\C$.
+
+ If $\fl=1$, do the product of the ($1+\var{expr}$) instead.
+
+ \synt{prodinf}{void *E, GEN (*eval)(void*,GEN), GEN a, long prec}
+ ($\fl=0$), or \tet{prodinf1} with the same arguments ($\fl=1$).
diff --git a/src/functions/sums/solve b/src/functions/sums/solve
new file mode 100644
index 0000000..4fa89f4
--- /dev/null
+++ b/src/functions/sums/solve
@@ -0,0 +1,17 @@
+Function: solve
+Section: sums
+C-Name: zbrent0
+Prototype: V=GGEp
+Help: solve(X=a,b,expr): real root of expression expr (X between a and b),
+ where expr(a)*expr(b)<=0.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen):gen:prec zbrent(${3 cookie}, ${3 wrapper}, $1, $2, prec)
+Doc: find a real root of expression
+ \var{expr} between $a$ and $b$, under the condition
+ $\var{expr}(X=a) * \var{expr}(X=b) \le 0$. (You will get an error message
+ \kbd{roots must be bracketed in solve} if this does not hold.)
+ This routine uses Brent's method and can fail miserably if \var{expr} is
+ not defined in the whole of $[a,b]$ (try \kbd{solve(x=1, 2, tan(x))}).
+
+ \synt{zbrent}{void *E,GEN (*eval)(void*,GEN),GEN a,GEN b,long prec}.
diff --git a/src/functions/sums/sum b/src/functions/sums/sum
new file mode 100644
index 0000000..f967183
--- /dev/null
+++ b/src/functions/sums/sum
@@ -0,0 +1,22 @@
+Function: sum
+Section: sums
+C-Name: somme
+Prototype: V=GGEDG
+Help: sum(X=a,b,expr,{x=0}): x plus the sum (X goes from a to b) of
+ expression expr.
+Doc: sum of expression \var{expr},
+ initialized at $x$, the formal parameter going from $a$ to $b$. As for
+ \kbd{prod}, the initialization parameter $x$ may be given to force the type
+ of the operations being performed.
+
+ \noindent As an extreme example, compare
+
+ \bprog
+ ? sum(i=1, 10^4, 1/i); \\@com rational number: denominator has $4345$ digits.
+ time = 236 ms.
+ ? sum(i=1, 5000, 1/i, 0.)
+ time = 8 ms.
+ %2 = 9.787606036044382264178477904
+ @eprog
+
+ \synt{somme}{GEN a, GEN b, char *expr, GEN x}.
diff --git a/src/functions/sums/sumalt b/src/functions/sums/sumalt
new file mode 100644
index 0000000..a1b876b
--- /dev/null
+++ b/src/functions/sums/sumalt
@@ -0,0 +1,53 @@
+Function: sumalt
+Section: sums
+C-Name: sumalt0
+Prototype: V=GED0,L,p
+Help: sumalt(X=a,expr,{flag=0}): Cohen-Villegas-Zagier's acceleration of
+ alternating series expr, X starting at a. flag is optional, and can be 0:
+ default, or 1: uses a slightly different method using Zagier's polynomials.
+Wrapper: (,G)
+Description:
+  (gen,gen,?0):gen:prec sumalt(${2 cookie}, ${2 wrapper}, $1, prec)
+  (gen,gen,1):gen:prec sumalt2(${2 cookie}, ${2 wrapper}, $1, prec)
+Doc: numerical summation of the series \var{expr}, which should be an
+ \idx{alternating series}, the formal variable $X$ starting at $a$. Use an
+ algorithm of Cohen, Villegas and Zagier (\emph{Experiment. Math.} {\bf 9}
+ (2000), no.~1, 3--12).
+
+ If $\fl=1$, use a variant with slightly different polynomials. Sometimes
+ faster.
+
+ The routine is heuristic and a rigorous proof assumes that the values of
+ \var{expr} are the moments of a positive measure on $[0,1]$. Divergent
+ alternating series can sometimes be summed by this method, as well as series
+ which are not exactly alternating (see for example
+ \secref{se:user_defined}). It should be used to try and guess the value of
+ an infinite sum. (However, see the example at the end of
+ \secref{se:userfundef}.)
+
+ If the series already converges geometrically,
+ \tet{suminf} is often a better choice:
+ \bprog
+ ? \p28
+ ? sumalt(i = 1, -(-1)^i / i)  - log(2)
+ time = 0 ms.
+ %1 = -2.524354897 E-29
+ ? suminf(i = 1, -(-1)^i / i)   \\@com Had to hit <C-C>
+   ***   at top-level: suminf(i=1,-(-1)^i/i)
+   ***                                ^------
+   *** suminf: user interrupt after 10min, 20,100 ms.
+ ? \p1000
+ ? sumalt(i = 1, -(-1)^i / i)  - log(2)
+ time = 90 ms.
+ %2 = 4.459597722 E-1002
+
+ ? sumalt(i = 0, (-1)^i / i!) - exp(-1)
+ time = 670 ms.
+ %3 = -4.03698781490633483156497361352190615794353338591897830587 E-944
+ ? suminf(i = 0, (-1)^i / i!) - exp(-1)
+ time = 110 ms.
+ %4 = -8.39147638 E-1000   \\ @com faster and more accurate
+ @eprog
+
+ \synt{sumalt}{void *E, GEN (*eval)(void*,GEN),GEN a,long prec}. Also
+ available is \tet{sumalt2} with the same arguments ($\fl = 1$).
diff --git a/src/functions/sums/sumdiv b/src/functions/sums/sumdiv
new file mode 100644
index 0000000..799919c
--- /dev/null
+++ b/src/functions/sums/sumdiv
@@ -0,0 +1,14 @@
+Function: sumdiv
+Section: sums
+C-Name: sumdivexpr
+Prototype: GVE
+Help: sumdiv(n,X,expr): sum of expression expr, X running over the divisors
+ of n.
+Doc: sum of expression \var{expr} over the positive divisors of $n$.
+ This function is a trivial wrapper essentially equivalent to
+ \bprog
+   D = divisors(n);
+   for (i = 1, #D, X = D[i]; eval(expr))
+ @eprog\noindent (except that \kbd{X} is lexically scoped to the \kbd{sumdiv}
+ loop). If \var{expr} is a multiplicative function, use \tet{sumdivmult}.
+ %\syn{NO}
diff --git a/src/functions/sums/sumdivmult b/src/functions/sums/sumdivmult
new file mode 100644
index 0000000..948e3e2
--- /dev/null
+++ b/src/functions/sums/sumdivmult
@@ -0,0 +1,11 @@
+Function: sumdivmult
+Section: sums
+C-Name: sumdivmultexpr
+Prototype: GVE
+Help: sumdivmult(n,d,expr): sum of multiplicative function expr,
+ d running over the divisors of n.
+Doc: sum of \emph{multiplicative} expression \var{expr} over the positive
+ divisors $d$ of $n$. Assume that \var{expr} evaluates to $f(d)$
+ where $f$ is multiplicative: $f(1) = 1$ and $f(ab) = f(a)f(b)$ for coprime
+ $a$ and $b$.
+ %\syn{NO}
diff --git a/src/functions/sums/suminf b/src/functions/sums/suminf
new file mode 100644
index 0000000..ebf8090
--- /dev/null
+++ b/src/functions/sums/suminf
@@ -0,0 +1,32 @@
+Function: suminf
+Section: sums
+C-Name: suminf0
+Prototype: V=GEp
+Help: suminf(X=a,expr): infinite sum (X goes from a to infinity) of real or
+ complex expression expr.
+Wrapper: (,G)
+Description:
+  (gen,gen):gen:prec suminf(${2 cookie}, ${2 wrapper}, $1, prec)
+Doc: \idx{infinite sum} of expression
+ \var{expr}, the formal parameter $X$ starting at $a$. The evaluation stops
+ when the relative error of the expression is less than the default precision
+ for 3 consecutive evaluations. The expressions must always evaluate to a
+ complex number.
+
+ If the series converges slowly, make sure \kbd{realprecision} is low (even 28
+ digits may be too much). In this case, if the series is alternating or the
+ terms have a constant sign, \tet{sumalt} and \tet{sumpos} should be used
+ instead.
+
+ \bprog
+ ? \p28
+ ? suminf(i = 1, -(-1)^i / i)   \\@com Had to hit <C-C>
+   ***   at top-level: suminf(i=1,-(-1)^i/i)
+   ***                                ^------
+   *** suminf: user interrupt after 10min, 20,100 ms.
+ ? sumalt(i = 1, -(-1)^i / i) - log(2)
+ time = 0 ms.
+ %1 = -2.524354897 E-29
+ @eprog
+
+ \synt{suminf}{void *E, GEN (*eval)(void*,GEN), GEN a, long prec}.
diff --git a/src/functions/sums/sumnum b/src/functions/sums/sumnum
new file mode 100644
index 0000000..cefeea9
--- /dev/null
+++ b/src/functions/sums/sumnum
@@ -0,0 +1,126 @@
+Function: sumnum
+Section: sums
+C-Name: sumnum0
+Prototype: V=GGEDGD0,L,p
+Help: sumnum(X=a,sig,expr,{tab},{flag=0}): numerical summation of expr from
+ X = ceiling(a) to +infinity. sig is either a scalar or a two-component vector
+ coding the function's decrease rate at infinity. It is assumed that the
+ scalar part of sig is to the right of all poles of expr. If present, tab
+ must be initialized by sumnuminit. If flag is nonzero, assumes that
+ conj(expr(z)) = expr(conj(z)).
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?gen,?small):gen:prec sumnum(${3 cookie}, ${3 wrapper}, $1, $2, $4, $5, prec)
+Doc: numerical summation of \var{expr}, the variable $X$ taking integer values
+ from ceiling of $a$ to $+\infty$, where \var{expr} is assumed to be a
+ holomorphic function $f(X)$ for $\Re(X)\ge \sigma$.
+
+ The parameter $\sigma\in\R$ is coded in the argument \kbd{sig} as follows: it
+ is either
+
+ \item a real number $\sigma$. Then the function $f$ is assumed to
+ decrease at least as $1/X^2$ at infinity, but not exponentially;
+
+ \item a two-component vector $[\sigma,\alpha]$, where $\sigma$ is as
+ before, $\alpha < -1$. The function $f$ is assumed to decrease like
+ $X^{\alpha}$. In particular, $\alpha\le-2$ is equivalent to no $\alpha$ at all.
+
+ \item a two-component vector $[\sigma,\alpha]$, where $\sigma$ is as
+ before, $\alpha > 0$. The function $f$ is assumed to decrease like
+ $\exp(-\alpha X)$. In this case it is essential that $\alpha$ be exactly the
+ rate of exponential decrease, and it is usually a good idea to increase
+ the default value of $m$ used for the integration step. In practice, if
+ the function is exponentially decreasing \kbd{sumnum} is slower and less
+ accurate than \kbd{sumpos} or \kbd{suminf}, so should not be used.
+
+ The function uses the \tet{intnum} routines and integration on the line
+ $\Re(s) = \sigma$. The optional argument \var{tab} is as in intnum, except it
+ must be initialized with \kbd{sumnuminit} instead of \kbd{intnuminit}.
+
+ When \var{tab} is not precomputed, \kbd{sumnum} can be slower than
+ \kbd{sumpos}, when the latter is applicable. It is in general faster for
+ slowly decreasing functions.
+
+
+ Finally, if $\fl$ is nonzero, we assume that the function $f$ to be summed is
+ of real type, i.e. satisfies $\overline{f(z)}=f(\overline{z})$, which
+ speeds up the computation.
+
+ \bprog
+ ? \p 308
+ ? a = sumpos(n=1, 1/(n^3+n+1));
+ time = 1,410 ms.
+ ? tab = sumnuminit(2);
+ time = 1,620 ms. \\@com slower but done once and for all.
+ ? b = sumnum(n=1, 2, 1/(n^3+n+1), tab);
+ time = 460 ms. \\@com 3 times as fast as \kbd{sumpos}
+ ? a - b
+ %4 = -1.0... E-306 + 0.E-320*I \\@com perfect.
+ ? sumnum(n=1, 2, 1/(n^3+n+1), tab, 1) - a; \\@com function of real type
+ time = 240 ms.
+ %2 = -1.0... E-306 \\@com twice as fast, no imaginary part.
+ ? c = sumnum(n=1, 2, 1/(n^2+1), tab, 1);
+ time = 170 ms. \\@com fast
+ ? d = sumpos(n=1, 1 / (n^2+1));
+ time = 2,700 ms. \\@com slow.
+ ? d - c
+ time = 0 ms.
+ %5 = 1.97... E-306 \\@com perfect.
+ @eprog
+
+ For slowly decreasing function, we must indicate singularities:
+ \bprog
+ ? \p 308
+ ? a = sumnum(n=1, 2, n^(-4/3));
+ time = 9,930 ms. \\@com slow because of the computation of $n^{-4/3}$.
+ ? a - zeta(4/3)
+ time = 110 ms.
+ %1 = -2.42... E-107 \\@com lost 200 decimals because of singularity at $\infty$
+ ? b = sumnum(n=1, [2,-4/3], n^(-4/3), /*omitted*/, 1); \\@com of real type
+ time = 12,210 ms.
+ ? b - zeta(4/3)
+ %3 = 1.05... E-300 \\@com better
+ @eprog
+
+ Since the \emph{complex} values of the function are used, beware of
+ determination problems. For instance:
+ \bprog
+ ? \p 308
+ ? tab = sumnuminit([2,-3/2]);
+ time = 1,870 ms.
+ ? sumnum(n=1,[2,-3/2], 1/(n*sqrt(n)), tab,1) - zeta(3/2)
+ time = 690 ms.
+ %1 = -1.19... E-305 \\@com fast and correct
+ ? sumnum(n=1,[2,-3/2], 1/sqrt(n^3), tab,1) - zeta(3/2)
+ time = 730 ms.
+ %2 = -1.55... \\@com nonsense. However
+ ? sumnum(n=1,[2,-3/2], 1/n^(3/2), tab,1) - zeta(3/2)
+ time = 8,990 ms.
+ %3 = -1.19... E-305 \\@com perfect, as $1/(n*\sqrt{n})$ above but much slower
+ @eprog
+
+ For exponentially decreasing functions, \kbd{sumnum} is given for
+ completeness, but one of \tet{suminf} or \tet{sumpos} should always be
+ preferred. If you experiment with such functions and \kbd{sumnum} anyway,
+ indicate the exact rate of decrease and increase $m$ by $1$ or $2$:
+
+ \bprog
+ ? suminf(n=1, 2^(-n)) - 1
+ time = 10 ms.
+ %1 = -1.11... E-308 \\@com fast and perfect
+ ? sumpos(n=1, 2^(-n)) - 1
+ time = 10 ms.
+ %2 = -2.78... E-308 \\@com also fast and perfect
+ ? sumnum(n=1,2, 2^(-n)) - 1
+ %3 = -1.321115060 E320 + 0.E311*I \\@com nonsense
+ ? sumnum(n=1, [2,log(2)], 2^(-n), /*omitted*/, 1) - 1 \\@com of real type
+ time = 5,860 ms.
+ %4 = -1.5... E-236 \\@com slow and lost $70$ decimals
+ ? m = intnumstep()
+ %5 = 9
+ ? sumnum(n=1,[2,log(2)], 2^(-n), m+1, 1) - 1
+ time = 11,770 ms.
+ %6 = -1.9... E-305 \\@com now perfect, but slow.
+ @eprog
+
+ \synt{sumnum}{void *E, GEN (*eval)(void*,GEN), GEN a,GEN sig,GEN tab,long flag, long prec}.
diff --git a/src/functions/sums/sumnumalt b/src/functions/sums/sumnumalt
new file mode 100644
index 0000000..00e9487
--- /dev/null
+++ b/src/functions/sums/sumnumalt
@@ -0,0 +1,53 @@
+Function: sumnumalt
+Section: sums
+C-Name: sumnumalt0
+Prototype: V=GGEDGD0,L,p
+Help: sumnumalt(X=a,sig,expr,{tab},{flag=0}): numerical summation of (-1)^X
+ expr(X)
+ from X = ceiling(a) to +infinity. Note that the (-1)^X must not be included.
+ sig is either a scalar or a two-component vector coded as in intnum, and the
+ scalar part is larger than all the real parts of the poles of expr. Uses intnum,
+ hence tab is as in intnum. If flag is nonzero, assumes that the function to
+ be summed satisfies conj(f(z))=f(conj(z)), and then up to twice faster.
+Wrapper: (,,G)
+Description:
+  (gen,gen,gen,?gen,?small):gen:prec sumnumalt(${3 cookie}, ${3 wrapper}, $1, $2, $4, $5, prec)
+Doc: numerical
+ summation of $(-1)^X\var{expr}(X)$, the variable $X$ taking integer values from
+ ceiling of $a$ to $+\infty$, where \var{expr} is assumed to be a holomorphic
+ function for $\Re(X)\ge sig$ (or $sig[1]$).
+
+ \misctitle{Warning} This function uses the \kbd{intnum} routines and is
+ orders of magnitude slower than \kbd{sumalt}. It is only given for
+ completeness and should not be used in practice.
+
+ \misctitle{Warning 2} The expression \var{expr} must \emph{not} include the
+ $(-1)^X$ coefficient. Thus $\kbd{sumalt}(n=a,(-1)^nf(n))$ is (approximately)
+ equal to $\kbd{sumnumalt}(n=a,sig,f(n))$.
+
+ $sig$ is coded as in \kbd{sumnum}. However for slowly decreasing functions
+ (where $sig$ is coded as $[\sigma,\alpha]$ with $\alpha<-1$), it is not
+ really important to indicate $\alpha$. In fact, as for \kbd{sumalt}, the
+ program will often give meaningful results (usually analytic continuations)
+ even for divergent series. On the other hand the exponential decrease must be
+ indicated.
+
+ \var{tab} is as in \kbd{intnum}, but if used must be initialized with
+ \kbd{sumnuminit}. If $\fl$ is nonzero, assumes that the function $f$ to be
+ summed is of real type, i.e. satisfies $\overline{f(z)}=f(\overline{z})$, and
+ then twice faster when \var{tab} is precomputed.
+
+ \bprog
+ ? \p 308
+ ? tab = sumnuminit(2, /*omitted*/, -1); \\@com abscissa $\sigma=2$, alternating sums.
+ time = 1,620 ms. \\@com slow, but done once and for all.
+ ? a = sumnumalt(n=1, 2, 1/(n^3+n+1), tab, 1);
+ time = 230 ms. \\@com similar speed to \kbd{sumnum}
+ ? b = sumalt(n=1, (-1)^n/(n^3+n+1));
+ time = 0 ms. \\@com infinitely faster!
+ ? a - b
+ time = 0 ms.
+ %1 = -1.66... E-308 \\@com perfect
+ @eprog
+
+ \synt{sumnumalt}{void *E, GEN (*eval)(void*,GEN), GEN a, GEN sig, GEN tab, long flag, long prec}.
diff --git a/src/functions/sums/sumnuminit b/src/functions/sums/sumnuminit
new file mode 100644
index 0000000..5898c99
--- /dev/null
+++ b/src/functions/sums/sumnuminit
@@ -0,0 +1,12 @@
+Function: sumnuminit
+Section: sums
+C-Name: sumnuminit
+Prototype: GD0,L,D1,L,p
+Help: sumnuminit(sig, {m=0}, {sgn=1}): initialize tables for numerical
+ summation. sgn is 1 (in fact >= 0), the default, for sumnum (ordinary sums)
+ or -1 (in fact < 0) for sumnumalt (alternating sums). sig is as in sumnum and
+ m is as in intnuminit.
+Doc: initialize tables for numerical summation using \kbd{sumnum} (with
+ $\var{sgn}=1$) or \kbd{sumnumalt} (with $\var{sgn}=-1$), $sig$ is the
+ abscissa of integration coded as in \kbd{sumnum}, and $m$ is as in
+ \kbd{intnuminit}.
diff --git a/src/functions/sums/sumpos b/src/functions/sums/sumpos
new file mode 100644
index 0000000..484d710
--- /dev/null
+++ b/src/functions/sums/sumpos
@@ -0,0 +1,29 @@
+Function: sumpos
+Section: sums
+C-Name: sumpos0
+Prototype: V=GED0,L,p
+Help: sumpos(X=a,expr,{flag=0}): sum of positive (or negative) series expr,
+ the formal
+ variable X starting at a. flag is optional, and can be 0: default, or 1:
+ uses a slightly different method using Zagier's polynomials.
+Wrapper: (,G)
+Description:
+  (gen,gen,?0):gen:prec sumpos(${2 cookie}, ${2 wrapper}, $1, prec)
+  (gen,gen,1):gen:prec sumpos2(${2 cookie}, ${2 wrapper}, $1, prec)
+Doc: numerical summation of the series \var{expr}, which must be a series of
+ terms having the same sign, the formal variable $X$ starting at $a$. The
+ algorithm used is Van Wijngaarden's trick for converting such a series into
+ an alternating one, then we use \tet{sumalt}. For regular functions, the
+ function \kbd{sumnum} is in general much faster once the initializations
+ have been made using \kbd{sumnuminit}.
+
+ The routine is heuristic and assumes that \var{expr} is more or less a
+ decreasing function of $X$. In particular, the result will be completely
+ wrong if \var{expr} is 0 too often. We do not check either that all terms
+ have the same sign. As \tet{sumalt}, this function should be used to
+ try and guess the value of an infinite sum.
+
+ If $\fl=1$, use slightly different polynomials. Sometimes faster.
+
+ \synt{sumpos}{void *E, GEN (*eval)(void*,GEN),GEN a,long prec}. Also
+ available is \tet{sumpos2} with the same arguments ($\fl = 1$).
diff --git a/src/functions/symbolic_operators/add b/src/functions/symbolic_operators/add
new file mode 100644
index 0000000..f93f627
--- /dev/null
+++ b/src/functions/symbolic_operators/add
@@ -0,0 +1,22 @@
+Function: _+_
+Section: symbolic_operators
+C-Name: gadd
+Prototype: GG
+Help: x+y: sum of x and y.
+Description:
+ (lg, 1):small:parens            $(1)
+ (small, small):small:parens     $(1) + $(2)
+ (lg, small):lg:parens           $(1) + $(2)
+ (small, lg):lg:parens           $(1) + $(2)
+ (int, small):int                addis($1, $2)
+ (small, int):int                addsi($1, $2)
+ (int, int):int                  addii($1, $2)
+ (real, small):real              addrs($1, $2)
+ (small, real):real              addsr($1, $2)
+ (real, real):real               addrr($1, $2)
+ (mp, real):real                 mpadd($1, $2)
+ (real, mp):real                 mpadd($1, $2)
+ (mp, mp):mp                     mpadd($1, $2)
+ (gen, small):gen                gaddgs($1, $2)
+ (small, gen):gen                gaddsg($1, $2)
+ (gen, gen):gen                  gadd($1, $2)
diff --git a/src/functions/symbolic_operators/adde b/src/functions/symbolic_operators/adde
new file mode 100644
index 0000000..c2113d0
--- /dev/null
+++ b/src/functions/symbolic_operators/adde
@@ -0,0 +1,19 @@
+Function: _+=_
+C-Name: gadde
+Prototype: &G
+Help: x+=y: shortcut for x=x+y.
+Section: symbolic_operators
+Description:
+ (*small, small):small:parens             $1 += $(2)
+ (*lg, small):lg:parens                   $1 += $(2)
+ (*int, small):int:parens                 $1 = addis($1, $2)
+ (*int, int):int:parens                   $1 = addii($1, $2)
+ (*real, small):real:parens               $1 = addrs($1, $2)
+ (*real, int):real:parens                 $1 = addir($2, $1)
+ (*real, real):real:parens                $1 = addrr($1, $2)
+ (*mp, mp):mp:parens                      $1 = mpadd($1, $2)
+ (*pol, small):gen:parens                 $1 = gaddgs($1, $2)
+ (*pol, gen):gen:parens                   $1 = gadd($1, $2)
+ (*vec, gen):gen:parens                   $1 = gadd($1, $2)
+ (*gen, small):gen:parens                 $1 = gaddgs($1, $2)
+ (*gen, gen):gen:parens                   $1 = gadd($1, $2)
diff --git a/src/functions/symbolic_operators/and b/src/functions/symbolic_operators/and
new file mode 100644
index 0000000..93bc16c
--- /dev/null
+++ b/src/functions/symbolic_operators/and
@@ -0,0 +1,7 @@
+Function: _&&_
+C-Name: andpari
+Prototype: GE
+Help: _&&_
+Section: symbolic_operators
+Description:
+ (bool, bool):bool:parens               $(1) && $(2)
diff --git a/src/functions/symbolic_operators/call b/src/functions/symbolic_operators/call
new file mode 100644
index 0000000..a8cdb82
--- /dev/null
+++ b/src/functions/symbolic_operators/call
@@ -0,0 +1,8 @@
+Function: _(_)
+Class: symbolic_operators
+Help: f(a,b,...): evaluates the function f on a,b,...
+Description:
+ (gen):gen          closure_callgenall($1, 0)
+ (gen,gen):gen      closure_callgen1($1, $2)
+ (gen,gen,gen):gen  closure_callgen2($1, $2, $3)
+ (gen,gen,...):gen  closure_callgenall($1, ${nbarg 1 sub}, $3)
diff --git a/src/functions/symbolic_operators/coeff b/src/functions/symbolic_operators/coeff
new file mode 100644
index 0000000..59b4be2
--- /dev/null
+++ b/src/functions/symbolic_operators/coeff
@@ -0,0 +1,29 @@
+Function: _[_,_]
+Class: symbolic_operators
+Description:
+ (mp,small):gen                 $"Scalar has no components"
+ (mp,small,small):gen           $"Scalar has no components"
+ (vecsmall,small):small         $(1)[$2]
+ (vecsmall,small,small):gen     $"Vecsmall are single-dimensional"
+ (list,small):gen:copy          gel(list_data($1), $2)
+ (vec,small):gen:copy           gel($1, $2)
+ (vec,small,small):gen:copy     gcoeff($1, $2, $3)
+ (gen,small):gen:copy           gel($1, $2)
+ (gen,small,small):gen:copy     gcoeff($1, $2, $3)
+
+Function: _safecoeff
+Class: symbolic_operators
+Help: safe version of x[a], x[,a] and x[a,b]. Must be lvalues.
+Description:
+ (vecsmall,small):small         *safeel($1, $2)
+ (list,small):gen:copy          *safelistel($1, $2)
+ (gen,small):gen:copy           *safegel($1, $2)
+ (gen,small,small):gen:copy     *safegcoeff($1, $2, $3)
+
+Function: _[_,]
+Class: symbolic_operators
+Help: x[y,]: y-th row of x.
+Description:
+ (mp,small):gen                 $"Scalar has no rows"
+ (vec,small):vec                rowcopy($1, $2)
+ (gen,small):vec                rowcopy($1, $2)
diff --git a/src/functions/symbolic_operators/compr b/src/functions/symbolic_operators/compr
new file mode 100644
index 0000000..5c74edc
--- /dev/null
+++ b/src/functions/symbolic_operators/compr
@@ -0,0 +1,20 @@
+Function: [_|_<-_,_;_]
+Section: programming/internals
+C-Name: vecexpr1
+Prototype: mGVDEDE
+Help: [a(x)|x<-b,c(x);...]
+Wrapper: (,,G,bG)
+Description:
+ (gen,,closure):gen         veccatapply(${3 cookie}, ${3 wrapper}, $1)
+ (gen,,closure,closure):gen veccatselapply(${4 cookie}, ${4 wrapper}, ${3 cookie}, ${3 wrapper}, $1)
+
+Function: [_|_<-_,_]
+Section: programming/internals
+C-Name: vecexpr0
+Prototype: GVDEDE
+Help: [a(x)|x<-b,c(x)] = apply(a,select(c,b))
+Wrapper: (,,G,bG)
+Description:
+ (gen,,closure):gen         vecapply(${3 cookie}, ${3 wrapper}, $1)
+ (gen,,,closure):gen        vecselect(${4 cookie}, ${4 wrapper}, $1)
+ (gen,,closure,closure):gen vecselapply(${4 cookie}, ${4 wrapper}, ${3 cookie}, ${3 wrapper}, $1)
diff --git a/src/functions/symbolic_operators/concat b/src/functions/symbolic_operators/concat
new file mode 100644
index 0000000..300f866
--- /dev/null
+++ b/src/functions/symbolic_operators/concat
@@ -0,0 +1,8 @@
+Function: __
+Help: __
+Section: symbolic_operators
+Description:
+ (genstr, genstr):genstr                concat($1, $2)
+ (genstr, gen):genstr                   concat($1, $2)
+ (gen, genstr):genstr                   concat($1, $2)
+ (gen, gen):genstr                      concat($genstr:1, $2)
diff --git a/src/functions/symbolic_operators/deriv b/src/functions/symbolic_operators/deriv
new file mode 100644
index 0000000..d97bd81
--- /dev/null
+++ b/src/functions/symbolic_operators/deriv
@@ -0,0 +1,7 @@
+Function: _'
+Section: symbolic_operators
+C-Name: deriv
+Prototype: GDn
+Help: x': derivative of x with respect to the main variable.
+Description:
+ (gen):gen                      deriv($1,-1)
diff --git a/src/functions/symbolic_operators/div b/src/functions/symbolic_operators/div
new file mode 100644
index 0000000..0370f60
--- /dev/null
+++ b/src/functions/symbolic_operators/div
@@ -0,0 +1,18 @@
+Function: _/_
+Section: symbolic_operators
+C-Name: gdiv
+Prototype: GG
+Help: x/y: quotient of x and y.
+Description:
+ (0, mp):small                   ($2, 0)/*for side effect*/
+ (1, real):real                  invr($2)
+ (#small, real):real             divsr($1, $2)
+ (small, real):mp                divsr($1, $2)
+ (real, small):real              divrs($1, $2)
+ (real, real):real               divrr($1, $2)
+ (real, mp):real                 mpdiv($1, $2)
+ (mp, real):mp                   mpdiv($1, $2)
+ (1, gen):gen                    ginv($2)
+ (gen, small):gen                gdivgs($1, $2)
+ (small, gen):gen                gdivsg($1, $2)
+ (gen, gen):gen                  gdiv($1, $2)
diff --git a/src/functions/symbolic_operators/dive b/src/functions/symbolic_operators/dive
new file mode 100644
index 0000000..3f61416
--- /dev/null
+++ b/src/functions/symbolic_operators/dive
@@ -0,0 +1,16 @@
+Function: _/=_
+Section: symbolic_operators
+C-Name: gdive
+Prototype: &G
+Help: x/=y: shortcut for x=x/y.
+Description:
+ (*small, gen):void                $"cannot divide small: use \= instead."
+ (*int, gen):void                  $"cannot divide int: use \= instead."
+ (*real, real):real:parens               $1 = divrr($1, $2)
+ (*real, small):real:parens              $1 = divrs($1, $2)
+ (*real, mp):real:parens                 $1 = mpdiv($1, $2)
+ (*mp, real):mp:parens                   $1 = mpdiv($1, $2)
+ (*pol, gen):gen:parens                  $1 = gdiv($1, $2)
+ (*vec, gen):gen:parens                  $1 = gdiv($1, $2)
+ (*gen, small):gen:parens                $1 = gdivgs($1, $2)
+ (*gen, gen):gen:parens                  $1 = gdiv($1, $2)
diff --git a/src/functions/symbolic_operators/divent b/src/functions/symbolic_operators/divent
new file mode 100644
index 0000000..31f7a6e
--- /dev/null
+++ b/src/functions/symbolic_operators/divent
@@ -0,0 +1,13 @@
+Function: _\_
+Section: symbolic_operators
+C-Name: gdivent
+Help: x\y: Euclidean quotient of x and y.
+Prototype: GG
+Description:
+ (small, small):small:parens             $(1)/$(2)
+ (int, small):int                        truedivis($1, $2)
+ (small, int):int                        gdiventsg($1, $2)
+ (int, int):int                          truedivii($1, $2)
+ (gen, small):gen                        gdiventgs($1, $2)
+ (small, gen):gen                        gdiventsg($1, $2)
+ (gen, gen):gen                          gdivent($1, $2)
diff --git a/src/functions/symbolic_operators/divente b/src/functions/symbolic_operators/divente
new file mode 100644
index 0000000..28c4d14
--- /dev/null
+++ b/src/functions/symbolic_operators/divente
@@ -0,0 +1,10 @@
+Function: _\=_
+Section: symbolic_operators
+C-Name: gdivente
+Prototype: &G
+Help: x\=y: shortcut for x=x\y.
+Description:
+ (*small, small):small:parens                   $1 /= $(2)
+ (*int, int):int:parens                         $1 = gdivent($1, $2)
+ (*pol, gen):gen:parens                         $1 = gdivent($1, $2)
+ (*gen, gen):gen:parens                         $1 = gdivent($1, $2)
diff --git a/src/functions/symbolic_operators/divround b/src/functions/symbolic_operators/divround
new file mode 100644
index 0000000..7f64055
--- /dev/null
+++ b/src/functions/symbolic_operators/divround
@@ -0,0 +1,8 @@
+Function: _\/_
+Help: x\/y: rounded Euclidean quotient of x and y.
+Section: symbolic_operators
+C-Name: gdivround
+Prototype: GG
+Description:
+ (int, int):int                        gdivround($1, $2)
+ (gen, gen):gen                        gdivround($1, $2)
diff --git a/src/functions/symbolic_operators/divrounde b/src/functions/symbolic_operators/divrounde
new file mode 100644
index 0000000..e1eeecc
--- /dev/null
+++ b/src/functions/symbolic_operators/divrounde
@@ -0,0 +1,9 @@
+Function: _\/=_
+Section: symbolic_operators
+C-Name: gdivrounde
+Prototype: &G
+Help: x\/=y: shortcut for x=x\/y.
+Description:
+ (*int, int):int:parens                         $1 = gdivround($1, $2)
+ (*pol, gen):gen:parens                         $1 = gdivround($1, $2)
+ (*gen, gen):gen:parens                         $1 = gdivround($1, $2)
diff --git a/src/functions/symbolic_operators/eq b/src/functions/symbolic_operators/eq
new file mode 100644
index 0000000..0a17ef4
--- /dev/null
+++ b/src/functions/symbolic_operators/eq
@@ -0,0 +1,29 @@
+Function: _==_
+Help: _==_
+Section: symbolic_operators
+C-Name: geq
+Prototype: GG
+Description:
+ (small, small):bool:parens             $(1) == $(2)
+ (lg, lg):bool:parens                   $(1) == $(2)
+ (small, int):bool:parens               cmpsi($1, $2) == 0
+ (mp, 0):bool                           !signe($1)
+ (int, 1):bool                          equali1($1)
+ (int, -1):bool                         equalim1($1)
+ (int, small):bool:parens               cmpis($1, $2) == 0
+ (int, int):bool                        equalii($1, $2)
+ (gen, 0):bool                          gequal0($1)
+ (gen, 1):bool                          gequal1($1)
+ (gen, -1):bool                         gequalm1($1)
+ (real,real):bool                       cmprr($1, $2) == 0
+ (mp, mp):bool:parens                   mpcmp($1, $2) == 0
+ (errtyp, errtyp):bool:parens           $(1) == $(2)
+ (errtyp, #str):bool:parens             $(1) == $(errtyp:2)
+ (#str, errtyp):bool:parens             $(errtyp:1) == $(2)
+ (typ, typ):bool:parens                 $(1) == $(2)
+ (typ, #str):bool:parens                $(1) == $(typ:2)
+ (#str, typ):bool:parens                $(typ:1) == $(2)
+ (str, str):negbool                     strcmp($1, $2)
+ (small, gen):bool                      gequalsg($1, $2)
+ (gen, small):bool                      gequalgs($1, $2)
+ (gen, gen):bool                        gequal($1, $2)
diff --git a/src/functions/symbolic_operators/fact b/src/functions/symbolic_operators/fact
new file mode 100644
index 0000000..d7ad5a0
--- /dev/null
+++ b/src/functions/symbolic_operators/fact
@@ -0,0 +1,7 @@
+Function: _!
+Section: symbolic_operators
+C-Name: mpfact
+Prototype: L
+Help: n!: factorial of n.
+Description:
+ (small):int                        mpfact($1)
diff --git a/src/functions/symbolic_operators/ge b/src/functions/symbolic_operators/ge
new file mode 100644
index 0000000..fdc27c5
--- /dev/null
+++ b/src/functions/symbolic_operators/ge
@@ -0,0 +1,17 @@
+Function: _>=_
+Help: x>=y: return 1 if x is greater or equal to y, 0 otherwise.
+Section: symbolic_operators
+C-Name: gge
+Prototype: GG
+Description:
+ (small, small):bool:parens              $(1) >= $(2)
+ (lg, lg):bool:parens                    $(1) >= $(2)
+ (lg, small):bool:parens                 $(1) > $(2)
+ (small, int):bool:parens                cmpsi($1, $2) >= 0
+ (int, small):bool:parens                cmpis($1, $2) >= 0
+ (int, int):bool:parens                  cmpii($1, $2) >= 0
+ (mp, mp):bool:parens                    mpcmp($1, $2) >= 0
+ (str, str):bool:parens                  strcmp($1, $2) >= 0
+ (small, gen):bool:parens                gcmpsg($1, $2) >= 0
+ (gen, small):bool:parens                gcmpgs($1, $2) >= 0
+ (gen, gen):bool:parens                  gcmp($1, $2) >= 0
diff --git a/src/functions/symbolic_operators/gt b/src/functions/symbolic_operators/gt
new file mode 100644
index 0000000..aabe2ed
--- /dev/null
+++ b/src/functions/symbolic_operators/gt
@@ -0,0 +1,17 @@
+Function: _>_
+Help: x>y: return 1 if x is strictly greater than y, 0 otherwise.
+Section: symbolic_operators
+C-Name: ggt
+Prototype: GG
+Description:
+ (small, small):bool:parens              $(1) > $(2)
+ (lg, lg):bool:parens                    $(1) > $(2)
+ (small, lg):bool:parens                 $(1) >= $(2)
+ (small, int):bool:parens                cmpsi($1, $2) > 0
+ (int, small):bool:parens                cmpis($1, $2) > 0
+ (int, int):bool:parens                  cmpii($1, $2) > 0
+ (mp, mp):bool:parens                    mpcmp($1, $2) > 0
+ (str, str):bool:parens                  strcmp($1, $2) > 0
+ (small, gen):bool:parens                gcmpsg($1, $2) > 0
+ (gen, small):bool:parens                gcmpgs($1, $2) > 0
+ (gen, gen):bool:parens                  gcmp($1, $2) > 0
diff --git a/src/functions/symbolic_operators/hist b/src/functions/symbolic_operators/hist
new file mode 100644
index 0000000..2be08d6
--- /dev/null
+++ b/src/functions/symbolic_operators/hist
@@ -0,0 +1,11 @@
+Function: %
+C-Name: pari_get_hist
+Prototype: D0,L,
+Section: symbolic_operators
+Help: last history item.
+
+Function: %#
+C-Name: pari_get_histtime
+Prototype: lD0,L,
+Section: symbolic_operators
+Help: time to compute last history item.
diff --git a/src/functions/symbolic_operators/id b/src/functions/symbolic_operators/id
new file mode 100644
index 0000000..7727fe5
--- /dev/null
+++ b/src/functions/symbolic_operators/id
@@ -0,0 +1,5 @@
+Function: _===_
+Help: a === b : true if a and b are identical
+Section: symbolic_operators
+C-Name: gidentical
+Prototype: iGG
diff --git a/src/functions/symbolic_operators/le b/src/functions/symbolic_operators/le
new file mode 100644
index 0000000..0212d0f
--- /dev/null
+++ b/src/functions/symbolic_operators/le
@@ -0,0 +1,18 @@
+Function: _<=_
+Help: x<=y: return 1 if x is less or equal to y, 0 otherwise.
+Section: symbolic_operators
+C-Name: gle
+Prototype: GG
+Description:
+ (small, small):bool:parens              $(1) <= $(2)
+ (small, lg):bool:parens                 $(1) < $(2)
+ (lg, lg):bool:parens                    $(1) <= $(2)
+ (small, int):bool:parens                cmpsi($1, $2) <= 0
+ (int, lg):bool:parens                   cmpis($1, $2) < 0
+ (int, small):bool:parens                cmpis($1, $2) <= 0
+ (int, int):bool:parens                  cmpii($1, $2) <= 0
+ (mp, mp):bool:parens                    mpcmp($1, $2) <= 0
+ (str, str):bool:parens                  strcmp($1, $2) <= 0
+ (small, gen):bool:parens                gcmpsg($1, $2) <= 0
+ (gen, small):bool:parens                gcmpgs($1, $2) <= 0
+ (gen, gen):bool:parens                  gcmp($1, $2) <= 0
diff --git a/src/functions/symbolic_operators/lt b/src/functions/symbolic_operators/lt
new file mode 100644
index 0000000..2f0e616
--- /dev/null
+++ b/src/functions/symbolic_operators/lt
@@ -0,0 +1,17 @@
+Function: _<_
+Help: x<y: return 1 if x is strictly less than y, 0 otherwise.
+Section: symbolic_operators
+C-Name: glt
+Prototype: GG
+Description:
+ (small, small):bool:parens              $(1) < $(2)
+ (lg, lg):bool:parens                    $(1) < $(2)
+ (lg, small):bool:parens                 $(1) <= $(2)
+ (small, int):bool:parens                cmpsi($1, $2) < 0
+ (int, small):bool:parens                cmpis($1, $2) < 0
+ (int, int):bool:parens                  cmpii($1, $2) < 0
+ (mp, mp):bool:parens                    mpcmp($1, $2) < 0
+ (str, str):bool:parens                  strcmp($1, $2) < 0
+ (small, gen):bool:parens                gcmpsg($1, $2) < 0
+ (gen, small):bool:parens                gcmpgs($1, $2) < 0
+ (gen, gen):bool:parens                  gcmp($1, $2) < 0
diff --git a/src/functions/symbolic_operators/mm b/src/functions/symbolic_operators/mm
new file mode 100644
index 0000000..d93e09a
--- /dev/null
+++ b/src/functions/symbolic_operators/mm
@@ -0,0 +1,14 @@
+Function: _--
+Section: symbolic_operators
+C-Name: gsub1e
+Prototype: &
+Help: x--
+Description:
+ (*bptr):bptr                          --$1
+ (*small):small                        --$1
+ (*lg):lg                              --$1
+ (*int):int:parens                     $1 = subis($1, 1)
+ (*real):real:parens                   $1 = subrs($1, 1)
+ (*mp):mp:parens                       $1 = mpsub($1, gen_1)
+ (*pol):pol:parens                     $1 = gsubgs($1, 1)
+ (*gen):gen:parens                     $1 = gsubgs($1, 1)
diff --git a/src/functions/symbolic_operators/mod b/src/functions/symbolic_operators/mod
new file mode 100644
index 0000000..df17293
--- /dev/null
+++ b/src/functions/symbolic_operators/mod
@@ -0,0 +1,13 @@
+Function: _%_
+Section: symbolic_operators
+C-Name: gmod
+Prototype: GG
+Help: x%y: Euclidean remainder of x and y.
+Description:
+ (small, small):small            smodss($1, $2)
+ (small, int):int                modsi($1, $2)
+ (int, small):small              smodis($1, $2)
+ (int, int):int                  modii($1, $2)
+ (gen, small):gen                gmodgs($1, $2)
+ (small, gen):gen                gmodsg($1, $2)
+ (gen, gen):gen                  gmod($1, $2)
diff --git a/src/functions/symbolic_operators/mode b/src/functions/symbolic_operators/mode
new file mode 100644
index 0000000..9e65577
--- /dev/null
+++ b/src/functions/symbolic_operators/mode
@@ -0,0 +1,12 @@
+Function: _%=_
+C-Name: gmode
+Prototype: &G
+Section: symbolic_operators
+Help: x%=y: shortcut for x=x%y.
+Description:
+ (*small, small):small:parens            $1 = smodss($1, $2)
+ (*int, small):int:parens                $1 = modis($1, $2)
+ (*int, int):int:parens                  $1 = modii($1, $2)
+ (*pol, gen):gen:parens                  $1 = gmod($1, $2)
+ (*gen, small):gen:parens                $1 = gmodgs($1, $2)
+ (*gen, gen):gen:parens                  $1 = gmod($1, $2)
diff --git a/src/functions/symbolic_operators/mul b/src/functions/symbolic_operators/mul
new file mode 100644
index 0000000..5a01282
--- /dev/null
+++ b/src/functions/symbolic_operators/mul
@@ -0,0 +1,20 @@
+Function: _*_
+Section: symbolic_operators
+C-Name: gmul
+Prototype: GG
+Help: x*y: product of x and y.
+Description:
+ (small, small):small:parens     $(1)*$(2)
+ (int, small):int                mulis($1, $2)
+ (small, int):int                mulsi($1, $2)
+ (int, int):int                  mulii($1, $2)
+ (0, mp):small                   ($2, 0)/*for side effect*/
+ (#small, real):real             mulsr($1, $2)
+ (small, real):mp                mulsr($1, $2)
+ (real, small):mp                mulrs($1, $2)
+ (real, real):real               mulrr($1, $2)
+ (mp, mp):mp                     mpmul($1, $2)
+ (gen, small):gen                gmulgs($1, $2)
+ (small, gen):gen                gmulsg($1, $2)
+ (vecsmall, vecsmall):vecsmall   perm_mul($1, $2)
+ (gen, gen):gen                  gmul($1, $2)
diff --git a/src/functions/symbolic_operators/mule b/src/functions/symbolic_operators/mule
new file mode 100644
index 0000000..c52e1d5
--- /dev/null
+++ b/src/functions/symbolic_operators/mule
@@ -0,0 +1,18 @@
+Function: _*=_
+Section: symbolic_operators
+C-Name: gmule
+Prototype: &G
+Help: x*=y: shortcut for x=x*y.
+Description:
+ (*small, small):small:parens             $1 *= $(2)
+ (*int, small):int:parens                 $1 = mulis($1, $2)
+ (*int, int):int:parens                   $1 = mulii($1, $2)
+ (*real, small):real:parens               $1 = mulrs($1, $2)
+ (*real, int):real:parens                 $1 = mulri($1, $2)
+ (*real, real):real:parens                $1 = mulrr($1, $2)
+ (*mp, mp):mp:parens                      $1 = mpmul($1, $2)
+ (*pol, small):gen:parens                 $1 = gmulgs($1, $2)
+ (*pol, gen):gen:parens                   $1 = gmul($1, $2)
+ (*vec, gen):gen:parens                   $1 = gmul($1, $2)
+ (*gen, small):gen:parens                 $1 = gmulgs($1, $2)
+ (*gen, gen):gen:parens                   $1 = gmul($1, $2)
diff --git a/src/functions/symbolic_operators/ne b/src/functions/symbolic_operators/ne
new file mode 100644
index 0000000..4cc1516
--- /dev/null
+++ b/src/functions/symbolic_operators/ne
@@ -0,0 +1,25 @@
+Function: _!=_
+Help: _!=_
+Section: symbolic_operators
+C-Name: gne
+Prototype: GG
+Description:
+ (small, small):bool:parens             $(1) != $(2)
+ (lg, lg):bool:parens                   $(1) != $(2)
+ (small, int):bool:parens               cmpsi($1, $2) != 0
+ (int, small):bool:parens               cmpis($1, $2) != 0
+ (int, 1):negbool                       equali1($1)
+ (int, -1):negbool                      equalim1($1)
+ (int, int):negbool                     equalii($1, $2)
+ (real,real):bool                       cmprr($1, $2) != 0
+ (mp, mp):bool:parens                   mpcmp($1, $2) != 0
+ (errtyp, errtyp):bool:parens           $(1) != $(2)
+ (errtyp, #str):bool:parens             $(1) != $(errtyp:2)
+ (#str, errtyp):bool:parens             $(errtyp:1) != $(2)
+ (typ, typ):bool:parens                 $(1) != $(2)
+ (typ, #str):bool:parens                $(1) != $(typ:2)
+ (#str, typ):bool:parens                $(typ:1) != $(2)
+ (str, str):bool                        strcmp($1, $2)
+ (small, gen):negbool                   gequalsg($1, $2)
+ (gen, small):negbool                   gequalgs($1, $2)
+ (gen, gen):negbool                     gequal($1, $2)
diff --git a/src/functions/symbolic_operators/neg b/src/functions/symbolic_operators/neg
new file mode 100644
index 0000000..a2e7737
--- /dev/null
+++ b/src/functions/symbolic_operators/neg
@@ -0,0 +1,11 @@
+Function: -_
+Help: -_
+Section: symbolic_operators
+C-Name: gneg
+Prototype: G
+Description:
+ (small):small:parens           -$(1)
+ (int):int                      negi($1)
+ (real):real                    negr($1)
+ (mp):mp                        mpneg($1)
+ (gen):gen                      gneg($1)
diff --git a/src/functions/symbolic_operators/not b/src/functions/symbolic_operators/not
new file mode 100644
index 0000000..5199727
--- /dev/null
+++ b/src/functions/symbolic_operators/not
@@ -0,0 +1,8 @@
+Function: !_
+Help: !_
+Section: symbolic_operators
+C-Name: gnot
+Prototype: G
+Description:
+ (negbool):bool:parens                $1
+ (bool):negbool:parens                $1
diff --git a/src/functions/symbolic_operators/or b/src/functions/symbolic_operators/or
new file mode 100644
index 0000000..5bc51af
--- /dev/null
+++ b/src/functions/symbolic_operators/or
@@ -0,0 +1,7 @@
+Function: _||_
+C-Name: orpari
+Prototype: GE
+Help: x||y: inclusive OR.
+Section: symbolic_operators
+Description:
+ (bool, bool):bool:parens               $(1) || $(2)
diff --git a/src/functions/symbolic_operators/pl b/src/functions/symbolic_operators/pl
new file mode 100644
index 0000000..de2ead7
--- /dev/null
+++ b/src/functions/symbolic_operators/pl
@@ -0,0 +1,9 @@
+Function: +_
+Help: +_
+Section: symbolic_operators
+Description:
+ (small):small:parens                      $1
+ (int):int:parens:copy                     $1
+ (real):real:parens:copy                   $1
+ (mp):mp:parens:copy                       $1
+ (gen):gen:parens:copy                     $1
diff --git a/src/functions/symbolic_operators/pound b/src/functions/symbolic_operators/pound
new file mode 100644
index 0000000..52a7d0f
--- /dev/null
+++ b/src/functions/symbolic_operators/pound
@@ -0,0 +1,10 @@
+Function: #_
+Section: symbolic_operators
+C-Name: glength
+Prototype: lG
+Help: #x: number of non code words in x, number of characters for a string.
+Description:
+ (vecsmall):lg      lg($1)
+ (vec):lg           lg($1)
+ (pol):small        lgpol($1)
+ (gen):small        glength($1)
diff --git a/src/functions/symbolic_operators/pow b/src/functions/symbolic_operators/pow
new file mode 100644
index 0000000..07889ab
--- /dev/null
+++ b/src/functions/symbolic_operators/pow
@@ -0,0 +1,28 @@
+Function: _^_
+Help: x^y: compute x to the power y.
+Section: symbolic_operators
+C-Name: gpow
+Prototype: GGp
+Description:
+ (int, 2):int                sqri($1)
+ (int, 3):int                powiu($1, 3)
+ (int, 4):int                powiu($1, 4)
+ (int, 5):int                powiu($1, 5)
+ (real, -1):real             invr($1)
+ (mp, -1):mp                 ginv($1)
+ (gen, -1):gen               ginv($1)
+ (real, 2):real              sqrr($1)
+ (mp, 2):mp                  mpsqr($1)
+ (gen, 2):gen                gsqr($1)
+ (int, small):gen            powis($1, $2)
+ (real, small):real          gpowgs($1, $2)
+ (gen, small):gen            gpowgs($1, $2)
+ (real, int):real            powgi($1, $2)
+ (gen, int):gen              powgi($1, $2)
+ (gen, gen):gen:prec         gpow($1, $2, prec)
+
+Function: _^s
+Help: return x^n where n is a small integer
+Section: programming/internals
+C-Name: gpowgs
+Prototype: GL
diff --git a/src/functions/symbolic_operators/pp b/src/functions/symbolic_operators/pp
new file mode 100644
index 0000000..dc03e62
--- /dev/null
+++ b/src/functions/symbolic_operators/pp
@@ -0,0 +1,14 @@
+Function: _++
+C-Name: gadd1e
+Prototype: &
+Section: symbolic_operators
+Help: x++
+Description:
+ (*bptr):bptr                            ++$1
+ (*small):small                          ++$1
+ (*lg):lg                                ++$1
+ (*int):int:parens                       $1 = addis($1, 1)
+ (*real):real:parens                     $1 = addrs($1, 1)
+ (*mp):mp:parens                         $1 = mpadd($1, gen_1)
+ (*pol):pol:parens                       $1 = gaddgs($1, 1)
+ (*gen):gen:parens                       $1 = gaddgs($1, 1)
diff --git a/src/functions/symbolic_operators/range b/src/functions/symbolic_operators/range
new file mode 100644
index 0000000..f607004
--- /dev/null
+++ b/src/functions/symbolic_operators/range
@@ -0,0 +1,8 @@
+Function: [_.._]
+Section: programming/internals
+C-Name: vecrange
+Prototype: GG
+Help: [a..b] = [a,a+1,...,b]
+Description:
+ (gen,gen):vec     vecrange($1, $2)
+ (small,small):vec vecrangess($1, $2)
diff --git a/src/functions/symbolic_operators/shiftl b/src/functions/symbolic_operators/shiftl
new file mode 100644
index 0000000..ffb34e2
--- /dev/null
+++ b/src/functions/symbolic_operators/shiftl
@@ -0,0 +1,9 @@
+Function: _<<_
+Section: symbolic_operators
+C-Name: gshift
+Prototype: GL
+Help: x<<y
+Description:
+ (int, small):int               shifti($1, $2)
+ (mp, small):mp                 mpshift($1, $2)
+ (gen, small):mp                gshift($1, $2)
diff --git a/src/functions/symbolic_operators/shiftle b/src/functions/symbolic_operators/shiftle
new file mode 100644
index 0000000..1a59464
--- /dev/null
+++ b/src/functions/symbolic_operators/shiftle
@@ -0,0 +1,10 @@
+Function: _<<=_
+Section: symbolic_operators
+C-Name: gshiftle
+Prototype: &L
+Help: x<<=y: shortcut for x=x<<y.
+Description:
+ (*small, small):small:parens             $1 <<= $(2)
+ (*int, small):int:parens                 $1 = shifti($1, $2)
+ (*mp, small):mp:parens                   $1 = mpshift($1, $2)
+ (*gen, small):mp:parens                  $1 = gshift($1, $2)
diff --git a/src/functions/symbolic_operators/shiftr b/src/functions/symbolic_operators/shiftr
new file mode 100644
index 0000000..a2aad4d
--- /dev/null
+++ b/src/functions/symbolic_operators/shiftr
@@ -0,0 +1,10 @@
+Function: _>>_
+Section: symbolic_operators
+C-Name: gshift_right
+Prototype: GL
+Help: x>>y
+Description:
+ (small, small):small:parens     $(1)>>$(2)
+ (int, small):int                shifti($1, -$(2))
+ (mp, small):mp                  mpshift($1, -$(2))
+ (gen, small):mp                 gshift($1, -$(2))
diff --git a/src/functions/symbolic_operators/shiftre b/src/functions/symbolic_operators/shiftre
new file mode 100644
index 0000000..a16311c
--- /dev/null
+++ b/src/functions/symbolic_operators/shiftre
@@ -0,0 +1,10 @@
+Function: _>>=_
+Section: symbolic_operators
+C-Name: gshiftre
+Prototype: &L
+Help: x>>=y: shortcut for x=x>>y.
+Description:
+ (*small, small):small:parens             $1 >>= $(2)
+ (*int, small):int:parens                 $1 = shifti($1, -$(2))
+ (*mp, small):mp:parens                   $1 = mpshift($1, -$(2))
+ (*gen, small):mp:parens                  $1 = gshift($1, -$(2))
diff --git a/src/functions/symbolic_operators/slice b/src/functions/symbolic_operators/slice
new file mode 100644
index 0000000..09d39bf
--- /dev/null
+++ b/src/functions/symbolic_operators/slice
@@ -0,0 +1,14 @@
+Function: _[_.._]
+Section: symbolic_operators
+C-Name: vecslice0
+Prototype: GD0,L,L
+Help: x[a..b] = [x[a],x[a+1],...,x[b]]
+
+Function: _[_.._,_.._]
+Section: symbolic_operators
+C-Name: matslice0
+Prototype: GD0,L,D0,L,D0,L,D0,L,
+Help: x[a..b,c..d] = [x[a,c],  x[a+1,c],  ...,x[b,c];
+                      x[a,c+1],x[a+1,c+1],...,x[b,c+1];
+                        ...       ...          ...
+                      x[a,d],  x[a+1,d]  ,...,x[b,d]]
diff --git a/src/functions/symbolic_operators/sub b/src/functions/symbolic_operators/sub
new file mode 100644
index 0000000..8a38361
--- /dev/null
+++ b/src/functions/symbolic_operators/sub
@@ -0,0 +1,20 @@
+Function: _-_
+Section: symbolic_operators
+C-Name: gsub
+Prototype: GG
+Help: x-y: difference of x and y.
+Description:
+ (small, small):small:parens     $(1) - $(2)
+ (lg, small):lg:parens           $(1) - $(2)
+ (int, small):int                subis($1, $2)
+ (small, int):int                subsi($1, $2)
+ (int, int):int                  subii($1, $2)
+ (real, small):real              subrs($1, $2)
+ (small, real):real              subsr($1, $2)
+ (real, real):real               subrr($1, $2)
+ (mp, real):real                 mpsub($1, $2)
+ (real, mp):real                 mpsub($1, $2)
+ (mp, mp):mp                     mpsub($1, $2)
+ (gen, small):gen                gsubgs($1, $2)
+ (small, gen):gen                gsubsg($1, $2)
+ (gen, gen):gen                  gsub($1, $2)
diff --git a/src/functions/symbolic_operators/sube b/src/functions/symbolic_operators/sube
new file mode 100644
index 0000000..a75be81
--- /dev/null
+++ b/src/functions/symbolic_operators/sube
@@ -0,0 +1,19 @@
+Function: _-=_
+C-Name: gsube
+Prototype: &G
+Help: x-=y: shortcut for x=x-y.
+Section: symbolic_operators
+Description:
+ (*small, small):small:parens             $1 -= $(2)
+ (*lg, small):lg:parens                   $1 -= $(2)
+ (*int, small):int:parens                 $1 = subis($1, $2)
+ (*int, int):int:parens                   $1 = subii($1, $2)
+ (*real, small):real:parens               $1 = subrs($1, $2)
+ (*real, int):real:parens                 $1 = subri($1, $2)
+ (*real, real):real:parens                $1 = subrr($1, $2)
+ (*mp, mp):mp:parens                      $1 = mpsub($1, $2)
+ (*pol, small):gen:parens                 $1 = gsubgs($1, $2)
+ (*pol, gen):gen:parens                   $1 = gsub($1, $2)
+ (*vec, gen):gen:parens                   $1 = gsub($1, $2)
+ (*gen, small):gen:parens                 $1 = gsubgs($1, $2)
+ (*gen, gen):gen:parens                   $1 = gsub($1, $2)
diff --git a/src/functions/symbolic_operators/trans b/src/functions/symbolic_operators/trans
new file mode 100644
index 0000000..d100701
--- /dev/null
+++ b/src/functions/symbolic_operators/trans
@@ -0,0 +1,8 @@
+Function: _~
+Section: symbolic_operators
+C-Name: gtrans
+Prototype: G
+Help: x~: transpose of x.
+Description:
+ (vec):vec                        gtrans($1)
+ (gen):gen                        gtrans($1)
diff --git a/src/functions/transcendental/Catalan b/src/functions/transcendental/Catalan
new file mode 100644
index 0000000..6bb1f48
--- /dev/null
+++ b/src/functions/transcendental/Catalan
@@ -0,0 +1,10 @@
+Function: Catalan
+Section: transcendental
+C-Name: mpcatalan
+Prototype: p
+Help: Catalan=Catalan(): Catalan's number with current precision.
+Description:
+ ():real:prec        mpcatalan(prec)
+Doc: Catalan's constant $G = \sum_{n>=0}\dfrac{(-1)^n}{(2n+1)^2}=0.91596\cdots$.
+ Note that \kbd{Catalan} is one of the few reserved names which cannot be
+ used for user variables.
diff --git a/src/functions/transcendental/Euler b/src/functions/transcendental/Euler
new file mode 100644
index 0000000..4834ae4
--- /dev/null
+++ b/src/functions/transcendental/Euler
@@ -0,0 +1,10 @@
+Function: Euler
+Section: transcendental
+C-Name: mpeuler
+Prototype: p
+Help: Euler=Euler(): Euler's constant with current precision.
+Description:
+ ():real:prec        mpeuler(prec)
+Doc: Euler's constant $\gamma=0.57721\cdots$. Note that
+ \kbd{Euler} is one of the few reserved names which cannot be used for
+ user variables.
diff --git a/src/functions/transcendental/I b/src/functions/transcendental/I
new file mode 100644
index 0000000..060f49f
--- /dev/null
+++ b/src/functions/transcendental/I
@@ -0,0 +1,7 @@
+Function: I
+Section: transcendental
+C-Name: gen_I
+Prototype:
+Help: I=I(): square root of -1.
+Description:
+Doc: the complex number $\sqrt{-1}$.
diff --git a/src/functions/transcendental/Pi b/src/functions/transcendental/Pi
new file mode 100644
index 0000000..78f3803
--- /dev/null
+++ b/src/functions/transcendental/Pi
@@ -0,0 +1,9 @@
+Function: Pi
+Section: transcendental
+C-Name: mppi
+Prototype: p
+Help: Pi=Pi(): the constant pi, with current precision.
+Description:
+ ():real:prec        mppi(prec)
+Doc: the constant $\pi$ ($3.14159\cdots$). Note that \kbd{Pi} is one of the few
+ reserved names which cannot be used for user variables.
diff --git a/src/functions/transcendental/abs b/src/functions/transcendental/abs
new file mode 100644
index 0000000..3bc000d
--- /dev/null
+++ b/src/functions/transcendental/abs
@@ -0,0 +1,26 @@
+Function: abs
+Section: transcendental
+C-Name: gabs
+Prototype: Gp
+Help: abs(x): absolute value (or modulus) of x.
+Description:
+ (small):small    labs($1)
+ (int):int        mpabs($1)
+ (real):real      mpabs($1)
+ (mp):mp          mpabs($1)
+ (gen):gen:prec        gabs($1, prec)
+Doc: absolute value of $x$ (modulus if $x$ is complex).
+ Rational functions are not allowed. Contrary to most transcendental
+ functions, an exact argument is \emph{not} converted to a real number before
+ applying \kbd{abs} and an exact result is returned if possible.
+ \bprog
+ ? abs(-1)
+ %1 = 1
+ ? abs(3/7 + 4/7*I)
+ %2 = 5/7
+ ? abs(1 + I)
+ %3 = 1.414213562373095048801688724
+ @eprog\noindent
+ If $x$ is a polynomial, returns $-x$ if the leading coefficient is
+ real and negative else returns $x$. For a power series, the constant
+ coefficient is considered instead.
diff --git a/src/functions/transcendental/acos b/src/functions/transcendental/acos
new file mode 100644
index 0000000..bb3f3c3
--- /dev/null
+++ b/src/functions/transcendental/acos
@@ -0,0 +1,11 @@
+Function: acos
+Section: transcendental
+C-Name: gacos
+Prototype: Gp
+Help: acos(x): arc cosine of x.
+Doc: principal branch of $\text{cos}^{-1}(x) = -i \log (x + i\sqrt{1-x^2})$.
+ In particular, $\text{Re(acos}(x))\in [0,\pi]$ and if $x\in \R$ and $|x|>1$,
+ then $\text{acos}(x)$ is complex. The branch cut is in two pieces:
+ $]-\infty,-1]$ , continuous with quadrant II, and $[1,+\infty[$, continuous
+ with quadrant IV. We have $\text{acos}(x) = \pi/2 - \text{asin}(x)$ for all
+ $x$.
diff --git a/src/functions/transcendental/acosh b/src/functions/transcendental/acosh
new file mode 100644
index 0000000..997e53e
--- /dev/null
+++ b/src/functions/transcendental/acosh
@@ -0,0 +1,10 @@
+Function: acosh
+Section: transcendental
+C-Name: gacosh
+Prototype: Gp
+Help: acosh(x): inverse hyperbolic cosine of x.
+Doc: principal branch of $\text{cosh}^{-1}(x) = 2
+  \log(\sqrt{(x+1)/2} + \sqrt{(x-1)/2})$. In particular,
+ $\text{Re}(\text{acosh}(x))\geq 0$ and
+ $\text{In}(\text{acosh}(x))\in ]-\pi,\pi]0$; if $x\in \R$ and $x<1$, then
+ $\text{acosh}(x)$ is complex.
diff --git a/src/functions/transcendental/agm b/src/functions/transcendental/agm
new file mode 100644
index 0000000..d7850fa
--- /dev/null
+++ b/src/functions/transcendental/agm
@@ -0,0 +1,11 @@
+Function: agm
+Section: transcendental
+C-Name: agm
+Prototype: GGp
+Help: agm(x,y): arithmetic-geometric mean of x and y.
+Doc: arithmetic-geometric mean of $x$ and $y$. In the
+ case of complex or negative numbers, the optimal AGM is returned
+ (the largest in absolute value over all choices of the signs of the square
+ roots).  $p$-adic or power series arguments are also allowed. Note that
+ a $p$-adic agm exists only if $x/y$ is congruent to 1 modulo $p$ (modulo
+ 16 for $p=2$). $x$ and $y$ cannot both be vectors or matrices.
diff --git a/src/functions/transcendental/arg b/src/functions/transcendental/arg
new file mode 100644
index 0000000..61fd346
--- /dev/null
+++ b/src/functions/transcendental/arg
@@ -0,0 +1,6 @@
+Function: arg
+Section: transcendental
+C-Name: garg
+Prototype: Gp
+Help: arg(x): argument of x,such that -pi<arg(x)<=pi.
+Doc: argument of the complex number $x$, such that $-\pi<\text{arg}(x)\le\pi$.
diff --git a/src/functions/transcendental/asin b/src/functions/transcendental/asin
new file mode 100644
index 0000000..f445d22
--- /dev/null
+++ b/src/functions/transcendental/asin
@@ -0,0 +1,11 @@
+Function: asin
+Section: transcendental
+C-Name: gasin
+Prototype: Gp
+Help: asin(x): arc sine of x.
+Doc: principal branch of $\text{sin}^{-1}(x) = -i \log(ix + \sqrt{1 - x^2})$.
+ In particular, $\text{Re(asin}(x))\in [-\pi/2,\pi/2]$ and if $x\in \R$ and
+ $|x|>1$ then $\text{asin}(x)$ is complex. The branch cut is in two pieces:
+ $]-\infty,-1]$, continuous with quadrant II, and $[1,+\infty[$ continuous
+ with quadrant IV. The function satisfies $i \text{asin}(x) =
+ \text{asinh}(ix)$.
diff --git a/src/functions/transcendental/asinh b/src/functions/transcendental/asinh
new file mode 100644
index 0000000..09f2be6
--- /dev/null
+++ b/src/functions/transcendental/asinh
@@ -0,0 +1,10 @@
+Function: asinh
+Section: transcendental
+C-Name: gasinh
+Prototype: Gp
+Help: asinh(x): inverse hyperbolic sine of x.
+Doc: principal branch of $\text{sinh}^{-1}(x) = \log(x + \sqrt{1+x^2})$. In
+ particular $\text{Im(asinh}(x))\in [-\pi/2,\pi/2]$.
+ The branch cut is in two pieces: [-i oo ,-i],  continuous with quadrant III
+ and [i,+i oo [ continuous with quadrant I.
+
diff --git a/src/functions/transcendental/atan b/src/functions/transcendental/atan
new file mode 100644
index 0000000..bffed00
--- /dev/null
+++ b/src/functions/transcendental/atan
@@ -0,0 +1,12 @@
+Function: atan
+Section: transcendental
+C-Name: gatan
+Prototype: Gp
+Help: atan(x): arc tangent of x.
+Doc: principal branch of $\text{tan}^{-1}(x) = \log ((1+ix)/(1-ix)) /
+ 2i$. In particular the real part of $\text{atan}(x))$ belongs to
+ $]-\pi/2,\pi/2[$.
+ The branch cut is in two pieces:
+ $]-i\infty,-i[$, continuous with quadrant IV, and $]i,+i \infty[$ continuous
+ with quadrant II. The function satisfies $i \text{atan}(x) =
+ -i\text{atanh}(ix)$ for all $x\neq \pm i$.
diff --git a/src/functions/transcendental/atanh b/src/functions/transcendental/atanh
new file mode 100644
index 0000000..7e2df65
--- /dev/null
+++ b/src/functions/transcendental/atanh
@@ -0,0 +1,8 @@
+Function: atanh
+Section: transcendental
+C-Name: gatanh
+Prototype: Gp
+Help: atanh(x): inverse hyperbolic tangent of x.
+Doc: principal branch of $\text{tanh}^{-1}(x) = log ((1+x)/(1-x)) / 2$. In
+ particular the imaginary part of $\text{atanh}(x)$ belongs to
+ $[-\pi/2,\pi/2]$; if $x\in \R$ and $|x|>1$ then $\text{atanh}(x)$ is complex.
diff --git a/src/functions/transcendental/bernfrac b/src/functions/transcendental/bernfrac
new file mode 100644
index 0000000..ee6070f
--- /dev/null
+++ b/src/functions/transcendental/bernfrac
@@ -0,0 +1,8 @@
+Function: bernfrac
+Section: transcendental
+C-Name: bernfrac
+Prototype: L
+Help: bernfrac(x): Bernoulli number B_x, as a rational number.
+Doc: Bernoulli number\sidx{Bernoulli numbers} $B_x$,
+ where $B_0=1$, $B_1=-1/2$, $B_2=1/6$,\dots, expressed as a rational number.
+ The argument $x$ should be of type integer.
diff --git a/src/functions/transcendental/bernpol b/src/functions/transcendental/bernpol
new file mode 100644
index 0000000..699698e
--- /dev/null
+++ b/src/functions/transcendental/bernpol
@@ -0,0 +1,12 @@
+Function: bernpol
+Section: transcendental
+C-Name: bernpol
+Prototype: LDn
+Help: bernpol(n, {v = 'x}): Bernoulli polynomial B_n, in variable v.
+Doc: \idx{Bernoulli polynomial} $B_n$ in variable $v$.
+ \bprog
+ ? bernpol(1)
+ %1 = x - 1/2
+ ? bernpol(3)
+ %2 = x^3 - 3/2*x^2 + 1/2*x
+ @eprog
diff --git a/src/functions/transcendental/bernreal b/src/functions/transcendental/bernreal
new file mode 100644
index 0000000..692d746
--- /dev/null
+++ b/src/functions/transcendental/bernreal
@@ -0,0 +1,9 @@
+Function: bernreal
+Section: transcendental
+C-Name: bernreal
+Prototype: Lp
+Help: bernreal(x): Bernoulli number B_x, as a real number with the current
+ precision.
+Doc: Bernoulli number\sidx{Bernoulli numbers}
+ $B_x$, as \kbd{bernfrac}, but $B_x$ is returned as a real number
+ (with the current precision).
diff --git a/src/functions/transcendental/bernvec b/src/functions/transcendental/bernvec
new file mode 100644
index 0000000..478627d
--- /dev/null
+++ b/src/functions/transcendental/bernvec
@@ -0,0 +1,16 @@
+Function: bernvec
+Section: transcendental
+C-Name: bernvec
+Prototype: L
+Help: bernvec(x): Vector of rational Bernoulli numbers B_0, B_2,...up to
+ B_(2x).
+Doc: creates a vector containing, as rational numbers,
+ the \idx{Bernoulli numbers} $B_0$, $B_2$,\dots, $B_{2x}$.
+ This routine is obsolete. Use \kbd{bernfrac} instead each time you need a
+ Bernoulli number in exact form.
+
+ \misctitle{Note} This routine is implemented using repeated independent
+ calls to \kbd{bernfrac}, which is faster than the standard recursion in exact
+ arithmetic. It is only kept for backward compatibility: it is not faster than
+ individual calls to \kbd{bernfrac}, its output uses a lot of memory space,
+ and coping with the index shift is awkward.
diff --git a/src/functions/transcendental/besselh1 b/src/functions/transcendental/besselh1
new file mode 100644
index 0000000..d02a312
--- /dev/null
+++ b/src/functions/transcendental/besselh1
@@ -0,0 +1,6 @@
+Function: besselh1
+Section: transcendental
+C-Name: hbessel1
+Prototype: GGp
+Help: besselh1(nu,x): H^1-bessel function of index nu and argument x.
+Doc: $H^1$-Bessel function of index \var{nu} and argument $x$.
diff --git a/src/functions/transcendental/besselh2 b/src/functions/transcendental/besselh2
new file mode 100644
index 0000000..dd308a5
--- /dev/null
+++ b/src/functions/transcendental/besselh2
@@ -0,0 +1,6 @@
+Function: besselh2
+Section: transcendental
+C-Name: hbessel2
+Prototype: GGp
+Help: besselh2(nu,x): H^2-bessel function of index nu and argument x.
+Doc: $H^2$-Bessel function of index \var{nu} and argument $x$.
diff --git a/src/functions/transcendental/besseli b/src/functions/transcendental/besseli
new file mode 100644
index 0000000..22f8f6a
--- /dev/null
+++ b/src/functions/transcendental/besseli
@@ -0,0 +1,9 @@
+Function: besseli
+Section: transcendental
+C-Name: ibessel
+Prototype: GGp
+Help: besseli(nu,x): I-bessel function of index nu and argument x.
+Doc: $I$-Bessel function of index \var{nu} and
+ argument $x$. If $x$ converts to a power series, the initial factor
+ $(x/2)^\nu/\Gamma(\nu+1)$ is omitted (since it cannot be represented in PARI
+ when $\nu$ is not integral).
diff --git a/src/functions/transcendental/besselj b/src/functions/transcendental/besselj
new file mode 100644
index 0000000..0e47b38
--- /dev/null
+++ b/src/functions/transcendental/besselj
@@ -0,0 +1,9 @@
+Function: besselj
+Section: transcendental
+C-Name: jbessel
+Prototype: GGp
+Help: besselj(nu,x): J-bessel function of index nu and argument x.
+Doc: $J$-Bessel function of index \var{nu} and
+ argument $x$. If $x$ converts to a power series, the initial factor
+ $(x/2)^\nu/\Gamma(\nu+1)$ is omitted (since it cannot be represented in PARI
+ when $\nu$ is not integral).
diff --git a/src/functions/transcendental/besseljh b/src/functions/transcendental/besseljh
new file mode 100644
index 0000000..7cb1c10
--- /dev/null
+++ b/src/functions/transcendental/besseljh
@@ -0,0 +1,10 @@
+Function: besseljh
+Section: transcendental
+C-Name: jbesselh
+Prototype: GGp
+Help: besseljh(n,x): J-bessel function of index n+1/2 and argument x, where
+ n is a non-negative integer.
+Doc: $J$-Bessel function of half integral index.
+ More precisely, $\kbd{besseljh}(n,x)$ computes $J_{n+1/2}(x)$ where $n$
+ must be of type integer, and $x$ is any element of $\C$. In the
+ present version \vers, this function is not very accurate when $x$ is small.
diff --git a/src/functions/transcendental/besselk b/src/functions/transcendental/besselk
new file mode 100644
index 0000000..f32b320
--- /dev/null
+++ b/src/functions/transcendental/besselk
@@ -0,0 +1,6 @@
+Function: besselk
+Section: transcendental
+C-Name: kbessel
+Prototype: GGp
+Help: besselk(nu,x): K-bessel function of index nu and argument x.
+Doc: $K$-Bessel function of index \var{nu} and argument $x$.
diff --git a/src/functions/transcendental/besseln b/src/functions/transcendental/besseln
new file mode 100644
index 0000000..97e22d8
--- /dev/null
+++ b/src/functions/transcendental/besseln
@@ -0,0 +1,6 @@
+Function: besseln
+Section: transcendental
+C-Name: nbessel
+Prototype: GGp
+Help: besseln(nu,x): N-bessel function of index nu and argument x.
+Doc: $N$-Bessel function of index \var{nu} and argument $x$.
diff --git a/src/functions/transcendental/cos b/src/functions/transcendental/cos
new file mode 100644
index 0000000..189b8cf
--- /dev/null
+++ b/src/functions/transcendental/cos
@@ -0,0 +1,6 @@
+Function: cos
+Section: transcendental
+C-Name: gcos
+Prototype: Gp
+Help: cos(x): cosine of x.
+Doc: cosine of $x$.
diff --git a/src/functions/transcendental/cosh b/src/functions/transcendental/cosh
new file mode 100644
index 0000000..4ae829f
--- /dev/null
+++ b/src/functions/transcendental/cosh
@@ -0,0 +1,6 @@
+Function: cosh
+Section: transcendental
+C-Name: gcosh
+Prototype: Gp
+Help: cosh(x): hyperbolic cosine of x.
+Doc: hyperbolic cosine of $x$.
diff --git a/src/functions/transcendental/cotan b/src/functions/transcendental/cotan
new file mode 100644
index 0000000..15b3c6b
--- /dev/null
+++ b/src/functions/transcendental/cotan
@@ -0,0 +1,6 @@
+Function: cotan
+Section: transcendental
+C-Name: gcotan
+Prototype: Gp
+Help: cotan(x): cotangent of x.
+Doc: cotangent of $x$.
diff --git a/src/functions/transcendental/dilog b/src/functions/transcendental/dilog
new file mode 100644
index 0000000..2cb6cda
--- /dev/null
+++ b/src/functions/transcendental/dilog
@@ -0,0 +1,7 @@
+Function: dilog
+Section: transcendental
+C-Name: dilog
+Prototype: Gp
+Help: dilog(x): dilogarithm of x.
+Doc: principal branch of the dilogarithm of $x$,
+ i.e.~analytic continuation of the power series $\log_2(x)=\sum_{n\ge1}x^n/n^2$.
diff --git a/src/functions/transcendental/eint1 b/src/functions/transcendental/eint1
new file mode 100644
index 0000000..55b35f6
--- /dev/null
+++ b/src/functions/transcendental/eint1
@@ -0,0 +1,22 @@
+Function: eint1
+Section: transcendental
+C-Name: veceint1
+Prototype: GDGp
+Help: eint1(x,{n}): exponential integral E1(x). If n is present and x > 0,
+ computes the vector of the first n values of the exponential integral E1(n.x)
+Doc: exponential integral $\int_x^\infty \dfrac{e^{-t}}{t}\,dt =
+ \kbd{incgam}(0, x)$, where the latter expression extends the function
+ definition from real $x > 0$ to all complex $x \neq 0$.
+
+ If $n$ is present, we must have $x > 0$; the function returns the
+ $n$-dimensional vector $[\kbd{eint1}(x),\dots,\kbd{eint1}(nx)]$. Contrary to
+ other transcendental functions, and to the default case ($n$ omitted), the
+ values are correct up to a bounded \emph{absolute}, rather than relative,
+ error $10^-n$, where $n$ is \kbd{precision}$(x)$ if $x$ is a \typ{REAL}
+ and defaults to \kbd{realprecision} otherwise. (In the most important
+ application, to the computation of $L$-functions via approximate functional
+ equations, those values appear as weights in long sums and small individual
+ relative errors are less useful than controlling the absolute error.) This is
+ faster than repeatedly calling \kbd{eint1($i$ * x)}, but less precise.
+
+Variant: Also available is \fun{GEN}{eint1}{GEN x, long prec}.
diff --git a/src/functions/transcendental/erfc b/src/functions/transcendental/erfc
new file mode 100644
index 0000000..a3486ad
--- /dev/null
+++ b/src/functions/transcendental/erfc
@@ -0,0 +1,9 @@
+Function: erfc
+Section: transcendental
+C-Name: gerfc
+Prototype: Gp
+Help: erfc(x): complementary error function.
+Doc: complementary error function, analytic continuation of
+ $(2/\sqrt\pi)\int_x^\infty e^{-t^2}\,dt = \kbd{incgam}(1/2,x^2)/\sqrt\pi$,
+ where the latter expression extends the function definition from real $x$ to
+ all complex $x \neq 0$.
diff --git a/src/functions/transcendental/eta b/src/functions/transcendental/eta
new file mode 100644
index 0000000..5ea8cc2
--- /dev/null
+++ b/src/functions/transcendental/eta
@@ -0,0 +1,26 @@
+Function: eta
+Section: transcendental
+C-Name: eta0
+Prototype: GD0,L,p
+Help: eta(z,{flag=0}): if flag=0, returns prod(n=1,oo, 1-q^n), where
+ q = exp(2 i Pi z) if z is a complex scalar (belonging to the upper half plane);
+ q = z if z is a p-adic number or can be converted to a power series.
+ If flag is non-zero, the function only applies to complex scalars and returns
+ the true eta function, with the factor q^(1/24) included.
+
+Doc: Variants of \idx{Dedekind}'s $\eta$ function.
+ If $\fl = 0$, return $\prod_{n=1}^\infty(1-q^n)$, where $q$ depends on $x$
+ in the following way:
+
+ \item $q = e^{2i\pi x}$ if $x$ is a \emph{complex number} (which must then
+ have positive imaginary part); notice that the factor $q^{1/24}$ is
+ missing!
+
+ \item $q = x$ if $x$ is a \typ{PADIC}, or can be converted to a
+ \emph{power series} (which must then have positive valuation).
+
+ If $\fl$ is non-zero, $x$ is converted to a complex number and we return the
+ true $\eta$ function, $q^{1/24}\prod_{n=1}^\infty(1-q^n)$,
+ where $q = e^{2i\pi x}$.
+Variant:
+ Also available is \fun{GEN}{trueeta}{GEN x, long prec} ($\fl=1$).
diff --git a/src/functions/transcendental/exp b/src/functions/transcendental/exp
new file mode 100644
index 0000000..fd641e6
--- /dev/null
+++ b/src/functions/transcendental/exp
@@ -0,0 +1,14 @@
+Function: exp
+Section: transcendental
+C-Name: gexp
+Prototype: Gp
+Help: exp(x): exponential of x.
+Description:
+ (real):real         mpexp($1)
+ (mp):mp:prec        gexp($1, prec)
+ (gen):gen:prec      gexp($1, prec)
+Doc: exponential of $x$.
+ $p$-adic arguments with positive valuation are accepted.
+Variant: For a \typ{PADIC} $x$, the function
+ \fun{GEN}{Qp_exp}{GEN x} is also available.
+
diff --git a/src/functions/transcendental/expm1 b/src/functions/transcendental/expm1
new file mode 100644
index 0000000..d5e9642
--- /dev/null
+++ b/src/functions/transcendental/expm1
@@ -0,0 +1,27 @@
+Function: expm1
+Section: transcendental
+C-Name: gexpm1
+Prototype: Gp
+Help: expm1(x): exp(x)-1.
+Description:
+ (real):real         mpexpm1($1)
+Doc: return $\exp(x)-1$, computed in a way that is also accurate
+ when the real part of $x$ is near $0$. Only accept real or complex arguments.
+ A naive direct computation would suffer from catastrophic cancellation;
+ PARI's direct computation of $\exp(x)$ alleviates this well known problem at
+ the expense of computing $\exp(x)$ to a higher accuracy when $x$ is small.
+ Using \kbd{expm1} is recommended instead:
+ \bprog
+ ? default(realprecision, 10000); x = 1e-100;
+ ? a = expm1(x);
+ time = 4 ms.
+ ? b = exp(x)-1;
+ time = 28 ms.
+ ? default(realprecision, 10040); x = 1e-100;
+ ? c = expm1(x);  \\ reference point
+ ? abs(a-c)/c  \\ relative error in expm1(x)
+ %7 = 0.E-10017
+ ? abs(b-c)/c  \\ relative error in exp(x)-1
+ %8 = 1.7907031188259675794 E-9919
+ @eprog\noindent As the example above shows, when $x$ is near $0$,
+ \kbd{expm1} is both faster and more accurate than \kbd{exp(x)-1}.
diff --git a/src/functions/transcendental/gamma b/src/functions/transcendental/gamma
new file mode 100644
index 0000000..0c78dc9
--- /dev/null
+++ b/src/functions/transcendental/gamma
@@ -0,0 +1,22 @@
+Function: gamma
+Section: transcendental
+C-Name: ggamma
+Prototype: Gp
+Help: gamma(s): gamma function at s, a complex or p-adic number, or a series.
+Doc: For $s$ a complex number, evaluates Euler's gamma
+ function \sidx{gamma-function}
+ $$\Gamma(s)=\int_0^\infty t^{s-1}\exp(-t)\,dt.$$
+ Error if $s$ is a non-positive integer, where $\Gamma$ has a pole.
+
+ For $s$ a \typ{PADIC}, evaluates the Morita gamma function at $s$, that
+ is the unique continuous $p$-adic function on the $p$-adic integers
+ extending $\Gamma_p(k)=(-1)^k \prod_{j<k}'j$, where the prime means that $p$
+ does not divide $j$.
+ \bprog
+ ? gamma(1/4 + O(5^10))
+ %1= 1 + 4*5 + 3*5^4 + 5^6 + 5^7 + 4*5^9 + O(5^10)
+ ? algdep(%,4)
+ %2 = x^4 + 4*x^2 + 5
+ @eprog
+Variant: For a \typ{PADIC} $x$, the function \fun{GEN}{Qp_gamma}{GEN x} is
+ also available.
diff --git a/src/functions/transcendental/gammah b/src/functions/transcendental/gammah
new file mode 100644
index 0000000..cbb125c
--- /dev/null
+++ b/src/functions/transcendental/gammah
@@ -0,0 +1,6 @@
+Function: gammah
+Section: transcendental
+C-Name: ggammah
+Prototype: Gp
+Help: gammah(x): gamma of x+1/2 (x integer).
+Doc: gamma function evaluated at the argument $x+1/2$.
diff --git a/src/functions/transcendental/hyperu b/src/functions/transcendental/hyperu
new file mode 100644
index 0000000..6a115bb
--- /dev/null
+++ b/src/functions/transcendental/hyperu
@@ -0,0 +1,8 @@
+Function: hyperu
+Section: transcendental
+C-Name: hyperu
+Prototype: GGGp
+Help: hyperu(a,b,x): U-confluent hypergeometric function.
+Doc: $U$-confluent hypergeometric function with
+ parameters $a$ and $b$. The parameters $a$ and $b$ can be complex but
+ the present implementation requires $x$ to be positive.
diff --git a/src/functions/transcendental/incgam b/src/functions/transcendental/incgam
new file mode 100644
index 0000000..59d5d05
--- /dev/null
+++ b/src/functions/transcendental/incgam
@@ -0,0 +1,12 @@
+Function: incgam
+Section: transcendental
+C-Name: incgam0
+Prototype: GGDGp
+Help: incgam(s,x,{g}): incomplete gamma function. g is optional and is the
+ precomputed value of gamma(s).
+Doc: incomplete gamma function $\int_x^\infty e^{-t}t^{s-1}\,dt$, extended by
+ analytic continuation to all complex $x, s$ not both $0$. The relative error
+ is bounded in terms of the precision of $s$ (the accuracy of $x$ is ignored
+ when determining the output precision). When $g$ is given, assume that
+ $g=\Gamma(s)$. For small $|x|$, this will speed up the computation.
+Variant: Also available is \fun{GEN}{incgam}{GEN s, GEN x, long prec}.
diff --git a/src/functions/transcendental/incgamc b/src/functions/transcendental/incgamc
new file mode 100644
index 0000000..d885103
--- /dev/null
+++ b/src/functions/transcendental/incgamc
@@ -0,0 +1,10 @@
+Function: incgamc
+Section: transcendental
+C-Name: incgamc
+Prototype: GGp
+Help: incgamc(s,x): complementary incomplete gamma function.
+Doc: complementary incomplete gamma function.
+ The arguments $x$ and $s$ are complex numbers such that $s$ is not a pole of
+ $\Gamma$ and $|x|/(|s|+1)$ is not much larger than 1 (otherwise the
+ convergence is very slow). The result returned is $\int_0^x
+ e^{-t}t^{s-1}\,dt$.
diff --git a/src/functions/transcendental/lambertw b/src/functions/transcendental/lambertw
new file mode 100644
index 0000000..a51d2ee
--- /dev/null
+++ b/src/functions/transcendental/lambertw
@@ -0,0 +1,7 @@
+Function: lambertw
+Section: transcendental
+C-Name: glambertW
+Prototype: Gp
+Help: lambertw(y): solution of the implicit equation x*exp(x)=y.
+Doc: Lambert $W$ function, solution of the implicit equation $xe^x=y$,
+ for $y > 0$.
diff --git a/src/functions/transcendental/lngamma b/src/functions/transcendental/lngamma
new file mode 100644
index 0000000..8cd4cba
--- /dev/null
+++ b/src/functions/transcendental/lngamma
@@ -0,0 +1,25 @@
+Function: lngamma
+Section: transcendental
+C-Name: glngamma
+Prototype: Gp
+Help: lngamma(x): logarithm of the gamma function of x.
+Doc: principal branch of the logarithm of the gamma function of $x$. This
+ function is analytic on the complex plane with non-positive integers
+ removed, and can have much larger arguments than \kbd{gamma} itself.
+
+ For $x$ a power series such that $x(0)$ is not a pole of \kbd{gamma},
+ compute the Taylor expansion. (PARI only knows about regular power series
+ and can't include logarithmic terms.)
+ \bprog
+ ? lngamma(1+x+O(x^2))
+ %1 = -0.57721566490153286060651209008240243104*x + O(x^2)
+ ? lngamma(x+O(x^2))
+  ***   at top-level: lngamma(x+O(x^2))
+  ***                 ^-----------------
+  *** lngamma: domain error in lngamma: valuation != 0
+ ? lngamma(-1+x+O(x^2))
+  *** lngamma: Warning: normalizing a series with 0 leading term.
+  ***   at top-level: lngamma(-1+x+O(x^2))
+  ***                 ^--------------------
+  *** lngamma: domain error in intformal: residue(series, pole) != 0
+ @eprog
diff --git a/src/functions/transcendental/log b/src/functions/transcendental/log
new file mode 100644
index 0000000..1e5b8ae
--- /dev/null
+++ b/src/functions/transcendental/log
@@ -0,0 +1,23 @@
+Function: log
+Section: transcendental
+C-Name: glog
+Prototype: Gp
+Help: log(x): natural logarithm of x.
+Description:
+ (gen):gen:prec        glog($1, prec)
+Doc: principal branch of the natural logarithm of
+ $x \in \C^*$, i.e.~such that $\text{Im(log}(x))\in{} ]-\pi,\pi]$.
+ The branch cut lies
+ along the negative real axis, continuous with quadrant 2, i.e.~such that
+ $\lim_{b\to 0^+} \log (a+bi) = \log a$ for $a \in\R^*$. The result is complex
+ (with imaginary part equal to $\pi$) if $x\in \R$ and $x < 0$. In general,
+ the algorithm uses the formula
+ $$\log(x) \approx {\pi\over 2\text{agm}(1, 4/s)} - m \log 2, $$
+ if $s = x 2^m$ is large enough. (The result is exact to $B$ bits provided
+ $s > 2^{B/2}$.) At low accuracies, the series expansion near $1$ is used.
+
+ $p$-adic arguments are also accepted for $x$, with the convention that
+ $\log(p)=0$. Hence in particular $\exp(\log(x))/x$ is not in general equal to
+ 1 but to a $(p-1)$-th root of unity (or $\pm1$ if $p=2$) times a power of $p$.
+Variant: For a \typ{PADIC} $x$, the function
+ \fun{GEN}{Qp_log}{GEN x} is also available.
diff --git a/src/functions/transcendental/polylog b/src/functions/transcendental/polylog
new file mode 100644
index 0000000..0fa2a19
--- /dev/null
+++ b/src/functions/transcendental/polylog
@@ -0,0 +1,35 @@
+Function: polylog
+Section: transcendental
+C-Name: polylog0
+Prototype: LGD0,L,p
+Help: polylog(m,x,{flag=0}): m-th polylogarithm of x. flag is optional, and
+ can be 0: default, 1: D_m~-modified m-th polylog of x, 2: D_m-modified m-th
+ polylog of x, 3: P_m-modified m-th polylog of x.
+Doc: one of the different polylogarithms, depending on \fl:
+
+ If $\fl=0$ or is omitted: $m^\text{th}$ polylogarithm of $x$, i.e.~analytic
+ continuation of the power series $\text{Li}_m(x)=\sum_{n\ge1}x^n/n^m$
+ ($x < 1$). Uses the functional equation linking the values at $x$ and $1/x$
+ to restrict to the case $|x|\leq 1$, then the power series when
+ $|x|^2\le1/2$, and the power series expansion in $\log(x)$ otherwise.
+
+ Using $\fl$, computes a modified $m^\text{th}$ polylogarithm of $x$.
+ We use Zagier's notations; let $\Re_m$ denote $\Re$ or $\Im$ depending
+ on whether $m$ is odd or even:
+
+ If $\fl=1$: compute $\tilde D_m(x)$, defined for $|x|\le1$ by
+ $$\Re_m\left(\sum_{k=0}^{m-1} \dfrac{(-\log|x|)^k}{k!}\text{Li}_{m-k}(x)
+ +\dfrac{(-\log|x|)^{m-1}}{m!}\log|1-x|\right).$$
+
+ If $\fl=2$: compute $D_m(x)$, defined for $|x|\le1$ by
+ $$\Re_m\left(\sum_{k=0}^{m-1}\dfrac{(-\log|x|)^k}{k!}\text{Li}_{m-k}(x)
+ -\dfrac{1}{2}\dfrac{(-\log|x|)^m}{m!}\right).$$
+
+ If $\fl=3$: compute $P_m(x)$, defined for $|x|\le1$ by
+ $$\Re_m\left(\sum_{k=0}^{m-1}\dfrac{2^kB_k}{k!}(\log|x|)^k\text{Li}_{m-k}(x)
+ -\dfrac{2^{m-1}B_m}{m!}(\log|x|)^m\right).$$
+
+ These three functions satisfy the functional equation
+ $f_m(1/x) = (-1)^{m-1}f_m(x)$.
+Variant: Also available is
+ \fun{GEN}{gpolylog}{long m, GEN x, long prec} (\fl = 0).
diff --git a/src/functions/transcendental/psi b/src/functions/transcendental/psi
new file mode 100644
index 0000000..bc144ee
--- /dev/null
+++ b/src/functions/transcendental/psi
@@ -0,0 +1,7 @@
+Function: psi
+Section: transcendental
+C-Name: gpsi
+Prototype: Gp
+Help: psi(x): psi-function at x.
+Doc: the $\psi$-function of $x$, i.e.~the logarithmic derivative
+ $\Gamma'(x)/\Gamma(x)$.
diff --git a/src/functions/transcendental/sin b/src/functions/transcendental/sin
new file mode 100644
index 0000000..44f1981
--- /dev/null
+++ b/src/functions/transcendental/sin
@@ -0,0 +1,6 @@
+Function: sin
+Section: transcendental
+C-Name: gsin
+Prototype: Gp
+Help: sin(x): sine of x.
+Doc: sine of $x$.
diff --git a/src/functions/transcendental/sinh b/src/functions/transcendental/sinh
new file mode 100644
index 0000000..5bbfc29
--- /dev/null
+++ b/src/functions/transcendental/sinh
@@ -0,0 +1,6 @@
+Function: sinh
+Section: transcendental
+C-Name: gsinh
+Prototype: Gp
+Help: sinh(x): hyperbolic sine of x.
+Doc: hyperbolic sine of $x$.
diff --git a/src/functions/transcendental/sqr b/src/functions/transcendental/sqr
new file mode 100644
index 0000000..e459677
--- /dev/null
+++ b/src/functions/transcendental/sqr
@@ -0,0 +1,30 @@
+Function: sqr
+Section: transcendental
+C-Name: gsqr
+Prototype: G
+Help: sqr(x): square of x. NOT identical to x*x.
+Description:
+ (int):int        sqri($1)
+ (mp):mp          gsqr($1)
+ (gen):gen        gsqr($1)
+Doc: square of $x$. This operation is not completely
+ straightforward, i.e.~identical to $x * x$, since it can usually be
+ computed more efficiently (roughly one-half of the elementary
+ multiplications can be saved). Also, squaring a $2$-adic number increases
+ its precision. For example,
+ \bprog
+ ? (1 + O(2^4))^2
+ %1 = 1 + O(2^5)
+ ? (1 + O(2^4)) * (1 + O(2^4))
+ %2 = 1 + O(2^4)
+ @eprog\noindent
+ Note that this function is also called whenever one multiplies two objects
+ which are known to be \emph{identical}, e.g.~they are the value of the same
+ variable, or we are computing a power.
+ \bprog
+ ? x = (1 + O(2^4)); x * x
+ %3 = 1 + O(2^5)
+ ? (1 + O(2^4))^4
+ %4 = 1 + O(2^6)
+ @eprog\noindent
+ (note the difference between \kbd{\%2} and \kbd{\%3} above).
diff --git a/src/functions/transcendental/sqrt b/src/functions/transcendental/sqrt
new file mode 100644
index 0000000..8151c2d
--- /dev/null
+++ b/src/functions/transcendental/sqrt
@@ -0,0 +1,19 @@
+Function: sqrt
+Section: transcendental
+C-Name: gsqrt
+Prototype: Gp
+Help: sqrt(x): square root of x.
+Description:
+ (real):gen           sqrtr($1)
+ (gen):gen:prec       gsqrt($1, prec)
+Doc: principal branch of the square root of $x$, defined as $\sqrt{x} =
+ \exp(\log x / 2)$. In particular, we have
+ $\text{Arg}(\text{sqrt}(x))\in{} ]-\pi/2, \pi/2]$, and if $x\in \R$ and $x<0$,
+ then the result is complex with positive imaginary part.
+
+ Intmod a prime $p$, \typ{PADIC} and \typ{FFELT} are allowed as arguments. In
+ the first 2 cases (\typ{INTMOD}, \typ{PADIC}), the square root (if it
+ exists) which is returned is the one whose first $p$-adic digit is in the
+ interval $[0,p/2]$. For other arguments, the result is undefined.
+Variant: For a \typ{PADIC} $x$, the function
+ \fun{GEN}{Qp_sqrt}{GEN x} is also available.
diff --git a/src/functions/transcendental/sqrtn b/src/functions/transcendental/sqrtn
new file mode 100644
index 0000000..64b045d
--- /dev/null
+++ b/src/functions/transcendental/sqrtn
@@ -0,0 +1,47 @@
+Function: sqrtn
+Section: transcendental
+C-Name: gsqrtn
+Prototype: GGD&p
+Help: sqrtn(x,n,{&z}): nth-root of x, n must be integer. If present, z is
+ set to a suitable root of unity to recover all solutions. If it was not
+ possible, z is set to zero.
+Doc: principal branch of the $n$th root of $x$,
+ i.e.~such that $\text{Arg}(\text{sqrt}(x))\in{} ]-\pi/n, \pi/n]$. Intmod
+ a prime and $p$-adics are allowed as arguments.
+
+ If $z$ is present, it is set to a suitable root of unity allowing to
+ recover all the other roots. If it was not possible, z is
+ set to zero. In the case this argument is present and no square root exist,
+ $0$ is returned instead or raising an error.
+ \bprog
+ ? sqrtn(Mod(2,7), 2)
+ %1 = Mod(4, 7)
+ ? sqrtn(Mod(2,7), 2, &z); z
+ %2 = Mod(6, 7)
+ ? sqrtn(Mod(2,7), 3)
+   ***   at top-level: sqrtn(Mod(2,7),3)
+   ***                 ^-----------------
+   *** sqrtn: nth-root does not exist in gsqrtn.
+ ? sqrtn(Mod(2,7), 3,  &z)
+ %2 = 0
+ ? z
+ %3 = 0
+ @eprog
+
+ The following script computes all roots in all possible cases:
+ \bprog
+ sqrtnall(x,n)=
+ { my(V,r,z,r2);
+   r = sqrtn(x,n, &z);
+   if (!z, error("Impossible case in sqrtn"));
+   if (type(x) == "t_INTMOD" || type(x)=="t_PADIC",
+     r2 = r*z; n = 1;
+     while (r2!=r, r2*=z;n++));
+   V = vector(n); V[1] = r;
+   for(i=2, n, V[i] = V[i-1]*z);
+   V
+ }
+ addhelp(sqrtnall,"sqrtnall(x,n):compute the vector of nth-roots of x");
+ @eprog\noindent
+Variant: If $x$ is a \typ{PADIC}, the function
+ \fun{GEN}{Qp_sqrt}{GEN x, GEN n, GEN *z} is also available.
diff --git a/src/functions/transcendental/tan b/src/functions/transcendental/tan
new file mode 100644
index 0000000..12a2d40
--- /dev/null
+++ b/src/functions/transcendental/tan
@@ -0,0 +1,6 @@
+Function: tan
+Section: transcendental
+C-Name: gtan
+Prototype: Gp
+Help: tan(x): tangent of x.
+Doc: tangent of $x$.
diff --git a/src/functions/transcendental/tanh b/src/functions/transcendental/tanh
new file mode 100644
index 0000000..bf035de
--- /dev/null
+++ b/src/functions/transcendental/tanh
@@ -0,0 +1,6 @@
+Function: tanh
+Section: transcendental
+C-Name: gtanh
+Prototype: Gp
+Help: tanh(x): hyperbolic tangent of x.
+Doc: hyperbolic tangent of $x$.
diff --git a/src/functions/transcendental/teichmuller b/src/functions/transcendental/teichmuller
new file mode 100644
index 0000000..241f29b
--- /dev/null
+++ b/src/functions/transcendental/teichmuller
@@ -0,0 +1,7 @@
+Function: teichmuller
+Section: transcendental
+C-Name: teich
+Prototype: G
+Help: teichmuller(x): teichmuller character of p-adic number x.
+Doc: Teichm\"uller character of the $p$-adic number $x$, i.e. the unique
+ $(p-1)$-th root of unity congruent to $x / p^{v_p(x)}$ modulo $p$.
diff --git a/src/functions/transcendental/theta b/src/functions/transcendental/theta
new file mode 100644
index 0000000..51ee027
--- /dev/null
+++ b/src/functions/transcendental/theta
@@ -0,0 +1,7 @@
+Function: theta
+Section: transcendental
+C-Name: theta
+Prototype: GGp
+Help: theta(q,z): Jacobi sine theta-function.
+Doc: Jacobi sine theta-function
+ $$ \theta_1(z, q) = 2q^{1/4} \sum_{n\geq 0} (-1)^n q^{n(n+1)} \sin((2n+1)z).$$
diff --git a/src/functions/transcendental/thetanullk b/src/functions/transcendental/thetanullk
new file mode 100644
index 0000000..56a9c08
--- /dev/null
+++ b/src/functions/transcendental/thetanullk
@@ -0,0 +1,12 @@
+Function: thetanullk
+Section: transcendental
+C-Name: thetanullk
+Prototype: GLp
+Help: thetanullk(q,k): k-th derivative at z=0 of theta(q,z).
+Doc: $k$-th derivative at $z=0$ of $\kbd{theta}(q,z)$.
+Variant:
+ \fun{GEN}{vecthetanullk}{GEN q, long k, long prec} returns the vector
+ of all $\dfrac{d^i\theta}{dz^i}(q,0)$ for all odd $i = 1, 3, \dots, 2k-1$.
+ \fun{GEN}{vecthetanullk_tau}{GEN tau, long k, long prec} returns
+ \kbd{vecthetanullk\_tau} at $q = \exp(2i\pi \kbd{tau})$.
+
diff --git a/src/functions/transcendental/weber b/src/functions/transcendental/weber
new file mode 100644
index 0000000..e33e77a
--- /dev/null
+++ b/src/functions/transcendental/weber
@@ -0,0 +1,23 @@
+Function: weber
+Section: transcendental
+C-Name: weber0
+Prototype: GD0,L,p
+Help: weber(x,{flag=0}): One of Weber's f function of x. flag is optional,
+ and can be 0: default, function f(x)=exp(-i*Pi/24)*eta((x+1)/2)/eta(x),
+ 1: function f1(x)=eta(x/2)/eta(x)
+ 2: function f2(x)=sqrt(2)*eta(2*x)/eta(x). Note that
+ j = (f^24-16)^3/f^24 = (f1^24+16)^3/f1^24 = (f2^24+16)^3/f2^24.
+Doc: one of Weber's three $f$ functions.
+ If $\fl=0$, returns
+ $$f(x)=\exp(-i\pi/24)\cdot\eta((x+1)/2)\,/\,\eta(x) \quad\hbox{such that}\quad
+ j=(f^{24}-16)^3/f^{24}\,,$$
+ where $j$ is the elliptic $j$-invariant  (see the function \kbd{ellj}).
+ If $\fl=1$, returns
+ $$f_1(x)=\eta(x/2)\,/\,\eta(x)\quad\hbox{such that}\quad
+ j=(f_1^{24}+16)^3/f_1^{24}\,.$$
+ Finally, if $\fl=2$, returns
+ $$f_2(x)=\sqrt{2}\eta(2x)\,/\,\eta(x)\quad\hbox{such that}\quad
+ j=(f_2^{24}+16)^3/f_2^{24}.$$
+ Note the identities $f^8=f_1^8+f_2^8$ and $ff_1f_2=\sqrt2$.
+Variant: Also available are \fun{GEN}{weberf}{GEN x, long prec},
+ \fun{GEN}{weberf1}{GEN x, long prec} and \fun{GEN}{weberf2}{GEN x, long prec}.
diff --git a/src/functions/transcendental/zeta b/src/functions/transcendental/zeta
new file mode 100644
index 0000000..4493628
--- /dev/null
+++ b/src/functions/transcendental/zeta
@@ -0,0 +1,17 @@
+Function: zeta
+Section: transcendental
+C-Name: gzeta
+Prototype: Gp
+Help: zeta(s): Riemann zeta function at s with s a complex or a p-adic number.
+Doc: For $s$ a complex number, Riemann's zeta
+ function \sidx{Riemann zeta-function} $\zeta(s)=\sum_{n\ge1}n^{-s}$,
+ computed using the \idx{Euler-Maclaurin} summation formula, except
+ when $s$ is of type integer, in which case it is computed using
+ Bernoulli numbers\sidx{Bernoulli numbers} for $s\le0$ or $s>0$ and
+ even, and using modular forms for $s>0$ and odd.
+
+ For $s$ a $p$-adic number, Kubota-Leopoldt zeta function at $s$, that
+ is the unique continuous $p$-adic function on the $p$-adic integers
+ that interpolates the values of $(1 - p^{-k}) \zeta(k)$ at negative
+ integers $k$ such that $k \equiv 1 \pmod{p-1}$ (resp. $k$ is odd) if
+ $p$ is odd (resp. $p = 2$).
diff --git a/src/gp/gp.c b/src/gp/gp.c
new file mode 100644
index 0000000..c7af7e2
--- /dev/null
+++ b/src/gp/gp.c
@@ -0,0 +1,2627 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/**                                                               **/
+/**                        PARI CALCULATOR                        **/
+/**                                                               **/
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#include "gp.h"
+
+#ifdef _WIN32
+#  include <windows.h>
+#  include "../systems/mingw/mingw.h"
+#  ifndef WINCE
+#    include <process.h>
+#  endif
+#endif
+
+/********************************************************************/
+/**                                                                **/
+/**                            STRINGS                             **/
+/**                                                                **/
+/********************************************************************/
+
+static void
+skip_space(char **s) {
+  char *t = *s;
+  while (isspace((int)*t)) t++;
+  *s = t;
+}
+static void
+skip_alpha(char **s) {
+  char *t = *s;
+  while (isalpha((int)*t)) t++;
+  *s = t;
+}
+
+static char *
+translate(char **src, char *s, char *entry)
+{
+  char *t = *src;
+  while (*t)
+  {
+    while (*t == '\\')
+    {
+      switch(*++t)
+      {
+        case 'e':  *s='\033'; break; /* escape */
+        case 'n':  *s='\n'; break;
+        case 't':  *s='\t'; break;
+        default:   *s=*t;
+                   if (!*t) pari_err(e_SYNTAX,"unfinished string",s,entry);
+      }
+      t++; s++;
+    }
+    if (*t == '"')
+    {
+      if (t[1] != '"') break;
+      t += 2; continue;
+    }
+    *s++ = *t++;
+  }
+  *s=0; *src=t; return s;
+}
+
+static void
+matchQ(char *s, char *entry)
+{
+  if (*s != '"')
+    pari_err(e_SYNTAX,"expected character: '\"' instead of",s,entry);
+}
+
+/*  Read a "string" from src. Format then copy it, starting at s. Return
+ *  pointer to char following the end of the input string */
+static char *
+readstring(char *src, char *s, char *entry)
+{
+  matchQ(src, entry); src++; s = translate(&src, s, entry);
+  matchQ(src, entry); return src+1;
+}
+/*******************************************************************/
+/**                                                               **/
+/**                    TEXMACS-SPECIFIC STUFF                     **/
+/**                                                               **/
+/*******************************************************************/
+static int tm_is_waiting = 0, tm_did_complete = 0;
+
+/* tell TeXmacs GP will start outputing data */
+static void
+tm_start_output(void)
+{
+  if (!tm_is_waiting) { printf("%cverbatim:",DATA_BEGIN); fflush(stdout); }
+  tm_is_waiting = 1;
+}
+/* tell TeXmacs GP is done and is waiting for new data */
+static void
+tm_end_output(void)
+{
+  if (tm_is_waiting) { printf("%c", DATA_END); fflush(stdout); }
+  tm_is_waiting = 0;
+}
+static char *
+fgets_texmacs(char *s, int n, FILE *f)
+{
+  if (!tm_did_complete)
+  {
+    tm_start_output(); tm_end_output(); /* tell TeXmacs we need input */
+  }
+  return fgets(s,n,f);
+}
+
+#ifdef READLINE
+typedef struct {
+  char *cmd;
+  long n; /* number of args */
+  char **v; /* args */
+} tm_cmd;
+
+static void
+parse_texmacs_command(tm_cmd *c, const char *ch)
+{
+  long l = strlen(ch);
+  char *t, *s = (char*)ch, *send = s+l-1;
+  char **A;
+  pari_stack s_A;
+
+  if (*s != DATA_BEGIN || *send-- != DATA_END)
+    pari_err(e_MISC, "missing DATA_[BEGIN | END] in TeXmacs command");
+  s++;
+  if (strncmp(s, "special:", 8)) pari_err(e_MISC, "unrecognized TeXmacs command");
+  s += 8;
+  if (*s != '(' || *send-- != ')')
+    pari_err(e_MISC, "missing enclosing parentheses for TeXmacs command");
+  s++; t = s;
+  skip_alpha(&s);
+  c->cmd = pari_strndup(t, s - t);
+  pari_stack_init(&s_A,sizeof(*A),(void**)&A);
+  for (c->n = 0; s <= send; c->n++)
+  {
+    char *u = (char*)pari_malloc(strlen(s) + 1);
+    skip_space(&s);
+    if (*s == '"') s = readstring(s, u, t);
+    else
+    { /* read integer */
+      t = s;
+      while (isdigit((int)*s)) s++;
+      strncpy(u, t, s - t); u[s-t] = 0;
+    }
+    pari_stack_pushp(&s_A, u);
+  }
+  c->v = A;
+}
+
+static void
+free_cmd(tm_cmd *c)
+{
+  while (c->n--) pari_free((void*)c->v[c->n]);
+  pari_free((void*)c->v);
+}
+
+static void
+handle_texmacs_command(const char *s)
+{
+  tm_cmd c;
+  parse_texmacs_command(&c, s);
+  if (strcmp(c.cmd, "complete"))
+    pari_err(e_MISC,"Texmacs_stdin command %s not implemented", c.cmd);
+  if (c.n != 2)
+    pari_err(e_MISC,"was expecting 2 arguments for Texmacs_stdin command");
+  texmacs_completion(c.v[0], atol(c.v[1]));
+  free_cmd(&c);
+  tm_did_complete = 1;
+}
+#else
+static void
+handle_texmacs_command(const char *s)
+{ (void)s;pari_err(e_MISC, "readline not available"); }
+#endif
+
+/*******************************************************************/
+/**                                                               **/
+/**                          BUFFERS                              **/
+/**                                                               **/
+/*******************************************************************/
+static Buffer **bufstack;
+static pari_stack s_bufstack;
+
+static void
+pop_buffer(void)
+{
+  if (s_bufstack.n)
+    delete_buffer( bufstack[ --s_bufstack.n ] );
+}
+
+/* kill all buffers until B is met or nothing is left */
+static void
+kill_buffers_upto(Buffer *B)
+{
+  while (s_bufstack.n) {
+    if (bufstack[ s_bufstack.n-1 ] == B) break;
+    pop_buffer();
+  }
+}
+static void
+kill_buffers_upto_including(Buffer *B)
+{
+  while (s_bufstack.n) {
+    if (bufstack[ s_bufstack.n-1 ] == B) { pop_buffer(); break; }
+    pop_buffer();
+  }
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                             HELP                               **/
+/**                                                                **/
+/********************************************************************/
+static int disable_exception_handler = 0;
+#define BLOCK_EH_START                \
+{                                     \
+  int block=disable_exception_handler;\
+  disable_exception_handler = 1;
+
+#define BLOCK_EH_END                \
+  disable_exception_handler = block;\
+}
+static char *Help;
+
+static char *
+init_help(void)
+{
+  char *h = os_getenv("GPHELP");
+# ifdef GPHELP
+  if (!h) h = (char*)GPHELP;
+# endif
+#ifdef _WIN32
+  win32_set_pdf_viewer();
+#endif
+  if (h) h = pari_strdup(h);
+  return h;
+}
+
+static void
+hit_return(void)
+{
+  int c;
+  if (GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS)) return;
+  BLOCK_EH_START
+  pari_puts("/*-- (type RETURN to continue) --*/");
+  pari_flush();
+  /* if called from a readline callback, may be in a funny TTY mode */
+  do c = fgetc(stdin); while (c >= 0 && c != '\n' && c != '\r');
+  pari_putc('\n');
+  BLOCK_EH_END
+}
+static void
+gp_ask_confirm(const char *s)
+{
+  err_printf(s);
+  err_printf(". OK ? (^C if not)\n");
+  hit_return();
+}
+
+static int
+has_ext_help(void) { return (Help && *Help); }
+
+static int
+compare_str(char **s1, char **s2) { return strcmp(*s1, *s2); }
+
+/* Print all elements of list in columns, pausing every nbli lines
+ * if nbli is non-zero.
+ * list is a NULL terminated list of function names
+ */
+void
+print_fun_list(char **list, long nbli)
+{
+  long i=0, j=0, maxlen=0, nbcol,len, w = term_width();
+  char **l;
+
+  while (list[i]) i++;
+  qsort (list, i, sizeof(char *), (QSCOMP)compare_str);
+
+  for (l=list; *l; l++)
+  {
+    len = strlen(*l);
+    if (len > maxlen) maxlen=len;
+  }
+  maxlen++; nbcol= w / maxlen;
+  if (nbcol * maxlen == w) nbcol--;
+  if (!nbcol) nbcol = 1;
+
+  pari_putc('\n'); i=0;
+  for (l=list; *l; l++)
+  {
+    pari_puts(*l); i++;
+    if (i >= nbcol)
+    {
+      i=0; pari_putc('\n');
+      if (nbli && j++ > nbli) { j = 0; hit_return(); }
+      continue;
+    }
+    len = maxlen - strlen(*l);
+    while (len--) pari_putc(' ');
+  }
+  if (i) pari_putc('\n');
+}
+
+static void
+commands(long n)
+{
+  long i;
+  entree *ep;
+  char **t_L;
+  pari_stack s_L;
+
+  pari_stack_init(&s_L, sizeof(*t_L), (void**)&t_L);
+  for (i = 0; i < functions_tblsz; i++)
+    for (ep = functions_hash[i]; ep; ep = ep->next)
+    {
+      long m;
+      switch (EpVALENCE(ep))
+      {
+        case EpVAR:
+          if (typ((GEN)ep->value) == t_CLOSURE) break;
+          /* fall through */
+        case EpNEW: continue;
+      }
+      m = ep->menu;
+      if ((n < 0 && m && m < 13) || m == n) pari_stack_pushp(&s_L, (void*)ep->name);
+    }
+  pari_stack_pushp(&s_L, NULL);
+  print_fun_list(t_L, term_height()-4);
+  pari_stack_delete(&s_L);
+}
+
+static void
+center(const char *s)
+{
+  long i, l = strlen(s), pad = term_width() - l;
+  char *buf, *u;
+
+  if (pad<0) pad=0; else pad >>= 1;
+  u = buf = (char*)pari_malloc(l + pad + 2);
+  for (i=0; i<pad; i++) *u++ = ' ';
+  while (*s) *u++ = *s++;
+  *u++ = '\n'; *u = 0;
+  pari_puts(buf); pari_free(buf);
+}
+
+static void
+usage(char *s)
+{
+  printf("### Usage: %s [options] [GP files]\n", s);
+  printf("Available options:\n");
+  printf("  [-f,--fast]\t\tFast start: do not read .gprc\n");
+  printf("  [-q,--quiet]\t\tQuiet mode: do not print banner and history numbers\n");
+  printf("  [-s stacksize]\tStart with the PARI stack of given size (in bytes)\n");
+  printf("  [--default key=val]\tExecute default(key,val) on startup\n");
+  printf("  [--emacs]\t\tRun as if in Emacs shell\n");
+  printf("  [--help]\t\tPrint this message\n");
+  printf("  [--test]\t\tTest mode. No history, wrap long lines (bench only)\n");
+  printf("  [--texmacs]\t\tRun as if using TeXmacs frontend\n");
+  printf("  [--version]\t\tOutput version info and exit\n");
+  printf("  [--version-short]\tOutput version number and exit\n\n");
+  exit(0);
+}
+
+static void
+community(void)
+{
+  print_text("The PARI/GP distribution includes a reference manual, a \
+tutorial, a reference card and quite a few examples. They have been installed \
+in the directory ");
+  pari_puts("  ");
+  pari_puts(pari_datadir);
+  pari_puts("\nYou can also download them from http://pari.math.u-bordeaux.fr/.\
+\n\nThree mailing lists are devoted to PARI:\n\
+  - pari-announce (moderated) to announce major version changes.\n\
+  - pari-dev for everything related to the development of PARI, including\n\
+    suggestions, technical questions, bug reports and patch submissions.\n\
+  - pari-users for everything else!\n\
+To subscribe, send an empty message to\n\
+  <pari_list_name>-request at pari.math.u-bordeaux.fr\n\
+with a Subject: field containing the word 'subscribe'.\n\n");
+  print_text("An archive is kept at the WWW site mentioned above. You can also \
+reach the authors at pari at math.u-bordeaux.fr (answer not guaranteed)."); }
+
+static void
+gentypes(void)
+{
+  pari_puts("List of the PARI types:\n\
+  t_INT    : long integers     [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\
+  t_REAL   : long real numbers [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\
+  t_INTMOD : integermods       [ code ] [ mod  ] [ integer ]\n\
+  t_FRAC   : irred. rationals  [ code ] [ num. ] [ den. ]\n\
+  t_FFELT  : finite field elt. [ code ] [ cod2 ] [ elt ] [ mod ] [ p ]\n\
+  t_COMPLEX: complex numbers   [ code ] [ real ] [ imag ]\n\
+  t_PADIC  : p-adic numbers    [ cod1 ] [ cod2 ] [ p ] [ p^r ] [ int ]\n\
+  t_QUAD   : quadratic numbers [ cod1 ] [ mod  ] [ real ] [ imag ]\n\
+  t_POLMOD : poly mod          [ code ] [ mod  ] [ polynomial ]\n\
+  -------------------------------------------------------------\n\
+  t_POL    : polynomials       [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\
+  t_SER    : power series      [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\
+  t_RFRAC  : irred. rat. func. [ code ] [ num. ] [ den. ]\n\
+  t_QFR    : real qfb          [ code ] [ a ] [ b ] [ c ] [ del ]\n\
+  t_QFI    : imaginary qfb     [ code ] [ a ] [ b ] [ c ]\n\
+  t_VEC    : row vector        [ code ] [  x_1  ] ... [  x_k  ]\n\
+  t_COL    : column vector     [ code ] [  x_1  ] ... [  x_k  ]\n\
+  t_MAT    : matrix            [ code ] [ col_1 ] ... [ col_k ]\n\
+  t_LIST   : list              [ code ] [ n ] [ nmax ][ vec ]\n\
+  t_STR    : string            [ code ] [ man_1 ] ... [ man_k ]\n\
+  t_VECSMALL: vec. small ints  [ code ] [ x_1 ] ... [ x_k ]\n\
+  t_CLOSURE: functions [ code ] [ arity ] [ code ] [ operand ] [ data ] [ text ]\n\
+  t_ERROR  : error context     [ code ] [ errnum ] [ dat_1 ] ... [ dat_k ]\n\
+\n");
+}
+
+static void
+menu_commands(void)
+{
+  pari_puts("Help topics: for a list of relevant subtopics, type ?n for n in\n\
+  0: user-defined functions (aliases, installed and user functions)\n\
+  1: Standard monadic or dyadic OPERATORS\n\
+  2: CONVERSIONS and similar elementary functions\n\
+  3: TRANSCENDENTAL functions\n\
+  4: NUMBER THEORETICAL functions\n\
+  5: Functions related to ELLIPTIC CURVES\n\
+  6: Functions related to general NUMBER FIELDS\n\
+  7: POLYNOMIALS and power series\n\
+  8: Vectors, matrices, LINEAR ALGEBRA and sets\n\
+  9: SUMS, products, integrals and similar functions\n\
+ 10: GRAPHIC functions\n\
+ 11: PROGRAMMING under GP\n\
+ 12: The PARI community\n\
+\n\
+Also:\n\
+  ? functionname (short on-line help)\n\
+  ?\\             (keyboard shortcuts)\n\
+  ?.             (member functions)\n");
+  if (has_ext_help()) pari_puts("\
+Extended help (if available):\n\
+  ??             (opens the full user's manual in a dvi previewer)\n\
+  ??  tutorial / refcard / libpari (tutorial/reference card/libpari manual)\n\
+  ??  keyword    (long help text about \"keyword\" from the user's manual)\n\
+  ??? keyword    (a propos: list of related functions).");
+}
+
+static void
+slash_commands(void)
+{
+  pari_puts("#       : enable/disable timer\n\
+##      : print time for last result\n\
+\\\\      : comment up to end of line\n\
+\\a {n}  : print result in raw format (readable by PARI)\n\
+\\B {n}  : print result in beautified format\n\
+\\c      : list all commands (same effect as ?*)\n\
+\\d      : print all defaults\n\
+\\e {n}  : enable/disable echo (set echo=n)\n\
+\\g {n}  : set debugging level\n\
+\\gf{n}  : set file debugging level\n\
+\\gm{n}  : set memory debugging level\n\
+\\h {m-n}: hashtable information\n\
+\\l {f}  : enable/disable logfile (set logfile=f)\n\
+\\m {n}  : print result in prettymatrix format\n\
+\\o {n}  : set output method (0=raw, 1=prettymatrix, 2=prettyprint, 3=2-dim)\n\
+\\p {n}  : change real precision\n\
+\\ps{n}  : change series precision\n\
+\\q      : quit completely this GP session\n\
+\\r {f}  : read in a file\n\
+\\s      : print stack information\n\
+\\t      : print the list of PARI types\n\
+\\u      : print the list of user-defined functions\n\
+\\um     : print the list of user-defined member functions\n\
+\\v      : print current version of GP\n\
+\\w {nf} : write to a file\n\
+\\x {n}  : print complete inner structure of result\n\
+\\y {n}  : disable/enable automatic simplification (set simplify=n)\n\
+\n\
+{f}=optional filename. {n}=optional integer\n");
+}
+
+static void
+member_commands(void)
+{
+  pari_puts("\
+Member functions, followed by relevant objects\n\n\
+a1-a6, b2-b8, c4-c6 : coeff. of the curve.         ell\n\
+area : area                                        ell\n\
+bid  : big ideal                     bid,                     bnr\n\
+bnf  : big number field                                   bnf,bnr\n\
+clgp : class group                   bid,                 bnf,bnr\n\
+cyc  : cyclic decomposition (SNF)    bid,     clgp,ell,   bnf,bnr\n\
+diff, codiff: different and codifferent                nf,bnf,bnr\n\
+disc : discriminant                                ell,nf,bnf,bnr,rnf\n\
+e, f : inertia/residue  degree           prid\n\
+fu   : fundamental units                                  bnf,bnr\n\
+gen  : generators                    bid,prid,clgp,ell,   bnf,bnr,    gal\n\
+group: group                                       ell,          ,rnf,gal\n\
+index: index                                           nf,bnf,bnr\n\
+j    : j-invariant                                 ell\n");
+/* split: some compilers can't handle long constant strings */
+  pari_puts("\
+mod  : modulus                       bid,                     bnr,    gal\n\
+nf   : number field                                    nf,bnf,bnr,rnf\n\
+no   : number of elements            bid,     clgp,ell,   bnf,bnr\n\
+omega, eta: [w1,w2] and [eta1, eta2]               ell\n\
+orders: relative orders of generators                                 gal\n\
+p    : rational prime                    prid,     ell,           rnf,gal\n\
+pol  : defining polynomial                             nf,bnf,bnr,    gal\n\
+polabs: defining polynomial over Q                                rnf\n\
+reg  : regulator                                          bnf,bnr\n\
+roots: roots                                       ell,nf,bnf,bnr,    gal\n\
+sign,r1,r2 : signature                                 nf,bnf,bnr\n\
+t2   : t2 matrix                                       nf,bnf,bnr\n\
+tate : Tate's [u^2, u, q, [a,b]]                   ell\n\
+tu   : torsion unit and its order                         bnf,bnr\n\
+zk   : integral basis                                  nf,bnf,bnr,rnf\n\
+zkst : structure of (Z_K/m)*         bid,                     bnr\n");
+}
+
+#define QUOTE "_QUOTE"
+#define DOUBQUOTE "_DOUBQUOTE"
+#define BACKQUOTE "_BACKQUOTE"
+
+static char *
+_cat(char *s, const char *t)
+{
+  *s = 0; strcat(s,t); return s + strlen(t);
+}
+
+static char *
+filter_quotes(const char *s)
+{
+  int i, l = strlen(s);
+  int quote = 0;
+  int backquote = 0;
+  int doubquote = 0;
+  char *str, *t;
+
+  for (i=0; i < l; i++)
+    switch(s[i])
+    {
+      case '\'': quote++; break;
+      case '`' : backquote++; break;
+      case '"' : doubquote++;
+    }
+  str = (char*)pari_malloc(l + quote * (strlen(QUOTE)-1)
+                          + doubquote * (strlen(DOUBQUOTE)-1)
+                          + backquote * (strlen(BACKQUOTE)-1) + 1);
+  t = str;
+  for (i=0; i < l; i++)
+    switch(s[i])
+    {
+      case '\'': t = _cat(t, QUOTE); break;
+      case '`' : t = _cat(t, BACKQUOTE); break;
+      case '"' : t = _cat(t, DOUBQUOTE); break;
+      default: *t++ = s[i];
+    }
+  *t = 0; return str;
+}
+
+static int
+nl_read(char *s) { size_t l = strlen(s); return s[l-1] == '\n'; }
+
+#define nbof(a) sizeof(a) / sizeof(a[0])
+/* query external help program for s. num < 0 [keyword] or chapter number */
+static void
+external_help(const char *s, int num)
+{
+  long nbli = term_height()-3, li = 0;
+  char buf[256], *str;
+  const char *opt = "", *ar = "", *cdir = "";
+  char *t, *help = Help;
+  pariFILE *z;
+  FILE *f;
+
+  if (!has_ext_help()) pari_err(e_MISC,"no external help program");
+  t = filter_quotes(s);
+  if (num < 0)
+    opt = "-k";
+  else if (t[strlen(t)-1] != '@')
+    ar = stack_sprintf("@%d",num);
+#ifdef _WIN32
+  if (*help=='@')
+  {
+    const char *basedir = win32_basedir();
+    help++;
+    cdir = stack_sprintf("%c:& cd %s & ", *basedir, basedir);
+  }
+#endif
+  str=stack_sprintf("%s%s -fromgp %s %c%s%s%c",cdir,help,opt,
+                                               SHELL_Q,t,ar,SHELL_Q);
+  z = try_pipe(str,0); f = z->file;
+  pari_free(t);
+  while (fgets(buf, nbof(buf), f))
+  {
+    if (!strncmp("ugly_kludge_done",buf,16)) break;
+    pari_puts(buf);
+    if (nl_read(buf) && ++li > nbli) { hit_return(); li = 0; }
+  }
+  pari_fclose(z);
+}
+
+const char *keyword_list[]={
+  "operator",
+  "libpari",
+  "member",
+  "integer",
+  "real",
+  "readline",
+  "refcard",
+  "tutorial",
+  "nf",
+  "bnf",
+  "bnr",
+  "ell",
+  "rnf",
+  "bid",
+  "modulus",
+  "prototype",
+  NULL
+};
+
+static int
+ok_external_help(char **s)
+{
+  long n;
+  if (!**s) return 1;
+  if (!isalpha((int)**s)) return 3; /* operator or section number */
+  if (!strncmp(*s,"t_",2)) { *s += 2; return 2; } /* type name */
+
+  for (n=0; keyword_list[n]; n++)
+    if (!strcmp(*s,keyword_list[n])) return 3;
+  return 0;
+}
+
+static void
+cut_trailing_garbage(char *s)
+{
+  char c;
+  while ( (c = *s++) )
+  {
+    if (c == '\\' && ! *s++) return; /* gobble next char, return if none. */
+    if (!is_keyword_char(c) && c != '@') { s[-1] = 0; return; }
+  }
+}
+
+static void
+digit_help(char *s, long flag)
+{
+  long n = atoi(s);
+  if (n < 0 || n > 15) pari_err(e_SYNTAX,"no such section in help: ?",s,s);
+  if (n == 12)
+    community();
+  else if (flag & h_LONG)
+    external_help(s,3);
+  else
+    commands(n);
+  return;
+}
+
+static void
+simple_help(const char *s1, const char *s2) { pari_printf("%s: %s\n", s1, s2); }
+
+static void
+default_help(char *s, long flag)
+{
+  if (flag & h_LONG)
+    external_help(stack_strcat("se:def,",s),3);
+  else
+    simple_help(s,"default");
+}
+
+static void
+aide0(const char *s0, int flag)
+{
+  const long long_help = flag & h_LONG;
+  long n;
+  entree *ep;
+  char *s = get_sep(s0);
+
+  if (isdigit((int)*s)) { digit_help(s,flag); return; }
+  if (flag & h_APROPOS) { external_help(s,-1); return; }
+  /* Get meaningful answer on '\ps 5' (e.g. from <F1>) */
+  if (*s == '\\') { char *t = s+1; skip_alpha(&t); *t = '\0'; }
+  if (isalpha((int)*s))
+  {
+    if (!strncmp(s, "default", 7))
+    { /* special-case ?default(dft_name), e.g. default(log) */
+      char *t = s+7;
+      skip_space(&t);
+      if (*t == '(')
+      {
+        t++; skip_space(&t);
+        cut_trailing_garbage(t);
+        if (pari_is_default(t)) { default_help(t,flag); return; }
+      }
+    }
+    cut_trailing_garbage(s);
+  }
+
+  if (long_help && (n = ok_external_help(&s))) { external_help(s,n); return; }
+  switch (*s)
+  {
+    case '*' : commands(-1); return;
+    case '\0': menu_commands(); return;
+    case '\\': slash_commands(); return;
+    case '.' : member_commands(); return;
+  }
+  ep = is_entry(s);
+  if (!ep)
+  {
+    if (pari_is_default(s))
+      default_help(s,flag);
+    else if (long_help)
+      external_help(s,3);
+    else if (!cb_pari_whatnow(pariOut, s,1))
+      simple_help(s,"unknown identifier");
+    return;
+  }
+
+  if (EpVALENCE(ep) == EpALIAS)
+  {
+    pari_printf("%s is aliased to:\n\n",s);
+    ep = do_alias(ep);
+  }
+  switch(EpVALENCE(ep))
+  {
+    case EpVAR:
+      if (!ep->help)
+      {
+        if (typ((GEN)ep->value)!=t_CLOSURE)
+          simple_help(s, "user defined variable");
+        else
+        {
+          GEN str = closure_get_text((GEN)ep->value);
+          if (typ(str) == t_VEC)
+            pari_printf("%s =\n  %Ps\n", ep->name, ep->value);
+        }
+        return;
+      }
+      break;
+
+    case EpINSTALL:
+      if (!ep->help) { simple_help(s, "installed function"); return; }
+      break;
+
+    case EpNEW:
+      if (!ep->help) { simple_help(s, "new identifier"); return; };
+      break;
+
+    default: /* built-in function */
+      if (!ep->help) pari_err_BUG("aide (no help found)"); /*paranoia*/
+      if (long_help) { external_help(ep->name,3); return; }
+  }
+  print_text(ep->help);
+}
+
+void
+aide(const char *s, long flag)
+{
+  pari_sp av = avma;
+  if ((flag & h_RL) == 0)
+  {
+    if (*s == '?') { flag |= h_LONG; s++; }
+    if (*s == '?') { flag |= h_APROPOS; s++; }
+  }
+  term_color(c_HELP); aide0(s,flag); term_color(c_NONE);
+  if ((flag & h_RL) == 0) pari_putc('\n');
+  avma = av;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         GP HEADER                              **/
+/**                                                                **/
+/********************************************************************/
+static char *
+what_readline(void)
+{
+#ifdef READLINE
+  const char *v = READLINE;
+  char *s = stack_malloc(3 + strlen(v) + 8);
+  (void)sprintf(s, "v%s %s", v, GP_DATA->use_readline? "enabled": "disabled");
+  return s;
+#else
+  return (char*)"not compiled in";
+#endif
+}
+
+static void
+print_shortversion(void)
+{
+  const ulong mask = (1UL<<PARI_VERSION_SHIFT) - 1;
+  ulong n = paricfg_version_code, major, minor, patch;
+
+  patch = n & mask; n >>= PARI_VERSION_SHIFT;
+  minor = n & mask; n >>= PARI_VERSION_SHIFT;
+  major = n;
+  printf("%lu.%lu.%lu\n", major,minor,patch); exit(0);
+}
+
+static char *
+what_cc(void)
+{
+  char *s;
+#ifdef GCC_VERSION
+#  ifdef __cplusplus
+#    define Format "(C++) %s"
+#  else
+#    define Format "%s"
+#  endif
+  s = stack_malloc(6 + strlen(GCC_VERSION) + 1);
+  (void)sprintf(s, Format, GCC_VERSION);
+#else
+#  ifdef _MSC_VER
+  s = stack_malloc(32);
+  (void)sprintf(s, "MSVC-%i", _MSC_VER);
+#  else
+  s = NULL;
+#  endif
+#endif
+  return s;
+}
+
+static void
+print_version(void)
+{
+  pari_sp av = avma;
+  char *buf, *ver = what_cc();
+  const char *date = paricfg_compiledate;
+
+  center(paricfg_version);
+  center(paricfg_buildinfo);
+  buf = stack_malloc(strlen(date) +  32 + (ver? strlen(ver): 0));
+  if (ver) (void)sprintf(buf, "compiled: %s, %s", date, ver);
+  else     (void)sprintf(buf, "compiled: %s", date);
+  center(buf);
+  sprintf(buf, "threading engine: %s",paricfg_mt_engine);
+  center(buf);
+  ver = what_readline();
+  buf = stack_malloc(strlen(ver) + 64);
+  (void)sprintf(buf, "(readline %s, extended help%s enabled)", ver,
+                has_ext_help()? "": " not");
+  center(buf); avma = av;
+}
+
+static void
+gp_head(void)
+{
+#ifdef READLINE
+  if (GP_DATA->flags & gpd_TEXMACS)
+    printf("%ccommand:(cas-supports-completions-set! \"pari\")%c\n",
+           DATA_BEGIN, DATA_END);
+#endif
+  print_version();
+  pari_putc('\n');
+  center("Copyright (C) 2000-2014 The PARI Group");
+  pari_putc('\n');
+  print_text("PARI/GP is free software, covered by the GNU General Public \
+License, and comes WITHOUT ANY WARRANTY WHATSOEVER.");
+  pari_puts("\nType ? for help, \\q to quit.\n");
+  print_text("Type ?12 for how to get moral (and possibly technical) support.");
+  pari_printf("\nparisize = %lu, primelimit = %lu\n",
+              top - bot, GP_DATA->primelimit);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         METACOMMANDS                           **/
+/**                                                                **/
+/********************************************************************/
+#define pariputs_opt(s) if (!(GP_DATA->flags & gpd_QUIET)) pari_puts(s)
+
+void
+gp_quit(long exitcode)
+{
+  if (Help) pari_free((void*)Help);
+  free_graph();
+  pari_close();
+  kill_buffers_upto(NULL);
+  pariputs_opt("Goodbye!\n");
+  if (GP_DATA->flags & gpd_TEXMACS) tm_end_output();
+  exit(exitcode);
+}
+
+static GEN
+gpreadbin(const char *s, int *vector)
+{
+  GEN x = readbin(s,pari_infile, vector);
+  popinfile();
+  if (!x) pari_err_FILE("input file",s);
+  return x;
+}
+
+static void
+escape(char *tch, int ismain)
+{
+  const char *s;
+  char c;
+
+  if (compatible != NONE)
+  {
+    s = tch;
+    while (*s)
+      if (*s++ == '=')
+      {
+        GEN (*f)(const char *v, long flag) = NULL;
+        long len = (s-tch) - 1;
+        if      (!strncmp(tch,"precision",len))    f = sd_realprecision;
+        else if (!strncmp(tch,"serieslength",len)) f = sd_seriesprecision;
+        else if (!strncmp(tch,"format",len))       f = sd_format;
+        else if (!strncmp(tch,"prompt",len))       f = sd_prompt;
+        if (f) { (void)f(s, d_ACKNOWLEDGE); return; }
+        break;
+      }
+  }
+  s = tch;
+  switch ((c = *s++))
+  {
+    case 'w': case 'x': case 'a': case 'b': case 'B': case 'm':
+    { /* history things */
+      long d;
+      GEN x;
+      if (c != 'w' && c != 'x') d = get_int(s,0);
+      else
+      {
+        d = atol(s); if (*s == '-') s++;
+        while (isdigit((int)*s)) s++;
+      }
+      x = gp_history(GP_DATA->hist, d, tch+1,tch-1);
+      switch (c)
+      {
+        case 'B':
+        { /* prettyprinter */
+          gp_data G = *GP_DATA; /* copy */
+          gp_hist   h = *(G.hist); /* copy */
+          pariout_t f = *(G.fmt);  /* copy */
+
+          G.hist = &h; h.total = 0; /* no hist number */
+          G.fmt  = &f; f.prettyp = f_PRETTY;
+          G.flags &= ~(gpd_TEST|gpd_TEXMACS);
+          G.lim_lines = 0;
+          gp_output(x, &G); break;
+        }
+        case 'a': brute   (x, GP_DATA->fmt->format, -1); break;
+        case 'b': /* fall through */
+        case 'm': matbrute(x, GP_DATA->fmt->format, -1); break;
+        case 'x': dbgGEN(x, get_int(s, -1)); break;
+        case 'w':
+          s = get_sep(s); if (!*s) s = current_logfile;
+          write0(s, mkvec(x)); return;
+      }
+      pari_putc('\n'); return;
+    }
+
+    case 'c': commands(-1); break;
+    case 'd': (void)setdefault(NULL,NULL,d_SILENT); break;
+    case 'e':
+      s = get_sep(s);
+      if (!*s) s = (GP_DATA->echo)? "0": "1";
+      (void)sd_echo(s,d_ACKNOWLEDGE); break;
+    case 'g':
+      switch (*s)
+      {
+        case 'm': s++; (void)sd_debugmem(*s? s: NULL,d_ACKNOWLEDGE); break;
+        case 'f': s++; (void)sd_debugfiles(*s? s: NULL,d_ACKNOWLEDGE); break;
+        default : (void)sd_debug(*s? s: NULL,d_ACKNOWLEDGE); break;
+      }
+      break;
+    case 'h': print_functions_hash(s); break;
+    case 'l':
+      s = get_sep(s);
+      if (*s)
+      {
+        (void)sd_logfile(s,d_ACKNOWLEDGE);
+        if (pari_logfile) break;
+      }
+      (void)sd_log(pari_logfile?"0":"1",d_ACKNOWLEDGE);
+      break;
+    case 'o': (void)sd_output(*s? s: NULL,d_ACKNOWLEDGE); break;
+    case 'p':
+      switch (*s)
+      {
+        case 's': s++;
+          (void)sd_seriesprecision(*s? s: NULL,d_ACKNOWLEDGE); break;
+        default :
+          (void)sd_realprecision(*s? s: NULL,d_ACKNOWLEDGE); break;
+      }
+      break;
+    case 'q': gp_quit(0); break;
+    case 'r':
+      s = get_sep(s);
+      if (!ismain) { read0(s); break; }
+      switchin(s);
+      if (file_is_binary(pari_infile))
+      {
+        int vector;
+        GEN x = gpreadbin(s, &vector);
+        if (vector) /* many BIN_GEN */
+        {
+          long i, l = lg(x);
+          pari_warn(warner,"setting %ld history entries", l-1);
+          for (i=1; i<l; i++) pari_add_hist(gel(x,i), 0);
+        }
+      }
+      break;
+    case 's': dbg_pari_heap(); break;
+    case 't': gentypes(); break;
+    case 'u':
+      print_all_user_fun((*s == 'm')? 1: 0);
+      break;
+    case 'v': print_version(); break;
+    case 'y':
+      s = get_sep(s);
+      if (!*s) s = (GP_DATA->simplify)? "0": "1";
+      (void)sd_simplify(s,d_ACKNOWLEDGE); break;
+    default: pari_err(e_SYNTAX,"unexpected character", tch,tch-1);
+  }
+}
+
+static void
+convert_time(char *s, long delay)
+{
+  if (delay >= 3600000)
+  {
+    sprintf(s, "%ldh, ", delay / 3600000); s+=strlen(s);
+    delay %= 3600000;
+  }
+  if (delay >= 60000)
+  {
+    sprintf(s, "%ldmin, ", delay / 60000); s+=strlen(s);
+    delay %= 60000;
+  }
+  if (delay >= 1000)
+  {
+    sprintf(s, "%ld,", delay / 1000); s+=strlen(s);
+    delay %= 1000;
+    if (delay < 100)
+    {
+      sprintf(s, "%s", (delay<10)? "00": "0");
+      s+=strlen(s);
+    }
+  }
+  sprintf(s, "%ld ms", delay); s+=strlen(s);
+}
+
+/* Format a time of 'delay' ms */
+static char *
+gp_format_time(long delay)
+{
+  static char buf[64];
+  char *s = buf;
+
+  term_get_color(s, c_TIME);
+  convert_time(s + strlen(s), delay);
+  s+=strlen(s);
+  term_get_color(s, c_NONE);
+  s+=strlen(s);
+  s[0] = '.';
+  s[1] = '\n';
+  s[2] = 0; return buf;
+}
+
+static int
+chron(char *s)
+{
+  if (*s)
+  { /* if "#" or "##" timer metacommand. Otherwise let the parser get it */
+    char *t;
+    if (*s == '#') s++;
+    if (*s) return 0;
+    t = gp_format_time(pari_get_histtime(0));
+    pari_printf("  ***   last result computed in %s", t);
+  }
+  else { GP_DATA->chrono ^= 1; (void)sd_timer(NULL,d_ACKNOWLEDGE); }
+  return 1;
+}
+
+/* return 0: can't interpret *buf as a metacommand
+ *        1: did interpret *buf as a metacommand or empty command */
+static int
+check_meta(char *buf, int ismain)
+{
+  switch(*buf++)
+  {
+    case '?': aide(buf, h_REGULAR); break;
+    case '#': return chron(buf);
+    case '\\': escape(buf, ismain); break;
+    case '\0': break;
+    default: return 0;
+  }
+  return 1;
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                              GPRC                                */
+/*                                                                  */
+/********************************************************************/
+/* LOCATE GPRC */
+
+static int get_line_from_file(const char *prompt, filtre_t *F, FILE *file);
+static void
+err_gprc(const char *s, char *t, char *u)
+{
+  err_printf("\n");
+  pari_err(e_SYNTAX,s,t,u);
+}
+
+/* return $HOME or the closest we can find */
+static const char *
+get_home(int *free_it)
+{
+  char *drv, *pth = os_getenv("HOME");
+  if (pth) return pth;
+  if ((drv = os_getenv("HOMEDRIVE"))
+   && (pth = os_getenv("HOMEPATH")))
+  { /* looks like WinNT */
+    char *buf = (char*)pari_malloc(strlen(pth) + strlen(drv) + 1);
+    sprintf(buf, "%s%s",drv,pth);
+    *free_it = 1; return buf;
+  }
+  pth = pari_get_homedir("");
+  return pth? pth: ".";
+}
+
+static FILE *
+gprc_chk(const char *s)
+{
+  FILE *f = fopen(s, "r");
+  if (f && !(GP_DATA->flags & gpd_QUIET)) err_printf("Reading GPRC: %s ...", s);
+  return f;
+}
+
+/* Look for [._]gprc: $GPRC, then in $HOME, ., /etc, pari_datadir */
+static FILE *
+gprc_get(void)
+{
+  FILE *f = NULL;
+  const char *gprc = os_getenv("GPRC");
+  if (gprc) f = gprc_chk(gprc);
+  if (!f)
+  {
+    int free_it = 0;
+    const char *home = get_home(&free_it);
+    char *str, *s, c;
+    long l;
+    l = strlen(home); c = home[l-1];
+    /* + "/gprc.txt" + \0*/
+    str = strcpy((char*)pari_malloc(l+10), home);
+    if (free_it) pari_free((void*)home);
+    s = str + l;
+    if (c != '/' && c != '\\') *s++ = '/';
+#ifndef _WIN32
+    strcpy(s, ".gprc");
+#else
+    strcpy(s, "gprc.txt");
+#endif
+    f = gprc_chk(str); /* in $HOME */
+    if (!f) f = gprc_chk(s); /* in . */
+#ifndef _WIN32
+    if (!f) f = gprc_chk("/etc/gprc");
+#else
+    if (!f)  /* in basedir */
+    {
+      const char *basedir = win32_basedir();
+      char *t = (char *) pari_malloc(strlen(basedir)+strlen(s)+2);
+      sprintf(t, "%s/%s", basedir, s);
+      f = gprc_chk(t); free(t);
+    }
+#endif
+    pari_free(str);
+  }
+  return f;
+}
+
+/* PREPROCESSOR */
+
+static ulong
+read_uint(char **s)
+{
+  long v = atol(*s);
+  if (!isdigit((int)**s)) err_gprc("not an integer", *s, *s);
+  while (isdigit((int)**s)) (*s)++;
+  return v;
+}
+static ulong
+read_dot_uint(char **s)
+{
+  if (**s != '.') return 0;
+  (*s)++; return read_uint(s);
+}
+/* read a.b.c */
+static long
+read_version(char **s)
+{
+  long a, b, c;
+  a = read_uint(s);
+  b = read_dot_uint(s);
+  c = read_dot_uint(s);
+  return PARI_VERSION(a,b,c);
+}
+
+static int
+get_preproc_value(char **s)
+{
+  if (!strncmp(*s,"EMACS",5)) {
+    *s += 5;
+    return GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS);
+  }
+  if (!strncmp(*s,"READL",5)) {
+    *s += 5;
+    return GP_DATA->use_readline;
+  }
+  if (!strncmp(*s,"VERSION",7)) {
+    int less = 0, orequal = 0;
+    long d;
+    *s += 7;
+    switch(**s)
+    {
+      case '<': (*s)++; less = 1; break;
+      case '>': (*s)++; less = 0; break;
+      default: return -1;
+    }
+    if (**s == '=') { (*s)++; orequal = 1; }
+    d = paricfg_version_code - read_version(s);
+    if (!d) return orequal;
+    return less? (d < 0): (d > 0);
+  }
+  return -1;
+}
+
+/* PARSE GPRC */
+
+/* 1) replace next separator by '\0' (t must be writable)
+ * 2) return the next expression ("" if none)
+ * see get_sep() */
+static char *
+next_expr(char *t)
+{
+  int outer = 1;
+  char *s = t;
+
+  for(;;)
+  {
+    char c;
+    switch ((c = *s++))
+    {
+      case '"':
+        if (outer || (s >= t+2 && s[-2] != '\\')) outer = !outer;
+        break;
+      case '\0':
+        return (char*)"";
+      default:
+        if (outer && c == ';') { s[-1] = 0; return s; }
+    }
+  }
+}
+
+static Buffer *
+filtered_buffer(filtre_t *F)
+{
+  Buffer *b = new_buffer();
+  init_filtre(F, b);
+  pari_stack_pushp(&s_bufstack, (void*)b);
+  return b;
+}
+
+static jmp_buf *env;
+static pari_stack s_env;
+/* parse src of the form s=t (or s="t"), set *ps to s, and *pt to t.
+ * modifies src (replaces = by \0) */
+static void
+parse_key_val(char *src, char **ps, char **pt)
+{
+  char *s_end, *t;
+  t = src; while (*t && *t != '=') t++;
+  if (*t != '=') err_gprc("missing '='",t,src);
+  s_end = t;
+  t++;
+  if (*t == '"') (void)readstring(t, t, src);
+  *s_end = 0; *ps = src; *pt = t;
+}
+
+static void
+gp_initrc(pari_stack *p_A)
+{
+  FILE *file = gprc_get();
+  Buffer *b;
+  filtre_t F;
+  VOLATILE long c = 0;
+
+  if (!file) return;
+  b = filtered_buffer(&F);
+  (void)pari_stack_new(&s_env);
+  for(;;)
+  {
+    char *nexts, *s, *t;
+    if (setjmp(env[s_env.n-1])) err_printf("...skipping line %ld.\n", c);
+    c++;
+    if (!get_line_from_file(NULL,&F,file)) break;
+    s = b->buf;
+    if (*s == '#')
+    { /* preprocessor directive */
+      int z, NOT = 0;
+      s++;
+      if (strncmp(s,"if",2)) err_gprc("unknown directive",s,b->buf);
+      s += 2;
+      if (!strncmp(s,"not",3)) { NOT = !NOT; s += 3; }
+      if (*s == '!')           { NOT = !NOT; s++; }
+      t = s;
+      z = get_preproc_value(&s);
+      if (z < 0) err_gprc("unknown preprocessor variable",t,b->buf);
+      if (NOT) z = !z;
+      if (!*s)
+      { /* make sure at least an expr follows the directive */
+        if (!get_line_from_file(NULL,&F,file)) break;
+        s = b->buf;
+      }
+      if (!z) continue; /* dump current line */
+    }
+    /* parse line */
+    for ( ; *s; s = nexts)
+    {
+      nexts = next_expr(s);
+      if (!strncmp(s,"read",4) && (s[4] == ' ' || s[4] == '\t' || s[4] == '"'))
+      { /* read file */
+        s += 4;
+        t = (char*)pari_malloc(strlen(s) + 1);
+        if (*s == '"') (void)readstring(s, t, s-4); else strcpy(t,s);
+        pari_stack_pushp(p_A,t);
+      }
+      else
+      { /* set default */
+        parse_key_val(s, &s,&t);
+        (void)setdefault(s,t,d_INITRC);
+      }
+    }
+  }
+  s_env.n--;
+  pop_buffer();
+  if (!(GP_DATA->flags & gpd_QUIET)) err_printf("Done.\n\n");
+  fclose(file);
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                             PROMPTS                              */
+/*                                                                  */
+/********************************************************************/
+static int gp_is_interactive = 0;
+static const char *DFT_PROMPT = "? ";
+static const char *CONTPROMPT = "";
+static const char *COMMENTPROMPT = "comment> ";
+static const char *DFT_INPROMPT = "";
+
+static char Prompt[MAX_PROMPT_LEN], Prompt_cont[MAX_PROMPT_LEN];
+
+#ifndef _WIN32
+/* if prompt is coloured, we must tell readline to ignore the
+ * corresponding ANSI escape sequences */
+static void
+brace_color(char *s, int c, int force)
+{
+  if (disable_color || (gp_colors[c] == c_NONE && !force)) return;
+#ifdef READLINE
+  if (GP_DATA->use_readline)
+    readline_prompt_color(s, c);
+  else
+#endif
+    term_get_color(s, c);
+}
+
+static void
+color_prompt(char *buf, const char *prompt)
+{
+  char *s = buf;
+  *s = 0;
+  /* escape sequences bug readline, so use special bracing (if available) */
+  brace_color(s, c_PROMPT, 0);
+  s += strlen(s); strcpy(s, prompt);
+  s += strlen(s);
+  brace_color(s, c_INPUT, 1);
+}
+#else
+static void
+color_prompt(char *buf, const char *prompt) { strcpy(buf,prompt); }
+#endif
+
+static const char *
+expand_prompt(char *buf, const char *prompt, filtre_t *F)
+{
+  if (F && F->in_comment) return COMMENTPROMPT;
+  strftime_expand(prompt, buf, MAX_PROMPT_LEN-1);
+  return buf;
+}
+
+const char *
+do_prompt(char *buf, const char *prompt, filtre_t *F)
+{
+  if (GP_DATA->flags & gpd_TEST)
+    strcpy(buf, prompt);
+  else
+  {
+    char b[MAX_PROMPT_LEN];
+    const char *s = expand_prompt(b, prompt, F);
+    color_prompt(buf, s);
+  }
+  return buf;
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                           GP MAIN LOOP                           */
+/*                                                                  */
+/********************************************************************/
+static int
+is_interactive(void)
+{
+  ulong f = GP_DATA->flags&(gpd_TEXMACS|gpd_TEST);
+  return pari_infile == stdin && !f && gp_is_interactive;
+}
+
+static const char esc = (0x1f & '['); /* C-[ = escape */
+static char *
+strip_prompt(const char *s)
+{
+  long l = strlen(s);
+  char *t, *t0 = stack_malloc(l+1);
+  t = t0;
+  for (; *s; s++)
+  {
+    /* RL_PROMPT_START_IGNORE / RL_PROMPT_END_IGNORE */
+    if (*s == 1 || *s == 2) continue;
+    if (*s == esc) /* skip ANSI color escape sequence */
+    {
+      while (*++s != 'm')
+        if (!*s) goto end;
+      continue;
+    }
+    *t = *s; t++;
+  }
+end:
+  *t = 0; return t0;
+}
+static void
+update_logfile(const char *prompt, const char *s)
+{
+  pari_sp av;
+  const char *p;
+  if (!pari_logfile) return;
+  if (!is_interactive() && !GP_DATA->echo) return;
+  av = avma;
+  p = strip_prompt(prompt); /* raw prompt */
+
+  switch (logstyle) {
+    case logstyle_TeX:
+      fprintf(pari_logfile,
+              "\\PARIpromptSTART|%s\\PARIpromptEND|%s\\PARIinputEND|%%\n",
+              p, s);
+    break;
+    case logstyle_plain:
+      fprintf(pari_logfile,"%s%s\n",p, s);
+    break;
+    case logstyle_color:
+      fprintf(pari_logfile,"%s%s%s%s%s\n",term_get_color(NULL,c_PROMPT), p,
+                                          term_get_color(NULL,c_INPUT), s,
+                                          term_get_color(NULL,c_NONE));
+      break;
+  }
+  avma = av;
+}
+
+void
+echo_and_log(const char *prompt, const char *s)
+{
+  if (GP_DATA->echo && !is_interactive()) {
+    /* not pari_puts(): would duplicate in logfile */
+    fputs(prompt, pari_outfile);
+    fputs(s,      pari_outfile);
+    fputc('\n',   pari_outfile);
+    pari_set_last_newline(1);
+  }
+  update_logfile(prompt, s);
+  pari_flush();
+}
+
+/* prompt = NULL --> from gprc. Return 1 if new input, and 0 if EOF */
+static int
+get_line_from_file(const char *prompt, filtre_t *F, FILE *file)
+{
+  const int Texmacs_stdin = ((GP_DATA->flags & gpd_TEXMACS) && file == stdin);
+  char *s;
+  input_method IM;
+
+  IM.file = file;
+  IM.fgets= Texmacs_stdin? &fgets_texmacs: &fgets;
+  IM.getline = &file_input;
+  IM.free = 0;
+  if (! input_loop(F,&IM))
+  {
+    if (Texmacs_stdin) tm_start_output();
+    return 0;
+  }
+
+  s = ((Buffer*)F->buf)->buf;
+  /* don't log if from gprc or empty input */
+  if (*s && prompt) echo_and_log(prompt, s);
+  if (GP_DATA->flags & gpd_TEXMACS)
+  {
+    tm_did_complete = 0;
+    if (Texmacs_stdin && *s == DATA_BEGIN)
+    { handle_texmacs_command(s); *s = 0; }
+    else
+      tm_start_output();
+  }
+  return 1;
+}
+
+/* return 0 if no line could be read (EOF). If PROMPT = NULL, expand and
+ * color default prompt; otherwise, use PROMPT as-is. */
+static int
+gp_read_line(filtre_t *F, const char *PROMPT)
+{
+  Buffer *b = (Buffer*)F->buf;
+  char buf[MAX_PROMPT_LEN + 24];
+  const char *p;
+  int res, interactive;
+  F->downcase = (compatible == OLDALL);
+  if (b->len > 100000) fix_buffer(b, 100000);
+  interactive = is_interactive();
+  if (interactive || pari_logfile || GP_DATA->echo)
+    p = PROMPT? PROMPT: do_prompt(buf, Prompt, F);
+  else
+    p = DFT_PROMPT;
+
+  if (interactive)
+  {
+    BLOCK_EH_START
+#ifdef READLINE
+    if (GP_DATA->use_readline)
+      res = get_line_from_readline(p, Prompt_cont, F);
+    else
+#endif
+    {
+      pari_puts(p); pari_flush();
+      res = get_line_from_file(p, F, pari_infile);
+    }
+    BLOCK_EH_END
+  }
+  else
+    res = get_line_from_file(p, F, pari_infile);
+
+  if (!disable_color && p != DFT_PROMPT &&
+      (gp_colors[c_PROMPT] != c_NONE || gp_colors[c_INPUT] != c_NONE))
+  {
+    term_color(c_NONE); pari_flush();
+  }
+  return res;
+}
+
+static int
+is_silent(char *s) { return s[strlen(s) - 1] == ';'; }
+
+static void
+reset_ctrlc(void)
+{
+#if defined(_WIN32) || defined(__CYGWIN32__)
+  win32ctrlc = 0;
+#endif
+}
+
+enum { gp_ISMAIN = 1, gp_RECOVER = 2 };
+
+static GEN
+gp_main_loop(long flag)
+{
+  VOLATILE const long dorecover = flag & gp_RECOVER;
+  VOLATILE const long ismain    = flag & gp_ISMAIN;
+  VOLATILE GEN z = gnil;
+  VOLATILE long t = 0;
+  VOLATILE pari_sp av = avma;
+  filtre_t F;
+  Buffer *b = filtered_buffer(&F);
+  struct gp_context rec;
+
+  if (dorecover) /* set recovery point */
+  {
+    long er;
+    if ((er = setjmp(env[s_env.n-1])))
+    { /* recover: jump from error [ > 0 ] or allocatemem [ -1 ] */
+      if (er > 0) { /* true error */
+        if (!(GP_DATA->recover)) exit(1);
+        gp_context_restore(&rec);
+        /* true error not from main instance, let caller sort it out */
+        if (!ismain) { kill_buffers_upto_including(b); return NULL; }
+      } else { /* allocatemem */
+        filestate_restore(rec.file);
+        gp_context_save(&rec);
+      }
+      avma = av = top;
+      kill_buffers_upto(b);
+      alarm0(0);
+    }
+  }
+  for(;;)
+  {
+    if (dorecover) gp_context_save(&rec);
+    if (! gp_read_line(&F, NULL))
+    {
+      if (popinfile()) gp_quit(0);
+      if (ismain) continue;
+      pop_buffer(); return z;
+    }
+    if (check_meta(b->buf, ismain)) continue;
+
+    avma = av;
+    if (ismain)
+    {
+      reset_ctrlc();
+      timer_start(GP_DATA->T);
+      pari_set_last_newline(1);
+    }
+    z = closure_evalres(pari_compile_str(b->buf, GP_DATA->strictmatch));
+    if (! ismain) continue;
+    alarm0(0);
+
+    if (!pari_last_was_newline()) pari_putc('\n');
+
+    t = timer_delay(GP_DATA->T);
+    if (t && GP_DATA->chrono)
+    {
+      pari_puts("time = ");
+      pari_puts(gp_format_time(t));
+    }
+    if (GP_DATA->simplify) z = simplify_shallow(z);
+    pari_add_hist(z, t);
+    if (z != gnil && ! is_silent(b->buf) ) gp_output(z, GP_DATA);
+  }
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                      EXCEPTION HANDLER                           */
+/*                                                                  */
+/********************************************************************/
+static void
+gp_sigint_fun(void) {
+  char buf[64];
+  if (GP_DATA->flags & gpd_TEXMACS) tm_start_output();
+  convert_time(buf, timer_get(GP_DATA->T));
+  pari_sigint(buf);
+}
+
+#if defined(SIGALRM) || defined(HAS_ALARM)
+static THREAD pari_timer ti_alarm;
+#endif
+#ifdef SIGALRM
+static void
+gp_alarm_fun(void) {
+  char buf[64];
+  if (GP_DATA->flags & gpd_TEXMACS) tm_start_output();
+  convert_time(buf, timer_get(&ti_alarm));
+  pari_err(e_ALARM, buf);
+}
+#endif /* SIGALRM */
+
+static const char *
+break_loop_prompt(char *buf, long n)
+{
+  char s[128];
+  if (n == 1)
+    strcpy(s, "break> ");
+  else
+    sprintf(s, "break[%ld]> ", n);
+  return do_prompt(buf, s, NULL);
+}
+
+static long frame_level=0, dbg_level = 0;
+
+static int
+break_loop(int numerr)
+{
+  filtre_t F;
+  Buffer *b;
+  int sigint = numerr<0, go_on = sigint;
+  struct gp_context rec;
+  const char *prompt, *msg;
+  char promptbuf[MAX_PROMPT_LEN + 24];
+  long nenv, oldframe_level = frame_level;
+  pari_sp av;
+
+  if (numerr == e_SYNTAX) return 0;
+  if (numerr == e_STACK) { evalstate_clone(); avma = top; }
+
+  b = filtered_buffer(&F);
+  nenv=pari_stack_new(&s_env);
+  gp_context_save(&rec);
+  iferr_env = NULL;
+  dbg_level = 0;
+  frame_level = closure_context(oldframe_level, dbg_level);
+  pari_infile = newfile(stdin, "stdin", mf_IN)->file;
+  term_color(c_ERR); pari_putc('\n');
+  if (sigint)
+    msg = "Break loop: <Return> to continue; 'break' to go back to GP prompt";
+  else
+    msg = "Break loop: type 'break' to go back to GP prompt";
+  print_errcontext(pariOut, msg, NULL, NULL);
+  term_color(c_NONE);
+  prompt = break_loop_prompt(promptbuf, s_env.n-1);
+  av = avma;
+  for(;;)
+  {
+    GEN x;
+    long er, br_status;
+    avma = av;
+    if ((er=setjmp(env[nenv])))
+    {
+      if (er < 0)
+      {
+        s_env.n = 1;
+        frame_level = oldframe_level;
+        longjmp(env[s_env.n-1], er);
+      }
+      gp_context_restore(&rec);
+      iferr_env = NULL;
+      closure_err(dbg_level);
+      (void) closure_context(oldframe_level, dbg_level);
+      pari_infile = newfile(stdin, "stdin", mf_IN)->file;
+    }
+    term_color(c_NONE);
+    if (!gp_read_line(&F, prompt))
+      br_status = br_BREAK; /* EOF */
+    else
+    {
+      /* Empty input ? Continue if entry on sigint (exit debugger frame) */
+      if (! *(b->buf) && sigint) break;
+      reset_ctrlc();
+      if (check_meta(b->buf, 0)) continue;
+      x = closure_evalbrk(pari_compile_str(b->buf,0), &br_status);
+    }
+    switch (br_status)
+    {
+      case br_NEXT: case br_MULTINEXT:
+        popinfile(); /* exit frame. Don't exit debugger if s_env.n > 2 */
+        go_on = 0; goto BR_EXIT;
+      case br_BREAK: case br_RETURN:
+        killallfiles(); /* completely exit the debugger */
+        go_on = 0; goto BR_EXIT;
+    }
+
+    if (x != gnil && !is_silent(b->buf))
+    {
+      term_color(c_OUTPUT);
+      gen_output(x, GP_DATA->fmt);
+      pari_putc('\n');
+    }
+  }
+BR_EXIT:
+  s_env.n=nenv;
+  frame_level = oldframe_level;
+  gp_context_restore(&rec);
+  pop_buffer(); return go_on;
+}
+
+/* numerr < 0: from SIGINT */
+static void
+gp_pre_recover(long numerr)
+{
+  if (numerr>=0)
+  {
+    out_puts(pariErr, "\n"); pariErr->flush();
+  }
+  longjmp(env[s_env.n-1], numerr);
+}
+
+/* numerr < 0: from SIGINT */
+static void
+gp_err_recover(long numerr)
+{
+  longjmp(env[s_env.n-1], numerr);
+}
+
+void
+dbg_up(long k)
+{
+  if (k<0) k=0;
+  dbg_level += k;
+  if (dbg_level>frame_level) dbg_level=frame_level;
+  gp_err_recover(e_NONE);
+}
+
+void
+dbg_down(long k)
+{
+  if (k<0) k=0;
+  dbg_level -= k;
+  if (dbg_level<0) dbg_level=0;
+  gp_err_recover(e_NONE);
+}
+
+GEN
+dbg_err(void) { GEN E = pari_err_last(); return E? gcopy(E):gnil; }
+
+void
+pari_breakpoint(void)
+{
+  if (!pari_last_was_newline()) pari_putc('\n');
+  closure_err(0);
+  if (break_loop(-1)) return;
+  gp_err_recover(e_MISC);
+}
+
+/* numerr < 0: from SIGINT */
+static int
+gp_handle_exception(long numerr)
+{
+  if (disable_exception_handler) disable_exception_handler = 0;
+  else if ((GP_DATA->breakloop) && break_loop(numerr)) return 1;
+  return 0;
+}
+
+#ifdef SIGALRM
+static void
+gp_alarm_handler(int sig)
+{
+#ifndef HAS_SIGACTION
+  /*SYSV reset the signal handler in the handler*/
+  (void)os_signal(sig,gp_alarm_handler);
+#endif
+  if (PARI_SIGINT_block) PARI_SIGINT_pending=sig;
+  else gp_alarm_fun();
+  return;
+}
+#endif /* SIGALRM */
+
+/********************************************************************/
+/*                                                                  */
+/*                      GP-SPECIFIC ROUTINES                        */
+/*                                                                  */
+/********************************************************************/
+static void
+check_secure(const char *s)
+{
+  if (GP_DATA->secure)
+    pari_err(e_MISC, "[secure mode]: system commands not allowed\nTried to run '%s'",s);
+}
+
+GEN
+read0(const char *s)
+{
+  switchin(s);
+  if (file_is_binary(pari_infile)) return gpreadbin(s, NULL);
+  return gp_main_loop(0);
+}
+/* as read0 but without a main instance of gp running */
+static void
+read_main(const char *s)
+{
+  GEN z;
+  if (setjmp(env[s_env.n-1]))
+    z = NULL;
+  else {
+    switchin(s);
+    if (file_is_binary(pari_infile)) {
+      z = readbin(s,pari_infile, NULL);
+      popinfile();
+    }
+    else z = gp_main_loop(gp_RECOVER);
+  }
+  if (!z) err_printf("... skipping file '%s'\n", s);
+  avma = top;
+}
+
+static GEN
+get_lines(FILE *F)
+{
+  pari_sp av = avma;
+  long i, nz = 16;
+  GEN z = cgetg(nz + 1, t_VEC);
+  Buffer *b = new_buffer();
+  input_method IM;
+  IM.fgets = &fgets;
+  IM.file = F;
+  for(i = 1;;)
+  {
+    char *s = b->buf, *e;
+    if (!file_getline(b, &s, &IM)) break;
+    if (i > nz) { nz <<= 1; z = vec_lengthen(z, nz); }
+    e = s + strlen(s)-1;
+    if (*e == '\n') *e = 0;
+    gel(z,i++) = strtoGENstr(s);
+  }
+  delete_buffer(b); setlg(z, i);
+  return gerepilecopy(av, z);
+}
+
+GEN
+externstr(const char *s)
+{
+  pariFILE *F;
+  GEN z;
+  check_secure(s);
+  F = try_pipe(s, mf_IN);
+  z = get_lines(F->file);
+  pari_fclose(F); return z;
+}
+GEN
+readstr(const char *s)
+{
+  GEN z = get_lines(switchin(s));
+  popinfile(); return z;
+}
+
+GEN
+extern0(const char *s)
+{
+  check_secure(s);
+  pari_infile = try_pipe(s, mf_IN)->file;
+  return gp_main_loop(0);
+}
+
+GEN
+input0(void)
+{
+  filtre_t F;
+  Buffer *b = filtered_buffer(&F);
+  GEN x;
+
+  while (! get_line_from_file(DFT_INPROMPT,&F,pari_infile))
+    if (popinfile()) { err_printf("no input ???"); gp_quit(1); }
+  x = readseq(b->buf);
+  pop_buffer(); return x;
+}
+
+void
+system0(const char *s)
+{
+/*FIXME: HAS_SYSTEM */
+#if defined(UNIX) || defined(__EMX__) || defined(_WIN32)
+  check_secure(s);
+  if (system(s) < 0)
+    pari_err(e_MISC, "system(\"%s\") failed", s);
+#else
+  pari_err(e_ARCH,"system");
+#endif
+}
+
+static GEN
+closure_alarmer(GEN C, long s)
+{
+  struct pari_evalstate state;
+  VOLATILE GEN x;
+  if (!s) { alarm0(0); return closure_evalgen(C); }
+  evalstate_save(&state);
+#ifndef HAS_ALARM
+  pari_err(e_ARCH,"alarm");
+#endif
+  pari_CATCH(CATCH_ALL) /* We need to stop the timer after any error */
+  {
+    GEN E = pari_err_last();
+    if (err_get_num(E) != e_ALARM) { alarm0(0); pari_err(0, E); }
+    x = evalstate_restore_err(&state);
+  }
+  pari_TRY { alarm0(s); x = closure_evalgen(C); alarm0(0); } pari_ENDCATCH;
+  return x;
+}
+
+void
+alarm0(long s)
+{
+  if (s < 0) pari_err_DOMAIN("alarm","delay","<",gen_0,stoi(s));
+#ifdef HAS_ALARM
+  if (s) timer_start(&ti_alarm);
+  alarm(s);
+#else
+  if (s) pari_err(e_ARCH,"alarm");
+#endif
+}
+
+GEN
+gp_alarm(long s, GEN code)
+{
+  if (!code) { alarm0(s); return gnil; }
+  return closure_alarmer(code,s);
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                        INITIALIZATION                         **/
+/**                                                               **/
+/*******************************************************************/
+static char *
+read_arg(long *nread, char *t, long argc, char **argv)
+{
+  long i = *nread;
+  if (isdigit((int)*t)) return t;
+  if (*t || i==argc) usage(argv[0]);
+  *nread = i+1; return argv[i];
+}
+
+static char *
+read_arg_equal(long *nread, char *t, long argc, char **argv)
+{
+  long i = *nread;
+  if (*t=='=' && isdigit((int)t[1])) return t+1;
+  if (*t || i==argc) usage(argv[0]);
+  *nread = i+1; return argv[i];
+}
+
+static void
+init_trivial_stack(void)
+{
+  const size_t s = 2048;
+  bot = (pari_sp)pari_malloc(s);
+  avma = top = bot + s;
+}
+
+typedef struct { char *key, *val; } pair_t;
+/* If ab of the form key=val, record pair in new stack entry
+ * P[n].key must be freed by caller to avoid memory leak */
+static void
+record_default(pari_stack *s_P, char *ab)
+{
+  pair_t *P = (pair_t*)*pari_stack_base(s_P);
+  char *k, *v;
+  long n;
+  ab = pari_strdup(ab);
+  parse_key_val(ab, &k, &v);
+  n = pari_stack_new(s_P);
+  P[n].key = k;
+  P[n].val = v;
+}
+static void
+read_opt(pari_stack *p_A, long argc, char **argv)
+{
+  pair_t *P;
+  pari_stack s_P; /* key / value to record default() settings */
+  char *b = NULL, *p = NULL, *s = NULL;
+  ulong f = GP_DATA->flags;
+  long i = 1, initrc = 1;
+
+  (void)&p; (void)&b; (void)&s; /* -Wall gcc-2.95 */
+
+  pari_stack_init(&s_P,sizeof(*P),(void**)&P);
+  pari_stack_alloc(&s_P, 64);
+  pari_outfile = stderr;
+  while (i < argc)
+  {
+    char *t = argv[i];
+
+    if (*t++ != '-') break;
+    i++;
+START:
+    switch(*t++)
+    {
+      case 'p': p = read_arg(&i,t,argc,argv); break;
+      case 's': s = read_arg(&i,t,argc,argv); break;
+      case 'e':
+        f |= gpd_EMACS; if (*t) goto START;
+        break;
+      case 'q':
+        f |= gpd_QUIET; if (*t) goto START;
+        break;
+      case 't':
+        f |= gpd_TEST; if (*t) goto START;
+        break;
+      case 'f':
+        initrc = 0; if (*t) goto START;
+        break;
+      case 'D':
+        if (*t || i == argc) usage(argv[0]);
+        record_default(&s_P, argv[i++]);
+        break;
+      case '-':
+        if (strcmp(t, "version-short") == 0) { print_shortversion(); exit(0); }
+        if (strcmp(t, "version") == 0) {
+          init_trivial_stack(); print_version();
+          pari_free((void*)bot); exit(0);
+        }
+        if (strcmp(t, "default") == 0) {
+          if (i == argc) usage(argv[0]);
+          record_default(&s_P, argv[i++]);
+          break;
+        }
+        if (strcmp(t, "texmacs") == 0) { f |= gpd_TEXMACS; break; }
+        if (strcmp(t, "emacs") == 0) { f |= gpd_EMACS; break; }
+        if (strcmp(t, "test") == 0) { f |= gpd_TEST; break; }
+        if (strcmp(t, "quiet") == 0) { f |= gpd_QUIET; break; }
+        if (strcmp(t, "fast") == 0) { initrc = 0; break; }
+        if (strncmp(t, "primelimit",10) == 0) {p = read_arg_equal(&i,t+10,argc,argv); break; }
+        if (strncmp(t, "stacksize",9) == 0) {s = read_arg_equal(&i,t+9,argc,argv); break; }
+       /* fall through */
+      default:
+        usage(argv[0]);
+    }
+  }
+#ifdef READLINE
+  GP_DATA->use_readline = gp_is_interactive;
+#else
+  GP_DATA->use_readline = 0;
+#endif
+  if (!gp_is_interactive && !(GP_DATA->flags & gpd_EMACS))
+    GP_DATA->breakloop = 0;
+  if (f & gpd_TEXMACS) tm_start_output();
+  GP_DATA->flags = f;
+  if (f & gpd_TEST) {
+    GP_DATA->breakloop = 0;
+    init_linewrap(76);
+  } else if (initrc)
+    gp_initrc(p_A);
+  for ( ; i < argc; i++) pari_stack_pushp(p_A, pari_strdup(argv[i]));
+
+  /* override the values from gprc */
+  if (p) (void)sd_primelimit(p, d_INITRC);
+  if (s) (void)sd_parisize(s, d_INITRC);
+  for (i = 0; i < s_P.n; i++) {
+    setdefault(P[i].key, P[i].val, d_INITRC);
+    free((void*)P[i].key);
+  }
+  pari_stack_delete(&s_P);
+
+  if (GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS|gpd_TEST)) disable_color = 1;
+  pari_outfile = stdout;
+}
+
+#ifdef __CYGWIN32__
+void
+cyg_environment(int argc, char ** argv)
+{
+  char *ti_dirs = getenv("TERMINFO_DIRS");
+  char *argv0, *p;
+  char *newdir;
+  long n;
+
+  if (!argc || !argv) return;
+  argv0 = *argv;
+  if (!argv0 || !*argv0) return;
+  p = strrchr(argv0, '/');
+  if (!p)
+    p = argv0 = "";
+  else
+    p++;
+  n = p - argv0;
+  if (ti_dirs)
+  {
+    n += 14 + strlen(ti_dirs) + 1 + 8 + 1;
+    newdir = malloc(n);
+    if (!newdir) return;
+    snprintf(newdir, n-8, "TERMINFO_DIRS=%s:%s", ti_dirs, argv0);
+  }
+  else
+  {
+    n += 14 + 8 + 1;
+    newdir = malloc(n);
+    if (!newdir) return;
+    snprintf(newdir, n-8, "TERMINFO_DIRS=%s", argv0);
+  }
+  strcpy(newdir+n-9,"terminfo");
+  putenv(newdir);
+}
+#endif
+
+#ifndef WINCE
+int
+main(int argc, char **argv)
+{
+#else
+int
+WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+        LPWSTR lpCmdLine, int nShowCmd)
+{
+  char **argv = NULL;
+  int argc = 1;
+#endif
+  void **A;
+  pari_stack s_A;
+
+  GP_DATA = default_gp_data();
+  pari_stack_init(&s_env, sizeof(*env), (void**)&env);
+  (void)pari_stack_new(&s_env);
+
+  if (setjmp(env[s_env.n-1]))
+  {
+    puts("### Errors on startup, exiting...\n\n");
+    exit(1);
+  }
+#ifdef __CYGWIN32__
+  cyg_environment(argc, argv);
+#endif
+  gp_is_interactive = pari_stdin_isatty();
+  pari_init_defaults();
+  pari_library_path = DL_DFLT_NAME;
+  pari_stack_init(&s_A,sizeof(*A),(void**)&A);
+  pari_stack_init(&s_bufstack, sizeof(Buffer*), (void**)&bufstack);
+  cb_pari_err_recover = gp_err_recover;
+  pari_init_opts(1000000 * sizeof(long), 0, INIT_SIGm | INIT_noPRIMEm);
+  cb_pari_pre_recover = gp_pre_recover;
+  pari_add_defaults_module(functions_gp_default);
+  (void)sd_graphcolormap("[\"white\",\"black\",\"blue\",\"violetred\",\"red\",\"green\",\"grey\",\"gainsboro\"]", d_SILENT);
+  (void)sd_graphcolors("[4, 5]", d_SILENT);
+  strcpy(Prompt,      DFT_PROMPT);
+  strcpy(Prompt_cont, CONTPROMPT);
+  Help = init_help();
+
+  read_opt(&s_A, argc,argv);
+  initprimetable(GP_DATA->primelimit);
+#ifdef SIGALRM
+  (void)os_signal(SIGALRM,gp_alarm_handler);
+#endif
+  pari_add_module(functions_gp);
+  pari_add_module(functions_highlevel);
+  pari_add_oldmodule(functions_oldgp);
+
+  init_graph();
+#ifdef READLINE
+  init_readline();
+#endif
+  cb_pari_whatnow = whatnow;
+  cb_pari_sigint = gp_sigint_fun;
+  cb_pari_handle_exception = gp_handle_exception;
+  cb_pari_ask_confirm = gp_ask_confirm;
+  gp_expand_path(GP_DATA->path);
+
+  timer_start(GP_DATA->T);
+  if (!(GP_DATA->flags & gpd_QUIET)) gp_head();
+  if (s_A.n)
+  {
+    FILE *l = pari_logfile;
+    long i;
+    pari_logfile = NULL;
+    for (i = 0; i < s_A.n; pari_free(A[i]),i++) read_main((char*)A[i]);
+    /* Reading one of the input files above can set pari_logfile.
+     * Don't restore in that case. */
+    if (!pari_logfile) pari_logfile = l;
+  }
+  pari_stack_delete(&s_A);
+  (void)gp_main_loop(gp_RECOVER|gp_ISMAIN);
+  gp_quit(0); return 0; /* not reached */
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                          GP OUTPUT                            **/
+/**                                                               **/
+/*******************************************************************/
+    /* EXTERNAL PRETTYPRINTER */
+/* Wait for prettinprinter to finish, to prevent new prompt from overwriting
+ * the output.  Fill the output buffer, wait until it is read.
+ * Better than sleep(2): give possibility to print */
+static void
+prettyp_wait(FILE *out)
+{
+  const char *s = "                                                     \n";
+  long i = 2000;
+
+  fputs("\n\n", out); fflush(out); /* start translation */
+  while (--i) fputs(s, out);
+  fputs("\n", out); fflush(out);
+}
+
+/* initialise external prettyprinter (tex2mail) */
+static int
+prettyp_init(void)
+{
+  gp_pp *pp = GP_DATA->pp;
+  if (!pp->cmd) return 0;
+  if (pp->file || (pp->file = try_pipe(pp->cmd, mf_OUT))) return 1;
+
+  pari_warn(warner,"broken prettyprinter: '%s'",pp->cmd);
+  pari_free(pp->cmd); pp->cmd = NULL;
+  sd_output("1", d_SILENT);
+  return 0;
+}
+
+/* n = history number. if n = 0 no history */
+static int
+tex2mail_output(GEN z, long n)
+{
+  pariout_t T = *(GP_DATA->fmt); /* copy */
+  FILE *log = pari_logfile, *out;
+
+  if (!prettyp_init()) return 0;
+  out = GP_DATA->pp->file->file;
+  /* Emit first: there may be lines before the prompt */
+  if (n) term_color(c_OUTPUT);
+  pari_flush();
+  T.prettyp = f_TEX;
+  /* history number */
+  if (n)
+  {
+    pari_sp av = avma;
+    const char *c_hist = term_get_color(NULL, c_HIST);
+    const char *c_out = term_get_color(NULL, c_OUTPUT);
+    if (!(GP_DATA->flags & gpd_QUIET))
+    {
+      if (*c_hist || *c_out)
+        fprintf(out, "\\LITERALnoLENGTH{%s}\\%%%ld =\\LITERALnoLENGTH{%s} ",
+                     c_hist, n, c_out);
+      else
+        fprintf(out, "\\%%%ld = ", n);
+    }
+    if (log) {
+      switch (logstyle) {
+      case logstyle_plain:
+        fprintf(log, "%%%ld = ", n);
+        break;
+      case logstyle_color:
+        fprintf(log, "%s%%%ld = %s", c_hist, n, c_out);
+        break;
+      case logstyle_TeX:
+        fprintf(log, "\\PARIout{%ld}", n);
+        break;
+      }
+    }
+    avma = av;
+  }
+  /* output */
+  fputGEN_pariout(z, &T, out);
+  /* flush and restore, output to logfile */
+  prettyp_wait(out);
+  if (log) {
+    if (logstyle == logstyle_TeX) {
+      T.TeXstyle |= TEXSTYLE_BREAK;
+      fputGEN_pariout(z, &T, log);
+      fputc('%', log);
+    } else {
+      T.prettyp = f_RAW;
+      fputGEN_pariout(z, &T, log);
+    }
+    fputc('\n', log); fflush(log);
+  }
+  if (n) term_color(c_NONE);
+  return 1;
+}
+
+    /* TEXMACS */
+static void
+texmacs_output(GEN z, long n)
+{
+  char *sz = GENtoTeXstr(z);
+  printf("%clatex:", DATA_BEGIN);
+  if (n) printf("\\magenta\\%%%ld = ", n);
+  printf("$\\blue %s$%c", sz,DATA_END);
+  pari_free(sz); fflush(stdout);
+}
+
+    /* REGULAR */
+static void
+normal_output(GEN z, long n)
+{
+  long l = 0;
+  char *s;
+  /* history number */
+  if (n)
+  {
+    char buf[64];
+    if (!(GP_DATA->flags & gpd_QUIET))
+    {
+      term_color(c_HIST);
+      sprintf(buf, "%%%ld = ", n);
+      pari_puts(buf);
+      l = strlen(buf);
+    }
+  }
+  /* output */
+  term_color(c_OUTPUT);
+  s = GENtostr(z);
+  if (GP_DATA->lim_lines)
+    lim_lines_output(s, l, GP_DATA->lim_lines);
+  else
+    pari_puts(s);
+  pari_free(s);
+  term_color(c_NONE); pari_putc('\n');
+}
+
+void
+gp_output(GEN z, gp_data *G)
+{
+  if (G->flags & gpd_TEST) {
+    init_linewrap(76);
+    gen_output(z, G->fmt); pari_putc('\n');
+  }
+  else if (G->flags & gpd_TEXMACS)
+    texmacs_output(z, G->hist->total);
+  else if (G->fmt->prettyp != f_PRETTY || !tex2mail_output(z, G->hist->total))
+    normal_output(z, G->hist->total);
+  pari_flush();
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                     GP-SPECIFIC DEFAULTS                      **/
+/**                                                               **/
+/*******************************************************************/
+
+static long
+atocolor(const char *s)
+{
+  long l = atol(s);
+  if (l <   0) l =   0;
+  if (l > 255) l = 255;
+  return l;
+}
+
+GEN
+sd_graphcolormap(const char *v, long flag)
+{
+  char *p, *q;
+  long i, j, l, a, s, *lp;
+
+  if (v)
+  {
+    char *t = filtre(v, 0);
+    if (*t != '[' || t[strlen(t)-1] != ']')
+      pari_err(e_SYNTAX, "incorrect value for graphcolormap", t, t);
+    for (s = 0, p = t+1, l = 2, a=0; *p; p++)
+      if (*p == '[')
+      {
+        a++;
+        while (*++p != ']')
+          if (!*p || *p == '[')
+            pari_err(e_SYNTAX, "incorrect value for graphcolormap", p, t);
+      }
+      else if (*p == '"')
+      {
+        s += sizeof(long)+1;
+        while (*p && *++p != '"') s++;
+        if (!*p) pari_err(e_SYNTAX, "incorrect value for graphcolormap", p, t);
+        s = (s+sizeof(long)-1) & ~(sizeof(long)-1);
+      }
+      else if (*p == ',')
+        l++;
+    if (l < 4)
+      pari_err(e_MISC, "too few colors (< 4) in graphcolormap");
+    if (pari_colormap) pari_free(pari_colormap);
+    pari_colormap = (GEN)pari_malloc((l+4*a)*sizeof(long) + s);
+    pari_colormap[0] = evaltyp(t_VEC)|evallg(l);
+    for (p = t+1, i = 1, lp = pari_colormap+l; i < l; p++)
+      switch(*p)
+      {
+      case '"':
+        gel(pari_colormap, i) = lp;
+        q = ++p; while (*q != '"') q++;
+        *q = 0;
+        j = 1 + nchar2nlong(q-p+1);
+        lp[0] = evaltyp(t_STR)|evallg(j);
+        strncpy(GSTR(lp), p, q-p+1);
+        lp += j; p = q;
+        break;
+      case '[': {
+        const char *ap[3];
+        gel(pari_colormap, i) = lp;
+        lp[0] = evaltyp(t_VECSMALL)|_evallg(4);
+        for (ap[0] = ++p, j=0; *p && *p != ']'; p++)
+          if (*p == ',' && j<2) { *p++ = 0; ap[++j] = p; }
+        while (j<2) ap[++j] = "0";
+        if (j>2 || *p != ']')
+        {
+          char buf[100];
+          sprintf(buf, "incorrect value for graphcolormap[%ld]: ", i);
+          pari_err(e_SYNTAX, buf, p, t);
+        }
+        *p = '\0';
+        lp[1] = atocolor(ap[0]);
+        lp[2] = atocolor(ap[1]);
+        lp[3] = atocolor(ap[2]);
+        lp += 4;
+        break;
+      }
+      case ',':
+      case ']':
+        i++;
+        break;
+      default:
+        pari_err(e_SYNTAX, "incorrect value for graphcolormap", p, t);
+      }
+    pari_free(t);
+  }
+  if (flag == d_RETURN || flag == d_ACKNOWLEDGE)
+  {
+    GEN cols = cgetg(lg(pari_colormap), t_VEC);
+    long i;
+
+    for (i = 1; i < lg(cols); i++)
+    {
+      GEN c = gel(pari_colormap, i);
+      if (typ(c) == t_STR)
+        gel(cols, i) = gcopy(c);
+      else
+        gel(cols, i) = vecsmall_to_vec(c);
+    }
+    if (flag == d_RETURN) return cols;
+    pari_printf("   graphcolormap = %Ps\n", cols);
+  }
+  return gnil;
+}
+
+GEN
+sd_graphcolors(const char *v, long flag)
+{
+  long i, l;
+  char *p;
+
+  if (v) {
+    char *t = filtre(v, 0);
+    for (p = t+1, l=2; *p != ']'; p++)
+      if (*p == ',') l++;
+      else if (*p < '0' || *p > '9')
+        pari_err(e_SYNTAX, "incorrect value for graphcolors", p, t);
+    if (*++p) pari_err(e_SYNTAX, "incorrect value for graphcolors", p, t);
+    if (pari_graphcolors) pari_free(pari_graphcolors);
+    pari_graphcolors = cgetalloc(t_VECSMALL, l);
+    for (p = t+1, i=0; *p; p++)
+    {
+      long n = 0;
+      while (*p >= '0' && *p <= '9')
+      {
+        n *= 10;
+        n += *p-'0';
+        p++;
+      }
+      pari_graphcolors[++i] = n;
+    }
+    pari_free(t);
+  }
+  switch(flag)
+  {
+  case d_RETURN:
+    return vecsmall_to_vec(pari_graphcolors);
+  case d_ACKNOWLEDGE:
+    pari_printf("   graphcolors = %Ps\n", vecsmall_to_vec(pari_graphcolors));
+  }
+  return gnil;
+}
+
+GEN
+sd_help(const char *v, long flag)
+{
+  const char *str;
+  if (v)
+  {
+    if (GP_DATA->secure)
+      pari_err(e_MISC,"[secure mode]: can't modify 'help' default (to %s)",v);
+    if (Help) pari_free((void*)Help);
+#ifndef _WIN32
+    Help = path_expand(v);
+#else
+    Help = strdup(v);
+#endif
+  }
+  str = Help? Help: "none";
+  if (flag == d_RETURN) return strtoGENstr(str);
+  if (flag == d_ACKNOWLEDGE)
+    pari_printf("   help = \"%s\"\n", str);
+  return gnil;
+}
+
+static GEN
+sd_prompt_set(const char *v, long flag, const char *how, char *p)
+{
+  if (v) strncpy(p,v,MAX_PROMPT_LEN);
+  if (flag == d_RETURN) return strtoGENstr(p);
+  if (flag == d_ACKNOWLEDGE)
+    pari_printf("   prompt%s = \"%s\"\n", how, p);
+  return gnil;
+}
+GEN
+sd_prompt(const char *v, long flag)
+{ return sd_prompt_set(v, flag, "", Prompt); }
+GEN
+sd_prompt_cont(const char *v, long flag)
+{ return sd_prompt_set(v, flag, "_cont", Prompt_cont); }
+
+GEN
+sd_breakloop(const char *v, long flag)
+{ return sd_toggle(v,flag,"breakloop", &(GP_DATA->breakloop)); }
+GEN
+sd_echo(const char *v, long flag)
+{ return sd_toggle(v,flag,"echo", &(GP_DATA->echo)); }
+GEN
+sd_timer(const char *v, long flag)
+{ return sd_toggle(v,flag,"timer", &(GP_DATA->chrono)); }
+GEN
+sd_recover(const char *v, long flag)
+{ return sd_toggle(v,flag,"recover", &(GP_DATA->recover)); }
+
+#ifndef READLINE /* default not implemented */
+GEN
+sd_readline(const char *v, long flag)
+{ (void)v; (void)flag; return gnil; }
+GEN
+sd_histfile(const char *v, long flag)
+{ (void)v; (void)flag; return gnil; }
+#endif
+
+GEN
+sd_psfile(const char *v, long flag)
+{ return sd_string(v, flag, "psfile", &current_psfile); }
+
+GEN
+sd_lines(const char *v, long flag)
+{ return sd_ulong(v,flag,"lines",&(GP_DATA->lim_lines), 0,LONG_MAX,NULL); }
+GEN
+sd_linewrap(const char *v, long flag)
+{
+  ulong old = GP_DATA->linewrap, n = GP_DATA->linewrap;
+  GEN z = sd_ulong(v,flag,"linewrap",&n, 0,LONG_MAX,NULL);
+  if (old)
+  { if (!n) resetout(1); }
+  else
+  { if (n) init_linewrap(n); }
+  GP_DATA->linewrap = n; return z;
+}
diff --git a/src/gp/gp.h b/src/gp/gp.h
new file mode 100644
index 0000000..4de1327
--- /dev/null
+++ b/src/gp/gp.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*************************************************************************/
+/*                                                                       */
+/*                      GP-SPECIFIC DECLARATIONS                         */
+/*                                                                       */
+/*************************************************************************/
+BEGINEXTERN
+
+void aide(const char *s, long flag);
+void echo_and_log(const char *prompt, const char *s);
+int  get_line_from_readline(const char *prompt, const char *prompt_cont, filtre_t *F);
+void readline_prompt_color(char *s, int c);
+void gp_output(GEN z, gp_data *G);
+void init_readline(void);
+void texmacs_completion(const char *s, long pos);
+void print_fun_list(char **list, long nbli);
+
+/* aide() */
+#define h_REGULAR 0
+#define h_LONG    1
+#define h_APROPOS 2
+#define h_RL      4
+
+/* readline completions */
+extern const char *keyword_list[];
+
+/* TeXmacs */
+#define DATA_BEGIN  ((char) 2)
+#define DATA_END    ((char) 5)
+#define DATA_ESCAPE ((char) 27)
+
+/* gp specific routines */
+void alarm0(long s);
+void dbg_down(long k);
+GEN  dbg_err(void);
+void dbg_up(long k);
+GEN  extern0(const char *cmd);
+GEN  externstr(const char *cmd);
+GEN  gp_alarm(long s, GEN code);
+void gp_quit(long exitcode);
+GEN  input0(void);
+void pari_breakpoint(void);
+GEN  read0(const char *s);
+GEN  readstr(const char *s);
+void system0(const char *cmd);
+int  whatnow(PariOUT *out, const char *s, int silent);
+GEN sd_breakloop(const char *v, long flag);
+GEN sd_echo(const char *v, long flag);
+GEN sd_graphcolormap(const char *v, long flag);
+GEN sd_graphcolors(const char *v, long flag);
+GEN sd_help(const char *v, long flag);
+GEN sd_histfile(const char *v, long flag);
+GEN sd_lines(const char *v, long flag);
+GEN sd_linewrap(const char *v, long flag);
+GEN sd_prompt(const char *v, long flag);
+GEN sd_prompt_cont(const char *v, long flag);
+GEN sd_psfile(const char *v, long flag);
+GEN sd_readline(const char *v, long flag);
+GEN sd_recover(const char *v, long flag);
+GEN sd_timer(const char *v, long flag);
+#define MAX_PROMPT_LEN 128
+const char *do_prompt(char *buf, const char *prompt, filtre_t *F);
+
+extern entree  functions_highlevel[];
+/* list of GP-specific defaults */
+extern entree functions_gp_default[], functions_gp_rl_default[];
+/* list of GP-specific functions */
+extern entree  functions_gp[];
+/* list of old GP-specific fonctions (up to 1.39.15) */
+extern entree functions_oldgp[];
+
+ENDEXTERN
diff --git a/src/gp/gp_default.h b/src/gp/gp_default.h
new file mode 100644
index 0000000..b7f6afa
--- /dev/null
+++ b/src/gp/gp_default.h
@@ -0,0 +1,20 @@
+/* This file is autogenerated from the database. */
+/* See src/desc/gen_proto */
+/* Do not edit*/
+entree functions_gp_default[]={
+{"breakloop",0,(void*)sd_breakloop,16,"",""},
+{"echo",0,(void*)sd_echo,16,"",""},
+{"graphcolormap",0,(void*)sd_graphcolormap,16,"",""},
+{"graphcolors",0,(void*)sd_graphcolors,16,"",""},
+{"help",0,(void*)sd_help,16,"",""},
+{"histfile",0,(void*)sd_histfile,16,"",""},
+{"lines",0,(void*)sd_lines,16,"",""},
+{"linewrap",0,(void*)sd_linewrap,16,"",""},
+{"prompt",0,(void*)sd_prompt,16,"",""},
+{"prompt_cont",0,(void*)sd_prompt_cont,16,"",""},
+{"psfile",0,(void*)sd_psfile,16,"",""},
+{"readline",0,(void*)sd_readline,16,"",""},
+{"recover",0,(void*)sd_recover,16,"",""},
+{"timer",0,(void*)sd_timer,16,"",""},
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
diff --git a/src/gp/gp_init.c b/src/gp/gp_init.c
new file mode 100644
index 0000000..76745bc
--- /dev/null
+++ b/src/gp/gp_init.c
@@ -0,0 +1,117 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                    GP-SPECIFIC FUNCTIONS                        */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#include "../graph/rect.h"
+#include "gp.h"
+
+static void whatnow0(char *s) { whatnow(pariOut, s,0); }
+
+static void
+allocatemem0(GEN z)
+{
+  ulong newsize;
+  if (!z) newsize = 0;
+  else {
+    if (typ(z) != t_INT) pari_err_TYPE("allocatemem",z);
+    newsize = itou(z);
+    if (signe(z) < 0) pari_err_DOMAIN("allocatemem","size","<",gen_0,z);
+  }
+  allocatemem(newsize);
+}
+
+#include "gp_init.h"
+#include "gp_default.h"
+
+/* Backward Compatibility */
+
+static GEN
+gtype(GEN x)
+{
+  return stoi(typ(x));
+}
+
+static GEN
+gsettype(GEN x,long t)
+{
+  x=gcopy(x); settyp(x,t); return x;
+}
+
+static long
+setserieslength(long n)
+{
+  long m=precdl;
+  if(n>0) precdl=n;
+  return m;
+}
+
+static long
+setprecr(long n)
+{
+  long m = GP_DATA->fmt->sigd;
+  if (n > 0) { GP_DATA->fmt->sigd = n; precreal = ndec2prec(n); }
+  return m;
+}
+
+entree functions_oldgp[] = {
+{"allocatemem",11,(void *)allocatemem0,2,"vLp","allocatemem(s)=allocates a new stack of s bytes, or doubles the stack if size is 0"},
+{"box",35,(void *)rectbox,10,"vLGG","box(w,x2,y2)=if the cursor is at position (x1,y1), draw a box with diagonal (x1,y1) and (x2,y2) in rectwindow w (cursor does not move)"},
+{"color",2,(void *)rectcolor,2,"vLL","color(w,c)=set default color to c in rectwindow. Possible values for c are 1=sienna, 2=cornsilk, 3=red, 4=black, 5=grey, 6=blue, 7=gainsborough"},
+{"cursor",11,(void*)rectcursor,10,"vLp","cursor(w)=current position of cursor in rectwindow w"},
+{"default",0,(void*)default0,11,"D\"\",r,D\"\",s,D0,L,","default({opt},{v},{flag}): set the default opt to v. If v is omitted, print the current default for opt. If no argument is given, print a list of all defaults as well as their values. If flag is non-zero, return the result instead of printing it on screen. See manual for details"},
+{"draw",1,(void*)rectdraw,10,"vGp","draw(list)=draw vector of rectwindows list at indicated x,y positions; list is a vector w1,x1,y1,w2,x2,y2,etc..."},
+{"initrect",34,(void*)initrect,10,"vLLL","initrect(w,x,y)=initialize rectwindow w to size x,y"},
+{"kill",85,(void*)kill0,11,"vr","kill(x)=kills the present value of the variable or function x. Returns new value or 0"},
+{"killrect",11,(void *)killrect,10,"vL","killrect(w)=erase the rectwindow w"},
+{"line",35,(void *)rectline,10,"vLGG","line(w,x2,y2)=if cursor is at position (x1,y1), draw a line from (x1,y1) to (x2,y2) (and move the cursor) in the rectwindow w"},
+{"lines",35,(void *)rectlines,10,"vLGG","lines(w,listx,listy)=draws an open polygon in rectwindow w where listx and listy contain the x (resp. y) coordinates of the vertices"},
+{"move",35,(void*)rectmove,10,"vLGG","move(w,x,y)=move cursor to position x,y in rectwindow w"},
+{"plot",99,(void *)plot,10,"vV=GGEDGDGp","plot(X=a,b,expr)=crude plot of expression expr, X goes from a to b"},
+{"ploth",37,(void *)ploth,10,"V=GGEp","ploth(X=a,b,expr)=plot of expression expr, X goes from a to b in high resolution"},
+{"ploth2",37,(void *)ploth2,10,"V=GGEp","ploth2(X=a,b,[expr1,expr2])=plot of points [expr1,expr2], X goes from a to b in high resolution"},
+{"plothmult",37,(void *)plothmult,10,"V=GGEp","plothmult(X=a,b,[expr1,...])=plot of expressions expr1,..., X goes from a to b in high resolution"},
+{"plothraw",2,(void *)plothraw,10,"GGp","plothraw(listx,listy)=plot in high resolution points whose x (resp. y) coordinates are in listx (resp. listy)"},
+{"point",35,(void *)rectpoint,10,"vLGG","point(w,x,y)=draw a point (and move cursor) at position x,y in rectwindow w"},
+{"points",35,(void *)rectpoints,10,"vLGG","points(w,listx,listy)=draws in rectwindow w the points whose x (resp y) coordinates are in listx (resp listy)"},
+{"postdraw",1,(void *)postdraw,10,"vG","postdraw(list)=same as plotdraw, except that the output is a PostScript program in file \"pari.ps\""},
+{"postploth",37,(void *)postploth,10,"V=GGEpD0,L,D0,L,","postploth(X=a,b,expr)=same as ploth, except that the output is a PostScript program in the file \"pari.ps\""},
+{"postploth2",37,(void *)postploth2,10,"V=GGEpD0,L,","postploth2(X=a,b,[expr1,expr2])=same as ploth2, except that the output is a PostScript program in the file \"pari.ps\""},
+{"postplothraw",2,(void *)postplothraw,10,"GGD0,L,","postplothraw(listx,listy)=same as plothraw, except that the output is a PostScript program in the file \"pari.ps\""},
+{"pprint",0,(void*)print,11,"vs*","pprint(a)=outputs a in beautified format ending with newline"},
+{"pprint1",0,(void*)print1,11,"vs*","pprint1(a)=outputs a in beautified format without ending with newline"},
+{"print",0,(void*)print,11,"vs*","print(a)=outputs a in raw format ending with newline"},
+{"print1",0,(void*)print1,11,"vs*","print1(a)=outputs a in raw format without ending with newline"},
+{"rbox",35,(void *)rectrbox,10,"vLGG","rbox(w,dx,dy)=if the cursor is at (x1,y1), draw a box with diagonal (x1,y1)-(x1+dx,y1+dy) in rectwindow w (cursor does not move)"},
+{"read",0,(void *)input0,11,"","read()=read an expression from the input file or standard input"},
+{"rline",35,(void *)rectrline,10,"vLGG","rline(w,dx,dy)=if the cursor is at (x1,y1), draw a line from (x1,y1) to (x1+dx,y1+dy) (and move the cursor) in the rectwindow w"},
+{"rlines",35,(void *)rectlines,10,"vLGG","rlines(w,dx,dy)=draw in rectwindow w the points given by vector of first coordinates xsand vector of second coordinates, connect them by lines"},
+{"rmove",35,(void *)rectrmove,10,"vLGG","rmove(w,dx,dy)=move cursor to position (dx,dy) relative to the present position in the rectwindow w"},
+{"rpoint",35,(void *)rectrpoint,10,"vLGG","rpoint(w,dx,dy)=draw a point (and move cursor) at position dx,dy relative to present position of the cursor in rectwindow w"},
+{"rpoints",35,(void *)rectpoints,10,"vLGG","rpoints(w,xs,ys)=draw in rectwindow w the points given by vector of first coordinates xs and vector of second coordinates ys"},
+{"scale",59,(void *)rectscale,10,"vLGGGG","scale(w,x1,x2,y1,y2)=scale the coordinates in rectwindow w so that x goes from x1 to x2 and y from y1 to y2 (y2<y1 is allowed)"},
+{"setprecision",15,(void *)setprecr,2,"lL","setprecision(n)=set the current precision to n decimal digits if n>0, or return the current precision if n<=0"},
+{"setserieslength",15,(void *)setserieslength,2,"lL","setserieslength(n)=set the default length of power series to n if n>0, or return the current default length if n<=0"},
+{"settype",21,(void *)gsettype,2,"GL","settype(x,t)=make a copy of x with type t (to use with extreme care)"},
+{"string",57,(void*)rectstring,10,"vLs","string(w,x)=draw in rectwindow w the string corresponding to x, where x is either a string, or a number in R, written in format 9.3"},
+{"system",70,(void*) system0,11,"vs","system(a): a being a string, execute the system command a (not valid on every machine)"},
+{"texprint",0,(void*)printtex,11,"vs*","texprint(a)=outputs a in TeX format"},
+{"type",1,(void *)gtype,2,"Gp","type(x)=internal type number of the GEN x"},
+
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+,};
diff --git a/src/gp/gp_init.h b/src/gp/gp_init.h
new file mode 100644
index 0000000..f5d11bd
--- /dev/null
+++ b/src/gp/gp_init.h
@@ -0,0 +1,20 @@
+/* This file is autogenerated from the database. */
+/* See src/desc/gen_proto */
+/* Do not edit*/
+entree functions_gp[]={
+{"alarm",0,(void*)gp_alarm,11,"D0,L,DE","alarm({s = 0},{code}): if code is omitted, trigger an \"e_ALARM\" exception after s seconds, cancelling any previously set alarm; stop a pending alarm if s = 0 or is omitted. Otherwise, evaluate code, aborting after s seconds."},
+{"allocatemem",0,(void*)allocatemem0,11,"vDG","allocatemem({s=0}): allocates a new stack of s bytes. doubles the stack if s is omitted."},
+{"breakpoint",0,(void*)pari_breakpoint,11,"v","breakpoint(): interrupt the program and enter the breakloop. The program continues when the breakloop is exited."},
+{"dbg_down",0,(void*)dbg_down,11,"vD1,L,","dbg_down({n=1}): (break loop) go down n frames. Cancel a previous dbg_up."},
+{"dbg_err",0,(void*)dbg_err,11,"","dbg_err(): (break loop) return the error data of the current error, if any."},
+{"dbg_up",0,(void*)dbg_up,11,"vD1,L,","dbg_up({n=1}): (break loop) go up n frames. Allow to inspect data of the parent function."},
+{"extern",0,(void*)extern0,11,"s","extern(str): execute shell command str, and feeds the result to GP (as if loading from file)."},
+{"externstr",0,(void*)externstr,11,"s","externstr(str): execute shell command str, and returns the result as a vector of GP strings, one component per output line."},
+{"input",0,(void*)input0,11,"","input(): read an expression from the input file or standard input."},
+{"quit",0,(void*)gp_quit,11,"vD0,L,","quit({status = 0}): quit, return to the system with exit status 'status'."},
+{"read",0,(void*)read0,11,"D\"\",s,","read({filename}): read from the input file filename. If filename is omitted, reread last input file, be it from read() or \\r."},
+{"readstr",0,(void*)readstr,11,"D\"\",s,","readstr({filename}): returns the vector of GP strings containing the lines in filename."},
+{"system",0,(void*)system0,11,"vs","system(str): str being a string, execute the system command str."},
+{"whatnow",0,(void*)whatnow0,11,"vr","whatnow(key): if key was present in GP version 1.39.15 or lower, gives the new function name."},
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
diff --git a/src/gp/gp_rl.c b/src/gp/gp_rl.c
new file mode 100644
index 0000000..069deac
--- /dev/null
+++ b/src/gp/gp_rl.c
@@ -0,0 +1,890 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                 INTERFACE TO READLINE COMPLETION                */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+
+#ifdef READLINE
+
+#include "paripriv.h"
+#include "gp.h"
+
+typedef int (*RLCI)(int, int); /* rl_complete and rl_insert functions */
+typedef char* (*GF)(const char*, int); /* generator function */
+
+BEGINEXTERN
+/* otherwise C++ compilers will choke on rl_message() prototype */
+#define USE_VARARGS
+#define PREFER_STDARG
+#include <readline/readline.h>
+#include <readline/history.h>
+ENDEXTERN
+
+/**************************************************************************/
+
+enum { DO_MATCHED_INSERT = 2, DO_ARGS_COMPLETE = 4 };
+static ulong readline_state = DO_ARGS_COMPLETE;
+static char *current_histfile = NULL;
+
+static int pari_rl_back;
+static int did_init_matched = 0;
+static entree *current_ep = NULL;
+
+static int
+change_state(const char *msg, ulong flag, int count)
+{
+  int c = (readline_state & flag) != 0;
+  ulong o_readline_state = readline_state;
+
+  switch(count)
+  {
+    default: c = 0; break; /* off */
+    case -1: c = 1; break; /* on  */
+    case -2: c = 1 - c; /* toggle */
+  }
+  if (c)
+    readline_state |= flag;
+  else {
+    readline_state &= ~flag;
+    if (!readline_state && o_readline_state)
+        readline_state = 1;
+  }
+  rl_save_prompt();
+  rl_message("[%s: %s] ", msg, c? "on": "off");
+  c = rl_read_key();
+  rl_restore_prompt();
+  rl_clear_message();
+  rl_stuff_char(c); return 1;
+}
+
+/* Wrapper around rl_complete to allow toggling insertion of arguments */
+static int
+pari_rl_complete(int count, int key)
+{
+  int ret;
+
+  pari_rl_back = 0;
+  if (count <= 0)
+    return change_state("complete args", DO_ARGS_COMPLETE, count);
+
+  rl_begin_undo_group();
+  if (rl_last_func == pari_rl_complete)
+    rl_last_func = (RLCI) rl_complete; /* Make repeated TABs different */
+  ret = ((RLCI)rl_complete)(count,key);
+  if (pari_rl_back && (pari_rl_back <= rl_point))
+    rl_point -= pari_rl_back;
+  rl_end_undo_group(); return ret;
+}
+
+static int did_matched_insert;
+
+static int
+pari_rl_matched_insert_suspend(int count, int key)
+{
+  ulong o_readline_state = readline_state;
+  (void)count; (void)key;
+
+  did_matched_insert = (readline_state & DO_MATCHED_INSERT);
+  readline_state &= ~DO_MATCHED_INSERT;
+  if (!readline_state && o_readline_state)
+    readline_state = 1;
+  return 1;
+}
+
+static int
+pari_rl_matched_insert_restore(int count, int key)
+{
+  (void)count; (void)key;
+  if (did_matched_insert)
+    readline_state |= DO_MATCHED_INSERT;
+  return 1;
+}
+
+static const char paropen[] = "([{";
+static const char parclose[] = ")]}";
+
+/* To allow insertion of () with a point in between. */
+static int
+pari_rl_matched_insert(int count, int key)
+{
+  int i = 0, ret;
+
+  if (count <= 0)
+    return change_state("electric parens", DO_MATCHED_INSERT, count);
+  while (paropen[i] && paropen[i] != key) i++;
+  if (!paropen[i] || !(readline_state & DO_MATCHED_INSERT) || GP_DATA->flags & gpd_EMACS)
+    return ((RLCI)rl_insert)(count,key);
+  rl_begin_undo_group();
+  ((RLCI)rl_insert)(count,key);
+  ret = ((RLCI)rl_insert)(count,parclose[i]);
+  rl_point -= count;
+  rl_end_undo_group(); return ret;
+}
+
+static int
+pari_rl_default_matched_insert(int count, int key)
+{
+    if (!did_init_matched) {
+      did_init_matched = 1;
+      readline_state |= DO_MATCHED_INSERT;
+    }
+    return pari_rl_matched_insert(count, key);
+}
+
+static int
+pari_rl_forward_sexp(int count, int key)
+{
+  int deep = 0, dir = 1, move_point = 0, lfail;
+
+  (void)key;
+  if (count < 0)
+  {
+    count = -count; dir = -1;
+    if (!rl_point) goto fail;
+    rl_point--;
+  }
+  while (count || deep)
+  {
+    move_point = 1;        /* Need to move point if moving left. */
+    lfail = 0;                /* Do not need to fail left movement yet. */
+    while ( !is_keyword_char(rl_line_buffer[rl_point])
+            && !strchr("\"([{}])",rl_line_buffer[rl_point])
+            && !( (dir == 1)
+                  ? (rl_point >= rl_end)
+                  : (rl_point <= 0 && (lfail = 1))))
+        rl_point += dir;
+    if (lfail || !rl_line_buffer[rl_point]) goto fail;
+
+    if (is_keyword_char(rl_line_buffer[rl_point]))
+    {
+      while ( is_keyword_char(rl_line_buffer[rl_point])
+              && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0 && (lfail = 1)))
+                  || (move_point = 0)))
+        rl_point += dir;
+      if (deep && lfail) goto fail;
+      if (!deep) count--;
+    }
+    else if (strchr(paropen,rl_line_buffer[rl_point]))
+    {
+      if (deep == 0 && dir == -1) goto fail; /* We are already out of pars. */
+      rl_point += dir;
+      deep++; if (!deep) count--;
+    }
+    else if (strchr(parclose,rl_line_buffer[rl_point]))
+    {
+      if (deep == 0 && dir == 1)
+      {
+        rl_point++; goto fail; /* Get out of pars. */
+      }
+      rl_point += dir;
+      deep--; if (!deep) count--;
+    }
+    else if (rl_line_buffer[rl_point] == '\"')
+    {
+      int bad = 1;
+
+      rl_point += dir;
+      while ( ((rl_line_buffer[rl_point] != '\"') || (bad = 0))
+              && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0))
+                  || (move_point = 0)) )
+        rl_point += dir;
+      if (bad) goto fail;
+      rl_point += dir;        /* Skip the other delimiter */
+      if (!deep) count--;
+    }
+    else
+    {
+      fail: rl_ding(); return 1;
+    }
+  }
+  if (dir != 1 && move_point) rl_point++;
+  return 1;
+}
+
+static int
+pari_rl_backward_sexp(int count, int key)
+{
+  return pari_rl_forward_sexp(-count, key);
+}
+
+/* do we add () at the end of completed word? (is it a function?) */
+static int
+add_paren(int end)
+{
+  entree *ep;
+  const char *s;
+
+  if (end < 0 || rl_line_buffer[end] == '(')
+    return 0; /* not from command_generator or already there */
+  ep = do_alias(current_ep); /* current_ep set in command_generator */
+  if (EpVALENCE(ep) < EpNEW)
+  { /* is it a constant masked as a function (e.g Pi)? */
+    s = ep->help; if (!s) return 1;
+    while (is_keyword_char(*s)) s++;
+    return (*s != '=');
+  }
+  switch(EpVALENCE(ep))
+  {
+    case EpVAR: return typ((GEN)ep->value) == t_CLOSURE;
+    case EpINSTALL: return 1;
+  }
+  return 0;
+}
+
+static void
+match_concat(char **matches, const char *s)
+{
+  matches[0] = (char*)pari_realloc((void*)matches[0],
+                                strlen(matches[0])+strlen(s)+1);
+  strcat(matches[0],s);
+}
+
+#define add_comma(x) (x==-2) /* from default_generator */
+
+/* a single match, possibly modify matches[0] in place */
+static void
+treat_single(int code, char **matches)
+{
+  if (add_paren(code))
+  {
+    match_concat(matches,"()");
+    pari_rl_back = 1;
+    if (rl_point == rl_end)
+    rl_completion_append_character = '\0'; /* Do not append space. */
+  }
+  else if (add_comma(code))
+    match_concat(matches,",");
+}
+#undef add_comma
+
+
+static char **
+matches_for_emacs(const char *text, char **matches)
+{
+  if (!matches) printf("@");
+  else
+  {
+    int i;
+    printf("%s@", matches[0] + strlen(text));
+    if (matches[1]) print_fun_list(matches+1,0);
+
+   /* we don't want readline to do anything, but insert some junk
+    * which will be erased by emacs.
+    */
+    for (i=0; matches[i]; i++) pari_free(matches[i]);
+    pari_free(matches);
+  }
+  matches = (char **) pari_malloc(2*sizeof(char *));
+  matches[0] = (char*)pari_malloc(2); sprintf(matches[0],"_");
+  matches[1] = NULL; printf("@E_N_D"); pari_flush();
+  return matches;
+}
+
+/* Attempt to complete on the contents of TEXT. 'code' is used to
+ * differentiate between callers when a single match is found.
+ * Return the array of matches, NULL if there are none. */
+static char **
+get_matches(int code, const char *text, GF f)
+{
+  char **matches = rl_completion_matches(text, f);
+  if (matches && !matches[1]) treat_single(code, matches);
+  if (GP_DATA->flags & gpd_EMACS) matches = matches_for_emacs(text,matches);
+  return matches;
+}
+
+static char *
+generator(void *list, const char *text, int *nn, int len)
+{
+  const char *def = NULL;
+  int n = *nn;
+
+  /* Return the next name which partially matches from list.*/
+  do
+    def = (((entree *) list)[n++]).name;
+  while (def && strncmp(def,text,len));
+  *nn = n;
+  if (def)
+  {
+    char *name = strcpy((char*)pari_malloc(strlen(def)+1), def);
+    return name;
+  }
+  return NULL; /* no names matched */
+}
+static char *
+old_generator(const char *text,int state)
+{
+  static int n,len;
+  static char *res;
+
+  if (!state) { res = (char*)"a"; n=0; len=strlen(text); }
+  if (res)
+  {
+    res = generator((void *)oldfonctions,text,&n,len);
+    if (res) return res;
+    n=0;
+  }
+  return generator((void *)functions_oldgp,text,&n,len);
+}
+static char *
+add_prefix(const char *name, const char *text, long junk)
+{
+  char *s = strncpy((char*)pari_malloc(strlen(name)+1+junk),text,junk);
+  strcpy(s+junk,name); return s;
+}
+static void
+init_prefix(const char *text, int *len, int *junk, char **TEXT)
+{
+  long l = strlen(text), j = l-1;
+  while (j >= 0 && is_keyword_char(text[j])) j--;
+  j++;
+  *TEXT = (char*)text + j;
+  *junk = j;
+  *len  = l - j;
+}
+static int
+is_internal(entree *ep) { return ep->menu >= 13 && ep->menu <= 15; }
+
+/* Generator function for command completion.  STATE lets us know whether
+ * to start from scratch; without any state (i.e. STATE == 0), then we
+ * start at the top of the list. */
+static char *
+hashtable_generator(const char *text, int state, entree **hash)
+{
+  static int hashpos, len, junk;
+  static entree* ep;
+  static char *TEXT;
+
+ /* If this is a new word to complete, initialize now:
+  *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
+  *  + file completion and keyword completion use different word boundaries,
+  *    have TEXT point to the keyword start.
+  *  + save the length of TEXT for efficiency.
+  */
+  if (!state)
+  {
+    hashpos = 0; ep = hash[hashpos];
+    init_prefix(text, &len, &junk, &TEXT);
+  }
+
+  /* Return the next name which partially matches from the command list. */
+  for(;;)
+    if (!ep)
+    {
+      if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
+      ep = hash[hashpos];
+    }
+    else if (is_internal(ep) || strncmp(ep->name,TEXT,len))
+      ep = ep->next;
+    else
+      break;
+  current_ep = ep; ep = ep->next;
+  return add_prefix(current_ep->name,text,junk);
+}
+/* Generator function for member completion.  STATE lets us know whether
+ * to start from scratch; without any state (i.e. STATE == 0), then we
+ * start at the top of the list. */
+static char *
+member_generator(const char *text, int state)
+{
+  static int hashpos, len, junk;
+  static entree* ep;
+  static char *TEXT;
+  entree **hash=functions_hash;
+
+ /* If this is a new word to complete, initialize now:
+  *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
+  *  + file completion and keyword completion use different word boundaries,
+  *    have TEXT point to the keyword start.
+  *  + save the length of TEXT for efficiency.
+  */
+  if (!state)
+  {
+    hashpos = 0; ep = hash[hashpos];
+    init_prefix(text, &len, &junk, &TEXT);
+  }
+
+  /* Return the next name which partially matches from the command list. */
+  for(;;)
+    if (!ep)
+    {
+      if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
+      ep = hash[hashpos];
+    }
+    else if (ep->name[0]=='_' && ep->name[1]=='.'
+             && !strncmp(ep->name+2,TEXT,len))
+        break;
+    else
+        ep = ep->next;
+  current_ep = ep; ep = ep->next;
+  return add_prefix(current_ep->name+2,text,junk);
+}
+static char *
+command_generator(const char *text, int state)
+{ return hashtable_generator(text,state, functions_hash); }
+static char *
+default_generator(const char *text,int state)
+{ return hashtable_generator(text,state, defaults_hash); }
+
+static char *
+ext_help_generator(const char *text, int state)
+{
+  static int len, junk, n, def, key;
+  static char *TEXT;
+  if (!state) {
+    n = 0;
+    def = key = 1;
+    init_prefix(text, &len, &junk, &TEXT);
+  }
+  if (def)
+  {
+    char *s = default_generator(TEXT, state);
+    if (s) return add_prefix(s, text, junk);
+    def = 0;
+  }
+  if (key)
+  {
+    for ( ; keyword_list[n]; n++)
+      if (!strncmp(keyword_list[n],TEXT,len))
+        return add_prefix(keyword_list[n++], text, junk);
+    key = 0; state = 0;
+  }
+  return command_generator(text, state);
+}
+
+static void
+rl_print_aide(char *s, int flag)
+{
+  int p = rl_point, e = rl_end;
+  FILE *save = pari_outfile;
+
+  rl_point = 0; rl_end = 0; pari_outfile = rl_outstream;
+  rl_save_prompt();
+  rl_message("%s",""); /* rl_message("") ==> "zero length format" warning */
+  aide(s, flag);
+  rl_restore_prompt();
+  rl_point = p; rl_end = e; pari_outfile = save;
+  rl_clear_message();
+  rl_refresh_line(0,0);
+}
+
+/* add a space between \<char> and following text. Attempting completion now
+ * would delete char. Hitting <TAB> again will complete properly */
+static char **
+add_space(int start)
+{
+  char **m;
+  int p = rl_point + 1;
+  rl_point = start + 2;
+  rl_insert(1, ' '); rl_point = p;
+  /*better: fake an empty completion, but don't append ' ' after it! */
+  rl_completion_append_character = '\0';
+  m = (char**)pari_malloc(2 * sizeof(char*));
+  m[0] = (char*)pari_malloc(1); *(m[0]) = 0;
+  m[1] = NULL; return m;
+}
+
+static char **
+pari_completion(char *text, int START, int END)
+{
+  int i, first=0, start=START;
+
+  rl_completion_append_character = ' ';
+  current_ep = NULL;
+/* If the line does not begin by a backslash, then it is:
+ * . an old command ( if preceded by "whatnow(" ).
+ * . a default ( if preceded by "default(" ).
+ * . a member function ( if preceded by "." + keyword_chars )
+ * . a file name (in current directory) ( if preceded by 'read' or 'writexx' )
+ * . a command */
+  if (start >=1 && rl_line_buffer[start] != '~') start--;
+  while (start && is_keyword_char(rl_line_buffer[start])) start--;
+  if (rl_line_buffer[start] == '~')
+  {
+    GF f = (GF)rl_username_completion_function;
+    for(i=start+1;i<=END;i++)
+      if (rl_line_buffer[i] == '/') { f = (GF)rl_filename_completion_function; break; }
+    return get_matches(-1, text, f);
+  }
+
+  while (rl_line_buffer[first] && isspace((int)rl_line_buffer[first])) first++;
+  switch (rl_line_buffer[first])
+  {
+    case '\\':
+      if (first == start) return add_space(start);
+      return get_matches(-1, text, rl_filename_completion_function);
+    case '?':
+      if (rl_line_buffer[first+1] == '?')
+        return get_matches(-1, text, ext_help_generator);
+      return get_matches(-1, text, command_generator);
+  }
+
+  while (start && rl_line_buffer[start] != '('
+               && rl_line_buffer[start] != ',') start--;
+  if (rl_line_buffer[start] == '(' && start)
+  {
+    int iend, j,k;
+    entree *ep;
+    char buf[200];
+
+    i = start;
+
+    while (i && isspace((int)rl_line_buffer[i-1])) i--;
+    iend = i;
+    while (i && is_keyword_char(rl_line_buffer[i-1])) i--;
+
+    if (strncmp(rl_line_buffer + i,"default",7) == 0)
+      return get_matches(-2, text, default_generator);
+    if (strncmp(rl_line_buffer + i,"whatnow",7) == 0)
+      return get_matches(-1, text, old_generator);
+    if ( strncmp(rl_line_buffer + i,"read",4)  == 0
+      || strncmp(rl_line_buffer + i,"write",5) == 0)
+      return get_matches(-1, text, rl_filename_completion_function);
+
+    j = start + 1;
+    while (j <= END && isspace((int)rl_line_buffer[j])) j++;
+    k = END;
+    while (k > j && isspace((int)rl_line_buffer[k])) k--;
+    /* If we are in empty parens, insert the default arguments */
+    if ((readline_state & DO_ARGS_COMPLETE) && k == j
+         && (rl_line_buffer[j] == ')' || !rl_line_buffer[j])
+         && (iend - i < (long)sizeof(buf))
+         && ( strncpy(buf, rl_line_buffer + i, iend - i),
+              buf[iend - i] = 0, 1)
+         && (ep = is_entry(buf)) && ep->help)
+    {
+      const char *s = ep->help;
+      while (is_keyword_char(*s)) s++;
+      if (*s++ == '(')
+      { /* function call: insert arguments */
+        const char *e = s;
+        while (*e && *e != ')' && *e != '(') e++;
+        if (*e == ')')
+        { /* we just skipped over the arguments in short help text */
+          char *str = strncpy((char*)pari_malloc(e-s + 1), s, e-s);
+          char **ret = (char**)pari_malloc(sizeof(char*)*2);
+          str[e-s] = 0;
+          ret[0] = str; ret[1] = NULL;
+          if (GP_DATA->flags & gpd_EMACS) ret = matches_for_emacs("",ret);
+          return ret;
+        }
+      }
+    }
+  }
+  for(i = END-1; i >= start; i--)
+    if (!is_keyword_char(rl_line_buffer[i]))
+    {
+      if (rl_line_buffer[i] == '.')
+        return get_matches(-1, text, member_generator);
+      break;
+    }
+  return get_matches(END, text, command_generator);
+}
+
+/* long help if count < 0 */
+static int
+rl_short_help(int count, int key)
+{
+  int flag = h_RL;
+  char *s = rl_line_buffer + rl_point;
+
+  (void)key;
+  /* func() with cursor on ')', e.g. following completion */
+  if (s > rl_line_buffer && *s == ')' && s[-1] == '(') s--;
+  while (s > rl_line_buffer && is_keyword_char(s[-1])) s--;
+  /* check for '\c' */
+  if (s > rl_line_buffer && s[-1] == '\\') s--;
+
+  if (count < 0 || rl_last_func == rl_short_help) flag |= h_LONG;
+  rl_print_aide(s, flag); return 0;
+}
+
+static int
+rl_long_help(int count, int key) { (void)count; return rl_short_help(-1,key); }
+
+static void
+init_histfile(void)
+{
+  if (current_histfile && read_history(current_histfile))
+    write_history(current_histfile);
+}
+
+void
+init_readline(void)
+{
+  static int init_done = 0;
+
+  if (init_done) return;
+  if (! GP_DATA->use_readline) readline_state = 0;
+  init_done = 1;
+  init_histfile();
+
+  /* Allow conditional parsing of the ~/.inputrc file. */
+  rl_readline_name = "Pari-GP";
+
+  /* added ~, ? and , */
+  rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(?~";
+  rl_special_prefixes = "~";
+
+  /* custom completer */
+  rl_attempted_completion_function = (rl_completion_func_t*) pari_completion;
+
+  /* we always want the whole list of completions under emacs */
+  if (GP_DATA->flags & gpd_EMACS) rl_completion_query_items = 0x8fff;
+
+  rl_add_defun("short-help", rl_short_help, -1);
+  rl_add_defun("long-help", rl_long_help, -1);
+  rl_add_defun("pari-complete", pari_rl_complete, '\t');
+  rl_add_defun("pari-matched-insert", pari_rl_default_matched_insert, -1);
+  rl_add_defun("pari-matched-insert-suspend", pari_rl_matched_insert_suspend, -1);
+  rl_add_defun("pari-matched-insert-restore", pari_rl_matched_insert_restore, -1);
+  rl_add_defun("pari-forward-sexp", pari_rl_forward_sexp, -1);
+  rl_add_defun("pari-backward-sexp", pari_rl_backward_sexp, -1);
+
+  rl_bind_key_in_map('h', rl_short_help, emacs_meta_keymap);
+  rl_bind_key_in_map('H', rl_long_help,  emacs_meta_keymap);
+
+#define KSbind(s,f,k) rl_generic_bind(ISFUNC, (s), (char*)(f), (k))
+
+  KSbind("OP",   rl_short_help,  emacs_meta_keymap); /* f1, vt100 */
+  KSbind("[11~", rl_short_help,  emacs_meta_keymap); /* f1, xterm */
+  KSbind("OP",   rl_short_help,  vi_movement_keymap); /* f1, vt100 */
+  KSbind("[11~", rl_short_help,  vi_movement_keymap); /* f1, xterm */
+  /* XTerm may signal start/end of paste by emitting F200/F201
+   * TODO: check to what extent this patch has been applied */
+  /* FIXME: For vi mode something more intelligent is needed - to switch to the
+     insert mode - and back when restoring. */
+  KSbind("[200~", pari_rl_matched_insert_suspend,  emacs_meta_keymap);  /* pre-paste xterm */
+  KSbind("[200~", pari_rl_matched_insert_suspend,  vi_movement_keymap); /* pre-paste xterm */
+  KSbind("[201~", pari_rl_matched_insert_restore,  emacs_meta_keymap);  /* post-paste xterm */
+  KSbind("[201~", pari_rl_matched_insert_restore,  vi_movement_keymap); /* post-paste xterm */
+  rl_bind_key_in_map('(', pari_rl_matched_insert, emacs_standard_keymap);
+  rl_bind_key_in_map('[', pari_rl_matched_insert, emacs_standard_keymap);
+  rl_bind_key_in_map(6, pari_rl_forward_sexp,  emacs_meta_keymap); /* M-C-f */
+  rl_bind_key_in_map(2, pari_rl_backward_sexp, emacs_meta_keymap); /* M-C-b */
+}
+
+void
+readline_prompt_color(char *s, int c)
+{
+#ifdef RL_PROMPT_START_IGNORE
+  *s++ = RL_PROMPT_START_IGNORE;
+  term_get_color(s, c);
+  s += strlen(s);
+  *s++ = RL_PROMPT_END_IGNORE;
+  *s = 0;
+#else
+  term_get_color(s, c);
+#endif
+}
+
+/* readline-specific defaults */
+GEN
+sd_readline(const char *v, long flag)
+{
+  const char *msg[] = {
+    "(bits 0x2/0x4 control matched-insert/arg-complete)", NULL};
+  ulong o_readline_state = readline_state;
+  GEN res = sd_ulong(v,flag,"readline", &readline_state, 0, 7, msg);
+
+  if (o_readline_state != readline_state)
+    (void)sd_toggle(readline_state? "1": "0", d_SILENT, "readline", &(GP_DATA->use_readline));
+  return res;
+}
+GEN
+sd_histfile(const char *v, long flag)
+{
+  char *old = current_histfile;
+  GEN r = sd_string(v, flag, "histfile", &current_histfile);
+  if (v && !*v)
+  {
+    free(current_histfile);
+    current_histfile = NULL;
+  }
+  else if (current_histfile != old && (!old || strcmp(old,current_histfile)))
+    init_histfile();
+  return r;
+}
+
+static void
+print_escape_string(char *s)
+{
+  long l = strlen(s);
+  char *t, *t0 = (char*)pari_malloc(l * 3 + 3);
+
+  t = t0; *t++ = '"';
+  for ( ;*s; *t++ = *s++)
+    switch(*s)
+    {
+      case DATA_BEGIN:
+      case DATA_END:
+      case DATA_ESCAPE: *t++ = DATA_ESCAPE; continue;
+
+      case '\\':
+      case '"': *t++ = '\\'; continue;
+    }
+  *t++ = '"';
+  *t = '\0'; puts(t0); pari_free(t0);
+}
+
+static char *
+completion_word(long end)
+{
+  char *s = rl_line_buffer + end, *found_quote = NULL;
+  long i;
+  /* truncate at cursor position */
+  *s = 0;
+  /* first look for unclosed string */
+  for (i=0; i < end; i++)
+  {
+    switch(rl_line_buffer[i])
+    {
+      case '"':
+        found_quote = found_quote? NULL: rl_line_buffer + i;
+        break;
+
+      case '\\': i++; break;
+    }
+
+  }
+  if (found_quote) return found_quote + 1; /* return next char after quote */
+
+  /* else find beginning of word */
+  while (s >  rl_line_buffer)
+  {
+    s--;
+    if (!is_keyword_char(*s)) { s++; break; }
+  }
+  return s;
+}
+
+/* completion required, cursor on s + pos. Complete wrt strict left prefix */
+void
+texmacs_completion(const char *s, long pos)
+{
+  char **matches, *text;
+
+  if (rl_line_buffer) pari_free(rl_line_buffer);
+  rl_line_buffer = pari_strdup(s);
+  text = completion_word(pos);
+  /* text = start of expression we complete */
+  rl_end = strlen(s)-1;
+  rl_point = pos;
+  matches = pari_completion(text, text - rl_line_buffer, pos);
+  printf("%cscheme:(tuple",DATA_BEGIN);
+  if (matches)
+  {
+    long i, prelen = (rl_line_buffer+pos) - text;
+    char *t = (char*)pari_malloc(prelen+1);
+    strncpy(t, text, prelen); t[prelen] = 0; /* prefix */
+    printf(" ");
+    print_escape_string(t); pari_free(t);
+    for (i = matches[1]? 1: 0; matches[i]; i++)
+    {
+      printf(" ");
+      print_escape_string(matches[i] + prelen);
+      pari_free(matches[i]);
+    }
+    pari_free(matches);
+  }
+  printf(")%c", DATA_END);
+  fflush(stdout);
+}
+
+static int
+history_is_new(char *s)
+{
+  HIST_ENTRY *e;
+  if (!*s) return 0;
+  if (!history_length) return 1;
+  e = history_get(history_length);
+  /* paranoia: e != NULL, unless readline is in a weird state */
+  return e? strcmp(s, e->line): 0;
+}
+
+static void
+gp_add_history(char *s)
+{
+  if (history_is_new(s)) { add_history(s); append_history(1,current_histfile); }
+}
+
+/* Read line; returns a malloc()ed string of the user input or NULL on EOF.
+   Increments the buffer size appropriately if needed; fix *endp if so. */
+static char *
+gprl_input(char **endp, int first, input_method *IM, filtre_t *F)
+{
+  char buf[MAX_PROMPT_LEN + 24];
+  Buffer *b = F->buf;
+  ulong used = *endp - b->buf;
+  ulong left = b->len - used, l;
+  const char *prompt = first? IM->prompt
+                            : do_prompt(buf, IM->prompt_cont, F);
+  char *s, *t;
+
+  if (! (s = readline(prompt)) ) return NULL; /* EOF */
+  gp_add_history(s); /* Makes a copy */
+  l = strlen(s) + 1;
+  /* put back \n that readline stripped. This is needed for
+   * { print("a
+   *   b"); }
+   * and conforms with the other input methods anyway. */
+  t = (char*)pari_malloc(l + 1);
+  strncpy(t, s, l-1);
+  t[l-1] = '\n';
+  t[l]   = 0; /* equivalent to sprintf(t,"%s\n", s) */
+  if (left < l)
+  {
+    ulong incr = b->len;
+    if (incr < l) incr = l;
+    fix_buffer(b, b->len + incr);
+    *endp = b->buf + used;
+  }
+  return t;
+}
+
+/* request one line interactively.
+ * Return 0: EOF
+ *        1: got one line from readline or pari_infile */
+int
+get_line_from_readline(const char *prompt, const char *prompt_cont, filtre_t *F)
+{
+  const int index = history_length;
+  char *s;
+  input_method IM;
+
+  IM.prompt      = prompt;
+  IM.prompt_cont = prompt_cont;
+  IM.getline = &gprl_input;
+  IM.free = 1;
+  if (! input_loop(F,&IM)) { pari_puts("\n"); return 0; }
+
+  s = F->buf->buf;
+  if (*s)
+  {
+    if (history_length > index+1)
+    { /* Multi-line input. Remove incomplete lines */
+      int i = history_length;
+      while (i > index) {
+        HIST_ENTRY *e = remove_history(--i);
+        pari_free(e->line); pari_free(e);
+      }
+      gp_add_history(s);
+    }
+    echo_and_log(prompt, s);
+  }
+  return 1;
+}
+#endif
diff --git a/src/gp/highlvl.c b/src/gp/highlvl.c
new file mode 100644
index 0000000..8c6d1aa
--- /dev/null
+++ b/src/gp/highlvl.c
@@ -0,0 +1,23 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*        SOME GP FUNCTION THAT MAY BE USEFUL OUTSIDE OF IT        */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#include "gp.h"
+#include "../graph/rect.h"
+#include "highlvl.h"
diff --git a/src/gp/highlvl.h b/src/gp/highlvl.h
new file mode 100644
index 0000000..28ca5ca
--- /dev/null
+++ b/src/gp/highlvl.h
@@ -0,0 +1,35 @@
+/* This file is autogenerated from the database. */
+/* See src/desc/gen_proto */
+/* Do not edit*/
+entree functions_highlevel[]={
+{"plot",0,(void*)plot,10,"vV=GGEDGDGp","plot(X=a,b,expr,{Ymin},{Ymax}): crude plot of expression expr, X goes from a to b, with Y ranging from Ymin to Ymax. If Ymin (resp. Ymax) is not given, the minimum (resp. the maximum) of the expression is used instead."},
+{"plotbox",0,(void*)rectbox,10,"vLGG","plotbox(w,x2,y2): if the cursor is at position (x1,y1), draw a box with diagonal (x1,y1) and (x2,y2) in rectwindow w (cursor does not move)."},
+{"plotclip",0,(void*)rectclip,10,"vL","plotclip(w): clip the contents of the rectwindow to the bounding box (except strings)."},
+{"plotcolor",0,(void*)rectcolor,10,"vLL","plotcolor(w,c): in rectwindow w, set default color to c. Possible values for c are given by the graphcolormap default: factory settings are 1=black, 2=blue, 3=sienna, 4=red, 5=green, 6=grey, 7=gainsborough."},
+{"plotcopy",0,(void*)rectcopy_gen,10,"vLLGGD0,L,","plotcopy(sourcew,destw,dx,dy,{flag=0}): copy the contents of rectwindow sourcew to rectwindow destw with offset (dx,dy). If flag's bit 1 is set, dx and dy express fractions of the size of the current output device, otherwise dx and dy are in pixels. dx and dy are relative positions of northwest corners if other bits of flag vanish, otherwise of: 2: southwest, 4: southeast, 6: northeast corners."},
+{"plotcursor",0,(void*)rectcursor,10,"L","plotcursor(w): current position of cursor in rectwindow w."},
+{"plotdraw",0,(void*)rectdraw_flag,10,"vGD0,L,","plotdraw(list, {flag=0}): draw vector of rectwindows list at indicated x,y positions; list is a vector w1,x1,y1,w2,x2,y2,etc. If flag!=0, x1, y1 etc. express fractions of the size of the current output device."},
+{"ploth",0,(void*)ploth,10,"V=GGEpD0,M,D0,L,\nParametric|1; Recursive|2; no_Rescale|4; no_X_axis|8; no_Y_axis|16; no_Frame|32; no_Lines|64; Points_too|128; Splines|256; no_X_ticks|512; no_Y_ticks|1024; Same_ticks|2048; Complex|4096","ploth(X=a,b,expr,{flags=0},{n=0}): plot of expression expr, X goes from a to b in high resolution. Both flags and n are optional. Binary digits of flags mean: 1=Parametric, 2=Recursive, 4=no_Rescale, 8=no_X_axis, 16=no_Y_axis, 32=no_Frame, 64=no_Lines (do no [...]
+{"plothraw",0,(void*)plothraw,10,"GGD0,L,","plothraw(listx,listy,{flag=0}): plot in high resolution points whose x (resp. y) coordinates are in listx (resp. listy). If flag is 1, join points, other non-0 flags should be combinations of bits 8,16,32,64,128,256 meaning the same as for ploth()."},
+{"plothsizes",0,(void*)plothsizes_flag,10,"D0,L,","plothsizes({flag=0}): returns array of 6 elements: terminal width and height, sizes for ticks in horizontal and vertical directions, width and height of characters. If flag=0, sizes of ticks and characters are in pixels, otherwise are fractions of the screen size."},
+{"plotinit",0,(void*)initrect_gen,10,"vLDGDGD0,L,","plotinit(w,{x},{y},{flag=0}): initialize rectwindow w to size x,y. If flag!=0, x and y express fractions of the size of the current output device. Omitting x or y means use the full size of the device."},
+{"plotkill",0,(void*)killrect,10,"vL","plotkill(w): erase the rectwindow w."},
+{"plotlines",0,(void*)rectlines,10,"vLGGD0,L,","plotlines(w,X,Y,{flag=0}): draws an open polygon in rectwindow w where X and Y contain the x (resp. y) coordinates of the vertices. If X and Y are both single values (i.e not vectors), draw the corresponding line (and move cursor). If (optional) flag is non-zero, close the polygon."},
+{"plotlinetype",0,(void*)rectlinetype,10,"vLL","plotlinetype(w,type): change the type of following lines in rectwindow w. type -2 corresponds to frames, -1 to axes, larger values may correspond to something else. w=-1 changes highlevel plotting."},
+{"plotmove",0,(void*)rectmove,10,"vLGG","plotmove(w,x,y): move cursor to position x,y in rectwindow w."},
+{"plotpoints",0,(void*)rectpoints,10,"vLGG","plotpoints(w,X,Y): draws in rectwindow w the points whose x (resp y) coordinates are in X (resp Y). If X and Y are both single values (i.e not vectors), draw the corresponding point (and move cursor)."},
+{"plotpointsize",0,(void*)rectpointsize,10,"vLG","plotpointsize(w,size): change the \"size\" of following points in rectwindow w. w=-1 changes global value."},
+{"plotpointtype",0,(void*)rectpointtype,10,"vLL","plotpointtype(w,type): change the type of following points in rectwindow w. type -1 corresponds to a dot, larger values may correspond to something else. w=-1 changes highlevel plotting."},
+{"plotrbox",0,(void*)rectrbox,10,"vLGG","plotrbox(w,dx,dy): if the cursor is at (x1,y1), draw a box with diagonal (x1,y1)-(x1+dx,y1+dy) in rectwindow w (cursor does not move)."},
+{"plotrecth",0,(void*)rectploth,10,"LV=GGEpD0,M,D0,L,\nParametric|1; Recursive|2; no_Rescale|4; no_X_axis|8; no_Y_axis|16; no_Frame|32; no_Lines|64; Points_too|128; Splines|256; no_X_ticks|512; no_Y_ticks|1024; Same_ticks|2048; Complex|4096","plotrecth(w,X=a,b,expr,{flag=0},{n=0}): writes to rectwindow w the curve output of ploth(w,X=a,b,expr,flag,n). Returns a vector for the bounding box."},
+{"plotrecthraw",0,(void*)rectplothraw,10,"LGD0,L,","plotrecthraw(w,data,{flags=0}): plot graph(s) for data in rectwindow w, where data is a vector of vectors. If plot is parametric, length of data should be even, and pairs of entries give curves to plot. If not, first entry gives x-coordinate, and the other ones y-coordinates. Admits the same optional flags as plotrecth, save that recursive plot is meaningless."},
+{"plotrline",0,(void*)rectrline,10,"vLGG","plotrline(w,dx,dy): if the cursor is at (x1,y1), draw a line from (x1,y1) to (x1+dx,y1+dy) (and move the cursor) in the rectwindow w."},
+{"plotrmove",0,(void*)rectrmove,10,"vLGG","plotrmove(w,dx,dy): move cursor to position (dx,dy) relative to the present position in the rectwindow w."},
+{"plotrpoint",0,(void*)rectrpoint,10,"vLGG","plotrpoint(w,dx,dy): draw a point (and move cursor) at position dx,dy relative to present position of the cursor in rectwindow w."},
+{"plotscale",0,(void*)rectscale,10,"vLGGGG","plotscale(w,x1,x2,y1,y2): scale the coordinates in rectwindow w so that x goes from x1 to x2 and y from y1 to y2 (y2<y1 is allowed)."},
+{"plotstring",0,(void*)rectstring3,10,"vLsD0,L,","plotstring(w,x,{flags=0}): draw in rectwindow w the string corresponding to x. Bits 1 and 2 of flag regulate horizontal alignment: left if 0, right if 2, center if 1. Bits 4 and 8 regulate vertical alignment: bottom if 0, top if 8, v-center if 4. Can insert additional gap between point and string: horizontal if bit 16 is set, vertical if bit 32 is set."},
+{"psdraw",0,(void*)postdraw_flag,10,"vGD0,L,","psdraw(list, {flag=0}): same as plotdraw, except that the output is a PostScript program in psfile (pari.ps by default), and flag!=0 scales the plot from size of the current output device to the standard PostScript plotting size."},
+{"psploth",0,(void*)postploth,10,"V=GGEpD0,L,D0,L,","psploth(X=a,b,expr,{flags=0},{n=0}): same as ploth, except that the output is a PostScript program in psfile (pari.ps by default)."},
+{"psplothraw",0,(void*)postplothraw,10,"GGD0,L,","psplothraw(listx,listy,{flag=0}): same as plothraw, except that the output is a postscript program in psfile (pari.ps by default)."},
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
diff --git a/src/gp/whatnow.c b/src/gp/whatnow.c
new file mode 100644
index 0000000..6d3b2c7
--- /dev/null
+++ b/src/gp/whatnow.c
@@ -0,0 +1,102 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "gp.h"
+
+typedef struct whatnow_t
+{
+  const char *name, *oldarg, *newarg;
+} whatnow_t;
+
+#define SAME NULL
+#define REMOV (char *)1L
+#define _REMOV {REMOV,NULL,NULL}
+#define _SAME  {SAME,NULL,NULL}
+
+#include "whatnow.h"
+
+static void
+msg(PariOUT *out, const char *s)
+{
+  out_term_color(out, c_HELP);
+  out_print_text(out, s);
+  out_putc(out, '\n');
+  out_term_color(out, c_NONE);
+}
+/* If flag = 0 (default): check if s existed in 1.39.15 and print verbosely
+ * the answer.
+ * Else: return 1 if function changed, 0 otherwise, and print help message
+ * plus the above. */
+int
+whatnow(PariOUT *out, const char *s, int flag)
+{
+  int n;
+  const char *def;
+  whatnow_t wp;
+  entree *ep;
+
+  if (flag && s[0] && !s[1]) return 0; /* special case "i" and "o" */
+  n = 0;
+  do
+    def = (oldfonctions[n++]).name;
+  while (def && strcmp(def,s));
+  if (!def)
+  {
+    int m = 0;
+    do
+      def = (functions_oldgp[m++]).name;
+    while (def && strcmp(def,s));
+    n += m - 1;
+  }
+  /* Above linear search is slow, esp. if the symbol is not found. BUT no
+   * point in wasting time by preallocating [ or autoloading ] a hashtable:
+   * whatnow() is never used in a case where speed would be necessary */
+  if (!def)
+  {
+    if (!flag)
+      msg(out, "As far as I can recall, this function never existed");
+    return 0;
+  }
+
+  wp = whatnowlist[n-1]; def = wp.name;
+  if (def == SAME)
+  {
+    if (!flag)
+      msg(out, "This function did not change");
+    return 0;
+  }
+  if (flag)
+  {
+    out_term_color(out, c_NONE);
+    out_print_text(out, "\nA function with that name existed in GP-1.39.15; to run in backward compatibility mode, type \"default(compatible,3)\", or set \"compatible = 3\" in your GPRC file.");
+    out_putc(out, '\n');
+  }
+
+  if (def == REMOV)
+  {
+    msg(out, "This function was suppressed");
+    return 0;
+  }
+  if (!strcmp(def,"x*y")) ep = NULL;
+  else {
+    ep = is_entry(wp.name);
+    if (!ep) pari_err_BUG("whatnow");
+  }
+  out_puts(out, "New syntax: ");
+  out_term_color(out, c_ERR);
+  out_printf(out, "%s%s ===> %s%s\n\n", s, wp.oldarg, wp.name, wp.newarg);
+  if (ep) msg(out, ep->help);
+  out_term_color(out, c_NONE); return 1;
+}
diff --git a/src/gp/whatnow.h b/src/gp/whatnow.h
new file mode 100644
index 0000000..ecc347c
--- /dev/null
+++ b/src/gp/whatnow.h
@@ -0,0 +1,657 @@
+/* generated by the perl script 'whatnow' */
+static const whatnow_t whatnowlist[]={
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"elladd","(e,z1,z2)","(e,z1,z2)"},
+_SAME,
+{"matadjoint","(x)","(x)"},
+_SAME,
+{"ellak","(e,n)","(e,n)"},
+_SAME,
+{"algdep","(x,n,dec)","(x,n,dec)"},
+{"nfalgtobasis","(nf,x)","(nf,x)"},
+{"ellan","(e,n)","(e,n)"},
+{"ellap","(e,n)","(e,n)"},
+{"ellap","(e,n)","(e,n)"},
+{"padicappr","(x,a)","(x,a)"},
+_SAME,
+_SAME,
+_SAME,
+{"matcompanion","(x)","(x)"},
+_SAME,
+_SAME,
+{"nfbasis","(x)","(x)"},
+{"nfbasis","(x)","(x,2)"},
+{"nfbasistoalg","(nf,x)","(nf,x)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"ellbil","(e,z1,z2)","(e,z1,z2)"},
+{"binomial","(x,y)","(x,y)"},
+_SAME,
+_SAME,
+{"contfrac","(x,lmax)","(x,,lmax)"},
+{"factor","(x,lim)","(x,lim)"},
+{"bnfcertify","(bnf)","(bnf)"},
+_REMOV,
+_REMOV,
+_REMOV,
+_REMOV,
+{"quadclassunit","(D,c1,c2,g)","(D,,[c1,c2,g])"},
+{"bnfinit","(P)","(P,2)"},
+{"bnfinit","(P)","(P,1)"},
+{"bnfinit","(P)","(P)"},
+{"bnfnarrow","(bnf)","(bnf)"},
+{"bnrinit","(bnf,ideal)","(bnf,ideal)"},
+{"bnrinit","(bnf,ideal)","(bnf,ideal)"},
+{"bnrinit","(bnf,ideal)","(bnf,ideal,1)"},
+{"quadclassunit","(D)","(D)"},
+{"sizebyte","(x)","(x)"},
+_SAME,
+_SAME,
+{"contfrac","(x)","(x)"},
+{"contfrac","(b,x)","(x,b)"},
+_REMOV,
+{"charpoly","(x,y)","(x,y)"},
+{"charpoly","(x,y)","(x,y,1)"},
+{"charpoly","(x,y)","(x,y,2)"},
+{"ellchangecurve","(x,y)","(x,y)"},
+_SAME,
+{"ellchangepoint","(x,y)","(x,y)"},
+{"qfbclassno","(x)","(x)"},
+{"qfbclassno","(x)","(x,1)"},
+{"polcoeff","(x,s)","(x,s)"},
+{"x*y","(x,y)",""},
+{"component","(x,s)","(x,s)"},
+{"polcompositum","(pol1,pol2)","(pol1,pol2)"},
+{"polcompositum","(pol1,pol2)","(pol1,pol2,1)"},
+{"qfbcompraw","(x,y)","(x,y)"},
+_SAME,
+{"bnrconductor","(a1)","(a1)"},
+{"bnrconductorofchar","(bnr,chi)","(bnr,chi)"},
+_SAME,
+_SAME,
+_SAME,
+{"serconvol","(x,y)","(x,y)"},
+_SAME,
+{"core","(x)","(x,1)"},
+_SAME,
+{"coredisc","(x)","(x,1)"},
+_SAME,
+_SAME,
+{"truncate","(x)","(x,&e)"},
+{"polcyclo","(n)","(n)"},
+{"factorback","(fa)","(fa)"},
+{"bnfdecodemodule","(nf,fa)","(nf,fa)"},
+{"poldegree","(x)","(x)"},
+{"denominator","(x)","(x)"},
+{"lindep","(x)","(x,-1)"},
+_SAME,
+{"matdet","(x)","(x)"},
+{"matdet","(x)","(x,1)"},
+{"matdetint","(x)","(x)"},
+{"matdiagonal","(x)","(x)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"poldisc","(x)","(x)"},
+{"nfdisc","(x)","(x)"},
+{"nfdisc","(x)","(x,2)"},
+{"bnrdisc","(bnr,subgroup)","(bnr,subgroup)"},
+{"bnrdisc","(bnr)","(bnr,,,2)"},
+{"bnrdisclist","(bnf,list)","(bnf,list)"},
+{"bnrdisclist","(bnf,arch,bound)","(bnf,bound,arch)"},
+{"bnrdisclist","(bnf,bound)","(bnf,bound,,1)"},
+{"bnrdisclist","(bnf,bound)","(bnf,bound)"},
+{"bnrdisc","(bnr,subgroup)","(bnr,subgroup,,1)"},
+{"bnrdisc","(bnr,subgroup)","(bnr,subgroup,,3)"},
+_SAME,
+{"divrem","(x,y)","(x,y)"},
+{"sumdiv","(n,X,expr)","(n,X,expr)"},
+{"mateigen","(x)","(x)"},
+_SAME,
+_SAME,
+_SAME,
+{"Euler","",""},
+_SAME,
+_SAME,
+{"vecextract","(x,y)","(x,y)"},
+{"factorial","(x)","(x)"},
+{"factorcantor","(x,p)","(x,p)"},
+{"factorff","(x,p,a)","(x,p,a)"},
+{"factormod","(x,p)","(x,p)"},
+_SAME,
+{"nfbasis","(x,p)","(x,,p)"},
+{"nfdisc","(x,p)","(x,,p)"},
+{"polred","(x,p)","(x,,p)"},
+{"polred","(x,p)","(x,2,p)"},
+_SAME,
+_SAME,
+{"factorpadic","(x,p,r)","(x,p,r,1)"},
+{"factor","(x,l,hint)","(x)"},
+{"factor","(x,l,hint)","(x)"},
+{"fibonacci","(x)","(x)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"ffinit","(p,n)","(p,n)"},
+_SAME,
+{"polgalois","(x)","(x)"},
+{"nfgaloisapply","(nf,aut,x)","(nf,aut,x)"},
+{"nfgaloisconj","(nf)","(nf)"},
+{"nfgaloisconj","(nf)","(nf,2)"},
+{"nfgaloisconj","","(nf,1)"},
+{"gammah","(x)","(x)"},
+_SAME,
+{"matsolve","(a,b)","(a,b)"},
+{"matsolvemod","(M,D,Y)","(M,D,Y)"},
+{"matsolvemod","(M,D,Y)","(M,D,Y,1)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"ellglobalred","(x,y)","(x,y)"},
+_REMOV,
+{"qfbhclassno","(x)","(x)"},
+{"ellheight","(e,x)","(e,x)"},
+{"ellheight","(e,x)","(e,x,1)"},
+{"mathnf","(x)","(x)"},
+{"mathnf","(x)","(x,1)"},
+_REMOV,
+{"mathnfmod","(x,d)","(x,d)"},
+{"mathnfmodid","(x,d)","(x,d)"},
+{"mathnf","(x)","(x,3)"},
+{"mathess","(x)","(x)"},
+{"hilbert","(x,y)","(x,y)"},
+{"mathilbert","(n)","(n)"},
+{"hilbert","(x,y,p)","(x,y,p)"},
+{"vector","(n,X,expr)","(n,X,expr)"},
+_SAME,
+{"I","",""},
+_SAME,
+{"idealaddtoone","(nf,list)","(nf,list)"},
+{"idealaddtoone","(nf,x,y)","(nf,x,y)"},
+_SAME,
+{"idealappr","(nf,x)","(nf,x,1)"},
+_SAME,
+_SAME,
+_SAME,
+{"idealdiv","(nf,x,y)","(nf,x,y,1)"},
+_SAME,
+{"idealhnf","(nf,x)","(nf,x)"},
+{"idealhnf","(nf,x)","(nf,x)"},
+_SAME,
+_SAME,
+{"idealinv","(nf,x)","(nf,x,1)"},
+_SAME,
+_SAME,
+{"ideallistarch","(nf,list,arch)","(nf,list,arch)"},
+{"ideallist","(nf,list)","(nf,list,2)"},
+{"ideallistarch","","(nf,list,arch)"},
+{"ideallistarch","","(nf,list,arch)"},
+{"ideallist","","(nf,list,3)"},
+{"ideallist","(nf,bound)","(nf,bound)"},
+{"ideallist","(nf,bound)","(nf,bound,1)"},
+{"idealred","(nf,x,vdir)","(nf,x,vdir)"},
+_SAME,
+{"idealmul","(nf,x,y)","(nf,x,y,1)"},
+_SAME,
+_SAME,
+{"idealpow","(nf,x,y)","(nf,x,y,1)"},
+_SAME,
+{"idealtwoelt","(nf,x,a)","(nf,x,a)"},
+_SAME,
+{"matid","(n)","(n)"},
+_SAME,
+_SAME,
+{"matimage","(x)","(x)"},
+{"matimage","(x)","(x,1)"},
+{"matimagecompl","(x)","(x)"},
+_SAME,
+_REMOV,
+_REMOV,
+_REMOV,
+{"incgam","(s,x,y)","(s,x,y)"},
+{"matindexrank","(x)","(x)"},
+{"vecsort","(x)","(x,,1)"},
+{"nfinit","(pol)","(pol)"},
+{"nfinit","(x)","(x,2)"},
+{"nfinit","(x)","(x,3)"},
+{"ellinit","(x)","(x)"},
+{"zetakinit","(x)","(x)"},
+{"intformal","(x,y)","(x,y)"},
+{"matintersect","(x,y)","(x,y)"},
+{"intnum","(x=a,b,s)","(x=a,b,s,1)"},
+{"intnum","(x=a,b,s)","(x=a,b,s,2)"},
+_SAME,
+{"intnum","(x=a,b,s)","(x=a,b,s,3)"},
+{"matinverseimage","(x,y)","(x,y)"},
+{"matisdiagonal","(x)","(x)"},
+{"isfundamental","(x)","(x)"},
+{"nfisideal","(nf,x)","(nf,x)"},
+{"nfisincl","(x,y)","(x,y)"},
+{"nfisincl","(nf1,nf2)","(nf1,nf2,1)"},
+{"polisirreducible","(x)","(x)"},
+{"nfisisom","(x,y)","(x,y)"},
+{"nfisisom","(x,y)","(x,y)"},
+{"ellisoncurve","(e,x)","(e,x)"},
+_SAME,
+{"bnfisprincipal","(bnf,x)","(bnf,x,0)"},
+{"bnfisprincipal","(bnf,x)","(bnf,x,2)"},
+{"bnfisprincipal","(bnf,x)","(bnf,x)"},
+{"bnfisprincipal","(bnf,x)","(bnf,x,3)"},
+{"bnrisprincipal","(bnf,x)","(bnf,x)"},
+_SAME,
+{"ispseudoprime","(x)","(x)"},
+{"sqrtint","(x)","(x)"},
+{"setisset","(x)","(x)"},
+{"issquarefree","(x)","(x)"},
+_SAME,
+{"bnfisunit","(bnf,x)","(bnf,x)"},
+{"qfjacobi","(x)","(x)"},
+{"besseljh","(n,x)","(n,x)"},
+{"ellj","(x)","(x)"},
+_REMOV,
+{"besselk","(nu,x)","(nu,x)"},
+{"besselk","(nu,x)","(nu,x)"},
+{"matker","(x)","(x)"},
+{"matker","(x)","(x,1)"},
+{"matkerint","(x)","(x)"},
+{"matkerint","(x)","(x,1)"},
+_REMOV,
+{"kronecker","(x,y)","(x,y)"},
+_REMOV,
+{"zetak","(nfz,s)","(nfz,s,1)"},
+{"serlaplace","(x)","(x)"},
+_SAME,
+{"pollegendre","(n)","(n)"},
+_SAME,
+_SAME,
+{"vecsort","(x)","(x,,2)"},
+_SAME,
+_SAME,
+{"lindep","(x)","(x,1)"},
+{"qflll","(x)","(x)"},
+_REMOV,
+{"qflll","(x)","(x,8)"},
+{"qflllgram","(x)","(x)"},
+_REMOV,
+{"qflllgram","(x)","(x,8)"},
+{"qflllgram","(x)","(x,1)"},
+{"qflllgram","(x)","(x,4)"},
+{"qflllgram","(x)","(x,5)"},
+{"qflll","(x)","(x,1)"},
+{"qflll","(x)","(x,2)"},
+{"qflll","(x)","(x,4)"},
+{"qflll","(x)","(x,5)"},
+_REMOV,
+{"log","(x)","(x)"},
+_SAME,
+{"elllocalred","(e)","(e)"},
+_SAME,
+{"log","(x)","(x,1)"},
+{"elllseries","(e,s,N,A)","(e,s,A)"},
+{"bnfinit","(sbnf)","(sbnf)"},
+{"Mat","(x)","(x)"},
+{"vecextract","(x,y,z)","(x,y,z)"},
+{"ellheightmatrix","(e,x)","(e,x)"},
+_SAME,
+_SAME,
+{"matrixqz","(x,p)","(x,-1)"},
+{"matrixqz","(x,p)","(x,-2)"},
+_SAME,
+_SAME,
+_SAME,
+{"idealmin","(nf,ix,vdir)","(nf,ix,vdir)"},
+{"qfminim","(x,bound,maxnum)","(x,bound,maxnum)"},
+{"qfminim","(x,bound)","(x,bound,,1)"},
+{"Mod","(x,y)","(x,y)"},
+{"Mod","(x,y,p)","(x,y)"},
+_SAME,
+{"gcd","(x,y)","(x,y,1)"},
+{"moebius","(n)","(n)"},
+_SAME,
+_SAME,
+_SAME,
+{"nfeltdiv","(nf,a,b)","(nf,a,b)"},
+{"nfeltdiveuc","(nf,a,b)","(nf,a,b)"},
+{"nfeltdivrem","(nf,a,b)","(nf,a,b)"},
+{"nfhnf","(nf,x)","(nf,x)"},
+{"nfhnfmod","(nf,x,detx)","(nf,x,detx)"},
+{"nfeltmod","(nf,a,b)","(nf,a,b)"},
+{"nfeltmul","(nf,a,b)","(nf,a,b)"},
+{"nfeltpow","(nf,a,k)","(nf,a,k)"},
+{"nfeltreduce","(nf,a,id)","(nf,a,id)"},
+{"nfsnf","(nf,x)","(nf,x)"},
+{"nfeltval","(nf,a,pr)","(nf,a,pr)"},
+_SAME,
+_SAME,
+{"qfbnucomp","(x,y,l)","(x,y,l)"},
+_SAME,
+{"numerator","(x)","(x)"},
+{"qfbnupow","(x,n)","(x,n)"},
+{"O","(x)","(x)"},
+_SAME,
+{"ellordinate","(e,x)","(e,x)"},
+{"znorder","(x)","(x)"},
+{"ellorder","(e,x)","(e,x)"},
+{"polredord","(x)","(x)"},
+_SAME,
+{"matpascal","(n)","(n)"},
+{"qfperfection","(a)","(a)"},
+{"numtoperm","(n,k)","(n,k)"},
+{"permtonum","(vect)","(vect)"},
+{"qfbprimeform","(x,p)","(x,p)"},
+{"eulerphi","(x)","(x)"},
+{"Pi","",""},
+{"contfracpnqn","(x)","(x)"},
+{"ellztopoint","(e,z)","(e,z)"},
+{"polinterpolate","(xa,ya,x)","(xa,ya,p)"},
+_SAME,
+{"polred","(x)","(x,2)"},
+_SAME,
+{"polredabs","(x)","(x,1)"},
+{"polredabs","(x)","(x,4)"},
+{"polredabs","(x)","(x,8)"},
+{"polredabs","(x)","(x,2)"},
+_SAME,
+{"variable","(x)","(x)"},
+{"Pol","(x,v)","(x,v)"},
+_SAME,
+{"polylog","(m,x)","(m,x,1)"},
+{"polylog","(m,x)","(m,x,2)"},
+{"polylog","(m,x)","(m,x,3)"},
+{"Polrev","(x,v)","(x,v)"},
+{"polzagier","(n,m)","(n,m)"},
+{"ellmul","(e,x,n)","(e,x,n)"},
+{"qfbpowraw","(x,n)","(x,n)"},
+{"precision","(x,n)","(x,n)"},
+_SAME,
+_SAME,
+{"idealprimedec","(nf,p)","(nf,p)"},
+_SAME,
+{"znprimroot","(n)","(n)"},
+_REMOV,
+_REMOV,
+{"prod","(x,X=a,b,expr)","(X=a,b,expr,x)"},
+_SAME,
+_SAME,
+{"prodinf","(X=a,expr)","(X=a,expr,1)"},
+_SAME,
+{"Qfb","(a,b,c)","(a,b,c)"},
+{"Qfb","(a,b,c,d)","(a,b,c,d)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"matrank","(x)","(x)"},
+{"bnrclassno","(bnf,x)","(bnf,x)"},
+{"bnrclassnolist","(bnf,liste)","(bnf,liste)"},
+_SAME,
+{"polrecip","(x)","(x)"},
+{"qfbred","(x)","(x)"},
+{"qfbred","(x)","(x)"},
+{"qfbred","(x,d)","(x,2,,d)"},
+{"poldiscreduced","(f)","(f)"},
+{"quadregulator","(x)","(x)"},
+_REMOV,
+{"polresultant","(x,y)","(x,y)"},
+{"polresultant","(x,y)","(x,y,1)"},
+{"serreverse","(x)","(x)"},
+{"qfbred","(x)","(x,1)"},
+{"qfbred","(x,d)","(x,3,,d)"},
+{"round","(x)","(x,&e)"},
+_SAME,
+{"rnfdisc","(nf,pol)","(nf,pol)"},
+_SAME,
+{"rnfequation","(nf,pol)","(nf,pol,1)"},
+{"rnfhnfbasis","(bnf,order)","(bnf,order)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"polrootsmod","(x,p)","(x,p)"},
+{"polrootsmod","(x,p)","(x,p,1)"},
+{"polrootspadic","(x,p,r)","(x,p,r)"},
+{"polroots","(x)","(x)"},
+{"nfrootsof1","(nf)","(nf)"},
+_REMOV,
+_SAME,
+{"round","(x)","(x,&e)"},
+{"Ser","(x,v)","(x,v)"},
+{"Set","(x)","(x)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"sigma","(k,x)","(x,k)"},
+_SAME,
+{"qfsign","(x)","(x)"},
+{"bnfsignunit","(bnf)","(bnf)"},
+{"factormod","(x,p)","(x,p,1)"},
+_SAME,
+_SAME,
+_SAME,
+{"sizedigit","(x)","(x)"},
+{"nfbasis","(x)","(x,1)"},
+{"bnfcompress","(x)","(x)"},
+{"nfdisc","(x)","(x,1)"},
+{"factor","(x)","(x,0)"},
+{"ellinit","(x)","(x,1)"},
+{"polred","(x)","(x,1)"},
+{"polred","(x)","(x,3)"},
+{"matsnf","(x)","(x)"},
+{"matsnf","(x)","(x,1)"},
+{"matsnf","(x)","(x,4)"},
+{"matsnf","(x)","(x,2)"},
+_SAME,
+{"vecsort","(x)","(x)"},
+_SAME,
+{"qfgaussred","(x)","(x)"},
+_SAME,
+{"gcd","(x,y)","(x,y,2)"},
+{"polsturm","(x)","(x)"},
+{"polsturm","(x,a,b)","(x,a,b)"},
+{"polsubcyclo","(p,d)","(p,d)"},
+{"ellsub","(e,a,b)","(e,a,b)"},
+_SAME,
+{"sum","(x,X=a,b,expr)","(X=a,b,expr,x)"},
+_SAME,
+{"sumalt","(X=a,expr)","(X=a,expr,1)"},
+_SAME,
+_SAME,
+{"sumpos","(X=a,expr)","(X=a,expr,1)"},
+{"matsupplement","(x)","(x)"},
+{"polsylvestermatrix","(x,y)","(x,y)"},
+_SAME,
+_SAME,
+{"elltaniyama","(e)","(e)"},
+_SAME,
+{"polchebyshev","(n)","(n)"},
+{"teichmuller","(x)","(x)"},
+_SAME,
+_SAME,
+_REMOV,
+_REMOV,
+{"elltors","(e)","(e)"},
+_SAME,
+{"mattranspose","(x)","(x)"},
+{"truncate","(x)","(x)"},
+{"poltschirnhaus","(x)","(x)"},
+_REMOV,
+{"quadunit","(x)","(x)"},
+_SAME,
+_SAME,
+{"Vec","(x)","(x)"},
+{"vecsort","(x)","(x,,1)"},
+{"vecsort","(x)","(x,,2)"},
+_SAME,
+_SAME,
+_SAME,
+_SAME,
+{"vectorv","(n,X,expr)","(n,X,expr)"},
+{"ellwp","(e)","(e)"},
+{"weber","(x)","(x)"},
+{"weber","(x)","(x,2)"},
+_SAME,
+{"ellpointtoz","(e,P)","(e,P)"},
+_SAME,
+_SAME,
+{"ideallog","(nf,x,bid)","(nf,x,bid)"},
+{"idealstar","(nf,I)","(nf,I)"},
+{"idealstar","(nf,id)","(nf,id,1)"},
+{"idealstar","(nf,id)","(nf,id,2)"},
+_SAME,
+
+_SAME,
+{"plotbox","(x,a)","(x,a)"},
+{"plotcolor","(w,c)","(w,c)"},
+{"plotcursor","(w)","(w)"},
+_SAME,
+{"plotdraw","(list)","(list)"},
+{"plotinit","(w,x,y)","(w,x,y)"},
+_SAME,
+{"plotkill","(w)","(w)"},
+{"plotlines","(w,x2,y2)","(w,x2,y2)"},
+{"plotlines","(w,x2,y2)","(w,x2,y2)"},
+{"plotmove","(w,x,y)","(w,x,y)"},
+_SAME,
+_SAME,
+{"ploth","(X=a,b,expr)","(X=a,b,expr,1)"},
+{"ploth","(X=a,b,expr)","(X=a,b,expr)"},
+_SAME,
+{"plotpoints","(w,x,y)","(w,x,y)"},
+{"plotpoints","(w,x,y)","(w,x,y)"},
+{"psdraw","(list)","(list)"},
+{"psploth","(X=a,b,expr)","(X=a,b,expr)"},
+{"psploth","(X=a,b,expr)","(X=a,b,expr,1)"},
+{"psplothraw","(listx,listy)","(listx,listy)"},
+_REMOV,
+_REMOV,
+_SAME,
+_SAME,
+{"plotrbox","(w,dx,dy)","(w,dx,dy)"},
+{"input","(x)","(x)"},
+{"plotrline","(w,dx,dy)","(w,dx,dy)"},
+{"plotrlines","(w,dx,dy)","(w,dx,dy,1)"},
+{"plotrmove","(w,dx,dy)","(w,dx,dy)"},
+{"plotrpoint","(w,dx,dy)","(w,dx,dy)"},
+{"plotrpoints","(w,dx,dy)","(w,dx,dy)"},
+{"plotscale","(w,x1,x2,y1,y2)","(w,x1,x2,y1,y2)"},
+{"default","(n)","(realprecision,n)"},
+{"default","(n)","(seriesprecision,n)"},
+{"type","(x,t)","(x,t)"},
+{"plotstring","(w,x)","(w,x)"},
+_SAME,
+{"printtex","(x)","(x)"},
+_SAME
+};
diff --git a/src/graph/plotQt.c b/src/graph/plotQt.c
new file mode 100644
index 0000000..f55caf6
--- /dev/null
+++ b/src/graph/plotQt.c
@@ -0,0 +1,625 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/////////////////////////////////////////////////////////////////////////////
+//
+//  High resolution plot using Trolltech's Qt library
+//
+//  You may possibly want to use this file with a "Qt Free Edition"
+//  which is distributed under the terms of the Q PUBLIC LICENSE (QPL),
+//  or with a "Qt/Embedded Free Edition" which is
+//  distributed under the terms of the GNU General Public License (GPL).
+//  Please check http://www.trolltech.com for details.
+//
+//                            ---Nils-Peter Skoruppa (www.countnumber.de)
+/////////////////////////////////////////////////////////////////////////////
+extern "C" {
+#include "pari.h"
+#include "paripriv.h"
+#undef grem
+#undef swap
+#include "rect.h"
+}
+
+#ifdef __QPE__
+#include <qpeapplication.h>
+#else
+#include <qapplication.h>
+#endif
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qarray.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qcolor.h>
+#include <qpixmap.h>
+#include <qimage.h>
+
+
+class Plotter: public QWidget {
+
+#ifdef __FANCY_WIN__
+     Q_OBJECT
+
+signals:
+    void clicked();
+
+protected:
+    void mouseReleaseEvent( QMouseEvent*);
+#endif
+
+public:
+    Plotter( long *w, long *x, long *y, long lw,
+             QWidget* parent = 0, const char* name = 0, WFlags fl = 0);
+    void save( const QString& s = *plotFile + ".xpm",//QString("pariplot.xpm"),
+               const QString& f = QString( "XPM"));
+
+protected:
+    void paintEvent( QPaintEvent *);
+    void resizeEvent ( QResizeEvent *);
+#ifndef __FANCY_WIN__
+    void keyPressEvent( QKeyEvent *);
+#endif
+
+private:
+    long *w;                           // map into rectgraph indexes
+    long *x;                           // x, y: array of x,y-coorinates of the
+    long *y;                           //   top left corners of the rectwindows
+    long lw;                           // lw: number of rectwindows
+    long numcolors;
+    QColor *color;
+    QFont font;
+    static QString *plotFile;
+    void draw(QPainter *p);
+
+// public:
+//     static void setPlotFile( const char *);
+};
+
+
+QString *Plotter::plotFile = new QString( "pariplot");
+
+
+Plotter::Plotter( long *w, long *x, long *y, long lw,
+                  QWidget* parent,  const char* name, WFlags fl)
+    : QWidget( parent, name, fl), font( "lucida", 9) {
+
+    long i;
+
+    this->w=w; this->x=x; this->y=y; this->lw=lw;
+#ifndef __FANCY_WIN__
+    this->resize( pari_plot.width, pari_plot.height);
+    this->setCaption( "Pari QtPlot");
+#endif
+    this->setFont( font);
+    numcolors = lg(pari_colormap)-1;
+    color = (QColor*)pari_malloc(numcolors*sizeof(QColor));
+    for (i = 1; i < lg(pari_colormap); i++)
+    {
+      int r, g, b;
+      color_to_rgb(gel(pari_colormap,i), &r, &g, &b);
+      color[i-1] = QColor(r, g, b);
+    }
+    this->setBackgroundColor( color[0]);
+}
+
+// void Plotter::setPlotFile( const char *s) {
+
+//     delete Plotter::plotFile;
+//     Plotter::plotFile = new QString( s);
+// }
+
+struct data_qt
+{
+  QPainter *p;
+  long numcolors;
+  QColor *color;
+};
+
+static void SetForeground(void *data, long col)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->setPen(d->color[col]);
+}
+
+static void DrawPoint(void *data, long x, long y)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->drawPoint(x, y);
+}
+
+static void DrawLine(void *data, long x1, long y1, long x2, long y2)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->drawLine(x1, y1, x2, y2);
+}
+
+static void DrawRectangle(void *data, long x, long y, long w, long h)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->drawRect(x, y, w, h);
+}
+
+static void DrawPoints(void *data, long nb, struct plot_points *p)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   QPointArray xp=QPointArray(nb);
+   long i;
+   for (i=0;i<nb;i++)
+     xp.setPoint(i,p[i].x, p[i].y);
+   d->p->drawPoints(xp);
+}
+
+static void DrawLines(void *data, long nb, struct plot_points *p)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   QPointArray xp=QPointArray(nb);
+   long i;
+   for (i=0;i<nb;i++)
+     xp.setPoint(i,p[i].x, p[i].y);
+   d->p->drawPolyline(xp);
+}
+
+static void DrawString(void *data, long x, long y, char *text, long numtext)
+{
+  struct data_qt *d = (struct data_qt *) data;
+  d->p->drawText(x, y, QString(text), numtext);
+}
+
+void Plotter::draw(QPainter *p){
+  struct plot_eng plotQt;
+  struct data_qt d;
+  d.p= p;
+  d.numcolors = numcolors;
+  d.color=color;
+  plotQt.sc=&SetForeground;
+  plotQt.pt=&DrawPoint;
+  plotQt.ln=&DrawLine;
+  plotQt.bx=&DrawRectangle;
+  plotQt.mp=&DrawPoints;
+  plotQt.ml=&DrawLines;
+  plotQt.st=&DrawString;
+  plotQt.pl=&pari_plot;
+  plotQt.data = (void *)&d;
+  double xs = double(this->width()) / pari_plot.width,
+         ys = double(this->height()) / pari_plot.height;
+  gen_rectdraw0(&plotQt, this->w, this->x, this->y,this->lw,xs,ys);
+}
+
+void Plotter::save( const QString& s, const QString& f){
+
+    QPixmap pm( this->width(), this->height());
+    QPainter p;
+
+    p.begin( &pm, this);
+    p.fillRect( 0, 0, pm.width(), pm.height(), color[0]);
+    this->draw(&p);
+    p.end();
+
+    // supported formats in qt2: BMP, JPEG, PNG, PNM, XBM, XPM ; PNG is broken
+    pm.save( s, f);
+}
+
+void Plotter::paintEvent( QPaintEvent *) {
+
+    QPainter p;
+    p.begin( this);
+    this->draw(&p);
+    p.end();
+}
+
+void Plotter::resizeEvent( QResizeEvent *) { }
+
+#ifndef __FANCY_WIN__
+void Plotter::keyPressEvent( QKeyEvent *e) {
+
+    switch ( tolower( e->ascii())) {
+        case 's':
+            save();
+            this->setCaption( "Pari QtPlot: " + *plotFile);
+            break;
+    }
+}
+#endif
+
+
+#ifdef __FANCY_WIN__
+void Plotter::mouseReleaseEvent( QMouseEvent*) {
+
+    emit clicked();
+}
+#endif
+
+
+
+#ifdef __FANCY_WIN__
+//
+// The envelope for an instance of plotter
+//
+
+
+#include <qmainwindow.h>
+#include <qpopupmenu.h>
+#include <qmenubar.h>
+#include <qtoolbar.h>
+#include <qaction.h>
+#include <qfiledialog.h>
+#include <qmessagebox.h>
+#include <qfile.h>
+#include <qstatusbar.h>
+#include <qimage.h>
+#include <qstrlist.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qlayout.h>
+
+
+/* XPM */
+static const char * const fullscreen_xpm[] = {
+"14 14 2 1",
+"         c None",
+".        c #000000",
+"..............",
+".     ..     .",
+".     ..     .",
+".    ....    .",
+".     ..     .",
+".  .  ..  .  .",
+"..............",
+"..............",
+".  .  ..  .  .",
+".     ..     .",
+".    ....    .",
+".     ..     .",
+".     ..     .",
+".............."};
+
+
+class SaveAsDialog: public
+#ifdef __QPE__
+//QDialog
+#else
+QFileDialog
+#endif
+{
+
+    Q_OBJECT
+
+public:
+    SaveAsDialog( const QString & c = QString::null,
+                  const QString & s = QString::null, int w = 0, int h = 0,
+                  QWidget *parent = 0, const char *name = 0, WFlags f = 0);
+    ~SaveAsDialog();
+#ifdef __QPE__
+    QString selectedFile() { return nameW->text();}
+#endif
+    int picWidth() { return widthW->value();}
+    int picHeight() { return heightW->value();}
+
+private:
+    QLineEdit *nameW;
+    QSpinBox *widthW, *heightW;
+
+};
+
+
+SaveAsDialog::SaveAsDialog( const QString & c, const QString & s, int w, int h,
+                            QWidget *parent, const char *name, WFlags f)
+#ifdef __QPE__
+    // simplistic dialog in case of QPE ( fancy alternative: class FileSelector)
+
+    : QDialog( parent, name, TRUE, f) {
+
+    if( c) this->setCaption( c);
+    nameW = new QLineEdit( this);
+    if( s) nameW->setText( s);
+    widthW = new QSpinBox( 1, 65536, 1, this);
+    if( w > 0) widthW->setValue( w);
+    heightW = new QSpinBox( 1, 65536, 1, this);
+    if( h > 0) heightW->setValue( h);
+
+    QVBoxLayout *top = new QVBoxLayout( this, 10);
+    QGridLayout *contents = new QGridLayout( 3, 2);
+
+    top->addLayout( contents);
+
+    QLabel *l;
+    l = new QLabel( nameW, "Name : ", this);
+    l->setAlignment( AlignRight | AlignVCenter);
+    contents->addWidget( l, 0, 0);
+    contents->addWidget( nameW, 0, 1);
+    l = new QLabel( widthW, "Width : ", this);
+    l->setAlignment( AlignRight | AlignVCenter);
+    contents->addWidget( l, 1, 0);
+    contents->addWidget( widthW, 1, 1);
+    l = new QLabel( heightW, "Height : ", this);
+    l->setAlignment( AlignRight | AlignVCenter);
+    contents->addWidget( l, 2, 0);
+    contents->addWidget( heightW, 2, 1);
+
+    top->activate();
+    this->resize( 160, this->height()); // hack!!!
+#else
+    : QFileDialog( parent, name, TRUE) {
+
+    if( c) this->setFilters( c);
+    if( s) this->setSelection( s);
+
+    QLabel *l;
+    QWidget *wt = new QWidget( this);
+    QHBoxLayout *spinBoxes = new QHBoxLayout( wt, 5);
+    widthW = new QSpinBox( 1, 65536, 1, wt);
+    l  = new QLabel( widthW, "&width ", wt);
+    spinBoxes->addWidget( l);
+    spinBoxes->addWidget( widthW);
+    if( w > 0) widthW->setValue( w);
+    heightW = new QSpinBox( 1, 65536, 1, wt);
+    spinBoxes->addSpacing(10);
+    l  = new QLabel( heightW, "&height ", wt);
+    l->setAlignment( AlignRight | AlignVCenter);
+    spinBoxes->addWidget( l);
+    spinBoxes->addWidget( heightW);
+    if( h > 0) heightW->setValue( h);
+    l = new QLabel( "Resolution:", this);
+    QFileDialog::addWidgets( l, wt, 0);
+#endif
+}
+
+
+SaveAsDialog::~SaveAsDialog() {
+}
+
+
+
+class PlotWindow: public QMainWindow {
+
+     Q_OBJECT
+
+public:
+    PlotWindow( long *w, long *x, long *y, long lw,
+                QWidget* parent = 0, const char* name = 0, WFlags fl = 0);
+    ~PlotWindow();
+
+#ifndef __QPE__
+protected:
+    void resizeEvent( QResizeEvent *);
+#endif
+
+private slots:
+    void fullScreen();
+    void normalView();
+    void save();
+    void save( int);
+
+private:
+    static const QStrList file_formats;
+    Plotter *plr;
+    QString saveFileName;
+    int saveFileFormat;
+#ifndef __QPE__
+    QLabel *res;
+#endif
+};
+
+
+const QStrList PlotWindow::file_formats = QImage::outputFormats();
+
+
+PlotWindow::PlotWindow( long *w, long *x, long *y, long lw,
+                        QWidget* parent, const char* name, WFlags fl)
+    : QMainWindow( parent, name, fl),
+      saveFileName( "pariplot"), saveFileFormat( 0) {
+
+    this->setCaption( "Pari QtPlot");
+
+#ifdef __QPE__
+    QToolBar *toolBar = new QToolBar( this);
+    QMenuBar *menuBar = new QMenuBar( toolBar);
+    toolBar->setHorizontalStretchable( TRUE);
+    this->setToolBarsMovable( FALSE);
+#else
+    QMenuBar *menuBar = this->menuBar();
+    menuBar->setFrameStyle( QFrame::Panel | QFrame::Raised);
+#endif
+
+    // Setting up the File and View menu
+    QPopupMenu *format = new QPopupMenu( this);
+    for( uint i = 0; i < file_formats.count(); i++) {
+        format->insertItem( QString( QStrList(file_formats).at(i)) + " ...",
+                            this, SLOT( save( int)), 0, i);
+        if( 0 == QString( QStrList(file_formats).at(i)).compare( "PNG"))
+            format->setItemEnabled( i, FALSE); // PNG seems to be broken
+    }
+    QPopupMenu *file = new QPopupMenu( this);
+    CHECK_PTR( file );
+    file->insertItem( "&Save", this, SLOT( save()), CTRL+Key_S);
+    file->insertItem( "Save &as", format);
+    file->insertSeparator();
+    file->insertItem( "&Close", this, SLOT(close()), CTRL+Key_C);
+    menuBar->insertItem( "&File", file );
+
+#ifndef __QPE__
+    QPopupMenu *view = new QPopupMenu( this);
+    menuBar->insertItem( "&View", view);
+#endif
+    // Setting up the Fullscreen action
+    QAction *a = new QAction( "use full screen",
+                              QPixmap( (const char ** )fullscreen_xpm),
+                              "&Fullscreen", CTRL+Key_F, this);
+    connect( a, SIGNAL( activated()), this, SLOT( fullScreen()));
+#ifdef __QPE__
+    a->addTo( toolBar);
+#else
+    a->addTo( view);
+#endif
+
+    // Setting up an instance of plotter
+    plr = new Plotter( w, x, y, lw, this);
+    connect( plr, SIGNAL(clicked()), this, SLOT( normalView()));
+    this->setCentralWidget( plr);
+
+#ifndef __QPE__
+    this->resize( pari_plot.width,
+                  pari_plot.height + 25);
+    res = new QLabel( statusBar());
+    statusBar()->addWidget( res);
+#endif
+}
+
+
+PlotWindow::~PlotWindow() {
+}
+
+
+#ifndef __QPE__
+void PlotWindow::resizeEvent( QResizeEvent *e) {
+
+    QMainWindow::resizeEvent( e);
+    res->setText( QString( "Resolution: ") +
+                  QString::number( plr->width()) + "x" +
+                  QString::number( plr->height()));
+    res->setFixedSize( res->sizeHint());
+}
+#endif
+
+
+void PlotWindow::fullScreen() {
+
+    if ( plr->parentWidget()) {
+        plr->reparent( 0, WStyle_Tool | WStyle_Customize | WStyle_StaysOnTop,
+                      QPoint( 0, 0), FALSE);
+        plr->resize( qApp->desktop()->width(), qApp->desktop()->height());
+        plr->show();
+    }
+}
+
+
+void PlotWindow::normalView() {
+
+    if ( !plr->parentWidget()) {
+        plr->reparent( this, 0, QPoint(0,0), FALSE);
+        this->setCentralWidget( plr);
+        plr->show();
+    }
+}
+
+
+void PlotWindow::save() {
+
+    QString ff = QString( QStrList(file_formats).at( saveFileFormat));
+    QString fn = saveFileName + "." + ff.lower();
+    plr->save( fn, ff);
+    this->setCaption( QString( "Pari QtPlot:") + fn);
+#ifndef __QPE__
+    statusBar()->message( QString( "File %1 saved" ).arg( fn), 2000 );
+#endif
+}
+
+
+void PlotWindow::save( int id) {
+
+    QString ff( QStrList(file_formats).at( id));
+#ifdef __QPE__
+    QString s( "Save as");
+#else
+    QString s( ff + " (*." + ff.lower() +");;All (*)");
+#endif
+    SaveAsDialog d( s, saveFileName + "." + ff.lower(),
+                    plr->width(), plr->height());
+    if( QDialog::Rejected == d.exec()) return;
+    QString fn = d.selectedFile();
+    if ( !fn.isEmpty()) {
+        if( QFile( fn).exists() &&
+            QMessageBox::warning(
+                this, this->caption(),
+                QString( "A file named\n\"") + fn
+                + QString( "\"\nalready exists\n"
+                           "Should this file be overwritten ?\n\n"),
+                "&Overwrite", "&Cancel"))  return;
+        saveFileName = fn;
+        int p;
+        if( (p = saveFileName.findRev( "." + ff, -1, FALSE)) >=0)
+            saveFileName.truncate( p);
+        saveFileFormat = id;
+        int old_w = plr->width(), old_h = plr->height();
+        int w = d.picWidth(), h = d.picHeight();
+        if( w != old_w ||  h != old_h) {
+            plr->resize( w, h);
+            save();
+            plr->resize( old_w, old_h);
+        } else
+            save();
+    }
+}
+
+
+#include "plotQt.moc.cpp"
+#endif // __FANCY_WIN__
+
+
+
+//
+// Implementation of the two architecture-dependent functions
+// (from rect.h) requested by pari's plotting routines
+//
+
+
+void
+rectdraw0(long *w, long *x, long *y, long lw)
+{
+    if (pari_daemon()) return;  // parent process returns
+
+    pari_close();
+    PARI_get_plot();
+
+    // launch Qt window
+    int argc = 1; char *argv[] = { "gp", "-qws"}; // set argc = 2 for cross
+                                                  // development using qvfb
+#ifdef __QPE__
+    QPEApplication
+#else
+    QApplication
+#endif
+        a( argc, argv);
+#ifdef __FANCY_WIN__
+    PlotWindow *win = new PlotWindow(w, x, y, lw);
+#else
+    Plotter *win = new Plotter( w, x, y, lw);
+#endif
+#ifdef __QPE__
+    a.showMainWidget( win);
+#else
+    a.setMainWidget( win);
+    win->show();
+#endif
+    a.exec();
+    exit( 0);
+}
+
+void
+PARI_get_plot()
+/* This function initialises the structure rect.h: pari_plot */
+{
+    if (pari_plot.init) return;      // pari_plot is already set
+#ifdef __QPE__
+    pari_plot.width   = 240;         // width and
+    pari_plot.height  = 320;         //  height of plot window
+#else
+    pari_plot.width   = 400;         // width and
+    pari_plot.height  = 300;         //  height of plot window
+#endif
+    pari_plot.hunit   = 3;           //
+    pari_plot.vunit   = 3;           //
+    pari_plot.fwidth  = 6;           // font width
+    pari_plot.fheight = 9;           //   and height
+    pari_plot.init    = 1;           // flag: pari_plot is set now!
+}
diff --git a/src/graph/plotQt4.c b/src/graph/plotQt4.c
new file mode 100644
index 0000000..a3e8428
--- /dev/null
+++ b/src/graph/plotQt4.c
@@ -0,0 +1,612 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/////////////////////////////////////////////////////////////////////////////
+//
+//  High resolution plot using Trolltech's Qt library
+//
+//  You may possibly want to use this file with a "Qt Free Edition"
+//  which is distributed under the terms of the Q PUBLIC LICENSE (QPL),
+//  or with a "Qt/Embedded Free Edition" which is
+//  distributed under the terms of the GNU General Public License (GPL).
+//  Please check http://www.trolltech.com for details.
+//
+//                            ---Nils-Peter Skoruppa (www.countnumber.de)
+/////////////////////////////////////////////////////////////////////////////
+#ifdef __QPE__
+#include <Qt/qpeapplication.h>
+#else
+#include <Qt/qapplication.h>
+#endif
+#include <Qt/qwidget.h>
+#include <Qt/qpainter.h>
+#include <Qt/qcolor.h>
+#include <Qt/qdesktopwidget.h>
+#include <Qt/qevent.h>
+#include <Qt/qpixmap.h>
+#include <Qt/qsignalmapper.h>
+#include <Qt/qimage.h>
+#include <Qt/qimagewriter.h>
+#include <Qt/qmainwindow.h>
+#include <Qt/qmenubar.h>
+#include <Qt/qtoolbar.h>
+#include <Qt/qaction.h>
+#include <Qt/qfiledialog.h>
+#include <Qt/qmessagebox.h>
+#include <Qt/qfile.h>
+#include <Qt/qstatusbar.h>
+#include <Qt/qimage.h>
+#include <Qt/qlabel.h>
+#include <Qt/qspinbox.h>
+#include <Qt/qlayout.h>
+
+extern "C" {
+#include "pari.h"
+#include "paripriv.h"
+#undef grem
+#include "rect.h"
+}
+
+using namespace Qt;
+
+class Plotter: public QWidget {
+
+#ifdef __FANCY_WIN__
+     Q_OBJECT
+
+signals:
+    void clicked();
+
+protected:
+    void mouseReleaseEvent( QMouseEvent*);
+#endif
+
+public:
+    Plotter( long *w, long *x, long *y, long lw,
+             QWidget* parent = 0);
+    void save( const QString& s = *plotFile + ".xpm",//QString("pariplot.xpm"),
+               const QString& f = QString( "XPM"));
+
+protected:
+    void paintEvent( QPaintEvent *);
+    //void resizeEvent ( QResizeEvent *);
+#ifndef __FANCY_WIN__
+    void keyPressEvent( QKeyEvent *);
+#endif
+
+private:
+    long *w;                           // map into rectgraph indexes
+    long *x;                           // x, y: array of x,y-coorinates of the
+    long *y;                           //   top left corners of the rectwindows
+    long lw;                           // lw: number of rectwindows
+    long numcolors;
+    QColor *color;
+    QFont font;
+    static QString *plotFile;
+    void draw(QPainter *p);
+
+// public:
+//     static void setPlotFile( const char *);
+};
+
+
+QString *Plotter::plotFile = new QString( "pariplot");
+
+
+Plotter::Plotter( long *w, long *x, long *y, long lw,
+                  QWidget* parent)
+    : QWidget( parent), font( "lucida", 9) {
+
+    long i;
+
+    this->w=w; this->x=x; this->y=y; this->lw=lw;
+#ifndef __FANCY_WIN__
+    this->resize( pari_plot.width, pari_plot.height);
+    this->setCaption( "Pari QtPlot");
+#endif
+    this->setFont( font);
+    numcolors = lg(pari_colormap)-1;
+    color = (QColor*)gpmalloc(numcolors*sizeof(QColor));
+    for (i = 1; i < lg(pari_colormap); i++)
+    {
+      int r, g, b;
+      color_to_rgb(gel(pari_colormap,i), &r, &g, &b);
+      color[i-1] = QColor(r, g, b);
+    }
+    QPalette palette;
+    palette.setColor(backgroundRole(), color[0]);
+    setPalette(palette);
+}
+
+// void Plotter::setPlotFile( const char *s) {
+
+//     delete Plotter::plotFile;
+//     Plotter::plotFile = new QString( s);
+// }
+
+struct data_qt
+{
+  QPainter *p;
+  long numcolors;
+  QColor *color;
+};
+
+static void SetForeground(void *data, long col)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->setPen(d->color[col]);
+}
+
+static void DrawPoint(void *data, long x, long y)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->drawPoint(x, y);
+}
+
+static void DrawLine(void *data, long x1, long y1, long x2, long y2)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->drawLine(x1, y1, x2, y2);
+}
+
+static void DrawRectangle(void *data, long x, long y, long w, long h)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   d->p->drawRect(x, y, w, h);
+}
+
+static void DrawPoints(void *data, long nb, struct plot_points *p)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   QPolygon xp=QPolygon(nb);
+   long i;
+   for (i=0;i<nb;i++)
+     xp.setPoint(i,p[i].x, p[i].y);
+   d->p->drawPoints(xp);
+}
+
+static void DrawLines(void *data, long nb, struct plot_points *p)
+{
+   struct data_qt *d = (struct data_qt *) data;
+   QPolygon xp=QPolygon(nb);
+   long i;
+   for (i=0;i<nb;i++)
+     xp.setPoint(i,p[i].x, p[i].y);
+   d->p->drawPolyline(xp);
+}
+
+static void DrawString(void *data, long x, long y, char *text, long numtext)
+{
+  struct data_qt *d = (struct data_qt *) data;
+  d->p->drawText(x, y, QString(text).left(numtext));
+}
+
+void Plotter::draw(QPainter *p){
+  struct plot_eng plotQt;
+  struct data_qt d;
+  d.p= p;
+  d.numcolors = numcolors;
+  d.color=color;
+  plotQt.sc=&SetForeground;
+  plotQt.pt=&DrawPoint;
+  plotQt.ln=&DrawLine;
+  plotQt.bx=&DrawRectangle;
+  plotQt.mp=&DrawPoints;
+  plotQt.ml=&DrawLines;
+  plotQt.st=&DrawString;
+  plotQt.pl=&pari_plot;
+  plotQt.data=(void *)&d;
+  double xs = double(this->width()) / pari_plot.width,
+         ys = double(this->height()) / pari_plot.height;
+  gen_rectdraw0(&plotQt, this->w, this->x, this->y,this->lw,xs,ys);
+}
+
+void Plotter::save( const QString& s, const QString& f)
+{
+    QPixmap pm( this->width(), this->height());
+    QPainter p;
+    p.begin(&pm);
+        p.initFrom(this);
+        p.fillRect( 0, 0, pm.width(), pm.height(), color[0]);
+        draw(&p);
+    p.end();
+    pm.save( s, f.toAscii().data());
+}
+
+
+
+void Plotter::paintEvent( QPaintEvent *) {
+
+    QPainter p;
+    p.begin( this);
+    this->draw(&p);
+    p.end();
+}
+
+//void Plotter::resizeEvent( QResizeEvent *) { }
+
+#ifndef __FANCY_WIN__
+void Plotter::keyPressEvent( QKeyEvent *e) {
+
+    switch ( tolower( e->ascii())) {
+        case 's':
+            save();
+            this->setCaption( "Pari QtPlot: " + *plotFile);
+            break;
+    }
+}
+#endif
+
+
+#ifdef __FANCY_WIN__
+void Plotter::mouseReleaseEvent( QMouseEvent*) {
+
+    emit clicked();
+}
+#endif
+
+
+
+#ifdef __FANCY_WIN__
+//
+// The envelope for an instance of plotter
+//
+
+
+
+
+/* XPM */
+static const char * const fullscreen_xpm[] = {
+"14 14 2 1",
+"         c None",
+".        c #000000",
+"..............",
+".     ..     .",
+".     ..     .",
+".    ....    .",
+".     ..     .",
+".  .  ..  .  .",
+"..............",
+"..............",
+".  .  ..  .  .",
+".     ..     .",
+".    ....    .",
+".     ..     .",
+".     ..     .",
+".............."};
+
+/*
+class SaveAsDialog: public
+#ifdef __QPE__
+//QDialog
+#else
+QFileDialog
+#endif
+{
+
+    Q_OBJECT
+
+public:
+    SaveAsDialog( const QString & c = QString::null,
+                  const QString & s = QString::null, int w = 0, int h = 0,
+                  QWidget *parent = 0, const char *name = 0, WFlags f = 0);
+    ~SaveAsDialog();
+#ifdef __QPE__
+    QString selectedFile() { return nameW->text();}
+#endif
+    int picWidth() { return widthW->value();}
+    int picHeight() { return heightW->value();}
+
+private:
+    QLineEdit *nameW;
+    QSpinBox *widthW, *heightW;
+
+};
+
+
+SaveAsDialog::SaveAsDialog( const QString & c, const QString & s, int w, int h,
+                            QWidget *parent, const char *name, WFlags f)
+#ifdef __QPE__
+    // simplistic dialog in case of QPE ( fancy alternative: class FileSelector)
+
+    : QDialog( parent, name, TRUE, f) {
+
+    if( c) this->setCaption( c);
+    nameW = new QLineEdit( this);
+    if( s) nameW->setText( s);
+    widthW = new QSpinBox( 1, 65536, 1, this);
+    if( w > 0) widthW->setValue( w);
+    heightW = new QSpinBox( 1, 65536, 1, this);
+    if( h > 0) heightW->setValue( h);
+
+    QVBoxLayout *top = new QVBoxLayout( this, 10);
+    QGridLayout *contents = new QGridLayout( 3, 2);
+
+    top->addLayout( contents);
+
+    QLabel *l;
+    l = new QLabel( nameW, "Name : ", this);
+    l->setAlignment( AlignRight | AlignVCenter);
+    contents->addWidget( l, 0, 0);
+    contents->addWidget( nameW, 0, 1);
+    l = new QLabel( widthW, "Width : ", this);
+    l->setAlignment( AlignRight | AlignVCenter);
+    contents->addWidget( l, 1, 0);
+    contents->addWidget( widthW, 1, 1);
+    l = new QLabel( heightW, "Height : ", this);
+    l->setAlignment( AlignRight | AlignVCenter);
+    contents->addWidget( l, 2, 0);
+    contents->addWidget( heightW, 2, 1);
+
+    top->activate();
+    this->resize( 160, this->height()); // hack!!!
+#else
+    : QFileDialog( parent, name, TRUE) {
+
+    if( c) this->setFilters( c);
+    if( s) this->setSelection( s);
+
+    QLabel *l;
+    QWidget *wt = new QWidget( this);
+    QHBoxLayout *spinBoxes = new QHBoxLayout( wt, 5);
+    widthW = new QSpinBox( 1, 65536, 1, wt);
+    l  = new QLabel( widthW, "&width ", wt);
+    spinBoxes->addWidget( l);
+    spinBoxes->addWidget( widthW);
+    if( w > 0) widthW->setValue( w);
+    heightW = new QSpinBox( 1, 65536, 1, wt);
+    spinBoxes->addSpacing(10);
+    l  = new QLabel( heightW, "&height ", wt);
+    l->setAlignment( AlignRight | AlignVCenter);
+    spinBoxes->addWidget( l);
+    spinBoxes->addWidget( heightW);
+    if( h > 0) heightW->setValue( h);
+    l = new QLabel( "Resolution:", this);
+    QFileDialog::addWidgets( l, wt, 0);
+#endif
+}
+
+
+SaveAsDialog::~SaveAsDialog() {
+}
+*/
+
+
+class PlotWindow: public QMainWindow {
+
+     Q_OBJECT
+
+public:
+    PlotWindow( long *w, long *x, long *y, long lw,
+                QWidget* parent = 0, const char* name = 0);
+    ~PlotWindow();
+
+#ifndef __QPE__
+protected:
+    void resizeEvent( QResizeEvent *);
+#endif
+
+private slots:
+    void fullScreen();
+    void normalView();
+    void save();
+    void save( int);
+
+private:
+    static const QList<QByteArray> file_formats;
+    Plotter *plr;
+    QString saveFileName;
+    int saveFileFormat;
+#ifndef __QPE__
+    QLabel *res;
+    QMenu* menuFile;
+    QMenu* menuView;
+    QMenu* menuFormat;
+    QAction* quitAction;
+    QAction* saveAction;
+    QAction* fullScreenAction;
+    QSignalMapper* signalMapper;
+    QIcon* icon;
+#endif
+};
+
+
+const QList<QByteArray> PlotWindow::file_formats = QImageWriter::supportedImageFormats();
+
+
+PlotWindow::PlotWindow( long *w, long *x, long *y, long lw,
+                        QWidget* parent, const char* name)
+    : QMainWindow( parent),
+      saveFileName( "pariplot"), saveFileFormat( 0) {
+
+    setWindowTitle( "Pari QtPlot");
+
+        QPalette palette;
+        palette.setColor(this->backgroundRole(), white);
+        this->setPalette(palette);
+
+        menuFile = menuBar()->addMenu("&File");
+
+        saveAction = new QAction("&Save",this);
+        saveAction->setShortcut(QKeySequence(CTRL+Key_S));
+        connect (saveAction, SIGNAL(triggered()), this, SLOT(save()));
+        menuFile->addAction(saveAction);
+
+        menuFormat = menuFile->addMenu("Save &as");
+
+        signalMapper = new QSignalMapper(this);
+
+        for( int i = 0; i < file_formats.count(); i++)
+        {
+        QAction* tmpAction;
+        tmpAction = new QAction(QString(file_formats.at(i)),this);
+        connect (tmpAction, SIGNAL(triggered()), signalMapper, SLOT(map()));
+        signalMapper->setMapping(tmpAction,i);
+        menuFormat->addAction(tmpAction);
+        }
+
+        connect (signalMapper, SIGNAL(mapped(int)), this,SLOT(save(int)));
+
+        quitAction = new QAction("&Quit",this);
+        quitAction->setShortcut(QKeySequence(CTRL+Key_Q));
+        connect (quitAction, SIGNAL(triggered()), this, SLOT(close()));
+        menuFile->addAction(quitAction);
+
+        menuView = menuBar()->addMenu("&View");
+
+        fullScreenAction = new QAction("Use &full screen", this);
+        fullScreenAction->setShortcut(QKeySequence(CTRL+Key_F));
+        icon = new QIcon();
+        icon->addPixmap(QPixmap( (const char ** )fullscreen_xpm));
+        fullScreenAction->setIcon(*icon);
+        connect(fullScreenAction, SIGNAL( triggered()), this, SLOT( fullScreen()));
+        menuView->addAction(fullScreenAction);
+
+    // Setting up an instance of plotter
+    plr = new Plotter( w, x, y, lw, this);
+    connect( plr, SIGNAL(clicked()), this, SLOT( normalView()));
+    this->setCentralWidget( plr);
+
+#ifndef __QPE__
+    this->resize( pari_plot.width,
+                  pari_plot.height + 24);
+    res = new QLabel( );
+    statusBar()->addWidget( res);
+#endif
+}
+
+
+PlotWindow::~PlotWindow() {
+}
+
+
+#ifndef __QPE__
+void PlotWindow::resizeEvent( QResizeEvent *e) {
+
+    QMainWindow::resizeEvent( e);
+    res->setText( QString( "Resolution: ") +
+                  QString::number( plr->width()) + "x" +
+                  QString::number( plr->height()));
+    res->setFixedSize( res->sizeHint());
+}
+#endif
+
+
+void PlotWindow::fullScreen() {
+    plr->setParent( 0);
+    plr->showMaximized();
+    plr->show();
+
+}
+
+
+void PlotWindow::normalView() {
+
+    if ( !plr->parentWidget()) {
+        plr->setParent( this);
+        this->setCentralWidget( plr);
+        plr->show();
+    }
+}
+
+
+void PlotWindow::save() {
+
+    QString ff = QString( file_formats.at( saveFileFormat));
+    QString fn = saveFileName + "." + ff.toLower();
+    plr->save( fn, ff);
+    setWindowTitle( QString( "Pari QtPlot:") + fn);
+#ifndef __QPE__
+    //statusBar()->message( QString( "File %1 saved" ).arg( fn), 2000 );
+#endif
+}
+
+
+void PlotWindow::save( int id)
+{
+        QString ff( file_formats.at( id));
+        QString s( ff + " (*." + ff.toLower() +");;All (*)");
+        QString fn = QFileDialog::getSaveFileName(this, saveFileName + "." + ff,
+        saveFileName + "." + ff, s);
+        if ( !fn.isEmpty() )
+        {
+                saveFileName = fn;
+                int p;
+                if( (p = saveFileName.lastIndexOf( "." + ff, -1)) >=0)
+                        saveFileName.truncate( p);
+                saveFileFormat = id;
+                save();
+        }
+}
+
+
+#include "plotQt4.moc.cpp"
+#endif // __FANCY_WIN__
+
+
+
+//
+// Implementation of the two architecture-dependent functions
+// (from rect.h) requested by pari's plotting routines
+//
+
+
+void
+rectdraw0(long *w, long *x, long *y, long lw)
+{
+    if (pari_daemon()) return;  // parent process returns
+
+    pari_close();
+    PARI_get_plot();
+
+    // launch Qt window
+    int argc = 1;                         // set argc = 2 for cross
+    char * argv[] = { "gp", "-qws"}; // development using qvfb
+#ifdef __QPE__
+    QPEApplication
+#else
+    QApplication
+#endif
+        a( argc, argv);
+#ifdef __FANCY_WIN__
+    PlotWindow *win = new PlotWindow(w, x, y, lw);
+#else
+    Plotter *win = new Plotter( w, x, y, lw);
+#endif
+#ifdef __QPE__
+    a.showMainWidget( win);
+#else
+    //a.setMainWidget( win);
+    win->show();
+#endif
+    a.exec();
+    exit( 0);
+}
+
+void
+PARI_get_plot()
+/* This function initialises the structure rect.h: pari_plot */
+{
+    if (pari_plot.init) return;      // pari_plot is already set
+#ifdef __QPE__
+    pari_plot.width   = 240;         // width and
+    pari_plot.height  = 320;         //  height of plot window
+#else
+    pari_plot.width   = 400;         // width and
+    pari_plot.height  = 300;         //  height of plot window
+#endif
+    pari_plot.hunit   = 3;           //
+    pari_plot.vunit   = 3;           //
+    pari_plot.fwidth  = 6;           // font width
+    pari_plot.fheight = 9;           //   and height
+    pari_plot.init    = 1;           // flag: pari_plot is set now!
+}
diff --git a/src/graph/plotWin32.c b/src/graph/plotWin32.c
new file mode 100644
index 0000000..17dfc32
--- /dev/null
+++ b/src/graph/plotWin32.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 2009  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Written by Vasili Burdo */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "rect.h"
+#include <windows.h>
+#include <time.h>
+
+static void SetForeground(void *data, long col)
+{
+  int r,g,b;
+  color_to_rgb(gel(pari_colormap,col), &r, &g, &b);
+
+  HPEN hOldPen = SelectObject((HDC)data, CreatePen(PS_SOLID, 2, RGB(r,g,b)));
+  if( hOldPen ) DeleteObject(hOldPen);
+}
+
+static void DrawPoint(void *data, long x, long y)
+{
+  Ellipse((HDC)data,x-1,y-1,x+1,y+1);
+}
+
+static void DrawLine(void *data, long x1, long y1, long x2, long y2)
+{
+  MoveToEx((HDC)data, x1, y1, NULL);
+  LineTo((HDC)data,x2,y2);
+}
+
+static void DrawRectangle(void *data, long x, long y, long w, long h)
+{
+  RECT rc;
+  rc.left = x; rc.right  = x+w;
+  rc.top  = y; rc.bottom = y+h;
+  FrameRect((HDC)data, &rc, GetStockObject(HOLLOW_BRUSH));
+}
+
+static void DrawPoints(void *data, long nb, struct plot_points *p)
+{
+  long i;
+  for (i=0; i<nb; ++i)
+    DrawPoint(data,p[i].x,p[i].y);
+}
+
+static void DrawLines(void *data, long nb, struct plot_points *p)
+{
+  long i;
+  MoveToEx((HDC)data, p[0].x, p[0].y, NULL);
+  for(i=1; i<nb; ++i)
+    LineTo((HDC)data,p[i].x,p[i].y);
+}
+
+static void DrawString(void *data, long x, long y, char *text, long numtext)
+{
+  TextOut((HDC)data, x, y, text, numtext);
+}
+
+void rectdraw0(long *w, long *x, long *y, long lw)
+{
+  char tmppath[MAX_PATH], fname[MAX_PATH];
+  struct plot_eng plotWin32;
+  HDC hEmf;
+
+  GetTempPath(sizeof(tmppath), tmppath);
+  sprintf(fname, "%s\\gp-ploth-%lx.emf", tmppath, time(NULL)/(24*60*60)*1000+GetTickCount());
+
+  hEmf = CreateEnhMetaFile(GetDC(NULL), fname, NULL, NULL);
+  SetMapMode(hEmf, MM_TEXT);
+  SelectObject(hEmf, GetStockObject(DEFAULT_GUI_FONT));
+  SetBkColor(hEmf, RGB(255,255,255));
+  SetBkMode(hEmf, TRANSPARENT);
+
+  plotWin32.sc=&SetForeground;
+  plotWin32.pt=&DrawPoint;
+  plotWin32.ln=&DrawLine;
+  plotWin32.bx=&DrawRectangle;
+  plotWin32.mp=&DrawPoints;
+  plotWin32.ml=&DrawLines;
+  plotWin32.st=&DrawString;
+  plotWin32.pl=&pari_plot;
+  plotWin32.data=(void*)hEmf;
+
+  gen_rectdraw0(&plotWin32, w, x, y, lw, 1, 1);
+  DeleteEnhMetaFile(CloseEnhMetaFile(hEmf));
+
+  ShellExecute(NULL,NULL,fname,NULL,NULL,SW_SHOWDEFAULT);
+}
+
+void
+PARI_get_plot()
+{
+  HDC hdc;
+  TEXTMETRIC tm;
+  if (pari_plot.init) return;      /* pari_plot is already set */
+
+  pari_plot.init    = 1;
+  pari_plot.width   = GetSystemMetrics(SM_CXSCREEN)/2;
+  pari_plot.height  = GetSystemMetrics(SM_CYSCREEN)/2;
+  pari_plot.hunit   = pari_plot.width/100;
+  pari_plot.vunit   = pari_plot.height/100;
+
+  hdc = GetDC(0);
+  SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+  GetTextMetrics(hdc, &tm);
+  ReleaseDC(0,hdc);
+
+  pari_plot.fwidth  = tm.tmAveCharWidth;
+  pari_plot.fheight = tm.tmHeight;
+}
diff --git a/src/graph/plotX.c b/src/graph/plotX.c
new file mode 100644
index 0000000..53af787
--- /dev/null
+++ b/src/graph/plotX.c
@@ -0,0 +1,298 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                       HIGH RESOLUTION PLOT                      */
+/*                                                                 */
+/*******************************************************************/
+
+#include "pari.h"
+#include "paripriv.h"
+#include "rect.h"
+
+#ifdef HPPA
+#  ifndef __GNUC__
+     typedef char *caddr_t;
+#  endif
+#endif
+
+BEGINEXTERN
+#include <X11/XKBlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#ifndef XK_c
+#  include <X11/keysym.h>
+#endif
+ENDEXTERN
+
+static Colormap PARI_Colormap;
+static XColor  *PARI_Colors;
+
+struct data_x
+{
+  Display *display;
+  Window win;
+  int numcolors;
+  GC gc;
+};
+
+/* after fork(), we don't want the child to recover but to exit */
+static void
+exiterr(const char *str)
+{
+  term_color(c_ERR);
+  err_printf("\n  *** X fatal error: %s\n",str);
+  term_color(c_NONE); exit(1);
+}
+
+static void SetForeground(void *data, long col)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XSetForeground(dx->display,dx->gc, PARI_Colors[col].pixel);
+}
+
+static void DrawPoint(void *data, long x, long y)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XDrawPoint(dx->display,dx->win,dx->gc, x,y);
+}
+
+static void DrawLine(void *data, long x1, long y1, long x2, long y2)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XDrawLine(dx->display,dx->win,dx->gc, x1,y1, x2,y2);
+}
+
+static void DrawRectangle(void *data, long x, long y, long w, long h)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XDrawRectangle(dx->display,dx->win,dx->gc, x,y, w,h);
+}
+
+static void DrawPoints(void *data, long nb, struct plot_points *p)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XPoint *xp=(XPoint*)pari_malloc(sizeof(xp)*nb);
+  long i;
+  for (i=0;i<nb;i++)
+  {
+    xp[i].x=p[i].x;
+    xp[i].y=p[i].y;
+  }
+  XDrawPoints(dx->display,dx->win,dx->gc, xp, nb, 0);
+  pari_free(xp);
+}
+
+static void DrawLines(void *data, long nb, struct plot_points *p)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XPoint *xp=(XPoint*)pari_malloc(sizeof(xp)*nb);
+  long i;
+  for (i=0;i<nb;i++)
+  {
+    xp[i].x=p[i].x;
+    xp[i].y=p[i].y;
+  }
+  XDrawLines(dx->display,dx->win,dx->gc, xp, nb, 0);
+  pari_free(xp);
+}
+
+static void DrawString(void *data, long x, long y, char *text, long numtext)
+{
+  struct data_x *dx = (struct data_x *) data;
+  XDrawString(dx->display,dx->win,dx->gc, x,y, text, numtext);
+}
+
+#define MAX_BUF 256
+
+static int
+Xerror(Display *d, XErrorEvent *pari_err) {
+  char buf[MAX_BUF];
+  XGetErrorText(d,pari_err->error_code,buf,MAX_BUF);
+  exiterr(buf); return 0;
+}
+
+static int
+IOerror(Display *d) {
+  char buf[MAX_BUF];
+  sprintf(buf, "lost display on %s", DisplayString(d));
+  exiterr(buf); return 0;
+}
+
+static void
+PARI_ColorSetUp(Display *display, GEN colors)
+{
+  static int init_done = 0;
+  long i, n = lg(colors)-1;
+
+  if (init_done) return;
+  init_done=1;
+
+  PARI_Colormap = DefaultColormap(display, 0);
+  PARI_Colors = (XColor *) pari_malloc((n+1) * sizeof(XColor));
+  for (i=0; i<n; i++)
+  {
+    int r, g, b;
+    color_to_rgb(gel(colors,i+1), &r, &g, &b);
+    PARI_Colors[i].red   = r*65535/255;
+    PARI_Colors[i].green = g*65535/255;
+    PARI_Colors[i].blue  = b*65535/255;
+    PARI_Colors[i].flags = DoRed | DoGreen | DoBlue;
+    if (!XAllocColor(display, PARI_Colormap, &PARI_Colors[i]))
+      exiterr("cannot allocate color");
+  }
+}
+
+void
+rectdraw0(long *w, long *x, long *y, long lw)
+{
+  long oldwidth,oldheight;
+  struct plot_eng plotX;
+  struct data_x dx;
+  double xs = 1, ys = 1;
+  int screen, keystate;
+  Display *display;
+  GC gc;
+  Window win;
+  XEvent event;
+  XSizeHints size_hints;
+  XFontStruct *font_info;
+  XSetWindowAttributes attrib;
+  Atom wm_delete_window, wm_protocols;
+
+  if (pari_daemon()) return;  /* parent process returns */
+
+  PARI_get_plot();
+  pari_close();
+
+  display = XOpenDisplay(NULL);
+  font_info = XLoadQueryFont(display, "9x15");
+  if (!font_info) exiterr("cannot open 9x15 font");
+  XSetErrorHandler(Xerror);
+  XSetIOErrorHandler(IOerror);
+  PARI_ColorSetUp(display,pari_colormap);
+
+  screen = DefaultScreen(display);
+  win = XCreateSimpleWindow
+    (display, RootWindow(display, screen), 0, 0,
+     pari_plot.width, pari_plot.height, 4,
+     PARI_Colors[1].pixel, PARI_Colors[0].pixel);
+
+  size_hints.flags = PPosition | PSize;
+  size_hints.x = 0;
+  size_hints.y = 0;
+  size_hints.width  = pari_plot.width;
+  size_hints.height = pari_plot.height;
+  XSetStandardProperties
+    (display, win, "rectplot", NULL, None, NULL, 0, &size_hints);
+
+  wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
+  wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False);
+  XSetWMProtocols(display,win,&wm_delete_window, 1);
+
+  XSelectInput (display, win,
+    ExposureMask | ButtonPressMask | KeyReleaseMask | StructureNotifyMask);
+
+  /* enable backing-store */
+  attrib.backing_store = Always;
+  attrib.backing_planes = AllPlanes;
+  XChangeWindowAttributes(display,win,CWBackingStore|CWBackingPlanes,&attrib);
+
+  gc = XCreateGC(display, win, 0, NULL);
+  XSetFont(display, gc, font_info->fid);
+
+  XClearWindow(display, win);
+  XMapWindow(display, win);
+  oldwidth  = pari_plot.width;
+  oldheight = pari_plot.height;
+  dx.display= display;
+  dx.win = win;
+  dx.numcolors = lg(pari_colormap)-1;
+  dx.gc = gc;
+  plotX.sc = &SetForeground;
+  plotX.pt = &DrawPoint;
+  plotX.ln = &DrawLine;
+  plotX.bx = &DrawRectangle;
+  plotX.mp = &DrawPoints;
+  plotX.ml = &DrawLines;
+  plotX.st = &DrawString;
+  plotX.pl = &pari_plot;
+  plotX.data = (void*)&dx;
+
+  for(;;)
+  {
+    XNextEvent(display, &event);
+    switch(event.type)
+    {
+      case ClientMessage:
+        if (event.xclient.message_type != wm_protocols ||
+            (Atom)event.xclient.data.l[0] != wm_delete_window) break;
+      case ButtonPress:
+      case DestroyNotify:
+EXIT:
+        XUnloadFont(display,font_info->fid);
+        XFreeGC(display,gc);
+        XCloseDisplay(display); exit(0);
+
+      case KeyRelease:
+        /* Mod4 == Super on "std" Linux */
+        keystate = event.xkey.state & (ShiftMask|ControlMask|Mod1Mask|Mod4Mask);
+        switch (XkbKeycodeToKeysym(display, event.xkey.keycode, 0,0))
+        {
+        case XK_q:
+          if (!keystate || keystate == ControlMask) goto EXIT;
+          break;
+        case XK_c:
+          if (keystate == ControlMask) goto EXIT;
+          break;
+        }
+        break;
+
+      case ConfigureNotify:
+      {
+        int width  = event.xconfigure.width;
+        int height = event.xconfigure.height;
+
+        if (width == oldwidth && height == oldheight) break;
+        oldwidth  = width;
+        oldheight = height;
+
+        /* recompute scale */
+        xs = ((double)width)/pari_plot.width;
+        ys = ((double)height)/pari_plot.height;
+      }
+      case Expose:
+        gen_rectdraw0(&plotX, w, x, y,lw,xs,ys);
+    }
+  }
+}
+
+void
+PARI_get_plot()
+{
+  Display *display;
+  int screen;
+
+  if (pari_plot.init) return;
+  if (!(display = XOpenDisplay(NULL))) pari_err(e_MISC, "no X server");
+  screen = DefaultScreen(display);
+  pari_plot.width  = DisplayWidth(display, screen) - 40;
+  pari_plot.height = DisplayHeight(display, screen) - 60;
+  pari_plot.fheight = 15;
+  pari_plot.fwidth  = 9;
+  pari_plot.hunit   = 5;
+  pari_plot.vunit   = 5;
+  pari_plot.init = 1;
+  XCloseDisplay(display);
+}
diff --git a/src/graph/plotfltk.c b/src/graph/plotfltk.c
new file mode 100644
index 0000000..e04cc41
--- /dev/null
+++ b/src/graph/plotfltk.c
@@ -0,0 +1,229 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/////////////////////////////////////////////////////////////////////////////
+//
+//  High resolution plot using FLTK library
+//  Bill Allombert 2003
+//
+//  Based on plotQt by Nils-Peter Skoruppa (www.countnumber.de)
+/////////////////////////////////////////////////////////////////////////////
+extern "C" {
+#include "pari.h"
+#include "paripriv.h"
+#include "rect.h"
+}
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/fl_draw.H>
+
+static long numcolors;
+
+class Plotter: public Fl_Window {
+
+public:
+    Plotter( long *w, long *x, long *y, long lw, const char* name = 0);
+
+private:
+    void draw();
+    int handle(int event);
+
+private:
+    long *my_w;                        // map into rectgraph indexes
+    long *my_x;                        // x, y: array of x,y-coordinates of the
+    long *my_y;                        // top left corners of the rectwindows
+    long my_lw;                        // lw: number of rectwindows
+    Fl_Color *color;
+};
+
+static Fl_Color
+rgb_color(int R, int G, int B)
+{
+  return fl_color_cube(R*FL_NUM_RED/256, G*FL_NUM_GREEN/256, B*FL_NUM_BLUE/256);
+}
+
+Plotter::Plotter( long *w, long *x, long *y, long lw,
+             const char* name)
+        : Fl_Window(pari_plot.width, pari_plot.height, "PARI/GP")
+
+{
+    long i;
+
+    this->my_w=w; this->my_x=x; this->my_y=y; this->my_lw=lw;
+    numcolors = lg(pari_colormap)-1;
+    color = (Fl_Color*)pari_malloc(numcolors*sizeof(Fl_Color));
+    for (i = 1; i < lg(pari_colormap); i++)
+    {
+      int r, g, b;
+      color_to_rgb(gel(pari_colormap,i), &r, &g, &b);
+      color[i-1] = rgb_color(r, g, b);
+    }
+}
+
+static void DrawPoint(void *data, long x, long y)
+{
+  (void)data;
+  fl_point(x,y);
+}
+
+static void DrawLine(void *data, long x1, long y1, long x2, long y2)
+{
+  (void)data;
+  fl_line(x1,y1, x2,y2);
+}
+
+static void DrawRectangle(void *data, long x, long y, long w, long h)
+{
+  (void)data;
+  fl_rect(x,y,w,h);
+}
+
+static void DrawPoints(void *data, long nb, struct plot_points *p)
+{
+  long i;
+  (void)data;
+  for (i=0; i<nb; i++) fl_point(p[i].x, p[i].y);
+}
+
+static void SetForeground(void *data, long col)
+{
+  Fl_Color *color = (Fl_Color*)data;
+  fl_color(color[col]);
+}
+
+static void DrawLines(void *data, long nb, struct plot_points *p)
+{
+  long i;
+  (void)data;
+  for (i=1; i<nb; i++) fl_line(p[i-1].x, p[i-1].y, p[i].x, p[i].y);
+}
+
+static void DrawString(void *data, long x, long y, char *text, long numtext)
+{
+  (void)data;
+  fl_draw(text,numtext,x,y);
+}
+
+void Plotter::draw()
+{
+  struct plot_eng pl;
+
+  double xs = double(this->w())/pari_plot.width;
+  double ys = double(this->h())/pari_plot.height;
+
+  fl_font(FL_COURIER, int(pari_plot.fheight * xs));
+  fl_color(color[0]); // transparent window on Windows otherwise
+  fl_rectf(0, 0, this->w(), this->h());
+  pl.sc = &SetForeground;
+  pl.pt = &DrawPoint;
+  pl.ln = &DrawLine;
+  pl.bx = &DrawRectangle;
+  pl.mp = &DrawPoints;
+  pl.ml = &DrawLines;
+  pl.st = &DrawString;
+  pl.pl = &pari_plot;
+  pl.data = (void*)color;
+  gen_rectdraw0(&pl, my_w, my_x, my_y, my_lw, xs, ys);
+}
+
+int Plotter::handle(int event)
+{
+  switch(event)
+  {
+  case FL_PUSH:
+    switch(Fl::event_button())
+    {
+    case 1:
+     exit(0);
+    case 2:
+     {
+       static int flag=0;
+       static int my_x;
+       static int my_y;
+       static int my_w;
+       static int my_h;
+       flag=1-flag;
+       if (flag)
+       {
+         my_x=this->x();
+         my_y=this->y();
+         my_w=this->w();
+         my_h=this->h();
+         this->fullscreen();
+       }
+       else
+       {
+         this->fullscreen_off(my_x,my_y,my_w,my_h);
+         this->size_range(1,1);
+       }
+       return 1;
+     }
+    }
+  case FL_KEYUP:
+    switch(Fl::event_key())
+    {
+    case 'q':
+      switch(Fl::event_shift())
+      {
+        case 0:
+        case FL_CTRL:
+          exit(0);
+      }
+      break;
+    case 'c':
+      if (Fl::event_state() == FL_CTRL) exit(0);
+      break;
+    }
+  default:
+    return 0;
+  }
+}
+
+//
+// Implementation of the two architecture-dependent functions
+// (from rect.h) requested by pari's plotting routines
+//
+
+void
+rectdraw0(long *w, long *x, long *y, long lw)
+{
+    Plotter *win;
+
+    if (pari_daemon()) return;  // parent process returns
+
+    pari_close();
+    PARI_get_plot();
+
+    Fl::visual(FL_DOUBLE|FL_INDEX);
+    win = new Plotter( w, x, y, lw);
+    win->size_range(1,1);
+    win->box(FL_FLAT_BOX);
+    win->end();
+    win->show();
+    Fl::run();
+    exit(0);
+}
+
+void
+PARI_get_plot()
+/* This function initialises the structure rect.h: pari_plot */
+{
+    if (pari_plot.init) return;      // pari_plot is already set
+    pari_plot.width   = 400;         // width and
+    pari_plot.height  = 300;         //  height of plot window
+    pari_plot.hunit   = 3;           //
+    pari_plot.vunit   = 3;           //
+    pari_plot.fwidth  = 6;           // font width
+    pari_plot.fheight = 9;           //   and height
+    pari_plot.init    = 1;           // flag: pari_plot is set now!
+}
diff --git a/src/graph/plotnull.c b/src/graph/plotnull.c
new file mode 100644
index 0000000..b08fbbc
--- /dev/null
+++ b/src/graph/plotnull.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "rect.h"
+
+void
+rectdraw0(long *w, long *x, long *y, long lw)
+{
+  (void)w;
+  (void)x;
+  (void)y;
+  (void)lw;
+}
+
+void
+PARI_get_plot()
+{ pari_err(e_MISC,"high resolution graphics disabled"); }
diff --git a/src/graph/plotport.c b/src/graph/plotport.c
new file mode 100644
index 0000000..da17978
--- /dev/null
+++ b/src/graph/plotport.c
@@ -0,0 +1,2836 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                         PLOT ROUTINES                           */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#include "rect.h"
+
+void postdraw0(long *w, long *x, long *y, long lw, long scale);
+static void PARI_get_psplot(void);
+
+/* no need for THREAD: OK to share this */
+static hashtable *rgb_colors = NULL;
+PariRect *rectgraph[18]; /*NUMRECT*/
+
+/* no need for THREAD: gp-specific */
+static long current_color[18]; /*NUMRECT*/
+
+PARI_plot pari_plot, pari_psplot;
+PARI_plot *pari_plot_engine = &pari_plot;
+long rectpoint_itype = 0, rectline_itype  = 0;
+
+const long NUMRECT = 18;
+const long RECUR_MAXDEPTH = 10;
+const double RECUR_PREC = 0.001;
+const long DEFAULT_COLOR = 1, AXIS_COLOR = 2;
+
+INLINE long
+DTOL(double t) { return (long)(t + 0.5); }
+
+/********************************************************************/
+/**                                                                **/
+/**                         LOW-RES PLOT                           **/
+/**                                                                **/
+/********************************************************************/
+#define ISCR 64
+#define JSCR 22
+static char
+PICT(long j) {
+  switch(j%3) {
+    case 0:  return '_';
+    case 1:  return 'x';
+    default: return '"';
+  }
+}
+static char
+PICTZERO(long j) {
+  switch(j%3) {
+    case 0:  return ',';
+    case 1:  return '-';
+    default: return '`';
+  }
+}
+
+static char *
+dsprintf9(double d, char *buf)
+{
+  int i = 10;
+
+  while (--i >= 0) {
+    sprintf(buf, "%9.*g", i, d);
+    if (strlen(buf) <= 9) return buf;
+  }
+  return buf; /* Should not happen? */
+}
+
+typedef unsigned char screen[ISCR+1][JSCR+1];
+
+static void
+fill_gap(screen scr, long i, int jnew, int jpre)
+{
+  int mid, i_up, i_lo, up, lo;
+
+  if (jpre < jnew - 2) {
+    up = jnew - 1; i_up = i;
+    lo = jpre + 1; i_lo = i - 1;
+  } else if (jnew < jpre - 2) {
+    up = jpre - 1; i_up = i - 1;
+    lo = jnew + 1; i_lo = i;
+  } else return; /* if gap < 2, leave it as it is. */
+
+  mid = (jpre+jnew)/2;
+  if (mid>JSCR) mid=JSCR; else if (mid<0) mid=0;
+  if (lo<0) lo=0;
+  if (lo<=JSCR) while (lo <= mid) scr[i_lo][lo++] = ':';
+  if (up>JSCR) up=JSCR;
+  if (up>=0) while (up > mid) scr[i_up][up--] = ':';
+}
+
+static double
+todbl(GEN x) { return rtodbl(gtofp(x, LOWDEFAULTPREC)); }
+
+/* code is either a t_CLOSURE (from GP: ploth, etc.) or a t_POL/t_VEC of
+ * two t_POLs from rectsplines */
+static GEN
+READ_EXPR(GEN code, GEN x) {
+  if (typ(code)!=t_CLOSURE) return gsubst(code,0,x);
+  set_lex(-1, x); return closure_evalgen(code);
+}
+
+void
+plot(GEN a, GEN b, GEN code, GEN ysmlu,GEN ybigu, long prec)
+{
+  const char BLANK = ' ', YY = '|', XX_UPPER = '\'', XX_LOWER = '.';
+  long jz, j, i, sig;
+  pari_sp av = avma, av2, limite;
+  int jnew, jpre = 0; /* for lint */
+  GEN x, dx;
+  double diff, dyj, ysml, ybig, y[ISCR+1];
+  screen scr;
+  char buf[80], z;
+
+  sig=gcmp(b,a); if (!sig) return;
+  if (sig<0) { x=a; a=b; b=x; }
+  x = gtofp(a, prec); push_lex(x, code);
+  dx = divru(gtofp(gsub(b,a),prec), ISCR-1);
+  for (j=1; j<=JSCR; j++) scr[1][j]=scr[ISCR][j]=YY;
+  for (i=2; i<ISCR; i++)
+  {
+    scr[i][1]   = XX_LOWER;
+    scr[i][JSCR]= XX_UPPER;
+    for (j=2; j<JSCR; j++) scr[i][j] = BLANK;
+  }
+  av2 = avma; limite=stack_lim(av2,1);
+  ysml = ybig = 0.; /* -Wall */
+  for (i=1; i<=ISCR; i++)
+  {
+    y[i] = gtodouble( READ_EXPR(code,x) );
+    if (i == 1)
+      ysml = ybig = y[1];
+    else
+    {
+      if (y[i] < ysml) ysml = y[i];
+      if (y[i] > ybig) ybig = y[i];
+    }
+    x = addrr(x,dx);
+    if (low_stack(limite, stack_lim(av2,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"plot");
+      x = gerepileuptoleaf(av2, x);
+    }
+  }
+  avma = av;
+  if (ysmlu) ysml = gtodouble(ysmlu);
+  if (ybigu) ybig = gtodouble(ybigu);
+  diff = ybig - ysml;
+  if (!diff) { ybig += 1; diff= 1.; }
+  dyj = ((JSCR-1)*3+2) / diff;
+  /* work around bug in gcc-4.8 (32bit): plot(x=-5,5,sin(x)))) */
+  jz = 3 - (long)(ysml*dyj + 0.5); /* 3 - DTOL(ysml*dyj) */
+  z = PICTZERO(jz); jz /= 3;
+  for (i=1; i<=ISCR; i++, avma = av2)
+  {
+    if (0<=jz && jz<=JSCR) scr[i][jz]=z;
+    j = 3 + DTOL((y[i]-ysml)*dyj);
+    jnew = j/3;
+    if (i > 1) fill_gap(scr, i, jnew, jpre);
+    if (0<=jnew && jnew<=JSCR) scr[i][jnew] = PICT(j);
+    jpre = jnew;
+  }
+  pari_putc('\n');
+  pari_printf("%s ", dsprintf9(ybig, buf));
+  for (i=1; i<=ISCR; i++) pari_putc(scr[i][JSCR]);
+  pari_putc('\n');
+  for (j=(JSCR-1); j>=2; j--)
+  {
+    pari_puts("          ");
+    for (i=1; i<=ISCR; i++) pari_putc(scr[i][j]);
+    pari_putc('\n');
+  }
+  pari_printf("%s ", dsprintf9(ysml, buf));
+  for (i=1; i<=ISCR; i++)  pari_putc(scr[i][1]);
+  pari_putc('\n');
+  {
+    char line[10 + 32 + 32 + ISCR - 9];
+    sprintf(line, "%10s%-9.7g%*.7g\n"," ",todbl(a),ISCR-9,todbl(b));
+    pari_printf(line);
+  }
+  pop_lex(1);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                      RECTPLOT FUNCTIONS                        **/
+/**                                                                **/
+/********************************************************************/
+void
+init_graph(void)
+{
+  long n;
+  for (n=0; n<NUMRECT; n++)
+  {
+    PariRect *e = (PariRect*) pari_malloc(sizeof(PariRect));
+    e->head = e->tail = NULL;
+    e->sizex = e->sizey = 0;
+    current_color[n] = DEFAULT_COLOR;
+    rectgraph[n] = e;
+  }
+}
+
+void
+free_graph(void)
+{
+  int i;
+  for (i=0; i<NUMRECT; i++)
+  {
+    PariRect *e = rectgraph[i];
+    if (RHead(e)) killrect(i);
+    pari_free((void *)e);
+  }
+  if (rgb_colors)
+  {
+    pari_free((void*)rgb_colors->table);
+    pari_free((void*)rgb_colors);
+  }
+  if (pari_colormap) pari_free(pari_colormap);
+  if (pari_graphcolors) pari_free(pari_graphcolors);
+}
+
+static PariRect *
+check_rect(long ne)
+{
+  const long m = NUMRECT-1;
+  if (ne < 0)
+    pari_err_DOMAIN("graphic function", "rectwindow", "<", gen_0, stoi(ne));
+  if (ne > m)
+    pari_err_DOMAIN("graphic function", "rectwindow", ">", stoi(m), stoi(ne));
+  return rectgraph[ne];
+}
+
+static PariRect *
+check_rect_init(long ne)
+{
+  PariRect *e = check_rect(ne);
+  if (!RHead(e))
+    pari_err_TYPE("graphic function [use plotinit() first]", stoi(ne));
+  return e;
+}
+
+static long
+initrect_get_arg(GEN x, long flag, long *dft)
+{ /* FIXME: gequal0(x) undocumented backward compatibility hack */
+  if (!x || gequal0(x) || flag) { PARI_get_plot(); return *dft - 1; }
+  if (typ(x) != t_INT) pari_err_TYPE("initrect",x);
+  return itos(x);
+}
+void
+initrect_gen(long ne, GEN x, GEN y, long flag)
+{
+  const long m = NUMRECT-3;
+  long xi, yi;
+
+  xi = initrect_get_arg(x, flag, &pari_plot.width);
+  yi = initrect_get_arg(y, flag, &pari_plot.height);
+  if (flag) {
+    if (x) xi = DTOL(xi * gtodouble(x));
+    if (y) yi = DTOL(yi * gtodouble(y));
+  }
+  if (ne > m)
+    pari_err_DOMAIN("graphic function", "rectwindow", ">", stoi(m), stoi(ne));
+  initrect(ne, xi, yi);
+}
+
+static void
+Rchain(PariRect *e, RectObj *z)
+{
+  if (!RHead(e)) RHead(e) = z; else RoNext(RTail(e)) = z;
+  RTail(e) = z;
+  RoNext(z) = NULL;
+}
+
+void
+initrect(long ne, long x, long y)
+{
+  PariRect *e;
+  RectObj *z;
+
+  if (x <= 1) pari_err_DOMAIN("initrect", "x", "<=", gen_1, stoi(x));
+  if (y <= 1) pari_err_DOMAIN("initrect", "y", "<=", gen_1, stoi(y));
+  e = check_rect(ne); if (RHead(e)) killrect(ne);
+
+  z = (RectObj*) pari_malloc(sizeof(RectObj));
+  RoType(z) = ROt_NULL;
+  Rchain(e, z);
+  RXsize(e) = x; RXcursor(e) = 0;
+  RYsize(e) = y; RYcursor(e) = 0;
+  RXscale(e) = 1; RXshift(e) = 0;
+  RYscale(e) = 1; RYshift(e) = 0;
+  RHasGraph(e) = 0;
+}
+
+GEN
+rectcursor(long ne)
+{
+  PariRect *e = check_rect_init(ne);
+  return mkvec2s((long)RXcursor(e), (long)RYcursor(e));
+}
+
+static void
+rectscale0(long ne, double x1, double x2, double y1, double y2)
+{
+  PariRect *e = check_rect_init(ne);
+  double x, y;
+
+  x = RXshift(e) + RXscale(e) * RXcursor(e);
+  y = RYshift(e) + RYscale(e) * RYcursor(e);
+  RXscale(e) = RXsize(e)/(x2-x1); RXshift(e) = -x1*RXscale(e);
+  RYscale(e) = RYsize(e)/(y1-y2); RYshift(e) = -y2*RYscale(e);
+  RXcursor(e) = (x - RXshift(e)) / RXscale(e);
+  RYcursor(e) = (y - RYshift(e)) / RYscale(e);
+}
+
+void
+rectscale(long ne, GEN x1, GEN x2, GEN y1, GEN y2)
+{
+  rectscale0(ne, gtodouble(x1), gtodouble(x2), gtodouble(y1), gtodouble(y2));
+}
+
+static void
+rectmove0(long ne, double x, double y, long relative)
+{
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObj1P));
+
+  if (relative) { RXcursor(e) += x; RYcursor(e) += y; }
+  else          { RXcursor(e) = x; RYcursor(e) = y; }
+  RoType(z) = ROt_MV;
+  RoMVx(z) = RXcursor(e) * RXscale(e) + RXshift(e);
+  RoMVy(z) = RYcursor(e) * RYscale(e) + RYshift(e);
+  Rchain(e, z);
+}
+
+void
+rectmove(long ne, GEN x, GEN y)
+{
+  rectmove0(ne,gtodouble(x),gtodouble(y),0);
+}
+
+void
+rectrmove(long ne, GEN x, GEN y)
+{
+  rectmove0(ne,gtodouble(x),gtodouble(y),1);
+}
+
+void
+rectpoint0(long ne, double x, double y,long relative) /* code = ROt_MV/ROt_PT */
+{
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObj1P));
+
+  if (relative) { RXcursor(e) += x; RYcursor(e) += y; }
+  else          { RXcursor(e) = x; RYcursor(e) = y; }
+  RoPTx(z) = RXcursor(e)*RXscale(e) + RXshift(e);
+  RoPTy(z) = RYcursor(e)*RYscale(e) + RYshift(e);
+  RoType(z) = ( DTOL(RoPTx(z)) < 0
+                || DTOL(RoPTy(z)) < 0 || DTOL(RoPTx(z)) > RXsize(e)
+                || DTOL(RoPTy(z)) > RYsize(e) ) ? ROt_MV : ROt_PT;
+  Rchain(e, z);
+  RoCol(z) = current_color[ne];
+}
+
+void
+rectpoint(long ne, GEN x, GEN y)
+{
+  rectpoint0(ne,gtodouble(x),gtodouble(y),0);
+}
+
+void
+rectrpoint(long ne, GEN x, GEN y)
+{
+  rectpoint0(ne,gtodouble(x),gtodouble(y),1);
+}
+
+void
+rectcolor(long ne, long c)
+{
+  long n = lg(pari_colormap)-2;
+  check_rect(ne);
+  if (c < 1) pari_err_DOMAIN("rectcolor", "color", "<", gen_1, stoi(c));
+  if (c > n) pari_err_DOMAIN("rectcolor", "color", ">", stoi(n), stoi(c));
+  current_color[ne] = c;
+}
+
+void
+rectline0(long ne, double gx2, double gy2, long relative) /* code = ROt_MV/ROt_LN */
+{
+  double dx,dy,dxy,xmin,xmax,ymin,ymax,x1,y1,x2,y2;
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObj2P));
+  const double c = 1 + 1e-10;
+
+  x1 = RXcursor(e)*RXscale(e) + RXshift(e);
+  y1 = RYcursor(e)*RYscale(e) + RYshift(e);
+  if (relative)
+    { RXcursor(e)+=gx2; RYcursor(e)+=gy2; }
+  else
+    { RXcursor(e)=gx2; RYcursor(e)=gy2; }
+  x2 = RXcursor(e)*RXscale(e) + RXshift(e);
+  y2 = RYcursor(e)*RYscale(e) + RYshift(e);
+  xmin = maxdd(mindd(x1,x2),0); xmax = mindd(maxdd(x1,x2),RXsize(e));
+  ymin = maxdd(mindd(y1,y2),0); ymax = mindd(maxdd(y1,y2),RYsize(e));
+  dxy = x1*y2 - y1*x2; dx = x2-x1; dy = y2-y1;
+  if (dy)
+  {
+    if (dx*dy<0)
+      { xmin = maxdd(xmin,(dxy+RYsize(e)*dx)/dy); xmax=mindd(xmax,dxy/dy); }
+    else
+      { xmin=maxdd(xmin,dxy/dy); xmax=mindd(xmax,(dxy+RYsize(e)*dx)/dy); }
+  }
+  if (dx)
+  {
+    if (dx*dy<0)
+      { ymin=maxdd(ymin,(RXsize(e)*dy-dxy)/dx); ymax=mindd(ymax,-dxy/dx); }
+    else
+      { ymin=maxdd(ymin,-dxy/dx); ymax=mindd(ymax,(RXsize(e)*dy-dxy)/dx); }
+  }
+  RoLNx1(z) = xmin; RoLNx2(z) = xmax;
+  if (dx*dy<0) { RoLNy1(z) = ymax; RoLNy2(z) = ymin; }
+  else         { RoLNy1(z) = ymin; RoLNy2(z) = ymax; }
+  RoType(z) = (xmin>xmax*c || ymin>ymax*c) ? ROt_MV : ROt_LN;
+  Rchain(e, z);
+  RoCol(z) = current_color[ne];
+}
+
+/* Given coordinates of ends of a line, and labels l1 l2 attached to the
+   ends, plot ticks where the label coordinate takes "round" values */
+
+static void
+rectticks(PARI_plot *WW, long ne,
+          double dx1, double dy1, double dx2, double dy2,
+          double l1, double l2, long flags)
+{
+  long dx,dy,dxy,dxy1,x1,y1,x2,y2,nticks,n,n1,dn;
+  double minstep, maxstep, step, l_min, l_max, minl, maxl, dl, dtx, dty, x, y;
+  double ddx, ddy;
+  const double mult[3] = { 2./1., 5./2., 10./5. };
+  PariRect *e = check_rect_init(ne);
+  int do_double = !(flags & TICKS_NODOUBLE);
+
+  x1 = DTOL(dx1*RXscale(e) + RXshift(e));
+  y1 = DTOL(dy1*RYscale(e) + RYshift(e));
+  x2 = DTOL(dx2*RXscale(e) + RXshift(e));
+  y2 = DTOL(dy2*RYscale(e) + RYshift(e));
+  dx = x2 - x1;
+  dy = y2 - y1;
+  if (dx < 0) dx = -dx;
+  if (dy < 0) dy = -dy;
+  dxy1 = maxss(dx, dy);
+  dx /= WW->hunit;
+  dy /= WW->vunit;
+  if (dx > 1000 || dy > 1000)
+    dxy = 1000; /* avoid overflow */
+  else
+    dxy = (long)sqrt(dx*dx + dy*dy);
+  nticks = (long) ((dxy + 2.5)/4);
+  if (!nticks) return;
+
+  /* Now we want to find nticks (or less) "round" numbers between l1 and l2.
+     For our purpose round numbers have "last significant" digit either
+        *) any;
+        *) even;
+        *) divisible by 5.
+     We need to choose which alternative is better.
+   */
+  if (l1 < l2)
+    l_min = l1, l_max = l2;
+  else
+    l_min = l2, l_max = l1;
+  minstep = (l_max - l_min)/(nticks + 1);
+  maxstep = 2.5*(l_max - l_min);
+  step = exp(log(10) * floor(log10(minstep)));
+  if (!(flags & TICKS_ENDSTOO)) {
+    double d = 2*(l_max - l_min)/dxy1;        /* Two pixels off */
+
+    l_min += d;
+    l_max -= d;
+  }
+  for (n = 0; ; n++) {
+    if (step >= maxstep) return;
+
+    if (step >= minstep) {
+      minl = ceil(l_min/step);
+      maxl = floor(l_max/step);
+      if (minl <= maxl && maxl - minl + 1 <= nticks) {
+        nticks = (long) (maxl - minl + 1);
+        l_min = minl * step;
+        l_max = maxl * step; break;
+      }
+    }
+    step *= mult[ n % 3 ];
+  }
+  /* Where to position doubleticks, variants:
+     small: each 5, double: each 10        (n===2 mod 3)
+     small: each 2, double: each 10        (n===1 mod 3)
+     small: each 1, double: each  5 */
+  dn = (n % 3 == 2)? 2: 5;
+  n1 = ((long)minl) % dn; /* unused if do_double = FALSE */
+
+  /* now l_min and l_max keep min/max values of l with ticks, and nticks is
+     the number of ticks to draw. */
+  if (nticks == 1) ddx = ddy = 0; /* unused: for lint */
+  else {
+    dl = (l_max - l_min)/(nticks - 1);
+    ddx = (dx2 - dx1) * dl / (l2 - l1);
+    ddy = (dy2 - dy1) * dl / (l2 - l1);
+  }
+  x = dx1 + (dx2 - dx1) * (l_min - l1) / (l2 - l1);
+  y = dy1 + (dy2 - dy1) * (l_min - l1) / (l2 - l1);
+  /* assume hunit and vunit form a square.  For clockwise ticks: */
+  dtx = WW->hunit * dy/dxy * (y2 > y1 ? 1 : -1);        /* y-coord runs down */
+  dty = WW->vunit * dx/dxy * (x2 > x1 ? 1 : -1);
+  for (n = 0; n < nticks; n++) {
+    RectObj *z = (RectObj*) pari_malloc(sizeof(RectObj2P));
+    double lunit = WW->hunit > 1 ? 1.5 : 2;
+    double l = (do_double && (n + n1) % dn == 0) ? lunit: 1;
+
+    RoLNx1(z) = RoLNx2(z) = x*RXscale(e) + RXshift(e);
+    RoLNy1(z) = RoLNy2(z) = y*RYscale(e) + RYshift(e);
+
+    if (flags & TICKS_CLOCKW) {
+      RoLNx1(z) += dtx*l;
+      RoLNy1(z) -= dty*l; /* y-coord runs down */
+    }
+    if (flags & TICKS_ACLOCKW) {
+      RoLNx2(z) -= dtx*l;
+      RoLNy2(z) += dty*l; /* y-coord runs down */
+    }
+    RoType(z) = ROt_LN;
+
+    Rchain(e, z);
+    RoCol(z) = current_color[ne];
+    x += ddx;
+    y += ddy;
+  }
+}
+
+void
+rectline(long ne, GEN gx2, GEN gy2)
+{
+  rectline0(ne, gtodouble(gx2), gtodouble(gy2),0);
+}
+
+void
+rectrline(long ne, GEN gx2, GEN gy2)
+{
+  rectline0(ne, gtodouble(gx2), gtodouble(gy2),1);
+}
+
+void
+rectbox0(long ne, double gx2, double gy2, long relative)
+{
+  double x1,y1,x2,y2,xmin,ymin,xmax,ymax;
+  double xx,yy;
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObj2P));
+
+  x1 = RXcursor(e)*RXscale(e) + RXshift(e);
+  y1 = RYcursor(e)*RYscale(e) + RYshift(e);
+  if (relative)
+  { xx = RXcursor(e)+gx2; yy = RYcursor(e)+gy2; }
+  else
+  {  xx = gx2; yy = gy2; }
+  x2 = xx*RXscale(e) + RXshift(e);
+  y2 = yy*RYscale(e) + RYshift(e);
+  xmin = maxdd(mindd(x1,x2),0); xmax = mindd(maxdd(x1,x2),RXsize(e));
+  ymin = maxdd(mindd(y1,y2),0); ymax = mindd(maxdd(y1,y2),RYsize(e));
+
+  RoType(z) = ROt_BX;
+  RoBXx1(z) = xmin; RoBXy1(z) = ymin;
+  RoBXx2(z) = xmax; RoBXy2(z) = ymax;
+  Rchain(e, z);
+  RoCol(z) = current_color[ne];
+}
+
+void
+rectbox(long ne, GEN gx2, GEN gy2)
+{
+  rectbox0(ne, gtodouble(gx2), gtodouble(gy2), 0);
+}
+
+void
+rectrbox(long ne, GEN gx2, GEN gy2)
+{
+  rectbox0(ne, gtodouble(gx2), gtodouble(gy2), 1);
+}
+
+static void
+freeobj(RectObj *z) {
+  switch(RoType(z)) {
+    case ROt_MP: case ROt_ML:
+      pari_free(RoMPxs(z));
+      pari_free(RoMPys(z)); break;
+    case ROt_ST:
+      pari_free(RoSTs(z)); break;
+  }
+  pari_free(z);
+}
+
+
+void
+killrect(long ne)
+{
+  RectObj *z, *t;
+  PariRect *e = check_rect_init(ne);
+
+  current_color[ne]=DEFAULT_COLOR;
+  z=RHead(e);
+  RHead(e) = RTail(e) = NULL;
+  RXsize(e) = RYsize(e) = 0;
+  RXcursor(e) = RYcursor(e) = 0;
+  RXscale(e) = RYscale(e) = 1;
+  RXshift(e) = RYshift(e) = 0;
+  while (z) { t = RoNext(z); freeobj(z); z = t; }
+}
+
+void
+rectpoints0(long ne, double *listx, double *listy, long lx) /* code = ROt_MP */
+{
+  double *ptx, *pty, x, y;
+  long i, cp=0;
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObjMP));
+
+  RoMPxs(z) = ptx = (double*) pari_malloc(lx*sizeof(double));
+  RoMPys(z) = pty = (double*) pari_malloc(lx*sizeof(double));
+  for (i=0; i<lx; i++)
+  {
+    x = RXscale(e)*listx[i] + RXshift(e);
+    y = RYscale(e)*listy[i] + RYshift(e);
+    if (x>=0 && y>=0 && x<=RXsize(e) && y<=RYsize(e))
+    {
+      ptx[cp]=x; pty[cp]=y; cp++;
+    }
+  }
+  RoType(z) = ROt_MP;
+  RoMPcnt(z) = cp;
+  Rchain(e, z);
+  RoCol(z) = current_color[ne];
+}
+
+void
+rectpoints(long ne, GEN listx, GEN listy)
+{
+  long i,lx, tx=typ(listx), ty=typ(listy);
+  double *px,*py;
+
+  if (!is_matvec_t(tx) || !is_matvec_t(ty)) {
+    rectpoint(ne, listx, listy); return;
+  }
+  lx = lg(listx);
+  if (tx == t_MAT) pari_err_TYPE("rectpoints",listx);
+  if (ty == t_MAT) pari_err_TYPE("rectpoints",listy);
+  if (lg(listy) != lx) pari_err_DIM("rectpoints");
+  lx--; if (!lx) return;
+
+  px = (double*) pari_malloc(lx*sizeof(double)); listx++;
+  py = (double*) pari_malloc(lx*sizeof(double)); listy++;
+  for (i=0; i<lx; i++)
+  {
+    px[i] = gtodouble(gel(listx,i));
+    py[i] = gtodouble(gel(listy,i));
+  }
+  rectpoints0(ne,px,py,lx);
+  pari_free(px); pari_free(py);
+}
+
+void
+rectlines0(long ne, double *x, double *y, long lx, long flag) /* code = ROt_ML */
+{
+  long i,I;
+  double *ptx,*pty;
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObj2P));
+
+  I = flag ? lx+1 : lx;
+  ptx = (double*) pari_malloc(I*sizeof(double));
+  pty = (double*) pari_malloc(I*sizeof(double));
+  for (i=0; i<lx; i++)
+  {
+    ptx[i] = RXscale(e)*x[i] + RXshift(e);
+    pty[i] = RYscale(e)*y[i] + RYshift(e);
+  }
+  if (flag)
+  {
+    ptx[i] = RXscale(e)*x[0] + RXshift(e);
+    pty[i] = RYscale(e)*y[0] + RYshift(e);
+  }
+  Rchain(e, z);
+  RoType(z) = ROt_ML;
+  RoMLcnt(z) = lx;
+  RoMLxs(z) = ptx;
+  RoMLys(z) = pty;
+  RoCol(z) = current_color[ne];
+}
+
+void
+rectlines(long ne, GEN listx, GEN listy, long flag)
+{
+  long tx=typ(listx), ty=typ(listy), lx=lg(listx), i;
+  double *x, *y;
+
+  if (!is_matvec_t(tx) || !is_matvec_t(ty))
+  {
+    rectline(ne, listx, listy); return;
+  }
+  if (tx == t_MAT) pari_err_TYPE("rectlines",listx);
+  if (ty == t_MAT) pari_err_TYPE("rectlines",listy);
+  if (lg(listy) != lx) pari_err_DIM("rectlines");
+  lx--; if (!lx) return;
+
+  x = (double*) pari_malloc(lx*sizeof(double));
+  y = (double*) pari_malloc(lx*sizeof(double));
+  for (i=0; i<lx; i++)
+  {
+    x[i] = gtodouble(gel(listx,i+1));
+    y[i] = gtodouble(gel(listy,i+1));
+  }
+  rectlines0(ne,x,y,lx,flag);
+  pari_free(x); pari_free(y);
+}
+
+static void
+put_label(long ne, long x, long y, double d, long dir)
+{
+  char c[16];
+  sprintf(c,"%.5g", d);
+  rectmove0(ne,(double)x,(double)y,0);
+  rectstring3(ne, c, dir);
+}
+
+void
+rectstring(long ne, char *str)
+{
+  rectstring3(ne,str,RoSTdirLEFT);
+}
+
+/* Allocate memory, then put string */
+void
+rectstring3(long ne, char *str, long dir) /* code = ROt_ST */
+{
+  PariRect *e = check_rect_init(ne);
+  RectObj *z = (RectObj*) pari_malloc(sizeof(RectObjST));
+  long l = strlen(str);
+  char *s = (char *) pari_malloc(l+1);
+
+  memcpy(s,str,l+1);
+  RoType(z) = ROt_ST;
+  RoSTl(z) = l;
+  RoSTs(z) = s;
+  RoSTx(z) = RXscale(e)*RXcursor(e)+RXshift(e);
+  RoSTy(z) = RYscale(e)*RYcursor(e)+RYshift(e);
+  RoSTdir(z) = dir;
+  Rchain(e, z);
+  RoCol(z) = current_color[ne];
+}
+
+void
+rectpointtype(long ne, long type) /* code = ROt_PTT */
+{
+ if (ne == -1) {
+   rectpoint_itype = type;
+ } else {
+   PariRect *e = check_rect_init(ne);
+   RectObj *z = (RectObj*) pari_malloc(sizeof(RectObjPN));
+
+   RoType(z) = ROt_PTT;
+   RoPTTpen(z) = type;
+   Rchain(e, z);
+ }
+}
+
+/*FIXME: this function is a noop, since no graphic driver implement
+ * the ROt_PTS code. ne==-1 is a legacy, meaningless value. */
+void
+rectpointsize(long ne, GEN size) /* code = ROt_PTS */
+{
+ if (ne == -1) { /*do nothing*/ }
+ else {
+   PariRect *e = check_rect_init(ne);
+   RectObj *z = (RectObj*) pari_malloc(sizeof(RectObjPS));
+
+   RoType(z) = ROt_PTS;
+   RoPTSsize(z) = gtodouble(size);
+   Rchain(e, z);
+ }
+}
+
+void
+rectlinetype(long ne, long type)
+{
+ if (ne == -1) {
+   rectline_itype = type;
+ } else {
+   PariRect *e = check_rect_init(ne);
+   RectObj *z = (RectObj*) pari_malloc(sizeof(RectObjPN));
+
+   RoType(z) = ROt_LNT;
+   RoLNTpen(z) = type;
+   Rchain(e, z);
+ }
+}
+
+void
+rectcopy_gen(long source, long dest, GEN xoff, GEN yoff, long flag)
+{
+  long xi, yi;
+  if (flag & RECT_CP_RELATIVE) {
+    double xd = gtodouble(xoff), yd = gtodouble(yoff);
+
+    PARI_get_plot();
+    xi = pari_plot.width - 1;
+    yi = pari_plot.height - 1;
+    xi = DTOL(xd*xi);
+    yi = DTOL(yd*yi);
+  } else {
+    xi = itos(xoff);
+    yi = itos(yoff);
+  }
+  if (flag & ~RECT_CP_RELATIVE) {
+    PariRect *s = check_rect_init(source), *d = check_rect_init(dest);
+
+    switch (flag & ~RECT_CP_RELATIVE) {
+      case RECT_CP_NW:
+        break;
+      case RECT_CP_SW:
+        yi = RYsize(d) - RYsize(s) - yi;
+        break;
+      case RECT_CP_SE:
+        yi = RYsize(d) - RYsize(s) - yi;
+        /* FALL THROUGH */
+      case RECT_CP_NE:
+        xi = RXsize(d) - RXsize(s) - xi;
+        break;
+    }
+  }
+  rectcopy(source, dest, xi, yi);
+}
+
+void
+rectcopy(long source, long dest, long xoff, long yoff)
+{
+  PariRect *s = check_rect_init(source), *d = check_rect_init(dest);
+  RectObj *R = RHead(s);
+  RectObj *tail = RTail(d);
+  RectObj *next;
+  int i;
+
+  while (R)
+  {
+    switch(RoType(R))
+    {
+      case ROt_PT:
+        next = (RectObj*) pari_malloc(sizeof(RectObj1P));
+        memcpy(next,R,sizeof(RectObj1P));
+        RoPTx(next) += xoff; RoPTy(next) += yoff;
+        RoNext(tail) = next; tail = next;
+        break;
+      case ROt_LN: case ROt_BX:
+        next = (RectObj*) pari_malloc(sizeof(RectObj2P));
+        memcpy(next,R,sizeof(RectObj2P));
+        RoLNx1(next) += xoff; RoLNy1(next) += yoff;
+        RoLNx2(next) += xoff; RoLNy2(next) += yoff;
+        RoNext(tail) = next; tail = next;
+        break;
+      case ROt_MP: case ROt_ML:
+        next = (RectObj*) pari_malloc(sizeof(RectObjMP));
+        memcpy(next,R,sizeof(RectObjMP));
+        RoMPxs(next) = (double*) pari_malloc(sizeof(double)*RoMPcnt(next));
+        RoMPys(next) = (double*) pari_malloc(sizeof(double)*RoMPcnt(next));
+        memcpy(RoMPxs(next),RoMPxs(R),sizeof(double)*RoMPcnt(next));
+        memcpy(RoMPys(next),RoMPys(R),sizeof(double)*RoMPcnt(next));
+        for (i=0; i<RoMPcnt(next); i++)
+        {
+          RoMPxs(next)[i] += xoff; RoMPys(next)[i] += yoff;
+         }
+        RoNext(tail) = next; tail = next;
+        break;
+      case ROt_ST:
+        next = (RectObj*) pari_malloc(sizeof(RectObjST));
+        memcpy(next,R,sizeof(RectObjMP));
+        RoSTs(next) = (char*) pari_malloc(RoSTl(R)+1);
+        memcpy(RoSTs(next),RoSTs(R),RoSTl(R)+1);
+        RoSTx(next) += xoff; RoSTy(next) += yoff;
+        RoNext(tail) = next; tail = next;
+        break;
+      case ROt_PTT: case ROt_LNT: case ROt_PTS:
+        next = (RectObj*) pari_malloc(sizeof(RectObjPN));
+        memcpy(next,R,sizeof(RectObjPN));
+        RoNext(tail) = next; tail = next;
+        break;
+    }
+    R=RoNext(R);
+  }
+  RoNext(tail) = NULL; RTail(d) = tail;
+}
+
+enum {CLIPLINE_NONEMPTY = 1, CLIPLINE_CLIP_1 = 2, CLIPLINE_CLIP_2 = 4};
+/* A simpler way is to clip by 4 half-planes */
+static int
+clipline(double xmin, double xmax, double ymin, double ymax,
+         double *x1p, double *y1p, double *x2p, double *y2p)
+{
+  int xy_exch = 0, rc = CLIPLINE_NONEMPTY;
+  double t, sl;
+  double xi, xmn, xmx;
+  double yi, ymn, ymx;
+  int x1_is_ymn, x1_is_xmn;
+  double x1 = *x1p, x2 = *x2p, y1 = *y1p, y2 = *y2p;
+
+  if ((x1 < xmin &&  x2 < xmin) || (x1 > xmax && x2 > xmax))
+    return 0;
+  if (fabs(x1 - x2) < fabs(y1 - y2)) { /* Exchange x and y */
+    xy_exch = 1;
+    dswap(xmin, ymin); dswap(x1, y1);
+    dswap(xmax, ymax); dswap(x2, y2);
+  }
+
+  /* Build y as a function of x */
+  xi = x1;
+  yi = y1;
+  sl = x1==x2? 0: (y2 - yi)/(x2 - xi);
+
+  if (x1 > x2) {
+    x1_is_xmn = 0;
+    xmn = x2;
+    xmx = x1;
+  } else {
+    x1_is_xmn = 1;
+    xmn = x1;
+    xmx = x2;
+  }
+
+  if (xmn < xmin) {
+    xmn = xmin;
+    rc |= x1_is_xmn? CLIPLINE_CLIP_1: CLIPLINE_CLIP_2;
+  }
+  if (xmx > xmax) {
+    xmx = xmax;
+    rc |= x1_is_xmn? CLIPLINE_CLIP_2: CLIPLINE_CLIP_1;
+  }
+  if (xmn > xmx) return 0;
+
+  ymn = yi + (xmn - xi)*sl;
+  ymx = yi + (xmx - xi)*sl;
+
+  if (sl < 0) t = ymn, ymn = ymx, ymx = t;
+  if (ymn > ymax || ymx < ymin) return 0;
+
+  if (rc & CLIPLINE_CLIP_1) x1 = x1_is_xmn? xmn: xmx;
+  if (rc & CLIPLINE_CLIP_2) x2 = x1_is_xmn? xmx: xmn;
+
+  /* Now we know there is an intersection, need to move x1 and x2 */
+  x1_is_ymn = ((sl >= 0) == (x1 < x2));
+  if (ymn < ymin) {
+    double x = (ymin - yi)/sl + xi; /* slope != 0  ! */
+    if (x1_is_ymn) x1 = x, rc |= CLIPLINE_CLIP_1;
+    else           x2 = x, rc |= CLIPLINE_CLIP_2;
+  }
+  if (ymx > ymax) {
+    double x = (ymax - yi)/sl + xi; /* slope != 0  ! */
+    if (x1_is_ymn) x2 = x, rc |= CLIPLINE_CLIP_2;
+    else           x1 = x, rc |= CLIPLINE_CLIP_1;
+  }
+  if (rc & CLIPLINE_CLIP_1) y1 = yi + (x1 - xi)*sl;
+  if (rc & CLIPLINE_CLIP_2) y2 = yi + (x2 - xi)*sl;
+  if (xy_exch) /* Exchange x and y */
+    *x1p = y1, *x2p = y2, *y1p = x1, *y2p = x2;
+  else
+    *x1p = x1, *x2p = x2, *y1p = y1, *y2p = y2;
+  return rc;
+}
+
+void
+rectclip(long rect)
+{
+  PariRect *s = check_rect_init(rect);
+  RectObj *next, *R = RHead(s), **prevp = &RHead(s);
+  double xmin = 0, xmax = RXsize(s);
+  double ymin = 0, ymax = RYsize(s);
+
+  for (; R; R = next) {
+    int did_clip = 0;
+#define REMOVE() { *prevp = next; freeobj(R); break; }
+#define NEXT() { prevp = &RoNext(R); break; }
+
+    next = RoNext(R);
+    switch(RoType(R)) {
+      case ROt_PT:
+        if ( DTOL(RoPTx(R)) < xmin || DTOL(RoPTx(R)) > xmax
+          || DTOL(RoPTy(R)) < ymin || DTOL(RoPTy(R)) > ymax) REMOVE();
+        NEXT();
+      case ROt_BX:
+        if (RoLNx1(R) < xmin) RoLNx1(R) = xmin, did_clip = 1;
+        if (RoLNx2(R) < xmin) RoLNx2(R) = xmin, did_clip = 1;
+        if (RoLNy1(R) < ymin) RoLNy1(R) = ymin, did_clip = 1;
+        if (RoLNy2(R) < ymin) RoLNy2(R) = ymin, did_clip = 1;
+        if (RoLNx1(R) > xmax) RoLNx1(R) = xmax, did_clip = 1;
+        if (RoLNx2(R) > xmax) RoLNx2(R) = xmax, did_clip = 1;
+        if (RoLNy1(R) > ymax) RoLNy1(R) = ymax, did_clip = 1;
+        if (RoLNy2(R) > ymax) RoLNy2(R) = ymax, did_clip = 1;
+        /* Remove zero-size clipped boxes */
+        if (did_clip && RoLNx1(R) == RoLNx2(R)
+                     && RoLNy1(R) == RoLNy2(R)) REMOVE();
+        NEXT();
+      case ROt_LN:
+        if (!clipline(xmin, xmax, ymin, ymax,
+                      &RoLNx1(R), &RoLNy1(R),
+                      &RoLNx2(R), &RoLNy2(R))) REMOVE();
+        NEXT();
+      case ROt_MP: {
+        int c = RoMPcnt(R), f = 0, t = 0;
+
+        while (f < c) {
+          if ( DTOL(RoMPxs(R)[f]) >= xmin && DTOL(RoMPxs(R)[f]) <= xmax
+            && DTOL(RoMPys(R)[f]) >= ymin && DTOL(RoMPys(R)[f]) <= ymax) {
+            if (t != f) {
+              RoMPxs(R)[t] = RoMPxs(R)[f];
+              RoMPys(R)[t] = RoMPys(R)[f];
+            }
+            t++;
+          }
+          f++;
+        }
+        if (t == 0) REMOVE();
+        RoMPcnt(R) = t;
+        NEXT();
+      }
+      case ROt_ML: {
+        /* Hard case. Break a multiline into several pieces
+         * if some part is clipped. */
+        int c = RoMPcnt(R) - 1;
+        int f = 0, t = 0, had_lines = 0, had_hole = 0, rc;
+        double ox = RoMLxs(R)[0], oy = RoMLys(R)[0], oxn, oyn;
+
+        while (f < c) {
+        /* Endpoint of this segment is startpoint of next one: need to
+         * preserve it if it is clipped. */
+          oxn = RoMLxs(R)[f+1];
+          oyn = RoMLys(R)[f+1];
+          rc = clipline(xmin, xmax, ymin, ymax,
+                  &ox, &oy, /* &RoMLxs(R)[f], &RoMLys(R)[f], */
+                  &RoMLxs(R)[f+1], &RoMLys(R)[f+1]);
+          RoMLxs(R)[f] = ox; ox = oxn;
+          RoMLys(R)[f] = oy; oy = oyn;
+          if (!rc) {
+            if (had_lines) had_hole = 1;
+            f++; continue;
+          }
+
+          if (!had_lines || (!(rc & CLIPLINE_CLIP_1) && !had_hole) ) {
+            /* Continuous */
+            had_lines = 1;
+            if (t != f) {
+              if (t == 0) {
+                RoMPxs(R)[t] = RoMPxs(R)[f];
+                RoMPys(R)[t] = RoMPys(R)[f];
+              }
+              RoMPxs(R)[t+1] = RoMPxs(R)[f+1];
+              RoMPys(R)[t+1] = RoMPys(R)[f+1];
+            }
+            t++;
+            f++;
+            if (rc & CLIPLINE_CLIP_2) had_hole = 1, RoMLcnt(R) = t+1;
+            continue;
+          }
+          /* Is not continuous, automatically R is not pari_free()ed.  */
+          t++;
+          RoMLcnt(R) = t;
+          if (rc & CLIPLINE_CLIP_2) { /* Needs separate entry */
+            RectObj *n = (RectObj*) pari_malloc(sizeof(RectObj2P));
+
+            RoType(n) = ROt_LN;
+            RoCol(n) = RoCol(R);
+            RoLNx1(n) = RoMLxs(R)[f];        RoLNy1(n) = RoMLys(R)[f];
+            RoLNx2(n) = RoMLxs(R)[f+1];        RoLNy2(n) = RoMLys(R)[f+1];
+            RoNext(n) = next;
+            RoNext(R) = n;
+            /* Restore the unclipped value: */
+            RoMLxs(R)[f+1] = oxn;        RoMLys(R)[f+1] = oyn;
+            f++;
+            prevp = &RoNext(n);
+          }
+          if (f + 1 < c) {                /* Are other lines */
+            RectObj *n = (RectObj*) pari_malloc(sizeof(RectObjMP));
+            RoType(n) = ROt_ML;
+            RoCol(n) = RoCol(R);
+            RoMLcnt(n) = c - f;
+            RoMLxs(n) = (double*) pari_malloc(sizeof(double)*(c - f));
+            RoMLys(n) = (double*) pari_malloc(sizeof(double)*(c - f));
+            memcpy(RoMPxs(n),RoMPxs(R) + f, sizeof(double)*(c - f));
+            memcpy(RoMPys(n),RoMPys(R) + f, sizeof(double)*(c - f));
+            RoMPxs(n)[0] = oxn;
+            RoMPys(n)[0] = oyn;
+            RoNext(n) = next;
+            RoNext(R) = n;
+            next = n;
+          }
+          break;
+        }
+        if (t == 0) REMOVE();
+        NEXT();
+      }
+    }
+#undef REMOVE
+#undef NEXT
+  }
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        HI-RES PLOT                             **/
+/**                                                                **/
+/********************************************************************/
+void
+Printx(dblPointList *f)
+{
+  long i;
+  printf("x: [%0.5g,%0.5g], y: [%0.5g,%0.5g]\n",
+         f->xsml, f->xbig, f->ysml, f->ybig);
+  for (i = 0; i < f->nb; i++) printf("%0.5g ", f->d[i]);
+  printf("\n");
+}
+
+static void
+Appendx(dblPointList *f, dblPointList *l,double x)
+{
+  (l->d)[l->nb++]=x;
+  if (x < f->xsml) f->xsml = x;
+  if (x > f->xbig) f->xbig = x;
+}
+
+static void
+Appendy(dblPointList *f, dblPointList *l,double y)
+{
+  (l->d)[l->nb++]=y;
+  if (y < f->ysml) f->ysml = y;
+  if (y > f->ybig) f->ybig = y;
+}
+
+static void
+get_xy(long cplx, GEN t, double *x, double *y)
+{
+  if (cplx)
+  {
+    if (typ(t) == t_VEC)
+    {
+      if (lg(t) != 2) pari_err_DIM("get_xy");
+      t = gel(t,1);
+    }
+    *x = gtodouble( real_i(t) );
+    *y = gtodouble( imag_i(t) );
+  }
+  else
+  {
+    if (typ(t) != t_VEC || lg(t) != 3) pari_err_DIM("get_xy");
+    *x = gtodouble( gel(t,1) );
+    *y = gtodouble( gel(t,2) );
+  }
+}
+/* t a t_VEC (possibly a scalar if cplx), get next (x,y) coordinate starting
+ * at index *i [update i] */
+static void
+get_xy_from_vec(long cplx, GEN t, long *i, double *x, double *y)
+{
+  if (cplx)
+  {
+    GEN z;
+    if (typ(t) == t_VEC) z = gel(t,(*i)++); else { z = t; (*i)++; }
+    *x = gtodouble( real_i(z) );
+    *y = gtodouble( imag_i(z) );
+  }
+  else
+  {
+    *x = gtodouble( gel(t, (*i)++) );
+    *y = gtodouble( gel(t, (*i)++) );
+  }
+}
+/* X,Y t_VEC, get next (x,y) coordinate starting at index i
+ * Y ignored if (cplx) */
+static void
+get_xy_from_vec2(long cplx, GEN X, GEN Y, long i, double *x, double *y)
+{
+  if (cplx)
+  {
+    GEN z = gel(X,i);
+    *x = gtodouble( real_i(z) );
+    *y = gtodouble( imag_i(z) );
+  }
+  else
+  {
+    *x = gtodouble( gel(X, i) );
+    *y = gtodouble( gel(Y, i) );
+  }
+}
+
+/* Convert data from GEN to double before we call rectplothrawin. */
+static dblPointList*
+gtodblList(GEN data, long flags)
+{
+  dblPointList *l;
+  double xsml, xbig, ysml, ybig;
+  long nl=lg(data)-1, lx1, i, j;
+  const long param = (flags & (PLOT_PARAMETRIC|PLOT_COMPLEX));
+  const long cplx = (flags & PLOT_COMPLEX);
+
+  if (! is_vec_t(typ(data))) pari_err_TYPE("gtodblList",data);
+  if (!nl) return NULL;
+  lx1 = lg(gel(data,1));
+  if (!param && lx1 == 1) return NULL;
+
+  if (nl == 1 && !cplx) pari_err_DIM("gtodblList");
+  /* Allocate memory, then convert coord. to double */
+  l = (dblPointList*)pari_malloc((cplx? 2*nl: nl)*sizeof(dblPointList));
+  for (i=0; i<nl; i += (cplx? 1: 2))
+  {
+    dblPointList *LX = l + i, *LY = l + (i+1);
+    GEN x = gel(data,i+1), y;
+    long lx = lg(x);
+    if (!is_vec_t(typ(x))) pari_err_TYPE("gtodblList",x);
+    if (cplx) y = NULL;
+    else
+    {
+      y = gel(data,i+2);
+      if (!is_vec_t(typ(y))) pari_err_TYPE("gtodblList",y);
+      if (lg(y) != lx || (!param && lx != lx1)) pari_err_DIM("gtodblList");
+    }
+
+    lx--;
+    LX->d = (double*)pari_malloc(lx*sizeof(double));
+    LY->d = (double*)pari_malloc(lx*sizeof(double));
+    for (j=1; j<=lx; j++)
+    {
+      double xx, yy;
+      get_xy_from_vec2(cplx, x,y, j, &xx,&yy);
+      LX->d[j-1] = xx;
+      LY->d[j-1] = yy;
+    }
+    LX->nb = LY->nb = lx;
+  }
+
+  /* Now compute extremas */
+  if (param)
+  {
+    l[0].nb = cplx? nl: nl/2;
+    for (i=0; i < l[0].nb; i+=2)
+      if (l[i+1].nb) break;
+    if (i >= l[0].nb) { pari_free(l); return NULL; }
+    xsml = xbig = l[i  ].d[0];
+    ysml = ybig = l[i+1].d[0];
+    for (; i < l[0].nb; i+=2)
+    {
+      dblPointList *LX = l + i, *LY = l + (i+1);
+      for (j=0; j < LY->nb; j++)
+      {
+        double x = LX->d[j], y = LY->d[j];
+        if (x < xsml) xsml = x; else if (x > xbig) xbig = x;
+        if (y < ysml) ysml = y; else if (y > ybig) ybig = y;
+      }
+    }
+  }
+  else
+  {
+    l[0].nb = nl-1;
+    xsml = xbig = l[0].d[0];
+    ysml = ybig = l[1].d[0];
+    for (j=0; j < l[1].nb; j++)
+    {
+      double x = l[0].d[j];
+      if (x < xsml) xsml = x; else if (x > xbig) xbig = x;
+    }
+    for (i=1; i <= l[0].nb; i++)
+      for (j=0; j < l[i].nb; j++)
+      {
+        double y = l[i].d[j];
+        if (y < ysml) ysml = y; else if (y > ybig) ybig = y;
+      }
+  }
+  l[0].xsml = xsml; l[0].xbig = xbig;
+  l[0].ysml = ysml; l[0].ybig = ybig; return l;
+}
+
+/* (x+y)/2 */
+static GEN
+rmiddle(GEN x, GEN y) { GEN z = addrr(x,y); shiftr_inplace(z,-1); return z; }
+
+static void
+single_recursion(dblPointList *pl,GEN code,GEN xleft,double yleft,
+  GEN xright,double yright,long depth)
+{
+  GEN xx;
+  pari_sp av = avma;
+  double yy, dy=pl[0].ybig - pl[0].ysml;
+
+  if (depth==RECUR_MAXDEPTH) return;
+
+  xx = rmiddle(xleft,xright);
+  yy = gtodouble(READ_EXPR(code,xx));
+
+  if (dy && fabs(yleft+yright-2*yy)< dy*RECUR_PREC) return;
+  single_recursion(pl,code, xleft,yleft, xx,yy, depth+1);
+
+  Appendx(&pl[0],&pl[0],rtodbl(xx));
+  Appendy(&pl[0],&pl[1],yy);
+
+  single_recursion(pl,code, xx,yy, xright,yright, depth+1);
+  avma = av;
+}
+
+static void
+param_recursion(long cplx, dblPointList *pl,GEN code,GEN tleft,double xleft,
+  double yleft, GEN tright,double xright,double yright, long depth)
+{
+  GEN tt, p1;
+  pari_sp av=avma;
+  double xx, dy=pl[0].ybig - pl[0].ysml;
+  double yy, dx=pl[0].xbig - pl[0].xsml;
+
+  if (depth==RECUR_MAXDEPTH) return;
+
+  tt = rmiddle(tleft,tright);
+  p1 = READ_EXPR(code,tt);
+  get_xy(cplx, p1, &xx,&yy);
+
+  if (dx && dy && fabs(xleft+xright-2*xx) < dx*RECUR_PREC
+               && fabs(yleft+yright-2*yy) < dy*RECUR_PREC) return;
+  param_recursion(cplx, pl,code, tleft,xleft,yleft, tt,xx,yy, depth+1);
+
+  Appendx(&pl[0],&pl[0],xx);
+  Appendy(&pl[0],&pl[1],yy);
+
+  param_recursion(cplx, pl,code, tt,xx,yy, tright,xright,yright, depth+1);
+  avma = av;
+}
+
+/* Graph 'code' for parameter values in [a,b], using 'testpoints' sample
+ * points (0 = use a default value); code is either a t_CLOSURE (from GP:
+ * ploth, etc.) or a t_POL/t_VEC of two t_POLs from rectsplines. Returns a
+ * dblPointList of (absolute) coordinates. */
+static dblPointList *
+rectplothin(GEN a, GEN b, GEN code, long prec, ulong flags, long testpoints)
+{
+  const double INF = 1./0.;
+  const long param = flags & (PLOT_PARAMETRIC|PLOT_COMPLEX);
+  const long recur = flags & PLOT_RECURSIVE;
+  const long cplx = flags & PLOT_COMPLEX;
+  GEN t,dx,x;
+  dblPointList *pl;
+  long tx, i, j, sig, nc, nl, ncoords, nbpoints, non_vec = 0;
+  pari_sp av = avma;
+
+  sig = gcmp(b,a); if (!sig) return NULL;
+  if (sig < 0) swap(a, b);
+  if (testpoints)
+  {
+    if (testpoints < 2)
+      pari_err_DOMAIN("ploth", "#points", "<", gen_2, stoi(testpoints));
+  }
+  else
+  {
+    if (recur) testpoints = 8;
+    else       testpoints = param? 1500: 1000;
+  }
+  /* compute F(a) to determine nc = #curves; nl = #coord. lists */
+  x = gtofp(a, prec);
+  if (typ(code) == t_CLOSURE) push_lex(x, code);
+  t = READ_EXPR(code,x); tx = typ(t);
+  if (param)
+  {
+    if (cplx) nc = nl = (tx == t_VEC)? lg(t)-1: 1;
+    else
+    {
+      if (tx != t_VEC)
+        pari_err_TYPE("ploth [not a t_VEC with PLOT_PARAMETRIC]", t);
+      nl = lg(t)-1;
+      nc = nl/2;
+      if (odd(nl))
+        pari_err_TYPE("ploth [parametric ploc with odd # of components]",t);
+    }
+  }
+  else
+  {
+    if (!is_matvec_t(tx)) { nl = 2; non_vec = 1; }
+    else
+    {
+      if (tx != t_VEC) pari_err_TYPE("ploth [not a t_VEC]",t);
+      nl = lg(t);
+    }
+    nc = nl-1;
+  }
+  if (!nc) { avma = av; return NULL; }
+  if (recur && nc > 1)
+    pari_err_TYPE("ploth [multi-curves cannot be plot recursively]",t);
+
+  ncoords = cplx? 2*nl: nl;
+  nbpoints = recur? testpoints << RECUR_MAXDEPTH: testpoints;
+  pl=(dblPointList*) pari_malloc(ncoords*sizeof(dblPointList));
+  /* set [xy]sml,[xy]big to default values */
+  if (param)
+  {
+    pl[0].xsml = INF;
+    pl[0].xbig =-INF;
+  } else {
+    pl[0].xsml = gtodouble(a);
+    pl[0].xbig = gtodouble(b);
+  }
+  pl[0].ysml = INF;
+  pl[0].ybig =-INF;
+  for (i = 0; i < ncoords; i++)
+  {
+    pl[i].d = (double*)pari_malloc((nbpoints+1)*sizeof(double));
+    pl[i].nb=0;
+  }
+  dx = divru(gtofp(gsub(b,a),prec), testpoints-1);
+  if (recur) /* recursive plot */
+  {
+    double yleft, yright = 0;
+    if (param)
+    {
+      GEN tleft = cgetr(prec), tright = cgetr(prec);
+      double xleft, xright = 0;
+      pari_sp av2 = avma;
+      affgr(a,tleft);
+      t = READ_EXPR(code,tleft);
+      get_xy(cplx,t, &xleft,&yleft);
+      for (i=0; i<testpoints-1; i++, avma = av2)
+      {
+        if (i) { affrr(tright,tleft); xleft = xright; yleft = yright; }
+        addrrz(tleft,dx,tright);
+        t = READ_EXPR(code,tright);
+        get_xy(cplx,t, &xright,&yright);
+        Appendx(&pl[0],&pl[0],xleft);
+        Appendy(&pl[0],&pl[1],yleft);
+        param_recursion(cplx, pl,code, tleft,xleft,yleft, tright,xright,yright, 0);
+      }
+      Appendx(&pl[0],&pl[0],xright);
+      Appendy(&pl[0],&pl[1],yright);
+    }
+    else /* single curve */
+    {
+      GEN xleft = cgetr(prec), xright = cgetr(prec);
+      pari_sp av2 = avma;
+      affgr(a,xleft);
+      yleft = gtodouble(READ_EXPR(code,xleft));
+      for (i=0; i<testpoints-1; i++, avma = av2)
+      {
+        addrrz(xleft,dx,xright);
+        yright = gtodouble(READ_EXPR(code,xright));
+
+        Appendx(&pl[0],&pl[0],rtodbl(xleft));
+        Appendy(&pl[0],&pl[1],yleft);
+
+        single_recursion(pl,code,xleft,yleft,xright,yright,0);
+        affrr(xright,xleft); yleft = yright;
+      }
+      Appendx(&pl[0],&pl[0],rtodbl(xright));
+      Appendy(&pl[0],&pl[1],yright);
+    }
+  }
+  else /* non-recursive plot */
+  {
+    pari_sp av2 = avma;
+    if (param)
+    {
+      for (i=0; i<testpoints; i++, affrr(addrr(x,dx), x), avma = av2)
+      {
+        long k, nt;
+        t = READ_EXPR(code,x);
+        if (typ(t) != t_VEC)
+        {
+          if (cplx) nt = 1;
+          else nt = 0; /* trigger error */
+        }
+        else
+          nt = lg(t)-1;
+        if (nt != nl) pari_err_DIM("rectploth");
+        k = 0; j = 1;
+        while (j <= nl)
+        {
+          double xx, yy;
+          get_xy_from_vec(cplx, t, &j, &xx, &yy);
+          Appendx(&pl[0], &pl[k++], xx);
+          Appendy(&pl[0], &pl[k++], yy);
+        }
+      }
+    }
+    else if (non_vec)
+      for (i=0; i<testpoints; i++, affrr(addrr(x,dx), x), avma = av2)
+      {
+        t = READ_EXPR(code,x);
+        pl[0].d[i] = gtodouble(x);
+        Appendy(&pl[0],&pl[1], gtodouble(t));
+      }
+    else /* vector of non-parametric curves */
+      for (i=0; i<testpoints; i++, affrr(addrr(x,dx), x), avma = av2)
+      {
+        t = READ_EXPR(code,x);
+        if (typ(t) != t_VEC || lg(t) != nl) pari_err_DIM("rectploth");
+        pl[0].d[i] = gtodouble(x);
+        for (j=1; j<nl; j++) Appendy(&pl[0],&pl[j], gtodouble(gel(t,j)));
+      }
+  }
+  if (typ(code) == t_CLOSURE) pop_lex(1);
+  pl[0].nb = nc; avma = av; return pl;
+}
+
+/* Uses highlevel plotting functions to implement splines as
+   a low-level plotting function. */
+static void
+rectsplines(long ne, double *x, double *y, long lx, long flag)
+{
+  long i, j;
+  pari_sp av0 = avma;
+  GEN X = pol_x(0), xa = cgetg(lx+1, t_VEC), ya = cgetg(lx+1, t_VEC);
+  GEN tas, pol3;
+  long param = flag & PLOT_PARAMETRIC;
+
+  if (lx < 4) pari_err(e_MISC, "Too few points (%ld) for spline plot", lx);
+  for (i = 1; i <= lx; i++) {
+    gel(xa,i) = dbltor(x[i-1]);
+    gel(ya,i) = dbltor(y[i-1]);
+  }
+  if (param) {
+    tas = new_chunk(4);
+    for (j = 1; j <= 4; j++) gel(tas,j-1) = utoipos(j);
+    pol3 = cgetg(3, t_VEC);
+  }
+  else
+    tas = pol3 = NULL; /* gcc -Wall */
+  for (i = 0; i <= lx - 4; i++) {
+    pari_sp av = avma;
+
+    xa++; ya++;
+    if (param) {
+      gel(pol3,1) = polint_i(tas, xa, X, 4, NULL);
+      gel(pol3,2) = polint_i(tas, ya, X, 4, NULL);
+    } else {
+      pol3 = polint_i(xa, ya, X, 4, NULL);
+      tas = xa;
+    }
+    /* Start with 3 points */
+    rectploth(ne, i==   0 ? gel(tas,0) : gel(tas,1),
+                  i==lx-4 ? gel(tas,3) : gel(tas,2), pol3,
+                  DEFAULTPREC, PLOT_RECURSIVE | PLOT_NO_RESCALE
+                  | PLOT_NO_FRAME | PLOT_NO_AXE_Y | PLOT_NO_AXE_X | param, 2);
+    avma = av;
+  }
+  avma = av0;
+}
+
+static void
+set_range(double m, double M, double *sml, double *big)
+{
+  if (M - m < 1.e-9)
+  {
+    double d = fabs(m)/10; if (!d) d = 0.1;
+    M += d; m -= d;
+  }
+  *sml = m; *big = M;
+}
+/* Plot a dblPointList. Complete with axes, bounding box, etc.
+ *
+ * data is an array of structs. Its meaning depends on flags :
+ *
+ * + data[0] contains global extremas, the number of curves to plot
+ *   (data[0].nb) and a list of doubles (first set of x-coordinates).
+ *
+ * + data[i].nb (i>0) contains the number of points in the list
+ *   data[i].d (hopefully, data[2i].nb=data[2i+1].nb when i>0...)
+ *
+ * + If flags contain PLOT_PARAMETRIC, the array length should be
+ *   even, and successive pairs (data[2i].d, data[2i+1].d) represent
+ *   curves to plot.
+ *
+ * + If there is no such flag, the first element is an array with
+ *   x-coordinates and the following ones contain y-coordinates.
+ * If grect >= 0, output to this rectwindow. Otherwise draw immediately to
+ * screen (grect=-1) or to screen (grect=-2), using two drawing rectangles:
+ * one for labels, another for graphs.*/
+static GEN
+rectplothrawin(long grect, dblPointList *data, long flags)
+{
+  const long param = flags & (PLOT_PARAMETRIC|PLOT_COMPLEX);
+  const pari_sp av = avma;
+  PARI_plot *W;
+  dblPointList y,x;
+  double xsml, xbig, ysml, ybig;
+  long ltype, max_graphcolors;
+  long i,nc,nbpoints, w[2], wx[2], wy[2];
+
+  if (!data) return cgetg(1,t_VEC);
+  x = data[0]; nc = x.nb;
+  set_range(x.xsml, x.xbig, &xsml, &xbig);
+  set_range(x.ysml, x.ybig, &ysml, &ybig);
+  if (grect >= 0) /* output to rectwindow, no labels */
+    W = NULL;
+  else
+  {
+    const long srect = NUMRECT-2;
+    long lm, rm, tm, bm;
+
+    if (grect == -1) /* output to screen */
+    { W = &pari_plot; PARI_get_plot(); }
+    else /* output to file */
+    { W = &pari_psplot; PARI_get_psplot(); }
+    grect = NUMRECT-1;
+    /* left/right/top/bottom margin */
+    lm = W->fwidth*10;
+    rm = W->hunit-1;
+    tm = W->vunit-1;
+    bm = W->vunit+W->fheight-1;
+    w[0] = srect; wx[0] = 0;  wy[0] = 0;
+    w[1] = grect;   wx[1] = lm; wy[1] = tm;
+   /* Window (width x height) is given in pixels, correct pixels are 0..n-1,
+    * whereas rect functions work with windows whose pixel range is [0,n] */
+    initrect(srect, W->width - 1, W->height - 1);
+    rectlinetype(srect,-2); /* Frame */
+    current_color[srect] = DEFAULT_COLOR;
+    initrect(grect, W->width - (lm+rm) - 1, W->height - (tm+bm) - 1);
+    /* draw labels on srect */
+    put_label(srect, lm, 0, ybig, RoSTdirRIGHT|RoSTdirHGAP|RoSTdirTOP);
+    put_label(srect, lm, W->height-bm, ysml, RoSTdirRIGHT|RoSTdirHGAP|RoSTdirVGAP);
+    put_label(srect, lm, W->height - bm, xsml, RoSTdirLEFT|RoSTdirTOP);
+    put_label(srect, W->width-rm-1, W->height-bm, xbig, RoSTdirRIGHT|RoSTdirTOP);
+  }
+  RHasGraph(check_rect(grect)) = 1;
+
+  if (!(flags & PLOT_NO_RESCALE))
+    rectscale0(grect, xsml, xbig, ysml, ybig);
+
+  if (!(flags & PLOT_NO_FRAME))
+  {
+    int do_double = (flags & PLOT_NODOUBLETICK) ? TICKS_NODOUBLE : 0;
+    PARI_plot *pl = W;
+    if (!pl) { PARI_get_plot(); pl = &pari_plot; }
+
+    rectlinetype(grect, -2); /* Frame. */
+    current_color[grect] = DEFAULT_COLOR;
+    rectmove0(grect,xsml,ysml,0);
+    rectbox0(grect,xbig,ybig,0);
+    if (!(flags & PLOT_NO_TICK_X)) {
+      rectticks(pl, grect, xsml, ysml, xbig, ysml, xsml, xbig,
+        TICKS_CLOCKW | do_double);
+      rectticks(pl, grect, xbig, ybig, xsml, ybig, xbig, xsml,
+        TICKS_CLOCKW | do_double);
+    }
+    if (!(flags & PLOT_NO_TICK_Y)) {
+      rectticks(pl, grect, xbig, ysml, xbig, ybig, ysml, ybig,
+        TICKS_CLOCKW | do_double);
+      rectticks(pl, grect, xsml, ybig, xsml, ysml, ybig, ysml,
+        TICKS_CLOCKW | do_double);
+    }
+  }
+
+  if (!(flags & PLOT_NO_AXE_Y) && (xsml<=0 && xbig >=0))
+  {
+    rectlinetype(grect, -1); /* Axes. */
+    current_color[grect] = AXIS_COLOR;
+    rectmove0(grect,0.0,ysml,0);
+    rectline0(grect,0.0,ybig,0);
+  }
+
+  if (!(flags & PLOT_NO_AXE_X) && (ysml<=0 && ybig >=0))
+  {
+    rectlinetype(grect, -1); /* Axes. */
+    current_color[grect] = AXIS_COLOR;
+    rectmove0(grect,xsml,0.0,0);
+    rectline0(grect,xbig,0.0,0);
+  }
+
+  if (param) {
+    i = 0;
+    flags |= PLOT_PARAMETRIC;
+    flags &= (~PLOT_COMPLEX); /* turn COMPLEX to PARAMETRIC*/
+  } else i = 1;
+  max_graphcolors = lg(pari_graphcolors)-1;
+  for (ltype = 0; ltype < nc; ltype++)
+  {
+    current_color[grect] = pari_graphcolors[1+(ltype%max_graphcolors)];
+    if (param) x = data[i++];
+
+    y = data[i++]; nbpoints = y.nb;
+    if (flags & (PLOT_POINTS_LINES|PLOT_POINTS)) {
+      rectlinetype(grect, rectpoint_itype + ltype); /* Graphs */
+      rectpointtype(grect,rectpoint_itype + ltype); /* Graphs */
+      rectpoints0(grect,x.d,y.d,nbpoints);
+      if (!(flags & PLOT_POINTS_LINES)) continue;
+    }
+
+    if (flags & PLOT_SPLINES) {
+      /* rectsplines will call us back with ltype == 0 */
+      int old = rectline_itype;
+
+      rectline_itype = rectline_itype + ltype;
+      rectsplines(grect,x.d,y.d,nbpoints,flags);
+      rectline_itype = old;
+    } else {
+      rectlinetype(grect, rectline_itype + ltype); /* Graphs */
+      rectlines0(grect,x.d,y.d,nbpoints,0);
+    }
+  }
+  for (i--; i>=0; i--) pari_free(data[i].d);
+  pari_free(data);
+
+  if (W)
+  {
+    if (W == &pari_plot)
+      rectdraw0(w,wx,wy,2);
+    else
+      postdraw0(w,wx,wy,2, 0);
+    killrect(w[1]);
+    killrect(w[0]);
+  }
+  avma = av;
+  retmkvec4(dbltor(xsml), dbltor(xbig), dbltor(ysml), dbltor(ybig));
+}
+
+/*************************************************************************/
+/*                                                                       */
+/*                          HI-RES FUNCTIONS                             */
+/*                                                                       */
+/*************************************************************************/
+
+GEN
+rectploth(long ne, GEN a,GEN b,GEN code, long prec,ulong flags,long tpts)
+{
+  dblPointList *pl = rectplothin(a,b, code, prec, flags, tpts);
+  return rectplothrawin(ne, pl, flags);
+}
+
+GEN
+rectplothraw(long ne, GEN data, long flags)
+{
+  dblPointList *pl = gtodblList(data,flags);
+  return rectplothrawin(ne, pl, flags);
+}
+
+static long
+plothraw_flags(long fl)
+{
+  switch(fl)
+  {
+    case 0: return PLOT_PARAMETRIC|PLOT_POINTS;
+    case 1: return PLOT_PARAMETRIC;
+    default:return PLOT_PARAMETRIC|fl;
+  }
+}
+static GEN
+plothraw0(long ne, GEN listx, GEN listy, long flags)
+{
+  pari_sp av = avma;
+  GEN z = rectplothraw(ne, mkvec2(listx,listy), plothraw_flags(flags));
+  return gerepileupto(av, z);
+}
+
+GEN
+plothraw(GEN listx, GEN listy, long flags)
+{ return plothraw0(-1, listx, listy, flags); }
+
+GEN
+ploth(GEN a, GEN b, GEN code, long prec,long flags,long numpoints)
+{ return rectploth(-1, a,b,code,prec,flags,numpoints); }
+GEN
+ploth2(GEN a, GEN b, GEN code, long prec)
+{ return rectploth(-1, a,b,code,prec,PLOT_PARAMETRIC,0); }
+GEN
+plothmult(GEN a, GEN b, GEN code, long prec)
+{ return rectploth(-1, a,b,code,prec,0,0); }
+
+GEN
+postplothraw(GEN listx, GEN listy, long flags)
+{ return plothraw0(-2, listx, listy, flags); }
+GEN
+postploth(GEN a, GEN b, GEN code, long prec,long flags, long numpoints)
+{ return rectploth(-2, a,b,code,prec, flags,numpoints); }
+GEN
+postploth2(GEN a, GEN b, GEN code, long prec, long numpoints)
+{ return rectploth(-2, a,b,code,prec, PLOT_PARAMETRIC,numpoints); }
+
+GEN
+plothsizes(void) { return plothsizes_flag(0); }
+GEN
+plothsizes_flag(long flag)
+{
+  GEN vect = cgetg(1+6,t_VEC);
+
+  PARI_get_plot();
+  gel(vect,1) = stoi(pari_plot.width);
+  gel(vect,2) = stoi(pari_plot.height);
+  if (flag) {
+    gel(vect,3) = dbltor(pari_plot.hunit*1.0/pari_plot.width);
+    gel(vect,4) = dbltor(pari_plot.vunit*1.0/pari_plot.height);
+    gel(vect,5) = dbltor(pari_plot.fwidth*1.0/pari_plot.width);
+    gel(vect,6) = dbltor(pari_plot.fheight*1.0/pari_plot.height);
+  } else {
+    gel(vect,3) = stoi(pari_plot.hunit);
+    gel(vect,4) = stoi(pari_plot.vunit);
+    gel(vect,5) = stoi(pari_plot.fwidth);
+    gel(vect,6) = stoi(pari_plot.fheight);
+  }
+  return vect;
+}
+
+void
+plot_count(long *w, long lw, col_counter rcolcnt)
+{
+  RectObj *O;
+  long col, i;
+
+  for (col = 1; col < lg(pari_colormap)-1; col++)
+    for (i = 0; i < ROt_MAX; i++) rcolcnt[col][i] = 0;
+  for (i = 0; i < lw; i++)
+  {
+    PariRect *e = rectgraph[w[i]];
+    for (O = RHead(e); O; O=RoNext(O))
+      switch(RoType(O))
+      {
+        case ROt_MP : rcolcnt[RoCol(O)][ROt_PT] += RoMPcnt(O);
+                      break;                 /* Multiple Point */
+        case ROt_PT :                        /* Point */
+        case ROt_LN :                        /* Line */
+        case ROt_BX :                        /* Box */
+        case ROt_ML :                        /* Multiple lines */
+        case ROt_ST : rcolcnt[RoCol(O)][RoType(O)]++;
+                      break;                 /* String */
+      }
+  }
+}
+/*************************************************************************/
+/*                                                                       */
+/*                         POSTSCRIPT OUTPUT                             */
+/*                                                                       */
+/*************************************************************************/
+
+static void
+PARI_get_psplot(void)
+{
+  if (pari_psplot.init) return;
+  pari_psplot.init = 1;
+
+  pari_psplot.width = 1120 - 60; /* 1400 - 60 for hi-res */
+  pari_psplot.height=  800 - 40; /* 1120 - 60 for hi-res */
+  pari_psplot.fheight= 15;
+  pari_psplot.fwidth = 6;
+  pari_psplot.hunit = 5;
+  pari_psplot.vunit = 5;
+}
+
+static void
+gendraw(GEN list, long ps, long flag)
+{
+  long i,n,ne,*w,*x,*y;
+
+  if (typ(list) != t_VEC) pari_err_TYPE("rectdraw",list);
+  n = lg(list)-1; if (!n) return;
+  if (n%3) pari_err_DIM("rectdraw");
+  n = n/3;
+  w = (long*)pari_malloc(n*sizeof(long));
+  x = (long*)pari_malloc(n*sizeof(long));
+  y = (long*)pari_malloc(n*sizeof(long));
+  if (flag) PARI_get_plot();
+  for (i=0; i<n; i++)
+  {
+    GEN win = gel(list,3*i+1), x0 = gel(list,3*i+2), y0 = gel(list,3*i+3);
+    long xi, yi;
+    if (typ(win)!=t_INT) pari_err_TYPE("rectdraw",win);
+    if (flag) {
+      xi = DTOL(gtodouble(x0)*(pari_plot.width - 1));
+      yi = DTOL(gtodouble(y0)*(pari_plot.height - 1));
+    } else {
+      xi = gtos(x0);
+      yi = gtos(y0);
+    }
+    x[i] = xi;
+    y[i] = yi;
+    ne = itos(win); check_rect(ne);
+    w[i] = ne;
+  }
+  if (ps) postdraw0(w,x,y,n,flag); else rectdraw0(w,x,y,n);
+  pari_free(x); pari_free(y); pari_free(w);
+}
+
+void
+postdraw(GEN list) { gendraw(list, 1, 0); }
+
+void
+rectdraw(GEN list) { gendraw(list, 0, 0); }
+
+void
+postdraw_flag(GEN list, long flag) { gendraw(list, 1, flag); }
+
+void
+rectdraw_flag(GEN list, long flag) { gendraw(list, 0, flag); }
+
+static void
+ps_sc(void *data, long col)
+{
+  long l = lg(pari_colormap)-1;
+  int r, g, b;
+  if (col >= l)
+  {
+    pari_warn(warner,"non-existent color: %ld", col);
+    col = l-1;
+  }
+  color_to_rgb(gel(pari_colormap,col+1), &r, &g, &b);
+  fprintf((FILE*)data,"%f %f %f setrgbcolor\n", r/255., g/255., b/255.);
+}
+
+static void
+ps_point(void *data, long x, long y)
+{
+  fprintf((FILE*)data,"%ld %ld p\n",y,x);
+}
+
+static void
+ps_line(void *data, long x1, long y1, long x2, long y2)
+{
+  fprintf((FILE*)data,"%ld %ld m %ld %ld l\n",y1,x1,y2,x2);
+  fprintf((FILE*)data,"stroke\n");
+}
+
+static void
+ps_rect(void *data, long x, long y, long w, long h)
+{
+  fprintf((FILE*)data,"%ld %ld m %ld %ld l %ld %ld l %ld %ld l closepath\n",y,x, y,x+w, y+h,x+w, y+h,x);
+}
+
+static void
+ps_points(void *data, long nb, struct plot_points *p)
+{
+  long i;
+  for (i=0; i<nb; i++) ps_point(data, p[i].x, p[i].y);
+}
+
+static void
+ps_lines(void *data, long nb, struct plot_points *p)
+{
+  FILE *psfile = (FILE*)data;
+  long i;
+  fprintf(psfile,"%ld %ld m\n",p[0].y,p[0].x);
+  for (i=1; i<nb; i++) fprintf(psfile, "%ld %ld l\n", p[i].y, p[i].x);
+  fprintf(psfile,"stroke\n");
+}
+
+static void
+ps_string(void *data, long x, long y, char *s, long length)
+{
+  FILE *psfile = (FILE*)data;
+  (void)length;
+  if (strpbrk(s, "(\\)")) {
+    fprintf(psfile,"(");
+    while (*s) {
+      if ( *s=='(' || *s==')' || *s=='\\' ) fputc('\\', psfile);
+      fputc(*s, psfile);
+      s++;
+    }
+  } else
+    fprintf(psfile,"(%s", s);
+  fprintf(psfile,") %ld %ld m 90 rotate show -90 rotate\n", y, x);
+}
+
+void
+psplot_init(struct plot_eng *S, FILE *f, double xscale, double yscale, long fontsize)
+{
+  PARI_get_psplot();
+  /* Definitions taken from post terminal of Gnuplot. */
+  fprintf(f, "%%!\n\
+50 50 translate\n\
+/p {moveto 0 2 rlineto 2 0 rlineto 0 -2 rlineto closepath fill} def\n\
+/l {lineto} def\n\
+/m {moveto} def\n\
+/Times-Roman findfont %ld scalefont setfont\n\
+%g %g scale\n", fontsize, yscale, xscale);
+
+  S->sc = &ps_sc;
+  S->pt = &ps_point;
+  S->ln = &ps_line;
+  S->bx = &ps_rect;
+  S->mp = &ps_points;
+  S->ml = &ps_lines;
+  S->st = &ps_string;
+  S->pl = &pari_psplot;
+  S->data = (void*)f;
+}
+
+void
+postdraw0(long *w, long *x, long *y, long lw, long scale)
+{
+  struct plot_eng plot;
+  FILE *psfile;
+  double xscale = 0.65, yscale = 0.65;
+  long fontsize = 16;
+
+  psfile = fopen(current_psfile, "a");
+  if (!psfile) pari_err_FILE("postscript file",current_psfile);
+  if (scale) {
+    double psxscale, psyscale;
+    PARI_get_psplot();
+    PARI_get_plot();
+    psxscale = pari_psplot.width * 1.0/pari_plot.width ;
+    psyscale = pari_psplot.height* 1.0/pari_plot.height;
+    fontsize = (long) (fontsize/psxscale);
+    xscale *= psxscale;
+    yscale *= psyscale;
+  }
+  psplot_init(&plot, psfile, xscale, yscale, fontsize);
+  gen_rectdraw0(&plot, w, x, y, lw, 1, 1);
+  fprintf(psfile,"stroke showpage\n"); fclose(psfile);
+}
+
+#define RoColT(R) minss(numcolors,RoCol(R))
+
+void
+gen_rectdraw0(struct plot_eng *eng, long *w, long *x, long *y, long lw, double xs, double ys)
+{
+  void *data = eng->data;
+  long i, j;
+  long hgapsize = eng->pl->hunit, fheight = eng->pl->fheight;
+  long vgapsize = eng->pl->vunit,  fwidth = eng->pl->fwidth;
+  long numcolors = lg(pari_colormap)-1;
+  for(i=0; i<lw; i++)
+  {
+    PariRect *e = rectgraph[w[i]];
+    RectObj *R;
+    long x0 = x[i], y0 = y[i];
+    for (R = RHead(e); R; R = RoNext(R))
+    {
+      switch(RoType(R))
+      {
+      case ROt_PT:
+        eng->sc(data,RoColT(R));
+        eng->pt(data, DTOL((RoPTx(R)+x0)*xs), DTOL((RoPTy(R)+y0)*ys));
+        break;
+      case ROt_LN:
+        eng->sc(data,RoColT(R));
+        eng->ln(data, DTOL((RoLNx1(R)+x0)*xs),
+                      DTOL((RoLNy1(R)+y0)*ys),
+                      DTOL((RoLNx2(R)+x0)*xs),
+                      DTOL((RoLNy2(R)+y0)*ys));
+        break;
+      case ROt_BX:
+        eng->sc(data,RoColT(R));
+        eng->bx(data,
+                DTOL((RoBXx1(R)+x0)*xs),
+                DTOL((RoBXy1(R)+y0)*ys),
+                DTOL((RoBXx2(R)-RoBXx1(R))*xs),
+                DTOL((RoBXy2(R)-RoBXy1(R))*ys));
+        break;
+      case ROt_MP:
+        {
+          double *ptx = RoMPxs(R);
+          double *pty = RoMPys(R);
+          long     nb = RoMPcnt(R);
+          struct plot_points *points =
+            (struct plot_points *) pari_malloc(sizeof(*points)*nb);
+          for(j=0;j<nb;j++)
+          {
+            points[j].x = DTOL((ptx[j]+x0)*xs);
+            points[j].y = DTOL((pty[j]+y0)*ys);
+          }
+          eng->sc(data,RoColT(R));
+          eng->mp(data, nb, points);
+          pari_free(points);
+          break;
+        }
+      case ROt_ML:
+        {
+          double *ptx = RoMLxs(R);
+          double *pty = RoMLys(R);
+          long     nb = RoMLcnt(R);
+          struct plot_points *points =
+            (struct plot_points *) pari_malloc(sizeof(*points)*nb);
+          for(j=0;j<nb;j++)
+          {
+            points[j].x = DTOL((ptx[j]+x0)*xs);
+            points[j].y = DTOL((pty[j]+y0)*ys);
+          }
+          eng->sc(data,RoColT(R));
+          eng->ml(data, nb, points);
+          pari_free(points);
+          break;
+        }
+      case ROt_ST:
+        {
+          long dir = RoSTdir(R);
+          long hjust = dir & RoSTdirHPOS_mask, hgap  = dir & RoSTdirHGAP;
+          long vjust = dir & RoSTdirVPOS_mask, vgap  = dir & RoSTdirVGAP;
+          char *text = RoSTs(R);
+          long l     = RoSTl(R);
+          long x, y;
+          long shift = (hjust == RoSTdirLEFT ? 0 :
+              (hjust == RoSTdirRIGHT ? 2 : 1));
+          if (hgap)
+            hgap = (hjust == RoSTdirLEFT) ? hgapsize : -hgapsize;
+          if (vgap)
+            vgap = (vjust == RoSTdirBOTTOM) ? 2*vgapsize : -2*vgapsize;
+          if (vjust != RoSTdirBOTTOM)
+            vgap -= ((vjust == RoSTdirTOP) ? 2 : 1)*(fheight - 1);
+          x = DTOL((RoSTx(R) + x0 + hgap - (l * fwidth * shift)/2)*xs);
+          y = DTOL((RoSTy(R) + y0 - vgap/2)*ys);
+          eng->sc(data,RoColT(R));
+          eng->st(data, x, y, text, l);
+          break;
+        }
+      default:
+        break;
+      }
+    }
+  }
+}
+
+/*************************************************************************/
+/*                                                                       */
+/*                           RGB COLORS                                  */
+/*                                                                       */
+/*************************************************************************/
+#if 0
+/* generated from /etc/X11/rgb.txt by the following perl script */
+#!/usr/bin/perl
+while(<>)
+{
+  ($hex, $name) = split(/\t\t/, $_);
+  $hex =~ s/^ +//; chomp($name); $name =~ s/ *//g;
+  $hex = sprintf("0x%02x%02x%02x", split(/\s+/, $hex));
+  $name = lc($name); next if ($done{$name});
+  $done{$name} = 1;
+  print "COL(\"$name\", $hex),\n";
+}
+#endif
+
+#define COL(x,y) {(void*)x,(void*)y,0,NULL}
+static hashentry col_list[] = {
+COL("", 0x000000),
+COL("snow", 0xfffafa),
+COL("ghostwhite", 0xf8f8ff),
+COL("whitesmoke", 0xf5f5f5),
+COL("gainsboro", 0xdcdcdc),
+COL("floralwhite", 0xfffaf0),
+COL("oldlace", 0xfdf5e6),
+COL("linen", 0xfaf0e6),
+COL("antiquewhite", 0xfaebd7),
+COL("papayawhip", 0xffefd5),
+COL("blanchedalmond", 0xffebcd),
+COL("bisque", 0xffe4c4),
+COL("peachpuff", 0xffdab9),
+COL("navajowhite", 0xffdead),
+COL("moccasin", 0xffe4b5),
+COL("cornsilk", 0xfff8dc),
+COL("ivory", 0xfffff0),
+COL("lemonchiffon", 0xfffacd),
+COL("seashell", 0xfff5ee),
+COL("honeydew", 0xf0fff0),
+COL("mintcream", 0xf5fffa),
+COL("azure", 0xf0ffff),
+COL("aliceblue", 0xf0f8ff),
+COL("lavender", 0xe6e6fa),
+COL("lavenderblush", 0xfff0f5),
+COL("mistyrose", 0xffe4e1),
+COL("white", 0xffffff),
+COL("black", 0x000000),
+COL("darkslategray", 0x2f4f4f),
+COL("darkslategrey", 0x2f4f4f),
+COL("dimgray", 0x696969),
+COL("dimgrey", 0x696969),
+COL("slategray", 0x708090),
+COL("slategrey", 0x708090),
+COL("lightslategray", 0x778899),
+COL("lightslategrey", 0x778899),
+COL("gray", 0xbebebe),
+COL("grey", 0xbebebe),
+COL("lightgrey", 0xd3d3d3),
+COL("lightgray", 0xd3d3d3),
+COL("midnightblue", 0x191970),
+COL("navy", 0x000080),
+COL("navyblue", 0x000080),
+COL("cornflowerblue", 0x6495ed),
+COL("darkslateblue", 0x483d8b),
+COL("slateblue", 0x6a5acd),
+COL("mediumslateblue", 0x7b68ee),
+COL("lightslateblue", 0x8470ff),
+COL("mediumblue", 0x0000cd),
+COL("royalblue", 0x4169e1),
+COL("blue", 0x0000ff),
+COL("dodgerblue", 0x1e90ff),
+COL("deepskyblue", 0x00bfff),
+COL("skyblue", 0x87ceeb),
+COL("lightskyblue", 0x87cefa),
+COL("steelblue", 0x4682b4),
+COL("lightsteelblue", 0xb0c4de),
+COL("lightblue", 0xadd8e6),
+COL("powderblue", 0xb0e0e6),
+COL("paleturquoise", 0xafeeee),
+COL("darkturquoise", 0x00ced1),
+COL("mediumturquoise", 0x48d1cc),
+COL("turquoise", 0x40e0d0),
+COL("cyan", 0x00ffff),
+COL("lightcyan", 0xe0ffff),
+COL("cadetblue", 0x5f9ea0),
+COL("mediumaquamarine", 0x66cdaa),
+COL("aquamarine", 0x7fffd4),
+COL("darkgreen", 0x006400),
+COL("darkolivegreen", 0x556b2f),
+COL("darkseagreen", 0x8fbc8f),
+COL("seagreen", 0x2e8b57),
+COL("mediumseagreen", 0x3cb371),
+COL("lightseagreen", 0x20b2aa),
+COL("palegreen", 0x98fb98),
+COL("springgreen", 0x00ff7f),
+COL("lawngreen", 0x7cfc00),
+COL("green", 0x00ff00),
+COL("chartreuse", 0x7fff00),
+COL("mediumspringgreen", 0x00fa9a),
+COL("greenyellow", 0xadff2f),
+COL("limegreen", 0x32cd32),
+COL("yellowgreen", 0x9acd32),
+COL("forestgreen", 0x228b22),
+COL("olivedrab", 0x6b8e23),
+COL("darkkhaki", 0xbdb76b),
+COL("khaki", 0xf0e68c),
+COL("palegoldenrod", 0xeee8aa),
+COL("lightgoldenrodyellow", 0xfafad2),
+COL("lightyellow", 0xffffe0),
+COL("yellow", 0xffff00),
+COL("gold", 0xffd700),
+COL("lightgoldenrod", 0xeedd82),
+COL("goldenrod", 0xdaa520),
+COL("darkgoldenrod", 0xb8860b),
+COL("rosybrown", 0xbc8f8f),
+COL("indianred", 0xcd5c5c),
+COL("saddlebrown", 0x8b4513),
+COL("sienna", 0xa0522d),
+COL("peru", 0xcd853f),
+COL("burlywood", 0xdeb887),
+COL("beige", 0xf5f5dc),
+COL("wheat", 0xf5deb3),
+COL("sandybrown", 0xf4a460),
+COL("tan", 0xd2b48c),
+COL("chocolate", 0xd2691e),
+COL("firebrick", 0xb22222),
+COL("brown", 0xa52a2a),
+COL("darksalmon", 0xe9967a),
+COL("salmon", 0xfa8072),
+COL("lightsalmon", 0xffa07a),
+COL("orange", 0xffa500),
+COL("darkorange", 0xff8c00),
+COL("coral", 0xff7f50),
+COL("lightcoral", 0xf08080),
+COL("tomato", 0xff6347),
+COL("orangered", 0xff4500),
+COL("red", 0xff0000),
+COL("hotpink", 0xff69b4),
+COL("deeppink", 0xff1493),
+COL("pink", 0xffc0cb),
+COL("lightpink", 0xffb6c1),
+COL("palevioletred", 0xdb7093),
+COL("maroon", 0xb03060),
+COL("mediumvioletred", 0xc71585),
+COL("violetred", 0xd02090),
+COL("magenta", 0xff00ff),
+COL("violet", 0xee82ee),
+COL("plum", 0xdda0dd),
+COL("orchid", 0xda70d6),
+COL("mediumorchid", 0xba55d3),
+COL("darkorchid", 0x9932cc),
+COL("darkviolet", 0x9400d3),
+COL("blueviolet", 0x8a2be2),
+COL("purple", 0xa020f0),
+COL("mediumpurple", 0x9370db),
+COL("thistle", 0xd8bfd8),
+COL("snow1", 0xfffafa),
+COL("snow2", 0xeee9e9),
+COL("snow3", 0xcdc9c9),
+COL("snow4", 0x8b8989),
+COL("seashell1", 0xfff5ee),
+COL("seashell2", 0xeee5de),
+COL("seashell3", 0xcdc5bf),
+COL("seashell4", 0x8b8682),
+COL("antiquewhite1", 0xffefdb),
+COL("antiquewhite2", 0xeedfcc),
+COL("antiquewhite3", 0xcdc0b0),
+COL("antiquewhite4", 0x8b8378),
+COL("bisque1", 0xffe4c4),
+COL("bisque2", 0xeed5b7),
+COL("bisque3", 0xcdb79e),
+COL("bisque4", 0x8b7d6b),
+COL("peachpuff1", 0xffdab9),
+COL("peachpuff2", 0xeecbad),
+COL("peachpuff3", 0xcdaf95),
+COL("peachpuff4", 0x8b7765),
+COL("navajowhite1", 0xffdead),
+COL("navajowhite2", 0xeecfa1),
+COL("navajowhite3", 0xcdb38b),
+COL("navajowhite4", 0x8b795e),
+COL("lemonchiffon1", 0xfffacd),
+COL("lemonchiffon2", 0xeee9bf),
+COL("lemonchiffon3", 0xcdc9a5),
+COL("lemonchiffon4", 0x8b8970),
+COL("cornsilk1", 0xfff8dc),
+COL("cornsilk2", 0xeee8cd),
+COL("cornsilk3", 0xcdc8b1),
+COL("cornsilk4", 0x8b8878),
+COL("ivory1", 0xfffff0),
+COL("ivory2", 0xeeeee0),
+COL("ivory3", 0xcdcdc1),
+COL("ivory4", 0x8b8b83),
+COL("honeydew1", 0xf0fff0),
+COL("honeydew2", 0xe0eee0),
+COL("honeydew3", 0xc1cdc1),
+COL("honeydew4", 0x838b83),
+COL("lavenderblush1", 0xfff0f5),
+COL("lavenderblush2", 0xeee0e5),
+COL("lavenderblush3", 0xcdc1c5),
+COL("lavenderblush4", 0x8b8386),
+COL("mistyrose1", 0xffe4e1),
+COL("mistyrose2", 0xeed5d2),
+COL("mistyrose3", 0xcdb7b5),
+COL("mistyrose4", 0x8b7d7b),
+COL("azure1", 0xf0ffff),
+COL("azure2", 0xe0eeee),
+COL("azure3", 0xc1cdcd),
+COL("azure4", 0x838b8b),
+COL("slateblue1", 0x836fff),
+COL("slateblue2", 0x7a67ee),
+COL("slateblue3", 0x6959cd),
+COL("slateblue4", 0x473c8b),
+COL("royalblue1", 0x4876ff),
+COL("royalblue2", 0x436eee),
+COL("royalblue3", 0x3a5fcd),
+COL("royalblue4", 0x27408b),
+COL("blue1", 0x0000ff),
+COL("blue2", 0x0000ee),
+COL("blue3", 0x0000cd),
+COL("blue4", 0x00008b),
+COL("dodgerblue1", 0x1e90ff),
+COL("dodgerblue2", 0x1c86ee),
+COL("dodgerblue3", 0x1874cd),
+COL("dodgerblue4", 0x104e8b),
+COL("steelblue1", 0x63b8ff),
+COL("steelblue2", 0x5cacee),
+COL("steelblue3", 0x4f94cd),
+COL("steelblue4", 0x36648b),
+COL("deepskyblue1", 0x00bfff),
+COL("deepskyblue2", 0x00b2ee),
+COL("deepskyblue3", 0x009acd),
+COL("deepskyblue4", 0x00688b),
+COL("skyblue1", 0x87ceff),
+COL("skyblue2", 0x7ec0ee),
+COL("skyblue3", 0x6ca6cd),
+COL("skyblue4", 0x4a708b),
+COL("lightskyblue1", 0xb0e2ff),
+COL("lightskyblue2", 0xa4d3ee),
+COL("lightskyblue3", 0x8db6cd),
+COL("lightskyblue4", 0x607b8b),
+COL("slategray1", 0xc6e2ff),
+COL("slategray2", 0xb9d3ee),
+COL("slategray3", 0x9fb6cd),
+COL("slategray4", 0x6c7b8b),
+COL("lightsteelblue1", 0xcae1ff),
+COL("lightsteelblue2", 0xbcd2ee),
+COL("lightsteelblue3", 0xa2b5cd),
+COL("lightsteelblue4", 0x6e7b8b),
+COL("lightblue1", 0xbfefff),
+COL("lightblue2", 0xb2dfee),
+COL("lightblue3", 0x9ac0cd),
+COL("lightblue4", 0x68838b),
+COL("lightcyan1", 0xe0ffff),
+COL("lightcyan2", 0xd1eeee),
+COL("lightcyan3", 0xb4cdcd),
+COL("lightcyan4", 0x7a8b8b),
+COL("paleturquoise1", 0xbbffff),
+COL("paleturquoise2", 0xaeeeee),
+COL("paleturquoise3", 0x96cdcd),
+COL("paleturquoise4", 0x668b8b),
+COL("cadetblue1", 0x98f5ff),
+COL("cadetblue2", 0x8ee5ee),
+COL("cadetblue3", 0x7ac5cd),
+COL("cadetblue4", 0x53868b),
+COL("turquoise1", 0x00f5ff),
+COL("turquoise2", 0x00e5ee),
+COL("turquoise3", 0x00c5cd),
+COL("turquoise4", 0x00868b),
+COL("cyan1", 0x00ffff),
+COL("cyan2", 0x00eeee),
+COL("cyan3", 0x00cdcd),
+COL("cyan4", 0x008b8b),
+COL("darkslategray1", 0x97ffff),
+COL("darkslategray2", 0x8deeee),
+COL("darkslategray3", 0x79cdcd),
+COL("darkslategray4", 0x528b8b),
+COL("aquamarine1", 0x7fffd4),
+COL("aquamarine2", 0x76eec6),
+COL("aquamarine3", 0x66cdaa),
+COL("aquamarine4", 0x458b74),
+COL("darkseagreen1", 0xc1ffc1),
+COL("darkseagreen2", 0xb4eeb4),
+COL("darkseagreen3", 0x9bcd9b),
+COL("darkseagreen4", 0x698b69),
+COL("seagreen1", 0x54ff9f),
+COL("seagreen2", 0x4eee94),
+COL("seagreen3", 0x43cd80),
+COL("seagreen4", 0x2e8b57),
+COL("palegreen1", 0x9aff9a),
+COL("palegreen2", 0x90ee90),
+COL("palegreen3", 0x7ccd7c),
+COL("palegreen4", 0x548b54),
+COL("springgreen1", 0x00ff7f),
+COL("springgreen2", 0x00ee76),
+COL("springgreen3", 0x00cd66),
+COL("springgreen4", 0x008b45),
+COL("green1", 0x00ff00),
+COL("green2", 0x00ee00),
+COL("green3", 0x00cd00),
+COL("green4", 0x008b00),
+COL("chartreuse1", 0x7fff00),
+COL("chartreuse2", 0x76ee00),
+COL("chartreuse3", 0x66cd00),
+COL("chartreuse4", 0x458b00),
+COL("olivedrab1", 0xc0ff3e),
+COL("olivedrab2", 0xb3ee3a),
+COL("olivedrab3", 0x9acd32),
+COL("olivedrab4", 0x698b22),
+COL("darkolivegreen1", 0xcaff70),
+COL("darkolivegreen2", 0xbcee68),
+COL("darkolivegreen3", 0xa2cd5a),
+COL("darkolivegreen4", 0x6e8b3d),
+COL("khaki1", 0xfff68f),
+COL("khaki2", 0xeee685),
+COL("khaki3", 0xcdc673),
+COL("khaki4", 0x8b864e),
+COL("lightgoldenrod1", 0xffec8b),
+COL("lightgoldenrod2", 0xeedc82),
+COL("lightgoldenrod3", 0xcdbe70),
+COL("lightgoldenrod4", 0x8b814c),
+COL("lightyellow1", 0xffffe0),
+COL("lightyellow2", 0xeeeed1),
+COL("lightyellow3", 0xcdcdb4),
+COL("lightyellow4", 0x8b8b7a),
+COL("yellow1", 0xffff00),
+COL("yellow2", 0xeeee00),
+COL("yellow3", 0xcdcd00),
+COL("yellow4", 0x8b8b00),
+COL("gold1", 0xffd700),
+COL("gold2", 0xeec900),
+COL("gold3", 0xcdad00),
+COL("gold4", 0x8b7500),
+COL("goldenrod1", 0xffc125),
+COL("goldenrod2", 0xeeb422),
+COL("goldenrod3", 0xcd9b1d),
+COL("goldenrod4", 0x8b6914),
+COL("darkgoldenrod1", 0xffb90f),
+COL("darkgoldenrod2", 0xeead0e),
+COL("darkgoldenrod3", 0xcd950c),
+COL("darkgoldenrod4", 0x8b6508),
+COL("rosybrown1", 0xffc1c1),
+COL("rosybrown2", 0xeeb4b4),
+COL("rosybrown3", 0xcd9b9b),
+COL("rosybrown4", 0x8b6969),
+COL("indianred1", 0xff6a6a),
+COL("indianred2", 0xee6363),
+COL("indianred3", 0xcd5555),
+COL("indianred4", 0x8b3a3a),
+COL("sienna1", 0xff8247),
+COL("sienna2", 0xee7942),
+COL("sienna3", 0xcd6839),
+COL("sienna4", 0x8b4726),
+COL("burlywood1", 0xffd39b),
+COL("burlywood2", 0xeec591),
+COL("burlywood3", 0xcdaa7d),
+COL("burlywood4", 0x8b7355),
+COL("wheat1", 0xffe7ba),
+COL("wheat2", 0xeed8ae),
+COL("wheat3", 0xcdba96),
+COL("wheat4", 0x8b7e66),
+COL("tan1", 0xffa54f),
+COL("tan2", 0xee9a49),
+COL("tan3", 0xcd853f),
+COL("tan4", 0x8b5a2b),
+COL("chocolate1", 0xff7f24),
+COL("chocolate2", 0xee7621),
+COL("chocolate3", 0xcd661d),
+COL("chocolate4", 0x8b4513),
+COL("firebrick1", 0xff3030),
+COL("firebrick2", 0xee2c2c),
+COL("firebrick3", 0xcd2626),
+COL("firebrick4", 0x8b1a1a),
+COL("brown1", 0xff4040),
+COL("brown2", 0xee3b3b),
+COL("brown3", 0xcd3333),
+COL("brown4", 0x8b2323),
+COL("salmon1", 0xff8c69),
+COL("salmon2", 0xee8262),
+COL("salmon3", 0xcd7054),
+COL("salmon4", 0x8b4c39),
+COL("lightsalmon1", 0xffa07a),
+COL("lightsalmon2", 0xee9572),
+COL("lightsalmon3", 0xcd8162),
+COL("lightsalmon4", 0x8b5742),
+COL("orange1", 0xffa500),
+COL("orange2", 0xee9a00),
+COL("orange3", 0xcd8500),
+COL("orange4", 0x8b5a00),
+COL("darkorange1", 0xff7f00),
+COL("darkorange2", 0xee7600),
+COL("darkorange3", 0xcd6600),
+COL("darkorange4", 0x8b4500),
+COL("coral1", 0xff7256),
+COL("coral2", 0xee6a50),
+COL("coral3", 0xcd5b45),
+COL("coral4", 0x8b3e2f),
+COL("tomato1", 0xff6347),
+COL("tomato2", 0xee5c42),
+COL("tomato3", 0xcd4f39),
+COL("tomato4", 0x8b3626),
+COL("orangered1", 0xff4500),
+COL("orangered2", 0xee4000),
+COL("orangered3", 0xcd3700),
+COL("orangered4", 0x8b2500),
+COL("red1", 0xff0000),
+COL("red2", 0xee0000),
+COL("red3", 0xcd0000),
+COL("red4", 0x8b0000),
+COL("debianred", 0xd70751),
+COL("deeppink1", 0xff1493),
+COL("deeppink2", 0xee1289),
+COL("deeppink3", 0xcd1076),
+COL("deeppink4", 0x8b0a50),
+COL("hotpink1", 0xff6eb4),
+COL("hotpink2", 0xee6aa7),
+COL("hotpink3", 0xcd6090),
+COL("hotpink4", 0x8b3a62),
+COL("pink1", 0xffb5c5),
+COL("pink2", 0xeea9b8),
+COL("pink3", 0xcd919e),
+COL("pink4", 0x8b636c),
+COL("lightpink1", 0xffaeb9),
+COL("lightpink2", 0xeea2ad),
+COL("lightpink3", 0xcd8c95),
+COL("lightpink4", 0x8b5f65),
+COL("palevioletred1", 0xff82ab),
+COL("palevioletred2", 0xee799f),
+COL("palevioletred3", 0xcd6889),
+COL("palevioletred4", 0x8b475d),
+COL("maroon1", 0xff34b3),
+COL("maroon2", 0xee30a7),
+COL("maroon3", 0xcd2990),
+COL("maroon4", 0x8b1c62),
+COL("violetred1", 0xff3e96),
+COL("violetred2", 0xee3a8c),
+COL("violetred3", 0xcd3278),
+COL("violetred4", 0x8b2252),
+COL("magenta1", 0xff00ff),
+COL("magenta2", 0xee00ee),
+COL("magenta3", 0xcd00cd),
+COL("magenta4", 0x8b008b),
+COL("orchid1", 0xff83fa),
+COL("orchid2", 0xee7ae9),
+COL("orchid3", 0xcd69c9),
+COL("orchid4", 0x8b4789),
+COL("plum1", 0xffbbff),
+COL("plum2", 0xeeaeee),
+COL("plum3", 0xcd96cd),
+COL("plum4", 0x8b668b),
+COL("mediumorchid1", 0xe066ff),
+COL("mediumorchid2", 0xd15fee),
+COL("mediumorchid3", 0xb452cd),
+COL("mediumorchid4", 0x7a378b),
+COL("darkorchid1", 0xbf3eff),
+COL("darkorchid2", 0xb23aee),
+COL("darkorchid3", 0x9a32cd),
+COL("darkorchid4", 0x68228b),
+COL("purple1", 0x9b30ff),
+COL("purple2", 0x912cee),
+COL("purple3", 0x7d26cd),
+COL("purple4", 0x551a8b),
+COL("mediumpurple1", 0xab82ff),
+COL("mediumpurple2", 0x9f79ee),
+COL("mediumpurple3", 0x8968cd),
+COL("mediumpurple4", 0x5d478b),
+COL("thistle1", 0xffe1ff),
+COL("thistle2", 0xeed2ee),
+COL("thistle3", 0xcdb5cd),
+COL("thistle4", 0x8b7b8b),
+COL("gray0", 0x000000),
+COL("grey0", 0x000000),
+COL("gray1", 0x030303),
+COL("grey1", 0x030303),
+COL("gray2", 0x050505),
+COL("grey2", 0x050505),
+COL("gray3", 0x080808),
+COL("grey3", 0x080808),
+COL("gray4", 0x0a0a0a),
+COL("grey4", 0x0a0a0a),
+COL("gray5", 0x0d0d0d),
+COL("grey5", 0x0d0d0d),
+COL("gray6", 0x0f0f0f),
+COL("grey6", 0x0f0f0f),
+COL("gray7", 0x121212),
+COL("grey7", 0x121212),
+COL("gray8", 0x141414),
+COL("grey8", 0x141414),
+COL("gray9", 0x171717),
+COL("grey9", 0x171717),
+COL("gray10", 0x1a1a1a),
+COL("grey10", 0x1a1a1a),
+COL("gray11", 0x1c1c1c),
+COL("grey11", 0x1c1c1c),
+COL("gray12", 0x1f1f1f),
+COL("grey12", 0x1f1f1f),
+COL("gray13", 0x212121),
+COL("grey13", 0x212121),
+COL("gray14", 0x242424),
+COL("grey14", 0x242424),
+COL("gray15", 0x262626),
+COL("grey15", 0x262626),
+COL("gray16", 0x292929),
+COL("grey16", 0x292929),
+COL("gray17", 0x2b2b2b),
+COL("grey17", 0x2b2b2b),
+COL("gray18", 0x2e2e2e),
+COL("grey18", 0x2e2e2e),
+COL("gray19", 0x303030),
+COL("grey19", 0x303030),
+COL("gray20", 0x333333),
+COL("grey20", 0x333333),
+COL("gray21", 0x363636),
+COL("grey21", 0x363636),
+COL("gray22", 0x383838),
+COL("grey22", 0x383838),
+COL("gray23", 0x3b3b3b),
+COL("grey23", 0x3b3b3b),
+COL("gray24", 0x3d3d3d),
+COL("grey24", 0x3d3d3d),
+COL("gray25", 0x404040),
+COL("grey25", 0x404040),
+COL("gray26", 0x424242),
+COL("grey26", 0x424242),
+COL("gray27", 0x454545),
+COL("grey27", 0x454545),
+COL("gray28", 0x474747),
+COL("grey28", 0x474747),
+COL("gray29", 0x4a4a4a),
+COL("grey29", 0x4a4a4a),
+COL("gray30", 0x4d4d4d),
+COL("grey30", 0x4d4d4d),
+COL("gray31", 0x4f4f4f),
+COL("grey31", 0x4f4f4f),
+COL("gray32", 0x525252),
+COL("grey32", 0x525252),
+COL("gray33", 0x545454),
+COL("grey33", 0x545454),
+COL("gray34", 0x575757),
+COL("grey34", 0x575757),
+COL("gray35", 0x595959),
+COL("grey35", 0x595959),
+COL("gray36", 0x5c5c5c),
+COL("grey36", 0x5c5c5c),
+COL("gray37", 0x5e5e5e),
+COL("grey37", 0x5e5e5e),
+COL("gray38", 0x616161),
+COL("grey38", 0x616161),
+COL("gray39", 0x636363),
+COL("grey39", 0x636363),
+COL("gray40", 0x666666),
+COL("grey40", 0x666666),
+COL("gray41", 0x696969),
+COL("grey41", 0x696969),
+COL("gray42", 0x6b6b6b),
+COL("grey42", 0x6b6b6b),
+COL("gray43", 0x6e6e6e),
+COL("grey43", 0x6e6e6e),
+COL("gray44", 0x707070),
+COL("grey44", 0x707070),
+COL("gray45", 0x737373),
+COL("grey45", 0x737373),
+COL("gray46", 0x757575),
+COL("grey46", 0x757575),
+COL("gray47", 0x787878),
+COL("grey47", 0x787878),
+COL("gray48", 0x7a7a7a),
+COL("grey48", 0x7a7a7a),
+COL("gray49", 0x7d7d7d),
+COL("grey49", 0x7d7d7d),
+COL("gray50", 0x7f7f7f),
+COL("grey50", 0x7f7f7f),
+COL("gray51", 0x828282),
+COL("grey51", 0x828282),
+COL("gray52", 0x858585),
+COL("grey52", 0x858585),
+COL("gray53", 0x878787),
+COL("grey53", 0x878787),
+COL("gray54", 0x8a8a8a),
+COL("grey54", 0x8a8a8a),
+COL("gray55", 0x8c8c8c),
+COL("grey55", 0x8c8c8c),
+COL("gray56", 0x8f8f8f),
+COL("grey56", 0x8f8f8f),
+COL("gray57", 0x919191),
+COL("grey57", 0x919191),
+COL("gray58", 0x949494),
+COL("grey58", 0x949494),
+COL("gray59", 0x969696),
+COL("grey59", 0x969696),
+COL("gray60", 0x999999),
+COL("grey60", 0x999999),
+COL("gray61", 0x9c9c9c),
+COL("grey61", 0x9c9c9c),
+COL("gray62", 0x9e9e9e),
+COL("grey62", 0x9e9e9e),
+COL("gray63", 0xa1a1a1),
+COL("grey63", 0xa1a1a1),
+COL("gray64", 0xa3a3a3),
+COL("grey64", 0xa3a3a3),
+COL("gray65", 0xa6a6a6),
+COL("grey65", 0xa6a6a6),
+COL("gray66", 0xa8a8a8),
+COL("grey66", 0xa8a8a8),
+COL("gray67", 0xababab),
+COL("grey67", 0xababab),
+COL("gray68", 0xadadad),
+COL("grey68", 0xadadad),
+COL("gray69", 0xb0b0b0),
+COL("grey69", 0xb0b0b0),
+COL("gray70", 0xb3b3b3),
+COL("grey70", 0xb3b3b3),
+COL("gray71", 0xb5b5b5),
+COL("grey71", 0xb5b5b5),
+COL("gray72", 0xb8b8b8),
+COL("grey72", 0xb8b8b8),
+COL("gray73", 0xbababa),
+COL("grey73", 0xbababa),
+COL("gray74", 0xbdbdbd),
+COL("grey74", 0xbdbdbd),
+COL("gray75", 0xbfbfbf),
+COL("grey75", 0xbfbfbf),
+COL("gray76", 0xc2c2c2),
+COL("grey76", 0xc2c2c2),
+COL("gray77", 0xc4c4c4),
+COL("grey77", 0xc4c4c4),
+COL("gray78", 0xc7c7c7),
+COL("grey78", 0xc7c7c7),
+COL("gray79", 0xc9c9c9),
+COL("grey79", 0xc9c9c9),
+COL("gray80", 0xcccccc),
+COL("grey80", 0xcccccc),
+COL("gray81", 0xcfcfcf),
+COL("grey81", 0xcfcfcf),
+COL("gray82", 0xd1d1d1),
+COL("grey82", 0xd1d1d1),
+COL("gray83", 0xd4d4d4),
+COL("grey83", 0xd4d4d4),
+COL("gray84", 0xd6d6d6),
+COL("grey84", 0xd6d6d6),
+COL("gray85", 0xd9d9d9),
+COL("grey85", 0xd9d9d9),
+COL("gray86", 0xdbdbdb),
+COL("grey86", 0xdbdbdb),
+COL("gray87", 0xdedede),
+COL("grey87", 0xdedede),
+COL("gray88", 0xe0e0e0),
+COL("grey88", 0xe0e0e0),
+COL("gray89", 0xe3e3e3),
+COL("grey89", 0xe3e3e3),
+COL("gray90", 0xe5e5e5),
+COL("grey90", 0xe5e5e5),
+COL("gray91", 0xe8e8e8),
+COL("grey91", 0xe8e8e8),
+COL("gray92", 0xebebeb),
+COL("grey92", 0xebebeb),
+COL("gray93", 0xededed),
+COL("grey93", 0xededed),
+COL("gray94", 0xf0f0f0),
+COL("grey94", 0xf0f0f0),
+COL("gray95", 0xf2f2f2),
+COL("grey95", 0xf2f2f2),
+COL("gray96", 0xf5f5f5),
+COL("grey96", 0xf5f5f5),
+COL("gray97", 0xf7f7f7),
+COL("grey97", 0xf7f7f7),
+COL("gray98", 0xfafafa),
+COL("grey98", 0xfafafa),
+COL("gray99", 0xfcfcfc),
+COL("grey99", 0xfcfcfc),
+COL("gray100", 0xffffff),
+COL("grey100", 0xffffff),
+COL("darkgrey", 0xa9a9a9),
+COL("darkgray", 0xa9a9a9),
+COL("darkblue", 0x00008b),
+COL("darkcyan", 0x008b8b),
+COL("darkmagenta", 0x8b008b),
+COL("darkred", 0x8b0000),
+COL("lightgreen", 0x90ee90),
+COL(NULL,0) /* sentinel */
+};
+#undef COL
+
+static void
+colorname_to_rgb(const char *s, int *r, int *g, int *b)
+{
+  hashentry *ep;
+  long rgb;
+
+  if (!rgb_colors) rgb_colors = hashstr_import_static(col_list, 1000);
+  ep = hash_search(rgb_colors, (void*)s);
+  if (!ep) pari_err(e_MISC, "unknown color %s", s);
+  rgb = (long)ep->val;
+  *b = rgb & 0xff; rgb >>= 8;
+  *g = rgb & 0xff; rgb >>= 8;
+  *r = rgb;
+}
+
+void
+color_to_rgb(GEN c, int *r, int *g, int *b)
+{
+  switch(typ(c))
+  {
+    case t_STR:
+      colorname_to_rgb(GSTR(c), r,g,b);
+      break;
+    default: /* t_VECSMALL: */
+      *r = c[1]; *g = c[2]; *b = c[3];
+      break;
+  }
+}
diff --git a/src/graph/plotps.c b/src/graph/plotps.c
new file mode 100644
index 0000000..bc73ab3
--- /dev/null
+++ b/src/graph/plotps.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*           HIGH RESOLUTION PLOT VIA POSTSCRIPT FILE              */
+/*******************************************************************/
+
+#include "pari.h"
+#include "paripriv.h"
+#include "rect.h"
+
+BEGINEXTERN
+void system0(const char *s);
+ENDEXTERN
+
+void
+rectdraw0(long *w, long *x, long *y, long lw)
+{
+  struct plot_eng plot;
+  FILE *file;
+  char *s, *cmd;
+  const char *v;
+  if (pari_daemon()) return;  /* parent process returns */
+  s = pari_unique_filename("plotps");
+  pari_unlink(s);
+  s = stack_strcat(s, ".ps");
+  file = fopen(s, "w");
+  if (!file) pari_err_FILE("postscript file", s);
+  psplot_init(&plot, file, 1.0, 1.0, 9);
+  fprintf(file,"0 %ld translate -90 rotate\n", plot.pl->height - 50);
+  gen_rectdraw0(&plot, w, x, y, lw, 1, 1);
+  fprintf(file,"stroke showpage\n"); (void)fclose(file);
+  v = os_getenv("GP_POSTSCRIPT_VIEWER");
+  if (!v) v = "open -W";
+  cmd = pari_sprintf("%s \"%s\" 2>/dev/null", v, s);
+  system0(cmd);
+  pari_unlink(s); exit(0);
+}
+
+void
+PARI_get_plot()
+{
+  if (pari_plot.init) return;
+  pari_plot.width  = 400;
+  pari_plot.height = 300;
+  pari_plot.fheight = 9;
+  pari_plot.fwidth  = 6;
+  pari_plot.hunit   = 3;
+  pari_plot.vunit   = 3;
+  pari_plot.init = 1;
+}
diff --git a/src/graph/rect.h b/src/graph/rect.h
new file mode 100644
index 0000000..688a218
--- /dev/null
+++ b/src/graph/rect.h
@@ -0,0 +1,287 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+BEGINEXTERN
+
+#define PLOT_NAME_LEN 20
+typedef struct PARI_plot {
+  long width;
+  long height;
+  long hunit;
+  long vunit;
+  long fwidth;
+  long fheight;
+  long init;
+  char name[PLOT_NAME_LEN+1];
+} PARI_plot;
+
+extern PARI_plot pari_plot, pari_psplot;
+extern PARI_plot *pari_plot_engine;
+
+typedef struct dblPointList{
+  double *d;                   /* data */
+  long nb;                     /* number of elements */
+  double xsml,xbig,ysml,ybig;  /* extrema */
+} dblPointList;
+
+typedef struct RectObj {
+  struct RectObj *next;
+  long code,color;
+} RectObj;
+
+typedef struct PariRect {
+  RectObj *head,*tail;
+  long sizex,sizey;
+  double cursorx,cursory;
+  double xscale,yscale;
+  double xshift,yshift;
+  long has_graph; /* xy-ranges of this rectangle should be used
+                     for interactive operations.  */
+} PariRect;
+
+/* The structures below are "subclasses" of RectObj. */
+
+typedef struct RectObj1P {
+  struct RectObj *next;
+  long code,color;
+  double x,y;
+} RectObj1P;
+
+typedef struct RectObj2P {
+  struct RectObj *next;
+  long code,color;
+  double x1,y1;
+  double x2,y2;
+} RectObj2P;
+
+typedef struct RectObjMP {
+  struct RectObj *next;
+  long code,color;
+  long count;
+  double *xs,*ys;
+} RectObjMP;
+
+typedef struct RectObjST {
+  struct RectObj *next;
+  long code,color;
+  long length;
+  char *s;
+  double x,y;
+  long dir;
+} RectObjST;
+
+typedef struct RectObjPN {
+  struct RectObj *next;
+  long code,color;
+  long pen;
+} RectObjPN;
+
+typedef struct RectObjPS {
+  struct RectObj *next;
+  long code,color;
+  double size;
+} RectObjPS;
+
+struct plot_points { long x, y; };
+
+struct plot_eng {
+  PARI_plot *pl;
+  void *data;
+  void (*sc)(void *data, long col);
+  void (*pt)(void *data, long x, long y);
+  void (*ln)(void *data, long x1, long y1, long x2, long y2);
+  void (*bx)(void *data, long x, long y, long w, long h);
+  void (*mp)(void *data, long n, struct plot_points *points);
+  void (*ml)(void *data, long n, struct plot_points *points);
+  void (*st)(void *data, long x, long y, char *s, long l);
+};
+
+#define ROt_MV 0                        /* Move */
+#define ROt_PT 1                        /* Point */
+#define ROt_LN 2                        /* Line */
+#define ROt_BX 3                        /* Box */
+#define ROt_MP 4                        /* Multiple point */
+#define ROt_ML 5                        /* Multiple lines */
+#define ROt_ST 6                        /* String */
+#define ROt_PTT 7                        /* Point type change */
+#define ROt_LNT 8                        /* Line type change */
+#define ROt_PTS 9                        /* Point size change */
+#define ROt_NULL 10                /* To be the start of the chain */
+
+#define ROt_MAX 10                /* Maximal type */
+
+/* Pointer conversion. */
+
+#define RoMV(rop) ((RectObj1P*)rop)
+#define RoPT(rop) ((RectObj1P*)rop)
+#define RoLN(rop) ((RectObj2P*)rop)
+#define RoBX(rop) ((RectObj2P*)rop)
+#define RoMP(rop) ((RectObjMP*)rop)
+#define RoML(rop) ((RectObjMP*)rop)
+#define RoST(rop) ((RectObjST*)rop)
+#define RoPTT(rop) ((RectObjPN*)rop)
+#define RoPTS(rop) ((RectObjPS*)rop)
+#define RoLNT(rop) ((RectObjPN*)rop)
+#define RoNULL(rop) ((RectObj*)rop)
+
+/* All the access to the rectangle data _should_ go via these macros! */
+
+#define RHead(rp) ((rp)->head)
+#define RTail(rp) ((rp)->tail)
+#define RXsize(rp) ((rp)->sizex)
+#define RYsize(rp) ((rp)->sizey)
+#define RXcursor(rp) ((rp)->cursorx)
+#define RYcursor(rp) ((rp)->cursory)
+#define RXshift(rp) ((rp)->xshift)
+#define RYshift(rp) ((rp)->yshift)
+#define RXscale(rp) ((rp)->xscale)
+#define RYscale(rp) ((rp)->yscale)
+#define RHasGraph(rp) ((rp)->has_graph)
+
+#define RoNext(rop) ((rop)->next)
+#define RoType(rop) ((rop)->code)
+#define RoCol(rop) ((rop)->color)
+#define RoMVx(rop) (RoMV(rop)->x)
+#define RoMVy(rop) (RoMV(rop)->y)
+#define RoPTx(rop) (RoPT(rop)->x)
+#define RoPTy(rop) (RoPT(rop)->y)
+#define RoLNx1(rop) (RoLN(rop)->x1)
+#define RoLNy1(rop) (RoLN(rop)->y1)
+#define RoLNx2(rop) (RoLN(rop)->x2)
+#define RoLNy2(rop) (RoLN(rop)->y2)
+#define RoBXx1(rop) (RoBX(rop)->x1)
+#define RoBXy1(rop) (RoBX(rop)->y1)
+#define RoBXx2(rop) (RoBX(rop)->x2)
+#define RoBXy2(rop) (RoBX(rop)->y2)
+
+#define RoMPcnt(rop) (RoMP(rop)->count)
+#define RoMPxs(rop) (RoMP(rop)->xs)
+#define RoMPys(rop) (RoMP(rop)->ys)
+
+#define RoMLcnt(rop) (RoML(rop)->count)
+#define RoMLxs(rop) (RoML(rop)->xs)
+#define RoMLys(rop) (RoML(rop)->ys)
+
+#define RoSTs(rop) (RoST(rop)->s)
+#define RoSTl(rop) (RoST(rop)->length)
+#define RoSTx(rop) (RoST(rop)->x)
+#define RoSTy(rop) (RoST(rop)->y)
+#define RoSTdir(rop) (RoST(rop)->dir)
+
+#define RoSTdirLEFT          0x00
+#define RoSTdirCENTER          0x01
+#define RoSTdirRIGHT          0x02
+#define RoSTdirHPOS_mask  0x03
+
+#define RoSTdirBOTTOM          0x00
+#define RoSTdirVCENTER          0x04
+#define RoSTdirTOP          0x08
+#define RoSTdirVPOS_mask  0x0c
+
+#define RoSTdirHGAP          0x10
+#define RoSTdirVGAP          0x20
+
+
+#define RoPTTpen(rop) (RoPTT(rop)->pen)
+#define RoLNTpen(rop) (RoLNT(rop)->pen)
+#define RoPTSsize(rop) (RoPTS(rop)->size)
+
+#define PL_POINTS 1
+
+#define PLOT_PARAMETRIC   0x00001
+#define PLOT_RECURSIVE    0x00002
+#define PLOT_NO_RESCALE   0x00004
+#define PLOT_NO_AXE_X     0x00008
+#define PLOT_NO_AXE_Y     0x00010
+#define PLOT_NO_FRAME     0x00020
+#define PLOT_POINTS       0x00040
+#define PLOT_POINTS_LINES 0x00080
+#define PLOT_SPLINES      0x00100
+#define PLOT_NO_TICK_X    0x00200
+#define PLOT_NO_TICK_Y    0x00400
+#define PLOT_NODOUBLETICK 0x00800
+#define PLOT_COMPLEX      0x01000
+
+#define RECT_CP_RELATIVE  0x1
+#define RECT_CP_NW        0x0
+#define RECT_CP_SW        0x2
+#define RECT_CP_SE        0x4
+#define RECT_CP_NE        0x6
+
+#define TICKS_CLOCKW        1        /* Draw in clockwise direction */
+#define TICKS_ACLOCKW        2        /* Draw in anticlockwise direction */
+#define TICKS_ENDSTOO        4        /* Draw at endspoints if needed */
+#define TICKS_NODOUBLE        8        /* Do not draw double-length ticks */
+
+/* Not implemented yet */
+#define TICKS_COORD        16        /* Output [x,y,l,isdbl] for each tick */
+#define TICKS_RELATIVE        32        /* x,y-coordinates are relative */
+
+extern long  rectpoint_itype;
+extern long  rectline_itype;
+
+/* plotport.c */
+typedef long (*col_counter)[ROt_MAX];
+
+void  color_to_rgb(GEN c, int *r, int *g, int *b);
+void  initrect(long ne, long x, long y);
+void  initrect_gen(long ne, GEN x, GEN y, long flag);
+void  killrect(long ne);
+void  plot_count(long *w, long lw, col_counter rcolcnt);
+void  plot(GEN a, GEN b, GEN code, GEN ysmlu, GEN ybigu, long prec);
+GEN   ploth(GEN a, GEN b, GEN code, long prec, long flag, long numpoints);
+GEN   ploth2(GEN a, GEN b, GEN code, long prec);
+GEN   plothmult(GEN a, GEN b, GEN code, long prec);
+GEN   plothraw(GEN listx, GEN listy, long flag);
+GEN   plothsizes(void);
+GEN   plothsizes_flag(long flag);
+void  postdraw(GEN list);
+void  postdraw_flag(GEN list, long flag);
+GEN   postploth(GEN a,GEN b,GEN code,long prec,long flag,long numpoints);
+GEN   postploth2(GEN a,GEN b,GEN code,long prec,long numpoints);
+GEN   postplothraw(GEN listx, GEN listy, long flag);
+void  psplot_init(struct plot_eng *S, FILE *f, double xscale, double yscale, long fontsize);
+void  Printx(dblPointList *f);
+void  rectbox(long ne, GEN gx2, GEN gy2);
+void  rectcolor(long ne, long color);
+void  rectcopy(long source, long dest, long xoff, long yoff);
+void  rectcopy_gen(long source, long dest, GEN xoff, GEN yoff, long flag);
+GEN   rectcursor(long ne);
+void  rectdraw(GEN list);
+void  rectdraw_flag(GEN list, long flag);
+void  rectline(long ne, GEN gx2, GEN gy2);
+void  rectlines(long ne, GEN listx, GEN listy, long flag);
+void  rectlinetype(long ne, long t);
+void  rectmove(long ne, GEN x, GEN y);
+GEN   rectploth(long drawrect,GEN a, GEN b, GEN code, long prec, ulong flags, long testpoints);
+GEN   rectplothraw(long drawrect, GEN data, long flags);
+void  rectpoint(long ne, GEN x, GEN y);
+void  rectpoints(long ne, GEN listx, GEN listy);
+void  rectpointtype(long ne, long t);
+void  rectpointsize(long ne, GEN size);
+void  rectrbox(long ne, GEN gx2, GEN gy2);
+void  rectrline(long ne, GEN gx2, GEN gy2);
+void  rectrmove(long ne, GEN x, GEN y);
+void  rectrpoint(long ne, GEN x, GEN y);
+void  rectscale(long ne, GEN x1, GEN x2, GEN y1, GEN y2);
+void  rectstring(long ne, char *x);
+void  rectstring3(long ne, char *x, long dir);
+void  rectclip(long rect);
+
+void gen_rectdraw0(struct plot_eng *eng, long *w, long *x, long *y, long lw, double xs, double ys);
+
+/* architecture-dependent plot file (plotX.c ...) */
+void  PARI_get_plot();
+void  rectdraw0(long *w, long *x, long *y, long lw);
+
+ENDEXTERN
diff --git a/src/headers/pari.h b/src/headers/pari.h
new file mode 100644
index 0000000..b655a0d
--- /dev/null
+++ b/src/headers/pari.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#ifndef __GENPARI__
+#define __GENPARI__
+#include "paricfg.h"
+
+#include <stdlib.h>   /* malloc, free, atoi */
+#ifdef UNIX
+#  define _INCLUDE_POSIX_SOURCE /* for HPUX */
+#  include <sys/types.h> /* size_t */
+#endif
+
+#ifdef WINCE
+#  include <windows.h>
+#else
+#  include <signal.h>
+#  include <stdio.h>
+#endif
+
+#include <stdarg.h>
+#include <setjmp.h>
+#include <string.h>
+#if !defined(_WIN32) && !defined(WINCE)
+#  include <unistd.h>
+#else
+#  include <io.h>
+#endif
+#include <math.h>
+#include <memory.h>
+#include <ctype.h>
+
+#ifdef WINCE
+#  include "pariCE.h"
+#endif
+#include "parisys.h"
+#include "parigen.h"
+#include "paricast.h"
+#include "paristio.h"
+#include "paricom.h"
+#include "pariold.h"
+#include "parierr.h"
+BEGINEXTERN
+#include "paridecl.h"
+#include "paritune.h"
+#include "parimt.h"
+#ifndef PARI_NO_MPINL_H
+#  include "mpinl.h"
+#endif
+#ifndef PARI_NO_PARIINL_H
+#  include "pariinl.h"
+#endif
+ENDEXTERN
+#endif
diff --git a/src/headers/paricast.h b/src/headers/paricast.h
new file mode 100644
index 0000000..423073f
--- /dev/null
+++ b/src/headers/paricast.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define mael2(m,x1,x2)          (((GEN*)   (m))[x1][x2])
+#define mael3(m,x1,x2,x3)       (((GEN**)  (m))[x1][x2][x3])
+#define mael4(m,x1,x2,x3,x4)    (((GEN***) (m))[x1][x2][x3][x4])
+#define mael5(m,x1,x2,x3,x4,x5) (((GEN****)(m))[x1][x2][x3][x4][x5])
+#define mael mael2
+
+#define gmael1(m,x1)             (((GEN*)    (m))[x1])
+#define gmael2(m,x1,x2)          (((GEN**)   (m))[x1][x2])
+#define gmael3(m,x1,x2,x3)       (((GEN***)  (m))[x1][x2][x3])
+#define gmael4(m,x1,x2,x3,x4)    (((GEN****) (m))[x1][x2][x3][x4])
+#define gmael5(m,x1,x2,x3,x4,x5) (((GEN*****)(m))[x1][x2][x3][x4][x5])
+#define gmael(m,x,y) gmael2(m,x,y)
+#define gel(m,x)     gmael1(m,x)
+
+#define gcoeff(a,i,j) (((GEN**)(a))[j][i])
+#define coeff(a,i,j) (((GEN*)(a))[j][i])
+
+#define GSTR(x) ((char*) (((GEN) (x)) + 1 ))
diff --git a/src/headers/paricom.h b/src/headers/paricom.h
new file mode 100644
index 0000000..fef9970
--- /dev/null
+++ b/src/headers/paricom.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/******************************************************************/
+/*                                                                */
+/*              PARI header file (common to all versions)         */
+/*                                                                */
+/******************************************************************/
+#ifdef STMT_START /* perl headers */
+#  undef STMT_START
+#endif
+#ifdef STMT_END
+#  undef STMT_END
+#endif
+/* STMT_START { statements; } STMT_END;
+ * can be used as a single statement, as in
+ * if (x) STMT_START { ... } STMT_END; else ...
+ * [ avoid "dangling else" problem in macros ] */
+#define STMT_START        do
+#define STMT_END        while (0)
+/*=====================================================================*/
+/* pari_CATCH(numer) {
+ *   recovery
+ * } pari_TRY {
+ *   code
+ * } pari_ENDCATCH
+ * will execute 'code', then 'recovery' if exception 'numer' is thrown
+ * [ any exception if numer == CATCH_ALL ].
+ * pari_RETRY = as pari_TRY, but execute 'recovery', then 'code' again [still catching] */
+
+extern THREAD jmp_buf *iferr_env;
+extern const long CATCH_ALL;
+
+#define pari_CATCH2(var,err) {         \
+  jmp_buf *var=iferr_env;    \
+  jmp_buf __env;             \
+  iferr_env = &__env;        \
+  if (setjmp(*iferr_env))    \
+  {                          \
+    GEN __iferr_data = pari_err_last(); \
+    iferr_env = var; \
+    if (err!=CATCH_ALL && err_get_num(__iferr_data) != err) \
+      pari_err(0, __iferr_data);
+
+#define pari_CATCH2_reset(var) (iferr_env = var)
+#define pari_ENDCATCH2(var) iferr_env = var; } }
+
+#define pari_CATCH(err) pari_CATCH2(__iferr_old,err)
+#define pari_RETRY } iferr_env = &__env; {
+#define pari_TRY } else {
+#define pari_CATCH_reset() pari_CATCH2_reset(__iferr_old)
+#define pari_ENDCATCH pari_ENDCATCH2(__iferr_old)
+
+extern const double LOG2, LOG10_2, LOG2_10;
+#ifndef  PI
+#  define PI (3.141592653589)
+#endif
+
+/* Common global variables: */
+extern int new_galois_format, factor_add_primes, factor_proven;
+extern ulong DEBUGFILES, DEBUGLEVEL, DEBUGMEM, precdl;
+extern long DEBUGVAR;
+extern ulong pari_mt_nbthreads;
+extern THREAD GEN  bernzone;
+extern GEN primetab;
+extern GEN gen_m1,gen_1,gen_2,gen_m2,ghalf,gen_0,gnil,err_e_STACK;
+extern THREAD VOLATILE int PARI_SIGINT_block, PARI_SIGINT_pending;
+
+extern const long lontyp[];
+extern void (*cb_pari_ask_confirm)(const char *);
+extern int  (*cb_pari_whatnow)(PariOUT *out, const char *, int);
+extern void (*cb_pari_sigint)(void);
+extern int (*cb_pari_handle_exception)(long);
+extern void (*cb_pari_pre_recover)(long);
+extern void (*cb_pari_err_recover)(long);
+extern const char *pari_library_path;
+
+enum manage_var_t {
+  manage_var_create,
+  manage_var_delete,
+  manage_var_init,
+  manage_var_next,
+  manage_var_max_avail,
+  manage_var_pop
+};
+
+/* pari_init_opts */
+enum {
+  INIT_JMPm = 1,
+  INIT_SIGm = 2,
+  INIT_DFTm = 4,
+  INIT_noPRIMEm = 8,
+  INIT_noIMTm = 16
+};
+
+#ifndef HAS_EXP2
+#  undef exp2
+#  define exp2(x) (exp((double)(x)*LOG2))
+#endif
+#ifndef HAS_LOG2
+#  undef log2
+#  define log2(x) (log((double)(x))/LOG2)
+#endif
+
+#define ONLY_REM ((GEN*)0x1L)
+#define ONLY_DIVIDES ((GEN*)0x2L)
+
+#define NEXT_PRIME_VIADIFF(p,d) STMT_START { (p) += *(d)++; } STMT_END
+#define PREC_PRIME_VIADIFF(p,d) STMT_START { (p) -= *--(d); } STMT_END
+#define NEXT_PRIME_VIADIFF_CHECK(p,d)  STMT_START \
+  { if (!*(d)) pari_err_MAXPRIME(0); NEXT_PRIME_VIADIFF(p,d); } STMT_END
diff --git a/src/headers/paridecl.h b/src/headers/paridecl.h
new file mode 100644
index 0000000..f60b229
--- /dev/null
+++ b/src/headers/paridecl.h
@@ -0,0 +1,4152 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                DECLARATIONS of PUBLIC FUNCTIONS                 */
+/*                                                                 */
+/*******************************************************************/
+#include "parinf.h"
+
+/* black box groups */
+struct bb_group
+{
+  GEN   (*mul)(void *E, GEN, GEN);
+  GEN   (*pow)(void *E, GEN, GEN);
+  GEN   (*rand)(void *E);
+  ulong (*hash)(GEN);
+  int   (*equal)(GEN,GEN);
+  int   (*equal1)(GEN);
+  GEN   (*easylog)(void *E, GEN, GEN, GEN);
+};
+
+/* black box fields */
+struct bb_field
+{
+  GEN (*red)(void *E ,GEN);
+  GEN (*add)(void *E ,GEN, GEN);
+  GEN (*mul)(void *E ,GEN, GEN);
+  GEN (*neg)(void *E ,GEN);
+  GEN (*inv)(void *E ,GEN);
+  int (*equal0)(GEN);
+  GEN (*s)(void *E, long);
+};
+
+/* black box algebra */
+struct bb_algebra
+{
+  GEN (*red)(void *E, GEN x);
+  GEN (*add)(void *E, GEN x, GEN y);
+  GEN (*mul)(void *E, GEN x, GEN y);
+  GEN (*sqr)(void *E, GEN x);
+  GEN (*one)(void *E);
+  GEN (*zero)(void *E);
+};
+
+/* OBSOLETE */
+GEN     bernvec(long nomb);
+GEN     buchimag(GEN D, GEN c1, GEN c2, GEN gCO);
+GEN     buchreal(GEN D, GEN gsens, GEN c1, GEN c2, GEN gRELSUP, long prec);
+GEN     zidealstar(GEN nf, GEN x);
+GEN     zidealstarinit(GEN nf, GEN x);
+GEN     zidealstarinitgen(GEN nf, GEN x);
+GEN     rootmod(GEN f, GEN p);
+GEN     rootmod2(GEN f, GEN p);
+GEN     factmod(GEN f, GEN p);
+GEN     simplefactmod(GEN f, GEN p);
+GEN     listcreate(void);
+void    listkill(GEN list);
+GEN     discrayabs(GEN bnr,GEN subgroup);
+GEN     discrayabscond(GEN bnr,GEN subgroup);
+GEN     discrayrel(GEN bnr,GEN subgroup);
+GEN     discrayrelcond(GEN bnr,GEN subgroup);
+GEN     isprincipalforce(GEN bnf,GEN x);
+GEN     isprincipalgen(GEN bnf, GEN x);
+GEN     isprincipalgenforce(GEN bnf,GEN x);
+
+/* F2x.c */
+
+GEN     F2c_to_Flc(GEN x);
+GEN     F2c_to_ZC(GEN x);
+GEN     F2c_to_mod(GEN x);
+GEN     F2m_rowslice(GEN x, long a, long b);
+GEN     F2m_to_Flm(GEN z);
+GEN     F2m_to_ZM(GEN z);
+GEN     F2m_to_mod(GEN z);
+void    F2v_add_inplace(GEN x, GEN y);
+ulong   F2v_dotproduct(GEN x, GEN y);
+GEN     F2v_slice(GEN x, long a, long b);
+GEN     F2x_F2xq_eval(GEN Q, GEN x, GEN T);
+GEN     F2x_F2xqV_eval(GEN P, GEN V, GEN T);
+GEN     F2x_1_add(GEN y);
+GEN     F2x_add(GEN x, GEN y);
+GEN     F2x_deflate(GEN x0, long d);
+long    F2x_degree(GEN x);
+GEN     F2x_deriv(GEN x);
+GEN     F2x_divrem(GEN x, GEN y, GEN *pr);
+void    F2x_even_odd(GEN p, GEN *pe, GEN *po);
+GEN     F2x_extgcd(GEN a, GEN b, GEN *ptu, GEN *ptv);
+GEN     F2x_gcd(GEN a, GEN b);
+GEN     F2x_halfgcd(GEN a, GEN b);
+int     F2x_issquare(GEN a);
+GEN     F2x_mul(GEN x, GEN y);
+GEN     F2x_rem(GEN x, GEN y);
+GEN     F2x_shift(GEN y, long d);
+GEN     F2x_sqr(GEN x);
+GEN     F2x_sqrt(GEN x);
+GEN     F2x_to_F2v(GEN x, long n);
+GEN     F2x_to_Flx(GEN x);
+GEN     F2x_to_ZX(GEN x);
+long    F2x_valrem(GEN x, GEN *Z);
+GEN     F2xC_to_ZXC(GEN x);
+GEN     F2xV_to_F2m(GEN v, long n);
+GEN     F2xq_Artin_Schreier(GEN a, GEN T);
+GEN     FlxqXQV_autsum(GEN aut, long n, GEN S, GEN T, ulong p);
+GEN     F2xq_autpow(GEN x, long n, GEN T);
+GEN     F2xq_conjvec(GEN x, GEN T);
+GEN     F2xq_div(GEN x,GEN y,GEN T);
+GEN     F2xq_inv(GEN x, GEN T);
+GEN     F2xq_invsafe(GEN x, GEN T);
+GEN     F2xq_log(GEN a, GEN g, GEN ord, GEN T);
+GEN     F2xq_matrix_pow(GEN y, long n, long m, GEN P);
+GEN     F2xq_mul(GEN x, GEN y, GEN pol);
+GEN     F2xq_order(GEN a, GEN ord, GEN T);
+GEN     F2xq_pow(GEN x, GEN n, GEN pol);
+GEN     F2xq_powu(GEN x, ulong n, GEN pol);
+GEN     F2xq_powers(GEN x, long l, GEN T);
+GEN     F2xq_sqr(GEN x,GEN pol);
+GEN     F2xq_sqrt(GEN a, GEN T);
+GEN     F2xq_sqrt_fast(GEN c, GEN sqx, GEN T);
+GEN     F2xq_sqrtn(GEN a, GEN n, GEN T, GEN *zeta);
+ulong   F2xq_trace(GEN x, GEN T);
+GEN     Flm_to_F2m(GEN x);
+GEN     Flv_to_F2v(GEN x);
+GEN     Flx_to_F2x(GEN x);
+GEN     Rg_to_F2xq(GEN x, GEN T);
+GEN     RgM_to_F2m(GEN x);
+GEN     RgV_to_F2v(GEN x);
+GEN     RgX_to_F2x(GEN x);
+GEN     Z_to_F2x(GEN x, long v);
+GEN     ZM_to_F2m(GEN x);
+GEN     ZV_to_F2v(GEN x);
+GEN     ZX_to_F2x(GEN x);
+GEN     ZXT_to_FlxT(GEN z, ulong p);
+GEN     ZXX_to_F2xX(GEN B, long v);
+GEN     gener_F2xq(GEN T, GEN *po);
+const struct bb_field *get_F2xq_field(void **E, GEN T);
+GEN     random_F2x(long d, long vs);
+
+/* F2xqE.c */
+
+GEN     F2xq_ellcard(GEN a2, GEN a6, GEN T);
+GEN     F2xq_ellgens(GEN a2, GEN a6, GEN ch, GEN D, GEN m, GEN T);
+GEN     F2xq_ellgroup(GEN a2, GEN a6, GEN N, GEN T, GEN *pt_m);
+GEN     F2xqE_add(GEN P, GEN Q, GEN a2, GEN T);
+GEN     F2xqE_changepoint(GEN x, GEN ch, GEN T);
+GEN     F2xqE_changepointinv(GEN x, GEN ch, GEN T);
+GEN     F2xqE_dbl(GEN P, GEN a2, GEN T);
+GEN     F2xqE_log(GEN a, GEN b, GEN o, GEN a2, GEN T);
+GEN     F2xqE_mul(GEN P, GEN n, GEN a2, GEN T);
+GEN     F2xqE_neg(GEN P, GEN a2, GEN T);
+GEN     F2xqE_order(GEN z, GEN o, GEN a2, GEN T);
+GEN     F2xqE_sub(GEN P, GEN Q, GEN a2, GEN T);
+GEN     F2xqE_tatepairing(GEN t, GEN s, GEN m, GEN a2, GEN T);
+GEN     F2xqE_weilpairing(GEN t, GEN s, GEN m, GEN a2, GEN T);
+const struct bb_group * get_F2xqE_group(void **E, GEN a2, GEN a6, GEN T);
+GEN     RgE_to_F2xqE(GEN x, GEN T);
+GEN     random_F2xqE(GEN a2, GEN a6, GEN T);
+
+/* Flx.c */
+
+GEN     Fl_to_Flx(ulong x, long sv);
+GEN     Flc_to_ZC(GEN z);
+GEN     Flm_to_FlxV(GEN x, long sv);
+GEN     Flm_to_FlxX(GEN x, long v,long w);
+GEN     Flm_to_ZM(GEN z);
+GEN     Flv_to_Flx(GEN x, long vs);
+GEN     Flv_to_ZV(GEN z);
+GEN     Flv_polint(GEN xa, GEN ya, ulong p, long vs);
+GEN     Flv_roots_to_pol(GEN a, ulong p, long vs);
+GEN     Fly_to_FlxY(GEN B, long v);
+GEN     Flx_Fl_add(GEN y, ulong x, ulong p);
+GEN     Flx_Fl_mul(GEN y, ulong x, ulong p);
+GEN     Flx_Fl_mul_to_monic(GEN y, ulong x, ulong p);
+GEN     Flx_Flxq_eval(GEN f,GEN x,GEN T,ulong p);
+GEN     Flx_FlxqV_eval(GEN f,GEN x,GEN T,ulong p);
+GEN     Flx_add(GEN x, GEN y, ulong p);
+GEN     Flx_deflate(GEN x0, long d);
+GEN     Flx_deriv(GEN z, ulong p);
+GEN     Flx_double(GEN y, ulong p);
+GEN     Flx_div_by_X_x(GEN a, ulong x, ulong p, ulong *rem);
+GEN     Flx_divrem(GEN x, GEN y, ulong p, GEN *pr);
+int     Flx_equal(GEN V, GEN W);
+ulong   Flx_eval(GEN x, ulong y, ulong p);
+GEN     Flx_extgcd(GEN a, GEN b, ulong p, GEN *ptu, GEN *ptv);
+ulong   Flx_extresultant(GEN a, GEN b, ulong p, GEN *ptU, GEN *ptV);
+GEN     Flx_gcd(GEN a, GEN b, ulong p);
+GEN     Flx_get_red(GEN T, ulong p);
+GEN     Flx_halfgcd(GEN a, GEN b, ulong p);
+GEN     Flx_inflate(GEN x0, long d);
+GEN     Flx_invBarrett(GEN T, ulong p);
+int     Flx_is_squarefree(GEN z, ulong p);
+int     Flx_is_smooth(GEN g, long r, ulong p);
+GEN     Flx_mod_Xn1(GEN T, ulong n, ulong p);
+GEN     Flx_mod_Xnm1(GEN T, ulong n, ulong p);
+GEN     Flx_mul(GEN x, GEN y, ulong p);
+GEN     Flx_neg(GEN x, ulong p);
+GEN     Flx_neg_inplace(GEN x, ulong p);
+GEN     Flx_normalize(GEN z, ulong p);
+GEN     Flx_pow(GEN x, long n, ulong p);
+GEN     Flx_recip(GEN x);
+GEN     Flx_red(GEN z, ulong p);
+GEN     Flx_rem(GEN x, GEN y, ulong p);
+GEN     Flx_renormalize(GEN x, long l);
+ulong   Flx_resultant(GEN a, GEN b, ulong p);
+GEN     Flx_shift(GEN a, long n);
+GEN     Flx_splitting(GEN p, long k);
+GEN     Flx_sqr(GEN x, ulong p);
+GEN     Flx_sub(GEN x, GEN y, ulong p);
+GEN     Flx_to_Flv(GEN x, long N);
+GEN     Flx_to_FlxX(GEN z, long v);
+GEN     Flx_to_ZX(GEN z);
+GEN     Flx_to_ZX_inplace(GEN z);
+GEN     Flx_triple(GEN y, ulong p);
+long    Flx_val(GEN x);
+long    Flx_valrem(GEN x, GEN *Z);
+GEN     FlxC_to_ZXC(GEN x);
+GEN     FlxM_Flx_add_shallow(GEN x, GEN y, ulong p);
+GEN     FlxM_to_ZXM(GEN z);
+GEN     FlxT_red(GEN z, ulong p);
+GEN     FlxV_to_ZXV(GEN x);
+GEN     FlxV_Flc_mul(GEN V, GEN W, ulong p);
+GEN     FlxV_red(GEN z, ulong p);
+GEN     FlxV_to_Flm(GEN v, long n);
+GEN     FlxX_Fl_mul(GEN x, ulong y, ulong p);
+GEN     FlxX_Flx_add(GEN y, GEN x, ulong p);
+GEN     FlxX_Flx_mul(GEN x, GEN y, ulong p);
+GEN     FlxX_add(GEN P, GEN Q, ulong p);
+GEN     FlxX_double(GEN x, ulong p);
+GEN     FlxX_neg(GEN x, ulong p);
+GEN     FlxX_sub(GEN P, GEN Q, ulong p);
+GEN     FlxX_swap(GEN x, long n, long ws);
+GEN     FlxX_renormalize(GEN x, long lx);
+GEN     FlxX_shift(GEN a, long n);
+GEN     FlxX_to_Flm(GEN v, long n);
+GEN     FlxX_to_FlxC(GEN x, long N, long sv);
+GEN     FlxX_to_ZXX(GEN B);
+GEN     FlxX_triple(GEN x, ulong p);
+GEN     FlxXV_to_FlxM(GEN v, long n, long sv);
+GEN     FlxY_Flxq_evalx(GEN P, GEN x, GEN T, ulong p);
+GEN     FlxY_Flx_div(GEN x, GEN y, ulong p);
+GEN     FlxY_evalx(GEN Q, ulong x, ulong p);
+GEN     FlxYqq_pow(GEN x, GEN n, GEN S, GEN T, ulong p);
+GEN     Flxq_autpow(GEN x, ulong n, GEN T, ulong p);
+GEN     Flxq_autsum(GEN x, ulong n, GEN T, ulong p);
+GEN     Flxq_charpoly(GEN x, GEN T, ulong p);
+GEN     Flxq_conjvec(GEN x, GEN T, ulong p);
+GEN     Flxq_div(GEN x, GEN y, GEN T, ulong p);
+GEN     Flxq_inv(GEN x,GEN T,ulong p);
+GEN     Flxq_invsafe(GEN x, GEN T, ulong p);
+int     Flxq_issquare(GEN x, GEN T, ulong p);
+int     Flxq_is2npower(GEN x, long n, GEN T, ulong p);
+GEN     Flxq_log(GEN a, GEN g, GEN ord, GEN T, ulong p);
+GEN     Flxq_lroot(GEN a, GEN T, long p);
+GEN     Flxq_lroot_fast(GEN a, GEN sqx, GEN T, long p);
+GEN     Flxq_matrix_pow(GEN y, long n, long m, GEN P, ulong l);
+GEN     Flxq_minpoly(GEN x, GEN T, ulong p);
+GEN     Flxq_mul(GEN x, GEN y, GEN T, ulong p);
+ulong   Flxq_norm(GEN x, GEN T, ulong p);
+GEN     Flxq_order(GEN a, GEN ord, GEN T, ulong p);
+GEN     Flxq_pow(GEN x, GEN n, GEN T, ulong p);
+GEN     Flxq_powu(GEN x, ulong n, GEN T, ulong p);
+GEN     Flxq_powers(GEN x, long l, GEN T, ulong p);
+GEN     Flxq_sqr(GEN y,GEN T,ulong p);
+GEN     Flxq_sqrt(GEN a, GEN T, ulong p);
+GEN     Flxq_sqrtn(GEN a, GEN n, GEN T, ulong p, GEN *zetan);
+ulong   Flxq_trace(GEN x, GEN T, ulong p);
+GEN     FlxqV_dotproduct(GEN x, GEN y, GEN T, ulong p);
+GEN     FlxqV_roots_to_pol(GEN V, GEN T, ulong p, long v);
+GEN     FlxqX_FlxqXQ_eval(GEN Q, GEN x, GEN S, GEN T, ulong p);
+GEN     FlxqX_FlxqXQV_eval(GEN P, GEN V, GEN S, GEN T, ulong p);
+GEN     FlxqX_Flxq_mul(GEN P, GEN U, GEN T, ulong p);
+GEN     FlxqX_Flxq_mul_to_monic(GEN P, GEN U, GEN T, ulong p);
+GEN     FlxqX_divrem(GEN x, GEN y, GEN T, ulong p, GEN *pr);
+GEN     FlxqX_extgcd(GEN a, GEN b, GEN T, ulong p, GEN *ptu, GEN *ptv);
+GEN     FlxqX_gcd(GEN P, GEN Q, GEN T, ulong p);
+GEN     FlxqX_invBarrett(GEN T, GEN Q, ulong p);
+GEN     FlxqX_mul(GEN x, GEN y, GEN T, ulong p);
+GEN     FlxqX_normalize(GEN z, GEN T, ulong p);
+GEN     FlxqX_pow(GEN V, long n, GEN T, ulong p);
+GEN     FlxqX_red(GEN z, GEN T, ulong p);
+GEN     FlxqX_rem_Barrett(GEN x, GEN mg, GEN T, GEN Q, ulong p);
+GEN     FlxqX_safegcd(GEN P, GEN Q, GEN T, ulong p);
+GEN     FlxqX_sqr(GEN x, GEN T, ulong p);
+GEN     FlxqXQ_div(GEN x, GEN y, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_inv(GEN x, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_invsafe(GEN x, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_matrix_pow(GEN x, long n, long m, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_mul(GEN x, GEN y, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_pow(GEN x, GEN n, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_powers(GEN x, long n, GEN S, GEN T, ulong p);
+GEN     FlxqXQ_sqr(GEN x, GEN S, GEN T, ulong p);
+GEN     FlxqXQV_autpow(GEN x, long n, GEN S, GEN T, ulong p);
+GEN     FlxqXV_prod(GEN V, GEN T, ulong p);
+GEN     Kronecker_to_FlxqX(GEN z, GEN T, ulong p);
+ulong   Rg_to_F2(GEN x);
+ulong   Rg_to_Fl(GEN x, ulong p);
+GEN     Rg_to_Flxq(GEN x, GEN T, ulong p);
+GEN     RgX_to_Flx(GEN x, ulong p);
+GEN     Z_to_Flx(GEN x, ulong p, long v);
+GEN     ZX_to_Flx(GEN x, ulong p);
+GEN     ZXV_to_FlxV(GEN v, ulong p);
+GEN     ZXX_to_FlxX(GEN B, ulong p, long v);
+GEN     ZXXV_to_FlxXV(GEN V, ulong p, long v);
+GEN     gener_Flxq(GEN T, ulong p, GEN *o);
+long    get_Flx_degree(GEN T);
+GEN     get_Flx_mod(GEN T);
+long    get_Flx_var(GEN T);
+const struct bb_field *get_Flxq_field(void **E, GEN T, ulong p);
+GEN     pol1_FlxX(long v, long sv);
+GEN     polx_FlxX(long v, long sv);
+GEN     random_Flx(long d1, long v, ulong p);
+GEN     zxX_to_Kronecker(GEN P, GEN Q);
+
+/* FlxqE.c */
+
+GEN     Flxq_ellcard(GEN a4, GEN a6, GEN T, ulong p);
+GEN     Flxq_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, ulong p);
+GEN     Flxq_ellgroup(GEN a4, GEN a6, GEN N, GEN T, ulong p, GEN *pt_m);
+GEN     Flxq_ellj(GEN a4, GEN a6, GEN T, ulong p);
+GEN     FlxqE_add(GEN P, GEN Q, GEN a4, GEN T, ulong p);
+GEN     FlxqE_changepoint(GEN x, GEN ch, GEN T, ulong p);
+GEN     FlxqE_changepointinv(GEN x, GEN ch, GEN T, ulong p);
+GEN     FlxqE_dbl(GEN P, GEN a4, GEN T, ulong p);
+GEN     FlxqE_log(GEN a, GEN b, GEN o, GEN a4, GEN T, ulong p);
+GEN     FlxqE_mul(GEN P, GEN n, GEN a4, GEN T, ulong p);
+GEN     FlxqE_neg(GEN P, GEN T, ulong p);
+GEN     FlxqE_order(GEN z, GEN o, GEN a4, GEN T, ulong p);
+GEN     FlxqE_sub(GEN P, GEN Q, GEN a4, GEN T, ulong p);
+GEN     FlxqE_tatepairing(GEN t, GEN s, GEN m, GEN a4, GEN T, ulong p);
+GEN     FlxqE_weilpairing(GEN t, GEN s, GEN m, GEN a4, GEN T, ulong p);
+const struct bb_group * get_FlxqE_group(void **E, GEN a4, GEN a6, GEN T, ulong p);
+GEN     RgE_to_FlxqE(GEN x, GEN T, ulong p);
+GEN     random_FlxqE(GEN a4, GEN a6, GEN T, ulong p);
+
+/* FpE.c */
+
+long    Fl_elltrace(ulong a4, ulong a6, ulong p);
+GEN     Fle_add(GEN P, GEN Q, ulong a4, ulong p);
+GEN     Fle_dbl(GEN P, ulong a4, ulong p);
+GEN     Fle_mul(GEN P, GEN n, ulong a4, ulong p);
+GEN     Fle_mulu(GEN P, ulong n, ulong a4, ulong p);
+GEN     Fle_order(GEN z, GEN o, ulong a4, ulong p);
+GEN     Fle_sub(GEN P, GEN Q, ulong a4, ulong p);
+GEN     Fp_ellcard(GEN a4, GEN a6, GEN p);
+GEN     Fp_elldivpol(GEN a4, GEN a6, long n, GEN p);
+GEN     Fp_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN p);
+GEN     Fp_ellgroup(GEN a4, GEN a6, GEN N, GEN p, GEN *pt_m);
+GEN     Fp_ellj(GEN a4, GEN a6, GEN p);
+GEN     Fp_ffellcard(GEN a4, GEN a6, GEN q, long n, GEN p);
+GEN     FpE_add(GEN P, GEN Q, GEN a4, GEN p);
+GEN     FpE_changepoint(GEN x, GEN ch, GEN p);
+GEN     FpE_changepointinv(GEN x, GEN ch, GEN p);
+GEN     FpE_dbl(GEN P, GEN a4, GEN p);
+GEN     FpE_log(GEN a, GEN b, GEN o, GEN a4, GEN p);
+GEN     FpE_mul(GEN P, GEN n, GEN a4, GEN p);
+GEN     FpE_neg(GEN P, GEN p);
+GEN     FpE_order(GEN z, GEN o, GEN a4, GEN p);
+GEN     FpE_sub(GEN P, GEN Q, GEN a4, GEN p);
+GEN     FpE_to_mod(GEN P, GEN p);
+GEN     FpE_tatepairing(GEN t, GEN s, GEN m, GEN a4, GEN p);
+GEN     FpE_weilpairing(GEN t, GEN s, GEN m, GEN a4, GEN p);
+GEN     FpXQ_ellcard(GEN a4, GEN a6, GEN T, GEN p);
+GEN     FpXQ_elldivpol(GEN a4, GEN a6, long n, GEN T, GEN p);
+GEN     FpXQ_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, GEN p);
+GEN     FpXQ_ellgroup(GEN a4, GEN a6, GEN N, GEN T, GEN p, GEN *pt_m);
+GEN     FpXQ_ellj(GEN a4, GEN a6, GEN T, GEN p);
+GEN     FpXQE_add(GEN P, GEN Q, GEN a4, GEN T, GEN p);
+GEN     FpXQE_changepoint(GEN x, GEN ch, GEN T, GEN p);
+GEN     FpXQE_changepointinv(GEN x, GEN ch, GEN T, GEN p);
+GEN     FpXQE_dbl(GEN P, GEN a4, GEN T, GEN p);
+GEN     FpXQE_log(GEN a, GEN b, GEN o, GEN a4, GEN T, GEN p);
+GEN     FpXQE_mul(GEN P, GEN n, GEN a4, GEN T, GEN p);
+GEN     FpXQE_neg(GEN P, GEN T, GEN p);
+GEN     FpXQE_order(GEN z, GEN o, GEN a4, GEN T, GEN p);
+GEN     FpXQE_sub(GEN P, GEN Q, GEN a4, GEN T, GEN p);
+GEN     FpXQE_tatepairing(GEN t, GEN s, GEN m, GEN a4, GEN T, GEN p);
+GEN     FpXQE_weilpairing(GEN t, GEN s, GEN m, GEN a4, GEN T, GEN p);
+GEN     Fq_elldivpolmod(GEN a4, GEN a6, long n, GEN h, GEN T, GEN p);
+GEN     RgE_to_FpE(GEN x, GEN p);
+GEN     RgE_to_FpXQE(GEN x, GEN T, GEN p);
+const struct bb_group * get_FpE_group(void **E, GEN a4, GEN a6, GEN p);
+const struct bb_group * get_FpXQE_group(void **E, GEN a4, GEN a6, GEN T, GEN p);
+GEN     elltrace_extension(GEN t, long n, GEN p);
+GEN     random_Fle(ulong a4, ulong a6, ulong p);
+GEN     random_FpE(GEN a4, GEN a6, GEN p);
+GEN     random_FpXQE(GEN a4, GEN a6, GEN T, GEN p);
+
+/* FpX.c */
+
+int     Fp_issquare(GEN x, GEN p);
+GEN     Fp_FpX_sub(GEN x, GEN y, GEN p);
+GEN     Fp_FpXQ_log(GEN a, GEN g, GEN ord, GEN T, GEN p);
+GEN     FpV_inv(GEN x, GEN p);
+GEN     FpV_roots_to_pol(GEN V, GEN p, long v);
+GEN     FpX_Fp_add(GEN x, GEN y, GEN p);
+GEN     FpX_Fp_add_shallow(GEN y,GEN x,GEN p);
+GEN     FpX_Fp_mul(GEN x, GEN y, GEN p);
+GEN     FpX_Fp_mul_to_monic(GEN y,GEN x,GEN p);
+GEN     FpX_Fp_mulspec(GEN y,GEN x,GEN p,long ly);
+GEN     FpX_Fp_sub(GEN x, GEN y, GEN p);
+GEN     FpX_Fp_sub_shallow(GEN y,GEN x,GEN p);
+GEN     FpX_FpXQ_eval(GEN f,GEN x,GEN T,GEN p);
+GEN     FpX_FpXQV_eval(GEN f,GEN x,GEN T,GEN p);
+GEN     FpX_add(GEN x, GEN y, GEN p);
+GEN     FpX_center(GEN x, GEN p, GEN pov2);
+GEN     FpX_chinese_coprime(GEN x,GEN y,GEN Tx,GEN Ty,GEN Tz,GEN p);
+GEN     FpX_deriv(GEN x, GEN p);
+GEN     FpX_disc(GEN x, GEN p);
+GEN     FpX_div_by_X_x(GEN a, GEN x, GEN p, GEN *r);
+GEN     FpX_divrem(GEN x, GEN y, GEN p, GEN *pr);
+GEN     FpX_eval(GEN x,GEN y,GEN p);
+GEN     FpX_extgcd(GEN x, GEN y, GEN p, GEN *ptu, GEN *ptv);
+GEN     FpX_gcd(GEN x, GEN y, GEN p);
+GEN     FpX_get_red(GEN T, GEN p);
+GEN     FpX_halfgcd(GEN x, GEN y, GEN p);
+GEN     FpX_invBarrett(GEN T, GEN p);
+int     FpX_is_squarefree(GEN f, GEN p);
+GEN     FpX_mul(GEN x, GEN y, GEN p);
+GEN     FpX_mulspec(GEN a, GEN b, GEN p, long na, long nb);
+GEN     FpX_mulu(GEN x, ulong y, GEN p);
+GEN     FpX_neg(GEN x, GEN p);
+GEN     FpX_normalize(GEN z, GEN p);
+GEN     FpX_red(GEN z, GEN p);
+GEN     FpX_rem(GEN x, GEN y, GEN p);
+GEN     FpX_rescale(GEN P, GEN h, GEN p);
+GEN     FpX_resultant(GEN a, GEN b, GEN p);
+GEN     FpX_sqr(GEN x, GEN p);
+GEN     FpX_sub(GEN x, GEN y, GEN p);
+long    FpX_valrem(GEN x0, GEN t, GEN p, GEN *py);
+GEN     FpXQ_autpow(GEN x, ulong n, GEN T, GEN p);
+GEN     FpXQ_autpowers(GEN aut, long f, GEN T, GEN p);
+GEN     FpXQ_autsum(GEN x, ulong n, GEN T, GEN p);
+GEN     FpXQ_charpoly(GEN x, GEN T, GEN p);
+GEN     FpXQ_conjvec(GEN x, GEN T, GEN p);
+GEN     FpXQ_div(GEN x,GEN y,GEN T,GEN p);
+GEN     FpXQ_inv(GEN x,GEN T,GEN p);
+GEN     FpXQ_invsafe(GEN x, GEN T, GEN p);
+int     FpXQ_issquare(GEN x, GEN T, GEN p);
+GEN     FpXQ_log(GEN a, GEN g, GEN ord, GEN T, GEN p);
+GEN     FpXQ_matrix_pow(GEN y, long n, long m, GEN P, GEN l);
+GEN     FpXQ_minpoly(GEN x, GEN T, GEN p);
+GEN     FpXQ_mul(GEN y,GEN x,GEN T,GEN p);
+GEN     FpXQ_norm(GEN x, GEN T, GEN p);
+GEN     FpXQ_order(GEN a, GEN ord, GEN T, GEN p);
+GEN     FpXQ_pow(GEN x, GEN n, GEN T, GEN p);
+GEN     FpXQ_powu(GEN x, ulong n, GEN T, GEN p);
+GEN     FpXQ_powers(GEN x, long l, GEN T, GEN p);
+GEN     FpXQ_red(GEN x, GEN T, GEN p);
+GEN     FpXQ_sqr(GEN y, GEN T, GEN p);
+GEN     FpXQ_sqrt(GEN a, GEN T, GEN p);
+GEN     FpXQ_sqrtn(GEN a, GEN n, GEN T, GEN p, GEN *zetan);
+GEN     FpXQ_trace(GEN x, GEN T, GEN p);
+GEN     FpXQC_to_mod(GEN z, GEN T, GEN p);
+GEN     FpXT_red(GEN z, GEN p);
+GEN     FpXV_prod(GEN V, GEN p);
+GEN     FpXV_red(GEN z, GEN p);
+int     Fq_issquare(GEN x, GEN T, GEN p);
+GEN     FqV_inv(GEN x, GEN T, GEN p);
+GEN     Z_to_FpX(GEN a, GEN p, long v);
+GEN     gener_FpXQ(GEN T, GEN p, GEN *o);
+GEN     gener_FpXQ_local(GEN T, GEN p, GEN L);
+long    get_FpX_degree(GEN T);
+GEN     get_FpX_mod(GEN T);
+long    get_FpX_var(GEN T);
+const struct bb_group *get_FpXQ_star(void **E, GEN T, GEN p);
+GEN     random_FpX(long d, long v, GEN p);
+
+/* FpX_factor.c */
+
+GEN     F2x_factor(GEN f);
+int     F2x_is_irred(GEN f);
+void    F2xV_to_FlxV_inplace(GEN v);
+void    F2xV_to_ZXV_inplace(GEN v);
+int     Flx_is_irred(GEN f, ulong p);
+GEN     Flx_degfact(GEN f, ulong p);
+GEN     Flx_factor(GEN f, ulong p);
+long    Flx_nbfact(GEN z, ulong p);
+GEN     Flx_nbfact_by_degree(GEN z, long *nb, ulong p);
+long    Flx_nbroots(GEN f, ulong p);
+ulong   Flx_oneroot(GEN f, ulong p);
+GEN     Flx_roots(GEN f, ulong p);
+GEN     FlxqX_Frobenius(GEN S, GEN T, ulong p);
+GEN     FlxqXQ_halfFrobenius(GEN a, GEN S, GEN T, ulong p);
+long    FlxqX_nbroots(GEN f, GEN T, ulong p);
+void    FlxV_to_ZXV_inplace(GEN v);
+GEN     FpX_degfact(GEN f, GEN p);
+int     FpX_is_irred(GEN f, GEN p);
+int     FpX_is_totally_split(GEN f, GEN p);
+GEN     FpX_factor(GEN f, GEN p);
+GEN     FpX_factorff(GEN P, GEN T, GEN p);
+long    FpX_nbfact(GEN f, GEN p);
+long    FpX_nbroots(GEN f, GEN p);
+GEN     FpX_oneroot(GEN f, GEN p);
+GEN     FpX_roots(GEN f, GEN p);
+GEN     FpX_rootsff(GEN P, GEN T, GEN p);
+GEN     FpXQX_Frobenius(GEN S, GEN T, GEN p);
+long    FpXQX_nbfact(GEN u, GEN T, GEN p);
+long    FpXQX_nbroots(GEN f, GEN T, GEN p);
+GEN     FpXQXQ_halfFrobenius(GEN a, GEN S, GEN T, GEN p);
+GEN     FqX_deriv(GEN f, GEN T, GEN p);
+GEN     FqX_factor(GEN x, GEN T, GEN p);
+long    FqX_is_squarefree(GEN P, GEN T, GEN p);
+long    FqX_nbfact(GEN u, GEN T, GEN p);
+long    FqX_nbroots(GEN f, GEN T, GEN p);
+GEN     FqX_roots(GEN f, GEN T, GEN p);
+GEN     factcantor(GEN x, GEN p);
+GEN     factorff(GEN f, GEN p, GEN a);
+GEN     factormod0(GEN f, GEN p,long flag);
+GEN     polrootsff(GEN f, GEN p, GEN T);
+GEN     rootmod0(GEN f, GEN p,long flag);
+
+/* FpXX.c */
+
+GEN     FpXQX_FpXQ_mul(GEN P, GEN U, GEN T, GEN p);
+GEN     FpXQX_FpXQXQV_eval(GEN P, GEN V, GEN S, GEN T, GEN p);
+GEN     FpXQX_FpXQXQ_eval(GEN P, GEN x, GEN S, GEN T, GEN p);
+GEN     FpXQX_divrem(GEN x, GEN y, GEN T, GEN p, GEN *pr);
+GEN     FpXQX_divrem_Barrett(GEN x, GEN B, GEN S, GEN T, GEN p, GEN *pr);
+GEN     FpXQX_extgcd(GEN x, GEN y, GEN T, GEN p, GEN *ptu, GEN *ptv);
+GEN     FpXQX_gcd(GEN P, GEN Q, GEN T, GEN p);
+GEN     FpXQX_invBarrett(GEN S, GEN T, GEN p);
+GEN     FpXQX_mul(GEN x, GEN y, GEN T, GEN p);
+GEN     FpXQX_red(GEN z, GEN T, GEN p);
+GEN     FpXQX_rem(GEN x, GEN S, GEN T, GEN p);
+GEN     FpXQX_rem_Barrett(GEN x, GEN mg, GEN S, GEN T, GEN p);
+GEN     FpXQX_sqr(GEN x, GEN T, GEN p);
+GEN     FpXQXQ_div(GEN x,GEN y,GEN S, GEN T,GEN p);
+GEN     FpXQXQ_inv(GEN x, GEN S, GEN T,GEN p);
+GEN     FpXQXQ_invsafe(GEN x, GEN S, GEN T, GEN p);
+GEN     FpXQXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, GEN p);
+GEN     FpXQXQ_mul(GEN x, GEN y, GEN S, GEN T, GEN p);
+GEN     FpXQXQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p);
+GEN     FpXQXQ_powers(GEN x, long n, GEN S, GEN T, GEN p);
+GEN     FpXQXQ_sqr(GEN x, GEN S, GEN T, GEN p);
+GEN     FpXQXQV_autpow(GEN aut, long n, GEN S, GEN T, GEN p);
+GEN     FpXQXQV_autsum(GEN aut, long n, GEN S, GEN T, GEN p);
+GEN     FpXQXV_prod(GEN V, GEN Tp, GEN p);
+GEN     FpXX_Fp_mul(GEN x, GEN y, GEN p);
+GEN     FpXX_FpX_mul(GEN x, GEN y, GEN p);
+GEN     FpXX_add(GEN x, GEN y, GEN p);
+GEN     FpXX_mulu(GEN P, ulong u, GEN p);
+GEN     FpXX_neg(GEN x, GEN p);
+GEN     FpXX_red(GEN z, GEN p);
+GEN     FpXX_sub(GEN x, GEN y, GEN p);
+GEN     FpXY_FpXQ_evalx(GEN P, GEN x, GEN T, GEN p);
+GEN     FpXY_eval(GEN Q, GEN y, GEN x, GEN p);
+GEN     FpXY_evalx(GEN Q, GEN x, GEN p);
+GEN     FpXY_evaly(GEN Q, GEN y, GEN p, long vy);
+GEN     FpXYQQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p);
+GEN     Kronecker_to_FpXQX(GEN z, GEN pol, GEN p);
+GEN     Kronecker_to_ZXX(GEN z, long N, long v);
+GEN     ZXX_mul_Kronecker(GEN x, GEN y, long n);
+
+/* FpV.c */
+
+GEN     F2m_F2c_mul(GEN x, GEN y);
+GEN     F2m_mul(GEN x, GEN y);
+GEN     F2m_powu(GEN x, ulong n);
+GEN     Flc_Fl_div(GEN x, ulong y, ulong p);
+void    Flc_Fl_div_inplace(GEN x, ulong y, ulong p);
+GEN     Flc_Fl_mul(GEN x, ulong y, ulong p);
+void    Flc_Fl_mul_inplace(GEN x, ulong y, ulong p);
+void    Flc_Fl_mul_part_inplace(GEN x, ulong y, ulong p, long l);
+GEN     Flc_to_mod(GEN z, ulong pp);
+GEN     Flm_Fl_add(GEN x, ulong y, ulong p);
+GEN     Flm_Fl_mul(GEN y, ulong x, ulong p);
+void    Flm_Fl_mul_inplace(GEN y, ulong x, ulong p);
+GEN     Flm_Flc_mul(GEN x, GEN y, ulong p);
+GEN     Flm_center(GEN z, ulong p, ulong ps2);
+GEN     Flm_mul(GEN x, GEN y, ulong p);
+GEN     Flm_neg(GEN y, ulong p);
+GEN     Flm_powu(GEN x, ulong n, ulong p);
+GEN     Flm_to_mod(GEN z, ulong pp);
+GEN     Flm_transpose(GEN x);
+GEN     Flv_add(GEN x, GEN y, ulong p);
+void    Flv_add_inplace(GEN x, GEN y, ulong p);
+ulong   Flv_dotproduct(GEN x, GEN y, ulong p);
+GEN     Flv_center(GEN z, ulong p, ulong ps2);
+GEN     Flv_sub(GEN x, GEN y, ulong p);
+void    Flv_sub_inplace(GEN x, GEN y, ulong p);
+ulong   Flv_sum(GEN x, ulong p);
+GEN     Fp_to_mod(GEN z, GEN p);
+GEN     FpC_FpV_mul(GEN x, GEN y, GEN p);
+GEN     FpC_Fp_mul(GEN x, GEN y, GEN p);
+GEN     FpC_center(GEN z, GEN p, GEN pov2);
+GEN     FpC_red(GEN z, GEN p);
+GEN     FpC_to_mod(GEN z, GEN p);
+GEN     FpM_FpC_mul(GEN x, GEN y, GEN p);
+GEN     FpM_FpC_mul_FpX(GEN x, GEN y, GEN p, long v);
+GEN     FpM_center(GEN z, GEN p, GEN pov2);
+GEN     FpM_mul(GEN x, GEN y, GEN p);
+GEN     FpM_powu(GEN x, ulong n, GEN p);
+GEN     FpM_red(GEN z, GEN p);
+GEN     FpM_to_mod(GEN z, GEN p);
+GEN     FpMs_FpC_mul(GEN M, GEN B, GEN p);
+GEN     FpMs_FpCs_solve(GEN M, GEN B, long nbrow, GEN p);
+GEN     FpMs_FpCs_solve_safe(GEN M, GEN A, long nbrow, GEN p);
+GEN     FpMs_leftkernel_elt(GEN M, long nbrow, GEN p);
+GEN     FpC_add(GEN x, GEN y, GEN p);
+GEN     FpC_sub(GEN x, GEN y, GEN p);
+GEN     FpV_FpMs_mul(GEN B, GEN M, GEN p);
+GEN     FpV_add(GEN x, GEN y, GEN p);
+GEN     FpV_sub(GEN x, GEN y, GEN p);
+GEN     FpV_dotproduct(GEN x, GEN y, GEN p);
+GEN     FpV_dotsquare(GEN x, GEN p);
+GEN     FpV_red(GEN z, GEN p);
+GEN     FpV_to_mod(GEN z, GEN p);
+GEN     FpVV_to_mod(GEN z, GEN p);
+GEN     FpX_to_mod(GEN z, GEN p);
+GEN     ZV_zMs_mul(GEN B, GEN M);
+GEN     ZpMs_ZpCs_solve(GEN M, GEN B, long nbrow, GEN p, long e);
+GEN     gen_FpM_Wiedemann(void *E, GEN (*f)(void*, GEN), GEN B, GEN p);
+GEN     gen_ZpM_Dixon(void *E, GEN (*f)(void*, GEN), GEN B, GEN p, long e);
+GEN     gen_matid(long n, void *E, const struct bb_field *S);
+GEN     matid_F2m(long n);
+GEN     matid_Flm(long n);
+GEN     matid_F2xqM(long n, GEN T);
+GEN     matid_FlxqM(long n, GEN T, ulong p);
+GEN     scalar_Flm(long s, long n);
+GEN     zCs_to_ZC(GEN C, long nbrow);
+GEN     zMs_to_ZM(GEN M, long nbrow);
+GEN     zMs_ZC_mul(GEN M, GEN B);
+
+/* Hensel.c */
+
+GEN     Zp_sqrtlift(GEN b, GEN a, GEN p, long e);
+GEN     Zp_sqrtnlift(GEN b, GEN n, GEN a, GEN p, long e);
+GEN     ZpX_liftfact(GEN pol, GEN Q, GEN T, GEN p, long e, GEN pe);
+GEN     ZpX_liftroot(GEN f, GEN a, GEN p, long e);
+GEN     ZpX_liftroots(GEN f, GEN S, GEN q, long e);
+GEN     ZpXQ_inv(GEN a, GEN T, GEN p, long e);
+GEN     ZpXQ_invlift(GEN b, GEN a, GEN T, GEN p, long e);
+GEN     ZpXQ_log(GEN a, GEN T, GEN p, long N);
+GEN     ZpXQ_sqrtnlift(GEN b, GEN n, GEN a, GEN T, GEN p, long e);
+GEN     ZpXQX_liftroot(GEN f, GEN a, GEN T, GEN p, long e);
+GEN     ZpXQX_liftroot_vald(GEN f, GEN a, long v, GEN T, GEN p, long e);
+GEN     gen_ZpX_Dixon(GEN F, GEN V, GEN q, GEN p, long N, void *E,
+                             GEN lin(void *E, GEN F, GEN d, GEN q),
+                             GEN invl(void *E, GEN d));
+GEN     gen_ZpX_Newton(GEN x, GEN p, long n, void *E,
+                              GEN eval(void *E, GEN f, GEN q),
+                              GEN invd(void *E, GEN V, GEN v, GEN q, long M));
+GEN     polhensellift(GEN pol, GEN fct, GEN p, long exp);
+ulong   quadratic_prec_mask(long n);
+
+/* QX_factor.c */
+
+GEN     QX_factor(GEN x);
+GEN     ZX_factor(GEN x);
+long    ZX_is_irred(GEN x);
+GEN     ZX_squff(GEN f, GEN *ex);
+GEN     polcyclofactors(GEN f);
+long    poliscyclo(GEN f);
+long    poliscycloprod(GEN f);
+
+
+/* RgV.c */
+
+GEN     RgC_Rg_add(GEN x, GEN y);
+GEN     RgC_Rg_div(GEN x, GEN y);
+GEN     RgC_Rg_mul(GEN x, GEN y);
+GEN     RgC_RgM_mul(GEN x, GEN y);
+GEN     RgC_RgV_mul(GEN x, GEN y);
+GEN     RgC_add(GEN x, GEN y);
+GEN     RgC_neg(GEN x);
+GEN     RgC_sub(GEN x, GEN y);
+GEN     RgM_Rg_add(GEN x, GEN y);
+GEN     RgM_Rg_add_shallow(GEN x, GEN y);
+GEN     RgM_Rg_div(GEN x, GEN y);
+GEN     RgM_Rg_mul(GEN x, GEN y);
+GEN     RgM_Rg_sub(GEN x, GEN y);
+GEN     RgM_Rg_sub_shallow(GEN x, GEN y);
+GEN     RgM_RgC_mul(GEN x, GEN y);
+GEN     RgM_RgV_mul(GEN x, GEN y);
+GEN     RgM_add(GEN x, GEN y);
+GEN     RgM_det_triangular(GEN x);
+int     RgM_is_ZM(GEN x);
+int     RgM_isdiagonal(GEN x);
+int     RgM_isidentity(GEN x);
+int     RgM_isscalar(GEN x, GEN s);
+GEN     RgM_mul(GEN x, GEN y);
+GEN     RgM_multosym(GEN x, GEN y);
+GEN     RgM_neg(GEN x);
+GEN     RgM_powers(GEN x, long l);
+GEN     RgM_sqr(GEN x);
+GEN     RgM_sub(GEN x, GEN y);
+GEN     RgM_transmul(GEN x, GEN y);
+GEN     RgM_transmultosym(GEN x, GEN y);
+GEN     RgM_zc_mul(GEN x, GEN y);
+GEN     RgM_zm_mul(GEN x, GEN y);
+GEN     RgMrow_RgC_mul(GEN x, GEN y, long i);
+GEN     RgV_RgM_mul(GEN x, GEN y);
+GEN     RgV_RgC_mul(GEN x, GEN y);
+GEN     RgV_Rg_mul(GEN x, GEN y);
+GEN     RgV_add(GEN x, GEN y);
+GEN     RgV_dotproduct(GEN x, GEN y);
+GEN     RgV_dotsquare(GEN x);
+int     RgV_is_ZMV(GEN V);
+long    RgV_isin(GEN v, GEN x);
+GEN     RgV_neg(GEN x);
+GEN     RgV_sub(GEN x, GEN y);
+GEN     RgV_sum(GEN v);
+GEN     RgV_sumpart(GEN v, long n);
+GEN     RgV_sumpart2(GEN v, long m, long n);
+GEN     RgV_zc_mul(GEN x, GEN y);
+GEN     RgV_zm_mul(GEN x, GEN y);
+GEN     RgX_RgM_eval(GEN x, GEN y);
+GEN     RgX_RgMV_eval(GEN x, GEN y);
+int     isdiagonal(GEN x);
+GEN     matid(long n);
+GEN     scalarcol(GEN x, long n);
+GEN     scalarcol_shallow(GEN x, long n);
+GEN     scalarmat(GEN x, long n);
+GEN     scalarmat_shallow(GEN x, long n);
+GEN     scalarmat_s(long x, long n);
+
+/* RgX.c */
+
+GEN     Kronecker_to_mod(GEN z, GEN pol);
+GEN     QX_ZXQV_eval(GEN P, GEN V, GEN dV);
+GEN     QXQ_powers(GEN a, long n, GEN T);
+GEN     QXQX_to_mod_shallow(GEN z, GEN T);
+GEN     QXQV_to_mod(GEN V, GEN T);
+GEN     QXQXV_to_mod(GEN V, GEN T);
+GEN     QXV_QXQ_eval(GEN v, GEN a, GEN T);
+GEN     QXX_QXQ_eval(GEN v, GEN a, GEN T);
+GEN     Rg_to_RgV(GEN x, long N);
+GEN     RgM_to_RgXV(GEN x, long v);
+GEN     RgM_to_RgXX(GEN x, long v,long w);
+GEN     RgV_to_RgX(GEN x, long v);
+GEN     RgV_to_RgM(GEN v, long n);
+GEN     RgV_to_RgX_reverse(GEN x, long v);
+GEN     RgXQC_red(GEN P, GEN T);
+GEN     RgXQV_red(GEN P, GEN T);
+GEN     RgXQX_RgXQ_mul(GEN x, GEN y, GEN T);
+GEN     RgXQX_divrem(GEN x,GEN y,GEN T,GEN *r);
+GEN     RgXQX_mul(GEN x,GEN y,GEN T);
+GEN     RgXQX_pseudodivrem(GEN x, GEN y, GEN T, GEN *ptr);
+GEN     RgXQX_pseudorem(GEN x, GEN y, GEN T);
+GEN     RgXQX_red(GEN P, GEN T);
+GEN     RgXQX_sqr(GEN x, GEN T);
+GEN     RgXQX_translate(GEN P, GEN c, GEN T);
+GEN     RgXQ_matrix_pow(GEN y, long n, long m, GEN P);
+GEN     RgXQ_norm(GEN x, GEN T);
+GEN     RgXQ_pow(GEN x, GEN n, GEN T);
+GEN     RgXQ_powu(GEN x, ulong n, GEN T);
+GEN     RgXQ_powers(GEN x, long l, GEN T);
+GEN     RgXV_to_RgM(GEN v, long n);
+GEN     RgXV_unscale(GEN v, GEN h);
+GEN     RgXX_to_RgM(GEN v, long n);
+GEN     RgXY_swap(GEN x, long n, long w);
+GEN     RgXY_swapspec(GEN x, long n, long w, long nx);
+GEN     RgX_RgXQ_eval(GEN f, GEN x, GEN T);
+GEN     RgX_RgXQV_eval(GEN P, GEN V, GEN T);
+GEN     RgX_Rg_add(GEN y, GEN x);
+GEN     RgX_Rg_add_shallow(GEN y, GEN x);
+GEN     RgX_Rg_div(GEN y, GEN x);
+GEN     RgX_Rg_divexact(GEN x, GEN y);
+GEN     RgX_Rg_mul(GEN y, GEN x);
+GEN     RgX_Rg_sub(GEN y, GEN x);
+GEN     RgX_add(GEN x, GEN y);
+GEN     RgX_blocks(GEN P, long n, long m);
+GEN     RgX_deflate(GEN x0, long d);
+GEN     RgX_deriv(GEN x);
+GEN     RgX_div_by_X_x(GEN a, GEN x, GEN *r);
+GEN     RgX_divrem(GEN x,GEN y,GEN *r);
+GEN     RgX_divs(GEN y, long x);
+long    RgX_equal(GEN x, GEN y);
+void    RgX_even_odd(GEN p, GEN *pe, GEN *po);
+GEN     RgX_get_0(GEN x);
+GEN     RgX_get_1(GEN x);
+GEN     RgX_inflate(GEN x0, long d);
+GEN     RgX_modXn_shallow(GEN a, long n);
+GEN     RgX_modXn_eval(GEN Q, GEN x, long n);
+GEN     RgX_mul(GEN x,GEN y);
+GEN     RgX_mul_normalized(GEN A, long a, GEN B, long b);
+GEN     RgX_mulXn(GEN x, long d);
+GEN     RgX_mullow(GEN f, GEN g, long n);
+GEN     RgX_muls(GEN y, long x);
+GEN     RgX_mulspec(GEN a, GEN b, long na, long nb);
+GEN     RgX_neg(GEN x);
+GEN     RgX_pseudodivrem(GEN x, GEN y, GEN *ptr);
+GEN     RgX_pseudorem(GEN x, GEN y);
+GEN     RgX_recip(GEN x);
+GEN     RgX_recip_shallow(GEN x);
+GEN     RgX_renormalize_lg(GEN x, long lx);
+GEN     RgX_rescale(GEN P, GEN h);
+GEN     RgX_rotate_shallow(GEN P, long k, long p);
+GEN     RgX_shift(GEN a, long n);
+GEN     RgX_shift_shallow(GEN x, long n);
+GEN     RgX_splitting(GEN p, long k);
+GEN     RgX_sqr(GEN x);
+GEN     RgX_sqrlow(GEN f, long n);
+GEN     RgX_sqrspec(GEN a, long na);
+GEN     RgX_sub(GEN x, GEN y);
+GEN     RgX_to_RgV(GEN x, long N);
+GEN     RgX_translate(GEN P, GEN c);
+GEN     RgX_unscale(GEN P, GEN h);
+GEN     Rg_RgX_sub(GEN x, GEN y);
+GEN     ZX_translate(GEN P, GEN c);
+GEN     ZX_unscale(GEN P, GEN h);
+GEN     ZX_unscale_div(GEN P, GEN h);
+int     ZXQX_dvd(GEN x, GEN y, GEN T);
+long    brent_kung_optpow(long d, long n, long m);
+GEN     gen_bkeval(GEN Q, long d, GEN x, int use_sqr, void *E,
+          const struct bb_algebra *ff, GEN cmul(void *E, GEN P, long a, GEN x));
+GEN     gen_bkeval_powers(GEN P, long d, GEN V, void *E,
+          const struct bb_algebra *ff, GEN cmul(void *E, GEN P, long a, GEN x));
+
+/* ZV.c */
+
+void    Flc_lincomb1_inplace(GEN X, GEN Y, ulong v, ulong q);
+void    RgM_check_ZM(GEN A, const char *s);
+void    RgV_check_ZV(GEN A, const char *s);
+GEN     ZC_ZV_mul(GEN x, GEN y);
+GEN     ZC_Z_add(GEN x, GEN y);
+GEN     ZC_Z_divexact(GEN X, GEN c);
+GEN     ZC_Z_mul(GEN X, GEN c);
+GEN     ZC_Z_sub(GEN x, GEN y);
+GEN     ZC_add(GEN x, GEN y);
+GEN     ZC_copy(GEN x);
+GEN     ZC_hnfremdiv(GEN x, GEN y, GEN *Q);
+GEN     ZC_lincomb(GEN u, GEN v, GEN X, GEN Y);
+void    ZC_lincomb1_inplace(GEN X, GEN Y, GEN v);
+GEN     ZC_neg(GEN M);
+GEN     ZC_reducemodlll(GEN x,GEN y);
+GEN     ZC_reducemodmatrix(GEN v, GEN y);
+GEN     ZC_sub(GEN x, GEN y);
+GEN     ZC_z_mul(GEN X, long c);
+GEN     ZM_ZC_mul(GEN x, GEN y);
+GEN     ZM_Z_divexact(GEN X, GEN c);
+GEN     ZM_Z_mul(GEN X, GEN c);
+GEN     ZM_add(GEN x, GEN y);
+GEN     ZM_copy(GEN x);
+GEN     ZM_det_triangular(GEN mat);
+int     ZM_equal(GEN A, GEN B);
+GEN     ZM_hnfdivrem(GEN x, GEN y, GEN *Q);
+int     ZM_ishnf(GEN x);
+int     ZM_isidentity(GEN x);
+long    ZM_max_lg(GEN x);
+GEN     ZM_mul(GEN x, GEN y);
+GEN     ZM_multosym(GEN x, GEN y);
+GEN     ZM_neg(GEN x);
+GEN     ZM_nm_mul(GEN x, GEN y);
+GEN     ZM_pow(GEN x, GEN n);
+GEN     ZM_powu(GEN x, ulong n);
+GEN     ZM_reducemodlll(GEN x,GEN y);
+GEN     ZM_reducemodmatrix(GEN v, GEN y);
+GEN     ZM_sub(GEN x, GEN y);
+GEN     ZM_supnorm(GEN x);
+GEN     ZM_to_Flm(GEN x, ulong p);
+GEN     ZM_to_zm(GEN z);
+GEN     ZM_transmultosym(GEN x, GEN y);
+GEN     ZMV_to_zmV(GEN z);
+void    ZM_togglesign(GEN M);
+GEN     ZM_zc_mul(GEN x, GEN y);
+GEN     ZM_zm_mul(GEN x, GEN y);
+GEN     ZMrow_ZC_mul(GEN x, GEN y, long i);
+GEN     ZV_ZM_mul(GEN x, GEN y);
+int     ZV_abscmp(GEN x, GEN y);
+int     ZV_cmp(GEN x, GEN y);
+GEN     ZV_content(GEN x);
+GEN     ZV_dotproduct(GEN x,GEN y);
+GEN     ZV_dotsquare(GEN x);
+int     ZV_equal(GEN V, GEN W);
+int     ZV_equal0(GEN V);
+long    ZV_max_lg(GEN x);
+void    ZV_neg_inplace(GEN M);
+GEN     ZV_prod(GEN v);
+GEN     ZV_sum(GEN v);
+GEN     ZV_to_Flv(GEN x, ulong p);
+GEN     ZV_to_nv(GEN z);
+void    ZV_togglesign(GEN M);
+GEN     gram_matrix(GEN M);
+GEN     nm_Z_mul(GEN X, GEN c);
+GEN     zm_mul(GEN x, GEN y);
+GEN     zm_to_Flm(GEN z, ulong p);
+GEN     zm_to_ZM(GEN z);
+GEN     zm_zc_mul(GEN x, GEN y);
+GEN     zmV_to_ZMV(GEN z);
+long    zv_content(GEN x);
+long    zv_dotproduct(GEN x, GEN y);
+int     zv_equal(GEN V, GEN W);
+int     zv_equal0(GEN V);
+GEN     zv_neg(GEN x);
+GEN     zv_neg_inplace(GEN M);
+long    zv_prod(GEN v);
+GEN     zv_prod_Z(GEN v);
+long    zv_sum(GEN v);
+GEN     zv_to_Flv(GEN z, ulong p);
+GEN     zv_z_mul(GEN v, long n);
+int     zvV_equal(GEN V, GEN W);
+
+/* ZX.c */
+
+void    RgX_check_QX(GEN x, const char *s);
+void    RgX_check_ZX(GEN x, const char *s);
+void    RgX_check_ZXX(GEN x, const char *s);
+GEN     Z_ZX_sub(GEN x, GEN y);
+GEN     ZX_Z_add(GEN y,GEN x);
+GEN     ZX_Z_add_shallow(GEN y, GEN x);
+GEN     ZX_Z_divexact(GEN y,GEN x);
+GEN     ZX_Z_mul(GEN y,GEN x);
+GEN     ZX_Z_sub(GEN y, GEN x);
+GEN     ZX_add(GEN x, GEN y);
+GEN     ZX_copy(GEN x);
+GEN     ZX_deriv(GEN x);
+int     ZX_equal(GEN V, GEN W);
+GEN     ZX_eval1(GEN x);
+long    ZX_max_lg(GEN x);
+GEN     ZX_mod_Xnm1(GEN T, ulong n);
+GEN     ZX_mul(GEN x, GEN y);
+GEN     ZX_mulspec(GEN a, GEN b, long na, long nb);
+GEN     ZX_mulu(GEN y, ulong x);
+GEN     ZX_neg(GEN x);
+GEN     ZX_rem(GEN x, GEN y);
+GEN     ZX_remi2n(GEN y, long n);
+GEN     ZX_rescale(GEN P, GEN h);
+GEN     ZX_rescale_lt(GEN P);
+GEN     ZX_shifti(GEN x, long n);
+GEN     ZX_sqr(GEN x);
+GEN     ZX_sqrspec(GEN a, long na);
+GEN     ZX_sub(GEN x, GEN y);
+long    ZX_val(GEN x);
+long    ZX_valrem(GEN x, GEN *Z);
+GEN     ZXT_remi2n(GEN z, long n);
+GEN     ZXV_Z_mul(GEN y, GEN x);
+GEN     ZXV_dotproduct(GEN V, GEN W);
+int     ZXV_equal(GEN V, GEN W);
+GEN     ZXV_remi2n(GEN x, long n);
+GEN     ZXX_Z_divexact(GEN y, GEN x);
+long    ZXX_max_lg(GEN x);
+GEN     ZXX_renormalize(GEN x, long lx);
+GEN     ZXX_to_Kronecker(GEN P, long n);
+GEN     ZXX_to_Kronecker_spec(GEN P, long lP, long n);
+GEN     scalar_ZX(GEN x, long v);
+GEN     scalar_ZX_shallow(GEN x, long v);
+GEN     zx_to_ZX(GEN z);
+
+/* alglin1.c */
+
+GEN     F2m_F2c_gauss(GEN a, GEN b);
+GEN     F2m_F2c_invimage(GEN A, GEN y);
+GEN     F2m_deplin(GEN x);
+ulong   F2m_det(GEN x);
+ulong   F2m_det_sp(GEN x);
+GEN     F2m_gauss(GEN a, GEN b);
+GEN     F2m_image(GEN x);
+GEN     F2m_indexrank(GEN x);
+GEN     F2m_inv(GEN x);
+GEN     F2m_invimage(GEN A, GEN B);
+GEN     F2m_ker(GEN x);
+GEN     F2m_ker_sp(GEN x, long deplin);
+long    F2m_rank(GEN x);
+GEN     F2m_suppl(GEN x);
+GEN     F2xqM_F2xqC_mul(GEN a, GEN b, GEN T);
+GEN     F2xqM_det(GEN a, GEN T);
+GEN     F2xqM_ker(GEN x, GEN T);
+GEN     F2xqM_image(GEN x, GEN T);
+GEN     F2xqM_inv(GEN a, GEN T);
+GEN     F2xqM_mul(GEN a, GEN b, GEN T);
+long    F2xqM_rank(GEN x, GEN T);
+GEN     Flm_Flc_gauss(GEN a, GEN b, ulong p);
+GEN     Flm_Flc_invimage(GEN mat, GEN y, ulong p);
+GEN     Flm_deplin(GEN x, ulong p);
+ulong   Flm_det(GEN x, ulong p);
+ulong   Flm_det_sp(GEN x, ulong p);
+GEN     Flm_gauss(GEN a, GEN b, ulong p);
+GEN     Flm_image(GEN x, ulong p);
+GEN     Flm_invimage(GEN m, GEN v, ulong p);
+GEN     Flm_indexrank(GEN x, ulong p);
+GEN     Flm_inv(GEN x, ulong p);
+GEN     Flm_ker(GEN x, ulong p);
+GEN     Flm_ker_sp(GEN x, ulong p, long deplin);
+long    Flm_rank(GEN x, ulong p);
+GEN     Flm_suppl(GEN x, ulong p);
+GEN     FlxqM_FlxqC_gauss(GEN a, GEN b, GEN T, ulong p);
+GEN     FlxqM_FlxqC_mul(GEN a, GEN b, GEN T, ulong p);
+GEN     FlxqM_det(GEN a, GEN T, ulong p);
+GEN     FlxqM_gauss(GEN a, GEN b, GEN T, ulong p);
+GEN     FlxqM_ker(GEN x, GEN T, ulong p);
+GEN     FlxqM_image(GEN x, GEN T, ulong p);
+GEN     FlxqM_inv(GEN x, GEN T, ulong p);
+GEN     FlxqM_mul(GEN a, GEN b, GEN T, ulong p);
+long    FlxqM_rank(GEN x, GEN T, ulong p);
+GEN     FpM_FpC_gauss(GEN a, GEN b, GEN p);
+GEN     FpM_FpC_invimage(GEN m, GEN v, GEN p);
+GEN     FpM_deplin(GEN x, GEN p);
+GEN     FpM_det(GEN x, GEN p);
+GEN     FpM_gauss(GEN a, GEN b, GEN p);
+GEN     FpM_image(GEN x, GEN p);
+GEN     FpM_indexrank(GEN x, GEN p);
+GEN     FpM_intersect(GEN x, GEN y, GEN p);
+GEN     FpM_inv(GEN x, GEN p);
+GEN     FpM_invimage(GEN m, GEN v, GEN p);
+GEN     FpM_ker(GEN x, GEN p);
+long    FpM_rank(GEN x, GEN p);
+GEN     FpM_suppl(GEN x, GEN p);
+GEN     FqM_FqC_gauss(GEN a, GEN b, GEN T, GEN p);
+GEN     FqM_FqC_mul(GEN a, GEN b, GEN T, GEN p);
+GEN     FqM_deplin(GEN x, GEN T, GEN p);
+GEN     FqM_det(GEN x, GEN T, GEN p);
+GEN     FqM_gauss(GEN a, GEN b, GEN T, GEN p);
+GEN     FqM_ker(GEN x, GEN T, GEN p);
+GEN     FqM_image(GEN x, GEN T, GEN p);
+GEN     FqM_inv(GEN x, GEN T, GEN p);
+GEN     FqM_mul(GEN a, GEN b, GEN T, GEN p);
+long    FqM_rank(GEN a, GEN T, GEN p);
+GEN     FqM_suppl(GEN x, GEN T, GEN p);
+GEN     QM_inv(GEN M, GEN dM);
+GEN     RgM_Fp_init(GEN a, GEN p, ulong *pp);
+GEN     RgM_RgC_invimage(GEN A, GEN B);
+GEN     RgM_diagonal(GEN m);
+GEN     RgM_diagonal_shallow(GEN m);
+GEN     RgM_Hadamard(GEN a);
+GEN     RgM_inv_upper(GEN a);
+GEN     RgM_invimage(GEN A, GEN B);
+GEN     RgM_solve(GEN a, GEN b);
+GEN     RgM_solve_realimag(GEN x, GEN y);
+void    RgMs_structelim(GEN M, long N, GEN A, GEN *p_col, GEN *p_lin);
+GEN     ZM_det(GEN a);
+GEN     ZM_detmult(GEN A);
+GEN     ZM_gauss(GEN a, GEN b);
+GEN     ZM_imagecompl(GEN x);
+GEN     ZM_indeximage(GEN x);
+GEN     ZM_inv(GEN M, GEN dM);
+long    ZM_rank(GEN x);
+GEN     ZlM_gauss(GEN a, GEN b, ulong p, long e, GEN C);
+GEN     closemodinvertible(GEN x, GEN y);
+GEN     deplin(GEN x);
+GEN     det(GEN a);
+GEN     det0(GEN a,long flag);
+GEN     det2(GEN a);
+GEN     detint(GEN x);
+GEN     eigen(GEN x, long prec);
+GEN     gauss(GEN a, GEN b);
+GEN     gaussmodulo(GEN M, GEN D, GEN Y);
+GEN     gaussmodulo2(GEN M, GEN D, GEN Y);
+GEN     gen_Gauss(GEN a, GEN b, void *E, const struct bb_field *ff);
+GEN     gen_Gauss_pivot(GEN x, long *rr, void *E, const struct bb_field *ff);
+GEN     gen_det(GEN a, void *E, const struct bb_field *ff);
+GEN     gen_ker(GEN x, long deplin, void *E, const struct bb_field *ff);
+GEN     gen_matcolmul(GEN a, GEN b, void *E, const struct bb_field *ff);
+GEN     gen_matmul(GEN a, GEN b, void *E, const struct bb_field *ff);
+GEN     image(GEN x);
+GEN     image2(GEN x);
+GEN     imagecompl(GEN x);
+GEN     indexrank(GEN x);
+GEN     inverseimage(GEN mat, GEN y);
+GEN     ker(GEN x);
+GEN     keri(GEN x);
+GEN     mateigen(GEN x, long flag, long prec);
+GEN     matimage0(GEN x,long flag);
+GEN     matker0(GEN x, long flag);
+GEN     matsolvemod0(GEN M, GEN D, GEN Y,long flag);
+long    rank(GEN x);
+GEN     reducemodinvertible(GEN x, GEN y);
+GEN     reducemodlll(GEN x,GEN y);
+GEN     split_realimag(GEN x, long r1, long r2);
+GEN     suppl(GEN x);
+
+/* alglin2.c */
+
+GEN     FpM_charpoly(GEN x, GEN p);
+GEN     FpM_hess(GEN x, GEN p);
+GEN     Flm_charpoly(GEN x, long p);
+GEN     Flm_hess(GEN x, ulong p);
+GEN     QM_minors_coprime(GEN x, GEN pp);
+GEN     QM_ImZ_hnf(GEN x);
+GEN     QM_ImQ_hnf(GEN x);
+GEN     gnorml1_fake(GEN x);
+GEN     ZM_charpoly(GEN x);
+GEN     adj(GEN x);
+GEN     adjsafe(GEN x);
+GEN     caract(GEN x, long v);
+GEN     caradj(GEN x, long v, GEN *py);
+GEN     carberkowitz(GEN x, long v);
+GEN     carhess(GEN x, long v);
+GEN     charpoly(GEN x, long v);
+GEN     charpoly0(GEN x, long v,long flag);
+GEN     gnorm(GEN x);
+GEN     gnorml1(GEN x,long prec);
+GEN     gnormlp(GEN x, GEN p, long prec);
+GEN     gnorml2(GEN x);
+GEN     gsupnorm(GEN x, long prec);
+void    gsupnorm_aux(GEN x, GEN *m, GEN *msq, long prec);
+GEN     gtrace(GEN x);
+GEN     hess(GEN x);
+GEN     intersect(GEN x, GEN y);
+GEN     jacobi(GEN a, long prec);
+GEN     matadjoint0(GEN x, long flag);
+GEN     matcompanion(GEN x);
+GEN     matrixqz0(GEN x, GEN pp);
+GEN     minpoly(GEN x, long v);
+GEN     qfgaussred(GEN a);
+GEN     qfgaussred_positive(GEN a);
+GEN     qfsign(GEN a);
+
+/* alglin3.c */
+
+GEN     apply0(GEN f, GEN A);
+GEN     diagonal(GEN x);
+GEN     diagonal_shallow(GEN x);
+GEN     extract0(GEN x, GEN l1, GEN l2);
+GEN     genapply(void *E, GEN (*f)(void *E, GEN x), GEN A);
+GEN     genindexselect(void *E, long (*f)(void *E, GEN x), GEN A);
+GEN     genselect(void *E, long (*f)(void *E, GEN x), GEN A);
+GEN     gtomat(GEN x);
+GEN     gtrans(GEN x);
+GEN     matmuldiagonal(GEN x, GEN d);
+GEN     matmultodiagonal(GEN x, GEN y);
+GEN     matslice0(GEN A, long x1, long x2, long y1, long y2);
+GEN     parapply(GEN V, GEN C);
+GEN     parselect(GEN C, GEN D, long flag);
+GEN     select0(GEN A, GEN f, long flag);
+GEN     shallowextract(GEN x, GEN L);
+GEN     shallowtrans(GEN x);
+GEN     vecapply(void *E, GEN (*f)(void* E, GEN x), GEN x);
+GEN     veccatapply(void *E, GEN (*f)(void* E, GEN x), GEN x);
+GEN     veccatselapply(void *Epred, long (*pred)(void* E, GEN x), void *Efun,
+                            GEN (*fun)(void* E, GEN x), GEN A);
+GEN     vecrange(GEN a, GEN b);
+GEN     vecrangess(long a, long b);
+GEN     vecselapply(void *Epred, long (*pred)(void* E, GEN x), void *Efun,
+                            GEN (*fun)(void* E, GEN x), GEN A);
+GEN     vecselect(void *E, long (*f)(void* E, GEN x), GEN A);
+GEN     vecslice0(GEN A, long y1, long y2);
+GEN     vecsum(GEN v);
+
+/* anal.c */
+
+void    addhelp(const char *e, char *s);
+void    alias0(const char *s, const char *old);
+GEN     compile_str(const char *s);
+GEN     chartoGENstr(char c);
+long    delete_var(void);
+entree* fetch_named_var(const char *s);
+long    fetch_user_var(const char *s);
+long    fetch_var(void);
+GEN     fetch_var_value(long vx, GEN t);
+GEN     gp_read_str(const char *t);
+entree* install(void *f, const char *name, const char *code);
+entree* is_entry(const char *s);
+void    kill0(const char *e);
+long    manage_var(long n, entree *ep);
+void    pari_var_init(void);
+long    pari_var_next(void);
+long    pari_var_next_temp(void);
+void    pari_var_create(entree *ep);
+void    name_var(long n, const char *s);
+GEN     readseq(char *t);
+GEN*    safegel(GEN x, long l);
+long*   safeel(GEN x, long l);
+GEN*    safelistel(GEN x, long l);
+GEN*    safegcoeff(GEN x, long a, long b);
+GEN     strntoGENstr(const char *s, long n0);
+GEN     strtoGENstr(const char *s);
+GEN     strtoi(const char *s);
+GEN     strtor(const char *s, long prec);
+GEN     type0(GEN x);
+
+/* aprcl.c */
+
+long    isprimeAPRCL(GEN N);
+
+/* Qfb.c */
+
+GEN     Qfb0(GEN x, GEN y, GEN z, GEN d, long prec);
+void    check_quaddisc(GEN x, long *s, long *r, const char *f);
+void    check_quaddisc_imag(GEN x, long *r, const char *f);
+void    check_quaddisc_real(GEN x, long *r, const char *f);
+long    cornacchia(GEN d, GEN p, GEN *px, GEN *py);
+long    cornacchia2(GEN d, GEN p, GEN *px, GEN *py);
+GEN     nucomp(GEN x, GEN y, GEN l);
+GEN     nudupl(GEN x, GEN l);
+GEN     nupow(GEN x, GEN n);
+GEN     primeform(GEN x, GEN p, long prec);
+GEN     primeform_u(GEN x, ulong p);
+GEN     qfbcompraw(GEN x, GEN y);
+GEN     qfbpowraw(GEN x, long n);
+GEN     qfbred0(GEN x, long flag, GEN D, GEN isqrtD, GEN sqrtD);
+GEN     qfbsolve(GEN Q, GEN n);
+GEN     qfi(GEN x, GEN y, GEN z);
+GEN     qfi_1(GEN x);
+GEN     qficomp(GEN x, GEN y);
+GEN     qficompraw(GEN x, GEN y);
+GEN     qfipowraw(GEN x, long n);
+GEN     qfisolvep(GEN Q, GEN p);
+GEN     qfisqr(GEN x);
+GEN     qfisqrraw(GEN x);
+GEN     qfr(GEN x, GEN y, GEN z, GEN d);
+GEN     qfr3_comp(GEN x, GEN y, struct qfr_data *S);
+GEN     qfr3_pow(GEN x, GEN n, struct qfr_data *S);
+GEN     qfr3_red(GEN x, struct qfr_data *S);
+GEN     qfr3_rho(GEN x, struct qfr_data *S);
+GEN     qfr3_to_qfr(GEN x, GEN z);
+GEN     qfr5_comp(GEN x, GEN y, struct qfr_data *S);
+GEN     qfr5_dist(GEN e, GEN d, long prec);
+GEN     qfr5_pow(GEN x, GEN n, struct qfr_data *S);
+GEN     qfr5_red(GEN x, struct qfr_data *S);
+GEN     qfr5_rho(GEN x, struct qfr_data *S);
+GEN     qfr5_to_qfr(GEN x, GEN d0);
+GEN     qfr_1(GEN x);
+void    qfr_data_init(GEN D, long prec, struct qfr_data *S);
+GEN     qfr_to_qfr5(GEN x, long prec);
+GEN     qfrcomp(GEN x, GEN y);
+GEN     qfrcompraw(GEN x, GEN y);
+GEN     qfrpow(GEN x, GEN n);
+GEN     qfrpowraw(GEN x, long n);
+GEN     qfrsolvep(GEN Q, GEN p);
+GEN     qfrsqr(GEN x);
+GEN     qfrsqrraw(GEN x);
+GEN     quadgen(GEN x);
+GEN     quadpoly(GEN x);
+GEN     quadpoly0(GEN x, long v);
+GEN     redimag(GEN x);
+GEN     redreal(GEN x);
+GEN     redrealnod(GEN x, GEN isqrtD);
+GEN     rhoreal(GEN x);
+GEN     rhorealnod(GEN x, GEN isqrtD);
+
+/* arith1.c */
+
+ulong   Fl_order(ulong a, ulong o, ulong p);
+ulong   Fl_powu(ulong x, ulong n, ulong p);
+ulong   Fl_sqrt(ulong a, ulong p);
+GEN     Fp_factored_order(GEN a, GEN o, GEN p);
+int     Fp_ispower(GEN x, GEN K, GEN p);
+GEN     Fp_log(GEN a, GEN g, GEN ord, GEN p);
+GEN     Fp_order(GEN a, GEN o, GEN p);
+GEN     Fp_pow(GEN a, GEN n, GEN m);
+GEN     Fp_pows(GEN A, long k, GEN N);
+GEN     Fp_powu(GEN x, ulong k, GEN p);
+GEN     Fp_sqrt(GEN a, GEN p);
+GEN     Fp_sqrtn(GEN a, GEN n, GEN p, GEN *zetan);
+GEN     Z_chinese(GEN a, GEN b, GEN A, GEN B);
+GEN     Z_chinese_all(GEN a, GEN b, GEN A, GEN B, GEN *pC);
+GEN     Z_chinese_coprime(GEN a, GEN b, GEN A, GEN B, GEN C);
+GEN     Z_chinese_post(GEN a, GEN b, GEN C, GEN U, GEN d);
+void    Z_chinese_pre(GEN A, GEN B, GEN *pC, GEN *pU, GEN *pd);
+GEN     Z_factor_listP(GEN N, GEN L);
+long    Z_isanypower(GEN x, GEN *y);
+long    Z_isfundamental(GEN x);
+long    Z_ispow2(GEN x);
+long    Z_ispowerall(GEN x, ulong k, GEN *pt);
+long    Z_issquareall(GEN x, GEN *pt);
+long    Zp_issquare(GEN a, GEN p);
+GEN     bestappr(GEN x, GEN k);
+GEN     bestapprPade(GEN x, long B);
+long    cgcd(long a,long b);
+GEN     chinese(GEN x, GEN y);
+GEN     chinese1(GEN x);
+GEN     chinese1_coprime_Z(GEN x);
+GEN     classno(GEN x);
+GEN     classno2(GEN x);
+long    clcm(long a,long b);
+GEN     contfrac0(GEN x, GEN b, long flag);
+GEN     contfracpnqn(GEN x, long n);
+GEN     fibo(long n);
+GEN     gboundcf(GEN x, long k);
+GEN     gcf(GEN x);
+GEN     gcf2(GEN b, GEN x);
+const struct bb_field *get_Fp_field(void **E, GEN p);
+ulong   pgener_Fl(ulong p);
+ulong   pgener_Fl_local(ulong p, GEN L);
+GEN     pgener_Fp(GEN p);
+GEN     pgener_Fp_local(GEN p, GEN L);
+ulong   pgener_Zl(ulong p);
+GEN     pgener_Zp(GEN p);
+long    gisanypower(GEN x, GEN *pty);
+GEN     gissquare(GEN x);
+GEN     gissquareall(GEN x, GEN *pt);
+GEN     hclassno(GEN x);
+long    hilbert(GEN x, GEN y, GEN p);
+long    hilbertii(GEN x, GEN y, GEN p);
+long    isfundamental(GEN x);
+long    ispolygonal(GEN x, GEN S, GEN *N);
+long    ispower(GEN x, GEN k, GEN *pty);
+long    isprimepower(GEN x, GEN *pty);
+long    issquare(GEN x);
+long    issquareall(GEN x, GEN *pt);
+long    krois(GEN x, long y);
+long    kroiu(GEN x, ulong y);
+long    kronecker(GEN x, GEN y);
+long    krosi(long s, GEN x);
+long    kross(long x, long y);
+long    krouu(ulong x, ulong y);
+GEN     lcmii(GEN a, GEN b);
+long    logint(GEN B, GEN y, GEN *ptq);
+long    logint0(GEN B, GEN y, GEN *ptq);
+GEN     mpfact(long n);
+GEN     mulu_interval(ulong a, ulong b);
+GEN     odd_prime_divisors(GEN q);
+GEN     order(GEN x);
+GEN     pnqn(GEN x);
+GEN     qfbclassno0(GEN x,long flag);
+GEN     quaddisc(GEN x);
+GEN     quadregulator(GEN x, long prec);
+GEN     quadunit(GEN x);
+ulong   rootsof1_Fl(ulong n, ulong p);
+GEN     rootsof1_Fp(GEN n, GEN p);
+GEN     rootsof1u_Fp(ulong n, GEN p);
+GEN     sqrtint(GEN a);
+ulong   ugcd(ulong a,ulong b);
+long    uisprimepower(ulong n, ulong *p);
+long    uissquare(ulong A);
+long    uissquareall(ulong A, ulong *sqrtA);
+long    unegisfundamental(ulong x);
+long    uposisfundamental(ulong x);
+GEN     znlog(GEN x, GEN g, GEN o);
+GEN     znorder(GEN x, GEN o);
+GEN     znprimroot(GEN m);
+GEN     znstar(GEN x);
+
+/* arith2.c */
+
+GEN     Z_smoothen(GEN N, GEN L, GEN *pP, GEN *pe);
+GEN     boundfact(GEN n, ulong lim);
+GEN     check_arith_pos(GEN n, const char *f);
+GEN     check_arith_non0(GEN n, const char *f);
+GEN     check_arith_all(GEN n, const char *f);
+GEN     clean_Z_factor(GEN f);
+GEN     corepartial(GEN n, long l);
+GEN     core0(GEN n,long flag);
+GEN     core2(GEN n);
+GEN     core2partial(GEN n, long l);
+GEN     coredisc(GEN n);
+GEN     coredisc0(GEN n,long flag);
+GEN     coredisc2(GEN n);
+GEN     digits(GEN N, GEN B);
+GEN     divisors(GEN n);
+GEN     divisorsu(ulong n);
+GEN     factor_pn_1(GEN p, ulong n);
+GEN     factor_pn_1_limit(GEN p, long n, ulong lim);
+GEN     factoru_pow(ulong n);
+byteptr initprimes(ulong maxnum, long *lenp, ulong *lastp);
+void    initprimetable(ulong maxnum);
+ulong   init_primepointer_geq(ulong a, byteptr *pd);
+ulong   init_primepointer_gt(ulong a, byteptr *pd);
+ulong   init_primepointer_leq(ulong a, byteptr *pd);
+ulong   init_primepointer_lt(ulong a, byteptr *pd);
+int     is_Z_factor(GEN f);
+int     is_Z_factornon0(GEN f);
+int     is_Z_factorpos(GEN f);
+ulong   maxprime(void);
+void    maxprime_check(ulong c);
+GEN     sumdigits(GEN n);
+ulong   sumdigitsu(ulong n);
+
+/* DedekZeta.c */
+
+GEN     glambdak(GEN nfz, GEN s, long prec);
+GEN     gzetak(GEN nfz, GEN s, long prec);
+GEN     gzetakall(GEN nfz, GEN s, long flag, long prec);
+GEN     initzeta(GEN pol, long prec);
+GEN     dirzetak(GEN nf, GEN b);
+
+/* base1.c */
+
+GEN     FpX_FpC_nfpoleval(GEN nf, GEN pol, GEN a, GEN p);
+GEN     embed_T2(GEN x, long r1);
+GEN     embednorm_T2(GEN x, long r1);
+GEN     embed_norm(GEN x, long r1);
+void    check_ZKmodule(GEN x, const char *s);
+void    checkbid(GEN bid);
+GEN     checkbnf(GEN bnf);
+void    checkbnr(GEN bnr);
+void    checkbnrgen(GEN bnr);
+void    checkabgrp(GEN v);
+void    checksqmat(GEN x, long N);
+GEN     checknf(GEN nf);
+GEN     checknfelt_mod(GEN nf, GEN x, const char *s);
+void    checkprid(GEN bid);
+void    checkrnf(GEN rnf);
+GEN     factoredpolred(GEN x, GEN fa);
+GEN     factoredpolred2(GEN x, GEN fa);
+GEN     galoisapply(GEN nf, GEN aut, GEN x);
+GEN     get_bnf(GEN x, long *t);
+GEN     get_bnfpol(GEN x, GEN *bnf, GEN *nf);
+GEN     get_nf(GEN x, long *t);
+GEN     get_nfpol(GEN x, GEN *nf);
+GEN     get_prid(GEN x);
+GEN     idealfrobenius(GEN nf, GEN gal, GEN pr);
+GEN     idealramgroups(GEN nf, GEN gal, GEN pr);
+GEN     nf_get_allroots(GEN nf);
+long    nf_get_prec(GEN x);
+GEN     nfcertify(GEN x);
+GEN     nfgaloismatrix(GEN nf, GEN s);
+GEN     nfinit(GEN x, long prec);
+GEN     nfinit0(GEN x, long flag, long prec);
+GEN     nfinitall(GEN x, long flag, long prec);
+GEN     nfinitred(GEN x, long prec);
+GEN     nfinitred2(GEN x, long prec);
+GEN     nfisincl(GEN a, GEN b);
+GEN     nfisisom(GEN a, GEN b);
+GEN     nfnewprec(GEN nf, long prec);
+GEN     nfnewprec_shallow(GEN nf, long prec);
+GEN     nfpoleval(GEN nf, GEN pol, GEN a);
+long    nftyp(GEN x);
+GEN     polredord(GEN x);
+GEN     polgalois(GEN x, long prec);
+GEN     polred(GEN x);
+GEN     polred0(GEN x, long flag, GEN p);
+GEN     polred2(GEN x);
+GEN     polredabs(GEN x);
+GEN     polredabs0(GEN x, long flag);
+GEN     polredabs2(GEN x);
+GEN     polredabsall(GEN x, long flun);
+GEN     polredbest(GEN x, long flag);
+GEN     rnfpolredabs(GEN nf, GEN pol, long flag);
+GEN     rnfpolredbest(GEN nf, GEN relpol, long flag);
+GEN     smallpolred(GEN x);
+GEN     smallpolred2(GEN x);
+GEN     tschirnhaus(GEN x);
+GEN     ZX_Q_normalize(GEN pol, GEN *ptlc);
+GEN     ZX_Z_normalize(GEN pol, GEN *ptk);
+GEN     ZX_to_monic(GEN pol, GEN *lead);
+GEN     ZX_primitive_to_monic(GEN pol, GEN *lead);
+
+/* base2.c */
+
+GEN     Fq_to_nf(GEN x, GEN modpr);
+GEN     FqM_to_nfM(GEN z, GEN modpr);
+GEN     FqV_to_nfV(GEN z, GEN modpr);
+GEN     FqX_to_nfX(GEN x, GEN modpr);
+GEN     Rg_nffix(const char *f, GEN T, GEN c, int lift);
+GEN     RgV_nffix(const char *f, GEN T, GEN P, int lift);
+GEN     RgX_nffix(const char *s, GEN nf, GEN x, int lift);
+long    ZpX_disc_val(GEN f, GEN p);
+GEN     ZpX_gcd(GEN f1,GEN f2,GEN p, GEN pm);
+GEN     ZpX_reduced_resultant(GEN x, GEN y, GEN p, GEN pm);
+GEN     ZpX_reduced_resultant_fast(GEN f, GEN g, GEN p, long M);
+long    ZpX_resultant_val(GEN f, GEN g, GEN p, long M);
+void    checkmodpr(GEN modpr);
+GEN     ZX_compositum_disjoint(GEN A, GEN B);
+GEN     compositum(GEN P, GEN Q);
+GEN     compositum2(GEN P, GEN Q);
+GEN     nfdisc(GEN x);
+GEN     indexpartial(GEN P, GEN DP);
+GEN     modpr_genFq(GEN modpr);
+GEN     nf_to_Fq_init(GEN nf, GEN *pr, GEN *T, GEN *p);
+GEN     nf_to_Fq(GEN nf, GEN x, GEN modpr);
+GEN     nfM_to_FqM(GEN z, GEN nf,GEN modpr);
+GEN     nfV_to_FqV(GEN z, GEN nf,GEN modpr);
+GEN     nfX_to_FqX(GEN x, GEN nf,GEN modpr);
+GEN     nfbasis(GEN x, GEN *y,GEN p);
+GEN     nfbasis0(GEN x,long flag,GEN p);
+GEN     nfdisc0(GEN x,long flag, GEN p);
+void    nfmaxord(nfmaxord_t *S, GEN T, long flag);
+GEN     nfmodprinit(GEN nf, GEN pr);
+GEN     nfreducemodpr(GEN nf, GEN x, GEN modpr);
+GEN     polcompositum0(GEN P, GEN Q,long flag);
+GEN     idealprimedec(GEN nf,GEN p);
+GEN     rnfbasis(GEN bnf, GEN order);
+GEN     rnfdedekind(GEN nf, GEN T, GEN pr, long flag);
+GEN     rnfdet(GEN nf, GEN order);
+GEN     rnfdiscf(GEN nf, GEN pol);
+GEN     rnfequation(GEN nf, GEN pol);
+GEN     rnfequation0(GEN nf, GEN pol, long flall);
+GEN     rnfequation2(GEN nf, GEN pol);
+GEN     nf_rnfeq(GEN nf, GEN relpol);
+GEN     nf_rnfeqsimple(GEN nf, GEN relpol);
+GEN     rnfequationall(GEN A, GEN B, long *pk, GEN *pLPRS);
+GEN     rnfhnfbasis(GEN bnf, GEN order);
+long    rnfisfree(GEN bnf, GEN order);
+GEN     rnflllgram(GEN nf, GEN pol, GEN order,long prec);
+GEN     rnfpolred(GEN nf, GEN pol, long prec);
+GEN     rnfpseudobasis(GEN nf, GEN pol);
+GEN     rnfsimplifybasis(GEN bnf, GEN order);
+GEN     rnfsteinitz(GEN nf, GEN order);
+long    factorial_lval(ulong n, ulong p);
+GEN     zk_to_Fq_init(GEN nf, GEN *pr, GEN *T, GEN *p);
+GEN     zk_to_Fq(GEN x, GEN modpr);
+GEN     zkmodprinit(GEN nf, GEN pr);
+
+/* base3.c */
+
+GEN     Idealstar(GEN nf, GEN x,long flun);
+GEN     RgC_to_nfC(GEN nf,GEN x);
+GEN     RgM_to_nfM(GEN nf,GEN x);
+GEN     RgX_to_nfX(GEN nf,GEN pol);
+GEN     algtobasis(GEN nf, GEN x);
+GEN     basistoalg(GEN nf, GEN x);
+GEN     ideallist(GEN nf,long bound);
+GEN     ideallist0(GEN nf,long bound, long flag);
+GEN     ideallistarch(GEN nf, GEN list, GEN arch);
+GEN     idealprincipalunits(GEN nf, GEN pr, long e);
+GEN     idealstar0(GEN nf, GEN x,long flag);
+GEN     indices_to_vec01(GEN archp, long r);
+GEN     matalgtobasis(GEN nf, GEN x);
+GEN     matbasistoalg(GEN nf, GEN x);
+GEN     nf_to_scalar_or_alg(GEN nf, GEN x);
+GEN     nf_to_scalar_or_basis(GEN nf, GEN x);
+GEN     nfadd(GEN nf, GEN x, GEN y);
+GEN     nfarchstar(GEN nf,GEN x,GEN arch);
+GEN     nfdiv(GEN nf, GEN x, GEN y);
+GEN     nfdiveuc(GEN nf, GEN a, GEN b);
+GEN     nfdivrem(GEN nf, GEN a, GEN b);
+GEN     nfinv(GEN nf, GEN x);
+GEN     nfinvmodideal(GEN nf, GEN x, GEN ideal);
+GEN     nfmod(GEN nf, GEN a, GEN b);
+GEN     nfmul(GEN nf,GEN x,GEN y);
+GEN     nfmuli(GEN nf,GEN x,GEN y);
+GEN     nfnorm(GEN nf, GEN x);
+GEN     nfpow(GEN nf,GEN x,GEN k);
+GEN     nfpow_u(GEN nf, GEN z, ulong n);
+GEN     nfpowmodideal(GEN nf,GEN x,GEN k,GEN ideal);
+GEN     nfsign(GEN nf,GEN alpha);
+GEN     nfsign_arch(GEN nf,GEN alpha,GEN arch);
+GEN     nfsign_from_logarch(GEN Larch, GEN invpi, GEN archp);
+GEN     nfsqr(GEN nf,GEN x);
+GEN     nfsqri(GEN nf, GEN x);
+GEN     nftrace(GEN nf, GEN x);
+long    nfval(GEN nf, GEN x, GEN vp);
+GEN     polmod_nffix(const char *f, GEN rnf, GEN x,int lift);
+GEN     polmod_nffix2(const char *f, GEN T, GEN relpol, GEN x, int lift);
+int     pr_equal(GEN nf, GEN P, GEN Q);
+GEN     rnfalgtobasis(GEN rnf, GEN x);
+GEN     rnfbasistoalg(GEN rnf, GEN x);
+GEN     rnfeltnorm(GEN rnf, GEN x);
+GEN     rnfelttrace(GEN rnf, GEN x);
+GEN     set_sign_mod_divisor(GEN nf, GEN x, GEN y, GEN idele, GEN sarch);
+GEN     vec01_to_indices(GEN arch);
+GEN     vecmodii(GEN a, GEN b);
+GEN     ideallog(GEN nf,GEN x,GEN bigideal);
+
+GEN     multable(GEN nf, GEN x);
+GEN     tablemul(GEN TAB, GEN x, GEN y);
+GEN     tablemul_ei(GEN M, GEN x, long i);
+GEN     tablemul_ei_ej(GEN M, long i, long j);
+GEN     tablemulvec(GEN M, GEN x, GEN v);
+GEN     tablesqr(GEN tab, GEN x);
+GEN     ei_multable(GEN nf, long i);
+long    ZC_nfval(GEN nf, GEN x, GEN P);
+long    ZC_nfvalrem(GEN nf, GEN x, GEN P, GEN *t);
+GEN     zk_multable(GEN nf, GEN x);
+GEN     zk_scalar_or_multable(GEN, GEN x);
+int     ZC_prdvd(GEN nf, GEN x, GEN P);
+
+/* base4.c */
+
+GEN     RM_round_maxrank(GEN G);
+GEN     ZM_famat_limit(GEN fa, GEN limit);
+GEN     famat_inv(GEN f);
+GEN     famat_inv_shallow(GEN f);
+GEN     famat_makecoprime(GEN nf, GEN g, GEN e, GEN pr, GEN prk, GEN EX);
+GEN     famat_mul(GEN f, GEN g);
+GEN     famat_pow(GEN f, GEN n);
+GEN     famat_sqr(GEN f);
+GEN     famat_reduce(GEN fa);
+GEN     famat_to_nf(GEN nf, GEN f);
+GEN     famat_to_nf_modideal_coprime(GEN nf, GEN g, GEN e, GEN id, GEN EX);
+GEN     famat_to_nf_moddivisor(GEN nf, GEN g, GEN e, GEN bid);
+GEN     famatsmall_reduce(GEN fa);
+GEN     idealtwoelt(GEN nf, GEN ix);
+GEN     idealtwoelt0(GEN nf, GEN ix, GEN a);
+GEN     idealtwoelt2(GEN nf, GEN x, GEN a);
+GEN     idealadd(GEN nf, GEN x, GEN y);
+GEN     idealaddmultoone(GEN nf, GEN list);
+GEN     idealaddtoone(GEN nf, GEN x, GEN y);
+GEN     idealaddtoone_i(GEN nf, GEN x, GEN y);
+GEN     idealaddtoone0(GEN nf, GEN x, GEN y);
+GEN     idealappr(GEN nf, GEN x);
+GEN     idealappr0(GEN nf, GEN x, long fl);
+GEN     idealapprfact(GEN nf, GEN x);
+GEN     idealchinese(GEN nf, GEN x, GEN y);
+GEN     idealcoprime(GEN nf, GEN x, GEN y);
+GEN     idealcoprimefact(GEN nf, GEN x, GEN fy);
+GEN     idealdiv(GEN nf, GEN x, GEN y);
+GEN     idealdiv0(GEN nf, GEN x, GEN y,long flag);
+GEN     idealdivexact(GEN nf, GEN x, GEN y);
+GEN     idealdivpowprime(GEN nf, GEN x, GEN vp, GEN n);
+GEN     idealmulpowprime(GEN nf, GEN x, GEN vp, GEN n);
+GEN     idealfactor(GEN nf, GEN x);
+GEN     idealhnf(GEN nf, GEN x);
+GEN     idealhnf_principal(GEN nf, GEN x);
+GEN     idealhnf_shallow(GEN nf, GEN x);
+GEN     idealhnf_two(GEN nf, GEN vp);
+GEN     idealhnf0(GEN nf, GEN a, GEN b);
+GEN     idealintersect(GEN nf, GEN x, GEN y);
+GEN     idealinv(GEN nf, GEN ix);
+GEN     idealred0(GEN nf, GEN I,GEN vdir);
+GEN     idealmul(GEN nf, GEN ix, GEN iy);
+GEN     idealmul0(GEN nf, GEN ix, GEN iy, long flag);
+GEN     idealmul_HNF(GEN nf, GEN ix, GEN iy);
+GEN     idealmulred(GEN nf, GEN ix, GEN iy);
+GEN     idealnorm(GEN nf, GEN x);
+GEN     idealnumden(GEN nf, GEN x);
+GEN     idealpow(GEN nf, GEN ix, GEN n);
+GEN     idealpow0(GEN nf, GEN ix, GEN n, long flag);
+GEN     idealpowred(GEN nf, GEN ix, GEN n);
+GEN     idealpows(GEN nf, GEN ideal, long iexp);
+GEN     idealprodprime(GEN nf, GEN L);
+GEN     idealsqr(GEN nf, GEN x);
+long    idealtyp(GEN *ideal, GEN *arch);
+long    idealval(GEN nf,GEN ix,GEN vp);
+long    isideal(GEN nf,GEN x);
+GEN     idealmin(GEN nf,GEN ix,GEN vdir);
+GEN     nf_get_Gtwist(GEN nf, GEN vdir);
+GEN     nf_get_Gtwist1(GEN nf, long i);
+GEN     nfC_nf_mul(GEN nf, GEN v, GEN x);
+GEN     nfdetint(GEN nf,GEN pseudo);
+GEN     nfdivmodpr(GEN nf, GEN x, GEN y, GEN modpr);
+GEN     nfhnf(GEN nf, GEN x);
+GEN     nfhnfmod(GEN nf, GEN x, GEN d);
+GEN     nfkermodpr(GEN nf, GEN x, GEN modpr);
+GEN     nfmulmodpr(GEN nf, GEN x, GEN y, GEN modpr);
+GEN     nfpowmodpr(GEN nf, GEN x, GEN k, GEN modpr);
+GEN     nfreduce(GEN nf, GEN x, GEN ideal);
+GEN     nfsnf(GEN nf, GEN x);
+GEN     nfsolvemodpr(GEN nf, GEN a, GEN b, GEN modpr);
+GEN     to_famat(GEN x, GEN y);
+GEN     to_famat_shallow(GEN x, GEN y);
+GEN     vecdiv(GEN x, GEN y);
+GEN     vecinv(GEN x);
+GEN     vecmul(GEN x, GEN y);
+GEN     vecpow(GEN x, GEN n);
+
+/* base5.c */
+
+GEN     eltreltoabs(GEN rnfeq, GEN x);
+GEN     eltabstorel(GEN eq, GEN P);
+GEN     eltabstorel_lift(GEN rnfeq, GEN P);
+void    nf_nfzk(GEN nf, GEN rnfeq, GEN *zknf, GEN *czknf);
+GEN     nfeltup(GEN nf, GEN x, GEN zknf, GEN czknf);
+GEN     rnfeltabstorel(GEN rnf, GEN x);
+GEN     rnfeltdown(GEN rnf, GEN x);
+GEN     rnfeltreltoabs(GEN rnf, GEN x);
+GEN     rnfeltup(GEN rnf, GEN x);
+GEN     rnfidealabstorel(GEN rnf, GEN x);
+GEN     rnfidealdown(GEN rnf, GEN x);
+GEN     rnfidealhnf(GEN rnf, GEN x);
+GEN     rnfidealmul(GEN rnf,GEN x,GEN y);
+GEN     rnfidealnormabs(GEN rnf, GEN x);
+GEN     rnfidealnormrel(GEN rnf, GEN x);
+GEN     rnfidealreltoabs(GEN rnf, GEN x);
+GEN     rnfidealtwoelement(GEN rnf,GEN x);
+GEN     rnfidealup(GEN rnf, GEN x);
+GEN     rnfinit(GEN nf,GEN pol);
+
+/* bb_group.c */
+
+GEN dlog_get_ordfa(GEN o);
+GEN dlog_get_ord(GEN o);
+GEN gen_PH_log(GEN a, GEN g, GEN ord, void *E, const struct bb_group *grp);
+GEN gen_Shanks_sqrtn(GEN a, GEN n, GEN q, GEN *zetan, void *E, const struct bb_group *grp);
+GEN gen_gener(GEN o, void *E, const struct bb_group *grp);
+GEN gen_ellgens(GEN d1, GEN d2, GEN m, void *E, const struct bb_group *grp,
+                 GEN pairorder(void *E, GEN P, GEN Q, GEN m, GEN F));
+GEN gen_ellgroup(GEN N, GEN F, GEN *pt_m, void *E, const struct bb_group *grp,
+                 GEN pairorder(void *E, GEN P, GEN Q, GEN m, GEN F));
+GEN gen_factored_order(GEN a, GEN o, void *E, const struct bb_group *grp);
+GEN gen_order(GEN x, GEN o, void *E, const struct bb_group *grp);
+GEN gen_select_order(GEN o, void *E, const struct bb_group *grp);
+
+GEN gen_plog(GEN x,GEN g0,GEN q, void *E, const struct bb_group *grp);
+GEN gen_pow(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN), GEN (*mul)(void*,GEN,GEN));
+GEN gen_pow_i(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN), GEN (*mul)(void*,GEN,GEN));
+GEN gen_pow_fold(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN), GEN (*msqr)(void*,GEN));
+GEN gen_pow_fold_i(GEN x, GEN n, void *E, GEN (*sqr)(void*,GEN), GEN (*msqr)(void*,GEN));
+GEN gen_powers(GEN x, long l, int use_sqr, void *E, GEN (*sqr)(void*,GEN), GEN (*mul)(void*,GEN,GEN), GEN (*one)(void*));
+GEN gen_powu(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN), GEN (*mul)(void*,GEN,GEN));
+GEN gen_powu_i(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN), GEN (*mul)(void*,GEN,GEN));
+GEN gen_powu_fold(GEN x, ulong n, void *E, GEN (*sqr)(void*,GEN), GEN (*msqr)(void*,GEN));
+GEN gen_powu_fold_i(GEN x, ulong n, void *E, GEN  (*sqr)(void*,GEN), GEN (*msqr)(void*,GEN));
+
+/* bibli1.c */
+
+int     QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec);
+GEN     R_from_QR(GEN x, long prec);
+int     RgM_QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec);
+GEN     Xadic_lindep(GEN x);
+GEN     algdep(GEN x, long n);
+GEN     algdep0(GEN x, long n, long bit);
+void    forqfvec0(GEN a, GEN BORNE, GEN code);
+GEN     gaussred_from_QR(GEN x, long prec);
+GEN     lindep0(GEN x, long flag);
+GEN     lindep(GEN x);
+GEN     lindep2(GEN x, long bit);
+GEN     mathouseholder(GEN Q, GEN v);
+GEN     matqr(GEN x, long flag, long prec);
+GEN     minim(GEN a, GEN borne, GEN stockmax);
+GEN     minim_raw(GEN a, GEN borne, GEN stockmax);
+GEN     minim2(GEN a, GEN borne, GEN stockmax);
+GEN     padic_lindep(GEN x);
+GEN     perf(GEN a);
+GEN     qfrep0(GEN a, GEN borne, long flag);
+GEN     qfminim0(GEN a, GEN borne, GEN stockmax,long flag, long prec);
+GEN     seralgdep(GEN s, long p, long r);
+GEN     zncoppersmith(GEN P0, GEN N, GEN X, GEN B) ;
+
+/* bibli2.c */
+
+GEN     QXQ_reverse(GEN a, GEN T);
+GEN     RgV_polint(GEN X, GEN Y, long v);
+GEN     RgXQ_reverse(GEN a, GEN T);
+GEN     ZV_indexsort(GEN L);
+long    ZV_search(GEN x, GEN y);
+GEN     ZV_sort(GEN L);
+GEN     ZV_sort_uniq(GEN L);
+GEN     ZV_union_shallow(GEN x, GEN y);
+GEN     binomial(GEN x, long k);
+GEN     binomialuu(ulong n, ulong k);
+int     cmp_nodata(void *data, GEN x, GEN y);
+int     cmp_prime_ideal(GEN x, GEN y);
+int     cmp_prime_over_p(GEN x, GEN y);
+int     cmp_RgX(GEN x, GEN y);
+int     cmp_universal(GEN x, GEN y);
+GEN     convol(GEN x, GEN y);
+int     gen_cmp_RgX(void *data, GEN x, GEN y);
+GEN     polcyclo(long n, long v);
+GEN     polcyclo_eval(long n, GEN x);
+GEN     dirdiv(GEN x, GEN y);
+GEN     dirmul(GEN x, GEN y);
+GEN     gen_indexsort(GEN x, void *E, int (*cmp)(void*,GEN,GEN));
+GEN     gen_indexsort_uniq(GEN x, void *E, int (*cmp)(void*,GEN,GEN));
+long    gen_search(GEN x, GEN y, long flag, void *data, int (*cmp)(void*,GEN,GEN));
+GEN     gen_setminus(GEN set1, GEN set2, int (*cmp)(GEN,GEN));
+GEN     gen_sort(GEN x, void *E, int (*cmp)(void*,GEN,GEN));
+void    gen_sort_inplace(GEN x, void *E, int (*cmp)(void*,GEN,GEN), GEN *perm);
+GEN     gen_sort_uniq(GEN x, void *E, int (*cmp)(void*,GEN,GEN));
+long    getstack(void);
+long    gettime(void);
+long    getabstime(void);
+GEN     gprec(GEN x, long l);
+GEN     gprec_wtrunc(GEN x, long pr);
+GEN     gprec_w(GEN x, long pr);
+GEN     gtoset(GEN x);
+GEN     indexlexsort(GEN x);
+GEN     indexsort(GEN x);
+GEN     indexvecsort(GEN x, GEN k);
+GEN     laplace(GEN x);
+GEN     lexsort(GEN x);
+GEN     mathilbert(long n);
+GEN     matqpascal(long n, GEN q);
+GEN     merge_factor(GEN fx, GEN fy, void *data, int (*cmp)(void *,GEN,GEN));
+GEN     merge_sort_uniq(GEN x, GEN y, void *data, int (*cmp)(void *,GEN,GEN));
+GEN     modreverse(GEN x);
+GEN     numtoperm(long n, GEN x);
+GEN     permtonum(GEN x);
+GEN     polhermite(long n, long v);
+GEN     polhermite_eval(long n, GEN x);
+GEN     pollegendre(long n, long v);
+GEN     pollegendre_eval(long n, GEN x);
+GEN     polint(GEN xa, GEN ya, GEN x, GEN *dy);
+GEN     polchebyshev(long n, long kind, long v);
+GEN     polchebyshev_eval(long n, long kind, GEN x);
+GEN     polchebyshev1(long n, long v);
+GEN     polchebyshev2(long n, long v);
+GEN     polrecip(GEN x);
+GEN     setbinop(GEN f, GEN x, GEN y);
+GEN     setintersect(GEN x, GEN y);
+long    setisset(GEN x);
+GEN     setminus(GEN x, GEN y);
+long    setsearch(GEN x, GEN y, long flag);
+GEN     setunion(GEN x, GEN y);
+GEN     sort(GEN x);
+GEN     sort_factor(GEN y, void *data, int (*cmp)(void*,GEN,GEN));
+GEN     stirling(long n, long m, long flag);
+GEN     stirling1(ulong n, ulong m);
+GEN     stirling2(ulong n, ulong m);
+long    tablesearch(GEN T, GEN x, int (*cmp)(GEN,GEN));
+GEN     vecbinome(long n);
+long    vecsearch(GEN v, GEN x, GEN k);
+GEN     vecsort(GEN x, GEN k);
+GEN     vecsort0(GEN x, GEN k, long flag);
+long    zv_search(GEN x, long y);
+
+/* bit.c */
+
+GEN     binaire(GEN x);
+long    bittest(GEN x, long n);
+GEN     gbitand(GEN x, GEN y);
+GEN     gbitneg(GEN x, long n);
+GEN     gbitnegimply(GEN x, GEN y);
+GEN     gbitor(GEN x, GEN y);
+GEN     gbittest(GEN x, long n);
+GEN     gbitxor(GEN x, GEN y);
+long    hammingweight(GEN n);
+GEN     ibitand(GEN x, GEN y);
+GEN     ibitnegimply(GEN x, GEN y);
+GEN     ibitor(GEN x, GEN y);
+GEN     ibitxor(GEN x, GEN y);
+
+/* buch1.c */
+
+GEN     Buchquad(GEN D, double c1, double c2, long prec);
+GEN     quadclassunit0(GEN x, long flag,GEN data, long prec);
+GEN     quadhilbert(GEN D, long prec);
+GEN     quadray(GEN bnf, GEN f, long prec);
+
+/* buch2.c */
+
+GEN     bnfcompress(GEN bnf);
+GEN     bnfinit0(GEN P,long flag,GEN data,long prec);
+GEN     bnfnewprec(GEN nf, long prec);
+GEN     bnfnewprec_shallow(GEN nf, long prec);
+GEN     bnrnewprec(GEN bnr, long prec);
+GEN     bnrnewprec_shallow(GEN bnr, long prec);
+GEN     Buchall(GEN P, long flag, long prec);
+GEN     Buchall_param(GEN P, double bach, double bach2, long nbrelpid, long flun, long prec);
+GEN     isprincipal(GEN bnf, GEN x);
+GEN     bnfisprincipal0(GEN bnf, GEN x,long flall);
+GEN     isprincipalfact(GEN bnf, GEN C, GEN L, GEN f, long flag);
+GEN     isprincipalfact_or_fail(GEN bnf, GEN C, GEN P, GEN e);
+GEN     bnfisunit(GEN bignf, GEN x);
+GEN     signunits(GEN bignf);
+GEN     nfsign_units(GEN bnf, GEN archp, int add_zu);
+
+/* buch3.c */
+
+GEN     ABC_to_bnr(GEN A, GEN B, GEN C, GEN *H, int gen);
+GEN     Buchray(GEN bnf, GEN module, long flag);
+GEN     bnrclassno(GEN bignf,GEN ideal);
+GEN     bnrclassno0(GEN A,GEN B,GEN C);
+GEN     bnrclassnolist(GEN bnf,GEN listes);
+GEN     bnrconductor0(GEN A, GEN B, GEN C, long flag);
+GEN     bnrconductor(GEN bnr, GEN H0, long flag);
+GEN     bnrconductorofchar(GEN bnr,GEN chi);
+GEN     bnrdisc0(GEN A, GEN B, GEN C, long flag);
+GEN     bnrdisc(GEN bnr, GEN H, long flag);
+GEN     bnrdisclist0(GEN bnf,GEN borne, GEN arch);
+GEN     bnrinit0(GEN bignf,GEN ideal,long flag);
+long    bnrisconductor0(GEN A, GEN B, GEN C);
+long    bnrisconductor(GEN bnr, GEN H);
+GEN     bnrisprincipal(GEN bnf, GEN x,long flag);
+GEN     bnrsurjection(GEN bnr1, GEN bnr2);
+GEN     buchnarrow(GEN bignf);
+long    bnfcertify(GEN bnf);
+long    bnfcertify0(GEN bnf, long flag);
+GEN     decodemodule(GEN nf, GEN fa);
+GEN     discrayabslist(GEN bnf,GEN listes);
+GEN     discrayabslistarch(GEN bnf, GEN arch, ulong bound);
+GEN     discrayabslistlong(GEN bnf, ulong bound);
+GEN     idealmoddivisor(GEN bnr, GEN x);
+GEN     isprincipalray(GEN bnf, GEN x);
+GEN     isprincipalraygen(GEN bnf, GEN x);
+GEN     rnfconductor(GEN bnf, GEN polrel);
+long    rnfisabelian(GEN nf, GEN pol);
+GEN     rnfnormgroup(GEN bnr, GEN polrel);
+GEN     subgrouplist0(GEN bnr, GEN indexbound, long all);
+
+/* buch4.c */
+
+GEN     bnfisnorm(GEN bnf,GEN x,long flag);
+GEN     rnfisnorm(GEN S, GEN x, long flag);
+GEN     rnfisnorminit(GEN bnf, GEN relpol, int galois);
+GEN     bnfissunit(GEN bnf,GEN suni,GEN x);
+GEN     bnfsunit(GEN bnf,GEN s,long PREC);
+long    nfhilbert(GEN bnf,GEN a,GEN b);
+long    nfhilbert0(GEN bnf,GEN a,GEN b,GEN p);
+long    hyperell_locally_soluble(GEN pol,GEN p);
+long    nf_hyperell_locally_soluble(GEN nf,GEN pol,GEN p);
+
+/* compile.c */
+
+GEN     closure_deriv(GEN G);
+long    localvars_find(GEN pack, entree *ep);
+GEN     localvars_read_str(const char *str, GEN pack);
+GEN     snm_closure(entree *ep, GEN data);
+GEN     strtoclosure(const char *s, long n, ...);
+GEN     strtofunction(const char *s);
+
+/* concat.c */
+
+GEN     concat(GEN x, GEN y);
+GEN     concat1(GEN x);
+GEN     matconcat(GEN v);
+GEN     shallowconcat(GEN x, GEN y);
+GEN     shallowconcat1(GEN x);
+GEN     shallowmatconcat(GEN v);
+GEN     vconcat(GEN A, GEN B);
+
+/* default.c */
+enum { d_SILENT = 0, d_ACKNOWLEDGE, d_INITRC, d_RETURN };
+
+GEN default0(const char *a, const char *b);
+long getrealprecision(void);
+int pari_is_default(const char *s);
+GEN sd_TeXstyle(const char *v, long flag);
+GEN sd_colors(const char *v, long flag);
+GEN sd_compatible(const char *v, long flag);
+GEN sd_datadir(const char *v, long flag);
+GEN sd_debug(const char *v, long flag);
+GEN sd_debugfiles(const char *v, long flag);
+GEN sd_debugmem(const char *v, long flag);
+GEN sd_factor_add_primes(const char *v, long flag);
+GEN sd_factor_proven(const char *v, long flag);
+GEN sd_format(const char *v, long flag);
+GEN sd_histsize(const char *v, long flag);
+GEN sd_log(const char *v, long flag);
+GEN sd_logfile(const char *v, long flag);
+GEN sd_nbthreads(const char *v, long flag);
+GEN sd_new_galois_format(const char *v, long flag);
+GEN sd_output(const char *v, long flag);
+GEN sd_parisize(const char *v, long flag);
+GEN sd_path(const char *v, long flag);
+GEN sd_prettyprinter(const char *v, long flag);
+GEN sd_primelimit(const char *v, long flag);
+GEN sd_realprecision(const char *v, long flag);
+GEN sd_secure(const char *v, long flag);
+GEN sd_seriesprecision(const char *v, long flag);
+GEN sd_simplify(const char *v, long flag);
+GEN sd_sopath(char *v, int flag);
+GEN sd_strictargs(const char *v, long flag);
+GEN sd_strictmatch(const char *v, long flag);
+GEN sd_string(const char *v, long flag, const char *s, char **f);
+GEN sd_threadsize(const char *v, long flag);
+GEN sd_toggle(const char *v, long flag, const char *s, int *ptn);
+GEN sd_ulong(const char *v, long flag, const char *s, ulong *ptn, ulong Min, ulong Max, const char **msg);
+GEN setdefault(const char *s, const char *v, long flag);
+long setrealprecision(long n, long *prec);
+
+/* ellanal.c */
+
+GEN     ellanalyticrank(GEN e, GEN eps, long prec);
+GEN     ellL1(GEN e, long r, long prec);
+
+/* elldata.c */
+
+GEN     ellconvertname(GEN s);
+GEN     elldatagenerators(GEN E);
+GEN     ellidentify(GEN E);
+GEN     ellsearch(GEN A);
+GEN     ellsearchcurve(GEN name);
+void    forell(void *E, long call(void*, GEN), long a, long b);
+
+/* elliptic.c */
+enum { t_ELL_Rg = 0, t_ELL_Q = 1, t_ELL_Qp = 2, t_ELL_Fp = 3, t_ELL_Fq = 4};
+GEN     akell(GEN e, GEN n);
+GEN     anell(GEN e, long n);
+GEN     anellsmall(GEN e, long n);
+GEN     bilhell(GEN e, GEN z1, GEN z2, long prec);
+void    checkell(GEN e);
+void    checkell_Fq(GEN e);
+void    checkell_Q(GEN e);
+void    checkell_Qp(GEN e);
+void    checkellpt(GEN z);
+void    checkell5(GEN e);
+GEN     ellanal_globalred(GEN e, GEN *gr);
+GEN     ellQ_get_N(GEN e);
+void    ellQ_get_Nfa(GEN e, GEN *N, GEN *faN);
+GEN     ellQp_Tate_uniformization(GEN E, long prec);
+GEN     ellQp_u(GEN E, long prec);
+GEN     ellQp_u2(GEN E, long prec);
+GEN     ellQp_q(GEN E, long prec);
+GEN     ellQp_ab(GEN E, long prec);
+GEN     ellQp_root(GEN E, long prec);
+GEN     ellR_ab(GEN E, long prec);
+GEN     ellR_eta(GEN E, long prec);
+GEN     ellR_omega(GEN x, long prec);
+GEN     ellR_roots(GEN E, long prec);
+GEN     elladd(GEN e, GEN z1, GEN z2);
+GEN     ellap(GEN e, GEN p);
+GEN     ellcard(GEN E, GEN p);
+GEN     ellchangecurve(GEN e, GEN ch);
+GEN     ellchangeinvert(GEN w);
+GEN     ellchangepoint(GEN x, GEN ch);
+GEN     ellchangepointinv(GEN x, GEN ch);
+GEN     elldivpol(GEN e, long n, long v);
+GEN     elleisnum(GEN om, long k, long flag, long prec);
+GEN     elleta(GEN om, long prec);
+GEN     ellff_get_card(GEN E);
+GEN     ellff_get_gens(GEN E);
+GEN     ellff_get_group(GEN E);
+GEN     ellff_get_o(GEN x);
+GEN     ellff_get_p(GEN E);
+GEN     ellfromj(GEN j);
+GEN     ellgenerators(GEN E);
+GEN     ellglobalred(GEN e1);
+GEN     ellgroup(GEN E, GEN p);
+GEN     ellgroup0(GEN E, GEN p, long flag);
+GEN     ellheight0(GEN e, GEN a, long flag,long prec);
+GEN     ellheegner(GEN e);
+GEN     ellinit(GEN x, GEN p, long prec);
+GEN     ellisoncurve(GEN e, GEN z);
+GEN     elllseries(GEN e, GEN s, GEN A, long prec);
+GEN     elllocalred(GEN e, GEN p1);
+GEN     elllog(GEN e, GEN a, GEN g, GEN o);
+GEN     ellminimalmodel(GEN E, GEN *ptv);
+GEN     ellmul(GEN e, GEN z, GEN n);
+GEN     ellneg(GEN e, GEN z);
+GEN     ellorder(GEN e, GEN p, GEN o);
+GEN     ellordinate(GEN e, GEN x, long prec);
+GEN     ellperiods(GEN w, long flag, long prec);
+GEN     ellrandom(GEN e);
+long    ellrootno(GEN e, GEN p);
+long    ellrootno_global(GEN e);
+GEN     ellsigma(GEN om, GEN z, long flag, long prec);
+GEN     ellsub(GEN e, GEN z1, GEN z2);
+GEN     elltaniyama(GEN e, long prec);
+GEN     elltatepairing(GEN E, GEN t, GEN s, GEN m);
+GEN     elltors(GEN e);
+GEN     elltors0(GEN e, long flag);
+GEN     ellweilpairing(GEN E, GEN t, GEN s, GEN m);
+GEN     ellwp(GEN w, GEN z, long prec);
+GEN     ellwp0(GEN w, GEN z, long flag, long prec);
+GEN     ellwpseries(GEN e, long v, long PRECDL);
+GEN     ellzeta(GEN om, GEN z, long prec);
+GEN     expIxy(GEN x, GEN y, long prec);
+GEN     ghell(GEN e, GEN a, long prec);
+GEN     mathell(GEN e, GEN x, long prec);
+int     oncurve(GEN e, GEN z);
+GEN     orderell(GEN e, GEN p);
+GEN     pointell(GEN e, GEN z, long prec);
+GEN     zell(GEN e, GEN z, long prec);
+
+/* ellsea.c */
+
+GEN     Fp_ellcard_SEA(GEN a4, GEN a6, GEN p, long early_abort);
+GEN     Fq_ellcard_SEA(GEN a4, GEN a6, GEN q, GEN T, GEN p, long early_abort);
+GEN     ellmodulareqn(long l, long vx, long vy);
+GEN     ellsea(GEN E, GEN p, long early_abort);
+
+/* es.c */
+
+GEN     GENtoGENstr_nospace(GEN x);
+GEN     GENtoGENstr(GEN x);
+char*   GENtoTeXstr(GEN x);
+char*   GENtostr(GEN x);
+char*   GENtostr_unquoted(GEN x);
+GEN     Str(GEN g);
+GEN     Strchr(GEN g);
+GEN     Strexpand(GEN g);
+GEN     Strtex(GEN g);
+void    brute(GEN g, char format, long dec);
+void    dbgGEN(GEN x, long nb);
+void    error0(GEN g);
+void    dbg_pari_heap(void);
+int     file_is_binary(FILE *f);
+void    err_flush(void);
+void    err_printf(const char* pat, ...);
+GEN     gp_getenv(const char *s);
+GEN     gp_read_file(char *s);
+GEN     gp_read_stream(FILE *f);
+GEN     gp_readvec_file(char *s);
+GEN     gp_readvec_stream(FILE *f);
+void    gpinstall(const char *s, const char *code,
+                  const char *gpname, const char *lib);
+GEN     gsprintf(const char *fmt, ...);
+GEN     gvsprintf(const char *fmt, va_list ap);
+char*   itostr(GEN x);
+void    matbrute(GEN g, char format, long dec);
+char*   os_getenv(const char *s);
+void    (*os_signal(int sig, void (*f)(int)))(int);
+void    outmat(GEN x);
+void    output(GEN x);
+char*   RgV_to_str(GEN g, long flag);
+void    pari_add_hist(GEN z, long t);
+void    pari_ask_confirm(const char *s);
+void    pari_fclose(pariFILE *f);
+void    pari_flush(void);
+pariFILE* pari_fopen(const char *s, const char *mode);
+pariFILE* pari_fopen_or_fail(const char *s, const char *mode);
+pariFILE* pari_fopengz(const char *s);
+void    pari_fprintf(FILE *file, const char *fmt, ...);
+void    pari_fread_chars(void *b, size_t n, FILE *f);
+GEN     pari_get_hist(long p);
+long    pari_get_histtime(long p);
+char*   pari_get_homedir(const char *user);
+int     pari_is_dir(const char *name);
+int     pari_is_file(const char *name);
+int     pari_last_was_newline(void);
+void    pari_set_last_newline(int last);
+ulong   pari_nb_hist(void);
+void    pari_printf(const char *fmt, ...);
+void    pari_putc(char c);
+void    pari_puts(const char *s);
+pariFILE* pari_safefopen(const char *s, const char *mode);
+char*   pari_sprintf(const char *fmt, ...);
+int     pari_stdin_isatty(void);
+char*   pari_strdup(const char *s);
+char*   pari_strndup(const char *s, long n);
+char*   pari_unique_dir(const char *s);
+char*   pari_unique_filename(const char *s);
+void    pari_unlink(const char *s);
+void    pari_vfprintf(FILE *file, const char *fmt, va_list ap);
+void    pari_vprintf(const char *fmt, va_list ap);
+char*   pari_vsprintf(const char *fmt, va_list ap);
+char*   path_expand(const char *s);
+void    out_print0(PariOUT *out, const char *sep, GEN g, long flag);
+void    out_printf(PariOUT *out, const char *fmt, ...);
+void    out_putc(PariOUT *out, char c);
+void    out_puts(PariOUT *out, const char *s);
+void    out_term_color(PariOUT *out, long c);
+void    out_vprintf(PariOUT *out, const char *fmt, va_list ap);
+char*   pari_sprint0(const char *msg, GEN g, long flag);
+void    print(GEN g);
+enum { f_RAW = 0, f_PRETTYMAT = 1, f_PRETTY = 3, f_TEX = 4 };
+void    print0(GEN g, long flag);
+void    print1(GEN g);
+void    printf0(const char *fmt, GEN args);
+void    printsep(const char *s, GEN g, long flag);
+void    printsep1(const char *s, GEN g, long flag);
+void    printtex(GEN g);
+char*   stack_sprintf(const char *fmt, ...);
+char*   stack_strcat(const char *s, const char *t);
+char*   stack_strdup(const char *s);
+void    strftime_expand(const char *s, char *buf, long max);
+GEN     Strprintf(const char *fmt, GEN args);
+FILE*   switchin(const char *name);
+void    switchout(const char *name);
+void    term_color(long c);
+char*   term_get_color(char *s, long c);
+void    texe(GEN g, char format, long dec);
+const char* type_name(long t);
+void    warning0(GEN g);
+void    write0(const char *s, GEN g);
+void    write1(const char *s, GEN g);
+void    writebin(const char *name, GEN x);
+void    writetex(const char *s, GEN g);
+
+/* eval.c */
+
+enum { br_NONE = 0, br_BREAK, br_NEXT, br_MULTINEXT, br_RETURN };
+void    bincopy_relink(GEN C, GEN vi);
+GEN     break0(long n);
+GEN     closure_callgen1(GEN C, GEN x);
+GEN     closure_callgen2(GEN C, GEN x, GEN y);
+GEN     closure_callgenall(GEN C, long n, ...);
+GEN     closure_callgenvec(GEN C, GEN args);
+void    closure_callvoid1(GEN C, GEN x);
+long    closure_context(long start, long level);
+void    closure_disassemble(GEN n);
+void    closure_err(long level);
+GEN     closure_evalbrk(GEN C, long *status);
+GEN     closure_evalgen(GEN C);
+GEN     closure_evalnobrk(GEN C);
+GEN     closure_evalres(GEN C);
+void    closure_evalvoid(GEN C);
+GEN     closure_trapgen(GEN C, long numerr);
+GEN     copybin_unlink(GEN C);
+GEN     get_lex(long vn);
+GEN     gp_call(void *E, GEN x);
+long    gp_callbool(void *E, GEN x);
+long    gp_callvoid(void *E, GEN x);
+GEN     gp_eval(void *E, GEN x);
+long    gp_evalbool(void *E, GEN x);
+GEN     gp_evalupto(void *E, GEN x);
+long    gp_evalvoid(void *E, GEN x);
+long    loop_break(void);
+GEN     next0(long n);
+GEN     pareval(GEN C);
+GEN     parsum(GEN a, GEN b, GEN code, GEN x);
+GEN     parvector(long n, GEN code);
+void    pop_lex(long n);
+void    push_lex(GEN a, GEN C);
+GEN     return0(GEN x);
+void    set_lex(long vn, GEN x);
+
+/* FF.c */
+
+GEN     FF_1(GEN a);
+GEN     FF_Z_Z_muldiv(GEN x, GEN y, GEN z);
+GEN     FF_Q_add(GEN x, GEN y);
+GEN     FF_Z_add(GEN a, GEN b);
+GEN     FF_Z_mul(GEN a, GEN b);
+GEN     FF_add(GEN a, GEN b);
+GEN     FF_charpoly(GEN x);
+GEN     FF_conjvec(GEN x);
+GEN     FF_div(GEN a, GEN b);
+GEN     FF_ellcard(GEN E);
+GEN     FF_ellgens(GEN E);
+GEN     FF_ellgroup(GEN E);
+GEN     FF_elllog(GEN E, GEN P, GEN Q, GEN o);
+GEN     FF_ellmul(GEN E, GEN P, GEN n);
+GEN     FF_ellorder(GEN E, GEN P, GEN o);
+GEN     FF_ellrandom(GEN E);
+GEN     FF_elltatepairing(GEN E, GEN P, GEN Q, GEN m);
+GEN     FF_ellweilpairing(GEN E, GEN P, GEN Q, GEN m);
+int     FF_equal(GEN a, GEN b);
+int     FF_equal0(GEN x);
+int     FF_equal1(GEN x);
+int     FF_equalm1(GEN x);
+long    FF_f(GEN x);
+GEN     FF_inv(GEN a);
+long    FF_issquare(GEN x);
+long    FF_issquareall(GEN x, GEN *pt);
+long    FF_ispower(GEN x, GEN K, GEN *pt);
+GEN     FF_log(GEN a, GEN b, GEN o);
+GEN     FF_minpoly(GEN x);
+GEN     FF_mod(GEN x);
+GEN     FF_mul(GEN a, GEN b);
+GEN     FF_mul2n(GEN a, long n);
+GEN     FF_neg(GEN a);
+GEN     FF_neg_i(GEN a);
+GEN     FF_norm(GEN x);
+GEN     FF_order(GEN x, GEN o);
+GEN     FF_p(GEN x);
+GEN     FF_p_i(GEN x);
+GEN     FF_pow(GEN x, GEN n);
+GEN     FF_primroot(GEN x, GEN *o);
+GEN     FF_q(GEN x);
+int     FF_samefield(GEN x, GEN y);
+GEN     FF_sqr(GEN a);
+GEN     FF_sqrt(GEN a);
+GEN     FF_sqrtn(GEN x, GEN n, GEN *zetan);
+GEN     FF_sub(GEN x, GEN y);
+GEN     FF_to_F2xq(GEN x);
+GEN     FF_to_F2xq_i(GEN x);
+GEN     FF_to_Flxq(GEN x);
+GEN     FF_to_Flxq_i(GEN x);
+GEN     FF_to_FpXQ(GEN x);
+GEN     FF_to_FpXQ_i(GEN x);
+GEN     FF_trace(GEN x);
+GEN     FF_zero(GEN a);
+GEN     FFM_FFC_mul(GEN M, GEN C, GEN ff);
+GEN     FFM_det(GEN M, GEN ff);
+GEN     FFM_image(GEN M, GEN ff);
+GEN     FFM_inv(GEN M, GEN ff);
+GEN     FFM_ker(GEN M, GEN ff);
+GEN     FFM_mul(GEN M, GEN N, GEN ff);
+long    FFM_rank(GEN M, GEN ff);
+GEN     FFX_factor(GEN f, GEN x);
+GEN     FFX_roots(GEN f, GEN x);
+GEN     Z_FF_div(GEN a, GEN b);
+GEN     ffgen(GEN T, long v);
+GEN     fflog(GEN x, GEN g, GEN o);
+GEN     fforder(GEN x, GEN o);
+GEN     ffprimroot(GEN x, GEN *o);
+GEN     ffrandom(GEN ff);
+int     Rg_is_FF(GEN c, GEN *ff);
+int     RgC_is_FFC(GEN x, GEN *ff);
+int     RgM_is_FFM(GEN x, GEN *ff);
+GEN     p_to_FF(GEN p, long v);
+
+/* galconj.c */
+
+GEN     checkgal(GEN gal);
+GEN     checkgroup(GEN g, GEN *S);
+GEN     embed_disc(GEN r, long r1, long prec);
+GEN     embed_roots(GEN r, long r1);
+GEN     galois_group(GEN gal);
+GEN     galoisconj(GEN nf, GEN d);
+GEN     galoisconj0(GEN nf, long flag, GEN d, long prec);
+GEN     galoisexport(GEN gal, long format);
+GEN     galoisfixedfield(GEN gal, GEN v, long flag, long y);
+GEN     galoisidentify(GEN gal);
+GEN     galoisinit(GEN nf, GEN den);
+GEN     galoisisabelian(GEN gal, long flag);
+long    galoisisnormal(GEN gal, GEN sub);
+GEN     galoispermtopol(GEN gal, GEN perm);
+GEN     galoissubgroups(GEN G);
+GEN     galoissubfields(GEN G, long flag, long v);
+long    numberofconjugates(GEN T, long pdepart);
+GEN     vandermondeinverse(GEN L, GEN T, GEN den, GEN prep);
+
+/* galpol.c */
+
+GEN     galoisnbpol(long a);
+GEN     galoisgetpol(long a, long b, long s);
+
+/* gen1.c */
+GEN     conjvec(GEN x,long prec);
+GEN     gadd(GEN x, GEN y);
+GEN     gaddsg(long x, GEN y);
+GEN     gconj(GEN x);
+GEN     gdiv(GEN x, GEN y);
+GEN     gdivgs(GEN x, long s);
+GEN     ginv(GEN x);
+GEN     gmul(GEN x, GEN y);
+GEN     gmul2n(GEN x, long n);
+GEN     gmulsg(long s, GEN y);
+GEN     gsqr(GEN x);
+GEN     gsub(GEN x, GEN y);
+GEN     gsubsg(long x, GEN y);
+GEN     inv_ser(GEN b);
+GEN     mulcxI(GEN x);
+GEN     mulcxmI(GEN x);
+GEN     ser_normalize(GEN x);
+
+/* gen2.c */
+
+GEN     gassoc_proto(GEN f(GEN,GEN),GEN,GEN);
+GEN     map_proto_G(GEN f(GEN), GEN x);
+GEN     map_proto_lG(long f(GEN), GEN x);
+GEN     map_proto_lGL(long f(GEN,long), GEN x, long y);
+
+long    Q_pval(GEN x, GEN p);
+long    Q_pvalrem(GEN x, GEN p, GEN *y);
+long    RgX_val(GEN x);
+long    RgX_valrem(GEN x, GEN *z);
+long    RgX_valrem_inexact(GEN x, GEN *Z);
+int     ZV_Z_dvd(GEN v, GEN p);
+long    ZV_pval(GEN x, GEN p);
+long    ZV_pvalrem(GEN x, GEN p, GEN *px);
+long    ZV_lval(GEN x, ulong p);
+long    ZV_lvalrem(GEN x, ulong p, GEN *px);
+long    ZX_lvalrem(GEN x, ulong p, GEN *px);
+long    ZX_lval(GEN x, ulong p);
+long    ZX_pval(GEN x, GEN p);
+long    ZX_pvalrem(GEN x, GEN p, GEN *px);
+long    Z_lval(GEN n, ulong p);
+long    Z_lvalrem(GEN n, ulong p, GEN *py);
+long    Z_lvalrem_stop(GEN *n, ulong p, int *stop);
+long    Z_pval(GEN n, GEN p);
+long    Z_pvalrem(GEN x, GEN p, GEN *py);
+GEN     cgetp(GEN x);
+GEN     cvstop2(long s, GEN y);
+GEN     cvtop(GEN x, GEN p, long l);
+GEN     cvtop2(GEN x, GEN y);
+GEN     gabs(GEN x, long prec);
+void    gaffect(GEN x, GEN y);
+void    gaffsg(long s, GEN x);
+int     gcmp(GEN x, GEN y);
+int     gequal0(GEN x);
+int     gequal1(GEN x);
+int     gequalX(GEN x);
+int     gequalm1(GEN x);
+int     gcmpsg(long x, GEN y);
+GEN     gcvtop(GEN x, GEN p, long r);
+int     gequal(GEN x, GEN y);
+int     gequalsg(long s, GEN x);
+long    gexpo(GEN x);
+long    gvaluation(GEN x, GEN p);
+int     gidentical(GEN x, GEN y);
+long    glength(GEN x);
+GEN     gmax(GEN x, GEN y);
+GEN     gmaxgs(GEN x, long y);
+GEN     gmin(GEN x, GEN y);
+GEN     gmings(GEN x, long y);
+GEN     gneg(GEN x);
+GEN     gneg_i(GEN x);
+GEN     RgX_to_ser(GEN x, long l);
+GEN     RgX_to_ser_inexact(GEN x, long l);
+int     gsigne(GEN x);
+GEN     gtolist(GEN x);
+long    gtolong(GEN x);
+int     lexcmp(GEN x, GEN y);
+GEN     listinsert(GEN list, GEN object, long index);
+void    listpop(GEN L, long index);
+GEN     listput(GEN list, GEN object, long index);
+void    listsort(GEN list, long flag);
+GEN     matsize(GEN x);
+GEN     mklistcopy(GEN x);
+GEN     normalize(GEN x);
+GEN     normalizepol(GEN x);
+GEN     normalizepol_approx(GEN x, long lx);
+GEN     normalizepol_lg(GEN x, long lx);
+ulong   padic_to_Fl(GEN x, ulong p);
+GEN     padic_to_Fp(GEN x, GEN Y);
+GEN     quadtofp(GEN x, long l);
+GEN     rfrac_to_ser(GEN x, long l);
+long    sizedigit(GEN x);
+long    u_lval(ulong x, ulong p);
+long    u_lvalrem(ulong x, ulong p, ulong *py);
+long    u_lvalrem_stop(ulong *n, ulong p, int *stop);
+long    u_pval(ulong x, GEN p);
+long    u_pvalrem(ulong x, GEN p, ulong *py);
+long    vecindexmax(GEN x);
+long    vecindexmin(GEN x);
+GEN     vecmax0(GEN x, GEN *pv);
+GEN     vecmax(GEN x);
+GEN     vecmin0(GEN x, GEN *pv);
+GEN     vecmin(GEN x);
+long    z_lval(long s, ulong p);
+long    z_lvalrem(long s, ulong p, long *py);
+long    z_pval(long n, GEN p);
+long    z_pvalrem(long n, GEN p, long *py);
+
+
+/* gen3.c */
+
+GEN     padic_to_Q(GEN x);
+GEN     padic_to_Q_shallow(GEN x);
+GEN     QpV_to_QV(GEN v);
+GEN     RgM_mulreal(GEN x, GEN y);
+GEN     RgX_RgM_eval_col(GEN x, GEN M, long c);
+GEN     RgX_deflate_max(GEN x0, long *m);
+GEN     RgX_integ(GEN x);
+GEN     ceil_safe(GEN x);
+GEN     ceilr(GEN x);
+GEN     centerlift(GEN x);
+GEN     centerlift0(GEN x,long v);
+GEN     compo(GEN x, long n);
+GEN     deg1pol(GEN x1, GEN x0,long v);
+GEN     deg1pol_shallow(GEN x1, GEN x0,long v);
+long    degree(GEN x);
+GEN     denom(GEN x);
+GEN     deriv(GEN x, long v);
+GEN     derivser(GEN x);
+GEN     diffop(GEN x, GEN v, GEN dv);
+GEN     diffop0(GEN x, GEN v, GEN dv, long n);
+GEN     diviiround(GEN x, GEN y);
+GEN     divrem(GEN x, GEN y, long v);
+GEN     floor_safe(GEN x);
+GEN     gceil(GEN x);
+GEN     gcvtoi(GEN x, long *e);
+GEN     gdeflate(GEN x, long v, long d);
+GEN     gdivent(GEN x, GEN y);
+GEN     gdiventgs(GEN x, long y);
+GEN     gdiventsg(long x, GEN y);
+GEN     gdiventres(GEN x, GEN y);
+GEN     gdivmod(GEN x, GEN y, GEN *pr);
+GEN     gdivround(GEN x, GEN y);
+int     gdvd(GEN x, GEN y);
+GEN     geq(GEN x, GEN y);
+GEN     geval(GEN x);
+GEN     gfloor(GEN x);
+GEN     gtrunc2n(GEN x, long s);
+GEN     gfrac(GEN x);
+GEN     gge(GEN x, GEN y);
+GEN     ggrando(GEN x, long n);
+GEN     ggt(GEN x, GEN y);
+GEN     gimag(GEN x);
+GEN     gle(GEN x, GEN y);
+GEN     glt(GEN x, GEN y);
+GEN     gmod(GEN x, GEN y);
+GEN     gmodgs(GEN x, long y);
+GEN     gmodsg(long x, GEN y);
+GEN     gmodulo(GEN x,GEN y);
+GEN     gmodulsg(long x, GEN y);
+GEN     gmodulss(long x, long y);
+GEN     gne(GEN x, GEN y);
+GEN     gnot(GEN x);
+GEN     gpolvar(GEN y);
+long    gprecision(GEN x);
+GEN     greal(GEN x);
+GEN     grndtoi(GEN x, long *e);
+GEN     ground(GEN x);
+GEN     gshift(GEN x, long n);
+GEN     gsubst(GEN x, long v, GEN y);
+GEN     gsubstpol(GEN x, GEN v, GEN y);
+GEN     gsubstvec(GEN x, GEN v, GEN y);
+GEN     gtocol(GEN x);
+GEN     gtocol0(GEN x, long n);
+GEN     gtocolrev(GEN x);
+GEN     gtocolrev0(GEN x, long n);
+GEN     gtopoly(GEN x, long v);
+GEN     gtopolyrev(GEN x, long v);
+GEN     gtoser(GEN x, long v, long precdl);
+GEN     gtovec(GEN x);
+GEN     gtovec0(GEN x, long n);
+GEN     gtovecrev(GEN x);
+GEN     gtovecrev0(GEN x, long n);
+GEN     gtovecsmall(GEN x);
+GEN     gtovecsmall0(GEN x, long n);
+GEN     gtrunc(GEN x);
+long    gvar(GEN x);
+long    gvar2(GEN x);
+GEN     hqfeval(GEN q, GEN x);
+GEN     imag_i(GEN x);
+GEN     integ(GEN x, long v);
+GEN     integser(GEN x);
+int     iscomplex(GEN x);
+int     isexactzero(GEN g);
+int     isrationalzeroscalar(GEN g);
+int     isinexact(GEN x);
+int     isinexactreal(GEN x);
+int     isint(GEN n, GEN *ptk);
+int     isrationalzero(GEN g);
+int     issmall(GEN n, long *ptk);
+GEN     lift(GEN x);
+GEN     lift0(GEN x,long v);
+GEN     liftall(GEN x);
+GEN     liftall_shallow(GEN x);
+GEN     liftint(GEN x);
+GEN     liftint_shallow(GEN x);
+GEN     liftpol(GEN x);
+GEN     liftpol_shallow(GEN x);
+GEN     mkcoln(long n, ...);
+GEN     mkintn(long n, ...);
+GEN     mkpoln(long n, ...);
+GEN     mkvecn(long n, ...);
+GEN     mkvecsmalln(long n, ...);
+GEN     mulreal(GEN x, GEN y);
+GEN     numer(GEN x);
+long    padicprec(GEN x, GEN p);
+long    padicprec_relative(GEN x);
+GEN     polcoeff0(GEN x,long n,long v);
+GEN     polcoeff_i(GEN x, long n, long v);
+long    poldegree(GEN x,long v);
+long    RgX_degree(GEN x,long v);
+GEN     poleval(GEN x, GEN y);
+GEN     pollead(GEN x,long v);
+long    precision(GEN x);
+GEN     precision0(GEN x,long n);
+GEN     qf_apply_RgM(GEN q, GEN M);
+GEN     qf_apply_ZM(GEN q, GEN M);
+GEN     qfbil(GEN x, GEN y, GEN q);
+GEN     qfeval(GEN q, GEN x);
+GEN     qfevalb(GEN q, GEN x, GEN y);
+GEN     qfnorm(GEN x, GEN q);
+GEN     real_i(GEN x);
+GEN     round0(GEN x, GEN *pte);
+GEN     roundr(GEN x);
+GEN     roundr_safe(GEN x);
+GEN     scalarpol(GEN x, long v);
+GEN     scalarpol_shallow(GEN x, long v);
+GEN     scalarser(GEN x, long v, long prec);
+GEN     ser_unscale(GEN P, GEN h);
+GEN     serreverse(GEN x);
+GEN     simplify(GEN x);
+GEN     simplify_shallow(GEN x);
+GEN     tayl(GEN x, long v, long precdl);
+GEN     toser_i(GEN x);
+GEN     trunc0(GEN x, GEN *pte);
+GEN     uu32toi(ulong a, ulong b);
+
+/* genus2red.c */
+
+GEN     genus2red(GEN Q, GEN P, GEN p);
+
+/* groupid.c */
+
+long    group_ident(GEN G, GEN S);
+long    group_ident_trans(GEN G, GEN S);
+
+/* hash.c */
+
+hashtable *hash_create(ulong minsize, ulong (*hash)(void*), int (*eq)(void*,void*), int use_stack);
+void hash_insert(hashtable *h, void *k, void *v);
+hashentry *hash_search(hashtable *h, void *k);
+hashentry *hash_remove(hashtable *h, void *k);
+void hash_destroy(hashtable *h);
+ulong hash_str(const char *str);
+ulong hash_str2(const char *s);
+ulong hash_GEN(GEN x);
+
+/* hnf_snf.c */
+
+GEN     Frobeniusform(GEN V, long n);
+GEN     RgM_hnfall(GEN A, GEN *pB, long remove);
+GEN     ZM_hnf(GEN x);
+GEN     ZM_hnfall(GEN A, GEN *ptB, long remove);
+GEN     ZM_hnfcenter(GEN M);
+GEN     ZM_hnflll(GEN A, GEN *ptB, int remove);
+GEN     ZV_gcdext(GEN A);
+GEN     ZM_hnfmod(GEN x, GEN d);
+GEN     ZM_hnfmodall(GEN x, GEN dm, long flag);
+GEN     ZM_hnfmodid(GEN x, GEN d);
+GEN     ZM_hnfperm(GEN A, GEN *ptU, GEN *ptperm);
+void    ZM_snfclean(GEN d, GEN u, GEN v);
+GEN     ZM_snf(GEN x);
+GEN     ZM_snf_group(GEN H, GEN *newU, GEN *newUi);
+GEN     ZM_snfall(GEN x, GEN *ptU, GEN *ptV);
+GEN     ZM_snfall_i(GEN x, GEN *ptU, GEN *ptV, int return_vec);
+GEN     zlm_echelon(GEN x, long early_abort, ulong p, ulong pm);
+GEN     ZpM_echelon(GEN x, long early_abort, GEN p, GEN pm);
+GEN     gsmith(GEN x);
+GEN     gsmithall(GEN x);
+GEN     hnf(GEN x);
+GEN     hnf_divscale(GEN A, GEN B,GEN t);
+GEN     hnf_solve(GEN A, GEN B);
+GEN     hnf_invimage(GEN A, GEN b);
+GEN     hnfall(GEN x);
+int     hnfdivide(GEN A, GEN B);
+GEN     hnflll(GEN x);
+GEN     hnfmerge_get_1(GEN A, GEN B);
+GEN     hnfmod(GEN x, GEN d);
+GEN     hnfmodid(GEN x,GEN p);
+GEN     hnfperm(GEN x);
+GEN     matfrobenius(GEN M, long flag, long v);
+GEN     mathnf0(GEN x, long flag);
+GEN     matsnf0(GEN x, long flag);
+GEN     smith(GEN x);
+GEN     smithall(GEN x);
+GEN     smithclean(GEN z);
+
+/* ifactor1.c */
+
+GEN     Z_factor(GEN n);
+GEN     Z_factor_limit(GEN n, ulong all);
+GEN     Z_factor_until(GEN n, GEN limit);
+long    Z_issmooth(GEN m, ulong lim);
+GEN     Z_issmooth_fact(GEN m, ulong lim);
+long    Z_issquarefree(GEN x);
+GEN     absi_factor(GEN n);
+GEN     absi_factor_limit(GEN n, ulong all);
+long    bigomega(GEN n);
+GEN     core(GEN n);
+ulong   coreu(ulong n);
+GEN     eulerphi(GEN n);
+ulong   eulerphiu(ulong n);
+ulong   eulerphiu_fact(GEN f);
+GEN     factorint(GEN n, long flag);
+GEN     factoru(ulong n);
+int     ifac_isprime(GEN x);
+int     ifac_next(GEN *part, GEN *p, long *e);
+int     ifac_read(GEN part, GEN *p, long *e);
+void    ifac_skip(GEN part);
+GEN     ifac_start(GEN n, int moebius);
+int     is_357_power(GEN x, GEN *pt, ulong *mask);
+int     is_pth_power(GEN x, GEN *pt, forprime_t *T, ulong cutoffbits);
+long    ispowerful(GEN n);
+long    issquarefree(GEN x);
+long    istotient(GEN n, GEN *px);
+long    moebius(GEN n);
+long    moebiusu(ulong n);
+GEN     nextprime(GEN n);
+GEN     numdiv(GEN n);
+long    omega(GEN n);
+GEN     precprime(GEN n);
+GEN     sumdiv(GEN n);
+GEN     sumdivk(GEN n,long k);
+ulong   tridiv_bound(GEN n);
+int     uis_357_power(ulong x, ulong *pt, ulong *mask);
+int     uis_357_powermod(ulong x, ulong *mask);
+long    uissquarefree(ulong n);
+long    uissquarefree_fact(GEN f);
+ulong   unextprime(ulong n);
+ulong   uprecprime(ulong n);
+GEN     usumdivkvec(ulong n, GEN K);
+
+/* init.c */
+
+void    allocatemem(ulong newsize);
+long    timer_delay(pari_timer *T);
+long    timer_get(pari_timer *T);
+void    timer_start(pari_timer *T);
+int     chk_gerepileupto(GEN x);
+GENbin* copy_bin(GEN x);
+GENbin* copy_bin_canon(GEN x);
+void    dbg_gerepile(pari_sp av);
+void    dbg_gerepileupto(GEN q);
+GEN     errname(GEN err);
+GEN     gclone(GEN x);
+GEN     gcloneref(GEN x);
+void    gclone_refc(GEN x);
+GEN     gcopy(GEN x);
+GEN     gcopy_avma(GEN x, pari_sp *AVMA);
+GEN     gcopy_lg(GEN x, long lx);
+GEN     gerepile(pari_sp ltop, pari_sp lbot, GEN q);
+void    gerepileallsp(pari_sp av, pari_sp tetpil, int n, ...);
+void    gerepilecoeffssp(pari_sp av, pari_sp tetpil, long *g, int n);
+void    gerepilemanysp(pari_sp av, pari_sp tetpil, GEN* g[], int n);
+GEN     getheap(void);
+void    gp_context_save(struct gp_context* rec);
+void    gp_context_restore(struct gp_context* rec);
+long    gsizeword(GEN x);
+long    gsizebyte(GEN x);
+void    gunclone(GEN x);
+void    gunclone_deep(GEN x);
+GEN     listcopy(GEN x);
+void    timer_printf(pari_timer *T, const char *format, ...);
+void    msgtimer(const char *format, ...);
+long    name_numerr(const char *s);
+GEN     newblock(size_t n);
+const char * numerr_name(long errnum);
+GEN     obj_check(GEN S, long K);
+GEN     obj_checkbuild(GEN S, long tag, GEN (*build)(GEN));
+GEN     obj_checkbuild_padicprec(GEN S, long tag, GEN (*build)(GEN,long), long prec);
+GEN     obj_checkbuild_prec(GEN S, long tag, GEN (*build)(GEN,long), long prec);
+void    obj_free(GEN S);
+GEN     obj_init(long d, long n);
+GEN     obj_insert(GEN S, long K, GEN O);
+GEN     obj_insert_shallow(GEN S, long K, GEN O);
+void    pari_add_function(entree *ep);
+void    pari_add_module(entree *ep);
+void    pari_add_defaults_module(entree *ep);
+void    pari_add_oldmodule(entree *ep);
+void    pari_close(void);
+void    pari_close_opts(ulong init_opts);
+int     pari_daemon(void);
+void    pari_err(int numerr, ...);
+GEN     pari_err_last(void);
+char *  pari_err2str(GEN err);
+void    pari_init_opts(size_t parisize, ulong maxprime, ulong init_opts);
+void    pari_init(size_t parisize, ulong maxprime);
+void    pari_stackcheck_init(void *pari_stack_base);
+void    pari_sig_init(void (*f)(int));
+void    pari_thread_alloc(struct pari_thread *t, size_t s, GEN arg);
+void    pari_thread_close(void);
+void    pari_thread_free(struct pari_thread *t);
+void    pari_thread_init(void);
+GEN     pari_thread_start(struct pari_thread *t);
+GEN     pari_version(void);
+void    pari_warn(int numerr, ...);
+GEN     trap0(const char *e, GEN f, GEN r);
+void    shiftaddress(GEN x, long dec);
+void    shiftaddress_canon(GEN x, long dec);
+long    timer(void);
+long    timer2(void);
+void    traverseheap( void(*f)(GEN, void *), void *data );
+
+/* intnum.c */
+
+GEN     intcirc(void *E, GEN (*eval) (void *, GEN), GEN a, GEN R, GEN tab, long prec);
+GEN     intfouriercos(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, GEN x, GEN tab, long prec);
+GEN     intfourierexp(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, GEN x, GEN tab, long prec);
+GEN     intfouriersin(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, GEN x, GEN tab, long prec);
+GEN     intfuncinit(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, long m, long flag, long prec);
+GEN     intlaplaceinv(void *E, GEN (*eval) (void *, GEN), GEN sig, GEN x, GEN tab, long prec);
+GEN     intmellininv(void *E, GEN (*eval) (void *, GEN), GEN sig, GEN x, GEN tab, long prec);
+GEN     intmellininvshort(GEN sig, GEN x, GEN tab, long prec);
+GEN     intnum(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, GEN tab, long prec);
+GEN     intnuminit(GEN a, GEN b, long m, long prec);
+GEN     intnuminitgen(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, long m, long flext, long prec);
+GEN     intnumromb(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, long flag, long prec);
+long    intnumstep(long prec);
+GEN     sumnum(void *E, GEN (*f) (void *, GEN), GEN a, GEN sig, GEN tab, long flag, long prec);
+GEN     sumnumalt(void *E, GEN (*f) (void *, GEN), GEN a, GEN s, GEN tab, long flag, long prec);
+GEN     sumnuminit(GEN sig, long m, long sgn, long prec);
+
+/* krasner.c */
+
+GEN     padicfields0(GEN p, GEN n, long flag);
+GEN     padicfields(GEN p, long m, long d, long flag);
+
+/* kummer.c */
+
+GEN     rnfkummer(GEN bnr, GEN subgroup, long all, long prec);
+
+/* lll.c */
+GEN     ZM_lll_norms(GEN x, double D, long flag, GEN *B);
+GEN     kerint(GEN x);
+GEN     lll(GEN x);
+GEN     lllfp(GEN x, double D, long flag);
+GEN     lllgen(GEN x);
+GEN     lllgram(GEN x);
+GEN     lllgramgen(GEN x);
+GEN     lllgramint(GEN x);
+GEN     lllgramkerim(GEN x);
+GEN     lllgramkerimgen(GEN x);
+GEN     lllint(GEN x);
+GEN     lllintpartial(GEN mat);
+GEN     lllintpartial_inplace(GEN mat);
+GEN     lllkerim(GEN x);
+GEN     lllkerimgen(GEN x);
+GEN     matkerint0(GEN x,long flag);
+GEN     qflll0(GEN x, long flag);
+GEN     qflllgram0(GEN x, long flag);
+
+/* members.c */
+
+GEN     member_a1(GEN x);
+GEN     member_a2(GEN x);
+GEN     member_a3(GEN x);
+GEN     member_a4(GEN x);
+GEN     member_a6(GEN x);
+GEN     member_area(GEN x);
+GEN     member_b2(GEN x);
+GEN     member_b4(GEN x);
+GEN     member_b6(GEN x);
+GEN     member_b8(GEN x);
+GEN     member_bid(GEN x);
+GEN     member_bnf(GEN x);
+GEN     member_c4(GEN x);
+GEN     member_c6(GEN x);
+GEN     member_clgp(GEN x);
+GEN     member_codiff(GEN x);
+GEN     member_cyc(GEN clg);
+GEN     member_diff(GEN x);
+GEN     member_disc(GEN x);
+GEN     member_e(GEN x);
+GEN     member_eta(GEN x);
+GEN     member_f(GEN x);
+GEN     member_fu(GEN x);
+GEN     member_futu(GEN x);
+GEN     member_gen(GEN x);
+GEN     member_group(GEN x);
+GEN     member_index(GEN x);
+GEN     member_j(GEN x);
+GEN     member_mod(GEN x);
+GEN     member_nf(GEN x);
+GEN     member_no(GEN clg);
+GEN     member_omega(GEN x);
+GEN     member_orders(GEN x);
+GEN     member_p(GEN x);
+GEN     member_pol(GEN x);
+GEN     member_polabs(GEN x);
+GEN     member_reg(GEN x);
+GEN     member_r1(GEN x);
+GEN     member_r2(GEN x);
+GEN     member_roots(GEN x);
+GEN     member_sign(GEN x);
+GEN     member_t2(GEN x);
+GEN     member_tate(GEN x);
+GEN     member_tufu(GEN x);
+GEN     member_tu(GEN x);
+GEN     member_zk(GEN x);
+GEN     member_zkst(GEN bid);
+
+/* mp.c */
+
+GEN     addmulii(GEN x, GEN y, GEN z);
+GEN     addmulii_inplace(GEN x, GEN y, GEN z);
+ulong   Fl_inv(ulong x, ulong p);
+ulong   Fl_invsafe(ulong x, ulong p);
+int     Fp_ratlift(GEN x, GEN m, GEN amax, GEN bmax, GEN *a, GEN *b);
+int     absi_cmp(GEN x, GEN y);
+int     absi_equal(GEN x, GEN y);
+int     absr_cmp(GEN x, GEN y);
+GEN     addii_sign(GEN x, long sx, GEN y, long sy);
+GEN     addir_sign(GEN x, long sx, GEN y, long sy);
+GEN     addrr_sign(GEN x, long sx, GEN y, long sy);
+GEN     addsi_sign(long x, GEN y, long sy);
+GEN     addui_sign(ulong x, GEN y, long sy);
+GEN     addsr(long x, GEN y);
+GEN     addumului(ulong a, ulong b, GEN Y);
+void    affir(GEN x, GEN y);
+void    affrr(GEN x, GEN y);
+GEN     bezout(GEN a, GEN b, GEN *u, GEN *v);
+long    cbezout(long a,long b,long *uu,long *vv);
+int     cmpii(GEN x, GEN y);
+int     cmprr(GEN x, GEN y);
+long    dblexpo(double x);
+ulong   dblmantissa(double x);
+GEN     dbltor(double x);
+GEN     diviiexact(GEN x, GEN y);
+GEN     divir(GEN x, GEN y);
+GEN     divis(GEN y, long x);
+GEN     divis_rem(GEN x, long y, long *rem);
+GEN     diviu_rem(GEN y, ulong x, ulong *rem);
+GEN     diviuuexact(GEN x, ulong y, ulong z);
+GEN     diviuexact(GEN x, ulong y);
+GEN     divri(GEN x, GEN y);
+GEN     divrr(GEN x, GEN y);
+GEN     divrs(GEN x, long y);
+GEN     divru(GEN x, ulong y);
+GEN     divsi(long x, GEN y);
+GEN     divsr(long x, GEN y);
+GEN     divur(ulong x, GEN y);
+GEN     dvmdii(GEN x, GEN y, GEN *z);
+int     equalii(GEN x, GEN y);
+int     equalrr(GEN x, GEN y);
+GEN     floorr(GEN x);
+GEN     gcdii(GEN x, GEN y);
+GEN     int2n(long n);
+GEN     int2u(ulong n);
+GEN     int_normalize(GEN x, long known_zero_words);
+int     invmod(GEN a, GEN b, GEN *res);
+ulong   invmod2BIL(ulong b);
+GEN     invr(GEN b);
+GEN     mantissa_real(GEN x, long *e);
+GEN     modii(GEN x, GEN y);
+void    modiiz(GEN x, GEN y, GEN z);
+GEN     mulii(GEN x, GEN y);
+GEN     mulir(GEN x, GEN y);
+GEN     mulrr(GEN x, GEN y);
+GEN     mulsi(long x, GEN y);
+GEN     mulsr(long x, GEN y);
+GEN     mulss(long x, long y);
+GEN     mului(ulong x, GEN y);
+GEN     mulur(ulong x, GEN y);
+GEN     muluu(ulong x, ulong y);
+GEN     muluui(ulong x, ulong y, GEN z);
+GEN     remi2n(GEN x, long n);
+double  rtodbl(GEN x);
+GEN     shifti(GEN x, long n);
+GEN     sqri(GEN x);
+GEN     sqrr(GEN x);
+GEN     sqrs(long x);
+GEN     sqrtr_abs(GEN x);
+GEN     sqrtremi(GEN S, GEN *R);
+GEN     sqru(ulong x);
+GEN     subsr(long x, GEN y);
+GEN     truedvmdii(GEN x, GEN y, GEN *z);
+GEN     truedvmdis(GEN x, long y, GEN *z);
+GEN     truedvmdsi(long x, GEN y, GEN *z);
+GEN     trunc2nr(GEN x, long n);
+GEN     mantissa2nr(GEN x, long n);
+GEN     truncr(GEN x);
+ulong   umodiu(GEN y, ulong x);
+long    vals(ulong x);
+
+/* nffactor.c */
+
+GEN     FpC_ratlift(GEN P, GEN mod, GEN amax, GEN bmax, GEN denom);
+GEN     FpM_ratlift(GEN M, GEN mod, GEN amax, GEN bmax, GEN denom);
+GEN     FpX_ratlift(GEN P, GEN mod, GEN amax, GEN bmax, GEN denom);
+GEN     nffactor(GEN nf,GEN x);
+GEN     nffactormod(GEN nf,GEN pol,GEN pr);
+GEN     nfgcd(GEN P, GEN Q, GEN nf, GEN den);
+GEN     nfgcd_all(GEN P, GEN Q, GEN T, GEN den, GEN *Pnew);
+GEN     nfroots(GEN nf,GEN pol);
+GEN     polfnf(GEN a, GEN t);
+GEN     rootsof1(GEN x);
+GEN     rootsof1_kannan(GEN nf);
+
+/* paricfg.c */
+
+extern const char *paricfg_datadir;
+extern const char *paricfg_version;
+extern const char *paricfg_buildinfo;
+extern const long  paricfg_version_code;
+extern const char *paricfg_vcsversion;
+extern const char *paricfg_compiledate;
+extern const char *paricfg_mt_engine;
+
+/* part.c */
+
+void    forpart(void *E, long call(void*,GEN), long k, GEN nbound, GEN abound);
+void    forpart_init(forpart_t *T, long k, GEN abound, GEN nbound);
+GEN     forpart_next(forpart_t *T);
+GEN     forpart_prev(forpart_t *T);
+GEN     numbpart(GEN x);
+GEN     partitions(long k, GEN nbound, GEN abound);
+
+/* perm.c */
+
+GEN     abelian_group(GEN G);
+GEN     cyclicgroup(GEN g, long s);
+GEN     cyc_pow(GEN cyc, long exp);
+GEN     cyc_pow_perm(GEN cyc, long exp);
+GEN     dicyclicgroup(GEN g1, GEN g2, long s1, long s2);
+GEN     group_abelianHNF(GEN G, GEN L);
+GEN     group_abelianSNF(GEN G, GEN L);
+long    group_domain(GEN G);
+GEN     group_elts(GEN G, long n);
+GEN     group_export(GEN G, long format);
+long    group_isA4S4(GEN G);
+long    group_isabelian(GEN G);
+GEN     group_leftcoset(GEN G, GEN g);
+long    group_order(GEN G);
+long    group_perm_normalize(GEN N, GEN g);
+GEN     group_quotient(GEN G, GEN H);
+GEN     group_rightcoset(GEN G, GEN g);
+GEN     group_set(GEN G, long n);
+long    group_subgroup_isnormal(GEN G, GEN H);
+GEN     group_subgroups(GEN G);
+GEN     groupelts_abelian_group(GEN S);
+GEN     groupelts_center(GEN S);
+GEN     groupelts_set(GEN G, long n);
+int     perm_commute(GEN p, GEN q);
+GEN     perm_cycles(GEN v);
+long    perm_order(GEN perm);
+GEN     perm_pow(GEN perm, long exp);
+GEN     quotient_group(GEN C, GEN G);
+GEN     quotient_perm(GEN C, GEN p);
+GEN     quotient_subgroup_lift(GEN C, GEN H, GEN S);
+GEN     subgroups_tableset(GEN S, long n);
+long    tableset_find_index(GEN tbl, GEN set);
+GEN     trivialgroup(void);
+GEN     vecperm_orbits(GEN v, long n);
+GEN     vec_insert(GEN v, long n, GEN x);
+int     vec_is1to1(GEN v);
+int     vec_isconst(GEN v);
+long    vecsmall_duplicate(GEN x);
+long    vecsmall_duplicate_sorted(GEN x);
+GEN     vecsmall_indexsort(GEN V);
+void    vecsmall_sort(GEN V);
+GEN     vecsmall_uniq(GEN V);
+GEN     vecsmall_uniq_sorted(GEN V);
+GEN     vecvecsmall_indexsort(GEN x);
+long    vecvecsmall_search(GEN x, GEN y, long flag);
+GEN     vecvecsmall_sort(GEN x);
+GEN     vecvecsmall_sort_uniq(GEN x);
+
+/* mt.c */
+
+void    mt_broadcast(GEN code);
+void    mt_sigint_block(void);
+void    mt_sigint_unblock(void);
+void    mt_queue_end(struct pari_mt *pt);
+GEN     mt_queue_get(struct pari_mt *pt, long *jobid, long *pending);
+void    mt_queue_start(struct pari_mt *pt, GEN worker);
+void    mt_queue_submit(struct pari_mt *pt, long jobid, GEN work);
+void    pari_mt_init(void);
+void    pari_mt_close(void);
+
+/* polarit1.c */
+
+GEN     ZX_Zp_root(GEN f, GEN a, GEN p, long prec);
+GEN     Zp_appr(GEN f, GEN a);
+GEN     factorpadic0(GEN f,GEN p,long r,long flag);
+GEN     factorpadic(GEN x, GEN p, long r);
+GEN     gdeuc(GEN x, GEN y);
+GEN     grem(GEN x, GEN y);
+GEN     padicappr(GEN f, GEN a);
+GEN     poldivrem(GEN x, GEN y, GEN *pr);
+GEN     rootpadic(GEN f, GEN p, long r);
+GEN     rootpadicfast(GEN f, GEN p, long e);
+
+/* polarit2.c */
+
+GEN     Q_content(GEN x);
+GEN     Q_denom(GEN x);
+GEN     Q_div_to_int(GEN x, GEN c);
+GEN     Q_gcd(GEN x, GEN y);
+GEN     Q_mul_to_int(GEN x, GEN c);
+GEN     Q_muli_to_int(GEN x, GEN d);
+GEN     Q_primitive_part(GEN x, GEN *ptc);
+GEN     Q_primpart(GEN x);
+GEN     Q_remove_denom(GEN x, GEN *ptd);
+GEN     RgXQ_charpoly(GEN x, GEN T, long v);
+GEN     RgXQ_inv(GEN x, GEN y);
+GEN     RgX_disc(GEN x);
+GEN     RgX_extgcd(GEN x, GEN y, GEN *U, GEN *V);
+GEN     RgX_extgcd_simple(GEN a, GEN b, GEN *pu, GEN *pv);
+GEN     RgX_gcd(GEN x, GEN y);
+GEN     RgX_gcd_simple(GEN x, GEN y);
+int     RgXQ_ratlift(GEN y, GEN x, long amax, long bmax, GEN *P, GEN *Q);
+GEN     RgX_resultant_all(GEN P, GEN Q, GEN *sol);
+long    RgX_type(GEN x, GEN *ptp, GEN *ptpol, long *ptpa);
+void    RgX_type_decode(long x, long *t1, long *t2);
+int     RgX_type_is_composite(long t);
+GEN     ZX_content(GEN x);
+GEN     centermod(GEN x, GEN p);
+GEN     centermod_i(GEN x, GEN p, GEN ps2);
+GEN     centermodii(GEN x, GEN p, GEN po2);
+GEN     content(GEN x);
+GEN     deg1_from_roots(GEN L, long v);
+GEN     divide_conquer_assoc(GEN x, void *data, GEN (*mul)(void*,GEN,GEN));
+GEN     divide_conquer_prod(GEN x, GEN (*mul)(GEN,GEN));
+GEN     factor(GEN x);
+GEN     factor0(GEN x,long flag);
+GEN     factorback(GEN fa);
+GEN     factorback2(GEN fa,GEN e);
+GEN     famat_mul_shallow(GEN f, GEN g);
+GEN     gbezout(GEN x, GEN y, GEN *u, GEN *v);
+GEN     gdivexact(GEN x, GEN y);
+GEN     gen_factorback(GEN L, GEN e, GEN (*_mul)(void*,GEN,GEN), GEN (*_pow)(void*,GEN,GEN), void *data);
+GEN     ggcd(GEN x, GEN y);
+GEN     ggcd0(GEN x, GEN y);
+GEN     ginvmod(GEN x, GEN y);
+GEN     glcm(GEN x, GEN y);
+GEN     glcm0(GEN x, GEN y);
+GEN     gp_factor0(GEN x, GEN flag);
+GEN     idealfactorback(GEN nf, GEN L, GEN e, int red);
+long    isirreducible(GEN x);
+GEN     newtonpoly(GEN x, GEN p);
+GEN     nffactorback(GEN nf, GEN L, GEN e);
+GEN     nfrootsQ(GEN x);
+GEN     poldisc0(GEN x, long v);
+GEN     polresultant0(GEN x, GEN y,long v,long flag);
+GEN     polsym(GEN x, long n);
+GEN     primitive_part(GEN x, GEN *c);
+GEN     primpart(GEN x);
+GEN     reduceddiscsmith(GEN pol);
+GEN     resultant2(GEN x, GEN y);
+GEN     resultant_all(GEN u, GEN v, GEN *sol);
+GEN     rnfcharpoly(GEN nf, GEN T, GEN alpha, long v);
+GEN     roots_from_deg1(GEN x);
+GEN     roots_to_pol(GEN a, long v);
+GEN     roots_to_pol_r1(GEN a, long v, long r1);
+long    sturmpart(GEN x, GEN a, GEN b);
+GEN     subresext(GEN x, GEN y, GEN *U, GEN *V);
+GEN     sylvestermatrix(GEN x,GEN y);
+GEN     trivial_fact(void);
+GEN     gcdext0(GEN x, GEN y);
+GEN     polresultantext0(GEN x, GEN y, long v);
+GEN     polresultantext(GEN x, GEN y);
+GEN     prime_fact(GEN x);
+
+/* polarit3.c */
+
+GEN     Flx_FlxY_resultant(GEN a, GEN b, ulong pp);
+GEN     Flx_factorff_irred(GEN P, GEN Q, ulong p);
+void    Flx_ffintersect(GEN P,GEN Q,long n,ulong l,GEN *SP,GEN *SQ,GEN MA,GEN MB);
+GEN     Flx_ffisom(GEN P,GEN Q,ulong l);
+GEN     Flx_roots_naive(GEN f, ulong p);
+GEN     FlxX_resultant(GEN u, GEN v, ulong p, long sx);
+GEN     Flxq_ffisom_inv(GEN S,GEN Tp, ulong p);
+GEN     FpV_polint(GEN xa, GEN ya, GEN p, long v);
+GEN     FpX_FpXY_resultant(GEN a, GEN b0, GEN p);
+GEN     FpX_factorff_irred(GEN P, GEN Q, GEN p);
+void    FpX_ffintersect(GEN P,GEN Q,long n,GEN l,GEN *SP,GEN *SQ,GEN MA,GEN MB);
+GEN     FpX_ffisom(GEN P,GEN Q,GEN l);
+GEN     FpX_translate(GEN P, GEN c, GEN p);
+GEN     FpXQ_ffisom_inv(GEN S,GEN Tp, GEN p);
+GEN     FpXV_FpC_mul(GEN V, GEN W, GEN p);
+GEN     FpXY_Fq_evaly(GEN Q, GEN y, GEN T, GEN p, long vx);
+GEN     Fq_Fp_mul(GEN x, GEN y, GEN T, GEN p);
+GEN     Fq_add(GEN x, GEN y, GEN T/*unused*/, GEN p);
+GEN     Fq_div(GEN x, GEN y, GEN T, GEN p);
+GEN     Fq_inv(GEN x, GEN T, GEN p);
+GEN     Fq_invsafe(GEN x, GEN T, GEN p);
+GEN     Fq_mul(GEN x, GEN y, GEN T, GEN p);
+GEN     Fq_mulu(GEN x, ulong y, /*unused*/GEN T, GEN p);
+GEN     Fq_neg(GEN x, GEN T, GEN p);
+GEN     Fq_neg_inv(GEN x, GEN T, GEN p);
+GEN     Fq_pow(GEN x, GEN n, GEN T, GEN p);
+GEN     Fq_powu(GEN x, ulong n, GEN pol, GEN p);
+GEN     Fq_sub(GEN x, GEN y, GEN T/*unused*/, GEN p);
+GEN     Fq_sqr(GEN x, GEN T, GEN p);
+GEN     Fq_sqrt(GEN x, GEN T, GEN p);
+GEN     Fq_sqrtn(GEN x, GEN n, GEN T, GEN p, GEN *zeta);
+GEN     FqC_add(GEN x, GEN y, GEN T, GEN p);
+GEN     FqC_sub(GEN x, GEN y, GEN T, GEN p);
+GEN     FqC_Fq_mul(GEN x, GEN y, GEN T, GEN p);
+GEN     FqC_to_FlxC(GEN v, GEN T, GEN pp);
+GEN     FqM_to_FlxM(GEN x, GEN T, GEN pp);
+GEN     FqV_roots_to_pol(GEN V, GEN T, GEN p, long v);
+GEN     FqV_red(GEN z, GEN T, GEN p);
+GEN     FqV_to_FlxV(GEN v, GEN T, GEN pp);
+GEN     FqX_Fq_add(GEN y, GEN x, GEN T, GEN p);
+GEN     FqX_Fq_mul_to_monic(GEN P, GEN U, GEN T, GEN p);
+GEN     FqX_eval(GEN x, GEN y, GEN T, GEN p);
+GEN     FqX_normalize(GEN z, GEN T, GEN p);
+GEN     FqX_translate(GEN P, GEN c, GEN T, GEN p);
+GEN     FqXQ_powers(GEN x, long l, GEN S, GEN T, GEN p);
+GEN     FqXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, GEN p);
+GEN     FqXY_eval(GEN Q, GEN y, GEN x, GEN T, GEN p);
+GEN     FqXY_evalx(GEN Q, GEN x, GEN T, GEN p);
+GEN     QX_disc(GEN x);
+GEN     QX_gcd(GEN a,GEN b);
+GEN     QX_resultant(GEN A, GEN B);
+GEN     QXQ_intnorm(GEN A, GEN B);
+GEN     QXQ_inv(GEN A, GEN B);
+GEN     QXQ_norm(GEN A, GEN B);
+int     Rg_is_Fp(GEN x, GEN *p);
+int     Rg_is_FpXQ(GEN x, GEN *pT, GEN *pp);
+GEN     Rg_to_Fp(GEN x, GEN p);
+GEN     Rg_to_FpXQ(GEN x, GEN T, GEN p);
+GEN     RgC_to_Flc(GEN x, ulong p);
+GEN     RgC_to_FpC(GEN x, GEN p);
+int     RgM_is_FpM(GEN x, GEN *p);
+GEN     RgM_to_Flm(GEN x, ulong p);
+GEN     RgM_to_FpM(GEN x, GEN p);
+int     RgV_is_FpV(GEN x, GEN *p);
+GEN     RgV_to_FpV(GEN x, GEN p);
+int     RgX_is_FpX(GEN x, GEN *p);
+GEN     RgX_to_FpX(GEN x, GEN p);
+int     RgX_is_FpXQX(GEN x, GEN *pT, GEN *pp);
+GEN     RgX_to_FpXQX(GEN x, GEN T, GEN p);
+GEN     RgX_to_FqX(GEN x, GEN T, GEN p);
+GEN     ZX_ZXY_rnfequation(GEN A, GEN B, long *lambda);
+GEN     ZXQ_charpoly(GEN A, GEN T, long v);
+GEN     ZX_disc(GEN x);
+int     ZX_is_squarefree(GEN x);
+GEN     ZX_gcd(GEN A, GEN B);
+GEN     ZX_gcd_all(GEN A, GEN B, GEN *Anew);
+GEN     ZX_resultant(GEN A, GEN B);
+int     Z_incremental_CRT(GEN *H, ulong Hp, GEN *q, ulong p);
+GEN     Z_init_CRT(ulong Hp, ulong p);
+int     ZM_incremental_CRT(GEN *H, GEN Hp, GEN *q, ulong p);
+GEN     ZM_init_CRT(GEN Hp, ulong p);
+int     ZX_incremental_CRT(GEN *ptH, GEN Hp, GEN *q, ulong p);
+GEN     ZX_init_CRT(GEN Hp, ulong p, long v);
+GEN     characteristic(GEN x);
+GEN     ffinit(GEN p, long n, long v);
+GEN     ffnbirred(GEN p, long n);
+GEN     ffnbirred0(GEN p, long n, long flag);
+GEN     ffsumnbirred(GEN p, long n);
+const struct bb_field *get_Fq_field(void **E, GEN T, GEN p);
+GEN     init_Fq(GEN p, long n, long v);
+GEN     pol_x_powers(long N, long v);
+GEN     residual_characteristic(GEN x);
+
+/* prime.c */
+
+long    BPSW_isprime(GEN x);
+long    BPSW_psp(GEN N);
+GEN     addprimes(GEN primes);
+GEN     gisprime(GEN x, long flag);
+GEN     gispseudoprime(GEN x, long flag);
+GEN     gprimepi_upper_bound(GEN x);
+GEN     gprimepi_lower_bound(GEN x);
+long    isprime(GEN x);
+long    ispseudoprime(GEN x, long flag);
+long    millerrabin(GEN n, long k);
+GEN     prime(long n);
+GEN     primepi(GEN x);
+double  primepi_upper_bound(double x);
+double  primepi_lower_bound(double x);
+GEN     primes(long n);
+GEN     primes_interval(GEN a, GEN b);
+GEN     primes_interval_zv(ulong a, ulong b);
+GEN     primes_upto_zv(ulong b);
+GEN     primes0(GEN n);
+GEN     primes_zv(long m);
+GEN     randomprime(GEN N);
+GEN     removeprimes(GEN primes);
+int     uislucaspsp(ulong n);
+int     uisprime(ulong n);
+ulong   uprime(long n);
+ulong   uprimepi(ulong n);
+
+/* qfisom.c */
+
+GEN     qfauto(GEN g, GEN flags);
+GEN     qfauto0(GEN g, GEN flags);
+GEN     qfautoexport(GEN g, long flag);
+GEN     qfisom(GEN g, GEN h, GEN flags);
+GEN     qfisom0(GEN g, GEN h, GEN flags);
+GEN     qfisominit(GEN g, GEN flags);
+GEN     qfisominit0(GEN g, GEN flags);
+
+/* random.c */
+
+GEN     genrand(GEN N);
+GEN     getrand(void);
+ulong   pari_rand(void);
+GEN     randomi(GEN x);
+GEN     randomr(long prec);
+ulong   random_Fl(ulong n);
+void    setrand(GEN seed);
+
+/* rootpol.c */
+
+GEN     QX_complex_roots(GEN p, long l);
+GEN     ZX_graeffe(GEN p);
+GEN     cleanroots(GEN x,long l);
+int     isrealappr(GEN x, long l);
+GEN     polgraeffe(GEN p);
+GEN     polmod_to_embed(GEN x, long prec);
+GEN     roots(GEN x,long l);
+
+/* subcyclo.c */
+
+GEN     factor_Aurifeuille(GEN p, long n);
+GEN     factor_Aurifeuille_prime(GEN p, long n);
+GEN     galoissubcyclo(GEN N, GEN sg, long flag, long v);
+GEN     polsubcyclo(long n, long d, long v);
+
+/* subfield.c */
+
+GEN     nfsubfields(GEN nf, long d);
+
+/* subgroup.c */
+
+GEN     subgrouplist(GEN cyc, GEN bound);
+void    forsubgroup(void *E, long fun(void*,GEN), GEN cyc, GEN B);
+
+/* stark.c */
+
+GEN     bnrL1(GEN bnr, GEN sbgrp, long flag, long prec);
+GEN     bnrrootnumber(GEN bnr, GEN chi, long flag, long prec);
+GEN     bnrstark(GEN bnr, GEN subgroup, long prec);
+
+/* sumiter.c */
+
+GEN     derivnum(void *E, GEN (*eval)(void *, GEN), GEN x, long prec);
+GEN     derivfun(void *E, GEN (*eval)(void *, GEN), GEN x, long prec);
+GEN     direuler(void *E, GEN (*eval)(void *, GEN), GEN ga, GEN gb, GEN c);
+int     forcomposite_init(forcomposite_t *C, GEN a, GEN b);
+GEN     forcomposite_next(forcomposite_t *C);
+GEN     forprime_next(forprime_t *T);
+int     forprime_init(forprime_t *T, GEN a, GEN b);
+int     forvec_init(forvec_t *T, GEN x, long flag);
+GEN     forvec_next(forvec_t *T);
+GEN     polzag(long n, long m);
+GEN     prodeuler(void *E, GEN (*eval)(void *, GEN), GEN ga, GEN gb, long prec);
+GEN     prodinf(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+GEN     prodinf1(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+GEN     sumalt(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+GEN     sumalt2(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+GEN     sumpos(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+GEN     sumpos2(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+GEN     suminf(void *E, GEN (*eval)(void *, GEN), GEN a, long prec);
+ulong   u_forprime_next(forprime_t *T);
+int     u_forprime_init(forprime_t *T, ulong a, ulong b);
+void    u_forprime_restrict(forprime_t *T, ulong c);
+int     u_forprime_arith_init(forprime_t *T, ulong a, ulong b, ulong c, ulong q);
+GEN     zbrent(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long prec);
+
+/* thue.c */
+
+GEN     bnfisintnorm(GEN x, GEN y);
+GEN     bnfisintnormabs(GEN bnf, GEN a);
+GEN     thue(GEN thueres, GEN rhs, GEN ne);
+GEN     thueinit(GEN pol, long flag, long prec);
+
+/* trans1.c */
+
+GEN     Pi2n(long n, long prec);
+GEN     PiI2(long prec);
+GEN     PiI2n(long n, long prec);
+GEN     Qp_exp(GEN x);
+GEN     Qp_log(GEN x);
+GEN     Qp_sqrt(GEN x);
+GEN     Qp_sqrtn(GEN x, GEN n, GEN *zetan);
+long    Zn_ispower(GEN a, GEN q, GEN K, GEN *pt);
+long    Zn_issquare(GEN x, GEN n);
+GEN     Zn_sqrt(GEN x, GEN n);
+GEN     Zp_teichmuller(GEN x, GEN p, long n, GEN q);
+GEN     agm(GEN x, GEN y, long prec);
+GEN     constcatalan(long prec);
+GEN     consteuler(long prec);
+GEN     constlog2(long prec);
+GEN     constpi(long prec);
+GEN     cxexpm1(GEN z, long prec);
+GEN     expIr(GEN x);
+GEN     exp1r_abs(GEN x);
+GEN     gcos(GEN x, long prec);
+GEN     gcotan(GEN x, long prec);
+GEN     gexp(GEN x, long prec);
+GEN     gexpm1(GEN x, long prec);
+GEN     glog(GEN x, long prec);
+GEN     gpow(GEN x, GEN n, long prec);
+GEN     gpowgs(GEN x, long n);
+GEN     gsin(GEN x, long prec);
+void    gsincos(GEN x, GEN *s, GEN *c, long prec);
+GEN     gsqrt(GEN x, long prec);
+GEN     gsqrtn(GEN x, GEN n, GEN *zetan, long prec);
+GEN     gtan(GEN x, long prec);
+GEN     logr_abs(GEN x);
+GEN     mpcos(GEN x);
+GEN     mpeuler(long prec);
+GEN     mpcatalan(long prec);
+void    mpsincosm1(GEN x, GEN *s, GEN *c);
+GEN     mpexp(GEN x);
+GEN     mpexpm1(GEN x);
+GEN     mplog(GEN x);
+GEN     mplog2(long prec);
+GEN     mppi(long prec);
+GEN     mpsin(GEN x);
+void    mpsincos(GEN x, GEN *s, GEN *c);
+GEN     powis(GEN x, long n);
+GEN     powiu(GEN p, ulong k);
+GEN     powrfrac(GEN x, long n, long d);
+GEN     powrs(GEN x, long n);
+GEN     powrshalf(GEN x, long s);
+GEN     powru(GEN x, ulong n);
+GEN     powruhalf(GEN x, ulong s);
+GEN     powuu(ulong p, ulong k);
+GEN     powgi(GEN x, GEN n);
+GEN     serchop0(GEN s);
+GEN     sqrtnint(GEN a, long n);
+GEN     teich(GEN x);
+GEN     trans_eval(const char *fun, GEN (*f) (GEN, long), GEN x, long prec);
+ulong   upowuu(ulong p, ulong k);
+ulong   usqrtn(ulong a, ulong n);
+ulong   usqrt(ulong a);
+
+/* trans2.c */
+
+GEN     Qp_gamma(GEN x);
+GEN     bernfrac(long n);
+GEN     bernpol(long k, long v);
+GEN     bernreal(long n, long prec);
+GEN     gacosh(GEN x, long prec);
+GEN     gacos(GEN x, long prec);
+GEN     garg(GEN x, long prec);
+GEN     gasinh(GEN x, long prec);
+GEN     gasin(GEN x, long prec);
+GEN     gatan(GEN x, long prec);
+GEN     gatanh(GEN x, long prec);
+GEN     gcosh(GEN x, long prec);
+GEN     ggammah(GEN x, long prec);
+GEN     ggamma(GEN x, long prec);
+GEN     glngamma(GEN x, long prec);
+GEN     gpsi(GEN x, long prec);
+GEN     gsinh(GEN x, long prec);
+GEN     gtanh(GEN x, long prec);
+void    mpbern(long nomb, long prec);
+GEN     mpfactr(long n, long prec);
+GEN     sumformal(GEN T, long v);
+
+/* trans3.c */
+
+GEN     dilog(GEN x, long prec);
+GEN     eint1(GEN x, long prec);
+GEN     eta(GEN x, long prec);
+GEN     eta0(GEN x, long flag,long prec);
+GEN     gerfc(GEN x, long prec);
+GEN     gpolylog(long m, GEN x, long prec);
+GEN     gzeta(GEN x, long prec);
+GEN     hyperu(GEN a, GEN b, GEN gx, long prec);
+GEN     incgam(GEN a, GEN x, long prec);
+GEN     incgam0(GEN a, GEN x, GEN z,long prec);
+GEN     incgamc(GEN a, GEN x, long prec);
+GEN     hbessel1(GEN n, GEN z, long prec);
+GEN     hbessel2(GEN n, GEN z, long prec);
+GEN     ibessel(GEN n, GEN z, long prec);
+GEN     jbessel(GEN n, GEN z, long prec);
+GEN     jbesselh(GEN n, GEN z, long prec);
+GEN     mpeint1(GEN x, GEN expx);
+GEN     mplambertW(GEN y);
+GEN     mpveceint1(GEN C, GEN eC, long n);
+GEN     powruvec(GEN e, ulong n);
+GEN     nbessel(GEN n, GEN z, long prec);
+GEN     jell(GEN x, long prec);
+GEN     kbessel(GEN nu, GEN gx, long prec);
+GEN     polylog0(long m, GEN x, long flag, long prec);
+GEN     sumdedekind_coprime(GEN h, GEN k);
+GEN     sumdedekind(GEN h, GEN k);
+GEN     szeta(long x, long prec);
+GEN     theta(GEN q, GEN z, long prec);
+GEN     thetanullk(GEN q, long k, long prec);
+GEN     trueeta(GEN x, long prec);
+GEN     u_sumdedekind_coprime(long h, long k);
+GEN     veceint1(GEN nmax, GEN C, long prec);
+GEN     vecthetanullk(GEN q, long k, long prec);
+GEN     vecthetanullk_tau(GEN tau, long k, long prec);
+GEN     weber0(GEN x, long flag,long prec);
+GEN     weberf(GEN x, long prec);
+GEN     weberf1(GEN x, long prec);
+GEN     weberf2(GEN x, long prec);
+GEN     glambertW(GEN y, long prec);
+
+/* level1.h */
+
+INLINE ulong  Fl_add(ulong a, ulong b, ulong p);
+INLINE long   Fl_center(ulong u, ulong p, ulong ps2);
+INLINE ulong  Fl_div(ulong a, ulong b, ulong p);
+INLINE ulong  Fl_double(ulong a, ulong p);
+INLINE ulong  Fl_mul(ulong a, ulong b, ulong p);
+INLINE ulong  Fl_neg(ulong x, ulong p);
+INLINE ulong  Fl_sqr(ulong a, ulong p);
+INLINE ulong  Fl_sub(ulong a, ulong b, ulong p);
+INLINE ulong  Fl_triple(ulong a, ulong p);
+INLINE GEN    absi(GEN x);
+INLINE GEN    absi_shallow(GEN x);
+INLINE GEN    absr(GEN x);
+INLINE int    absrnz_equal1(GEN x);
+INLINE int    absrnz_equal2n(GEN x);
+INLINE GEN    addii(GEN x, GEN y);
+INLINE void   addiiz(GEN x, GEN y, GEN z);
+INLINE GEN    addir(GEN x, GEN y);
+INLINE void   addirz(GEN x, GEN y, GEN z);
+INLINE GEN    addis(GEN x, long s);
+INLINE GEN    addri(GEN x, GEN y);
+INLINE void   addriz(GEN x, GEN y, GEN z);
+INLINE GEN    addrr(GEN x, GEN y);
+INLINE void   addrrz(GEN x, GEN y, GEN z);
+INLINE GEN    addrs(GEN x, long s);
+INLINE GEN    addsi(long x, GEN y);
+INLINE void   addsiz(long s, GEN y, GEN z);
+INLINE void   addsrz(long s, GEN y, GEN z);
+INLINE GEN    addss(long x, long y);
+INLINE void   addssz(long s, long y, GEN z);
+INLINE GEN    adduu(ulong x, ulong y);
+INLINE void   affgr(GEN x, GEN y);
+INLINE void   affii(GEN x, GEN y);
+INLINE void   affiz(GEN x, GEN y);
+INLINE void   affrr_fixlg(GEN y, GEN z);
+INLINE void   affsi(long s, GEN x);
+INLINE void   affsr(long s, GEN x);
+INLINE void   affsz(long x, GEN y);
+INLINE void   affui(ulong s, GEN x);
+INLINE void   affur(ulong s, GEN x);
+INLINE GEN    cgetg(long x, long y);
+INLINE GEN    cgetg_block(long x, long y);
+INLINE GEN    cgetg_copy(GEN x, long *plx);
+INLINE GEN    cgeti(long x);
+INLINE GEN    cgetineg(long x);
+INLINE GEN    cgetipos(long x);
+INLINE GEN    cgetr(long x);
+INLINE GEN    cgetr_block(long prec);
+INLINE int    cmpir(GEN x, GEN y);
+INLINE int    cmpis(GEN x, long y);
+INLINE int    cmpiu(GEN x, ulong y);
+INLINE int    cmpri(GEN x, GEN y);
+INLINE int    cmprs(GEN x, long y);
+INLINE int    cmpsi(long x, GEN y);
+INLINE int    cmpsr(long x, GEN y);
+INLINE int    cmpui(ulong x, GEN y);
+INLINE GEN    cxtofp(GEN x, long prec);
+INLINE GEN    divii(GEN a, GEN b);
+INLINE void   diviiz(GEN x, GEN y, GEN z);
+INLINE void   divirz(GEN x, GEN y, GEN z);
+INLINE void   divisz(GEN x, long s, GEN z);
+INLINE void   divriz(GEN x, GEN y, GEN z);
+INLINE void   divrrz(GEN x, GEN y, GEN z);
+INLINE void   divrsz(GEN y, long s, GEN z);
+INLINE GEN    divsi_rem(long x, GEN y, long *rem);
+INLINE void   divsiz(long x, GEN y, GEN z);
+INLINE void   divsrz(long s, GEN y, GEN z);
+INLINE GEN    divss(long x, long y);
+INLINE GEN    divss_rem(long x, long y, long *rem);
+INLINE void   divssz(long x, long y, GEN z);
+INLINE int    dvdii(GEN x, GEN y);
+INLINE int    dvdiiz(GEN x, GEN y, GEN z);
+INLINE int    dvdis(GEN x, long y);
+INLINE int    dvdisz(GEN x, long y, GEN z);
+INLINE int    dvdiu(GEN x, ulong y);
+INLINE int    dvdiuz(GEN x, ulong y, GEN z);
+INLINE int    dvdsi(long x, GEN y);
+INLINE int    dvdui(ulong x, GEN y);
+INLINE void   dvmdiiz(GEN x, GEN y, GEN z, GEN t);
+INLINE GEN    dvmdis(GEN x, long y, GEN *z);
+INLINE void   dvmdisz(GEN x, long y, GEN z, GEN t);
+INLINE long   dvmdsBIL(long n, long *r);
+INLINE GEN    dvmdsi(long x, GEN y, GEN *z);
+INLINE void   dvmdsiz(long x, GEN y, GEN z, GEN t);
+INLINE GEN    dvmdss(long x, long y, GEN *z);
+INLINE void   dvmdssz(long x, long y, GEN z, GEN t);
+INLINE ulong  dvmduBIL(ulong n, ulong *r);
+INLINE int    equalis(GEN x, long y);
+INLINE int    equaliu(GEN x, ulong y);
+INLINE int    equalsi(long x, GEN y);
+INLINE int    equalui(ulong x, GEN y);
+INLINE long   evalexpo(long x);
+INLINE long   evallg(long x);
+INLINE long   evalprecp(long x);
+INLINE long   evalvalp(long x);
+INLINE long   expi(GEN x);
+INLINE long   expu(ulong x);
+INLINE void   fixlg(GEN z, long ly);
+INLINE GEN    fractor(GEN x, long prec);
+INLINE GEN    icopy(GEN x);
+INLINE GEN    icopyspec(GEN x, long nx);
+INLINE GEN    icopy_avma(GEN x, pari_sp av);
+INLINE ulong  int_bit(GEN x, long n);
+INLINE GEN    itor(GEN x, long prec);
+INLINE long   itos(GEN x);
+INLINE long   itos_or_0(GEN x);
+INLINE ulong  itou(GEN x);
+INLINE ulong  itou_or_0(GEN x);
+INLINE GEN    leafcopy(GEN x);
+INLINE GEN    leafcopy_avma(GEN x, pari_sp av);
+INLINE double maxdd(double x, double y);
+INLINE long   maxss(long x, long y);
+INLINE long   maxuu(ulong x, ulong y);
+INLINE double mindd(double x, double y);
+INLINE long   minss(long x, long y);
+INLINE long   minuu(ulong x, ulong y);
+INLINE long   mod16(GEN x);
+INLINE long   mod2(GEN x);
+INLINE ulong  mod2BIL(GEN x);
+INLINE long   mod32(GEN x);
+INLINE long   mod4(GEN x);
+INLINE long   mod64(GEN x);
+INLINE long   mod8(GEN x);
+INLINE GEN    modis(GEN x, long y);
+INLINE void   modisz(GEN y, long s, GEN z);
+INLINE GEN    modsi(long x, GEN y);
+INLINE void   modsiz(long s, GEN y, GEN z);
+INLINE GEN    modss(long x, long y);
+INLINE void   modssz(long s, long y, GEN z);
+INLINE GEN    mpabs(GEN x);
+INLINE GEN    mpabs_shallow(GEN x);
+INLINE GEN    mpadd(GEN x, GEN y);
+INLINE void   mpaddz(GEN x, GEN y, GEN z);
+INLINE void   mpaff(GEN x, GEN y);
+INLINE GEN    mpceil(GEN x);
+INLINE int    mpcmp(GEN x, GEN y);
+INLINE GEN    mpcopy(GEN x);
+INLINE GEN    mpdiv(GEN x, GEN y);
+INLINE long   mpexpo(GEN x);
+INLINE GEN    mpfloor(GEN x);
+INLINE GEN    mpmul(GEN x, GEN y);
+INLINE void   mpmulz(GEN x, GEN y, GEN z);
+INLINE GEN    mpneg(GEN x);
+INLINE int    mpodd(GEN x);
+INLINE GEN    mpround(GEN x);
+INLINE GEN    mpshift(GEN x,long s);
+INLINE GEN    mpsqr(GEN x);
+INLINE GEN    mpsub(GEN x, GEN y);
+INLINE void   mpsubz(GEN x, GEN y, GEN z);
+INLINE GEN    mptrunc(GEN x);
+INLINE void   muliiz(GEN x, GEN y, GEN z);
+INLINE void   mulirz(GEN x, GEN y, GEN z);
+INLINE GEN    mulis(GEN x, long s);
+INLINE GEN    muliu(GEN x, ulong s);
+INLINE GEN    mulri(GEN x, GEN s);
+INLINE void   mulriz(GEN x, GEN y, GEN z);
+INLINE void   mulrrz(GEN x, GEN y, GEN z);
+INLINE GEN    mulrs(GEN x, long s);
+INLINE GEN    mulru(GEN x, ulong s);
+INLINE void   mulsiz(long s, GEN y, GEN z);
+INLINE void   mulsrz(long s, GEN y, GEN z);
+INLINE void   mulssz(long s, long y, GEN z);
+INLINE GEN    negi(GEN x);
+INLINE GEN    negr(GEN x);
+INLINE GEN    new_chunk(size_t x);
+INLINE GEN    rcopy(GEN x);
+INLINE GEN    rdivii(GEN x, GEN y, long prec);
+INLINE void   rdiviiz(GEN x, GEN y, GEN z);
+INLINE GEN    rdivis(GEN x, long y, long prec);
+INLINE GEN    rdivsi(long x, GEN y, long prec);
+INLINE GEN    rdivss(long x, long y, long prec);
+INLINE GEN    real2n(long n, long prec);
+INLINE GEN    real_m2n(long n, long prec);
+INLINE GEN    real_0(long prec);
+INLINE GEN    real_0_bit(long bitprec);
+INLINE GEN    real_1(long prec);
+INLINE GEN    real_m1(long prec);
+INLINE GEN    remii(GEN a, GEN b);
+INLINE void   remiiz(GEN x, GEN y, GEN z);
+INLINE GEN    remis(GEN x, long y);
+INLINE void   remisz(GEN y, long s, GEN z);
+INLINE GEN    remsi(long x, GEN y);
+INLINE void   remsiz(long s, GEN y, GEN z);
+INLINE GEN    remss(long x, long y);
+INLINE void   remssz(long s, long y, GEN z);
+INLINE GEN    rtor(GEN x, long prec);
+INLINE long   sdivsi(long x, GEN y);
+INLINE long   sdivsi_rem(long x, GEN y, long *rem);
+INLINE long   sdivss_rem(long x, long y, long *rem);
+INLINE ulong  udiviu_rem(GEN n, ulong d, ulong *r);
+INLINE ulong  udivuu_rem(ulong x, ulong y, ulong *r);
+INLINE void   setabssign(GEN x);
+INLINE void   shift_left(GEN z2, GEN z1, long min, long M, ulong f,  ulong sh);
+INLINE void   shift_right(GEN z2, GEN z1, long min, long M, ulong f, ulong sh);
+INLINE ulong  shiftl(ulong x, ulong y);
+INLINE ulong  shiftlr(ulong x, ulong y);
+INLINE GEN    shiftr(GEN x, long n);
+INLINE void   shiftr_inplace(GEN z, long d);
+INLINE long   smodis(GEN x, long y);
+INLINE long   smodss(long x, long y);
+INLINE void   stackdummy(pari_sp av, pari_sp ltop);
+INLINE char  *stack_malloc(size_t N);
+INLINE char  *stack_calloc(size_t N);
+INLINE GEN    stoi(long x);
+INLINE GEN    stor(long x, long prec);
+INLINE GEN    subii(GEN x, GEN y);
+INLINE void   subiiz(GEN x, GEN y, GEN z);
+INLINE GEN    subir(GEN x, GEN y);
+INLINE void   subirz(GEN x, GEN y, GEN z);
+INLINE GEN    subis(GEN x, long y);
+INLINE void   subisz(GEN y, long s, GEN z);
+INLINE GEN    subri(GEN x, GEN y);
+INLINE void   subriz(GEN x, GEN y, GEN z);
+INLINE GEN    subrr(GEN x, GEN y);
+INLINE void   subrrz(GEN x, GEN y, GEN z);
+INLINE GEN    subrs(GEN x, long y);
+INLINE void   subrsz(GEN y, long s, GEN z);
+INLINE GEN    subsi(long x, GEN y);
+INLINE void   subsiz(long s, GEN y, GEN z);
+INLINE void   subsrz(long s, GEN y, GEN z);
+INLINE GEN    subss(long x, long y);
+INLINE void   subssz(long x, long y, GEN z);
+INLINE GEN    subuu(ulong x, ulong y);
+INLINE void   togglesign(GEN x);
+INLINE void   togglesign_safe(GEN *px);
+INLINE void   affectsign(GEN x, GEN y);
+INLINE void   affectsign_safe(GEN x, GEN *py);
+INLINE GEN    truedivii(GEN a,GEN b);
+INLINE GEN    truedivis(GEN a, long b);
+INLINE GEN    truedivsi(long a, GEN b);
+INLINE ulong  udivui_rem(ulong x, GEN y, ulong *rem);
+INLINE ulong  umodui(ulong x, GEN y);
+INLINE GEN    utoi(ulong x);
+INLINE GEN    utoineg(ulong x);
+INLINE GEN    utoipos(ulong x);
+INLINE GEN    utor(ulong s, long prec);
+INLINE GEN    uutoi(ulong x, ulong y);
+INLINE GEN    uutoineg(ulong x, ulong y);
+INLINE long   vali(GEN x);
+
+/* pariinl.h */
+INLINE GEN    abgrp_get_cyc(GEN x);
+INLINE GEN    abgrp_get_gen(GEN x);
+INLINE GEN    abgrp_get_no(GEN x);
+INLINE GEN    bid_get_arch(GEN bid);
+INLINE GEN    bid_get_cyc(GEN bid);
+INLINE GEN    bid_get_gen(GEN bid);
+INLINE GEN    bid_get_gen_nocheck(GEN bid);
+INLINE GEN    bid_get_grp(GEN bid);
+INLINE GEN    bid_get_ideal(GEN bid);
+INLINE GEN    bid_get_mod(GEN bid);
+INLINE GEN    bid_get_no(GEN bid);
+INLINE GEN    bnf_get_clgp(GEN bnf);
+INLINE GEN    bnf_get_cyc(GEN bnf);
+INLINE GEN    bnf_get_fu(GEN bnf);
+INLINE GEN    bnf_get_fu_nocheck(GEN bnf);
+INLINE GEN    bnf_get_gen(GEN bnf);
+INLINE GEN    bnf_get_logfu(GEN bnf);
+INLINE GEN    bnf_get_nf(GEN bnf);
+INLINE GEN    bnf_get_no(GEN bnf);
+INLINE GEN    bnf_get_reg(GEN bnf);
+INLINE GEN    bnf_get_tuU(GEN bnf);
+INLINE long   bnf_get_tuN(GEN bnf);
+INLINE GEN    bnr_get_bnf(GEN bnr);
+INLINE GEN    bnr_get_clgp(GEN bnr);
+INLINE GEN    bnr_get_cyc(GEN bnr);
+INLINE GEN    bnr_get_gen(GEN bnr);
+INLINE GEN    bnr_get_gen_nocheck(GEN bnr);
+INLINE GEN    bnr_get_no(GEN bnr);
+INLINE GEN    bnr_get_bid(GEN bnr);
+INLINE GEN    bnr_get_mod(GEN bnr);
+INLINE GEN    bnr_get_nf(GEN bnr);
+INLINE GEN    ell_get_a1(GEN e);
+INLINE GEN    ell_get_a2(GEN e);
+INLINE GEN    ell_get_a3(GEN e);
+INLINE GEN    ell_get_a4(GEN e);
+INLINE GEN    ell_get_a6(GEN e);
+INLINE GEN    ell_get_b2(GEN e);
+INLINE GEN    ell_get_b4(GEN e);
+INLINE GEN    ell_get_b6(GEN e);
+INLINE GEN    ell_get_b8(GEN e);
+INLINE GEN    ell_get_c4(GEN e);
+INLINE GEN    ell_get_c6(GEN e);
+INLINE GEN    ell_get_disc(GEN e);
+INLINE GEN    ell_get_j(GEN e);
+INLINE long   ell_get_type(GEN e);
+INLINE int    ell_is_inf(GEN z);
+INLINE GEN    ellinf(void);
+INLINE GEN    ellff_get_field(GEN x);
+INLINE GEN    ellff_get_a4a6(GEN x);
+INLINE GEN    ellQp_get_p(GEN E);
+INLINE long   ellQp_get_prec(GEN E);
+INLINE GEN    ellQp_get_zero(GEN x);
+INLINE long   ellR_get_prec(GEN x);
+INLINE long   ellR_get_sign(GEN x);
+
+INLINE GEN    gal_get_pol(GEN gal);
+INLINE GEN    gal_get_p(GEN gal);
+INLINE GEN    gal_get_e(GEN gal);
+INLINE GEN    gal_get_mod(GEN gal);
+INLINE GEN    gal_get_roots(GEN gal);
+INLINE GEN    gal_get_invvdm(GEN gal);
+INLINE GEN    gal_get_den(GEN gal);
+INLINE GEN    gal_get_group(GEN gal);
+INLINE GEN    gal_get_gen(GEN gal);
+INLINE GEN    gal_get_orders(GEN gal);
+INLINE GEN    idealpseudomin(GEN I, GEN G);
+INLINE GEN    idealpseudomin_nonscalar(GEN I, GEN G);
+INLINE GEN    idealred_elt(GEN nf, GEN I);
+INLINE GEN    idealred(GEN nf, GEN I);
+INLINE GEN    nf_get_M(GEN nf);
+INLINE GEN    nf_get_G(GEN nf);
+INLINE GEN    nf_get_Tr(GEN nf);
+INLINE GEN    nf_get_diff(GEN nf);
+INLINE long   nf_get_degree(GEN nf);
+INLINE GEN    nf_get_disc(GEN nf);
+INLINE GEN    nf_get_index(GEN nf);
+INLINE GEN    nf_get_invzk(GEN nf);
+INLINE GEN    nf_get_pol(GEN nf);
+INLINE long   nf_get_r1(GEN nf);
+INLINE long   nf_get_r2(GEN nf);
+INLINE GEN    nf_get_roots(GEN nf);
+INLINE GEN    nf_get_roundG(GEN nf);
+INLINE void   nf_get_sign(GEN nf, long *r1, long *r2);
+INLINE long   nf_get_varn(GEN nf);
+INLINE GEN    nf_get_zk(GEN nf);
+INLINE long   pr_get_e(GEN pr);
+INLINE long   pr_get_f(GEN pr);
+INLINE GEN    pr_get_gen(GEN pr);
+INLINE GEN    pr_get_p(GEN pr);
+INLINE GEN    pr_get_tau(GEN pr);
+INLINE int    pr_is_inert(GEN P);
+INLINE GEN    pr_norm(GEN pr);
+INLINE long   rnf_get_absdegree(GEN rnf);
+INLINE long   rnf_get_degree(GEN rnf);
+INLINE GEN    rnf_get_invzk(GEN rnf);
+INLINE GEN    rnf_get_map(GEN rnf);
+INLINE GEN    rnf_get_nf(GEN rnf);
+INLINE long   rnf_get_nfdegree(GEN rnf);
+INLINE GEN    rnf_get_nfpol(GEN rnf);
+INLINE long   rnf_get_nfvarn(GEN rnf);
+INLINE GEN    rnf_get_pol(GEN rnf);
+INLINE GEN    rnf_get_polabs(GEN rnf);
+INLINE GEN    rnf_get_zk(GEN nf);
+INLINE void   rnf_get_nfzk(GEN rnf, GEN *b, GEN *cb);
+INLINE long   rnf_get_varn(GEN rnf);
+
+INLINE long   closure_arity(GEN C);
+INLINE const char * closure_codestr(GEN C);
+INLINE GEN    closure_get_code(GEN C);
+INLINE GEN    closure_get_oper(GEN C);
+INLINE GEN    closure_get_data(GEN C);
+INLINE GEN    closure_get_dbg(GEN C);
+INLINE GEN    closure_get_text(GEN C);
+INLINE GEN    closure_get_frame(GEN C);
+
+INLINE GEN    addmuliu(GEN x, GEN y, ulong u);
+INLINE GEN    addmuliu_inplace(GEN x, GEN y, ulong u);
+INLINE GEN    lincombii(GEN u, GEN v, GEN x, GEN y);
+INLINE GEN    mulsubii(GEN y, GEN z, GEN x);
+INLINE GEN    submulii(GEN x, GEN y, GEN z);
+INLINE GEN    submuliu(GEN x, GEN y, ulong u);
+INLINE GEN    submuliu_inplace(GEN x, GEN y, ulong u);
+
+INLINE GEN    FpXQ_add(GEN x,GEN y,GEN T,GEN p);
+INLINE GEN    FpXQ_sub(GEN x,GEN y,GEN T,GEN p);
+INLINE GEN    Flxq_add(GEN x,GEN y,GEN T,ulong p);
+INLINE GEN    Flxq_sub(GEN x,GEN y,GEN T,ulong p);
+
+INLINE GEN    FpXQX_div(GEN x, GEN y, GEN T, GEN p);
+INLINE GEN    FlxqX_div(GEN x, GEN y, GEN T, ulong p);
+INLINE GEN    FlxqX_rem(GEN x, GEN y, GEN T, ulong p);
+
+INLINE GEN    Fq_red(GEN x, GEN T, GEN p);
+INLINE GEN    FqX_Fp_mul(GEN P, GEN U, GEN T, GEN p);
+INLINE GEN    FqX_Fq_mul(GEN P, GEN U, GEN T, GEN p);
+INLINE GEN    FqX_add(GEN x,GEN y,GEN T,GEN p);
+INLINE GEN    FqX_div(GEN x, GEN y, GEN T, GEN p);
+INLINE GEN    FqX_divrem(GEN x, GEN y, GEN T, GEN p, GEN *z);
+INLINE GEN    FqX_extgcd(GEN P,GEN Q,GEN T,GEN p, GEN *U, GEN *V);
+INLINE GEN    FqX_gcd(GEN P, GEN Q, GEN T, GEN p);
+INLINE GEN    FqX_mul(GEN x, GEN y, GEN T, GEN p);
+INLINE GEN    FqX_mulu(GEN x, ulong y, GEN T, GEN p);
+INLINE GEN    FqX_neg(GEN x,GEN T,GEN p);
+INLINE GEN    FqX_red(GEN z, GEN T, GEN p);
+INLINE GEN    FqX_rem(GEN x, GEN y, GEN T, GEN p);
+INLINE GEN    FqX_sqr(GEN x, GEN T, GEN p);
+INLINE GEN    FqX_sub(GEN x,GEN y,GEN T,GEN p);
+
+INLINE GEN    FqXQ_add(GEN x, GEN y, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_div(GEN x, GEN y, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_inv(GEN x, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_invsafe(GEN x, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_mul(GEN x, GEN y, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_sqr(GEN x, GEN S, GEN T, GEN p);
+INLINE GEN    FqXQ_sub(GEN x, GEN y, GEN S, GEN T, GEN p);
+
+INLINE ulong  F2m_coeff(GEN x, long a, long b);
+INLINE void   F2m_clear(GEN x, long a, long b);
+INLINE void   F2m_flip(GEN x, long a, long b);
+INLINE void   F2m_set(GEN x, long a, long b);
+INLINE void   F2v_clear(GEN x,long v);
+INLINE ulong  F2v_coeff(GEN x,long v);
+INLINE void   F2v_flip(GEN x,long v);
+INLINE GEN    F2v_to_F2x(GEN x, long sv);
+INLINE void   F2v_set(GEN x,long v);
+INLINE void   F2x_clear(GEN x,long v);
+INLINE ulong  F2x_coeff(GEN x,long v);
+INLINE void   F2x_flip(GEN x,long v);
+INLINE void   F2x_set(GEN x,long v);
+INLINE int    F2x_equal1(GEN x);
+INLINE int    F2x_equal(GEN V, GEN W);
+INLINE GEN    F2x_div(GEN x, GEN y);
+INLINE GEN    F2x_renormalize(GEN x, long lx);
+INLINE GEN    F2m_copy(GEN x);
+INLINE GEN    F2v_copy(GEN x);
+INLINE GEN    F2v_ei(long n, long i);
+INLINE GEN    Flm_copy(GEN x);
+INLINE GEN    Flv_copy(GEN x);
+INLINE int    Flx_equal1(GEN x);
+INLINE GEN    Flx_copy(GEN x);
+INLINE GEN    Flx_div(GEN x, GEN y, ulong p);
+INLINE ulong  Flx_lead(GEN x);
+INLINE GEN    Flx_mulu(GEN x, ulong a, ulong p);
+INLINE GEN    FpV_FpC_mul(GEN x, GEN y, GEN p);
+INLINE GEN    FpXQX_renormalize(GEN x, long lx);
+INLINE GEN    FpXX_renormalize(GEN x, long lx);
+INLINE GEN    FpX_div(GEN x, GEN y, GEN p);
+INLINE GEN    FpX_renormalize(GEN x, long lx);
+INLINE GEN    Fp_add(GEN a, GEN b, GEN m);
+INLINE GEN    Fp_addmul(GEN x, GEN y, GEN z, GEN p);
+INLINE GEN    Fp_center(GEN u, GEN p, GEN ps2);
+INLINE GEN    Fp_div(GEN a, GEN b, GEN m);
+INLINE GEN    Fp_inv(GEN a, GEN m);
+INLINE GEN    Fp_invsafe(GEN a, GEN m);
+INLINE GEN    Fp_mul(GEN a, GEN b, GEN m);
+INLINE GEN    Fp_muls(GEN a, long b, GEN m);
+INLINE GEN    Fp_mulu(GEN a, ulong b, GEN m);
+INLINE GEN    Fp_neg(GEN b, GEN m);
+INLINE GEN    Fp_red(GEN x, GEN p);
+INLINE GEN    Fp_sqr(GEN a, GEN m);
+INLINE GEN    Fp_sub(GEN a, GEN b, GEN m);
+INLINE GEN    GENbinbase(GENbin *p);
+INLINE GEN    Q_abs(GEN x);
+INLINE GEN    Q_abs_shallow(GEN x);
+INLINE int    QV_isscalar(GEN x);
+INLINE GEN    RgC_fpnorml2(GEN x, long prec);
+INLINE GEN    RgC_gtofp(GEN x, long prec);
+INLINE GEN    RgC_gtomp(GEN x, long prec);
+INLINE void   RgM_dimensions(GEN x, long *m, long *n);
+INLINE GEN    RgM_fpnorml2(GEN x, long prec);
+INLINE GEN    RgM_gtofp(GEN x, long prec);
+INLINE GEN    RgM_gtomp(GEN x, long prec);
+INLINE GEN    RgM_inv(GEN a);
+INLINE GEN    RgM_minor(GEN a, long i, long j);
+INLINE GEN    RgM_shallowcopy(GEN x);
+INLINE int    RgV_isscalar(GEN x);
+INLINE int    RgV_is_ZV(GEN x);
+INLINE int    RgV_is_QV(GEN x);
+INLINE long   RgX_equal_var(GEN x, GEN y);
+INLINE int    RgX_is_monomial(GEN x);
+INLINE int    RgX_is_rational(GEN x);
+INLINE int    RgX_is_QX(GEN x);
+INLINE int    RgX_is_ZX(GEN x);
+INLINE int    RgX_isscalar(GEN x);
+INLINE GEN    RgX_shift_inplace(GEN x, long v);
+INLINE void   RgX_shift_inplace_init(long v);
+INLINE GEN    RgXQ_mul(GEN x,GEN y,GEN T);
+INLINE GEN    RgXQ_sqr(GEN x,GEN T);
+INLINE GEN    RgXQX_div(GEN x, GEN y, GEN T);
+INLINE GEN    RgXQX_rem(GEN x, GEN y, GEN T);
+INLINE GEN    RgX_copy(GEN x);
+INLINE GEN    RgX_div(GEN x, GEN y);
+INLINE GEN    RgX_fpnorml2(GEN x, long prec);
+INLINE GEN    RgX_gtofp(GEN x, long prec);
+INLINE GEN    RgX_rem(GEN x, GEN y);
+INLINE GEN    RgX_renormalize(GEN x);
+INLINE GEN    Rg_col_ei(GEN x, long n, long i);
+INLINE GEN    ZC_hnfrem(GEN x, GEN y);
+INLINE GEN    ZM_hnfrem(GEN x, GEN y);
+INLINE GEN    ZM_lll(GEN x, double D, long f);
+INLINE int    ZV_dvd(GEN x, GEN y);
+INLINE int    ZV_isscalar(GEN x);
+INLINE GEN    ZV_to_zv(GEN x);
+INLINE GEN    ZX_ZXY_resultant(GEN a, GEN b);
+INLINE int    ZX_equal1(GEN x);
+INLINE GEN    ZX_renormalize(GEN x, long lx);
+INLINE GEN    ZXQ_mul(GEN x,GEN y,GEN T);
+INLINE GEN    ZXQ_sqr(GEN x,GEN T);
+INLINE long   Z_ispower(GEN x, ulong k);
+INLINE long   Z_issquare(GEN x);
+INLINE GEN    absfrac(GEN x);
+INLINE GEN    absfrac_shallow(GEN x);
+INLINE GEN    affc_fixlg(GEN x, GEN res);
+INLINE GEN    bin_copy(GENbin *p);
+INLINE long   bit_accuracy(long x);
+INLINE double bit_accuracy_mul(long x, double y);
+INLINE long   bit_prec(GEN x);
+INLINE int    both_odd(long x, long y);
+INLINE GEN    cgetc(long x);
+INLINE GEN    cgetalloc(long t, size_t l);
+INLINE GEN    cxcompotor(GEN z, long prec);
+INLINE void   cgiv(GEN x);
+INLINE GEN    col_ei(long n, long i);
+INLINE GEN    const_col(long n, GEN x);
+INLINE GEN    const_vec(long n, GEN x);
+INLINE GEN    const_vecsmall(long n, long c);
+INLINE GEN    constant_term(GEN x);
+INLINE GEN    cxnorm(GEN x);
+INLINE GEN    cyclic_perm(long l, long d);
+INLINE double dbllog2r(GEN x);
+INLINE long   degpol(GEN x);
+INLINE long   divsBIL(long n);
+INLINE void   gabsz(GEN x, long prec, GEN z);
+INLINE GEN    gaddgs(GEN y, long s);
+INLINE void   gaddz(GEN x, GEN y, GEN z);
+INLINE int    gcmpgs(GEN y, long s);
+INLINE void   gdiventz(GEN x, GEN y, GEN z);
+INLINE GEN    gdivsg(long s, GEN y);
+INLINE void   gdivz(GEN x, GEN y, GEN z);
+INLINE GEN    gen_I(void);
+INLINE void   gerepileall(pari_sp av, int n, ...);
+INLINE void   gerepilecoeffs(pari_sp av, GEN x, int n);
+INLINE GEN    gerepilecopy(pari_sp av, GEN x);
+INLINE void   gerepilemany(pari_sp av, GEN* g[], int n);
+INLINE int    gequalgs(GEN y, long s);
+INLINE GEN    gerepileupto(pari_sp av, GEN q);
+INLINE GEN    gerepileuptoint(pari_sp av, GEN q);
+INLINE GEN    gerepileuptoleaf(pari_sp av, GEN q);
+INLINE GEN    gmaxsg(long s, GEN y);
+INLINE GEN    gminsg(long s, GEN y);
+INLINE void   gmodz(GEN x, GEN y, GEN z);
+INLINE void   gmul2nz(GEN x, long s, GEN z);
+INLINE GEN    gmulgs(GEN y, long s);
+INLINE void   gmulz(GEN x, GEN y, GEN z);
+INLINE void   gnegz(GEN x, GEN z);
+INLINE void   gshiftz(GEN x, long s, GEN z);
+INLINE GEN    gsubgs(GEN y, long s);
+INLINE void   gsubz(GEN x, GEN y, GEN z);
+INLINE double gtodouble(GEN x);
+INLINE GEN    gtofp(GEN z, long prec);
+INLINE GEN    gtomp(GEN z, long prec);
+INLINE long   gtos(GEN x);
+INLINE long   gval(GEN x, long v);
+INLINE GEN    identity_perm(long l);
+INLINE int    equali1(GEN n);
+INLINE int    equalim1(GEN n);
+INLINE int    is_bigint(GEN n);
+INLINE int    is_const_t(long t);
+INLINE int    is_extscalar_t(long t);
+INLINE int    is_intreal_t(long t);
+INLINE int    is_matvec_t(long t);
+INLINE int    is_noncalc_t(long tx);
+INLINE int    is_pm1(GEN n);
+INLINE int    is_rational_t(long t);
+INLINE int    is_recursive_t(long t);
+INLINE int    is_scalar_t(long t);
+INLINE int    is_universal_constant(GEN x);
+INLINE int    is_vec_t(long t);
+INLINE int    isint1(GEN x);
+INLINE int    isintm1(GEN x);
+INLINE int    isintzero(GEN x);
+INLINE int    ismpzero(GEN x);
+INLINE int    isonstack(GEN x);
+INLINE void   killblock(GEN x);
+INLINE GEN    leading_term(GEN x);
+INLINE long   lgcols(GEN x);
+INLINE long   lgpol(GEN x);
+INLINE GEN    matpascal(long n);
+INLINE GEN    mkcol(GEN x);
+INLINE GEN    mkcol2(GEN x, GEN y);
+INLINE GEN    mkcol2s(long x, long y);
+INLINE GEN    mkcol3(GEN x, GEN y, GEN z);
+INLINE GEN    mkcol3s(long x, long y, long z);
+INLINE GEN    mkcol4(GEN x, GEN y, GEN z, GEN t);
+INLINE GEN    mkcol4s(long x, long y, long z, long t);
+INLINE GEN    mkcol5(GEN x, GEN y, GEN z, GEN t, GEN u);
+INLINE GEN    mkcolcopy(GEN x);
+INLINE GEN    mkcols(long x);
+INLINE GEN    mkcomplex(GEN x, GEN y);
+INLINE GEN    mkerr(long n);
+INLINE GEN    mkfrac(GEN x, GEN y);
+INLINE GEN    mkfraccopy(GEN x, GEN y);
+INLINE GEN    mkintmod(GEN x, GEN y);
+INLINE GEN    mkintmodu(ulong x, ulong y);
+INLINE GEN    mkmat(GEN x);
+INLINE GEN    mkmat2(GEN x, GEN y);
+INLINE GEN    mkmat3(GEN x, GEN y, GEN z);
+INLINE GEN    mkmat4(GEN x, GEN y, GEN z, GEN t);
+INLINE GEN    mkmat5(GEN x, GEN y, GEN z, GEN t, GEN u);
+INLINE GEN    mkmatcopy(GEN x);
+INLINE GEN    mkpolmod(GEN x, GEN y);
+INLINE GEN    mkqfi(GEN x, GEN y, GEN z);
+INLINE GEN    mkquad(GEN n, GEN x, GEN y);
+INLINE GEN    mkrfrac(GEN x, GEN y);
+INLINE GEN    mkrfraccopy(GEN x, GEN y);
+INLINE GEN    mkvec(GEN x);
+INLINE GEN    mkvec2(GEN x, GEN y);
+INLINE GEN    mkvec2copy(GEN x, GEN y);
+INLINE GEN    mkvec2s(long x, long y);
+INLINE GEN    mkvec3(GEN x, GEN y, GEN z);
+INLINE GEN    mkvec3s(long x, long y, long z);
+INLINE GEN    mkvec4(GEN x, GEN y, GEN z, GEN t);
+INLINE GEN    mkvec4s(long x, long y, long z, long t);
+INLINE GEN    mkvec5(GEN x, GEN y, GEN z, GEN t, GEN u);
+INLINE GEN    mkveccopy(GEN x);
+INLINE GEN    mkvecs(long x);
+INLINE GEN    mkvecsmall(long x);
+INLINE GEN    mkvecsmall2(long x,long y);
+INLINE GEN    mkvecsmall3(long x,long y,long z);
+INLINE GEN    mkvecsmall4(long x,long y,long z,long t);
+INLINE void   mpcosz(GEN x, GEN z);
+INLINE void   mpexpz(GEN x, GEN z);
+INLINE void   mplogz(GEN x, GEN z);
+INLINE void   mpsinz(GEN x, GEN z);
+INLINE GEN    mul_content(GEN cx, GEN cy);
+INLINE GEN    mul_denom(GEN cx, GEN cy);
+INLINE long   nbits2nlong(long x);
+INLINE long   nbits2extraprec(long x);
+INLINE long   nbits2prec(long x);
+INLINE long   nbits2lg(long x);
+INLINE long   nbrows(GEN x);
+INLINE long   nchar2nlong(long x);
+INLINE long   ndec2nlong(long x);
+INLINE long   ndec2prec(long x);
+INLINE void   normalize_frac(GEN z);
+INLINE int    odd(long x);
+INLINE void   pari_free(void *pointer);
+INLINE void*  pari_calloc(size_t size);
+INLINE void*  pari_malloc(size_t bytes);
+INLINE void*  pari_realloc(void *pointer,size_t size);
+INLINE GEN    perm_conj(GEN s, GEN t);
+INLINE GEN    perm_inv(GEN x);
+INLINE GEN    perm_mul(GEN s, GEN t);
+INLINE GEN    pol_0(long v);
+INLINE GEN    pol_1(long v);
+INLINE GEN    pol_x(long v);
+INLINE GEN    pol0_F2x(long sv);
+INLINE GEN    pol1_F2x(long sv);
+INLINE GEN    polx_F2x(long sv);
+INLINE GEN    pol0_Flx(long sv);
+INLINE GEN    pol1_Flx(long sv);
+INLINE GEN    polx_Flx(long sv);
+INLINE GEN    polx_zx(long sv);
+INLINE GEN    powii(GEN x, GEN n);
+INLINE GEN    powIs(long n);
+INLINE long   prec2nbits(long x);
+INLINE double prec2nbits_mul(long x, double y);
+INLINE long   prec2ndec(long x);
+INLINE long   precdbl(long x);
+INLINE GEN    quad_disc(GEN x);
+INLINE GEN    qfb_disc(GEN x);
+INLINE GEN    qfb_disc3(GEN x, GEN y, GEN z);
+INLINE GEN    quadnorm(GEN q);
+INLINE long   random_bits(long k);
+INLINE long   remsBIL(long n);
+INLINE GEN    resultant(GEN x, GEN y);
+INLINE GEN    row(GEN A, long x1);
+INLINE GEN    row_Flm(GEN A, long x0);
+INLINE GEN    row_i(GEN A, long x0, long x1, long x2);
+INLINE GEN    row_zm(GEN x, long i);
+INLINE GEN    rowcopy(GEN A, long x0);
+INLINE GEN    rowpermute(GEN A, GEN p);
+INLINE GEN    rowslice(GEN A, long x1, long x2);
+INLINE GEN    rowslicepermute(GEN A, GEN p, long x1, long x2);
+INLINE GEN    shallowcopy(GEN x);
+INLINE GEN    sqrfrac(GEN x);
+INLINE GEN    sqrti(GEN x);
+INLINE GEN    sqrtnr(GEN x, long n);
+INLINE GEN    sqrtr(GEN x);
+INLINE void   pari_stack_alloc(pari_stack *s, long nb);
+INLINE void** pari_stack_base(pari_stack *s);
+INLINE void   pari_stack_delete(pari_stack *s);
+INLINE void   pari_stack_init(pari_stack *s, size_t size, void **data);
+INLINE long   pari_stack_new(pari_stack *s);
+INLINE void   pari_stack_pushp(pari_stack *s, void *u);
+INLINE long   sturm(GEN x);
+INLINE GEN    truecoeff(GEN x, long n);
+INLINE GEN    trunc_safe(GEN x);
+INLINE GEN    vec_ei(long n, long i);
+INLINE GEN    vec_lengthen(GEN v, long n);
+INLINE GEN    vec_setconst(GEN v, GEN x);
+INLINE GEN    vec_shorten(GEN v, long n);
+INLINE GEN    vec_to_vecsmall(GEN z);
+INLINE GEN    vecpermute(GEN A, GEN p);
+INLINE GEN    vecreverse(GEN A);
+INLINE GEN    vecslice(GEN A, long y1, long y2);
+INLINE GEN    vecslicepermute(GEN A, GEN p, long y1, long y2);
+INLINE GEN    vecsplice(GEN a, long j);
+INLINE GEN    vecsmall_append(GEN V, long s);
+INLINE long   vecsmall_coincidence(GEN u, GEN v);
+INLINE GEN    vecsmall_concat(GEN u, GEN v);
+INLINE GEN    vecsmall_copy(GEN x);
+INLINE GEN    vecsmall_ei(long n, long i);
+INLINE long   vecsmall_indexmax(GEN x);
+INLINE long   vecsmall_indexmin(GEN x);
+INLINE long   vecsmall_isin(GEN v, long x);
+INLINE GEN    vecsmall_lengthen(GEN v, long n);
+INLINE int    vecsmall_lexcmp(GEN x, GEN y);
+INLINE long   vecsmall_max(GEN v);
+INLINE long   vecsmall_min(GEN v);
+INLINE long   vecsmall_pack(GEN V, long base, long mod);
+INLINE int    vecsmall_prefixcmp(GEN x, GEN y);
+INLINE GEN    vecsmall_prepend(GEN V, long s);
+INLINE GEN    vecsmall_shorten(GEN v, long n);
+INLINE GEN    vecsmall_to_col(GEN z);
+INLINE GEN    vecsmall_to_vec(GEN z);
+INLINE void   vecsmalltrunc_append(GEN x, long t);
+INLINE GEN    vecsmalltrunc_init(long l);
+INLINE void   vectrunc_append(GEN x, GEN t);
+INLINE void   vectrunc_append_batch(GEN x, GEN y);
+INLINE GEN    vectrunc_init(long l);
+INLINE GEN    zc_to_ZC(GEN x);
+INLINE GEN    zero_F2m(long n, long m);
+INLINE GEN    zero_F2m_copy(long n, long m);
+INLINE GEN    zero_F2v(long m);
+INLINE GEN    zero_F2x(long sv);
+INLINE GEN    zero_Flm(long m, long n);
+INLINE GEN    zero_Flm_copy(long m, long n);
+INLINE GEN    zero_Flv(long n);
+INLINE GEN    zero_Flx(long sv);
+INLINE GEN    zero_zm(long x, long y);
+INLINE GEN    zero_zv(long x);
+INLINE GEN    zero_zx(long sv);
+INLINE GEN    zerocol(long n);
+INLINE GEN    zeromat(long m, long n);
+INLINE GEN    zeromatcopy(long m, long n);
+INLINE GEN    zeropadic(GEN p, long e);
+INLINE GEN    zeropadic_shallow(GEN p, long e);
+INLINE GEN    zeropol(long v);
+INLINE GEN    zeroser(long v, long e);
+INLINE GEN    zerovec(long n);
+INLINE GEN    zm_copy(GEN x);
+INLINE GEN    zm_to_zxV(GEN x, long sv);
+INLINE GEN    zm_transpose(GEN x);
+INLINE GEN    zv_copy(GEN x);
+INLINE GEN    zv_to_ZV(GEN x);
+INLINE GEN    zv_to_zx(GEN x, long sv);
+INLINE GEN    zx_renormalize(GEN x, long l);
+INLINE GEN    zx_shift(GEN x, long n);
+INLINE GEN    zx_to_zv(GEN x, long N);
+
+INLINE GEN     err_get_compo(GEN e, long i);
+INLINE long    err_get_num(GEN e);
+INLINE void    pari_err_BUG(const char *f);
+INLINE void    pari_err_COMPONENT(const char *f, const char *op, GEN l, GEN x);
+INLINE void    pari_err_CONSTPOL(const char *f);
+INLINE void    pari_err_COPRIME(const char *f, GEN x, GEN y);
+INLINE void    pari_err_DIM(const char *f);
+INLINE void    pari_err_DOMAIN(const char *f, const char *v, const char *op, GEN l, GEN x);
+INLINE void    pari_err_FILE(const char *f, const char *g);
+INLINE void    pari_err_FLAG(const char *f);
+INLINE void    pari_err_IMPL(const char *f);
+INLINE void    pari_err_INV(const char *f, GEN x);
+INLINE void    pari_err_IRREDPOL(const char *f, GEN x);
+INLINE void    pari_err_MAXPRIME(ulong c);
+INLINE void    pari_err_MODULUS(const char *f, GEN x, GEN y);
+INLINE void    pari_err_OP(const char *f, GEN x, GEN y);
+INLINE void    pari_err_OVERFLOW(const char *f);
+INLINE void    pari_err_PACKAGE(const char *f);
+INLINE void    pari_err_PREC(const char *f);
+INLINE void    pari_err_PRIME(const char *f, GEN x);
+INLINE void    pari_err_PRIORITY(const char *f, GEN x, const char *op, long v);
+INLINE void    pari_err_SQRTN(const char *f, GEN x);
+INLINE void    pari_err_TYPE(const char *f, GEN x);
+INLINE void    pari_err_TYPE2(const char *f, GEN x, GEN y);
+INLINE void    pari_err_VAR(const char *f, GEN x, GEN y);
+INLINE void    pari_err_ROOTS0(const char *f);
diff --git a/src/headers/parierr.h b/src/headers/parierr.h
new file mode 100644
index 0000000..6d6966a
--- /dev/null
+++ b/src/headers/parierr.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+enum err_list {
+/* Force errors into non-0 */
+  e_SYNTAX = 1, e_BUG,
+
+  e_ALARM, e_FILE,
+
+  e_MISC, e_FLAG, e_IMPL, e_ARCH, e_PACKAGE, e_NOTFUNC,
+
+  e_PREC, e_TYPE, e_DIM, e_VAR, e_PRIORITY, e_USER,
+
+  e_STACK, e_OVERFLOW, e_DOMAIN, e_COMPONENT,
+
+  e_MAXPRIME,
+
+  e_CONSTPOL, e_IRREDPOL, e_COPRIME, e_PRIME, e_MODULUS, e_ROOTS0,
+
+  e_OP, e_TYPE2, e_INV,
+
+  e_MEM,
+
+  e_SQRTN,
+
+/* NO ERROR */
+  e_NONE
+};
+
+enum { warner, warnprec, warnfile, warnmem, warnuser };
diff --git a/src/headers/parigen.h b/src/headers/parigen.h
new file mode 100644
index 0000000..5d86649
--- /dev/null
+++ b/src/headers/parigen.h
@@ -0,0 +1,171 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file defines the parameters of the GEN type               */
+
+typedef long *GEN;
+typedef unsigned long pari_ulong;
+#define ulong pari_ulong
+
+#undef ULONG_MAX
+#undef LONG_MAX
+
+#ifdef LONG_IS_64BIT
+#  define BITS_IN_LONG 64
+#  define TWOPOTBITS_IN_LONG 6
+#  define LONG_MAX (9223372036854775807L) /* 2^63-1 */
+#  define SMALL_ULONG(p) ((ulong)p <= 3037000493UL)
+#else
+#  define BITS_IN_LONG 32
+#  define TWOPOTBITS_IN_LONG 5
+#  define LONG_MAX (2147483647L) /* 2^31-1 */
+#  define SMALL_ULONG(p) ((ulong)p <= 46337UL) /* 2p^2 < 2^BITS_IN_LONG */
+#endif
+#define ULONG_MAX (~0x0UL)
+
+#define DEFAULTPREC    (2 + (long)(8/sizeof(long)))
+#define MEDDEFAULTPREC (2 + (long)(16/sizeof(long)))
+#define BIGDEFAULTPREC (2 + (long)(24/sizeof(long)))
+#define LOWDEFAULTPREC  3
+#define EXTRAPRECWORD   1
+#define HIGHBIT (1UL << (BITS_IN_LONG-1))
+#define BITS_IN_HALFULONG (BITS_IN_LONG>>1)
+
+#define LOWMASK ((1UL<<BITS_IN_HALFULONG) - 1)
+#define HIGHMASK (~LOWMASK)
+
+#define HIGHWORD(a) ((a) >> BITS_IN_HALFULONG)
+#define LOWWORD(a) ((a) & LOWMASK)
+
+/* Order of bits in codewords:
+ *  x[0]       TYPBITS, CLONEBIT, LGBITS
+ *  x[1].real  SIGNBITS, EXPOBITS
+ *       int   SIGNBITS, LGBITS
+ *       pol   SIGNBITS, VARNBITS
+ *       ser   SIGNBITS, VARNBITS, VALPBITS
+ *       padic VALPBITS, PRECPBITS */
+#define TYPnumBITS   7
+#define SIGNnumBITS  2
+
+#ifdef LONG_IS_64BIT
+#  define VARNnumBITS 16 /* otherwise MAXVARN too large */
+#else
+#  define VARNnumBITS 14
+#endif
+
+/* no user serviceable parts below :-) */
+#define   LGnumBITS (BITS_IN_LONG - 1 - TYPnumBITS)
+#define VALPnumBITS (BITS_IN_LONG - SIGNnumBITS - VARNnumBITS)
+#define EXPOnumBITS (BITS_IN_LONG - SIGNnumBITS)
+#define PRECPSHIFT VALPnumBITS
+#define  VARNSHIFT VALPnumBITS
+#define   TYPSHIFT (BITS_IN_LONG - TYPnumBITS)
+#define  SIGNSHIFT (BITS_IN_LONG - SIGNnumBITS)
+
+#define EXPOBITS    ((1UL<<EXPOnumBITS)-1)
+#define SIGNBITS    (~((1UL<<SIGNSHIFT) - 1))
+#define  TYPBITS    (~((1UL<< TYPSHIFT) - 1))
+#define PRECPBITS   (~VALPBITS)
+#define LGBITS      ((1UL<<LGnumBITS)-1)
+#define VALPBITS    ((1UL<<VALPnumBITS)-1)
+#define VARNBITS    (MAXVARN<<VARNSHIFT)
+#define MAXVARN     ((1UL<<VARNnumBITS)-1)
+#define NO_VARIABLE (2147483647L) /* > MAXVARN */
+
+#define HIGHEXPOBIT (1UL<<(EXPOnumBITS-1))
+#define HIGHVALPBIT (1UL<<(VALPnumBITS-1))
+#define CLONEBIT    (1UL<<LGnumBITS)
+
+#define evaltyp(x)    (((ulong)(x)) << TYPSHIFT)
+#define evalvarn(x)   (((ulong)(x)) << VARNSHIFT)
+#define evalsigne(x)  ((ulong)(((long)(x)) << SIGNSHIFT))
+#define _evalexpo(x)  (HIGHEXPOBIT + (x))
+#define _evalvalp(x)  (HIGHVALPBIT + (x))
+#define _evalprecp(x) (((long)(x)) << PRECPSHIFT)
+#define evallgefint(x)  (x)
+#define evallgeflist(x) (x)
+#define _evallg(x)    (x)
+
+#define typ(x)        ((long)(((ulong)((x)[0])) >> TYPSHIFT))
+#define settyp(x,s)   (((ulong*)(x))[0]=\
+                        (((ulong*)(x))[0]&(~TYPBITS)) | evaltyp(s))
+
+#define isclone(x)    (((ulong*) (x))[0] & CLONEBIT)
+#define setisclone(x) (((ulong*) (x))[0] |= CLONEBIT)
+#define unsetisclone(x) (((ulong*) (x))[0] &= (~CLONEBIT))
+
+#define lg(x)         ((long)(((ulong)((x)[0])) & LGBITS))
+#define setlg(x,s)    (((ulong*)(x))[0]=\
+                      (((ulong*)(x))[0]&(~LGBITS)) | evallg(s))
+
+#define signe(x)      (((long)((x)[1])) >> SIGNSHIFT)
+#define setsigne(x,s) (((ulong*)(x))[1]=\
+                        (((ulong*)(x))[1]&(~SIGNBITS)) | (ulong)evalsigne(s))
+
+#define lgefint(x)      ((long)(((ulong)((x)[1])) & LGBITS))
+#define setlgefint(x,s) (((ulong*)(x))[1]=\
+                          (((ulong*)(x))[1]&(~LGBITS)) | (ulong)evallgefint(s))
+
+#define realprec(x)   ((long)(((ulong)((x)[0])) & LGBITS))
+#define setprec(x,s)  (((ulong*)(x))[0]=\
+                      (((ulong*)(x))[0]&(~LGBITS)) | evallg(s))
+#define incrprec(x)   ((x)++)
+
+#define expo(x)       ((long) ((((ulong*)(x))[1] & EXPOBITS) - HIGHEXPOBIT))
+#define setexpo(x,s)  (((ulong*)(x))[1]=\
+                       (((ulong*)(x))[1]&(~EXPOBITS)) | (ulong)evalexpo(s))
+
+#define valp(x)       ((long) ((((ulong*)(x))[1] & VALPBITS) - HIGHVALPBIT))
+#define setvalp(x,s)  (((ulong*)(x))[1]=\
+                       (((ulong*)(x))[1]&(~VALPBITS)) | (ulong)evalvalp(s))
+
+#define precp(x)      ((long) (((ulong*)(x))[1] >> PRECPSHIFT))
+#define setprecp(x,s) (((ulong*)(x))[1]=\
+                       (((ulong*)(x))[1]&(~PRECPBITS)) | (ulong)evalprecp(s))
+
+#define varn(x)       ((long)((((ulong*)(x))[1]&VARNBITS) >> VARNSHIFT))
+#define setvarn(x,s)  (((ulong*)(x))[1]=\
+                       (((ulong*)(x))[1]&(~VARNBITS)) | (ulong)evalvarn(s))
+
+#define varncmp(x,y)  ((x)-(y))
+
+/* t_LIST */
+#define list_nmax(x) ((GEN)x)[1]
+#define list_data(x) ((GEN*)x)[2]
+
+/* DO NOT REORDER THESE
+ * actual values can be changed. Adapt lontyp in gen2.c */
+enum {
+  t_INT    =  1,
+  t_REAL   =  2,
+  t_INTMOD =  3,
+  t_FRAC   =  4,
+  t_FFELT  =  5,
+  t_COMPLEX=  6,
+  t_PADIC  =  7,
+  t_QUAD   =  8,
+  t_POLMOD =  9,
+  t_POL    =  10,
+  t_SER    =  11,
+  t_RFRAC  =  13,
+  t_QFR    =  15,
+  t_QFI    =  16,
+  t_VEC    =  17,
+  t_COL    =  18,
+  t_MAT    =  19,
+  t_LIST   =  20,
+  t_STR    =  21,
+  t_VECSMALL= 22,
+  t_CLOSURE = 23,
+  t_ERROR   = 24
+};
diff --git a/src/headers/pariinl.h b/src/headers/pariinl.h
new file mode 100644
index 0000000..d1c929d
--- /dev/null
+++ b/src/headers/pariinl.h
@@ -0,0 +1,2592 @@
+/* Copyright (C) 2000-2010  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                          CONSTRUCTORS                           */
+/*                                                                 */
+/*******************************************************************/
+#define retmkfrac(x,y)\
+  do { GEN _v = cgetg(3, t_FRAC);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y); return _v; } while(0)
+#define retmkintmod(x,y)\
+  do { GEN _v = cgetg(3, t_INTMOD);\
+       gel(_v,1) = (y);\
+       gel(_v,2) = (x); return _v; } while(0)
+#define retmkcomplex(x,y)\
+  do { GEN _v = cgetg(3, t_COMPLEX);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y); return _v; } while(0)
+#define retmkpolmod(x,y)\
+  do { GEN _v = cgetg(3, t_POLMOD);\
+       gel(_v,1) = (y);\
+       gel(_v,2) = (x); return _v; } while(0)
+#define retmkvec(x)\
+  do { GEN _v = cgetg(2, t_VEC);\
+       gel(_v,1) = (x); return _v; } while(0)
+#define retmkvec2(x,y)\
+  do { GEN _v = cgetg(3, t_VEC);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y); return _v; } while(0)
+#define retmkvec3(x,y,z)\
+  do { GEN _v = cgetg(4, t_VEC);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z); return _v; } while(0)
+#define retmkqfi(x,y,z)\
+  do { GEN _v = cgetg(4, t_QFI);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z); return _v; } while(0)
+#define retmkqfr(x,y,z,d)\
+  do { GEN _v = cgetg(5, t_QFR);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (d); return _v; } while(0)
+#define retmkquad(x,y,z)\
+  do { GEN _v = cgetg(4, t_QUAD);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z); return _v; } while(0)
+#define retmkvec4(x,y,z,t)\
+  do { GEN _v = cgetg(5, t_VEC);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t); return _v; } while(0)
+#define retmkvec5(x,y,z,t,u)\
+  do { GEN _v = cgetg(6, t_VEC);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t);\
+       gel(_v,5) = (u); return _v; } while(0)
+#define retmkcol(x)\
+  do { GEN _v = cgetg(2, t_COL);\
+       gel(_v,1) = (x); return _v; } while(0)
+#define retmkcol2(x,y)\
+  do { GEN _v = cgetg(3, t_COL);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y); return _v; } while(0)
+#define retmkcol3(x,y,z)\
+  do { GEN _v = cgetg(4, t_COL);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z); return _v; } while(0)
+#define retmkcol4(x,y,z,t)\
+  do { GEN _v = cgetg(5, t_COL);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t); return _v; } while(0)
+#define retmkcol5(x,y,z,t,u)\
+  do { GEN _v = cgetg(6, t_COL);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t);\
+       gel(_v,5) = (u); return _v; } while(0)
+#define retmkmat(x)\
+  do { GEN _v = cgetg(2, t_MAT);\
+       gel(_v,1) = (x); return _v; } while(0)
+#define retmkmat2(x,y)\
+  do { GEN _v = cgetg(3, t_MAT);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y); return _v; } while(0)
+#define retmkmat3(x,y,z)\
+  do { GEN _v = cgetg(4, t_MAT);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z); return _v; } while(0)
+#define retmkmat4(x,y,z,t)\
+  do { GEN _v = cgetg(5, t_MAT);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t); return _v; } while(0)
+#define retmkmat5(x,y,z,t,u)\
+  do { GEN _v = cgetg(6, t_MAT);\
+       gel(_v,1) = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t);\
+       gel(_v,5) = (u); return _v; } while(0)
+
+INLINE GEN
+mkintmod(GEN x, GEN y) { retmkintmod(x,y); }
+INLINE GEN
+mkintmodu(ulong x, ulong y) {
+  GEN v = cgetg(3,t_INTMOD);
+  gel(v,1) = utoipos(y);
+  gel(v,2) = utoi(x); return v;
+}
+INLINE GEN
+mkpolmod(GEN x, GEN y) { retmkpolmod(x,y); }
+INLINE GEN
+mkfrac(GEN x, GEN y) { retmkfrac(x,y); }
+INLINE GEN
+mkfraccopy(GEN x, GEN y) { retmkfrac(icopy(x), icopy(y)); }
+INLINE GEN
+mkrfrac(GEN x, GEN y) { GEN v = cgetg(3, t_RFRAC);
+  gel(v,1) = x; gel(v,2) = y; return v; }
+INLINE GEN
+mkrfraccopy(GEN x, GEN y) { GEN v = cgetg(3, t_RFRAC);
+  gel(v,1) = gcopy(x); gel(v,2) = gcopy(y); return v; }
+INLINE GEN
+mkcomplex(GEN x, GEN y) { retmkcomplex(x,y); }
+INLINE GEN
+gen_I(void) { return mkcomplex(gen_0, gen_1); }
+INLINE GEN
+cgetc(long l) { retmkcomplex(cgetr(l), cgetr(l)); }
+INLINE GEN
+mkquad(GEN n, GEN x, GEN y) { GEN v = cgetg(4, t_QUAD);
+  gel(v,1) = n; gel(v,2) = x; gel(v,3) = y; return v; }
+/* vecsmall */
+INLINE GEN
+mkvecsmall(long x) { GEN v = cgetg(2, t_VECSMALL); v[1] = x; return v; }
+INLINE GEN
+mkvecsmall2(long x,long y) { GEN v = cgetg(3, t_VECSMALL);
+  v[1]=x; v[2]=y; return v; }
+INLINE GEN
+mkvecsmall3(long x,long y,long z) { GEN v = cgetg(4, t_VECSMALL);
+  v[1]=x; v[2]=y; v[3]=z; return v; }
+INLINE GEN
+mkvecsmall4(long x,long y,long z,long t) { GEN v = cgetg(5, t_VECSMALL);
+  v[1]=x; v[2]=y; v[3]=z; v[4]=t; return v; }
+
+INLINE GEN
+mkqfi(GEN x, GEN y, GEN z) { retmkqfi(x,y,z); }
+/* vec */
+INLINE GEN
+mkvec(GEN x) { retmkvec(x); }
+INLINE GEN
+mkvec2(GEN x, GEN y) { retmkvec2(x,y); }
+INLINE GEN
+mkvec3(GEN x, GEN y, GEN z) { retmkvec3(x,y,z); }
+INLINE GEN
+mkvec4(GEN x, GEN y, GEN z, GEN t) { retmkvec4(x,y,z,t); }
+INLINE GEN
+mkvec5(GEN x, GEN y, GEN z, GEN t, GEN u) { retmkvec5(x,y,z,t,u); }
+INLINE GEN
+mkvecs(long x) { retmkvec(stoi(x)); }
+INLINE GEN
+mkvec2s(long x, long y) { retmkvec2(stoi(x),stoi(y)); }
+INLINE GEN
+mkvec3s(long x, long y, long z) { retmkvec3(stoi(x),stoi(y),stoi(z)); }
+INLINE GEN
+mkvec4s(long x, long y, long z, long t) { retmkvec4(stoi(x),stoi(y),stoi(z),stoi(t)); }
+INLINE GEN
+mkveccopy(GEN x) { GEN v = cgetg(2, t_VEC); gel(v,1) = gcopy(x); return v; }
+INLINE GEN
+mkvec2copy(GEN x, GEN y) {
+  GEN v = cgetg(3,t_VEC); gel(v,1) = gcopy(x); gel(v,2) = gcopy(y); return v; }
+/* col */
+INLINE GEN
+mkcol(GEN x) { retmkcol(x); }
+INLINE GEN
+mkcol2(GEN x, GEN y) { retmkcol2(x,y); }
+INLINE GEN
+mkcol3(GEN x, GEN y, GEN z) { retmkcol3(x,y,z); }
+INLINE GEN
+mkcol4(GEN x, GEN y, GEN z, GEN t) { retmkcol4(x,y,z,t); }
+INLINE GEN
+mkcol5(GEN x, GEN y, GEN z, GEN t, GEN u) { retmkcol5(x,y,z,t,u); }
+INLINE GEN
+mkcols(long x) { retmkcol(stoi(x)); }
+INLINE GEN
+mkcol2s(long x, long y) { retmkcol2(stoi(x),stoi(y)); }
+INLINE GEN
+mkcol3s(long x, long y, long z) { retmkcol3(stoi(x),stoi(y),stoi(z)); }
+INLINE GEN
+mkcol4s(long x, long y, long z, long t) { retmkcol4(stoi(x),stoi(y),stoi(z),stoi(t)); }
+INLINE GEN
+mkcolcopy(GEN x) { GEN v = cgetg(2, t_COL); gel(v,1) = gcopy(x); return v; }
+/* mat */
+INLINE GEN
+mkmat(GEN x) { retmkmat(x); }
+INLINE GEN
+mkmat2(GEN x, GEN y) { retmkmat2(x,y); }
+INLINE GEN
+mkmat3(GEN x, GEN y, GEN z) { retmkmat3(x,y,z); }
+INLINE GEN
+mkmat4(GEN x, GEN y, GEN z, GEN t) { retmkmat4(x,y,z,t); }
+INLINE GEN
+mkmat5(GEN x, GEN y, GEN z, GEN t, GEN u) { retmkmat5(x,y,z,t,u); }
+INLINE GEN
+mkmatcopy(GEN x) { GEN v = cgetg(2, t_MAT); gel(v,1) = gcopy(x); return v; }
+INLINE GEN
+mkerr(long x) { GEN v = cgetg(2, t_ERROR); v[1] = x; return v; }
+/* pol */
+INLINE GEN
+pol_x(long v) {
+  GEN p = cgetg(4, t_POL);
+  p[1] = evalsigne(1)|evalvarn(v);
+  gel(p,2) = gen_0;
+  gel(p,3) = gen_1; return p;
+}
+INLINE GEN
+pol_1(long v) {
+  GEN p = cgetg(3, t_POL);
+  p[1] = evalsigne(1)|evalvarn(v);
+  gel(p,2) = gen_1; return p;
+}
+INLINE GEN
+pol_0(long v)
+{
+  GEN x = cgetg(2,t_POL);
+  x[1] = evalvarn(v); return x;
+}
+#define retconst_vec(n,x)\
+  do { long _i, _n = (n);\
+       GEN _v = cgetg(n+1, t_VEC), _x = (x);\
+       for (_i = 1; _i <= _n; _i++) gel(_v,_i) = _x;\
+       return _v; } while(0)
+INLINE GEN
+const_vec(long n, GEN x) { retconst_vec(n, x); }
+#define retconst_col(n,x)\
+  do { long _i, _n = (n);\
+       GEN _v = cgetg(n+1, t_COL), _x = (x);\
+       for (_i = 1; _i <= _n; _i++) gel(_v,_i) = _x;\
+       return _v; } while(0)
+INLINE GEN
+const_col(long n, GEN x) { retconst_col(n, x); }
+INLINE GEN
+const_vecsmall(long n, long c)
+{
+  long i;
+  GEN V = cgetg(n+1,t_VECSMALL);
+  for(i=1;i<=n;i++) V[i] = c;
+  return V;
+}
+
+/***   ZERO   ***/
+/* O(p^e) */
+INLINE GEN
+zeropadic(GEN p, long e)
+{
+  GEN y = cgetg(5,t_PADIC);
+  gel(y,4) = gen_0;
+  gel(y,3) = gen_1;
+  gel(y,2) = icopy(p);
+  y[1] = evalvalp(e) | _evalprecp(0);
+  return y;
+}
+INLINE GEN
+zeropadic_shallow(GEN p, long e)
+{
+  GEN y = cgetg(5,t_PADIC);
+  gel(y,4) = gen_0;
+  gel(y,3) = gen_1;
+  gel(y,2) = p;
+  y[1] = evalvalp(e) | _evalprecp(0);
+  return y;
+}
+/* O(pol_x(v)^e) */
+INLINE GEN
+zeroser(long v, long e)
+{
+  GEN x = cgetg(2, t_SER);
+  x[1] = evalvalp(e) | evalvarn(v); return x;
+}
+/* 0 * pol_x(v) */
+INLINE GEN
+zeropol(long v) { return pol_0(v); }
+/* vector(n) */
+INLINE GEN
+zerocol(long n)
+{
+  GEN y = cgetg(n+1,t_COL);
+  long i; for (i=1; i<=n; i++) gel(y,i) = gen_0;
+  return y;
+}
+/* vectorv(n) */
+INLINE GEN
+zerovec(long n)
+{
+  GEN y = cgetg(n+1,t_VEC);
+  long i; for (i=1; i<=n; i++) gel(y,i) = gen_0;
+  return y;
+}
+/* matrix(m, n) */
+INLINE GEN
+zeromat(long m, long n)
+{
+  GEN y = cgetg(n+1,t_MAT);
+  GEN v = zerocol(m);
+  long i; for (i=1; i<=n; i++) gel(y,i) = v;
+  return y;
+}
+/* = zero_zx, sv is a evalvarn()*/
+INLINE GEN
+zero_Flx(long sv) { return pol0_Flx(sv); }
+INLINE GEN
+zero_Flv(long n)
+{
+  GEN y = cgetg(n+1,t_VECSMALL);
+  long i; for (i=1; i<=n; i++) y[i] = 0;
+  return y;
+}
+/* matrix(m, n) */
+INLINE GEN
+zero_Flm(long m, long n)
+{
+  GEN y = cgetg(n+1,t_MAT);
+  GEN v = zero_Flv(m);
+  long i; for (i=1; i<=n; i++) gel(y,i) = v;
+  return y;
+}
+/* matrix(m, n) */
+INLINE GEN
+zero_Flm_copy(long m, long n)
+{
+  GEN y = cgetg(n+1,t_MAT);
+  long i; for (i=1; i<=n; i++) gel(y,i) = zero_Flv(m);
+  return y;
+}
+
+INLINE GEN
+zero_F2v(long m)
+{
+  long l = nbits2nlong(m);
+  GEN v  = zero_Flv(l+1);
+  v[1] = m;
+  return v;
+}
+
+INLINE GEN
+zero_F2m(long m, long n)
+{
+  long i;
+  GEN M = cgetg(n+1, t_MAT);
+  GEN v = zero_F2v(m);
+  for (i = 1; i <= n; i++)
+    gel(M,i) = v;
+  return M;
+}
+
+
+INLINE GEN
+zero_F2m_copy(long m, long n)
+{
+  long i;
+  GEN M = cgetg(n+1, t_MAT);
+  for (i = 1; i <= n; i++)
+    gel(M,i)= zero_F2v(m);
+  return M;
+}
+
+/* matrix(m, n) */
+INLINE GEN
+zeromatcopy(long m, long n)
+{
+  GEN y = cgetg(n+1,t_MAT);
+  long i; for (i=1; i<=n; i++) gel(y,i) = zerocol(m);
+  return y;
+}
+
+/* i-th vector in the standard basis */
+INLINE GEN
+col_ei(long n, long i) { GEN e = zerocol(n); gel(e,i) = gen_1; return e; }
+INLINE GEN
+vec_ei(long n, long i) { GEN e = zerovec(n); gel(e,i) = gen_1; return e; }
+INLINE GEN
+F2v_ei(long n, long i) { GEN e = zero_F2v(n); F2v_set(e,i); return e; }
+INLINE GEN
+vecsmall_ei(long n, long i) { GEN e = zero_zv(n); e[i] = 1; return e; }
+INLINE GEN
+Rg_col_ei(GEN x, long n, long i) { GEN e = zerocol(n); gel(e,i) = x; return e; }
+
+INLINE GEN
+shallowcopy(GEN x)
+{ return typ(x) == t_MAT ? RgM_shallowcopy(x): leafcopy(x); }
+
+/* routines for naive growarrays */
+INLINE GEN
+vectrunc_init(long l)
+{
+  GEN z = new_chunk(l);
+  z[0] = evaltyp(t_VEC) | _evallg(1); return z;
+}
+INLINE void
+vectrunc_append(GEN x, GEN t)
+{ long l = lg(x); gel(x,l) = t; setlg(x, l+1); }
+INLINE void
+vectrunc_append_batch(GEN x, GEN y)
+{
+  long i, l = lg(x), ly = lg(y);
+  GEN z = x + l-1;
+  for (i = 1; i < ly; i++) gel(z,i) = gel(y,i);
+  setlg(x, l+ly-1);
+}
+INLINE GEN
+vecsmalltrunc_init(long l)
+{
+  GEN z = new_chunk(l);
+  z[0] = evaltyp(t_VECSMALL) | _evallg(1); return z;
+}
+INLINE void
+vecsmalltrunc_append(GEN x, long t)
+{ long l = lg(x); x[l] = t; setlg(x, l+1); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                        VEC / COL / VECSMALL                     */
+/*                                                                 */
+/*******************************************************************/
+/* shallow*/
+INLINE GEN
+vec_shorten(GEN v, long n)
+{
+  long i;
+  GEN V = cgetg(n+1,t_VEC);
+  for(i=1;i<=n;i++) gel(V,i) = gel(v,i);
+  return V;
+}
+/* shallow*/
+INLINE GEN
+vec_lengthen(GEN v, long n)
+{
+  long i;
+  long l=lg(v);
+  GEN V = cgetg(n+1,t_VEC);
+  for(i=1;i<l;i++) gel(V,i) = gel(v,i);
+  return V;
+}
+/* shallow*/
+INLINE GEN
+vec_setconst(GEN v, GEN x)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++) gel(v,i) = x;
+  return v;
+}
+INLINE GEN
+vecsmall_shorten(GEN v, long n)
+{
+  long i;
+  GEN V = cgetg(n+1,t_VECSMALL);
+  for(i=1;i<=n;i++) V[i] = v[i];
+  return V;
+}
+INLINE GEN
+vecsmall_lengthen(GEN v, long n)
+{
+  long i, l = lg(v);
+  GEN V = cgetg(n+1,t_VECSMALL);
+  for(i=1;i<l;i++) V[i] = v[i];
+  return V;
+}
+
+INLINE GEN
+vec_to_vecsmall(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l, t_VECSMALL);
+  for (i=1; i<l; i++) x[i] = itos(gel(z,i));
+  return x;
+}
+INLINE GEN
+vecsmall_to_vec(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_VEC);
+  for (i=1; i<l; i++) gel(x,i) = stoi(z[i]);
+  return x;
+}
+INLINE GEN
+vecsmall_to_col(GEN z)
+{
+  long i, l = lg(z);
+  GEN x = cgetg(l,t_COL);
+  for (i=1; i<l; i++) gel(x,i) = stoi(z[i]);
+  return x;
+}
+
+INLINE int
+vecsmall_lexcmp(GEN x, GEN y)
+{
+  long lx,ly,l,i;
+  lx = lg(x);
+  ly = lg(y); l = minss(lx,ly);
+  for (i=1; i<l; i++)
+    if (x[i] != y[i]) return x[i]<y[i]? -1: 1;
+  if (lx == ly) return 0;
+  return (lx < ly)? -1 : 1;
+}
+
+INLINE int
+vecsmall_prefixcmp(GEN x, GEN y)
+{
+  long i, lx = lg(x), ly = lg(y), l = minss(lx,ly);
+  for (i=1; i<l; i++)
+    if (x[i] != y[i]) return x[i]<y[i]? -1: 1;
+  return 0;
+}
+
+/*Can be used on t_VEC, but coeffs not gcopy-ed*/
+INLINE GEN
+vecsmall_prepend(GEN V, long s)
+{
+  long i, l2 = lg(V);
+  GEN res = cgetg(l2+1, typ(V));
+  res[1] = s;
+  for (i = 2; i <= l2; ++i) res[i] = V[i - 1];
+  return res;
+}
+
+/*Can be used on t_VEC, but coeffs not gcopy-ed*/
+INLINE GEN
+vecsmall_append(GEN V, long s)
+{
+  long i, l2 = lg(V);
+  GEN res = cgetg(l2+1, typ(V));
+  for (i = 1; i < l2; ++i) res[i] = V[i];
+  res[l2] = s; return res;
+}
+
+INLINE GEN
+vecsmall_concat(GEN u, GEN v)
+{
+  long i, l1 = lg(u)-1, l2 = lg(v)-1;
+  GEN res = cgetg(l1+l2+1, t_VECSMALL);
+  for (i = 1; i <= l1; ++i) res[i]    = u[i];
+  for (i = 1; i <= l2; ++i) res[i+l1] = v[i];
+  return res;
+}
+
+/* return the number of indices where u and v are equal */
+INLINE long
+vecsmall_coincidence(GEN u, GEN v)
+{
+  long i, s = 0, l = minss(lg(u),lg(v));
+  for(i=1; i<l; i++)
+    if(u[i] == v[i]) s++;
+  return s;
+}
+
+/* returns the first index i<=n such that x=v[i] if it exists, 0 otherwise */
+INLINE long
+vecsmall_isin(GEN v, long x)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+    if (v[i] == x) return i;
+  return 0;
+}
+
+INLINE long
+vecsmall_pack(GEN V, long base, long mod)
+{
+  long i, s = 0;
+  for(i=1; i<lg(V); i++) s = (base*s + V[i]) % mod;
+  return s;
+}
+
+INLINE long
+vecsmall_indexmax(GEN x)
+{
+  long i, i0 = 1, t = x[1], lx = lg(x);
+  for (i=2; i<lx; i++)
+    if (x[i] > t) t = x[i0=i];
+  return i0;
+}
+
+INLINE long
+vecsmall_max(GEN x)
+{
+  long i, t = x[1], lx = lg(x);
+  for (i=2; i<lx; i++)
+    if (x[i] > t) t = x[i];
+  return t;
+}
+
+INLINE long
+vecsmall_indexmin(GEN x)
+{
+  long i, i0 = 1, t = x[1], lx =lg(x);
+  for (i=2; i<lx; i++)
+    if (x[i] < t) t = x[i0=i];
+  return i0;
+}
+
+INLINE long
+vecsmall_min(GEN x)
+{
+  long i, t = x[1], lx =lg(x);
+  for (i=2; i<lx; i++)
+    if (x[i] < t) t = x[i];
+  return t;
+}
+
+INLINE int
+ZV_isscalar(GEN x)
+{
+  long l = lg(x);
+  while (--l > 1)
+    if (signe(gel(x, l))) return 0;
+  return 1;
+}
+INLINE int
+QV_isscalar(GEN x)
+{
+  long lx = lg(x),i;
+  for (i=2; i<lx; i++)
+    if (!isintzero(gel(x, i))) return 0;
+  return 1;
+}
+INLINE int
+RgV_isscalar(GEN x)
+{
+  long lx = lg(x),i;
+  for (i=2; i<lx; i++)
+    if (!gequal0(gel(x, i))) return 0;
+  return 1;
+}
+INLINE int
+RgX_isscalar(GEN x)
+{
+  long i;
+  for (i=lg(x)-1; i>2; i--)
+    if (!gequal0(gel(x, i))) return 0;
+  return 1;
+}
+INLINE long
+RgX_equal_var(GEN x, GEN y) { return varn(x) == varn(y) && RgX_equal(x,y); }
+
+INLINE int
+RgX_is_rational(GEN x)
+{
+  long i;
+  for (i = lg(x)-1; i > 1; i--)
+    if (!is_rational_t(typ(gel(x,i)))) return 0;
+  return 1;
+}
+INLINE int
+RgX_is_ZX(GEN x)
+{
+  long i;
+  for (i = lg(x)-1; i > 1; i--)
+    if (typ(gel(x,i)) != t_INT) return 0;
+  return 1;
+}
+INLINE int
+RgX_is_QX(GEN x)
+{
+  long k = lg(x)-1;
+  for ( ; k>1; k--)
+    if (!is_rational_t(typ(gel(x,k)))) return 0;
+  return 1;
+}
+INLINE int
+RgX_is_monomial(GEN x)
+{
+  long i;
+  if (!signe(x)) return 0;
+  for (i=lg(x)-2; i>1; i--)
+    if (!isexactzero(gel(x,i))) return 0;
+  return 1;
+}
+INLINE int
+RgV_is_ZV(GEN x)
+{
+  long i;
+  for (i = lg(x)-1; i > 0; i--)
+    if (typ(gel(x,i)) != t_INT) return 0;
+  return 1;
+}
+INLINE int
+RgV_is_QV(GEN x)
+{
+  long i;
+  for (i = lg(x)-1; i > 0; i--)
+    if (!is_rational_t(typ(gel(x,i)))) return 0;
+  return 1;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**            Dynamic arrays implementation                       **/
+/**                                                                **/
+/********************************************************************/
+INLINE void **
+pari_stack_base(pari_stack *s) { return s->data; }
+
+INLINE void
+pari_stack_init(pari_stack *s, size_t size, void **data)
+{
+  s->data = data;
+  *data = NULL;
+  s->n = 0;
+  s->alloc = 0;
+  s->size = size;
+}
+
+INLINE void
+pari_stack_alloc(pari_stack *s, long nb)
+{
+  void **sdat = pari_stack_base(s);
+  if (s->n+nb <= s->alloc) return;
+  if (!s->alloc)
+    s->alloc = nb;
+  else
+  {
+    while (s->n+nb > s->alloc) s->alloc <<= 1;
+  }
+  *sdat = pari_realloc(*sdat,s->alloc*s->size);
+}
+
+INLINE long
+pari_stack_new(pari_stack *s) { pari_stack_alloc(s, 1); return s->n++; }
+
+INLINE void
+pari_stack_delete(pari_stack *s)
+{
+  void **sdat = pari_stack_base(s);
+  if (*sdat) free(*sdat);
+}
+
+INLINE void
+pari_stack_pushp(pari_stack *s, void *u)
+{
+  long n = pari_stack_new(s);
+  void **sdat =(void**) *pari_stack_base(s);
+  sdat[n] = u;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            EXTRACT                              */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+vecslice(GEN A, long y1, long y2)
+{
+  long i,lB = y2 - y1 + 2;
+  GEN B = cgetg(lB, typ(A));
+  for (i=1; i<lB; i++) B[i] = A[y1-1+i];
+  return B;
+}
+INLINE GEN
+vecslicepermute(GEN A, GEN p, long y1, long y2)
+{
+  long i,lB = y2 - y1 + 2;
+  GEN B = cgetg(lB, typ(A));
+  for (i=1; i<lB; i++) B[i] = A[p[y1-1+i]];
+  return B;
+}
+/* rowslice(rowpermute(A,p), x1, x2) */
+INLINE GEN
+rowslicepermute(GEN A, GEN p, long x1, long x2)
+{
+  long i, lB = lg(A);
+  GEN B = cgetg(lB, typ(A));
+  for (i=1; i<lB; i++) gel(B,i) = vecslicepermute(gel(A,i),p,x1,x2);
+  return B;
+}
+INLINE GEN
+rowslice(GEN A, long x1, long x2)
+{
+  long i, lB = lg(A);
+  GEN B = cgetg(lB, typ(A));
+  for (i=1; i<lB; i++) gel(B,i) = vecslice(gel(A,i),x1,x2);
+  return B;
+}
+
+/* shallow, remove coeff of index j */
+INLINE GEN
+vecsplice(GEN a, long j)
+{
+  long i, k, l = lg(a);
+  GEN b;
+  if (l == 1) pari_err(e_MISC, "incorrect component in vecsplice");
+  b = cgetg(l-1, typ(a));
+  for (i = k = 1; i < l; i++)
+    if (i != j) gel(b, k++) = gel(a,i);
+  return b;
+}
+/* shallow */
+INLINE GEN
+RgM_minor(GEN a, long i, long j)
+{
+  GEN b = vecsplice(a, j);
+  long k, l = lg(b);
+  for (k = 1; k < l; k++) gel(b,k) = vecsplice(gel(b,k), i);
+  return b;
+}
+
+/* A[x0,] */
+INLINE GEN
+row(GEN A, long x0)
+{
+  long i, lB = lg(A);
+  GEN B  = cgetg(lB, t_VEC);
+  for (i=1; i<lB; i++) gel(B, i) = gcoeff(A, x0, i);
+  return B;
+}
+INLINE GEN
+row_Flm(GEN A, long x0)
+{
+  long i, lB = lg(A);
+  GEN B  = cgetg(lB, t_VECSMALL);
+  for (i=1; i<lB; i++) B[i] = coeff(A, x0, i);
+  return B;
+}
+/* A[x0,] */
+INLINE GEN
+rowcopy(GEN A, long x0)
+{
+  long i, lB = lg(A);
+  GEN B  = cgetg(lB, t_VEC);
+  for (i=1; i<lB; i++) gel(B, i) = gcopy(gcoeff(A, x0, i));
+  return B;
+}
+/* A[x0, x1..x2] */
+INLINE GEN
+row_i(GEN A, long x0, long x1, long x2)
+{
+  long i, lB = x2 - x1 + 2;
+  GEN B  = cgetg(lB, t_VEC);
+  for (i=x1; i<=x2; i++) gel(B, i) = gcoeff(A, x0, i);
+  return B;
+}
+
+INLINE GEN
+vecreverse(GEN A)
+{
+  long i,l = lg(A);
+  GEN B = cgetg(l, typ(A));
+  for (i=1; i<l; i++) gel(B, i) = gel(A, l-i);
+  return B;
+}
+
+INLINE GEN
+vecpermute(GEN A, GEN p)
+{
+  long i,lB = lg(p);
+  GEN B = cgetg(lB, typ(A));
+  for (i=1; i<lB; i++) gel(B, i) = gel(A, p[i]);
+  return B;
+}
+INLINE GEN
+rowpermute(GEN A, GEN p)
+{
+  long i, lB = lg(A);
+  GEN B = cgetg(lB, typ(A));
+  for (i=1; i<lB; i++) gel(B, i) = vecpermute(gel(A, i), p);
+  return B;
+}
+/*******************************************************************/
+/*                                                                 */
+/*                          PERMUTATIONS                           */
+/*                                                                 */
+/*******************************************************************/
+/* identity permutation */
+INLINE GEN
+identity_perm(long n)
+{
+  GEN perm = cgetg(n+1, t_VECSMALL);
+  long i;
+  for (i = 1; i <= n; i++) perm[i] = i;
+  return perm;
+}
+
+/* assume d <= n */
+INLINE GEN
+cyclic_perm(long n, long d)
+{
+  GEN perm = cgetg(n+1, t_VECSMALL);
+  long i;
+  for (i = 1; i <= n-d; i++) perm[i] = i+d;
+  for (     ; i <= n;   i++) perm[i] = i-n+d;
+  return perm;
+}
+
+/* Multiply (compose) two permutations */
+INLINE GEN
+perm_mul(GEN s, GEN t)
+{
+  GEN u;
+  long i, l = lg(t);
+  u = cgetg(l, t_VECSMALL);
+  for (i = 1; i < l; i++) u[i] = s[ t[i] ];
+  return u;
+}
+/* Compute the inverse (reciprocal) of a permutation. */
+INLINE GEN
+perm_inv(GEN x)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x, &lx);
+  for (i=1; i<lx; i++) y[ x[i] ] = i;
+  return y;
+}
+/* Return s*t*s^-1 */
+INLINE GEN
+perm_conj(GEN s, GEN t)
+{
+  long i, l;
+  GEN v = cgetg_copy(s, &l);
+  for (i = 1; i < l; i++) v[ s[i] ] = s[ t[i] ];
+  return v;
+}
+
+/*********************************************************************/
+/*                       MALLOC/FREE WRAPPERS                        */
+/*********************************************************************/
+#define BLOCK_SIGALRM_START          \
+{                                    \
+  int block=PARI_SIGINT_block;       \
+  PARI_SIGINT_block = 2;             \
+  MT_SIGINT_BLOCK(block);
+
+#define BLOCK_SIGINT_START           \
+{                                    \
+  int block=PARI_SIGINT_block;       \
+  PARI_SIGINT_block = 1;             \
+  MT_SIGINT_BLOCK(block);
+
+#define BLOCK_SIGINT_END             \
+  PARI_SIGINT_block = block;         \
+  MT_SIGINT_UNBLOCK(block);          \
+  if (!block && PARI_SIGINT_pending) \
+  {                                  \
+    int sig = PARI_SIGINT_pending;   \
+    PARI_SIGINT_pending = 0;         \
+    raise(sig);                      \
+  }                                  \
+}
+
+INLINE void
+pari_free(void *pointer)
+{
+  BLOCK_SIGINT_START;
+  free(pointer);
+  BLOCK_SIGINT_END;
+}
+INLINE void*
+pari_malloc(size_t size)
+{
+  if (size)
+  {
+    char *tmp;
+    BLOCK_SIGINT_START;
+    tmp = (char*)malloc(size);
+    BLOCK_SIGINT_END;
+    if (!tmp) pari_err(e_MEM);
+    return tmp;
+  }
+  if (DEBUGMEM) pari_warn(warner,"mallocing NULL object");
+  return NULL;
+}
+INLINE void*
+pari_realloc(void *pointer, size_t size)
+{
+  char *tmp;
+
+  BLOCK_SIGINT_START;
+  if (!pointer) tmp = (char *) malloc(size);
+  else tmp = (char *) realloc(pointer,size);
+  BLOCK_SIGINT_END;
+  if (!tmp) pari_err(e_MEM);
+  return tmp;
+}
+INLINE void*
+pari_calloc(size_t size)
+{
+  void *t = pari_malloc(size);
+  memset(t, 0, size); return t;
+}
+INLINE GEN
+cgetalloc(long t, size_t l)
+{
+  GEN x = (GEN)pari_malloc(l * sizeof(long));
+  x[0] = evaltyp(t) | evallg(l); return x;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                       GARBAGE COLLECTION                        */
+/*                                                                 */
+/*******************************************************************/
+/* copy integer x as if we had avma = av */
+INLINE GEN
+icopy_avma(GEN x, pari_sp av)
+{
+  long i = lgefint(x), lx = i;
+  GEN y = ((GEN)av) - i;
+  while (--i > 0) y[i] = x[i];
+  y[0] = evaltyp(t_INT)|evallg(lx);
+  return y;
+}
+/* copy leaf x as if we had avma = av */
+INLINE GEN
+leafcopy_avma(GEN x, pari_sp av)
+{
+  long i = lg(x);
+  GEN y = ((GEN)av) - i;
+  while (--i > 0) y[i] = x[i];
+  y[0] = x[0] & (~CLONEBIT);
+  return y;
+}
+INLINE GEN
+gerepileuptoleaf(pari_sp av, GEN x)
+{
+  long lx;
+  GEN q;
+
+  if (!isonstack(x) || (GEN)av<=x) { avma = av; return x; }
+  lx = lg(x);
+  q = ((GEN)av) - lx;
+  avma = (pari_sp)q;
+  while (--lx >= 0) q[lx] = x[lx];
+  return q;
+}
+INLINE GEN
+gerepileuptoint(pari_sp av, GEN x)
+{
+  if (!isonstack(x) || (GEN)av<=x) { avma = av; return x; }
+  avma = (pari_sp)icopy_avma(x, av);
+  return (GEN)avma;
+}
+INLINE GEN
+gerepileupto(pari_sp av, GEN x)
+{
+  if (!isonstack(x) || (GEN)av<=x) { avma = av; return x; }
+  switch(typ(x))
+  { /* non-default = !is_recursive_t(tq) */
+    case t_INT: return gerepileuptoint(av, x);
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL: return gerepileuptoleaf(av,x);
+    default:
+      /* NB: x+i --> ((long)x) + i*sizeof(long) */
+      return gerepile(av, (pari_sp) (x+lg(x)), x);
+  }
+}
+
+/* gerepileupto(av, gcopy(x)) */
+INLINE GEN
+gerepilecopy(pari_sp av, GEN x)
+{
+  if (is_recursive_t(typ(x)))
+  {
+    GENbin *p = copy_bin(x);
+    avma = av; return bin_copy(p);
+  }
+  else
+  {
+    avma = av;
+    if (x < (GEN)av) {
+      if (x < (GEN)bot) new_chunk(lg(x));
+      x = leafcopy_avma(x, av);
+      avma = (pari_sp)x;
+    } else
+      x = leafcopy(x);
+    return x;
+  }
+}
+
+/* Takes an array of pointers to GENs, of length n. Copies all
+ * objects to contiguous locations and cleans up the stack between
+ * av and avma. */
+INLINE void
+gerepilemany(pari_sp av, GEN* gptr[], int n)
+{
+  int i;
+  for (i=0; i<n; i++) *gptr[i] = (GEN)copy_bin(*gptr[i]);
+  avma = av;
+  for (i=0; i<n; i++) *gptr[i] = bin_copy((GENbin*)*gptr[i]);
+}
+
+INLINE void
+gerepileall(pari_sp av, int n, ...)
+{
+  int i;
+  va_list a; va_start(a, n);
+  if (n < 10)
+  {
+    GEN *gptr[10];
+    for (i=0; i<n; i++)
+    { gptr[i] = va_arg(a,GEN*); *gptr[i] = (GEN)copy_bin(*gptr[i]); }
+    avma = av;
+    for (--i; i>=0; i--) *gptr[i] = bin_copy((GENbin*)*gptr[i]);
+
+  }
+  else
+  {
+    GEN **gptr = (GEN**)  pari_malloc(n*sizeof(GEN*));
+    for (i=0; i<n; i++)
+    { gptr[i] = va_arg(a,GEN*); *gptr[i] = (GEN)copy_bin(*gptr[i]); }
+    avma = av;
+    for (--i; i>=0; i--) *gptr[i] = bin_copy((GENbin*)*gptr[i]);
+    pari_free(gptr);
+  }
+}
+
+INLINE void
+gerepilecoeffs(pari_sp av, GEN x, int n)
+{
+  int i;
+  for (i=0; i<n; i++) gel(x,i) = (GEN)copy_bin(gel(x,i));
+  avma = av;
+  for (i=0; i<n; i++) gel(x,i) = bin_copy((GENbin*)x[i]);
+}
+
+/* p from copy_bin. Copy p->x back to stack, then destroy p */
+INLINE GEN
+bin_copy(GENbin *p)
+{
+  GEN x, y, base;
+  long dx, len;
+
+  x   = p->x; if (!x) { pari_free(p); return gen_0; }
+  len = p->len;
+  base= p->base; dx = x - base;
+  y = (GEN)memcpy((void*)new_chunk(len), (void*)GENbinbase(p), len*sizeof(long));
+  y += dx;
+  if (p->canon)
+    shiftaddress_canon(y, (y-x)*sizeof(long));
+  else
+    shiftaddress(y, (y-x)*sizeof(long));
+  pari_free(p); return y;
+}
+
+INLINE GEN
+GENbinbase(GENbin *p) { return (GEN)(p + 1); }
+
+INLINE void
+cgiv(GEN x)
+{
+  pari_sp av = (pari_sp)(x+lg(x));
+  if (isonstack((GEN)av)) avma = av;
+}
+
+INLINE void
+killblock(GEN x) { gunclone(x); }
+
+INLINE int
+is_universal_constant(GEN x) { return (x >= gen_0 && x <= ghalf); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                    CONVERSION / ASSIGNMENT                      */
+/*                                                                 */
+/*******************************************************************/
+/* z is a type which may be a t_COMPLEX component (not a t_QUAD) */
+INLINE GEN
+cxcompotor(GEN z, long prec)
+{
+  switch(typ(z))
+  {
+    case t_INT:  return itor(z, prec);
+    case t_FRAC: return fractor(z, prec);
+    case t_REAL: return rtor(z, prec);
+    default: pari_err_TYPE("cxcompotor",z); return NULL; /* not reached */
+  }
+}
+INLINE GEN
+cxtofp(GEN x, long prec)
+{ retmkcomplex(cxcompotor(gel(x,1),prec), cxcompotor(gel(x,2),prec)); }
+
+INLINE double
+gtodouble(GEN x)
+{
+  if (typ(x)!=t_REAL) {
+    pari_sp av = avma;
+    x = gtofp(x, DEFAULTPREC);
+    if (typ(x)!=t_REAL) pari_err_TYPE("gtodouble [t_REAL expected]", x);
+    avma = av;
+  }
+  return rtodbl(x);
+}
+INLINE long
+gtos(GEN x) {
+  if (typ(x) != t_INT) pari_err_TYPE("gtos [integer expected]",x);
+  return itos(x);
+}
+
+INLINE GEN
+absfrac(GEN x)
+{
+  GEN y = cgetg(3, t_FRAC);
+  gel(y,1) = absi(gel(x,1));
+  gel(y,2) = icopy(gel(x,2)); return y;
+}
+INLINE GEN
+absfrac_shallow(GEN x)
+{ return signe(gel(x,1))>0? x: mkfrac(negi(gel(x,1)), gel(x,2)); }
+INLINE GEN
+Q_abs(GEN x) { return (typ(x) == t_INT)? absi(x): absfrac(x); }
+INLINE GEN
+Q_abs_shallow(GEN x)
+{ return (typ(x) == t_INT)? absi_shallow(x): absfrac_shallow(x); }
+
+/* Force z to be of type real/complex with floating point components */
+INLINE GEN
+gtofp(GEN z, long prec)
+{
+  switch(typ(z))
+  {
+    case t_INT:  return itor(z, prec);
+    case t_FRAC: return fractor(z, prec);
+    case t_REAL: return rtor(z, prec);
+    case t_COMPLEX: {
+      GEN a = gel(z,1), b = gel(z,2);
+      if (isintzero(b)) return cxcompotor(a, prec);
+      if (isintzero(a)) {
+        GEN y = cgetg(3, t_COMPLEX);
+        b = cxcompotor(b, prec);
+        gel(y,1) = real_0_bit(expo(b) - prec2nbits(prec));
+        gel(y,2) = b; return y;
+      }
+      return cxtofp(z, prec);
+    }
+    case t_QUAD: return quadtofp(z, prec);
+    default: pari_err_TYPE("gtofp",z); return NULL; /* not reached */
+  }
+}
+/* Force z to be of type real / int */
+INLINE GEN
+gtomp(GEN z, long prec)
+{
+  switch(typ(z))
+  {
+    case t_INT:  return z;
+    case t_FRAC: return fractor(z, prec);
+    case t_REAL: return rtor(z, prec);
+    case t_QUAD: z = quadtofp(z, prec);
+                 if (typ(z) == t_REAL) return z;
+    default: pari_err_TYPE("gtomp",z); return NULL; /* not reached */
+  }
+}
+
+INLINE GEN
+RgX_gtofp(GEN x, long prec)
+{
+  long l;
+  GEN y = cgetg_copy(x, &l);
+  while (--l > 1) gel(y,l) = gtofp(gel(x,l), prec);
+  y[1] = x[1]; return y;
+}
+INLINE GEN
+RgC_gtofp(GEN x, long prec)
+{
+  long l = lg(x);
+  GEN y = cgetg(l, t_COL);
+  while (--l > 0) gel(y,l) = gtofp(gel(x,l), prec);
+  return y;
+}
+INLINE GEN
+RgM_gtofp(GEN x, long prec)
+{
+  long l;
+  GEN y = cgetg_copy(x, &l);
+  while (--l > 0) gel(y,l) = RgC_gtofp(gel(x,l), prec);
+  return y;
+}
+INLINE GEN
+RgC_gtomp(GEN x, long prec)
+{
+  long l = lg(x);
+  GEN y = cgetg(l, t_COL);
+  while (--l > 0) gel(y,l) = gtomp(gel(x,l), prec);
+  return y;
+}
+INLINE GEN
+RgM_gtomp(GEN x, long prec)
+{
+  long l;
+  GEN y = cgetg_copy(x, &l);
+  while (--l > 0) gel(y,l) = RgC_gtomp(gel(x,l), prec);
+  return y;
+}
+
+INLINE GEN
+RgX_fpnorml2(GEN x, long prec)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gnorml2(RgX_gtofp(x, prec)));
+}
+INLINE GEN
+RgC_fpnorml2(GEN x, long prec)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gnorml2(RgC_gtofp(x, prec)));
+}
+INLINE GEN
+RgM_fpnorml2(GEN x, long prec)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gnorml2(RgM_gtofp(x, prec)));
+}
+
+/* y a t_REAL */
+INLINE void
+affgr(GEN x, GEN y)
+{
+  pari_sp av;
+  switch(typ(x)) {
+    case t_INT:  affir(x,y); break;
+    case t_REAL: affrr(x,y); break;
+    case t_FRAC: rdiviiz(gel(x,1),gel(x,2), y); break;
+    case t_QUAD: av = avma; affgr(quadtofp(x,realprec(y)), y); avma = av; break;
+    default: pari_err_TYPE2("=",x,y);
+  }
+}
+
+INLINE GEN
+affc_fixlg(GEN x, GEN res)
+{
+  if (typ(x) == t_COMPLEX)
+  {
+    affrr_fixlg(gel(x,1), gel(res,1));
+    affrr_fixlg(gel(x,2), gel(res,2));
+  }
+  else
+  {
+    avma = (pari_sp)(res+3);
+    res = cgetr(realprec(gel(res,1)));
+    affrr_fixlg(x, res);
+  }
+  return res;
+}
+
+INLINE GEN
+trunc_safe(GEN x) { long e; return gcvtoi(x,&e); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                          LENGTH CONVERSIONS                     */
+/*                                                                 */
+/*******************************************************************/
+INLINE long
+ndec2nlong(long x) { return 1 + (long)((x)*(LOG2_10/BITS_IN_LONG)); }
+INLINE long
+ndec2prec(long x) { return 2 + ndec2nlong(x); }
+/* Fast implementation of ceil(x / (8*sizeof(long))); typecast to (ulong)
+ * to avoid overflow. Faster than 1 + ((x-1)>>TWOPOTBITS_IN_LONG)) :
+ *   addl, shrl instead of subl, sarl, addl */
+INLINE long
+nbits2nlong(long x) {
+  return (long)(((ulong)x+BITS_IN_LONG-1) >> TWOPOTBITS_IN_LONG);
+}
+
+INLINE long
+nbits2extraprec(long x) {
+  return (long)(((ulong)x+BITS_IN_LONG-1) >> TWOPOTBITS_IN_LONG);
+}
+
+/* Fast implementation of 2 + nbits2nlong(x) */
+INLINE long
+nbits2prec(long x) {
+  return (long)(((ulong)x+3*BITS_IN_LONG-1) >> TWOPOTBITS_IN_LONG);
+}
+INLINE long
+nbits2lg(long x) {
+  return (long)(((ulong)x+3*BITS_IN_LONG-1) >> TWOPOTBITS_IN_LONG);
+}
+/* ceil(x / sizeof(long)) */
+INLINE long
+nchar2nlong(long x) {
+  return (long)(((ulong)x+sizeof(long)-1) >> (TWOPOTBITS_IN_LONG-3L));
+}
+INLINE long
+bit_accuracy(long x) { return (x-2) * BITS_IN_LONG; }
+INLINE double
+bit_accuracy_mul(long x, double y) { return (x-2) * (BITS_IN_LONG*y); }
+INLINE double
+prec2nbits_mul(long x, double y) { return (x-2) * (BITS_IN_LONG*y); }
+INLINE long
+bit_prec(GEN x) { return bit_accuracy(realprec(x)); }
+INLINE long
+prec2nbits(long x) { return bit_accuracy(x); }
+INLINE long
+prec2ndec(long x) { return (long)prec2nbits_mul(x, LOG10_2); }
+INLINE long
+precdbl(long x) {return (x - 1) << 1;}
+INLINE long
+divsBIL(long n) { return n >> TWOPOTBITS_IN_LONG; }
+INLINE long
+remsBIL(long n) { return n & (BITS_IN_LONG-1); }
+
+/*********************************************************************/
+/**                                                                 **/
+/**                      OPERATIONS MODULO m                        **/
+/**                                                                 **/
+/*********************************************************************/
+/* Assume m > 0, more efficient if 0 <= a, b < m */
+
+INLINE GEN
+Fp_red(GEN a, GEN m) { return modii(a, m); }
+
+INLINE GEN
+Fp_add(GEN a, GEN b, GEN m)
+{
+  pari_sp av=avma;
+  GEN p = addii(a,b);
+  long s = signe(p);
+  if (!s) return p; /* = gen_0 */
+  if (s > 0) /* general case */
+  {
+    GEN t = subii(p, m);
+    s = signe(t);
+    if (!s) { avma = av; return gen_0; }
+    if (s < 0) { avma = (pari_sp)p; return p; }
+    if (cmpii(t, m) < 0) return gerepileuptoint(av, t); /* general case ! */
+    p = remii(t, m);
+  }
+  else
+    p = modii(p, m);
+  return gerepileuptoint(av, p);
+}
+INLINE GEN
+Fp_sub(GEN a, GEN b, GEN m)
+{
+  pari_sp av=avma;
+  GEN p = subii(a,b);
+  long s = signe(p);
+  if (!s) return p; /* = gen_0 */
+  if (s > 0)
+  {
+    if (cmpii(p, m) < 0) return p; /* general case ! */
+    p = remii(p, m);
+  }
+  else
+  {
+    GEN t = addii(p, m);
+    if (!s) { avma = av; return gen_0; }
+    if (s > 0) return gerepileuptoint(av, t); /* general case ! */
+    p = modii(t, m);
+  }
+  return gerepileuptoint(av, p);
+}
+INLINE GEN
+Fp_neg(GEN b, GEN m)
+{
+  pari_sp av = avma;
+  long s = signe(b);
+  GEN p;
+  if (!s) return gen_0;
+  if (s > 0)
+  {
+    p = subii(m, b);
+    if (signe(p) >= 0) return p; /* general case ! */
+    p = modii(p, m);
+  } else
+    p = remii(negi(b), m);
+  return gerepileuptoint(av, p);
+}
+/* assume 0 <= u < p and ps2 = p>>1 */
+INLINE GEN
+Fp_center(GEN u, GEN p, GEN ps2)
+{ return absi_cmp(u,ps2)<=0? icopy(u): subii(u,p); }
+
+/* x + y*z mod p */
+INLINE GEN
+Fp_addmul(GEN x, GEN y, GEN z, GEN p)
+{
+  pari_sp av;
+  if (!signe(y) || !signe(z)) return Fp_red(x, p);
+  if (!signe(x)) return Fp_mul(z,y, p);
+  av = avma;
+  return gerepileuptoint(av, modii(addii(x, mulii(y,z)), p));
+}
+
+INLINE GEN
+Fp_mul(GEN a, GEN b, GEN m)
+{
+  pari_sp av=avma;
+  GEN p; /*HACK: assume modii use <=lg(p)+(lg(m)<<1) space*/
+  (void)new_chunk(lg(a)+lg(b)+(lg(m)<<1));
+  p = mulii(a,b);
+  avma = av; return modii(p,m);
+}
+INLINE GEN
+Fp_sqr(GEN a, GEN m)
+{
+  pari_sp av=avma;
+  GEN p; /*HACK: assume modii use <=lg(p)+(lg(m)<<1) space*/
+  (void)new_chunk((lg(a)+lg(m))<<1);
+  p = sqri(a);
+  avma = av; return modii(p,m);
+}
+INLINE GEN
+Fp_mulu(GEN a, ulong b, GEN m)
+{
+  long l = lgefint(m);
+  if (l == 3)
+  {
+    ulong mm = m[2];
+    return utoi( Fl_mul(umodiu(a, mm), b, mm) );
+  } else {
+    pari_sp av = avma;
+    GEN p; /*HACK: assume modii use <=lg(p)+(lg(m)<<1) space*/
+    (void)new_chunk(lg(a)+1+(l<<1));
+    p = muliu(a,b);
+    avma = av; return modii(p,m);
+  }
+}
+INLINE GEN
+Fp_muls(GEN a, long b, GEN m)
+{
+  long l = lgefint(m);
+  if (l == 3)
+  {
+    ulong mm = m[2];
+    if (b < 0)
+    {
+      ulong t = Fl_mul(umodiu(a, mm), -b, mm);
+      return t? utoipos(mm - t): gen_0;
+    }
+    else
+      return utoi( Fl_mul(umodiu(a, mm), b, mm) );
+  } else {
+    pari_sp av = avma;
+    GEN p; /*HACK: assume modii use <=lg(p)+(lg(m)<<1) space*/
+    (void)new_chunk(lg(a)+1+(l<<1));
+    p = mulis(a,b);
+    avma = av; return modii(p,m);
+  }
+}
+
+INLINE GEN
+Fp_inv(GEN a, GEN m)
+{
+  GEN res;
+  if (! invmod(a,m,&res)) pari_err_INV("Fp_inv", mkintmod(res,m));
+  return res;
+}
+INLINE GEN
+Fp_invsafe(GEN a, GEN m)
+{
+  GEN res;
+  if (! invmod(a,m,&res)) return NULL;
+  return res;
+}
+INLINE GEN
+Fp_div(GEN a, GEN b, GEN m)
+{
+  pari_sp av=avma;
+  GEN p; /*HACK: assume modii use <=lg(p)+(lg(m)<<1) space*/
+  (void)new_chunk(lg(a)+(lg(m)<<1));
+  p = mulii(a, Fp_inv(b,m));
+  avma = av; return modii(p,m);
+}
+
+INLINE GEN
+Flx_mulu(GEN x, ulong a, ulong p) { return Flx_Fl_mul(x,a%p,p); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                        ADDMULII / SUBMULII                      */
+/*                                                                 */
+/*******************************************************************/
+/* x - y*z */
+INLINE GEN
+submulii(GEN x, GEN y, GEN z)
+{
+  long lx = lgefint(x), ly, lz;
+  pari_sp av;
+  GEN t;
+  if (lx == 2) { t = mulii(z,y); togglesign(t); return t; }
+  ly = lgefint(y);
+  if (ly == 2) return icopy(x);
+  lz = lgefint(z);
+  av = avma; (void)new_chunk(lx+ly+lz); /* HACK */
+  t = mulii(z, y);
+  avma = av; return subii(x,t);
+}
+/* y*z - x */
+INLINE GEN
+mulsubii(GEN y, GEN z, GEN x)
+{
+  long lx = lgefint(x), ly, lz;
+  pari_sp av;
+  GEN t;
+  if (lx == 2) return mulii(z,y);
+  ly = lgefint(y);
+  if (ly == 2) return negi(x);
+  lz = lgefint(z);
+  av = avma; (void)new_chunk(lx+ly+lz); /* HACK */
+  t = mulii(z, y);
+  avma = av; return subii(t,x);
+}
+
+/* x - u*y */
+INLINE GEN
+submuliu(GEN x, GEN y, ulong u)
+{
+  pari_sp av;
+  long ly = lgefint(y);
+  if (ly == 2) return icopy(x);
+  av = avma;
+  (void)new_chunk(3+ly+lgefint(x)); /* HACK */
+  y = mului(u,y);
+  avma = av; return subii(x, y);
+}
+/* x + u*y */
+INLINE GEN
+addmuliu(GEN x, GEN y, ulong u)
+{
+  pari_sp av;
+  long ly = lgefint(y);
+  if (ly == 2) return icopy(x);
+  av = avma;
+  (void)new_chunk(3+ly+lgefint(x)); /* HACK */
+  y = mului(u,y);
+  avma = av; return addii(x, y);
+}
+/* x - u*y */
+INLINE GEN
+submuliu_inplace(GEN x, GEN y, ulong u)
+{
+  pari_sp av;
+  long ly = lgefint(y);
+  if (ly == 2) return x;
+  av = avma;
+  (void)new_chunk(3+ly+lgefint(x)); /* HACK */
+  y = mului(u,y);
+  avma = av; return subii(x, y);
+}
+/* x + u*y */
+INLINE GEN
+addmuliu_inplace(GEN x, GEN y, ulong u)
+{
+  pari_sp av;
+  long ly = lgefint(y);
+  if (ly == 2) return x;
+  av = avma;
+  (void)new_chunk(3+ly+lgefint(x)); /* HACK */
+  y = mului(u,y);
+  avma = av; return addii(x, y);
+}
+/* ux + vy */
+INLINE GEN
+lincombii(GEN u, GEN v, GEN x, GEN y)
+{
+  long lx = lgefint(x), ly;
+  GEN p1, p2;
+  pari_sp av;
+  if (lx == 2) return mulii(v,y);
+  ly = lgefint(y);
+  if (ly == 2) return mulii(u,x);
+  av = avma; (void)new_chunk(lx+ly+lgefint(u)+lgefint(v)); /* HACK */
+  p1 = mulii(u,x);
+  p2 = mulii(v,y);
+  avma = av; return addii(p1,p2);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                          GEN SUBTYPES                           */
+/*                                                                 */
+/*******************************************************************/
+
+INLINE int
+is_const_t(long t) { return (t < t_POLMOD); }
+INLINE int
+is_extscalar_t(long t) { return (t <= t_POL); }
+INLINE int
+is_intreal_t(long t) { return (t <= t_REAL); }
+INLINE int
+is_matvec_t(long t) { return (t >= t_VEC && t <= t_MAT); }
+INLINE int
+is_noncalc_t(long tx) { return (tx) >= t_LIST; }
+INLINE int
+is_rational_t(long t) { return (t == t_INT || t == t_FRAC); }
+INLINE int
+is_recursive_t(long t) { return lontyp[t]; }
+INLINE int
+is_scalar_t(long t) { return (t < t_POL); }
+INLINE int
+is_vec_t(long t) { return (t == t_VEC || t == t_COL); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                         TRANSCENDENTAL                          */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+sqrtr(GEN x) {
+  long s = signe(x);
+  if (s == 0) return real_0_bit(expo(x) >> 1);
+  if (s >= 0) return sqrtr_abs(x);
+  retmkcomplex(gen_0, sqrtr_abs(x));
+}
+/* x^(1/n) */
+INLINE GEN
+sqrtnr(GEN x, long n) { return mpexp(divrs(mplog(x), n)); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                         MISCELLANEOUS                           */
+/*                                                                 */
+/*******************************************************************/
+INLINE int ismpzero(GEN x) { return is_intreal_t(typ(x)) && !signe(x); }
+INLINE int isintzero(GEN x) { return typ(x) == t_INT && !signe(x); }
+INLINE int isint1(GEN x) { return typ(x)==t_INT && equali1(x); }
+INLINE int isintm1(GEN x){ return typ(x)==t_INT && equalim1(x);}
+INLINE int equali1(GEN n)
+{ return (ulong) n[1] == (evallgefint(3UL) | evalsigne(1)) && n[2] == 1; }
+INLINE int equalim1(GEN n)
+{ return (ulong) n[1] == (evallgefint(3UL) | evalsigne(-1)) && n[2] == 1; }
+/* works only for POSITIVE integers */
+INLINE int is_pm1(GEN n)
+{ return lgefint(n) == 3 && n[2] == 1; }
+INLINE int is_bigint(GEN n)
+{ long l = lgefint(n); return l > 3 || (l == 3 && (n[2] & HIGHBIT)); }
+
+INLINE int odd(long x) { return x & 1; }
+INLINE int both_odd(long x, long y) { return x & y & 1; }
+
+INLINE int
+isonstack(GEN x) { return ((pari_sp)x >= bot && (pari_sp)x < top); }
+
+/* assume 0 <= k <= BITS_IN_LONG. Return uniform random 0 <= x < (1<<k) */
+INLINE long
+random_bits(long k) { return pari_rand() >> (BITS_IN_LONG - k); }
+
+/* assume x != 0 and x t_REAL, return an approximation to log2(|x|) */
+INLINE double
+dbllog2r(GEN x)
+{ return log2((double)(ulong)x[2]) + (double)(expo(x) - (BITS_IN_LONG-1)); }
+
+INLINE GEN
+mul_content(GEN cx, GEN cy)
+{
+  if (!cx) return cy;
+  if (!cy) return cx;
+  return gmul(cx,cy);
+}
+INLINE GEN
+mul_denom(GEN dx, GEN dy)
+{
+  if (!dx) return dy;
+  if (!dy) return dx;
+  return mulii(dx,dy);
+}
+
+/* POLYNOMIALS */
+INLINE GEN
+constant_term(GEN x) { return signe(x)? gel(x,2): gen_0; }
+INLINE GEN
+leading_term(GEN x) { return lg(x) == 2? gen_0: gel(x,lg(x)-1); }
+INLINE ulong
+Flx_lead(GEN x) { return lg(x) == 2? 0: x[lg(x)-1]; }
+INLINE long
+degpol(GEN x) { return lg(x)-3; }
+INLINE long
+lgpol(GEN x) { return lg(x)-2; }
+INLINE long
+lgcols(GEN x) { return lg(gel(x,1)); }
+INLINE long
+nbrows(GEN x) { return lg(gel(x,1))-1; }
+INLINE GEN
+truecoeff(GEN x, long n) { return polcoeff0(x,n,-1); }
+
+INLINE GEN
+RgXQ_mul(GEN y, GEN x, GEN T) { return RgX_rem(RgX_mul(y, x), T); }
+INLINE GEN
+RgXQ_sqr(GEN x, GEN T) { return RgX_rem(RgX_sqr(x), T); }
+
+INLINE GEN
+ZXQ_mul(GEN y, GEN x, GEN T) { return ZX_rem(ZX_mul(y, x), T); }
+INLINE GEN
+ZXQ_sqr(GEN x, GEN T) { return ZX_rem(ZX_sqr(x), T); }
+
+INLINE GEN
+RgX_copy(GEN x)
+{
+  long lx, i;
+  GEN y = cgetg_copy(x, &lx); y[1] = x[1];
+  for (i = 2; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+  return y;
+}
+INLINE GEN
+RgX_renormalize(GEN x) { return RgX_renormalize_lg(x, lg(x)); }
+INLINE GEN
+RgX_div(GEN x, GEN y) { return RgX_divrem(x,y,NULL); }
+INLINE GEN
+RgX_rem(GEN x, GEN y) { return RgX_divrem(x,y,ONLY_REM); }
+INLINE GEN
+RgXQX_div(GEN x, GEN y, GEN T) { return RgXQX_divrem(x,y,T,NULL); }
+INLINE GEN
+RgXQX_rem(GEN x, GEN y, GEN T) { return RgXQX_divrem(x,y,T,ONLY_REM); }
+INLINE GEN
+FpX_div(GEN x, GEN y, GEN p) { return FpX_divrem(x,y,p, NULL); }
+INLINE GEN
+Flx_div(GEN x, GEN y, ulong p) { return Flx_divrem(x,y,p, NULL); }
+INLINE GEN
+F2x_div(GEN x, GEN y) { return F2x_divrem(x,y, NULL); }
+INLINE GEN
+FpV_FpC_mul(GEN x, GEN y, GEN p) { return FpV_dotproduct(x,y,p); }
+INLINE GEN
+pol0_Flx(long sv) { return mkvecsmall(sv); }
+INLINE GEN
+pol1_Flx(long sv) { return mkvecsmall2(sv, 1); }
+INLINE GEN
+polx_Flx(long sv) { return mkvecsmall3(sv, 0, 1); }
+INLINE GEN
+zero_zx(long sv) { return zero_Flx(sv); }
+INLINE GEN
+polx_zx(long sv) { return polx_Flx(sv); }
+INLINE GEN
+zx_shift(GEN x, long n) { return Flx_shift(x,n); }
+INLINE GEN
+zx_renormalize(GEN x, long l) { return Flx_renormalize(x,l); }
+INLINE GEN
+zero_F2x(long sv) { return zero_Flx(sv); }
+INLINE GEN
+pol0_F2x(long sv) { return pol0_Flx(sv); }
+INLINE GEN
+pol1_F2x(long sv) { return pol1_Flx(sv); }
+INLINE GEN
+polx_F2x(long sv) { return mkvecsmall2(sv, 2); }
+INLINE int
+F2x_equal1(GEN x) { return Flx_equal1(x); }
+INLINE int
+F2x_equal(GEN V, GEN W) { return Flx_equal(V,W); }
+INLINE GEN
+F2v_copy(GEN x) { return leafcopy(x); }
+INLINE GEN
+Flv_copy(GEN x) { return leafcopy(x); }
+INLINE GEN
+Flx_copy(GEN x) { return leafcopy(x); }
+INLINE GEN
+vecsmall_copy(GEN x) { return leafcopy(x); }
+INLINE int
+Flx_equal1(GEN x) { return degpol(x)==0 && x[2] == 1; }
+INLINE int
+ZX_equal1(GEN x) { return degpol(x)==0 && equali1(gel(x,2)); }
+
+INLINE GEN
+ZX_renormalize(GEN x, long lx)    { return ZXX_renormalize(x,lx); }
+INLINE GEN
+FpX_renormalize(GEN x, long lx)   { return ZXX_renormalize(x,lx); }
+INLINE GEN
+FpXX_renormalize(GEN x, long lx)  { return ZXX_renormalize(x,lx); }
+INLINE GEN
+FpXQX_renormalize(GEN x, long lx) { return ZXX_renormalize(x,lx); }
+INLINE GEN
+F2x_renormalize(GEN x, long lx)   { return Flx_renormalize(x,lx); }
+INLINE GEN
+F2v_to_F2x(GEN x, long sv) {
+  GEN y = leafcopy(x);
+  y[1] = sv; F2x_renormalize(y, lg(y)); return y;
+}
+
+INLINE GEN
+ZX_ZXY_resultant(GEN a, GEN b) { return ZX_ZXY_rnfequation(a,b,NULL); }
+INLINE long
+sturm(GEN x) { return sturmpart(x, NULL, NULL); }
+INLINE GEN
+resultant(GEN x, GEN y) { return resultant_all(x,y,NULL); }
+
+INLINE long
+gval(GEN x, long v) {
+  pari_sp av = avma;
+  long n = gvaluation(x, pol_x(v));
+  avma = av; return n;
+}
+
+INLINE void
+RgX_shift_inplace_init(long v)
+{ if (v) (void)cgetg(v, t_VECSMALL); }
+/* shift polynomial in place. assume v free cells have been left before x */
+INLINE GEN
+RgX_shift_inplace(GEN x, long v)
+{
+  long i, lx;
+  GEN y, z;
+  if (!v) return x;
+  lx = lg(x);
+  if (lx == 2) return x;
+  y = x + v;
+  z = x + lx;
+  /* stackdummy from normalizepol: move it up */
+  if (lg(z) != v) x[lx + v] = z[0];
+  for (i = lx-1; i >= 2; i--) gel(y,i) = gel(x,i);
+  for (i = v+1;  i >= 2; i--) gel(x,i) = gen_0;
+  /* leave x[1] alone: it is correct */
+  x[0] = evaltyp(t_POL) | evallg(lx+v); return x;
+}
+
+
+/* LINEAR ALGEBRA */
+INLINE GEN
+zv_to_ZV(GEN x) { return vecsmall_to_vec(x); }
+INLINE GEN
+zc_to_ZC(GEN x) { return vecsmall_to_col(x); }
+INLINE GEN
+ZV_to_zv(GEN x) { return vec_to_vecsmall(x); }
+INLINE GEN
+zx_to_zv(GEN x, long N) { return Flx_to_Flv(x,N); }
+INLINE GEN
+zv_to_zx(GEN x, long sv) { return Flv_to_Flx(x,sv); }
+INLINE GEN
+zm_to_zxV(GEN x, long sv) { return Flm_to_FlxV(x,sv); }
+INLINE GEN
+zero_zm(long x, long y) { return zero_Flm(x,y); }
+INLINE GEN
+zero_zv(long x) { return zero_Flv(x); }
+INLINE GEN
+zm_transpose(GEN x) { return Flm_transpose(x); }
+INLINE GEN
+zm_copy(GEN x) { return Flm_copy(x); }
+INLINE GEN
+zv_copy(GEN x) { return Flv_copy(x); }
+INLINE GEN
+row_zm(GEN x, long i) { return row_Flm(x,i); }
+
+INLINE GEN
+ZC_hnfrem(GEN x, GEN y) { return ZC_hnfremdiv(x,y,NULL); }
+INLINE GEN
+ZM_hnfrem(GEN x, GEN y) { return ZM_hnfdivrem(x,y,NULL); }
+INLINE GEN
+ZM_lll(GEN x, double D, long f) { return ZM_lll_norms(x,D,f,NULL); }
+INLINE void
+RgM_dimensions(GEN x, long *m, long *n) { *n = lg(x)-1; *m = *n? nbrows(x): 0; }
+INLINE GEN
+RgM_inv(GEN a) { return RgM_solve(a, NULL); }
+INLINE GEN
+RgM_shallowcopy(GEN x)
+{
+  long l;
+  GEN y = cgetg_copy(x, &l);
+  while (--l > 0) gel(y,l) = leafcopy(gel(x,l));
+  return y;
+}
+INLINE GEN
+F2m_copy(GEN x) { return RgM_shallowcopy(x); }
+
+INLINE GEN
+Flm_copy(GEN x) { return RgM_shallowcopy(x); }
+
+/* divisibility: return 1 if y[i] | x[i] for all i, 0 otherwise. Assume
+ * x,y are ZV of the same length */
+INLINE int
+ZV_dvd(GEN x, GEN y)
+{
+  long i, l = lg(x);
+  for (i=1; i < l; i++)
+    if ( ! dvdii( gel(x,i), gel(y,i) ) ) return 0;
+  return 1;
+}
+
+/* Fq */
+INLINE GEN
+Fq_red(GEN x, GEN T, GEN p)
+{ return typ(x)==t_INT? Fp_red(x,p): FpXQ_red(x,T,p); }
+
+/* FpXQX */
+INLINE GEN
+FpXQX_div(GEN x, GEN y, GEN T, GEN p) { return FpXQX_divrem(x, y, T, p, NULL); }
+INLINE GEN
+FlxqX_rem(GEN x, GEN y, GEN T, ulong p) { return FlxqX_divrem(x, y, T, p, ONLY_REM); }
+INLINE GEN
+FlxqX_div(GEN x, GEN y, GEN T, ulong p) { return FlxqX_divrem(x, y, T, p, NULL); }
+
+/* FqX */
+INLINE GEN
+FqX_red(GEN z, GEN T, GEN p) { return T? FpXQX_red(z, T, p): FpX_red(z, p); }
+INLINE GEN
+FqX_add(GEN x,GEN y,GEN T,GEN p) { return T? FpXX_add(x,y,p): FpX_add(x,y,p); }
+INLINE GEN
+FqX_neg(GEN x,GEN T,GEN p) { return T? FpXX_neg(x,p): FpX_neg(x,p); }
+INLINE GEN
+FqX_sub(GEN x,GEN y,GEN T,GEN p) { return T? FpXX_sub(x,y,p): FpX_sub(x,y,p); }
+INLINE GEN
+FqX_Fp_mul(GEN P, GEN u, GEN T, GEN p)
+{ return T? FpXX_Fp_mul(P, u, p): FpX_Fp_mul(P, u, p); }
+INLINE GEN
+FqX_Fq_mul(GEN P, GEN U, GEN T, GEN p)
+{ return typ(U)==t_INT ? FqX_Fp_mul(P, U, T, p): FpXQX_FpXQ_mul(P, U, T, p); }
+INLINE GEN
+FqX_mul(GEN x, GEN y, GEN T, GEN p)
+{ return T? FpXQX_mul(x, y, T, p): FpX_mul(x, y, p); }
+INLINE GEN
+FqX_mulu(GEN x, ulong y, GEN T, GEN p)
+{ return T? FpXX_mulu(x, y, p): FpX_mulu(x, y, p); }
+INLINE GEN
+FqX_sqr(GEN x, GEN T, GEN p)
+{ return T? FpXQX_sqr(x, T, p): FpX_sqr(x, p); }
+INLINE GEN
+FqX_div(GEN x, GEN y, GEN T, GEN p)
+{ return T? FpXQX_divrem(x,y,T,p,NULL): FpX_divrem(x,y,p,NULL); }
+INLINE GEN
+FqX_rem(GEN x, GEN y, GEN T, GEN p)
+{ return T? FpXQX_rem(x,y,T,p): FpX_rem(x,y,p); }
+INLINE GEN
+FqX_divrem(GEN x, GEN y, GEN T, GEN p, GEN *z)
+{ return T? FpXQX_divrem(x,y,T,p,z): FpX_divrem(x,y,p,z); }
+INLINE GEN
+FqX_gcd(GEN P,GEN Q,GEN T,GEN p)
+{return T? FpXQX_gcd(P,Q,T,p): FpX_gcd(P,Q,p);}
+INLINE GEN
+FqX_extgcd(GEN P,GEN Q,GEN T,GEN p, GEN *U, GEN *V)
+{ return T? FpXQX_extgcd(P,Q,T,p,U,V): FpX_extgcd(P,Q,p,U,V); }
+
+/*FqXQ*/
+INLINE GEN
+FqXQ_add(GEN x, GEN y, GEN S/*unused*/, GEN T, GEN p)
+{ (void)S; return T? FpXX_add(x,y,p): FpX_add(x,y,p); }
+INLINE GEN
+FqXQ_sub(GEN x, GEN y, GEN S/*unused*/, GEN T, GEN p)
+{ (void)S; return T? FpXX_sub(x,y,p): FpX_sub(x,y,p); }
+INLINE GEN
+FqXQ_div(GEN x, GEN y, GEN S, GEN T, GEN p)
+{ return T? FpXQXQ_div(x,y,S,T,p): FpXQ_div(x,y,S,p); }
+INLINE GEN
+FqXQ_inv(GEN x, GEN S, GEN T, GEN p)
+{ return T? FpXQXQ_inv(x,S,T,p): FpXQ_inv(x,S,p); }
+INLINE GEN
+FqXQ_invsafe(GEN x, GEN S, GEN T, GEN p)
+{ return T? FpXQXQ_invsafe(x,S,T,p): FpXQ_inv(x,S,p); }
+INLINE GEN
+FqXQ_mul(GEN x, GEN y, GEN S, GEN T, GEN p)
+{ return T? FpXQXQ_mul(x,y,S,T,p): FpXQ_mul(x,y,S,p); }
+INLINE GEN
+FqXQ_sqr(GEN x, GEN S, GEN T, GEN p)
+{ return T? FpXQXQ_sqr(x,S,T,p): FpXQ_sqr(x,S,p); }
+INLINE GEN
+FqXQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p)
+{ return T? FpXQXQ_pow(x,n,S,T,p): FpXQ_pow(x,n,S,p); }
+
+/*FpXQ*/
+INLINE GEN
+FpXQ_add(GEN x,GEN y,GEN T/*unused*/,GEN p)
+{ (void)T; return FpX_add(x,y,p); }
+INLINE GEN
+FpXQ_sub(GEN x,GEN y,GEN T/*unused*/,GEN p)
+{ (void)T; return FpX_sub(x,y,p); }
+
+/*Flxq*/
+INLINE GEN
+Flxq_add(GEN x,GEN y,GEN T/*unused*/,ulong p)
+{ (void)T; return Flx_add(x,y,p); }
+INLINE GEN
+Flxq_sub(GEN x,GEN y,GEN T/*unused*/,ulong p)
+{ (void)T; return Flx_sub(x,y,p); }
+
+/* F2x */
+
+INLINE ulong
+F2x_coeff(GEN x,long v)
+{
+   ulong u=(ulong)x[2+divsBIL(v)];
+   return (u>>remsBIL(v))&1UL;
+}
+
+INLINE void
+F2x_clear(GEN x,long v)
+{
+   ulong* u=(ulong*)&x[2+divsBIL(v)];
+   *u&=~(1UL<<remsBIL(v));
+}
+
+INLINE void
+F2x_set(GEN x,long v)
+{
+   ulong* u=(ulong*)&x[2+divsBIL(v)];
+   *u|=1UL<<remsBIL(v);
+}
+
+INLINE void
+F2x_flip(GEN x,long v)
+{
+   ulong* u=(ulong*)&x[2+divsBIL(v)];
+   *u^=1UL<<remsBIL(v);
+}
+
+/* F2v */
+
+INLINE ulong
+F2v_coeff(GEN x,long v) { return F2x_coeff(x,v-1); }
+
+INLINE void
+F2v_clear(GEN x,long v) { F2x_clear(x,v-1); }
+
+INLINE void
+F2v_set(GEN x,long v)   { F2x_set(x,v-1); }
+
+INLINE void
+F2v_flip(GEN x,long v) { F2x_flip(x,v-1); }
+
+/* F2m */
+
+INLINE ulong
+F2m_coeff(GEN x, long a, long b) { return F2v_coeff(gel(x,b), a); }
+
+INLINE void
+F2m_clear(GEN x, long a, long b) { F2v_clear(gel(x,b), a); }
+
+INLINE void
+F2m_set(GEN x, long a, long b) { F2v_set(gel(x,b), a); }
+
+INLINE void
+F2m_flip(GEN x, long a, long b) { F2v_flip(gel(x,b), a); }
+
+/* ARITHMETIC */
+INLINE GEN
+matpascal(long n) { return matqpascal(n, NULL); }
+INLINE long
+Z_issquare(GEN x) { return Z_issquareall(x, NULL); }
+INLINE long
+Z_ispower(GEN x, ulong k) { return Z_ispowerall(x, k, NULL); }
+INLINE GEN
+sqrti(GEN x) { return sqrtremi(x,NULL); }
+INLINE GEN
+gaddgs(GEN y, long s) { return gaddsg(s,y); }
+INLINE int
+gcmpgs(GEN y, long s) { return -gcmpsg(s,y); }
+INLINE int
+gequalgs(GEN y, long s) { return gequalsg(s,y); }
+INLINE GEN
+gmaxsg(long s, GEN y) { return gmaxgs(y,s); }
+INLINE GEN
+gminsg(long s, GEN y) { return gmings(y,s); }
+INLINE GEN
+gmulgs(GEN y, long s) { return gmulsg(s,y); }
+INLINE GEN
+gsubgs(GEN y, long s) { return gaddgs(y, -s); }
+INLINE GEN
+gdivsg(long s, GEN y) { return gdiv(stoi(s), y); }
+
+/* x t_COMPLEX */
+INLINE GEN
+cxnorm(GEN x) { return gadd(gsqr(gel(x,1)), gsqr(gel(x,2))); }
+/* q t_QUAD */
+INLINE GEN
+quadnorm(GEN q)
+{
+  GEN X = gel(q,1), b = gel(X,3), c = gel(X,2);
+  GEN z, u = gel(q,3), v = gel(q,2);
+  if (typ(u) == t_INT && typ(v) == t_INT) /* generic case */
+  {
+    z = signe(b)? mulii(v, addii(u,v)): sqri(v);
+    return addii(z, mulii(c, sqri(u)));
+  }
+  else
+  {
+    z = signe(b)? gmul(v, gadd(u,v)): gsqr(v);
+    return gadd(z, gmul(c, gsqr(u)));
+  }
+}
+/* x a t_QUAD, return the associated discriminant */
+INLINE GEN
+quad_disc(GEN x)
+{
+  GEN Q = gel(x,1), b = gel(Q,3), c = gel(Q,2), c4;
+  if (is_pm1(b))
+  {
+    pari_sp av = avma; (void)new_chunk(lgefint(c) + 1);
+    c4 = shifti(c,2); avma = av; return subsi(1, c4);
+  }
+  c4 = shifti(c,2); togglesign_safe(&c4); return c4;
+}
+INLINE GEN
+qfb_disc3(GEN x, GEN y, GEN z) { return subii(sqri(y), shifti(mulii(x,z),2)); }
+INLINE GEN
+qfb_disc(GEN x) { return qfb_disc3(gel(x,1), gel(x,2), gel(x,3)); }
+
+INLINE GEN
+sqrfrac(GEN x)
+{
+  GEN z = cgetg(3,t_FRAC);
+  gel(z,1) = sqri(gel(x,1));
+  gel(z,2) = sqri(gel(x,2)); return z;
+}
+
+INLINE void
+normalize_frac(GEN z) {
+  if (signe(gel(z,2)) < 0) { togglesign(gel(z,1)); setabssign(gel(z,2)); }
+}
+
+INLINE GEN
+powii(GEN x, GEN n)
+{
+  long ln = lgefint(n);
+  if (ln == 3) {
+    GEN z;
+    if (signe(n) > 0) return powiu(x, n[2]);
+    z = cgetg(3, t_FRAC);
+    gel(z,1) = gen_1;
+    gel(z,2) = powiu(x, n[2]);
+    return z;
+  }
+  if (ln == 2) return gen_1; /* rare */
+  /* should never happen */
+  return powgi(x, n); /* overflow unless x = 0, 1, -1 */
+}
+INLINE GEN
+powIs(long n) {
+  switch(n & 3)
+  {
+    case 1: return mkcomplex(gen_0,gen_1);
+    case 2: return gen_m1;
+    case 3: return mkcomplex(gen_0,gen_m1);
+  }
+  return gen_1;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                             ASSIGNMENTS                         */
+/*                                                                 */
+/*******************************************************************/
+INLINE void mpexpz(GEN x, GEN z)
+{ pari_sp av = avma; gaffect(mpexp(x), z); avma = av; }
+INLINE void mplogz(GEN x, GEN z)
+{ pari_sp av = avma; gaffect(mplog(x), z); avma = av; }
+INLINE void mpcosz(GEN x, GEN z)
+{ pari_sp av = avma; gaffect(mpcos(x), z); avma = av; }
+INLINE void mpsinz(GEN x, GEN z)
+{ pari_sp av = avma; gaffect(mpsin(x), z); avma = av; }
+INLINE void gnegz(GEN x, GEN z)
+{ pari_sp av = avma; gaffect(gneg(x), z); avma = av; }
+INLINE void gabsz(GEN x, long prec, GEN z)
+{ pari_sp av = avma; gaffect(gabs(x,prec), z); avma = av; }
+INLINE void gaddz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; gaffect(gadd(x,y), z); avma = av; }
+INLINE void gsubz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; gaffect(gsub(x,y), z); avma = av; }
+INLINE void gmulz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; gaffect(gmul(x,y), z); avma = av; }
+INLINE void gdivz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; gaffect(gdiv(x,y), z); avma = av; }
+INLINE void gdiventz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; gaffect(gdivent(x,y), z); avma = av; }
+INLINE void gmodz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; gaffect(gmod(x,y), z); avma = av; }
+INLINE void gmul2nz(GEN x, long s, GEN z)
+{ pari_sp av = avma; gaffect(gmul2n(x,s), z); avma = av; }
+INLINE void gshiftz(GEN x, long s, GEN z)
+{ pari_sp av = avma; gaffect(gshift(x,s), z); avma = av; }
+
+/*******************************************************************/
+/*                                                                 */
+/*                       ELLIPTIC CURVES                           */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN ell_get_a1(GEN e) { return gel(e,1); }
+INLINE GEN ell_get_a2(GEN e) { return gel(e,2); }
+INLINE GEN ell_get_a3(GEN e) { return gel(e,3); }
+INLINE GEN ell_get_a4(GEN e) { return gel(e,4); }
+INLINE GEN ell_get_a6(GEN e) { return gel(e,5); }
+INLINE GEN ell_get_b2(GEN e) { return gel(e,6); }
+INLINE GEN ell_get_b4(GEN e) { return gel(e,7); }
+INLINE GEN ell_get_b6(GEN e) { return gel(e,8); }
+INLINE GEN ell_get_b8(GEN e) { return gel(e,9); }
+INLINE GEN ell_get_c4(GEN e) { return gel(e,10); }
+INLINE GEN ell_get_c6(GEN e) { return gel(e,11); }
+INLINE GEN ell_get_disc(GEN e) { return gel(e,12); }
+INLINE GEN ell_get_j(GEN e) { return gel(e,13); }
+INLINE long ell_get_type(GEN e) { return mael(e,14,1); }
+INLINE GEN ellff_get_field(GEN x) { return gmael(x, 15, 1); }
+INLINE GEN ellff_get_a4a6(GEN x)  { return gmael(x, 15, 2); }
+INLINE GEN ellQp_get_zero(GEN x) { return gmael(x, 15, 1); }
+INLINE long ellQp_get_prec(GEN E) { GEN z = ellQp_get_zero(E); return valp(z); }
+INLINE GEN ellQp_get_p(GEN E) { GEN z = ellQp_get_zero(E); return gel(z,2); }
+INLINE long ellR_get_prec(GEN x) { return nbits2prec(mael3(x, 15, 1, 1)); }
+INLINE long ellR_get_sign(GEN x) { return mael3(x, 15, 1, 2); }
+
+INLINE int ell_is_inf(GEN z) { return lg(z) == 2; }
+INLINE GEN ellinf(void) { return mkvec(gen_0); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                    ALGEBRAIC NUMBER THEORY                      */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN pr_get_p(GEN pr)  { return gel(pr,1); }
+INLINE GEN pr_get_gen(GEN pr){ return gel(pr,2); }
+/* .[2] instead of itos works: e and f are small positive integers */
+INLINE long pr_get_e(GEN pr) { return gel(pr,3)[2]; }
+INLINE long pr_get_f(GEN pr) { return gel(pr,4)[2]; }
+INLINE GEN pr_get_tau(GEN pr){ return gel(pr,5); }
+INLINE int
+pr_is_inert(GEN P) { return pr_get_f(P) == lg(pr_get_gen(P))-1; }
+INLINE GEN
+pr_norm(GEN pr) { return powiu(pr_get_p(pr), pr_get_f(pr)); }
+
+/* assume nf a genuine nf */
+INLINE long
+nf_get_varn(GEN nf) { return varn(gel(nf,1)); }
+INLINE GEN
+nf_get_pol(GEN nf) { return gel(nf,1); }
+INLINE long
+nf_get_degree(GEN nf) { return degpol( nf_get_pol(nf) ); }
+INLINE long
+nf_get_r1(GEN nf) { GEN x = gel(nf,2); return itou(gel(x,1)); }
+INLINE long
+nf_get_r2(GEN nf) { GEN x = gel(nf,2); return itou(gel(x,2)); }
+INLINE GEN
+nf_get_disc(GEN nf) { return gel(nf,3); }
+INLINE GEN
+nf_get_index(GEN nf) { return gel(nf,4); }
+INLINE GEN
+nf_get_M(GEN nf) { return gmael(nf,5,1); }
+INLINE GEN
+nf_get_G(GEN nf) { return gmael(nf,5,2); }
+INLINE GEN
+nf_get_roundG(GEN nf) { return gmael(nf,5,3); }
+INLINE GEN
+nf_get_Tr(GEN nf) { return gmael(nf,5,4); }
+INLINE GEN
+nf_get_diff(GEN nf) { return gmael(nf,5,5); }
+INLINE GEN
+nf_get_roots(GEN nf) { return gel(nf,6); }
+INLINE GEN
+nf_get_zk(GEN nf) { return gel(nf,7); }
+INLINE GEN
+nf_get_invzk(GEN nf) { return gel(nf,8); }
+INLINE void
+nf_get_sign(GEN nf, long *r1, long *r2)
+{
+  GEN x = gel(nf,2);
+  *r1 = itou(gel(x,1));
+  *r2 = itou(gel(x,2));
+}
+
+INLINE GEN
+abgrp_get_no(GEN x) { return gel(x,1); }
+INLINE GEN
+abgrp_get_cyc(GEN x) { return gel(x,2); }
+INLINE GEN
+abgrp_get_gen(GEN x) { return gel(x,3); }
+INLINE GEN
+bnf_get_nf(GEN bnf) { return gel(bnf,7); }
+INLINE GEN
+bnf_get_clgp(GEN bnf) { return gmael(bnf,8,1); }
+INLINE GEN
+bnf_get_no(GEN bnf) { return abgrp_get_no(bnf_get_clgp(bnf)); }
+INLINE GEN
+bnf_get_cyc(GEN bnf) { return abgrp_get_cyc(bnf_get_clgp(bnf)); }
+INLINE GEN
+bnf_get_gen(GEN bnf)  { return abgrp_get_gen(bnf_get_clgp(bnf)); }
+INLINE GEN
+bnf_get_reg(GEN bnf) { return gmael(bnf,8,2); }
+INLINE GEN
+bnf_get_logfu(GEN bnf) { return gel(bnf,3); }
+INLINE GEN
+bnf_get_tuU(GEN bnf) { return gmael3(bnf,8,4,2); }
+INLINE long
+bnf_get_tuN(GEN bnf) { return gmael3(bnf,8,4,1)[2]; }
+INLINE GEN
+bnf_get_fu(GEN bnf) {
+  GEN fu = bnf_get_fu_nocheck(bnf);
+  if (typ(fu) == t_MAT) pari_err(e_MISC,"missing units in bnf");
+  return fu;
+}
+INLINE GEN
+bnf_get_fu_nocheck(GEN bnf) { return gmael(bnf,8,5); }
+
+INLINE GEN
+bnr_get_bnf(GEN bnr) { return gel(bnr,1); }
+INLINE GEN
+bnr_get_bid(GEN bnr) { return gel(bnr,2); }
+INLINE GEN
+bnr_get_mod(GEN bnr) { return gmael(bnr,2,1); }
+INLINE GEN
+bnr_get_nf(GEN bnr) { return gmael(bnr,1,7); }
+INLINE GEN
+bnr_get_clgp(GEN bnr) { return gel(bnr,5); }
+INLINE GEN
+bnr_get_no(GEN bnr) { return abgrp_get_no(bnr_get_clgp(bnr)); }
+INLINE GEN
+bnr_get_cyc(GEN bnr) { return abgrp_get_cyc(bnr_get_clgp(bnr)); }
+INLINE GEN
+bnr_get_gen_nocheck(GEN bnr) { return abgrp_get_gen(bnr_get_clgp(bnr)); }
+INLINE GEN
+bnr_get_gen(GEN bnr) {
+  GEN G = bnr_get_clgp(bnr);
+  if (lg(G) !=  4)
+    pari_err(e_MISC,"missing bnr generators: please use bnrinit(,,1)");
+  return gel(G,3);
+}
+
+INLINE GEN
+bid_get_mod(GEN bid) { return gel(bid,1); }
+INLINE GEN
+bid_get_ideal(GEN bid) { return gmael(bid,1,1); }
+INLINE GEN
+bid_get_arch(GEN bid) { return gmael(bid,1,2); }
+INLINE GEN
+bid_get_grp(GEN bid) { return gel(bid,2); }
+INLINE GEN
+bid_get_no(GEN bid) { return abgrp_get_no(bid_get_grp(bid)); }
+INLINE GEN
+bid_get_cyc(GEN bid) { return abgrp_get_cyc(bid_get_grp(bid)); }
+INLINE GEN
+bid_get_gen_nocheck(GEN bid)  { return abgrp_get_gen(bid_get_grp(bid)); }
+INLINE GEN
+bid_get_gen(GEN bid) {
+  GEN G = bid_get_grp(bid);
+  if (lg(G) != 4) pari_err(e_MISC,"missing bid generators. Use idealstar(,,2)");
+  return abgrp_get_gen(G);
+}
+INLINE GEN
+gal_get_pol(GEN gal) { return gel(gal,1); }
+INLINE GEN
+gal_get_p(GEN gal) { return gmael(gal,2,1); }
+INLINE GEN
+gal_get_e(GEN gal) { return gmael(gal,2,2); }
+INLINE GEN
+gal_get_mod(GEN gal) { return gmael(gal,2,3); }
+INLINE GEN
+gal_get_roots(GEN gal) { return gel(gal,3); }
+INLINE GEN
+gal_get_invvdm(GEN gal) { return gel(gal,4); }
+INLINE GEN
+gal_get_den(GEN gal) { return gel(gal,5); }
+INLINE GEN
+gal_get_group(GEN gal) { return gel(gal,6); }
+INLINE GEN
+gal_get_gen(GEN gal) { return gel(gal,7); }
+INLINE GEN
+gal_get_orders(GEN gal) { return gel(gal,8); }
+
+/* assume rnf a genuine rnf */
+INLINE long
+rnf_get_degree(GEN rnf) { return degpol(rnf_get_pol(rnf)); }
+INLINE long
+rnf_get_nfdegree(GEN rnf) { return degpol(nf_get_pol(rnf_get_nf(rnf))); }
+INLINE long
+rnf_get_absdegree(GEN rnf) { return degpol(gmael(rnf,11,1)); }
+INLINE GEN
+rnf_get_nf(GEN rnf) { return gel(rnf,10); }
+INLINE void
+rnf_get_nfzk(GEN rnf, GEN *b, GEN *cb) {*b=gmael(rnf,2,1); *cb=gmael(rnf,2,2);}
+INLINE GEN
+rnf_get_polabs(GEN rnf) { return gmael(rnf,11,1); }
+INLINE GEN
+rnf_get_pol(GEN rnf) { return gel(rnf,1); }
+INLINE GEN
+rnf_get_disc(GEN rnf) { return gel(rnf,3); }
+INLINE GEN
+rnf_get_index(GEN rnf) { return gel(rnf,4); }
+INLINE long
+rnf_get_varn(GEN rnf) { return varn(gel(rnf,1)); }
+INLINE GEN
+rnf_get_nfpol(GEN rnf) { return gmael(rnf,10,1); }
+INLINE long
+rnf_get_nfvarn(GEN rnf) { return varn(gmael(rnf,10,1)); }
+INLINE GEN
+rnf_get_zk(GEN rnf) { return gel(rnf,7); }
+INLINE GEN
+rnf_get_map(GEN rnf) { return gel(rnf,11); }
+INLINE GEN
+rnf_get_invzk(GEN rnf) { return gel(rnf,8); }
+
+/* I integral (not necessarily HNF), G ZM, rounded Cholesky form of a weighted
+ * T2 matrix. Return m in I with T2(m) small */
+INLINE GEN
+idealpseudomin(GEN I, GEN G)
+{
+  GEN u = ZM_lll(ZM_mul(G, I), 0.99, LLL_IM);
+  return ZM_ZC_mul(I, gel(u,1));
+}
+/* I, G as in idealpseudomin. Return an irrational m in I with T2(m) small */
+INLINE GEN
+idealpseudomin_nonscalar(GEN I, GEN G)
+{
+  GEN u = ZM_lll(ZM_mul(G, I), 0.99, LLL_IM);
+  GEN m = ZM_ZC_mul(I, gel(u,1));
+  if (ZV_isscalar(m) && lg(u) > 2) m = ZM_ZC_mul(I, gel(u,2));
+  return m;
+}
+
+INLINE GEN
+idealred_elt(GEN nf, GEN I) {
+  pari_sp av = avma;
+  GEN u = idealpseudomin(I, nf_get_roundG(nf));
+  return gerepileupto(av, u);
+}
+INLINE GEN
+idealred(GEN nf, GEN I) { return idealred0(nf, I, NULL); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                              CLOSURES                           */
+/*                                                                 */
+/*******************************************************************/
+INLINE long closure_arity(GEN C)    { return C[1]; }
+INLINE const char *closure_codestr(GEN C)  { return GSTR(gel(C,2))-1; }
+INLINE GEN closure_get_code(GEN C)  { return gel(C,2); }
+INLINE GEN closure_get_oper(GEN C)  { return gel(C,3); }
+INLINE GEN closure_get_data(GEN C)  { return gel(C,4); }
+INLINE GEN closure_get_dbg(GEN C)   { return gel(C,5); }
+INLINE GEN closure_get_text(GEN C)  { return gel(C,6); }
+INLINE GEN closure_get_frame(GEN C) { return gel(C,7); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                               ERRORS                            */
+/*                                                                 */
+/*******************************************************************/
+INLINE long
+err_get_num(GEN e) { return e[1]; }
+INLINE GEN
+err_get_compo(GEN e, long i) { return gel(e, i+1); }
+
+INLINE void
+pari_err_BUG(const char *f) { pari_err(e_BUG,f); }
+INLINE void
+pari_err_CONSTPOL(const char *f) { pari_err(e_CONSTPOL, f); }
+INLINE void
+pari_err_COPRIME(const char *f, GEN x, GEN y) { pari_err(e_COPRIME, f,x,y); }
+INLINE void
+pari_err_DIM(const char *f) { pari_err(e_DIM, f); }
+INLINE void
+pari_err_FILE(const char *f, const char *g) { pari_err(e_FILE, f,g); }
+INLINE void
+pari_err_FLAG(const char *f) { pari_err(e_FLAG,f); }
+INLINE void
+pari_err_IMPL(const char *f) { pari_err(e_IMPL,f); }
+INLINE void
+pari_err_INV(const char *f, GEN x) { pari_err(e_INV,f,x); }
+INLINE void
+pari_err_IRREDPOL(const char *f, GEN x) { pari_err(e_IRREDPOL, f,x); }
+INLINE void
+pari_err_DOMAIN(const char *f, const char *v, const char *op, GEN l, GEN x) { pari_err(e_DOMAIN, f,v,op,l,x); }
+INLINE void
+pari_err_COMPONENT(const char *f, const char *op, GEN l, GEN x) { pari_err(e_COMPONENT, f,op,l,x); }
+INLINE void
+pari_err_MAXPRIME(ulong c) { pari_err(e_MAXPRIME, c); }
+INLINE void
+pari_err_OP(const char *f, GEN x, GEN y) { pari_err(e_OP, f,x,y); }
+INLINE void
+pari_err_OVERFLOW(const char *f) { pari_err(e_OVERFLOW, f); }
+INLINE void
+pari_err_PREC(const char *f) { pari_err(e_PREC,f); }
+INLINE void
+pari_err_PACKAGE(const char *f) { pari_err(e_PACKAGE,f); }
+INLINE void
+pari_err_PRIME(const char *f, GEN x) { pari_err(e_PRIME, f,x); }
+INLINE void
+pari_err_MODULUS(const char *f, GEN x, GEN y) { pari_err(e_MODULUS, f,x,y); }
+INLINE void
+pari_err_ROOTS0(const char *f) { pari_err(e_ROOTS0, f); }
+INLINE void
+pari_err_SQRTN(const char *f, GEN x) { pari_err(e_SQRTN, f,x); }
+INLINE void
+pari_err_TYPE(const char *f, GEN x) { pari_err(e_TYPE, f,x); }
+INLINE void
+pari_err_TYPE2(const char *f, GEN x, GEN y) { pari_err(e_TYPE2, f,x,y); }
+INLINE void
+pari_err_VAR(const char *f, GEN x, GEN y) { pari_err(e_VAR, f,x,y); }
+INLINE void
+pari_err_PRIORITY(const char *f, GEN x, const char *op, long v)
+{ pari_err(e_PRIORITY, f,x,op,v); }
diff --git a/src/headers/parinf.h b/src/headers/parinf.h
new file mode 100644
index 0000000..171eab9
--- /dev/null
+++ b/src/headers/parinf.h
@@ -0,0 +1,202 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* output of get_nf and get_bnf */
+enum {
+  typ_NULL = 0,
+  typ_POL,
+  typ_Q,
+  typ_NF,
+  typ_BNF,
+  typ_BNR,
+  typ_ELL, /* elliptic curve */
+  typ_QUA, /* quadclassunit  */
+  typ_GAL, /* galoisinit     */
+  typ_BID,
+  typ_PRID,
+  typ_MODPR,
+  typ_RNF
+};
+
+/* idealtyp */
+enum {
+  id_PRINCIPAL = 0,
+  id_PRIME,
+  id_MAT
+};
+
+typedef struct {
+  GEN x; /* defining polynomial (monic, integral) */
+  GEN x0; /* original defining polynomial (integral) */
+  GEN bas;  /* Z-basis of O_K (t_VEC of t_POL) */
+  long r1; /* number of real places of K */
+/* possibly NULL = irrelevant or not computed */
+  GEN dK; /* disc(K) */
+  GEN dKP; /* "primes" dividing disc(K) [if we have a composite in the list
+              then the structure may not be correct] */
+  GEN index; /* [O_K : Z[X]/(x)] */
+  GEN unscale; /* x = C*x0(X / unscale), rational */
+  GEN dx;   /* disc(x) */
+  GEN basden; /* [nums(bas), dens(bas)] */
+} nfbasic_t;
+
+typedef struct {
+  GEN T, dT; /* monic defining polynomial, disc(T) */
+  GEN T0; /* ORIGINAL polynomial T0 */
+  GEN unscale; /* T = C*T0(x / unscale), rational */
+  GEN dK; /* field discriminant */
+  GEN index; /* index of power basis in maximal order */
+  GEN dTP, dTE; /* (possibly partial) factorization of dT, primes / exponents */
+  GEN dKP, dKE; /* (possibly partial) factorization of dK, primes / exponents */
+  GEN basis; /* Z-basis for maximal order */
+} nfmaxord_t;
+
+typedef struct {
+  GEN x;
+  GEN ro;   /* roots of x */
+  long r1;
+  GEN basden;
+  long prec;
+/* possibly -1 = irrelevant or not computed */
+  long extraprec;
+/* possibly NULL = irrelevant or not computed */
+  GEN M;
+  GEN G;
+} nffp_t;
+
+/* qfr3 / qfr5 */
+struct qfr_data { GEN D, sqrtD, isqrtD; };
+
+/* various flags for nf/bnf routines */
+enum {
+  nf_ORIG = 1,
+  nf_GEN = 1,
+  nf_ABSOLUTE = 2,
+  nf_FORCE = 2,
+  nf_ALL = 4,
+  nf_GENMAT = 4,
+  nf_INIT = 4,
+  nf_RAW = 8,
+  nf_RED = 8,
+  nf_PARTIALFACT = 16,
+  nf_ROUND2 = 64, /* obsolete */
+  nf_ADDZK =  256,
+  nf_GEN_IF_PRINCIPAL = 512
+};
+
+enum {
+  rnf_REL = 1,
+  rnf_COND = 2
+};
+
+/* LLL */
+enum {
+  LLL_KER  = 1, /* only kernel */
+  LLL_IM   = 2, /* only image */
+  LLL_ALL  = 4, /* kernel & image */
+  LLL_GRAM       = 0x100,
+  LLL_KEEP_FIRST = 0x200,
+  LLL_INPLACE    = 0x400
+};
+
+/* HNF */
+enum { hnf_MODID = 1, hnf_PART = 2, hnf_CENTER = 4 };
+
+/* for minim */
+enum {
+  min_ALL       = 0,
+  min_FIRST     = 1,
+  min_PERF      = 2,
+  min_VECSMALL  = 3,
+  min_VECSMALL2 = 4
+};
+/* for fincke_pohst() */
+typedef struct FP_chk_fun {
+  GEN (*f)(void *,GEN);
+  /* f_init allowed to permute the columns of u and r */
+  GEN (*f_init)(struct FP_chk_fun*,GEN,GEN);
+  GEN (*f_post)(struct FP_chk_fun*,GEN,GEN);
+  void *data;
+  long skipfirst;
+} FP_chk_fun;
+
+/* for ideallog / zlog */
+typedef struct {
+  GEN lists; /* lists[i] = */
+  GEN ind;  /* ind[i] = start of vector */
+  GEN P, e; /* finit part of conductor = prod P^e */
+  GEN archp; /* archimedean part of conductor, in permutation form */
+  long n;  /* total number of generators for all (O_K/P^e)^* and (O_K/f_oo) */
+  GEN U; /* base change matrix from generators to bid.gen */
+} zlog_S;
+
+GEN fincke_pohst(GEN a,GEN BOUND,long stockmax,long PREC, FP_chk_fun *CHECK);
+void remake_GM(GEN nf, nffp_t *F, long prec);
+GEN nfbasic_to_nf(nfbasic_t *T, GEN ro, long prec);
+
+void init_zlog_bid(zlog_S *S, GEN bid);
+GEN  log_gen_arch(zlog_S *S, long index);
+GEN  log_gen_pr(zlog_S *S, long index, GEN nf, long e);
+GEN  zlog(GEN nf, GEN a, GEN sgn, zlog_S *S);
+
+/* conversions basis / alg */
+
+/* nf a genuine NF, x an nfelt (t_COL) or t_MAT whose columns represent nfelts.
+ * Return the corresponding elements as t_POLs (implicitly mod nf.pol) */
+#define coltoliftalg(nf,x) (gmul(gel((nf),7), (x)))
+GEN    poltobasis(GEN nf,GEN x);
+GEN    coltoalg(GEN nf,GEN x);
+
+/* Other number fields routines */
+GEN    archstar_full_rk(GEN x, GEN bas, GEN v, GEN gen);
+GEN    check_and_build_cycgen(GEN bnf);
+long   check_LIMC(long LIMC, long LIMCMAX);
+GEN    checkbid_i(GEN bid);
+GEN    checkbnf_i(GEN bnf);
+GEN    checknf_i(GEN nf);
+GEN    pow_ei_mod_p(GEN nf, long I, GEN n, GEN p);
+GEN    galoisbig(GEN x, long prec);
+GEN    get_arch_real(GEN nf,GEN x,GEN *emb,long prec);
+GEN    get_bas_den(GEN bas);
+void   nf_set_multable(GEN nf, GEN bas, GEN basden);
+GEN    get_nfindex(GEN bas);
+GEN    get_proj_modT(GEN basis, GEN T, GEN p);
+GEN    get_roots(GEN x,long r1,long prec);
+GEN    get_theta_abstorel(GEN T, GEN pol, GEN k);
+GEN    idealsqrtn(GEN nf, GEN x, GEN gn, int strict);
+GEN    init_unif_mod_fZ(GEN L);
+GEN    init_units(GEN BNF);
+GEN    make_integral(GEN nf, GEN L0, GEN f, GEN listpr);
+GEN    maxord_i(GEN p, GEN f, long mf, GEN w, long flag);
+GEN    nf_deg1_prime(GEN nf);
+GEN    nfpol_to_Flx(GEN nf, GEN pol, ulong *ptp);
+GEN    nfroots_split(GEN nf, GEN pol);
+GEN    pidealprimeinv(GEN nf, GEN x);
+GEN    primedec_apply_kummer(GEN nf,GEN pol,long e,GEN p);
+GEN    prodid(GEN nf, GEN I);
+GEN    rnfallbase(GEN nf, GEN *ppol, GEN *pD, GEN *pd, GEN *pfi);
+GEN    rnf_basM(GEN rnf);
+GEN    special_anti_uniformizer(GEN nf, GEN pr);
+GEN    subgroupcondlist(GEN cyc, GEN bound, GEN listKer);
+void   testprimes(GEN bnf, GEN bound);
+GEN    to_Fp_simple(GEN nf, GEN x, GEN ffproj);
+GEN    unif_mod_fZ(GEN pr, GEN F);
+GEN    unnf_minus_x(GEN x);
+GEN    ideallog_sgn(GEN nf, GEN x, GEN sgn, GEN bid);
+GEN    zlog_units(GEN nf, GEN U, GEN sgnU, GEN bid);
+GEN    zlog_units_noarch(GEN nf, GEN U, GEN bid);
+
+/* Dedekind zeta */
+GEN  zeta_get_limx(long r1, long r2, long bit);
+long zeta_get_i0(long r1, long r2, long bit, GEN limx);
+long zeta_get_N0(GEN C,  GEN limx);
diff --git a/src/headers/pariold.h b/src/headers/pariold.h
new file mode 100644
index 0000000..13d0ad4
--- /dev/null
+++ b/src/headers/pariold.h
@@ -0,0 +1,428 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* For compatibility with 1.x.x */
+/*functions renamed*/
+#define mpexp1 mpexpm1
+#define ggamd ggammah
+#define gach gacosh
+#define gash gasinh
+#define gath gatanh
+#define gch gcosh
+#define gsh gsinh
+#define gth gtanh
+#define recip serreverse
+#define gcmpX gequalX
+#define ZM_hnfremdiv ZM_hnfdivrem
+#define rnfidealhermite rnfidealhnf
+#define rnfelementabstorel rnfeltabstorel
+#define rnfelementreltoabs rnfeltreltoabs
+#define rnfelementdown rnfeltdown
+#define rnfelementup rnfeltup
+#define exp_Ir expIr
+#define vecbezout gcdext0
+#define vecbezoutres polresultantext
+#define init_primepointer init_primepointer_geq
+#define geulerphi eulerphi
+#define numbdiv numdiv
+#define gnumbdiv numdiv
+#define gsumdivk sumdivk
+#define gnextprime nextprime
+#define gprecprime precprime
+#define leftright_pow_fold gen_pow_fold
+#define leftright_pow_u_fold gen_powu_fold
+#define subell ellsub
+#define addell elladd
+#define powell ellmul
+#define ggval gvaluation
+#define stackmalloc stack_malloc
+#define fprintferr err_printf
+#define msgTIMER timer_printf
+#define TIMER timer_delay
+#define TIMERread timer_get
+#define TIMERstart timer_start
+#define rnfinitalg rnfinit
+#define ordell ellordinate
+#define gcmp0 gequal0
+#define gcmp1 gequal1
+#define gcmp_1 gequalm1
+#define nfsmith nfsnf
+#define certifybuchall bnfcertify
+#define greffe(x,y,z) (RgX_to_ser(x,y))
+#define newbloc newblock
+#define killbloc killblock
+#define taille2 gsizeword
+#define taille gsizebyte
+#define polymodrecip modreverse
+#define primedec idealprimedec
+#define initalg nfinit
+#define initalgred nfinitred
+#define initalgred2 nfinitred2
+#define initell ellinit
+#define smallinitell smallellinit
+#define isunit bnfisunit
+#define zideallog ideallog
+#define ideallllred idealred0
+#define ideal_two_elt idealtwoelt
+#define ideal_two_elt2 idealtwoelt2
+#define ideal_two_elt0 idealtwoelt0
+#define gregula quadregulator
+#define gfundunit quadunit
+#define minideal(x,y,z,t) idealmin(x,y,z)
+#define idealhermite idealhnf
+#define srgcd(x) RgX_gcd(x)
+#define nfdiscf0(x) nfdisc0(x)
+#define discf(x) nfdisc(x)
+#define discsr(x) poldisc0((x),-1)
+#define factorpadic4 factorpadic
+#define smith2 smithall
+#define gsmith2 gsmithall
+#define derivpol RgX_deriv
+#define gpmalloc pari_malloc
+#define gprealloc pari_realloc
+#define gpfree pari_free
+#define pariprintf pari_printf
+#define pariputc pari_putc
+#define pariputs pari_puts
+#define pariflush pari_flush
+#define ellap0(e,p,flag) ellap(e,p)
+#define apell2(e,p) ellap(e,p)
+#define apell ellap
+#define gscalsmat scalarmat_s
+#define sqred qfgaussred
+#define signat qfsign
+#define infile pari_infile
+#define errfile pari_errfile
+#define logfile pari_logfile
+#define voir dbgGEN
+#define pointch ellchangepoint
+#define coordch ellchangecurve
+#define Flx_rand random_Flx
+#define FpX_rand random_FpX
+#define galois polgalois
+#define sindexlexsort indexlexsort
+#define sindexsort indexsort
+#define sindexrank indexrank
+#define decomp Z_factor
+#define gmodulcp gmodulo
+#define forcecopy gcopy
+#define lseriesell elllseries
+#define uissquarerem uissquareall
+#define Z_issquarerem Z_issquareall
+#define gissquarerem gissquareall
+#define gcarrecomplet gissquareall
+#define gcarreparfait gissquare
+#define rnfhermitebasis rnfhnfbasis
+#define wf weberf
+#define wf1 weberf1
+#define wf2 weberf2
+#define coefs_to_col mkcoln
+#define coefs_to_int mkintn
+#define coefs_to_pol mkpoln
+#define coefs_to_vec mkvecn
+#define localreduction elllocalred
+#define idmat matid
+#define globalreduction ellglobalred
+#define taniyama(e) elltaniyama((e),precdl)
+#define chinois chinese
+#define binome binomial
+#define egalii equalii
+#define gegal gequal
+#define gegalgs gequalgs
+#define gegalsg gequalsg
+#define gzero gen_0
+#define gun   gen_1
+#define gdeux gen_2
+#define realzero real_0
+#define realzero_bit real_0_bit
+#define realun real_1
+#define realmun real_m1
+#define gen2str GENtostr
+#define gpui gpow
+#define gpuigs gpowgs
+#define classno3 hclassno
+#define strtoGEN gp_read_str
+#define flisexpr gp_read_str
+#define flisseq gp_read_str
+#define lisseq readseq
+#define lisGEN gp_read_stream
+#define lisexpr readseq
+#define permute numtoperm
+#define permuteInv permtonum
+#define evallgef(x) 0
+#define lgef lg
+#define setlgef setlg
+#define leadingcoeff(x) (pollead((x),-1))
+#define poldivres poldivrem
+#define nfdivres nfdivrem
+#define gred gcopy
+#define pvaluation Z_pvalrem
+#define svaluation u_lvalrem
+#define isprincipalrayall bnrisprincipal
+#define rayclassno bnrclassno
+#define rayclassnolist bnrclassnolist
+#define idealhermite2 idealhnf0
+#define gener_Fp pgener_Fp
+#define gener_Fl pgener_Fl
+#define cyclo polcyclo
+#define tchebi polchebyshev1
+#define legendre pollegendre
+#define subcyclo polsubcyclo
+#define leftright_pow gen_pow
+#define leftright_pow_u gen_powu
+
+#define apprgen padicappr
+#define apprgen9 padicappr
+#define factmod9 factorff
+#define ggrandocp ggrando
+#define glogagm glog
+#define logagm  mplog
+#define mpsqrtz  gopgz(absr,(x),(y))
+#define adduumod Fl_add
+#define subuumod Fl_sub
+#define muluumod Fl_mul
+#define divuumod Fl_div
+#define powuumod Fl_powu
+#define invumod Fl_inv
+#define invsmod Fl_inv_signed
+#define mpinvmod Fp_inv
+#define powmodulo Fp_pow
+#define mpsqrtmod Fp_sqrt
+#define mpsqrtnmod Fp_sqrtn
+#define mpsqrt  sqrtr
+#define mpsqrtn  sqrtnr
+#define resii  remii
+#define resis  remis
+#define ressi  remsi
+#define resss  remss
+#define resiiz  remiiz
+#define resisz  remisz
+#define ressiz  remsiz
+#define resssz  remssz
+#define gres    grem
+#define lres    lrem
+#define gdivise gdvd
+#define divise dvdii
+#define mpdivis dvdiiz
+#define mpdivisis dvdisz
+#define mpent mpfloor
+#define mpentz mpfloorz
+#define mpnegz(x,y) \
+  STMT_START {pari_sp _av=avma;mpaff(mpneg(x),y);avma=_av;} STMT_END
+#define mpabsz(x,y) \
+  STMT_START {pari_sp _av=avma;mpaff(mpabs(x),y);avma=_av;} STMT_END
+#define absrz(x,z)  mpabsz((x),(z))
+#define negrz(x,z)  mpnegz((x),(z))
+
+/* Following deprecated for a long time now. Or really, really bad, e.g
+ * un, init, er. */
+#ifdef PARI_OLD_NAMES
+#define err pari_err
+#define init pari_init
+
+#define zero  (long)gen_0
+#define un    (long)gen_1
+#define deux  (long)gen_2
+#define lhalf (long)ghalf
+
+/* removed GEN subtypes */
+#define t_FRACN  t_FRAC
+#define t_RFRACN t_RFRAC
+#define is_frac_t(t) ( (t) == t_FRAC )
+#define is_rfrac_t(t) ( (t) == t_RFRAC )
+
+/*casts*/
+#define labsi   (long)absi
+#define labsr   (long)absr
+#define lach    (long)gacosh
+#define lacos   (long)gacos
+#define ladd    (long)gadd
+#define laddgs  (long)gaddgs
+#define laddii  (long)addii
+#define laddir  (long)addir
+#define laddis  (long)addis
+#define laddrr  (long)addrr
+#define laddsg  (long)gaddsg
+#define laddsi  (long)addsi
+#define laddrs  (long)addrs
+#define laddsr  (long)addsr
+#define ladj    (long)adj
+#define larg    (long)garg
+#define lash    (long)gasinh
+#define lasin   (long)gasin
+#define lassmat (long)matcompanion
+#define latan   (long)gatan
+#define lath    (long)gatanh
+#define lbezout (long)bezout
+#define lbinome (long)binomial
+#define lcaract (long)caract
+#define lcaradj (long)caradj
+#define lceil   (long)gceil
+#define lch     (long)gcosh
+#define lchangevar (long)changevar
+#define lclone  (long)gclone
+#define lconcat (long)concat
+#define lconj   (long)gconj
+#define lcontent (long)content
+#define lcopy   (long)gcopy
+#define lcos    (long)gcos
+#define lcvtoi  (long)gcvtoi
+#define lderiv  (long)deriv
+#define ldet2   (long)det2
+#define ldet    (long)det
+#define ldeuc   (long)gdeuc
+#define ldiscsr (long)discsr
+#define ldiventgs (long)gdiventgs
+#define ldiventsg (long)gdiventsg
+#define ldivgs  (long)gdivgs
+#define ldivii  (long)divii
+#define ldivir  (long)divir
+#define ldivis  (long)divis
+#define ldivmod (long)gdivmod
+#define ldivri  (long)divri
+#define ldivrr  (long)divrr
+#define ldivrs  (long)divrs
+#define ldivsg    (long)gdivsg
+#define ldivsi  (long)divsi
+#define ldivsr  (long)divsr
+#define ldvmdii (long)dvmdii
+#define ldvmdis (long)dvmdis
+#define ldvmdsi (long)dvmdsi
+#define lexp    (long)gexp
+#define lfibo   (long)fibo
+#define lfloor  (long)gfloor
+#define lfrac   (long)gfrac
+#define lgamd   (long)ggammah
+#define lgamma  (long)ggamma
+#define lgauss  (long)gauss
+#define lgcd    (long)ggcd
+#define lgetg   (long)cgetg
+#define lgeti   (long)cgeti
+#define lgetp   (long)cgetp
+#define lgetr   (long)cgetr
+#define licopy  (long)icopy
+#define limag   (long)gimag
+#define linteg  (long)integ
+#define linv    (long)ginv
+#define linvmat (long)RgM_inv
+#define linvmod (long)ginvmod
+#define llegendre (long)legendre
+#define llift   (long)lift
+#define llngamma  (long)glngamma
+#define llog    (long)glog
+#define lmaxgs    (long)gmaxgs
+#define lmax    (long)gmax
+#define lmaxsg    (long)gmaxsg
+#define lmings    (long)gmings
+#define lmin    (long)gmin
+#define lminsg    (long)gminsg
+#define lmodgs    (long)gmodgs
+#define lmodii  (long)modii
+#define lmod    (long)gmod
+#define lmodsg    (long)gmodsg
+#define lmodsi  (long)modsi
+#define lmodulcp  (long)gmodulo
+#define lmodulo   (long)gmodulo
+#define lmpabs  (long)mpabs
+#define lmpadd  (long)mpadd
+#define lmpcos  (long)mpcos
+#define lmpdiv  (long)mpdiv
+#define lmpent  (long)mpent
+#define lmpeuler (long)mpeuler
+#define lmpexp1 (long)mpexpm1
+#define lmpexp  (long)mpexp
+#define lmpfact (long)mpfact
+#define lmplog  (long)mplog
+#define lmpmul  (long)mpmul
+#define lmpneg  (long)mpneg
+#define lmppgcd (long)mppgcd
+#define lmppi   (long)mppi
+#define lmpshift (long)mpshift
+#define lmpsin  (long)mpsin
+#define lmpsqrt (long)mpsqrt
+#define lmpsub  (long)mpsub
+#define lmptrunc (long)mptrunc
+#define lmul2n  (long)gmul2n
+#define lmulgs  (long)gmulgs
+#define lmulii  (long)mulii
+#define lmulir  (long)mulir
+#define lmulis  (long)mulis
+#define lmul    (long)gmul
+#define lmulri  (long)mulri
+#define lmulrr  (long)mulrr
+#define lmulrs  (long)mulrs
+#define lmulsg  (long)gmulsg
+#define lmulsi  (long)mulsi
+#define lmulsr  (long)mulsr
+#define lmulss  (long)mulss
+#define lnegi   (long)negi
+#define lneg    (long)gneg
+#define lnegr   (long)negr
+#define lnorml2 (long)gnorml2
+#define lnorm   (long)gnorm
+#define lpile   (long)gerepile
+#define lpilecopy (long)gerepilecopy
+#define lpileupto (long)gerepileupto
+#define lpileuptoint (long)gerepileuptoint
+#define lpileuptoleaf (long)gerepileuptoleaf
+#define lpoleval (long)poleval
+#define lpowgs  (long)gpowgs
+#define lprec   (long)gprec
+#define lpsi    (long)gpsi
+#define lpuigs  (long)gpuigs
+#define lpui    (long)gpui
+#define lquadgen (long)quadgen
+#define lquadpoly (long)quadpoly
+#define lracine (long)sqrtint
+#define lrcopy  (long)rcopy
+#define lreal   (long)greal
+#define lrecip  (long)recip
+#define lred    (long)gred
+#define lremii  (long)remii
+#define lrem    (long)grem
+#define lrndtoi (long)grndtoi
+#define lroots  (long)roots
+#define lscalmat (long)scalarmat
+#define lscalsmat (long)scalarmat_s
+#define lsh     (long)gsinh
+#define lshifti (long)shifti
+#define lshift  (long)gshift
+#define lshiftr (long)shiftr
+#define lsin    (long)gsin
+#define lsqri   (long)sqri
+#define lsqr    (long)gsqr
+#define lsqrt   (long)gsqrt
+#define lstoi   (long)stoi
+#define lsub    (long)gsub
+#define lsubgs  (long)gsubgs
+#define lsubii  (long)subii
+#define lsubir  (long)subir
+#define lsubis  (long)subis
+#define lsubres (long)resultant
+#define lsubri  (long)subri
+#define lsubrr  (long)subrr
+#define lsubrs  (long)subrs
+#define lsubst  (long)gsubst
+#define lsubsg  (long)gsubsg
+#define lsubsi  (long)subsi
+#define lsubsr  (long)subsr
+#define ltan    (long)gtan
+#define ltchebi (long)polchebyshev
+#define lth     (long)gtanh
+#define ltrace  (long)gtrace
+#define ltrans  (long)gtrans
+#define ltrunc  (long)gtrunc
+#define lutoi   (long)utoi
+#define lround  (long)ground
+#define ldiv    (long)gdiv
+#endif
diff --git a/src/headers/paripriv.h b/src/headers/paripriv.h
new file mode 100644
index 0000000..93fadcd
--- /dev/null
+++ b/src/headers/paripriv.h
@@ -0,0 +1,727 @@
+/* Copyright (C) 2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+BEGINEXTERN
+/* hashtables */
+hashtable *hashstr_import_static(hashentry *e, ulong size);
+void hashstr_dbg(hashtable *h);
+
+/* for qsort */
+typedef int (*QSCOMP)(const void *, const void *);
+
+#define ucoeff(a,i,j)  (((ulong**)(a))[j][i])
+#define umael(a,i,j)   (((ulong**)(a))[i][j])
+#define uel(a,i)       (((ulong*)(a))[i])
+
+/* to manipulate 'blocs' */
+#define BL_HEAD 4
+#define bl_base(x) (void*)((x) - BL_HEAD)
+#define bl_refc(x) (((GEN)x)[-4])
+#define bl_next(x) (((GEN*)x)[-3])
+#define bl_prev(x) (((GEN*)x)[-2])
+#define bl_num(x)  (((GEN)x)[-1])
+INLINE void
+clone_lock(GEN C) { if (isclone(C)) ++bl_refc(C); }
+INLINE void
+clone_unlock(GEN C) { if (isclone(C)) gunclone(C); }
+
+/* swap */
+#define lswap(x,y) {long _z=x; x=y; y=_z;}
+#define pswap(x,y) {GEN *_z=x; x=y; y=_z;}
+#define swap(x,y)  {GEN  _z=x; x=y; y=_z;}
+#define dswap(x,y) { double _t=x; x=y; y=_t; }
+#define pdswap(x,y) { double* _t=x; x=y; y=_t; }
+#define swapspec(x,y, nx,ny) {swap(x,y); lswap(nx,ny);}
+
+/* unused */
+GEN ellheightoo(GEN e, GEN z, long prec);
+void ellprint(GEN e);
+
+/* binary splitting */
+struct abpq { GEN *a, *b, *p, *q; };
+struct abpq_res { GEN P, Q, B, T; };
+void abpq_init(struct abpq *A, long n);
+void abpq_sum(struct abpq_res *r, long n1, long n2, struct abpq *A);
+
+/* generic */
+GEN trans_fix_arg(long *prec, GEN *s0, GEN *sig, GEN *tau, pari_sp *av, GEN *res);
+GEN sort_factor_pol(GEN y, int (*cmp)(GEN,GEN));
+
+/* loops */
+GEN incloop(GEN a);
+GEN resetloop(GEN a, GEN b);
+GEN setloop(GEN a);
+
+/* parser */
+GEN  iferrpari(GEN a, GEN b, GEN c);
+void forpari(GEN a, GEN b, GEN node);
+void untilpari(GEN a, GEN b);
+void whilepari(GEN a, GEN b);
+GEN  ifpari(GEN g, GEN a, GEN b);
+GEN  andpari(GEN a, GEN b);
+GEN  orpari(GEN a, GEN b);
+void ifpari_void(GEN g, GEN a, GEN b);
+GEN  ifpari_multi(GEN g, GEN a);
+GEN  geval_gp(GEN x, GEN t);
+
+GEN  gadde(GEN *x, GEN y);
+GEN  gadd1e(GEN *x);
+GEN  gdive(GEN *x, GEN y);
+GEN  gdivente(GEN *x, GEN y);
+GEN  gdivrounde(GEN *x, GEN y);
+GEN  gmode(GEN *x, GEN y);
+GEN  gmule(GEN *x, GEN y);
+GEN  gshiftle(GEN *x, long n);
+GEN  gshiftre(GEN *x, long n);
+GEN  gsube(GEN *x, GEN y);
+GEN  gsub1e(GEN *x);
+GEN  gshift_right(GEN x, long n);
+
+GEN  derivnum0(GEN a, GEN code, long prec);
+GEN  derivfun0(GEN code, GEN args, long prec);
+GEN  direuler0(GEN a, GEN b, GEN code, GEN c);
+void forcomposite(GEN a, GEN b, GEN code);
+void fordiv(GEN a, GEN code);
+void forell0(long a, long b, GEN code);
+void forprime(GEN a, GEN b, GEN code);
+void forstep(GEN a, GEN b, GEN s, GEN code);
+void forsubgroup0(GEN cyc, GEN bound, GEN code);
+void forvec(GEN x, GEN code, long flag);
+void forpart0(GEN k, GEN code , GEN nbound, GEN abound);
+GEN  intcirc0(GEN a, GEN R, GEN code, GEN tab, long prec);
+GEN  intfourcos0(GEN a, GEN b, GEN x, GEN code, GEN tab, long prec);
+GEN  intfourexp0(GEN a, GEN b, GEN x, GEN code, GEN tab, long prec);
+GEN  intfoursin0(GEN a, GEN b, GEN x, GEN code, GEN tab, long prec);
+GEN  intfuncinit0(GEN a, GEN b, GEN code, long flag, long m, long prec);
+GEN  intlaplaceinv0(GEN sig, GEN x, GEN code, GEN tab, long prec);
+GEN  intmellininv0(GEN sig, GEN x, GEN code, GEN tab, long prec);
+GEN  intnum0(GEN a, GEN b, GEN code, GEN tab, long prec);
+GEN  intnuminit0(GEN a, GEN b, GEN tab, long prec);
+GEN  intnuminitgen0(GEN a, GEN b, GEN code, long m, long flag, long prec);
+GEN  intnumromb0(GEN a, GEN b, GEN code, long flag, long prec);
+GEN  matrice(GEN nlig, GEN ncol, GEN code);
+GEN  prodeuler0(GEN a, GEN b, GEN code, long prec);
+GEN  prodinf0(GEN a, GEN code, long flag, long prec);
+GEN  produit(GEN a, GEN b, GEN code, GEN x);
+GEN  somme(GEN a, GEN b, GEN code, GEN x);
+GEN  sumalt0(GEN a, GEN code,long flag, long prec);
+GEN  sumdivexpr(GEN num, GEN code);
+GEN  sumdivmultexpr(GEN num, GEN code);
+GEN  suminf0(GEN a, GEN code, long prec);
+GEN  sumnum0(GEN a, GEN sig, GEN code, GEN tab, long flag, long prec);
+GEN  sumnumalt0(GEN a, GEN sig, GEN code, GEN tab, long flag, long prec);
+GEN  sumnuminit0(GEN a, GEN tab, long sgn, long prec);
+GEN  sumpos0(GEN a, GEN code, long flag,long prec);
+GEN  vecexpr0(GEN nmax, GEN code, GEN pred);
+GEN  vecexpr1(GEN nmax, GEN code, GEN pred);
+GEN  vecteursmall(GEN nmax, GEN code);
+GEN  vecteur(GEN nmax, GEN n);
+GEN  vvecteur(GEN nmax, GEN n);
+GEN  zbrent0(GEN a, GEN b, GEN code, long prec);
+
+/* mt */
+void mt_sigint(void);
+void mt_err_recover(long er);
+void mt_init_stack(size_t s);
+int  mt_is_thread(void);
+GEN  parapply_worker(GEN d, GEN code);
+GEN  pareval_worker(GEN code);
+void parfor(GEN a, GEN b, GEN code, GEN code2);
+GEN  parfor_worker(GEN i, GEN C);
+void parforprime(GEN a, GEN b, GEN code, GEN code2);
+GEN  parvector_worker(GEN i, GEN C);
+
+/* multiprecision */
+GEN   addrex01(GEN x);
+GEN   adduispec_offset(ulong s, GEN x, long offset, long nx);
+int   lgcdii(ulong* d, ulong* d1, ulong* u, ulong* u1, ulong* v, ulong* v1, ulong vmax);
+ulong rgcduu(ulong d, ulong d1, ulong vmax, ulong* u, ulong* u1, ulong* v, ulong* v1, long *s);
+ulong xgcduu(ulong d, ulong d1, int f, ulong* v, ulong* v1, long *s);
+ulong xxgcduu(ulong d, ulong d1, int f, ulong* u, ulong* u1, ulong* v, ulong* v1, long *s);
+GEN   divgunu(GEN x, ulong i);
+GEN   divrunu(GEN x, ulong i);
+GEN   muliispec(GEN x, GEN y, long nx, long ny);
+GEN   red_montgomery(GEN T, GEN N, ulong inv);
+GEN   sqrispec(GEN x, long nx);
+GEN   subrex01(GEN x);
+GEN   modr_safe(GEN x, GEN y);
+ulong *convi(GEN x, long *l);
+
+int approx_0(GEN x, GEN y);
+GEN bernfrac_using_zeta(long n);
+
+/* powers */
+GEN    rpowuu(ulong a, ulong n, long prec);
+ulong  u_pow10(int n);
+
+/* floats */
+double dabs(double s, double t);
+void   dcxlog(double s, double t, double *a, double *b);
+double dnorm(double s, double t);
+double dbllog2(GEN z);
+
+/* hnf */
+GEN hnfadd(GEN m,GEN p,GEN* ptdep,GEN* ptA,GEN* ptC,GEN extramat,GEN extraC);
+GEN hnfadd_i(GEN m,GEN p,GEN* ptdep,GEN* ptA,GEN* ptC,GEN extramat,GEN extraC);
+GEN hnfspec_i(GEN m,GEN p,GEN* ptdep,GEN* ptA,GEN* ptC,long k0);
+GEN hnfspec(GEN m,GEN p,GEN* ptdep,GEN* ptA,GEN* ptC,long k0);
+GEN mathnfspec(GEN x, GEN *ptperm, GEN *ptdep, GEN *ptB, GEN *ptC);
+GEN ZM_hnfmodall_i(GEN x, GEN dm, long flag);
+
+GEN LLL_check_progress(GEN Bnorm, long n0, GEN m, int final, long *ti_LLL);
+GEN extendedgcd(GEN A);
+
+/* miscellaneous linear algebra */
+GEN  imagecomplspec(GEN x, long *nlze);
+GEN  ZM_imagecomplspec(GEN x, long *nlze);
+GEN  dim1proj(GEN prh);
+GEN  detcyc(GEN cyc, long *L);
+
+GEN merge_factor_i(GEN f, GEN g);
+
+/* integer factorization / discrete log */
+GEN   coprime_part(GEN x, GEN f);
+ulong ucoprime_part(ulong x, ulong f);
+ulong is_kth_power(GEN x, ulong p, GEN *pt);
+GEN   mpqs(GEN N);
+ulong gcduodd(ulong x, ulong y);
+
+/* Polynomials */
+/* a) Arithmetic/conversions */
+GEN  addmulXn(GEN x, GEN y, long d);
+GEN  addshiftpol(GEN x, GEN y, long d);
+GEN  lift_if_rational(GEN x);
+GEN  monomial(GEN a, long degpol, long v);
+GEN  monomialcopy(GEN a, long degpol, long v);
+GEN  mulmat_pol(GEN A, GEN x);
+GEN  ser2pol_i(GEN x, long lx);
+GEN  ser2rfrac_i(GEN x);
+GEN  shiftpol_i(GEN x, long v);
+GEN  swap_vars(GEN b0, long v);
+GEN  RgX_recipspec_shallow(GEN x, long l, long n);
+
+/* b) Modular */
+GEN  bezout_lift_fact(GEN T, GEN Tmod, GEN p, long e);
+long F2x_split_Berlekamp(GEN *t);
+long Flx_split_Berlekamp(GEN *t, ulong p);
+long FpX_split_Berlekamp(GEN *t, GEN pp);
+long FqX_split_Berlekamp(GEN *t, GEN T, GEN p);
+GEN  FpX_quad_root(GEN x, GEN p, int unknown);
+GEN  FqX_split_all(GEN z, GEN T, GEN p);
+long FqX_split_by_degree(GEN *pz, GEN u, GEN T, GEN p);
+long FqX_split_deg1(GEN *pz, GEN u, GEN T, GEN p);
+GEN  FqX_split_roots(GEN z, GEN T, GEN p, GEN pol);
+GEN  polsym_gen(GEN P, GEN y0, long n, GEN T, GEN N);
+GEN  ZXQ_charpoly_sqf(GEN A, GEN B, long *lambda, long v);
+GEN  ZX_disc_all(GEN,ulong);
+GEN  ZX_resultant_all(GEN A, GEN B, GEN dB, ulong bound);
+GEN  ZX_ZXY_resultant_all(GEN A, GEN B, long *lambda, GEN *LPRS);
+GEN  RgXQ_minpoly_naive(GEN y, GEN P);
+GEN lift_intern(GEN x);
+
+/* c) factorization */
+double cauchy_bound(GEN p);
+GEN chk_factors_get(GEN lt, GEN famod, GEN c, GEN T, GEN N);
+long cmbf_maxK(long nb);
+GEN ZX_DDF(GEN x);
+GEN fact_from_DDF(GEN fa, GEN e, long n);
+GEN initgaloisborne(GEN T, GEN dn, long prec, GEN *pL, GEN *pprep, GEN *pdis);
+GEN logmax_modulus_bound(GEN p);
+GEN polint_i(GEN xa, GEN ya, GEN x, long n, GEN *ptdy);
+GEN quicktrace(GEN x, GEN sym);
+GEN special_pivot(GEN x);
+GEN vandermondeinversemod(GEN L, GEN T, GEN den, GEN mod);
+GEN ZX_monic_factorpadic(GEN f, GEN p, long prec);
+
+/* Finite fields */
+
+enum { t_FF_FpXQ = 0, t_FF_Flxq = 1, t_FF_F2xq = 2 };
+GEN FF_ellinit(GEN E, GEN fg);
+GEN FF_elldata(GEN E, GEN fg);
+
+/* Elliptic curves */
+/* common to Q and Rg */
+enum { R_PERIODS = 1, R_ETA, R_ROOTS, R_AB };
+
+enum { Qp_ROOT = 1, Qp_TATE };
+enum { Q_GROUPGEN = 5, Q_GLOBALRED, Q_ROOTNO, Q_MINIMALMODEL };
+
+/* common to Fp and Fq */
+enum { FF_CARD = 1, FF_GROUP, FF_GROUPGEN, FF_O };
+
+/* for Buchall_param */
+enum { fupb_NONE, fupb_RELAT, fupb_LARGE, fupb_PRECI };
+
+/* Allocation / gerepile */
+void   setdebugvar(long n);
+void   debug_stack(void);
+void   fill_stack(void);
+void   init_dalloc(void);
+double *dalloc(size_t n);
+void   minim_alloc(long n, double ***q, GEN *x, double **y,  double **z, double **v);
+int    pop_entree_block(entree *ep, long loc);
+int    pop_val_if_newer(entree *ep, long loc);
+
+/* general printing */
+void print_errcontext(PariOUT *out, const char *msg, const char *s, const char *entry);
+void print_prefixed_text(PariOUT *out, const char *s, const char *prefix, const char *str);
+INLINE void
+print_text(const char *s) { print_prefixed_text(pariOut, s,NULL,NULL); }
+INLINE void
+out_print_text(PariOUT *out, const char *s) { print_prefixed_text(out, s,NULL,NULL); }
+INLINE long
+is_keyword_char(char c) { return (isalnum((int)c) || c=='_'); }
+
+/* Interfaces (GP, etc.) */
+hashtable *hash_from_link(GEN e, GEN names, int use_stack);
+void gen_relink(GEN x, hashtable *table);
+entree* is_entry_intern(const char *s, entree **table, long *hash);
+entree* do_alias(entree *ep);
+char* get_sep(const char *t);
+long get_int(const char *s, long dflt);
+ulong get_uint(const char *s);
+int  gp_init_functions(void);
+GEN  pari_compile_str(char *lex, int strict);
+
+void pari_sigint(const char *s);
+pariFILE *pari_last_tmp_file(void);
+void* get_stack(double fraction, long min);
+void  init_graph(void);
+void  free_graph(void);
+void  initout(int initerr);
+void  resetout(int initerr);
+void  init_linewrap(long w);
+void  pari_kernel_init(void);
+void  pari_kernel_close(void);
+void  print_functions_hash(const char *s);
+void  print_all_user_fun(int member);
+GEN   readbin(const char *name, FILE *f, int *vector);
+int   term_height(void);
+int   term_width(void);
+void  whatnow_new_syntax(const char *f, long n);
+/* gp_colors */
+void decode_color(long n, long *c);
+extern GEN pari_colormap, pari_graphcolors;
+
+/* defaults */
+extern ulong precreal;
+
+/* history */
+typedef struct {
+  GEN z; /* result */
+  time_t t; /* time to obtain result */
+} gp_hist_cell;
+typedef struct {
+  gp_hist_cell *v; /* array of previous results, FIFO */
+  size_t size; /* # res */
+  ulong total; /* # of results computed since big bang */
+} gp_hist;
+
+/* prettyprinter */
+typedef struct {
+  pariFILE *file;
+  char *cmd;
+} gp_pp;
+
+/* path */
+typedef struct {
+  char *PATH;
+  char **dirs;
+} gp_path;
+
+/* for output */
+typedef struct {
+  char format; /* e,f,g */
+  long sigd;   /* -1 (all) or number of significant digits printed */
+  int sp;      /* 0 = suppress whitespace from output */
+  int prettyp; /* output style: raw, prettyprint, etc */
+  int TeXstyle;
+} pariout_t;
+
+void lim_lines_output(char *s, long n, long max);
+void gen_output(GEN x, pariout_t *T);
+void fputGEN_pariout(GEN x, pariout_t *T, FILE *out);
+
+void parsestate_reset(void);
+void parsestate_save(struct pari_parsestate *state);
+void parsestate_restore(struct pari_parsestate *state);
+
+void compilestate_reset(void);
+void compilestate_save(struct pari_compilestate *comp);
+void compilestate_restore(struct pari_compilestate *comp);
+
+void evalstate_clone(void);
+void evalstate_reset(void);
+void evalstate_restore(struct pari_evalstate *state);
+GEN  evalstate_restore_err(struct pari_evalstate *state);
+void evalstate_save(struct pari_evalstate *state);
+
+void mtstate_save(long *pending);
+void mtstate_reset(void);
+void mtstate_restore(long *pending);
+
+void debug_context(void);
+
+/* GP_DATA */
+typedef struct {
+  gp_hist *hist;
+  gp_pp *pp;
+  gp_path *path, *sopath;
+  pariout_t *fmt;
+  ulong lim_lines, flags, linewrap;
+  int echo, breakloop, recover, use_readline; /* GP-specific */
+  int secure, simplify, strictmatch, strictargs, chrono; /* libpari ? */
+  pari_timer *T;
+  ulong primelimit; /* deprecated */
+  ulong threadsize;
+} gp_data;
+extern gp_data *GP_DATA;
+  /* GP_DATA->flags */
+enum { gpd_QUIET=1, gpd_TEST=2, gpd_EMACS=256, gpd_TEXMACS=512};
+
+typedef struct {
+  const char *s;
+  size_t ls;
+  char **dir;
+} forpath_t;
+void forpath_init(forpath_t *T, gp_path *path, const char *s);
+char *forpath_next(forpath_t *T);
+
+/* GP output && output format */
+void gpwritebin(const char *s, GEN x);
+extern char *current_logfile;
+
+/* colors */
+extern long    gp_colors[];
+extern int     disable_color;
+
+/* backward compatibility */
+extern ulong compatible;
+enum { NONE, WARN, OLDFUN, OLDALL };
+#define new_fun_set (compatible == NONE || compatible == WARN)
+
+/* entrees */
+#define EpVALENCE(ep) ((ep)->valence & 0xFF)
+#define EpSTATIC(ep) ((ep)->valence & 0x100)
+#define EpSETSTATIC(ep) ((ep)->valence |= 0x100)
+enum { EpNEW = 100, EpALIAS, EpVAR, EpINSTALL };
+#define initial_value(ep) ((ep)+1)
+
+/* functions lists */
+extern const long functions_tblsz;  /* hashcodes table size */
+extern entree **functions_hash;   /* functions hashtable */
+extern entree **defaults_hash;    /* defaults hashtable */
+extern entree oldfonctions[];
+
+/* buffers */
+typedef struct Buffer {
+  char *buf;
+  ulong len;
+  jmp_buf env;
+} Buffer;
+Buffer *new_buffer(void);
+void delete_buffer(Buffer *b);
+void fix_buffer(Buffer *b, long newlbuf);
+
+typedef struct {
+  const char *s; /* source */
+  char *t, *end; /* target, last char read */
+  int in_string, in_comment, more_input, wait_for_brace, downcase;
+  Buffer *buf;
+} filtre_t;
+void init_filtre(filtre_t *F, Buffer *buf);
+char *filtre(const char *s, int flag);
+void check_filtre(filtre_t *F);
+
+gp_data *default_gp_data(void);
+GEN  gp_history(gp_hist *H, long p, char *old, char *entry);
+
+void delete_dirs(gp_path *p);
+void gp_expand_path(gp_path *p);
+const char *pari_default_path(void);
+int path_is_absolute(char *s);
+
+typedef struct input_method {
+/* mandatory */
+  char * (*fgets)(char *,int,FILE*);
+  char * (*getline)(char**, int f, struct input_method*, filtre_t *F);
+  int free; /* boolean: must we free the output of getline() ? */
+/* for interactive methods */
+  const char *prompt, *prompt_cont;
+/* for non-interactive methods */
+  FILE *file;
+} input_method;
+
+int input_loop(filtre_t *F, input_method *IM);
+char *file_input(char **s0, int junk, input_method *IM, filtre_t *F);
+char *file_getline(Buffer *b, char **s0, input_method *IM);
+
+/* By files */
+
+/* FpE.c */
+long Fl_elltrace_CM(int CM, ulong a4, ulong a6, ulong p);
+
+/* Qfb.c */
+
+GEN     redimagsl2(GEN q, GEN *U);
+GEN     redrealsl2(GEN V, GEN d, GEN rd);
+GEN     redrealsl2step(GEN A, GEN d, GEN rd);
+GEN     redtausl2(GEN t, GEN *U);
+
+/* alglin1.c */
+typedef long (*pivot_fun)(GEN,GEN,long,GEN);
+GEN ZM_pivots(GEN x0, long *rr);
+GEN RgM_pivots(GEN x0, GEN data, long *rr, pivot_fun pivot);
+
+/* arith1.c */
+
+int     is_gener_Fp(GEN x, GEN p, GEN p_1, GEN L);
+int     is_gener_Fl(ulong x, ulong p, ulong p_1, GEN L);
+
+/* arith2.c */
+
+int     divisors_init(GEN n, GEN *pP, GEN *pE);
+long    set_optimize(long what, GEN g);
+
+/* base2.c */
+
+GEN     gen_if_principal(GEN bnf, GEN x);
+int     nfissquarefree(GEN nf, GEN x);
+GEN     polsymmodp(GEN g, GEN p);
+GEN     nfbasis_gp(GEN T, GEN P, GEN junk);
+GEN     nfdisc_gp(GEN T, GEN P, GEN junk);
+
+/* base3.c */
+
+void    check_nfelt(GEN x, GEN *den);
+long    nfvalrem(GEN nf, GEN x, GEN pr, GEN *py);
+GEN     zk_ei_mul(GEN nf, GEN x, long i);
+
+/* base4.c */
+
+void    check_listpr(GEN x);
+GEN     extideal_HNF_mul(GEN nf, GEN x, GEN y);
+GEN     factor_norm(GEN x);
+GEN     factorbackprime(GEN nf, GEN L, GEN e);
+long    val_norm(GEN x, GEN p, long *vz);
+
+/* base5.c */
+
+GEN     check_and_build_nfabs(GEN rnf);
+GEN     check_and_build_norms(GEN rnf);
+
+/* buch1.c */
+
+GEN     form_to_ideal(GEN x);
+GEN     qfbforms(GEN D);
+
+/* buch2.c */
+
+typedef struct GRHprime_t { ulong p; double logp; GEN dec; } GRHprime_t;
+typedef struct GRHcheck_t { double cD, cN; GRHprime_t *primes; long clone, nprimes, maxprimes; ulong limp; forprime_t P; } GRHcheck_t;
+void    free_GRHcheck(GRHcheck_t *S);
+void    init_GRHcheck(GRHcheck_t *S, long N, long R1, double LOGD);
+void    GRH_ensure(GRHcheck_t *S, long nb);
+ulong   GRH_last_prime(GRHcheck_t *S);
+int     GRHok(GRHcheck_t *S, double L, double SA, double SB);
+GEN     check_and_build_matal(GEN bnf);
+GEN     extract_full_lattice(GEN x);
+GEN     init_red_mod_units(GEN bnf, long prec);
+GEN     isprincipalarch(GEN bnf, GEN col, GEN kNx, GEN e, GEN dx, long *pe);
+GEN     red_mod_units(GEN col, GEN z);
+
+/* buch3.c */
+
+GEN     minkowski_bound(GEN D, long N, long r2, long prec);
+int     subgroup_conductor_ok(GEN H, GEN L);
+GEN     subgrouplist_cond_sub(GEN bnr, GEN C, GEN bound);
+
+/* ellsea.c */
+
+void    pari_close_seadata(void);
+void    pari_init_seadata(void);
+
+/* es.c */
+
+const char * eng_ord(long i);
+char *  env_ok(const char *s);
+void    filestate_restore(pariFILE *F);
+void    killallfiles(void);
+pariFILE* newfile(FILE *f, const char *name, int type);
+void    pari_init_homedir(void);
+void    pari_close_homedir(void);
+void    pari_init_files(void);
+void    pari_close_files(void);
+int     popinfile(void);
+pariFILE* try_pipe(const char *cmd, int flag);
+
+/* Flxq_log.c */
+
+GEN Flxq_log_index(GEN a0, GEN b0, GEN m, GEN T0, ulong p);
+
+/* FlxqE.c */
+
+GEN     ZpXQ_norm_pcyc(GEN x, GEN T, GEN q, GEN p);
+long    zx_is_pcyc(GEN T);
+
+/* galconj.c */
+
+GEN     galoiscosets(GEN O, GEN perm);
+long    intheadlong(GEN x, GEN mod);
+GEN     listznstarelts(long m, long p);
+GEN     matheadlong(GEN W, GEN mod);
+GEN     matrixnorm(GEN M, long prec);
+GEN     monomorphismlift(GEN P, GEN S, GEN Q, GEN p, long e);
+long    polheadlong(GEN P, long n, GEN mod);
+GEN     vandermondeinverseprep(GEN L);
+
+/* galois.c */
+
+GEN     polgaloisnamesbig(long n, long k);
+
+/* gen1.c */
+
+int     ff_poltype(GEN *x, GEN *p, GEN *pol);
+GEN     gred_frac2(GEN x1, GEN x2);
+GEN     gred_rfrac2(GEN x1, GEN x2);
+GEN     gred_rfrac_simple(GEN n, GEN d);
+GEN     sqr_ser_part(GEN x, long l1, long l2);
+
+/* gen3.c */
+
+GEN     gsubst_expr(GEN pol, GEN from, GEN to);
+GEN     poltoser(GEN x, long v, long prec);
+GEN     rfractoser(GEN x, long v, long prec);
+
+/* ifactor1.c */
+
+GEN     ellfacteur(GEN n, int insist);
+GEN     pollardbrent(GEN n);
+ulong   snextpr(ulong p, byteptr *d, long *rcn, long *q, long k);
+GEN     squfof(GEN n);
+
+/* prime.c */
+
+long    BPSW_psp_nosmalldiv(GEN N);
+int     Fl_MR_Jaeschke(ulong n, long k);
+int     MR_Jaeschke(GEN n, long k);
+long    isanypower_nosmalldiv(GEN N, GEN *px);
+void    prime_table_next_p(ulong a, byteptr *pd, ulong *pp, ulong *pn);
+int     uisprime_101(ulong n);
+int     uisprime_661(ulong n);
+
+/* init.c */
+
+void    pari_init_defaults(void);
+void    pari_init_stack(size_t size, size_t old);
+
+/* nffactor.c */
+
+int     nfissplit(GEN nf, GEN x);
+
+/* perm.c */
+
+long    cosets_perm_search(GEN C, GEN p);
+GEN     group_export_GAP(GEN G);
+GEN     group_export_MAGMA(GEN G);
+GEN     perm_generate(GEN S, GEN H, long o);
+long    perm_relorder(GEN p, GEN S);
+GEN     perm_to_GAP(GEN p);
+
+/* polarit1.c */
+
+GEN     F2x_Berlekamp_ker(GEN u);
+GEN     Flx_Berlekamp_ker(GEN u, ulong p);
+GEN     FpX_Berlekamp_ker(GEN u, GEN p);
+GEN     FlxqX_Berlekamp_ker(GEN u, GEN T, ulong p);
+GEN     FpXQX_Berlekamp_ker(GEN u, GEN T, GEN p);
+GEN     F2x_factcantor(GEN f, long flag);
+GEN     Flx_factcantor(GEN f, ulong p, long flag);
+GEN     FpX_factcantor(GEN f, GEN pp, long flag);
+GEN     FqX_rand(long d1, long v, GEN T, GEN p);
+int     cmp_padic(GEN x, GEN y);
+GEN     factcantor0(GEN f, GEN pp, long flag);
+
+/* polarit2.c */
+
+GEN     sylvestermatrix_i(GEN x, GEN y);
+
+/* QX_factor */
+
+void    factor_quad(GEN x, GEN res, long *ptcnt);
+
+/* FpX.c */
+
+GEN     FpX_gcd_check(GEN x, GEN y, GEN p);
+
+/* polarit3.c */
+
+GEN     Flm_Frobenius_pow(GEN M, long d, GEN T, ulong p);
+GEN     FpM_Frobenius_pow(GEN M, long d, GEN T, GEN p);
+GEN     FpX_compositum(GEN A, GEN B, GEN p);
+GEN     FpX_direct_compositum(GEN A, GEN B, GEN p);
+ulong   ZX_ZXY_ResBound(GEN A, GEN B, GEN dB);
+GEN     ffinit_Artin_Shreier(GEN ip, long l);
+GEN     ffinit_rand(GEN p, long n);
+void    init_modular(forprime_t *S);
+GEN     polint_triv(GEN xa, GEN ya);
+
+/* random.c */
+
+void    pari_init_rand(void);
+
+/* rootpol.c */
+
+GEN     FFT(GEN x, GEN Omega);
+GEN     FFTinit(long k, long prec);
+
+/* subcyclo.c */
+
+GEN     bnr_to_znstar(GEN bnr, long *complex);
+GEN     galoiscyclo(long n, long v);
+GEN     znstar_bits(long n, GEN H);
+long    znstar_conductor(long n, GEN H);
+GEN     znstar_cosets(long n, long phi_n, GEN H);
+GEN     znstar_elts(long n, GEN H);
+GEN     znstar_generate(long n, GEN V);
+GEN     znstar_hnf(GEN Z, GEN M);
+GEN     znstar_hnf_elts(GEN Z, GEN H);
+GEN     znstar_hnf_generators(GEN Z, GEN M);
+GEN     znstar_reduce_modulus(GEN H, long n);
+GEN     znstar_small(GEN zn);
+
+/* trans1.c */
+
+GEN     logagmcx(GEN q, long prec);
+void    pari_init_floats(void);
+void    pari_close_floats(void);
+GEN     rootsof1complex(GEN n, long prec);
+GEN     rootsof1padic(GEN n, GEN y);
+GEN     zellagmcx(GEN a0, GEN b0, GEN r, GEN t, long prec);
+
+/* trans2.c */
+
+GEN     cxpsi(GEN s0, long prec);
+double  darg(double s, double t);
+
+/* trans3.c */
+
+GEN     bernreal_using_zeta(long n, GEN iz, long prec);
+GEN     czeta(GEN s0, long prec);
+GEN     double_eta_quotient(GEN a, GEN w, GEN D, long p, long q, GEN pq, GEN sqrtD);
+GEN     inv_szeta_euler(long n, double lba, long prec);
+GEN     polylogd0(long m, GEN x, long flag, long prec);
+GEN     trueE2(GEN tau, long prec);
+GEN     twistpartialzeta(GEN q, long f, long c, GEN va, GEN cff);
+
+ENDEXTERN
diff --git a/src/headers/paristio.h b/src/headers/paristio.h
new file mode 100644
index 0000000..824d3d3
--- /dev/null
+++ b/src/headers/paristio.h
@@ -0,0 +1,246 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file contains memory and I/O management definitions       */
+
+typedef struct {
+  long s, us;
+} pari_timer;
+
+typedef unsigned char *byteptr;
+typedef ulong pari_sp;
+
+/* iterator over primes */
+typedef struct {
+  int strategy; /* 1 to 4 */
+  GEN bb; /* iterate through primes <= bb */
+  ulong c, q; /* congruent to c (mod q) */
+
+  /* strategy 1: private prime table */
+  byteptr d; /* diffptr + n */
+  ulong p; /* current p = n-th prime */
+  ulong b; /* min(bb, ULONG_MAX) */
+
+  /* strategy 2: sieve, use p */
+  unsigned char *sieve;
+  ulong cache[9]; /* look-ahead primes already computed */
+  ulong chunk; /* # of odd integers in sieve */
+  ulong a, end, sieveb; /* [a,end] interval currently being sieved,
+                         * end <= sieveb = min(bb, maxprime^2, ULONG_MAX) */
+  ulong pos, maxpos; /* current cell and max cell */
+
+  /* strategy 3: unextprime, use p */
+
+  /* strategy 4: nextprime */
+  GEN pp;
+} forprime_t;
+
+typedef struct {
+  int first;
+  GEN b, n, p;
+  forprime_t T;
+} forcomposite_t;
+
+typedef struct forvec_t {
+  long first;
+  GEN *a, *m, *M; /* current n-uplet, minima, Maxima */
+  long n; /* length */
+  GEN (*next)(struct forvec_t *);
+} forvec_t;
+
+/* Iterate over partitions */
+typedef struct
+{
+  long k;
+  long amax, amin, nmin, nmax, strip;
+  GEN v;
+} forpart_t;
+
+/* binary I/O */
+typedef struct GENbin {
+  size_t len; /* gsizeword(x) */
+  GEN x; /* binary copy of x */
+  GEN base; /* base address of p->x */
+  int canon; /* 1: t_INT in canonical (native kernel) form,
+                0: t_INT according to current kernel */
+} GENbin;
+
+struct pari_mainstack
+{
+  pari_sp top, bot, avma;
+  size_t memused;
+};
+
+struct pari_thread
+{
+  struct pari_mainstack st;
+  GEN data;
+};
+
+typedef struct pariFILE {
+  FILE *file;
+  int type;
+  const char *name;
+  struct pariFILE* prev;
+  struct pariFILE* next;
+} pariFILE;
+/* pariFILE.type */
+enum { mf_IN  = 1, mf_PIPE = 2, mf_FALSE = 4, mf_OUT = 8, mf_PERM = 16 };
+
+typedef struct entree {
+  const char *name;
+  ulong valence;
+  void *value;
+  long menu;
+  const char *code;
+  const char *help;
+  void *pvalue;
+  long arity;
+  struct entree *next;
+} entree;
+
+struct pari_parsestate
+{
+  long node;
+  int once;
+  long discarded;
+  const char *lex_start, *unused_chars;
+  GEN lasterror;
+};
+
+struct pari_compilestate
+{
+  long opcode, operand, data, localvars, frames, dbginfo;
+  long offset;
+  const char *dbgstart;
+};
+
+struct pari_evalstate
+{
+  pari_sp avma;
+  long sp;
+  long rp;
+  long var;
+  long lvars;
+  long trace;
+  long pending_threads;
+  struct pari_compilestate comp;
+};
+
+struct gp_context
+{
+  long listloc;
+  long prettyp;
+  struct pari_evalstate eval;
+  struct pari_parsestate parse;
+  pariFILE *file;
+  jmp_buf *iferr_env;
+  GEN err_data;
+};
+
+struct mt_state
+{
+  GEN worker;
+  GEN pending;
+  long workid;
+};
+
+struct pari_mt
+{
+  struct mt_state mt;
+  GEN (*get)(struct mt_state *mt, long *workid, long *pending);
+  void (*submit)(struct mt_state *mt, long workid, GEN work);
+  void (*end)(void);
+};
+
+typedef struct PariOUT {
+  void (*putch)(char);
+  void (*puts)(const char*);
+  void (*flush)(void);
+} PariOUT;
+
+/* hashtables */
+typedef struct hashentry {
+  void *key, *val;
+  ulong hash; /* hash(key) */
+  struct hashentry *next;
+} hashentry;
+
+typedef struct hashtable {
+  ulong len; /* table length */
+  hashentry **table; /* the table */
+  ulong nb, maxnb; /* number of entries stored and max nb before enlarging */
+  ulong pindex; /* prime index */
+  ulong (*hash) (void *k); /* hash function */
+  int (*eq) (void *k1, void *k2); /* equality test */
+  int use_stack; /* use stack_malloc instead of malloc ? */
+} hashtable;
+
+typedef struct {
+  void **data;
+  long n;
+  long alloc;
+  size_t size;
+} pari_stack;
+
+/* Common global variables: */
+
+extern PariOUT *pariOut, *pariErr;
+extern FILE    *pari_outfile, *pari_logfile, *pari_infile, *pari_errfile;
+extern ulong    logstyle;
+
+enum logstyles {
+    logstyle_none,        /* 0 */
+    logstyle_plain,        /* 1 */
+    logstyle_color,        /* 2 */
+    logstyle_TeX         /* 3 */
+};
+
+enum { c_ERR, c_HIST, c_PROMPT, c_INPUT, c_OUTPUT, c_HELP, c_TIME, c_LAST,
+       c_NONE = 0xffffUL };
+
+enum { TEXSTYLE_PAREN=2, TEXSTYLE_BREAK=4 };
+
+extern THREAD pari_sp avma, bot, top;
+#define DISABLE_MEMUSED (size_t)-1
+extern THREAD size_t memused;
+extern byteptr diffptr;
+extern char *current_psfile, *pari_datadir;
+
+#define gcopyifstack(x,y)  STMT_START {pari_sp _t=(pari_sp)(x); \
+  (y)=(_t>=bot &&_t<top)? gcopy((GEN)_t): (GEN)_t;} STMT_END
+#define copyifstack(x,y)  STMT_START {pari_sp _t=(pari_sp)(x); \
+  (y)=(_t>=bot &&_t<top)? gcopy((GEN)_t): (GEN)_t;} STMT_END
+#define icopyifstack(x,y) STMT_START {pari_sp _t=(pari_sp)(x); \
+  (y)=(_t>=bot &&_t<top)? icopy((GEN)_t): (GEN)_t;} STMT_END
+
+/* Define this to (1) locally (in a given file, NOT here) to check
+ * "random" garbage collecting */
+#ifdef DEBUG_LOWSTACK
+#  define low_stack(x,l) 1
+#else
+#ifdef DYNAMIC_STACK
+#  define low_stack(x,l) (avma < (pari_sp)(l))
+#else
+#  define low_stack(x,l) (avma < (pari_sp)(x))
+#endif
+#endif
+
+#define stack_lim(av,n) (bot + (((av)-bot)>>(n)))
+
+#ifndef SIG_IGN
+#  define SIG_IGN (void(*)())1
+#endif
+#ifndef SIGINT
+#  define SIGINT 2
+#endif
diff --git a/src/headers/parisys.h b/src/headers/parisys.h
new file mode 100644
index 0000000..5b68599
--- /dev/null
+++ b/src/headers/parisys.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This files contains macros depending on system and compiler    */
+
+#ifndef LITTLE_ENDIAN_64
+#  define   LITTLE_ENDIAN_64 12345678
+#endif
+#ifndef BIG_ENDIAN_64
+#  define   BIG_ENDIAN_64    87654321
+#endif
+#ifndef LITTLE_ENDIAN
+#  define   LITTLE_ENDIAN 1234
+#endif
+#ifndef BIG_ENDIAN
+#  define   BIG_ENDIAN    4321
+#endif
+#ifndef PDP_ENDIAN
+#  define   PDP_ENDIAN    3412
+#endif
+
+#ifdef __cplusplus
+#  define ANYARG ...
+#  define BEGINEXTERN extern "C" {
+#  define ENDEXTERN }
+#else
+#  define ANYARG
+#  define BEGINEXTERN
+#  define ENDEXTERN
+#endif
+
+#ifdef DISABLE_INLINE
+#  undef ASMINLINE
+#else
+#  ifdef __cplusplus
+#    define INLINE inline static
+#  elif defined(__GNUC__)
+#    define INLINE __inline__ static
+#  endif
+#endif
+
+#ifndef DISABLE_VOLATILE
+#  ifdef __GNUC__
+#    define VOLATILE volatile
+#  endif
+#endif
+
+#ifndef VOLATILE
+#  define VOLATILE
+#endif
+#ifndef INLINE
+#  define INLINE static
+#endif
+#ifdef ENABLE_TLS
+#  define THREAD __thread
+#else
+#  define THREAD
+#endif
+
+#if defined(_WIN32) || defined(__CYGWIN32__)
+/* ANSI C does not allow to longjmp() out of a signal handler, in particular,
+ * the SIGINT handler. On Win32, the handler is executed in another thread, and
+ * longjmp'ing into another thread's stack will utterly confuse the system.
+ * Instead, we check whether win32ctrlc is set in new_chunk(). */
+BEGINEXTERN
+  extern int win32ctrlc;
+  void dowin32ctrlc(void);
+ENDEXTERN
+#define CHECK_CTRLC if (win32ctrlc) dowin32ctrlc();
+#else
+#define CHECK_CTRLC
+#endif
diff --git a/src/headers/paritune.h b/src/headers/paritune.h
new file mode 100644
index 0000000..025cc3c
--- /dev/null
+++ b/src/headers/paritune.h
@@ -0,0 +1,81 @@
+#define PARI_TUNE
+
+#ifdef PARI_TUNE
+extern long SQRI_KARATSUBA_LIMIT;
+extern long MULII_KARATSUBA_LIMIT;
+extern long MULRR_MULII_LIMIT;
+extern long SQRI_FFT_LIMIT;
+extern long MULII_FFT_LIMIT;
+extern long Fp_POW_REDC_LIMIT;
+extern long Fp_POW_BARRETT_LIMIT;
+extern long INVMOD_GMP_LIMIT;
+extern long DIVRR_GMP_LIMIT;
+extern long Flx_MUL_KARATSUBA_LIMIT;
+extern long Flx_SQR_KARATSUBA_LIMIT;
+extern long Flx_MUL_HALFMULII_LIMIT;
+extern long Flx_SQR_HALFSQRI_LIMIT;
+extern long Flx_MUL_MULII_LIMIT;
+extern long Flx_SQR_SQRI_LIMIT;
+extern long Flx_MUL_MULII2_LIMIT;
+extern long Flx_SQR_SQRI2_LIMIT;
+extern long Flx_INVBARRETT_LIMIT;
+extern long Flx_DIVREM_BARRETT_LIMIT;
+extern long Flx_REM_BARRETT_LIMIT;
+extern long Flx_BARRETT_LIMIT;
+extern long Flx_HALFGCD_LIMIT;
+extern long Flx_GCD_LIMIT;
+extern long Flx_EXTGCD_LIMIT;
+extern long FpX_INVBARRETT_LIMIT;
+extern long FpX_DIVREM_BARRETT_LIMIT;
+extern long FpX_REM_BARRETT_LIMIT;
+extern long FpX_BARRETT_LIMIT;
+extern long FpX_HALFGCD_LIMIT;
+extern long FpX_GCD_LIMIT;
+extern long FpX_EXTGCD_LIMIT;
+extern long EXPNEWTON_LIMIT;
+extern long INVNEWTON_LIMIT;
+extern long LOGAGM_LIMIT;
+extern long LOGAGMCX_LIMIT;
+extern long AGM_ATAN_LIMIT;
+extern long RgX_SQR_LIMIT;
+extern long RgX_MUL_LIMIT;
+#else
+#  define SQRI_KARATSUBA_LIMIT     __SQRI_KARATSUBA_LIMIT
+#  define MULII_KARATSUBA_LIMIT    __MULII_KARATSUBA_LIMIT
+#  define MULRR_MULII_LIMIT        __MULRR_MULII_LIMIT
+#  define SQRI_FFT_LIMIT           __SQRI_FFT_LIMIT
+#  define MULII_FFT_LIMIT          __MULII_FFT_LIMIT
+#  define Fp_POW_REDC_LIMIT        __Fp_POW_REDC_LIMIT
+#  define Fp_POW_BARRETT_LIMIT     __Fp_POW_BARRETT_LIMIT
+#  define INVMOD_GMP_LIMIT         __INVMOD_GMP_LIMIT
+#  define DIVRR_GMP_LIMIT          __DIVRR_GMP_LIMIT
+#  define EXPNEWTON_LIMIT          __EXPNEWTON_LIMIT
+#  define INVNEWTON_LIMIT          __INVNEWTON_LIMIT
+#  define LOGAGM_LIMIT             __LOGAGM_LIMIT
+#  define LOGAGMCX_LIMIT           __LOGAGMCX_LIMIT
+#  define AGM_ATAN_LIMIT           __AGM_ATAN_LIMIT
+#  define Flx_MUL_KARATSUBA_LIMIT  __Flx_MUL_KARATSUBA_LIMIT
+#  define Flx_SQR_KARATSUBA_LIMIT  __Flx_SQR_KARATSUBA_LIMIT
+#  define Flx_MUL_HALFMULII_LIMIT  __Flx_MUL_HALFMULII_LIMIT
+#  define Flx_SQR_HALFSQRI_LIMIT   __Flx_SQR_HALFSQRI_LIMIT
+#  define Flx_MUL_MULII_LIMIT      __Flx_MUL_MULII_LIMIT
+#  define Flx_SQR_SQRI_LIMIT       __Flx_SQR_SQRI_LIMIT
+#  define Flx_MUL_MULII2_LIMIT     __Flx_MUL_MULII2_LIMIT
+#  define Flx_SQR_SQRI2_LIMIT      __Flx_SQR_SQRI2_LIMIT
+#  define Flx_INVBARRETT_LIMIT     __Flx_INVBARRETT_LIMIT
+#  define Flx_DIVREM_BARRETT_LIMIT __Flx_DIVREM_BARRETT_LIMIT
+#  define Flx_REM_BARRETT_LIMIT    __Flx_REM_BARRETT_LIMIT
+#  define Flx_BARRETT_LIMIT        __Flx_BARRETT_LIMIT
+#  define Flx_HALFGCD_LIMIT        __Flx_HALFGCD_LIMIT
+#  define Flx_GCD_LIMIT            __Flx_GCD_LIMIT
+#  define Flx_EXTGCD_LIMIT         __Flx_EXTGCD_LIMIT
+#  define FpX_INVBARRETT_LIMIT     __FpX_INVBARRETT_LIMIT
+#  define FpX_DIVREM_BARRETT_LIMIT __FpX_DIVREM_BARRETT_LIMIT
+#  define FpX_REM_BARRETT_LIMIT    __FpX_REM_BARRETT_LIMIT
+#  define FpX_BARRETT_LIMIT        __FpX_BARRETT_LIMIT
+#  define FpX_HALFGCD_LIMIT        __FpX_HALFGCD_LIMIT
+#  define FpX_GCD_LIMIT            __FpX_GCD_LIMIT
+#  define FpX_EXTGCD_LIMIT         __FpX_EXTGCD_LIMIT
+#  define RgX_SQR_LIMIT            __RgX_SQR_LIMIT
+#  define RgX_MUL_LIMIT            __RgX_MUL_LIMIT
+#endif
diff --git a/src/kernel/README b/src/kernel/README
new file mode 100644
index 0000000..c9481a1
--- /dev/null
+++ b/src/kernel/README
@@ -0,0 +1,33 @@
+There is a different directory for each value of "$asmarch". If it contain
+files MakeLVL[01].SH, these are included in the general Makefile. It
+contains rules for creating headers and object files in the build directory.
+
+The list of object files is given by "$kernel" (default: "mp mpinl")
+
+* Level 0 symbols, in object file mpinl.o, are :
+  overflow hiremainder
+  addll addllx subll subllx mulll addmul
+  divll
+  bfffo
+If no assembler implementation is available the definitions are in
+none/ (addll.h, mulll.h, bfffo.h, divll.h). Otherwise, in $asmarch/asm0.h
+
+If MakeLVL0.SH is not present, the file asm0.h is treated by
+config/genkernel, creating parilvl0.h. The script expands the
+following tokens [ to be placed in a C comment ] as follows:
+
+  ^ASM  mod1 mod2    // asm0.h implements modules mod1, mod2
+  ^NOASM mod1 mod2   // asm0.h does not implement modules mod1, mod2
+
+where mod1, mod2 etc. are modules among addll, mulll, bfffo, divll.
+In effect, this includes portable code from kernel/none/<module>.h
+to supplement whatever is provided by asm0.h.
+CAVEAT: if bfffo and divll are both mentioned, they must appear in this
+order (divll depends on bfffo).
+
+* Inline Level 1 symbols, in mpinl.o: the definitions are in
+none/level1.h. Inlining may or may not be possible, but a symbol
+must be defined in mpinl.o in any case.
+
+* Non-Inline Level 1 symbols, defined in mp.o: the definitions are in
+various *.c files in none/ and gmp/ concatenated into $objdir/mp.c.
diff --git a/src/kernel/alpha/asm0.h b/src/kernel/alpha/asm0.h
new file mode 100644
index 0000000..ed33f26
--- /dev/null
+++ b/src/kernel/alpha/asm0.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*
+ASM addll mulll
+NOASM bfffo divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+  __asm__ ("addq %2,%3,%0\n\tcmpult %4,%2,%1" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong) 0)); \
+  __value; \
+})
+
+#define addllx(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+ __asm__ ("addq %3,%4,%0\n\tcmpult %5,%3,%2\n\taddq %5,%6,%0\n\tcmpult %5,%6,%1\n\taddq %6,%7,%1\n\t" \
+   : "=&r" (__value), "=r" (overflow), "=r" (__temp) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong) 0), "1" (overflow), "2" ((ulong) 0)); \
+__value; \
+})
+
+#define subll(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+  __asm__ ("subq %2,%3,%0\n\tcmpult %2,%4,%1" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong)0)); \
+  __value; \
+})
+
+#define subllx(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp1, __temp2; \
+__asm__ ("subq %4,%5,%2\n\tcmpult %4,%8,%3\n\tsubq %8,%7,%0\n\tcmpult %8,%6,%1\n\taddq %7,%9,%1\n\t" \
+   : "=r" (__value), "=r" (overflow), "=&r" (__temp1), "=r" (__temp2)  \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong)0), "1" (overflow), "2" ((ulong)0), "3" ((ulong)0)); \
+ __value; \
+})
+
+#define mulll(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("umulh %2,%3,%1\n\tmulq %2,%3,%0\n\t" \
+   : "=r" (__value), "=&r" (hiremainder) \
+   : "r" (__arg1), "r" (__arg2)); \
+ __value; \
+})
+
+#define addmul(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+ __asm__ ("mulq %3,%4,%0\n\tumulh %3,%4,%2\n\taddq %5,%6,%0\n\tcmpult %5,%6,%1\n\taddq %7,%6,%1\n\t" \
+   : "=&r" (__value), "=r" (hiremainder), "=r" (__temp) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong) 0), "1" (hiremainder), "2" ((ulong) 0)); \
+ __value; \
+})
+#endif
diff --git a/src/kernel/alpha/asm1.h b/src/kernel/alpha/asm1.h
new file mode 100644
index 0000000..7f6eda0
--- /dev/null
+++ b/src/kernel/alpha/asm1.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file is a slight adaptation of source code extracted from gmp-3.1.1
+  (from T. Granlund), files longlong.h and gmp-e_IMPL.h
+
+  Copyright (C) 2000 Free Software Foundation, Inc.
+
+ * FIXME: This file is unused until somebody implements
+ * invert_word(x) = return floor( 2^(2*BIL)/x ) */
+
+extern ulong invert_word(ulong);
+
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {                                     \
+    ulong __x;                             \
+    __x = (al) - (bl);                     \
+    (sh) = (ah) - (bh) - (__x > (al));     \
+    (sl) = __x;                            \
+  } while (0)
+
+#ifdef __GNUC__
+
+#define divll(x, y)                                      \
+__extension__ ({                                         \
+  register ulong _di, _x = (x), _y = (y), _q, _ql, _r;   \
+  register ulong _xh, _xl, _k, __hire;                   \
+                                                         \
+  if (_y & 0x8000000000000000UL)                         \
+      { _k = 0; __hire = hiremainder; }                  \
+  else                                                   \
+  {                                                      \
+    _k = bfffo(_y);                                      \
+    __hire = (hiremainder << _k) | (_x >> (64 - _k));    \
+    _x <<= _k; _y <<=  _k;                               \
+  }                                                      \
+  _di = invert_word(_y);                                 \
+  _ql = mulll (__hire, _di);                             \
+  _q = __hire + hiremainder;                             \
+  _xl = mulll(_q, _y); _xh = hiremainder;                \
+  sub_ddmmss (_xh, _r, __hire, _x, _xh, _xl);            \
+  if (_xh != 0)                                          \
+  {                                                      \
+    sub_ddmmss (_xh, _r, _xh, _r, 0, _y); _q += 1;       \
+    if (_xh != 0)                                        \
+      { sub_ddmmss (_xh, _r, _xh, _r, 0, _y); _q += 1; } \
+  }                                                      \
+  if (_r >= _y)                                          \
+    { _r -= _y; _q += 1; }                               \
+  hiremainder = _r >> _k;                                \
+  _q;                                                    \
+})
+
+#else /* __GNUC__ */
+
+static ulong
+divll(ulong x, ulong y)
+{
+  register ulong _di, _x = (x), _y = (y), _q, _ql, _r;
+  register ulong _xh, _xl, _k, __hire;
+
+  if (_y & 0x8000000000000000UL)
+      { _k = 0; __hire = hiremainder; }
+  else
+  {
+    _k = bfffo(_y);
+    __hire = (hiremainder << _k) | (_x >> (64 - _k));
+    _x <<= _k; _y <<=  _k;
+  }
+  _di = invert_word(_y);
+  _ql = mulll (__hire, _di);
+  _q = __hire + hiremainder;
+  _xl = mulll(_q, _y); _xh = hiremainder;
+  sub_ddmmss (_xh, _r, __hire, _x, _xh, _xl);
+  if (_xh != 0)
+  {
+    sub_ddmmss (_xh, _r, _xh, _r, 0, _y); _q += 1;
+    if (_xh != 0)
+      { sub_ddmmss (_xh, _r, _xh, _r, 0, _y); _q += 1; }
+  }
+  if (_r >= _y)
+    { _r -= _y; _q += 1; }
+  hiremainder = _r >> _k;
+  return _q;
+}
+
+#endif /* __GNUC__ */
diff --git a/src/kernel/arm/asm0.h b/src/kernel/arm/asm0.h
new file mode 100644
index 0000000..93e3117
--- /dev/null
+++ b/src/kernel/arm/asm0.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/*
+ASM addll mulll
+NOASM bfffo divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("adds %0,%2,%3\n\tadc %1,%4,%4\n\t" \
+   : "=&r" (__value), "=&r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "r" ((ulong)0): "cc"); \
+ __value; \
+})
+
+#define addllx(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("subs %1,%4,#1\n\tadcs %0,%2,%3\n\tadc %1,%5,%5\n\t" \
+   : "=&r" (__value), "=&r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "r" (overflow), "r" ((ulong)0) \
+   : "cc"); \
+ __value; \
+})
+
+#define subll(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("subs %0,%2,%3\n\tadc %1,%4,%4\n\trsb %1,%1,#1\n\t" \
+   : "=&r" (__value), "=&r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "r" ((ulong)0) \
+   : "cc"); \
+ __value; \
+})
+
+#define subllx(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("rsbs %1,%4,%5\n\tsbcs %0,%2,%3\n\tadc %1,%5,%5\n\trsb %1,%1,#1\n\t" \
+   : "=&r" (__value), "=&r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "r" (overflow), "r" ((ulong)0) \
+   : "cc"); \
+ __value; \
+})
+
+#define mulll(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("umull %0,%1,%2,%3\n\t" \
+   : "=&r" (__value), "=&r" (hiremainder) \
+   : "r" (__arg1), "r" (__arg2)); \
+ __value; \
+})
+
+#define addmul(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("umlal %0,%1,%2,%3\n\t" \
+   : "=&r" (__value), "=&r" (hiremainder) \
+   : "r" (__arg1), "r" (__arg2), "1" ((ulong) 0), "0" (hiremainder)); \
+ __value; \
+})
+
+#if 0 /* Not supported by all CPU */
+#define bfffo(a) \
+__extension__ ({ \
+  ulong __arg1 = (a), __value; \
+  __asm__ ("clz %0,%1\n\t" \
+           : "=&r" (__value) \
+           : "r" (__arg1)); \
+  __value; \
+})
+#endif
+
+#endif
diff --git a/src/kernel/gmp/MakeLVL1.SH b/src/kernel/gmp/MakeLVL1.SH
new file mode 100644
index 0000000..41fb791
--- /dev/null
+++ b/src/kernel/gmp/MakeLVL1.SH
@@ -0,0 +1,12 @@
+cat >> $file << EOT
+L1OBJS=$kern1/int.h $knone/level1.h
+parilvl1.h: \$(L1OBJS) $src/headers/paritune.h
+	if test -r ./tune.h; then d=.; else d=$kern1; fi;\
+          cat \$\$d/tune.h \$(L1OBJS) > parilvl1.h
+MP_C=$kern1/mp.c $knone/cmp.c $knone/gcdll.c $knone/ratlift.c\
+  $knone/invmod.c $kern1/gcd.c $kern1/gcdext.c $knone/mp_indep.c $knone/add.c
+mpker.c: \$(MP_C)
+	cat \$(MP_C) > mpker.c
+mpker\$(_O): .headers mpker.c
+	\$(CC) -c \$(CFLAGS) \$(KERNELCFLAGS) \$(DLCFLAGS) \$(CPPFLAGS) \$(GMPINCLUDE) -o mpker\$(_O) mpker.c
+EOT
diff --git a/src/kernel/gmp/gcd.c b/src/kernel/gmp/gcd.c
new file mode 100644
index 0000000..1883ba6
--- /dev/null
+++ b/src/kernel/gmp/gcd.c
@@ -0,0 +1,73 @@
+#line 2 "../src/kernel/gmp/gcd.c"
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* assume y > x > 0. return y mod x */
+static ulong
+resiu(GEN y, ulong x)
+{
+  return mpn_mod_1(LIMBS(y), NLIMBS(y), x);
+}
+
+GEN
+gcdii(GEN a, GEN b)
+{
+  long v, w;
+  pari_sp av;
+  GEN t;
+
+  switch (absi_cmp(a,b))
+  {
+    case 0: return absi(a);
+    case -1: swap(a,b);
+  }
+  if (!signe(b)) return absi(a);
+  /* here |a|>|b|>0. Try single precision first */
+  if (lgefint(a)==3)
+    return igcduu((ulong)a[2], (ulong)b[2]);
+  if (lgefint(b)==3)
+  {
+    ulong u = resiu(a,(ulong)b[2]);
+    if (!u) return absi(b);
+    return igcduu((ulong)b[2], u);
+  }
+  /* larger than gcd: "avma=av" gerepile (erasing t) is valid */
+  av = avma; (void)new_chunk(lgefint(b)+1); /* HACK */
+  t = remii(a,b);
+  if (!signe(t)) { avma=av; return absi(b); }
+
+  a = b; b = t;
+  v = vali(a); a = shifti(a,-v); setabssign(a);
+  w = vali(b); b = shifti(b,-w); setabssign(b);
+  if (w < v) v = w;
+  switch(absi_cmp(a,b))
+  {
+    case  0: avma=av; a=shifti(a,v); return a;
+    case -1: swap(a,b);
+  }
+  if (is_pm1(b)) { avma=av; return int2n(v); }
+ {
+  /* general case */
+  /*This serve two purposes: 1) mpn_gcd destroy its input and need an extra
+   * limb 2) this allows us to use icopy instead of gerepile later.  NOTE: we
+   * must put u before d else the final icopy could fail.
+   */
+  GEN res= cgeti(lgefint(a)+1);
+  GEN ca = icopy_ef(a,lgefint(a)+1);
+  GEN cb = icopy_ef(b,lgefint(b)+1);
+  long l = mpn_gcd(LIMBS(res), LIMBS(ca), NLIMBS(ca), LIMBS(cb), NLIMBS(cb));
+  res[1] = evalsigne(1)|evallgefint(l+2);
+  avma=av;
+  return shifti(res,v);
+  }
+}
diff --git a/src/kernel/gmp/gcdext.c b/src/kernel/gmp/gcdext.c
new file mode 100644
index 0000000..543b901
--- /dev/null
+++ b/src/kernel/gmp/gcdext.c
@@ -0,0 +1,165 @@
+#line 2 "../src/kernel/gmp/gcdext.c"
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*==================================
+ * invmod(a,b,res)
+ *==================================
+ *    If a is invertible, return 1, and set res  = a^{ -1 }
+ *    Otherwise, return 0, and set res = gcd(a,b)
+ */
+int
+invmod(GEN a, GEN b, GEN *res)
+{
+  if (!signe(b)) { *res=absi(a); return 0; }
+  if (NLIMBS(b) < INVMOD_GMP_LIMIT)
+    return invmod_pari(a,b,res);
+  { /* General case: use gcdext(a+b, b) since mpn_gcdext require S1>=S2 */
+    pari_sp av = avma;
+    GEN  ca, cb, u, d;
+    long lu, l, su, sa = signe(a), lb,lna;
+    GEN na;
+    if (!sa) { avma = av; *res = absi(b); return 0; }
+    if (signe(b) < 0) b = negi(b);
+    if (absi_cmp(a, b) < 0)
+      na = sa > 0? addii(a, b): subii(a, b);
+    else
+      na = a;
+    /* Copy serves two purposes:
+     * 1) mpn_gcdext destroys its input and needs an extra limb
+     * 2) allows us to use icopy instead of gerepile later. */
+    lb = lgefint(b); lna = lgefint(na);
+    ca = icopy_ef(na,lna+1);
+    cb = icopy_ef( b,lb+1);
+    /* must create u first else final icopy could fail. */
+    u = cgeti(lna+1);
+    d = cgeti(lna+1);
+    /* na >= b */
+    l = mpn_gcdext(LIMBS(d), LIMBS(u), &lu, LIMBS(ca), NLIMBS(ca), LIMBS(cb), NLIMBS(cb));
+    d[1] = evalsigne(1)|evallgefint(l+2);
+    if (!is_pm1(d)) {avma=av; *res=icopy(d); return 0;}
+    su = lu?((sa ^ lu) < 0)? -1: 1: 0;
+    u[1] = evalsigne(su) | evallgefint(labs(lu)+2);
+    if (su < 0) u = addii(u, b);
+    avma=av; *res=icopy(u); return 1;
+  }
+}
+
+/*==================================
+ * bezout(a,b,pu,pv)
+ *==================================
+ *    Return g = gcd(a,b) >= 0, and assign GENs u,v through pointers pu,pv
+ *    such that g = u*a + v*b.
+ * Special cases:
+ *    a == b == 0 ==> pick u=1, v=0
+ *    a != 0 == b ==> keep v=0
+ *    a == 0 != b ==> keep u=0
+ *    |a| == |b| != 0 ==> keep u=0, set v=+-1
+ * Assignments through pu,pv will be suppressed when the corresponding
+ * pointer is NULL  (but the computations will happen nonetheless).
+ */
+
+GEN
+bezout(GEN a, GEN b, GEN *pu, GEN *pv)
+{
+  long s, sa, sb;
+  ulong g;
+  ulong xu,xu1,xv,xv1;                /* Lehmer stage recurrence matrix */
+
+  s = absi_cmp(a,b);
+  if (s < 0) { swap(a,b); pswap(pu,pv); }
+  /* now |a| >= |b| */
+
+  sa = signe(a); sb = signe(b);
+  if (!sb)
+  {
+    if (pv) *pv = gen_0;
+    switch(sa)
+    {
+    case  0: if (pu) *pu = gen_0;  return gen_0;
+    case  1: if (pu) *pu = gen_1;  return icopy(a);
+    case -1: if (pu) *pu = gen_m1; return negi(a);
+    }
+  }
+  if (s == 0)                        /* |a| == |b| != 0 */
+  {
+    if (pu) *pu = gen_0;
+    if (sb > 0)
+    { if (pv) *pv = gen_1;  return icopy(b); }
+    else
+    { if (pv) *pv = gen_m1; return negi(b); }
+  }
+  /* now |a| > |b| > 0 */
+
+  if (lgefint(a) == 3)                /* single-word affair */
+  {
+    g = xxgcduu((ulong)a[2], (ulong)b[2], 0, &xu, &xu1, &xv, &xv1, &s);
+    sa = s > 0 ? sa : -sa;
+    sb = s > 0 ? -sb : sb;
+    if (pu)
+    {
+      if (xu == 0) *pu = gen_0; /* can happen when b divides a */
+      else if (xu == 1) *pu = sa < 0 ? gen_m1 : gen_1;
+      else if (xu == 2) *pu = sa < 0 ? gen_m2 : gen_2;
+      else
+      {
+        *pu = cgeti(3);
+        (*pu)[1] = evalsigne(sa)|evallgefint(3);
+        (*pu)[2] = xu;
+      }
+    }
+    if (pv)
+    {
+      if (xv == 1) *pv = sb < 0 ? gen_m1 : gen_1;
+      else if (xv == 2) *pv = sb < 0 ? gen_m2 : gen_2;
+      else
+      {
+        *pv = cgeti(3);
+        (*pv)[1] = evalsigne(sb)|evallgefint(3);
+        (*pv)[2] = xv;
+      }
+    }
+    if (g == 1) return gen_1;
+    else if (g == 2) return gen_2;
+    else return utoipos(g);
+  }
+  else
+  { /* general case */
+    pari_sp av = avma;
+    /*Copy serves two purposes:
+     * 1) mpn_gcdext destroys its input and needs an extra limb
+     * 2) allows us to use icopy instead of gerepile later.
+     * NOTE: we must put u before d else the final icopy could fail. */
+    GEN ca = icopy_ef(a,lgefint(a)+1);
+    GEN cb = icopy_ef(b,lgefint(b)+1);
+    GEN u = cgeti(lgefint(a)+1), v = NULL;
+    GEN d = cgeti(lgefint(a)+1);
+    long su,l,lu;
+    l = mpn_gcdext(LIMBS(d), LIMBS(u), &lu, LIMBS(ca), NLIMBS(ca), LIMBS(cb), NLIMBS(cb));
+    if (lu<=0)
+    {
+      if (lu==0) su=0;
+      else {su=-1;lu=-lu;}
+    }
+    else
+      su=1;
+    if (sa<0) su=-su;
+    d[1] = evalsigne(1)|evallgefint(l+2);
+    u[1] = evalsigne(su)|evallgefint(lu+2);
+    if (pv) v=diviiexact(subii(d,mulii(u,a)),b);
+    avma = av;
+    if (pu) *pu=icopy(u);
+    if (pv) *pv=icopy(v);
+    return icopy(d);
+  }
+}
diff --git a/src/kernel/gmp/int.h b/src/kernel/gmp/int.h
new file mode 100644
index 0000000..65bbb89
--- /dev/null
+++ b/src/kernel/gmp/int.h
@@ -0,0 +1,37 @@
+#line 2 "../src/kernel/gmp/int.h"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define int_MSW(x) ((x)+lgefint((x))-1)
+/*x being a t_INT, return a pointer to the most significant word of x.*/
+
+#define int_LSW(x) ((x)+2)
+/*x being a t_INT, return a pointer to the least significant word of x.*/
+
+#define int_precW(x) ((x)-1)
+/*x pointing to a mantissa word, return the previous (less significant)
+ * mantissa word.*/
+
+#define int_nextW(x) ((x)+1)
+/*x pointing to a mantissa word, return the next (more significant) mantissa
+ * word.*/
+
+#define int_W(x,l) ((x)+2+(l))
+/*x being a t_INT, return a pointer to the l-th least significant word of x.*/
+
+#define int_W_lg(x,l,lx) ((x)+2+(l))
+/*x being a t_INT, return a pointer to the l-th least significant word of x,
+ * assuming lgefint(x) = lx.*/
+
+#define PARI_KERNEL_GMP
+/*This macro should not be used in libpari itself.*/
diff --git a/src/kernel/gmp/mp.c b/src/kernel/gmp/mp.c
new file mode 100644
index 0000000..eff7751
--- /dev/null
+++ b/src/kernel/gmp/mp.c
@@ -0,0 +1,1400 @@
+#line 2 "../src/kernel/gmp/mp.c"
+/* Copyright (C) 2002-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                               GMP KERNEL                          **/
+/** BA2002Sep24                                                       **/
+/***********************************************************************/
+/* GMP t_INT as just like normal t_INT, just the mantissa is the other way
+ * round
+ *
+ *   `How would you like to live in Looking-glass House, Kitty?  I
+ *   wonder if they'd give you milk in there?  Perhaps Looking-glass
+ *   milk isn't good to drink--But oh, Kitty! now we come to the
+ *   passage.  You can just see a little PEEP of the passage in
+ *   Looking-glass House, if you leave the door of our drawing-room
+ *   wide open:  and it's very like our passage as far as you can see,
+ *   only you know it may be quite different on beyond.  Oh, Kitty!
+ *   how nice it would be if we could only get through into Looking-
+ *   glass House!  I'm sure it's got, oh! such beautiful things in it!
+ *
+ *  Through the Looking Glass,  Lewis Carrol
+ *
+ *  (pityful attempt to beat GN code/comments rate)
+ *  */
+
+#include <gmp.h>
+#include "pari.h"
+#include "paripriv.h"
+#include "../src/kernel/none/tune-gen.h"
+
+/*We need PARI invmod renamed to invmod_pari*/
+#define INVMOD_PARI
+
+static void *pari_gmp_realloc(void *ptr, size_t old_size, size_t new_size) {
+  (void)old_size; return (void *) pari_realloc(ptr,new_size);
+}
+
+static void pari_gmp_free(void *ptr, size_t old_size){
+  (void)old_size; pari_free(ptr);
+}
+
+static void *(*old_gmp_malloc)(size_t new_size);
+static void *(*old_gmp_realloc)(void *ptr, size_t old_size, size_t new_size);
+static void (*old_gmp_free)(void *ptr, size_t old_size);
+
+void
+pari_kernel_init(void)
+{
+  mp_get_memory_functions (&old_gmp_malloc, &old_gmp_realloc, &old_gmp_free);
+  mp_set_memory_functions((void *(*)(size_t)) pari_malloc, pari_gmp_realloc, pari_gmp_free);
+}
+
+void
+pari_kernel_close(void)
+{
+  void *(*new_gmp_malloc)(size_t new_size);
+  void *(*new_gmp_realloc)(void *ptr, size_t old_size, size_t new_size);
+  void (*new_gmp_free)(void *ptr, size_t old_size);
+  mp_get_memory_functions (&new_gmp_malloc, &new_gmp_realloc, &new_gmp_free);
+  if (new_gmp_malloc==pari_malloc) new_gmp_malloc = old_gmp_malloc;
+  if (new_gmp_realloc==pari_gmp_realloc) new_gmp_realloc = old_gmp_realloc;
+  if (new_gmp_free==pari_gmp_free) new_gmp_free = old_gmp_free;
+  mp_set_memory_functions(new_gmp_malloc, new_gmp_realloc, new_gmp_free);
+}
+
+#define LIMBS(x)  ((mp_limb_t *)((x)+2))
+#define NLIMBS(x) (lgefint(x)-2)
+/*This one is for t_REALs to emphasize they are not t_INTs*/
+#define RLIMBS(x)  ((mp_limb_t *)((x)+2))
+#define RNLIMBS(x) (lg(x)-2)
+
+INLINE void
+xmpn_copy(mp_limb_t *x, mp_limb_t *y, long n)
+{
+  while (--n >= 0) x[n]=y[n];
+}
+
+INLINE void
+xmpn_mirror(mp_limb_t *x, long n)
+{
+  long i;
+  for(i=0;i<(n>>1);i++)
+  {
+    ulong m=x[i];
+    x[i]=x[n-1-i];
+    x[n-1-i]=m;
+  }
+}
+
+INLINE void
+xmpn_mirrorcopy(mp_limb_t *z, mp_limb_t *x, long n)
+{
+  long i;
+  for(i=0;i<n;i++)
+    z[i]=x[n-1-i];
+}
+
+INLINE void
+xmpn_zero(mp_ptr x, mp_size_t n)
+{
+  while (--n >= 0) x[n]=0;
+}
+
+INLINE GEN
+icopy_ef(GEN x, long l)
+{
+  register long lx = lgefint(x);
+  const GEN y = cgeti(l);
+
+  while (--lx > 0) y[lx]=x[lx];
+  return y;
+}
+
+/* NOTE: arguments of "spec" routines (muliispec, addiispec, etc.) aren't
+ * GENs but pairs (long *a, long na) representing a list of digits (in basis
+ * BITS_IN_LONG) : a[0], ..., a[na-1]. [ In ordre to facilitate splitting: no
+ * need to reintroduce codewords ]
+ * Use speci(a,na) to visualize the corresponding GEN.
+ */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                     ADDITION / SUBTRACTION                        **/
+/**                                                                   **/
+/***********************************************************************/
+
+GEN
+setloop(GEN a)
+{
+  pari_sp av = avma - 2 * sizeof(long);
+  (void)cgetg(lgefint(a) + 3, t_VECSMALL);
+  return icopy_avma(a, av); /* two cells of extra space after a */
+}
+
+/* we had a = setloop(?), then some incloops. Reset a to b */
+GEN
+resetloop(GEN a, GEN b) {
+  a[0] = evaltyp(t_INT) | evallg(lgefint(b));
+  affii(b, a); return a;
+}
+
+/* assume a > 0, initialized by setloop. Do a++ */
+static GEN
+incpos(GEN a)
+{
+  long i, l = lgefint(a);
+  for (i=2; i<l; i++)
+    if (++a[i]) return a;
+  a[l] = 1; l++;
+  a[0]=evaltyp(t_INT) | _evallg(l);
+  a[1]=evalsigne(1) | evallgefint(l);
+  return a;
+}
+
+/* assume a < 0, initialized by setloop. Do a++ */
+static GEN
+incneg(GEN a)
+{
+  long i, l = lgefint(a)-1;
+  if (a[2]--)
+  {
+    if (!a[l]) /* implies l = 2 */
+    {
+      a[0] = evaltyp(t_INT) | _evallg(2);
+      a[1] = evalsigne(0) | evallgefint(2);
+    }
+    return a;
+  }
+  for (i=3; i<=l; i++)
+    if (a[i]--) break;
+  if (!a[l])
+  {
+    a[0] = evaltyp(t_INT) | _evallg(l);
+    a[1] = evalsigne(-1) | evallgefint(l);
+  }
+  return a;
+}
+
+/* assume a initialized by setloop. Do a++ */
+GEN
+incloop(GEN a)
+{
+  switch(signe(a))
+  {
+    case 0:
+      a[0]=evaltyp(t_INT) | _evallg(3);
+      a[1]=evalsigne(1) | evallgefint(3);
+      a[2]=1; return a;
+    case -1: return incneg(a);
+    default: return incpos(a);
+  }
+}
+
+INLINE GEN
+adduispec(ulong s, GEN x, long nx)
+{
+  GEN  zd;
+  long lz;
+
+  if (nx == 1) return adduu((ulong)x[0], s);
+  lz = nx+3; zd = cgeti(lz);
+  if (mpn_add_1(LIMBS(zd),(mp_limb_t *)x,nx,s))
+    zd[lz-1]=1;
+  else
+    lz--;
+  zd[1] = evalsigne(1) | evallgefint(lz);
+  return zd;
+}
+
+GEN
+adduispec_offset(ulong s, GEN x, long offset, long nx)
+{
+  GEN xd=x+2+offset;
+  while (nx && *(xd+nx-1)==0) nx--;
+  if (!nx) return utoi(s);
+  return adduispec(s,xd,nx);
+}
+
+INLINE GEN
+addiispec(GEN x, GEN y, long nx, long ny)
+{
+  GEN zd;
+  long lz;
+
+  if (nx < ny) swapspec(x,y, nx,ny);
+  if (ny == 1) return adduispec(*y,x,nx);
+  lz = nx+3; zd = cgeti(lz);
+
+  if (mpn_add(LIMBS(zd),(mp_limb_t *)x,nx,(mp_limb_t *)y,ny))
+    zd[lz-1]=1;
+  else
+    lz--;
+
+  zd[1] = evalsigne(1) | evallgefint(lz);
+  return zd;
+}
+
+/* assume x >= y */
+INLINE GEN
+subiuspec(GEN x, ulong s, long nx)
+{
+  GEN zd;
+  long lz;
+
+  if (nx == 1) return utoi(x[0] - s);
+
+  lz = nx + 2; zd = cgeti(lz);
+  mpn_sub_1 (LIMBS(zd), (mp_limb_t *)x, nx, s);
+  if (! zd[lz - 1]) { --lz; }
+
+  zd[1] = evalsigne(1) | evallgefint(lz);
+  return zd;
+}
+
+/* assume x > y */
+INLINE GEN
+subiispec(GEN x, GEN y, long nx, long ny)
+{
+  GEN zd;
+  long lz;
+  if (ny==1) return subiuspec(x,*y,nx);
+  lz = nx+2; zd = cgeti(lz);
+
+  mpn_sub (LIMBS(zd), (mp_limb_t *)x, nx, (mp_limb_t *)y, ny);
+  while (lz >= 3 && zd[lz - 1] == 0) { lz--; }
+
+  zd[1] = evalsigne(1) | evallgefint(lz);
+  return zd;
+}
+
+static void
+roundr_up_ip(GEN x, long l)
+{
+  long i = l;
+  for(;;)
+  {
+    if (++((ulong*)x)[--i]) break;
+    if (i == 2) { x[2] = HIGHBIT; shiftr_inplace(x, 1); break; }
+  }
+}
+
+void
+affir(GEN x, GEN y)
+{
+  const long s = signe(x), ly = lg(y);
+  long lx, sh, i;
+
+  if (!s)
+  {
+    y[1] = evalexpo(-bit_accuracy(ly));
+    return;
+  }
+  lx = lgefint(x); sh = bfffo(*int_MSW(x));
+  y[1] = evalsigne(s) | evalexpo(bit_accuracy(lx)-sh-1);
+  if (sh) {
+    if (lx <= ly)
+    {
+      for (i=lx; i<ly; i++) y[i]=0;
+      mpn_lshift(LIMBS(y),LIMBS(x),lx-2,sh);
+      xmpn_mirror(LIMBS(y),lx-2);
+      return;
+    }
+    mpn_lshift(LIMBS(y),LIMBS(x)+lx-ly,ly-2,sh);
+    y[2]|=((ulong) x[lx-ly+1])>>(BITS_IN_LONG-sh);
+    xmpn_mirror(LIMBS(y),ly-2);
+    /* lx > ly: round properly */
+    if ((x[lx-ly+1]<<sh) & HIGHBIT) roundr_up_ip(y, ly);
+  }
+  else {
+    GEN xd=int_MSW(x);
+    if (lx <= ly)
+    {
+      for (i=2; i<lx; i++,xd=int_precW(xd)) y[i]=*xd;
+      for (   ; i<ly; i++) y[i]=0;
+      return;
+    }
+    for (i=2; i<ly; i++,xd=int_precW(xd)) y[i]=*xd;
+    /* lx > ly: round properly */
+    if (x[lx-ly+1] & HIGHBIT) roundr_up_ip(y, ly);
+  }
+}
+
+INLINE GEN
+shiftispec(GEN x, long nx, long n)
+{
+  long ny,m;
+  GEN yd, y;
+
+  if (!n) return icopyspec(x, nx);
+
+  if (n > 0)
+  {
+    long d = dvmdsBIL(n, &m);
+    long i;
+
+    ny = nx + d + (m!=0);
+    y = cgeti(ny + 2); yd = y + 2;
+    for (i=0; i<d; i++) yd[i] = 0;
+
+    if (!m) xmpn_copy((mp_limb_t *) (yd + d), (mp_limb_t *) x, nx);
+    else
+    {
+      ulong carryd = mpn_lshift((mp_limb_t *) (yd + d), (mp_limb_t *) x, nx, m);
+      if (carryd) yd[ny - 1] = carryd;
+      else ny--;
+    }
+  }
+  else
+  {
+    long d = dvmdsBIL(-n, &m);
+
+    ny = nx - d;
+    if (ny < 1) return gen_0;
+    y = cgeti(ny + 2); yd = y + 2;
+
+    if (!m) xmpn_copy((mp_limb_t *) yd, (mp_limb_t *) (x + d), nx - d);
+    else
+    {
+      mpn_rshift((mp_limb_t *) yd, (mp_limb_t *) (x + d), nx - d, m);
+      if (yd[ny - 1] == 0)
+      {
+        if (ny == 1) { avma = (pari_sp)(yd + 1); return gen_0; }
+        ny--;
+      }
+    }
+  }
+  y[1] = evalsigne(1)|evallgefint(ny + 2);
+  return y;
+}
+
+GEN
+mantissa2nr(GEN x, long n)
+{
+  long ly, i, m, s = signe(x), lx = lg(x);
+  GEN y;
+  if (!s) return gen_0;
+  if (!n)
+  {
+    y = cgeti(lx);
+    y[1] = evalsigne(s) | evallgefint(lx);
+    xmpn_mirrorcopy(LIMBS(y),RLIMBS(x),lx-2);
+    return y;
+  }
+  if (n > 0)
+  {
+    GEN z = (GEN)avma;
+    long d = dvmdsBIL(n, &m);
+
+    ly = lx+d; y = new_chunk(ly);
+    for ( ; d; d--) *--z = 0;
+    if (!m) for (i=2; i<lx; i++) y[i]=x[i];
+    else
+    {
+      register const ulong sh = BITS_IN_LONG - m;
+      shift_left(y,x, 2,lx-1, 0,m);
+      i = ((ulong)x[2]) >> sh;
+      /* Extend y on the left? */
+      if (i) { ly++; y = new_chunk(1); y[2] = i; }
+    }
+  }
+  else
+  {
+    ly = lx - dvmdsBIL(-n, &m);
+    if (ly<3) return gen_0;
+    y = new_chunk(ly);
+    if (m) {
+      shift_right(y,x, 2,ly, 0,m);
+      if (y[2] == 0)
+      {
+        if (ly==3) { avma = (pari_sp)(y+3); return gen_0; }
+        ly--; avma = (pari_sp)(++y);
+      }
+    } else {
+      for (i=2; i<ly; i++) y[i]=x[i];
+    }
+  }
+  xmpn_mirror(LIMBS(y),ly-2);
+  y[1] = evalsigne(s)|evallgefint(ly);
+  y[0] = evaltyp(t_INT)|evallg(ly); return y;
+}
+
+GEN
+truncr(GEN x)
+{
+  long s, e, d, m, i;
+  GEN y;
+  if ((s=signe(x)) == 0 || (e=expo(x)) < 0) return gen_0;
+  d = nbits2prec(e+1); m = remsBIL(e);
+  if (d > lg(x)) pari_err_PREC( "truncr (precision loss in truncation)");
+
+  y=cgeti(d); y[1] = evalsigne(s) | evallgefint(d);
+  if (++m == BITS_IN_LONG)
+    for (i=2; i<d; i++) y[d-i+1]=x[i];
+  else
+  {
+    GEN z=cgeti(d);
+    for (i=2; i<d; i++) z[d-i+1]=x[i];
+    mpn_rshift(LIMBS(y),LIMBS(z),d-2,BITS_IN_LONG-m);
+    avma=(pari_sp)y;
+  }
+  return y;
+}
+
+/* integral part */
+GEN
+floorr(GEN x)
+{
+  long e, d, m, i, lx;
+  GEN y;
+  if (signe(x) >= 0) return truncr(x);
+  if ((e=expo(x)) < 0) return gen_m1;
+  d = nbits2prec(e+1); m = remsBIL(e);
+  lx=lg(x); if (d>lx) pari_err_PREC( "floorr (precision loss in truncation)");
+  y = cgeti(d+1);
+  if (++m == BITS_IN_LONG)
+  {
+    for (i=2; i<d; i++) y[d-i+1]=x[i];
+    i=d; while (i<lx && !x[i]) i++;
+    if (i==lx) goto END;
+  }
+  else
+  {
+    GEN z=cgeti(d);
+    for (i=2; i<d; i++) z[d-i+1]=x[i];
+    mpn_rshift(LIMBS(y),LIMBS(z),d-2,BITS_IN_LONG-m);
+    if (x[d-1]<<m == 0)
+    {
+      i=d; while (i<lx && !x[i]) i++;
+      if (i==lx) goto END;
+    }
+  }
+  if (mpn_add_1(LIMBS(y),LIMBS(y),d-2,1))
+    y[d++]=1;
+END:
+  y[1] = evalsigne(-1) | evallgefint(d);
+  return y;
+}
+
+INLINE int
+cmpiispec(GEN x, GEN y, long lx, long ly)
+{
+  if (lx < ly) return -1;
+  if (lx > ly) return  1;
+  return mpn_cmp((mp_limb_t*)x,(mp_limb_t*)y, lx);
+}
+
+INLINE int
+equaliispec(GEN x, GEN y, long lx, long ly)
+{
+  if (lx != ly) return 0;
+  return !mpn_cmp((mp_limb_t*)x,(mp_limb_t*)y, lx);
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          MULTIPLICATION                           **/
+/**                                                                   **/
+/***********************************************************************/
+/* assume ny > 0 */
+INLINE GEN
+muluispec(ulong x, GEN y, long ny)
+{
+  if (ny == 1)
+    return muluu(x, *y);
+  else
+  {
+    long lz = ny+3;
+    GEN z = cgeti(lz);
+    ulong hi = mpn_mul_1 (LIMBS(z), (mp_limb_t *)y, ny, x);
+    if (hi) { z[lz - 1] = hi; } else lz--;
+    z[1] = evalsigne(1) | evallgefint(lz);
+    return z;
+  }
+}
+
+/* a + b*|y| */
+GEN
+addumului(ulong a, ulong b, GEN y)
+{
+  GEN z;
+  long i, lz;
+  ulong hi;
+  if (!signe(y)) return utoi(a);
+  lz = lgefint(y)+1;
+  z = cgeti(lz);
+  z[2]=a;
+  for(i=3;i<lz;i++) z[i]=0;
+  hi=mpn_addmul_1(LIMBS(z), LIMBS(y), NLIMBS(y), b);
+  if (hi) z[lz-1]=hi; else lz--;
+  z[1] = evalsigne(1) | evallgefint(lz);
+  avma=(pari_sp)z; return z;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          DIVISION                                 **/
+/**                                                                   **/
+/***********************************************************************/
+
+ulong
+umodiu(GEN y, ulong x)
+{
+  long sy=signe(y);
+  ulong hi;
+  if (!x) pari_err_INV("umodiu",gen_0);
+  if (!sy) return 0;
+  hi = mpn_mod_1(LIMBS(y),NLIMBS(y),x);
+  if (!hi) return 0;
+  return (sy > 0)? hi: x - hi;
+}
+
+/* return |y| \/ x */
+GEN
+diviu_rem(GEN y, ulong x, ulong *rem)
+{
+  long ly;
+  GEN z;
+
+  if (!x) pari_err_INV("diviu_rem",gen_0);
+  if (!signe(y)) { *rem = 0; return gen_0; }
+
+  ly = lgefint(y);
+  if (ly == 3 && (ulong)x > (ulong)y[2]) { *rem = (ulong)y[2]; return gen_0; }
+
+  z = cgeti(ly);
+  *rem = mpn_divrem_1(LIMBS(z), 0, LIMBS(y), NLIMBS(y), x);
+  if (z [ly - 1] == 0) ly--;
+  z[1] = evallgefint(ly) | evalsigne(1);
+  return z;
+}
+
+GEN
+divis_rem(GEN y, long x, long *rem)
+{
+  long sy=signe(y),ly,s;
+  GEN z;
+
+  if (!x) pari_err_INV("divis_rem",gen_0);
+  if (!sy) { *rem = 0; return gen_0; }
+  if (x<0) { s = -sy; x = -x; } else s = sy;
+
+  ly = lgefint(y);
+  if (ly == 3 && (ulong)x > (ulong)y[2]) { *rem = itos(y); return gen_0; }
+
+  z = cgeti(ly);
+  *rem = mpn_divrem_1(LIMBS(z), 0, LIMBS(y), NLIMBS(y), x);
+  if (sy<0) *rem = -  *rem;
+  if (z[ly - 1] == 0) ly--;
+  z[1] = evallgefint(ly) | evalsigne(s);
+  return z;
+}
+
+GEN
+divis(GEN y, long x)
+{
+  long sy=signe(y),ly,s;
+  GEN z;
+
+  if (!x) pari_err_INV("divis",gen_0);
+  if (!sy) return gen_0;
+  if (x<0) { s = -sy; x = -x; } else s=sy;
+
+  ly = lgefint(y);
+  if (ly == 3 && (ulong)x > (ulong)y[2]) return gen_0;
+
+  z = cgeti(ly);
+  (void)mpn_divrem_1(LIMBS(z), 0, LIMBS(y), NLIMBS(y), x);
+  if (z[ly - 1] == 0) ly--;
+  z[1] = evallgefint(ly) | evalsigne(s);
+  return z;
+}
+
+/* We keep llx bits of x and lly bits of y*/
+static GEN
+divrr_with_gmp(GEN x, GEN y)
+{
+  long lx=RNLIMBS(x),ly=RNLIMBS(y);
+  long lw=minss(lx,ly);
+  long lly=minss(lw+1,ly);
+  GEN  w=cgetr(lw+2);
+  long lu=lw+lly;
+  long llx=minss(lu,lx);
+  mp_limb_t *u=(mp_limb_t *)new_chunk(lu);
+  mp_limb_t *z=(mp_limb_t *)new_chunk(lly);
+  mp_limb_t *q,*r;
+  long e=expo(x)-expo(y);
+  long sx=signe(x),sy=signe(y);
+  xmpn_mirrorcopy(z,RLIMBS(y),lly);
+  xmpn_mirrorcopy(u+lu-llx,RLIMBS(x),llx);
+  xmpn_zero(u,lu-llx);
+  q = (mp_limb_t *)new_chunk(lw+1);
+  r = (mp_limb_t *)new_chunk(lly);
+
+  mpn_tdiv_qr(q,r,0,u,lu,z,lly);
+
+  /*Round up: This is not exactly correct we should test 2*r>z*/
+  if ((ulong)r[lly-1] > ((ulong)z[lly-1]>>1))
+    mpn_add_1(q,q,lw+1,1);
+
+  xmpn_mirrorcopy(RLIMBS(w),q,lw);
+
+  if (q[lw] == 0) e--;
+  else if (q[lw] == 1) { shift_right(w,w, 2,lw+2, 1,1); }
+  else { w[2] = HIGHBIT; e++; }
+  if (sy < 0) sx = -sx;
+  w[1] = evalsigne(sx) | evalexpo(e);
+  avma=(pari_sp) w;
+  return w;
+}
+
+/* We keep llx bits of x and lly bits of y*/
+static GEN
+divri_with_gmp(GEN x, GEN y)
+{
+  long llx=RNLIMBS(x),ly=NLIMBS(y);
+  long lly=minss(llx+1,ly);
+  GEN  w=cgetr(llx+2);
+  long lu=llx+lly, ld=ly-lly;
+  mp_limb_t *u=(mp_limb_t *)new_chunk(lu);
+  mp_limb_t *z=(mp_limb_t *)new_chunk(lly);
+  mp_limb_t *q,*r;
+  long sh=bfffo(y[ly+1]);
+  long e=expo(x)-expi(y);
+  long sx=signe(x),sy=signe(y);
+  if (sh) mpn_lshift(z,LIMBS(y)+ld,lly,sh);
+  else xmpn_copy(z,LIMBS(y)+ld,lly);
+  xmpn_mirrorcopy(u+lu-llx,RLIMBS(x),llx);
+  xmpn_zero(u,lu-llx);
+  q = (mp_limb_t *)new_chunk(llx+1);
+  r = (mp_limb_t *)new_chunk(lly);
+
+  mpn_tdiv_qr(q,r,0,u,lu,z,lly);
+
+  /*Round up: This is not exactly correct we should test 2*r>z*/
+  if ((ulong)r[lly-1] > ((ulong)z[lly-1]>>1))
+    mpn_add_1(q,q,llx+1,1);
+
+  xmpn_mirrorcopy(RLIMBS(w),q,llx);
+
+  if (q[llx] == 0) e--;
+  else if (q[llx] == 1) { shift_right(w,w, 2,llx+2, 1,1); }
+  else { w[2] = HIGHBIT; e++; }
+  if (sy < 0) sx = -sx;
+  w[1] = evalsigne(sx) | evalexpo(e);
+  avma=(pari_sp) w;
+  return w;
+}
+
+GEN
+divri(GEN x, GEN y)
+{
+  long  s = signe(y);
+
+  if (!s) pari_err_INV("divri",gen_0);
+  if (!signe(x)) return real_0_bit(expo(x) - expi(y));
+  if (!is_bigint(y)) {
+    GEN z = divru(x, y[2]);
+    if (s < 0) togglesign(z);
+    return z;
+  }
+  return divri_with_gmp(x,y);
+}
+
+GEN
+divrr(GEN x, GEN y)
+{
+  long sx=signe(x), sy=signe(y), lx,ly,lr,e,i,j;
+  ulong y0,y1;
+  GEN r, r1;
+
+  if (!sy) pari_err_INV("divrr",y);
+  e = expo(x) - expo(y);
+  if (!sx) return real_0_bit(e);
+  if (sy<0) sx = -sx;
+
+  lx=lg(x); ly=lg(y);
+  if (ly==3)
+  {
+    ulong k = x[2], l = (lx>3)? x[3]: 0;
+    LOCAL_HIREMAINDER;
+    if (k < (ulong)y[2]) e--;
+    else
+    {
+      l >>= 1; if (k&1) l |= HIGHBIT;
+      k >>= 1;
+    }
+    hiremainder = k; k = divll(l,y[2]);
+    if (hiremainder & HIGHBIT)
+    {
+      k++;
+      if (!k) { k = HIGHBIT; e++; }
+    }
+    r = cgetr(3);
+    r[1] = evalsigne(sx) | evalexpo(e);
+    r[2] = k; return r;
+  }
+
+  if (ly>=DIVRR_GMP_LIMIT)
+    return divrr_with_gmp(x,y);
+
+  lr = minss(lx,ly); r = new_chunk(lr);
+  r1 = r-1;
+  r1[1] = 0; for (i=2; i<lr; i++) r1[i]=x[i];
+  r1[lr] = (lx>ly)? x[lr]: 0;
+  y0 = y[2]; y1 = y[3];
+  for (i=0; i<lr-1; i++)
+  { /* r1 = r + (i-1), OK up to r1[2] (accesses at most r[lr]) */
+    ulong k, qp;
+    LOCAL_HIREMAINDER;
+    LOCAL_OVERFLOW;
+
+    if ((ulong)r1[1] == y0)
+    {
+      qp = ULONG_MAX; k = addll(y0,r1[2]);
+    }
+    else
+    {
+      if ((ulong)r1[1] > y0) /* can't happen if i=0 */
+      {
+        GEN y1 = y+1;
+        j = lr-i; r1[j] = subll(r1[j],y1[j]);
+        for (j--; j>0; j--) r1[j] = subllx(r1[j],y1[j]);
+        j=i; do ((ulong*)r)[--j]++; while (j && !r[j]);
+      }
+      hiremainder = r1[1]; overflow = 0;
+      qp = divll(r1[2],y0); k = hiremainder;
+    }
+    j = lr-i+1;
+    if (!overflow)
+    {
+      long k3, k4;
+      k3 = mulll(qp,y1);
+      if (j == 3) /* i = lr - 2 maximal, r1[3] undefined -> 0 */
+        k4 = subll(hiremainder,k);
+      else
+      {
+        k3 = subll(k3, r1[3]);
+        k4 = subllx(hiremainder,k);
+      }
+      while (!overflow && k4) { qp--; k3=subll(k3,y1); k4=subllx(k4,y0); }
+    }
+    if (j<ly) (void)mulll(qp,y[j]); else { hiremainder = 0 ; j = ly; }
+    for (j--; j>1; j--)
+    {
+      r1[j] = subll(r1[j], addmul(qp,y[j]));
+      hiremainder += overflow;
+    }
+    if ((ulong)r1[1] != hiremainder)
+    {
+      if ((ulong)r1[1] < hiremainder)
+      {
+        qp--;
+        j = lr-i-(lr-i>=ly); r1[j] = addll(r1[j], y[j]);
+        for (j--; j>1; j--) r1[j] = addllx(r1[j], y[j]);
+      }
+      else
+      {
+        r1[1] -= hiremainder;
+        while (r1[1])
+        {
+          qp++; if (!qp) { j=i; do ((ulong*)r)[--j]++; while (j && !r[j]); }
+          j = lr-i-(lr-i>=ly); r1[j] = subll(r1[j],y[j]);
+          for (j--; j>1; j--) r1[j] = subllx(r1[j],y[j]);
+          r1[1] -= overflow;
+        }
+      }
+    }
+    *++r1 = qp;
+  }
+  /* i = lr-1 */
+  /* round correctly */
+  if ((ulong)r1[1] > (y0>>1))
+  {
+    j=i; do r[--j]++; while (j && !r[j]);
+  }
+  r1 = r-1; for (j=i; j>=2; j--) r[j]=r1[j];
+  if (r[0] == 0) e--;
+  else if (r[0] == 1) { shift_right(r,r, 2,lr, 1,1); }
+  else { /* possible only when rounding up to 0x2 0x0 ... */
+    r[2] = (long)HIGHBIT; e++;
+  }
+  r[0] = evaltyp(t_REAL)|evallg(lr);
+  r[1] = evalsigne(sx) | evalexpo(e);
+  return r;
+}
+
+/* Integer division x / y: such that sign(r) = sign(x)
+ *   if z = ONLY_REM return remainder, otherwise return quotient
+ *   if z != NULL set *z to remainder
+ *   *z is the last object on stack (and thus can be disposed of with cgiv
+ *   instead of gerepile)
+ * If *z is zero, we put gen_0 here and no copy.
+ * space needed: lx + ly
+ */
+GEN
+dvmdii(GEN x, GEN y, GEN *z)
+{
+  long sx=signe(x),sy=signe(y);
+  long lx, ly, lq;
+  pari_sp av;
+  GEN r,q;
+
+  if (!sy)
+  {
+    if (z == ONLY_REM && !sx) return gen_0;
+    pari_err_INV("dvmdii",y);
+  }
+  if (!sx)
+  {
+    if (!z || z == ONLY_REM) return gen_0;
+    *z=gen_0; return gen_0;
+  }
+  lx=lgefint(x);
+  ly=lgefint(y); lq=lx-ly;
+  if (lq <= 0)
+  {
+    if (lq == 0)
+    {
+      long s=mpn_cmp(LIMBS(x),LIMBS(y),NLIMBS(x));
+      if (s>0) goto DIVIDE;
+      if (s==0)
+      {
+        if (z == ONLY_REM) return gen_0;
+        if (z) *z = gen_0;
+        if (sx < 0) sy = -sy;
+        return stoi(sy);
+      }
+    }
+    if (z == ONLY_REM) return icopy(x);
+    if (z) *z = icopy(x);
+    return gen_0;
+  }
+DIVIDE: /* quotient is non-zero */
+  av=avma; if (sx<0) sy = -sy;
+  if (ly==3)
+  {
+    ulong lq = lx;
+    ulong si;
+    q  = cgeti(lq);
+    si = mpn_divrem_1(LIMBS(q), 0, LIMBS(x), NLIMBS(x), y[2]);
+    if (q[lq - 1] == 0) lq--;
+    if (z == ONLY_REM)
+    {
+      avma=av; if (!si) return gen_0;
+      r=cgeti(3);
+      r[1] = evalsigne(sx) | evallgefint(3);
+      r[2] = si; return r;
+    }
+    q[1] = evalsigne(sy) | evallgefint(lq);
+    if (!z) return q;
+    if (!si) { *z=gen_0; return q; }
+    r=cgeti(3);
+    r[1] = evalsigne(sx) | evallgefint(3);
+    r[2] = si; *z=r; return q;
+  }
+  if (z == ONLY_REM)
+  {
+    ulong lr = lgefint(y);
+    ulong lq = lgefint(x)-lgefint(y)+3;
+    GEN r = cgeti(lr);
+    GEN q = cgeti(lq);
+    mpn_tdiv_qr(LIMBS(q), LIMBS(r),0, LIMBS(x), NLIMBS(x), LIMBS(y), NLIMBS(y));
+    if (!r[lr - 1])
+    {
+      while(lr>2 && !r[lr - 1]) lr--;
+      if (lr == 2) {avma=av; return gen_0;} /* exact division */
+    }
+    r[1] = evalsigne(sx) | evallgefint(lr);
+    avma = (pari_sp) r; return r;
+  }
+  else
+  {
+    ulong lq = lgefint(x)-lgefint(y)+3;
+    ulong lr = lgefint(y);
+    GEN q = cgeti(lq);
+    GEN r = cgeti(lr);
+    mpn_tdiv_qr(LIMBS(q), LIMBS(r),0, LIMBS(x), NLIMBS(x), LIMBS(y), NLIMBS(y));
+    if (q[lq - 1] == 0) lq--;
+    q[1] = evalsigne(sy) | evallgefint(lq);
+    if (!z) { avma = (pari_sp)q; return q; }
+    if (!r[lr - 1])
+    {
+      while(lr>2 && !r[lr - 1]) lr--;
+      if (lr == 2) {avma=(pari_sp) q; *z=gen_0; return q;} /* exact division */
+    }
+    r[1] = evalsigne(sx) | evallgefint(lr);
+    avma = (pari_sp) r; *z = r; return q;
+  }
+}
+
+/* Montgomery reduction.
+ * N has k words, assume T >= 0 has less than 2k.
+ * Return res := T / B^k mod N, where B = 2^BIL
+ * such that 0 <= res < T/B^k + N  and  res has less than k words */
+GEN
+red_montgomery(GEN T, GEN N, ulong inv)
+{
+  pari_sp av;
+  GEN Te, Td, Ne, Nd, scratch;
+  ulong i, j, m, t, d, k = NLIMBS(N);
+  int carry;
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  if (k == 0) return gen_0;
+  d = NLIMBS(T); /* <= 2*k */
+  if (d == 0) return gen_0;
+#ifdef DEBUG
+  if (d > 2*k) pari_err_BUG("red_montgomery");
+#endif
+  if (k == 1)
+  { /* as below, special cased for efficiency */
+    ulong n = (ulong)N[2];
+    if (d == 1) {
+      hiremainder = (ulong)T[2];
+      m = hiremainder * inv;
+      (void)addmul(m, n); /* t + m*n = 0 */
+      return utoi(hiremainder);
+    } else { /* d = 2 */
+      hiremainder = (ulong)T[2];
+      m = hiremainder * inv;
+      (void)addmul(m, n); /* t + m*n = 0 */
+      t = addll(hiremainder, (ulong)T[3]);
+      if (overflow) t -= n; /* t > n doesn't fit in 1 word */
+      return utoi(t);
+    }
+  }
+  /* assume k >= 2 */
+  av = avma; scratch = new_chunk(k<<1); /* >= k + 2: result fits */
+
+  /* copy T to scratch space (pad with zeroes to 2k words) */
+  Td = scratch;
+  Te = T + 2;
+  for (i=0; i < d     ; i++) *Td++ = *Te++;
+  for (   ; i < (k<<1); i++) *Td++ = 0;
+
+  Te = scratch - 1; /* 1 beyond end of current T mantissa (in scratch) */
+  Ne = N + 1;       /* 1 beyond end of N mantissa */
+
+  carry = 0;
+  for (i=0; i<k; i++) /* set T := T/B nod N, k times */
+  {
+    Td = Te; /* one beyond end of (new) T mantissa */
+    Nd = Ne;
+    hiremainder = *++Td;
+    m = hiremainder * inv; /* solve T + m N = O(B) */
+
+    /* set T := (T + mN) / B */
+    Te = Td;
+    (void)addmul(m, *++Nd); /* = 0 */
+    for (j=1; j<k; j++)
+    {
+      t = addll(addmul(m, *++Nd), *++Td);
+      *Td = t;
+      hiremainder += overflow;
+    }
+    t = addll(hiremainder, *++Td); *Td = t + carry;
+    carry = (overflow || (carry && *Td == 0));
+  }
+  if (carry)
+  { /* Td > N overflows (k+1 words), set Td := Td - N */
+    Td = Te;
+    Nd = Ne;
+    t = subll(*++Td, *++Nd); *Td = t;
+    while (Td < (GEN)av) { t = subllx(*++Td, *++Nd); *Td = t; }
+  }
+
+  /* copy result */
+  Td = (GEN)av - 1; /* *Td = high word of final result */
+  while (*Td == 0 && Te < Td) Td--; /* strip leading 0s */
+  k = Td - Te; if (!k) { avma = av; return gen_0; }
+  Td = (GEN)av - k; /* will write mantissa there */
+  (void)memmove(Td, Te+1, k*sizeof(long));
+  Td -= 2;
+  Td[0] = evaltyp(t_INT) | evallg(k+2);
+  Td[1] = evalsigne(1) | evallgefint(k+2);
+#ifdef DEBUG
+{
+  long l = NLIMBS(N), s = BITS_IN_LONG*l;
+  GEN R = int2n(s);
+  GEN res = remii(mulii(T, Fp_inv(R, N)), N);
+  if (k > lgefint(N)
+    || !equalii(remii(Td,N),res)
+    || cmpii(Td, addii(shifti(T, -s), N)) >= 0) pari_err_BUG("red_montgomery");
+}
+#endif
+  avma = (pari_sp)Td; return Td;
+}
+
+/* EXACT INTEGER DIVISION */
+
+#if 1 /* use undocumented GMP interface */
+static void
+GEN2mpz(mpz_t X, GEN x)
+{
+  long l = lgefint(x)-2;
+  X->_mp_alloc = l;
+  X->_mp_size = signe(x) > 0? l: -l;
+  X->_mp_d = LIMBS(x);
+}
+static void
+mpz2GEN(GEN z, mpz_t Z)
+{
+  long l = Z->_mp_size;
+  z[1] = evalsigne(l > 0? 1: -1) | evallgefint(labs(l)+2);
+}
+
+/* assume y != 0 and the division is exact */
+GEN
+diviuexact(GEN x, ulong y)
+{
+  if (!signe(x)) return gen_0;
+  {
+    long l = lgefint(x);
+    mpz_t X, Z;
+    GEN z = cgeti(l);
+    GEN2mpz(X, x);
+    Z->_mp_alloc = l-2;
+    Z->_mp_size  = l-2;
+    Z->_mp_d = LIMBS(z);
+    mpz_divexact_ui(Z, X, y);
+    mpz2GEN(z, Z); return z;
+  }
+}
+
+/* Find z such that x=y*z, knowing that y | x (unchecked) */
+GEN
+diviiexact(GEN x, GEN y)
+{
+  if (!signe(y)) pari_err_INV("diviiexact",y);
+  if (lgefint(y) == 3)
+  {
+    GEN z = diviuexact(x, y[2]);
+    if (signe(y) < 0) togglesign(z);
+    return z;
+  }
+  if (!signe(x)) return gen_0;
+  {
+    long l = lgefint(x);
+    mpz_t X, Y, Z;
+    GEN z = cgeti(l);
+    GEN2mpz(X, x);
+    GEN2mpz(Y, y);
+    Z->_mp_alloc = l-2;
+    Z->_mp_size  = l-2;
+    Z->_mp_d = LIMBS(z);
+    mpz_divexact(Z, X, Y);
+    mpz2GEN(z, Z); return z;
+  }
+}
+#else
+/* assume y != 0 and the division is exact */
+GEN
+diviuexact(GEN x, ulong y)
+{
+  /*TODO: implement true exact division.*/
+  return divii(x,utoi(y));
+}
+
+/* Find z such that x=y*z, knowing that y | x (unchecked)
+ * Method: y0 z0 = x0 mod B = 2^BITS_IN_LONG ==> z0 = 1/y0 mod B.
+ *    Set x := (x - z0 y) / B, updating only relevant words, and repeat */
+GEN
+diviiexact(GEN x, GEN y)
+{
+  /*TODO: use mpn_bdivmod instead*/
+  return divii(x,y);
+}
+#endif
+
+/* assume yz != and yz | x */
+GEN
+diviuuexact(GEN x, ulong y, ulong z)
+{
+  long tmp[4];
+  ulong t;
+  LOCAL_HIREMAINDER;
+  t = mulll(y, z);
+  if (!hiremainder) return diviuexact(x, t);
+  tmp[0] = evaltyp(t_INT)|_evallg(4);
+  tmp[1] = evalsigne(1)|evallgefint(4);
+  tmp[2] = t;
+  tmp[3] = hiremainder;
+  return diviiexact(x, tmp);
+}
+
+
+/********************************************************************/
+/**                                                                **/
+/**               INTEGER MULTIPLICATION                           **/
+/**                                                                **/
+/********************************************************************/
+
+/* nx >= ny = num. of digits of x, y (not GEN, see mulii) */
+GEN
+muliispec(GEN x, GEN y, long nx, long ny)
+{
+  GEN zd;
+  long lz;
+  ulong hi;
+
+  if (nx < ny) swapspec(x,y, nx,ny);
+  if (!ny) return gen_0;
+  if (ny == 1) return muluispec((ulong)*y, x, nx);
+
+  lz = nx+ny+2;
+  zd = cgeti(lz);
+  hi = mpn_mul(LIMBS(zd), (mp_limb_t *)x, nx, (mp_limb_t *)y, ny);
+  if (!hi) lz--;
+  /*else zd[lz-1]=hi; GH tell me it is not necessary.*/
+
+  zd[1] = evalsigne(1) | evallgefint(lz);
+  return zd;
+}
+GEN
+muluui(ulong x, ulong y, GEN z)
+{
+  long t, s = signe(z);
+  GEN r;
+  LOCAL_HIREMAINDER;
+
+  if (!x || !y || !signe(z)) return gen_0;
+  t = mulll(x,y);
+  if (!hiremainder)
+    r = muluispec(t, z+2, lgefint(z)-2);
+  else
+  {
+    long tmp[2];
+    tmp[1] = hiremainder;
+    tmp[0] = t;
+    r = muliispec(z+2,tmp, lgefint(z)-2, 2);
+  }
+  setsigne(r,s); return r;
+}
+
+GEN
+sqrispec(GEN x, long nx)
+{
+  GEN zd;
+  long lz;
+
+  if (!nx) return gen_0;
+  if (nx==1) return sqru(*x);
+
+  lz = (nx<<1)+2;
+  zd = cgeti(lz);
+#ifdef mpn_sqr
+  mpn_sqr(LIMBS(zd), (mp_limb_t *)x, nx);
+#else
+  mpn_mul_n(LIMBS(zd), (mp_limb_t *)x, (mp_limb_t *)x, nx);
+#endif
+  if (zd[lz-1]==0) lz--;
+
+  zd[1] = evalsigne(1) | evallgefint(lz);
+  return zd;
+}
+
+INLINE GEN
+sqrispec_mirror(GEN x, long nx)
+{
+  GEN cx=new_chunk(nx);
+  GEN z;
+  xmpn_mirrorcopy((mp_limb_t *)cx,(mp_limb_t *)x,nx);
+  z=sqrispec(cx, nx);
+  xmpn_mirror(LIMBS(z), NLIMBS(z));
+  return z;
+}
+
+/* leaves garbage on the stack. */
+INLINE GEN
+muliispec_mirror(GEN x, GEN y, long nx, long ny)
+{
+  GEN cx, cy, z;
+  long s = 0;
+  while (nx && x[nx-1]==0) { nx--; s++; }
+  while (ny && y[ny-1]==0) { ny--; s++; }
+  cx=new_chunk(nx); cy=new_chunk(ny);
+  xmpn_mirrorcopy((mp_limb_t *)cx,(mp_limb_t *)x,nx);
+  xmpn_mirrorcopy((mp_limb_t *)cy,(mp_limb_t *)y,ny);
+  z =  nx>=ny ? muliispec(cx, cy, nx, ny): muliispec(cy, cx, ny, nx);
+  if (s)
+  {
+    long i, lz = lgefint(z) + s;
+    (void)new_chunk(s);
+    z -= s;
+    for (i=0; i<s; i++) z[2+i]=0;
+    z[1] = evalsigne(1) | evallgefint(lz);
+    z[0] = evaltyp(t_INT) | evallg(lz);
+  }
+  xmpn_mirror(LIMBS(z), NLIMBS(z));
+  return z;
+}
+
+/* x % (2^n), assuming n >= 0 */
+GEN
+remi2n(GEN x, long n)
+{
+  ulong hi;
+  long l, k, lx, ly, sx = signe(x);
+  GEN z, xd, zd;
+
+  if (!sx || !n) return gen_0;
+
+  k = dvmdsBIL(n, &l);
+  lx = lgefint(x);
+  if (lx < k+3) return icopy(x);
+
+  xd = x + (2 + k);
+  /* x = |k|...|1|#|... : copy the last l bits of # and the first k words
+   *              ^--- initial xd  */
+  hi = ((ulong)*xd) & ((1UL<<l)-1); /* last l bits of # = top bits of result */
+  if (!hi)
+  { /* strip leading zeroes from result */
+    xd--; while (k && !*xd) { k--; xd--; }
+    if (!k) return gen_0;
+    ly = k+2;
+  }
+  else
+    ly = k+3;
+
+  zd = z = cgeti(ly);
+  *++zd = evalsigne(sx) | evallgefint(ly);
+  xd = x+1; for ( ;k; k--) *++zd = *++xd;
+  if (hi) *++zd = hi;
+  return z;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                      INTEGER SQUARE ROOT                       **/
+/**                                                                **/
+/********************************************************************/
+
+/* Return S (and set R) s.t S^2 + R = N, 0 <= R <= 2S.
+ * As for dvmdii, R is last on stack and guaranteed to be gen_0 in case the
+ * remainder is 0. R = NULL is allowed. */
+GEN
+sqrtremi(GEN a, GEN *r)
+{
+  long l, na = NLIMBS(a);
+  mp_size_t nr;
+  GEN S;
+  if (!na) {
+    if (r) *r = gen_0;
+    return gen_0;
+  }
+  l = (na + 5) >> 1; /* 2 + ceil(na/2) */
+  S = cgetipos(l);
+  if (r) {
+    GEN R = cgeti(2 + na);
+    nr = mpn_sqrtrem(LIMBS(S), LIMBS(R), LIMBS(a), na);
+    if (nr) R[1] = evalsigne(1) | evallgefint(nr+2);
+    else    { avma = (pari_sp)S; R = gen_0; }
+    *r = R;
+  }
+  else
+    (void)mpn_sqrtrem(LIMBS(S), NULL, LIMBS(a), na);
+  return S;
+}
+
+/* compute sqrt(|a|), assuming a != 0 */
+GEN
+sqrtr_abs(GEN a)
+{
+  GEN res;
+  mp_limb_t *b, *c;
+  long l = RNLIMBS(a), e = expo(a), er = e>>1;
+  long n;
+  res = cgetr(2 + l);
+  res[1] = evalsigne(1) | evalexpo(er);
+  if (e&1)
+  {
+    b = (mp_limb_t *) new_chunk(l<<1);
+    xmpn_zero(b,l);
+    xmpn_mirrorcopy(b+l, RLIMBS(a), l);
+    c = (mp_limb_t *) new_chunk(l);
+    n = mpn_sqrtrem(c,b,b,l<<1); /* c <- sqrt; b <- rem */
+    if (n>l || (n==l && mpn_cmp(b,c,l) > 0)) mpn_add_1(c,c,l,1);
+  }
+  else
+  {
+    ulong u;
+    b = (mp_limb_t *) mantissa2nr(a,-1);
+    b[1] = ((ulong)a[l+1])<<(BITS_IN_LONG-1);
+    b = (mp_limb_t *) new_chunk(l);
+    xmpn_zero(b,l+1); /* overwrites the former b[0] */
+    c = (mp_limb_t *) new_chunk(l + 1);
+    n = mpn_sqrtrem(c,b,b,(l<<1)+2); /* c <- sqrt; b <- rem */
+    u = (ulong)*c++;
+    if ( u&HIGHBIT || (u == ~HIGHBIT &&
+             (n>l || (n==l && mpn_cmp(b,c,l)>0))))
+      mpn_add_1(c,c,l,1);
+  }
+  xmpn_mirrorcopy(RLIMBS(res),c,l);
+  avma = (pari_sp)res; return res;
+}
+
+/* Normalize a non-negative integer */
+GEN
+int_normalize(GEN x, long known_zero_words)
+{
+  long i =  lgefint(x) - 1 - known_zero_words;
+  for ( ; i > 1; i--)
+    if (x[i]) { setlgefint(x, i+1); return x; }
+  x[1] = evalsigne(0) | evallgefint(2); return x;
+}
+
+/********************************************************************
+ **                                                                **
+ **                           Base Conversion                      **
+ **                                                                **
+ ********************************************************************/
+
+ulong *
+convi(GEN x, long *l)
+{
+  long n = nchar2nlong(2 + (long)(NLIMBS(x) * (BITS_IN_LONG * LOG10_2)));
+  GEN str = cgetg(n+1, t_VECSMALL);
+  unsigned char *res = (unsigned char*) GSTR(str);
+  long llz = mpn_get_str(res, 10, LIMBS(icopy(x)), NLIMBS(x));
+  long lz;
+  ulong *z;
+  long i, j;
+  unsigned char *t;
+  while (!*res) {res++; llz--;} /*Strip leading zeros*/
+  lz  = (8+llz)/9;
+  z = (ulong*)new_chunk(1+lz);
+  t=res+llz+9;
+  for(i=0;i<llz-8;i+=9)
+  {
+    ulong s;
+    t-=18;
+    s=*t++;
+    for (j=1; j<9;j++)
+      s=10*s+*t++;
+    *z++=s;
+  }
+  if (i<llz)
+  {
+    unsigned char *t = res;
+    ulong s=*t++;
+    for (j=i+1; j<llz;j++)
+      s=10*s+*t++;
+    *z++=s;
+  }
+  *l = lz;
+  return z;
+}
diff --git a/src/kernel/gmp/tune.h b/src/kernel/gmp/tune.h
new file mode 100644
index 0000000..849aeec
--- /dev/null
+++ b/src/kernel/gmp/tune.h
@@ -0,0 +1,79 @@
+#ifdef LONG_IS_64BIT
+#define __MULII_KARATSUBA_LIMIT         -1
+#define __SQRI_KARATSUBA_LIMIT          -1
+#define __MULII_FFT_LIMIT               -1
+#define __SQRI_FFT_LIMIT                -1
+#define __MULRR_MULII_LIMIT             74
+#define __Fp_POW_REDC_LIMIT             17
+#define __Fp_POW_BARRETT_LIMIT         127
+#define __INVNEWTON_LIMIT              520
+#define __DIVRR_GMP_LIMIT                4
+#define __EXPNEWTON_LIMIT               66
+#define __LOGAGM_LIMIT                   6
+#define __LOGAGMCX_LIMIT                22
+#define __AGM_ATAN_LIMIT                60
+#define __INVMOD_GMP_LIMIT               3
+#define __Flx_MUL_KARATSUBA_LIMIT      142
+#define __Flx_SQR_KARATSUBA_LIMIT      316
+#define __Flx_MUL_HALFMULII_LIMIT        5
+#define __Flx_SQR_HALFSQRI_LIMIT         3
+#define __Flx_MUL_MULII_LIMIT            7
+#define __Flx_SQR_SQRI_LIMIT             5
+#define __Flx_MUL_MULII2_LIMIT           5
+#define __Flx_SQR_SQRI2_LIMIT            7
+#define __Flx_INVBARRETT_LIMIT        1142
+#define __Flx_DIVREM_BARRETT_LIMIT     768
+#define __Flx_REM_BARRETT_LIMIT       1266
+#define __Flx_BARRETT_LIMIT            398
+#define __Flx_HALFGCD_LIMIT             81
+#define __Flx_GCD_LIMIT               1017
+#define __Flx_EXTGCD_LIMIT             241
+#define __FpX_INVBARRETT_LIMIT         111
+#define __FpX_DIVREM_BARRETT_LIMIT     113
+#define __FpX_REM_BARRETT_LIMIT        111
+#define __FpX_BARRETT_LIMIT             38
+#define __FpX_HALFGCD_LIMIT             58
+#define __FpX_GCD_LIMIT                406
+#define __FpX_EXTGCD_LIMIT              87
+#define __RgX_MUL_LIMIT                  9
+#define __RgX_SQR_LIMIT                 38
+#else
+#define __MULII_KARATSUBA_LIMIT         -1
+#define __SQRI_KARATSUBA_LIMIT          -1
+#define __MULII_FFT_LIMIT               -1
+#define __SQRI_FFT_LIMIT                -1
+#define __MULRR_MULII_LIMIT              8
+#define __Fp_POW_REDC_LIMIT              3
+#define __Fp_POW_BARRETT_LIMIT          11
+#define __INVNEWTON_LIMIT               66
+#define __DIVRR_GMP_LIMIT                4
+#define __EXPNEWTON_LIMIT              197
+#define __LOGAGM_LIMIT                  45
+#define __LOGAGMCX_LIMIT                32
+#define __AGM_ATAN_LIMIT                89
+#define __INVMOD_GMP_LIMIT               3
+#define __Flx_MUL_KARATSUBA_LIMIT       90
+#define __Flx_SQR_KARATSUBA_LIMIT      159
+#define __Flx_MUL_HALFMULII_LIMIT        7
+#define __Flx_SQR_HALFSQRI_LIMIT         4
+#define __Flx_MUL_MULII_LIMIT            8
+#define __Flx_SQR_SQRI_LIMIT             5
+#define __Flx_MUL_MULII2_LIMIT         152
+#define __Flx_SQR_SQRI2_LIMIT          470
+#define __Flx_INVBARRETT_LIMIT        1115
+#define __Flx_DIVREM_BARRETT_LIMIT    1289
+#define __Flx_REM_BARRETT_LIMIT        689
+#define __Flx_BARRETT_LIMIT            327
+#define __Flx_HALFGCD_LIMIT            321
+#define __Flx_GCD_LIMIT               2514
+#define __Flx_EXTGCD_LIMIT             632
+#define __FpX_INVBARRETT_LIMIT         121
+#define __FpX_DIVREM_BARRETT_LIMIT     116
+#define __FpX_REM_BARRETT_LIMIT        127
+#define __FpX_BARRETT_LIMIT             44
+#define __FpX_HALFGCD_LIMIT             55
+#define __FpX_GCD_LIMIT                414
+#define __FpX_EXTGCD_LIMIT              81
+#define __RgX_MUL_LIMIT                  7
+#define __RgX_SQR_LIMIT                 34
+#endif
diff --git a/src/kernel/hppa/asm0.h b/src/kernel/hppa/asm0.h
new file mode 100644
index 0000000..9e71cec
--- /dev/null
+++ b/src/kernel/hppa/asm0.h
@@ -0,0 +1,89 @@
+#line 2 "../src/kernel/hppa/asm0.h"
+/* Copyright (C) 2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file was made using idea from Bruno Haible ix86 asm inline kernel
+ * and code from Nigel Smart hppa asm kernel. mulll was inspired from
+ * longlong.h from the GNU MP package.*/
+
+/*
+ASM addll mulll
+NOASM bfffo divll
+*/
+#ifdef ASMINLINE
+
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("add %2,%3,%0\n\taddc %%r0,%%r0,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2) \
+        : "cc"); \
+  __value; \
+})
+
+#define addllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("sub %4,%5,%%r0\n\taddc %2,%3,%0\n\taddc %%r0,%%r0,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (overflow), "r" ((ulong) 1)\
+        : "cc"); \
+  __value; \
+})
+
+#define subll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("sub %2,%3,%0\n\taddc %%r0,%%r0,%1\n\tsubi 1,%1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2) \
+        : "cc"); \
+  __value; \
+})
+
+#define subllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("sub %%r0,%4,%%r0\n\tsubb %2,%3,%0\n\taddc %%r0,%%r0,%1\n\tsubi 1,%1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (overflow)\
+        : "cc"); \
+  __value; \
+})
+
+#define mulll(a,b) \
+__extension__ ({ ulong __arg1 = (a), __arg2 = (b); \
+   union {double z; ulong x[2];} __vtab; \
+   __asm__ ("xmpyu %1,%2,%0" \
+        : "=f" (__vtab.z) \
+        : "f" (__arg1), "f" (__arg2) \
+        : "cc"); \
+   hiremainder=__vtab.x[0]; \
+   __vtab.x[1]; \
+})
+
+#define addmul(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+    union {double z; ulong x[2];} __vtab; \
+    __asm__ ("xmpyu %1,%2,%0" \
+        : "=f" (__vtab.z) \
+        : "f" (__arg1), "f" (__arg2) \
+        : "cc"); \
+    __asm__ ("add %2,%3,%0\n\taddc %%r0, %4, %1" \
+        : "=&r" (__value), "=r" (hiremainder) \
+        : "r" (__vtab.x[1]),"r" (hiremainder), "r" (__vtab.x[0]) \
+        : "cc"); \
+    __value; \
+})
+
+#endif
diff --git a/src/kernel/hppa64/asm0.h b/src/kernel/hppa64/asm0.h
new file mode 100644
index 0000000..a80b74c
--- /dev/null
+++ b/src/kernel/hppa64/asm0.h
@@ -0,0 +1,121 @@
+#line 2 "../src/kernel/hppa64/asm0.h"
+/* Copyright (C) 2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file was made using idea from Bruno Haible ix86 asm inline kernel
+ * and code from Nigel Smart hppa asm kernel. mulll was inspired from
+ * longlong.h from the GNU MP package.*/
+
+/*
+ASM addll mulll
+NOASM bfffo divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("add %2,%3,%0\n\tadd,dc %%r0,%%r0,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2) \
+        : "cc"); \
+  __value; \
+})
+
+#define addllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("sub %4,%5,%%r0\n\tadd,dc %2,%3,%0\n\tadd,dc %%r0,%%r0,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (overflow), "r" ((ulong) 1)\
+        : "cc"); \
+  __value; \
+})
+
+#define subll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("sub %2,%3,%0\n\tadd,dc %%r0,%%r0,%1\n\tsubi 1,%1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2) \
+        : "cc"); \
+  __value; \
+})
+
+#define subllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("sub %%r0,%4,%%r0\n\tsub,db %2,%3,%0\n\tadd,dc %%r0,%%r0,%1\n\tsubi 1,%1,%1" \
+        : "=&r" (__value), "=r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (overflow)\
+        : "cc"); \
+  __value; \
+})
+
+/* z=a+b; c+= carry; return z */
+#define __addllc(a,b,c) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("add %2,%3,%0\n\tadd,dc %4,%%r0,%1" \
+        : "=&r" (__value), "=r" (c) \
+        : "r" (__arg1), "r" (__arg2), "r" (c) \
+        : "cc"); \
+  __value; \
+})
+
+/* 32x32->64 multiply*/
+#define __mulhh(a,b) \
+__extension__ ({ unsigned int __arg1 = (a), __arg2 = (b); \
+   ulong __value; \
+   __asm__ ("xmpyu %1,%2,%0" \
+        : "=f" (__value) \
+        : "f" (__arg1), "f" (__arg2) \
+        : "cc"); \
+   __value; \
+})
+
+#define mulll(arg1,arg2) \
+__extension__ ({ \
+  const ulong __x=(arg1), __y=(arg2); \
+  const ulong __xlo = LOWWORD(__x), __xhi = HIGHWORD(__x); \
+  const ulong __ylo = LOWWORD(__y), __yhi = HIGHWORD(__y); \
+  ulong __xylo,__xymid,__xyhi,__xymidhi,__xymidlo; \
+  ulong __xylh,__xyhl; \
+  __xylo = __mulhh(__xlo,__ylo); __xyhi = __mulhh(__xhi,__yhi); \
+  __xylh = __mulhh(__xlo,__yhi); __xyhl = __mulhh(__xhi,__ylo); \
+  __xymid = __xylh+__xyhl; \
+  if (__xymid<__xylh) __xyhi += (1UL << BITS_IN_HALFULONG); \
+  __xymidhi = HIGHWORD(__xymid); \
+  __xymidlo = __xymid << BITS_IN_HALFULONG; \
+  __xylo = __addllc(__xylo,__xymidlo,__xyhi); \
+  hiremainder = __xyhi + __xymidhi; \
+  __xylo; \
+})
+
+#define addmul(arg1,arg2) \
+__extension__ ({ \
+  const ulong __x=(arg1), __y=(arg2); \
+  const ulong __xlo = LOWWORD(__x), __xhi = HIGHWORD(__x); \
+  const ulong __ylo = LOWWORD(__y), __yhi = HIGHWORD(__y); \
+  ulong __xylo,__xymid,__xyhi,__xymidhi,__xymidlo; \
+  ulong __xylh,__xyhl; \
+  __xylo = __mulhh(__xlo,__ylo); __xyhi = __mulhh(__xhi,__yhi); \
+  __xylh = __mulhh(__xlo,__yhi); __xyhl = __mulhh(__xhi,__ylo); \
+  __xymid = __xylh+__xyhl; \
+  if (__xymid<__xylh) __xyhi += (1UL << BITS_IN_HALFULONG); \
+  __xylo = __addllc(__xylo,hiremainder,__xyhi); \
+  __xymidhi = HIGHWORD(__xymid); \
+  __xymidlo = __xymid << BITS_IN_HALFULONG; \
+  __xylo = __addllc(__xylo,__xymidlo,__xyhi); \
+  hiremainder = __xyhi + __xymidhi; \
+  __xylo; \
+})
+
+#endif
diff --git a/src/kernel/ia64/asm0.h b/src/kernel/ia64/asm0.h
new file mode 100644
index 0000000..5e26a4a
--- /dev/null
+++ b/src/kernel/ia64/asm0.h
@@ -0,0 +1,55 @@
+#line 2 "../src/kernel/ia64/asm0.h"
+/* Copyright (C) 2006  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*
+ASM mulll bfffo
+NOASM addll divll
+*/
+
+#ifdef ASMINLINE
+/* Written by Guillaume Hanrot */
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+
+#define bfffo(a)                                                        \
+__extension__ ({ ulong __arg1 = (a), __tmp, _a, _c;                     \
+    __asm__ ("mux1 %0 = %1, @rev" : "=r" (__tmp) : "r" (__arg1));       \
+    __asm__ ("czx1.l %0 = %1" : "=r" (_a) : "r" (-__tmp | __tmp));      \
+    _c = (_a - 1) << 3;                                                 \
+    __arg1 >>= _c;                                                      \
+    if (__arg1 >= 1 << 4)                                               \
+      __arg1 >>= 4, _c += 4;                                            \
+    if (__arg1 >= 1 << 2)                                               \
+      __arg1 >>= 2, _c += 2;                                            \
+    _c += __arg1 >> 1;                                                  \
+    63 - _c;                                                            \
+})
+
+#define mulll(a, b)                                                     \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __arg2 = (b), __value;                            \
+  __asm__ ("xma.hu %0 = %2, %3, f0\n\t;;\n\txma.l %1 = %2, %3, f0"      \
+           : "=&f" (hiremainder), "=f" (__value)                        \
+           : "f" (__arg1), "f" (__arg2));                               \
+  __value;                                                              \
+})
+
+#define addmul(a, b)                                                    \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __arg2 = (b), __value;                            \
+  __asm__ ("xma.hu %0 = %2, %3, %4\n\txma.l %1 = %2, %3, %4"            \
+           : "=&f" (hiremainder), "=f" (__value)                        \
+           : "f" (__arg1), "f" (__arg2), "f" (hiremainder));            \
+  __value;                                                              \
+})
+#endif
diff --git a/src/kernel/ia64/asm1.h b/src/kernel/ia64/asm1.h
new file mode 100644
index 0000000..7446b7e
--- /dev/null
+++ b/src/kernel/ia64/asm1.h
@@ -0,0 +1,42 @@
+/* Extracted from gmp-4.1.2
+ * FIXME: This file is unused until somebody implements
+ * invert_word(x) = return floor( 2^(2*BIL)/x ) */
+extern ulong invert_word(ulong);
+
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+__extension__ ({                          \
+    ulong __x;                             \
+    __x = (al) - (bl);                     \
+    (sh) = (ah) - (bh) - (__x > (al));     \
+    (sl) = __x;                            \
+})
+
+#define divll(x, y)                                      \
+__extension__ ({                                         \
+  register ulong _di, _x = (x), _y = (y), _q, _ql, _r;   \
+  register ulong _xh, _xl, _k, __hire;                   \
+                                                         \
+  if (_y & 0x8000000000000000UL)                         \
+      { _k = 0; __hire = hiremainder; }                  \
+  else                                                   \
+  {                                                      \
+    _k = bfffo(_y);                                      \
+    __hire = (hiremainder << _k) | (_x >> (64 - _k));    \
+    _x <<= _k; _y <<=  _k;                               \
+  }                                                      \
+  _di = invert_word(_y);                                 \
+  _ql = mulll (__hire, _di);                             \
+  _q = __hire + hiremainder;                             \
+  _xl = mulll(_q, _y); _xh = hiremainder;                \
+  sub_ddmmss (_xh, _r, __hire, _x, _xh, _xl);            \
+  if (_xh != 0)                                          \
+  {                                                      \
+    sub_ddmmss (_xh, _r, _xh, _r, 0, _y); _q += 1;       \
+    if (_xh != 0)                                        \
+      { sub_ddmmss (_xh, _r, _xh, _r, 0, _y); _q += 1; } \
+  }                                                      \
+  if (_r >= _y)                                          \
+    { _r -= _y; _q += 1; }                               \
+  hiremainder = _r >> _k;                                \
+  _q;                                                    \
+})
diff --git a/src/kernel/ix86/asm0.h b/src/kernel/ix86/asm0.h
new file mode 100644
index 0000000..36bb324
--- /dev/null
+++ b/src/kernel/ix86/asm0.h
@@ -0,0 +1,135 @@
+#line 2 "../src/kernel/ix86/asm0.h"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file defines some "level 0" kernel functions for Intel ix86  */
+/* It is intended for use with an external "asm" definition          */
+
+/*
+ASM addll mulll bfffo divll
+*/
+#ifdef ASMINLINE
+/* Written by Bruno Haible, 1996-1998.
+   addllx8/subllx8 by Bill Allombert, 2011.
+*/
+
+/* This file can assume the GNU C extensions.
+   (It is included only if __GNUC__ is defined.) */
+
+/* Use local variables whenever possible. */
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("addl %3,%0 ; adcl %1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "0" (__arg1), "g" (__arg2), "1" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define addllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("subl %5,%2 ; adcl %4,%0 ; adcl %1,%1" \
+        : "=r" (__value), "=&r" (overflow), "=&r" (__temp) \
+        : "0" (__arg1), "g" (__arg2), "g" (overflow), "1" ((ulong)0), "2" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define addllx8(a,b,c,overflow) \
+do { long *__arg1 = a, *__arg2 = b, *__out = c; \
+     ulong __temp; \
+   __asm__ ("subl %5, %0 \n\t" \
+            "movl    (%2), %0 ; adcl    (%3),%0; movl %0,    (%4) \n\t" \
+            "movl  -4(%2), %0 ; adcl  -4(%3),%0; movl %0,  -4(%4) \n\t" \
+            "movl  -8(%2), %0 ; adcl  -8(%3),%0; movl %0,  -8(%4) \n\t" \
+            "movl -12(%2), %0 ; adcl -12(%3),%0; movl %0, -12(%4) \n\t" \
+            "movl -16(%2), %0 ; adcl -16(%3),%0; movl %0, -16(%4) \n\t" \
+            "movl -20(%2), %0 ; adcl -20(%3),%0; movl %0, -20(%4) \n\t" \
+            "movl -24(%2), %0 ; adcl -24(%3),%0; movl %0, -24(%4) \n\t" \
+            "movl -28(%2), %0 ; adcl -28(%3),%0; movl %0, -28(%4) \n\t" \
+            "adcl  %1, %1" \
+        : "=&r" (__temp), "=&r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (__out), "g" (overflow), "0" ((ulong)0), "1" ((ulong)0)        : "cc"); \
+} while(0)
+
+#define subll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("subl %3,%0 ; adcl %1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "0" (__arg1), "g" (__arg2), "1" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define subllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("subl %5,%2 ; sbbl %4,%0 ; adcl %1,%1" \
+        : "=r" (__value), "=&r" (overflow), "=&r" (__temp) \
+        : "0" (__arg1), "g" (__arg2), "g" (overflow), "1" ((ulong)0), "2" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define subllx8(a,b,c,overflow) \
+do { long *__arg1 = a, *__arg2 = b, *__out = c; \
+     ulong __temp; \
+   __asm__ ("subl %5, %0 \n\t" \
+            "movl    (%2), %0 ; sbbl    (%3),%0; movl %0,    (%4) \n\t" \
+            "movl  -4(%2), %0 ; sbbl  -4(%3),%0; movl %0,  -4(%4) \n\t" \
+            "movl  -8(%2), %0 ; sbbl  -8(%3),%0; movl %0,  -8(%4) \n\t" \
+            "movl -12(%2), %0 ; sbbl -12(%3),%0; movl %0, -12(%4) \n\t" \
+            "movl -16(%2), %0 ; sbbl -16(%3),%0; movl %0, -16(%4) \n\t" \
+            "movl -20(%2), %0 ; sbbl -20(%3),%0; movl %0, -20(%4) \n\t" \
+            "movl -24(%2), %0 ; sbbl -24(%3),%0; movl %0, -24(%4) \n\t" \
+            "movl -28(%2), %0 ; sbbl -28(%3),%0; movl %0, -28(%4) \n\t" \
+            "adcl  %1, %1" \
+        : "=&r" (__temp), "=&r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (__out), "g" (overflow), "0" ((ulong)0), "1" ((ulong)0)        : "cc"); \
+} while(0)
+
+
+#define mulll(a,b) \
+__extension__ ({ ulong __valuelo, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("mull %3" \
+        : "=a" /* %eax */ (__valuelo), "=d" /* %edx */ (hiremainder) \
+        : "0" (__arg1), "rm" (__arg2)); \
+   __valuelo; \
+})
+
+#define addmul(a,b) \
+__extension__ ({ ulong __valuelo, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("mull %4 ; addl %5,%0 ; adcl %6,%1" \
+        : "=a" /* %eax */ (__valuelo), "=&d" /* %edx */ (hiremainder), "=r" (__temp) \
+        : "0" (__arg1), "rm" (__arg2), "g" (hiremainder), "2" ((ulong)0)); \
+   __valuelo; \
+})
+
+#define divll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("divl %4" \
+        : "=a" /* %eax */ (__value), "=d" /* %edx */ (hiremainder) \
+        : "0" /* %eax */ (__arg1), "1" /* %edx */ (hiremainder), "mr" (__arg2)); \
+   __value; \
+})
+
+#define bfffo(x) \
+__extension__ ({ ulong __arg = (x); \
+   int leading_one_position; \
+  __asm__ ("bsrl %1,%0" : "=r" (leading_one_position) : "rm" (__arg)); \
+  31 - leading_one_position; \
+})
+
+#endif
diff --git a/src/kernel/m68k/asm0.h b/src/kernel/m68k/asm0.h
new file mode 100644
index 0000000..40067c3
--- /dev/null
+++ b/src/kernel/m68k/asm0.h
@@ -0,0 +1,101 @@
+#line 2 "../src/kernel/m68k/asm0.h"
+/* Copyright (C) 2006  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Written by Bill Allombert and dedicated to thoses who wrote the original
+ * m68k kernel mp.s */
+
+/*
+ASM addll mulll bfffo divll
+*/
+
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a,b)                                              \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b);     \
+   __asm__ ("add.l %2,%0 ; addx.l %1,%1"                        \
+        : "=&d" (__value), "=d" (overflow)                      \
+        : "rm" (__arg1), "0" (__arg2), "1" (0UL)                \
+        : "cc");                                                \
+  __value;                                                      \
+})
+
+#define addllx(a,b)                                             \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("neg.l %2 ; addx.l %4,%0 ; addx.l %1,%1"            \
+        : "=d" (__value), "=d" (overflow), "=d" (__temp)        \
+        : "0" (__arg1), "d" (__arg2), "2" (overflow), "1" (0UL) \
+        : "cc");                                                \
+  __value;                                                      \
+})
+
+#define subll(a,b)                                              \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b);     \
+   __asm__ ("sub.l %3,%0 ; addx.l %1,%1"                        \
+        : "=&d" (__value), "=d" (overflow)                      \
+        : "0" (__arg1), "rm" (__arg2), "1" (0UL)                \
+        : "cc");                                                \
+  __value;                                                      \
+})
+
+#define subllx(a,b)                                             \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("neg.l %2 ; subx.l %4,%0 ; addx.l %1,%1"            \
+        : "=d" (__value), "=d" (overflow), "=d" (__temp)        \
+        : "0" (__arg1), "d" (__arg2), "2" (overflow), "1" (0UL) \
+        : "cc");                                                \
+  __value;                                                      \
+})
+
+#define mulll(a, b)                                                     \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __arg2 = (b), __value;                            \
+  __asm__ ("mulu.l %2, %0:%1"                                           \
+           : "=d" (hiremainder), "=d" (__value)                         \
+           : "md" (__arg1) , "1" (__arg2)                               \
+           : "cc");                                                     \
+  __value;                                                              \
+})
+
+#define addmul(a, b)                                                    \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __arg2 = (b), __value;                            \
+  __asm__ ("mulu.l %2, %0:%1; add.l %4,%1; addx.l %5,%0"                \
+           : "=&d" (hiremainder), "=&d" (__value)                       \
+           : "md" (__arg1), "1" (__arg2), "d" (hiremainder), "d" (0UL)  \
+           : "cc" );                                                    \
+  __value;                                                              \
+})
+
+#define bfffo(a)                                                        \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __value;                                          \
+  __asm__ ("bfffo %1{#0:#0}, %0"                                        \
+           : "=d" (__value)                                             \
+           : "md" (__arg1)                                              \
+           : "cc" );                                                    \
+  __value;                                                              \
+})
+
+#define divll(a, b)                                                     \
+__extension__ ({                                                        \
+  ulong __arg2 = (b), __value =(a);                                     \
+  __asm__ ("divu.l %2, %0:%1"                                           \
+           : "+d" (hiremainder), "+d" (__value)                         \
+           : "md" (__arg2)                                              \
+           : "cc");                                                     \
+  __value;                                                              \
+})
+#endif
diff --git a/src/kernel/mips/asm0.h b/src/kernel/mips/asm0.h
new file mode 100644
index 0000000..f790915
--- /dev/null
+++ b/src/kernel/mips/asm0.h
@@ -0,0 +1,42 @@
+#line 2 "../src/kernel/mips/asm0.h"
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/*
+ASM mulll
+NOASM addll bfffo divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define mulll(a, b)                                         \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("multu %2,%3\n\tmfhi %1"                          \
+   : "=&l" (__value), "=&r" (hiremainder)                   \
+   : "r" (__arg1), "r" (__arg2)                             \
+   : "hi");                                                 \
+ __value;                                                   \
+})
+
+#define addmul(a, b)                                                    \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __arg2 = (b), __value, __tmp;                     \
+  __asm__ ("multu %3,%4\n\tmfhi %0\n\tmflo %2\n\t"                      \
+           "addu %1,%2,%5\n\tsltu %2,%1,%5\n\taddu %0,%0,%2"            \
+           : "=&r" (hiremainder), "=&r" (__value), "=&r" (__tmp)        \
+           : "r" (__arg1), "r" (__arg2), "r" (hiremainder)              \
+           : "hi", "lo");                                               \
+  __value;                                                              \
+})
+
+#endif
diff --git a/src/kernel/mips64/asm0.h b/src/kernel/mips64/asm0.h
new file mode 100644
index 0000000..627042d
--- /dev/null
+++ b/src/kernel/mips64/asm0.h
@@ -0,0 +1,42 @@
+#line 2 "../src/kernel/mips64/asm0.h"
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/*
+ASM mulll
+NOASM addll bfffo divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define mulll(a, b)                                         \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("dmultu %2,%3\n\tmfhi %1"                         \
+   : "=&l" (__value), "=&r" (hiremainder)                   \
+   : "r" (__arg1), "r" (__arg2)                             \
+   : "hi");                                                 \
+ __value;                                                   \
+})
+
+#define addmul(a, b)                                                    \
+__extension__ ({                                                        \
+  ulong __arg1 = (a), __arg2 = (b), __value, __tmp;                     \
+  __asm__ ("dmultu %3,%4\n\tmfhi %0\n\tmflo %2\n\t"                     \
+           "daddu %1,%2,%5\n\tsltu %2,%1,%5\n\tdaddu %0,%0,%2"          \
+           : "=&r" (hiremainder), "=&r" (__value), "=&r" (__tmp)        \
+           : "r" (__arg1), "r" (__arg2), "r" (hiremainder)              \
+           : "hi", "lo");                                               \
+  __value;                                                              \
+})
+
+#endif
diff --git a/src/kernel/none/MakeLVL1.SH b/src/kernel/none/MakeLVL1.SH
new file mode 100644
index 0000000..3174c7b
--- /dev/null
+++ b/src/kernel/none/MakeLVL1.SH
@@ -0,0 +1,12 @@
+cat >> $file << EOT
+L1OBJS=$kern1/int.h $knone/level1.h
+parilvl1.h: \$(L1OBJS) $src/headers/paritune.h
+	if test -r ./tune.h; then d=.; else d=$kern1; fi;\
+          cat \$\$d/tune.h \$(L1OBJS) > parilvl1.h
+MP_C=$kern1/mp.c $knone/cmp.c $knone/gcdll.c $knone/ratlift.c\
+  $knone/invmod.c $kern1/gcd.c $kern1/gcdext.c $knone/mp_indep.c $knone/add.c
+mpker.c: \$(MP_C)
+	cat \$(MP_C) > mpker.c
+mpker\$(_O): .headers mpker.c
+	\$(CC) -c \$(CFLAGS) \$(KERNELCFLAGS) \$(DLCFLAGS) \$(CPPFLAGS) -o mpker\$(_O) mpker.c
+EOT
diff --git a/src/kernel/none/add.c b/src/kernel/none/add.c
new file mode 100644
index 0000000..d76c3e5
--- /dev/null
+++ b/src/kernel/none/add.c
@@ -0,0 +1,374 @@
+#line 2 "../src/kernel/none/add.c"
+/* Copyright (C) 2002-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+INLINE GEN
+icopy_sign(GEN x, long sx)
+{
+  GEN y=icopy(x);
+  setsigne(y,sx);
+  return y;
+}
+
+GEN
+addsi_sign(long x, GEN y, long sy)
+{
+  long sx,ly;
+  GEN z;
+
+  if (!x) return icopy_sign(y, sy);
+  if (!sy) return stoi(x);
+  if (x<0) { sx=-1; x=-x; } else sx=1;
+  if (sx==sy)
+  {
+    z = adduispec(x,y+2, lgefint(y)-2);
+    setsigne(z,sy); return z;
+  }
+  ly=lgefint(y);
+  if (ly==3)
+  {
+    const long d = y[2] - x;
+    if (!d) return gen_0;
+    z=cgeti(3);
+    if (y[2] < 0 || d > 0) {
+      z[1] = evalsigne(sy) | evallgefint(3);
+      z[2] = d;
+    }
+    else {
+      z[1] = evalsigne(-sy) | evallgefint(3);
+      z[2] =-d;
+    }
+    return z;
+  }
+  z = subiuspec(y+2,x, ly-2);
+  setsigne(z,sy); return z;
+}
+GEN
+addui_sign(ulong x, GEN y, long sy)
+{
+  long ly;
+  GEN z;
+
+  if (!x) return icopy_sign(y, sy);
+  if (!sy) return utoipos(x);
+  if (sy == 1) return adduispec(x,y+2, lgefint(y)-2);
+  ly=lgefint(y);
+  if (ly==3)
+  {
+    const ulong t = y[2];
+    if (x == t) return gen_0;
+    z=cgeti(3);
+    if (x < t) {
+      z[1] = evalsigne(-1) | evallgefint(3);
+      z[2] = t - x;
+    }
+    else {
+      z[1] = evalsigne(1) | evallgefint(3);
+      z[2] = x - t;
+    }
+    return z;
+  }
+  z = subiuspec(y+2,x, ly-2);
+  setsigne(z,-1); return z;
+}
+
+/* return gen_0 when the sign is 0 */
+GEN
+addii_sign(GEN x, long sx, GEN y, long sy)
+{
+  long lx,ly;
+  GEN z;
+
+  if (!sx) return sy? icopy_sign(y, sy): gen_0;
+  if (!sy) return icopy_sign(x, sx);
+  lx = lgefint(x);
+  ly = lgefint(y);
+  if (sx==sy)
+    z = addiispec(x+2,y+2,lx-2,ly-2);
+  else
+  { /* sx != sy */
+    long i = cmpiispec(x+2,y+2,lx-2,ly-2);
+    if (!i) return gen_0;
+    /* we must ensure |x| > |y| for subiispec */
+    if (i < 0) {
+      sx = sy;
+      z = subiispec(y+2,x+2,ly-2,lx-2);
+    }
+    else
+      z = subiispec(x+2,y+2,lx-2,ly-2);
+  }
+  setsigne(z,sx); return z;
+}
+
+INLINE GEN
+rcopy_sign(GEN x, long sx) { GEN y = rcopy(x); setsigne(y,sx); return y; }
+
+GEN
+addir_sign(GEN x, long sx, GEN y, long sy)
+{
+  long e, l, ly;
+  GEN z;
+
+  if (!sx) return rcopy_sign(y, sy);
+  e = expo(y) - expi(x);
+  if (!sy)
+  {
+    if (e >= 0) return rcopy_sign(y, sy);
+    z = itor(x, nbits2prec(-e));
+    setsigne(z, sx); return z;
+  }
+
+  ly = lg(y);
+  if (e > 0)
+  {
+    l = ly - divsBIL(e);
+    if (l < 3) return rcopy_sign(y, sy);
+  }
+  else l = ly + nbits2extraprec(-e);
+  z = (GEN)avma;
+  y = addrr_sign(itor(x,l), sx, y, sy);
+  ly = lg(y); while (ly--) *--z = y[ly];
+  avma = (pari_sp)z; return z;
+}
+
+static GEN
+addsr_sign(long x, GEN y, long sy)
+{
+  long e, l, ly, sx;
+  GEN z;
+
+  if (!x) return rcopy_sign(y, sy);
+  if (x < 0) { sx = -1; x = -x; } else sx = 1;
+  e = expo(y) - expu(x);
+  if (!sy)
+  {
+    if (e >= 0) return rcopy_sign(y, sy);
+    if (sx == -1) x = -x;
+    return stor(x, nbits2prec(-e));
+  }
+
+  ly = lg(y);
+  if (e > 0)
+  {
+    l = ly - divsBIL(e);
+    if (l < 3) return rcopy_sign(y, sy);
+  }
+  else l = ly + nbits2extraprec(-e);
+  z = (GEN)avma;
+  y = addrr_sign(stor(x,l), sx, y, sy);
+  ly = lg(y); while (ly--) *--z = y[ly];
+  avma = (pari_sp)z; return z;
+}
+
+GEN
+addsr(long x, GEN y) { return addsr_sign(x, y, signe(y)); }
+
+GEN
+subsr(long x, GEN y) { return addsr_sign(x, y, -signe(y)); }
+
+/* return x + 1, assuming x > 0 is a normalized t_REAL of exponent 0 */
+GEN
+addrex01(GEN x)
+{
+  long l = lg(x);
+  GEN y = cgetr(l);
+  y[1] = evalsigne(1) | _evalexpo(1);
+  y[2] = HIGHBIT | (((ulong)x[2] & ~HIGHBIT) >> 1);
+  shift_right(y, x, 3,l, x[2], 1);
+  return y;
+}
+/* return subrs(x,1) to the min of (prec(x), prec(x-1) + 1),
+ * assuming x > 1 is a normalized t_REAL of exponent 0
+ * [ goal: avoid the loss of significant bits form subrs ]*/
+GEN
+subrex01(GEN x)
+{
+  long i, sh, k, ly, lx = lg(x);
+  ulong u;
+  GEN y;
+  k = 2;
+  u = (ulong)x[2] & (~HIGHBIT);
+  while (!u) u = x[++k]; /* terminates: x not a power of 2 */
+  ly = (k == 2)? lx: lx - k+3; /* NB: +3, not +2: 1 extra word */
+  y = cgetr(ly);
+  sh = bfffo(u);
+  if (sh)
+    shift_left(y+2, x+k, 0, lx-k-1, 0, sh);
+  else
+  { for (i = 2; i < lx-k+2; i++) y[i] = x[k-2 + i]; }
+  for (i = lx-k+2; i < ly; i++) y[i] = 0;
+  y[1] = evalsigne(1) | evalexpo(- (bit_accuracy(k) + sh));
+  return y;
+}
+
+GEN
+addrr_sign(GEN x, long sx, GEN y, long sy)
+{
+  long lx, ex = expo(x);
+  long ly, ey = expo(y), e = ey - ex;
+  long i, j, lz, ez, m;
+  int extend, f2;
+  GEN z;
+  LOCAL_OVERFLOW;
+
+  if (!sy)
+  {
+    if (!sx)
+    {
+      if (e > 0) ex = ey;
+      return real_0_bit(ex);
+    }
+    if (e >= 0) return real_0_bit(ey);
+    lz = nbits2prec(-e);
+    lx = lg(x); if (lz > lx) lz = lx;
+    z = cgetr(lz); while(--lz) z[lz] = x[lz];
+    setsigne(z,sx); return z;
+  }
+  if (!sx)
+  {
+    if (e <= 0) return real_0_bit(ex);
+    lz = nbits2prec(e);
+    ly = lg(y); if (lz > ly) lz = ly;
+    z = cgetr(lz); while (--lz) z[lz] = y[lz];
+    setsigne(z,sy); return z;
+  }
+
+  if (e < 0) { swap(x,y); lswap(sx,sy); ey=ex; e=-e; }
+  /* now ey >= ex */
+  lx = lg(x);
+  ly = lg(y);
+  /* If exponents differ, need to shift one argument, here x. If
+   * extend = 1: extension of x,z by m < BIL bits (round to 1 word) */
+  /* in this case, lz = lx + d + 1, otherwise lx + d */
+  extend = 0;
+  if (e)
+  {
+    long d = dvmdsBIL(e, &m), l = ly-d;
+    if (l <= 2) return rcopy_sign(y, sy);
+    if (l > lx) { lz = lx + d + 1; extend = 1; }
+    else        { lz = ly; lx = l; }
+    if (m)
+    { /* shift x right m bits */
+      const pari_sp av = avma;
+      const ulong sh = BITS_IN_LONG-m;
+      GEN p1 = x; x = new_chunk(lx + lz + 1);
+      shift_right(x,p1,2,lx, 0,m);
+      if (extend) x[lx] = p1[lx-1] << sh;
+      avma = av; /* HACK: cgetr(lz) will not overwrite x */
+    }
+  }
+  else
+  { /* d = 0 */
+    m = 0;
+    if (lx > ly) lx = ly;
+    lz = lx;
+  }
+
+  if (sx == sy)
+  { /* addition */
+    i = lz-1;
+    j = lx-1;
+    if (extend) {
+      ulong garde = addll(x[lx], y[i]);
+      if (m < 4) /* don't extend for few correct bits */
+        z = cgetr(--lz);
+      else
+      {
+        z = cgetr(lz);
+        z[i] = garde;
+      }
+    }
+    else
+    {
+      z = cgetr(lz);
+      z[i] = addll(x[j], y[i]); j--;
+    }
+    i--;
+    for (; j>=2; i--,j--) z[i] = addllx(x[j],y[i]);
+    if (overflow)
+    {
+      z[1] = 1; /* stops since z[1] != 0 */
+      for (;;) { z[i] = (ulong) y[i]+1; if (z[i--]) break; }
+      if (i <= 0)
+      {
+        shift_right(z,z, 2,lz, 1,1);
+        z[1] = evalsigne(sx) | evalexpo(ey+1); return z;
+      }
+    }
+    for (; i>=2; i--) z[i] = y[i];
+    z[1] = evalsigne(sx) | evalexpo(ey); return z;
+  }
+
+  /* subtraction */
+  if (e) f2 = 1;
+  else
+  {
+    i = 2; while (i < lx && x[i] == y[i]) i++;
+    if (i==lx) return real_0_bit(ey+1 - bit_accuracy(lx));
+    f2 = ((ulong)y[i] > (ulong)x[i]);
+  }
+  /* result is non-zero. f2 = (y > x) */
+  i = lz-1; z = cgetr(lz);
+  if (f2)
+  {
+    j = lx-1;
+    if (extend) z[i] = subll(y[i], x[lx]);
+    else        z[i] = subll(y[i], x[j--]);
+    for (i--; j>=2; i--) z[i] = subllx(y[i], x[j--]);
+    if (overflow) /* stops since y[1] != 0 */
+      for (;;) { z[i] = (ulong) y[i]-1; if (y[i--]) break; }
+    for (; i>=2; i--) z[i] = y[i];
+    sx = sy;
+  }
+  else
+  {
+    if (extend) z[i] = subll(x[lx], y[i]);
+    else        z[i] = subll(x[i],  y[i]);
+    for (i--; i>=2; i--) z[i] = subllx(x[i], y[i]);
+  }
+
+  x = z+2; i = 0; while (!x[i]) i++;
+  lz -= i; z += i;
+  j = bfffo(z[2]); /* need to shift left by j bits to normalize mantissa */
+  ez = ey - (j | (i * BITS_IN_LONG));
+  if (extend)
+  { /* z was extended by d+1 words [should be e bits = d words + m bits] */
+    /* not worth keeping extra word if less than 5 significant bits in there */
+    if (m - j < 5 && lz > 3)
+    { /* shorten z */
+      ulong last = (ulong)z[--lz]; /* cancelled word */
+
+      /* if we need to shift anyway, shorten from left
+       * If not, shorten from right, neutralizing last word of z */
+      if (j == 0)
+        /* stackdummy((pari_sp)(z + lz+1), (pari_sp)(z + lz)); */
+        z[lz] = evaltyp(t_VECSMALL) | _evallg(1);
+      else
+      {
+        GEN t = z;
+        z++; shift_left(z,t,2,lz-1, last,j);
+      }
+      if ((last<<j) & HIGHBIT)
+      { /* round up */
+        i = lz-1;
+        while (++((ulong*)z)[i] == 0 && i > 1) i--;
+        if (i == 1) { ez++; z[2] = (long)HIGHBIT; }
+      }
+    }
+    else if (j) shift_left(z,z,2,lz-1, 0,j);
+  }
+  else if (j) shift_left(z,z,2,lz-1, 0,j);
+  z[1] = evalsigne(sx) | evalexpo(ez);
+  z[0] = evaltyp(t_REAL) | evallg(lz);
+  avma = (pari_sp)z; return z;
+}
diff --git a/src/kernel/none/addll.h b/src/kernel/none/addll.h
new file mode 100644
index 0000000..7e8082a
--- /dev/null
+++ b/src/kernel/none/addll.h
@@ -0,0 +1,103 @@
+#line 2 "../src/kernel/none/addll.h"
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file originally adapted from gmp-3.1.1 (from T. Granlund), files
+ * longlong.h and gmp-e_IMPL.h
+
+  Copyright (C) 2000 Free Software Foundation, Inc. */
+
+#undef LOCAL_OVERFLOW
+#define LOCAL_OVERFLOW
+extern ulong overflow;
+
+#if !defined(INLINE)
+extern long addll(ulong x, ulong y);
+extern long addllx(ulong x, ulong y);
+extern long subll(ulong x, ulong y);
+extern long subllx(ulong x, ulong y);
+#else
+
+#if defined(__GNUC__) && !defined(DISABLE_INLINE)
+#undef LOCAL_OVERFLOW
+#define LOCAL_OVERFLOW register ulong overflow
+
+#define addll(a, b)                                             \
+__extension__ ({                                                \
+   ulong __arg1 = (a), __arg2 = (b), __value = __arg1 + __arg2; \
+   overflow = (__value < __arg1);                               \
+   __value;                                                     \
+})
+
+#define addllx(a, b)                                          \
+__extension__ ({                                              \
+   ulong __arg1 = (a), __arg2 = (b), __value, __tmp = __arg1 + overflow;\
+   overflow = (__tmp < __arg1);                               \
+   __value = __tmp + __arg2;                                  \
+   overflow |= (__value < __tmp);                             \
+   __value;                                                   \
+})
+
+#define subll(a, b)                                           \
+__extension__ ({                                              \
+   ulong __arg1 = (a), __arg2 = (b);                          \
+   overflow = (__arg2 > __arg1);                              \
+   __arg1 - __arg2;                                           \
+})
+
+#define subllx(a, b)                                  \
+__extension__ ({                                      \
+   ulong __arg1 = (a), __arg2 = (b), __value, __tmp = __arg1 - overflow;\
+   overflow = (__arg1 < overflow);                    \
+   __value = __tmp - __arg2;                          \
+   overflow |= (__arg2 > __tmp);                      \
+   __value;                                           \
+})
+
+#else /* __GNUC__ */
+
+INLINE long
+addll(ulong x, ulong y)
+{
+  const ulong z = x+y;
+  overflow=(z<x);
+  return (long) z;
+}
+
+INLINE long
+addllx(ulong x, ulong y)
+{
+  const ulong z = x+y+overflow;
+  overflow = (z<x || (z==x && overflow));
+  return (long) z;
+}
+
+INLINE long
+subll(ulong x, ulong y)
+{
+  const ulong z = x-y;
+  overflow = (z>x);
+  return (long) z;
+}
+
+INLINE long
+subllx(ulong x, ulong y)
+{
+  const ulong z = x-y-overflow;
+  overflow = (z>x || (z==x && overflow));
+  return (long) z;
+}
+
+#endif /* __GNUC__ */
+
+#endif
diff --git a/src/kernel/none/asm0.h b/src/kernel/none/asm0.h
new file mode 100644
index 0000000..b5bc691
--- /dev/null
+++ b/src/kernel/none/asm0.h
@@ -0,0 +1,16 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*
+NOASM addll mulll bfffo divll
+*/
diff --git a/src/kernel/none/bfffo.h b/src/kernel/none/bfffo.h
new file mode 100644
index 0000000..2ca3f35
--- /dev/null
+++ b/src/kernel/none/bfffo.h
@@ -0,0 +1,64 @@
+#line 2 "../src/kernel/none/bfffo.h"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#if !defined(INLINE)
+extern int  bfffo(ulong x);
+#else
+
+#if defined(__GNUC__) && !defined(DISABLE_INLINE)
+
+#ifdef LONG_IS_64BIT
+#  define bfffo(x) \
+__extension__ ({ \
+  static int __bfffo_tabshi[16]={4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0};\
+  int __value = BITS_IN_LONG - 4; \
+  ulong __arg1=(x); \
+  if (__arg1 & ~0xffffffffUL) {__value -= 32; __arg1 >>= 32;}\
+  if (__arg1 & ~0xffffUL) {__value -= 16; __arg1 >>= 16;} \
+  if (__arg1 & ~0x00ffUL) {__value -= 8; __arg1 >>= 8;} \
+  if (__arg1 & ~0x000fUL) {__value -= 4; __arg1 >>= 4;} \
+  __value + __bfffo_tabshi[__arg1]; \
+})
+#else
+#  define bfffo(x) \
+__extension__ ({ \
+  static int __bfffo_tabshi[16]={4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0};\
+  int __value = BITS_IN_LONG - 4; \
+  ulong __arg1=(x); \
+  if (__arg1 & ~0xffffUL) {__value -= 16; __arg1 >>= 16;} \
+  if (__arg1 & ~0x00ffUL) {__value -= 8; __arg1 >>= 8;} \
+  if (__arg1 & ~0x000fUL) {__value -= 4; __arg1 >>= 4;} \
+  __value + __bfffo_tabshi[__arg1]; \
+})
+#endif
+
+#else
+
+INLINE int
+bfffo(ulong x)
+{
+  static int tabshi[16]={4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0};
+  int value = BITS_IN_LONG - 4;
+  ulong arg1=x;
+#ifdef LONG_IS_64BIT
+  if (arg1 & ~0xffffffffUL) {value -= 32; arg1 >>= 32;}
+#endif
+  if (arg1 & ~0xffffUL) {value -= 16; arg1 >>= 16;}
+  if (arg1 & ~0x00ffUL) {value -= 8; arg1 >>= 8;}
+  if (arg1 & ~0x000fUL) {value -= 4; arg1 >>= 4;}
+  return value + tabshi[arg1];
+}
+#endif
+
+#endif
diff --git a/src/kernel/none/cmp.c b/src/kernel/none/cmp.c
new file mode 100644
index 0000000..3e11ca0
--- /dev/null
+++ b/src/kernel/none/cmp.c
@@ -0,0 +1,137 @@
+#line 2 "../src/kernel/none/cmp.c"
+/* Copyright (C) 2002-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+
+/********************************************************************/
+/**                                                                **/
+/**                      Comparison routines                       **/
+/**                                                                **/
+/********************************************************************/
+
+/*They depend on cmpiispec and equaliispec in mp.c*/
+
+int
+equalii(GEN x, GEN y)
+{
+  if ((x[1] & (LGBITS|SIGNBITS)) != (y[1] & (LGBITS|SIGNBITS))) return 0;
+  return equaliispec(x+2, y+2, lgefint(x)-2, lgefint(y)-2);
+}
+
+int
+cmpii(GEN x, GEN y)
+{
+  const long sx = signe(x), sy = signe(y);
+  if (sx<sy) return -1;
+  if (sx>sy) return 1;
+  if (!sx) return 0;
+  if (sx>0)
+    return cmpiispec(x+2, y+2, lgefint(x)-2, lgefint(y)-2);
+  else
+    return -cmpiispec(x+2, y+2, lgefint(x)-2, lgefint(y)-2);
+}
+
+int
+equalrr(GEN x, GEN y)
+{
+  long lx, ly, i;
+
+  if (!signe(x)) return signe(y) == 0; /* all zeroes are equal */
+  if (x[1] != y[1]) return 0; /* includes signe(y) = 0 */
+
+  lx = lg(x);
+  ly = lg(y);
+  if (lx < ly)
+  {
+    i=2; while (i<lx && x[i]==y[i]) i++;
+    if (i<lx) return 0;
+    for (; i < ly; i++) if (y[i]) return 0;
+  }
+  else
+  {
+    i=2; while (i<ly && x[i]==y[i]) i++;
+    if (i<ly) return 0;
+    for (; i < lx; i++) if (x[i]) return 0;
+  }
+  return 1;
+}
+
+int
+cmprr(GEN x, GEN y)
+{
+  const long sx = signe(x), sy = signe(y);
+  long ex,ey,lx,ly,lz,i;
+
+  if (sx<sy) return -1;
+  if (sx>sy) return 1;
+  if (!sx) return 0;
+
+  ex=expo(x); ey=expo(y);
+  if (ex>ey) return sx;
+  if (ex<ey) return -sx;
+
+  lx=lg(x); ly=lg(y); lz = (lx<ly)?lx:ly;
+  i=2; while (i<lz && x[i]==y[i]) i++;
+  if (i<lz) return ((ulong)x[i] > (ulong)y[i]) ? sx : -sx;
+  if (lx>=ly)
+  {
+    while (i<lx && !x[i]) i++;
+    return (i==lx) ? 0 : sx;
+  }
+  while (i<ly && !y[i]) i++;
+  return (i==ly) ? 0 : -sx;
+}
+
+/* x and y are integers. Return 1 if |x| == |y|, 0 otherwise */
+int
+absi_equal(GEN x, GEN y)
+{
+  if (!signe(x)) return !signe(y);
+  if (!signe(y)) return 0;
+  return equaliispec(x+2, y+2, lgefint(x)-2, lgefint(y)-2);
+}
+
+/* x and y are integers. Return sign(|x| - |y|) */
+int
+absi_cmp(GEN x, GEN y)
+{
+  if (!signe(x)) return signe(y)? -1: 0;
+  if (!signe(y)) return 1;
+  return cmpiispec(x+2, y+2, lgefint(x)-2, lgefint(y)-2);
+}
+
+/* x and y are reals. Return sign(|x| - |y|) */
+int
+absr_cmp(GEN x, GEN y)
+{
+  long ex,ey,lx,ly,lz,i;
+
+  if (!signe(x)) return signe(y)? -1: 0;
+  if (!signe(y)) return 1;
+
+  ex=expo(x); ey=expo(y);
+  if (ex>ey) return  1;
+  if (ex<ey) return -1;
+
+  lx=lg(x); ly=lg(y); lz = (lx<ly)?lx:ly;
+  i=2; while (i<lz && x[i]==y[i]) i++;
+  if (i<lz) return ((ulong)x[i] > (ulong)y[i])? 1: -1;
+  if (lx>=ly)
+  {
+    while (i<lx && !x[i]) i++;
+    return (i==lx)? 0: 1;
+  }
+  while (i<ly && !y[i]) i++;
+  return (i==ly)? 0: -1;
+}
+
diff --git a/src/kernel/none/divll.h b/src/kernel/none/divll.h
new file mode 100644
index 0000000..1c896bc
--- /dev/null
+++ b/src/kernel/none/divll.h
@@ -0,0 +1,158 @@
+#line 2 "../src/kernel/none/divll.h"
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file originally adapted from gmp-3.1.1 (from T. Granlund), files
+ * longlong.h and gmp-e_IMPL.h
+
+  Copyright (C) 2000 Free Software Foundation, Inc. */
+
+#undef  LOCAL_HIREMAINDER
+#define LOCAL_HIREMAINDER
+extern ulong hiremainder;
+
+#if !defined(INLINE)
+extern long divll(ulong x, ulong y);
+#else
+
+#define __GLUE(hi, lo) (((hi) << BITS_IN_HALFULONG) | (lo))
+#define __SPLIT(a, b, c) b = HIGHWORD(a); c = LOWWORD(a)
+#define __LDIV(a, b, q, r) q = a / b; r = a - q*b
+extern ulong hiremainder;
+
+/* divide (hiremainder * 2^BITS_IN_LONG + n0) by d; assume hiremainder < d.
+ * Return quotient, set hiremainder to remainder */
+
+#if defined(__GNUC__) && !defined(DISABLE_INLINE)
+#undef LOCAL_HIREMAINDER
+#define LOCAL_HIREMAINDER register ulong hiremainder
+
+#define divll(n0, d)                                                    \
+__extension__ ({                                                        \
+  ulong __d1, __d0, __q1, __q0, __r1, __r0, __m, __n1, __n0;            \
+  ulong __k, __d;                                                       \
+                                                                        \
+  __n1 = hiremainder; __n0 = n0; __d = d;                               \
+  if (__n1 == 0)                                                        \
+  { /* Only one division needed */                                      \
+    __LDIV(__n0, __d, __q1, hiremainder);                               \
+  }                                                                     \
+  else if (__d < LOWMASK)                                               \
+  { /* Two half-word divisions  */                                      \
+    __n1 = __GLUE(__n1, HIGHWORD(__n0));                                \
+    __LDIV(__n1, __d, __q1, __r1);                                      \
+    __n1 = __GLUE(__r1,  LOWWORD(__n0));                                \
+    __LDIV(__n1, __d, __q0, hiremainder);                               \
+    __q1 = __GLUE(__q1, __q0);                                          \
+  }                                                                     \
+  else                                                                  \
+  { /* General case */                                                  \
+    if (__d & HIGHBIT)                                                  \
+    {                                                                   \
+      __k = 0; __SPLIT(__d, __d1, __d0);                                \
+    }                                                                   \
+    else                                                                \
+    {                                                                   \
+      __k = bfffo(__d);                                                 \
+      __n1 = (__n1 << __k) | (__n0 >> (BITS_IN_LONG - __k));            \
+      __n0 <<= __k;                                                     \
+      __d = __d << __k; __SPLIT(__d, __d1, __d0);                       \
+    }                                                                   \
+    __LDIV(__n1, __d1, __q1, __r1);                                     \
+    __m =  __q1 * __d0;                                                 \
+    __r1 = __GLUE(__r1, HIGHWORD(__n0));                                  \
+    if (__r1 < __m)                                                        \
+    {                                                                        \
+      __q1--, __r1 += __d;                                                \
+      if (__r1 >= __d) /* we didn't get carry when adding to __r1 */    \
+        if (__r1 < __m)        __q1--, __r1 += __d;                                \
+    }                                                                        \
+    __r1 -= __m;                                                        \
+    __LDIV(__r1, __d1, __q0, __r0);                                     \
+    __m =  __q0 * __d0;                                                  \
+    __r0 = __GLUE(__r0, LOWWORD(__n0));                                   \
+    if (__r0 < __m)                                                        \
+    {                                                                        \
+      __q0--, __r0 += __d;                                                \
+      if (__r0 >= __d)                                                        \
+        if (__r0 < __m)        __q0--, __r0 += __d;                                \
+    }                                                                        \
+    hiremainder = (__r0 - __m) >> __k;                                        \
+    __q1 = __GLUE(__q1, __q0);                                                 \
+  }                                                                           \
+  __q1;                                                                        \
+})
+
+#else /* __GNUC__ */
+
+INLINE long
+divll(ulong n0, ulong d)
+{
+  ulong __d1, __d0, __q1, __q0, __r1, __r0, __m, __n1, __n0;
+  ulong __k, __d;
+
+  __n1 = hiremainder; __n0 = n0; __d = d;
+
+  if (__n1 == 0)
+  { /* Only one division needed */
+    __LDIV(__n0, __d, __q1, hiremainder);
+  }
+  else if (__d < LOWMASK)
+  { /* Two half-word divisions  */
+    __n1 = __GLUE(__n1, HIGHWORD(__n0));
+    __LDIV(__n1, __d, __q1, __r1);
+    __n1 = __GLUE(__r1,  LOWWORD(__n0));
+    __LDIV(__n1, __d, __q0, hiremainder);
+    __q1 = __GLUE(__q1, __q0);
+  }
+  else
+  { /* General case */
+    if (__d & HIGHBIT)
+    {
+      __k = 0; __SPLIT(__d, __d1, __d0);
+    }
+    else
+    {
+      __k = bfffo(__d);
+      __n1 = (__n1 << __k) | (__n0 >> (BITS_IN_LONG - __k));
+      __n0 = __n0 << __k;
+      __d = __d << __k; __SPLIT(__d, __d1, __d0);
+    }
+    __LDIV(__n1, __d1, __q1, __r1);
+    __m =  __q1 * __d0;
+    __r1 = __GLUE(__r1, HIGHWORD(__n0));
+    if (__r1 < __m)
+      {
+        __q1--, __r1 += __d;
+        if (__r1 >= __d) /* we didn't get carry when adding to __r1 */
+          if (__r1 < __m) __q1--, __r1 += __d;
+      }
+    __r1 -= __m;
+    __LDIV(__r1, __d1, __q0, __r0);
+    __m =  __q0 * __d0;
+    __r0 = __GLUE(__r0, LOWWORD(__n0));
+    if (__r0 < __m)
+      {
+        __q0--, __r0 += __d;
+        if (__r0 >= __d)
+          if (__r0 < __m) __q0--, __r0 += __d;
+      }
+    hiremainder = (__r0 - __m) >> __k;
+    __q1 = __GLUE(__q1, __q0);
+  }
+  return __q1;
+}
+
+#endif /* __GNUC__ */
+
+#endif
diff --git a/src/kernel/none/gcd.c b/src/kernel/none/gcd.c
new file mode 100644
index 0000000..19dd756
--- /dev/null
+++ b/src/kernel/none/gcd.c
@@ -0,0 +1,122 @@
+#line 2 "../src/kernel/none/gcd.c"
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* assume y > x > 0. return y mod x */
+static ulong
+resiu(GEN y, ulong x)
+{
+  long i, ly = lgefint(y);
+  LOCAL_HIREMAINDER;
+
+  hiremainder = 0;
+  for (i=2; i<ly; i++) (void)divll(y[i],x);
+  return hiremainder;
+}
+
+/* Assume x>y>0, both of them odd. return x-y if x=y mod 4, x+y otherwise */
+static void
+gcd_plus_minus(GEN x, GEN y, GEN res)
+{
+  pari_sp av = avma;
+  long lx = lgefint(x)-1;
+  long ly = lgefint(y)-1, lt,m,i;
+  GEN t;
+
+  if ((x[lx]^y[ly]) & 3) /* x != y mod 4*/
+    t = addiispec(x+2,y+2,lx-1,ly-1);
+  else
+    t = subiispec(x+2,y+2,lx-1,ly-1);
+
+  lt = lgefint(t)-1; while (!t[lt]) lt--;
+  m = vals(t[lt]); lt++;
+  if (m == 0) /* 2^32 | t */
+  {
+    for (i = 2; i < lt; i++) res[i] = t[i];
+  }
+  else if (t[2] >> m)
+  {
+    shift_right(res,t, 2,lt, 0,m);
+  }
+  else
+  {
+    lt--; t++;
+    shift_right(res,t, 2,lt, t[1],m);
+  }
+  res[1] = evalsigne(1)|evallgefint(lt);
+  avma = av;
+}
+
+/* uses modified right-shift binary algorithm now --GN 1998Jul23 */
+GEN
+gcdii(GEN a, GEN b)
+{
+  long v, w;
+  pari_sp av;
+  GEN t, p1;
+
+  switch (absi_cmp(a,b))
+  {
+    case 0: return absi(a);
+    case -1: t=b; b=a; a=t;
+  }
+  if (!signe(b)) return absi(a);
+  /* here |a|>|b|>0. Try single precision first */
+  if (lgefint(a)==3)
+    return igcduu((ulong)a[2], (ulong)b[2]);
+  if (lgefint(b)==3)
+  {
+    ulong u = resiu(a,(ulong)b[2]);
+    if (!u) return absi(b);
+    return igcduu((ulong)b[2], u);
+  }
+
+  /* larger than gcd: "avma=av" gerepile (erasing t) is valid */
+  av = avma; (void)new_chunk(lgefint(b)); /* HACK */
+  t = remii(a,b);
+  if (!signe(t)) { avma=av; return absi(b); }
+
+  a = b; b = t;
+  v = vali(a); a = shifti(a,-v); setabssign(a);
+  w = vali(b); b = shifti(b,-w); setabssign(b);
+  if (w < v) v = w;
+  switch(absi_cmp(a,b))
+  {
+    case  0: avma=av; a=shifti(a,v); return a;
+    case -1: p1=b; b=a; a=p1;
+  }
+  if (is_pm1(b)) { avma=av; return int2n(v); }
+
+  /* we have three consecutive memory locations: a,b,t.
+   * All computations are done in place */
+
+  /* a and b are odd now, and a>b>1 */
+  while (lgefint(a) > 3)
+  {
+    /* if a=b mod 4 set t=a-b, otherwise t=a+b, then strip powers of 2 */
+    /* so that t <= (a+b)/4 < a/2 */
+    gcd_plus_minus(a,b, t);
+    if (is_pm1(t)) { avma=av; return int2n(v); }
+    switch(absi_cmp(t,b))
+    {
+      case -1: p1 = a; a = b; b = t; t = p1; break;
+      case  1: p1 = a; a = t; t = p1; break;
+      case  0: avma = av; b=shifti(b,v); return b;
+    }
+  }
+  {
+    long r[] = {evaltyp(t_INT)|_evallg(3), evalsigne(1)|evallgefint(3), 0};
+    r[2] = (long) gcduodd((ulong)b[2], (ulong)a[2]);
+    avma = av; return shifti(r,v);
+  }
+}
diff --git a/src/kernel/none/gcdext.c b/src/kernel/none/gcdext.c
new file mode 100644
index 0000000..0926980
--- /dev/null
+++ b/src/kernel/none/gcdext.c
@@ -0,0 +1,211 @@
+#line 2 "../src/kernel/none/gcdext.c"
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*==================================
+ * bezout(a,b,pu,pv)
+ *==================================
+ *    Return g = gcd(a,b) >= 0, and assign GENs u,v through pointers pu,pv
+ *    such that g = u*a + v*b.
+ * Special cases:
+ *    a == b == 0 ==> pick u=1, v=0
+ *    a != 0 == b ==> keep v=0
+ *    a == 0 != b ==> keep u=0
+ *    |a| == |b| != 0 ==> keep u=0, set v=+-1
+ * Assignments through pu,pv will be suppressed when the corresponding
+ * pointer is NULL  (but the computations will happen nonetheless).
+ */
+
+GEN
+bezout(GEN a, GEN b, GEN *pu, GEN *pv)
+{
+  GEN t,u,u1,v,v1,d,d1,q,r;
+  GEN *pt;
+  pari_sp av, av1, lim;
+  long s, sa, sb;
+  ulong g;
+  ulong xu,xu1,xv,xv1;                /* Lehmer stage recurrence matrix */
+  int lhmres;                        /* Lehmer stage return value */
+
+  s = absi_cmp(a,b);
+  if (s < 0)
+  {
+    t=b; b=a; a=t;
+    pt=pu; pu=pv; pv=pt;
+  }
+  /* now |a| >= |b| */
+
+  sa = signe(a); sb = signe(b);
+  if (!sb)
+  {
+    if (pv) *pv = gen_0;
+    switch(sa)
+    {
+    case  0: if (pu) *pu = gen_0; return gen_0;
+    case  1: if (pu) *pu = gen_1; return icopy(a);
+    case -1: if (pu) *pu = gen_m1; return(negi(a));
+    }
+  }
+  if (s == 0)                        /* |a| == |b| != 0 */
+  {
+    if (pu) *pu = gen_0;
+    if (sb > 0)
+    { if (pv) *pv = gen_1; return icopy(b); }
+    else
+    { if (pv) *pv = gen_m1; return(negi(b)); }
+  }
+  /* now |a| > |b| > 0 */
+
+  if (lgefint(a) == 3)                /* single-word affair */
+  {
+    g = xxgcduu((ulong)a[2], (ulong)b[2], 0, &xu, &xu1, &xv, &xv1, &s);
+    sa = s > 0 ? sa : -sa;
+    sb = s > 0 ? -sb : sb;
+    if (pu)
+    {
+      if (xu == 0) *pu = gen_0; /* can happen when b divides a */
+      else if (xu == 1) *pu = sa < 0 ? gen_m1 : gen_1;
+      else if (xu == 2) *pu = sa < 0 ? gen_m2 : gen_2;
+      else
+      {
+        *pu = cgeti(3);
+        (*pu)[1] = evalsigne(sa)|evallgefint(3);
+        (*pu)[2] = xu;
+      }
+    }
+    if (pv)
+    {
+      if (xv == 1) *pv = sb < 0 ? gen_m1 : gen_1;
+      else if (xv == 2) *pv = sb < 0 ? gen_m2 : gen_2;
+      else
+      {
+        *pv = cgeti(3);
+        (*pv)[1] = evalsigne(sb)|evallgefint(3);
+        (*pv)[2] = xv;
+      }
+    }
+    if      (g == 1) return gen_1;
+    else if (g == 2) return gen_2;
+    else return utoipos(g);
+  }
+
+  /* general case */
+  av = avma;
+  (void)new_chunk(lgefint(b) + (lgefint(a)<<1)); /* room for u,v,gcd */
+  /* if a is significantly larger than b, calling lgcdii() is not the best
+   * way to start -- reduce a mod b first
+   */
+  if (lgefint(a) > lgefint(b))
+  {
+    d = absi(b), q = dvmdii(absi(a), d, &d1);
+    if (!signe(d1))                /* a == qb */
+    {
+      avma = av;
+      if (pu) *pu = gen_0;
+      if (pv) *pv = sb < 0 ? gen_m1 : gen_1;
+      return (icopy(d));
+    }
+    else
+    {
+      u = gen_0;
+      u1 = v = gen_1;
+      v1 = negi(q);
+    }
+    /* if this results in lgefint(d) == 3, will fall past main loop */
+  }
+  else
+  {
+    d = absi(a); d1 = absi(b);
+    u = v1 = gen_1; u1 = v = gen_0;
+  }
+  av1 = avma; lim = stack_lim(av, 1);
+
+  /* main loop is almost identical to that of invmod() */
+  while (lgefint(d) > 3 && signe(d1))
+  {
+    lhmres = lgcdii((ulong *)d, (ulong *)d1, &xu, &xu1, &xv, &xv1, ULONG_MAX);
+    if (lhmres != 0)                /* check progress */
+    {                                /* apply matrix */
+      if ((lhmres == 1) || (lhmres == -1))
+      {
+        if (xv1 == 1)
+        {
+          r = subii(d,d1); d=d1; d1=r;
+          a = subii(u,u1); u=u1; u1=a;
+          a = subii(v,v1); v=v1; v1=a;
+        }
+        else
+        {
+          r = subii(d, mului(xv1,d1)); d=d1; d1=r;
+          a = subii(u, mului(xv1,u1)); u=u1; u1=a;
+          a = subii(v, mului(xv1,v1)); v=v1; v1=a;
+        }
+      }
+      else
+      {
+        r  = subii(muliu(d,xu),  muliu(d1,xv));
+        d1 = subii(muliu(d,xu1), muliu(d1,xv1)); d = r;
+        a  = subii(muliu(u,xu),  muliu(u1,xv));
+        u1 = subii(muliu(u,xu1), muliu(u1,xv1)); u = a;
+        a  = subii(muliu(v,xu),  muliu(v1,xv));
+        v1 = subii(muliu(v,xu1), muliu(v1,xv1)); v = a;
+        if (lhmres&1) { togglesign(d);  togglesign(u);  togglesign(v); }
+        else          { togglesign(d1); togglesign(u1); togglesign(v1); }
+      }
+    }
+    if (lhmres <= 0 && signe(d1))
+    {
+      q = dvmdii(d,d1,&r);
+#ifdef DEBUG_LEHMER
+      err_printf("Full division:\n");
+      printf("  q = "); output(q); sleep (1);
+#endif
+      a = subii(u,mulii(q,u1));
+      u=u1; u1=a;
+      a = subii(v,mulii(q,v1));
+      v=v1; v1=a;
+      d=d1; d1=r;
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"bezout");
+      gerepileall(av1,6, &d,&d1,&u,&u1,&v,&v1);
+    }
+  } /* end while */
+
+  /* Postprocessing - final sprint */
+  if (signe(d1))
+  {
+    /* Assertions: lgefint(d)==lgefint(d1)==3, and
+     * gcd(d,d1) is nonzero and fits into one word
+     */
+    g = xxgcduu((ulong)(d[2]), (ulong)(d1[2]), 0, &xu, &xu1, &xv, &xv1, &s);
+    u = subii(muliu(u,xu), muliu(u1, xv));
+    v = subii(muliu(v,xu), muliu(v1, xv));
+    if (s < 0) { sa = -sa; sb = -sb; }
+    avma = av;
+    if (pu) *pu = sa < 0 ? negi(u) : icopy(u);
+    if (pv) *pv = sb < 0 ? negi(v) : icopy(v);
+    if (g == 1) return gen_1;
+    else if (g == 2) return gen_2;
+    else return utoipos(g);
+  }
+  /* get here when the final sprint was skipped (d1 was zero already).
+   * Now the matrix is final, and d contains the gcd.
+   */
+  avma = av;
+  if (pu) *pu = sa < 0 ? negi(u) : icopy(u);
+  if (pv) *pv = sb < 0 ? negi(v) : icopy(v);
+  return icopy(d);
+}
+
diff --git a/src/kernel/none/gcdll.c b/src/kernel/none/gcdll.c
new file mode 100644
index 0000000..b6f31f1
--- /dev/null
+++ b/src/kernel/none/gcdll.c
@@ -0,0 +1,1084 @@
+#line 2 "../src/kernel/none/gcdll.c"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          GCD                                      **/
+/**                                                                   **/
+/***********************************************************************/
+/* Fast ulong gcd.  Called with y odd; x can be arbitrary (but will most of
+ * the time be smaller than y). */
+
+/* Gotos are Harmful, and Programming is a Science.  E.W.Dijkstra. */
+ulong
+gcduodd(ulong x, ulong y)         /* assume y&1==1, y > 1 */
+{
+  if (!x) return y;
+  /* fix up x */
+  while (!(x&1)) x>>=1;
+  if (x==1) return 1;
+  if (x==y) return y;
+  else if (x>y) goto xislarger;/* will be rare, given how we'll use this */
+  /* loop invariants: x,y odd and distinct. */
+ yislarger:
+  if ((x^y)&2)                 /* ...01, ...11 or vice versa */
+    y=(x>>2)+(y>>2)+1;         /* ==(x+y)>>2 except it can't overflow */
+  else                         /* ...01,...01 or ...11,...11 */
+    y=(y-x)>>2;                /* now y!=0 in either case */
+  while (!(y&1)) y>>=1;        /* kill any windfall-gained powers of 2 */
+  if (y==1) return 1;          /* comparand == return value... */
+  if (x==y) return y;          /* this and the next is just one comparison */
+  else if (x<y) goto yislarger;/* else fall through to xislarger */
+
+ xislarger:                    /* same as above, seen through a mirror */
+  if ((x^y)&2)
+    x=(x>>2)+(y>>2)+1;
+  else
+    x=(x-y)>>2;                /* x!=0 */
+  while (!(x&1)) x>>=1;
+  if (x==1) return 1;
+  if (x==y) return y;
+  else if (x>y) goto xislarger;
+
+  goto yislarger;
+}
+/* Gotos are useful, and Programming is an Art.  D.E.Knuth. */
+/* PS: Of course written with Dijkstra's lessons firmly in mind... --GN */
+
+/* at least one of a or b is odd, return gcd(a,b) */
+INLINE ulong
+mygcduodd(ulong a, ulong b)
+{
+  ulong c;
+  if (b&1)
+  {
+    if (a==1 || b==1)
+      c = 1;
+    else
+     c = gcduodd(a, b);
+  }
+  else
+  {
+    if (a==1)
+      c = 1;
+    else
+      c = gcduodd(b, a);
+  }
+  return c;
+}
+
+/* modified right shift binary algorithm with at most one division */
+long
+cgcd(long a,long b)
+{
+  long v;
+  a=labs(a); if (!b) return a;
+  b=labs(b); if (!a) return b;
+  if (a>b) { a %= b; if (!a) return b; }
+  else     { b %= a; if (!b) return a; }
+  v = vals(a|b);
+  return (long)(mygcduodd((ulong)(a>>v), (ulong)(b>>v)) << v);
+}
+ulong
+ugcd(ulong a,ulong b)
+{
+  long v;
+  if (!b) return a;
+  if (!a) return b;
+  if (a>b) { a %= b; if (!a) return b; }
+  else     { b %= a; if (!b) return a; }
+  v = vals(a|b);
+  return mygcduodd(a>>v, b>>v) << v;
+}
+
+/* For gcdii(): assume a>b>0, return gcd(a,b) as a GEN */
+static GEN
+igcduu(ulong a, ulong b)
+{
+  long v;
+  a %= b; if (!a) return utoipos(b);
+  v = vals(a|b);
+  return utoipos( mygcduodd(a>>v, b>>v) << v );
+}
+
+/*Warning: overflows silently if lcm does not fit*/
+long
+clcm(long a,long b)
+{
+  long d = cgcd(a,b);
+  if (!d) return 0;
+  return d == 1? a*b: a*(b/d);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**               INTEGER EXTENDED GCD  (AND INVMOD)               **/
+/**                                                                **/
+/********************************************************************/
+
+/* GN 1998Oct25, originally developed in January 1998 under 2.0.4.alpha,
+ * in the context of trying to improve elliptic curve cryptosystem attacking
+ * algorithms.  2001Jan02 -- added bezout() functionality.
+ *
+ * Two basic ideas - (1) avoid many integer divisions, especially when the
+ * quotient is 1 (which happens more than 40% of the time).  (2) Use Lehmer's
+ * trick as modified by Jebelean of extracting a couple of words' worth of
+ * leading bits from both operands, and compute partial quotients from them
+ * as long as we can be sure of their values.  The Jebelean modifications
+ * consist in reliable inequalities from which we can decide fast whether
+ * to carry on or to return to the outer loop, and in re-shifting after the
+ * first word's worth of bits has been used up.  All of this is described
+ * in R. Lercier's these [pp148-153 & 163f.], except his outer loop isn't
+ * quite right  (the catch-up divisions needed when one partial quotient is
+ * larger than a word are missing).
+ *
+ * The API consists of invmod() and bezout() below;  the single-word routines
+ * xgcduu and xxgcduu may be called directly if desired;  lgcdii() probably
+ * doesn't make much sense out of context.
+ *
+ * The whole lot is a factor 6 .. 8 faster on word-sized operands, and asym-
+ * ptotically about a factor 2.5 .. 3, depending on processor architecture,
+ * than the naive continued-division code.  Unfortunately, thanks to the
+ * unrolled loops and all, the code is a bit lengthy.
+ */
+
+/*==================================
+ * xgcduu(d,d1,f,v,v1,s)
+ * xxgcduu(d,d1,f,u,u1,v,v1,s)
+ * rgcduu(d,d1,vmax,u,u1,v,v1,s)
+ *==================================*/
+/*
+ * Fast `final' extended gcd algorithm, acting on two ulongs.  Ideally this
+ * should be replaced with assembler versions wherever possible.  The present
+ * code essentially does `subtract, compare, and possibly divide' at each step,
+ * which is reasonable when hardware division (a) exists, (b) is a bit slowish
+ * and (c) does not depend a lot on the operand values (as on i486).  When
+ * wordsize division is in fact an assembler routine based on subtraction,
+ * this strategy may not be the most efficient one.
+ *
+ * xxgcduu() should be called with  d > d1 > 0, returns gcd(d,d1), and assigns
+ * the usual signless cont.frac. recurrence matrix to [u, u1; v, v1]  (i.e.,
+ * the product of all the [0, 1; 1 q_j] where the leftmost factor arises from
+ * the quotient of the first division step),  and the information about the
+ * implied signs to s  (-1 when an odd number of divisions has been done,
+ * 1 otherwise).  xgcduu() is exactly the same except that u,u1 are not com-
+ * puted (and not returned, of course).
+ *
+ * The input flag f should be set to 1 if we know in advance that gcd(d,d1)==1
+ * (so we can stop the chain division one step early:  as soon as the remainder
+ * equals 1).  Use this when you intend to use only what would be v, or only
+ * what would be u and v, after that final division step, but not u1 and v1.
+ * With the flag in force and thus without that final step, the interesting
+ * quantity/ies will still sit in [u1 and] v1, of course.
+ *
+ * For computing the inverse of a single-word INTMOD known to exist, pass f=1
+ * to xgcduu(), and obtain the result from s and v1.  (The routine does the
+ * right thing when d1==1 already.)  For finishing a multiword modinv known
+ * to exist, pass f=1 to xxgcduu(), and multiply the returned matrix  (with
+ * properly adjusted signs)  onto the values v' and v1' previously obtained
+ * from the multiword division steps.  Actually, just take the scalar product
+ * of [v',v1'] with [u1,-v1], and change the sign if s==-1.  (If the final
+ * step had been carried out, it would be [-u,v], and s would also change.)
+ * For reducing a rational number to lowest terms, pass f=0 to xgcduu().
+ * Finally, f=0 with xxgcduu() is useful for Bezout computations.
+ * [Harrumph.  In the above prescription, the sign turns out to be precisely
+ * wrong.]
+ * (It is safe for invmod() to call xgcduu() with f=1, because f&1 doesn't
+ * make a difference when gcd(d,d1)>1.  The speedup is negligible.)
+ *
+ * In principle, when gcd(d,d1) is known to be 1, it is straightforward to
+ * recover the final u,u1 given only v,v1 and s.  However, it probably isn't
+ * worthwhile, as it trades a few multiplications for a division.
+ *
+ * Note that these routines do not know and do not need to know about the
+ * PARI stack.
+ *
+ * Added 2001Jan15:
+ * rgcduu() is a variant of xxgcduu() which does not have f  (the effect is
+ * that of f=0),  but instead has a ulong vmax parameter, for use in rational
+ * reconstruction below.  It returns when v1 exceeds vmax;  v will never
+ * exceed vmax.  (vmax=0 is taken as a synonym of ULONG_MAX i.e. unlimited,
+ * in which case rgcduu behaves exactly like xxgcduu with f=0.)  The return
+ * value of rgcduu() is typically meaningless;  the interesting part is the
+ * matrix.
+ */
+
+ulong
+xgcduu(ulong d, ulong d1, int f, ulong* v, ulong* v1, long *s)
+{
+  ulong xv,xv1, xs, q,res;
+  LOCAL_HIREMAINDER;
+
+  /* The above blurb contained a lie.  The main loop always stops when d1
+   * has become equal to 1.  If (d1 == 1 && !(f&1)) after the loop, we do
+   * the final `division' of d by 1 `by hand' as it were.
+   *
+   * The loop has already been unrolled once.  Aggressive optimization could
+   * well lead to a totally unrolled assembler version...
+   *
+   * On modern x86 architectures, this loop is a pig anyway.  The division
+   * instruction always puts its result into the same pair of registers, and
+   * we always want to use one of them straight away, so pipeline performance
+   * will suck big time.  An assembler version should probably do a first loop
+   * computing and storing all the quotients -- their number is bounded in
+   * advance -- and then assembling the matrix in a second pass.  On other
+   * architectures where we can cycle through four or so groups of registers
+   * and exploit a fast ALU result-to-operand feedback path, this is much less
+   * of an issue.  (Intel sucks.  See http://www.x86.org/ ...)
+   */
+  xs = res = 0;
+  xv = 0UL; xv1 = 1UL;
+  while (d1 > 1UL)
+  {
+    d -= d1;                        /* no need to use subll */
+    if (d >= d1)
+    {
+      hiremainder = 0; q = 1 + divll(d,d1); d = hiremainder;
+      xv += q * xv1;
+    }
+    else
+      xv += xv1;
+                                /* possible loop exit */
+    if (d <= 1UL) { xs=1; break; }
+                                /* repeat with inverted roles */
+    d1 -= d;
+    if (d1 >= d)
+    {
+      hiremainder = 0; q = 1 + divll(d1,d); d1 = hiremainder;
+      xv1 += q * xv;
+    }
+    else
+      xv1 += xv;
+  } /* while */
+
+  if (!(f&1))                        /* division by 1 postprocessing if needed */
+  {
+    if (xs && d==1)
+    { xv1 += d1 * xv; xs = 0; res = 1UL; }
+    else if (!xs && d1==1)
+    { xv += d * xv1; xs = 1; res = 1UL; }
+  }
+
+  if (xs)
+  {
+    *s = -1; *v = xv1; *v1 = xv;
+    return (res ? res : (d==1 ? 1UL : d1));
+  }
+  else
+  {
+    *s = 1; *v = xv; *v1 = xv1;
+    return (res ? res : (d1==1 ? 1UL : d));
+  }
+}
+
+
+ulong
+xxgcduu(ulong d, ulong d1, int f,
+        ulong* u, ulong* u1, ulong* v, ulong* v1, long *s)
+{
+  ulong xu,xu1, xv,xv1, xs, q,res;
+  LOCAL_HIREMAINDER;
+
+  xs = res = 0;
+  xu = xv1 = 1UL;
+  xu1 = xv = 0UL;
+  while (d1 > 1UL)
+  {
+    d -= d1;                        /* no need to use subll */
+    if (d >= d1)
+    {
+      hiremainder = 0; q = 1 + divll(d,d1); d = hiremainder;
+      xv += q * xv1;
+      xu += q * xu1;
+    }
+    else
+    { xv += xv1; xu += xu1; }
+                                /* possible loop exit */
+    if (d <= 1UL) { xs=1; break; }
+                                /* repeat with inverted roles */
+    d1 -= d;
+    if (d1 >= d)
+    {
+      hiremainder = 0; q = 1 + divll(d1,d); d1 = hiremainder;
+      xv1 += q * xv;
+      xu1 += q * xu;
+    }
+    else
+    { xv1 += xv; xu1 += xu; }
+  } /* while */
+
+  if (!(f&1))                        /* division by 1 postprocessing if needed */
+  {
+    if (xs && d==1)
+    {
+      xv1 += d1 * xv;
+      xu1 += d1 * xu;
+      xs = 0; res = 1UL;
+    }
+    else if (!xs && d1==1)
+    {
+      xv += d * xv1;
+      xu += d * xu1;
+      xs = 1; res = 1UL;
+    }
+  }
+
+  if (xs)
+  {
+    *s = -1; *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+    return (res ? res : (d==1 ? 1UL : d1));
+  }
+  else
+  {
+    *s = 1; *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+    return (res ? res : (d1==1 ? 1UL : d));
+  }
+}
+
+ulong
+rgcduu(ulong d, ulong d1, ulong vmax,
+       ulong* u, ulong* u1, ulong* v, ulong* v1, long *s)
+{
+  ulong xu,xu1, xv,xv1, xs, q, res=0;
+  int f = 0;
+  LOCAL_HIREMAINDER;
+
+  if (vmax == 0) vmax = ULONG_MAX;
+  xs = res = 0;
+  xu = xv1 = 1UL;
+  xu1 = xv = 0UL;
+  while (d1 > 1UL)
+  {
+    d -= d1;                        /* no need to use subll */
+    if (d >= d1)
+    {
+      hiremainder = 0; q = 1 + divll(d,d1); d = hiremainder;
+      xv += q * xv1;
+      xu += q * xu1;
+    }
+    else
+    { xv += xv1; xu += xu1; }
+                                /* possible loop exit */
+    if (xv > vmax) { f=xs=1; break; }
+    if (d <= 1UL) { xs=1; break; }
+                                /* repeat with inverted roles */
+    d1 -= d;
+    if (d1 >= d)
+    {
+      hiremainder = 0; q = 1 + divll(d1,d); d1 = hiremainder;
+      xv1 += q * xv;
+      xu1 += q * xu;
+    }
+    else
+    { xv1 += xv; xu1 += xu; }
+                                /* possible loop exit */
+    if (xv1 > vmax) { f=1; break; }
+  } /* while */
+
+  if (!(f&1))                        /* division by 1 postprocessing if needed */
+  {
+    if (xs && d==1)
+    {
+      xv1 += d1 * xv;
+      xu1 += d1 * xu;
+      xs = 0; res = 1UL;
+    }
+    else if (!xs && d1==1)
+    {
+      xv += d * xv1;
+      xu += d * xu1;
+      xs = 1; res = 1UL;
+    }
+  }
+
+  if (xs)
+  {
+    *s = -1; *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+    return (res ? res : (d==1 ? 1UL : d1));
+  }
+  else
+  {
+    *s = 1; *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+    return (res ? res : (d1==1 ? 1UL : d));
+  }
+}
+
+/*==================================
+ * cbezout(a,b,uu,vv)
+ *==================================
+ * Same as bezout() but for C longs.
+ *    Return g = gcd(a,b) >= 0, and assign longs u,v through pointers uu,vv
+ *    such that g = u*a + v*b.
+ * Special cases:
+ *    a == b == 0 ==> pick u=1, v=0 (and return 1, surprisingly)
+ *    a != 0 == b ==> keep v=0
+ *    a == 0 != b ==> keep u=0
+ *    |a| == |b| != 0 ==> keep u=0, set v=+-1
+ * Assignments through uu,vv happen unconditionally;  non-NULL pointers
+ * _must_ be used.
+ */
+long
+cbezout(long a,long b,long *uu,long *vv)
+{
+  long s,*t;
+  ulong d = labs(a), d1 = labs(b);
+  ulong r,u,u1,v,v1;
+
+#ifdef DEBUG_CBEZOUT
+  err_printf("> cbezout(%ld,%ld,%p,%p)\n", a, b, (void *)uu, (void *)vv);
+#endif
+  if (!b)
+  {
+    *vv=0L;
+    if (!a)
+    {
+      *uu=1L;
+#ifdef DEBUG_CBEZOUT
+      err_printf("< %ld (%ld, %ld)\n", 1L, *uu, *vv);
+#endif
+      return 0L;
+    }
+    *uu = a < 0 ? -1L : 1L;
+#ifdef DEBUG_CBEZOUT
+    err_printf("< %ld (%ld, %ld)\n", (long)d, *uu, *vv);
+#endif
+    return (long)d;
+  }
+  else if (!a || (d == d1))
+  {
+    *uu = 0L; *vv = b < 0 ? -1L : 1L;
+#ifdef DEBUG_CBEZOUT
+    err_printf("< %ld (%ld, %ld)\n", (long)d1, *uu, *vv);
+#endif
+    return (long)d1;
+  }
+  else if (d == 1)                /* frequently used by nfinit */
+  {
+    *uu = a; *vv = 0L;
+#ifdef DEBUG_CBEZOUT
+    err_printf("< %ld (%ld, %ld)\n", 1L, *uu, *vv);
+#endif
+    return 1L;
+  }
+  else if (d < d1)
+  {
+/* bug in gcc-2.95.3:
+ * s = a; a = b; b = s; produces wrong result a = b. This is OK:  */
+    { long _x = a; a = b; b = _x; }        /* in order to keep the right signs */
+    r = d; d = d1; d1 = r;
+    t = uu; uu = vv; vv = t;
+#ifdef DEBUG_CBEZOUT
+    err_printf("  swapping\n");
+#endif
+  }
+  /* d > d1 > 0 */
+  r = xxgcduu(d, d1, 0, &u, &u1, &v, &v1, &s);
+  if (s < 0)
+  {
+    *uu = a < 0 ? (long)u : -(long)u;
+    *vv = b < 0 ? -(long)v : (long)v;
+  }
+  else
+  {
+    *uu = a < 0 ? -(long)u : (long)u;
+    *vv = b < 0 ? (long)v : -(long)v;
+  }
+#ifdef DEBUG_CBEZOUT
+  err_printf("< %ld (%ld, %ld)\n", (long)r, *uu, *vv);
+#endif
+  return (long)r;
+}
+
+/*==================================
+ * lgcdii(d,d1,u,u1,v,v1,vmax)
+ *==================================*/
+/* Lehmer's partial extended gcd algorithm, acting on two t_INT GENs.
+ *
+ * Tries to determine, using the leading 2*BITS_IN_LONG significant bits of d
+ * and a quantity of bits from d1 obtained by a shift of the same displacement,
+ * as many partial quotients of d/d1 as possible, and assigns to [u,u1;v,v1]
+ * the product of all the [0, 1; 1, q_j] thus obtained, where the leftmost
+ * factor arises from the quotient of the first division step.
+ *
+ * For use in rational reconstruction, input param vmax can be given a
+ * nonzero value.  In this case, we will return early as soon as v1 > vmax
+ * (i.e. it is guaranteed that v <= vmax). --2001Jan15
+ *
+ * MUST be called with  d > d1 > 0, and with  d  occupying more than one
+ * significant word  (if it doesn't, the caller has no business with us;
+ * he/she/it should use xgcduu() instead).  Returns the number of reduction/
+ * swap steps carried out, possibly zero, or under certain conditions minus
+ * that number.  When the return value is nonzero, the caller should use the
+ * returned recurrence matrix to update its own copies of d,d1.  When the
+ * return value is non-positive, and the latest remainder after updating
+ * turns out to be nonzero, the caller should at once attempt a full division,
+ * rather than first trying lgcdii() again -- this typically happens when we
+ * are about to encounter a quotient larger than half a word.  (This is not
+ * detected infallibly -- after a positive return value, it is perfectly
+ * possible that the next stage will end up needing a full division.  After
+ * a negative return value, however, this is certain, and should be acted
+ * upon.)
+ *
+ * (The sign information, for which xgcduu() has its return argument s, is now
+ * implicit in the LSB of our return value, and the caller may take advantage
+ * of the fact that a return value of +-1 implies u==0,u1==v==1  [only v1 pro-
+ * vides interesting information in this case].  One might also use the fact
+ * that if the return value is +-2, then u==1, but this is rather marginal.)
+ *
+ * If it was not possible to determine even the first quotient, either because
+ * we're too close to an integer quotient or because the quotient would be
+ * larger than one word  (if the `leading digit' of d1 after shifting is all
+ * zeros),  we return 0 and do not bother to assign anything to the last four
+ * args.
+ *
+ * The division chain might (sometimes) even run to completion.  It will be
+ * up to the caller to detect this case.
+ *
+ * This routine does _not_ change d or d1;  this will also be up to the caller.
+ *
+ * Note that this routine does not know and does not need to know about the
+ * PARI stack.
+ */
+/*#define DEBUG_LEHMER 1 */
+int
+lgcdii(ulong* d, ulong* d1,
+       ulong* u, ulong* u1, ulong* v, ulong* v1,
+       ulong vmax)
+{
+  /* Strategy:  (1) Extract/shift most significant bits.  We assume that d
+   * has at least two significant words, but we can cope with a one-word d1.
+   * Let dd,dd1 be the most significant dividend word and matching part of the
+   * divisor.
+   * (2) Check for overflow on the first division.  For our purposes, this
+   * happens when the upper half of dd1 is zero.  (Actually this is detected
+   * during extraction.)
+   * (3) Get a fix on the first quotient.  We compute q = floor(dd/dd1), which
+   * is an upper bound for floor(d/d1), and which gives the true value of the
+   * latter if (and-almost-only-if) the remainder dd' = dd-q*dd1 is >= q.
+   * (If it isn't, we give up.  This is annoying because the subsequent full
+   * division will repeat some work already done, but it happens very infre-
+   * quently.  Doing the extra-bit-fetch in this case would be awkward.)
+   * (4) Finish initializations.
+   *
+   * The remainder of the action is comparatively boring... The main loop has
+   * been unrolled once  (so we don't swap things and we can apply Jebelean's
+   * termination conditions which alternatingly take two different forms during
+   * successive iterations).  When we first run out of sufficient bits to form
+   * a quotient, and have an extra word of each operand, we pull out two whole
+   * word's worth of dividend bits, and divisor bits of matching significance;
+   * to these we apply our partial matrix (disregarding overflow because the
+   * result mod 2^(2*BITS_IN_LONG) will in fact give the correct values), and
+   * re-extract one word's worth of the current dividend and a matching amount
+   * of divisor bits.  The affair will normally terminate with matrix entries
+   * just short of a whole word.  (We terminate the inner loop before these can
+   * possibly overflow.)
+   */
+  ulong dd,dd1,ddlo,dd1lo, sh,shc;        /* `digits', shift count */
+  ulong xu,xu1, xv,xv1, q,res;        /* recurrences, partial quotient, count */
+  ulong tmp0,tmp1,tmp2,tmpd,tmpu,tmpv; /* temps */
+  ulong dm1,dm2,d1m1,d1m2;
+  long ld, ld1, lz;                /* t_INT effective lengths */
+  int skip = 0;                        /* a boolean flag */
+  LOCAL_OVERFLOW;
+  LOCAL_HIREMAINDER;
+
+#ifdef DEBUG_LEHMER
+  voir(d, -1); voir(d1, -1);
+#endif
+  /* following is just for convenience: vmax==0 means no bound */
+  if (vmax == 0) vmax = ULONG_MAX;
+  ld = lgefint(d); ld1 = lgefint(d1); lz = ld - ld1; /* >= 0 */
+  if (lz > 1) return 0;                /* rare, quick and desperate exit */
+
+  d = int_MSW(d); d1 = int_MSW(d1);                /* point at the leading `digits' */
+  dm1 = *int_precW(d); d1m1 = *int_precW(d1);
+  dm2 = *int_precW(int_precW(d)); d1m2 = *int_precW(int_precW(d1));
+  dd1lo = 0;                        /* unless we find something better */
+  sh = bfffo(*d);                /* obtain dividend left shift count */
+
+  if (sh)
+  {                                /* do the shifting */
+    shc = BITS_IN_LONG - sh;
+    if (lz)
+    {                                /* dividend longer than divisor */
+      dd1 = (*d1 >> shc);
+      if (!(HIGHMASK & dd1)) return 0;  /* overflow detected */
+      if (ld1 > 3)
+        dd1lo = (*d1 << sh) + (d1m1 >> shc);
+      else
+        dd1lo = (*d1 << sh);
+    }
+    else
+    {                                /* dividend and divisor have the same length */
+      dd1 = (*d1 << sh);
+      if (!(HIGHMASK & dd1)) return 0;
+      if (ld1 > 3)
+      {
+        dd1 += (d1m1 >> shc);
+        if (ld1 > 4)
+          dd1lo = (d1m1 << sh) + (d1m2 >> shc);
+        else
+          dd1lo = (d1m1 << sh);
+      }
+    }
+    /* following lines assume d to have 2 or more significant words */
+    dd = (*d << sh) + (dm1 >> shc);
+    if (ld > 4)
+      ddlo = (dm1 << sh) + (dm2 >> shc);
+    else
+      ddlo = (dm1 << sh);
+  }
+  else
+  {                                /* no shift needed */
+    if (lz) return 0;                /* div'd longer than div'r: o'flow automatic */
+    dd1 = *d1;
+    if (!(HIGHMASK & dd1)) return 0;
+    if(ld1 > 3) dd1lo = d1m1;
+    /* assume again that d has another significant word */
+    dd = *d; ddlo = dm1;
+  }
+#ifdef DEBUG_LEHMER
+  err_printf("  %lx:%lx, %lx:%lx\n", dd, ddlo, dd1, dd1lo);
+#endif
+
+  /* First subtraction/division stage.  (If a subtraction initially suffices,
+   * we don't divide at all.)  If a Jebelean condition is violated, and we
+   * can't fix it even by looking at the low-order bits in ddlo,dd1lo, we
+   * give up and ask for a full division.  Otherwise we commit the result,
+   * possibly deciding to re-shift immediately afterwards.
+   */
+  dd -= dd1;
+  if (dd < dd1)
+  {                                /* first quotient known to be == 1 */
+    xv1 = 1UL;
+    if (!dd)                        /* !(Jebelean condition), extraspecial case */
+    {                                /* note this can actually happen...  Now
+                                     * q==1 is known, but we underflow already.
+                                 * OTOH we've just shortened d by a whole word.
+                                 * Thus we feel pleased with ourselves and
+                                 * return.  (The re-shift code below would
+                                 * do so anyway.) */
+      *u = 0; *v = *u1 = *v1 = 1UL;
+      return -1;                /* Next step will be a full division. */
+    } /* if !(Jebelean) then */
+  }
+  else
+  {                                /* division indicated */
+    hiremainder = 0;
+    xv1 = 1 + divll(dd, dd1);        /* xv1: alternative spelling of `q', here ;) */
+    dd = hiremainder;
+    if (dd < xv1)                /* !(Jebelean cond'), non-extra special case */
+    {                                /* Attempt to complete the division using the
+                                 * less significant bits, before skipping right
+                                 * past the 1st loop to the reshift stage. */
+      ddlo = subll(ddlo, mulll(xv1, dd1lo));
+      dd = subllx(dd, hiremainder);
+
+      /* If we now have an overflow, q was _certainly_ too large.  Thanks to
+       * our decision not to get here unless the original dd1 had bits set in
+       * the upper half of the word, however, we now do know that the correct
+       * quotient is in fact q-1.  Adjust our data accordingly. */
+      if (overflow)
+      {
+        xv1--;
+        ddlo = addll(ddlo,dd1lo);
+        dd = addllx(dd,dd1);        /* overflows again which cancels the borrow */
+        /* ...and fall through to skip=1 below */
+      }
+      else
+      /* Test Jebelean condition anew, at this point using _all_ the extracted
+       * bits we have.  This is clutching at straws; we have a more or less
+       * even chance of succeeding this time.  Note that if we fail, we really
+       * do not know whether the correct quotient would have been q or some
+       * smaller value. */
+        if (!dd && ddlo < xv1) return 0;
+
+      /* Otherwise, we now know that q is correct, but we cannot go into the
+       * 1st loop.  Raise a flag so we'll remember to skip past the loop.
+       * Get here also after the q-1 adjustment case. */
+      skip = 1;
+    } /* if !(Jebelean) then */
+  }
+  res = 1;
+#ifdef DEBUG_LEHMER
+  err_printf("  q = %ld, %lx, %lx\n", xv1, dd1, dd);
+#endif
+  if (xv1 > vmax)
+  {                                /* gone past the bound already */
+    *u = 0UL; *u1 = 1UL; *v = 1UL; *v1 = xv1;
+    return res;
+  }
+  xu = 0UL; xv = xu1 = 1UL;
+
+  /* Some invariants from here across the first loop:
+   *
+   * At this point, and again after we are finished with the first loop and
+   * subsequent conditional, a division and the associated update of the
+   * recurrence matrix have just been carried out completely.  The matrix
+   * xu,xu1;xv,xv1 has been initialized (or updated, possibly with permuted
+   * columns), and the current remainder == next divisor (dd at the moment)
+   * is nonzero (it might be zero here, but then skip will have been set).
+   *
+   * After the first loop, or when skip is set already, it will also be the
+   * case that there aren't sufficiently many bits to continue without re-
+   * shifting.  If the divisor after reshifting is zero, or indeed if it
+   * doesn't have more than half a word's worth of bits, we will have to
+   * return at that point.  Otherwise, we proceed into the second loop.
+   *
+   * Furthermore, when we reach the re-shift stage, dd:ddlo and dd1:dd1lo will
+   * already reflect the result of applying the current matrix to the old
+   * ddorig:ddlo and dd1orig:dd1lo.  (For the first iteration above, this
+   * was easy to achieve, and we didn't even need to peek into the (now
+   * no longer existent!) saved words.  After the loop, we'll stop for a
+   * moment to merge in the ddlo,dd1lo contributions.)
+   *
+   * Note that after the first division, even an a priori quotient of 1 cannot
+   * be trusted until we've checked Jebelean's condition -- it cannot be too
+   * large, of course, but it might be too small.
+   */
+
+  if (!skip)
+  {
+    for(;;)
+    {
+      /* First half of loop divides dd into dd1, and leaves the recurrence
+       * matrix xu,...,xv1 groomed the wrong way round (xu,xv will be the newer
+       * entries) when successful. */
+      tmpd = dd1 - dd;
+      if (tmpd < dd)
+      {                                /* quotient suspected to be 1 */
+#ifdef DEBUG_LEHMER
+        q = 1;
+#endif
+        tmpu = xu + xu1;        /* cannot overflow -- everything bounded by
+                                 * the original dd during first loop */
+        tmpv = xv + xv1;
+      }
+      else
+      {                                /* division indicated */
+        hiremainder = 0;
+        q = 1 + divll(tmpd, dd);
+        tmpd = hiremainder;
+        tmpu = xu + q*xu1;        /* can't overflow, but may need to be undone */
+        tmpv = xv + q*xv1;
+      }
+
+      tmp0 = addll(tmpv, xv1);
+      if ((tmpd < tmpu) || overflow ||
+          (dd - tmpd < tmp0))        /* !(Jebelean cond.) */
+        break;                        /* skip ahead to reshift stage */
+      else
+      {                                /* commit dd1, xu, xv */
+        res++;
+        dd1 = tmpd; xu = tmpu; xv = tmpv;
+#ifdef DEBUG_LEHMER
+        err_printf("  q = %ld, %lx, %lx [%lu,%lu;%lu,%lu]\n",
+                   q, dd, dd1, xu1, xu, xv1, xv);
+#endif
+        if (xv > vmax)
+        {                        /* time to return */
+          *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+          return res;
+        }
+      }
+
+      /* Second half of loop divides dd1 into dd, and the matrix returns to its
+       * normal arrangement. */
+      tmpd = dd - dd1;
+      if (tmpd < dd1)
+      {                                /* quotient suspected to be 1 */
+#ifdef DEBUG_LEHMER
+        q = 1;
+#endif
+        tmpu = xu1 + xu;        /* cannot overflow */
+        tmpv = xv1 + xv;
+      }
+      else
+      {                                /* division indicated */
+        hiremainder = 0;
+        q = 1 + divll(tmpd, dd1);
+        tmpd = hiremainder;
+        tmpu = xu1 + q*xu;
+        tmpv = xv1 + q*xv;
+      }
+
+      tmp0 = addll(tmpu, xu);
+      if ((tmpd < tmpv) || overflow ||
+          (dd1 - tmpd < tmp0))        /* !(Jebelean cond.) */
+        break;                        /* skip ahead to reshift stage */
+      else
+      {                                /* commit dd, xu1, xv1 */
+        res++;
+        dd = tmpd; xu1 = tmpu; xv1 = tmpv;
+#ifdef DEBUG_LEHMER
+        err_printf("  q = %ld, %lx, %lx [%lu,%lu;%lu,%lu]\n",
+                q, dd1, dd, xu, xu1, xv, xv1);
+#endif
+        if (xv1 > vmax)
+        {                        /* time to return */
+          *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+          return res;
+        }
+      }
+
+    } /* end of first loop */
+
+    /* Intermezzo: update dd:ddlo, dd1:dd1lo.  (But not if skip is set.) */
+
+    if (res&1)
+    {
+      /* after failed division in 1st half of loop:
+       * [dd1:dd1lo,dd:ddlo] = [ddorig:ddlo,dd1orig:dd1lo]
+       *                       * [ -xu, xu1 ; xv, -xv1 ]
+       * (Actually, we only multiply [ddlo,dd1lo] onto the matrix and
+       * add the high-order remainders + overflows onto [dd1,dd].)
+       */
+      tmp1 = mulll(ddlo, xu); tmp0 = hiremainder;
+      tmp1 = subll(mulll(dd1lo,xv), tmp1);
+      dd1 += subllx(hiremainder, tmp0);
+      tmp2 = mulll(ddlo, xu1); tmp0 = hiremainder;
+      ddlo = subll(tmp2, mulll(dd1lo,xv1));
+      dd += subllx(tmp0, hiremainder);
+      dd1lo = tmp1;
+    }
+    else
+    {
+      /* after failed division in 2nd half of loop:
+       * [dd:ddlo,dd1:dd1lo] = [ddorig:ddlo,dd1orig:dd1lo]
+       *                       * [ xu1, -xu ; -xv1, xv ]
+       * (Actually, we only multiply [ddlo,dd1lo] onto the matrix and
+       * add the high-order remainders + overflows onto [dd,dd1].)
+       */
+      tmp1 = mulll(ddlo, xu1); tmp0 = hiremainder;
+      tmp1 = subll(tmp1, mulll(dd1lo,xv1));
+      dd += subllx(tmp0, hiremainder);
+      tmp2 = mulll(ddlo, xu); tmp0 = hiremainder;
+      dd1lo = subll(mulll(dd1lo,xv), tmp2);
+      dd1 += subllx(hiremainder, tmp0);
+      ddlo = tmp1;
+    }
+#ifdef DEBUG_LEHMER
+    err_printf("  %lx:%lx, %lx:%lx\n", dd, ddlo, dd1, dd1lo);
+#endif
+  } /* end of skip-pable section:  get here also, with res==1, when there
+     * was a problem immediately after the very first division. */
+
+  /* Re-shift.  Note:  the shift count _can_ be zero, viz. under the following
+   * precise conditions:  The original dd1 had its topmost bit set, so the 1st
+   * q was 1, and after subtraction, dd had its topmost bit unset.  If now
+   * dd==0, we'd have taken the return exit already, so we couldn't have got
+   * here.  If not, then it must have been the second division which has gone
+   * amiss  (because dd1 was very close to an exact multiple of the remainder
+   * dd value, so this will be very rare).  At this point, we'd have a fairly
+   * slim chance of fixing things by re-examining dd1:dd1lo vs. dd:ddlo, but
+   * this is not guaranteed to work.  Instead of trying, we return at once.
+   * The caller will see to it that the initial subtraction is re-done using
+   * _all_ the bits of both operands, which already helps, and the next round
+   * will either be a full division  (if dd occupied a halfword or less),  or
+   * another llgcdii() first step.  In the latter case, since we try a little
+   * harder during our first step, we may actually be able to fix the problem,
+   * and get here again with improved low-order bits and with another step
+   * under our belt.  Otherwise we'll have given up above and forced a full-
+   * blown division.
+   *
+   * If res is even, the shift count _cannot_ be zero.  (The first step forces
+   * a zero into the remainder's MSB, and all subsequent remainders will have
+   * inherited it.)
+   *
+   * The re-shift stage exits if the next divisor has at most half a word's
+   * worth of bits.
+   *
+   * For didactic reasons, the second loop will be arranged in the same way
+   * as the first -- beginning with the division of dd into dd1, as if res
+   * was odd.  To cater for this, if res is actually even, we swap things
+   * around during reshifting.  (During the second loop, the parity of res
+   * does not matter;  we know in which half of the loop we are when we decide
+   * to return.)
+   */
+#ifdef DEBUG_LEHMER
+  err_printf("(sh)");
+#endif
+
+  if (res&1)
+  {                                /* after odd number of division(s) */
+    if (dd1 && (sh = bfffo(dd1)))
+    {
+      shc = BITS_IN_LONG - sh;
+      dd = (ddlo >> shc) + (dd << sh);
+      if (!(HIGHMASK & dd))
+      {
+        *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+        return -res;                /* full division asked for */
+      }
+      dd1 = (dd1lo >> shc) + (dd1 << sh);
+    }
+    else
+    {                                /* time to return: <= 1 word left, or sh==0 */
+      *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+      return res;
+    }
+  }
+  else
+  {                                /* after even number of divisions */
+    if (dd)
+    {
+      sh = bfffo(dd);                /* Known to be positive. */
+      shc = BITS_IN_LONG - sh;
+                                /* dd:ddlo will become the new dd1, and v.v. */
+      tmpd = (ddlo >> shc) + (dd << sh);
+      dd = (dd1lo >> shc) + (dd1 << sh);
+      dd1 = tmpd;
+      /* This has completed the swap;  now dd is again the current divisor.
+       * The following test originally inspected dd1 -- a most subtle and
+       * most annoying bug. The Management. */
+      if (HIGHMASK & dd)
+      {
+        /* recurrence matrix is the wrong way round;  swap it. */
+        tmp0 = xu; xu = xu1; xu1 = tmp0;
+        tmp0 = xv; xv = xv1; xv1 = tmp0;
+      }
+      else
+      {
+        /* recurrence matrix is the wrong way round;  fix this. */
+        *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+        return -res;                /* full division asked for */
+      }
+    }
+    else
+    {                                /* time to return: <= 1 word left */
+      *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+      return res;
+    }
+  } /* end reshift */
+
+#ifdef DEBUG_LEHMER
+  err_printf("  %lx:%lx, %lx:%lx\n", dd, ddlo, dd1, dd1lo);
+#endif
+
+  /* The Second Loop.  Rip-off of the first, but we now check for overflow
+   * in the recurrences.  Returns instead of breaking when we cannot fix the
+   * quotient any longer.
+   */
+
+  for(;;)
+  {
+    /* First half of loop divides dd into dd1, and leaves the recurrence
+     * matrix xu,...,xv1 groomed the wrong way round (xu,xv will be the newer
+     * entries) when successful. */
+    tmpd = dd1 - dd;
+    if (tmpd < dd)
+    {                                /* quotient suspected to be 1 */
+#ifdef DEBUG_LEHMER
+      q = 1;
+#endif
+      tmpu = xu + xu1;
+      tmpv = addll(xv, xv1);        /* xv,xv1 will overflow first */
+      tmp1 = overflow;
+    }
+    else
+    {                                /* division indicated */
+      hiremainder = 0;
+      q = 1 + divll(tmpd, dd);
+      tmpd = hiremainder;
+      tmpu = xu + q*xu1;
+      tmpv = addll(xv, mulll(q,xv1));
+      tmp1 = overflow | hiremainder;
+    }
+
+    tmp0 = addll(tmpv, xv1);
+    if ((tmpd < tmpu) || overflow || tmp1 ||
+        (dd - tmpd < tmp0))        /* !(Jebelean cond.) */
+    {
+      /* The recurrence matrix has not yet been warped... */
+      *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+      break;
+    }
+    /* commit dd1, xu, xv */
+    res++;
+    dd1 = tmpd; xu = tmpu; xv = tmpv;
+#ifdef DEBUG_LEHMER
+    err_printf("  q = %ld, %lx, %lx\n", q, dd, dd1);
+#endif
+    if (xv > vmax)
+    {                                /* time to return */
+      *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+      return res;
+    }
+
+    /* Second half of loop divides dd1 into dd, and the matrix returns to its
+     * normal arrangement. */
+    tmpd = dd - dd1;
+    if (tmpd < dd1)
+    {                                /* quotient suspected to be 1 */
+#ifdef DEBUG_LEHMER
+      q = 1;
+#endif
+      tmpu = xu1 + xu;
+      tmpv = addll(xv1, xv);
+      tmp1 = overflow;
+    }
+    else
+    {                                /* division indicated */
+      hiremainder = 0;
+      q = 1 + divll(tmpd, dd1);
+      tmpd = hiremainder;
+      tmpu = xu1 + q*xu;
+      tmpv = addll(xv1, mulll(q, xv));
+      tmp1 = overflow | hiremainder;
+    }
+
+    tmp0 = addll(tmpu, xu);
+    if ((tmpd < tmpv) || overflow || tmp1 ||
+        (dd1 - tmpd < tmp0))        /* !(Jebelean cond.) */
+    {
+      /* The recurrence matrix has not yet been unwarped, so it is
+       * the wrong way round;  fix this. */
+      *u = xu1; *u1 = xu; *v = xv1; *v1 = xv;
+      break;
+    }
+
+    res++; /* commit dd, xu1, xv1 */
+    dd = tmpd; xu1 = tmpu; xv1 = tmpv;
+#ifdef DEBUG_LEHMER
+    err_printf("  q = %ld, %lx, %lx\n", q, dd1, dd);
+#endif
+    if (xv1 > vmax)
+    {                                /* time to return */
+      *u = xu; *u1 = xu1; *v = xv; *v1 = xv1;
+      return res;
+    }
+  } /* end of second loop */
+
+  return res;
+}
+
+/* 1 / Mod(x,p). Assume x < p */
+ulong
+Fl_invsafe(ulong x, ulong p)
+{
+  long s;
+  ulong xv, xv1, g = xgcduu(p, x, 1, &xv, &xv1, &s);
+  if (g != 1UL) return 0UL;
+  xv = xv1 % p; if (s < 0) xv = p - xv;
+  return xv;
+}
+
+/* 1 / Mod(x,p). Assume x < p */
+ulong
+Fl_inv(ulong x, ulong p)
+{
+  ulong xv  = Fl_invsafe(x, p);
+  if (!xv && p!=1UL) pari_err_INV("Fl_inv", mkintmod(utoi(x), utoi(p)));
+  return xv;
+}
diff --git a/src/kernel/none/int.h b/src/kernel/none/int.h
new file mode 100644
index 0000000..7ffc0dd
--- /dev/null
+++ b/src/kernel/none/int.h
@@ -0,0 +1,37 @@
+#line 2 "../src/kernel/none/int.h"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define int_MSW(x) ((x)+2)
+/*x being a t_INT, return a pointer to the most significant word of x.*/
+
+#define int_LSW(x) ((x)+lgefint((x))-1)
+/*x being a t_INT, return a pointer to the least significant word of x.*/
+
+#define int_precW(x) ((x)+1)
+/*x pointing to a mantissa word, return the previous (less significant)
+ * mantissa word.*/
+
+#define int_nextW(x) ((x)-1)
+/*x pointing to a mantissa word, return the next (more significant) mantissa
+ * word.*/
+
+#define int_W(x,l) ((x)+lgefint((x))-1-(l))
+/*x being a t_INT, return a pointer to the l-th least significant word of x.*/
+
+#define int_W_lg(x,l,lx) ((x)+lx-1-(l))
+/*x being a t_INT, return a pointer to the l-th least significant word of x,
+ * assuming lgefint(x) = lx.*/
+
+#define PARI_KERNEL_NONE
+/*This macro should not be used in libpari itself.*/
diff --git a/src/kernel/none/invmod.c b/src/kernel/none/invmod.c
new file mode 100644
index 0000000..6fce136
--- /dev/null
+++ b/src/kernel/none/invmod.c
@@ -0,0 +1,173 @@
+#line 2 "../src/kernel/none/invmod.c"
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*==================================
+ * invmod(a,b,res)
+ *==================================
+ *    If a is invertible, return 1, and set res  = a^{ -1 }
+ *    Otherwise, return 0, and set res = gcd(a,b)
+ *
+ * This is sufficiently different from bezout() to be implemented separately
+ * instead of having a bunch of extra conditionals in a single function body
+ * to meet both purposes.
+ */
+
+#ifdef INVMOD_PARI
+INLINE int
+invmod_pari(GEN a, GEN b, GEN *res)
+#else
+int
+invmod(GEN a, GEN b, GEN *res)
+#endif
+{
+  GEN v,v1,d,d1,q,r;
+  pari_sp av, av1, lim;
+  long s;
+  ulong g;
+  ulong xu,xu1,xv,xv1; /* Lehmer stage recurrence matrix */
+  int lhmres; /* Lehmer stage return value */
+
+  if (!signe(b)) { *res=absi(a); return 0; }
+  av = avma;
+  if (lgefint(b) == 3) /* single-word affair */
+  {
+    ulong d1 = umodiu(a, (ulong)(b[2]));
+    if (d1 == 0)
+    {
+      if (b[2] == 1L)
+        { *res = gen_0; return 1; }
+      else
+        { *res = absi(b); return 0; }
+    }
+    g = xgcduu((ulong)(b[2]), d1, 1, &xv, &xv1, &s);
+#ifdef DEBUG_LEHMER
+    err_printf(" <- %lu,%lu\n", (ulong)(b[2]), (ulong)(d1[2]));
+    err_printf(" -> %lu,%ld,%lu; %lx\n", g,s,xv1,avma);
+#endif
+    avma = av;
+    if (g != 1UL) { *res = utoipos(g); return 0; }
+    xv = xv1 % (ulong)(b[2]); if (s < 0) xv = ((ulong)(b[2])) - xv;
+    *res = utoipos(xv); return 1;
+  }
+
+  (void)new_chunk(lgefint(b));
+  d = absi(b); d1 = modii(a,d);
+
+  v=gen_0; v1=gen_1;        /* general case */
+#ifdef DEBUG_LEHMER
+  err_printf("INVERT: -------------------------\n");
+  output(d1);
+#endif
+  av1 = avma; lim = stack_lim(av,1);
+
+  while (lgefint(d) > 3 && signe(d1))
+  {
+#ifdef DEBUG_LEHMER
+    err_printf("Calling Lehmer:\n");
+#endif
+    lhmres = lgcdii((ulong*)d, (ulong*)d1, &xu, &xu1, &xv, &xv1, ULONG_MAX);
+    if (lhmres != 0)                /* check progress */
+    {                                /* apply matrix */
+#ifdef DEBUG_LEHMER
+      err_printf("Lehmer returned %d [%lu,%lu;%lu,%lu].\n",
+              lhmres, xu, xu1, xv, xv1);
+#endif
+      if ((lhmres == 1) || (lhmres == -1))
+      {
+        if (xv1 == 1)
+        {
+          r = subii(d,d1); d=d1; d1=r;
+          a = subii(v,v1); v=v1; v1=a;
+        }
+        else
+        {
+          r = subii(d, mului(xv1,d1)); d=d1; d1=r;
+          a = subii(v, mului(xv1,v1)); v=v1; v1=a;
+        }
+      }
+      else
+      {
+        r  = subii(muliu(d,xu),  muliu(d1,xv));
+        a  = subii(muliu(v,xu),  muliu(v1,xv));
+        d1 = subii(muliu(d,xu1), muliu(d1,xv1)); d = r;
+        v1 = subii(muliu(v,xu1), muliu(v1,xv1)); v = a;
+        if (lhmres&1) { togglesign(d);  togglesign(v); }
+        else          { togglesign(d1); togglesign(v1); }
+      }
+    }
+#ifdef DEBUG_LEHMER
+    else
+      err_printf("Lehmer returned 0.\n");
+    output(d); output(d1); output(v); output(v1);
+    sleep(1);
+#endif
+
+    if (lhmres <= 0 && signe(d1))
+    {
+      q = dvmdii(d,d1,&r);
+#ifdef DEBUG_LEHMER
+      err_printf("Full division:\n");
+      printf("  q = "); output(q); sleep (1);
+#endif
+      a = subii(v,mulii(q,v1));
+      v=v1; v1=a;
+      d=d1; d1=r;
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"invmod");
+      gerepileall(av1, 4, &d,&d1,&v,&v1);
+    }
+  } /* end while */
+
+  /* Postprocessing - final sprint */
+  if (signe(d1))
+  {
+    /* Assertions: lgefint(d)==lgefint(d1)==3, and
+     * gcd(d,d1) is nonzero and fits into one word
+     */
+    g = xxgcduu((ulong)d[2], (ulong)d1[2], 1, &xu, &xu1, &xv, &xv1, &s);
+#ifdef DEBUG_LEHMER
+    output(d);output(d1);output(v);output(v1);
+    err_printf(" <- %lu,%lu\n", (ulong)d[2], (ulong)d1[2]);
+    err_printf(" -> %lu,%ld,%lu; %lx\n", g,s,xv1,avma);
+#endif
+    if (g != 1UL) { avma = av; *res = utoipos(g); return 0; }
+    /* (From the xgcduu() blurb:)
+     * For finishing the multiword modinv, we now have to multiply the
+     * returned matrix  (with properly adjusted signs)  onto the values
+     * v' and v1' previously obtained from the multiword division steps.
+     * Actually, it is sufficient to take the scalar product of [v',v1']
+     * with [u1,-v1], and change the sign if s==1.
+     */
+    v = subii(muliu(v,xu1),muliu(v1,xv1));
+    if (s > 0) setsigne(v,-signe(v));
+    avma = av; *res = modii(v,b);
+#ifdef DEBUG_LEHMER
+    output(*res); fprintfderr("============================Done.\n");
+    sleep(1);
+#endif
+    return 1;
+  }
+  /* get here when the final sprint was skipped (d1 was zero already) */
+  avma = av;
+  if (!equalii(d,gen_1)) { *res = icopy(d); return 0; }
+  *res = modii(v,b);
+#ifdef DEBUG_LEHMER
+  output(*res); err_printf("============================Done.\n");
+  sleep(1);
+#endif
+  return 1;
+}
+
diff --git a/src/kernel/none/level1.h b/src/kernel/none/level1.h
new file mode 100644
index 0000000..0f8d21b
--- /dev/null
+++ b/src/kernel/none/level1.h
@@ -0,0 +1,1285 @@
+#line 2 "../src/kernel/none/level1.h"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file defines "level 1" kernel functions.
+ * These functions can be inline; they are also defined externally in
+ * mpinl.c, which includes this file and never needs to be changed */
+
+INLINE long
+evallg(long x)
+{
+  if (x & ~LGBITS) pari_err_OVERFLOW("lg()");
+  return _evallg(x);
+}
+INLINE long
+evalvalp(long x)
+{
+  long v = _evalvalp(x);
+  if (v & ~VALPBITS) pari_err_OVERFLOW("valp()");
+  return v;
+}
+INLINE long
+evalexpo(long x)
+{
+  long v = _evalexpo(x);
+  if (v & ~EXPOBITS) pari_err_OVERFLOW("expo()");
+  return v;
+}
+INLINE long
+evalprecp(long x)
+{
+  long v = _evalprecp(x);
+  if (x & ~((1UL<<(BITS_IN_LONG-VALPnumBITS))-1)) pari_err_OVERFLOW("precp()");
+  return v;
+}
+
+/* Inhibit some area gerepile-wise: declare it to be a non recursive
+ * type, of length l. Thus gerepile won't inspect the zone, just copy it.
+ * For the following situation:
+ *   z = cgetg(t,a); av = avma; garbage(); ltop = avma;
+ *   for (i=1; i<HUGE; i++) gel(z,i) = blah();
+ *   stackdummy(av,ltop);
+ * loses (av-ltop) words but save a costly gerepile. */
+INLINE void
+stackdummy(pari_sp av, pari_sp ltop) {
+  long l = ((GEN)av) - ((GEN)ltop);
+  if (l > 0) {
+    GEN z = (GEN)ltop;
+    z[0] = evaltyp(t_VECSMALL) | evallg(l);
+#ifdef DEBUG
+    { long i; for (i = 1; i < l; i++) z[i] = 0; }
+#endif
+  }
+}
+INLINE void
+fixlg(GEN x, long ly) {
+  long lx = lg(x), l = lx - ly;
+  if (l > 0)
+  { /* stackdummy(x+lx, x+ly) */
+    GEN z = x + ly;
+    z[0] = evaltyp(t_VECSMALL) | evallg(l);
+    setlg(x, ly);
+#ifdef DEBUG
+    { long i; for (i = 1; i < l; i++) z[i] = 0; }
+#endif
+  }
+}
+/* update lg(z) before affrr(y, z)  [ to cater for precision loss ]*/
+INLINE void
+affrr_fixlg(GEN y, GEN z) { fixlg(z, lg(y)); affrr(y, z); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                       ALLOCATE ON STACK                         */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+new_chunk(size_t x) /* x is a number of longs */
+{
+  GEN z = ((GEN) avma) - x;
+  if (x > (avma-bot) / sizeof(long)) pari_err(e_STACK);
+  CHECK_CTRLC
+  avma = (pari_sp)z;
+
+#ifdef MEMSTEP
+  if (DEBUGMEM && memused != DISABLE_MEMUSED) {
+    long d = (long)memused - (long)z;
+    if (d > 4*MEMSTEP || d < -4*MEMSTEP)
+    {
+      memused = (pari_sp)z;
+      err_printf("...%4.0lf Mbytes used\n",(top-memused)/1048576.);
+    }
+  }
+#endif
+  return z;
+}
+
+INLINE char *
+stack_malloc(size_t N)
+{
+  long n = nchar2nlong(N);
+  return (char*)new_chunk(n);
+}
+
+INLINE char *
+stack_calloc(size_t N)
+{
+  char *p = stack_malloc(N);
+  memset(p, 0, N); return p;
+}
+
+/* cgetg(lg(x), typ(x)), set *lx. Implicit unsetisclone() */
+INLINE GEN
+cgetg_copy(GEN x, long *plx) {
+  GEN y;
+  *plx = lg(x); y = new_chunk((size_t)*plx);
+  y[0] = x[0] & (TYPBITS|LGBITS); return y;
+}
+INLINE GEN
+cgetg_block(long x, long y)
+{
+  GEN z = newblock((size_t)x);
+  z[0] = evaltyp(y) | evallg(x);
+  return z;
+}
+INLINE GEN
+cgetg(long x, long y)
+{
+  GEN z = new_chunk((size_t)x);
+  z[0] = evaltyp(y) | evallg(x);
+  return z;
+}
+INLINE GEN
+cgeti(long x)
+{
+  GEN z = new_chunk((size_t)x);
+  z[0] = evaltyp(t_INT) | evallg(x);
+  return z;
+}
+INLINE GEN
+cgetipos(long x)
+{
+  GEN z = cgeti(x);
+  z[1] = evalsigne(1) | evallgefint(x);
+  return z;
+}
+INLINE GEN
+cgetineg(long x)
+{
+  GEN z = cgeti(x);
+  z[1] = evalsigne(-1) | evallgefint(x);
+  return z;
+}
+INLINE GEN
+cgetr_block(long x)
+{
+  GEN z = newblock((size_t)x);
+  z[0] = evaltyp(t_REAL) | evallg(x);
+  return z;
+}
+INLINE GEN
+cgetr(long x)
+{
+  GEN z = new_chunk((size_t)x);
+  z[0] = evaltyp(t_REAL) | evallg(x);
+  return z;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                     COPY, NEGATION, ABSOLUTE VALUE              */
+/*                                                                 */
+/*******************************************************************/
+/* cannot do memcpy because sometimes x and y overlap */
+INLINE GEN
+leafcopy(GEN x)
+{
+  register long lx = lg(x);
+  GEN y = new_chunk(lx); /* can't use cgetg_copy, in case x,y overlap */
+  while (--lx > 0) y[lx] = x[lx];
+  y[0] = x[0] & (TYPBITS|LGBITS); return y;
+}
+INLINE GEN
+icopy(GEN x)
+{
+  long i = lgefint(x), lx = i;
+  GEN y = new_chunk(lx); /* can't use cgeti, in case x,y overlap */
+  while (--i > 0) y[i] = x[i];
+  y[0] = evaltyp(t_INT) | evallg(lx);
+  return y;
+}
+INLINE GEN
+icopyspec(GEN x, long nx)
+{
+  long i = nx+2, lx = i;
+  GEN y = new_chunk(lx); /* can't use cgeti, in case x,y overlap */
+  x -= 2; while (--i >= 2) y[i] = x[i];
+  y[1] = evalsigne(1) | evallgefint(lx);
+  y[0] = evaltyp(t_INT) | evallg(lx);
+  return y;
+}
+INLINE GEN rcopy(GEN x) { return leafcopy(x); }
+INLINE GEN mpcopy(GEN x) { return leafcopy(x); }
+
+INLINE GEN
+mpabs(GEN x) { GEN y = leafcopy(x); setabssign(y); return y; }
+INLINE GEN
+mpabs_shallow(GEN x) { return signe(x) < 0? mpabs(x): x; }
+INLINE GEN absi(GEN x) { return mpabs(x); }
+INLINE GEN absi_shallow(GEN x) { return signe(x) < 0? negi(x): x; }
+INLINE GEN absr(GEN x) { return mpabs(x); }
+
+INLINE GEN
+mpneg(GEN x) { GEN y = leafcopy(x); togglesign(y); return y; }
+INLINE GEN negi(GEN x) { return mpneg(x); }
+INLINE GEN negr(GEN x) { return mpneg(x); }
+
+/* negate in place */
+INLINE void
+togglesign(GEN x) { if (x[1] & SIGNBITS) { x[1] ^= HIGHBIT; } }
+INLINE void
+setabssign(GEN x) { x[1] &= ~HIGHBIT; }
+/* negate in place, except universal constants */
+INLINE void
+togglesign_safe(GEN *px)
+{
+  switch(*px - gen_1) /* gen_1, gen_2, gen_m1, gen_m2 */
+  {
+    case 0: *px = gen_m1; break;
+    case 3: *px = gen_m2;  break;
+    case 6: *px = gen_1; break;
+    case 9: *px = gen_2;  break;
+    default: togglesign(*px);
+  }
+}
+/* setsigne(y, signe(x)) */
+INLINE void
+affectsign(GEN x, GEN y)
+{
+  y[1] = (x[1] & SIGNBITS) | (y[1] & ~SIGNBITS);
+}
+/* copies sign in place, except for universal constants */
+INLINE void
+affectsign_safe(GEN x, GEN *py)
+{
+  if (((*py)[1] ^ x[1]) & HIGHBIT) togglesign_safe(py);
+}
+/*******************************************************************/
+/*                                                                 */
+/*                     GEN -> LONG, LONG -> GEN                    */
+/*                                                                 */
+/*******************************************************************/
+/* assume x != 0, return -x as a t_INT */
+INLINE GEN
+utoineg(ulong x) { GEN y = cgetineg(3); y[2] = x; return y; }
+/* assume x != 0, return utoi(x) */
+INLINE GEN
+utoipos(ulong x) { GEN y = cgetipos(3); y[2] = x; return y; }
+INLINE GEN
+utoi(ulong x) { return x? utoipos(x): gen_0; }
+INLINE GEN
+stoi(long x)
+{
+  if (!x) return gen_0;
+  return x > 0? utoipos((ulong)x): utoineg((ulong)-x);
+}
+
+/* x 2^BIL + y */
+INLINE GEN
+uutoi(ulong x, ulong y)
+{
+  GEN z;
+  if (!x) return utoi(y);
+  z = cgetipos(4);
+  *int_W_lg(z, 1, 4) = x;
+  *int_W_lg(z, 0, 4) = y; return z;
+}
+/* - (x 2^BIL + y) */
+INLINE GEN
+uutoineg(ulong x, ulong y)
+{
+  GEN z;
+  if (!x) return y? utoineg(y): gen_0;
+  z = cgetineg(4);
+  *int_W_lg(z, 1, 4) = x;
+  *int_W_lg(z, 0, 4) = y; return z;
+}
+
+INLINE long
+itos(GEN x)
+{
+  long s = signe(x);
+  long u;
+
+  if (!s) return 0;
+  u = x[2];
+  if (lgefint(x) > 3 || u < 0)
+    pari_err_OVERFLOW("t_INT-->long assignment");
+  return (s>0) ? u : -u;
+}
+/* as itos, but return 0 if too large. Cf is_bigint */
+INLINE long
+itos_or_0(GEN x) {
+  long n;
+  if (lgefint(x) != 3 || (n = x[2]) & HIGHBIT) return 0;
+  return signe(x) > 0? n: -n;
+}
+INLINE ulong
+itou(GEN x)
+{
+  switch(lgefint(x)) {
+    case 2: return 0;
+    case 3: return x[2];
+    default:
+      pari_err_OVERFLOW("t_INT-->ulong assignment");
+      return 0; /* not reached */
+  }
+}
+
+/* as itou, but return 0 if too large. Cf is_bigint */
+INLINE ulong
+itou_or_0(GEN x) {
+  if (lgefint(x) != 3) return 0;
+  return (ulong)x[2];
+}
+
+INLINE GEN
+real_0_bit(long bitprec) { GEN x=cgetr(2); x[1]=evalexpo(bitprec); return x; }
+INLINE GEN
+real_0(long prec) { return real_0_bit(-prec2nbits(prec)); }
+INLINE GEN
+real_1(long prec) {
+  GEN x = cgetr(prec);
+  long i;
+  x[1] = evalsigne(1) | _evalexpo(0);
+  x[2] = (long)HIGHBIT; for (i=3; i<prec; i++) x[i] = 0;
+  return x;
+}
+INLINE GEN
+real_m1(long prec) {
+  GEN x = cgetr(prec);
+  long i;
+  x[1] = evalsigne(-1) | _evalexpo(0);
+  x[2] = (long)HIGHBIT; for (i=3; i<prec; i++) x[i] = 0;
+  return x;
+}
+
+/* 2.^n */
+INLINE GEN
+real2n(long n, long prec) { GEN z = real_1(prec); setexpo(z, n); return z; }
+INLINE GEN
+real_m2n(long n, long prec) { GEN z = real_m1(prec); setexpo(z, n); return z; }
+INLINE GEN
+stor(long s, long prec) { GEN z = cgetr(prec); affsr(s,z); return z; }
+INLINE GEN
+utor(ulong s, long prec){ GEN z = cgetr(prec); affur(s,z); return z; }
+INLINE GEN
+itor(GEN x, long prec) { GEN z = cgetr(prec); affir(x,z); return z; }
+INLINE GEN
+rtor(GEN x, long prec) { GEN z = cgetr(prec); affrr(x,z); return z; }
+
+INLINE ulong int_bit(GEN x, long n)
+{
+  long r, q = dvmdsBIL(n, &r);
+  return q < lgefint(x)-2?((ulong)*int_W(x,q) >> r) & 1UL:0;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                           COMPARISON                            */
+/*                                                                 */
+/*******************************************************************/
+INLINE int
+cmpir(GEN x, GEN y)
+{
+  pari_sp av;
+  GEN z;
+
+  if (!signe(x)) return -signe(y);
+  if (!signe(y)) return  signe(x);
+  av=avma; z = itor(x, realprec(y)); avma=av;
+  return cmprr(z,y); /* cmprr does no memory adjustment */
+}
+INLINE int
+cmpri(GEN x, GEN y) { return -cmpir(y,x); }
+INLINE int
+cmpsr(long x, GEN y)
+{
+  pari_sp av;
+  GEN z;
+
+  if (!x) return -signe(y);
+  av=avma; z = stor(x, LOWDEFAULTPREC); avma=av;
+  return cmprr(z,y);
+}
+INLINE int
+cmprs(GEN x, long y) { return -cmpsr(y,x); }
+/* compare x and |y| */
+INLINE int
+cmpui(ulong x, GEN y)
+{
+  long l = lgefint(y);
+  ulong p;
+
+  if (!x) return (l > 2)? -1: 0;
+  if (l == 2) return 1;
+  if (l > 3) return -1;
+  p = y[2]; if (p == x) return 0;
+  return p < x ? 1 : -1;
+}
+INLINE int
+cmpiu(GEN x, ulong y) { return -cmpui(y,x); }
+INLINE int
+cmpsi(long x, GEN y)
+{
+  ulong p;
+
+  if (!x) return -signe(y);
+
+  if (x > 0)
+  {
+    if (signe(y)<=0) return 1;
+    if (lgefint(y)>3) return -1;
+    p = y[2]; if (p == (ulong)x) return 0;
+    return p < (ulong)x ? 1 : -1;
+  }
+
+  if (signe(y)>=0) return -1;
+  if (lgefint(y)>3) return 1;
+  p = y[2]; if (p == (ulong)-x) return 0;
+  return p < (ulong)(-x) ? -1 : 1;
+}
+INLINE int
+cmpis(GEN x, long y) { return -cmpsi(y,x); }
+INLINE int
+mpcmp(GEN x, GEN y)
+{
+  if (typ(x)==t_INT)
+    return (typ(y)==t_INT) ? cmpii(x,y) : cmpir(x,y);
+  return (typ(y)==t_INT) ? -cmpir(y,x) : cmprr(x,y);
+}
+
+/* x == y ? */
+INLINE int
+equalsi(long x, GEN y)
+{
+  if (!x) return !signe(y);
+  if (x > 0)
+  {
+    if (signe(y) <= 0 || lgefint(y) != 3) return 0;
+    return ((ulong)y[2] == (ulong)x);
+  }
+  if (signe(y) >= 0 || lgefint(y) != 3) return 0;
+  return ((ulong)y[2] == (ulong)-x);
+}
+/* x == |y| ? */
+INLINE int
+equalui(ulong x, GEN y)
+{
+  if (!x) return !signe(y);
+  return (lgefint(y) == 3 && (ulong)y[2] == x);
+}
+INLINE int
+equaliu(GEN x, ulong y) { return equalui(y,x); }
+INLINE int
+equalis(GEN x, long y) { return equalsi(y,x); }
+
+/* assume x != 0, is |x| == 2^n ? */
+INLINE int
+absrnz_equal2n(GEN x) {
+  if ((ulong)x[2]==HIGHBIT)
+  {
+    long i, lx = lg(x);
+    for (i = 3; i < lx; i++)
+      if (x[i]) return 0;
+    return 1;
+  }
+  return 0;
+}
+/* assume x != 0, is |x| == 1 ? */
+INLINE int
+absrnz_equal1(GEN x) { return !expo(x) && absrnz_equal2n(x); }
+
+INLINE long
+maxss(long x, long y) { return x>y?x:y; }
+INLINE long
+minss(long x, long y) { return x<y?x:y; }
+INLINE long
+minuu(ulong x, ulong y) { return x<y?x:y; }
+INLINE long
+maxuu(ulong x, ulong y) { return x>y?x:y; }
+INLINE double
+maxdd(double x, double y) { return x>y?x:y; }
+INLINE double
+mindd(double x, double y) { return x<y?x:y; }
+
+/*******************************************************************/
+/*                                                                 */
+/*                             ADD / SUB                           */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+subuu(ulong x, ulong y)
+{
+  ulong z;
+  LOCAL_OVERFLOW;
+  z = subll(x, y);
+  return overflow? utoineg(-z): utoi(z);
+}
+INLINE GEN
+adduu(ulong x, ulong y) { ulong t = x+y; return uutoi((t < x), t); }
+
+INLINE GEN
+addss(long x, long y)
+{
+  if (!x) return stoi(y);
+  if (!y) return stoi(x);
+  if (x > 0) return y > 0? adduu(x,y): subuu(x, -y);
+
+  if (y > 0) return subuu(y, -x);
+  else { /* - adduu(-x, -y) */
+    ulong t = (-x)+(-y); return uutoineg((t < (ulong)(-x)), t);
+  }
+}
+INLINE GEN subss(long x, long y) { return addss(-y,x); }
+
+INLINE GEN
+subii(GEN x, GEN y)
+{
+  if (x==y) return gen_0; /* frequent with x = y = gen_0 */
+  return addii_sign(x, signe(x), y, -signe(y));
+}
+INLINE GEN
+addii(GEN x, GEN y) { return addii_sign(x, signe(x), y, signe(y)); }
+INLINE GEN
+addrr(GEN x, GEN y) { return addrr_sign(x, signe(x), y, signe(y)); }
+INLINE GEN
+subrr(GEN x, GEN y) { return addrr_sign(x, signe(x), y, -signe(y)); }
+INLINE GEN
+addir(GEN x, GEN y) { return addir_sign(x, signe(x), y, signe(y)); }
+INLINE GEN
+subir(GEN x, GEN y) { return addir_sign(x, signe(x), y, -signe(y)); }
+INLINE GEN
+subri(GEN x, GEN y) { return addir_sign(y, -signe(y), x, signe(x)); }
+INLINE GEN
+addsi(long x, GEN y) { return addsi_sign(x, y, signe(y)); }
+INLINE GEN
+addui(ulong x, GEN y) { return addui_sign(x, y, signe(y)); }
+INLINE GEN
+subsi(long x, GEN y) { return addsi_sign(x, y, -signe(y)); }
+INLINE GEN
+subui(ulong x, GEN y) { return addui_sign(x, y, -signe(y)); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                           MOD, REM, DIV                         */
+/*                                                                 */
+/*******************************************************************/
+INLINE ulong mod2BIL(GEN x) { return *int_LSW(x); }
+INLINE long mod64(GEN x) { return mod2BIL(x) & 63; }
+INLINE long mod32(GEN x) { return mod2BIL(x) & 31; }
+INLINE long mod16(GEN x) { return mod2BIL(x) & 15; }
+INLINE long mod8(GEN x)  { return mod2BIL(x) & 7; }
+INLINE long mod4(GEN x)  { return mod2BIL(x) & 3; }
+INLINE long mod2(GEN x)  { return mod2BIL(x) & 1; }
+INLINE int
+mpodd(GEN x) { return signe(x) && mod2(x); }
+
+INLINE GEN
+truedivii(GEN a,GEN b) { return truedvmdii(a,b,NULL); }
+INLINE GEN
+truedivis(GEN a, long b) { return truedvmdis(a,b,NULL); }
+INLINE GEN
+truedivsi(long a, GEN b) { return truedvmdsi(a,b,NULL); }
+
+INLINE GEN
+divii(GEN a, GEN b) { return dvmdii(a,b,NULL); }
+INLINE GEN
+remii(GEN a, GEN b) { return dvmdii(a,b,ONLY_REM); }
+
+INLINE GEN
+divss(long x, long y) { return stoi(x / y); }
+INLINE GEN
+modss(long x, long y) { return stoi(smodss(x, y)); }
+INLINE GEN
+remss(long x, long y) { return stoi(x % y); }
+INLINE long
+smodss(long x, long y)
+{
+  long r = x%y;
+  return (r >= 0)? r: labs(y) + r;
+}
+
+INLINE long
+sdivss_rem(long x, long y, long *r)
+{
+  long q;
+  LOCAL_HIREMAINDER;
+  if (!y) pari_err_INV("sdivss_rem",gen_0);
+  hiremainder = 0; q = divll((ulong)labs(x),(ulong)labs(y));
+  if (x < 0) { hiremainder = -((long)hiremainder); q = -q; }
+  if (y < 0) q = -q;
+  *r = hiremainder; return q;
+}
+INLINE GEN
+divss_rem(long x, long y, long *r) { return stoi(sdivss_rem(x,y,r)); }
+INLINE ulong
+udivuu_rem(ulong x, ulong y, ulong *r)
+{
+  if (!y) pari_err_INV("udivuu_rem",gen_0);
+  *r = x % y; return x / y;
+}
+
+INLINE ulong
+udivui_rem(ulong x, GEN y, ulong *r)
+{
+  long q, s = signe(y);
+  LOCAL_HIREMAINDER;
+
+  if (!s) pari_err_INV("udivui_rem",gen_0);
+  if (!x || lgefint(y)>3) { *r = x; return 0; }
+  hiremainder=0; q = (long)divll(x, (ulong)y[2]);
+  if (s < 0) q = -q;
+  *r = hiremainder; return q;
+}
+
+/* assume d != 0 and |n| / d can be represented as an ulong.
+ * Return |n|/d, set *r = |n| % d */
+INLINE ulong
+udiviu_rem(GEN n, ulong d, ulong *r)
+{
+  switch(lgefint(n))
+  {
+    case 2: *r = 0; return 0;
+    case 3:
+    {
+      ulong nn = n[2];
+      *r = nn % d; return nn / d;
+    }
+    default: /* 4 */
+    {
+      ulong n1, n0, q;
+      LOCAL_HIREMAINDER;
+      n0 = *int_W(n,0);
+      n1 = *int_W(n,1);
+      hiremainder = n1;
+      q = divll(n0, d);
+      *r = hiremainder; return q;
+    }
+  }
+}
+
+INLINE long
+sdivsi_rem(long x, GEN y, long *r)
+{
+  long q, s = signe(y);
+  LOCAL_HIREMAINDER;
+
+  if (!s) pari_err_INV("sdivsi_rem",gen_0);
+  if (!x || lgefint(y)>3 || ((long)y[2]) < 0) { *r = x; return 0; }
+  hiremainder=0; q = (long)divll(labs(x), (ulong)y[2]);
+  if (x < 0) { hiremainder = -((long)hiremainder); q = -q; }
+  if (s < 0) q = -q;
+  *r = hiremainder; return q;
+}
+INLINE GEN
+divsi_rem(long s, GEN y, long *r) { return stoi(sdivsi_rem(s,y,r)); }
+
+INLINE long
+sdivsi(long x, GEN y)
+{
+  long q, s = signe(y);
+
+  if (!s) pari_err_INV("sdivsi",gen_0);
+  if (!x || lgefint(y)>3 || ((long)y[2]) < 0) return 0;
+  q = labs(x) / y[2];
+  if (x < 0) q = -q;
+  if (s < 0) q = -q;
+  return q;
+}
+
+INLINE GEN
+dvmdss(long x, long y, GEN *z)
+{
+  long r;
+  GEN q = divss_rem(x,y, &r);
+  *z = stoi(r); return q;
+}
+INLINE long
+dvmdsBIL(long n, long *r) { *r = remsBIL(n); return divsBIL(n); }
+INLINE ulong
+dvmduBIL(ulong n, ulong *r) { *r = remsBIL(n); return divsBIL(n); }
+INLINE GEN
+dvmdsi(long x, GEN y, GEN *z)
+{
+  long r;
+  GEN q = divsi_rem(x,y, &r);
+  *z = stoi(r); return q;
+}
+INLINE GEN
+dvmdis(GEN x, long y, GEN *z)
+{
+  long r;
+  GEN q = divis_rem(x,y, &r);
+  *z = stoi(r); return q;
+}
+
+INLINE long
+smodis(GEN x, long y)
+{
+  pari_sp av = avma;
+  long r;
+  (void)divis_rem(x,y, &r); avma = av; return (r >= 0) ? r: labs(y) + r;
+}
+INLINE GEN
+modis(GEN x, long y) { return stoi(smodis(x,y)); }
+INLINE GEN
+modsi(long x, GEN y) {
+  long r;
+  (void)sdivsi_rem(x, y, &r);
+  return (r >= 0)? stoi(r): addsi_sign(r, y, 1);
+}
+
+INLINE ulong
+umodui(ulong x, GEN y)
+{
+  if (!signe(y)) pari_err_INV("umodui",gen_0);
+  if (!x || lgefint(y) > 3) return x;
+  return x % (ulong)y[2];
+}
+
+INLINE GEN
+remsi(long x, GEN y)
+{ long r; (void)sdivsi_rem(x,y, &r); return stoi(r); }
+INLINE GEN
+remis(GEN x, long y)
+{
+  pari_sp av = avma;
+  long r;
+  (void)divis_rem(x,y, &r); avma = av; return stoi(r);
+}
+
+INLINE GEN
+rdivis(GEN x, long y, long prec)
+{
+  GEN z = cgetr(prec);
+  pari_sp av = avma;
+  affrr(divrs(itor(x,prec), y),z);
+  avma = av; return z;
+}
+INLINE GEN
+rdivsi(long x, GEN y, long prec)
+{
+  GEN z = cgetr(prec);
+  pari_sp av = avma;
+  affrr(divsr(x, itor(y,prec)), z);
+  avma = av; return z;
+}
+INLINE GEN
+rdivss(long x, long y, long prec)
+{
+  GEN z = cgetr(prec);
+  pari_sp av = avma;
+  affrr(divrs(stor(x, prec), y), z);
+  avma = av; return z;
+}
+
+INLINE void
+rdiviiz(GEN x, GEN y, GEN z)
+{
+  pari_sp av = avma;
+  long prec = realprec(z);
+  affir(x, z);
+  if (!is_bigint(y)) {
+    affrr(divrs(z, y[2]), z);
+    if (signe(y) < 0) togglesign(z);
+  }
+  else
+    affrr(divrr(z, itor(y,prec)), z);
+  avma = av;
+}
+INLINE GEN
+rdivii(GEN x, GEN y, long prec)
+{
+  GEN z = cgetr(prec);
+  pari_sp av = avma;
+  affir(x, z);
+  if (lg(y) == 3) {
+    affrr(divru(z, y[2]), z);
+    if (signe(y) < 0) togglesign(z);
+  }
+  else
+    affrr(divrr(z, itor(y,prec)), z);
+  avma = av; return z;
+}
+INLINE GEN
+fractor(GEN x, long prec) { return rdivii(gel(x,1), gel(x,2), prec); }
+
+INLINE int
+dvdii(GEN x, GEN y)
+{
+  pari_sp av=avma;
+  GEN r = remii(x,y);
+  avma = av; return r == gen_0;
+}
+INLINE int
+dvdsi(long x, GEN y)
+{
+  if (!signe(y)) return x == 0;
+  if (lgefint(y) != 3) return 0;
+  return x % y[2] == 0;
+}
+INLINE int
+dvdui(ulong x, GEN y)
+{
+  if (!signe(y)) return x == 0;
+  if (lgefint(y) != 3) return 0;
+  return x % y[2] == 0;
+}
+INLINE int
+dvdis(GEN x, long y)
+{ return y? smodis(x, y) == 0: signe(x) == 0; }
+INLINE int
+dvdiu(GEN x, ulong y)
+{ return y? umodiu(x, y) == 0: signe(x) == 0; }
+
+INLINE int
+dvdisz(GEN x, long y, GEN z)
+{
+  const pari_sp av = avma;
+  long r;
+  GEN p1 = divis_rem(x,y, &r);
+  avma = av; if (r) return 0;
+  affii(p1,z); return 1;
+}
+INLINE int
+dvdiuz(GEN x, ulong y, GEN z)
+{
+  const pari_sp av = avma;
+  ulong r;
+  GEN p1 = diviu_rem(x,y, &r);
+  avma = av; if (r) return 0;
+  affii(p1,z); return 1;
+}
+INLINE int
+dvdiiz(GEN x, GEN y, GEN z)
+{
+  const pari_sp av=avma;
+  GEN p2;
+  const GEN p1=dvmdii(x,y,&p2);
+
+  if (signe(p2)) { avma=av; return 0; }
+  affii(p1,z); avma=av; return 1;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                        MP (INT OR REAL)                         */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+mptrunc(GEN x) { return typ(x)==t_INT? icopy(x): truncr(x); }
+INLINE GEN
+mpfloor(GEN x) { return typ(x)==t_INT? icopy(x): floorr(x); }
+INLINE GEN
+mpceil(GEN x) { return typ(x)==t_INT? icopy(x): ceilr(x); }
+INLINE GEN
+mpround(GEN x) { return typ(x) == t_INT? icopy(x): roundr(x); }
+
+INLINE long
+mpexpo(GEN x) { return typ(x) == t_INT? expi(x): expo(x); }
+
+INLINE GEN
+mpadd(GEN x, GEN y)
+{
+  if (typ(x)==t_INT)
+    return (typ(y)==t_INT) ? addii(x,y) : addir(x,y);
+  return (typ(y)==t_INT) ? addir(y,x) : addrr(x,y);
+}
+INLINE GEN
+mpsub(GEN x, GEN y)
+{
+  if (typ(x)==t_INT)
+    return (typ(y)==t_INT) ? subii(x,y) : subir(x,y);
+  return (typ(y)==t_INT) ? subri(x,y) : subrr(x,y);
+}
+INLINE GEN
+mpmul(GEN x, GEN y)
+{
+  if (typ(x)==t_INT)
+    return (typ(y)==t_INT) ? mulii(x,y) : mulir(x,y);
+  return (typ(y)==t_INT) ? mulir(y,x) : mulrr(x,y);
+}
+INLINE GEN
+mpsqr(GEN x) { return (typ(x)==t_INT) ? sqri(x) : sqrr(x); }
+INLINE GEN
+mpdiv(GEN x, GEN y)
+{
+  if (typ(x)==t_INT)
+    return (typ(y)==t_INT) ? divii(x,y) : divir(x,y);
+  return (typ(y)==t_INT) ? divri(x,y) : divrr(x,y);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                          Z/nZ, n ULONG                          */
+/*                                                                 */
+/*******************************************************************/
+INLINE ulong
+Fl_double(ulong a, ulong p)
+{
+  ulong res = a << 1;
+  return (res >= p || res < a) ? res - p : res;
+}
+INLINE ulong
+Fl_triple(ulong a, ulong p)
+{
+  ulong res = a << 1;
+  if (res >= p || res < a) res -= p;
+  res += a;
+  return (res >= p || res < a)? res - p: res;
+}
+INLINE ulong
+Fl_add(ulong a, ulong b, ulong p)
+{
+  ulong res = a + b;
+  return (res >= p || res < a) ? res - p : res;
+}
+INLINE ulong
+Fl_neg(ulong x, ulong p) { return x ? p - x: 0; }
+
+INLINE ulong
+Fl_sub(ulong a, ulong b, ulong p)
+{
+  ulong res = a - b;
+  return (res > a) ? res + p: res;
+}
+
+/* centerlift(u mod p) */
+INLINE long
+Fl_center(ulong u, ulong p, ulong ps2) { return (long) (u > ps2)? u - p: u; }
+
+INLINE ulong
+Fl_mul(ulong a, ulong b, ulong p)
+{
+  register ulong x;
+  LOCAL_HIREMAINDER;
+  x = mulll(a,b);
+  if (!hiremainder) return x % p;
+  (void)divll(x,p); return hiremainder;
+}
+INLINE ulong
+Fl_sqr(ulong a, ulong p)
+{
+  register ulong x;
+  LOCAL_HIREMAINDER;
+  x = mulll(a,a);
+  if (!hiremainder) return x % p;
+  (void)divll(x,p); return hiremainder;
+}
+INLINE ulong
+Fl_div(ulong a, ulong b, ulong p) { return Fl_mul(a, Fl_inv(b, p), p); }
+
+/*******************************************************************/
+/*                                                                 */
+/*        DEFINED FROM EXISTING ONE EXPLOITING COMMUTATIVITY       */
+/*                                                                 */
+/*******************************************************************/
+INLINE GEN
+addri(GEN x, GEN y) { return addir(y,x); }
+INLINE GEN
+addis(GEN x, long s) { return addsi(s,x); }
+INLINE GEN
+addiu(GEN x, ulong s) { return addui(s,x); }
+INLINE GEN
+addrs(GEN x, long s) { return addsr(s,x); }
+
+INLINE GEN
+subiu(GEN x, long y) { GEN z = subui(y, x); togglesign(z); return z; }
+INLINE GEN
+subis(GEN x, long y) { return addsi(-y,x); }
+INLINE GEN
+subrs(GEN x, long y) { return addsr(-y,x); }
+
+INLINE GEN
+mulis(GEN x, long s) { return mulsi(s,x); }
+INLINE GEN
+muliu(GEN x, ulong s) { return mului(s,x); }
+INLINE GEN
+mulru(GEN x, ulong s) { return mulur(s,x); }
+INLINE GEN
+mulri(GEN x, GEN s) { return mulir(s,x); }
+INLINE GEN
+mulrs(GEN x, long s) { return mulsr(s,x); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                  VALUATION, EXPONENT, SHIFTS                    */
+/*                                                                 */
+/*******************************************************************/
+INLINE long
+vali(GEN x)
+{
+  long i;
+  GEN xp;
+
+  if (!signe(x)) return -1;
+  xp=int_LSW(x);
+  for (i=0; !*xp; i++) xp=int_nextW(xp);
+  return vals(*xp) + i * BITS_IN_LONG;
+}
+
+
+/* assume x > 0 */
+INLINE long
+expu(ulong x) { return (BITS_IN_LONG-1) - (long)bfffo(x); }
+
+INLINE long
+expi(GEN x)
+{
+  const long lx=lgefint(x);
+  return lx==2? -(long)HIGHEXPOBIT: bit_accuracy(lx)-(long)bfffo(*int_MSW(x))-1;
+}
+
+INLINE GEN
+shiftr(GEN x, long n)
+{
+  const long e = evalexpo(expo(x)+n);
+  const GEN y = rcopy(x);
+
+  if (e & ~EXPOBITS) pari_err_OVERFLOW("expo()");
+  y[1] = (y[1]&~EXPOBITS) | e; return y;
+}
+INLINE GEN
+mpshift(GEN x,long s) { return (typ(x)==t_INT)?shifti(x,s):shiftr(x,s); }
+
+/* FIXME: adapt/use mpn_[lr]shift instead */
+/* z2[imin..imax] := z1[imin..imax].f shifted left sh bits
+ * (feeding f from the right). Assume sh > 0 */
+INLINE void
+shift_left(GEN z2, GEN z1, long imin, long imax, ulong f,  ulong sh)
+{
+  GEN sb = z1 + imin, se = z1 + imax, te = z2 + imax;
+  ulong l, m = BITS_IN_LONG - sh, k = f >> m;
+  while (se > sb) {
+    l     = *se--;
+    *te-- = (l << sh) | k;
+    k     = l >> m;
+  }
+  *te = (*se << sh) | k;
+}
+/* z2[imin..imax] := f.z1[imin..imax-1] shifted right sh bits
+ * (feeding f from the left). Assume sh > 0 */
+INLINE void
+shift_right(GEN z2, GEN z1, long imin, long imax, ulong f, ulong sh)
+{
+  GEN sb = z1 + imin, se = z1 + imax, tb = z2 + imin;
+  ulong k, l = *sb++, m = BITS_IN_LONG - sh;
+  *tb++ = (l >> sh) | (f << m);
+  while (sb < se) {
+    k     = l << m;
+    l     = *sb++;
+    *tb++ = (l >> sh) | k;
+  }
+}
+
+/* Backward compatibility. Inefficient && unused */
+extern ulong hiremainder;
+INLINE ulong
+shiftl(ulong x, ulong y)
+{ hiremainder = x>>(BITS_IN_LONG-y); return (x<<y); }
+
+INLINE ulong
+shiftlr(ulong x, ulong y)
+{ hiremainder = x<<(BITS_IN_LONG-y); return (x>>y); }
+
+INLINE void
+shiftr_inplace(GEN z, long d)
+{
+  setexpo(z, expo(z)+d);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                           ASSIGNMENT                            */
+/*                                                                 */
+/*******************************************************************/
+INLINE void
+affii(GEN x, GEN y)
+{
+  long lx = lgefint(x);
+  if (lg(y)<lx) pari_err_OVERFLOW("t_INT-->t_INT assignment");
+  while (--lx) y[lx] = x[lx];
+}
+INLINE void
+affsi(long s, GEN x)
+{
+  if (!s) x[1] = evalsigne(0) | evallgefint(2);
+  else
+  {
+    if (s > 0) { x[1] = evalsigne( 1) | evallgefint(3); x[2] =  s; }
+    else       { x[1] = evalsigne(-1) | evallgefint(3); x[2] = -s; }
+  }
+}
+INLINE void
+affui(ulong u, GEN x)
+{
+  if (!u) x[1] = evalsigne(0) | evallgefint(2);
+  else  { x[1] = evalsigne(1) | evallgefint(3); x[2] = u; }
+}
+
+INLINE void
+affsr(long x, GEN y)
+{
+  long sh, i, ly = lg(y);
+
+  if (!x)
+  {
+    y[1] = evalexpo(-prec2nbits(ly));
+    return;
+  }
+  if (x < 0) {
+    x = -x; sh = bfffo(x);
+    y[1] = evalsigne(-1) | _evalexpo((BITS_IN_LONG-1)-sh);
+  }
+  else
+  {
+    sh = bfffo(x);
+    y[1] = evalsigne(1) | _evalexpo((BITS_IN_LONG-1)-sh);
+  }
+  y[2] = x<<sh; for (i=3; i<ly; i++) y[i]=0;
+}
+
+INLINE void
+affur(ulong x, GEN y)
+{
+  long sh, i, ly = lg(y);
+
+  if (!x)
+  {
+    y[1] = evalexpo(-prec2nbits(ly));
+    return;
+  }
+  sh = bfffo(x);
+  y[1] = evalsigne(1) | _evalexpo((BITS_IN_LONG-1)-sh);
+  y[2] = x<<sh; for (i=3; i<ly; i++) y[i] = 0;
+}
+
+INLINE void
+affiz(GEN x, GEN y) { if (typ(y)==t_INT) affii(x,y); else affir(x,y); }
+INLINE void
+affsz(long x, GEN y) { if (typ(y)==t_INT) affsi(x,y); else affsr(x,y); }
+INLINE void
+mpaff(GEN x, GEN y) { if (typ(x)==t_INT) affiz(x, y); else affrr(x,y); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                    OPERATION + ASSIGNMENT                       */
+/*                                                                 */
+/*******************************************************************/
+
+INLINE void addiiz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affii(addii(x,y),z); avma = av; }
+INLINE void addirz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(addir(x,y),z); avma = av; }
+INLINE void addriz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(addri(x,y),z); avma = av; }
+INLINE void addrrz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(addrr(x,y),z); avma = av; }
+INLINE void addsiz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affii(addsi(s,y),z); avma = av; }
+INLINE void addsrz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affrr(addsr(s,y),z); avma = av; }
+INLINE void addssz(long s, long y, GEN z)
+{ pari_sp av = avma; affii(addss(s,y),z); avma = av; }
+
+INLINE void diviiz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affii(divii(x,y),z); avma = av; }
+INLINE void divirz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(divir(x,y),z); avma = av; }
+INLINE void divisz(GEN x, long y, GEN z)
+{ pari_sp av = avma; affii(divis(x,y),z); avma = av; }
+INLINE void divriz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(divri(x,y),z); avma = av; }
+INLINE void divrrz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(divrr(x,y),z); avma = av; }
+INLINE void divrsz(GEN y, long s, GEN z)
+{ pari_sp av = avma; affrr(divrs(y,s),z); avma = av; }
+INLINE void divsiz(long x, GEN y, GEN z)
+{ long junk; affsi(sdivsi_rem(x,y,&junk), z); }
+INLINE void divsrz(long s, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(divsr(s,y),z); avma = av; }
+INLINE void divssz(long x, long y, GEN z)
+{ affsi(x/y, z); }
+
+INLINE void modisz(GEN y, long s, GEN z)
+{ pari_sp av = avma; affii(modis(y,s),z); avma = av; }
+INLINE void modsiz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affii(modsi(s,y),z); avma = av; }
+INLINE void modssz(long s, long y, GEN z)
+{ pari_sp av = avma; affii(modss(s,y),z); avma = av; }
+
+INLINE void mpaddz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(mpadd(x,y),z); avma = av; }
+INLINE void mpsubz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(mpsub(x,y),z); avma = av; }
+INLINE void mpmulz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(mpmul(x,y),z); avma = av; }
+
+INLINE void muliiz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affii(mulii(x,y),z); avma = av; }
+INLINE void mulirz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(mulir(x,y),z); avma = av; }
+INLINE void mulriz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(mulri(x,y),z); avma = av; }
+INLINE void mulrrz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(mulrr(x,y),z); avma = av; }
+INLINE void mulsiz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affii(mulsi(s,y),z); avma = av; }
+INLINE void mulsrz(long s, GEN y, GEN z)
+{ pari_sp av = avma; mpaff(mulsr(s,y),z); avma = av; }
+INLINE void mulssz(long s, long y, GEN z)
+{ pari_sp av = avma; affii(mulss(s,y),z); avma = av; }
+
+INLINE void remiiz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affii(remii(x,y),z); avma = av; }
+INLINE void remisz(GEN y, long s, GEN z)
+{ pari_sp av = avma; affii(remis(y,s),z); avma = av; }
+INLINE void remsiz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affii(remsi(s,y),z); avma = av; }
+INLINE void remssz(long s, long y, GEN z)
+{ pari_sp av = avma; affii(remss(s,y),z); avma = av; }
+
+INLINE void subiiz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affii(subii(x,y),z); avma = av; }
+INLINE void subirz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(subir(x,y),z); avma = av; }
+INLINE void subisz(GEN y, long s, GEN z)
+{ pari_sp av = avma; affii(addsi(-s,y),z); avma = av; }
+INLINE void subriz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(subri(x,y),z); avma = av; }
+INLINE void subrrz(GEN x, GEN y, GEN z)
+{ pari_sp av = avma; affrr(subrr(x,y),z); avma = av; }
+INLINE void subrsz(GEN y, long s, GEN z)
+{ pari_sp av = avma; affrr(addsr(-s,y),z); avma = av; }
+INLINE void subsiz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affii(subsi(s,y),z); avma = av; }
+INLINE void subsrz(long s, GEN y, GEN z)
+{ pari_sp av = avma; affrr(subsr(s,y),z); avma = av; }
+INLINE void subssz(long x, long y, GEN z) { addssz(x,-y,z); }
+
+INLINE void
+dvmdssz(long x, long y, GEN z, GEN t) {
+  pari_sp av = avma;
+  long r;
+  affii(divss_rem(x,y, &r), z); avma = av; affsi(r,t);
+}
+INLINE void
+dvmdsiz(long x, GEN y, GEN z, GEN t) {
+  pari_sp av = avma;
+  long r;
+  affii(divsi_rem(x,y, &r), z); avma = av; affsi(r,t);
+}
+INLINE void
+dvmdisz(GEN x, long y, GEN z, GEN t) {
+  pari_sp av = avma;
+  long r;
+  affii(divis_rem(x,y, &r),z); avma = av; affsi(r,t);
+}
+INLINE void
+dvmdiiz(GEN x, GEN y, GEN z, GEN t) {
+  pari_sp av = avma;
+  GEN r;
+  affii(dvmdii(x,y,&r),z); affii(r,t); avma=av;
+}
diff --git a/src/kernel/none/mp.c b/src/kernel/none/mp.c
new file mode 100644
index 0000000..6c060c4
--- /dev/null
+++ b/src/kernel/none/mp.c
@@ -0,0 +1,2155 @@
+#line 2 "../src/kernel/none/mp.c"
+/* Copyright (C) 2000-2003 The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/***********************************************************************/
+/**                                                                   **/
+/**                       MULTIPRECISION KERNEL                       **/
+/**                                                                   **/
+/***********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#include "../src/kernel/none/tune-gen.h"
+
+void pari_kernel_init(void) { }
+void pari_kernel_close(void) { }
+
+/* NOTE: arguments of "spec" routines (muliispec, addiispec, etc.) aren't
+ * GENs but pairs (long *a, long na) representing a list of digits (in basis
+ * BITS_IN_LONG) : a[0], ..., a[na-1]. [ In ordre to facilitate splitting: no
+ * need to reintroduce codewords ] */
+
+#define LIMBS(x)  ((x)+2)
+#define NLIMBS(x) (lgefint(x)-2)
+
+/* Normalize a non-negative integer */
+GEN
+int_normalize(GEN x, long known_zero_words)
+{
+  long i, lx = lgefint(x);
+  GEN x0;
+  if (lx == 2) { x[1] = evalsigne(0) | evallgefint(2); return x; }
+  if (!known_zero_words && x[2]) return x;
+  for (i = 2+known_zero_words; i < lx; i++)
+    if (x[i]) break;
+  x0 = x; i -= 2; x += i;
+  if (x0 == (GEN)avma) avma = (pari_sp)x;
+  else stackdummy((pari_sp)(x0+i), (pari_sp)x0);
+  lx -= i;
+  x[0] = evaltyp(t_INT) | evallg(lx);
+  if (lx == 2) x[1] = evalsigne(0) | evallgefint(lx);
+  else         x[1] = evalsigne(1) | evallgefint(lx);
+  return x;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                      ADDITION / SUBTRACTION                       **/
+/**                                                                   **/
+/***********************************************************************/
+
+GEN
+setloop(GEN a)
+{
+  pari_sp av = avma;
+  (void)cgetg(lgefint(a) + 3, t_VECSMALL);
+  return icopy_avma(a, av); /* two cells of extra space before a */
+}
+
+/* we had a = setloop(?), then some incloops. Reset a to b */
+GEN
+resetloop(GEN a, GEN b) {
+  long lb = lgefint(b);
+  a += lgefint(a) - lb;
+  a[0] = evaltyp(t_INT) | evallg(lb);
+  affii(b, a); return a;
+}
+
+/* assume a > 0, initialized by setloop. Do a++ */
+static GEN
+incpos(GEN a)
+{
+  long i, l = lgefint(a);
+  for (i=l-1; i>1; i--)
+    if (++a[i]) return a;
+  l++; a--; /* use extra cell */
+  a[0]=evaltyp(t_INT) | _evallg(l);
+  a[1]=evalsigne(1) | evallgefint(l);
+  a[2]=1; return a;
+}
+
+/* assume a < 0, initialized by setloop. Do a++ */
+static GEN
+incneg(GEN a)
+{
+  long i, l = lgefint(a)-1;
+  if (a[l]--)
+  {
+    if (l == 2 && !a[2])
+    {
+      a++; /* save one cell */
+      a[0] = evaltyp(t_INT) | _evallg(2);
+      a[1] = evalsigne(0) | evallgefint(2);
+    }
+    return a;
+  }
+  for (i = l-1;; i--) /* finishes since a[2] != 0 */
+    if (a[i]--) break;
+  if (!a[2])
+  {
+    a++; /* save one cell */
+    a[0] = evaltyp(t_INT) | _evallg(l);
+    a[1] = evalsigne(-1) | evallgefint(l);
+  }
+  return a;
+}
+
+/* assume a initialized by setloop. Do a++ */
+GEN
+incloop(GEN a)
+{
+  switch(signe(a))
+  {
+    case 0: a--; /* use extra cell */
+      a[0]=evaltyp(t_INT) | _evallg(3);
+      a[1]=evalsigne(1) | evallgefint(3);
+      a[2]=1; return a;
+    case -1: return incneg(a);
+    default: return incpos(a);
+  }
+}
+
+INLINE GEN
+adduispec(ulong s, GEN x, long nx)
+{
+  GEN xd, zd = (GEN)avma;
+  long lz;
+
+  if (nx == 1) return adduu(s, (ulong)x[0]);
+  lz = nx+3; (void)new_chunk(lz);
+  xd = x + nx;
+  *--zd = (ulong)*--xd + s;
+  if ((ulong)*zd < s)
+    for(;;)
+    {
+      if (xd == x) { *--zd = 1; break; } /* enlarge z */
+      *--zd = ((ulong)*--xd) + 1;
+      if (*zd) { lz--; break; }
+    }
+  else lz--;
+  while (xd > x) *--zd = *--xd;
+  *--zd = evalsigne(1) | evallgefint(lz);
+  *--zd = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)zd; return zd;
+}
+
+GEN
+adduispec_offset(ulong s, GEN x, long offset, long nx)
+{
+  GEN xd = x+lgefint(x)-nx-offset;
+  while (nx && *xd==0) {xd++; nx--;}
+  if (!nx) return utoi(s);
+  return adduispec(s,xd,nx);
+}
+
+static GEN
+addiispec(GEN x, GEN y, long nx, long ny)
+{
+  GEN xd, yd, zd;
+  long lz, i = -2;
+  LOCAL_OVERFLOW;
+
+  if (nx < ny) swapspec(x,y, nx,ny);
+  if (ny == 1) return adduispec(*y,x,nx);
+  zd = (GEN)avma;
+  lz = nx+3; (void)new_chunk(lz);
+  xd = x + nx;
+  yd = y + ny;
+  zd[-1] = addll(xd[-1], yd[-1]);
+#ifdef addllx8
+  for (  ; i-8 > -ny; i-=8)
+    addllx8(xd+i, yd+i, zd+i, overflow);
+#endif
+  for (  ; i >= -ny; i--) zd[i] = addllx(xd[i], yd[i]);
+  if (overflow)
+    for(;;)
+    {
+      if (i < -nx) { zd[i] = 1; i--; break; } /* enlarge z */
+      zd[i] = (ulong)xd[i] + 1;
+      if (zd[i]) { i--; lz--; break; }
+      i--;
+    }
+  else lz--;
+  for (; i >= -nx; i--) zd[i] = xd[i];
+  zd += i+1;
+  *--zd = evalsigne(1) | evallgefint(lz);
+  *--zd = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)zd; return zd;
+}
+
+/* assume x >= s */
+INLINE GEN
+subiuspec(GEN x, ulong s, long nx)
+{
+  GEN xd, zd = (GEN)avma;
+  long lz;
+  LOCAL_OVERFLOW;
+
+  if (nx == 1) return utoi(x[0] - s);
+
+  lz = nx+2; (void)new_chunk(lz);
+  xd = x + nx;
+  *--zd = subll(*--xd, s);
+  if (overflow)
+    for(;;)
+    {
+      *--zd = ((ulong)*--xd) - 1;
+      if (*xd) break;
+    }
+  if (xd == x)
+    while (*zd == 0) { zd++; lz--; } /* shorten z */
+  else
+    do  *--zd = *--xd; while (xd > x);
+  *--zd = evalsigne(1) | evallgefint(lz);
+  *--zd = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)zd; return zd;
+}
+
+/* assume x > y */
+static GEN
+subiispec(GEN x, GEN y, long nx, long ny)
+{
+  GEN xd,yd,zd;
+  long lz, i = -2;
+  LOCAL_OVERFLOW;
+
+  if (ny==1) return subiuspec(x,*y,nx);
+  zd = (GEN)avma;
+  lz = nx+2; (void)new_chunk(lz);
+  xd = x + nx;
+  yd = y + ny;
+  zd[-1] = subll(xd[-1], yd[-1]);
+#ifdef subllx8
+  for (  ; i-8 > -ny; i-=8)
+    subllx8(xd+i, yd+i, zd+i, overflow);
+#endif
+  for (  ; i >= -ny; i--) zd[i] = subllx(xd[i], yd[i]);
+  if (overflow)
+    for(;;)
+    {
+      zd[i] = ((ulong)xd[i]) - 1;
+      if (xd[i--]) break;
+    }
+  if (i>=-nx)
+    for (; i >= -nx; i--) zd[i] = xd[i];
+  else
+    while (zd[i+1] == 0) { i++; lz--; } /* shorten z */
+  zd += i+1;
+  *--zd = evalsigne(1) | evallgefint(lz);
+  *--zd = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)zd; return zd;
+}
+
+static void
+roundr_up_ip(GEN x, long l)
+{
+  long i = l;
+  for(;;)
+  {
+    if (++((ulong*)x)[--i]) break;
+    if (i == 2) { x[2] = (long)HIGHBIT; shiftr_inplace(x, 1); break; }
+  }
+}
+
+void
+affir(GEN x, GEN y)
+{
+  const long s = signe(x), ly = lg(y);
+  long lx, sh, i;
+
+  if (!s)
+  {
+    y[1] = evalexpo(-bit_accuracy(ly));
+    return;
+  }
+
+  lx = lgefint(x); sh = bfffo(x[2]);
+  y[1] = evalsigne(s) | evalexpo(bit_accuracy(lx)-sh-1);
+  if (sh) {
+    if (lx <= ly)
+    {
+      for (i=lx; i<ly; i++) y[i]=0;
+      shift_left(y,x,2,lx-1, 0,sh);
+      return;
+    }
+    shift_left(y,x,2,ly-1, x[ly],sh);
+    /* lx > ly: round properly */
+    if ((x[ly]<<sh) & HIGHBIT) roundr_up_ip(y, ly);
+  }
+  else {
+    if (lx <= ly)
+    {
+      for (i=2; i<lx; i++) y[i]=x[i];
+      for (   ; i<ly; i++) y[i]=0;
+      return;
+    }
+    for (i=2; i<ly; i++) y[i]=x[i];
+    /* lx > ly: round properly */
+    if (x[ly] & HIGHBIT) roundr_up_ip(y, ly);
+  }
+}
+
+INLINE GEN
+shiftispec(GEN x, long nx, long n)
+{
+  long ny, i, m;
+  GEN y, yd;
+  if (!n)  return icopyspec(x, nx);
+
+  if (n > 0)
+  {
+    GEN z = (GEN)avma;
+    long d = dvmdsBIL(n, &m);
+
+    ny = nx+d; y = new_chunk(ny + 2); yd = y + 2;
+    for ( ; d; d--) *--z = 0;
+    if (!m) for (i=0; i<nx; i++) yd[i]=x[i];
+    else
+    {
+      register const ulong sh = BITS_IN_LONG - m;
+      shift_left(yd,x, 0,nx-1, 0,m);
+      i = ((ulong)x[0]) >> sh;
+      /* Extend y on the left? */
+      if (i) { ny++; y = new_chunk(1); y[2] = i; }
+    }
+  }
+  else
+  {
+    ny = nx - dvmdsBIL(-n, &m);
+    if (ny<1) return gen_0;
+    y = new_chunk(ny + 2); yd = y + 2;
+    if (m) {
+      shift_right(yd,x, 0,ny, 0,m);
+      if (yd[0] == 0)
+      {
+        if (ny==1) { avma = (pari_sp)(y+3); return gen_0; }
+        ny--; avma = (pari_sp)(++y);
+      }
+    } else {
+      for (i=0; i<ny; i++) yd[i]=x[i];
+    }
+  }
+  y[1] = evalsigne(1)|evallgefint(ny + 2);
+  y[0] = evaltyp(t_INT)|evallg(ny + 2); return y;
+}
+
+GEN
+mantissa2nr(GEN x, long n)
+{ /*This is a kludge since x is not an integer*/
+  long s = signe(x);
+  GEN y;
+
+  if(s == 0) return gen_0;
+  y = shiftispec(x + 2, lg(x) - 2, n);
+  if (signe(y)) setsigne(y, s);
+  return y;
+}
+
+GEN
+truncr(GEN x)
+{
+  long d,e,i,s,m;
+  GEN y;
+
+  if ((s=signe(x)) == 0 || (e=expo(x)) < 0) return gen_0;
+  d = nbits2prec(e+1); m = remsBIL(e);
+  if (d > lg(x)) pari_err_PREC( "truncr (precision loss in truncation)");
+
+  y=cgeti(d); y[1] = evalsigne(s) | evallgefint(d);
+  if (++m == BITS_IN_LONG)
+    for (i=2; i<d; i++) y[i]=x[i];
+  else
+    shift_right(y,x, 2,d,0, BITS_IN_LONG - m);
+  return y;
+}
+
+/* integral part */
+GEN
+floorr(GEN x)
+{
+  long d,e,i,lx,m;
+  GEN y;
+
+  if (signe(x) >= 0) return truncr(x);
+  if ((e=expo(x)) < 0) return gen_m1;
+  d = nbits2prec(e+1); m = remsBIL(e);
+  lx=lg(x); if (d>lx) pari_err_PREC( "floorr (precision loss in truncation)");
+  y = new_chunk(d);
+  if (++m == BITS_IN_LONG)
+  {
+    for (i=2; i<d; i++) y[i]=x[i];
+    i=d; while (i<lx && !x[i]) i++;
+    if (i==lx) goto END;
+  }
+  else
+  {
+    shift_right(y,x, 2,d,0, BITS_IN_LONG - m);
+    if (x[d-1]<<m == 0)
+    {
+      i=d; while (i<lx && !x[i]) i++;
+      if (i==lx) goto END;
+    }
+  }
+  /* set y:=y+1 */
+  for (i=d-1; i>=2; i--) { y[i]++; if (y[i]) goto END; }
+  y=new_chunk(1); y[2]=1; d++;
+END:
+  y[1] = evalsigne(-1) | evallgefint(d);
+  y[0] = evaltyp(t_INT) | evallg(d); return y;
+}
+
+INLINE int
+cmpiispec(GEN x, GEN y, long lx, long ly)
+{
+  long i;
+  if (lx < ly) return -1;
+  if (lx > ly) return  1;
+  i = 0; while (i<lx && x[i]==y[i]) i++;
+  if (i==lx) return 0;
+  return ((ulong)x[i] > (ulong)y[i])? 1: -1;
+}
+
+INLINE int
+equaliispec(GEN x, GEN y, long lx, long ly)
+{
+  long i;
+  if (lx != ly) return 0;
+  i = ly-1; while (i>=0 && x[i]==y[i]) i--;
+  return i < 0;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          MULTIPLICATION                           **/
+/**                                                                   **/
+/***********************************************************************/
+/* assume ny > 0 */
+INLINE GEN
+muluispec(ulong x, GEN y, long ny)
+{
+  GEN yd, z = (GEN)avma;
+  long lz = ny+3;
+  LOCAL_HIREMAINDER;
+
+  (void)new_chunk(lz);
+  yd = y + ny; *--z = mulll(x, *--yd);
+  while (yd > y) *--z = addmul(x,*--yd);
+  if (hiremainder) *--z = hiremainder; else lz--;
+  *--z = evalsigne(1) | evallgefint(lz);
+  *--z = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)z; return z;
+}
+
+/* a + b*|Y| */
+GEN
+addumului(ulong a, ulong b, GEN Y)
+{
+  GEN yd,y,z;
+  long ny,lz;
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  if (!signe(Y)) return utoi(a);
+
+  y = LIMBS(Y); z = (GEN)avma;
+  ny = NLIMBS(Y);
+  lz = ny+3;
+
+  (void)new_chunk(lz);
+  yd = y + ny; *--z = addll(a, mulll(b, *--yd));
+  if (overflow) hiremainder++; /* can't overflow */
+  while (yd > y) *--z = addmul(b,*--yd);
+  if (hiremainder) *--z = hiremainder; else lz--;
+  *--z = evalsigne(1) | evallgefint(lz);
+  *--z = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)z; return z;
+}
+
+/***********************************************************************/
+/**                                                                   **/
+/**                          DIVISION                                 **/
+/**                                                                   **/
+/***********************************************************************/
+
+ulong
+umodiu(GEN y, ulong x)
+{
+  long sy=signe(y),ly,i;
+  LOCAL_HIREMAINDER;
+
+  if (!x) pari_err_INV("umodiu",gen_0);
+  if (!sy) return 0;
+  ly = lgefint(y);
+  if (x <= (ulong)y[2]) hiremainder=0;
+  else
+  {
+    if (ly==3) return (sy > 0)? (ulong)y[2]: x - (ulong)y[2];
+    hiremainder=y[2]; ly--; y++;
+  }
+  for (i=2; i<ly; i++) (void)divll(y[i],x);
+  if (!hiremainder) return 0;
+  return (sy > 0)? hiremainder: x - hiremainder;
+}
+
+/* return |y| \/ x */
+GEN
+diviu_rem(GEN y, ulong x, ulong *rem)
+{
+  long ly,i;
+  GEN z;
+  LOCAL_HIREMAINDER;
+
+  if (!x) pari_err_INV("diviu_rem",gen_0);
+  if (!signe(y)) { *rem = 0; return gen_0; }
+
+  ly = lgefint(y);
+  if (x <= (ulong)y[2]) hiremainder=0;
+  else
+  {
+    if (ly==3) { *rem = (ulong)y[2]; return gen_0; }
+    hiremainder=y[2]; ly--; y++;
+  }
+  z = cgetipos(ly);
+  for (i=2; i<ly; i++) z[i]=divll(y[i],x);
+  *rem = hiremainder; return z;
+}
+
+GEN
+divis_rem(GEN y, long x, long *rem)
+{
+  long sy=signe(y),ly,s,i;
+  GEN z;
+  LOCAL_HIREMAINDER;
+
+  if (!x) pari_err_INV("divis_rem",gen_0);
+  if (!sy) { *rem=0; return gen_0; }
+  if (x<0) { s = -sy; x = -x; } else s = sy;
+
+  ly = lgefint(y);
+  if ((ulong)x <= (ulong)y[2]) hiremainder=0;
+  else
+  {
+    if (ly==3) { *rem = itos(y); return gen_0; }
+    hiremainder=y[2]; ly--; y++;
+  }
+  z = cgeti(ly); z[1] = evallgefint(ly) | evalsigne(s);
+  for (i=2; i<ly; i++) z[i]=divll(y[i],x);
+  if (sy<0) hiremainder = - ((long)hiremainder);
+  *rem = (long)hiremainder; return z;
+}
+
+GEN
+divis(GEN y, long x)
+{
+  long sy=signe(y),ly,s,i;
+  GEN z;
+  LOCAL_HIREMAINDER;
+
+  if (!x) pari_err_INV("divis",gen_0);
+  if (!sy) return gen_0;
+  if (x<0) { s = -sy; x = -x; } else s = sy;
+
+  ly = lgefint(y);
+  if ((ulong)x <= (ulong)y[2]) hiremainder=0;
+  else
+  {
+    if (ly==3) return gen_0;
+    hiremainder=y[2]; ly--; y++;
+  }
+  z = cgeti(ly); z[1] = evallgefint(ly) | evalsigne(s);
+  for (i=2; i<ly; i++) z[i]=divll(y[i],x);
+  return z;
+}
+
+GEN
+divrr(GEN x, GEN y)
+{
+  long sx=signe(x), sy=signe(y), lx,ly,lr,e,i,j;
+  ulong y0,y1;
+  GEN r, r1;
+
+  if (!x) pari_err_INV("divrr",y);
+  e = expo(x) - expo(y);
+  if (!sx) return real_0_bit(e);
+  if (sy<0) sx = -sx;
+
+  lx=lg(x); ly=lg(y);
+  if (ly==3)
+  {
+    ulong k = x[2], l = (lx>3)? x[3]: 0;
+    LOCAL_HIREMAINDER;
+    if (k < (ulong)y[2]) e--;
+    else
+    {
+      l >>= 1; if (k&1) l |= HIGHBIT;
+      k >>= 1;
+    }
+    hiremainder = k; k = divll(l,y[2]);
+    if (hiremainder & HIGHBIT)
+    {
+      k++;
+      if (!k) { k = HIGHBIT; e++; }
+    }
+    r = cgetr(3);
+    r[1] = evalsigne(sx) | evalexpo(e);
+    r[2] = k; return r;
+  }
+
+  lr = minss(lx,ly); r = new_chunk(lr);
+  r1 = r-1;
+  r1[1] = 0; for (i=2; i<lr; i++) r1[i]=x[i];
+  r1[lr] = (lx>ly)? x[lr]: 0;
+  y0 = y[2]; y1 = y[3];
+  for (i=0; i<lr-1; i++)
+  { /* r1 = r + (i-1), OK up to r1[2] (accesses at most r[lr]) */
+    ulong k, qp;
+    LOCAL_HIREMAINDER;
+    LOCAL_OVERFLOW;
+
+    if ((ulong)r1[1] == y0)
+    {
+      qp = ULONG_MAX; k = addll(y0,r1[2]);
+    }
+    else
+    {
+      if ((ulong)r1[1] > y0) /* can't happen if i=0 */
+      {
+        GEN y1 = y+1;
+        j = lr-i; r1[j] = subll(r1[j],y1[j]);
+        for (j--; j>0; j--) r1[j] = subllx(r1[j],y1[j]);
+        j=i; do ((ulong*)r)[--j]++; while (j && !r[j]);
+      }
+      hiremainder = r1[1]; overflow = 0;
+      qp = divll(r1[2],y0); k = hiremainder;
+    }
+    j = lr-i+1;
+    if (!overflow)
+    {
+      long k3, k4;
+      k3 = mulll(qp,y1);
+      if (j == 3) /* i = lr - 2 maximal, r1[3] undefined -> 0 */
+        k4 = subll(hiremainder,k);
+      else
+      {
+        k3 = subll(k3, r1[3]);
+        k4 = subllx(hiremainder,k);
+      }
+      while (!overflow && k4) { qp--; k3 = subll(k3,y1); k4 = subllx(k4,y0); }
+    }
+    if (j<ly) (void)mulll(qp,y[j]); else { hiremainder = 0 ; j = ly; }
+    for (j--; j>1; j--)
+    {
+      r1[j] = subll(r1[j], addmul(qp,y[j]));
+      hiremainder += overflow;
+    }
+    if ((ulong)r1[1] != hiremainder)
+    {
+      if ((ulong)r1[1] < hiremainder)
+      {
+        qp--;
+        j = lr-i-(lr-i>=ly); r1[j] = addll(r1[j], y[j]);
+        for (j--; j>1; j--) r1[j] = addllx(r1[j], y[j]);
+      }
+      else
+      {
+        r1[1] -= hiremainder;
+        while (r1[1])
+        {
+          qp++; if (!qp) { j=i; do ((ulong*)r)[--j]++; while (j && !r[j]); }
+          j = lr-i-(lr-i>=ly); r1[j] = subll(r1[j],y[j]);
+          for (j--; j>1; j--) r1[j] = subllx(r1[j],y[j]);
+          r1[1] -= overflow;
+        }
+      }
+    }
+    *++r1 = qp;
+  }
+  /* i = lr-1 */
+  /* round correctly */
+  if ((ulong)r1[1] > (y0>>1))
+  {
+    j=i; do ((ulong*)r)[--j]++; while (j && !r[j]);
+  }
+  r1 = r-1; for (j=i; j>=2; j--) r[j]=r1[j];
+  if (r[0] == 0) e--;
+  else if (r[0] == 1) { shift_right(r,r, 2,lr, 1,1); }
+  else { /* possible only when rounding up to 0x2 0x0 ... */
+    r[2] = (long)HIGHBIT; e++;
+  }
+  r[0] = evaltyp(t_REAL)|evallg(lr);
+  r[1] = evalsigne(sx) | evalexpo(e);
+  return r;
+}
+
+GEN
+divri(GEN x, GEN y)
+{
+  long lx, s = signe(y);
+  pari_sp av;
+  GEN z;
+
+  if (!s) pari_err_INV("divri",y);
+  if (!signe(x)) return real_0_bit(expo(x) - expi(y));
+  if (!is_bigint(y)) {
+    GEN z = divru(x, y[2]);
+    if (s < 0) togglesign(z);
+    return z;
+  }
+  lx = lg(x); z = cgetr(lx); av = avma;
+  affrr(divrr(x, itor(y, lx+1)), z);
+  avma = av; return z;
+}
+
+/* Integer division x / y: such that sign(r) = sign(x)
+ *   if z = ONLY_REM return remainder, otherwise return quotient
+ *   if z != NULL set *z to remainder
+ *   *z is the last object on stack (and thus can be disposed of with cgiv
+ *   instead of gerepile)
+ * If *z is zero, we put gen_0 here and no copy.
+ * space needed: lx + ly */
+GEN
+dvmdii(GEN x, GEN y, GEN *z)
+{
+  long sx=signe(x),sy=signe(y);
+  long lx, ly, lz, i, j, sh, lq, lr;
+  pari_sp av;
+  ulong y0,y1, *xd,*rd,*qd;
+  GEN q, r, r1;
+
+  if (!sy)
+  {
+    if (z == ONLY_REM && !sx) return gen_0;
+    if (!sy) pari_err_INV("dvmdii",gen_0);
+  }
+  if (!sx)
+  {
+    if (!z || z == ONLY_REM) return gen_0;
+    *z=gen_0; return gen_0;
+  }
+  lx=lgefint(x);
+  ly=lgefint(y); lz=lx-ly;
+  if (lz <= 0)
+  {
+    if (lz == 0)
+    {
+      for (i=2; i<lx; i++)
+        if (x[i] != y[i])
+        {
+          if ((ulong)x[i] > (ulong)y[i]) goto DIVIDE;
+          goto TRIVIAL;
+        }
+      if (z == ONLY_REM) return gen_0;
+      if (z) *z = gen_0;
+      if (sx < 0) sy = -sy;
+      return stoi(sy);
+    }
+TRIVIAL:
+    if (z == ONLY_REM) return icopy(x);
+    if (z) *z = icopy(x);
+    return gen_0;
+  }
+DIVIDE: /* quotient is non-zero */
+  av=avma; if (sx<0) sy = -sy;
+  if (ly==3)
+  {
+    LOCAL_HIREMAINDER;
+    y0 = y[2];
+    if (y0 <= (ulong)x[2]) hiremainder=0;
+    else
+    {
+      hiremainder = x[2]; lx--; x++;
+    }
+    q = new_chunk(lx); for (i=2; i<lx; i++) q[i]=divll(x[i],y0);
+    if (z == ONLY_REM)
+    {
+      avma=av; if (!hiremainder) return gen_0;
+      r=cgeti(3);
+      r[1] = evalsigne(sx) | evallgefint(3);
+      r[2]=hiremainder; return r;
+    }
+    q[1] = evalsigne(sy) | evallgefint(lx);
+    q[0] = evaltyp(t_INT) | evallg(lx);
+    if (!z) return q;
+    if (!hiremainder) { *z=gen_0; return q; }
+    r=cgeti(3);
+    r[1] = evalsigne(sx) | evallgefint(3);
+    r[2] = hiremainder; *z=r; return q;
+  }
+
+  r1 = new_chunk(lx); sh = bfffo(y[2]);
+  if (sh)
+  { /* normalize so that highbit(y) = 1 (shift left x and y by sh bits)*/
+    register const ulong m = BITS_IN_LONG - sh;
+    r = new_chunk(ly);
+    shift_left(r, y,2,ly-1, 0,sh); y = r;
+    shift_left(r1,x,2,lx-1, 0,sh);
+    r1[1] = ((ulong)x[2]) >> m;
+  }
+  else
+  {
+    r1[1] = 0; for (j=2; j<lx; j++) r1[j] = x[j];
+  }
+  x = r1;
+  y0 = y[2]; y1 = y[3];
+  for (i=0; i<=lz; i++)
+  { /* r1 = x + i */
+    ulong k, qp;
+    LOCAL_HIREMAINDER;
+    LOCAL_OVERFLOW;
+
+    if ((ulong)r1[1] == y0)
+    {
+      qp = ULONG_MAX; k = addll(y0,r1[2]);
+    }
+    else
+    {
+      hiremainder = r1[1]; overflow = 0;
+      qp = divll(r1[2],y0); k = hiremainder;
+    }
+    if (!overflow)
+    {
+      long k3 = subll(mulll(qp,y1), r1[3]);
+      long k4 = subllx(hiremainder,k);
+      while (!overflow && k4) { qp--; k3 = subll(k3,y1); k4 = subllx(k4,y0); }
+    }
+    hiremainder = 0; j = ly;
+    for (j--; j>1; j--)
+    {
+      r1[j] = subll(r1[j], addmul(qp,y[j]));
+      hiremainder += overflow;
+    }
+    if ((ulong)r1[1] < hiremainder)
+    {
+      qp--;
+      j = ly-1; r1[j] = addll(r1[j],y[j]);
+      for (j--; j>1; j--) r1[j] = addllx(r1[j],y[j]);
+    }
+    *++r1 = qp;
+  }
+
+  lq = lz+2;
+  if (!z)
+  {
+    qd = (ulong*)av;
+    xd = (ulong*)(x + lq);
+    if (x[1]) { lz++; lq++; }
+    while (lz--) *--qd = *--xd;
+    *--qd = evalsigne(sy) | evallgefint(lq);
+    *--qd = evaltyp(t_INT) | evallg(lq);
+    avma = (pari_sp)qd; return (GEN)qd;
+  }
+
+  j=lq; while (j<lx && !x[j]) j++;
+  lz = lx-j;
+  if (z == ONLY_REM)
+  {
+    if (lz==0) { avma = av; return gen_0; }
+    rd = (ulong*)av; lr = lz+2;
+    xd = (ulong*)(x + lx);
+    if (!sh) while (lz--) *--rd = *--xd;
+    else
+    { /* shift remainder right by sh bits */
+      const ulong shl = BITS_IN_LONG - sh;
+      ulong l;
+      xd--;
+      while (--lz) /* fill r[3..] */
+      {
+        l = *xd >> sh;
+        *--rd = l | (*--xd << shl);
+      }
+      l = *xd >> sh;
+      if (l) *--rd = l; else lr--;
+    }
+    *--rd = evalsigne(sx) | evallgefint(lr);
+    *--rd = evaltyp(t_INT) | evallg(lr);
+    avma = (pari_sp)rd; return (GEN)rd;
+  }
+
+  lr = lz+2;
+  rd = NULL; /* gcc -Wall */
+  if (lz)
+  { /* non zero remainder: initialize rd */
+    xd = (ulong*)(x + lx);
+    if (!sh)
+    {
+      rd = (ulong*)avma; (void)new_chunk(lr);
+      while (lz--) *--rd = *--xd;
+    }
+    else
+    { /* shift remainder right by sh bits */
+      const ulong shl = BITS_IN_LONG - sh;
+      ulong l;
+      rd = (ulong*)x; /* overwrite shifted y */
+      xd--;
+      while (--lz)
+      {
+        l = *xd >> sh;
+        *--rd = l | (*--xd << shl);
+      }
+      l = *xd >> sh;
+      if (l) *--rd = l; else lr--;
+    }
+    *--rd = evalsigne(sx) | evallgefint(lr);
+    *--rd = evaltyp(t_INT) | evallg(lr);
+    rd += lr;
+  }
+  qd = (ulong*)av;
+  xd = (ulong*)(x + lq);
+  if (x[1]) lq++;
+  j = lq-2; while (j--) *--qd = *--xd;
+  *--qd = evalsigne(sy) | evallgefint(lq);
+  *--qd = evaltyp(t_INT) | evallg(lq);
+  q = (GEN)qd;
+  if (lr==2) *z = gen_0;
+  else
+  { /* rd has been properly initialized: we had lz > 0 */
+    while (lr--) *--qd = *--rd;
+    *z = (GEN)qd;
+  }
+  avma = (pari_sp)qd; return q;
+}
+
+/* Montgomery reduction.
+ * N has k words, assume T >= 0 has less than 2k.
+ * Return res := T / B^k mod N, where B = 2^BIL
+ * such that 0 <= res < T/B^k + N  and  res has less than k words */
+GEN
+red_montgomery(GEN T, GEN N, ulong inv)
+{
+  pari_sp av;
+  GEN Te, Td, Ne, Nd, scratch;
+  ulong i, j, m, t, d, k = NLIMBS(N);
+  int carry;
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  if (k == 0) return gen_0;
+  d = NLIMBS(T); /* <= 2*k */
+  if (d == 0) return gen_0;
+#ifdef DEBUG
+  if (d > 2*k) pari_err_BUG("red_montgomery");
+#endif
+  if (k == 1)
+  { /* as below, special cased for efficiency */
+    ulong n = (ulong)N[2];
+    if (d == 1) {
+      hiremainder = (ulong)T[2];
+      m = hiremainder * inv;
+      (void)addmul(m, n); /* t + m*n = 0 */
+      return utoi(hiremainder);
+    } else { /* d = 2 */
+      hiremainder = (ulong)T[3];
+      m = hiremainder * inv;
+      (void)addmul(m, n); /* t + m*n = 0 */
+      t = addll(hiremainder, (ulong)T[2]);
+      if (overflow) t -= n; /* t > n doesn't fit in 1 word */
+      return utoi(t);
+    }
+  }
+  /* assume k >= 2 */
+  av = avma; scratch = new_chunk(k<<1); /* >= k + 2: result fits */
+
+  /* copy T to scratch space (pad with zeroes to 2k words) */
+  Td = (GEN)av;
+  Te = T + (d+2);
+  for (i=0; i < d     ; i++) *--Td = *--Te;
+  for (   ; i < (k<<1); i++) *--Td = 0;
+
+  Te = (GEN)av; /* 1 beyond end of current T mantissa (in scratch) */
+  Ne = N + k+2; /* 1 beyond end of N mantissa */
+
+  carry = 0;
+  for (i=0; i<k; i++) /* set T := T/B nod N, k times */
+  {
+    Td = Te; /* one beyond end of (new) T mantissa */
+    Nd = Ne;
+    hiremainder = *--Td;
+    m = hiremainder * inv; /* solve T + m N = O(B) */
+
+    /* set T := (T + mN) / B */
+    Te = Td;
+    (void)addmul(m, *--Nd); /* = 0 */
+    for (j=1; j<k; j++)
+    {
+      t = addll(addmul(m, *--Nd), *--Td);
+      *Td = t;
+      hiremainder += overflow;
+    }
+    t = addll(hiremainder, *--Td); *Td = t + carry;
+    carry = (overflow || (carry && *Td == 0));
+  }
+  if (carry)
+  { /* Td > N overflows (k+1 words), set Td := Td - N */
+    Td = Te;
+    Nd = Ne;
+    t = subll(*--Td, *--Nd); *Td = t;
+    while (Td > scratch) { t = subllx(*--Td, *--Nd); *Td = t; }
+  }
+
+  /* copy result */
+  Td = (GEN)av;
+  while (*scratch == 0 && Te > scratch) scratch++; /* strip leading 0s */
+  while (Te > scratch) *--Td = *--Te;
+  k = (GEN)av - Td; if (!k) { avma = av; return gen_0; }
+  k += 2;
+  *--Td = evalsigne(1) | evallgefint(k);
+  *--Td = evaltyp(t_INT) | evallg(k);
+#ifdef DEBUG
+{
+  long l = NLIMBS(N), s = BITS_IN_LONG*l;
+  GEN R = int2n(s);
+  GEN res = remii(mulii(T, Fp_inv(R, N)), N);
+  if (k > lgefint(N)
+    || !equalii(remii(Td,N),res)
+    || cmpii(Td, addii(shifti(T, -s), N)) >= 0) pari_err_BUG("red_montgomery");
+}
+#endif
+  avma = (pari_sp)Td; return Td;
+}
+
+/* EXACT INTEGER DIVISION */
+
+/* assume xy>0, the division is exact and y is odd. Destroy x */
+static GEN
+diviuexact_i(GEN x, ulong y)
+{
+  long i, lz, lx;
+  ulong q, yinv;
+  GEN z, z0, x0, x0min;
+
+  if (y == 1) return icopy(x);
+  lx = lgefint(x);
+  if (lx == 3) return utoipos((ulong)x[2] / y);
+  yinv = invmod2BIL(y);
+  lz = (y <= (ulong)x[2]) ? lx : lx-1;
+  z = new_chunk(lz);
+  z0 = z + lz;
+  x0 = x + lx; x0min = x + lx-lz+2;
+
+  while (x0 > x0min)
+  {
+    *--z0 = q = yinv*((ulong)*--x0); /* i-th quotient */
+    if (!q) continue;
+    /* x := x - q * y */
+    { /* update neither lowest word (could set it to 0) nor highest ones */
+      register GEN x1 = x0 - 1;
+      LOCAL_HIREMAINDER;
+      (void)mulll(q,y);
+      if (hiremainder)
+      {
+        if ((ulong)*x1 < hiremainder)
+        {
+          *x1 -= hiremainder;
+          do (*--x1)--; while ((ulong)*x1 == ULONG_MAX);
+        }
+        else
+          *x1 -= hiremainder;
+      }
+    }
+  }
+  i=2; while(!z[i]) i++;
+  z += i-2; lz -= i-2;
+  z[0] = evaltyp(t_INT)|evallg(lz);
+  z[1] = evalsigne(1)|evallg(lz);
+  avma = (pari_sp)z; return z;
+}
+
+/* assume y != 0 and the division is exact */
+GEN
+diviuexact(GEN x, ulong y)
+{
+  pari_sp av;
+  long lx, vy, s = signe(x);
+  GEN z;
+
+  if (!s) return gen_0;
+  if (y == 1) return icopy(x);
+  lx = lgefint(x);
+  if (lx == 3) {
+    ulong q = (ulong)x[2] / y;
+    return (s > 0)? utoipos(q): utoineg(q);
+  }
+  av = avma; (void)new_chunk(lx); vy = vals(y);
+  if (vy) {
+    y >>= vy;
+    if (y == 1) { avma = av; return shifti(x, -vy); }
+    x = shifti(x, -vy);
+    if (lx == 3) {
+      ulong q = (ulong)x[2] / y;
+      avma = av;
+      return (s > 0)? utoipos(q): utoineg(q);
+    }
+  } else x = icopy(x);
+  avma = av;
+  z = diviuexact_i(x, y);
+  setsigne(z, s); return z;
+}
+
+/* Find z such that x=y*z, knowing that y | x (unchecked)
+ * Method: y0 z0 = x0 mod B = 2^BITS_IN_LONG ==> z0 = 1/y0 mod B.
+ *    Set x := (x - z0 y) / B, updating only relevant words, and repeat */
+GEN
+diviiexact(GEN x, GEN y)
+{
+  long lx, ly, lz, vy, i, ii, sx = signe(x), sy = signe(y);
+  pari_sp av;
+  ulong y0inv,q;
+  GEN z;
+
+  if (!sy) pari_err_INV("diviiexact",gen_0);
+  if (!sx) return gen_0;
+  lx = lgefint(x);
+  if (lx == 3) {
+    q = (ulong)x[2] / (ulong)y[2];
+    return (sx+sy) ? utoipos(q): utoineg(q);
+  }
+  vy = vali(y); av = avma;
+  (void)new_chunk(lx); /* enough room for z */
+  if (vy)
+  { /* make y odd */
+    y = shifti(y,-vy);
+    x = shifti(x,-vy); lx = lgefint(x);
+  }
+  else x = icopy(x); /* necessary because we destroy x */
+  avma = av; /* will erase our x,y when exiting */
+  /* now y is odd */
+  ly = lgefint(y);
+  if (ly == 3)
+  {
+    x = diviuexact_i(x,(ulong)y[2]); /* x != 0 */
+    setsigne(x, (sx+sy)? 1: -1); return x;
+  }
+  y0inv = invmod2BIL(y[ly-1]);
+  i=2; while (i<ly && y[i]==x[i]) i++;
+  lz = (i==ly || (ulong)y[i] < (ulong)x[i]) ? lx-ly+3 : lx-ly+2;
+  z = new_chunk(lz);
+
+  y += ly - 1; /* now y[-i] = i-th word of y */
+  for (ii=lx-1,i=lz-1; i>=2; i--,ii--)
+  {
+    long limj;
+    LOCAL_HIREMAINDER;
+    LOCAL_OVERFLOW;
+
+    z[i] = q = y0inv*((ulong)x[ii]); /* i-th quotient */
+    if (!q) continue;
+
+    /* x := x - q * y */
+    (void)mulll(q,y[0]); limj = maxss(lx - lz, ii+3-ly);
+    { /* update neither lowest word (could set it to 0) nor highest ones */
+      register GEN x0 = x + (ii - 1), y0 = y - 1, xlim = x + limj;
+      for (; x0 >= xlim; x0--, y0--)
+      {
+        *x0 = subll(*x0, addmul(q,*y0));
+        hiremainder += overflow;
+      }
+      if (hiremainder && limj != lx - lz)
+      {
+        if ((ulong)*x0 < hiremainder)
+        {
+          *x0 -= hiremainder;
+          do (*--x0)--; while ((ulong)*x0 == ULONG_MAX);
+        }
+        else
+          *x0 -= hiremainder;
+      }
+    }
+  }
+  i=2; while(!z[i]) i++;
+  z += i-2; lz -= (i-2);
+  z[0] = evaltyp(t_INT)|evallg(lz);
+  z[1] = evalsigne((sx+sy)? 1: -1) | evallg(lz);
+  avma = (pari_sp)z; return z;
+}
+
+/* assume yz != and yz | x */
+GEN
+diviuuexact(GEN x, ulong y, ulong z)
+{
+  long tmp[4];
+  ulong t;
+  LOCAL_HIREMAINDER;
+  t = mulll(y, z);
+  if (!hiremainder) return diviuexact(x, t);
+  tmp[0] = evaltyp(t_INT)|_evallg(4);
+  tmp[1] = evalsigne(1)|evallgefint(4);
+  tmp[2] = hiremainder;
+  tmp[3] = t;
+  return diviiexact(x, tmp);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**               INTEGER MULTIPLICATION (BASECASE)                **/
+/**                                                                **/
+/********************************************************************/
+/* nx >= ny = num. of digits of x, y (not GEN, see mulii) */
+INLINE GEN
+muliispec_basecase(GEN x, GEN y, long nx, long ny)
+{
+  GEN z2e,z2d,yd,xd,ye,zd;
+  long p1,lz;
+  LOCAL_HIREMAINDER;
+
+  if (ny == 1) return muluispec((ulong)*y, x, nx);
+  if (ny == 0) return gen_0;
+  zd = (GEN)avma; lz = nx+ny+2;
+  (void)new_chunk(lz);
+  xd = x + nx;
+  yd = y + ny;
+  ye = yd; p1 = *--xd;
+
+  *--zd = mulll(p1, *--yd); z2e = zd;
+  while (yd > y) *--zd = addmul(p1, *--yd);
+  *--zd = hiremainder;
+
+  while (xd > x)
+  {
+    LOCAL_OVERFLOW;
+    yd = ye; p1 = *--xd;
+
+    z2d = --z2e;
+    *z2d = addll(mulll(p1, *--yd), *z2d); z2d--;
+    while (yd > y)
+    {
+      hiremainder += overflow;
+      *z2d = addll(addmul(p1, *--yd), *z2d); z2d--;
+    }
+    *--zd = hiremainder + overflow;
+  }
+  if (*zd == 0) { zd++; lz--; } /* normalize */
+  *--zd = evalsigne(1) | evallgefint(lz);
+  *--zd = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)zd; return zd;
+}
+
+INLINE GEN
+sqrispec_basecase(GEN x, long nx)
+{
+  GEN z2e,z2d,yd,xd,zd,x0,z0;
+  long p1,lz;
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  if (nx == 1) return sqru((ulong)*x);
+  if (nx == 0) return gen_0;
+  zd = (GEN)avma; lz = (nx+1) << 1;
+  z0 = new_chunk(lz);
+  if (nx == 1)
+  {
+    *--zd = mulll(*x, *x);
+    *--zd = hiremainder; goto END;
+  }
+  xd = x + nx;
+
+  /* compute double products --> zd */
+  p1 = *--xd; yd = xd; --zd;
+  *--zd = mulll(p1, *--yd); z2e = zd;
+  while (yd > x) *--zd = addmul(p1, *--yd);
+  *--zd = hiremainder;
+
+  x0 = x+1;
+  while (xd > x0)
+  {
+    LOCAL_OVERFLOW;
+    p1 = *--xd; yd = xd;
+
+    z2e -= 2; z2d = z2e;
+    *z2d = addll(mulll(p1, *--yd), *z2d); z2d--;
+    while (yd > x)
+    {
+      hiremainder += overflow;
+      *z2d = addll(addmul(p1, *--yd), *z2d); z2d--;
+    }
+    *--zd = hiremainder + overflow;
+  }
+  /* multiply zd by 2 (put result in zd - 1) */
+  zd[-1] = ((*zd & HIGHBIT) != 0);
+  shift_left(zd, zd, 0, (nx<<1)-3, 0, 1);
+
+  /* add the squares */
+  xd = x + nx; zd = z0 + lz;
+  p1 = *--xd;
+  zd--; *zd = mulll(p1,p1);
+  zd--; *zd = addll(hiremainder, *zd);
+  while (xd > x)
+  {
+    p1 = *--xd;
+    zd--; *zd = addll(mulll(p1,p1)+ overflow, *zd);
+    zd--; *zd = addll(hiremainder + overflow, *zd);
+  }
+
+END:
+  if (*zd == 0) { zd++; lz--; } /* normalize */
+  *--zd = evalsigne(1) | evallgefint(lz);
+  *--zd = evaltyp(t_INT) | evallg(lz);
+  avma=(pari_sp)zd; return zd;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**               INTEGER MULTIPLICATION (FFT)                     **/
+/**                                                                **/
+/********************************************************************/
+
+/*
+ Compute parameters for FFT:
+   len: result length
+   k: FFT depth.
+   n: number of blocks (2^k)
+   bs: block size
+   mod: Modulus is M=2^(BIL*mod)+1
+   ord: order of 2 in Z/MZ.
+ We must have:
+   bs*n >= l
+   2^(BIL*mod) > nb*2^(2*BIL*bs)
+   2^k | 2*BIL*mod
+*/
+static void
+mulliifft_params(long len, long *k, long *mod, long *bs, long *n, ulong *ord)
+{
+  long r;
+  *k = expu((3*len)>>2)-3;
+  do {
+    (*k)--;
+    r  = *k-(TWOPOTBITS_IN_LONG+2);
+    *n = 1L<<*k;
+    *bs = (len+*n-1)>>*k;
+    *mod= 2**bs+1;
+    if (r>0)
+      *mod=((*mod+(1L<<r)-1)>>r)<<r;
+  } while(*mod>=3**bs);
+  *ord= 4**mod*BITS_IN_LONG;
+}
+
+/* Zf_: arithmetic in ring Z/MZ where M= 2^(BITS_IN_LONG*mod)+1
+ * for some mod.
+ * Do not garbage collect.
+ */
+
+static GEN
+Zf_add(GEN a, GEN b, GEN M)
+{
+  GEN y, z = addii(a,b);
+  long mod = lgefint(M)-3;
+  long l = NLIMBS(z);
+  if (l<=mod) return z;
+  y = subis(z, 1);
+  if (NLIMBS(y)<=mod) return z;
+  return int_normalize(y,1);
+}
+
+static GEN
+Zf_sub(GEN a, GEN b, GEN M)
+{
+  GEN z = subii(a,b);
+  return signe(z)>=0? z: addii(M,z);
+}
+
+/* destroy z */
+static GEN
+Zf_red_destroy(GEN z, GEN M)
+{
+  long mod = lgefint(M)-3;
+  long l = NLIMBS(z);
+  GEN y;
+  if (l<=mod) return z;
+  y = shifti(z, -mod*BITS_IN_LONG);
+  z = int_normalize(z, NLIMBS(y));
+  y = Zf_red_destroy(y, M);
+  z = subii(z, y);
+  if (signe(z)<0) z = addii(z, M);
+  return z;
+}
+
+INLINE GEN
+Zf_shift(GEN a, ulong s, GEN M) { return Zf_red_destroy(shifti(a, s), M); }
+
+/*
+ Multiply by sqrt(2)^s
+ We use the formula sqrt(2)=z_8*(1-z_4)) && z_8=2^(ord/16) [2^(ord/4)+1]
+*/
+
+static GEN
+Zf_mulsqrt2(GEN a, ulong s, ulong ord, GEN M)
+{
+  ulong hord = ord>>1;
+  if (!signe(a)) return gen_0;
+  if (odd(s)) /* Multiply by 2^(s/2) */
+  {
+    GEN az8  = Zf_shift(a,   ord>>4, M);
+    GEN az83 = Zf_shift(az8, ord>>3, M);
+    a = Zf_sub(az8, az83, M);
+    s--;
+  }
+  if (s < hord)
+    return Zf_shift(a, s>>1, M);
+  else
+    return subii(M,Zf_shift(a, (s-hord)>>1, M));
+}
+
+INLINE GEN
+Zf_sqr(GEN a, GEN M) { return Zf_red_destroy(sqri(a), M); }
+
+INLINE GEN
+Zf_mul(GEN a, GEN b, GEN M) { return Zf_red_destroy(mulii(a,b), M); }
+
+/* In place, bit reversing FFT */
+static void
+muliifft_dit(ulong o, ulong ord, GEN M, GEN FFT, long d, long step)
+{
+  pari_sp av = avma;
+  long i;
+  ulong j, no = (o<<1)%ord;
+  long hstep=step>>1;
+  for (i = d+1, j = 0; i <= d+hstep; ++i, j =(j+o)%ord)
+  {
+    GEN a = Zf_add(gel(FFT,i), gel(FFT,i+hstep), M);
+    GEN b = Zf_mulsqrt2(Zf_sub(gel(FFT,i), gel(FFT,i+hstep), M), j, ord, M);
+    affii(a,gel(FFT,i));
+    affii(b,gel(FFT,i+hstep));
+    avma = av;
+  }
+  if (hstep>1)
+  {
+    muliifft_dit(no, ord, M, FFT, d, hstep);
+    muliifft_dit(no, ord, M, FFT, d+hstep, hstep);
+  }
+}
+
+/* In place, bit reversed FFT, inverse of muliifft_dit */
+static void
+muliifft_dis(ulong o, ulong ord, GEN M, GEN FFT, long d, long step)
+{
+  pari_sp av = avma;
+  long i;
+  ulong j, no = (o<<1)%ord;
+  long hstep=step>>1;
+  if (hstep>1)
+  {
+    muliifft_dis(no, ord, M, FFT, d, hstep);
+    muliifft_dis(no, ord, M, FFT, d+hstep, hstep);
+  }
+  for (i = d+1, j = 0; i <= d+hstep; ++i, j =(j+o)%ord)
+  {
+    GEN z = Zf_mulsqrt2(gel(FFT,i+hstep), j, ord, M);
+    GEN a = Zf_add(gel(FFT,i), z, M);
+    GEN b = Zf_sub(gel(FFT,i), z, M);
+    affii(a,gel(FFT,i));
+    affii(b,gel(FFT,i+hstep));
+    avma = av;
+  }
+}
+
+static GEN
+muliifft_spliti(GEN a, long na, long bs, long n, long mod)
+{
+  GEN ap = a+na-1;
+  GEN c  = cgetg(n+1, t_VEC);
+  long i,j;
+  for(i=1;i<=n;i++)
+  {
+    GEN z = cgeti(mod+3);
+    if (na)
+    {
+      long m = minss(bs, na), v=0;
+      GEN zp, aa=ap-m+1;
+      while (!*aa && v<m) {aa++; v++;}
+      zp = z+m-v+1;
+      for (j=v; j < m; j++)
+        *zp-- = *ap--;
+      ap -= v; na -= m;
+      z[1] = evalsigne(m!=v) | evallgefint(m-v+2);
+    } else
+      z[1] = evalsigne(0) | evallgefint(2);
+    gel(c, i) = z;
+  }
+  return c;
+}
+
+static GEN
+muliifft_unspliti(GEN V, long bs, long len)
+{
+  long s, i, j, l = lg(V);
+  GEN a = cgeti(len);
+  a[1] = evalsigne(1)|evallgefint(len);
+  for(i=2;i<len;i++)
+    a[i] = 0;
+  for(i=1, s=0; i<l; i++, s+=bs)
+  {
+    GEN  u = gel(V,i);
+    if (signe(u))
+    {
+      GEN ap = int_W(a,s);
+      GEN up = int_LSW(u);
+      long lu = NLIMBS(u);
+      LOCAL_OVERFLOW;
+      *ap = addll(*ap, *up--); ap--;
+      for (j=1; j<lu; j++)
+       { *ap = addllx(*ap, *up--); ap--; }
+      while (overflow)
+       { *ap = addll(*ap, 1); ap--; }
+    }
+  }
+  return int_normalize(a,0);
+}
+
+static GEN
+sqrispec_fft(GEN a, long na)
+{
+  pari_sp av, ltop = avma;
+  long len = 2*na;
+  long k, mod, bs, n;
+  GEN  FFT, M;
+  long i;
+  ulong o, ord;
+
+  mulliifft_params(len,&k,&mod,&bs,&n,&ord);
+  o = ord>>k;
+  M = int2n(mod*BITS_IN_LONG);
+  M[2+mod] = 1;
+  FFT = muliifft_spliti(a, na, bs, n, mod);
+  muliifft_dit(o, ord, M, FFT, 0, n);
+  av = avma;
+  for(i=1; i<=n; i++)
+  {
+    affii(Zf_sqr(gel(FFT,i), M), gel(FFT,i));
+    avma=av;
+  }
+  muliifft_dis(ord-o, ord, M, FFT, 0, n);
+  for(i=1; i<=n; i++)
+  {
+    affii(Zf_shift(gel(FFT,i), (ord>>1)-k, M), gel(FFT,i));
+    avma=av;
+  }
+  return gerepileuptoint(ltop, muliifft_unspliti(FFT,bs,2+len));
+}
+
+static GEN
+muliispec_fft(GEN a, GEN b, long na, long nb)
+{
+  pari_sp av, av2, ltop = avma;
+  long len = na+nb;
+  long k, mod, bs, n;
+  GEN FFT, FFTb, M;
+  long i;
+  ulong o, ord;
+
+  mulliifft_params(len,&k,&mod,&bs,&n,&ord);
+  o = ord>>k;
+  M = int2n(mod*BITS_IN_LONG);
+  M[2+mod] = 1;
+  FFT = muliifft_spliti(a, na, bs, n, mod);
+  av=avma;
+  muliifft_dit(o, ord, M, FFT, 0, n);
+  FFTb = muliifft_spliti(b, nb, bs, n, mod);
+  av2 = avma;
+  muliifft_dit(o, ord, M, FFTb, 0, n);
+  for(i=1; i<=n; i++)
+  {
+    affii(Zf_mul(gel(FFT,i), gel(FFTb,i), M), gel(FFT,i));
+    avma=av2;
+  }
+  avma=av;
+  muliifft_dis(ord-o, ord, M, FFT, 0, n);
+  for(i=1; i<=n; i++)
+  {
+    affii(Zf_shift(gel(FFT,i),(ord>>1)-k,M), gel(FFT,i));
+    avma=av;
+  }
+  return gerepileuptoint(ltop, muliifft_unspliti(FFT,bs,2+len));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**               INTEGER MULTIPLICATION (KARATSUBA)               **/
+/**                                                                **/
+/********************************************************************/
+
+/* return (x shifted left d words) + y. Assume d > 0, x > 0 and y >= 0 */
+static GEN
+addshiftw(GEN x, GEN y, long d)
+{
+  GEN z,z0,y0,yd, zd = (GEN)avma;
+  long a,lz,ly = lgefint(y);
+
+  z0 = new_chunk(d);
+  a = ly-2; yd = y+ly;
+  if (a >= d)
+  {
+    y0 = yd-d; while (yd > y0) *--zd = *--yd; /* copy last d words of y */
+    a -= d;
+    if (a)
+      z = addiispec(LIMBS(x), LIMBS(y), NLIMBS(x), a);
+    else
+      z = icopy(x);
+  }
+  else
+  {
+    y0 = yd-a; while (yd > y0) *--zd = *--yd; /* copy last a words of y */
+    while (zd > z0) *--zd = 0;    /* complete with 0s */
+    z = icopy(x);
+  }
+  lz = lgefint(z)+d;
+  z[1] = evalsigne(1) | evallgefint(lz);
+  z[0] = evaltyp(t_INT) | evallg(lz); return z;
+}
+
+/* Fast product (Karatsuba) of integers. a and b are "special" GENs
+ * c,c0,c1,c2 are genuine GENs.
+ */
+GEN
+muliispec(GEN a, GEN b, long na, long nb)
+{
+  GEN a0,c,c0;
+  long n0, n0a, i;
+  pari_sp av;
+
+  if (na < nb) swapspec(a,b, na,nb);
+  if (nb < MULII_KARATSUBA_LIMIT) return muliispec_basecase(a,b,na,nb);
+  if (nb >= MULII_FFT_LIMIT)      return muliispec_fft(a,b,na,nb);
+  i=(na>>1); n0=na-i; na=i;
+  av=avma; a0=a+na; n0a=n0;
+  while (n0a && !*a0) { a0++; n0a--; }
+
+  if (n0a && nb > n0)
+  { /* nb <= na <= n0 */
+    GEN b0,c1,c2;
+    long n0b;
+
+    nb -= n0;
+    c = muliispec(a,b,na,nb);
+    b0 = b+nb; n0b = n0;
+    while (n0b && !*b0) { b0++; n0b--; }
+    if (n0b)
+    {
+      c0 = muliispec(a0,b0, n0a,n0b);
+
+      c2 = addiispec(a0,a, n0a,na);
+      c1 = addiispec(b0,b, n0b,nb);
+      c1 = muliispec(LIMBS(c1),LIMBS(c2), NLIMBS(c1),NLIMBS(c2));
+      c2 = addiispec(LIMBS(c0),LIMBS(c),  NLIMBS(c0),NLIMBS(c));
+
+      c1 = subiispec(LIMBS(c1),LIMBS(c2), NLIMBS(c1),NLIMBS(c2));
+    }
+    else
+    {
+      c0 = gen_0;
+      c1 = muliispec(a0,b, n0a,nb);
+    }
+    c = addshiftw(c,c1, n0);
+  }
+  else
+  {
+    c = muliispec(a,b,na,nb);
+    c0 = muliispec(a0,b,n0a,nb);
+  }
+  return gerepileuptoint(av, addshiftw(c,c0, n0));
+}
+GEN
+muluui(ulong x, ulong y, GEN z)
+{
+  long t, s = signe(z);
+  GEN r;
+  LOCAL_HIREMAINDER;
+
+  if (!x || !y || !signe(z)) return gen_0;
+  t = mulll(x,y);
+  if (!hiremainder)
+    r = muluispec(t, z+2, lgefint(z)-2);
+  else
+  {
+    long tmp[2];
+    tmp[0] = hiremainder;
+    tmp[1] = t;
+    r = muliispec(z+2,tmp,lgefint(z)-2,2);
+  }
+  setsigne(r,s); return r;
+}
+
+#define sqrispec_mirror sqrispec
+#define muliispec_mirror muliispec
+
+/* x % (2^n), assuming n >= 0 */
+GEN
+remi2n(GEN x, long n)
+{
+  long hi,l,k,lx,ly, sx = signe(x);
+  GEN z, xd, zd;
+
+  if (!sx || !n) return gen_0;
+
+  k = dvmdsBIL(n, &l);
+  lx = lgefint(x);
+  if (lx < k+3) return icopy(x);
+
+  xd = x + (lx-k-1);
+  /* x = |_|...|#|1|...|k| : copy the last l bits of # and the last k words
+   *            ^--- initial xd  */
+  hi = ((ulong)*xd) & ((1UL<<l)-1); /* last l bits of # = top bits of result */
+  if (!hi)
+  { /* strip leading zeroes from result */
+    xd++; while (k && !*xd) { k--; xd++; }
+    if (!k) return gen_0;
+    ly = k+2; xd--;
+  }
+  else
+    ly = k+3;
+
+  zd = z = cgeti(ly);
+  *++zd = evalsigne(sx) | evallgefint(ly);
+  if (hi) *++zd = hi;
+  for ( ;k; k--) *++zd = *++xd;
+  return z;
+}
+
+GEN
+sqrispec(GEN a, long na)
+{
+  GEN a0,c;
+  long n0, n0a, i;
+  pari_sp av;
+
+  if (na < SQRI_KARATSUBA_LIMIT) return sqrispec_basecase(a,na);
+  if (na >= SQRI_FFT_LIMIT) return sqrispec_fft(a,na);
+  i=(na>>1); n0=na-i; na=i;
+  av=avma; a0=a+na; n0a=n0;
+  while (n0a && !*a0) { a0++; n0a--; }
+  c = sqrispec(a,na);
+  if (n0a)
+  {
+    GEN t, c1, c0 = sqrispec(a0,n0a);
+#if 0
+    c1 = shifti(muliispec(a0,a, n0a,na),1);
+#else /* faster */
+    t = addiispec(a0,a,n0a,na);
+    t = sqrispec(LIMBS(t),NLIMBS(t));
+    c1= addiispec(LIMBS(c0),LIMBS(c), NLIMBS(c0), NLIMBS(c));
+    c1= subiispec(LIMBS(t),LIMBS(c1), NLIMBS(t), NLIMBS(c1));
+#endif
+    c = addshiftw(c,c1, n0);
+    c = addshiftw(c,c0, n0);
+  }
+  else
+    c = addshiftw(c,gen_0,n0<<1);
+  return gerepileuptoint(av, c);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                    KARATSUBA SQUARE ROOT                       **/
+/**      adapted from Paul Zimmermann's implementation of          **/
+/**      his algorithm in GMP (mpn_sqrtrem)                        **/
+/**                                                                **/
+/********************************************************************/
+
+/* Square roots table */
+static const unsigned char approx_tab[192] = {
+  128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,
+  143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,
+  156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,
+  169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,
+  181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,
+  192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,
+  202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,
+  212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,
+  221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,
+  230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,
+  239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,
+  247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255
+};
+
+/* N[0], assume N[0] >= 2^(BIL-2).
+ * Return r,s such that s^2 + r = N, 0 <= r <= 2s */
+static void
+p_sqrtu1(ulong *N, ulong *ps, ulong *pr)
+{
+  ulong prec, r, s, q, u, n0 = N[0];
+
+  q = n0 >> (BITS_IN_LONG - 8);
+  /* 2^6 = 64 <= q < 256 = 2^8 */
+  s = approx_tab[q - 64];                                /* 128 <= s < 255 */
+  r = (n0 >> (BITS_IN_LONG - 16)) - s * s;                /* r <= 2*s */
+  if (r > (s << 1)) { r -= (s << 1) | 1; s++; }
+
+  /* 8-bit approximation from the high 8-bits of N[0] */
+  prec = 8;
+  n0 <<= 2 * prec;
+  while (2 * prec < BITS_IN_LONG)
+  { /* invariant: s has prec bits, and r <= 2*s */
+    r = (r << prec) + (n0 >> (BITS_IN_LONG - prec));
+    n0 <<= prec;
+    u = 2 * s;
+    q = r / u; u = r - q * u;
+    s = (s << prec) + q;
+    u = (u << prec) + (n0 >> (BITS_IN_LONG - prec));
+    q = q * q;
+    r = u - q;
+    if (u < q) { s--; r += (s << 1) | 1; }
+    n0 <<= prec;
+    prec = 2 * prec;
+  }
+  *ps = s;
+  *pr = r;
+}
+
+/* N[0..1], assume N[0] >= 2^(BIL-2).
+ * Return 1 if remainder overflows, 0 otherwise */
+static int
+p_sqrtu2(ulong *N, ulong *ps, ulong *pr)
+{
+  ulong cc, qhl, r, s, q, u, n1 = N[1];
+  LOCAL_OVERFLOW;
+
+  p_sqrtu1(N, &s, &r); /* r <= 2s */
+  qhl = 0; while (r >= s) { qhl++; r -= s; }
+  /* now r < s < 2^(BIL/2) */
+  r = (r << BITS_IN_HALFULONG) | (n1 >> BITS_IN_HALFULONG);
+  u = s << 1;
+  q = r / u; u = r - q * u;
+  q += (qhl & 1) << (BITS_IN_HALFULONG - 1);
+  qhl >>= 1;
+  /* (initial r)<<(BIL/2) + n1>>(BIL/2) = (qhl<<(BIL/2) + q) * 2s + u */
+  s = ((s + qhl) << BITS_IN_HALFULONG) + q;
+  cc = u >> BITS_IN_HALFULONG;
+  r = (u << BITS_IN_HALFULONG) | (n1 & LOWMASK);
+  r = subll(r, q * q);
+  cc -= overflow + qhl;
+  /* now subtract 2*q*2^(BIL/2) + 2^BIL if qhl is set */
+  if ((long)cc < 0)
+  {
+    if (s) {
+      r = addll(r, s);
+      cc += overflow;
+      s--;
+    } else {
+      cc++;
+      s = ~0UL;
+    }
+    r = addll(r, s);
+    cc += overflow;
+  }
+  *ps = s;
+  *pr = r; return cc;
+}
+
+static void
+xmpn_zero(GEN x, long n)
+{
+  while (--n >= 0) x[n]=0;
+}
+static void
+xmpn_copy(GEN z, GEN x, long n)
+{
+  long k = n;
+  while (--k >= 0) z[k] = x[k];
+}
+/* a[0..la-1] * 2^(lb BIL) | b[0..lb-1] */
+static GEN
+catii(GEN a, long la, GEN b, long lb)
+{
+  long l = la + lb + 2;
+  GEN z = cgetipos(l);
+  xmpn_copy(LIMBS(z), a, la);
+  xmpn_copy(LIMBS(z) + la, b, lb);
+  return int_normalize(z, 0);
+}
+
+/* sqrt n[0..1], assume n normalized */
+static GEN
+sqrtispec2(GEN n, GEN *pr)
+{
+  ulong s, r;
+  int hi = p_sqrtu2((ulong*)n, &s, &r);
+  GEN S = utoi(s);
+  *pr = hi? uutoi(1,r): utoi(r);
+  return S;
+}
+
+/* sqrt n[0], _dont_ assume n normalized */
+static GEN
+sqrtispec1_sh(GEN n, GEN *pr)
+{
+  GEN S;
+  ulong r, s, u0 = (ulong)n[0];
+  int sh = bfffo(u0) & ~1UL;
+  if (sh) u0 <<= sh;
+  p_sqrtu1(&u0, &s, &r);
+  /* s^2 + r = u0, s < 2^(BIL/2). Rescale back:
+   * 2^(2k) n = S^2 + R
+   * so 2^(2k) n = (S - s0)^2 + (2*S*s0 - s0^2 + R), s0 = S mod 2^k. */
+  if (sh) {
+    int k = sh >> 1;
+    ulong s0 = s & ((1L<<k) - 1);
+    r += s * (s0<<1);
+    s >>= k;
+    r >>= sh;
+  }
+  S = utoi(s);
+  if (pr) *pr = utoi(r);
+  return S;
+}
+
+/* sqrt n[0..1], _dont_ assume n normalized */
+static GEN
+sqrtispec2_sh(GEN n, GEN *pr)
+{
+  GEN S;
+  ulong U[2], r, s, u0 = (ulong)n[0], u1 = (ulong)n[1];
+  int hi, sh = bfffo(u0) & ~1UL;
+  if (sh) {
+    u0 = (u0 << sh) | (u1 >> (BITS_IN_LONG-sh));
+    u1 <<= sh;
+  }
+  U[0] = u0;
+  U[1] = u1; hi = p_sqrtu2(U, &s, &r);
+  /* s^2 + R = u0|u1. Rescale back:
+   * 2^(2k) n = S^2 + R
+   * so 2^(2k) n = (S - s0)^2 + (2*S*s0 - s0^2 + R), s0 = S mod 2^k. */
+  if (sh) {
+    int k = sh >> 1;
+    ulong s0 = s & ((1L<<k) - 1);
+    LOCAL_HIREMAINDER;
+    LOCAL_OVERFLOW;
+    r = addll(r, mulll(s, (s0<<1)));
+    if (overflow) hiremainder++;
+    hiremainder += hi; /* + 0 or 1 */
+    s >>= k;
+    r = (r>>sh) | (hiremainder << (BITS_IN_LONG-sh));
+    hi = (hiremainder & (1L<<sh));
+  }
+  S = utoi(s);
+  if (pr) *pr = hi? uutoi(1,r): utoi(r);
+  return S;
+}
+
+/* Let N = N[0..2n-1]. Return S (and set R) s.t S^2 + R = N, 0 <= R <= 2S
+ * Assume N normalized */
+static GEN
+sqrtispec(GEN N, long n, GEN *r)
+{
+  GEN S, R, q, z, u;
+  long l, h;
+
+  if (n == 1) return sqrtispec2(N, r);
+  l = n >> 1;
+  h = n - l; /* N = a3(h) | a2(h) | a1(l) | a0(l words) */
+  S = sqrtispec(N, h, &R); /* S^2 + R = a3|a2 */
+
+  z = catii(LIMBS(R), NLIMBS(R), N + 2*h, l); /* = R | a1(l) */
+  q = dvmdii(z, shifti(S,1), &u);
+  z = catii(LIMBS(u), NLIMBS(u), N + n + h, l); /* = u | a0(l) */
+
+  S = addshiftw(S, q, l);
+  R = subii(z, sqri(q));
+  if (signe(R) < 0)
+  {
+    GEN S2 = shifti(S,1);
+    R = addis(subiispec(LIMBS(S2),LIMBS(R), NLIMBS(S2),NLIMBS(R)), -1);
+    S = addis(S, -1);
+  }
+  *r = R; return S;
+}
+
+/* Return S (and set R) s.t S^2 + R = N, 0 <= R <= 2S.
+ * As for dvmdii, R is last on stack and guaranteed to be gen_0 in case the
+ * remainder is 0. R = NULL is allowed. */
+GEN
+sqrtremi(GEN N, GEN *r)
+{
+  pari_sp av;
+  GEN S, R, n = N+2;
+  long k, l2, ln = NLIMBS(N);
+  int sh;
+
+  if (ln <= 2)
+  {
+    if (ln == 2) return sqrtispec2_sh(n, r);
+    if (ln == 1) return sqrtispec1_sh(n, r);
+    if (r) *r = gen_0;
+    return gen_0;
+  }
+  av = avma;
+  sh = bfffo(n[0]) >> 1;
+  l2 = (ln + 1) >> 1;
+  if (sh || (ln & 1)) { /* normalize n, so that n[0] >= 2^BIL / 4 */
+    GEN s0, t = new_chunk(ln + 1);
+    t[ln] = 0;
+    if (sh)
+      shift_left(t, n, 0,ln-1, 0, sh << 1);
+    else
+      xmpn_copy(t, n, ln);
+    S = sqrtispec(t, l2, &R); /* t normalized, 2 * l2 words */
+    /* Rescale back:
+     * 2^(2k) n = S^2 + R, k = sh + (ln & 1)*BIL/2
+     * so 2^(2k) n = (S - s0)^2 + (2*S*s0 - s0^2 + R), s0 = S mod 2^k. */
+    k = sh + (ln & 1) * (BITS_IN_LONG/2);
+    s0 = remi2n(S, k);
+    R = addii(shifti(R,-1), mulii(s0, S));
+    R = shifti(R, 1 - (k<<1));
+    S = shifti(S, -k);
+  }
+  else
+    S = sqrtispec(n, l2, &R);
+
+  if (!r) { avma = (pari_sp)S; return gerepileuptoint(av, S); }
+  gerepileall(av, 2, &S, &R); *r = R; return S;
+}
+
+/* compute sqrt(|a|), assuming a != 0 */
+
+#if 1
+GEN
+sqrtr_abs(GEN x)
+{
+  long l = lg(x) - 2, e = expo(x), er = e>>1;
+  GEN b, c, res = cgetr(2 + l);
+  res[1] = evalsigne(1) | evalexpo(er);
+  if (e&1) {
+    b = new_chunk(l << 1);
+    xmpn_copy(b, x+2, l);
+    xmpn_zero(b + l,l);
+    b = sqrtispec(b, l, &c);
+    xmpn_copy(res+2, b+2, l);
+    if (cmpii(c, b) > 0) roundr_up_ip(res, l+2);
+  } else {
+    ulong u;
+    b = new_chunk(2 + (l << 1));
+    shift_left(b+1, x+2, 0,l-1, 0, BITS_IN_LONG-1);
+    b[0] = ((ulong)x[2])>>1;
+    xmpn_zero(b + l+1,l+1);
+    b = sqrtispec(b, l+1, &c);
+    xmpn_copy(res+2, b+2, l);
+    u = (ulong)b[l+2];
+    if ( u&HIGHBIT || (u == ~HIGHBIT && cmpii(c,b) > 0))
+      roundr_up_ip(res, l+2);
+  }
+  avma = (pari_sp)res; return res;
+}
+
+#else /* use t_REAL: currently much slower (quadratic division) */
+
+#ifdef LONG_IS_64BIT
+/* 64 bits of b = sqrt(a[0] * 2^64 + a[1])  [ up to 1ulp ] */
+static ulong
+sqrtu2(ulong *a)
+{
+  ulong c, b = dblmantissa( sqrt((double)a[0]) );
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  /* > 32 correct bits, 1 Newton iteration to reach 64 */
+  if (b <= a[0]) return HIGHBIT | (a[0] >> 1);
+  hiremainder = a[0]; c = divll(a[1], b);
+  return (addll(c, b) >> 1) | HIGHBIT;
+}
+/* 64 bits of sqrt(a[0] * 2^63) */
+static ulong
+sqrtu2_1(ulong *a)
+{
+  ulong t[2];
+  t[0] = (a[0] >> 1);
+  t[1] = (a[0] << (BITS_IN_LONG-1)) | (a[1] >> 1);
+  return sqrtu2(t);
+}
+#else
+/* 32 bits of sqrt(a[0] * 2^32) */
+static ulong
+sqrtu2(ulong *a)   { return dblmantissa( sqrt((double)a[0]) ); }
+/* 32 bits of sqrt(a[0] * 2^31) */
+static ulong
+sqrtu2_1(ulong *a) { return dblmantissa( sqrt(2. * a[0]) ); }
+#endif
+
+GEN
+sqrtr_abs(GEN x)
+{
+  long l1, i, l = lg(x), ex = expo(x);
+  GEN a, t, y = cgetr(l);
+  pari_sp av, av0 = avma;
+
+  a = rtor(x, l+1);
+  t = cgetr(l+1);
+  if (ex & 1) { /* odd exponent */
+    a[1] = evalsigne(1) | _evalexpo(1);
+    t[2] = (long)sqrtu2((ulong*)a + 2);
+  } else { /* even exponent */
+    a[1] = evalsigne(1) | _evalexpo(0);
+    t[2] = (long)sqrtu2_1((ulong*)a + 2);
+  }
+  t[1] = evalsigne(1) | _evalexpo(0);
+  for (i = 3; i <= l; i++) t[i] = 0;
+
+  /* |x| = 2^(ex/2) a, t ~ sqrt(a) */
+  l--; l1 = 1; av = avma;
+  while (l1 < l) { /* let t := (t + a/t)/2 */
+    l1 <<= 1; if (l1 > l) l1 = l;
+    setlg(a, l1 + 2);
+    setlg(t, l1 + 2);
+    affrr(addrr(t, divrr(a,t)), t); shiftr_inplace(t, -1);
+    avma = av;
+  }
+  affrr(t,y); shiftr_inplace(y, (ex>>1));
+  avma = av0; return y;
+}
+
+#endif
+
+/*******************************************************************
+ *                                                                 *
+ *                           Base Conversion                       *
+ *                                                                 *
+ *******************************************************************/
+
+static void
+convi_dac(GEN x, ulong l, ulong *res)
+{
+  pari_sp ltop=avma;
+  ulong m;
+  GEN x1,x2;
+  if (l==1) { *res=itou(x); return; }
+  m=l>>1;
+  x1=dvmdii(x,powuu(1000000000UL,m),&x2);
+  convi_dac(x1,l-m,res+m);
+  convi_dac(x2,m,res);
+  avma=ltop;
+}
+
+/* convert integer --> base 10^9 [not memory clean] */
+ulong *
+convi(GEN x, long *l)
+{
+  long lz, lx = lgefint(x);
+  ulong *z;
+  if (lx == 3 && (ulong)x[2] < 1000000000UL) {
+    z = (ulong*)new_chunk(1);
+    *z = x[2];
+    *l = 1; return z+1;
+  }
+  lz = 1 + (long)bit_accuracy_mul(lx, LOG10_2/9);
+  z = (ulong*)new_chunk(lz);
+  convi_dac(x,(ulong)lz,z);
+  while (z[lz-1]==0) lz--;
+  *l=lz; return z+lz;
+}
+
diff --git a/src/kernel/none/mp_indep.c b/src/kernel/none/mp_indep.c
new file mode 100644
index 0000000..93d7af8
--- /dev/null
+++ b/src/kernel/none/mp_indep.c
@@ -0,0 +1,1072 @@
+#line 2 "../src/kernel/none/mp_indep.c"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Find c such that 1=c*b mod 2^BITS_IN_LONG, assuming b odd (unchecked) */
+ulong
+invmod2BIL(ulong b)
+{
+  static int tab[] = { 0, 0, 0, 8, 0, 8, 0, 0 };
+  ulong x = b + tab[b & 7]; /* b^(-1) mod 2^4 */
+
+  /* Newton applied to 1/x - b = 0 */
+#ifdef LONG_IS_64BIT
+  x = x*(2-b*x); /* one more pass necessary */
+#endif
+  x = x*(2-b*x);
+  x = x*(2-b*x); return x*(2-b*x);
+}
+
+void
+affrr(GEN x, GEN y)
+{
+  long lx,ly,i;
+
+  y[1] = x[1]; if (!signe(x)) return;
+
+  lx=lg(x); ly=lg(y);
+  if (lx <= ly)
+  {
+    for (i=2; i<lx; i++) y[i]=x[i];
+    for (   ; i<ly; i++) y[i]=0;
+    return;
+  }
+  for (i=2; i<ly; i++) y[i]=x[i];
+  /* lx > ly: round properly */
+  if (x[ly] & HIGHBIT) roundr_up_ip(y, ly);
+}
+
+GEN
+trunc2nr(GEN x, long n)
+{
+  long ex;
+  if (!signe(x)) return gen_0;
+  ex = expo(x) + n; if (ex < 0) return gen_0;
+  return mantissa2nr(x, ex - bit_prec(x) + 1);
+}
+
+/* x a t_REAL, x = i/2^e, i a t_INT */
+GEN
+mantissa_real(GEN x, long *e)
+{
+  *e = bit_prec(x)-1-expo(x);
+  return mantissa2nr(x, 0);
+}
+
+GEN
+mului(ulong x, GEN y)
+{
+  long s = signe(y);
+  GEN z;
+
+  if (!s || !x) return gen_0;
+  z = muluispec(x, y+2, lgefint(y)-2);
+  setsigne(z,s); return z;
+}
+
+GEN
+mulsi(long x, GEN y)
+{
+  long s = signe(y);
+  GEN z;
+
+  if (!s || !x) return gen_0;
+  if (x<0) { s = -s; x = -x; }
+  z = muluispec((ulong)x, y+2, lgefint(y)-2);
+  setsigne(z,s); return z;
+}
+
+GEN
+mulss(long x, long y)
+{
+  long p1;
+  LOCAL_HIREMAINDER;
+
+  if (!x || !y) return gen_0;
+  if (x<0) {
+    x = -x;
+    if (y<0) { y = -y; p1 = mulll(x,y); return uutoi(hiremainder, p1); }
+    p1 = mulll(x,y); return uutoineg(hiremainder, p1);
+  } else {
+    if (y<0) { y = -y; p1 = mulll(x,y); return uutoineg(hiremainder, p1); }
+    p1 = mulll(x,y); return uutoi(hiremainder, p1);
+  }
+}
+GEN
+sqrs(long x)
+{
+  long p1;
+  LOCAL_HIREMAINDER;
+
+  if (!x) return gen_0;
+  if (x<0) x = -x;
+  p1 = mulll(x,x); return uutoi(hiremainder, p1);
+}
+GEN
+muluu(ulong x, ulong y)
+{
+  long p1;
+  LOCAL_HIREMAINDER;
+
+  if (!x || !y) return gen_0;
+  p1 = mulll(x,y); return uutoi(hiremainder, p1);
+}
+GEN
+sqru(ulong x)
+{
+  long p1;
+  LOCAL_HIREMAINDER;
+
+  if (!x) return gen_0;
+  p1 = mulll(x,x); return uutoi(hiremainder, p1);
+}
+
+/* assume x > 1, y != 0. Return u * y with sign s */
+static GEN
+mulur_2(ulong x, GEN y, long s)
+{
+  long m, sh, i, lx = lg(y), e = expo(y);
+  GEN z = cgetr(lx);
+  ulong garde;
+  LOCAL_HIREMAINDER;
+
+  y--; garde = mulll(x,y[lx]);
+  for (i=lx-1; i>=3; i--) z[i]=addmul(x,y[i]);
+  z[2]=hiremainder; /* != 0 since y normalized and |x| > 1 */
+  sh = bfffo(hiremainder); m = BITS_IN_LONG-sh;
+  if (sh) shift_left(z,z, 2,lx-1, garde,sh);
+  z[1] = evalsigne(s) | evalexpo(m+e);
+  if ((garde << sh) & HIGHBIT) roundr_up_ip(z, lx);
+  return z;
+}
+
+INLINE GEN
+mul0r(GEN x)
+{
+  long l = lg(x), e = expo(x);
+  e = (l > 2)? -bit_accuracy(l) + e: (e < 0? (e<<1): 0);
+  return real_0_bit(e);
+}
+/* lg(x) > 2 */
+INLINE GEN
+div0r(GEN x) {
+  long l = lg(x), e = expo(x);
+  return real_0_bit(-bit_accuracy(l) - e);
+}
+
+GEN
+mulsr(long x, GEN y)
+{
+  long s;
+
+  if (!x) return mul0r(y);
+  s = signe(y);
+  if (!s)
+  {
+    if (x < 0) x = -x;
+    return real_0_bit( expo(y) + expu(x) );
+  }
+  if (x==1)  return rcopy(y);
+  if (x==-1) return negr(y);
+  if (x < 0)
+    return mulur_2((ulong)-x, y, -s);
+  else
+    return mulur_2((ulong)x, y, s);
+}
+
+GEN
+mulur(ulong x, GEN y)
+{
+  long s;
+
+  if (!x) return mul0r(y);
+  s = signe(y);
+  if (!s) return real_0_bit( expo(y) + expu(x) );
+  if (x==1) return rcopy(y);
+  return mulur_2(x, y, s);
+}
+
+INLINE void
+mulrrz_end(GEN z, GEN hi, long lz, long sz, long ez, ulong garde)
+{
+  long i;
+  if (hi[2] < 0)
+  {
+    if (z != hi)
+      for (i=2; i<lz ; i++) z[i] = hi[i];
+    ez++;
+  }
+  else
+  {
+    shift_left(z,hi,2,lz-1, garde, 1);
+    garde <<= 1;
+  }
+  if (garde & HIGHBIT)
+  { /* round to nearest */
+    i = lz; do ((ulong*)z)[--i]++; while (i>1 && z[i]==0);
+    if (i == 1) { z[2] = (long)HIGHBIT; ez++; }
+  }
+  z[1] = evalsigne(sz)|evalexpo(ez);
+}
+/* mulrrz_end for lz = 3, minor simplifications. z[2]=hiremainder from mulll */
+INLINE void
+mulrrz_3end(GEN z, long sz, long ez, ulong garde)
+{
+  if (z[2] < 0)
+  { /* z2 < (2^BIL-1)^2 / 2^BIL, hence z2+1 != 0 */
+    if (garde & HIGHBIT) z[2]++; /* round properly */
+    ez++;
+  }
+  else
+  {
+    z[2] = (z[2]<<1) | (garde>>(BITS_IN_LONG-1));
+    if (garde & (1UL<<(BITS_IN_LONG-2)))
+    {
+      z[2]++; /* round properly, z2+1 can overflow */
+      if (!z[2]) { z[2] = (long)HIGHBIT; ez++; }
+    }
+  }
+  z[1] = evalsigne(sz)|evalexpo(ez);
+}
+
+/* set z <-- x^2 != 0, floating point multiplication.
+ * lz = lg(z) = lg(x) */
+INLINE void
+sqrz_i(GEN z, GEN x, long lz)
+{
+  long ez = expo(x) << 1;
+  long i, j, lzz, p1;
+  ulong garde;
+  GEN x1;
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  if (lz > MULRR_MULII_LIMIT)
+  {
+    pari_sp av = avma;
+    GEN hi = sqrispec_mirror(x+2, lz-2);
+    mulrrz_end(z, hi, lz, 1, ez, hi[lz]);
+    avma = av; return;
+  }
+  if (lz == 3)
+  {
+    garde = mulll(x[2],x[2]);
+    z[2] = hiremainder;
+    mulrrz_3end(z, 1, ez, garde);
+    return;
+  }
+
+  lzz = lz-1; p1 = x[lzz];
+  if (p1)
+  {
+    (void)mulll(p1,x[3]);
+    garde = addmul(p1,x[2]);
+    z[lzz] = hiremainder;
+  }
+  else
+  {
+    garde = 0;
+    z[lzz] = 0;
+  }
+  for (j=lz-2, x1=x-j; j>=3; j--)
+  {
+    p1 = x[j]; x1++;
+    if (p1)
+    {
+      (void)mulll(p1,x1[lz+1]);
+      garde = addll(addmul(p1,x1[lz]), garde);
+      for (i=lzz; i>j; i--)
+      {
+        hiremainder += overflow;
+        z[i] = addll(addmul(p1,x1[i]), z[i]);
+      }
+      z[j] = hiremainder+overflow;
+    }
+    else z[j]=0;
+  }
+  p1 = x[2]; x1++;
+  garde = addll(mulll(p1,x1[lz]), garde);
+  for (i=lzz; i>2; i--)
+  {
+    hiremainder += overflow;
+    z[i] = addll(addmul(p1,x1[i]), z[i]);
+  }
+  z[2] = hiremainder+overflow;
+  mulrrz_end(z, z, lz, 1, ez, garde);
+}
+
+/* lz "large" = lg(y) = lg(z), lg(x) > lz if flag = 1 and >= if flag = 0 */
+INLINE void
+mulrrz_int(GEN z, GEN x, GEN y, long lz, long flag, long sz)
+{
+  pari_sp av = avma;
+  GEN hi = muliispec_mirror(y+2, x+2, lz+flag-2, lz-2);
+  mulrrz_end(z, hi, lz, sz, expo(x)+expo(y), hi[lz]);
+  avma = av;
+}
+
+/* lz = 3 */
+INLINE void
+mulrrz_3(GEN z, GEN x, GEN y, long flag, long sz)
+{
+  ulong garde;
+  LOCAL_HIREMAINDER;
+  if (flag)
+  {
+    (void)mulll(x[2],y[3]);
+    garde = addmul(x[2],y[2]);
+  }
+  else
+    garde = mulll(x[2],y[2]);
+  z[2] = hiremainder;
+  mulrrz_3end(z, sz, expo(x)+expo(y), garde);
+}
+
+/* set z <-- x*y, floating point multiplication. Trailing 0s for x are
+ * treated efficiently (important application: mulir).
+ * lz = lg(z) = lg(x) <= ly <= lg(y), sz = signe(z). flag = lg(x) < lg(y) */
+INLINE void
+mulrrz_i(GEN z, GEN x, GEN y, long lz, long flag, long sz)
+{
+  long ez, i, j, lzz, p1;
+  ulong garde;
+  GEN y1;
+  LOCAL_HIREMAINDER;
+  LOCAL_OVERFLOW;
+
+  if (x == y) { sqrz_i(z,x,lz); return; }
+  if (lz > MULRR_MULII_LIMIT) { mulrrz_int(z,x,y,lz,flag,sz); return; }
+  if (lz == 3) { mulrrz_3(z,x,y,flag,sz); return; }
+  ez = expo(x) + expo(y);
+  if (flag) { (void)mulll(x[2],y[lz]); garde = hiremainder; } else garde = 0;
+  lzz=lz-1; p1=x[lzz];
+  if (p1)
+  {
+    (void)mulll(p1,y[3]);
+    garde = addll(addmul(p1,y[2]), garde);
+    z[lzz] = overflow+hiremainder;
+  }
+  else z[lzz]=0;
+  for (j=lz-2, y1=y-j; j>=3; j--)
+  {
+    p1 = x[j]; y1++;
+    if (p1)
+    {
+      (void)mulll(p1,y1[lz+1]);
+      garde = addll(addmul(p1,y1[lz]), garde);
+      for (i=lzz; i>j; i--)
+      {
+        hiremainder += overflow;
+        z[i] = addll(addmul(p1,y1[i]), z[i]);
+      }
+      z[j] = hiremainder+overflow;
+    }
+    else z[j]=0;
+  }
+  p1 = x[2]; y1++;
+  garde = addll(mulll(p1,y1[lz]), garde);
+  for (i=lzz; i>2; i--)
+  {
+    hiremainder += overflow;
+    z[i] = addll(addmul(p1,y1[i]), z[i]);
+  }
+  z[2] = hiremainder+overflow;
+  mulrrz_end(z, z, lz, sz, ez, garde);
+}
+
+GEN
+mulrr(GEN x, GEN y)
+{
+  long flag, ly, lz, sx, sy;
+  GEN z;
+
+  if (x == y) return sqrr(x);
+  sx = signe(x); if (!sx) return real_0_bit(expo(x) + expo(y));
+  sy = signe(y); if (!sy) return real_0_bit(expo(x) + expo(y));
+  if (sy < 0) sx = -sx;
+  lz = lg(x);
+  ly = lg(y);
+  if (lz > ly) { lz = ly; swap(x, y); flag = 1; } else flag = (lz != ly);
+  z = cgetr(lz);
+  mulrrz_i(z, x,y, lz,flag, sx);
+  return z;
+}
+
+GEN
+sqrr(GEN x)
+{
+  long lz, sx = signe(x);
+  GEN z;
+
+  if (!sx) return real_0_bit(2*expo(x));
+  lz = lg(x); z = cgetr(lz);
+  sqrz_i(z, x, lz);
+  return z;
+}
+
+GEN
+mulir(GEN x, GEN y)
+{
+  long sx = signe(x), sy;
+  if (!sx) return mul0r(y);
+  if (lgefint(x) == 3) {
+    GEN z = mulur((ulong)x[2], y);
+    if (sx < 0) togglesign(z);
+    return z;
+  }
+  sy = signe(y);
+  if (!sy) return real_0_bit(expi(x) + expo(y));
+  if (sy < 0) sx = -sx;
+  {
+    long lz = lg(y), lx = lgefint(x);
+    GEN hi, z = cgetr(lz);
+    pari_sp av = avma;
+    if (lx < (lz>>1) || (lx < lz && lz > MULRR_MULII_LIMIT))
+    { /* size mantissa of x < half size of mantissa z, or lx < lz so large
+       * that mulrr will call mulii anyway: mulii */
+      x = itor(x, lx);
+      hi = muliispec_mirror(y+2, x+2, lz-2, lx-2);
+      mulrrz_end(z, hi, lz, sx, expo(x)+expo(y), hi[lz]);
+    }
+    else /* dubious: complete x with 0s and call mulrr */
+      mulrrz_i(z, itor(x,lz), y, lz, 0, sx);
+    avma = av; return z;
+  }
+}
+
+/* x + y*z, generic. If lgefint(z) <= 3, caller should use faster variants  */
+static GEN
+addmulii_gen(GEN x, GEN y, GEN z, long lz)
+{
+  long lx = lgefint(x), ly;
+  pari_sp av;
+  GEN t;
+  if (lx == 2) return mulii(z,y);
+  ly = lgefint(y);
+  if (ly == 2) return icopy(x); /* y = 0, wasteful copy */
+  av = avma; (void)new_chunk(lx+ly+lz); /*HACK*/
+  t = mulii(z, y);
+  avma = av; return addii(t,x);
+}
+/* x + y*z, lgefint(z) == 3 */
+static GEN
+addmulii_lg3(GEN x, GEN y, GEN z)
+{
+  long s = signe(z), lx, ly;
+  ulong w = z[2];
+  pari_sp av;
+  GEN t;
+  if (w == 1) return (s > 0)? addii(x,y): subii(x,y); /* z = +- 1 */
+  lx = lgefint(x);
+  ly = lgefint(y);
+  if (lx == 2)
+  { /* x = 0 */
+    if (ly == 2) return gen_0;
+    t = muluispec(w, y+2, ly-2);
+    if (signe(y) < 0) s = -s;
+    setsigne(t, s); return t;
+  }
+  if (ly == 2) return icopy(x); /* y = 0, wasteful copy */
+  av = avma; (void)new_chunk(1+lx+ly);/*HACK*/
+  t = muluispec(w, y+2, ly-2);
+  if (signe(y) < 0) s = -s;
+  setsigne(t, s);
+  avma = av; return addii(x,t);
+}
+/* x + y*z */
+GEN
+addmulii(GEN x, GEN y, GEN z)
+{
+  long lz = lgefint(z);
+  switch(lz)
+  {
+    case 2: return icopy(x); /* z = 0, wasteful copy */
+    case 3: return addmulii_lg3(x, y, z);
+    default:return addmulii_gen(x, y, z, lz);
+  }
+}
+/* x + y*z, returns x itself and not a copy when y*z = 0 */
+GEN
+addmulii_inplace(GEN x, GEN y, GEN z)
+{
+  long lz;
+  if (lgefint(y) == 2) return x;
+  lz = lgefint(z);
+  switch(lz)
+  {
+    case 2: return x;
+    case 3: return addmulii_lg3(x, y, z);
+    default:return addmulii_gen(x, y, z, lz);
+  }
+}
+
+/* written by Bruno Haible following an idea of Robert Harley */
+long
+vals(ulong z)
+{
+  static char tab[64]={-1,0,1,12,2,6,-1,13,3,-1,7,-1,-1,-1,-1,14,10,4,-1,-1,8,-1,-1,25,-1,-1,-1,-1,-1,21,27,15,31,11,5,-1,-1,-1,-1,-1,9,-1,-1,24,-1,-1,20,26,30,-1,-1,-1,-1,23,-1,19,29,-1,22,18,28,17,16,-1};
+#ifdef LONG_IS_64BIT
+  long s;
+#endif
+
+  if (!z) return -1;
+#ifdef LONG_IS_64BIT
+  if (! (z&0xffffffff)) { s = 32; z >>=32; } else s = 0;
+#endif
+  z |= ~z + 1;
+  z += z << 4;
+  z += z << 6;
+  z ^= z << 16; /* or  z -= z<<16 */
+#ifdef LONG_IS_64BIT
+  return s + tab[(z&0xffffffff)>>26];
+#else
+  return tab[z>>26];
+#endif
+}
+
+GEN
+divsi(long x, GEN y)
+{
+  long p1, s = signe(y);
+  LOCAL_HIREMAINDER;
+
+  if (!s) pari_err_INV("divsi",gen_0);
+  if (!x || lgefint(y)>3 || ((long)y[2])<0) return gen_0;
+  hiremainder=0; p1=divll(labs(x),y[2]);
+  if (x<0) { hiremainder = -((long)hiremainder); p1 = -p1; }
+  if (s<0) p1 = -p1;
+  return stoi(p1);
+}
+
+GEN
+divir(GEN x, GEN y)
+{
+  GEN z;
+  long ly = lg(y), lx = lgefint(x);
+  pari_sp av;
+
+  if (ly == 2) pari_err_INV("divir",y);
+  if (lx == 2) return div0r(y);
+  if (lx == 3) {
+    z = divur(x[2], y);
+    if (signe(x) < 0) togglesign(z);
+    return z;
+  }
+  z = cgetr(ly); av = avma;
+  affrr(divrr(itor(x, ly+1), y), z);
+  avma = av; return z;
+}
+
+GEN
+divur(ulong x, GEN y)
+{
+  pari_sp av;
+  long ly = lg(y);
+  GEN z;
+
+  if (ly == 2) pari_err_INV("divur",y);
+  if (!x) return div0r(y);
+  if (ly > INVNEWTON_LIMIT) {
+    av = avma; z = invr(y);
+    if (x == 1) return z;
+    return gerepileuptoleaf(av, mulur(x, z));
+  }
+  z = cgetr(ly); av = avma;
+  affrr(divrr(utor(x,ly+1), y), z);
+  avma = av; return z;
+}
+
+GEN
+divsr(long x, GEN y)
+{
+  pari_sp av;
+  long ly = lg(y);
+  GEN z;
+
+  if (ly == 2) pari_err_INV("divsr",y);
+  if (!x) return div0r(y);
+  if (ly > INVNEWTON_LIMIT) {
+    av = avma; z = invr(y);
+    if (x == 1) return z;
+    if (x ==-1) { togglesign(z); return z; }
+    return gerepileuptoleaf(av, mulsr(x, z));
+  }
+  z = cgetr(ly); av = avma;
+  affrr(divrr(stor(x,ly+1), y), z);
+  avma = av; return z;
+}
+
+/* returns 1/y, assume y != 0 */
+static GEN
+invr_basecase(GEN y)
+{
+  long ly = lg(y);
+  GEN z = cgetr(ly);
+  pari_sp av = avma;
+  affrr(divrr(real_1(ly+1), y), z);
+  avma = av; return z;
+}
+/* returns 1/b, Newton iteration */
+GEN
+invr(GEN b)
+{
+  const long s = 6;
+  long i, p, l = lg(b);
+  GEN x, a;
+  ulong mask;
+
+  if (l <= maxss(INVNEWTON_LIMIT, (1L<<s) + 2)) {
+    if (l == 2) pari_err_INV("invr",b);
+    return invr_basecase(b);
+  }
+  mask = quadratic_prec_mask(l-2);
+  for(i=0, p=1; i<s; i++) { p <<= 1; if (mask & 1) p--; mask >>= 1; }
+  x = cgetr(l);
+  a = rcopy(b); a[1] = _evalexpo(0) | evalsigne(1);
+  affrr(invr_basecase(rtor(a, p+2)), x);
+  while (mask > 1)
+  {
+    p <<= 1; if (mask & 1) p--;
+    mask >>= 1;
+    setlg(a, p + 2);
+    setlg(x, p + 2);
+    /* TODO: mulrr(a,x) should be a half product (the higher half is known).
+     * mulrr(x, ) already is */
+    affrr(addrr(x, mulrr(x, subsr(1, mulrr(a,x)))), x);
+    avma = (pari_sp)a;
+  }
+  x[1] = (b[1] & SIGNBITS) | evalexpo(expo(x)-expo(b));
+  avma = (pari_sp)x; return x;
+}
+
+GEN
+modii(GEN x, GEN y)
+{
+  switch(signe(x))
+  {
+    case 0: return gen_0;
+    case 1: return remii(x,y);
+    default:
+    {
+      pari_sp av = avma;
+      (void)new_chunk(lgefint(y));
+      x = remii(x,y); avma=av;
+      if (x==gen_0) return x;
+      return subiispec(y+2,x+2,lgefint(y)-2,lgefint(x)-2);
+    }
+  }
+}
+
+void
+modiiz(GEN x, GEN y, GEN z)
+{
+  const pari_sp av = avma;
+  affii(modii(x,y),z); avma=av;
+}
+
+GEN
+divrs(GEN x, long y)
+{
+  long i, lx, garde, sh, s = signe(x);
+  GEN z;
+  LOCAL_HIREMAINDER;
+
+  if (!y) pari_err_INV("divrs",gen_0);
+  if (y<0) { s = -s; y = -y; }
+  if (!s) return real_0_bit(expo(x) - expu(y));
+  if (y==1) { z = rcopy(x); setsigne(z,s); return z; }
+  if (y==2) { z = shiftr(x, -1); setsigne(z,s); return z; }
+
+  z=cgetr(lx=lg(x)); hiremainder=0;
+  for (i=2; i<lx; i++) z[i] = divll(x[i],y);
+
+  /* we may have hiremainder != 0 ==> garde */
+  garde=divll(0,y); sh=bfffo(z[2]);
+  if (sh) shift_left(z,z, 2,lx-1, garde,sh);
+  z[1] = evalsigne(s) | evalexpo(expo(x)-sh);
+  if ((garde << sh) & HIGHBIT) roundr_up_ip(z, lx);
+  return z;
+}
+
+GEN
+divru(GEN x, ulong y)
+{
+  long i, lx, garde, sh, e, s = signe(x);
+  GEN z;
+  LOCAL_HIREMAINDER;
+
+  if (!y) pari_err_INV("divru",gen_0);
+  if (!s) return real_0_bit(expo(x) - expu(y));
+  if (y==1) return rcopy(x);
+  if (y==2) return shiftr(x, -1);
+
+  e = expo(x);
+  lx = lg(x);
+  z = cgetr(lx);
+  if (y <= (ulong)x[2])
+  {
+    hiremainder = 0;
+    for (i=2; i<lx; i++) z[i] = divll(x[i],y);
+    /* we may have hiremainder != 0 ==> garde */
+    garde = divll(0,y);
+  }
+  else
+  {
+    long l = lx-1;
+    hiremainder = x[2];
+    for (i=2; i<l; i++) z[i] = divll(x[i+1],y);
+    z[i] = divll(0,y);
+    garde = hiremainder;
+    e -= BITS_IN_LONG;
+  }
+  sh=bfffo(z[2]); /* z[2] != 0 */
+  if (sh) shift_left(z,z, 2,lx-1, garde,sh);
+  z[1] = evalsigne(s) | evalexpo(e-sh);
+  if ((garde << sh) & HIGHBIT) roundr_up_ip(z, lx);
+  return z;
+}
+
+GEN
+truedvmdii(GEN x, GEN y, GEN *z)
+{
+  pari_sp av;
+  GEN r, q, *gptr[2];
+  if (!is_bigint(y)) return truedvmdis(x, itos(y), z);
+  if (z == ONLY_REM) return modii(x,y);
+
+  av = avma;
+  q = dvmdii(x,y,&r); /* assume that r is last on stack */
+  switch(signe(r))
+  {
+    case 0:
+      if (z) *z = gen_0;
+      return q;
+    case 1:
+      if (z) *z = r; else cgiv(r);
+      return q;
+    case -1: break;
+  }
+  q = addis(q, -signe(y));
+  if (!z) return gerepileuptoint(av, q);
+
+  *z = subiispec(y+2,r+2, lgefint(y)-2,lgefint(r)-2);
+  gptr[0]=&q; gptr[1]=z; gerepilemanysp(av,(pari_sp)r,gptr,2);
+  return q;
+}
+GEN
+truedvmdis(GEN x, long y, GEN *z)
+{
+  pari_sp av = avma;
+  long r;
+  GEN q;
+
+  if (z == ONLY_REM) return modis(x, y);
+  q = divis_rem(x,y,&r);
+
+  if (r >= 0)
+  {
+    if (z) *z = utoi(r);
+    return q;
+  }
+  q = gerepileuptoint(av, addis(q, (y < 0)? 1: -1));
+  if (z) *z = utoi(r + labs(y));
+  return q;
+}
+GEN
+truedvmdsi(long x, GEN y, GEN *z)
+{
+  long q, r;
+  if (z == ONLY_REM) return modsi(x, y);
+  q = sdivsi_rem(x,y,&r);
+  if (r >= 0) {
+    if (z) *z = utoi(r);
+    return stoi(q);
+  }
+  q = q - signe(y);
+  if (!z) return stoi(q);
+
+  *z = subiuspec(y+2,(ulong)-r, lgefint(y)-2);
+  return stoi(q);
+}
+
+/* 2^n = shifti(gen_1, n) */
+GEN
+int2n(long n) {
+  long i, m, l;
+  GEN z;
+  if (n < 0) return gen_0;
+  if (n == 0) return gen_1;
+
+  l = dvmdsBIL(n, &m) + 3;
+  z = cgetipos(l);
+  for (i = 2; i < l; i++) z[i] = 0;
+  *int_MSW(z) = 1L << m; return z;
+}
+/* To avoid problems when 2^(BIL-1) < n. Overflow cleanly, where int2n
+ * returns gen_0 */
+GEN
+int2u(ulong n) {
+  ulong i, m, l;
+  GEN z;
+  if (n == 0) return gen_1;
+
+  l = dvmduBIL(n, &m) + 3;
+  z = cgetipos(l);
+  for (i = 2; i < l; i++) z[i] = 0;
+  *int_MSW(z) = 1L << m; return z;
+}
+
+GEN
+shifti(GEN x, long n)
+{
+  long s = signe(x);
+  GEN y;
+
+  if(s == 0) return gen_0;
+  y = shiftispec(x + 2, lgefint(x) - 2, n);
+  if (signe(y)) setsigne(y, s);
+  return y;
+}
+
+/* actual operations will take place on a+2 and b+2: we strip the codewords */
+GEN
+mulii(GEN a,GEN b)
+{
+  long sa,sb;
+  GEN z;
+
+  sa=signe(a); if (!sa) return gen_0;
+  sb=signe(b); if (!sb) return gen_0;
+  if (sb<0) sa = -sa;
+  z = muliispec(a+2,b+2, lgefint(a)-2,lgefint(b)-2);
+  setsigne(z,sa); return z;
+}
+
+GEN
+sqri(GEN a) { return sqrispec(a+2, lgefint(a)-2); }
+
+/* sqrt()'s result may be off by 1 when a is not representable exactly as a
+ * double [64bit machine] */
+ulong
+usqrt(ulong a)
+{
+  ulong x = (ulong)sqrt((double)a);
+#ifdef LONG_IS_64BIT
+  if (x > LOWMASK || x*x > a) x--;
+#endif
+  return x;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**              EXPONENT / CONVERSION t_REAL --> double           **/
+/**                                                                **/
+/********************************************************************/
+
+#ifdef LONG_IS_64BIT
+long
+dblexpo(double x)
+{
+  union { double f; ulong i; } fi;
+  const int mant_len = 52;  /* mantissa bits (excl. hidden bit) */
+  const int exp_mid = 0x3ff;/* exponent bias */
+
+  if (x==0.) return -exp_mid;
+  fi.f = x;
+  return ((fi.i & (HIGHBIT-1)) >> mant_len) - exp_mid;
+}
+
+ulong
+dblmantissa(double x)
+{
+  union { double f; ulong i; } fi;
+  const int expo_len = 11; /* number of bits of exponent */
+
+  if (x==0.) return 0;
+  fi.f = x;
+  return (fi.i << expo_len) | HIGHBIT;
+}
+
+GEN
+dbltor(double x)
+{
+  GEN z;
+  long e;
+  union { double f; ulong i; } fi;
+  const int mant_len = 52;  /* mantissa bits (excl. hidden bit) */
+  const int exp_mid = 0x3ff;/* exponent bias */
+  const int expo_len = 11; /* number of bits of exponent */
+
+  if (x==0.) return real_0_bit(-exp_mid);
+  fi.f = x; z = cgetr(DEFAULTPREC);
+  {
+    const ulong a = fi.i;
+    ulong A;
+    e = ((a & (HIGHBIT-1)) >> mant_len) - exp_mid;
+    if (e == exp_mid+1) pari_err_OVERFLOW("dbltor [NaN or Infinity]");
+    A = a << expo_len;
+    if (e == -exp_mid)
+    { /* unnormalized values */
+      int sh = bfffo(A);
+      e -= sh-1;
+      z[2] = A << sh;
+    }
+    else
+      z[2] = HIGHBIT | A;
+    z[1] = _evalexpo(e) | evalsigne(x<0? -1: 1);
+  }
+  return z;
+}
+
+double
+rtodbl(GEN x)
+{
+  long ex,s=signe(x);
+  ulong a;
+  union { double f; ulong i; } fi;
+  const int mant_len = 52;  /* mantissa bits (excl. hidden bit) */
+  const int exp_mid = 0x3ff;/* exponent bias */
+  const int expo_len = 11; /* number of bits of exponent */
+
+  if (!s || (ex=expo(x)) < - exp_mid) return 0.0;
+
+  /* start by rounding to closest */
+  a = (x[2] & (HIGHBIT-1)) + 0x400;
+  if (a & HIGHBIT) { ex++; a=0; }
+  if (ex >= exp_mid) pari_err_OVERFLOW("t_REAL->double conversion");
+  fi.i = ((ex + exp_mid) << mant_len) | (a >> expo_len);
+  if (s<0) fi.i |= HIGHBIT;
+  return fi.f;
+}
+
+#else /* LONG_IS_64BIT */
+
+#if   PARI_DOUBLE_FORMAT == 1
+#  define INDEX0 1
+#  define INDEX1 0
+#elif PARI_DOUBLE_FORMAT == 0
+#  define INDEX0 0
+#  define INDEX1 1
+#endif
+
+long
+dblexpo(double x)
+{
+  union { double f; ulong i[2]; } fi;
+  const int mant_len = 52;  /* mantissa bits (excl. hidden bit) */
+  const int exp_mid = 0x3ff;/* exponent bias */
+  const int shift = mant_len-32;
+
+  if (x==0.) return -exp_mid;
+  fi.f = x;
+  {
+    const ulong a = fi.i[INDEX0];
+    return ((a & (HIGHBIT-1)) >> shift) - exp_mid;
+  }
+}
+
+ulong
+dblmantissa(double x)
+{
+  union { double f; ulong i[2]; } fi;
+  const int expo_len = 11; /* number of bits of exponent */
+
+  if (x==0.) return 0;
+  fi.f = x;
+  {
+    const ulong a = fi.i[INDEX0];
+    const ulong b = fi.i[INDEX1];
+    return HIGHBIT | b >> (BITS_IN_LONG-expo_len) | (a << expo_len);
+  }
+}
+
+GEN
+dbltor(double x)
+{
+  GEN z;
+  long e;
+  union { double f; ulong i[2]; } fi;
+  const int mant_len = 52;  /* mantissa bits (excl. hidden bit) */
+  const int exp_mid = 0x3ff;/* exponent bias */
+  const int expo_len = 11; /* number of bits of exponent */
+  const int shift = mant_len-32;
+
+  if (x==0.) return real_0_bit(-exp_mid);
+  fi.f = x; z = cgetr(DEFAULTPREC);
+  {
+    const ulong a = fi.i[INDEX0];
+    const ulong b = fi.i[INDEX1];
+    ulong A, B;
+    e = ((a & (HIGHBIT-1)) >> shift) - exp_mid;
+    if (e == exp_mid+1) pari_err_OVERFLOW("dbltor [NaN or Infinity]");
+    A = b >> (BITS_IN_LONG-expo_len) | (a << expo_len);
+    B = b << expo_len;
+    if (e == -exp_mid)
+    { /* unnormalized values */
+      int sh;
+      if (A)
+      {
+        sh = bfffo(A);
+        e -= sh-1;
+        z[2] = (A << sh) | (B >> (32-sh));
+        z[3] = B << sh;
+      }
+      else
+      {
+        sh = bfffo(B); /* B != 0 */
+        e -= sh-1 + 32;
+        z[2] = B << sh;
+        z[3] = 0;
+      }
+    }
+    else
+    {
+      z[3] = B;
+      z[2] = HIGHBIT | A;
+    }
+    z[1] = _evalexpo(e) | evalsigne(x<0? -1: 1);
+  }
+  return z;
+}
+
+double
+rtodbl(GEN x)
+{
+  long ex,s=signe(x),lx=lg(x);
+  ulong a,b,k;
+  union { double f; ulong i[2]; } fi;
+  const int mant_len = 52;  /* mantissa bits (excl. hidden bit) */
+  const int exp_mid = 0x3ff;/* exponent bias */
+  const int expo_len = 11; /* number of bits of exponent */
+  const int shift = mant_len-32;
+
+  if (!s || (ex=expo(x)) < - exp_mid) return 0.0;
+
+  /* start by rounding to closest */
+  a = x[2] & (HIGHBIT-1);
+  if (lx > 3)
+  {
+    b = x[3] + 0x400UL; if (b < 0x400UL) a++;
+    if (a & HIGHBIT) { ex++; a=0; }
+  }
+  else b = 0;
+  if (ex >= exp_mid) pari_err_OVERFLOW("t_REAL->double conversion");
+  ex += exp_mid;
+  k = (a >> expo_len) | (ex << shift);
+  if (s<0) k |= HIGHBIT;
+  fi.i[INDEX0] = k;
+  fi.i[INDEX1] = (a << (BITS_IN_LONG-expo_len)) | (b >> expo_len);
+  return fi.f;
+}
+#endif /* LONG_IS_64BIT */
+
diff --git a/src/kernel/none/mpinl.c b/src/kernel/none/mpinl.c
new file mode 100644
index 0000000..180a62d
--- /dev/null
+++ b/src/kernel/none/mpinl.c
@@ -0,0 +1,19 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#define PARI_NO_PARIINL_H
+#define INLINE
+#ifndef DISABLE_INLINE
+#  define DISABLE_INLINE
+#endif
+#include "pari.h"
+ulong hiremainder, overflow;
diff --git a/src/kernel/none/mulll.h b/src/kernel/none/mulll.h
new file mode 100644
index 0000000..7c7b811
--- /dev/null
+++ b/src/kernel/none/mulll.h
@@ -0,0 +1,140 @@
+#line 2 "../src/kernel/none/mulll.h"
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#undef  LOCAL_HIREMAINDER
+#define LOCAL_HIREMAINDER
+extern ulong hiremainder;
+
+/* Version Peter Montgomery */
+/*
+ *      Assume (for presentation) that BITS_IN_LONG = 32.
+ *      Then 0 <= xhi, xlo, yhi, ylo <= 2^16 - 1.  Hence
+ *
+ * -2^31 + 2^16 <= (xhi-2^15)*(ylo-2^15) + (xlo-2^15)*(yhi-2^15) <= 2^31.
+ *
+ *      If xhi*ylo + xlo*yhi = 2^32*overflow + xymid, then
+ *
+ * -2^32 + 2^16 <= 2^32*overflow + xymid - 2^15*(xhi + ylo + xlo + yhi) <= 0.
+ *
+ * 2^16*overflow <= (xhi+xlo+yhi+ylo)/2 - xymid/2^16 <= 2^16*overflow + 2^16-1
+ *
+ *       This inequality was derived using exact (rational) arithmetic;
+ *       it remains valid when we truncate the two middle terms.
+ */
+
+#if !defined(INLINE)
+extern long mulll(ulong x, ulong y);
+extern long addmul(ulong x, ulong y);
+#else
+
+#if defined(__GNUC__) && !defined(DISABLE_INLINE)
+#undef LOCAL_HIREMAINDER
+#define LOCAL_HIREMAINDER register ulong hiremainder
+
+#define mulll(x, y) \
+__extension__ ({ \
+  const ulong __x = (x), __y = (y);\
+  const ulong __xlo = LOWWORD(__x), __xhi = HIGHWORD(__x); \
+  const ulong __ylo = LOWWORD(__y), __yhi = HIGHWORD(__y); \
+  ulong __xylo,__xymid,__xyhi,__xymidhi,__xymidlo; \
+  ulong __xhl,__yhl; \
+ \
+  __xylo = __xlo*__ylo; __xyhi = __xhi*__yhi; \
+  __xhl = __xhi+__xlo; __yhl = __yhi+__ylo; \
+  __xymid = __xhl*__yhl - (__xyhi+__xylo); \
+ \
+  __xymidhi = HIGHWORD(__xymid); \
+  __xymidlo = __xymid << BITS_IN_HALFULONG; \
+ \
+  __xylo += __xymidlo; \
+  hiremainder = __xyhi + __xymidhi + (__xylo < __xymidlo) \
+     + ((((__xhl + __yhl) >> 1) - __xymidhi) & HIGHMASK); \
+ \
+  __xylo; \
+})
+
+#define addmul(x, y) \
+__extension__ ({                                           \
+  const ulong __x = (x), __y = (y);\
+  const ulong __xlo = LOWWORD(__x), __xhi = HIGHWORD(__x); \
+  const ulong __ylo = LOWWORD(__y), __yhi = HIGHWORD(__y); \
+  ulong __xylo,__xymid,__xyhi,__xymidhi,__xymidlo; \
+  ulong __xhl,__yhl; \
+ \
+  __xylo = __xlo*__ylo; __xyhi = __xhi*__yhi; \
+  __xhl = __xhi+__xlo; __yhl = __yhi+__ylo; \
+  __xymid = __xhl*__yhl - (__xyhi+__xylo); \
+ \
+  __xylo += hiremainder; __xyhi += (__xylo < hiremainder); \
+ \
+  __xymidhi = HIGHWORD(__xymid); \
+  __xymidlo = __xymid << BITS_IN_HALFULONG; \
+ \
+  __xylo += __xymidlo; \
+  hiremainder = __xyhi + __xymidhi + (__xylo < __xymidlo) \
+     + ((((__xhl + __yhl) >> 1) - __xymidhi) & HIGHMASK); \
+ \
+  __xylo; \
+})
+
+#else
+
+INLINE long
+mulll(ulong x, ulong y)
+{
+  const ulong xlo = LOWWORD(x), xhi = HIGHWORD(x);
+  const ulong ylo = LOWWORD(y), yhi = HIGHWORD(y);
+  ulong xylo,xymid,xyhi,xymidhi,xymidlo;
+  ulong xhl,yhl;
+
+  xylo = xlo*ylo; xyhi = xhi*yhi;
+  xhl = xhi+xlo; yhl = yhi+ylo;
+  xymid = xhl*yhl - (xyhi+xylo);
+
+  xymidhi = HIGHWORD(xymid);
+  xymidlo = xymid << BITS_IN_HALFULONG;
+
+  xylo += xymidlo;
+  hiremainder = xyhi + xymidhi + (xylo < xymidlo)
+     + ((((xhl + yhl) >> 1) - xymidhi) & HIGHMASK);
+
+  return xylo;
+}
+
+INLINE long
+addmul(ulong x, ulong y)
+{
+  const ulong xlo = LOWWORD(x), xhi = HIGHWORD(x);
+  const ulong ylo = LOWWORD(y), yhi = HIGHWORD(y);
+  ulong xylo,xymid,xyhi,xymidhi,xymidlo;
+  ulong xhl,yhl;
+
+  xylo = xlo*ylo; xyhi = xhi*yhi;
+  xhl = xhi+xlo; yhl = yhi+ylo;
+  xymid = xhl*yhl - (xyhi+xylo);
+
+  xylo += hiremainder; xyhi += (xylo < hiremainder);
+
+  xymidhi = HIGHWORD(xymid);
+  xymidlo = xymid << BITS_IN_HALFULONG;
+
+  xylo += xymidlo;
+  hiremainder = xyhi + xymidhi + (xylo < xymidlo)
+     + ((((xhl + yhl) >> 1) - xymidhi) & HIGHMASK);
+
+  return xylo;
+}
+#endif
+
+#endif
diff --git a/src/kernel/none/ratlift.c b/src/kernel/none/ratlift.c
new file mode 100644
index 0000000..94cfd6e
--- /dev/null
+++ b/src/kernel/none/ratlift.c
@@ -0,0 +1,289 @@
+#line 2 "../src/kernel/none/ratlift.c"
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*==========================================================
+ * Fp_ratlift(GEN x, GEN m, GEN *a, GEN *b, GEN amax, GEN bmax)
+ *==========================================================
+ * Reconstruct rational number from its residue x mod m
+ *    Given t_INT x, m, amax>=0, bmax>0 such that
+ *         0 <= x < m;  2*amax*bmax < m
+ *    attempts to find t_INT a, b such that
+ *         (1) a = b*x (mod m)
+ *         (2) |a| <= amax, 0 < b <= bmax
+ *         (3) gcd(m, b) = gcd(a, b)
+ *    If unsuccessful, it will return 0 and leave a,b unchanged  (and
+ *    caller may deduce no such a,b exist).  If successful, sets a,b
+ *    and returns 1.  If there exist a,b satisfying (1), (2), and
+ *         (3') gcd(m, b) = 1
+ *    then they are uniquely determined subject to (1),(2) and
+ *         (3'') gcd(a, b) = 1,
+ *    and will be returned by the routine.  (The caller may wish to
+ *    check gcd(a,b)==1, either directly or based on known prime
+ *    divisors of m, depending on the application.)
+ * Reference:
+ @article {MR97c:11116,
+     AUTHOR = {Collins, George E. and Encarnaci{\'o}n, Mark J.},
+      TITLE = {Efficient rational number reconstruction},
+    JOURNAL = {J. Symbolic Comput.},
+     VOLUME = {20},
+       YEAR = {1995},
+     NUMBER = {3},
+      PAGES = {287--297},
+ }
+ * Preprint available from:
+ * ftp://ftp.risc.uni-linz.ac.at/pub/techreports/1994/94-64.ps.gz */
+
+/* #define DEBUG_RATLIFT */
+static ulong
+get_vmax(GEN r, long lb, long lbb)
+{
+  long lr = lb - lgefint(r);
+  ulong vmax;
+  if (lr > 1)        /* still more than a word's worth to go */
+    vmax = ULONG_MAX;        /* (cannot in fact happen) */
+  else
+  { /* take difference of bit lengths */
+    long lbr = bfffo(*int_MSW(r));
+    lr = lr*BITS_IN_LONG - lbb + lbr;
+    if ((ulong)lr > BITS_IN_LONG)
+      vmax = ULONG_MAX;
+    else if (lr == 0)
+      vmax = 1UL;
+    else
+      vmax = 1UL << (lr-1); /* pessimistic but faster than a division */
+  }
+#ifdef DEBUG_RATLIFT
+  err_printf("rl-fs: vmax=%lu\n", vmax);
+#endif
+  return vmax;
+}
+
+/* Assume x,m,amax >= 0,bmax > 0 are t_INTs, 0 <= x < m, 2 amax * bmax < m */
+int
+Fp_ratlift(GEN x, GEN m, GEN amax, GEN bmax, GEN *a, GEN *b)
+{
+  GEN d, d1, v, v1, q, r;
+  pari_sp av = avma, av1, lim;
+  long lb, lbb, s, s0;
+  ulong vmax;
+  ulong xu, xu1, xv, xv1; /* Lehmer stage recurrence matrix */
+  int lhmres;             /* Lehmer stage return value */
+
+  /* special cases x=0 and/or amax=0 */
+  if (!signe(x)) { *a = gen_0; *b = gen_1; return 1; }
+  if (!signe(amax)) return 0;
+  /* assert: m > x > 0, amax > 0 */
+
+  /* check whether a=x, b=1 is a solution */
+  if (cmpii(x,amax) <= 0) { *a = icopy(x); *b = gen_1; return 1; }
+
+  /* There is no special case for single-word numbers since this is
+   * mainly meant to be used with large moduli. */
+  (void)new_chunk(lgefint(bmax) + lgefint(amax)); /* room for a,b */
+  d = m; d1 = x;
+  v = gen_0; v1 = gen_1;
+  /* assert d1 > amax, v1 <= bmax here */
+  lb = lgefint(bmax);
+  lbb = bfffo(*int_MSW(bmax));
+  s = 1;
+  av1 = avma; lim = stack_lim(av, 1);
+
+  /* General case: Euclidean division chain starting with m div x, and
+   * with bounds on the sequence of convergents' denoms v_j.
+   * Just to be different from what invmod and bezout are doing, we work
+   * here with the all-nonnegative matrices [u,u1;v,v1]=prod_j([0,1;1,q_j]).
+   * Loop invariants:
+   * (a) (sign)*[-v,v1]*x = [d,d1] (mod m)  (componentwise)
+   * (sign initially +1, changes with each Euclidean step)
+   * so [a,b] will be obtained in the form [-+d,v] or [+-d1,v1];
+   * this congruence is a consequence of
+   *
+   * (b) [x,m]~ = [u,u1;v,v1]*[d1,d]~,
+   * where u,u1 is the usual numerator sequence starting with 1,0
+   * instead of 0,1  (just multiply the eqn on the left by the inverse
+   * matrix, which is det*[v1,-u1;-v,u], where "det" is the same as the
+   * "(sign)" in (a)).  From m = v*d1 + v1*d and
+   *
+   * (c) d > d1 >= 0, 0 <= v < v1,
+   * we have d >= m/(2*v1), so while v1 remains smaller than m/(2*amax),
+   * the pair [-(sign)*d,v] satisfies (1) but violates (2) (d > amax).
+   * Conversely, v1 > bmax indicates that no further solutions will be
+   * forthcoming;  [-(sign)*d,v] will be the last, and first, candidate.
+   * Thus there's at most one point in the chain division where a solution
+   * can live:  v < bmax, v1 >= m/(2*amax) > bmax,  and this is acceptable
+   * iff in fact d <= amax  (e.g. m=221, x=34 or 35, amax=bmax=10 fail on
+   * this count while x=32,33,36,37 succeed).  However, a division may leave
+   * a zero residue before we ever reach this point  (consider m=210, x=35,
+   * amax=bmax=10),  and our caller may find that gcd(d,v) > 1  (Examples:
+   * keep m=210 and consider any of x=29,31,32,33,34,36,37,38,39,40,41).
+   * Furthermore, at the start of the loop body we have in fact
+   *
+   * (c') 0 <= v < v1 <= bmax, d > d1 > amax >= 0,
+   * (and are never done already).
+   *
+   * Main loop is similar to those of invmod() and bezout(), except for
+   * having to determine appropriate vmax bounds, and checking termination
+   * conditions.  The signe(d1) condition is only for paranoia */
+  while (lgefint(d) > 3 && signe(d1))
+  {
+    /* determine vmax for lgcdii so as to ensure v won't overshoot.
+     * If v+v1 > bmax, the next step would take v1 beyond the limit, so
+     * since [+-d1,v1] is not a solution, we give up.  Otherwise if v+v1
+     * is way shorter than bmax, use vmax=MAXULUNG.  Otherwise, set vmax
+     * to a crude lower approximation of bmax/(v+v1), or to 1, which will
+     * allow the inner loop to do one step */
+    r = addii(v,v1);
+    if (cmpii(r,bmax) > 0) { avma = av; return 0; } /* done, not found */
+    vmax = get_vmax(r, lb, lbb);
+    /* do a Lehmer-Jebelean round */
+    lhmres = lgcdii((ulong *)d, (ulong *)d1, &xu, &xu1, &xv, &xv1, vmax);
+    if (lhmres) /* check progress */
+    { /* apply matrix */
+      if (lhmres == 1 || lhmres == -1)
+      {
+        s = -s;
+        if (xv1 == 1)
+        { /* re-use v+v1 computed above */
+          v = v1; v1 = r;
+          r = subii(d,d1); d = d1; d1 = r;
+        }
+        else
+        {
+          r = subii(d, mului(xv1,d1)); d = d1; d1 = r;
+          r = addii(v, mului(xv1,v1)); v = v1; v1 = r;
+        }
+      }
+      else
+      {
+        r  = subii(muliu(d,xu),  muliu(d1,xv));
+        d1 = subii(muliu(d,xu1), muliu(d1,xv1)); d = r;
+        r  = addii(muliu(v,xu),  muliu(v1,xv));
+        v1 = addii(muliu(v,xu1), muliu(v1,xv1)); v = r;
+        if (lhmres&1) { togglesign(d); s = -s; } else togglesign(d1);
+      }
+      /* check whether we're done.  Assert v <= bmax here.  Examine v1:
+       * if v1 > bmax, check d and return 0 or 1 depending on the outcome;
+       * if v1 <= bmax, check d1 and return 1 if d1 <= amax, otherwise proceed*/
+      if (cmpii(v1,bmax) > 0)
+      {
+        avma = av;
+        if (cmpii(d,amax) > 0) return 0; /* done, not found */
+        /* done, found */
+        *a = icopy(d); setsigne(*a,-s);
+        *b = icopy(v); return 1;
+      }
+      if (cmpii(d1,amax) <= 0)
+      { /* done, found */
+        avma = av;
+        if (signe(d1)) { *a = icopy(d1); setsigne(*a,s); } else *a = gen_0;
+        *b = icopy(v1); return 1;
+      }
+    } /* lhmres != 0 */
+
+    if (lhmres <= 0 && signe(d1))
+    {
+      q = dvmdii(d,d1,&r);
+#ifdef DEBUG_LEHMER
+      err_printf("Full division:\n  q = %Ps\n", q);
+#endif
+      d = d1; d1 = r;
+      r = addii(v, mulii(q,v1));
+      v = v1; v1 = r;
+      s = -s;
+      /* check whether we are done now.  Since we weren't before the div, it
+       * suffices to examine v1 and d1 -- the new d (former d1) cannot cut it */
+      if (cmpii(v1,bmax) > 0) { avma = av; return 0; } /* done, not found */
+      if (cmpii(d1,amax) <= 0) /* done, found */
+      {
+        avma = av;
+        if (signe(d1)) { *a = icopy(d1); setsigne(*a,s); } else *a = gen_0;
+        *b = icopy(v1); return 1;
+      }
+    }
+
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"ratlift");
+      gerepileall(av1, 4, &d, &d1, &v, &v1);
+    }
+  } /* end while */
+
+  /* Postprocessing - final sprint.  Since we usually underestimate vmax,
+   * this function needs a loop here instead of a simple conditional.
+   * Note we can only get here when amax fits into one word  (which will
+   * typically not be the case!).  The condition is bogus -- d1 is never
+   * zero at the start of the loop.  There will be at most a few iterations,
+   * so we don't bother collecting garbage */
+  while (signe(d1))
+  {
+    /* Assertions: lgefint(d)==lgefint(d1)==3.
+     * Moreover, we aren't done already, or we would have returned by now.
+     * Recompute vmax */
+#ifdef DEBUG_RATLIFT
+    err_printf("rl-fs: d,d1=%Ps,%Ps\n", d, d1);
+    err_printf("rl-fs: v,v1=%Ps,%Ps\n", v, v1);
+#endif
+    r = addii(v,v1);
+    if (cmpii(r,bmax) > 0) { avma = av; return 0; } /* done, not found */
+    vmax = get_vmax(r, lb, lbb);
+    /* single-word "Lehmer", discarding the gcd or whatever it returns */
+    (void)rgcduu((ulong)*int_MSW(d), (ulong)*int_MSW(d1), vmax, &xu, &xu1, &xv, &xv1, &s0);
+#ifdef DEBUG_RATLIFT
+    err_printf("rl-fs: [%lu,%lu; %lu,%lu] %s\n",
+               xu, xu1, xv, xv1, s0 < 0 ? "-" : "+");
+#endif
+    if (xv1 == 1) /* avoid multiplications */
+    { /* re-use r = v+v1 computed above */
+      v = v1; v1 = r;
+      r = subii(d,d1); d = d1; d1 = r;
+      s = -s;
+    }
+    else if (xu == 0) /* and xv==1, xu1==1, xv1 > 1 */
+    {
+      r = subii(d, mului(xv1,d1)); d = d1; d1 = r;
+      r = addii(v, mului(xv1,v1)); v = v1; v1 = r;
+      s = -s;
+    }
+    else
+    {
+      r  = subii(muliu(d,xu),  muliu(d1,xv));
+      d1 = subii(muliu(d,xu1), muliu(d1,xv1)); d = r;
+      r  = addii(muliu(v,xu),  muliu(v1,xv));
+      v1 = addii(muliu(v,xu1), muliu(v1,xv1)); v = r;
+      if (s0 < 0) { togglesign(d); s = -s; } else togglesign(d1);
+    }
+    /* check whether we're done, as above.  Assert v <= bmax.
+     * if v1 > bmax, check d and return 0 or 1 depending on the outcome;
+     * if v1 <= bmax, check d1 and return 1 if d1 <= amax, otherwise proceed.
+     */
+    if (cmpii(v1,bmax) > 0)
+    {
+      avma = av;
+      if (cmpii(d,amax) > 0) return 0; /* done, not found */
+      /* done, found */
+      *a = icopy(d); setsigne(*a,-s);
+      *b = icopy(v); return 1;
+    }
+    if (cmpii(d1,amax) <= 0)
+    { /* done, found */
+      avma = av;
+      if (signe(d1)) { *a = icopy(d1); setsigne(*a,s); } else *a = gen_0;
+      *b = icopy(v1); return 1;
+    }
+  } /* while */
+
+  /* We have run into d1 == 0 before returning. This cannot happen */
+  pari_err_BUG("ratlift failed to catch d1 == 0");
+  return 0; /* NOTREACHED */
+}
diff --git a/src/kernel/none/tune-gen.h b/src/kernel/none/tune-gen.h
new file mode 100644
index 0000000..df60f49
--- /dev/null
+++ b/src/kernel/none/tune-gen.h
@@ -0,0 +1,40 @@
+#ifdef PARI_TUNE
+long SQRI_FFT_LIMIT           = __SQRI_FFT_LIMIT;
+long MULII_FFT_LIMIT          = __MULII_FFT_LIMIT;
+long SQRI_KARATSUBA_LIMIT     = __SQRI_KARATSUBA_LIMIT;
+long MULII_KARATSUBA_LIMIT    = __MULII_KARATSUBA_LIMIT;
+long MULRR_MULII_LIMIT        = __MULRR_MULII_LIMIT;
+long Fp_POW_REDC_LIMIT        = __Fp_POW_REDC_LIMIT;
+long Fp_POW_BARRETT_LIMIT     = __Fp_POW_BARRETT_LIMIT;
+long INVMOD_GMP_LIMIT         = __INVMOD_GMP_LIMIT;
+long DIVRR_GMP_LIMIT          = __DIVRR_GMP_LIMIT;
+long Flx_MUL_KARATSUBA_LIMIT  = __Flx_MUL_KARATSUBA_LIMIT;
+long Flx_SQR_KARATSUBA_LIMIT  = __Flx_SQR_KARATSUBA_LIMIT;
+long Flx_MUL_HALFMULII_LIMIT  = __Flx_MUL_HALFMULII_LIMIT;
+long Flx_SQR_HALFSQRI_LIMIT   = __Flx_SQR_HALFSQRI_LIMIT;
+long Flx_MUL_MULII_LIMIT      = __Flx_MUL_MULII_LIMIT;
+long Flx_SQR_SQRI_LIMIT       = __Flx_SQR_SQRI_LIMIT;
+long Flx_MUL_MULII2_LIMIT     = __Flx_MUL_MULII2_LIMIT;
+long Flx_SQR_SQRI2_LIMIT      = __Flx_SQR_SQRI2_LIMIT;
+long Flx_INVBARRETT_LIMIT     = __Flx_INVBARRETT_LIMIT;
+long Flx_DIVREM_BARRETT_LIMIT = __Flx_DIVREM_BARRETT_LIMIT;
+long Flx_REM_BARRETT_LIMIT    = __Flx_REM_BARRETT_LIMIT;
+long Flx_BARRETT_LIMIT        = __Flx_BARRETT_LIMIT;
+long Flx_HALFGCD_LIMIT        = __Flx_HALFGCD_LIMIT;
+long Flx_GCD_LIMIT            = __Flx_GCD_LIMIT;
+long Flx_EXTGCD_LIMIT         = __Flx_EXTGCD_LIMIT;
+long FpX_INVBARRETT_LIMIT     = __FpX_INVBARRETT_LIMIT;
+long FpX_DIVREM_BARRETT_LIMIT = __FpX_DIVREM_BARRETT_LIMIT;
+long FpX_REM_BARRETT_LIMIT    = __FpX_REM_BARRETT_LIMIT;
+long FpX_BARRETT_LIMIT        = __FpX_BARRETT_LIMIT;
+long FpX_HALFGCD_LIMIT        = __FpX_HALFGCD_LIMIT;
+long FpX_GCD_LIMIT            = __FpX_GCD_LIMIT;
+long FpX_EXTGCD_LIMIT         = __FpX_EXTGCD_LIMIT;
+long EXPNEWTON_LIMIT          = __EXPNEWTON_LIMIT;
+long INVNEWTON_LIMIT          = __INVNEWTON_LIMIT;
+long LOGAGM_LIMIT             = __LOGAGM_LIMIT;
+long LOGAGMCX_LIMIT           = __LOGAGMCX_LIMIT;
+long AGM_ATAN_LIMIT           = __AGM_ATAN_LIMIT;
+long RgX_SQR_LIMIT            = __RgX_SQR_LIMIT;
+long RgX_MUL_LIMIT            = __RgX_MUL_LIMIT;
+#endif
diff --git a/src/kernel/none/tune.h b/src/kernel/none/tune.h
new file mode 100644
index 0000000..9753e41
--- /dev/null
+++ b/src/kernel/none/tune.h
@@ -0,0 +1,79 @@
+#ifdef LONG_IS_64BIT
+#define __MULII_KARATSUBA_LIMIT         23
+#define __SQRI_KARATSUBA_LIMIT          36
+#define __MULII_FFT_LIMIT             1441
+#define __SQRI_FFT_LIMIT              1651
+#define __MULRR_MULII_LIMIT            276
+#define __Fp_POW_REDC_LIMIT             99
+#define __Fp_POW_BARRETT_LIMIT         101
+#define __INVNEWTON_LIMIT              656
+#define __DIVRR_GMP_LIMIT               -1
+#define __EXPNEWTON_LIMIT               66
+#define __LOGAGM_LIMIT                  16
+#define __LOGAGMCX_LIMIT                13
+#define __AGM_ATAN_LIMIT                56
+#define __INVMOD_GMP_LIMIT              -1
+#define __Flx_MUL_KARATSUBA_LIMIT      147
+#define __Flx_SQR_KARATSUBA_LIMIT      330
+#define __Flx_MUL_HALFMULII_LIMIT        5
+#define __Flx_SQR_HALFSQRI_LIMIT         3
+#define __Flx_MUL_MULII_LIMIT         1639
+#define __Flx_SQR_SQRI_LIMIT             5
+#define __Flx_MUL_MULII2_LIMIT           5
+#define __Flx_SQR_SQRI2_LIMIT            8
+#define __Flx_INVBARRETT_LIMIT        3577
+#define __Flx_DIVREM_BARRETT_LIMIT    2804
+#define __Flx_REM_BARRETT_LIMIT       3577
+#define __Flx_BARRETT_LIMIT           1623
+#define __Flx_HALFGCD_LIMIT             80
+#define __Flx_GCD_LIMIT               1890
+#define __Flx_EXTGCD_LIMIT             284
+#define __FpX_INVBARRETT_LIMIT         254
+#define __FpX_DIVREM_BARRETT_LIMIT     292
+#define __FpX_REM_BARRETT_LIMIT        306
+#define __FpX_BARRETT_LIMIT             85
+#define __FpX_HALFGCD_LIMIT             75
+#define __FpX_GCD_LIMIT                731
+#define __FpX_EXTGCD_LIMIT             117
+#define __RgX_MUL_LIMIT                  9
+#define __RgX_SQR_LIMIT                 35
+#else
+#define __MULII_KARATSUBA_LIMIT         18
+#define __SQRI_KARATSUBA_LIMIT          27
+#define __MULII_FFT_LIMIT             1386
+#define __SQRI_FFT_LIMIT              1469
+#define __MULRR_MULII_LIMIT            102
+#define __Fp_POW_REDC_LIMIT             99
+#define __Fp_POW_BARRETT_LIMIT          97
+#define __INVNEWTON_LIMIT              380
+#define __DIVRR_GMP_LIMIT               -1
+#define __EXPNEWTON_LIMIT               66
+#define __LOGAGM_LIMIT                  55
+#define __LOGAGMCX_LIMIT                58
+#define __AGM_ATAN_LIMIT               159
+#define __INVMOD_GMP_LIMIT              -1
+#define __Flx_MUL_KARATSUBA_LIMIT       85
+#define __Flx_SQR_KARATSUBA_LIMIT      159
+#define __Flx_MUL_HALFMULII_LIMIT        8
+#define __Flx_SQR_HALFSQRI_LIMIT         6
+#define __Flx_MUL_MULII_LIMIT          698
+#define __Flx_SQR_SQRI_LIMIT          1276
+#define __Flx_MUL_MULII2_LIMIT        3755
+#define __Flx_SQR_SQRI2_LIMIT         4139
+#define __Flx_INVBARRETT_LIMIT        4345
+#define __Flx_DIVREM_BARRETT_LIMIT    3942
+#define __Flx_REM_BARRETT_LIMIT       3942
+#define __Flx_BARRETT_LIMIT            915
+#define __Flx_HALFGCD_LIMIT            232
+#define __Flx_GCD_LIMIT               7165
+#define __Flx_EXTGCD_LIMIT             850
+#define __FpX_INVBARRETT_LIMIT         337
+#define __FpX_DIVREM_BARRETT_LIMIT     306
+#define __FpX_REM_BARRETT_LIMIT        306
+#define __FpX_BARRETT_LIMIT            144
+#define __FpX_HALFGCD_LIMIT            145
+#define __FpX_GCD_LIMIT               1292
+#define __FpX_EXTGCD_LIMIT             238
+#define __RgX_MUL_LIMIT                  5
+#define __RgX_SQR_LIMIT                 26
+#endif
diff --git a/src/kernel/ppc/asm0.h b/src/kernel/ppc/asm0.h
new file mode 100644
index 0000000..d93a6ef
--- /dev/null
+++ b/src/kernel/ppc/asm0.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/*
+ASM addll mulll bfffo
+NOASM divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("addc %0,%2,%3\n\txor %1,%2,%2\n\taddze %1,%4\n\t" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "1" ((ulong) 0)); \
+  __value; \
+})
+
+#define addllx(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("addc %0,%3,%4\n\tli %1,0\n\taddze %1,%4\n\taddc %0,%2,%5\n\taddze %1,%4\n\t" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "1" (overflow), "0" ((ulong) 0)); \
+__value; \
+})
+
+#define bfffo(a) \
+__extension__ ({ ulong __a = (a), __value; \
+    __asm__ ("cntlzw %0, %1" : "=r" (__value) : "r" (__a)); \
+    __value; \
+})
+
+#define subll(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+  __asm__ ("subfc %0,%3,%2\n\tli %1,0\n\taddme %1,%4\n\tneg %1,%4" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "1" ((ulong)0)); \
+  __value; \
+})
+
+#define subllx(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+__asm__ ("subfc %0,%5,%2\n\tli %1,0\n\taddme %1,%5\n\tsubfc %0,%3,%4\n\taddme %1,%5\n\tneg %1,%5" \
+   : "=r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong)0), "1" (overflow)); \
+ __value; \
+})
+
+#define mulll(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("mulhwu %1,%2,%3\n\tmullw %0,%2,%3\n\t" \
+   : "=r" (__value), "=&r" (hiremainder) \
+   : "r" (__arg1), "r" (__arg2)); \
+ __value; \
+})
+
+#define addmul(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+ __asm__ ("mullw %0,%3,%4\n\tmulhwu %2,%3,%4\n\taddc %0,%5,%6\n\taddze %1,%7\n\t" \
+   : "=&r" (__value), "=r" (hiremainder), "=r" (__temp) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong) 0), "1" (hiremainder), "2" ((ulong) 0)); \
+ __value; \
+})
+#endif
diff --git a/src/kernel/ppc64/asm0.h b/src/kernel/ppc64/asm0.h
new file mode 100644
index 0000000..30bec4e
--- /dev/null
+++ b/src/kernel/ppc64/asm0.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/*
+ASM addll mulll bfffo
+NOASM divll
+*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("addc %0,%2,%3\n\txor %1,%2,%2\n\taddze %1,%4\n\t" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "1" ((ulong) 0)); \
+  __value; \
+})
+
+#define addllx(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("addc %0,%3,%4\n\tli %1,0\n\taddze %1,%4\n\taddc %0,%2,%5\n\taddze %1,%4\n\t" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "1" (overflow), "0" ((ulong) 0)); \
+__value; \
+})
+
+#define bfffo(a) \
+__extension__ ({ ulong __a = (a), __value; \
+    __asm__ ("cntlzd %0, %1" : "=r" (__value) : "r" (__a)); \
+    __value; \
+})
+
+#define subll(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+  __asm__ ("subfc %0,%3,%2\n\tli %1,0\n\taddme %1,%4\n\tneg %1,%4" \
+   : "=&r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "1" ((ulong)0)); \
+  __value; \
+})
+
+#define subllx(a, b)\
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+__asm__ ("subfc %0,%5,%2\n\tli %1,0\n\taddme %1,%5\n\tsubfc %0,%3,%4\n\taddme %1,%5\n\tneg %1,%5" \
+   : "=r" (__value), "=r" (overflow) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong)0), "1" (overflow)); \
+ __value; \
+})
+
+#define mulll(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+ __asm__ ("mulhdu %1,%2,%3\n\tmulld %0,%2,%3\n\t" \
+   : "=r" (__value), "=&r" (hiremainder) \
+   : "r" (__arg1), "r" (__arg2)); \
+ __value; \
+})
+
+#define addmul(a, b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+ __asm__ ("mulld %0,%3,%4\n\tmulhdu %2,%3,%4\n\taddc %0,%5,%6\n\taddze %1,%7\n\t" \
+   : "=&r" (__value), "=r" (hiremainder), "=r" (__temp) \
+   : "r" (__arg1), "r" (__arg2), "0" ((ulong) 0), "1" (hiremainder), "2" ((ulong) 0)); \
+ __value; \
+})
+#endif
diff --git a/src/kernel/sparcv8_micro/MakeLVL0.SH b/src/kernel/sparcv8_micro/MakeLVL0.SH
new file mode 100644
index 0000000..aaf6b70
--- /dev/null
+++ b/src/kernel/sparcv8_micro/MakeLVL0.SH
@@ -0,0 +1,4 @@
+cat >> $file << EOT
+parilvl0.h: $kern0/asm0-common.h \$(L0MODS)
+	$cfg/genkernel $kern0/asm0-common.h $kern0/asm0.h > parilvl0.h
+EOT
diff --git a/src/kernel/sparcv8_micro/asm0-common.h b/src/kernel/sparcv8_micro/asm0-common.h
new file mode 100644
index 0000000..d2d7a0f
--- /dev/null
+++ b/src/kernel/sparcv8_micro/asm0-common.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file is common to SuperSparc and MicroSparc */
+/*
+ASM addll mulll
+NOASM bfffo
+*/
+#ifdef ASMINLINE
+
+#define LOCAL_HIREMAINDER  ulong hiremainder
+#define LOCAL_OVERFLOW     ulong overflow
+
+#define addll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ( "addcc %2,%3,%0; \
+          addx  %%g0,%%g0,%1" \
+         : "=r" (__value), "=r" (overflow) \
+         : "r" (__arg1), "r" (__arg2) \
+         : "cc"); \
+__value; })
+
+#define addllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ( "subcc %%g0,%1,%%g0; \
+          addxcc %2,%3,%0; \
+          addx  %%g0,%%g0,%1" \
+         : "=r" (__value), "=r" (overflow) \
+         : "r" (__arg1), "r" (__arg2), "1" (overflow) \
+         : "cc"); \
+__value; })
+
+#define subll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ( "subcc %2,%3,%0; \
+          addx  %%g0,%%g0,%1" \
+         : "=r" (__value), "=r" (overflow) \
+         : "r" (__arg1), "r" (__arg2) \
+         : "cc"); \
+__value; })
+
+#define subllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ( "subcc %%g0,%1,%%g0; \
+          subxcc %2,%3,%0; \
+          addx  %%g0,%%g0,%1" \
+         : "=r" (__value), "=r" (overflow) \
+         : "r" (__arg1), "r" (__arg2), "1" (overflow) \
+         : "cc"); \
+__value; })
+
+#define mulll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ( "umul %2,%3,%0; \
+          rd  %%y,%1" \
+         : "=r" (__value), "=r" (hiremainder) \
+         : "r" (__arg1), "r" (__arg2));        \
+__value;})
+
+#define addmul(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __tmp; \
+   __asm__ ( "umul %3,%4,%0; \
+          rd  %%y,%2; \
+          addcc %0,%1,%0; \
+          addx %%g0,%2,%1" \
+         : "=&r" (__value), "=&r" (hiremainder), "=&r" (__tmp) \
+         : "r" (__arg1), "r" (__arg2), "1" (hiremainder) \
+         : "cc");        \
+__value;})
+#endif
diff --git a/src/kernel/sparcv8_micro/asm0.h b/src/kernel/sparcv8_micro/asm0.h
new file mode 100644
index 0000000..721fd16
--- /dev/null
+++ b/src/kernel/sparcv8_micro/asm0.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/*
+ASM divll
+*/
+#ifdef ASMINLINE
+#define divll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __tmp; \
+  __asm__( "mov %1, %%y; nop;nop;nop;\n\t\
+udivcc  %3,%4,%0;\n\tumul    %0,%4,%2;\n\tsub     %3,%2,%1"\
+        : "=&r" (__value), "=&r" (hiremainder), "=&r" (__tmp) \
+        : "r" (__arg1), "r" (__arg2), "1" (hiremainder) \
+        : "cc");        \
+__value;})
+#endif
diff --git a/src/kernel/sparcv8_super/MakeLVL0.SH b/src/kernel/sparcv8_super/MakeLVL0.SH
new file mode 100644
index 0000000..27b22b3
--- /dev/null
+++ b/src/kernel/sparcv8_super/MakeLVL0.SH
@@ -0,0 +1,4 @@
+cat >> $file << EOT
+parilvl0.h: $src/kernel/sparcv8_micro/asm0-common.h \$(L0MODS)
+	$cfg/genkernel $src/kernel/sparcv8_micro/asm0-common.h $kern0/asm0.h > parilvl0.h
+EOT
diff --git a/src/kernel/sparcv8_super/asm0.h b/src/kernel/sparcv8_super/asm0.h
new file mode 100644
index 0000000..0ec859e
--- /dev/null
+++ b/src/kernel/sparcv8_super/asm0.h
@@ -0,0 +1,3 @@
+/*
+NOASM divll
+*/
diff --git a/src/kernel/x86_64/asm0.h b/src/kernel/x86_64/asm0.h
new file mode 100644
index 0000000..c7edcab
--- /dev/null
+++ b/src/kernel/x86_64/asm0.h
@@ -0,0 +1,126 @@
+#line 2 "../src/kernel/x86-64/asm0.h"
+/* Copyright (C) 2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*
+ASM addll mulll bfffo divll
+*/
+/* Written by Bill Allombert from the ix86 version by Bruno Haible. Basically
+ * change insl to insq*/
+#ifdef ASMINLINE
+#define LOCAL_HIREMAINDER  register ulong hiremainder
+#define LOCAL_OVERFLOW     register ulong overflow
+
+#define addll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("addq %3,%0 ; adcq %1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "0" (__arg1), "g" (__arg2), "1" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define addllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("subq %5,%2 ; adcq %4,%0 ; adcq %1,%1" \
+        : "=r" (__value), "=&r" (overflow), "=&r" (__temp) \
+        : "0" (__arg1), "g" (__arg2), "g" (overflow), "1" ((ulong)0), "2" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define addllx8(a,b,c,overflow) \
+do { long *__arg1 = a, *__arg2 = b, *__out = c; \
+     ulong __temp; \
+   __asm__ ("subq %5, %0 \n\t" \
+            "movq    (%2), %0 ; adcq    (%3),%0; movq %0,    (%4) \n\t" \
+            "movq  -8(%2), %0 ; adcq  -8(%3),%0; movq %0,  -8(%4) \n\t" \
+            "movq -16(%2), %0 ; adcq -16(%3),%0; movq %0, -16(%4) \n\t" \
+            "movq -24(%2), %0 ; adcq -24(%3),%0; movq %0, -24(%4) \n\t" \
+            "movq -32(%2), %0 ; adcq -32(%3),%0; movq %0, -32(%4) \n\t" \
+            "movq -40(%2), %0 ; adcq -40(%3),%0; movq %0, -40(%4) \n\t" \
+            "movq -48(%2), %0 ; adcq -48(%3),%0; movq %0, -48(%4) \n\t" \
+            "movq -56(%2), %0 ; adcq -56(%3),%0; movq %0, -56(%4) \n\t" \
+            "adcq  %1, %1" \
+        : "=&r" (__temp), "=&r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (__out), "g" (overflow), "0" ((ulong)0), "1" ((ulong)0) \
+        : "cc"); \
+} while(0)
+
+#define subll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("subq %3,%0 ; adcq %1,%1" \
+        : "=r" (__value), "=r" (overflow) \
+        : "0" (__arg1), "g" (__arg2), "1" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define subllx(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("subq %5,%2 ; sbbq %4,%0 ; adcq %1,%1" \
+        : "=r" (__value), "=&r" (overflow), "=&r" (__temp) \
+        : "0" (__arg1), "g" (__arg2), "g" (overflow), "1" ((ulong)0), "2" ((ulong)0) \
+        : "cc"); \
+  __value; \
+})
+
+#define subllx8(a,b,c,overflow) \
+do { long *__arg1 = a, *__arg2 = b, *__out = c; \
+     ulong __temp; \
+   __asm__ ("subq %5, %0 \n\t" \
+            "movq    (%2), %0 ; sbbq    (%3),%0; movq %0,    (%4) \n\t" \
+            "movq  -8(%2), %0 ; sbbq  -8(%3),%0; movq %0,  -8(%4) \n\t" \
+            "movq -16(%2), %0 ; sbbq -16(%3),%0; movq %0, -16(%4) \n\t" \
+            "movq -24(%2), %0 ; sbbq -24(%3),%0; movq %0, -24(%4) \n\t" \
+            "movq -32(%2), %0 ; sbbq -32(%3),%0; movq %0, -32(%4) \n\t" \
+            "movq -40(%2), %0 ; sbbq -40(%3),%0; movq %0, -40(%4) \n\t" \
+            "movq -48(%2), %0 ; sbbq -48(%3),%0; movq %0, -48(%4) \n\t" \
+            "movq -56(%2), %0 ; sbbq -56(%3),%0; movq %0, -56(%4) \n\t" \
+            "adcq  %1, %1" \
+        : "=&r" (__temp), "=&r" (overflow) \
+        : "r" (__arg1), "r" (__arg2), "r" (__out), "g" (overflow), "0" ((ulong)0), "1" ((ulong)0) \
+        : "cc"); \
+} while(0)
+
+#define mulll(a,b) \
+__extension__ ({ ulong __valuelo, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("mulq %3" \
+        : "=a" /* %eax */ (__valuelo), "=d" /* %edx */ (hiremainder) \
+        : "0" (__arg1), "rm" (__arg2)); \
+   __valuelo; \
+})
+
+#define addmul(a,b) \
+__extension__ ({ ulong __valuelo, __arg1 = (a), __arg2 = (b), __temp; \
+   __asm__ ("mulq %4 ; addq %5,%0 ; adcq %6,%1" \
+        : "=a" /* %eax */ (__valuelo), "=&d" /* %edx */ (hiremainder), "=r" (__temp) \
+        : "0" (__arg1), "rm" (__arg2), "g" (hiremainder), "2" ((ulong)0)); \
+   __valuelo; \
+})
+
+#define divll(a,b) \
+__extension__ ({ ulong __value, __arg1 = (a), __arg2 = (b); \
+   __asm__ ("divq %4" \
+        : "=a" /* %eax */ (__value), "=d" /* %edx */ (hiremainder) \
+        : "0" /* %eax */ (__arg1), "1" /* %edx */ (hiremainder), "mr" (__arg2)); \
+   __value; \
+})
+
+#define bfffo(x) \
+__extension__ ({ ulong __arg = (x); \
+   long leading_one_position; \
+  __asm__ ("bsrq %1,%0" : "=r" (leading_one_position) : "rm" (__arg)); \
+  63 - leading_one_position; \
+})
+#endif
diff --git a/src/language/anal.c b/src/language/anal.c
new file mode 100644
index 0000000..41054e9
--- /dev/null
+++ b/src/language/anal.c
@@ -0,0 +1,1174 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+#include "parse.h"
+
+/***************************************************************************
+ **                                                                       **
+ **                           Mnemonic codes parser                       **
+ **                                                                       **
+ ***************************************************************************/
+
+/* TEMPLATE is assumed to be ";"-separated list of items.  Each item
+ * may have one of the following forms: id=value id==value id|value id&~value.
+ * Each id consists of alphanum characters, dashes and underscores.
+ * IDs are case-sensitive.
+
+ * ARG consists of several IDs separated by punctuation (and optional
+ * whitespace).  Each modifies the return value in a "natural" way: an
+ * ID from id=value should be the first in the sequence and sets RETVAL to
+ * VALUE (and cannot be negated), ID from id|value bit-ORs RETVAL with
+ * VALUE (and bit-ANDs RETVAL with ~VALUE if negated), ID from
+ * id&~value behaves as if it were noid|value, ID from
+ * id==value behaves the same as id=value, but should come alone.
+
+ * For items of the form id|value and id&~value negated forms are
+ * allowed: either when arg looks like no[-_]id, or when id looks like
+ * this, and arg is not-negated. */
+
+enum { A_ACTION_ASSIGN, A_ACTION_SET, A_ACTION_UNSET };
+#define IS_ID(c)        (isalnum((int)c) || ((c) == '_') || ((c) == '-'))
+
+long
+eval_mnemonic(GEN str, const char *tmplate)
+{
+  pari_sp av=avma;
+  ulong retval = 0;
+  const char *etmplate = NULL;
+  const char *arg;
+
+  if (typ(str)==t_INT) return itos(str);
+  if (typ(str)!=t_STR) pari_err_TYPE("eval_mnemonic",str);
+
+  arg=GSTR(str);
+  etmplate = strchr(tmplate, '\n');
+  if (!etmplate)
+    etmplate = tmplate + strlen(tmplate);
+
+  while (1)
+  {
+    long numarg;
+    const char *e, *id;
+    const char *negated;                /* action found with 'no'-ID */
+    int negate;                 /* Arg has 'no' prefix removed */
+    ulong l, action = 0, first = 1, singleton = 0;
+    char *buf, *inibuf;
+    static char b[80];
+
+    while (isspace((int)*arg)) arg++;
+    if (!*arg)
+      break;
+    e = arg;
+    while (IS_ID(*e)) e++;
+    /* Now the ID is whatever is between arg and e. */
+    l = e - arg;
+    if (l >= sizeof(b))
+      pari_err(e_MISC,"id too long in a stringified flag");
+    if (!l)                             /* Garbage after whitespace? */
+      pari_err(e_MISC,"a stringified flag does not start with an id");
+    strncpy(b, arg, l);
+    b[l] = 0;
+    arg = e;
+    e = inibuf = buf = b;
+    while (('0' <= *e) && (*e <= '9'))
+      e++;
+    if (*e == 0)
+      pari_err(e_MISC,"numeric id in a stringified flag");
+    negate = 0;
+    negated = NULL;
+find:
+    id = tmplate;
+    while ((id = strstr(id, buf)) && id < etmplate)
+    {
+      if (IS_ID(id[l])) {       /* We do not allow abbreviations yet */
+        id += l;                /* False positive */
+        continue;
+      }
+      if ((id >= tmplate + 2) && (IS_ID(id[-1])))
+      {
+        const char *s = id;
+
+        if ( !negate && s >= tmplate+3
+            && ((id[-1] == '_') || (id[-1] == '-')) )
+          s--;
+        /* Check whether we are preceeded by "no" */
+        if ( negate             /* buf initially started with "no" */
+            || (s < tmplate+2) || (s[-1] != 'o') || (s[-2] != 'n')
+            || (s >= tmplate+3 && IS_ID(s[-3]))) {
+          id += l;              /* False positive */
+          continue;
+        }
+        /* Found noID in the template! */
+        id += l;
+        negated = id;
+        continue;               /* Try to find without 'no'. */
+      }
+      /* Found as is */
+      id += l;
+      break;
+    }
+    if ( !id && !negated && !negate
+        && (l > 2) && buf[0] == 'n' && buf[1] == 'o' ) {
+      /* Try to find the flag without the prefix "no". */
+      buf += 2; l -= 2;
+      if ((buf[0] == '_') || (buf[0] == '-')) { buf++; l--; }
+      negate = 1;
+      if (buf[0])
+        goto find;
+    }
+    if (!id && negated) /* Negated and AS_IS forms, prefer AS_IS */
+    {
+      id = negated;     /* Otherwise, use negated form */
+      negate = 1;
+    }
+    if (!id)
+      pari_err(e_MISC,"Unrecognized id '%s' in a stringified flag", inibuf);
+    if (singleton && !first)
+      pari_err(e_MISC,"Singleton id non-single in a stringified flag");
+    if (id[0] == '=') {
+      if (negate)
+        pari_err(e_MISC,"Cannot negate id=value in a stringified flag");
+      if (!first)
+        pari_err(e_MISC,"Assign action should be first in a stringified flag");
+      action = A_ACTION_ASSIGN;
+      id++;
+      if (id[0] == '=') {
+        singleton = 1;
+        id++;
+      }
+    } else if (id[0] == '^') {
+      if (id[1] != '~')
+        pari_err(e_MISC, "Unrecognized action in a template");
+      id += 2;
+      if (negate)
+        action = A_ACTION_SET;
+      else
+        action = A_ACTION_UNSET;
+    } else if (id[0] == '|') {
+      id++;
+      if (negate)
+        action = A_ACTION_UNSET;
+      else
+        action = A_ACTION_SET;
+    }
+
+    e = id;
+
+    while ((*e >= '0' && *e <= '9')) e++;
+    while (isspace((int)*e))
+      e++;
+    if (*e && (*e != ';') && (*e != ','))
+      pari_err(e_MISC, "Non-numeric argument of an action in a template");
+    numarg = atol(id);          /* Now it is safe to get it... */
+    switch (action) {
+    case A_ACTION_SET:
+      retval |= numarg;
+      break;
+    case A_ACTION_UNSET:
+      retval &= ~numarg;
+      break;
+    case A_ACTION_ASSIGN:
+      retval = numarg;
+      break;
+    default:
+      pari_err(e_MISC,"error in parse_option_string");
+    }
+    first = 0;
+    while (isspace((int)*arg))
+      arg++;
+    if (*arg && !(ispunct((int)*arg) && *arg != '-'))
+      pari_err(e_MISC,"Junk after an id in a stringified flag");
+    /* Skip punctuation */
+    if (*arg)
+      arg++;
+  }
+  avma=av;
+  return retval;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                  SYNTACTICAL ANALYZER FOR GP                    */
+/*                                                                 */
+/*******************************************************************/
+GEN
+readseq(char *t)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, closure_evalres(pari_compile_str(t,0)));
+}
+
+/* filtered readseq = remove blanks and comments */
+GEN
+gp_read_str(const char *s)
+{
+  char *t = filtre(s, (compatible == OLDALL));
+  GEN x = readseq(t);
+  pari_free(t); return x;
+}
+
+GEN
+compile_str(const char *s)
+{
+  char *t = filtre(s, (compatible == OLDALL));
+  GEN x = pari_compile_str(t, 1);
+  pari_free(t); return x;
+}
+
+static long
+check_proto(const char *code)
+{
+  long arity = 0;
+  const char *s = code, *old;
+  if (*s == 'l' || *s == 'v' || *s == 'i' || *s == 'm') s++;
+  while (*s && *s != '\n') switch (*s++)
+  {
+    case '&':
+    case 'C':
+    case 'G':
+    case 'I':
+    case 'J':
+    case 'L':
+    case 'M':
+    case 'P':
+    case 'W':
+    case 'f':
+    case 'n':
+    case 'p':
+    case 'r':
+    case 'x':
+      arity++;
+      break;
+    case 'E':
+    case 's':
+      if (*s == '*') s++;
+      arity++;
+      break;
+    case 'D':
+      if (*s == 'G' || *s == '&' || *s == 'n' || *s == 'I' || *s == 'E'
+                    || *s == 'V' || *s == 'P' || *s == 's' || *s == 'r')
+      {
+        if (*s != 'V') arity++;
+        s++; break;
+      }
+      old = s; while (*s && *s != ',') s++;
+      if (*s != ',') pari_err(e_SYNTAX, "missing comma", old, code);
+      break;
+    case 'V':
+    case '=':
+    case ',': break;
+    case '\n': break; /* Before the mnemonic */
+
+    case 'm':
+    case 'l':
+    case 'i':
+    case 'v': pari_err(e_SYNTAX, "this code has to come first", s-1, code);
+    default: pari_err(e_SYNTAX, "unknown parser code", s-1, code);
+  }
+  if (arity > 20) pari_err_IMPL("functions with more than 20 parameters");
+  return arity;
+}
+
+static entree *
+installep(const char *name, long len, entree **table)
+{
+  const long add = 4*sizeof(long);
+  entree *ep = (entree *) pari_calloc(sizeof(entree) + add + len+1);
+  entree *ep1 = initial_value(ep);
+  char *u = (char *) ep1 + add;
+  ep->name    = u; strncpy(u, name,len); u[len]=0;
+  ep->valence = EpNEW;
+  ep->value   = NULL;
+  ep->menu    = 0;
+  ep->code    = NULL;
+  ep->help    = NULL;
+  ep->pvalue  = NULL;
+  ep->arity   = 0;
+  ep->next    = *table;
+  return *table = ep;
+}
+
+entree *
+install(void *f, const char *name, const char *code)
+{
+  long hash, arity;
+  entree *ep = is_entry_intern(name, functions_hash, &hash);
+
+  arity=check_proto(code);
+  if (ep && ep->valence != EpNEW)
+  {
+    if (ep->valence != EpINSTALL)
+      pari_err(e_MISC,"[install] identifier '%s' already in use", name);
+    pari_warn(warner, "[install] updating '%s' prototype; module not reloaded", name);
+    if (ep->code) pari_free((void*)ep->code);
+  }
+  else
+  {
+    const char *s = name;
+    if (isalpha((int)*s))
+      while (is_keyword_char(*++s)) /* empty */;
+    if (*s) pari_err(e_SYNTAX,"not a valid identifier", s, name);
+    if (!ep) ep = installep(name, strlen(name), functions_hash + hash);
+    ep->value=f; ep->valence=EpINSTALL;
+  }
+  ep->code = pari_strdup(code);
+  ep->arity=arity;
+  return ep;
+}
+
+/* Kill ep, i.e free all memory it references, and reset to initial value */
+void
+kill0(const char *e)
+{
+  entree *ep = is_entry(e);
+  if (!ep || EpSTATIC(ep)) pari_err(e_MISC,"can't kill that");
+  freeep(ep);
+  ep->valence = EpNEW;
+  ep->value   = NULL;
+  ep->pvalue  = NULL;
+}
+
+void
+addhelp(const char *e, char *s)
+{
+  entree *ep = fetch_entry(e, strlen(e));
+  if (ep->help && !EpSTATIC(ep)) pari_free((void*)ep->help);
+  ep->help = pari_strdup(s);
+}
+
+GEN
+type0(GEN x)
+{
+  const char *s = type_name(typ(x));
+  return strtoGENstr(s);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                              PARSER                             */
+/*                                                                 */
+/*******************************************************************/
+
+#ifdef LONG_IS_64BIT
+static const long MAX_DIGITS = 19;
+#else
+static const long MAX_DIGITS = 9;
+#endif
+
+static ulong
+number(int *n, const char **s)
+{
+  ulong m = 0;
+  for (*n = 0; *n < MAX_DIGITS && isdigit((int)**s); (*n)++,(*s)++)
+    m = 10*m + (**s - '0');
+  return m;
+}
+
+ulong
+u_pow10(int n)
+{
+  const ulong pw10[] = {
+    1UL
+    ,10UL
+    ,100UL
+    ,1000UL
+    ,10000UL
+    ,100000UL
+    ,1000000UL
+    ,10000000UL
+    ,100000000UL
+    ,1000000000UL
+#ifdef LONG_IS_64BIT
+    ,10000000000UL
+    ,100000000000UL
+    ,1000000000000UL
+    ,10000000000000UL
+    ,100000000000000UL
+    ,1000000000000000UL
+    ,10000000000000000UL
+    ,100000000000000000UL
+    ,1000000000000000000UL
+    ,10000000000000000000UL
+#endif
+  };
+  return pw10[n];
+}
+
+static GEN
+int_read_more(GEN y, const char **ps)
+{
+  pari_sp av = avma;
+  int i = 0, nb;
+  while (isdigit((int)**ps))
+  {
+    ulong m = number(&nb, ps);
+    if (avma != av && ++i > 4) { avma = av; i = 0; } /* HACK gerepile */
+    y = addumului(m, u_pow10(nb), y);
+  }
+  return y;
+}
+
+static long
+exponent(const char **pts)
+{
+  const char *s = *pts;
+  long n;
+  int nb;
+  switch(*++s)
+  {
+    case '-': s++; n = -(long)number(&nb, &s); break;
+    case '+': s++; /* Fall through */
+    default: n = (long)number(&nb, &s);
+  }
+  *pts = s; return n;
+}
+
+static GEN
+real_0_digits(long n) {
+  long b = (n > 0)? (long)(n/LOG10_2): (long)-((-n)/LOG10_2 + 1);
+  return real_0_bit(b);
+}
+
+static GEN
+real_read(pari_sp av, const char **s, GEN y, long prec)
+{
+  long l, n = 0;
+  switch(**s)
+  {
+    default: return y; /* integer */
+    case '.':
+    {
+      const char *old = ++*s;
+      if (isalpha((int)**s) || **s=='.')
+      {
+        if (**s == 'E' || **s == 'e') {
+          n = exponent(s);
+          if (!signe(y)) { avma = av; return real_0_digits(n); }
+          break;
+        }
+        --*s; return y; /* member */
+      }
+      y = int_read_more(y, s);
+      n = old - *s;
+      if (**s != 'E' && **s != 'e')
+      {
+        if (!signe(y)) { avma = av; return real_0(prec); }
+        break;
+      }
+    }
+    /* Fall through */
+    case 'E': case 'e':
+      n += exponent(s);
+      if (!signe(y)) { avma = av; return real_0_digits(n); }
+  }
+  l = nbits2prec(bit_accuracy(lgefint(y)));
+  if (l < prec) l = prec; else prec = l;
+  if (!n) return itor(y, prec);
+  incrprec(l);
+  y = itor(y, l);
+  if (n > 0)
+    y = mulrr(y, rpowuu(10UL, (ulong)n, l));
+  else
+    y = divrr(y, rpowuu(10UL, (ulong)-n, l));
+  return gerepileuptoleaf(av, rtor(y, prec));
+}
+
+static GEN
+int_read(const char **s)
+{
+  int nb;
+  GEN y = utoi(number(&nb, s));
+  if (nb == MAX_DIGITS) y = int_read_more(y, s);
+  return y;
+}
+
+GEN
+strtoi(const char *s) { return int_read(&s); }
+
+GEN
+strtor(const char *s, long prec)
+{
+  pari_sp av = avma;
+  GEN y = int_read(&s);
+  y = real_read(av, &s, y, prec);
+  if (typ(y) == t_REAL) return y;
+  return gerepileuptoleaf(av, itor(y, prec));
+}
+
+static void
+skipdigits(char **lex) {
+  while (isdigit((int)**lex)) ++*lex;
+}
+
+static int
+skipexponent(char **lex)
+{
+  char *old=*lex;
+  if ((**lex=='e' || **lex=='E'))
+  {
+    ++*lex;
+    if ( **lex=='+' || **lex=='-' ) ++*lex;
+    if (!isdigit((int)**lex))
+    {
+      *lex=old;
+      return KINTEGER;
+    }
+    skipdigits(lex);
+    return KREAL;
+  }
+  return KINTEGER;
+}
+
+static int
+skipconstante(char **lex)
+{
+  skipdigits(lex);
+  if (**lex=='.')
+  {
+    char *old = ++*lex;
+    if (**lex == '.') { --*lex; return KINTEGER; }
+    if (isalpha((int)**lex))
+    {
+      skipexponent(lex);
+      if (*lex == old)
+      {
+        --*lex; /* member */
+        return KINTEGER;
+      }
+      return KREAL;
+    }
+    skipdigits(lex);
+    skipexponent(lex);
+    return KREAL;
+  }
+  return skipexponent(lex);
+}
+
+static void
+skipstring(char **lex)
+{
+  while (**lex)
+  {
+    while (**lex == '\\') *lex+=2;
+    if (**lex == '"')
+    {
+      if ((*lex)[1] != '"') break;
+      *lex += 2; continue;
+    }
+    (*lex)++;
+  }
+}
+
+int
+pari_lex(union token_value *yylval, struct node_loc *yylloc, char **lex)
+{
+  (void) yylval;
+  yylloc->start=*lex;
+  if (!**lex)
+  {
+    yylloc->end=*lex;
+    return 0;
+  }
+  if (isalpha((int)**lex))
+  {
+    while (is_keyword_char(**lex)) ++*lex;
+    yylloc->end=*lex;
+    return KENTRY;
+  }
+  if (**lex=='"')
+  {
+    ++*lex;
+    skipstring(lex);
+    if (!**lex)
+      compile_err("run-away string",*lex-1);
+    ++*lex;
+    yylloc->end=*lex;
+    return KSTRING;
+  }
+  if (**lex == '.')
+  {
+    int token;
+    if ((*lex)[1]== '.')
+    {
+      *lex+=2; yylloc->end = *lex; return KDOTDOT;
+    }
+    token=skipconstante(lex);
+    if (token==KREAL)
+    {
+      yylloc->end = *lex;
+      return token;
+    }
+    ++*lex;
+    yylloc->end=*lex;
+    return '.';
+  }
+  if (isdigit((int)**lex))
+  {
+    int token=skipconstante(lex);
+    yylloc->end = *lex;
+    return token;
+  }
+  if ((*lex)[1]=='=')
+    switch (**lex)
+    {
+    case '=':
+      if ((*lex)[2]=='=')
+      { *lex+=3; yylloc->end = *lex; return KID; }
+      else
+      { *lex+=2; yylloc->end = *lex; return KEQ; }
+    case '>':
+      *lex+=2; yylloc->end = *lex; return KGE;
+    case '<':
+      *lex+=2; yylloc->end = *lex; return KLE;
+    case '*':
+      *lex+=2; yylloc->end = *lex; return KME;
+    case '/':
+      *lex+=2; yylloc->end = *lex; return KDE;
+    case '%':
+      if ((*lex)[2]=='=') break;
+      *lex+=2; yylloc->end = *lex; return KMODE;
+    case '!':
+      if ((*lex)[2]=='=') break;
+      *lex+=2; yylloc->end = *lex; return KNE;
+    case '\\':
+      *lex+=2; yylloc->end = *lex; return KEUCE;
+    case '+':
+      *lex+=2; yylloc->end = *lex; return KPE;
+    case '-':
+      *lex+=2; yylloc->end = *lex; return KSE;
+    }
+  if (**lex==')' && (*lex)[1]=='-' && (*lex)[2]=='>')
+  {
+    *lex+=3; yylloc->end = *lex; return KPARROW;
+  }
+  if (**lex=='-' && (*lex)[1]=='>')
+  {
+    *lex+=2; yylloc->end = *lex; return KARROW;
+  }
+  if (**lex=='<' && (*lex)[1]=='>')
+  {
+    *lex+=2; yylloc->end = *lex; return KNE;
+  }
+  if (**lex=='\\' && (*lex)[1]=='/')
+    switch((*lex)[2])
+    {
+    case '=':
+      *lex+=3; yylloc->end = *lex; return KDRE;
+    default:
+      *lex+=2; yylloc->end = *lex; return KDR;
+    }
+  if ((*lex)[1]==**lex)
+    switch (**lex)
+    {
+    case '&':
+      *lex+=2; yylloc->end = *lex; return KAND;
+    case '|':
+      *lex+=2; yylloc->end = *lex; return KOR;
+    case '+':
+      *lex+=2; yylloc->end = *lex; return KPP;
+    case '-':
+      *lex+=2; yylloc->end = *lex; return KSS;
+    case '>':
+      if ((*lex)[2]=='=') { *lex+=3; yylloc->end = *lex; return KSRE;}
+      *lex+=2; yylloc->end = *lex; return KSR;
+    case '<':
+      if ((*lex)[2]=='=')
+      { *lex+=3; yylloc->end = *lex; return KSLE; }
+      *lex+=2; yylloc->end = *lex; return KSL;
+    }
+  yylloc->end = *lex+1;
+  return (unsigned char) *(*lex)++;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                            STRINGS                             **/
+/**                                                                **/
+/********************************************************************/
+
+/* return the first n0 chars of s as a GEN [s may not be 0-terminated] */
+GEN
+strntoGENstr(const char *s, long n0)
+{
+  long n = nchar2nlong(n0+1);
+  GEN x = cgetg(n+1, t_STR);
+  char *t = GSTR(x);
+  strncpy(t, s, n0); t[n0] = 0; return x;
+}
+
+GEN
+strtoGENstr(const char *s) { return strntoGENstr(s, strlen(s)); }
+
+GEN
+chartoGENstr(char c)
+{
+  GEN x = cgetg(2, t_STR);
+  char *t = GSTR(x);
+  t[0] = c; t[1] = 0; return x;
+}
+/********************************************************************/
+/**                                                                **/
+/**                   HASH TABLE MANIPULATIONS                     **/
+/**                                                                **/
+/********************************************************************/
+/* return hashing value for identifier s */
+static ulong
+hashvalue(const char *s)
+{
+  ulong n = 0, c;
+  while ( (c = (ulong)*s++) ) n = (n<<1) ^ c;
+  return n % functions_tblsz;
+}
+
+static ulong
+hashvalue_raw(const char *s, long len, ulong n)
+{
+  long i;
+  for(i=0; i<len; i++) { n = (n<<1) ^ *s; s++; }
+  return n % functions_tblsz;
+}
+
+/* Looking for entry in hashtable. ep1 is the cell's first element */
+static entree *
+findentry(const char *name, long len, entree *ep1)
+{
+  entree *ep;
+  for (ep = ep1; ep; ep = ep->next)
+    if (!strncmp(ep->name, name, len) && !(ep->name)[len]) return ep;
+  return NULL; /* not found */
+}
+
+entree *
+is_entry_intern(const char *s, entree **table, long *pthash)
+{
+  long hash = hashvalue(s);
+  if (pthash) *pthash = hash;
+  return findentry(s, strlen(s), table[hash]);
+}
+
+entree *
+is_entry(const char *s)
+{
+  return is_entry_intern(s,functions_hash,NULL);
+}
+
+entree *
+fetch_entry(const char *s, long len)
+{
+  entree **funhash = functions_hash + hashvalue_raw(s, len, 0);
+  entree *ep = findentry(s, len, *funhash);
+  if (ep) return ep;
+  else return installep(s,len,funhash);
+}
+
+/* Assume s point somewhere in the code text, so s[-1]='.' and s[-2]>0
+ * So many kludges, so little time */
+entree *
+fetch_member(const char *s, long len)
+{
+  entree **funhash = functions_hash+hashvalue_raw(s-1, len+1, '_');
+  entree *ep;
+  for (ep = *funhash; ep; ep = ep->next)
+  {
+    if (ep->name[0]!='_' || ep->name[1]!='.') continue;
+    if (!strncmp(ep->name+2, s, len) && !(ep->name)[len+2]) break;
+  }
+  if (ep) return ep;
+  ep=installep(s-2,len+2,funhash);
+  ((char*)ep->name)[0]='_';
+  return ep;
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                Formal variables management                       */
+/*                                                                  */
+/********************************************************************/
+static long max_avail; /* max variable not yet used */
+static long nvar; /* first GP free variable */
+
+void pari_var_init(void) {
+  nvar = 0; max_avail = MAXVARN;
+  (void)fetch_var();
+  (void)fetch_named_var("x");
+}
+long pari_var_next(void) { return nvar; }
+long pari_var_next_temp(void) { return max_avail; }
+static long
+pari_var_pop(long v)
+{
+  if (v != nvar-1) pari_err(e_MISC,"can't pop user variable %ld", v);
+  return --nvar;
+}
+void
+pari_var_create(entree *ep)
+{
+  GEN p = (GEN)initial_value(ep);
+  long v;
+  if (*p) return;
+  if (nvar == max_avail) pari_err(e_MISC,"no more variables available");
+  v = nvar++;
+  /* set p = pol_x(v) */
+  p[0] = evaltyp(t_POL) | _evallg(4);
+  p[1] = evalsigne(1) | evalvarn(v);
+  gel(p,2) = gen_0;
+  gel(p,3) = gen_1;
+  varentries[v] = ep;
+}
+
+long
+delete_var(void)
+{ /* user wants to delete one of his/her/its variables */
+  if (max_avail == MAXVARN-1) return 0; /* nothing to delete */
+  max_avail++; return max_avail+1;
+}
+long
+fetch_var(void)
+{
+  if (nvar == max_avail) pari_err(e_MISC,"no more variables available");
+  return max_avail--;
+}
+
+/* FIXE: obsolete, kept for backward compatibility */
+long
+manage_var(long n, entree *ep)
+{
+  switch(n) {
+      case manage_var_init: pari_var_init(); return 0;
+      case manage_var_next: return pari_var_next();
+      case manage_var_max_avail: return pari_var_next_temp();
+      case manage_var_pop: return pari_var_pop((long)ep);
+      case manage_var_delete: return delete_var();
+      case manage_var_create:
+        pari_var_create(ep);
+        return varn((GEN)initial_value(ep));
+  }
+  pari_err(e_MISC, "panic");
+  return -1; /* not reached */
+}
+
+entree *
+fetch_named_var(const char *s)
+{
+  entree **funhash = functions_hash + hashvalue(s);
+  entree *ep = findentry(s, strlen(s), *funhash);
+  if (!ep) ep = installep(s,strlen(s),funhash);
+  else switch (EpVALENCE(ep))
+  {
+    case EpVAR: return ep;
+    case EpNEW: break;
+    default: pari_err(e_MISC, "%s already exists with incompatible valence", s);
+  }
+  pari_var_create(ep);
+  ep->valence=EpVAR;
+  ep->value=initial_value(ep);
+  return ep;
+}
+
+long
+fetch_user_var(const char *s)
+{
+  return varn((GEN)initial_value(fetch_named_var(s)) );
+}
+
+GEN
+fetch_var_value(long vx, GEN t)
+{
+  entree *ep = varentries[vx];
+  long vn;
+  if (!ep) return NULL;
+  if (!t)  return (GEN) ep->value;
+  vn=localvars_find(t,ep);
+  if (vn) return get_lex(vn);
+  return (GEN) ep->value;
+}
+
+void
+name_var(long n, const char *s)
+{
+  entree *ep;
+  char *u;
+
+  if (n < pari_var_next())
+    pari_err(e_MISC, "renaming a GP variable is forbidden");
+  if (n > (long)MAXVARN)
+    pari_err_OVERFLOW("variable number");
+
+  ep = (entree*)pari_malloc(sizeof(entree) + strlen(s) + 1);
+  u = (char *)initial_value(ep);
+  ep->valence = EpVAR;
+  ep->name = u; strcpy(u,s);
+  ep->value = gen_0; /* in case geval is called */
+  if (varentries[n]) pari_free(varentries[n]);
+  varentries[n] = ep;
+}
+
+GEN
+gpolvar(GEN x)
+{
+  long v;
+  if (!x) {
+    long k = 1, n = pari_var_next();
+    GEN z = cgetg(n+1, t_VEC);
+    for (v = 0; v < n; v++)
+    {
+      entree *ep = varentries[v];
+      if (ep && ep->name[0] != '_') gel(z,k++) = (GEN)initial_value(ep);
+    }
+    if (k <= n) {
+      setlg(z,k);
+      stackdummy((pari_sp)(z+n), (pari_sp)(z+k));
+    }
+    return z;
+  }
+  if (typ(x)==t_PADIC) return gcopy( gel(x,2) );
+  v = gvar(x);
+  if (v==NO_VARIABLE) return gen_0;
+  return pol_x(v);
+}
+
+static void
+fill_hashtable_single(entree **table, entree *ep)
+{
+  long n = hashvalue(ep->name);
+  EpSETSTATIC(ep);
+  ep->next = table[n]; table[n] = ep;
+  if (ep->code) ep->arity=check_proto(ep->code);
+  ep->pvalue = NULL;
+}
+
+void
+pari_fill_hashtable(entree **table, entree *ep)
+{
+  for ( ; ep->name; ep++) fill_hashtable_single(table, ep);
+}
+
+void
+pari_add_function(entree *ep)
+{
+  fill_hashtable_single(functions_hash, ep);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        SIMPLE GP FUNCTIONS                     **/
+/**                                                                **/
+/********************************************************************/
+
+#define ALIAS(ep) (entree *) ((GEN)ep->value)[1]
+
+entree *
+do_alias(entree *ep)
+{
+  while (ep->valence == EpALIAS) ep = ALIAS(ep);
+  return ep;
+}
+
+void
+alias0(const char *s, const char *old)
+{
+  entree *ep, *e;
+  GEN x;
+
+  ep = fetch_entry(old,strlen(old));
+  e  = fetch_entry(s,strlen(s));
+  if (EpVALENCE(e) != EpALIAS && EpVALENCE(e) != EpNEW)
+    pari_err(e_MISC,"can't replace an existing symbol by an alias");
+  freeep(e);
+  x = newblock(2); x[0] = evaltyp(t_STR)|_evallg(2); /* for getheap */
+  gel(x,1) = (GEN)ep;
+  e->value=x; e->valence=EpALIAS;
+}
+
+GEN
+ifpari(GEN g, GEN a/*closure*/, GEN b/*closure*/)
+{
+  if (gequal0(g)) /* false */
+    return b?closure_evalgen(b):gnil;
+  else /* true */
+    return a?closure_evalgen(a):gnil;
+}
+
+void
+ifpari_void(GEN g, GEN a/*closure*/, GEN b/*closure*/)
+{
+  if (gequal0(g)) /* false */
+  {
+    if(b) closure_evalvoid(b);
+  }
+  else /* true */
+  {
+    if(a) closure_evalvoid(a);
+  }
+}
+
+GEN
+ifpari_multi(GEN g, GEN a/*closure*/)
+{
+  long i, nb = lg(a)-1;
+  if (!gequal0(g)) /* false */
+    return closure_evalgen(gel(a,1));
+  for(i=2;i<nb;i+=2)
+  {
+    GEN g = closure_evalgen(gel(a,i));
+    if (!g) return g;
+    if (!gequal0(g))
+      return closure_evalgen(gel(a,i+1));
+  }
+  return i<=nb? closure_evalgen(gel(a,i)): gnil;
+}
+
+GEN
+andpari(GEN a, GEN b/*closure*/)
+{
+  GEN g;
+  if (gequal0(a))
+    return gen_0;
+  g=closure_evalgen(b);
+  if (!g) return g;
+  return gequal0(g)?gen_0:gen_1;
+}
+
+GEN
+orpari(GEN a, GEN b/*closure*/)
+{
+  GEN g;
+  if (!gequal0(a))
+    return gen_1;
+  g=closure_evalgen(b);
+  if (!g) return g;
+  return gequal0(g)?gen_0:gen_1;
+}
+
+GEN gmule(GEN *x, GEN y)
+{
+  *x=gmul(*x,y);
+  return *x;
+}
+
+GEN gdive(GEN *x, GEN y)
+{
+  *x=gdiv(*x,y);
+  return *x;
+}
+
+GEN gdivente(GEN *x, GEN y)
+{
+  *x=gdivent(*x,y);
+  return *x;
+}
+
+GEN gdivrounde(GEN *x, GEN y)
+{
+  *x=gdivround(*x,y);
+  return *x;
+}
+
+GEN gmode(GEN *x, GEN y)
+{
+  *x=gmod(*x,y);
+  return *x;
+}
+
+GEN gshiftle(GEN *x, long n)
+{
+  *x=gshift(*x,n);
+  return *x;
+}
+
+GEN gshiftre(GEN *x, long n)
+{
+  *x=gshift(*x,-n);
+  return *x;
+}
+
+GEN gadde(GEN *x, GEN y)
+{
+  *x=gadd(*x,y);
+  return *x;
+}
+
+GEN gadd1e(GEN *x)
+{
+  *x=typ(*x)==t_INT?addis(*x,1):gaddgs(*x,1);
+  return *x;
+}
+
+GEN gsube(GEN *x, GEN y)
+{
+  *x=gsub(*x,y);
+  return *x;
+}
+
+GEN gsub1e(GEN *x)
+{
+  *x=typ(*x)==t_INT?subis(*x,1):gsubgs(*x,1);
+  return *x;
+}
+
+GEN gshift_right(GEN x, long n) { return gshift(x,-n); }
+
+/********************************************************************/
+/**                                                                **/
+/**            PRINT USER FUNCTION AND MEMBER FUNCTION             **/
+/**                                                                **/
+/********************************************************************/
+static int
+cmp_epname(void *E, GEN e, GEN f)
+{
+  (void)E;
+  return strcmp(((entree*)e)->name, ((entree*)f)->name);
+}
+
+void
+print_all_user_fun(int member)
+{
+  pari_sp av = avma;
+  long iL = 0, lL = 1024;
+  GEN L = cgetg(lL+1, t_VECSMALL);
+  entree *ep;
+  int i;
+  for (i = 0; i < functions_tblsz; i++)
+    for (ep = functions_hash[i]; ep; ep = ep->next)
+    {
+      const char *f;
+      int is_member;
+      if (EpVALENCE(ep) != EpVAR || typ((GEN)ep->value)!=t_CLOSURE) continue;
+      f = ep->name;
+      is_member = (f[0] == '_' && f[1] == '.');
+      if (member != is_member) continue;
+
+      if (iL >= lL)
+      {
+        GEN oL = L;
+        long j;
+        lL *= 2; L = cgetg(lL+1, t_VECSMALL);
+        for (j = 1; j <= iL; j++) gel(L,j) = gel(oL,j);
+      }
+      L[++iL] = (long)ep;
+    }
+  if (iL)
+  {
+    setlg(L, iL+1);
+    L = gen_sort(L, NULL, &cmp_epname);
+    for (i = 1; i <= iL; i++)
+    {
+      ep = (entree*)L[i];
+      pari_printf("%s =\n  %Ps\n\n", ep->name, ep->value);
+    }
+  }
+  avma = av;
+}
diff --git a/src/language/anal.h b/src/language/anal.h
new file mode 100644
index 0000000..f77d470
--- /dev/null
+++ b/src/language/anal.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*************************************************************************/
+/*                                                                       */
+/*                 Declarations specific to the analyzer                 */
+/*                                                                       */
+/*************************************************************************/
+BEGINEXTERN
+/* GP control structures */
+#define EXPR_WRAP(code, call) \
+{ GEN z; GEN __E = code; \
+  push_lex(gen_0, __E); z = call; pop_lex(1); return z; }
+#define EXPR_ARG __E, &gp_eval
+#define EXPR_ARGUPTO __E, &gp_evalupto
+#define EXPR_ARGBOOL __E, &gp_evalbool
+
+/* functions */
+void   changevalue(entree *ep, GEN val);
+void    freeep(entree *ep);
+void   pari_fill_hashtable(entree **table, entree *ep);
+
+void compile_err(const char *msg, const char *str);
+void compile_varerr(const char *str);
+
+#ifdef STACK_CHECK
+extern THREAD void *PARI_stack_limit;
+#endif
+
+extern entree  **varentries;
+
+struct node_loc
+{
+  const char *start,*end;
+};
+
+union token_value { long val; };
+
+int pari_lex(union token_value *yylval, struct node_loc *yylloc, char **lex);
+int pari_parse(char **lex);
+entree* fetch_entry(const char *s, long len);
+entree* fetch_member(const char *s, long len);
+void pari_close_parser(void);
+void pari_close_compiler(void);
+void pari_close_evaluator(void);
+void pari_init_parser(void);
+void pari_init_compiler(void);
+void pari_init_evaluator(void);
+void optimizenode(long n);
+void push_frame(GEN C, long lpc, long flag);
+const char * closure_func_err(void);
+GEN  gp_closure(long n);
+long eval_mnemonic(GEN str, const char *tmplate);
+
+ENDEXTERN
diff --git a/src/language/compat.c b/src/language/compat.c
new file mode 100644
index 0000000..7290ee1
--- /dev/null
+++ b/src/language/compat.c
@@ -0,0 +1,722 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+static GEN
+ghell2(GEN e, GEN a, long prec) { return ellheight0(e,a,0,prec); }
+
+static GEN
+polylogd(long m, GEN x, long prec) { return polylog0(m,x,1,prec); }
+
+static GEN
+polylogdold(long m, GEN x, long prec) { return polylog0(m,x,2,prec); }
+
+static GEN
+polylogp(long m, GEN x, long prec) { return polylog0(m,x,3,prec); }
+
+static GEN
+prod0(GEN x, GEN a, GEN b, GEN ch) {return produit(a,b,ch,x);}
+
+static GEN
+sum0(GEN x, GEN a, GEN b, GEN ch) {return somme(a,b,ch,x);}
+
+static long
+sturm0(GEN x) {return sturm(x);}
+
+static GEN
+sigmak0(long k, GEN x) {return sumdivk(x,k);}
+
+static GEN
+lseriesell0(GEN a, GEN b, GEN c, GEN d, long prec)
+  {(void)c; return elllseries(a,b,d,prec);}
+
+static GEN
+weipell(GEN e, long precdl) { return ellwpseries(e,0,precdl); }
+
+static long
+rounderror(GEN x)
+{
+  pari_sp av = avma;
+  long e; (void)grndtoi(x,&e);
+  avma = av; return (long)(e*LOG10_2);
+}
+
+static GEN
+_factpol(GEN x, long t/*unused*/, long hint/*unused*/) {
+  (void)t; (void)hint; return QX_factor(x);
+}
+
+static void
+suppressed(void) {pari_err(e_MISC,"this function no longer exists");}
+
+#define BUCH_PROTO "GDGDGD5,G,D1,G,D4,L,D3,L,p"
+#define B_ARGS GEN g1,GEN g2,GEN g3,GEN g4,GEN g5,long l1,long l2,long prec
+#define B_ARG1 g1,gtodouble(g2),gtodouble(g3),l1
+#define B_CALL(flag) Buchall_param(B_ARG1,(flag),prec)
+#define B_UNUSED (void)g4,(void)g5,(void)l2
+
+#define B_DEFAULT if (!g2) g2 = dbltor(.3); \
+                  if (!g3) g3 = dbltor(.3)
+
+#define CLASSUNIT(flag) do { \
+  pari_sp av = avma; \
+  GEN bnf = B_CALL(flag), nf = bnf_get_nf(bnf), x; \
+  B_UNUSED; \
+  x = mkvec4(gel(nf,1), gel(nf,2), mkvec2(gel(nf,3), gel(nf,4)), gel(nf,7));\
+  return gerepilecopy(av, mkmat(shallowconcat(x, gel(bnf,8)))); } while(0)
+
+static GEN
+buchgenfu(B_ARGS) { B_DEFAULT; CLASSUNIT(0); }
+static GEN
+buchgenforcefu(B_ARGS) { B_DEFAULT; CLASSUNIT(nf_FORCE); }
+static GEN
+buchinitfu(B_ARGS) { B_DEFAULT; B_UNUSED; return B_CALL(0); }
+static GEN
+buchinitforcefu(B_ARGS) { B_DEFAULT; B_UNUSED; return B_CALL(nf_FORCE); }
+static GEN
+smallbuchinit(B_ARGS) { (void)g2,(void)g3,(void)l1,B_UNUSED; return bnfcompress(Buchall(g1, 0, prec)); }
+static GEN
+factoredbase(GEN x, GEN fa) { return nfbasis0(x, 0, fa); }
+static GEN
+factoreddiscf(GEN x, GEN fa) { return nfdisc0(x, 0, fa); }
+static GEN
+buchfu(GEN bnf) { return bnf_get_fu_nocheck(checkbnf(bnf)); }
+static GEN
+buchrayinitgen(GEN bnf, GEN ideal)
+{ return Buchray(bnf,ideal, nf_INIT | nf_GEN); }
+static GEN
+buchrayinit(GEN bnf, GEN ideal)
+{ return Buchray(bnf,ideal, nf_INIT); }
+static GEN
+buchray(GEN bnf, GEN ideal)
+{ return Buchray(bnf,ideal, nf_GEN); }
+static GEN
+principalideal(GEN nf, GEN x) { return algtobasis(nf,x); }
+
+entree oldfonctions[]={
+{"!_",0,(void*)gnot,13,"G","!_"},
+{"#_",0,(void*)glength,13,"lG","length(x): number of non code words in x, number of characters for a string."},
+{"%",0,(void*)pari_get_hist,1,"D0,L,","last history item"},
+{"+_",0,NULL,13,NULL,"+_"},
+{"-_",0,(void*)gneg,13,"G","-_"},
+{"_!",0,(void*)mpfact,13,"L","_!"},
+{"_!=_",0,(void*)gne,13,"GG","_!=_"},
+{"_%=_",0,(void*)gmode,13,"&G","_%=_"},
+{"_%_",0,(void*)gmod,13,"GG","_%_"},
+{"_&&_",0,(void*)andpari,13,"GE","_&&_"},
+{"_'",0,(void*)deriv,13,"GDn","_'"},
+{"_*=_",0,(void*)gmule,13,"&G","x*=y: shortcut for x=x*y."},
+{"_*_",0,(void*)gmul,13,"GG","_*_"},
+{"_++",0,(void*)gadd1e,13,"&","x++"},
+{"_+=_",0,(void*)gadde,13,"&G","x+=y: shortcut for x=x+y."},
+{"_+_",0,(void*)gadd,13,"GG","x+y: sum of x and y."},
+{"_--",0,(void*)gsub1e,13,"&","x--"},
+{"_-=_",0,(void*)gsube,13,"&G","x-=y"},
+{"_-_",0,(void*)gsub,13,"GG","x-y: difference of x and y."},
+{"_.a1",0,(void*)member_a1,14,"G","_.a1"},
+{"_.a2",0,(void*)member_a2,14,"G","_.a2"},
+{"_.a3",0,(void*)member_a3,14,"G","_.a3"},
+{"_.a4",0,(void*)member_a4,14,"G","_.a4"},
+{"_.a6",0,(void*)member_a6,14,"G","_.a6"},
+{"_.area",0,(void*)member_area,14,"G","_.area"},
+{"_.b2",0,(void*)member_b2,14,"G","_.b2"},
+{"_.b4",0,(void*)member_b4,14,"G","_.b4"},
+{"_.b6",0,(void*)member_b6,14,"G","_.b6"},
+{"_.b8",0,(void*)member_b8,14,"G","_.b8"},
+{"_.bid",0,(void*)member_bid,14,"G","_.bid"},
+{"_.bnf",0,(void*)member_bnf,14,"G","_.bnf"},
+{"_.c4",0,(void*)member_c4,14,"G","_.c4"},
+{"_.c6",0,(void*)member_c6,14,"G","_.c6"},
+{"_.clgp",0,(void*)member_clgp,14,"G","_.clgp"},
+{"_.codiff",0,(void*)member_codiff,14,"G","_.codiff"},
+{"_.cyc",0,(void*)member_cyc,14,"G","_.cyc"},
+{"_.diff",0,(void*)member_diff,14,"G","_.diff"},
+{"_.disc",0,(void*)member_disc,14,"G","_.disc"},
+{"_.e",0,(void*)member_e,14,"G","_.e"},
+{"_.eta",0,(void*)member_eta,14,"G","_.eta"},
+{"_.f",0,(void*)member_f,14,"G","_.f"},
+{"_.fu",0,(void*)member_fu,14,"G","_.fu"},
+{"_.futu",0,(void*)member_futu,14,"G","_.futu"},
+{"_.gen",0,(void*)member_gen,14,"G","_.gen"},
+{"_.group",0,(void*)member_group,14,"G","_.group"},
+{"_.index",0,(void*)member_index,14,"G","_.index"},
+{"_.j",0,(void*)member_j,14,"G","_.j"},
+{"_.mod",0,(void*)member_mod,14,"G","_.mod"},
+{"_.nf",0,(void*)member_nf,14,"G","_.nf"},
+{"_.no",0,(void*)member_no,14,"G","_.no"},
+{"_.omega",0,(void*)member_omega,14,"G","_.omega"},
+{"_.orders",0,(void*)member_orders,14,"G","_.orders"},
+{"_.p",0,(void*)member_p,14,"G","_.p"},
+{"_.pol",0,(void*)member_pol,14,"G","_.pol"},
+{"_.r1",0,(void*)member_r1,14,"G","_.r1"},
+{"_.r2",0,(void*)member_r2,14,"G","_.r2"},
+{"_.reg",0,(void*)member_reg,14,"G","_.reg"},
+{"_.roots",0,(void*)member_roots,14,"G","_.roots"},
+{"_.sign",0,(void*)member_sign,14,"G","_.sign"},
+{"_.t2",0,(void*)member_t2,14,"G","_.t2"},
+{"_.tate",0,(void*)member_tate,14,"G","_.tate"},
+{"_.tu",0,(void*)member_tu,14,"G","_.tu"},
+{"_.tufu",0,(void*)member_tufu,14,"G","_.tufu"},
+{"_.zk",0,(void*)member_zk,14,"G","_.zk"},
+{"_.zkst",0,(void*)member_zkst,14,"G","_.zkst"},
+{"_/=_",0,(void*)gdive,13,"&G","x/=y"},
+{"_/_",0,(void*)gdiv,13,"GG","_/_"},
+{"_<<=_",0,(void*)gshiftle,13,"&L","x<<=y"},
+{"_<<_",0,(void*)gshift,13,"GL","x<<y"},
+{"_<=_",0,(void*)gle,13,"GG","_<=_"},
+{"_<_",0,(void*)glt,13,"GG","_<_"},
+{"_==_",0,(void*)geq,13,"GG","_==_"},
+{"_>=_",0,(void*)gge,13,"GG","_>=_"},
+{"_>>=_",0,(void*)gshiftre,13,"&L","_>>=_"},
+{"_>>_",0,(void*)gshift_right,13,"GL","x>>y"},
+{"_>_",0,(void*)ggt,13,"GG","_>_"},
+{"_[_.._,_.._]",0,(void*)matslice0,13,"GD0,L,D0,L,D0,L,D0,L,","x[a..b,c..d] = [x[a,c],  x[a+1,c],  ...,x[b,c];                      x[a,c+1],x[a+1,c+1],...,x[b,c+1];                        ...       ...          ...                      x[a,d],  x[a+1,d]  ,...,x[b,d]]"},
+{"_[_.._]",0,(void*)vecslice0,13,"GD0,L,L","x[a..b] = [x[a],x[a+1],...,x[b]]"},
+{"_\\/=_",0,(void*)gdivrounde,13,"&G","x\\\\/=y"},
+{"_\\/_",0,(void*)gdivround,13,"GG","x\\\\/y: rounded Euclidean quotient of x and y."},
+{"_\\=_",0,(void*)gdivente,13,"&G","x\\\\=y"},
+{"_\\_",0,(void*)gdivent,13,"GG","x\\\\y: Euclidean quotient of x and y."},
+{"_^_",0,(void*)gpow,13,"GGp","_^_"},
+{"_^s",0,(void*)gpowgs,13,"GL","_^s"},
+{"__",0,NULL,13,NULL,"__"},
+{"_derivfun",0,(void*)derivfun0,15,"GGp","_derivfun(closure,[args]) numerical derivation of closure with respect to the first variable at (args)."},
+{"_eval_mnemonic",0,(void*)eval_mnemonic,15,"lGs","Convert a mnemonic string to a flag."},
+{"_multi_if",0,(void*)ifpari_multi,15,"GE*","internal variant of if() that allows more than 3 arguments."},
+{"_void_if",0,(void*)ifpari_void,11,"vGDIDI","internal variant of if() that does not return a value."},
+{"_||_",0,(void*)orpari,13,"GE","x||y"},
+{"_~",0,(void*)gtrans,13,"G","_~"},
+{"O",0,(void*)ggrando,7,"GD1,L,","O(a^b): p-adic or power series zero with precision given by b."},
+{"O(_^_)",0,(void*)ggrando,7,"GD1,L,","O(a^b): p-adic or power series zero with precision given by b."},
+{"Str",0,(void*)Str,2,"s*","Str({x}*): concatenates its (string) argument into a single string."},
+{"abs",1,(void*)gabs,3,"Gp","abs(x)=absolute value (or modulus) of x."},
+{"acos",1,(void*)gacos,3,"Gp","acos(x)=inverse cosine of x."},
+{"acosh",1,(void*)gacosh,3,"Gp","acosh(x)=inverse hyperbolic cosine of x."},
+{"addell",3,(void*)elladd,5,"GGG","addell(e,z1,z2)=sum of the points z1 and z2 on elliptic curve e."},
+{"addprimes",1,(void*)addprimes,4,"G","addprimes(x)=add primes in the vector x (with at most 20 components) to the prime table."},
+{"adj",1,(void*)adj,8,"G","adj(x)=adjoint matrix of x."},
+{"agm",2,(void*)agm,3,"GGp","agm(x,y)=arithmetic-geometric mean of x and y."},
+{"akell",2,(void*)akell,5,"GG","akell(e,n)=computes the n-th Fourier coefficient of the L-function of the elliptic curve e."},
+{"algdep",23,(void*)algdep,8,"GL","algdep(x,n)=algebraic relations up to degree n of x."},
+{"algdep2",33,(void*)algdep0,8,"GLL","algdep2(x,n,dec)=algebraic relations up to degree n of x where dec is as in  lindep2."},
+{"algtobasis",2,(void*)algtobasis,6,"GG","algtobasis(nf,x)=transforms the algebraic number x into a column vector on the integral basis nf[7]."},
+{"anell",23,(void*)anell,5,"GL","anell(e,n)=computes the first n Fourier coefficients of the L-function of the elliptic curve e (n<32768)."},
+{"apell",2,(void*)ellap,5,"GG","apell(e,p)=computes a_p for the elliptic curve e using Shanks-Mestre's method."},
+{"apell2",2,(void*)ellap,5,"GG","apell2(e,p)=apell(e,p)."},
+{"apprpadic",2,(void*)padicappr,7,"GG","apprpadic(x,a)=p-adic roots of the polynomial x congruent to a mod p."},
+{"arg",1,(void*)garg,3,"Gp","arg(x)=argument of x,such that -pi<arg(x)<=pi."},
+{"asin",1,(void*)gasin,3,"Gp","asin(x)=inverse sine of x."},
+{"asinh",1,(void*)gasinh,3,"Gp","asinh(x)=inverse hyperbolic sine of x."},
+{"assmat",1,(void*)matcompanion,8,"G","matcompanion(x)=associated matrix to polynomial x."},
+{"atan",1,(void*)gatan,3,"Gp","atan(x)=inverse tangent of x."},
+{"atanh",1,(void*)gatanh,3,"Gp","atanh(x)=inverse hyperbolic tangent of x."},
+{"basis",13,(void*)nfbasis0,6,"GD0,L,DG","basis(x)=integral basis of the field Q[a], where a is a root of the polynomial x, using the round 4 algorithm."},
+{"basis2",13,(void*)nfbasis0,6,"GD2,L,DG","basis2(x)=integral basis of the field Q[a], where a is a root of the polynomial x, using the round 2 algorithm."},
+{"basistoalg",2,(void*)basistoalg,6,"GG","basistoalg(nf,x)=transforms the vertical vector x on the integral basis into an algebraic number."},
+{"bernreal",11,(void*)bernreal,3,"Lp","bernreal(x)=Bernoulli number B_x, as a real number with the current precision."},
+{"bernvec",11,(void*)bernvec,3,"L","bernvec(x)=Vector of rational Bernoulli numbers B_0, B_2,... up to B_(2x)."},
+{"bestappr",2,(void*)bestappr,4,"GGp","bestappr(x,k)=gives the best approximation to the real x with denominator less or equal to k."},
+{"bezout",2,(void*)gcdext0,4,"GG","bezout(x,y)=gives a 3-dimensional row vector [u,v,d] such that d=gcd(x,y) and u*x+v*y=d."},
+{"bezoutres",2,(void*)polresultantext,4,"GG","bezoutres(x,y)=gives a 3-dimensional row vector [u,v,d] such that d=resultant(x,y) and u*x+v*y=d, where x and y are polynomials."},
+{"bigomega",1,(void*)bigomega,4,"lG","bigomega(x)=number of repeated prime divisors of x."},
+{"bilhell",3,(void*)bilhell,5,"GGGp","bilhell(e,z1,z2)=canonical bilinear form for the points z1,z2 on the elliptic curve e. Either z1 or z2 can also be a vector/matrix of points."},
+{"bin",21,(void*)binomial,4,"GL","bin(x,y)=binomial coefficient x*(x-1)...*(x-y+1)/y! defined for y in Z and any x."},
+{"binary",1,(void*)binaire,2,"G","binary(x)=gives the vector formed by the binary digits of x (x C-integer)."},
+{"bittest",2,(void*)gbittest,2,"GL","bittest(x,n)=gives bit number n (coefficient of 2^n) of the integer x."},
+{"boundcf",21,(void*)gboundcf,4,"GL","boundcf(x,lmax)=continued fraction expansion of x with at most lmax terms."},
+{"boundfact",21,(void*)boundfact,4,"GL","boundfact(x,lim)=partial factorization of the integer x (using primes up to lim)."},
+{"buchcertify",10,(void*)bnfcertify,6,"lG","buchcertify(bnf)=certify the correctness (i.e. remove the GRH) of the bnf data output by buchinit or buchinitfu."},
+{"buchfu",1,(void*)buchfu,6,"Gp","buchfu(bnf)=compute the fundamental units of the number field bnf output by buchinit."},
+{"buchgen",99,(void*)buchgenfu,6,BUCH_PROTO,"buchgen(P,...)=compute the structure of the class group and the regulator for the number field defined by the polynomial P. See manual for the other parameters (which can be omitted)."},
+{"buchgenforcefu",99,(void*)buchgenforcefu,6,BUCH_PROTO,"buchgenforcefu(P,...)=compute the structure of the class group, the regulator a primitive root of unity and a system of fundamental units for the number field defined by the polynomial P, and insist until the units are obtained. See manual for the other parameters (which can be omitted)."},
+{"buchgenfu",99,(void*)buchgenfu,6,BUCH_PROTO,"buchgenfu(P,...)=compute the structure of the class group, the regulator a primitive root of unity and a system of fundamental units (if they are not too large) for the number field defined by the polynomial P. See manual for the other parameters (which can be omitted)."},
+{"buchimag",99,(void*)buchimag,4,"GD0.1,G,D0.1,G,D5,G,","buchimag(D,...)=compute the structure of the class group of the complex quadratic field of discriminant D<0. See manual for the other parameters (which can be omitted)."},
+{"buchinit",99,(void*)buchinitfu,6,BUCH_PROTO,"buchinit(P,...)=compute the necessary data for future use in ideal and unit group computations. See manual for details."},
+{"buchinitforcefu",99,(void*)buchinitforcefu,6,BUCH_PROTO,"buchinitforcefu(P,...)=compute the necessary data for future use in ideal and unit group computations, and insist on having fundamental units. See manual for details."},
+{"buchinitfu",99,(void*)buchinitfu,6,BUCH_PROTO,"buchinitfu(P,...)=compute the necessary data for future use in ideal and unit group computations, including fundamental units if they are not too large. See manual for details."},
+{"buchnarrow",1,(void*)buchnarrow,6,"Gp","buchnarrow(bnf)=given a big number field as output by buchinitxx, gives as a 3-component vector the structure of the narrow class group."},
+{"buchray",2,(void*)buchray,6,"GGp","buchray(bnf,ideal)=given a big number field as output by buchinitfu (only) and  an ideal or a 2-component row vector formed by an ideal and a list of R1 zeros or ones representing a module, finds the ray class group structure corresponding to this module."},
+{"buchrayinit",2,(void*)buchrayinit,6,"GGp","buchrayinit(bnf,ideal)=same as buchrayinitgen, except that the generators are not explicitly computed."},
+{"buchrayinitgen",2,(void*)buchrayinitgen,6,"GGp","buchrayinitgen(bnf,ideal)=given a big number field as output by buchinitfu (only) and  an ideal or a 2-component row vector formed by an ideal and a list of R1 zeros or ones representing a module, initializes data for computing in the ray class group  corresponding to this module. In particular, the fifth component is the ray class group structure."},
+{"buchreal",97,(void*)buchreal,4,"GD0,G,D0.1,G,D0.1,G,D5,G,p","buchreal(D,...)=compute the structure of the class group and the regulator of the real quadratic field of discriminant D>0 in the wide sense. See manual for the other parameters (which can be omitted)."},
+{"bytesize",10,(void*)gsizebyte,2,"lG","bytesize(x)=number of bytes occupied by the complete tree of the object x."},
+{"ceil",1,(void*)gceil,2,"G","ceil(x)=ceiling of x=smallest integer>=x."},
+{"centerlift",1,(void*)centerlift,2,"G","centerlift(x)=centered lift of x. Same as lift except for integermods."},
+{"cf",1,(void*)gcf,4,"Gp","cf(x)=continued fraction expansion of x (x rational,real or rational function)."},
+{"cf2",2,(void*)gcf2,4,"GGp","cf2(b,x)=continued fraction expansion of x (x rational,real or rational function), where b is the vector of numerators of the continued fraction."},
+{"changevar",0,(void*)suppressed,11,"GG","changevar(x,y)=THIS FUNCTION HAS BEEN SUPPRESSED."},
+{"char",14,(void*)caradj,8,"GnD&","char(x,y)=det(y*I-x)=characteristic polynomial of the matrix x using the comatrix."},
+{"char1",14,(void*)caract,8,"Gn","char1(x,y)=det(y*I-x)=characteristic polynomial of the matrix x using Lagrange interpolation."},
+{"char2",14,(void*)carhess,8,"Gn","char2(x,y)=characteristic polynomial of the matrix x expressed with variable y, using the Hessenberg form. Can be much faster or much slower than char, depending on the base ring."},
+{"chell",2,(void*)ellchangecurve,5,"GG","chell(x,y)=change data on elliptic curve according to y=[u,r,s,t]."},
+{"chinese",2,(void*)chinese,4,"GG","chinese(x,y)=x,y being integers modulo mx and my,finds z such that z is congruent to x mod mx and y mod my."},
+{"chptell",2,(void*)ellchangepoint,5,"GG","chptell(x,y)=change data on point or vector of points x on an elliptic curve according to y=[u,r,s,t]."},
+{"classno",1,(void*)classno,4,"G","classno(x)=class number of discriminant x."},
+{"classno2",1,(void*)classno2,4,"G","classno2(x)=class number of discriminant x."},
+{"coeff",21,(void*)truecoeff,2,"GL","coeff(x,s)=coefficient of degree s of x, or the s-th component for vectors or matrices (for which it is simpler to use x[])."},
+{"compimag",2,(void*)qficomp,4,"GG","compimag(x,y)=Gaussian composition of the binary quadratic forms x and y of negative discriminant."},
+{"compo",21,(void*)compo,2,"GL","compo(x,s)=the s'th component of the internal representation of x. For vectors or matrices, it is simpler to use x[]."},
+{"compositum",2,(void*)compositum,6,"GG","compositum(pol1,pol2)=vector of all possible compositums of the number fields defined by the polynomials pol1 and pol2."},
+{"compositum2",2,(void*)compositum2,6,"GG","compositum2(pol1,pol2)=vector of all possible compositums of the number fields defined by the polynomials pol1 and pol2, with roots of pol1 and pol2 expressed on the compositum polynomials."},
+{"comprealraw",2,(void*)qfrcompraw,4,"GG","comprealraw(x,y)=Gaussian composition without reduction of the binary quadratic forms x and y of positive discriminant."},
+{"concat",2,(void*)concat,8,"GG","concat(x,y)=concatenation of x and y."},
+{"conductor",99,(void*)bnrconductor0,6,"GDGDGD1,L,","conductor(bnr,subgroup)=conductor of the subfield of the ray class field bnr given by buchrayinit, defined by the HNF matrix subgroup."},
+{"conductorofchar",2,(void*)bnrconductorofchar,6,"GG","conductorofchar(bnr,chi)=conductor of the character chi on the ray class group bnr."},
+{"conj",1,(void*)gconj,2,"G","conj(x)=the algebraic conjugate of x."},
+{"conjvec",1,(void*)conjvec,2,"Gp","conjvec(x)=conjugate vector of the algebraic number x."},
+{"content",1,(void*)content,4,"G","content(x)=gcd of all the components of x, when this makes sense."},
+{"convol",2,(void*)convol,7,"GG","convol(x,y)=convolution (or Hadamard product) of two power series."},
+{"core",1,(void*)core,4,"G","core(n)=unique (positive of negative) squarefree integer d dividing n such that n/d is a square."},
+{"core2",1,(void*)core2,4,"G","core2(n)=2-component row vector [d,f], where d is the unique squarefree integer dividing n such that n/d=f^2 is a square."},
+{"coredisc",1,(void*)coredisc,4,"G","coredisc(n)=discriminant of the quadratic field Q(sqrt(n))."},
+{"coredisc2",1,(void*)coredisc2,4,"G","coredisc2(n)=2-component row vector [d,f], where d is the discriminant of the quadratic field Q(sqrt(n)) and n=df^2. f may be a half integer."},
+{"cos",1,(void*)gcos,3,"Gp","cos(x)=cosine of x."},
+{"cosh",1,(void*)gcosh,3,"Gp","cosh(x)=hyperbolic cosine of x."},
+{"cvtoi",13,(void*)gcvtoi,2,"Gf","cvtoi(x)=truncation of x, without taking into account loss of integer part precision."},
+{"cyclo",11,(void*)polcyclo,7,"LDn","cyclo(n)=n-th cyclotomic polynomial."},
+{"decodefactor",1,(void*)factorback,4,"G","decodefactor(fa)=given a factorisation fa, gives the factored object back."},
+{"decodemodule",2,(void*)decodemodule,6,"GG","decodemodule(nf,fa)=given a coded module fa as in discrayabslist, gives the true module."},
+{"degree",10,(void*)degree,2,"lG","degree(x)=degree of the polynomial or rational function x. -1 if equal 0, 0 if non-zero scalar."},
+{"denom",1,(void*)denom,2,"G","denom(x)=denominator of x (or lowest common denominator in case of an array)."},
+{"deplin",1,(void*)deplin,8,"Gp","deplin(x)=finds a linear dependence between the columns of the matrix x."},
+{"deriv",14,(void*)deriv,7,"Gn","deriv(x,y)=derivative of x with respect to the main variable of y."},
+{"det",1,(void*)det,8,"G","det(x)=determinant of the matrix x."},
+{"det2",1,(void*)det2,8,"G","det2(x)=determinant of the matrix x (better for integer entries)."},
+{"detint",1,(void*)detint,8,"G","detint(x)=some multiple of the determinant of the lattice generated by the columns of x (0 if not of maximal rank). Useful with hermitemod."},
+{"diagonal",1,(void*)diagonal,8,"G","diagonal(x)=creates the diagonal matrix whose diagonal entries are the entries of the vector x."},
+{"dilog",1,(void*)dilog,3,"Gp","dilog(x)=dilogarithm of x."},
+{"dirdiv",2,(void*)dirdiv,7,"GG","dirdiv(x,y)=division of the Dirichlet series x by the Dir. series y."},
+{"direuler",83,(void*)direuler0,7,"V=GGEDG","direuler(p=a,b,expr)=Dirichlet Euler product of expression expr from p=a to p=b, limited to b terms. Expr should be a polynomial or rational function in p and X, and X is understood to mean p^(-s)."},
+{"dirmul",2,(void*)dirmul,7,"GG","dirmul(x,y)=multiplication of the Dirichlet series x by the Dir. series y."},
+{"dirzetak",2,(void*)dirzetak,6,"GG","dirzetak(nf,b)=Dirichlet series of the Dedekind zeta function of the number field nf up to the bound b-1."},
+{"disc",1,(void*)poldisc0,7,"GDn","disc(x)=discriminant of the polynomial x."},
+{"discf",1,(void*)nfdisc0,6,"GD0,L,DG","discf(x)=discriminant of the number field defined by the polynomial x using round 4."},
+{"discf2",1,(void*)nfdisc0,6,"GD2,L,DG","discf2(x)=discriminant of the number field defined by the polynomial x using round 2."},
+{"discrayabs",62,(void*)bnrdisc0,6,"GD0,G,D0,G,D0,L,","discrayabs(bnr,subgroup)=absolute [N,R1,disc] of the subfield of the ray class field bnr given by buchrayinit, defined by the HNF matrix subgroup."},
+{"discrayabscond",62,(void*)bnrdisc0,6,"GD0,G,D0,G,D2,L,","discrayabscond(bnr,subgroup)=absolute [N,R1,disc] of the subfield of the ray class field bnr given by buchrayinit, defined by the HNF matrix subgroup. Result is zero if fmodule is not the conductor."},
+{"discrayabslist",2,(void*)discrayabslist,6,"GG","discrayabslist(bnf,listes)=if listes is a 2-component vector as output by ideallistunit or similar, gives list of corresponding discrayabscond."},
+{"discrayabslistarch",32,(void*)discrayabslistarch,6,"GGL","discrayabslistarch(bnf,arch,bound)=gives list of discrayabscond of all modules up to norm bound with archimedean places arch, in a longvector format."},
+{"discrayabslistarchall",32,(void*)discrayabslistarch,6,"GL","discrayabslistarchall(bnf,bound)=gives list of discrayabscond of all modules up to norm bound with all possible archimedean places arch in reverse lexicographic order, in a longvector format."},
+{"discrayabslistlong",21,(void*)discrayabslistlong,6,"GL","discrayabslistlong(bnf,bound)=gives list of discrayabscond of all modules up to norm bound without archimedean places, in a longvector format."},
+{"discrayrel",62,(void*)bnrdisc0,6,"GD0,G,D0,G,D1,L,","discrayrel(bnr,subgroup)=relative [N,R1,rnfdisc] of the subfield of the ray class field bnr given by buchrayinit, defined by the HNF matrix subgroup."},
+{"discrayrelcond",62,(void*)bnrdisc0,6,"GD0,G,D0,G,D3,L,","discrayrelcond(bnr,subgroup)=relative [N,R1,rnfdisc] of the subfield of the ray class field bnr given by buchrayinit, defined by the HNF matrix subgroup. Result is zero if module is not the conductor."},
+{"divisors",1,(void*)divisors,4,"G","divisors(x)=gives a vector formed by the divisors of x in increasing order."},
+{"divres",2,(void*)gdiventres,1,"GG","divres(x,y)=euclidean division of x by y giving as a 2-dimensional column vector the quotient and the remainder."},
+{"divsum",22,(void*)sumdivexpr,9,"GVE","divsum(n,X,expr)=sum of expression expr, X running over the divisors of n."},
+{"eigen",1,(void*)eigen,8,"Gp","eigen(x)=eigenvectors of the matrix x given as columns of a matrix."},
+{"eint1",1,(void*)eint1,3,"Gp","eint1(x)=exponential integral E1(x)."},
+{"erfc",1,(void*)gerfc,3,"Gp","erfc(x)=complementary error function."},
+{"eta",1,(void*)eta,3,"Gp","eta(x)=eta function without the q^(1/24)."},
+{"euler",0,(void*)mpeuler,3,"p","euler=euler()=euler's constant with current precision."},
+{"eval",1,(void*)geval_gp,7,"GC","eval(x)=evaluation of x, replacing variables by their value."},
+{"exp",1,(void*)gexp,3,"Gp","exp(x)=exponential of x."},
+{"extract",2,(void*)extract0,8,"GGDG","extract(x,y)=extraction of the components of the vector x according to the vector or mask y, from left to right (1, 2, 4, 8, ...for the first, second, third, fourth,...component)."},
+{"fact",11,(void*)mpfactr,4,"Lp","fact(x)=factorial of x (x C-integer), the result being given as a real number."},
+{"factcantor",2,(void*)factcantor,4,"GG","factcantor(x,p)=factorization mod p of the polynomial x using Cantor-Zassenhaus."},
+{"factfq",3,(void*)factorff,4,"GGG","factfq(x,p,a)=factorization of the polynomial x in the finite field F_p[X]/a(X)F_p[X]."},
+{"factmod",2,(void*)factmod,4,"GG","factmod(x,p)=factorization mod p of the polynomial x using Berlekamp."},
+{"factor",1,(void*)factor,4,"G","factor(x)=factorization of x."},
+{"factoredbasis",28,(void*)factoredbase,6,"GG","factoredbasis(x,p)=integral basis of the maximal order defined by the polynomial x, where p is the matrix of the factorization of the discriminant of x."},
+{"factoreddiscf",2,(void*)factoreddiscf,6,"GG","factoreddiscf(x,p)=discriminant of the maximal order defined by the polynomial x, where p is the matrix of the factorization of the discriminant of x."},
+{"factoredpolred",2,(void*)factoredpolred,6,"GG","factoredpolred(x,p)=reduction of the polynomial x, where p is the matrix of the factorization of the discriminant of x (gives minimal polynomials only)."},
+{"factoredpolred2",2,(void*)factoredpolred2,6,"GG","factoredpolred2(x,p)=reduction of the polynomial x, where p is the matrix of the factorization of the discriminant of x (gives elements and minimal polynomials)."},
+{"factornf",2,(void*)polfnf,6,"GG","factornf(x,t)=factorization of the polynomial x over the number field defined by the polynomial t."},
+{"factorpadic",32,(void*)factorpadic,7,"GGL","factorpadic(x,p,r)=p-adic factorization of the polynomial x to precision r, using the round 4 algorithm."},
+{"factorpadic2",32,(void*)factorpadic0,7,"GGLD1,L,","factorpadic2(x,p,r)=p-adic factorization of the polynomial x to precision r, using Buchmann-Lenstra."},
+{"factpol",33,(void*)_factpol,7,"GLL","factpol(x,l,hint)=factorization over Z of the polynomial x up to degree l (complete if l=0) using Hensel lift, knowing that the degree of each factor is a multiple of hint."},
+{"factpol2",0,(void*)suppressed,6,"GL","factpol2(x,l)=factorization over Z of the polynomial x up to degree l (complete if l=0) using root finding."},
+{"fibo",11,(void*)fibo,4,"L","fibo(x)=fibonacci number of index x (x C-integer)."},
+{"floor",1,(void*)gfloor,2,"G","floor(x)=floor of x=largest integer<=x."},
+{"for",83,(void*)forpari,11,"vV=GGI","for(X=a,b,seq)=the sequence is evaluated, X going from a up to b."},
+{"fordiv",84,(void*)fordiv,11,"vGVI","fordiv(n,X,seq)=the sequence is evaluated, X running over the divisors of n."},
+{"forprime",83,(void*)forprime,11,"vV=GGI","forprime(X=a,b,seq)=the sequence is evaluated, X running over the primes between a and b."},
+{"forstep",86,(void*)forstep,11,"vV=GGGI","forstep(X=a,b,s,seq)=the sequence is evaluated, X going from a to b in steps of s."},
+{"forvec",87,(void*)forvec,11,"vV=GID0,L,","forvec(x=v,seq)=v being a vector of two-component vectors of length n, the sequence is evaluated with x[i] going from v[i][1] to v[i][2] for i=n,..,1."},
+{"fpn",21,(void*)ffinit,2,"GLDn","fpn(p,n)=monic irreducible polynomial of degree n over F_p[x]."},
+{"frac",1,(void*)gfrac,2,"G","frac(x)=fractional part of x=x-floor(x)."},
+{"galois",1,(void*)polgalois,6,"Gp","galois(x)=Galois group of the polynomial x (see manual for group coding)."},
+{"galoisapply",3,(void*)galoisapply,6,"GGG","galoisapply(nf,aut,x)=Apply the Galois automorphism sigma (polynomial or polymod) to the object x (element or ideal) in the number field nf."},
+{"galoisconj",1,(void*)galoisconj,6,"GDG","galoisconj(nf)=list of conjugates of a root of the polynomial x=nf[1] in the same number field, using p-adics, LLL on integral basis (not always complete)."},
+{"galoisconj1",0,(void*)suppressed,6,"G","galoisconj1(nf)=list of conjugates of a root of the polynomial x=nf[1] in the same number field nf, using complex numbers, LLL on integral basis (not always complete)."},
+{"galoisconjforce",0,(void*)suppressed,6,"G","galoisconjforce(nf)=list of conjugates of a root of the polynomial x=nf[1] in the Galois number field nf, using p-adics, LLL on integral basis. Guaranteed to be complete if the field is Galois, otherwise there is an infinite loop."},
+{"gamh",1,(void*)ggammah,3,"Gp","gamh(x)=gamma of x+1/2 (x integer)."},
+{"gamma",1,(void*)ggamma,3,"Gp","gamma(x)=gamma function at x."},
+{"gauss",2,(void*)gauss,8,"GG","gauss(a,b)=gaussian solution of ax=b (a matrix,b vector)."},
+{"gaussmodulo",3,(void*)gaussmodulo,8,"GGG","gaussmodulo(M,D,Y)=(long)gen_1 solution of system of congruences MX=Y mod D."},
+{"gaussmodulo2",3,(void*)gaussmodulo2,8,"GGG","gaussmodulo2(M,D,Y)=all solutions of system of congruences MX=Y mod D."},
+{"gcd",2,(void*)ggcd,4,"GG","gcd(x,y)=greatest common divisor of x and y."},
+{"getheap",0,(void*)getheap,2,"","getheap()=2-component vector giving the current number of objects in the heap and the space they occupy."},
+{"getrand",0,(void*)getrand,2,"","getrand()=current value of random number seed."},
+{"getstack",0,(void*)getstack,2,"l","getstack()=current value of stack pointer avma."},
+{"gettime",0,(void*)gettime,2,"l","gettime()=time (in milliseconds) since last call to gettime."},
+{"globalred",1,(void*)ellglobalred,5,"G","globalred(e)=e being an elliptic curve, returns [N,[u,r,s,t],c], where N is the conductor of e, [u,r,s,t] leads to the standard model for e, and c is the product of the local Tamagawa numbers c_p."},
+{"goto",0,(void*)suppressed,11,"s*","goto(n)=THIS FUNCTION HAS BEEN SUPPRESSED."},
+{"hclassno",1,(void*)hclassno,4,"G","hclassno(x)=Hurwitz-Kronecker class number of x>0."},
+{"hell",2,(void*)ghell,5,"GGp","hell(e,x)=canonical height of point x on elliptic curve E defined by the vector e computed using theta-functions."},
+{"hell2",2,(void*)ghell2,5,"GGp","hell2(e,x)=canonical height of point x on elliptic curve E defined by the vector e computed using Tate's method."},
+{"hermite",1,(void*)ZM_hnf,8,"G","hermite(x)=(upper triangular) Hermite normal form of x, basis for the lattice formed by the columns of x, using a naive algorithm."},
+{"hermite2",1,(void*)hnfall,8,"G","hermite2(x)=2-component vector [H,U] such that H is an (upper triangular) Hermite normal form of x, basis for the lattice formed by the columns of x, and U is a unimodular matrix such that xU=H, using Batut's algorithm."},
+{"hermitehavas",0,(void*)suppressed,8,"G","hermitehavas(x)=3-component vector [H,U,P] such that H is an (upper triangular) Hermite normal form of x with extra zero columns, U is a unimodular matrix and P is a permutation of the rows such that P applied to xU gives H, using Havas's algorithm."},
+{"hermitemod",2,(void*)hnfmod,8,"GG","hermitemod(x,d)=(upper triangular) Hermite normal form of x, basis for the lattice formed by the columns of x, where d is the non-zero determinant of this lattice."},
+{"hermitemodid",2,(void*)hnfmodid,8,"GG","hermitemodid(x,d)=(upper triangular) Hermite normal form of x concatenated with d times the identity matrix."},
+{"hermiteperm",1,(void*)hnfperm,8,"G","hermiteperm(x)=3-component vector [H,U,P] such that H is an (upper triangular) Hermite normal form of x with extra zero columns, U is a unimodular matrix and P is a permutation of the rows such that P applied to xU gives H, using Batut's algorithm."},
+{"hess",1,(void*)hess,8,"G","hess(x)=Hessenberg form of x."},
+{"hilb",30,(void*) hilbert,4,"lGGG","hilb(x,y,p)=Hilbert symbol at p of x,y (integers or fractions)."},
+{"hilbert",11,(void*)mathilbert,8,"L","hilbert(n)=Hilbert matrix of order n (n C-integer)."},
+{"hilbp",20,(void*) hilbert,4,"lGGDG","hilbp(x,y)=Hilbert symbol of x,y (where x or y is integermod or p-adic)."},
+{"hvector",22,(void*)vecteur,9,"GVE","hvector(n,X,expr)=row vector with n components of expression expr, the variable X ranging from 1 to n."},
+{"hyperu",3,(void*)hyperu,3,"GGGp","hyperu(a,b,x)=U-confluent hypergeometric function."},
+{"i",0,(void*)gen_I,2,"","i=i()=square root of -1."},
+{"idealadd",3,(void*)idealadd,6,"GGG","idealadd(nf,x,y)=sum of two ideals x and y in the number field defined by nf."},
+{"idealaddmultone",2,(void*)idealaddmultoone,6,"GG","idealaddone(nf,x,y)=when the sum of two ideals x and y in the number field K defined by nf is equal to Z_K, gives a two-component vector [a,b] such that a is in x, b is in y and a+b=1."},
+{"idealaddone",3,(void*)idealaddtoone,6,"GGG","idealaddmultone(nf,list)=when the sum of the ideals in the number field K defined by nf and given in the vector list is equal to Z_K, gives a vector of elements of the corresponding ideals who sum to 1."},
+{"idealappr",2,(void*)idealappr,6,"GGp","idealappr(nf,x)=x being a fractional ideal, gives an element b such that v_p(b)=v_p(x) for all prime ideals p dividing x, and v_p(b)>=0 for all other p."},
+{"idealapprfact",2,(void*)idealapprfact,6,"GG","idealapprfact(nf,x)=x being a prime ideal factorization with possibly zero or negative exponents, gives an element b such that v_p(b)=v_p(x) for all prime ideals p dividing x, and v_p(b)>=0 for all other p."},
+{"idealchinese",3,(void*)idealchinese,6,"GGG","idealchinese(nf,x,y)=x being a prime ideal factorization and y a vector of elements, gives an element b such that v_p(b-y_p)>=v_p(x) for all prime ideals p dividing x, and v_p(b)>=0 for all other p."},
+{"idealcoprime",3,(void*)idealcoprime,6,"GGG","idealcoprime(nf,x,y)=gives an element b in nf such that b.x is an integral ideal coprime to the integral ideal y."},
+{"idealdiv",3,(void*)idealdiv,6,"GGG","idealdiv(nf,x,y)=quotient x/y of two ideals x and y in HNF in the number field nf."},
+{"idealdivexact",3,(void*)idealdivexact,6,"GGG","idealdivexact(nf,x,y)=quotient x/y of two ideals x and y in HNF in the number field nf when the quotient is known to be an integral ideal."},
+{"idealfactor",2,(void*)idealfactor,6,"GG","idealfactor(nf,x)=factorization of the ideal x given in HNF into prime ideals in the number field nf."},
+{"idealhermite",2,(void*)idealhnf,6,"GG","idealhermite(nf,x)=hermite normal form of the ideal x in the number field nf, whatever form x may have."},
+{"idealhermite2",3,(void*)idealhnf0,6,"GGG","idealhermite2(nf,a,b)=hermite normal form of the ideal aZ_K+bZ_K in the number field K defined by nf, where a and b are elements."},
+{"idealintersect",3,(void*)idealintersect,6,"GGG","idealintersect(nf,x,y)=intersection of two ideals x and y in HNF in the number field defined by nf."},
+{"idealinv",2,(void*)idealinv,6,"GG","idealinv(nf,x)=inverse of the ideal x in the number field nf not using the different."},
+{"idealinv2",2,(void*)idealinv,6,"GG","idealinv2(nf,x)=inverse of the ideal x in the number field nf using the different."},
+{"ideallist",21,(void*)ideallist,6,"GL","ideallist(nf,bound)=vector of vectors of all ideals of norm<=bound in nf."},
+{"ideallistarch",3,(void*)ideallistarch,6,"GGG","ideallistarch(nf,list,arch)=vector of vectors of all zidealstarinits of all modules in list with archimedean arch added, without generators."},
+{"ideallistarchgen",3,(void*)ideallistarch,6,"GGG","ideallistarchgen(nf,list,arch)=vector of vectors of all zidealstarinits of all modules in list with archimedean arch added, with generators."},
+{"ideallistunit",21,(void*)ideallist0,6,"GLD2,L,","ideallistunit(bnf,bound)=2-component vector [L,U] where L is as ideallistzstar, and U is a vector of vector of zinternallogs of the units, without generators."},
+{"ideallistunitarch",3,(void*)ideallistarch,6,"GGG","ideallistunitarch(bnf,lists,arch)=adds the archimedean arch to the lists output by ideallistunit."},
+{"ideallistunitarchgen",3,(void*)ideallistarch,6,"GGG","ideallistunitarchgen(bnf,lists,arch)=adds the archimedean arch to the lists output by ideallistunitgen."},
+{"ideallistunitgen",21,(void*)ideallist0,6,"GLD3,L,","ideallistunitgen(bnf,bound)=2-component vector [L,U] where L is as ideallistzstar, and U is a vector of vector of zinternallogs of the units, with generators."},
+{"ideallistzstar",21,(void*)ideallist0,6,"GLD0,L,","ideallistzstar(nf,bound)=vector of vectors of all zidealstarinits of all ideals of norm<=bound, without generators."},
+{"ideallistzstargen",21,(void*)ideallist0,6,"GLD1,L,","ideallistzstargen(nf,bound)=vector of vectors of all zidealstarinits of all ideals of norm<=bound, with generators."},
+{"ideallllred",3,(void*)idealred0,6,"GGGp","ideallllred(nf,x,vdir)=LLL reduction of the ideal x in the number field nf along direction vdir, in HNF."},
+{"idealmul",3,(void*)idealmul,6,"GGG","idealmul(nf,x,y)=product of the two ideals x and y in the number field nf."},
+{"idealmulred",3,(void*)idealmulred,6,"GGGp","idealmulred(nf,x,y)=reduced product of the two ideals x and y in the number field nf."},
+{"idealnorm",2,(void*)idealnorm,6,"GG","idealnorm(nf,x)=norm of the ideal x in the number field nf."},
+{"idealpow",3,(void*)idealpow,6,"GGG","idealpow(nf,x,n)=n-th power of the ideal x in HNF in the number field nf."},
+{"idealpowred",3,(void*)idealpowred,6,"GGGp","idealpowred(nf,x,n)=reduced n-th power of the ideal x in HNF in the number field nf."},
+{"idealtwoelt",2,(void*)idealtwoelt,6,"GG","idealtwoelt(nf,x)=2-element representation of an ideal x in the number field nf."},
+{"idealtwoelt2",3,(void*)idealtwoelt2,6,"GGG","idealtwoelt2(nf,x,a)=2-element representation of an ideal x in the number field nf, with the first element equal to a."},
+{"idealval",30,(void*)idealval,6,"lGGG","idealval(nf,x,p)=valuation at p given in primedec format of the ideal x in the number field nf."},
+{"idmat",11,(void*)matid,8,"L","idmat(n)=identity matrix of order n (n C-integer)."},
+{"if",0,(void*)ifpari,11,"GDEDE","if(a,seq1,seq2): if a is nonzero, seq1 is evaluated, otherwise seq2. seq1 and seq2 are optional, and if seq2 is omitted, the preceding comma can be omitted also."},
+{"imag",1,(void*)gimag,2,"G","imag(x)=imaginary part of x."},
+{"image",1,(void*)image,8,"G","image(x)=basis of the image of the matrix x."},
+{"image2",1,(void*)image2,8,"G","image2(x)=basis of the image of the matrix x."},
+{"imagecompl",1,(void*)imagecompl,8,"G","imagecompl(x)=vector of column indices not corresponding to the indices given by the function image."},
+{"incgam",2,(void*)incgam,3,"GGp","incgam(s,x)=incomplete gamma function."},
+{"incgam1",2,(void*)suppressed,3,"GGp","incgam1(s,x)=incomplete gamma function (for debugging only)."},
+{"incgam2",2,(void*)suppressed,3,"GGp","incgam2(s,x)=incomplete gamma function (for debugging only)."},
+{"incgam3",2,(void*)incgamc,3,"GGp","incgam3(s,x)=complementary incomplete gamma function."},
+{"incgam4",3,(void*)incgam0,3,"GGGp","incgam4(s,x,y)=incomplete gamma function where y=gamma(s) is precomputed."},
+{"indexrank",1,(void*)indexrank,8,"G","indexrank(x)=gives two extraction vectors (rows and columns) for the matrix x such that the exracted matrix is square of maximal rank."},
+{"indsort",1,(void*)indexsort,8,"G","indsort(x)=indirect sorting of the vector x."},
+{"initalg",1,(void*)nfinit,6,"Gp","initalg(x)=x being a nonconstant irreducible polynomial, gives the vector: [x,[r1,r2],nfdisc,index,[M,MC,T2,T,different] (see manual),r1+r2 first roots, integral basis, matrix of power basis in terms of integral basis, multiplication table of basis]."},
+{"initalgred",1,(void*)nfinitred,6,"Gp","initalgred(x)=x being a nonconstant irreducible polynomial, finds (using polred) a simpler polynomial pol defining the same number field, and gives the vector: [pol,[r1,r2],nfdisc,index,[M,MC,T2,T,different] (see manual), r1+r2 first roots, integral basis, matrix of power basis in terms of integral basis, multiplication table of basis]."},
+{"initalgred2",1,(void*)nfinitred2,6,"Gp","initalgred2(P)=P being a nonconstant irreducible polynomial, gives a two-element vector [nf,mod(a,pol)], where nf is as output by nfinitred and mod(a,pol) is a polymod equal to mod(x,P) and pol=nf[1]."},
+{"initell",1,(void*)ellinit,5,"GDGp","initell(x)=x being the vector [a1,a2,a3,a4,a6], gives the vector: [a1,a2,a3,a4,a6,b2,b4,b6,b8,c4,c6,delta,j,[e1,e2,e3],w1,w2,eta1,eta2,q,area]."},
+{"initzeta",1,(void*)initzeta,6,"Gp","initzeta(x)=compute number field information necessary to use zetak, where x is an irreducible polynomial."},
+{"integ",14,(void*)integ,7,"Gn","integ(x,y)=formal integration of x with respect to the main variable of y."},
+{"intersect",2,(void*)intersect,8,"GG","intersect(x,y)=intersection of the vector spaces whose bases are the columns of x and y."},
+{"intgen",99,(void*)intnumromb0,9,"V=GGED1,L,p","intgen(X=a,b,s)=general numerical integration of s from a to b with respect to X, to be used after removing singularities."},
+{"intinf",99,(void*)intnumromb0,9,"V=GGED2,L,p","intinf(X=a,b,s)=numerical integration of s from a to b with respect to X, where a or b can be plus or minus infinity (1.0e4000), but of same sign."},
+{"intnum",99,(void*)intnumromb0,9,"V=GGED0,L,p","intnum(X=a,b,s)=numerical integration of s from a to b with respect to X."},
+{"intopen",99,(void*)intnumromb0,9,"V=GGED3,L,p","intopen(X=a,b,s)=numerical integration of s from a to b with respect to X, where s has only limits at a or b."},
+{"inverseimage",2,(void*)inverseimage,8,"GG","inverseimage(x,y)=an element of the inverse image of the vector y by the matrix x if one exists, the empty vector otherwise."},
+{"isdiagonal",10,(void*)isdiagonal,8,"lG","isdiagonal(x)=true(1) if x is a diagonal matrix, false(0) otherwise."},
+{"isfund",1,(void*)isfundamental,4,"lG","isfund(x)=true(1) if x is a fundamental discriminant (including 1), false(0) if not."},
+{"isideal",20,(void*)isideal,6,"lGG","isideal(nf,x)=true(1) if x is an ideal in the number field nf, false(0) if not."},
+{"isincl",2,(void*)nfisincl,6,"GG","isincl(x,y)=tests whether the number field defined by the polynomial x is isomorphic to a subfield of the one defined by y; 0 if not, otherwise all the isomorphisms."},
+{"isinclfast",2,(void*)nfisincl,6,"GG","isinclfast(nf1,nf2)=tests whether the number nf1 is isomorphic to a subfield of nf2 or not. If it gives a non-zero result, this proves that this is the case. However if it gives zero, nf1 may still be isomorphic to a subfield of nf2 so you have to use the much slower isincl to be sure."},
+{"isirreducible",1,(void*)isirreducible,7,"lG","isirreducible(x)=true(1) if x is an irreducible non-constant polynomial, false(0) if x is reducible or constant."},
+{"isisom",2,(void*)nfisisom,6,"GG","isisom(x,y)=tests whether the number field defined by the polynomial x is isomorphic to the one defined by y; 0 if not, otherwise all the isomorphisms."},
+{"isisomfast",2,(void*)nfisisom,6,"GG","isisomfast(nf1,nf2)=tests whether the number fields nf1 and nf2 are isomorphic or not. If it gives a non-zero result, this proves that they are isomorphic. However if it gives zero, nf1 and nf2 may still be isomorphic so you have to use the much slower isisom to be sure."},
+{"isoncurve",20,(void*)oncurve,5,"iGG","isoncurve(e,x)=true(1) if x is on elliptic curve e, false(0) if not."},
+{"isprime",1,(void*)gisprime,4,"GD0,L,","isprime(x)=true(1) if x is a strong pseudoprime for 10 random bases, false(0) if not."},
+{"isprincipal",2,(void*)isprincipal,6,"GG","isprincipal(bnf,x)=bnf being output by buchinit, gives the vector of exponents on the class group generators of x. In particular x is principal if and only if the result is the zero vector."},
+{"isprincipalforce",2,(void*)isprincipalforce,6,"GG","isprincipalforce(bnf,x)=same as isprincipal, except that the precision is doubled until the result is obtained."},
+{"isprincipalgen",2,(void*)isprincipalgen,6,"GG","isprincipalgen(bnf,x)=bnf being output by buchinit, gives [v,alpha,bitaccuracy], where v is the vector of exponents on the class group generators and alpha is the generator of the resulting principal ideal. In particular x is principal if and only if v is the zero vector."},
+{"isprincipalgenforce",2,(void*)isprincipalgenforce,6,"GG","isprincipalgenforce(bnf,x)=same as isprincipalgen, except that the precision is doubled until the result is obtained."},
+{"isprincipalray",2,(void*)isprincipalray,6,"GG","isprincipalray(bnf,x)=bnf being output by buchrayinit, gives the vector of exponents on the ray class group generators of x. In particular x is principal if and only if the result is the zero vector."},
+{"isprincipalraygen",2,(void*)isprincipalraygen,6,"GG","isprincipalraygen(bnf,x)=bnf being output by buchrayinit, gives [v,alpha,bitaccuracy], where v is the vector of exponents on the class group generators and alpha is the generator of the resulting principal ideal. In particular x is principal if and only if v is the zero vector."},
+{"ispsp",1,(void*)gispseudoprime,4,"GD1,L,","ispsp(x)=true(1) if x is a strong pseudoprime, false(0) if not."},
+{"isqrt",1,(void*)sqrtint,4,"G","isqrt(x)=integer square root of x (x integer)."},
+{"isset",10,(void*)setisset,8,"lG","isset(x)=true(1) if x is a set (row vector with strictly increasing entries), false(0) if not."},
+{"issqfree",1,(void*)issquarefree,4,"lG","issqfree(x)=true(1) if x is squarefree, false(0) if not."},
+{"issquare",1,(void*)issquare,4,"lG","issquare(x)=true(1) if x is a square, false(0) if not."},
+{"isunit",2,(void*)bnfisunit,6,"GG","isunit(bnf,x)=bnf being output by buchinit, gives the vector of exponents of x on the fundamental units and the roots of unity if x is a unit, the empty vector otherwise."},
+{"jacobi",1,(void*)jacobi,8,"Gp","jacobi(x)=eigenvalues and orthogonal matrix of eigenvectors of the real symmetric matrix x."},
+{"jbesselh",2,(void*)jbesselh,3,"GGp","jbesselh(n,x)=J-bessel function of index n+1/2 and argument x, where n is a non-negative integer."},
+{"jell",1,(void*)jell,3,"Gp","jell(x)=elliptic j invariant of x."},
+{"karamul",0,(void*)suppressed,7,"GGL","karamul(x,y,k)=THIS FUNCTION HAS BEEN SUPPRESSED."},
+{"kbessel",2,(void*)kbessel,3,"GGp","kbessel(nu,x)=K-bessel function of index nu and argument x (x positive real of type real, nu of any scalar type)."},
+{"kbessel2",2,(void*)kbessel,3,"GGp","kbessel2(nu,x)=K-bessel function of index nu and argument x (x positive real of type real, nu of any scalar type)."},
+{"ker",1,(void*)ker,8,"G","ker(x)=basis of the kernel of the matrix x."},
+{"keri",1,(void*)keri,8,"G","keri(x)=basis of the kernel of the matrix x with integer entries."},
+{"kerint",1,(void*)matkerint0,8,"GD0,L,","kerint(x)=LLL-reduced Z-basis of the kernel of the matrix x with integral entries using a modified LLL."},
+{"kerint1",1,(void*)matkerint0,8,"GD1,L,","kerint1(x)=LLL-reduced Z-basis of the kernel of the matrix x with rational entries using matrixqz3 and the HNF."},
+{"kerint2",1,(void*)suppressed,8,"G","kerint2(x)=LLL-reduced Z-basis of the kernel of the matrix x with integral entries using a modified LLL."},
+{"kro",2,(void*)kronecker,4,"lGG","kro(x,y)=kronecker symbol (x/y)."},
+{"label",0,(void*)suppressed,11,"s*","label(n)=THIS FUNCTION HAS BEEN SUPPRESSED."},
+{"lambdak",2,(void*)glambdak,6,"GGp","lambdak(nfz,s)=Dedekind lambda function of the number field nfz at s, where nfz is the vector computed by initzeta (NOT by nfinit)."},
+{"laplace",1,(void*)laplace,7,"G","laplace(x)=replaces the power series sum of a_n*x^n/n! by sum of a_n*x^n."},
+{"lcm",2,(void*)glcm,4,"GG","lcm(x,y)=least common multiple of x and y=x*y/gcd(x,y)."},
+{"legendre",11,(void*)pollegendre,7,"LDn","legendre(n)=legendre polynomial of degree n (n C-integer)."},
+{"length",1,(void*)glength,2,"lG","length(x)=number of non code words in x."},
+{"lex",20,(void*)lexcmp,2,"iGG","lex(x,y)=compare x and y lexicographically (1 if x>y, 0 if x=y, -1 if x<y)."},
+{"lexsort",1,(void*)lexsort,8,"G","lexsort(x)=sort the elements of the vector x in ascending lexicographic order."},
+{"lift",1,(void*)lift,2,"G","lift(x)=lifts every element of Z/nZ to Z or Z[x]/PZ[x] to Z[x]."},
+{"lindep",1,(void*)lindep,8,"G","lindep(x)=Z-linear dependencies between components of x (Hastad et al)."},
+{"lindep2",23,(void*)lindep2,8,"GL","lindep2(x,dec)=Z-linear dependencies between components of x using LLL, where dec should be about one half the number of decimal digits of precision."},
+{"lll",1,(void*)lll,8,"Gp","lll(x)=lll reduction of the vectors forming the matrix x (gives the unimodular transformation matrix)."},
+{"lll1",1,(void*)suppressed,8,"Gp","lll1(x)=old version of lll reduction of the vectors forming the matrix x (gives the unimodular transformation matrix)."},
+{"lllgen",1,(void*)lllgen,8,"Gp","lllgen(x)=lll reduction of the vectors forming the matrix x with polynomial coefficients (gives the unimodular transformation matrix)."},
+{"lllgram",1,(void*)lllgram,8,"Gp","lllgram(x)=lll reduction of the lattice whose gram matrix is x (gives the unimodular transformation matrix)."},
+{"lllgram1",1,(void*)suppressed,8,"Gp","lllgram1(x)=old version of lll reduction of the lattice whose gram matrix is x (gives the unimodular transformation matrix)."},
+{"lllgramgen",1,(void*)lllgramgen,8,"G","lllgramgen(x)=lll reduction of the lattice whose gram matrix is x with polynomial coefficients (gives the unimodular transformation matrix)."},
+{"lllgramint",1,(void*)lllgramint,8,"G","lllgramint(x)=lll reduction of the lattice whose gram matrix is the integral matrix x (gives the unimodular transformation matrix)."},
+{"lllgramkerim",1,(void*)lllgramkerim,8,"G","lllgramkerim(x)=kernel and lll reduction of the lattice whose gram matrix is the integral matrix x."},
+{"lllgramkerimgen",1,(void*)lllgramkerimgen,8,"G","lllgramkerimgen(x)=kernel and lll reduction of the lattice whose gram matrix is the matrix x with polynomial coefficients."},
+{"lllint",1,(void*)lllint,8,"G","lllint(x)=lll reduction of the vectors forming the matrix x when the gram matrix is integral (gives the unimodular transformation matrix)."},
+{"lllintpartial",1,(void*)lllintpartial,8,"G","lllintpartial(x)=partial (hence faster) lll reduction of the vectors forming the matrix x when the gram matrix is integral (gives the unimodular transformation matrix)."},
+{"lllkerim",1,(void*)lllkerim,8,"G","lllkerim(x)=kernel and lll reduction of the vectors forming the integral matrix x."},
+{"lllkerimgen",1,(void*)lllkerimgen,8,"G","lllkerimgen(x)=kernel and lll reduction of the vectors forming the matrix x with polynomial coefficients."},
+{"lllrat",1,(void*)suppressed,8,"G","lllrat(x)=lll reduction of the vectors forming the matrix x, computations done with rational numbers (gives the unimodular transformation matrix)."},
+{"ln",1,(void*)glog,3,"Gp","ln(x)=log(x)=natural logarithm of x."},
+{"lngamma",1,(void*)glngamma,3,"Gp","lngamma(x)=logarithm of the gamma function of x."},
+{"localred",2,(void*)elllocalred,5,"GG","localred(e,p)=e being an ellliptic curve, returns [f,kod,[u,r,s,t],c], where f is the conductor's exponent, kod is the kodaira type for e at p, [u,r,s,t] is the change of variable needed to make e minimal at p, and c is the local Tamagawa number c_p."},
+{"log",1,(void*)glog,3,"Gp","log(x)=ln(x)=natural logarithm of x."},
+{"logagm",1,(void*)glog,3,"Gp","logagm(x)=natural logarithm of x, computed using agm (faster than log for more than a few hundred decimal digits)."},
+{"lseriesell",4,(void*)lseriesell0,5,"GGGGp","lseriesell(e,s,N,A)=L-series at s of the elliptic curve e, where |N| is the conductor, sign(N) the sign of the functional equation, and A a cut-off point close to 1."},
+{"makebigbnf",1,(void*)bnfinit0,6,"GD0,L,DGp","makebigbnf(sbnf)=transforms small sbnf as output by smallbuchinit into a true big bnf."},
+{"mat",1,(void*)gtomat,8,"G","mat(x)=transforms any GEN x into a matrix."},
+{"matextract",3,(void*)extract0,8,"GGG","matextract(x,y,z)=extraction of the components of the matrix x according to the vector or masks y (for the rows) and z (for the columns) from left to right (1,2,4,8,...for the first, second, third, fourth, ...rows or columns)."},
+{"mathell",2,(void*)mathell,5,"GGp","mathell(e,x)=gives the height matrix for vector of points x on elliptic curve e using theta functions."},
+{"matrix",49,(void*)matrice,9,"GGVVE","matrix(m,n,X,Y,expr)=mXn matrix of expression expr, the row variable X going  from 1 to m and the column variable Y going from 1 to n."},
+{"matrixqz",2,(void*)QM_minors_coprime,8,"GG","matrixqz(x,p)=transforms the rational or integral mxn (m>=n) matrix x into an integral matrix with gcd of maximal determinants equal to 1 if p is equal to 0, not divisible by p otherwise."},
+{"matrixqz2",1,(void*)QM_ImZ_hnf,8,"G","matrixqz2(x)=finds a basis of the intersection with Z^n of the lattice spanned by the columns of x."},
+{"matrixqz3",1,(void*)QM_ImQ_hnf,8,"G","matrixqz3(x)=finds a basis of the intersection with Z^n of the Q-vector space spanned by the columns of x."},
+{"matsize",1,(void*)matsize,2,"G","matsize(x)=number of rows and columns of the vector/matrix x as a 2-vector."},
+{"max",2,(void*)gmax,1,"GG","max(x,y)=maximum of x and y."},
+{"min",2,(void*)gmin,1,"GG","min(x,y)=minimum of x and y."},
+{"minideal",3,(void*)idealmin,6,"GGG","idealmin(nf,ix,vdir)=minimum of the ideal ix in the direction vdir in the number field nf."},
+{"minim",33,(void*)minim,8,"GGG","minim(x,bound,maxnum)=number of vectors of square norm <= bound, maximum norm and list of vectors for the integral and definite quadratic form x; minimal non-zero vectors if bound=0."},
+{"minim2",23,(void*)minim2,8,"GG","minim2(x,bound)=looks for vectors of square norm <= bound, return the first one and its norm."},
+{"mod",25,(void*)gmodulo,2,"GG","mod(x,y)=creates the integer x modulo y on the PARI stack."},
+{"modp",25,(void*)gmodulo,2,"GG","modp(x,y)=creates the integer x modulo y as a permanent object (on the heap)."},
+{"modreverse",1,(void*)modreverse,6,"G","modreverse(x)=reverse polymod of the polymod x, if it exists."},
+{"modulargcd",2,(void*)QX_gcd,2,"GG","modulargcd(x,y)=gcd of the polynomials x and y using the modular method."},
+{"mu",1,(void*)moebius,4,"lG","mu(x)=Moebius function of x."},
+{"newtonpoly",2,(void*)newtonpoly,6,"GG","newtonpoly(x,p)=Newton polygon of polynomial x with respect to the prime p."},
+{"nextprime",1,(void*)nextprime,4,"G","nextprime(x)=smallest prime number>=x."},
+{"nfdetint",2,(void*)nfdetint,6,"GG","nfdetint(nf,x)=multiple of the ideal determinant of the pseudo generating set x."},
+{"nfdiv",3,(void*)nfdiv,6,"GGG","nfdiv(nf,a,b)=element a/b in nf."},
+{"nfdiveuc",3,(void*)nfdiveuc,6,"GGG","nfdiveuc(nf,a,b)=gives algebraic integer q such that a-bq is small."},
+{"nfdivres",3,(void*)nfdivrem,6,"GGG","nfdivres(nf,a,b)=gives [q,r] such that r=a-bq is small."},
+{"nfhermite",2,(void*)nfhnf,6,"GG","nfhermite(nf,x)=if x=[A,I], gives a pseudo-basis of the module sum A_jI_j."},
+{"nfhermitemod",3,(void*)nfhnfmod,6,"GGG","nfhermitemod(nf,x,detx)=if x=[A,I], and detx is a multiple of the ideal determinant of x, gives a pseudo-basis of the module sum A_jI_j."},
+{"nfmod",3,(void*)nfmod,6,"GGG","nfmod(nf,a,b)=gives r such that r=a-bq is small with q algebraic integer."},
+{"nfmul",3,(void*)nfmul,6,"GGG","nfmul(nf,a,b)=element a.b in nf."},
+{"nfpow",3,(void*)nfpow,6,"GGG","nfpow(nf,a,k)=element a^k in nf."},
+{"nfreduce",3,(void*)nfreduce,6,"GGG","nfreduce(nf,a,id)=gives r such that a-r is the ideal id and r is small."},
+{"nfsmith",2,(void*)nfsnf,6,"GG","nfsmith(nf,x)=if x=[A,I,J], outputs [c_1,...c_n] Smith normal form of x."},
+{"nfval",30,(void*)nfval,6,"lGGG","nfval(nf,a,pr)=valuation of element a at the prime pr."},
+{"norm",1,(void*)gnorm,2,"G","norm(x)=norm of x."},
+{"norml2",1,(void*)gnorml2,2,"G","norml2(x)=square of the L2-norm of the vector x."},
+{"nucomp",3,(void*)nucomp,4,"GGG","nucomp(x,y,l)=composite of primitive positive definite quadratic forms x and y using nucomp and nudupl, where l=[|D/4|^(1/4)] is precomputed."},
+{"numdiv",1,(void*)numdiv,4,"G","numdiv(x)=number of divisors of x."},
+{"numer",1,(void*)numer,2,"G","numer(x)=numerator of x."},
+{"nupow",2,(void*)nupow,4,"GG","nupow(x,n)=n-th power of primitive positive definite quadratic form x using nucomp and nudupl."},
+{"o",0,(void*)ggrando,7,"GD1,L,","o(a^b): p-adic or power series zero with precision given by b."},
+{"omega",1,(void*)omega,4,"lG","omega(x)=number of unrepeated prime divisors of x."},
+{"ordell",2,(void*)ellordinate,5,"GGp","ordell(e,x)=y-coordinates corresponding to x-ordinate x on elliptic curve e."},
+{"order",1,(void*)order,4,"G","order(x)=order of the integermod x in (Z/nZ)*."},
+{"orderell",2,(void*)orderell,5,"GG","orderell(e,p)=order of the point p on the elliptic curve e over Q, 0 if non-torsion."},
+{"ordred",1,(void*)polredord,6,"Gp","ordred(x)=reduction of the polynomial x, staying in the same order."},
+{"padicprec",20,(void*)padicprec,2,"lGG","padicprec(x,p)=absolute p-adic precision of object x."},
+{"pascal",99,(void*)matqpascal,8,"LDG","pascal(n)=pascal triangle of order n (n C-integer)."},
+{"perf",10,(void*)perf,8,"G","perf(a)=rank of matrix of xx~ for x minimal vectors of a gram matrix a."},
+{"permutation",24,(void*)numtoperm,2,"LG","permutation(n,k)=permutation number k (mod n!) of n letters (n C-integer)."},
+{"permutation2num",1,(void*)permtonum,2,"G","permutation2num(vect)=ordinal (between 1 and n!) of permutation vect."},
+{"pf",2,(void*)primeform,4,"GGp","pf(x,p)=returns the prime form whose first coefficient is p, of discriminant x."},
+{"phi",1,(void*)eulerphi,4,"G","phi(x)=Euler's totient function of x."},
+{"pi",0,(void*)mppi,3,"p","pi=pi()=the constant pi, with current precision."},
+{"pnqn",1,(void*)pnqn,4,"G","pnqn(x)=[p_n,p_{n-1};q_n,q_{n-1}] corresponding to the continued fraction x."},
+{"pointell",2,(void*)pointell,5,"GGp","pointell(e,z)=coordinates of point on the curve e corresponding to the complex number z."},
+{"polint",31,(void*)polint,7,"GGGD&","polint(xa,ya,x)=polynomial interpolation at x according to data vectors xa, ya."},
+{"polred",1,(void*)polred,6,"G","polred(x)=reduction of the polynomial x (gives minimal polynomials only)."},
+{"polred2",1,(void*)polred2,6,"G","polred2(x)=reduction of the polynomial x (gives elements and minimal polynomials)."},
+{"polredabs",1,(void*)polredabs,6,"G","polredabs(x)=a smallest generating polynomial of the number field for the T2 norm on the roots, with smallest index for the minimal T2 norm."},
+{"polredabs2",1,(void*)polredabs2,6,"G","polredabs2(x)=gives [pol,a] where pol is as in polredabs, and alpha is the element whose characteristic polynomial is pol."},
+{"polredabsall",1,(void*)polredabsall,6,"G","polredabsall(x)=complete list of the smallest generating polynomials of the number field for the T2 norm on the roots."},
+{"polredabsfast",0,(void*)suppressed,6,"G","polredabsfast(x)=a smallest generating polynomial of the number field for the T2 norm on the roots."},
+{"polredabsnored",1,(void*)polredabs,6,"G","polredabsnored(x)=a smallest generating polynomial of the number field for the T2 norm on the roots without initial polred."},
+{"polsym",21,(void*)polsym,7,"GL","polsym(x,n)=vector of symmetric powers of the roots of x up to n."},
+{"polvar",1,(void*)gpolvar,2,"G","polvar(x)=main variable of object x. Gives p for p-adic x, error for scalars."},
+{"poly",14,(void*)gtopoly,2,"Gn","poly(x,v)=convert x (usually a vector or a power series) into a polynomial with variable v, starting with the leading coefficient."},
+{"polylog",24,(void*)gpolylog,3,"LGp","polylog(m,x)=m-th polylogarithm of x."},
+{"polylogd",24,(void*)polylogd,3,"LGp","polylogd(m,x)=D_m~-modified m-th polylog of x."},
+{"polylogdold",24,(void*)polylogdold,3,"LGp","polylogdold(m,x)=D_m-modified m-th polylog of x."},
+{"polylogp",24,(void*)polylogp,3,"LGp","polylogp(m,x)=P_m-modified m-th polylog of x."},
+{"polyrev",14,(void*)gtopolyrev,2,"Gn","polyrev(x,v)=convert x (usually a vector or a power series) into a polynomial with variable v, starting with the constant term."},
+{"polzag",21,(void*)polzag,9,"LL","polzag(n,m)=Zagier's polynomials of index n,m."},
+{"powell",3,(void*)ellmul,5,"GGGp","powell(e,x,n)=n times the point x on elliptic curve e (n in Z)."},
+{"powrealraw",23,(void*)qfrpowraw,4,"GL","powrealraw(x,n)=n-th power without reduction of the binary quadratic form x of positive discriminant."},
+{"prec",21,(void*)gprec,2,"GL","prec(x,n)=change the precision of x to be n (n C-integer)."},
+{"precision",1,(void*)precision0,2,"GD0,L,","precision(x)=real precision of object x."},
+{"prime",11,(void*)prime,4,"L","prime(n)=returns the n-th prime (n C-integer)."},
+{"primedec",2,(void*)idealprimedec,6,"GG","primedec(nf,p)=prime ideal decomposition of the prime number p in the number field nf as a vector of 5 component vectors [p,a,e,f,b] representing the prime ideals pZ_K+a.Z_K, e,f as usual, a as vector of components on the  integral basis, b Lenstra's constant."},
+{"primes",11,(void*)primes,4,"L","primes(n)=returns the vector of the first n primes (n C-integer)."},
+{"primroot",1,(void*)znprimroot,4,"G","primroot(n)=returns a primitive root of n when it exists."},
+{"principalideal",2,(void*)principalideal,6,"GG","principalideal(nf,x)=returns the principal ideal generated by the algebraic number x in the number field nf."},
+{"principalidele",2,(void*)suppressed,6,"GG","principalidele(nf,x)=returns the principal idele generated by the algebraic number x in the number field nf."},
+{"prod",48,(void*)prod0,9,"GV=GGE","prod(x,X=a,b,expr)=x times the product (X runs from a to b) of expression."},
+{"prodeuler",37,(void*)prodeuler0,9,"V=GGEp","prodeuler(X=a,b,expr)=Euler product (X runs over the primes between a and b) of real or complex expression."},
+{"prodinf",27,(void*)prodinf0,9,"V=GED0,L,p","prodinf(X=a,expr)=infinite product (X goes from a to infinity) of real or complex expression."},
+{"prodinf1",27,(void*)prodinf0,9,"V=GED1,L,p","prodinf1(X=a,expr)=infinite product (X goes from a to infinity) of real or complex 1+expression."},
+{"psi",1,(void*)gpsi,3,"Gp","psi(x)=psi-function at x."},
+{"qfi",3,(void*)qfi,4,"GGG","qfi(a,b,c)=binary quadratic form a*x^2+b*x*y+c*y^2 with b^2-4*a*c<0."},
+{"qfr",4,(void*)qfr,4,"GGGG","qfr(a,b,c,d)=binary quadratic form a*x^2+b*x*y+c*y^2 with b^2-4*a*c>0 and distance d."},
+{"quaddisc",1,(void*)quaddisc,4,"G","quaddisc(x)=discriminant of the quadratic field Q(sqrt(x))."},
+{"quadgen",1,(void*)quadgen,2,"G","quadgen(x)=standard generator of quadratic order of discriminant x."},
+{"quadpoly",1,(void*)quadpoly,2,"G","quadpoly(x)=quadratic polynomial corresponding to the discriminant x."},
+{"random",0,(void*)genrand,2,"DG","random()=random integer between 0 and 2^31-1."},
+{"rank",10,(void*)rank,8,"lG","rank(x)=rank of the matrix x."},
+{"rayclassno",2,(void*)bnrclassno,6,"GG","rayclassno(bnf,x)=ray class number of the module x for the big number field bnf. Faster than buchray if only the ray class number is wanted."},
+{"rayclassnolist",2,(void*)bnrclassnolist,6,"GG","rayclassnolist(bnf,liste)=if listes is as output by idealisunit or similar, gives list of corresponding ray class numbers."},
+{"real",1,(void*)greal,2,"G","real(x)=real part of x."},
+{"recip",1,(void*)polrecip,7,"G","recip(x)=reciprocal polynomial of x."},
+{"redimag",1,(void*)redimag,4,"G","redimag(x)=reduction of the binary quadratic form x with D<0."},
+{"redreal",1,(void*)redreal,4,"G","redreal(x)=reduction of the binary quadratic form x with D>0."},
+{"redrealnod",2,(void*)redrealnod,4,"GG","redrealnod(x,sq)=reduction of the binary quadratic form x with D>0 without distance function where sq=[sqrt D]."},
+{"reduceddisc",1,(void*)reduceddiscsmith,7,"G","reduceddisc(f)=vector of elementary divisors of Z[a]/f'(a)Z[a], where a is a root of the polynomial f."},
+{"regula",1,(void*)quadregulator,4,"Gp","regula(x)=regulator of the real quadratic field of discriminant x."},
+{"reorder",0,(void*)suppressed,11,"G","reorder(x)=THIS FUNCTION HAS BEEN SUPPRESSED."},
+{"resultant",2,(void*)resultant_all,7,"GGD&","resultant(x,y)=resultant of the polynomials x and y with exact entries."},
+{"resultant2",2,(void*)resultant2,7,"GG","resultant2(x,y)=resultant of the polynomials x and y."},
+{"reverse",1,(void*)serreverse,7,"G","reverse(x)=reversion of the power series x."},
+{"rhoreal",1,(void*)rhoreal,4,"G","rhoreal(x)=single reduction step of the binary quadratic form x of positive discriminant."},
+{"rhorealnod",2,(void*)rhorealnod,4,"GG","rhorealnod(x,sq)=single reduction step of the binary quadratic form x with D>0 without distance function where sq=[sqrt D]."},
+{"rndtoi",13,(void*)grndtoi,2,"Gf","rndtoi(x)=take the nearest integer to all the coefficients of x, without taking into account loss of integer part precision."},
+{"rnfbasis",2,(void*)rnfbasis,6,"GG","rnfbasis(bnf,order)=given an order as output by rnfpseudobasis or rnfsteinitz, gives either a basis of the order if it is free, or an n+1-element generating set."},
+{"rnfdiscf",2,(void*)rnfdiscf,6,"GG","rnfdiscf(nf,pol)=given a pol with coefficients in nf, gives a 2-component vector [D,d], where D is the relative ideal discriminant, and d is the relative discriminant in nf^*/nf*^2."},
+{"rnfequation",2,(void*)rnfequation,6,"GG","rnfequation(nf,pol)=given a pol with coefficients in nf, gives the absolute equation of the number field defined by pol."},
+{"rnfequation2",2,(void*)rnfequation2,6,"GG","rnfequation2(nf,pol)=given a pol with coefficients in nf, gives [apol,th], where apol is the absolute equation of the number field defined by pol and th expresses the root of nf[1] in terms of the root of apol."},
+{"rnfhermitebasis",2,(void*)rnfhnfbasis,6,"GG","rnfhermitebasis(bnf,order)=given an order as output by rnfpseudobasis, gives either a true HNF basis of the order if it exists, zero otherwise."},
+{"rnfisfree",20,(void*)rnfisfree,6,"lGG","rnfisfree(bnf,order)=given an order as output by rnfpseudobasis or rnfsteinitz, outputs true (1) or false (0) according to whether the order is free or not."},
+{"rnflllgram",3,(void*)rnflllgram,6,"GGG","rnflllgram(nf,pol,order)=given a pol with coefficients in nf and an order as output by rnfpseudobasis or similar, gives [[neworder],U], where neworder is a reduced order and U is the unimodular transformation matrix."},
+{"rnfpolred",2,(void*)rnfpolred,6,"GG","rnfpolred(nf,pol)=given a pol with coefficients in nf, finds a list of polynomials defining some subfields, hopefully simpler."},
+{"rnfpseudobasis",2,(void*)rnfpseudobasis,6,"GG","rnfpseudobasis(nf,pol)=given a pol with coefficients in nf, gives a 4-component vector [A,I,D,d] where [A,I] is a pseudo basis of the maximal order in HNF on the power basis, D is the relative ideal discriminant, and d is the relative discriminant in nf^*/nf*^2."},
+{"rnfsteinitz",2,(void*)rnfsteinitz,6,"GG","rnfsteinitz(nf,order)=given an order as output by rnfpseudobasis, gives [A,I,..] where (A,I) is a pseudo basis where all the ideals except perhaps the last are trivial."},
+{"rootmod",2,(void*)rootmod,7,"GG","rootmod(x,p)=roots mod p of the polynomial x."},
+{"rootmod2",2,(void*)rootmod2,7,"GG","rootmod2(x,p)=roots mod p of the polynomial x, when p is small."},
+{"rootpadic",32,(void*)rootpadic,7,"GGL","rootpadic(x,p,r)=p-adic roots of the polynomial x to precision r."},
+{"roots",1,(void*)roots,7,"Gp","roots(x)=roots of the polynomial x using Schonhage's method modified by Gourdon."},
+{"rootsof1",1,(void*)rootsof1,6,"G","rootsof1(nf)=number of roots of unity and primitive root of unity in the number field nf."},
+{"rootsold",0,(void*)suppressed,11,"G","rootsold(x)=THIS FUNCTION HAS BEEN SUPPRESSED."},
+{"round",1,(void*)ground,2,"G","round(x)=take the nearest integer to all the coefficients of x."},
+{"rounderror",10,(void*)rounderror,2,"lG","rounderror(x)=maximum error found in rounding x."},
+{"series",14,(void*)gtoser,2,"GnDP","series(x,v)=convert x (usually a vector) into a power series with variable v, starting with the constant coefficient."},
+{"set",1,(void*)gtoset,8,"G","set(x)=convert x into a set, i.e. a row vector with strictly increasing coefficients."},
+{"setintersect",2,(void*)setintersect,8,"GG","setintersect(x,y)=intersection of the sets x and y."},
+{"setminus",2,(void*)setminus,8,"GG","setminus(x,y)=set of elements of x not belonging to y."},
+{"setrand",11,(void*)setrand,2,"vG","setrand(n)=reset the seed of the random number generator to n."},
+{"setsearch",20,(void*)setsearch,8,"lGGD0,L,","setsearch(x,y)=looks if y belongs to the set x. Returns 0 if it is not, otherwise returns the index j such that y==x[j]."},
+{"setunion",2,(void*)setunion,8,"GG","setunion(x,y)=union of the sets x and y."},
+{"shift",21,(void*)gshift,1,"GL","shift(x,n)=shift x left n bits if n>=0, right -n bits if n<0."},
+{"shiftmul",21,(void*)gmul2n,1,"GL","shiftmul(x,n)=multiply x by 2^n (n>=0 or n<0)."},
+{"sigma",1,(void*)sumdiv,4,"G","sigma(x)=sum of the divisors of x."},
+{"sigmak",24,(void*)sigmak0,4,"LG","sigmak(k,x)=sum of the k-th powers of the divisors of x (k C-integer)."},
+{"sign",10,(void*)gsigne,1,"iG","sign(x)=sign of x, of type integer, real or fraction."},
+{"signat",1,(void*)qfsign,8,"G","signat(x)=signature of the symmetric matrix x."},
+{"signunit",1,(void*)signunits,6,"G","signunit(bnf)=matrix of signs of the real embeddings of the system of fundamental units found by buchinit."},
+{"simplefactmod",2,(void*)simplefactmod,4,"GG","simplefactmod(x,p)=same as factmod except that only the degrees of the irreducible factors are given."},
+{"simplify",1,(void*)simplify,2,"G","simplify(x)=simplify the object x as much as possible."},
+{"sin",1,(void*)gsin,3,"Gp","sin(x)=sine of x."},
+{"sinh",1,(void*)gsinh,3,"Gp","sinh(x)=hyperbolic sine of x."},
+{"size",10,(void*)sizedigit,2,"lG","size(x)=maximum number of decimal digits minus one of (the coefficients of) x."},
+{"smallbasis",13,(void*)nfbasis0,6,"GD1,L,DG","smallbasis(x)=integral basis of the field Q[a], where a is a root of the polynomial x where one assumes that no square of a prime>primelimit divides the discriminant of x."},
+{"smallbuchinit",93,(void*)smallbuchinit,6,BUCH_PROTO,"smallbuchinit(pol)=small buchinit, which can be converted to a big one using makebigbnf."},
+{"smalldiscf",1,(void*)nfdisc0,6,"GD1,L,DG","smalldiscf(x)=discriminant of the number field defined by the polynomial x where one assumes that no square of a prime>primelimit divides the discriminant of x."},
+{"smallfact",1,(void*)boundfact,4,"GD0,L,","smallfact(x)=partial factorization of the integer x (using only the stored primes)."},
+{"smallinitell",1,(void*)ellinit,5,"GDGp","smallinitell(x)=x being the vector [a1,a2,a3,a4,a6], gives the vector: [a1,a2,a3,a4,a6,b2,b4,b6,b8,c4,c6,delta,j]."},
+{"smallpolred",1,(void*)smallpolred,6,"G","smallpolred(x)=partial reduction of the polynomial x (gives minimal polynomials only)."},
+{"smallpolred2",1,(void*)smallpolred2,6,"G","smallpolred2(x)=partial reduction of the polynomial x (gives elements and minimal polynomials)."},
+{"smith",1,(void*)smith,8,"G","smith(x)=Smith normal form (i.e. elementary divisors) of the matrix x, expressed as a vector."},
+{"smith2",1,(void*)smithall,8,"G","smith2(x)=gives a three element vector [u,v,d] where u and v are square unimodular matrices such that d=u*x*v=diagonal(smith(x))."},
+{"smithclean",1,(void*)smithclean,8,"G","smithclean(z)=if z=[u,v,d] as output by smith2, removes from u,v,d the rows and columns corresponding to entries equal to 1 in d."},
+{"smithpol",1,(void*)gsmith,8,"G","smithpol(x)=Smith normal form (i.e. elementary divisors) of the matrix x with polynomial coefficients, expressed as a vector."},
+{"solve",37,(void*)zbrent0,9,"V=GGEp","solve(X=a,b,expr)=real root of expression expr (X between a and b), where expr(a)*expr(b)<=0."},
+{"sort",1,(void*)sort,8,"G","sort(x)=sort in ascending order of the vector x."},
+{"sqr",1,(void*)gsqr,3,"G","sqr(x)=square of x. NOT identical to x*x."},
+{"sqred",1,(void*)qfgaussred,8,"G","sqred(x)=square reduction of the (symmetric) matrix x ( returns a square matrix whose i-th diagonal term is the coefficient of the i-th square in which the coefficient of the i-th variable is 1)."},
+{"sqrt",1,(void*)gsqrt,3,"Gp","sqrt(x)=square root of x."},
+{"srgcd",2,(void*)RgX_gcd,7,"GG","srgcd(x,y)=polynomial gcd of x and y using the subresultant algorithm."},
+{"sturm",10,(void*)sturm0,7,"lG","sturm(x)=number of real roots of the polynomial x."},
+{"sturmpart",30,(void*)sturmpart,7,"lGGG","sturmpart(x,a,b)=number of real roots of the polynomial x in the interval (a,b]."},
+{"subcyclo",2,(void*)polsubcyclo,6,"LLDn","subcyclo(p,d)=finds an equation for the d-th degree subfield of Q(zeta_p), where p must be a prime power."},
+{"subell",3,(void*)ellsub,5,"GGGp","subell(e,z1,z2)=difference of the points z1 and z2 on elliptic curve e."},
+{"subst",26,(void*)gsubst,7,"GnG","subst(x,y,z)=in expression x, replace the variable y by the expression z."},
+{"sum",48,(void*)sum0,9,"GV=GGE","sum(x,X=a,b,expr)=x plus the sum (X goes from a to b) of expression expr."},
+{"sumalt",27,(void*)sumalt0,9,"V=GED0,L,p","sumalt(X=a,expr)=Villegas-Zagier's acceleration of alternating series expr, X starting at a."},
+{"sumalt2",27,(void*)sumalt0,9,"V=GED1,L,p","sumalt2(X=a,expr)=Cohen-Villegas-Zagier's acceleration of alternating series expr, X starting at a."},
+{"suminf",27,(void*)suminf0,9,"V=GEp","suminf(X=a,expr)=infinite sum (X goes from a to infinity) of real or complex expression expr."},
+{"sumpos",27,(void*)sumpos0,9,"V=GED0,L,p","sumpos(X=a,expr)=sum of positive series expr, the formal variable X starting at a."},
+{"sumpos2",27,(void*)sumpos0,9,"V=GED1,L,p","sumpos2(X=a,expr)=sum of positive series expr, the formal variable X starting at a, using Zagier's polynomials."},
+{"supplement",1,(void*)suppl,8,"G","supplement(x)=supplement the columns of the matrix x to an invertible matrix."},
+{"sylvestermatrix",2,(void*)sylvestermatrix,7,"GG","sylvestermatrix(x,y)=forms the sylvester matrix associated to the two polynomials x and y. Warning: the polynomial coefficients are in columns, not in rows."},
+{"tan",1,(void*)gtan,3,"Gp","tan(x)=tangent of x."},
+{"tanh",1,(void*)gtanh,3,"Gp","tanh(x)=hyperbolic tangent of x."},
+{"taniyama",1,(void*)elltaniyama,5,"GP","taniyama(e)=modular parametrization of elliptic curve e."},
+{"taylor",12,(void*)tayl,7,"GnP","taylor(x,y)=taylor expansion of x with respect to the main variable of y."},
+{"tchebi",11,(void*)polchebyshev,7,"LD1,L,Dn","tchebi(n)=Tchebitcheff polynomial of degree n (n C-integer)."},
+{"teich",1,(void*)teich,3,"Gp","teich(x)=teichmuller character of p-adic number x."},
+{"theta",2,(void*)theta,3,"GGp","theta(q,z)=Jacobi sine theta-function."},
+{"thetanullk",21,(void*)thetanullk,3,"GLp","thetanullk(q,k)=k'th derivative at z=0 of theta(q,z)."},
+{"threetotwo",0,(void*)suppressed,6,"GGGG","threetotwo(nf,a,b,c)=returns a 3-component vector [d,e,U] such that U is a unimodular 3x3 matrix with algebraic integer coefficients such that [a,b,c]*U=[0,d,e]."},
+{"threetotwo2",0,(void*)suppressed,6,"GGGG","threetotwo2(nf,a,b,c)=returns a 3-component vector [d,e,U] such that U is a unimodular 3x3 matrix with algebraic integer coefficients such that [a,b,c]*U=[0,d,e]."},
+{"torsell",1,(void*)elltors,5,"Gp","torsell(e)=torsion subgroup of elliptic curve e: order, structure, generators."},
+{"trace",1,(void*)gtrace,8,"G","trace(x)=trace of x."},
+{"trans",1,(void*)gtrans,8,"G","trans(x)=x~=transpose of x."},
+{"trunc",1,(void*)gtrunc,2,"G","trunc(x)=truncation of x;when x is a power series,take away the O(X^)."},
+{"tschirnhaus",1,(void*)tschirnhaus,6,"G","tschirnhaus(x)=random Tschirnhausen transformation of the polynomial x."},
+{"twototwo",0,(void*)suppressed,6,"GGG","twototwo(nf,a,b)=returns a 3-component vector [d,e,U] such that U is a unimodular 2x2 matrix with algebraic integer coefficients such that [a,b]*U=[d,e] and d,e are hopefully smaller."},
+{"unit",1,(void*)quadunit,4,"G","unit(x)=fundamental unit of the quadratic field of discriminant x where x must be positive."},
+{"until",0,(void*)untilpari,11,"vEI","until(a,seq): evaluate the expression sequence seq until a is nonzero."},
+{"valuation",20,(void*)gvaluation,2,"lGG","valuation(x,p)=valuation of x with respect to p."},
+{"vec",1,(void*)gtovec,2,"G","vec(x)=transforms the object x into a vector. Used mainly if x is a polynomial or a power series."},
+{"vecindexsort",2,(void*)indexsort,8,"G","vecindexsort(x): indirect sorting of the vector x."},
+{"veclexsort",2,(void*)lexsort,8,"G","veclexsort(x): sort the elements of the vector x in ascending lexicographic order."},
+{"vecmax",1,(void*)vecmax,2,"G","vecmax(x)=maximum of the elements of the vector/matrix x."},
+{"vecmin",1,(void*)vecmin,2,"G","vecmin(x)=minimum of the elements of the vector/matrix x."},
+{"vecsort",2,(void*)vecsort0,8,"GGD0,L,","vecsort(x,k)=sorts the vector of vector (or matrix) x according to the value of its k-th component."},
+{"vector",22,(void*)vecteur,9,"GVE","vector(n,X,expr)=row vector with n components of expression expr (X ranges from 1 to n)."},
+{"vvector",22,(void*)vvecteur,9,"GVE","vvector(n,X,expr)=column vector with n components of expression expr (X ranges from 1 to n)."},
+{"weipell",1,(void*)weipell,5,"GP","weipell(e)=formal expansion in x=z of Weierstrass P function."},
+{"wf",1,(void*)weberf,3,"Gp","weberf(x)=Weber's f function of x (j=(f^24-16)^3/f^24)."},
+{"wf2",1,(void*)weberf2,3,"Gp","weberf2(x)=Weber's f2 function of x (j=(f2^24+16)^3/f2^24)."},
+{"while",0,(void*)whilepari,11,"vEI","while(a,seq): while a is nonzero evaluate the expression sequence seq. Otherwise 0."},
+{"zell",2,(void*)zell,5,"GGp","zell(e,z)=In the complex case, lattice point corresponding to the point z on the elliptic curve e."},
+{"zeta",1,(void*)gzeta,3,"Gp","zeta(s)=Riemann zeta function at s."},
+{"zetak",2,(void*)gzetak,6,"GGp","zetak(nfz,s)=Dedekind zeta function of the number field nfz at s, where nfz is the vector computed by initzeta (NOT by nfinit)."},
+{"zideallog",3,(void*)ideallog,6,"GGG","zideallog(nf,x,bid)=if bid is a big ideal as given by zidealstarinit or zidealstarinitgen , gives the vector of exponents on the generators bid[2][3] (even if these generators have not been computed)."},
+{"zidealstar",2,(void*)idealstar0,6,"GGD0,L,","zidealstar(nf,I)=3-component vector v, giving the structure of (Z_K/I)^*. v[1] is  the order (i.e. phi(I)), v[2] is a vector of cyclic components, and v[3]  is a vector giving the corresponding generators."},
+{"zidealstarinit",2,(void*)idealstar0,6,"GGD1,L,","zidealstarinit(nf,I)=6-component vector [I,v,fa,f2,U,V] where v is as in zidealstar without the generators, fa is the prime ideal factorisation of I and f2, U and V are technical but essential to work in (Z_K/I)^*."},
+{"zidealstarinitgen",2,(void*)idealstar0,6,"GGD2,L,","zidealstarinitgen(nf,I)=6-component vector [I,v,fa,f2,U,V] where v is as in zidealstar fa is the prime ideal factorisation of I and f2, U and V are technical but essential to work in (Z_K/I)^*."},
+{"znstar",1,(void*)znstar,4,"G","znstar(n)=3-component vector v, giving the structure of (Z/nZ)^*. v[1] is  the order (i.e. phi(n)), v[2] is a vector of cyclic components, and v[3]  is a vector giving the corresponding generators."},
+
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
diff --git a/src/language/compile.c b/src/language/compile.c
new file mode 100644
index 0000000..19cc6fb
--- /dev/null
+++ b/src/language/compile.c
@@ -0,0 +1,2392 @@
+/* Copyright (C) 2006  The PARI group.
+
+This file is part of the PARI package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+#include "tree.h"
+#include "opcode.h"
+
+#define tree pari_tree
+
+enum COflags {COsafelex=1, COsafedyn=2};
+
+/***************************************************************************
+ **                                                                       **
+ **                           String constant expansion                   **
+ **                                                                       **
+ ***************************************************************************/
+
+static GEN
+strntoGENexp(const char *str, long len)
+{
+  GEN z = cgetg(1+nchar2nlong(len-1), t_STR);
+  char *s=GSTR(z);
+  const char *t=str+1;
+  while (t<=str+len)
+  {
+    while (*t == '\\')
+    {
+      switch(*++t)
+      {
+        case 'e':  *s='\033'; break; /* escape */
+        case 'n':  *s='\n'; break;
+        case 't':  *s='\t'; break;
+        default:   *s=*t; if (!*t) compile_err("unfinished string",str);
+      }
+      t++; s++;
+    }
+    if (*t == '"')
+    {
+      if (t[1] != '"') break;
+      t += 2; continue;
+    }
+    *s++ = *t++;
+  }
+  *s = '\0';
+  return z;
+}
+
+/***************************************************************************
+ **                                                                       **
+ **                           Byte-code compiler                          **
+ **                                                                       **
+ ***************************************************************************/
+
+typedef enum {Llocal, Lmy} Ltype;
+
+struct vars_s
+{
+  Ltype type; /*Only Llocal and Lmy are allowed */
+  int inl;
+  entree *ep;
+};
+
+struct frame_s
+{
+  long pc;
+  GEN frame;
+};
+
+static THREAD pari_stack s_opcode, s_operand, s_data, s_lvar;
+static THREAD pari_stack s_dbginfo, s_frame;
+static THREAD char *opcode;
+static THREAD long *operand;
+static THREAD GEN *data;
+static THREAD long offset;
+static THREAD struct vars_s *localvars;
+static THREAD const char **dbginfo, *dbgstart;
+static THREAD struct frame_s *frames;
+
+void
+pari_init_compiler(void)
+{
+  pari_stack_init(&s_opcode,sizeof(*opcode),(void **)&opcode);
+  pari_stack_init(&s_operand,sizeof(*operand),(void **)&operand);
+  pari_stack_init(&s_data,sizeof(*data),(void **)&data);
+  pari_stack_init(&s_lvar,sizeof(*localvars),(void **)&localvars);
+  pari_stack_init(&s_dbginfo,sizeof(*dbginfo),(void **)&dbginfo);
+  pari_stack_init(&s_frame,sizeof(*frames),(void **)&frames);
+  offset=-1;
+}
+void
+pari_close_compiler(void)
+{
+  pari_stack_delete(&s_opcode);
+  pari_stack_delete(&s_operand);
+  pari_stack_delete(&s_data);
+  pari_stack_delete(&s_lvar);
+}
+
+struct codepos
+{
+  long opcode, data, localvars, frames;
+  long offset;
+  const char *dbgstart;
+};
+
+static void
+getcodepos(struct codepos *pos)
+{
+  pos->opcode=s_opcode.n;
+  pos->data=s_data.n;
+  pos->offset=offset;
+  pos->localvars=s_lvar.n;
+  pos->dbgstart=dbgstart;
+  pos->frames=s_frame.n;
+  offset=s_data.n-1;
+}
+
+void
+compilestate_reset(void)
+{
+  s_opcode.n=0;
+  s_operand.n=0;
+  s_dbginfo.n=0;
+  s_data.n=0;
+  s_lvar.n=0;
+  s_frame.n=0;
+  offset=-1;
+  dbgstart=NULL;
+}
+
+void
+compilestate_save(struct pari_compilestate *comp)
+{
+  comp->opcode=s_opcode.n;
+  comp->operand=s_operand.n;
+  comp->data=s_data.n;
+  comp->offset=offset;
+  comp->localvars=s_lvar.n;
+  comp->dbgstart=dbgstart;
+  comp->dbginfo=s_dbginfo.n;
+  comp->frames=s_frame.n;
+}
+
+void
+compilestate_restore(struct pari_compilestate *comp)
+{
+  s_opcode.n=comp->opcode;
+  s_operand.n=comp->operand;
+  s_data.n=comp->data;
+  offset=comp->offset;
+  s_lvar.n=comp->localvars;
+  dbgstart=comp->dbgstart;
+  s_dbginfo.n=comp->dbginfo;
+  s_frame.n=comp->frames;
+}
+
+static GEN
+getfunction(const struct codepos *pos, long arity, long nbmvar, GEN text, long gap)
+{
+  long lop =s_opcode.n+1-pos->opcode;
+  long ldat=s_data.n+1-pos->data;
+  long lfram=s_frame.n+1-pos->frames;
+  GEN cl=cgetg(nbmvar?8:(text?7:6),t_CLOSURE);
+  GEN frpc, fram, dbg;
+  char *s;
+  long i;
+  cl[1] = arity;
+  gel(cl,2) = cgetg(nchar2nlong(lop)+1, t_STR);
+  gel(cl,3) = cgetg(lop,  t_VECSMALL);
+  gel(cl,4) = cgetg(ldat, t_VEC);
+  dbg = cgetg(lop,  t_VECSMALL);
+  frpc = cgetg(lfram,  t_VECSMALL);
+  fram = cgetg(lfram,  t_VEC);
+  gel(cl,5) = mkvec3(dbg, frpc, fram);
+  if (text) gel(cl,6) = text;
+  if (nbmvar) gel(cl,7) = zerovec(nbmvar);
+  s=GSTR(gel(cl,2))-1;
+  for(i=1;i<lop;i++)
+  {
+    s[i] = opcode[i+pos->opcode-1];
+    mael(cl, 3, i) = operand[i+pos->opcode-1];
+    dbg[i] = dbginfo[i+pos->opcode-1]-dbgstart;
+    if (dbg[i]<0) dbg[i]+=gap;
+  }
+  s[i]=0;
+  s_opcode.n=pos->opcode;
+  s_operand.n=pos->opcode;
+  s_dbginfo.n=pos->opcode;
+  for(i=1;i<ldat;i++)
+    if(data[i+pos->data-1])
+    {
+      gmael(cl, 4, i) = gcopy(data[i+pos->data-1]);
+      gunclone(data[i+pos->data-1]);
+    }
+  s_data.n=pos->data;
+  while (s_lvar.n>pos->localvars && !localvars[s_lvar.n-1].inl)
+    s_lvar.n--;
+  for(i=1;i<lfram;i++)
+  {
+    long j=i+pos->frames-1;
+    frpc[i] = frames[j].pc-pos->opcode+1;
+    gel(fram, i) = gcopy(frames[j].frame);
+    gunclone(frames[j].frame);
+  }
+  s_frame.n=pos->frames;
+  offset=pos->offset;
+  dbgstart=pos->dbgstart;
+  return cl;
+}
+
+static GEN
+getclosure(struct codepos *pos)
+{
+  return getfunction(pos,0,0,NULL,0);
+}
+
+static void
+op_push_loc(op_code o, long x, const char *loc)
+{
+  long n=pari_stack_new(&s_opcode);
+  long m=pari_stack_new(&s_operand);
+  long d=pari_stack_new(&s_dbginfo);
+  opcode[n]=o;
+  operand[m]=x;
+  dbginfo[d]=loc;
+}
+
+static void
+op_push(op_code o, long x, long n)
+{
+  op_push_loc(o,x,tree[n].str);
+}
+
+static void
+op_insert_loc(long k, op_code o, long x, const char *loc)
+{
+  long i;
+  long n=pari_stack_new(&s_opcode);
+  (void) pari_stack_new(&s_operand);
+  (void) pari_stack_new(&s_dbginfo);
+  for (i=n-1; i>=k; i--)
+  {
+    opcode[i+1] = opcode[i];
+    operand[i+1]= operand[i];
+    dbginfo[i+1]= dbginfo[i];
+  }
+  opcode[k]  = o;
+  operand[k] = x;
+  dbginfo[k] = loc;
+}
+
+static long
+data_push(GEN x)
+{
+  long n=pari_stack_new(&s_data);
+  data[n] = x?gclone(x):x;
+  return n-offset;
+}
+
+static void
+var_push(entree *ep, Ltype type)
+{
+  long n=pari_stack_new(&s_lvar);
+  localvars[n].ep   = ep;
+  localvars[n].inl  = 0;
+  localvars[n].type = type;
+}
+
+static void
+frame_push(GEN x)
+{
+  long n=pari_stack_new(&s_frame);
+  frames[n].pc = s_opcode.n-1;
+  frames[n].frame = gclone(x);
+}
+
+static GEN
+pack_localvars(void)
+{
+  GEN pack=cgetg(3,t_VEC);
+  long i,l=s_lvar.n;
+  GEN t=cgetg(1+l,t_VECSMALL);
+  GEN e=cgetg(1+l,t_VECSMALL);
+  gel(pack,1)=t;
+  gel(pack,2)=e;
+  for(i=1;i<=l;i++)
+  {
+    t[i]=localvars[i-1].type;
+    e[i]=(long)localvars[i-1].ep;
+  }
+  return pack;
+}
+
+void
+push_frame(GEN C, long lpc, long dummy)
+{
+  const char *code=closure_codestr(C);
+  GEN oper=closure_get_oper(C);
+  GEN dbg=closure_get_dbg(C);
+  GEN frpc=gel(dbg,2);
+  GEN fram=gel(dbg,3);
+  long pc, j=1, lfr = lg(frpc);
+  if (lpc==-1)
+  {
+    long k;
+    GEN e = gel(fram, 1);
+    for(k=1; k<lg(e); k++)
+      var_push(dummy?NULL:(entree*)e[k], Lmy);
+    return;
+  }
+  if (lg(C)<8) while (j<lfr && frpc[j]==0) j++;
+  for(pc=0; pc<=lpc; pc++)
+  {
+    if (pc>0 && (code[pc]==OClocalvar || code[pc]==OClocalvar0))
+      var_push((entree*)oper[pc],Llocal);
+    if (j<lfr && pc==frpc[j])
+    {
+      long k;
+      GEN e = gel(fram,j);
+      for(k=1; k<lg(e); k++)
+        var_push(dummy?NULL:(entree*)e[k], Lmy);
+      j++;
+    }
+  }
+}
+
+void
+debug_context(void)
+{
+  long i;
+  for(i=0;i<s_lvar.n;i++)
+  {
+    entree *ep = localvars[i].ep;
+    Ltype type = localvars[i].type;
+    err_printf("%ld: %s: %s\n",i,(type==Lmy?"my":"local"),(ep?ep->name:"NULL"));
+  }
+}
+
+GEN
+localvars_read_str(const char *x, GEN pack)
+{
+  GEN code;
+  long l=0;
+  if (pack)
+  {
+    GEN t=gel(pack,1);
+    GEN e=gel(pack,2);
+    long i;
+    l=lg(t)-1;
+    for(i=1;i<=l;i++)
+      var_push((entree*)e[i],(Ltype)t[i]);
+  }
+  code = compile_str(x);
+  s_lvar.n -= l;
+  return closure_evalres(code);
+}
+
+long
+localvars_find(GEN pack, entree *ep)
+{
+  GEN t=gel(pack,1);
+  GEN e=gel(pack,2);
+  long i;
+  long vn=0;
+  for(i=lg(e)-1;i>=1;i--)
+  {
+    if(t[i]==Lmy)
+      vn--;
+    if(e[i]==(long)ep)
+      return t[i]==Lmy?vn:0;
+  }
+  return 0;
+}
+
+/*
+ Flags for copy optimisation:
+ -- Freturn: The result will be returned.
+ -- FLsurvive: The result must survive the closure.
+ -- FLnocopy: The result will never be updated nor part of a user variable.
+ -- FLnocopylex: The result will never be updated nor part of dynamic variable.
+*/
+enum FLflag {FLreturn=1, FLsurvive=2, FLnocopy=4, FLnocopylex=8};
+
+static void
+copyifclone(long n, long mode, long flag, long mask)
+{
+  if (mode==Ggen && !(flag&mask))
+  {
+    op_push(OCcopyifclone,0,n);
+    if (!(flag&FLsurvive) && DEBUGLEVEL)
+      pari_warn(warner,"compiler generates copy for `%.*s'",
+                       tree[n].len,tree[n].str);
+  }
+}
+
+static void compilenode(long n, int mode, long flag);
+
+typedef enum {PPend,PPstd,PPdefault,PPdefaultmulti,PPstar,PPauto} PPproto;
+
+static PPproto
+parseproto(char const **q, char *c, const char *str)
+{
+  char  const *p=*q;
+  long i;
+  switch(*p)
+  {
+  case 0:
+  case '\n':
+    return PPend;
+  case 'D':
+    switch(p[1])
+    {
+    case 0:
+      compile_err("function has incomplete prototype",str);
+    case 'G':
+    case '&':
+    case 'W':
+    case 'V':
+    case 'I':
+    case 'E':
+    case 'J':
+    case 'n':
+    case 'P':
+    case 'r':
+    case 's':
+      *c=p[1];
+      *q=p+2;
+      return PPdefault;
+    default:
+      for(i=0;*p && i<2;p++) i+=*p==',';
+      if (i<2)
+        compile_err("function has incomplete prototype",str);
+      *c=p[-2];
+      *q=p;
+      return PPdefaultmulti;
+    }
+    break;
+  case 'C':
+  case 'p':
+  case 'P':
+  case 'f':
+    *c=*p;
+    *q=p+1;
+    return PPauto;
+  case '&':
+    *c='*';
+    *q=p+1;
+    return PPstd;
+  case 'V':
+    if (p[1]=='=')
+    {
+      if (p[2]!='G')
+        compile_err("function prototype is not supported",str);
+      *c='=';
+      p+=2;
+    }
+    else
+      *c=*p;
+    *q=p+1;
+    return PPstd;
+  case 'E':
+  case 's':
+    if (p[1]=='*')
+    {
+      *c=*p++;
+      *q=p+1;
+      return PPstar;
+    }
+    /*fall through*/
+  }
+  *c=*p;
+  *q=p+1;
+  return PPstd;
+}
+
+static long
+detag(long n)
+{
+  while (tree[n].f==Ftag)
+    n=tree[n].x;
+  return n;
+}
+
+/* return type for GP functions */
+static op_code
+get_ret_type(const char **p, long arity, Gtype *t, long *flag)
+{
+  *flag = 0;
+  if (**p == 'v') { (*p)++; *t=Gvoid; return OCcallvoid; }
+  else if (**p == 'i') { (*p)++; *t=Gsmall; return OCcallint; }
+  else if (**p == 'l') { (*p)++; *t=Gsmall; return OCcalllong; }
+  else if (**p == 'm') { (*p)++; *flag = FLnocopy; }
+  *t=Ggen; return arity==2?OCcallgen2:OCcallgen;
+}
+
+/*supported types:
+ * type: Gsmall, Ggen, Gvoid, Gvec, Gclosure
+ * mode: Gsmall, Ggen, Gvar, Gvoid
+ */
+static void
+compilecast_loc(int type, int mode, const char *loc)
+{
+  if (type==mode) return;
+  switch (mode)
+  {
+  case Gsmall:
+    if (type==Ggen)        op_push_loc(OCitos,-1,loc);
+    else if (type==Gvoid)  op_push_loc(OCpushlong,0,loc);
+    else compile_err("this should be a small integer",loc);
+    break;
+  case Ggen:
+    if (type==Gsmall)      op_push_loc(OCstoi,0,loc);
+    else if (type==Gvoid)  op_push_loc(OCpushgnil,0,loc);
+    break;
+  case Gvoid:
+    op_push_loc(OCpop, 1,loc);
+    break;
+  case Gvar:
+    if (type==Ggen)        op_push_loc(OCvarn,-1,loc);
+    else compile_varerr(loc);
+     break;
+  default:
+    pari_err_BUG("compilecast [unknown type]");
+  }
+}
+
+static void
+compilecast(long n, int type, int mode) { compilecast_loc(type, mode, tree[n].str); }
+
+static entree *
+getfunc(long n)
+{
+  long x=tree[n].x;
+  if (tree[x].x==CSTmember)
+    return do_alias(fetch_member(tree[x].str, tree[x].len));
+  else
+    return do_alias(fetch_entry(tree[x].str, tree[x].len));
+}
+
+static entree *
+getentry(long n)
+{
+  n = detag(n);
+  if (tree[n].f!=Fentry)
+  {
+    if (tree[n].f==Fseq)
+      compile_err("unexpected character: ';'", tree[tree[n].y].str-1);
+    compile_varerr(tree[n].str);
+  }
+  return getfunc(n);
+}
+
+/* match Fentry that are not actually EpSTATIC functions called without parens*/
+static entree *
+getvar(long n)
+{
+  entree *ep = getentry(n);
+  if (EpSTATIC(do_alias(ep)))
+    compile_varerr(tree[n].str);
+  return ep;
+}
+
+static long
+getmvar(entree *ep)
+{
+  long i;
+  long vn=0;
+  for(i=s_lvar.n-1;i>=0;i--)
+  {
+    if(localvars[i].type==Lmy)
+      vn--;
+    if(localvars[i].ep==ep)
+      return localvars[i].type==Lmy?vn:0;
+  }
+  return 0;
+}
+
+static long
+ctxmvar(void)
+{
+  pari_sp av=avma;
+  long i, n=0;
+  GEN ctx;
+  for(i=s_lvar.n-1;i>=0;i--)
+    if(localvars[i].type==Lmy)
+      n++;
+  if (n==0) return 0;
+  ctx = cgetg(n+1,t_VECSMALL);
+  for(n=0, i=0; i<s_lvar.n; i++)
+    if(localvars[i].type==Lmy)
+      ctx[++n]=(long)localvars[i].ep;
+  frame_push(ctx);
+  avma=av; return n;
+}
+
+INLINE int
+is_func_named(entree *ep, const char *s)
+{
+  return !strcmp(ep->name, s);
+}
+
+INLINE int
+is_node_zero(long n)
+{
+  n = detag(n);
+  return (tree[n].f==Fsmall && tree[n].x==0);
+}
+
+static void
+str_defproto(const char *p, const char *q, const char *loc)
+{
+  long len = p-4-q;
+  if (q[1]!='"' || q[len]!='"')
+    compile_err("default argument must be a string",loc);
+  op_push_loc(OCpushgen,data_push(strntoGENexp(q+1,len)),loc);
+}
+
+static long
+countlisttogen(long n, Ffunc f)
+{
+  long x,i;
+  if (n==-1 || tree[n].f==Fnoarg) return 0;
+  for(x=n, i=0; tree[x].f==f ;x=tree[x].x, i++);
+  return i+1;
+}
+
+static GEN
+listtogen(long n, Ffunc f)
+{
+  long x,i,nb = countlisttogen(n, f);
+  GEN z=cgetg(nb+1, t_VECSMALL);
+  if (nb)
+  {
+    for (x=n, i = nb-1; i>0; z[i+1]=tree[x].y, x=tree[x].x, i--);
+    z[1]=x;
+  }
+  return z;
+}
+
+static long
+first_safe_arg(GEN arg, long mask)
+{
+  long lnc, l=lg(arg);
+  for (lnc=l-1; lnc>0 && (tree[arg[lnc]].flags&mask)==mask; lnc--);
+  return lnc;
+}
+
+static void
+checkdups(GEN arg, GEN vep)
+{
+  long l=vecsmall_duplicate(vep);
+  if (l!=0) compile_err("variable declared twice",tree[arg[l]].str);
+}
+
+enum {MAT_range,MAT_std,MAT_line,MAT_column,VEC_std};
+
+static int
+matindex_type(long n)
+{
+  long x = tree[n].x, y = tree[n].y;
+  long fxx = tree[tree[x].x].f, fxy = tree[tree[x].y].f;
+  if (y==-1)
+  {
+    if (fxy!=Fnoarg) return MAT_range;
+    if (fxx==Fnoarg) compile_err("missing index",tree[n].str);
+    return VEC_std;
+  }
+  else
+  {
+    long fyx = tree[tree[y].x].f, fyy = tree[tree[y].y].f;
+    if (fxy!=Fnoarg || fyy!=Fnoarg) return MAT_range;
+    if (fxx==Fnoarg && fyx==Fnoarg) compile_err("missing index",tree[n].str);
+    if (fxx==Fnoarg) return MAT_column;
+    if (fyx==Fnoarg) return MAT_line;
+    return MAT_std;
+  }
+}
+
+static entree *
+getlvalue(long n)
+{
+  while ((tree[n].f==Fmatcoeff && matindex_type(tree[n].y)!=MAT_range) || tree[n].f==Ftag)
+    n=tree[n].x;
+  return getvar(n);
+}
+
+INLINE void
+compilestore(long vn, entree *ep, long n)
+{
+  if (vn)
+    op_push(OCstorelex,vn,n);
+  else
+    op_push(OCstoredyn,(long)ep,n);
+}
+
+INLINE void
+compilenewptr(long vn, entree *ep, long n)
+{
+  if (vn)
+    op_push(OCnewptrlex,vn,n);
+  else
+    op_push(OCnewptrdyn,(long)ep,n);
+}
+
+static void
+compilelvalue(long n)
+{
+  n = detag(n);
+  if (tree[n].f==Fentry)
+    return;
+  else
+  {
+    long x = tree[n].x, y = tree[n].y;
+    long yx = tree[y].x, yy = tree[y].y;
+    long m = matindex_type(y);
+    if (m == MAT_range)
+      compile_err("not an lvalue",tree[n].str);
+    if (m == VEC_std && tree[x].f==Fmatcoeff)
+    {
+      int mx = matindex_type(tree[x].y);
+      if (mx==MAT_line)
+      {
+        int xy = tree[x].y, xyx = tree[xy].x;
+        compilelvalue(tree[x].x);
+        compilenode(tree[xyx].x,Gsmall,0);
+        compilenode(tree[yx].x,Gsmall,0);
+        op_push(OCcompo2ptr,0,y);
+        return;
+      }
+    }
+    compilelvalue(x);
+    switch(m)
+    {
+    case VEC_std:
+      compilenode(tree[yx].x,Gsmall,0);
+      op_push(OCcompo1ptr,0,y);
+      break;
+    case MAT_std:
+      compilenode(tree[yx].x,Gsmall,0);
+      compilenode(tree[yy].x,Gsmall,0);
+      op_push(OCcompo2ptr,0,y);
+      break;
+    case MAT_line:
+      compilenode(tree[yx].x,Gsmall,0);
+      op_push(OCcompoLptr,0,y);
+      break;
+    case MAT_column:
+      compilenode(tree[yy].x,Gsmall,0);
+      op_push(OCcompoCptr,0,y);
+      break;
+    }
+  }
+}
+
+static void
+compilematcoeff(long n, int mode)
+{
+  long x=tree[n].x, y=tree[n].y;
+  long yx=tree[y].x, yy=tree[y].y;
+  long m=matindex_type(y);
+  compilenode(x,Ggen,FLnocopy);
+  switch(m)
+  {
+  case VEC_std:
+    compilenode(tree[yx].x,Gsmall,0);
+    op_push(OCcompo1,mode,y);
+    return;
+  case MAT_std:
+    compilenode(tree[yx].x,Gsmall,0);
+    compilenode(tree[yy].x,Gsmall,0);
+    op_push(OCcompo2,mode,y);
+    return;
+  case MAT_line:
+    compilenode(tree[yx].x,Gsmall,0);
+    op_push(OCcompoL,0,y);
+    compilecast(n,Gvec,mode);
+    return;
+  case MAT_column:
+    compilenode(tree[yy].x,Gsmall,0);
+    op_push(OCcompoC,0,y);
+    compilecast(n,Gvec,mode);
+    return;
+  case MAT_range:
+    compilenode(tree[yx].x,Gsmall,0);
+    compilenode(tree[yx].y,Gsmall,0);
+    if (yy==-1)
+      op_push(OCcallgen,(long)is_entry("_[_.._]"),n);
+    else
+    {
+      compilenode(tree[yy].x,Gsmall,0);
+      compilenode(tree[yy].y,Gsmall,0);
+      op_push(OCcallgen,(long)is_entry("_[_.._,_.._]"),n);
+    }
+    return;
+  default:
+    pari_err_BUG("compilematcoeff");
+  }
+}
+
+static void
+compilesmall(long n, long x, long mode)
+{
+  if (mode==Ggen)
+    op_push(OCpushstoi, x, n);
+  else
+  {
+    op_push(OCpushlong, x, n);
+    compilecast(n,Gsmall,mode);
+  }
+}
+
+static void
+compilevec(long n, long mode, op_code op)
+{
+  pari_sp ltop=avma;
+  long x=tree[n].x;
+  long i;
+  GEN arg=listtogen(x,Fmatrixelts);
+  long l=lg(arg);
+  op_push(op,l,n);
+  for (i=1;i<l;i++)
+  {
+    compilenode(arg[i],Ggen,FLsurvive);
+    op_push(OCstackgen,i,n);
+  }
+  avma=ltop;
+  op_push(OCpop,1,n);
+  compilecast(n,Gvec,mode);
+}
+
+static void
+compilemat(long n, long mode)
+{
+  pari_sp ltop=avma;
+  long x=tree[n].x;
+  long i,j;
+  GEN line=listtogen(x,Fmatrixlines);
+  long lglin = lg(line), lgcol=0;
+  op_push(OCpushlong, lglin,n);
+  if (lglin==1)
+    op_push(OCmat,1,n);
+  for(i=1;i<lglin;i++)
+  {
+    GEN col=listtogen(line[i],Fmatrixelts);
+    long l=lg(col), k;
+    if (i==1)
+    {
+      lgcol=l;
+      op_push(OCmat,lgcol,n);
+    }
+    else if (l!=lgcol)
+      compile_err("matrix must be rectangular",tree[line[i]].str);
+    k=i;
+    for(j=1;j<lgcol;j++)
+    {
+      k-=lglin;
+      compilenode(col[j], Ggen, FLsurvive);
+      op_push(OCstackgen,k,n);
+    }
+  }
+  avma=ltop;
+  op_push(OCpop,1,n);
+  compilecast(n,Gvec,mode);
+}
+
+
+static GEN
+cattovec(long n, long fnum)
+{
+  long x=n, y, i=0, nb;
+  GEN stack;
+  if (tree[n].f==Fnoarg) return cgetg(1,t_VECSMALL);
+  while(1)
+  {
+    long xx=tree[x].x;
+    long xy=tree[x].y;
+    if (tree[x].f!=Ffunction || xx!=fnum) break;
+    x=tree[xy].x;
+    y=tree[xy].y;
+    if (tree[y].f==Fnoarg)
+      compile_err("unexpected character: ", tree[y].str);
+    i++;
+  }
+  if (tree[x].f==Fnoarg)
+    compile_err("unexpected character: ", tree[x].str);
+  nb=i+1;
+  stack=cgetg(nb+1,t_VECSMALL);
+  for(x=n;i>0;i--)
+  {
+    long y=tree[x].y;
+    x=tree[y].x;
+    stack[i+1]=tree[y].y;
+  }
+  stack[1]=x;
+  return stack;
+}
+
+static GEN
+compilelambda(long n, long y, GEN vep, struct codepos *pos)
+{
+  long nbmvar, lev = vep ? lg(vep)-1 : 0;
+  GEN text=cgetg(3,t_VEC);
+  gel(text,1)=strtoGENstr(lev? ((entree*) vep[1])->name: "");
+  gel(text,2)=strntoGENstr(tree[y].str,tree[y].len);
+  nbmvar=ctxmvar()-lev;
+  if (lev) op_push(OCgetargs,lev,n);
+  compilenode(y,Ggen,FLsurvive|FLreturn);
+  return getfunction(pos,lev,nbmvar,text,2);
+}
+
+static void
+compilecall(long n, int mode)
+{
+  pari_sp ltop=avma;
+  long j;
+  long x=tree[n].x;
+  long y=tree[n].y;
+  GEN arg=listtogen(y,Flistarg);
+  long nb=lg(arg)-1;
+  long lnc=first_safe_arg(arg, COsafelex);
+  for (j=1;j<=nb;j++)
+  {
+    long x = tree[arg[j]].x, f = tree[arg[j]].f;
+    if (f==Fseq)
+      compile_err("unexpected ';'", tree[x].str+tree[x].len);
+    else if (f!=Fnoarg)
+      compilenode(arg[j], Ggen,j>=lnc?FLnocopylex:0);
+    else
+      op_push(OCpushlong,0,n);
+  }
+  op_push(OCcalluser,nb,x);
+  compilecast(n,Ggen,mode);
+  avma=ltop;
+}
+
+static void
+compileuserfunc(entree *ep, long n, int mode)
+{
+  long vn=getmvar(ep);
+  if (tree[n].x<OPnboperator) compile_err("operator unknown",tree[n].str);
+  if (vn)
+    op_push(OCpushlex,vn,n);
+  else
+    op_push(OCpushdyn,(long)ep,n);
+  compilecall(n, mode);
+}
+
+static GEN
+compilefuncinline(long n, long c, long a, long flag, long isif, long lev, long *ev)
+{
+  struct codepos pos;
+  int type=c=='I'?Gvoid:Ggen;
+  long rflag=c=='I'?0:FLsurvive;
+  GEN vep = NULL;
+  if (isif && (flag&FLreturn)) rflag|=FLreturn;
+  getcodepos(&pos);
+  if (lev)
+  {
+    long i;
+    GEN varg=cgetg(lev+1,t_VECSMALL);
+    vep=cgetg(lev+1,t_VECSMALL);
+    for(i=0;i<lev;i++)
+    {
+      entree *ve;
+      if (ev[i]<0)
+        compile_err("missing variable name", tree[a].str-1);
+      ve = getvar(ev[i]);
+      vep[i+1]=(long)ve;
+      varg[i+1]=ev[i];
+      var_push(ve,Lmy);
+    }
+    checkdups(varg,vep);
+    frame_push(vep);
+  }
+  if (c=='J')
+    return compilelambda(n,a,vep,&pos);
+  else if (tree[a].f==Fnoarg)
+    compilecast(a,Gvoid,type);
+  else
+    compilenode(a,type,rflag);
+  return getclosure(&pos);
+}
+
+static long
+countvar(GEN arg)
+{
+  long i, l = lg(arg);
+  long n = l-1;
+  for(i=1; i<l; i++)
+  {
+    long a=arg[i];
+    if (tree[a].f==Fassign)
+    {
+      long x = detag(tree[a].x);
+      if (tree[x].f==Fvec && tree[x].x>=0)
+        n += countlisttogen(tree[x].x,Fmatrixelts)-1;
+    }
+  }
+  return n;
+}
+
+static void
+compileuninline(GEN arg)
+{
+  long j;
+  if (lg(arg) > 1)
+    compile_err("too many arguments",tree[arg[1]].str);
+  for(j=0; j<s_lvar.n; j++)
+    if(!localvars[j].inl)
+      pari_err(e_MISC,"uninline is only valid at top level");
+  s_lvar.n = 0;
+}
+
+static void
+compilemy(GEN arg, const char *str, int inl)
+{
+  long i, j, k, l = lg(arg);
+  long n = countvar(arg);
+  GEN vep = cgetg(n+1,t_VECSMALL);
+  GEN ver = cgetg(n+1,t_VECSMALL);
+  if (inl)
+  {
+    for(j=0; j<s_lvar.n; j++)
+      if(!localvars[j].inl)
+        pari_err(e_MISC,"inline is only valid at top level");
+  }
+  for(k=0, i=1; i<l; i++)
+  {
+    long a=arg[i];
+    if (tree[a].f==Fassign)
+    {
+      long x = detag(tree[a].x);
+      if (tree[x].f==Fvec && tree[x].x>=0)
+      {
+        GEN vars = listtogen(tree[x].x,Fmatrixelts);
+        long nv = lg(vars)-1;
+        for (j=1; j<=nv; j++)
+        {
+          ver[++k] = vars[j];
+          vep[k] = (long)getvar(ver[k]);
+        }
+        continue;
+      } else ver[++k] = x;
+    } else ver[++k] = a;
+    vep[k] = (long)getvar(ver[k]);
+  }
+  checkdups(ver,vep);
+  for(i=1; i<=n; i++) var_push(NULL,Lmy);
+  op_push_loc(OCnewframe,inl?-n:n,str);
+  frame_push(vep);
+  for (k=0, i=1; i<l; i++)
+  {
+    long a=arg[i];
+    if (tree[a].f==Fassign)
+    {
+      long x = detag(tree[a].x);
+      if (tree[x].f==Fvec && tree[x].x>=0)
+      {
+        GEN vars = listtogen(tree[x].x,Fmatrixelts);
+        long nv = lg(vars)-1;
+        compilenode(tree[a].y,Ggen,FLnocopy);
+        op_push(OCdup,nv-1,x);
+        for (j=1; j<=nv; j++)
+        {
+          long v = detag(vars[j]);
+          op_push(OCpushlong,j,v);
+          op_push(OCcompo1,Ggen,v);
+          k++;
+          op_push(OCstorelex,-n+k-1,a);
+          localvars[s_lvar.n-n+k-1].ep=(entree*)vep[k];
+          localvars[s_lvar.n-n+k-1].inl=inl;
+        }
+        continue;
+      }
+      else if (!is_node_zero(tree[a].y))
+      {
+        compilenode(tree[a].y,Ggen,FLnocopy);
+        op_push(OCstorelex,-n+k,a);
+      }
+    }
+    k++;
+    localvars[s_lvar.n-n+k-1].ep=(entree*)vep[k];
+    localvars[s_lvar.n-n+k-1].inl=inl;
+  }
+}
+
+static long
+localpush(op_code op, long a)
+{
+  entree *ep = getvar(a);
+  long vep  = (long) ep;
+  op_push(op,vep,a);
+  var_push(ep,Llocal);
+  return vep;
+}
+
+static void
+compilelocal(GEN arg)
+{
+  long i, j, k, l = lg(arg);
+  long n = countvar(arg);
+  GEN vep = cgetg(n+1,t_VECSMALL);
+  GEN ver = cgetg(n+1,t_VECSMALL);
+  for(k=0, i=1; i<l; i++)
+  {
+    long a=arg[i];
+    if (tree[a].f==Fassign)
+    {
+      long x = detag(tree[a].x);
+      if (tree[x].f==Fvec && tree[x].x>=0)
+      {
+        GEN vars = listtogen(tree[x].x,Fmatrixelts);
+        long nv = lg(vars)-1;
+        compilenode(tree[a].y,Ggen,FLnocopy);
+        op_push(OCdup,nv-1,x);
+        for (j=1; j<=nv; j++)
+        {
+          long v = detag(vars[j]);
+          op_push(OCpushlong,j,v);
+          op_push(OCcompo1,Ggen,v);
+          vep[++k] = localpush(OClocalvar, v);
+          ver[k] = v;
+        }
+        continue;
+      } else if (!is_node_zero(tree[a].y))
+      {
+        compilenode(tree[a].y,Ggen,FLnocopy);
+        ver[++k] = x;
+        vep[k] = localpush(OClocalvar, ver[k]);
+        continue;
+      }
+      else
+        ver[++k] = x;
+    } else
+      ver[++k] = a;
+    vep[k] = localpush(OClocalvar0, ver[k]);
+  }
+  checkdups(ver,vep);
+}
+
+static void
+compilefunc(entree *ep, long n, int mode, long flag)
+{
+  pari_sp ltop=avma;
+  long j;
+  long x=tree[n].x, y=tree[n].y;
+  op_code ret_op;
+  long ret_flag;
+  Gtype ret_typ;
+  char const *p,*q;
+  char c;
+  const char *flags = NULL;
+  const char *str;
+  PPproto mod;
+  GEN arg=listtogen(y,Flistarg);
+  long lnc=first_safe_arg(arg, COsafelex|COsafedyn);
+  long lnl=first_safe_arg(arg, COsafelex);
+  long nbpointers=0, nbopcodes;
+  long nb=lg(arg)-1, lev=0;
+  long ev[20];
+  if (x>=OPnboperator)
+    str=tree[x].str;
+  else
+  {
+    if (nb==2)
+      str=tree[arg[1]].str+tree[arg[1]].len;
+    else if (nb==1)
+      str=tree[arg[1]].str;
+    else
+      str=tree[n].str;
+    while(*str==')') str++;
+  }
+  if (tree[n].f==Fassign)
+  {
+    nb=2; lnc=2; lnl=2; arg=mkvecsmall2(x,y);
+  }
+  else if (is_func_named(ep,"if"))
+  {
+    if (nb>=4)
+      ep=is_entry("_multi_if");
+    else if (mode==Gvoid)
+      ep=is_entry("_void_if");
+  }
+  else if (is_func_named(ep,"return") && (flag&FLreturn) && nb<=1)
+  {
+    if (nb==0) op_push(OCpushgnil,0,n);
+    else compilenode(arg[1],Ggen,FLsurvive|FLreturn);
+    avma=ltop;
+    return;
+  }
+  else if (is_func_named(ep,"inline"))
+  {
+    compilemy(arg, str, 1);
+    compilecast(n,Gvoid,mode);
+    avma=ltop;
+    return;
+  }
+  else if (is_func_named(ep,"uninline"))
+  {
+    compileuninline(arg);
+    compilecast(n,Gvoid,mode);
+    avma=ltop;
+    return;
+  }
+  else if (is_func_named(ep,"my"))
+  {
+    compilemy(arg, str, 0);
+    compilecast(n,Gvoid,mode);
+    avma=ltop;
+    return;
+  }
+  else if (is_func_named(ep,"local"))
+  {
+    compilelocal(arg);
+    compilecast(n,Gvoid,mode);
+    avma=ltop;
+    return;
+  }
+  /*We generate dummy code for global() for compatibility with gp2c*/
+  else if (is_func_named(ep,"global"))
+  {
+    long i;
+    for (i=1;i<=nb;i++)
+    {
+      long a=arg[i];
+      long en;
+      if (tree[a].f==Fassign)
+      {
+        compilenode(tree[a].y,Ggen,0);
+        a=tree[a].x;
+        en=(long)getvar(a);
+        op_push(OCstoredyn,en,a);
+      }
+      else
+      {
+        en=(long)getvar(a);
+        op_push(OCpushdyn,en,a);
+        op_push(OCpop,1,a);
+      }
+    }
+    compilecast(n,Gvoid,mode);
+    avma=ltop;
+    return;
+  }
+  else if (is_func_named(ep,"O") || (compatible==OLDALL && is_func_named(ep,"o")))
+  {
+    if (nb!=1)
+      compile_err("wrong number of arguments", tree[n].str+tree[n].len-1);
+    ep=is_entry("O(_^_)");
+    if (tree[arg[1]].f==Ffunction && tree[arg[1]].x==OPpow)
+    {
+      arg = listtogen(tree[arg[1]].y,Flistarg);
+      nb  = lg(arg)-1;
+      lnc = first_safe_arg(arg,COsafelex|COsafedyn);
+      lnl = first_safe_arg(arg,COsafelex);
+    }
+  }
+  else if (x==OPn && tree[y].f==Fsmall)
+  {
+    avma=ltop;
+    compilesmall(y, -tree[y].x, mode);
+    return;
+  }
+  else if (x==OPtrans && tree[y].f==Fvec)
+  {
+    avma=ltop;
+    compilevec(y, mode, OCcol);
+    return;
+  }
+  else if (x==OPpow && nb==2 && tree[arg[2]].f==Fsmall)
+    ep=is_entry("_^s");
+  else if (x==OPcat)
+    compile_err("expected character: ',' or ')' instead of",
+        tree[arg[1]].str+tree[arg[1]].len);
+  p=ep->code;
+  if (!ep->value)
+    compile_err("unknown function",tree[n].str);
+  nbopcodes = s_opcode.n;
+  ret_op = get_ret_type(&p, ep->arity, &ret_typ, &ret_flag);
+  j=1;
+  if (*p)
+  {
+    q=p;
+    while((mod=parseproto(&p,&c,tree[n].str))!=PPend)
+    {
+      if (j<=nb && tree[arg[j]].f!=Fnoarg
+          && (mod==PPdefault || mod==PPdefaultmulti))
+        mod=PPstd;
+      switch(mod)
+      {
+      case PPstd:
+        if (j>nb) compile_err("too few arguments", tree[n].str+tree[n].len-1);
+        if (c!='I' && c!='E' && c!='J')
+        {
+          long x = tree[arg[j]].x, f = tree[arg[j]].f;
+          if (f==Fnoarg)
+            compile_err("missing mandatory argument", tree[arg[j]].str);
+          if (f==Fseq)
+            compile_err("unexpected ';'", tree[x].str+tree[x].len);
+        }
+        switch(c)
+        {
+        case 'G':
+          compilenode(arg[j],Ggen,j>=lnl?(j>=lnc?FLnocopy:FLnocopylex):0);
+          j++;
+          break;
+        case 'W':
+          {
+            entree *ep = getlvalue(arg[j]);
+            long vn = getmvar(ep);
+            if (vn) op_push(OCcowvarlex,vn,n);
+            else op_push(OCcowvardyn,(long)ep,n);
+            compilenode(arg[j++],Ggen,FLnocopy);
+            break;
+          }
+        case 'M':
+          if (tree[arg[j]].f!=Fsmall)
+          {
+            if (!flags) flags = ep->code;
+            flags = strchr(flags, '\n'); /* Skip to the following '\n' */
+            if (!flags)
+              compile_err("missing flag in string function signature",
+                           tree[n].str);
+            flags++;
+            if (tree[arg[j]].f==Fconst && tree[arg[j]].x==CSTstr)
+            {
+              GEN str=strntoGENexp(tree[arg[j]].str,tree[arg[j]].len);
+              op_push(OCpushlong, eval_mnemonic(str, flags),n);
+              j++;
+            } else
+            {
+              compilenode(arg[j++],Ggen,0);
+              op_push(OCpushlong,(long)flags,n);
+              op_push(OCcallgen2,(long)is_entry("_eval_mnemonic"),n);
+            }
+            break;
+          }
+        case 'P': case 'L': /*Fall through*/
+          compilenode(arg[j++],Gsmall,0);
+          break;
+        case 'n':
+          compilenode(arg[j++],Gvar,0);
+          break;
+        case '&': case '*':
+          {
+            long vn, a=arg[j++];
+            entree *ep;
+            if (c=='&')
+            {
+              if (tree[a].f!=Frefarg)
+                compile_err("expected character: '&'", tree[a].str);
+              a=tree[a].x;
+            }
+            a=detag(a);
+            ep=getlvalue(a);
+            vn=getmvar(ep);
+            if (tree[a].f==Fentry)
+            {
+              if (vn)
+                op_push(OCsimpleptrlex, vn,n);
+              else
+                op_push(OCsimpleptrdyn, (long)ep,n);
+            }
+            else
+            {
+              compilenewptr(vn,ep,n);
+              compilelvalue(a);
+              op_push(OCpushptr, 0,n);
+            }
+            nbpointers++;
+            break;
+          }
+        case 'I':
+        case 'E':
+        case 'J':
+          {
+            long a = arg[j++];
+            GEN  d = compilefuncinline(n, c, a, flag, is_func_named(ep,"if"), lev, ev);
+            op_push(OCpushgen, data_push(d), a);
+            if (lg(d)==8) op_push(OCsaveframe,FLsurvive,n);
+            break;
+          }
+        case 'V':
+          {
+            long a = arg[j++];
+            (void)getvar(a);
+            ev[lev++] = a;
+            break;
+          }
+        case '=':
+          {
+            long a = arg[j++];
+            ev[lev++] = tree[a].x;
+            compilenode(tree[a].y, Ggen, FLnocopy);
+          }
+          break;
+        case 'r':
+          {
+            long a=arg[j++];
+            if (tree[a].f==Fentry)
+            {
+              op_push(OCpushgen, data_push(strntoGENstr(tree[tree[a].x].str,
+                                                        tree[tree[a].x].len)),n);
+              op_push(OCtostr, -1,n);
+            }
+            else
+            {
+              compilenode(a,Ggen,FLnocopy);
+              op_push(OCtostr, -1,n);
+            }
+            break;
+          }
+        case 's':
+          {
+            long a = arg[j++];
+            GEN g = cattovec(a, OPcat);
+            long l, nb = lg(g)-1;
+            if (nb==1)
+            {
+              compilenode(g[1], Ggen, FLnocopy);
+              op_push(OCtostr, -1, a);
+            } else
+            {
+              op_push(OCvec, nb+1, a);
+              for(l=1; l<=nb; l++)
+              {
+                compilenode(g[l], Ggen, FLsurvive);
+                op_push(OCstackgen,l, a);
+              }
+              op_push(OCpop, 1, a);
+              op_push(OCcallgen,(long)is_entry("Str"), a);
+              op_push(OCtostr, -1, a);
+            }
+            break;
+          }
+        default:
+          pari_err(e_MISC,"Unknown prototype code `%c' for `%.*s'",c,
+              tree[x].len, tree[x].str);
+        }
+        break;
+      case PPauto:
+        switch(c)
+        {
+        case 'p':
+          op_push(OCprecreal,0,n);
+          break;
+        case 'P':
+          op_push(OCprecdl,0,n);
+          break;
+        case 'C':
+          op_push(OCpushgen,data_push(pack_localvars()),n);
+          break;
+        case 'f':
+          {
+            static long foo;
+            op_push(OCpushlong,(long)&foo,n);
+            break;
+          }
+        }
+        break;
+      case PPdefault:
+        j++;
+        switch(c)
+        {
+        case 'G':
+        case '&':
+        case 'E':
+        case 'I':
+        case 'r':
+        case 's':
+          op_push(OCpushlong,0,n);
+          break;
+        case 'n':
+          op_push(OCpushlong,-1,n);
+          break;
+        case 'V':
+          ev[lev++] = -1;
+          break;
+        case 'P':
+          op_push(OCprecdl,0,n);
+          break;
+        default:
+          pari_err(e_MISC,"Unknown prototype code `%c' for `%.*s'",c,
+              tree[x].len, tree[x].str);
+        }
+        break;
+      case PPdefaultmulti:
+        j++;
+        switch(c)
+        {
+        case 'G':
+          op_push(OCpushstoi,strtol(q+1,NULL,10),n);
+          break;
+        case 'L':
+        case 'M':
+          op_push(OCpushlong,strtol(q+1,NULL,10),n);
+          break;
+        case 'r':
+        case 's':
+          str_defproto(p, q, tree[n].str);
+          op_push(OCtostr, -1, n);
+          break;
+        default:
+          pari_err(e_MISC,"Unknown prototype code `%c' for `%.*s'",c,
+              tree[x].len, tree[x].str);
+        }
+        break;
+      case PPstar:
+        switch(c)
+        {
+        case 'E':
+          {
+            long k, n=nb+1-j;
+            GEN g=cgetg(n+1,t_VEC);
+            int ismif = is_func_named(ep,"_multi_if");
+            for(k=1; k<=n; k++)
+              gel(g, k) = compilefuncinline(n, c, arg[j+k-1], flag,
+                          ismif && (k==n || odd(k)), lev, ev);
+            op_push(OCpushgen, data_push(g), arg[j]);
+            j=nb+1;
+            break;
+          }
+        case 's':
+          {
+            long n=nb+1-j;
+            long k,l,l1,m;
+            GEN g=cgetg(n+1,t_VEC);
+            for(l1=0,k=1;k<=n;k++)
+            {
+              gel(g,k)=cattovec(arg[j+k-1],OPcat);
+              l1+=lg(gel(g,k))-1;
+            }
+            op_push_loc(OCvec, l1+1, str);
+            for(m=1,k=1;k<=n;k++)
+              for(l=1;l<lg(gel(g,k));l++,m++)
+              {
+                compilenode(mael(g,k,l),Ggen,FLsurvive);
+                op_push(OCstackgen,m,mael(g,k,l));
+              }
+            op_push_loc(OCpop, 1, str);
+            j=nb+1;
+            break;
+          }
+        default:
+          pari_err(e_MISC,"Unknown prototype code `%c*' for `%.*s'",c,
+              tree[x].len, tree[x].str);
+        }
+        break;
+      default:
+        pari_err_BUG("compilefunc [unknown PPproto]");
+      }
+      q=p;
+    }
+  }
+  if (j<=nb)
+    compile_err("too many arguments",tree[arg[j]].str);
+  op_push_loc(ret_op, (long) ep, str);
+  if ((ret_flag&FLnocopy) && !(flag&FLnocopy))
+    op_push_loc(OCcopy,0,str);
+  if (ret_typ==Ggen && nbpointers==0 && s_opcode.n>nbopcodes+128)
+  {
+    op_insert_loc(nbopcodes,OCavma,0,str);
+    op_push_loc(OCgerepile,0,str);
+  }
+  compilecast(n,ret_typ,mode);
+  if (nbpointers) op_push_loc(OCendptr,nbpointers, str);
+  avma=ltop;
+}
+
+static GEN
+genclosure(entree *ep, const char *loc, long  nbdata, int check)
+{
+  struct codepos pos;
+  long i, nb=0;
+  const char *code=ep->code,*p,*q;
+  char c;
+  long index=ep->arity;
+  long arity=0, maskarg=0, maskarg0=0, stop=0;
+  PPproto mod;
+  Gtype ret_typ;
+  long ret_flag;
+  op_code ret_op=get_ret_type(&code,ep->arity,&ret_typ,&ret_flag);
+  p=code;
+  while ((mod=parseproto(&p,&c,NULL))!=PPend)
+  {
+    if (mod==PPauto)
+      stop=1;
+    else
+    {
+      if (stop) return NULL;
+      if (c=='V') continue;
+      maskarg<<=1; maskarg0<<=1; arity++;
+      switch(mod)
+      {
+      case PPstd:
+        maskarg|=1L;
+        break;
+      case PPdefault:
+        switch(c)
+        {
+        case '&':
+        case 'E':
+        case 'I':
+          maskarg0|=1L;
+          break;
+        }
+        break;
+      default:
+        break;
+      }
+    }
+  }
+  if (check && EpSTATIC(ep) && maskarg==0)
+    return gen_0;
+  getcodepos(&pos);
+  dbgstart = loc;
+  if (nbdata > arity)
+    pari_err(e_MISC,"too many parameters for closure `%s'", ep->name);
+  for(i=1; i<= nbdata; i++)
+    op_push_loc(OCpushgen,data_push(NULL),loc);
+  arity -= nbdata;
+  if (maskarg)  op_push_loc(OCcheckargs,maskarg,loc);
+  if (maskarg0) op_push_loc(OCcheckargs0,maskarg0,loc);
+  p=code;
+  while ((mod=parseproto(&p,&c,NULL))!=PPend)
+  {
+    switch(mod)
+    {
+    case PPauto:
+      switch(c)
+      {
+      case 'p':
+        op_push_loc(OCprecreal,0,loc);
+        break;
+      case 'P':
+        op_push_loc(OCprecdl,0,loc);
+        break;
+      case 'C':
+        op_push_loc(OCpushgen,data_push(pack_localvars()),loc);
+        break;
+      case 'f':
+        {
+          static long foo;
+          op_push_loc(OCpushlong,(long)&foo,loc);
+          break;
+        }
+      }
+    default:
+      break;
+    }
+  }
+  q = p = code;
+  while ((mod=parseproto(&p,&c,NULL))!=PPend)
+  {
+    switch(mod)
+    {
+    case PPstd:
+      switch(c)
+      {
+      case 'G':
+        break;
+      case 'M':
+      case 'L':
+        op_push_loc(OCitos,-index,loc);
+        break;
+      case 'n':
+        op_push_loc(OCvarn,-index,loc);
+        break;
+      case '&': case '*':
+      case 'I':
+      case 'E':
+      case 'V':
+      case '=':
+        return NULL;
+      case 'r':
+      case 's':
+        op_push_loc(OCtostr,-index,loc);
+        break;
+      }
+      break;
+    case PPauto:
+      break;
+    case PPdefault:
+      switch(c)
+      {
+      case 'G':
+      case '&':
+      case 'E':
+      case 'I':
+      case 'V':
+      case 'r':
+      case 's':
+        break;
+      case 'n':
+        op_push_loc(OCvarn,-index,loc);
+        break;
+      case 'P':
+        op_push_loc(OCprecdl,0,loc);
+        op_push_loc(OCdefaultlong,-index,loc);
+        break;
+      default:
+        pari_err(e_MISC,"Unknown prototype code `D%c' for `%s'",c,ep->name);
+      }
+      break;
+    case PPdefaultmulti:
+      switch(c)
+      {
+      case 'G':
+        op_push_loc(OCpushstoi,strtol(q+1,NULL,10),loc);
+        op_push_loc(OCdefaultgen,-index,loc);
+        break;
+      case 'L':
+      case 'M':
+        op_push_loc(OCpushlong,strtol(q+1,NULL,10),loc);
+        op_push_loc(OCdefaultlong,-index,loc);
+        break;
+      case 'r':
+      case 's':
+        str_defproto(p, q, loc);
+        op_push_loc(OCdefaultgen,-index,loc);
+        op_push_loc(OCtostr,-index,loc);
+        break;
+      default:
+        pari_err(e_MISC,
+            "Unknown prototype code `D...,%c,' for `%s'",c,ep->name);
+      }
+      break;
+    case PPstar:
+      switch(c)
+      {
+      case 'E':
+      case 's':
+        return NULL;
+      default:
+        pari_err(e_MISC,"Unknown prototype code `%c*' for `%s'",c,ep->name);
+      }
+      break;
+    default:
+      return NULL;
+    }
+    index--;
+    q = p;
+  }
+  op_push_loc(ret_op, (long) ep, loc);
+  if (ret_flag==FLnocopy) op_push_loc(OCcopy,0,loc);
+  compilecast_loc(ret_typ, Ggen, loc);
+  return getfunction(&pos,nb+arity,0,strtoGENstr(ep->name),0);
+}
+
+GEN
+snm_closure(entree *ep, GEN data)
+{
+  long i;
+  long n = data ? lg(data)-1: 0;
+  GEN C = genclosure(ep,ep->name,n,0);
+  for(i=1; i<=n; i++)
+    gmael(C,4,i) = gel(data,i);
+  return C;
+}
+
+GEN
+strtoclosure(const char *s, long n,  ...)
+{
+  pari_sp av = avma;
+  entree *ep = is_entry(s);
+  GEN C;
+  if (!ep) pari_err(e_MISC,"no function named \"%s\"",s);
+  ep = do_alias(ep);
+  if ((!EpSTATIC(ep) && EpVALENCE(ep)!=EpINSTALL) || !ep->value)
+    pari_err(e_MISC,"not a built-in/install'ed function: \"%s\"",s);
+  C = genclosure(ep,ep->name,n,0);
+  if (!C) pari_err(e_MISC,"function prototype unsupported: \"%s\"",s);
+  else
+  {
+    va_list ap;
+    long i;
+    va_start(ap,n);
+    for(i=1; i<=n; i++)
+      gmael(C,4,i) = va_arg(ap, GEN);
+    va_end(ap);
+  }
+  return gerepilecopy(av, C);
+}
+
+GEN
+strtofunction(const char *s)
+{
+  return strtoclosure(s, 0);
+}
+
+static void
+closurefunc(entree *ep, long n, long mode)
+{
+  pari_sp ltop=avma;
+  GEN C;
+  if (!ep->value) compile_err("unknown function",tree[n].str);
+  C = genclosure(ep,tree[n].str,0,1);
+  if (!C) compile_err("sorry, closure not implemented",tree[n].str);
+  if (C==gen_0)
+  {
+    compilefunc(ep,n,mode,0);
+    return;
+  }
+  op_push(OCpushgen, data_push(C), n);
+  compilecast(n,Gclosure,mode);
+  avma=ltop;
+}
+
+static void
+compileseq(long n, int mode, long flag)
+{
+  pari_sp av = avma;
+  GEN L = listtogen(n, Fseq);
+  long i, l = lg(L)-1;
+  for(i = 1; i < l; i++)
+    compilenode(L[i],Gvoid,0);
+  compilenode(L[l],mode,flag&(FLreturn|FLsurvive));
+  avma = av;
+}
+
+static void
+compilenode(long n, int mode, long flag)
+{
+  long x,y;
+#ifdef STACK_CHECK
+  if (PARI_stack_limit && (void*) &x <= PARI_stack_limit)
+    pari_err(e_MISC, "expression nested too deeply");
+#endif
+  if (n<0) pari_err_BUG("compilenode");
+  x=tree[n].x;
+  y=tree[n].y;
+
+  switch(tree[n].f)
+  {
+  case Fseq:
+    compileseq(n, mode, flag);
+    return;
+  case Fmatcoeff:
+    compilematcoeff(n,mode);
+    if (mode==Ggen && !(flag&FLnocopy))
+      op_push(OCcopy,0,n);
+    return;
+  case Fassign:
+    x = detag(x);
+    if (tree[x].f==Fvec && tree[x].x>=0)
+    {
+      GEN vars = listtogen(tree[x].x,Fmatrixelts);
+      long i, l = lg(vars)-1;
+      compilenode(y,Ggen,mode==Gvoid?FLnocopy:flag&FLsurvive);
+      op_push(OCdup,mode==Gvoid?l-1:l,x);
+      for(i=1; i<=l; i++)
+      {
+        long a = detag(vars[i]);
+        entree *ep=getlvalue(a);
+        long vn=getmvar(ep);
+        op_push(OCpushlong,i,a);
+        op_push(OCcompo1,Ggen,a);
+        if (tree[a].f==Fentry)
+          compilestore(vn,ep,n);
+        else
+        {
+          compilenewptr(vn,ep,n);
+          compilelvalue(a);
+          op_push(OCstoreptr,0,a);
+        }
+      }
+      if (mode!=Gvoid)
+        compilecast(n,Ggen,mode);
+    }
+    else
+    {
+      entree *ep=getlvalue(x);
+      long vn=getmvar(ep);
+      if (tree[x].f!=Fentry)
+      {
+        compilenewptr(vn,ep,n);
+        compilelvalue(x);
+      }
+      compilenode(y,Ggen,mode==Gvoid?FLnocopy:flag&FLsurvive);
+      if (mode!=Gvoid)
+        op_push(OCdup,1,n);
+      if (tree[x].f==Fentry)
+        compilestore(vn,ep,n);
+      else
+        op_push(OCstoreptr,0,x);
+      if (mode!=Gvoid)
+        compilecast(n,Ggen,mode);
+    }
+    return;
+  case Fconst:
+    {
+      pari_sp ltop=avma;
+      if (tree[n].x!=CSTquote)
+      {
+        if (mode==Gvoid) return;
+        if (mode==Gvar) compile_varerr(tree[n].str);
+      }
+      if (mode==Gsmall)
+        compile_err("this should be a small integer", tree[n].str);
+      switch(tree[n].x)
+      {
+      case CSTreal:
+        op_push(OCpushreal, data_push(strntoGENstr(tree[n].str,tree[n].len)),n);
+        break;
+      case CSTint:
+        op_push(OCpushgen,  data_push(strtoi((char*)tree[n].str)),n);
+        compilecast(n,Ggen, mode);
+        break;
+      case CSTstr:
+        op_push(OCpushgen,  data_push(strntoGENexp(tree[n].str,tree[n].len)),n);
+        break;
+      case CSTquote:
+        {
+          entree *ep = fetch_entry(tree[n].str+1,tree[n].len-1);
+          if (EpSTATIC(ep)) compile_varerr(tree[n].str+1);
+          op_push(OCpushvar, (long)ep,n);
+          compilecast(n,Ggen, mode);
+          break;
+        }
+      default:
+        pari_err_BUG("compilenode, unsupported constant");
+      }
+      avma=ltop;
+      return;
+    }
+  case Fsmall:
+    compilesmall(n, x, mode);
+    return;
+  case Fvec:
+    compilevec(n, mode, OCvec);
+    return;
+  case Fmat:
+    compilemat(n, mode);
+    return;
+  case Frefarg:
+    compile_err("unexpected character '&':",tree[n].str);
+    return;
+  case Fentry:
+    {
+      entree *ep=getentry(n);
+      long vn=getmvar(ep);
+      if (vn)
+      {
+        op_push(OCpushlex,(long)vn,n);
+        copyifclone(n,mode,flag,FLnocopy|FLnocopylex);
+        compilecast(n,Ggen,mode);
+      }
+      else if (ep->valence==EpVAR || ep->valence==EpNEW)
+      {
+        if (DEBUGLEVEL && mode==Gvoid)
+          pari_warn(warner,"statement with no effect: `%s'",ep->name);
+        op_push(OCpushdyn,(long)ep,n);
+        copyifclone(n,mode,flag,FLnocopy);
+        compilecast(n,Ggen,mode);
+      }
+      else
+        closurefunc(ep,n,mode);
+      return;
+    }
+  case Ffunction:
+    {
+      entree *ep=getfunc(n);
+      if (EpVALENCE(ep)==EpVAR || EpVALENCE(ep)==EpNEW)
+        compileuserfunc(ep,n,mode);
+      else
+        compilefunc(ep,n,mode,flag);
+      return;
+    }
+  case Fcall:
+    compilenode(x,Ggen,0);
+    compilecall(n,mode);
+    return;
+  case Flambda:
+    {
+      pari_sp ltop=avma;
+      struct codepos pos;
+      GEN arg=listtogen(x,Flistarg);
+      long nb, lgarg, nbmvar, gap;
+      long strict = GP_DATA->strictargs;
+      GEN vep = cgetg_copy(arg, &lgarg);
+      GEN text=cgetg(3,t_VEC);
+      gel(text,1)=strntoGENstr(tree[x].str,tree[x].len);
+      gel(text,2)=strntoGENstr(tree[y].str,tree[y].len);
+      getcodepos(&pos);
+      dbgstart=tree[x].str+tree[x].len;
+      gap = tree[y].str-dbgstart;
+      nbmvar=ctxmvar();
+      nb = lgarg-1;
+      if (nb)
+      {
+        long i;
+        for(i=1;i<=nb;i++)
+        {
+          long a=arg[i];
+          vep[i]=(long)getvar(tree[a].f==Fassign?tree[a].x:a);
+          var_push(NULL,Lmy);
+        }
+        checkdups(arg,vep);
+        op_push(OCgetargs,nb,x);
+        frame_push(vep);
+        for (i=1;i<=nb;i++)
+        {
+          long a=arg[i];
+          long y = tree[a].y;
+          if (tree[a].f==Fassign && (strict || !is_node_zero(y)))
+          {
+            if (tree[y].f==Fsmall)
+              compilenode(y,Ggen,a);
+            else
+            {
+              struct codepos lpos;
+              getcodepos(&lpos);
+              compilenode(y,Ggen,a);
+              op_push(OCpushgen, data_push(getclosure(&lpos)),a);
+            }
+            op_push(OCdefaultarg,-nb+i-1,a);
+          }
+          localvars[s_lvar.n-nb+i-1].ep=(entree*)vep[i];
+        }
+      }
+      if (strict)
+        op_push(OCcheckuserargs,nb,x);
+      dbgstart=tree[y].str;
+      if (y>=0 && tree[y].f!=Fnoarg)
+        compilenode(y,Ggen,FLsurvive|FLreturn);
+      else
+        compilecast(n,Gvoid,Ggen);
+      op_push(OCpushgen, data_push(getfunction(&pos,nb,nbmvar,text,gap)),n);
+      if (nbmvar) op_push(OCsaveframe,!!(flag&FLsurvive),n);
+      compilecast(n, Gclosure, mode);
+      avma=ltop;
+      return;
+    }
+  case Ftag:
+    compilenode(x, mode,flag);
+    return;
+  case Fnoarg:
+    compilecast(n,Gvoid,mode);
+    return;
+  default:
+    pari_err_BUG("compilenode");
+  }
+}
+
+GEN
+gp_closure(long n)
+{
+  struct codepos pos;
+  getcodepos(&pos);
+  dbgstart=tree[n].str;
+  compilenode(n,Ggen,FLsurvive|FLreturn);
+  return getfunction(&pos,0,0,strntoGENstr(tree[n].str,tree[n].len),0);
+}
+
+GEN
+closure_deriv(GEN G)
+{
+  pari_sp ltop=avma;
+  long i;
+  struct codepos pos;
+  const char *code;
+  GEN text;
+  long arity=G[1];
+  if (typ(gel(G,6))==t_STR)
+  {
+    code = GSTR(gel(G,6));
+    text = cgetg(1+nchar2nlong(2+strlen(code)),t_STR);
+    sprintf(GSTR(text),"%s'",code);
+  }
+  else
+  {
+    code = GSTR(GENtoGENstr(G));
+    text = cgetg(1+nchar2nlong(4+strlen(code)),t_STR);
+    sprintf(GSTR(text),"(%s)'",code);
+  }
+  getcodepos(&pos);
+  dbgstart=code;
+  op_push_loc(OCgetargs, arity,code);
+  op_push_loc(OCpushgen,data_push(G),code);
+  op_push_loc(OCvec,arity+1,code);
+  for (i=1;i<=arity;i++)
+  {
+    op_push_loc(OCpushlex,i-arity-1,code);
+    op_push_loc(OCstackgen,i,code);
+  }
+  op_push_loc(OCpop,1,code);
+  op_push_loc(OCprecreal,0,code);
+  op_push_loc(OCcallgen,(long)is_entry("_derivfun"),code);
+  return gerepilecopy(ltop, getfunction(&pos,arity,0,text,0));
+}
+
+static long
+vec_optimize(GEN arg)
+{
+  long fl = COsafelex|COsafedyn;
+  long i;
+  for (i=1; i<lg(arg); i++)
+  {
+    optimizenode(arg[i]);
+    fl &= tree[arg[i]].flags;
+  }
+  return fl;
+}
+
+static void
+optimizevec(long n)
+{
+  pari_sp ltop=avma;
+  long x = tree[n].x;
+  GEN  arg = listtogen(x, Fmatrixelts);
+  tree[n].flags = vec_optimize(arg);
+  avma = ltop;
+}
+
+static void
+optimizemat(long n)
+{
+  pari_sp ltop = avma;
+  long x = tree[n].x;
+  long i;
+  GEN line = listtogen(x,Fmatrixlines);
+  long fl = COsafelex|COsafedyn;
+  for(i=1;i<lg(line);i++)
+  {
+    GEN col=listtogen(line[i],Fmatrixelts);
+    fl &= vec_optimize(col);
+  }
+  avma=ltop; tree[n].flags=fl;
+}
+
+static void
+optimizematcoeff(long n)
+{
+  long x=tree[n].x;
+  long y=tree[n].y;
+  long yx=tree[y].x;
+  long yy=tree[y].y;
+  long fl;
+  optimizenode(x);
+  optimizenode(yx);
+  fl=tree[x].flags&tree[yx].flags;
+  if (yy>=0)
+  {
+    optimizenode(yy);
+    fl&=tree[yy].flags;
+  }
+  tree[n].flags=fl;
+}
+
+
+static void
+optimizefunc(entree *ep, long n)
+{
+  pari_sp av=avma;
+  long j;
+  long x=tree[n].x;
+  long y=tree[n].y;
+  Gtype t;
+  PPproto mod;
+  long fl=COsafelex|COsafedyn;
+  const char *p=ep->code;
+  char c;
+  GEN arg = listtogen(y,Flistarg);
+  long nb=lg(arg)-1, ret_flag;
+  if (!p)
+    fl=0;
+  else
+    (void) get_ret_type(&p, 2, &t, &ret_flag);
+  if (p && *p)
+  {
+    j=1;
+    while((mod=parseproto(&p,&c,tree[n].str))!=PPend)
+    {
+      if (j<=nb && tree[arg[j]].f!=Fnoarg
+          && (mod==PPdefault || mod==PPdefaultmulti))
+        mod=PPstd;
+      switch(mod)
+      {
+      case PPstd:
+        if (j>nb) compile_err("too few arguments", tree[n].str+tree[n].len-1);
+        if (tree[arg[j]].f==Fnoarg && c!='I' && c!='E')
+          compile_err("missing mandatory argument", tree[arg[j]].str);
+        switch(c)
+        {
+        case 'G':
+        case 'n':
+        case 'M':
+        case 'L':
+        case 'P':
+          optimizenode(arg[j]);
+          fl&=tree[arg[j++]].flags;
+          break;
+        case 'I':
+        case 'E':
+        case 'J':
+          optimizenode(arg[j]);
+          fl&=tree[arg[j]].flags;
+          tree[arg[j++]].flags=COsafelex|COsafedyn;
+          break;
+        case '&': case '*':
+          {
+            long a=arg[j];
+            if (c=='&')
+            {
+              if (tree[a].f!=Frefarg)
+                compile_err("expected character: '&'", tree[a].str);
+              a=tree[a].x;
+            }
+            optimizenode(a);
+            tree[arg[j++]].flags=COsafelex|COsafedyn;
+            fl=0;
+            break;
+          }
+        case 'W':
+          optimizenode(arg[j++]);
+          fl=0;
+          break;
+        case 'V':
+        case 'r':
+          tree[arg[j++]].flags=COsafelex|COsafedyn;
+          break;
+        case '=':
+          {
+            long a=arg[j++], y=tree[a].y;
+            if (tree[a].f!=Fassign)
+              compile_err("expected character: '=' instead of",
+                  tree[a].str+tree[a].len);
+            optimizenode(y);
+            fl&=tree[y].flags;
+          }
+          break;
+        case 's':
+          fl &= vec_optimize(cattovec(arg[j++], OPcat));
+          break;
+        default:
+          pari_err(e_MISC,"Unknown prototype code `%c' for `%.*s'",c,
+              tree[x].len, tree[x].str);
+        }
+        break;
+      case PPauto:
+        break;
+      case PPdefault:
+      case PPdefaultmulti:
+        if (j<=nb) optimizenode(arg[j++]);
+        break;
+      case PPstar:
+        switch(c)
+        {
+        case 'E':
+          {
+            long n=nb+1-j;
+            long k;
+            for(k=1;k<=n;k++)
+            {
+              optimizenode(arg[j+k-1]);
+              fl &= tree[arg[j+k-1]].flags;
+            }
+            j=nb+1;
+            break;
+          }
+        case 's':
+          {
+            long n=nb+1-j;
+            long k;
+            for(k=1;k<=n;k++)
+              fl &= vec_optimize(cattovec(arg[j+k-1],OPcat));
+            j=nb+1;
+            break;
+          }
+        default:
+          pari_err(e_MISC,"Unknown prototype code `%c*' for `%.*s'",c,
+              tree[x].len, tree[x].str);
+        }
+        break;
+      default:
+        pari_err_BUG("optimizefun [unknown PPproto]");
+      }
+    }
+  }
+  else (void)vec_optimize(arg);
+  avma=av; tree[n].flags=fl;
+}
+
+static void
+optimizecall(long n)
+{
+  pari_sp av=avma;
+  long x=tree[n].x;
+  long y=tree[n].y;
+  GEN arg=listtogen(y,Flistarg);
+  optimizenode(x);
+  tree[n].flags = COsafelex&tree[x].flags&vec_optimize(arg);
+  avma=av;
+}
+
+static void
+optimizeseq(long n)
+{
+  pari_sp av = avma;
+  GEN L = listtogen(n, Fseq);
+  long i, l = lg(L)-1, flags=-1L;
+  for(i = 1; i <= l; i++)
+  {
+    optimizenode(L[i]);
+    flags &= tree[L[i]].flags;
+  }
+  avma = av;
+  tree[n].flags = flags;
+}
+
+void
+optimizenode(long n)
+{
+  long x,y;
+#ifdef STACK_CHECK
+  if (PARI_stack_limit && (void*) &x <= PARI_stack_limit)
+    pari_err(e_MISC, "expression nested too deeply");
+#endif
+  if (n<0)
+    pari_err_BUG("optimizenode");
+  x=tree[n].x;
+  y=tree[n].y;
+
+  switch(tree[n].f)
+  {
+  case Fseq:
+    optimizeseq(n);
+    return;
+  case Frange:
+    optimizenode(x);
+    optimizenode(y);
+    tree[n].flags=tree[x].flags&tree[y].flags;
+    break;
+  case Fmatcoeff:
+    optimizematcoeff(n);
+    break;
+  case Fassign:
+    optimizenode(x);
+    optimizenode(y);
+    tree[n].flags=0;
+    break;
+  case Fnoarg:
+  case Fsmall:
+  case Fconst:
+  case Fentry:
+    tree[n].flags=COsafelex|COsafedyn;
+    return;
+  case Fvec:
+    optimizevec(n);
+    return;
+  case Fmat:
+    optimizemat(n);
+    return;
+  case Frefarg:
+    compile_err("unexpected character '&'",tree[n].str);
+    return;
+  case Ffunction:
+    {
+      entree *ep=getfunc(n);
+      if (EpVALENCE(ep)==EpVAR || EpVALENCE(ep)==EpNEW)
+        optimizecall(n);
+      else
+        optimizefunc(ep,n);
+      return;
+    }
+  case Fcall:
+    optimizecall(n);
+    return;
+  case Flambda:
+    optimizenode(y);
+    tree[n].flags=COsafelex|COsafedyn;
+    return;
+  case Ftag:
+    optimizenode(x);
+    tree[n].flags=tree[x].flags;
+    return;
+  default:
+    pari_err_BUG("optimizenode");
+  }
+}
diff --git a/src/language/default.c b/src/language/default.c
new file mode 100644
index 0000000..3b8f75b
--- /dev/null
+++ b/src/language/default.c
@@ -0,0 +1,814 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/* Simple-minded parsing utilities. These are forbidden to use the GP stack
+ * which may not exist at this point [e.g upon GP initialization]  */
+
+#ifdef MAXPATHLEN
+#  define GET_SEP_SIZE MAXPATHLEN
+#else
+#  define GET_SEP_SIZE 128
+#endif
+
+/* Return all chars, up to next separator
+ * [as strtok but must handle verbatim character string] */
+char*
+get_sep(const char *t)
+{
+  static char buf[GET_SEP_SIZE], *lim = buf + GET_SEP_SIZE;
+  char *s = buf;
+  int outer = 1;
+
+  for(;;)
+  {
+    switch(*s++ = *t++)
+    {
+      case '"':
+        outer = !outer; break;
+      case '\0':
+        return buf;
+      case ';':
+        if (outer) { s[-1] = 0; return buf; }
+        break;
+      case '\\': /* gobble next char */
+        if (s == lim) break;
+        if (! (*s++ = *t++) ) return buf;
+    }
+    if (s == lim)
+      pari_err(e_MISC,"get_sep: argument too long (< %ld chars)", GET_SEP_SIZE);
+  }
+}
+
+static ulong
+safe_mul(ulong x, ulong y)
+{
+  ulong z;
+  LOCAL_HIREMAINDER;
+  z = mulll(x, y);
+  return hiremainder? 0: z;
+}
+
+/* "atoul" + optional [kmg] suffix */
+static ulong
+my_int(char *s)
+{
+  ulong n = 0;
+  char *p = s;
+
+  while (isdigit((int)*p)) {
+    ulong m;
+    if (n > (~0UL / 10)) pari_err(e_SYNTAX,"integer too large",s,s);
+    n *= 10; m = n;
+    n += *p++ - '0';
+    if (n < m) pari_err(e_SYNTAX,"integer too large",s,s);
+  }
+  if (n)
+  {
+    switch(*p)
+    {
+      case 'k': case 'K': n = safe_mul(n,1000UL);       p++; break;
+      case 'm': case 'M': n = safe_mul(n,1000000UL);    p++; break;
+      case 'g': case 'G': n = safe_mul(n,1000000000UL); p++; break;
+    }
+    if (!n) pari_err(e_SYNTAX,"integer too large",s,s);
+  }
+  if (*p) pari_err(e_SYNTAX,"I was expecting an integer here", s, s);
+  return n;
+}
+
+long
+get_int(const char *s, long dflt)
+{
+  char *p = get_sep(s);
+  long n;
+  int minus = 0;
+
+  if (*p == '-') { minus = 1; p++; }
+  if (!isdigit((int)*p)) return dflt;
+
+  n = (long)my_int(p);
+  if (n < 0) pari_err(e_SYNTAX,"integer too large",s,s);
+  return minus? -n: n;
+}
+
+ulong
+get_uint(const char *s)
+{
+  char *p = get_sep(s);
+  if (*p == '-') pari_err(e_SYNTAX,"arguments must be positive integers",s,s);
+  return my_int(p);
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                            DEFAULTS                              */
+/*                                                                  */
+/********************************************************************/
+
+long
+getrealprecision(void)
+{
+  return GP_DATA->fmt->sigd;
+}
+
+long
+setrealprecision(long n, long *prec)
+{
+  GP_DATA->fmt->sigd = n;
+  *prec = precreal = ndec2prec(n);
+  return n;
+}
+
+GEN
+sd_toggle(const char *v, long flag, const char *s, int *ptn)
+{
+  int state = *ptn;
+  if (v)
+  {
+    int n = (int)get_int(v,0);
+    if (n == state) return gnil;
+    if (n != !state)
+    {
+      char *t = stack_malloc(64 + strlen(s));
+      (void)sprintf(t, "default: incorrect value for %s [0:off / 1:on]", s);
+      pari_err(e_SYNTAX, t, v,v);
+    }
+    state = *ptn = n;
+  }
+  switch(flag)
+  {
+    case d_RETURN: return utoi(state);
+    case d_ACKNOWLEDGE:
+      if (state) pari_printf("   %s = 1 (on)\n", s);
+      else       pari_printf("   %s = 0 (off)\n", s);
+      break;
+  }
+  return gnil;
+}
+
+static void
+sd_ulong_init(const char *v, const char *s, ulong *ptn, ulong Min, ulong Max)
+{
+  if (v)
+  {
+    ulong n = get_uint(v);
+    if (n > Max || n < Min)
+    {
+      char *buf = stack_malloc(strlen(s) + 2 * 20 + 40);
+      (void)sprintf(buf, "default: incorrect value for %s [%lu-%lu]",
+                    s, Min, Max);
+      pari_err(e_SYNTAX, buf, v,v);
+    }
+    *ptn = n;
+  }
+}
+
+/* msg is NULL or NULL-terminated array with msg[0] != NULL. */
+GEN
+sd_ulong(const char *v, long flag, const char *s, ulong *ptn, ulong Min, ulong Max,
+         const char **msg)
+{
+  ulong n = *ptn;
+  sd_ulong_init(v, s, ptn, Min, Max);
+  switch(flag)
+  {
+    case d_RETURN:
+      return utoi(*ptn);
+    case d_ACKNOWLEDGE:
+      if (!v || *ptn != n) {
+        if (!msg)         /* no specific message */
+          pari_printf("   %s = %lu\n", s, *ptn);
+        else if (!msg[1]) /* single message, always printed */
+          pari_printf("   %s = %lu %s\n", s, *ptn, msg[0]);
+        else              /* print (new)-n-th message */
+          pari_printf("   %s = %lu %s\n", s, *ptn, msg[*ptn]);
+      }
+      break;
+  }
+  return gnil;
+}
+
+GEN
+sd_realprecision(const char *v, long flag)
+{
+  pariout_t *fmt = GP_DATA->fmt;
+  if (v)
+  {
+    ulong newnb = fmt->sigd, prec;
+    sd_ulong_init(v, "realprecision", &newnb, 1, prec2ndec(LGBITS));
+    if (fmt->sigd == (long)newnb) return gnil;
+    if (fmt->sigd >= 0) fmt->sigd = newnb;
+    prec = (ulong)ndec2prec(newnb);
+    if (prec == precreal) return gnil;
+    precreal = prec;
+  }
+  if (flag == d_RETURN) return stoi(prec2ndec(precreal));
+  if (flag == d_ACKNOWLEDGE)
+  {
+    long n = prec2ndec(precreal);
+    pari_printf("   realprecision = %ld significant digits", n);
+    if (fmt->sigd < 0)
+      pari_puts(" (all digits displayed)");
+    else if (n != fmt->sigd)
+      pari_printf(" (%ld digits displayed)", fmt->sigd);
+    pari_putc('\n');
+  }
+  return gnil;
+}
+
+GEN
+sd_seriesprecision(const char *v, long flag)
+{
+  const char *msg[] = {"significant terms", NULL};
+  return sd_ulong(v,flag,"seriesprecision",&precdl, 1,LGBITS,msg);
+}
+
+static long
+gp_get_color(char **st)
+{
+  char *s, *v = *st;
+  int trans;
+  long c;
+  if (isdigit((int)*v))
+    { c = atol(v); trans = 1; } /* color on transparent background */
+  else
+  {
+    if (*v == '[')
+    {
+      const char *a[3];
+      long i = 0;
+      for (a[0] = s = ++v; *s && *s != ']'; s++)
+        if (*s == ',') { *s = 0; a[++i] = s+1; }
+      if (*s != ']') pari_err(e_SYNTAX,"expected character: ']'",s, *st);
+      *s = 0; for (i++; i<3; i++) a[i] = "";
+      /*    properties    |   color    | background */
+      c = (atoi(a[2])<<8) | atoi(a[0]) | (atoi(a[1])<<4);
+      trans = (*(a[1]) == 0);
+      v = s + 1;
+    }
+    else { c = c_NONE; trans = 0; }
+  }
+  if (trans) c = c | (1L<<12);
+  while (*v && *v++ != ',') /* empty */;
+  if (c != c_NONE) disable_color = 0;
+  *st = v; return c;
+}
+
+/* 1: error, 2: history, 3: prompt, 4: input, 5: output, 6: help, 7: timer */
+GEN
+sd_colors(const char *v, long flag)
+{
+  long c,l;
+  if (v && !(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS)))
+  {
+    char *v0, *s;
+    disable_color=1;
+    l = strlen(v);
+    if (l <= 2 && strncmp(v, "no", l) == 0)
+      v = "";
+    if (l <= 6 && strncmp(v, "darkbg", l) == 0)
+      v = "1, 5, 3, 7, 6, 2, 3"; /* Assume recent ReadLine. */
+    if (l <= 7 && strncmp(v, "lightbg", l) == 0)
+      v = "1, 6, 3, 4, 5, 2, 3"; /* Assume recent ReadLine. */
+    if (l <= 8 && strncmp(v, "brightfg", l) == 0)      /* Good for windows consoles */
+      v = "9, 13, 11, 15, 14, 10, 11";
+    if (l <= 6 && strncmp(v, "boldfg", l) == 0)        /* Good for darkbg consoles */
+      v = "[1,,1], [5,,1], [3,,1], [7,,1], [6,,1], , [2,,1]";
+    v0 = s = filtre(v, 0);
+    for (c=c_ERR; c < c_LAST; c++)
+      gp_colors[c] = gp_get_color(&s);
+    pari_free(v0);
+  }
+  if (flag == d_ACKNOWLEDGE || flag == d_RETURN)
+  {
+    char s[128], *t = s;
+    long col[3], n;
+    for (*t=0,c=c_ERR; c < c_LAST; c++)
+    {
+      n = gp_colors[c];
+      if (n == c_NONE)
+        sprintf(t,"no");
+      else
+      {
+        decode_color(n,col);
+        if (n & (1L<<12))
+        {
+          if (col[0])
+            sprintf(t,"[%ld,,%ld]",col[1],col[0]);
+          else
+            sprintf(t,"%ld",col[1]);
+        }
+        else
+          sprintf(t,"[%ld,%ld,%ld]",col[1],col[2],col[0]);
+      }
+      t += strlen(t);
+      if (c < c_LAST - 1) { *t++=','; *t++=' '; }
+    }
+    if (flag==d_RETURN) return strtoGENstr(s);
+    pari_printf("   colors = \"%s\"\n",s);
+  }
+  return gnil;
+}
+
+GEN
+sd_format(const char *v, long flag)
+{
+  pariout_t *fmt = GP_DATA->fmt;
+  if (v)
+  {
+    char c = *v;
+    if (c!='e' && c!='f' && c!='g')
+      pari_err(e_SYNTAX,"default: inexistent format",v,v);
+    fmt->format = c; v++;
+
+    if (isdigit((int)*v))
+      { while (isdigit((int)*v)) v++; } /* FIXME: skip obsolete field width */
+    if (*v++ == '.')
+    {
+      if (*v == '-') fmt->sigd = -1;
+      else
+        if (isdigit((int)*v)) fmt->sigd=atol(v);
+    }
+  }
+  if (flag == d_RETURN)
+  {
+    char *s = stack_malloc(64);
+    (void)sprintf(s, "%c.%ld", fmt->format, fmt->sigd);
+    return strtoGENstr(s);
+  }
+  if (flag == d_ACKNOWLEDGE)
+    pari_printf("   format = %c.%ld\n", fmt->format, fmt->sigd);
+  return gnil;
+}
+
+GEN
+sd_compatible(const char *v, long flag)
+{
+  const char *msg[] = {
+    "(no backward compatibility)",
+    "(warn when using obsolete functions)",
+    "(use old functions, don't ignore case)",
+    "(use old functions, ignore case)", NULL
+  };
+  ulong old = compatible;
+  GEN r = sd_ulong(v,flag,"compatible",&compatible, 0,3,msg);
+
+  if (old != compatible && flag != d_INITRC && gp_init_functions())
+    pari_warn(warner,"user functions re-initialized");
+  return r;
+}
+
+GEN
+sd_secure(const char *v, long flag)
+{
+  if (v && GP_DATA->secure)
+    pari_ask_confirm("[secure mode]: About to modify the 'secure' flag");
+  return sd_toggle(v,flag,"secure", &(GP_DATA->secure));
+}
+
+GEN
+sd_debug(const char *v, long flag)
+{ return sd_ulong(v,flag,"debug",&DEBUGLEVEL, 0,20,NULL); }
+
+GEN
+sd_debugfiles(const char *v, long flag)
+{ return sd_ulong(v,flag,"debugfiles",&DEBUGFILES, 0,20,NULL); }
+
+GEN
+sd_debugmem(const char *v, long flag)
+{ return sd_ulong(v,flag,"debugmem",&DEBUGMEM, 0,20,NULL); }
+
+/* set D->hist to size = s / total = t */
+static void
+init_hist(gp_data *D, size_t s, ulong t)
+{
+  gp_hist *H = D->hist;
+  H->total = t;
+  H->size = s;
+  H->v = (gp_hist_cell*)pari_calloc(s * sizeof(gp_hist_cell));
+}
+GEN
+sd_histsize(const char *s, long flag)
+{
+  gp_hist *H = GP_DATA->hist;
+  ulong n = H->size;
+  GEN r = sd_ulong(s,flag,"histsize",&n, 1,
+                     (LONG_MAX / sizeof(long)) - 1,NULL);
+  if (n != H->size)
+  {
+    const ulong total = H->total;
+    long g, h, k, kmin;
+    gp_hist_cell *v = H->v, *w; /* v = old data, w = new one */
+    size_t sv = H->size, sw;
+
+    init_hist(GP_DATA, n, total);
+    if (!total) return r;
+
+    w = H->v;
+    sw= H->size;
+    /* copy relevant history entries */
+    g     = (total-1) % sv;
+    h = k = (total-1) % sw;
+    kmin = k - minss(sw, sv);
+    for ( ; k > kmin; k--, g--, h--)
+    {
+      w[h]   = v[g];
+      v[g].z = NULL;
+      if (!g) g = sv;
+      if (!h) h = sw;
+    }
+    /* clean up */
+    for ( ; v[g].z; g--)
+    {
+      gunclone(v[g].z);
+      if (!g) g = sv;
+    }
+    pari_free((void*)v);
+  }
+  return r;
+}
+
+static void
+TeX_define(const char *s, const char *def) {
+  fprintf(pari_logfile, "\\ifx\\%s\\undefined\n  \\def\\%s{%s}\\fi\n", s,s,def);
+}
+static void
+TeX_define2(const char *s, const char *def) {
+  fprintf(pari_logfile, "\\ifx\\%s\\undefined\n  \\def\\%s#1#2{%s}\\fi\n", s,s,def);
+}
+
+static FILE *
+open_logfile(const char *s) {
+  FILE *log = fopen(s, "a");
+  if (!log) pari_err_FILE("logfile",s);
+#ifndef WINCE
+  setbuf(log,(char *)NULL);
+#endif
+  return log;
+}
+
+GEN
+sd_log(const char *v, long flag)
+{
+  const char *msg[] = {
+      "(off)",
+      "(on)",
+      "(on with colors)",
+      "(TeX output)", NULL
+  };
+  ulong oldstyle = logstyle;
+  GEN res = sd_ulong(v,flag,"log", &logstyle, 0, 3, msg);
+
+  if (!oldstyle != !logstyle)                /* Compare converts to boolean */
+  { /* toggled LOG */
+    if (oldstyle)
+    { /* close log */
+      if (flag == d_ACKNOWLEDGE)
+        pari_printf("   [logfile was \"%s\"]\n", current_logfile);
+      fclose(pari_logfile); pari_logfile = NULL;
+    }
+    else
+      pari_logfile = open_logfile(current_logfile);
+  }
+  if (pari_logfile && oldstyle != logstyle && logstyle == logstyle_TeX)
+  {
+    TeX_define("PARIbreak",
+               "\\hskip 0pt plus \\hsize\\relax\\discretionary{}{}{}");
+    TeX_define("PARIpromptSTART", "\\vskip\\medskipamount\\bgroup\\bf");
+    TeX_define("PARIpromptEND", "\\egroup\\bgroup\\tt");
+    TeX_define("PARIinputEND", "\\egroup");
+    TeX_define2("PARIout",
+                "\\vskip\\smallskipamount$\\displaystyle{\\tt\\%#1} = #2$");
+  }
+  return res;
+}
+
+GEN
+sd_TeXstyle(const char *v, long flag)
+{
+  const char *msg[] = { "(bits 0x2/0x4 control output of \\left/\\PARIbreak)",
+                        NULL };
+  ulong n = GP_DATA->fmt->TeXstyle;
+  GEN z = sd_ulong(v,flag,"TeXstyle", &n, 0, 7, msg);
+  GP_DATA->fmt->TeXstyle = n; return z;
+}
+
+GEN
+sd_nbthreads(const char *v, long flag)
+{ return sd_ulong(v,flag,"nbthreads",&pari_mt_nbthreads, 1,LONG_MAX,NULL); }
+
+GEN
+sd_output(const char *v, long flag)
+{
+  const char *msg[] = {"(raw)", "(prettymatrix)", "(prettyprint)",
+                 "(external prettyprint)", NULL};
+  ulong n = GP_DATA->fmt->prettyp;
+  GEN z = sd_ulong(v,flag,"output", &n, 0,3,msg);
+  GP_DATA->fmt->prettyp = n;
+  GP_DATA->fmt->sp = (n != f_RAW);
+  return z;
+}
+
+GEN
+sd_parisize(const char *v, long flag)
+{
+  ulong size = top - bot, n = size;
+  GEN r = sd_ulong(v,flag,"parisize",&n, 10000,LONG_MAX,NULL);
+  if (n != size) {
+    if (flag == d_INITRC)
+      pari_init_stack(n, size);
+    else
+      allocatemem(n);
+  }
+  return r;
+}
+
+GEN
+sd_threadsize(const char *v, long flag)
+{
+  ulong size = GP_DATA->threadsize, n = size;
+  GEN r = sd_ulong(v,flag,"threadsize",&n, 0,LONG_MAX,NULL);
+  if (n != size)
+    GP_DATA->threadsize = n;
+  return r;
+}
+
+GEN
+sd_primelimit(const char *v, long flag)
+{ return sd_ulong(v,flag,"primelimit",&(GP_DATA->primelimit),
+                  0,2*(ulong)(LONG_MAX-1024) + 1,NULL); }
+
+GEN
+sd_simplify(const char *v, long flag)
+{ return sd_toggle(v,flag,"simplify", &(GP_DATA->simplify)); }
+
+GEN
+sd_strictmatch(const char *v, long flag)
+{ return sd_toggle(v,flag,"strictmatch", &(GP_DATA->strictmatch)); }
+
+GEN
+sd_strictargs(const char *v, long flag)
+{ return sd_toggle(v,flag,"strictargs", &(GP_DATA->strictargs)); }
+
+GEN
+sd_string(const char *v, long flag, const char *s, char **pstr)
+{
+  char *old = *pstr;
+  if (v)
+  {
+    char *str, *ev = path_expand(v);
+    long l = strlen(ev) + 256;
+    str = (char *) pari_malloc(l);
+    strftime_expand(ev,str, l-1); pari_free(ev);
+    if (GP_DATA->secure)
+    {
+      char *msg=pari_sprintf("[secure mode]: About to change %s to '%s'",s,str);
+      pari_ask_confirm(msg);
+      pari_free(msg);
+    }
+    if (old) pari_free(old);
+    *pstr = old = pari_strdup(str);
+    pari_free(str);
+  }
+  else if (!old) old = (char*)"<undefined>";
+  if (flag == d_RETURN) return strtoGENstr(old);
+  if (flag == d_ACKNOWLEDGE) pari_printf("   %s = \"%s\"\n",s,old);
+  return gnil;
+}
+
+GEN
+sd_logfile(const char *v, long flag)
+{
+  GEN r = sd_string(v, flag, "logfile", &current_logfile);
+  if (v && pari_logfile)
+  {
+    FILE *log = open_logfile(current_logfile);
+    fclose(pari_logfile); pari_logfile = log;
+  }
+  return r;
+}
+
+GEN
+sd_factor_add_primes(const char *v, long flag)
+{ return sd_toggle(v,flag,"factor_add_primes", &factor_add_primes); }
+
+GEN
+sd_factor_proven(const char *v, long flag)
+{ return sd_toggle(v,flag,"factor_proven", &factor_proven); }
+
+GEN
+sd_new_galois_format(const char *v, long flag)
+{ return sd_toggle(v,flag,"new_galois_format", &new_galois_format); }
+
+GEN
+sd_datadir(const char *v, long flag)
+{
+  const char *str;
+  if (v)
+  {
+    if (pari_datadir) pari_free(pari_datadir);
+    pari_datadir = path_expand(v);
+  }
+  str = pari_datadir? pari_datadir: "none";
+  if (flag == d_RETURN) return strtoGENstr(str);
+  if (flag == d_ACKNOWLEDGE)
+    pari_printf("   datadir = \"%s\"\n", str);
+  return gnil;
+}
+
+static GEN
+sd_PATH(const char *v, long flag, const char* s, gp_path *p)
+{
+  if (v)
+  {
+    pari_free((void*)p->PATH);
+    p->PATH = pari_strdup(v);
+    if (flag == d_INITRC) return gnil;
+    gp_expand_path(p);
+  }
+  if (flag == d_RETURN) return strtoGENstr(p->PATH);
+  if (flag == d_ACKNOWLEDGE)
+    pari_printf("   %s = \"%s\"\n", s, p->PATH);
+  return gnil;
+}
+GEN
+sd_path(const char *v, long flag)
+{ return sd_PATH(v, flag, "path", GP_DATA->path); }
+GEN
+sd_sopath(char *v, int flag)
+{ return sd_PATH(v, flag, "sopath", GP_DATA->sopath); }
+
+static const char *DFT_PRETTYPRINTER = "tex2mail -TeX -noindent -ragged -by_par";
+GEN
+sd_prettyprinter(const char *v, long flag)
+{
+  gp_pp *pp = GP_DATA->pp;
+  if (v && !(GP_DATA->flags & gpd_TEXMACS))
+  {
+    char *old = pp->cmd;
+    int cancel = (!strcmp(v,"no"));
+
+    if (GP_DATA->secure)
+      pari_err(e_MISC,"[secure mode]: can't modify 'prettyprinter' default (to %s)",v);
+    if (!strcmp(v,"yes")) v = DFT_PRETTYPRINTER;
+    if (old && strcmp(old,v) && pp->file)
+    {
+      pariFILE *f;
+      if (cancel) f = NULL;
+      else
+      {
+        f = try_pipe(v, mf_OUT);
+        if (!f)
+        {
+          pari_warn(warner,"broken prettyprinter: '%s'",v);
+          return gnil;
+        }
+      }
+      pari_fclose(pp->file);
+      pp->file = f;
+    }
+    pp->cmd = cancel? NULL: pari_strdup(v);
+    if (old) pari_free(old);
+    if (flag == d_INITRC) return gnil;
+  }
+  if (flag == d_RETURN)
+    return strtoGENstr(pp->cmd? pp->cmd: "");
+  if (flag == d_ACKNOWLEDGE)
+    pari_printf("   prettyprinter = \"%s\"\n",pp->cmd? pp->cmd: "");
+  return gnil;
+}
+
+/* compare entrees s1 s2 according to the associated function name */
+static int
+compare_name(const void *s1, const void *s2) {
+  entree *e1 = *(entree**)s1, *e2 = *(entree**)s2;
+  return strcmp(e1->name, e2->name);
+}
+/* return all entries with class '16' */
+static void
+defaults_list(pari_stack *s)
+{
+  entree *ep;
+  long i;
+  for (i = 0; i < functions_tblsz; i++)
+    for (ep = defaults_hash[i]; ep; ep = ep->next)
+      if (ep->menu == 16) pari_stack_pushp(s, ep);
+}
+/* ep associated to function f of arity 2. Call f(v,flag) */
+static GEN
+call_f2(entree *ep, const char *v, long flag)
+{ return ((GEN (*)(const char*,long))ep->value)(v, flag); }
+GEN
+setdefault(const char *s, const char *v, long flag)
+{
+  entree *ep;
+  if (!s)
+  { /* list all defaults */
+    pari_stack st;
+    entree **L;
+    long i;
+    pari_stack_init(&st, sizeof(*L), (void**)&L);
+    defaults_list(&st);
+    qsort (L, st.n, sizeof(*L), compare_name);
+    for (i = 0; i < st.n; i++) (void)call_f2(L[i], NULL, d_ACKNOWLEDGE);
+    pari_stack_delete(&st);
+    return gnil;
+  }
+  ep = is_entry_intern(s, defaults_hash, NULL);
+  if (!ep)
+  {
+    pari_err(e_MISC,"unknown default: %s",s);
+    return NULL; /* not reached */
+  }
+  return call_f2(ep, v, flag);
+}
+int
+pari_is_default(const char *s)
+{ return !!is_entry_intern(s, defaults_hash, NULL); }
+
+GEN
+default0(const char *a, const char *b) { return setdefault(a,b, b? d_SILENT: d_RETURN); }
+
+/********************************************************************/
+/*                                                                  */
+/*                     INITIALIZE GP_DATA                           */
+/*                                                                  */
+/********************************************************************/
+/* initialize path */
+static void
+init_path(gp_path *path, const char *v)
+{
+  path->PATH = pari_strdup(v);
+  path->dirs = NULL;
+}
+
+/* initialize D->fmt */
+static void
+init_fmt(gp_data *D)
+{
+#ifdef LONG_IS_64BIT
+  static pariout_t DFLT_OUTPUT = { 'g', 38, 1, f_PRETTYMAT, 0 };
+#else
+  static pariout_t DFLT_OUTPUT = { 'g', 28, 1, f_PRETTYMAT, 0 };
+#endif
+  D->fmt = &DFLT_OUTPUT;
+}
+
+/* initialize D->pp */
+static void
+init_pp(gp_data *D)
+{
+  gp_pp *p = D->pp;
+  p->cmd = pari_strdup(DFT_PRETTYPRINTER);
+  p->file = NULL;
+}
+
+gp_data *
+default_gp_data(void)
+{
+  static gp_data __GPDATA, *D = &__GPDATA;
+  static gp_hist __HIST;
+  static gp_pp   __PP;
+  static gp_path __PATH, __SOPATH;
+  static pari_timer __T;
+
+  D->flags       = 0;
+  D->primelimit  = 500000;
+
+  /* GP-specific */
+  D->breakloop   = 1;
+  D->echo        = 0;
+  D->lim_lines   = 0;
+  D->linewrap    = 0;
+  D->recover     = 1;
+  D->chrono      = 0;
+
+  D->strictargs  = 0;
+  D->strictmatch = 1;
+  D->simplify    = 1;
+  D->secure      = 0;
+  D->use_readline= 0;
+  D->T    = &__T;
+  D->hist = &__HIST;
+  D->pp   = &__PP;
+  D->path = &__PATH;
+  D->sopath=&__SOPATH;
+  init_fmt(D);
+  init_hist(D, 5000, 0);
+  init_path(D->path, pari_default_path());
+  init_path(D->sopath, "");
+  init_pp(D);
+  return D;
+}
diff --git a/src/language/default.h b/src/language/default.h
new file mode 100644
index 0000000..2472126
--- /dev/null
+++ b/src/language/default.h
@@ -0,0 +1,34 @@
+/* This file is autogenerated from the database. */
+/* See src/desc/gen_proto */
+/* Do not edit*/
+entree functions_default[]={
+{"TeXstyle",0,(void*)sd_TeXstyle,16,"",""},
+{"colors",0,(void*)sd_colors,16,"",""},
+{"compatible",0,(void*)sd_compatible,16,"",""},
+{"datadir",0,(void*)sd_datadir,16,"",""},
+{"debug",0,(void*)sd_debug,16,"",""},
+{"debugfiles",0,(void*)sd_debugfiles,16,"",""},
+{"debugmem",0,(void*)sd_debugmem,16,"",""},
+{"factor_add_primes",0,(void*)sd_factor_add_primes,16,"",""},
+{"factor_proven",0,(void*)sd_factor_proven,16,"",""},
+{"format",0,(void*)sd_format,16,"",""},
+{"histsize",0,(void*)sd_histsize,16,"",""},
+{"log",0,(void*)sd_log,16,"",""},
+{"logfile",0,(void*)sd_logfile,16,"",""},
+{"nbthreads",0,(void*)sd_nbthreads,16,"",""},
+{"new_galois_format",0,(void*)sd_new_galois_format,16,"",""},
+{"output",0,(void*)sd_output,16,"",""},
+{"parisize",0,(void*)sd_parisize,16,"",""},
+{"path",0,(void*)sd_path,16,"",""},
+{"prettyprinter",0,(void*)sd_prettyprinter,16,"",""},
+{"primelimit",0,(void*)sd_primelimit,16,"",""},
+{"realprecision",0,(void*)sd_realprecision,16,"",""},
+{"secure",0,(void*)sd_secure,16,"",""},
+{"seriesprecision",0,(void*)sd_seriesprecision,16,"",""},
+{"simplify",0,(void*)sd_simplify,16,"",""},
+{"sopath",0,(void*)sd_sopath,16,"",""},
+{"strictargs",0,(void*)sd_strictargs,16,"",""},
+{"strictmatch",0,(void*)sd_strictmatch,16,"",""},
+{"threadsize",0,(void*)sd_threadsize,16,"",""},
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
diff --git a/src/language/es.c b/src/language/es.c
new file mode 100644
index 0000000..0c11602
--- /dev/null
+++ b/src/language/es.c
@@ -0,0 +1,4941 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/**                                                               **/
+/**                 INPUT/OUTPUT SUBROUTINES                      **/
+/**                                                               **/
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+#ifdef _WIN32
+#include <windows.h>
+#include "../systems/mingw/mingw.h"
+#endif
+
+static const char esc = (0x1f & '['); /* C-[ = escape */
+
+typedef struct outString {
+  char *string; /* start of the output buffer */
+  char *end;    /* end of the output buffer */
+  char *cur;   /* current writing place in the output buffer */
+  size_t size; /* buffer size */
+  int use_stack; /* use stack_malloc instead of malloc ? */
+} outString;
+
+typedef void (*OUT_FUN)(GEN, pariout_t *, outString *);
+
+static void bruti_sign(GEN g, pariout_t *T, outString *S, int addsign);
+static void matbruti(GEN g, pariout_t *T, outString *S);
+static void texi_sign(GEN g, pariout_t *T, outString *S, int addsign);
+static char *GENtostr_fun(GEN x, pariout_t *T, OUT_FUN out);
+
+static void bruti(GEN g, pariout_t *T, outString *S)
+{ bruti_sign(g,T,S,1); }
+static void texi(GEN g, pariout_t *T, outString *S)
+{ texi_sign(g,T,S,1); }
+
+void
+pari_ask_confirm(const char *s)
+{
+  if (!cb_pari_ask_confirm)
+    pari_err(e_MISC,"Can't ask for confirmation. Please define cb_pari_ask_confirm()");
+  cb_pari_ask_confirm(s);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        INPUT FILTER                            **/
+/**                                                                **/
+/********************************************************************/
+#define ONE_LINE_COMMENT   2
+#define MULTI_LINE_COMMENT 1
+#define LBRACE '{'
+#define RBRACE '}'
+/* Filter F->s into F->t */
+static char *
+filtre0(filtre_t *F)
+{
+  const int downcase = F->downcase;
+  const char *s = F->s;
+  char *t;
+  char c;
+
+  if (!F->t) F->t = (char*)pari_malloc(strlen(s)+1);
+  t = F->t;
+
+  if (F->more_input == 1) F->more_input = 0;
+
+  while ((c = *s++))
+  {
+    if (F->in_string)
+    {
+      *t++ = c; /* copy verbatim */
+      switch(c)
+      {
+        case '\\': /* in strings, \ is the escape character */
+          if (*s) *t++ = *s++;
+          break;
+
+        case '"': F->in_string = 0;
+      }
+      continue;
+    }
+
+    if (F->in_comment)
+    { /* look for comment's end */
+      if (F->in_comment == MULTI_LINE_COMMENT)
+      {
+        while (c != '*' || *s != '/')
+        {
+          if (!*s)
+          {
+            if (!F->more_input) F->more_input = 1;
+            goto END;
+          }
+          c = *s++;
+        }
+        s++;
+      }
+      else
+        while (c != '\n' && *s) c = *s++;
+      F->in_comment = 0;
+      continue;
+    }
+
+    /* weed out comments and spaces */
+    if (c=='\\' && *s=='\\') { F->in_comment = ONE_LINE_COMMENT; continue; }
+    if (isspace((int)c)) continue;
+    *t++ = downcase? tolower((int)c): c;
+
+    switch(c)
+    {
+      case '/':
+        if (*s == '*') { t--; F->in_comment = MULTI_LINE_COMMENT; }
+        break;
+
+      case '\\':
+        if (!*s) {
+          if (*F->buf->buf == '?') break; /* '?...\' */
+          t--;
+          if (!F->more_input) F->more_input = 1;
+          goto END;
+        }
+        if (*s == '\r') s++; /* DOS */
+        if (*s == '\n') {
+          if (*F->buf->buf == '?') break; /* '?...\' */
+          t--; s++;
+          if (!*s)
+          {
+            if (!F->more_input) F->more_input = 1;
+            goto END;
+          }
+        } /* skip \<CR> */
+        break;
+
+      case '"': F->in_string = 1;
+        break;
+
+      case LBRACE:
+        t--;
+        if (F->wait_for_brace) pari_err_IMPL("embedded braces (in parser)");
+        F->more_input = 2;
+        F->wait_for_brace = 1;
+        break;
+
+      case RBRACE:
+        if (!F->wait_for_brace) pari_err(e_MISC,"unexpected closing brace");
+        F->more_input = 0; t--;
+        F->wait_for_brace = 0;
+        break;
+    }
+  }
+
+  if (t != F->t) /* non empty input */
+  {
+    c = t[-1]; /* last char */
+    if (c == '=')
+    { if (*F->buf->buf != '?') F->more_input = 2; }
+    else if (! F->wait_for_brace) F->more_input = 0;
+    else if (c == RBRACE)       { F->more_input = 0; t--; F->wait_for_brace--;}
+  }
+
+END:
+  F->end = t; *t = 0; return F->t;
+}
+#undef ONE_LINE_COMMENT
+#undef MULTI_LINE_COMMENT
+
+char *
+filtre(const char *s, int downcase)
+{
+  filtre_t T;
+  T.s = s;    T.in_string = 0; T.more_input = 0;
+  T.t = NULL; T.in_comment= 0; T.wait_for_brace = 0;
+  T.downcase = downcase;
+  return filtre0(&T);
+}
+
+void
+init_filtre(filtre_t *F, Buffer *buf)
+{
+  F->buf = buf;
+  F->in_string  = 0;
+  F->in_comment = 0;
+  F->downcase = 0;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        INPUT METHODS                           **/
+/**                                                                **/
+/********************************************************************/
+/* create */
+Buffer *
+new_buffer(void)
+{
+  Buffer *b = (Buffer*) pari_malloc(sizeof(Buffer));
+  b->len = 1024;
+  b->buf = (char*)pari_malloc(b->len);
+  return b;
+}
+/* delete */
+void
+delete_buffer(Buffer *b)
+{
+  if (!b) return;
+  pari_free((void*)b->buf); pari_free((void*)b);
+}
+/* resize */
+void
+fix_buffer(Buffer *b, long newlbuf)
+{
+  b->len = newlbuf;
+  b->buf = (char*)pari_realloc((void*)b->buf, b->len);
+}
+
+static int
+gp_read_stream_buf(FILE *fi, Buffer *b)
+{
+  input_method IM;
+  filtre_t F;
+
+  init_filtre(&F, b);
+
+  IM.file = fi;
+  IM.fgets= &fgets;
+  IM.getline= &file_input;
+  IM.free = 0;
+  return input_loop(&F,&IM);
+}
+
+GEN
+gp_read_stream(FILE *fi)
+{
+  Buffer *b = new_buffer();
+  GEN x = gp_read_stream_buf(fi, b)? readseq(b->buf): gnil;
+  delete_buffer(b); return x;
+}
+
+GEN
+gp_read_file(char *s)
+{
+  GEN x = gnil;
+  FILE *f = switchin(s);
+  if (file_is_binary(f)) {
+    int junk;
+    x = readbin(s,f, &junk);
+  } else {
+    Buffer *b = new_buffer();
+    x = gnil;
+    for (;;) {
+      if (!gp_read_stream_buf(f, b)) break;
+      if (*(b->buf)) x = readseq(b->buf);
+    }
+    delete_buffer(b);
+  }
+  popinfile(); return x;
+}
+
+GEN
+gp_readvec_stream(FILE *fi)
+{
+  pari_sp ltop = avma;
+  Buffer *b = new_buffer();
+  long i = 1, n = 16;
+  GEN z = cgetg(n+1,t_VEC);
+  for(;;)
+  {
+    if (!gp_read_stream_buf(fi, b)) break;
+    if (!*(b->buf)) continue;
+    if (i>n)
+    {
+      if (DEBUGLEVEL) err_printf("gp_readvec_stream: reaching %ld entries\n",n);
+      n <<= 1;
+      z = vec_lengthen(z,n);
+    }
+    gel(z,i++) = readseq(b->buf);
+  }
+  if (DEBUGLEVEL) err_printf("gp_readvec_stream: found %ld entries\n",i-1);
+  setlg(z,i); delete_buffer(b);
+  return gerepilecopy(ltop,z);
+}
+
+GEN
+gp_readvec_file(char *s)
+{
+  GEN x = NULL;
+  FILE *f = switchin(s);
+  if (file_is_binary(f)) {
+    int junk;
+    x = readbin(s,f,&junk);
+  } else
+    x = gp_readvec_stream(f);
+  popinfile(); return x;
+}
+
+char *
+file_getline(Buffer *b, char **s0, input_method *IM)
+{
+  const ulong MAX = (1UL << 31) - 1;
+  ulong used0, used;
+
+  **s0 = 0; /* paranoia */
+  used0 = used = *s0 - b->buf;
+  for(;;)
+  {
+    ulong left = b->len - used, l, read;
+    char *s;
+
+    /* If little space left, double the buffer size before next read. */
+    if (left < 512)
+    {
+      fix_buffer(b, b->len << 1);
+      left = b->len - used;
+      *s0 = b->buf + used0;
+    }
+    /* # of chars read by fgets is an int; be careful */
+    read = minuu(left, MAX);
+    s = b->buf + used;
+    if (! IM->fgets(s, (int)read, IM->file)) return **s0? *s0: NULL; /* EOF */
+
+    l = strlen(s);
+    if (l+1 < read || s[l-1] == '\n') return *s0; /* \n */
+    used += l;
+  }
+}
+
+/* Read from file (up to '\n' or EOF) and copy at s0 (points in b->buf) */
+char *
+file_input(char **s0, int junk, input_method *IM, filtre_t *F)
+{
+  (void)junk;
+  return file_getline(F->buf, s0, IM);
+}
+
+/* Read a "complete line" and filter it. Return: 0 if EOF, 1 otherwise */
+int
+input_loop(filtre_t *F, input_method *IM)
+{
+  Buffer *b = (Buffer*)F->buf;
+  char *to_read, *s = b->buf;
+
+  /* read first line */
+  if (! (to_read = IM->getline(&s,1, IM, F)) ) { check_filtre(F); return 0; }
+
+  /* buffer is not empty, init filter */
+  F->in_string = 0;
+  F->more_input= 0;
+  F->wait_for_brace = 0;
+  for(;;)
+  {
+    F->s = to_read;
+    F->t = s;
+    (void)filtre0(F); /* pre-processing of line, read by previous call to IM->getline */
+    if (IM->free) pari_free(to_read);
+    if (! F->more_input) break;
+
+    /* read continuation line */
+    s = F->end;
+    to_read = IM->getline(&s,0, IM, F);
+    if (!to_read) break;
+  }
+  return 1;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                  GENERAL PURPOSE PRINTING                      **/
+/**                                                                **/
+/********************************************************************/
+PariOUT *pariOut, *pariErr;
+static void
+_fputs(const char *s, FILE *f ) {
+#ifdef _WIN32
+   win32_ansi_fputs(s, f);
+#else
+  fputs(s, f);
+#endif
+}
+static void
+_putc_log(char c) { if (pari_logfile) (void)putc(c, pari_logfile); }
+static void
+_puts_log(const char *s)
+{
+  FILE *f = pari_logfile;
+  const char *p;
+  if (!f) return;
+  if (logstyle != logstyle_color)
+    while ( (p = strchr(s, esc)) )
+    { /* skip ANSI color escape sequence */
+      if ( p!=s ) fwrite(s, 1, p-s, f);
+      s = strchr(p, 'm');
+      if (!s) return;
+      s++;
+    }
+  fputs(s, f);
+}
+static void
+_flush_log(void) { (void)fflush(pari_logfile); }
+
+static void
+normalOutC(char c) { putc(c, pari_outfile); _putc_log(c); }
+static void
+normalOutS(const char *s) { _fputs(s, pari_outfile); _puts_log(s); }
+static void
+normalOutF(void) { fflush(pari_outfile); _flush_log(); }
+static PariOUT defaultOut = {normalOutC, normalOutS, normalOutF};
+
+static void
+normalErrC(char c) { putc(c, pari_errfile); _putc_log(c); }
+static void
+normalErrS(const char *s) { _fputs(s, pari_errfile); _puts_log(s); }
+static void
+normalErrF(void) { fflush(pari_errfile); _flush_log(); }
+static PariOUT defaultErr = {normalErrC, normalErrS, normalErrF};
+
+/**                         GENERIC PRINTING                       **/
+void
+resetout(int initerr)
+{
+  pariOut = &defaultOut;
+  if (initerr) pariErr = &defaultErr;
+}
+void
+initout(int initerr)
+{
+  pari_infile = stdin;
+  pari_outfile = stdout;
+  pari_errfile = stderr;
+  resetout(initerr);
+}
+
+static int last_was_newline = 1;
+
+static void
+set_last_newline(char c) { last_was_newline = (c == '\n'); }
+
+void
+out_putc(PariOUT *out, char c) { set_last_newline(c); out->putch(c); }
+void
+pari_putc(char c) { out_putc(pariOut, c); }
+
+void
+out_puts(PariOUT *out, const char *s) {
+  if (*s) {  set_last_newline(s[strlen(s)-1]); out->puts(s); }
+}
+void
+pari_puts(const char *s) { out_puts(pariOut, s); }
+
+int
+pari_last_was_newline(void) { return last_was_newline; }
+void
+pari_set_last_newline(int last) { last_was_newline = last; }
+
+void
+pari_flush(void) { pariOut->flush(); }
+
+void
+err_flush(void) { pariErr->flush(); }
+
+static GEN
+log10_2(void)
+{ return divrr(mplog2(LOWDEFAULTPREC), mplog(utor(10,LOWDEFAULTPREC))); }
+
+/* e binary exponent, return exponent in base ten */
+static long
+ex10(long e) {
+  pari_sp av;
+  GEN z;
+  if (e >= 0) {
+    if (e < 1e15) return (long)(e*LOG10_2);
+    av = avma; z = mulur(e, log10_2());
+    z = floorr(z); e = itos(z);
+  }
+  else /* e < 0 */
+  {
+    if (e > -1e15) return (long)(-(-e*LOG10_2)-1);
+    av = avma; z = mulsr(e, log10_2());
+    z = floorr(z); e = itos(z) - 1;
+  }
+  avma = av; return e;
+}
+
+static char *
+zeros(char *b, long nb) { while (nb-- > 0) *b++ = '0'; *b = 0; return b; }
+
+/* # of decimal digits, assume l > 0 */
+static long
+numdig(ulong l)
+{
+  if (l < 100000)
+  {
+    if (l < 100)    return (l < 10)? 1: 2;
+    if (l < 10000)  return (l < 1000)? 3: 4;
+    return 5;
+  }
+  if (l < 10000000)   return (l < 1000000)? 6: 7;
+  if (l < 1000000000) return (l < 100000000)? 8: 9;
+  return 10;
+}
+
+/* let ndig <= 9, x < 10^ndig, write in p[-ndig..-1] the decimal digits of x */
+static void
+utodec(char *p, ulong x, long ndig)
+{
+  switch(ndig)
+  {
+    case  9: *--p = x % 10 + '0'; x = x/10;
+    case  8: *--p = x % 10 + '0'; x = x/10;
+    case  7: *--p = x % 10 + '0'; x = x/10;
+    case  6: *--p = x % 10 + '0'; x = x/10;
+    case  5: *--p = x % 10 + '0'; x = x/10;
+    case  4: *--p = x % 10 + '0'; x = x/10;
+    case  3: *--p = x % 10 + '0'; x = x/10;
+    case  2: *--p = x % 10 + '0'; x = x/10;
+    case  1: *--p = x % 10 + '0'; x = x/10;
+  }
+}
+
+/* convert abs(x) != 0 to str. Prepend '-' if (sx < 0) */
+static char *
+itostr_sign(GEN x, int sx, long *len)
+{
+  long l, d;
+  ulong *res = convi(x, &l);
+  /* l 9-digits words (< 10^9) + (optional) sign + \0 */
+  char *s = (char*)new_chunk(nchar2nlong(l*9 + 1 + 1)), *t = s;
+
+  if (sx < 0) *t++ = '-';
+  d = numdig(*--res); t += d; utodec(t, *res, d);
+  while (--l > 0) { t += 9; utodec(t, *--res, 9); }
+  *t = 0; *len = t - s; return s;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        WRITE A REAL NUMBER                     **/
+/**                                                                **/
+/********************************************************************/
+/* 19 digits (if 64 bits, at most 2^60-1) + 1 sign */
+static const long MAX_EXPO_LEN = 20;
+
+/* write z to buf, inserting '.' at 'point', 0 < point < strlen(z) */
+static void
+wr_dec(char *buf, char *z, long point)
+{
+  char *s = buf + point;
+  strncpy(buf, z, point); /* integer part */
+  *s++ = '.'; z += point;
+  while ( (*s++ = *z++) ) /* empty */;
+}
+
+static char *
+zerotostr(void)
+{
+  char *s = (char*)new_chunk(1);
+  s[0] = '0';
+  s[1] = 0; return s;
+}
+
+/* write a real 0 of exponent ex in format f */
+static char *
+real0tostr_width_frac(long width_frac)
+{
+  char *buf, *s;
+  if (width_frac == 0) return zerotostr();
+  buf = s = stack_malloc(width_frac + 3);
+  *s++ = '0';
+  *s++ = '.';
+  (void)zeros(s, width_frac);
+  return buf;
+}
+
+/* write a real 0 of exponent ex */
+static char *
+real0tostr(long ex, char format, char exp_char, long wanted_dec)
+{
+  char *buf, *buf0;
+
+  if (format == 'f') {
+    long width_frac = wanted_dec;
+    if (width_frac < 0) width_frac = (ex >= 0)? 0: (long)(-ex * LOG10_2);
+    return real0tostr_width_frac(width_frac);
+  } else {
+    buf0 = buf = stack_malloc(3 + MAX_EXPO_LEN + 1);
+    *buf++ = '0';
+    *buf++ = '.';
+    *buf++ = exp_char;
+    sprintf(buf, "%ld", ex10(ex) + 1);
+  }
+  return buf0;
+}
+
+/* format f, width_frac >= 0: number of digits in fractional part, */
+static char *
+absrtostr_width_frac(GEN x, int width_frac)
+{
+  long beta, ls, point, lx, sx = signe(x);
+  char *s, *buf;
+  GEN z;
+
+  if (!sx) return real0tostr_width_frac(width_frac);
+
+  /* x != 0 */
+  lx = realprec(x);
+  beta = width_frac;
+  if (beta) /* >= 0 */
+  { /* z = |x| 10^beta, 10^b = 5^b * 2^b, 2^b goes into exponent */
+    if (beta > 4e9) lx++;
+    z = mulrr(x, rpowuu(5UL, (ulong)beta, lx+1));
+    setsigne(z, 1);
+    shiftr_inplace(z, beta);
+  }
+  else
+    z = mpabs(x);
+  z = roundr_safe(z);
+  if (!signe(z)) return real0tostr_width_frac(width_frac);
+
+  s = itostr_sign(z, 1, &ls); /* ls > 0, number of digits in s */
+  point = ls - beta; /* position of . in s; <= ls, may be < 0 */
+  if (point > 0) /* write integer_part.fractional_part */
+  {
+    /* '.', trailing \0 */
+    buf = stack_malloc( ls + 1+1 );
+    if (ls == point)
+      strcpy(buf, s); /* no '.' */
+    else
+      wr_dec(buf, s, point);
+  } else { /* point <= 0, fractional part must be written */
+    char *t;
+    /* '0', '.', zeroes, trailing \0 */
+    buf = t = stack_malloc( 1 + 1 - point + ls + 1 );
+    *t++ = '0';
+    *t++ = '.';
+    t = zeros(t, -point);
+    strcpy(t, s);
+  }
+  return buf;
+}
+
+/* Return t_REAL |x| in floating point format.
+ * Allocate freely, the caller must clean the stack.
+ *   FORMAT: E/e (exponential), F/f (floating point), G/g
+ *   wanted_dec: number of significant digits to print (all if < 0).
+ */
+static char *
+absrtostr(GEN x, int sp, char FORMAT, long wanted_dec)
+{
+  const char format = (char)tolower((int)FORMAT), exp_char = (format == FORMAT)? 'e': 'E';
+  long beta, ls, point, lx, sx = signe(x), ex = expo(x);
+  char *s, *buf, *buf0;
+  GEN z;
+
+  if (!sx) return real0tostr(ex, format, exp_char, wanted_dec);
+
+  /* x != 0 */
+  lx = realprec(x);
+  if (wanted_dec >= 0)
+  { /* reduce precision if possible */
+    long w = ndec2prec(wanted_dec); /* digits -> pari precision in words */
+    if (lx > w) lx = w; /* truncature with guard, no rounding */
+  }
+  beta = ex10(prec2nbits(lx) - ex);
+  if (beta)
+  { /* z = |x| 10^beta, 10^b = 5^b * 2^b, 2^b goes into exponent */
+    if (beta > 0)
+    {
+      if (beta > 18) { lx++; x = rtor(x, lx); }
+      z = mulrr(x, rpowuu(5UL, (ulong)beta, lx+1));
+    }
+    else
+    {
+      if (beta < -18) { lx++; x = rtor(x, lx); }
+      z = divrr(x, rpowuu(5UL, (ulong)-beta, lx+1));
+    }
+    setsigne(z, 1);
+    shiftr_inplace(z, beta);
+  }
+  else
+    z = x;
+  z = roundr_safe(z);
+  if (!signe(z)) return real0tostr(ex, format, exp_char, wanted_dec);
+
+  s = itostr_sign(z, 1, &ls); /* ls > 0, number of digits in s */
+  if (wanted_dec < 0)
+    wanted_dec = ls;
+  else if (ls > wanted_dec)
+  {
+    beta -= ls - wanted_dec;
+    ls = wanted_dec;
+    if (s[ls] >= '5') /* round up */
+    {
+      long i;
+      for (i = ls-1; i >= 0; s[i--] = '0')
+        if (++s[i] <= '9') break;
+      if (i < 0) { s[0] = '1'; beta--; }
+    }
+    s[ls] = 0;
+  }
+
+  /* '.', " E", exponent, trailing \0 */
+  buf0 = buf = stack_malloc( ls + 1+2+MAX_EXPO_LEN+1 );
+  point = ls - beta; /* position of . in s; < 0 or > 0 */
+  if (beta <= 0 || format == 'e' || (format == 'g' && point-1 < -4))
+  { /* e format */
+    wr_dec(buf, s, 1); buf += ls + 1;
+    if (sp) *buf++ = ' ';
+    *buf++ = exp_char;
+    sprintf(buf, "%ld", point-1);
+  } else { /* f format */
+    if (point > 0) /* write integer_part.fractional_part */
+      wr_dec(buf, s, point); /* point < ls since beta > 0 */
+    else { /* point <= 0, write fractional part */
+      *buf++ = '0';
+      *buf++ = '.';
+      buf = zeros(buf, -point);
+      strcpy(buf, s);
+    }
+  }
+  return buf0;
+}
+
+/* vsnprintf implementation rewritten from snprintf.c to be found at
+ *
+ * http://www.nersc.gov/~scottc/misc/docs/snort-2.1.1-RC1/snprintf_8c-source.html
+ * The original code was
+ *   Copyright (C) 1998-2002 Martin Roesch <roesch at sourcefire.com>
+ * available under the terms of the GNU GPL version 2 or later. It
+ * was itself adapted from an original version by Patrick Powell. */
+
+/* Modifications for format %Ps: R.Butel IMB/CNRS 2007/12/03 */
+
+/* l = old len, L = new len */
+static void
+str_alloc0(outString *S, long l, long L)
+{
+  char *s;
+  if (S->use_stack)
+  {
+    s = stack_malloc(L);
+    memcpy(s, S->string, l);
+  }
+  else
+    s = (char*)pari_realloc((void*)S->string, L);
+  S->string = s;
+  S->cur = s + l;
+  S->end = s + L;
+  S->size = L;
+}
+/* make sure S is large enough to write l further words (<= l * 20 chars).
+ * To avoid automatic extension in between av = avma / avma = av pairs
+ * [ would destroy S->string if (S->use_stack) ] */
+static void
+str_alloc(outString *S, long l)
+{
+  l *= 20;
+  if (S->end - S->cur <= l)
+    str_alloc0(S, S->cur - S->string, S->size + maxss(S->size, l));
+}
+static void
+str_putc(outString *S, char c) {
+  *S->cur++ = c;
+  if (S->cur == S->end) str_alloc0(S, S->size, S->size << 1);
+}
+
+static void
+str_init(outString *S, int use_stack)
+{
+  char *s;
+  S->size = 1024;
+  S->use_stack = use_stack;
+  if (S->use_stack)
+    s = (char*)stack_malloc(S->size);
+  else
+    s = (char*)pari_malloc(S->size);
+  S->string = S->cur = s;
+  S->end = S->string + S->size;
+}
+static void
+str_puts(outString *S, const char *s) { while (*s) str_putc(S, *s++); }
+
+static void
+str_putscut(outString *S, const char *str, int cut)
+{
+  if (cut < 0) str_puts(S, str);
+  else {
+    while (*str && cut-- > 0) str_putc(S, *str++);
+  }
+}
+
+/* lbuf = strlen(buf), len < 0: unset */
+static void
+outpad(outString *S, const char *buf, long lbuf, int sign, long ljust, long len, long zpad)
+{
+  long padlen = len - lbuf;
+  if (padlen < 0) padlen = 0;
+  if (ljust) padlen = -padlen;
+  if (padlen > 0)
+  {
+    if (zpad) {
+      if (sign) { str_putc(S, sign); --padlen; }
+      while (padlen > 0) { str_putc(S, '0'); --padlen; }
+    }
+    else
+    {
+      if (sign) --padlen;
+      while (padlen > 0) { str_putc(S, ' '); --padlen; }
+      if (sign) str_putc(S, sign);
+    }
+  } else
+    if (sign) str_putc(S, sign);
+  str_puts(S, buf);
+  while (padlen < 0) { str_putc(S, ' '); ++padlen; }
+}
+
+/* len < 0 or maxwidth < 0: unset */
+static void
+fmtstr(outString *S, const char *buf, int ljust, int len, int maxwidth)
+{
+  int padlen, lbuf = strlen(buf);
+
+  if (maxwidth >= 0 && lbuf > maxwidth) lbuf = maxwidth;
+
+  padlen = len - lbuf;
+  if (padlen < 0) padlen = 0;
+  if (ljust) padlen = -padlen;
+  while (padlen > 0) { str_putc(S, ' '); --padlen; }
+  str_putscut(S, buf, maxwidth);
+  while (padlen < 0) { str_putc(S, ' '); ++padlen; }
+}
+
+/* abs(base) is 8, 10, 16. If base < 0, some "alternate" form
+ * -- print hex in uppercase
+ * -- prefix octal with 0
+ * signvalue = -1: unsigned, otherwise ' ' or '+' */
+static void
+fmtnum(outString *S, long lvalue, GEN gvalue, int base, int signvalue,
+       int ljust, int len, int zpad)
+{
+  int caps;
+  char *buf0, *buf;
+  long lbuf, mxl;
+  GEN uvalue = NULL;
+  ulong ulvalue = 0;
+  pari_sp av = avma; /* Assume !S->use_stack */
+
+  if (gvalue)
+  {
+    long s, l;
+    if (typ(gvalue) != t_INT) {
+      long i, j, h;
+      l = lg(gvalue);
+      switch(typ(gvalue))
+      {
+        case t_VEC:
+          str_putc(S, '[');
+          for (i = 1; i < l; i++)
+          {
+            fmtnum(S, 0, gel(gvalue,i), base, signvalue, ljust,len,zpad);
+            if (i < l-1) str_putc(S, ',');
+          }
+          str_putc(S, ']');
+          return;
+        case t_COL:
+          str_putc(S, '[');
+          for (i = 1; i < l; i++)
+          {
+            fmtnum(S, 0, gel(gvalue,i), base, signvalue, ljust,len,zpad);
+            if (i < l-1) str_putc(S, ',');
+          }
+          str_putc(S, ']');
+          str_putc(S, '~');
+          return;
+        case t_MAT:
+          if (l == 1)
+            str_puts(S, "[;]");
+          else
+          {
+            h = lgcols(gvalue);
+            for (i=1; i<l; i++)
+            {
+              str_putc(S, '[');
+              for (j=1; j<h; j++)
+              {
+                fmtnum(S, 0, gcoeff(gvalue,i,j), base, signvalue, ljust,len,zpad);
+                if (j<h-1) str_putc(S, ' ');
+              }
+              str_putc(S, ']');
+              str_putc(S, '\n');
+              if (i<l-1) str_putc(S, '\n');
+            }
+          }
+          return;
+      }
+      gvalue = gfloor( simplify_shallow(gvalue) );
+      if (typ(gvalue) != t_INT)
+        pari_err(e_MISC,"not a t_INT in integer format conversion: %Ps", gvalue);
+    }
+    s = signe(gvalue);
+    if (!s) { lbuf = 1; buf = zerotostr(); signvalue = 0; goto END; }
+
+    l = lgefint(gvalue);
+    uvalue = gvalue;
+    if (signvalue < 0)
+    {
+      if (s < 0) uvalue = addii(int2n(bit_accuracy(l)), gvalue);
+      signvalue = 0;
+    }
+    else
+    {
+      if (s < 0) { signvalue = '-'; uvalue = absi(uvalue); }
+    }
+    mxl = (l-2)* 22 + 1; /* octal at worst; 22 octal chars per 64bit word */
+  } else {
+    ulvalue = lvalue;
+    if (signvalue < 0)
+      signvalue = 0;
+    else
+      if (lvalue < 0) { signvalue = '-'; ulvalue = - lvalue; }
+    mxl = 22 + 1; /* octal at worst; 22 octal chars to write down 2^64 - 1 */
+  }
+  if (base > 0) caps = 0; else { caps = 1; base = -base; }
+
+  buf0 = buf = stack_malloc(mxl) + mxl; /* fill from the right */
+  *--buf = 0; /* trailing \0 */
+  if (gvalue) {
+    if (base == 10) {
+      long i, l, cnt;
+      ulong *larray = convi(uvalue, &l);
+      larray -= l;
+      for (i = 0; i < l; i++) {
+        cnt = 0;
+        ulvalue = larray[i];
+        do {
+          *--buf = '0' + ulvalue%10;
+          ulvalue = ulvalue / 10;
+          cnt++;
+        } while (ulvalue);
+        if (i + 1 < l)
+          for (;cnt<9;cnt++) *--buf = '0';
+      }
+    } else if (base == 16) {
+      long i, l = lgefint(uvalue);
+      GEN up = int_LSW(uvalue);
+      for (i = 2; i < l; i++, up = int_nextW(up)) {
+        ulong ucp = (ulong)*up;
+        long j;
+        for (j=0; j < BITS_IN_LONG/4; j++) {
+          unsigned char cv = ucp & 0xF;
+          *--buf = (caps? "0123456789ABCDEF":"0123456789abcdef")[cv];
+          ucp >>= 4;
+          if (ucp == 0 && i+1 == l) break;
+        }
+      } /* loop on hex digits in word */
+    } else if (base == 8) {
+      long i, l = lgefint(uvalue);
+      GEN up = int_LSW(uvalue);
+      ulong rem = 0;
+      int shift = 0;
+      int mask[3] = {0, 1, 3};
+      for (i = 2; i < l; i++, up = int_nextW(up)) {
+        ulong ucp = (ulong)*up;
+        long j, ldispo = BITS_IN_LONG;
+        if (shift) { /* 0, 1 or 2 */
+          unsigned char cv = ((ucp & mask[shift]) <<(3-shift)) + rem;
+          *--buf = "01234567"[cv];
+          ucp >>= shift;
+          ldispo -= shift;
+        };
+        shift = (shift + 3 - BITS_IN_LONG % 3) % 3;
+        for (j=0; j < BITS_IN_LONG/3; j++) {
+          unsigned char cv = ucp & 0x7;
+          if (ucp == 0 && i+1 == l) { rem = 0; break; };
+          *--buf = "01234567"[cv];
+          ucp >>= 3;
+          ldispo -= 3;
+          rem = ucp;
+          if (ldispo < 3) break;
+        }
+      } /* loop on hex digits in word */
+      if (rem) *--buf = "01234567"[rem];
+    }
+  } else { /* not a gvalue, thus a standard integer */
+    do {
+      *--buf = (caps? "0123456789ABCDEF":"0123456789abcdef")[ulvalue % (unsigned)base ];
+      ulvalue /= (unsigned)base;
+    } while (ulvalue);
+  }
+  /* leading 0 if octal and alternate # form */
+  if (caps && base == 8) *--buf = '0';
+  lbuf = (buf0 - buf) - 1;
+END:
+  outpad(S, buf, lbuf, signvalue, ljust, len, zpad);
+  avma = av;
+}
+
+static GEN
+v_get_arg(GEN arg_vector, int *index, const char *save_fmt)
+{
+  if (*index >= lg(arg_vector))
+    pari_err(e_MISC, "missing arg %d for printf format '%s'", *index, save_fmt);
+  return gel(arg_vector, (*index)++);
+}
+
+static int
+dosign(int blank, int plus)
+{
+  if (plus) return('+');
+  if (blank) return(' ');
+  return 0;
+}
+
+/* x * 10 + 'digit whose char value is ch'. Do not check for overflow */
+static int
+shift_add(int x, int ch)
+{
+  if (x < 0) /* was unset */
+    x = ch - '0';
+  else
+    x = x*10 + ch - '0';
+  return x;
+}
+
+static long
+get_sigd(GEN gvalue, char ch, int maxwidth)
+{
+  long sigd, e;
+  if (maxwidth < 0) return prec2ndec(precreal);
+  switch(ch)
+  {
+    case 'E':
+    case 'e': sigd = maxwidth+1; break;
+    case 'F':
+    case 'f':
+      e = gexpo(gvalue);
+      if (e == -(long)HIGHEXPOBIT) /* exact 0 */
+        sigd = 0;
+      else
+        sigd = ex10(e) + 1 + maxwidth;
+      break;
+    /* 'g', 'G' */
+    default : sigd = maxwidth? maxwidth: 1; break;
+  }
+  return sigd;
+}
+
+static void
+fmtreal(outString *S, GEN gvalue, int space, int signvalue, int FORMAT,
+        int maxwidth, int ljust, int len, int zpad)
+{
+  pari_sp av = avma; /* Assume !S->use_stack */
+  long sigd;
+  char *buf;
+
+  if (typ(gvalue) == t_REAL)
+    sigd = get_sigd(gvalue, FORMAT, maxwidth);
+  else
+  {
+    long i, j, h, l = lg(gvalue);
+    switch(typ(gvalue))
+    {
+      case t_VEC:
+        str_putc(S, '[');
+        for (i = 1; i < l; i++)
+        {
+          fmtreal(S, gel(gvalue,i), space, signvalue, FORMAT, maxwidth,
+                  ljust,len,zpad);
+          if (i < l-1) str_putc(S, ',');
+        }
+        str_putc(S, ']');
+        return;
+      case t_COL:
+        str_putc(S, '[');
+        for (i = 1; i < l; i++)
+        {
+          fmtreal(S, gel(gvalue,i), space, signvalue, FORMAT, maxwidth,
+                  ljust,len,zpad);
+          if (i < l-1) str_putc(S, ',');
+        }
+        str_putc(S, ']');
+        str_putc(S, '~');
+        return;
+      case t_MAT:
+        if (l == 1)
+          str_puts(S, "[;]");
+        else
+        {
+          h = lgcols(gvalue);
+          for (i=1; i<l; i++)
+          {
+            str_putc(S, '[');
+            for (j=1; j<h; j++)
+            {
+              fmtreal(S, gcoeff(gvalue,j,i), space, signvalue, FORMAT, maxwidth,
+                      ljust,len,zpad);
+              if (j<h-1) str_putc(S, ' ');
+            }
+            str_putc(S, ']');
+            str_putc(S, '\n');
+            if (i<l-1) str_putc(S, '\n');
+          }
+        }
+        return;
+    }
+    sigd = get_sigd(gvalue, FORMAT, maxwidth);
+    gvalue = gtofp(gvalue, ndec2prec(sigd));
+    if (typ(gvalue) != t_REAL)
+      pari_err(e_MISC,"impossible conversion to t_REAL: %Ps",gvalue);
+  }
+  if ((FORMAT == 'f' || FORMAT == 'F') && maxwidth >= 0)
+    buf = absrtostr_width_frac(gvalue, maxwidth);
+  else
+    buf = absrtostr(gvalue, space, FORMAT, sigd);
+  if (signe(gvalue) < 0) signvalue = '-';
+  outpad(S, buf, strlen(buf), signvalue, ljust, len, zpad);
+  avma = av;
+}
+/* format handling "inspired" by the standard draft at
+-- http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf pages 274ff
+ * fmt is a standard printf format, except 'P' is a "length modifier"
+ * allowing GEN arguments. Use either the arg_vector or (if NULL) the va_list */
+static char *
+sm_dopr(const char *fmt, GEN arg_vector, va_list args)
+{
+  int GENflag = 0, longflag = 0, pointflag = 0;
+  int print_plus, print_blank, with_sharp, ch, ljust, len, maxwidth, zpad;
+  long lvalue;
+  int index = 1;
+  GEN gvalue;
+  const char *save_fmt = fmt;
+  outString __S, *S = &__S;
+
+  str_init(S, 0);
+
+  while ((ch = *fmt++) != '\0') {
+    switch(ch) {
+      case '%':
+        ljust = zpad = 0;
+        len = maxwidth = -1;
+        GENflag = longflag = pointflag = 0;
+        print_plus = print_blank = with_sharp = 0;
+nextch:
+        ch = *fmt++;
+        switch(ch) {
+          case 0:
+            pari_err(e_MISC, "printf: end of format");
+/*------------------------------------------------------------------------
+                             -- flags
+------------------------------------------------------------------------*/
+          case '-':
+            ljust = 1;
+            goto nextch;
+          case '+':
+            print_plus = 1;
+            goto nextch;
+          case '#':
+            with_sharp = 1;
+            goto nextch;
+          case ' ':
+            print_blank = 1;
+            goto nextch;
+          case '0':
+            /* appears as a flag: set zero padding */
+            if (len < 0 && !pointflag) { zpad = '0'; goto nextch; }
+
+            /* else part of a field width or precision */
+            /* fall through */
+/*------------------------------------------------------------------------
+                       -- maxwidth or precision
+------------------------------------------------------------------------*/
+          case '1':
+          case '2':
+          case '3':
+          case '4':
+          case '5':
+          case '6':
+          case '7':
+          case '8':
+          case '9':
+            if (pointflag)
+              maxwidth = shift_add(maxwidth, ch);
+            else
+              len = shift_add(len, ch);
+            goto nextch;
+
+          case '*':
+          {
+            int *t = pointflag? &maxwidth: &len;
+            if (arg_vector)
+              *t = (int)gtolong( v_get_arg(arg_vector, &index, save_fmt) );
+            else
+              *t = va_arg(args, int);
+            goto nextch;
+          }
+          case '.':
+            if (pointflag)
+              pari_err(e_MISC, "two '.' in conversion specification");
+            pointflag = 1;
+            goto nextch;
+/*------------------------------------------------------------------------
+                       -- length modifiers
+------------------------------------------------------------------------*/
+          case 'l':
+            if (GENflag)
+              pari_err(e_MISC, "P/l length modifiers in the same conversion");
+            if (longflag)
+              pari_err_IMPL( "ll length modifier in printf");
+            longflag = 1;
+            goto nextch;
+          case 'P':
+            if (longflag)
+              pari_err(e_MISC, "P/l length modifiers in the same conversion");
+            if (GENflag)
+              pari_err(e_MISC, "'P' length modifier appears twice");
+            GENflag = 1;
+            goto nextch;
+          case 'h': /* dummy: va_arg promotes short into int */
+            goto nextch;
+/*------------------------------------------------------------------------
+                       -- conversions
+------------------------------------------------------------------------*/
+          case 'u': /* not a signed conversion: print_(blank|plus) ignored */
+#define get_num_arg() \
+  if (arg_vector) { \
+    lvalue = 0; \
+    gvalue = v_get_arg(arg_vector, &index, save_fmt); \
+  } else { \
+    if (GENflag) { \
+      lvalue = 0; \
+      gvalue = va_arg(args, GEN); \
+    } else { \
+      lvalue = longflag? va_arg(args, long): va_arg(args, int); \
+      gvalue = NULL; \
+    } \
+  }
+            get_num_arg();
+            fmtnum(S, lvalue, gvalue, 10, -1, ljust, len, zpad);
+            break;
+          case 'o': /* not a signed conversion: print_(blank|plus) ignored */
+            get_num_arg();
+            fmtnum(S, lvalue, gvalue, with_sharp? -8: 8, -1, ljust, len, zpad);
+            break;
+          case 'd':
+          case 'i':
+            get_num_arg();
+            fmtnum(S, lvalue, gvalue, 10,
+                   dosign(print_blank, print_plus), ljust, len, zpad);
+            break;
+          case 'p':
+            str_putc(S, '0'); str_putc(S, 'x');
+            if (arg_vector)
+              lvalue = (long)v_get_arg(arg_vector, &index, save_fmt);
+            else
+              lvalue = (long)va_arg(args, void*);
+            fmtnum(S, lvalue, NULL, 16, -1, ljust, len, zpad);
+            break;
+          case 'x': /* not a signed conversion: print_(blank|plus) ignored */
+            if (with_sharp) { str_putc(S, '0'); str_putc(S, 'x'); }
+            get_num_arg();
+            fmtnum(S, lvalue, gvalue, 16, -1, ljust, len, zpad);
+            break;
+          case 'X': /* not a signed conversion: print_(blank|plus) ignored */
+            if (with_sharp) { str_putc(S, '0'); str_putc(S, 'X'); }
+            get_num_arg();
+            fmtnum(S, lvalue, gvalue,-16, -1, ljust, len, zpad);
+            break;
+          case 's':
+          {
+            char *strvalue;
+            int tofree = 0;
+
+            if (arg_vector) {
+              gvalue = v_get_arg(arg_vector, &index, save_fmt);
+              strvalue = NULL;
+            } else {
+              if (GENflag) {
+                gvalue = va_arg(args, GEN);
+                strvalue = NULL;
+              } else {
+                gvalue = NULL;
+                strvalue = va_arg(args, char *);
+              }
+            }
+            if (gvalue)
+            {
+              if (typ(gvalue) == t_STR)
+                strvalue = GSTR(gvalue);
+              else
+              {
+                strvalue = GENtostr_fun(gvalue, GP_DATA->fmt, bruti);
+                tofree = 1;
+              }
+            }
+            fmtstr(S, strvalue, ljust, len, maxwidth);
+            if (tofree) pari_free(strvalue);
+            break;
+          }
+          case 'c':
+            if (arg_vector) {
+              gvalue = v_get_arg(arg_vector, &index, save_fmt);
+              ch = (int)gtolong(gvalue);
+            } else {
+              if (GENflag)
+                ch = (int)gtolong( va_arg(args,GEN) );
+              else
+                ch = va_arg(args, int);
+            }
+            str_putc(S, ch);
+            break;
+
+          case '%':
+            str_putc(S, ch);
+            continue;
+          case 'g':
+          case 'G':
+          case 'e':
+          case 'E':
+          case 'f':
+          case 'F':
+          {
+            pari_sp av = avma;
+            if (arg_vector)
+              gvalue = simplify_shallow( v_get_arg(arg_vector, &index, save_fmt) );
+            else {
+              if (GENflag)
+                gvalue = simplify_shallow( va_arg(args, GEN) );
+              else
+                gvalue = dbltor( va_arg(args, double) );
+            }
+            fmtreal(S, gvalue, GP_DATA->fmt->sp, dosign(print_blank,print_plus),
+                    ch, maxwidth, ljust, len, zpad);
+            avma = av; break;
+          }
+          default:
+            pari_err(e_MISC, "invalid conversion or specification %c in format `%s'", ch, save_fmt);
+        } /* second switch on ch */
+        break;
+      default:
+        str_putc(S, ch);
+        break;
+    } /* first switch on ch */
+  } /* while loop on ch */
+  *S->cur = 0;
+  return S->string;
+}
+
+void
+decode_color(long n, long *c)
+{
+  c[1] = n & 0xf; n >>= 4; /* foreground */
+  c[2] = n & 0xf; n >>= 4; /* background */
+  c[0] = n & 0xf; /* attribute */
+}
+
+#define COLOR_LEN 16
+/* start printing in "color" c */
+/* terminal has to support ANSI color escape sequences */
+void
+out_term_color(PariOUT *out, long c)
+{
+  static char s[COLOR_LEN];
+  out->puts(term_get_color(s, c));
+}
+void
+term_color(long c) { out_term_color(pariOut, c); }
+
+char *
+term_get_color(char *s, long n)
+{
+  long c[3], a;
+  if (!s) s = stack_malloc(COLOR_LEN);
+
+  if (disable_color) { *s = 0; return s; }
+  if (n == c_NONE || (a = gp_colors[n]) == c_NONE)
+    sprintf(s, "%c[0m", esc); /* reset */
+  else
+  {
+    decode_color(a,c);
+    if (c[1]<8) c[1] += 30; else c[1] += 82;
+    if (a & (1L<<12)) /* transparent background */
+      sprintf(s, "%c[%ld;%ldm", esc, c[0], c[1]);
+    else
+    {
+      if (c[2]<8) c[2] += 40; else c[2] += 92;
+      sprintf(s, "%c[%ld;%ld;%ldm", esc, c[0], c[1], c[2]);
+    }
+  }
+  return s;
+}
+
+static long
+strlen_real(const char *s)
+{
+  const char *t = s;
+  long len = 0;
+  while (*t)
+  {
+    if (t[0] == esc && t[1] == '[')
+    { /* skip ANSI escape sequence */
+      t += 2;
+      while (*t && *t++ != 'm') /* empty */;
+      continue;
+    }
+    t++; len++;
+  }
+  return len;
+}
+
+#undef COLOR_LEN
+
+/********************************************************************/
+/**                                                                **/
+/**                  PRINTING BASED ON SCREEN WIDTH                **/
+/**                                                                **/
+/********************************************************************/
+#undef larg /* problems with SCO Unix headers (ioctl_arg) */
+#ifdef HAS_TIOCGWINSZ
+#  ifdef __sun
+#    include <sys/termios.h>
+#  endif
+#  include <sys/ioctl.h>
+#endif
+
+static int
+term_width_intern(void)
+{
+  if (GP_DATA->flags & gpd_TEST) return 0;
+#ifdef _WIN32
+  return win32_terminal_width();
+#endif
+#ifdef HAS_TIOCGWINSZ
+  {
+    struct winsize s;
+    if (!(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS))
+     && !ioctl(0, TIOCGWINSZ, &s)) return s.ws_col;
+  }
+#endif
+  {
+    char *str;
+    if ((str = os_getenv("COLUMNS"))) return atoi(str);
+  }
+#ifdef __EMX__
+  {
+    int scrsize[2];
+    _scrsize(scrsize); return scrsize[0];
+  }
+#endif
+  return 0;
+}
+
+static int
+term_height_intern(void)
+{
+  if (GP_DATA->flags & gpd_TEST) return 0;
+#ifdef _WIN32
+  return win32_terminal_height();
+#endif
+#ifdef HAS_TIOCGWINSZ
+  {
+    struct winsize s;
+    if (!(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS))
+     && !ioctl(0, TIOCGWINSZ, &s)) return s.ws_row;
+  }
+#endif
+  {
+    char *str;
+    if ((str = os_getenv("LINES"))) return atoi(str);
+  }
+#ifdef __EMX__
+  {
+    int scrsize[2];
+    _scrsize(scrsize); return scrsize[1];
+  }
+#endif
+  return 0;
+}
+
+#define DFT_TERM_WIDTH  80
+#define DFT_TERM_HEIGHT 20
+
+int
+term_width(void)
+{
+  int n = term_width_intern();
+  return (n>1)? n: DFT_TERM_WIDTH;
+}
+
+int
+term_height(void)
+{
+  int n = term_height_intern();
+  return (n>1)? n: DFT_TERM_HEIGHT;
+}
+
+static ulong col_index;
+
+/* output string wrapped after MAX_WIDTH characters (for gp -test) */
+static void
+putc_lw(char c)
+{
+  if (c == '\n') col_index = 0;
+  else if (col_index >= GP_DATA->linewrap) { normalOutC('\n'); col_index = 1; }
+  else col_index++;
+  normalOutC(c);
+}
+static void
+puts_lw(const char *s) { while (*s) putc_lw(*s++); }
+
+static PariOUT pariOut_lw= {putc_lw, puts_lw, normalOutF};
+
+void
+init_linewrap(long w) { col_index=0; GP_DATA->linewrap=w; pariOut=&pariOut_lw; }
+
+/* output stopped after max_line have been printed, for default(lines,).
+ * n = length of prefix already printed (print up to max_lin lines) */
+void
+lim_lines_output(char *s, long n, long max_lin)
+{
+  long lin, col, width;
+  char c;
+  if (!*s) return;
+  width = term_width();
+  lin = 1;
+  col = n;
+
+  if (lin > max_lin) return;
+  while ( (c = *s++) )
+  {
+    if (lin >= max_lin)
+      if (c == '\n' || col >= width-5)
+      {
+        pari_sp av = avma;
+        normalOutS(term_get_color(NULL, c_ERR)); avma = av;
+        normalOutS("[+++]"); return;
+      }
+    if (c == '\n')         { col = -1; lin++; }
+    else if (col == width) { col =  0; lin++; }
+    set_last_newline(c);
+    col++; normalOutC(c);
+  }
+}
+
+static void
+new_line(PariOUT *out, const char *prefix)
+{
+  out_putc(out, '\n'); if (prefix) out_puts(out, prefix);
+}
+
+#define is_blank(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
+/* output: <prefix>< s wrapped at EOL >
+ *         <prefix>< ... > <str>
+ *                         ^---  (no \n at the end)
+ * If str is NULL, omit the arrow, end the text with '\n'.
+ * If prefix is NULL, use "" */
+void
+print_prefixed_text(PariOUT *out, const char *s, const char *prefix,
+                    const char *str)
+{
+  const long prelen = prefix? strlen_real(prefix): 0;
+  const long W = term_width(), ls = strlen(s);
+  long linelen = prelen;
+  char *word = (char*)pari_malloc(ls + 3);
+
+  if (prefix) out_puts(out, prefix);
+  for(;;)
+  {
+    long len;
+    int blank = 0;
+    char *u = word;
+    while (*s && !is_blank(*s)) *u++ = *s++;
+    *u = 0; /* finish "word" */
+    len = strlen_real(word);
+    linelen += len;
+    if (linelen >= W) { new_line(out, prefix); linelen = prelen + len; }
+    out_puts(out, word);
+    while (is_blank(*s)) {
+      switch (*s) {
+        case ' ': break;
+        case '\t':
+          linelen = (linelen & ~7UL) + 8; out_putc(out, '\t');
+          blank = 1; break;
+        case '\n':
+          linelen = W;
+          blank = 1; break;
+      }
+      if (linelen >= W) { new_line(out, prefix); linelen = prelen; }
+      s++;
+    }
+    if (!*s) break;
+    if (!blank) { out_putc(out, ' '); linelen++; }
+  }
+  if (!str)
+    out_putc(out, '\n');
+  else
+  {
+    long i,len = strlen_real(str);
+    int space = (*str == ' ' && str[1]);
+    if (linelen + len >= W)
+    {
+      new_line(out, prefix); linelen = prelen;
+      if (space) { str++; len--; space = 0; }
+    }
+    out_term_color(out, c_OUTPUT);
+    out_puts(out, str);
+    if (!len || str[len-1] != '\n') out_putc(out, '\n');
+    if (space) { linelen++; len--; }
+    out_term_color(out, c_ERR);
+    if (prefix) { out_puts(out, prefix); linelen -= prelen; }
+    for (i=0; i<linelen; i++) out_putc(out, ' ');
+    out_putc(out, '^');
+    for (i=0; i<len; i++) out_putc(out, '-');
+  }
+  pari_free(word);
+}
+
+#define STR_LEN 20
+#define MAX_TERM_COLOR 16
+/* Outputs a beautiful error message (not \n terminated)
+ *   msg is errmessage to print.
+ *   s points to the offending chars.
+ *   entry tells how much we can go back from s[0] */
+void
+print_errcontext(PariOUT *out,
+                 const char *msg, const char *s, const char *entry)
+{
+  const long MAX_PAST = 25;
+  long past = s - entry, lmsg;
+  char str[STR_LEN + 1 + 1], pre[MAX_TERM_COLOR + 8 + 1];
+  char *buf, *t;
+
+  if (!s || !entry) { print_prefixed_text(out, msg,"  ***   ",NULL); return; }
+
+  /* message + context */
+  lmsg = strlen(msg);
+  /* msg + past + ': ' + '...' + term_get_color + \0 */
+  t = buf = (char*)pari_malloc(lmsg + MAX_PAST + 2 + 3 + MAX_TERM_COLOR + 1);
+  strncpy(t, msg, lmsg); t += lmsg;
+  strcpy(t, ": "); t += 2;
+  if (past <= 0) past = 0;
+  else
+  {
+    if (past > MAX_PAST) { past = MAX_PAST; strcpy(t, "..."); t += 3; }
+    term_get_color(t, c_OUTPUT);
+    t += strlen(t);
+    strncpy(t, s - past, past); t[past] = 0;
+  }
+
+  /* suffix (past arrow) */
+  t = str; if (!past) *t++ = ' ';
+  strncpy(t, s, STR_LEN); t[STR_LEN] = 0;
+  /* prefix '***' */
+  term_get_color(pre, c_ERR);
+  strcat(pre, "  ***   ");
+  /* now print */
+  print_prefixed_text(out, buf, pre, str);
+  pari_free(buf);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                    GEN <---> CHARACTER STRINGS                 **/
+/**                                                                **/
+/********************************************************************/
+static OUT_FUN
+get_fun(long flag)
+{
+  switch(flag) {
+    case f_RAW : return bruti;
+    case f_TEX : return texi;
+    default: return matbruti;
+  }
+}
+
+char *
+stack_strdup(const char *s)
+{
+  long n = strlen(s)+1;
+  char *t = stack_malloc(n);
+  memcpy(t,s,n); return t;
+}
+char *
+stack_strcat(const char *s, const char *t)
+{
+  long ls = strlen(s), lt = strlen(t);
+  long n = ls + lt + 1;
+  char *u = stack_malloc(n);
+  memcpy(u,     s, ls);
+  memcpy(u + ls,t, lt+1); return u;
+}
+
+char *
+pari_strdup(const char *s)
+{
+  long n = strlen(s)+1;
+  char *t = (char*)pari_malloc(n);
+  memcpy(t,s,n); return t;
+}
+
+char *
+pari_strndup(const char *s, long n)
+{
+  char *t = (char*)pari_malloc(n+1);
+  memcpy(t,s,n); t[n] = 0; return t;
+}
+
+/* not stack clean */
+static char *
+GENtostr_aux(GEN x, pariout_t *T, OUT_FUN out, int use_stack) {
+  outString S;
+  str_init(&S, use_stack); out(x, T, &S); *S.cur = 0;
+  return S.string;
+}
+static char *
+GENtostr_fun(GEN x, pariout_t *T, OUT_FUN out)
+{
+  pari_sp av = avma;
+  char *s = GENtostr_aux(x, T, out, 0);
+  avma = av; return s;
+}
+
+/* returns a malloc-ed string, which should be freed after usage */
+/* Returns pari_malloc()ed string */
+char *
+GENtostr(GEN x) {
+  pariout_t *T = GP_DATA->fmt;
+  return GENtostr_fun(x, T, get_fun(T->prettyp));
+}
+char *
+GENtoTeXstr(GEN x) { return GENtostr_fun(x, GP_DATA->fmt, &texi); }
+
+static char *
+GENtostr1(GEN x, OUT_FUN out)
+{
+  return (typ(x) == t_STR)? pari_strdup(GSTR(x))
+                          : GENtostr_fun(x, GP_DATA->fmt, out);
+}
+
+/* see print0(). Returns pari_malloc()ed string */
+static char *
+RgV_to_str_fun(GEN g, OUT_FUN out) {
+  pari_sp av = avma;
+  char *t, *t2;
+  long i, tlen = 0, l = lg(g);
+  GEN Ls, Ll;
+
+  /* frequent special case */
+  if (l == 2) return GENtostr1(gel(g,1), out);
+
+  Ls = cgetg(l, t_VEC);
+  Ll = cgetg(l, t_VECSMALL);
+  for (i = 1; i < l; i++)
+  {
+    char *s = GENtostr1(gel(g,i), out);
+    gel(Ls,i) = (GEN)s;
+    Ll[i] = strlen(s);
+    tlen += Ll[i];
+  }
+  t2 = t = (char*)pari_malloc(tlen + 1);
+  *t = 0;
+  for (i = 1; i < l; i++)
+  {
+    strcpy(t2, (char*)Ls[i]);
+    t2 += Ll[i];
+    pari_free((void*)Ls[i]);
+  }
+  avma = av; return t;
+}
+
+char *
+RgV_to_str(GEN g, long flag) { return RgV_to_str_fun(g, get_fun(flag)); }
+
+static GEN
+Str_fun(GEN g, OUT_FUN out) {
+  char *t = RgV_to_str_fun(g, out);
+  GEN z = strtoGENstr(t);
+  pari_free(t); return z;
+}
+GEN Str(GEN g)    { return Str_fun(g, &bruti); }
+GEN Strtex(GEN g) { return Str_fun(g, &texi); }
+GEN
+Strexpand(GEN g) {
+  char *s = RgV_to_str_fun(g, &bruti), *t = path_expand(s);
+  GEN z = strtoGENstr(t);
+  pari_free(t); pari_free(s); return z;
+}
+
+GEN
+GENtoGENstr(GEN x)
+{
+  char *s = GENtostr_fun(x, GP_DATA->fmt, &bruti);
+  GEN z = strtoGENstr(s); pari_free(s); return z;
+}
+GEN
+GENtoGENstr_nospace(GEN x)
+{
+  pariout_t T = *(GP_DATA->fmt);
+  char *s;
+  GEN z;
+  T.sp = 0;
+  s = GENtostr_fun(x, &T, &bruti);
+  z = strtoGENstr(s); pari_free(s); return z;
+}
+
+static char *
+GENtostr_fun_unquoted(GEN x, pariout_t *T, OUT_FUN f)
+{
+  if (typ(x)==t_STR) return GSTR(x); /* text surrounded by "" otherwise */
+  return GENtostr_aux(x, T, f, 1);
+}
+char *
+GENtostr_unquoted(GEN x)
+{ return GENtostr_fun_unquoted(x, GP_DATA->fmt, &bruti); }
+
+static char
+ltoc(long n) {
+  if (n <= 0 || n > 255)
+    pari_err(e_MISC, "out of range in integer -> character conversion (%ld)", n);
+  return (char)n;
+}
+static char
+itoc(GEN x) { return ltoc(gtos(x)); }
+
+GEN
+Strchr(GEN g)
+{
+  long i, l, len, t = typ(g);
+  char *s;
+  GEN x;
+  if (is_vec_t(t)) {
+    l = lg(g); len = nchar2nlong(l);
+    x = cgetg(len+1, t_STR); s = GSTR(x);
+    for (i=1; i<l; i++) *s++ = itoc(gel(g,i));
+  }
+  else if (t == t_VECSMALL)
+  {
+    l = lg(g); len = nchar2nlong(l);
+    x = cgetg(len+1, t_STR); s = GSTR(x);
+    for (i=1; i<l; i++) *s++ = ltoc(g[i]);
+  }
+  else
+    return chartoGENstr(itoc(g));
+  *s = 0; return x;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         WRITE AN INTEGER                       **/
+/**                                                                **/
+/********************************************************************/
+char *
+itostr(GEN x) {
+  long sx = signe(x), l;
+  return sx? itostr_sign(x, sx, &l): zerotostr();
+}
+
+/* x != 0 t_INT, write abs(x) to S */
+static void
+str_absint(outString *S, GEN x)
+{
+  pari_sp av;
+  long l;
+  str_alloc(S, lgefint(x)); /* careful ! */
+  av = avma;
+  str_puts(S, itostr_sign(x, 1, &l)); avma = av;
+}
+
+#define putsigne_nosp(S, x) str_putc(S, (x>0)? '+' : '-')
+#define putsigne(S, x) str_puts(S, (x>0)? " + " : " - ")
+#define sp_sign_sp(T,S, x) ((T)->sp? putsigne(S,x): putsigne_nosp(S,x))
+#define comma_sp(T,S)     ((T)->sp? str_puts(S, ", "): str_putc(S, ','))
+
+/* print e to S (more efficient than sprintf) */
+static void
+str_ulong(outString *S, ulong e)
+{
+  if (e == 0) str_putc(S, '0');
+  else
+  {
+    char buf[21], *p = buf + sizeof(buf)/sizeof(*buf);
+    *--p = 0;
+    if (e > 9) {
+      do
+        *--p = "0123456789"[e % 10];
+      while ((e /= 10) > 9);
+    }
+    *--p = "0123456789"[e];
+    str_puts(S, p);
+  }
+}
+static void
+str_long(outString *S, long e)
+{
+  if (e >= 0) str_ulong(S, (ulong)e);
+  else { str_putc(S, '-'); str_ulong(S, (ulong)(-e)); }
+}
+
+static void
+wr_vecsmall(pariout_t *T, outString *S, GEN g)
+{
+  long i, l;
+  str_puts(S, "Vecsmall(["); l = lg(g);
+  for (i=1; i<l; i++)
+  {
+    str_long(S, g[i]);
+    if (i<l-1) comma_sp(T,S);
+  }
+  str_puts(S, "])");
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                       HEXADECIMAL OUTPUT                       **/
+/**                                                                **/
+/********************************************************************/
+/* English ordinal numbers -- GN1998Apr17 */
+static const char *ordsuff[4] = {"st","nd","rd","th"};
+const char*
+eng_ord(long i)                        /* i > 0 assumed */
+{
+  switch (i%10)
+  {
+    case 1:
+      if (i%100==11) return ordsuff[3]; /* xxx11-th */
+      return ordsuff[0];         /* xxx01-st, xxx21-st,... */
+    case 2:
+      if (i%100==12) return ordsuff[3]; /* xxx12-th */
+      return ordsuff[1];         /* xxx02-nd, xxx22-nd,... */
+    case 3:
+      if (i%100==13) return ordsuff[3]; /* xxx13-th */
+      return ordsuff[2];         /* xxx03-rd, xxx23-rd,... */
+    default:
+      return ordsuff[3];         /* xxxx4-th,... */
+  }
+}
+
+const char *
+type_name(long t)
+{
+  const char *s;
+  switch(t)
+  {
+    case t_INT    : s="t_INT";     break;
+    case t_REAL   : s="t_REAL";    break;
+    case t_INTMOD : s="t_INTMOD";  break;
+    case t_FRAC   : s="t_FRAC";    break;
+    case t_FFELT  : s="t_FFELT";   break;
+    case t_COMPLEX: s="t_COMPLEX"; break;
+    case t_PADIC  : s="t_PADIC";   break;
+    case t_QUAD   : s="t_QUAD";    break;
+    case t_POLMOD : s="t_POLMOD";  break;
+    case t_POL    : s="t_POL";     break;
+    case t_SER    : s="t_SER";     break;
+    case t_RFRAC  : s="t_RFRAC";   break;
+    case t_QFR    : s="t_QFR";     break;
+    case t_QFI    : s="t_QFI";     break;
+    case t_VEC    : s="t_VEC";     break;
+    case t_COL    : s="t_COL";     break;
+    case t_MAT    : s="t_MAT";     break;
+    case t_LIST   : s="t_LIST";    break;
+    case t_STR    : s="t_STR";     break;
+    case t_VECSMALL:s="t_VECSMALL";break;
+    case t_CLOSURE: s="t_CLOSURE"; break;
+    case t_ERROR:   s="t_ERROR";   break;
+    default: pari_err(e_MISC,"unknown type %ld",t);
+      s = NULL; /* not reached */
+  }
+  return s;
+}
+
+static char
+vsigne(GEN x)
+{
+  long s = signe(x);
+  if (!s) return '0';
+  return (s > 0) ? '+' : '-';
+}
+
+static void
+blancs(long nb) { while (nb-- > 0) pari_putc(' '); }
+
+/* write an "address" */
+static void
+str_addr(outString *S, ulong x)
+{ char s[128]; sprintf(s,"%0*lx", BITS_IN_LONG/4, x); str_puts(S, s); }
+static void
+dbg_addr(ulong x) { pari_printf("[&=%0*lx] ", BITS_IN_LONG/4, x); }
+/* write a "word" */
+static void
+dbg_word(ulong x) { pari_printf("%0*lx ", BITS_IN_LONG/4, x); }
+
+/* bl: indent level */
+static void
+dbg(GEN x, long nb, long bl)
+{
+  long tx,i,j,e,dx,lx;
+
+  if (!x) { pari_puts("NULL\n"); return; }
+  tx = typ(x);
+  if (tx == t_INT && x == gen_0) { pari_puts("gen_0\n"); return; }
+  dbg_addr((ulong)x);
+
+  lx = lg(x);
+  pari_printf("%s(lg=%ld%s):",type_name(tx)+2,lx,isclone(x)? ",CLONE" : "");
+  dbg_word(x[0]);
+  if (! is_recursive_t(tx)) /* t_INT, t_REAL, t_STR, t_VECSMALL */
+  {
+    if (tx == t_STR)
+      pari_puts("chars:");
+    else if (tx == t_INT)
+    {
+      lx = lgefint(x);
+      pari_printf("(%c,lgefint=%ld):", vsigne(x), lx);
+    }
+    else if (tx == t_REAL)
+      pari_printf("(%c,expo=%ld):", vsigne(x), expo(x));
+    if (nb < 0) nb = lx;
+    for (i=1; i < nb; i++) dbg_word(x[i]);
+    pari_putc('\n'); return;
+  }
+
+  if (tx == t_PADIC)
+    pari_printf("(precp=%ld,valp=%ld):", precp(x), valp(x));
+  else if (tx == t_POL)
+    pari_printf("(%c,varn=%ld):", vsigne(x), varn(x));
+  else if (tx == t_SER)
+    pari_printf("(%c,varn=%ld,prec=%ld,valp=%ld):",
+               vsigne(x), varn(x), lgpol(x), valp(x));
+  else if (tx == t_LIST)
+  {
+    pari_printf("(lmax=%ld):", list_nmax(x));
+    x = list_data(x); lx = x? lg(x): 1;
+    tx = t_VEC; /* print list_data as vec */
+  } else if (tx == t_CLOSURE)
+    pari_printf("(arity=%ld):", closure_arity(x));
+  for (i=1; i<lx; i++) dbg_word(x[i]);
+  bl+=2; pari_putc('\n');
+  switch(tx)
+  {
+    case t_INTMOD: case t_POLMOD:
+    {
+      const char *s = (tx==t_INTMOD)? "int = ": "pol = ";
+      blancs(bl); pari_puts("mod = "); dbg(gel(x,1),nb,bl);
+      blancs(bl); pari_puts(s);        dbg(gel(x,2),nb,bl);
+      break;
+    }
+    case t_FRAC: case t_RFRAC:
+      blancs(bl); pari_puts("num = "); dbg(gel(x,1),nb,bl);
+      blancs(bl); pari_puts("den = "); dbg(gel(x,2),nb,bl);
+      break;
+
+    case t_FFELT:
+      blancs(bl); pari_puts("pol = "); dbg(gel(x,2),nb,bl);
+      blancs(bl); pari_puts("mod = "); dbg(gel(x,3),nb,bl);
+      blancs(bl); pari_puts("p   = "); dbg(gel(x,4),nb,bl);
+      break;
+
+    case t_COMPLEX:
+      blancs(bl); pari_puts("real = "); dbg(gel(x,1),nb,bl);
+      blancs(bl); pari_puts("imag = "); dbg(gel(x,2),nb,bl);
+      break;
+
+    case t_PADIC:
+      blancs(bl); pari_puts("  p : "); dbg(gel(x,2),nb,bl);
+      blancs(bl); pari_puts("p^l : "); dbg(gel(x,3),nb,bl);
+      blancs(bl); pari_puts("  I : "); dbg(gel(x,4),nb,bl);
+      break;
+
+    case t_QUAD:
+      blancs(bl); pari_puts("pol = ");  dbg(gel(x,1),nb,bl);
+      blancs(bl); pari_puts("real = "); dbg(gel(x,2),nb,bl);
+      blancs(bl); pari_puts("imag = "); dbg(gel(x,3),nb,bl);
+      break;
+
+    case t_POL: case t_SER:
+      e = (tx==t_SER)? valp(x): 0;
+      for (i=2; i<lx; i++)
+      {
+        blancs(bl); pari_printf("coef of degree %ld = ",e);
+        e++; dbg(gel(x,i),nb,bl);
+      }
+      break;
+
+    case t_QFR: case t_QFI: case t_VEC: case t_COL:
+      for (i=1; i<lx; i++)
+      {
+        blancs(bl); pari_printf("%ld%s component = ",i,eng_ord(i));
+        dbg(gel(x,i),nb,bl);
+      }
+      break;
+
+    case t_CLOSURE:
+      blancs(bl); pari_puts("code = "); dbg(closure_get_code(x),nb,bl);
+      blancs(bl); pari_puts("operand = "); dbg(closure_get_oper(x),nb,bl);
+      blancs(bl); pari_puts("data = "); dbg(closure_get_data(x),nb,bl);
+      blancs(bl); pari_puts("dbg/frpc/fram = "); dbg(closure_get_dbg(x),nb,bl);
+      if (lg(x)>=7)
+      {
+        blancs(bl); pari_puts("text = "); dbg(closure_get_text(x),nb,bl);
+        if (lg(x)>=8)
+        {
+          blancs(bl); pari_puts("frame = "); dbg(closure_get_frame(x),nb,bl);
+        }
+      }
+      break;
+
+    case t_MAT:
+    {
+      GEN c = gel(x,1);
+      if (lx == 1) return;
+      if (typ(c) == t_VECSMALL)
+      {
+        for (i = 1; i < lx; i++)
+        {
+          blancs(bl); pari_printf("%ld%s column = ",i,eng_ord(i));
+          dbg(gel(x,i),nb,bl);
+        }
+      }
+      else
+      {
+        dx = lg(c);
+        for (i=1; i<dx; i++)
+          for (j=1; j<lx; j++)
+          {
+            blancs(bl); pari_printf("mat(%ld,%ld) = ",i,j);
+            dbg(gcoeff(x,i,j),nb,bl);
+          }
+      }
+    }
+  }
+}
+
+void
+dbgGEN(GEN x, long nb) { dbg(x,nb,0); }
+
+static void
+print_entree(entree *ep, long hash)
+{
+  pari_printf(" %s ",ep->name); dbg_addr((ulong)ep);
+  pari_printf(":\n   hash = %3ld, menu = %2ld, code = %-10s",
+            hash, ep->menu, ep->code? ep->code: "NULL");
+  if (ep->next)
+  {
+    pari_printf("next = %s ",(ep->next)->name);
+    dbg_addr((ulong)ep->next);
+  }
+  pari_puts("\n");
+}
+
+/* s = digit n : list of entrees in functions_hash[n] (s = $: last entry)
+ *   = range m-n: functions_hash[m..n]
+ *   = identifier: entree for that identifier */
+void
+print_functions_hash(const char *s)
+{
+  long m, n, Max, Total;
+  entree *ep;
+
+  if (isdigit((int)*s) || *s == '$')
+  {
+    m = functions_tblsz-1; n = atol(s);
+    if (*s=='$') n = m;
+    if (m<n) pari_err(e_MISC,"invalid range in print_functions_hash");
+    while (isdigit((int)*s)) s++;
+
+    if (*s++ != '-') m = n;
+    else
+    {
+      if (*s !='$') m = minss(atol(s),m);
+      if (m<n) pari_err(e_MISC,"invalid range in print_functions_hash");
+    }
+
+    for(; n<=m; n++)
+    {
+      pari_printf("*** hashcode = %lu\n",n);
+      for (ep=functions_hash[n]; ep; ep=ep->next)
+        print_entree(ep,n);
+    }
+    return;
+  }
+  if (is_keyword_char((int)*s))
+  {
+    ep = is_entry_intern(s,functions_hash,&n);
+    if (!ep) pari_err(e_MISC,"no such function");
+    print_entree(ep,n); return;
+  }
+  if (*s=='-')
+  {
+    for (n=0; n<functions_tblsz; n++)
+    {
+      m=0;
+      for (ep=functions_hash[n]; ep; ep=ep->next) m++;
+      pari_printf("%3ld:%3ld ",n,m);
+      if (n%9 == 8) pari_putc('\n');
+    }
+    pari_putc('\n'); return;
+  }
+  Max = Total = 0;
+  for (n=0; n<functions_tblsz; n++)
+  {
+    long cnt = 0;
+    for (ep=functions_hash[n]; ep; ep=ep->next)
+    {
+      print_entree(ep,n);
+      cnt++;
+    }
+    Total += cnt;
+    if (cnt > Max) Max = cnt;
+  }
+  pari_printf("Total: %ld, Max: %ld\n", Total, Max);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        FORMATTED OUTPUT                        **/
+/**                                                                **/
+/********************************************************************/
+static const char *
+get_var(long v, char *buf)
+{
+  entree *ep = varentries[v];
+
+  if (ep) return (char*)ep->name;
+  if (v==MAXVARN) return "#";
+  sprintf(buf,"#<%d>",(int)v); return buf;
+}
+
+static void
+do_append(char **sp, char c, char *last, int count)
+{
+  if (*sp + count > last)
+    pari_err(e_MISC, "TeX variable name too long");
+  while (count--)
+    *(*sp)++ = c;
+}
+
+static char *
+get_texvar(long v, char *buf, unsigned int len)
+{
+  entree *ep = varentries[v];
+  char *t = buf, *e = buf + len - 1;
+  const char *s;
+
+  if (!ep) pari_err(e_MISC, "this object uses debugging variables");
+  s = ep->name;
+  if (strlen(s) >= len) pari_err(e_MISC, "TeX variable name too long");
+  while (isalpha((int)*s)) *t++ = *s++;
+  *t = 0;
+  if (isdigit((int)*s) || *s == '_') {
+    int seen1 = 0, seen = 0;
+
+    /* Skip until the first non-underscore */
+    while (*s == '_') s++, seen++;
+
+    /* Special-case integers and empty subscript */
+    if (*s == 0 || isdigit((unsigned char)*s))
+      seen++;
+
+    do_append(&t, '_', e, 1);
+    do_append(&t, '{', e, 1);
+    do_append(&t, '[', e, seen - 1);
+    while (1) {
+      if (*s == '_')
+        seen1++, s++;
+      else {
+        if (seen1) {
+          do_append(&t, ']', e, (seen >= seen1 ? seen1 : seen) - 1);
+          do_append(&t, ',', e, 1);
+          do_append(&t, '[', e, seen1 - 1);
+          if (seen1 > seen)
+            seen = seen1;
+          seen1 = 0;
+        }
+        if (*s == 0)
+          break;
+        do_append(&t, *s++, e, 1);
+      }
+    }
+    do_append(&t, ']', e, seen - 1);
+    do_append(&t, '}', e, 1);
+    *t = 0;
+  }
+  return buf;
+}
+
+void
+dbg_pari_heap(void)
+{
+  long nu, l, u, s;
+  pari_sp av = avma;
+  GEN adr = getheap();
+
+  nu = (top-avma)/sizeof(long);
+  l = (top-bot)/sizeof(long);
+  pari_printf("\n Top : %lx   Bottom : %lx   Current stack : %lx\n",
+              top, bot, avma);
+  pari_printf(" Used :                         %ld  long words  (%ld K)\n",
+              nu, nu/1024*sizeof(long));
+  pari_printf(" Available :                    %ld  long words  (%ld K)\n",
+              (l-nu), (l-nu)/1024*sizeof(long));
+  pari_printf(" Occupation of the PARI stack : %6.2f percent\n", 100.0*nu/l);
+  pari_printf(" %ld objects on heap occupy %ld long words\n\n",
+              itos(gel(adr,1)), itos(gel(adr,2)));
+  u = pari_var_next();
+  s = MAXVARN - pari_var_next_temp();
+  pari_printf(" %ld variable names used (%ld user + %ld private) out of %d\n\n",
+              u+s, u, s, MAXVARN);
+  avma = av;
+}
+
+#define isnull_for_pol(g)  ((typ(g)==t_INTMOD)? !signe(gel(g,2)): isnull(g))
+
+/* is to be printed as '0' */
+static long
+isnull(GEN g)
+{
+  long i;
+  switch (typ(g))
+  {
+    case t_INT:
+      return !signe(g);
+    case t_COMPLEX:
+      return isnull(gel(g,1)) && isnull(gel(g,2));
+    case t_FFELT:
+      return FF_equal0(g);
+    case t_QUAD:
+      return isnull(gel(g,2)) && isnull(gel(g,3));
+    case t_FRAC: case t_RFRAC:
+      return isnull(gel(g,1));
+    case t_POLMOD:
+      return isnull(gel(g,2));
+    case t_POL:
+      for (i=lg(g)-1; i>1; i--)
+        if (!isnull(gel(g,i))) return 0;
+      return 1;
+  }
+  return 0;
+}
+
+/* return 1 or -1 if g is 1 or -1, 0 otherwise*/
+static long
+isone(GEN g)
+{
+  long i;
+  switch (typ(g))
+  {
+    case t_INT:
+      return (signe(g) && is_pm1(g))? signe(g): 0;
+    case t_FFELT:
+      return FF_equal1(g);
+    case t_COMPLEX:
+      return isnull(gel(g,2))? isone(gel(g,1)): 0;
+    case t_QUAD:
+      return isnull(gel(g,3))? isone(gel(g,2)): 0;
+    case t_FRAC: case t_RFRAC:
+      return isone(gel(g,1)) * isone(gel(g,2));
+    case t_POL:
+      if (!signe(g)) return 0;
+      for (i=lg(g)-1; i>2; i--)
+        if (!isnull(gel(g,i))) return 0;
+      return isone(gel(g,2));
+  }
+  return 0;
+}
+
+/* if g is a "monomial", return its sign, 0 otherwise */
+static long
+isfactor(GEN g)
+{
+  long i,deja,sig;
+  switch(typ(g))
+  {
+    case t_INT: case t_REAL:
+      return (signe(g)<0)? -1: 1;
+    case t_FRAC: case t_RFRAC:
+      return isfactor(gel(g,1));
+    case t_FFELT:
+      return isfactor(FF_to_FpXQ_i(g));
+    case t_COMPLEX:
+      if (isnull(gel(g,1))) return isfactor(gel(g,2));
+      if (isnull(gel(g,2))) return isfactor(gel(g,1));
+      return 0;
+    case t_PADIC:
+      return !signe(gel(g,4));
+    case t_QUAD:
+      if (isnull(gel(g,2))) return isfactor(gel(g,3));
+      if (isnull(gel(g,3))) return isfactor(gel(g,2));
+      return 0;
+    case t_POL: deja = 0; sig = 1;
+      for (i=lg(g)-1; i>1; i--)
+        if (!isnull_for_pol(gel(g,i)))
+        {
+          if (deja) return 0;
+          sig=isfactor(gel(g,i)); deja=1;
+        }
+      return sig? sig: 1;
+    case t_SER:
+      for (i=lg(g)-1; i>1; i--)
+        if (!isnull(gel(g,i))) return 0;
+  }
+  return 1;
+}
+
+/* return 1 if g is a "truc" (see anal.c) */
+static long
+isdenom(GEN g)
+{
+  long i,deja;
+  switch(typ(g))
+  {
+    case t_FRAC: case t_RFRAC:
+      return 0;
+    case t_COMPLEX: return isnull(gel(g,2));
+    case t_PADIC: return !signe(gel(g,4));
+    case t_QUAD: return isnull(gel(g,3));
+
+    case t_POL: deja = 0;
+      for (i=lg(g)-1; i>1; i--)
+        if (!isnull(gel(g,i)))
+        {
+          if (deja) return 0;
+          if (i==2) return isdenom(gel(g,2));
+          if (!isone(gel(g,i))) return 0;
+          deja=1;
+        }
+      return 1;
+    case t_SER:
+      for (i=lg(g)-1; i>1; i--)
+        if (!isnull(gel(g,i))) return 0;
+  }
+  return 1;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           RAW OUTPUT                           **/
+/**                                                                **/
+/********************************************************************/
+/* ^e */
+static void
+texexpo(outString *S, long e)
+{
+  if (e != 1) {
+    str_putc(S, '^');
+    if (e >= 0 && e < 10)
+    { str_putc(S, '0' + e); }
+    else
+    {
+      str_putc(S, '{'); str_long(S, e); str_putc(S, '}');
+    }
+  }
+}
+static void
+wrexpo(outString *S, long e)
+{
+  if (e != 1) { str_putc(S, '^'); str_long(S, e); }
+}
+
+/* v^e */
+static void
+VpowE(outString *S, const char *v, long e) { str_puts(S, v); wrexpo(S,e); }
+static void
+texVpowE(outString *S, const char *v, long e) {
+  str_puts(S, v); texexpo(S,e);
+}
+static void
+monome(outString *S, const char *v, long e)
+{
+  if (e) VpowE(S, v, e); else str_putc(S, '1');
+}
+static void
+texnome(outString *S, const char *v, long e)
+{
+  if (e) texVpowE(S, v, e); else str_putc(S, '1');
+}
+
+/* ( a ) */
+static void
+paren(pariout_t *T, outString *S, GEN a)
+{
+  str_putc(S, '('); bruti(a,T,S); str_putc(S, ')');
+}
+static void
+texparen(pariout_t *T, outString *S, GEN a)
+{
+  if (T->TeXstyle & TEXSTYLE_PAREN)
+    str_puts(S, " (");
+  else
+    str_puts(S, " \\left(");
+  texi(a,T,S);
+  if (T->TeXstyle & TEXSTYLE_PAREN)
+    str_puts(S, ") ");
+  else
+    str_puts(S, "\\right) ");
+}
+
+/* * v^d */
+static void
+times_texnome(outString *S, const char *v, long d)
+{
+  if (d)
+  {
+    if (GP_DATA->flags & gpd_TEXMACS) str_puts(S, "\\*"); else str_putc(S, ' ');
+    texnome(S,v,d);
+  }
+}
+static void
+times_monome(outString *S, const char *v, long d)
+{
+  if (d) { str_putc(S, '*'); monome(S,v,d); }
+}
+
+/* write a * v^d */
+static void
+wr_monome(pariout_t *T, outString *S, GEN a, const char *v, long d)
+{
+  long sig = isone(a);
+
+  if (sig) {
+    sp_sign_sp(T,S,sig); monome(S,v,d);
+  } else {
+    sig = isfactor(a);
+    if (sig) { sp_sign_sp(T,S,sig); bruti_sign(a,T,S,0); }
+    else { sp_sign_sp(T,S,1); paren(T,S, a); }
+    times_monome(S, v, d);
+  }
+}
+static void
+wr_texnome(pariout_t *T, outString *S, GEN a, const char *v, long d)
+{
+  long sig = isone(a);
+
+  str_putc(S, '\n'); /* Avoid TeX buffer overflow */
+  if (T->TeXstyle & TEXSTYLE_BREAK) str_puts(S, "\\PARIbreak ");
+
+  if (sig) {
+    putsigne(S,sig); texnome(S,v,d);
+  } else {
+    sig = isfactor(a);
+    if (sig) { putsigne(S,sig); texi_sign(a,T,S,0); }
+    else { str_puts(S, " +"); texparen(T,S, a); }
+    times_texnome(S, v, d);
+  }
+}
+
+static void
+wr_lead_monome(pariout_t *T, outString *S, GEN a,const char *v, long d, int addsign)
+{
+  long sig = isone(a);
+  if (sig) {
+    if (addsign && sig<0) str_putc(S, '-');
+    monome(S,v,d);
+  } else {
+    if (isfactor(a)) bruti_sign(a,T,S,addsign);
+    else paren(T,S, a);
+    times_monome(S, v, d);
+  }
+}
+static void
+wr_lead_texnome(pariout_t *T, outString *S, GEN a,const char *v, long d, int addsign)
+{
+  long sig = isone(a);
+  if (sig) {
+    if (addsign && sig<0) str_putc(S, '-');
+    texnome(S,v,d);
+  } else {
+    if (isfactor(a)) texi_sign(a,T,S,addsign);
+    else texparen(T,S, a);
+    times_texnome(S, v, d);
+  }
+}
+
+static void
+prints(GEN g, pariout_t *T, outString *S)
+{ (void)T; str_long(S, (long)g); }
+
+static void
+quote_string(outString *S, char *s)
+{
+  str_putc(S, '"');
+  while (*s)
+  {
+    char c=*s++;
+    if (c=='\\' || c=='"' || c=='\033' || c=='\n' || c=='\t')
+    {
+      str_putc(S, '\\');
+      switch(c)
+      {
+      case '\\': case '"': break;
+      case '\n':   c='n'; break;
+      case '\033': c='e'; break;
+      case '\t':   c='t'; break;
+      }
+    }
+    str_putc(S, c);
+  }
+  str_putc(S, '"');
+}
+
+static int
+print_0_or_pm1(GEN g, outString *S, int addsign)
+{
+  long r;
+  if (!g) { str_puts(S, "NULL"); return 1; }
+  if (isnull(g)) { str_putc(S, '0'); return 1; }
+  r = isone(g);
+  if (r)
+  {
+    if (addsign && r<0) str_putc(S, '-');
+    str_putc(S, '1'); return 1;
+  }
+  return 0;
+}
+static void
+print_context(GEN g, pariout_t *T, outString *S, long tex)
+{
+  if (lg(g)>=8 && lg(gel(g,7))>1 && lg(gmael(g,5,3))>=2)
+  {
+    GEN v = gel(g,7), d = gmael3(g,5,3,1);
+    long i, l = lg(v), n=0;
+    for(i=1; i<l; i++)
+      if (gel(d,i))
+        n++;
+    if (n==0) return;
+    str_puts(S,"my(");
+    for(i=1; i<l; i++)
+      if (gel(d,i))
+      {
+        entree *ep = (entree*) gel(d,i);
+        str_puts(S,ep->name);
+        str_putc(S,'=');
+        if (tex) texi(gel(v,l-i),T,S); else bruti(gel(v,l-i),T,S);
+        if (--n)
+          str_putc(S,',');
+      }
+    str_puts(S,");");
+  }
+}
+
+static void
+bruti_intern(GEN g, pariout_t *T, outString *S, int addsign)
+{
+  long l,i,j,r, tg = typ(g);
+  GEN a,b;
+  const char *v;
+  char buf[32];
+
+  switch(tg)
+  {
+    case t_INT:
+      if (addsign && signe(g) < 0) str_putc(S, '-');
+      str_absint(S, g); break;
+    case t_REAL:
+    {
+      pari_sp av;
+      str_alloc(S, lg(g)); /* careful! */
+      av = avma;
+      if (addsign && signe(g) < 0) str_putc(S, '-');
+      str_puts(S, absrtostr(g, T->sp, (char)toupper((int)T->format), T->sigd) );
+      avma = av; break;
+    }
+
+    case t_INTMOD: case t_POLMOD:
+      str_puts(S, new_fun_set? "Mod(": "mod(");
+      bruti(gel(g,2),T,S); comma_sp(T,S);
+      bruti(gel(g,1),T,S); str_putc(S, ')'); break;
+
+    case t_FFELT:
+      bruti_sign(FF_to_FpXQ_i(g),T,S,addsign);
+      break;
+
+    case t_FRAC: case t_RFRAC:
+      r = isfactor(gel(g,1)); if (!r) str_putc(S, '(');
+      bruti_sign(gel(g,1),T,S,addsign);
+      if (!r) str_putc(S, ')');
+      str_putc(S, '/');
+      r = isdenom(gel(g,2)); if (!r) str_putc(S, '(');
+      bruti(gel(g,2),T,S);
+      if (!r) str_putc(S, ')');
+      break;
+
+    case t_COMPLEX: case t_QUAD: r = (tg==t_QUAD);
+      a = gel(g,r+1); b = gel(g,r+2); v = r? "w": "I";
+      if (isnull(a))
+      {
+        wr_lead_monome(T,S,b,v,1,addsign);
+        return;
+      }
+      bruti_sign(a,T,S,addsign);
+      if (!isnull(b)) wr_monome(T,S,b,v,1);
+      break;
+
+    case t_POL: v = get_var(varn(g), buf);
+      /* hack: we want g[i] = coeff of degree i. */
+      i = degpol(g); g += 2; while (isnull(gel(g,i))) i--;
+      wr_lead_monome(T,S,gel(g,i),v,i,addsign);
+      while (i--)
+      {
+        a = gel(g,i);
+        if (!isnull_for_pol(a)) wr_monome(T,S,a,v,i);
+      }
+      break;
+
+    case t_SER: v = get_var(varn(g), buf);
+      i = valp(g);
+      l = lgpol(g);
+      if (l)
+      {
+        /* See normalize(): Mod(0,2)*x^i*(1+O(x)), has valp = i+1 */
+        if (l == 1 && !signe(g)) i--;
+        /* hack: we want g[i] = coeff of degree i */
+        l += i; g -= i-2;
+        wr_lead_monome(T,S,gel(g,i),v,i,addsign);
+        while (++i < l)
+        {
+          a = gel(g,i);
+          if (!isnull_for_pol(a)) wr_monome(T,S,a,v,i);
+        }
+        sp_sign_sp(T,S,1);
+      }
+      str_puts(S, "O("); VpowE(S, v, i); str_putc(S, ')'); break;
+
+    case t_PADIC:
+    {
+      GEN p = gel(g,2);
+      pari_sp av, av0;
+      char *ev;
+      str_alloc(S, (precp(g)+1) * lgefint(p)); /* careful! */
+      av0 = avma;
+      ev = itostr(p);
+      av = avma;
+      i = valp(g); l = precp(g)+i;
+      g = gel(g,4);
+      for (; i<l; i++)
+      {
+        g = dvmdii(g,p,&a);
+        if (signe(a))
+        {
+          if (!i || !is_pm1(a))
+          {
+            str_absint(S, a); if (i) str_putc(S, '*');
+          }
+          if (i) VpowE(S, ev,i);
+          sp_sign_sp(T,S,1);
+        }
+        if ((i & 0xff) == 0) g = gerepileuptoint(av,g);
+      }
+      str_puts(S, "O("); VpowE(S, ev,i); str_putc(S, ')');
+      avma = av0; break;
+    }
+
+    case t_QFR: case t_QFI: r = (tg == t_QFR);
+      if (new_fun_set) str_puts(S, "Qfb("); else str_puts(S, r? "qfr(": "qfi(");
+      bruti(gel(g,1),T,S); comma_sp(T,S);
+      bruti(gel(g,2),T,S); comma_sp(T,S);
+      bruti(gel(g,3),T,S);
+      if (r) { comma_sp(T,S); bruti(gel(g,4),T,S); }
+      str_putc(S, ')'); break;
+
+    case t_VEC: case t_COL:
+      str_putc(S, '['); l = lg(g);
+      for (i=1; i<l; i++)
+      {
+        bruti(gel(g,i),T,S);
+        if (i<l-1) comma_sp(T,S);
+      }
+      str_putc(S, ']'); if (tg==t_COL) str_putc(S, '~');
+      break;
+    case t_VECSMALL: wr_vecsmall(T,S,g); break;
+
+    case t_LIST:
+      str_puts(S, "List([");
+      g = list_data(g);
+      l = g? lg(g): 1;
+      for (i=1; i<l; i++)
+      {
+        bruti(gel(g,i),T,S);
+        if (i<l-1) comma_sp(T,S);
+      }
+      str_puts(S, "])"); break;
+
+    case t_STR:
+      quote_string(S, GSTR(g)); break;
+    case t_ERROR:
+      {
+        char *s = pari_err2str(g);
+        str_puts(S, "error(");
+        quote_string(S, s); pari_free(s);
+        str_puts(S, ")"); break;
+      }
+    case t_CLOSURE:
+      if (lg(g)>=7)
+      {
+        GEN str = closure_get_text(g);
+        if (typ(str)==t_STR)
+          str_puts(S, GSTR(str));
+        else
+        {
+          str_putc(S,'(');   str_puts(S,GSTR(gel(str,1)));
+          str_puts(S,")->");
+          print_context(g, T, S, 0);
+          str_puts(S,GSTR(gel(str,2)));
+        }
+      }
+      else
+      {
+        str_puts(S,"{\""); str_puts(S,GSTR(closure_get_code(g)));
+        str_puts(S,"\","); wr_vecsmall(T,S,closure_get_oper(g));
+        str_putc(S,',');   bruti(gel(g,4),T,S);
+        str_putc(S,',');   bruti(gel(g,5),T,S);
+        str_putc(S,'}');
+      }
+      break;
+
+    case t_MAT:
+    {
+      OUT_FUN print;
+
+      r = lg(g); if (r==1) { str_puts(S, "[;]"); return; }
+      l = lgcols(g);
+      if (l==1)
+      {
+        str_puts(S, "matrix(0,");
+        str_long(S, r-1);
+        if (new_fun_set)
+          str_putc(S, ')');
+        else
+          str_puts(S, ",j,k,0)");
+        return;
+      }
+      print = (typ(gel(g,1)) == t_VECSMALL)? prints: bruti;
+      if (l==2)
+      {
+        str_puts(S, new_fun_set? "Mat(": "mat(");
+        if (r == 2) { print(gcoeff(g,1,1),T,S); str_putc(S, ')'); return; }
+      }
+      str_putc(S, '[');
+      for (i=1; i<l; i++)
+      {
+        for (j=1; j<r; j++)
+        {
+          print(gcoeff(g,i,j),T,S);
+          if (j<r-1) comma_sp(T,S);
+        }
+        if (i<l-1) { str_putc(S, ';'); if (T->sp) str_putc(S, ' '); }
+      }
+      str_putc(S, ']'); if (l==2) str_putc(S, ')');
+      break;
+    }
+
+    default: str_addr(S, *g);
+  }
+}
+
+static void
+bruti_sign(GEN g, pariout_t *T, outString *S, int addsign)
+{
+  if (!print_0_or_pm1(g, S, addsign))
+    bruti_intern(g, T, S, addsign);
+}
+
+static void
+matbruti(GEN g, pariout_t *T, outString *S)
+{
+  long i, j, r, w, l, *pad = NULL;
+  pari_sp av;
+  OUT_FUN print;
+
+  if (typ(g) != t_MAT) { bruti(g,T,S); return; }
+
+  r=lg(g); if (r==1 || lgcols(g)==1) { str_puts(S, "[;]"); return; }
+  l = lgcols(g); str_putc(S, '\n');
+  print = (typ(gel(g,1)) == t_VECSMALL)? prints: bruti;
+  av = avma;
+  w = term_width();
+  if (2*r < w)
+  {
+    long lgall = 2; /* opening [ and closing ] */
+    pari_sp av2;
+    outString scratchstr;
+    pad = cgetg(l*r+1, t_VECSMALL); /* left on stack if (S->use_stack)*/
+    av2 = avma;
+    str_init(&scratchstr, 1);
+    for (j=1; j<r; j++)
+    {
+      GEN col = gel(g,j);
+      long maxc = 0;
+      for (i=1; i<l; i++)
+      {
+        long lgs;
+        scratchstr.cur = scratchstr.string;
+        print(gel(col,i),T,&scratchstr);
+        lgs = scratchstr.cur-scratchstr.string;
+        pad[j*l+i] = -lgs;
+        if (maxc < lgs) maxc = lgs;
+      }
+      for (i=1; i<l; i++) pad[j*l+i] += maxc;
+      lgall += maxc + 1; /* column width, including separating space */
+      if (lgall > w) { pad = NULL; break; } /* doesn't fit, abort padding */
+    }
+    avma = av2;
+  }
+  for (i=1; i<l; i++)
+  {
+    str_putc(S, '[');
+    for (j=1; j<r; j++)
+    {
+      if (pad) {
+        long white = pad[j*l+i];
+        while (white-- > 0) str_putc(S, ' ');
+      }
+      print(gcoeff(g,i,j),T,S); if (j<r-1) str_putc(S, ' ');
+    }
+    if (i<l-1) str_puts(S, "]\n\n"); else str_puts(S, "]\n");
+  }
+  if (!S->use_stack) avma = av;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           TeX OUTPUT                           **/
+/**                                                                **/
+/********************************************************************/
+/* this follows bruti_sign */
+static void
+texi_sign(GEN g, pariout_t *T, outString *S, int addsign)
+{
+  long tg,i,j,l,r;
+  GEN a,b;
+  const char *v;
+  char buf[67];
+
+  if (print_0_or_pm1(g, S, addsign)) return;
+
+  tg = typ(g);
+  switch(tg)
+  {
+    case t_INT: case t_REAL: case t_QFR: case t_QFI:
+      bruti_intern(g, T, S, addsign); break;
+
+    case t_INTMOD: case t_POLMOD:
+      texi(gel(g,2),T,S); str_puts(S, " mod ");
+      texi(gel(g,1),T,S); break;
+
+    case t_FRAC:
+      if (addsign && isfactor(gel(g,1)) < 0) str_putc(S, '-');
+      str_puts(S, "\\frac{");
+      texi_sign(gel(g,1),T,S,0);
+      str_puts(S, "}{");
+      texi_sign(gel(g,2),T,S,0);
+      str_puts(S, "}"); break;
+
+    case t_RFRAC:
+      str_puts(S, "\\frac{");
+      texi(gel(g,1),T,S); /* too complicated otherwise */
+      str_puts(S, "}{");
+      texi(gel(g,2),T,S);
+      str_puts(S, "}"); break;
+
+    case t_FFELT:
+      bruti_sign(FF_to_FpXQ_i(g),T,S,addsign);
+      break;
+
+    case t_COMPLEX: case t_QUAD: r = (tg==t_QUAD);
+      a = gel(g,r+1); b = gel(g,r+2); v = r? "w": "I";
+      if (isnull(a))
+      {
+        wr_lead_texnome(T,S,b,v,1,addsign);
+        break;
+      }
+      texi_sign(a,T,S,addsign);
+      if (!isnull(b)) wr_texnome(T,S,b,v,1);
+      break;
+
+    case t_POL: v = get_texvar(varn(g), buf, sizeof(buf));
+      /* hack: we want g[i] = coeff of degree i. */
+      i = degpol(g); g += 2; while (isnull(gel(g,i))) i--;
+      wr_lead_texnome(T,S,gel(g,i),v,i,addsign);
+      while (i--)
+      {
+        a = gel(g,i);
+        if (!isnull_for_pol(a)) wr_texnome(T,S,a,v,i);
+      }
+      break;
+
+    case t_SER: v = get_texvar(varn(g), buf, sizeof(buf));
+      i = valp(g);
+      if (lgpol(g))
+      { /* hack: we want g[i] = coeff of degree i. */
+        l = i + lgpol(g); g -= i-2;
+        wr_lead_texnome(T,S,gel(g,i),v,i,addsign);
+        while (++i < l)
+        {
+          a = gel(g,i);
+          if (!isnull_for_pol(a)) wr_texnome(T,S,a,v,i);
+        }
+        str_puts(S, "+ ");
+      }
+      str_puts(S, "O("); texnome(S,v,i); str_putc(S, ')'); break;
+
+    case t_PADIC:
+    {
+      GEN p = gel(g,2);
+      pari_sp av;
+      char *ev;
+      str_alloc(S, (precp(g)+1) * lgefint(p)); /* careful! */
+      av = avma;
+      i = valp(g); l = precp(g)+i;
+      g = gel(g,4); ev = itostr(p);
+      for (; i<l; i++)
+      {
+        g = dvmdii(g,p,&a);
+        if (signe(a))
+        {
+          if (!i || !is_pm1(a))
+          {
+            str_absint(S, a); if (i) str_puts(S, "\\cdot");
+          }
+          if (i) texVpowE(S, ev,i);
+          str_putc(S, '+');
+        }
+      }
+      str_puts(S, "O("); texVpowE(S, ev,i); str_putc(S, ')');
+      avma = av; break;
+    }
+
+    case t_VEC:
+      str_puts(S, "\\pmatrix{ "); l = lg(g);
+      for (i=1; i<l; i++)
+      {
+        texi(gel(g,i),T,S); if (i < l-1) str_putc(S, '&');
+      }
+      str_puts(S, "\\cr}\n"); break;
+
+    case t_LIST:
+      str_puts(S, "\\pmatrix{ ");
+      g = list_data(g);
+      l = g? lg(g): 1;
+      for (i=1; i<l; i++)
+      {
+        texi(gel(g,i),T,S); if (i < l-1) str_putc(S, '&');
+      }
+      str_puts(S, "\\cr}\n"); break;
+
+    case t_COL:
+      str_puts(S, "\\pmatrix{ "); l = lg(g);
+      for (i=1; i<l; i++)
+      {
+        texi(gel(g,i),T,S); str_puts(S, "\\cr\n");
+      }
+      str_putc(S, '}'); break;
+
+    case t_VECSMALL:
+      str_puts(S, "\\pmatrix{ "); l = lg(g);
+      for (i=1; i<l; i++)
+      {
+        str_long(S, g[i]);
+        if (i < l-1) str_putc(S, '&');
+      }
+      str_puts(S, "\\cr}\n"); break;
+
+    case t_STR:
+      str_puts(S, GSTR(g)); break;
+
+    case t_CLOSURE:
+      if (lg(g)>=6)
+      {
+        GEN str = closure_get_text(g);
+        if (typ(str)==t_STR)
+          str_puts(S, GSTR(str));
+        else
+        {
+          str_putc(S,'(');          str_puts(S,GSTR(gel(str,1)));
+          str_puts(S,")\\mapsto ");
+          print_context(g, T, S ,1); str_puts(S,GSTR(gel(str,2)));
+        }
+      }
+      else
+      {
+        str_puts(S,"\\{\""); str_puts(S,GSTR(closure_get_code(g)));
+        str_puts(S,"\","); texi(gel(g,3),T,S);
+        str_putc(S,',');   texi(gel(g,4),T,S);
+        str_putc(S,',');   texi(gel(g,5),T,S); str_puts(S,"\\}");
+      }
+      break;
+
+    case t_MAT:
+    {
+      str_puts(S, "\\pmatrix{\n "); r = lg(g);
+      if (r>1)
+      {
+        OUT_FUN print = (typ(gel(g,1)) == t_VECSMALL)? prints: texi;
+
+        l = lgcols(g);
+        for (i=1; i<l; i++)
+        {
+          for (j=1; j<r; j++)
+          {
+            print(gcoeff(g,i,j),T,S); if (j<r-1) str_putc(S, '&');
+          }
+          str_puts(S, "\\cr\n ");
+        }
+      }
+      str_putc(S, '}'); break;
+    }
+  }
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                        USER OUTPUT FUNCTIONS                  **/
+/**                                                               **/
+/*******************************************************************/
+static void
+_initout(pariout_t *T, char f, long sigd, long sp)
+{
+  T->format = f;
+  T->sigd = sigd;
+  T->sp = sp;
+}
+
+static void
+gen_output_fun(GEN x, pariout_t *T, OUT_FUN out)
+{
+  char *s = GENtostr_fun(x, T, out);
+  pari_puts(s); pari_free(s);
+}
+
+void
+fputGEN_pariout(GEN x, pariout_t *T, FILE *out)
+{
+  char *s = GENtostr_fun(x, T, get_fun(T->prettyp));
+  if (*s) set_last_newline(s[strlen(s)-1]);
+  fputs(s, out); pari_free(s);
+}
+
+void
+gen_output(GEN x, pariout_t *T)
+{
+  if (!T) T = GP_DATA->fmt;
+  gen_output_fun(x, T, get_fun(T->prettyp));
+}
+
+void
+brute(GEN g, char f, long d)
+{
+  pariout_t T; _initout(&T,f,d,0);
+  gen_output_fun(g, &T, &bruti);
+}
+
+void
+matbrute(GEN g, char f, long d)
+{
+  pariout_t T; _initout(&T,f,d,1);
+  gen_output_fun(g, &T, &matbruti);
+}
+
+void
+texe(GEN g, char f, long d)
+{
+  pariout_t T; _initout(&T,f,d,0);
+  gen_output_fun(g, &T, &texi);
+}
+
+void
+output(GEN x)
+{ brute(x,'g',-1); pari_putc('\n'); pari_flush(); }
+void
+outmat(GEN x)
+{ matbrute(x,'g',-1); pari_putc('\n'); pari_flush(); }
+
+void
+err_printf(const char* fmt, ...)
+{
+  va_list args; va_start(args, fmt);
+  out_vprintf(pariErr,fmt,args); va_end(args);
+}
+
+/*******************************************************************/
+/**                            FILES                              **/
+/*******************************************************************/
+/* to cache '~' expansion */
+static char *homedir;
+/* last file read successfully from try_name() */
+static THREAD char *last_filename;
+/* stack of temporary files (includes all infiles + some output) */
+static THREAD pariFILE *last_tmp_file;
+/* stack of "permanent" (output) files */
+static THREAD pariFILE *last_file;
+
+pariFILE *
+pari_last_tmp_file(void) { return last_tmp_file; }
+
+#if defined(UNIX) || defined(__EMX__)
+#  include <fcntl.h>
+#  include <sys/stat.h> /* for open */
+#  ifdef __EMX__
+#    include <process.h>
+#  endif
+#  define HAVE_PIPES
+#endif
+#if defined(_WIN32)
+#  define HAVE_PIPES
+#endif
+#ifndef O_RDONLY
+#  define O_RDONLY 0
+#endif
+
+pariFILE *
+newfile(FILE *f, const char *name, int type)
+{
+  pariFILE *file = (pariFILE*) pari_malloc(strlen(name) + 1 + sizeof(pariFILE));
+  file->type = type;
+  file->name = strcpy((char*)(file+1), name);
+  file->file = f;
+  file->next = NULL;
+  if (type & mf_PERM)
+  {
+    file->prev = last_file;
+    last_file = file;
+  }
+  else
+  {
+    file->prev = last_tmp_file;
+    last_tmp_file = file;
+  }
+  if (file->prev) (file->prev)->next = file;
+  if (DEBUGFILES)
+    err_printf("I/O: new pariFILE %s (code %d) \n",name,type);
+  return file;
+}
+
+static void
+pari_kill_file(pariFILE *f)
+{
+  if ((f->type & mf_PIPE) == 0)
+  {
+    if (f->file != stdin && fclose(f->file))
+      pari_warn(warnfile, "close", f->name);
+  }
+#ifdef HAVE_PIPES
+  else
+  {
+    if (f->type & mf_FALSE)
+    {
+      if (f->file != stdin && fclose(f->file))
+        pari_warn(warnfile, "close", f->name);
+      if (unlink(f->name)) pari_warn(warnfile, "delete", f->name);
+    }
+    else
+      if (pclose(f->file) < 0) pari_warn(warnfile, "close pipe", f->name);
+  }
+#endif
+  if (DEBUGFILES)
+    err_printf("I/O: closing file %s (code %d) \n",f->name,f->type);
+  pari_free(f);
+}
+
+void
+pari_fclose(pariFILE *f)
+{
+  if (f->next) (f->next)->prev = f->prev;
+  else if (f == last_tmp_file) last_tmp_file = f->prev;
+  else if (f == last_file) last_file = f->prev;
+  if (f->prev) (f->prev)->next = f->next;
+  pari_kill_file(f);
+}
+
+static pariFILE *
+pari_open_file(FILE *f, const char *s, const char *mode)
+{
+  if (!f) pari_err_FILE("requested file", s);
+  if (DEBUGFILES)
+    err_printf("I/O: opening file %s (mode %s)\n", s, mode);
+  return newfile(f,s,0);
+}
+
+pariFILE *
+pari_fopen_or_fail(const char *s, const char *mode)
+{
+  return pari_open_file(fopen(s, mode), s, mode);
+}
+pariFILE *
+pari_fopen(const char *s, const char *mode)
+{
+  FILE *f = fopen(s, mode);
+  return f? pari_open_file(f, s, mode): NULL;
+}
+
+void
+pari_fread_chars(void *b, size_t n, FILE *f)
+{
+  if (fread(b, sizeof(char), n, f) < n)
+    pari_err_FILE("input file [fread]", "FILE*");
+}
+
+/* FIXME: HAS_FDOPEN & allow standard open() flags */
+#ifdef UNIX
+/* open tmpfile s (a priori for writing) avoiding symlink attacks */
+pariFILE *
+pari_safefopen(const char *s, const char *mode)
+{
+  long fd = open(s, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
+
+  if (fd == -1) pari_err(e_MISC,"tempfile %s already exists",s);
+  return pari_open_file(fdopen(fd, mode), s, mode);
+}
+#else
+pariFILE *
+pari_safefopen(const char *s, const char *mode)
+{
+  return pari_fopen_or_fail(s, mode);
+}
+#endif
+
+void
+pari_unlink(const char *s)
+{
+  if (unlink(s)) pari_warn(warner, "I/O: can\'t remove file %s", s);
+  else if (DEBUGFILES)
+    err_printf("I/O: removed file %s\n", s);
+}
+
+void
+check_filtre(filtre_t *T)
+{
+  if (T && T->in_string)
+  {
+    pari_warn(warner,"run-away string. Closing it");
+    T->in_string = 0;
+  }
+  if (T && T->in_comment)
+  {
+    pari_warn(warner,"run-away comment. Closing it");
+    T->in_comment = 0;
+  }
+}
+
+/* Remove one INFILE from the stack. Reset pari_infile (to the most recent
+ * infile)
+ * Return -1, if we're trying to pop out stdin itself; 0 otherwise
+ * Check for leaked file handlers (temporary files) */
+int
+popinfile(void)
+{
+  pariFILE *f = last_tmp_file, *g;
+  while (f)
+  {
+    if (f->type & mf_IN) break;
+    pari_warn(warner, "I/O: leaked file descriptor (%d): %s", f->type, f->name);
+    g = f; f = f->prev; pari_fclose(g);
+  }
+  last_tmp_file = f; if (!f) return -1;
+  pari_fclose(last_tmp_file);
+  for (f = last_tmp_file; f; f = f->prev)
+    if (f->type & mf_IN) { pari_infile = f->file; return 0; }
+  pari_infile = stdin; return 0;
+}
+
+/* delete all "temp" files open since last reference point F */
+void
+filestate_restore(pariFILE *F)
+{
+  pariFILE *f = pari_last_tmp_file();
+  if (DEBUGFILES>1) err_printf("gp_context_restore: deleting open files...\n");
+  while (f)
+  {
+    pariFILE *g = f->prev;
+    if (f == F) break;
+    pari_fclose(f); f = g;
+  }
+  for (; f; f = f->prev) {
+    if (f->type & mf_IN) {
+      pari_infile = f->file;
+      if (DEBUGFILES>1)
+        err_printf("restoring pari_infile to %s\n", f->name);
+      break;
+    }
+  }
+  if (!f) {
+    pari_infile = stdin;
+    if (DEBUGFILES>1)
+      err_printf("gp_context_restore: restoring pari_infile to stdin\n");
+  }
+  if (DEBUGFILES>1) err_printf("done\n");
+}
+
+static void
+kill_file_stack(pariFILE **s)
+{
+  pariFILE *f = *s;
+  while (f)
+  {
+    pariFILE *t = f->prev;
+    pari_kill_file(f);
+    *s = f = t; /* have to update *s in case of ^C */
+  }
+}
+
+void
+killallfiles(void)
+{
+  kill_file_stack(&last_tmp_file);
+  pari_infile = stdin;
+}
+
+void
+pari_init_homedir(void)
+{
+  homedir = NULL;
+}
+
+void
+pari_close_homedir(void)
+{
+  if (homedir) pari_free(homedir);
+}
+
+void
+pari_init_files(void)
+{
+  last_filename = NULL;
+  last_tmp_file = NULL;
+  last_file=NULL;
+}
+
+void
+pari_close_files(void)
+{
+  popinfile(); /* look for leaks */
+  kill_file_stack(&last_file);
+  if (last_filename) pari_free(last_filename);
+  if (pari_logfile) { fclose(pari_logfile); pari_logfile = NULL; }
+  killallfiles();
+}
+
+static int
+ok_pipe(FILE *f)
+{
+  if (DEBUGFILES) err_printf("I/O: checking output pipe...\n");
+  pari_CATCH(CATCH_ALL) {
+    return 0;
+  }
+  pari_TRY {
+    int i;
+    fprintf(f,"\n\n"); fflush(f);
+    for (i=1; i<1000; i++) fprintf(f,"                  \n");
+    fprintf(f,"\n"); fflush(f);
+  } pari_ENDCATCH;
+  return 1;
+}
+
+pariFILE *
+try_pipe(const char *cmd, int fl)
+{
+#ifndef HAVE_PIPES
+  pari_err(e_ARCH,"pipes"); return NULL;
+#else
+  FILE *file;
+  const char *f;
+  VOLATILE int flag = fl;
+
+#  ifdef __EMX__
+  if (_osmode == DOS_MODE) /* no pipes under DOS */
+  {
+    pari_sp av = avma;
+    char *s;
+    if (flag & mf_OUT) pari_err(e_ARCH,"pipes");
+    f = pari_unique_filename("pipe");
+    s = stack_malloc(strlen(cmd)+strlen(f)+4);
+    sprintf(s,"%s > %s",cmd,f);
+    file = system(s)? NULL: fopen(f,"r");
+    flag |= mf_FALSE; pari_free(f); avma = av;
+  }
+  else
+#  endif
+  {
+    file = (FILE *) popen(cmd, (flag & mf_OUT)? "w": "r");
+    if (flag & mf_OUT) {
+      if (!ok_pipe(file)) return NULL;
+      flag |= mf_PERM;
+    }
+    f = cmd;
+  }
+  if (!file) pari_err(e_MISC,"[pipe:] '%s' failed",cmd);
+  return newfile(file, f, mf_PIPE|flag);
+#endif
+}
+
+typedef void (*pari_sighandler_t)(int);
+
+pari_sighandler_t
+os_signal(int sig, pari_sighandler_t f)
+{
+#ifdef HAS_SIGACTION
+  struct sigaction sa, oldsa;
+
+  sa.sa_handler = f;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_NODEFER;
+
+  if (sigaction(sig, &sa, &oldsa)) return NULL;
+  return oldsa.sa_handler;
+#elif defined(WINCE)
+  return SIG_IGN;
+#else
+  return signal(sig,f);
+#endif
+}
+
+#if 0
+void
+os_close(long fd)
+{
+#ifdef WINCE
+  CloseHandle((HANDLE)fd);
+#else
+  close(fd);
+#endif
+}
+void
+os_read(long fd, char ch[], long s)
+{
+#ifdef WINCE
+  DWORD chRead;
+  ReadFile((HANDLE)fd, ch, s, &chRead, NULL);
+#else
+  (void)read(fd,ch,s);
+#endif
+}
+long
+os_open(const char *s, int mode)
+{
+  long fd;
+#ifdef WINCE
+  HANDLE h;
+  short ws[256];
+  if (mode != O_RDONLY) pari_err_IMPL("generic open for Windows");
+  MultiByteToWideChar(CP_ACP, 0, s, strlen(s)+1, ws, 256);
+  h = CreateFile(ws,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
+  fd = (h == INVALID_HANDLE_VALUE)? (long)-1: (long)h;
+#else
+  fd = open(s,mode);
+#endif
+  return fd;
+}
+#endif
+
+char *
+os_getenv(const char *s)
+{
+#ifdef HAS_GETENV
+  return getenv(s);
+#else
+  (void) s; return NULL;
+#endif
+}
+
+GEN
+gp_getenv(const char *s)
+{
+  char *t = os_getenv(s);
+  return t?strtoGENstr(t):gen_0;
+}
+
+/* FIXME: HAS_GETPWUID */
+#if defined(UNIX) || defined(__EMX__)
+#include <pwd.h>
+#include <sys/types.h>
+/* user = "": use current uid */
+char *
+pari_get_homedir(const char *user)
+{
+  struct passwd *p;
+  char *dir = NULL;
+
+  if (!*user)
+  {
+    if (homedir) dir = homedir;
+    else
+    {
+      p = getpwuid(geteuid());
+      if (p)
+      {
+        dir = p->pw_dir;
+        homedir = pari_strdup(dir); /* cache result */
+      }
+    }
+  }
+  else
+  {
+    p = getpwnam(user);
+    if (p) dir = p->pw_dir;
+  }
+  /* warn, but don't kill session on startup (when expanding path) */
+  if (!dir) pari_warn(warner,"can't expand ~%s", user? user: "");
+  return dir;
+}
+#else
+char *
+pari_get_homedir(const char *user) { (void) user; return NULL; }
+#endif
+
+/*******************************************************************/
+/**                                                               **/
+/**                   GP STANDARD INPUT AND OUTPUT                **/
+/**                                                               **/
+/*******************************************************************/
+#ifdef HAS_OPENDIR
+/* slow, but more portable than stat + S_ISDIR */
+#  include <dirent.h>
+static int
+is_dir_opendir(const char *name)
+{
+  DIR *d = opendir(name);
+  if (d) { (void)closedir(d); return 1; }
+  return 0;
+}
+#endif
+
+#ifdef HAS_STAT
+#include <sys/stat.h>
+static int
+is_dir_stat(const char *name)
+{
+  struct stat buf;
+  if (stat(name, &buf)) return 0;
+  return S_ISDIR(buf.st_mode);
+}
+#endif
+
+/* Does name point to a directory? */
+int
+pari_is_dir(const char *name)
+{
+#ifdef HAS_STAT
+  return is_dir_stat(name);
+#else
+#  ifdef HAS_OPENDIR
+  return is_dir_opendir(name);
+#  else
+  (void) name; return 0;
+#  endif
+#endif
+}
+
+/* Does name point to a regular file? */
+/* If unknown, assume that it is indeed regular. */
+int
+pari_is_file(const char *name)
+{
+#ifdef HAS_STAT
+  struct stat buf;
+  if (stat(name, &buf)) return 1;
+  return S_ISREG(buf.st_mode);
+#else
+  (void) name; return 1;
+#endif
+}
+
+int
+pari_stdin_isatty(void)
+{
+#ifdef HAS_ISATTY
+  return isatty( fileno(stdin) );
+#else
+  return 1;
+#endif
+}
+
+/* expand tildes in filenames, return a malloc'ed buffer */
+static char *
+_path_expand(const char *s)
+{
+  const char *t;
+  char *ret, *dir = NULL;
+
+  if (*s != '~') return pari_strdup(s);
+  s++; /* skip ~ */
+  t = s; while (*t && *t != '/') t++;
+  if (t == s)
+    dir = pari_get_homedir("");
+  else
+  {
+    size_t len = t - s;
+    char *user = (char*)pari_malloc(len+1);
+    (void)strncpy(user,s,len); user[len] = 0;
+    dir = pari_get_homedir(user);
+    pari_free(user);
+  }
+  if (!dir) return pari_strdup(s);
+  ret = (char*)pari_malloc(strlen(dir) + strlen(t) + 1);
+  sprintf(ret,"%s%s",dir,t); return ret;
+}
+
+/* expand environment variables in str, return a malloc'ed buffer
+ * assume no \ remain and str can be freed */
+static char *
+_expand_env(char *str)
+{
+  long i, l, len = 0, xlen = 16, xnum = 0;
+  char *s = str, *s0 = s, *env;
+  char **x = (char **)pari_malloc(xlen * sizeof(char*));
+
+  while (*s)
+  {
+    if (*s != '$') { s++; continue; }
+    l = s - s0;
+    if (l)
+    {
+      s0 = strncpy((char*)pari_malloc(l+1), s0, l); s0[l] = 0;
+      x[xnum++] = s0; len += l;
+    }
+    if (xnum > xlen - 3) /* need room for possibly two more elts */
+    {
+      xlen <<= 1;
+      x = (char **)pari_realloc((void*)x, xlen * sizeof(char*));
+    }
+
+    s0 = ++s; /* skip $ */
+    while (is_keyword_char(*s)) s++;
+    l = s - s0;
+    env = strncpy((char*)pari_malloc(l+1), s0, l); env[l] = 0;
+    s0 = os_getenv(env);
+    if (!s0)
+    {
+      pari_warn(warner,"undefined environment variable: %s",env);
+      s0 = (char*)"";
+    }
+    l = strlen(s0);
+    if (l)
+    {
+      s0 = strncpy((char*)pari_malloc(l+1), s0, l); s0[l] = 0;
+      x[xnum++] = s0; len += l;
+    }
+    pari_free(env); s0 = s;
+  }
+  l = s - s0;
+  if (l)
+  {
+    s0 = strncpy((char*)pari_malloc(l+1), s0, l); s0[l] = 0;
+    x[xnum++] = s0; len += l;
+  }
+
+  s = (char*)pari_malloc(len+1); *s = 0;
+  for (i = 0; i < xnum; i++) { (void)strcat(s, x[i]); pari_free(x[i]); }
+  pari_free(str); pari_free(x); return s;
+}
+
+char *
+path_expand(const char *s)
+{
+#ifdef _WIN32
+  char *ss, *p;
+  ss = pari_strdup(s);
+  for (p = ss; *p != 0; ++p)
+    if (*p == '\\') *p = '/';
+  p = _expand_env(_path_expand(ss));
+  pari_free(ss);
+  return p;
+#else
+  return _expand_env(_path_expand(s));
+#endif
+}
+
+#ifdef HAS_STRFTIME
+#  include <time.h>
+void
+strftime_expand(const char *s, char *buf, long max)
+{
+  time_t t;
+  BLOCK_SIGINT_START
+  t = time(NULL);
+  (void)strftime(buf,max,s,localtime(&t));
+  BLOCK_SIGINT_END
+}
+#else
+void
+strftime_expand(const char *s, char *buf, long max)
+{ strcpy(buf,s); }
+#endif
+
+void
+delete_dirs(gp_path *p)
+{
+  char **v = p->dirs, **dirs;
+  if (v)
+  {
+    p->dirs = NULL; /* in case of error */
+    for (dirs = v; *dirs; dirs++) pari_free(*dirs);
+    pari_free(v);
+  }
+}
+
+#if defined(__EMX__) || defined(_WIN32) || defined(__CYGWIN32__)
+#  define PATH_SEPARATOR ';' /* beware DOSish 'C:' disk drives */
+#else
+#  define PATH_SEPARATOR ':'
+#endif
+
+const char *
+pari_default_path(void) {
+#if PATH_SEPARATOR == ';'
+  return ".;C:;C:/gp";
+#elif defined(UNIX)
+  return ".:~:~/gp";
+#else
+  return ".";
+#endif
+}
+
+void
+gp_expand_path(gp_path *p)
+{
+  char **dirs, *s, *v = p->PATH;
+  int i, n = 0;
+
+  delete_dirs(p);
+  v = pari_strdup(v);
+  for (s=v; *s; s++)
+    if (*s == PATH_SEPARATOR) {
+      *s = 0;
+      if (s == v || s[-1] != 0) n++; /* ignore empty path components */
+    }
+  dirs = (char**) pari_malloc((n + 2)*sizeof(char *));
+
+  for (s=v, i=0; i<=n; i++)
+  {
+    char *end, *f;
+    while (!*s) s++; /* skip empty path components */
+    f = end = s + strlen(s);
+    while (f > s && *--f == '/') *f = 0; /* skip trailing '/' */
+    dirs[i] = path_expand(s);
+    s = end + 1; /* next path component */
+  }
+  pari_free((void*)v);
+  dirs[i] = NULL; p->dirs = dirs;
+}
+
+/* name is a malloc'ed (existing) filename. Accept it as new pari_infile
+ * (unzip if needed). */
+static pariFILE *
+pari_get_infile(const char *name, FILE *file)
+{
+#ifdef ZCAT
+  long l = strlen(name);
+  const char *end = name + l-1;
+
+  if (l > 2 && (!strncmp(end-1,".Z",2)
+#ifdef GNUZCAT
+             || !strncmp(end-2,".gz",3)
+#endif
+  ))
+  { /* compressed file (compress or gzip) */
+    char *cmd = stack_malloc(strlen(ZCAT) + l + 4);
+    sprintf(cmd,"%s \"%s\"",ZCAT,name);
+    fclose(file);
+    return try_pipe(cmd, mf_IN);
+  }
+#endif
+  return newfile(file, name, mf_IN);
+}
+
+pariFILE *
+pari_fopengz(const char *s)
+{
+  pari_sp av = avma;
+  char *name;
+  long l;
+  FILE *f = fopen(s, "r");
+  pariFILE *pf;
+
+  if (f) return pari_get_infile(s, f);
+
+  l = strlen(s);
+  name = stack_malloc(l + 3 + 1);
+  strcpy(name, s); (void)sprintf(name + l, ".gz");
+  f = fopen(name, "r");
+  pf = f ? pari_get_infile(name, f): NULL;
+  avma = av; return pf;
+}
+
+static FILE*
+try_open(char *s)
+{
+  if (!pari_is_dir(s)) return fopen(s, "r");
+  pari_warn(warner,"skipping directory %s",s);
+  return NULL;
+}
+
+void
+forpath_init(forpath_t *T, gp_path *path, const char *s)
+{
+  T->s = s;
+  T->ls = strlen(s);
+  T->dir = path->dirs;
+}
+char *
+forpath_next(forpath_t *T)
+{
+  char *t, *dir = T->dir[0];
+
+  if (!dir) return NULL; /* done */
+  /* room for dir + '/' + s + '\0' */
+  t = (char*)pari_malloc(strlen(dir) + T->ls + 2);
+  sprintf(t,"%s/%s", dir, T->s);
+  T->dir++; return t;
+}
+
+/* If a file called "name" exists (possibly after appending ".gp")
+ * record it in the file_stack (as a pipe if compressed).
+ * name is malloc'ed, we free it before returning
+ */
+static FILE *
+try_name(char *name)
+{
+  pari_sp av = avma;
+  char *s = name;
+  FILE *file = try_open(name);
+
+  if (!file)
+  { /* try appending ".gp" to name */
+    s = stack_malloc(strlen(name)+4);
+    sprintf(s, "%s.gp", name);
+    file = try_open(s);
+  }
+  if (file)
+  {
+    if (! last_tmp_file)
+    {  /* empty file stack, record this name */
+      if (last_filename) pari_free(last_filename);
+      last_filename = pari_strdup(s);
+    }
+    file = pari_infile = pari_get_infile(s,file)->file;
+  }
+  pari_free(name); avma = av;
+  return file;
+}
+static FILE *
+switchin_last(void)
+{
+  char *s = last_filename;
+  FILE *file;
+  if (!s) pari_err(e_MISC,"You never gave me anything to read!");
+  file = try_open(s);
+  if (!file) pari_err_FILE("input file",s);
+  return pari_infile = pari_get_infile(s,file)->file;
+}
+
+/* return 1 if s starts by '/' or './' or '../' */
+int
+path_is_absolute(char *s)
+{
+#ifdef _WIN32
+  if( (*s >= 'A' && *s <= 'Z') ||
+      (*s >= 'a' && *s <= 'z') )
+  {
+      return *(s+1) == ':';
+  }
+#endif
+  if (*s == '/') return 1;
+  if (*s++ != '.') return 0;
+  if (*s == '/') return 1;
+  if (*s++ != '.') return 0;
+  return *s == '/';
+}
+
+/* If name = "", re-read last file */
+FILE *
+switchin(const char *name)
+{
+  FILE *f;
+  char *s;
+
+  if (!*name) return switchin_last();
+  s = path_expand(name);
+  /* if s is an absolute path, don't use dir_list */
+  if (path_is_absolute(s)) { if ((f = try_name(s))) return f; }
+  else
+  {
+    char *t;
+    forpath_t T;
+    forpath_init(&T, GP_DATA->path, s);
+    while ( (t = forpath_next(&T)) )
+      if ((f = try_name(t))) return f;
+  }
+  pari_err_FILE("input file",name);
+  return NULL; /*not reached*/
+}
+
+static int is_magic_ok(FILE *f);
+
+static FILE *
+switchout_get_FILE(const char *name)
+{
+  FILE* f;
+  /* only for ordinary files (to avoid blocking on pipes). */
+  if (pari_is_file(name))
+  {
+    f = fopen(name, "r");
+    if (f)
+    {
+      int magic = is_magic_ok(f);
+      fclose(f);
+      if (magic) pari_err_FILE("binary output file [ use writebin ! ]", name);
+    }
+  }
+  f = fopen(name, "a");
+  if (!f) pari_err_FILE("output file",name);
+  return f;
+}
+
+void
+switchout(const char *name)
+{
+  if (name)
+    pari_outfile = switchout_get_FILE(name);
+  else if (pari_outfile != stdout)
+  {
+    fclose(pari_outfile);
+    pari_outfile = stdout;
+  }
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                    I/O IN BINARY FORM                         **/
+/**                                                               **/
+/*******************************************************************/
+static void
+pari_fread_longs(void *a, size_t c, FILE *d)
+{ if (fread(a,sizeof(long),c,d) < c)
+    pari_err_FILE("input file [fread]", "FILE*"); }
+
+static void
+_fwrite(const void *a, size_t b, size_t c, FILE *d)
+{ if (fwrite(a,b,c,d) < c) pari_err_FILE("output file [fwrite]", "FILE*"); }
+static void
+_lfwrite(const void *a, size_t b, FILE *c) { _fwrite(a,sizeof(long),b,c); }
+static void
+_cfwrite(const void *a, size_t b, FILE *c) { _fwrite(a,sizeof(char),b,c); }
+
+enum { BIN_GEN, NAM_GEN, VAR_GEN, RELINK_TABLE };
+
+static long
+rd_long(FILE *f) { long L; pari_fread_longs(&L, 1UL, f); return L; }
+static void
+wr_long(long L, FILE *f) { _lfwrite(&L, 1UL, f); }
+
+/* append x to file f */
+static void
+wrGEN(GEN x, FILE *f)
+{
+  GENbin *p = copy_bin_canon(x);
+  size_t L = p->len;
+
+  wr_long(L,f);
+  if (L)
+  {
+    wr_long((long)p->x,f);
+    wr_long((long)p->base,f);
+    _lfwrite(GENbinbase(p), L,f);
+  }
+  pari_free((void*)p);
+}
+
+static void
+wrstr(const char *s, FILE *f)
+{
+  size_t L = strlen(s)+1;
+  wr_long(L,f);
+  _cfwrite(s, L, f);
+}
+
+static char *
+rdstr(FILE *f)
+{
+  size_t L = (size_t)rd_long(f);
+  char *s;
+  if (!L) return NULL;
+  s = (char*)pari_malloc(L);
+  pari_fread_chars(s, L, f); return s;
+}
+
+static void
+writeGEN(GEN x, FILE *f)
+{
+  fputc(BIN_GEN,f);
+  wrGEN(x, f);
+}
+
+static void
+writenamedGEN(GEN x, const char *s, FILE *f)
+{
+  fputc(x ? NAM_GEN : VAR_GEN,f);
+  wrstr(s, f);
+  if (x) wrGEN(x, f);
+}
+
+/* read a GEN from file f */
+static GEN
+rdGEN(FILE *f)
+{
+  size_t L = (size_t)rd_long(f);
+  GENbin *p;
+
+  if (!L) return gen_0;
+  p = (GENbin*)pari_malloc(sizeof(GENbin) + L*sizeof(long));
+  p->len  = L;
+  p->x    = (GEN)rd_long(f);
+  p->base = (GEN)rd_long(f);
+  p->canon= 1;
+  pari_fread_longs(GENbinbase(p), L,f);
+  return bin_copy(p);
+}
+
+/* read a binary object in file f. Set *ptc to the object "type":
+ * BIN_GEN: an anonymous GEN x; return x.
+ * NAM_GEN: a named GEN x, with name v; set 'v to x (changevalue) and return x
+ * VAR_GEN: a name v; create the (unassigned) variable v and return gnil
+ * RELINK_TABLE: a relinking table for gen_relink(), to replace old adresses
+ * in * the original session by new incarnations in the current session.
+ * H is the current relinking table
+ * */
+static GEN
+readobj(FILE *f, int *ptc, hashtable *H)
+{
+  int c = fgetc(f);
+  GEN x = NULL;
+  switch(c)
+  {
+    case BIN_GEN:
+      x = rdGEN(f);
+      if (H) gen_relink(x, H);
+      break;
+    case NAM_GEN:
+    case VAR_GEN:
+    {
+      char *s = rdstr(f);
+      if (!s) pari_err(e_MISC,"malformed binary file (no name)");
+      if (c == NAM_GEN)
+      {
+        x = rdGEN(f);
+        if (H) gen_relink(x, H);
+        err_printf("setting %s\n",s);
+        changevalue(fetch_named_var(s), x);
+      }
+      else
+      {
+        pari_var_create(fetch_entry(s, strlen(s)));
+        x = gnil;
+      }
+      break;
+    }
+    case RELINK_TABLE:
+      x = rdGEN(f); break;
+    case EOF: break;
+    default: pari_err(e_MISC,"unknown code in readobj");
+  }
+  *ptc = c; return x;
+}
+
+#define MAGIC "\020\001\022\011-\007\020" /* ^P^A^R^I-^G^P */
+#ifdef LONG_IS_64BIT
+#  define ENDIAN_CHECK 0x0102030405060708L
+#else
+#  define ENDIAN_CHECK 0x01020304L
+#endif
+static const long BINARY_VERSION = 1; /* since 2.2.9 */
+
+static int
+is_magic_ok(FILE *f)
+{
+  pari_sp av = avma;
+  size_t L = strlen(MAGIC);
+  char *s = stack_malloc(L);
+  int r = (fread(s,1,L, f) == L && strncmp(s,MAGIC,L) == 0);
+  avma = av; return r;
+}
+
+static int
+is_sizeoflong_ok(FILE *f)
+{
+  char c;
+  return (fread(&c,1,1, f) == 1 && c == (char)sizeof(long));
+}
+
+static int
+is_long_ok(FILE *f, long L)
+{
+  long c;
+  return (fread(&c,sizeof(long),1, f) == 1 && c == L);
+}
+
+/* return 1 if valid binary file */
+static int
+check_magic(const char *name, FILE *f)
+{
+  if (!is_magic_ok(f))
+    pari_warn(warner, "%s is not a GP binary file",name);
+  else if (!is_sizeoflong_ok(f))
+    pari_warn(warner, "%s not written for a %ld bit architecture",
+               name, sizeof(long)*8);
+  else if (!is_long_ok(f, ENDIAN_CHECK))
+    pari_warn(warner, "unexpected endianness in %s",name);
+  else if (!is_long_ok(f, BINARY_VERSION))
+    pari_warn(warner, "%s written by an incompatible version of GP",name);
+  else return 1;
+  return 0;
+}
+
+static void
+write_magic(FILE *f)
+{
+  fprintf(f, MAGIC);
+  fprintf(f, "%c", (char)sizeof(long));
+  wr_long(ENDIAN_CHECK, f);
+  wr_long(BINARY_VERSION, f);
+}
+
+int
+file_is_binary(FILE *f)
+{
+  int c = fgetc(f); ungetc(c,f);
+  return (c != EOF && isprint(c) == 0 && isspace(c) == 0);
+}
+
+void
+writebin(const char *name, GEN x)
+{
+  FILE *f = fopen(name,"r");
+  pari_sp av = avma;
+  GEN V;
+  int already = f? 1: 0;
+
+  if (f) {
+    int ok = check_magic(name,f);
+    fclose(f);
+    if (!ok) pari_err_FILE("binary output file",name);
+  }
+  f = fopen(name,"a");
+  if (!f) pari_err_FILE("binary output file",name);
+  if (!already) write_magic(f);
+
+  V = copybin_unlink(x);
+  if (lg(gel(V,1)) > 1)
+  {
+    fputc(RELINK_TABLE,f);
+    wrGEN(V, f);
+  }
+  if (x) writeGEN(x,f);
+  else
+  {
+    long v, maxv = pari_var_next();
+    for (v=0; v<maxv; v++)
+    {
+      entree *ep = varentries[v];
+      if (!ep) continue;
+      writenamedGEN((GEN)ep->value,ep->name,f);
+    }
+  }
+  avma = av; fclose(f);
+}
+
+/* read all objects in f. If f contains BIN_GEN that would be silently ignored
+ * [i.e f contains more than one objet, not all of them 'named GENs'], return
+ * them all in a vector and set 'vector'. */
+GEN
+readbin(const char *name, FILE *f, int *vector)
+{
+  pari_sp av = avma;
+  hashtable *H = NULL;
+  pari_stack s_obj;
+  GEN obj, x, y;
+  int cy;
+  if (vector) *vector = 0;
+  if (!check_magic(name,f)) return NULL;
+  pari_stack_init(&s_obj, sizeof(GEN), (void**)&obj);
+  /* HACK: push codeword so as to be able to treat s_obj.data as a t_VEC */
+  pari_stack_pushp(&s_obj, (void*) (evaltyp(t_VEC)|evallg(1)));
+  x = gnil;
+  while ((y = readobj(f, &cy, H)))
+  {
+    x = y;
+    switch(cy)
+    {
+      case BIN_GEN:
+        pari_stack_pushp(&s_obj, (void*)y); break;
+      case RELINK_TABLE:
+        if (H) hash_destroy(H);
+        H = hash_from_link(gel(y,1),gel(y,2), 0);
+    }
+  }
+  if (H) hash_destroy(H);
+  switch(s_obj.n) /* >= 1 */
+  {
+    case 1: break; /* nothing but the codeword */
+    case 2: x = gel(obj,1); break; /* read a single BIN_GEN */
+    default: /* more than one BIN_GEN */
+      setlg(obj, s_obj.n);
+      if (DEBUGLEVEL)
+        pari_warn(warner,"%ld unnamed objects read. Returning then in a vector",
+                  s_obj.n - 1);
+      x = gerepilecopy(av, obj);
+      if (vector) *vector = 1;
+  }
+  pari_stack_delete(&s_obj);
+  return x;
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                             GP I/O                            **/
+/**                                                               **/
+/*******************************************************************/
+/* print a vector of GENs, in output context 'out', using 'sep' as a
+ * separator between sucessive entries [ NULL = no separator ]*/
+void
+out_print0(PariOUT *out, const char *sep, GEN g, long flag)
+{
+  pari_sp av0 = avma;
+  OUT_FUN f = get_fun(flag);
+  long i, l = lg(g);
+  for (i = 1; i < l; i++)
+  {
+    pari_sp av = avma;
+    GEN x = gel(g,i);
+    char *s = GENtostr_fun_unquoted(x, GP_DATA->fmt, f);
+    out_puts(out, s); avma = av;
+    if (sep && i+1 < l) out_puts(out, sep);
+  }
+  avma = av0;
+}
+
+static void
+str_print0(outString *S, GEN g, long flag)
+{
+  OUT_FUN f = get_fun(flag);
+  long i, l = lg(g);
+  for (i = 1; i < l; i++)
+  {
+    GEN x = gel(g,i);
+    if (typ(x)==t_STR)
+      str_puts(S, GSTR(x)); /* text surrounded by "" otherwise */
+    else
+    {
+      char *s = GENtostr_fun(x, GP_DATA->fmt, f);
+      str_puts(S, s); pari_free(s);
+    }
+  }
+}
+
+/*Display s, followed by the element of g */
+
+char *
+pari_sprint0(const char *s, GEN g, long flag)
+{
+  outString S;
+  str_init(&S, 0);
+  str_puts(&S, s);
+  str_print0(&S, g, flag);
+  *S.cur = 0; return S.string;
+}
+static void
+print0_file(FILE *out, GEN g, long flag)
+{
+  pari_sp av = avma;
+  outString S;
+  str_init(&S, 1);
+  str_print0(&S, g, flag);
+  *S.cur = 0;
+  fputs(S.string, out);
+  avma = av;
+}
+
+void
+print0(GEN g, long flag) { out_print0(pariOut, NULL, g, flag); }
+
+void
+printsep(const char *s, GEN g, long flag)
+{ out_print0(pariOut, s, g, flag); pari_putc('\n'); pari_flush(); }
+
+void
+printsep1(const char *s, GEN g, long flag) { out_print0(pariOut, s, g, flag); }
+
+/* dummy needed to pass a (empty!) va_list to sm_dopr */
+static char *
+dopr_arg_vector(GEN arg_vector, const char* fmt, ...)
+{
+  va_list ap;
+  char *s;
+  va_start(ap, fmt);
+  s = sm_dopr(fmt, arg_vector, ap);
+  va_end(ap); return s;
+}
+/* GP only */
+void
+printf0(const char *fmt, GEN args)
+{ char *s = dopr_arg_vector(args, fmt);
+  pari_puts(s); pari_free(s); pari_flush(); }
+/* GP only */
+GEN
+Strprintf(const char *fmt, GEN args)
+{ char *s = dopr_arg_vector(args, fmt);
+  GEN z = strtoGENstr(s); pari_free(s); return z; }
+
+void
+out_vprintf(PariOUT *out, const char *fmt, va_list ap)
+{
+  char *s = sm_dopr(fmt, NULL, ap);
+  out_puts(out, s); pari_free(s);
+}
+void
+pari_vprintf(const char *fmt, va_list ap) { out_vprintf(pariOut, fmt, ap); }
+
+/* variadic version of printf0 */
+void
+out_printf(PariOUT *out, const char *fmt, ...)
+{
+  va_list args; va_start(args,fmt);
+  out_vprintf(out,fmt,args); va_end(args);
+}
+void
+pari_printf(const char *fmt, ...) /* variadic version of printf0 */
+{
+  va_list args; va_start(args,fmt);
+  pari_vprintf(fmt,args); va_end(args);
+}
+
+char *
+pari_vsprintf(const char *fmt, va_list ap)
+{ return sm_dopr(fmt, NULL, ap); }
+
+GEN
+gvsprintf(const char *fmt, va_list ap)
+{
+  char *s = sm_dopr(fmt, NULL, ap);
+  GEN z = strtoGENstr(s);
+  pari_free(s); return z;
+}
+
+char *
+pari_sprintf(const char *fmt, ...) /* variadic version of Strprintf */
+{
+  char *s;
+  va_list ap;
+  va_start(ap, fmt);
+  s = pari_vsprintf(fmt, ap);
+  va_end(ap); return s;
+}
+
+char *
+stack_sprintf(const char *fmt, ...)
+{
+  char *s, *t;
+  va_list ap;
+  va_start(ap, fmt);
+  s = pari_vsprintf(fmt, ap);
+  va_end(ap);
+  t = stack_strdup(s);
+  pari_free(s); return t;
+}
+
+GEN
+gsprintf(const char *fmt, ...) /* variadic version of gvsprintf */
+{
+  GEN s;
+  va_list ap;
+  va_start(ap, fmt);
+  s = gvsprintf(fmt, ap);
+  va_end(ap); return s;
+}
+
+/* variadic version of fprintf0. FIXME: fprintf0 not yet available */
+void
+pari_vfprintf(FILE *file, const char *fmt, va_list ap)
+{
+  char *s = sm_dopr(fmt, NULL, ap);
+  fputs(s, file); pari_free(s);
+}
+void
+pari_fprintf(FILE *file, const char *fmt, ...)
+{
+  va_list ap; va_start(ap, fmt);
+  pari_vfprintf(file, fmt, ap); va_end(ap);
+}
+
+void print   (GEN g) { print0(g, f_RAW);       pari_putc('\n'); pari_flush(); }
+void printtex(GEN g) { print0(g, f_TEX);       pari_putc('\n'); pari_flush(); }
+void print1  (GEN g) { print0(g, f_RAW);       pari_flush(); }
+
+void
+error0(GEN g)
+{
+  if (lg(g)==2 && typ(gel(g,1))==t_ERROR) pari_err(0, gel(g,1));
+  else pari_err(e_USER, g);
+}
+
+void warning0(GEN g) { pari_warn(warnuser, g); }
+
+static char *
+wr_check(const char *s) {
+  char *t = path_expand(s);
+  if (GP_DATA->secure)
+  {
+    char *msg = pari_sprintf("[secure mode]: about to write to '%s'",t);
+    pari_ask_confirm(msg);
+    pari_free(msg);
+  }
+  return t;
+}
+
+/* write to file s */
+static void
+wr(const char *s, GEN g, long flag, int addnl)
+{
+  char *t = wr_check(s);
+  FILE *out = switchout_get_FILE(t);
+  pari_free(t);
+  print0_file(out, g, flag);
+  if (addnl) fputc('\n', out);
+  fflush(out);
+  if (fclose(out)) pari_warn(warnfile, "close", t);
+}
+void write0  (const char *s, GEN g) { wr(s, g, f_RAW, 1); }
+void writetex(const char *s, GEN g) { wr(s, g, f_TEX, 1); }
+void write1  (const char *s, GEN g) { wr(s, g, f_RAW, 0); }
+void gpwritebin(const char *s, GEN x) { char *t=wr_check(s); writebin(t, x); pari_free(t);}
+
+/*******************************************************************/
+/**                                                               **/
+/**                       HISTORY HANDLING                        **/
+/**                                                               **/
+/*******************************************************************/
+/* history management function:
+ *   p > 0, called from %p or %#p
+ *   p <= 0, called from %` or %#` (|p| backquotes, possibly 0) */
+static gp_hist_cell *
+history(gp_hist *H, long p, char *old, char *entry)
+{
+  ulong t = H->total, s = H->size;
+  gp_hist_cell *c;
+
+  if (!t)
+    pari_err(old?e_SYNTAX:e_MISC,"The result history is empty", old, entry);
+
+  if (p <= 0) p += t; /* count |p| entries starting from last */
+  if (p <= 0 || p <= (long)(t - s) || (ulong)p > t)
+  {
+    char *str = stack_malloc(128);
+    long pmin = (long)(t - s) + 1;
+    if (pmin <= 0) pmin = 1;
+    sprintf(str, "History result %%%ld not available [%%%ld-%%%lu]", p,pmin,t);
+    pari_err(e_SYNTAX, str, old, entry);
+  }
+  c = H->v + ((p-1) % s);
+  if (!c->z)
+  {
+    char *str = stack_malloc(128);
+    sprintf(str, "History result %%%ld has been deleted (histsize changed)", p);
+    pari_err(e_SYNTAX, str, old, entry);
+  }
+  return c;
+}
+GEN
+gp_history(gp_hist *H, long p, char *old, char *entry)
+{ return history(H,p,old,entry)->z; }
+GEN
+pari_get_hist(long p)
+{ return history(GP_DATA->hist, p, NULL,NULL)->z; }
+long
+pari_get_histtime(long p)
+{ return history(GP_DATA->hist, p, NULL,NULL)->t; }
+
+void
+pari_add_hist(GEN x, long time)
+{
+  gp_hist *H = GP_DATA->hist;
+  ulong i = H->total % H->size;
+  H->total++;
+  if (H->v[i].z) gunclone(H->v[i].z);
+  H->v[i].t = time;
+  H->v[i].z = gclone(x);
+}
+
+ulong
+pari_nb_hist(void)
+{
+  return GP_DATA->hist->total;
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                       TEMPORARY FILES                         **/
+/**                                                               **/
+/*******************************************************************/
+#ifdef __WIN32
+#  include <process.h> /* for getpid */
+#endif
+
+#ifndef R_OK
+#  define R_OK 4
+#  define W_OK 2
+#  define X_OK 1
+#  define F_OK 0
+#endif
+
+#ifdef __EMX__
+#include <io.h>
+static int
+unix_shell(void)
+{
+  char *base, *sh = getenv("EMXSHELL");
+  if (!sh) {
+    sh = getenv("COMSPEC");
+    if (!sh) return 0;
+  }
+  base = _getname(sh);
+  return (stricmp (base, "cmd.exe") && stricmp (base, "4os2.exe")
+       && stricmp (base, "command.com") && stricmp (base, "4dos.com"));
+}
+#endif
+
+/* check if s has rwx permissions for us */
+static int
+pari_is_rwx(const char *s)
+{
+/* FIXME: HAS_ACCESS */
+#if defined(UNIX) || defined (__EMX__)
+  return access(s, R_OK | W_OK | X_OK) == 0;
+#else
+  (void) s; return 1;
+#endif
+}
+
+#if defined(UNIX) || defined (__EMX__)
+#include <sys/types.h>
+#include <sys/stat.h>
+static int
+pari_file_exists(const char *s)
+{
+  int id = open(s, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
+  return id < 0 || close(id);
+}
+static int
+pari_dir_exists(const char *s) { return mkdir(s, 0777); }
+#elif defined(_WIN32)
+static int
+pari_file_exists(const char *s) { return GetFileAttributesA(s) != ~0UL; }
+static int
+pari_dir_exists(const char *s) { return mkdir(s); }
+#else
+static int
+pari_file_exists(const char *s) { return 0; }
+static int
+pari_dir_exists(const char *s) { return 0; }
+#endif
+
+char *
+env_ok(const char *s)
+{
+  char *t = os_getenv(s);
+  if (t && !pari_is_rwx(t))
+  {
+    pari_warn(warner,"%s is set (%s), but is not writable", s,t);
+    t = NULL;
+  }
+  if (t && !pari_is_dir(t))
+  {
+    pari_warn(warner,"%s is set (%s), but is not a directory", s,t);
+    t = NULL;
+  }
+  return t;
+}
+
+static const char*
+pari_tmp_dir(void)
+{
+  char *s;
+#ifdef WINCE
+  s = env_ok("TEMP"); if (s) return s;
+  return "\\temp";
+#endif
+  s = env_ok("GPTMPDIR"); if (s) return s;
+  s = env_ok("TMPDIR"); if (s) return s;
+#if defined(_WIN32) || defined(__EMX__)
+  s = env_ok("TMP"); if (s) return s;
+  s = env_ok("TEMP"); if (s) return s;
+#endif
+#if defined(UNIX) || defined(__EMX__)
+  if (pari_is_rwx("/tmp")) return "/tmp";
+  if (pari_is_rwx("/var/tmp")) return "/var/tmp";
+#endif
+  return ".";
+}
+
+/* loop through 26^2 variants [suffix 'aa' to 'zz'] */
+static int
+get_file(char *buf, int test(const char *))
+{
+  char c, d, *end = buf + strlen(buf) - 1;
+  for (d = 'a'; d <= 'z'; d++)
+  {
+    end[-1] = d;
+    for (c = 'a'; c <= 'z'; c++)
+    {
+      *end = c;
+      if (! test(buf)) return 1;
+      if (DEBUGFILES) err_printf("I/O: file %s exists!\n", buf);
+    }
+  }
+  return 0;
+}
+
+#if defined(__EMX__) || defined(WINCE) || defined(_WIN32)
+static void
+swap_slash(char *s)
+{
+#ifdef __EMX__
+  if (!unix_shell())
+#endif
+  {
+    char *t;
+    for (t=s; *t; t++)
+      if (*t == '/') *t = '\\';
+  }
+}
+#endif
+
+static char *
+init_unique(const char *s)
+{
+  const char *pre = pari_tmp_dir();
+  char *buf, suf[64];
+  size_t lpre, lsuf;
+#ifdef UNIX
+  sprintf(suf,"-%ld-%ld", (long)getuid(), (long)getpid());
+#else
+  suf[0] = 0;
+#endif
+  lsuf = strlen(suf);
+  lpre = strlen(pre);
+  /* room for prefix + '/' + s + suffix '\0' */
+  buf = (char*) pari_malloc(lpre + 1 + 8 + lsuf + 1);
+  strcpy(buf, pre);
+  if (buf[lpre-1] != '/') { (void)strcat(buf, "/"); lpre++; }
+#if defined(__EMX__) || defined(WINCE) || defined(_WIN32)
+  swap_slash(buf);
+#endif
+
+  sprintf(buf + lpre, "%.8s%s", s, suf);
+  if (DEBUGFILES) err_printf("I/O: prefix for unique file/dir = %s\n", buf);
+  return buf;
+}
+
+/* Return a "unique filename" built from the string s, possibly the user id
+ * and the process pid (on Unix systems). A "temporary" directory name is
+ * prepended. The name returned is pari_malloc'ed. It is DOS-safe
+ * (s truncated to 8 chars) */
+char*
+pari_unique_filename(const char *s)
+{
+  char *buf = init_unique(s);
+  if (pari_file_exists(buf) && !get_file(buf, pari_file_exists))
+    pari_err(e_MISC,"couldn't find a suitable name for a tempfile (%s)",s);
+  return buf;
+}
+
+/* Create a "unique directory" and return its name built from the string
+ * s, the user id and process pid (on Unix systems). A "temporary"
+ * directory name is prepended. The name returned is pari_malloc'ed.
+ * It is DOS-safe (truncated to 8 chars) */
+char*
+pari_unique_dir(const char *s)
+{
+  char *buf = init_unique(s);
+  if (pari_dir_exists(buf) && !get_file(buf, pari_dir_exists))
+    pari_err(e_MISC,"couldn't find a suitable name for a tempdir (%s)",s);
+  return buf;
+}
+
+/*******************************************************************/
+/**                                                               **/
+/**                             INSTALL                           **/
+/**                                                               **/
+/*******************************************************************/
+
+#ifdef HAS_DLOPEN
+#include <dlfcn.h>
+
+/* see try_name() */
+static void *
+try_dlopen(const char *s, int flag)
+{ void *h = dlopen(s, flag); pari_free((void*)s); return h; }
+
+/* like dlopen, but using default(sopath) */
+static void *
+gp_dlopen(const char *name, int flag)
+{
+  void *handle;
+  char *s;
+
+  if (!name) return dlopen(NULL, flag);
+  s = path_expand(name);
+
+  /* if sopath empty or path is absolute, use dlopen */
+  if (!GP_DATA || *(GP_DATA->sopath->PATH)==0 || path_is_absolute(s))
+    return try_dlopen(s, flag);
+  else
+  {
+    forpath_t T;
+    char *t;
+    forpath_init(&T, GP_DATA->sopath, s);
+    while ( (t = forpath_next(&T)) )
+    {
+      if ( (handle = try_dlopen(t,flag)) ) return handle;
+      (void)dlerror(); /* clear error message */
+    }
+  }
+  return NULL;
+}
+
+static void *
+install0(const char *name, const char *lib)
+{
+  void *handle;
+
+#ifndef RTLD_GLOBAL /* OSF1 has dlopen but not RTLD_GLOBAL*/
+#  define RTLD_GLOBAL 0
+#endif
+  handle = gp_dlopen(lib, RTLD_LAZY|RTLD_GLOBAL);
+
+  if (!handle)
+  {
+    const char *s = dlerror(); if (s) err_printf("%s\n\n",s);
+    if (lib) pari_err(e_MISC,"couldn't open dynamic library '%s'",lib);
+    pari_err(e_MISC,"couldn't open dynamic symbol table of process");
+  }
+  return dlsym(handle, name);
+}
+#else
+#  ifdef _WIN32
+#  include <windows.h>
+static HMODULE
+try_LoadLibrary(const char *s)
+{ void *h = LoadLibrary(s); pari_free((void*)s); return h; }
+
+/* like LoadLibrary, but using default(sopath) */
+static HMODULE
+gp_LoadLibrary(const char *name)
+{
+  HMODULE handle;
+  char *s = path_expand(name);
+
+  /* if sopath empty or path is absolute, use LoadLibrary */
+  if (!GP_DATA || *(GP_DATA->sopath->PATH)==0 || path_is_absolute(s))
+    return try_LoadLibrary(s);
+  else
+  {
+    forpath_t T;
+    char *t;
+    forpath_init(&T, GP_DATA->sopath, s);
+    while ( (t = forpath_next(&T)) )
+      if ( (handle = try_LoadLibrary(t)) ) return handle;
+  }
+  return NULL;
+}
+static void *
+install0(const char *name, const char *lib)
+{
+  HMODULE handle;
+#ifdef WINCE
+  short wlib[256], wname[256];
+
+  MultiByteToWideChar(CP_ACP, 0, lib, strlen(lib)+1, wlib, 256);
+  MultiByteToWideChar(CP_ACP, 0, name, strlen(name)+1, wname, 256);
+  lib = wlib;
+  name = wname;
+#endif
+
+  handle = gp_LoadLibrary(lib);
+  if (!handle)
+  {
+    if (lib) pari_err(e_MISC,"couldn't open dynamic library '%s'",lib);
+    pari_err(e_MISC,"couldn't open dynamic symbol table of process");
+  }
+  return (void *) GetProcAddress(handle,name);
+}
+#  else
+static void *
+install0(const char *name, const char *lib)
+{ pari_err(e_ARCH,"install"); return NULL; }
+#endif
+#endif
+
+static char *
+dft_help(const char *gp, const char *s, const char *code)
+{ return stack_sprintf("%s: installed function\nlibrary name: %s\nprototype: %s" , gp, s, code); }
+
+void
+gpinstall(const char *s, const char *code, const char *gpname, const char *lib)
+{
+  pari_sp av = avma;
+  const char *gp = *gpname? gpname: s;
+  void *f;
+  entree *ep;
+  if (GP_DATA->secure)
+  {
+    char *msg = pari_sprintf("[secure mode]: about to install '%s'", s);
+    pari_ask_confirm(msg);
+    pari_free(msg);
+  }
+  ep = is_entry(gp);
+  if (ep && ep->valence == EpINSTALL
+      && strcmp(ep->code, code)
+      && !strcmp(ep->help, dft_help(gp,s,ep->code)))
+  { /* help is the default AND prototype changes: delete help */
+    pari_free((void*)ep->help); ep->help = NULL;
+  }
+  f = install0(s, *lib ?lib :pari_library_path);
+  if (!f)
+  {
+    if (*lib) pari_err(e_MISC,"can't find symbol '%s' in library '%s'",s,lib);
+    pari_err(e_MISC,"can't find symbol '%s' in dynamic symbol table of process",s);
+  }
+  ep = install(f,gp,code);
+  if (ep && !ep->help) addhelp(gp, dft_help(gp,s,code));
+  mt_broadcast(strtoclosure("install",4,strtoGENstr(s),strtoGENstr(code),
+                                       strtoGENstr(gp),strtoGENstr(lib)));
+  avma = av;
+}
diff --git a/src/language/eval.c b/src/language/eval.c
new file mode 100644
index 0000000..426c4d5
--- /dev/null
+++ b/src/language/eval.c
@@ -0,0 +1,2236 @@
+/* Copyright (C) 2006  The PARI group.
+
+This file is part of the PARI package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+#include "opcode.h"
+
+/********************************************************************/
+/*                                                                  */
+/*                   break/next/return handling                     */
+/*                                                                  */
+/********************************************************************/
+
+static THREAD long br_status, br_count;
+static THREAD GEN br_res;
+
+long
+loop_break(void)
+{
+  switch(br_status)
+  {
+    case br_MULTINEXT :
+      if (! --br_count) br_status = br_NEXT;
+      return 1;
+    case br_BREAK : if (! --br_count) br_status = br_NONE; /* fall through */
+    case br_RETURN: return 1;
+    case br_NEXT: br_status = br_NONE; /* fall through */
+  }
+  return 0;
+}
+
+static void
+reset_break(void)
+{
+  br_status = br_NONE;
+  if (br_res) { gunclone_deep(br_res); br_res = NULL; }
+}
+
+GEN
+return0(GEN x)
+{
+  GEN y = br_res;
+  br_res = (x && x != gnil)? gcloneref(x): NULL;
+  if (y) gunclone_deep(y);
+  br_status = br_RETURN; return NULL;
+}
+
+GEN
+next0(long n)
+{
+  if (n < 1) pari_err_DOMAIN("next", "n", "<", gen_1, stoi(n));
+  if (n == 1) br_status = br_NEXT;
+  else
+  {
+    br_count = n-1;
+    br_status = br_MULTINEXT;
+  }
+  return NULL;
+}
+
+GEN
+break0(long n)
+{
+  if (n < 1) pari_err_DOMAIN("break", "n", "<", gen_1, stoi(n));
+  br_count = n;
+  br_status = br_BREAK; return NULL;
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                            VARIABLES                            */
+/*                                                                 */
+/*******************************************************************/
+
+/* As a rule, ep->value is a clone (COPY). push_val and pop_val are private
+ * functions for use in sumiter: we want a temporary ep->value, which is NOT
+ * a clone (PUSH), to avoid unnecessary copies. */
+
+enum {PUSH_VAL = 0, COPY_VAL = 1, DEFAULT_VAL = 2};
+
+/* ep->args is the stack of old values (INITIAL if initial value, from
+ * installep) */
+typedef struct var_cell {
+  struct var_cell *prev; /* cell associated to previous value on stack */
+  GEN value; /* last value (not including current one, in ep->value) */
+  char flag; /* status of _current_ ep->value: PUSH or COPY ? */
+  long valence; /* valence of entree* associated to 'value', to be restored
+                    * by pop_val */
+} var_cell;
+#define INITIAL NULL
+
+/* Push x on value stack associated to ep. */
+static void
+new_val_cell(entree *ep, GEN x, char flag)
+{
+  var_cell *v = (var_cell*) pari_malloc(sizeof(var_cell));
+  v->value  = (GEN)ep->value;
+  v->prev   = (var_cell*) ep->pvalue;
+  v->flag   = flag;
+  v->valence= ep->valence;
+
+  /* beware: f(p) = Nv = 0
+   *         Nv = p; f(Nv) --> this call would destroy p [ isclone ] */
+  ep->value = (flag == COPY_VAL)? gclone(x):
+                                  (x && isclone(x))? gcopy(x): x;
+  /* Do this last. In case the clone is <C-C>'ed before completion ! */
+  ep->pvalue= (char*)v;
+  ep->valence=EpVAR;
+}
+
+/* kill ep->value and replace by preceding one, poped from value stack */
+static void
+pop_val(entree *ep)
+{
+  var_cell *v = (var_cell*) ep->pvalue;
+
+  if (v == INITIAL) return;
+  if (v->flag == COPY_VAL) gunclone_deep((GEN)ep->value);
+  ep->value  = v->value;
+  ep->pvalue = (char*) v->prev;
+  ep->valence=v->valence;
+  pari_free((void*)v);
+}
+
+void
+freeep(entree *ep)
+{
+  if (EpSTATIC(ep)) return; /* gp function loaded at init time */
+  if (ep->help) {pari_free((void*)ep->help); ep->help=NULL;}
+  if (ep->code) {pari_free((void*)ep->code); ep->code=NULL;}
+  switch(EpVALENCE(ep))
+  {
+    case EpVAR:
+      while (ep->pvalue!=INITIAL) pop_val(ep);
+      break;
+    case EpALIAS:
+      killblock((GEN)ep->value); ep->value=NULL; break;
+  }
+}
+
+INLINE void
+pushvalue(entree *ep, GEN x) {
+  new_val_cell(ep, x, COPY_VAL);
+}
+
+INLINE void
+zerovalue (entree *ep)
+{
+  var_cell *v = (var_cell*) pari_malloc(sizeof(var_cell));
+  v->value  = (GEN)ep->value;
+  v->prev   = (var_cell*) ep->pvalue;
+  v->flag   = PUSH_VAL;
+  v->valence= ep->valence;
+  ep->value = gen_0;
+  ep->pvalue= (char*)v;
+  ep->valence=EpVAR;
+}
+
+
+/* as above IF ep->value was PUSHed, or was created after block number 'loc'
+   return 0 if not deleted, 1 otherwise [for recover()] */
+int
+pop_val_if_newer(entree *ep, long loc)
+{
+  var_cell *v = (var_cell*) ep->pvalue;
+
+  if (v == INITIAL) return 0;
+  if (v->flag == COPY_VAL && !pop_entree_block(ep, loc)) return 0;
+  ep->value = v->value;
+  ep->pvalue= (char*) v->prev;
+  ep->valence=v->valence;
+  pari_free((void*)v); return 1;
+}
+
+/* set new value of ep directly to val (COPY), do not save last value unless
+ * it's INITIAL. */
+void
+changevalue(entree *ep, GEN x)
+{
+  var_cell *v = (var_cell*) ep->pvalue;
+  if (v == INITIAL) new_val_cell(ep, x, COPY_VAL);
+  else
+  {
+    GEN old_val = (GEN) ep->value; /* beware: gunclone_deep may destroy old x */
+    ep->value = (void *) gclone(x);
+    if (v->flag == COPY_VAL) gunclone_deep(old_val); else v->flag = COPY_VAL;
+  }
+}
+
+INLINE GEN
+copyvalue(entree *ep)
+{
+  var_cell *v = (var_cell*) ep->pvalue;
+  if (v && v->flag != COPY_VAL)
+  {
+    ep->value = (void*) gclone((GEN)ep->value);
+    v->flag = COPY_VAL;
+  }
+  return (GEN) ep->value;
+}
+
+INLINE void
+err_var(GEN x) { pari_err_TYPE("evaluator [variable name expected]", x); }
+
+INLINE void
+checkvalue(entree *ep)
+{
+  if (MT_IS_THREAD)
+    pari_err(e_MISC,"mt: global variable not supported: %s",ep->name);
+  if (ep->valence==EpNEW)
+  {
+    pari_var_create(ep);
+    ep->valence = EpVAR;
+    ep->value = initial_value(ep);
+  }
+  else if (ep->valence!=EpVAR)
+    err_var(strtoGENstr(ep->name));
+}
+
+/* make GP variables safe for avma = top */
+static void
+lvar_make_safe(void)
+{
+  long n;
+  entree *ep;
+  for (n = 0; n < functions_tblsz; n++)
+    for (ep = functions_hash[n]; ep; ep = ep->next)
+      if (EpVALENCE(ep) == EpVAR)
+      { /* make sure ep->value is a COPY */
+        var_cell *v = (var_cell*)ep->pvalue;
+        if (v && v->flag == PUSH_VAL) {
+          GEN x = (GEN)ep->value;
+          if (x) changevalue(ep, (GEN)ep->value); else pop_val(ep);
+        }
+      }
+}
+
+static void
+check_array_index(long c, long l)
+{
+  if (c < 1) pari_err_COMPONENT("", "<", gen_1, stoi(c));
+  if (c >= l) pari_err_COMPONENT("", ">", stoi(l-1), stoi(c));
+}
+
+GEN*
+safegel(GEN x, long l)
+{
+  if (!is_matvec_t(typ(x)))
+    pari_err_TYPE("safegel",x);
+  check_array_index(l, lg(x));
+  return &(gel(x,l));
+}
+
+GEN*
+safelistel(GEN x, long l)
+{
+  GEN d;
+  if (typ(x)!=t_LIST)
+    pari_err_TYPE("safelistel",x);
+  d = list_data(x);
+  check_array_index(l, lg(d));
+  return &(gel(d,l));
+}
+
+long*
+safeel(GEN x, long l)
+{
+  if (typ(x)!=t_VECSMALL)
+    pari_err_TYPE("safeel",x);
+  check_array_index(l, lg(x));
+  return &(x[l]);
+}
+
+GEN*
+safegcoeff(GEN x, long a, long b)
+{
+  if (typ(x)!=t_MAT) pari_err_TYPE("safegcoeff", x);
+  check_array_index(b, lg(x));
+  check_array_index(a, lg(gel(x,b)));
+  return &(gcoeff(x,a,b));
+}
+
+typedef struct matcomp
+{
+  GEN *ptcell;
+  GEN parent;
+  int full_col, full_row;
+} matcomp;
+
+typedef struct gp_pointer
+{
+  matcomp c;
+  GEN x, ox;
+  entree *ep;
+  long vn;
+  long sp;
+} gp_pointer;
+
+
+/* assign res at *pt in "simple array object" p and return it, or a copy.*/
+static void
+change_compo(matcomp *c, GEN res)
+{
+  GEN p = c->parent, *pt = c->ptcell;
+  long i, t;
+
+  if (typ(p) == t_VECSMALL)
+  {
+    if (typ(res) != t_INT || is_bigint(res))
+      pari_err_TYPE("t_VECSMALL assignment", res);
+    *pt = (GEN)itos(res); return;
+  }
+  t = typ(res);
+  if (c->full_row)
+  {
+    if (t != t_VEC) pari_err_TYPE("matrix row assignment", res);
+    if (lg(res) != lg(p)) pari_err_DIM("matrix row assignment");
+    for (i=1; i<lg(p); i++)
+    {
+      GEN p1 = gcoeff(p,c->full_row,i); if (isclone(p1)) gunclone_deep(p1);
+      gcoeff(p,c->full_row,i) = gclone(gel(res,i));
+    }
+    return;
+  }
+  if (c->full_col)
+  {
+    if (t != t_COL) pari_err_TYPE("matrix col assignment", res);
+    if (lg(res) != lg(*pt)) pari_err_DIM("matrix col assignment");
+  }
+
+  res = gclone(res);
+  gunclone_deep(*pt);
+  *pt = res;
+}
+
+/***************************************************************************
+ **                                                                       **
+ **                           Byte-code evaluator                         **
+ **                                                                       **
+ ***************************************************************************/
+
+struct var_lex
+{
+  long flag;
+  GEN value;
+};
+
+struct trace
+{
+  long pc;
+  GEN closure;
+};
+
+static THREAD long sp, rp, dbg_level;
+static THREAD long *st;
+static THREAD gp_pointer *ptrs;
+static THREAD entree **lvars;
+static THREAD struct var_lex *var;
+static THREAD struct trace *trace;
+static THREAD pari_stack s_st, s_ptrs, s_var, s_lvars, s_trace;
+
+static void
+changelex(long vn, GEN x)
+{
+  struct var_lex *v=var+s_var.n+vn;
+  GEN old_val = v->value;
+  v->value = gclone(x);
+  if (v->flag == COPY_VAL) gunclone_deep(old_val); else v->flag = COPY_VAL;
+}
+
+INLINE GEN
+copylex(long vn)
+{
+  struct var_lex *v = var+s_var.n+vn;
+  if (v->flag!=COPY_VAL)
+  {
+    v->value = gclone(v->value);
+    v->flag  = COPY_VAL;
+  }
+  return v->value;
+}
+
+INLINE void
+pushlex(long vn, GEN x)
+{
+  struct var_lex *v=var+s_var.n+vn;
+  v->flag  = PUSH_VAL;
+  v->value = x;
+}
+
+INLINE void
+freelex(long vn)
+{
+  struct var_lex *v=var+s_var.n+vn;
+  if (v->flag == COPY_VAL) gunclone_deep(v->value);
+}
+
+INLINE void
+restore_vars(long nbmvar, long nblvar)
+{
+  long j;
+  for(j=1;j<=nbmvar;j++)
+    freelex(-j);
+  s_var.n-=nbmvar;
+  for(j=1;j<=nblvar;j++)
+    pop_val(lvars[s_lvars.n-j]);
+  s_lvars.n-=nblvar;
+}
+
+INLINE void
+restore_trace(long nbtrace)
+{
+  long j;
+  for(j=1;j<=nbtrace;j++)
+  {
+    GEN C = trace[s_trace.n-j].closure;
+    if (isclone(C)) gunclone(C);
+  }
+  s_trace.n-=nbtrace;
+}
+
+INLINE long
+trace_push(long pc, GEN C)
+{
+  long tr;
+  BLOCK_SIGINT_START
+  tr = pari_stack_new(&s_trace);
+  trace[tr].pc = pc;
+  trace[tr].closure = C;
+  BLOCK_SIGINT_END
+  return tr;
+}
+
+void
+push_lex(GEN a, GEN C)
+{
+  long vn=pari_stack_new(&s_var);
+  struct var_lex *v=var+vn;
+  v->flag  = PUSH_VAL;
+  v->value = a;
+  if (C) (void) trace_push(-1, C);
+}
+
+GEN
+get_lex(long vn)
+{
+  struct var_lex *v=var+s_var.n+vn;
+  return v->value;
+}
+
+void
+set_lex(long vn, GEN x)
+{
+  struct var_lex *v=var+s_var.n+vn;
+  if (v->flag == COPY_VAL) { gunclone_deep(v->value); v->flag = PUSH_VAL; }
+  v->value = x;
+}
+
+void
+pop_lex(long n)
+{
+  long j;
+  for(j=1; j<=n; j++)
+    freelex(-j);
+  s_var.n-=n;
+  s_trace.n--;
+}
+
+static THREAD pari_stack s_relocs;
+static THREAD entree **relocs;
+
+void
+pari_init_evaluator(void)
+{
+  sp=0;
+  pari_stack_init(&s_st,sizeof(*st),(void**)&st);
+  pari_stack_alloc(&s_st,32);
+  s_st.n=s_st.alloc;
+  rp=0;
+  pari_stack_init(&s_ptrs,sizeof(*ptrs),(void**)&ptrs);
+  pari_stack_alloc(&s_ptrs,16);
+  s_ptrs.n=s_ptrs.alloc;
+  pari_stack_init(&s_var,sizeof(*var),(void**)&var);
+  pari_stack_init(&s_lvars,sizeof(*lvars),(void**)&lvars);
+  pari_stack_init(&s_trace,sizeof(*trace),(void**)&trace);
+  br_res = NULL;
+  pari_stack_init(&s_relocs,sizeof(*relocs),(void**)&relocs);
+}
+void
+pari_close_evaluator(void)
+{
+  pari_stack_delete(&s_st);
+  pari_stack_delete(&s_ptrs);
+  pari_stack_delete(&s_var);
+  pari_stack_delete(&s_lvars);
+  pari_stack_delete(&s_trace);
+}
+
+static gp_pointer *
+new_ptr(void)
+{
+  if (rp==s_ptrs.n-1)
+  {
+    long i;
+    gp_pointer *old = ptrs;
+    (void)pari_stack_new(&s_ptrs);
+    if (old != ptrs)
+      for(i=0; i<rp; i++)
+      {
+        gp_pointer *g = &ptrs[i];
+        if(g->sp >= 0) gel(st,g->sp) = (GEN) &(g->x);
+      }
+  }
+  return &ptrs[rp++];
+}
+
+INLINE GEN
+copyupto(GEN z, GEN t)
+{
+  if (is_universal_constant(z) || (z>(GEN)bot && z<=t))
+    return z;
+  else
+    return gcopy(z);
+}
+
+static void closure_eval(GEN C);
+
+INLINE GEN
+closure_return(GEN C)
+{
+  pari_sp ltop=avma;
+  closure_eval(C);
+  if (br_status)
+  {
+    GEN z;
+    avma=ltop;
+    z=br_res?gcopy(br_res):gnil;
+    reset_break();
+    return z;
+  }
+  return gerepileupto(ltop,gel(st,--sp));
+}
+
+/* for the break_loop debugger. Not memory clean */
+GEN
+closure_evalbrk(GEN C, long *status)
+{
+  closure_eval(C);
+  *status = br_status;
+  if (br_status)
+  {
+    GEN z = br_res? gcopy(br_res): gnil;
+    reset_break();
+    return z;
+  }
+  return gel(st,--sp);
+}
+
+INLINE long
+closure_varn(GEN x)
+{
+  if (!x) return -1;
+  if (!gequalX(x)) err_var(x);
+  return varn(x);
+}
+
+INLINE void
+closure_castgen(GEN z, long mode)
+{
+  switch (mode)
+  {
+  case Ggen:
+    gel(st,sp++)=z;
+    break;
+  case Gsmall:
+    st[sp++]=gtos(z);
+    break;
+  case Gvar:
+    st[sp++]=closure_varn(z);
+    break;
+  case Gvoid:
+    break;
+  default:
+    pari_err_BUG("closure_castgen, type unknown");
+  }
+}
+
+INLINE void
+closure_castlong(long z, long mode)
+{
+  switch (mode)
+  {
+  case Gsmall:
+    st[sp++]=z;
+    break;
+  case Ggen:
+    gel(st,sp++)=stoi(z);
+    break;
+  case Gvar:
+    err_var(stoi(z));
+  default:
+    pari_err_BUG("closure_castlong, type unknown");
+  }
+}
+
+const char *
+closure_func_err(void)
+{
+  long fun=s_trace.n-1, pc;
+  const char *code;
+  GEN C, oper;
+  if (fun < 0 || trace[fun].pc < 0) return NULL;
+  pc = trace[fun].pc; C  = trace[fun].closure;
+  code = closure_codestr(C); oper = closure_get_oper(C);
+  if (code[pc]==OCcallgen || code[pc]==OCcallgen2 ||
+      code[pc]==OCcallint || code[pc]==OCcalllong || code[pc]==OCcallvoid)
+    return ((entree*)oper[pc])->name;
+  return NULL;
+}
+
+/* return the next label for the call chain debugger closure_err(),
+ * incorporating the name of the user of member function. Return NULL for an
+ * anonymous (inline) closure. */
+static char *
+get_next_label(const char *s, int member, char **next_fun)
+{
+  const char *v, *t = s+1;
+  char *u, *next_label;
+
+  if (!is_keyword_char(*s)) return NULL;
+  while (is_keyword_char(*t)) t++;
+  /* e.g. (x->1/x)(0) instead of (x)->1/x */
+  if (t[0] == '-' && t[1] == '>') return NULL;
+  next_label = (char*)pari_malloc(t - s + 32);
+  sprintf(next_label, "in %sfunction ", member? "member ": "");
+  u = *next_fun = next_label + strlen(next_label);
+  v = s;
+  while (v < t) *u++ = *v++;
+  *u++ = 0; return next_label;
+}
+
+static const char *
+get_arg_name(GEN C, long i)
+{
+  GEN e = gmael(closure_get_dbg(C), 3, 1);
+  return ((entree*)e[i])->name;
+}
+
+void
+closure_err(long level)
+{
+  GEN base;
+  const long lastfun = s_trace.n - 1 - level;
+  char *next_label, *next_fun;
+  long i = maxss(0, lastfun - 19);
+  if (lastfun < 0) return; /*e.g. when called by gp_main_loop's simplify */
+  if (i > 0) while (lg(trace[i].closure)==6) i--;
+  base = closure_get_text(trace[i].closure); /* gcc -Wall*/
+  next_label = pari_strdup(i == 0? "at top-level": "[...] at");
+  next_fun = next_label;
+  for (; i <= lastfun; i++)
+  {
+    GEN C = trace[i].closure;
+    if (lg(C) >= 7) base=closure_get_text(C);
+    if ((i==lastfun || lg(trace[i+1].closure)>=7))
+    {
+      GEN dbg = gel(closure_get_dbg(C),1);
+      /* After a SIGINT, pc can be slightly off: ensure 0 <= pc < lg() */
+      long pc = minss(lg(dbg)-1, trace[i].pc>=0 ? trace[i].pc: 1);
+      long offset = pc? dbg[pc]: 0;
+      int member;
+      const char *s, *sbase;
+      if (typ(base)!=t_VEC) sbase = GSTR(base);
+      else if (offset>=0)   sbase = GSTR(gel(base,2));
+      else { sbase = GSTR(gel(base,1)); offset += strlen(sbase); }
+      s = sbase + offset;
+      member = offset>0 && (s[-1] == '.');
+      /* avoid "in function foo: foo" */
+      if (!next_fun || strcmp(next_fun, s)) {
+        print_errcontext(pariErr, next_label, s, sbase);
+        out_putc(pariErr, '\n');
+      }
+      pari_free(next_label);
+      if (i == lastfun) break;
+
+      next_label = get_next_label(s, member, &next_fun);
+      if (!next_label) {
+        next_label = pari_strdup("in anonymous function");
+        next_fun = NULL;
+      }
+    }
+  }
+}
+
+long
+closure_context(long start, long level)
+{
+  const long lastfun = s_trace.n - 1 - level;
+  long i, fun = lastfun;
+  if (fun<0) return lastfun;
+  while (fun>start && lg(trace[fun].closure)==6) fun--;
+  for (i=fun; i <= lastfun; i++)
+    push_frame(trace[i].closure, trace[i].pc,0);
+  for (  ; i < s_trace.n; i++)
+    push_frame(trace[i].closure, trace[i].pc,1);
+  return s_trace.n-level;
+}
+
+INLINE void
+st_alloc(long n)
+{
+  if (sp+n>s_st.n)
+  {
+    pari_stack_alloc(&s_st,n+16);
+    s_st.n=s_st.alloc;
+    if (DEBUGMEM>=2) pari_warn(warner,"doubling evaluator stack");
+  }
+}
+
+INLINE void
+ptr_proplock(gp_pointer *g, GEN C)
+{
+  g->x = C;
+  if (isclone(g->x))
+  {
+    clone_unlock(g->ox);
+    g->ox = g->x;
+    ++bl_refc(g->ox);
+  }
+}
+
+static void
+closure_eval(GEN C)
+{
+  const char *code=closure_codestr(C);
+  GEN oper=closure_get_oper(C);
+  GEN data=closure_get_data(C);
+  long loper=lg(oper);
+  long saved_sp=sp-closure_arity(C);
+  long saved_rp=rp;
+  long j, nbmvar=0, nblvar=0;
+  long pc, t;
+  clone_lock(C);
+  t = trace_push(0, C);
+  if (lg(C)==8)
+  {
+    GEN z=closure_get_frame(C);
+    long l=lg(z)-1;
+    pari_stack_alloc(&s_var,l);
+    s_var.n+=l;
+    nbmvar+=l;
+    for(j=1;j<=l;j++)
+    {
+      var[s_var.n-j].flag=PUSH_VAL;
+      var[s_var.n-j].value=gel(z,j);
+    }
+  }
+
+  for(pc=1;pc<loper;pc++)
+  {
+    op_code opcode=(op_code) code[pc];
+    long operand=oper[pc];
+    if (sp<0) pari_err_BUG("closure_eval, stack underflow");
+    st_alloc(16);
+    trace[t].pc = pc;
+    CHECK_CTRLC
+    switch(opcode)
+    {
+    case OCpushlong:
+      st[sp++]=operand;
+      break;
+    case OCpushgnil:
+      gel(st,sp++)=gnil;
+      break;
+    case OCpushgen:
+      gel(st,sp++)=gel(data,operand);
+      break;
+    case OCpushreal:
+      gel(st,sp++)=strtor(GSTR(data[operand]),precreal);
+      break;
+    case OCpushstoi:
+      gel(st,sp++)=stoi(operand);
+      break;
+    case OCpushvar:
+      {
+        entree *ep = (entree *)operand;
+        pari_var_create(ep);
+        gel(st,sp++)=(GEN)initial_value(ep);
+        break;
+      }
+    case OCpushdyn:
+      {
+        entree *ep = (entree *)operand;
+        checkvalue(ep);
+        gel(st,sp++)=(GEN)ep->value;
+        break;
+      }
+    case OCpushlex:
+      gel(st,sp++)=var[s_var.n+operand].value;
+      break;
+    case OCsimpleptrdyn:
+      {
+        gp_pointer *g = new_ptr();
+        g->vn=0;
+        g->ep = (entree*) operand;
+        checkvalue(g->ep);
+        g->x = (GEN) g->ep->value;
+        g->ox = g->x; clone_lock(g->ox);
+        g->sp = sp;
+        gel(st,sp++) = (GEN)&(g->x);
+        break;
+      }
+    case OCsimpleptrlex:
+      {
+        gp_pointer *g = new_ptr();
+        g->vn=operand;
+        g->ep=(entree *)0x1L;
+        g->x = (GEN) var[s_var.n+operand].value;
+        g->ox = g->x; clone_lock(g->ox);
+        g->sp = sp;
+        gel(st,sp++) = (GEN)&(g->x);
+        break;
+      }
+    case OCnewptrdyn:
+      {
+        entree *ep = (entree *)operand;
+        gp_pointer *g = new_ptr();
+        matcomp *C;
+        checkvalue(ep);
+        g->sp = -1;
+        g->x = copyvalue(ep);
+        g->ox = g->x; clone_lock(g->ox);
+        g->vn=0;
+        g->ep=NULL;
+        C=&g->c;
+        C->full_col = C->full_row = 0;
+        C->parent   = (GEN)    g->x;
+        C->ptcell   = (GEN *) &g->x;
+        break;
+      }
+    case OCnewptrlex:
+      {
+        gp_pointer *g = new_ptr();
+        matcomp *C;
+        g->sp = -1;
+        g->x = copylex(operand);
+        g->ox = g->x; clone_lock(g->ox);
+        g->vn=0;
+        g->ep=NULL;
+        C=&g->c;
+        C->full_col = C->full_row = 0;
+        C->parent   = (GEN)     g->x;
+        C->ptcell   = (GEN *) &(g->x);
+        break;
+      }
+    case OCpushptr:
+      {
+        gp_pointer *g = &ptrs[rp-1];
+        g->sp = sp;
+        gel(st,sp++) = (GEN)&(g->x);
+      }
+      break;
+    case OCendptr:
+      for(j=0;j<operand;j++)
+      {
+        gp_pointer *g = &ptrs[--rp];
+        if (g->ep)
+        {
+          if (g->vn)
+            changelex(g->vn, g->x);
+          else
+            changevalue(g->ep, g->x);
+        }
+        else change_compo(&(g->c), g->x);
+        clone_unlock(g->ox);
+      }
+      break;
+    case OCstoredyn:
+      {
+        entree *ep = (entree *)operand;
+        checkvalue(ep);
+        changevalue(ep, gel(st,--sp));
+        break;
+      }
+    case OCstorelex:
+      changelex(operand,gel(st,--sp));
+      break;
+    case OCstoreptr:
+      {
+        gp_pointer *g = &ptrs[--rp];
+        change_compo(&(g->c), gel(st,--sp));
+        clone_unlock(g->ox);
+        break;
+      }
+    case OCstackgen:
+      {
+        GEN z = gerepileupto(st[sp-2],gel(st,sp-1));
+        gmael(st,sp-3,operand) = copyupto(z,gel(st,sp-2));
+        st[sp-2] = avma;
+        sp--;
+        break;
+      }
+    case OCprecreal:
+      st[sp++]=precreal;
+      break;
+    case OCprecdl:
+      st[sp++]=precdl;
+      break;
+    case OCavma:
+      st[sp++]=avma;
+      break;
+    case OCcowvardyn:
+      {
+        entree *ep = (entree *)operand;
+        checkvalue(ep);
+        (void)copyvalue(ep);
+        break;
+      }
+    case OCcowvarlex:
+      (void)copylex(operand);
+      break;
+    case OCstoi:
+      gel(st,sp-1)=stoi(st[sp-1]);
+      break;
+    case OCitos:
+      st[sp+operand]=gtos(gel(st,sp+operand));
+      break;
+    case OCtostr:
+      {
+        GEN z = gel(st,sp+operand);
+        st[sp+operand] = (long)GENtostr_unquoted(z);
+        break;
+      }
+    case OCvarn:
+      st[sp+operand] = closure_varn(gel(st,sp+operand));
+      break;
+    case OCcopy:
+      gel(st,sp-1) = gcopy(gel(st,sp-1));
+      break;
+    case OCgerepile:
+    {
+      pari_sp av;
+      GEN x;
+      sp--;
+      av = st[sp-1];
+      x = gel(st,sp);
+      if (isonstack(x))
+      {
+        pari_sp av2 = (pari_sp)(x + lg(x));
+        if ((long) (av - av2) > 1000000L)
+        {
+          if (DEBUGMEM>=2)
+            pari_warn(warnmem,"eval: recovering %ld bytes", av - av2);
+          x = gerepileupto(av, x);
+        }
+      } else avma = av;
+      gel(st,sp-1) = x;
+      break;
+    }
+    case OCcopyifclone:
+      if (isclone(gel(st,sp-1)))
+        gel(st,sp-1) = gcopy(gel(st,sp-1));
+      break;
+    case OCcompo1:
+      {
+        GEN  p=gel(st,sp-2);
+        long c=st[sp-1];
+        sp-=2;
+        switch(typ(p))
+        {
+        case t_VEC: case t_COL:
+          check_array_index(c, lg(p));
+          closure_castgen(gel(p,c),operand);
+          break;
+        case t_LIST:
+          {
+            long lx;
+            p = list_data(p); lx = p? lg(p): 1;
+            check_array_index(c, lx);
+            closure_castgen(gel(p,c),operand);
+            break;
+          }
+        case t_VECSMALL:
+          check_array_index(c,lg(p));
+          closure_castlong(p[c],operand);
+          break;
+        default:
+          pari_err_TYPE("_[_] OCcompo1 [not a vector]", p);
+          break;
+        }
+        break;
+      }
+    case OCcompo1ptr:
+      {
+        long c=st[sp-1];
+        long lx;
+        gp_pointer *g = &ptrs[rp-1];
+        matcomp *C=&g->c;
+        GEN p = g->x;
+        sp--;
+        switch(typ(p))
+        {
+        case t_VEC: case t_COL:
+          check_array_index(c, lg(p));
+          C->ptcell = (GEN *) p+c;
+          ptr_proplock(g, *(C->ptcell));
+          break;
+        case t_VECSMALL:
+          check_array_index(c, lg(p));
+          C->ptcell = (GEN *) p+c;
+          g->x = stoi(p[c]);
+          break;
+        case t_LIST:
+          p = list_data(p); lx = p? lg(p): 1;
+          check_array_index(c,lx);
+          C->ptcell = (GEN *) p+c;
+          ptr_proplock(g, *(C->ptcell));
+          break;
+        default:
+          pari_err_TYPE("&_[_] OCcompo1ptr [not a vector]", p);
+        }
+        C->parent   = p;
+        break;
+      }
+    case OCcompo2:
+      {
+        GEN  p=gel(st,sp-3);
+        long c=st[sp-2];
+        long d=st[sp-1];
+        if (typ(p)!=t_MAT) pari_err_TYPE("_[_,_] OCcompo2 [not a matrix]", p);
+        check_array_index(d, lg(p));
+        check_array_index(c, lg(gel(p,d)));
+        sp-=3;
+        closure_castgen(gcoeff(p,c,d),operand);
+        break;
+      }
+    case OCcompo2ptr:
+      {
+        long c=st[sp-2];
+        long d=st[sp-1];
+        gp_pointer *g = &ptrs[rp-1];
+        matcomp *C=&g->c;
+        GEN p = g->x;
+        sp-=2;
+        if (typ(p)!=t_MAT)
+          pari_err_TYPE("&_[_,_] OCcompo2ptr [not a matrix]", p);
+        check_array_index(d, lg(p));
+        check_array_index(c, lg(gel(p,d)));
+        C->ptcell = (GEN *) gel(p,d)+c;
+        C->parent   = p;
+        ptr_proplock(g, *(C->ptcell));
+        break;
+      }
+    case OCcompoC:
+      {
+        GEN  p=gel(st,sp-2);
+        long c=st[sp-1];
+        if (typ(p)!=t_MAT)
+          pari_err_TYPE("_[,_] OCcompoC [not a matrix]", p);
+        check_array_index(c, lg(p));
+        sp--;
+        gel(st,sp-1) = gel(p,c);
+        break;
+      }
+    case OCcompoCptr:
+      {
+        long c=st[sp-1];
+        gp_pointer *g = &ptrs[rp-1];
+        matcomp *C=&g->c;
+        GEN p = g->x;
+        sp--;
+        if (typ(p)!=t_MAT)
+          pari_err_TYPE("&_[,_] OCcompoCptr [not a matrix]", p);
+        check_array_index(c, lg(p));
+        C->ptcell = (GEN *) p+c;
+        C->full_col = c;
+        C->parent   = p;
+        ptr_proplock(g, *(C->ptcell));
+        break;
+      }
+    case OCcompoL:
+      {
+        GEN  p=gel(st,sp-2);
+        long r=st[sp-1];
+        sp--;
+        if (typ(p)!=t_MAT)
+          pari_err_TYPE("_[_,] OCcompoL [not a matrix]", p);
+        check_array_index(r,lg(p) == 1? 1: lgcols(p));
+        gel(st,sp-1) = row(p,r);
+        break;
+      }
+    case OCcompoLptr:
+      {
+        long r=st[sp-1];
+        gp_pointer *g = &ptrs[rp-1];
+        matcomp *C=&g->c;
+        GEN p = g->x, p2;
+        sp--;
+        if (typ(p)!=t_MAT)
+          pari_err_TYPE("&_[_,] OCcompoLptr [not a matrix]", p);
+        check_array_index(r,lg(p) == 1? 1: lgcols(p));
+        p2 = rowcopy(p,r);
+        C->full_row = r; /* record row number */
+        C->ptcell = &p2;
+        C->parent   = p;
+        g->x = p2;
+        break;
+      }
+    case OCdefaultarg:
+      if (var[s_var.n+operand].flag==DEFAULT_VAL)
+      {
+        GEN z = gel(st,sp-1);
+        pushlex(operand,typ(z)== t_CLOSURE? closure_evalnobrk(z): z);
+      }
+      sp--;
+      break;
+    case OClocalvar:
+      {
+        long n = pari_stack_new(&s_lvars);
+        entree *ep = (entree *)operand;
+        checkvalue(ep);
+        lvars[n] = ep;
+        nblvar++;
+        pushvalue(ep,gel(st,--sp));
+        break;
+      }
+    case OClocalvar0:
+      {
+        long n = pari_stack_new(&s_lvars);
+        entree *ep = (entree *)operand;
+        checkvalue(ep);
+        lvars[n] = ep;
+        nblvar++;
+        zerovalue(ep);
+        break;
+      }
+
+#define EVAL_f(f) \
+  switch (ep->arity) \
+  { \
+    case 0: f(); break; \
+    case 1: sp--; f(st[sp]); break; \
+    case 2: sp-=2; f(st[sp],st[sp+1]); break; \
+    case 3: sp-=3; f(st[sp],st[sp+1],st[sp+2]); break; \
+    case 4: sp-=4; f(st[sp],st[sp+1],st[sp+2],st[sp+3]); break; \
+    case 5: sp-=5; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4]); break; \
+    case 6: sp-=6; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5]); break; \
+    case 7: sp-=7; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6]); break; \
+    case 8: sp-=8; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7]); break; \
+    case 9: sp-=9; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8]); break; \
+    case 10: sp-=10; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9]); break; \
+    case 11: sp-=11; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10]); break; \
+    case 12: sp-=12; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11]); break; \
+    case 13: sp-=13; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12]); break; \
+    case 14: sp-=14; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13]); break; \
+    case 15: sp-=15; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13],st[sp+14]); break; \
+    case 16: sp-=16; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13],st[sp+14],st[sp+15]); break; \
+    case 17: sp-=17; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13],st[sp+14],st[sp+15],st[sp+16]); break; \
+    case 18: sp-=18; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13],st[sp+14],st[sp+15],st[sp+16],st[sp+17]); break; \
+    case 19: sp-=19; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13],st[sp+14],st[sp+15],st[sp+16],st[sp+17],st[sp+18]); break; \
+    case 20: sp-=20; f(st[sp],st[sp+1],st[sp+2],st[sp+3],st[sp+4],st[sp+5],st[sp+6],st[sp+7],st[sp+8],st[sp+9],st[sp+10],st[sp+11],st[sp+12],st[sp+13],st[sp+14],st[sp+15],st[sp+16],st[sp+17],st[sp+18],st[sp+19]); break; \
+    default: \
+      pari_err_IMPL("functions with more than 20 parameters");\
+      goto endeval; /*not reached*/ \
+  }
+
+    case OCcallgen:
+      {
+        entree *ep = (entree *)operand;
+        GEN res;
+        /* Macro Madness : evaluate function ep->value on arguments
+         * st[sp-ep->arity .. sp]. Set res = result. */
+        EVAL_f(res = ((GEN (*)(ANYARG))ep->value));
+        if (br_status) goto endeval;
+        gel(st,sp++)=res;
+        break;
+      }
+    case OCcallgen2: /*same for ep->arity = 2. Is this optimization worth it ?*/
+      {
+        entree *ep = (entree *)operand;
+        GEN res;
+        sp-=2;
+        res = ((GEN (*)(GEN,GEN))ep->value)(gel(st,sp),gel(st,sp+1));
+        if (br_status) goto endeval;
+        gel(st,sp++)=res;
+        break;
+      }
+    case OCcalllong:
+      {
+        entree *ep = (entree *)operand;
+        long res;
+        EVAL_f(res = ((long (*)(ANYARG))ep->value));
+        if (br_status) goto endeval;
+        st[sp++] = res;
+        break;
+      }
+    case OCcallint:
+      {
+        entree *ep = (entree *)operand;
+        long res;
+        EVAL_f(res = ((int (*)(ANYARG))ep->value));
+        if (br_status) goto endeval;
+        st[sp++] = res;
+        break;
+      }
+    case OCcallvoid:
+      {
+        entree *ep = (entree *)operand;
+        EVAL_f(((void (*)(ANYARG))ep->value));
+        if (br_status) goto endeval;
+        break;
+      }
+#undef EVAL_f
+
+    case OCcalluser:
+      {
+        long n=operand;
+        GEN fun = gel(st,sp-1-n);
+        long arity;
+        GEN z;
+        if (typ(fun)!=t_CLOSURE) pari_err(e_NOTFUNC, fun);
+        arity = closure_arity(fun);
+        if (n!=arity)
+        {
+          if (n>arity)
+            pari_err(e_MISC,"too many parameters in user-defined function call");
+          st_alloc(arity-n);
+          for (j=n+1;j<=arity;j++)
+            gel(st,sp++)=0;
+        }
+#ifdef STACK_CHECK
+        if (PARI_stack_limit && (void*) &z <= PARI_stack_limit)
+          pari_err(e_MISC, "deep recursion");
+#endif
+        z = closure_return(fun);
+        if (br_status) goto endeval;
+        gel(st, sp-1) = z;
+        break;
+      }
+    case OCnewframe:
+      if (operand>0) nbmvar+=operand;
+      else operand=-operand;
+      pari_stack_alloc(&s_var,operand);
+      s_var.n+=operand;
+      for(j=1;j<=operand;j++)
+      {
+        var[s_var.n-j].flag=PUSH_VAL;
+        var[s_var.n-j].value=gen_0;
+      }
+      break;
+    case OCsaveframe:
+      {
+        GEN cl = (operand?gcopy:shallowcopy)(gel(st,sp-1));
+        long l = lg(gel(cl,7));
+        GEN  v = cgetg(l, t_VEC);
+        for(j=1; j<l; j++)
+        {
+          GEN val = var[s_var.n-j].value;
+          gel(v,j) = operand?gcopy(val):val;
+        }
+        gel(cl,7) = v;
+        gel(st,sp-1) = cl;
+      }
+      break;
+    case OCgetargs:
+      pari_stack_alloc(&s_var,operand);
+      s_var.n+=operand;
+      nbmvar+=operand;
+      sp-=operand;
+      for (j=0;j<operand;j++)
+      {
+        if (gel(st,sp+j))
+          pushlex(j-operand,gel(st,sp+j));
+        else
+        {
+          var[s_var.n+j-operand].flag=DEFAULT_VAL;
+          var[s_var.n+j-operand].value=gen_0;
+        }
+      }
+      break;
+    case OCcheckuserargs:
+      for (j=0; j<operand; j++)
+        if (var[s_var.n-operand+j].flag==DEFAULT_VAL)
+          pari_err(e_MISC,"missing mandatory argument"
+                   " '%s' in user function",get_arg_name(C,j+1));
+      break;
+    case OCcheckargs:
+      for (j=sp-1;operand;operand>>=1UL,j--)
+        if ((operand&1L) && gel(st,j)==NULL)
+          pari_err(e_MISC,"missing mandatory argument");
+      break;
+    case OCcheckargs0:
+      for (j=sp-1;operand;operand>>=1UL,j--)
+        if ((operand&1L) && gel(st,j))
+          pari_err(e_MISC,"argument type not implemented");
+      break;
+    case OCdefaultlong:
+      sp--;
+      if (st[sp+operand])
+        st[sp+operand]=gtos(gel(st,sp+operand));
+      else
+        st[sp+operand]=st[sp];
+      break;
+    case OCdefaultgen:
+      sp--;
+      if (!st[sp+operand])
+        st[sp+operand]=st[sp];
+      break;
+    case OCvec:
+      gel(st,sp++)=cgetg(operand,t_VEC);
+      st[sp++]=avma;
+      break;
+    case OCcol:
+      gel(st,sp++)=cgetg(operand,t_COL);
+      st[sp++]=avma;
+      break;
+    case OCmat:
+      {
+        GEN z;
+        long l=st[sp-1];
+        z=cgetg(operand,t_MAT);
+        for(j=1;j<operand;j++)
+          gel(z,j) = cgetg(l,t_COL);
+        gel(st,sp-1) = z;
+        st[sp++]=avma;
+      }
+      break;
+    case OCpop:
+      sp-=operand;
+      break;
+    case OCdup:
+      {
+        long i, s=st[sp-1];
+        st_alloc(operand);
+        for(i=1;i<=operand;i++)
+          st[sp++]=s;
+      }
+      break;
+    }
+  }
+  if (0)
+  {
+endeval:
+    sp = saved_sp;
+    for(  ; rp>saved_rp ;  )
+    {
+      gp_pointer *g = &ptrs[--rp];
+      clone_unlock(g->ox);
+    }
+  }
+  s_trace.n--;
+  restore_vars(nbmvar, nblvar);
+  clone_unlock(C);
+}
+
+GEN
+closure_evalgen(GEN C)
+{
+  pari_sp ltop=avma;
+  closure_eval(C);
+  if (br_status) { avma=ltop; return NULL; }
+  return gerepileupto(ltop,gel(st,--sp));
+}
+
+void
+evalstate_save(struct pari_evalstate *state)
+{
+  state->avma = avma;
+  state->sp   = sp;
+  state->rp   = rp;
+  state->var  = s_var.n;
+  state->lvars= s_lvars.n;
+  state->trace= s_trace.n;
+  compilestate_save(&state->comp);
+  mtstate_save(&state->pending_threads);
+}
+
+void
+evalstate_restore(struct pari_evalstate *state)
+{
+  avma = state->avma;
+  mtstate_restore(&state->pending_threads);
+  sp = state->sp;
+  rp = state->rp;
+  restore_vars(s_var.n-state->var,s_lvars.n-state->lvars);
+  restore_trace(s_trace.n-state->trace);
+  reset_break();
+  compilestate_restore(&state->comp);
+}
+
+GEN
+evalstate_restore_err(struct pari_evalstate *state)
+{
+  GENbin* err = copy_bin(pari_err_last());
+  evalstate_restore(state);
+  return bin_copy(err);
+}
+
+void
+evalstate_reset(void)
+{
+  mtstate_reset();
+  sp = 0;
+  rp = 0;
+  dbg_level = 0;
+  restore_vars(s_var.n, s_lvars.n);
+  s_trace.n = 0;
+  reset_break();
+  compilestate_reset();
+  parsestate_reset();
+  avma = top;
+}
+
+void
+evalstate_clone(void)
+{
+  long i;
+  for (i = 1; i<=s_var.n; i++) copylex(-i);
+  lvar_make_safe();
+  for (i = 0; i< s_trace.n; i++)
+  {
+    GEN C = trace[i].closure;
+    if (isonstack(C)) trace[i].closure = gclone(C);
+  }
+}
+
+GEN
+closure_trapgen(GEN C, long numerr)
+{
+  VOLATILE GEN x;
+  struct pari_evalstate state;
+  evalstate_save(&state);
+  pari_CATCH(numerr) { x = (GEN)1L; }
+  pari_TRY { x = closure_evalgen(C); } pari_ENDCATCH;
+  if (x == (GEN)1L) evalstate_restore(&state);
+  return x;
+}
+
+GEN
+closure_evalnobrk(GEN C)
+{
+  pari_sp ltop=avma;
+  closure_eval(C);
+  if (br_status) pari_err(e_MISC, "break not allowed here");
+  return gerepileupto(ltop,gel(st,--sp));
+}
+
+void
+closure_evalvoid(GEN C)
+{
+  pari_sp ltop=avma;
+  closure_eval(C);
+  avma=ltop;
+}
+
+GEN
+closure_evalres(GEN C)
+{
+  return closure_return(C);
+}
+
+INLINE GEN
+closure_returnupto(GEN C)
+{
+  pari_sp av=avma;
+  return copyupto(closure_return(C),(GEN)av);
+}
+
+void
+closure_callvoid1(GEN C, GEN x)
+{
+  long i, ar = closure_arity(C);
+  gel(st,sp++) = x;
+  for(i=2; i <= ar; i++) gel(st,sp++) = NULL;
+  closure_evalvoid(C);
+}
+
+GEN
+closure_callgen1(GEN C, GEN x)
+{
+  long i, ar = closure_arity(C);
+  gel(st,sp++) = x;
+  for(i=2; i<= ar; i++) gel(st,sp++) = NULL;
+  return closure_returnupto(C);
+}
+
+GEN
+pareval_worker(GEN C)
+{
+  return closure_callgenall(C, 0);
+}
+
+GEN
+pareval(GEN C)
+{
+  pari_sp av = avma;
+  long l = lg(C), i, pending = 0, workid;
+  struct pari_mt pt;
+  GEN worker, V, done;
+  if (!is_vec_t(typ(C))) pari_err_TYPE("pareval",C);
+  for (i=1; i<l; i++)
+    if (typ(gel(C,i))!=t_CLOSURE)
+      pari_err_TYPE("pareval",gel(C,i));
+  worker = snm_closure(is_entry("_pareval_worker"), NULL);
+  V = cgetg(l, t_VEC);
+  mt_queue_start(&pt, worker);
+  for (i=1; i<l || pending; i++)
+  {
+    mt_queue_submit(&pt, i, i<l? mkvec(gel(C,i)): NULL);
+    done = mt_queue_get(&pt, &workid, &pending);
+    if (done) gel(V,workid) = done;
+  }
+  mt_queue_end(&pt);
+  return gerepilecopy(av, V);
+}
+
+GEN
+parvector_worker(GEN i, GEN C)
+{
+  return closure_callgen1(C, i);
+}
+
+GEN
+parfor_worker(GEN i, GEN C)
+{
+  retmkvec2(gcopy(i), closure_callgen1(C, i));
+}
+
+GEN
+parvector(long n, GEN code)
+{
+  long i, pending = 0, workid;
+  GEN worker = snm_closure(is_entry("_parvector_worker"), mkvec(code));
+  GEN a, V, done;
+  struct pari_mt pt;
+  mt_queue_start(&pt, worker);
+  a = mkvec(cgetipos(3)); /* left on the stack */
+  V = cgetg(n+1, t_VEC);
+  for (i=1; i<=n || pending; i++)
+  {
+    mael(a,1,2) = i;
+    mt_queue_submit(&pt, i, i<=n? a: NULL);
+    done = mt_queue_get(&pt, &workid, &pending);
+    if (done) gel(V,workid) = done;
+  }
+  mt_queue_end(&pt);
+  return V;
+}
+
+GEN
+parsum(GEN a, GEN b, GEN code, GEN x)
+{
+  pari_sp av = avma, av2, lim;
+  long pending = 0;
+  GEN worker = snm_closure(is_entry("_parvector_worker"), mkvec(code));
+  GEN done;
+  struct pari_mt pt;
+  if (typ(a) != t_INT) pari_err_TYPE("parsum",a);
+  if (!x) x = gen_0;
+  if (gcmp(b,a) < 0) return gcopy(x);
+
+  mt_queue_start(&pt, worker);
+  b = gfloor(b);
+  a = mkvec(setloop(a));
+  av2=avma; lim = stack_lim(av2,1);
+  for (; cmpii(gel(a,1),b) <= 0 || pending; gel(a,1) = incloop(gel(a,1)))
+  {
+    mt_queue_submit(&pt, 0, cmpii(gel(a,1),b) <= 0? a: NULL);
+    done = mt_queue_get(&pt, NULL, &pending);
+    if (done)
+    {
+      x = gadd(x, done);
+      if (low_stack(lim, stack_lim(av2,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"sum");
+        x = gerepileupto(av2,x);
+      }
+    }
+  }
+  mt_queue_end(&pt);
+  return gerepilecopy(av, x);
+}
+
+void
+parfor(GEN a, GEN b, GEN code, GEN code2)
+{
+  pari_sp av = avma, av2;
+  long running, pending = 0;
+  long status = br_NONE;
+  GEN worker = snm_closure(is_entry("_parfor_worker"), mkvec(code));
+  GEN done, stop = NULL;
+  struct pari_mt pt;
+  if (typ(a) != t_INT) pari_err_TYPE("parfor",a);
+  if (b && gcmp(b,a) < 0) return;
+
+  mt_queue_start(&pt, worker);
+  b = b ? gfloor(b): NULL;
+  a = mkvec(setloop(a));
+  av2 = avma;
+  while ((running = (!stop && (!b || cmpii(gel(a,1),b) <= 0))) || pending)
+  {
+    mt_queue_submit(&pt, 0, running ? a: NULL);
+    done = mt_queue_get(&pt, NULL, &pending);
+    if (code2 && done && (!stop || cmpii(gel(done,1),stop) < 0))
+    {
+      push_lex(gel(done,1), code2);
+      push_lex(gel(done,2), NULL);
+      closure_evalvoid(code2);
+      pop_lex(2);
+      if (loop_break())
+      {
+        status = br_status;
+        br_status = br_NONE;
+        stop = gerepileuptoint(av2, gel(done,1));
+      }
+    }
+    gel(a,1) = incloop(gel(a,1));
+    if (!stop) avma = av2;
+  }
+  avma = av2;
+  mt_queue_end(&pt);
+  br_status = status;
+  avma = av;
+}
+
+void
+parforprime(GEN a, GEN b, GEN code, GEN code2)
+{
+  pari_sp av = avma, av2;
+  long running, pending = 0;
+  long status = br_NONE;
+  GEN worker = snm_closure(is_entry("_parfor_worker"), mkvec(code));
+  GEN done, stop = NULL;
+  struct pari_mt pt;
+  forprime_t T;
+
+  if (!forprime_init(&T, a,b)) { avma = av; return; }
+  mt_queue_start(&pt, worker);
+  av2 = avma;
+  while ((running = (!stop && forprime_next(&T))) || pending)
+  {
+    mt_queue_submit(&pt, 0, running ? mkvec(T.pp): NULL);
+    done = mt_queue_get(&pt, NULL, &pending);
+    if (code2 && done && (!stop || cmpii(gel(done,1),stop) < 0))
+    {
+      push_lex(gel(done,1), code2);
+      push_lex(gel(done,2), NULL);
+      closure_evalvoid(code2);
+      pop_lex(2);
+      if (loop_break())
+      {
+        status = br_status;
+        br_status = br_NONE;
+        stop = gerepileuptoint(av2, gel(done,1));
+      }
+    }
+    if (!stop) avma = av2;
+  }
+  avma = av2;
+  mt_queue_end(&pt);
+  br_status = status;
+  avma = av;
+}
+
+GEN
+closure_callgen2(GEN C, GEN x, GEN y)
+{
+  long i, ar = closure_arity(C);
+  st_alloc(ar);
+  gel(st,sp++) = x;
+  gel(st,sp++) = y;
+  for(i=3; i<=ar; i++) gel(st,sp++) = NULL;
+  return closure_returnupto(C);
+}
+
+GEN
+closure_callgenvec(GEN C, GEN args)
+{
+  long i, l = lg(args), ar = closure_arity(C);
+  st_alloc(ar);
+  for (i = 1; i < l;   i++) gel(st,sp++) = gel(args,i);
+  for(      ; i <= ar; i++) gel(st,sp++) = NULL;
+  return closure_returnupto(C);
+}
+
+GEN
+closure_callgenall(GEN C, long n, ...)
+{
+  va_list ap;
+  long i, ar = closure_arity(C);
+  va_start(ap,n);
+  st_alloc(ar);
+  for (i = 1; i <=n;  i++) gel(st,sp++) = va_arg(ap, GEN);
+  for(      ; i <=ar; i++) gel(st,sp++) = NULL;
+  va_end(ap);
+  return closure_returnupto(C);
+}
+
+GEN
+gp_eval(void *E, GEN x)
+{
+  GEN code = (GEN)E;
+  set_lex(-1,x);
+  return closure_evalnobrk(code);
+}
+
+GEN
+gp_evalupto(void *E, GEN x)
+{
+  pari_sp av = avma;
+  return copyupto(gp_eval(E,x), (GEN)av);
+}
+
+long
+gp_evalbool(void *E, GEN x)
+{
+  pari_sp av = avma;
+  long res  = !gequal0(gp_eval(E,x));
+  avma = av; return res;
+}
+
+long
+gp_evalvoid(void *E, GEN x)
+{
+  GEN code = (GEN)E;
+  set_lex(-1,x);
+  closure_evalvoid(code);
+  return loop_break();
+}
+
+GEN
+gp_call(void *E, GEN x)
+{
+  GEN code = (GEN)E;
+  return closure_callgen1(code, x);
+}
+
+long
+gp_callbool(void *E, GEN x)
+{
+  pari_sp av = avma;
+  GEN code = (GEN)E;
+  long res  = !gequal0(closure_callgen1(code, x));
+  avma = av; return res;
+}
+
+long
+gp_callvoid(void *E, GEN x)
+{
+  GEN code = (GEN)E;
+  closure_callvoid1(code, x);
+  return loop_break();
+}
+
+INLINE const char *
+disassemble_cast(long mode)
+{
+  switch (mode)
+  {
+  case Gsmall:
+    return "small";
+  case Ggen:
+    return "gen";
+  case Gvar:
+    return "var";
+  case Gvoid:
+    return "void";
+  default:
+    return "unknown";
+  }
+}
+
+void
+closure_disassemble(GEN C)
+{
+  const char * code;
+  GEN oper;
+  long i;
+  if (typ(C)!=t_CLOSURE) pari_err_TYPE("disassemble",C);
+  code=closure_codestr(C);
+  oper=closure_get_oper(C);
+  for(i=1;i<lg(oper);i++)
+  {
+    op_code opcode=(op_code) code[i];
+    long operand=oper[i];
+    pari_printf("%05ld\t",i);
+    switch(opcode)
+    {
+    case OCpushlong:
+      pari_printf("pushlong\t%ld\n",operand);
+      break;
+    case OCpushgnil:
+      pari_printf("pushgnil\n");
+      break;
+    case OCpushgen:
+      pari_printf("pushgen\t\t%ld\n",operand);
+      break;
+    case OCpushreal:
+      pari_printf("pushreal\t%ld\n",operand);
+      break;
+    case OCpushstoi:
+      pari_printf("pushstoi\t%ld\n",operand);
+      break;
+    case OCpushvar:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("pushvar\t%s\n",ep->name);
+        break;
+      }
+    case OCpushdyn:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("pushdyn\t\t%s\n",ep->name);
+        break;
+      }
+    case OCpushlex:
+      pari_printf("pushlex\t\t%ld\n",operand);
+      break;
+    case OCstoredyn:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("storedyn\t%s\n",ep->name);
+        break;
+      }
+    case OCstorelex:
+      pari_printf("storelex\t%ld\n",operand);
+      break;
+    case OCstoreptr:
+      pari_printf("storeptr\n");
+      break;
+    case OCsimpleptrdyn:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("simpleptrdyn\t%s\n",ep->name);
+        break;
+      }
+    case OCsimpleptrlex:
+      pari_printf("simpleptrlex\t%ld\n",operand);
+      break;
+    case OCnewptrdyn:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("newptrdyn\t%s\n",ep->name);
+        break;
+      }
+    case OCnewptrlex:
+      pari_printf("newptrlex\t%ld\n",operand);
+      break;
+    case OCpushptr:
+      pari_printf("pushptr\n");
+      break;
+    case OCstackgen:
+      pari_printf("stackgen\t%ld\n",operand);
+      break;
+    case OCendptr:
+      pari_printf("endptr\t\t%ld\n",operand);
+      break;
+    case OCprecreal:
+      pari_printf("precreal\n");
+      break;
+    case OCprecdl:
+      pari_printf("precdl\n");
+      break;
+    case OCstoi:
+      pari_printf("stoi\n");
+      break;
+    case OCitos:
+      pari_printf("itos\t\t%ld\n",operand);
+      break;
+    case OCtostr:
+      pari_printf("tostr\t\t%ld\n",operand);
+      break;
+    case OCvarn:
+      pari_printf("varn\t\t%ld\n",operand);
+      break;
+    case OCcopy:
+      pari_printf("copy\n");
+      break;
+    case OCcopyifclone:
+      pari_printf("copyifclone\n");
+      break;
+    case OCcompo1:
+      pari_printf("compo1\t\t%s\n",disassemble_cast(operand));
+      break;
+    case OCcompo1ptr:
+      pari_printf("compo1ptr\n");
+      break;
+    case OCcompo2:
+      pari_printf("compo2\t\t%s\n",disassemble_cast(operand));
+      break;
+    case OCcompo2ptr:
+      pari_printf("compo2ptr\n");
+      break;
+    case OCcompoC:
+      pari_printf("compoC\n");
+      break;
+    case OCcompoCptr:
+      pari_printf("compoCptr\n");
+      break;
+    case OCcompoL:
+      pari_printf("compoL\n");
+      break;
+    case OCcompoLptr:
+      pari_printf("compoLptr\n");
+      break;
+    case OCcheckargs:
+      pari_printf("checkargs\t0x%lx\n",operand);
+      break;
+    case OCcheckargs0:
+      pari_printf("checkargs0\t0x%lx\n",operand);
+      break;
+    case OCcheckuserargs:
+      pari_printf("checkuserargs\t%ld\n",operand);
+      break;
+    case OCdefaultlong:
+      pari_printf("defaultlong\t%ld\n",operand);
+      break;
+    case OCdefaultgen:
+      pari_printf("defaultgen\t%ld\n",operand);
+      break;
+    case OCgetargs:
+      pari_printf("getargs\t\t%ld\n",operand);
+      break;
+    case OCdefaultarg:
+      pari_printf("defaultarg\t%ld\n",operand);
+      break;
+    case OClocalvar:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("localvar\t%s\n",ep->name);
+        break;
+      }
+    case OClocalvar0:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("localvar0\t%s\n",ep->name);
+        break;
+      }
+    case OCcallgen:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("callgen\t\t%s\n",ep->name);
+        break;
+      }
+    case OCcallgen2:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("callgen2\t%s\n",ep->name);
+        break;
+      }
+    case OCcalllong:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("calllong\t%s\n",ep->name);
+        break;
+      }
+    case OCcallint:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("callint\t\t%s\n",ep->name);
+        break;
+      }
+    case OCcallvoid:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("callvoid\t%s\n",ep->name);
+        break;
+      }
+    case OCcalluser:
+      pari_printf("calluser\t%ld\n",operand);
+      break;
+    case OCvec:
+      pari_printf("vec\t\t%ld\n",operand);
+      break;
+    case OCcol:
+      pari_printf("col\t\t%ld\n",operand);
+      break;
+    case OCmat:
+      pari_printf("mat\t\t%ld\n",operand);
+      break;
+    case OCnewframe:
+      pari_printf("newframe\t%ld\n",operand);
+      break;
+    case OCsaveframe:
+      pari_printf("saveframe\t%ld\n", operand);
+      break;
+    case OCpop:
+      pari_printf("pop\t\t%ld\n",operand);
+      break;
+    case OCdup:
+      pari_printf("dup\t\t%ld\n",operand);
+      break;
+    case OCavma:
+      pari_printf("avma\n",operand);
+      break;
+    case OCgerepile:
+      pari_printf("gerepile\n",operand);
+      break;
+    case OCcowvardyn:
+      {
+        entree *ep = (entree *)operand;
+        pari_printf("cowvardyn\t%s\n",ep->name);
+        break;
+      }
+    case OCcowvarlex:
+      pari_printf("cowvarlex\t%ld\n",operand);
+      break;
+    }
+  }
+}
+
+static int
+opcode_need_relink(op_code opcode)
+{
+  switch(opcode)
+  {
+  case OCpushlong:
+  case OCpushgen:
+  case OCpushgnil:
+  case OCpushreal:
+  case OCpushstoi:
+  case OCpushlex:
+  case OCstorelex:
+  case OCstoreptr:
+  case OCsimpleptrlex:
+  case OCnewptrlex:
+  case OCpushptr:
+  case OCstackgen:
+  case OCendptr:
+  case OCprecreal:
+  case OCprecdl:
+  case OCstoi:
+  case OCitos:
+  case OCtostr:
+  case OCvarn:
+  case OCcopy:
+  case OCcopyifclone:
+  case OCcompo1:
+  case OCcompo1ptr:
+  case OCcompo2:
+  case OCcompo2ptr:
+  case OCcompoC:
+  case OCcompoCptr:
+  case OCcompoL:
+  case OCcompoLptr:
+  case OCcheckargs:
+  case OCcheckargs0:
+  case OCcheckuserargs:
+  case OCgetargs:
+  case OCdefaultarg:
+  case OCdefaultgen:
+  case OCdefaultlong:
+  case OCcalluser:
+  case OCvec:
+  case OCcol:
+  case OCmat:
+  case OCnewframe:
+  case OCsaveframe:
+  case OCdup:
+  case OCpop:
+  case OCavma:
+  case OCgerepile:
+  case OCcowvarlex:
+    break;
+  case OCpushvar:
+  case OCpushdyn:
+  case OCstoredyn:
+  case OCsimpleptrdyn:
+  case OCnewptrdyn:
+  case OClocalvar:
+  case OClocalvar0:
+  case OCcallgen:
+  case OCcallgen2:
+  case OCcalllong:
+  case OCcallint:
+  case OCcallvoid:
+  case OCcowvardyn:
+    return 1;
+  }
+  return 0;
+}
+
+static void
+closure_relink(GEN C, hashtable *table)
+{
+  const char *code = closure_codestr(C);
+  GEN oper = closure_get_oper(C);
+  GEN fram = gel(closure_get_dbg(C),3);
+  long i, j;
+  for(i=1;i<lg(oper);i++)
+    if (oper[i] && opcode_need_relink((op_code)code[i]))
+      oper[i] = (long) hash_search(table,(void*) oper[i])->val;
+  for (i=1;i<lg(fram);i++)
+    for (j=1;j<lg(gel(fram,i));j++)
+      if (mael(fram,i,j))
+        mael(fram,i,j) = (long) hash_search(table,(void*) mael(fram,i,j))->val;
+}
+
+void
+gen_relink(GEN x, hashtable *table)
+{
+  long i, lx, tx = typ(x);
+  switch(tx)
+  {
+    case t_CLOSURE:
+      closure_relink(x, table);
+      gen_relink(closure_get_data(x), table);
+      if (lg(x)==8) gen_relink(closure_get_frame(x), table);
+      break;
+    case t_LIST:
+      gen_relink(list_data(x), table);
+      break;
+    case t_VEC: case t_COL: case t_MAT: case t_ERROR:
+      lx = lg(x);
+      for (i=lontyp[tx]; i<lx; i++) gen_relink(gel(x,i), table);
+  }
+}
+
+static void
+closure_unlink(GEN C)
+{
+  const char *code = closure_codestr(C);
+  GEN oper = closure_get_oper(C);
+  GEN fram = gel(closure_get_dbg(C),3);
+  long i, j;
+  for(i=1;i<lg(oper);i++)
+    if (oper[i] && opcode_need_relink((op_code) code[i]))
+    {
+      long n = pari_stack_new(&s_relocs);
+      relocs[n] = (entree *) oper[i];
+    }
+  for (i=1;i<lg(fram);i++)
+    for (j=1;j<lg(gel(fram,i));j++)
+      if (mael(fram,i,j))
+      {
+        long n = pari_stack_new(&s_relocs);
+        relocs[n] = (entree *) mael(fram,i,j);
+      }
+}
+
+static void
+gen_unlink(GEN x)
+{
+  long i, lx, tx = typ(x);
+  switch(tx)
+  {
+    case t_CLOSURE:
+      closure_unlink(x);
+      gen_unlink(closure_get_data(x));
+      if (lg(x)==8) gen_unlink(closure_get_frame(x));
+      break;
+    case t_LIST:
+      gen_unlink(list_data(x));
+      break;
+    case t_VEC: case t_COL: case t_MAT: case t_ERROR:
+      lx = lg(x);
+      for (i = lontyp[tx]; i<lx; i++) gen_unlink(gel(x,i));
+  }
+}
+
+GEN
+copybin_unlink(GEN C)
+{
+  long i, l , n, nold = s_relocs.n;
+  GEN v, w, V, res;
+  if (C)
+    gen_unlink(C);
+  else
+  { /* contents of all variables */
+    long v, maxv = pari_var_next();
+    for (v=0; v<maxv; v++)
+    {
+      entree *ep = varentries[v];
+      if (!ep || !ep->value) continue;
+      gen_unlink((GEN)ep->value);
+    }
+  }
+  n = s_relocs.n-nold;
+  v = cgetg(n+1, t_VECSMALL);
+  for(i=0; i<n; i++)
+    v[i+1] = (long) relocs[i];
+  s_relocs.n = nold;
+  w = vecsmall_uniq(v); l = lg(w);
+  res = cgetg(3,t_VEC);
+  V = cgetg(l, t_VEC);
+  for(i=1; i<l; i++)
+  {
+    entree *ep = (entree*) w[i];
+    gel(V,i) = strtoGENstr(ep->name);
+  }
+  gel(res,1) = vecsmall_copy(w);
+  gel(res,2) = V;
+  return res;
+}
+
+static ulong
+hash_id(void *x) { return (ulong)x; }
+static int
+eq_id(void *x, void *y) { return x == y; }
+
+/* e = t_VECSMALL of entree *ep [ addresses ],
+ * names = t_VEC of strtoGENstr(ep.names),
+ * Return hashtable : ep => is_entry(ep.name) */
+hashtable *
+hash_from_link(GEN e, GEN names, int use_stack)
+{
+  long i, l = lg(e);
+  hashtable *h = hash_create(l-1, &hash_id, &eq_id, use_stack);
+  if (lg(names) != l) pari_err_DIM("hash_from_link");
+  for (i = 1; i < l; i++)
+  {
+    char *s = GSTR(gel(names,i));
+    hash_insert(h, (void*)e[i], (void*)fetch_entry(s, strlen(s)));
+  }
+  return h;
+}
+
+void
+bincopy_relink(GEN C, GEN V)
+{
+  pari_sp av = avma;
+  hashtable *table = hash_from_link(gel(V,1),gel(V,2),1);
+  gen_relink(C, table);
+  avma = av;
+}
diff --git a/src/language/hash.c b/src/language/hash.c
new file mode 100644
index 0000000..a2b7c6b
--- /dev/null
+++ b/src/language/hash.c
@@ -0,0 +1,244 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/*                                                                  */
+/*                    GENERAL HASHTABLES                            */
+/*                                                                  */
+/********************************************************************/
+/* http://planetmath.org/encyclopedia/GoodHashTablePrimes.html */
+static const ulong hashprimes[] = {
+  53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613,
+  393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653,
+  100663319, 201326611, 402653189, 805306457, 1610612741
+};
+static const int hashprimes_len = sizeof(hashprimes) / sizeof(hashprimes[0]);
+
+INLINE void
+setlen(hashtable *h, ulong len) {
+  h->maxnb = (ulong)ceil(len * 0.65);
+  h->len  = len;
+}
+
+static int
+get_prime_index(ulong len)
+{
+  int i;
+  for (i=0; i < hashprimes_len; i++)
+    if (hashprimes[i] > len) return i;
+  pari_err_OVERFLOW("hash table [too large]");
+  return -1; /* not reached */
+}
+
+/* link hashentry e to hashtable h, setting e->hash / e->next */
+INLINE void
+hash_link(hashtable *h, hashentry *e)
+{
+  ulong index;
+  e->hash = h->hash(e->key); index = e->hash % h->len;
+  e->next = h->table[index]; h->table[index] = e;
+}
+
+hashtable *
+hash_create(ulong minsize, ulong (*hash)(void*), int (*eq)(void*,void*),
+            int use_stack)
+{
+  int i = get_prime_index(minsize);
+  ulong len = hashprimes[i];
+  hashtable *h;
+
+  if (use_stack)
+  {
+    h = (hashtable*)stack_malloc(sizeof(hashtable));
+    h->table = (hashentry**)stack_calloc(len * sizeof(hashentry*));
+    h->use_stack = 1;
+  }
+  else
+  {
+    h = (hashtable*)pari_malloc(sizeof(hashtable));
+    h->table = (hashentry**)pari_calloc(len * sizeof(hashentry*));
+    h->use_stack = 0;
+  }
+  h->pindex = i;
+  h->nb = 0;
+  h->hash = hash;
+  h->eq   = eq;
+  setlen(h, len); return h;
+}
+
+void
+hash_insert(hashtable *h, void *k, void *v)
+{
+  hashentry *e;
+  ulong index;
+
+  if (h->use_stack)
+    e = (hashentry*) stack_malloc(sizeof(hashentry));
+  else
+    e = (hashentry*) pari_malloc(sizeof(hashentry));
+
+  if (++(h->nb) > h->maxnb && h->pindex < hashprimes_len-1)
+  { /* double table size */
+    ulong i, newlen = hashprimes[++(h->pindex)];
+    hashentry *E, **newtable;
+    if (h->use_stack)
+      newtable = (hashentry**)stack_calloc(newlen*sizeof(hashentry*));
+    else
+      newtable = (hashentry**)pari_calloc(newlen*sizeof(hashentry*));
+    for (i = 0; i < h->len; i++)
+      while ( (E = h->table[i]) )
+      {
+        h->table[i] = E->next;
+        index = E->hash % newlen;
+        E->next = newtable[index];
+        newtable[index] = E;
+      }
+    if (!h->use_stack) pari_free(h->table);
+    h->table = newtable;
+    setlen(h, newlen);
+  }
+  e->key = k;
+  e->val = v; hash_link(h, e);
+}
+
+/* returns entry associated with key k or NULL */
+hashentry *
+hash_search(hashtable *h, void *k)
+{
+  ulong hash;
+  hashentry *e;
+  if (h->nb == 0) return NULL;
+  hash = h->hash(k);
+  e = h->table[ hash % h->len ];
+  while (e)
+  {
+    if (hash == e->hash && h->eq(k, e->key)) return e;
+    e = e->next;
+  }
+  return NULL; /* not found */
+}
+
+hashentry *
+hash_remove(hashtable *h, void *k)
+{
+  ulong hash = h->hash(k), index = hash % h->len;
+  hashentry **pE = &(h->table[index]), *e = *pE;
+  while (e)
+  {
+    if (hash == e->hash && h->eq(k, e->key)) {
+      *pE = e->next; h->nb--;
+      return e;
+    }
+    pE = &(e->next);
+    e = e->next;
+  }
+  return NULL;
+}
+void
+hash_destroy(hashtable *h)
+{
+  ulong i;
+  if (h->use_stack) return;
+  for (i = 0; i < h->len; i++)
+  {
+    hashentry *e = h->table[i];
+    while (e) { hashentry *f = e; e = e->next; pari_free(f); }
+  }
+  pari_free(h->table); pari_free(h);
+}
+
+static
+int strequal(void *a, void *b) { return !strcmp((char*)a,(char*)b); }
+hashtable *
+hashstr_import_static(hashentry *e, ulong size)
+{
+  hashtable *h = hash_create(size, (ulong (*)(void *))hash_str, strequal, 0);
+  for ( ; e->key; e++) { hash_link(h, e); h->nb++; }
+  return h;
+}
+
+void
+hashstr_dbg(hashtable *h)
+{
+  ulong n, Total = 0, Max = 0;
+  hashentry *e, **table = h->table;
+  for (n=0; n < h->len; n++)
+  {
+    ulong m=0;
+    for (e=table[n]; e; e=e->next) m++;
+    Total += m; if (Max < m) Max = m;
+    pari_printf("%4ld:%2ld ",n,m);
+    if (n%9 == 8) pari_putc('\n');
+  }
+  pari_printf("\nTotal = %ld, Max = %ld\n", Total, Max);
+}
+
+/********************************************************************/
+/*                                                                  */
+/*                          HASH FUNCTIONS                          */
+/*                                                                  */
+/********************************************************************/
+
+INLINE ulong
+glue(ulong h, ulong a) { return (h << 5) + (h>>2) + a; }
+ulong
+hash_GEN(GEN x)
+{
+  ulong h = x[0];
+  long tx = typ(x), lx, i;
+  switch(tx)
+  { /* non recursive types */
+    case t_INT:
+      lx = lgefint(x);
+      h &= TYPBITS;
+      for (i = 1; i < lx; i++) h = glue(h, (ulong)x[i]);
+      return h;
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL:
+      lx = lg(x);
+      for (i = 1; i < lx; i++) h = glue(h, (ulong)x[i]);
+      return h;
+    /* one more special case */
+    case t_LIST:
+      x = list_data(x);
+      if (!x) return h;
+      /* fall through */
+    default:
+      if (lontyp[tx] == 2) { h = glue(h, x[1]); i = 2; } else i = 1;
+      lx = lg(x);
+      for (; i < lx; i++) h = glue(h, hash_GEN(gel(x,i)));
+      return h;
+  }
+}
+
+/* djb's hash */
+ulong
+hash_str(const char *str)
+{
+  ulong hash = 5381, c;
+  while ( (c = (ulong)*str++) )
+    hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+  return hash;
+}
+
+/* hashvalue's underlying hash function */
+ulong
+hash_str2(const char *s)
+{
+  ulong n = 0, c;
+  while ( (c = (ulong)*s++) ) n = (n<<1) ^ c;
+  return n;
+}
diff --git a/src/language/init.c b/src/language/init.c
new file mode 100644
index 0000000..4aaa79b
--- /dev/null
+++ b/src/language/init.c
@@ -0,0 +1,2338 @@
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*        INITIALIZING THE SYSTEM, ERRORS, STACK MANAGEMENT        */
+/*                                                                 */
+/*******************************************************************/
+#include <string.h>
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+#if defined(_WIN32) || defined(__CYGWIN32__)
+#  include "../systems/mingw/mingw.h"
+#  ifndef WINCE
+#    include <process.h>
+#  endif
+#endif
+
+const double LOG2    = 0.6931471805599453; /* log(2) */
+const double LOG10_2 = 0.3010299956639812; /* log_10(2) */
+const double LOG2_10 = 3.321928094887362;  /* log_2(10) */
+
+GEN gnil, gen_0, gen_1, gen_m1, gen_2, gen_m2, ghalf, err_e_STACK;
+
+static const ulong readonly_constants[] = {
+  evaltyp(t_INT) | _evallg(2),  /* gen_0 */
+  evallgefint(2),
+  evaltyp(t_INT) | _evallg(2),  /* gnil */
+  evallgefint(2),
+  evaltyp(t_INT) | _evallg(3),  /* gen_1 */
+  evalsigne(1) | evallgefint(3),
+  1,
+  evaltyp(t_INT) | _evallg(3),  /* gen_2 */
+  evalsigne(1) | evallgefint(3),
+  2,
+  evaltyp(t_INT) | _evallg(3),  /* gen_m1 */
+  evalsigne(-1) | evallgefint(3),
+  1,
+  evaltyp(t_INT) | _evallg(3),  /* gen_m2 */
+  evalsigne(-1) | evallgefint(3),
+  2,
+};
+static const long readonly_ghalf[] = {
+  evaltyp(t_FRAC) | _evallg(3), /* ghalf */
+  (long)(readonly_constants+4),
+  (long)(readonly_constants+7)
+};
+static const ulong readonly_err_STACK[] = {
+  evaltyp(t_ERROR) | _evallg(2),
+  e_STACK
+};
+THREAD GEN    bernzone;
+GEN     primetab; /* private primetable */
+byteptr diffptr;
+FILE    *pari_outfile, *pari_errfile, *pari_logfile, *pari_infile;
+char    *current_logfile, *current_psfile, *pari_datadir;
+long    gp_colors[c_LAST];
+int     disable_color;
+ulong   DEBUGFILES, DEBUGLEVEL, DEBUGMEM;
+long    DEBUGVAR;
+ulong   pari_mt_nbthreads;
+ulong   compatible, precreal, precdl, logstyle;
+gp_data *GP_DATA;
+
+GEN pari_colormap, pari_graphcolors;
+
+entree  **varentries;
+
+THREAD pari_sp bot, top, avma;
+THREAD size_t memused;
+
+static void ** MODULES, ** OLDMODULES;
+static pari_stack s_MODULES, s_OLDMODULES;
+const long functions_tblsz = 135; /* size of functions_hash */
+entree **functions_hash, **defaults_hash;
+
+void (*cb_pari_ask_confirm)(const char *);
+int  (*cb_pari_handle_exception)(long);
+int  (*cb_pari_whatnow)(PariOUT *out, const char *, int);
+void (*cb_pari_sigint)(void);
+void (*cb_pari_pre_recover)(long);
+void (*cb_pari_err_recover)(long);
+const char * pari_library_path = NULL;
+
+static THREAD GEN global_err_data;
+THREAD jmp_buf *iferr_env;
+const long CATCH_ALL = -1;
+
+/*********************************************************************/
+/*                                                                   */
+/*                       BLOCKS & CLONES                             */
+/*                                                                   */
+/*********************************************************************/
+/*#define DEBUG*/
+static THREAD long next_block;
+static THREAD GEN cur_block; /* current block in block list */
+#ifdef DEBUG
+static THREAD long NUM;
+#endif
+
+static void
+pari_init_blocks(void)
+{
+  next_block = 0; cur_block = NULL;
+#ifdef DEBUG
+  NUM = 0;
+#endif
+}
+
+static void
+pari_close_blocks(void)
+{
+  while (cur_block) killblock(cur_block);
+}
+
+/* Return x, where:
+ * x[-4]: reference count
+ * x[-3]: adress of next block
+ * x[-2]: adress of preceding block.
+ * x[-1]: number of allocated blocs.
+ * x[0..n-1]: malloc-ed memory. */
+GEN
+newblock(size_t n)
+{
+  long *x = (long *) pari_malloc((n + BL_HEAD)*sizeof(long)) + BL_HEAD;
+
+  bl_refc(x) = 1;
+  bl_next(x) = NULL;
+  bl_prev(x) = cur_block;
+  bl_num(x)  = next_block++;
+  if (cur_block) bl_next(cur_block) = x;
+#ifdef DEBUG
+  err_printf("+ %ld\n", ++NUM);
+#endif
+  if (DEBUGMEM)
+  {
+    if (!n) pari_warn(warner,"mallocing NULL object in newblock");
+    if (DEBUGMEM > 2)
+      err_printf("new block, size %6lu (no %ld): %08lx\n", n, next_block-1, x);
+  }
+  return cur_block = x;
+}
+
+GEN
+gcloneref(GEN x)
+{
+  if (isclone(x)) { ++bl_refc(x); return x; }
+  else return gclone(x);
+}
+
+void
+gclone_refc(GEN x) { ++bl_refc(x); }
+
+void
+gunclone(GEN x)
+{
+  if (--bl_refc(x) > 0) return;
+  BLOCK_SIGINT_START;
+  if (bl_next(x)) bl_prev(bl_next(x)) = bl_prev(x);
+  else
+  {
+    cur_block = bl_prev(x);
+    next_block = bl_num(x);
+  }
+  if (bl_prev(x)) bl_next(bl_prev(x)) = bl_next(x);
+  if (DEBUGMEM > 2)
+    err_printf("killing block (no %ld): %08lx\n", bl_num(x), x);
+  free((void*)bl_base(x)); /* pari_free not needed: we already block */
+  BLOCK_SIGINT_END;
+#ifdef DEBUG
+  err_printf("- %ld\n", NUM--);
+#endif
+}
+
+/* Recursively look for clones in the container and kill them. Then kill
+ * container if clone. SIGINT could be blocked until it returns */
+void
+gunclone_deep(GEN x)
+{
+  long i, lx;
+  GEN v;
+  if (isclone(x) && bl_refc(x) > 1) { --bl_refc(x); return; }
+  BLOCK_SIGINT_START;
+  switch(typ(x))
+  {
+    case t_VEC: case t_COL: case t_MAT:
+      lx = lg(x);
+      for (i=1;i<lx;i++) gunclone_deep(gel(x,i));
+      break;
+    case t_LIST:
+      v = list_data(x); lx = v? lg(v): 1;
+      for (i=1;i<lx;i++) gunclone_deep(gel(v,i));
+      pari_free(v); break;
+  }
+  if (isclone(x)) gunclone(x);
+  BLOCK_SIGINT_END;
+}
+
+int
+pop_entree_block(entree *ep, long loc)
+{
+  GEN x = (GEN)ep->value;
+  if (bl_num(x) < loc) return 0; /* older */
+  if (DEBUGMEM>2)
+    err_printf("popping %s (block no %ld)\n", ep->name, bl_num(x));
+  gunclone_deep(x); return 1;
+}
+
+/*********************************************************************/
+/*                                                                   */
+/*                       C STACK SIZE CONTROL                        */
+/*                                                                   */
+/*********************************************************************/
+/* Avoid core dump on deep recursion. Adapted Perl code by Dominic Dunlop */
+THREAD void *PARI_stack_limit = NULL;
+
+#ifdef STACK_CHECK
+
+#  ifdef __EMX__                                /* Emulate */
+void
+pari_stackcheck_init(void *pari_stack_base)
+{
+  (void) pari_stack_base;
+  if (!pari_stack_base) { PARI_stack_limit = NULL; return; }
+  PARI_stack_limit = get_stack(1./16, 32*1024);
+}
+#  else /* !__EMX__ */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+/* Set PARI_stack_limit to (a little above) the lowest safe address that can be
+ * used on the stack. Leave PARI_stack_limit at its initial value (NULL) to
+ * show no check should be made [init failed]. Assume stack grows downward. */
+void
+pari_stackcheck_init(void *pari_stack_base)
+{
+  struct rlimit rip;
+  ulong size;
+  if (!pari_stack_base) { PARI_stack_limit = NULL; return; }
+  if (getrlimit(RLIMIT_STACK, &rip)) return;
+  size = rip.rlim_cur;
+  if (size == (ulong)RLIM_INFINITY || size > (ulong)pari_stack_base)
+    PARI_stack_limit = (void*)(((ulong)pari_stack_base) / 16);
+  else
+    PARI_stack_limit = (void*)((ulong)pari_stack_base - (size/16)*15);
+}
+#  endif /* !__EMX__ */
+
+#else
+void
+pari_stackcheck_init(void *pari_stack_base)
+{
+  (void) pari_stack_base; PARI_stack_limit = NULL;
+}
+#endif /* STACK_CHECK */
+
+/*******************************************************************/
+/*                         HEAP TRAVERSAL                          */
+/*******************************************************************/
+struct getheap_t { long n, l; };
+static void
+f_getheap(GEN x, void *D)
+{
+  struct getheap_t *T = (struct getheap_t*)D;
+  T->n++;
+  T->l += gsizeword(x);
+}
+GEN
+getheap(void)
+{
+  struct getheap_t T = { 0, 0 };
+  traverseheap(&f_getheap, &T);
+  return mkvec2s(T.n, T.l + BL_HEAD * T.n);
+}
+
+void
+traverseheap( void(*f)(GEN, void *), void *data )
+{
+  GEN x;
+  for (x = cur_block; x; x = bl_prev(x)) f(x, data);
+}
+
+/*********************************************************************/
+/*                          DAEMON / FORK                            */
+/*********************************************************************/
+#if defined(HAS_WAITPID) && defined(HAS_SETSID)
+#  include <sys/wait.h>
+/* Properly fork a process, detaching from main process group without creating
+ * zombies on exit. Parent returns 1, son returns 0 */
+int
+pari_daemon(void)
+{
+  pid_t pid = fork();
+  switch(pid) {
+      case -1: return 1; /* father, fork failed */
+      case 0:
+        (void)setsid(); /* son becomes process group leader */
+        if (fork()) exit(0); /* now son exits, also when fork fails */
+        break; /* grandson: its father is the son, which exited,
+                * hence father becomes 'init', that'll take care of it */
+      default: /* father, fork succeeded */
+        (void)waitpid(pid,NULL,0); /* wait for son to exit, immediate */
+        return 1;
+  }
+  /* grandson */
+  return 0;
+}
+#else
+int
+pari_daemon(void)
+{
+  pari_err_IMPL("pari_daemon without waitpid & setsid");
+  return 0;
+}
+#endif
+
+/*********************************************************************/
+/*                                                                   */
+/*                       SYSTEM INITIALIZATION                       */
+/*                                                                   */
+/*********************************************************************/
+static int try_to_recover = 0;
+THREAD VOLATILE int PARI_SIGINT_block = 0, PARI_SIGINT_pending = 0;
+static void pari_sighandler(int sig);
+
+/*********************************************************************/
+/*                         SIGNAL HANDLERS                           */
+/*********************************************************************/
+static void
+dflt_sigint_fun(void) { pari_err(e_MISC, "user interrupt"); }
+
+#if defined(_WIN32) || defined(__CYGWIN32__)
+int win32ctrlc = 0;
+void
+dowin32ctrlc(void)
+{
+  win32ctrlc = 0;
+  cb_pari_sigint();
+}
+#endif
+
+static void
+pari_handle_SIGINT(void)
+{
+#ifdef _WIN32
+  if (++win32ctrlc >= 5) _exit(3);
+#else
+  cb_pari_sigint();
+#endif
+}
+
+static void
+pari_sighandler(int sig)
+{
+  const char *msg;
+#ifndef HAS_SIGACTION
+  /*SYSV reset the signal handler in the handler*/
+  (void)os_signal(sig,pari_sighandler);
+#endif
+  switch(sig)
+  {
+#ifdef SIGBREAK
+    case SIGBREAK:
+      if (PARI_SIGINT_block==1)
+      {
+        PARI_SIGINT_pending=SIGBREAK;
+        mt_sigint();
+      }
+      else pari_handle_SIGINT();
+      return;
+#endif
+
+#ifdef SIGINT
+    case SIGINT:
+      if (PARI_SIGINT_block==1)
+      {
+        PARI_SIGINT_pending=SIGINT;
+        mt_sigint();
+      }
+      else pari_handle_SIGINT();
+      return;
+#endif
+
+#ifdef SIGSEGV
+    case SIGSEGV:
+      msg="PARI/GP (Segmentation Fault)"; break;
+#endif
+#ifdef SIGBUS
+    case SIGBUS:
+      msg="PARI/GP (Bus Error)"; break;
+#endif
+#ifdef SIGFPE
+    case SIGFPE:
+      msg="PARI/GP (Floating Point Exception)"; break;
+#endif
+
+#ifdef SIGPIPE
+    case SIGPIPE:
+    {
+      pariFILE *f = GP_DATA->pp->file;
+      if (f && pari_outfile == f->file)
+      {
+        GP_DATA->pp->file = NULL; /* to avoid oo recursion on error */
+        pari_outfile = stdout; pari_fclose(f);
+        pari_err(e_MISC, "Broken Pipe, resetting file stack...");
+      }
+      return; /* not reached */
+    }
+#endif
+
+    default: msg="signal handling"; break;
+  }
+  pari_err_BUG(msg);
+}
+
+void
+pari_sig_init(void (*f)(int))
+{
+#ifdef SIGBUS
+  (void)os_signal(SIGBUS,f);
+#endif
+#ifdef SIGFPE
+  (void)os_signal(SIGFPE,f);
+#endif
+#ifdef SIGINT
+  (void)os_signal(SIGINT,f);
+#endif
+#ifdef SIGBREAK
+  (void)os_signal(SIGBREAK,f);
+#endif
+#ifdef SIGPIPE
+  (void)os_signal(SIGPIPE,f);
+#endif
+#ifdef SIGSEGV
+  (void)os_signal(SIGSEGV,f);
+#endif
+}
+
+/*********************************************************************/
+/*                      STACK AND UNIVERSAL CONSTANTS                */
+/*********************************************************************/
+static void
+init_universal_constants(void)
+{
+  gen_0  = (GEN)readonly_constants;
+  gnil   = (GEN)readonly_constants+2;
+  gen_1  = (GEN)readonly_constants+4;
+  gen_2  = (GEN)readonly_constants+7;
+  gen_m1 = (GEN)readonly_constants+10;
+  gen_m2 = (GEN)readonly_constants+13;
+  ghalf  = (GEN)readonly_ghalf;
+  err_e_STACK = (GEN)readonly_err_STACK;
+}
+
+static const size_t MIN_STACK = 500032;
+static size_t
+fix_size(size_t a)
+{
+  size_t b = a & (~0x3fUL); /* Align */
+  if (b < MIN_STACK) b = MIN_STACK;
+  return b;
+}
+/* old = current stack size (0 = unallocated), size = new size */
+void
+pari_init_stack(size_t size, size_t old)
+{
+  size_t s = fix_size(size);
+  if (old != s) {
+    BLOCK_SIGINT_START;
+    if (old) pari_free((void*)bot);
+    for (;;)
+    {
+      char buf[128];
+      if (s < MIN_STACK) pari_err(e_MEM); /* no way out. Die */
+      bot = (pari_sp)malloc(s); /* NOT pari_malloc, e_MEM would be deadly */
+      if (bot) break;
+      /* must use sprintf: pari stack is currently dead */
+      s = fix_size(s>>1);
+      sprintf(buf, "not enough memory, new stack %lu", (ulong)s);
+      pari_warn(warner, buf, s);
+    }
+    BLOCK_SIGINT_END;
+  }
+  avma = top = bot+s;
+  memused = 0;
+}
+
+static void
+pari_init_errcatch(void)
+{
+  iferr_env = NULL;
+  global_err_data = NULL;
+}
+
+void
+allocatemem(ulong newsize)
+{
+  size_t s, old = top - bot;
+
+  evalstate_reset();
+  if (!newsize) newsize = old << 1;
+  pari_init_stack(newsize, old);
+  s = top - bot;
+  pari_warn(warner,"new stack size = %lu (%.3f Mbytes)", s, s/1048576.);
+  if (cb_pari_pre_recover) cb_pari_pre_recover(-1);
+  pari_init_errcatch();
+  cb_pari_err_recover(-1);
+}
+
+/*********************************************************************/
+/*                           INIT DEFAULTS                           */
+/*********************************************************************/
+void
+pari_init_defaults(void)
+{
+  long i;
+  initout(1);
+
+#ifdef LONG_IS_64BIT
+  precreal = 4;
+#else
+  precreal = 5;
+#endif
+
+  precdl = 16;
+  compatible = NONE;
+  DEBUGFILES = DEBUGLEVEL = DEBUGMEM = 0;
+  disable_color = 1;
+  logstyle = logstyle_none;
+
+  current_psfile = pari_strdup("pari.ps");
+  current_logfile= pari_strdup("pari.log");
+  pari_logfile = NULL;
+
+  pari_datadir = os_getenv("GP_DATA_DIR");
+  if (!pari_datadir)
+  {
+#if defined(_WIN32) || defined(__CYGWIN32__)
+    if (paricfg_datadir[0]=='@' && paricfg_datadir[1]==0)
+      pari_datadir = win32_datadir();
+    else
+#endif
+      pari_datadir = pari_strdup(paricfg_datadir);
+  }
+  else pari_datadir= pari_strdup(pari_datadir);
+  for (i=0; i<c_LAST; i++) gp_colors[i] = c_NONE;
+  pari_colormap = NULL; pari_graphcolors = NULL;
+}
+
+/*********************************************************************/
+/*                   FUNCTION HASHTABLES, MODULES                    */
+/*********************************************************************/
+
+/* Initialize hashtable */
+static void
+init_hashtable(entree **table, long tblsz)
+{
+  long i;
+  for (i = 0; i < tblsz; i++)
+  {
+    entree *last = NULL, *ep = table[i];
+    table[i] = NULL;
+    while (ep)
+    {
+      entree *EP = ep->next;
+      switch(EpVALENCE(ep))
+      {
+        case EpVAR: case EpINSTALL:
+        /* keep: attach it to last entree seen */
+          if (last)
+            last->next = ep;
+          else
+            table[i] = ep;
+          ep->next = NULL; last = ep;
+          break;
+        default: freeep(ep);
+      }
+      ep = EP;
+    }
+  }
+}
+/* Load in hashtable hash the modules contained in A */
+static int
+gp_init_entrees(pari_stack *p_A, entree **hash)
+{
+  long i;
+  entree **v = (entree **)*pari_stack_base(p_A);
+  init_hashtable(hash, functions_tblsz);
+  for (i = 0; i < p_A->n; i++) pari_fill_hashtable(hash, v[i]);
+  return (hash == functions_hash);
+}
+int
+gp_init_functions(void)
+{
+  return gp_init_entrees(new_fun_set? &s_MODULES: &s_OLDMODULES, functions_hash);
+}
+
+extern entree functions_basic[], functions_default[];
+static void
+pari_init_functions(void)
+{
+  pari_stack_init(&s_MODULES, sizeof(*MODULES),(void**)&MODULES);
+  pari_stack_pushp(&s_MODULES,functions_basic);
+  pari_stack_init(&s_OLDMODULES, sizeof(*OLDMODULES),(void**)&OLDMODULES);
+  pari_stack_pushp(&s_OLDMODULES,oldfonctions);
+  functions_hash = (entree**) pari_calloc(sizeof(entree*)*functions_tblsz);
+  pari_fill_hashtable(functions_hash,
+                      new_fun_set? functions_basic: oldfonctions);
+  defaults_hash = (entree**) pari_calloc(sizeof(entree*)*functions_tblsz);
+  pari_add_defaults_module(functions_default);
+}
+
+void
+pari_add_module(entree *ep)
+{
+  if (new_fun_set)
+    pari_fill_hashtable(functions_hash, ep);
+  pari_stack_pushp(&s_MODULES, ep);
+}
+
+void
+pari_add_defaults_module(entree *ep)
+{ pari_fill_hashtable(defaults_hash, ep); }
+
+void
+pari_add_oldmodule(entree *ep)
+{
+  if (!new_fun_set)
+    pari_fill_hashtable(functions_hash, ep);
+  pari_stack_pushp(&s_OLDMODULES, ep);
+}
+
+/*********************************************************************/
+/*                       PARI THREAD                                 */
+/*********************************************************************/
+
+static void
+pari_mainstack_alloc(struct pari_mainstack *st, size_t s)
+{
+  st->bot = (pari_sp)pari_malloc(s);
+  st->avma = st->top = st->bot+s;
+  st->memused = 0;
+}
+
+static void
+pari_mainstack_free(struct pari_mainstack *st)
+{
+  pari_free((void*)st->bot);
+  st->avma = st->top = st->bot = 0;
+}
+
+static void
+pari_mainstack_use(struct pari_mainstack *st)
+{
+  bot = st->bot; top = st->top; avma = st->avma;
+  memused = st->memused;
+}
+
+/* Initial PARI thread structure t with a stack of size s and
+ * argument arg */
+
+void
+pari_thread_alloc(struct pari_thread *t, size_t s, GEN arg)
+{
+  pari_mainstack_alloc(&t->st,s);
+  t->data = arg;
+}
+
+void
+pari_thread_free(struct pari_thread *t)
+{
+  pari_mainstack_free(&t->st);
+}
+
+void
+pari_thread_init(void)
+{
+  pari_init_blocks();
+  pari_init_errcatch();
+  pari_init_rand();
+  pari_init_floats();
+  pari_init_parser();
+  pari_init_compiler();
+  pari_init_evaluator();
+  pari_init_files();
+}
+
+void
+pari_thread_close(void)
+{
+  pari_close_files();
+  pari_close_evaluator();
+  pari_close_compiler();
+  pari_close_parser();
+  pari_close_floats();
+  pari_close_blocks();
+}
+
+GEN
+pari_thread_start(struct pari_thread *t)
+{
+  pari_mainstack_use(&t->st);
+  pari_thread_init();
+  return t->data;
+}
+
+/*********************************************************************/
+/*                       LIBPARI INIT / CLOSE                        */
+/*********************************************************************/
+
+static void
+pari_exit(void)
+{
+  err_printf("  ***   Error in the PARI system. End of program.\n");
+  exit(1);
+}
+
+static void
+dflt_err_recover(long errnum) { (void) errnum; pari_exit(); }
+
+/* initialize PARI data. Initialize [new|old]fun to NULL for default set. */
+void
+pari_init_opts(size_t parisize, ulong maxprime, ulong init_opts)
+{
+  ulong u;
+
+  cb_pari_whatnow = NULL;
+  cb_pari_pre_recover = NULL;
+  cb_pari_sigint = dflt_sigint_fun;
+  if (init_opts&INIT_JMPm) cb_pari_err_recover = dflt_err_recover;
+
+  pari_stackcheck_init(&u);
+  pari_init_homedir();
+  if (init_opts&INIT_DFTm) {
+    pari_init_defaults();
+    GP_DATA = default_gp_data();
+    gp_expand_path(GP_DATA->path);
+  }
+
+  if (init_opts&INIT_SIGm) pari_sig_init(pari_sighandler);
+  pari_init_stack(parisize, 0);
+  init_universal_constants();
+  diffptr = NULL;
+  if (!(init_opts&INIT_noPRIMEm)) initprimetable(maxprime);
+  pari_kernel_init();
+
+  primetab = cgetalloc(t_VEC, 1);
+  varentries = (entree**) pari_calloc((MAXVARN+1)*sizeof(entree*));
+  pari_thread_init();
+  pari_init_seadata();
+  pari_init_functions();
+  pari_var_init();
+  (void)getabstime();
+  try_to_recover = 1;
+  if (!(init_opts&INIT_noIMTm)) pari_mt_init();
+}
+
+void
+pari_init(size_t parisize, ulong maxprime)
+{ pari_init_opts(parisize, maxprime, INIT_JMPm | INIT_SIGm | INIT_DFTm); }
+
+void
+pari_close_opts(ulong init_opts)
+{
+  long i;
+
+  BLOCK_SIGINT_START;
+  if ((init_opts&INIT_SIGm)) pari_sig_init(SIG_DFL);
+  if (!(init_opts&INIT_noIMTm)) pari_mt_close();
+
+  while (delete_var()) /* empty */;
+  for (i = 0; i < functions_tblsz; i++)
+  {
+    entree *ep = functions_hash[i];
+    while (ep) {
+      entree *EP = ep->next;
+      if (!EpSTATIC(ep)) { freeep(ep); free(ep); }
+      ep = EP;
+    }
+  }
+  free((void*)varentries);
+  free((void*)primetab);
+  pari_close_seadata();
+  pari_thread_close();
+  pari_kernel_close();
+
+  free((void*)functions_hash);
+  free((void*)defaults_hash);
+  free((void*)bot);
+  if (diffptr) free((void*)diffptr);
+  free(current_logfile);
+  free(current_psfile);
+  pari_stack_delete(&s_MODULES);
+  pari_stack_delete(&s_OLDMODULES);
+  pari_close_homedir();
+  if (pari_datadir) free(pari_datadir);
+  if (init_opts&INIT_DFTm)
+  { /* delete GP_DATA */
+    if (GP_DATA->hist->v) free((void*)GP_DATA->hist->v);
+    if (GP_DATA->pp->cmd) free((void*)GP_DATA->pp->cmd);
+    delete_dirs(GP_DATA->path);
+    free((void*)GP_DATA->path->PATH);
+  }
+  BLOCK_SIGINT_END;
+}
+
+void
+pari_close(void)
+{ pari_close_opts(INIT_JMPm | INIT_SIGm | INIT_DFTm); }
+
+/*******************************************************************/
+/*                                                                 */
+/*                         ERROR RECOVERY                          */
+/*                                                                 */
+/*******************************************************************/
+void
+gp_context_save(struct gp_context* rec)
+{
+  rec->file = pari_last_tmp_file();
+  if (DEBUGFILES>1)
+    err_printf("gp_context_save: %s\n", rec->file ? rec->file->name: "NULL");
+  rec->prettyp = GP_DATA->fmt->prettyp;
+  rec->listloc = next_block;
+  rec->iferr_env = iferr_env;
+  rec->err_data  = global_err_data;
+  evalstate_save(&rec->eval);
+  parsestate_save(&rec->parse);
+}
+
+void
+gp_context_restore(struct gp_context* rec)
+{
+  long i;
+
+  if (!try_to_recover) return;
+  /* disable gp_context_restore() and SIGINT */
+  try_to_recover = 0;
+  BLOCK_SIGINT_START
+  if (DEBUGMEM>2) err_printf("entering recover(), loc = %ld\n", rec->listloc);
+  evalstate_restore(&rec->eval);
+  parsestate_restore(&rec->parse);
+  filestate_restore(rec->file);
+  global_err_data = rec->err_data;
+  iferr_env = rec->iferr_env;
+  GP_DATA->fmt->prettyp = rec->prettyp;
+
+  for (i = 0; i < functions_tblsz; i++)
+  {
+    entree *ep = functions_hash[i];
+    while (ep)
+    {
+      entree *EP = ep->next;
+      switch(EpVALENCE(ep))
+      {
+        case EpVAR:
+          while (pop_val_if_newer(ep,rec->listloc)) /* empty */;
+          break;
+        case EpNEW: break;
+      }
+      ep = EP;
+    }
+  }
+  if (DEBUGMEM>2) err_printf("leaving recover()\n");
+  BLOCK_SIGINT_END
+  try_to_recover = 1;
+}
+
+static void
+err_recover(long numerr)
+{
+  if (cb_pari_pre_recover)
+    cb_pari_pre_recover(numerr);
+  evalstate_reset();
+  killallfiles();
+  pari_init_errcatch();
+  out_puts(pariErr, "\n");
+  pariErr->flush();
+
+  cb_pari_err_recover(numerr);
+}
+
+static void
+err_init(void)
+{
+  /* make sure pari_err msg starts at the beginning of line */
+  if (!pari_last_was_newline()) pari_putc('\n');
+  pariOut->flush();
+  pariErr->flush();
+  out_term_color(pariErr, c_ERR);
+}
+
+static void
+err_init_msg(int numerr, int user)
+{
+  const char *gp_function_name;
+  out_puts(pariErr, "  *** ");
+  if (numerr != user && (gp_function_name = closure_func_err()))
+    out_printf(pariErr, "%s: ", gp_function_name);
+  else
+    out_puts(pariErr, "  ");
+}
+
+void
+pari_warn(int numerr, ...)
+{
+  char *ch1;
+  va_list ap;
+
+  va_start(ap,numerr);
+
+  err_init();
+  err_init_msg(numerr, warnuser);
+  switch (numerr)
+  {
+    case warnuser:
+      out_puts(pariErr, "user warning: ");
+      out_print0(pariErr, NULL, va_arg(ap, GEN), f_RAW);
+      break;
+
+    case warnmem:
+      out_puts(pariErr, "collecting garbage in "); ch1=va_arg(ap, char*);
+      out_vprintf(pariErr, ch1,ap); out_putc(pariErr, '.');
+      break;
+
+    case warner:
+      out_puts(pariErr, "Warning: "); ch1=va_arg(ap, char*);
+      out_vprintf(pariErr, ch1,ap); out_putc(pariErr, '.');
+      break;
+
+    case warnprec:
+      out_vprintf(pariErr, "Warning: increasing prec in %s; new prec = %ld",
+                      ap);
+      break;
+
+    case warnfile:
+      out_puts(pariErr, "Warning: failed to "),
+      ch1 = va_arg(ap, char*);
+      out_printf(pariErr, "%s: %s", ch1, va_arg(ap, char*));
+      break;
+  }
+  va_end(ap);
+  out_term_color(pariErr, c_NONE);
+  out_putc(pariErr, '\n');
+  pariErr->flush();
+}
+void
+pari_sigint(const char *time_s)
+{
+  int recover=0;
+  BLOCK_SIGALRM_START
+  err_init();
+  closure_err(0);
+  err_init_msg(e_MISC, e_USER);
+  out_puts(pariErr, "user interrupt after ");
+  out_puts(pariErr, time_s);
+  out_term_color(pariErr, c_NONE);
+  pariErr->flush();
+  if (cb_pari_handle_exception)
+    recover = cb_pari_handle_exception(-1);
+  if (!recover && !block)
+    PARI_SIGINT_pending = 0;
+  BLOCK_SIGINT_END
+  if (!recover) err_recover(e_MISC);
+}
+
+#define retmkerr2(x,y)\
+  do { GEN _v = cgetg(3, t_ERROR);\
+       _v[1] = (x);\
+       gel(_v,2) = (y); return _v; } while(0)
+#define retmkerr3(x,y,z)\
+  do { GEN _v = cgetg(4, t_ERROR);\
+       _v[1] = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z); return _v; } while(0)
+#define retmkerr4(x,y,z,t)\
+  do { GEN _v = cgetg(5, t_ERROR);\
+       _v[1] = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t); return _v; } while(0)
+#define retmkerr5(x,y,z,t,u)\
+  do { GEN _v = cgetg(6, t_ERROR);\
+       _v[1] = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t);\
+       gel(_v,5) = (u); return _v; } while(0)
+#define retmkerr6(x,y,z,t,u,v)\
+  do { GEN _v = cgetg(7, t_ERROR);\
+       _v[1] = (x);\
+       gel(_v,2) = (y);\
+       gel(_v,3) = (z);\
+       gel(_v,4) = (t);\
+       gel(_v,5) = (u);\
+       gel(_v,6) = (v); return _v; } while(0)
+
+static GEN
+pari_err2GEN(long numerr, va_list ap)
+{
+  switch ((enum err_list) numerr)
+  {
+  case e_SYNTAX:
+    {
+      const char *msg = va_arg(ap, char*);
+      const char *s = va_arg(ap,char *);
+      const char *entry = va_arg(ap,char *);
+      retmkerr3(numerr,strtoGENstr(msg), mkvecsmall2((long)s,(long)entry));
+    }
+  case e_MISC: case e_ALARM:
+    {
+      const char *ch1 = va_arg(ap, char*);
+      retmkerr2(numerr, gvsprintf(ch1,ap));
+    }
+  case e_NOTFUNC:
+  case e_USER:
+    retmkerr2(numerr,va_arg(ap, GEN));
+  case e_FILE:
+    {
+      const char *f = va_arg(ap, const char*);
+      retmkerr3(numerr, strtoGENstr(f), strtoGENstr(va_arg(ap, char*)));
+    }
+  case e_OVERFLOW:
+  case e_IMPL:
+  case e_DIM:
+  case e_CONSTPOL:
+  case e_ROOTS0:
+  case e_FLAG:
+  case e_PREC:
+  case e_BUG:
+  case e_ARCH:
+  case e_PACKAGE:
+    retmkerr2(numerr, strtoGENstr(va_arg(ap, char*)));
+  case e_MODULUS:
+  case e_VAR:
+    {
+      const char *f = va_arg(ap, const char*);
+      GEN x = va_arg(ap, GEN);
+      GEN y = va_arg(ap, GEN);
+      retmkerr4(numerr, strtoGENstr(f), x,y);
+    }
+  case e_INV:
+  case e_IRREDPOL:
+  case e_PRIME:
+  case e_SQRTN:
+  case e_TYPE:
+    {
+      const char *f = va_arg(ap, const char*);
+      GEN x = va_arg(ap, GEN);
+      retmkerr3(numerr, strtoGENstr(f), x);
+    }
+  case e_COPRIME: case e_OP: case e_TYPE2:
+    {
+      const char *f = va_arg(ap, const char*);
+      GEN x = va_arg(ap, GEN);
+      GEN y = va_arg(ap, GEN);
+      retmkerr4(numerr,strtoGENstr(f),x,y);
+    }
+  case e_COMPONENT:
+    {
+      const char *f= va_arg(ap, const char *);
+      const char *op = va_arg(ap, const char *);
+      GEN l = va_arg(ap, GEN);
+      GEN x = va_arg(ap, GEN);
+      retmkerr5(numerr,strtoGENstr(f),strtoGENstr(op),l,x);
+    }
+  case e_DOMAIN:
+    {
+      const char *f = va_arg(ap, const char*);
+      const char *v = va_arg(ap, const char *);
+      const char *op = va_arg(ap, const char *);
+      GEN l = va_arg(ap, GEN);
+      GEN x = va_arg(ap, GEN);
+      retmkerr6(numerr,strtoGENstr(f),strtoGENstr(v),strtoGENstr(op),l,x);
+    }
+  case e_PRIORITY:
+    {
+      const char *f = va_arg(ap, const char*);
+      GEN x = va_arg(ap, GEN);
+      const char *op = va_arg(ap, const char *);
+      long v = va_arg(ap, long);
+      retmkerr5(numerr,strtoGENstr(f),x,strtoGENstr(op),stoi(v));
+    }
+  case e_MAXPRIME:
+    retmkerr2(numerr, utoi(va_arg(ap, ulong)));
+  case e_STACK:
+    return err_e_STACK;
+  default:
+    return mkerr(numerr);
+  }
+}
+
+static char *
+type_dim(GEN x)
+{
+  char *v = stack_malloc(64);
+  switch(typ(x))
+  {
+    case t_MAT:
+    {
+      long l = lg(x), r = (l == 1)? 1: lgcols(x);
+      sprintf(v, "t_MAT (%ldx%ld)", r-1,l-1);
+      break;
+    }
+    case t_COL:
+      sprintf(v, "t_COL (%ld elts)", lg(x)-1);
+      break;
+    case t_VEC:
+      sprintf(v, "t_VEC (%ld elts)", lg(x)-1);
+      break;
+    default:
+      v = (char*)type_name(typ(x));
+  }
+  return v;
+}
+
+char *
+pari_err2str(GEN e)
+{
+  long numerr = err_get_num(e);
+  switch ((enum err_list) numerr)
+  {
+  case e_ALARM:
+    return pari_sprintf("alarm interrupt after %Ps.",gel(e,2));
+  case e_MISC:
+    return pari_sprintf("%Ps.",gel(e,2));
+
+  case e_ARCH:
+    return pari_sprintf("sorry, '%Ps' not available on this system.",gel(e,2));
+  case e_BUG:
+    return pari_sprintf("bug in %Ps, please report.",gel(e,2));
+  case e_CONSTPOL:
+    return pari_sprintf("constant polynomial in %Ps.", gel(e,2));
+  case e_COPRIME:
+    return pari_sprintf("elements not coprime in %Ps:\n    %Ps\n    %Ps",
+                        gel(e,2), gel(e,3), gel(e,4));
+  case e_DIM:
+    return pari_sprintf("inconsistent dimensions in %Ps.", gel(e,2));
+  case e_FILE:
+    return pari_sprintf("error opening %Ps: `%Ps'.", gel(e,2), gel(e,3));
+  case e_FLAG:
+    return pari_sprintf("invalid flag in %Ps.", gel(e,2));
+  case e_IMPL:
+    return pari_sprintf("sorry, %Ps is not yet implemented.", gel(e,2));
+  case e_PACKAGE:
+    return pari_sprintf("package %Ps is required, please install it.", gel(e,2));
+  case e_INV:
+    return pari_sprintf("impossible inverse in %Ps: %Ps.", gel(e,2), gel(e,3));
+  case e_IRREDPOL:
+    return pari_sprintf("not an irreducible polynomial in %Ps: %Ps.",
+                        gel(e,2), gel(e,3));
+  case e_MAXPRIME:
+    {
+      const char * msg = "not enough precomputed primes";
+      ulong c = itou(gel(e,2));
+      if (c) return pari_sprintf("%s, need primelimit ~ %lu.",msg, c);
+      else   return pari_strdup(msg);
+    }
+  case e_MEM:
+    return pari_strdup("not enough memory");
+  case e_MODULUS:
+    {
+      GEN x = gel(e,3), y = gel(e,4);
+      return pari_sprintf("inconsistent moduli in %Ps: %Ps != %Ps",
+                          gel(e,2), x, y);
+    }
+  case e_NONE: return NULL;
+  case e_NOTFUNC:
+    return pari_strdup("not a function in function call");
+  case e_OP: case e_TYPE2:
+    {
+      pari_sp av = avma;
+      char *v;
+      const char *f, *op = GSTR(gel(e,2));
+      const char *what = numerr == e_OP? "inconsistent": "forbidden";
+      GEN x = gel(e,3);
+      GEN y = gel(e,4);
+      switch(*op)
+      {
+      case '+': f = "addition"; break;
+      case '*': f = "multiplication"; break;
+      case '/': case '%': case '\\': f = "division"; break;
+      case '=': op = "-->"; f = "assignment"; break;
+      default:  f = op; op = ","; break;
+      }
+      v = pari_sprintf("%s %s %s %s %s.", what,f,type_dim(x),op,type_dim(y));
+      avma = av; return v;
+    }
+  case e_COMPONENT:
+    {
+      const char *f= GSTR(gel(e,2));
+      const char *op= GSTR(gel(e,3));
+      GEN l = gel(e,4);
+      if (!*f)
+        return pari_sprintf("non-existent component: index %s %Ps",op,l);
+      return pari_sprintf("non-existent component in %s: index %s %Ps",f,op,l);
+    }
+  case e_DOMAIN:
+    {
+      const char *f = GSTR(gel(e,2));
+      const char *v = GSTR(gel(e,3));
+      const char *op= GSTR(gel(e,4));
+      GEN l = gel(e,5);
+      if (!*op)
+        return pari_sprintf("domain error in %s: %s out of range",f,v);
+      return pari_sprintf("domain error in %s: %s %s %Ps",f,v,op,l);
+    }
+  case e_PRIORITY:
+    {
+      const char *f = GSTR(gel(e,2));
+      long vx = gvar(gel(e,3));
+      const char *op= GSTR(gel(e,4));
+      long v = itos(gel(e,5));
+      return pari_sprintf("incorrect priority in %s: variable %Ps %s %Ps",f,
+             pol_x(vx), op, pol_x(v));
+    }
+  case e_OVERFLOW:
+    return pari_sprintf("overflow in %Ps.", gel(e,2));
+  case e_PREC:
+    return pari_sprintf("precision too low in %Ps.", gel(e,2));
+  case e_PRIME:
+    return pari_sprintf("not a prime number in %Ps: %Ps.",
+                        gel(e,2), gel(e,3));
+  case e_ROOTS0:
+    return pari_sprintf("zero polynomial in %Ps.", gel(e,2));
+  case e_SQRTN:
+    return pari_sprintf("not an n-th power residue in %Ps: %Ps.",
+                        gel(e,2), gel(e,3));
+  case e_STACK:
+    {
+      size_t d = top - bot;
+      char *buf = (char *) pari_malloc(512*sizeof(char));
+      sprintf(buf, "the PARI stack overflows !\n"
+          "  current stack size: %lu (%.3f Mbytes)\n"
+          "  [hint] you can increase GP stack with allocatemem()\n",
+          (ulong)d, (double)d/1048576.);
+      return buf;
+    }
+  case e_SYNTAX:
+    return pari_strdup(GSTR(gel(e,2)));
+  case e_TYPE:
+    return pari_sprintf("incorrect type in %Ps (%s).",
+                        gel(e,2), type_name(typ(gel(e,3))));
+  case e_USER:
+    return pari_sprint0("user error: ", gel(e,2), f_RAW);
+  case e_VAR:
+    {
+      GEN x = gel(e,3), y = gel(e,4);
+      return pari_sprintf("inconsistent variables in %Ps, %Ps != %Ps.",
+                          gel(e,2), pol_x(varn(x)), pol_x(varn(y)));
+    }
+  }
+  return NULL; /*NOT REACHED*/
+}
+
+static void
+pari_err_display(GEN err)
+{
+  long numerr=err_get_num(err);
+  if (numerr==e_SYNTAX)
+  {
+    const char *msg = GSTR(gel(err,2));
+    const char *s     = (const char *) gmael(err,3,1);
+    const char *entry = (const char *) gmael(err,3,2);
+    print_errcontext(pariErr, msg, s, entry);
+    return;
+  }
+  else
+  {
+    char *s = pari_err2str(err);
+    err_init_msg(numerr, e_USER);
+    pariErr->puts(s);
+    if (numerr==e_NOTFUNC)
+    {
+      GEN fun = gel(err,2);
+      if (gequalX(fun))
+      {
+        entree *ep = varentries[varn(fun)];
+        const char *s = ep->name;
+        if (cb_pari_whatnow) cb_pari_whatnow(pariErr,s,1);
+      }
+    }
+    pari_free(s);
+  }
+}
+
+void
+pari_err(int numerr, ...)
+{
+  va_list ap;
+  GEN E;
+
+  va_start(ap,numerr);
+
+  if (numerr)
+    E = pari_err2GEN(numerr,ap);
+  else
+  {
+    E = va_arg(ap,GEN);
+    numerr = err_get_num(E);
+  }
+  global_err_data = E;
+  if (*iferr_env) longjmp(*iferr_env, numerr);
+  mt_err_recover(numerr);
+  err_init();
+  if (numerr != e_SYNTAX) closure_err(0);
+  pari_err_display(E);
+  out_term_color(pariErr, c_NONE);
+  va_end(ap);
+  pariErr->flush();
+  if (cb_pari_handle_exception &&
+      cb_pari_handle_exception(numerr)) return;
+  err_recover(numerr);
+}
+
+GEN
+pari_err_last(void) { return global_err_data; }
+
+const char *
+numerr_name(long numerr)
+{
+  switch ((enum err_list) numerr)
+  {
+  case e_ALARM:    return "e_ALARM";
+  case e_ARCH:     return "e_ARCH";
+  case e_BUG:      return "e_BUG";
+  case e_COMPONENT: return "e_COMPONENT";
+  case e_CONSTPOL: return "e_CONSTPOL";
+  case e_COPRIME:  return "e_COPRIME";
+  case e_DIM:      return "e_DIM";
+  case e_DOMAIN:   return "e_DOMAIN";
+  case e_FILE:     return "e_FILE";
+  case e_FLAG:     return "e_FLAG";
+  case e_IMPL:     return "e_IMPL";
+  case e_INV:      return "e_INV";
+  case e_IRREDPOL: return "e_IRREDPOL";
+  case e_MAXPRIME: return "e_MAXPRIME";
+  case e_MEM:      return "e_MEM";
+  case e_MISC:     return "e_MISC";
+  case e_MODULUS:  return "e_MODULUS";
+  case e_NONE:     return "e_NONE";
+  case e_NOTFUNC:  return "e_NOTFUNC";
+  case e_OP:       return "e_OP";
+  case e_OVERFLOW: return "e_OVERFLOW";
+  case e_PACKAGE:  return "e_PACKAGE";
+  case e_PREC:     return "e_PREC";
+  case e_PRIME:    return "e_PRIME";
+  case e_PRIORITY: return "e_PRIORITY";
+  case e_ROOTS0:   return "e_ROOTS0";
+  case e_SQRTN:    return "e_SQRTN";
+  case e_STACK:    return "e_STACK";
+  case e_SYNTAX:   return "e_SYNTAX";
+  case e_TYPE2:    return "e_TYPE2";
+  case e_TYPE:     return "e_TYPE";
+  case e_USER:     return "e_USER";
+  case e_VAR:      return "e_VAR";
+  }
+  return "invalid error number";
+}
+
+long
+name_numerr(const char *s)
+{
+  if (!strcmp(s,"e_ALARM"))    return e_ALARM;
+  if (!strcmp(s,"e_ARCH"))     return e_ARCH;
+  if (!strcmp(s,"e_BUG"))      return e_BUG;
+  if (!strcmp(s,"e_COMPONENT")) return e_COMPONENT;
+  if (!strcmp(s,"e_CONSTPOL")) return e_CONSTPOL;
+  if (!strcmp(s,"e_COPRIME"))  return e_COPRIME;
+  if (!strcmp(s,"e_DIM"))      return e_DIM;
+  if (!strcmp(s,"e_DOMAIN"))   return e_DOMAIN;
+  if (!strcmp(s,"e_FILE"))     return e_FILE;
+  if (!strcmp(s,"e_FLAG"))     return e_FLAG;
+  if (!strcmp(s,"e_IMPL"))     return e_IMPL;
+  if (!strcmp(s,"e_INV"))      return e_INV;
+  if (!strcmp(s,"e_IRREDPOL")) return e_IRREDPOL;
+  if (!strcmp(s,"e_MAXPRIME")) return e_MAXPRIME;
+  if (!strcmp(s,"e_MEM"))      return e_MEM;
+  if (!strcmp(s,"e_MISC"))     return e_MISC;
+  if (!strcmp(s,"e_MODULUS"))  return e_MODULUS;
+  if (!strcmp(s,"e_NONE"))     return e_NONE;
+  if (!strcmp(s,"e_NOTFUNC"))  return e_NOTFUNC;
+  if (!strcmp(s,"e_OP"))       return e_OP;
+  if (!strcmp(s,"e_OVERFLOW")) return e_OVERFLOW;
+  if (!strcmp(s,"e_PACKAGE"))  return e_PACKAGE;
+  if (!strcmp(s,"e_PREC"))     return e_PREC;
+  if (!strcmp(s,"e_PRIME"))    return e_PRIME;
+  if (!strcmp(s,"e_PRIORITY")) return e_PRIORITY;
+  if (!strcmp(s,"e_ROOTS0"))   return e_ROOTS0;
+  if (!strcmp(s,"e_SQRTN"))    return e_SQRTN;
+  if (!strcmp(s,"e_STACK"))    return e_STACK;
+  if (!strcmp(s,"e_SYNTAX"))   return e_SYNTAX;
+  if (!strcmp(s,"e_TYPE"))     return e_TYPE;
+  if (!strcmp(s,"e_TYPE2"))    return e_TYPE2;
+  if (!strcmp(s,"e_USER"))     return e_USER;
+  if (!strcmp(s,"e_VAR"))      return e_VAR;
+  pari_err(e_MISC,"unknown error name");
+  return -1; /* NOT REACHED */
+}
+
+GEN
+errname(GEN err)
+{
+  if (typ(err)!=t_ERROR) pari_err_TYPE("errname",err);
+  return strtoGENstr(numerr_name(err_get_num(err)));
+}
+
+/* Try f (trapping error e), recover using r (break_loop, if NULL) */
+GEN
+trap0(const char *e, GEN r, GEN f)
+{
+  long numerr = CATCH_ALL;
+  GEN x;
+  if (!e || !*e) numerr = CATCH_ALL;
+  else numerr = name_numerr(e);
+  if (!f) {
+    pari_warn(warner,"default handlers are no longer supported --> ignored");
+    return gnil;
+  }
+  x = closure_trapgen(f, numerr);
+  if (x == (GEN)1L) x = r? closure_evalgen(r): gnil;
+  return x;
+}
+
+/*******************************************************************/
+/*                                                                */
+/*                       CLONING & COPY                            */
+/*                  Replicate an existing GEN                      */
+/*                                                                 */
+/*******************************************************************/
+/* lontyp[tx] = 0 (non recursive type) or number of codewords for type tx */
+const  long lontyp[] = { 0,0,0,1,1,2,1,2,1,1, 2,2,0,1,1,1,1,1,1,1, 2,0,0,2,2 };
+
+static GEN
+list_internal_copy(GEN z, long nmax)
+{
+  long i, l;
+  GEN a;
+  if (!z) return NULL;
+  l = lg(z);
+  a = (GEN)pari_malloc((nmax+1) * sizeof(long));
+  for (i = 1; i < l; i++) gel(a,i) = gclone( gel(z,i) );
+  a[0] = z[0]; return a;
+}
+
+static void
+listassign(GEN x, GEN y)
+{
+  long nmax = list_nmax(x);
+  GEN L = list_data(x);
+  if (!nmax && L) nmax = lg(L) + 32; /* not malloc'ed yet */
+  list_nmax(y) = nmax;
+  list_data(y) = list_internal_copy(L, nmax);
+}
+
+/* copy list on the PARI stack */
+GEN
+listcopy(GEN x)
+{
+  GEN y = listcreate(), L = list_data(x);
+  if (L) list_data(y) = gcopy(L);
+  return y;
+}
+
+GEN
+gcopy(GEN x)
+{
+  long tx = typ(x), lx, i;
+  GEN y;
+  switch(tx)
+  { /* non recursive types */
+    case t_INT: return signe(x)? icopy(x): gen_0;
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL: return leafcopy(x);
+    /* one more special case */
+    case t_LIST: return listcopy(x);
+  }
+  y = cgetg_copy(x, &lx);
+  if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+  for (; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+  return y;
+}
+
+/* as gcopy, but truncate to the first lx components if recursive type
+ * [ leaves use their own lg ]. No checks. */
+GEN
+gcopy_lg(GEN x, long lx)
+{
+  long tx = typ(x), i;
+  GEN y;
+  switch(tx)
+  { /* non recursive types */
+    case t_INT: return signe(x)? icopy(x): gen_0;
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL: return leafcopy(x);
+    /* one more special case */
+    case t_LIST: return listcopy(x);
+  }
+  y = cgetg(lx, tx);
+  if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+  for (; i<lx; i++) gel(y,i) = gcopy(gel(x,i));
+  return y;
+}
+
+/* cf cgetg_copy: "allocate" (by updating first codeword only) for subsequent
+ * copy of x, as if avma = *AVMA */
+INLINE GEN
+cgetg_copy_avma(GEN x, long *plx, pari_sp *AVMA) {
+  GEN z;
+  *plx = lg(x);
+  z = ((GEN)*AVMA) - *plx;
+  z[0] = x[0] & (TYPBITS|LGBITS);
+  *AVMA = (pari_sp)z; return z;
+}
+INLINE GEN
+cgetlist_avma(pari_sp *AVMA)
+{
+  GEN y = ((GEN)*AVMA) - 3;
+  y[0] = _evallg(3) | evaltyp(t_LIST);
+  *AVMA = (pari_sp)y; return y;
+}
+
+/* copy x as if avma = *AVMA, update *AVMA */
+GEN
+gcopy_avma(GEN x, pari_sp *AVMA)
+{
+  long i, lx, tx = typ(x);
+  GEN y;
+
+  switch(typ(x))
+  { /* non recursive types */
+    case t_INT:
+      *AVMA = (pari_sp)icopy_avma(x, *AVMA);
+      return (GEN)*AVMA;
+    case t_REAL: case t_STR: case t_VECSMALL:
+      *AVMA = (pari_sp)leafcopy_avma(x, *AVMA);
+      return (GEN)*AVMA;
+
+    /* one more special case */
+    case t_LIST:
+      y = cgetlist_avma(AVMA);
+      listassign(x, y); return y;
+
+    default:
+      y = cgetg_copy_avma(x, &lx, AVMA);
+      if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+      for (; i<lx; i++) gel(y,i) = gcopy_avma(gel(x,i), AVMA);
+  }
+  return y;
+}
+
+/* [copy_bin/bin_copy:] same as gcopy_avma but use NULL to code an exact 0, and
+ * make shallow copies of t_LISTs */
+static GEN
+gcopy_av0(GEN x, pari_sp *AVMA)
+{
+  long i, lx, tx = typ(x);
+  GEN y;
+
+  switch(tx)
+  { /* non recursive types */
+    case t_INT:
+      if (!signe(x)) return NULL; /* special marker */
+      *AVMA = (pari_sp)icopy_avma(x, *AVMA);
+      return (GEN)*AVMA;
+    case t_REAL: case t_STR: case t_VECSMALL: case t_LIST:
+      *AVMA = (pari_sp)leafcopy_avma(x, *AVMA);
+      return (GEN)*AVMA;
+    default:
+      y = cgetg_copy_avma(x, &lx, AVMA);
+      if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+      for (; i<lx; i++) gel(y,i) = gcopy_av0(gel(x,i), AVMA);
+  }
+  return y;
+}
+
+INLINE GEN
+icopy_avma_canon(GEN x, pari_sp AVMA)
+{
+  long i, lx = lgefint(x);
+  GEN y = ((GEN)AVMA) - lx;
+  y[0] = evaltyp(t_INT)|evallg(lx); /* kills isclone */
+  y[1] = x[1]; x = int_MSW(x);
+  for (i=2; i<lx; i++, x = int_precW(x)) y[i] = *x;
+  return y;
+}
+
+/* [copy_bin_canon/bin_copy_canon:] same as gcopy_av0, but copy integers in
+ * canonical (native kernel) form and make a full copy of t_LISTs */
+static GEN
+gcopy_av0_canon(GEN x, pari_sp *AVMA)
+{
+  long i, lx, tx = typ(x);
+  GEN y;
+
+  switch(tx)
+  { /* non recursive types */
+    case t_INT:
+      if (!signe(x)) return NULL; /* special marker */
+      *AVMA = (pari_sp)icopy_avma_canon(x, *AVMA);
+      return (GEN)*AVMA;
+    case t_REAL: case t_STR: case t_VECSMALL:
+      *AVMA = (pari_sp)leafcopy_avma(x, *AVMA);
+      return (GEN)*AVMA;
+
+    /* one more special case */
+    case t_LIST:
+    {
+      GEN y = cgetlist_avma(AVMA), z = list_data(x);
+      if (z) {
+        list_data(y) = gcopy_av0_canon(z, AVMA);
+        list_nmax(y) = lg(z)-1;
+      } else {
+        list_data(y) = NULL;
+        list_nmax(y) = 0;
+      }
+      return y;
+    }
+    default:
+      y = cgetg_copy_avma(x, &lx, AVMA);
+      if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+      for (; i<lx; i++) gel(y,i) = gcopy_av0_canon(gel(x,i), AVMA);
+  }
+return y;
+}
+
+/* [copy_bin/bin_copy:] size (number of words) required for gcopy_av0(x) */
+static long
+taille0(GEN x)
+{
+  long i,n,lx, tx = typ(x);
+  switch(tx)
+  { /* non recursive types */
+    case t_INT: return signe(x)? lgefint(x): 0;
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL: return lg(x);
+
+    /* one more special case */
+    case t_LIST:
+    {
+      GEN L = list_data(x);
+      return L? 3 + taille0(L): 3;
+    }
+    default:
+      n = lx = lg(x);
+      for (i=lontyp[tx]; i<lx; i++) n += taille0(gel(x,i));
+      return n;
+  }
+}
+
+/* [copy_bin/bin_copy:] size (number of words) required for gcopy_av0(x) */
+static long
+taille0_nolist(GEN x)
+{
+  long i,n,lx, tx = typ(x);
+  switch(tx)
+  { /* non recursive types */
+    case t_INT:
+      lx = lgefint(x);
+      return lx == 2? 0: lx;
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL:
+    case t_LIST:
+      return lg(x);
+    default:
+      n = lx = lg(x);
+      for (i=lontyp[tx]; i<lx; i++) n += taille0_nolist(gel(x,i));
+      return n;
+  }
+}
+
+/* How many words do we need to allocate to copy x ? t_LIST is a special case
+ * since list_data() is malloc'ed later, in list_internal_copy() */
+static long
+words_to_allocate(GEN x)
+{
+  long i,n,lx, tx = typ(x);
+  switch(tx)
+  { /* non recursive types */
+    case t_INT: return lgefint(x);
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL: return lg(x);
+
+    case t_LIST: return 3;
+    default:
+      n = lx = lg(x);
+      for (i=lontyp[tx]; i<lx; i++) n += words_to_allocate(gel(x,i));
+      return n;
+  }
+}
+
+long
+gsizeword(GEN x)
+{
+  GEN L;
+  if (typ(x) != t_LIST) return words_to_allocate(x);
+  /* For t_LIST, return the actual list size, words_to_allocate() is always 3 */
+  L = list_data(x);
+  return L? 3 + words_to_allocate(L): 3;
+}
+long
+gsizebyte(GEN x) { return gsizeword(x) * sizeof(long); }
+
+/* return a clone of x structured as a gcopy */
+GENbin*
+copy_bin(GEN x)
+{
+  long t = taille0_nolist(x);
+  GENbin *p = (GENbin*)pari_malloc(sizeof(GENbin) + t*sizeof(long));
+  pari_sp AVMA = (pari_sp)(GENbinbase(p) + t);
+  p->canon = 0;
+  p->len = t;
+  p->x   = gcopy_av0(x, &AVMA);
+  p->base= (GEN)AVMA; return p;
+}
+
+/* same, writing t_INT in canonical native form */
+GENbin*
+copy_bin_canon(GEN x)
+{
+  long t = taille0(x);
+  GENbin *p = (GENbin*)pari_malloc(sizeof(GENbin) + t*sizeof(long));
+  pari_sp AVMA = (pari_sp)(GENbinbase(p) + t);
+  p->canon = 1;
+  p->len = t;
+  p->x   = gcopy_av0_canon(x, &AVMA);
+  p->base= (GEN)AVMA; return p;
+}
+
+GEN
+gclone(GEN x)
+{
+  long i,lx,tx = typ(x), t = words_to_allocate(x);
+  GEN y = newblock(t);
+  switch(tx)
+  { /* non recursive types */
+    case t_INT:
+      lx = lgefint(x);
+      y[0] = evaltyp(t_INT)|evallg(lx);
+      for (i=1; i<lx; i++) y[i] = x[i];
+      break;
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL:
+      lx = lg(x);
+      for (i=0; i<lx; i++) y[i] = x[i];
+      break;
+
+    /* one more special case */
+    case t_LIST:
+      y[0] = evaltyp(t_LIST)|_evallg(3);
+      listassign(x, y);
+      break;
+    default: {
+      pari_sp AVMA = (pari_sp)(y + t);
+      lx = lg(x);
+      y[0] = x[0];
+      if (lontyp[tx] == 1) i = 1; else { y[1] = x[1]; i = 2; }
+      for (; i<lx; i++) gel(y,i) = gcopy_avma(gel(x,i), &AVMA);
+    }
+  }
+  setisclone(y); return y;
+}
+
+void
+shiftaddress(GEN x, long dec)
+{
+  long i, lx, tx = typ(x);
+  if (is_recursive_t(tx) && tx != t_LIST)
+  {
+    lx = lg(x);
+    for (i=lontyp[tx]; i<lx; i++) {
+      if (!x[i]) gel(x,i) = gen_0;
+      else
+      {
+        x[i] += dec;
+        shiftaddress(gel(x,i), dec);
+      }
+    }
+  }
+}
+
+void
+shiftaddress_canon(GEN x, long dec)
+{
+  long i, lx, tx = typ(x);
+  switch(tx)
+  { /* non recursive types */
+    case t_INT: {
+      GEN y;
+      lx = lgefint(x); if (lx <= 3) return;
+      y = x + 2;
+      x = int_MSW(x);  if (x == y) return;
+      while (x > y) { lswap(*x, *y); x = int_precW(x); y++; }
+      break;
+    }
+    case t_REAL:
+    case t_STR:
+    case t_VECSMALL:
+      break;
+
+    /* one more special case */
+    case t_LIST: {
+      GEN Lx = list_data(x);
+      if (Lx) {
+        pari_sp av = avma;
+        GEN L = (GEN)((long)Lx+dec);
+        shiftaddress_canon(L, dec);
+        list_data(x) = list_internal_copy(L, lg(L)); avma = av;
+      }
+    }
+    default:
+      lx = lg(x);
+      for (i=lontyp[tx]; i<lx; i++) {
+        if (!x[i]) gel(x,i) = gen_0;
+        else
+        {
+          x[i] += dec;
+          shiftaddress_canon(gel(x,i), dec);
+        }
+      }
+  }
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                INSERT DYNAMIC OBJECT IN STRUCTURE              **/
+/**                                                                **/
+/********************************************************************/
+GEN
+obj_init(long d, long n)
+{
+  GEN S = cgetg(d+2, t_VEC);
+  gel(S, d+1) = zerovec(n);
+  return S;
+}
+/* insert O in S [last position] at position K, return it */
+GEN
+obj_insert(GEN S, long K, GEN O)
+{ return obj_insert_shallow(S, K, gclone(O)); }
+/* as obj_insert. WITHOUT cloning (for libpari, when creating a *new* obj
+ * from an existing one) */
+GEN
+obj_insert_shallow(GEN S, long K, GEN O)
+{
+  GEN o, v = gel(S, lg(S)-1);
+  if (typ(v) != t_VEC) pari_err_TYPE("obj_insert", S);
+  o = gel(v,K);
+  gel(v,K) = O; /*SIGINT: before unclone(o)*/
+  if (isclone(o)) gunclone(o);
+  return gel(v,K);
+}
+
+/* Does S [last position] contain data at position K ? Return it, or NULL */
+GEN
+obj_check(GEN S, long K)
+{
+  GEN O, v = gel(S, lg(S)-1);
+  if (typ(v) != t_VEC || K >= lg(v)) pari_err_TYPE("obj_check", S);
+  O = gel(v,K); return isintzero(O)? NULL: O;
+}
+
+GEN
+obj_checkbuild(GEN S, long tag, GEN (*build)(GEN))
+{
+  GEN O = obj_check(S, tag);
+  if (!O)
+  { pari_sp av = avma; O = obj_insert(S, tag, build(S)); avma = av; }
+  return O;
+}
+
+GEN
+obj_checkbuild_prec(GEN S, long tag, GEN (*build)(GEN,long), long prec)
+{
+  pari_sp av = avma;
+  GEN w = obj_check(S, tag);
+  if (w)
+  {
+    long p = gprecision(w);
+    if (p >= prec) return gprec_w(w, prec);
+  }
+  w = obj_insert(S, tag, build(S, prec));
+  avma = av; return gcopy(w);
+}
+
+GEN
+obj_checkbuild_padicprec(GEN S, long tag, GEN (*build)(GEN,long), long prec)
+{
+  pari_sp av = avma;
+  GEN w = obj_check(S, tag);
+  if (w)
+  {
+    long p = padicprec_relative(w);
+    if (p >= prec) return gprec_w(w, prec);
+  }
+  w = obj_insert(S, tag, build(S, prec));
+  avma = av; return gcopy(w);
+}
+
+/* Reset S [last position], freeing all clones */
+void
+obj_free(GEN S)
+{
+  GEN v = gel(S, lg(S)-1);
+  long i;
+  if (typ(v) != t_VEC) pari_err_TYPE("obj_free", S);
+  for (i = 1; i < lg(v); i++)
+  {
+    GEN o = gel(v,i);
+    if (isclone(o)) gunclone(o);
+    gel(v,i) = gen_0;
+  }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*                         STACK MANAGEMENT                        */
+/*                                                                 */
+/*******************************************************************/
+INLINE void
+dec_gerepile(pari_sp *x, pari_sp av0, pari_sp av, pari_sp tetpil, size_t dec)
+{
+  if (*x < av && *x >= av0)
+  { /* update address if in stack */
+    if (*x < tetpil) *x += dec;
+    else pari_err_BUG("gerepile, significant pointers lost");
+  }
+}
+
+void
+gerepileallsp(pari_sp av, pari_sp tetpil, int n, ...)
+{
+  const pari_sp av0 = avma;
+  const size_t dec = av-tetpil;
+  int i;
+  va_list a; va_start(a, n);
+  (void)gerepile(av,tetpil,NULL);
+  for (i=0; i<n; i++) dec_gerepile((pari_sp*)va_arg(a,GEN*), av0,av,tetpil,dec);
+}
+
+/* Takes an array of pointers to GENs, of length n.
+ * Cleans up the stack between av and tetpil, updating those GENs. */
+void
+gerepilemanysp(pari_sp av, pari_sp tetpil, GEN* gptr[], int n)
+{
+  const pari_sp av0 = avma;
+  const size_t dec = av-tetpil;
+  int i;
+  (void)gerepile(av,tetpil,NULL);
+  for (i=0; i<n; i++) dec_gerepile((pari_sp*)gptr[i], av0, av, tetpil, dec);
+}
+
+/* Takes an array of GENs (cast to longs), of length n.
+ * Cleans up the stack between av and tetpil, updating those GENs. */
+void
+gerepilecoeffssp(pari_sp av, pari_sp tetpil, long *g, int n)
+{
+  const pari_sp av0 = avma;
+  const size_t dec = av-tetpil;
+  int i;
+  (void)gerepile(av,tetpil,NULL);
+  for (i=0; i<n; i++,g++) dec_gerepile((pari_sp*)g, av0, av, tetpil, dec);
+}
+
+static int
+dochk_gerepileupto(GEN av, GEN x)
+{
+  long i,lx,tx;
+  if (!isonstack(x)) return 1;
+  if (x > av)
+  {
+    pari_warn(warner,"bad object %Ps",x);
+    return 0;
+  }
+  tx = typ(x);
+  if (! is_recursive_t(tx)) return 1;
+
+  lx = lg(x);
+  for (i=lontyp[tx]; i<lx; i++)
+    if (!dochk_gerepileupto(av, gel(x,i)))
+    {
+      pari_warn(warner,"bad component %ld in object %Ps",i,x);
+      return 0;
+    }
+  return 1;
+}
+/* check that x and all its components are out of stack, or have been
+ * created after av */
+int
+chk_gerepileupto(GEN x) { return dochk_gerepileupto(x, x); }
+
+/* print stack between avma & av */
+void
+dbg_gerepile(pari_sp av)
+{
+  GEN x = (GEN)avma;
+  while (x < (GEN)av)
+  {
+    const long tx = typ(x), lx = lg(x);
+    GEN *a;
+
+    pari_printf(" [%ld] %Ps:", x - (GEN)avma, x);
+    if (! is_recursive_t(tx)) { pari_putc('\n'); x += lx; continue; }
+    a = (GEN*)x + lontyp[tx]; x += lx;
+    for (  ; a < (GEN*)x; a++)
+    {
+      if (*a == gen_0)
+        pari_puts("  gen_0");
+      else if (*a == gen_1)
+        pari_puts("  gen_1");
+      else if (*a == gen_m1)
+        pari_puts("  gen_m1");
+      else if (*a == gen_2)
+        pari_puts("  gen_2");
+      else if (*a == gen_m2)
+        pari_puts("  gen_m2");
+      else if (*a == ghalf)
+        pari_puts("  ghalf");
+      else if (isclone(*a))
+        pari_printf("  %Ps (clone)", *a);
+      else
+        pari_printf("  %Ps [%ld]", *a, *a - (GEN)avma);
+      if (a+1 < (GEN*)x) pari_putc(',');
+    }
+    pari_printf("\n");
+  }
+}
+void
+dbg_gerepileupto(GEN q)
+{
+  err_printf("%Ps:\n", q);
+  dbg_gerepile((pari_sp) (q+lg(q)));
+}
+
+GEN
+gerepile(pari_sp av, pari_sp tetpil, GEN q)
+{
+  const size_t dec = av - tetpil;
+  const pari_sp av0 = avma;
+  GEN x, a;
+
+  if (dec == 0) return q;
+  if ((long)dec < 0) pari_err(e_MISC,"lbot>ltop in gerepile");
+
+  /* dec_gerepile(&q, av0, av, tetpil, dec), saving 1 comparison */
+  if (q >= (GEN)av0 && q < (GEN)tetpil)
+    q = (GEN) (((pari_sp)q) + dec);
+
+  for (x = (GEN)av, a = (GEN)tetpil; a > (GEN)av0; ) *--x = *--a;
+  avma = (pari_sp)x;
+  while (x < (GEN)av)
+  {
+    const long tx = typ(x), lx = lg(x);
+
+    if (! is_recursive_t(tx)) { x += lx; continue; }
+    a = x + lontyp[tx]; x += lx;
+    for (  ; a < x; a++) dec_gerepile((pari_sp*)a, av0, av, tetpil, dec);
+  }
+  return q;
+}
+
+void
+fill_stack(void)
+{
+  GEN x = ((GEN)bot);
+  while (x < (GEN)avma) *x++ = 0xfefefefeUL;
+}
+
+void
+debug_stack(void)
+{
+  GEN z;
+  err_printf("bot=0x%lx\ttop=0x%lx\tavma=0x%lx\n", bot, top, avma);
+  for (z = ((GEN)top)-1; z >= (GEN)avma; z--)
+    err_printf("%p:\t0x%lx\t%lu\n",z,*z,*z);
+}
+
+void
+setdebugvar(long n) { DEBUGVAR=n; }
+
+long
+getstack(void) { return top-avma; }
+
+/*******************************************************************/
+/*                                                                 */
+/*                               timer_delay                             */
+/*                                                                 */
+/*******************************************************************/
+
+#if defined(USE_CLOCK_GETTIME)
+#include <time.h>
+void
+timer_start(pari_timer *T)
+{
+  struct timespec t;
+  clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);
+  T->us = t.tv_nsec / 1000;
+  T->s  = t.tv_sec;
+}
+#elif defined(USE_GETRUSAGE)
+# include <sys/time.h>
+# include <sys/resource.h>
+void
+timer_start(pari_timer *T)
+{
+  struct rusage r;
+  getrusage(RUSAGE_SELF,&r);
+  T->us = r.ru_utime.tv_usec;
+  T->s  = r.ru_utime.tv_sec;
+}
+#elif defined(USE_FTIME)
+
+# include <sys/timeb.h>
+void
+timer_start(pari_timer *T)
+{
+  struct timeb t;
+  ftime(&t);
+  T->us = ((long)t.millitm) * 1000;
+  T->s  = t.time;
+}
+
+#else
+
+static void
+_get_time(pari_timer *T, long Ticks, long TickPerSecond)
+{
+  T->us = (long) ((Ticks % TickPerSecond) * (1000000. / TickPerSecond));
+  T->s  = Ticks / TickPerSecond;
+}
+
+# ifdef USE_TIMES
+#  include <sys/times.h>
+#  include <sys/time.h>
+#  include <time.h>
+void
+timer_start(pari_timer *T)
+{
+# ifdef _SC_CLK_TCK
+  long tck = sysconf(_SC_CLK_TCK);
+# else
+  long tck = CLK_TCK;
+# endif
+  struct tms t; times(&t);
+  _get_time(T, t.tms_utime, tck);
+}
+# elif defined(WINCE)
+void
+timer_start(pari_timer *T)
+{ _get_time(T, GetTickCount(), 1000); }
+# else
+#  include <time.h>
+#  ifndef CLOCKS_PER_SEC
+#   define CLOCKS_PER_SEC 1000000 /* may be false on YOUR system */
+#  endif
+void
+timer_start(pari_timer *T)
+{ _get_time(T, clock(), CLOCKS_PER_SEC); }
+# endif
+#endif
+
+static long
+timer_aux(pari_timer *T, pari_timer *U)
+{
+  long s = T->s, us = T->us; timer_start(U);
+  return 1000 * (U->s - s) + (U->us - us + 500) / 1000;
+}
+/* return delay, reset timer */
+long
+timer_delay(pari_timer *T) { return timer_aux(T, T); }
+/* return delay, don't reset timer */
+long
+timer_get(pari_timer *T) { pari_timer t; return timer_aux(T, &t); }
+
+static void
+timer_vprintf(pari_timer *T, const char *format, va_list args)
+{
+  out_puts(pariErr, "Time ");
+  out_vprintf(pariErr, format,args);
+  out_printf(pariErr, ": %ld\n", timer_delay(T));
+  pariErr->flush();
+}
+void
+timer_printf(pari_timer *T, const char *format, ...)
+{
+  va_list args; va_start(args, format);
+  timer_vprintf(T, format, args);
+  va_end(args);
+}
+
+long
+timer(void)  { static THREAD pari_timer T; return timer_delay(&T);}
+long
+gettime(void)  { static THREAD pari_timer T; return timer_delay(&T);}
+
+static THREAD pari_timer timer2_T, abstimer_T;
+long
+timer2(void) {  return timer_delay(&timer2_T);}
+void
+msgtimer(const char *format, ...)
+{
+  va_list args; va_start(args, format);
+  timer_vprintf(&timer2_T, format, args);
+  va_end(args);
+}
+long
+getabstime(void)  { return timer_get(&abstimer_T);}
+
+/*******************************************************************/
+/*                                                                 */
+/*                   FUNCTIONS KNOWN TO THE ANALYZER               */
+/*                                                                 */
+/*******************************************************************/
+GEN
+pari_version(void)
+{
+  const ulong mask = (1UL<<PARI_VERSION_SHIFT) - 1;
+  ulong major, minor, patch, n = paricfg_version_code;
+  patch = n & mask; n >>= PARI_VERSION_SHIFT;
+  minor = n & mask; n >>= PARI_VERSION_SHIFT;
+  major = n;
+  if (*paricfg_vcsversion) {
+    const char *ver = paricfg_vcsversion;
+    const char *s = strchr(ver, '-');
+    char t[8];
+    const long len = s-ver;
+    GEN v;
+    if (!s || len > 6) pari_err_BUG("pari_version()"); /* paranoia */
+    memcpy(t, ver, len); t[len] = 0;
+    v = cgetg(6, t_VEC);
+    gel(v,1) = utoi(major);
+    gel(v,2) = utoi(minor);
+    gel(v,3) = utoi(patch);
+    gel(v,4) = stoi( atoi(t) );
+    gel(v,5) = strtoGENstr(s+1);
+    return v;
+  } else {
+    GEN v = cgetg(4, t_VEC);
+    gel(v,1) = utoi(major);
+    gel(v,2) = utoi(minor);
+    gel(v,3) = utoi(patch);
+    return v;
+  }
+}
+
+/* List of GP functions:
+ * generated from the description system.
+ * ---------------------
+ * Format (struct entree) :
+ *   char *name    : name (under GP).
+ *   ulong valence : (EpNEW, EpALIAS,EpVAR, EpINSTALL)|EpSTATIC
+ *   void *value   : For PREDEFINED FUNCTIONS: C function to call.
+ *                   For USER FUNCTIONS: pointer to defining data (block) =
+ *                    entree*: NULL, list of entree (arguments), NULL
+ *                    char*  : function text
+ *   long menu     : which help section do we belong to (See below).
+ *   char *code    : argument list (See below).
+ *   char *help    : short help text (init to NULL).
+ *   void *pvalue  : push_val history.
+ *   long arity    : maximum number of arguments.
+ *   entree *next  : next entree (init to NULL, used in hashing code).
+ * menu:
+ * -----
+ *  1: Standard monadic or dyadic OPERATORS
+ *  2: CONVERSIONS and similar elementary functions
+ *  3: TRANSCENDENTAL functions
+ *  4: NUMBER THEORETICAL functions
+ *  5: Functions related to ELLIPTIC CURVES
+ *  6: Functions related to general NUMBER FIELDS
+ *  7: POLYNOMIALS and power series
+ *  8: Vectors, matrices, LINEAR ALGEBRA and sets
+ *  9: SUMS, products, integrals and similar functions
+ *  10: GRAPHIC functions
+ *  11: PROGRAMMING under GP
+ *
+ * code: describe function prototype. NULL = use valence instead.
+ * -----
+ * Arguments:
+ *  I  closure whose value is ignored, like in for() loop
+ *  E  closure whose value is used, like in sum() loop
+ *  J  implicit function of arity 1, like in parsum() loop
+ *  G  GEN
+ *  L  long
+ *  V  lexical variable
+ *  C  lexical context
+ *  n  variable number
+ *  W  GEN, which will be modified in place (for t_LIST)
+ *  &  *GEN
+ *  f  Fake *long (function requires it, but we don't use the resulting long)
+ *  p  real precision (prec for the C library)
+ *  P  series precision (precdl for the C library)
+ *  r  raw input (treated as a string without quotes).
+ *     Quoted args are copied as strings. Stops at first unquoted ')' or ','.
+ *     Special chars can be quoted using '\'.  Ex : aa"b\n)"c => "aab\n)c".
+ *  s  expanded string. Example: Pi"x"2 yields "3.142x2".
+ *     The unquoted components can be of any pari type (converted according to
+ *     the current output format)
+ *  E* any number of E
+ *  s* any number of strings (see s)
+ *  M  Mnemonic or a flag (converted to a long); description follows
+ *         after \n at the end of the argument description
+ *  D  Has a default value. Format is "Dvalue,type," (the ending comma is
+ *     mandatory). Ex: D0,L, (arg is long, 0 by default).
+ *     Special syntax:
+ *       if type = G, &, r, s, I or V:  D[G&rsIV] all send NULL.
+ *       if type = n: Dn sends -1.
+ *       if type = &: argument must be prefixed by '&'.
+ *
+ *     The user-given args are read first, then completed by the defaults
+ *
+ * Return type (first char or immediately after 'x'): GEN by default, otherwise
+ *  m Return GEN but is can point to the input (member function).
+ *  l Return long
+ *  i Return int
+ *  v Return void
+ *
+ * Syntax requirements:
+ *  = Separator '=' required.
+ ****************************************************************************
+ */
+#include "init.h"
+#include "default.h"
diff --git a/src/language/init.h b/src/language/init.h
new file mode 100644
index 0000000..756fdfe
--- /dev/null
+++ b/src/language/init.h
@@ -0,0 +1,740 @@
+/* This file is autogenerated from the database. */
+/* See src/desc/gen_proto */
+/* Do not edit*/
+entree functions_basic[]={
+{"!_",0,(void*)gnot,13,"G","!_"},
+{"#_",0,(void*)glength,13,"lG","#x: number of non code words in x, number of characters for a string."},
+{"%",0,(void*)pari_get_hist,13,"D0,L,","last history item."},
+{"%#",0,(void*)pari_get_histtime,13,"lD0,L,","time to compute last history item."},
+{"+_",0,NULL,13,NULL,"+_"},
+{"-_",0,(void*)gneg,13,"G","-_"},
+{"Catalan",0,(void*)mpcatalan,3,"p","Catalan=Catalan(): Catalan's number with current precision."},
+{"Col",0,(void*)gtocol0,2,"GD0,L,","Col(x, {n}): transforms the object x into a column vector of dimension n."},
+{"Colrev",0,(void*)gtocolrev0,2,"GD0,L,","Colrev(x, {n}): transforms the object x into a column vector of dimension n in reverse order with respect to Col(x, {n}). Empty vector if x is omitted."},
+{"Euler",0,(void*)mpeuler,3,"p","Euler=Euler(): Euler's constant with current precision."},
+{"I",0,(void*)gen_I,3,"","I=I(): square root of -1."},
+{"List",0,(void*)gtolist,2,"DG","List({x=[]}): transforms the vector or list x into a list. Empty list if x is omitted."},
+{"Mat",0,(void*)gtomat,2,"DG","Mat({x=[]}): transforms any GEN x into a matrix. Empty matrix if x is omitted."},
+{"Mod",0,(void*)gmodulo,2,"GG","Mod(a,b): creates 'a modulo b'."},
+{"O",0,(void*)ggrando,7,"","O(p^e): p-adic or power series zero with precision given by e"},
+{"O(_^_)",0,(void*)ggrando,15,"GD1,L,","O(p^e): p-adic or power series zero with precision given by e."},
+{"Pi",0,(void*)mppi,3,"p","Pi=Pi(): the constant pi, with current precision."},
+{"Pol",0,(void*)gtopoly,2,"GDn","Pol(t,{v='x}): convert t (usually a vector or a power series) into a polynomial with variable v, starting with the leading coefficient."},
+{"Polrev",0,(void*)gtopolyrev,2,"GDn","Polrev(t,{v='x}): convert t (usually a vector or a power series) into a polynomial with variable v, starting with the constant term."},
+{"Qfb",0,(void*)Qfb0,2,"GGGDGp","Qfb(a,b,c,{D=0.}): binary quadratic form a*x^2+b*x*y+c*y^2. D is optional (0.0 by default) and initializes Shanks's distance if b^2-4*a*c>0."},
+{"Ser",0,(void*)gtoser,2,"GDnDP","Ser(s,{v='x},{d=seriesprecision}): convert s into a power series with variable v and precision d, starting with the constant coefficient."},
+{"Set",0,(void*)gtoset,2,"DG","Set({x=[]}): convert x into a set, i.e. a row vector with strictly increasing coefficients. Empty set if x is omitted."},
+{"Str",0,(void*)Str,2,"s*","Str({x}*): concatenates its (string) argument into a single string."},
+{"Strchr",0,(void*)Strchr,2,"G","Strchr(x): converts x to a string, translating each integer into a character."},
+{"Strexpand",0,(void*)Strexpand,2,"s*","Strexpand({x}*): concatenates its (string) argument into a single string, performing tilde expansion."},
+{"Strprintf",0,(void*)Strprintf,11,"ss*","Strprintf(fmt,{x}*): returns a string built from the remaining arguments according to the format fmt."},
+{"Strtex",0,(void*)Strtex,2,"s*","Strtex({x}*): translates its (string) arguments to TeX format and returns the resulting string."},
+{"Vec",0,(void*)gtovec0,2,"GD0,L,","Vec(x, {n}): transforms the object x into a vector of dimension n."},
+{"Vecrev",0,(void*)gtovecrev0,2,"GD0,L,","Vecrev(x, {n}): transforms the object x into a vector of dimension n in reverse order with respect to Vec(x, {n}). Empty vector if x is omitted."},
+{"Vecsmall",0,(void*)gtovecsmall0,2,"GD0,L,","Vecsmall(x, {n}): transforms the object x into a VECSMALL of dimension n."},
+{"[_.._]",0,(void*)vecrange,15,"GG","[a..b] = [a,a+1,...,b]"},
+{"[_|_<-_,_;_]",0,(void*)vecexpr1,15,"mGVDEDE","[a(x)|x<-b,c(x);...]"},
+{"[_|_<-_,_]",0,(void*)vecexpr0,15,"GVDEDE","[a(x)|x<-b,c(x)] = apply(a,select(c,b))"},
+{"_!",0,(void*)mpfact,13,"L","n!: factorial of n."},
+{"_!=_",0,(void*)gne,13,"GG","_!=_"},
+{"_%=_",0,(void*)gmode,13,"&G","x%=y: shortcut for x=x%y."},
+{"_%_",0,(void*)gmod,13,"GG","x%y: Euclidean remainder of x and y."},
+{"_&&_",0,(void*)andpari,13,"GE","_&&_"},
+{"_'",0,(void*)deriv,13,"GDn","x': derivative of x with respect to the main variable."},
+{"_*=_",0,(void*)gmule,13,"&G","x*=y: shortcut for x=x*y."},
+{"_*_",0,(void*)gmul,13,"GG","x*y: product of x and y."},
+{"_++",0,(void*)gadd1e,13,"&","x++"},
+{"_+=_",0,(void*)gadde,13,"&G","x+=y: shortcut for x=x+y."},
+{"_+_",0,(void*)gadd,13,"GG","x+y: sum of x and y."},
+{"_--",0,(void*)gsub1e,13,"&","x--"},
+{"_-=_",0,(void*)gsube,13,"&G","x-=y: shortcut for x=x-y."},
+{"_-_",0,(void*)gsub,13,"GG","x-y: difference of x and y."},
+{"_.a1",0,(void*)member_a1,14,"mG","_.a1"},
+{"_.a2",0,(void*)member_a2,14,"mG","_.a2"},
+{"_.a3",0,(void*)member_a3,14,"mG","_.a3"},
+{"_.a4",0,(void*)member_a4,14,"mG","_.a4"},
+{"_.a6",0,(void*)member_a6,14,"mG","_.a6"},
+{"_.area",0,(void*)member_area,14,"mG","_.area"},
+{"_.b2",0,(void*)member_b2,14,"mG","_.b2"},
+{"_.b4",0,(void*)member_b4,14,"mG","_.b4"},
+{"_.b6",0,(void*)member_b6,14,"mG","_.b6"},
+{"_.b8",0,(void*)member_b8,14,"mG","_.b8"},
+{"_.bid",0,(void*)member_bid,14,"mG","_.bid"},
+{"_.bnf",0,(void*)member_bnf,14,"mG","_.bnf"},
+{"_.c4",0,(void*)member_c4,14,"mG","_.c4"},
+{"_.c6",0,(void*)member_c6,14,"mG","_.c6"},
+{"_.clgp",0,(void*)member_clgp,14,"mG","_.clgp"},
+{"_.codiff",0,(void*)member_codiff,14,"mG","_.codiff"},
+{"_.cyc",0,(void*)member_cyc,14,"mG","_.cyc"},
+{"_.diff",0,(void*)member_diff,14,"mG","_.diff"},
+{"_.disc",0,(void*)member_disc,14,"mG","_.disc"},
+{"_.e",0,(void*)member_e,14,"mG","_.e"},
+{"_.eta",0,(void*)member_eta,14,"mG","_.eta"},
+{"_.f",0,(void*)member_f,14,"mG","_.f"},
+{"_.fu",0,(void*)member_fu,14,"G","_.fu"},
+{"_.futu",0,(void*)member_futu,14,"mG","_.futu"},
+{"_.gen",0,(void*)member_gen,14,"mG","_.gen"},
+{"_.group",0,(void*)member_group,14,"mG","_.group"},
+{"_.index",0,(void*)member_index,14,"mG","_.index"},
+{"_.j",0,(void*)member_j,14,"mG","_.j"},
+{"_.mod",0,(void*)member_mod,14,"mG","_.mod"},
+{"_.nf",0,(void*)member_nf,14,"mG","_.nf"},
+{"_.no",0,(void*)member_no,14,"mG","_.no"},
+{"_.omega",0,(void*)member_omega,14,"mG","_.omega"},
+{"_.orders",0,(void*)member_orders,14,"mG","_.orders"},
+{"_.p",0,(void*)member_p,14,"mG","_.p"},
+{"_.pol",0,(void*)member_pol,14,"mG","_.pol"},
+{"_.polabs",0,(void*)member_polabs,14,"mG","_.polabs"},
+{"_.r1",0,(void*)member_r1,14,"mG","_.r1"},
+{"_.r2",0,(void*)member_r2,14,"mG","_.r2"},
+{"_.reg",0,(void*)member_reg,14,"mG","_.reg"},
+{"_.roots",0,(void*)member_roots,14,"mG","_.roots"},
+{"_.sign",0,(void*)member_sign,14,"mG","_.sign"},
+{"_.t2",0,(void*)member_t2,14,"G","_.t2"},
+{"_.tate",0,(void*)member_tate,14,"mG","_.tate"},
+{"_.tu",0,(void*)member_tu,14,"G","_.tu"},
+{"_.tufu",0,(void*)member_tufu,14,"mG","_.tufu"},
+{"_.zk",0,(void*)member_zk,14,"mG","_.zk"},
+{"_.zkst",0,(void*)member_zkst,14,"mG","_.zkst"},
+{"_/=_",0,(void*)gdive,13,"&G","x/=y: shortcut for x=x/y."},
+{"_/_",0,(void*)gdiv,13,"GG","x/y: quotient of x and y."},
+{"_<<=_",0,(void*)gshiftle,13,"&L","x<<=y: shortcut for x=x<<y."},
+{"_<<_",0,(void*)gshift,13,"GL","x<<y"},
+{"_<=_",0,(void*)gle,13,"GG","x<=y: return 1 if x is less or equal to y, 0 otherwise."},
+{"_<_",0,(void*)glt,13,"GG","x<y: return 1 if x is strictly less than y, 0 otherwise."},
+{"_===_",0,(void*)gidentical,13,"iGG","a === b : true if a and b are identical"},
+{"_==_",0,(void*)geq,13,"GG","_==_"},
+{"_>=_",0,(void*)gge,13,"GG","x>=y: return 1 if x is greater or equal to y, 0 otherwise."},
+{"_>>=_",0,(void*)gshiftre,13,"&L","x>>=y: shortcut for x=x>>y."},
+{"_>>_",0,(void*)gshift_right,13,"GL","x>>y"},
+{"_>_",0,(void*)ggt,13,"GG","x>y: return 1 if x is strictly greater than y, 0 otherwise."},
+{"_[_.._,_.._]",0,(void*)matslice0,13,"GD0,L,D0,L,D0,L,D0,L,","x[a..b,c..d] = [x[a,c],  x[a+1,c],  ...,x[b,c];                      x[a,c+1],x[a+1,c+1],...,x[b,c+1];                        ...       ...          ...                      x[a,d],  x[a+1,d]  ,...,x[b,d]]"},
+{"_[_.._]",0,(void*)vecslice0,13,"GD0,L,L","x[a..b] = [x[a],x[a+1],...,x[b]]"},
+{"_\\/=_",0,(void*)gdivrounde,13,"&G","x\\/=y: shortcut for x=x\\/y."},
+{"_\\/_",0,(void*)gdivround,13,"GG","x\\/y: rounded Euclidean quotient of x and y."},
+{"_\\=_",0,(void*)gdivente,13,"&G","x\\=y: shortcut for x=x\\y."},
+{"_\\_",0,(void*)gdivent,13,"GG","x\\y: Euclidean quotient of x and y."},
+{"_^_",0,(void*)gpow,13,"GGp","x^y: compute x to the power y."},
+{"_^s",0,(void*)gpowgs,15,"GL","return x^n where n is a small integer"},
+{"__",0,NULL,13,NULL,"__"},
+{"_derivfun",0,(void*)derivfun0,15,"GGp","_derivfun(closure,[args]) numerical derivation of closure with respect to the first variable at (args)."},
+{"_eval_mnemonic",0,(void*)eval_mnemonic,15,"lGs","Convert a mnemonic string to a flag."},
+{"_factor_Aurifeuille",0,(void*)factor_Aurifeuille,15,"GL","_factor_Aurifeuille(a,d): return an algebraic factor of Phi_d(a), a != 0"},
+{"_factor_Aurifeuille_prime",0,(void*)factor_Aurifeuille_prime,15,"GL","_factor_Aurifeuille_prime(p,d): return an algebraic factor of Phi_d(p), p prime"},
+{"_multi_if",0,(void*)ifpari_multi,15,"GE*","internal variant of if() that allows more than 3 arguments."},
+{"_parapply_worker",0,(void*)parapply_worker,15,"GG","_parapply_worker(d,C): evaluate the closure C on d."},
+{"_pareval_worker",0,(void*)pareval_worker,15,"G","_pareval_worker(C): evaluate the closure C."},
+{"_parfor_worker",0,(void*)parfor_worker,15,"GG","_parfor_worker(i,C): evaluate the closure C on i and return [i,C(i)]"},
+{"_parvector_worker",0,(void*)parvector_worker,15,"GG","_parvector_worker(i,C): evaluate the closure C on i."},
+{"_void_if",0,(void*)ifpari_void,15,"vGDIDI","internal variant of if() that does not return a value."},
+{"_||_",0,(void*)orpari,13,"GE","x||y: inclusive OR."},
+{"_~",0,(void*)gtrans,13,"G","x~: transpose of x."},
+{"abs",0,(void*)gabs,3,"Gp","abs(x): absolute value (or modulus) of x."},
+{"acos",0,(void*)gacos,3,"Gp","acos(x): arc cosine of x."},
+{"acosh",0,(void*)gacosh,3,"Gp","acosh(x): inverse hyperbolic cosine of x."},
+{"addhelp",0,(void*)addhelp,11,"vrs","addhelp(sym,str): add/change help message for the symbol sym."},
+{"addprimes",0,(void*)addprimes,4,"DG","addprimes({x=[]}): add primes in the vector x to the prime table to be used in trial division. x may also be a single integer. Composite \"primes\" are NOT allowed!"},
+{"agm",0,(void*)agm,3,"GGp","agm(x,y): arithmetic-geometric mean of x and y."},
+{"algdep",0,(void*)algdep0,8,"GLD0,L,","algdep(x,k,{flag=0}): algebraic relations up to degree n of x, using lindep([1,x,...,x^(k-1)], flag)."},
+{"alias",0,(void*)alias0,11,"vrr","alias(newsym,sym): defines the symbol newsym as an alias for the symbol sym."},
+{"apply",0,(void*)apply0,11,"GG","apply(f, A): apply function f to each entry in A."},
+{"arg",0,(void*)garg,3,"Gp","arg(x): argument of x,such that -pi<arg(x)<=pi."},
+{"asin",0,(void*)gasin,3,"Gp","asin(x): arc sine of x."},
+{"asinh",0,(void*)gasinh,3,"Gp","asinh(x): inverse hyperbolic sine of x."},
+{"atan",0,(void*)gatan,3,"Gp","atan(x): arc tangent of x."},
+{"atanh",0,(void*)gatanh,3,"Gp","atanh(x): inverse hyperbolic tangent of x."},
+{"bernfrac",0,(void*)bernfrac,3,"L","bernfrac(x): Bernoulli number B_x, as a rational number."},
+{"bernpol",0,(void*)bernpol,3,"LDn","bernpol(n, {v = 'x}): Bernoulli polynomial B_n, in variable v."},
+{"bernreal",0,(void*)bernreal,3,"Lp","bernreal(x): Bernoulli number B_x, as a real number with the current precision."},
+{"bernvec",0,(void*)bernvec,3,"L","bernvec(x): Vector of rational Bernoulli numbers B_0, B_2,...up to B_(2x)."},
+{"besselh1",0,(void*)hbessel1,3,"GGp","besselh1(nu,x): H^1-bessel function of index nu and argument x."},
+{"besselh2",0,(void*)hbessel2,3,"GGp","besselh2(nu,x): H^2-bessel function of index nu and argument x."},
+{"besseli",0,(void*)ibessel,3,"GGp","besseli(nu,x): I-bessel function of index nu and argument x."},
+{"besselj",0,(void*)jbessel,3,"GGp","besselj(nu,x): J-bessel function of index nu and argument x."},
+{"besseljh",0,(void*)jbesselh,3,"GGp","besseljh(n,x): J-bessel function of index n+1/2 and argument x, where n is a non-negative integer."},
+{"besselk",0,(void*)kbessel,3,"GGp","besselk(nu,x): K-bessel function of index nu and argument x."},
+{"besseln",0,(void*)nbessel,3,"GGp","besseln(nu,x): N-bessel function of index nu and argument x."},
+{"bestappr",0,(void*)bestappr,4,"GDG","bestappr(x, {B}): returns a rational approximation to x, whose denominator is limited by B, if present. This function applies to reals, intmods, p-adics, and rationals of course. Otherwise it applies recursively to all components."},
+{"bestapprPade",0,(void*)bestapprPade,4,"GD-1,L,","bestappr(x, {B}): returns a rational function approximation to x. This function applies to series, polmods, and rational functions of course. Otherwise it applies recursively to all components."},
+{"bezout",0,(void*)gcdext0,4,"GG","bezout(x,y): deprecated alias for gcdext"},
+{"bezoutres",0,(void*)polresultantext0,7,"GGDn","bezoutre(A,B,{v}): deprecated alias for polresultantext"},
+{"bigomega",0,(void*)bigomega,4,"lG","bigomega(x): number of prime divisors of x, counted with multiplicity."},
+{"binary",0,(void*)binaire,2,"G","binary(x): gives the vector formed by the binary digits of x (x integer)."},
+{"binomial",0,(void*)binomial,4,"GL","binomial(x,y): binomial coefficient x*(x-1)...*(x-y+1)/y! defined for y in Z and any x."},
+{"bitand",0,(void*)gbitand,2,"GG","bitand(x,y): bitwise \"and\" of two integers x and y. Negative numbers behave as if modulo big power of 2."},
+{"bitneg",0,(void*)gbitneg,2,"GD-1,L,","bitneg(x,{n=-1}): bitwise negation of an integers x truncated to n bits. n=-1 means represent infinite sequences of bit 1 as negative numbers. Negative numbers behave as if modulo big power of 2."},
+{"bitnegimply",0,(void*)gbitnegimply,2,"GG","bitnegimply(x,y): bitwise \"negated imply\" of two integers x and y, in other words, x BITAND BITNEG(y). Negative numbers behave as if modulo big power of 2."},
+{"bitor",0,(void*)gbitor,2,"GG","bitor(x,y): bitwise \"or\" of two integers x and y. Negative numbers behave as if modulo big power of 2."},
+{"bittest",0,(void*)gbittest,2,"GL","bittest(x,n): gives bit number n (coefficient of 2^n) of the integer x. Negative numbers behave as if modulo big power of 2."},
+{"bitxor",0,(void*)gbitxor,2,"GG","bitxor(x,y): bitwise \"exclusive or\" of two integers x and y. Negative numbers behave as if modulo big power of 2."},
+{"bnfcertify",0,(void*)bnfcertify0,6,"lGD0,L,","bnfcertify(bnf,{flag = 0}): certify the correctness (i.e. remove the GRH) of the bnf data output by bnfinit. If flag is present, only certify that the class group is a quotient of the one computed in bnf (much simpler in general)."},
+{"bnfcompress",0,(void*)bnfcompress,6,"G","bnfcompress(bnf): converts bnf to a much smaller sbnf, containing the same information. Use bnfinit(sbnf) to recover a true bnf."},
+{"bnfdecodemodule",0,(void*)decodemodule,6,"GG","bnfdecodemodule(nf,m): given a coded module m as in bnrdisclist, gives the true module."},
+{"bnfinit",0,(void*)bnfinit0,6,"GD0,L,DGp","bnfinit(P,{flag=0},{tech=[]}): compute the necessary data for future use in ideal and unit group computations, including fundamental units if they are not too large. flag and tech are both optional. flag can be any of 0: default, 1: insist on having fundamental units. See manual for details about tech."},
+{"bnfisintnorm",0,(void*)bnfisintnorm,6,"GG","bnfisintnorm(bnf,x): compute a complete system of solutions (modulo units of positive norm) of the absolute norm equation N(a)=x, where a belongs to the maximal order of big number field bnf (if bnf is not certified, this depends on GRH)."},
+{"bnfisnorm",0,(void*)bnfisnorm,6,"GGD1,L,","bnfisnorm(bnf,x,{flag=1}): Tries to tell whether x (in Q) is the norm of some fractional y (in bnf). Returns a vector [a,b] where x=Norm(a)*b. Looks for a solution which is a S-unit, with S a certain list of primes (in bnf) containing (among others) all primes dividing x. If bnf is known to be Galois, set flag=0 (in this case, x is a norm iff b=1). If flag is non zero the program adds to S all the primes: dividing flag if flag<0, or less than  [...]
+{"bnfisprincipal",0,(void*)bnfisprincipal0,6,"GGD1,L,","bnfisprincipal(bnf,x,{flag=1}): bnf being output by bnfinit (with flag<=2), gives [v,alpha], where v is the vector of exponents on the class group generators and alpha is the generator of the resulting principal ideal. In particular x is principal if and only if v is the zero vector. flag is optional, whose binary digits mean 1: output [v,alpha] (only v if unset); 2: increase precision until alpha can be computed (do not insist if u [...]
+{"bnfissunit",0,(void*)bnfissunit,6,"GGG","bnfissunit(bnf,sfu,x): bnf being output by bnfinit (with flag<=2), sfu by bnfsunit, gives the column vector of exponents of x on the fundamental S-units and the roots of unity if x is a unit, the empty vector otherwise."},
+{"bnfisunit",0,(void*)bnfisunit,6,"GG","bnfisunit(bnf,x): bnf being output by bnfinit, gives the column vector of exponents of x on the fundamental units and the roots of unity if x is a unit, the empty vector otherwise."},
+{"bnfnarrow",0,(void*)buchnarrow,6,"G","bnfnarrow(bnf): given a big number field as output by bnfinit, gives as a 3-component vector the structure of the narrow class group."},
+{"bnfsignunit",0,(void*)signunits,6,"G","bnfsignunit(bnf): matrix of signs of the real embeddings of the system of fundamental units found by bnfinit."},
+{"bnfsunit",0,(void*)bnfsunit,6,"GGp","bnfsunit(bnf,S): compute the fundamental S-units of the number field bnf output by bnfinit, S being a list of prime ideals. res[1] contains the S-units, res[5] the S-classgroup. See manual for details."},
+{"bnrL1",0,(void*)bnrL1,6,"GDGD0,L,p","bnrL1(bnr, {H}, {flag=0}): bnr being output by bnrinit(,,1) and H being a square matrix defining a congruence subgroup of bnr (the trivial subgroup if omitted), for each character of bnr trivial on this subgroup, compute L(1, chi) (or equivalently the first non-zero term c(chi) of the expansion at s = 0). The binary digits of flag mean 1: if 0 then compute the term c(chi) and return [r(chi), c(chi)] where r(chi) is the order of L(s, chi) at s = 0, o [...]
+{"bnrclassno",0,(void*)bnrclassno0,6,"GDGDG","bnrclassno(A,{B},{C}): relative degree of the class field defined by A,B,C. [A,{B},{C}] is of type [bnr], [bnr,subgroup], [bnf,modulus], or [bnf,modulus,subgroup]. Faster than bnrinit if only the ray class number is wanted."},
+{"bnrclassnolist",0,(void*)bnrclassnolist,6,"GG","bnrclassnolist(bnf,list): if list is as output by ideallist or similar, gives list of corresponding ray class numbers."},
+{"bnrconductor",0,(void*)bnrconductor0,6,"GDGDGD0,L,","bnrconductor(A,{B},{C},{flag=0}): conductor f of the subfield of the ray class field given by A,B,C. flag is optional and can be 0: default, 1: returns [f, Cl_f, H], H subgroup of the ray class group modulo f defining the extension, 2: returns [f, bnr(f), H]."},
+{"bnrconductorofchar",0,(void*)bnrconductorofchar,6,"GG","bnrconductorofchar(bnr,chi): conductor of the character chi on the ray class group bnr."},
+{"bnrdisc",0,(void*)bnrdisc0,6,"GDGDGD0,L,","bnrdisc(A,{B},{C},{flag=0}): absolute or relative [N,R1,discf] of the field defined by A,B,C. [A,{B},{C}] is of type [bnr], [bnr,subgroup], [bnf, modulus] or [bnf,modulus,subgroup], where bnf is as output by bnfinit, bnr by bnrinit, and subgroup is the HNF matrix of a subgroup of the corresponding ray class group (if omitted, the trivial subgroup). flag is optional whose binary digits mean 1: give relative data; 2: return 0 if modulus is not t [...]
+{"bnrdisclist",0,(void*)bnrdisclist0,6,"GGDG","bnrdisclist(bnf,bound,{arch}): gives list of discriminants of ray class fields of all conductors up to norm bound, in a long vector The ramified Archimedean places are given by arch; all possible values are taken if arch is omitted. Supports the alternative syntax bnrdisclist(bnf,list), where list is as output by ideallist or ideallistarch (with units)."},
+{"bnrinit",0,(void*)bnrinit0,6,"GGD0,L,","bnrinit(bnf,f,{flag=0}): given a bnf as output by bnfinit and a modulus f, initializes data linked to the ray class group structure corresponding to this module. flag is optional, and can be 0: default, 1: compute also the generators."},
+{"bnrisconductor",0,(void*)bnrisconductor0,6,"lGDGDG","bnrisconductor(A,{B},{C}): returns 1 if the modulus is the conductor of the subfield of the ray class field given by A,B,C (see bnrdisc), and 0 otherwise. Slightly faster than bnrconductor if this is the only desired result."},
+{"bnrisprincipal",0,(void*)bnrisprincipal,6,"GGD1,L,","bnrisprincipal(bnr,x,{flag=1}): bnr being output by bnrinit, gives [v,alpha], where v is the vector of exponents on the class group generators and alpha is the generator of the resulting principal ideal. In particular x is principal if and only if v is the zero vector. If (optional) flag is set to 0, output only v."},
+{"bnrrootnumber",0,(void*)bnrrootnumber,6,"GGD0,L,p","bnrrootnumber(bnr,chi,{flag=0}): returns the so-called Artin Root Number, i.e. the constant W appearing in the functional equation of the Hecke L-function associated to chi. Set flag = 1 if the character is known to be primitive."},
+{"bnrstark",0,(void*)bnrstark,6,"GDGp","bnrstark(bnr,{subgroup}): bnr being as output by bnrinit(,,1), finds a relative equation for the class field corresponding to the module in bnr and the given congruence subgroup (the trivial subgroup if omitted) using Stark's units. The ground field and the class field must be totally real."},
+{"break",0,(void*)break0,11,"D1,L,","break({n=1}): interrupt execution of current instruction sequence, and exit from the n innermost enclosing loops."},
+{"ceil",0,(void*)gceil,2,"G","ceil(x): ceiling of x = smallest integer >= x."},
+{"centerlift",0,(void*)centerlift0,2,"GDn","centerlift(x,{v}): centered lift of x. Same as lift except for intmod and padic components."},
+{"characteristic",0,(void*)characteristic,2,"mG","characteristic(x): characteristic of the base ring over which x is defined"},
+{"charpoly",0,(void*)charpoly0,8,"GDnD5,L,","charpoly(A,{v='x},{flag=5}): det(v*Id-A)=characteristic polynomial of the matrix or polmod A. flag is optional and ignored unless A is a matrix; it may be set to 0 (Le Verrier), 1 (Lagrange interpolation), 2 (Hessenberg form), 3 (Berkowitz), 4 (modular) if A is integral, or 5 (default, choose best method). Algorithms 0 (Le Verrier) and 1 (Lagrange) assume that n! is invertible, where n is the dimension of the matrix."},
+{"chinese",0,(void*)chinese,4,"GDG","chinese(x,{y}): x,y being both intmods (or polmods) computes z in the same residue classes as x and y."},
+{"cmp",0,(void*)cmp_universal,1,"iGG","cmp(x,y): compare two arbitrary objects x and y (1 if x>y, 0 if x=y, -1 if x<y). The function is used to implement sets, and has no useful mathematical meaning."},
+{"component",0,(void*)compo,2,"GL","component(x,n): the n'th component of the internal representation of x. For vectors or matrices, it is simpler to use x[]. For list objects such as nf, bnf, bnr or ell, it is much easier to use member functions starting with \".\"."},
+{"concat",0,(void*)concat,8,"GDG","concat(x,{y}): concatenation of x and y, which can be scalars, vectors or matrices, or lists (in this last case, both x and y have to be lists). If y is omitted, x has to be a list or row vector and its elements are concatenated."},
+{"conj",0,(void*)gconj,2,"G","conj(x): the algebraic conjugate of x."},
+{"conjvec",0,(void*)conjvec,2,"Gp","conjvec(z): conjugate vector of the algebraic number z."},
+{"content",0,(void*)content,4,"G","content(x): gcd of all the components of x, when this makes sense."},
+{"contfrac",0,(void*)contfrac0,4,"GDGD0,L,","contfrac(x,{b},{nmax}): continued fraction expansion of x (x rational,real or rational function). b and nmax are both optional, where b is the vector of numerators of the continued fraction, and nmax is a bound for the number of terms in the continued fraction expansion."},
+{"contfracpnqn",0,(void*)contfracpnqn,4,"GD-1,L,","contfracpnqn(x, {n=-1}): [p_n,p_{n-1}; q_n,q_{n-1}] corresponding to the continued fraction x. If n >= 0 is present, returns all convergents from p_0/q_0 up to p_n/q_n."},
+{"core",0,(void*)core0,4,"GD0,L,","core(n,{flag=0}): unique squarefree integer d dividing n such that n/d is a square. If (optional) flag is non-null, output the two-component row vector [d,f], where d is the unique squarefree integer dividing n such that n/d=f^2 is a square."},
+{"coredisc",0,(void*)coredisc0,4,"GD0,L,","coredisc(n,{flag=0}): discriminant of the quadratic field Q(sqrt(n)). If (optional) flag is non-null, output a two-component row vector [d,f], where d is the discriminant of the quadratic field Q(sqrt(n)) and n=df^2. f may be a half integer."},
+{"cos",0,(void*)gcos,3,"Gp","cos(x): cosine of x."},
+{"cosh",0,(void*)gcosh,3,"Gp","cosh(x): hyperbolic cosine of x."},
+{"cotan",0,(void*)gcotan,3,"Gp","cotan(x): cotangent of x."},
+{"dbg_x",0,(void*)dbgGEN,11,"vGD-1,L,","dbg_x(A{,n}): print inner structure of A, complete if n is omitted, up to level n otherwise. Intended for debugging."},
+{"default",0,(void*)default0,11,"DrDs","default({key},{val}): returns the current value of the default key. If val is present, set opt to val first. If no argument is given, print a list of all defaults as well as their values."},
+{"denominator",0,(void*)denom,2,"G","denominator(x): denominator of x (or lowest common denominator in case of an array)."},
+{"deriv",0,(void*)deriv,7,"GDn","deriv(x,{v}): derivative of x with respect to v, or to the main variable of x if v is omitted."},
+{"derivnum",0,(void*)derivnum0,9,"V=GEp","derivnum(X=a,expr): numerical derivation of expr with respect to X at X = a."},
+{"diffop",0,(void*)diffop0,7,"GGGD1,L,","diffop(x,v,d,{n=1}): apply the differential operator D to x, where D is defined by D(v[i])=d[i], where v is a vector of variable names. D is 0 for variables outside of v unless they appear as modulus of a POLMOD. If the optional parameter n is given, return D^n(x) instead."},
+{"digits",0,(void*)digits,2,"GDG","digits(x,{b=10}): gives the vector formed by the digits of x in base b (x and b integers)."},
+{"dilog",0,(void*)dilog,3,"Gp","dilog(x): dilogarithm of x."},
+{"dirdiv",0,(void*)dirdiv,4,"GG","dirdiv(x,y): division of the Dirichlet series x by the Dirichlet series y."},
+{"direuler",0,(void*)direuler0,4,"V=GGEDG","direuler(p=a,b,expr,{c}): Dirichlet Euler product of expression expr from p=a to p=b, limited to b terms. Expr should be a polynomial or rational function in p and X, and X is understood to mean p^(-s). If c is present, output only the first c terms."},
+{"dirmul",0,(void*)dirmul,4,"GG","dirmul(x,y): multiplication of the Dirichlet series x by the Dirichlet series y."},
+{"dirzetak",0,(void*)dirzetak,6,"GG","dirzetak(nf,b): Dirichlet series of the Dedekind zeta function of the number field nf up to the bound b-1."},
+{"divisors",0,(void*)divisors,4,"G","divisors(x): gives a vector formed by the divisors of x in increasing order."},
+{"divrem",0,(void*)divrem,1,"GGDn","divrem(x,y,{v}): euclidean division of x by y giving as a 2-dimensional column vector the quotient and the remainder, with respect to v (to main variable if v is omitted)"},
+{"eint1",0,(void*)veceint1,3,"GDGp","eint1(x,{n}): exponential integral E1(x). If n is present and x > 0, computes the vector of the first n values of the exponential integral E1(n.x)"},
+{"ellL1",0,(void*)ellL1,5,"GLp","ellL1(e, r): returns the value at s=1 of the derivative of order r of the L-function of the elliptic curve e assuming that r is at most the order of vanishing of the function at s=1."},
+{"elladd",0,(void*)elladd,5,"GGG","elladd(E,z1,z2): sum of the points z1 and z2 on elliptic curve E."},
+{"ellak",0,(void*)akell,5,"GG","ellak(E,n): computes the n-th Fourier coefficient of the L-function of the elliptic curve E (assumed E is an integral model)."},
+{"ellan",0,(void*)anell,5,"GL","ellan(E,n): computes the first n Fourier coefficients of the L-function of the elliptic curve E (n<2^24 on a 32-bit machine)."},
+{"ellanalyticrank",0,(void*)ellanalyticrank,5,"GDGp","ellanalyticrank(e, {eps}): returns the order of vanishing at s=1 of the L-function of the elliptic curve e and the value of the first non-zero derivative. To determine this order, it is assumed that any value less than eps is zero. If no value of eps is given, a value of half the current precision is used."},
+{"ellap",0,(void*)ellap,5,"GDG","ellap(E,{p}): computes the trace of Frobenius a_p for the elliptic curve E, defined over Q or a finite field."},
+{"ellbil",0,(void*)bilhell,5,"GGGp","ellbil(E,z1,z2): canonical bilinear form for the points z1,z2 on the elliptic curve E. Either z1 or z2 can also be a vector/matrix of points."},
+{"ellcard",0,(void*)ellcard,5,"GDG","ellcard(E,{p}): computes the order of the group E(Fp) for the elliptic curve E, defined over Q or a finite field."},
+{"ellchangecurve",0,(void*)ellchangecurve,5,"GG","ellchangecurve(E,v): change data on elliptic curve according to v=[u,r,s,t]."},
+{"ellchangepoint",0,(void*)ellchangepoint,5,"GG","ellchangepoint(x,v): change data on point or vector of points x on an elliptic curve according to v=[u,r,s,t]."},
+{"ellchangepointinv",0,(void*)ellchangepointinv,5,"GG","ellchangepointinv(x,v): change data on point or vector of points x on an elliptic curve according to v=[u,r,s,t], inverse of ellchangepoint."},
+{"ellconvertname",0,(void*)ellconvertname,5,"G","ellconvertname(name): convert an elliptic curve name (as found in the elldata database) from a string to a triplet [conductor, isogeny class, index]. It will also convert a triplet back to a curve name."},
+{"elldivpol",0,(void*)elldivpol,5,"GLDn","elldivpol(E,n,{v='x}): n-division polynomial f_n for the curve E in the variable v."},
+{"elleisnum",0,(void*)elleisnum,5,"GLD0,L,p","elleisnum(w,k,{flag=0}): k being an even positive integer, computes the numerical value of the Eisenstein series of weight k at the lattice w, as given by ellperiods. When flag is non-zero and k=4 or 6, this gives the elliptic invariants g2 or g3 with the correct normalization."},
+{"elleta",0,(void*)elleta,5,"Gp","elleta(w): w=[w1,w2], returns the vector [eta1,eta2] of quasi-periods associated to [w1,w2]."},
+{"ellfromj",0,(void*)ellfromj,5,"G","ellfromj(j): returns the coefficients [a1,a2,a3,a4,a6] of a fixed elliptic curve with j-invariant j."},
+{"ellgenerators",0,(void*)ellgenerators,5,"G","ellgenerators(E): If E is an elliptic curve over the rationals, return the generators of the Mordell-Weil group associated to the curve. This relies on the curve being referenced in the elldata database. If E is an elliptic curve over a finite field Fq as output by ellinit(), return a minimal set of generators for the group E(Fq)."},
+{"ellglobalred",0,(void*)ellglobalred,5,"G","ellglobalred(E): E being an elliptic curve, returns [N,[u,r,s,t],c, faN,L], where N is the conductor of E, [u,r,s,t] leads to the standard model for E, c is the product of the local Tamagawa numbers c_p, faN is factor(N) and L[i] is elllocalred(E, faN[i,1])."},
+{"ellgroup",0,(void*)ellgroup0,5,"GDGD0,L,","ellgroup(E,{p},{flag}): computes the structure of the group E(Fp) If flag is 1, return also generators."},
+{"ellheegner",0,(void*)ellheegner,5,"G","ellheegner(E): return a rational non-torsion point on the elliptic curve E assumed to be of rank 1"},
+{"ellheight",0,(void*)ellheight0,5,"GGD2,L,p","ellheight(E,x,{flag=2}): canonical height of point x on elliptic curve E. flag is optional and selects the algorithm used to compute the Archimedean local height. Its meaning is 0: use theta-functions, 1: use Tate's method, 2: use Mestre's AGM."},
+{"ellheightmatrix",0,(void*)mathell,5,"GGp","ellheightmatrix(E,x): gives the height matrix for vector of points x on elliptic curve E."},
+{"ellidentify",0,(void*)ellidentify,5,"G","ellidentify(E): look up the elliptic curve E in the elldata database and return [[N, M, ...], C] where N is the name of the curve in Cremona's database, M the minimal model and C the coordinates change (see ellchangecurve)."},
+{"ellinit",0,(void*)ellinit,5,"GDGp","ellinit(x,{D=1}): let x be a vector [a1,a2,a3,a4,a6], or [a4,a6] if a1=a2=a3=0, defining the curve Y^2 + a1.XY + a3.Y = X^3 + a2.X^2 + a4.X + a6; x can also be a string, in which case the curve with matching name is retrieved from the elldata database, if available. This function initializes an elliptic curve over the domain D (inferred from coefficients if omitted)."},
+{"ellisoncurve",0,(void*)ellisoncurve,5,"GG","ellisoncurve(E,z): true(1) if z is on elliptic curve E, false(0) if not."},
+{"ellj",0,(void*)jell,5,"Gp","ellj(x): elliptic j invariant of x."},
+{"elllocalred",0,(void*)elllocalred,5,"GG","elllocalred(E,p): E being an elliptic curve, returns [f,kod,[u,r,s,t],c], where f is the conductor's exponent, kod is the Kodaira type for E at p, [u,r,s,t] is the change of variable needed to make E minimal at p, and c is the local Tamagawa number c_p."},
+{"elllog",0,(void*)elllog,5,"GGGDG","elllog(E,P,G,{o}): return the discrete logarithm of the point P of the elliptic curve E in base G. If present, o represents the order of G. If not present, assume that G generates the curve."},
+{"elllseries",0,(void*)elllseries,5,"GGDGp","elllseries(E,s,{A=1}): L-series at s of the elliptic curve E, where A a cut-off point close to 1."},
+{"ellminimalmodel",0,(void*)ellminimalmodel,5,"GD&","ellminimalmodel(E,{&v}): return the standard minimal integral model of the rational elliptic curve E. Sets v to the corresponding change of variables."},
+{"ellmodulareqn",0,(void*)ellmodulareqn,5,"LDnDn","ellmodulareqn(N,{x},{y}): return a vector [eqn, t] where eqn is a modular equation of level N, for N<500, N prime. This requires the package seadata to be installed.  The equation is either of canonical type (t=0) or of Atkin type (t=1)"},
+{"ellmul",0,(void*)ellmul,5,"GGG","ellmul(E,z,n): n times the point z on elliptic curve E (n in Z)."},
+{"ellneg",0,(void*)ellneg,5,"GG","ellneg(E,z): opposite of the point z on elliptic curve E."},
+{"ellorder",0,(void*)ellorder,5,"GGDG","ellorder(E,z,{o}): order of the point z on the elliptic curve E over Q or a finite field, 0 if non-torsion. The parameter o, if present, represents a non-zero multiple of the order of z."},
+{"ellordinate",0,(void*)ellordinate,5,"GGp","ellordinate(E,x): y-coordinates corresponding to x-ordinate x on elliptic curve E."},
+{"ellperiods",0,(void*)ellperiods,5,"GD0,L,p","ellperiods(w, {flag = 0}): w describes a complex period lattice ([w1,w2] or an ellinit structure). Returns normalized periods [W1,W2] generating the same lattice such that tau := W1/W2 satisfies Im(tau) > 0 and lies in the standard fundamental domain for SL2. If flag is 1, the return value is [[W1,W2], [eta1,eta2]], where eta1, eta2 are the quasi-periods associated to [W1,W2], satisfying eta1 W2 - eta2 W1 = 2 I Pi."},
+{"ellpointtoz",0,(void*)zell,5,"GGp","ellpointtoz(E,P): lattice point z corresponding to the point P on the elliptic curve E."},
+{"ellpow",0,(void*)ellmul,5,"GGG","ellpow(E,z,n): deprecated alias for ellmul."},
+{"ellrootno",0,(void*)ellrootno,5,"lGDG","ellrootno(E,{p}): root number for the L-function of the elliptic curve E/Q at a prime p (including 0, for the infinite place); global root number if p is omitted."},
+{"ellsearch",0,(void*)ellsearch,5,"G","ellsearch(N): returns all curves in the elldata database matching constraint N:  given name (N = \"11a1\" or [11,0,1]), given isogeny class (N = \"11a\" or [11,0]), or given conductor (N = 11, \"11\", or [11])."},
+{"ellsigma",0,(void*)ellsigma,5,"GDGD0,L,p","ellsigma(L,{z='x},{flag=0}): computes the value at z of the Weierstrass sigma function attached to the lattice w, as given by ellperiods(,1). If flag = 1, returns an arbitrary determination of the logarithm of sigma."},
+{"ellsub",0,(void*)ellsub,5,"GGG","ellsub(E,z1,z2): difference of the points z1 and z2 on elliptic curve E."},
+{"elltaniyama",0,(void*)elltaniyama,5,"GDP","elltaniyama(E, {d = seriesprecision}): modular parametrization of elliptic curve E/Q."},
+{"elltatepairing",0,(void*)elltatepairing,5,"GGGG","elltatepairing(E, P, Q, m): Computes the Tate pairing of the two points P and Q on the elliptic curve E. The point P must be of m-torsion."},
+{"elltors",0,(void*)elltors0,5,"GD0,L,","elltors(E,{flag=0}): torsion subgroup of elliptic curve E: order, structure, generators. If flag = 0, use division polynomials; if flag = 1, use Lutz-Nagell; if flag = 2, use Doud's algorithm."},
+{"ellweilpairing",0,(void*)ellweilpairing,5,"GGGG","ellweilpairing(E, P, Q, m): Computes the Weil pairing of the two points of m-torsion P and Q on the elliptic curve E."},
+{"ellwp",0,(void*)ellwp0,5,"GDGD0,L,p","ellwp(w,{z='x},{flag=0}): computes the value at z of the Weierstrass P function attached to the lattice w, as given by ellperiods. Optional flag means 0 (default), compute only P(z), 1 compute [P(z),P'(z)]."},
+{"ellzeta",0,(void*)ellzeta,5,"GDGp","ellzeta(w,{z='x}): computes the value at z of the Weierstrass Zeta function attached to the lattice w, as given by ellperiods(,1)."},
+{"ellztopoint",0,(void*)pointell,5,"GGp","ellztopoint(E,z): coordinates of point P on the curve E corresponding to the complex number z."},
+{"erfc",0,(void*)gerfc,3,"Gp","erfc(x): complementary error function."},
+{"errname",0,(void*)errname,11,"G","errname(E): returns the type of the error message E."},
+{"error",0,(void*)error0,11,"vs*","error({str}*): abort script with error message str."},
+{"eta",0,(void*)eta0,3,"GD0,L,p","eta(z,{flag=0}): if flag=0, returns prod(n=1,oo, 1-q^n), where q = exp(2 i Pi z) if z is a complex scalar (belonging to the upper half plane); q = z if z is a p-adic number or can be converted to a power series. If flag is non-zero, the function only applies to complex scalars and returns the true eta function, with the factor q^(1/24) included."},
+{"eulerphi",0,(void*)eulerphi,4,"G","eulerphi(x): Euler's totient function of x."},
+{"eval",0,(void*)geval_gp,7,"GC","eval(x): evaluation of x, replacing variables by their value."},
+{"exp",0,(void*)gexp,3,"Gp","exp(x): exponential of x."},
+{"expm1",0,(void*)gexpm1,3,"Gp","expm1(x): exp(x)-1."},
+{"factor",0,(void*)gp_factor0,4,"GDG","factor(x,{lim}): factorization of x. lim is optional and can be set whenever x is of (possibly recursive) rational type. If lim is set return partial factorization, using primes < lim."},
+{"factorback",0,(void*)factorback2,4,"GDG","factorback(f,{e}): given a factorisation f, gives the factored object back. If this is a prime ideal factorisation you must supply the corresponding number field as last argument. If e is present, f has to be a vector of the same length, and we return the product of the f[i]^e[i]."},
+{"factorcantor",0,(void*)factcantor,4,"GG","factorcantor(x,p): factorization mod p of the polynomial x using Cantor-Zassenhaus."},
+{"factorff",0,(void*)factorff,4,"GDGDG","factorff(x,{p},{a}): factorization of the polynomial x in the finite field F_p[X]/a(X)F_p[X]."},
+{"factorial",0,(void*)mpfactr,4,"Lp","factorial(x): factorial of x, the result being given as a real number."},
+{"factorint",0,(void*)factorint,4,"GD0,L,","factorint(x,{flag=0}): factor the integer x. flag is optional, whose binary digits mean 1: avoid MPQS, 2: avoid first-stage ECM (may fall back on it later), 4: avoid Pollard-Brent Rho and Shanks SQUFOF, 8: skip final ECM (huge composites will be declared prime)."},
+{"factormod",0,(void*)factormod0,4,"GGD0,L,","factormod(x,p,{flag=0}): factors the polynomial x modulo the prime p, using Berlekamp. flag is optional, and can be 0: default or 1: only the degrees of the irreducible factors are given."},
+{"factornf",0,(void*)polfnf,6,"GG","factornf(x,t): factorization of the polynomial x over the number field defined by the polynomial t."},
+{"factorpadic",0,(void*)factorpadic0,7,"GGLD0,L,","factorpadic(pol,p,r): p-adic factorization of the polynomial pol to precision r."},
+{"ffgen",0,(void*)ffgen,4,"GDn","ffgen(q,{v}): return a generator X mod P(X) for the finite field with q elements. If v is given, the variable name is used to display g, else the variable 'x' is used. Alternative syntax, q = P(X) an irreducible polynomial with t_INTMOD coefficients, return the generator X mod P(X) of the finite field defined by P. If v is given, the variable name is used to display g, else the variable of the polynomial P is used."},
+{"ffinit",0,(void*)ffinit,4,"GLDn","ffinit(p,n,{v='x}): monic irreducible polynomial of degree n over F_p[v]."},
+{"fflog",0,(void*)fflog,4,"GGDG","fflog(x,g,{o}): return the discrete logarithm of the finite field element x in base g. If present, o must represents the multiplicative order of g. If no o is given, assume that g is a primitive root."},
+{"ffnbirred",0,(void*)ffnbirred0,4,"GLD0,L,","ffnbirred(q,n{,fl=0}): number of monic irreducible polynomials over F_q, of degree n (fl=0, default) or at most n (fl=1)."},
+{"fforder",0,(void*)fforder,4,"GDG","fforder(x,{o}): multiplicative order of the finite field element x. Optional o represents a multiple of the order of the element."},
+{"ffprimroot",0,(void*)ffprimroot,4,"GD&","ffprimroot(x, {&o}): return a primitive root of the multiplicative group of the definition field of the finite field element x (not necessarily the same as the field generated by x). If present, o is set to [ord, fa], where ord is the order of the group, and fa its factorization (useful in fflog and fforder)."},
+{"fibonacci",0,(void*)fibo,4,"L","fibonacci(x): fibonacci number of index x (x C-integer)."},
+{"floor",0,(void*)gfloor,2,"G","floor(x): floor of x = largest integer <= x."},
+{"for",0,(void*)forpari,11,"vV=GGI","for(X=a,b,seq): the sequence is evaluated, X going from a up to b."},
+{"forcomposite",0,(void*)forcomposite,11,"vV=GDGI","forcomposite(n=a,{b},seq): the sequence is evaluated, n running over the composite numbers between a and b. Omitting b runs through composites >= a"},
+{"fordiv",0,(void*)fordiv,11,"vGVI","fordiv(n,X,seq): the sequence is evaluated, X running over the divisors of n."},
+{"forell",0,(void*)forell0,11,"vVLLI","forell(E,a,b,seq): execute seq for each elliptic curves E of conductor between a and b in the elldata database."},
+{"forpart",0,(void*)forpart0,11,"vV=GIDGDG","forpart(X=k,seq,{a=k},{n=k}): evaluate seq where the Vecsmall X goes over the partitions of k. Optional parameter n (n=nmax or n=[nmin,nmax]) restricts the length of the partition. Optional parameter a (a=amax or a=[amin,amax]) restricts the range of the parts. Zeros are removed unless one sets amin=0 to get X of fixed length nmax (=k by default)."},
+{"forprime",0,(void*)forprime,11,"vV=GDGI","forprime(p=a,{b},seq): the sequence is evaluated, p running over the primes between a and b. Omitting b runs through primes >= a"},
+{"forqfvec",0,(void*)forqfvec0,8,"vVGDGI","forqfvec(v,q,b,expr): q being a square and symmetric matrix representing a positive definite quadratic form, evaluate expr for all vector v such that q(v)<=b."},
+{"forstep",0,(void*)forstep,11,"vV=GGGI","forstep(X=a,b,s,seq): the sequence is evaluated, X going from a to b in steps of s (can be a vector of steps)."},
+{"forsubgroup",0,(void*)forsubgroup0,11,"vV=GDGI","forsubgroup(H=G,{bound},seq): execute seq for each subgroup H of the abelian group G, whose index is bounded by bound if not omitted. H is given as a left divisor of G in HNF form."},
+{"forvec",0,(void*)forvec,11,"vV=GID0,L,","forvec(X=v,seq,{flag=0}): v being a vector of two-component vectors of length n, the sequence is evaluated with X[i] going from v[i][1] to v[i][2] for i=n,..,1 if flag is zero or omitted. If flag = 1 (resp. flag = 2), restrict to increasing (resp. strictly increasing) sequences."},
+{"frac",0,(void*)gfrac,2,"G","frac(x): fractional part of x = x-floor(x)."},
+{"galoisexport",0,(void*)galoisexport,6,"GD0,L,","galoisexport(gal,{flag}): gal being a Galois group as output by galoisinit, output a string representing the underlying permutation group in GAP notation (default) or Magma notation (flag = 1)."},
+{"galoisfixedfield",0,(void*)galoisfixedfield,6,"GGD0,L,Dn","galoisfixedfield(gal,perm,{flag},{v=y}): gal being a Galois group as output by galoisinit and perm a subgroup, an element of gal.group or a vector of such elements, return [P,x] such that P is a polynomial defining the fixed field of gal[1] by the subgroup generated by perm, and x is a root of P in gal expressed as a polmod in gal.pol. If flag is 1 return only P. If flag is 2 return [P,x,F] where F is the factorization of gal.p [...]
+{"galoisgetpol",0,(void*)galoisgetpol,6,"LD0,L,D1,L,","galoisgetpol(a,{b},{s}): Query the galpol package for a polynomial with Galois group isomorphic to GAP4(a,b), totally real if s=1 (default) and totally complex if s=2.  The output is a vector [pol, den] where pol is the polynomial and den is the common denominator of the conjugates expressed as a polynomial in a root of pol. If b and s are omitted, return the number of isomorphism classes of groups of order a."},
+{"galoisidentify",0,(void*)galoisidentify,6,"G","galoisidentify(gal): gal being a Galois group as output by galoisinit, output the isomorphism class of the underlying abstract group as a two-components vector [o,i], where o is the group order, and i is the group index in the GAP4 small group library."},
+{"galoisinit",0,(void*)galoisinit,6,"GDG","galoisinit(pol,{den}): pol being a polynomial or a number field as output by nfinit defining a Galois extension of Q, compute the Galois group and all necessary information for computing fixed fields. den is optional and has the same meaning as in nfgaloisconj(,4)(see manual)."},
+{"galoisisabelian",0,(void*)galoisisabelian,6,"GD0,L,","galoisisabelian(gal,{flag=0}): gal being as output by galoisinit, return 0 if gal is not abelian, the HNF matrix of gal over gal.gen if flag=0, 1 if flag is 1, and the SNF of gal is flag=2."},
+{"galoisisnormal",0,(void*)galoisisnormal,6,"lGG","galoisisnormal(gal,subgrp): gal being as output by galoisinit, and subgrp a subgroup of gal as output by galoissubgroups, return 1 if subgrp is a normal subgroup of gal, else return 0."},
+{"galoispermtopol",0,(void*)galoispermtopol,6,"GG","galoispermtopol(gal,perm): gal being a Galois group as output by galoisinit and perm a element of gal.group, return the polynomial defining the corresponding Galois automorphism."},
+{"galoissubcyclo",0,(void*)galoissubcyclo,6,"GDGD0,L,Dn","galoissubcyclo(N,H,{fl=0},{v}):Compute a polynomial (in variable v) defining the subfield of Q(zeta_n) fixed by the subgroup H of (Z/nZ)*. N can be an integer n, znstar(n) or bnrinit(bnfinit(y),[n,[1]],1). H can be given by a generator, a set of generator given by a vector or a HNF matrix (see manual). If flag is 1, output only the conductor of the abelian extension. If flag is 2 output [pol,f] where pol is the polynomial and f th [...]
+{"galoissubfields",0,(void*)galoissubfields,6,"GD0,L,Dn","galoissubfields(G,{flags=0},{v}):Output all the subfields of G. flags have the same meaning as for galoisfixedfield."},
+{"galoissubgroups",0,(void*)galoissubgroups,6,"G","galoissubgroups(G):Output all the subgroups of G."},
+{"gamma",0,(void*)ggamma,3,"Gp","gamma(s): gamma function at s, a complex or p-adic number, or a series."},
+{"gammah",0,(void*)ggammah,3,"Gp","gammah(x): gamma of x+1/2 (x integer)."},
+{"gcd",0,(void*)ggcd0,4,"GDG","gcd(x,{y}): greatest common divisor of x and y."},
+{"gcdext",0,(void*)gcdext0,4,"GG","gcdext(x,y): returns [u,v,d] such that d=gcd(x,y) and u*x+v*y=d."},
+{"genus2red",0,(void*)genus2red,5,"GGDG","genus2red(Q,P,{p}): let Q,P be polynomials with integer coefficients. Determines the reduction at p > 2 of the (proper, smooth) hyperelliptic curve C/Q: y^2+Qy = P, of genus 2. (The special fiber X_p of the minimal regular model X of C over Z.)"},
+{"getabstime",0,(void*)getabstime,11,"l","getabstime(): time (in milliseconds) since startup."},
+{"getenv",0,(void*)gp_getenv,11,"s","getenv(s): value of the environment variable s, 0 if it is not defined."},
+{"getheap",0,(void*)getheap,11,"","getheap(): 2-component vector giving the current number of objects in the heap and the space they occupy."},
+{"getrand",0,(void*)getrand,11,"","getrand(): current value of random number seed."},
+{"getstack",0,(void*)getstack,11,"l","getstack(): current value of stack pointer avma."},
+{"gettime",0,(void*)gettime,11,"l","gettime(): time (in milliseconds) since last call to gettime."},
+{"global",0,NULL,11,NULL,"global(list of variables): obsolete. Scheduled for deletion."},
+{"hammingweight",0,(void*)hammingweight,2,"lG","hammingweight(x): returns the Hamming weight of x."},
+{"hilbert",0,(void*)hilbert,4,"lGGDG","hilbert(x,y,{p}): Hilbert symbol at p of x,y."},
+{"hyperu",0,(void*)hyperu,3,"GGGp","hyperu(a,b,x): U-confluent hypergeometric function."},
+{"idealadd",0,(void*)idealadd,6,"GGG","idealadd(nf,x,y): sum of two ideals x and y in the number field defined by nf."},
+{"idealaddtoone",0,(void*)idealaddtoone0,6,"GGDG","idealaddtoone(nf,x,{y}): if y is omitted, when the sum of the ideals in the number field K defined by nf and given in the vector x is equal to Z_K, gives a vector of elements of the corresponding ideals who sum to 1. Otherwise, x and y are ideals, and if they sum up to 1, find one element in each of them such that the sum is 1."},
+{"idealappr",0,(void*)idealappr0,6,"GGD0,L,","idealappr(nf,x,{flag=0}): x being a fractional ideal, gives an element b such that v_p(b)=v_p(x) for all prime ideals p dividing x, and v_p(b)>=0 for all other p. If (optional) flag is non-null x must be a prime ideal factorization with possibly zero exponents."},
+{"idealchinese",0,(void*)idealchinese,6,"GGG","idealchinese(nf,x,y): x being a prime ideal factorization and y a vector of elements, gives an element b such that v_p(b-y_p)>=v_p(x) for all prime ideals p dividing x, and v_p(b)>=0 for all other p."},
+{"idealcoprime",0,(void*)idealcoprime,6,"GGG","idealcoprime(nf,x,y): gives an element b in nf such that b. x is an integral ideal coprime to the integral ideal y."},
+{"idealdiv",0,(void*)idealdiv0,6,"GGGD0,L,","idealdiv(nf,x,y,{flag=0}): quotient x/y of two ideals x and y in HNF in the number field nf. If (optional) flag is non-null, the quotient is supposed to be an integral ideal (slightly faster)."},
+{"idealfactor",0,(void*)idealfactor,6,"GG","idealfactor(nf,x): factorization of the ideal x given in HNF into prime ideals in the number field nf."},
+{"idealfactorback",0,(void*)idealfactorback,6,"GGDGD0,L,","idealfactorback(nf,f,{e},{flag = 0}): given a factorisation f, gives the ideal product back. If e is present, f has to be a vector of the same length, and we return the product of the f[i]^e[i]. If flag is non-zero, perform idealred along the way."},
+{"idealfrobenius",0,(void*)idealfrobenius,6,"GGG","idealfrobenius(nf,gal,pr): Returns the Frobenius element (pr|nf/Q) associated with the unramified prime ideal pr in prid format, in the Galois group gal of the number field nf."},
+{"idealhnf",0,(void*)idealhnf0,6,"GGDG","idealhnf(nf,u,{v}): hermite normal form of the ideal u in the number field nf if v is omitted. If called as idealhnf(nf,u,v), the ideal is given as uZ_K + vZ_K in the number field K defined by nf."},
+{"idealintersect",0,(void*)idealintersect,6,"GGG","idealintersect(nf,A,B): intersection of two ideals A and B in the number field defined by nf."},
+{"idealinv",0,(void*)idealinv,6,"GG","idealinv(nf,x): inverse of the ideal x in the number field nf."},
+{"ideallist",0,(void*)ideallist0,6,"GLD4,L,","ideallist(nf,bound,{flag=4}): vector of vectors L of all idealstar of all ideals of norm<=bound. If (optional) flag is present, its binary digits are toggles meaning 1: give generators; 2: add units; 4: give only the ideals and not the bid."},
+{"ideallistarch",0,(void*)ideallistarch,6,"GGG","ideallistarch(nf,list,arch): list is a vector of vectors of of bid's as output by ideallist. Return a vector of vectors with the same number of components as the original list. The leaves give information about moduli whose finite part is as in original list, in the same order, and Archimedean part is now arch. The information contained is of the same kind as was present in the input."},
+{"ideallog",0,(void*)ideallog,6,"GGG","ideallog(nf,x,bid): if bid is a big ideal, as given by idealstar(nf,I,1) or idealstar(nf,I,2), gives the vector of exponents on the generators bid[2][3] (even if these generators have not been computed)."},
+{"idealmin",0,(void*)idealmin,6,"GGDG","idealmin(nf,ix,{vdir}): pseudo-minimum of the ideal ix in the direction vdir in the number field nf."},
+{"idealmul",0,(void*)idealmul0,6,"GGGD0,L,","idealmul(nf,x,y,{flag=0}): product of the two ideals x and y in the number field nf. If (optional) flag is non-nul, reduce the result."},
+{"idealnorm",0,(void*)idealnorm,6,"GG","idealnorm(nf,x): norm of the ideal x in the number field nf."},
+{"idealnumden",0,(void*)idealnumden,6,"GG","idealnumden(nf,x): returns [A,B], where A,B are coprime integer ideals such that x = A/B"},
+{"idealpow",0,(void*)idealpow0,6,"GGGD0,L,","idealpow(nf,x,k,{flag=0}): k-th power of the ideal x in HNF in the number field nf. If (optional) flag is non-null, reduce the result."},
+{"idealprimedec",0,(void*)idealprimedec,6,"GG","idealprimedec(nf,p): prime ideal decomposition of the prime number p in the number field nf as a vector of 5 component vectors [p,a,e,f,b] representing the prime ideals pZ_K+a. Z_K, e,f as usual, a as vector of components on the integral basis, b Lenstra's constant."},
+{"idealprincipalunits",0,(void*)idealprincipalunits,6,"GGL","idealprincipalunits(nf,pr,k): returns the structure [no, cyc, gen] of the multiplicative group (1 + pr) / (1 + pr^k)^*."},
+{"idealramgroups",0,(void*)idealramgroups,6,"GGG","idealramgroups(nf,gal,pr): let pr be a prime ideal in prid format, and gal the Galois group of the number field nf, return a vector g such that g[1] is the decomposition group of pr, g[2] is the inertia group, g[i] is the (i-2)th ramification group of pr, all trivial subgroups being omitted."},
+{"idealred",0,(void*)idealred0,6,"GGDG","idealred(nf,I,{v=0}): LLL reduction of the ideal I in the number field nf along direction v, in HNF."},
+{"idealstar",0,(void*)idealstar0,6,"GGD1,L,","idealstar(nf,I,{flag=1}): gives the structure of (Z_K/I)^*. flag is optional, and can be 0: simply gives the structure as a 3-component vector v such that v[1] is the order (i.e. eulerphi(I)), v[2] is a vector of cyclic components, and v[3] is a vector giving the corresponding generators. If flag=1 (default), gives idealstarinit, i.e. a 6-component vector [I,v,fa,f2,U,V] where v is as above without the generators, fa is the prime ideal factor [...]
+{"idealtwoelt",0,(void*)idealtwoelt0,6,"GGDG","idealtwoelt(nf,x,{a}): two-element representation of an ideal x in the number field nf. If (optional) a is non-zero, first element will be equal to a."},
+{"idealval",0,(void*)idealval,6,"lGGG","idealval(nf,x,pr): valuation at pr given in idealprimedec format of the ideal x in the number field nf."},
+{"if",0,(void*)ifpari,11,"GDEDE","if(a,{seq1},{seq2}): if a is nonzero, seq1 is evaluated, otherwise seq2. seq1 and seq2 are optional, and if seq2 is omitted, the preceding comma can be omitted also."},
+{"iferr",0,(void*)iferrpari,11,"EVEDE","iferr(seq1,E,seq2{,pred}): evaluates the expression sequence seq1. If an error occurs, set the formal parameter E set to the error data. If pred is not present or evaluates to true, catch the error and evaluate seq2. Both pred and seq2 can reference E."},
+{"imag",0,(void*)gimag,2,"G","imag(x): imaginary part of x."},
+{"incgam",0,(void*)incgam0,3,"GGDGp","incgam(s,x,{g}): incomplete gamma function. g is optional and is the precomputed value of gamma(s)."},
+{"incgamc",0,(void*)incgamc,3,"GGp","incgamc(s,x): complementary incomplete gamma function."},
+{"inline",0,NULL,11,NULL,"inline(x,...,z): declares x,...,z as inline variables [EXPERIMENTAL]"},
+{"install",0,(void*)gpinstall,11,"vrrD\"\",r,D\"\",s,","install(name,code,{gpname},{lib}): load from dynamic library 'lib' the function 'name'. Assign to it the name 'gpname' in this GP session, with prototype 'code'. If 'lib' is omitted, all symbols known to gp (includes the whole 'libpari.so' and possibly others) are available. If 'gpname' is omitted, use 'name'."},
+{"intcirc",0,(void*)intcirc0,9,"V=GGEDGp","intcirc(X=a,R,expr,{tab}): numerical integration of expr on the circle |z-a|=R, divided by 2*I*Pi. tab is as in intnum."},
+{"intformal",0,(void*)integ,7,"GDn","intformal(x,{v}): formal integration of x with respect to v, or to the main variable of x if v is omitted."},
+{"intfouriercos",0,(void*)intfourcos0,9,"V=GGGEDGp","intfouriercos(X=a,b,z,expr,{tab}): numerical integration from a to b of cos(2*Pi*z*X)*expr(X) from a to b, where a, b, and tab are as in intnum. This is the cosine-Fourier transform if a=-infty and b=+infty."},
+{"intfourierexp",0,(void*)intfourexp0,9,"V=GGGEDGp","intfourierexp(X=a,b,z,expr,{tab}): numerical integration from a to b of exp(-2*I*Pi*z*X)*expr(X) from a to b, where a, b, and tab are as in intnum. This is the ordinary Fourier transform if a=-infty and b=+infty. Note the minus sign."},
+{"intfouriersin",0,(void*)intfoursin0,9,"V=GGGEDGp","intfouriersin(X=a,b,z,expr,{tab}): numerical integration from a to b of sin(2*Pi*z*X)*expr(X) from a to b, where a, b, and tab are as in intnum. This is the sine-Fourier transform if a=-infty and b=+infty."},
+{"intfuncinit",0,(void*)intfuncinit0,9,"V=GGED0,L,D0,L,p","intfuncinit(X=a,b,expr,{flag=0},{m=0}): initialize tables for integrations from a to b using a weight expr(X). Essential for integral transforms such as intmellininv, intlaplaceinv and intfourier, since it avoids recomputing all the time the same quantities. Must then be used with intmellininvshort (for intmellininv) and directly with intnum and not with the corresponding integral transforms for the others. See help for intnum fo [...]
+{"intlaplaceinv",0,(void*)intlaplaceinv0,9,"V=GGEDGp","intlaplaceinv(X=sig,z,expr,{tab}): numerical integration on the line real(X) = sig of expr(X)exp(zX)dz/(2*I*Pi), i.e. inverse Laplace transform of expr at z. tab is as in intnum."},
+{"intmellininv",0,(void*)intmellininv0,9,"V=GGEDGp","intmellininv(X=sig,z,expr,{tab}): numerical integration on the line real(X) = sig (or sig[1]) of expr(X)z^(-X)dX/(2*I*Pi), i.e. inverse Mellin transform of s at x. sig is coded as follows: either it is real, and then by default assume s(z) decreases like exp(-z). Or sig = [sigR, al], sigR is the abscissa of integration, and al = 0 for slowly decreasing functions, or al > 0 if s(z) decreases like exp(-al*z). tab is as in intnum. Use int [...]
+{"intmellininvshort",0,(void*)intmellininvshort,9,"GGGp","intmellininvshort(sig,z,tab): numerical integration on the line real(X) = sig (or sig[1]) of s(X)z^(-X)dX/(2*I*Pi), i.e. inverse Mellin transform of s at z. sig is coded as follows: either it is real, and then by default assume s(X) decreases like exp(-X). Or sig = [sigR, al], sigR is the abscissa of integration, and al = 0 for slowly decreasing functions, or al > 0 if s(X) decreases like exp(-al*X). Compulsory table tab has been  [...]
+{"intnum",0,(void*)intnum0,9,"V=GGEDGp","intnum(X=a,b,expr,{tab}): numerical integration of expr from a to b with respect to X. Plus/minus infinity is coded as [+1]/ [-1]. Finally tab is either omitted (let the program choose the integration step), a positive integer m (choose integration step 1/2^m), or data precomputed with intnuminit."},
+{"intnuminit",0,(void*)intnuminit,9,"GGD0,L,p","intnuminit(a,b,{m=0}): initialize tables for integrations from a to b. See help for intnum for coding of a and b. Possible types: compact interval, semi-compact (one extremity at + or - infinity) or R, and very slowly, slowly or exponentially decreasing, or sine or cosine oscillating at infinities."},
+{"intnuminitgen",0,(void*)intnuminitgen0,9,"VGGED0,L,D0,L,p","intnuminitgen(t,a,b,ph,{m=0},{flag=0}): initialize tables for integrations from a to b using abscissas ph(t) and weights ph'(t). Note that there is no equal sign after the variable name t since t always goes from -infty to +infty, but it is ph(t) which goes from a to b, and this is not checked. If flag = 1 or 2, multiply the reserved table length by 4^flag, to avoid corresponding error."},
+{"intnumromb",0,(void*)intnumromb0,9,"V=GGED0,L,p","intnumromb(X=a,b,expr,{flag=0}): numerical integration of expr (smooth in ]a,b[) from a to b with respect to X. flag is optional and mean 0: default. expr can be evaluated exactly on [a,b]; 1: general function; 2: a or b can be plus or minus infinity (chosen suitably), but of same sign; 3: expr has only limits at a or b."},
+{"intnumstep",0,(void*)intnumstep,9,"lp","intnumstep(): gives the default value of m used by all intnum and sumnum routines, such that the integration step is 1/2^m."},
+{"isfundamental",0,(void*)isfundamental,4,"lG","isfundamental(x): true(1) if x is a fundamental discriminant (including 1), false(0) if not."},
+{"ispolygonal",0,(void*)ispolygonal,4,"lGGD&","ispolygonal(x,s,{&N}): true(1) if x is an s-gonal number, false(0) if not (s > 2). If N is given set it to n if x is the n-th s-gonal number."},
+{"ispower",0,(void*)ispower,4,"lGDGD&","ispower(x,{k},{&n}): if k > 0 is given, return true (1) if x is a k-th power, false (0) if not. If k is omitted, return the maximal k >= 2 such that x = n^k is a perfect power, or 0 if no such k exist. If n is present, and the function returns a non-zero result, set n to the k-th root of x."},
+{"ispowerful",0,(void*)ispowerful,4,"lG","ispowerful(x): true(1) if x is a powerful integer (valuation at all primes is greater than 1), false(0) if not."},
+{"isprime",0,(void*)gisprime,4,"GD0,L,","isprime(x,{flag=0}): true(1) if x is a (proven) prime number, false(0) if not. If flag is 0 or omitted, use a combination of algorithms. If flag is 1, the primality is certified by the Pocklington-Lehmer Test. If flag is 2, the primality is certified using the APRCL test."},
+{"isprimepower",0,(void*)isprimepower,4,"lGD&","isprimepower(x,{&n}): if x = p^k is a prime power (p prime, k > 0), return k, else return 0. If n is present, and the function returns a non-zero result, set n to p, the k-th root of x."},
+{"ispseudoprime",0,(void*)gispseudoprime,4,"GD0,L,","ispseudoprime(x,{flag}): true(1) if x is a strong pseudoprime, false(0) if not. If flag is 0 or omitted, use BPSW test, otherwise use strong Rabin-Miller test for flag randomly chosen bases."},
+{"issquare",0,(void*)issquareall,4,"lGD&","issquare(x,{&n}): true(1) if x is a square, false(0) if not. If n is given puts the exact square root there if it was computed."},
+{"issquarefree",0,(void*)issquarefree,4,"lG","issquarefree(x): true(1) if x is squarefree, false(0) if not."},
+{"istotient",0,(void*)istotient,4,"lGD&","istotient(x,{&N}): true(1) if x = eulerphi(n) for some integer n, false(0) if not. If N is given, set N = n as well."},
+{"kill",0,(void*)kill0,11,"vr","kill(sym): restores the symbol sym to its ``undefined'' status and kill associated help messages."},
+{"kronecker",0,(void*)kronecker,4,"lGG","kronecker(x,y): kronecker symbol (x/y)."},
+{"lambertw",0,(void*)glambertW,3,"Gp","lambertw(y): solution of the implicit equation x*exp(x)=y."},
+{"lcm",0,(void*)glcm0,4,"GDG","lcm(x,{y}): least common multiple of x and y, i.e. x*y / gcd(x,y)."},
+{"length",0,(void*)glength,2,"lG","length(x): number of non code words in x, number of characters for a string."},
+{"lex",0,(void*)lexcmp,1,"iGG","lex(x,y): compare x and y lexicographically (1 if x>y, 0 if x=y, -1 if x<y)"},
+{"lift",0,(void*)lift0,2,"GDn","lift(x,{v}): if v is omitted, lifts elements of Z/nZ to Z, of Qp to Q, and of K[x]/(P) to K[x]. Otherwise lift only polmods with main variable v."},
+{"liftall",0,(void*)liftall,2,"G","liftall(x): lifts every element of Z/nZ to Z, of Qp to Q, and of K[x]/(P) to K[x]."},
+{"liftint",0,(void*)liftint,2,"G","liftint(x): lifts every element of Z/nZ to Z, of Qp to Q, and of K[x]/(P) to K[x]."},
+{"liftpol",0,(void*)liftpol,2,"G","liftpol(x): lifts every polmod component of x to polynomials"},
+{"lindep",0,(void*)lindep0,8,"GD0,L,","lindep(v,{flag=0}): integral linear dependencies between components of v. flag is optional, and can be 0: default, guess a suitable accuracy, or positive: accuracy to use for the computation, in decimal digits."},
+{"listcreate",0,(void*)listcreate,8,"D0,L,","listcreate(): creates an empty list."},
+{"listinsert",0,(void*)listinsert,8,"WGL","listinsert(L,x,n): insert x at index n in list L, shifting the remaining elements to the right."},
+{"listkill",0,(void*)listkill,8,"vG","listkill(L): obsolete, retained for backward compatibility."},
+{"listpop",0,(void*)listpop,8,"vWD0,L,","listpop(list,{n}): removes n-th element from list. If n is omitted or greater than the current list length, removes last element."},
+{"listput",0,(void*)listput,8,"WGD0,L,","listput(list,x,{n}): sets n-th element of list equal to x. If n is omitted or greater than the current list length, appends x."},
+{"listsort",0,(void*)listsort,8,"vWD0,L,","listsort(L,{flag=0}): sort the list L in place. If flag is non-zero, suppress all but one occurence of each element in list."},
+{"lngamma",0,(void*)glngamma,3,"Gp","lngamma(x): logarithm of the gamma function of x."},
+{"local",0,NULL,11,NULL,"local(x,...,z): declare x,...,z as (dynamically scoped) local variables."},
+{"log",0,(void*)glog,3,"Gp","log(x): natural logarithm of x."},
+{"logint",0,(void*)logint0,4,"lGGD&","logint(x,b,&z): return the largest integer e so that b^e <= x, where the parameters b > 1 and x > 0 are both integers. If the parameter z is present, set it to b^e."},
+{"matadjoint",0,(void*)matadjoint0,8,"GD0,L,","matadjoint(M,{flag=0}): adjoint matrix of M using Leverrier-Faddeev's algorithm. If flag is 1, compute the characteristic polynomial independently first."},
+{"matalgtobasis",0,(void*)matalgtobasis,6,"GG","matalgtobasis(nf,x): nfalgtobasis applied to every element of the vector or matrix x."},
+{"matbasistoalg",0,(void*)matbasistoalg,6,"GG","matbasistoalg(nf,x): nfbasistoalg applied to every element of the matrix or vector x."},
+{"matcompanion",0,(void*)matcompanion,8,"G","matcompanion(x): companion matrix to polynomial x."},
+{"matconcat",0,(void*)matconcat,8,"G","matconcat(v): concatenate the entries of v and return the resulting matrix"},
+{"matdet",0,(void*)det0,8,"GD0,L,","matdet(x,{flag=0}): determinant of the matrix x using an appropriate algorithm depending on the coefficients. If (optional) flag is set to 1, use classical Gaussian elimination (usually worse than the default)."},
+{"matdetint",0,(void*)detint,8,"G","matdetint(B): some multiple of the determinant of the lattice generated by the columns of B (0 if not of maximal rank). Useful with mathnfmod."},
+{"matdiagonal",0,(void*)diagonal,8,"G","matdiagonal(x): creates the diagonal matrix whose diagonal entries are the entries of the vector x."},
+{"mateigen",0,(void*)mateigen,8,"GD0,L,p","mateigen(x,{flag=0}): complex eigenvectors of the matrix x given as columns of a matrix H. If flag=1, return [L,H], where L contains the eigenvalues and H the corresponding eigenvectors."},
+{"matfrobenius",0,(void*)matfrobenius,8,"GD0,L,Dn","matfrobenius(M,{flag},{v='x}): Return the Frobenius form of the square matrix M. If flag is 1, return only the elementary divisors as a vector of polynomials in the variable v. If flag is 2, return a two-components vector [F,B] where F is the Frobenius form and B is the basis change so that M=B^-1*F*B."},
+{"mathess",0,(void*)hess,8,"G","mathess(x): Hessenberg form of x."},
+{"mathilbert",0,(void*)mathilbert,8,"L","mathilbert(n): Hilbert matrix of order n."},
+{"mathnf",0,(void*)mathnf0,8,"GD0,L,","mathnf(M,{flag=0}): (upper triangular) Hermite normal form of M, basis for the lattice formed by the columns of M. flag is optional whose value range from 0 to 3 have a binary meaning. Bit 1: complete output, returns a 2-component vector [H,U] such that H is the HNF of M, and U is an invertible matrix such that MU=H. Bit 2: allow polynomial entries, otherwise assume that M is integral. These use a naive algorithm; larger values correspond to more in [...]
+{"mathnfmod",0,(void*)hnfmod,8,"GG","mathnfmod(x,d): (upper triangular) Hermite normal form of x, basis for the lattice formed by the columns of x, where d is a multiple of the non-zero determinant of this lattice."},
+{"mathnfmodid",0,(void*)hnfmodid,8,"GG","mathnfmodid(x,d): (upper triangular) Hermite normal form of x concatenated with matdiagonal(d)"},
+{"mathouseholder",0,(void*)mathouseholder,8,"GG","mathouseholder(Q,v): applies a sequence Q of Householder transforms to the vector or matrix v."},
+{"matid",0,(void*)matid,8,"L","matid(n): identity matrix of order n."},
+{"matimage",0,(void*)matimage0,8,"GD0,L,","matimage(x,{flag=0}): basis of the image of the matrix x. flag is optional and can be set to 0 or 1, corresponding to two different algorithms."},
+{"matimagecompl",0,(void*)imagecompl,8,"G","matimagecompl(x): vector of column indices not corresponding to the indices given by the function matimage."},
+{"matindexrank",0,(void*)indexrank,8,"G","matindexrank(x): gives two extraction vectors (rows and columns) for the matrix x such that the extracted matrix is square of maximal rank."},
+{"matintersect",0,(void*)intersect,8,"GG","matintersect(x,y): intersection of the vector spaces whose bases are the columns of x and y."},
+{"matinverseimage",0,(void*)inverseimage,8,"GG","matinverseimage(x,y): an element of the inverse image of the vector y by the matrix x if one exists, the empty vector otherwise."},
+{"matisdiagonal",0,(void*)isdiagonal,8,"iG","matisdiagonal(x): true(1) if x is a diagonal matrix, false(0) otherwise."},
+{"matker",0,(void*)matker0,8,"GD0,L,","matker(x,{flag=0}): basis of the kernel of the matrix x. flag is optional, and may be set to 0: default; non-zero: x is known to have integral entries."},
+{"matkerint",0,(void*)matkerint0,8,"GD0,L,","matkerint(x,{flag=0}): LLL-reduced Z-basis of the kernel of the matrix x with integral entries. flag is optional, and may be set to 0: default, uses LLL, 1: uses matrixqz (much slower)."},
+{"matmuldiagonal",0,(void*)matmuldiagonal,8,"GG","matmuldiagonal(x,d): product of matrix x by diagonal matrix whose diagonal coefficients are those of the vector d, equivalent but faster than x*matdiagonal(d)."},
+{"matmultodiagonal",0,(void*)matmultodiagonal,8,"GG","matmultodiagonal(x,y): product of matrices x and y, knowing that the result will be a diagonal matrix. Much faster than general multiplication in that case."},
+{"matpascal",0,(void*)matqpascal,8,"LDG","matpascal(n,{q}): Pascal triangle of order n if q is omitted. q-Pascal triangle otherwise."},
+{"matqr",0,(void*)matqr,8,"GD0,L,p","matqr(M,{flag=0}): returns [Q,R], the QR-decomposition of the square invertible matrix M. If flag=1, Q is given as a sequence of Householder transforms (faster and stabler)."},
+{"matrank",0,(void*)rank,8,"lG","matrank(x): rank of the matrix x."},
+{"matrix",0,(void*)matrice,8,"GGDVDVDE","matrix(m,n,{X},{Y},{expr=0}): mXn matrix of expression expr, the row variable X going from 1 to m and the column variable Y going from 1 to n. By default, fill with 0s."},
+{"matrixqz",0,(void*)matrixqz0,8,"GDG","matrixqz(A,{p=0}): if p>=0, transforms the rational or integral mxn (m>=n) matrix A into an integral matrix with gcd of maximal determinants coprime to p. If p=-1, finds a basis of the intersection with Z^n of the lattice spanned by the columns of A. If p=-2, finds a basis of the intersection with Z^n of the Q-vector space spanned by the columns of A."},
+{"matsize",0,(void*)matsize,8,"G","matsize(x): number of rows and columns of the vector/matrix x as a 2-vector."},
+{"matsnf",0,(void*)matsnf0,8,"GD0,L,","matsnf(X,{flag=0}): Smith normal form (i.e. elementary divisors) of the matrix X, expressed as a vector d. Binary digits of flag mean 1: returns [u,v,d] where d=u*X*v, otherwise only the diagonal d is returned, 2: allow polynomial entries, otherwise assume X is integral, 4: removes all information corresponding to entries equal to 1 in d."},
+{"matsolve",0,(void*)gauss,8,"GG","matsolve(M,B): solution of MX=B (M matrix, B column vector)."},
+{"matsolvemod",0,(void*)matsolvemod0,8,"GGGD0,L,","matsolvemod(M,D,B,{flag=0}): one solution of system of congruences MX=B mod D (M matrix, B and D column vectors). If (optional) flag is non-null return all solutions."},
+{"matsupplement",0,(void*)suppl,8,"G","matsupplement(x): supplement the columns of the matrix x to an invertible matrix."},
+{"mattranspose",0,(void*)gtrans,8,"G","mattranspose(x): x~ = transpose of x."},
+{"max",0,(void*)gmax,1,"GG","max(x,y): maximum of x and y"},
+{"min",0,(void*)gmin,1,"GG","min(x,y): minimum of x and y"},
+{"minpoly",0,(void*)minpoly,8,"GDn","minpoly(A,{v='x}): minimal polynomial of the matrix or polmod A."},
+{"modreverse",0,(void*)modreverse,6,"G","modreverse(z): reverse polmod of the polmod z, if it exists."},
+{"moebius",0,(void*)moebius,4,"lG","moebius(x): Moebius function of x."},
+{"my",0,NULL,11,NULL,"my(x,...,z): declare x,...,z as lexically-scoped local variables."},
+{"newtonpoly",0,(void*)newtonpoly,6,"GG","newtonpoly(x,p): Newton polygon of polynomial x with respect to the prime p."},
+{"next",0,(void*)next0,11,"D1,L,","next({n=1}): interrupt execution of current instruction sequence, and start another iteration from the n-th innermost enclosing loops."},
+{"nextprime",0,(void*)nextprime,4,"G","nextprime(x): smallest pseudoprime >= x."},
+{"nfalgtobasis",0,(void*)algtobasis,6,"GG","nfalgtobasis(nf,x): transforms the algebraic number x into a column vector on the integral basis nf.zk."},
+{"nfbasis",0,(void*)nfbasis_gp,6,"GDGDG","nfbasis(T): integral basis of the field Q[a], where a is a root of the polynomial T, using the round 4 algorithm. An argument [T,listP] is possible, where listP is a list of primes (to get an order which is maximal at certain primes only) or a prime bound."},
+{"nfbasistoalg",0,(void*)basistoalg,6,"GG","nfbasistoalg(nf,x): transforms the column vector x on the integral basis into an algebraic number."},
+{"nfcertify",0,(void*)nfcertify,6,"G","nfcertify(nf): returns a vector of composite integers used to certify nf.zk and nf.disc unconditionally (both are correct when the output is the empty vector)."},
+{"nfdetint",0,(void*)nfdetint,6,"GG","nfdetint(nf,x): multiple of the ideal determinant of the pseudo generating set x."},
+{"nfdisc",0,(void*)nfdisc_gp,6,"GDGDG","nfdisc(T): discriminant of the number field defined by the polynomial T. An argument [T,listP] is possible, where listP is a list of primes or a prime bound."},
+{"nfeltadd",0,(void*)nfadd,6,"GGG","nfadd(nf,x,y): element x+y in nf."},
+{"nfeltdiv",0,(void*)nfdiv,6,"GGG","nfdiv(nf,x,y): element x/y in nf."},
+{"nfeltdiveuc",0,(void*)nfdiveuc,6,"GGG","nfdiveuc(nf,x,y): gives algebraic integer q such that x-by is small."},
+{"nfeltdivmodpr",0,(void*)nfdivmodpr,6,"GGGG","nfeltdivmodpr(nf,x,y,pr): element x/y modulo pr in nf, where pr is in modpr format (see nfmodprinit)."},
+{"nfeltdivrem",0,(void*)nfdivrem,6,"GGG","nfeltdivrem(nf,x,y): gives [q,r] such that r=x-by is small."},
+{"nfeltmod",0,(void*)nfmod,6,"GGG","nfeltmod(nf,x,y): gives r such that r=x-by is small with q algebraic integer."},
+{"nfeltmul",0,(void*)nfmul,6,"GGG","nfmul(nf,x,y): element x.y in nf."},
+{"nfeltmulmodpr",0,(void*)nfmulmodpr,6,"GGGG","nfeltmulmodpr(nf,x,y,pr): element x.y modulo pr in nf, where pr is in modpr format (see nfmodprinit)."},
+{"nfeltnorm",0,(void*)nfnorm,6,"GG","nfeltnorm(nf,x): norm of x."},
+{"nfeltpow",0,(void*)nfpow,6,"GGG","nfeltpow(nf,x,k): element x^k in nf."},
+{"nfeltpowmodpr",0,(void*)nfpowmodpr,6,"GGGG","nfeltpowmodpr(nf,x,k,pr): element x^k modulo pr in nf, where pr is in modpr format (see nfmodprinit)."},
+{"nfeltreduce",0,(void*)nfreduce,6,"GGG","nfeltreduce(nf,a,id): gives r such that a-r is in the ideal id and r is small."},
+{"nfeltreducemodpr",0,(void*)nfreducemodpr,6,"GGG","nfeltreducemodpr(nf,x,pr): element x modulo pr in nf, where pr is in modpr format (see nfmodprinit)."},
+{"nfelttrace",0,(void*)nftrace,6,"GG","nfelttrace(nf,x): trace of x."},
+{"nfeltval",0,(void*)nfval,6,"lGGG","nfeltval(nf,x,pr): valuation of element x at the prime pr as output by idealprimedec."},
+{"nffactor",0,(void*)nffactor,6,"GG","nffactor(nf,T): factor polynomial T in number field nf."},
+{"nffactorback",0,(void*)nffactorback,6,"GGDG","nffactorback(nf,f,{e}): given a factorisation f, returns the factored object back as an nf element."},
+{"nffactormod",0,(void*)nffactormod,6,"GGG","nffactormod(nf,Q,pr): factor polynomial Q modulo prime ideal pr in number field nf."},
+{"nfgaloisapply",0,(void*)galoisapply,6,"GGG","nfgaloisapply(nf,aut,x): Apply the Galois automorphism aut to the object x (element or ideal) in the number field nf."},
+{"nfgaloisconj",0,(void*)galoisconj0,6,"GD0,L,DGp","nfgaloisconj(nf,{flag=0},{d}): list of conjugates of a root of the polynomial x=nf.pol in the same number field. flag is optional (set to 0 by default), meaning 0: use combination of flag 4 and 1, always complete; 1: use nfroots; 2 : use complex numbers, LLL on integral basis (not always complete); 4: use Allombert's algorithm, complete if the field is Galois of degree <= 35 (see manual for details). nf can be simply a polynomial."},
+{"nfhilbert",0,(void*)nfhilbert0,6,"lGGGDG","nfhilbert(nf,a,b,{pr}): if pr is omitted, global Hilbert symbol (a,b) in nf, that is 1 if X^2-aY^2-bZ^2 has a non-trivial solution (X,Y,Z) in nf, -1 otherwise. Otherwise compute the local symbol modulo the prime ideal pr."},
+{"nfhnf",0,(void*)nfhnf,6,"GG","nfhnf(nf,x): if x=[A,I], gives a pseudo-basis of the module sum A_jI_j"},
+{"nfhnfmod",0,(void*)nfhnfmod,6,"GGG","nfhnfmod(nf,x,detx): if x=[A,I], and detx is a multiple of the ideal determinant of x, gives a pseudo-basis of the module sum A_jI_j."},
+{"nfinit",0,(void*)nfinit0,6,"GD0,L,p","nfinit(pol,{flag=0}): pol being a nonconstant irreducible polynomial, gives the vector: [pol,[r1,r2],discf,index,[M,MC,T2,T,different] (see manual),r1+r2 first roots, integral basis, matrix of power basis in terms of integral basis, multiplication table of basis]. flag is optional and can be set to 0: default; 1: do not compute different; 2: first use polred to find a simpler polynomial; 3: outputs a two-element vector [nf,Mod(a,P)], where nf is as [...]
+{"nfisideal",0,(void*)isideal,6,"lGG","nfisideal(nf,x): true(1) if x is an ideal in the number field nf, false(0) if not."},
+{"nfisincl",0,(void*)nfisincl,6,"GG","nfisincl(x,y): tests whether the number field x is isomorphic to a subfield of y (where x and y are either polynomials or number fields as output by nfinit). Return 0 if not, and otherwise all the isomorphisms. If y is a number field, a faster algorithm is used."},
+{"nfisisom",0,(void*)nfisisom,6,"GG","nfisisom(x,y): as nfisincl but tests whether x is isomorphic to y."},
+{"nfkermodpr",0,(void*)nfkermodpr,6,"GGG","nfkermodpr(nf,x,pr): kernel of the matrix x in Z_K/pr, where pr is in modpr format (see nfmodprinit)."},
+{"nfmodprinit",0,(void*)nfmodprinit,6,"GG","nfmodprinit(nf,pr): transform the 5 element row vector pr representing a prime ideal into modpr format necessary for all operations mod pr in the number field nf (see manual for details about the format)."},
+{"nfnewprec",0,(void*)nfnewprec,6,"Gp","nfnewprec(nf): transform the number field data nf into new data using the current (usually larger) precision."},
+{"nfroots",0,(void*)nfroots,6,"DGG","nfroots({nf},x): roots of polynomial x belonging to nf (Q if omitted) without multiplicity."},
+{"nfrootsof1",0,(void*)rootsof1,6,"G","nfrootsof1(nf): number of roots of unity and primitive root of unity in the number field nf."},
+{"nfsnf",0,(void*)nfsnf,6,"GG","nfsnf(nf,x): if x=[A,I,J], outputs [c_1,...c_n] Smith normal form of x."},
+{"nfsolvemodpr",0,(void*)nfsolvemodpr,6,"GGGG","nfsolvemodpr(nf,a,b,P): solution of a*x=b in Z_K/P, where a is a matrix and b a column vector, and where P is in modpr format (see nfmodprinit)."},
+{"nfsubfields",0,(void*)nfsubfields,6,"GD0,L,","nfsubfields(pol,{d=0}): find all subfields of degree d of number field defined by pol (all subfields if d is null or omitted). Result is a vector of subfields, each being given by [g,h], where g is an absolute equation and h expresses one of the roots of g in terms of the root x of the polynomial defining nf."},
+{"norm",0,(void*)gnorm,2,"G","norm(x): norm of x."},
+{"norml2",0,(void*)gnorml2,8,"G","norml2(x): square of the L2-norm of x."},
+{"normlp",0,(void*)gnormlp,8,"GDGp","normlp(x,{p}): Lp-norm of x; sup norm if p is omitted."},
+{"numbpart",0,(void*)numbpart,4,"G","numbpart(n): number of partitions of n."},
+{"numdiv",0,(void*)numdiv,4,"G","numdiv(x): number of divisors of x."},
+{"numerator",0,(void*)numer,2,"G","numerator(x): numerator of x."},
+{"numtoperm",0,(void*)numtoperm,2,"LG","numtoperm(n,k): permutation number k (mod n!) of n letters (n C-integer)."},
+{"omega",0,(void*)omega,4,"lG","omega(x): number of distinct prime divisors of x."},
+{"padicappr",0,(void*)padicappr,7,"GG","padicappr(pol,a): p-adic roots of the polynomial pol congruent to a mod p."},
+{"padicfields",0,(void*)padicfields0,7,"GGD0,L,","padicfields(p, N, {flag=0}): returns polynomials generating all the extensions of degree N of the field of p-adic rational numbers; N is allowed to be a 2-component vector [n,d], in which case, returns the extensions of degree n and discriminant p^d. flag is optional, and can be 0: default, 1: return also the ramification index, the residual degree, the valuation of the discriminant and the number of conjugate fields, or 2: return only th [...]
+{"padicprec",0,(void*)padicprec,2,"lGG","padicprec(x,p): absolute p-adic precision of object x."},
+{"parapply",0,(void*)parapply,11,"GG","parapply(f, x): parallel evaluation of f on the elements of x."},
+{"pareval",0,(void*)pareval,11,"G","pareval(x): parallel evaluation of the elements of the vector of closures x."},
+{"parfor",0,(void*)parfor,11,"vV=GDGJDVDI","parfor(i=a,{b},expr1,{j},{expr2}): evaluates the sequence expr2 (dependent on i and j) for i between a and b, in random order, computed in parallel. Substitute for j the value of expr1 (dependent on i). If b is omitted, the loop will not stop."},
+{"parforprime",0,(void*)parforprime,11,"vV=GDGJDVDI","parforprime(p=a,{b},expr1,{j},{expr2}): evaluates the sequence expr2 (dependent on p and j) for p prime between a and b, in random order, computed in parallel. Substitute for j the value of expr1 (dependent on i). If b is omitted, the loop will not stop."},
+{"parselect",0,(void*)parselect,11,"GGD0,L,","parselect(f, A, {flag = 0}): (parallel select) selects elements of A according to the selection function f which is tested in parallel. If flag is 1, return the indices of those elements (indirect selection)"},
+{"parsum",0,(void*)parsum,11,"V=GGJDG","parsum(i=a,b,expr,{x}): x plus the sum (X goes from a to b) of expression expr, evaluated in parallel (in random order)"},
+{"partitions",0,(void*)partitions,4,"LDGDG","partitions(k,{a=k},{n=k})): vector of partitions of the integer k. You can restrict the length of the partitions with parameter n (n=nmax or n=[nmin,nmax]), or the range of the parts with parameter a (a=amax or a=[amin,amax]). By default remove zeros, but one can set amin=0 to get X of fixed length nmax (=k by default)."},
+{"parvector",0,(void*)parvector,11,"LVJ","parvector(N,i,expr): as vector(N,i,expr) but the evaluations of expr are done in parallel."},
+{"permtonum",0,(void*)permtonum,2,"G","permtonum(x): ordinal (between 1 and n!) of permutation x."},
+{"polchebyshev",0,(void*)polchebyshev_eval,7,"LD1,L,DG","polchebyshev(n,{flag=1},{a='x}): Chebychev polynomial of the first (flag = 1) or second (flag = 2) kind, of degree n, evaluated at a."},
+{"polcoeff",0,(void*)polcoeff0,7,"GLDn","polcoeff(x,n,{v}): coefficient of degree n of x, or the n-th component for vectors or matrices (for which it is simpler to use x[]). With respect to the main variable if v is omitted, with respect to the variable v otherwise."},
+{"polcompositum",0,(void*)polcompositum0,6,"GGD0,L,","polcompositum(P,Q,{flag=0}): vector of all possible compositums of the number fields defined by the polynomials P and Q. If (optional) flag is set (i.e non-null), output for each compositum, not only the compositum polynomial pol, but a vector [R,a,b,k] where a (resp. b) is a root of P (resp. Q) expressed as a polynomial modulo R, and a small integer k such that al2+k*al1 is the chosen root of R."},
+{"polcyclo",0,(void*)polcyclo_eval,7,"LDG","polcyclo(n,{a = 'x}): n-th cyclotomic polynomial evaluated at a."},
+{"polcyclofactors",0,(void*)polcyclofactors,7,"G","polcyclofactors(f): returns a vector of polynomials, whose product is the product of distinct cyclotomic polynomials dividing f."},
+{"poldegree",0,(void*)poldegree,7,"lGDn","poldegree(x,{v}): degree of the polynomial or rational function x with respect to main variable if v is omitted, with respect to v otherwise. For scalar x, return 0 is x is non-zero and a negative number otherwise."},
+{"poldisc",0,(void*)poldisc0,7,"GDn","poldisc(pol,{v}): discriminant of the polynomial pol, with respect to main variable if v is omitted, with respect to v otherwise."},
+{"poldiscreduced",0,(void*)reduceddiscsmith,7,"G","poldiscreduced(f): vector of elementary divisors of Z[a]/f'(a)Z[a], where a is a root of the polynomial f."},
+{"polgalois",0,(void*)polgalois,6,"Gp","polgalois(T): Galois group of the polynomial T (see manual for group coding). Return [n, s, k, name] where n is the group order, s the signature, k the index and name is the GAP4 name of the transitive group."},
+{"polgraeffe",0,(void*)polgraeffe,7,"G","polgraeffe(f): returns the Graeffe transform g of f, such that g(x^2) = f(x)f(-x)"},
+{"polhensellift",0,(void*)polhensellift,7,"GGGL","polhensellift(A, B, p, e): lift the factorization B of A modulo p to a factorization modulo p^e using Hensel lift. The factors in B must be pairwise relatively prime modulo p."},
+{"polhermite",0,(void*)polhermite_eval,7,"LDG","polhermite(n,{a='x}): Hermite polynomial H(n,v) of degree n, evaluated at a."},
+{"polinterpolate",0,(void*)polint,7,"GDGDGD&","polinterpolate(X,{Y},{x},{&e}): polynomial interpolation at x according to data vectors X, Y (ie return P such that P(X[i]) = Y[i] for all i). If Y is omitted, return P such that P(i) = X[i]. If present, e will contain an error estimate on the returned value."},
+{"poliscyclo",0,(void*)poliscyclo,7,"lG","poliscyclo(f): returns 0 if f is not a cyclotomic polynomial, and n > 0 if f = Phi_n, the n-th cyclotomic polynomial."},
+{"poliscycloprod",0,(void*)poliscycloprod,7,"lG","poliscycloprod(f): returns 1 if f is a product of cyclotomic polynonials, and 0 otherwise."},
+{"polisirreducible",0,(void*)isirreducible,7,"lG","polisirreducible(pol): true(1) if pol is an irreducible non-constant polynomial, false(0) if pol is reducible or constant."},
+{"pollead",0,(void*)pollead,7,"GDn","pollead(x,{v}): leading coefficient of polynomial or series x, or x itself if x is a scalar. Error otherwise. With respect to the main variable of x if v is omitted, with respect to the variable v otherwise."},
+{"pollegendre",0,(void*)pollegendre_eval,7,"LDG","pollegendre(n,{a='x}): legendre polynomial of degree n evaluated at a."},
+{"polrecip",0,(void*)polrecip,7,"G","polrecip(pol): reciprocal polynomial of pol."},
+{"polred",0,(void*)polred0,6,"GD0,L,DG","polred(T,{flag=0}): Deprecated, use polredbest. Reduction of the polynomial T (gives minimal polynomials only). The following binary digits of (optional) flag are significant 1: partial reduction, 2: gives also elements."},
+{"polredabs",0,(void*)polredabs0,6,"GD0,L,","polredabs(T,{flag=0}): a smallest generating polynomial of the number field for the T2 norm on the roots, with smallest index for the minimal T2 norm. flag is optional, whose binary digit mean 1: give the element whose characteristic polynomial is the given polynomial. 4: give all polynomials of minimal T2 norm (give only one of P(x) and P(-x))."},
+{"polredbest",0,(void*)polredbest,6,"GD0,L,","polredbest(T,{flag=0}): reduction of the polynomial T (gives minimal polynomials only). If flag=1, gives also elements."},
+{"polredord",0,(void*)polredord,6,"G","polredord(x): reduction of the polynomial x, staying in the same order."},
+{"polresultant",0,(void*)polresultant0,7,"GGDnD0,L,","polresultant(x,y,{v},{flag=0}): resultant of the polynomials x and y, with respect to the main variables of x and y if v is omitted, with respect to the variable v otherwise. flag is optional, and can be 0: default, uses either the subresultant algorithm, a modular algorithm or Sylvester's matrix, depending on the inputs; 1 uses Sylvester's matrix (should always be slower than the default)."},
+{"polresultantext",0,(void*)polresultantext0,7,"GGDn","polresultantext(A,B,{v}): return [U,V,R] such that R=polresultant(A,B,v) and U*A+V*B = R, where A and B are polynomials."},
+{"polroots",0,(void*)roots,7,"Gp","polroots(x): complex roots of the polynomial x using Schonhage's method, as modified by Gourdon."},
+{"polrootsff",0,(void*)polrootsff,4,"GDGDG","polrootsff(x,{p},{a}): returns the roots of the polynomial x in the finite field F_p[X]/a(X)F_p[X]. a or p can be omitted if x has t_FFELT coefficients."},
+{"polrootsmod",0,(void*)rootmod0,7,"GGD0,L,","polrootsmod(pol,p,{flag=0}): roots mod the prime p of the polynomial pol. flag is optional, and can be 0: default, or 1: use a naive search, useful for small p."},
+{"polrootspadic",0,(void*)rootpadic,7,"GGL","polrootspadic(x,p,r): p-adic roots of the polynomial x to precision r."},
+{"polsturm",0,(void*)sturmpart,7,"lGDGDG","polsturm(pol,{a},{b}): number of real roots of the squarefree polynomial pol in the interval ]a,b] (which are respectively taken to be -oo or +oo when omitted)."},
+{"polsubcyclo",0,(void*)polsubcyclo,7,"LLDn","polsubcyclo(n,d,{v='x}): finds an equation (in variable v) for the d-th degree subfields of Q(zeta_n). Output is a polynomial, or a vector of polynomials if there are several such fields or none."},
+{"polsylvestermatrix",0,(void*)sylvestermatrix,7,"GG","polsylvestermatrix(x,y): forms the sylvester matrix associated to the two polynomials x and y. Warning: the polynomial coefficients are in columns, not in rows."},
+{"polsym",0,(void*)polsym,7,"GL","polsym(x,n): column vector of symmetric powers of the roots of x up to n."},
+{"poltchebi",0,(void*)polchebyshev1,7,"LDn","poltchebi(n,{v='x}): deprecated alias for polchebyshev"},
+{"poltschirnhaus",0,(void*)tschirnhaus,6,"G","poltschirnhaus(x): random Tschirnhausen transformation of the polynomial x."},
+{"polylog",0,(void*)polylog0,3,"LGD0,L,p","polylog(m,x,{flag=0}): m-th polylogarithm of x. flag is optional, and can be 0: default, 1: D_m~-modified m-th polylog of x, 2: D_m-modified m-th polylog of x, 3: P_m-modified m-th polylog of x."},
+{"polzagier",0,(void*)polzag,7,"LL","polzagier(n,m): Zagier's polynomials of index n,m."},
+{"precision",0,(void*)precision0,2,"GD0,L,","precision(x,{n}): if n is present, return x at precision n. If n is omitted, return real precision of object x."},
+{"precprime",0,(void*)precprime,4,"G","precprime(x): largest pseudoprime <= x, 0 if x<=1."},
+{"prime",0,(void*)prime,4,"L","prime(n): returns the n-th prime (n C-integer)."},
+{"primepi",0,(void*)primepi,4,"G","primepi(x): the prime counting function pi(x) = #{p <= x, p prime}."},
+{"primes",0,(void*)primes0,4,"G","primes(n): returns the vector of the first n primes (integer), or the primes in interval n = [a,b]."},
+{"print",0,(void*)print,11,"vs*","print({str}*): outputs its string arguments (in raw format) ending with a newline."},
+{"print1",0,(void*)print1,11,"vs*","print1({str}*): outputs its string arguments (in raw format) without ending with newline."},
+{"printf",0,(void*)printf0,11,"vss*","printf(fmt,{x}*): prints its arguments according to the format fmt."},
+{"printsep",0,(void*)printsep,11,"vss*","printsep(sep,{str}*): outputs its string arguments (in raw format), separated by 'sep', ending with a newline."},
+{"printsep1",0,(void*)printsep1,11,"vss*","printsep(sep,{str}*): outputs its string arguments (in raw format), separated by 'sep', without ending with a newline."},
+{"printtex",0,(void*)printtex,11,"vs*","printtex({str}*): outputs its string arguments in TeX format."},
+{"prod",0,(void*)produit,9,"V=GGEDG","prod(X=a,b,expr,{x=1}): x times the product (X runs from a to b) of expression."},
+{"prodeuler",0,(void*)prodeuler0,9,"V=GGEp","prodeuler(X=a,b,expr): Euler product (X runs over the primes between a and b) of real or complex expression."},
+{"prodinf",0,(void*)prodinf0,9,"V=GED0,L,p","prodinf(X=a,expr,{flag=0}): infinite product (X goes from a to infinity) of real or complex expression. flag can be 0 (default) or 1, in which case compute the product of the 1+expr instead."},
+{"psi",0,(void*)gpsi,3,"Gp","psi(x): psi-function at x."},
+{"qfauto",0,(void*)qfauto0,8,"GDG","qfauto(G,{fl}): automorphism group of the positive definite quadratic form G."},
+{"qfautoexport",0,(void*)qfautoexport,8,"GD0,L,","qfautoexport(qfa,{flag}): qfa being an automorphism group as output by qfauto, output a string representing the underlying matrix group in GAP notation (default) or Magma notation (flag = 1)."},
+{"qfbclassno",0,(void*)qfbclassno0,4,"GD0,L,","qfbclassno(D,{flag=0}): class number of discriminant D using Shanks's method by default. If (optional) flag is set to 1, use Euler products."},
+{"qfbcompraw",0,(void*)qfbcompraw,4,"GG","qfbcompraw(x,y): Gaussian composition without reduction of the binary quadratic forms x and y."},
+{"qfbhclassno",0,(void*)hclassno,4,"G","qfbhclassno(x): Hurwitz-Kronecker class number of x>0."},
+{"qfbil",0,(void*)qfbil,8,"GGDG","qfbil(x,y,{q}): evaluate the bilinear form q (symmetric matrix) at (x,y); if q omitted, use the standard Euclidean scalar product."},
+{"qfbnucomp",0,(void*)nucomp,4,"GGG","qfbnucomp(x,y,L): composite of primitive positive definite quadratic forms x and y using nucomp and nudupl, where L=[|D/4|^(1/4)] is precomputed."},
+{"qfbnupow",0,(void*)nupow,4,"GG","qfbnupow(x,n): n-th power of primitive positive definite quadratic form x using nucomp and nudupl."},
+{"qfbpowraw",0,(void*)qfbpowraw,4,"GL","qfbpowraw(x,n): n-th power without reduction of the binary quadratic form x."},
+{"qfbprimeform",0,(void*)primeform,4,"GGp","qfbprimeform(x,p): returns the prime form of discriminant x, whose first coefficient is p."},
+{"qfbred",0,(void*)qfbred0,4,"GD0,L,DGDGDG","qfbred(x,{flag=0},{d},{isd},{sd}): reduction of the binary quadratic form x. All other args. are optional. The arguments d, isd and sd, if present, supply the values of the discriminant, floor(sqrt(d)) and sqrt(d) respectively. If d<0, its value is not used and all references to Shanks's distance hereafter are meaningless. flag can be any of 0: default, uses Shanks's distance function d; 1: use d, do a single reduction step; 2: do not use d; 3 [...]
+{"qfbsolve",0,(void*)qfbsolve,4,"GG","qfbsolve(Q,p): Return [x,y] so that Q(x,y)=p where Q is a binary quadratic form and p a prime number, or 0 if there is no solution."},
+{"qfgaussred",0,(void*)qfgaussred,8,"G","qfgaussred(q): square reduction of the (symmetric) matrix q (returns a square matrix whose i-th diagonal term is the coefficient of the i-th square in which the coefficient of the i-th variable is 1)."},
+{"qfisom",0,(void*)qfisom0,8,"GGDG","qfisom(G,H,{fl}): find an isomorphism between the integral positive definite quadratic forms G and H if it exists. G can also be given by a qfisominit structure which is preferable if several forms need to be compared to G."},
+{"qfisominit",0,(void*)qfisominit0,8,"GDG","qfisominit(G,{fl}): G being a square and symmetric matrix representing an integral positive definite quadratic form, this function return a structure allowing to compute isomorphisms between G and other quadratic form faster."},
+{"qfjacobi",0,(void*)jacobi,8,"Gp","qfjacobi(A): eigenvalues and orthogonal matrix of eigenvectors of the real symmetric matrix A."},
+{"qflll",0,(void*)qflll0,8,"GD0,L,","qflll(x,{flag=0}): LLL reduction of the vectors forming the matrix x (gives the unimodular transformation matrix T such that x*T is LLL-reduced). flag is optional, and can be 0: default, 1: assumes x is integral, 2: assumes x is integral, returns a partially reduced basis, 4: assumes x is integral, returns [K,T] where K is the integer kernel of x and T the LLL reduced image, 5: same as 4 but x may have polynomial coefficients, 8: same as 0 but x may h [...]
+{"qflllgram",0,(void*)qflllgram0,8,"GD0,L,","qflllgram(G,{flag=0}): LLL reduction of the lattice whose gram matrix is G (gives the unimodular transformation matrix). flag is optional and can be 0: default,1: assumes x is integral, 4: assumes x is integral, returns [K,T],  where K is the integer kernel of x and T the LLL reduced image, 5: same as 4 but x may have polynomial coefficients, 8: same as 0 but x may have polynomial coefficients."},
+{"qfminim",0,(void*)qfminim0,8,"GDGDGD0,L,p","qfminim(x,{b},{m},{flag=0}): x being a square and symmetric matrix representing a positive definite quadratic form, this function deals with the vectors of x whose norm is less than or equal to b, enumerated using the Fincke-Pohst algorithm, storing at most m vectors (no limit if m is omitted). The function searches for the minimal non-zero vectors if b is omitted. The precise behavior depends on flag. 0: seeks at most 2m vectors (unless m om [...]
+{"qfnorm",0,(void*)qfnorm,8,"GDG","qfnorm(x,{q}): evaluate the binary quadratic form q (symmetric matrix) at x; if q omitted, use the standard Euclidean form."},
+{"qfperfection",0,(void*)perf,8,"G","qfperfection(G): rank of matrix of xx~ for x minimal vectors of a gram matrix G."},
+{"qfrep",0,(void*)qfrep0,8,"GGD0,L,","qfrep(q,B,{flag=0}): vector of (half) the number of vectors of norms from 1 to B for the integral and definite quadratic form q. If flag is 1, count vectors of even norm from 1 to 2B."},
+{"qfsign",0,(void*)qfsign,8,"G","qfsign(x): signature of the symmetric matrix x."},
+{"quadclassunit",0,(void*)quadclassunit0,4,"GD0,L,DGp","quadclassunit(D,{flag=0},{tech=[]}): compute the structure of the class group and the regulator of the quadratic field of discriminant D. See manual for the optional technical parameters."},
+{"quaddisc",0,(void*)quaddisc,4,"G","quaddisc(x): discriminant of the quadratic field Q(sqrt(x))."},
+{"quadgen",0,(void*)quadgen,4,"G","quadgen(D): standard generator of quadratic order of discriminant D."},
+{"quadhilbert",0,(void*)quadhilbert,4,"Gp","quadhilbert(D): relative equation for the Hilbert class field of the quadratic field of discriminant D (which can also be a bnf)."},
+{"quadpoly",0,(void*)quadpoly0,4,"GDn","quadpoly(D,{v='x}): quadratic polynomial corresponding to the discriminant D, in variable v."},
+{"quadray",0,(void*)quadray,4,"GGp","quadray(D,f): relative equation for the ray class field of conductor f for the quadratic field of discriminant D (which can also be a bnf)."},
+{"quadregulator",0,(void*)quadregulator,4,"Gp","quadregulator(x): regulator of the real quadratic field of discriminant x."},
+{"quadunit",0,(void*)quadunit,4,"G","quadunit(D): fundamental unit of the quadratic field of discriminant D where D must be positive."},
+{"random",0,(void*)genrand,2,"DG","random({N=2^31}): random object, depending on the type of N. Integer between 0 and N-1 (t_INT), int mod N (t_INTMOD), element in a finite field (t_FFELT), point on an elliptic curve (ellinit mod p or over a finite field)."},
+{"randomprime",0,(void*)randomprime,4,"DG","randomprime({N = 2^31}): returns a strong pseudo prime in [2, N-1]."},
+{"readvec",0,(void*)gp_readvec_file,11,"D\"\",s,","readvec({filename}): create a vector whose components are the evaluation of all the expressions found in the input file filename."},
+{"real",0,(void*)greal,2,"G","real(x): real part of x."},
+{"removeprimes",0,(void*)removeprimes,4,"DG","removeprimes({x=[]}): remove primes in the vector x from the prime table. x can also be a single integer. List the current extra primes if x is omitted."},
+{"return",0,(void*)return0,11,"DG","return({x=0}): return from current subroutine with result x."},
+{"rnfalgtobasis",0,(void*)rnfalgtobasis,6,"GG","rnfalgtobasis(rnf,x): relative version of nfalgtobasis, where rnf is a relative numberfield."},
+{"rnfbasis",0,(void*)rnfbasis,6,"GG","rnfbasis(bnf,M): given a projective Z_K-module M as output by rnfpseudobasis or rnfsteinitz, gives either a basis of M if it is free, or an n+1-element generating set."},
+{"rnfbasistoalg",0,(void*)rnfbasistoalg,6,"GG","rnfbasistoalg(rnf,x): relative version of nfbasistoalg, where rnf is a relative numberfield."},
+{"rnfcharpoly",0,(void*)rnfcharpoly,6,"GGGDn","rnfcharpoly(nf,T,a,{var='x}): characteristic polynomial of a over nf, where a belongs to the algebra defined by T over nf. Returns a polynomial in variable var (x by default)."},
+{"rnfconductor",0,(void*)rnfconductor,6,"GG","rnfconductor(bnf,pol): conductor of the Abelian extension of bnf defined by pol. The result is [conductor,rayclassgroup,subgroup], where conductor is the conductor itself, rayclassgroup the structure of the corresponding full ray class group, and subgroup the HNF defining the norm group (Artin or Takagi group) on the given generators rayclassgroup[3]."},
+{"rnfdedekind",0,(void*)rnfdedekind,6,"GGDGD0,L,","rnfdedekind(nf,pol,{pr},{flag=0}): relative Dedekind criterion over the number field K, represented by nf, applied to the order O_K[X]/(P), modulo the prime ideal pr (at all primes if pr omitted, in which case flag is automatically set to 1). P is assumed to be monic, irreducible, in O_K[X]. Returns [max,basis,v], where basis is a pseudo-basis of the enlarged order, max is 1 iff this order is pr-maximal, and v is the valuation at pr of t [...]
+{"rnfdet",0,(void*)rnfdet,6,"GG","rnfdet(nf,M): given a pseudo-matrix M, compute its determinant."},
+{"rnfdisc",0,(void*)rnfdiscf,6,"GG","rnfdisc(nf,pol): given a pol with coefficients in nf, gives a 2-component vector [D,d], where D is the relative ideal discriminant, and d is the relative discriminant in nf^*/nf*^2."},
+{"rnfeltabstorel",0,(void*)rnfeltabstorel,6,"GG","rnfeltabstorel(rnf,x): transforms the element x from absolute to relative representation."},
+{"rnfeltdown",0,(void*)rnfeltdown,6,"GG","rnfeltdown(rnf,x): expresses x on the base field if possible; returns an error otherwise."},
+{"rnfeltnorm",0,(void*)rnfeltnorm,6,"GG","rnfeltnorm(rnf,x): returns the relative norm N_{L/K}(x), as an element of K"},
+{"rnfeltreltoabs",0,(void*)rnfeltreltoabs,6,"GG","rnfeltreltoabs(rnf,x): transforms the element x from relative to absolute representation."},
+{"rnfelttrace",0,(void*)rnfelttrace,6,"GG","rnfelttrace(rnf,x): returns the relative trace N_{L/K}(x), as an element of K"},
+{"rnfeltup",0,(void*)rnfeltup,6,"GG","rnfeltup(rnf,x): expresses x (belonging to the base field) on the relative field."},
+{"rnfequation",0,(void*)rnfequation0,6,"GGD0,L,","rnfequation(nf,pol,{flag=0}): given a pol with coefficients in nf, gives an absolute equation z of the number field defined by pol. flag is optional, and can be 0: default, or non-zero, gives [z,al,k], where z defines the absolute equation L/Q as in the default behavior, al expresses as an element of L a root of the polynomial defining the base field nf, and k is a small integer such that t = b + k al is a root of z, for b a root of pol."},
+{"rnfhnfbasis",0,(void*)rnfhnfbasis,6,"GG","rnfhnfbasis(bnf,x): given an order x as output by rnfpseudobasis, gives either a true HNF basis of the order if it exists, zero otherwise."},
+{"rnfidealabstorel",0,(void*)rnfidealabstorel,6,"GG","rnfidealabstorel(rnf,x): transforms the ideal x from absolute to relative representation."},
+{"rnfidealdown",0,(void*)rnfidealdown,6,"GG","rnfidealdown(rnf,x): finds the intersection of the ideal x with the base field."},
+{"rnfidealhnf",0,(void*)rnfidealhnf,6,"GG","rnfidealhnf(rnf,x): relative version of idealhnf, where rnf is a relative numberfield."},
+{"rnfidealmul",0,(void*)rnfidealmul,6,"GGG","rnfidealmul(rnf,x,y): relative version of idealmul, where rnf is a relative numberfield."},
+{"rnfidealnormabs",0,(void*)rnfidealnormabs,6,"GG","rnfidealnormabs(rnf,x): absolute norm of the ideal x."},
+{"rnfidealnormrel",0,(void*)rnfidealnormrel,6,"GG","rnfidealnormrel(rnf,x): relative norm of the ideal x."},
+{"rnfidealreltoabs",0,(void*)rnfidealreltoabs,6,"GG","rnfidealreltoabs(rnf,x): transforms the ideal x from relative to absolute representation."},
+{"rnfidealtwoelt",0,(void*)rnfidealtwoelement,6,"GG","rnfidealtwoelt(rnf,x): relative version of idealtwoelt, where rnf is a relative numberfield."},
+{"rnfidealup",0,(void*)rnfidealup,6,"GG","rnfidealup(rnf,x): lifts the ideal x (of the base field) to the relative field."},
+{"rnfinit",0,(void*)rnfinit,6,"GG","rnfinit(nf,pol): pol being an irreducible polynomial defined over the number field nf, initializes a vector of data necessary for working in relative number fields (rnf functions). See manual for technical details."},
+{"rnfisabelian",0,(void*)rnfisabelian,6,"lGG","rnfisabelian(nf,T): T being a relative polynomial with coefficients in nf, return 1 if it defines an abelian extension, and 0 otherwise."},
+{"rnfisfree",0,(void*)rnfisfree,6,"lGG","rnfisfree(bnf,x): given an order x as output by rnfpseudobasis or rnfsteinitz, outputs true (1) or false (0) according to whether the order is free or not."},
+{"rnfisnorm",0,(void*)rnfisnorm,6,"GGD0,L,","rnfisnorm(T,a,{flag=0}): T is as output by rnfisnorminit applied to L/K. Tries to tell whether a is a norm from L/K. Returns a vector [x,q] where a=Norm(x)*q. Looks for a solution which is a S-integer, with S a list of places in K containing the ramified primes, generators of the class group of ext, as well as those primes dividing a. If L/K is Galois, omit flag, otherwise it is used to add more places to S: all the places above the primes p < [...]
+{"rnfisnorminit",0,(void*)rnfisnorminit,6,"GGD2,L,","rnfisnorminit(pol,polrel,{flag=2}): let K be defined by a root of pol, L/K the extension defined by polrel. Compute technical data needed by rnfisnorm to solve norm equations Nx = a, for x in L, and a in K. If flag=0, do not care whether L/K is Galois or not; if flag = 1, assume L/K is Galois; if flag = 2, determine whether L/K is Galois."},
+{"rnfkummer",0,(void*)rnfkummer,6,"GDGD0,L,p","rnfkummer(bnr,{subgp},{d=0}): bnr being as output by bnrinit, finds a relative equation for the class field corresponding to the module in bnr and the given congruence subgroup (the ray class field if subgp is omitted). d can be zero (default), or positive, and in this case the output is the list of all relative equations of degree d for the given bnr, with the same conductor as (bnr, subgp)."},
+{"rnflllgram",0,(void*)rnflllgram,6,"GGGp","rnflllgram(nf,pol,order): given a pol with coefficients in nf and an order as output by rnfpseudobasis or similar, gives [[neworder],U], where neworder is a reduced order and U is the unimodular transformation matrix."},
+{"rnfnormgroup",0,(void*)rnfnormgroup,6,"GG","rnfnormgroup(bnr,pol): norm group (or Artin or Takagi group) corresponding to the Abelian extension of bnr.bnf defined by pol, where the module corresponding to bnr is assumed to be a multiple of the conductor. The result is the HNF defining the norm group on the generators in bnr.gen."},
+{"rnfpolred",0,(void*)rnfpolred,6,"GGp","rnfpolred(nf,pol): given a pol with coefficients in nf, finds a list of relative polynomials defining some subfields, hopefully simpler."},
+{"rnfpolredabs",0,(void*)rnfpolredabs,6,"GGD0,L,","rnfpolredabs(nf,pol,{flag=0}): given a pol with coefficients in nf, finds a relative simpler polynomial defining the same field. Binary digits of flag mean: 1: return also the element whose characteristic polynomial is the given polynomial, 2: return an absolute polynomial, 16: partial reduction."},
+{"rnfpolredbest",0,(void*)rnfpolredbest,6,"GGD0,L,","rnfpolredbest(nf,pol,{flag=0}): given a pol with coefficients in nf, finds a relative polynomial P defining the same field, hopefully simpler than pol; flag can be 0: default, 1: return [P,a], where a is a root of pol 2: return an absolute polynomial Pabs, 3: return [Pabs, a,b], where a is a root of nf.pol and b is a root of pol."},
+{"rnfpseudobasis",0,(void*)rnfpseudobasis,6,"GG","rnfpseudobasis(nf,pol): given a pol with coefficients in nf, gives a 4-component vector [A,I,D,d] where [A,I] is a pseudo basis of the maximal order in HNF on the power basis, D is the relative ideal discriminant, and d is the relative discriminant in nf^*/nf*^2."},
+{"rnfsteinitz",0,(void*)rnfsteinitz,6,"GG","rnfsteinitz(nf,x): given an order x as output by rnfpseudobasis, gives [A,I,D,d] where (A,I) is a pseudo basis where all the ideals except perhaps the last are trivial."},
+{"round",0,(void*)round0,2,"GD&","round(x,{&e}): take the nearest integer to all the coefficients of x. If e is present, do not take into account loss of integer part precision, and set e = error estimate in bits."},
+{"select",0,(void*)select0,11,"GGD0,L,","select(f, A, {flag = 0}): selects elements of A according to the selection function f. If flag is 1, return the indices of those elements (indirect selection)"},
+{"seralgdep",0,(void*)seralgdep,8,"GLL","seralgdep(s,p,r): find a linear relation between powers (1,s, ..., s^p) of the series s, with polynomial coefficients of degree <= r."},
+{"serconvol",0,(void*)convol,7,"GG","serconvol(x,y): convolution (or Hadamard product) of two power series."},
+{"serlaplace",0,(void*)laplace,7,"G","serlaplace(x): replaces the power series sum of a_n*x^n/n! by sum of a_n*x^n. For the reverse operation, use serconvol(x,exp(X))."},
+{"serreverse",0,(void*)serreverse,7,"G","serreverse(s): reversion of the power series s."},
+{"setbinop",0,(void*)setbinop,8,"GGDG","setbinop(f,X,{Y}): the set {f(x,y), x in X, y in Y}. If Y is omitted, assume that X = Y and that f is symmetric."},
+{"setintersect",0,(void*)setintersect,8,"GG","setintersect(x,y): intersection of the sets x and y."},
+{"setisset",0,(void*)setisset,8,"lG","setisset(x): true(1) if x is a set (row vector with strictly increasing entries), false(0) if not."},
+{"setminus",0,(void*)setminus,8,"GG","setminus(x,y): set of elements of x not belonging to y."},
+{"setrand",0,(void*)setrand,11,"vG","setrand(n): reset the seed of the random number generator to n."},
+{"setsearch",0,(void*)setsearch,8,"lGGD0,L,","setsearch(S,x,{flag=0}): determines whether x belongs to the set (or sorted list) S. If flag is 0 or omitted, returns 0 if it does not, otherwise returns the index j such that x==S[j]. If flag is non-zero, return 0 if x belongs to S, otherwise the index j where it should be inserted."},
+{"setunion",0,(void*)setunion,8,"GG","setunion(x,y): union of the sets x and y."},
+{"shift",0,(void*)gshift,1,"GL","shift(x,n): shift x left n bits if n>=0, right -n bits if n<0."},
+{"shiftmul",0,(void*)gmul2n,1,"GL","shiftmul(x,n): multiply x by 2^n (n>=0 or n<0)"},
+{"sigma",0,(void*)sumdivk,4,"GD1,L,","sigma(x,{k=1}): sum of the k-th powers of the divisors of x. k is optional and if omitted is assumed to be equal to 1."},
+{"sign",0,(void*)gsigne,1,"iG","sign(x): sign of x, of type integer, real or fraction"},
+{"simplify",0,(void*)simplify,2,"G","simplify(x): simplify the object x as much as possible."},
+{"sin",0,(void*)gsin,3,"Gp","sin(x): sine of x."},
+{"sinh",0,(void*)gsinh,3,"Gp","sinh(x): hyperbolic sine of x."},
+{"sizebyte",0,(void*)gsizebyte,2,"lG","sizebyte(x): number of bytes occupied by the complete tree of the object x."},
+{"sizedigit",0,(void*)sizedigit,2,"lG","sizedigit(x): maximum number of decimal digits minus one of (the coefficients of) x."},
+{"solve",0,(void*)zbrent0,9,"V=GGEp","solve(X=a,b,expr): real root of expression expr (X between a and b), where expr(a)*expr(b)<=0."},
+{"sqr",0,(void*)gsqr,3,"G","sqr(x): square of x. NOT identical to x*x."},
+{"sqrt",0,(void*)gsqrt,3,"Gp","sqrt(x): square root of x."},
+{"sqrtint",0,(void*)sqrtint,4,"G","sqrtint(x): integer square root of x, where x is a non-negative integer."},
+{"sqrtn",0,(void*)gsqrtn,3,"GGD&p","sqrtn(x,n,{&z}): nth-root of x, n must be integer. If present, z is set to a suitable root of unity to recover all solutions. If it was not possible, z is set to zero."},
+{"sqrtnint",0,(void*)sqrtnint,4,"GL","sqrtnint(x,n): integer n-th root of x, where x is non-negative integer."},
+{"stirling",0,(void*)stirling,4,"LLD1,L,","stirling(n,k,{flag=1}): If flag=1 (default) return the Stirling number of the first kind s(n,k), if flag=2, return the Stirling number of the second kind S(n,k)."},
+{"subgrouplist",0,(void*)subgrouplist0,6,"GDGD0,L,","subgrouplist(bnr,{bound},{flag=0}): bnr being as output by bnrinit or a list of cyclic components of a finite Abelian group G, outputs the list of subgroups of G (of index bounded by bound, if not omitted), given as HNF left divisors of the SNF matrix corresponding to G. If flag=0 (default) and bnr is as output by bnrinit, gives only the subgroups for which the modulus is the conductor."},
+{"subst",0,(void*)gsubst,7,"GnG","subst(x,y,z): in expression x, replace the variable y by the expression z."},
+{"substpol",0,(void*)gsubstpol,7,"GGG","substpol(x,y,z): in expression x, replace the polynomial y by the expression z, using remainder decomposition of x."},
+{"substvec",0,(void*)gsubstvec,7,"GGG","substvec(x,v,w): in expression x, make a best effort to replace the variables v1,...,vn by the expression w1,...,wn."},
+{"sum",0,(void*)somme,9,"V=GGEDG","sum(X=a,b,expr,{x=0}): x plus the sum (X goes from a to b) of expression expr."},
+{"sumalt",0,(void*)sumalt0,9,"V=GED0,L,p","sumalt(X=a,expr,{flag=0}): Cohen-Villegas-Zagier's acceleration of alternating series expr, X starting at a. flag is optional, and can be 0: default, or 1: uses a slightly different method using Zagier's polynomials."},
+{"sumdedekind",0,(void*)sumdedekind,4,"GG","sumdedekind(h,k): Dedekind sum associated to h,k"},
+{"sumdigits",0,(void*)sumdigits,4,"G","sumdigits(n): sum of (decimal) digits in the integer n."},
+{"sumdiv",0,(void*)sumdivexpr,9,"GVE","sumdiv(n,X,expr): sum of expression expr, X running over the divisors of n."},
+{"sumdivmult",0,(void*)sumdivmultexpr,9,"GVE","sumdivmult(n,d,expr): sum of multiplicative function expr, d running over the divisors of n."},
+{"sumformal",0,(void*)sumformal,7,"GDn","sumformal(f,{v}): formal sum of f with respect to v, or to the main variable of f if v is omitted."},
+{"suminf",0,(void*)suminf0,9,"V=GEp","suminf(X=a,expr): infinite sum (X goes from a to infinity) of real or complex expression expr."},
+{"sumnum",0,(void*)sumnum0,9,"V=GGEDGD0,L,p","sumnum(X=a,sig,expr,{tab},{flag=0}): numerical summation of expr from X = ceiling(a) to +infinity. sig is either a scalar or a two-component vector coding the function's decrease rate at infinity. It is assumed that the scalar part of sig is to the right of all poles of expr. If present, tab must be initialized by sumnuminit. If flag is nonzero, assumes that conj(expr(z)) = expr(conj(z))."},
+{"sumnumalt",0,(void*)sumnumalt0,9,"V=GGEDGD0,L,p","sumnumalt(X=a,sig,expr,{tab},{flag=0}): numerical summation of (-1)^X expr(X) from X = ceiling(a) to +infinity. Note that the (-1)^X must not be included. sig is either a scalar or a two-component vector coded as in intnum, and the scalar part is larger than all the real parts of the poles of expr. Uses intnum, hence tab is as in intnum. If flag is nonzero, assumes that the function to be summed satisfies conj(f(z))=f(conj(z)), and then [...]
+{"sumnuminit",0,(void*)sumnuminit,9,"GD0,L,D1,L,p","sumnuminit(sig, {m=0}, {sgn=1}): initialize tables for numerical summation. sgn is 1 (in fact >= 0), the default, for sumnum (ordinary sums) or -1 (in fact < 0) for sumnumalt (alternating sums). sig is as in sumnum and m is as in intnuminit."},
+{"sumpos",0,(void*)sumpos0,9,"V=GED0,L,p","sumpos(X=a,expr,{flag=0}): sum of positive (or negative) series expr, the formal variable X starting at a. flag is optional, and can be 0: default, or 1: uses a slightly different method using Zagier's polynomials."},
+{"tan",0,(void*)gtan,3,"Gp","tan(x): tangent of x."},
+{"tanh",0,(void*)gtanh,3,"Gp","tanh(x): hyperbolic tangent of x."},
+{"taylor",0,(void*)tayl,7,"GnDP","taylor(x,t,{d=seriesprecision}): taylor expansion of x with respect to t, adding O(t^d) to all components of x."},
+{"teichmuller",0,(void*)teich,3,"G","teichmuller(x): teichmuller character of p-adic number x."},
+{"theta",0,(void*)theta,3,"GGp","theta(q,z): Jacobi sine theta-function."},
+{"thetanullk",0,(void*)thetanullk,3,"GLp","thetanullk(q,k): k-th derivative at z=0 of theta(q,z)."},
+{"thue",0,(void*)thue,7,"GGDG","thue(tnf,a,{sol}): solve the equation P(x,y)=a, where tnf was created with thueinit(P), and sol, if present, contains the solutions of Norm(x)=a modulo units in the number field defined by P. If tnf was computed without assuming GRH (flag 1 in thueinit), the result is unconditional. If tnf is a polynomial, compute thue(thueinit(P,0), a)."},
+{"thueinit",0,(void*)thueinit,7,"GD0,L,p","thueinit(P,{flag=0}): initialize the tnf corresponding to P, that will be used to solve Thue equations P(x,y) = some-integer. If flag is non-zero, certify the result unconditionaly. Otherwise, assume GRH (much faster of course)."},
+{"trace",0,(void*)gtrace,8,"G","trace(x): trace of x."},
+{"trap",0,(void*)trap0,11,"DrDEDE","trap({e}, {rec}, seq): try to execute seq, trapping runtime error e (all of them if e omitted); sequence rec is executed if the error occurs and is the result of the command. THIS FUNCTION IS OBSOLETE: use \"IFERR\""},
+{"truncate",0,(void*)trunc0,2,"GD&","truncate(x,{&e}): truncation of x; when x is a power series,take away the O(X^). If e is present, do not take into account loss of integer part precision, and set e = error estimate in bits."},
+{"type",0,(void*)type0,11,"G","type(x): return the type of the GEN x."},
+{"uninline",0,NULL,11,NULL,"uninline(): forget all inline variables [EXPERIMENTAL]"},
+{"until",0,(void*)untilpari,11,"vEI","until(a,seq): evaluate the expression sequence seq until a is nonzero."},
+{"valuation",0,(void*)gvaluation,2,"lGG","valuation(x,p): valuation of x with respect to p."},
+{"variable",0,(void*)gpolvar,2,"DG","variable({x}): main variable of object x. Gives p for p-adic x, 0 if no variable can be associated to x. Returns the list of user variables if x is omitted."},
+{"vecextract",0,(void*)extract0,8,"GGDG","vecextract(x,y,{z}): extraction of the components of the matrix or vector x according to y and z. If z is omitted, y represents columns, otherwise y corresponds to rows and z to columns. y and z can be vectors (of indices), strings (indicating ranges as in \"1..10\") or masks (integers whose binary representation indicates the indices to extract, from left to right 1, 2, 4, 8, etc.)."},
+{"vecmax",0,(void*)vecmax0,1,"GD&","vecmax(x,{&v}): largest entry in the vector/matrix x. If v is present, set it to the index of a largest entry (indirect max)."},
+{"vecmin",0,(void*)vecmin0,1,"GD&","vecmin(x,{&v}): smallest entry in the vector/matrix x. If v is present, set it to the index of a smallest entry (indirect min)."},
+{"vecsearch",0,(void*)vecsearch,8,"lGGDG","vecsearch(v,x,{cmpf}): determines whether x belongs to the sorted vector v. If the comparison function cmpf is explicitly given, assume that v was sorted according to vecsort(, cmpf)."},
+{"vecsort",0,(void*)vecsort0,8,"GDGD0,L,","vecsort(x,{cmpf},{flag=0}): sorts the vector of vectors (or matrix) x in ascending order, according to the comparison function cmpf, if not omitted. (If cmpf is an integer, sort according to the value of the k-th component of each entry.) Binary digits of flag (if present) mean: 1: indirect sorting, return the permutation instead of the permuted vector, 2: sort using lexicographic order, 4: use descending instead of ascending order, 8: remove du [...]
+{"vecsum",0,(void*)vecsum,8,"G","vecsum(v): return the sum of the component of the vector v"},
+{"vector",0,(void*)vecteur,8,"GDVDE","vector(n,{X},{expr=0}): row vector with n components of expression expr (X ranges from 1 to n). By default, fill with 0s."},
+{"vectorsmall",0,(void*)vecteursmall,8,"GDVDE","vectorsmall(n,{X},{expr=0}): VECSMALL with n components of expression expr (X ranges from 1 to n) which must be small integers. By default, fill with 0s."},
+{"vectorv",0,(void*)vvecteur,8,"GDVDE","vectorv(n,{X},{expr=0}): column vector with n components of expression expr (X ranges from 1 to n). By default, fill with 0s."},
+{"version",0,(void*)pari_version,11,"","version(): returns the PARI version as [major,minor,patch] or [major,minor,patch,VCSversion]."},
+{"warning",0,(void*)warning0,11,"vs*","warning({str}*): display warning message str"},
+{"weber",0,(void*)weber0,3,"GD0,L,p","weber(x,{flag=0}): One of Weber's f function of x. flag is optional, and can be 0: default, function f(x)=exp(-i*Pi/24)*eta((x+1)/2)/eta(x), 1: function f1(x)=eta(x/2)/eta(x) 2: function f2(x)=sqrt(2)*eta(2*x)/eta(x). Note that j = (f^24-16)^3/f^24 = (f1^24+16)^3/f1^24 = (f2^24+16)^3/f2^24."},
+{"while",0,(void*)whilepari,11,"vEI","while(a,seq): while a is nonzero evaluate the expression sequence seq. Otherwise 0."},
+{"write",0,(void*)write0,11,"vss*","write(filename,{str}*): appends the remaining arguments (same output as print) to filename."},
+{"write1",0,(void*)write1,11,"vss*","write1(filename,{str}*): appends the remaining arguments (same output as print1) to filename."},
+{"writebin",0,(void*)gpwritebin,11,"vsDG","writebin(filename,{x}): write x as a binary object to file filename. If x is omitted, write all session variables."},
+{"writetex",0,(void*)writetex,11,"vss*","writetex(filename,{str}*): appends the remaining arguments (same format as print) to filename, in TeX format."},
+{"zeta",0,(void*)gzeta,3,"Gp","zeta(s): Riemann zeta function at s with s a complex or a p-adic number."},
+{"zetak",0,(void*)gzetakall,6,"GGD0,L,p","zetak(nfz,x,{flag=0}): Dedekind zeta function of the number field nfz at x, where nfz is the vector computed by zetakinit (NOT by nfinit); flag is optional, and can be 0: default, compute zetak, or non-zero: compute the lambdak function, i.e. with the gamma factors."},
+{"zetakinit",0,(void*)initzeta,6,"Gp","zetakinit(bnf): compute number field information necessary to use zetak. bnf may also be an irreducible polynomial."},
+{"zncoppersmith",0,(void*)zncoppersmith,4,"GGGDG","zncoppersmith(P, N, X, {B=N}): finds all integers x with |x| <= X such that  gcd(N, P(x)) >= B. X should be smaller than exp((log B)^2 / (deg(P) log N))."},
+{"znlog",0,(void*)znlog,4,"GGDG","znlog(x,g,{o}): return the discrete logarithm of x in (Z/nZ)* in base g. If present, o represents the multiplicative order of g. Return [] if no solution exist."},
+{"znorder",0,(void*)znorder,4,"GDG","znorder(x,{o}): order of the integermod x in (Z/nZ)*. Optional o represents a multiple of the order of the element."},
+{"znprimroot",0,(void*)znprimroot,4,"G","znprimroot(n): returns a primitive root of n when it exists."},
+{"znstar",0,(void*)znstar,4,"G","znstar(n): 3-component vector v, giving the structure of (Z/nZ)^*. v[1] is the order (i.e. eulerphi(n)), v[2] is a vector of cyclic components, and v[3] is a vector giving the corresponding generators."},
+{NULL,0,NULL,0,NULL,NULL} /* sentinel */
+};
diff --git a/src/language/intnum.c b/src/language/intnum.c
new file mode 100644
index 0000000..8bd60c4
--- /dev/null
+++ b/src/language/intnum.c
@@ -0,0 +1,1785 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+/********************************************************************/
+/**                                                                **/
+/**                NUMERICAL INTEGRATION (Romberg)                 **/
+/**                                                                **/
+/********************************************************************/
+typedef struct {
+  void *E;
+  GEN (*f)(void *E, GEN);
+} invfun;
+
+/* 1/x^2 f(1/x) */
+static GEN
+_invf(void *E, GEN x)
+{
+  invfun *S = (invfun*)E;
+  GEN y = ginv(x);
+  return gmul(S->f(S->E, y), gsqr(y));
+}
+
+static GEN
+interp(GEN h, GEN s, long j, long lim, long KLOC)
+{
+  pari_sp av = avma;
+  long e1,e2;
+  GEN dss, ss = polint_i(h+j-KLOC,s+j-KLOC,gen_0,KLOC+1,&dss);
+
+  e1 = gexpo(ss);
+  e2 = gexpo(dss);
+  if (e1-e2 <= lim && (j <= 10 || e1 >= -lim)) { avma = av; return NULL; }
+  if (typ(ss) == t_COMPLEX && gequal0(gel(ss,2))) ss = gel(ss,1);
+  return ss;
+}
+
+static GEN
+qrom3(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long prec)
+{
+  const long JMAX = 25, KLOC = 4;
+  GEN ss,s,h,p1,p2,qlint,del,x,sum;
+  long j, j1, it, sig;
+
+  a = gtofp(a,prec);
+  b = gtofp(b,prec);
+  qlint = subrr(b,a); sig = signe(qlint);
+  if (!sig)  return gen_0;
+  if (sig < 0) { setabssign(qlint); swap(a,b); }
+
+  s = new_chunk(JMAX+KLOC-1);
+  h = new_chunk(JMAX+KLOC-1);
+  gel(h,0) = real_1(prec);
+
+  p1 = eval(E, a); if (p1 == a) p1 = rcopy(p1);
+  p2 = eval(E, b);
+  gel(s,0) = gmul2n(gmul(qlint,gadd(p1,p2)),-1);
+  for (it=1,j=1; j<JMAX; j++, it<<=1) /* it = 2^(j-1) */
+  {
+    pari_sp av, av2;
+    gel(h,j) = real2n(-2*j, prec); /* 2^(-2j) */
+    av = avma; del = divru(qlint,it);
+    x = addrr(a, shiftr(del,-1));
+    av2 = avma;
+    for (sum = gen_0, j1 = 1; j1 <= it; j1++, x = addrr(x,del))
+    {
+      sum = gadd(sum, eval(E, x));
+      if ((j1 & 0x1ff) == 0) gerepileall(av2, 2, &sum,&x);
+    }
+    sum = gmul(sum,del);
+    gel(s,j) = gerepileupto(av, gmul2n(gadd(gel(s,j-1), sum), -1));
+    if (DEBUGLEVEL>3) err_printf("qrom3: iteration %ld: %Ps\n", j,s[j]);
+
+    if (j >= KLOC && (ss = interp(h, s, j, prec2nbits(prec)-j-6, KLOC)))
+      return gmulsg(sig,ss);
+  }
+  return NULL;
+}
+
+static GEN
+qrom2(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long prec)
+{
+  const long JMAX = 16, KLOC = 4;
+  GEN ss,s,h,p1,qlint,del,ddel,x,sum;
+  long j, j1, it, sig;
+
+  a = gtofp(a, prec);
+  b = gtofp(b, prec);
+  qlint = subrr(b,a); sig = signe(qlint);
+  if (!sig)  return gen_0;
+  if (sig < 0) { setabssign(qlint); swap(a,b); }
+
+  s = new_chunk(JMAX+KLOC-1);
+  h = new_chunk(JMAX+KLOC-1);
+  gel(h,0) = real_1(prec);
+
+  p1 = shiftr(addrr(a,b),-1);
+  gel(s,0) = gmul(qlint, eval(E, p1));
+  for (it=1, j=1; j<JMAX; j++, it*=3) /* it = 3^(j-1) */
+  {
+    pari_sp av, av2;
+    gel(h,j) = divru(gel(h,j-1), 9); /* 3^(-2j) */
+    av = avma; del = divru(qlint,3*it); ddel = shiftr(del,1);
+    x = addrr(a, shiftr(del,-1));
+    av2 = avma;
+    for (sum = gen_0, j1 = 1; j1 <= it; j1++)
+    {
+      sum = gadd(sum, eval(E, x)); x = addrr(x,ddel);
+      sum = gadd(sum, eval(E, x)); x = addrr(x,del);
+      if ((j1 & 0x1ff) == 0) gerepileall(av2, 2, &sum,&x);
+    }
+    sum = gmul(sum,del); p1 = gdivgs(gel(s,j-1),3);
+    gel(s,j) = gerepileupto(av, gadd(p1,sum));
+    if (DEBUGLEVEL>3) err_printf("qrom2: iteration %ld: %Ps\n", j,s[j]);
+
+    if (j >= KLOC && (ss = interp(h, s, j, prec2nbits(prec)-(3*j/2)-6, KLOC)))
+      return gmulsg(sig, ss);
+  }
+  return NULL;
+}
+
+/* integrate after change of variables x --> 1/x */
+static GEN
+qromi(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, long prec)
+{
+  GEN A = ginv(b), B = ginv(a);
+  invfun S;
+  S.f = eval;
+  S.E = E; return qrom2(&S, &_invf, A, B, prec);
+}
+
+/* a < b, assume b "small" (< 100 say) */
+static GEN
+rom_bsmall(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, long prec)
+{
+  if (gcmpgs(a,-100) >= 0) return qrom2(E,eval,a,b,prec);
+  if (b == gen_1 || gcmpgs(b, -1) >= 0) /* a < -100, b >= -1 */
+    return gadd(qromi(E,eval,a,gen_m1,prec), /* split at -1 */
+                qrom2(E,eval,gen_m1,b,prec));
+  /* a < -100, b < -1 */
+  return qromi(E,eval,a,b,prec);
+}
+
+static GEN
+rombint(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, long prec)
+{
+  long l = gcmp(b,a);
+  GEN z;
+
+  if (!l) return gen_0;
+  if (l < 0) swap(a,b);
+  if (gcmpgs(b,100) >= 0)
+  {
+    if (gcmpgs(a,1) >= 0)
+      z = qromi(E,eval,a,b,prec);
+    else /* split at 1 */
+      z = gadd(rom_bsmall(E,eval,a,gen_1,prec), qromi(E,eval,gen_1,b,prec));
+  }
+  else
+    z = rom_bsmall(E,eval,a,b,prec);
+  if (l < 0) z = gneg(z);
+  return z;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                DOUBLE EXPONENTIAL INTEGRATION                  **/
+/**                                                                **/
+/********************************************************************/
+
+/* The init functions have the following purposes:
+* 1) They fill the value tabx0 = phi(0) and arrays of abcissas
+*   tabxp[] = phi(k/2^m) (positive x) and also of tabxm[] = phi(-k/2^m)
+*   (negative x) unless the phi function is odd, in which case this is useless.
+* 2) They fill the corresponding arrays of weights tabw0 = phi'(0) and
+*   tabwp[] = phi'(k/2^m) (and possibly also of tabwm[] = phi'(-k/2^m)).
+* 3) They set eps to the desired accuracy (depending on the GP default).
+* 4) They compute nt which says that the weights tabwp[k] and tabwm[k] are
+*   negligible with respect to eps if k > nt. In particular the tabxx[] arrays
+*   are indexed from 1 to nt+1. */
+
+typedef struct _intdata {
+  long m;    /* integration step h = 1/2^m */
+  long eps;  /* bit accuracy of current precision */
+  GEN tabx0; /* abcissa phi(0) for t = 0 */
+  GEN tabw0; /* weight phi'(0) for t = 0 */
+  GEN tabxp; /* table of abcissas phi(kh) for k > 0 */
+  GEN tabwp; /* table of weights phi'(kh) for k > 0 */
+  GEN tabxm; /* table of abcissas phi(kh) for k < 0 */
+  GEN tabwm; /* table of weights phi'(kh) for k < 0 */
+} intdata;
+
+#define TABm(v)  gel(v,1)
+#define TABx0(v) gel(v,2)
+#define TABw0(v) gel(v,3)
+#define TABxp(v) gel(v,4)
+#define TABwp(v) gel(v,5)
+#define TABxm(v) gel(v,6)
+#define TABwm(v) gel(v,7)
+
+static int
+isinR(GEN z)
+{
+  long tz = typ(z);
+  return (tz == t_INT || tz == t_REAL || tz == t_FRAC);
+}
+
+static int
+isinC(GEN z)
+{
+  return (typ(z) == t_COMPLEX)? isinR(gel(z,1)) && isinR(gel(z,2)):
+                                isinR(z);
+}
+
+static int
+checktabsimp(GEN tab)
+{
+  long L, LN, LW;
+  if (!tab || typ(tab) != t_VEC) return 0;
+  if (lg(tab) != 8) return 0;
+  if (typ(TABm(tab))!= t_INT) return 0;
+  if (typ(TABxp(tab)) != t_VEC) return 0;
+  if (typ(TABwp(tab)) != t_VEC) return 0;
+  if (typ(TABxm(tab)) != t_VEC) return 0;
+  if (typ(TABwm(tab)) != t_VEC) return 0;
+  L = lg(TABxp(tab)); if (lg(TABwp(tab)) != L) return 0;
+  LN = lg(TABxm(tab)); if (LN != 1 && LN != L) return 0;
+  LW = lg(TABwm(tab)); if (LW != 1 && LW != L) return 0;
+  return 1;
+}
+
+static int
+checktabdoub(GEN tab)
+{
+  long L;
+  if (typ(tab) != t_VEC) return 0;
+  if (lg(tab) != 8) return 0;
+  if (typ(TABm(tab)) != t_INT) return 0;
+  L = lg(TABxp(tab));
+  if (lg(TABwp(tab)) != L) return 0;
+  if (lg(TABxm(tab)) != L) return 0;
+  if (lg(TABwm(tab)) != L) return 0;
+  return 1;
+}
+
+static int
+checktab(GEN tab)
+{
+  if (typ(tab) != t_VEC) return 0;
+  if (lg(tab) != 3) return checktabsimp(tab);
+  return checktabsimp(gel(tab,1))
+      && checktabsimp(gel(tab,2));
+}
+
+static long
+findmforinit(long m, long prec)
+{
+  long p, r;
+
+  if (m <= 0)
+  {
+    p = (long)prec2nbits_mul(prec, 0.3);
+    m = 2; r = 4;
+    while (r < p) { m++; r <<= 1; }
+  }
+  return m;
+}
+
+long
+intnumstep(long prec) { return findmforinit(0, prec); }
+
+static void
+intinit_start(intdata *D, long m0, long flext, long prec)
+{
+  long m = findmforinit(m0, prec), lim = 20L<<m;
+  if (flext > 0) lim = lim << (2*flext);
+  D->m = m;
+  D->eps = prec2nbits(prec);
+  D->tabxp = cgetg(lim+1, t_VEC);
+  D->tabwp = cgetg(lim+1, t_VEC);
+  D->tabxm = cgetg(lim+1, t_VEC);
+  D->tabwm = cgetg(lim+1, t_VEC);
+}
+
+static GEN
+intinit_end(intdata *D, long pnt, long mnt)
+{
+  GEN v = cgetg(8, t_VEC);
+  if (pnt < 0) pari_err_DOMAIN("intnuminit","table length","<",gen_0,stoi(pnt));
+  gel(v,1) = stoi(D->m);
+  TABx0(v) = D->tabx0;
+  TABw0(v) = D->tabw0;
+  TABxp(v) = D->tabxp; setlg(D->tabxp, pnt+1);
+  TABwp(v) = D->tabwp; setlg(D->tabwp, pnt+1);
+  TABxm(v) = D->tabxm; setlg(D->tabxm, mnt+1);
+  TABwm(v) = D->tabwm; setlg(D->tabwm, mnt+1); return v;
+}
+
+static const long EXTRAPREC =
+#ifdef LONG_IS_64BIT
+  1;
+#else
+  2;
+#endif
+
+/* divide by 2 in place */
+static GEN
+divr2_ip(GEN x) { shiftr_inplace(x, -1); return x; }
+
+/* phi(t)=tanh((3/2)sinh(t)) : from -1 to 1, hence also from a to b compact
+ * interval. */
+static GEN
+inittanhsinh(long m, long prec)
+{
+  pari_sp av, ltop = avma;
+  GEN h, et, ct, st, ext, ex, xp, wp;
+  long k, nt = -1, lim;
+  intdata D; intinit_start(&D, m, 0, prec);
+
+  lim = lg(D.tabxp) - 1;
+  D.tabx0 = real_0(prec);
+  D.tabw0 = divr2_ip(stor(3, prec));
+  h = real2n(-D.m, prec);
+  et = ex = mpexp(h);
+  for (k = 1; k <= lim; k++)
+  {
+    gel(D.tabxp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwp,k) = cgetr(prec+EXTRAPREC); av = avma;
+    ct = divr2_ip(addrr(et, invr(et)));
+    st = subrr(et, ct);
+    ext = invr( addrs(mpexp(mulur(3, st)), 1) );
+    shiftr_inplace(ext, 1);
+    xp = subsr(1, ext);
+    wp = divr2_ip(mulur(3, mulrr(ct, mulrr(ext, addsr(1, xp)))));
+    if (expo(wp) < -D.eps) { nt = k-1; break; }
+    affrr(xp, gel(D.tabxp,k));
+    affrr(wp, gel(D.tabwp,k)); et = gerepileuptoleaf(av, mulrr(et, ex));
+  }
+  return gerepilecopy(ltop, intinit_end(&D, nt, 0));
+}
+
+/* phi(t)=sinh(sinh(t)) : from -\infty to \infty, slowly decreasing, at least
+ * as 1/x^2. */
+static GEN
+initsinhsinh(long m, long prec)
+{
+  pari_sp av, ltop = avma;
+  GEN h, et, ct, st, ext, exu, ex, xp, wp;
+  long k, nt = -1, lim;
+  intdata D; intinit_start(&D, m, 0, prec);
+
+  lim = lg(D.tabxp) - 1;
+  D.tabx0 = real_0(prec);
+  D.tabw0 = real_1(prec);
+  h = real2n(-D.m, prec);
+  et = ex = mpexp(h);
+  for (k = 1; k <= lim; k++)
+  {
+    gel(D.tabxp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwp,k) = cgetr(prec+EXTRAPREC); av = avma;
+    ct = divr2_ip(addrr(et, invr(et)));
+    st = subrr(et, ct);
+    ext = mpexp(st);
+    exu = invr(ext);
+    xp = divr2_ip(subrr(ext, exu));
+    wp = divr2_ip(mulrr(ct, addrr(ext, exu)));
+    if (expo(wp) - 2*expo(xp) < -D.eps) { nt = k-1; break; }
+    affrr(xp, gel(D.tabxp,k));
+    affrr(wp, gel(D.tabwp,k)); et = gerepileuptoleaf(av, mulrr(et, ex));
+  }
+  return gerepilecopy(ltop, intinit_end(&D, nt, 0));
+}
+
+/* phi(t)=2sinh(t) : from -\infty to \infty, exponentially decreasing as
+ * exp(-x). */
+static GEN
+initsinh(long m, long prec)
+{
+  pari_sp av, ltop = avma;
+  GEN h, et, ex, eti, xp, wp;
+  long k, nt = -1, lim;
+  intdata D; intinit_start(&D, m, 0, prec);
+
+  lim = lg(D.tabxp) - 1;
+  D.tabx0 = real_0(prec);
+  D.tabw0 = real2n(1, prec);
+  h = real2n(-D.m, prec);
+  et = ex = mpexp(h);
+  for (k = 1; k <= lim; k++)
+  {
+    gel(D.tabxp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwp,k) = cgetr(prec+EXTRAPREC); av = avma;
+    eti = invr(et);
+    xp = subrr(et, eti);
+    wp = addrr(et, eti);
+    if (cmprs(xp, (long)(LOG2*(expo(wp)+D.eps) + 1)) > 0) { nt = k-1; break; }
+    affrr(xp, gel(D.tabxp,k));
+    affrr(wp, gel(D.tabwp,k)); et = gerepileuptoleaf(av, mulrr(et, ex));
+  }
+  return gerepilecopy(ltop, intinit_end(&D, nt, 0));
+}
+
+/* phi(t)=exp(2sinh(t)) : from 0 to \infty, slowly decreasing at least as
+ * 1/x^2. */
+static GEN
+initexpsinh(long m, long prec)
+{
+  pari_sp ltop = avma;
+  GEN h, et, eti, ex, xp;
+  long k, nt = -1, lim;
+  intdata D; intinit_start(&D, m, 0, prec);
+
+  lim = lg(D.tabxp) - 1;
+  D.tabx0 = real_1(prec);
+  D.tabw0 = real2n(1, prec);
+  h = real2n(-D.m, prec);
+  ex = mpexp(h);
+  et = real_1(prec);
+  for (k = 1; k <= lim; k++)
+  {
+    GEN t;
+    et = mulrr(et, ex);
+    eti = invr(et); t = addrr(et, eti);
+    xp = mpexp(subrr(et, eti));
+    gel(D.tabxp,k) = xp;
+    gel(D.tabwp,k) = mulrr(xp, t);
+    gel(D.tabxm,k) = invr(xp);
+    gel(D.tabwm,k) = mulrr(gel(D.tabxm,k), t);
+    if (expo(gel(D.tabxm,k)) < -D.eps) { nt = k-1; break; }
+  }
+  return gerepilecopy(ltop, intinit_end(&D, nt, nt));
+}
+
+/* phi(t)=exp(t-exp(-t)) : from 0 to \infty, exponentially decreasing. */
+static GEN
+initexpexp(long m, long prec)
+{
+  pari_sp av, ltop = avma;
+  GEN kh, h, et, eti, ex, xp, xm, wp, wm;
+  long k, nt = -1, lim;
+  intdata D; intinit_start(&D, m, 0, prec);
+
+  lim = lg(D.tabxp) - 1;
+  D.tabx0 = mpexp(real_m1(prec));
+  D.tabw0 = gmul2n(D.tabx0, 1);
+  h = real2n(-D.m, prec);
+  et = ex = mpexp(negr(h));
+  for (k = 1; k <= lim; k++)
+  {
+    gel(D.tabxp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabxm,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwm,k) = cgetr(prec+EXTRAPREC); av = avma;
+    eti = invr(et); kh = mulur(k,h);
+    xp = mpexp(subrr(kh, et));
+    xm = mpexp(negr(addrr(kh, eti)));
+    wp = mulrr(xp, addsr(1, et));
+    wm = mulrr(xm, addsr(1, eti));
+    if (expo(xm) < -D.eps && cmprs(xp, (long)(LOG2*(expo(wp)+D.eps) + 1)) > 0) { nt = k-1; break; }
+    affrr(xp, gel(D.tabxp,k));
+    affrr(wp, gel(D.tabwp,k));
+    affrr(xm, gel(D.tabxm,k));
+    affrr(wm, gel(D.tabwm,k)); et = gerepileuptoleaf(av, mulrr(et, ex));
+  }
+  return gerepilecopy(ltop, intinit_end(&D, nt, nt));
+}
+
+/* phi(t)=(Pi/h)t/(1-exp(-sinh(t))) : from 0 to \infty, sine oscillation. */
+static GEN
+initnumsine(long m, long prec)
+{
+  pari_sp av, ltop = avma;
+  GEN h, et, eti, ex, st, ct, extp, extm, extp1, extm1, extp2, extm2, kpi, kct;
+  GEN xp, xm, wp, wm, pi = mppi(prec);
+  long k, nt = -1, lim;
+  intdata D; intinit_start(&D, m, 0, prec);
+
+  lim = lg(D.tabxp) - 1;
+  D.tabx0 = gmul2n(pi, D.m);
+  D.tabw0 = gmul2n(pi, D.m - 1);
+  h = real2n(-D.m, prec);
+  et = ex = mpexp(h);
+  for (k = 1; k <= lim; k++)
+  {
+    gel(D.tabxp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwp,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabxm,k) = cgetr(prec+EXTRAPREC);
+    gel(D.tabwm,k) = cgetr(prec+EXTRAPREC); av = avma;
+    eti = invr(et); /* exp(-kh) */
+    ct = divr2_ip(addrr(et, eti));
+    st = divr2_ip(subrr(et, eti));
+    extp = mpexp(st);  extp1 = subsr(1, extp); extp2 = invr(extp1);
+    extm = invr(extp); extm1 = subsr(1, extm); extm2 = invr(extm1);
+    kpi = mulur(k, pi);
+    kct = mulur(k, ct);
+    shiftr_inplace(extm1, D.m);
+    shiftr_inplace(extp1, D.m);
+    xp = mulrr(kpi, extm2);
+    wp = mulrr(subrr(extm1, mulrr(kct, extm)), mulrr(pi, sqrr(extm2)));
+    xm = mulrr(negr(kpi), extp2);
+    wm = mulrr(addrr(extp1, mulrr(kct, extp)), mulrr(pi, sqrr(extp2)));
+    if (expo(wm) < -D.eps && expo(extm) + D.m + expu(10 * k) < -D.eps) { nt = k-1; break; }
+    affrr(xp, gel(D.tabxp,k));
+    affrr(wp, gel(D.tabwp,k));
+    affrr(xm, gel(D.tabxm,k));
+    affrr(wm, gel(D.tabwm,k)); et = gerepileuptoleaf(av, mulrr(et, ex));
+  }
+  return gerepilecopy(ltop, intinit_end(&D, nt, nt));
+}
+
+static GEN
+suminit_start(GEN sig)
+{
+  GEN sig2;
+
+  if (typ(sig) == t_VEC)
+  {
+    if (lg(sig) != 3) pari_err_TYPE("sumnum",sig);
+    sig2 = gel(sig,2);
+    sig  = gel(sig,1);
+    if (!isinR(sig2)) pari_err_TYPE("sumnum",sig2);
+    if (gsigne(sig2) > 0) sig2 = mulcxmI(sig2);
+  }
+  else sig2 = gen_0;
+  if (!isinR(sig)) pari_err_TYPE("sumnum",sig);
+  return mkvec2(mkvec(gen_1), sig2);
+}
+
+/* phi(t) depending on sig[2] as in intnum, with weights phi'(t)tanh(Pi*phi(t))
+ * (sgn >= 0) or phi'(t)/cosh(Pi*phi(t)) (otherwise), for use in sumnumall.
+ * integrations are done from 0 to +infty (flii is set to 0), except if slowly
+   decreasing, from -infty to +infty (flii is set to 1). */
+GEN
+sumnuminit(GEN sig, long m, long sgn, long prec)
+{
+  pari_sp ltop = avma;
+  GEN b, t, tab, tabxp, tabwp, tabxm, tabwm, pi = mppi(prec);
+  long L, k, eps, flii;
+
+  b = suminit_start(sig);
+  flii = gequal0(gel(b,2));
+  if (flii)
+    tab = intnuminit(mkvec(gen_m1), mkvec(gen_1), m, prec);
+  else
+    tab = intnuminit(gen_0, b, m, prec);
+  eps = prec2nbits(prec);
+  t = gmul(pi, TABx0(tab));
+  if (sgn < 0) TABw0(tab) = gdiv(TABw0(tab), gcosh(t, prec));
+  else         TABw0(tab) = gmul(TABw0(tab), gtanh(t, prec));
+  tabxp = TABxp(tab); L = lg(tabxp);
+  tabwp = TABwp(tab);
+  tabxm = TABxm(tab);
+  tabwm = TABwm(tab);
+  for (k = 1; k < L; k++)
+  {
+    if (cmprs(gel(tabxp,k), eps) < 0)
+    {
+      t = mulrr(pi, gel(tabxp,k));
+      gel(tabwp,k) = (sgn < 0)? divrr(gel(tabwp,k), gcosh(t, prec))
+                              : mulrr(gel(tabwp,k), gtanh(t, prec));
+    }
+    else
+      if (sgn < 0) gel(tabwp,k) = real_0_bit(-eps);
+    if (!flii)
+    {
+      t = mulrr(pi, gel(tabxm,k));
+      gel(tabwm,k) = (sgn < 0)? divrr(gel(tabwm,k), gcosh(t, prec))
+                              : mulrr(gel(tabwm,k), gtanh(t, prec));
+    }
+  }
+  return gerepilecopy(ltop, tab);
+}
+
+/* End of initialization functions. These functions can be executed once
+ * and for all for a given accuracy, type of integral ([a,b], [a,\infty[ or
+ * ]-\infty,a], ]-\infty,\infty[) and of integrand in the noncompact case
+ * (slowly decreasing, exponentially decreasing, oscillating with a fixed
+ * oscillating factor such as sin(x)). */
+
+/* In the following integration functions the parameters are as follows:
+* 1) The parameter denoted by m is the most crucial and difficult to
+* determine in advance: h = 1/2^m is the integration step size. Usually
+* m = floor(log(D)/log(2)), where D is the number of decimal digits of accuracy
+* is plenty for very regulat functions, for instance m = 6 for 100D, and m = 9
+* for 1000D, but values of m 1 or 2 less are often sufficient, while for
+* singular functions, 1 or 2 more may be necessary. The best test is to take 2
+* or 3 consecutive values of m and look. Note that the number of function
+* evaluations, hence the time doubles when m increases by 1. */
+
+/* All inner functions such as intn, etc... must be called with a
+ * valid 'tab' table. The wrapper intnum provides a higher level interface */
+
+/* compute $\int_a^b f(t)dt$ with [a,b] compact and f nonsingular. */
+static GEN
+intn(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN tab)
+{
+  GEN tabx0, tabw0, tabxp, tabwp;
+  GEN bpa, bma, bmb, S, SP, SM;
+  long m, k, L, i;
+  pari_sp ltop = avma, av;
+
+  if (!checktabsimp(tab)) pari_err_TYPE("intnum",tab);
+  if (!isinC(a)) pari_err_TYPE("intnum",a);
+  if (!isinC(b)) pari_err_TYPE("intnum",b);
+  m = itos(TABm(tab));
+  tabx0 = TABx0(tab); tabw0 = TABw0(tab);
+  tabxp = TABxp(tab); tabwp = TABwp(tab); L = lg(tabxp);
+  bpa = gmul2n(gadd(b, a), -1); /* (b+a)/2 */
+  bma = gsub(bpa, a); /* (b-a)/2 */
+  bmb = gmul(bma, tabx0); /* (b-a)/2 phi(0) */
+  av = avma;
+  /* phi'(0) f( (b+a)/2 + (b-a)/2 * phi(0) ) */
+  S = gmul(tabw0, eval(E, gadd(bpa, bmb)));
+  for (k = 1; k <= m; k++)
+  {
+    long pas = 1L<<(m-k);
+    for (i = pas; i < L; i += pas)
+      if (i & pas || k == 1)
+      {
+        bmb = gmul(bma, gel(tabxp,i));
+        SP = eval(E, gsub(bpa, bmb));
+        SM = eval(E, gadd(bpa, bmb));
+        S = gadd(S, gmul(gel(tabwp,i), gadd(SP, SM)));
+        if ((i & 0x7f) == 1) S = gerepileupto(av, S);
+      }
+  }
+  return gerepileupto(ltop, gmul(S, gmul2n(bma, -m)));
+}
+
+/* compute $\int_{a[1]}^{b} f(t)dt$ with [a,b] compact, possible
+ *  singularity with exponent a[2] at lower extremity, b regular.
+ *  Use tanh(sinh(t)). */
+static GEN
+intnsing(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN tab, long prec)
+{
+  GEN tabx0, tabw0, tabxp, tabwp, ea, ba, bm, bp, S, tra, SP, SM;
+  long m, k, L, i;
+  pari_sp ltop = avma, av;
+
+  if (!checktabsimp(tab)) pari_err_TYPE("intnum",tab);
+  m = itos(TABm(tab));
+  tabx0 = TABx0(tab); tabw0 = TABw0(tab);
+  tabxp = TABxp(tab); tabwp = TABwp(tab); L = lg(tabxp);
+  tra = gel(a,1);
+  ea = ginv(gaddsg(1, gel(a,2)));
+  ba = gdiv(gsub(b, tra), gpow(gen_2, ea, prec));
+  av = avma;
+  S = gmul(gmul(tabw0, ba), eval(E, gadd(gmul(ba, gaddsg(1, tabx0)), tra)));
+  for (k = 1; k <= m; k++)
+  {
+    long pas = 1L<<(m-k);
+    for (i = pas; i < L; i += pas)
+      if (i & pas || k == 1) /* i = odd multiple of pas = 2^(m-k) */
+      {
+        GEN p = addsr(1, gel(tabxp,i));
+        GEN m = subsr(1, gel(tabxp,i));
+        bp = gmul(ba, gpow(p, ea, prec));
+        bm = gmul(ba, gpow(m, ea, prec));
+        SP = gmul(gdiv(bp, p), eval(E, gadd(bp, tra)));
+        SM = gmul(gdiv(bm, m), eval(E, gadd(bm, tra)));
+        S = gadd(S, gmul(gel(tabwp,i), gadd(SP, SM)));
+        if ((i & 0x7f) == 1) S = gerepileupto(av, S);
+      }
+  }
+  return gerepileupto(ltop, gmul(gmul2n(S, -m), ea));
+}
+
+/* compute  $\int_a^\infty f(t)dt$ if $si=1$ or $\int_{-\infty}^a f(t)dt$
+   if $si=-1$. Use exp(2sinh(t)) for slowly decreasing functions,
+   exp(1+t-exp(-t)) for exponentially decreasing functions, and
+   (pi/h)t/(1-exp(-sinh(t))) for oscillating functions. */
+
+static GEN
+intninfpm(void *E, GEN (*eval)(void*, GEN), GEN a, long si, GEN tab)
+{
+  GEN tabx0, tabw0, tabxp, tabwp, tabxm, tabwm;
+  GEN S, SP, SM;
+  long m, L, k, h = 0, pas, i;
+  pari_sp ltop = avma, av;
+
+  if (!checktabdoub(tab)) pari_err_TYPE("intnum",tab);
+  m = itos(TABm(tab));
+  tabx0 = TABx0(tab); tabw0 = TABw0(tab);
+  tabxp = TABxp(tab); tabwp = TABwp(tab); L = lg(tabxp);
+  tabxm = TABxm(tab); tabwm = TABwm(tab);
+  if (si < 0) { tabxp = gneg(tabxp); tabxm = gneg(tabxm); }
+  av = avma;
+  S = gmul(tabw0, eval(E, gadd(a, gmulsg(si, tabx0))));
+  for (k = 1; k <= m; k++)
+  {
+    h++; pas = 1L<<(m-k);
+    for (i = pas; i < L; i += pas)
+      if (i & pas || k == 1)
+      {
+        SP = eval(E, gadd(a, gel(tabxp,i)));
+        SM = eval(E, gadd(a, gel(tabxm,i)));
+        S = gadd(S, gadd(gmul(gel(tabwp,i), SP), gmul(gel(tabwm,i), SM)));
+        if ((i & 0x7f) == 1) S = gerepileupto(av, S);
+      }
+  }
+  return gerepileupto(ltop, gmul2n(S, -h));
+}
+
+/* compute  $\int_{-\infty}^\infty f(t)dt$
+ * use sinh(sinh(t)) for slowly decreasing functions and sinh(t) for
+ * exponentially decreasing functions.
+ * HACK: in case TABwm(tab) contains something, assume function to be integrated
+ * satisfies f(-x) = conj(f(x)).
+ * Usually flag < 0, but flag > 0 is used in sumnumall. */
+static GEN
+intninfinfintern(void *E, GEN (*eval)(void*, GEN), GEN tab, long flag)
+{
+  GEN tabx0, tabw0, tabxp, tabwp, tabwm;
+  GEN S, SP, SM;
+  long m, L, k, i, spf;
+  pari_sp ltop = avma;
+
+  if (!checktabsimp(tab)) pari_err_TYPE("intnum",tab);
+  m = itos(TABm(tab));
+  tabx0 = TABx0(tab); tabw0 = TABw0(tab);
+  tabxp = TABxp(tab); tabwp = TABwp(tab); L = lg(tabxp);
+  tabwm = TABwm(tab);
+  spf = (lg(tabwm) == lg(tabwp));
+  S = flag > 0 ? gen_0 : gmul(tabw0, eval(E, tabx0));
+  if (spf) S = gmul2n(real_i(S), -1);
+  for (k = 1; k <= m; k++)
+  {
+    long pas = 1L<<(m-k);
+    for (i = pas; i < L; i += pas)
+      if (i & pas || k == 1)
+      {
+        SP = eval(E, gel(tabxp,i));
+        if (spf) S = gadd(S, real_i(gmul(gel(tabwp,i), SP)));
+        else
+        {
+          SM = eval(E, negr(gel(tabxp,i)));
+          if (flag > 0) SM = gneg(SM);
+          S = gadd(S, gmul(gel(tabwp,i), gadd(SP, SM)));
+        }
+        if ((i & 0x7f) == 1) S = gerepileupto(ltop, S);
+      }
+  }
+  if (spf) m--;
+  return gerepileupto(ltop, gmul2n(S, -m));
+}
+
+static GEN
+intninfinf(void *E, GEN (*eval)(void*, GEN), GEN tab)
+{
+  return intninfinfintern(E, eval, tab, -1);
+}
+
+/* general num integration routine int_a^b f(t)dt, where a and b are as follows:
+ (1) a scalar : the scalar, no singularity worse than logarithmic at a.
+ (2) [a, e] : the scalar a, singularity exponent -1 < e <= 0.
+ (3) [1], [-1] : +\infty, -\infty, slowly decreasing function.
+ (4) [[+-1], a], a nonnegative real : +-\infty, function behaving like
+      exp(-a|t|) at +-\infty.
+ (5) [[+-1], e], e < -1 : +-\infty, function behaving like t^e
+      at +-\infty.
+ (5) [[+-1], a*I], a real : +-\infty, function behaving like cos(at) if a>0
+     and like sin(at) if a < 0 at +-\infty.
+*/
+
+/* FIXME: The numbers below can be changed, but NOT the ordering */
+enum {
+  f_REG    = 0, /* regular function */
+  f_SING   = 1, /* algebraic singularity */
+  f_YSLOW  = 2, /* +\infty, slowly decreasing */
+  f_YVSLO  = 3, /* +\infty, very slowly decreasing */
+  f_YFAST  = 4, /* +\infty, exponentially decreasing */
+  f_YOSCS  = 5, /* +\infty, sine oscillating */
+  f_YOSCC  = 6  /* +\infty, cosine oscillating */
+};
+/* is c finite */
+static int
+is_fin_f(long c) { return c == f_REG || c == f_SING; }
+/* oscillating case: valid for +oo (c > 0) or -oo (c < 0) */
+static int
+is_osc_f(long c) { c = labs(c); return c == f_YOSCS || c == f_YOSCC; }
+
+static GEN
+f_getycplx(GEN a, long prec)
+{
+  long s;
+  GEN tmp, a2R, a2I;
+
+  if (lg(a) == 2 || gequal0(gel(a,2))) return gen_1;
+  a2R = real_i(gel(a,2));
+  a2I = imag_i(gel(a,2));
+  s = gsigne(a2I); if (s < 0) a2I = gneg(a2I);
+  tmp = s ? ginv(a2I) : ginv(a2R);
+  if (gprecision(tmp) < prec) tmp = gprec_w(tmp, prec);
+  return tmp;
+}
+
+static void
+err_code(GEN a, const char *name)
+{
+  char *s = stack_sprintf("intnum [incorrect %s]", name);
+  pari_err_TYPE(s, a);
+}
+
+/* a = [[+/-1], alpha]*/
+static long
+code_aux(GEN a, const char *name)
+{
+  GEN re, im, alpha = gel(a,2);
+  long s;
+  if (!isinC(alpha)) err_code(a, name);
+  re = real_i(alpha);
+  im = imag_i(alpha);
+  s = gsigne(im);
+  if (s)
+  {
+    if(!gequal0(re))
+      pari_warn(warner,"real(z)*imag(z)!=0 in endpoint code, real(z) ignored");
+    return s > 0 ? f_YOSCC : f_YOSCS;
+  }
+  if (gequal0(re) || gcmpgs(re, -2)<=0) return f_YSLOW;
+  if (gsigne(re) > 0) return f_YFAST;
+  if (gcmpgs(re, -1) >= 0) err_code(a, name);
+  return f_YVSLO;
+}
+
+static long
+transcode(GEN a, const char *name)
+{
+  GEN a1, a2;
+  if (typ(a) != t_VEC) return f_REG;
+  switch(lg(a))
+  {
+    case 2: return gsigne(gel(a,1)) > 0 ? f_YSLOW : -f_YSLOW;
+    case 3: a1 = gel(a,1); a2 = gel(a,2); break;
+    default: err_code(a,name);
+  }
+  a1 = gel(a,1);
+  a2 = gel(a,2);
+  if (typ(a1) != t_VEC)
+  {
+    if (!isinC(a1) || !isinR(a2) || gcmpgs(a2, -1) <= 0) err_code(a,name);
+    return gsigne(a2) < 0 ? f_SING : f_REG;
+  }
+  if (lg(a1) != 2) err_code(a,name);
+  return gsigne(gel(a1,1)) * code_aux(a, name);
+}
+
+/* computes the necessary tabs, knowing a, b and m */
+static GEN
+homtab(GEN tab, GEN k)
+{
+  GEN z;
+  if (gequal0(k) || gequal(k, gen_1)) return tab;
+  if (gsigne(k) < 0) k = gneg(k);
+  z = cgetg(8, t_VEC);
+  TABm(z)  = icopy(TABm(tab));
+  TABx0(z) = gmul(TABx0(tab), k);
+  TABw0(z) = gmul(TABw0(tab), k);
+  TABxp(z) = gmul(TABxp(tab), k);
+  TABwp(z) = gmul(TABwp(tab), k);
+  TABxm(z) = gmul(TABxm(tab), k);
+  TABwm(z) = gmul(TABwm(tab), k); return z;
+}
+
+static GEN
+expvec(GEN v, GEN ea, long prec)
+{
+  long lv = lg(v), i;
+  GEN z = cgetg(lv, t_VEC);
+  for (i = 1; i < lv; i++) gel(z,i) = gpow(gel(v,i),ea,prec);
+  return z;
+}
+
+static GEN
+expscalpr(GEN vnew, GEN xold, GEN wold, GEN ea)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gdiv(gmul(gmul(vnew, wold), ea), xold));
+}
+static GEN
+expvecpr(GEN vnew, GEN xold, GEN wold, GEN ea)
+{
+  long lv = lg(vnew), i;
+  GEN z = cgetg(lv, t_VEC);
+  for (i = 1; i < lv; i++)
+    gel(z,i) = expscalpr(gel(vnew,i), gel(xold,i), gel(wold,i), ea);
+  return z;
+}
+
+/* here k < -1 */
+static GEN
+exptab(GEN tab, GEN k, long prec)
+{
+  GEN v, ea;
+
+  if (gcmpgs(k, -2) <= 0) return tab;
+  ea = ginv(gsubsg(-1, k));
+  v = cgetg(8, t_VEC);
+  TABm(v) = icopy(TABm(tab));
+  TABx0(v) = gpow(TABx0(tab), ea, prec);
+  TABw0(v) = expscalpr(TABx0(v), TABx0(tab), TABw0(tab), ea);
+  TABxp(v) = expvec(TABxp(tab), ea, prec);
+  TABwp(v) = expvecpr(TABxp(v), TABxp(tab), TABwp(tab), ea);
+  TABxm(v) = expvec(TABxm(tab), ea, prec);
+  TABwm(v) = expvecpr(TABxm(v), TABxm(tab), TABwm(tab), ea);
+  return v;
+}
+
+GEN
+intnuminit(GEN a, GEN b, long m, long prec)
+{
+  long codea, codeb, l;
+  GEN T, U, km, kma, kmb, tmp;
+
+  if (m > 30) pari_err_OVERFLOW("intnuminit [m]");
+  l = prec+EXTRAPREC;
+  codea = transcode(a, "a");
+  codeb = transcode(b, "b");
+  if (is_fin_f(codea) && is_fin_f(codeb)) return inittanhsinh(m, l);
+  if (labs(codea) > labs(codeb)) { swap(a, b); lswap(codea, codeb); }
+  if (codea == f_REG)
+  {
+    km = f_getycplx(b, l);
+    switch(labs(codeb))
+    {
+      case f_YSLOW: return initexpsinh(m, l);
+      case f_YVSLO: return exptab(initexpsinh(m, l), gel(b,2), prec);
+      case f_YFAST: return homtab(initexpexp(m, l), km);
+      case f_YOSCS:
+        if (typ(a) == t_VEC || gequal0(a)) return homtab(initnumsine(m, l), km);
+            /* fall through */
+      case f_YOSCC:
+        T = cgetg(3, t_VEC);
+        gel(T,1) = inittanhsinh(m, l);
+        gel(T,2) = homtab(initnumsine(m, l), km);
+        return T;
+    }
+  }
+  if (codea == f_SING)
+  {
+    km = f_getycplx(b, l);
+    T = cgetg(3, t_VEC);
+    gel(T,1) = inittanhsinh(m, l);
+    switch(labs(codeb))
+    {
+      case f_YSLOW: gel(T,2) = initexpsinh(m, l); break;
+      case f_YVSLO: gel(T,2) = exptab(initexpsinh(m, l), gel(b,2), prec); break;
+      case f_YFAST: gel(T,2) = homtab(initexpexp(m, l), km); break;
+      case f_YOSCS: case f_YOSCC:
+        gel(T,2) = homtab(initnumsine(m, l), km); break;
+    }
+    return T;
+  }
+  if (codea * codeb > 0) return gen_0;
+  kma = f_getycplx(a, l);
+  kmb = f_getycplx(b, l);
+  codea = labs(codea);
+  codeb = labs(codeb);
+  if (codea == f_YSLOW && codeb == f_YSLOW) return initsinhsinh(m, l);
+  if (codea == f_YFAST && codeb == f_YFAST && gequal(kma, kmb))
+    return homtab(initsinh(m, l), kmb);
+  T = cgetg(3, t_VEC);
+  switch (codea)
+  {
+    case f_YSLOW: gel(T,1) = initexpsinh(m, l);
+      switch (codeb)
+      {
+        case f_YVSLO: gel(T,2) = exptab(gel(T,1), gel(b,2), prec); return T;
+        case f_YFAST: gel(T,2) = homtab(initexpexp(m, l), kmb); return T;
+        case f_YOSCS: case f_YOSCC:
+          gel(T,2) = homtab(initnumsine(m, l), kmb); return T;
+      }
+    case f_YVSLO:
+      tmp = initexpsinh(m, l);
+      gel(T,1) = exptab(tmp, gel(a,2), prec);
+      switch (codeb)
+      {
+        case f_YVSLO: gel(T,2) = exptab(tmp, gel(b,2), prec); return T;
+        case f_YFAST: gel(T,2) = homtab(initexpexp(m, l), kmb); return T;
+        case f_YOSCS:
+        case f_YOSCC: gel(T,2) = homtab(initnumsine(m, l), kmb); return T;
+      }
+    case f_YFAST:
+      tmp = initexpexp(m, l);
+      gel(T,1) = homtab(tmp, kma);
+      switch (codeb)
+      {
+        case f_YFAST: gel(T,2) = homtab(tmp, kmb); return T;
+        case f_YOSCS:
+        case f_YOSCC: gel(T,2) = homtab(initnumsine(m, l), kmb); return T;
+      }
+    case f_YOSCS: case f_YOSCC: tmp = initnumsine(m, l);
+      gel(T,1) = homtab(tmp, kma);
+      if (codea == f_YOSCC && codeb == f_YOSCC && !gequal(kma, kmb))
+      {
+        U = cgetg(3, t_VEC);
+        gel(U,1) = inittanhsinh(m, l);
+        gel(U,2) = homtab(tmp, kmb);
+        gel(T,2) = U;
+      }
+      else gel(T,2) = homtab(tmp, kmb);
+      return T;
+  }
+  return gen_0; /* not reached */
+}
+
+GEN
+intnuminit0(GEN a, GEN b, GEN tab, long prec)
+{
+  long m;
+  if (!tab) m = 0;
+  else if (typ(tab) != t_INT)
+  {
+    if (!checktab(tab)) pari_err_TYPE("intnuminit0",tab);
+    return tab;
+  }
+  else
+    m = itos(tab);
+  return intnuminit(a, b, m, prec);
+}
+GEN
+sumnuminit0(GEN a, GEN tab, long sgn, long prec)
+{
+  long m;
+  if (!tab) m = 0;
+  else if (typ(tab) != t_INT)
+  {
+    if (!checktab(tab)) pari_err_TYPE("sumnuminit0",tab);
+    return tab;
+  }
+  else
+    m = itos(tab);
+  return sumnuminit(a, m, sgn, prec);
+}
+
+/* User-defined change of variable phi(t) = f(t), where t goes from -oo to +oo,
+ * and a and b are as in intnuminit. If [a,b] compact, assume phi(t) odd. */
+static int
+condfin(long code, GEN xw, long eps, long m, long k)
+{
+  GEN x, w;
+  eps -= 8; /* for safety. Lose 8 bits, but took 1 whole word extra. */
+  x = gel(xw,1);
+  w = gel(xw,2);
+  switch(labs(code))
+  {
+    case f_REG: case f_SING:
+      return gexpo(w) < -eps;
+    case f_YSLOW: case f_YVSLO:
+      return gexpo(w) - 2*gexpo(x) < -eps;
+    case f_YFAST:
+      return cmprs(x, (long)(LOG2 * (gexpo(w) + eps) + 1)) > 0;
+    case f_YOSCS: case f_YOSCC:
+      return gexpo(x) + m + expu(10 * k) < - eps;
+    default: return 0;
+  }
+}
+
+/* eps = 2^(-k). Return f'(a) ~ (f(a+eps) - f(a-eps)) / 2eps*/
+static GEN
+myderiv_num(void *E, GEN (*eval)(void*, GEN), GEN a, GEN eps, long k, long prec)
+{
+  GEN d = gmul2n(gsub(eval(E, gadd(a,eps)), eval(E, gsub(a,eps))), k-1);
+  return gprec_w(d, prec);
+}
+/* zp = z to a higher accuracy (enough to evaluate numerical derivative) */
+static GEN
+ffprime(void *E, GEN (*eval)(void*, GEN), GEN z, GEN zp, GEN eps, long h, long precl)
+{
+  GEN f = eval(E, z), fp = myderiv_num(E, eval, zp, eps, h, precl);
+  return mkvec2(f, fp);
+}
+/* v = [f(z), f'(z)]. Return h := z/(1-f(z)), h + h^2 zf'(z) */
+static GEN
+ffmodify(GEN v, GEN z)
+{
+  GEN f = gel(v,1), fp = gel(v,2), h = ginv(gsubsg(1, f));
+  return mkvec2(gmul(z, h), gadd(h, gmul(gsqr(h), gmul(z,fp))));
+}
+GEN
+intnuminitgen(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, long m,
+              long flext, long prec)
+{
+  enum {
+    f_COMP, /* [a,b] */
+    f_SEMI, /* [a,+-oo[, no oscillation */
+    f_OSC1, /* [a,+-oo[, oscillation */
+    f_INF , /* ]-oo,+oo[, no oscillation */
+    f_OSC2  /* ]-oo,+oo[, oscillation */
+  };
+  pari_sp ltop = avma;
+  GEN hnpr, eps, v;
+  long k, h, newprec, lim, precl = prec+EXTRAPREC;
+  long flag, codea = transcode(a, "a"), codeb = transcode(b, "b");
+  int NOT_OSC, NOT_ODD;
+  intdata D; intinit_start(&D, m, flext, precl);
+
+  flag = f_SEMI;
+  if (is_osc_f(codea) || is_osc_f(codeb)) flag = f_OSC1;
+  if (is_fin_f(codea) && is_fin_f(codeb)) flag = f_COMP;
+  else if (!is_fin_f(codea) && !is_fin_f(codeb))
+  {
+    if (codea * codeb > 0) return gen_0;
+    if (codea != -codeb)
+      pari_err_TYPE("intnuminitgen [infinities of different types]",
+                    mkvec2(a,b));
+    flag = (flag == f_SEMI) ? f_INF : f_OSC2;
+  }
+  NOT_OSC = (flag == f_COMP || flag == f_SEMI || flag == f_INF);
+  NOT_ODD = (flag == f_SEMI || flag == f_OSC1);
+
+  newprec = (3*precl - 1)>>1;
+  h = prec2nbits(precl)/2;
+  eps = real2n(-h, newprec);
+
+  if (NOT_OSC || !gequal1(eval(E, gen_0)))
+  {
+    GEN a0 = real_0(precl), a0n = real_0(newprec), xw, xwmod;
+    xw = ffprime(E, eval, a0, a0n, eps, h, precl);
+    xwmod = NOT_OSC? xw: ffmodify(xw, a0);
+    D.tabx0 = gel(xwmod,1);
+    D.tabw0 = gel(xwmod,2);
+  }
+  else
+  {
+    GEN xw = gdiv(pol_x(0), gsubsg(1, eval(E, gadd(pol_x(0), zeroser(0, 4)))));
+    D.tabx0 = gprec_w(polcoeff0(xw, 0, 0), precl);
+    D.tabw0 = gprec_w(polcoeff0(xw, 1, 0), precl);
+  }
+  /* precl <= newprec */
+  hnpr = real2n(-D.m, newprec);
+  lim = lg(D.tabxp) - 1;
+  for (k = 1; k <= lim; k++)
+  {
+    GEN akn = mulur(k, hnpr), ak = rtor(akn, precl), xw, xwmod;
+    int finb;
+    xw = ffprime(E, eval, ak, akn, eps, h, precl);
+    xwmod = NOT_OSC? xw: ffmodify(xw, ak);
+    gel(D.tabxp,k) = gel(xwmod,1);
+    gel(D.tabwp,k) = gel(xwmod,2);
+    finb = condfin(codeb, is_osc_f(codeb)? xw: xwmod, D.eps, D.m, k);
+    if (NOT_ODD)
+    {
+      ak = negr(ak); akn = negr(akn);
+      xw = ffprime(E, eval, ak, akn, eps, h, precl);
+      xwmod = NOT_OSC? xw: ffmodify(xw, ak);
+      gel(D.tabxm,k) = gel(xwmod,1);
+      gel(D.tabwm,k) = gel(xwmod,2);
+      if (finb && condfin(codea, is_osc_f(codeb)? xw: xwmod, D.eps, D.m, k))
+        break;
+    }
+    else if (finb) break;
+  }
+  v = intinit_end(&D, k-1, NOT_ODD? k-1: 0);
+  if (!NOT_OSC)
+  {
+    GEN C = Pi2n(D.m, precl);
+    TABx0(v) = gmul(TABx0(v), C);
+    TABw0(v) = gmul(TABw0(v), C);
+    TABxp(v) = RgV_Rg_mul(TABxp(v), C);
+    TABwp(v) = RgV_Rg_mul(TABwp(v), C);
+    TABxm(v) = RgV_Rg_mul(TABxm(v), C);
+    TABwm(v) = RgV_Rg_mul(TABwm(v), C);
+  }
+  return gerepilecopy(ltop, v);
+}
+
+/* Assigns the values of the function weighted by w[k] at quadrature points x[k]
+ * [replacing the weights]. Return the index of the last non-zero coeff */
+static long
+weight(void *E, GEN (*eval)(void *, GEN), GEN x, GEN w)
+{
+  long k, l = lg(x);
+  for (k = 1; k < l; k++) gel(w,k) = gmul(gel(w,k), eval(E, gel(x,k)));
+  k--; while (k >= 1) if (!gequal0(gel(w,k--))) break;
+  return k;
+}
+/* compute the necessary tabs, weights multiplied by f(t).
+ * If flag set, assumes that f(-t) = conj(f(t)). */
+static GEN
+intfuncinitintern(void *E, GEN (*eval)(void*, GEN), GEN tab, long flag)
+{
+  GEN tabxp = TABxp(tab), tabwp = TABwp(tab);
+  GEN tabxm = TABxm(tab), tabwm = TABwm(tab);
+  long L = weight(E, eval, tabxp, tabwp), L0 = lg(tabxp);
+
+  TABw0(tab) = gmul(TABw0(tab), eval(E, TABx0(tab)));
+  if (lg(tabxm) > 1) (void)weight(E, eval, tabxm, tabwm);
+  else
+  {
+    tabxm = gneg(tabxp);
+    if (flag) tabwm = gconj(tabwp);
+    else
+    {
+      long L2;
+      tabwm = leafcopy(tabwp);
+      L2 = weight(E, eval, tabxm, tabwm);
+      if (L > L2) L = L2;
+    }
+    TABxm(tab) = tabxm;
+    TABwm(tab) = tabwm;
+  }
+  if (L < L0)
+  { /* catch up functions whose growth at oo was not adequately described */
+    setlg(tabxp, L+1);
+    setlg(tabwp, L+1);
+    if (lg(tabxm) > 1) { setlg(tabxm, L+1); setlg(tabwm, L+1); }
+  }
+  return tab;
+}
+
+GEN
+intfuncinit(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, long m, long flag, long prec)
+{
+  pari_sp ltop = avma;
+  GEN T, tab = intnuminit(a, b, m, prec);
+
+  if (lg(tab) != 3) T = intfuncinitintern(E, eval, tab, flag);
+  else
+  {
+    T = cgetg(3, t_VEC);
+    gel(T,1) = intfuncinitintern(E, eval, gel(tab,1), flag);
+    gel(T,2) = intfuncinitintern(E, eval, gel(tab,2), flag);
+  }
+  return gerepilecopy(ltop, T);
+}
+
+static GEN
+intnum_i(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN tab, long prec)
+{
+  GEN tmp, S = gen_0, res1, res2, tm, pi2, pi2p, pis2, pis2p, kma, kmb;
+  GEN SP, SN;
+  long tmpi, sgns = 1, codea = transcode(a, "a"), codeb = transcode(b, "b");
+
+  if (codea == f_REG && typ(a) == t_VEC) a = gel(a,1);
+  if (codeb == f_REG && typ(b) == t_VEC) b = gel(b,1);
+  if (codea == f_REG && codeb == f_REG) return intn(E, eval, a, b, tab);
+  if (labs(codea) > labs(codeb)) { swap(a, b); lswap(codea, codeb); sgns = -1; }
+  /* now labs(codea) <= labs(codeb) */
+  if (codeb == f_SING)
+  {
+    if (codea == f_REG)
+      S = intnsing(E, eval, b, a, tab, prec), sgns = -sgns;
+    else
+    {
+      tmp = gmul2n(gadd(gel(a,1), gel(b,1)), -1);
+      res1 = intnsing(E, eval, a, tmp, tab, prec);
+      res2 = intnsing(E, eval, b, tmp, tab, prec);
+      S = gsub(res1, res2);
+    }
+    return (sgns < 0) ? gneg(S) : S;
+  }
+  /* now b is infinite */
+  tmpi = codeb > 0 ? 1 : -1;
+  if (codea == f_REG && labs(codeb) != f_YOSCC
+      && (labs(codeb) != f_YOSCS || gequal0(a)))
+  {
+    S = intninfpm(E, eval, a, tmpi, tab);
+    return sgns*tmpi < 0 ? gneg(S) : S;
+  }
+  pi2 = Pi2n(1, prec); pis2 = Pi2n(-1, prec);
+  if (is_fin_f(codea))
+  { /* either codea == f_SING  or codea == f_REG and codeb = f_YOSCC
+     * or (codeb == f_YOSCS and !gequal0(a)) */
+    pi2p = gmul(pi2, f_getycplx(b, prec));
+    pis2p = gmul2n(pi2p, -2);
+    tm = real_i(codea == f_SING ? gel(a,1) : a);
+    if (labs(codeb) == f_YOSCC) tm = gadd(tm, pis2p);
+    tm = gdiv(tm, pi2p);
+    if (tmpi > 0)
+      tm = addsi(1, gceil(tm));
+    else
+      tm = subis(gfloor(tm), 1);
+    tm = gmul(pi2p, tm);
+    if (labs(codeb) == f_YOSCC) tm = gsub(tm, pis2p);
+    res1 = codea==f_SING? intnsing(E, eval, a,  tm,  gel(tab,1), prec)
+                        : intn    (E, eval, a,  tm,  gel(tab,1));
+    res2 = intninfpm(E, eval, tm, tmpi,gel(tab,2));
+    if (tmpi < 0) res2 = gneg(res2);
+    res1 = gadd(res1, res2);
+    return sgns < 0 ? gneg(res1) : res1;
+  }
+  /* now a and b are infinite */
+  if (codea * codeb > 0)
+  {
+    pari_warn(warner, "integral from infty to infty or from -infty to -infty");
+    return gen_0;
+  }
+  if (codea > 0) { lswap(codea, codeb); swap(a, b); sgns = -sgns; }
+  /* now codea < 0 < codeb */
+  codea = -codea;
+  kma = f_getycplx(a, prec);
+  kmb = f_getycplx(b, prec);
+  if ((codea == f_YSLOW && codeb == f_YSLOW)
+   || (codea == f_YFAST && codeb == f_YFAST && gequal(kma, kmb)))
+    S = intninfinf(E, eval, tab);
+  else
+  {
+    GEN coupea = (codea == f_YOSCC)? gmul(pis2, kma): gen_0;
+    GEN coupeb = (codeb == f_YOSCC)? gmul(pis2, kmb): gen_0;
+    GEN coupe = codea == f_YOSCC ? coupea : coupeb;
+    SN = intninfpm(E, eval, coupe, -1, gel(tab,1));
+    if (codea != f_YOSCC)
+      SP = intninfpm(E, eval, coupeb, 1, gel(tab,2));
+    else
+    {
+      if (codeb != f_YOSCC) pari_err_BUG("code error in intnum");
+      if (gequal(kma, kmb))
+        SP = intninfpm(E, eval, coupeb, 1, gel(tab,2));
+      else
+      {
+        tab = gel(tab,2);
+        SP = intninfpm(E, eval, coupeb, 1, gel(tab,2));
+        SP = gadd(SP, intn(E, eval, coupea, coupeb, gel(tab,1)));
+      }
+    }
+    S = gadd(SN, SP);
+  }
+  if (sgns < 0) S = gneg(S);
+  return S;
+}
+
+GEN
+intnum(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN tab, long prec)
+{
+  pari_sp ltop = avma;
+  long l = prec+EXTRAPREC;
+  GEN S;
+
+  tab = intnuminit0(a, b, tab, prec);
+  S = intnum_i(E, eval, gprec_w(a, l), gprec_w(b, l), tab, l);
+  return gerepilecopy(ltop, gprec_wtrunc(S, prec));
+}
+
+typedef struct auxint_s {
+  GEN a, R, pi;
+  GEN (*f)(void*, GEN);
+  GEN (*w)(GEN, long);
+  long prec;
+  void *E;
+} auxint_t;
+
+static GEN
+auxcirc(void *E, GEN t)
+{
+  auxint_t *D = (auxint_t*) E;
+  GEN s, c, z;
+  mpsincos(mulrr(t, D->pi), &s, &c); z = mkcomplex(c,s);
+  return gmul(z, D->f(D->E, gadd(D->a, gmul(D->R, z))));
+}
+
+GEN
+intcirc(void *E, GEN (*eval)(void*, GEN), GEN a, GEN R, GEN tab, long prec)
+{
+  auxint_t D;
+  GEN z;
+
+  D.a = a;
+  D.R = R;
+  D.pi = mppi(prec);
+  D.f = eval;
+  D.E = E;
+  z = intnum(&D, &auxcirc, real_m1(prec), real_1(prec), tab, prec);
+  return gmul2n(gmul(R, z), -1);
+}
+
+static GEN
+gettmpP(GEN x) { return mkvec2(mkvec(gen_1), x); }
+
+static GEN
+gettmpN(GEN tmpP) { return mkvec2(gneg(gel(tmpP,1)), gel(tmpP,2)); }
+
+/* w(Rt) f(a+it) */
+static GEN
+auxinv(void *E, GEN t)
+{
+  auxint_t *D = (auxint_t*) E;
+  GEN tmp = D->w(gmul(D->R, t), D->prec);
+  return gmul(tmp, D->f(D->E, gadd(D->a, mulcxI(t))));
+}
+static GEN
+intinvintern(void *E, GEN (*eval)(void*, GEN), GEN sig, GEN x, GEN tab, long prec)
+{
+  auxint_t D;
+  GEN z, zR, zI, tmpP, tmpN;
+
+  if (lg(sig) != 3 || !isinR(gel(sig,1)) || !isinR(gel(sig,2)))
+    pari_err_TYPE("integral transform",sig);
+  if (gsigne(gel(sig,2)) < 0)
+    pari_err_OVERFLOW("integral transform [exponential increase]");
+  D.a = gel(sig,1);
+  D.prec = prec;
+  D.f = eval;
+  D.E = E;
+  if (gequal0(gel(sig,2)))
+  {
+    D.R = x;
+    tmpP = gettmpP(mulcxI(gabs(x, prec)));
+    tmpN = gettmpN(tmpP);
+    tab = intnuminit0(tmpN, tmpP, tab, prec);
+    D.w = gcos;
+    zR = intnum_i(&D, &auxinv, tmpN, tmpP, tab, prec);
+    gel(tmpP,2) = gneg(gel(tmpP,2));
+    D.w = gsin;
+    zI = intnum_i(&D, &auxinv, gettmpN(tmpP), tmpP, tab, prec);
+    z = gadd(zR, mulcxI(zI));
+  }
+  else
+  {
+    D.R = mulcxI(x);
+    tmpP = gettmpP(gel(sig,2));
+    D.w = gexp;
+    z = intnum(&D, &auxinv, gettmpN(tmpP), tmpP, tab, prec);
+  }
+  return gdiv(gmul(gexp(gmul(gel(sig,1), x), prec), z), Pi2n(1, prec));
+}
+
+/* If sig = [sigR, e]: if e = 0, slowly decreasing, if e > 0, exponentially
+ * decreasing like exp(-e*t). If sig is real, identical to [sig, 1]. */
+GEN
+intmellininv(void *E, GEN (*eval)(void*, GEN), GEN sig, GEN x, GEN tab, long prec)
+{
+  if (typ(sig) != t_VEC) sig = mkvec2(sig, gen_1);
+  return intinvintern(E, eval, sig, gneg(glog(x, prec)), tab, prec);
+}
+
+/* If sig = [sigR, e]: if e = 0, slowly decreasing, if e > 0, exponentially
+ * decreasing like exp(-e*t). If sig is real, identical to [sig, 0]. */
+GEN
+intlaplaceinv(void *E, GEN (*eval)(void*, GEN), GEN sig, GEN x, GEN tab, long prec)
+{
+  if (typ(sig) != t_VEC) sig = mkvec2(sig, gen_0);
+  return intinvintern(E, eval, sig, x, tab, prec);
+}
+
+/* assume tab computed with additional weights f(sig + I*T) */
+typedef struct auxmel_s {
+  GEN L;
+  long prec;
+} auxmel_t;
+
+static GEN
+auxmelshort(void *E, GEN t)
+{
+  auxmel_t *D = (auxmel_t*) E;
+  return gexp(gmul(D->L, t), D->prec);
+}
+
+GEN
+intmellininvshort(GEN sig, GEN x, GEN tab, long prec)
+{
+  auxmel_t D;
+  GEN z, tmpP, LX = gneg(glog(x, prec));
+
+  if (typ(sig) != t_VEC) sig = mkvec2(sig, gen_1);
+  if (lg(sig) != 3 || !isinR(gel(sig,1)) || !isinR(gel(sig,2)))
+    pari_err_TYPE("intmellininvshort",sig);
+  if (gsigne(gel(sig,2)) <= 0)
+    pari_err_OVERFLOW("intinvmellinshort [need exponential decrease]");
+  D.L = mulcxI(LX);
+  D.prec = prec;
+  tmpP = gettmpP(gel(sig,2));
+  z = intnum_i(&D, &auxmelshort, gettmpN(tmpP), tmpP, tab, prec);
+  return gdiv(gmul(gexp(gmul(gel(sig,1), LX), prec), z), Pi2n(1, prec));
+}
+
+/* a as in intnum. flag = 0 for sin, flag = 1 for cos. */
+static GEN
+mytra(GEN a, GEN x, long flag, const char *name)
+{
+  GEN b, xa;
+  long s, codea = transcode(a, name);
+
+  switch (labs(codea))
+  {
+    case f_REG: case f_SING: case f_YFAST: return a;
+    case f_YSLOW: case f_YVSLO:
+      xa = real_i(x); s = gsigne(xa);
+      if (!s) pari_err_DOMAIN("Fourier transform","Re(x)","=",gen_0,x);
+      if (s < 0) xa = gneg(xa);
+      b = cgetg(3, t_VEC);
+      gel(b,1) = mkvec( codea > 0 ? gen_1 : gen_m1 );
+      gel(b,2) = (flag? mulcxI(xa): mulcxmI(xa));
+      return b;
+    case f_YOSCS: case f_YOSCC:
+      pari_err_IMPL("Fourier transform of oscillating functions");
+  }
+  return NULL;
+}
+
+/* w(ta) f(t) */
+static GEN
+auxfour(void *E, GEN t)
+{
+  auxint_t *D = (auxint_t*) E;
+  return gmul(D->w(gmul(t, D->a), D->prec), D->f(D->E, t));
+}
+
+GEN
+intfouriersin(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN x, GEN tab, long prec)
+{
+  auxint_t D;
+  GEN tmp;
+
+  if (gequal0(x)) return gcopy(x);
+  tmp = gmul(x, Pi2n(1, prec));
+  D.a = tmp;
+  D.R = NULL;
+  D.prec = prec;
+  D.f = eval;
+  D.E = E;
+  a = mytra(a,tmp,0,"a");
+  b = mytra(b,tmp,0,"b");
+  D.w = gsin;
+  return intnum(&D, &auxfour, a, b, tab, prec);
+}
+
+GEN
+intfouriercos(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN x, GEN tab, long prec)
+{
+  auxint_t D;
+  GEN tmp;
+
+  if (gequal0(x)) return intnum(E, eval, a, b, tab, prec);
+  tmp = gmul(x, Pi2n(1, prec));
+  D.a = tmp;
+  D.R = NULL;
+  D.prec = prec;
+  D.f = eval;
+  D.E = E;
+  a = mytra(a,tmp,1,"a");
+  b = mytra(b,tmp,1,"b");
+  D.w = gcos;
+  return intnum(&D, &auxfour, a, b, tab, prec);
+}
+
+GEN
+intfourierexp(void *E, GEN (*eval)(void*, GEN), GEN a, GEN b, GEN x, GEN tab,
+              long prec)
+{
+  pari_sp ltop = avma;
+  GEN R = intfouriercos(E, eval, a, b, x, tab, prec);
+  GEN I = intfouriersin(E, eval, a, b, x, tab, prec);
+  return gerepileupto(ltop, gadd(R, mulcxmI(I)));
+}
+
+GEN
+intnumromb(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long flag, long prec)
+{
+  pari_sp av = avma;
+  GEN z;
+  switch(flag)
+  {
+    case 0: z = qrom3  (E, eval, a, b, prec); break;
+    case 1: z = rombint(E, eval, a, b, prec); break;
+    case 2: z = qromi  (E, eval, a, b, prec); break;
+    case 3: z = qrom2  (E, eval, a, b, prec); break;
+    default: pari_err_FLAG("intnumromb"); return NULL; /* not reached */
+  }
+  if (!z) pari_err_IMPL("intnumromb recovery [too many iterations]");
+  return gerepileupto(av, z);
+}
+
+GEN
+intnumromb0(GEN a, GEN b, GEN code, long flag, long prec)
+{ EXPR_WRAP(code, intnumromb(EXPR_ARG, a, b, flag, prec)); }
+GEN
+intnum0(GEN a, GEN b, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intnum(EXPR_ARG, a, b, tab, prec)); }
+GEN
+intcirc0(GEN a, GEN R, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intcirc(EXPR_ARG, a, R, tab, prec)); }
+GEN
+intmellininv0(GEN sig, GEN x, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intmellininv(EXPR_ARG, sig, x, tab, prec)); }
+GEN
+intlaplaceinv0(GEN sig, GEN x, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intlaplaceinv(EXPR_ARG, sig, x, tab, prec)); }
+GEN
+intfourcos0(GEN a, GEN b, GEN x, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intfouriercos(EXPR_ARG, a, b, x, tab, prec)); }
+GEN
+intfoursin0(GEN a, GEN b, GEN x, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intfouriersin(EXPR_ARG, a, b, x, tab, prec)); }
+GEN
+intfourexp0(GEN a, GEN b, GEN x, GEN code, GEN tab, long prec)
+{ EXPR_WRAP(code, intfourierexp(EXPR_ARG, a, b, x, tab, prec)); }
+
+GEN
+intnuminitgen0(GEN a, GEN b, GEN code, long m, long flag, long prec)
+{ EXPR_WRAP(code, intnuminitgen(EXPR_ARG, a, b, m, flag, prec)); }
+
+/* m and flag reversed on purpose */
+GEN
+intfuncinit0(GEN a, GEN b, GEN code, long flag, long m, long prec)
+{ EXPR_WRAP(code, intfuncinit(EXPR_ARG, a, b, m, flag? 1: 0, prec)); }
+
+#if 0
+/* Two variable integration */
+
+typedef struct auxf_s {
+  GEN x;
+  GEN (*f)(void *, GEN, GEN);
+  void *E;
+} auxf_t;
+
+typedef struct indi_s {
+  GEN (*c)(void*, GEN);
+  GEN (*d)(void*, GEN);
+  GEN (*f)(void *, GEN, GEN);
+  void *Ec;
+  void *Ed;
+  void *Ef;
+  GEN tabintern;
+  long prec;
+} indi_t;
+
+static GEN
+auxf(GEN y, void *E)
+{
+  auxf_t *D = (auxf_t*) E;
+  return D->f(D->E, D->x, y);
+}
+
+static GEN
+intnumdoubintern(GEN x, void *E)
+{
+  indi_t *D = (indi_t*) E;
+  GEN c = D->c(x, D->Ec), d = D->d(x, D->Ed);
+  auxf_t A;
+
+  A.x = x;
+  A.f = D->f;
+  A.E = D->Ef;
+  return intnum(&A, &auxf, c, d, D->tabintern, D->prec);
+}
+
+GEN
+intnumdoub(void *Ef, GEN (*evalf)(void *, GEN, GEN), void *Ec, GEN (*evalc)(void*, GEN), void *Ed, GEN (*evald)(void*, GEN), GEN a, GEN b, GEN tabext, GEN tabint, long prec)
+{
+  indi_t E;
+
+  E.c = evalc;
+  E.d = evald;
+  E.f = evalf;
+  E.Ec = Ec;
+  E.Ed = Ed;
+  E.Ef = Ef;
+  E.prec = prec;
+  if (typ(tabint) == t_INT)
+  {
+    GEN C = evalc(a, Ec), D = evald(a, Ed);
+    if (typ(C) != t_VEC && typ(D) != t_VEC) { C = gen_0; D = gen_1; }
+    E.tabintern = intnuminit0(C, D, tabint, prec);
+  }
+  else E.tabintern = tabint;
+  return intnum(&E, &intnumdoubintern, a, b, tabext, prec);
+}
+
+GEN
+intnumdoub0(GEN a, GEN b, int nc, int nd, int nf, GEN tabext, GEN tabint, long prec)
+{
+  GEN z;
+  push_lex(NULL);
+  push_lex(NULL);
+  z = intnumdoub(chf, &gp_eval2, chc, &gp_eval, chd, &gp_eval, a, b, tabext, tabint, prec);
+  pop_lex(1); pop_lex(1); return z;
+}
+#endif
+
+/* Numerical summation routine assuming f holomorphic for Re(s) >= sig.
+ * Computes sum_{n>=a} f(n)  if sgn >= 0,
+ *          sum_{n>=a} (-1)^n f(n) otherwise,  where a is real.
+ * Variant of Abel-Plana. */
+
+static GEN
+auxsum(void *E, GEN t)
+{
+  auxint_t *D = (auxint_t*) E;
+  GEN z = mkcomplex(D->a, t);
+  return D->f(D->E, z);
+}
+/* assume that conj(f(z)) = f(conj(z)) */
+static GEN
+auxsumintern1(void *E, GEN t, long sgn)
+{
+  auxint_t *D = (auxint_t*) E;
+  GEN z = mkcomplex(D->a, t), u = D->f(D->E, z);
+  return sgn > 0 ? imag_i(u): real_i(u);
+}
+/* no assumption */
+static GEN
+auxsumintern(void *E, GEN t, long sgn)
+{
+  auxint_t *D = (auxint_t*) E;
+  GEN u,v, z = mkcomplex(D->a, t);
+  u = D->f(D->E, z); gel(z,2) = gneg(t);
+  v = D->f(D->E, z); return sgn > 0 ? gsub(u, v) : gadd(u, v);
+}
+static GEN
+auxsum0(void *E, GEN t) { return auxsumintern(E, t, 1); }
+static GEN
+auxsum1(void *E, GEN t) { return auxsumintern1(E, t, 1); }
+static GEN
+auxsumalt0(void *E, GEN t) { return auxsumintern(E, t, -1); }
+static GEN
+auxsumalt1(void *E, GEN t) { return auxsumintern1(E, t, -1); }
+
+static GEN
+sumnumall(void *E, GEN (*eval)(void*, GEN), GEN a, GEN sig, GEN tab, long flag, long sgn, long prec)
+{
+  GEN SI, S, nsig, b, signew;
+  long si = 1, flii;
+  pari_sp ltop = avma;
+  auxint_t D;
+
+  b = suminit_start(sig);
+  flii = gequal0(gel(b,2));
+  if (!is_scalar_t(typ(a))) pari_err_TYPE("sumnum",a);
+  tab = sumnuminit0(sig, tab, sgn, prec);
+
+  signew = (typ(sig) == t_VEC) ? gel(sig,1) : sig;
+  a = gceil(a); nsig = gmax(subis(a, 1), gceil(gsub(signew, ghalf)));
+  if (sgn < 0) {
+    if (mpodd(nsig)) nsig = addsi(1, nsig);
+    si = mpodd(a) ? -1 : 1;
+  }
+  SI = real_0(prec);
+  while (cmpii(a, nsig) <= 0)
+  {
+    SI = (si < 0) ? gsub(SI, eval(E, a)) : gadd(SI, eval(E, a));
+    a = addsi(1, a); if (sgn < 0) si = -si;
+  }
+  D.a = gadd(nsig, ghalf);
+  D.R = gen_0;
+  D.f = eval;
+  D.E = E;
+  D.prec = prec;
+  if (!flii)
+    S = intnum_i(&D, sgn > 0? (flag ? &auxsum1 : &auxsum0)
+                            : (flag ? &auxsumalt1 : &auxsumalt0),
+                      gen_0, b, tab, prec);
+  else
+  {
+    if (flag)
+    {
+      GEN emp = leafcopy(tab); TABwm(emp) = TABwp(emp);
+      S = gmul2n(intninfinf(&D, sgn > 0? &auxsum1: &auxsumalt1, emp),-1);
+    }
+    else
+      S = intninfinfintern(&D, &auxsum, tab, sgn);
+  }
+  if (flag) S = gneg(S);
+  else
+  {
+    S = gmul2n(S, -1);
+    S = (sgn < 0) ? gneg(S): mulcxI(S);
+  }
+  return gerepileupto(ltop, gadd(SI, S));
+}
+GEN
+sumnum(void *E, GEN (*f)(void *, GEN), GEN a,GEN sig,GEN tab,long flag,long prec)
+{ return sumnumall(E,f,a,sig,tab,flag,1,prec); }
+GEN
+sumnumalt(void *E, GEN (*f)(void *, GEN),GEN a,GEN s,GEN tab,long flag,long prec)
+{ return sumnumall(E,f,a,s,tab,flag,-1,prec); }
+
+GEN
+sumnum0(GEN a, GEN sig, GEN code, GEN tab, long flag, long prec)
+{ EXPR_WRAP(code, sumnum(EXPR_ARG, a, sig, tab, flag, prec)); }
+GEN
+sumnumalt0(GEN a, GEN sig, GEN code, GEN tab, long flag, long prec)
+{ EXPR_WRAP(code, sumnumalt(EXPR_ARG, a, sig, tab, flag, prec)); }
diff --git a/src/language/members.c b/src/language/members.c
new file mode 100644
index 0000000..96b612c
--- /dev/null
+++ b/src/language/members.c
@@ -0,0 +1,596 @@
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/**                                                                **/
+/**                          MEMBER FUNCTIONS                      **/
+/**                                                                **/
+/********************************************************************/
+INLINE int
+is_ell5(GEN x) {
+  long lx;
+  if (typ(x) != t_VEC) return 0;
+  lx = lg(x);
+  return lx == 17 || (lx == 6 && !is_vec_t(typ(gel(x,2))));
+}
+INLINE int is_ell(GEN x) {
+  long lx = lg(x);
+  return (typ(x) == t_VEC && lx == 17);
+}
+
+static void
+member_err(const char *s, GEN y) { pari_err_TYPE(s,y); }
+
+GEN
+member_e(GEN x)
+{
+  GEN y = get_prid(x);
+  if (!y) member_err("e",x);
+  return gel(y,3);
+}
+
+GEN
+member_f(GEN x)
+{
+  GEN y = get_prid(x);
+  if (!y)
+  {
+    if (typ(x) == t_FFELT) return utoipos(FF_f(x));
+    member_err("f",x);
+  }
+  return gel(y,4);
+}
+
+GEN
+member_p(GEN x)
+{
+  long t; (void)get_nf(x,&t);
+  switch(t)
+  {
+    case typ_GAL: return gal_get_p(x);
+    case typ_ELL: switch(ell_get_type(x))
+    {
+      case t_ELL_Fp:
+      case t_ELL_Fq: return ellff_get_p(x);
+      case t_ELL_Qp: return ellQp_get_p(x);
+      default: member_err("p",x);
+    }
+    case typ_MODPR: x = get_prid(x);
+    case typ_PRID: return pr_get_p(x);
+  }
+  switch(typ(x)) {
+    case t_PADIC: return gel(x,2);
+    case t_FFELT: return FF_p_i(x);
+  }
+  member_err("p",x);
+  return NULL;
+}
+
+GEN
+member_bid(GEN x)
+{
+  long t; (void)get_nf(x,&t);
+  switch(t) {
+    case typ_BNR: return bnr_get_bid(x);
+    case typ_BID: return x;
+  }
+  member_err("bid",x);
+  return NULL;
+}
+
+GEN
+member_bnf(GEN x)
+{
+  long t; GEN y = get_bnf(x,&t);
+  if (!y) member_err("bnf",x);
+  return y;
+}
+
+GEN
+member_nf(GEN x)
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y) {
+    if (t == typ_RNF) return gel(x,10);
+    member_err("nf",x);
+  }
+  return y;
+}
+
+/* integral basis */
+GEN
+member_zk(GEN x)
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y)
+  {
+    switch(t)
+    {
+      case typ_Q:
+        y = cgetg(3,t_VEC);
+        gel(y,1) = gen_1;
+        gel(y,2) = pol_x(varn(gel(x,1))); return y;
+      case typ_RNF:
+        return gel(x,7);
+    }
+    member_err("zk",x);
+  }
+  return nf_get_zk(y);
+}
+
+GEN
+member_disc(GEN x) /* discriminant */
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y)
+  {
+    switch(t)
+    {
+      case typ_Q  : return quad_disc(x);
+      case typ_ELL: return ell_get_disc(x);
+      case typ_RNF: return rnf_get_disc(x);
+    }
+    member_err("disc",x);
+  }
+  return nf_get_disc(y);
+}
+
+GEN
+member_pol(GEN x) /* polynomial */
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y)
+  {
+    switch(t)
+    {
+      case typ_POL: return x;
+      case typ_Q  : return gel(x,1);
+      case typ_GAL: return gal_get_pol(x);
+      case typ_RNF: return rnf_get_pol(x);
+    }
+    if (typ(x)==t_POLMOD) return gel(x,2);
+    if (typ(x)==t_FFELT) return FF_to_FpXQ(x);
+    member_err("pol",x);
+  }
+  return nf_get_pol(y);
+}
+
+GEN
+member_polabs(GEN x)
+{
+  long t; (void)get_nf(x,&t);
+  if (t != typ_RNF) member_err("pol",x);
+  return rnf_get_polabs(x);
+}
+
+GEN
+member_mod(GEN x) /* modulus */
+{
+  long t; (void)get_nf(x,&t);
+  switch(t) {
+    case typ_GAL: return gal_get_mod(x);
+    case typ_BNR: return bnr_get_mod(x);
+    case typ_BID: return gel(x,1);
+  }
+  switch(typ(x))
+  {
+    case t_INTMOD: case t_POLMOD: case t_QUAD: break;
+    case t_PADIC: return gel(x,3);
+    case t_FFELT: return FF_mod(x);
+    default: member_err("mod",x);
+  }
+  return gel(x,1);
+}
+
+GEN
+member_sign(GEN x) /* signature */
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y) member_err("sign",x);
+  return gel(y,2);
+}
+GEN
+member_r1(GEN x) { return gel(member_sign(x), 1); }
+GEN
+member_r2(GEN x) { return gel(member_sign(x), 2); }
+
+GEN
+member_index(GEN x)
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y)
+  {
+    if (t == typ_RNF) return rnf_get_index(x);
+    member_err("index",x);
+  }
+  return nf_get_index(y);
+}
+
+/* x assumed to be output by get_nf: ie a t_VEC with length 11 */
+static GEN
+nfmats(GEN x)
+{
+  GEN y;
+  if (!x) return NULL;
+  y = gel(x,5);
+  if (typ(y) == t_VEC && lg(y) < 8) return NULL;
+  return y;
+}
+
+GEN
+member_t2(GEN x) /* T2 matrix */
+{
+  long t; GEN y = nfmats(get_nf(x,&t));
+  if (!y) member_err("t2",x);
+  return gram_matrix(gel(y,2));
+}
+
+GEN
+member_diff(GEN x) /* different */
+{
+  long t; GEN y = nfmats(get_nf(x,&t));
+  if (!y) member_err("diff",x);
+  return gel(y,5);
+}
+
+GEN
+member_codiff(GEN x) /* codifferent */
+{
+  long t; GEN T, D, DinvT, nf = get_nf(x,&t), y = nfmats(nf);
+  if (!y) member_err("codiff",x);
+  T = gel(y,4);
+  D = absi(nf_get_disc(nf));
+  DinvT = ZM_inv(T,D);
+  return gdiv(ZM_hnfmod(DinvT, D), D);
+}
+
+GEN
+member_roots(GEN x) /* roots */
+{
+  long t; GEN y = get_nf(x,&t);
+  if (!y)
+  {
+    if (t == typ_GAL) return gal_get_roots(x);
+    if (t == typ_ELL)
+      switch(ell_get_type(x))
+      {
+        case t_ELL_Qp: return mkcol( ellQp_root(x, ellQp_get_prec(x)) );
+        case t_ELL_Q:
+        case t_ELL_Rg: return ellR_roots(x, ellR_get_prec(x));
+      }
+    member_err("roots",x);
+  }
+  return nf_get_roots(y);
+}
+
+/* assume x output by get_bnf: ie a t_VEC with length 10 */
+static GEN
+check_RES(GEN x, const char *s)
+{
+  GEN y = gel(x,8);
+  if (typ(y) != t_VEC || lg(y) < 4) member_err(s,x);
+  return y;
+}
+
+/* y = get_bnf(x, &t) */
+static GEN
+_member_clgp(GEN x, GEN y, long t) /* class group (3-component row vector) */
+{
+  if (!y)
+  {
+    switch(t)
+    {
+      case typ_QUA: return mkvec3(gel(x,1), gel(x,2), gel(x,3));
+      case typ_BID: return gel(x,2);
+    }
+    if (typ(x)==t_VEC)
+      switch(lg(x))
+      {
+        case 3: /* no gen */
+        case 4: return x;
+      }
+    member_err("clgp",x);
+  }
+  if (t==typ_BNR) return gel(x,5);
+  y = check_RES(y, "clgp"); return gel(y,1);
+}
+static GEN
+_check_clgp(GEN x, GEN y, long t)
+{ GEN c = _member_clgp(x,y,t); checkabgrp(c); return c; }
+GEN
+member_clgp(GEN x)
+{ long t; GEN y = get_bnf(x,&t); return _check_clgp(x,y,t); }
+
+GEN
+member_reg(GEN x) /* regulator */
+{
+  long t; GEN y = get_bnf(x,&t);
+  if (!y)
+  {
+    if (t == typ_QUA) return gel(x,4);
+    member_err("reg",x);
+  }
+  if (t == typ_BNR) pari_err_IMPL("ray regulator");
+  y = check_RES(y, "reg");
+  return gel(y,2);
+}
+
+GEN
+member_fu(GEN x) /* fundamental units */
+{
+  long t; GEN y = get_bnf(x,&t);
+  if (!y)
+  {
+    switch(t)
+    {
+      case typ_Q:
+        x = quad_disc(x);
+        return (signe(x)<0)? cgetg(1,t_VEC): quadunit(x);
+    }
+    member_err("fu",x);
+  }
+  if (t == typ_BNR) pari_err_IMPL("ray units");
+  return matbasistoalg(y, bnf_get_fu(y));
+}
+
+/* torsion units. return [w,e] where w is the number of roots of 1, and e a
+ * polymod generator */
+GEN
+member_tu(GEN x)
+{
+  long t; GEN bnf = get_bnf(x,&t), res = cgetg(3,t_VEC);
+  if (!bnf)
+  {
+    GEN y;
+    if (t != typ_Q) member_err("tu",x);
+    y = quad_disc(x);
+    if (signe(y) > 0 || cmpiu(y,4) > 0) return mkvec2(gen_m1, gen_2);
+
+    gel(res,1) = utoipos((itos(y) == -4)? 4: 6);
+    gel(res,2) = gcopy(x);
+  }
+  else
+  {
+    if (t == typ_BNR) pari_err_IMPL("ray torsion units");
+    gel(res,1) = utoipos( bnf_get_tuN(bnf) );
+    gel(res,2) = basistoalg(bnf, bnf_get_tuU(bnf));
+  }
+  return res;
+}
+
+GEN
+member_futu(GEN x) /*  concatenation of fu and tu, w is lost */
+{
+  return shallowconcat(member_fu(x), gel(member_tu(x),2));
+}
+
+GEN
+member_tufu(GEN x) /*  concatenation of tu and fu, w is lost */
+{
+  return shallowconcat(gel(member_tu(x),2), member_fu(x));
+}
+
+/* structure of (Z_K/m)^*, where x is an idealstarinit (with or without gen)
+ * or a bnrinit (with or without gen) */
+GEN
+member_zkst(GEN x)
+{
+  long t; (void)get_nf(x,&t);
+  switch(t)
+  {
+    case typ_BID: return gel(x,2);
+    case typ_BNR: {
+      GEN bid = bnr_get_bid(x);
+      if (typ(bid) == t_VEC && lg(bid) > 2) return gel(bid,2);
+    }
+  }
+  member_err("zkst",x);
+  return NULL; /* not reached */
+}
+
+GEN
+member_no(GEN x) /* number of elements of a group (of type clgp) */
+{
+  pari_sp av = avma;
+  long t; GEN y = get_bnf(x,&t);
+  if (t == typ_ELL) switch(ell_get_type(x))
+  {
+    case t_ELL_Fp:
+    case t_ELL_Fq: return ellcard(x, NULL);
+  }
+  x = _check_clgp(x,y,t);
+  avma = av; return gel(x,1);
+}
+
+GEN
+member_cyc(GEN x) /* cyclic decomposition (SNF) of a group (of type clgp) */
+{
+  pari_sp av = avma;
+  long t; GEN y = get_bnf(x,&t);
+  if (t == typ_ELL) switch(ell_get_type(x))
+  {
+    case t_ELL_Fp:
+    case t_ELL_Fq: return ellgroup(x, NULL);
+  }
+  x = _check_clgp(x,y,t);
+  avma = av; return gel(x,2);
+}
+
+/* SNF generators of a group (of type clgp), or generators of a prime
+ * ideal */
+GEN
+member_gen(GEN x)
+{
+  pari_sp av;
+  long t; GEN y = get_bnf(x,&t);
+  switch(t)
+  {
+    case typ_MODPR: x = get_prid(x);
+    case typ_PRID: return mkvec2(gel(x,1), gel(x,2));
+    case typ_GAL: return gal_get_gen(x);
+    case typ_ELL: return ellgenerators(x);
+  }
+  av = avma;
+  x = _check_clgp(x,y,t);
+  if (lg(x)!=4) member_err("gen",x);
+  avma = av; return gel(x,3);
+}
+GEN
+member_group(GEN x)
+{
+  long t; (void)get_nf(x,&t);
+  if (t == typ_GAL) return gal_get_group(x);
+  if (t == typ_ELL) return ellgroup0(x, NULL, 1);
+  member_err("group",x);
+  return NULL; /* not reached */
+}
+GEN
+member_orders(GEN x)
+{
+  long t; (void)get_nf(x,&t);
+  if (t == typ_GAL) return gal_get_orders(x);
+  member_err("orders",x);
+  return NULL; /* not reached */
+}
+
+GEN
+member_a1(GEN x)
+{
+  if (!is_ell5(x)) member_err("a1",x);
+  return ell_get_a1(x);
+}
+
+GEN
+member_a2(GEN x)
+{
+  if (!is_ell5(x)) member_err("a2",x);
+  return ell_get_a2(x);
+}
+
+GEN
+member_a3(GEN x)
+{
+  if (!is_ell5(x)) member_err("a3",x);
+  return ell_get_a3(x);
+}
+
+GEN
+member_a4(GEN x)
+{
+  if (!is_ell5(x)) member_err("a4",x);
+  return ell_get_a4(x);
+}
+
+GEN
+member_a6(GEN x)
+{
+  if (!is_ell5(x)) member_err("a6",x);
+  return ell_get_a6(x);
+}
+
+GEN
+member_b2(GEN x)
+{
+  if (!is_ell(x)) member_err("b2",x);
+  return ell_get_b2(x);
+}
+
+GEN
+member_b4(GEN x)
+{
+  if (!is_ell(x)) member_err("b4",x);
+  return ell_get_b4(x);
+}
+
+GEN
+member_b6(GEN x)
+{
+  if (!is_ell(x)) member_err("b6",x);
+  return ell_get_b6(x);
+}
+
+GEN
+member_b8(GEN x)
+{
+  if (!is_ell(x)) member_err("b8",x);
+  return ell_get_b8(x);
+}
+
+GEN
+member_c4(GEN x)
+{
+  if (!is_ell(x)) member_err("c4",x);
+  return ell_get_c4(x);
+}
+
+GEN
+member_c6(GEN x)
+{
+  if (!is_ell(x)) member_err("c6",x);
+  return ell_get_c6(x);
+}
+
+GEN
+member_j(GEN x)
+{
+  if (!is_ell(x)) member_err("j",x);
+  return ell_get_j(x);
+}
+
+static int
+ell_is_complex(GEN x)
+{ long t = ell_get_type(x); return t == t_ELL_Q || t == t_ELL_Rg; }
+
+GEN
+member_omega(GEN x)
+{
+  if (!is_ell(x)) member_err("omega",x);
+  if (!ell_is_complex(x)) pari_err_TYPE("omega [not defined over C]",x);
+  return ellR_omega(x, ellR_get_prec(x));
+}
+
+GEN
+member_eta(GEN x)
+{
+  if (!is_ell(x)) member_err("eta",x);
+  if (!ell_is_complex(x)) pari_err_TYPE("eta [not defined over C]",x);
+  return ellR_eta(x, ellR_get_prec(x));
+}
+
+GEN
+member_area(GEN x)
+{
+  GEN w, w1, w2, a,b,c,d;
+  long prec;
+  if (!is_ell(x)) member_err("area",x);
+  if (!ell_is_complex(x)) pari_err_TYPE("area [not defined over C]",x);
+  prec = ellR_get_prec(x);
+  w = ellR_omega(x, prec);
+  w1 = gel(w,1); a = real_i(w1); b = imag_i(w1);
+  w2 = gel(w,2); c = real_i(w2); d = imag_i(w2);
+  return gabs(gsub(gmul(a,d),gmul(b,c)), prec);
+}
+
+GEN
+member_tate(GEN x)
+{
+  long prec;
+  if (!is_ell(x)) member_err("tate",x);
+  if (ell_get_type(x) != t_ELL_Qp)
+    pari_err_TYPE("tate [not defined over Qp]",x);
+  prec = ellQp_get_prec(x);
+  return ellQp_Tate_uniformization(x, prec);
+}
diff --git a/src/language/opcode.h b/src/language/opcode.h
new file mode 100644
index 0000000..daa2f66
--- /dev/null
+++ b/src/language/opcode.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+BEGINEXTERN
+
+typedef enum {Gvoid, Gsmall, Gvec, Gvar, Ggen, Gclosure} Gtype;
+
+typedef enum {OCpushlong='A',OCpushgnil,OCpushgen,OCpushreal,OCpushstoi,OCpushvar,
+              OCpop,
+              OCstoi,OCitos,OCtostr,OCvarn,OCcopy,OCcopyifclone,
+              OCprecreal,OCprecdl,
+              OCvec,OCmat,OCcol,
+              OCstackgen,
+              OCcompo1,OCcompo2,OCcompoC,OCcompoL,
+              OCpushptr,OCendptr,
+              OCcompo1ptr,OCcompo2ptr,OCcompoCptr,OCcompoLptr,
+              OCcalllong,OCcallgen,OCcallgen2,OCcallint,OCcallvoid,OCcalluser,
+              OCnewframe,OCsaveframe,
+              OCpushdyn,OCstoredyn,OCnewptrdyn,OCsimpleptrdyn,
+              OCpushlex,OCstorelex,OCnewptrlex,OCsimpleptrlex,
+              OCgetargs,OCdefaultarg,OClocalvar,OClocalvar0,
+              OCcheckargs,OCcheckargs0,OCdefaultgen,OCdefaultlong,
+              OCavma,OCgerepile,
+              OCcowvardyn,OCcowvarlex,
+              OCdup,OCstoreptr,OCcheckuserargs} op_code;
+
+ENDEXTERN
diff --git a/src/language/paricfg.c b/src/language/paricfg.c
new file mode 100644
index 0000000..8c58180
--- /dev/null
+++ b/src/language/paricfg.c
@@ -0,0 +1,21 @@
+/* Copyright (C) 2010  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+
+const char *paricfg_datadir = GPDATADIR;
+const char *paricfg_version = PARIVERSION;
+const char *paricfg_buildinfo = PARIINFO;
+const long  paricfg_version_code = PARI_VERSION_CODE;
+const char *paricfg_vcsversion = PARI_VCSVERSION;
+const char *paricfg_compiledate = __DATE__;
+const char *paricfg_mt_engine = PARI_MT_ENGINE;
diff --git a/src/language/pariinl.c b/src/language/pariinl.c
new file mode 100644
index 0000000..21fc467
--- /dev/null
+++ b/src/language/pariinl.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#define PARI_NO_MPINL_H
+#define INLINE
+#ifndef DISABLE_INLINE
+#  define DISABLE_INLINE
+#endif
+#include "pari.h"
diff --git a/src/language/parse.c b/src/language/parse.c
new file mode 100644
index 0000000..a25def3
--- /dev/null
+++ b/src/language/parse.c
@@ -0,0 +1,2903 @@
+/* A Bison parser, made by GNU Bison 2.5.  */
+
+/* Bison implementation for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+   
+   This program 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 of the License, or
+   (at your option) any later version.
+   
+   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names.  */
+#define yyparse         pari_parse
+#define yylex           pari_lex
+#define yyerror         pari_error
+#define yylval          pari_lval
+#define yychar          pari_char
+#define yydebug         pari_debug
+#define yynerrs         pari_nerrs
+#define yylloc          pari_lloc
+
+/* Copy the first part of user declarations.  */
+
+/* Line 268 of yacc.c  */
+#line 1 "../src/language/parse.y"
+
+/* Copyright (C) 2006  The PARI group.
+
+This file is part of the PARI package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define YYSIZE_T size_t
+#define YYSTYPE union token_value
+#define YYLTYPE struct node_loc
+#define YYLLOC_DEFAULT(Current, Rhs, N)     \
+        ((Current).start  = ((N)?(Rhs)[1].start:(Rhs)[0].end),  \
+         (Current).end    = (Rhs)[N].end)
+#include "parsec.h"
+#define NOARG(x) newnode(Fnoarg,-1,-1,&(x))
+
+
+/* Line 268 of yacc.c  */
+#line 104 "../src/language/parse.c"
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     KPARROW = 258,
+     KARROW = 259,
+     KDOTDOT = 260,
+     KPE = 261,
+     KSE = 262,
+     KME = 263,
+     KDE = 264,
+     KDRE = 265,
+     KEUCE = 266,
+     KMODE = 267,
+     KAND = 268,
+     KOR = 269,
+     KID = 270,
+     KEQ = 271,
+     KNE = 272,
+     KGE = 273,
+     KLE = 274,
+     KSRE = 275,
+     KSLE = 276,
+     KSR = 277,
+     KSL = 278,
+     KDR = 279,
+     KPP = 280,
+     KSS = 281,
+     KINTEGER = 282,
+     KREAL = 283,
+     KENTRY = 284,
+     KSTRING = 285,
+     DEFFUNC = 286,
+     SEQ = 287,
+     LVAL = 288,
+     INT = 289,
+     SIGN = 290
+   };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 343 of yacc.c  */
+#line 193 "../src/language/parse.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+    int yyi;
+#endif
+{
+  return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+	     && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+  YYLTYPE yyls_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)				\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack_alloc, Stack, yysize);			\
+	Stack = &yyptr->Stack_alloc;					\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  46
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   638
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  61
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  21
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  109
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  189
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   290
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    50,     2,    49,     2,    43,    38,    53,
+      55,    59,    46,    41,    36,    42,    54,    45,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,    56,    35,
+      40,    37,    39,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,    52,    44,    57,    48,     2,    58,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,    60,     2,    51,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      47
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint16 yyprhs[] =
+{
+       0,     0,     3,     5,     8,     9,    11,    14,    18,    19,
+      21,    25,    28,    34,    38,    40,    43,    45,    48,    51,
+      54,    58,    62,    64,    66,    68,    72,    74,    77,    79,
+      84,    86,    88,    90,    92,    94,    98,   102,   105,   108,
+     112,   116,   120,   124,   128,   132,   136,   140,   144,   147,
+     150,   154,   158,   162,   166,   170,   174,   178,   182,   186,
+     190,   194,   198,   202,   206,   210,   214,   218,   222,   226,
+     229,   232,   236,   239,   242,   245,   248,   250,   254,   258,
+     260,   263,   267,   269,   273,   277,   281,   284,   290,   294,
+     298,   302,   306,   311,   313,   317,   321,   327,   333,   335,
+     338,   339,   344,   346,   350,   355,   359,   366,   372,   376
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      62,     0,    -1,    63,    -1,    63,     1,    -1,    -1,    68,
+      -1,    63,    35,    -1,    63,    35,    68,    -1,    -1,    68,
+      -1,    68,     5,    68,    -1,    48,    68,    -1,    52,    64,
+      36,    64,    57,    -1,    52,    64,    57,    -1,    58,    -1,
+      66,    58,    -1,    43,    -1,    43,    27,    -1,    43,    66,
+      -1,    43,    49,    -1,    43,    49,    27,    -1,    43,    49,
+      66,    -1,    27,    -1,    28,    -1,    54,    -1,    27,    54,
+      29,    -1,    30,    -1,    53,    29,    -1,    67,    -1,    68,
+      55,    78,    59,    -1,    79,    -1,    69,    -1,    72,    -1,
+      75,    -1,    81,    -1,    72,    37,    68,    -1,    69,    37,
+      68,    -1,    69,    25,    -1,    69,    26,    -1,    69,     8,
+      68,    -1,    69,     9,    68,    -1,    69,    10,    68,    -1,
+      69,    11,    68,    -1,    69,    12,    68,    -1,    69,    21,
+      68,    -1,    69,    20,    68,    -1,    69,     6,    68,    -1,
+      69,     7,    68,    -1,    50,    68,    -1,    49,    68,    -1,
+      68,    14,    68,    -1,    68,    13,    68,    -1,    68,    38,
+      68,    -1,    68,    15,    68,    -1,    68,    16,    68,    -1,
+      68,    17,    68,    -1,    68,    18,    68,    -1,    68,    39,
+      68,    -1,    68,    19,    68,    -1,    68,    40,    68,    -1,
+      68,    42,    68,    -1,    68,    41,    68,    -1,    68,    23,
+      68,    -1,    68,    22,    68,    -1,    68,    43,    68,    -1,
+      68,    24,    68,    -1,    68,    44,    68,    -1,    68,    45,
+      68,    -1,    68,    46,    68,    -1,    41,    68,    -1,    42,
+      68,    -1,    68,    48,    68,    -1,    68,    51,    -1,    68,
+      53,    -1,    68,    50,    -1,    68,    65,    -1,    80,    -1,
+      68,    56,    29,    -1,    55,    68,    59,    -1,    29,    -1,
+      69,    65,    -1,    69,    56,    29,    -1,    68,    -1,    70,
+      36,    68,    -1,    70,    35,    70,    -1,    71,    35,    70,
+      -1,    52,    57,    -1,    52,    68,     5,    68,    57,    -1,
+      52,    35,    57,    -1,    52,    70,    57,    -1,    52,    71,
+      57,    -1,    52,     1,    57,    -1,    69,    40,    42,    68,
+      -1,    73,    -1,    73,    36,    68,    -1,    73,    35,    74,
+      -1,    73,    36,    68,    35,    74,    -1,    52,    68,    60,
+      74,    57,    -1,    63,    -1,    38,    69,    -1,    -1,    76,
+       1,    77,    68,    -1,    76,    -1,    78,    36,    76,    -1,
+      29,    55,    78,    59,    -1,    68,    54,    29,    -1,    29,
+      55,    78,    59,    37,    63,    -1,    68,    54,    29,    37,
+      63,    -1,    69,     4,    63,    -1,    55,    78,     3,    63,
+      -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    85,    85,    86,    89,    90,    91,    92,    95,    96,
+      97,    98,   101,   102,   105,   106,   109,   110,   111,   112,
+     113,   114,   117,   118,   119,   120,   122,   123,   124,   125,
+     126,   127,   128,   129,   130,   131,   132,   133,   134,   135,
+     136,   137,   138,   139,   140,   141,   142,   143,   144,   145,
+     146,   147,   148,   149,   150,   151,   152,   153,   154,   155,
+     156,   157,   158,   159,   160,   161,   162,   163,   164,   165,
+     166,   167,   168,   169,   170,   171,   172,   173,   174,   177,
+     178,   179,   182,   183,   186,   187,   190,   191,   192,   193,
+     194,   195,   198,   201,   202,   203,   204,   207,   210,   211,
+     212,   212,   216,   217,   220,   223,   226,   228,   230,   231
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "\")->\"", "\"->\"", "\"..\"", "\"+=\"",
+  "\"-=\"", "\"*=\"", "\"/=\"", "\"\\\\/=\"", "\"\\\\=\"", "\"%=\"",
+  "\"&&\"", "\"||\"", "\"===\"", "\"==\"", "\"!=\"", "\">=\"", "\"<=\"",
+  "\">>=\"", "\"<<=\"", "\">>\"", "\"<<\"", "\"\\\\/\"", "\"++\"",
+  "\"--\"", "\"integer\"", "\"real number\"", "\"variable name\"",
+  "\"character string\"", "DEFFUNC", "SEQ", "LVAL", "INT", "';'", "','",
+  "'='", "'&'", "'>'", "'<'", "'+'", "'-'", "'%'", "'\\\\'", "'/'", "'*'",
+  "SIGN", "'^'", "'#'", "'!'", "'~'", "'['", "'\\''", "'.'", "'('", "':'",
+  "']'", "'`'", "')'", "'|'", "$accept", "sequnused", "seq", "range",
+  "matrix_index", "backticks", "history", "expr", "lvalue", "matrixelts",
+  "matrixlines", "matrix", "in", "inseq", "compr", "arg", "$@1", "listarg",
+  "funcid", "memberid", "definition", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,    59,    44,    61,    38,    62,
+      60,    43,    45,    37,    92,    47,    42,   290,    94,    35,
+      33,   126,    91,    39,    46,    40,    58,    93,    96,    41,
+     124
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    61,    62,    62,    63,    63,    63,    63,    64,    64,
+      64,    64,    65,    65,    66,    66,    67,    67,    67,    67,
+      67,    67,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    68,    68,    68,    68,    68,    69,
+      69,    69,    70,    70,    71,    71,    72,    72,    72,    72,
+      72,    72,    73,    74,    74,    74,    74,    75,    76,    76,
+      77,    76,    78,    78,    79,    80,    81,    81,    81,    81
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     1,     2,     0,     1,     2,     3,     0,     1,
+       3,     2,     5,     3,     1,     2,     1,     2,     2,     2,
+       3,     3,     1,     1,     1,     3,     1,     2,     1,     4,
+       1,     1,     1,     1,     1,     3,     3,     2,     2,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     2,     2,
+       3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     3,     2,
+       2,     3,     2,     2,     2,     2,     1,     3,     3,     1,
+       2,     3,     1,     3,     3,     3,     2,     5,     3,     3,
+       3,     3,     4,     1,     3,     3,     5,     5,     1,     2,
+       0,     4,     1,     3,     4,     3,     6,     5,     3,     4
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       4,    22,    23,    79,    26,     0,     0,    16,     0,     0,
+       0,     0,    24,     4,     0,     0,    28,     5,    31,    32,
+      33,    30,    76,    34,     0,     4,    69,    70,    17,    19,
+      14,    18,    49,    48,     0,     0,    86,    82,     0,     0,
+      27,     0,    98,     5,     0,     0,     1,     3,     6,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    74,
+      72,     8,    73,     0,     4,     0,    75,     4,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,    37,    38,     0,
+       0,    80,     0,    25,     0,    20,    21,    15,    91,    88,
+       0,     0,     0,     0,    89,     0,    90,    79,    99,    78,
+     100,     4,     4,     7,    51,    50,    53,    54,    55,    56,
+      58,    63,    62,    65,    52,    57,    59,    61,    60,    64,
+      66,    67,    68,    71,     0,     0,     9,   105,     0,    77,
+     108,    46,    47,    39,    40,    41,    42,    43,    45,    44,
+      36,    81,    35,   104,     0,     0,    93,     0,    82,    84,
+      83,    85,     0,   109,     0,    11,     8,    13,     0,     4,
+      29,     4,    87,     0,     0,     0,    97,   101,     0,    10,
+     107,   106,     0,    95,    94,    12,    92,     0,    96
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,    14,    42,   135,    76,    31,    16,    17,    18,    38,
+      39,    19,   156,   157,    20,    44,   162,    45,    21,    22,
+      23
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -162
+static const yytype_int16 yypact[] =
+{
+     583,   -29,  -162,   -26,  -162,   583,   583,    45,   583,   583,
+      81,    -2,  -162,   549,    31,    36,  -162,   428,   223,     1,
+    -162,  -162,  -162,  -162,    41,   549,    93,    93,  -162,   -17,
+    -162,    27,   143,    58,    39,    42,  -162,   168,   -15,    48,
+    -162,    60,    66,   292,    29,     3,  -162,  -162,   583,   583,
+     583,   583,   583,   583,   583,   583,   583,   583,   583,   583,
+     583,   583,   583,   583,   583,   583,   583,   583,   583,  -162,
+    -162,   566,  -162,    64,   549,    73,  -162,   583,   583,   583,
+     583,   583,   583,   583,   583,   583,   583,  -162,  -162,   583,
+      75,  -162,   583,  -162,   -25,  -162,    27,  -162,  -162,  -162,
+     583,    60,   583,   583,  -162,   583,  -162,  -162,   -38,  -162,
+    -162,   583,   549,   428,   470,   470,   505,   505,   505,   505,
+     505,    93,    93,    93,   470,   505,   505,   519,   519,    93,
+      93,    93,    93,    93,   583,    33,   248,    69,   -19,  -162,
+      66,   428,   428,   428,   428,   428,   428,   428,   428,   428,
+     428,  -162,   428,    70,   339,   -28,   -13,    61,   428,    79,
+     428,    79,   583,    66,    32,   428,   566,  -162,   583,   583,
+    -162,   583,  -162,    77,    60,   583,  -162,   428,    68,   428,
+      66,    66,   583,  -162,   384,  -162,   428,    60,  -162
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+    -162,  -162,     9,   -49,   -16,    92,  -162,    -5,   -34,   -86,
+    -162,  -162,  -162,  -161,  -162,    14,  -162,   -10,  -162,  -162,
+    -162
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -104
+static const yytype_int16 yytable[] =
+{
+      26,    27,    91,    32,    33,    37,   111,   108,    43,    15,
+      95,   112,   173,   183,    71,    94,   159,   112,    90,   161,
+     102,   103,   174,   175,    71,    24,   188,    40,    90,    25,
+     110,    46,  -102,   110,   153,  -103,    -2,    47,    92,   112,
+     170,    30,   104,   113,   114,   115,   116,   117,   118,   119,
+     120,   121,   122,   123,   124,   125,   126,   127,   128,   129,
+     130,   131,   132,   133,   138,  -102,   136,   155,  -103,   166,
+      93,    48,    28,   141,   142,   143,   144,   145,   146,   147,
+     148,   149,    34,   105,   150,    97,   140,   152,  -102,   107,
+     167,  -103,    91,   137,    29,   154,    98,   158,   160,    99,
+     158,    48,   139,    30,   151,   106,   169,   171,     1,     2,
+       3,     4,    73,    74,    75,   103,    35,   178,   176,   182,
+     163,    96,     5,     6,     7,   185,   164,     0,     0,   165,
+       8,     9,     0,    10,    11,    12,    13,     0,    36,    91,
+     155,    68,     0,    69,    70,    71,    72,    73,    74,    75,
+       0,     0,     0,   155,     0,     0,     0,   177,     0,     0,
+       0,   136,     0,   179,     0,     0,     0,     0,     0,     0,
+     184,     0,     0,   100,     0,     0,     0,   186,   180,     0,
+     181,    49,    50,    51,    52,    53,    54,    55,     0,     0,
+      56,    57,    58,    69,    70,    71,    72,    73,    74,    75,
+       0,     0,     0,     0,     0,     0,    59,    60,    61,    62,
+      63,    64,    65,    66,    67,     0,    68,     0,    69,    70,
+      71,    72,    73,    74,    75,     0,     0,    77,   101,    78,
+      79,    80,    81,    82,    83,    84,     0,     0,     0,     0,
+       0,     0,     0,    85,    86,     0,     0,     0,    87,    88,
+       0,     0,     0,   168,     0,     0,     0,     0,     0,     0,
+      89,    49,    50,    51,    52,    53,    54,    55,     0,     0,
+      56,    57,    58,     0,     0,    71,     0,     0,     0,    90,
+       0,     0,     0,     0,     0,     0,    59,    60,    61,    62,
+      63,    64,    65,    66,    67,     0,    68,     0,    69,    70,
+      71,    72,    73,    74,    75,    49,    50,    51,    52,    53,
+      54,    55,     0,     0,    56,    57,    58,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      59,    60,    61,    62,    63,    64,    65,    66,    67,     0,
+      68,     0,    69,    70,    71,    72,    73,    74,    75,     0,
+       0,   109,    49,    50,    51,    52,    53,    54,    55,     0,
+       0,    56,    57,    58,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,    59,    60,    61,
+      62,    63,    64,    65,    66,    67,     0,    68,     0,    69,
+      70,    71,    72,    73,    74,    75,   172,    49,    50,    51,
+      52,    53,    54,    55,     0,     0,    56,    57,    58,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   187,
+       0,     0,    59,    60,    61,    62,    63,    64,    65,    66,
+      67,     0,    68,     0,    69,    70,    71,    72,    73,    74,
+      75,    49,    50,    51,    52,    53,    54,    55,     0,     0,
+      56,    57,    58,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,    59,    60,    61,    62,
+      63,    64,    65,    66,    67,     0,    68,     0,    69,    70,
+      71,    72,    73,    74,    75,    51,    52,    53,    54,    55,
+       0,     0,    56,    57,    58,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    60,
+      61,    62,    63,    64,    65,    66,    67,     0,    68,     0,
+      69,    70,    71,    72,    73,    74,    75,    56,    57,    58,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    56,    57,    58,     0,     0,    62,    63,    64,    65,
+      66,    67,     0,    68,     0,    69,    70,    71,    72,    73,
+      74,    75,    64,    65,    66,    67,     0,    68,     0,    69,
+      70,    71,    72,    73,    74,    75,     1,     2,     3,     4,
+       0,     0,     0,     0,     0,     0,     0,    41,     0,     0,
+       5,     6,     7,     1,     2,     3,     4,     0,     8,     9,
+       0,    10,    11,    12,    13,     0,     0,     5,     6,     7,
+       1,     2,     3,     4,   134,     8,     9,     0,    10,    11,
+      12,    13,     0,     0,     5,     6,     7,     0,     0,     0,
+       0,     0,     8,     9,     0,    10,    11,    12,    13
+};
+
+#define yypact_value_is_default(yystate) \
+  ((yystate) == (-162))
+
+#define yytable_value_is_error(yytable_value) \
+  YYID (0)
+
+static const yytype_int16 yycheck[] =
+{
+       5,     6,    18,     8,     9,    10,     3,    41,    13,     0,
+      27,    36,    40,   174,    52,    25,   102,    36,    56,   105,
+      35,    36,    35,    36,    52,    54,   187,    29,    56,    55,
+       1,     0,     3,     1,    59,     3,     0,     1,    37,    36,
+      59,    58,    57,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    74,    36,    71,   101,    36,    36,
+      29,    35,    27,    78,    79,    80,    81,    82,    83,    84,
+      85,    86,     1,    35,    89,    58,    77,    92,    59,    29,
+      57,    59,   108,    29,    49,   100,    57,   102,   103,    57,
+     105,    35,    29,    58,    29,    57,    37,    37,    27,    28,
+      29,    30,    54,    55,    56,    36,    35,   166,    57,    42,
+     111,    29,    41,    42,    43,    57,   112,    -1,    -1,   134,
+      49,    50,    -1,    52,    53,    54,    55,    -1,    57,   155,
+     174,    48,    -1,    50,    51,    52,    53,    54,    55,    56,
+      -1,    -1,    -1,   187,    -1,    -1,    -1,   162,    -1,    -1,
+      -1,   166,    -1,   168,    -1,    -1,    -1,    -1,    -1,    -1,
+     175,    -1,    -1,     5,    -1,    -1,    -1,   182,   169,    -1,
+     171,    13,    14,    15,    16,    17,    18,    19,    -1,    -1,
+      22,    23,    24,    50,    51,    52,    53,    54,    55,    56,
+      -1,    -1,    -1,    -1,    -1,    -1,    38,    39,    40,    41,
+      42,    43,    44,    45,    46,    -1,    48,    -1,    50,    51,
+      52,    53,    54,    55,    56,    -1,    -1,     4,    60,     6,
+       7,     8,     9,    10,    11,    12,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    20,    21,    -1,    -1,    -1,    25,    26,
+      -1,    -1,    -1,     5,    -1,    -1,    -1,    -1,    -1,    -1,
+      37,    13,    14,    15,    16,    17,    18,    19,    -1,    -1,
+      22,    23,    24,    -1,    -1,    52,    -1,    -1,    -1,    56,
+      -1,    -1,    -1,    -1,    -1,    -1,    38,    39,    40,    41,
+      42,    43,    44,    45,    46,    -1,    48,    -1,    50,    51,
+      52,    53,    54,    55,    56,    13,    14,    15,    16,    17,
+      18,    19,    -1,    -1,    22,    23,    24,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      38,    39,    40,    41,    42,    43,    44,    45,    46,    -1,
+      48,    -1,    50,    51,    52,    53,    54,    55,    56,    -1,
+      -1,    59,    13,    14,    15,    16,    17,    18,    19,    -1,
+      -1,    22,    23,    24,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    38,    39,    40,
+      41,    42,    43,    44,    45,    46,    -1,    48,    -1,    50,
+      51,    52,    53,    54,    55,    56,    57,    13,    14,    15,
+      16,    17,    18,    19,    -1,    -1,    22,    23,    24,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    35,
+      -1,    -1,    38,    39,    40,    41,    42,    43,    44,    45,
+      46,    -1,    48,    -1,    50,    51,    52,    53,    54,    55,
+      56,    13,    14,    15,    16,    17,    18,    19,    -1,    -1,
+      22,    23,    24,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    38,    39,    40,    41,
+      42,    43,    44,    45,    46,    -1,    48,    -1,    50,    51,
+      52,    53,    54,    55,    56,    15,    16,    17,    18,    19,
+      -1,    -1,    22,    23,    24,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    39,
+      40,    41,    42,    43,    44,    45,    46,    -1,    48,    -1,
+      50,    51,    52,    53,    54,    55,    56,    22,    23,    24,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    22,    23,    24,    -1,    -1,    41,    42,    43,    44,
+      45,    46,    -1,    48,    -1,    50,    51,    52,    53,    54,
+      55,    56,    43,    44,    45,    46,    -1,    48,    -1,    50,
+      51,    52,    53,    54,    55,    56,    27,    28,    29,    30,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    38,    -1,    -1,
+      41,    42,    43,    27,    28,    29,    30,    -1,    49,    50,
+      -1,    52,    53,    54,    55,    -1,    -1,    41,    42,    43,
+      27,    28,    29,    30,    48,    49,    50,    -1,    52,    53,
+      54,    55,    -1,    -1,    41,    42,    43,    -1,    -1,    -1,
+      -1,    -1,    49,    50,    -1,    52,    53,    54,    55
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,    27,    28,    29,    30,    41,    42,    43,    49,    50,
+      52,    53,    54,    55,    62,    63,    67,    68,    69,    72,
+      75,    79,    80,    81,    54,    55,    68,    68,    27,    49,
+      58,    66,    68,    68,     1,    35,    57,    68,    70,    71,
+      29,    38,    63,    68,    76,    78,     0,     1,    35,    13,
+      14,    15,    16,    17,    18,    19,    22,    23,    24,    38,
+      39,    40,    41,    42,    43,    44,    45,    46,    48,    50,
+      51,    52,    53,    54,    55,    56,    65,     4,     6,     7,
+       8,     9,    10,    11,    12,    20,    21,    25,    26,    37,
+      56,    65,    37,    29,    78,    27,    66,    58,    57,    57,
+       5,    60,    35,    36,    57,    35,    57,    29,    69,    59,
+       1,     3,    36,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    68,    68,    68,    48,    64,    68,    29,    78,    29,
+      63,    68,    68,    68,    68,    68,    68,    68,    68,    68,
+      68,    29,    68,    59,    68,    69,    73,    74,    68,    70,
+      68,    70,    77,    63,    76,    68,    36,    57,     5,    37,
+      59,    37,    57,    40,    35,    36,    57,    68,    64,    68,
+      63,    63,    42,    74,    68,    57,    68,    35,    74
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  However,
+   YYFAIL appears to be in use.  Nevertheless, it is formally deprecated
+   in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+   discussed.  */
+
+#define YYFAIL		goto yyerrlab
+#if defined YYFAIL
+  /* This is here to suppress warnings from the GCC cpp's
+     -Wunused-macros.  Normally we don't worry about that warning, but
+     some users do, and we want to make it easy for users to remove
+     YYFAIL uses, which will produce warnings from Bison 2.5.  */
+#endif
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (&yylloc, lex, YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc, lex)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value, Location, lex); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, char **lex)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, lex)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    char **lex;
+#endif
+{
+  if (!yyvaluep)
+    return;
+  YYUSE (yylocationp);
+  YYUSE (lex);
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, char **lex)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, lex)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+    YYLTYPE const * const yylocationp;
+    char **lex;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  YY_LOCATION_PRINT (yyoutput, *yylocationp);
+  YYFPRINTF (yyoutput, ": ");
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, lex);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+    yytype_int16 *yybottom;
+    yytype_int16 *yytop;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, char **lex)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule, lex)
+    YYSTYPE *yyvsp;
+    YYLTYPE *yylsp;
+    int yyrule;
+    char **lex;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       , &(yylsp[(yyi + 1) - (yynrhs)])		       , lex);
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, yylsp, Rule, lex); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  YYSIZE_T yysize1;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = 0;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - Assume YYFAIL is not used.  It's too flawed to consider.  See
+       <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+       for details.  YYERROR is fine as it does not invoke this
+       function.
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+                if (! (yysize <= yysize1
+                       && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  return 2;
+                yysize = yysize1;
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  yysize1 = yysize + yystrlen (yyformat);
+  if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    return 2;
+  yysize = yysize1;
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, char **lex)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp, lex)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+    YYLTYPE *yylocationp;
+    char **lex;
+#endif
+{
+  YYUSE (yyvaluep);
+  YYUSE (yylocationp);
+  YYUSE (lex);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+      case 63: /* "seq" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1415 "../src/language/parse.c"
+	break;
+      case 64: /* "range" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1424 "../src/language/parse.c"
+	break;
+      case 65: /* "matrix_index" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1433 "../src/language/parse.c"
+	break;
+      case 66: /* "backticks" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1442 "../src/language/parse.c"
+	break;
+      case 67: /* "history" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1451 "../src/language/parse.c"
+	break;
+      case 68: /* "expr" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1460 "../src/language/parse.c"
+	break;
+      case 69: /* "lvalue" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1469 "../src/language/parse.c"
+	break;
+      case 70: /* "matrixelts" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1478 "../src/language/parse.c"
+	break;
+      case 71: /* "matrixlines" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1487 "../src/language/parse.c"
+	break;
+      case 72: /* "matrix" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1496 "../src/language/parse.c"
+	break;
+      case 73: /* "in" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1505 "../src/language/parse.c"
+	break;
+      case 74: /* "inseq" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1514 "../src/language/parse.c"
+	break;
+      case 75: /* "compr" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1523 "../src/language/parse.c"
+	break;
+      case 76: /* "arg" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1532 "../src/language/parse.c"
+	break;
+      case 78: /* "listarg" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1541 "../src/language/parse.c"
+	break;
+      case 79: /* "funcid" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1550 "../src/language/parse.c"
+	break;
+      case 80: /* "memberid" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1559 "../src/language/parse.c"
+	break;
+      case 81: /* "definition" */
+
+/* Line 1391 of yacc.c  */
+#line 82 "../src/language/parse.y"
+	{ pari_discarded++; };
+
+/* Line 1391 of yacc.c  */
+#line 1568 "../src/language/parse.c"
+	break;
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (char **lex);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (char **lex)
+#else
+int
+yyparse (lex)
+    char **lex;
+#endif
+#endif
+{
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Location data for the lookahead symbol.  */
+YYLTYPE yylloc;
+
+    /* Number of syntax errors so far.  */
+    int yynerrs;
+
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       `yyss': related to states.
+       `yyvs': related to semantic values.
+       `yyls': related to locations.
+
+       Refer to the stacks thru separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    /* The location stack.  */
+    YYLTYPE yylsa[YYINITDEPTH];
+    YYLTYPE *yyls;
+    YYLTYPE *yylsp;
+
+    /* The locations where the error started and ended.  */
+    YYLTYPE yyerror_range[3];
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+  YYLTYPE yyloc;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yytoken = 0;
+  yyss = yyssa;
+  yyvs = yyvsa;
+  yyls = yylsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+  yyssp = yyss;
+  yyvsp = yyvs;
+  yylsp = yyls;
+
+#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 1;
+#endif
+
+/* User initialization code.  */
+
+/* Line 1590 of yacc.c  */
+#line 29 "../src/language/parse.y"
+{ yylloc.start=yylloc.end=*lex; }
+
+/* Line 1590 of yacc.c  */
+#line 1719 "../src/language/parse.c"
+  yylsp[0] = yylloc;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+	YYLTYPE *yyls1 = yyls;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+
+	yyls = yyls1;
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss_alloc, yyss);
+	YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+	YYSTACK_RELOCATE (yyls_alloc, yyls);
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+      yylsp = yyls + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+  *++yylsp = yylloc;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+
+/* Line 1806 of yacc.c  */
+#line 85 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 3:
+
+/* Line 1806 of yacc.c  */
+#line 86 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (2)].val); pari_unused_chars=(yylsp[(1) - (2)]).end;YYABORT;}
+    break;
+
+  case 4:
+
+/* Line 1806 of yacc.c  */
+#line 89 "../src/language/parse.y"
+    {(yyval.val)=NOARG((yyloc));}
+    break;
+
+  case 5:
+
+/* Line 1806 of yacc.c  */
+#line 90 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 6:
+
+/* Line 1806 of yacc.c  */
+#line 91 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (2)].val); (yyloc)=(yylsp[(1) - (2)]);}
+    break;
+
+  case 7:
+
+/* Line 1806 of yacc.c  */
+#line 92 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fseq,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 8:
+
+/* Line 1806 of yacc.c  */
+#line 95 "../src/language/parse.y"
+    { (yyval.val)=newnode(Frange,NOARG((yyloc)),NOARG((yyloc)),&(yyloc)); }
+    break;
+
+  case 9:
+
+/* Line 1806 of yacc.c  */
+#line 96 "../src/language/parse.y"
+    { (yyval.val)=newnode(Frange,(yyvsp[(1) - (1)].val),NOARG((yyloc)),&(yyloc)); }
+    break;
+
+  case 10:
+
+/* Line 1806 of yacc.c  */
+#line 97 "../src/language/parse.y"
+    { (yyval.val)=newnode(Frange,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc)); }
+    break;
+
+  case 11:
+
+/* Line 1806 of yacc.c  */
+#line 98 "../src/language/parse.y"
+    { (yyval.val)=newnode(Frange,NOARG((yyloc)),(yyvsp[(2) - (2)].val),&(yyloc)); }
+    break;
+
+  case 12:
+
+/* Line 1806 of yacc.c  */
+#line 101 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatrix,(yyvsp[(2) - (5)].val),(yyvsp[(4) - (5)].val),&(yyloc));}
+    break;
+
+  case 13:
+
+/* Line 1806 of yacc.c  */
+#line 102 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatrix,(yyvsp[(2) - (3)].val),-1,&(yyloc));}
+    break;
+
+  case 14:
+
+/* Line 1806 of yacc.c  */
+#line 105 "../src/language/parse.y"
+    {(yyval.val)=1;}
+    break;
+
+  case 15:
+
+/* Line 1806 of yacc.c  */
+#line 106 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (2)].val)+1;}
+    break;
+
+  case 16:
+
+/* Line 1806 of yacc.c  */
+#line 109 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPhist,-1,-1,&(yyloc));}
+    break;
+
+  case 17:
+
+/* Line 1806 of yacc.c  */
+#line 110 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPhist,newintnode(&(yylsp[(2) - (2)])),-1,&(yyloc));}
+    break;
+
+  case 18:
+
+/* Line 1806 of yacc.c  */
+#line 111 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPhist,newnode(Fsmall,-(yyvsp[(2) - (2)].val),-1,&(yyloc)),-1,&(yyloc));}
+    break;
+
+  case 19:
+
+/* Line 1806 of yacc.c  */
+#line 112 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPhisttime,-1,-1,&(yyloc));}
+    break;
+
+  case 20:
+
+/* Line 1806 of yacc.c  */
+#line 113 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPhisttime,newintnode(&(yylsp[(3) - (3)])),-1,&(yyloc));}
+    break;
+
+  case 21:
+
+/* Line 1806 of yacc.c  */
+#line 114 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPhisttime,newnode(Fsmall,-(yyvsp[(3) - (3)].val),-1,&(yyloc)),-1,&(yyloc));}
+    break;
+
+  case 22:
+
+/* Line 1806 of yacc.c  */
+#line 117 "../src/language/parse.y"
+    {(yyval.val)=newintnode(&(yylsp[(1) - (1)]));}
+    break;
+
+  case 23:
+
+/* Line 1806 of yacc.c  */
+#line 118 "../src/language/parse.y"
+    {(yyval.val)=newconst(CSTreal,&(yyloc));}
+    break;
+
+  case 24:
+
+/* Line 1806 of yacc.c  */
+#line 119 "../src/language/parse.y"
+    {(yyval.val)=newconst(CSTreal,&(yyloc));}
+    break;
+
+  case 25:
+
+/* Line 1806 of yacc.c  */
+#line 120 "../src/language/parse.y"
+    {(yyval.val)=newnode(Ffunction,newconst(CSTmember,&(yylsp[(3) - (3)])),
+                                                newintnode(&(yylsp[(1) - (3)])),&(yyloc));}
+    break;
+
+  case 26:
+
+/* Line 1806 of yacc.c  */
+#line 122 "../src/language/parse.y"
+    {(yyval.val)=newconst(CSTstr,&(yyloc));}
+    break;
+
+  case 27:
+
+/* Line 1806 of yacc.c  */
+#line 123 "../src/language/parse.y"
+    {(yyval.val)=newconst(CSTquote,&(yyloc));}
+    break;
+
+  case 28:
+
+/* Line 1806 of yacc.c  */
+#line 124 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 29:
+
+/* Line 1806 of yacc.c  */
+#line 125 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fcall,(yyvsp[(1) - (4)].val),(yyvsp[(3) - (4)].val),&(yyloc));}
+    break;
+
+  case 30:
+
+/* Line 1806 of yacc.c  */
+#line 126 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 31:
+
+/* Line 1806 of yacc.c  */
+#line 127 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 32:
+
+/* Line 1806 of yacc.c  */
+#line 128 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 33:
+
+/* Line 1806 of yacc.c  */
+#line 129 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 34:
+
+/* Line 1806 of yacc.c  */
+#line 130 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 35:
+
+/* Line 1806 of yacc.c  */
+#line 131 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fassign,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 36:
+
+/* Line 1806 of yacc.c  */
+#line 132 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fassign,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 37:
+
+/* Line 1806 of yacc.c  */
+#line 133 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPpp,(yyvsp[(1) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 38:
+
+/* Line 1806 of yacc.c  */
+#line 134 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPss,(yyvsp[(1) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 39:
+
+/* Line 1806 of yacc.c  */
+#line 135 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPme,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 40:
+
+/* Line 1806 of yacc.c  */
+#line 136 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPde,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 41:
+
+/* Line 1806 of yacc.c  */
+#line 137 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPdre,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 42:
+
+/* Line 1806 of yacc.c  */
+#line 138 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPeuce,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 43:
+
+/* Line 1806 of yacc.c  */
+#line 139 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPmode,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 44:
+
+/* Line 1806 of yacc.c  */
+#line 140 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPsle,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 45:
+
+/* Line 1806 of yacc.c  */
+#line 141 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPsre,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 46:
+
+/* Line 1806 of yacc.c  */
+#line 142 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPpe,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 47:
+
+/* Line 1806 of yacc.c  */
+#line 143 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPse,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 48:
+
+/* Line 1806 of yacc.c  */
+#line 144 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPnb,(yyvsp[(2) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 49:
+
+/* Line 1806 of yacc.c  */
+#line 145 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPlength,(yyvsp[(2) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 50:
+
+/* Line 1806 of yacc.c  */
+#line 146 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPor,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 51:
+
+/* Line 1806 of yacc.c  */
+#line 147 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPand,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 52:
+
+/* Line 1806 of yacc.c  */
+#line 148 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPand,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 53:
+
+/* Line 1806 of yacc.c  */
+#line 149 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPid,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 54:
+
+/* Line 1806 of yacc.c  */
+#line 150 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPeq,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 55:
+
+/* Line 1806 of yacc.c  */
+#line 151 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPne,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 56:
+
+/* Line 1806 of yacc.c  */
+#line 152 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPge,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 57:
+
+/* Line 1806 of yacc.c  */
+#line 153 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPg,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 58:
+
+/* Line 1806 of yacc.c  */
+#line 154 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPle,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 59:
+
+/* Line 1806 of yacc.c  */
+#line 155 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPl,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 60:
+
+/* Line 1806 of yacc.c  */
+#line 156 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPs,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 61:
+
+/* Line 1806 of yacc.c  */
+#line 157 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPp,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 62:
+
+/* Line 1806 of yacc.c  */
+#line 158 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPsl,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 63:
+
+/* Line 1806 of yacc.c  */
+#line 159 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPsr,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 64:
+
+/* Line 1806 of yacc.c  */
+#line 160 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPmod,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 65:
+
+/* Line 1806 of yacc.c  */
+#line 161 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPdr,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 66:
+
+/* Line 1806 of yacc.c  */
+#line 162 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPeuc,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 67:
+
+/* Line 1806 of yacc.c  */
+#line 163 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPd,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 68:
+
+/* Line 1806 of yacc.c  */
+#line 164 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPm,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 69:
+
+/* Line 1806 of yacc.c  */
+#line 165 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(2) - (2)].val);}
+    break;
+
+  case 70:
+
+/* Line 1806 of yacc.c  */
+#line 166 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPn,(yyvsp[(2) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 71:
+
+/* Line 1806 of yacc.c  */
+#line 167 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPpow,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 72:
+
+/* Line 1806 of yacc.c  */
+#line 168 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPtrans,(yyvsp[(1) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 73:
+
+/* Line 1806 of yacc.c  */
+#line 169 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPderiv,(yyvsp[(1) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 74:
+
+/* Line 1806 of yacc.c  */
+#line 170 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPfact,(yyvsp[(1) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 75:
+
+/* Line 1806 of yacc.c  */
+#line 171 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatcoeff,(yyvsp[(1) - (2)].val),(yyvsp[(2) - (2)].val),&(yyloc));}
+    break;
+
+  case 76:
+
+/* Line 1806 of yacc.c  */
+#line 172 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 77:
+
+/* Line 1806 of yacc.c  */
+#line 173 "../src/language/parse.y"
+    {(yyval.val)=newnode(Ftag,(yyvsp[(1) - (3)].val),0,&(yyloc));}
+    break;
+
+  case 78:
+
+/* Line 1806 of yacc.c  */
+#line 174 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(2) - (3)].val);}
+    break;
+
+  case 79:
+
+/* Line 1806 of yacc.c  */
+#line 177 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fentry,newconst(CSTentry,&(yylsp[(1) - (1)])),-1,&(yyloc));}
+    break;
+
+  case 80:
+
+/* Line 1806 of yacc.c  */
+#line 178 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatcoeff,(yyvsp[(1) - (2)].val),(yyvsp[(2) - (2)].val),&(yyloc));}
+    break;
+
+  case 81:
+
+/* Line 1806 of yacc.c  */
+#line 179 "../src/language/parse.y"
+    {(yyval.val)=newnode(Ftag,(yyvsp[(1) - (3)].val),newconst(CSTentry,&(yylsp[(2) - (3)])),&(yyloc));}
+    break;
+
+  case 82:
+
+/* Line 1806 of yacc.c  */
+#line 182 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 83:
+
+/* Line 1806 of yacc.c  */
+#line 183 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatrixelts,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 84:
+
+/* Line 1806 of yacc.c  */
+#line 186 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatrixlines,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 85:
+
+/* Line 1806 of yacc.c  */
+#line 187 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmatrixlines,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 86:
+
+/* Line 1806 of yacc.c  */
+#line 190 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fvec,-1,-1,&(yyloc));}
+    break;
+
+  case 87:
+
+/* Line 1806 of yacc.c  */
+#line 191 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPrange,(yyvsp[(2) - (5)].val),(yyvsp[(4) - (5)].val),&(yyloc));}
+    break;
+
+  case 88:
+
+/* Line 1806 of yacc.c  */
+#line 192 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmat,-1,-1,&(yyloc));}
+    break;
+
+  case 89:
+
+/* Line 1806 of yacc.c  */
+#line 193 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fvec,(yyvsp[(2) - (3)].val),-1,&(yyloc));}
+    break;
+
+  case 90:
+
+/* Line 1806 of yacc.c  */
+#line 194 "../src/language/parse.y"
+    {(yyval.val)=newnode(Fmat,(yyvsp[(2) - (3)].val),-1,&(yyloc));}
+    break;
+
+  case 91:
+
+/* Line 1806 of yacc.c  */
+#line 195 "../src/language/parse.y"
+    {(yyval.val)=-1; YYABORT;}
+    break;
+
+  case 92:
+
+/* Line 1806 of yacc.c  */
+#line 198 "../src/language/parse.y"
+    {(yyval.val)=newnode(Flistarg,(yyvsp[(4) - (4)].val),(yyvsp[(1) - (4)].val),&(yyloc));}
+    break;
+
+  case 93:
+
+/* Line 1806 of yacc.c  */
+#line 201 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPcompr,(yyvsp[(1) - (1)].val),-2,&(yyloc));}
+    break;
+
+  case 94:
+
+/* Line 1806 of yacc.c  */
+#line 202 "../src/language/parse.y"
+    {(yyval.val)=newopcall3(OPcompr,(yyvsp[(1) - (3)].val),-2,(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 95:
+
+/* Line 1806 of yacc.c  */
+#line 203 "../src/language/parse.y"
+    {(yyval.val)=newopcall(OPcomprc,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 96:
+
+/* Line 1806 of yacc.c  */
+#line 204 "../src/language/parse.y"
+    {(yyval.val)=newopcall3(OPcomprc,(yyvsp[(1) - (5)].val),(yyvsp[(5) - (5)].val),(yyvsp[(3) - (5)].val),&(yyloc));}
+    break;
+
+  case 97:
+
+/* Line 1806 of yacc.c  */
+#line 207 "../src/language/parse.y"
+    {(yyval.val)=addcurrexpr((yyvsp[(4) - (5)].val),(yyvsp[(2) - (5)].val),&(yyloc));}
+    break;
+
+  case 98:
+
+/* Line 1806 of yacc.c  */
+#line 210 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 99:
+
+/* Line 1806 of yacc.c  */
+#line 211 "../src/language/parse.y"
+    {(yyval.val)=newnode(Frefarg,(yyvsp[(2) - (2)].val),-1,&(yyloc));}
+    break;
+
+  case 100:
+
+/* Line 1806 of yacc.c  */
+#line 212 "../src/language/parse.y"
+    {if (!pari_once) { yyerrok; } pari_once=1;}
+    break;
+
+  case 101:
+
+/* Line 1806 of yacc.c  */
+#line 213 "../src/language/parse.y"
+    {pari_once=0; (yyval.val)=newopcall(OPcat,(yyvsp[(1) - (4)].val),(yyvsp[(4) - (4)].val),&(yyloc));}
+    break;
+
+  case 102:
+
+/* Line 1806 of yacc.c  */
+#line 216 "../src/language/parse.y"
+    {(yyval.val)=(yyvsp[(1) - (1)].val);}
+    break;
+
+  case 103:
+
+/* Line 1806 of yacc.c  */
+#line 217 "../src/language/parse.y"
+    {(yyval.val)=newnode(Flistarg,(yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 104:
+
+/* Line 1806 of yacc.c  */
+#line 220 "../src/language/parse.y"
+    {(yyval.val)=newnode(Ffunction,newconst(CSTentry,&(yylsp[(1) - (4)])),(yyvsp[(3) - (4)].val),&(yyloc));}
+    break;
+
+  case 105:
+
+/* Line 1806 of yacc.c  */
+#line 223 "../src/language/parse.y"
+    {(yyval.val)=newnode(Ffunction,newconst(CSTmember,&(yylsp[(3) - (3)])),(yyvsp[(1) - (3)].val),&(yyloc));}
+    break;
+
+  case 106:
+
+/* Line 1806 of yacc.c  */
+#line 227 "../src/language/parse.y"
+    {(yyval.val)=newfunc(CSTentry,&(yylsp[(1) - (6)]),(yyvsp[(3) - (6)].val),(yyvsp[(6) - (6)].val),&(yyloc));}
+    break;
+
+  case 107:
+
+/* Line 1806 of yacc.c  */
+#line 229 "../src/language/parse.y"
+    {(yyval.val)=newfunc(CSTmember,&(yylsp[(3) - (5)]),(yyvsp[(1) - (5)].val),(yyvsp[(5) - (5)].val),&(yyloc));}
+    break;
+
+  case 108:
+
+/* Line 1806 of yacc.c  */
+#line 230 "../src/language/parse.y"
+    {(yyval.val)=newnode(Flambda, (yyvsp[(1) - (3)].val),(yyvsp[(3) - (3)].val),&(yyloc));}
+    break;
+
+  case 109:
+
+/* Line 1806 of yacc.c  */
+#line 231 "../src/language/parse.y"
+    {(yyval.val)=newnode(Flambda, (yyvsp[(2) - (4)].val),(yyvsp[(4) - (4)].val),&(yyloc));}
+    break;
+
+
+
+/* Line 1806 of yacc.c  */
+#line 2664 "../src/language/parse.c"
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+  *++yylsp = yyloc;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (&yylloc, lex, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (&yylloc, lex, yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+  yyerror_range[1] = yylloc;
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval, &yylloc, lex);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  yyerror_range[1] = yylsp[1-yylen];
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      yyerror_range[1] = *yylsp;
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp, yylsp, lex);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  *++yyvsp = yylval;
+
+  yyerror_range[2] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the lookahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, yyerror_range, 2);
+  *++yylsp = yyloc;
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (&yylloc, lex, YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval, &yylloc, lex);
+    }
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp, yylsp, lex);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c  */
+#line 234 "../src/language/parse.y"
+
+
diff --git a/src/language/parse.h b/src/language/parse.h
new file mode 100644
index 0000000..5ca9868
--- /dev/null
+++ b/src/language/parse.h
@@ -0,0 +1,100 @@
+/* A Bison parser, made by GNU Bison 2.5.  */
+
+/* Bison interface for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+   
+   This program 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 of the License, or
+   (at your option) any later version.
+   
+   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     KPARROW = 258,
+     KARROW = 259,
+     KDOTDOT = 260,
+     KPE = 261,
+     KSE = 262,
+     KME = 263,
+     KDE = 264,
+     KDRE = 265,
+     KEUCE = 266,
+     KMODE = 267,
+     KAND = 268,
+     KOR = 269,
+     KID = 270,
+     KEQ = 271,
+     KNE = 272,
+     KGE = 273,
+     KLE = 274,
+     KSRE = 275,
+     KSLE = 276,
+     KSR = 277,
+     KSL = 278,
+     KDR = 279,
+     KPP = 280,
+     KSS = 281,
+     KINTEGER = 282,
+     KREAL = 283,
+     KENTRY = 284,
+     KSTRING = 285,
+     DEFFUNC = 286,
+     SEQ = 287,
+     LVAL = 288,
+     INT = 289,
+     SIGN = 290
+   };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
diff --git a/src/language/parse.y b/src/language/parse.y
new file mode 100644
index 0000000..cd77f20
--- /dev/null
+++ b/src/language/parse.y
@@ -0,0 +1,234 @@
+%{
+/* Copyright (C) 2006  The PARI group.
+
+This file is part of the PARI package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define YYSIZE_T size_t
+#define YYSTYPE union token_value
+#define YYLTYPE struct node_loc
+#define YYLLOC_DEFAULT(Current, Rhs, N)     \
+        ((Current).start  = ((N)?(Rhs)[1].start:(Rhs)[0].end),  \
+         (Current).end    = (Rhs)[N].end)
+#include "parsec.h"
+#define NOARG(x) newnode(Fnoarg,-1,-1,&(x))
+%}
+%error-verbose
+%name-prefix "pari_"
+%pure-parser
+%parse-param {char **lex}
+%lex-param {char **lex}
+%initial-action{ @$.start=@$.end=*lex; }
+%token KPARROW ")->"
+%token KARROW "->"
+%token KDOTDOT ".."
+%token KPE   "+="
+%token KSE   "-="
+%token KME   "*="
+%token KDE   "/="
+%token KDRE  "\\/="
+%token KEUCE "\\="
+%token KMODE "%="
+%token KAND  "&&"
+%token KOR   "||"
+%token KID   "==="
+%token KEQ   "=="
+%token KNE   "!="
+%token KGE   ">="
+%token KLE   "<="
+%token KSRE  ">>="
+%token KSLE  "<<="
+%token KSR   ">>"
+%token KSL   "<<"
+%token KDR   "\\/"
+%token KPP   "++"
+%token KSS   "--"
+%token <gen> KINTEGER "integer"
+%token <gen> KREAL "real number"
+%token KENTRY "variable name"
+%token KSTRING "character string"
+%left SEQ DEFFUNC
+%left INT LVAL
+%right ")->" "->"
+%left ';' ',' ".."
+%right '=' "+=" "-=" "*=" "/=" "\\/=" "\\=" "%=" ">>=" "<<="
+%left '&' "&&" "||"
+%left "===" "==" "!=" '>' ">=" '<' "<="
+%left '+' '-'
+%left '%' "\\/" '\\' '/' '*' ">>" "<<"
+%left SIGN
+%right '^'
+%left '#'
+%left '!' '~' '[' '\''
+%left '.'
+%left "++" "--"
+%left '('
+%left ':'
+%type <val> seq sequnused
+%type <val> range matrix matrix_index expr
+%type <val> lvalue
+%type <val> matrixelts matrixlines arg listarg definition
+%type <val> funcid memberid
+%type <val> backticks history
+%type <val> compr in inseq
+%destructor { pari_discarded++; } seq matrix range matrix_index expr lvalue matrixelts matrixlines arg listarg definition funcid memberid backticks history compr in inseq
+%%
+
+sequnused: seq       {$$=$1;}
+         | seq error {$$=$1; pari_unused_chars=@1.end;YYABORT;}
+;
+
+seq: /**/ %prec SEQ  {$$=NOARG(@$);}
+   | expr %prec SEQ  {$$=$1;}
+   | seq ';'         {$$=$1; @$=@1;}
+   | seq ';' expr    {$$=newnode(Fseq,$1,$3,&@$);}
+;
+
+range: /* */          { $$=newnode(Frange,NOARG(@$),NOARG(@$),&@$); }
+     | expr           { $$=newnode(Frange,$1,NOARG(@$),&@$); }
+     | expr ".." expr { $$=newnode(Frange,$1,$3,&@$); }
+     | '^' expr       { $$=newnode(Frange,NOARG(@$),$2,&@$); }
+;
+
+matrix_index: '[' range ',' range ']' {$$=newnode(Fmatrix,$2,$4,&@$);}
+            | '[' range ']'           {$$=newnode(Fmatrix,$2,-1,&@$);}
+;
+
+backticks: '`' {$$=1;}
+         | backticks '`' {$$=$1+1;}
+;
+
+history: '%'           {$$=newopcall(OPhist,-1,-1,&@$);}
+       | '%' KINTEGER  {$$=newopcall(OPhist,newintnode(&@2),-1,&@$);}
+       | '%' backticks {$$=newopcall(OPhist,newnode(Fsmall,-$2,-1,&@$),-1,&@$);}
+       | '%' '#'          {$$=newopcall(OPhisttime,-1,-1,&@$);}
+       | '%' '#' KINTEGER {$$=newopcall(OPhisttime,newintnode(&@3),-1,&@$);}
+       | '%' '#' backticks{$$=newopcall(OPhisttime,newnode(Fsmall,-$3,-1,&@$),-1,&@$);}
+;
+
+expr: KINTEGER %prec INT  {$$=newintnode(&@1);}
+    | KREAL               {$$=newconst(CSTreal,&@$);}
+    | '.'                 {$$=newconst(CSTreal,&@$);}
+    | KINTEGER '.' KENTRY {$$=newnode(Ffunction,newconst(CSTmember,&@3),
+                                                newintnode(&@1),&@$);}
+    | KSTRING       {$$=newconst(CSTstr,&@$);}
+    | '\'' KENTRY   {$$=newconst(CSTquote,&@$);}
+    | history           {$$=$1;}
+    | expr '(' listarg ')'  {$$=newnode(Fcall,$1,$3,&@$);}
+    | funcid            {$$=$1;}
+    | lvalue %prec LVAL {$$=$1;}
+    | matrix            {$$=$1;}
+    | compr             {$$=$1;}
+    | definition        {$$=$1;}
+    | matrix '=' expr {$$=newnode(Fassign,$1,$3,&@$);}
+    | lvalue '=' expr {$$=newnode(Fassign,$1,$3,&@$);}
+    | lvalue "++"     {$$=newopcall(OPpp,$1,-1,&@$);}
+    | lvalue "--"     {$$=newopcall(OPss,$1,-1,&@$);}
+    | lvalue "*="   expr {$$=newopcall(OPme,$1,$3,&@$);}
+    | lvalue "/="   expr {$$=newopcall(OPde,$1,$3,&@$);}
+    | lvalue "\\/=" expr {$$=newopcall(OPdre,$1,$3,&@$);}
+    | lvalue "\\="  expr {$$=newopcall(OPeuce,$1,$3,&@$);}
+    | lvalue "%="   expr {$$=newopcall(OPmode,$1,$3,&@$);}
+    | lvalue "<<="  expr {$$=newopcall(OPsle,$1,$3,&@$);}
+    | lvalue ">>="  expr {$$=newopcall(OPsre,$1,$3,&@$);}
+    | lvalue "+="   expr {$$=newopcall(OPpe,$1,$3,&@$);}
+    | lvalue "-="   expr {$$=newopcall(OPse,$1,$3,&@$);}
+    | '!' expr         {$$=newopcall(OPnb,$2,-1,&@$);}
+    | '#' expr         {$$=newopcall(OPlength,$2,-1,&@$);}
+    | expr "||"  expr  {$$=newopcall(OPor,$1,$3,&@$);}
+    | expr "&&"  expr  {$$=newopcall(OPand,$1,$3,&@$);}
+    | expr '&'   expr  {$$=newopcall(OPand,$1,$3,&@$);}
+    | expr "===" expr  {$$=newopcall(OPid,$1,$3,&@$);}
+    | expr "=="  expr  {$$=newopcall(OPeq,$1,$3,&@$);}
+    | expr "!="  expr  {$$=newopcall(OPne,$1,$3,&@$);}
+    | expr ">="  expr  {$$=newopcall(OPge,$1,$3,&@$);}
+    | expr '>'   expr  {$$=newopcall(OPg,$1,$3,&@$);}
+    | expr "<="  expr  {$$=newopcall(OPle,$1,$3,&@$);}
+    | expr '<'   expr  {$$=newopcall(OPl,$1,$3,&@$);}
+    | expr '-'   expr  {$$=newopcall(OPs,$1,$3,&@$);}
+    | expr '+'   expr  {$$=newopcall(OPp,$1,$3,&@$);}
+    | expr "<<"  expr  {$$=newopcall(OPsl,$1,$3,&@$);}
+    | expr ">>"  expr  {$$=newopcall(OPsr,$1,$3,&@$);}
+    | expr '%'   expr  {$$=newopcall(OPmod,$1,$3,&@$);}
+    | expr "\\/" expr  {$$=newopcall(OPdr,$1,$3,&@$);}
+    | expr '\\'  expr  {$$=newopcall(OPeuc,$1,$3,&@$);}
+    | expr '/'   expr  {$$=newopcall(OPd,$1,$3,&@$);}
+    | expr '*'   expr  {$$=newopcall(OPm,$1,$3,&@$);}
+    | '+' expr %prec SIGN {$$=$2;}
+    | '-' expr %prec SIGN {$$=newopcall(OPn,$2,-1,&@$);}
+    | expr '^' expr {$$=newopcall(OPpow,$1,$3,&@$);}
+    | expr '~' {$$=newopcall(OPtrans,$1,-1,&@$);}
+    | expr '\'' {$$=newopcall(OPderiv,$1,-1,&@$);}
+    | expr '!'  {$$=newopcall(OPfact,$1,-1,&@$);}
+    | expr matrix_index {$$=newnode(Fmatcoeff,$1,$2,&@$);}
+    | memberid {$$=$1;}
+    | expr ':' KENTRY   {$$=newnode(Ftag,$1,0,&@$);}
+    | '(' expr ')' {$$=$2;}
+;
+
+lvalue: KENTRY %prec LVAL   {$$=newnode(Fentry,newconst(CSTentry,&@1),-1,&@$);}
+      | lvalue matrix_index {$$=newnode(Fmatcoeff,$1,$2,&@$);}
+      | lvalue ':' KENTRY   {$$=newnode(Ftag,$1,newconst(CSTentry,&@2),&@$);}
+;
+
+matrixelts: expr {$$=$1;}
+          | matrixelts ',' expr {$$=newnode(Fmatrixelts,$1,$3,&@$);}
+;
+
+matrixlines: matrixelts  ';' matrixelts {$$=newnode(Fmatrixlines,$1,$3,&@$);}
+           | matrixlines ';' matrixelts {$$=newnode(Fmatrixlines,$1,$3,&@$);}
+;
+
+matrix: '[' ']'             {$$=newnode(Fvec,-1,-1,&@$);}
+      | '[' expr ".." expr ']' {$$=newopcall(OPrange,$2,$4,&@$);}
+      | '[' ';' ']'         {$$=newnode(Fmat,-1,-1,&@$);}
+      | '[' matrixelts ']'  {$$=newnode(Fvec,$2,-1,&@$);}
+      | '[' matrixlines ']' {$$=newnode(Fmat,$2,-1,&@$);}
+      | '[' error ']'       {$$=-1; YYABORT;}
+;
+
+in: lvalue '<' '-' expr {$$=newnode(Flistarg,$4,$1,&@$);}
+;
+
+inseq: in                    {$$=newopcall(OPcompr,$1,-2,&@$);}
+     | in ',' expr           {$$=newopcall3(OPcompr,$1,-2,$3,&@$);}
+     | in ';' inseq          {$$=newopcall(OPcomprc,$1,$3,&@$);}
+     | in ',' expr ';' inseq {$$=newopcall3(OPcomprc,$1,$5,$3,&@$);}
+;
+
+compr: '[' expr '|' inseq ']' {$$=addcurrexpr($4,$2,&@$);}
+;
+
+arg: seq        {$$=$1;}
+   | '&' lvalue {$$=newnode(Frefarg,$2,-1,&@$);}
+   | arg error  {if (!pari_once) { yyerrok; } pari_once=1;}  expr
+                     {pari_once=0; $$=newopcall(OPcat,$1,$4,&@$);}
+;
+
+listarg: arg {$$=$1;}
+       | listarg ',' arg {$$=newnode(Flistarg,$1,$3,&@$);}
+;
+
+funcid: KENTRY '(' listarg ')' {$$=newnode(Ffunction,newconst(CSTentry,&@1),$3,&@$);}
+;
+
+memberid: expr '.' KENTRY {$$=newnode(Ffunction,newconst(CSTmember,&@3),$1,&@$);}
+;
+
+definition: KENTRY '(' listarg ')' '=' seq %prec DEFFUNC
+                                   {$$=newfunc(CSTentry,&@1,$3,$6,&@$);}
+          | expr '.' KENTRY '=' seq %prec DEFFUNC
+                                   {$$=newfunc(CSTmember,&@3,$1,$5,&@$);}
+          | lvalue "->" seq              {$$=newnode(Flambda, $1,$3,&@$);}
+          | '(' listarg ")->" seq        {$$=newnode(Flambda, $2,$4,&@$);}
+;
+
+%%
diff --git a/src/language/parsec.h b/src/language/parsec.h
new file mode 100644
index 0000000..d21dcdc
--- /dev/null
+++ b/src/language/parsec.h
@@ -0,0 +1,223 @@
+/* Copyright (C) 2006-2008  The PARI group.
+
+This file is part of the PARI package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+BEGINEXTERN
+#include "parse.h"
+ENDEXTERN
+#include "anal.h"
+#include "tree.h"
+
+static THREAD int pari_once;
+static THREAD long pari_discarded;
+static THREAD const char *pari_lex_start, *pari_unused_chars;
+static THREAD GEN pari_lasterror;
+
+static void pari_error(YYLTYPE *yylloc, char **lex, const char *s)
+{
+  (void) yylloc; (void) lex;
+  if (pari_lasterror) cgiv(pari_lasterror);
+  pari_lasterror=strtoGENstr(s);
+}
+
+static THREAD pari_stack s_node;
+THREAD node *pari_tree;
+
+void
+pari_init_parser(void)
+{
+  long i;
+  const char *opname[]={"_||_", "_&&_", "_===_", "_==_", "_!=_", "_>=_", "_>_", "_<=_", "_<_", "_-_","_+_","_<<_", "_>>_", "_%_", "_\\/_", "_\\_", "_/_", "_*_","_^_","__","_--","_++","_-=_", "_+=_", "_<<=_", "_>>=_", "_%=_", "_\\/=_", "_\\=_", "_/=_", "_*=_","+_","-_","!_","_!","_'","_~","[_.._]","[_|_<-_,_]","[_|_<-_,_;_]","%","%#","#_",""};
+
+  pari_stack_init(&s_node,sizeof(*pari_tree),(void **)&pari_tree);
+  pari_stack_alloc(&s_node,OPnboperator);
+  parsestate_reset();
+  for (i=0;i<OPnboperator;i++)
+  {
+    pari_tree[i].f    = Fconst;
+    pari_tree[i].x    = CSTentry;
+    pari_tree[i].y    = -1;
+    pari_tree[i].str  = opname[i];
+    pari_tree[i].len  = strlen(opname[i]);
+    pari_tree[i].flags= 0;
+  }
+}
+void
+pari_close_parser(void) { pari_stack_delete(&s_node); }
+
+static void
+unused_chars(const char *lex, int strict)
+{
+  long n = 2 * term_width() - (17+19+1); /* Warning + unused... + . */
+  if (strict) compile_err("unused characters", lex);
+  if ((long)strlen(lex) > n) /* at most 2 lines */
+    pari_warn(warner, "unused characters: %.*s[+++]", n-5, lex);
+  else
+    pari_warn(warner, "unused characters: %s", lex);
+}
+
+void
+compile_err(const char *msg, const char *str)
+{
+  pari_err(e_SYNTAX, msg, str, pari_lex_start);
+}
+
+void
+compile_varerr(const char *str)
+{
+  pari_err(e_SYNTAX, "variable name expected", str, pari_lex_start);
+}
+
+void
+parsestate_reset(void)
+{
+  s_node.n = OPnboperator;
+  pari_lex_start = NULL;
+  pari_unused_chars=NULL;
+  pari_once=1;
+  pari_discarded=0;
+  pari_lasterror=NULL;
+}
+void
+parsestate_save(struct pari_parsestate *state)
+{
+  state->node = s_node.n;
+  state->lex_start = pari_lex_start;
+  state->unused_chars = pari_unused_chars;
+  state->once = pari_once;
+  state->discarded = pari_discarded;
+  state->lasterror = pari_lasterror;
+}
+void
+parsestate_restore(struct pari_parsestate *state)
+{
+  s_node.n = state->node;
+  pari_lex_start = state->lex_start;
+  pari_unused_chars = state->unused_chars;
+  pari_once = state->once;
+  pari_discarded = state->discarded;
+  pari_lasterror = state->lasterror;
+}
+
+GEN
+pari_compile_str(char *lex, int strict)
+{
+  pari_sp ltop=avma;
+  GEN code;
+  struct pari_parsestate state;
+  parsestate_save(&state);
+  pari_lex_start = lex;
+  pari_unused_chars=NULL;
+  pari_once=1;
+  pari_discarded=0;
+  pari_lasterror=NULL;
+  if (pari_parse(&lex) || pari_discarded)
+  {
+    if (pari_unused_chars && !pari_discarded)
+      unused_chars(pari_unused_chars,strict);
+    else if (pari_lasterror)
+      compile_err(GSTR(pari_lasterror),lex-1);
+    else /* should not happen */
+      compile_err("syntax error",lex-1);
+  }
+  avma=ltop;
+  optimizenode(s_node.n-1);
+  code=gp_closure(s_node.n-1);
+  parsestate_restore(&state);
+  return code;
+}
+
+static long
+newnode(Ffunc f, long x, long y, struct node_loc *loc)
+{
+  long n=pari_stack_new(&s_node);
+  pari_tree[n].f=f;
+  pari_tree[n].x=x;
+  pari_tree[n].y=y;
+  pari_tree[n].str=loc->start;
+  pari_tree[n].len=loc->end-loc->start;
+  pari_tree[n].flags=0;
+  return n;
+}
+
+static long
+newconst(long x, struct node_loc *loc)
+{
+  return newnode(Fconst,x,-1,loc);
+}
+
+static long
+newopcall(OPerator op, long x, long y, struct node_loc *loc)
+{
+  if (y==-1)
+    return newnode(Ffunction,op,x,loc);
+  else
+    return newnode(Ffunction,op,newnode(Flistarg,x,y,loc),loc);
+}
+
+static long
+newopcall3(OPerator op, long x, long y, long z, struct node_loc *loc)
+{
+  return newopcall(op,newnode(Flistarg,x,y,loc),z,loc);
+}
+
+static long
+countarg(long n)
+{
+  long i;
+  for(i=1; pari_tree[n].f==Flistarg; i++)
+    n = pari_tree[n].x;
+  return i;
+}
+
+static long
+addcurrexpr(long n, long currexpr, struct node_loc *loc)
+{
+  long y, m = n;
+  while (pari_tree[m].x==OPcomprc)
+  {
+    y = pari_tree[m].y; if (countarg(y)==4) y = pari_tree[y].x;
+    m = pari_tree[y].y;
+  }
+  y = pari_tree[m].y; if (countarg(y)==4) y = pari_tree[y].x;
+  pari_tree[y].y = currexpr;
+  pari_tree[n].str=loc->start;
+  pari_tree[n].len=loc->end-loc->start;
+  return n;
+}
+
+static long
+newintnode(struct node_loc *loc)
+{
+  if (loc->end-loc->start<=(long)(1+LOG10_2*BITS_IN_LONG))
+  {
+    pari_sp ltop=avma;
+    GEN g=strtoi(loc->start);
+    long s;
+    avma=ltop;
+    if (signe(g)==0)      return newnode(Fsmall,0,-1,loc);
+    if ((s=itos_or_0(g))) return newnode(Fsmall,s,-1,loc);
+  }
+  return newconst(CSTint,loc);
+}
+
+static long
+newfunc(CSTtype t, struct node_loc *func, long args, long code,
+                   struct node_loc *loc)
+{
+  long name=newnode(Fentry,newconst(t,func),-1,func);
+  return newnode(Fassign,name,newnode(Flambda,args,code,loc),loc);
+}
+
+
diff --git a/src/language/sumiter.c b/src/language/sumiter.c
new file mode 100644
index 0000000..c03134f
--- /dev/null
+++ b/src/language/sumiter.c
@@ -0,0 +1,1760 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+#include "anal.h"
+
+GEN
+iferrpari(GEN a, GEN b, GEN c)
+{
+  GEN res;
+  struct pari_evalstate state;
+  evalstate_save(&state);
+  pari_CATCH(CATCH_ALL)
+  {
+    GEN E;
+    if (!b&&!c) return gnil;
+    E = evalstate_restore_err(&state);
+    if (c)
+    {
+      push_lex(E,c);
+      res = closure_evalgen(c);
+      pop_lex(1);
+      if (gequal0(res))
+        pari_err(0, E);
+    }
+    if (!b) return gnil;
+    push_lex(E,b);
+    res = closure_evalgen(b);
+    pop_lex(1);
+    return res;
+  } pari_TRY {
+    res = closure_evalgen(a);
+  } pari_ENDCATCH;
+  return res;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                        ITERATIONS                              **/
+/**                                                                **/
+/********************************************************************/
+
+static void
+forparii(GEN a, GEN b, GEN code)
+{
+  pari_sp av, av0 = avma, lim;
+  GEN aa;
+  if (gcmp(b,a) < 0) return;
+  b = gfloor(b);
+  aa = a = setloop(a);
+  av=avma; lim = stack_lim(av,1);
+  push_lex(a,code);
+  while (gcmp(a,b) <= 0)
+  {
+    closure_evalvoid(code); if (loop_break()) break;
+    a = get_lex(-1);
+    if (a == aa)
+    {
+      a = incloop(a);
+      if (a != aa) { set_lex(-1,a); aa = a; }
+    }
+    else
+    { /* 'code' modified a ! Be careful (and slow) from now on */
+      a = gaddgs(a,1);
+      if (low_stack(lim, stack_lim(av,1)))
+      {
+        if (DEBUGMEM>1) pari_warn(warnmem,"forparii");
+        a = gerepileupto(av,a);
+      }
+      set_lex(-1,a);
+    }
+  }
+  pop_lex(1);  avma = av0;
+}
+
+void
+forpari(GEN a, GEN b, GEN code)
+{
+  pari_sp ltop=avma, av, lim;
+  if (typ(a) == t_INT) { forparii(a,b,code); return; }
+  b = gcopy(b); /* Kludge to work-around the a+(a=2) bug */
+  av=avma; lim = stack_lim(av,1);
+  push_lex(a,code);
+  while (gcmp(a,b) <= 0)
+  {
+    closure_evalvoid(code); if (loop_break()) break;
+    a = get_lex(-1); a = gaddgs(a,1);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"forpari");
+      a = gerepileupto(av,a);
+    }
+    set_lex(-1, a);
+  }
+  pop_lex(1); avma = ltop;
+}
+
+void
+whilepari(GEN a, GEN b)
+{
+  pari_sp av = avma;
+  for(;;)
+  {
+    GEN res = closure_evalnobrk(a);
+    if (gequal0(res)) break;
+    avma = av;
+    closure_evalvoid(b); if (loop_break()) break;
+  }
+  avma = av;
+}
+
+void
+untilpari(GEN a, GEN b)
+{
+  pari_sp av = avma;
+  for(;;)
+  {
+    GEN res;
+    closure_evalvoid(b); if (loop_break()) break;
+    res = closure_evalnobrk(a);
+    if (!gequal0(res)) break;
+    avma = av;
+  }
+  avma = av;
+}
+
+static int negcmp(GEN x, GEN y) { return gcmp(y,x); }
+
+void
+forstep(GEN a, GEN b, GEN s, GEN code)
+{
+  long ss, i;
+  pari_sp av, av0 = avma, lim;
+  GEN v = NULL;
+  int (*cmp)(GEN,GEN);
+
+  b = gcopy(b); av=avma; lim = stack_lim(av,1);
+  push_lex(a,code);
+  if (is_vec_t(typ(s)))
+  {
+    v = s; s = gen_0;
+    for (i=lg(v)-1; i; i--) s = gadd(s,gel(v,i));
+  }
+  ss = gsigne(s);
+  if (!ss) pari_err_DOMAIN("forstep","step","=",gen_0,s);
+  cmp = (ss > 0)? &gcmp: &negcmp;
+  i = 0;
+  while (cmp(a,b) <= 0)
+  {
+    closure_evalvoid(code); if (loop_break()) break;
+    if (v)
+    {
+      if (++i >= lg(v)) i = 1;
+      s = gel(v,i);
+    }
+    a = get_lex(-1); a = gadd(a,s);
+
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"forstep");
+      a = gerepileupto(av,a);
+    }
+    set_lex(-1,a);
+  }
+  pop_lex(1); avma = av0;
+}
+
+/* return good chunk size for sieve, 16 | chunk + 2 */
+static ulong
+optimize_chunk(ulong a, ulong b)
+{
+  /* TODO: Optimize size (surely < 512k to stay in L1 cache, but not so large */
+  /* as to force recalculating too often). */
+  /* Guesstimate: greater of sqrt(n) * lg(n) or 1M */
+  ulong chunk = maxuu(0x100000, usqrt(b) * expu(b));
+  ulong tmp = (b - a) / chunk + 1;
+
+  if (tmp == 1)
+    chunk = b - a + 16;
+  else
+    chunk = (b - a) / tmp + 15;
+  /* Don't take up more than 2/3 of the stack */
+  chunk = minuu(chunk, avma - stack_lim(avma, 2));
+  /* ensure 16 | chunk + 2 */
+  return (((chunk + 2)>>4)<<4) - 2;
+}
+static void
+sieve_init(forprime_t *T, ulong a, ulong b)
+{
+  T->sieveb = b;
+  T->chunk = optimize_chunk(a, b);
+  T->sieve = (unsigned char*)stack_malloc(((T->chunk+2) >> 4) + 1);
+  T->cache[0] = 0;
+  /* >> 1 [only odds] + 3 [convert from bits to bytes] */
+  T->a = a;
+  T->end = minuu(a + T->chunk, b);
+  T->pos = T->maxpos = 0;
+}
+
+static void
+u_forprime_set_prime_table(forprime_t *T, ulong a)
+{
+  T->strategy = 1;
+  if (a < 3)
+  {
+    T->p = 0;
+    T->d = diffptr;
+  }
+  else
+    T->p = init_primepointer_lt(a, &T->d);
+}
+
+/* Set p so that p + q the smallest integer = c (mod q) and > original p.
+ * Assume 0 < c < q. Set p = 0 on overflow */
+static void
+arith_set(forprime_t *T)
+{
+  ulong r = T->p % T->q; /* 0 <= r <= min(p, q-1) */
+  pari_sp av = avma;
+  GEN d = adduu(T->p - r, T->c);
+  if (T->c > r) d = subiu(d, T->q);
+  /* d = c mod q,  d = c > r? p-r+c-q: p-r+c, so that
+   *  d <= p  and  d+q = c>r? p-r+c  : p-r+c+q > p */
+  T->p = itou_or_0(d); avma = av; /* d = 0 is impossible */
+}
+
+/* run through primes in arithmetic progression = c (mod q).
+ * Assume (c,q)=1, 0 <= c < q */
+int
+u_forprime_arith_init(forprime_t *T, ulong a, ulong b, ulong c, ulong q)
+{
+  ulong maxp, maxp2;
+  if (a > b || b < 2)
+  {
+    T->strategy = 1; /* paranoia */
+    T->p = 0; /* empty */
+    T->b = 0; /* empty */
+    T->d = diffptr;
+    return 0;
+  }
+  maxp = maxprime();
+  if (q != 1 && c != 2 && odd(q)) {
+    /* only allow *odd* primes. If c = 2, then p = 2 must be included :-( */
+    if (!odd(c)) c += q;
+    q <<= 1;
+  }
+  T->q = q;
+  T->c = c;
+  T->strategy = 0; /* unknown */
+  T->sieve = NULL; /* unused for now */
+  T->b = b;
+  if (maxp >= b) { /* [a,b] \subset prime table */
+    u_forprime_set_prime_table(T, a);
+    return 1;
+  }
+  /* b > maxp */
+  if (a >= maxp)
+  {
+    if (T->q == 1)
+      T->p = a - 1;
+    else
+      arith_set(T);
+  }
+  else
+    u_forprime_set_prime_table(T, a);
+
+  maxp2 = (maxp & HIGHMASK)? 0 : maxp*maxp;
+  /* FIXME: should sieve as well if q != 1, adapt sieve code */
+  if (q != 1 || (maxp2 && maxp2 <= a)
+             || T->b - maxuu(a,maxp) < maxp / expu(b))
+  { if (!T->strategy) T->strategy = 3; }
+  else
+  { /* worth sieving */
+#ifdef LONG_IS_64BIT
+    const ulong UPRIME_MAX = 18446744073709551557UL;
+#else
+    const ulong UPRIME_MAX = 4294967291UL;
+#endif
+    ulong sieveb;
+    if (b > UPRIME_MAX) b = UPRIME_MAX;
+    sieveb = b;
+    if (maxp2 && maxp2 < b) sieveb = maxp2;
+    if (!T->strategy) T->strategy = 2;
+    if (!odd(sieveb)) sieveb--;
+    sieve_init(T, maxuu(maxp+2, a), sieveb);
+  }
+  return 1;
+}
+
+/* will run through primes in [a,b] */
+int
+u_forprime_init(forprime_t *T, ulong a, ulong b)
+{ return u_forprime_arith_init(T, a,b, 0,1); }
+/* now only run through primes <= c; assume c <= b above */
+void
+u_forprime_restrict(forprime_t *T, ulong c) { T->b = c; }
+
+/* b = NULL: loop forever */
+int
+forprime_init(forprime_t *T, GEN a, GEN b)
+{
+  long lb;
+  a = gceil(a); if (typ(a) != t_INT) pari_err_TYPE("forprime_init",a);
+  if (signe(a) <= 0) a = gen_1;
+  if (b)
+  {
+    b = gfloor(b);
+    if (typ(b) != t_INT) pari_err_TYPE("forprime_init",b);
+    if (signe(b) < 0 || cmpii(a,b) > 0)
+    {
+      T->strategy = 4; /* paranoia */
+      T->bb = gen_0;
+      T->pp = gen_0;
+      return 0;
+    }
+    lb = lgefint(b);
+  }
+  else
+    lb = lgefint(a) + 4;
+  T->bb = b;
+  T->pp = cgeti(lb);
+  /* a, b are positive integers, a <= b */
+  if (lgefint(a) == 3) /* lb == 3 implies b != NULL */
+    return u_forprime_init(T, a[2], lb == 3? (ulong)b[2]: ULONG_MAX);
+  T->strategy = 4;
+  affii(subiu(a,1), T->pp);
+  return 1;
+}
+
+/* assume a <= b <= maxprime()^2, a,b odd, sieve[n] corresponds to
+ *   a+16*n, a+16*n+2, ..., a+16*n+14 (bits 0 to 7)
+ * maxpos = index of last sieve cell. */
+static void
+sieve_block(ulong a, ulong b, ulong maxpos, unsigned char* sieve)
+{
+  ulong p = 2, lim = usqrt(b), sz = (b-a) >> 1;
+  byteptr d = diffptr+1;
+  (void)memset(sieve, 0, maxpos+1);
+  for (;;)
+  { /* p is odd */
+    ulong k, r;
+    NEXT_PRIME_VIADIFF(p, d); /* starts at p = 3 */
+    if (p > lim) break;
+
+    /* solve a + 2k = 0 (mod p) */
+    r = a % p;
+    if (r == 0)
+      k = 0;
+    else
+    {
+      k = p - r;
+      if (odd(k)) k += p;
+      k >>= 1;
+    }
+    /* m = a + 2k is the smallest odd m >= a, p | m */
+    /* position n (corresponds to a+2n) is sieve[n>>3], bit n&7 */
+    while (k <= sz) { sieve[k>>3] |= 1 << (k&7); k += p; /* 2k += 2p */ }
+  }
+}
+
+/* T->cache is a 0-terminated list of primes, return the first one and
+ * remove it from list. Most of the time the list contains a single prime */
+static ulong
+shift_cache(forprime_t *T)
+{
+  long i;
+  T->p = T->cache[0];
+  for (i = 1;; i++)  /* remove one prime from cache */
+    if (! (T->cache[i-1] = T->cache[i]) ) break;
+  return T->p;
+}
+
+ulong
+u_forprime_next(forprime_t *T)
+{
+  if (T->strategy == 1)
+  {
+    for(;;)
+    {
+      if (!*(T->d))
+      {
+        T->strategy = T->sieve? 2: 3;
+        if (T->q != 1) { arith_set(T); if (!T->p) return 0; }
+        /* T->p possibly not a prime if q != 1 */
+        break;
+      }
+      else
+      {
+        NEXT_PRIME_VIADIFF(T->p, T->d);
+        if (T->p > T->b) return 0;
+        if (T->q == 1 || T->p % T->q == T->c) return T->p;
+      }
+    }
+  }
+  if (T->strategy == 2)
+  {
+    ulong n;
+    if (T->cache[0]) return shift_cache(T);
+NEXT_CHUNK:
+    for (n = T->pos; n < T->maxpos; n++)
+      if (T->sieve[n] != 0xFF)
+      {
+        unsigned char mask = T->sieve[n];
+        ulong p = T->a + (n<<4);
+        long i = 0;
+        T->pos = n;
+        if (!(mask &  1)) T->cache[i++] = p;
+        if (!(mask &  2)) T->cache[i++] = p+2;
+        if (!(mask &  4)) T->cache[i++] = p+4;
+        if (!(mask &  8)) T->cache[i++] = p+6;
+        if (!(mask & 16)) T->cache[i++] = p+8;
+        if (!(mask & 32)) T->cache[i++] = p+10;
+        if (!(mask & 64)) T->cache[i++] = p+12;
+        if (!(mask &128)) T->cache[i++] = p+14;
+        T->cache[i] = 0;
+        T->pos = n+1;
+        return shift_cache(T);
+      }
+    /* n = T->maxpos, last cell: check p <= b */
+    if (T->maxpos && n == T->maxpos && T->sieve[n] != 0xFF)
+    {
+      unsigned char mask = T->sieve[n];
+      ulong p = T->a + (n<<4);
+      long i = 0;
+      T->pos = n;
+      if (!(mask &  1) && p <= T->sieveb) T->cache[i++] = p;
+      if (!(mask &  2) && p <= T->sieveb-2) T->cache[i++] = p+2;
+      if (!(mask &  4) && p <= T->sieveb-4) T->cache[i++] = p+4;
+      if (!(mask &  8) && p <= T->sieveb-6) T->cache[i++] = p+6;
+      if (!(mask & 16) && p <= T->sieveb-8) T->cache[i++] = p+8;
+      if (!(mask & 32) && p <= T->sieveb-10) T->cache[i++] = p+10;
+      if (!(mask & 64) && p <= T->sieveb-12) T->cache[i++] = p+12;
+      if (!(mask &128) && p <= T->sieveb-14) T->cache[i++] = p+14;
+      if (i)
+      {
+        T->cache[i] = 0;
+        T->pos = n+1;
+        return shift_cache(T);
+      }
+    }
+
+    if (T->end >= T->sieveb) /* done */
+      T->strategy = 3;
+    else
+    { /* initialize next chunk */
+      if (T->maxpos == 0)
+        T->a |= 1; /* first time; ensure odd */
+      else
+        T->a = (T->end + 2) | 1;
+      T->end = T->a + T->chunk; /* may overflow */
+      if (T->end < T->a || T->end > T->sieveb) T->end = T->sieveb;
+      /* end and a are odd; sieve[k] contains the a + 8*2k + (0,2,...,14).
+       * The largest k is (end-a) >> 4 */
+      T->pos = 0;
+      T->maxpos = (T->end - T->a) >> 4;
+      sieve_block(T->a, T->end, T->maxpos, T->sieve);
+      goto NEXT_CHUNK;
+    }
+  }
+  if (T->strategy == 3)
+  {
+    if (T->q == 1)
+      T->p = unextprime(T->p + 1);
+    else
+    {
+      do {
+        T->p += T->q;
+        if (T->p < T->q) return 0; /* overflow */
+      } while (!uisprime(T->p));
+    }
+    if (!T->p) /* overflow ulong, switch to GEN */
+      T->strategy = 4;
+    else
+    {
+      if (T->p > T->b) return 0;
+      return T->p;
+    }
+  }
+  return 0; /* overflow */
+}
+
+GEN
+forprime_next(forprime_t *T)
+{
+  pari_sp av;
+  GEN p;
+  if (T->strategy <= 3)
+  {
+    ulong q = u_forprime_next(T);
+    if (q) { affui(q, T->pp); return T->pp; }
+    /* failure */
+    if (T->strategy <= 3) return NULL; /* we're done */
+    /* overflow ulong, switch to GEN */
+    affui(ULONG_MAX, T->pp); /* initialize */
+  }
+  av = avma;
+  p = nextprime(addiu(T->pp, 1));
+  if (T->bb && absi_cmp(p, T->bb) > 0) { avma = av; return NULL; }
+  affii(p, T->pp); avma = av; return T->pp;
+}
+
+void
+forprime(GEN a, GEN b, GEN code)
+{
+  pari_sp av = avma;
+  forprime_t T;
+
+  if (!forprime_init(&T, a,b)) { avma = av; return; }
+
+  push_lex(T.pp,code);
+  while(forprime_next(&T))
+  {
+    closure_evalvoid(code); if (loop_break()) break;
+    /* p changed in 'code', complain */
+    if (get_lex(-1) != T.pp)
+      pari_err(e_MISC,"prime index read-only: was changed to %Ps", get_lex(-1));
+  }
+  pop_lex(1); avma = av;
+}
+
+int
+forcomposite_init(forcomposite_t *C, GEN a, GEN b)
+{
+  pari_sp av = avma;
+  a = gceil(a); if (typ(a)!=t_INT) pari_err_TYPE("forcomposite",a);
+  if (b) {
+    b = gfloor(b);if (typ(b)!=t_INT) pari_err_TYPE("forcomposite",b);
+  }
+  if (signe(a) < 0) pari_err_DOMAIN("forcomposite", "a", "<", gen_0, a);
+  if (cmpiu(a, 4) < 0) a = utoipos(4);
+  C->first = 1;
+  if (!forprime_init(&C->T, a,b))
+  {
+    C->n = gen_1; /* in case caller forgets to check the return value */
+    C->b = gen_0;
+    avma = av; return 0;
+  }
+  C->n = setloop(a);
+  C->b = b;
+  C->p = NULL; return 1;
+}
+
+GEN
+forcomposite_next(forcomposite_t *C)
+{
+  if (C->first) /* first call ever */
+  {
+    C->first = 0;
+    C->p = forprime_next(&C->T);
+  }
+  else
+    C->n = incloop(C->n);
+  if (C->p)
+  {
+    if (cmpii(C->n, C->p) < 0) return C->n;
+    C->n = incloop(C->n);
+    /* n = p+1 */
+    C->p = forprime_next(&C->T); /* nextprime(p) > n */
+    if (C->p) return C->n;
+  }
+  if (!C->b || cmpii(C->n, C->b) <= 0) return C->n;
+  return NULL;
+}
+
+void
+forcomposite(GEN a, GEN b, GEN code)
+{
+  pari_sp av = avma;
+  forcomposite_t T;
+  GEN n;
+  if (!forcomposite_init(&T,a,b)) return;
+  push_lex(T.n,code);
+  while((n = forcomposite_next(&T)))
+  {
+    closure_evalvoid(code); if (loop_break()) break;
+    /* n changed in 'code', complain */
+    if (get_lex(-1) != n)
+      pari_err(e_MISC,"index read-only: was changed to %Ps", get_lex(-1));
+  }
+  pop_lex(1); avma = av;
+}
+
+void
+fordiv(GEN a, GEN code)
+{
+  long i, l;
+  pari_sp av2, av = avma;
+  GEN t = divisors(a);
+
+  push_lex(gen_0,code); l=lg(t); av2 = avma;
+  for (i=1; i<l; i++)
+  {
+    set_lex(-1,gel(t,i));
+    closure_evalvoid(code); if (loop_break()) break;
+    avma = av2;
+  }
+  pop_lex(1); avma=av;
+}
+
+/* Embedded for loops:
+ *   fl = 0: execute ch (a), where a = (ai) runs through all n-uplets in
+ *     [m1,M1] x ... x [mn,Mn]
+ *   fl = 1: impose a1 <= ... <= an
+ *   fl = 2:        a1 <  ... <  an
+ */
+/* increment and return d->a [over integers]*/
+static GEN
+_next_i(forvec_t *d)
+{
+  long i = d->n;
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  for (;;) {
+    if (cmpii(d->a[i], d->M[i]) < 0) {
+      d->a[i] = incloop(d->a[i]);
+      return (GEN)d->a;
+    }
+    d->a[i] = resetloop(d->a[i], d->m[i]);
+    if (--i <= 0) return NULL;
+  }
+}
+/* increment and return d->a [generic]*/
+static GEN
+_next(forvec_t *d)
+{
+  long i = d->n;
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  for (;;) {
+    d->a[i] = gaddgs(d->a[i], 1);
+    if (gcmp(d->a[i], d->M[i]) <= 0) return (GEN)d->a;
+    d->a[i] = d->m[i];
+    if (--i <= 0) return NULL;
+  }
+}
+
+/* non-decreasing order [over integers] */
+static GEN
+_next_le_i(forvec_t *d)
+{
+  long i = d->n;
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  for (;;) {
+    if (cmpii(d->a[i], d->M[i]) < 0)
+    {
+      d->a[i] = incloop(d->a[i]);
+      /* m[i] < a[i] <= M[i] < M[i+1] */
+      while (i < d->n)
+      {
+        GEN t;
+        i++;
+        if (cmpii(d->a[i-1], d->a[i]) <= 0) continue;
+        /* a[i-1] <= M[i-1] <= M[i] */
+        t = d->a[i-1]; if (cmpii(t, d->m[i]) < 0) t = d->m[i];
+        d->a[i] = resetloop(d->a[i], t);/*a[i]:=max(a[i-1],m[i])*/
+      }
+      return (GEN)d->a;
+    }
+    d->a[i] = resetloop(d->a[i], d->m[i]);
+    if (--i <= 0) return NULL;
+  }
+}
+/* non-decreasing order [generic] */
+static GEN
+_next_le(forvec_t *d)
+{
+  long i = d->n, imin = d->n;
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  for (;;) {
+    d->a[i] = gaddgs(d->a[i], 1);
+    if (gcmp(d->a[i], d->M[i]) <= 0)
+    {
+      while (i < d->n)
+      {
+        i++;
+        if (gcmp(d->a[i-1], d->a[i]) <= 0) continue;
+        while (gcmp(d->a[i-1], d->M[i]) > 0)
+        {
+          i = imin - 1; if (!i) return NULL;
+          imin = i;
+          d->a[i] = gaddgs(d->a[i], 1);
+          if (gcmp(d->a[i], d->M[i]) <= 0) break;
+        }
+        if (i > 1) { /* a >= a[i-1] - a[i] */
+          GEN a = gceil(gsub(d->a[i-1], d->a[i]));
+          d->a[i] = gadd(d->a[i], a);
+        }
+      }
+      return (GEN)d->a;
+    }
+    d->a[i] = d->m[i];
+    if (--i <= 0) return NULL;
+    if (i < imin) imin = i;
+  }
+}
+/* strictly increasing order [over integers] */
+static GEN
+_next_lt_i(forvec_t *d)
+{
+  long i = d->n;
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  for (;;) {
+    if (cmpii(d->a[i], d->M[i]) < 0)
+    {
+      d->a[i] = incloop(d->a[i]);
+      /* m[i] < a[i] <= M[i] < M[i+1] */
+      while (i < d->n)
+      {
+        pari_sp av;
+        GEN t;
+        i++;
+        if (cmpii(d->a[i-1], d->a[i]) < 0) continue;
+        av = avma;
+        /* a[i-1] <= M[i-1] < M[i] */
+        t = addis(d->a[i-1],1); if (cmpii(t, d->m[i]) < 0) t = d->m[i];
+        d->a[i] = resetloop(d->a[i], t);/*a[i]:=max(a[i-1]+1,m[i]) <= M[i]*/
+        avma = av;
+      }
+      return (GEN)d->a;
+    }
+    d->a[i] = resetloop(d->a[i], d->m[i]);
+    if (--i <= 0) return NULL;
+  }
+}
+/* strictly increasing order [generic] */
+static GEN
+_next_lt(forvec_t *d)
+{
+  long i = d->n, imin = d->n;
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  for (;;) {
+    d->a[i] = gaddgs(d->a[i], 1);
+    if (gcmp(d->a[i], d->M[i]) <= 0)
+    {
+      while (i < d->n)
+      {
+        i++;
+        if (gcmp(d->a[i-1], d->a[i]) < 0) continue;
+        for(;;)
+        {
+          GEN a, b;
+          a = addis(gfloor(gsub(d->a[i-1], d->a[i])), 1); /* a> v[i-1]-v[i] */
+          b = gadd(d->a[i], a);
+          /* v[i-1] < b <= v[i-1] + 1 */
+          if (gcmp(b, d->M[i]) <= 0) { d->a[i] = b; break; }
+
+          for (; i >= imin; i--) d->a[i] = d->m[i];
+          if (!i) return NULL;
+          imin = i;
+          d->a[i] = gaddgs(d->a[i], 1);
+          if (gcmp(d->a[i], d->M[i]) <= 0) break;
+        }
+      }
+      return (GEN)d->a;
+    }
+    d->a[i] = d->m[i];
+    if (--i <= 0) return NULL;
+    if (i < imin) imin = i;
+  }
+}
+/* for forvec(v=[],) */
+static GEN
+_next_void(forvec_t *d)
+{
+  if (d->first) { d->first = 0; return (GEN)d->a; }
+  return NULL;
+}
+
+/* Initialize minima (m) and maxima (M); guarantee
+ *   if flag = 1: m[i-1] <= m[i] <= M[i] <= M[i+1]
+ *   if flag = 2: m[i-1] <  m[i] <= M[i] <  M[i+1] */
+int
+forvec_init(forvec_t *d, GEN x, long flag)
+{
+  long i, tx = typ(x), l = lg(x), t = t_INT;
+  if (!is_vec_t(tx)) pari_err_TYPE("forvec [not a vector]", x);
+  d->first = 1;
+  d->n = l - 1;
+  d->a = (GEN*)cgetg(l,tx);
+  d->m = (GEN*)cgetg(l,tx);
+  d->M = (GEN*)cgetg(l,tx);
+  if (l == 1) { d->next = &_next_void; return 1; }
+  for (i = 1; i < l; i++)
+  {
+    GEN a, e = gel(x,i), m = gel(e,1), M = gel(e,2);
+    tx = typ(e);
+    if (! is_vec_t(tx) || lg(e)!=3)
+      pari_err_TYPE("forvec [expected vector not of type [min,MAX]]",e);
+    if (typ(m) != t_INT) t = t_REAL;
+    if (i > 1) switch(flag)
+    {
+      case 1: /* a >= m[i-1] - m */
+        a = gceil(gsub(d->m[i-1], m));
+        if (typ(a) != t_INT) pari_err_TYPE("forvec",a);
+        if (signe(a) > 0) m = gadd(m, a); else m = gcopy(m);
+        break;
+      case 2: /* a > m[i-1] - m */
+        a = gfloor(gsub(d->m[i-1], m));
+        if (typ(a) != t_INT) pari_err_TYPE("forvec",a);
+        a = addis(a, 1);
+        if (signe(a) > 0) m = gadd(m, a); else m = gcopy(m);
+        break;
+      default: m = gcopy(m);
+        break;
+    }
+    if (gcmp(m,M) > 0) { d->a = NULL; d->next = &_next; return 0; }
+    d->m[i] = m;
+    d->M[i] = M;
+  }
+  for (i = l-2; i >= 1; i--)
+  {
+    GEN a, M = d->M[i];
+    switch(flag) {
+      case 1:/* a >= M - M[i] */
+        a = gfloor(gsub(d->M[i+1], M));
+        if (typ(a) != t_INT) pari_err_TYPE("forvec",a);
+        if (signe(a) < 0) M = gadd(M, a); else M = gcopy(M);
+        /* M <= M[i+1] */
+        break;
+      case 2:
+        a = gceil(gsub(d->M[i+1], M));
+        if (typ(a) != t_INT) pari_err_TYPE("forvec",a);
+        a = subis(a, 1);
+        if (signe(a) < 0) M = gadd(M, a); else M = gcopy(M);
+        /* M < M[i+1] */
+        break;
+      default:
+        M = gcopy(M);
+        break;
+    }
+    d->M[i] = M;
+  }
+  if (t == t_INT) {
+    for (i = 1; i < l; i++) {
+      d->a[i] = setloop(d->m[i]);
+      if (typ(d->M[i]) != t_INT) d->M[i] = gfloor(d->M[i]);
+    }
+  } else {
+    for (i = 1; i < l; i++) d->a[i] = d->m[i];
+  }
+  switch(flag)
+  {
+    case 0: d->next = t==t_INT? &_next_i:    &_next; break;
+    case 1: d->next = t==t_INT? &_next_le_i: &_next_le; break;
+    case 2: d->next = t==t_INT? &_next_lt_i: &_next_lt; break;
+    default: pari_err_FLAG("forvec");
+  }
+  return 1;
+}
+GEN
+forvec_next(forvec_t *d) { return d->next(d); }
+
+void
+forvec(GEN x, GEN code, long flag)
+{
+  pari_sp av = avma;
+  forvec_t T;
+  GEN v;
+  if (!forvec_init(&T, x, flag)) { avma = av; return; }
+  push_lex((GEN)T.a, code);
+  while ((v = forvec_next(&T)))
+  {
+    closure_evalvoid(code);
+    if (loop_break()) break;
+  }
+  pop_lex(1); avma = av;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                              SUMS                              **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+somme(GEN a, GEN b, GEN code, GEN x)
+{
+  pari_sp av, av0 = avma, lim;
+  GEN p1;
+
+  if (typ(a) != t_INT) pari_err_TYPE("sum",a);
+  if (!x) x = gen_0;
+  if (gcmp(b,a) < 0) return gcopy(x);
+
+  b = gfloor(b);
+  a = setloop(a);
+  av=avma; lim = stack_lim(av,1);
+  push_lex(a,code);
+  for(;;)
+  {
+    p1 = closure_evalnobrk(code);
+    x=gadd(x,p1); if (cmpii(a,b) >= 0) break;
+    a = incloop(a);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"sum");
+      x = gerepileupto(av,x);
+    }
+    set_lex(-1,a);
+  }
+  pop_lex(1); return gerepileupto(av0,x);
+}
+
+GEN
+suminf(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  long fl, G;
+  pari_sp av0 = avma, av, lim;
+  GEN p1,x = real_1(prec);
+
+  if (typ(a) != t_INT) pari_err_TYPE("suminf",a);
+  a = setloop(a);
+  av = avma; lim = stack_lim(av,1);
+  fl=0; G = prec2nbits(prec) + 5;
+  for(;;)
+  {
+    p1 = eval(E, a); x = gadd(x,p1); a = incloop(a);
+    if (gequal0(p1) || gexpo(p1) <= gexpo(x)-G)
+      { if (++fl==3) break; }
+    else
+      fl=0;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"suminf");
+      x = gerepileupto(av,x);
+    }
+  }
+  return gerepileupto(av0, gaddgs(x,-1));
+}
+GEN
+suminf0(GEN a, GEN code, long prec)
+{ EXPR_WRAP(code, suminf(EXPR_ARG, a, prec)); }
+
+GEN
+sumdivexpr(GEN num, GEN code)
+{
+  pari_sp av = avma;
+  GEN y = gen_0, t = divisors(num);
+  long i, l = lg(t);
+
+  push_lex(gen_0, code);
+  for (i=1; i<l; i++)
+  {
+    set_lex(-1,gel(t,i));
+    y = gadd(y, closure_evalnobrk(code));
+  }
+  pop_lex(1); return gerepileupto(av,y);
+}
+GEN
+sumdivmultexpr(GEN num, GEN code)
+{
+  pari_sp av = avma;
+  GEN y = gen_1, P,E;
+  int isint = divisors_init(num, &P,&E);
+  long i, l = lg(P);
+
+  if (l == 1) { avma = av; return gen_1; }
+  push_lex(gen_0, code);
+  for (i=1; i<l; i++)
+  {
+    GEN p = gel(P,i), q = p, z = gen_1;
+    long j, e = E[i];
+    for (j = 1; j <= e; j++, q = isint?mulii(q, p): gmul(q,p))
+    {
+      set_lex(-1, q);
+      z = gadd(z, closure_evalnobrk(code));
+      if (j == e) break;
+    }
+    y = gmul(y, z);
+  }
+  pop_lex(1); return gerepileupto(av,y);
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                           PRODUCTS                             **/
+/**                                                                **/
+/********************************************************************/
+
+GEN
+produit(GEN a, GEN b, GEN code, GEN x)
+{
+  pari_sp av, av0 = avma, lim;
+  GEN p1;
+
+  if (typ(a) != t_INT) pari_err_TYPE("prod",a);
+  if (!x) x = gen_1;
+  if (gcmp(b,a) < 0) return gcopy(x);
+
+  b = gfloor(b);
+  a = setloop(a);
+  av=avma; lim = stack_lim(av,1);
+  push_lex(a,code);
+  for(;;)
+  {
+    p1 = closure_evalnobrk(code);
+    x = gmul(x,p1); if (cmpii(a,b) >= 0) break;
+    a = incloop(a);
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"prod");
+      x = gerepileupto(av,x);
+    }
+    set_lex(-1,a);
+  }
+  pop_lex(1); return gerepileupto(av0,x);
+}
+
+GEN
+prodinf(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  pari_sp av0 = avma, av, lim;
+  long fl,G;
+  GEN p1,x = real_1(prec);
+
+  if (typ(a) != t_INT) pari_err_TYPE("prodinf",a);
+  a = setloop(a);
+  av = avma; lim = stack_lim(av,1);
+  fl=0; G = -prec2nbits(prec)-5;
+  for(;;)
+  {
+    p1 = eval(E, a); if (gequal0(p1)) { x = p1; break; }
+    x = gmul(x,p1); a = incloop(a);
+    p1 = gsubgs(p1, 1);
+    if (gequal0(p1) || gexpo(p1) <= G) { if (++fl==3) break; } else fl=0;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"prodinf");
+      x = gerepileupto(av,x);
+    }
+  }
+  return gerepilecopy(av0,x);
+}
+GEN
+prodinf1(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  pari_sp av0 = avma, av, lim;
+  long fl,G;
+  GEN p1,p2,x = real_1(prec);
+
+  if (typ(a) != t_INT) pari_err_TYPE("prodinf1",a);
+  a = setloop(a);
+  av = avma; lim = stack_lim(av,1);
+  fl=0; G = -prec2nbits(prec)-5;
+  for(;;)
+  {
+    p2 = eval(E, a); p1 = gaddgs(p2,1);
+    if (gequal0(p1)) { x = p1; break; }
+    x = gmul(x,p1); a = incloop(a);
+    if (gequal0(p2) || gexpo(p2) <= G) { if (++fl==3) break; } else fl=0;
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"prodinf1");
+      x = gerepileupto(av,x);
+    }
+  }
+  return gerepilecopy(av0,x);
+}
+GEN
+prodinf0(GEN a, GEN code, long flag, long prec)
+{
+  switch(flag)
+  {
+    case 0: EXPR_WRAP(code, prodinf (EXPR_ARG, a, prec));
+    case 1: EXPR_WRAP(code, prodinf1(EXPR_ARG, a, prec));
+  }
+  pari_err_FLAG("prodinf");
+  return NULL; /* not reached */
+}
+
+GEN
+prodeuler(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long prec)
+{
+  pari_sp av, av0 = avma, lim;
+  GEN x = real_1(prec), prime;
+  forprime_t T;
+
+  av = avma;
+  if (!forprime_init(&T, a,b)) { avma = av; return x; }
+
+  av = avma;
+  lim = stack_lim(avma,1);
+  while ( (prime = forprime_next(&T)) )
+  {
+    x = gmul(x, eval(E, prime));
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"prodeuler");
+      x = gerepilecopy(av, x);
+    }
+  }
+  return gerepilecopy(av0,x);
+}
+GEN
+prodeuler0(GEN a, GEN b, GEN code, long prec)
+{ EXPR_WRAP(code, prodeuler(EXPR_ARG, a, b, prec)); }
+
+static void
+err_direuler(GEN x)
+{ pari_err_DOMAIN("direuler","constant term","!=", gen_1,x); }
+
+GEN
+direuler(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, GEN c)
+{
+  ulong i, k, n;
+  pari_sp av0 = avma, av, lim = stack_lim(av0, 1);
+  long j, tx, lx;
+  GEN x, y, s, polnum, polden, prime;
+  forprime_t T;
+
+  if (c)
+  {
+    if (typ(c) != t_INT)
+    {
+      c = gfloor(c);
+      if (typ(c) != t_INT) pari_err_TYPE("direuler", c);
+    }
+    if (signe(c) <= 0) { avma = av0; return mkvec(gen_1); }
+    n = itou(c);
+    if (cmpui(n, b) < 0) b = c;
+  }
+  if (!forprime_init(&T, a,b)) { avma = av0; return mkvec(gen_1); }
+
+  if (c)
+  {
+    n = itou(c);
+    if (cmpui(n, b) < 0) b = c;
+  }
+  else
+  {
+    if (lgefint(b) > 3) pari_err_OVERFLOW("direuler");
+    n = itou(b);
+  }
+
+  y = cgetg(n+1,t_VEC); av = avma;
+  x = zerovec(n); gel(x,1) = gen_1;
+  while ( (prime = forprime_next(&T)) )
+  {
+    ulong p = prime[2];
+    s = eval(E, prime);
+    polnum = numer(s);
+    polden = denom(s);
+    tx = typ(polnum);
+    if (is_scalar_t(tx))
+    {
+      if (!gequal1(polnum))
+      {
+        if (!gequalm1(polnum)) err_direuler(polnum);
+        polden = gneg(polden);
+      }
+    }
+    else
+    {
+      ulong k1, q, qlim;
+      if (tx != t_POL) pari_err_TYPE("direuler",polnum);
+      lx = degpol(polnum);
+      if (lx < 0) err_direuler(polnum);
+      c = gel(polnum,2);
+      if (!gequal1(c))
+      {
+        if (!gequalm1(c)) err_direuler(polnum);
+        polnum = gneg(polnum);
+        polden = gneg(polden);
+      }
+      for (i=1; i<=n; i++) gel(y,i) = gel(x,i);
+      q = p; qlim = n/p;
+      for (j = 1; q<=n && j<=lx; j++)
+      {
+        c = gel(polnum,j+2);
+        if (!gequal0(c))
+          for (k=1,k1=q; k1<=n; k1+=q,k++)
+            gel(x,k1) = gadd(gel(x,k1), gmul(c,gel(y,k)));
+        if (q > qlim) break;
+        q *= p;
+      }
+    }
+    tx = typ(polden);
+    if (is_scalar_t(tx))
+    {
+      if (!gequal1(polden))
+        pari_err_DOMAIN("direuler","constant term", "!=", gen_1,polden);
+    }
+    else
+    {
+      if (tx != t_POL) pari_err_TYPE("direuler",polden);
+      c = gel(polden,2);
+      if (!gequal1(c))
+        pari_err_DOMAIN("direuler","constant term", "!=", gen_1,polden);
+      lx = degpol(polden);
+      for (i=p; i<=n; i+=p)
+      {
+        s = gen_0; k = i;
+        for (j = 1; !(k%p) && j<=lx; j++)
+        {
+          c = gel(polden,j+2); k /= p;
+          if (!gequal0(c)) s = gadd(s, gmul(c,gel(x,k)));
+        }
+        gel(x,i) = gsub(gel(x,i),s);
+      }
+    }
+    if (low_stack(lim, stack_lim(av,1)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"direuler");
+      x = gerepilecopy(av, x);
+    }
+  }
+  return gerepilecopy(av0,x);
+}
+GEN
+direuler0(GEN a, GEN b, GEN code, GEN c)
+{ EXPR_WRAP(code, direuler(EXPR_ARG, a, b, c)); }
+
+/********************************************************************/
+/**                                                                **/
+/**                       VECTORS & MATRICES                       **/
+/**                                                                **/
+/********************************************************************/
+
+INLINE GEN
+copyupto(GEN z, GEN t)
+{
+  if (is_universal_constant(z) || (z>(GEN)bot && z<=t))
+    return z;
+  else
+    return gcopy(z);
+}
+
+GEN
+vecexpr0(GEN vec, GEN code, GEN pred)
+{
+  switch(typ(vec))
+  {
+    case t_LIST:
+    {
+      vec = list_data(vec);
+      if (!vec) return cgetg(1, t_VEC);
+      break;
+    }
+    case t_VEC: case t_COL: case t_MAT: break;
+    default: pari_err_TYPE("[_|_<-_,_]",vec);
+  }
+  if (pred && code)
+    EXPR_WRAP(code,vecselapply((void*)pred,&gp_evalbool,EXPR_ARGUPTO,vec))
+  else if (code)
+    EXPR_WRAP(code,vecapply(EXPR_ARGUPTO,vec))
+  else
+    EXPR_WRAP(pred,vecselect(EXPR_ARGBOOL,vec))
+}
+
+GEN
+vecexpr1(GEN vec, GEN code, GEN pred)
+{
+  GEN v = vecexpr0(vec, code, pred);
+  return lg(v) == 1? v: shallowconcat1(v);
+}
+
+GEN
+vecteur(GEN nmax, GEN code)
+{
+  GEN y,p1;
+  long i,m;
+  GEN c=utoipos(1);
+
+  m = gtos(nmax);
+  if (m < 0)  pari_err_DOMAIN("vector", "dimension", "<", gen_0, stoi(m));
+  if (!code) return zerovec(m);
+  y = cgetg(m+1,t_VEC); push_lex(c, code);
+  for (i=1; i<=m; i++)
+  {
+    c[2] = i; p1 = closure_evalnobrk(code);
+    gel(y,i) = copyupto(p1, y);
+    set_lex(-1,c);
+  }
+  pop_lex(1); return y;
+}
+
+GEN
+vecteursmall(GEN nmax, GEN code)
+{
+  GEN y;
+  long i,m;
+  GEN c=utoipos(1);
+
+  m = gtos(nmax);
+  if (m < 0)  pari_err_DOMAIN("vectorsmall", "dimension", "<", gen_0, stoi(m));
+  if (!code) return zero_zv(m);
+  y = cgetg(m+1,t_VECSMALL); push_lex(c,code);
+  for (i=1; i<=m; i++)
+  {
+    c[2] = i;
+    y[i] = gtos(closure_evalnobrk(code));
+    set_lex(-1,c);
+  }
+  pop_lex(1); return y;
+}
+
+GEN
+vvecteur(GEN nmax, GEN n)
+{
+  GEN y = vecteur(nmax,n);
+  settyp(y,t_COL); return y;
+}
+
+GEN
+matrice(GEN nlig, GEN ncol, GEN code)
+{
+  GEN y, z, p1;
+  long i, j, m, n;
+  GEN c1 = utoipos(1), c2 = utoipos(1);
+
+  m = gtos(ncol);
+  n = gtos(nlig);
+  if (m < 0)  pari_err_DOMAIN("matrix", "nbcols", "<", gen_0, stoi(m));
+  if (n < 0)  pari_err_DOMAIN("matrix", "nbrows", "<", gen_0, stoi(n));
+  if (!m) return cgetg(1,t_MAT);
+  if (!code || !n) return zeromatcopy(n, m);
+  push_lex(c1,code);
+  push_lex(c2,NULL); y = cgetg(m+1,t_MAT);
+  for (i=1; i<=m; i++)
+  {
+    c2[2] = i; z = cgetg(n+1,t_COL); gel(y,i) = z;
+    for (j=1; j<=n; j++)
+    {
+      c1[2] = j; p1 = closure_evalnobrk(code);
+      gel(z,j) = copyupto(p1, y);
+      set_lex(-2,c1);
+      set_lex(-1,c2);
+    }
+  }
+  pop_lex(2); return y;
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                         SUMMING SERIES                         **/
+/**                                                                **/
+/********************************************************************/
+GEN
+polzag(long n, long m)
+{
+  pari_sp av = avma;
+  long k, d = n - m;
+  GEN A, Bx, g, s;
+
+  if (d <= 0 || m < 0) return gen_0;
+  A  = mkpoln(2, stoi(-2), gen_1); /* 1 - 2x */
+  Bx = mkpoln(3, stoi(-2), gen_2, gen_0); /* 2x - 2x^2 */
+  g = gmul(poleval(ZX_deriv(polchebyshev1(d,0)), A), gpowgs(Bx, (m+1)>>1));
+  for (k = m; k >= 0; k--)
+    g = (k&1)? ZX_deriv(g): gadd(gmul(A,g), gmul(Bx,ZX_deriv(g)));
+  s = mulii(sqru(d), mpfact(m+1));
+  return gerepileupto(av, gdiv(g,s));
+}
+
+#ifdef _MSC_VER /* Bill Daly: work around a MSVC bug */
+#pragma optimize("g",off)
+#endif
+static GEN
+polzagreel(long n, long m, long prec)
+{
+  const long d = n - m, d2 = d<<1, r = (m+1)>>1;
+  long j, k, k2;
+  pari_sp av = avma;
+  GEN Bx, g, h, v, b, s;
+
+  if (d <= 0 || m < 0) return gen_0;
+  Bx = mkpoln(3, gen_1, gen_1, gen_0); /* x + x^2 */
+  v = cgetg(d+1,t_VEC);
+  g = cgetg(d+1,t_VEC);
+  gel(v,d) = gen_1; b = stor(d2, prec);
+  gel(g,d) = b;
+  for (k = 1; k < d; k++)
+  {
+    gel(v,d-k) = gen_1;
+    for (j=1; j<k; j++)
+      gel(v,d-k+j) = addii(gel(v,d-k+j), gel(v,d-k+j+1));
+    /* v[d-k+j] = binom(k, j), j = 0..k */
+    k2 = k+k; b = divri(mulri(b,mulss(d2-k2+1,d2-k2)), mulss(k2,k2+1));
+    for (j=1; j<=k; j++)
+      gel(g,d-k+j) = mpadd(gel(g,d-k+j), mulri(b,gel(v,d-k+j)));
+    gel(g,d-k) = b;
+  }
+  g = gmul(RgV_to_RgX(g,0), gpowgs(Bx,r));
+  for (j=0; j<=r; j++)
+  {
+    if (j) g = RgX_deriv(g);
+    if (j || !(m&1))
+    {
+      h = cgetg(n+3,t_POL);
+      h[1] = evalsigne(1);
+      gel(h,2) = gel(g,2);
+      for (k=1; k<n; k++)
+        gel(h,k+2) = gadd(gmulsg(k+k+1,gel(g,k+2)), gmulsg(k<<1,gel(g,k+1)));
+      gel(h,n+2) = gmulsg(n<<1, gel(g,n+1));
+      g = h;
+    }
+  }
+  g = gmul2n(g, r-1);
+  s = mului(d, mpfact(m+1));
+  return gerepileupto(av, gdiv(g,s));
+}
+
+#ifdef _MSC_VER
+#pragma optimize("g",on)
+#endif
+GEN
+sumalt(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  ulong k, N;
+  pari_sp av = avma, av2, lim;
+  GEN s, az, c, e1, d;
+
+  if (typ(a) != t_INT) pari_err_TYPE("sumalt",a);
+  e1 = addsr(3, sqrtr(stor(8,prec)));
+  N = (ulong)(0.4*(prec2nbits(prec)+ 7));
+  d = powru(e1,N);
+  d = shiftr(addrr(d, invr(d)),-1);
+  a = setloop(a);
+  az = gen_m1; c = d;
+  s = gen_0;
+  av2 = avma; lim = stack_lim(av,4);
+  for (k=0; ; k++) /* k < N */
+  {
+    c = addir(az,c); s = gadd(s, gmul(c, eval(E, a)));
+    if (k==N-1) break;
+    az = diviuuexact(muluui((N-k)<<1,N+k,az), k+1, (k<<1)+1);
+    a = incloop(a); /* in place! */
+    if (low_stack(lim, stack_lim(av,4)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"sumalt, k = %ld/%ld", k,N-1);
+      gerepileall(av2, 3, &az,&c,&s);
+    }
+  }
+  return gerepileupto(av, gdiv(s,d));
+}
+
+GEN
+sumalt2(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  long k, N;
+  pari_sp av = avma, av2, lim;
+  GEN s, dn, pol;
+
+  if (typ(a) != t_INT) pari_err_TYPE("sumalt",a);
+  N = (long)(0.31*(prec2nbits(prec) + 5));
+  pol = polzagreel(N,N>>1,prec+EXTRAPRECWORD);
+  pol = RgX_div_by_X_x(pol, gen_1, &dn);
+  a = setloop(a);
+  N = degpol(pol);
+  s = gen_0;
+  av2 = avma; lim = stack_lim(av,4);
+  for (k=0; k<=N; k++)
+  {
+    s = gadd(s, gmul(gel(pol,k+2), eval(E, a)));
+    if (k == N) break;
+    a = incloop(a); /* in place! */
+    if (low_stack(lim, stack_lim(av,4)))
+    {
+      if (DEBUGMEM>1) pari_warn(warnmem,"sumalt2, k = %ld/%ld", k,N-1);
+      s = gerepileupto(av2, s);
+    }
+  }
+  return gerepileupto(av, gdiv(s,dn));
+}
+
+GEN
+sumalt0(GEN a, GEN code, long flag, long prec)
+{
+  switch(flag)
+  {
+    case 0: EXPR_WRAP(code, sumalt (EXPR_ARG,a,prec));
+    case 1: EXPR_WRAP(code, sumalt2(EXPR_ARG,a,prec));
+    default: pari_err_FLAG("sumalt");
+  }
+  return NULL; /* not reached */
+}
+
+GEN
+sumpos(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  long k, kk, N, G;
+  pari_sp av = avma;
+  GEN r, reel, s, az, c, x, e1, d, *stock;
+
+  if (typ(a) != t_INT) pari_err_TYPE("sumpos",a);
+  a = subis(a,1); reel = cgetr(prec);
+  e1 = addsr(3, sqrtr(stor(8,prec)));
+  N = (long)(0.4*(prec2nbits(prec) + 7));
+  d = powru(e1,N);
+  d = shiftr(addrr(d, invr(d)),-1);
+  az = gen_m1; c = d;
+  s = gen_0;
+
+  G = -prec2nbits(prec) - 5;
+  stock = (GEN*)new_chunk(N+1); for (k=1; k<=N; k++) stock[k] = NULL;
+  for (k=0; k<N; k++)
+  {
+    if (odd(k) && stock[k]) x = stock[k];
+    else
+    {
+      pari_sp av2 = avma;
+      x = gen_0; r = utoipos(2*k+2);
+      for(kk=0;;kk++)
+      {
+
+        long ex;
+        affgr(eval(E, addii(r,a)), reel);
+        if (!signe(reel)) break;
+        ex = expo(reel) + kk; shiftr_inplace(reel, kk);
+        x = mpadd(x,reel); if (kk && ex < G) break;
+        r = shifti(r,1);
+      }
+      x = gerepileupto(av2, x);
+      if (2*k < N) stock[2*k+1] = x;
+      affgr(eval(E, addsi(k+1,a)), reel);
+      x = addrr(reel, gmul2n(x,1));
+    }
+    c = addir(az,c);
+    s = mpadd(s, mulrr(x, k&1? negr(c): c));
+    az = diviiexact(mulii(mulss(N-k,N+k),shifti(az,1)),mulss(k+1,k+k+1));
+  }
+  return gerepileupto(av, gdiv(s,d));
+}
+
+GEN
+sumpos2(void *E, GEN (*eval)(void *, GEN), GEN a, long prec)
+{
+  long k, kk, N, G;
+  pari_sp av = avma;
+  GEN r, reel, s, pol, dn, x, *stock;
+
+  if (typ(a) != t_INT) pari_err_TYPE("sumpos2",a);
+  a = subis(a,1); reel = cgetr(prec);
+  N = (long)(0.31*(prec2nbits(prec) + 5));
+
+  G = -prec2nbits(prec) - 5;
+  stock = (GEN*)new_chunk(N+1); for (k=1; k<=N; k++) stock[k] = NULL;
+  for (k=1; k<=N; k++)
+    if (odd(k) || !stock[k])
+    {
+      pari_sp av2 = avma;
+      x = gen_0; r = utoipos(2*k);
+      for(kk=0;;kk++)
+      {
+        long ex;
+        affgr(eval(E, addii(r,a)), reel);
+        ex = expo(reel) + kk; shiftr_inplace(reel, kk);
+        x = mpadd(x,reel); if (kk && ex < G) break;
+        r = shifti(r,1);
+      }
+      x = gerepileupto(av2, x);
+      if (2*k-1 < N) stock[2*k] = x;
+      affgr(eval(E, addsi(k,a)), reel);
+      stock[k] = addrr(reel, gmul2n(x,1));
+    }
+  s = gen_0;
+  pol = polzagreel(N,N>>1,prec+EXTRAPRECWORD);
+  pol = RgX_div_by_X_x(pol, gen_1, &dn);
+  for (k=1; k<=lg(pol)-2; k++)
+  {
+    GEN p1 = gmul(gel(pol,k+1),stock[k]);
+    if (!odd(k)) p1 = gneg_i(p1);
+    s = gadd(s,p1);
+  }
+  return gerepileupto(av, gdiv(s,dn));
+}
+
+GEN
+sumpos0(GEN a, GEN code, long flag, long prec)
+{
+  switch(flag)
+  {
+    case 0: EXPR_WRAP(code, sumpos (EXPR_ARG,a,prec));
+    case 1: EXPR_WRAP(code, sumpos2(EXPR_ARG,a,prec));
+    default: pari_err_FLAG("sumpos");
+  }
+  return NULL; /* not reached */
+}
+
+/********************************************************************/
+/**                                                                **/
+/**            SEARCH FOR REAL ZEROS of an expression              **/
+/**                                                                **/
+/********************************************************************/
+/* Brent's method, [a,b] bracketing interval */
+GEN
+zbrent(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long prec)
+{
+  long sig, iter, itmax;
+  pari_sp av = avma;
+  GEN c, d, e, tol, fa, fb, fc;
+
+  a = gtofp(a,prec);
+  b = gtofp(b,prec); sig = cmprr(b,a);
+  if (!sig) return gerepileupto(av, a);
+  if (sig < 0) { c=a; a=b; b=c; } else c = b;
+
+  fa = eval(E, a);
+  fb = eval(E, b);
+  if (gsigne(fa)*gsigne(fb) > 0)
+    pari_err_DOMAIN("solve", "f(a)f(b)", ">", gen_0, mkvec2(fa,fb));
+  itmax = prec2nbits(prec) * 2 + 1;
+  tol = real2n(5-prec2nbits(prec), LOWDEFAULTPREC);
+  fc = fb;
+  e = d = NULL; /* gcc -Wall */
+  for (iter=1; iter<=itmax; iter++)
+  {
+    GEN xm, tol1;
+    if (gsigne(fb)*gsigne(fc) > 0)
+    {
+      c = a; fc = fa; e = d = subrr(b,a);
+    }
+    if (gcmp(gabs(fc,0),gabs(fb,0)) < 0)
+    {
+      a = b; b = c; c = a; fa = fb; fb = fc; fc = fa;
+    }
+    tol1 = absr_cmp(tol, b) > 0? sqrr(tol): mulrr(tol, absr(b));
+    xm = shiftr(subrr(c,b),-1);
+    if (absr_cmp(xm,tol1) <= 0 || gequal0(fb)) break; /* SUCCESS */
+
+    if (absr_cmp(e,tol1) >= 0 && gcmp(gabs(fa,0),gabs(fb,0)) > 0)
+    { /* attempt interpolation */
+      GEN min1, min2, p, q, s = gdiv(fb,fa);
+      if (cmprr(a,c)==0)
+      {
+        p = gmul2n(gmul(xm,s),1);
+        q = gsubsg(1,s);
+      }
+      else
+      {
+        GEN r = gdiv(fb,fc);
+        q = gdiv(fa,fc);
+        p = gmul2n(gmul(gsub(q,r),gmul(xm,q)),1);
+        p = gmul(s, gsub(p, gmul(gsub(b,a),gsubgs(r,1))));
+        q = gmul(gmul(gsubgs(q,1),gsubgs(r,1)),gsubgs(s,1));
+      }
+      if (gsigne(p) > 0) q = gneg_i(q); else p = gneg_i(p);
+      min1 = gsub(gmulsg(3,gmul(xm,q)), gabs(gmul(q,tol1),0));
+      min2 = gabs(gmul(e,q),0);
+      if (gcmp(gmul2n(p,1), gmin(min1,min2)) < 0)
+        { e = d; d = gdiv(p,q); } /* interpolation OK */
+      else
+        { d = xm; e = d; } /* failed, use bisection */
+    }
+    else { d = xm; e = d; } /* bound decreasing too slowly, use bisection */
+    a = b; fa = fb;
+    if (gcmp(gabs(d,0),tol1) > 0) b = gadd(b,d);
+    else if (gsigne(xm) > 0)      b = addrr(b,tol1);
+    else                          b = subrr(b,tol1);
+    fb = eval(E, b);
+  }
+  if (iter > itmax) pari_err_IMPL("solve recovery [too many iterations]");
+  return gerepileuptoleaf(av, rcopy(b));
+}
+
+GEN
+zbrent0(GEN a, GEN b, GEN code, long prec)
+{ EXPR_WRAP(code, zbrent(EXPR_ARG, a,b, prec)); }
+
+/* x = solve_start(&D, a, b, prec)
+ * while (x) {
+ *   y = ...(x);
+ *   x = solve_next(&D, y);
+ * }
+ * return D.res; */
+
+/********************************************************************/
+/**                                                                **/
+/**            Numerical derivation                                **/
+/**                                                                **/
+/********************************************************************/
+
+/* Rationale: (f(2^-e) - f(-2^-e) + O(2^-pr)) / (2 * 2^-e) = f'(0) + O(2^-2e)
+ * since 2nd derivatives cancel.
+ *   prec(LHS) = pr - e
+ *   prec(RHS) = 2e, equal when  pr = 3e = 3/2 fpr (fpr = required final prec)
+ *
+ * For f'(x), x far from 0: prec(LHS) = pr - e - expo(x)
+ * --> pr = 3/2 fpr + expo(x) */
+GEN
+derivnum(void *E, GEN (*eval)(void *, GEN), GEN x, long prec)
+{
+  GEN eps,a,b, y;
+  long pr, l, e, ex;
+  pari_sp av = avma;
+  long p = precision(x);
+  long fpr = p ? prec2nbits(p): prec2nbits(prec);
+  ex = gexpo(x);
+  if (ex < 0) ex = 0; /* near 0 */
+  pr = (long)ceil(fpr * 1.5 + ex);
+  l = nbits2prec(pr);
+  switch(typ(x))
+  {
+    case t_REAL:
+    case t_COMPLEX:
+      x = gprec_w(x, l + nbits2extraprec(ex + BITS_IN_LONG));
+  }
+
+  e = fpr/2; /* 1/2 required prec (in sig. bits) */
+  eps = real2n(-e, l);
+  a = eval(E, gsub(x, eps));
+  b = eval(E, gadd(x, eps));
+  y = gmul2n(gsub(b,a), e-1);
+  return gerepileupto(av, gprec_w(y, nbits2prec(fpr)));
+}
+
+GEN
+derivfun(void *E, GEN (*eval)(void *, GEN), GEN x, long prec)
+{
+  pari_sp av = avma;
+  long vx;
+  switch(typ(x))
+  {
+  case t_REAL: case t_INT: case t_FRAC: case t_COMPLEX:
+    return derivnum(E,eval, x, prec);
+  case t_POL:
+    x = RgX_to_ser(x, precdl+2+1); /* +1 because deriv reduce the precision by 1 */
+  case t_SER: /* FALL THROUGH */
+    vx = varn(x);
+    return gerepileupto(av, gdiv(deriv(eval(E, x),vx), deriv(x,vx)));
+  default: pari_err_TYPE("formal derivation",x);
+    return NULL; /*NOT REACHED*/
+  }
+}
+
+GEN
+derivnum0(GEN a, GEN code, long prec)
+{
+  EXPR_WRAP(code, derivfun (EXPR_ARG,a,prec));
+}
+
+struct deriv_data
+{
+  GEN code;
+  GEN args;
+};
+
+static GEN deriv_eval(void *E, GEN x)
+{
+ struct deriv_data *data=(struct deriv_data *)E;
+ gel(data->args,1)=x;
+ return closure_callgenvec(data->code, data->args);
+}
+
+GEN
+derivfun0(GEN code, GEN args, long prec)
+{
+  struct deriv_data E;
+  E.code=code; E.args=args;
+  return derivfun((void*)&E, deriv_eval, gel(args,1), prec);
+}
diff --git a/src/language/tree.h b/src/language/tree.h
new file mode 100644
index 0000000..77c1fc3
--- /dev/null
+++ b/src/language/tree.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+BEGINEXTERN
+
+typedef enum {Fseq,
+              Fmatrix,Frange,
+              Fassign,
+              Fmatcoeff,
+              Fmatrixelts,Fmatrixlines,
+              Fmat,Fvec,Fnoarg,
+              Flistarg,
+              Frefarg,
+              Fconst,Fsmall,
+              Ftag,
+              Fentry,Fcall,Ffunction,Flambda
+} Ffunc;
+
+#define Flastfunc  (Fdeffunc)
+#define FneedENTRY (Fconst)
+
+typedef struct node_s
+{
+  Ffunc f;           /*node function   */
+  long x;            /*node left child */
+  long y;            /*node right child*/
+  const char *str;   /*text start      */
+  size_t len;        /*text length     */
+  long flags;        /*flags from the copy optimizer*/
+} node;
+
+typedef enum {CSTstr, CSTquote, CSTint, CSTreal, CSTmember, CSTentry} CSTtype;
+
+typedef enum {OPor, OPand, OPid, OPeq, OPne, OPge, OPg, OPle, OPl, OPs, OPp, OPsl, OPsr, OPmod, OPdr, OPeuc, OPd, OPm, OPpow, OPcat, OPss, OPpp, OPse ,OPpe ,OPsle ,OPsre ,OPmode ,OPdre ,OPeuce ,OPde ,OPme, OPpl, OPn, OPnb, OPfact, OPderiv, OPtrans, OPrange, OPcompr, OPcomprc, OPhist, OPhisttime, OPlength, OPnboperator} OPerator;
+
+extern THREAD node *pari_tree;
+
+ENDEXTERN
diff --git a/src/modules/DedekZeta.c b/src/modules/DedekZeta.c
new file mode 100644
index 0000000..3f07da1
--- /dev/null
+++ b/src/modules/DedekZeta.c
@@ -0,0 +1,592 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/*******************************************************************/
+/*                                                                 */
+/*                     DEDEKIND ZETA FUNCTION                      */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+static GEN
+dirzetak0(GEN nf, ulong N)
+{
+  GEN vect, c, c2, T = nf_get_pol(nf), index = nf_get_index(nf);
+  pari_sp av = avma, av2;
+  const ulong SQRTN = (ulong)(sqrt(N) + 1e-3);
+  ulong i, p, lx;
+  long court[] = {evaltyp(t_INT)|_evallg(3), evalsigne(1)|evallgefint(3),0};
+  forprime_t S;
+
+  (void)evallg(N+1);
+  c  = cgetalloc(t_VECSMALL, N+1);
+  c2 = cgetalloc(t_VECSMALL, N+1);
+  c2[1] = c[1] = 1; for (i=2; i<=N; i++) c[i] = 0;
+  u_forprime_init(&S, 2, N);
+  av2 = avma;
+  while ( (p = u_forprime_next(&S)) )
+  {
+    avma = av2;
+    if (umodiu(index, p)) /* p does not divide index */
+    {
+      vect = gel(Flx_degfact(ZX_to_Flx(T,p), p),1);
+      lx = lg(vect);
+    }
+    else
+    {
+      GEN P;
+      court[2] = p; P = idealprimedec(nf,court);
+      lx = lg(P); vect = cgetg(lx,t_VECSMALL);
+      for (i=1; i<lx; i++) vect[i] = pr_get_f(gel(P,i));
+    }
+    if (p <= SQRTN)
+      for (i=1; i<lx; i++)
+      {
+        ulong qn, q = upowuu(p, vect[i]); /* Norm P[i] */
+        if (!q || q > N) break;
+        memcpy(c2 + 2, c + 2, (N-1)*sizeof(long));
+        /* c2[i] <- c[i] + sum_{k = 1}^{v_q(i)} c[i/q^k] for all i <= N */
+        for (qn = q; qn <= N; qn *= q)
+        {
+          ulong k0 = N/qn, k, k2; /* k2 = k*qn */
+          for (k = k0, k2 = k*qn; k > 0; k--, k2 -=qn) c2[k2] += c[k];
+          if (q > k0) break; /* <=> q*qn > N */
+        }
+        swap(c, c2);
+      }
+    else /* p > sqrt(N): simpler */
+      for (i=1; i<lx; i++)
+      {
+        ulong k, k2; /* k2 = k*p */
+        if (vect[i] > 1) break;
+        /* c2[i] <- c[i] + sum_{k = 1}^{v_q(i)} c[i/q^k] for all i <= N */
+        for (k = N/p, k2 = k*p; k > 0; k--, k2 -= p) c[k2] += c[k];
+      }
+  }
+  avma = av;
+  pari_free(c2); return c;
+}
+
+GEN
+dirzetak(GEN nf, GEN b)
+{
+  GEN z, c;
+  long n;
+
+  if (typ(b) != t_INT) pari_err_TYPE("dirzetak",b);
+  if (signe(b) <= 0) return cgetg(1,t_VEC);
+  nf = checknf(nf);
+  n = itou_or_0(b); if (!n) pari_err_OVERFLOW("dirzetak");
+  c = dirzetak0(nf, n);
+  z = vecsmall_to_vec(c); pari_free(c); return z;
+}
+
+/* return a t_REAL */
+GEN
+zeta_get_limx(long r1, long r2, long bit)
+{
+  pari_sp av = avma;
+  GEN p1, p2, c0, c1, A0;
+  long r = r1 + r2, N = r + r2;
+
+  /* c1 = N 2^(-2r2 / N) */
+  c1 = mulrs(powrfrac(real2n(1, DEFAULTPREC), -2*r2, N), N);
+
+  p1 = powru(Pi2n(1, DEFAULTPREC), r - 1);
+  p2 = mulir(powuu(N,r), p1); shiftr_inplace(p2, -r2);
+  c0 = sqrtr( divrr(p2, powru(c1, r+1)) );
+
+  A0 = logr_abs( gmul2n(c0, bit) ); p2 = divrr(A0, c1);
+  p1 = divrr(mulur(N*(r+1), logr_abs(p2)), addsr(2*(r+1), gmul2n(A0,2)));
+  return gerepileuptoleaf(av, divrr(addrs(p1, 1), powruhalf(p2, N)));
+}
+/* N_0 = floor( C_K / limx ). Large */
+long
+zeta_get_N0(GEN C,  GEN limx)
+{
+  long e;
+  pari_sp av = avma;
+  GEN z = gcvtoi(gdiv(C, limx), &e); /* avoid truncation error */
+  if (e >= 0 || is_bigint(z))
+    pari_err_OVERFLOW("zeta_get_N0 [need too many primes]");
+  if (DEBUGLEVEL>1) err_printf("\ninitzeta: N0 = %Ps\n", z);
+  avma = av; return itos(z);
+}
+
+/* even i such that limx^i ( (i\2)! )^r1 ( i! )^r2 ~ B. Small. */
+static long
+get_i0(long r1, long r2, GEN B, GEN limx)
+{
+  long imin = 1, imax = 1400;
+  while(imax - imin >= 4)
+  {
+    long i = (imax + imin) >> 1;
+    GEN t = powru(limx, i);
+    if (!r1)      t = mulrr(t, powru(mpfactr(i  , DEFAULTPREC), r2));
+    else if (!r2) t = mulrr(t, powru(mpfactr(i/2, DEFAULTPREC), r1));
+    else {
+      GEN u1 = mpfactr(i/2, DEFAULTPREC);
+      GEN u2 = mpfactr(i,   DEFAULTPREC);
+      if (r1 == r2) t = mulrr(t, powru(mulrr(u1,u2), r1));
+      else t = mulrr(t, mulrr(powru(u1,r1), powru(u2,r2)));
+    }
+    if (mpcmp(t, B) >= 0) imax = i; else imin = i;
+  }
+  return imax & ~1; /* make it even */
+}
+
+/* assume limx = zeta_get_limx(r1, r2, bit), a t_REAL */
+long
+zeta_get_i0(long r1, long r2, long bit, GEN limx)
+{
+  pari_sp av = avma;
+  GEN B = gmul(sqrtr( divrr(powrs(mppi(DEFAULTPREC), r2-3), limx) ),
+               gmul2n(powuu(5, r1), bit + r2));
+  long i0 = get_i0(r1, r2, B, limx);
+  if (DEBUGLEVEL>1) { err_printf("i0 = %ld\n",i0); err_flush(); }
+  avma = av; return i0;
+}
+
+/* sum(j=1, r-k+1, A[j] * B[j]), assumes k <= r and A[j],B[j] are 'mp' */
+INLINE GEN
+sumprod(GEN A, GEN B, long r, long k)
+{
+  GEN s = signe(gel(A,1))? mpmul(gel(A,1), gel(B,1)): gen_0;
+  long j;
+  for (j=2; j<=r-k+1; j++)
+    if (signe(gel(A,j))) s = mpadd(s, mpmul(gel(A,j), gel(B,j)));
+  return s;
+}
+
+GEN
+initzeta(GEN T, long prec)
+{
+  GEN znf, nf, bnf, gr2, gru, p1, p2, cst, coef;
+  GEN limx, resi,zet,C,coeflog,racpi,aij,tabj,colzero, tabcstn, tabcstni;
+  GEN c_even, ck_even, c_odd, ck_odd, serie_even, serie_odd, serie_exp;
+  GEN VOID;
+  long N0, i0, r1, r2, r, R, N, i, j, k, n, bit = prec2nbits(prec) + 6;
+  pari_timer ti;
+
+  pari_sp av, av2;
+  long court[] = {evaltyp(t_INT)|_evallg(3), evalsigne(1)|evallgefint(3),0};
+
+  /*************** residue & constants ***************/
+  T = get_bnfpol(T, &bnf, &nf);
+  if (!nf) {
+    bnf = Buchall(T, 0, prec);
+    nf = bnf_get_nf(bnf);
+  }
+  else if (!bnf) {
+    if (nf_get_prec(nf) < prec) nf = nfnewprec_shallow(nf, prec);
+    bnf = Buchall(nf, 0, prec);
+  } else if (nf_get_prec(bnf) < prec) {
+    bnf = bnfnewprec(bnf, prec);
+    nf = bnf_get_nf(bnf);
+  }
+
+  prec = precdbl(prec);
+  racpi = sqrtr(mppi(prec));
+  /* all the above left on stack for efficiency */
+
+  /* class number & regulator */
+  N = nf_get_degree(nf);
+  nf_get_sign(nf, &r1, &r2);
+  gr2 = gmael(nf,2,2);
+  r = r1 + r2; R = r+2;
+
+  znf = cgetg(10,t_VEC);
+  VOID = cgetg(1, t_STR); /* unused value */
+  av = avma;
+  resi = gerepileupto(av,
+           gdivgs(mpmul(shifti(bnf_get_no(bnf),r1), bnf_get_reg(bnf)),
+                  bnf_get_tuN(bnf))); /* hr 2^r1 / w*/
+  av = avma;
+  p1 = sqrtr_abs(itor(nf_get_disc(nf), prec));
+  p2 = gmul2n(powru(racpi,N), r2);
+  cst = gerepileuptoleaf(av, divrr(p1,p2));
+
+  /* N0, i0 */
+  limx = zeta_get_limx(r1, r2, bit);
+  N0 = zeta_get_N0(cst, limx);
+  i0 = zeta_get_i0(r1, r2, bit + 4, limx);
+
+  /* Array of i/cst (i=1..N0) */
+  av = avma;
+  tabcstn  = cgetg(N0+1,t_VEC);
+  tabcstni = cgetg(N0+1,t_VEC);
+  p1 = invr(cst);
+  for (i=1; i<=N0; i++) gel(tabcstni,i) = gel(tabcstn,i) = mulur(i,p1);
+  tabcstn  = gclone(tabcstn);
+  tabcstni = gclone(tabcstni);
+
+  /********** compute a(i,j) **********/
+  if (DEBUGLEVEL>1) timer_start(&ti);
+  zet = cgetg(R,t_VEC); gel(zet,1) = mpeuler(prec);
+  for (i=2; i<R; i++) gel(zet,i) = szeta(i, prec);
+
+  aij = cgetg(i0+1,t_VEC);
+  for (i=1; i<=i0; i++) gel(aij,i) = cgetg(R,t_VEC);
+
+  c_even = real2n(r1, prec);
+  c_odd = gmul(c_even, powru(racpi,r1));
+  if (r&1) c_odd = gneg_i(c_odd);
+  ck_even = cgetg(R,t_VEC); ck_odd = cgetg(r2+2,t_VEC);
+  for (k=1; k<R; k++)
+  {
+    GEN t = mulri(gel(zet,k), addis(shifti(gr2, k), r1));
+    shiftr_inplace(t, -k);
+    if (k&1) togglesign(t);
+    gel(ck_even,k) = t;
+  }
+  gru = utoipos(r);
+  for (k = 1; k <= r2+1; k++)
+  {
+    GEN t = mulri(gel(zet,k), subis(shifti(gru,k), r1));
+    shiftr_inplace(t, -k);
+    if (k&1) togglesign(t);
+    gel(ck_odd,k) = addsr(r, t);
+  }
+  if (r1) gel(ck_odd,1) = subrr(gel(ck_odd,1), mulur(r1, mplog2(prec)));
+  serie_even = cgetg(r+3,t_SER);
+  serie_odd = cgetg(r2+3,t_SER);
+  serie_even[1] = serie_odd[1] = evalsigne(1)|_evalvalp(1);
+  i = 0;
+  while (i < i0/2)
+  {
+    for (k=1; k<R; k++) gel(serie_even,k+1) = gdivgs(gel(ck_even,k),k);
+    serie_exp = gmul(c_even, gexp(serie_even,0));
+    p1 = gel(aij,2*i+1);
+    for (j=1; j<R; j++) p1[j] = serie_exp[r+3-j];
+
+    for (k=1; k<=r2+1; k++) gel(serie_odd,k+1) = gdivgs(gel(ck_odd,k),k);
+    serie_exp = gmul(c_odd, gexp(serie_odd,0));
+    p1 = gel(aij,2*i+2);
+    for (j=1; j<=r2+1; j++) p1[j] = serie_exp[r2+3-j];
+    for (   ; j<R; j++) gel(p1,j) = gen_0;
+    i++;
+
+    c_even = gdiv(c_even,gmul(powuu(i,r),powuu(2*i-1,r2)));
+    c_odd  = gdiv(c_odd, gmul(powuu(i,r2),powuu(2*i+1,r)));
+    c_even = gmul2n(c_even,-r2);
+    c_odd  = gmul2n(c_odd,r1-r2);
+    if (r1 & 1) { c_even = gneg_i(c_even); c_odd = gneg_i(c_odd); }
+    p1 = gr2; p2 = gru;
+    for (k=1; k<R; k++)
+    {
+      p1 = gdivgs(p1,2*i-1); p2 = gdivgs(p2,2*i);
+      gel(ck_even,k) = gadd(gel(ck_even,k), gadd(p1,p2));
+    }
+    p1 = gru; p2 = gr2;
+    for (k=1; k<=r2+1; k++)
+    {
+      p1 = gdivgs(p1,2*i+1); p2 = gdivgs(p2,2*i);
+      gel(ck_odd,k) = gadd(gel(ck_odd,k), gadd(p1,p2));
+    }
+  }
+  aij = gerepilecopy(av, aij);
+  if (DEBUGLEVEL>1) timer_printf(&ti,"a(i,j)");
+  gel(znf,1) = mkvecsmall2(r1,r2);
+  gel(znf,2) = resi;
+  gel(znf,5) = cst;
+  gel(znf,6) = logr_abs(cst);
+  gel(znf,7) = aij;
+
+  /************* Calcul du nombre d'ideaux de norme donnee *************/
+  coef = dirzetak0(nf,N0); tabj = cgetg(N0+1,t_MAT);
+  if (DEBUGLEVEL>1) timer_printf(&ti,"coef");
+  colzero = zerocol(r+1);
+  for (i=1; i<=N0; i++)
+    if (coef[i])
+    {
+      GEN t = cgetg(r+2,t_COL);
+      p1 = logr_abs(gel(tabcstn,i)); togglesign(p1);
+      gel(t,1) = utoi(coef[i]);
+      gel(t,2) = mulur(coef[i], p1);
+      for (j=2; j<=r; j++)
+      {
+        pari_sp av2 = avma;
+        gel(t,j+1) = gerepileuptoleaf(av2, divru(mulrr(gel(t,j), p1), j));
+      }
+      gel(tabj,i) = t; /* tabj[n,j] = coef(n)*ln(c/n)^(j-1)/(j-1)! */
+    }
+    else
+      gel(tabj,i) = colzero;
+  if (DEBUGLEVEL>1) timer_printf(&ti,"a(n)");
+
+  coeflog=cgetg(N0+1,t_VEC); gel(coeflog,1) = VOID;
+  for (i=2; i<=N0; i++)
+    if (coef[i])
+    {
+      court[2] = i; p1 = glog(court,prec);
+      setsigne(p1, -1); gel(coeflog,i) = p1;
+    }
+    else gel(coeflog,i) = VOID;
+  if (DEBUGLEVEL>1) timer_printf(&ti,"log(n)");
+
+  gel(znf,3) = tabj;
+  gel(znf,8) = vecsmall_copy(coef);
+  gel(znf,9) = coeflog;
+
+  /******************** Calcul des coefficients Cik ********************/
+  C = cgetg(r+1,t_MAT);
+  for (k=1; k<=r; k++) gel(C,k) = cgetg(i0+1,t_COL);
+  av2 = avma;
+  for (i=1; i<=i0; i++)
+  {
+    for (k=1; k<=r; k++)
+    {
+      GEN A = gel(aij,i) + k; /* A[j] = aij[i, j+k] */
+      GEN t = sumprod(A, gel(tabj,1), r, k);
+      /* n = 1 */
+      if (i > 1 && signe(t)) t = mpmul(t, gel(tabcstni,1));
+      for (n=2; n<=N0; n++)
+        if (coef[n])
+        { /* sum(j=1, r-k+1, aij[i,j+k] * tabj[n, j]) */
+          GEN s = sumprod(A, gel(tabj,n), r, k);
+          if (!signe(s)) continue;
+          if (i > 1) s = mpmul(s, gel(tabcstni,n));
+          t = mpadd(t,s);
+        }
+      togglesign(t);
+      gcoeff(C,i,k) = gerepileuptoleaf(av2,t);
+      av2 = avma;
+    }
+    if (i > 1 && i < i0) {
+      for (n=1; n<=N0; n++)
+        if (coef[n]) mpmulz(gel(tabcstni,n), gel(tabcstn,n), gel(tabcstni,n));
+    }
+  }
+  gel(znf,4) = C;
+  if (DEBUGLEVEL>1) timer_printf(&ti,"Cik");
+  gunclone(tabcstn); gunclone(tabcstni);
+  pari_free((void*)coef); return znf;
+}
+
+static void
+znf_get_sign(GEN znf, long *r1, long *r2)
+{ GEN v = gel(znf,1); *r1 = v[1]; *r2 = v[2]; }
+
+/* s != 0,1 */
+static GEN
+slambdak(GEN znf, long s, long flag, long prec)
+{
+  GEN resi, C, cst, cstlog, coeflog, cs, coef;
+  GEN lambd, gammas2, gammaunmoins2, var1, var2;
+  GEN gar, val, valm, valk, valkm;
+  long r1, r2, r, i0, i, k, N0;
+
+  znf_get_sign(znf, &r1, &r2); r = r1+r2;
+  resi   = gel(znf,2);
+  C      = gel(znf,4);
+  cst    = gel(znf,5);
+  cstlog = gel(znf,6);
+  coef   = gel(znf,8);
+  coeflog= gel(znf,9);
+  i0 = nbrows(C);
+  N0 = lg(coef)-1;
+
+  if (s < 0 && (r2 || !odd(s))) s = 1 - s;
+
+  /* s >= 2 or s < 0 */
+  lambd = gdiv(resi, mulss(s, s-1));
+  gammas2 = ggamma(gmul2n(stoi(s),-1),prec);
+  gar = gpowgs(gammas2,r1);
+  cs = mpexp( mulrs(cstlog,s) );
+  val = stoi(s); valm = stoi(1 - s);
+  if (s < 0) /* r2 = 0 && odd(s) */
+  {
+    gammaunmoins2 = ggamma(gmul2n(valm,-1),prec); /* gamma((1-s) / 2) */
+    var1 = var2 = gen_1;
+    for (i=2; i<=N0; i++)
+      if (coef[i])
+      {
+        GEN gexpro = mpexp(mulrs(gel(coeflog,i),s));
+        var1 = gadd(var1, mulsr(coef[i],gexpro));
+        var2 = gadd(var2, divsr(coef[i],mulsr(i,gexpro)));
+      }
+    lambd = gadd(lambd,gmul(gmul(var1,cs),gar));
+    lambd = gadd(lambd,gmul(gmul(var2,gdiv(cst,cs)),
+                            gpowgs(gammaunmoins2,r1)));
+    var1 = gen_0;
+    for (i=1; i<=i0; i+=2)
+    {
+      valk  = val;
+      valkm = valm;
+      for (k=1; k<=r; k++)
+      {
+        GEN c = gcoeff(C,i,k);
+        var1 = mpadd(var1,mpdiv(c,valk )); valk  = mulii(val,valk);
+        var1 = mpadd(var1,mpdiv(c,valkm)); valkm = mulii(valm,valkm);
+      }
+      val  = addis(val, 2);
+      valm = addis(valm,2);
+    }
+  }
+  else
+  {
+    GEN tabj = gel(znf,3), aij = gel(znf,7), A = gel(aij,s);
+    long n;
+
+    gar = gmul(gar,gpowgs(mpfactr(s-1,prec),r2)); /* x gamma(s)^r2 */
+    /* n = 1 */
+    var1 = gen_1;
+    var2 = (s <= i0)? sumprod(A, gel(tabj,1), r, 0): gen_0;
+    for (n=2; n<=N0; n++)
+      if (coef[n])
+      {
+        GEN gexpro = mpexp( mulrs(gel(coeflog,n),s) );
+        var1 = mpadd(var1, mulsr(coef[n],gexpro));
+        if (s <= i0)
+        {
+          GEN t = sumprod(A, gel(tabj,n), r, 0);
+          if (!signe(t)) continue;
+          var2 = mpadd(var2, mpdiv(t, mulsr(n,gexpro)));
+        }
+      }
+    lambd = gadd(lambd,gmul(gmul(var1,cs),gar));
+    lambd = gadd(lambd,gmul(var2,gdiv(cst,cs)));
+    var1 = gen_0;
+    for (n=1; n<=i0; n++)
+    {
+      valk  = val;
+      valkm = valm;
+      if (n == s)
+        for (k=1; k<=r; k++)
+        {
+          GEN c = gcoeff(C,n,k);
+          var1 = mpadd(var1,mpdiv(c,valk)); valk = mulii(val,valk);
+        }
+      else
+      for (k=1; k<=r; k++)
+      {
+          GEN c = gcoeff(C,n,k);
+          var1 = mpadd(var1,mpdiv(c,valk )); valk  = mulii(val,valk);
+          var1 = mpadd(var1,mpdiv(c,valkm)); valkm = mulii(valm,valkm);
+      }
+      val  = addis(val,1);
+      valm = addis(valm,1);
+    }
+  }
+  lambd = gadd(lambd, var1);
+  if (!flag) lambd = gdiv(lambd,gmul(gar,cs)); /* zetak */
+  return lambd;
+}
+
+/* s not an integer */
+static GEN
+cxlambdak(GEN znf, GEN s, long flag, long prec)
+{
+  GEN resi, C, cst, cstlog, coeflog, cs, coef;
+  GEN lambd, gammas, gammaunmoins, gammas2, gammaunmoins2, var1, var2;
+  GEN smoinun, unmoins, gar, val, valm, valk, valkm, Pi, sPi;
+  long r1, r2, r, i0, i, k, N0, bigprec;
+
+  znf_get_sign(znf, &r1, &r2);
+  resi   = gel(znf,2);
+  C      = gel(znf,4);
+  cst    = gel(znf,5);
+  cstlog = gel(znf,6);
+  coef   = gel(znf,8);
+  coeflog= gel(znf,9);
+  r1 = mael(znf,1,1);
+  r2 = mael(znf,1,2); r = r1+r2;
+  i0 = nbrows(C);
+  N0 = lg(coef)-1;
+  bigprec = precision(cst);
+
+  Pi = mppi(bigprec);
+  s = gtofp(s, bigprec); sPi = gmul(s, Pi);
+  smoinun = gsubgs(s,1);
+  unmoins = gneg(smoinun);
+  lambd = gdiv(resi,gmul(s,smoinun));
+  gammas = ggamma(s,prec);
+  gammas2= ggamma(gmul2n(s,-1),prec);
+  gar = gmul(gpowgs(gammas,r2),gpowgs(gammas2,r1));
+  cs = gexp(gmul(cstlog,s),prec);
+  gammaunmoins = gdiv(Pi, gmul(gsin(sPi,prec),gammas));
+  gammaunmoins2= gdiv(gmul(gmul(sqrtr(Pi),gpow(gen_2,smoinun,prec)),
+                           gammas2),
+                      gmul(gcos(gmul2n(sPi,-1),prec),gammas));
+  var1 = var2 = gen_1;
+  for (i=2; i<=N0; i++)
+    if (coef[i])
+    {
+      GEN gexpro = gexp(gmul(gel(coeflog,i),s),bigprec);
+      var1 = gadd(var1,gmulsg(coef[i], gexpro));
+      var2 = gadd(var2,gdivsg(coef[i], gmulsg(i,gexpro)));
+    }
+  lambd = gadd(lambd,gmul(gmul(var1,cs),gar));
+  lambd = gadd(lambd,gmul(gmul(gmul(var2,gdiv(cst,cs)),
+                               gpowgs(gammaunmoins,r2)),
+                          gpowgs(gammaunmoins2,r1)));
+  val  = s;
+  valm = unmoins;
+  var1 = gen_0;
+  for (i=1; i<=i0; i++)
+  {
+    valk  = val;
+    valkm = valm;
+    for (k=1; k<=r; k++)
+    {
+      GEN c = gcoeff(C,i,k);
+      var1 = gadd(var1,gdiv(c,valk )); valk  = gmul(val, valk);
+      var1 = gadd(var1,gdiv(c,valkm)); valkm = gmul(valm,valkm);
+    }
+    if (r2)
+    {
+      val  = gaddgs(val, 1);
+      valm = gaddgs(valm,1);
+    }
+    else
+    {
+      val  = gaddgs(val, 2);
+      valm = gaddgs(valm,2); i++;
+    }
+  }
+  lambd = gadd(lambd, var1);
+  if (!flag) lambd = gdiv(lambd,gmul(gar,cs)); /* zetak */
+  return lambd;
+}
+
+GEN
+gzetakall(GEN znf, GEN s, long flag, long prec)
+{
+  pari_sp av = avma;
+  GEN z;
+
+  if (typ(znf)!=t_VEC || lg(znf)!=10 || typ(gel(znf,1)) != t_VECSMALL)
+    pari_err_TYPE("zetakall", znf);
+  if (isint(s, &s))
+  {
+    long ss = itos(s), r1, r2;
+    if (ss==1) pari_err_DOMAIN("zetak", "argument", "=", gen_1, s);
+    znf_get_sign(znf, &r1, &r2);
+    if (ss==0)
+    {
+      avma = av;
+      if (flag) pari_err_DOMAIN("zetak", "argument", "=", gen_0, s);
+      if (r1 + r2 > 1) return gen_0;
+      return r1? mkfrac(gen_m1, gen_2): gneg(gel(znf, 2));
+    }
+    if (!flag && ss < 0 && (r2 || !odd(ss))) return gen_0;
+    z = slambdak(znf, itos(s), flag, prec+EXTRAPRECWORD);
+  }
+  else
+    z = cxlambdak(znf, s, flag, prec+EXTRAPRECWORD);
+  if (gprecision(z) > prec) z = gprec_w(z, prec);
+  return gerepileupto(av, z);
+}
+GEN
+gzetak(GEN nfz, GEN s, long prec) { return gzetakall(nfz,s,0,prec); }
+GEN
+glambdak(GEN nfz, GEN s, long prec) { return gzetakall(nfz,s,1,prec); }
diff --git a/src/modules/aprcl.c b/src/modules/aprcl.c
new file mode 100644
index 0000000..3af8919
--- /dev/null
+++ b/src/modules/aprcl.c
@@ -0,0 +1,1028 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                    THE APRCL PRIMALITY TEST                     */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+typedef struct Red {
+/* global data */
+  GEN N; /* prime we are certifying */
+  GEN N2; /* floor(N/2) */
+/* globa data for flexible window */
+  long k, lv;
+  ulong mask;
+/* reduction data */
+  long n;
+  GEN C; /* polcyclo(n) */
+  GEN (*red)(GEN x, struct Red*);
+} Red;
+
+typedef struct Cache { /* data associated to p^k */
+  GEN aall, tall;
+  GEN cyc;
+  GEN E;
+  GEN eta;
+  GEN matvite, matinvvite;
+  GEN avite, pkvite;
+  ulong ctsgt; /* DEBUG */
+} Cache;
+
+static GEN
+makepoldeg1(GEN c, GEN d)
+{
+  GEN z;
+  if (signe(c)) {
+    z = cgetg(4,t_POL); z[1] = evalsigne(1);
+    gel(z,2) = d; gel(z,3) = c;
+  } else if (signe(d)) {
+    z = cgetg(3,t_POL); z[1] = evalsigne(1);
+    gel(z,2) = d;
+  } else {
+    z = cgetg(2,t_POL); z[1] = evalsigne(0);
+  }
+  return z;
+}
+
+/* T mod polcyclo(p), assume deg(T) < 2p */
+static GEN
+red_cyclop(GEN T, long p)
+{
+  long i, d;
+  GEN y, z;
+
+  d = degpol(T) - p; /* < p */
+  if (d <= -2) return T;
+
+  /* reduce mod (x^p - 1) */
+  y = ZX_mod_Xnm1(T, p);
+  z = y+2;
+
+  /* reduce mod x^(p-1) + ... + 1 */
+  d = p-1;
+  if (degpol(y) == d)
+    for (i=0; i<d; i++) gel(z,i) = subii(gel(z,i), gel(z,d));
+  return normalizepol_lg(y, d+2);
+}
+
+/* x t_VECSMALL, as red_cyclo2n_ip */
+static GEN
+u_red_cyclo2n_ip(GEN x, long n)
+{
+  long i, pow2 = 1L<<(n-1);
+  GEN z;
+
+  for (i = lg(x)-1; i>pow2; i--) x[i-pow2] -= x[i];
+  for (; i>0; i--)
+    if (x[i]) break;
+  i += 2;
+  z = cgetg(i, t_POL); z[1] = evalsigne(1);
+  for (i--; i>=2; i--) gel(z,i) = stoi(x[i-1]);
+  return z;
+}
+/* x t_POL, n > 0. Return x mod polcyclo(2^n) = (x^(2^(n-1)) + 1). IN PLACE */
+static GEN
+red_cyclo2n_ip(GEN x, long n)
+{
+  long i, pow2 = 1L<<(n-1);
+  for (i = lg(x)-1; i>pow2+1; i--)
+    if (signe(gel(x,i))) gel(x,i-pow2) = subii(gel(x,i-pow2), gel(x,i));
+  return normalizepol_lg(x, i+1);
+}
+static GEN
+red_cyclo2n(GEN x, long n) { return red_cyclo2n_ip(leafcopy(x), n); }
+
+/* x a non-zero VECSMALL */
+static GEN
+smallpolrev(GEN x)
+{
+  long i,j, lx = lg(x);
+  GEN y;
+
+  while (lx-- && x[lx]==0) /* empty */;
+  i = lx+2; y = cgetg(i,t_POL);
+  y[1] = evalsigne(1);
+  for (j=2; j<i; j++) gel(y,j) = stoi(x[j-1]);
+  return y;
+}
+
+/* x polynomial in t_VECSMALL form, T t_POL return x mod T */
+static GEN
+u_red(GEN x, GEN T) {
+  return RgX_rem(smallpolrev(x), T);
+}
+
+/* special case R->C = polcyclo(2^n) */
+static GEN
+_red_cyclo2n(GEN x, Red *R) {
+  return centermod_i(red_cyclo2n(x, R->n), R->N, R->N2);
+}
+/* special case R->C = polcyclo(p) */
+static GEN
+_red_cyclop(GEN x, Red *R) {
+  return centermod_i(red_cyclop(x, R->n), R->N, R->N2);
+}
+static GEN
+_red(GEN x, Red *R) {
+  return centermod_i(grem(x, R->C), R->N, R->N2);
+}
+static GEN
+_redsimple(GEN x, Red *R) { return centermodii(x, R->N, R->N2); }
+
+static GEN
+sqrmod(GEN x, Red *R) {
+  return R->red(gsqr(x), R);
+}
+
+static GEN
+sqrconst(GEN pol, Red *R)
+{
+  GEN z = cgetg(3,t_POL);
+  gel(z,2) = centermodii(sqri(gel(pol,2)), R->N, R->N2);
+  z[1] = pol[1]; return z;
+}
+
+/* pol^2 mod (x^2+x+1, N) */
+static GEN
+sqrmod3(GEN pol, Red *R)
+{
+  GEN a,b,bma,A,B;
+  long lv = lg(pol);
+
+  if (lv==2) return pol;
+  if (lv==3) return sqrconst(pol, R);
+  a = gel(pol,3);
+  b = gel(pol,2); bma = subii(b,a);
+  A = centermodii(mulii(a,addii(b,bma)), R->N, R->N2);
+  B = centermodii(mulii(bma,addii(a,b)), R->N, R->N2);
+  return makepoldeg1(A,B);
+}
+
+/* pol^2 mod (x^2+1,N) */
+static GEN
+sqrmod4(GEN pol, Red *R)
+{
+  GEN a,b,A,B;
+  long lv = lg(pol);
+
+  if (lv==2) return pol;
+  if (lv==3) return sqrconst(pol, R);
+  a = gel(pol,3);
+  b = gel(pol,2);
+  A = centermodii(mulii(a, shifti(b,1)), R->N, R->N2);
+  B = centermodii(mulii(subii(b,a),addii(b,a)), R->N, R->N2);
+  return makepoldeg1(A,B);
+}
+
+/* pol^2 mod (polcyclo(5),N) */
+static GEN
+sqrmod5(GEN pol, Red *R)
+{
+  GEN c2,b,c,d,A,B,C,D;
+  long lv = lg(pol);
+
+  if (lv==2) return pol;
+  if (lv==3) return sqrconst(pol, R);
+  c = gel(pol,3); c2 = shifti(c,1);
+  d = gel(pol,2);
+  if (lv==4)
+  {
+    A = sqri(d);
+    B = mulii(c2, d);
+    C = sqri(c);
+    A = centermodii(A, R->N, R->N2);
+    B = centermodii(B, R->N, R->N2);
+    C = centermodii(C, R->N, R->N2); return mkpoln(3,A,B,C);
+  }
+  b = gel(pol,4);
+  if (lv==5)
+  {
+    A = mulii(b, subii(c2,b));
+    B = addii(sqri(c), mulii(b, subii(shifti(d,1),b)));
+    C = subii(mulii(c2,d), sqri(b));
+    D = mulii(subii(d,b), addii(d,b));
+  }
+  else
+  { /* lv == 6 */
+    GEN a = gel(pol,5), a2 = shifti(a,1);
+    /* 2a(d - c) + b(2c - b) */
+    A = addii(mulii(a2, subii(d,c)), mulii(b, subii(c2,b)));
+    /* c(c - 2a) + b(2d - b) */
+    B = addii(mulii(c, subii(c,a2)), mulii(b, subii(shifti(d,1),b)));
+    /* (a-b)(a+b) + 2c(d - a) */
+    C = addii(mulii(subii(a,b),addii(a,b)), mulii(c2,subii(d,a)));
+    /* 2a(b - c) + (d-b)(d+b) */
+    D = addii(mulii(a2, subii(b,c)), mulii(subii(d,b), addii(d,b)));
+  }
+  A = centermodii(A, R->N, R->N2);
+  B = centermodii(B, R->N, R->N2);
+  C = centermodii(C, R->N, R->N2);
+  D = centermodii(D, R->N, R->N2); return mkpoln(4,A,B,C,D);
+}
+
+static GEN
+_mul(GEN x, GEN y, Red *R) { return R->red(gmul(x,y), R); }
+
+/* jac^floor(N/pk) mod (N, polcyclo(pk)), flexible window */
+static GEN
+_powpolmod(Cache *C, GEN jac, Red *R, GEN (*_sqr)(GEN, Red *))
+{
+  const GEN taba = C->aall;
+  const GEN tabt = C->tall;
+  const long efin = lg(taba)-1, lv = R->lv;
+  GEN L, res = jac, pol2 = _sqr(res, R);
+  long f;
+  pari_sp av0 = avma, av, lim;
+
+  L = cgetg(lv+1, t_VEC); gel(L,1) = res;
+  for (f=2; f<=lv; f++) gel(L,f) = _mul(gel(L,f-1), pol2, R);
+  av = avma; lim = stack_lim(av, 1);
+  for (f = efin; f >= 1; f--)
+  {
+    GEN t = gel(L, taba[f]);
+    long tf = tabt[f];
+    res = (f==efin)? t: _mul(t, res, R);
+    while (tf--) {
+      res = _sqr(res, R);
+      if (low_stack(lim, stack_lim(av,1))) {
+        res = gerepilecopy(av, res);
+        if(DEBUGMEM>1) pari_warn(warnmem,"powpolmod: f = %ld",f);
+      }
+    }
+  }
+  return gerepilecopy(av0, res);
+}
+
+static GEN
+_powpolmodsimple(Cache *C, Red *R, GEN jac)
+{
+  pari_sp av = avma;
+  GEN w = mulmat_pol(C->matvite, jac);
+  long j, ph = lg(w);
+
+  R->red = &_redsimple;
+  for (j=1; j<ph; j++)
+    gel(w,j) = _powpolmod(C, centermodii(gel(w,j), R->N, R->N2), R, &sqrmod);
+  w = centermod_i( gmul(C->matinvvite, w), R->N, R->N2 );
+  w = gerepileupto(av, w);
+  return RgV_to_RgX(w, 0);
+}
+
+static GEN
+powpolmod(Cache *C, Red *R, long p, long k, GEN jac)
+{
+  GEN (*_sqr)(GEN, Red *);
+
+  if (DEBUGLEVEL>2) C->ctsgt++;
+  if (C->matvite) return _powpolmodsimple(C, R, jac);
+  if (p == 2) /* p = 2 */
+  {
+    if (k == 2) _sqr = &sqrmod4;
+    else        _sqr = &sqrmod;
+    R->n = k;
+    R->red = &_red_cyclo2n;
+  } else if (k == 1)
+  {
+    if      (p == 3) _sqr = &sqrmod3;
+    else if (p == 5) _sqr = &sqrmod5;
+    else             _sqr = &sqrmod;
+    R->n = p;
+    R->red = &_red_cyclop;
+  } else {
+    R->red = &_red; _sqr = &sqrmod;
+  }
+  return _powpolmod(C, jac, R, _sqr);
+}
+
+/* Return e(t) = \prod_{p-1 | t} p^{1+v_p(t)}}
+ * faet contains the odd prime divisors of e(t) */
+static GEN
+compute_e(ulong t, GEN *faet)
+{
+  GEN L, P, D = divisorsu(t);
+  long l = lg(D);
+  ulong k;
+
+  P = vecsmalltrunc_init(l);
+  L = vecsmalltrunc_init(l);
+  for (k=l-1; k>1; k--) /* k != 1: avoid d = 1 */
+  {
+    ulong d = D[k];
+    if (uisprime(++d))
+    { /* we want q = 1 (mod p) prime, not too large */
+      if (d > 50000000) return gen_0;
+      vecsmalltrunc_append(P, d);
+      vecsmalltrunc_append(L, upowuu(d, 1 + u_lval(t,d)));
+    }
+  }
+  if (faet) *faet = P;
+  return shifti(zv_prod_Z(L), 2 + u_lval(t,2));
+}
+
+/* Table obtained by the following script:
+
+install(compute_e, "LD&"); \\ remove 'static' first
+
+table(first = 6, step = 6, MAXT = 8648640)=
+{
+  emax = 0;
+  forstep(t = first, MAXT, step,
+    e = compute_e(t);
+    if (e > 1.9*emax, emax = e;
+      printf("  if (C < %5.5g) return %8d;\n", 2*log(e)/log(2)*0.9999, t)
+    );
+  );
+}
+
+Previous values can be recovered using the following table:
+
+T=[6,12,24,48,36,60,120,180,240,504,360,420,720,840,1440,1260,1680,2520,3360,5040,13860,10080,16380,21840,18480,27720,32760,36960,55440,65520,98280,110880,131040,166320,196560,262080,277200,360360,480480,332640,554400,720720,665280,831600,1113840,1441440,1663200,2227680,2162160,2827440,3326400,3603600,6126120,4324320,6683040,7207200,11138400,8648640];
+f(t) = 2*log(compute_e(t))/log(2);
+for (i=1,#T, t=T[i]; printf("  if (C < %5.5g) return %8d;\n", f(t),t));
+
+*/
+
+/* assume C < 3514.6 */
+static ulong
+compute_t_small(double C)
+{
+  if (C < 17.953) return        6;
+  if (C < 31.996) return       12;
+  if (C < 33.996) return       24;
+  if (C < 54.079) return       36;
+  if (C < 65.325) return       60;
+  if (C < 68.457) return       72;
+  if (C < 70.783) return      108;
+  if (C < 78.039) return      120;
+  if (C < 102.41) return      180;
+  if (C < 127.50) return      360;
+  if (C < 136.68) return      420;
+  if (C < 153.43) return      540;
+  if (C < 165.66) return      840;
+  if (C < 169.17) return     1008;
+  if (C < 178.52) return     1080;
+  if (C < 192.68) return     1200;
+  if (C < 206.34) return     1260;
+  if (C < 211.94) return     1620;
+  if (C < 222.09) return     1680;
+  if (C < 225.11) return     2016;
+  if (C < 244.19) return     2160;
+  if (C < 270.29) return     2520;
+  if (C < 279.50) return     3360;
+  if (C < 293.62) return     3780;
+  if (C < 346.68) return     5040;
+  if (C < 348.70) return     6480;
+  if (C < 383.34) return     7560;
+  if (C < 396.68) return     8400;
+  if (C < 426.04) return    10080;
+  if (C < 458.34) return    12600;
+  if (C < 527.16) return    15120;
+  if (C < 595.38) return    25200;
+  if (C < 636.29) return    30240;
+  if (C < 672.53) return    42840;
+  if (C < 684.90) return    45360;
+  if (C < 708.78) return    55440;
+  if (C < 771.30) return    60480;
+  if (C < 775.86) return    75600;
+  if (C < 859.62) return    85680;
+  if (C < 893.16) return   100800;
+  if (C < 912.27) return   110880;
+  if (C < 966.13) return   128520;
+  if (C < 1009.1) return   131040;
+  if (C < 1041.9) return   166320;
+  if (C < 1124.9) return   196560;
+  if (C < 1251.0) return   257040;
+  if (C < 1375.0) return   332640;
+  if (C < 1431.0) return   393120;
+  if (C < 1483.3) return   514080;
+  if (C < 1546.3) return   655200;
+  if (C < 1585.8) return   665280;
+  if (C < 1661.3) return   786240;
+  if (C < 1667.5) return   831600;
+  if (C < 1676.9) return   917280;
+  if (C < 1728.0) return   982800;
+  if (C < 1747.4) return  1081080;
+  if (C < 1773.6) return  1179360;
+  if (C < 1810.6) return  1285200;
+  if (C < 1924.5) return  1310400;
+  if (C < 2001.1) return  1441440;
+  if (C < 2096.3) return  1663200;
+  if (C < 2165.8) return  1965600;
+  if (C < 2321.6) return  2162160;
+  if (C < 2368.2) return  2751840;
+  if (C < 2377.2) return  2827440;
+  if (C < 2514.7) return  3326400;
+  if (C < 2588.5) return  3341520;
+  if (C < 2636.6) return  3603600;
+  if (C < 2667.2) return  3931200;
+  if (C < 3028.6) return  4324320;
+  if (C < 3045.5) return  5654880;
+  if (C < 3080.5) return  6652800;
+  if (C < 3121.6) return  6683040;
+  if (C < 3283.1) return  7207200;
+  return  8648640;
+}
+
+/* return t such that e(t) > sqrt(N), set *faet = odd prime divisors of e(t) */
+static ulong
+compute_t(GEN N, GEN *e, GEN *faet)
+{
+  /* 2^e b <= N < 2^e (b+1), where b >= 2^52. Approximating log_2 N by
+   * log2(gtodouble(N)) ~ e+log2(b), the error is less than log(1+1/b) < 1e-15*/
+  double C = dbllog2(N) + 1e-6; /* > log_2 N */
+  ulong t;
+  GEN B;
+  /* Return "smallest" t such that f(t) >= C, which implies e(t) > sqrt(N) */
+  /* For N < 2^3515 ~ 10^1058 */
+  if (C < 3514.6)
+  {
+    t = compute_t_small(C);
+    *e = compute_e(t, faet);
+    return t;
+  }
+  B = sqrti(N);
+  for (t = 8648640+840;; t+=840)
+  {
+    pari_sp av = avma;
+    *e = compute_e(t, faet);
+    if (cmpii(*e, B) > 0) break;
+    avma = av;
+  }
+  return t;
+}
+
+/* T[i] = discrete log of i in (Z/q)^*, q odd prime
+ * To save on memory, compute half the table: T[q-x] = T[x] + (q-1)/2 */
+static GEN
+computetabdl(ulong q)
+{
+  ulong g, a, i, qs2 = q>>1; /* (q-1)/2 */
+  GEN T = cgetg(qs2+2,t_VECSMALL);
+
+  g = pgener_Fl(q); a = 1;
+  for (i=1; i < qs2; i++) /* g^((q-1)/2) = -1 */
+  {
+    a = Fl_mul(g,a,q);
+    if (a > qs2) T[q-a] = i+qs2; else T[a] = i;
+  }
+  T[qs2+1] = T[qs2] + qs2;
+  T[1] = 0; return T;
+}
+
+/* Return T: T[x] = dl of x(1-x) */
+static GEN
+compute_g(ulong q)
+{
+  const ulong qs2 = q>>1; /* (q-1)/2 */
+  ulong x, a;
+  GEN T = computetabdl(q); /* updated in place to save on memory */
+  a = 0; /* dl[1] */
+  for (x=2; x<=qs2+1; x++)
+  { /* a = dl(x) */
+    ulong b = T[x]; /* = dl(x) */
+    T[x] = b + a + qs2; /* dl(x) + dl(x-1) + dl(-1) */
+    a = b;
+  }
+  return T;
+}
+
+/* p odd prime */
+static GEN
+get_jac(Cache *C, ulong q, long pk, GEN tabg)
+{
+  ulong x, qs2 = q>>1; /* (q-1)/2 */
+  GEN vpk = zero_zv(pk);
+
+  for (x=2; x<=qs2; x++) vpk[ tabg[x]%pk + 1 ] += 2;
+  vpk[ tabg[x]%pk + 1 ]++; /* x = (q+1)/2 */
+  return u_red(vpk, C->cyc);
+}
+
+/* p = 2 */
+static GEN
+get_jac2(GEN N, ulong q, long k, GEN *j2q, GEN *j3q)
+{
+  GEN jpq, vpk, T = computetabdl(q);
+  ulong x, pk, i, qs2;
+
+  /* could store T[x+1] + T[x] + qs2 (cf compute_g).
+   * Recompute instead, saving half the memory. */
+  pk = 1UL << k;;
+  vpk = zero_zv(pk);
+
+  qs2 = q>>1; /* (q-1)/2 */
+
+  for (x=2; x<=qs2; x++) vpk[ (T[x]+T[x-1]+qs2)%pk + 1 ] += 2;
+  vpk[ (T[x]+T[x-1]+qs2)%pk + 1 ]++;
+  jpq = u_red_cyclo2n_ip(vpk, k);
+
+  if (k == 2) return jpq;
+
+  if (mod8(N) >= 5)
+  {
+    GEN v8 = cgetg(9,t_VECSMALL);
+    for (x=1; x<=8; x++) v8[x] = 0;
+    for (x=2; x<=qs2; x++) v8[ ((3*T[x]+T[x-1]+qs2)&7) + 1 ]++;
+    for (   ; x<=q-1; x++) v8[ ((3*T[q-x]+T[q-x+1]-3*qs2)&7) + 1 ]++;
+    *j2q = RgX_inflate(ZX_sqr(u_red_cyclo2n_ip(v8,3)), pk>>3);
+    *j2q = red_cyclo2n_ip(*j2q, k);
+  }
+  for (i=1; i<=pk; i++) vpk[i] = 0;
+  for (x=2; x<=qs2; x++) vpk[ (2*T[x]+T[x-1]+qs2)%pk + 1 ]++;
+  for (   ; x<=q-1; x++) vpk[ (2*T[q-x]+T[q-x+1]-2*qs2)%pk + 1 ]++;
+  *j3q = ZX_mul(jpq, u_red_cyclo2n_ip(vpk,k));
+  *j3q = red_cyclo2n_ip(*j3q, k);
+  return jpq;
+}
+
+/* N = 1 mod p^k, return an elt of order p^k in (Z/N)^* */
+static GEN
+finda(Cache *Cp, GEN N, long pk, long p)
+{
+  GEN a, pv;
+  if (Cp && Cp->avite) {
+    a  = Cp->avite;
+    pv = Cp->pkvite;
+  }
+  else
+  {
+    GEN ph, b, q;
+    ulong u = 2;
+    long v = Z_lvalrem(addis(N,-1), p, &q);
+    ph = powuu(p, v-1); pv = muliu(ph, p); /* N - 1 = p^v q */
+    if (p > 2)
+    {
+      for (;;u++)
+      {
+        a = Fp_pow(utoipos(u), q, N);
+        b = Fp_pow(a, ph, N);
+        if (!gequal1(b)) break;
+      }
+    }
+    else
+    {
+      while (krosi(u,N) >= 0) u++;
+      a = Fp_pow(utoipos(u), q, N);
+      b = Fp_pow(a, ph, N);
+    }
+    /* checking b^p = 1 mod N done economically in caller */
+    b = gcdii(addis(b,-1), N);
+    if (!gequal1(b)) return NULL;
+
+    if (Cp) {
+      Cp->avite  = a; /* a has order p^v */
+      Cp->pkvite = pv;
+    }
+  }
+  return Fp_pow(a, divis(pv, pk), N);
+}
+
+/* return 0: N not a prime, 1: no problem so far */
+static long
+filltabs(Cache *C, Cache *Cp, Red *R, long p, long pk, long ltab)
+{
+  pari_sp av;
+  long i, j;
+  long e;
+  GEN tabt, taba, m;
+
+  C->cyc = polcyclo(pk,0);
+
+  if (p > 2)
+  {
+    long LE = pk - pk/p + 1;
+    GEN E = cgetg(LE, t_VECSMALL), eta = cgetg(pk+1,t_VEC);
+    for (i=1,j=0; i<pk; i++)
+      if (i%p) E[++j] = i;
+    C->E = E;
+
+    for (i=1; i<=pk; i++)
+    {
+      GEN z = FpX_rem(monomial(gen_1, i-1, 0), C->cyc, R->N);
+      gel(eta,i) = FpX_center(z, R->N, R->N2);
+    }
+    C->eta = eta;
+  }
+  else if (pk >= 8)
+  {
+    long LE = (pk>>2) + 1;
+    GEN E = cgetg(LE, t_VECSMALL);
+    for (i=1,j=0; i<pk; i++)
+      if ((i%8)==1 || (i%8)==3) E[++j] = i;
+    C->E = E;
+  }
+
+  if (pk > 2 && umodiu(R->N,pk) == 1)
+  {
+    GEN vpa, p1, p2, p3, a2 = NULL, a = finda(Cp, R->N, pk, p);
+    long jj, ph;
+
+    if (!a) return 0;
+    ph = pk - pk/p;
+    vpa = cgetg(ph+1,t_COL); gel(vpa,1) = a;
+    if (pk > p) a2 = centermodii(sqri(a), R->N, R->N2);
+    jj = 1;
+    for (i=2; i<pk; i++) /* vpa = { a^i, (i,p) = 1 } */
+      if (i%p) {
+        GEN z = mulii((i%p==1) ? a2 : a, gel(vpa,jj));
+        gel(vpa,++jj) = centermodii(z , R->N, R->N2);
+      }
+    if (!gequal1( centermodii( mulii(a, gel(vpa,ph)), R->N, R->N2) )) return 0;
+    p1 = cgetg(ph+1,t_MAT);
+    p2 = cgetg(ph+1,t_COL); gel(p1,1) = p2;
+    for (i=1; i<=ph; i++) gel(p2,i) = gen_1;
+    j = 2; gel(p1,j) = vpa; p3 = vpa;
+    for (j++; j <= ph; j++)
+    {
+      p2 = cgetg(ph+1,t_COL); gel(p1,j) = p2;
+      for (i=1; i<=ph; i++)
+        gel(p2,i) = centermodii(mulii(gel(vpa,i),gel(p3,i)), R->N, R->N2);
+      p3 = p2;
+    }
+    C->matvite = p1;
+    C->matinvvite = FpM_inv(p1, R->N);
+  }
+
+  tabt = cgetg(ltab+1, t_VECSMALL);
+  taba = cgetg(ltab+1, t_VECSMALL);
+  av = avma; m = divis(R->N, pk);
+  for (e=1; e<=ltab && signe(m); e++)
+  {
+    long s = vali(m); m = shifti(m,-s);
+    tabt[e] = e==1? s: s + R->k;
+    taba[e] = signe(m)? ((mod2BIL(m) & R->mask)+1)>>1: 0;
+    m = shifti(m, -R->k);
+  }
+  setlg(taba, e); C->aall = taba;
+  setlg(tabt, e); C->tall = tabt;
+  avma = av; return 1;
+}
+
+static Cache *
+alloc_cache(void)
+{
+  Cache *C = (Cache*)stack_malloc(sizeof(Cache));
+  C->matvite = NULL;
+  C->avite   = NULL;
+  C->ctsgt = 0; return C;
+}
+
+static Cache **
+calcglobs(Red *R, ulong t, long *plpC, long *pltab, GEN *pP)
+{
+  GEN fat, P, E, PE;
+  long lv, i, k, b;
+  Cache **pC;
+
+  b = expi(R->N)+1;
+  k = 3; while (((k+1)*(k+2) << (k-1)) < b) k++;
+  *pltab = (b/k)+2;
+  R->k  = k;
+  R->lv = 1L << (k-1);
+  R->mask = (1UL << k) - 1;
+
+  fat = factoru_pow(t);
+  P = gel(fat,1);
+  E = gel(fat,2);
+  PE= gel(fat,3);
+  *plpC = lv = vecsmall_max(PE); /* max(p^e, p^e | t) */
+  pC = (Cache**)stack_malloc((lv+1)*sizeof(Cache*));
+  pC[1] = alloc_cache(); /* to be used as temp in step5() */
+  for (i = 2; i <= lv; i++) pC[i] = NULL;
+  for (i=1; i<lg(P); i++)
+  {
+    long pk, p = P[i], e = E[i];
+    pk = p;
+    for (k=1; k<=e; k++, pk*=p)
+    {
+      pC[pk] = alloc_cache();
+      if (!filltabs(pC[pk], pC[p], R, p,pk, *pltab)) return NULL;
+    }
+  }
+  *pP = P; return pC;
+}
+
+/* sig_a^{-1}(z) for z in Q(zeta_pk) and sig_a: zeta -> zeta^a. Assume
+ * a reduced mod pk := p^k*/
+static GEN
+aut(long pk, GEN z, long a)
+{
+  GEN v;
+  long b, i, dz = degpol(z);
+  if (a == 1 || dz < 0) return z;
+  v = cgetg(pk+2,t_POL);
+  v[1] = evalvarn(0);
+  b = 0;
+  gel(v,2) = gel(z,2); /* i = 0 */
+  for (i = 1; i < pk; i++)
+  {
+    b += a; if (b > pk) b -= pk; /* b = (a*i) % pk */
+    gel(v,i+2) = b > dz? gen_0: gel(z,b+2);
+  }
+  return normalizepol_lg(v, pk+2);
+}
+
+/* z^v for v in Z[G], represented by couples [sig_x^{-1},x] */
+static GEN
+autvec_TH(long pk, GEN z, GEN v, GEN C)
+{
+  long i, lv = lg(v);
+  GEN s = pol_1(varn(C));
+  for (i=1; i<lv; i++)
+  {
+    long y = v[i];
+    if (y) s = RgXQ_mul(s, RgXQ_powu(aut(pk,z, y), y, C), C);
+  }
+  return s;
+}
+
+static GEN
+autvec_AL(long pk, GEN z, GEN v, Red *R)
+{
+  const long r = umodiu(R->N, pk);
+  GEN s = pol_1(varn(R->C));
+  long i, lv = lg(v);
+  for (i=1; i<lv; i++)
+  {
+    long y = (r*v[i]) / pk;
+    if (y) s = RgXQ_mul(s, RgXQ_powu(aut(pk,z, v[i]), y, R->C), R->C);
+  }
+  return s;
+}
+
+/* 0 <= i < pk, such that x^i = z mod polcyclo(pk),  -1 if no such i exist */
+static long
+look_eta(GEN eta, long pk, GEN z)
+{
+  long i;
+  for (i=1; i<=pk; i++)
+    if (ZX_equal(z, gel(eta,i))) return i-1;
+  return -1;
+}
+/* same pk = 2^k */
+static long
+look_eta2(long k, GEN z)
+{
+  long d, s;
+  if (typ(z) != t_POL) d = 0; /* t_INT */
+  else
+  {
+    if (!RgX_is_monomial(z)) return -1;
+    d = degpol(z);
+    z = gel(z,d+2); /* leading term */
+  }
+  s = signe(z);
+  if (!s || !is_pm1(z)) return -1;
+  return (s > 0)? d: d + (1L<<(k-1));
+}
+
+static long
+step4a(Cache *C, Red *R, ulong q, long p, long k, GEN tabg)
+{
+  const long pk = upowuu(p,k);
+  long ind;
+  GEN jpq, s1, s2, s3;
+
+  if (!tabg) tabg = compute_g(q);
+  jpq = get_jac(C, q, pk, tabg);
+  s1 = autvec_TH(pk, jpq, C->E, C->cyc);
+  s2 = powpolmod(C,R, p,k, s1);
+  s3 = autvec_AL(pk, jpq, C->E, R);
+  s3 = _red(gmul(s3,s2), R);
+
+  ind = look_eta(C->eta, pk, s3);
+  if (ind < 0) return -1;
+  return (ind%p) != 0;
+}
+
+/* x == -1 mod N ? */
+static long
+is_m1(GEN x, GEN N)
+{
+  return equalii(addis(x,1), N);
+}
+
+/* p=2, k>=3 */
+static long
+step4b(Cache *C, Red *R, ulong q, long k)
+{
+  const long pk = 1L << k;
+  long ind;
+  GEN s1, s2, s3, j2q = NULL, j3q = NULL;
+
+  (void)get_jac2(R->N,q,k, &j2q,&j3q);
+
+  s1 = autvec_TH(pk, j3q, C->E, C->cyc);
+  s2 = powpolmod(C,R, 2,k, s1);
+  s3 = autvec_AL(pk, j3q, C->E, R);
+  s3 = _red(gmul(s3,s2), R);
+  if (j2q) s3 = _red(gmul(j2q, s3), R);
+
+  ind = look_eta2(k, s3);
+  if (ind < 0) return -1;
+  if ((ind&1)==0) return 0;
+  if (DEBUGLEVEL>2) C->ctsgt++;
+  s3 = Fp_pow(utoipos(q), R->N2, R->N);
+  return is_m1(s3, R->N);
+}
+
+/* p=2, k=2 */
+static long
+step4c(Cache *C, Red *R, ulong q)
+{
+  long ind;
+  GEN s0,s1,s3, jpq = get_jac2(R->N,q,2, NULL,NULL);
+
+  s0 = sqrmod4(jpq, R);
+  s1 = gmulsg(q,s0);
+  s3 = powpolmod(C,R, 2,2, s1);
+  if (mod4(R->N) == 3) s3 = _red(gmul(s0,s3), R);
+
+  ind = look_eta2(2, s3);
+  if (ind < 0) return -1;
+  if ((ind&1)==0) return 0;
+  if (DEBUGLEVEL>2) C->ctsgt++;
+  s3 = Fp_pow(utoipos(q), R->N2, R->N);
+  return is_m1(s3, R->N);
+}
+
+/* p=2, k=1 */
+static long
+step4d(Cache *C, Red *R, ulong q)
+{
+  GEN s1 = Fp_pow(utoipos(q), R->N2, R->N);
+  if (DEBUGLEVEL>2) C->ctsgt++;
+  if (is_pm1(s1)) return 0;
+  if (is_m1(s1, R->N)) return (mod4(R->N) == 1);
+  return -1;
+}
+
+static GEN
+_res(long a, long b) { return b? mkvec2s(a, b): mkvecs(a); }
+
+/* return 1 [OK so far] or <= 0 [not a prime] */
+static GEN
+step5(Cache **pC, Red *R, long p, GEN et, ulong ltab, long lpC)
+{
+  pari_sp av;
+  ulong q;
+  long pk, k, fl = -1;
+  Cache *C, *Cp;
+  forprime_t T;
+
+  (void)u_forprime_arith_init(&T, 3, ULONG_MAX, 1,p);
+  while( (q = u_forprime_next(&T)) )
+  { /* q = 1 (mod p) */
+    if (umodiu(et,q) == 0) continue;
+    if (umodiu(R->N,q) == 0) return _res(1,p);
+    k = u_lval(q-1, p);
+    pk = upowuu(p,k);
+    if (pk <= lpC && pC[pk]) {
+      C = pC[pk];
+      Cp = pC[p];
+    } else {
+      C = pC[1];
+      Cp = NULL;
+      C->matvite = NULL; /* re-init */
+    }
+
+    av = avma;
+    if (!filltabs(C, Cp, R, p, pk, ltab)) return _res(1,0);
+    R->C = C->cyc;
+    if (p >= 3)      fl = step4a(C,R, q,p,k, NULL);
+    else if (k >= 3) fl = step4b(C,R, q,k);
+    else if (k == 2) fl = step4c(C,R, q);
+    else             fl = step4d(C,R, q);
+    if (fl == -1) return _res(q,p);
+    if (fl == 1) return NULL; /*OK*/
+    avma = av;
+  }
+  pari_err_BUG("aprcl test fails! This is highly improbable");
+  return NULL;
+}
+
+static GEN
+step6(GEN N, ulong t, GEN et)
+{
+  GEN r, N1 = remii(N, et);
+  ulong i;
+  pari_sp av = avma;
+
+  r = gen_1;
+  for (i=1; i<t; i++)
+  {
+    r = remii(mulii(r,N1), et);
+    if (equali1(r)) break;
+    if (!signe(remii(N,r)) && !equalii(r,N)) return mkvec2(r, gen_0);
+    if ((i & 0x1f) == 0) r = gerepileuptoint(av, r);
+  }
+  return gen_1;
+}
+
+static GEN
+aprcl(GEN N)
+{
+  GEN et, fat, flaglp, faet = NULL; /*-Wall*/
+  long i, j, l, ltab, lfat, lpC;
+  ulong t;
+  Red R;
+  Cache **pC;
+
+  if (typ(N) != t_INT) pari_err_TYPE("aprcl",N);
+  if (cmpis(N,12) <= 0)
+    switch(itos(N))
+    {
+      case 2: case 3: case 5: case 7: case 11: return gen_1;
+      default: return _res(0,0);
+    }
+  if (Z_issquare(N)) return _res(0,0);
+  t = compute_t(N, &et, &faet);
+  if (DEBUGLEVEL) err_printf("Starting APRCL with t = %ld\n",t);
+  if (cmpii(sqri(et),N) < 0) pari_err_BUG("aprcl: e(t) too small");
+  if (!equali1(gcdii(N,mului(t,et)))) return _res(1,0);
+
+  R.N = N;
+  R.N2= shifti(N, -1);
+  pC = calcglobs(&R, t, &lpC, &ltab, &fat);
+  if (!pC) return _res(1,0);
+  lfat = lg(fat);
+  flaglp = cgetg(lfat, t_VECSMALL);
+  flaglp[1] = 0;
+  for (i=2; i<lfat; i++)
+  {
+    ulong p = fat[i];
+    flaglp[i] = equaliu(Fp_powu(N, p-1, sqru(p)), 1);
+  }
+  vecsmall_sort(faet);
+
+  l = lg(faet);
+  if (DEBUGLEVEL>2) err_printf("Step4: %ld q-values\n", l-1);
+  for (i=l-1; i>0; i--) /* decreasing order: slowest first */
+  {
+    pari_sp av1 = avma;
+    ulong q = faet[i];
+    GEN faq = factoru_pow(q-1), tabg = compute_g(q);
+    GEN P = gel(faq,1), E = gel(faq,2), PE = gel(faq,3);
+    long lfaq = lg(P);
+    pari_sp av2 = avma;
+    if (DEBUGLEVEL>2) err_printf("testing Jacobi sums for q = %ld...",q);
+    for (j=1; j<lfaq; j++, avma = av2)
+    {
+      long p = P[j], e = E[j], pe = PE[j], fl;
+      Cache *C = pC[pe];
+      R.C = C->cyc;
+      if (p >= 3)      fl = step4a(C,&R, q,p,e, tabg);
+      else if (e >= 3) fl = step4b(C,&R, q,e);
+      else if (e == 2) fl = step4c(C,&R, q);
+      else             fl = step4d(C,&R, q);
+      if (fl == -1) return _res(q,p);
+      if (fl == 1) flaglp[ zv_search(fat, p) ] = 0;
+    }
+    if (DEBUGLEVEL>2) err_printf("OK\n");
+    avma = av1;
+  }
+  if (DEBUGLEVEL>2) err_printf("Step5: testing conditions lp\n");
+  for (i=2; i<lfat; i++) /*skip fat[1] = 2*/
+  {
+    pari_sp av = avma;
+    long p = fat[i];
+    GEN r;
+    if (flaglp[i] && (r = step5(pC, &R, p, et, ltab, lpC))) return r;
+    avma = av;
+  }
+  if (DEBUGLEVEL>2)
+  {
+    ulong sc = pC[1]->ctsgt;
+    err_printf("Individual Fermat powerings:\n");
+    for (i=2; i<lpC; i++)
+      if (pC[i]) {
+        err_printf("  %-3ld: %3ld\n", i, pC[i]->ctsgt);
+        sc += pC[i]->ctsgt;
+      }
+    err_printf("Number of Fermat powerings = %lu\n",sc);
+    err_printf("Step6: testing potential divisors\n");
+  }
+  return step6(N, t, et);
+}
+
+long
+isprimeAPRCL(GEN N)
+{
+  pari_sp av = avma;
+  GEN res = aprcl(N);
+  avma = av; return (typ(res) == t_INT);
+}
diff --git a/src/modules/elldata.c b/src/modules/elldata.c
new file mode 100644
index 0000000..7715a9b
--- /dev/null
+++ b/src/modules/elldata.c
@@ -0,0 +1,263 @@
+/* Copyright (C) 2005  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/********************************************************************/
+/**                                                                **/
+/**  INTERFACE TO JOHN CREMONA ELLIPTIC CURVES DATABASE            **/
+/**                                                                **/
+/********************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+static long
+strtoclass(const char *s)
+{
+  long c=0;
+  while (*s && *s<='9') s++;
+  if (!*s) return -1;
+  while ('a'<=*s && *s<='z') c = 26*c + *(s++)-'a';
+  return c;
+}
+
+/*Take a curve name like "100a2" and set
+ * f to the conductor, (100)
+ * c to the isogeny class (in base 26), ("a" or 0)
+ * i to the curve index (2).
+ * return 0 if parse error. */
+static int
+ellparsename(const char *s, long *f, long *c, long *i)
+{
+  long j;
+  *f=-1; *c=-1; *i=-1;
+  if (*s<'0' || *s>'9') return 0;
+  *f=0;
+  for (j=0;j<10 && '0'<=*s && *s<='9';j++)
+    *f=10**f+*(s++)-'0';
+  if (j==10) {*f=-1; return 0;}
+  if (*s<'a' || *s>'z') return !*s;
+  *c=0;
+  for (j=0; j<7 && 'a'<=*s && *s<='z';j++)
+    *c=26**c+*(s++)-'a';
+  if (j==7) {*c=-1; return 0;}
+  if (*s<'0' || *s>'9') return !*s;
+  *i=0;
+  for (j=0; j<10 && '0'<=*s && *s<='9';j++)
+    *i=10**i+*(s++)-'0';
+  if (j==10) {*i=-1; return 0;}
+  return !*s;
+}
+
+/* Take an integer and convert it to base 26 */
+static GEN
+ellrecode(long x)
+{
+  GEN str;
+  char *s;
+  long d = 0, n = x;
+  do { d++; n /= 26; } while (n);
+  str = cgetg(nchar2nlong(d+1)+1, t_STR);
+  s = GSTR(str); s[d] = 0;
+  n = x;
+  do { s[--d] = n%26 + 'a'; n /= 26; } while (n);
+  return str;
+}
+
+GEN
+ellconvertname(GEN n)
+{
+  switch(typ(n))
+  {
+  case t_STR:
+    {
+      long f,i,c;
+      if (!ellparsename(GSTR(n),&f,&c,&i)) pari_err_TYPE("ellconvertname", n);
+      if (f<0 || c<0 || i<0)
+        pari_err_TYPE("ellconvertname [incomplete name]", n);
+      return mkvec3s(f,c,i);
+    }
+  case t_VEC:
+    if (lg(n)==4)
+    {
+      pari_sp av = avma;
+      GEN f=gel(n,1), c=gel(n,2), s=gel(n,3);
+      if (typ(f)!=t_INT || typ(c)!=t_INT || typ(s)!=t_INT)
+        pari_err_TYPE("ellconvertname",n);
+      return gerepilecopy(av, shallowconcat1(mkvec3(f, ellrecode(itos(c)), s)));
+    }
+    /*fall through*/
+  }
+  pari_err_TYPE("ellconvertname",n);
+  return NULL; /*Not reached*/
+}
+
+static GEN
+ellcondfile(long f)
+{
+  pari_sp av = avma;
+  long n = f / 1000;
+  char *s = stack_malloc(strlen(pari_datadir) + 12 + 20 + 1);
+  pariFILE *F;
+  GEN V;
+  sprintf(s, "%s/elldata/ell%ld", pari_datadir, n);
+  F = pari_fopengz(s);
+  if (!F) pari_err_FILE("elldata file",s);
+  avma = av;
+  V = gp_read_stream(F->file);
+  if (!V || typ(V)!=t_VEC ) pari_err_FILE("elldata file [read]",s);
+  pari_fclose(F); return V;
+}
+
+/* return the vector of all curves of conductor f */
+static int cmpi1(GEN x, GEN v) { return cmpii(x, gel(v,1)); }
+static GEN
+ellcondlist(long f)
+{
+  pari_sp av = avma;
+  GEN V = ellcondfile(f);
+  long i = tablesearch(V, utoipos(f), &cmpi1);
+  if (i)
+  {
+    GEN v = gel(V,i);
+    return vecslice(v,2, lg(v)-1);
+  }
+  avma = av; return cgetg(1,t_VEC);
+}
+
+static GEN
+ellsearchbyname(GEN V, char *name)
+{
+  GEN x;
+  long j;
+  for (j=1; j<lg(V); j++)
+  {
+    GEN v = gel(V,j);
+    if (!strcmp(GSTR(gel(v,1)), name)) return v;
+  }
+  x = strtoGENstr(name);
+  pari_err_DOMAIN("ellsearchbyname", "name", "=", x,x);
+  return NULL;
+}
+
+static GEN
+ellsearchbyclass(GEN V, long c)
+{
+  long i,j,n;
+  GEN res;
+  for (n=0,j=1; j<lg(V); j++)
+    if (strtoclass(GSTR(gmael(V,j,1)))==c) n++;
+  res = cgetg(n+1,t_VEC);
+  for (i=1,j=1; j<lg(V); j++)
+    if (strtoclass(GSTR(gmael(V,j,1)))==c) res[i++] = V[j];
+  return res;
+}
+
+GEN
+ellsearch(GEN A)
+{
+  pari_sp av = avma;
+  long f, c, i;
+  GEN V;
+  if      (typ(A)==t_INT) { f = itos(A); c = i = -1; }
+  else if  (typ(A)==t_VEC)
+  {
+    long l = lg(A)-1;
+    if (l<1 || l>3)
+      pari_err_TYPE("ellsearch",A);
+    f = gtos(gel(A,1));
+    c = l>=2 ? gtos(gel(A,2)): -1;
+    i = l>=3 ? gtos(gel(A,3)): -1;
+    if (l>=3) A = ellconvertname(A);
+  }
+  else if (typ(A)==t_STR) {
+    if (!ellparsename(GSTR(A),&f,&c,&i))
+      pari_err_TYPE("ellsearch",A);
+  } else {
+    pari_err_TYPE("ellsearch",A);
+    return NULL;
+  }
+  if (f <= 0) pari_err_DOMAIN("ellsearch", "conductor", "<=", gen_0,stoi(f));
+  V = ellcondlist(f);
+  if (c >= 0)
+    V = (i < 0)? ellsearchbyclass(V,c): ellsearchbyname(V, GSTR(A));
+  return gerepilecopy(av, V);
+}
+
+GEN
+ellsearchcurve(GEN name)
+{
+  pari_sp ltop=avma;
+  long f, c, i;
+  if (!ellparsename(GSTR(name),&f,&c,&i)) pari_err_TYPE("ellsearch",name);
+  if (f<0 || c<0 || i<0) pari_err_TYPE("ellsearch [incomplete name]", name);
+  return gerepilecopy(ltop, ellsearchbyname(ellcondlist(f), GSTR(name)));
+}
+
+GEN
+ellidentify(GEN E)
+{
+  pari_sp ltop=avma;
+  GEN V, M, G = ellglobalred(E), N = gel(G,1);
+  long j;
+  V = ellcondlist(itos(N));
+  M = ellchangecurve(vecslice(E,1,5),gel(G,2));
+  for (j=1; j<lg(V); j++)
+    if (ZV_equal(gmael(V,j,2), M))
+      return gerepilecopy(ltop, mkvec2(gel(V,j),gel(G,2)));
+  pari_err_BUG("ellidentify [missing curve]");
+  return NULL;
+}
+
+GEN
+elldatagenerators(GEN E)
+{
+  pari_sp ltop=avma;
+  GEN V=ellidentify(E);
+  GEN gens=gmael(V,1,3);
+  GEN W=ellchangepointinv(gens,gel(V,2));
+  return gerepileupto(ltop,W);
+}
+
+void
+forell(void *E, long call(void*, GEN), long a, long b)
+{
+  long ca=a/1000, cb=b/1000;
+  long i, j, k;
+
+  if (ca < 0) ca = 0;
+  for(i=ca; i<=cb; i++)
+  {
+    pari_sp ltop=avma;
+    GEN V = ellcondfile(i*1000);
+    for (j=1; j<lg(V); j++)
+    {
+      GEN ells = gel(V,j);
+      long cond= itos(gel(ells,1));
+
+      if (i==ca && cond<a) continue;
+      if (i==cb && cond>b) break;
+      for(k=2; k<lg(ells); k++)
+      {
+        if (call(E, gel(ells, k))) return;
+      }
+    }
+    avma = ltop;
+  }
+}
+
+void
+forell0(long a, long b, GEN code)
+{
+  push_lex(gen_0, code);
+  forell((void*)code, &gp_evalvoid, a, b);
+  pop_lex(1);
+}
diff --git a/src/modules/ellsea.c b/src/modules/ellsea.c
new file mode 100644
index 0000000..104ecc8
--- /dev/null
+++ b/src/modules/ellsea.c
@@ -0,0 +1,1536 @@
+/* Copyright (C) 2008  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file is a C version by Bill Allombert of the 'ellsea' GP package
+ * whose copyright statement is as follows:
+Authors:
+  Christophe Doche   <cdoche at math.u-bordeaux.fr>
+  Sylvain Duquesne <duquesne at math.u-bordeaux.fr>
+
+Universite Bordeaux I, Laboratoire A2X
+For the AREHCC project, see http://www.arehcc.com/
+
+Contributors:
+  Karim Belabas (code cleanup and package release, faster polynomial arithmetic)
+
+'ellsea' 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER. */
+
+/* Extension to non prime finite fields by Bill Allombert 2012 */
+
+#include "pari.h"
+#include "paripriv.h"
+
+static GEN modular_eqn;
+
+void
+pari_init_seadata(void)  { modular_eqn = NULL; }
+void
+pari_close_seadata(void) { if (modular_eqn) gunclone(modular_eqn); }
+
+static int
+FqX_equal(GEN x, GEN y) { return gequal(x,y); }
+
+static int
+FlxX_equal(GEN x, GEN y) { return gequal(x,y); }
+
+static char *
+seadata_filename(ulong ell)
+{ return stack_sprintf("%s/seadata/sea%ld", pari_datadir, ell); }
+
+static GEN
+get_seadata(ulong ell)
+{
+  pari_sp av=avma;
+  GEN eqn;
+  char *s = seadata_filename(ell);
+  pariFILE *F = pari_fopengz(s);
+  if (!F) return NULL;
+  if (ell==0)
+  {
+    eqn = gp_readvec_stream(F->file);
+    pari_fclose(F);
+    modular_eqn = gclone(eqn);
+    avma=av;
+    return gen_0;
+  } else {
+    eqn = gp_read_stream(F->file);
+    pari_fclose(F);
+    return eqn;
+  }
+}
+
+/*Builds the modular equation corresponding to the vector list. Shallow */
+static GEN
+list_to_pol(GEN list, long vx, long vy)
+{
+  long i, l = lg(list);
+  GEN P = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    GEN L = gel(list,i);
+    if (typ(L) == t_VEC) L = RgV_to_RgX_reverse(L, vy);
+    gel(P, i) = L;
+  }
+  return RgV_to_RgX_reverse(P, vx);
+}
+
+struct meqn { char type; GEN eq; };
+
+static int
+get_modular_eqn(struct meqn *M, ulong ell, long vx, long vy)
+{
+  GEN eqn;
+  long idx = uprimepi(ell)-1;
+  if (!modular_eqn && !get_seadata(0)) pari_err_PACKAGE("seadata");
+  if (idx && idx<lg(modular_eqn))
+    eqn = gel(modular_eqn, idx);
+  else
+    eqn = get_seadata(ell);
+  if (!eqn) return 0;
+  M->type = *GSTR(gel(eqn, 2));
+  M->eq = list_to_pol(gel(eqn, 3), vx, vy);
+  return 1;
+}
+
+static void
+err_modular_eqn(long ell)
+{ pari_err_FILE("seadata file", seadata_filename(ell)); }
+
+GEN
+ellmodulareqn(long ell, long vx, long vy)
+{
+  pari_sp av = avma;
+  struct meqn meqn;
+  if (vx<0) vx=0;
+  if (vy<0) vy=fetch_user_var("y");
+  if (varncmp(vx,vy)>=0)
+    pari_err_PRIORITY("ellmodulareqn", pol_x(vx), ">=", vy);
+  if (ell < 0 || !uisprime(ell))
+    pari_err_PRIME("ellmodulareqn (level)", stoi(ell));
+
+  if (!get_modular_eqn(&meqn, ell, vx, vy))
+    err_modular_eqn(ell);
+  return gerepilecopy(av,mkvec2(meqn.eq, stoi(meqn.type=='A')));
+}
+
+/*Gives the first precS terms of the Weierstrass series related to */
+/*E: y^2 = x^3 + a4x + a6.  Assumes (precS-2)*(2precS+3) < ULONG_MAX, i.e.
+ * precS < 46342 in 32-bit machines */
+static GEN
+find_coeff(GEN a4, GEN a6, GEN T, GEN p, long precS)
+{
+  GEN res = cgetg(precS+1, t_VEC);
+  long k, h;
+  if (precS == 0) return res;
+  gel(res, 1) = Fq_div(a4, stoi(-5), T, p);
+  if (precS == 1) return res;
+  gel(res, 2) = Fq_div(a6, stoi(-7), T, p);
+  for (k = 3; k <= precS; ++k)
+  {
+    pari_sp btop = avma;
+    GEN a = gen_0;
+    for (h = 1; h <= k-2; h++)
+      a = Fq_add(a, Fq_mul(gel(res, h), gel(res, k-1-h), T, p), T, p);
+    a = Fq_div(Fq_mulu(a, 3, T, p), utoi((k-2) * (2*k + 3)), T, p);
+    gel(res, k) = gerepileupto(btop, a);
+  }
+  return res;
+}
+
+/* Given power series s1 and s2, finds a polynomial P such that s2 = P(s1) */
+static GEN
+find_transformation(GEN s2, GEN s1)
+{
+  pari_sp ltop = avma, btop, st_lim;
+  long i, vx = varn(s1), vs1 = valp(s1), vs2 = valp(s2), degP = vs2/vs1;
+  GEN invs1coeff = ginv(gel(s1, 2)), P = gen_0, s1pl = cgetg(degP+1, t_VEC);
+
+  gel(s1pl, 1) = s1;
+  for (i = 2; i <= degP; i++) gel(s1pl, i) = gmul(s1, gel(s1pl, i-1));
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for (i = 0; i < degP; i++)
+  {
+    GEN Pcoeff = gmul(gel(s2,2), invs1coeff);
+    P = gadd(P, gmul(Pcoeff, monomial(gen_1, degP-i, vx)));
+    s2 = gsub(s2, gmul(Pcoeff, gel(s1pl, degP-i)));
+    if (low_stack(st_lim, stack_lim(btop, 1))) gerepileall(btop, 2, &P, &s2);
+  }
+  P = gadd(P, gmul(gel(s2,2), invs1coeff));
+  return gerepileupto(ltop, P);
+}
+
+static GEN
+compute_W(GEN a4, GEN a6, GEN T, GEN p, long vx, long precS)
+{
+  pari_sp ltop = avma;
+  GEN c  = find_coeff(a4, a6, T, p, precS);
+  GEN s  = RgX_inflate(RgV_to_RgX(c,vx), 2);
+  GEN z2 = monomial(gen_1, 2, vx);
+  s = gadd(gadd(ginv(z2), gmul(s, z2)), zeroser(vx, 2*precS));
+  return gerepileupto(ltop, s);
+}
+
+/*Finds numerator phi of the isogeny between Eb and Ec whose denominator is h*/
+static GEN
+find_numerator_isogeny(GEN Eba4, GEN Eba6, GEN Eca4, GEN Eca6, GEN h, GEN T, GEN p,
+                       long precS)
+{
+  pari_sp ltop = avma;
+  GEN mod1p = gmodulsg(1,p);
+  GEN mod = T ? gmodulo(mod1p, gmul(get_FpX_mod(T), mod1p)): mod1p;
+  GEN WEb = gmul(compute_W(Eba4, Eba6, T, p, varn(h), precS), mod);
+  GEN WEc = gmul(compute_W(Eca4, Eca6, T, p, varn(h), precS), mod);
+  GEN den = poleval(h, WEb);
+  return gerepileupto(ltop, find_transformation(gmul(gsqr(den), WEc), WEb));
+}
+
+/****************************************************************************/
+/*               SIMPLE ELLIPTIC CURVE OVER Fq                              */
+/****************************************************************************/
+
+static GEN
+Fq_ellj(GEN a4, GEN a6, GEN T, GEN p)
+{
+  pari_sp ltop=avma;
+  GEN a43 = Fq_mulu(Fq_powu(a4, 3, T, p), 4, T, p);
+  GEN j   = Fq_div(Fq_mulu(a43, 1728, T, p),
+                   Fq_add(a43, Fq_mulu(Fq_sqr(a6, T, p), 27, T, p), T, p), T, p);
+  return gerepileupto(ltop, j);
+}
+
+/****************************************************************************/
+/*                              EIGENVALUE                                  */
+/****************************************************************************/
+
+struct eigen_ellinit
+{
+  GEN a4, h, T, p;
+  GEN RHS, DRHS, X12, Gr, nGr,O;
+  ulong pp;
+};
+
+static void
+init_eigen(struct eigen_ellinit *Edat, GEN a4, GEN a6, GEN h, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN RHS  = FqX_rem(mkpoln(4, gen_1, gen_0, a4, a6), h, T, p);
+  GEN DRHS = FqX_rem(mkpoln(3, utoi(3), gen_0, a4), h, T, p);
+  GEN lambda = FqXQ_div(DRHS, FqX_mulu(RHS, 4, T, p), h, T, p);
+  GEN C = FqX_sub(FqXQ_mul(lambda, DRHS, h, T, p), monomial(gen_2,1,0), T, p);
+  GEN D = FqXQ_mul(FqX_mulu(lambda, 2, T, p),FqX_sub(pol_x(0), C, T, p), h, T, p);
+  GEN X12 = mkvec2(C, FqX_Fq_add(D, gen_m1, T, p));
+  GEN Gr = T ? FpXQXQ_halfFrobenius(RHS, h, T, p):
+               FpXQ_pow(RHS, shifti(p, -1), h, p);
+  GEN nGr = FqX_neg(Gr, T, p);
+  gerepileall(ltop, 5, &RHS, &DRHS, &X12, &Gr, &nGr);
+  Edat->a4    = gcopy(a4);
+  Edat->h     = gcopy(h);
+  Edat->T     = T;
+  Edat->p     = p;
+  Edat->pp    = 0;
+  Edat->RHS   = RHS;
+  Edat->DRHS  = DRHS;
+  Edat->X12   = X12;
+  Edat->Gr    = Gr;
+  Edat->nGr   = nGr;
+  Edat->O     = mkvec2(pol_x(0), pol_1(0));
+}
+
+static void
+init_eigenu(struct eigen_ellinit *Edat, GEN a4, GEN a6, GEN h, GEN T, ulong p)
+{
+  pari_sp ltop = avma;
+  long vT = get_Flx_var(T);
+  GEN g1 = pol1_Flx(vT), g0 = pol0_Flx(vT);
+  GEN RHS  = FlxqX_rem(mkpoln(4, g1, g0, a4, a6), h, T, p);
+  GEN DRHS = FlxqX_rem(mkpoln(3, Fl_to_Flx(3, T[1]), g0, a4), h, T, p);
+  GEN lambda = FlxqXQ_div(DRHS, FlxX_Fl_mul(RHS, 4, p), h, T, p);
+  GEN C = FlxX_sub(FlxqXQ_mul(lambda, DRHS, h, T, p), monomial(Fl_to_Flx(2,vT),1,0), p);
+  GEN D = FlxqXQ_mul(FlxX_double(lambda, p),FlxX_sub(pol_x(0), C, p), h, T, p);
+  GEN X12 = mkvec2(C, FlxX_Flx_add(D, Fl_to_Flx(p-1,vT), p));
+  GEN Gr = FlxqXQ_halfFrobenius(RHS,h,T,p);
+  GEN nGr = FlxX_neg(Gr, p);
+  GEN O = mkvec2(monomial(g1,1,0), monomial(g1,0,0));
+  gerepileall(ltop, 6, &RHS, &DRHS, &X12, &Gr, &nGr, &O);
+  Edat->a4    = gcopy(a4);
+  Edat->h     = gcopy(h);
+  Edat->T     = T;
+  Edat->p     = NULL;
+  Edat->pp    = p;
+  Edat->RHS   = RHS;
+  Edat->DRHS  = DRHS;
+  Edat->X12   = X12;
+  Edat->Gr    = Gr;
+  Edat->nGr   = nGr;
+  Edat->O     = O;
+}
+static GEN
+eigen_elldbl(void *E, GEN P)
+{
+  pari_sp ltop = avma;
+  struct eigen_ellinit *Edat=(struct eigen_ellinit *)E;
+  GEN T = Edat->T, p = Edat->p, h = Edat->h, x, y;
+  if (ell_is_inf(P)) return gcopy(P);
+  x = gel(P,1), y = gel(P,2);
+  if (FqX_equal(x, pol_x(0)) && FqX_equal(y, pol_1(0)))
+    return Edat->X12;
+  else
+  {
+    GEN t1 = FqX_Fq_add(FqX_mulu(FqXQ_sqr(x,h,T,p),3,T, p), Edat->a4, T, p);
+    GEN t2 = FqXQ_mul(FqX_mulu(y, 2, T, p), Edat->RHS, h, T, p);
+    GEN lambda = FqXQ_div(t1, t2, h, T, p);
+    GEN C = FqX_sub(FqXQ_mul(FqXQ_sqr(lambda, h, T, p), Edat->RHS, h, T, p),
+                    FqX_mulu(x, 2, T, p), T, p);
+    GEN D = FqX_sub(FqXQ_mul(lambda, FqX_sub(x, C, T, p), h, T, p), y, T, p);
+    return gerepilecopy(ltop, mkvec2(C,D));
+  }
+}
+
+/* Returns the addition of [P[1], P[2]*Y] and of [Q[1], Q[2]*Y]
+ * Computations are done modulo Y^2 - (X^3 + a4X + a6)
+ * An inversion is equivalent to 4M, so that this function requires about 7M
+ * which is the same as with the method using ell-division polynomials
+ * Working in mixed projective coordinates would require 11M */
+static GEN
+eigen_elladd(void *E, GEN P, GEN Q)
+{
+  pari_sp ltop = avma;
+  struct eigen_ellinit *Edat=(struct eigen_ellinit *)E;
+  GEN Px, Py, Qx, Qy;
+  GEN T = Edat->T, p = Edat->p, h = Edat->h, lambda, C, D;
+  if (ell_is_inf(P)) return gcopy(Q);
+  if (ell_is_inf(Q)) return gcopy(P);
+  Px = gel(P,1); Py = gel(P,2);
+  Qx = gel(Q,1); Qy = gel(Q,2);
+  if (FqX_equal(Px, Qx))
+  {
+    if (FqX_equal(Py, Qy))
+      return eigen_elldbl(E, P);
+    else
+      return ellinf();
+  }
+  lambda = FqXQ_div(FqX_sub(Py, Qy, T, p), FqX_sub(Px, Qx, T, p), h, T, p);
+  C = FqX_sub(FqX_sub(FqXQ_mul(FqXQ_sqr(lambda, h, T, p), Edat->RHS, h, T, p), Px, T, p), Qx, T, p);
+  D = FqX_sub(FqXQ_mul(lambda, FqX_sub(Px, C, T, p), h, T, p), Py, T, p);
+  return gerepilecopy(ltop, mkvec2(C,D));
+}
+
+static GEN
+eigenu_elldbl(void *E, GEN P)
+{
+  pari_sp ltop = avma;
+  struct eigen_ellinit *Edat=(struct eigen_ellinit *)E;
+  GEN T = Edat->T, h = Edat->h, x, y;
+  long vT = get_Flx_var(T);
+  ulong p = Edat->pp;
+  if (ell_is_inf(P)) return gcopy(P);
+  x = gel(P,1), y = gel(P,2);
+  if (FlxX_equal(x, monomial(pol1_Flx(vT),1,0)) && FlxX_equal(y, monomial(pol1_Flx(vT),0,0)))
+    return Edat->X12;
+  else
+  {
+    GEN t1 = FlxX_Flx_add(FlxX_triple(FlxqXQ_sqr(x,h,T,p),p), Edat->a4, p);
+    GEN t2 = FlxqXQ_mul(FlxX_double(y, p), Edat->RHS, h, T, p);
+    GEN lambda = FlxqXQ_div(t1, t2, h, T, p);
+    GEN C = FlxX_sub(FlxqXQ_mul(FlxqXQ_sqr(lambda, h, T, p), Edat->RHS, h, T, p),
+                     FlxX_double(x, p), p);
+    GEN D = FlxX_sub(FlxqXQ_mul(lambda, FlxX_sub(x, C, p), h, T, p), y, p);
+    return gerepilecopy(ltop, mkvec2(C,D));
+  }
+}
+
+/* Returns the addition of [P[1], P[2]*Y] and of [Q[1], Q[2]*Y]
+ * Computations are done modulo Y^2 - (X^3 + a4X + a6)
+ * An inversion is equivalent to 4M, so that this function requires about 7M
+ * which is the same as with the method using ell-division polynomials
+ * Working in mixed projective coordinates would require 11M */
+static GEN
+eigenu_elladd(void *E, GEN P, GEN Q)
+{
+  pari_sp ltop = avma;
+  struct eigen_ellinit *Edat=(struct eigen_ellinit *)E;
+  GEN Px, Py, Qx, Qy;
+  GEN T = Edat->T, h = Edat->h, lambda, C, D;
+  ulong p = Edat->pp;
+  if (ell_is_inf(P)) return gcopy(Q);
+  if (ell_is_inf(Q)) return gcopy(P);
+  Px = gel(P,1); Py = gel(P,2);
+  Qx = gel(Q,1); Qy = gel(Q,2);
+  if (FlxX_equal(Px, Qx))
+  {
+    if (FlxX_equal(Py, Qy))
+      return eigenu_elldbl(E, P);
+    else
+      return ellinf();
+  }
+  lambda = FlxqXQ_div(FlxX_sub(Py, Qy, p), FlxX_sub(Px, Qx, p), h, T, p);
+  C = FlxX_sub(FlxX_sub(FlxqXQ_mul(FlxqXQ_sqr(lambda, h, T, p), Edat->RHS, h, T, p), Px, p), Qx, p);
+  D = FlxX_sub(FlxqXQ_mul(lambda, FlxX_sub(Px, C, p), h, T, p), Py, p);
+  return gerepilecopy(ltop, mkvec2(C,D));
+}
+
+static GEN
+eigen_ellmulu(struct eigen_ellinit *E, GEN z, ulong n)
+{
+  pari_sp av = avma;
+  if (!n || ell_is_inf(z)) return mkvec(gen_0);
+  if (n == 1) return gcopy(z);
+  if (E->pp)
+    return gerepileupto(av, gen_powu(z, n, E, &eigenu_elldbl, &eigenu_elladd));
+  else
+    return gerepileupto(av, gen_powu(z, n, E, &eigen_elldbl, &eigen_elladd));
+}
+
+/*Finds the eigenvalue of the Frobenius given E, ell odd prime, h factor of the
+ *ell-division polynomial, p and tr the possible values for the trace
+ *(useful for primes with one root)*/
+static ulong
+find_eigen_value(GEN a4, GEN a6, ulong ell, GEN h, GEN T, GEN p, GEN tr)
+{
+  pari_sp ltop = avma;
+  GEN BP, Dr;
+  ulong t;
+  struct eigen_ellinit Edat;
+  ulong pp = T ?itou_or_0(p): 0;
+  if (pp)
+    init_eigenu(&Edat, ZX_to_Flx(a4,pp), ZX_to_Flx(a6,pp),
+                       ZXX_to_FlxX(h,pp, get_FpX_var(T)), ZXT_to_FlxT(T,pp), pp);
+  else
+    init_eigen(&Edat, a4, a6, h, T, p);
+  Dr = BP = Edat.O;
+  /*[0,Gr], BP, Dr are not points on the curve. */
+  /*To obtain the corresponding points, multiply the y-coordinates by Y */
+  if (!tr || lg(tr)==1)
+  {
+    pari_sp btop = avma;
+    for (t = 1; t <= (ell>>1); t++)
+    {
+      if (gequal(gel(Dr,2), Edat.Gr))  { avma = ltop; return t; }
+      if (gequal(gel(Dr,2), Edat.nGr)) { avma = ltop; return ell-t; }
+      Dr = pp ? eigenu_elladd(&Edat, Dr, BP): eigen_elladd(&Edat, Dr, BP);
+      Dr = gerepileupto(btop, Dr);
+    }
+    pari_err_BUG("find_eigen_value");
+    return 0; /* NOT REACHED */
+  }
+  else
+  {
+    t = Fl_div(tr[1], 2, ell);
+    if (t < (ell>>1)) t = ell - t;
+    Dr = eigen_ellmulu(&Edat, BP, t);
+    if (!gequal(gel(Dr,2), Edat.Gr)) t = ell - t;
+    avma = ltop; return t;
+  }
+}
+
+/*Finds the eigenvalue of the Frobenius modulo ell^k given E, ell, k, h factor
+ *of the ell-division polynomial, lambda the previous eigen value and p */
+static ulong
+find_eigen_value_power(GEN a4, GEN a6, ulong ell, long k, GEN h, ulong lambda, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  pari_sp btop, st_lim;
+  struct eigen_ellinit Edat;
+  GEN BP, Dr, Gr, nGr;
+  /*[0,Gr], BP, Dr are not points on the curve. */
+  /*To obtain the corresponding points, multiply the y-coordinates by Y */
+  ulong t, ellk1 = upowuu(ell, k-1), ellk = ell*ellk1;
+  ulong pp = T ?itou_or_0(p): 0;
+  if (pp)
+    init_eigenu(&Edat, ZX_to_Flx(a4,pp), ZX_to_Flx(a6,pp),
+        ZXX_to_FlxX(h, pp, get_FpX_var(T)), ZXT_to_FlxT(T,pp), pp);
+  else
+    init_eigen(&Edat, a4, a6, h, T, p);
+  BP = eigen_ellmulu(&Edat, Edat.O, ellk1);
+  Dr = eigen_ellmulu(&Edat, Edat.O, lambda);
+  Gr = Edat.Gr; nGr = Edat.nGr;
+
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for (t = 0; t < ellk; t += ellk1)
+  {
+    if (gequal(gel(Dr,2), Gr))  { avma = ltop; return t+lambda; }
+    if (gequal(gel(Dr,2), nGr)) { avma = ltop; return ellk-(t+lambda); }
+    Dr = pp ? eigenu_elladd(&Edat, Dr, BP): eigen_elladd(&Edat, Dr, BP);
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+      Dr = gerepileupto(btop, Dr);
+  }
+  pari_err_BUG("find_eigen_value_power");
+  return 0; /* NOT REACHED */
+}
+
+/*Finds the kernel polynomial h, dividing the ell-division polynomial from the
+  isogenous curve Eb and trace term pp1. Uses CCR algorithm and returns h.
+  Return NULL if E and Eb are *not* isogenous. */
+static GEN
+find_kernel(GEN a4, GEN a6, ulong ell, GEN a4t, GEN a6t, GEN pp1, GEN T, GEN p)
+{
+  const long ext = 2;
+  pari_sp ltop = avma;
+  GEN M, N, V, K, K1, K2, v, tlist, res;
+  long i, j, k;
+  long deg = (ell - 1)/2, dim = deg + ext;
+  GEN Coeff  = find_coeff(a4, a6, T, p, dim);
+  GEN Coefft = find_coeff(a4t, a6t, T, p, dim);
+  GEN psi2  = mkpoln(4, utoi(4), gen_0, Fq_mulu(a4, 4, T, p), Fq_mulu(a6, 4, T, p));
+  GEN list  = cgetg(dim+1, t_VEC);
+  GEN Dpsi2 = mkpoln(3, utoi(6), gen_0, Fq_mulu(a4, 2, T, p));
+  gel(list, 1) = Dpsi2;
+  for (k = 2; k <= dim; k++)
+  {
+    pari_sp btop = avma;
+    GEN tsil = gel(list, k-1);
+    GEN r = FqX_Fq_mul(Dpsi2, gel(tsil,3), T, p);
+    for (j = 4; j < lg(tsil); j++)
+    {
+      long o = j - 2;
+      GEN D = FqX_add(RgX_shift_shallow(Dpsi2, 1), FqX_mulu(psi2, o-1, T, p), T, p);
+      GEN E = FqX_Fq_mul(D, Fq_mulu(gel(tsil, j), o, T, p), T, p);
+      r = FqX_add(r, RgX_shift_shallow(E, o-2), T, p);
+    }
+    gel(list, k) = gerepileupto(btop, r);
+  }
+  for (k = 2; k <= dim; k++)
+  {
+     GEN C = Fq_inv(shifti(mpfact(2*k),-1), T, p);
+     gel(list, k) = FqX_Fq_mul(gel(list, k), C, T, p);
+  }
+  M = shallowtrans(RgXV_to_RgM(list, dim+2));
+  N = vecslice(M, 1, dim);
+  V = FqC_sub(Coefft, Coeff, T, p);
+  v = shallowconcat(FqM_FqC_gauss(N, V, T, p), mkcol2(gen_0, gen_0));
+  K = FqM_ker(M, T, p);
+  if (lg(K) != 3) pari_err_BUG("trace not determined in a unique way");
+  K1 = FqC_Fq_mul(gel(K,1), Fq_inv(gcoeff(K,1,1), T, p), T, p);
+  K2 = FqC_sub(gel(K,2), FqC_Fq_mul(K1, gcoeff(K,1,2), T, p), T, p);
+  K2 = FqC_Fq_mul(K2, Fq_inv(gel(K2,2), T, p), T, p);
+  K1 = FqC_sub(K1, FqC_Fq_mul(K2, gel(K1,2), T, p), T, p);
+  v = FqC_add(v, FqC_Fq_mul(K1, Fq_sub(utoi(deg), gel(v,1), T, p), T, p), T, p);
+  v = FqC_add(v, FqC_Fq_mul(K2, Fq_sub(pp1, gel(v,2), T, p), T, p), T, p);
+  tlist = cgetg(dim+2, t_VEC);
+  gel(tlist, dim+1) = gen_1;
+  for (k = 1; k <= dim; k++)
+  {
+    pari_sp btop = avma;
+    GEN s = gel(v, k+1);
+    for (i = 1; i < k; i++)
+      s = Fq_add(s, Fq_mul(gel(tlist, dim-i+1), gel(v, k-i+1), T, p), T, p);
+    gel(tlist, dim-k+1) = gerepileupto(btop, Fq_div(s, stoi(-k), T, p));
+  }
+  for (i = 1; i <= ext; i++)
+    if (signe(gel(tlist, i))) { avma = ltop; return NULL; }
+  res = vecslice(tlist, ext+1, dim+1);
+
+  return RgV_to_RgX(res, 0);
+}
+
+static GEN
+compute_u(GEN gprime, GEN Dxxg, GEN DxJg, GEN DJJg, GEN j, GEN pJ, GEN px, ulong q, GEN E4, GEN E6, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  GEN dxxgj = FqX_eval(Dxxg, j, T, p);
+  GEN dxJgj = FqX_eval(DxJg, j, T, p);
+  GEN dJJgj = FqX_eval(DJJg, j, T, p);
+  GEN E42 = Fq_sqr(E4, T, p), E6ovE4 = Fq_div(E6, E4, T, p);
+  GEN a = Fq_mul(gprime, dxxgj, T, p);
+  GEN b = Fq_mul(Fq_mul(Fq_mulu(j,2*q, T, p), dxJgj, T, p), E6ovE4, T, p);
+  GEN c = Fq_mul(Fq_div(Fq_sqr(E6ovE4, T, p), gprime, T, p), j, T, p);
+  GEN d = Fq_mul(Fq_mul(c,sqru(q), T, p), Fq_add(pJ, Fq_mul(j, dJJgj, T, p), T, p), T, p);
+  GEN e = Fq_sub(Fq_div(E6ovE4,utoi(3), T, p), Fq_div(E42, Fq_mulu(E6,2,T, p), T, p), T, p);
+  GEN f = Fq_sub(Fq_sub(b,a,T,p), d, T, p);
+  return gerepileupto(ltop, Fq_add(Fq_div(f,px,T,p), Fq_mulu(e,q,T,p), T, p));
+}
+
+/* Finds the isogenous EC, and the sum of the x-coordinates of the points in
+ * the kernel of the isogeny E -> Eb
+ * E: elliptic curve, ell: a prime, meqn: Atkin modular equation
+ * g: root of meqn defining isogenous curve Eb. */
+static GEN
+find_isogenous_from_Atkin(GEN a4, GEN a6, long ell, GEN meqn, GEN g, GEN T, GEN p)
+{
+  pari_sp ltop = avma, btop;
+  GEN Roots, gprime, u1;
+  long k, vx = 0, vJ = MAXVARN;
+  GEN E4 = Fq_div(a4, stoi(-3), T, p);
+  GEN E6 = Fq_mul(a6, shifti(p, -1), T, p);
+  GEN E42 = Fq_sqr(E4, T, p);
+  GEN E43 = Fq_mul(E4, E42, T, p);
+  GEN E62 = Fq_sqr(E6, T, p);
+  GEN delta = Fq_div(Fq_sub(E43, E62, T, p), utoi(1728), T, p);
+  GEN j = Fq_div(E43, delta, T, p);
+  GEN Dx = deriv(meqn, vx);
+  GEN DJ = deriv(meqn, vJ);
+  GEN Dxg = FpXY_Fq_evaly(Dx, g, T, p, vJ);
+  GEN px = FqX_eval(Dxg, j, T, p), dx = Fq_mul(px, g, T, p);
+  GEN DJg = FpXY_Fq_evaly(DJ, g, T, p, vJ);
+  GEN pJ = FqX_eval(DJg, j, T, p), dJ = Fq_mul(pJ, j, T, p);
+  GEN Dxx = deriv(Dx, vx);
+  GEN DxJg = FqX_deriv(Dxg, T, p);
+
+  GEN Dxxg = FpXY_Fq_evaly(Dxx, g, T, p, vJ);
+  GEN DJJg = FqX_deriv(DJg, T, p);
+
+  GEN a = Fq_mul(dJ, Fq_mul(g, E6, T, p), T, p);
+  GEN b = Fq_mul(E4, dx, T, p);
+  if (!signe(a) || !signe(b))
+  { /* TODO: understand what this means and use the information */
+    if (DEBUGLEVEL)
+      err_printf("find_isogenous_from_Atkin: division by zero at prime %ld", ell);
+    avma = ltop; return NULL;
+  }
+  gprime = Fq_div(a, b, T, p);
+
+  u1 = compute_u(gprime, Dxxg, DxJg, DJJg, j, pJ, px, 1, E4, E6, T, p);
+  Roots = FqX_roots(FpXY_Fq_evaly(meqn, g, T, p, vJ), T, p);
+  btop = avma;
+  for (k = lg(Roots)-1; k >= 1; k--, avma = btop)
+  {
+    GEN jt = gel(Roots, k);
+    GEN pxstar = FqX_eval(Dxg, jt, T, p);
+    GEN dxstar = Fq_mul(pxstar, g, T, p);
+    GEN pJstar = FqX_eval(DJg, jt, T, p);
+    GEN dJstar = Fq_mul(Fq_mulu(jt, ell, T, p), pJstar, T, p);
+    GEN u = Fq_mul(Fq_mul(dxstar, dJ, T, p), E6, T, p);
+    GEN v = Fq_mul(Fq_mul(dJstar, dx, T, p), E4, T, p);
+    GEN E4t = Fq_div(Fq_mul(Fq_sqr(u, T, p), jt, T, p), Fq_mul(Fq_sqr(v, T, p), Fq_sub(jt, utoi(1728), T, p), T, p), T, p);
+    GEN E6t = Fq_div(Fq_mul(u, E4t, T, p), v, T, p);
+    GEN u2 = compute_u(gprime, Dxxg, DxJg, DJJg, jt, pJstar, pxstar, ell, E4t, E6t, T, p);
+    GEN pp1 = Fq_mulu(Fq_sub(u1, u2, T, p), 3*ell, T, p);
+    GEN a4t = Fq_mul(mulsi(-3, powuu(ell,4)), E4t, T, p);
+    GEN a6t = Fq_mul(mulsi(-2, powuu(ell,6)), E6t, T, p);
+    GEN h = find_kernel(a4, a6, ell, a4t, a6t, pp1, T, p);
+    if (h) return gerepilecopy(ltop, mkvec3(a4t, a6t, h));
+  }
+  pari_err_BUG("find_isogenous_from_Atkin, kernel not found");
+  return NULL;
+}
+
+/* Finds E' ell-isogenous to E and the trace term p1 from canonical modular
+ *   equation meqn
+ * E: elliptic curve, ell: a prime, meqn: canonical modular equation
+ * g: root of meqn defining isogenous curve Eb. */
+static GEN
+find_isogenous_from_canonical(GEN a4, GEN a6, long ell, GEN meqn, GEN g, GEN T, GEN p)
+{
+  pari_sp ltop = avma;
+  long vx = 0, vJ = MAXVARN;
+  GEN h;
+  GEN E4 = Fq_div(a4, stoi(-3), T, p);
+  GEN E6 = Fq_mul(a6, shifti(p, -1), T, p);
+  GEN E42 = Fq_sqr(E4, T, p);
+  GEN E43 = Fq_mul(E4, E42, T, p);
+  GEN E62 = Fq_sqr(E6, T, p);
+  GEN delta = Fq_div(Fq_sub(E43, E62, T, p), utoi(1728), T, p);
+  GEN j = Fq_div(E43, delta, T, p);
+  GEN Dx = deriv(meqn, vx);
+  GEN DJ = deriv(meqn, vJ);
+  GEN Dxg = FpXY_Fq_evaly(Dx, g, T, p, vJ);
+  GEN px  = FqX_eval(Dxg, j, T, p), dx  = Fq_mul(px, g, T, p);
+  GEN DJg = FpXY_Fq_evaly(DJ, g, T, p, vJ);
+  GEN pJ = FqX_eval(DJg, j, T, p), dJ = Fq_mul(j, pJ, T, p);
+  GEN Dxx = deriv(Dx, vx);
+  GEN DxJg = FqX_deriv(Dxg, T, p);
+
+  GEN ExJ = FqX_eval(DxJg, j, T, p);
+  ulong tis = ugcd(12, ell-1), is = 12 / tis;
+  GEN itis = Fq_inv(stoi(-tis), T, p);
+  GEN deltal = Fq_div(Fq_mul(delta, Fq_powu(g, tis, T, p), T, p), powuu(ell, 12), T, p);
+  GEN E4l, E6l, a4tilde, a6tilde, p_1;
+  if (signe(dJ)==0)
+  {
+    GEN jl;
+    if (DEBUGLEVEL) err_printf("Division by zero for prime %Ps\n", T, p);
+    E4l = Fq_div(E4, sqru(ell), T, p);
+    jl  = Fq_div(Fq_powu(E4l, 3, T, p), deltal, T, p);
+    E6l = Fq_sqrt(Fq_mul(Fq_sub(jl, utoi(1728), T, p), deltal, T, p), T, p);
+    p_1 = gen_0;
+  }
+  else
+  {
+    GEN jl, f, fd, Dgs, Djs, jld;
+    GEN E2s = Fq_div(Fq_mul(Fq_neg(Fq_mulu(E6, 12, T, p), T, p), dJ, T, p), Fq_mul(Fq_mulu(E4, is, T, p), dx, T, p), T, p);
+    GEN gd = Fq_mul(Fq_mul(E2s, itis, T, p), g, T, p);
+    GEN jd = Fq_div(Fq_mul(Fq_neg(E42, T, p), E6, T, p), delta, T, p);
+    GEN E0b = Fq_div(E6, Fq_mul(E4, E2s, T, p), T, p);
+    GEN Dxxgj = FqXY_eval(Dxx, g, j, T, p);
+    GEN Dgd = Fq_add(Fq_mul(gd, px, T, p), Fq_mul(g, Fq_add(Fq_mul(gd, Dxxgj, T, p), Fq_mul(jd, ExJ, T, p), T, p), T, p), T, p);
+    GEN DJgJj = FqX_eval(FqX_deriv(DJg, T, p), j, T, p);
+    GEN Djd = Fq_add(Fq_mul(jd, pJ, T, p), Fq_mul(j, Fq_add(Fq_mul(jd, DJgJj, T, p), Fq_mul(gd, ExJ, T, p), T, p), T, p), T, p);
+    GEN E0bd = Fq_div(Fq_sub(Fq_mul(Dgd, itis, T, p), Fq_mul(E0b, Djd, T, p), T, p), dJ, T, p);
+    E4l = Fq_div(Fq_sub(E4, Fq_mul(E2s, Fq_sub(Fq_sub(Fq_add(Fq_div(Fq_mulu(E0bd, 12, T, p), E0b, T, p), Fq_div(Fq_mulu(E42, 6, T, p), E6, T, p), T, p), Fq_div(Fq_mulu(E6, 4, T, p), E4, T, p), T, p), E2s, T, p), T, p), T, p), sqru(ell), T, p);
+    jl = Fq_div(Fq_powu(E4l, 3, T, p), deltal, T, p);
+    f =  Fq_div(powuu(ell, is), g, T, p);
+    fd = Fq_neg(Fq_mul(Fq_mul(E2s, f, T, p), itis, T, p), T, p);
+    Dgs = FqXY_eval(Dx, f, jl, T, p);
+    Djs = FqXY_eval(DJ, f, jl, T, p);
+    jld = Fq_div(Fq_mul(Fq_neg(fd, T, p), Dgs, T, p), Fq_mulu(Djs, ell, T, p), T, p);
+    E6l = Fq_div(Fq_mul(Fq_neg(E4l, T, p), jld, T, p), jl, T, p);
+    p_1 = Fq_mul(Fq_mulu(E2s, ell, T, p), shifti(p, -1), T, p);
+  }
+  a4tilde = Fq_mul(Fq_mul(stoi(-3), powuu(ell,4), T, p), E4l, T, p);
+  a6tilde = Fq_mul(Fq_mul(stoi(-2), powuu(ell,6), T, p), E6l, T, p);
+  h = find_kernel(a4, a6, ell, a4tilde, a6tilde, p_1, T, p);
+  return gerepilecopy(ltop, mkvec3(a4tilde, a6tilde, h));
+}
+
+static GEN
+find_isogenous(GEN a4, GEN a6, long ell, struct meqn *MEQN, GEN g, GEN T, GEN p)
+{
+  return (MEQN->type == 'C')
+    ? find_isogenous_from_canonical(a4, a6, ell, MEQN->eq, g, T, p)
+    : find_isogenous_from_Atkin(a4, a6, ell, MEQN->eq, g, T, p);
+}
+
+static GEN
+find_kernel_power(GEN Eba4, GEN Eba6, GEN Eca4, GEN Eca6, ulong ell, struct meqn *MEQN, GEN kpoly, GEN Ib, GEN T, GEN p)
+{
+  pari_sp ltop = avma, btop;
+  GEN a4t, a6t, gtmp;
+  GEN num_iso = find_numerator_isogeny(Eba4, Eba6, Eca4, Eca6, kpoly, T, p, ell+1);
+  GEN mpoly = FqXY_evalx(MEQN->eq, Fq_ellj(Eca4, Eca6, T, p), T, p);
+  GEN tmp, mroots = FqX_roots(mpoly, T, p);
+  long i, vx = 0, l1 = lg(mroots);
+  btop = avma;
+  for (i = 1; i < l1; i++)
+  {
+    GEN kpoly2, h;
+    tmp = find_isogenous(Eca4, Eca6, ell, MEQN, gel(mroots, i), T, p);
+    if (!tmp) { avma = ltop; return NULL; }
+    a4t =  gel(tmp, 1);
+    a6t =  gel(tmp, 2);
+    gtmp = gel(tmp, 3);
+
+    /*check that the kernel kpoly is the good one */
+    kpoly2 = FqX_sqr(kpoly, T, p);
+    h = liftall_shallow(numer(gsubst(gtmp, vx, gdiv(num_iso, kpoly2))));
+    if (signe(Fq_elldivpolmod(Eba4, Eba6, ell, h, T, p)))
+    {
+      GEN Ic = gdiv(gsubst(num_iso, vx, Ib), gsqr(gsubst(kpoly, vx, Ib)));
+      GEN kpoly_new = liftall_shallow(numer(gsubst(gtmp, vx, Ic)));
+      return gerepilecopy(ltop, mkvecn(5, a4t, a6t, kpoly_new, gtmp, Ic));
+    }
+    avma = btop;
+  }
+  pari_err_BUG("failed to find kernel polynomial");
+  return NULL; /*NOT REACHED*/
+}
+
+/****************************************************************************/
+/*                                  TRACE                                   */
+/****************************************************************************/
+enum mod_type {MTpathological, MTAtkin, MTElkies, MTone_root, MTroots};
+
+static GEN
+Flxq_study_eqn(long ell, GEN mpoly, GEN T, ulong p, long *pt_dG, long *pt_r)
+{
+  GEN Xq = FlxqX_Frobenius(mpoly, T, p);
+  GEN G  = FlxqX_gcd(FlxX_sub(Xq, pol_x(0), p), mpoly, T, p);
+  *pt_dG = degpol(G);
+  if (!*pt_dG)
+  {
+    GEN L = FlxqXQ_matrix_pow(Xq, ell+1, ell+1, mpoly, T, p);
+    long vT = get_Flx_var(T);
+    long s = ell + 1 - FlxqM_rank(FlxM_Flx_add_shallow(L, Fl_to_Flx(p-1, vT), p), T, p);
+    *pt_r = (ell + 1)/s;
+    return NULL;
+  }
+  return G;
+}
+
+static GEN
+Fp_study_eqn(long ell, GEN mpoly, GEN p, long *pt_dG, long *pt_r)
+{
+  GEN XP = FpXQ_pow(pol_x(0), p, mpoly, p);
+  GEN G  = FpX_gcd(FpX_sub(XP, pol_x(0), p), mpoly, p);
+  *pt_dG = degpol(G);
+  if (!*pt_dG)
+  {
+    GEN L = FpXQ_matrix_pow(XP, ell+1, ell+1, mpoly, p);
+    long s = ell + 1 - FpM_rank(RgM_Rg_add_shallow(L, gen_m1), p);
+    *pt_r = (ell + 1)/s;
+    return NULL;
+  }
+  return FpX_oneroot(G, p);
+}
+
+static GEN
+FpXQ_study_eqn(long ell, GEN mpoly, GEN T, GEN p, long *pt_dG, long *pt_r)
+{
+  GEN G;
+  if (lgefint(p)==3)
+  {
+    ulong pp = p[2];
+    GEN Tp = ZXT_to_FlxT(T,pp);
+    GEN mpolyp = ZXX_to_FlxX(mpoly,pp,get_FpX_var(T));
+    G = Flxq_study_eqn(ell, mpolyp, Tp, pp, pt_dG, pt_r);
+    if (!G) return NULL;
+    G = FlxX_to_ZXX(G);
+  }
+  else
+  {
+    GEN Xq = FpXQX_Frobenius(mpoly, T, p);
+    G  = FpXQX_gcd(FpXX_sub(Xq, pol_x(0), p), mpoly, T, p);
+    *pt_dG = degpol(G);
+    if (!*pt_dG)
+    {
+      GEN L = FpXQXQ_matrix_pow(Xq, ell+1, ell+1, mpoly, T, p);
+      long s = ell + 1 - FqM_rank(RgM_Rg_add(L, gen_m1), T, p);
+      *pt_r = (ell + 1)/s;
+      return NULL;
+    }
+  }
+  return gel(FqX_roots(G, T, p), 1);
+}
+
+/* Berlekamp variant */
+static GEN
+study_modular_eqn(long ell, GEN mpoly, GEN T, GEN p, enum mod_type *mt, long *ptr_r)
+{
+  pari_sp ltop = avma;
+  GEN g = gen_0;
+  *ptr_r = 0; /*gcc -Wall*/
+  if (degpol(FqX_gcd(mpoly, FqX_deriv(mpoly, T, p), T, p)) > 0)
+    *mt = MTpathological;
+  else
+  {
+    long dG;
+    g = T ? FpXQ_study_eqn(ell, mpoly, T, p, &dG, ptr_r)
+            : Fp_study_eqn(ell, mpoly, p, &dG, ptr_r);
+    switch(dG)
+    {
+      case 0:  *mt = MTAtkin; break;
+      case 1:  *mt = MTone_root; break;
+      case 2:  *mt = MTElkies;   break;
+      default: *mt = (dG == ell + 1)? MTroots: MTpathological;
+    }
+  }
+  if (DEBUGLEVEL) switch(*mt)
+  {
+    case MTone_root: err_printf("One root\t"); break;
+    case MTElkies: err_printf("Elkies\t"); break;
+    case MTroots: err_printf("l+1 roots\t"); break;
+    case MTAtkin: err_printf("Atkin\t"); break;
+    case MTpathological: err_printf("Pathological\n"); break;
+  }
+  return g ? gerepilecopy(ltop, g): NULL;
+}
+
+/*Returns the trace modulo ell^k when ell is an Elkies prime */
+static GEN
+find_trace_Elkies_power(GEN a4, GEN a6, ulong ell, long k, struct meqn *MEQN, GEN g, GEN tr, GEN q, GEN T, GEN p, ulong smallfact, pari_timer *ti)
+{
+  pari_sp ltop = avma, btop, st_lim;
+  GEN tmp, Eba4, Eba6, Eca4, Eca6, Ib, kpoly;
+  ulong lambda, ellk = upowuu(ell, k), pellk = umodiu(q, ellk);
+  long cnt;
+
+  if (DEBUGLEVEL) { err_printf("Trace mod %ld", ell); }
+  Eba4 = a4;
+  Eba6 = a6;
+  tmp = find_isogenous(a4,a6, ell, MEQN, g, T, p);
+  if (!tmp) { avma = ltop; return NULL; }
+  Eca4 =  gel(tmp, 1);
+  Eca6 =  gel(tmp, 2);
+  kpoly = gel(tmp, 3);
+  Ib = pol_x(0);
+  lambda = find_eigen_value(a4, a6, ell, kpoly, T, p, tr);
+  if (DEBUGLEVEL>1) err_printf(" [%ld ms]", timer_delay(ti));
+  if (smallfact && ell>smallfact)
+  {
+    ulong pell = pellk%ell;
+    ulong ap = Fl_add(lambda, Fl_div(pell, lambda, ell), ell);
+    if (Fl_sub(pell, ap, ell)==ell-1) { avma = ltop; return mkvecsmall(ap); }
+  }
+  btop = avma; st_lim = stack_lim(btop, 1);
+  for (cnt = 2; cnt <= k; cnt++)
+  {
+    GEN tmp;
+    if (DEBUGLEVEL) err_printf(", %Ps", powuu(ell, cnt));
+    tmp = find_kernel_power(Eba4, Eba6, Eca4, Eca6, ell, MEQN, kpoly, Ib, T, p);
+    if (!tmp) { avma = ltop; return NULL; }
+    lambda = find_eigen_value_power(a4, a6, ell, cnt, gel(tmp,3), lambda, T, p);
+    Eba4 = Eca4;
+    Eba6 = Eca6;
+    Eca4 = gel(tmp,1);
+    Eca6 = gel(tmp,2);
+    kpoly = gel(tmp,4);
+    Ib = gel(tmp, 5);
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+      gerepileall(btop, 6, &Eba4, &Eba6, &Eca4, &Eca6, &kpoly, &Ib);
+    if (DEBUGLEVEL>1) err_printf(" [%ld ms]", timer_delay(ti));
+  }
+  avma = ltop;
+  return mkvecsmall(Fl_add(lambda, Fl_div(pellk, lambda, ellk), ellk));
+}
+
+/*Returns the possible values of the trace when ell is an Atkin prime, */
+/*given r the splitting degree of the modular equation at J = E.j */
+static GEN
+find_trace_Atkin(ulong ell, long r, GEN q)
+{
+  pari_sp ltop = avma;
+  long nval = 0;
+  ulong teta, pell = umodiu(q, ell), invp = Fl_inv(pell, ell);
+  GEN val_pos = cgetg(1+ell, t_VECSMALL), P = gel(factoru(r), 1);
+  GEN S = mkvecsmall4(0, pell, 0, 1);
+  GEN U = mkvecsmall3(0, ell-1, 0);
+  pari_sp btop = avma;
+  if (r==2 && krouu(ell-pell, ell) < 0)
+    val_pos[++nval] = 0;
+  for (teta = 1; teta < ell; teta++, avma = btop)
+  {
+    ulong disc = Fl_sub(Fl_sqr(teta,ell), Fl_mul(4UL,pell,ell), ell);
+    GEN a;
+    if (krouu(disc, ell) >= 0) continue;
+    S[3] = Fl_neg(teta, ell);
+    U[3] = Fl_mul(invp, teta, ell);
+    a = Flxq_powu(U, r/P[1], S, ell);
+    if (!Flx_equal1(a) && Flx_equal1(Flxq_powu(a, P[1], S, ell)))
+    {
+      pari_sp av = avma;
+      long i, l=lg(P);
+      for (i = 2; i < l; i++, avma = av)
+        if (Flx_equal1(Flxq_powu(U, r/P[i], S, ell))) break;
+      if (i==l) val_pos[++nval] = teta;
+    }
+  }
+  return gerepileupto(ltop, vecsmall_shorten(val_pos, nval));
+}
+
+/*Returns the possible traces when there is only one root */
+static GEN
+find_trace_one_root(ulong ell, GEN q)
+{
+  ulong a = Fl_double(Fl_sqrt(umodiu(q,ell), ell), ell);
+  return mkvecsmall2(a, ell - a);
+}
+
+static GEN
+find_trace_lp1_roots(long ell, GEN q)
+{
+  ulong ell2 = ell * ell, pell = umodiu(q, ell2);
+  ulong a  = Fl_sqrt(pell%ell, ell);
+  ulong pa = Fl_add(Fl_div(pell, a, ell2), a, ell2);
+  return mkvecsmall2(pa, ell2 - pa);
+}
+
+/*trace modulo ell^k: [], [t] or [t1,...,td] */
+static GEN
+find_trace(GEN a4, GEN a6, ulong ell, GEN q, GEN T, GEN p, long *ptr_kt, ulong smallfact)
+{
+  pari_sp ltop = avma;
+  GEN g, meqnj, tr, tr2;
+  long k = 1, kt, r;
+  enum mod_type mt;
+  struct meqn MEQN;
+  pari_timer ti;
+
+  if (ell <= 13)
+  {
+    long lp = expi(q);
+    switch(ell)
+    {
+      case 3: k = 3 + (lp > 160) + (lp > 350); break;
+      case 5: k = 2 + (lp > 260); break;
+      case 7: k = 2 + (lp > 390); break;
+      default:k = 1 + (lp > 260);
+    }
+  }
+  kt = k;
+  if (!get_modular_eqn(&MEQN, ell, 0, MAXVARN)) err_modular_eqn(ell);
+  if (DEBUGLEVEL)
+  { err_printf("Process prime %5ld. ", ell); timer_start(&ti); }
+  meqnj = FqXY_evalx(MEQN.eq, Fq_ellj(a4, a6, T, p), T, p);
+  g = study_modular_eqn(ell, meqnj, T, p, &mt, &r);
+  /* If l is an Elkies prime, search for a factor of the l-division polynomial.
+  * Then deduce the trace by looking for eigenvalues of the Frobenius by
+  * computing modulo this factor */
+  switch (mt)
+  {
+  case MTone_root:
+    tr2 = find_trace_one_root(ell, q);
+    kt = k = 1;
+    /* Must take k = 1 because we can't apply Hensel: no guarantee that a
+     * root mod ell^2 exists */
+    tr = find_trace_Elkies_power(a4,a6,ell, k, &MEQN, g, tr2, q, T, p, smallfact, &ti);
+    if (!tr) tr = tr2;
+    break;
+  case MTElkies:
+    /* Contrary to MTone_root, may look mod higher powers of ell */
+    tr = find_trace_Elkies_power(a4,a6,ell, k, &MEQN, g, NULL, q, T, p, smallfact, &ti);
+    if (!tr) { avma = ltop; return NULL; }
+    break;
+  case MTroots:
+    tr = find_trace_lp1_roots(ell, q);
+    kt = 2;
+    break;
+  case MTAtkin:
+    tr = find_trace_Atkin(ell, r, q);
+    if (lg(tr)==1) pari_err_PRIME("ellsea",p);
+    kt = 1;
+    break;
+  default: /* case MTpathological: */
+    avma = ltop; return NULL;
+  }
+  if (DEBUGLEVEL) {
+    long n = lg(tr)-1;
+    if (n > 1 || mt == MTAtkin)
+    {
+      err_printf("%3ld trace(s)",n);
+      if (DEBUGLEVEL>1) err_printf(" [%ld ms]", timer_delay(&ti));
+    }
+    err_printf("\n");
+  }
+  *ptr_kt = kt;
+  return gerepileupto(ltop, tr);
+}
+
+/* A partition of compile_atkin in baby and giant is represented as the binary
+   developpement of an integer; if the i-th bit is 1, the i-th prime in
+   compile-atkin is a baby. The optimum is obtained when the ratio between
+   the number of possibilities for traces modulo giants (p_g) and babies (p_b)
+   is near 3/4. */
+static long
+separation(GEN cnt)
+{
+  pari_sp btop, st_lim;
+  long k = lg(cnt)-1, l = (1L<<k)-1, best_i, i, j;
+  GEN best_r, P, P3, r;
+
+  P = gen_1;
+  for (j = 1; j <= k; ++j) P = mulis(P, cnt[j]);
+  /* p_b * p_g = P is constant */
+  P3 = mulsi(3, P);
+  btop = avma; st_lim = stack_lim(btop, 1);
+  best_i = 0;
+  best_r = P3;
+  for (i = 1; i < l; i++)
+  {
+    /* scan all possibilities */
+    GEN p_b = gen_1;
+    for (j = 0; j < k; j++)
+      if (i & (1L<<j)) p_b = mulis(p_b, cnt[1+j]);
+    r = subii(shifti(sqri(p_b), 2), P3); /* (p_b/p_g - 3/4)*4*P */
+    if (!signe(r)) { best_i = i; break; }
+    if (absi_cmp(r, best_r) < 0) { best_i = i; best_r = r; }
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+      best_r = gerepileuptoint(btop, best_r);
+  }
+  return best_i;
+}
+
+/* x VEC defined modulo P (= *P), y VECSMALL modulo q, (q,P) = 1. */
+/* Update in place:
+ *   x to vector mod q P congruent to x mod P (resp. y mod q). */
+/*   P ( <-- qP ) */
+static void
+multiple_crt(GEN x, GEN y, GEN q, GEN P)
+{
+  pari_sp ltop = avma, av;
+  long i, j, k, lx = lg(x)-1, ly = lg(y)-1;
+  GEN  a1, a2, u, v, A2X;
+  (void)bezout(P,q,&u,&v);
+  a1 = mulii(P,u);
+  a2 = mulii(q,v); A2X = ZC_Z_mul(x, a2);
+  av = avma; affii(mulii(P,q), P);
+  for (i = 1, k = 1; i <= lx; i++, avma = av)
+  {
+    GEN a2x = gel(A2X,i);
+    for (j = 1; j <= ly; ++j)
+    {
+      GEN t = Fp_add(Fp_mulu(a1, y[j], P), a2x, P);
+      affii(t, gel(x, k++));
+    }
+  }
+  setlg(x, k); avma = ltop;
+}
+
+/****************************************************************************/
+/*                              MATCH AND SORT                              */
+/****************************************************************************/
+
+static GEN
+possible_traces(GEN compile, GEN mask, GEN *P, int larger)
+{
+  GEN V, Pfinal = gen_1, C = shallowextract(compile, mask);
+  long i, lfinal = 1, lC = lg(C), lP;
+  pari_sp av = avma;
+
+  for (i = 1; i < lC; i++)
+  {
+    GEN c = gel(C,i), t;
+    Pfinal = mulii(Pfinal, gel(c,1));
+    t = muluu(lfinal, lg(gel(c,2))-1);
+    lfinal = itou(t);
+  }
+  Pfinal = gerepileuptoint(av, Pfinal);
+  if (larger)
+    lP = lgefint(shifti(Pfinal,1));
+  else
+    lP = lgefint(Pfinal);
+  lfinal++;
+  /* allocate room for final result */
+  V = cgetg(lfinal, t_VEC);
+  for (i = 1; i < lfinal; i++) gel(V,i) = cgeti(lP);
+
+  {
+    GEN c = gel(C,1), v = gel(c,2);
+    long l = lg(v);
+    for (i = 1; i < l; i++) affsi(v[i], gel(V,i));
+    setlg(V, l); affii(gel(c,1), Pfinal); /* reset Pfinal */
+  }
+  for (i = 2; i < lC; i++)
+  {
+    GEN c = gel(C,i);
+    multiple_crt(V, gel(c,2), gel(c,1), Pfinal); /* Pfinal updated! */
+  }
+  *P = Pfinal; return V;
+}
+
+static GEN
+cost(long mask, GEN cost_vec)
+{
+  pari_sp ltop = avma;
+  long i;
+  GEN c = gen_1;
+  for (i = 1; i < lg(cost_vec); i++)
+    if (mask&(1L<<(i-1)))
+      c = mulis(c, cost_vec[i]);
+  return gerepileuptoint(ltop, c);
+}
+
+static GEN
+value(long mask, GEN atkin, long k)
+{
+  pari_sp ltop = avma;
+  long i;
+  GEN c = gen_1;
+  for (i = 1; i <= k; i++)
+    if (mask&(1L<<(i-1)))
+      c = mulii(c, gmael(atkin, i, 1));
+  return gerepileuptoint(ltop, c);
+}
+
+static void
+set_cost(GEN B, long b, GEN cost_vec, long *pi)
+{
+  pari_sp av = avma;
+  GEN costb = cost(b, cost_vec);
+  long i = *pi;
+  while (cmpii(costb, cost(B[i], cost_vec)) < 0) --i;
+  B[++i] = b;
+  *pi = i; avma = av;
+}
+
+static GEN
+get_lgatkin(GEN compile_atkin, long k)
+{
+  GEN v = cgetg(k+1, t_VECSMALL);
+  long j;
+  for (j = 1; j <= k; ++j) v[j] = lg(gmael(compile_atkin, j, 2))-1;
+  return v;
+}
+
+static GEN
+champion(GEN atkin, long k, GEN bound_champ)
+{
+  const long two_k = 1L<<k;
+  pari_sp ltop = avma;
+  long i, j, n, i1, i2;
+  GEN B, Bp, cost_vec, res = NULL;
+
+  cost_vec = get_lgatkin(atkin, k);
+  if (k == 1) return mkvec2(gen_1, utoipos(cost_vec[1]));
+
+  B  = zero_zv(two_k);
+  Bp = zero_zv(two_k);
+  Bp[2] = 1;
+  for (n = 2, j = 2; j <= k; j++)
+  {
+    long b;
+    i = 1;
+    for (i1 = 2, i2 = 1; i1 <= n; )
+    {
+      pari_sp av = avma;
+      long b1 = Bp[i1], b2 = Bp[i2]|(1L<<(j-1));
+      if (cmpii(value(b1, atkin, k), value(b2, atkin, k)) < 0)
+        { b = b1; i1++; } else { b = b2; i2++; }
+      avma = av;
+      set_cost(B, b, cost_vec, &i);
+    }
+    for ( ; i2 <= n; i2++)
+    {
+      b = Bp[i2]|(1L<<(j-1));
+      set_cost(B, b, cost_vec, &i);
+    }
+    n = i;
+    for (i = 1; i <= n; i++)
+      Bp[i] = B[i];
+  }
+  for (i = 1; i <= two_k; i++)
+    if (B[i])
+    {
+      GEN b = cost (B[i], cost_vec);
+      GEN v = value(B[i], atkin, k);
+      if (cmpii(v, bound_champ) <=0) continue;
+      if (res && gcmp(b, gel(res, 2)) >=0) continue;
+      res = mkvec2(utoi(B[i]), b);
+    }
+  return gerepilecopy(ltop, res);
+}
+
+static GEN
+compute_diff(GEN v)
+{
+  pari_sp av = avma;
+  long i, l = lg(v) - 1;
+  GEN diff = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++) gel(diff, i) = subii(gel(v, i+1), gel(v, i));
+  return gerepileupto(av, ZV_sort_uniq(diff));
+}
+
+static int
+cmp_atkin(void*E, GEN a, GEN b)
+{
+  long ta=typ(a)==t_INT, tb=typ(b)==t_INT, c;
+  (void) E;
+  if (ta || tb) return ta-tb;
+  c = lg(gel(a,2)) - lg(gel(b,2));
+  if (c) return c;
+  return cmpii(gel(b,1), gel(a,1));
+}
+
+static void
+add_atkin(GEN atkin, GEN trace, long *nb)
+{
+  long l = lg(atkin)-1;
+  long i, k = gen_search(atkin, trace, 1, NULL, cmp_atkin);
+  if (k==0 || k > l) return;
+  for (i = l; i > k; i--)
+    gel(atkin,i) = gel(atkin,i-1);
+  if (typ(gel(atkin,l))==t_INT) (*nb)++;
+  gel(atkin,k) = trace;
+}
+
+/* V = baby / giant, P = Pb / Pg */
+static GEN
+BSGS_pre(GEN *pdiff, GEN V, GEN P, void *E, const struct bb_group *grp)
+{
+  GEN diff = compute_diff(V);
+  GEN pre = cgetg(lg(diff), t_VEC);
+  long i, l = lg(diff);
+  gel(pre, 1) = grp->pow(E, P, gel(diff, 1));
+  /* what we'd _really_ want here is a hashtable diff[i] -> pre[i]  */
+  for (i = 2; i < l; i++)
+  {
+    pari_sp av = avma;
+    GEN d = subii(gel(diff, i), gel(diff, i-1));
+    GEN Q = grp->mul(E, gel(pre, i-1), grp->pow(E, P, d));
+    gel(pre, i) = gerepilecopy(av, Q);
+  }
+  *pdiff = diff; return pre;
+}
+
+/* u = trace_elkies, Mu = prod_elkies. Let caller collect garbage */
+/* Match & sort: variant from Lercier's thesis, section 11.2.3 */
+/* baby/giant/table updated in place: this routines uses
+ *   size(baby)+size(giant)+size(table)+size(table_ind) + O(log p)
+ * bits of stack */
+static GEN
+match_and_sort(GEN compile_atkin, GEN Mu, GEN u, GEN q, void *E, const struct bb_group *grp)
+{
+  pari_sp av1, av2, lim;
+  GEN baby, giant, SgMb, Mb, Mg, den, Sg, dec_inf, div, pp1 = addis(q,1);
+  GEN P, Pb, Pg, point, diff, pre, table, table_ind;
+  long best_i, i, lbaby, lgiant, k = lg(compile_atkin)-1;
+  pari_timer ti;
+
+  if (k == 1)
+  { /*only one Atkin prime, check the cardinality with random points */
+    GEN r = gel(compile_atkin, 1), r1 = gel(r,1), r2 = gel(r,2);
+    long l = lg(r2);
+    GEN card = cgetg(l, t_VEC), Cs2, C, U;
+    Z_chinese_pre(Mu, r1, &C,&U, NULL);
+    Cs2 = shifti(C, -1);
+    for (i = 1; i < l; i++)
+    {
+      GEN t = Z_chinese_post(u, stoi(r2[i]), C, U, NULL);
+      gel(card, i) = subii(pp1, Fp_center(t, C, Cs2));
+    }
+    return gen_select_order(card, E, grp);
+  }
+  if (DEBUGLEVEL>=2) timer_start(&ti);
+  av1 = avma;
+  best_i = separation( get_lgatkin(compile_atkin, k) );
+  avma = av1;
+
+  baby  = possible_traces(compile_atkin, stoi(best_i), &Mb, 1);
+  giant = possible_traces(compile_atkin, subis(int2n(k), best_i+1), &Mg, 0);
+  lbaby = lg(baby);
+  lgiant = lg(giant);
+  den = Fp_inv(Fp_mul(Mu, Mb, Mg), Mg);
+  av2 = avma;
+  for (i = 1; i < lgiant; i++, avma = av2)
+    affii(Fp_mul(gel(giant,i), den, Mg), gel(giant,i));
+  gen_sort_inplace(giant, (void*)&cmpii, &cmp_nodata, NULL);
+  Sg = Fp_mul(negi(u), den, Mg);
+  den = Fp_inv(Fp_mul(Mu, Mg, Mb), Mb);
+  dec_inf = divii(mulii(Mb,addii(Mg,shifti(Sg,1))), shifti(Mg,1));
+  togglesign(dec_inf); /* now, dec_inf = ceil(- (Mb/2 + Sg Mb/Mg) ) */
+  div = mulii(truedivii(dec_inf, Mb), Mb);
+  av2 = avma;
+  for (i = 1; i < lbaby; i++, avma = av2)
+  {
+    GEN b = addii(Fp_mul(Fp_sub(gel(baby,i), u, Mb), den, Mb), div);
+    if (cmpii(b, dec_inf) < 0) b = addii(b, Mb);
+    affii(b, gel(baby,i));
+  }
+  gen_sort_inplace(baby, (void*)&cmpii, &cmp_nodata, NULL);
+
+  SgMb = mulii(Sg, Mb);
+  P = grp->rand(E);
+  point = grp->pow(E,P, Mu);
+  Pb = grp->pow(E,point, Mg);
+  Pg = grp->pow(E,point, Mb);
+  /* Precomputation for babies */
+  pre = BSGS_pre(&diff, baby, Pb, E, grp);
+
+  /*Now we compute the table of babies, this table contains only the */
+  /*lifted x-coordinate of the points in order to use less memory */
+  table = cgetg(lbaby, t_VECSMALL);
+  av1 = avma; lim = stack_lim(av1,3);
+  /* (p+1 - u - Mu*Mb*Sg) P - (baby[1]) Pb */
+  point = grp->pow(E,P, subii(subii(pp1, u), mulii(Mu, addii(SgMb, mulii(Mg, gel(baby,1))))));
+  table[1] = grp->hash(gel(point,1));
+  for (i = 2; i < lbaby; i++)
+  {
+    GEN d = subii(gel(baby, i), gel(baby, i-1));
+    point =  grp->mul(E, point, grp->pow(E, gel(pre, ZV_search(diff, d)), gen_m1));
+    table[i] = grp->hash(gel(point,1));
+    if (low_stack(lim, stack_lim(av1,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"match_and_sort, baby = %ld", i);
+      point = gerepileupto(av1, point);
+    }
+  }
+  avma = av1;
+  /* Precomputations for giants */
+  pre = BSGS_pre(&diff, giant, Pg, E, grp);
+
+  /* Look for a collision among the x-coordinates */
+  table_ind = vecsmall_indexsort(table);
+  table = perm_mul(table,table_ind);
+
+  av1 = avma; lim = stack_lim(av1,3);
+  point = grp->pow(E, Pg, gel(giant, 1));
+  for (i = 1; ; i++)
+  {
+    GEN d;
+    long h = grp->hash(gel(point, 1));
+    long s = zv_search(table, h);
+    if (s) {
+      while (table[s] == h && s) s--;
+      for (s++; s < lbaby && table[s] == h; s++)
+      {
+        GEN B = gel(baby,table_ind[s]), G = gel(giant,i);
+        GEN GMb = mulii(G, Mb), BMg = mulii(B, Mg);
+        GEN Be = subii(subii(pp1, u), mulii(Mu, addii(SgMb, BMg)));
+        GEN Bp = grp->pow(E,P, Be);
+        /* p+1 - u - Mu (Sg Mb + GIANT Mb + BABY Mg) */
+        if (gequal(gel(Bp,1),gel(point,1)))
+        {
+          GEN card = subii(Be, mulii(Mu, GMb));
+          card = mkvec2(card, addii(card, mulii(mulsi(2,Mu), GMb)));
+          if (DEBUGLEVEL>=2) timer_printf(&ti,"match_and_sort");
+          return gen_select_order(card, E, grp);
+        }
+      }
+    }
+    if (i==lgiant-1) break;
+    d = subii(gel(giant, i+1), gel(giant, i));
+    point = grp->mul(E,point, gel(pre, ZV_search(diff, d)));
+    if (low_stack(lim, stack_lim(av1,3)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"match_and_sort, giant = %ld", i);
+      point = gerepileupto(av1, point);
+    }
+  }
+  /* no match ? */
+  pari_err_BUG("match_and_sort");
+  return NULL; /* not reached */
+}
+
+static GEN
+get_bound_bsgs(long lp)
+{
+  GEN B;
+  if (lp <= 160)
+    B = divru(powru(dbltor(1.048), lp), 9);
+  else if (lp <= 192)
+    B = divrr(powru(dbltor(1.052), lp), dbltor(16.65));
+  else
+    B = mulrr(powru(dbltor(1.035), minss(lp,307)), dbltor(1.35));
+  return mulru(B, 1000000);
+}
+
+/*FIXME: the name of the function does not quite match what it does*/
+static const struct bb_group *
+get_FqE_group(void ** pt_E, GEN a4, GEN a6, GEN T, GEN p)
+{
+  if (!T) return get_FpE_group(pt_E,a4,a6,p);
+  else if (lgefint(p)==3)
+  {
+    ulong pp=(ulong) p[2];
+    return get_FlxqE_group(pt_E, ZX_to_Flx(a4,pp),ZX_to_Flx(a6,pp),ZXT_to_FlxT(T,pp),pp);
+  }
+  return get_FpXQE_group(pt_E,a4,a6,T,p);
+}
+
+/* E is an elliptic curve defined over Z or over Fp in ellinit format, defined
+ * by the equation E: y^2 + a1*x*y + a2*y = x^3 + a2*x^2 + a4*x + a6
+ * p is a prime number
+ * set smallfact to stop whenever a small factor > smallfact of the order is
+ * detected. Useful when searching for a good curve for cryptographic
+ * applications */
+GEN
+Fq_ellcard_SEA(GEN a4, GEN a6, GEN q, GEN T, GEN p, long smallfact)
+{
+  const long MAX_ATKIN = 21;
+  pari_sp ltop = avma, btop, st_lim;
+  long ell, i, nb_atkin;
+  GEN TR, TR_mod, compile_atkin, bound, bound_bsgs, champ;
+  GEN prod_atkin = gen_1, max_traces = gen_0;
+  double bound_gr = 1.;
+  const double growth_factor = 1.26;
+  forprime_t TT;
+  void *E;
+
+  if (!modular_eqn && !get_seadata(0)) return NULL;
+  if (T && get_FpX_var(T)==0) /* 0 is used by the modular polynomial */
+  {
+    if (typ(T)==t_POL) { T  = shallowcopy(T); setvarn(T,1); }
+    else T = gsubst(T,0,pol_x(1));
+    a4 = shallowcopy(a4); setvarn(a4,1);
+    a6 = shallowcopy(a6); setvarn(a6,1);
+  }
+  /*First compute the trace modulo 2 */
+  switch(FqX_nbroots(mkpoln(4, gen_1, gen_0, a4, a6), T, p))
+  {
+  case 3: /* bonus time: 4 | #E(Fq) = q+1 - t */
+    i = mod4(q)+1; if (i > 2) i -= 4;
+    TR_mod = utoipos(4);
+    TR = stoi(i); break;
+  case 1:
+    TR_mod = gen_2;
+    TR = gen_0; break;
+  default : /* 0 */
+    TR_mod = gen_2;
+    TR = gen_1; break;
+  }
+  if (smallfact == 1 && !mpodd(TR))
+  {
+    if (DEBUGLEVEL) err_printf("Aborting: #E(Fq) divisible by 2\n");
+    avma = ltop; return gen_0;
+  }
+
+  /* compile_atkin is a vector containing informations about Atkin primes,
+   * informations about Elkies primes lie in Mod(TR, TR_mod). */
+  u_forprime_init(&TT, 3, 1000); /* way beyond what seadata provides */
+  bound = sqrti(shifti(q, 4));
+  bound_bsgs = get_bound_bsgs(expi(q));
+  compile_atkin = zerovec(MAX_ATKIN); nb_atkin = 0;
+  btop = avma; st_lim = stack_lim(btop, 1);
+  while ( (ell = u_forprime_next(&TT)) )
+  {
+    long ellkt, kt = 1, nbtrace;
+    GEN trace_mod;
+    trace_mod = find_trace(a4, a6, ell, q, T, p, &kt, smallfact);
+    if (!trace_mod) continue;
+
+    nbtrace = lg(trace_mod) - 1;
+    ellkt = (long)upowuu(ell, kt);
+    if (nbtrace == 1)
+    {
+      long t_mod_ellkt = trace_mod[1];
+      if (smallfact && ell > smallfact)
+      { /* does ell divide q + 1 - t ? */
+        long card_mod_ell = (umodiu(q,ell) + 1 - t_mod_ellkt) % ell ;
+        if (!card_mod_ell)
+        {
+          if (DEBUGLEVEL)
+            err_printf("\nAborting: #E(Fq) divisible by %ld\n",ell);
+          avma = ltop; return gen_0;
+        }
+      }
+      (void)Z_incremental_CRT(&TR, t_mod_ellkt, &TR_mod, ellkt);
+    }
+    else
+    {
+      add_atkin(compile_atkin, mkvec2(utoipos(ellkt), trace_mod), &nb_atkin);
+      prod_atkin = value(-1, compile_atkin, nb_atkin);
+    }
+    if (cmpii(mulii(TR_mod, prod_atkin), bound) > 0)
+    {
+      GEN bound_tr;
+      if (!nb_atkin) return gerepileuptoint(ltop, subii(addis(p,1),TR));
+      bound_tr = mulrr(bound_bsgs, dbltor(bound_gr));
+      bound_gr *= growth_factor;
+      if (signe(max_traces))
+      {
+        max_traces = divis(muliu(max_traces,nbtrace), ellkt);
+        if (DEBUGLEVEL>=3)
+          err_printf("At least %Ps remaining possibilities.\n",max_traces);
+      }
+      if (cmpir(max_traces, bound_tr) < 0)
+      {
+        GEN bound_atkin = truedivii(bound, TR_mod);
+        champ = champion(compile_atkin, nb_atkin, bound_atkin);
+        max_traces = gel(champ,2);
+        if (DEBUGLEVEL>=2)
+          err_printf("%Ps remaining possibilities.\n", max_traces);
+        if (cmpir(max_traces, bound_tr) < 0)
+        {
+          GEN res, cat = shallowextract(compile_atkin, gel(champ,1));
+          const struct bb_group *grp;
+          if (DEBUGLEVEL)
+            err_printf("Match and sort for %Ps possibilities.\n", max_traces);
+          grp = get_FqE_group(&E,a4,a6,T,p);
+          res = match_and_sort(cat, TR_mod, TR, q, E, grp);
+          return gerepileuptoint(ltop, res);
+        }
+      }
+    }
+    if (low_stack(st_lim, stack_lim(btop, 1)))
+      gerepileall(btop,5, &TR,&TR_mod, &compile_atkin, &max_traces, &prod_atkin);
+  }
+  return NULL;/*not reached*/
+}
+
+GEN
+Fp_ellcard_SEA(GEN a4, GEN a6, GEN p, long smallfact)
+{
+  return Fq_ellcard_SEA(a4, a6, p, NULL, p, smallfact);
+}
+
+GEN
+ellsea(GEN E, GEN p, long smallfact)
+{
+  pari_sp av = avma;
+  GEN a4 = modii(mulis(Rg_to_Fp(gel(E,10), p), -27), p);
+  GEN a6 = modii(mulis(Rg_to_Fp(gel(E,11), p), -54), p);
+  GEN card = Fp_ellcard_SEA(a4, a6, p, smallfact);
+  if (!card) pari_err_PACKAGE("seadata");
+  return gerepileuptoint(av, subii(addis(p,1),card));
+}
diff --git a/src/modules/galois.c b/src/modules/galois.c
new file mode 100644
index 0000000..f7a282e
--- /dev/null
+++ b/src/modules/galois.c
@@ -0,0 +1,2428 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/**************************************************************/
+/*                                                            */
+/*    Galois group for degree between 8 and 11 (included)     */
+/*                                                            */
+/**************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+#define NMAX 11 /* maximum degree */
+
+typedef GEN PERM;
+typedef PERM *GROUP;
+typedef struct {
+  PERM *a;
+  long nm, nv;
+} resolv; /* resolvent */
+
+typedef struct {
+  long pr, prmax, N;
+  GEN p, r, coef;
+} buildroot;
+
+static long isin_G_H(buildroot *BR, long n1, long n2);
+
+static long *par_vec;
+/* k-1 entries filled so far
+ * m = maximal allowed value, n = sum to reach with remaining elements */
+static void
+do_par(GEN T, long k, long n, long m)
+{
+  long i;
+  if (n <= 0)
+  {
+    GEN t = cgetg(k, t_VECSMALL);
+    for (i=1; i<k; i++) t[i] = par_vec[i];
+    gel(T, ++T[0]) = t; return;
+  }
+  if (n < m) m = n;
+  for (i=1; i<=m; i++) { par_vec[k] = i; do_par(T, k+1, n-i, i); }
+}
+
+/* compute the partitions of n, as decreasing t_VECSMALLs */
+static GEN
+partitions_galois(long n)
+{
+  pari_sp av;
+  long i, p;
+  GEN T;
+
+  switch(n) /* optimized for galoismoduloX ... */
+  {
+    case 8: p = 22; break;
+    case 9: p = 30; break;
+    case 10:p = 42; break;
+    default:
+      if (n < 0) pari_err_TYPE("partitions_galois", stoi(n));
+      av = avma; p = itos( numbpart(stoi(n)) ); avma = av; break;
+  }
+  T = new_chunk(p + 1); T[0] = 0;
+  par_vec = cgetg(n+1, t_VECSMALL); /* not Garbage Collected later */
+  do_par(T,1,n,n);
+  if (DEBUGLEVEL > 7)
+  {
+    err_printf("Partitions of %ld (%ld)\n",n, p);
+    for (i=1; i<=p; i++) err_printf("i = %ld: %Ps\n",i,gel(T,i));
+  }
+  T[0] = evallg(p + 1) | evaltyp(t_VEC); return T;
+}
+
+/* affect to the permutation x the N arguments that follow */
+static void
+_aff(long N, PERM x,...)
+{
+  va_list args; long i;
+  va_start(args,x); for (i=1; i<=N; i++) x[i] = va_arg(args,int);
+  va_end(args);
+}
+
+/* return an array of length |len| from the arguments (for galoismodulo) */
+static GEN
+_gr(long len,...)
+{
+  va_list args;
+  long i, l = labs(len);
+  GEN x = new_chunk(l+1);
+
+  va_start(args,len); x[0] = len;
+  for (i=1; i<=l; i++) x[i] = va_arg(args,int);
+  va_end(args); return x;
+}
+
+/* return a VECSMALL of length l from the arguments (for galoismodulo11) */
+static GEN
+_typ(long l,...)
+{
+  va_list args;
+  long i;
+  GEN x = cgetg(l+1, t_VECSMALL);
+
+  va_start(args,l);
+  for (i=1; i<=l; i++) x[i] = va_arg(args,int);
+  va_end(args); return x;
+}
+
+/* create a permutation with the N arguments of the function */
+static PERM
+_cr(long N, long a,...)
+{
+  static long x[NMAX+1];
+  va_list args;
+  long i;
+
+  va_start(args, a); x[0] = N; x[1] = a;
+  for (i=2; i<=N; i++) x[i] = va_arg(args,int);
+  va_end(args); return x;
+}
+
+static PERM
+permmul(PERM s1, PERM s2)
+{
+  long i, n1 = s1[0];
+  PERM s3 = (PERM)stack_malloc((n1+1) * sizeof(long));
+  for (i=1; i<=n1; i++) s3[i] = s1[s2[i]];
+  s3[0] = n1; return s3;
+}
+
+static void
+printperm(PERM perm)
+{
+  long i, n = perm[0];
+  err_printf("(");
+  for (i=1; i<=n; i++) err_printf(" %d",perm[i]);
+  err_printf(" )\n");
+}
+
+static int
+raye(long *g, long num)
+{
+  long i, nb = labs(g[0]);
+  for (i=1; i<=nb; i++)
+    if (g[i] == num) return 0;
+  return 1;
+}
+
+/* we can never determine the group completely in there */
+static long
+rayergroup11(long EVEN, long num, long *gr)
+{
+  long r = 0;
+
+  if (EVEN)
+    switch(num)
+    {
+      case 2: case 5:
+        if (gr[3]) { gr[3]=0; r++; }
+      case 3: case 6: case 7:
+        if (gr[2]) { gr[2]=0; r++; }
+      case 4:
+        if (gr[1]) { gr[1]=0; r++; }
+    }
+  else
+    switch(num)
+    {
+      case 2: case 3:
+        if (gr[1]) { gr[1]=0; r++; }
+    }
+  return r;
+}
+
+static long
+rayergroup(long EVEN, long **GR, long num, long *gr)
+{
+  long i,nbgr,r;
+
+  if (!GR) return rayergroup11(EVEN,num,gr);
+  nbgr = lg(GR); r = 0 ;
+  if (EVEN)
+  {
+    for (i=1; i<nbgr; i++)
+      if (gr[i] && GR[i][0] < 0 && raye(GR[i],num)) { gr[i]=0; r++; }
+  }
+  else
+  {
+    for (i=1; i<nbgr; i++)
+      if (gr[i] && GR[i][0] > 0 && raye(GR[i],num)) { gr[i]=0; r++; }
+  }
+  return r;
+}
+
+static long
+galmodp(long EVEN, GEN pol, GEN dpol, GEN TYP, long *gr, long **GR)
+{
+  long i,k,l,n,nbremain;
+  GEN p1, dtyp;
+  forprime_t T;
+
+  switch(degpol(pol))
+  {
+    case  8: nbremain = EVEN? 28: 22; break;
+    case  9: nbremain = EVEN? 18: 16; break;
+    case 10: nbremain = EVEN? 12: 33; break;
+    default: nbremain = EVEN?  5:  3; break; /* case 11 */
+  }
+
+  u_forprime_init(&T, 2, ULONG_MAX);
+  dtyp = new_chunk(NMAX+1);
+  k = gr[0]; for (i=1; i<k; i++) gr[i]=1;
+  for (k=1; k<15; k++)
+  {
+    ulong p = u_forprime_next(&T);
+    if (!umodiu(dpol,p)) continue; /* p divides dpol */
+
+    p1 = gel(Flx_degfact(ZX_to_Flx(pol,p),p),1);
+    l = lg(p1);
+    dtyp[0] = evaltyp(t_VECSMALL)|evallg(l);
+    for (i=1; i<l; i++) dtyp[i] = p1[l-i]; /* decreasing order */
+    n = RgV_isin(TYP, dtyp);
+    if (!n) return 1; /* only for N=11 */
+    nbremain -= rayergroup(EVEN,GR,n,gr);
+    if (nbremain==1) return 1;
+  }
+  return 0;
+}
+
+static void
+preci(GEN o, long p)
+{
+  long i, l = lg(o);
+  for (i=1; i<l; i++)
+  {
+    GEN x = gel(o,i);
+    if (typ(x)==t_COMPLEX) { setprec(gel(x,1),p); setprec(gel(x,2),p); } else setprec(x,p);
+  }
+}
+static void
+fixprec(buildroot *BR)
+{
+  GEN r = BR->r;
+  long i, l = lg(r), p = BR->pr;
+
+  if (p > BR->prmax) pari_err_BUG("fixprex [precision too large]");
+  for (i = 1; i < l; i++) preci(gel(r,i), p);
+}
+
+static long
+getpreci(buildroot *BR)
+{
+  GEN x = gmael(BR->r,1,1);
+  return (typ(x)==t_COMPLEX)? realprec(gel(x,1)): realprec(x);
+}
+
+#define setcard_obj(x,n) ((x)[0] = (PERM)(n))
+#define getcard_obj(x)   ((long)((x)[0]))
+
+/* allocate a list of m arrays of length n (index 0 is codeword) */
+static PERM *
+alloc_pobj(long n, long m)
+{
+  long i;
+  PERM *g = (PERM*) stack_malloc( (m+1)*sizeof(PERM) + (n+1)*m * sizeof(long) );
+  PERM gpt = (PERM) (g + (m+1));
+
+  for (i=1; i<=m; i++) { g[i] = gpt; gpt += (n+1); }
+  setcard_obj(g, m); return g;
+}
+
+static GROUP
+allocgroup(long n, long card)
+{
+  GROUP gr = alloc_pobj(n,card);
+  long i;
+
+  for (i=1; i<=card; i++) gr[i][0] = n;
+  return gr;
+}
+
+static pariFILE *
+galopen(const char *pre, long n, long n1, long n2)
+{
+  pari_sp av = avma;
+  char *s = stack_malloc(strlen(pari_datadir) + 3 + 4 * 20 + 1 + 3);
+  pariFILE *f;
+
+  (void)sprintf(s, "%s/galdata/%s%ld_%ld_%ld", pari_datadir, pre, n, n1, n2);
+  f = pari_fopengz(s);
+  if (!f) pari_err_FILE("galois file",s);
+  avma = av; return f;
+}
+
+static char
+bin(char c)
+{
+  if (c>='0' && c<='9') c -= '0';
+  else if (c>='A' && c<='Z') c -= 'A'-10;
+  else if (c>='a' && c<='z') c -= 'a'-36;
+  else pari_err_TYPE("bin [not alphanumeric]", stoi(c));
+  return c;
+}
+
+#define BUFFS 512
+/* fill in g[i][j] (i<=n, j<=m) with (buffered) data from f->file */
+static void
+read_obj(PERM *g, pariFILE *f, long n, long m)
+{
+  long i, j, k, N = m*n;
+  char *ch = stack_malloc(N);
+  pari_fread_chars(ch, N, f->file);
+  for (k = 0, i = 1; i <= n; i++)
+    for (j = 1; j <= m; j++,k++) g[i][j] = bin(ch[k]);
+  pari_fclose(f);
+}
+#undef BUFFS
+
+/* the first 8 bytes contain size data (possibly padded with \0) */
+static GROUP
+lirecoset(long n1, long n2, long n)
+{
+  GROUP gr;
+  char c, ch[8];
+  long m, cardgr;
+  pariFILE *f = galopen("COS", n, n1, n2);
+  pari_fread_chars(&c, 1, f->file); m=bin(c);
+  pari_fread_chars(&c, 1, f->file);
+  pari_fread_chars(ch, 6, f->file); cardgr=atol(ch);
+  gr=allocgroup(m,cardgr);
+  read_obj(gr, f,cardgr,m); return gr;
+}
+
+static void
+lireresolv(long n1, long n2, long n, resolv *R)
+{
+  char ch[5];
+  long nm, nv;
+  pariFILE *f = galopen("RES", n, n1, n2);
+  pari_fread_chars(ch,5,f->file); nm = atol(ch);
+  pari_fread_chars(ch,3,f->file); nv = atol(ch);
+  R->a = alloc_pobj(nv,nm);
+  read_obj(R->a, f,nm,nv);
+  R->nm = nm;
+  R->nv = nv;
+}
+
+static int
+cmp_re(GEN x, GEN y)
+{
+  if (typ(x) != t_COMPLEX) return -1;
+  if (typ(y) != t_COMPLEX) return 1; /* t_REALS are smallest */
+  return gcmp(gel(x,1), gel(y,1));
+}
+
+/* multiply the r o bb. Sort first to detect pairs of conjugate */
+static GEN
+Monomial(GEN r, PERM bb, long nbv)
+{
+  GEN t, R = cgetg(nbv + 1, t_VEC);
+  long i, s = 1;
+
+  for (i = 1; i <= nbv; i++)
+  {
+    t = gel(r,bb[i]);
+    if (typ(t) == t_COMPLEX && signe(gel(t,1)) < 0) { s = -s; t = gneg(t); }
+    gel(R,i) = t;
+  }
+  if (nbv > 2)
+    gen_sort_inplace(R, (void*)&cmp_re, cmp_nodata, NULL);
+  else if (nbv == 2 && typ(gel(R,2)) != t_COMPLEX)
+    swap(gel(R,1), gel(R,2));
+  t = NULL;
+  for (i=1; i<=nbv; i++)
+  {
+    GEN c = gel(R,i);
+    if (typ(c) == t_COMPLEX && i < nbv)
+    { /* detect conjugates */
+      GEN n = gel(R,++i);
+      if (!absr_cmp(gel(n,1), gel(c,1))
+       && !absr_cmp(gel(n,2), gel(c,2))
+       && signe(gel(c,2)) != signe(gel(n,2)))
+        c = addrr(sqrr(gel(c,1)), sqrr(gel(c,2)));
+      else
+        c = gmul(c,n);
+    }
+    t = t? gmul(t, c): c;
+  }
+  if (s < 0) t = gneg(t);
+  return t;
+}
+
+/* sum(i = 1, R->nm, Monomial(r, R->a[i], R->nv)). Sort real / imaginary part
+ * separately by increasing absolute values, to increase stability */
+static GEN
+gpolynomial(GEN r, resolv *R)
+{
+  GEN RE = cgetg(R->nm+1, t_VEC), IM = cgetg(R->nm+1, t_VEC), re, im;
+  long i, k;
+  for (i = k = 1; i <= R->nm; i++)
+  {
+    GEN m = Monomial(r,R->a[i], R->nv);
+    if (typ(m) == t_REAL)
+      gel(RE, i) = m;
+    else {
+      gel(RE, i)   = gel(m,1);
+      gel(IM, k++) = gel(m,2);
+    }
+  }
+  setlg(IM, k);
+  RE = vecpermute(RE, gen_indexsort(RE, (void*)&absr_cmp, cmp_nodata));
+  IM = vecpermute(IM, gen_indexsort(IM, (void*)&absr_cmp, cmp_nodata));
+  re = gel(RE,1);
+  for (i = 2; i <= R->nm; i++) re = addrr(re, gel(RE,i));
+  if (k == 1) return re;
+  im = gel(IM,1);
+  for (i = 2; i < k; i++) im = addrr(im, gel(IM,i));
+  return mkcomplex(re, im);
+}
+
+static void
+zaux1(GEN *z, GEN *r)
+{
+  GEN p2,p1;
+  p2=gsub(r[1], gadd(r[2],r[5]));
+  p2=gmul(p2, gsub(r[2],r[5]));
+  p1=gmul(p2,r[1]);
+  p2=gsub(r[3],gadd(r[2],r[4]));
+  p2=gmul(p2,gsub(r[4],r[2]));
+  p1=gadd(p1,gmul(p2,r[3]));
+  p2=gmul(r[5],gsub(r[4],r[5]));
+  z[1]=gadd(p1,gmul(p2,r[4]));
+
+  p2=gsub(r[1],gadd(r[3],r[4]));
+  p2=gmul(p2,gsub(r[3],r[4]));
+  p1=gmul(p2,r[1]);
+  p2=gsub(r[5],gadd(r[3],r[2]));
+  p2=gmul(p2,gsub(r[2],r[3]));
+  p1=gadd(p1,gmul(p2,r[5]));
+  p2=gmul(r[4],gsub(r[2],r[4]));
+  z[2]=gadd(p1,gmul(p2,r[2]));
+}
+
+static void
+zaux(GEN *z, GEN *r)
+{
+  zaux1(z, r); zaux1(z+2, r+5);
+}
+
+static GEN
+gpoly(GEN rr, long n1, long n2)
+{
+  const long N = lg(rr)-1;
+  GEN p1,p2,z[6], *r = (GEN*)rr; /* syntaxic kludge */
+  long i,j;
+
+  if (N==8)
+  {
+    if (n1==47 && n2==46)
+    {
+      p1=gsub(r[3],r[4]);
+      for (i=1; i<3; i++) for (j=i+1; j<5; j++) p1 = gmul(p1,gsub(r[i],r[j]));
+      for (i=5; i<8; i++) for (j=i+1; j<9; j++) p1 = gmul(p1,gsub(r[i],r[j]));
+      p2=r[1];
+      for (i=2; i<5; i++) p2=gadd(p2,r[i]);
+      for (i=5; i<9; i++) p2=gsub(p2,r[i]);
+    }
+    else /* n1==44 && n2==40 */
+    {
+      for (i=1; i<5; i++) z[i] = gadd(r[2*i-1],r[2*i]);
+      p1 = gsub(r[1],r[2]);
+      for (i=2; i<5; i++) p1 = gmul(p1,gsub(r[2*i-1],r[2*i]));
+      p2=gsub(z[3],z[4]);
+      for (i=1; i<3; i++) for (j=i+1; j<5; j++) p2 = gmul(p2,gsub(z[i],z[j]));
+    }
+    return gmul(p1,p2);
+  }
+
+  if (N==9)
+  {
+    if (n1==31 && n2==29)
+    {
+      p1=gsub(r[2],r[3]);
+      for (j=2; j<4; j++) p1 = gmul(p1,gsub(r[1],r[j]));
+      for (i=4; i<6; i++) for (j=i+1; j<7; j++) p1 = gmul(p1,gsub(r[i],r[j]));
+      p2 = gsub(r[8],r[9]);
+      for (j=8; j<10; j++) p2 = gmul(p2,gsub(r[7],r[j]));
+    }
+    else /* ((n1==34 && n2==31) || (n1=33 && n2==30)) */
+    {
+      p1=r[1]; for (i=2; i<4; i++) p1=gadd(p1,r[i]);
+      p2=r[4]; for (i=5; i<7; i++) p2=gadd(p2,r[i]);
+      p1=gmul(p1,p2);
+      p2=r[7]; for (i=8; i<10; i++) p2=gadd(p2,r[i]);
+    }
+    return gmul(p1,p2);
+  }
+
+  if (N==10)
+  {
+    if ((n1==45 && n2==43) || (n1==44 && n2==42))
+    {
+      p1=r[1]; for (i=2; i<6; i++) p1=gadd(p1,r[i]);
+      p2=r[6]; for (i=7; i<11; i++) p2=gadd(p2,r[i]);
+      return gmul(p1,p2);
+    }
+    else if ((n1==45 && n2==39) || (n1==44 && n2==37))
+    {
+      p1 = gadd(r[1],r[2]);
+      for (i=2; i<6; i++) p1 = gmul(p1,gadd(r[2*i-1],r[2*i]));
+      return p1;
+    }
+    else if ((n1==43 && n2==41) || (n1==33 && n2==27))
+    {
+      p1=gsub(r[4],r[5]);
+      for (i=1; i<4; i++) for (j=i+1; j<6; j++) p1=gmul(p1,gsub(r[i],r[j]));
+      p2=gsub(r[9],r[10]);
+      for (i=6; i<9; i++) for (j=i+1; j<11; j++) p2=gmul(p2,gsub(r[i],r[j]));
+      return gmul(p1,p2);
+    }
+    else if ((n1==43 && n2==33) || (n1==42 && n2==28) || (n1==41 && n2==27)
+          || (n1==40 && n2==21))
+    {
+      p2=gadd(r[2],r[5]);
+      p2=gsub(p2,gadd(r[3],r[4]));
+      p1=gmul(p2,r[1]);
+      p2=gsub(r[3],gadd(r[4],r[5]));
+      p1=gadd(p1,gmul(p2,r[2]));
+      p2=gsub(r[4],r[5]);
+      p1=gadd(p1,gmul(p2,r[3]));
+      z[1]=gadd(p1,gmul(r[4],r[5]));
+
+      p2=gadd(r[7],r[10]);
+      p2=gsub(p2,gadd(r[8],r[9]));
+      p1=gmul(p2,r[6]);
+      p2=gsub(r[8],gadd(r[9],r[10]));
+      p1=gadd(p1,gmul(p2,r[7]));
+      p2=gsub(r[9],r[10]);
+      p1=gadd(p1,gmul(p2,r[8]));
+      z[2]=gadd(p1,gmul(r[9],r[10]));
+      return gadd(gsqr(z[1]), gsqr(z[2]));
+    }
+    else if (n1==41 && n2==40)
+    {
+      p1=gsub(r[4],r[5]);
+      for (i=1; i<4; i++) for (j=i+1; j<6; j++) p1 = gmul(p1,gsub(r[i],r[j]));
+      p2=gsub(r[9],r[10]);
+      for (i=6; i<9; i++) for (j=i+1; j<11; j++) p2 = gmul(p2,gsub(r[i],r[j]));
+      return gadd(p1,p2);
+    }
+    else if ((n1==41 && n2==22) || (n1==40 && n2==11) || (n1==17 && n2==5)
+            || (n1==10 && n2==4) || (n1==9 && n2==3) || (n1==6 && n2==1))
+    {
+      p1=gadd(r[1],r[6]);
+      for (i=2; i<6; i++) p1=gmul(p1,gadd(r[i],r[i+5]));
+      return p1;
+    }
+    else if ((n1==39 && n2==38) || (n1==29 && n2==25))
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[2*i-1],r[2*i]);
+      p1=gsub(r[1],r[2]);
+      for (i=2; i<6; i++) p1=gmul(p1,gsub(r[2*i-1],r[2*i]));
+      p2=gsub(z[4],z[5]);
+      for (i=1; i<4; i++) for (j=i+1; j<6; j++) p2=gmul(p2,gsub(z[i],z[j]));
+      return gmul(p1,p2);
+    }
+    else if ((n1==39 && n2==36) || (n1==37 && n2==34) || (n1==29 && n2==23)
+          || (n1==24 && n2==15))
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[2*i-1],r[2*i]);
+      p1=gsub(z[4],z[5]); p2=gmul(gsub(z[3],z[4]),gsub(z[3],z[5]));
+      for (i=1; i<3; i++) for (j=i+1; j<6; j++) p2=gmul(p2,gsub(z[i],z[j]));
+      return gmul(p1,p2);
+    }
+    else if ((n1==39 && n2==29) || (n1==38 && n2==25) || (n1==37 && n2==24)
+          || (n1==36 && n2==23) || (n1==34 && n2==15))
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[2*i-1],r[2*i]);
+      p2=gadd(z[2],z[5]);
+      p2=gsub(p2,gadd(z[3],z[4]));
+      p1=gmul(p2,z[1]);
+      p2=gsub(z[3],gadd(z[4],z[5]));
+      p1=gadd(p1,gmul(p2,z[2]));
+      p2=gsub(z[4],z[5]);
+      p1=gadd(p1,gmul(p2,z[3]));
+      p1=gadd(p1,gmul(z[4],z[5]));
+      return gsqr(p1);
+    }
+    else if ((n1==39 && n2==22) || (n1==38 && n2==12) || (n1==36 && n2==11)
+          || (n1==29 && n2== 5) || (n1==25 && n2== 4) || (n1==23 && n2== 3)
+          || (n1==16 && n2== 2) || (n1==14 && n2== 1))
+    {
+      p1=r[1]; for (i=2; i<6; i++) p1=gadd(p1,r[2*i-1]);
+      p2=r[2]; for (i=2; i<6; i++) p2=gadd(p2,r[2*i]);
+      return gmul(p1,p2);
+    }
+    else if (n1==28 && n2==18)
+    {
+      zaux(z, r);
+      p1=gmul(z[1],gsub(z[3],z[4]));
+      p2=gmul(z[2],gadd(z[3],z[4])); return gadd(p1,p2);
+    }
+    else if (n1==27 && n2==20)
+    {
+      zaux(z, r); p1=gmul(z[1],z[3]); p2=gmul(z[2],z[4]);
+      p1 = gsub(p1,p2); p2=r[1];
+      for (i=2; i<6 ; i++) p2=gadd(p2,r[i]);
+      for (   ; i<11; i++) p2=gsub(p2,r[i]);
+      return gmul(p1,p2);
+    }
+    else if (n1==27 && n2==19)
+    {
+      zaux(z, r); p1=gmul(z[1],z[3]); p2=gmul(z[2],z[4]);
+      return gsub(p1,p2);
+    }
+    else if ((n1==27 && n2==17) || (n1==21 && n2==9))
+    {
+      zaux(z, r); p1=gmul(z[1],z[3]); p2=gmul(z[2],z[4]);
+      return gadd(p1,p2);
+    }
+    else if (n1==23 && n2==16)
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[2*i-1],r[2*i]);
+      p1=gsub(z[1],gadd(z[2],z[5])); p1=gmul(p1,gsub(z[2],z[5]));
+      p2=gmul(p1,z[1]); p1=gsub(z[3],gadd(z[2],z[4]));
+      p1=gmul(  p1,gsub(z[4],z[2])); p2=gadd(p2,gmul(p1,z[3]));
+      p1=gmul(z[5],gsub(z[4],z[5])); p2=gadd(p2,gmul(p1,z[4]));
+      p1=gsub(r[1],r[2]);
+      for (i=2; i<6; i++) p1=gmul(p1,gsub(r[2*i-1],r[2*i]));
+      return gmul(p1,p2);
+    }
+    else if (n1==22 && n2==12)
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[i],r[i+5]);
+      p1=gsub(r[1],r[6]);
+      for (i=2; i<6; i++) p1=gmul(p1,gsub(r[i],r[i+5]));
+      p2=gsub(z[4],z[5]);
+      for (i=1; i<4; i++) for (j=i+1; j<6; j++) p2=gmul(p2,gsub(z[i],z[j]));
+      return gmul(p1,p2);
+    }
+    else if ((n1==22 && n2==11) || (n1==5 && n2==3))
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[i],r[i+5]);
+      p1=gsub(z[4],z[5]); p2=gmul(gsub(z[3],z[4]),gsub(z[3],z[5]));
+      for (i=1; i<3; i++) for (j=i+1; j<6; j++) p2=gmul(p2,gsub(z[i],z[j]));
+      return gmul(p1,p2);
+    }
+    else if ((n1==22 && n2==5) || (n1==12 && n2==4) || (n1==11 && n2==3))
+    {
+      for (i=1; i<6; i++) z[i]=gadd(r[i],r[i+5]);
+      p2=gadd(z[2],z[5]); p2=gsub(p2,gadd(z[3],z[4])); p1=gmul(p2,z[1]);
+      p2=gsub(z[3],gadd(z[4],z[5])); p1=gadd(p1,gmul(p2,z[2]));
+      p2=gsub(z[4],z[5]);
+      p1=gadd(p1,gmul(p2,z[3])); p1=gadd(p1,gmul(z[4],z[5]));
+      return gsqr(p1);
+    }
+    else if (n1==21 && n2==10)
+    {
+      zaux(z, r); p1=gmul(z[1],z[4]); p2=gmul(z[2],z[3]);
+      return gsub(p1,p2);
+    }
+  }
+  pari_err_TYPE("gpoly [undefined invariant polynomial]", mkvecsmall2(n1,n2));
+  return NULL; /* not reached */
+}
+
+/* a is a t_VECSMALL representing a polynomial */
+static GEN
+new_pol(long N, GEN r, GEN a)
+{
+  long i, j, l = lg(a);
+  GEN x, z, v = cgetg(N+1, t_VEC);
+  for (i=1; i<=N; i++)
+  {
+    z = gel(r,i); x = gaddsg(a[2], z);
+    for (j = 3; j < l; j++) x = gaddsg(a[j], gmul(z,x));
+    gel(v,i) = x;
+  }
+  return gclone(v);
+}
+
+/* BR->r[l], BR->coef[l] hold data related to Tschirnausen transform of
+ * degree l - 1 */
+static void
+tschirn(buildroot *BR)
+{
+  long i, k, v = varn(BR->p), l = lg(BR->r);
+  GEN a, h, r;
+
+  if (l >= BR->N) pari_err_BUG("tschirn");
+  if (DEBUGLEVEL)
+    err_printf("\n$$$$$ Tschirnhaus transformation of degree %ld: $$$$$\n",l-1);
+
+  a = gel(BR->coef,l); /* fill with random polynomial of degree <= l-1 */
+  do
+  {
+    a[1]=0;
+    for (i=2; i < l+2; i++) a[i] = random_bits(3) + 1;
+    h = Flx_to_ZX(Flx_renormalize(a,l+2));
+  } while (degpol(h) <= 0 || !ZX_is_squarefree(h));
+  setvarn(h, v); k = 0;
+  (void)ZXQ_charpoly_sqf(BR->p, h, &k, v);
+  a[2] += k;
+
+  r = gel(BR->r,1);
+  preci(r, BR->prmax); /* max accuracy original roots */
+  vectrunc_append(BR->r, new_pol(BR->N, r, a));
+  fixprec(BR); /* restore accuracy */
+}
+
+static GEN
+sortroots(GEN newr, GEN oldr)
+{
+  long e, e0, i, j, k, l = lg(newr);
+  GEN r = cgetg(l, t_VEC), z = cgetg(l, t_VEC), t = const_vecsmall(l-1, 1);
+  k = 0; /* gcc -Wall */
+  for (i=1; i<l; i++)
+  {
+    e0 = EXPOBITS;
+    for (j=1; j<l; j++)
+      if (t[j])
+      {
+        e = gexpo(gsub(gel(oldr,i), gel(newr,j)));
+        if (e < e0) { e0 = e; k = j; }
+      }
+    gel(z,i) = gel(newr,k); t[k] = 0;
+  }
+  for (i=1; i<l; i++) gel(r,i) = gel(z,i);
+  return r;
+}
+
+static void
+delete_roots(buildroot *BR)
+{
+  GEN r = BR->r;
+  long i, l = lg(r);
+  for (i = 1; i < l; i++) gunclone(gel(r,i));
+  setlg(r, 1);
+}
+
+/* increase the roots accuracy */
+static void
+moreprec(buildroot *BR)
+{
+  long d = BR->pr - BR->prmax;
+  if (d > 0)
+  { /* recompute roots */
+    pari_sp av = avma;
+    long l = lg(BR->r);
+    GEN ro;
+
+    if (d < BIGDEFAULTPREC-2) d = BIGDEFAULTPREC-2;
+    BR->prmax = maxss(BR->prmax+d, (long)(BR->prmax * 1.2));
+    if (DEBUGLEVEL)
+      { err_printf("$$$$$ New prec = %ld\n",BR->prmax); err_flush(); }
+    ro = sortroots(QX_complex_roots(BR->p,BR->prmax), gel(BR->r,1));
+    delete_roots(BR);
+    vectrunc_append(BR->r, gclone(ro));
+    for (d = 2; d < l; d++)
+      vectrunc_append(BR->r, new_pol(BR->N, ro, gel(BR->coef,d)));
+    avma = av;
+  }
+  fixprec(BR);
+}
+
+/* determine "sufficient" extra bit-precision such that we may decide
+ * (heuristic) whether z is an integer. */
+static GEN
+get_ro(long N, GEN rr, PERM S1, PERM S2, resolv *R)
+{
+  GEN r = cgetg(N+1, t_VEC);
+  long i;
+  for (i=1; i<=N; i++) r[i] = rr[ S1[S2[i] ] ];
+  return R->a? gpolynomial(r, R): gpoly(r,R->nm,R->nv);
+}
+/* typ(z) = t_REAL, return zi = t_INT approximation */
+static long
+sufprec_r(GEN z)
+{
+  long p = bit_prec(z);
+  /* bit accuracy of fractional part large enough ? */
+  return ( p - expo(z) > maxss(3*32, (long)0.2*p) );
+}
+/* typ(z) = t_REAL or t_COMPLEX, return zi = t_INT approximation */
+static long
+sufprec(GEN z)
+{
+  if (typ(z) == t_REAL)
+    return sufprec_r(z);
+  else
+    return sufprec_r(gel(z,2)) && sufprec_r(gel(z,1));
+}
+
+static GEN
+get_ro_perm(PERM S1, PERM S2, long d, resolv *R, buildroot *BR)
+{
+  GEN ro, roi;
+  long e;
+  for (;;)
+  {
+    ro = get_ro(BR->N, gel(BR->r, d), S1,S2,R); roi = grndtoi(ro, &e);
+    if (e < 0)
+    {
+      if (e < -64 || sufprec(ro)) break;
+      e = 0;
+    }
+    BR->pr += nbits2extraprec(e + 10);
+    moreprec(BR);
+  }
+  if (e > -10 || typ(roi) == t_COMPLEX) return NULL;
+  /* compute with 128 more bits */
+  BR->pr += MEDDEFAULTPREC-2;
+  moreprec(BR);
+  ro = get_ro(BR->N, gel(BR->r, d), S1,S2,R);
+  BR->pr -= MEDDEFAULTPREC-2;
+  fixprec(BR);
+  /* ro closer to roi (32 more bits) ? */
+  return (gexpo(gsub(ro, roi)) < e - 32) ? roi: NULL;
+}
+
+static void
+dbg_rac(long nri,long nbracint,long numi[],GEN racint[],long multi[])
+{
+  long k;
+  err_printf("\t# rational integer roots = %ld:",nbracint-nri);
+  for (k = nri+1; k <= nbracint; k++) err_printf(" %ld^%ld", numi[k], multi[k]);
+  err_printf("\n");
+  for (k = nri+1; k <= nbracint; k++) err_printf("\t%2ld: %Ps\n", numi[k], racint[k]);
+  err_flush();
+}
+
+#define M 2521
+/* return NULL if not included, the permutation of the roots otherwise */
+static PERM
+check_isin(buildroot *BR, resolv *R, GROUP tau, GROUP ss)
+{
+  long nogr, nocos, init, i, j, k, l, d;
+  pari_sp av1 = avma, av2;
+  long nbgr,nbcos,nbracint,nbrac,lastnbri,lastnbrm;
+  static long numi[M],numj[M],lastnum[M],multi[M],norac[M],lastnor[M];
+  GEN  racint[M], roint;
+
+  if (getpreci(BR) != BR->pr) fixprec(BR);
+  nbcos = getcard_obj(ss);
+  nbgr  = getcard_obj(tau);
+  lastnbri = lastnbrm = -1; nbracint = nbrac = 0; /* gcc -Wall*/
+  for (nogr=1; nogr<=nbgr; nogr++)
+  {
+    PERM T = tau[nogr];
+    if (DEBUGLEVEL) err_printf("    ----> Group # %ld/%ld:\n",nogr,nbgr);
+    init = 0; d = 1;
+    for (;;)
+    {
+      if (!init)
+      {
+        av2 = avma; nbrac = nbracint = 0;
+        for (nocos=1; nocos<=nbcos; nocos++, avma = av2)
+        {
+          roint = get_ro_perm(T, ss[nocos], d, R, BR);
+          if (!roint) continue;
+
+          nbrac++;
+          if (nbrac >= M)
+          {
+            pari_warn(warner, "more than %ld rational integer roots\n", M);
+            avma = av1; goto NEXT;
+          }
+          for (j=1; j<=nbracint; j++)
+            if (equalii(roint,racint[j])) { multi[j]++; break; }
+          if (j > nbracint)
+          {
+            nbracint = j; multi[j] = 1; numi[j] = nocos;
+            racint[j] = gerepileuptoint(av2,roint); av2 = avma;
+          }
+          numj[nbrac] = nocos; norac[nbrac] = j;
+        }
+        if (DEBUGLEVEL) dbg_rac(0,nbracint,numi,racint,multi);
+        for (i=1; i<=nbracint; i++)
+          if (multi[i]==1) return permmul(T, ss[numi[i]]);
+        init = 1;
+      }
+      else
+      {
+        nbrac = nbracint = 0;
+        for (l=1; l<=lastnbri; l++, avma = av1)
+        {
+          long nri = nbracint;
+          av2 = avma;
+          for (k=1; k<=lastnbrm; k++)
+            if (lastnor[k]==l)
+            {
+              nocos = lastnum[k];
+              roint = get_ro_perm(T, ss[nocos], d, R, BR);
+              if (!roint) { avma = av2; continue; }
+
+              nbrac++;
+              for (j=nri+1; j<=nbracint; j++)
+                if (equalii(roint,racint[j])) { multi[j]++; break; }
+              if (j > nbracint)
+              {
+                nbracint = j; multi[j] = 1; numi[j] = nocos;
+                racint[j] = gerepileuptoint(av2,roint); av2=avma;
+              }
+              numj[nbrac] = nocos; norac[nbrac] = j;
+            }
+          if (DEBUGLEVEL) dbg_rac(nri,nbracint,numi,racint,multi);
+          for (i=nri+1; i<=nbracint; i++)
+            if (multi[i]==1) return permmul(T, ss[numi[i]]);
+        }
+      }
+      avma = av1; if (!nbracint) break;
+
+      lastnbri = nbracint; lastnbrm = nbrac;
+      for (j=1; j<=nbrac; j++) { lastnum[j] = numj[j]; lastnor[j] = norac[j]; }
+
+NEXT:
+      if (DEBUGLEVEL) {
+        err_printf("        all integer roots are double roots\n");
+        err_printf("      Working with polynomial #%ld:\n", d+1);
+      }
+      if (++d >= lg(BR->r)) tschirn(BR);
+    }
+  }
+  return NULL;
+}
+#undef M
+
+/* DEGREE 8 */
+static long
+galoisprim8(long EVEN, buildroot *BR)
+{
+  long rep;
+
+  rep=isin_G_H(BR,50,43);
+  if (rep) return EVEN? 37: 43;
+  if (!EVEN) return 50;
+  rep=isin_G_H(BR,49,48);
+  if (!rep) return 49;
+  rep=isin_G_H(BR,48,36);
+  if (!rep) return 48;
+  rep=isin_G_H(BR,36,25);
+  return rep? 25: 36;
+}
+
+static long
+galoisimpodd8(buildroot *BR, long nh)
+{
+  long rep;
+  if (nh!=47) goto IMPODD_8_6;
+  rep=isin_G_H(BR,47,46);
+  if (!rep) goto IMPODD_8_5;
+  rep=isin_G_H(BR,46,28);
+  if (rep) goto IMPODD_8_7; else return 46;
+
+IMPODD_8_5:
+  rep=isin_G_H(BR,47,35);
+  if (rep) goto IMPODD_8_9; else return 47;
+
+IMPODD_8_6:
+  rep=isin_G_H(BR,44,40);
+  if (rep) goto IMPODD_8_10; else goto IMPODD_8_11;
+
+IMPODD_8_7:
+  rep=isin_G_H(BR,28,21);
+  if (rep) return 21; else goto IMPODD_8_33;
+
+IMPODD_8_9:
+  rep=isin_G_H(BR,35,31);
+  if (rep) goto IMPODD_8_13; else goto IMPODD_8_14;
+
+IMPODD_8_10:
+  rep=isin_G_H(BR,40,26);
+  if (rep) goto IMPODD_8_15; else goto IMPODD_8_16;
+
+IMPODD_8_11:
+  rep=isin_G_H(BR,44,38);
+  if (rep) goto IMPODD_8_17; else goto IMPODD_8_18;
+
+IMPODD_8_12:
+  rep=isin_G_H(BR,16,7);
+  if (rep) goto IMPODD_8_19; else return 16;
+
+IMPODD_8_13:
+  rep=isin_G_H(BR,31,21);
+  return rep? 21: 31;
+
+IMPODD_8_14:
+  rep=isin_G_H(BR,35,30);
+  if (rep) goto IMPODD_8_34; else goto IMPODD_8_20;
+
+IMPODD_8_15:
+  rep=isin_G_H(BR,26,16);
+  if (rep) goto IMPODD_8_12; else goto IMPODD_8_21;
+
+IMPODD_8_16:
+  rep=isin_G_H(BR,40,23);
+  if (rep) goto IMPODD_8_22; else return 40;
+
+IMPODD_8_17:
+  rep=isin_G_H(BR,38,31);
+  if (rep) goto IMPODD_8_13; else return 38;
+
+IMPODD_8_18:
+  rep=isin_G_H(BR,44,35);
+  if (rep) goto IMPODD_8_9; else return 44;
+
+IMPODD_8_19:
+  rep=isin_G_H(BR,7,1);
+  return rep? 1: 7;
+
+IMPODD_8_20:
+  rep=isin_G_H(BR,35,28);
+  if (rep) goto IMPODD_8_7; else goto IMPODD_8_23;
+
+IMPODD_8_21:
+  rep=isin_G_H(BR,26,17);
+  if (rep) goto IMPODD_8_24; else goto IMPODD_8_25;
+
+IMPODD_8_22:
+  rep=isin_G_H(BR,23,8);
+  if (rep) goto IMPODD_8_26; else return 23;
+
+IMPODD_8_23:
+  rep=isin_G_H(BR,35,27);
+  if (rep) goto IMPODD_8_27; else goto IMPODD_8_28;
+
+IMPODD_8_24:
+  rep=isin_G_H(BR,17,7);
+  if (rep) goto IMPODD_8_19; else return 17;
+
+IMPODD_8_25:
+  rep=isin_G_H(BR,26,15);
+  if (rep) goto IMPODD_8_29; else return 26;
+
+IMPODD_8_26:
+  rep=isin_G_H(BR,8,1);
+  return rep? 1: 8;
+
+IMPODD_8_27:
+  rep=isin_G_H(BR,27,16);
+  if (rep) goto IMPODD_8_12; else return 27;
+
+IMPODD_8_28:
+  rep=isin_G_H(BR,35,26);
+  if (rep) goto IMPODD_8_15; else return 35;
+
+IMPODD_8_29:
+  rep=isin_G_H(BR,15,7);
+  if (rep) goto IMPODD_8_19;
+  rep=isin_G_H(BR,15,6);
+  if (!rep) goto IMPODD_8_32;
+  rep=isin_G_H(BR,6,1);
+  return rep? 1: 6;
+
+IMPODD_8_32:
+  rep=isin_G_H(BR,15,8);
+  if (rep) goto IMPODD_8_26; else return 15;
+
+IMPODD_8_33:
+  rep=isin_G_H(BR,28,16);
+  if (rep) goto IMPODD_8_12; else return 28;
+
+IMPODD_8_34:
+  rep=isin_G_H(BR,30,21);
+  return rep? 21: 30;
+}
+
+static long
+galoisimpeven8(buildroot *BR, long nh)
+{
+   long rep;
+   if (nh!=45) goto IMPEVEN_8_6;
+   rep=isin_G_H(BR,45,42);
+   if (!rep) goto IMPEVEN_8_5;
+  rep=isin_G_H(BR,42,34);
+  if (rep) goto IMPEVEN_8_7; else goto IMPEVEN_8_8;
+
+IMPEVEN_8_5:
+  rep=isin_G_H(BR,45,41);
+  if (rep) goto IMPEVEN_8_9; else return 45;
+
+IMPEVEN_8_6:
+  rep=isin_G_H(BR,39,32);
+  if (rep) goto IMPEVEN_8_10; else goto IMPEVEN_8_11;
+
+IMPEVEN_8_7:
+  rep=isin_G_H(BR,34,18);
+  if (rep) goto IMPEVEN_8_21; else goto IMPEVEN_8_45;
+
+IMPEVEN_8_8:
+  rep=isin_G_H(BR,42,33);
+  if (rep) goto IMPEVEN_8_14; else return 42;
+
+IMPEVEN_8_9:
+  rep=isin_G_H(BR,41,34);
+  if (rep) goto IMPEVEN_8_7; else goto IMPEVEN_8_15;
+
+IMPEVEN_8_10:
+  rep=isin_G_H(BR,32,22);
+  if (rep) goto IMPEVEN_8_16; else goto IMPEVEN_8_17;
+
+IMPEVEN_8_11:
+  rep=isin_G_H(BR,39,29);
+  if (rep) goto IMPEVEN_8_18; else goto IMPEVEN_8_19;
+
+IMPEVEN_8_12:
+  rep=isin_G_H(BR,14,4);
+  return rep? 4: 14;
+
+IMPEVEN_8_14:
+  rep=isin_G_H(BR,33,18);
+  if (rep) goto IMPEVEN_8_21; else goto IMPEVEN_8_22;
+
+IMPEVEN_8_15:
+  rep=isin_G_H(BR,41,33);
+  if (rep) goto IMPEVEN_8_14; else goto IMPEVEN_8_23;
+
+IMPEVEN_8_16:
+  rep=isin_G_H(BR,22,11);
+  if (rep) goto IMPEVEN_8_24; else goto IMPEVEN_8_25;
+
+IMPEVEN_8_17:
+  rep=isin_G_H(BR,32,13);
+  if (rep) goto IMPEVEN_8_26; else goto IMPEVEN_8_27;
+
+IMPEVEN_8_18:
+  rep=isin_G_H(BR,29,22);
+  if (rep) goto IMPEVEN_8_16; else goto IMPEVEN_8_28;
+
+IMPEVEN_8_19:
+  rep=isin_G_H(BR,39,24);
+  if (rep) goto IMPEVEN_8_29; else return 39;
+
+IMPEVEN_8_20:
+  rep=isin_G_H(BR,9,4);
+  if (rep) return 4; else goto IMPEVEN_8_30;
+
+IMPEVEN_8_21:
+  rep=isin_G_H(BR,18,10);
+  if (rep) goto IMPEVEN_8_31; else goto IMPEVEN_8_32;
+
+IMPEVEN_8_22:
+  rep=isin_G_H(BR,33,13);
+  if (rep) goto IMPEVEN_8_26; else return 33;
+
+IMPEVEN_8_23:
+  rep=isin_G_H(BR,41,29);
+  if (rep) goto IMPEVEN_8_18; else goto IMPEVEN_8_33;
+
+IMPEVEN_8_24:
+  rep=isin_G_H(BR,11,5);
+  if (rep) return 5; else goto IMPEVEN_8_34;
+
+IMPEVEN_8_25:
+  rep=isin_G_H(BR,22,9);
+  if (rep) goto IMPEVEN_8_20; else return 22;
+
+IMPEVEN_8_26:
+  rep=isin_G_H(BR,13,3);
+  return rep? 3: 13;
+
+IMPEVEN_8_27:
+  rep=isin_G_H(BR,32,12);
+  if (rep) goto IMPEVEN_8_35; else return 32;
+
+IMPEVEN_8_28:
+  rep=isin_G_H(BR,29,20);
+  if (rep) goto IMPEVEN_8_36; else goto IMPEVEN_8_37;
+
+IMPEVEN_8_29:
+  rep=isin_G_H(BR,24,14);
+  if (rep) goto IMPEVEN_8_12; else goto IMPEVEN_8_38;
+
+IMPEVEN_8_30:
+  rep=isin_G_H(BR,9,3);
+  if (rep) return 3; else goto IMPEVEN_8_39;
+
+IMPEVEN_8_31:
+  rep=isin_G_H(BR,10,2);
+  return rep? 2: 10;
+
+IMPEVEN_8_32:
+  rep=isin_G_H(BR,18,9);
+  if (rep) goto IMPEVEN_8_20; else return 18;
+
+IMPEVEN_8_33:
+  rep=isin_G_H(BR,41,24);
+  if (rep) goto IMPEVEN_8_29; else return 41;
+
+IMPEVEN_8_34:
+  rep=isin_G_H(BR,11,4);
+  if (rep) return 4; else goto IMPEVEN_8_44;
+
+IMPEVEN_8_35:
+  rep=isin_G_H(BR,12,5);
+  return rep? 5: 12;
+
+IMPEVEN_8_36:
+  rep=isin_G_H(BR,20,10);
+  if (rep) goto IMPEVEN_8_31; else return 20;
+
+IMPEVEN_8_37:
+  rep=isin_G_H(BR,29,19);
+  if (rep) goto IMPEVEN_8_40; else goto IMPEVEN_8_41;
+
+IMPEVEN_8_38:
+  rep=isin_G_H(BR,24,13);
+  if (rep) goto IMPEVEN_8_26; else goto IMPEVEN_8_42;
+
+IMPEVEN_8_39:
+  rep=isin_G_H(BR,9,2);
+  return rep? 2: 9;
+
+IMPEVEN_8_40:
+  rep=isin_G_H(BR,19,10);
+  if (rep) goto IMPEVEN_8_31; else goto IMPEVEN_8_43;
+
+IMPEVEN_8_41:
+  rep=isin_G_H(BR,29,18);
+  if (rep) goto IMPEVEN_8_21; else return 29;
+
+IMPEVEN_8_42:
+  rep=isin_G_H(BR,24,9);
+  if (rep) goto IMPEVEN_8_20; else return 24;
+
+IMPEVEN_8_43:
+  rep=isin_G_H(BR,19,9);
+  if (rep) goto IMPEVEN_8_20; else return 19;
+
+IMPEVEN_8_44:
+  rep=isin_G_H(BR,11,2);
+  return rep? 2: 11;
+
+IMPEVEN_8_45:
+  rep=isin_G_H(BR,34,14);
+  if (rep) goto IMPEVEN_8_12; else return 34;
+}
+
+static long
+closure8(long EVEN, buildroot *BR)
+{
+  long rep;
+
+  if (!EVEN)
+  {
+    rep=isin_G_H(BR,50,47);
+    if (rep) return galoisimpodd8(BR,47);
+    rep=isin_G_H(BR,50,44);
+    if (rep) return galoisimpodd8(BR,44);
+  }
+  else
+  {
+    rep=isin_G_H(BR,49,45);
+    if (rep) return galoisimpeven8(BR,45);
+    rep=isin_G_H(BR,49,39);
+    if (rep) return galoisimpeven8(BR,39);
+  }
+  return galoisprim8(EVEN, BR);
+}
+
+static GROUP
+initgroup(long n, long nbgr)
+{
+  GROUP t = allocgroup(n,nbgr);
+  PERM ID =  t[1];
+  long i;
+  for (i = 1; i <= n; i++) ID[i] = i;
+  return t;
+}
+
+static PERM
+data8(long N, long n1, long n2, GROUP *t)
+{
+  switch(n1)
+  {
+    case 7: if (n2!=1) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 6, 5, 8, 7);
+      return (*t)[1];
+    case 9: if (n2!=4) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 4, 3, 5, 6, 8, 7);
+      return (*t)[1];
+    case 10: if (n2!=2) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 6, 5, 8, 7);
+      return (*t)[1];
+    case 11:
+      switch(n2)
+      {
+        case 2:
+          *t=initgroup(N,2);
+          _aff(N, (*t)[2], 1, 2, 5, 6, 3, 4, 8, 7);
+          return _cr(N, 1, 3, 5, 8, 2, 4, 6, 7);
+        case 4:
+          *t=initgroup(N,1);
+          return _cr(N, 1, 3, 7, 5, 2, 4, 8, 6);
+      }break;
+    case 14: if (n2!=4) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 4, 3, 5, 6, 8, 7);
+    case 15: if (n2!=6 && n2!=8) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 6, 5, 8, 7);
+      return (*t)[1];
+    case 16: if (n2!=7) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 18:
+      switch(n2)
+      {
+        case 9: *t=initgroup(N,3);
+          _aff(N, (*t)[2], 1, 5, 3, 7, 2, 6, 4, 8);
+          _aff(N, (*t)[3], 1, 2, 3, 4, 6, 5, 8, 7);
+          return (*t)[1];
+        case 10: *t=initgroup(N,3);
+          _aff(N, (*t)[2], 1, 6, 3, 8, 2, 5, 4, 7);
+          _aff(N, (*t)[3], 1, 5, 3, 7, 2, 6, 4, 8);
+          return (*t)[1];
+      }break;
+    case 19: if (n2!=9) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 5, 3, 8, 2, 6, 4, 7);
+    case 20: if (n2!=10) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 22:
+      switch(n2)
+      {
+        case 9: *t=initgroup(N,6);
+          _aff(N, (*t)[2], 1, 2, 7, 8, 3, 4, 6, 5);
+          _aff(N, (*t)[3], 1, 2, 7, 8, 3, 4, 5, 6);
+          _aff(N, (*t)[4], 1, 2, 5, 6, 3, 4, 8, 7);
+          _aff(N, (*t)[5], 1, 2, 5, 6, 3, 4, 7, 8);
+          _aff(N, (*t)[6], 1, 2, 3, 4, 5, 6, 8, 7);
+          return _cr(N, 1, 3, 5, 7, 2, 4, 6, 8);
+        case 11: *t=initgroup(N,6);
+          _aff(N, (*t)[2], 1, 2, 5, 6, 7, 8, 4, 3);
+          _aff(N, (*t)[3], 1, 2, 5, 6, 7, 8, 3, 4);
+          _aff(N, (*t)[4], 1, 2, 3, 4, 7, 8, 6, 5);
+          _aff(N, (*t)[5], 1, 2, 3, 4, 7, 8, 5, 6);
+          _aff(N, (*t)[6], 1, 2, 3, 4, 5, 6, 8, 7);
+          return (*t)[1];
+      }break;
+    case 23: if (n2!=8) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 6, 5, 8, 7);
+    case 26: if (n2!=15 && n2!=17) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 28: if (n2!=21) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 7, 8, 5, 6);
+    case 29: if (n2!=18 && n2!=19) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 30: if (n2!=21) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 7, 8, 5, 6);
+    case 31: if (n2!=21) break;
+      *t=initgroup(N,3);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 7, 8, 5, 6);
+      _aff(N, (*t)[3], 1, 2, 5, 6, 7, 8, 3, 4);
+      return (*t)[1];
+    case 32: if (n2!=12 && n2!=13) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 33:
+      switch(n2)
+      {
+        case 13: *t=initgroup(N,1);
+          return _cr(N, 1, 5, 2, 6, 3, 7, 4, 8);
+        case 18: *t=initgroup(N,1);
+          return _cr(N, 1, 2, 5, 6, 3, 4, 7, 8);
+      }break;
+    case 34:
+      switch(n2)
+      {
+        case 14: *t=initgroup(N,3);
+          _aff(N, (*t)[2], 1, 2, 3, 4, 5, 8, 6, 7);
+          _aff(N, (*t)[3], 1, 2, 3, 4, 5, 7, 8, 6);
+          return _cr(N, 1, 5, 2, 6, 3, 7, 4, 8);
+        case 18: *t=initgroup(N,1);
+          return _cr(N, 1, 2, 5, 6, 3, 4, 8, 7);
+      }break;
+    case 39: if (n2!=24) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 40: if (n2!=23) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 41:
+      switch(n2)
+      {
+        case 24: *t=initgroup(N,1);
+          return _cr(N, 1, 5, 2, 6, 3, 7, 4, 8);
+        case 29: *t=initgroup(N,1);
+          return _cr(N, 1, 2, 5, 6, 3, 4, 7, 8);
+      }break;
+    case 42: if (n2!=34) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 5, 6, 8, 7);
+    case 45: if (n2!=41 && n2!=42) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+    case 46: if (n2!=28) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 5, 6, 3, 4, 7, 8);
+    case 47: if (n2!=35) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 5, 6, 3, 4, 7, 8);
+    case 49: if (n2!=48) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 7);
+      return (*t)[1];
+  }
+  *t=initgroup(N,1); return (*t)[1];
+}
+
+static long
+galoismodulo8(long EVEN, GEN pol, GEN dpol)
+{
+  long res, gr[51];
+  pari_sp av = avma;
+  long **GR = (long**)cgeti(49);
+  GEN TYP = partitions_galois(8);
+
+/* List of possible types in group j: GR[j][0] = #GR[j] if
+ * the group is odd, - #GR[j] if even */
+  GR[ 1]= _gr(  4, 1,5,15,22);
+  GR[ 2]= _gr( -3, 1,5,15);
+  GR[ 3]= _gr( -2, 1,5);
+  GR[ 4]= _gr( -3, 1,5,15);
+  GR[ 5]= _gr( -3, 1,5,15);
+  GR[ 6]= _gr(  5, 1,4,5,15,22);
+  GR[ 7]= _gr(  5, 1,3,5,15,22);
+  GR[ 8]= _gr(  5, 1,4,5,15,22);
+  GR[ 9]= _gr( -4, 1,3,5,15);
+  GR[10]= _gr( -4, 1,3,5,15);
+  GR[11]= _gr( -4, 1,3,5,15);
+  GR[12]= _gr( -5, 1,5,9,15,20);
+  GR[13]= _gr( -4, 1,5,9,20);
+  GR[14]= _gr( -4, 1,5,9,15);
+  GR[15]= _gr(  6, 1,3,4,5,15,22);
+  GR[16]= _gr(  5, 1,3,5,15,22);
+  GR[17]= _gr(  7, 1,3,5,11,13,15,22);
+  GR[18]= _gr( -4, 1,3,5,15);
+  GR[19]= _gr( -5, 1,3,5,12,15);
+  GR[20]= _gr( -4, 1,3,5,15);
+  GR[21]= _gr(  5, 1,3,5,13,15);
+  GR[22]= _gr( -4, 1,3,5,15);
+  GR[23]= _gr(  7, 1,4,5,9,15,20,22);
+  GR[24]= _gr( -6, 1,3,5,9,15,20);
+  GR[25]= _gr( -3, 1,5,21);
+  GR[26]= _gr(  8, 1,3,4,5,11,13,15,22);
+  GR[27]= _gr(  8, 1,2,3,4,5,13,15,22);
+  GR[28]= _gr(  7, 1,3,5,12,13,15,22);
+  GR[29]= _gr( -5, 1,3,5,12,15);
+  GR[30]= _gr(  7, 1,3,4,5,11,13,15);
+  GR[31]= _gr(  7, 1,2,3,4,5,13,15);
+  GR[32]= _gr( -6, 1,3,5,9,15,20);
+  GR[33]= _gr( -6, 1,3,5,9,15,20);
+  GR[34]= _gr( -5, 1,3,5,9,15);
+  GR[35]= _gr( 10, 1,2,3,4,5,11,12,13,15,22);
+  GR[36]= _gr( -5, 1,5,9,20,21);
+  GR[37]= _gr( -5, 1,5,9,15,21);
+  GR[38]= _gr( 11, 1,2,3,4,5,9,10,13,15,19,20);
+  GR[39]= _gr( -7, 1,3,5,9,12,15,20);
+  GR[40]= _gr( 10, 1,3,4,5,9,11,13,15,20,22);
+  GR[41]= _gr( -7, 1,3,5,9,12,15,20);
+  GR[42]= _gr( -8, 1,3,5,6,8,9,15,20);
+  GR[43]= _gr(  8, 1,4,5,9,15,19,21,22);
+  GR[44]= _gr( 14, 1,2,3,4,5,9,10,11,12,13,15,19,20,22);
+  GR[45]= _gr( -9, 1,3,5,6,8,9,12,15,20);
+  GR[46]= _gr( 10, 1,3,5,6,8,9,12,13,15,22);
+  GR[47]= _gr( 16, 1,2,3,4,5,6,7,8,9,11,12,13,14,15,20,22);
+  GR[48]= _gr( -8, 1,3,5,9,12,15,20,21);
+
+  gr[0]=51; res = galmodp(EVEN,pol,dpol,TYP,gr,GR);
+  avma=av; if (!res) return 0;
+  return EVEN? 49: 50;
+}
+
+/* DEGREE 9 */
+static long
+galoisprim9(long EVEN, buildroot *BR)
+{
+  long rep;
+
+  if (!EVEN)
+  {
+    rep=isin_G_H(BR,34,26);
+    if (!rep) return 34;
+    rep=isin_G_H(BR,26,19);
+    if (!rep) return 26;
+    rep=isin_G_H(BR,19,16);
+    if (rep) return 16;
+    rep=isin_G_H(BR,19,15);
+    return rep? 15: 19;
+  }
+  rep=isin_G_H(BR,33,32);
+  if (!rep) goto PRIM_9_7;
+  rep=isin_G_H(BR,32,27);
+  return rep? 27: 32;
+
+PRIM_9_7:
+  rep=isin_G_H(BR,33,23);
+  if (!rep) return 33;
+  rep=isin_G_H(BR,23,14);
+  if (!rep) return 23;
+  rep=isin_G_H(BR,14,9);
+  return rep? 9: 14;
+}
+
+static long
+galoisimpodd9(buildroot *BR)
+{
+  long rep;
+
+  rep=isin_G_H(BR,31,29);
+  if (!rep) goto IMPODD_9_5;
+  rep=isin_G_H(BR,29,20);
+  if (!rep) return 29;
+IMPODD_9_3:
+  rep=isin_G_H(BR,20,12);
+  if (!rep) return 20;
+IMPODD_9_4:
+  rep=isin_G_H(BR,12,4);
+  return rep? 4: 12;
+
+IMPODD_9_5:
+  rep=isin_G_H(BR,31,28);
+  if (!rep) goto IMPODD_9_9;
+  rep=isin_G_H(BR,28,22);
+  if (!rep) return 28;
+IMPODD_9_7:
+  rep=isin_G_H(BR,22,13);
+  if (!rep) return 22;
+IMPODD_9_8:
+  rep=isin_G_H(BR,13,4);
+  return rep? 4: 13;
+
+IMPODD_9_9:
+  rep=isin_G_H(BR,31,24);
+  if (!rep) return 31;
+  rep=isin_G_H(BR,24,22);
+  if (rep) goto IMPODD_9_7;
+  rep=isin_G_H(BR,24,20);
+  if (rep) goto IMPODD_9_3;
+  rep=isin_G_H(BR,24,18);
+  if (!rep) return 24;
+  rep=isin_G_H(BR,18,13);
+  if (rep) goto IMPODD_9_8;
+  rep=isin_G_H(BR,18,12);
+  if (rep) goto IMPODD_9_4;
+  rep=isin_G_H(BR,18,8);
+  if (!rep) return 18;
+  rep=isin_G_H(BR,8,4);
+  return rep? 4: 8;
+}
+
+static long
+galoisimpeven9(buildroot *BR)
+{
+  long rep;
+
+  rep=isin_G_H(BR,30,25);
+  if (!rep) goto IMPEVEN_9_7;
+  rep=isin_G_H(BR,25,17);
+  if (!rep) return 25;
+IMPEVEN_9_3:
+  rep=isin_G_H(BR,17,7);
+  if (!rep) goto IMPEVEN_9_5;
+IMPEVEN_9_4:
+  rep=isin_G_H(BR,7,2);
+  return rep? 2: 7;
+
+IMPEVEN_9_5:
+  rep=isin_G_H(BR,17,6);
+  if (!rep) return 17;
+IMPEVEN_9_6:
+  rep=isin_G_H(BR,6,1);
+  return rep? 1: 6;
+
+IMPEVEN_9_7:
+  rep=isin_G_H(BR,30,21);
+  if (!rep) return 30;
+  rep=isin_G_H(BR,21,17);
+  if (rep) goto IMPEVEN_9_3;
+  rep=isin_G_H(BR,21,11);
+  if (!rep) goto IMPEVEN_9_13;
+  rep=isin_G_H(BR,11,7);
+  if (rep) goto IMPEVEN_9_4;
+  rep=isin_G_H(BR,11,5);
+  if (!rep) return 11;
+  rep=isin_G_H(BR,5,2);
+  return rep? 2: 5;
+
+IMPEVEN_9_13:
+  rep=isin_G_H(BR,21,10);
+  if (!rep) return 21;
+  rep=isin_G_H(BR,10,6);
+  if (rep) goto IMPEVEN_9_6;
+  rep=isin_G_H(BR,10,3);
+  if (!rep) return 10;
+  rep=isin_G_H(BR,3,1);
+  return rep? 1: 3;
+}
+
+static long
+closure9(long EVEN, buildroot *BR)
+{
+  long rep;
+  if (!EVEN)
+  {
+    rep=isin_G_H(BR,34,31);
+    if (rep) return galoisimpodd9(BR);
+  }
+  else
+  {
+    rep=isin_G_H(BR,33,30);
+    if (rep) return galoisimpeven9(BR);
+  }
+  return galoisprim9(EVEN, BR);
+}
+
+static PERM
+data9(long N, long n1, long n2, GROUP *t)
+{
+  switch(n1)
+  {
+    case 6: if (n2!=1) break;
+      *t=initgroup(N,3);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 9, 7);
+      _aff(N, (*t)[3], 1, 2, 3, 4, 5, 6, 9, 7, 8);
+      return (*t)[1];
+    case 7: if (n2!=2) break;
+      *t=initgroup(N,3);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 9, 7);
+      _aff(N, (*t)[3], 1, 2, 3, 4, 5, 6, 9, 7, 8);
+      return (*t)[1];
+    case 8: if (n2!=4) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 4, 7, 2, 5, 8, 3, 6, 9);
+      return (*t)[1];
+    case 12: if (n2!=4) break;
+      *t=initgroup(N,3);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 9, 7);
+      _aff(N, (*t)[3], 1, 2, 3, 4, 5, 6, 9, 7, 8);
+      return (*t)[1];
+    case 13: if (n2!=4) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 4, 7, 2, 5, 8, 3, 6, 9);
+    case 14: if (n2!=9) break;
+      *t=initgroup(N,3);
+      _aff(N, (*t)[2], 1, 2, 3, 5, 6, 4, 9, 7, 8);
+      _aff(N, (*t)[3], 1, 2, 3, 6, 4, 5, 8, 9, 7);
+      return (*t)[1];
+    case 17: if (n2!=6) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 7, 8, 9, 4, 5, 6);
+      return (*t)[1];
+    case 21: if (n2!=10) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 7, 8, 9, 4, 5, 6);
+      return (*t)[1];
+    case 33: if (n2!=32) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 7, 9, 8);
+      return (*t)[1];
+  }
+  *t=initgroup(N,1); return (*t)[1];
+}
+
+static long
+galoismodulo9(long EVEN, GEN pol, GEN dpol)
+{
+  long res, gr[35];
+  pari_sp av = avma;
+  long **GR = (long**) cgeti(33);
+  GEN TYP = partitions_galois(9);
+
+  /* 42 TYPES ORDONNES CROISSANT (T[1],...,T[30])*/
+
+  GR[ 1]= _gr( -3, 1,12,30);
+  GR[ 2]= _gr( -2, 1,12);
+  GR[ 3]= _gr( -4, 1,5,12,30);
+  GR[ 4]= _gr(  4, 1,4,12,26);
+  GR[ 5]= _gr( -3, 1,5,12);
+  GR[ 6]= _gr( -4, 1,10,12,30);
+  GR[ 7]= _gr( -3, 1,10,12);
+  GR[ 8]= _gr(  5, 1,4,5,12,26);
+  GR[ 9]= _gr( -4, 1,5,12,18);
+  GR[10]= _gr( -6, 1,5,10,12,25,30);
+  GR[11]= _gr( -5, 1,5,10,12,25);
+  GR[12]= _gr(  5, 1,4,10,12,26);
+  GR[13]= _gr(  5, 1,4,10,12,26);
+  GR[14]= _gr( -4, 1,5,12,18);
+  GR[15]= _gr(  5, 1,5,12,18,29);
+  GR[16]= _gr(  6, 1,4,5,12,18,26);
+  GR[17]= _gr( -5, 1,6,10,12,30);
+  GR[18]= _gr(  7, 1,4,5,10,12,25,26);
+  GR[19]= _gr(  7, 1,4,5,12,18,26,29);
+  GR[20]= _gr(  9, 1,4,6,9,10,12,24,26,30);
+  GR[21]= _gr( -7, 1,5,6,10,12,25,30);
+  GR[22]= _gr(  7, 1,4,6,10,12,26,30);
+  GR[23]= _gr( -6, 1,5,10,12,18,25);
+  GR[24]= _gr( 11, 1,4,5,6,9,10,12,24,25,26,30);
+  GR[25]= _gr( -7, 1,3,6,8,10,12,30);
+  GR[26]= _gr(  9, 1,4,5,10,12,18,25,26,29);
+  GR[27]= _gr( -5, 1,5,12,27,30);
+  GR[28]= _gr( 12, 1,2,3,4,6,7,8,10,11,12,26,30);
+  GR[29]= _gr( 12, 1,3,4,6,8,9,10,12,15,24,26,30);
+  GR[30]= _gr(-11, 1,3,5,6,8,10,12,14,17,25,30);
+  GR[31]= _gr( 19, 1,2,3,4,5,6,7,8,9,10,11,12,14,15,17,24,25,26,30);
+  GR[32]= _gr( -7, 1,5,10,12,25,27,30);
+
+  gr[0]=35; res = galmodp(EVEN,pol,dpol,TYP,gr,GR);
+  avma=av; if (!res) return 0;
+  return EVEN? 33: 34;
+}
+
+/* DEGREE 10 */
+static long
+galoisprim10(long EVEN, buildroot *BR)
+{
+  long rep;
+  if (EVEN)
+  {
+    rep=isin_G_H(BR,44,31);
+    if (!rep) return 44;
+    rep=isin_G_H(BR,31,26);
+    if (!rep) return 31;
+    rep=isin_G_H(BR,26,7);
+    return rep? 7: 26;
+  }
+  else
+  {
+    rep=isin_G_H(BR,45,35);
+    if (!rep) return 45;
+    rep=isin_G_H(BR,35,32);
+    if (!rep) goto PRIM_10_7;
+    rep=isin_G_H(BR,32,13);
+    return rep? 13: 32;
+
+   PRIM_10_7:
+    rep=isin_G_H(BR,35,30);
+    return rep? 30: 35;
+  }
+}
+
+static long
+galoisimpeven10(buildroot *BR, long nogr)
+{
+  long rep;
+  if (nogr==42)
+  {
+    rep=isin_G_H(BR,42,28);
+    if (!rep) return 42;
+    rep=isin_G_H(BR,28,18);
+    return rep? 18: 28;
+  }
+  else
+  {
+    rep=isin_G_H(BR,37,34);
+    if (!rep) goto IMPEVEN_10_5;
+    rep=isin_G_H(BR,34,15);
+    if (rep) goto IMPEVEN_10_7; else return 34;
+
+  IMPEVEN_10_5:
+    rep=isin_G_H(BR,37,24);
+    if (!rep) return 37;
+    rep=isin_G_H(BR,24,15);
+    if (!rep) return 24;
+  IMPEVEN_10_7:
+    rep=isin_G_H(BR,15,8);
+    return rep? 8: 15;
+  }
+}
+
+static long
+galoisimpodd10(buildroot *BR, long nogr)
+{
+  long rep;
+  if (nogr==43)
+  {
+    rep=isin_G_H(BR,43,41);
+    if (!rep) goto IMPODD_10_3;
+    rep=isin_G_H(BR,41,40);
+    if (rep) goto IMPODD_10_4; else goto IMPODD_10_5;
+
+   IMPODD_10_3:
+    rep=isin_G_H(BR,43,33);
+    if (rep) goto IMPODD_10_6; else return 43;
+
+   IMPODD_10_4:
+    rep=isin_G_H(BR,40,21);
+    if (rep) goto IMPODD_10_7; else goto IMPODD_10_8;
+
+   IMPODD_10_5:
+    rep=isin_G_H(BR,41,27);
+    if (rep) goto IMPODD_10_9; else goto IMPODD_10_10;
+
+   IMPODD_10_6:
+    rep=isin_G_H(BR,33,27);
+    if (rep) goto IMPODD_10_9; else return 33;
+
+   IMPODD_10_7:
+    rep=isin_G_H(BR,21,10);
+    if (rep) goto IMPODD_10_12; else goto IMPODD_10_13;
+
+   IMPODD_10_8:
+    rep=isin_G_H(BR,40,12);
+    if (rep) goto IMPODD_10_14; else goto IMPODD_10_15;
+
+   IMPODD_10_9:
+    rep=isin_G_H(BR,27,21);
+    if (rep) goto IMPODD_10_7; else goto IMPODD_10_16;
+
+   IMPODD_10_10:
+    rep=isin_G_H(BR,41,22);
+    if (!rep) return 41;
+    rep=isin_G_H(BR,22,12);
+    if (rep) goto IMPODD_10_14; else goto IMPODD_10_18;
+
+   IMPODD_10_12:
+    rep=isin_G_H(BR,10,4);
+    return rep? 4: 10;
+
+   IMPODD_10_13:
+    rep=isin_G_H(BR,21,9);
+    if (rep) goto IMPODD_10_19; else return 21;
+   IMPODD_10_14:
+    rep=isin_G_H(BR,12,4);
+    return rep? 4: 12;
+
+   IMPODD_10_15:
+    rep=isin_G_H(BR,40,11);
+    if (rep) goto IMPODD_10_20; else return 40;
+   IMPODD_10_16:
+    rep=isin_G_H(BR,27,20);
+    if (!rep) goto IMPODD_10_21;
+    rep=isin_G_H(BR,20,10);
+    if (rep) goto IMPODD_10_12; return 20;
+
+   IMPODD_10_18:
+    rep=isin_G_H(BR,22,11);
+    if (rep) goto IMPODD_10_20; else goto IMPODD_10_23;
+
+   IMPODD_10_19:
+    rep=isin_G_H(BR,9,6);
+    if (rep) goto IMPODD_10_24; else goto IMPODD_10_25;
+
+   IMPODD_10_20:
+    rep=isin_G_H(BR,11,3);
+    if (rep) goto IMPODD_10_26; else return 11;
+
+   IMPODD_10_21:
+    rep=isin_G_H(BR,27,19);
+    if (rep) goto IMPODD_10_27;
+    rep=isin_G_H(BR,27,17);
+    if (rep) goto IMPODD_10_28; else return 27;
+
+   IMPODD_10_23:
+    rep=isin_G_H(BR,22,5);
+    if (rep) goto IMPODD_10_29; else return 22;
+
+   IMPODD_10_24:
+    rep=isin_G_H(BR,6,2);
+    if (rep) return 2; else goto IMPODD_10_30;
+
+   IMPODD_10_25:
+    rep=isin_G_H(BR,9,3);
+    if (!rep) return 9;
+   IMPODD_10_26:
+    rep=isin_G_H(BR,3,2);
+    if (rep) return 2; else goto IMPODD_10_31;
+
+   IMPODD_10_27:
+    rep=isin_G_H(BR,19,9);
+    if (rep) goto IMPODD_10_19; else return 19;
+
+   IMPODD_10_28:
+    rep=isin_G_H(BR,17,10);
+    if (rep) goto IMPODD_10_12; else goto IMPODD_10_32;
+
+   IMPODD_10_29:
+    rep=isin_G_H(BR,5,4);
+    if (rep) return 4; else goto IMPODD_10_33;
+
+   IMPODD_10_30:
+    rep=isin_G_H(BR,6,1);
+    return rep? 1: 6;
+
+   IMPODD_10_31:
+    rep=isin_G_H(BR,3,1);
+    return rep? 1: 3;
+
+   IMPODD_10_32:
+    rep=isin_G_H(BR,17,9);
+    if (rep) goto IMPODD_10_19; else goto IMPODD_10_60;
+
+   IMPODD_10_33:
+    rep=isin_G_H(BR,5,3);
+    if (rep) goto IMPODD_10_26; else return 5;
+
+   IMPODD_10_60:
+    rep=isin_G_H(BR,17,5);
+    if (rep) goto IMPODD_10_29; else return 17;
+  }
+  else
+  {
+    rep=isin_G_H(BR,39,38);
+    if (!rep) goto IMPODD_10_36;
+    rep=isin_G_H(BR,38,25);
+    if (rep) goto IMPODD_10_37; else goto IMPODD_10_38;
+
+   IMPODD_10_36:
+    rep=isin_G_H(BR,39,36);
+    if (rep) goto IMPODD_10_39; else goto IMPODD_10_40;
+
+   IMPODD_10_37:
+    rep=isin_G_H(BR,25,4);
+    return rep? 4: 25;
+
+   IMPODD_10_38:
+    rep=isin_G_H(BR,38,12);
+    if (rep) goto IMPODD_10_41; else return 38;
+
+   IMPODD_10_39:
+    rep=isin_G_H(BR,36,23);
+    if (rep) goto IMPODD_10_42; else goto IMPODD_10_43;
+
+   IMPODD_10_40:
+    rep=isin_G_H(BR,39,29);
+    if (rep) goto IMPODD_10_44; else goto IMPODD_10_45;
+
+   IMPODD_10_41:
+    rep=isin_G_H(BR,12,4);
+    return rep? 4: 12;
+
+   IMPODD_10_42:
+    rep=isin_G_H(BR,23,16);
+    if (rep) goto IMPODD_10_46; else goto IMPODD_10_47;
+
+   IMPODD_10_43:
+    rep=isin_G_H(BR,36,11);
+    if (rep) goto IMPODD_10_48; else return 36;
+
+   IMPODD_10_44:
+    rep=isin_G_H(BR,29,25);
+    if (rep) goto IMPODD_10_37; else goto IMPODD_10_49;
+
+   IMPODD_10_45:
+    rep=isin_G_H(BR,39,22);
+    if (rep) goto IMPODD_10_50; else return 39;
+
+   IMPODD_10_46:
+    rep=isin_G_H(BR,16,2);
+    return rep? 2: 16;
+
+   IMPODD_10_47:
+    rep=isin_G_H(BR,23,14);
+    if (rep) goto IMPODD_10_51; else goto IMPODD_10_52;
+
+   IMPODD_10_48:
+    rep=isin_G_H(BR,11,3);
+    if (rep) goto IMPODD_10_53; else return 11;
+
+   IMPODD_10_49:
+    rep=isin_G_H(BR,29,23);
+    if (rep) goto IMPODD_10_42; else goto IMPODD_10_54;
+
+   IMPODD_10_50:
+    rep=isin_G_H(BR,22,12);
+    if (rep) goto IMPODD_10_41; else goto IMPODD_10_55;
+
+   IMPODD_10_51:
+    rep=isin_G_H(BR,14,1);
+    return rep? 1: 14;
+
+   IMPODD_10_52:
+    rep=isin_G_H(BR,23,3);
+    if (!rep) return 23;
+   IMPODD_10_53:
+    rep=isin_G_H(BR,3,2);
+    if (rep) return 2; else goto IMPODD_10_57;
+
+   IMPODD_10_54:
+    rep=isin_G_H(BR,29,5);
+    if (rep) goto IMPODD_10_58; else return 29;
+
+   IMPODD_10_55:
+    rep=isin_G_H(BR,22,11);
+    if (rep) goto IMPODD_10_48;
+    rep=isin_G_H(BR,22,5);
+    if (rep) goto IMPODD_10_58; else return 22;
+
+   IMPODD_10_57:
+    rep=isin_G_H(BR,3,1);
+    return rep? 1: 3;
+
+   IMPODD_10_58:
+    rep=isin_G_H(BR,5,4);
+    if (rep) return 4;
+    rep=isin_G_H(BR,5,3);
+    if (rep) goto IMPODD_10_53; else return 5;
+  }
+}
+
+static long
+closure10(long EVEN, buildroot *BR)
+{
+  long rep;
+  if (EVEN)
+  {
+    rep=isin_G_H(BR,44,42);
+    if (rep) return galoisimpeven10(BR,42);
+    rep=isin_G_H(BR,44,37);
+    if (rep) return galoisimpeven10(BR,37);
+  }
+  else
+  {
+    rep=isin_G_H(BR,45,43);
+    if (rep) return galoisimpodd10(BR,43);
+    rep=isin_G_H(BR,45,39);
+    if (rep) return galoisimpodd10(BR,39);
+  }
+  return galoisprim10(EVEN, BR);
+}
+
+static PERM
+data10(long N, long n1,long n2,GROUP *t)
+{
+  switch(n1)
+  {
+    case 6: if (n2!=2) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 5, 6, 10, 9, 8, 7);
+    case 9: if (n2!=3 && n2!=6) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 10, 9, 8, 7);
+      return (*t)[1];
+    case 10: *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 10, 9, 8, 7);
+      return (*t)[1];
+    case 14: case 16:*t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 17: if (n2!=5) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 10, 9, 8, 7);
+      return (*t)[1];
+    case 19: case 20: *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 10, 7, 9);
+      return (*t)[1];
+    case 21: if (n2!=10) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 5, 6, 8, 10, 7, 9);
+    case 23: if (n2!=3) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 25: *t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 26: *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 4, 9, 6, 8, 10, 3, 7, 5);
+      return _cr(N, 1, 2, 3, 10, 6, 5, 7, 4, 8, 9);
+    case 27: if (n2!=17 && n2!=21) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 10, 7, 9);
+      return (*t)[1];
+    case 28: *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 8, 10, 7, 9);
+      return (*t)[1];
+    case 29: if (n2!=5) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 32: *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 4, 9, 6, 8, 10, 3, 7, 5);
+      return _cr(N, 1, 2, 3, 10, 6, 5, 7, 4, 8, 9);
+    case 36: if (n2!=11) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 38: if (n2!=12) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 39: if (n2!=22) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
+    case 40: if (n2!=12) break;
+      *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 5, 6, 7, 8, 10, 9);
+    case 41: if (n2!=22 && n2!=40) break;
+      *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 7, 8, 10, 9);
+      return (*t)[1];
+  }
+  *t=initgroup(N,1); return (*t)[1];
+}
+
+static long
+galoismodulo10(long EVEN, GEN pol, GEN dpol)
+{
+  long res, gr[46];
+  pari_sp av = avma;
+  long **GR = (long**) cgeti(45);
+  GEN TYP = partitions_galois(10);
+
+  GR[ 1]= _gr(  4, 1,6,30,42);
+  GR[ 2]= _gr(  3, 1,6,30);
+  GR[ 3]= _gr(  5, 1,5,6,30,42);
+  GR[ 4]= _gr(  4, 1,5,23,30);
+  GR[ 5]= _gr(  7, 1,5,6,22,23,30,42);
+  GR[ 6]= _gr(  5, 1,6,24,30,42);
+  GR[ 7]= _gr( -4, 1,5,14,30);
+  GR[ 8]= _gr( -4, 1,3,5,30);
+  GR[ 9]= _gr(  6, 1,5,6,24,30,42);
+  GR[10]= _gr(  5, 1,5,23,24,30);
+  GR[11]= _gr(  7, 1,5,6,11,30,33,42);
+  GR[12]= _gr(  7, 1,5,6,11,23,30,33);
+  GR[13]= _gr(  7, 1,4,5,14,23,30,34);
+  GR[14]= _gr(  8, 1,2,3,4,5,6,30,42);
+  GR[15]= _gr( -6, 1,3,5,18,22,30);
+  GR[16]= _gr(  7, 1,3,5,6,17,23,30);
+  GR[17]= _gr(  8, 1,5,6,22,23,24,30,42);
+  GR[18]= _gr( -6, 1,5,22,24,30,40);
+  GR[19]= _gr(  7, 1,5,6,22,24,30,42);
+  GR[20]= _gr(  6, 1,5,22,23,24,30);
+  GR[21]= _gr(  9, 1,3,5,6,23,24,26,30,42);
+  GR[22]= _gr( 11, 1,3,5,6,11,13,22,23,30,33,42);
+  GR[23]= _gr( 12, 1,2,3,4,5,6,17,18,22,23,30,42);
+  GR[24]= _gr( -7, 1,3,5,18,22,30,40);
+  GR[25]= _gr(  8, 1,3,5,18,22,23,30,39);
+  GR[26]= _gr( -5, 1,5,14,22,30);
+  GR[27]= _gr( 10, 1,3,5,6,22,23,24,26,30,42);
+  GR[28]= _gr( -8, 1,3,5,22,24,26,30,40);
+  GR[29]= _gr( 14, 1,2,3,4,5,6,17,18,22,23,30,39,40,42);
+  GR[30]= _gr(  8, 1,5,6,14,22,30,39,42);
+  GR[31]= _gr( -6, 1,5,14,22,30,40);
+  GR[32]= _gr(  8, 1,4,5,14,22,23,30,34);
+  GR[33]= _gr( 14, 1,3,5,6,15,17,22,23,24,26,29,30,40,42);
+  GR[34]= _gr( -9, 1,3,5,11,13,18,22,30,32);
+  GR[35]= _gr( 12, 1,4,5,6,14,22,23,30,34,39,40,42);
+  GR[36]= _gr( 18, 1,2,3,4,5,6,11,12,13,17,18,22,23,30,31,32,33,42);
+  GR[37]= _gr(-12, 1,3,5,11,13,16,18,22,30,32,35,40);
+  GR[38]= _gr( 18, 1,3,4,5,6,11,13,15,17,18,21,22,23,30,32,33,35,39);
+  GR[39]= _gr( 24, 1,2,3,4,5,6,11,12,13,15,16,17,18,21,22,23,30,31,32,33,35,39,40,42);
+  GR[40]= _gr( 14, 1,3,5,6,7,9,11,23,24,26,27,30,33,42);
+  GR[41]= _gr( 18, 1,3,5,6,7,9,11,13,16,20,22,23,24,26,27,30,33,42);
+  GR[42]= _gr(-17, 1,3,5,7,9,11,13,16,18,20,22,24,26,27,30,35,40);
+  GR[43]= _gr( 32, 1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,22,23,24,25,26,27,28,29,30,33,35,40,42);
+  GR[44]= _gr(-22, 1,3,5,7,9,11,13,14,16,18,20,22,24,26,27,30,32,35,36,38,40,41);
+
+  gr[0]=46; res = galmodp(EVEN,pol,dpol,TYP,gr,GR);
+  avma=av; if (!res) return 0;
+  return EVEN? 44: 45;
+}
+
+/* DEGREE 11 */
+static long
+closure11(long EVEN, buildroot *BR)
+{
+  long rep;
+  if (EVEN)
+  {
+    rep=isin_G_H(BR,7,6);
+    if (!rep) return 7;
+    rep=isin_G_H(BR,6,5);
+    if (!rep) return 6;
+    rep=isin_G_H(BR,5,3);
+    if (!rep) return 5;
+    rep=isin_G_H(BR,3,1);
+    return rep? 1: 3;
+  }
+  else
+  {
+    GEN h = BR->p, r = compositum(h, h);
+    r = gel(r,lg(r)-1);
+    if (degpol(r) == 22) return 2; /* D11 */
+    h = leafcopy(h); setvarn(h, MAXVARN);
+    setvarn(r, 0); r = nffactor(h, r);
+    /* S11 (P10*P10*P90) or F_110[11] (11 factors of degree 10) */
+    return (lgcols(r)-1 == 11)? 4: 8;
+  }
+}
+
+static PERM
+data11(long N, long n1, GROUP *t)
+{
+  switch(n1)
+  {
+    case 5: *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 7, 8, 6, 11, 5, 9, 4, 10);
+    case 6: *t=initgroup(N,1);
+      return _cr(N, 1, 2, 3, 4, 6, 10, 11, 9, 7, 5, 8);
+    case 7: *t=initgroup(N,2);
+      _aff(N, (*t)[2], 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 10);
+      return (*t)[1];
+  }
+  *t=initgroup(N,1); return (*t)[1];
+}
+
+static long
+galoismodulo11(long EVEN, GEN pol, GEN dpol)
+{
+  long res, gr[6] = {0, 1, 1, 1, 1, 1};
+  pari_sp av = avma;
+  GEN TYP = cgetg(EVEN? 9: 6, t_VEC);
+
+  gel(TYP,1) = _typ(1, 11);
+  if (EVEN)
+  {
+    gel(TYP,2) = _typ(3, 8,2,1);
+    gel(TYP,3) = _typ(3, 6,3,2);
+    gel(TYP,4) = _typ(3, 5,5,1);
+    gel(TYP,5) = _typ(5, 4,4,1,1,1);
+    gel(TYP,6) = _typ(5, 3,3,3,1,1);
+    gel(TYP,7) = _typ(7, 2,2,2,2,1,1,1);
+    gel(TYP,8) = _typ(11, 1,1,1,1,1,1,1,1,1,1,1);
+  }
+  else
+  {
+    gel(TYP,2) = _typ(2, 10,1);
+    gel(TYP,3) = _typ(3, 5,5,1);
+    gel(TYP,4) = _typ(6, 2,2,2,2,2,1);
+    gel(TYP,5) = _typ(11, 1,1,1,1,1,1,1,1,1,1,1);
+  }
+  res = galmodp(EVEN,pol,dpol,TYP,gr,NULL);
+  avma=av; if (!res) return 0;
+  return EVEN? 7: 8;
+}
+
+static void
+init_isin(long N, long n1, long n2, GROUP *tau, PERM *s0, resolv *R)
+{
+  int fl = 1;
+  if (DEBUGLEVEL) {
+    err_printf("\n*** Entering isin_%ld_G_H_(%ld,%ld)\n",N,n1,n2); err_flush();
+  }
+  switch(N)
+  {
+    case 8:
+      if ((n1==47 && n2==46) || (n1==44 && n2==40)) fl=0;
+      *s0=data8(N, n1,n2,tau); break;
+    case 9:
+      if ((n1==31 && n2==29) || (n1==34 && n2==31) || (n1==33 && n2==30)) fl=0;
+      *s0=data9(N,n1,n2,tau); break;
+    case 10:
+      if ((n1==45 && (n2==43||n2==39))
+       || (n1==44 && (n2==42||n2==37))
+       || (n1==43 && (n2==41||n2==33))
+       || (n1==42 && n2==28)
+       || (n1==41 && (n2==40||n2==27||n2==22))
+       || (n1==40 && (n2==21||n2==11))
+       || (n1==39 && (n2==38||n2==36||n2==29||n2==22))
+       || (n1==38 && (n2==25||n2==12))
+       || (n1==37 && (n2==34||n2==24))
+       || (n1==36 && (n2==23||n2==11))
+       || (n1==34 && n2==15)
+       || (n1==33 && n2==27)
+       || (n1==29 && (n2==25||n2==23||n2==5))
+       || (n1==28 && n2==18)
+       || (n1==27 && (n2==20||n2==19||n2==17))
+       || (n1==25 && n2==4)
+       || (n1==24 && n2==15)
+       || (n1==23 && (n2==16||n2==3))
+       || (n1==22 && (n2==12||n2==11||n2==5))
+       || (n1==21 && (n2==10||n2==9))
+       || (n1==17 && n2==5)
+       || (n1==16 && n2==2)
+       || (n1==14 && n2==1)
+       || (n1==12 && n2==4)
+       || (n1==11 && n2==3)
+       || (n1==10 && n2==4)
+       || (n1== 9 && n2==3)
+       || (n1== 6 && n2==1)
+       || (n1== 5 && n2==3)) fl = 0;
+      *s0=data10(N,n1,n2,tau); break;
+    default: /* case 11: */
+      *s0=data11(N,n1,tau); break;
+  }
+  if (fl) lireresolv(n1,n2,N,R); else { R->a = NULL; R->nm = n1; R->nv = n2; }
+}
+
+static long
+isin_G_H(buildroot *BR, long n1, long n2)
+{
+  pari_sp av = avma;
+  const long N = BR->N;
+  PERM s0, ww;
+  GROUP tau, ss = lirecoset(n1,n2,N);
+  resolv R;
+
+  init_isin(N,n1,n2, &tau, &s0, &R);
+  ww = check_isin(BR, &R, tau, ss);
+  if (ww)
+  {
+    GEN z = cgetg(N+1, t_VEC);
+    long i, j, l = lg(BR->r);
+    s0 = permmul(ww, s0);
+    if (DEBUGLEVEL)
+    {
+      err_printf("\n    Output of isin_%ld_G_H(%ld,%ld): %ld",N,n1,n2,n2);
+      err_printf("\n    Reordering of the roots: "); printperm(s0);
+      err_flush();
+    }
+    for (i = 1; i < l; i++)
+    {
+      GEN p1 = gel(BR->r,i);
+      for (j=1; j<=N; j++) gel(z,j) = gel(p1,s0[j]);
+      for (j=1; j<=N; j++) gel(p1,j) = gel(z,j);
+    }
+    avma = av; return n2;
+  }
+  if (DEBUGLEVEL)
+  {
+    err_printf("    Output of isin_%ld_G_H(%ld,%ld): not included.\n",N,n1,n2);
+    err_flush();
+  }
+  avma = av; return 0;
+}
+
+GEN
+polgaloisnamesbig(long n, long k)
+{
+  pari_sp av = avma;
+  char *s = stack_malloc(strlen(pari_datadir) + 13 + 20 + 3);
+  pariFILE *f;
+  GEN V;
+
+  (void)sprintf(s, "%s/galdata/NAM%ld", pari_datadir, n);
+  f = pari_fopengz(s);
+  if (!f)
+  {
+    pari_warn(warner,"Galois names files not available, please upgrade galdata\n[missing %s]",s);
+    avma = av; return strtoGENstr("");
+  }
+  V = gp_read_stream(f->file);
+  if (!V || typ(V)!=t_VEC || k>=lg(V)) pari_err_FILE("galois file %s",s);
+  pari_fclose(f);
+  return gerepilecopy(av, gel(V,k));
+}
+
+/* pol a monic ZX */
+GEN
+galoisbig(GEN pol, long prec)
+{
+  pari_sp av = avma;
+  const long *tab;
+  const long tab8[]={0,
+    8,8,8,8,8,16,16,16,16,16, 16,24,24,24,32,32,32,32,32,32,
+    32,32,48,48,56,64,64,64,64,64, 64,96,96,96,128,168,168,192,192,192,
+    192,288,336,384,576,576,1152,1344,20160,40320};
+  const long tab9[]={0,
+    9,9,18,18,18,27,27,36,36,54, 54,54,54,72,72,72,81,108,144,162,
+    162,162,216,324,324,432,504,648,648,648, 1296,1512,181440,362880};
+  const long tab10[]={0,
+    10,10,20,20,40,50,60,80,100,100, 120,120,120,160,160,160,200,200,200,200,
+    200,240,320,320,320,360,400,400,640,720, 720,720,800,960,1440,
+    1920,1920,1920,3840,7200,14400,14400,28800,1814400,3628800};
+  const long tab11[]={0, 11,22,55,110,660,7920,19958400,39916800};
+  GEN res, dpol = ZX_disc(pol);
+  long t = 0, N = degpol(pol), EVEN = Z_issquare(dpol);
+
+  if (DEBUGLEVEL)
+  {
+    err_printf("Galoisbig: polynomial #1 = %Ps\n", pol);
+    err_printf("%s group\n", EVEN? "EVEN": "ODD"); err_flush();
+  }
+  switch(N)
+  {
+    case 8: t = galoismodulo8(EVEN,pol,dpol);  tab=tab8; break;
+    case 9: t = galoismodulo9(EVEN,pol,dpol);  tab=tab9; break;
+    case 10:t = galoismodulo10(EVEN,pol,dpol); tab=tab10; break;
+    case 11:t = galoismodulo11(EVEN,pol,dpol); tab=tab11; break;
+    default: pari_err_IMPL("galois in degree > 11");
+      return NULL; /* not reached */
+  }
+  if (!t)
+  {
+    buildroot BR;
+    long i;
+    GEN r, z = cgetg(N + 1, t_VEC);
+    for (i = 1; i <= N; i++)
+    {
+      GEN v = cgetg(i+2,t_VECSMALL);
+      gel(z,i) = v; v[1] = 0;
+    }
+    BR.coef = z;
+    BR.p = pol;
+    BR.pr = prec + nbits2extraprec((long)(cauchy_bound(pol) / LOG2));
+    BR.prmax = BR.pr + BIGDEFAULTPREC-2;
+    BR.N = N;
+    BR.r = vectrunc_init(N+1);
+    r = gclone ( QX_complex_roots(BR.p, BR.prmax) );
+    vectrunc_append(BR.r, r); preci(r, BR.pr);
+    switch(N)
+    {
+      case  8: t = closure8(EVEN, &BR); break;
+      case  9: t = closure9(EVEN, &BR); break;
+      case 10: t = closure10(EVEN, &BR); break;
+      case 11: t = closure11(EVEN, &BR); break;
+    }
+    for (i = 1; i < lg(BR.r); i++) gunclone(gel(BR.r,i));
+  }
+  avma = av;
+  res    = cgetg(5,t_VEC);
+  gel(res,1) = stoi(tab[t]);
+  gel(res,2) = stoi(EVEN? 1: -1);
+  gel(res,3) = stoi(t);
+  gel(res,4) = polgaloisnamesbig(N,t);
+  return res;
+}
diff --git a/src/modules/galpol.c b/src/modules/galpol.c
new file mode 100644
index 0000000..50e8775
--- /dev/null
+++ b/src/modules/galpol.c
@@ -0,0 +1,63 @@
+/* Copyright (C) 2000-2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+GEN
+galoisnbpol(long a)
+{
+  GEN n;
+  pariFILE *F;
+  char *s = stack_malloc(strlen(pari_datadir) + 11 + 20 + 1);
+  sprintf(s,"%s/galpol/%ld/nb", pari_datadir, a);
+  F = pari_fopengz(s);
+  if (!F) pari_err_FILE("galpol file",s);
+  n = gp_read_stream(F->file);
+  if (!n || typ(n)!=t_INT) pari_err_FILE("galpol file [incompatible]",s);
+  pari_fclose(F); return n;
+}
+
+GEN
+galoisgetpol(long a, long b, long sig)
+{
+  pariFILE *F;
+  GEN V;
+  const char *si;
+  char *s;
+  if (a<=0) pari_err_DOMAIN("galoisgetpol", "degree", "<=", gen_0, stoi(a));
+  if (b<0) pari_err_DOMAIN("galoisgetpol", "index", "<", gen_0, stoi(b));
+  if (!b) return galoisnbpol(a);
+  switch(sig)
+  {
+    case 1: si="real"; break;
+    case 2: if (a%2==0) { si="complex"; break; }
+      pari_err_DOMAIN("galoisgetpol", "s", ">", gen_1, stoi(sig));
+    default:
+      pari_err_FLAG("galoisgetpol");
+      return NULL;
+  }
+  s = pari_sprintf("%s/galpol/%ld/%ld/%s", pari_datadir, a,b,si);
+  F = pari_fopengz(s); pari_free(s);
+  if (!F)
+  {
+    long n = itos(galoisnbpol(a));
+    if (b > n)
+      pari_err_DOMAIN("galoisgetpol", "group index", ">", stoi(n), stoi(b));
+    else pari_err_FILE("galpol file", F->name);
+  }
+  V = gp_read_stream(F->file);
+  if (!V || typ(V)!=t_VEC) pari_err_FILE("galpol file", F->name);
+  pari_fclose(F); return V;
+}
+
diff --git a/src/modules/genus2red.c b/src/modules/genus2red.c
new file mode 100644
index 0000000..9558773
--- /dev/null
+++ b/src/modules/genus2red.c
@@ -0,0 +1,2290 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/**                                                                **/
+/**                       IGUSA INVARIANTS                         **/
+/**                       (GP2C-generated)                         **/
+/**                                                                **/
+/********************************************************************/
+/*
+j2(a0,a1,a2,a3,a4,a5,a6) = (-120*a0*a6+20*a1*a5-8*a2*a4+3*a3^2) / 4;
+*/
+static GEN
+igusaj2(GEN a0, GEN a1, GEN a2, GEN a3, GEN a4, GEN a5, GEN a6)
+{
+  pari_sp av = avma;
+  return gerepileupto(av, gmul2n(gadd(gsub(gadd(gmul(gmulsg(-120, a0), a6), gmul(gmulsg(20, a1), a5)), gmul(gmulsg(8, a2), a4)), gmulsg(3, gsqr(a3))), -2));
+}
+
+/*
+j4(a0,a1,a2,a3,a4,a5,a6) = (240*(a0*a3*a4*a5+a1*a2*a3*a6)-400*(a0*a2*a5^2+a1^2*a4*a6)-64*(a0*a4^3+a2^3*a6)+16*(a1*a3*a4^2+a2^2*a3*a5)-672*a0*a3^2*a6+240*a1^2*a5^2-112*a1*a2*a4*a5-8*a1*a3^2*a5+16*a2^2*a4^2-16*a2*a3^2*a4+3*a3^4+2640*a0^2*a6^2-880*a0*a1*a5*a6+1312*a0*a2*a4*a6) / 2^7
+*/
+static GEN
+igusaj4(GEN a0, GEN a1, GEN a2, GEN a3, GEN a4, GEN a5, GEN a6)
+{
+  pari_sp av = avma;
+  return gerepileupto(av,
+gmul2n(gadd(gsub(gadd(gadd(gsub(gadd(gsub(gsub(gadd(gsub(gadd(gsub(gsub(gmulsg(240,
+gadd(gmul(gmul(gmul(a0, a3), a4), a5), gmul(gmul(gmul(a1, a2), a3), a6))),
+gmulsg(400, gadd(gmul(gmul(a0, a2), gsqr(a5)), gmul(gmul(gsqr(a1), a4), a6)))),
+gmulsg(64, gadd(gmul(a0, gpowgs(a4, 3)), gmul(gpowgs(a2, 3), a6)))), gmulsg(16,
+gadd(gmul(gmul(a1, a3), gsqr(a4)), gmul(gmul(gsqr(a2), a3), a5)))),
+gmul(gmul(gmulsg(672, a0), gsqr(a3)), a6)), gmul(gmulsg(240, gsqr(a1)),
+gsqr(a5))), gmul(gmul(gmul(gmulsg(112, a1), a2), a4), a5)), gmul(gmul(gmulsg(8,
+a1), gsqr(a3)), a5)), gmul(gmulsg(16, gsqr(a2)), gsqr(a4))),
+gmul(gmul(gmulsg(16, a2), gsqr(a3)), a4)), gmulsg(3, gpowgs(a3, 4))),
+gmul(gmulsg(2640, gsqr(a0)), gsqr(a6))), gmul(gmul(gmul(gmulsg(880, a0), a1),
+a5), a6)), gmul(gmul(gmul(gmulsg(1312, a0), a2), a4), a6)), -7));
+}
+
+/*
+j6(a0,a1,a2,a3,a4,a5,a6) = (1600*(a0^2*a4^2*a5^2+a1^2*a2^2*a6^2)+1600*(a0*a1*a2*a5^3+a1^3*a4*a5*a6)+640*(a0*a1*a3*a4*a5^2+a1^2*a2*a3*a5*a6)-4000*(a0^2*a3*a5^3+a1^3*a3*a6^2)-384*(a0*a1*a4^3*a5+a1*a2^3*a5*a6)-640*(a0*a2^2*a4*a5^2+a1^2*a2*a4^2*a6)+80*(a0*a2*a3^2*a5^2+a1^2*a3^2*a4*a6)+192*(a0*a2*a3*a4^2*a5+a1*a2^2*a3*a4*a6)-48*(a0*a3^3*a4*a5+a1*a2*a3^3*a6)-224*(a1^2*a3*a4^2*a5+a1*a2^2*a3*a5^2)+64*(a1^2*a4^4+a2^4*a5^2)-64*(a1*a2*a3*a4^3+a2^3*a3*a4*a5)+16*(a1*a3^3*a4^2+a2^2*a3^3*a5)-4096*(a0^2 [...]
+*/
+static GEN
+igusaj6(GEN a0, GEN a1, GEN a2, GEN a3, GEN a4, GEN a5, GEN a6)
+{
+  pari_sp av = avma;
+  return gerepileupto(av,
+gmul2n(gsub(gsub(gsub(gsub(gadd(gsub(gadd(gsub(gadd(gadd(gsub(gadd(gadd(gadd(gadd(gsub(gadd(gsub(gsub(gadd(gadd(gadd(gsub(gadd(gsub(gadd(gsub(gsub(gadd(gadd(gsub(gsub(gsub(gadd(gadd(gmulsg(1600,
+gadd(gmul(gmul(gsqr(a0), gsqr(a4)), gsqr(a5)), gmul(gmul(gsqr(a1), gsqr(a2)),
+gsqr(a6)))), gmulsg(1600, gadd(gmul(gmul(gmul(a0, a1), a2), gpowgs(a5, 3)),
+gmul(gmul(gmul(gpowgs(a1, 3), a4), a5), a6)))), gmulsg(640,
+gadd(gmul(gmul(gmul(gmul(a0, a1), a3), a4), gsqr(a5)),
+gmul(gmul(gmul(gmul(gsqr(a1), a2), a3), a5), a6)))), gmulsg(4000,
+gadd(gmul(gmul(gsqr(a0), a3), gpowgs(a5, 3)), gmul(gmul(gpowgs(a1, 3), a3),
+gsqr(a6))))), gmulsg(384, gadd(gmul(gmul(gmul(a0, a1), gpowgs(a4, 3)), a5),
+gmul(gmul(gmul(a1, gpowgs(a2, 3)), a5), a6)))), gmulsg(640,
+gadd(gmul(gmul(gmul(a0, gsqr(a2)), a4), gsqr(a5)), gmul(gmul(gmul(gsqr(a1),
+a2), gsqr(a4)), a6)))), gmulsg(80, gadd(gmul(gmul(gmul(a0, a2), gsqr(a3)),
+gsqr(a5)), gmul(gmul(gmul(gsqr(a1), gsqr(a3)), a4), a6)))), gmulsg(192,
+gadd(gmul(gmul(gmul(gmul(a0, a2), a3), gsqr(a4)), a5), gmul(gmul(gmul(gmul(a1,
+gsqr(a2)), a3), a4), a6)))), gmulsg(48, gadd(gmul(gmul(gmul(a0, gpowgs(a3, 3)),
+a4), a5), gmul(gmul(gmul(a1, a2), gpowgs(a3, 3)), a6)))), gmulsg(224,
+gadd(gmul(gmul(gmul(gsqr(a1), a3), gsqr(a4)), a5), gmul(gmul(gmul(a1,
+gsqr(a2)), a3), gsqr(a5))))), gmulsg(64, gadd(gmul(gsqr(a1), gpowgs(a4, 4)),
+gmul(gpowgs(a2, 4), gsqr(a5))))), gmulsg(64, gadd(gmul(gmul(gmul(a1, a2), a3),
+gpowgs(a4, 3)), gmul(gmul(gmul(gpowgs(a2, 3), a3), a4), a5)))), gmulsg(16,
+gadd(gmul(gmul(a1, gpowgs(a3, 3)), gsqr(a4)), gmul(gmul(gsqr(a2), gpowgs(a3,
+3)), a5)))), gmulsg(4096, gadd(gmul(gmul(gsqr(a0), gpowgs(a4, 3)), a6),
+gmul(gmul(a0, gpowgs(a2, 3)), gsqr(a6))))), gmulsg(6400,
+gadd(gmul(gmul(gmul(gsqr(a0), a2), gsqr(a5)), a6), gmul(gmul(gmul(a0,
+gsqr(a1)), a4), gsqr(a6))))), gmulsg(10560, gadd(gmul(gmul(gmul(gmul(gsqr(a0),
+a3), a4), a5), a6), gmul(gmul(gmul(gmul(a0, a1), a2), a3), gsqr(a6))))),
+gmulsg(2624, gadd(gmul(gmul(gmul(gmul(a0, a1), a3), gsqr(a4)), a6),
+gmul(gmul(gmul(gmul(a0, gsqr(a2)), a3), a5), a6)))),
+gmul(gmul(gmul(gmul(gmulsg(4432, a0), a1), gsqr(a3)), a5), a6)),
+gmul(gmul(gmulsg(8, a2), gpowgs(a3, 4)), a4)), gpowgs(a3, 6)), gmul(gmulsg(320,
+gpowgs(a1, 3)), gpowgs(a5, 3))), gmul(gmul(gmul(gmulsg(64, gsqr(a1)), a2), a4),
+gsqr(a5))), gmul(gmul(gmulsg(176, gsqr(a1)), gsqr(a3)), gsqr(a5))),
+gmul(gmul(gmul(gmulsg(128, a1), gsqr(a2)), gsqr(a4)), a5)),
+gmul(gmul(gmul(gmul(gmulsg(112, a1), a2), gsqr(a3)), a4), a5)),
+gmul(gmul(gmulsg(28, a1), gpowgs(a3, 4)), a5)), gmul(gmul(gmulsg(16, gsqr(a2)),
+gsqr(a3)), gsqr(a4))), gmul(gmulsg(5120, gpowgs(a0, 3)), gpowgs(a6, 3))),
+gmul(gmul(gmulsg(2544, gsqr(a0)), gsqr(a3)), gsqr(a6))), gmul(gmul(gmulsg(312,
+a0), gpowgs(a3, 4)), a6)), gmul(gmul(gmul(gmulsg(14336, gsqr(a0)), a2), a4),
+gsqr(a6))), gmul(gmul(gmul(gmulsg(1024, a0), gsqr(a2)), gsqr(a4)), a6)),
+gmul(gmul(gmul(gmulsg(2560, gsqr(a0)), a1), a5), gsqr(a6))),
+gmul(gmul(gmul(gmulsg(2240, a0), gsqr(a1)), gsqr(a5)), a6)),
+gmul(gmul(gmul(gmul(gmul(gmulsg(6528, a0), a1), a2), a4), a5), a6)),
+gmul(gmul(gmul(gmul(gmulsg(1568, a0), a2), gsqr(a3)), a4), a6)), -10));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**   A REDUCTION ALGORITHM "A LA TATE" FOR CURVES OF GENUS 2      **/
+/**                                                                **/
+/********************************************************************/
+/* Based on genus2reduction-0.3, http://www.math.u-bordeaux1.fr/~liu/G2R/
+ * by Qing Liu <liu at math.u-bordeaux1.fr>
+ * and Henri Cohen <cohen at math.u-bordeaux1.fr>
+
+ * Qing Liu: Modeles minimaux des courbes de genre deux
+ * J. fuer die Reine und Angew. Math., 453 (1994), 137-164.
+ * http://www.math.u-bordeaux1.fr/~liu/articles/modregE.ps */
+
+/* some auxiliary polynomials, gp2c-generated */
+
+/*
+apol2(a0,a1,a2) = -5*a1^2+12*a0*a2;
+*/
+static GEN
+apol2(GEN a0, GEN a1, GEN a2)
+{
+  return gadd(gmulsg(-5, gsqr(a1)), gmul(gmulsg(12, a0), a2));
+}
+
+/*
+apol3(a0,a1,a2,a3) = 5*a1^3+9*a0*(-2*a1*a2+3*a0*a3);
+*/
+static GEN
+apol3(GEN a0, GEN a1, GEN a2, GEN a3)
+{
+  return gadd(gmulsg(5, gpowgs(a1, 3)), gmul(gmulsg(9, a0), gadd(gmul(gmulsg(-2, a1), a2), gmul(gmulsg(3, a0), a3))));
+}
+
+/*
+apol5(a0,a1,a2,a3,a4,a5) = a1^5+3*a0*(-2*a1^3*a2+9*a0*a1^2*a3-36*a0^2*a1*a4+108*a0^3*a5);
+*/
+static GEN
+apol5(GEN a0, GEN a1, GEN a2, GEN a3, GEN a4, GEN a5)
+{
+  return gadd(gpowgs(a1, 5), gmul(gmulsg(3, a0), gadd(gsub(gadd(gmul(gmulsg(-2, gpowgs(a1, 3)), a2), gmul(gmul(gmulsg(9, a0), gsqr(a1)), a3)), gmul(gmul(gmulsg(36, gsqr(a0)), a1), a4)), gmul(gmulsg(108, gpowgs(a0, 3)), a5))));
+}
+
+/*
+bpol2(a0,a1,a2,a3,a4) = 2*a2^2-5*a1*a3+10*a0*a4;
+*/
+static GEN
+bpol2(GEN a0, GEN a1, GEN a2, GEN a3, GEN a4)
+{
+  return gadd(gsub(gmulsg(2, gsqr(a2)), gmul(gmulsg(5, a1), a3)), gmul(gmulsg(10, a0), a4));
+}
+
+static const long VERYBIG = (1L<<20);
+static long
+myval(GEN x, GEN p) { return signe(x)? Z_pval(x,p): VERYBIG; }
+static long
+my3val(GEN x) { return signe(x)? Z_lval(x,3): VERYBIG; }
+/* largest power of p dividing pol */
+static long
+polval(GEN pol, GEN p)
+{
+  long v, i, lx = lg(pol);
+  if (!signe(pol)) return VERYBIG;
+  v = myval(gel(pol,2),p);
+  for(i = 3;i<lx;i++) v = minss(v, myval(gel(pol,i),p));
+  return v;
+}
+/* b in Z[i], return v_3(b) */
+static long
+myval_zi(GEN b) { return minss(my3val(real_i(b)), my3val(imag_i(b))); }
+/* b in Z[i, Y]/(Y^2-3), return v_Y(b) */
+static long
+myval_zi2(GEN b)
+{
+  long v0, v1;
+  b = lift(b);
+  v0 = myval_zi(truecoeff(b,0));
+  v1 = myval_zi(truecoeff(b,1));
+  return minss(2*v0, 2*v1+1);
+}
+
+/* min(a,b,c) */
+static long
+min3(long a, long b, long c)
+{
+  long m = a;
+  if (b < m) m = b;
+  if (c < m) m = c;
+  return m;
+}
+/* min(a,b,c) */
+static GEN
+gmin3(GEN a, GEN b, GEN c)
+{
+  GEN m = a;
+  if (gcmp(b, m) < 0) m = b;
+  if (gcmp(c, m) < 0) m = c;
+  return m;
+}
+
+/* a/b */
+static GEN
+frac2s(long a, long b) { return b == 1? stoi(a): gdivgs(stoi(a), b); }
+
+/* Vector of p-adic factors (over Q_p) to accuracy r of pol. */
+static GEN
+padicfactors(GEN pol, GEN p, long r) { return gel(factorpadic(pol,p,r),1); }
+
+/* x(1/t)*t^6, deg x <= 6 */
+static GEN
+RgX_recip6(GEN x)
+{
+  long lx = lg(x), i, j;
+  GEN y = cgetg(9, t_POL);
+  y[1] = x[1];
+  for (i=8,j=2; j < lx; i--,j++) gel(y,i) = gel(x,j);
+  for (       ; j <  9; i--,j++) gel(y,i) = gen_0;
+  return normalizepol_lg(y, 9);
+}
+/* extract coefficients of a polynomial a0 X^6 + ... + a6, of degree <= 6 */
+static void
+RgX_to_6(GEN q, GEN *a0, GEN *a1, GEN *a2, GEN *a3, GEN *a4, GEN *a5, GEN *a6)
+{
+  *a0 = gen_0;
+  *a1 = gen_0;
+  *a2 = gen_0;
+  *a3 = gen_0;
+  *a4 = gen_0;
+  *a5 = gen_0;
+  *a6 = gen_0;
+  switch(degpol(q))
+  {
+    case 6: *a0 = gel(q,8); /*fall through*/
+    case 5: *a1 = gel(q,7); /*fall through*/
+    case 4: *a2 = gel(q,6); /*fall through*/
+    case 3: *a3 = gel(q,5); /*fall through*/
+    case 2: *a4 = gel(q,4); /*fall through*/
+    case 1: *a5 = gel(q,3); /*fall through*/
+    case 0: *a6 = gel(q,2); /*fall through*/
+  }
+}
+
+/* return a factor of maximal multiplicity (maxord) of the FpX Q, deg Q < 7.
+ * In the special case maxord = 3 or maxord = 2 and p <= 5, make sure
+ * the factor is irreducible */
+static GEN
+factmz(GEN Q, GEN p, long *maxord)
+{
+  GEN D, z = Q;
+  long m = 0;
+  if (cmpiu(p,5) <= 0)
+  {
+    if (FpX_is_squarefree(Q, p)) m = 1;
+    else
+    {
+      GEN F = FpX_factor(Q,p), P = gel(F,1), E = gel(F,2);
+      long i, l = 1;
+      for(i = 1;i<lg(E);i++)
+      {
+        long e = E[i];
+        if (e >= m) { m = e; l = i; }
+      }
+      z = gel(P,l);
+    }
+    *maxord = m; return z;
+  }
+  D = Q;
+  for(;;)
+  {
+    GEN T;
+    m++;
+    D = FpX_deriv(D, p);
+    T = FpX_gcd(z, D, p);
+    if (degpol(T) == 0) break;
+    z = T;
+  }
+  if (m >= 3 && degpol(z) == 2)
+  {
+    GEN F = FpX_factor(z,p);
+    z = gcoeff(F,1,1);
+  }
+  *maxord = m; return z;
+}
+
+/* deg(H mod p) = 3, return v_p( disc(correspondig p-adic factor) ) */
+static long
+discpart(GEN H, GEN p, long prec)
+{
+  GEN list, prod, dis;
+  long i, j;
+
+  if (degpol(FpX_red(H,p)) != 3)
+    pari_err_BUG("discpart [must not reach]");
+  list = padicfactors(H,p,prec);
+  prod = pol_1(varn(H));
+  for(i = 1; i < lg(list); i++)
+  {
+    GEN t = gel(list,i);
+    for(j = 3; j < lg(t); j++) /* include if non-constant mod p */
+      if (!valp(gel(t,j))) { prod = RgX_mul(prod,t); break; }
+  }
+  if (degpol(prod) != 3) pari_err_BUG("discpart [prod degree]");
+  dis = RgX_disc(prod);
+  return gequal0(dis)? prec+1: valp(dis);
+}
+
+
+/* B = b0 X^6 + ... + b6 a ZX, 0 <= j <= 3.
+ * Return theta_j(H) := min { v_p(b_i) / (i - j), lambda < i <= 6 } >= 0.
+ * N.B. 60 theta \in Z */
+static GEN
+theta_j(GEN B, GEN p, long j)
+{
+  GEN theta, b0, b1, b2, b3, b4, b5, b6, v = new_chunk(7);
+  long i;
+
+  RgX_to_6(B, &b0,&b1,&b2,&b3,&b4,&b5,&b6);
+  v[0] = myval(b0,p);
+  v[1] = myval(b1,p);
+  v[2] = myval(b2,p);
+  v[3] = myval(b3,p);
+  v[4] = myval(b4,p);
+  v[5] = myval(b5,p);
+  v[6] = myval(b6,p);
+  theta = stoi(v[1+j]);
+  for(i = 2+j; i <= 6; i++) theta = gmin(theta, frac2s(v[i], i-j));
+  return theta;
+}
+/* compute theta_3 for B in Z[i][X], p = 3 */
+static GEN
+theta_3_zi(GEN B)
+{
+  long v2 = myval_zi(truecoeff(B,2));
+  long v1 = myval_zi(truecoeff(B,1));
+  long v0 = myval_zi(truecoeff(B,0));
+  return frac2s(min3(6*v2, 3*v1, 2*v0), 6);
+}
+/* compute theta_3 for B in (Z[i,Y]/(Y^2-3))[X], p = 3 */
+static GEN
+theta_3_zi2(GEN B)
+{
+  long v2 = myval_zi2(truecoeff(B,2));
+  long v1 = myval_zi2(truecoeff(B,1));
+  long v0 = myval_zi2(truecoeff(B,0));
+  return frac2s(min3(6*v2, 3*v1, 2*v0), 6);
+}
+
+/* T an FpX of degree 1, return its root */
+static GEN
+deg1root(GEN T, GEN p)
+{
+  GEN a = gel(T,2), b = gel(T,3);
+  return Fp_neg(Fp_div(a, b, p), p);
+}
+/* H = minimal minimal over Z_p, p > 2.
+ * alpha = 0,1
+ * quad = 1 if root of order 3 in F_p^2 \ F_p, 0 otherwise
+ * 0 <= lambda <= 3, t_INT
+ * theta = theta(H, p), rational
+ * beta >= 0, t_INT */
+static GEN
+polymini(GEN pol, GEN p)
+{
+  GEN a0, a1, a2, a3, a4, a5, a6;
+  GEN H, Hp, rac, theta, polf, pro, quad;
+  long alpha, lambda, maxord, beta;
+
+  alpha = polval(pol,p);
+  H = RgX_Rg_div(pol, powiu(p,alpha));
+  RgX_to_6(H, &a0,&a1,&a2,&a3,&a4,&a5,&a6);
+  if (dvdii(a0,p) && dvdii(a1,p) && dvdii(a2,p) && dvdii(a3,p))
+  {
+    H = RgX_recip6(H);
+    RgX_to_6(H, &a0,&a1,&a2,&a3,&a4,&a5,&a6);
+  }
+  alpha = alpha&1;
+  beta = 0;
+  lambda = 0;
+  if (!dvdii(a1,p)) lambda = 1;
+  if (!dvdii(a2,p)) lambda = 2;
+  if (!dvdii(a3,p)) lambda = 3;
+  quad = gen_0;
+
+  for(;;)
+  {
+    theta = theta_j(H,p,lambda);
+    if (gcmp(theta,gen_1)>= 0)
+    {
+      long ent = itos(gfloor(theta));
+      GEN pent = powiu(p,ent);
+      H = RgX_Rg_div(RgX_unscale(H,pent), powiu(pent,6-lambda));
+      alpha = (alpha+lambda*ent)&1;
+      beta += ent;
+      theta = gsubgs(theta,ent);
+    }
+    Hp = FpX_red(H, p);
+    if (!gequal0(theta)) break;
+
+    rac = factmz(Hp,p, &maxord);
+    if (maxord <= 2)
+    {
+      if (degpol(Hp) <= 3) break;
+      goto end;
+    }
+    else
+    { /* maxord >= 3 */
+      if (degpol(rac) == 2) { quad = gen_1; goto end; }
+      rac = deg1root(rac, p);
+      H = RgX_translate(H, rac);
+      lambda = 6-maxord;
+    }
+  }
+
+  if (lambda <= 2)
+  {
+    if (myval(truecoeff(H,2),p) > 1-alpha &&
+        myval(truecoeff(H,1),p) > 2-alpha &&
+        myval(truecoeff(H,0),p) > 3-alpha)
+    {
+      pol = RgX_unscale(H, p);
+      if (alpha) pol = RgX_Rg_mul(pol, p);
+      return polymini(pol, p);
+    }
+  }
+  else if (lambda == 3 && alpha == 1)
+  {
+    if (degpol(Hp) == 3)
+    {
+      if (myval(truecoeff(H,6),p) >= 3 &&
+          myval(truecoeff(H,5),p) >= 2)
+      {
+        H = RgX_rescale(H, p); /* H(x/p)p^(deg H) */
+        H = RgX_Rg_div(H, powiu(p, degpol(H)-3)); /* H(x/p)p^3 */
+        theta = gadd(theta,gen_1);
+        alpha = 0;
+        beta--;
+      }
+    }
+    else if (degpol(Hp) == 6 && !gequal0(theta))
+    {
+      rac = factmz(RgX_mulXn(Hp, -3), p, &maxord);
+      if (maxord == 3)
+      {
+        rac = deg1root(rac, p);
+        pro = RgX_translate(RgX_unscale(H,p), rac); /* H(rac + px) */
+        if (polval(pro,p)>= 3)
+        {
+          H = RgX_Rg_div(pro, powiu(p,3));
+          alpha = 0;
+          beta--;
+          theta = theta_j(H,p,3);
+        }
+      }
+    }
+  }
+end:
+  polf = cgetg(7, t_VEC);
+  gel(polf,1) = H;
+  gel(polf,2) = stoi(lambda);
+  gel(polf,3) = theta;
+  gel(polf,4) = stoi(alpha);
+  gel(polf,5) = quad;
+  gel(polf,6) = stoi(beta);
+  return polf;
+}
+
+/* a in Q[i], return a^3 mod 3 */
+static GEN
+zi_pow3mod(GEN a)
+{
+  GEN x, y;
+  if (typ(a) != t_COMPLEX) return gmodgs(a,3);
+  x = gmodgs(gel(a,1), 3);
+  y = gmodgs(gel(a,2), 3);
+  return mkcomplex(x, negi(y));
+}
+static GEN
+polymini_zi(GEN pol) /* polynome minimal dans Z[i] */
+{
+  GEN p, polh, rac, theta;
+  GEN a0, a1, a2, a3, a4, a5, a6;
+  long alpha,beta;
+
+  p = stoi(3);
+  alpha = polval(pol,p) & 1;
+  polh = alpha? RgX_Rg_div(pol, p): pol;
+  beta = 0;
+  rac = mkcomplex(Fp_div(truecoeff(polh,3), truecoeff(polh,6), p), gen_1);
+  for(;;)
+  {
+    polh = RgX_translate(polh, rac);
+    theta = theta_3_zi(polh);
+    if (gcmp(theta,gen_1) >= 0)
+    {
+      long ent = itos(gfloor(theta));
+      GEN pent = powiu(p,ent);
+      polh = RgX_Rg_div(RgX_unscale(polh,pent), powiu(pent,3));
+      alpha = (alpha+ent)&1;
+      beta += ent;
+      theta = gsubgs(theta,ent);
+    }
+    RgX_to_6(polh, &a0,&a1,&a2,&a3,&a4,&a5,&a6);
+    if (!gequal0(theta) || !myval_zi(a4) || !myval_zi(a5)) break;
+    rac = zi_pow3mod(gdiv(a6, gneg(a3)));
+  }
+  if (alpha && myval_zi(a0) >= 3 && myval_zi(a1) >= 2 && myval_zi(a2) >= 1)
+  {
+    theta = gadd(theta, gen_1);
+    beta--;
+    alpha = 0;
+  }
+  return mkvec3(theta, stoi(alpha), stoi(beta));
+}
+
+/* pol is a ZX, minimal polynomial over Z_3[i,Y]/(Y^2-3) */
+static GEN
+polymini_zi2(GEN pol)
+{
+  long alpha, beta, v = MAXVARN;
+  GEN a0, a1, a2, a3, a4, a5, a6;
+  GEN p, polh, rac, theta, y = pol_x(v);
+
+  p = stoi(3);
+  if (polval(pol,p)) pari_err_BUG("polymini_zi2 [polynomial not minimal]");
+  y = mkpolmod(y, gsubgs(gsqr(y), 3)); /* mod(y,y^2-3) */
+  polh = pol;
+  polh = gdivgs(RgX_unscale(polh, y),27); /* H(y*x) / 27 */
+  if (myval_zi2(truecoeff(polh,4)) <= 0 ||
+      myval_zi2(truecoeff(polh,2)) <= 0) return mkcol2(gen_0, gen_0);
+
+  if (myval_zi2(gsub(truecoeff(polh,6), truecoeff(polh,0))) > 0)
+    rac = gen_I();
+  else
+    rac = gen_1;
+  alpha = 0;
+  beta  = 0;
+  for(;;)
+  {
+    polh = RgX_translate(polh, rac);
+    theta = theta_3_zi2(polh);
+    if (gcmp(theta,gen_1) >= 0)
+    {
+      long ent = itos(gfloor(theta));
+      GEN pent = gpowgs(y, ent);
+      polh = RgX_Rg_div(RgX_unscale(polh, pent), gpowgs(pent,3));
+      alpha = (alpha+ent)&1;
+      beta += ent;
+      theta = gsubgs(theta,ent);
+    }
+    RgX_to_6(polh, &a0,&a1,&a2,&a3,&a4,&a5,&a6);
+    if (!gequal0(theta) || !myval_zi2(a4) || !myval_zi2(a5)) break;
+    a3 = liftpol_shallow(a3); if (typ(a3)==t_POL) a3 = constant_term(a3);
+    a6 = liftpol_shallow(a6); if (typ(a6)==t_POL) a6 = constant_term(a6);
+    rac = zi_pow3mod(gdiv(a6,gneg(a3)));
+  }
+  if (alpha)
+  {
+    if (myval_zi2(a0) >= 3 && myval_zi2(a1) >= 2 && myval_zi2(a2) >= 1)
+    {
+      theta = gadd(theta,gen_1);
+      beta--;
+      alpha = 0;
+    }
+    else pari_err_BUG("polymini_zi2 [alpha]");
+  }
+  return mkcol2(theta, stoi(beta));
+}
+
+
+struct igusa {
+  GEN j2, i4, j4, j6, j8, j10, i12;
+  GEN a0, A2, A3, A5, B2;
+};
+struct igusa_p {
+  long eps, eps2, tt, r1, r2, R, tame;
+  GEN p, stable, val, neron;
+  const char *type;
+};
+
+static void
+stable_reduction(struct igusa *I, struct igusa_p *Ip)
+{
+  static const long deg[9] = { 0,2,4,4,6,8,10,12 };
+  GEN j2 = I->j2, i4 = I->i4, j6 = I->j6, j8 = I->j8, j10 = I->j10;
+  GEN i12 = I->i12, p = Ip->p, val = Ip->val;
+  GEN J, v, s, Ieps;
+  long r1, r2, r3, r4, i, eps, eps2;
+
+  v = cgetg(8,t_COL);
+  for(i = 1; i <= 7; i++) gel(v,i) = frac2s(val[i], deg[i]);
+  s = gel(v,1);
+  for(i = 2; i <= 7; i++)
+    if (gcmp(gel(v,i),s) < 0) s = gel(v,i);
+  switch(itos_or_0(p))
+  {
+    case 2:  eps = 4; eps2 = 5; Ieps = j8; break;
+    case 3:  eps = 3; eps2 = 4; Ieps = j6; break;
+    default: eps = 1; eps2 = 1; Ieps = gdivgs(j2,12); break;
+  }
+  Ip->eps  = eps;
+  Ip->eps2 = eps2;
+
+  r1 = 3*eps*val[3];
+  r3 = eps*val[6] + val[eps2];
+  r2 = eps*val[7];
+  r4 = min3(r1, r2, r3);
+
+  /* s = max(v_p(X) / deg(X)) */
+  J = cgetg(1, t_VEC);
+  if (gequal(s,gel(v,6)))
+    Ip->tt = 1;
+  else if (gequal(s,gel(v,7)))
+  {
+    J = mkvec( Fp_to_mod(gmod(gdiv(gpowgs(i4,3),i12), p), p) );
+    Ip->tt = 2;
+  }
+  else if (gequal(s,gel(v,3)))
+    Ip->tt = (val[2] == val[3] || 2*val[4] == 3*val[3])? 3: 4;
+  else if (r3 == r4)
+  {
+    GEN a,b, P, sj, pj, t = gmul(gpowgs(j10,eps),Ieps);
+    sj = gaddsg(1728, gdiv(gpowgs(i12,eps), t));
+    pj = gdiv(gpowgs(i4,3*eps), t);
+    a = gmod(sj, p);
+    b = gmod(pj, p);
+    P = mkpoln(3, gen_1, Fp_neg(a,p), b, 0); /* X^2 - SX + P: roots j1,j2 */
+    J = FpX_roots(P, p);
+    switch(lg(J)-1)
+    {
+      case 0:
+        P = FpX_to_mod(P, p);
+        a = FpX_to_mod(pol_x(0), p);
+        b = FpX_to_mod(deg1pol_shallow(b, gen_m1,0), p);
+        J = mkvec2(mkpolmod(a,P), mkpolmod(b,P)); break;
+      case 1:
+        a = Fp_to_mod(gel(J,1), p);
+        J = mkvec2(a, a); break;
+      case 2:
+        settyp(J, t_VEC);
+        J = FpV_to_mod(J, p); break;
+    }
+    Ip->tt = 5;
+  }
+  else if (r2 == r4)
+  {
+    J = mkvec( Fp_to_mod(gmod(gdiv(gpowgs(i4,3),i12), p), p) );
+    Ip->tt = 6;
+  }
+  else
+    Ip->tt = 7; /* r1 == r4 */
+  Ip->stable = mkvec2(stoi(Ip->tt), J);
+}
+
+struct red {
+  const char *t, *pages;
+  GEN g;
+};
+
+/* destroy v */
+static GEN
+zv_snf(GEN v)
+{
+  long i, l = lg(v);
+  for (i = 1; i < l; i++)
+  {
+    long j, a = v[i];
+    for (j = i+1; j < l; j++)
+    {
+      long b = v[j], d = ugcd(a,b);
+      v[i] = a = a*(b/d);
+      v[j] = d;
+    }
+  }
+  for (i = l-1; i > 0; i--)
+    if (v[i] != 1) { setlg(v, i+1); break; }
+  return zv_to_ZV(v);
+}
+
+static GEN
+cyclic(long n)
+{ return (n <= 1)? cgetg(1, t_VECSMALL): mkvecsmall(n); }
+static GEN
+dicyclic(long a, long b)
+{
+  long d;
+  if (!a) a = 1;
+  if (!b) b = 1;
+  if (a < b) lswap(a,b);
+  d = ugcd(a,b);
+  if (d == 1) return cyclic(a*b);
+  return mkvecsmall2(a*b/d, d);
+}
+/* Z/2xZ/2, resp Z/4 for n even, resp. odd */
+static GEN
+groupH(long n) { return odd(n)? cyclic(4): dicyclic(2,2); }
+
+static long
+get_red(struct red *S, struct igusa_p *Ip, GEN polh, GEN p, long alpha, long r)
+{
+  GEN val = Ip->val;
+  long indice;
+  switch(r)
+  {
+    case 0:
+      indice = FpX_is_squarefree(FpX_red(polh,p), p)
+               ? 0
+               : val[6] - val[7] + val[Ip->eps2]/Ip->eps;
+      S->t = stack_sprintf("I{%ld}", indice);
+      S->pages = "159-177";
+      S->g = cyclic(indice);
+      return indice ? indice: 1;
+    case 6:
+      if (alpha == 0) /* H(px) /p^3 */
+        polh = RgX_Rg_div(RgX_unscale(polh,p), powiu(p,3));
+      indice = FpX_is_squarefree(FpX_red(polh,p), p)
+               ? 0
+               : val[6] - val[7] + val[Ip->eps2]/Ip->eps;
+      S->t = stack_sprintf("I*{%ld}", indice);
+      S->pages = "159-177";
+      S->g = groupH(indice);
+      return indice + 5;
+    case 3:
+      S->t = "III";
+      S->pages = "161-177";
+      S->g = cyclic(2);
+      return 2;
+    case 9:
+      S->t = "III*";
+      S->pages = "162-177";
+      S->g = cyclic(2);
+      return 8;
+    case 2:
+      S->t = "II";
+      S->pages = "159-174";
+      S->g = cyclic(1);
+      return 1;
+    case 8:
+      S->t = "IV*";
+      S->pages = "160-175";
+      S->g = cyclic(3);
+      return 7;
+    case 4:
+      S->t = "IV";
+      S->pages = "160-174";
+      S->g = cyclic(3);
+      return 3;
+    case 10:
+      S->t = "II*";
+      S->pages = "160-174";
+      S->g = cyclic(1);
+      return 9;
+    default: pari_err_BUG("get_red [type]");
+      S->t = "";
+      S->pages = ""; /* gcc -Wall */
+      S->g = NULL;
+      return -1; /*notreached*/
+  }
+}
+
+static long labelm3(GEN polh, GEN theta, long alpha, long dismin, struct igusa *I, struct igusa_p *Ip);
+
+/* Ip->tt = 1 */
+static long
+tame_1(struct igusa *I, struct igusa_p *Ip)
+{
+  GEN p = Ip->p, val = Ip->val;
+  GEN r, n, pro1, pro2;
+  long condp = -1, va0, va5;
+  va0 = myval(I->a0,p);
+  va5 = myval(I->A5,p);
+  if (!gequal0(I->A5) && 20*va0+val[6] > 6*va5)
+  {
+    pro1 = frac2s(val[6]-2*va5, 20);
+    pro2 = frac2s(5*val[6]-6*va5, 40);
+  }
+  else
+  {
+    pro1 = frac2s(10*va0-val[6], 30);
+    pro2 = frac2s(5*va0-val[6], 10);
+  }
+  n = lcmii(denom(pro1),denom(pro2));
+  r = modii(gmul(n,pro1), n);
+  switch(itos(n))
+  {
+    case 1:
+      condp = 0;
+      Ip->type = "[I{0-0-0}] page 155";
+      Ip->neron = cyclic(1); break;
+    case 2:
+      switch(itos(r))
+      {
+        case 0:
+          condp = 4;
+          Ip->type = "[I*{0-0-0}] page 155";
+          Ip->neron = mkvecsmall4(2,2,2,2); break;
+        case 1:
+          condp = 2;
+          Ip->type = "[II] page 155";
+          Ip->neron = cyclic(1); break;
+        default: pari_err_BUG("tame_1 [bug1]");
+      }
+      break;
+    case 4:
+      condp = 4;
+      Ip->type = "[VI] page 156";
+      Ip->neron = dicyclic(2,2); break;
+    default: pari_err_BUG("tame_1 [bug8]");
+  }
+  return condp;
+}
+
+static void
+tame_234_init(struct igusa *I, struct igusa_p *Ip, long v12,
+                long *pn, long *pq, long *pr, long *flc)
+{
+  long va0, va5, vb2;
+  GEN p = Ip->p, pro1, pro2, n, r, q;
+  va0 = myval(I->a0,p);
+  va5 = myval(I->A5,p);
+  vb2 = myval(I->B2,p);
+  if (9*vb2 >= 6*va0+v12 && 36*va5 >= 120*va0+5*v12)
+  {
+    pro1 = frac2s(12*va0-v12, 36);
+    pro2 = frac2s(6*va0-v12, 12);
+    n = lcmii(denom(pro1),denom(pro2));
+    r = gmul(n,pro1);
+    q = gmul(n,pro2);
+    *flc = 1;
+  }
+  else if (120*va0+5*v12 > 36*va5 && 60*vb2 >= 12*va5+5*v12)
+  {
+    pro1 = frac2s(36*va5-25*v12, 240);
+    n = denom(pro1);
+    q = gmul(n,pro1);
+    r = gmulsg(-2,q);
+    *flc = 1;
+  }
+  else if (6*va0+v12 > 9*vb2 && 12*va5+5*v12 > 60*vb2)
+  {
+    pro1 = frac2s(v12-6*vb2, 12);
+    pro2 = frac2s(v12-9*vb2, 12);
+    n = lcmii(denom(pro1),denom(pro2));
+    r = gmul(n,pro1);
+    q = gmul(n,pro2);
+    *flc = 2;
+  }
+  else
+  {
+    pari_err_BUG("tame234 [bug9]");
+    return; /*not reached*/
+  }
+  r = gmod(r,n);
+  q = gmod(q,n);
+  *pn = itos(n);
+  *pq = itos(q);
+  *pr = itos(r);
+}
+
+/* Ip->tt = 2 */
+static long
+tame_2(struct igusa *I, struct igusa_p *Ip, long v12)
+{
+  long condp = -1, d, n, q, r, flc;
+  GEN val = Ip->val;
+  tame_234_init(I, Ip, v12, &n, &q, &r, &flc);
+  d = n * (6*val[6]-5*val[7]) / 6;
+  switch(n)
+  {
+    case 1: condp = 1;
+      Ip->type = stack_sprintf("[I{%ld-0-0}] page 170", d);
+      Ip->neron = cyclic(d); break;
+    case 2:
+      switch(r)
+      {
+        case 0: condp = 4;
+          Ip->type = stack_sprintf("[I*{%ld-0-0}] page 171",d/2);
+          Ip->neron = concat(dicyclic(2,2),groupH(d/2)); break;
+        case 1:
+          switch(q)
+          {
+            case 0: condp = 2;
+              Ip->type = stack_sprintf("[II*{%ld-0}] page 172",d/2);
+              Ip->neron = cyclic(1); break;
+            case 1: condp = 3;
+              Ip->type = stack_sprintf("[II{%ld-0}] page 171",d/2);
+              Ip->neron = cyclic(2*d); break;
+            default: pari_err_BUG("tame2 [bug10]");
+          }
+          break;
+        default: pari_err_BUG("tame2 [bug11]");
+      }
+      break;
+    case 3: condp = 3;
+      Ip->neron = cyclic(d);
+      switch(r)
+      {
+        case 1:
+          Ip->type = stack_sprintf("[IV-II{%ld}] page 175", (d-2)/3);
+          break;
+        case 2:
+          Ip->type = stack_sprintf("[IV*-II{%ld}] page 175", (d-1)/3);
+          break;
+        default: pari_err_BUG("tame2 [bug12]");
+      }
+      break;
+    case 4:
+      switch(r)
+      {
+        case 1:
+          switch(q)
+          {
+            case 1: condp = 3;
+              Ip->type = stack_sprintf("[III-II{%ld}] page 177",(d-2)/4);
+              Ip->neron = cyclic(d/2); break;
+            case 3: condp = 4;
+              Ip->type = stack_sprintf("[III*-II*{%ld}] page 178",(d-2)/4);
+              Ip->neron = cyclic(8); break;
+            default: pari_err_BUG("tame2 [bug13]");
+          }
+          break;
+        case 3:
+          switch(q)
+          {
+            case 1: condp = 4;
+              Ip->type = stack_sprintf("[III-II*{%ld}] page 178",(d-2)/4);
+              Ip->neron = cyclic(8); break;
+            case 3: condp = 3;
+              Ip->type = stack_sprintf("[III*-II{%ld}] page 178",(d-2)/4);
+              Ip->neron = cyclic(d/2); break;
+            default: pari_err_BUG("tame2 [bug14]");
+          }
+          break;
+        default: pari_err_BUG("tame2 [bug15]");
+      }
+      break;
+    case 6:
+      switch(r)
+      {
+        case 2: condp = 4;
+          Ip->type = stack_sprintf("[II*-II*{%ld}] page 176", (d-4)/6);
+          Ip->neron = groupH((d+2)/6); break;
+        case 4: condp = 4;
+          Ip->type = stack_sprintf("[II-II*{%ld}] page 176", (d-2)/6);
+          Ip->neron = groupH((d+4)/6); break;
+          break;
+        default: pari_err_BUG("tame2 [bug16]");
+      }
+      break;
+    default: pari_err_BUG("tame2 [bug17]");
+  }
+  return condp;
+}
+
+/* Ip->tt = 3 */
+static long
+tame_3(struct igusa *I, struct igusa_p *Ip, long v12)
+{
+  long condp = -1, n, q, r, flc;
+  long va5, d1, d2;
+  GEN val = Ip->val, e1, e2;
+  tame_234_init(I, Ip, v12, &n, &q, &r, &flc);
+
+  va5 = 2*val[6]-5*val[3];
+  e1 = gmin(stoi(val[7]-3*val[3]),gmul2n(stoi(va5),-2));
+  e2 = gsub(gmul2n(stoi(va5),-1),e1);
+  d1 = itos(gmulsg(n,e1));
+  d2 = itos(gmulsg(n,e2));
+  switch(n)
+  {
+    case 1: condp = 2;
+      Ip->type = stack_sprintf("[I{%ld-%ld-0}] page 179", d1,d2);
+      Ip->neron = dicyclic(d1,d2); break;
+    case 2:
+      switch(r)
+      {
+        case 0: condp = 4;
+          Ip->type = stack_sprintf("[I*{%ld-%ld-0}] page 180", d1/2,d2/2);
+          Ip->neron = concat(groupH(d1/2),groupH(d2/2)); break;
+        case 1:
+          switch(flc)
+          {
+            case 1:condp = 3;
+              Ip->type = stack_sprintf("[2I{%ld}-0] page 181", d1);
+              Ip->neron = cyclic(d1); break;
+            case 2: condp = 3;
+              Ip->type = stack_sprintf("[II{%ld-%ld}] page 182",d1/2,d2/2);
+              if ((d1*d2-4)&7)
+                Ip->neron = cyclic(2*d1);
+              else
+                Ip->neron = dicyclic(d1,2);
+              /* FIXME: "or" same with d1<->d2 */
+              break;
+          }
+          break;
+        default: pari_err_BUG("tame3 [bug20]");
+      }
+      break;
+    case 4: condp = 4;
+      Ip->type = stack_sprintf("[III{%ld}] page 182", d1/2);
+      Ip->neron = groupH(d1/2); break;
+    default: pari_err_BUG("tame3 [bug21]");
+  }
+  return condp;
+}
+
+/* Ip->tt = 4 */
+static long
+tame_4(struct igusa *I, struct igusa_p *Ip, long v12)
+{
+  long condp = -1, d1, d2, d3, f1, f2, g, h, n, q, r, flc;
+  GEN val = Ip->val, e1, e2, e3, vl, vn, vm;
+  tame_234_init(I, Ip, v12, &n, &q, &r, &flc);
+  vl = stoi(val[6]-5*val[1]);
+  vn = stoi(val[7]-6*val[1]);
+  vm = stoi(val[2]-2*val[1]);
+  e1 = gmin3(gdivgs(vl,3), gmul2n(vn,-1), vm);
+  e2 = gmin(gmul2n(gsub(vl,e1),-1), gsub(vn,e1));
+  e3 = gsub(vl,gadd(e1,e2));
+  d1 = itos(gmulsg(n,e1));
+  d2 = itos(gmulsg(n,e2));
+  d3 = itos(gmulsg(n,e3));
+  g = d1*d2 + d1*d3 + d2*d3;
+  h = cgcd(cgcd(d1,d2),d3);
+  switch(n)
+  {
+    case 1: condp = 2;
+      Ip->type = stack_sprintf("[I{%ld-%ld-%ld}] page 182",d1,d2,d3);
+      Ip->neron = dicyclic(h,g/h); break;
+    case 2:
+      switch(r)
+      {
+        case 0: condp = 4;
+          Ip->type = stack_sprintf("[I*{%ld-%ld-%ld}] page 183",d1/2,d2/2,d3/2);
+          Ip->neron = concat(groupH(g/4), groupH(2-((h&2)>>1))); break;
+        case 1:
+          if      (d1 == d2 || d1 == d3) f2 = d1;
+          else if (d2 == d3) f2 = d2;
+          else {
+            pari_err_BUG("tame4 [bug23]");
+            return -1; /*not reached*/
+          }
+          f1 = d1+d2+d3-2*f2;
+          switch(q)
+          {
+            case 0: condp = 3;
+              Ip->type = stack_sprintf("[II*{%ld-%ld}] page 184", f1/2,f2);
+              Ip->neron = cyclic(f2); break;
+            case 1: condp = 3;
+              Ip->type = stack_sprintf("[II{%ld-%ld}] page 183", f1/2,f2);
+              Ip->neron = cyclic(2*f1+f2); break;
+            default: pari_err_BUG("tame4 [bug24]");
+          }
+          break;
+        default: pari_err_BUG("tame4 [bug25]");
+      }
+      break;
+    case 3: condp = 4;
+      Ip->type = stack_sprintf("[III{%ld}] page 184",d1);
+      Ip->neron = (d1%3)? cyclic(9): dicyclic(3,3); break;
+    case 6: condp = 4;
+      Ip->type = stack_sprintf("[III*{%ld}] page 184",d1/2);
+      Ip->neron = cyclic(1); break;
+    default: pari_err_BUG("tame4 [bug26]");
+  }
+  return condp;
+}
+
+/* p = 3 */
+static void
+tame_567_init_3(struct igusa_p *Ip, GEN dk,
+                long *pd, long *pn, long *pdm, long *pr)
+{
+  long n = 1 + Ip->r1/6;
+  *pd = itos(gmulgs(dk,n));
+  *pn = n;
+  *pr = -1; /* unused */
+  *pdm = 0;
+}
+
+static void
+tame_567_init(struct igusa *I, struct igusa_p *Ip, GEN dk,
+              long *pd, long *pn, long *pdm, long *pr)
+{
+  long va0, va2, va3, va5, vb2;
+  long d, v1, v2;
+  GEN r, n, m;
+  GEN pro1, dm, rk;
+  GEN p = Ip->p, val = Ip->val;
+  long v5;
+
+  if (equalis(p, 3)) { tame_567_init_3(Ip, dk, pd, pn, pdm, pr); return; }
+  /* assume p > 3 */
+  va0 = myval(I->a0,p);
+  va2 = myval(I->A2,p);
+  va3 = myval(I->A3,p);
+  va5 = myval(I->A5,p);
+  vb2 = myval(I->B2,p);
+  v5 = myval(subii(mulii(I->A2,I->A3),mulsi(3,I->A5)),p);
+  rk = gadd(frac2s(va0, 2),
+            gmin3(gmul2n(dk,-1),
+                  frac2s(2*va3-3*va2, 8),
+                  frac2s(2*v5 - 5*va2, 12)));
+  v1 = 2*va3-4*va0-val[1];
+  v2 = 6*va5-20*va0-5*val[1];
+  /* the definition of n differs according to the parity of val[1] */
+  if (! odd(val[Ip->eps2]))
+  {
+    if (3*vb2 >= 2*va0+2*val[1] && v1 >= 0 && v2 >= 0
+                                && (v1 == 0 || v2 == 0))
+    { /* Prop 4.3.1 (a) */
+      pro1 = frac2s(va0+val[1], 6);
+      n = lcmii(denom(dk),denom(pro1));
+      r = gmul(n,pro1);
+    }
+    else if (20*va0+5*val[1] > 6*va5 && 10*vb2 >= 2*va5+5*val[1])
+    { /* Prop 4.3.1 (b) */
+      pro1 = frac2s(2*va5+val[1], 8);
+      n = lcmii(denom(dk),denom(pro1));
+      r = gmul(n,pro1);
+    }
+    else if (2*va0+2*val[1] > 3*vb2 && 2*va5+5*val[1] > 10*vb2)
+    { /* Prop 4.3.1 (c) */
+      pro1 = gmul2n(stoi(vb2),-2);
+      n = lcmii(denom(dk),denom(pro1));
+      r = gmul(n,pro1);
+    }
+    else if (3*vb2 >= 2*va0+2*val[1] && 2*va3 > 4*va0+val[1]
+                                     && 6*va5 > 20*va0+5*val[1])
+    { /* Prop 4.3.1 (d) */
+      if (gequal0(I->A2)) pari_err_BUG("tame567 [bug27]");
+      n = lcmii(denom(dk),denom(rk));
+      r = gmul(n,rk);
+    }
+    else
+    {
+      pari_err_BUG("tame567 [bug29]");
+      return; /*not reached*/
+    }
+  }
+  else
+  {
+    m = denom(dk);
+    r = gmul(m,dk);
+    n = gmul2n(m,1);
+  }
+  d = itos(gmul(n,dk));
+  dm = modsi(d,n);
+  r = modii(r,n);
+  *pd = d;
+  *pn = itos(n);
+  *pr = itos(r);
+  *pdm = itos(dm);
+}
+
+static long
+tame_5(struct igusa *I, struct igusa_p *Ip, GEN dk)
+{
+  long condp = -1, d, n, dm, r;
+  GEN val = Ip->val;
+
+  tame_567_init(I, Ip, dk, &d, &n, &dm, &r);
+  if (! odd(val[Ip->eps2]))
+  {
+    switch(n)
+    {
+      case 1: condp = 0;
+        Ip->type = stack_sprintf("[I{0}-I{0}-%ld] page 158", d);
+        Ip->neron = cyclic(1); break;
+      case 2:
+        switch(dm)
+        {
+          case 0: condp = 4;
+            Ip->type = stack_sprintf("[I*{0}-I*{0}-%ld] page 158",(d-2)/2);
+            Ip->neron = mkvecsmall4(2,2,2,2); break;
+          case 1: condp = 2;
+            Ip->type = stack_sprintf("[I{0}-I*{0}-%ld] page 159",(d-1)/2);
+            Ip->neron = dicyclic(2,2); break;
+        }
+        break;
+      case 3:
+        switch(dm)
+        {
+          case 0: condp = 4;
+            Ip->type = stack_sprintf("[IV-IV*-%ld] page 165",(d-3)/3);
+            Ip->neron = dicyclic(3,3); break;
+          case 1:
+            switch(r)
+            {
+              case 0: case 1: condp = 2;
+                Ip->type = stack_sprintf("[I{0}-IV-%ld] page 160",(d-1)/3);
+                Ip->neron = cyclic(3); break;
+              case 2: condp = 4;
+                Ip->type = stack_sprintf("[IV*-IV*-%ld] page 166",(d-4)/3);
+                Ip->neron = dicyclic(3,3); break;
+            }
+            break;
+          case 2:
+            switch(r)
+            {
+              case 0: case 2: condp = 2;
+                Ip->type = stack_sprintf("[I{0}-IV*-%ld] page 160",(d-2)/3);
+                Ip->neron = cyclic(3); break;
+              case 1: condp = 4;
+                Ip->type = stack_sprintf("[IV-IV-%ld] page 165",(d-2)/3);
+                Ip->neron = dicyclic(3,3); break;
+            }
+            break;
+        }
+        break;
+      case 4:
+        switch(dm)
+        {
+          case 0: condp = 4;
+            Ip->type = stack_sprintf("[III-III*-%ld] page 169",(d-4)/4);
+            Ip->neron = dicyclic(2,2); break;
+          case 1:
+            switch(r)
+            {
+              case 0: case 1: condp = 2;
+                Ip->type = stack_sprintf("[I{0}-III-%ld] page 161",(d-1)/4);
+                Ip->neron = cyclic(2); break;
+              case 2: case 3: condp = 4;
+                Ip->type = stack_sprintf("[I*{0}-III*-%ld] page 162",(d-5)/4);
+                Ip->neron = mkvecsmall3(2,2,2); break;
+            }
+            break;
+          case 2: condp = 4;
+            Ip->neron = dicyclic(2,2);
+            switch(r)
+            {
+              case 1:
+                Ip->type = stack_sprintf("[III-III-%ld] page 169",(d-2)/4);
+                break;
+              case 3:
+                Ip->type = stack_sprintf("[III*-III*-%ld] page 169",(d-6)/4);
+                break;
+              default: pari_err_BUG("tame5 [bug29]");
+            }
+            break;
+          case 3:
+            switch(r)
+            {
+              case 0: case 3: condp = 2;
+                Ip->type = stack_sprintf("[I{0}-III*-%ld] page 162",(d-3)/4);
+                Ip->neron = cyclic(2); break;
+              case 1: case 2: condp = 4;
+                Ip->type = stack_sprintf("[I*{0}-III-%ld] page 162",(d-3)/4);
+                Ip->neron = mkvecsmall3(2,2,2); break;
+            }
+            break;
+        }
+        break;
+      case 6:
+        switch(dm)
+        {
+          case 0: condp = 4;
+            Ip->type = stack_sprintf("[II-II*-%ld] page 163",(d-6)/6);
+            Ip->neron = cyclic(1); break;
+          case 1:
+            switch(r)
+            {
+              case 0: case 1: condp = 2;
+                Ip->type = stack_sprintf("[I{0}-II-%ld] page 159",(d-1)/6);
+                Ip->neron = cyclic(1); break;
+              case 2: case 5: condp = 4;
+                Ip->type = stack_sprintf("[II*-IV-%ld] page 164",(d-7)/6);
+                Ip->neron = cyclic(3); break;
+              case 3: case 4: condp = 4;
+                Ip->type = stack_sprintf("[I*{0}-IV*-%ld] page 161",(d-7)/6);
+                Ip->neron = mkvecsmall2(6,2); break;
+            }
+            break;
+          case 2:
+            switch(r)
+            {
+              case 1: condp = 4;
+                Ip->type = stack_sprintf("[II-II-%ld] page 163",(d-2)/6);
+                Ip->neron = cyclic(1); break;
+              case 3: case 5: condp = 4;
+                Ip->type = stack_sprintf("[I*{0}-II*-%ld] page 160",(d-8)/6);
+                Ip->neron = dicyclic(2,2); break;
+              default: pari_err_BUG("tame5 [bug30]");
+            }
+            break;
+          case 3:
+            Ip->neron = cyclic(3);
+            switch(r)
+            {
+              case 1: case 2: condp = 4;
+                Ip->type = stack_sprintf("[II-IV-%ld] page 164",(d-3)/6);
+                break;
+              case 4: case 5: condp = 4;
+                Ip->type = stack_sprintf("[II*-IV*-%ld] page 164",(d-9)/6);
+                break;
+              default: pari_err_BUG("tame5 [bug31]");
+            }
+            break;
+          case 4:
+            switch(r)
+            {
+              case 1: case 3: condp = 4;
+                Ip->type = stack_sprintf("[I*{0}-II-%ld] page 160",(d-4)/6);
+                Ip->neron = dicyclic(2,2); break;
+              case 5: condp = 4;
+                Ip->type = stack_sprintf("[II*-II*-%ld] page 163",(d-10)/6);
+                Ip->neron = cyclic(1); break;
+              default: pari_err_BUG("tame5 [bug32]");
+            }
+            break;
+          case 5:
+            switch(r)
+            {
+              case 0: case 5: condp = 2;
+                Ip->type = stack_sprintf("[I{0}-II*-%ld] page 160",(d-5)/6);
+                Ip->neron = cyclic(1); break;
+              case 1: case 4: condp = 4;
+                Ip->type = stack_sprintf("[II-IV*-%ld] page 164",(d-5)/6);
+                Ip->neron = cyclic(3); break;
+              case 2: case 3: condp = 4;
+                Ip->type = stack_sprintf("[I*{0}-IV-%ld] page 161",(d-5)/6);
+                Ip->neron = mkvecsmall2(6,2); break;
+            }
+            break;
+          default: pari_err_BUG("tame5 [bug33]");
+        }
+        break;
+      case 12:
+        condp = 4;
+        switch(dm)
+        {
+          case 1:
+            switch(r)
+            {
+              case 3: case 10:
+                Ip->type = stack_sprintf("[II*-III-%ld] page 166",(d-13)/12);
+                Ip->neron = cyclic(2); break;
+              case 4: case 9:
+                Ip->type = stack_sprintf("[IV-III*-%ld] page 167",(d-13)/12);
+                Ip->neron = cyclic(6); break;
+              default: pari_err_BUG("tame5 [bug34]");
+            }
+            break;
+          case 5:
+            switch(r)
+            {
+              case 2: case 3:
+                Ip->type = stack_sprintf("[II-III-%ld] page 166",(d-5)/12);
+                Ip->neron = cyclic(2); break;
+              case 8: case 9:
+                Ip->type = stack_sprintf("[IV*-III*-%ld] page 168",(d-17)/12);
+                Ip->neron = cyclic(6); break;
+              default: pari_err_BUG("tame5 [bug35]");
+            }
+            break;
+          case 7:
+            switch(r)
+            {
+              case 3: case 4:
+                Ip->type = stack_sprintf("[IV-III-%ld] page 167",(d-7)/12);
+                Ip->neron = cyclic(6); break;
+              case 9: case 10:
+                Ip->type = stack_sprintf("[II*-III*-%ld] page 167",(d-19)/12);
+                Ip->neron = cyclic(2); break;
+              default: pari_err_BUG("tame5 [bug36]");
+            }
+            break;
+          case 11:
+            switch(r)
+            {
+              case 3: case 8:
+                Ip->type = stack_sprintf("[IV*-III-%ld] page 168",(d-11)/12);
+                Ip->neron = cyclic(6); break;
+              case 2: case 9:
+                Ip->type = stack_sprintf("[II-III*-%ld] page 166",(d-11)/12);
+                Ip->neron = cyclic(2); break;
+              default: pari_err_BUG("tame5 [bug37]");
+            }
+            break;
+          default: pari_err_BUG("tame5 [bug38]");
+        }
+        break;
+      default: pari_err_BUG("tame5 [bug39]");
+    }
+  }
+  else
+  {
+    r %= (n >> 1);
+    switch(n)
+    {
+      case 2: condp = 2;
+        Ip->type = stack_sprintf("[2I{0}-%ld] page 159",(d/2));
+        Ip->neron = cyclic(1); break;
+      case 4: condp = 4;
+        Ip->type = stack_sprintf("[2I*{0}-%ld] page 159",(d/2-1)/2);
+        Ip->neron = dicyclic(2,2); break;
+      case 6: condp = 4;
+        Ip->neron = cyclic(3);
+        switch(r)
+          {
+          case 1:
+            Ip->type = stack_sprintf("[2IV-%ld] page 165",(d/2-1)/3);
+            break;
+          case 2:
+            Ip->type = stack_sprintf("[2IV*-%ld] page 165",(d/2-2)/3);
+            break;
+          default: pari_err_BUG("tame5 [bug40]");
+          }
+        break;
+      case 8: condp = 4;
+        Ip->neron = cyclic(2);
+        switch(r)
+        {
+          case 1:
+            Ip->type = stack_sprintf("[2III-%ld] page 168",(d/2-1)/4);
+            break;
+          case 3:
+            Ip->type = stack_sprintf("[2III*-%ld] page 168",(d/2-3)/4);
+            break;
+          default: pari_err_BUG("tame5 [bug41]");
+        }
+        break;
+      case 12: condp = 4;
+        Ip->neron = cyclic(1);
+        switch(r)
+        {
+          case 1:
+            Ip->type = stack_sprintf("[2II-%ld] page 162",(d/2-1)/6);
+            break;
+          case 5:
+            Ip->type = stack_sprintf("[2II*-%ld] page 163",(d/2-5)/6);
+            break;
+          default: pari_err_BUG("tame5 [bug42]");
+        }
+        break;
+      default: pari_err_BUG("tame5 [bug43]");
+    }
+  }
+  return condp;
+}
+
+static long
+tame_6(struct igusa *I, struct igusa_p *Ip, GEN dk,
+         GEN polh, GEN theta, long alpha, long dismin)
+{
+  long condp = -1, d, d1, n, dm, r;
+  GEN val = Ip->val, d1k;
+
+  tame_567_init(I, Ip, dk, &d, &n, &dm, &r);
+  d1k = frac2s(Ip->eps*(val[6]-val[7])+val[Ip->eps2], Ip->eps);
+  d1 = itos(gmulsg(n,d1k));
+  switch(n)
+  {
+    case 1: condp = 1;
+      Ip->type = stack_sprintf("[I{%ld}-I{0}-%ld] page 170",d1,d);
+      Ip->neron = cyclic(d1); break;
+    case 2:
+      switch(dm)
+      {
+        case 0: condp = 4;
+          Ip->type=stack_sprintf("[I*{0}-I*{%ld}-%ld] page 171", d1/2,(d-2)/2);
+          Ip->neron = concat(groupH(d1/2), dicyclic(2,2)); break;
+        case 1: return labelm3(polh,theta,alpha,dismin,I,Ip);
+        default: pari_err_BUG("tame6 [bug44]");
+      }
+      break;
+    case 3: condp = 3;
+      Ip->neron = dicyclic(3,d1/3);
+      switch(dm)
+      {
+        case 1:
+          Ip->type = stack_sprintf("[IV-I{%ld}-%ld] page 173",d1/3,(d-1)/3);
+          break;
+        case 2:
+          Ip->type = stack_sprintf("[IV*-I{%ld}-%ld] page 173",d1/3,(d-2)/3);
+          break;
+        default: pari_err_BUG("tame6 [bug45]");
+      }
+      break;
+    case 4:
+      switch(dm)
+      {
+        case 1:
+          switch(r)
+          {
+            case 0: case 1: condp = 3;
+              Ip->type=stack_sprintf("[III-I{%ld}-%ld] page 176",d1/4,(d-1)/4);
+              Ip->neron = dicyclic(2,d1/4); break;
+            case 2: case 3: condp = 4;
+              Ip->type=stack_sprintf("[III*-I*{%ld}-%ld] page 177",d1/4,(d-5)/4);
+              Ip->neron = concat(groupH(d1/4), cyclic(2)); break;
+              break;
+            default: pari_err_BUG("tame6 [bug46]");
+          }
+          break;
+        case 3:
+          switch(r)
+          {
+            case 0: case 3: condp = 3;
+              Ip->type=stack_sprintf("[III*-I{%ld}-%ld] page 176",d1/4,(d-3)/4);
+              Ip->neron = dicyclic(2,d1/4); break;
+            case 1: case 2: condp = 4;
+              Ip->type=stack_sprintf("[III-I*{%ld}-%ld] page 177",d1/4,(d-3)/4);
+              Ip->neron = concat(groupH(d1/4), cyclic(2)); break;
+            default: pari_err_BUG("tame6 [bug47]");
+          }
+          break;
+        default: pari_err_BUG("tame6 [bug48]");
+      }
+      break;
+    case 6:
+      switch(dm)
+      {
+        case 1:
+          switch(r)
+          {
+            case 0: case 1: condp = 3;
+              Ip->type = stack_sprintf("[II-I{%ld}-%ld] page 172",d1/6,(d-1)/6);
+              Ip->neron = cyclic(d1/6); break;
+            case 3: case 4: condp = 4;
+              Ip->type=stack_sprintf("[IV*-I*{%ld}-%ld] page 174",d1/6,(d-7)/6);
+              Ip->neron = concat(groupH(d1/6), cyclic(3)); break;
+            default: pari_err_BUG("tame6 [bug49]");
+          }
+          break;
+        case 2: condp = 4;
+          Ip->type = stack_sprintf("[II*-I*{%ld}-%ld] page 174",d1/6,(d-8)/6);
+          Ip->neron = groupH(d1/6); break;
+        case 4: condp = 4;
+          Ip->type = stack_sprintf("[II-I*{%ld}-%ld] page 173",d1/6,(d-4)/6);
+          Ip->neron = groupH(d1/6); break;
+        case 5:
+          switch(r)
+          {
+            case 0: case 5: condp = 3;
+              Ip->type=stack_sprintf("[II*-I{%ld}-%ld] page 172",d1/6,(d-5)/6);
+              Ip->neron = cyclic(d1/6); break;
+            case 2: case 3: condp = 4;
+              Ip->type=stack_sprintf("[IV-I*{%ld}-%ld] page 174",d1/6,(d-5)/6);
+              Ip->neron = concat(groupH(d1/6), cyclic(3)); break;
+            default: pari_err_BUG("tame6 [bug50]");
+          }
+          break;
+        default: pari_err_BUG("tame6 [bug51]");
+      }
+      break;
+    default: pari_err_BUG("tame6 [bug52]");
+  }
+  return condp;
+}
+
+static long
+tame_7(struct igusa *I, struct igusa_p *Ip, GEN dk,
+         GEN polh, GEN theta, long alpha, long dismin)
+{
+  long condp = -1, d, d1, d2, n, dm, r;
+  GEN val = Ip->val, d1k, d2k, pro1;
+
+  tame_567_init(I, Ip, dk, &d, &n, &dm, &r);
+  pro1 = frac2s(Ip->eps*val[6]+val[Ip->eps2]-3*Ip->eps*val[3], Ip->eps);
+  d1k = gmin(stoi(val[7]-3*val[3]),gmul2n(pro1,-1));
+  d2k = gsub(pro1,d1k);
+
+  d1 = itos(gmulsg(n,d1k));
+  d2 = itos(gmulsg(n,d2k));
+  switch(n)
+  {
+    case 1: condp = 2;
+      Ip->type = stack_sprintf("[I{%ld}-I{%ld}-%ld] page 179",d1,d2,d);
+      Ip->neron = dicyclic(d1,d2); break;
+    case 2:
+      if ( odd(val[Ip->eps2]) )
+      {
+        condp = 3;
+        Ip->type = stack_sprintf("[2I{%ld}-%ld] page 181",d1,d/2);
+        Ip->neron = cyclic(d1);
+      }
+      else if (dm == 0)
+      {
+        condp = 4;
+        Ip->type = stack_sprintf("[I*{%ld}-I*{%ld}-%ld] page 180", d1/2,d2/2,(d-2)/2);
+        Ip->neron = concat(groupH(d1/2),groupH(d2/2));
+      }
+      else
+      {
+        GEN H;
+        if (d1 != d2) return labelm3(polh,theta,alpha,dismin,I,Ip);
+        condp = 3; H = groupH(d1/2);
+        Ip->type = stack_sprintf("[I{%ld}-I*{%ld}-%ld] page 180", d1/2,d1/2,(d-1)/2);
+        Ip->neron = concat(H, H);
+      }
+      break;
+    case 4: condp = 4;
+      Ip->type = stack_sprintf("[2I*{%ld}-%ld] page 181",d1/2,(d-2)/4);
+      Ip->neron = groupH(d1/2); break;
+    default: pari_err_BUG("tame7 [bug55]");
+  }
+  return condp;
+}
+
+static long
+tame(GEN polh, GEN theta, long alpha, long dismin, struct igusa *I, struct igusa_p *Ip)
+{
+  GEN val = Ip->val, dk;
+  Ip->tame = 1;
+  switch(Ip->tt)
+  {
+    case 1: return tame_1(I, Ip);
+    case 2: return tame_2(I, Ip, myval(I->i12,  Ip->p));
+    case 3: return tame_3(I, Ip, 3*myval(I->i4, Ip->p));
+    case 4: return tame_4(I, Ip, 6*myval(I->j2, Ip->p));
+    case 5:
+      dk = frac2s(Ip->eps*val[6]-5*val[Ip->eps2], 12*Ip->eps);
+      return tame_5(I, Ip, dk);
+    case 6:
+      dk = frac2s(Ip->eps*val[7]-6*val[Ip->eps2], 12*Ip->eps);
+      return tame_6(I, Ip, dk, polh, theta, alpha, dismin);
+    case 7:
+      dk = frac2s(Ip->eps*val[3]-2*val[Ip->eps2], 4*Ip->eps);
+      return tame_7(I, Ip, dk, polh, theta, alpha, dismin);
+  }
+  return -1; /*not reached*/
+}
+
+/* maxc = maximum conductor valuation at p */
+static long
+get_maxc(GEN p)
+{
+  switch (itos_or_0(p))
+  {
+    case 2:  return 20; break;
+    case 3:  return 10; break;
+    case 5:  return 9; break;
+    default: return 4; break; /* p > 5 */
+  }
+}
+
+/* p = 3 */
+static long
+quartic(GEN polh, long alpha, long dismin, struct igusa_p *Ip)
+{
+  GEN theta, val = Ip->val, p = Ip->p;
+  GEN polf = polymini_zi2(ZX_Z_mul(polh, powiu(p, alpha)));
+  long condp = -1, d, R, r1, beta;
+  theta = gel(polf,1);
+  beta = itos(gel(polf,2));
+  if (odd(beta)) pari_err_BUG("quartic [type over Z[i] must be [K-K-(2*m)]]");
+  R = beta/2;
+  r1 = itos(gmulgs(theta,6));
+  switch(Ip->tt)
+  {
+    case 1: case 5: d = 0;break;
+    case 3: d = val[6] - 5*val[3]/2;break;
+    case 7: d = val[6] - 3*val[3] + val[Ip->eps2]/Ip->eps;break;
+    default: pari_err_BUG("quartic [type choices]");
+             d = 0; /*not reached*/
+  }
+  switch(r1)
+  {
+    case 0:
+      if (d)
+      {
+        condp = 3;
+        Ip->type = stack_sprintf("[2I{%ld}-%ld] page 181",d,R);
+        Ip->neron = cyclic(d);
+      }
+      else
+      {
+        condp = 2;
+        Ip->neron = cyclic(1);
+        if (R) Ip->type = stack_sprintf("[2I{0}-%ld] page 159",R);
+        else   Ip->type = "[II] page 155";
+      }
+      break;
+    case 6: condp = 4;
+      Ip->type = stack_sprintf("[2I*{%ld}-%ld] pages 159, 181",d,R);
+      Ip->neron = dicyclic(2,2); break;
+    case 3: condp = 4;
+      Ip->type = stack_sprintf("[2III-%ld] page 168",R);
+      Ip->neron = cyclic(2); break;
+    case 9: condp = 4;
+      Ip->type = stack_sprintf("[2III*-%ld] page 168",R);
+      Ip->neron = cyclic(2); break;
+    case 2: condp = dismin-12*R-13;
+      Ip->type = stack_sprintf("[2II-%ld] page 162",R);
+      Ip->neron = cyclic(1); break;
+    case 8: condp = dismin-12*R-19;
+      Ip->type = stack_sprintf("[2IV*-%ld] page 165",R);
+      Ip->neron = cyclic(3); break;
+    case 4: condp = dismin-12*R-15;
+      Ip->type = stack_sprintf("[2IV-%ld] page 165",R);
+      Ip->neron = cyclic(3); break;
+    case 10: condp = dismin-12*R-21;
+      Ip->type = stack_sprintf("[2II*-%ld] page 163",R);
+      Ip->neron = cyclic(1); break;
+    default: pari_err_BUG("quartic [type1]");
+  }
+  if (condp > get_maxc(p) || condp < 0) pari_err_BUG("quartic [conductor]");
+  return condp;
+}
+
+static long
+litredtp(long alpha, long alpha1, GEN theta, GEN theta1, GEN polh, GEN polh1,
+         long dismin, struct igusa *I, struct igusa_p *Ip)
+{
+  GEN val = Ip->val, p = Ip->p;
+  long condp = -1, indice, d, R = Ip->R;
+
+  if ((Ip->r1 == 0||Ip->r1 == 6) && (Ip->r2 == 0||Ip->r2 == 6))
+  { /* (r1,r2) = (0,0), (0,6), (6,0) or (6,6) */
+    if (Ip->tt == 5)
+    {
+      switch(Ip->r1 + Ip->r2)
+      {
+      case 0: /* (0,0) */
+        condp = 0;
+        Ip->type = stack_sprintf("[I{0}-I{0}-%ld] page 158",R);
+        Ip->neron = cyclic(1); break;
+      case 6: /* (0,6) or (6,0) */
+        condp = 2;
+        Ip->type = stack_sprintf("[I*{0}-I{0}-%ld] page 159",R);
+        Ip->neron = dicyclic(2,2); break;
+      case 12: /* (6,6) */
+        condp = 4;
+        Ip->type = stack_sprintf("[I*{0}-I*{0}-%ld] page 158",R);
+        Ip->neron = mkvecsmall4(2,2,2,2); break;
+      }
+      return condp;
+    }
+    if (Ip->r1 == Ip->r2) return tame(polh, theta, alpha, dismin, I, Ip);
+    if (Ip->tt == 6)
+    {
+      d = val[6] - val[7] + (val[Ip->eps2]/Ip->eps);
+      if (Ip->r1 && alpha1 == 0) /* H(px) / p^3 */
+        polh1 = RgX_Rg_div(RgX_unscale(polh1,p), powiu(p,3));
+      if (FpX_is_squarefree(FpX_red(polh1,p),p))
+      { indice = 0; condp = 3-Ip->r2/6; }
+      else
+      { indice = d; condp = 3-Ip->r1/6; }
+    }
+    else
+    { /* Ip->tt == 7 */
+      long d1;
+      d = val[6] - 3*val[3] + (val[Ip->eps2]/Ip->eps);
+      if (gequal1(theta1)) /* H(px) / p^3 */
+        polh1 = RgX_Rg_div(RgX_unscale(polh1,p), powiu(p,3));
+      d1 = minss(val[7]-3*val[3],d/2);
+      if (d == 2*d1) indice = d1;
+      else
+      {
+        indice = discpart(polh1,p,d1+1);
+        if (indice>= d1+1) indice = d-d1;
+        else indice = d1;
+      }
+      condp = 3;
+    }
+    if (Ip->r1)
+    { /* (6,0) */
+      Ip->neron = concat(cyclic(d-indice),groupH(indice));
+      if (Ip->tt == 6)
+        Ip->type = stack_sprintf("[I*{%ld}-I{%ld}-%ld] page 170",indice,d-indice,R);
+      else
+        Ip->type = stack_sprintf("[I*{%ld}-I{%ld}-%ld] page 180",indice,d-indice,R);
+    }
+    else
+    { /* (0,6) */
+      Ip->neron = concat(cyclic(indice),groupH(d-indice));
+      if (Ip->tt == 6)
+        Ip->type = stack_sprintf("[I{%ld}-I*{%ld}-%ld] page 170", indice,d-indice,R);
+      else
+        Ip->type = stack_sprintf("[I{%ld}-I*{%ld}-%ld] page 180", indice,d-indice,R);
+    }
+    return condp;
+  }
+  if (Ip->tt == 7) pari_err_BUG("litredtp [switch ri]");
+  {
+    struct red S1, S;
+    long comp = get_red(&S1, Ip, polh1, p, alpha1, Ip->r1)
+              + get_red(&S,  Ip, polh, p, alpha, Ip->r2);
+    Ip->type = stack_sprintf("[%s-%s-%ld] pages %s", S1.t, S.t, R, S.pages);
+    Ip->neron = concat(S1.g, S.g);
+    condp = (R >= 0)? dismin-comp+2-12*R: dismin-comp+4;
+  }
+  if (condp > get_maxc(p)) pari_err_BUG("litredtp [conductor]");
+  return condp;
+}
+
+static long
+labelm3(GEN polh, GEN theta, long alpha, long dismin,
+        struct igusa *I, struct igusa_p *Ip)
+{
+  GEN polh1, theta1, polf, val = Ip->val, p = Ip->p;
+  long alpha1, lambda, beta, R;
+
+  polh1 = polh;
+  theta1 = theta;
+  alpha1 = alpha;
+  polf = polymini(ZX_Z_mul(RgX_recip6(polh), powiu(p,alpha)), p);
+  polh  = gel(polf,1);
+  lambda= itos(gel(polf,2));
+  theta = gel(polf,3);
+  alpha = itos(gel(polf,4));
+  beta  = itos(gel(polf,6));
+  if (lambda != 3) pari_err_BUG("labelm3 [lambda = 3]");
+  R = beta-alpha1-alpha;
+  if (R&1) pari_err_BUG("labelm3 [R odd]");
+  R >>= 1;
+  if (R <= -2) pari_err_BUG("labelm3 [R <= -2]");
+  if (val[Ip->eps2] % (2*Ip->eps)) pari_err_BUG("labelm3 [val(eps2)]");
+  if (R >= 0 && (alpha+alpha1) >= 1) pari_err_BUG("labelm3 [minimal equation]");
+  Ip->r1 = itos(gmulgs(theta1,6)) + 6*alpha1;
+  Ip->r2 = itos(gmulgs(theta, 6)) + 6*alpha;
+  Ip->R = R;
+  return litredtp(alpha, alpha1, theta, theta1, polh, polh1, dismin, I, Ip);
+}
+
+/* p = 3 */
+static long
+quadratic(GEN polh, long alpha, long dismin,
+          struct igusa *I, struct igusa_p *Ip)
+{
+  long alpha1, beta, R;
+  GEN polf, polh1, theta, theta1;
+  alpha1 = alpha;
+  polf = polymini_zi(ZX_Z_mul(polh, powiu(Ip->p,alpha)));
+  theta = gel(polf,1);
+  alpha = itos(gel(polf,2));
+  beta  = itos(gel(polf,3));
+  if (alpha && beta >= 1) pari_err_BUG("quadratc");
+  R = beta-alpha;
+  if (R >= 0 && alpha1)
+  {
+    dismin -= 10;
+    if (DEBUGLEVEL)
+      err_printf("(Care: minimal discriminant over Z[i] smaller than over Z)\n");
+  }
+  Ip->r1 = itos(gmulgs(theta,6))+6*alpha;
+  Ip->r2 = Ip->r1;
+  Ip->R = R;
+  alpha1 = alpha;
+  theta1 = theta;
+  polh1 = polh; /* FIXME !!! */
+  return litredtp(alpha, alpha1, theta, theta1, polh, polh1, dismin, I, Ip);
+}
+
+static long
+genus2localred(struct igusa *I, struct igusa_p *Ip, GEN p, GEN polmini)
+{
+  GEN val, polh, theta, list, c1, c2, c3, c4, c5, c6, prod;
+  long i, vb5, vb6, d, dismin, alpha, lambda;
+  long condp = -1, indice, vc6, mm, nb, dism;
+
+  val = cgetg(8, t_VECSMALL);
+  Ip->tame = 0;
+  Ip->neron = NULL;
+  Ip->type = NULL;
+  Ip->p = p;
+  Ip->val = val;
+  val[1] = myval(I->j2,p);
+  val[2] = myval(I->j4,p);
+  val[3] = myval(I->i4,p);
+  val[4] = myval(I->j6,p);
+  val[5] = myval(I->j8,p);
+  val[6] = myval(I->j10,p);
+  val[7] = myval(I->i12,p);
+  dismin = val[6];
+  stable_reduction(I, Ip);
+  if (dismin == 0)
+  {
+    Ip->tame = 1;
+    Ip->type = "[I{0-0-0}] page 155";
+    Ip->neron = cyclic(1); return 0;
+  }
+  if (dismin == 1)
+  {
+    Ip->type = "[I{1-0-0}] page 170";
+    Ip->neron = cyclic(1); return 1;
+  }
+  if (dismin == 2) switch(Ip->tt)
+  {
+    case 2:
+      Ip->type = "[I{2-0-0}] page 170";
+      Ip->neron = cyclic(2); return 1;
+    case 3:
+      Ip->type = "[I{1-1-0}] page 179";
+      Ip->neron = cyclic(1); return 2;
+    case 5:
+      if (cmpis(p,3) <= 0) pari_err_BUG("genus2localred [tt 1]");
+      Ip->type = "[I{0}-II-0] page 159";
+      Ip->neron = cyclic(1); return 2;
+    default: pari_err_BUG("genus2localred [tt 2]");
+  }
+  if (equaliu(p,2)) return -1;
+  polh = gel(polmini,1);
+  lambda = itos(gel(polmini,2));
+  theta = gel(polmini,3);
+  alpha = itos(gel(polmini,4));
+  if (!gequal0(gel(polmini,5)))
+    return equalis(p,3)? quadratic(polh, alpha, dismin, I, Ip):
+                         tame(polh, theta, alpha, dismin, I, Ip);
+  if (gequal0(theta) && lambda<= 2)
+  {
+    if (Ip->tt >= 5) pari_err_BUG("genus2localred [tt 3]");
+    return tame(polh, theta, alpha, dismin, I, Ip);
+  }
+  if (dismin == 3)
+  {
+    switch(Ip->tt)
+    {
+      case 2: return tame(polh, theta, alpha, dismin, I, Ip);
+      case 3: Ip->type = "[I{2-1-0}] page 179"; Ip->neron = cyclic(2); return 2;
+      case 4: Ip->type = "[I{1-1-1}] page 182"; Ip->neron = cyclic(3); return 2;
+      case 5:
+        if (equalis(p,3) && !gequal(theta,ghalf))
+          return labelm3(polh,theta,alpha,dismin,I,Ip);
+        Ip->type = "[I{0}-III-0] page 161"; Ip->neron = cyclic(2); return 2;
+      case 6:
+        if (equalis(p,3)) pari_err_BUG("genus2localred [conductor]");
+        Ip->type = "[I{1}-II-0] page 172"; Ip->neron = cyclic(1); return 3;
+    }
+    pari_err_BUG("genus2localred [switch tt 4]");
+    return -1; /* not reached */
+  }
+  switch(lambda)
+  {
+    case 0:
+      switch(itos(gmulgs(theta, 60))+alpha)
+      {
+        case 10:
+          condp = dismin-1;
+          Ip->type = "[V] page 156";
+          Ip->neron = cyclic(3); break;
+        case 11:
+          condp = dismin-11;
+          Ip->type = "[V*] page 156";
+          Ip->neron = cyclic(3); break;
+        case 12:
+          condp = dismin-2;
+          Ip->type = "[IX-2] page 157";
+          Ip->neron = cyclic(5); break;
+        case 13:
+          condp = dismin-12;
+          Ip->type = "[VIII-4] page 157";
+          Ip->neron = cyclic(1); break;
+        case 24:
+          condp = dismin-8;
+          Ip->type = "[IX-4] page 158";
+          Ip->neron = cyclic(5);
+          break;
+        case 15: case 16:
+          if (Ip->tt>= 5) pari_err_BUG("genus2localred [tt 6]");
+          return tame(polh, theta, alpha, dismin, I, Ip);
+        case 20: case 21:
+          {
+            GEN b0, b1, b2, b3, b4, b5, b6, b02, b03, b04, b05;
+            RgX_to_6(polh, &b0,&b1,&b2,&b3,&b4,&b5,&b6);
+            vb5 = myval(b5,p);
+            vb6 = myval(b6,p);
+            if (vb6 >= 3)
+            {
+              if (vb5 < 2) pari_err_BUG("genus2localred [red1]");
+              if (vb5 >= 3)
+              {
+                condp = dismin-8;
+                Ip->type = "[II*-IV-(-1)] page 164";
+                Ip->neron = cyclic(3);
+              }
+              else
+              {
+                condp = dismin-7;
+                Ip->type = "[IV-III*-(-1)] page 167";
+                Ip->neron = cyclic(6);
+              }
+              break;
+            }
+            if (dvdii(b0,p)) pari_err_BUG("genus2localred [b0]");
+            b02 = gsqr(b0);
+            b03 = gmul(b02, b0);
+            b04 = gmul(b03, b0);
+            b05 = gmul(b04, b0);
+            c1 = gmul2n(b1,-1);
+            c2 = gmul2n(gsub(gmul(b0,b2), gsqr(c1)),-1);
+            c3 = gmul2n(gsub(gmul(b02,b3), gmul2n(gmul(c1,c2),1)),-1);
+            c4 = gsub(gmul(b03,b4), gadd(gmul2n(gmul(c1,c3),1),gsqr(c2)));
+            c5 = gsub(gmul(b04,b5), gmul2n(gmul(c2,c3),1));
+            c6 = gsub(gmul(b05,b6), gsqr(c3));
+            /* b0^5*H(x/b0) = (x^3+c1*x^2+c2*x+c3)^2+c4*x^2+c5*x+c6 */
+            vc6 = myval(c6,p);
+            if (vc6 == 2)
+            {
+              if (alpha)
+              {
+                condp = dismin-16;
+                Ip->type = "[IV] page 155";
+                Ip->neron = cyclic(1);
+              }
+              else
+              {
+                condp = dismin-6;
+                Ip->type = "[III] page 155";
+                Ip->neron = dicyclic(3,3);
+              }
+            }
+            else
+            {
+              if (myval(c3,p) > 1) pari_err_BUG("genus2localred [c3]");
+              mm = min3(3*myval(c4,p)-4, 3*myval(c5,p)-5, 3*vc6-6);
+              if (alpha)
+              {
+                condp = dismin-mm-16;
+                Ip->type = stack_sprintf("[III*{%ld}] page 184", mm);
+                Ip->neron = cyclic(1);
+              }
+              else
+              {
+                condp = dismin-mm-6;
+                Ip->type = stack_sprintf("[III{%ld}] page 184", mm);
+                Ip->neron = (mm%3)? cyclic(9): dicyclic(3,3);
+              }
+            }
+          }
+          break;
+        case 30:
+          return equalis(p,3)? quartic(polh, alpha, dismin, Ip)
+                             : tame(polh, theta, alpha, dismin, I, Ip);
+        default: pari_err_BUG("genus2localred [red2]");
+      }
+      break;
+    case 1:
+      switch(itos(gmulgs(theta, 60))+alpha)
+      {
+        case 12:
+          condp = dismin;
+          Ip->type = "[VIII-1] page 156";
+          Ip->neron = cyclic(1); break;
+        case 13:
+          condp = dismin-10;
+          Ip->type = "[IX-3] page 157";
+          Ip->neron = cyclic(5); break;
+        case 24:
+          condp = dismin-4;
+          Ip->type = "[IX-1] page 157";
+          Ip->neron = cyclic(5); break;
+        case 25:
+          condp = dismin-14;
+          Ip->type = "[VIII-3] page 157";
+          Ip->neron = cyclic(1); break;
+        case 36:
+          condp = dismin-8;
+          Ip->type = "[VIII-2] page 157";
+          Ip->neron = cyclic(1); break;
+        case 15:
+          condp = dismin-1;
+          Ip->type = "[VII] page 156";
+          Ip->neron = cyclic(2); break;
+        case 16:
+          condp = dismin-11;
+          Ip->type = "[VII*] page 156";
+          Ip->neron = cyclic(2); break;
+        case 20:
+          if (cmpis(p,3))
+          {
+            d = 6*val[6]-5*val[7]-2;
+            if (d%6) pari_err_BUG("genus2localred [index]");
+            dism = (d/6);
+          }
+          else
+          {
+            list = padicfactors(polh,p,dismin-5);
+            nb = lg(list);
+            prod = pol_1(varn(polh));
+            for(i = 1;i<nb;i++)
+            {
+              GEN c = gel(list,i);
+              if (valp(gel(c,2)) && degpol(c)<= 2) prod = RgX_mul(prod,c);
+            }
+            if (degpol(prod) > 2) pari_err_BUG("genus2localred [padicfactors]");
+            dism = valp(RgX_disc(prod)) - 1;
+          }
+          condp = dismin-dism-3;
+          Ip->type = stack_sprintf("[II-II*{%ld}] page 176", dism);
+          Ip->neron = groupH(dism+1); break;
+        case 21:
+          vb6 = myval(truecoeff(polh,0),p);
+          if (vb6<2) pari_err_BUG("genus2localred [red3]");
+          condp = dismin-14;
+          Ip->type = "[IV*-II{0}] page 175";
+          Ip->neron = cyclic(1); break;
+        case 30:
+          vb5 = myval(truecoeff(polh,1),p);
+          if (vb5 == 2)
+          {
+            if (Ip->tt >= 5) pari_err_BUG("genus2localred [tt 6]");
+            return tame(polh, theta, alpha, dismin, I, Ip);
+          }
+          condp = dismin-7;
+          Ip->type = "[II*-III-(-1)] page 167";
+          Ip->neron = cyclic(2); break;
+      }
+      break;
+    case 2:
+      if (equalis(denom(theta),4))
+      {
+        if (Ip->tt>4) pari_err_BUG("genus2localred [tt 5]");
+        return tame(polh, theta, alpha, dismin, I, Ip);
+      }
+      if (!equalis(p,3) && equalis(denom(theta),3))
+        return tame(polh, theta, alpha, dismin, I, Ip);
+      list = padicfactors(polh,p,dismin-10*alpha);
+      nb = lg(list); prod = pol_1(varn(polh));
+      for(i = 1;i<nb;i++)
+      {
+        GEN c = gel(list,i);
+        if (!valp(gel(c,2))) prod = RgX_mul(prod,c);
+      }
+      switch(degpol(prod))
+      {
+        GEN e0, e1, e2;
+        case 0:
+          dism = 0; break;
+        case 1:
+          e1 = gel(prod,3);
+          dism = 2*valp(e1); break;
+        case 2:
+          e0 = gel(prod,2);
+          e1 = gel(prod,3);
+          e2 = gel(prod,4);
+          dism = valp(gsub(gsqr(e1),gmul2n(gmul(e0,e2),2))); break;
+        default:
+          pari_err_BUG("genus2localred [padicfactors 2]");
+          dism = 0;
+      }
+      switch(itos(gmulgs(theta,12))+alpha-4)
+      {
+        case 0:
+          condp = dismin-dism-1;
+          Ip->type = stack_sprintf("[IV-II{%ld}] page 175", dism);
+          Ip->neron = cyclic(3*dism+2); break;
+        case 1:
+          condp = dismin-dism-10;
+          Ip->type = stack_sprintf("[II*-II*{%ld}] page 176",dism);
+          Ip->neron = groupH(dism+1); break;
+          break;
+        case 2: case 3:
+          if (myval(truecoeff(polh,0),p) == 2)
+          {
+            if (Ip->tt>4) pari_err_BUG("genus2localred [tt 5]");
+            return tame(polh, theta, alpha, dismin, I, Ip);
+          }
+          dism++;
+          indice = val[6]-(5*val[3]/2)-dism;
+          condp = dismin-dism-indice-2;
+          Ip->type = stack_sprintf("[II{%ld-%ld}] page 182", dism,indice);
+          Ip->neron = both_odd(dism,indice)? dicyclic(2,2*dism): cyclic(4*dism);
+          break;
+        case 4:
+          condp = dismin-dism-5;
+          Ip->type = stack_sprintf("[IV*-II{%ld}] page 175",dism+1);
+          Ip->neron = cyclic(3*dism+4); break;
+      }
+      break;
+    case 3:
+      if (!equalis(p,3) || Ip->tt <= 4)
+        return tame(polh, theta, alpha, dismin, I, Ip);
+      return labelm3(polh,theta,alpha,dismin,I,Ip); /* p = 3 */
+    default: pari_err_BUG("genus2localred [switch lambda]");
+  }
+  if (condp < 2 || condp > get_maxc(p))
+    pari_err_BUG("genus2localred [conductor]");
+  return condp;
+}
+
+static long
+chk_pol(GEN P) {
+  switch(typ(P))
+  {
+    case t_INT: break;
+    case t_POL: RgX_check_ZX(P,"genus2red"); return varn(P); break;
+    default: pari_err_TYPE("genus2red", P);
+  }
+  return -1;
+}
+
+/* P,Q are ZX, study Y^2 + Q(X) Y = P(X) */
+GEN
+genus2red(GEN Q, GEN P, GEN p)
+{
+  pari_sp av = avma;
+  struct igusa I;
+  GEN j22, j42, j2j6, a0,a1,a2,a3,a4,a5,a6, V,polr,facto,factp, vecmini, cond;
+  long i, l, dd, vP,vQ;
+
+  vP = chk_pol(P);
+  vQ = chk_pol(Q);
+  if (vP < 0)
+  {
+    if (vQ < 0) pari_err_TYPE("genus2red",mkvec2(P,Q));
+    P = scalarpol(P,vQ);
+  }
+  else if (vQ < 0) Q = scalarpol(Q,vP);
+  if (p && typ(p) != t_INT) pari_err_TYPE("genus2red", p);
+
+  polr = ZX_add(ZX_sqr(Q), gmul2n(P,2)); /* ZX */
+  switch(degpol(polr))
+  {
+    case 5: case 6: break;
+    default: pari_err_DOMAIN("genus2red","genus","!=", gen_2,mkvec2(P,Q));
+  }
+
+  RgX_to_6(polr, &a0,&a1,&a2,&a3,&a4,&a5,&a6);
+  I.j10 = !signe(a0)? mulii(sqri(a1), ZX_disc(polr)): ZX_disc(polr);
+  if (!signe(I.j10))
+    pari_err_DOMAIN("genus2red","genus","<",gen_2,mkvec2(P,Q));
+  I.j10 = gmul2n(I.j10, -12); /* t_INT */
+
+  if (p == NULL)
+  {
+    facto = factor(absi(I.j10));
+    factp = gel(facto,1);
+  }
+  else
+  {
+    factp = mkcol(p);
+    facto = mkmat2(factp, mkcol(gen_1));
+  }
+  l = lg(factp);
+  vecmini = cgetg(l, t_COL);
+  for(i = 1; i<l; i++)
+  {
+    GEN l = gel(factp,i), pm;
+    if (i == 1 && equalis(l, 2)) { gel(vecmini,1) = gen_0; continue; }
+    gel(vecmini,i) = pm = polymini(polr, l);
+    polr = RgX_Rg_mul(gel(pm,1), powii(l, gel(pm,4)));
+  }
+  RgX_to_6(polr, &a0,&a1,&a2,&a3,&a4,&a5,&a6);
+  I.j10 = !signe(a0)? mulii(sqri(a1), ZX_disc(polr)): ZX_disc(polr);
+  I.j10 = gmul2n(I.j10,-12);
+
+  I.a0 = a0;
+  I.A2 = apol2(a0,a1,a2);
+  I.A3 = apol3(a0,a1,a2,a3);
+  I.A5 = apol5(a0,a1,a2,a3,a4,a5);
+  I.B2 = bpol2(a0,a1,a2,a3,a4);
+
+  I.j2 = igusaj2(a0,a1,a2,a3,a4,a5,a6);
+  I.j4 = igusaj4(a0,a1,a2,a3,a4,a5,a6);
+  I.i4 = gsub(gsqr(I.j2), gmulsg(24,I.j4));
+  I.j6 = igusaj6(a0,a1,a2,a3,a4,a5,a6);
+  j42 = gsqr(I.j4);
+  j22 = gsqr(I.j2);
+  j2j6 = gmul(I.j2,I.j6);
+  I.j8 = gmul2n(gsub(j2j6,j42), -2);
+  I.i12= gmul2n(gsub(gadd(gmul(j22,j42),gmulsg(36,gmul(j2j6,I.j4))),
+                     gadd(gadd(gmulsg(32,gmul(j42,I.j4)),gmul(j2j6,j22)),gmulsg(108,gsqr(I.j6)))),-2);
+
+  for(i = 1; i < l; i++)
+    gcoeff(facto,i,2) = stoi(Q_pval(I.j10, gel(factp,i)));
+  dd = polval(polr,gen_2) & (~1); /* = 2 floor(val/2) */
+  polr = gmul2n(polr, -dd);
+
+  V = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    GEN q = gel(factp,i), red, N = NULL;
+    struct igusa_p Ip;
+    long f = genus2localred(&I, &Ip, q, gel(vecmini,i));
+    gcoeff(facto,i,2) = stoi(f);
+    if (Ip.tame) Ip.type = stack_strcat("(tame) ", Ip.type);
+    if (f >= 0)
+      N = zv_snf(Ip.neron);
+    if (DEBUGLEVEL)
+    {
+      if (!p) err_printf("p = %Ps\n", q);
+      err_printf("(potential) stable reduction: %Ps\n", Ip.stable);
+      if (f >= 0) {
+        err_printf("reduction at p: %s, %Ps", Ip.type, N);
+        err_printf(", f = %ld\n", f);
+      }
+    }
+    red = f >= 0? mkvec2(strtoGENstr(Ip.type), N): cgetg(1, t_VEC);
+    gel(V, i) = mkvec3(q, Ip.stable, red);
+  }
+  if (p) V = gel(V,1);
+  cond = factorback(facto);
+  /* remove denominator 2 coming from f = -1 in genuslocalred(, p = 2) */
+  if (typ(cond) != t_INT) cond = gel(cond,1);
+  return gerepilecopy(av, mkvec4(cond, facto, polr, V));
+}
diff --git a/src/modules/groupid.c b/src/modules/groupid.c
new file mode 100644
index 0000000..d622c12
--- /dev/null
+++ b/src/modules/groupid.c
@@ -0,0 +1,551 @@
+/* Copyright (C) 2003  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+static long
+groupelts_sumorders(GEN S)
+{
+  long i, s = 0;
+  for(i=1; i < lg(S); i++) s += perm_order(gel(S,i));
+  return s;
+}
+
+static long
+vecgroup_sumorders(GEN L)
+{
+  long i, s = 0;
+  for(i=1; i<lg(L); i++) s += group_order(gel(L,i));
+  return s;
+}
+
+static int
+indexgroupsubgroup(GEN L, long order, const long *good, const long *bad)
+{
+  long i;
+  for(i=1; i<lg(L); i++)
+  {
+    GEN G=gel(L,i);
+    long idx;
+    const long *p;
+    if (group_order(G)!=order) continue;
+    idx = group_ident(G,NULL);
+    for(p=good; *p; p++)
+      if (*p==idx) return 1;
+    for(p=bad; *p; p++)
+      if (*p==idx) return 0;
+  }
+  return 0;
+}
+
+static int
+indexgroupcentre(GEN G, GEN Z, const long *good, const long *bad)
+{
+  long i;
+  for(i=1;i<lg(Z);i++)
+  {
+    GEN z=gel(Z,i);
+    if (perm_order(z)==2)
+    {
+      pari_sp btop=avma;
+      GEN H = cyclicgroup(z,2);
+      GEN C = group_quotient(G,H);
+      GEN Q = quotient_group(C,G);
+      const long *p;
+      long idx=group_ident(Q,NULL);
+      avma=btop;
+      for(p=good;*p;p++)
+        if (*p==idx) return 1;
+      for(p=bad;*p;p++)
+        if (*p==idx) return 0;
+    }
+  }
+  return 0;
+}
+
+static GEN
+vecgroup_idxlist(GEN L, long order)
+{
+  pari_sp ltop=avma;
+  GEN V;
+  long i,j,n;
+  for (n=0,i=1; i<lg(L); i++)
+    if (group_order(gel(L,i))==order)
+      n++;
+  V=cgetg(n+1,t_VECSMALL);
+  for(i=1,j=1; j<=n; i++)
+    if (group_order(gel(L,i))==order)
+      V[j++]=group_ident(gel(L,i),NULL);
+  return gerepileupto(ltop,vecsmall_uniq(V));
+}
+
+/* Group identification code.
+ * The group data are taken from GAP 4 and the SMALL GROUPS LIBRARY by  Hans
+ * Ulrich Besche, Bettina Eick and Eamonn O'Brien.
+ */
+
+static long
+group_ident_i(GEN G, GEN S)
+{
+  long n = group_order(G);
+  long s;
+  GEN F,p,e;
+  if (n==1) return 1;
+  if (!S) S = group_elts(G,group_domain(G));
+  s = groupelts_sumorders(S);/*This is used as a hash value*/
+  F = factoru(n);
+  p = gel(F,1);
+  e = gel(F,2);
+  switch(lg(p)-1)
+  {
+  case 1:/*prime power*/
+    switch (e[1])
+    {
+    case 1: /* p */
+      return 1;
+    case 2: /* p^2 */
+      return (s == 1 - p[1] + n*p[1])? 2: 1; /* pxp || p^2 */
+    case 3: /* p^3 */
+      {
+        GEN H = group_abelianSNF(G, S);
+        if (H) /*G is abelian*/
+        {
+          long l = lg(H)-1;
+          return (l==3)?5: l; /*pxpxp || p^3 or p^2xp*/
+        } /*G is not abelian*/
+        if (p[1] == 2)
+          return (s == 19)? 3: 4; /*D8 || Q8*/
+        else
+        {
+          long q = p[1]*p[1];
+          q *= q;
+          return (s == q - p[1] + 1)?3 :4; /* pxp:p || p^2:p */
+        }
+      }
+    }
+    break;
+  case 2:
+    switch(e[1]+e[2])
+    {
+    case 2: /*pq*/
+      return (p[2]%p[1]!=1)?1:1+group_isabelian(G); /*pq || pq or p:q*/
+    case 3:
+      if (p[1]==2 && e[1]==2) /* 4p */
+      {
+        long q = p[2], q2 = q*q, pmq2 = (q%4 == 1 || q==3);
+        if (s==3+5*q+3*q2) return 1; /* 2p.2 */
+        if (s==11-11*q+11*q2) return 2; /* 4p */
+        if (s==3+q+3*q2) return 3+pmq2; /* D4p */
+        if (s==7-7*q+7*q2) return 4+pmq2; /* 2px2 */
+        return 3; /*A4 or p:4 */
+      }
+      else if (p[1]==2 && e[1]==1) /*2p^2*/
+      {
+        long q = p[2], q2 = q*q, q3 = q*q2, q4 = q*q3;
+        if (s==1-q+3*q2-q3+q4) return 1; /* p^2:2 */
+        if (s==3-3*q+3*q2-3*q3+3*q4) return 2; /* 2p^2 */
+        if (s==1+q-2*q2+3*q3) return 3; /* D2pxp */
+        if (s==1-q+2*q2+q3) return 4; /* p:2+p:2 */
+        return 5;   /* 2pxp */
+      }
+      else if (p[1]==3 && e[1]==2) /*9p, p>3*/
+      {
+        long q= p[2], q2 = q*q, p3 = (q%3 == 1), p9 = (q%9 == 1);
+        if (s==7+47*q+7*q2) return 1; /* 3p.3 */
+        if (s==61-61*q+61*q2) return 1+p3; /* 9p */
+        if (s==1+59*q+q2) return 3; /* p:9 */
+        if (s==7+11*q+7*q2) return 3+p9; /* p:3x3 */
+        return 2+2*p3+p9; /* 3^2xp */
+      }
+      break;
+    }
+  case 3:
+    switch(e[1]+e[2]+e[3])
+    {
+    case 3: /*pqr*/
+      if (p[1]==2 && p[2]==3)  /*6p*/
+      {
+        long q = p[3],q2 = q*q, pmq = (q%3==1)? 2: 0;
+        if (s==13-13*q+13*q2) return 1+pmq; /* S3xp */
+        if (s==7+7*q+7*q2) return 2+pmq; /* D2px3 */
+        if (s==7-q+7*q2) return 3+pmq; /* 3:2+p:2 */
+        if (s==21-21*q+21*q2) return 4+pmq; /* 6p */
+        if (s==1+19*q+q2) return 1; /* p:6 */
+        return 2;       /* p:3x2 */
+      }
+      break;
+    }
+  }
+  {
+    const long tab[]={
+    24, 173, 301, 99, 125, 113, 101, 97, 85, 161, 133, 189, 67, 87, 73, 105, -1,
+    36, 255, 671, 265, 219, 427, 243, 147, 275, 115, 127, 121, 159, 111, 175,
+       -1,
+    40, 391, 903, 263, 311, 291, 271, 227, 207, 483, 399, 567, 163, 187, 315,
+       -1,
+    56, 697, 1849, 585, 557, 529, 413, 385, 989, 817, 1161, 351, 357, 645, -1,
+    60, 945, 721, 561, 1617, 211, 497, 337, 373, 651, 581, 693, 501, 1029, -1,
+    70, 1333, 1197, 973, 2709, -1,
+    75, 3647, 271, 847, -1,
+    84, 647, 935, 1935, 1295, 1071, 3311, 451, 699, 595, 1333, 469, 1099, 1419,
+        987, 2107, -1,
+    88, 1573, 4773, 1397, 1353, 1309, 953, 909, 2553, 2109, 2997, 865, 1665, -1,
+    90, 1659, 1891, 1371, 3843, 775, 1407, 735, 903, 615, 1575, -1,
+    104, 2143, 6751, 991, 1935, 1883, 1831, 1307, 1255, 3611, 2983, 4239, 731,
+         1203, 2355, -1,
+    105, 1785, 6321, -1,
+    110, 793, 993, 3441, 2793, 2441, 6993,-1,
+    126, 1533, 2037, 3397, 3477, 2749, 7869, 777, 937, 721, 1281, 1425, 2881,
+         1369, 1849, 1201, 3225, -1,
+      -1};
+      long i;
+      const long *t;
+      for(t=tab;*t!=-1;t++)
+      {
+        if (t[0]==n)
+        {
+          for(i=1; t[i] != -1; i++)
+            if (t[i]==s) return i;
+          return -1;
+        }
+        while (*t>=0) t++;
+      }
+  }
+  {
+    const long tab[]={
+      16, 1710031, 550075, 70099, 70075, 870059, 110059, 30079, 30071, 30063,
+470131, 70155, 70107, 110115, 310307, -1,
+      32, 6830063, 150323, 1830171, /*230171*/0, 230227, 30283, 30251, 30203,
+/*70267*/0, 70219, 110227, /*230171*/0, /*70187*/0, /*70187*/0, 110155,
+3430123, 430123, 30191, 30175, 30159, 1110387, 150563, 150387, 230323, 230411,
+230299, 70619, 70467, 70355, /*70379*/0, /*70379*/0, /*70267*/0, 70291, 70555,
+70331, 1750291, 230291, 430275, 70411, 70363, 70315, 110331, 30371, 30323,
+950819, 150995, 150643, 230691, 30747, 30635, 632451, -1,
+      48, 430156, 11970124, 10219, 430324, 110324, 30396, 30444, 30348, 230300,
+110300, 230396, /*70396*/0, /*70396*/0, 70540, 30412, 30364, 30380, 30332,
+70492, 3850300, 490396, 490300, 6090236, 770236, 210316, 210284, 210252, 30299,
+30371, 30363, 110299, 70311, 110271, 70588, 230732, 70876, 110636, 30868,
+30628, 30596, 30644, 150684, 70828, 3290524, 490620, 490428, 770460, 30627,
+70571, 10643, 151740, 2171228, -1,
+      54, 10256, 16410120, 70292, 610232, 10373, 10292, 10616, 70517, 5610228,
+210309, 210228, 250448, 70616, 11696, 2370552, -1,
+      /*64*/
+      72, 110307, 26230195, 210272, 30523, 110631, 30739, 70575, 30683,
+14030351, 1830403, 1830299, 770346, 110586, 10750330, 10620, 210420, 71223,
+9150663, 30426, 30858, 30978, 30954, 31074, 30762, 210452, 210538, 770634,
+210730, 490626, 210722, 31018, 111234, 31450, 71106, 31322, 5750594, 750682,
+750506, 10346, 10826, 10490, 70620, 11124, 10716, 30786, 31746, 210636, 491202,
+72402, 3751122, -1,
+      80, 430250, 35910186, 110282, 430530, 110530, 30650, 30730, 30570,
+230482, 110482, 230642, /*70642*/0, /*70642*/0, 70882, 30666, 30586, 30618,
+30538, 70786, 11550450, 1470594, 1470450, 18270354, 2310354, 630474, 630426,
+630378, 110562, 30562, 110722, 30722, 70546, 30546, 30946, 70962, 231202,
+71442, 111042, 31426, 31026, 30978, 31058, 151106, 71346, 9870786, 1470930,
+1470642, 2310690, 10467, 71266, 152866, 6511842, -1,
+      81,49210121, 6730319, 250427, 250319, 16450238, 610238, 70454, 70346,
+70400, 70319, 5650670, 250994, 250670, 610589, 2412452, -1,
+      /*96*/
+      100, 30393, 57310217, 10481, 30693, 36470341, 630408, 30968, 13310392,
+210416, 10576, 11256, 10856, 11096, 630648, 31768, 8470616, -1,
+      108, 30552, 60170280, 610359, 30984, 38290440, 210660, 1830540, 30849,
+30660, 31308, 211137, 20570532, 770721, 770532, 70769, 11636, 11813, 610647,
+70647, 250674, 70674, 70890, 211092, 1830852, 31389, 31092, 32388, 211965,
+13090836, 491133, 490836, 751080, 211416, 33576, 8691288, 70904, 11048, 71720,
+13688, 12344, 251538, 751608, 212280, 36600, 5532024,-1,
+      112, 430344, 73530248, 430736, 110736, 30904, 31016, 30792, 230664,
+110664, 230888, 0/*70888*/, 0/*70888*/, 71224, 30920, 30808, 30856, 30744,
+71080, 23650600, 3010792, 3010600, 37410472, 4730472, 1290632, 1290568,
+1290504, 71336, 231672, 72008, 111448, 31984, 31424, 31360, 31472, 151528,
+71864, 20211048, 3011240, 3010856, 4730920, 30643, 153992, 13332456,-1,
+      120,2310456, 770488, 110648, 63210360, 30763, 210552, 30712, 31256,
+31240, 31336, 31400, 31496, 31480, 31096, 630498, 210808, 770968, 211128,
+490904, 211064, 630744, 2310888, 631032, 1470840, 630984, 31128, 111368, 31608,
+71224, 31464, 33810648, 4410744, 4410552, 11211, 31263, 11416, 210858, 11290,
+11090, 211032, 31352, 32600, 630738, 491864, 1471704, 72664, 22051224, -1,
+      -1};
+    long i;
+    const long *t;
+    GEN Z = groupelts_center(S), L = group_subgroups(G);
+    long scenter = groupelts_sumorders(Z), svecgroup = vecgroup_sumorders(L);
+    long u = svecgroup+10000*scenter; /*This is used as a hash value*/
+
+    for(t=tab; *t!=-1; t++)
+    {
+      if (t[0]==n)
+      {
+        for(i=1; t[i] != -1; i++)
+          if (t[i]==u) return i;
+        switch(n)
+        {
+        case 32:
+          switch(u)
+          {
+          case 230171:
+            {
+              const long good[]={2,0}, bad[]={4,5,0};
+              return indexgroupcentre(G,Z,good,bad)? 4: 12;
+            }
+          case 70267:
+            if (s==135) return 9;
+            return 32;
+          case 70187:
+            {
+              const long good[]={8,0}, bad[]={7,9,0};
+              return indexgroupcentre(G,Z,good,bad)? 13: 14;
+            }
+          case 70379:
+            {
+              const long good[]={4,0},bad[]={0};
+              return indexgroupsubgroup(L,8,good,bad)? 31: 30;
+            }
+          }
+          break;
+        case 48: case 80:
+          {
+            const long good[]={5,8,0},bad[]={6,7,0};
+            return indexgroupcentre(G,Z,good,bad)? 12: 13;
+          }
+        case 112:
+          {
+            const long good[]={7,4,0},bad[]={5,6,0};
+            return indexgroupcentre(G,Z,good,bad)? 11: 12;
+          }
+        }
+        return -1;
+      }
+      while (*t!=-1) t++;
+    }
+    {
+      const long tab[]={ 64, 1270001, /*4270003*/0, /*4270003*/0, 8190072,
+ 6430073, 6670445, 5550446, 8190278, 7070279, 6350071, 5230072, 8110154,
+ /*5870155*/0, /*5870155*/0, /*4270042*/0, /*4270042*/0, 7710246, 7390277,
+ 6750037, 8032377, 8670266, 6750397, 11390022, 7710267, 7070277, /*3630046*/0,
+ /*3630046*/0, 3630057, 4830196, 4830207, 4671808, 9070697, 6670700, 8750094,
+ 6990091, 6350111, 5870115, 6671599, 5711601, 5551702, 5871512, 6351709,
+ 5391711, /*3630046*/0, 3630047, 4111467, /*4430156*/0, /*4430156*/0, 3790166,
+ /*2510026*/0, /*2510026*/0, 4470028, 4150300, 3830030, 13470021, 20350065,
+ 10910041, 16514365, /*12190433*/0, 34110271, /*16514475*/0, 15230465,
+ /*10910433*/0, 9630041, 13470233, /*16514475*/0, 20834696, /*10910433*/0,
+ 13954343, /*12190433*/0, 19553542, 13474377, 25790466, 15870467, 18914476,
+ 14110477, /*14590443*/0, 13310443, 11550043, /*14590443*/0, 10270043, 8990002,
+ 8990546, 8990646, 8993647, 8356896, 13310905, 13310915, 12039018, 16990866,
+ 12670888, 15071116, 11551217, 12038218, 16031739, 12512740, 12353138, 12993048,
+ 15391849, 11872850, 12993551, 12353851, 8991446, 8991447, 8354830, 9951566,
+ 9951666, 8674709, 9317039, 8031897, 8030187, 7713641, 7714641, 7074743,
+ 9236585, 9236415, 9236586, 10990821, 9879066, 8751833, 9873399, 8751766,
+ 10990754, 8593054, 8593087, 6830446, 6833546, 17472434, 13953445, 14432313,
+ 16352544, 12833555, 13313423, 15635143, 13234877, 13874853, 12755287, 17870919,
+ 14190949, 13075209, 11955310, 10835253, 9715354, 11312124, 10193135, 11074927,
+ 12197529, 9957664, 11074970, 12196539, 9956674, 9958907, 10439497, 9479551,
+ 9554015, 8433958, 9553915, 8434058, 8918081, 7798421, 10110856, 10110756,
+ 9476648, 8991757, 8991857, 8356682, 10994275, 8750435, 8590474, 9230510,
+ 10355254, 8115355, 13556790, 15790679, 11310691, 12275539, 14035061, 11795172,
+ 8750465, 7474472, 8750475, 8114920, 6110196, 6111806, 5951808, 10191819,
+ 9238364, 8271841, 8590736, 9390959, 8432079, 25470255, 41792701, 25470275,
+ 20355344, 27233751, 18593673, 19717567, 23394762, 17312707, 19717633, 46115277,
+ 31557088, 22917189, 24677288, 24039835, 24676366, 16032362, 17793529, 17153269,
+ 38432486, 21153763, 23393635, 16037076, 27710971, 27074338, 20995174, 23396204,
+ 20193482, 17157790, 19550231, 14751475, 17153832, 19070249, 16038080, 33391110,
+ 25875097, 22197835, 22195018, 21070221, 24590112, 18999456, 15952565, 18356361,
+ 17237769, 18359003, 15951169, 14832955, 16110295, 18350268, 21392354, 22030301,
+ 18353365, 15955257, 13550032, 18430405, 18434015, 17150260, 17154128, 27234036,
+ 23710639, 20194057, 21157900, 24198470, 20679613, 21158141, 22435065, 21318520,
+ 20197076, 67390501, 83715011, 51070497, 54590283, 58915129, 50275230, 52035340,
+ 263870051, -1,
+      -1};
+      long i;
+      const long *t;
+      GEN V=vecgroup_idxlist(L,32);
+      long idxlist=vecsmall_pack(V,10,9967);
+      long w=10000*svecgroup+idxlist; /*This is used as a hash value*/
+      for(t=tab; *t!=-1; t++)
+      {
+        if (t[0]==n)
+        {
+          for(i=1; t[i] != -1; i++)
+            if (t[i]==w) return i;
+          switch(n)
+          {
+          case 64:
+            switch(w)
+            {
+            case 4270003:
+              return (scenter==439)? 2: 3;
+            case 5870155:
+              {
+                const long good[]={8,9,0},bad[]={7,0};
+                return indexgroupcentre(G,Z,good,bad)? 13: 14;
+              }
+            case 4270042:
+              {
+                const long good[]={13,0},bad[]={14,0};
+                return indexgroupcentre(G,Z,good,bad)? 15: 16;
+              }
+            case 4430156:
+              {
+                const long good[]={18,20,0},bad[]={19,0};
+                return indexgroupcentre(G,Z,good,bad)? 47: 48;
+              }
+            case 2510026:
+              return scenter==1367? 50: 51;
+            case 12190433:
+              return scenter==47? 59: 70;
+            case 16514475:
+              {
+                const long good[]={22,24,28,0},bad[]={23,25,27,30,0};
+                return indexgroupcentre(G,Z,good,bad)? 61: 66;
+              }
+            case 10910433:
+              {
+                const long good[]={23,31,0},bad[]={25,26,29,30,33,0};
+                return indexgroupcentre(G,Z,good,bad)? 63: 68;
+              }
+            case 14590443:
+              {
+                const long good[]={28,33,0},bad[]={30,34,0};
+                return indexgroupcentre(G,Z,good,bad)? 77: 80;
+              }
+            case 3630046:
+              {
+                const long good[]={3,0},bad[]={12,16,0};
+                if (scenter==695) return 26;
+                return indexgroupcentre(G,Z,good,bad)? 27: 44;
+              }
+            }
+            break;
+          }
+          return -1;
+        }
+        while (*t!=-1) t++;
+      }
+    }
+    {
+      const long tab[]={ 96, 316010002, 252010002, 707020000, 676160124,
+    676170124, 87180027, 988190278, 892200028, 876030110, 876040110, 876120110,
+    215111237, 503062153, 972141052, 972131052, 455091156, 167101154, 620160033,
+    620170033, 908031033, 908121033, 908041033, 199101564, 7130153, 7140153,
+    812150123, 247051165, 487091566, 812151024, 391071276, 103081274, 247111377,
+    988180195, 892190205, 924190197, 828200207, 103050134, 679020464, 295091075,
+    199070145, 391060235, 103101076, 7080146, 135111157, 295020044, 684030223,
+    684040223, 908050274, 135060255, 7070285, 812080286, 71092475, 876102476,
+    908112287, 684120223, 748130243, 748140243, 620150254, 492160043, 492170043,
+    764180045, 700190477, 636200047, 963110003, 779050031, 935100032, 799110033,
+    819210003, 791260032, 246270050, 723330003, 987340003, 651360031, 623380033,
+    647263930, 839351534, 455321350, 178211335, 791244031, 322256848, 189340236,
+    130316409, 599331360, 743244548, 935295937, 551333907, 189222029, 274255883,
+    525275609, 82306043, 610289391, 82315641, 82307025, 647262487, 839353950,
+    0/*455322385*/, 0/*455322385*/, 178233588, 791245051, 322256982, 130307015,
+    658286619, 983297004, 983297037, 599333858, 631365165, 631376165, 535386399,
+    66408676, 354390966, 871428265, 775411064, 631376407, 535386309, 114430028,
+    823441008, 314398920, 74437993, 831420054, 42405827, 90439425, 799440830,
+    847426805, 767410275, 815440314, 863429143, 487360134, 487371044, 66211564,
+    66231664, 871295713, 66231764, 679242567, 125228724, 210253894, 18306803,
+    546288536, 162390938, 919437378, 871401018, 162255761, 967304398, 967313318,
+    413274329, 498283470, 498288163, 29345108, 967401139, 727449579, 679411219,
+    775352619, 583261276, 919295225, 66312839, 423381047, 2437470, 759424560,
+    711448550, 770222372, 109272382, 551210244, 258222592, 551230264, 295242430,
+    647254411, 199262266, 482272602, 871283751, 423293752, 519303751, 519312662,
+    71320222, 167332232, 226340245, 327352266, 167360274, 167372584, 103382587,
+    647392595, 455406162, 263412616, 327428742, 487438955, 295440098, 358290331,
+    622253358, 886280358, 322410312, 754400322, 394443122, 282440313, 354423123,
+    522430323, 726220349, 990273529, 470450359, 742460359, 522470032, 198470031,
+    282480353, 290490033, 274500353, 414470000, 614490000, 605473864, 664459790,
+    723464091, 893482714, 675465704, 845486215, 184493728, 653478045, 941489155,
+    605501588, 925482982, 264492577, 589502601, 312450472, 371466994, 285450492,
+    989464902, 578470486, 770489139, 994490497, 546500507, 604460529, 65270050,
+    684510049, 468510050, 134510562, 831510052, -1
+        -1};
+      long i;
+      const long *t;
+      GEN V=vecgroup_idxlist(L,48);
+      long idx48=vecsmall_pack(V,10,9967);
+      long idx32=vecgroup_idxlist(L,32)[1];
+      long w=1000000*(svecgroup%997)+10000*idx32+idx48;
+      /*This is used as a hash value*/
+      for(t=tab; *t!=-1; t++)
+      {
+        if (t[0]==n)
+        {
+          for(i=1; t[i] != -1; i++)
+            if (t[i]==w) return i;
+          switch(n)
+          {
+          case 96:
+            switch(w)
+            {
+            case 455322385:
+              {
+                const long good[]={37,40,0},bad[]={34,41,0};
+                return indexgroupcentre(G,Z,good,bad)? 96: 97;
+              }
+            }
+            break;
+          }
+          return -1;
+        }
+        while (*t!=-1) t++;
+      }
+    }
+  }
+  return 0;
+}
+
+long
+group_ident(GEN G, GEN S)
+{
+  pari_sp av = avma;
+  long idx = group_ident_i(G, S);
+  if (idx < 0) pari_err_TYPE("group_ident [not a group]", G);
+  if (!idx) pari_err_IMPL("galoisidentify for groups of order > 127");
+  avma = av; return idx;
+}
+
+long
+group_ident_trans(GEN G, GEN S)
+{
+  const long tab[]={
+        4, 1, 2, -1,
+        6, 2, 1, -1,
+        8, 1, 2, 4, 5, 3, -1,
+        9, 1, 2, -1,
+        10, 2, 1, -1,
+        12, 5, 1, 4, 3, 2, -1,
+        14, 2, 1, -1,
+        15, 1, -1,
+        16, 1, 4, 10, 8, 5, 6, 13, 12, 14, 2, 9, 7, 11, 3, -1,
+        18, 5, 1, 3, 4, 2, -1,
+        20, 2, 1, 5, 4, 3, -1,
+        21, 2, 1, -1,
+        22, 2, 1, -1,
+        24, 8, 1, 7, 5, 12, 13, 6, 14, 2, 15, 4, 10, 9, 11, 3, -1,
+        25, 1, 2, -1,
+        26, 2, 1, -1,
+        27, 1, 2, 3, 5, 4, -1,
+        28, 3, 1, 4, 2, -1,
+        30, 2, 4, 3, 1, -1,
+        -1};
+  long n = group_order(G), s;
+  const long *t;
+  if (n == 1) return 1;
+  /* N.B. known up to 32 (Cannon-Holt) */
+  if (n > 30) pari_err_IMPL("group_ident_trans [n > 30]");
+  if (uisprime(n)) return 1;
+  s = group_ident(G,S);
+  for(t=tab;*t>=0;t++)
+  {
+    if (t[0]==n) return t[s];
+    while (*t>=0) t++;
+  }
+  return 0; /*NOT REACHED*/
+}
diff --git a/src/modules/krasner.c b/src/modules/krasner.c
new file mode 100644
index 0000000..1190f73
--- /dev/null
+++ b/src/modules/krasner.c
@@ -0,0 +1,957 @@
+/* Copyright (C) 2009  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/************************************************************/
+/*     Computation of all the extensions of a given         */
+/*               degree of a p-adic field                   */
+/* Xavier Roblot                                            */
+/************************************************************/
+
+#undef CHECK_EXTENSIONS
+
+/* cf. Math. Comp, vol. 70, No. 236, pp. 1641-1659 for more details.
+   Note that n is now e (since the e from the paper is always = 1) and l
+   is now f */
+/* Structure for a given type of extension */
+typedef struct {
+  GEN p;
+  long e, f, j;
+  long a, b, c;
+  long v;     /* auxiliary variable */
+  long r;     /* pr = p^r */
+  GEN pr;     /* p-adic precision for poly. reduction */
+  GEN q, qm1; /* p^f, q - 1 */
+  GEN upl;    /* cyclotomic polynomial (in v) generating K^ur (mod pr) */
+  GEN mv;     /* -v mod upl (mod pr) */
+  GEN uplr;   /* upl reduced mod p */
+  GEN frob;   /* Frobenius acting of the root of upl (mod pr) */
+  GEN nbext;  /* number of extensions */
+  GEN roottable; /* table of roots of polynomials over the residue field */
+  GEN pk;     /* powers of p: [p^1, p^2, ..., p^c] */
+} KRASNER_t;
+
+/* Structure containing the field data (constructed with FieldData) */
+typedef struct {
+  GEN p;
+  GEN top;  /* absolute polynomial with root zq + pi (mod pr) */
+  GEN topr; /* absolute polynomial with root zq + pi (mod p) */
+  GEN eis;  /* relative polynomial with root pi (mod pr) (y=zq) */
+  GEN zq;   /* (q-1)-th root of unity (root of upl) (mod pr) (y=zq+pi) */
+  GEN pi;   /* prime element (mod p) (y=zq+pi)*/
+  GEN ipi;  /* p/pi (mod pr) (y=zq+pi) (used to divide by pi) */
+  GEN pik;  /* [1, pi, ..., pi^(e-1), pi^e / p] (mod pr). Note the last one ! */
+  long cj;  /* number of conjugate fields */
+} FAD_t;
+
+static long
+ceildiv(ulong a, ulong b)
+{
+  long c = a/b;
+  if (a%b) c++;
+  return c;
+}
+
+/* Eval P(x) assuming P has positive coefficients and the result is a long */
+static ulong
+ZX_z_eval(GEN P, ulong x)
+{
+  long i, l = lg(P);
+  ulong z;
+
+  if (typ(P) == t_INT) return itou(P);
+  if (l == 2) return 0;
+  z = itou(gel(P, l-1));
+  for (i = l-2; i > 1; i--) z = z*x + itou(gel(P,i));
+  return z;
+}
+
+/* Eval P(x, y) assuming P has positive coefficients and the result is a long */
+static ulong
+ZXY_z_eval(GEN P, ulong x, ulong y)
+{
+  long i, l = lg(P);
+  ulong z;
+
+  if (l == 2) return 0;
+  z = ZX_z_eval(gel(P, l-1), y);
+  for (i = l-2; i > 1; i--) z = z*x + ZX_z_eval(gel(P, i), y);
+  return z;
+}
+
+/* P an Fq[X], where Fq = Fp[Y]/(T(Y)), a an FpX representing the automorphism
+ * y -> a(y). Return a(P), applying a() coefficientwise. */
+static GEN
+FqX_FpXQ_eval(GEN P, GEN a, GEN T, GEN p)
+{
+  long i, l;
+  GEN Q = cgetg_copy(P, &l);
+
+  Q[1] = P[1];
+  for (i = 2; i < l; i++)
+  {
+    GEN cf = gel(P, i);
+    if (typ(cf) == t_POL) {
+      switch(degpol(cf))
+      {
+        case -1: cf = gen_0; break;
+        case 0:  cf = gel(cf,2); break;
+        default:
+          cf = FpX_FpXQ_eval(cf, a, T, p);
+          cf = simplify_shallow(cf);
+          break;
+      }
+    }
+    gel(Q, i) = cf;
+  }
+
+  return Q;
+}
+
+/* Sieving routines */
+static GEN
+InitSieve(long l) { return zero_F2v(l); }
+static long
+NextZeroValue(GEN sv, long i)
+{
+  for(; i <= sv[1]; i++)
+    if (!F2v_coeff(sv, i)) return i;
+  return 0; /* sieve is full or out of the sieve! */
+}
+static void
+SetSieveValue(GEN sv, long i)
+{ if (i >= 1 && i <= sv[1]) F2v_set(sv, i); }
+
+/* return 1 if the data verify Ore condition and 0 otherwise */
+static long
+VerifyOre(GEN p, long e, long j)
+{
+  long nv, b, vb, nb;
+
+  if (j < 0) return 0;
+  nv = e * u_pval(e, p);
+  b  = j%e;
+  if (b == 0) return (j == nv);
+  if (j > nv) return 0;
+  /* j < nv */
+  vb = u_pval(b, p);
+  nb = vb*e;
+  return (nb <= j);
+}
+
+/* Given [K:Q_p] = m and disc(K/Q_p) = p^d, return all decompositions K/K^ur/Q_p
+ * as [e, f, j] with
+ *   K^ur/Q_p unramified of degree f,
+ *   K/K^ur totally ramified of degree e, and discriminant p^(e+j-1);
+ * thus d = f*(e+j-1) and j > 0 iff ramification is wild */
+static GEN
+possible_efj_by_d(GEN p, long m, long d)
+{
+  GEN rep, div;
+  long i, ctr, l;
+
+  if (d == 0) return mkvec(mkvecsmall3(1, m, 0)); /* unramified */
+  div = divisorsu(ugcd(m, d));
+  l = lg(div); ctr = 1;
+  rep = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    long f = div[i], e = m/f, j = d/f - e + 1;
+    if (VerifyOre(p, e, j)) gel(rep, ctr++) = mkvecsmall3(e, f, j);
+  }
+  setlg(rep, ctr); return rep;
+}
+
+/* return the number of extensions corresponding to (e,f,j) */
+static GEN
+NumberExtensions(KRASNER_t *data)
+{
+  ulong pp, pa;
+  long e, a, b;
+  GEN p, q, s0, p1;
+
+  e = data->e;
+  p = data->p;
+  q = data->q;
+  a = data->a; /* floor(j/e) <= v_p(e), hence p^a | e */
+  b = data->b; /* j % e */
+  if (is_bigint(p)) /* implies a = 0 */
+    return b == 0? utoipos(e): mulsi(e, data->qm1);
+
+  pp = p[2];
+  switch(a)
+  {
+    case 0:  pa = 1;  s0 = utoipos(e); break;
+    case 1:  pa = pp; s0 = mului(e, powiu(q, e / pa)); break;
+    default:
+      pa = upowuu(pp, a); /* p^a */
+      s0 = mulsi(e, powiu(q, (e / pa) * ((pa - 1) / (pp - 1))));
+  }
+  /* s0 = e * q^(e*sum(p^-i, i=1...a)) */
+  if (b == 0) return s0;
+
+  /* q^floor((b-1)/p^(a+1)) */
+  p1 = powiu(q, sdivsi(b-1, muluu(pa, pp)));
+  return mulii(mulii(data->qm1, s0), p1);
+}
+
+static GEN
+get_topx(KRASNER_t *data, GEN eis)
+{
+  GEN p1, p2, rpl;
+  long j;
+  pari_sp av, lim;
+  /* top poly. is the minimal polynomial of root(pol) + root(upl) */
+  rpl = FqX_translate(FqX_red(eis, data->upl, data->pr),
+                      data->mv, data->upl, data->pr);
+  p1 = p2 = rpl;
+  av = avma; lim = stack_lim(av, 1);
+  for (j = 1; j < data->f; j++)
+  {
+    p1 = FqX_FpXQ_eval(p1, data->frob, data->upl, data->pr);
+    p2 = FqX_mul(p2, p1, data->upl, data->pr);
+    if (low_stack(lim, stack_lim(av, 1))) gerepileall(av, 2, &p1, &p2);
+  }
+  return simplify_shallow(p2); /* ZX */
+}
+
+/* eis (ZXY): Eisenstein polynomial  over the field defined by upl.
+ * topx (ZX): corresponding absolute equation.
+ * Return the struct FAD corresponding to the field it defines (GENs created
+ * as clones). Assume e > 1. */
+static void
+FieldData(KRASNER_t *data, FAD_t *fdata, GEN eis, GEN topx)
+{
+  GEN p1, zq, ipi, cipi, dipi, t, Q;
+
+  fdata->p = data->p;
+  t = leafcopy(topx); setvarn(t, data->v);
+  fdata->top  = t;
+  fdata->topr = FpX_red(t, data->pr);
+
+  zq  = pol_x(data->v);
+  /* FIXME: do as in CycloPol (not so easy) */
+  for(;;)
+  {
+    GEN zq2 = zq;
+    zq = Fq_pow(zq, data->q, fdata->top, data->pr);
+    if (gequal(zq, zq2)) break;
+  }
+  fdata->zq  = zq;
+  fdata->eis = eis;
+  fdata->pi  = Fq_sub(pol_x(data->v), fdata->zq,
+                      FpX_red(fdata->top, data->p), data->p);
+  ipi = RgXQ_inv(fdata->pi, fdata->top);
+  ipi = Q_remove_denom(ipi, &dipi);
+  Q = mulii(data->pr, data->p);
+  cipi = Fp_inv(diviiexact(dipi, data->p), Q);
+  fdata->ipi = FpX_Fp_mul(ipi, cipi, Q); /* p/pi mod p^(pr+1) */
+
+  /* Last one is in fact pi^e/p */
+  p1 = FpXQ_powers(fdata->pi, data->e, fdata->topr, data->pr);
+  gel(p1, data->e+1) = ZX_Z_divexact(gel(p1, data->e+1), data->p);
+  fdata->pik  = p1;
+}
+
+/* return pol*ipi/p (mod top, pp) if it has integral coefficient, NULL
+   otherwise. The result is reduced mod top, pp */
+static GEN
+DivideByPi(FAD_t *fdata, GEN pp, GEN ppp, GEN pol)
+{
+  GEN P;
+  long i, l;
+  pari_sp av = avma;
+
+  /* "pol" is a t_INT or an FpX: signe() works for both */
+  if (!signe(pol)) return pol;
+
+  P = Fq_mul(pol, fdata->ipi, fdata->top, ppp); /* FpX */
+  l  = lg(P);
+  for (i = 2; i < l; i++)
+  {
+    GEN r;
+    gel(P,i) = dvmdii(gel(P,i), fdata->p, &r);
+    if (r != gen_0) { avma = av; return NULL; }
+  }
+  return FpX_red(P, pp);
+}
+
+/* return pol# = pol~/pi^vpi(pol~) mod pp where pol~(x) = pol(pi.x + beta)
+ * has coefficients in the field defined by top and pi is a prime element */
+static GEN
+GetSharp(FAD_t *fdata, GEN pp, GEN ppp, GEN pol, GEN beta, long *pl)
+{
+  GEN p1, p2;
+  long i, v, l, d = degpol(pol);
+  pari_sp av = avma;
+
+  if (!gequal0(beta))
+    p1 = FqX_translate(pol, beta, fdata->topr, pp);
+  else
+    p1 = shallowcopy(pol);
+  p2 = p1;
+  for (v = 0;; v++)
+  {
+    for (i = 0; i <= v; i++)
+    {
+      GEN c = gel(p2, i+2);
+      c = DivideByPi(fdata, pp, ppp, c);
+      if (!c) break;
+      gel(p2, i+2) = c;
+    }
+    if (i <= v) break;
+    p1 = shallowcopy(p2);
+  }
+  if (!v) pari_err_BUG("GetSharp [no division]");
+
+  for (l = v; l >= 0; l--)
+  {
+    GEN c = gel(p1, l+2);
+    c = DivideByPi(fdata, pp, ppp, c);
+    if (!c) { break; }
+  }
+
+  *pl = l; if (l < 2) return NULL;
+
+  /* adjust powers */
+  for (i = v+1; i <= d; i++)
+    gel(p1, i+2) = Fq_mul(gel(p1, i+2),
+                          gel(fdata->pik, i-v+1), fdata->topr, pp);
+
+  return gerepilecopy(av, normalizepol(p1));
+}
+
+#ifdef CHECK_EXTENSIONS
+static void
+PrintValuations(GEN pol, GEN mod, GEN p)
+{
+  long i, d = degpol(pol);
+  for (i = 0; i <= d; i++)
+    err_printf("%d ", Z_pval(RgXQ_norm(gel(pol, i+2), mod), p));
+}
+
+/* Return the degree of pol mod the prime ideal of top */
+static long
+DegreeMod(FAD_t *fdata, GEN pp, GEN ppp, GEN pol)
+{
+  long d = degpol(pol); /* should be > 0 */
+  pari_sp av = avma;
+
+  do
+  {
+    GEN c = gel(pol, d+2);
+    if (!gequal0(c) && !DivideByPi(fdata, pp, ppp, c))
+      return d;
+  }
+  while (--d >= 1);
+  avma = av; return 0;
+}
+#endif
+
+/* Compute roots of pol in the residue field. Use table look-up if possible */
+static GEN
+Quick_FqX_roots(KRASNER_t *data, GEN pol)
+{
+  GEN rts;
+  ulong ind = 0;
+
+  pol = FpXQX_red(pol, data->uplr, data->p);
+
+  if (data->roottable)
+  {
+    ind = ZXY_z_eval(pol, data->q[2], data->p[2]);
+    if (gel(data->roottable, ind)) return gel(data->roottable, ind);
+  }
+  rts = FqX_roots(pol, data->uplr, data->p);
+
+  if (ind) gel(data->roottable, ind) = gclone(rts);
+  return rts;
+}
+
+static void
+FreeRootTable(GEN T)
+{
+  if (T)
+  {
+    long j, l = lg(T);
+    for (j = 1; j < l; j++)
+      if (gel(T,j)) gunclone(gel(T,j));
+  }
+}
+
+/* Return the number of roots of pol congruent to alpha modulo pi working
+   modulo pp (all roots if alpha is NULL); if flag is non-zero, return 1
+   as soon as a root is found. (Note: ppp = pp*p for DivideByPi) */
+static long
+RootCongruents(KRASNER_t *data, FAD_t *fdata, GEN pol, GEN alpha, GEN pp, GEN ppp, long sl, long flag)
+{
+  GEN R;
+  long s, i;
+
+  if (alpha)
+  { /* FIXME: the data used in GetSharp is not reduced */
+    long l;
+    pol = GetSharp(fdata, pp, ppp, pol, alpha, &l);
+    if (l <= 1) return l;
+#ifdef CHECK_EXTENSIONS
+    if (l != DegreeMod(fdata, pp, ppp, pol))
+      pari_err_BUG("RootCongruents [degree mismatch in RCA]");
+#endif
+    /* decrease precision if sl gets bigger than a multiple of e */
+    sl += l;
+    if (sl >= data->e)
+    {
+      sl -= data->e;
+      ppp = pp;
+      pp = diviiexact(pp, data->p);
+    }
+  }
+
+  R  = Quick_FqX_roots(data, pol);
+  for (s = 0, i = 1; i < lg(R); i++)
+  {
+    s += RootCongruents(data, fdata, pol, gel(R, i), pp, ppp, sl, flag);
+    if (flag && s) return 1;
+  }
+  return s;
+}
+
+/* pol is a ZXY defining a polynomial over the field defined by fdata
+   If flag != 0, return 1 as soon as a root is found. Precision are done with
+   a precision of pr. */
+static long
+RootCountingAlgorithm(KRASNER_t *data, FAD_t *fdata, GEN pol, long flag)
+{
+  long j, l, d;
+  GEN P = cgetg_copy(pol, &l);
+
+  P[1] = pol[1];
+  d = l-3;
+  for (j = 0; j < d; j++)
+  {
+    GEN cf = gel(pol, j+2);
+    if (typ(cf) == t_INT)
+      cf = diviiexact(cf, data->p);
+    else
+      cf = ZX_Z_divexact(cf, data->p);
+    gel(P, j+2) = Fq_mul(cf, gel(fdata->pik, j+1), fdata->topr, data->pr);
+  }
+  gel(P, d+2) = gel(fdata->pik, d+1); /* pik[d] = pi^d/p */
+
+  return RootCongruents(data, fdata, P, NULL, diviiexact(data->pr, data->p), data->pr, 0, flag);
+}
+
+/* Return non-zero if the field given by fdata defines a field isomorphic to
+ * the one defined by pol */
+static long
+IsIsomorphic(KRASNER_t *data, FAD_t *fdata, GEN pol)
+{
+  long j, nb;
+  pari_sp av = avma;
+
+  if (RgX_is_ZX(pol)) return RootCountingAlgorithm(data, fdata, pol, 1);
+
+  for (j = 1; j <= data->f; j++)
+  {
+    GEN p1 = FqX_FpXQ_eval(pol, fdata->zq, fdata->top, data->pr);
+    nb = RootCountingAlgorithm(data, fdata, p1, 1);
+    if (nb) { avma = av; return nb; }
+    if (j < data->f)
+      pol = FqX_FpXQ_eval(pol, data->frob, data->upl, data->pr);
+  }
+  avma = av; return 0;
+}
+
+/* Compute the number of conjugates fields of the field given by fdata */
+static void
+NbConjugateFields(KRASNER_t *data, FAD_t *fdata)
+{
+  GEN pol = fdata->eis;
+  long j, nb;
+  pari_sp av = avma;
+
+  if (RgX_is_ZX(pol)) { /* split for efficiency; contains the case f = 1 */
+    fdata->cj = data->e / RootCountingAlgorithm(data, fdata, pol, 0);
+    avma = av; return;
+  }
+
+  nb = 0;
+  for (j = 1; j <= data->f; j++)
+  {
+    GEN p1 = FqX_FpXQ_eval(pol, fdata->zq, fdata->top, data->pr);
+    nb += RootCountingAlgorithm(data, fdata, p1, 0);
+    if (j < data->f)
+      pol = FqX_FpXQ_eval(pol, data->frob, data->upl, data->pr);
+  }
+  avma = av;
+  fdata->cj = data->e * data->f / nb;
+  return;
+}
+
+/* return a minimal list of polynomials generating all the totally
+   ramified extensions of K^ur of degree e and discriminant
+   p^{e + j - 1} in the tamely ramified case */
+static GEN
+TamelyRamifiedCase(KRASNER_t *data)
+{
+  long av = avma, g;
+  GEN rep, p2, topx, m, eis, Xe = gpowgs(pol_x(0), data->e);
+
+#ifdef CHECK_EXTENSIONS
+  FAD_t fdata;
+  long cnt = 0, nb, j;
+  GEN vpl;
+  err_printf("Number of extensions: %ld\n", itos(data->nbext));
+#endif
+
+  g   = ugcd(data->e, umodiu(data->qm1, data->e)); /* (e, q-1) */
+  m   = stoi(data->e/g);
+  rep = zerovec(g);
+
+  eis = gadd(Xe, data->p);
+  topx = get_topx(data, eis);
+  p2 = mkvec2(topx, m);
+  gel(rep, 1) = p2;
+#ifdef CHECK_EXTENSIONS
+  vpl = zerovec(g);
+  gel(vpl, 1) = eis;
+  if (data->e == 1)
+    nb = 1;
+  else
+  {
+    FieldData(data, &fdata, eis, topx);
+    NbConjugateFields(data, &fdata);
+    nb = fdata.cj;
+  }
+  err_printf("Found %ld field(s)\n", nb);
+  cnt += nb;
+#endif
+
+  if (g > 1)
+  {
+    ulong pmodg = umodiu(data->p, g);
+    long r = 1, ct = 1;
+    GEN sv = InitSieve(g-1);
+    while (r)
+    {
+      long gr;
+      GEN p1 = FpXQ_powu(pol_x(data->v), r, data->uplr, data->p);
+      eis = gadd(Xe, ZX_Z_mul(p1, data->p)); /* Adding a ZX and a ZY (cste coefficient) */
+      ct++;
+      topx = get_topx(data, eis);
+      p2 = mkvec2(topx, m);
+      gel(rep, ct) = p2;
+#ifdef CHECK_EXTENSIONS
+      gel(vpl, ct) = eis;
+      FieldData(data, &fdata, eis, topx);
+      for (j = 1; j < ct; j++)
+        if (IsIsomorphic(data, &fdata, gel(vpl, j)))
+          pari_err_BUG("TamelyRamifiedCase [isomorphic fields]");
+      NbConjugateFields(data, &fdata);
+      nb = fdata.cj;
+      err_printf("Found %ld field(s)\n", nb);
+      cnt += nb;
+#endif
+      gr = r;
+      do
+      {
+        SetSieveValue(sv, gr);
+        gr = Fl_mul(gr, pmodg, g);
+      }
+      while (gr != r);
+      r  = NextZeroValue(sv, r);
+    }
+    setlg(rep, ct+1);
+  }
+
+#ifdef CHECK_EXTENSIONS
+  if (!equaliu(data->nbext, cnt))
+    pari_err_BUG("TamelyRamifiedCase [incorrect #fields]");
+#endif
+
+  return gerepilecopy(av, rep);
+}
+
+static long
+function_l(GEN p, long a, long b, long i)
+{
+  long l = 1 + a - z_pval(i, p);
+  if (i < b) l++;
+  return (l < 1)? 1: l;
+}
+
+/* Structure of the coefficients set Omega. Each coefficient is [start, zr]
+ * meaning all the numbers of the form:
+ *   zeta_0 * p^start + ... + zeta_s * p^c (s = c - start)
+ * with zeta_i roots of unity (powers of zq + zero), zeta_0 = 0 is
+ * possible iff zr = 1 */
+static GEN
+StructureOmega(KRASNER_t *data, GEN *pnbpol)
+{
+  GEN nbpol, O = cgetg(data->e + 1, t_VEC);
+  long i;
+
+  /* i = 0 */
+  gel(O, 1) = mkvecsmall2(1, 0);
+  nbpol = mulii(data->qm1, powiu(data->q, data->c - 1));
+  for (i = 1; i < data->e; i++)
+  {
+    long v_start, zero = 0;
+    GEN nbcf, p1;
+    v_start = function_l(data->p, data->a, data->b, i);
+    p1 = powiu(data->q, data->c - v_start);
+    if (i == data->b)
+      nbcf = mulii(p1, data->qm1);
+    else
+    {
+      nbcf = mulii(p1, data->q);
+      zero = 1;
+    }
+    gel(O, i+1) = mkvecsmall2(v_start, zero);
+    nbpol = mulii(nbpol, nbcf);
+  }
+  *pnbpol = nbpol; return O;
+}
+
+/* a random element of the finite field */
+static GEN
+RandomFF(KRASNER_t *data)
+{
+  long i, l = data->f + 2, p = itou(data->p);
+  GEN c = cgetg(l, t_POL);
+  c[1] = evalvarn(data->v);
+  for (i = 2; i < l; i++) gel(c, i) = utoi(random_Fl(p));
+  return ZX_renormalize(c, l);
+}
+static GEN
+RandomPol(KRASNER_t *data, GEN Omega)
+{
+  long i, j, l = data->e + 3, end = data->c;
+  GEN pol = cgetg(l, t_POL);
+  pol[1] = evalsigne(1) | evalvarn(0);
+  for (i = 1; i <= data->e; i++)
+  {
+    GEN c, cf = gel(Omega, i);
+    long st = cf[1], zr = cf[2];
+    /* c = sum_{st <= j <= end} x_j p^j, where x_j are random Fq mod (p,upl)
+     * If (!zr), insist on x_st != 0 */
+    for (;;) {
+      c = RandomFF(data);
+      if (zr || signe(c)) break;
+    }
+    for (j = 1; j <= end-st; j++)
+      c = ZX_add(c, ZX_Z_mul(RandomFF(data), gel(data->pk, j)));
+    c = ZX_Z_mul(c, gel(data->pk, st));
+    gel(pol, i+1) = FpX_red(c, data->pr);
+  }
+  gel(pol, i+1) = gen_1; /* monic */
+  return pol;
+}
+
+static void
+CloneFieldData(FAD_t *fdata)
+{
+ fdata->top = gclone(fdata->top);
+ fdata->topr= gclone(fdata->topr);
+ fdata->zq  = gclone(fdata->zq);
+ fdata->eis = gclone(fdata->eis);
+ fdata->pi  = gclone(fdata->pi);
+ fdata->ipi = gclone(fdata->ipi);
+ fdata->pik = gclone(fdata->pik);
+}
+static void
+FreeFieldData(FAD_t *fdata)
+{
+  gunclone(fdata->top);
+  gunclone(fdata->topr);
+  gunclone(fdata->zq);
+  gunclone(fdata->eis);
+  gunclone(fdata->pi);
+  gunclone(fdata->ipi);
+  gunclone(fdata->pik);
+}
+
+static GEN
+WildlyRamifiedCase(KRASNER_t *data)
+{
+  long nbext, ct, fd, nb = 0, j;
+  GEN nbpol, rpl, rep, Omega;
+  FAD_t **vfd;
+  pari_timer T;
+  pari_sp av = avma, av2;
+
+  /* Omega = vector giving the structure of the set Omega */
+  /* nbpol = number of polynomials to consider = |Omega| */
+  Omega = StructureOmega(data, &nbpol);
+  nbext = itos_or_0(data->nbext);
+  if (!nbext || (nbext & ~LGBITS))
+    pari_err_OVERFLOW("padicfields [too many extensions]");
+
+  if (DEBUGLEVEL>0) {
+    err_printf("There are %ld extensions to find and %Ps polynomials to consider\n", nbext, nbpol);
+    timer_start(&T);
+  }
+
+  vfd = (FAD_t**)new_chunk(nbext);
+  for (j = 0; j < nbext; j++) vfd[j] = (FAD_t*)new_chunk(sizeof(FAD_t));
+
+  ct = 0;
+  fd = 0;
+  av2 = avma;
+
+  while (fd < nbext)
+  { /* Jump randomly among the polynomials : seems best... */
+    rpl = RandomPol(data, Omega);
+    if (DEBUGLEVEL>3) err_printf("considering polynomial %Ps\n", rpl);
+#ifdef CHECK_EXTENSIONS
+    {
+      GEN disc = poldisc0(rpl, 0);
+      long e = data->e, f = data->f, j = data->j;
+      disc = RgXQ_norm(disc, data->upl);
+      if (Z_pval(disc, data->p) != f*(e+j-1))
+        pari_err_BUG("WildlyRamifiedCase [incorrect discriminant]");
+    }
+#endif
+
+    for (j = 0; j < ct; j++)
+    {
+      nb = IsIsomorphic(data, vfd[j], rpl);
+      if (nb) break;
+    }
+    if (!nb)
+    {
+      GEN topx = get_topx(data, rpl);
+      FAD_t *fdata = (FAD_t*)vfd[ct];
+      FieldData(data, fdata, rpl, topx);
+      CloneFieldData(fdata);
+      NbConjugateFields(data, fdata);
+      nb = fdata->cj;
+      fd += nb;
+      ct++;
+      if (DEBUGLEVEL>1)
+        err_printf("%ld more extension%s\t(%ld/%ld, %ldms)\n",
+                   nb, (nb == 1)? "": "s", fd, nbext, timer_delay(&T));
+    }
+    avma = av2;
+  }
+
+  rep = cgetg(ct+1, t_VEC);
+  for (j = 0; j < ct; j++)
+  {
+    GEN topx = ZX_copy(((FAD_t*)vfd[j])->top);
+    GEN p1;
+    setvarn(topx, 0);
+    p1 = mkvec2(topx, stoi(((FAD_t*)vfd[j])->cj));
+    gel(rep, j+1) = p1;
+    FreeFieldData((FAD_t*)vfd[j]);
+  }
+  FreeRootTable(data->roottable);
+  return gerepileupto(av, rep);
+}
+
+/* return the minimal polynomial (mod pr) of a generator of (F_p^f)^x with variable v */
+static GEN
+CycloPol(KRASNER_t *d)
+{
+  GEN T, z;
+  /* v - primroot(p) */
+  if (d->f == 1)
+    return deg1pol_shallow(gen_1, Fp_neg(pgener_Fp(d->p), d->pr), d->v);
+  T = init_Fq(d->p, d->f, d->v);
+  z = gener_FpXQ(T, d->p, NULL);
+  z = ZpXQ_sqrtnlift(scalarpol(gen_1,varn(T)), d->qm1, z, T, d->p, d->r);
+  return FpX_red(ZXQ_charpoly(z, T, d->v), d->pr);
+}
+
+/* return [ p^1, p^2, ..., p^c ] */
+static GEN
+get_pk(KRASNER_t *data)
+{
+  long i, l = data->c + 1;
+  GEN pk = cgetg(l, t_VEC), p = data->p;
+  gel(pk, 1) = p;
+  for (i = 2; i <= data->c; i++) gel(pk, i) = mulii(gel(pk, i-1), p);
+  return pk;
+}
+
+/* Compute an absolute polynomial for all the totally ramified
+   extensions of Q_p(z) of degree e and discriminant p^{e + j - 1}
+   where z is a root of upl defining an unramified extension of Q_p */
+/* See padicfields for the meaning of flag */
+static GEN
+GetRamifiedPol(GEN p, GEN efj, long flag)
+{
+  long e = efj[1], f = efj[2], j = efj[3], i, l;
+  const long v = 1;
+  GEN L, pols;
+  KRASNER_t data;
+  pari_sp av = avma;
+
+  if (DEBUGLEVEL>1)
+    err_printf("  Computing extensions with decomposition [e, f, j] = [%ld, %ld, %ld]\n", e,f,j);
+  data.p   = p;
+  data.e   = e;
+  data.f   = f;
+  data.j   = j;
+  data.a   = j/e;
+  data.b   = j%e;
+  data.c   = (e+2*j)/e+1;
+  data.q   = powiu(p, f);
+  data.qm1 = subis(data.q, 1);
+  data.v   = v;
+  data.r   = 1 + ceildiv(2*j + 3, e); /* enough precision */
+  data.pr  = powiu(p, data.r);
+  data.nbext = NumberExtensions(&data);
+
+  if (flag == 2) return data.nbext;
+
+  data.upl   = CycloPol(&data);
+  /* mv = -v mod upl. If f = 1, then upl = v + c, hence mv = c */
+  data.mv    = f == 1? gel(data.upl, 2)
+                     : FpX_neg(pol_x(v), data.pr);
+  data.uplr  = FpX_red(data.upl, data.p);
+  data.frob  = FpXQ_pow(pol_x(v), p, data.upl, data.pr);
+  if (DEBUGLEVEL>1) err_printf("  Unramified part %Ps\n", data.upl);
+  data.roottable = NULL;
+  if (j)
+  {
+    if (lgefint(data.q) == 3)
+    {
+      ulong npol = upowuu(data.q[2], e+1);
+      if (npol && npol < (1<<19)) data.roottable = const_vec(npol, NULL);
+    }
+    data.pk = get_pk(&data);
+    L = WildlyRamifiedCase(&data);
+  }
+  else
+    L = TamelyRamifiedCase(&data);
+
+  pols = cgetg_copy(L, &l);
+  if (flag == 1)
+  {
+    GEN E = utoipos(e), F = utoipos(f), D = utoi(f*(e+j-1));
+    for (i = 1; i < l; i++)
+    {
+      GEN T = gel(L,i);
+      gel(pols, i) = mkvec5(ZX_copy(gel(T, 1)), E,F,D, icopy(gel(T, 2)));
+    }
+  }
+  else
+  {
+    for (i = 1; i < l; i++)
+    {
+      GEN T = gel(L,i);
+      gel(pols, i) = ZX_copy(gel(T,1));
+    }
+  }
+  return gerepileupto(av, pols);
+}
+
+static GEN
+possible_efj(GEN p, long m)
+{ /* maximal possible discriminant valuation d <= m * (1+v_p(m)) - 1 */
+  /* 1) [j = 0, tame] d = m - f with f | m and v_p(f) = v_p(m), or
+   * 2) [j > 0, wild] d >= m, j <= v_p(e)*e   */
+  ulong m1, pve, pp = p[2]; /* pp used only if v > 0 */
+  long ve, v = u_pvalrem(m, p, &m1);
+  GEN L, D = divisorsu(m1);
+  long i, taum1 = lg(D)-1, nb = 0;
+
+  if (v) {
+    for (pve = 1,ve = 1; ve <= v; ve++) { pve *= pp; nb += pve * ve; }
+    nb = itos_or_0(muluu(nb, zv_sum(D)));
+    if (!nb || is_bigint( mului(pve, sqru(v)) ) )
+      pari_err_OVERFLOW("padicfields [too many ramification possibilities]");
+  }
+  nb += taum1; /* upper bound for the number of possible triples [e,f,j] */
+
+  L = cgetg(nb + 1, t_VEC);
+  /* 1) tame */
+  for (nb=1, i=1; i < lg(D); i++)
+  {
+    long e = D[i], f = m / e;
+    gel(L, nb++) = mkvecsmall3(e, f, 0);
+  }
+  /* 2) wild */
+  /* Ore's condition: either
+   * 1) j = v_p(e) * e, or
+   * 2) j = a e + b, with 0 < b < e and v_p(b) <= a < v_p(e) */
+  for (pve = 1, ve = 1; ve <= v; ve++)
+  {
+    pve *= pp; /* = p^ve */
+    for (i = 1; i < lg(D); i++)
+    {
+      long a,b, e = D[i] * pve, f = m / e;
+      for (b = 1; b < e; b++)
+        for (a = u_lval(b, pp); a < ve; a++)
+          gel(L, nb++) = mkvecsmall3(e, f,  a*e + b);
+      gel(L, nb++) = mkvecsmall3(e, f, ve*e);
+    }
+  }
+  setlg(L, nb); return L;
+}
+
+static GEN
+pols_from_efj(pari_sp av, GEN EFJ, GEN p, long flag)
+{
+  long i, l;
+  GEN L = cgetg_copy(EFJ, &l);
+  if (l == 1) { avma = av; return flag == 2? gen_0: cgetg(1, t_VEC); }
+  for (i = 1; i < l; i++) gel(L,i) = GetRamifiedPol(p, gel(EFJ,i), flag);
+  if (flag == 2) return gerepileuptoint(av, ZV_sum(L));
+  return gerepilecopy(av, shallowconcat1(L));
+}
+
+/* return a minimal list of polynomials generating all the extensions of
+   Q_p of given degree N; if N is a t_VEC [n,d], return extensions of degree n
+   and discriminant p^d. */
+/* Return only the polynomials if flag = 0 (default), also the ramification
+   degree, the residual degree and the discriminant if flag = 1 and only the
+   number of extensions if flag = 2 */
+GEN
+padicfields0(GEN p, GEN N, long flag)
+{
+  pari_sp av = avma;
+  long m = 0, d = -1;
+  GEN L;
+
+  if (typ(p) != t_INT) pari_err_TYPE("padicfields",p);
+  /* be nice to silly users */
+  if (!BPSW_psp(p)) pari_err_PRIME("padicfields",p);
+  switch(typ(N))
+  {
+    case t_VEC:
+      if (lg(N) != 3) pari_err_TYPE("padicfields",N);
+      d = gtos(gel(N,2));
+      N = gel(N,1); /* fall through */
+    case t_INT:
+      m = itos(N);
+      if (m <= 0) pari_err_DOMAIN("padicfields", "degree", "<=", gen_0,N);
+      break;
+    default:
+      pari_err_TYPE("padicfields",N);
+  }
+  if (d >= 0) return padicfields(p, m, d, flag);
+  L = possible_efj(p, m);
+  return pols_from_efj(av, L, p, flag);
+}
+
+GEN
+padicfields(GEN p, long m, long d, long flag)
+{
+  long av = avma;
+  GEN L = possible_efj_by_d(p, m, d);
+  return pols_from_efj(av, L, p, flag);
+}
diff --git a/src/modules/kummer.c b/src/modules/kummer.c
new file mode 100644
index 0000000..2ad3380
--- /dev/null
+++ b/src/modules/kummer.c
@@ -0,0 +1,1347 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*                      KUMMER EXTENSIONS                          */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+typedef struct {
+  GEN R; /* nf.pol */
+  GEN x; /* tau ( Mod(x, R) ) */
+  GEN zk;/* action of tau on nf.zk (as t_MAT) */
+} tau_s;
+
+typedef struct {
+  GEN polnf, invexpoteta1;
+  tau_s *tau;
+  long m;
+} toK_s;
+
+typedef struct {
+  GEN R; /* ZX, compositum(P,Q) */
+  GEN p; /* QX, Mod(p,R) root of P */
+  GEN q; /* QX, Mod(q,R) root of Q */
+  long k; /* Q[X]/R generated by q + k p */
+  GEN rev;
+} compo_s;
+
+static long
+prank(GEN cyc, long ell)
+{
+  long i;
+  for (i=1; i<lg(cyc); i++)
+    if (smodis(gel(cyc,i),ell)) break;
+  return i-1;
+}
+
+/* increment y, which runs through [0,d-1]^(k-1). Return 0 when done. */
+static int
+increment(GEN y, long k, long d)
+{
+  long i = k, j;
+  do
+  {
+    if (--i == 0) return 0;
+    y[i]++;
+  } while (y[i] >= d);
+  for (j = i+1; j < k; j++) y[j] = 0;
+  return 1;
+}
+
+static int
+ok_congruence(GEN X, ulong ell, long lW, GEN vecMsup)
+{
+  long i, l;
+  if (zv_equal0(X)) return 0;
+  l = lg(X);
+  for (i=lW; i<l; i++)
+    if (X[i] == 0) return 0;
+  l = lg(vecMsup);
+  for (i=1; i<l; i++)
+    if (zv_equal0(Flm_Flc_mul(gel(vecMsup,i),X, ell))) return 0;
+  return 1;
+}
+
+static int
+ok_sign(GEN X, GEN msign, GEN arch)
+{
+  return zv_equal(Flm_Flc_mul(msign, X, 2), arch);
+}
+
+/* REDUCTION MOD ell-TH POWERS */
+
+static GEN
+fix_be(GEN bnfz, GEN be, GEN u)
+{
+  GEN nf = bnf_get_nf(bnfz), fu = bnf_get_fu_nocheck(bnfz);
+  return nfmul(nf, be, nffactorback(nf, fu, u));
+}
+
+static GEN
+logarch2arch(GEN x, long r1, long prec)
+{
+  long i, lx;
+  GEN y = cgetg_copy(x, &lx);
+  if (typ(x) == t_MAT)
+  {
+    for (i=1; i<lx; i++) gel(y,i) = logarch2arch(gel(x,i), r1, prec);
+  }
+  else
+  {
+    for (i=1; i<=r1;i++) gel(y,i) = gexp(gel(x,i),prec);
+    for (   ; i<lx; i++) gel(y,i) = gexp(gmul2n(gel(x,i),-1),prec);
+  }
+  return y;
+}
+
+/* multiply be by ell-th powers of units as to find small L2-norm for new be */
+static GEN
+reducebetanaive(GEN bnfz, GEN be, long ell, GEN elllogfu)
+{
+  GEN z, nmax, b, c, nf = bnf_get_nf(bnfz);
+  const long r1 = nf_get_r1(nf), n = maxss(ell>>1,3);
+  long i, k, ru;
+
+  b = gmul(nf_get_M(nf), be);
+  z = cgetg(n+1, t_VEC);
+  c = logarch2arch(elllogfu, r1, nf_get_prec(nf)); /* embeddings of fu^ell */
+  c = gprec_w(gnorm(c), DEFAULTPREC);
+  b = gprec_w(gnorm(b), DEFAULTPREC); /* need little precision */
+  gel(z,1) = shallowconcat(c, vecinv(c));
+  for (k=2; k<=n; k++) gel(z,k) = vecmul(gel(z,1), gel(z,k-1));
+  nmax = embednorm_T2(b, r1);
+  ru = lg(c)-1; c = zerovec(ru);
+  for(;;)
+  {
+    GEN B = NULL;
+    long besti = 0, bestk = 0;
+    for (k=1; k<=n; k++)
+    {
+      GEN zk = gel(z,k);
+      for (i=1; i<=ru; i++)
+      {
+        GEN v, t;
+        v = vecmul(b, gel(zk,i));    t = embednorm_T2(v,r1);
+        if (gcmp(t,nmax) < 0) { B=v; nmax=t; besti=i; bestk = k; continue; }
+        v = vecmul(b, gel(zk,i+ru)); t = embednorm_T2(v,r1);
+        if (gcmp(t,nmax) < 0) { B=v; nmax=t; besti=i; bestk =-k; }
+      }
+    }
+    if (!B) break;
+    b = B; gel(c,besti) = addis(gel(c,besti), bestk);
+  }
+  if (DEBUGLEVEL) err_printf("naive reduction mod U^l: unit exp. = %Ps\n",c);
+  return fix_be(bnfz, be, ZC_z_mul(c, ell));
+}
+
+static GEN
+reduce_mod_Qell(GEN bnfz, GEN be, GEN gell)
+{
+  GEN c;
+  be = nf_to_scalar_or_basis(bnfz, be);
+  be = Q_primitive_part(be, &c);
+  if (c)
+  {
+    GEN d, fa = factor(c);
+    gel(fa,2) = FpC_red(gel(fa,2), gell);
+    d = factorback(fa);
+    be = typ(be) == t_INT? mulii(be,d): ZC_Z_mul(be, d);
+  }
+  return be;
+}
+
+/* return q, q^n r = x, v_pr(r) < n for all pr. Insist q is a genuine n-th
+ * root (i.e r = 1) if strict != 0. */
+GEN
+idealsqrtn(GEN nf, GEN x, GEN gn, int strict)
+{
+  long i, l, n = itos(gn);
+  GEN fa, q, Ex, Pr;
+
+  fa = idealfactor(nf, x);
+  Pr = gel(fa,1); l = lg(Pr);
+  Ex = gel(fa,2); q = NULL;
+  for (i=1; i<l; i++)
+  {
+    long ex = itos(gel(Ex,i));
+    GEN e = stoi(ex / n);
+    if (strict && ex % n) pari_err_SQRTN("idealsqrtn", fa);
+    if (q) q = idealmulpowprime(nf, q, gel(Pr,i), e);
+    else   q = idealpow(nf, gel(Pr,i), e);
+  }
+  return q? q: gen_1;
+}
+
+static GEN
+reducebeta(GEN bnfz, GEN be, GEN ell)
+{
+  long j,ru, prec = nf_get_prec(bnfz);
+  GEN emb, z, u, elllogfu, nf = bnf_get_nf(bnfz);
+
+  if (DEBUGLEVEL>1) err_printf("reducing beta = %Ps\n",be);
+  /* reduce mod Q^ell */
+  be = reduce_mod_Qell(nf, be, ell);
+  /* reduce l-th root */
+  z = idealsqrtn(nf, be, ell, 0);
+  if (typ(z) == t_MAT && !is_pm1(gcoeff(z,1,1)))
+  {
+    z = idealred_elt(nf, z);
+    be = nfdiv(nf, be, nfpow(nf, z, ell));
+    /* make be integral */
+    be = reduce_mod_Qell(nf, be, ell);
+  }
+  if (DEBUGLEVEL>1) err_printf("beta reduced via ell-th root = %Ps\n",be);
+
+  for (;;)
+  {
+    z = get_arch_real(nf, be, &emb, prec);
+    if (z) break;
+    prec = precdbl(prec);
+    if (DEBUGLEVEL) pari_warn(warnprec,"reducebeta",prec);
+    nf = nfnewprec_shallow(nf,prec);
+  }
+  /* log. embeddings of fu^ell */
+  elllogfu = RgM_Rg_mul(real_i(bnf_get_logfu(bnfz)), ell);
+  z = shallowconcat(elllogfu, z);
+  u = lll(z);
+  if (lg(u) == lg(z))
+  {
+    ru = lg(u);
+    for (j=1; j < ru; j++)
+      if (gequal1(gcoeff(u,ru-1,j))) break;
+    if (j < ru)
+    {
+      u = gel(u,j); /* coords on (fu^ell, be) of a small generator */
+      ru--; setlg(u, ru);
+      be = fix_be(bnfz, be, ZC_Z_mul(u, ell));
+    }
+  }
+  if (DEBUGLEVEL>1) err_printf("beta LLL-reduced mod U^l = %Ps\n",be);
+  if (typ(be) == t_INT) return be;
+  return reducebetanaive(bnfz, be, itos(ell), elllogfu);
+}
+
+static GEN
+tauofalg(GEN x, tau_s *tau) {
+  long tx = typ(x);
+  if (tx == t_POLMOD) { x = gel(x,2); tx = typ(x); }
+  if (tx == t_POL) x = RgX_RgXQ_eval(x, tau->x, tau->R);
+  return mkpolmod(x, tau->R);
+}
+
+static tau_s *
+get_tau(tau_s *tau, GEN nf, compo_s *C, long g)
+{
+  GEN bas = nf_get_zk(nf), U, Uzk;
+  long i, l = lg(bas);
+
+  /* compute action of tau: q^g + kp */
+  U = RgX_add(RgXQ_powu(C->q, g, C->R), RgX_muls(C->p, C->k));
+  U = RgX_RgXQ_eval(C->rev, U, C->R);
+
+  tau->x  = U;
+  tau->R  = C->R;
+  Uzk = cgetg(l, t_MAT);
+  for (i=1; i<l; i++)
+    gel(Uzk,i) = algtobasis(nf, tauofalg(gel(bas,i), tau));
+  tau->zk = Uzk; return tau;
+}
+
+static GEN tauoffamat(GEN x, tau_s *tau);
+
+static GEN
+tauofelt(GEN x, tau_s *tau)
+{
+  switch(typ(x))
+  {
+    case t_COL: return RgM_RgC_mul(tau->zk, x);
+    case t_MAT: return tauoffamat(x, tau);
+    default: return tauofalg(x, tau);
+  }
+}
+static GEN
+tauofvec(GEN x, tau_s *tau)
+{
+  long i, l = lg(x);
+  GEN y = cgetg(l, typ(x));
+
+  for (i=1; i<l; i++) gel(y,i) = tauofelt(gel(x,i), tau);
+  return y;
+}
+/* [x, tau(x), ..., tau^m(x)] */
+static GEN
+powtau(GEN x, long m, tau_s *tau)
+{
+  GEN y = cgetg(m+1, t_VEC);
+  long i;
+  gel(y,1) = x;
+  for (i=2; i<=m; i++) gel(y,i) = tauofelt(gel(y,i-1), tau);
+  return y;
+}
+
+static GEN
+tauoffamat(GEN x, tau_s *tau)
+{
+  return mkmat2(tauofvec(gel(x,1), tau), gel(x,2));
+}
+
+static GEN
+tauofideal(GEN id, tau_s *tau)
+{
+  return ZM_hnfmodid(RgM_mul(tau->zk, id), gcoeff(id, 1,1));
+}
+
+static int
+isprimeidealconj(GEN nfz, GEN P, GEN Q, tau_s *tau)
+{
+  GEN p = pr_get_p(P);
+  GEN x = pr_get_gen(P);
+  if (!equalii(p, pr_get_p(Q))
+   || pr_get_e(P) != pr_get_e(Q)
+   || pr_get_f(P) != pr_get_f(Q)) return 0;
+  if (ZV_equal(x, pr_get_gen(Q))) return 1;
+  for(;;)
+  {
+    if (ZC_prdvd(nfz,x,Q)) return 1;
+    x = FpC_red(tauofelt(x, tau), p);
+    if (ZC_prdvd(nfz,x,P)) return 0;
+  }
+}
+
+static int
+isconjinprimelist(GEN nfz, GEN S, GEN pr, tau_s *tau)
+{
+  long i, l;
+
+  if (!tau) return 0;
+  l = lg(S);
+  for (i=1; i<l; i++)
+    if (isprimeidealconj(nfz, gel(S,i),pr,tau)) return 1;
+  return 0;
+}
+
+/* assume x in basistoalg form */
+static GEN
+downtoK(toK_s *T, GEN x)
+{
+  long degKz = lg(T->invexpoteta1) - 1;
+  GEN y = gmul(T->invexpoteta1, Rg_to_RgV(lift_intern(x), degKz));
+  return gmodulo(gtopolyrev(y,varn(T->polnf)), T->polnf);
+}
+
+static GEN
+no_sol(long all, long i)
+{
+  if (!all) pari_err_BUG(stack_sprintf("kummer [bug%ld]", i));
+  return cgetg(1,t_VEC);
+}
+
+static GEN
+get_gell(GEN bnr, GEN subgp, long all)
+{
+  GEN gell;
+  if (all && all != -1) return utoipos(labs(all));
+  if (!subgp) return ZV_prod(bnr_get_cyc(bnr));
+  gell = det(subgp);
+  if (typ(gell) != t_INT) pari_err_TYPE("rnfkummer",gell);
+  return gell;
+}
+
+typedef struct {
+  GEN Sm, Sml1, Sml2, Sl, ESml2;
+} primlist;
+
+static int
+build_list_Hecke(primlist *L, GEN nfz, GEN fa, GEN gothf, GEN gell, tau_s *tau)
+{
+  GEN listpr, listex, pr, factell;
+  long vp, i, l, ell = itos(gell), degKz = nf_get_degree(nfz);
+
+  if (!fa) fa = idealfactor(nfz, gothf);
+  listpr = gel(fa,1);
+  listex = gel(fa,2); l = lg(listpr);
+  L->Sm  = vectrunc_init(l);
+  L->Sml1= vectrunc_init(l);
+  L->Sml2= vectrunc_init(l);
+  L->Sl  = vectrunc_init(l+degKz);
+  L->ESml2=vecsmalltrunc_init(l);
+  for (i=1; i<l; i++)
+  {
+    pr = gel(listpr,i);
+    vp = itos(gel(listex,i));
+    if (!equalii(pr_get_p(pr), gell))
+    {
+      if (vp != 1) return 1;
+      if (!isconjinprimelist(nfz, L->Sm,pr,tau)) vectrunc_append(L->Sm,pr);
+    }
+    else
+    {
+      long e = pr_get_e(pr), vd = (vp-1)*(ell-1)-ell*e;
+      if (vd > 0) return 4;
+      if (vd==0)
+      {
+        if (!isconjinprimelist(nfz, L->Sml1,pr,tau)) vectrunc_append(L->Sml1, pr);
+      }
+      else
+      {
+        if (vp==1) return 2;
+        if (!isconjinprimelist(nfz, L->Sml2,pr,tau))
+        {
+          vectrunc_append(L->Sml2, pr);
+          vecsmalltrunc_append(L->ESml2, vp);
+        }
+      }
+    }
+  }
+  factell = idealprimedec(nfz,gell); l = lg(factell);
+  for (i=1; i<l; i++)
+  {
+    pr = gel(factell,i);
+    if (!idealval(nfz,gothf,pr))
+      if (!isconjinprimelist(nfz, L->Sl,pr,tau)) vectrunc_append(L->Sl, pr);
+  }
+  return 0; /* OK */
+}
+
+/* Return a Flm */
+static GEN
+logall(GEN nf, GEN vec, long lW, long mginv, long ell, GEN pr, long ex)
+{
+  GEN m, M, bid = Idealstar(nf, idealpows(nf, pr, ex), nf_INIT);
+  long ellrank, i, l = lg(vec);
+
+  ellrank = prank(bid_get_cyc(bid), ell);
+  M = cgetg(l,t_MAT);
+  for (i=1; i<l; i++)
+  {
+    m = ideallog(nf, gel(vec,i), bid);
+    setlg(m, ellrank+1);
+    if (i < lW) m = gmulsg(mginv, m);
+    gel(M,i) = ZV_to_Flv(m, ell);
+  }
+  return M;
+}
+
+/* compute the u_j (see remark 5.2.15.) */
+static GEN
+get_u(GEN cyc, long rc, GEN gell)
+{
+  long i, l = lg(cyc);
+  GEN u = cgetg(l,t_VEC);
+  for (i=1; i<=rc; i++) gel(u,i) = gen_0;
+  for (   ; i<  l; i++) gel(u,i) = Fp_inv(gel(cyc,i), gell);
+  return u;
+}
+
+/* alg. 5.2.15. with remark */
+static GEN
+isprincipalell(GEN bnfz, GEN id, GEN cycgen, GEN u, GEN gell, long rc)
+{
+  long i, l = lg(cycgen);
+  GEN logdisc, b, y = bnfisprincipal0(bnfz, id, nf_FORCE|nf_GENMAT);
+
+  logdisc = FpC_red(gel(y,1), gell);
+  b = gel(y,2);
+  for (i=rc+1; i<l; i++)
+  {
+    GEN e = modii(mulii(gel(logdisc,i),gel(u,i)), gell);
+    if (signe(e)) b = famat_mul(b, famat_pow(gel(cycgen,i), e));
+  }
+  setlg(logdisc,rc+1); return mkvec2(logdisc, b);
+}
+
+static GEN
+famat_factorback(GEN v, GEN e)
+{
+  long i, l = lg(e);
+  GEN V = cgetg(1, t_MAT);
+  for (i=1; i<l; i++)
+    if (signe(gel(e,i))) V = famat_mul(V, famat_pow(gel(v,i), gel(e,i)));
+  return V;
+}
+
+static GEN
+compute_beta(GEN X, GEN vecWB, GEN ell, GEN bnfz)
+{
+  GEN BE, be;
+  BE = famat_reduce(famat_factorback(vecWB, zv_to_ZV(X)));
+  gel(BE,2) = centermod(gel(BE,2), ell);
+  be = nffactorback(bnfz, BE, NULL);
+  be = reducebeta(bnfz, be, ell);
+  if (DEBUGLEVEL>1) err_printf("beta reduced = %Ps\n",be);
+  return be;
+}
+
+static GEN
+get_Selmer(GEN bnf, GEN cycgen, long rc)
+{
+  GEN fu = bnf_get_fu_nocheck(bnf), tu = bnf_get_tuU(bnf);
+  GEN units = matalgtobasis(bnf,shallowconcat(fu,tu));
+  return shallowconcat(units, vecslice(cycgen,1,rc));
+}
+
+GEN
+lift_if_rational(GEN x)
+{
+  long lx, i;
+  GEN y;
+
+  switch(typ(x))
+  {
+    default: break;
+
+    case t_POLMOD:
+      y = gel(x,2);
+      if (typ(y) == t_POL)
+      {
+        long d = degpol(y);
+        if (d > 0) return x;
+        return (d < 0)? gen_0: gel(y,2);
+      }
+      return y;
+
+    case t_POL: lx = lg(x);
+      for (i=2; i<lx; i++) gel(x,i) = lift_if_rational(gel(x,i));
+      break;
+    case t_VEC: case t_COL: case t_MAT: lx = lg(x);
+      for (i=1; i<lx; i++) gel(x,i) = lift_if_rational(gel(x,i));
+  }
+  return x;
+}
+
+/* A column vector representing a subgroup of prime index */
+static GEN
+grptocol(GEN H)
+{
+  long i, j, l = lg(H);
+  GEN col = cgetg(l, t_VECSMALL);
+  for (i = 1; i < l; i++)
+  {
+    ulong ell = itou( gcoeff(H,i,i) );
+    if (ell == 1) col[i] = 0; else { col[i] = ell-1; break; }
+  }
+  for (j=i; ++j < l; ) col[j] = itou( gcoeff(H,i,j) );
+  return col;
+}
+
+/* Reorganize kernel basis so that the tests of ok_congruence can be ok
+ * for y[ncyc]=1 and y[1..ncyc]=1 */
+static GEN
+fix_kernel(GEN K, GEN M, GEN vecMsup, long lW, long ell)
+{
+  pari_sp av = avma;
+  long i, j, idx, ffree, dK = lg(K)-1;
+  GEN Ki, Kidx = cgetg(dK+1, t_VECSMALL);
+
+  /* First step: Gauss elimination on vectors lW...lg(M) */
+  for (idx = lg(K), i=lg(M); --i >= lW; )
+  {
+    for (j=dK; j > 0; j--) if (coeff(K, i, j)) break;
+    if (!j)
+    { /* Do our best to ensure that K[dK,i] != 0 */
+      if (coeff(K, i, dK)) continue;
+      for (j = idx; j < dK; j++)
+        if (coeff(K, i, j) && coeff(K, Kidx[j], dK) != ell - 1)
+          Flv_add_inplace(gel(K,dK), gel(K,j), ell);
+    }
+    if (j != --idx) swap(gel(K, j), gel(K, idx));
+    Kidx[idx] = i;
+    if (coeff(K,i,idx) != 1)
+      Flc_Fl_div_inplace(gel(K,idx), coeff(K,i,idx), ell);
+    Ki = gel(K,idx);
+    if (coeff(K,i,dK) != 1)
+    {
+      ulong t = Fl_sub(coeff(K,i,dK), 1, ell);
+      Flv_sub_inplace(gel(K,dK), Flc_Fl_mul(Ki, t, ell), ell);
+    }
+    for (j = dK; --j > 0; )
+    {
+      if (j == idx) continue;
+      if (coeff(K,i,j))
+        Flv_sub_inplace(gel(K,j), Flc_Fl_mul(Ki, coeff(K,i,j), ell), ell);
+    }
+  }
+  /* ffree = first vector that is not "free" for the scalar products */
+  ffree = idx;
+  /* Second step: for each hyperplane equation in vecMsup, do the same
+   * thing as before. */
+  for (i=1; i < lg(vecMsup); i++)
+  {
+    GEN Msup = gel(vecMsup,i);
+    ulong dotprod;
+    if (lgcols(Msup) != 2) continue;
+    Msup = row_zm(Msup, 1);
+    for (j=ffree; --j > 0; )
+    {
+      dotprod = Flv_dotproduct(Msup, gel(K,j), ell);
+      if (dotprod)
+      {
+        if (j != --ffree) swap(gel(K, j), gel(K, ffree));
+        if (dotprod != 1) Flc_Fl_div_inplace(gel(K, ffree), dotprod, ell);
+        break;
+      }
+    }
+    if (!j)
+    { /* Do our best to ensure that vecMsup.K[dK] != 0 */
+      if (Flv_dotproduct(Msup, gel(K,dK), ell) == 0)
+      {
+        for (j = ffree-1; j <= dK; j++)
+          if (Flv_dotproduct(Msup, gel(K,j), ell)
+              && coeff(K,Kidx[j],dK) != ell-1)
+            Flv_add_inplace(gel(K,dK), gel(K,j), ell);
+      }
+      continue;
+    }
+    Ki = gel(K,ffree);
+    dotprod = Flv_dotproduct(Msup, gel(K,dK), ell);
+    if (dotprod != 1)
+    {
+      ulong t = Fl_sub(dotprod,1,ell);
+      Flv_sub_inplace(gel(K,dK), Flc_Fl_mul(Ki,t,ell), ell);
+    }
+    for (j = dK; --j > 0; )
+    {
+      if (j == ffree) continue;
+      dotprod = Flv_dotproduct(Msup, gel(K,j), ell);
+      if (dotprod) Flv_sub_inplace(gel(K,j), Flc_Fl_mul(Ki,dotprod,ell), ell);
+    }
+  }
+  if (ell == 2)
+  {
+    for (i = ffree, j = ffree-1; i <= dK && j; i++, j--)
+    { swap(gel(K,i), gel(K,j)); }
+  }
+  /* Try to ensure that y = vec_ei(n, i) gives a good candidate */
+  for (i = 1; i < dK; i++) Flv_add_inplace(gel(K,i), gel(K,dK), ell);
+  return gerepilecopy(av, K);
+}
+
+static GEN
+Flm_init(long m, long n)
+{
+  GEN M = cgetg(n+1, t_MAT);
+  long i; for (i = 1; i <= n; i++) gel(M,i) = cgetg(m+1, t_VECSMALL);
+  return M;
+}
+static void
+Flv_fill(GEN v, GEN y)
+{
+  long i, l = lg(y);
+  for (i = 1; i < l; i++) v[i] = y[i];
+}
+
+/* if all!=0, give all equations of degree 'all'. Assume bnr modulus is the
+ * conductor */
+static GEN
+rnfkummersimple(GEN bnr, GEN subgroup, GEN gell, long all)
+{
+  long ell, i, j, degK, dK;
+  long lSml2, lSl2, lSp, rc, lW;
+  long prec;
+  long rk=0, ncyc=0;
+  GEN mat=NULL, matgrp=NULL, xell, be1 = NULL;
+  long firstpass = all<0;
+
+  GEN bnf,nf,bid,ideal,arch,cycgen;
+  GEN cyc;
+  GEN Sp,listprSp,matP;
+  GEN res=NULL,u,M,K,y,vecMsup,vecW,vecWB,vecBp,msign;
+  primlist L;
+
+  bnf = bnr_get_bnf(bnr); (void)bnf_get_fu(bnf);
+  nf  = bnf_get_nf(bnf);
+  degK = nf_get_degree(nf);
+
+  bid = bnr_get_bid(bnr);
+  ideal= bid_get_ideal(bid);
+  arch = bid_get_arch(bid); /* this is the conductor */
+  ell = itos(gell);
+  i = build_list_Hecke(&L, nf, gel(bid,3), ideal, gell, NULL);
+  if (i) return no_sol(all,i);
+
+  lSml2 = lg(L.Sml2)-1;
+  Sp = shallowconcat(L.Sm, L.Sml1); lSp = lg(Sp)-1;
+  listprSp = shallowconcat(L.Sml2, L.Sl); lSl2 = lg(listprSp)-1;
+
+  cycgen = check_and_build_cycgen(bnf);
+  cyc = bnf_get_cyc(bnf); rc = prank(cyc, ell);
+
+  vecW = get_Selmer(bnf, cycgen, rc);
+  u = get_u(cyc, rc, gell);
+
+  vecBp = cgetg(lSp+1, t_VEC);
+  matP  = cgetg(lSp+1, t_MAT);
+  for (j=1; j<=lSp; j++)
+  {
+    GEN L = isprincipalell(bnf,gel(Sp,j), cycgen,u,gell,rc);
+    gel( matP,j) = gel(L,1);
+    gel(vecBp,j) = gel(L,2);
+  }
+  vecWB = shallowconcat(vecW, vecBp);
+
+  prec = DEFAULTPREC +
+      nbits2extraprec(((degK-1) * (gexpo(vecWB) + gexpo(nf_get_M(nf)))));
+  if (nf_get_prec(nf) < prec) nf = nfnewprec_shallow(nf, prec);
+  msign = nfsign(nf, vecWB);
+  arch = ZV_to_zv(arch);
+
+  vecMsup = cgetg(lSml2+1,t_VEC);
+  M = NULL;
+  for (i=1; i<=lSl2; i++)
+  {
+    GEN pr = gel(listprSp,i);
+    long e = pr_get_e(pr), z = ell * (e / (ell-1));
+
+    if (i <= lSml2)
+    {
+      z += 1 - L.ESml2[i];
+      gel(vecMsup,i) = logall(nf, vecWB, 0,0, ell, pr,z+1);
+    }
+    M = vconcat(M, logall(nf, vecWB, 0,0, ell, pr,z));
+  }
+  lW = lg(vecW);
+  M = vconcat(M, shallowconcat(zero_Flm(rc,lW-1), ZM_to_Flm(matP, ell)));
+  K = Flm_ker(M, ell);
+  dK = lg(K)-1;
+
+  if (all < 0)
+    K = fix_kernel(K, M, vecMsup, lW, ell);
+
+  y = cgetg(dK+1,t_VECSMALL);
+  if (all) res = cgetg(1,t_VEC); /* in case all = 1 */
+  if (all < 0)
+  {
+    ncyc = dK;
+    mat = Flm_init(dK, ncyc);
+    if (all == -1) matgrp = Flm_init(lg(bnr_get_cyc(bnr)), ncyc+1);
+    rk = 0;
+  }
+  xell = monomial(gen_1, ell, 0);
+  do {
+    dK = lg(K)-1;
+    while (dK)
+    {
+      for (i=1; i<dK; i++) y[i] = 0;
+      y[i] = 1; /* y = [0,...,0,1,0,...,0], 1 at dK'th position */
+      do
+      {
+        pari_sp av = avma;
+        GEN be, P=NULL, X;
+        if (all < 0)
+        {
+          Flv_fill(gel(mat, rk+1), y);
+          setlg(mat, rk+2);
+          if (Flm_rank(mat, ell) <= rk) continue;
+        }
+FOUND:  X = Flm_Flc_mul(K, y, ell);
+        if (ok_congruence(X, ell, lW, vecMsup) && ok_sign(X, msign, arch))
+        {/* be satisfies all congruences, x^ell - be is irreducible, signature
+          * and relative discriminant are correct */
+          if (all < 0) rk++;
+          be = compute_beta(X, vecWB, gell, bnf);
+          be = nf_to_scalar_or_alg(nf, be);
+          if (typ(be) == t_POL) be = mkpolmod(be, nf_get_pol(nf));
+          if (all == -1)
+          {
+            pari_sp av2 = avma;
+            GEN Kgrp, colgrp = grptocol(rnfnormgroup(bnr, gsub(xell, be)));
+            if (ell != 2)
+            {
+              if (rk == 1) be1 = be;
+              else
+              { /* Compute the pesky scalar */
+                GEN K2, C = cgetg(4, t_MAT);
+                gel(C,1) = gel(matgrp,1);
+                gel(C,2) = colgrp;
+                gel(C,3) = grptocol(rnfnormgroup(bnr, gsub(xell, gmul(be1,be))));
+                K2 = Flm_ker(C, ell);
+                if (lg(K2) != 2) pari_err_BUG("linear algebra");
+                K2 = gel(K2,1);
+                if (K2[1] != K2[2])
+                  Flc_Fl_mul_inplace(colgrp, Fl_div(K2[2],K2[1],ell), ell);
+              }
+            }
+            Flv_fill(gel(matgrp,rk), colgrp);
+            setlg(matgrp, rk+1);
+            Kgrp = Flm_ker(matgrp, ell);
+            if (lg(Kgrp) == 2)
+            {
+              setlg(gel(Kgrp,1), rk+1);
+              y = Flm_Flc_mul(mat, gel(Kgrp,1), ell);
+              all = 0; goto FOUND;
+            }
+            avma = av2;
+          }
+          else
+          {
+            P = gsub(xell, be);
+            if (all)
+              res = shallowconcat(res, gerepileupto(av, P));
+            else
+            {
+              if (ZM_equal(rnfnormgroup(bnr,P),subgroup)) return P; /*DONE*/
+              avma = av; continue;
+            }
+          }
+          if (all < 0 && rk == ncyc) return res;
+          if (firstpass) break;
+        }
+        else avma = av;
+      } while (increment(y, dK, ell));
+      y[dK--] = 0;
+    }
+  } while (firstpass--);
+  return all? res: gen_0;
+}
+
+/* alg. 5.3.11 (return only discrete log mod ell) */
+static GEN
+isvirtualunit(GEN bnf, GEN v, GEN cycgen, GEN cyc, GEN gell, long rc)
+{
+  GEN L, b, eps, y, q, nf = bnf_get_nf(bnf);
+  GEN w = idealsqrtn(nf, v, gell, 1);
+  long i, l = lg(cycgen);
+
+  L = bnfisprincipal0(bnf, w, nf_GENMAT|nf_FORCE);
+  q = gel(L,1);
+  if (gequal0(q)) { eps = v; y = q; }
+  else
+  {
+    b = gel(L,2);
+    y = cgetg(l,t_COL);
+    for (i=1; i<l; i++)
+      gel(y,i) = diviiexact(mulii(gell,gel(q,i)), gel(cyc,i));
+    eps = famat_mul(famat_factorback(cycgen, y), famat_pow(b, gell));
+    eps = famat_mul(famat_inv(eps), v);
+  }
+  setlg(y, rc+1);
+  b = bnfisunit(bnf,eps);
+  if (lg(b) == 1) pari_err_BUG("isvirtualunit");
+  return shallowconcat(lift_intern(b), y);
+}
+
+/* J a vector of elements in nfz = relative extension of nf by polrel,
+ * return the Steinitz element associated to the module generated by J */
+static GEN
+Stelt(GEN nf, GEN J, GEN polrel)
+{
+  long i, l = lg(J);
+  GEN A, I;
+
+  A = cgetg(l, t_VEC);
+  I = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+  {
+    GEN v = gel(J,i);
+    gel(A,i) = (typ(v) != t_POL)? v: RgX_rem(v, polrel);
+    gel(I,i) = gen_1;
+  }
+  A = RgV_to_RgM(A, degpol(polrel));
+  return prodid(nf, gel(nfhnf(nf, mkvec2(A,I)),2));
+}
+
+static GEN
+polrelKzK(toK_s *T, GEN x)
+{
+  GEN P = roots_to_pol(powtau(x, T->m, T->tau), 0);
+  long i, l = lg(P);
+  for (i=2; i<l; i++) gel(P,i) = downtoK(T, gel(P,i));
+  return P;
+}
+
+/* N: Cl_m(Kz) --> Cl_m(K), lift subgroup from bnr to bnrz using Algo 4.1.11 */
+static GEN
+invimsubgroup(GEN bnrz, GEN bnr, GEN subgroup, toK_s *T)
+{
+  long l, j;
+  GEN P,raycycz,rayclgpz,raygenz,U,polrel,StZk;
+  GEN nf = checknf(bnr), nfz = checknf(bnrz);
+  GEN polz = nf_get_pol(nfz), zkz = nf_get_zk(nfz);
+
+  polrel = polrelKzK(T, pol_x(varn(polz)));
+  StZk = Stelt(nf, zkz, polrel);
+  rayclgpz = gel(bnrz,5);
+  raycycz = gel(rayclgpz,2); l = lg(raycycz);
+  raygenz = gel(rayclgpz,3);
+  P = cgetg(l,t_MAT);
+  for (j=1; j<l; j++)
+  {
+    GEN g, id = idealhnf_shallow(nfz, gel(raygenz,j));
+    g = Stelt(nf, RgV_RgM_mul(zkz, id), polrel);
+    g = idealdiv(nf, g, StZk); /* N_{Kz/K}(gen[j]) */
+    gel(P,j) = isprincipalray(bnr, g);
+  }
+  (void)ZM_hnfall(shallowconcat(P, subgroup), &U, 1);
+  setlg(U, l); for (j=1; j<l; j++) setlg(U[j], l);
+  return ZM_hnfmodid(U, raycycz);
+}
+
+static GEN
+pol_from_Newton(GEN S)
+{
+  long i, k, l = lg(S);
+  GEN C = cgetg(l+1, t_VEC), c = C + 1;
+  gel(c,0) = gen_1;
+  gel(c,1) = gel(S,1); /* gen_0 in our case */
+  for (k = 2; k < l; k++)
+  {
+    GEN s = gel(S,k);
+    for (i = 2; i < k-1; i++) s = gadd(s, gmul(gel(S,i), gel(c,k-i)));
+    gel(c,k) = gdivgs(s, -k);
+  }
+  return gtopoly(C, 0);
+}
+
+/* - mu_b = sum_{0 <= i < m} floor(r_b r_{d-1-i} / ell) tau^i */
+static GEN
+get_mmu(long b, GEN r, long ell)
+{
+  long i, m = lg(r)-1;
+  GEN M = cgetg(m+1, t_VEC);
+  for (i = 0; i < m; i++) gel(M,i+1) = stoi((r[b + 1] * r[m - i]) / ell);
+  return M;
+}
+/* theta^ell = be ^ ( sum tau^a r_{d-1-a} ) */
+static GEN
+get_reverse(GEN r)
+{
+  long i, m = lg(r)-1;
+  GEN M = cgetg(m+1, t_VEC);
+  for (i = 0; i < m; i++) gel(M,i+1) = stoi(r[m - i]);
+  return M;
+}
+
+/* coeffs(x, a..b) in variable v >= varn(x) */
+static GEN
+split_pol(GEN x, long v, long a, long b)
+{
+  long i, l = degpol(x);
+  GEN y = x + a, z;
+
+  if (l < b) b = l;
+  if (a > b || varn(x) != v) return pol_0(v);
+  l = b-a + 3;
+  z = cgetg(l, t_POL); z[1] = x[1];
+  for (i = 2; i < l; i++) gel(z,i) = gel(y,i);
+  return normalizepol_lg(z, l);
+}
+
+/* return (den_a * z) mod (v^ell - num_a/den_a), assuming deg(z) < 2*ell
+ * allow either num/den to be NULL (= 1) */
+static GEN
+mod_Xell_a(GEN z, long v, long ell, GEN num_a, GEN den_a)
+{
+  GEN z1 = split_pol(z, v, ell, degpol(z));
+  GEN z0 = split_pol(z, v, 0,   ell-1); /* z = v^ell z1 + z0*/
+  if (den_a) z0 = gmul(den_a, z0);
+  if (num_a) z1 = gmul(num_a, z1);
+  return gadd(z0, z1);
+}
+static GEN
+to_alg(GEN nfz, GEN v)
+{
+  GEN z;
+  if (typ(v) != t_COL) return v;
+  z = gmul(nf_get_zk(nfz), v);
+  if (typ(z) == t_POL) setvarn(z, MAXVARN);
+  return z;
+}
+
+/* th. 5.3.5. and prop. 5.3.9. */
+static GEN
+compute_polrel(GEN nfz, toK_s *T, GEN be, long g, long ell)
+{
+  long i, k, m = T->m, vT = fetch_var();
+  GEN r, powtaubet, S, p1, root, num_t, den_t, nfzpol, powtau_prim_invbe;
+  GEN prim_Rk, C_Rk, prim_root, C_root, prim_invbe, C_invbe;
+  pari_timer ti;
+
+  r = cgetg(m+1,t_VECSMALL); /* r[i+1] = g^i mod ell */
+  r[1] = 1;
+  for (i=2; i<=m; i++) r[i] = (r[i-1] * g) % ell;
+  powtaubet = powtau(be, m, T->tau);
+  if (DEBUGLEVEL>1) { err_printf("Computing Newton sums: "); timer_start(&ti); }
+  prim_invbe = Q_primitive_part(nfinv(nfz, be), &C_invbe);
+  powtau_prim_invbe = powtau(prim_invbe, m, T->tau);
+
+  root = cgetg(ell + 2, t_POL);
+  root[1] = evalsigne(1) | evalvarn(0);
+  for (i = 0; i < ell; i++) gel(root,2+i) = gen_0;
+  for (i = 0; i < m; i++)
+  { /* compute (1/be) ^ (-mu) instead of be^mu [mu << 0].
+     * 1/be = C_invbe * prim_invbe */
+    GEN mmu = get_mmu(i, r, ell);
+    /* p1 = prim_invbe ^ -mu */
+    p1 = to_alg(nfz, nffactorback(nfz, powtau_prim_invbe, mmu));
+    if (C_invbe) p1 = gmul(p1, powgi(C_invbe, RgV_sumpart(mmu, m)));
+    /* root += zeta_ell^{r_i} T^{r_i} be^mu_i */
+    gel(root, 2 + r[i+1]) = monomial(p1, r[i+1], vT);
+  }
+  /* Other roots are as above with z_ell --> z_ell^j.
+   * Treat all contents (C_*) and principal parts (prim_*) separately */
+  prim_Rk = prim_root = Q_primitive_part(root, &C_root);
+  C_Rk = C_root;
+
+  /* Compute modulo X^ell - 1, T^ell - t, nfzpol(MAXVARN) */
+  p1 = to_alg(nfz, nffactorback(nfz, powtaubet, get_reverse(r)));
+  num_t = Q_remove_denom(p1, &den_t);
+
+  nfzpol = leafcopy(nf_get_pol(nfz));
+  setvarn(nfzpol, MAXVARN);
+  S = cgetg(ell+1, t_VEC); /* Newton sums */
+  gel(S,1) = gen_0;
+  for (k = 2; k <= ell; k++)
+  { /* compute the k-th Newton sum */
+    pari_sp av = avma;
+    GEN z, D, Rk = gmul(prim_Rk, prim_root);
+    C_Rk = mul_content(C_Rk, C_root);
+    Rk = mod_Xell_a(Rk, 0, ell, NULL, NULL); /* mod X^ell - 1 */
+    for (i = 2; i < lg(Rk); i++)
+    {
+      if (typ(gel(Rk,i)) != t_POL) continue;
+      z = mod_Xell_a(gel(Rk,i), vT, ell, num_t,den_t); /* mod T^ell - t */
+      gel(Rk,i) = RgXQX_red(z, nfzpol); /* mod nfz.pol */
+    }
+    if (den_t) C_Rk = mul_content(C_Rk, ginv(den_t));
+    prim_Rk = Q_primitive_part(Rk, &D);
+    C_Rk = mul_content(C_Rk, D); /* root^k = prim_Rk * C_Rk */
+
+    /* Newton sum is ell * constant coeff (in X), which has degree 0 in T */
+    z = polcoeff_i(prim_Rk, 0, 0);
+    z = polcoeff_i(z      , 0,vT);
+    z = downtoK(T, gmulgs(z, ell));
+    if (C_Rk) z = gmul(z, C_Rk);
+    gerepileall(av, C_Rk? 3: 2, &z, &prim_Rk, &C_Rk);
+    if (DEBUGLEVEL>1) { err_printf("%ld(%ld) ", k, timer_delay(&ti)); err_flush(); }
+    gel(S,k) = z;
+  }
+  if (DEBUGLEVEL>1) err_printf("\n");
+  (void)delete_var(); return pol_from_Newton(S);
+}
+
+static GEN
+lifttoKz(GEN nfz, GEN nf, GEN id, compo_s *C)
+{
+  GEN I = idealtwoelt(nf,id);
+  GEN x = nf_to_scalar_or_alg(nf, gel(I,2));
+  if (typ(x) != t_POL) return gel(I,1);
+  gel(I,2) = algtobasis(nfz, RgX_RgXQ_eval(x, C->p, C->R));
+  return idealhnf_two(nfz,I);
+}
+
+static void
+compositum_red(compo_s *C, GEN P, GEN Q)
+{
+  GEN p, q, a, z = gel(compositum2(P, Q),1);
+  a = gel(z,1);
+  p = gel(gel(z,2), 2);
+  q = gel(gel(z,3), 2);
+  C->k = itos( gel(z,4) );
+  /* reduce R. FIXME: should be polredbest(a, 1), but breaks rnfkummer bench */
+  z = polredabs0(a, nf_ORIG|nf_PARTIALFACT);
+  C->R = gel(z,1);
+  a = gel(gel(z,2), 2);
+  C->p = RgX_RgXQ_eval(p, a, C->R);
+  C->q = RgX_RgXQ_eval(q, a, C->R);
+  C->rev = QXQ_reverse(a, C->R);
+  if (DEBUGLEVEL>1) err_printf("polred(compositum) = %Ps\n",C->R);
+}
+
+/* replace P->C^(-deg P) P(xC) for the largest integer C such that coefficients
+ * remain algebraic integers. Lift *rational* coefficients */
+static void
+nfX_Z_normalize(GEN nf, GEN P)
+{
+  long i, l;
+  GEN C, Cj, PZ = cgetg_copy(P, &l);
+  PZ[1] = P[1];
+  for (i = 2; i < l; i++) /* minor variation on RgX_to_nfX (create PZ) */
+  {
+    GEN z = nf_to_scalar_or_basis(nf, gel(P,i));
+    if (typ(z) == t_INT)
+      gel(PZ,i) = gel(P,i) = z;
+    else
+      gel(PZ,i) = ZV_content(z);
+  }
+  (void)ZX_Z_normalize(PZ, &C);
+
+  if (C == gen_1) return;
+  Cj = C;
+  for (i = l-2; i > 1; i--)
+  {
+    if (i != l-2) Cj = mulii(Cj, C);
+    gel(P,i) = gdiv(gel(P,i), Cj);
+  }
+}
+
+static GEN
+_rnfkummer(GEN bnr, GEN subgroup, long all, long prec)
+{
+  long ell, i, j, m, d, dK, dc, rc, ru, rv, g, mginv, degK, degKz, vnf;
+  long lSp, lSml2, lSl2, lW;
+  GEN polnf,bnf,nf,bnfz,nfz,bid,ideal,cycgen,gell,p1,p2,vselmer;
+  GEN cyc,gen;
+  GEN Q,idealz,gothf;
+  GEN res=NULL,u,M,K,y,vecMsup,vecW,vecWA,vecWB,vecB,vecC,vecAp,vecBp;
+  GEN matP,Sp,listprSp,Tc,Tv,P;
+  primlist L;
+  toK_s T;
+  tau_s _tau, *tau;
+  compo_s COMPO;
+  pari_timer t;
+  long rk=0, ncyc=0;
+  GEN mat=NULL;
+  long firstpass = all<0;
+
+  if (DEBUGLEVEL) timer_start(&t);
+  checkbnr(bnr);
+  bnf = bnr_get_bnf(bnr);
+  nf  = bnf_get_nf(bnf);
+  polnf = nf_get_pol(nf); vnf = varn(polnf);
+  if (!vnf) pari_err_PRIORITY("rnfkummer", polnf, "=", 0);
+  /* step 7 */
+  p1 = bnrconductor(bnr, subgroup, 2);
+  if (DEBUGLEVEL) timer_printf(&t, "[rnfkummer] conductor");
+  bnr      = gel(p1,2);
+  subgroup = gel(p1,3);
+  gell = get_gell(bnr,subgroup,all);
+  ell = itos(gell);
+  if (ell == 1) return pol_x(0);
+  if (!uisprime(ell)) pari_err_IMPL("kummer for composite relative degree");
+  if (all && all != -1 && umodiu(bnr_get_no(bnr), ell))
+    return cgetg(1, t_VEC);
+  if (bnf_get_tuN(bnf) % ell == 0)
+    return rnfkummersimple(bnr, subgroup, gell, all);
+
+  if (all == -1) all = 0;
+  bid = bnr_get_bid(bnr);
+  ideal = bid_get_ideal(bid);
+  /* step 1 of alg 5.3.5. */
+  if (DEBUGLEVEL>2) err_printf("Step 1\n");
+  compositum_red(&COMPO, polnf, polcyclo(ell,vnf));
+  /* step 2 */
+  if (DEBUGLEVEL>2) err_printf("Step 2\n");
+  if (DEBUGLEVEL) timer_printf(&t, "[rnfkummer] compositum");
+  degK  = degpol(polnf);
+  degKz = degpol(COMPO.R);
+  m = degKz / degK;
+  d = (ell-1) / m;
+  g = (long)Fl_powu(pgener_Fl(ell), d, ell);
+  if (Fl_powu((ulong)g, m, ell*ell) == 1) g += ell;
+  /* ord(g) = m in all (Z/ell^k)^* */
+  /* step 3 */
+  if (DEBUGLEVEL>2) err_printf("Step 3\n");
+  /* could factor disc(R) using th. 2.1.6. */
+  bnfz = Buchall(COMPO.R, nf_FORCE, maxss(prec,BIGDEFAULTPREC));
+  if (DEBUGLEVEL) timer_printf(&t, "[rnfkummer] bnfinit(Kz)");
+  cycgen = check_and_build_cycgen(bnfz);
+  nfz = bnf_get_nf(bnfz);
+  cyc = bnf_get_cyc(bnfz); rc = prank(cyc,ell);
+  gen = bnf_get_gen(bnfz);
+  u = get_u(cyc, rc, gell);
+
+  vselmer = get_Selmer(bnfz, cycgen, rc);
+  if (DEBUGLEVEL) timer_printf(&t, "[rnfkummer] Selmer group");
+  ru = (degKz>>1)-1;
+  rv = rc+ru+1;
+  tau = get_tau(&_tau, nfz, &COMPO, g);
+
+  /* step 4 */
+  if (DEBUGLEVEL>2) err_printf("Step 4\n");
+  vecB=cgetg(rc+1,t_VEC);
+  Tc=cgetg(rc+1,t_MAT);
+  for (j=1; j<=rc; j++)
+  {
+    p1 = tauofideal(gel(gen,j), tau);
+    p1 = isprincipalell(bnfz, p1, cycgen,u,gell,rc);
+    Tc[j]  = p1[1];
+    vecB[j]= p1[2];
+  }
+
+  vecC = cgetg(rc+1,t_VEC);
+  if (rc)
+  {
+    for (j=1; j<=rc; j++) gel(vecC,j) = cgetg(1, t_MAT);
+    p1 = cgetg(m,t_VEC);
+    gel(p1,1) = matid(rc);
+    for (j=2; j<=m-1; j++) gel(p1,j) = gmul(gel(p1,j-1),Tc);
+    p2 = vecB;
+    for (j=1; j<=m-1; j++)
+    {
+      GEN z = FpM_red(gmulsg((j*d)%ell,gel(p1,m-j)), gell);
+      p2 = tauofvec(p2, tau);
+      for (i=1; i<=rc; i++)
+        gel(vecC,i) = famat_mul(gel(vecC,i), famat_factorback(p2,gel(z,i)));
+    }
+    for (i=1; i<=rc; i++) gel(vecC,i) = famat_reduce(gel(vecC,i));
+  }
+  /* step 5 */
+  if (DEBUGLEVEL>2) err_printf("Step 5\n");
+  Tv = cgetg(rv+1,t_MAT);
+  for (j=1; j<=rv; j++)
+  {
+    p1 = tauofelt(gel(vselmer,j), tau);
+    if (typ(p1) == t_MAT) /* famat */
+      p1 = nffactorback(nfz, gel(p1,1), FpC_red(gel(p1,2),gell));
+    gel(Tv,j) = isvirtualunit(bnfz, p1, cycgen,cyc,gell,rc);
+  }
+  P = FpM_ker(RgM_Rg_add_shallow(Tv, stoi(-g)), gell);
+  lW = lg(P); vecW = cgetg(lW,t_VEC);
+  for (j=1; j<lW; j++) gel(vecW,j) = famat_factorback(vselmer, gel(P,j));
+  /* step 6 */
+  if (DEBUGLEVEL>2) err_printf("Step 6\n");
+  Q = FpM_ker(RgM_Rg_add_shallow(shallowtrans(Tc), stoi(-g)), gell);
+  /* step 8 */
+  if (DEBUGLEVEL>2) err_printf("Step 8\n");
+  p1 = RgXQ_matrix_pow(COMPO.p, degKz, degK, COMPO.R);
+  T.invexpoteta1 = RgM_inv(p1); /* left inverse */
+  T.polnf = polnf;
+  T.tau = tau;
+  T.m = m;
+
+  idealz = lifttoKz(nfz, nf, ideal, &COMPO);
+  if (smodis(gcoeff(ideal,1,1), ell)) gothf = idealz;
+  else
+  { /* ell | N(ideal) */
+    GEN bnrz = Buchray(bnfz, idealz, nf_INIT|nf_GEN);
+    GEN subgroupz = invimsubgroup(bnrz, bnr, subgroup, &T);
+    gothf = bnrconductor(bnrz,subgroupz,0);
+  }
+  /* step 9, 10, 11 */
+  if (DEBUGLEVEL>2) err_printf("Step 9, 10 and 11\n");
+  i = build_list_Hecke(&L, nfz, NULL, gothf, gell, tau);
+  if (i) return no_sol(all,i);
+
+  lSml2 = lg(L.Sml2)-1;
+  Sp = shallowconcat(L.Sm, L.Sml1); lSp = lg(Sp)-1;
+  listprSp = shallowconcat(L.Sml2, L.Sl); lSl2 = lg(listprSp)-1;
+
+  /* step 12 */
+  if (DEBUGLEVEL>2) err_printf("Step 12\n");
+  vecAp = cgetg(lSp+1, t_VEC);
+  vecBp = cgetg(lSp+1, t_VEC);
+  matP  = cgetg(lSp+1, t_MAT);
+  for (j=1; j<=lSp; j++)
+  {
+    GEN e, a, ap;
+    p1 = isprincipalell(bnfz, gel(Sp,j), cycgen,u,gell,rc);
+    e = gel(p1,1); gel(matP,j) = e;
+    a = gel(p1,2);
+    p2 = famat_mul(famat_factorback(vecC, gneg(e)), a);
+    gel(vecBp,j) = p2;
+    ap = cgetg(1, t_MAT);
+    for (i=0; i<m; i++)
+    {
+      ap = famat_mul(ap, famat_pow(p2, utoi(Fl_powu(g,m-1-i,ell))));
+      if (i < m-1) p2 = tauofelt(p2, tau);
+    }
+    gel(vecAp,j) = ap;
+  }
+  /* step 13 */
+  if (DEBUGLEVEL>2) err_printf("Step 13\n");
+  vecWA = shallowconcat(vecW, vecAp);
+  vecWB = shallowconcat(vecW, vecBp);
+
+  /* step 14, 15, and 17 */
+  if (DEBUGLEVEL>2) err_printf("Step 14, 15 and 17\n");
+  mginv = (m * Fl_inv(g,ell)) % ell;
+  vecMsup = cgetg(lSml2+1,t_VEC);
+  M = NULL;
+  for (i=1; i<=lSl2; i++)
+  {
+    GEN pr = gel(listprSp,i);
+    long e = pr_get_e(pr), z = ell * (e / (ell-1));
+
+    if (i <= lSml2)
+    {
+      z += 1 - L.ESml2[i];
+      gel(vecMsup,i) = logall(nfz, vecWA,lW,mginv,ell, pr,z+1);
+    }
+    M = vconcat(M, logall(nfz, vecWA,lW,mginv,ell, pr,z));
+  }
+  dc = lg(Q)-1;
+  if (dc)
+  {
+    GEN QtP = gmul(shallowtrans(Q), matP);
+    M = vconcat(M, shallowconcat(zero_Flm(dc,lW-1), ZM_to_Flm(QtP,ell)));
+  }
+  if (!M) M = zero_Flm(1, lSp + lW - 1);
+  /* step 16 */
+  if (DEBUGLEVEL>2) err_printf("Step 16\n");
+  K = Flm_ker(M, ell);
+  if (all < 0)
+    K = fix_kernel(K, M, vecMsup, lW, ell);
+  /* step 18 & ff */
+  if (DEBUGLEVEL>2) err_printf("Step 18\n");
+  dK = lg(K)-1;
+  y = cgetg(dK+1,t_VECSMALL);
+  if (all) res = cgetg(1, t_VEC);
+  if (DEBUGLEVEL) timer_printf(&t, "[rnfkummer] candidate list");
+  if (all < 0) { ncyc = dK; rk = 0; mat = zero_Flm(lg(M)-1, ncyc); }
+
+  do {
+    dK = lg(K)-1;
+    while (dK)
+    {
+      for (i=1; i<dK; i++) y[i] = 0;
+      y[i] = 1; /* y = [0,...,0,1,0,...,0], 1 at dK'th position */
+      do
+      { /* cf. algo 5.3.18 */
+        GEN H, be, P, X = Flm_Flc_mul(K, y, ell);
+        if (ok_congruence(X, ell, lW, vecMsup))
+        {
+          pari_sp av = avma;
+          if (all < 0)
+          {
+            gel(mat, rk+1) = X;
+            if (Flm_rank(mat,ell) <= rk) continue;
+            rk++;
+          }
+          be = compute_beta(X, vecWB, gell, bnfz);
+          P = compute_polrel(nfz, &T, be, g, ell);
+          nfX_Z_normalize(nf, P);
+          if (DEBUGLEVEL>1) err_printf("polrel(beta) = %Ps\n", P);
+          if (!all) {
+            H = rnfnormgroup(bnr, P);
+            if (ZM_equal(subgroup, H)) return P; /* DONE */
+            avma = av; continue;
+          } else {
+            GEN P0 = Q_primpart(lift(P));
+            GEN g = nfgcd(P0, RgX_deriv(P0), polnf, nf_get_index(nf));
+            if (degpol(g)) continue;
+            H = rnfnormgroup(bnr, P);
+            if (!ZM_equal(subgroup,H) && !bnrisconductor(bnr,H)) continue;
+          }
+          P = gerepilecopy(av, P);
+          res = shallowconcat(res, P);
+          if (all < 0 && rk == ncyc) return res;
+          if (firstpass) break;
+        }
+      } while (increment(y, dK, ell));
+      y[dK--] = 0;
+    }
+  } while (firstpass--);
+  if (all) return res;
+  return gen_0; /* FAIL */
+}
+
+GEN
+rnfkummer(GEN bnr, GEN subgroup, long all, long prec)
+{
+  pari_sp av = avma;
+  return gerepilecopy(av, _rnfkummer(bnr, subgroup, all, prec));
+}
diff --git a/src/modules/mpqs.c b/src/modules/mpqs.c
new file mode 100644
index 0000000..aa02241
--- /dev/null
+++ b/src/modules/mpqs.c
@@ -0,0 +1,3136 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Written by Thomas Papanikolaou and Xavier Roblot
+ *
+ * Implementation of the Self-Initializing Multi-Polynomial Quadratic Sieve
+ * based on code developed as part of the LiDIA project
+ * (http://www.informatik.tu-darmstadt.de/TI/LiDIA/)
+ *
+ * Extensively modified by The PARI group.
+ */
+/* Notation commonly used in this file, and sketch of algorithm:
+ *
+ * Given an odd integer N > 1 to be factored, we throw in a small odd and
+ * squarefree multiplier k so as to make kN congruent 1 mod 4 and to have
+ * many small primes over which X^2 - kN splits.  We compute a factor base
+ * FB of such primes, and then essentially look for values x0 such that
+ * Q0(x0) = x0^2 - kN can be decomposed over this factor base, up to a
+ * possible factor dividing k and a possible "large prime".  Relations
+ * involving the latter can be combined into full relations (working mod N
+ * now) which don't, and full relations, by Gaussian elimination over the
+ * 2-element field for the exponent vectors, will finally lead us to an
+ * expression X^2 - Y^2 divisible by N and hopefully to a nontrivial
+ * splitting when we compute gcd(X +- Y, N).  Note that this can never
+ * split prime powers.  (Any odd prime dividing the X - Y factor, say, will
+ * divide it to the same power as it divides N.)
+ *
+ * Candidates x0 are found by sieving along arithmetic progressions modulo
+ * the small primes in the factor base, and evaluation of candidates picks
+ * out those x0 where many of these APs happen to coincide, resulting in a
+ * highly divisible Q0(x0).
+ *
+ * The Multi-Polynomial version improves this by choosing a modest subset of
+ * factor base primes and forcing these to divide Q0(x).  Let A be the product
+ * of the chosen primes, and write Q(x) = Q0(2Ax + B) = (2Ax + B)^2 - kN =
+ * 4A(Ax^2 + Bx + C), where B has been suitably chosen.  For each A, there
+ * are 2^omega_A possible values for B when A is the product of omega_A
+ * distinct primes, but we'll use only half of these, since the other half
+ * is more easily covered by exploiting the symmetry x <-> -x of the original
+ * quadratic.  The "Self-Initializating" bit refers to the fact that switching
+ * from one B to the next can be done very fast, whereas switching to the
+ * next A involves some recomputation from scratch.  (C is never needed
+ * explicitly except for debug diagnostics.)  Thus we can very quickly run
+ * through an entire "cohort" of polynomials sharing the same A.
+ *
+ * The sieve now ranges over values x0 such that |x0| < M  (we use x = x0 + M
+ * as the non-negative array subscript).  The coefficients A are chosen so
+ * that A*M is approximately sqrt(kN).  Then |B| will be bounded by about
+ * (j+4)*A, and |C| = -C will be about (M/4)*sqrt(kN), so Q(x0)/(4A) will
+ * take values roughly between -|C| and 3|C|.
+ *
+ * There are numerous refinements to this basic outline  (e.g. it is more
+ * efficient to _not_ use the very smallest primes in the FB for sieving,
+ * incorporating them only after selecting candidates).  The substition of
+ * 2Ax+B into X^2 - kN, with odd B, forces 2 to occur;  when kN is 1 mod 8,
+ * it will always occur at least to the 3rd power;  when kN = 5 mod 8, it
+ * will always occur exactly to the 2nd power.  We never sieve on 2 and we
+ * always pull out the power of 2 directly  (which is easy, of course).
+ * The prime factor(s) of k will show up whenever 2Ax + B has a factor in
+ * common with k;  we don't sieve on these either but can easily recognize
+ * them in a candidate.
+ */
+
+#include "pari.h"
+#include "paripriv.h"
+
+static char *
+paristrtok_r(char *str, const char *delim, char **saveptr)
+{
+  char *res;
+  if (!str) str = *saveptr;
+  str += strspn(str, delim);
+  if (!*str) return NULL;
+  res = str;
+  str += strcspn(str, delim);
+  if (*str) *str++ = 0;
+  *saveptr = str;
+  return res;
+}
+
+/** DEBUG **/
+/* #define MPQS_DEBUG_VERBOSE 1 */
+/* histograms are pretty, but don't help performance after all (see below) */
+/* #define MPQS_USE_HISTOGRAMS */
+
+#include "mpqs.h"
+
+/*********************************************************************/
+/**                                                                 **/
+/**                         INITIAL SIZING                          **/
+/**                                                                 **/
+/*********************************************************************/
+/* number of decimal digits of argument - for parameter choosing and for
+ * diagnostics */
+static long
+decimal_len(GEN N)
+{
+  pari_sp av = avma;
+  long d = strlen(itostr(N));
+  avma = av; return d;
+}
+
+/* To be called after choosing k and putting kN into the handle:
+ * Pick up the requested parameter set for the given size of kN in decimal
+ * digits and fill in various fields in the handle.  Return 0 when kN is
+ * too large, 1 when we're ok. */
+static int
+mpqs_set_parameters(mpqs_handle_t *h)
+{
+  long i;
+  const mpqs_parameterset_t *P;
+  double mb;
+
+  h->digit_size_kN = decimal_len(h->kN);
+  if (h->digit_size_kN <= 9)
+    i = 0;
+  else if (h->digit_size_kN > MPQS_MAX_DIGIT_SIZE_KN)
+    return 0;
+  else
+    i = h->digit_size_kN - 9;
+
+  /* (cf PARI bug#235) the following has always been, and will remain,
+   * a moving target... increased thresholds from 64, 80 to 79, 86
+   * respectively --GN20050601.  Note that the new values correspond to
+   * kN having >= 86 or >= 95 decimal digits, respectively.  Note also
+   * that the current sizing parameters for 90 or more digits are based
+   * on 100% theory and 0% practice. */
+  if (i >= 79)
+    pari_warn(warner, "MPQS: factoring this number will take %s hours:\nN = %Ps",
+        i >= 86 ? "many": "several", h->N);
+
+  if (DEBUGLEVEL >= 5)
+  {
+    err_printf("MPQS: kN = %Ps\n", h->kN);
+    err_printf("MPQS: kN has %ld decimal digits\n", h->digit_size_kN);
+  }
+
+  P = &(mpqs_parameters[i]);
+  h->tolerance        = P->tolerance;
+  h->lp_scale         = P->lp_scale;
+  /* make room for prime factors of k if any: */
+  h->size_of_FB       = P->size_of_FB + h->_k->omega_k;
+  /* for the purpose of Gauss elimination etc., prime factors of k behave
+   * like real FB primes, so take them into account when setting the goal: */
+  h->target_no_rels   = (h->size_of_FB >= 200 ?
+                         h->size_of_FB + 70 :
+                         (mpqs_int32_t)(h->size_of_FB * 1.35));
+  h->M                = P->M;
+  h->omega_A          = P->omega_A;
+  h->no_B             = 1UL << (P->omega_A - 1);
+  h->pmin_index1      = P->pmin_index1;
+  /* certain subscripts into h->FB should also be offset by omega_k: */
+  h->index0_FB        = 3 + h->_k->omega_k;
+  /* following are converted from % to parts per thousand: */
+  h->first_sort_point = 10 * P->first_sort_point;
+  h->sort_pt_interval = 10 * P->sort_pt_interval;
+
+  mb = (h->size_of_FB + 1)/(8.*1048576.) * h->target_no_rels;
+  if (mb > 128.)
+  {
+    pari_warn(warner,
+        "MPQS: Gauss elimination will require more than\n\t128MBy of memory");
+    if (DEBUGLEVEL >= 1)
+      err_printf("\t(estimated memory needed: %4.1fMBy)\n", mb);
+  }
+
+  return 1;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                       OBJECT HOUSEKEEPING                       **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* The sub-constructors for the pieces of the handle will be called in the
+ * same order as their appearance here, and the later ones in part rely on
+ * the earlier ones having filled in some fields.
+ * There's a single destructor to handle all cleanup at the end  (except
+ * for mpqs() itself resetting avma). */
+
+/* main handle constructor */
+static mpqs_handle_t *
+mpqs_handle_ctor(GEN N)
+{
+  mpqs_handle_t *h = (mpqs_handle_t *) pari_calloc(sizeof(mpqs_handle_t));
+  h->N = N;
+#ifdef MPQS_DEBUG_VERBOSE
+  err_printf("MPQS DEBUG: created handle @0x%p\n", (void *)h);
+#endif
+  return h;
+}
+
+/* factor base constructor. Really a home-grown memalign(3c) underneath.
+ * We don't want FB entries to straddle L1 cache line boundaries, and
+ * malloc(3c) only guarantees alignment adequate for all primitive data
+ * types of the platform ABI - typically to 8 or 16 byte boundaries.
+ * Also allocate the inv_A_H array.
+ * The FB array pointer is returned for convenience */
+static mpqs_FB_entry_t *
+mpqs_FB_ctor(mpqs_handle_t *h)
+{
+  /* leave room for slots 0, 1, and sentinel slot at the end of the array */
+  long size_FB_chunk = (h->size_of_FB + 3) * sizeof(mpqs_FB_entry_t);
+  /* like FB, except this one does not have a sentinel slot at the end */
+  long size_IAH_chunk = (h->size_of_FB + 2) * sizeof(mpqs_inv_A_H_t);
+  char *fbp = (char*)pari_malloc(size_FB_chunk + 64);
+  char *iahp = (char*)pari_malloc(size_IAH_chunk + 64);
+  long fbl, iahl;
+
+  h->FB_chunk = (void *)fbp;
+  h->invAH_chunk = (void *)iahp;
+  /* round up to next higher 64-bytes-aligned address */
+  fbl = (((long)fbp) + 64) & ~0x3FL;
+  /* and put the actual array there */
+  h->FB = (mpqs_FB_entry_t *)fbl;
+
+  iahl = (((long)iahp) + 64) & ~0x3FL;
+  h->inv_A_H = (mpqs_inv_A_H_t *)iahl;
+  return (mpqs_FB_entry_t *)fbl;
+}
+
+/* sieve array constructor;  also allocates the candidates array, the
+ * histograms, and temporary storage for relations under construction */
+static void
+mpqs_sieve_array_ctor(mpqs_handle_t *h)
+{
+  long size = (h->M << 1) + 1;
+  mpqs_int32_t size_of_FB = h->size_of_FB;
+
+  h->sieve_array = (unsigned char *) pari_malloc(size * sizeof(unsigned char));
+  h->sieve_array_end = h->sieve_array + size - 2;
+  h->sieve_array_end[1] = 255; /* sentinel */
+  h->candidates = (long *)pari_malloc(MPQS_CANDIDATE_ARRAY_SIZE * sizeof(long));
+
+  /* Room needed for string representation of a relation - worst case:
+   * + leading " 1 1"
+   * + trailing " 0\n" with final NUL character
+   * + in between up to size_of_FB pairs each consisting of an exponent, a
+   *   subscript into FB, and two spaces.
+   * Subscripts into FB fit into 5 digits, and exponents fit into 3 digits
+   * with room to spare -- anything needing 3 or more digits for the
+   * subscript must come with an exponent of at most 2 digits. Moreover the
+   * product of the first 58 primes is larger than 10^110  (and the righthand
+   * sides of proto-relations are much smaller than kN: on the order of
+   * M*sqrt(kN)),  so there cannot be more than 60 pairs in all, even if
+   * size_of_FB > 10^5. --GN */
+
+  /* whereas mpqs_self_init() uses size_of_FB+1, we just use the size as
+   * it is, not counting FB[1], to start off the following estimate */
+  if (size_of_FB > 60) size_of_FB = 60;
+  h->relations = (char *) pari_malloc((8 + size_of_FB * 9) * sizeof(char));
+  /* and for tracking which primes occur in the current relation: */
+  h->relaprimes = (long *) pari_malloc((size_of_FB << 1) * sizeof(long));
+
+#ifdef MPQS_USE_HISTOGRAMS
+  /* histograms to be used only when kN isn't very small */
+  if (h->size_of_FB > MPQS_MIN_SIZE_FB_FOR_HISTO) {
+    h->do_histograms = 1;
+    h->histo_full = (long *) pari_calloc(128 * sizeof(long));
+    h->histo_lprl = (long *) pari_calloc(128 * sizeof(long));
+    h->histo_drop = (long *) pari_calloc(128 * sizeof(long));
+  }
+#endif
+}
+
+/* mpqs() calls the following (after recording avma) to allocate GENs for
+ * the current polynomial and self-initialization scratchpad data on the
+ * PARI stack.  This space is released by mpqs() itself at the end. */
+static void
+mpqs_poly_ctor(mpqs_handle_t *h)
+{
+  mpqs_int32_t i;
+  long size_per = h->omega_A * sizeof(mpqs_per_A_prime_t);
+
+  h->per_A_pr = (mpqs_per_A_prime_t *) pari_calloc(size_per);
+  /* Sizing:  A is the product of omega_A primes, each well below word
+   * size.
+   * |B| is bounded by (omega_A + 4) * A, so can have at most one word
+   * more, and that's generous.
+   * |C| is less than A*M^2, so can take at most two words more than A.
+   * The array H holds residues modulo A, so the same size as used for A
+   * is sufficient. */
+  h->A = cgeti(h->omega_A + 2);
+  h->B = cgeti(h->omega_A + 3);
+#ifdef MPQS_DEBUG
+  h->C = cgeti(h->omega_A + 4);
+#endif
+  for (i = 0; i < h->omega_A; i++)
+    h->per_A_pr[i]._H = cgeti(h->omega_A + 2);
+  /* the handle starts out all zero, so in particular bin_index and index_i
+   * are initially 0.
+   * TODO: index_j currently initialized in mqps() but this is going to
+   * change. */
+}
+
+/* main handle destructor, also cleans up all other allocated pieces
+ * (except for stuff created on the PARI stack which the caller should
+ * deal with by resetting avma) */
+static void
+mpqs_handle_dtor(mpqs_handle_t *h)
+{
+#define myfree(x) if(x) pari_free((void*)x)
+  myfree((h->per_A_pr));
+  myfree((h->relaprimes));
+  myfree(h->relations);
+
+#ifdef MPQS_USE_HISTOGRAMS
+  myfree((h->histo_drop));
+  myfree((h->histo_lprl));
+  myfree((h->histo_full));
+#endif
+
+  myfree((h->candidates));
+  myfree((h->sieve_array));
+  myfree((h->invAH_chunk));
+  myfree((h->FB_chunk));
+  myfree(h);
+}
+
+/* TODO: relationsdb handle */
+
+/*********************************************************************/
+/**                                                                 **/
+/**                        FACTOR BASE SETUP                        **/
+/**                                                                 **/
+/*********************************************************************/
+/* fill in the best-guess multiplier k for N. We force kN = 1 mod 4.
+ * Caller should proceed to fill in kN */
+static ulong
+mpqs_find_k(mpqs_handle_t *h)
+{
+  const pari_sp av = avma;
+  const long N_mod_8 = mod8(h->N), N_mod_4 = N_mod_8 & 3;
+  forprime_t S;
+  struct {
+    const mpqs_multiplier_t *_k;
+    long np; /* number of primes in factorbase so far for this k */
+    double value; /* the larger, the better */
+  } cache[MPQS_POSSIBLE_MULTIPLIERS];
+  ulong p, i, j, nbk = MPQS_POSSIBLE_MULTIPLIERS;
+
+  for (i = j = 0; i < sizeof(cand_multipliers)/sizeof(mpqs_multiplier_t); i++)
+  {
+    const mpqs_multiplier_t *cand_k = &cand_multipliers[i];
+    long k = cand_k->k;
+    double v;
+    if ((k & 3) != N_mod_4) continue; /* want kN = 1 (mod 4) */
+    v = -0.35 * log2((double)k);
+    if ((k & 7) == N_mod_8) v += LOG2; /* kN = 1 (mod 8) */
+    cache[j].np = 0;
+    cache[j]._k = cand_k;
+    cache[j].value = v;
+    if (++j == nbk) break; /* enough */
+  }
+  nbk = j;
+  u_forprime_init(&S, 2, ULONG_MAX);
+  while ( (p = u_forprime_next(&S)) )
+  {
+    ulong Np = umodiu(h->N, p);
+    long kroNp, seen = 0;
+    if (!Np) return p;
+    kroNp = krouu(Np, p);
+    for (i = 0; i < nbk; i++)
+    {
+      if (cache[i].np > MPQS_MULTIPLIER_SEARCH_DEPTH) continue;
+      seen++;
+      if (krouu(cache[i]._k->k % p, p) == kroNp) /* kronecker(k*N, p)=1 */
+      {
+        cache[i].value += log2((double) p)/p;
+        cache[i].np++;
+      }
+    }
+    if (!seen) break; /* we're gone through SEARCH_DEPTH primes for all k */
+  }
+  if (!p) pari_err_OVERFLOW("mpqs_find_k [ran out of primes]");
+  {
+    long best_i = 0;
+    double v = cache[0].value;
+    for (i = 1; i < nbk; i++)
+      if (cache[i].value > v) { best_i = i; v = cache[i].value; }
+    h->_k = cache[best_i]._k; avma = av; return 0;
+  }
+}
+
+/******************************/
+/* Create a factor base of 'size' primes p_i such that legendre(k*N, p_i) != -1
+ * We could have shifted subscripts down from their historical arrangement,
+ * but this seems too risky for the tiny potential gain in memory economy.
+ * The real constraint is that the subscripts of anything which later shows
+ * up at the Gauss stage must be nonnegative, because the exponent vectors
+ * there use the same subscripts to refer to the same FB entries.  Thus in
+ * particular, the entry representing -1 could be put into FB[0], but could
+ * not be moved to FB[-1] (although mpqs_FB_ctor() could be easily adapted
+ * to support negative subscripts).-- The historically grown layout is:
+ * FB[0] is unused.
+ * FB[1] is not explicitly used but stands for -1.
+ * FB[2] contains 2 (always).
+ * Before we are called, the size_of_FB field in the handle will already have
+ * been adjusted by _k->omega_k, so there's room for the primes dividing k,
+ * which when present will occupy FB[3] and following.
+ * The "real" odd FB primes begin at FB[h->index0_FB].
+ * FB[size_of_FB+1] is the last prime p_i.
+ * FB[size_of_FB+2] is a sentinel to simplify some of our loops.
+ * Thus we allocate size_of_FB+3 slots for FB.
+ *
+ * If a prime factor of N is found during the construction, it is returned
+ * in f, otherwise f = 0. */
+
+/* returns the FB array pointer for convenience */
+static mpqs_FB_entry_t *
+mpqs_create_FB(mpqs_handle_t *h, ulong *f)
+{
+  const pari_sp av = avma;
+  mpqs_int32_t size = h->size_of_FB;
+  long i;
+  mpqs_uint32_t k = h->_k->k;
+  mpqs_FB_entry_t *FB;
+  forprime_t S;
+
+  FB = mpqs_FB_ctor(h);
+
+  FB[2].fbe_p = 2;
+  /* the fbe_logval and the fbe_sqrt_kN for 2 are never used */
+  FB[2].fbe_flags = MPQS_FBE_CLEAR;
+  (void)u_forprime_init(&S, 3, ULONG_MAX);
+
+  /* the first loop executes h->_k->omega_k = 0, 1, or 2 times */
+  for (i = 3; i < h->index0_FB; i++)
+  {
+    mpqs_uint32_t kp = (ulong)h->_k->kp[i-3];
+    if (MPQS_DEBUGLEVEL >= 7) err_printf(",<%lu>", (ulong)kp);
+    FB[i].fbe_p = kp;
+    /* we *could* flag divisors of k here, but so far I see no need,
+     * and no flags bit has been assigned for the purpose */
+    FB[i].fbe_flags = MPQS_FBE_CLEAR;
+    FB[i].fbe_flogp = (float) log2((double) kp);
+    FB[i].fbe_sqrt_kN = 0;
+  }
+  /* now i == h->index0_FB */
+  while (i < size + 2)
+  {
+    ulong p = u_forprime_next(&S);
+    if (p > k || k % p)
+    {
+      ulong kN_mod_p = umodiu(h->kN, p);
+      long kr = krouu(kN_mod_p, p);
+      if (kr != -1)
+      {
+        if (kr == 0) { *f = p; return FB; }
+        FB[i].fbe_p = (mpqs_uint32_t) p;
+        FB[i].fbe_flags = MPQS_FBE_CLEAR;
+        /* dyadic logarithm of p; single precision suffices */
+        FB[i].fbe_flogp = (float) log2((double)p);
+        /* cannot yet fill in fbe_logval because the scaling multiplier
+         * depends on the largest prime in FB, as yet unknown */
+
+        /* x such that x^2 = kN (mod p_i) */
+        FB[i++].fbe_sqrt_kN = (mpqs_uint32_t)Fl_sqrt(kN_mod_p, p);
+      }
+    }
+  }
+  avma = av;
+  if (MPQS_DEBUGLEVEL >= 7)
+  {
+    err_printf("MPQS: FB [-1,2");
+    for (i = 3; i < h->index0_FB; i++) err_printf(",<%lu>", FB[i].fbe_p);
+    for (; i < size + 2; i++) err_printf(",%lu", FB[i].fbe_p);
+    err_printf("]\n");
+  }
+
+  FB[i].fbe_p = 0;              /* sentinel */
+  h->largest_FB_p = FB[i-1].fbe_p; /* at subscript size_of_FB + 1 */
+
+  /* locate the smallest prime that will be used for sieving */
+  for (i = h->index0_FB; FB[i].fbe_p != 0; i++)
+    if (FB[i].fbe_p >= h->pmin_index1) break;
+  h->index1_FB = i;
+  /* with our parameters this will never fall of the end of the FB */
+  *f = 0; return FB;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                      MISC HELPER FUNCTIONS                      **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* Effect of the following:  multiplying the base-2 logarithm of some
+ * quantity by log_multiplier will rescale something of size
+ *    log2 ( sqrt(kN) * M / (largest_FB_prime)^tolerance )
+ * to 232.  Note that sqrt(kN) * M is just A*M^2, the value our polynomials
+ * take at the outer edges of the sieve interval.  The scale here leaves
+ * a little wiggle room for accumulated rounding errors from the approximate
+ * byte-sized scaled logarithms for the factor base primes which we add up
+ * in the sieving phase.-- The threshold is then chosen so that a point in
+ * the sieve has to reach a result which, under the same scaling, represents
+ *    log2 ( sqrt(kN) * M / (largest_FB_prime)^tolerance )
+ * in order to be accepted as a candidate. */
+/* The old formula was...
+ *   log_multiplier =
+ *      127.0 / (0.5 * log2 (handle->dkN)
+ *               + log2((double)M)
+ *               - tolerance * log2((double)handle->largest_FB_p)
+ *               );
+ * and we used to use this with a constant threshold of 128. */
+
+/* NOTE: We used to divide log_multiplier by an extra factor 2, and in
+ * compensation we were multiplying by 2 when the fbe_logp fields were being
+ * filled in, making all those bytes even.  Tradeoff: the extra bit of
+ * precision is helpful, but interferes with a possible sieving optimization
+ * (artifically shift right the logp's of primes in A, and just run over both
+ * arithmetical progressions  (which coincide in this case)  instead of
+ * skipping the second one, to avoid the conditional branch in the
+ * mpqs_sieve() loops).  We could still do this, but might lose a little bit
+ * accuracy for those primes.  Probably no big deal. */
+static void
+mpqs_set_sieve_threshold(mpqs_handle_t *h)
+{
+  mpqs_FB_entry_t *FB = h->FB;
+  long i;
+  double log_maxval;
+  double log_multiplier;
+
+  h->l2sqrtkN = 0.5 * log2(h->dkN);
+  h->l2M = log2((double)h->M);
+  log_maxval = h->l2sqrtkN + h->l2M - MPQS_A_FUDGE;
+  log_multiplier = 232.0 / log_maxval;
+  h->sieve_threshold =
+    (unsigned char) (log_multiplier *
+                     (log_maxval
+                      - h->tolerance * log2((double)h->largest_FB_p)
+                      )
+                     ) + 1;
+  /* That "+ 1" really helps - we may want to tune towards somewhat smaller
+   * tolerances  (or introduce self-tuning one day)... */
+
+  /* If this turns out to be <128, scream loudly.
+   * That means that the FB or the tolerance or both are way too
+   * large for the size of kN.  (Normally, the threshold should end
+   * up in the 150...170 range.) */
+  if (h->sieve_threshold < 128) {
+    h->sieve_threshold = 128;
+    pari_warn(warner,
+        "MPQS: sizing out of tune, FB size or tolerance\n\ttoo large");
+  }
+
+  /* Now fill in the byte-sized approximate scaled logarithms of p_i */
+  if (DEBUGLEVEL >= 5)
+  {
+    err_printf("MPQS: computing logarithm approximations for p_i in FB\n");
+  }
+  for (i = h->index0_FB; i < h->size_of_FB + 2; i++)
+  {
+    FB[i].fbe_logval =
+      (unsigned char) (log_multiplier * FB[i].fbe_flogp);
+  }
+}
+
+/* Given the partially populated handle, find the optimum place in the FB
+ * to pick prime factors for A from.  The lowest admissible subscript is
+ * index0_FB, but unless kN is very small, we stay away a bit from that.
+ * The highest admissible is size_of_FB + 1, where the largest FB prime
+ * resides.  The ideal corner is about (sqrt(kN)/M) ^ (1/omega_A),
+ * so that A will end up of size comparable to sqrt(kN)/M;  experimentally
+ * it seems desirable to stay slightly below this.  Moreover, the selection
+ * of the individual primes happens to err on the large side, for which we
+ * compensate a bit, using the (small positive) quantity MPQS_A_FUDGE.
+ * We rely on a few auxiliary fields in the handle to be already set by
+ * mqps_set_sieve_threshold() before we are called.
+ * Return 1 on success, and 0 otherwise. */
+static int
+mpqs_locate_A_range(mpqs_handle_t *h)
+{
+  /* i will be counted up to the desirable index2_FB + 1, and omega_A is never
+   * less than 3, and we want
+   *   index2_FB - (omega_A - 1) + 1 >= index0_FB + omega_A - 3,
+   * so: */
+  long i = h->index0_FB + 2*(h->omega_A) - 4;
+  double l2_target_pA;
+  mpqs_FB_entry_t *FB = h->FB;
+
+  h->l2_target_A = (h->l2sqrtkN - h->l2M - MPQS_A_FUDGE);
+  l2_target_pA = h->l2_target_A / h->omega_A;
+
+  /* find the sweet spot, normally shouldn't take long */
+  while ((FB[i].fbe_p != 0) && (FB[i].fbe_flogp <= l2_target_pA)) i++;
+
+#ifdef MPQS_DEBUG_LOCATE_A_RANGE
+  err_printf("MPQS DEBUG: omega_A=%ld, index0=%ld, i=%ld\n",
+             (long) h->omega_A, (long) h->index0_FB, i);
+#endif
+
+  /* check whether this hasn't walked off the top end... */
+  /* The following should actually NEVER happen. */
+  if (i > h->size_of_FB - 3)
+  { /* this isn't going to work at all. */
+    pari_warn(warner,
+        "MPQS: sizing out of tune, FB too small or\n\tway too few primes in A");
+    return 0;
+  }
+  h->index2_FB = i - 1;
+#ifdef MPQS_DEBUG_LOCATE_A_RANGE
+  err_printf("MPQS DEBUG: index2_FB = %ld\n", i - 1);
+#endif
+  /* GN20050723
+   * assert: index0_FB + (omega_A - 3) [the lowest FB subscript eligible to
+   * be used in picking primes for A]  plus  (omega_A - 2)  does not exceed
+   * index2_FB  [the subscript from which the choice of primes for A starts,
+   * putting omega_A - 1 of them at or below index2_FB, and the last and
+   * largest one above, cf. mpqs_si_choose_primes() below].
+   * Moreover, index2_FB indicates the last prime below the ideal size, unless
+   * (when kN is very small) the ideal size was too small to use. */
+
+  return 1;
+}
+
+
+/*********************************************************************/
+/**                                                                 **/
+/**                HISTOGRAMS AND THRESHOLD FEEDBACK                **/
+/**                                                                 **/
+/*********************************************************************/
+
+#ifdef MPQS_USE_HISTOGRAMS
+
+/* The histogram-related code is left in this file, but all under the
+ * above #ifdef, and disabled by default.  I'm finding that:
+ * - merely keeping the numbers updated in mpqs_eval_cand() below  (and
+ *   keeping the "negligible" 1.5 or 3KBys' worth of extra arrays in use)
+ *   causes us to run quite noticeably slower: 8-10% for a 73-digit number,
+ * - mpqs_eval_cand() has already become so much faster than it used to be
+ *   that raising the threshold to get rid of many low-valued unpromising
+ *   candidates does not save any significant time, and even losing a pretty
+ *   small number of additional LP relations actually harms us by lowering
+ *   the efficiency of LP relation combining.
+ * (The first point might be due merely to code bloat and less effective
+ * compiler optimizations - I'm not sure about that.)
+ * Just for getting a visual impression of how the sieve is performing,
+ * however, this is nice to have available.  Just turn on the #define at
+ * the top of the file and recompile with it. --GN2005-02-03
+ */
+
+/* histogram evaluation happens very infrequently if at all.  So we'll do
+ * all the adding and putting-into-relation-with here, while mpqs_eval_cand()
+ * merely bumps one cell at a time and doesn't keep running totals. */
+
+static void
+mpqs_print_histo(mpqs_handle_t *h)
+{
+  long i, tot = 0;
+
+  if (!h->do_histograms) return;
+
+  err_printf("\nMPQS: values from sieve vs. distribution of evaluated candidates:\n");
+  err_printf("   val  ___full __lprel ___none ___total\n");
+  for (i = 127; i >= 0; i--)
+  {
+    long rowtot = h->histo_full[i] + h->histo_lprl[i] + h->histo_drop[i];
+    tot += rowtot;
+    if ((rowtot > 0) || (i == h->sieve_threshold))
+      err_printf("%s[%3d] %7ld %7ld %7ld %8ld\n",
+                 i + 128 == h->sieve_threshold ? "^-" : "  ", i + 128,
+                 h->histo_full[i], h->histo_lprl[i], h->histo_drop[i],
+                 rowtot);
+  }
+  err_printf("        (total evaluated candidates: %ld)\n", tot);
+}
+
+/* evaluation/feedback heuristics:
+ * First of all, refuse to draw any conclusions unless and until there's
+ * enough material to be statistically significant.
+ * Second, after sifting through the histo arrays, the new threshold is
+ * set to the minimum of the following three quantities:
+ * - the position where going down from the top value the histo_full
+ *   totals first exceed 100 - MPQS_HISTO_FREL_QUANTILE percent of all
+ *   full relations found so far by direct sieving.  (I.e. if the quantile
+ *   is 4, we want to keep all rows which account for 96% of all frels
+ *   obtained from the sieve.  Note that once we increase the threshold,
+ *   further counts will be biased against smaller values;  but we normally
+ *   don't expect to do many adjustments.)
+ * - the position where, going down from the top towards smaller values,
+ *   the cumulative number of useless candidates in histo_drop first exceeds
+ *   MPQS_HISTO_DROP_LIMIT times the number of useful ones.  I.e. when that
+ *   limit is 2, we're aiming for at least about 1/3 of all candidates coming
+ *   from the sieve to result in usable relations.
+ * - one less than the position where the histo_lprl count first falls below
+ *   MPQS_HISTO_LPREL_BASEFLOW times the number of useless candidates.  This
+ *   one will be capable of lowering the current threshold  (but never below
+ *   128).
+ * FIXME: For Double Large Prime mode, this will need to be seriously reworked.
+ */
+
+/* This function returns 1 when it actually did something and decided on a
+ * good threshold  (although possibly the same as it was before),  -1 when
+ * there was nothing to do and never will be ("don't call us again"), 0
+ * when the caller should retry somewhat later.  Note that mpqs() already
+ * knows the total number of candidates generated so far  (from the return
+ * values of mpqs_eval_sieve()),  and won't call us too early;  but we also
+ * insist on minimal standards for the column sums.  Conversely when we ever
+ * lower the threshold, we ask for a re-evaluation later on.
+ * NB With the present accounting, once the threshold has been raised, it
+ * won't ever be lowered again, since the increasing counts above it will
+ * totally swamp the few earlier measurements below which can no longer
+ * grow.  So we might chop off those accumulating loops at the current sieve
+ * threshold. */
+static int
+mpqs_eval_histograms(mpqs_handle_t *h)
+{
+  long tot_full = 0, tot_lprl = 0, tot_drop = 0, total = 0;
+  long target_full, i;
+  int th_full, th_base, th_drop;
+  int th = h->sieve_threshold - 128;
+
+  if (!h->do_histograms) return -1;
+
+  /* first compute column sums */
+  for (i = 127; i >= 0; i--)
+  {
+    tot_full += h->histo_full[i];
+    tot_lprl += h->histo_lprl[i];
+    tot_drop += h->histo_drop[i];
+  }
+  total = tot_full + tot_lprl + tot_drop;
+  if ((total < MPQS_MIN_CANDS_FOR_HISTO) ||
+      (tot_full < MPQS_MIN_FRELS_FOR_HISTO))
+    return 0;                   /* too early to call the race */
+
+  th_full = th_drop = th_base = -1;
+  /* find the full relations quantile point */
+  target_full = tot_full - (tot_full * MPQS_HISTO_FREL_QUANTILE) / 100.;
+
+  tot_full = 0;
+  for (i = 127; i >= th; i--)
+  {
+    if ((tot_full += h->histo_full[i]) >= target_full)
+    {
+      th_full = i; break;
+    }
+  }
+
+  /* find the "lp relations baseflow" point */
+  for (i = 127; i >= th; i--)
+  {
+    if (h->histo_lprl[i] + 1 <
+        MPQS_HISTO_LPREL_BASEFLOW * h->histo_drop[i])
+    {
+      th_base = i; break;
+    }
+  }
+
+  /* find the wastefulness point */
+  tot_lprl = 0; tot_drop = 0;
+  for (i = 127; i >= th; i--)
+  {
+    tot_lprl += h->histo_full[i] + h->histo_lprl[i];
+    tot_drop += h->histo_drop[i];
+    if (tot_drop >
+        MPQS_HISTO_DROP_LIMIT * (tot_lprl + 1))
+    {
+      th_drop = i; break;
+    }
+  }
+  /* if these loops found nothing, then th_(full|base|drop) will still be -1.
+   * We won't tighten the sieve, but th_base would tell us we should loosen
+   * it  (reluctantly). */
+
+  if (MPQS_DEBUGLEVEL >= 5)
+  {
+    mpqs_print_histo(h);
+    if (th_full >= 0)
+      err_printf("MPQS: threshold estimate for full rels: %d\n",
+                 th_full + 128);
+    if (th_drop >= 0)
+      err_printf("MPQS: threshold estimate for useful candidates: %d\n",
+                 th_drop + 128);
+  }
+
+  /* any reason to open up the sieve?  wait until a minimal number of lprels
+   * at the threshold has been seen before going down... */
+  if ((th > 0) && (th_base <= th) &&
+      (h->histo_lprl[th] > (MPQS_MIN_FRELS_FOR_HISTO * 3.5)) )
+  {
+    h->sieve_threshold = th + 127;
+    if (MPQS_DEBUGLEVEL >= 4)
+      err_printf("MPQS: loosening sieve tolerance, new threshold %d\n",
+                 h->sieve_threshold);
+    return 0;                   /* this should be re-examined after a bit */
+  }
+  /* otherwise, any reason to tighten it? */
+  th = (th_full < th_drop ? th_full : th_drop) + 128;
+  if (th > h->sieve_threshold)
+  {
+    h->sieve_threshold = th;
+    if (MPQS_DEBUGLEVEL >= 4)
+      err_printf("MPQS: tightening sieve tolerance, new threshold %d\n",
+                 h->sieve_threshold);
+  }
+  /* maybe also loosen it if th_drop persistently stays below th... */
+  return 1;                     /* wait a good while before rechecking */
+}
+#endif
+
+/*********************************************************************/
+/**                                                                 **/
+/**           RELATIONS AS STRINGS AND RELATIONS DATABASE           **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* determines a unique name for a file based on a short nickname
+ * name is allocated on the stack */
+static char *
+mpqs_get_filename(char *dir, const char *s)
+{
+  char *buf = stack_malloc(strlen(dir) + strlen(s) + 2);
+#if defined(__EMX__) || defined(WINCE)
+  sprintf(buf, "%s\\%s", dir,s);
+#else
+  sprintf(buf, "%s/%s", dir,s);
+#endif
+  return buf;
+}
+
+/* compares two `large prime' relations according to their first element
+ * (the large prime itself). */
+static int
+mpqs_relations_cmp(const void *a, const void *b)
+{
+  char **sa = (char**) a;
+  char **sb = (char**) b;
+  long qa = strtol(*sa, NULL, 10);
+  long qb = strtol(*sb, NULL, 10);
+  /* atol() isn't entirely portable for the Full Relations case where the
+     strings of digits are too long to fit into a long --GN */
+  if (qa < qb) return -1;
+  else if (qa > qb) return 1;
+  else return strcmp(*sa, *sb);
+}
+
+static void
+pari_fputs(char *s, pariFILE *f)
+{
+  if (fputs(s, f->file) < 0) pari_err_FILE("output file [fputs]", f->name);
+}
+#define min_bufspace 120UL /* use new buffer when < min_bufspace left */
+#define buflist_size 1024  /* size of list-of-buffers blocks */
+
+/* Given a file "filename" containing full or `large prime' relations,
+ * rearrange the file so that relations are sorted by their first elements.
+ * Works also for sorting full relations. Works in memory, discards duplicate
+ * lines, and overwrites the original file. */
+static long
+mpqs_sort_lp_file(char *filename)
+{
+  pariFILE *pTMP;
+  FILE *TMP;
+  char *old_s, *buf, *cur_line;
+  char **sort_table, **buflist, **next_buflist, **buflist_head;
+  long i, j, count;
+  size_t length, bufspace;
+  pari_sp av=avma;
+
+  buflist_head = (char**) stack_malloc(buflist_size * sizeof(char*));
+  buflist = buflist_head;
+  *buflist++ = NULL; /* flag this as last and only buflist block */
+  /* extra blocks may be allocated as needed and linked ahead of
+   * buflist_head.  NB: whilst extra buflist blocks might have been
+   * needed when we were still sorting entire FREL files (more than 1023
+   * buffers, corresponding to about 20000 lines of ~200 characters), they
+   * should never be touched now that we only sort LPNEW and FNEW files, which
+   * are rather shorter. But the code might as well stay around for future
+   * upgrades to handling even larger numbers (and factor bases and thus
+   * relations files).  It costs one comparison per buffer allocation. --GN */
+
+  pTMP = pari_fopen_or_fail(filename, READ);
+  TMP = pTMP->file;
+  /* get first buffer and read first line, if any, into it */
+  buf = (char*) pari_malloc(MPQS_STRING_LENGTH * sizeof(char));
+  cur_line = buf;
+  bufspace = MPQS_STRING_LENGTH;
+
+  if (fgets(cur_line, bufspace, TMP) == NULL)
+  { /* file empty */
+    pari_free(buf); pari_fclose(pTMP);
+    avma = av; return 0;
+  }
+  /* enter first buffer into buflist */
+  *buflist++ = buf; /* can't overflow the buflist block */
+  length = strlen(cur_line) + 1; /* count the \0 byte as well */
+  bufspace -= length;
+
+  sort_table = (char**)avma;
+  /* at start of loop, one line from the file is sitting in cur_line inside buf,
+   * the next will go into cur_line + length, and there's room for bufspace
+   * further characters in buf. The loop reads another line if one exists, and
+   * if this overruns the current buffer, it allocates a fresh one --GN */
+  for (i=0, sort_table--; /* until end of file */; i++, sort_table--)
+  { /* sort_table is allocated on the stack, 0x100 cells at a time. Hence the
+     * stack must be left alone in the rest of the loop to keep the array
+     * connected. In particular, buffers can't be new_chunk'ed */
+    if ((i & 0xff) == 0) (void)new_chunk(0x100);
+    *sort_table = cur_line;
+    cur_line += length;
+
+    /* if little room is left, allocate a fresh buffer before attempting to
+     * read a line, and remember to free it if no further line is forthcoming.
+     * This avoids some copying of partial lines --GN */
+    if (bufspace < min_bufspace)
+    {
+      if (MPQS_DEBUGLEVEL >= 7)
+        err_printf("MQPS: short of space -- another buffer for sorting\n");
+      buf = (char*) pari_malloc(MPQS_STRING_LENGTH * sizeof(char));
+      cur_line = buf;
+      bufspace = MPQS_STRING_LENGTH;
+      if (fgets(cur_line, bufspace, TMP) == NULL) { pari_free(buf); break; }
+
+      /* remember buffer for later deallocation */
+      if (buflist - buflist_head >= buflist_size)
+      { /* need another buflist block */
+        next_buflist = (char**) pari_malloc(buflist_size * sizeof(char*));
+        *next_buflist = (char*)buflist_head; /* link */
+        buflist_head = next_buflist;
+        buflist = buflist_head + 1;
+      }
+      *buflist++ = buf;
+      length = strlen(cur_line) + 1;
+      bufspace -= length; continue;
+    }
+
+    /* normal case:  try fitting another line into the current buffer */
+    if (fgets(cur_line, bufspace, TMP) == NULL) break; /* none exists */
+    length = strlen(cur_line) + 1;
+    bufspace -= length;
+
+    /* check whether we got the entire line or only part of it */
+    if (bufspace == 0 && cur_line[length-2] != '\n')
+    {
+      size_t lg1;
+      if (MPQS_DEBUGLEVEL >= 7)
+        err_printf("MQPS: line wrap -- another buffer for sorting\n");
+      buf = (char*) pari_malloc(MPQS_STRING_LENGTH * sizeof(char));
+      /* remember buffer for later deallocation */
+      if (buflist - buflist_head >= buflist_size)
+      { /* need another buflist block */
+        next_buflist = (char**)pari_malloc(buflist_size * sizeof(char*));
+        *next_buflist = (char*)buflist_head; /* link */
+        buflist_head = next_buflist;
+        buflist = buflist_head + 1;
+      }
+      *buflist++ = buf;
+
+      /* copy what we've got to the new buffer */
+      (void)strcpy(buf, cur_line); /* cannot overflow */
+      cur_line = buf + length - 1; /* point at the \0 byte */
+      bufspace = MPQS_STRING_LENGTH - length + 1;
+      /* read remainder of line */
+      if (fgets(cur_line, bufspace, TMP) == NULL)
+        pari_err_FILE("TMP file [fgets]", pTMP->name);
+      lg1 = strlen(cur_line);
+      length += lg1; /* we already counted the \0 once */
+      bufspace -= (lg1 + 1); /* but here we must take it into account */
+      cur_line = buf; /* back up to the beginning of the line */
+    }
+  } /* for */
+
+  pari_fclose(pTMP);
+
+  /* sort the whole lot in place by swapping pointers */
+  qsort(sort_table, i, sizeof(char*), mpqs_relations_cmp);
+
+  /* copy results back to the original file, skipping exact duplicates */
+  pTMP = pari_fopen_or_fail(filename, WRITE);
+  old_s = sort_table[0];
+  pari_fputs(sort_table[0], pTMP);
+  count = 1;
+  for(j = 1; j < i; j++)
+  {
+    if (strcmp(old_s, sort_table[j]))
+    {
+      pari_fputs(sort_table[j], pTMP);
+      count++;
+    }
+    old_s = sort_table[j];
+  }
+  pari_fclose(pTMP);
+  if (MPQS_DEBUGLEVEL >= 6) err_printf("MPQS: done sorting one file.\n");
+
+  /* deallocate buffers and any extraneous buflist blocks except the first */
+  while (*--buflist)
+  {
+    if (buflist != buflist_head) /* not a linkage pointer */
+      pari_free((void*) *buflist);   /* free a buffer */
+    else
+    { /* linkage pointer */
+      next_buflist = (char**)(*buflist);
+      pari_free((void*)buflist_head); /* free a buflist block */
+      buflist_head = next_buflist;
+      buflist = buflist_head + buflist_size;
+    }
+  }
+  avma = av; return count;
+}
+
+/* appends contents of file fp1 to f (auxiliary routine for merge sort) and
+ * returns number of lines copied. Close f afterwards */
+static long
+mpqs_append_file(pariFILE *f, FILE *fp1)
+{
+  FILE *fp = f->file;
+  char line[MPQS_STRING_LENGTH];
+  long c = 0;
+  while (fgets(line, MPQS_STRING_LENGTH, fp1)) { pari_fputs(line, f); c++; }
+  if (fflush(fp)) pari_warn(warner, "error whilst flushing file %s", f->name);
+  pari_fclose(f); return c;
+}
+
+/* Merge-sort on the files LPREL and LPNEW; assumes that LPREL and LPNEW are
+ * already sorted. Creates/truncates the TMP file, writes result to it and
+ * closes it (via mpqs_append_file()). Instead of LPREL, LPNEW we may also call
+ * this with FREL, FNEW. In the latter case pCOMB should be NULL (and we
+ * return the count of all full relations), in the former non-NULL (and we
+ * return the count of frels we expect to be able to combine out of the
+ * present lprels). If pCOMB is non-NULL, the combinable lprels are written
+ * out to this separate file.
+ * We keep only one occurrence of each `large prime' in TMP (i.e. in the
+ * future LPREL file). --GN */
+
+#define swap_lines() { char *line_tmp;\
+  line_tmp = line_new_old; \
+  line_new_old = line_new; \
+  line_new = line_tmp; }
+
+static long
+mpqs_mergesort_lp_file0(FILE *LPREL, FILE *LPNEW, pariFILE *pCOMB,
+                        pariFILE *pTMP)
+{
+  char line1[MPQS_STRING_LENGTH], line2[MPQS_STRING_LENGTH];
+  char line[MPQS_STRING_LENGTH];
+  char *line_new = line1, *line_new_old = line2;
+  long q_new, q_new_old = -1, q, i = 0, c = 0;
+  long comb_in_progress;
+
+  if ( !fgets(line_new, MPQS_STRING_LENGTH, LPNEW) )
+  { /* LPNEW is empty: copy LPREL to TMP. Could be done by a rename if we
+     * didn't want to count the lines (again)... however, this case will not
+     * normally happen */
+    i = mpqs_append_file(pTMP, LPREL);
+    return pCOMB ? 0 : i;
+  }
+  /* we now have a line_new from LPNEW */
+
+  if (!fgets(line, MPQS_STRING_LENGTH, LPREL))
+  { /* LPREL is empty: copy LPNEW to TMP... almost. */
+    pari_fputs(line_new, pTMP);
+    if (!pCOMB)
+    { /* full relations mode */
+      i = mpqs_append_file(pTMP, LPNEW);
+      return i + 1;
+    }
+
+    /* LP mode:  check for combinable relations */
+    q_new_old = atol(line_new);
+    /* we need to retain a copy of the old line just for a moment, because we
+     * may yet have to write it to pCOMB. Do this by swapping the two buffers */
+    swap_lines();
+    comb_in_progress = 0;
+    i = 0;
+
+    while (fgets(line_new, MPQS_STRING_LENGTH, LPNEW))
+    {
+      q_new = atol(line_new);
+      if (q_new_old == q_new)
+      { /* found combinables, check whether we're already busy on this
+           particular `large prime' */
+        if (!comb_in_progress)
+        { /* if not, write first line to pCOMB, creating and opening the
+           * file first if it isn't open yet */
+          pari_fputs(line_new_old, pCOMB);
+          comb_in_progress = 1;
+        }
+        /* in any case, write the current line, and count it */
+        pari_fputs(line_new, pCOMB);
+        i++;
+      }
+      else
+      { /* not combinable */
+        q_new_old = q_new;
+        comb_in_progress = 0;
+        /* and dump it to the TMP file */
+        pari_fputs(line_new, pTMP);
+        /* and stash it away for a moment */
+        swap_lines();
+      }
+    } /* while */
+    pari_fclose(pTMP); return i;
+  }
+
+  /* normal case: both LPNEW and LPREL are not empty */
+  q_new = atol(line_new);
+  q = atol(line);
+
+  for(;;)
+  { /* main merging loop */
+    i = comb_in_progress = 0;
+
+    /* first the harder case:  let LPNEW catch up with LPREL, and possibly
+       overtake it, checking for combinables coming from LPNEW alone */
+    while (q > q_new)
+    {
+      if (!pCOMB || !comb_in_progress) pari_fputs(line_new, pTMP);
+      if (!pCOMB) c++; /* in FREL mode, count lines written */
+      else if (!comb_in_progress)
+      {
+        q_new_old = q_new;
+        swap_lines();
+      }
+      if (!fgets(line_new, MPQS_STRING_LENGTH, LPNEW))
+      {
+        pari_fputs(line, pTMP);
+        if (!pCOMB) c++; else c += i;
+        i = mpqs_append_file(pTMP, LPREL);
+        return pCOMB? c: c + i;
+      }
+      q_new = atol(line_new);
+      if (!pCOMB) continue;
+
+      /* LP mode only: */
+      if (q_new_old != q_new) /* not combinable */
+        comb_in_progress = 0; /* next loop will deal with it, or loop may end */
+      else
+      { /* found combinables, check whether we're already busy on this
+           `large prime' */
+        if (!comb_in_progress)
+        {
+          pari_fputs(line_new_old, pCOMB);
+          comb_in_progress = 1;
+        }
+        /* in any case, write the current line, and count it */
+        pari_fputs(line_new, pCOMB);
+        i++;
+      }
+    } /* while q > q_new */
+
+    /* q <= q_new */
+
+    if (pCOMB) c += i;   /* accumulate count of combinables */
+    i = 0;               /* and clear it */
+    comb_in_progress = 0;/* redundant */
+
+    /* now let LPREL catch up with LPNEW, and possibly overtake it */
+    while (q < q_new)
+    {
+      pari_fputs(line, pTMP);
+      if (!pCOMB) c++;
+      if (!fgets(line, MPQS_STRING_LENGTH, LPREL))
+      {
+        pari_fputs(line_new, pTMP);
+        i = mpqs_append_file(pTMP, LPNEW);
+        return pCOMB? c: c + i + 1;
+      }
+      else
+        q = atol(line);
+    }
+
+    /* q >= q_new */
+
+    /* Finally, it may happen that q == q_new, indicating combinables whose
+     * `large prime' is already in LPREL, and appears now once or more often in
+     * LPNEW. Thus in this sub-loop we advance LPNEW. The `line' from LPREL is
+     * left alone, and will be written to TMP the next time around the main for
+     * loop; we only write it to pCOMB here -- unless all we find is an exact
+     * duplicate of the line we already have, that is. (There can be at most
+     * one such, and if so it is simply discarded.) */
+    while (q == q_new)
+    {
+      if (!strcmp(line_new, line))
+      { /* duplicate -- move right ahead to the next LPNEW line */
+        ;/* do nothing here */
+      }
+      else if (!pCOMB)
+      { /* full relations mode: write line_new out first, keep line */
+        pari_fputs(line_new, pTMP);
+        c++;
+      }
+      else
+      { /* LP mode, and combinable relation */
+        if (!comb_in_progress)
+        {
+          pari_fputs(line, pCOMB);
+          comb_in_progress = 1;
+        }
+        pari_fputs(line_new, pCOMB);
+        i++;
+      }
+      /* NB comb_in_progress is cleared by q_new becoming bigger than q, thus
+       * the current while loop terminating, the next time through the main for
+       * loop */
+
+      /* common ending: get another line_new, if any */
+      if (!fgets(line_new, MPQS_STRING_LENGTH, LPNEW))
+      {
+        pari_fputs(line, pTMP);
+        if (!pCOMB) c++; else c += i;
+        i = mpqs_append_file(pTMP, LPREL);
+        return pCOMB? c: c + i;
+      }
+      else
+        q_new = atol(line_new);
+    } /* while */
+
+    if (pCOMB) c += i; /* accumulate count of combinables */
+  }
+}
+
+static long
+mpqs_mergesort_lp_file(char *REL_str, char *NEW_str, char *TMP_str, pariFILE *pCOMB)
+{
+  pariFILE *pREL = pari_fopen_or_fail(REL_str, READ);
+  pariFILE *pNEW = pari_fopen_or_fail(NEW_str, READ);
+  pariFILE *pTMP = pari_fopen_or_fail(TMP_str, WRITE);
+  long tp;
+
+  tp = mpqs_mergesort_lp_file0(pREL->file, pNEW->file, pCOMB, pTMP);
+  pari_fclose(pREL);
+  pari_fclose(pNEW);
+  pari_unlink(REL_str);
+  if (rename(TMP_str,REL_str)) pari_err_FILE("output file [rename]", REL_str);
+  if (MPQS_DEBUGLEVEL >= 6)
+    err_printf("MPQS: renamed file %s to %s\n", TMP_str, REL_str);
+  return tp;
+}
+
+
+/*********************************************************************/
+/**                                                                 **/
+/**                       SELF-INITIALIZATION                       **/
+/**                                                                 **/
+/*********************************************************************/
+
+#ifdef MPQS_DEBUG
+/* Debug-only helper routine: check correctness of the root z mod p_i
+ * by evaluting A * z^2 + B * z + C mod p_i  (which should be 0).
+ * C is written as (B*B-kN)/(4*A) */
+static void
+check_root(mpqs_handle_t *h, long p, long start)
+{
+  long z = start - ((long)(h->M) % p);
+  if (smodis(addii(h->C, mului(z, addii(h->B, mului(z, h->A)))), p))
+  {
+    err_printf("MPQS: p = %ld\n", p);
+    err_printf("MPQS: A = %Ps\n", h->A);
+    err_printf("MPQS: B = %Ps\n", h->B);
+    err_printf("MPQS: C = %Ps\n", h->C);
+    err_printf("MPQS: z = %ld\n", z);
+    pari_err_BUG("MPQS: self_init: found wrong polynomial");
+  }
+}
+#endif
+
+/* There are four parts to self-initialization, which are exercised at
+ * somewhat different times:
+ * - choosing a new coefficient A  (selecting the prime factors to go into it,
+ *   and doing the required bookkeeping in the FB entries, including clearing
+ *   out the flags from the previous cohort), together with:
+ * - doing the actual computations associated with a new A
+ * - choosing a new B keeping the same A (very much simpler and quicker)
+ * - and a small common bit that needs to happen in both cases.
+ * As to the first item, the new scheme works as follows:
+ * We pick omega_A - 1 prime factors for A below the index2_FB point which
+ * marks their ideal size, and one prime above this point, choosing the
+ * latter so as to get log2(A) as close as possible to l2_target_A.
+ * The lower prime factors are chosen using bit patterns of constant weight,
+ * gradually moving away from index2_FB towards smaller FB subscripts.
+ * If this bumps into index0_FB  (might happen for very small input),  we
+ * back up by increasing index2_FB by two, and from then on choosing only
+ * bit patterns with either or both of their bottom bits set, so at least
+ * one of the omega_A - 1 smaller prime factor will be beyond the original
+ * index2_FB point.  In this way we avoid re-using A's which had already
+ * been done.
+ * (The choice of the upper "flyer" prime is of course constrained by the
+ * size of the FB, which normally should never be anywhere close to becoming
+ * a problem.  In unfortunate cases, e.g. for very small kN, we might have
+ * to live with a rather non-optimal choice.
+ * Then again, MPQS as such is surprisingly robust.  One day, I had got the
+ * order of entries in mpqs_parameterset_t mixed up, and was running on a
+ * smallish N with a huuuuge factor base and extremely tiny sieve interval,
+ * and it still managed to factor it fairly quickly...)
+ *
+ * Mathematically, there isn't much more to this than the usual formula for
+ * solving a quadratic  (over the field of p elements for each prime p in
+ * the FB which doesn't divide A),  solving a linear equation for each of
+ * the primes which do divide A, and precomputing differences between roots
+ * mod p so we can adjust the roots quickly when we change B.
+ * See Thomas Sosnowski's diploma thesis.
+ */
+
+/* Helper function:
+ * Increment *x (!=0) to a larger value which has the same number of 1s in its
+ * binary representation.  Wraparound can be detected by the caller as long as
+ * we keep total_no_of_primes_for_A strictly less than BITS_IN_LONG.
+ *
+ * Changed switch to increment *x in all cases to the next larger number
+ * which (a) has the same count of 1 bits and (b) does not arise from the
+ * old value by moving a single 1 bit one position to the left  (which was
+ * undesirable for the sieve). --GN based on discussion with TP */
+INLINE void
+mpqs_increment(mpqs_uint32_t *x)
+{
+  mpqs_uint32_t r1_mask, r01_mask, slider=1UL;
+
+  /* 32-way computed jump handles 22 out of 32 cases */
+  switch (*x & 0x1F)
+  {
+  case 29:
+    (*x)++; break; /* shifts a single bit, but we postprocess this case */
+  case 26:
+    (*x) += 2; break; /* again */
+  case 1: case 3: case 6: case 9: case 11:
+  case 17: case 19: case 22: case 25: case 27:
+    (*x) += 3; return;
+  case 20:
+    (*x) += 4; break; /* again */
+  case 5: case 12: case 14: case 21:
+    (*x) += 5; return;
+  case 2: case 7: case 13: case 18: case 23:
+    (*x) += 6; return;
+  case 10:
+    (*x) += 7; return;
+  case 8:
+    (*x) += 8; break; /* and again */
+  case 4: case 15:
+    (*x) += 12; return;
+  default: /* 0, 16, 24, 28, 30, 31 */
+    /* isolate rightmost 1 */
+    r1_mask = ((*x ^ (*x - 1)) + 1) >> 1;
+    /* isolate rightmost 1 which has a 0 to its left */
+    r01_mask = ((*x ^ (*x + r1_mask)) + r1_mask) >> 2;
+    /* simple cases.  Both of these shift a single bit one position to the
+       left, and will need postprocessing */
+    if (r1_mask == r01_mask) { *x += r1_mask; break; }
+    if (r1_mask == 1) { *x += r01_mask; break; }
+    /* general case -- idea: add r01_mask, kill off as many 1 bits as possible
+     * to its right while at the same time filling in 1 bits from the LSB. */
+    if (r1_mask == 2) { *x += (r01_mask>>1) + 1; return; }
+    while (r01_mask > r1_mask && slider < r1_mask)
+    {
+      r01_mask >>= 1; slider <<= 1;
+    }
+    *x += r01_mask + slider - 1;
+    return;
+  }
+  /* post-process all cases which couldn't be finalized above.  If we get
+     here, slider still has its original value. */
+  r1_mask = ((*x ^ (*x - 1)) + 1) >> 1;
+  r01_mask = ((*x ^ (*x + r1_mask)) + r1_mask) >> 2;
+  if (r1_mask == r01_mask) { *x += r1_mask; return; }
+  if (r1_mask == 1) { *x += r01_mask; return; }
+  if (r1_mask == 2) { *x += (r01_mask>>1) + 1; return; }
+  while (r01_mask > r1_mask && slider < r1_mask)
+  {
+    r01_mask >>= 1; slider <<= 1;
+  }
+  *x += r01_mask + slider - 1;
+  return;
+}
+
+/* self-init (1): advancing the bit pattern, and choice of primes for A.
+ * The first time this is called, it finds h->bin_index == 0, which tells us
+ * to initialize things from scratch.  On later occasions, we need to begin
+ * by clearing the MPQS_FBE_DIVIDES_A bit in the fbe_flags of the former
+ * prime factors of A.  We use, of course, the per_A_pr array for finding
+ * them.  Upon successful return, that array will have been filled in, and
+ * the flag bits will have been turned on again in the right places.
+ * We return 1 (true) when we could set things up successfully, and 0 when
+ * we found we'd be using more bits to the left in bin_index than we have
+ * matching primes for in the FB.  In the latter case, bin_index will be
+ * zeroed out, index2_FB will be incremented by 2, index2_moved will be
+ * turned on, and the caller, after checking that index2_FB has not become
+ * too large, should just call us again, which then is guaranteed to succeed:
+ * we'll start again with a right-justified sequence of 1 bits in bin_index,
+ * now interpreted as selecting primes relative to the new index2_FB. */
+#ifndef MPQS_DEBUG_SI_CHOOSE_PRIMES
+#  define MPQS_DEBUG_SI_CHOOSE_PRIMES 0
+#endif
+INLINE int
+mpqs_si_choose_primes(mpqs_handle_t *h)
+{
+  mpqs_FB_entry_t *FB = h->FB;
+  mpqs_per_A_prime_t *per_A_pr = h->per_A_pr;
+  double l2_last_p = h->l2_target_A;
+  mpqs_int32_t omega_A = h->omega_A;
+  int i, j, v2, prev_last_p_idx;
+  int room = h->index2_FB - h->index0_FB - omega_A + 4;
+  /* GN 20050723:  I.e., index2_FB minus (index0_FB + omega_A - 3) plus 1
+   * The notion of room here (cf mpqs_locate_A_range() above) is the number
+   * of primes at or below index2_FB which are eligible for A.
+   * At the very least, we need omega_A - 1 of them, and it is guaranteed
+   * by mpqs_locate_A_range() that at least this many are available when we
+   * first get here.  The logic here ensures that the lowest FB slot used
+   * for A is never less than index0_FB + omega_A - 3.  In other words, when
+   * omega_A == 3 (very small kN), we allow ourselves to reach all the way
+   * down to index0_FB;  otherwise, we keep away from it by at least one
+   * position.  For omega_A >= 4 this avoids situations where the selection
+   * of the smaller primes here has advanced to a lot of very small ones, and
+   * the single last larger one has soared away to bump into the top end of
+   * the FB. */
+  mpqs_uint32_t room_mask;
+  mpqs_int32_t p;
+  ulong bits;
+
+  /* XXX also clear the index_j field here? */
+  if (h->bin_index == 0)
+  {
+    /* first time here, or after increasing index2_FB, initialize to a pattern
+     * of omega_A - 1 consecutive right-justified 1 bits.
+     * Caller will have ensured that there are enough primes for this in the
+     * FB below index2_FB. */
+    h->bin_index = (1UL << (omega_A - 1)) - 1;
+    prev_last_p_idx = 0;
+  }
+  else
+  {
+    /* clear out the old flags */
+    for (i = 0; i < omega_A; i++)
+      MPQS_FLG(i) &= ~MPQS_FBE_DIVIDES_A;
+    prev_last_p_idx = MPQS_I(omega_A-1);
+
+    /* find out how much maneuvering room we have before we're up against
+     * the index0_FB wall */
+    if (room > 30) room = 30;
+    room_mask = ~((1UL << room) - 1);
+
+    /* bump bin_index to the next acceptable value.  If index2_moved is off,
+     * call mpqs_increment() just once;  otherwise, repeat until there's
+     * something in the least significant 2 bits - this to ensure that we
+     * never re-use an A which we'd used before increasing index2_FB - but
+     * also stop if something shows up in the forbidden bits on the left
+     * where we'd run out of bits or out of subscripts  (i.e. walk beyond
+     * index0_FB + omega_A - 3). */
+    mpqs_increment(&h->bin_index);
+    if (h->index2_moved)
+    {
+      while ((h->bin_index & (room_mask | 0x3)) == 0)
+        mpqs_increment(&h->bin_index);
+    }
+    /* ok so did we fall off the edge on the left? */
+    if ((h->bin_index & room_mask) != 0)
+    {
+      /* Yes.  Turn on the index2_moved flag in the handle */
+      h->index2_FB += 2;        /* caller to check this isn't too large!!! */
+      h->index2_moved = 1;
+      h->bin_index = 0;
+      if (MPQS_DEBUG_SI_CHOOSE_PRIMES || (MPQS_DEBUGLEVEL >= 5))
+        err_printf("MPQS: wrapping, more primes for A now chosen near FB[%ld] = %ld\n",
+                   (long)h->index2_FB,
+                   (long)FB[h->index2_FB].fbe_p);
+      return 0;                 /* back off - caller should retry */
+    }
+  }
+  /* assert: we aren't occupying any of the room_mask bits now, and if
+   * index2_moved had already been on, at least one of the two LSBs is on */
+  bits = h->bin_index;
+  if (MPQS_DEBUG_SI_CHOOSE_PRIMES || (MPQS_DEBUGLEVEL >= 6))
+    err_printf("MPQS: new bit pattern for primes for A: 0x%lX\n", bits);
+
+  /* map bits to FB subscripts, counting downward with bit 0 corresponding
+   * to index2_FB, and accumulate logarithms against l2_last_p */
+  j = h->index2_FB;
+  v2 = vals((long)bits);
+  if (v2) { j -= v2; bits >>= v2; }
+  for (i = omega_A - 2; i >= 0; i--)
+  {
+    MPQS_I(i) = j;
+    l2_last_p -= MPQS_LP(i);
+    MPQS_FLG(i) |= MPQS_FBE_DIVIDES_A;
+    bits &= ~1UL;
+    if (!bits) break;           /* that was the i=0 iteration */
+    v2 = vals((long)bits);
+    j -= v2;
+    bits >>= v2;
+  }
+  /* Choose the larger prime.  Note we keep index2_FB <= size_of_FB - 3 */
+  for (j = h->index2_FB + 1; (p = FB[j].fbe_p) != 0; j++)
+  {
+    if (FB[j].fbe_flogp > l2_last_p) break;
+  }
+  /* GN 20050724: The following trick avoids generating a relatively large
+   * proportion of duplicate relations when the last prime happens to fall
+   * into an area where there are large gaps from one FB prime to the next,
+   * and would otherwise often be repeated  (so that successive A's would
+   * wind up too similar to each other).  While this trick isn't perfect,
+   * it seems to get rid of a major part of the potential duplication. */
+  if ((p != 0) && (j == prev_last_p_idx))
+  {
+    j++; p = FB[j].fbe_p;
+  }
+  MPQS_I(omega_A - 1) = (p == 0 ? /* did we fall off the end of the FB? */
+                         h->size_of_FB + 1 : /* then improvise */
+                         j);
+  MPQS_FLG(omega_A - 1) |= MPQS_FBE_DIVIDES_A;
+
+  if (MPQS_DEBUG_SI_CHOOSE_PRIMES || (MPQS_DEBUGLEVEL >= 6))
+  {
+    err_printf("MPQS: chose primes for A");
+    for (i = 0; i < omega_A; i++)
+    {
+      err_printf(" FB[%ld]=%ld%s",
+                 (long) MPQS_I(i),
+                 (long) MPQS_AP(i),
+                 i < omega_A - 1 ? "," : "\n");
+    }
+  }
+  return 1;
+}
+
+
+
+/* compute coefficients of the sieving polynomial for self initializing
+ * variant. Coefficients A and B are returned and several tables are
+ * updated.   */
+/* A and B are kept on the PARI stack in preallocated GENs.  So is C when
+ * we're compiled for debugging. */
+static void
+mpqs_self_init(mpqs_handle_t *h)
+{
+  const ulong size_of_FB = h->size_of_FB + 1;
+  mpqs_FB_entry_t *FB = h->FB;
+  mpqs_inv_A_H_t *inv_A_H = h->inv_A_H;
+  const pari_sp av = avma;
+  GEN p1, p2;
+  GEN A = h->A;
+  GEN B = h->B;
+  mpqs_per_A_prime_t *per_A_pr = h->per_A_pr;
+  long i, j;
+  long inv_A2;
+  ulong p;
+
+#ifdef MPQS_DEBUG_AVMA
+  err_printf("MPQS DEBUG: enter self init, avma = 0x%lX\n", (ulong)avma);
+#endif
+
+  /* when all of the B's have already been used, choose new A ;
+     this is indicated by setting index_j to 0 */
+  if (++h->index_j == (mpqs_uint32_t)h->no_B)
+  {
+    h->index_j = 0;
+    h->index_i++; /* count finished A's */
+  }
+
+  if (h->index_j == 0)
+  /* "mpqs_self_init_A()" */
+  { /* compute first polynomial with new A */
+    if (!mpqs_si_choose_primes(h))
+    {
+      /* We ran out of room towards small primes, and index2_FB was raised.
+       * Check that we're still ok in that direction before re-trying the
+       * operation, which then is guaranteed to succeed. The invariant we
+       * maintain towards the top end is that h->size_of_FB - h->index2_FB >= 3,
+       * but note that our size_of_FB is one larger. */
+
+      /* "throw exception up to caller." ( bin_index set to impossible value
+       * 0 by mpqs_si_choose_primes() */
+      if (size_of_FB - h->index2_FB < 4) return;
+      (void) mpqs_si_choose_primes(h);
+    }
+    /* assert: bin_index and per_A_pr are now populated with consistent
+     * values */
+
+    /* compute A = product of omega_A primes given by bin_index */
+    p1 = NULL;
+    for (i = 0; i < h->omega_A; i++)
+    {
+      p = (ulong) MPQS_AP(i);
+      p1 = p1 ? muliu(p1, p): utoipos(p);
+    }
+    affii(p1, A); avma = av;
+
+    /* Compute H[i], 0 <= i < omega_A.  Also compute the initial
+     * B = sum(v_i*H[i]), by taking all v_i = +1 */
+    /* TODO: following needs to be changed later for segmented FB and sieve
+     * interval, where we'll want to precompute several B's. */
+    p2 = NULL;
+    for (i = 0; i < h->omega_A; i++)
+    {
+      p = (ulong) MPQS_AP(i);
+      p1 = divis(A, (long)p);
+      p1 = muliu(p1, Fl_inv(umodiu(p1, p), p));
+      p1 = muliu(p1, MPQS_SQRT(i));
+      affii(remii(p1, A), MPQS_H(i));
+      p2 = p2 ? addii(p2, MPQS_H(i)) : MPQS_H(i);
+    }
+    affii(p2, B);
+    avma = av;                  /* must happen outside the loop */
+
+    /* ensure B = 1 mod 4 */
+    if (mod2(B) == 0)
+      affii(addii(B, mului(mod4(A), A)), B); /* B += (A % 4) * A; */
+
+    p1 = shifti(A, 1);
+    /* compute the roots z1, z2, of the polynomial Q(x) mod p_j and
+     * initialize start1[i] with the first value p_i | Q(z1 + i p_j)
+     * initialize start2[i] with the first value p_i | Q(z2 + i p_j)
+     * The following loop "miraculously" does The Right Thing for the
+     * primes dividing k (where sqrt_kN is 0 mod p).  Primes dividing A
+     * are skipped here, and are handled further down in the common part
+     * of SI. */
+    for (j = 3; (ulong)j <= size_of_FB; j++)
+    {
+      ulong mb, tmp1, tmp2, m;
+      if (FB[j].fbe_flags & MPQS_FBE_DIVIDES_A) continue;
+      p = (ulong)FB[j].fbe_p; m = h->M % p;
+      inv_A2 = Fl_inv(umodiu(p1, p), p); /* = 1/(2*A) mod p_j */
+      mb = umodiu(B, p); if (mb) mb = p - mb;
+      /* mb = -B mod p */
+      tmp1 = Fl_sub(mb, FB[j].fbe_sqrt_kN, p);
+      tmp1 = Fl_mul(tmp1, inv_A2, p);
+      FB[j].fbe_start1 = (mpqs_int32_t)Fl_add(tmp1, m, p);
+
+      tmp2 = Fl_add(mb, FB[j].fbe_sqrt_kN, p);
+      tmp2 = Fl_mul(tmp2, inv_A2, p);
+      FB[j].fbe_start2 = (mpqs_int32_t)Fl_add(tmp2, m, p);
+      for (i = 0; i < h->omega_A - 1; i++)
+      {
+        ulong h = umodiu(MPQS_H(i), p) << 1; if (h > p) h -= p;
+        MPQS_INV_A_H(i,j) = Fl_mul(h, inv_A2, p); /* 1/A * H[i] mod p_j */
+      }
+    }
+  }
+  else
+  /* "mpqs_self_init_B()" */
+  { /* no "real" computation -- use recursive formula */
+    /* The following exploits that B is the sum of omega_A terms +-H[i].
+     * Each time we switch to a new B, we choose a new pattern of signs;
+     * the precomputation of the inv_A_H array allows us to change the
+     * two arithmetic progressions equally fast.  The choice of sign
+     * patterns does *not* follow the bit pattern of the ordinal number
+     * of B in the current cohort;  rather, we use a Gray code, changing
+     * only one sign each time.  When the i-th rightmost bit of the new
+     * ordinal number index_j of B is 1, the sign of H[i] is changed;
+     * the next bit to the left tells us whether we should be adding or
+     * subtracting the difference term.  We never need to change the sign
+     * of H[omega_A-1] (the topmost one), because that would just give us
+     * the same sieve items Q(x) again with the opposite sign of x.  This
+     * is why we only precomputed inv_A_H up to i = omega_A - 2. */
+
+    ulong v2 = 0;               /* 2-valuation of h->index_j */
+
+    j = h->index_j;
+    /* could use vals() here, but we need to right shift the bit pattern
+     * anyway in order to find out which inv_A_H entries must be added to or
+     * subtracted from the modular roots */
+    while ((j & 1) == 0) { v2++; j >>= 1; }
+
+    /* v2 = v_2(index_j), determine new starting positions for sieving */
+    p1 = shifti(MPQS_H(v2), 1);
+    if (j & 2)
+    { /* j = 3 mod 4 */
+      for (j = 3; (ulong)j <= size_of_FB; j++)
+      {
+        if (FB[j].fbe_flags & MPQS_FBE_DIVIDES_A) continue;
+        p = (ulong)FB[j].fbe_p;
+        FB[j].fbe_start1 = Fl_sub(FB[j].fbe_start1, MPQS_INV_A_H(v2,j), p);
+        FB[j].fbe_start2 = Fl_sub(FB[j].fbe_start2, MPQS_INV_A_H(v2,j), p);
+      }
+      p1 = addii(B, p1);
+    }
+    else
+    { /* j = 1 mod 4 */
+      for (j = 3; (ulong)j <= size_of_FB; j++)
+      {
+        if (FB[j].fbe_flags & MPQS_FBE_DIVIDES_A) continue;
+        p = (ulong)FB[j].fbe_p;
+        FB[j].fbe_start1 = Fl_add(FB[j].fbe_start1, MPQS_INV_A_H(v2,j), p);
+        FB[j].fbe_start2 = Fl_add(FB[j].fbe_start2, MPQS_INV_A_H(v2,j), p);
+      }
+      p1 = subii(B, p1);
+    }
+    affii(p1, B);
+  }
+  avma = av;
+
+  /* p=2 is a special case.  start1[2], start2[2] are never looked at,
+   * so don't bother setting them. */
+
+  /* "mpqs_self_init_common()" */
+
+  /* now compute zeros of polynomials that have only one zero mod p
+     because p divides the coefficient A */
+
+  /* compute coefficient -C */
+  p1 = diviiexact(subii(h->kN, sqri(B)), shifti(A, 2));
+
+  for (i = 0; i < h->omega_A; i++)
+  {
+    ulong tmp, s;
+    p = (ulong) MPQS_AP(i);
+    tmp = Fl_div(umodiu(p1, p), umodiu(B, p), p); s = (tmp + h->M) % p;
+    FB[MPQS_I(i)].fbe_start1 = (mpqs_int32_t)s;
+    FB[MPQS_I(i)].fbe_start2 = (mpqs_int32_t)s;
+  }
+
+  if (MPQS_DEBUGLEVEL >= 6)
+  {
+    /* must happen before resetting avma, because of the absi() */
+    err_printf("MPQS: chose Q_%ld(x) = %Ps x^2 %c %Ps x + C\n",
+               (long) h->index_j, h->A,
+               signe(h->B) < 0? '-': '+', absi(h->B));
+  }
+
+  avma = av;
+
+#ifdef MPQS_DEBUG
+  /* stash C into the handle.  Since check_root() is the only thing which
+   * uses it, and only for debugging, C doesn't even exist as a field in
+   * the handle unless we're built with MPQS_DEBUG. */
+  affii(negi(p1), h->C);
+  for (j = 3; j <= size_of_FB; j++)
+  {
+    check_root(h, FB[j].fbe_p, FB[j].fbe_start1);
+    check_root(h, FB[j].fbe_p, FB[j].fbe_start2); avma = av;
+  }
+  if (DEBUGLEVEL >= 6)
+    PRINT_IF_VERBOSE("MPQS: checking of roots of Q(x) was successful\n");
+#endif
+
+#ifdef MPQS_DEBUG_AVMA
+  err_printf("MPQS DEBUG: leave self init, avma = 0x%lX\n", (ulong)avma);
+#endif
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                           THE SIEVE                             **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* Main sieving routine:
+ * p4 = 4*p (used for loop unrolling)
+ * log_p: approximation for log(p)
+ * begin: points to a sieve array
+ * end: points to the end of the sieve array
+ * starting_sieving_index: marks the first FB element used for sieving */
+INLINE void
+mpqs_sieve_p(unsigned char *begin, unsigned char *end,
+             long p4, long p, unsigned char log_p)
+{
+  register unsigned char *e = end - p4;
+  /* Loop unrolled some time ago. It might be better to let the compiler worry
+   * about *this* kind of optimization, based on its knowledge of whatever
+   * useful tricks the machine instruction set architecture is offering
+   * ("speculative loads" being the buzzword). --GN */
+  while (e - begin >= 0) /* signed comparison */
+  {
+    (*begin) += log_p, begin += p;
+    (*begin) += log_p, begin += p;
+    (*begin) += log_p, begin += p;
+    (*begin) += log_p, begin += p;
+  }
+  while (end - begin >= 0) /* again */
+    (*begin) += log_p, begin += p;
+}
+
+static void
+mpqs_sieve(mpqs_handle_t *h)
+{
+  long p, l = h->index1_FB;
+  mpqs_FB_entry_t *ptr_FB;
+  unsigned char *sieve_array = h->sieve_array;
+  unsigned char *sieve_array_end = h->sieve_array_end;
+
+  for (ptr_FB = &(h->FB[l]); (p = ptr_FB->fbe_p) != 0; ptr_FB++, l++)
+  {
+    unsigned char log_p = ptr_FB->fbe_logval;
+    long start1 = ptr_FB->fbe_start1;
+    long start2 = ptr_FB->fbe_start2;
+
+    /* sieve with FB[l] from start_1[l] */
+    /* if start1 != start2 sieve with FB[l] from start_2[l] */
+    /* Maybe it is more efficient not to have a conditional branch in
+     * the present loop body, and instead to right-shift log_p one bit
+     * based on a flag bit telling us that we're on a one-root prime?
+     * And instead roll the two invocations of mpqs_sieve_p into one. */
+    mpqs_sieve_p(sieve_array + start1, sieve_array_end, p << 2, p, log_p);
+    if (start1 != start2)
+      mpqs_sieve_p(sieve_array + start2, sieve_array_end, p << 2, p, log_p);
+  }
+}
+
+/******************************/
+
+/* Could make shameless use of the fact that M is divisible by 4, but
+ * let the compiler worry about loop unrolling.  Indeed I wonder whether
+ * modern compilers woudln't have an easier time optimizing this if it
+ * were written as array accesses.  Doing so. */
+static long
+mpqs_eval_sieve(mpqs_handle_t *h)
+{
+  long x = 0, count = 0, M_2 = h->M << 1;
+  /* TODO: replace the following by an auto-adjusting threshold driven
+   * by histogram yield measurements */
+  unsigned char th = h->sieve_threshold;
+  unsigned char *sieve_array = h->sieve_array;
+  long *candidates = h->candidates;
+
+  /* The following variation on the original is marginally faster with a
+   * good optimizing compiler.  Exploiting the sentinel, we don't need to
+   * check for x < M_2 in the inner while loop - this more than makes up
+   * for the "lack" of explicit unrolling.  Optimizations like loop
+   * unrolling are best left to the compiler anyway... */
+  while (count < MPQS_CANDIDATE_ARRAY_SIZE - 1)
+  {
+    while (sieve_array[x] < th) x++;
+    if (x >= M_2) break;
+    candidates[count++] = x++;
+  }
+
+  candidates[count] = 0; return count;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                     CONSTRUCTING RELATIONS                      **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* Main relation routine */
+static void
+mpqs_add_factor(char **last, ulong ei, ulong pi) {
+  sprintf(*last, " %lu %lu", ei, pi);
+  *last += strlen(*last);
+}
+
+/* concatenate " 0" */
+static void
+mpqs_add_0(char **last) {
+  char *s = *last;
+  *s++ = ' ';
+  *s++ = '0';
+  *s++ = 0; *last = s;
+}
+
+#ifdef MPQS_DEBUG
+static GEN
+mpqs_factorback(mpqs_handle_t *h, char *relations)
+{
+  char *s, *t = stack_strdup(relations), *tok;
+  GEN N = h->N, prod = gen_1;
+  long i;
+  mpqs_FB_entry_t *FB = h->FB;
+
+  s = paristrtok_r(t, " \n", &tok);
+  while (s != NULL)
+  {
+    long e = atol(s); if (!e) break;
+    s = paristrtok_r(NULL, " \n", &tok);
+    i = atol(s);
+    /* special case -1 */
+    if (i == 1) { prod = Fp_neg(prod,N); s = paristrtok_r(NULL, " \n", &tok); continue; }
+    prod = Fp_mul(prod, Fp_powu(utoipos(FB[i].fbe_p), e, N), N);
+    s = paristrtok_r(NULL, " \n", &tok);
+  }
+  return prod;
+}
+#endif
+
+/* NB FREL, LPREL are actually FNEW, LPNEW when we get called */
+static long
+mpqs_eval_cand(mpqs_handle_t *h, long number_of_cand,
+               FILE *FREL, FILE *LPREL)
+{
+  pari_sp av;
+  long number_of_relations = 0;
+  char *relations = h->relations;
+  long *relaprimes = h->relaprimes;
+  ulong i, pi;
+  mpqs_FB_entry_t *FB = h->FB;
+  GEN A = h->A;
+  GEN B = h->B;                 /* we don't need coefficient C here */
+  int pii;
+  long *candidates = h->candidates;
+
+  av = avma;
+#ifdef MPQS_DEBUG_AVMA
+  err_printf("MPQS DEBUG: enter eval cand, avma = 0x%lX\n", (ulong)avma);
+#endif
+  for (i = 0; i < (ulong)number_of_cand; i++, avma = av)
+  {
+    GEN Qx, Qx_part, A_2x_plus_B, Y;
+    long powers_of_2, p;
+    long x = candidates[i];
+    long x_minus_M = x - h->M;
+    char *relations_end = relations;
+    int relaprpos = 0;
+
+#ifdef MPQS_DEBUG_AVMA
+    err_printf("MPQS DEBUG: eval loop 1, avma = 0x%lX\n", (ulong)avma);
+#endif
+
+    *relations_end = 0;
+#ifdef MPQS_DEBUG_VERYVERBOSE
+    err_printf("%c", (char)('0' + i%10));
+#endif
+
+    /* A_2x_plus_B = (A*(2x)+B), Qx = (A*(2x)+B)^2/(4*A) = Q(x) */
+    A_2x_plus_B = addii(mulis(A, x_minus_M << 1), B);
+    Y = absi(A_2x_plus_B);
+
+    Qx = subii(sqri(A_2x_plus_B), h->kN);
+
+#ifdef MPQS_DEBUG_AVMA
+    err_printf("MPQS DEBUG: eval loop 2, avma = 0x%lX\n", (ulong)avma);
+#endif
+    /* When N is relatively small, it may happen that Qx is outright
+     * divisible by N at this point.  In any case, when no extensive prior
+     * trial division / Rho / ECM had been attempted, gcd(Qx,N) may turn
+     * out to be a nontrivial factor of N  (larger than what the FB contains
+     * or we'd have found it already, but possibly smaller than the large-
+     * prime bound).  This is too rare to check for here in the inner loop,
+     * but it will be caught if such an LP relation is ever combined with
+     * another. */
+
+    /* Qx cannot possibly vanish here */
+    if (!signe(Qx)) { PRINT_IF_VERBOSE("<+>"); continue; }
+    else if (signe(Qx) < 0) {
+      setabssign(Qx);
+      mpqs_add_factor(&relations_end, 1, 1); /* i = 1, ei = 1, pi */
+    }
+
+    /* divide by powers of 2;  we're really dealing with 4*A*Q(x), so we
+     * always have at least 2^2 here, and at least 2^3 when kN is 1 mod 4 */
+    powers_of_2 = vali(Qx);
+    Qx = shifti(Qx, -powers_of_2);
+    mpqs_add_factor(&relations_end, powers_of_2, 2);
+
+    /* That has dealt with a possible -1 and the power of 2.  First pass
+     * over odd primes in FB: pick up all possible divisors of Qx including
+     * those sitting in k or in A, and remember them in relaprimes. Do not
+     * yet worry about possible repeated factors, these will be found in the
+     * second pass. */
+    Qx_part = A;
+
+    /* The first pass recognizes divisors of A by their corresponding flags
+     * bit in the FB entry.  (Divisors of k require no special treatment at
+     * this stage.)  We construct a preliminary table of FB subscripts and
+     * "exponents" of the FB primes which divide Qx.  (We store subscripts
+     * rather than the primes themselves because the string representation
+     * of a relation is in terms of the subscripts.)
+     * We must distinguish three cases so we can do the right thing in the
+     * 2nd pass: prime not in A which divides Qx, prime in A which does not
+     * divide Qx/A, prime in A which does divide Qx/A. The first and third
+     * kinds need checking for repeated factors, the second kind doesn't. The
+     * first and second kinds contribute 1 to the exponent in the relation,
+     * the 3rd kind contributes 2. We store 1,0,2 respectively in these three
+     * cases.
+     * Factors in common with k are much simpler - if they occur, they occur
+     * exactly to the first power, and this makes no difference in the first
+     * pass - here they behave just like every normal odd factor base prime.
+     */
+
+    for (pi = 3; (p = FB[pi].fbe_p); pi++)
+    {
+      long tmp_p = x % p;
+      ulong ei = 0;
+
+      /* Here we use that MPQS_FBE_DIVIDES_A equals 1. */
+      ei = FB[pi].fbe_flags & MPQS_FBE_DIVIDES_A;
+
+      if (tmp_p == FB[pi].fbe_start1 || tmp_p == FB[pi].fbe_start2)
+      { /* p divides Q(x)/A (and possibly A), 1st or 3rd case */
+        relaprimes[relaprpos++] = pi;
+        relaprimes[relaprpos++] = 1 + ei;
+        Qx_part = muliu(Qx_part, p);
+      }
+      else if (ei)
+      { /* p divides A but does not divide Q(x)/A, 2nd case */
+        relaprimes[relaprpos++] = pi;
+        relaprimes[relaprpos++] = 0;
+      }
+    }
+
+    /* We have now accumulated the known factors of Qx except for possible
+     * repeated factors and for possible large primes.  Divide off what we
+     * have.  (This is faster than dividing off A and each prime separately.)
+     */
+    Qx = diviiexact(Qx, Qx_part);
+    /* (ToDo: MPQS_DEBUG sanity check...) */
+
+#ifdef MPQS_DEBUG_AVMA
+    err_printf("MPQS DEBUG: eval loop 3, avma = 0x%lX\n", (ulong)avma);
+#endif
+
+    /* second pass - deal with any repeated factors, and write out the string
+     * representation of the tentative relation. At this point, the only
+     * primes which can occur again in the adjusted Qx are those in relaprimes
+     * which are followed by 1 or 2.  We must pick up those followed by a 0,
+     * too, though. */
+    PRINT_IF_VERBOSE("a");
+    for (pii = 0; pii < relaprpos; pii+=2)
+    {
+      long remd_p;
+      ulong ei = relaprimes[pii+1];
+      GEN Qx_div_p;
+      pi = relaprimes[pii];
+
+      /* Here, prime factors of k go their separate way.  We could have
+       * introduced another FB entry flag for marking them, but it is easier
+       * to identify them just by their position before index0_FB. */
+      if ((mpqs_int32_t)pi < h->index0_FB) {
+#ifdef MPQS_DEBUG
+        PRINT_IF_VERBOSE("\bk!");
+#endif
+        mpqs_add_factor(&relations_end, 1, pi);
+        continue;
+      }
+      if (ei == 0) /* p divides A and that was it */
+      {
+        mpqs_add_factor(&relations_end, 1, pi);
+        continue;
+      }
+      p = FB[pi].fbe_p;
+#ifdef MPQS_DEBUG_CANDIDATE_EVALUATION
+      err_printf("MPQS DEBUG: Qx=%Ps p=%ld\n", Qx, (long)p);
+#endif
+      /* otherwise p might still divide the current adjusted Qx. Try it... */
+      /* NOTE: break out of loop when remaining Qx is 1 ?  Or rather, suppress
+       * the trial divisions, since we still need to write our string.
+       * Actually instead of testing for 1, test whether Qx is smaller than p;
+       * cf Karim's mail from 20050124.  If it is, without being 1, then it has
+       * a common factor with k.  But those factors are soon going to have
+       * disappeared before we get here.  However, inserting
+       * an explicit if (!is_pm1(Qx)) here did not help any. */
+      Qx_div_p = divis_rem(Qx, p, &remd_p);
+      while (remd_p == 0) {
+        ei++; Qx = Qx_div_p;
+        Qx_div_p = divis_rem(Qx, p, &remd_p);
+      }
+      mpqs_add_factor(&relations_end, ei, pi);
+    }
+
+#ifdef MPQS_DEBUG_AVMA
+    err_printf("MPQS DEBUG: eval loop 4, avma = 0x%lX\n", (ulong)avma);
+#endif
+    PRINT_IF_VERBOSE("\bb");
+    if (is_pm1(Qx))
+    {
+      mpqs_add_0(&relations_end);
+      fprintf(FREL, "%s :%s\n", itostr(Y), relations);
+      number_of_relations++;
+#ifdef MPQS_USE_HISTOGRAMS
+      /* bump full relations counter at candidate's value */
+      if (h->do_histograms) h->histo_full[sa[x]-128]++;
+#endif
+
+#ifdef MPQS_DEBUG
+      {
+        pari_sp av1 = avma;
+        GEN rhs = mpqs_factorback(h, relations);
+        GEN Qx_2 = remii(sqri(Y), h->N);
+        if (!equalii(Qx_2, rhs))
+        {
+          PRINT_IF_VERBOSE("\b(!)\n");
+          err_printf("MPQS: %Ps @ %Ps :%s\n", Y, Qx, relations);
+          err_printf("\tQx_2 = %Ps\n", Qx_2);
+          err_printf("\t rhs = %Ps\n", rhs);
+          pari_err_BUG("MPQS: wrong full relation found");
+        }
+        else
+          PRINT_IF_VERBOSE("\b(:)");
+        avma = av1;
+      }
+#endif
+    }
+    else if (cmpis(Qx, h->lp_bound) > 0)
+    { /* TODO: check for double large prime */
+#ifdef MPQS_USE_HISTOGRAMS
+      /* bump useless-candidates counter at candidate's value */
+      if (h->do_histograms) h->histo_drop[sa[x]-128]++;
+#endif
+      PRINT_IF_VERBOSE("\b.");
+    }
+    else
+    { /* if (mpqs_isprime(itos(Qx))) */
+      mpqs_add_0(&relations_end);
+      fprintf(LPREL, "%s @ %s :%s\n", itostr(Qx), itostr(Y), relations);
+#ifdef MPQS_USE_HISTOGRAMS
+      /* bump LP relations counter at candidate's value */
+      if (h->do_histograms) h->histo_lprl[sa[x]-128]++;
+#endif
+#ifdef MPQS_DEBUG
+      {
+        pari_sp av1 = avma;
+        GEN rhs = mpqs_factorback(h, relations);
+        GEN Qx_2 = remii(sqri(Y), h->N);
+
+        rhs = modii(mulii(rhs, Qx), h->N);
+        if (!equalii(Qx_2, rhs))
+        {
+          PRINT_IF_VERBOSE("\b(!)\n");
+          err_printf("MPQS: %Ps @ %Ps :%s\n", Y, Qx, relations);
+          err_printf("\tQx_2 = %Ps\n", Qx_2);
+          err_printf("\t rhs = %Ps\n", rhs);
+          pari_err_BUG("MPQS: wrong large prime relation found");
+        }
+        else
+          PRINT_IF_VERBOSE("\b(;)");
+        avma = av1;
+      }
+#endif
+    }
+
+#ifdef MPQS_DEBUG_AVMA
+    err_printf("MPQS DEBUG: eval loop end, avma = 0x%lX\n", (ulong)avma);
+#endif
+  } /* for */
+  PRINT_IF_VERBOSE("\n");
+#ifdef MPQS_DEBUG_AVMA
+  err_printf("MPQS DEBUG: leave eval cand, avma = 0x%lX\n", (ulong)avma);
+#endif
+  return number_of_relations;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                      COMBINING RELATIONS                        **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* combines the large prime relations in COMB to full relations in FNEW.
+ * FNEW is assumed to be open for writing / appending. */
+
+typedef struct {
+  long q;
+  char Y[MPQS_STRING_LENGTH];
+  char E[MPQS_STRING_LENGTH];
+} mpqs_lp_entry;
+
+static void
+mpqs_set_exponents(long *ei, char *r)
+{
+  char *s, b[MPQS_STRING_LENGTH], *tok;
+  long e;
+
+  strcpy(b, r);
+  s = paristrtok_r(b, " \n", &tok);
+  while (s != NULL)
+  {
+    e = atol(s); if (!e) break;
+    s = paristrtok_r(NULL, " \n", &tok);
+    ei[ atol(s) ] += e;
+    s = paristrtok_r(NULL, " \n", &tok);
+  }
+}
+
+static void
+set_lp_entry(mpqs_lp_entry *e, char *buf)
+{
+  char *s1, *s2;
+  s1 = buf; s2 = strchr(s1, ' '); *s2 = '\0';
+  e->q = atol(s1);
+  s1 = s2 + 3; s2 = strchr(s1, ' '); *s2 = '\0';
+  strcpy(e->Y, s1);
+  s1 = s2 + 3; s2 = strchr(s1, '\n'); *s2 = '\0';
+  strcpy(e->E, s1);
+}
+
+static long
+mpqs_combine_large_primes(mpqs_handle_t *h,
+                          FILE *COMB, pariFILE *pFNEW, GEN *f)
+{
+  pari_sp av0 = avma, av, av2;
+  char new_relation[MPQS_STRING_LENGTH], buf[MPQS_STRING_LENGTH];
+  mpqs_lp_entry e[2]; /* we'll use the two alternatingly */
+  long *ei, ei_size = h->size_of_FB + 2;
+  long old_q;
+  GEN inv_q, Y1, Y2, new_Y, new_Y1;
+  long i, l, c = 0;
+
+  *f = NULL;
+  if (!fgets(buf, MPQS_STRING_LENGTH, COMB)) return 0; /* should not happen */
+
+  ei = (long *) new_chunk(ei_size);
+  av = avma;
+  /* put first lp relation in row 0 of e */
+  set_lp_entry(&e[0], buf);
+
+  i = 1; /* second relation will go into row 1 */
+  old_q = e[0].q;
+  while (!invmod(utoipos(old_q), h->N, &inv_q)) /* can happen */
+  {
+    inv_q = gcdii(inv_q, h->N);
+    /* inv_q can no longer be 1 here (it could while we were doing this mod
+     * kN instead of mod N), but never mind - we're not in the fast path
+     * at this point.  It could be N when N is quite small;  or we might
+     * just have found a divisor by sheer luck. */
+    if (is_pm1(inv_q) || equalii(inv_q, h->N)) /* pity */
+    {
+#ifdef MPQS_DEBUG
+      err_printf("MPQS: skipping relation with non-invertible q\n");
+#endif
+      if (!fgets(buf, MPQS_STRING_LENGTH, COMB)) { avma = av0; return 0; }
+      avma = av;
+      set_lp_entry(&e[0], buf);
+      old_q = e[0].q; continue;
+    }
+    *f = gerepileuptoint(av0, inv_q);
+    return c;
+  }
+  Y1 = strtoi(e[0].Y);
+  av2 = avma; /* preserve inv_q and Y1 */
+
+  while (fgets(buf, MPQS_STRING_LENGTH, COMB))
+  {
+    set_lp_entry(&e[i], buf);
+    if (e[i].q != old_q)
+    {
+      /* switch to combining a new bunch, swapping the rows */
+      old_q = e[i].q;
+      avma = av; /* discard old inv_q and Y1 */
+      if (!invmod(utoipos(old_q), h->N, &inv_q)) /* can happen --GN */
+      {
+        inv_q = gcdii(inv_q, h->N);
+        if (is_pm1(inv_q) || equalii(inv_q, h->N)) /* pity */
+        {
+#ifdef MPQS_DEBUG
+          err_printf("MPQS: skipping relation with non-invertible q\n");
+#endif
+          old_q = -1; /* sentinel */
+          av2 = avma = av;
+          continue; /* discard this combination */
+        }
+        *f = gerepileuptoint(av0, inv_q);
+        return c;
+      }
+      Y1 = strtoi(e[i].Y);
+      i = 1 - i; /* subsequent relations go to other row */
+      av2 = avma; /* preserve inv_q and Y1 */
+      continue;
+    }
+    /* count and combine the two we've got, and continue in the same row */
+    c++;
+    memset((void *)ei, 0, ei_size * sizeof(long));
+    mpqs_set_exponents(ei, e[0].E);
+    mpqs_set_exponents(ei, e[1].E);
+    Y2 = strtoi(e[i].Y);
+    new_Y = modii(mulii(mulii(Y1, Y2), inv_q), h->N);
+    new_Y1 = subii(h->N, new_Y);
+    if (absi_cmp(new_Y1, new_Y) < 0) new_Y = new_Y1;
+    strcpy(new_relation, itostr(new_Y));
+    strcat(new_relation, " :");
+    if (ei[1] & 1) strcat(new_relation, " 1 1");
+    for (l = 2; l < ei_size; l++)
+      if (ei[l])
+      {
+        sprintf(buf, " %ld %ld", ei[l], l);
+        strcat(new_relation, buf);
+      }
+    strcat(new_relation, " 0");
+    if (DEBUGLEVEL >= 6)
+    {
+      err_printf("MPQS: combining\n");
+      err_printf("    {%ld @ %s : %s}\n", old_q, e[1-i].Y, e[1-i].E);
+      err_printf("  * {%ld @ %s : %s}\n", e[i].q, e[i].Y, e[i].E);
+      err_printf(" == {%s}\n", new_relation);
+    }
+    strcat(new_relation, "\n");
+
+#ifdef MPQS_DEBUG
+    {
+      GEN Qx_2, prod;
+      char *s = strchr(new_relation, ':') + 2;
+      pari_sp av1 = avma;
+
+      Qx_2 = modii(sqri(new_Y), h->N);
+      prod = mpqs_factorback(h, s);
+      if (!equalii(Qx_2, prod))
+        pari_err_BUG("MPQS: combined large prime relation is false");
+      avma = av1;
+    }
+#endif
+
+    pari_fputs(new_relation, pFNEW);
+    avma = av2;
+  } /* while */
+
+  if (DEBUGLEVEL >= 4)
+    err_printf("MPQS: combined %ld full relation%s\n", c, (c!=1 ? "s" : ""));
+  avma = av0; return c;
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**                    FROM RELATIONS TO DIVISORS                   **/
+/**                                                                 **/
+/*********************************************************************/
+
+/* create and read an F2m from a relation file FREL.
+ * Also record the position of each relation in the file for later use
+ * rows = size_of_FB+1, cols = rel */
+static GEN
+stream_read_F2m(pariFILE *pFREL, long rows, long cols, long *fpos)
+{
+  FILE *FREL = pFREL->file;
+  long i, e, p;
+  char buf[MPQS_STRING_LENGTH], *s;
+  GEN m;
+  long space = 2*((nbits2nlong(rows)+3)*cols+1);
+  if ((long)((GEN)avma - (GEN)bot) < space)
+  {
+    pari_sp av = avma;
+    m = gclone(zero_F2m(rows, cols));
+    if (DEBUGLEVEL>=4)
+      err_printf("MPQS: allocating %ld words for Gauss\n",space);
+    avma = av;
+  }
+  else
+    m = zero_F2m_copy(rows, cols);
+  for (i = 0;; i++)
+  {
+    char *tok=NULL;
+    if (i < cols && (fpos[i] = ftell(FREL)) < 0)
+      pari_err_FILE("full relations file [ftell]", pFREL->name);
+    if (!fgets(buf, MPQS_STRING_LENGTH, FREL)) break;
+    s = strchr(buf, ':');
+    if (!s) pari_err_FILE("full relations file [strchr]", pFREL->name);
+    s = paristrtok_r(s+2, " \n", &tok);
+    while (s != NULL)
+    {
+      e = atol(s); if (!e) break;
+      s = paristrtok_r(NULL, " \n", &tok);
+      p = atol(s);
+      if (e & 1) F2m_set(m, p, i+1);
+      s = paristrtok_r(NULL, " \n", &tok);
+    }
+  }
+  if (i != cols)
+  {
+    err_printf("MPQS: full relations file %s than expected",
+               i > cols ? "longer" : "shorter");
+    pari_err(e_BUG, "MPQS [panicking]");
+  }
+  return m;
+}
+
+/* NB: overwrites rel */
+static GEN
+mpqs_add_relation(GEN Y_prod, GEN N, long *ei, char *rel)
+{
+  pari_sp av = avma;
+  GEN res;
+  char *s, *tok=NULL;
+
+  s = strchr(rel, ':') - 1;
+  *s = '\0';
+
+  res = remii(mulii(Y_prod, strtoi(rel)), N);
+
+  s = paristrtok_r(s + 3, " \n", &tok);
+  while (s != NULL)
+  {
+    long e = atol(s), i;
+    if (!e) break;
+    s = paristrtok_r(NULL, " \n", &tok);
+    i = atol(s); /* bug in g++-3.4.1: miscompiles ei[ atol(s) ] */
+    ei[i] += e;
+    s = paristrtok_r(NULL, " \n", &tok);
+  }
+  return gerepileuptoint(av, res);
+}
+
+static char*
+mpqs_get_relation(char *buf, long pos, pariFILE *pFREL)
+{
+  if (fseek(pFREL->file, pos, SEEK_SET))
+    pari_err_FILE("FREL file [fseek]", pFREL->name);
+  if (!fgets(buf, MPQS_STRING_LENGTH, pFREL->file))
+    pari_err_FILE("FREL file [fgets]", pFREL->name);
+  return buf;
+}
+
+#define isprobableprime(n) (MR_Jaeschke((n),17))
+static int
+split(GEN N, GEN *e, GEN *res)
+{
+  ulong mask;
+  long flag;
+  GEN base;
+  if (isprobableprime(N)) { *e = gen_1; return 1; }
+  if (Z_issquareall(N, &base))
+  { /* squares could cost us a lot of time */
+    /* GN20050707: as used now, this is always called with res!=NULL */
+    *res = base;
+    *e = gen_2;
+    if (DEBUGLEVEL >= 5) err_printf("MPQS: decomposed a square\n");
+    return 1;
+  }
+  mask = 7;
+  /* 5th/7th powers aren't worth the trouble. OTOH once we have the hooks for
+   * dealing with cubes, higher powers can be handled essentially for free) */
+  if ( (flag = is_357_power(N, &base, &mask)) )
+  {
+    *res = base;
+    *e = utoipos(flag);
+    if (DEBUGLEVEL >= 5)
+      err_printf("MPQS: decomposed a %s\n",
+                 (flag == 3 ? "cube" :
+                  (flag == 5 ? "5th power" : "7th power")));
+    return 1;
+  }
+  *e = gen_0; return 0; /* known composite */
+}
+
+static GEN
+mpqs_solve_linear_system(mpqs_handle_t *h, pariFILE *pFREL, long rel)
+{
+  GEN N = h->N, X, Y_prod, X_plus_Y, D1, res, new_res;
+  mpqs_FB_entry_t *FB = h->FB;
+  pari_sp av=avma, av2, av3, lim, lim3;
+  long *fpos, *ei;
+  long i, j, H_cols, H_rows;
+  long res_last, res_next, res_size, res_max;
+  GEN  m, ker_m;
+  long done, rank;
+  char buf[MPQS_STRING_LENGTH];
+
+  fpos = (long *) pari_malloc(rel * sizeof(long));
+
+  m = stream_read_F2m(pFREL, h->size_of_FB+1, rel, fpos);
+  if (DEBUGLEVEL >= 7)
+    err_printf("\\\\ MATRIX READ BY MPQS\nFREL=%Ps\n",m);
+
+  ker_m = F2m_ker_sp(m,0); rank = lg(ker_m)-1;
+  if (isclone(m)) gunclone(m);
+
+  if (DEBUGLEVEL >= 4)
+  {
+    if (DEBUGLEVEL >= 7)
+    {
+      err_printf("\\\\ KERNEL COMPUTED BY MPQS\n");
+      err_printf("KERNEL=%Ps\n",ker_m);
+    }
+    err_printf("MPQS: Gauss done: kernel has rank %ld, taking gcds...\n", rank);
+  }
+
+  H_rows = rel;
+  H_cols = rank;
+
+  if (!H_cols)
+  { /* trivial kernel. Fail gracefully: main loop may look for more relations */
+    if (DEBUGLEVEL >= 3)
+      pari_warn(warner, "MPQS: no solutions found from linear system solver");
+    pari_free(fpos); /* ei not yet allocated */
+    avma = av; return NULL; /* no factors found */
+  }
+
+  /* If the rank is r, we can expect up to 2^r pairwise coprime factors,
+   * but it may happen that a kernel basis vector contributes nothing new to
+   * the decomposition.  We allocate room for up to eight factors initially
+   * (certainly adequate when one or two basis vectors work), adjusting this
+   * down at the end to what we actually found, or up if we are very lucky and
+   * find more factors.  In the upper half of our vector, we store information
+   * about which factors we know to be composite (zero) or believe to be
+   * composite (NULL) or suspect to be prime (one), or an exponent (two
+   * or some t_INT) if it is a proper power */
+  av2 = avma; lim = stack_lim(av2,1);
+  if (rank > (long)BITS_IN_LONG - 2)
+    res_max = LONG_MAX; /* the common case, unfortunately */
+  else
+    res_max = 1L<<rank; /* max number of factors we can hope for */
+  res_size = 8; /* no. of factors we can store in this res */
+  res = cgetg(2*res_size+1, t_VEC);
+  for (i=2*res_size; i; i--) res[i] = 0;
+  res_next = res_last = 1;
+
+  ei = (long *) pari_malloc((h->size_of_FB + 2) * sizeof(long));
+
+  for (i = 1; i <= H_cols; i++)
+  { /* loop over kernel basis */
+    X = Y_prod = gen_1;
+    memset((void *)ei, 0, (h->size_of_FB + 2) * sizeof(long));
+
+    av3 = avma; lim3 = stack_lim(av3,1);
+    for (j = 1; j <= H_rows; j++)
+    {
+      if (F2m_coeff(ker_m, j, i))
+        Y_prod = mpqs_add_relation(Y_prod, N, ei,
+                                   mpqs_get_relation(buf, fpos[j-1], pFREL));
+      if (low_stack(lim3, stack_lim(av3,1)))
+      {
+        if(DEBUGMEM>1) pari_warn(warnmem,"[1]: mpqs_solve_linear_system");
+        Y_prod = gerepileuptoint(av3, Y_prod);
+      }
+    }
+    Y_prod = gerepileuptoint(av3, Y_prod);
+
+    av3 = avma; lim3 = stack_lim(av3,1);
+    for (j = 2; j <= h->size_of_FB + 1; j++)
+      if (ei[j])
+      {
+        if (ei[j] & 1) pari_err_BUG("MPQS (relation is a nonsquare)");
+        X = remii(mulii(X,
+                        Fp_powu(utoipos(FB[j].fbe_p), (ulong)ei[j]>>1, N)),
+                  N);
+        if (low_stack(lim3, stack_lim(av3,1)))
+        {
+          if(DEBUGMEM>1) pari_warn(warnmem,"[2]: mpqs_solve_linear_system");
+          X = gerepileupto(av3, X);
+        }
+      }
+    X = gerepileuptoint(av3, X);
+    if (MPQS_DEBUGLEVEL >= 1)
+    {
+      if (signe(remii(subii(sqri(X), sqri(Y_prod)), N)))
+      { /* shouldn't happen */
+        err_printf("MPQS: X^2 - Y^2 != 0 mod N\n");
+        err_printf("\tindex i = %ld\n", i);
+        pari_warn(warner, "MPQS: wrong relation found after Gauss");
+      }
+    }
+    /* At this point, X^2 == Y^2 mod N.  Indeed, something stronger is true:
+     * We have gcd(X-Y, N) * gcd(X+Y, N) == N.  Why?
+     * First, N divides X^2 - Y^2, so it divides the lefthand side.
+     * Second, let P be any prime factor of N.  If P were to divide both
+     * X-Y and X+Y, then it would divide their sum 2X.  But X (in the present
+     * backwards notation!) is a product of powers of FB primes, and no FB
+     * prime is a divisor of N, or we would have found out about it already
+     * whilst constructing the FB.
+     * Therefore in the following it is sufficient to work with gcd(X+Y, N)
+     * alone, and never look at gcd(X-Y, N).
+     */
+    done = 0; /* (re-)counts probably-prime factors (or powers whose bases we
+               * don't want to handle any further) */
+    X_plus_Y = addii(X, Y_prod);
+    if (res_next < 3)
+    { /* we still haven't decomposed the original N, and want both a gcd and
+       * its cofactor. */
+      D1 = gcdii(X_plus_Y, N);
+      if (is_pm1(D1) || equalii(D1,N)) { avma = av3; continue; }
+      /* got something that works */
+      if (DEBUGLEVEL >= 5)
+        err_printf("MPQS: splitting N after %ld kernel vector%s\n",
+                   i+1, (i? "s" : ""));
+      /* GN20050707 Fixed:
+       * Don't divide N in place. We still need it for future X and Y_prod
+       * computations! */
+      gel(res,1) = diviiexact(N, D1);
+      gel(res,2) = D1;
+      res_last = res_next = 3;
+
+      if ( split(gel(res,1),  &gel(res,res_size+1), &gel(res,1)) ) done++;
+      if ( split(D1, &gel(res,res_size+2), &gel(res,2)) ) done++;
+      if (done == 2) break;     /* both factors look prime or were powers */
+      /* GN20050707: moved following line down to here, was before the
+       * two split() invocations.  Very rare case anyway. */
+      if (res_max == 2) break; /* two out of two possible factors seen */
+      if (DEBUGLEVEL >= 5)
+        err_printf("MPQS: got two factors, looking for more...\n");
+    }
+    else
+    { /* we already have factors */
+      for (j=1; j < res_next; j++)
+      { /* loop over known-composite factors */
+        if (gel(res,res_size+j) && gel(res,res_size+j) != gen_0)
+        {
+          done++; continue; /* skip probable primes etc */
+        }
+        /* actually, also skip square roots of squares etc.  They are a lot
+         * smaller than the original N, and should be easy to deal with later */
+        av3 = avma;
+        D1 = gcdii(X_plus_Y, gel(res,j));
+        if (is_pm1(D1) || equalii(D1, gel(res,j))) { avma = av3; continue; }
+        /* got one which splits this factor */
+        if (DEBUGLEVEL >= 5)
+          err_printf("MPQS: resplitting a factor after %ld kernel vectors\n",
+                     i+1);      /* always plural */
+        /* first make sure there's room for another factor */
+        if (res_next > res_size)
+        { /* need to reallocate (_very_ rare case) */
+          long i1, size = 2*res_size;
+          GEN RES;
+          if (size > res_max) size = res_max;
+          RES = cgetg(2*size+1, t_VEC);
+          for (i1=2*size; i1>=res_next; i1--) gel(RES,i1) = NULL;
+          for (i1=1; i1<res_next; i1++)
+          {
+            /* GN20050707:
+             * on-stack contents of RES must be rejuvenated */
+            icopyifstack(gel(res,i1), gel(RES,i1)); /* factors */
+            if ( gel(res,res_size+i1) )
+              icopyifstack(gel(res,res_size+i1), gel(RES,size+i1));
+            /* primality tags */
+          }
+          res = RES; res_size = size;   /* res_next unchanged */
+        }
+        /* now there is room; divide into existing factor and store the
+           new gcd */
+        diviiz(gel(res,j), D1, gel(res,j));
+        gel(res,res_next) = D1;
+
+        /* following overwrites the old known-composite indication at j */
+        if (split( gel(res,j), &gel(res,res_size+j), &gel(res,j)) ) done++;
+        /* GN20050707 Fixed:
+         * Don't increment done when the newly stored factor seems to be
+         * prime or otherwise devoid of interest - this happens later
+         * when we routinely revisit it during the present inner loop. */
+        (void)split(D1, &gel(res,res_size+res_next), &gel(res,res_next));
+
+        /* GN20050707: Following line moved down to here, was before the
+         * two split() invocations. */
+        if (++res_next > res_max)
+        {
+          /* all possible factors seen, outer loop postprocessing will
+           * proceed to break out of the outer loop below. */
+          break;
+        }
+      }       /* loop over known composite factors */
+
+      if (res_next > res_last)
+      {
+        res_last = res_next - 1; /* we might have resplit more than one */
+        if (DEBUGLEVEL >= 5)
+          err_printf("MPQS: got %ld factors%s\n", res_last,
+                     (done < res_last ? ", looking for more..." : ""));
+        res_last = res_next;
+      }
+      /* break out of the outer loop when we have seen res_max factors, and
+       * also when all current factors are probable primes */
+      if (res_next > res_max || done == res_next - 1) break;
+    } /* end case of further splitting of existing factors */
+    if (low_stack(lim, stack_lim(av2,1)))
+    {
+      long i1;
+      if(DEBUGMEM>1) pari_warn(warnmem,"[3]: mpqs_solve_linear_system");
+      /* gcopy would have a problem with our NULL pointers... */
+      new_res = cgetg(lg(res), t_VEC);
+      for (i1=2*res_size; i1>=res_next; i1--) new_res[i1] = 0;
+      for (i1=1; i1<res_next; i1++)
+      {
+        icopyifstack(gel(res,i1), gel(new_res,i1));
+        /* GN20050707: the marker GENs might need rejuvenating, too */
+        if (gel(res,res_size+i1))
+          icopyifstack(gel(res,res_size+i1), gel(new_res,res_size+i1));
+      }
+      res = gerepileupto(av2, new_res);
+    }
+  } /* for (loop over kernel basis) */
+
+  pari_free(ei); pari_free(fpos);
+  if (res_next < 3) { avma = av; return NULL; } /* no factors found */
+
+  /* normal case:  convert internal format to ifac format as described in
+   * src/basemath/ifactor1.c  (triples of components: value, exponent, class
+   * [unknown, known composite, known prime]),  clean up and return result */
+  res_last = res_next - 1; /* number of distinct factors found */
+  new_res = cgetg(3*res_last + 1, t_VEC);
+  if (DEBUGLEVEL >= 6)
+    err_printf("MPQS: wrapping up vector of %ld factors\n", res_last);
+  for (i=1,j=1; i <= res_last; i++)
+  {
+    GEN F = gel(res, res_size+i);
+    icopyifstack(gel(res,i), gel(new_res,j++)); /* factor */
+    gel(new_res,j++) = /* exponent */
+      F ? (F == gen_0 ? gen_1
+                      : (isonstack(F) ? icopy(F) : F))
+        : gen_1; /* F was NULL */
+    gel(new_res,j++) = /* class */
+      F == gen_0 ? gen_0 :      /* known composite */
+        NULL;           /* base of power or suspected prime --
+                                   mark as `unknown' */
+    if (DEBUGLEVEL >= 6)
+      err_printf("\tpackaging %ld: %Ps ^%ld (%s)\n", i, res[i],
+                 itos(gel(new_res,j-2)), (F == gen_0 ? "comp." : "unknown"));
+  }
+  return gerepileupto(av, new_res);
+}
+
+/*********************************************************************/
+/**                                                                 **/
+/**               MAIN ENTRY POINT AND DRIVER ROUTINE               **/
+/**                                                                 **/
+/*********************************************************************/
+
+
+/* All percentages below are actually fixed-point quantities scaled by 10
+ * (value of 1 means 0.1%, 1000 means 100%) */
+
+/* Factors N using the self-initializing multipolynomial quadratic sieve
+ * (SIMPQS).  Returns one of the two factors, or (usually) a vector of factors
+ * and exponents and information about which ones are still composite, or NULL
+ * when something goes wrong or when we can't seem to make any headway. */
+
+/* TODO: this function to be renamed mpqs_main() with several extra parameters,
+ * with mpqs() as a wrapper for the standard case, so we can do partial runs
+ * across several machines etc.  (from gp or a dedicated C program). --GN */
+static GEN
+mpqs_i(mpqs_handle_t *handle)
+{
+  GEN N = handle->N, fact; /* will in the end hold our factor(s) */
+  mpqs_int32_t size_of_FB; /* size of the factor base */
+  mpqs_FB_entry_t *FB; /* factor base */
+  mpqs_int32_t M;               /* sieve interval size [-M, M] */
+
+  /* local loop / auxiliary vars */
+  ulong p;
+
+  /* already exists in the handle, keep for convenience */
+  long lp_bound;                /* size limit for large primes */
+  long lp_scale;                /* ...relative to largest FB prime */
+
+  /* bookkeeping */
+  long tc;                      /* # of candidates found in one iteration */
+  long tp;                      /* # of recently sorted LP rels */
+  long tff = 0;                 /* # recently found full rels from sieving */
+  long tfc;                     /* # full rels recently combined from LPs */
+  double tfc_ratio = 0;         /* recent (tfc + tff) / tff */
+  ulong sort_interval;          /* determine when to sort and merge */
+  ulong followup_sort_interval; /* temporary files (scaled percentages) */
+  long percentage = 0;          /* scaled by 10, see comment above */
+  double net_yield;
+  long total_full_relations = 0, total_partial_relations = 0, total_no_cand = 0;
+  long vain_iterations = 0, good_iterations = 0, iterations = 0;
+#ifdef MPQS_USE_HISTOGRAMS
+  long histo_checkpoint = MPQS_MIN_CANDS_FOR_HISTO;
+#endif
+
+  pariFILE *pFNEW, *pLPNEW, *pCOMB, *pFREL, *pLPREL;
+  char *dir, *COMB_str, *FREL_str, *FNEW_str, *LPREL_str, *LPNEW_str, *TMP_str;
+  pari_timer T;
+
+/* END: global variables to disappear as soon as possible */
+
+/******************************/
+
+  pari_sp av = avma;
+
+  if (DEBUGLEVEL >= 4)
+  {
+    timer_start(&T);
+    err_printf("MPQS: number to factor N = %Ps\n", N);
+  }
+
+  handle->digit_size_N = decimal_len(N);
+  if (handle->digit_size_N > MPQS_MAX_DIGIT_SIZE_KN)
+  {
+    pari_warn(warner, "MPQS: number too big to be factored with MPQS,\n\tgiving up");
+    return NULL;
+  }
+
+  if (DEBUGLEVEL >= 4)
+    err_printf("MPQS: factoring number of %ld decimal digits\n",
+               handle->digit_size_N);
+
+  p = mpqs_find_k(handle);
+  if (p) { avma = av; return utoipos(p); }
+  if (DEBUGLEVEL >= 5) err_printf("MPQS: found multiplier %ld for N\n",
+                                  handle->_k->k);
+  handle->kN = muliu(N, handle->_k->k);
+
+  if (!mpqs_set_parameters(handle))
+  {
+    pari_warn(warner,
+        "MPQS: number too big to be factored with MPQS,\n\tgiving up");
+    return NULL;
+  }
+
+  size_of_FB = handle->size_of_FB;
+  M = handle->M;
+  sort_interval = handle->first_sort_point;
+  followup_sort_interval = handle->sort_pt_interval;
+
+  if (DEBUGLEVEL >= 5)
+    err_printf("MPQS: creating factor base and allocating arrays...\n");
+  FB = mpqs_create_FB(handle, &p);
+  if (p) { avma = av; return utoipos(p); }
+  mpqs_sieve_array_ctor(handle);
+  mpqs_poly_ctor(handle);
+
+  lp_bound = handle->largest_FB_p;
+  if (lp_bound > MPQS_LP_BOUND) lp_bound = MPQS_LP_BOUND;
+  /* don't allow large primes to have room for two factors both bigger than
+   * what the FB contains (...yet!) */
+  lp_scale = handle->lp_scale;
+  if (lp_scale >= handle->largest_FB_p)
+    lp_scale = handle->largest_FB_p - 1;
+  lp_bound *= lp_scale;
+  handle->lp_bound = lp_bound;
+
+  handle->dkN = gtodouble(handle->kN);
+  /* compute the threshold and fill in the byte-sized scaled logarithms */
+  mpqs_set_sieve_threshold(handle);
+
+  if (!mpqs_locate_A_range(handle)) return NULL;
+
+  if (DEBUGLEVEL >= 4)
+  {
+    err_printf("MPQS: sieving interval = [%ld, %ld]\n", -(long)M, (long)M);
+    /* that was a little white lie, we stop one position short at the top */
+    err_printf("MPQS: size of factor base = %ld\n",
+               (long)size_of_FB);
+    err_printf("MPQS: striving for %ld relations\n",
+               (long)handle->target_no_rels);
+    err_printf("MPQS: coefficients A will be built from %ld primes each\n",
+               (long)handle->omega_A);
+    err_printf("MPQS: primes for A to be chosen near FB[%ld] = %ld\n",
+               (long)handle->index2_FB,
+               (long)FB[handle->index2_FB].fbe_p);
+    err_printf("MPQS: smallest prime used for sieving FB[%ld] = %ld\n",
+               (long)handle->index1_FB,
+               (long)FB[handle->index1_FB].fbe_p);
+    err_printf("MPQS: largest prime in FB = %ld\n",
+               (long)handle->largest_FB_p);
+    err_printf("MPQS: bound for `large primes' = %ld\n", (long)lp_bound);
+  }
+
+  if (DEBUGLEVEL >= 5)
+  {
+    err_printf("MPQS: sieve threshold = %u\n",
+               (unsigned int)handle->sieve_threshold);
+  }
+
+  if (DEBUGLEVEL >= 4)
+  {
+    err_printf("MPQS: first sorting at %ld%%, then every %3.1f%% / %3.1f%%\n",
+               sort_interval/10, followup_sort_interval/10.,
+               followup_sort_interval/20.);
+  }
+
+
+  /* main loop which
+   * - computes polynomials and their zeros (SI)
+   * - does the sieving
+   * - tests candidates of the sieve array */
+
+  /* Let (A, B_i) the current pair of coeffs. If i == 0 a new A is generated */
+  handle->index_j = (mpqs_uint32_t)-1;  /* increment below will have it start at 0 */
+
+  if (DEBUGLEVEL >= 5) err_printf("MPQS: starting main loop\n");
+  /* compute names for the temp files we'll need */
+  dir = pari_unique_dir("MPQS");
+  TMP_str   = mpqs_get_filename(dir, "LPTMP");
+  FREL_str  = mpqs_get_filename(dir, "FREL");
+  FNEW_str  = mpqs_get_filename(dir, "FNEW");
+  LPREL_str = mpqs_get_filename(dir, "LPREL");
+  LPNEW_str = mpqs_get_filename(dir, "LPNEW");
+  COMB_str  = mpqs_get_filename(dir, "COMB");
+#define unlink_all()\
+      pari_unlink(FREL_str);\
+      pari_unlink(FNEW_str);\
+      pari_unlink(LPREL_str);\
+      pari_unlink(LPNEW_str);\
+      if (pCOMB) pari_unlink(COMB_str);\
+      rmdir(dir); pari_free(dir);
+
+  pFREL = pari_fopen_or_fail(FREL_str,  WRITE); pari_fclose(pFREL);
+  pLPREL = pari_fopen_or_fail(LPREL_str,  WRITE); pari_fclose(pLPREL);
+  pFNEW = pari_fopen_or_fail(FNEW_str,  WRITE);
+  pLPNEW= pari_fopen_or_fail(LPNEW_str, WRITE);
+  pCOMB = NULL;
+
+  for(;;)
+  { /* FNEW and LPNEW are open for writing */
+    iterations++;
+    /* self initialization: compute polynomial and its zeros */
+    mpqs_self_init(handle);
+    if (handle->bin_index == 0)
+    { /* have run out of primes for A */
+      /* We might change some parameters.  For the moment, simply give up */
+      if (DEBUGLEVEL >= 2)
+        err_printf("MPQS: Ran out of primes for A, giving up.\n");
+      pari_fclose(pFNEW);
+      pari_fclose(pLPNEW);
+      /* FREL, LPREL are closed at this point */
+      unlink_all(); avma = av; return NULL;
+    }
+
+    memset((void*)(handle->sieve_array), 0, (M << 1) * sizeof(unsigned char));
+    mpqs_sieve(handle);
+
+    tc = mpqs_eval_sieve(handle);
+    total_no_cand += tc;
+    if (DEBUGLEVEL >= 6)
+      err_printf("MPQS: found %lu candidate%s\n", tc, (tc==1? "" : "s"));
+
+    if (tc)
+    {
+      long t = mpqs_eval_cand(handle, tc, pFNEW->file, pLPNEW->file);
+      total_full_relations += t;
+      tff += t;
+      good_iterations++;
+    }
+
+#ifdef MPQS_USE_HISTOGRAMS
+    if (handle->do_histograms && !handle->done_histograms &&
+        total_no_cand >= histo_checkpoint)
+    {
+      int res = mpqs_eval_histograms(handle);
+      if (res >= 0)
+      {
+        /* retry later */
+        if (res > 0)
+          /* histo_checkpoint *= 2.6; */
+          handle->do_histograms = 0; /* no, don't retry later */
+        else
+          histo_checkpoint += (MPQS_MIN_CANDS_FOR_HISTO /* >> 1 */);
+      }
+      else
+        handle->done_histograms = 1;
+    }
+#endif
+
+    percentage =
+      (long)((1000.0 * total_full_relations) / handle->target_no_rels);
+
+    if ((ulong)percentage < sort_interval) continue;
+    /* most main loops continue here! */
+
+    /* Extra processing when we have completed a sort interval: */
+    if (DEBUGLEVEL >= 3)
+    {
+      if (DEBUGLEVEL >= 4)
+        err_printf("\nMPQS: passing the %3.1f%% sort point, time = %ld ms\n",
+                   sort_interval/10., timer_delay(&T));
+      else
+        err_printf("\nMPQS: passing the %3.1f%% sort point\n",
+                   sort_interval/10.);
+      err_flush();
+    }
+
+    /* sort LPNEW and merge it into LPREL, diverting combinables into COMB */
+    pari_fclose(pLPNEW);
+    (void)mpqs_sort_lp_file(LPNEW_str);
+    pCOMB = pari_fopen_or_fail(COMB_str, WRITE);
+    tp = mpqs_mergesort_lp_file(LPREL_str, LPNEW_str, TMP_str, pCOMB);
+    pari_fclose(pCOMB);
+    pLPNEW = pari_fopen_or_fail(LPNEW_str, WRITE);
+
+    /* combine whatever there is to be combined */
+    tfc = 0;
+    if (tp > 0)
+    {
+      /* build full relations out of large prime relations */
+      pCOMB = pari_fopen_or_fail(COMB_str, READ);
+      tfc = mpqs_combine_large_primes(handle, pCOMB->file, pFNEW, &fact);
+      pari_fclose(pCOMB);
+      /* now FREL, LPREL are closed and FNEW, LPNEW are still open */
+      if (fact)
+      { /* factor found during combining */
+        if (DEBUGLEVEL >= 4)
+        {
+          err_printf("\nMPQS: split N whilst combining, time = %ld ms\n",
+                     timer_delay(&T));
+          err_printf("MPQS: found factor = %Ps\n", fact);
+        }
+        pari_fclose(pLPNEW);
+        pari_fclose(pFNEW);
+        unlink_all();
+        return gerepileupto(av, fact);
+      }
+      total_partial_relations += tp;
+    }
+
+    /* sort FNEW and merge it into FREL */
+    pari_fclose(pFNEW);
+    (void)mpqs_sort_lp_file(FNEW_str);
+    /* definitive count (combinables combined, and duplicates removed) */
+    total_full_relations = mpqs_mergesort_lp_file(FREL_str, FNEW_str, TMP_str, NULL);
+    /* FNEW stays closed until we need to reopen it for another iteration */
+
+    /* Due to the removal of duplicates, percentage may actually decrease at
+     * this point.  Looks funny in the diagnostics but is nothing to worry
+     * about: we _are_ making progress. */
+    percentage =
+      (long)((1000.0 * total_full_relations) / handle->target_no_rels);
+    net_yield =
+      (total_full_relations * 100.) / (total_no_cand ? total_no_cand : 1);
+    vain_iterations =
+      (long)((1000.0 * (iterations - good_iterations)) / iterations);
+
+    /* Now estimate the current full relations yield rate:  we directly see
+     * each time through the main loop how many full relations we're getting
+     * as such from the sieve  (tff since the previous checkpoint),  but
+     * only at checkpoints do we see how many we're typically combining
+     * (tfc).  So we're really producing (tfc+tff)/tff as many full rels,
+     * and when we get close to 100%, we should bias the next interval by
+     * the inverse ratio.
+     * Avoid drawing conclusions from too-small samples during very short
+     * follow-on intervals  (in this case we'll just re-use an earlier
+     * estimated ratio). */
+    if ((tfc >= 16) && (tff >= 20))
+      tfc_ratio = (tfc + tff + 0.) / tff; /* floating-point division */
+    tff = 0;                    /* reset this count (tfc is always fresh) */
+
+    if (percentage >= 1000)     /* when Gauss had failed */
+      sort_interval = percentage + 2;
+    else if (percentage >= 820)
+    {
+      if (tfc_ratio > 1.)
+      {
+        if (percentage + (followup_sort_interval >> 1) * tfc_ratio > 994)
+        {
+          /* aim for a _slight_ overshoot */
+          sort_interval = (ulong)(percentage + 2 +
+            (1000 - percentage) / tfc_ratio);
+        }
+        else if (percentage >= 980)
+          sort_interval = percentage + 8;
+        else
+          sort_interval = percentage + (followup_sort_interval >> 1);
+      }
+      else
+      {
+        if (percentage >= 980)
+          sort_interval = percentage + 10;
+        else
+          sort_interval = percentage + (followup_sort_interval >> 1);
+        if (sort_interval >= 1000 && percentage < 1000)
+          sort_interval = 1000;
+      }
+    }
+    else
+      sort_interval = percentage + followup_sort_interval;
+
+    if (DEBUGLEVEL >= 4)
+    {
+      err_printf("MPQS: done sorting%s, time = %ld ms\n",
+                 tp > 0 ? " and combining" : "", timer_delay(&T));
+      err_printf("MPQS: found %3.1f%% of the required relations\n",
+                 percentage/10.);
+      if (DEBUGLEVEL >= 5)
+      { /* total_full_relations are always plural */
+        /* GN20050708: present code doesn't succeed in discarding all
+         * dups, so don't lie about it... */
+        err_printf("MPQS: found %ld full relations\n",
+                   total_full_relations);
+        if (lp_scale > 1)
+        err_printf("MPQS:   (%ld of these from partial relations)\n",
+                   total_partial_relations);
+        err_printf("MPQS: Net yield: %4.3g full relations per 100 candidates\n",
+                   net_yield);
+        err_printf("MPQS:            %4.3g full relations per 100 polynomials\n",
+                   (total_full_relations * 100.) / iterations);
+        err_printf("MPQS: %4.1f%% of the polynomials yielded no candidates\n",
+                   vain_iterations/10.);
+        err_printf("MPQS: next sort point at %3.1f%%\n", sort_interval/10.);
+      }
+    }
+
+    if (percentage < 1000)
+    {
+      pFNEW = pari_fopen_or_fail(FNEW_str, WRITE);
+      /* LPNEW and FNEW are again open for writing */
+      continue; /* main loop */
+    }
+
+    /* percentage >= 1000, which implies total_full_relations > size_of_FB:
+       try finishing it off */
+
+    /* solve the system over F_2 */
+    /* present code does NOT in fact guarantee absence of dup FRELs,
+     * therefore removing the adjective "distinct" for the time being */
+    if (DEBUGLEVEL >= 4)
+      err_printf("\nMPQS: starting Gauss over F_2 on %ld relations\n",
+                 total_full_relations);
+    pFREL = pari_fopen_or_fail(FREL_str, READ);
+    fact = mpqs_solve_linear_system(handle, pFREL, total_full_relations);
+    pari_fclose(pFREL);
+
+    if (fact)
+    { /* solution found */
+      if (DEBUGLEVEL >= 4)
+      {
+        err_printf("\nMPQS: time in Gauss and gcds = %ld ms\n", timer_delay(&T));
+        if (typ(fact) == t_INT) err_printf("MPQS: found factor = %Ps\n", fact);
+        else
+        {
+          long j, nf = (lg(fact)-1)/3;
+          if (nf == 2)
+            /* GN20050707: Changed the arrangement of the two factors,
+             * to match the debug diagnostics in mpqs_solve_linear_system()
+             * above */
+            err_printf("MPQS: found factors = %Ps\n\tand %Ps\n",
+                        fact[1], fact[4]);
+          else
+          {
+            /* GN20050707: Changed loop to scan upwards instead of downwards,
+             * to match the debug diagnostics in mpqs_solve_linear_system()
+             * above */
+            err_printf("MPQS: found %ld factors =\n", nf);
+            for (j=1; j<=nf; j++)
+              err_printf("\t%Ps%s\n", fact[3*j-2], (j<nf ? "," : ""));
+          }
+        }
+      }
+      pari_fclose(pLPNEW);
+      unlink_all();
+      /* fact not safe for a gerepilecopy(): segfaults on one of the NULL
+       * markers. However, it is a nice connected object, and it resides
+       * already the top of the stack, so... --GN */
+      return gerepileupto(av, fact);
+    }
+    else
+    {
+      if (DEBUGLEVEL >= 4)
+      {
+        err_printf("\nMPQS: time in Gauss and gcds = %ld ms\n",timer_delay(&T));
+        err_printf("MPQS: no factors found.\n");
+        if (percentage <= MPQS_ADMIT_DEFEAT)
+          err_printf("\nMPQS: restarting sieving ...\n");
+        else
+          err_printf("\nMPQS: giving up.\n");
+      }
+      if (percentage > MPQS_ADMIT_DEFEAT)
+      {
+        pari_fclose(pLPNEW);
+        unlink_all(); avma = av; return NULL;
+      }
+      pFNEW = pari_fopen_or_fail(FNEW_str, WRITE);
+    }
+  } /* main loop */
+}
+GEN
+mpqs(GEN N)
+{
+  mpqs_handle_t *handle = mpqs_handle_ctor(N);
+  GEN fact = mpqs_i(handle);
+  mpqs_handle_dtor(handle); return fact;
+}
diff --git a/src/modules/mpqs.h b/src/modules/mpqs.h
new file mode 100644
index 0000000..1a24a5e
--- /dev/null
+++ b/src/modules/mpqs.h
@@ -0,0 +1,502 @@
+/* - debug support */
+
+#ifdef MPQS_DEBUG_VERYVERBOSE
+#  ifndef MPQS_DEBUG_VERBOSE
+#  define MPQS_DEBUG_VERBOSE
+#  endif
+#endif
+
+#ifdef MPQS_DEBUG_VERBOSE
+#  ifndef MPQS_DEBUG
+#  define MPQS_DEBUG
+#  endif
+#  define PRINT_IF_VERBOSE(x) err_printf(x)
+#else
+#  define PRINT_IF_VERBOSE(x)
+#endif
+
+#ifdef MPQS_DEBUG
+#  define MPQS_DEBUGLEVEL 1000  /* infinity */
+#else
+#  define MPQS_DEBUGLEVEL DEBUGLEVEL
+#endif
+
+/* - string and external file stuff for the relations "database" */
+
+#ifndef SEEK_SET
+#  define SEEK_SET 0
+#endif
+
+#ifdef __CYGWIN32__
+/* otherwise fseek() goes crazy due to silent \n <--> LF translations */
+#  define WRITE "wb"
+#  define READ "rb"
+#else
+#  define WRITE "w"
+#  define READ "r"
+#endif
+
+#define MPQS_STRING_LENGTH         (4 * 1024UL)
+
+/* - non-configurable sizing parameters */
+
+#define MPQS_POSSIBLE_MULTIPLIERS  5 /* how many values for k we'll try */
+/* following must be in range of the cand_multipliers table below */
+#define MPQS_MULTIPLIER_SEARCH_DEPTH 5 /* how many primes to inspect per k */
+
+/* `large primes' must be smaller than
+ *   min(MPQS_LP_BOUND, largest_FB_p) * MPQS_LP_FACTOR
+ * - increased this with the idea of capping it at about 2^30 */
+#define MPQS_LP_BOUND              12500000 /* works for 32 and 64bit */
+
+/* see mpqs_locate_A_range() for an explanation of the following.  I had
+ * some good results with about -log2(0.85) but in the range I was testing,
+ * this shifts the primes for A only by one position in the FB.  Don't go
+ * over the top with this one... */
+#define MPQS_A_FUDGE               0.15 /* ~ -log2(0.9) */
+
+#define MPQS_CANDIDATE_ARRAY_SIZE  2000 /* max. this many cand's per poly */
+
+#ifdef MPQS_USE_HISTOGRAMS
+/* histogram evaluation/feedback available when size_of_FB exceeds this: */
+#  define MPQS_MIN_SIZE_FB_FOR_HISTO 600
+/* min number of candidates to look at before evaluating histograms */
+#  define MPQS_MIN_CANDS_FOR_HISTO   4000
+/* min number of full relations to have been created before etc. */
+#  define MPQS_MIN_FRELS_FOR_HISTO   110
+
+/* see mpqs_eval_histograms() for explanation of the following */
+#  define MPQS_HISTO_FREL_QUANTILE   2.4
+#  define MPQS_HISTO_DROP_LIMIT      3.6
+#  define MPQS_HISTO_LPREL_BASEFLOW  1.4
+#endif
+
+/* give up when nothing found after ~1.5 times the required number of
+ * relations has been computed  (N might be a prime power or the
+ * parameters might be exceptionally unfortunate for it) */
+#define MPQS_ADMIT_DEFEAT 1500
+
+
+/* - structures, types, and constants */
+
+/* -- reasonably-sized integers */
+#ifdef LONG_IS_64BIT
+typedef int  mpqs_int32_t;
+typedef unsigned int  mpqs_uint32_t;
+typedef unsigned long mpqs_uint64_t;
+#else
+typedef long mpqs_int32_t;
+typedef unsigned long mpqs_uint32_t;
+typedef struct {
+  ulong _w0;
+  ulong _w1;
+} mpqs_uint64_t;
+#endif
+
+/* -- we'll sometimes want to use the machine's native size here, and
+ * sometimes  (for future double-large-primes)  force 64 bits - thus: */
+typedef union mpqs_invp {
+  ulong _ul;
+  mpqs_uint64_t _u64;
+} mpqs_invp_t;
+
+/* -- factor base entries should occupy 32 bytes  (and we'll keep them
+ * aligned, for good L1 cache hit rates).  Some of the entries will be
+ * abused for e.g. -1 and (factors of) k instead for real factor base
+ * primes, and for a sentinel at the end.  This is why __p is a signed
+ * field.-- The two start fields depend on the current polynomial and
+ * keep changing during sieving, the flags will also change depending on
+ * the current A. */
+/* Let (z1, z2) be the roots of Q(x) = A x^2 + Bx + C mod p_i; then
+ * Q(z1 + p_i Z) == 0 mod p_i and Q(z2 + p_i Z) == 0 mod p_i;
+ * start_1, start_2 are the positions where p_i divides Q(x) for the
+ * first time, already adjusted for the fact that the sieving array,
+ * nominally [-M, M], is represented by a 0-based C array of length
+ * 2M + 1.  For the prime factors of A and those of k, the two roots
+ * are equal mod p_i. */
+
+#define MPQS_FB_ENTRY_PAD 32
+
+typedef union mpqs_FB_entry {
+  char __pad[MPQS_FB_ENTRY_PAD];
+  struct {
+    mpqs_int32_t __p;           /* the prime p */
+    /* Following two are not yet used: */
+    float __flogp;              /* its logarithm as a 4-byte float */
+    mpqs_invp_t __invp;         /* 1/p mod 2^64 or 2^BITS_IN_LONG */
+
+    mpqs_int32_t __start1;      /* representatives of the two APs mod p */
+    mpqs_int32_t __start2;
+    mpqs_uint32_t __sqrt_kN;    /* sqrt(kN) mod p */
+    unsigned char __val;        /* 8-bit approx. scaled log for sieving */
+    unsigned char __flags;
+  } __entry;
+} mpqs_FB_entry_t;
+
+/* --- convenience accessor macros for the preceding: */
+#define fbe_p           __entry.__p
+#define fbe_flogp       __entry.__flogp
+#define fbe_invp        __entry.__invp
+#define fbe_start1      __entry.__start1
+#define fbe_start2      __entry.__start2
+#define fbe_sqrt_kN     __entry.__sqrt_kN
+#define fbe_logval      __entry.__val
+#define fbe_flags       __entry.__flags
+
+/* --- flag bits for fbe_flags: */
+/* TODO */
+
+#define MPQS_FBE_CLEAR       0x0 /* no flags */
+
+/* following used for odd FB primes, and applies to the divisors of A but not
+ * those of k.  Must occupy the rightmost bit because we also use it as a
+ * shift count after extracting it from the byte. */
+#define MPQS_FBE_DIVIDES_A   0x1ul /* and Q(x) mod p only has degree 1 */
+
+/* XX tentative: one bit to mark normal FB primes,
+ * XX one to mark the factors of k,
+ * XX one to mark primes used in sieving,
+ * XX later maybe one to mark primes of which we'll be tracking the square,
+ * XX one to mark primes currently in use for A;
+ * XX once we segment the FB, one bit marking the members of the first segment
+ */
+
+/* -- multiplier k and associated quantities: More than two prime factors
+* for k will be pointless in practice, thus capping them at two. */
+#define MPQS_MAX_OMEGA_K 2
+typedef struct mpqs_multiplier {
+  mpqs_uint32_t k;              /* the multiplier (odd, squarefree) */
+  mpqs_uint32_t omega_k;        /* number (>=0) of primes dividing k */
+  mpqs_uint32_t kp[MPQS_MAX_OMEGA_K]; /* prime factors of k, if any */
+} mpqs_multiplier_t;
+
+static const mpqs_multiplier_t cand_multipliers[] = {
+  {  1, 0, {  0,  0} },
+  {  3, 1, {  3,  0} },
+  {  5, 1, {  5,  0} },
+  {  7, 1, {  7,  0} },
+  { 11, 1, { 11,  0} },
+  { 13, 1, { 13,  0} },
+  { 15, 2, {  3,  5} },
+  { 17, 1, { 17,  0} },
+  { 19, 1, { 19,  0} },
+  { 21, 2, {  3,  7} },
+  { 23, 1, { 23,  0} },
+  { 29, 1, { 29,  0} },
+  { 31, 1, { 31,  0} },
+  { 33, 2, {  3, 11} },
+  { 35, 2, {  5,  7} },
+  { 37, 1, { 37,  0} },
+  { 39, 2, {  3, 13} },
+  { 41, 1, { 41,  0} },
+  { 43, 1, { 43,  0} },
+  { 47, 1, { 47,  0} },
+  { 51, 2, {  3, 17} },
+  { 53, 1, { 53,  0} },
+  { 55, 2, {  5, 11} },
+  { 57, 2, {  3, 19} },
+  { 59, 1, { 59,  0} },
+  { 61, 1, { 61,  0} },
+  { 65, 2, {  5, 13} },
+  { 67, 1, { 67,  0} },
+  { 69, 2, {  3, 23} },
+  { 71, 1, { 71,  0} },
+  { 73, 1, { 73,  0} },
+  { 77, 2, {  7, 11} },
+  { 79, 1, { 79,  0} },
+  { 83, 1, { 83,  0} },
+  { 85, 2, {  5, 17} },
+  { 87, 2, {  3, 29} },
+  { 89, 1, { 89,  0} },
+  { 91, 2, {  7, 13} },
+  { 93, 2, {  3, 31} },
+  { 95, 2, {  5, 19} },
+  { 97, 1, { 97,  0} }
+};
+
+/* -- the array of (Chinese remainder) idempotents which add/subtract up to
+ * the middle coefficient B, and for convenience, the FB subscripts of the
+ * primes in current use for A.  We keep these together since both arrays
+ * are of the same size and are used at the same times. */
+typedef struct mqps_per_A_prime {
+  GEN _H;                       /* summand for B */
+  mpqs_int32_t _i;              /* subscript into FB */
+} mpqs_per_A_prime_t;
+
+/* following cooperate with names of local variables in the self_init fcns.
+ * per_A_pr must exist and be an alias for the eponymous handle pointer for
+ * all of these, and FB must exist and correspond to the handle FB pointer
+ * for all but the first two of them. */
+#define MPQS_H(i) (per_A_pr[i]._H)
+#define MPQS_I(i) (per_A_pr[i]._i)
+#define MPQS_AP(i) (FB[per_A_pr[i]._i].fbe_p)
+#define MPQS_LP(i) (FB[per_A_pr[i]._i].fbe_flogp)
+#define MPQS_SQRT(i) (FB[per_A_pr[i]._i].fbe_sqrt_kN)
+#define MPQS_FLG(i) (FB[per_A_pr[i]._i].fbe_flags)
+
+
+/* -- the array of addends / subtrahends for changing polynomials during
+ * self-initialization: (1/A) H[i] mod p_j, with i subscripting the inner
+ * array in each entry, and j choosing the entry in an outer array.
+ * Entries will occupy 64 bytes each no matter what  (which imposes one
+ * sizing restriction: at most 17 prime factors for A;  thus i will range
+ * from 0 to at most 15.)  This wastes a little memory for smaller N but
+ * makes it easier for compilers to generate efficient code. */
+
+/* NOTE: At present, memory locality vis-a-vis accesses to this array is good
+ * in the slow (new A) branch of mpqs_self_init(), but poor in the fast
+ * (same A, new B) branch, which now loops over the outer array index,
+ * reading just one field of each inner array each time through the FB
+ * loop.  This doesn't really harm, but will improve one day when we do
+ * segmented sieve arrays with the associated segmented FB-range accesses. */
+#define MPQS_MAX_OMEGA_A 17
+typedef struct mpqs_inv_A_H {
+  mpqs_uint32_t _i[MPQS_MAX_OMEGA_A - 1];
+} mpqs_inv_A_H_t;
+
+#define MPQS_INV_A_H(i,j) (inv_A_H[j]._i[i])
+
+/* -- global handle for keeping track of everything used throughout any one
+ * factorization attempt.  The order of the fields is roughly determined by
+ * wanting to keep the most frequently used stuff near the beginning. */
+
+typedef struct mpqs_handle {
+  /* pointers into pari_malloc()d memory which must be freed at the end: */
+  unsigned char *sieve_array;   /* 0-based, representing [-M,M-1] */
+  unsigned char *sieve_array_end; /* points at sieve_array[M-1] */
+  mpqs_FB_entry_t *FB;          /* (aligned) FB array itself */
+  long *candidates;             /* collects promising sieve subscripts */
+  char *relations;              /* freshly found relations (strings) */
+  long *relaprimes;             /* prime/exponent pairs in a relation */
+  mpqs_inv_A_H_t *inv_A_H;      /* self-init: (aligned) stepping array, and */
+  mpqs_per_A_prime_t *per_A_pr; /* FB subscripts of primes in A etc. */
+
+  /* other stuff that's being used all the time */
+  mpqs_int32_t M;               /* sieving over |x| <= M */
+  mpqs_int32_t size_of_FB;      /* # primes in FB (or dividing k) */
+  /* the following three are in non-descending order, and the first two must
+   * be adjusted for omega_k at the beginning */
+  mpqs_int32_t index0_FB;       /* lowest subscript into FB of a "real" prime
+                                 * (i.e. other than -1, 2, factors of k) */
+  mpqs_int32_t index1_FB;       /* lowest subscript into FB for sieving */
+  mpqs_int32_t index2_FB;       /* primes for A are chosen relative to this */
+  unsigned char index2_moved;   /* true when we're starved for small A's */
+  unsigned char sieve_threshold; /* distinguishes candidates in sieve */
+#ifdef MPQS_USE_HISTOGRAMS
+  /* histogram feedback */
+  unsigned char do_histograms;  /* (boolean) enable histogram updating */
+  unsigned char done_histograms; /* histos have been eval'd for feedback */
+  /* more pari_malloc()d memory here: */
+  long *histo_full;             /* distribution of full rels from sieve */
+  long *histo_lprl;             /* - of LP rels from sieve */
+  long *histo_drop;             /* - of useless candidates */
+#endif
+  GEN N;                        /* given number to be factored */
+  GEN kN;                       /* N with multiplier (on PARI stack) */
+  /* quantities associated with the current polynomial; all these also
+   * live in preallocated slots on the PARI stack: */
+  GEN A;                        /* leading coefficient */
+  GEN B;                        /* middle coefficient */
+#ifdef MPQS_DEBUG
+  GEN C;                        /* and constant coefficient */
+#endif
+  mpqs_int32_t omega_A;         /* number of primes going into each A */
+  mpqs_int32_t no_B;            /* number of B's for each A: 2^(omega_A-1) */
+  double l2_target_A;           /* ~log2 of desired typical A */
+  /* counters and bit pattern determining and numbering the current
+   * polynomial: */
+  mpqs_uint32_t bin_index;      /* bit pattern for selecting primes for A */
+  mpqs_uint32_t index_i;        /* running count of A's */
+  mpqs_uint32_t index_j;        /* B's ordinal number in A's cohort */
+  /* TODO: one more to follow here... */
+
+  /* further sizing parameters: */
+  mpqs_int32_t target_no_rels;  /* target number of full relations */
+  mpqs_int32_t largest_FB_p;    /* largest prime in the FB */
+  mpqs_int32_t pmin_index1;        /* lower bound for primes used for sieving */
+  mpqs_int32_t lp_scale;        /* factor by which LPs may exceed FB primes */
+
+  mpqs_int32_t first_sort_point; /* when to sort and combine */
+  mpqs_int32_t sort_pt_interval; /* (in units of 1/1000) */
+
+  /* subscripts determining where to pick primes for A... */
+
+  /* FIXME: lp_bound might have to be mpqs_int64_t ? or mpqs_invp_t ? */
+  long lp_bound;                /* cutoff for Large Primes */
+  long digit_size_N;
+  long digit_size_kN;
+  const mpqs_multiplier_t *_k;  /* multiplier k and associated quantities */
+  double tolerance;             /* controls the tightness of the sieve */
+  double dkN;                   /* - double prec. approximation of kN */
+  double l2sqrtkN;              /* ~log2(sqrt(kN)) */
+  double l2M;                   /* ~log2(M) (cf. below) */
+  /* TODO: need an index2_FB here to remember where to start picking primes */
+
+  /* Put statistics here ? Currently keep them as local variables in mpqs() */
+
+  /* bookkeeping pointers to containers of aligned memory chunks: */
+  void *FB_chunk;               /* (unaligned) chunk containing the FB */
+  void *invAH_chunk;            /* (unaligned) chunk for self-init array */
+
+} mpqs_handle_t;
+
+/* -- sizing table entries */
+
+/* The "tolerance" is explained below apropos of mpqs_set_sieve_threshold().
+ * The LP scale, for very large kN, prevents us from accumulating vast amounts
+ * of LP relations with little chance of hitting any particular large prime
+ * a second time and being able to combine a full relation from two LP ones;
+ * however, the sieve threshold (determined by the tolerance) already works
+ * against very large LPs being produced.-- The present relations "database"
+ * can detect duplicate full relations only during the sort/combine phases,
+ * so we must do some sort points even for tiny kN where we do not admit
+ * large primes at all.
+ * Some constraints imposed by the present implementation:
+ * + omega_A should be at least 3, and no more than MPQS_MAX_OMEGA_A
+ * + The size of the FB must be large enough compared to omega_A
+ *   (about 2*omega_A + 3, but this is always true below) */
+/* XXX Changes needed for segmented mode:
+ * XXX When using it (kN large enough),
+ * XXX - M must become a multiple of the (cache block) segment size
+ * XXX   (or to keep things simple: a multiple of 32K)
+ * XXX - we need index3_FB to seperate (smaller) primes used for normal
+ * XXX   sieving from larger ones used with transaction buffers
+ * XXX   (and the locate_A_range and associated logic must be changed to
+ * XXX   cap index2_FB below index3_FB instead of below size_of_FB)
+ */
+typedef struct mpqs_parameterset {
+  float tolerance;              /* "mesh width" of the sieve */
+  /* XX following subject to further change */
+  mpqs_int32_t lp_scale;        /* factor by which LPs may exceed FB primes */
+  mpqs_int32_t M;               /* size of half the sieving interval */
+  mpqs_int32_t size_of_FB;      /* #primes to use for FB (including 2) */
+  mpqs_int32_t omega_A;         /* #primes to go into each A */
+  /* following is auto-adjusted to account for prime factors of k inserted
+   * near the start of the FB.  NB never ever sieve on the prime 2  (which
+   * would just contribute a constant at each sieve point). */
+  mpqs_int32_t pmin_index1;     /* lower bound for primes used for sieving */
+  /* the remaining two are expressed in percent  (of the target number of full
+   * relations),  and determine when we stop sieving to review the contents
+   * of the relations DB and sort them and combine full relations from LP
+   * ones.  Note that the handle has these in parts per thousand instead. */
+  mpqs_int32_t first_sort_point;
+  mpqs_int32_t sort_pt_interval;
+} mpqs_parameterset_t;
+
+/* - the table of sizing parameters itself */
+
+/* indexed by size of kN in decimal digits, subscript 0 corresponding to
+ * 9 (or fewer) digits */
+static const mpqs_parameterset_t mpqs_parameters[] =
+{ /*       tol lp_scl     M   szFB  oA pmx1 1st  sti */
+  {  /*9*/ 0.8,   1,    900,    20,  3,   5, 70,  8},
+  { /*10*/ 0.8,   1,    900,    21,  3,   5, 70,  8},
+  { /*11*/ 0.8,   1,    920,    22,  3,   5, 70,  6},
+  { /*12*/ 0.8,   1,    960,    24,  3,   5, 70,  6},
+  { /*13*/ 0.8,   1,   1020,    26,  3,   5, 70,  6},
+  { /*14*/ 0.8,   1,   1100,    29,  3,   5, 70,  6},
+  { /*15*/ 0.8,   1,   1200,    32,  3,   5, 60,  8},
+  { /*16*/ 0.8,   1,   1500,    35,  3,   5, 60,  8},
+  { /*17*/ 0.8,   1,   1900,    40,  3,   5, 60,  8},
+  { /*18*/ 0.8,   1,   2500,    60,  3,   5, 50, 10},
+  { /*19*/ 0.8,   1,   3200,    80,  3,   5, 50, 10},
+  { /*20*/ 0.8,   1,   4000,   100,  3,   5, 40, 10},
+  { /*21*/ 0.8,   1,   4300,   100,  3,   5, 40, 10},
+  { /*22*/ 0.8,   1,   4500,   120,  3,   5, 40, 10},
+  { /*23*/ 0.8,   1,   4800,   140,  3,   5, 30, 10},
+  { /*24*/ 0.8,   1,   5100,   160,  4,   7, 30, 10},
+  { /*25*/ 0.8,   1,   5400,   180,  4,   7, 30, 10},
+  { /*26*/ 0.9,   1,   5700,   200,  4,   7, 30, 10},
+  { /*27*/ 1.12,  1,   6000,   220,  4,   7, 30, 10},
+  { /*28*/ 1.17,  1,   6300,   240,  4,  11, 30, 10},
+  { /*29*/ 1.22,  1,   6500,   260,  4,  11, 30, 10},
+  { /*30*/ 1.30,  1,   6800,   325,  4,  11, 20, 10},
+  { /*31*/ 1.33,  1,   7000,   355,  4,  13, 20, 10},
+  { /*32*/ 1.36,  1,   7200,   375,  5,  13, 20, 10},
+  { /*33*/ 1.40,  1,   7400,   400,  5,  13, 20, 10},
+  { /*34*/ 1.43,  1,   7600,   425,  5,  17, 20, 10},
+  /* around here, sieving takes long enough to make it worthwhile recording
+   * LP relations into their separate output files, although they tend not
+   * yet to contribute a lot to the full relations until we get up to around
+   * 47 digits or so. */
+  { /*35*/ 1.48, 30,   7800,   550,  5,  17, 20, 10},
+  { /*36*/ 1.53, 45,   8100,   650,  5,  17, 20, 10},
+  { /*37*/ 1.60, 60,   9000,   750,  6,  19, 20, 10},
+  { /*38*/ 1.66, 70,  10000,   850,  6,  19, 20, 10},
+  { /*39*/ 1.69, 80,  11000,   950,  6,  23, 20, 10},
+  /* around here, the largest prime in FB becomes comparable to M in size */
+  { /*40*/ 1.69, 80,  12500,  1000,  6,  23, 20, 10},
+  { /*41*/ 1.69, 80,  14000,  1150,  6,  23, 10, 10},
+  { /*42*/ 1.69, 80,  15000,  1300,  6,  29, 10, 10},
+  { /*43*/ 1.69, 80,  16000,  1500,  6,  29, 10, 10},
+  { /*44*/ 1.69, 80,  17000,  1700,  7,  31, 10, 10},
+  { /*45*/ 1.69, 80,  18000,  1900,  7,  31, 10, 10},
+  { /*46*/ 1.69, 80,  20000,  2100,  7,  37, 10, 10},
+  { /*47*/ 1.69, 80,  25000,  2300,  7,  37, 10, 10},
+  { /*48*/ 1.69, 80,  27500,  2500,  7,  37, 10, 10},
+  { /*49*/ 1.72, 80,  30000,  2700,  7,  41, 10, 10},
+  { /*50*/ 1.75, 80,  35000,  2900,  7,  41, 10, 10},
+  { /*51*/ 1.80, 80,  40000,  3000,  7,  43, 10, 10},
+  { /*52*/ 1.85, 80,  50000,  3200,  7,  43, 10, 10},
+  { /*53*/ 1.90, 80,  60000,  3500,  7,  47, 10, 10},
+  { /*54*/ 1.95, 80,  70000,  3800,  7,  47, 10, 10},
+  { /*55*/ 1.95, 80,  80000,  4100,  7,  53, 10, 10},
+  { /*56*/ 1.95, 80,  90000,  4400,  7,  53, 10,  8},
+  { /*57*/ 2.00, 80, 100000,  4700,  8,  53, 10,  8},
+  { /*58*/ 2.05, 80, 110000,  5000,  8,  59, 10,  8},
+  { /*59*/ 2.10, 80, 120000,  5400,  8,  59, 10,  8},
+  { /*60*/ 2.15, 80, 130000,  5800,  8,  61, 10,  8},
+  { /*61*/ 2.20, 80, 140000,  6100,  8,  61, 10,  8},
+  { /*62*/ 2.25, 80, 150000,  6400,  8,  67, 10,  6},
+  { /*63*/ 2.39, 80, 160000,  6700,  8,  67, 10,  6},
+  { /*64*/ 2.30, 80, 165000,  7000,  8,  67, 10,  6},
+  { /*65*/ 2.31, 80, 170000,  7300,  8,  71, 10,  6},
+  { /*66*/ 2.32, 80, 175000,  7600,  8,  71, 10,  6},
+  { /*67*/ 2.33, 80, 180000,  7900,  8,  73, 10,  6},
+  { /*68*/ 2.34, 80, 185000,  8200,  8,  73, 10,  6},
+  { /*69*/ 2.35, 80, 190000,  8600,  8,  79,  8,  6},
+  { /*70*/ 2.36, 80, 195000,  8800,  8,  79,  8,  6},
+  { /*71*/ 2.37, 80, 200000,  9000,  9,  79,  8,  6},
+  { /*72*/ 2.38, 80, 205000,  9250,  9,  83,  5,  5},
+  { /*73*/ 2.41, 80, 210000,  9500,  9,  83,  5,  5},
+  { /*74*/ 2.46, 80, 220000,  9750,  9,  83,  5,  5},
+  { /*75*/ 2.51, 80, 230000, 10000,  9,  89,  5,  5},
+  { /*76*/ 2.56, 80, 240000, 10500,  9,  89,  5,  5},
+  { /*77*/ 2.58, 80, 250000, 11200,  9,  89,  5,  5},
+  { /*78*/ 2.60, 80, 260000, 12500,  9,  89,  5,  5},
+  { /*79*/ 2.63, 80, 270000, 14000,  9,  97,  5,  4},
+  { /*80*/ 2.65, 80, 280000, 15500,  9,  97,  5,  4},
+  { /*81*/ 2.72, 80, 300000, 17000,  9,  97,  4,  4},
+  { /*82*/ 2.77, 80, 320000, 18500,  9, 101,  4,  4},
+  { /*83*/ 2.82, 80, 340000, 20000, 10, 101,  4,  4},
+  { /*84*/ 2.84, 80, 360000, 21500, 10, 103,  4,  4},
+  { /*85*/ 2.86, 80, 400000, 23000, 10, 103,  4,  3},
+  { /*86*/ 2.88, 80, 460000, 24500, 10, 107,  4,  3},
+  /* architectures with 1MBy L2 cache will become noticeably slower here
+   * as 2*M exceeds that mark - to be addressed in a future version by
+   * segmenting the sieve interval */
+  { /*87*/ 2.90, 80, 520000, 26000, 10, 107,  4,  3},
+  { /*88*/ 2.91, 80, 580000, 27500, 10, 109,  4,  3},
+  { /*89*/ 2.92, 80, 640000, 29000, 10, 109,  4,  3},
+  { /*90*/ 2.93, 80, 700000, 30500, 10, 113,  2,  2},
+  { /*91*/ 2.94, 80, 770000, 32200, 10, 113,  2,  2},
+  /* entries below due to Thomas Denny, never tested */
+  { /*92*/ 3.6, 90, 2000000, 35000,  9, 113,  2,  2},
+  { /*93*/ 3.7, 90, 2000000, 37000,  9, 113,  2,  2},
+  { /*94*/ 3.7, 90, 2000000, 39500,  9, 127,  2,  2},
+  { /*95*/ 3.7, 90, 2500000, 41500,  9, 127,  2,  2},
+  { /*96*/ 3.8, 90, 2500000, 45000, 10, 127,  2,  2},
+  { /*97*/ 3.8, 90, 2500000, 47500, 10, 131,  2,  2},
+  { /*98*/ 3.7, 90, 3000000, 51000, 10, 131,  2,  2},
+  { /*99*/ 3.8, 90, 3000000, 53000, 10, 133,  2,  2},
+  {/*100*/ 3.8, 90, 3500000, 51000, 10, 133,  2,  2},
+  {/*101*/ 3.8, 90, 3500000, 54000, 10, 139,  2,  2},
+  {/*102*/ 3.8, 90, 3500000, 57000, 10, 139,  2,  2},
+  {/*103*/ 3.9, 90, 4000000, 61000, 10, 139,  2,  2},
+  {/*104*/ 3.9, 90, 4000000, 66000, 10, 149,  2,  2},
+  {/*105*/ 3.9, 90, 4000000, 70000, 10, 149,  2,  2},
+  {/*106*/ 3.9, 90, 4000000, 75000, 10, 151,  2,  2},
+  {/*107*/ 3.9, 90, 4000000, 80000, 10, 151,  2,  2},
+};
+
+#define MPQS_MAX_DIGIT_SIZE_KN 107
diff --git a/src/modules/part.c b/src/modules/part.c
new file mode 100644
index 0000000..1f5c91e
--- /dev/null
+++ b/src/modules/part.c
@@ -0,0 +1,413 @@
+/* Copyright (C) 2002  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Original code contributed by: Ralf Stephan (ralf at ark.in-berlin.de).
+ *
+ * This program is basically the implementation of the script
+ *
+ * Psi(n, q) = my(a=sqrt(2/3)*Pi/q, b=n-1/24, c=sqrt(b));
+ *             (sqrt(q)/(2*sqrt(2)*b*Pi))*(a*cosh(a*c)-(sinh(a*c)/c))
+ * L(n,q)=if(q==1,1,sum(h=1,q-1,if(gcd(h,q)==1, cos((g(h,q)-24*h*n)*Pi/(12*q))))
+ * g(h, q) = if(q>=3, 12*sum(k=1,q-1,k*(frac(h*k/q)-1/2)))
+ *         \\ sumdedekind(h,q)*12*q
+ * part(n) = round(sum(q=1,5 + 0.24*sqrt(n),L(n,q)*Psi(n,q)))
+ *
+ * only faster. It is a translation of the C/mpfr version at
+ * http://www.ark.in-berlin.de/part.c
+ *
+ * ------------------------------------------------------------------
+ *   The first restriction depends on Pari's maximum precision of floating
+ * point reals, which is 268435454 bits in 2.2.4, since the algorithm needs
+ * high precision exponentials. For that engine, the maximum possible argument
+ * would be in [5*10^15,10^16], the computation of which would need days on
+ * a ~1-GHz computer. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/****************************************************************/
+
+/* Given:  b = N-1/24;
+ *   c = sqrt(2/3)*Pi*sqrt(b)
+ *   d = 1 / ((2*b)^(3/2) * Pi);
+ *
+ * Psi(N, q) = my(a = c/q); sqrt(q) * (a*cosh(a) - sinh(a)) */
+static GEN
+psi(GEN c, ulong q, long prec)
+{
+  GEN a = divru(c, q), ea = mpexp(a), invea = invr(ea);
+  GEN cha = shiftr(addrr(ea, invea), -1);  /* ch(a) */
+  GEN sha = shiftr(subrr(ea, invea), -1);  /* sh(a) */
+  return mulrr(sqrtr(stor(q,prec)), subrr(mulrr(a,cha), sha));
+}
+
+/* g(h, q) = if(q>=3, 12*sum(k=1,q-1,k*(frac(h*k/q)-1/2)))
+ *   \\ this is an integer = sumdedekind(h,q)*12*q
+ * assume h < q and (h,q) = 1. Not memory clean. */
+static GEN
+g(ulong h, ulong q)
+{
+  long s1, s2;
+  GEN v;
+
+  if (q < 3)  return gen_0;
+  if (h == 1) return muluu(q-1,q-2);
+  if (h == 2) return q == 3? gen_m2: muluu(q-1,(q-5)>>1);
+  v = u_sumdedekind_coprime(h, q);
+  s1 = v[1];
+  s2 = v[2];
+  return addis(mulss(q,s1), s2);
+}
+
+/* L(n,q)=if(q==1,1,sum(h=1,q-1,if(gcd(h,q)==1, cos((g(h,q)-24*h*n)*Pi/(12*q))))
+ * Never called with q < 3, so ignore this case */
+static GEN
+L(GEN n, ulong q, long bitprec)
+{
+  long pr = nbits2prec(bitprec / q + q);
+  ulong h, nmodq = umodiu(n, q), qov2 = q>>1, hn;
+  GEN r, res = stor(0, pr), q12 = muluu(q,12), q24 = shifti(q12,1);
+  GEN pi_q = divri(mppi(pr), q12);
+  pari_sp av = avma;
+  for (h = 1, hn = 0; h <= qov2; h++, avma = av) /* symmetry h <-> q-h */
+  {
+    GEN t;
+    hn += nmodq; if (hn >= q) hn -= q;
+    if (ugcd(q, h) > 1) continue;
+    r = subii(g(h,q), muluu(hn, 24));
+    r = centermodii(r, q24, q12);
+    t = isintzero(r)? addrs(res, 1): addrr(res, mpcos(mulri(pi_q,r)));
+    affrr(t, res);
+  }
+  return shiftr(res,1);
+}
+
+/* Return a low precision estimate of log p(n). */
+static GEN
+estim(GEN n)
+{
+  pari_sp av = avma;
+  GEN p1, pi = mppi (DEFAULTPREC);
+
+  p1 = divru( itor(shifti(n,1), DEFAULTPREC), 3 );
+  p1 = mpexp( mulrr(pi, sqrtr(p1)) ); /* exp(Pi * sqrt(2N/3)) */
+  p1 = divri (shiftr(p1,-2), n);
+  p1 = divrr(p1, sqrtr( stor(3,DEFAULTPREC) ));
+  return gerepileupto(av, mplog(p1));
+}
+
+static void
+pinit(GEN n, GEN *c, GEN *d, ulong prec)
+{
+  GEN b = divru( itor( subis(muliu(n,24), 1), prec ), 24 ); /* n - 1/24 */
+  GEN sqrtb = sqrtr(b), Pi = mppi(prec), pi2sqrt2, pisqrt2d3;
+
+
+  pisqrt2d3 = mulrr(Pi, sqrtr( divru(stor(2, prec), 3) ));
+  pi2sqrt2  = mulrr(Pi, sqrtr( stor(8, prec) ));
+  *c = mulrr(pisqrt2d3, sqrtb);
+  *d = invr( mulrr(pi2sqrt2, mulrr(b,sqrtb)) );
+}
+
+/* part(n) = round(sum(q=1,5 + 0.24*sqrt(n), L(n,q)*Psi(n,q))) */
+GEN
+numbpart(GEN n)
+{
+  pari_sp ltop = avma, av;
+  GEN sum, est, C, D, p1, p2;
+  long prec, bitprec;
+  ulong q;
+
+  if (typ(n) != t_INT) pari_err_TYPE("partition function",n);
+  if (signe(n) < 0) return gen_0;
+  if (cmpiu(n, 2) < 0) return gen_1;
+  if (cmpii(n, uu32toi(0x38d7e, 0xa4c68000)) >= 0)
+    pari_err_OVERFLOW("numbpart [n < 10^15]");
+  est = estim(n);
+  bitprec = (long)(rtodbl(est)/LOG2) + 32;
+  prec = nbits2prec(bitprec);
+  pinit(n, &C, &D, prec);
+  sum = cgetr (prec); affsr(0, sum);
+  /* Because N < 10^16 and q < sqrt(N), q fits into a long
+   * In fact q < 2 LONG_MAX / 3 */
+  av = avma; togglesign(est);
+  for (q = (ulong)(sqrt(gtodouble(n))*0.24 + 5); q >= 3; q--, avma=av)
+  {
+    GEN t = L(n, q, bitprec);
+    if (absr_cmp(t, mpexp(divru(est,q))) < 0) continue;
+
+    t = mulrr(t, psi(gprec_w(C, nbits2prec(bitprec / q + 32)), q, prec));
+    affrr(addrr(sum, t), sum);
+  }
+  p1 = addrr(sum, psi(C, 1, prec));
+  p2 = psi(C, 2, prec);
+  affrr(mod2(n)? subrr(p1,p2): addrr(p1,p2), sum);
+  return gerepileuptoint (ltop, roundr(mulrr(D,sum)));
+}
+
+/* for loop over partitions of integer k.
+ * nbounds can restrict partitions to have length between nmin and nmax
+ * (the length is the number of non zero entries) and
+ * abounds restrict to integers between amin and amax.
+ *
+ * Start from central partition.
+ * By default, remove zero entries on the left.
+ *
+ * Algorithm:
+ *
+ * A partition of k is an increasing sequence v1,... vn with sum(vi)=k
+ * The starting point is the minimal n-partition of k: a,...a,a+1,.. a+1
+ * (a+1 is repeated r times with k = a * n + r).
+ *
+ * The procedure to obtain the next partition:
+ * - find the last index i<n such that v{i-1} != v{i} (that is vi is the start
+ * of the last constant range excluding vn).
+ * - decrease vi by one, and set v{i+1},... v{n} to be a minimal partition (of
+ * the right sum).
+ *
+ * Examples: we highlight the index i
+ * 1 1 2 2 3
+ *     ^
+ * 1 1 1 3 3
+ *       ^
+ * 1 1 1 2 4
+ *       ^
+ * 1 1 1 1 5
+ * ^
+ * 0 2 2 2 3
+ *   ^
+ * This is recursive in nature. Restrictions on upper bounds of the vi or on
+ * the length of the partitions are straightforward to take into account. */
+
+static void
+parse_interval(GEN a, long *amin, long *amax)
+{
+  switch (typ(a))
+  {
+  case t_INT:
+    *amax = itos(a);
+    break;
+  case t_VEC:
+    if (lg(a) != 3)
+      pari_err_TYPE("forpart [expect vector of type [amin,amax]]",a);
+    *amin = gtos(gel(a,1));
+    *amax = gtos(gel(a,2));
+    if (*amin>*amax || *amin<0 || *amax<=0)
+      pari_err_TYPE("forpart [expect 0<=min<=max, 0<max]",a);
+    break;
+  default:
+    pari_err_TYPE("forpart",a);
+  }
+}
+
+void
+forpart_init(forpart_t *T, long k, GEN abound, GEN nbound)
+{
+
+  /* bound on coefficients */
+  T->amin=1;
+  if (abound) parse_interval(abound,&T->amin,&T->amax);
+  else T->amax = k;
+  /* strip leading zeros ? */
+  T->strip = (T->amin > 0) ? 1 : 0;
+  /* bound on number of non-zero coefficients */
+  T->nmin=0;
+  if (nbound) parse_interval(nbound,&T->nmin,&T->nmax);
+  else T->nmax = k;
+
+  /* non empty if nmin*amin <= k <= amax*nmax */
+  if ( T->amin*T->nmin > k || k > T->amax * T->nmax )
+  {
+    T->nmin = T->nmax = 0;
+  }
+  else
+  {
+    /* to reach nmin one must have k <= nmin*amax, otherwise increase nmin */
+    if ( T->nmin * T->amax < k )
+      T->nmin = 1 + (k - 1) / T->amax; /* ceil( k/tmax ) */
+    /* decrease nmax (if strip): k <= amin*nmax */
+    if (T->strip && T->nmax > k/T->amin)
+      T->nmax = k / T->amin; /* strip implies amin>0 */ /* fixme: take ceil( ) */
+    /* no need to change amin */
+    /* decrease amax if amax + (nmin-1)*amin > k  */
+    if ( T->amax + (T->nmin-1)* T->amin > k )
+      T->amax = k - (T->nmin-1)* T->amin;
+  }
+
+  if ( T->amax < T->amin )
+    T->nmin = T->nmax = 0;
+
+  T->v = zero_zv(T->nmax); /* partitions will be of length <= nmax */
+  T->k = k;
+}
+
+GEN
+forpart_next(forpart_t *T)
+{
+  GEN v = T->v;
+  long n = lg(v)-1;
+  long i, s, a, k, vi, vn;
+
+  if (n>0 && v[n])
+  {
+    /* find index to increase: i s.t. v[i+1],...v[n] is central a,..a,a+1,..a+1
+       keep s = v[i] + v[i+1] + ... + v[n] */
+    s = a = v[n];
+    for(i = n-1; i>0 && v[i]+1 >= a; s += v[i--]);
+    if (i == 0) {
+      /* v is central [ a, a, .. a, a+1, .. a+1 ] */
+      if ((n+1) * T->amin > s || n == T->nmax) return NULL;
+      i = 1; n++;
+      setlg(v, n+1);
+      vi = T->amin;
+    } else {
+      s += v[i];
+      vi = v[i]+1;
+    }
+  } else {
+    /* init v */
+    s = T->k;
+    if (T->amin == 0) T->amin = 1;
+    if (T->strip) { n = T->nmin; setlg(T->v, n+1); }
+    if (s==0)
+    {
+      if (n==0 && T->nmin==0) {T->nmin++; return v;}
+      return NULL;
+    }
+    if (n==0) return NULL;
+    vi = T->amin;
+    i = T->strip ? 1 : n + 1 - T->nmin; /* first non-zero index */
+  }
+  /* now fill [ v[i],... v[n] ] with s, start at vi */
+  vn = s - (n-i)*vi; /* expected value for v[n] */
+  if (T->amax && vn > T->amax)
+  {
+    /* do not exceed amax */
+    long ai, q, r;
+    vn -= vi;
+    ai = T->amax - vi;
+    q = vn / ai; /* number of nmax */
+    r = vn % ai; /* value before nmax */
+    /* fill [ v[i],... v[n] ] as [ vi,... vi, vi+r, amax,... amax ] */
+    while ( q-- ) v[n--] = T->amax;
+    if ( n >= i ) v[n--] = vi + r;
+    while ( n >= i ) v[n--] = vi;
+  } else {
+    /* fill as [ v[i], ... v[i], vn ] */
+    for ( k=i; k<n; v[k++] = vi );
+    v[n] = vn;
+  }
+  return v;
+}
+
+GEN
+forpart_prev(forpart_t *T)
+{
+  GEN v = T->v;
+  long n = lg(v)-1;
+  long j, ni, q, r;
+  long i, s;
+  if (n>0 && v[n])
+  {
+    /* find index to decrease: start of last constant sequence, excluding v[n] */
+    i = n-1; s = v[n];
+    while (i>1 && (v[i-1]==v[i] || v[i+1]==T->amax))
+      s+= v[i--];
+    if (!i) return NULL;
+    /* amax condition: cannot decrease i if maximal on the right */
+    if ( v[i+1] == T->amax ) return NULL;
+    /* amin condition: stop if below except if strip & try to remove */
+    if (v[i] == T->amin) {
+      if (!T->strip) return NULL;
+      s += v[i]; v[i] = 0;
+    } else {
+      v[i]--; s++;
+    }
+    /* zero case... */
+    if (v[i] == 0)
+    {
+      if (T->nmin > n-i) return NULL; /* need too many non zero coeffs */
+      /* reduce size of v ? */
+      if (T->strip) {
+        i = 0; n--;
+        setlg(v, n+1);
+      }
+    }
+  } else
+  {
+    s = T->k;
+    i = 0;
+    if (s==0)
+    {
+      if (n==0 && T->nmin==0) {T->nmin++; return v;}
+      return NULL;
+    }
+    if (n*T->amax < s || s < T->nmin*T->amin) return NULL;
+  }
+  /* set minimal partition of sum s starting from index i+1 */
+  ni = n-i;
+  q = s / ni;
+  r = s % ni;
+  for(j=i+1;   j<=n-r; j++) v[j]=q;
+  for(j=n-r+1; j<=n;   j++) v[j]=q + 1;
+  return v;
+}
+
+static long
+countpart(long k, GEN abound, GEN nbound)
+{
+  pari_sp av = avma;
+  long n;
+  forpart_t T;
+  if (k<0) return 0;
+  forpart_init(&T, k, abound, nbound);
+  for (n=0; forpart_next(&T); n++)
+  avma = av;
+  return n;
+}
+
+GEN
+partitions(long k, GEN abound, GEN nbound)
+{
+  GEN v;
+  forpart_t T;
+  long i, n = countpart(k,abound,nbound);
+  if (n==0) return cgetg(1, t_VEC);
+  forpart_init(&T, k, abound, nbound);
+  v = cgetg(n+1, t_VEC);
+  for (i=1; i<=n; i++)
+    gel(v,i)=zv_copy(forpart_next(&T));
+  return v;
+}
+
+void
+forpart(void *E, long call(void*, GEN), long k, GEN abound, GEN nbound)
+{
+  GEN v;
+  forpart_t T;
+  forpart_init(&T, k, abound, nbound);
+  while ((v=forpart_next(&T)))
+    if (call(E, v)) break;
+}
+
+void
+forpart0(GEN k, GEN code, GEN abound, GEN nbound)
+{
+  pari_sp av = avma;
+  if (typ(k) != t_INT) pari_err_TYPE("forpart",k);
+  if (signe(k)<0) return;
+  push_lex(gen_0, code);
+  forpart((void*)code, &gp_evalvoid, itos(k), abound, nbound);
+  pop_lex(1);
+  avma=av;
+}
diff --git a/src/modules/stark.c b/src/modules/stark.c
new file mode 100644
index 0000000..d14c8c7
--- /dev/null
+++ b/src/modules/stark.c
@@ -0,0 +1,3657 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*        COMPUTATION OF STARK UNITS OF TOTALLY REAL FIELDS        */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+#define EXTRA_PREC (DEFAULTPREC-1)
+#define ADD_PREC   (DEFAULTPREC-2)*3
+
+/* ComputeCoeff */
+typedef struct {
+  GEN L0, L1, L11, L2; /* VECSMALL of p */
+  GEN L1ray, L11ray; /* precomputed isprincipalray(pr), pr | p */
+  GEN rayZ; /* precomputed isprincipalray(i), i < condZ */
+  long condZ; /* generates cond(bnr) \cap Z, assumed small */
+} LISTray;
+
+/* Char evaluation */
+typedef struct {
+  long ord;
+  GEN *val, chi;
+} CHI_t;
+
+/* RecCoeff */
+typedef struct {
+  GEN M, beta, B, U, nB;
+  long v, G, N;
+} RC_data;
+
+/********************************************************************/
+/*                    Miscellaneous functions                       */
+/********************************************************************/
+/* exp(2iPi/den), assume den a t_INT */
+static GEN
+InitRU(GEN den, long prec)
+{
+  GEN c, s;
+  if (equaliu(den, 2)) return gen_m1;
+  gsincos(divri(Pi2n(1, prec), den), &s, &c, prec);
+  return mkcomplex(c, s);
+}
+/* Compute the image of logelt by character chi, as a complex number */
+static GEN
+ComputeImagebyChar(GEN chi, GEN logelt)
+{
+  GEN gn = ZV_dotproduct(gel(chi,1), logelt), x = gel(chi,2);
+  long d = itos(gel(chi,3)), n = smodis(gn, d);
+  /* x^d = 1 and, if d even, x^(d/2) = -1 */
+  if ((d & 1) == 0)
+  {
+    d /= 2;
+    if (n >= d) return gneg(gpowgs(x, n-d));
+  }
+  return gpowgs(x, n);
+}
+
+/* return n such that C(elt) = z^n */
+static ulong
+EvalChar_n(CHI_t *C, GEN logelt)
+{
+  GEN n = ZV_dotproduct(C->chi, logelt);
+  return umodiu(n, C->ord);
+}
+/* return C(elt) */
+static GEN
+EvalChar(CHI_t *C, GEN logelt)
+{
+  return C->val[EvalChar_n(C, logelt)];
+}
+
+static void
+init_CHI(CHI_t *c, GEN CHI, GEN z)
+{
+  long i, d = itos(gel(CHI,3));
+  GEN *v = (GEN*)new_chunk(d);
+  v[0] = gen_1;
+  v[1] = z;
+  for (i=2; i<d; i++) v[i] = gmul(v[i-1], z);
+  c->chi = gel(CHI,1);
+  c->ord = d;
+  c->val = v;
+}
+/* as t_POLMOD */
+static void
+init_CHI_alg(CHI_t *c, GEN CHI) {
+  long d = itos(gel(CHI,3));
+  GEN z;
+  switch(d)
+  {
+    case 1: z = gen_1; break;
+    case 2: z = gen_m1; break;
+    default: z = mkpolmod(pol_x(0), polcyclo(d,0));
+  }
+  init_CHI(c,CHI, z);
+}
+/* as t_COMPLEX */
+static void
+init_CHI_C(CHI_t *c, GEN CHI) {
+  init_CHI(c,CHI, gel(CHI,2));
+}
+
+/* Compute the conjugate character [ZV] */
+static GEN
+ConjChar(GEN chi, GEN cyc)
+{
+  long i, l = lg(chi);
+  GEN z = cgetg(l, t_VEC);
+  for (i = 1; i < l; i++)
+    gel(z,i) = signe(gel(chi,i))? subii(gel(cyc,i), gel(chi,i)): gen_0;
+  return z;
+}
+
+typedef struct {
+  long r; /* rank = lg(gen) */
+  GEN j; /* current elt is gen[1]^j[1] ... gen[r]^j[r] */
+  GEN cyc; /* t_VECSMALL of elementary divisors */
+} GROUP_t;
+
+static int
+NextElt(GROUP_t *G)
+{
+  long i = 1;
+  if (G->r == 0) return 0; /* no more elt */
+  while (++G->j[i] == G->cyc[i]) /* from 0 to cyc[i]-1 */
+  {
+    G->j[i] = 0;
+    if (++i > G->r) return 0; /* no more elt */
+  }
+  return i; /* we have multiplied by gen[i] */
+}
+
+/* Compute all the elements of a group given by its SNF */
+static GEN
+EltsOfGroup(long order, GEN cyc)
+{
+  long i;
+  GEN rep;
+  GROUP_t G;
+
+  G.cyc = gtovecsmall(cyc);
+  G.r = lg(cyc)-1;
+  G.j = zero_zv(G.r);
+
+  rep = cgetg(order + 1, t_VEC);
+  gel(rep,order) = vecsmall_to_col(G.j);
+
+  for  (i = 1; i < order; i++)
+  {
+    (void)NextElt(&G);
+    gel(rep,i) = vecsmall_to_col(G.j);
+  }
+  return rep;
+}
+
+/* Let dataC as given by InitQuotient, compute a system of
+   representatives of the quotient */
+static GEN
+ComputeLift(GEN dataC)
+{
+  long order, i;
+  pari_sp av = avma;
+  GEN cyc, surj, eltq, elt;
+
+  order = itos(gel(dataC,1));
+  cyc   = gel(dataC,2);
+  surj  = gel(dataC,3);
+
+  eltq = EltsOfGroup(order, cyc);
+  elt = cgetg(order + 1, t_VEC);
+  for (i = 1; i <= order; i++) gel(elt,i) = inverseimage(surj, gel(eltq,i));
+
+  return gerepileupto(av, elt);
+}
+
+/* Return c[1],  [c[1]/c[1] = 1,...,c[1]/c[n]] */
+static GEN
+init_get_chic(GEN c)
+{
+  long i, l = lg(c); /* > 1 */
+  GEN C, D = cgetg(l, t_VEC);
+  if (l == 1) C = gen_1;
+  else
+  {
+    C = gel(c,1); gel(D,1) = gen_1;
+    for (i = 2; i < l; i++) gel(D,i) = diviiexact(C, gel(c,i));
+  }
+  return mkvec2(C, D);
+}
+
+static GEN
+get_chic(GEN chi, GEN D)
+{
+  long i, l = lg(chi);
+  GEN chic = cgetg(l, t_VEC);
+  gel(chic,1) = gel(chi,1);
+  for (i = 2; i < l; i++) gel(chic,i) = mulii(gel(chi,i), gel(D,i));
+  return chic;
+}
+
+/* A character is given by a vector [(c_i), z, d] such that
+   chi(id) = z ^ sum(c_i * a_i) where
+     a_i= log(id) on the generators of bnr
+     z  = exp(2i * Pi / d) */
+/* U is NULL or a ZM */
+static GEN
+get_Char(GEN chi, GEN initc, GEN U, long prec)
+{
+  GEN d, chic = get_chic(chi, gel(initc,2));
+  if (U) chic = ZV_ZM_mul(chic, U);
+  d = ZV_content(chic);
+  if (is_pm1(d)) d = gel(initc,1);
+  else
+  {
+    GEN t = gred_frac2(gel(initc,1), d);
+    chic = ZC_Z_divexact(chic, d);
+    if (typ(t) == t_INT)
+      d = t;
+    else
+    {
+      d = gel(t,1);
+      chic = gmul(gel(t,2), chic);
+    }
+  }
+  return mkvec3(chic, InitRU(d, prec), d);
+}
+
+/* prime divisors of conductor */
+static GEN
+divcond(GEN bnr) { GEN bid = bnr_get_bid(bnr); return gmael(bid,3,1); }
+
+/* vector of prime ideals dividing bnr but not bnrc */
+static GEN
+get_prdiff(GEN bnr, GEN condc)
+{
+  GEN prdiff, M = gel(condc,1), D = divcond(bnr), nf = bnr_get_nf(bnr);
+  long nd, i, l  = lg(D);
+  prdiff = cgetg(l, t_COL);
+  for (nd=1, i=1; i < l; i++)
+    if (!idealval(nf, M, gel(D,i))) gel(prdiff,nd++) = gel(D,i);
+  setlg(prdiff, nd); return prdiff;
+}
+
+/* Let chi a character defined over bnr and primitive over bnrc, compute the
+ * corresponding primitive character. Returns NULL if bnr = bnrc */
+static GEN
+GetPrimChar(GEN chi, GEN bnr, GEN bnrc, long prec)
+{
+  long l;
+  pari_sp av = avma;
+  GEN U, M, cond, condc, initc, Mrc;
+
+  cond  = bnr_get_mod(bnr);
+  condc = bnr_get_mod(bnrc); if (gequal(cond, condc)) return NULL;
+
+  initc = init_get_chic(bnr_get_cyc(bnr));
+  Mrc   = diagonal_shallow(bnr_get_cyc(bnrc));
+  M = bnrsurjection(bnr, bnrc);
+  (void)ZM_hnfall(shallowconcat(M, Mrc), &U, 1);
+  l = lg(M);
+  U = rowslice(vecslice(U, l, lg(U)-1), 1, l-1);
+  return gerepilecopy(av, get_Char(chi, initc, U, prec));
+}
+
+#define ch_chi(x)  gel(x,1)
+#define ch_C(x)    gel(x,2)
+#define ch_bnr(x)  gel(x,3)
+#define ch_4(x)    gel(x,4)
+#define ch_CHI(x)  gel(x,5)
+#define ch_diff(x) gel(x,6)
+#define ch_cond(x) gel(x,7)
+#define ch_CHI0(x) gel(x,8)
+#define ch_comp(x) gel(x,9)
+
+static GEN
+GetDeg(GEN dataCR)
+{
+  long i, l = lg(dataCR);
+  GEN degs = cgetg(l, t_VECSMALL);
+
+  for (i = 1; i < l; i++) degs[i] = eulerphiu(itou(gel(ch_CHI(gel(dataCR, i)), 3)));
+  return degs;
+}
+
+/********************************************************************/
+/*                    1rst part: find the field K                   */
+/********************************************************************/
+static GEN AllStark(GEN data, GEN nf, long flag, long prec);
+
+/* Columns of C [HNF] give the generators of a subgroup of the finite abelian
+ * group A [ in terms of implicit generators ], compute data to work in A/C:
+ * 1) order
+ * 2) structure
+ * 3) the matrix A ->> A/C
+ * 4) the group C */
+static GEN
+InitQuotient(GEN C)
+{
+  long junk;
+  GEN U, D = ZM_snfall_i(C, &U, NULL, 1), h = detcyc(D, &junk);
+  return mkvec4(h, D, U, C);
+}
+
+/* Let s: A -> B given by P, and let cycA, cycB be the cyclic structure of
+ * A and B, compute the kernel of s. */
+static GEN
+ComputeKernel0(GEN P, GEN cycA, GEN cycB)
+{
+  pari_sp av = avma;
+  long nbA = lg(cycA)-1, rk;
+  GEN U, DB = diagonal_shallow(cycB);
+
+  rk = nbA + lg(cycB) - lg(ZM_hnfall(shallowconcat(P, DB), &U, 1));
+  U = vecslice(U, 1,rk);
+  U = rowslice(U, 1,nbA);
+  return gerepileupto(av, ZM_hnfmodid(U, cycA));
+}
+
+/* Let m and n be two moduli such that n|m and let C be a congruence
+   group modulo n, compute the corresponding congruence group modulo m
+   ie the kernel of the map Clk(m) ->> Clk(n)/C */
+static GEN
+ComputeKernel(GEN bnrm, GEN bnrn, GEN dtQ)
+{
+  pari_sp av = avma;
+  GEN P = ZM_mul(gel(dtQ,3), bnrsurjection(bnrm, bnrn));
+  return gerepileupto(av, ComputeKernel0(P, bnr_get_cyc(bnrm), gel(dtQ,2)));
+}
+
+static GEN
+Order(GEN cyc, GEN x)
+{
+  pari_sp av = avma;
+  long i, l = lg(cyc);
+  GEN c,o,f = gen_1;
+  for (i = 1; i < l; i++)
+  {
+    o = gel(cyc,i);
+    c = gcdii(o, gel(x,i));
+    if (!is_pm1(c)) o = diviiexact(o,c);
+    f = lcmii(f, o);
+  }
+  return gerepileuptoint(av, f);
+}
+
+/* Let H be a subgroup of cl(bnr)/sugbroup, return 1 if
+   cl(bnr)/subgoup/H is cyclic and the signature of the
+   corresponding field is equal to sig and no finite prime
+   dividing cond(bnr) is totally split in this field. Return 0
+   otherwise. */
+static long
+IsGoodSubgroup(GEN H, GEN bnr, GEN map)
+{
+  pari_sp av = avma;
+  long j, f;
+  GEN bnf, mod, mod0, mod1, modH, modH0, p1, p2, p3, p4;
+  GEN bnrH, cycH, iH, qH;
+
+  p1 = InitQuotient(H);
+  p2 = gel(p1, 2);
+  /* quotient is non cyclic */
+  if ((lg(p2) > 2) && !gequal1(gel(p2, 2))) { avma = av; return 0; }
+
+  bnf  = bnr_get_bnf(bnr);
+  mod  = bnr_get_mod(bnr);
+  mod0 = gel(mod,1);
+  mod1 = gel(mod,2);
+
+  p1 = concat(map, H);
+  p2 = ZM_hnfall(p1, &p3, 0);
+  setlg(p3, lg(H));
+  for (j = 1; j < lg(p3); j++) setlg(p3[j], lg(H));
+  p1 = ZM_hnfmodid(p3, bnr_get_cyc(bnr)); /* H as a subgroup of bnr */
+  modH = bnrconductor(bnr, p1, 0);
+
+  /* is the signature correct? */
+  if (!gequal(gel(modH, 2), mod1)) { avma = av; return 0; }
+
+  /* if the finite part are the same, then it's good */
+  if (gequal(gel(modH, 1), mod0)) { avma = av; return 1; }
+
+  /* we need to check the splitting of primes dividing mod0/p2 */
+  modH0 = gel(modH, 1);
+  p3 = idealdivexact(bnf, mod0, modH0);
+  p4 = gel(idealfactor(bnf, p3), 1);
+
+  bnrH = Buchray(bnf, mkvec2(modH, mod1), nf_INIT|nf_GEN);
+  cycH = bnr_get_cyc(bnrH);
+  p2 = ZM_mul(bnrsurjection(bnr, bnrH), p1);
+  /* H as a subgroup of bnrH */
+  iH = ZM_hnfmodid(p2, cycH);
+  qH = InitQuotient(iH);
+
+  for (j = 1; j < lg(p4); j++)
+  {
+    GEN pr = gel(p4, j);
+    /* if pr divides modH0, it is ramified, so it's good */
+    if (!idealval(bnf, modH0, pr))
+    {
+      /* we compute the inertia degree of pr in bnr(modH)/H*/
+      p1 = ZM_ZC_mul(gel(qH, 3), isprincipalray(bnrH, pr));
+      p2 = gel(qH, 2);
+      f  = itos(Order(p2, p1));
+      if (f == 1) { avma = av; return 0; }
+    }
+  }
+
+  avma = av;
+  return 1;
+}
+
+static GEN get_listCR(GEN bnr, GEN dtQ);
+static GEN InitChar(GEN bnr, GEN listCR, long prec);
+
+/* Given a conductor and a subgroups, return the corresponding
+   complexity and precision required using quickpol. Fill data[5] with
+   listCR */
+static long
+CplxModulus(GEN data, long *newprec)
+{
+  long pr, ex, dprec = DEFAULTPREC;
+  pari_sp av;
+  GEN pol, listCR, cpl, bnr = gel(data,1), nf = checknf(bnr);
+
+  listCR = get_listCR(bnr, gel(data,3));
+  for (av = avma;; avma = av)
+  {
+    gel(data,5) = InitChar(bnr, listCR, dprec);
+    pol = AllStark(data, nf, -1, dprec);
+    pr = nbits2extraprec( gexpo(pol) );
+    if (pr < 0) pr = 0;
+    dprec = maxss(dprec, pr) + EXTRA_PREC;
+    if (!gequal0(leading_term(pol)))
+    {
+      cpl = RgX_fpnorml2(pol, DEFAULTPREC);
+      if (!gequal0(cpl)) break;
+    }
+    if (DEBUGLEVEL>1) pari_warn(warnprec, "CplxModulus", dprec);
+  }
+  ex = gexpo(cpl); avma = av;
+  if (DEBUGLEVEL>1) err_printf("cpl = 2^%ld\n", ex);
+
+  gel(data,5) = listCR;
+  *newprec = dprec; return ex;
+}
+
+ /* Let f be a conductor without infinite part and let C be a
+   congruence group modulo f, compute (m,D) such that D is a
+   congruence group of conductor m where m is a multiple of f
+   divisible by all the infinite places but one, D is a subgroup of
+   index 2 of Im(C) in Clk(m), and m is such that the intersection
+   of the subgroups H of Clk(n)/Im(C) such that the quotient is
+   cyclic and no prime divding m, but the one infinite prime, is
+   totally split in the extension corresponding to H is trival.
+   Return bnr(m), D, the quotient Ck(m)/D and Clk(m)/C */
+static GEN
+FindModulus(GEN bnr, GEN dtQ, long *newprec)
+{
+  const long limnorm = 400;
+  long n, i, narch, maxnorm, minnorm, N, nbidnn, s, c, j, k, nbcand;
+  long first = 1, pr, rb, oldcpl = -1, iscyc = 0;
+  pari_sp av = avma, av1;
+  GEN bnf, nf, f, arch, m, listid, idnormn, bnrm, ImC, rep = NULL;
+  GEN candD, p2;
+
+  bnf = bnr_get_bnf(bnr);
+  nf  = bnf_get_nf(bnf);
+  N   = nf_get_degree(nf);
+  f   = gel(bnr_get_mod(bnr), 1);
+
+  /* if cpl < rb, it is not necessary to try another modulus */
+  rb = expi( powii(mulii(nf_get_disc(nf), ZM_det_triangular(f)), gmul2n(bnr_get_no(bnr), 3)) );
+
+  /* Initialization of the possible infinite part */
+  arch = const_vec(N, gen_1);
+
+  /* narch = (N == 2)? 1: N; -- if N=2, only one case is necessary */
+  narch = N;
+  m = mkvec2(NULL, arch);
+
+  /* go from minnorm up to maxnorm. If necessary, increase these values.
+   * If we cannot find a suitable conductor of norm < limnorm, stop */
+  maxnorm = 50;
+  minnorm = 1;
+
+  /* if the extension is cyclic then we _must_ find a suitable conductor */
+  if (lg(gel(dtQ,2)) == 2 || gequal1(gmael(dtQ,2,2))) iscyc = 1;
+
+  if (DEBUGLEVEL>1)
+    err_printf("Looking for a modulus of norm: ");
+
+  for(;;)
+  {
+    listid = ideallist(nf, maxnorm); /* all ideals of norm <= maxnorm */
+
+    av1 = avma;
+    for (n = minnorm; n <= maxnorm; n++)
+    {
+      if (DEBUGLEVEL>1) err_printf(" %ld", n);
+      avma = av1;
+
+      idnormn = gel(listid,n);
+      nbidnn  = lg(idnormn) - 1;
+      for (i = 1; i <= nbidnn; i++)
+      { /* finite part of the conductor */
+        gel(m,1) = idealmul(nf, f, gel(idnormn,i));
+
+        for (s = 1; s <= narch; s++)
+        { /* infinite part */
+          gel(arch,N+1-s) = gen_0;
+
+          /* compute Clk(m), check if m is a conductor */
+          bnrm = Buchray(bnf, m, nf_INIT|nf_GEN);
+          c = bnrisconductor(bnrm, NULL);
+          gel(arch,N+1-s) = gen_1;
+          if (!c) continue;
+
+          /* compute Im(C) in Clk(m)... */
+          ImC = ComputeKernel(bnrm, bnr, dtQ);
+
+          /* ... and its subgroups of index 2 with conductor m */
+          candD = subgrouplist_cond_sub(bnrm, ImC, mkvec(gen_2));
+          nbcand = lg(candD) - 1;
+          for (c = 1; c <= nbcand; c++)
+          {
+            GEN D  = gel(candD,c);
+            long cpl;
+
+            /* check if the conductor is suitable */
+            {
+              GEN p1 = InitQuotient(D), p2, ord = gel(p1, 1);
+              GEN map = gel(p1, 3), cyc = gel(p1, 2), H;
+              GEN lH = subgrouplist(cyc, NULL), IK = NULL;
+              long ok = 0;
+
+              /* if the extension is cyclic, then it's suitable */
+              if ((lg(cyc) > 2) && !gequal1(gel(cyc, 2)))
+              {
+                for (j = 1; j < lg(lH); j++)
+                {
+                  H = gel(lH, j);
+                  /* if IK != NULL and H > IK, no need to test H */
+                  if (IK)
+                  {
+                    p1 = RgM_mul(IK, RgM_inv_upper(H));
+                    if (RgM_is_ZM(p1)) continue;
+                  }
+                  if (IsGoodSubgroup(H, bnrm, map))
+                  {
+                    if (!IK)
+                      IK = H;
+                    else
+                    {
+                      /* compute the intersection of IK and H */
+                      p1 = shallowconcat(IK, H);
+                      p1 = ZM_hnfall(p1, &p2, 1);
+                      setlg(p2, lg(IK));
+                      for (k = 1; k < lg(p2); k++) setlg(p2[k], lg(p1));
+                      IK = ZM_mul(IK, p2);
+                      IK = ZM_hnf(shallowconcat(IK, diagonal(cyc)));
+                    }
+                    if (gequal(ord, ZM_det_triangular(IK)))
+                    {
+                      ok = 1; break;
+                    }
+                  }
+                }
+                if (!ok) continue;
+              }
+            }
+
+            p2 = cgetg(6, t_VEC); /* p2[5] filled in CplxModulus */
+            gel(p2,1) = bnrm;
+            gel(p2,2) = D;
+            gel(p2,3) = InitQuotient(D);
+            gel(p2,4) = InitQuotient(ImC);
+            if (DEBUGLEVEL>1)
+              err_printf("\nTrying modulus = %Ps and subgroup = %Ps\n",
+                         bnr_get_mod(bnrm), D);
+            cpl = CplxModulus(p2, &pr);
+            if (oldcpl < 0 || cpl < oldcpl)
+            {
+              *newprec = pr;
+              if (rep) gunclone(rep);
+              rep    = gclone(p2);
+              oldcpl = cpl;
+            }
+            if (oldcpl < rb) goto END; /* OK */
+
+            if (DEBUGLEVEL>1) err_printf("Trying to find another modulus...");
+            first = 0;
+          }
+        }
+        if (!first) goto END; /* OK */
+      }
+    }
+    /* if necessary compute more ideals */
+    minnorm = maxnorm;
+    maxnorm <<= 1;
+    if (!iscyc && maxnorm > limnorm) return NULL;
+
+  }
+END:
+  if (DEBUGLEVEL>1)
+    err_printf("No, we're done!\nModulus = %Ps and subgroup = %Ps\n",
+               bnr_get_mod(gel(rep,1)), gel(rep,2));
+  gel(rep,5) = InitChar(gel(rep,1), gel(rep,5), *newprec);
+  return gerepilecopy(av, rep);
+}
+
+/********************************************************************/
+/*                      2nd part: compute W(X)                      */
+/********************************************************************/
+
+/* compute the list of W(chi) such that Ld(s,chi) = W(chi) Ld(1 - s, chi*),
+ * for all chi in LCHI. All chi have the same conductor (= cond(bnr)).
+ * if check == 0 do not check the result */
+static GEN
+ArtinNumber(GEN bnr, GEN LCHI, long check, long prec)
+{
+  long ic, i, j, nz, nChar = lg(LCHI)-1;
+  pari_sp av = avma, av2, lim;
+  GEN sqrtnc, dc, cond, condZ, cond0, cond1, lambda, nf, T;
+  GEN cyc, vN, vB, diff, vt, idg, idh, zid, gen, z, nchi;
+  GEN indW, W, classe, s0, s, den, muslambda, sarch;
+  CHI_t **lC;
+  GROUP_t G;
+
+  lC = (CHI_t**)new_chunk(nChar + 1);
+  indW = cgetg(nChar + 1, t_VECSMALL);
+  W = cgetg(nChar + 1, t_VEC);
+  for (ic = 0, i = 1; i <= nChar; i++)
+  {
+    GEN CHI = gel(LCHI,i);
+    if (cmpui(2, gel(CHI,3)) >= 0) { gel(W,i) = gen_1; continue; } /* trivial case */
+    ic++; indW[ic] = i;
+    lC[ic] = (CHI_t*)new_chunk(sizeof(CHI_t));
+    init_CHI_C(lC[ic], CHI);
+  }
+  if (!ic) return W;
+  nChar = ic;
+
+  nf    = bnr_get_nf(bnr);
+  diff  = nf_get_diff(nf);
+  T     = nf_get_Tr(nf);
+  cond  = bnr_get_mod(bnr);
+  cond0 = gel(cond,1); condZ = gcoeff(cond0,1,1);
+  cond1 = vec01_to_indices(gel(cond,2));
+
+  sqrtnc = gsqrt(idealnorm(nf, cond0), prec);
+  dc  = idealmul(nf, diff, cond0);
+
+  /* compute a system of elements congruent to 1 mod cond0 and giving all
+     possible signatures for cond1 */
+  sarch = nfarchstar(nf, cond0, cond1);
+
+  /* find lambda in diff.cond such that gcd(lambda.(diff.cond)^-1,cond0) = 1
+     and lambda >> 0 at cond1 */
+  lambda = idealappr(nf, dc);
+  lambda = set_sign_mod_divisor(nf, NULL, lambda, cond,sarch);
+  idg = idealdivexact(nf, lambda, dc);
+
+  /* find mu in idg such that idh=(mu) / idg is coprime with cond0 and
+     mu >> 0 at cond1 */
+  if (!gequal1(gcoeff(idg, 1, 1))) {
+    GEN P = divcond(bnr);
+    GEN f = famat_mul_shallow(idealfactor(nf, idg),
+                          mkmat2(P, zerocol(lg(P)-1)));
+    GEN mu = set_sign_mod_divisor(nf, NULL, idealapprfact(nf, f), cond,sarch);
+    idh = idealdivexact(nf, mu, idg);
+    muslambda = nfdiv(nf, mu, lambda);
+  } else { /* mu = 1 */
+    idh = idg;
+    muslambda = nfinv(nf, lambda);
+  }
+  muslambda = Q_remove_denom(muslambda, &den);
+  z = InitRU(den, prec);
+
+  /* compute a system of generators of (Ok/cond)^*, we'll make them
+   * cond1-positive in the main loop */
+  zid = Idealstar(nf, cond0, nf_GEN);
+  cyc = gel(zid, 2);
+  gen = gel(zid, 3);
+  nz = lg(gen) - 1;
+
+  nchi = cgetg(nChar+1, t_VEC);
+  for (ic = 1; ic <= nChar; ic++) gel(nchi,ic) = cgetg(nz + 1, t_VECSMALL);
+
+  for (i = 1; i <= nz; i++)
+  {
+    if (is_bigint(gel(cyc,i)))
+      pari_err_OVERFLOW("ArtinNumber [conductor too large]");
+    gel(gen,i) = set_sign_mod_divisor(nf, NULL, gel(gen,i), cond,sarch);
+    classe = isprincipalray(bnr, gel(gen,i));
+    for (ic = 1; ic <= nChar; ic++) {
+      GEN n = gel(nchi,ic);
+      n[i] = EvalChar_n(lC[ic], classe);
+    }
+  }
+
+  /* Sum chi(beta) * exp(2i * Pi * Tr(beta * mu / lambda) where beta
+     runs through the classes of (Ok/cond0)^* and beta cond1-positive */
+
+  vt = gel(T,1); /* ( Tr(w_i) )_i */
+  vt = ZV_ZM_mul(vt, zk_multable(nf, muslambda)); /*den (Tr(w_i mu/lambda))_i */
+  G.cyc = gtovecsmall(cyc);
+  G.r = nz;
+  G.j = zero_zv(nz);
+
+  vN = cgetg(nChar+1, t_VEC);
+  for (ic = 1; ic <= nChar; ic++) gel(vN,ic) = zero_zv(nz);
+
+  av2 = avma; lim = stack_lim(av2, 1);
+  vB = const_vec(nz, gen_1);
+  s0 = powgi(z, modii(gel(vt,1), den)); /* for beta = 1 */
+  s = const_vec(nChar, s0);
+
+  while ( (i = NextElt(&G)) )
+  {
+    gel(vB,i) = FpC_red(nfmuli(nf, gel(vB,i), gel(gen,i)), condZ);
+    for (j=1; j<i; j++) gel(vB,j) = gel(vB,i);
+
+    for (ic = 1; ic <= nChar; ic++)
+    {
+      GEN v = gel(vN,ic), n = gel(nchi,ic);
+      v[i] = Fl_add(v[i], n[i], lC[ic]->ord);
+      for (j=1; j<i; j++) v[j] = v[i];
+    }
+
+    gel(vB,i) = set_sign_mod_divisor(nf, NULL, gel(vB,i), cond,sarch);
+    s0 = powgi(z, FpV_dotproduct(vt, gel(vB,i), den));
+    for (ic = 1; ic <= nChar; ic++)
+    {
+      GEN n = gel(vN,ic), val = lC[ic]->val[ n[i] ];
+      gel(s,ic) = gadd(gel(s,ic), gmul(val, s0));
+    }
+
+    if (low_stack(lim, stack_lim(av2, 1)))
+    {
+      if (DEBUGMEM > 1) pari_warn(warnmem,"ArtinNumber");
+      gerepileall(av2, 2, &s, &vB);
+    }
+  }
+
+  classe = isprincipalray(bnr, idh);
+  z = powIs(- (lg(cond1)-1));
+
+  for (ic = 1; ic <= nChar; ic++)
+  {
+    s0 = gmul(gel(s,ic), EvalChar(lC[ic], classe));
+    s0 = gdiv(s0, sqrtnc);
+    if (check && - expo(subrs(gnorm(s0), 1)) < prec2nbits(prec) >> 1)
+      pari_err_BUG("ArtinNumber");
+    gel(W, indW[ic]) = gmul(s0, z);
+  }
+  return gerepilecopy(av, W);
+}
+
+static GEN
+ComputeAllArtinNumbers(GEN dataCR, GEN vChar, int check, long prec)
+{
+  long j, k, cl = lg(dataCR) - 1, J = lg(vChar)-1;
+  GEN W = cgetg(cl+1,t_VEC), WbyCond, LCHI;
+
+  for (j = 1; j <= J; j++)
+  {
+    GEN LChar = gel(vChar,j), ldata = vecpermute(dataCR, LChar);
+    GEN dtcr = gel(ldata,1), bnr = ch_bnr(dtcr);
+    long l = lg(LChar);
+
+    if (DEBUGLEVEL>1)
+      err_printf("* Root Number: cond. no %ld/%ld (%ld chars)\n", j, J, l-1);
+    LCHI = cgetg(l, t_VEC);
+    for (k = 1; k < l; k++) gel(LCHI,k) = ch_CHI0(gel(ldata,k));
+    WbyCond = ArtinNumber(bnr, LCHI, check, prec);
+    for (k = 1; k < l; k++) W[LChar[k]] = WbyCond[k];
+  }
+  return W;
+}
+static GEN
+SingleArtinNumber(GEN bnr, GEN chi, long prec)
+{ return gel(ArtinNumber(bnr, mkvec(chi), 1, prec), 1); }
+
+/* compute the constant W of the functional equation of
+   Lambda(chi). If flag = 1 then chi is assumed to be primitive */
+GEN
+bnrrootnumber(GEN bnr, GEN chi, long flag, long prec)
+{
+  long l;
+  pari_sp av = avma;
+  GEN cond, condc, bnrc, CHI, cyc;
+
+  if (flag < 0 || flag > 1) pari_err_FLAG("bnrrootnumber");
+
+  checkbnr(bnr);
+  cyc = bnr_get_cyc(bnr);
+  cond = bnr_get_mod(bnr);
+  l    = lg(cyc);
+
+  if (typ(chi) != t_VEC || lg(chi) != l)
+    pari_err_TYPE("bnrrootnumber [character]", chi);
+
+  if (flag) condc = NULL;
+  else
+  {
+    condc = bnrconductorofchar(bnr, chi);
+    if (gequal(cond, condc)) flag = 1;
+  }
+
+  if (flag)
+  {
+    GEN initc = init_get_chic(cyc);
+    bnrc = bnr;
+    CHI = get_Char(chi, initc, NULL, prec);
+  }
+  else
+  {
+    bnrc = Buchray(bnr_get_bnf(bnr), condc, nf_INIT|nf_GEN);
+    CHI = GetPrimChar(chi, bnr, bnrc, prec);
+  }
+  return gerepilecopy(av, SingleArtinNumber(bnrc, CHI, prec));
+}
+
+/********************************************************************/
+/*               3rd part: initialize the characters                */
+/********************************************************************/
+/* returns a ZV */
+static GEN
+LiftChar(GEN cyc, GEN Mat, GEN chi, GEN D)
+{
+  long lm = lg(cyc), l  = lg(chi), i, j;
+  GEN lchi = cgetg(lm, t_VEC);
+  for (i = 1; i < lm; i++)
+  {
+    pari_sp av = avma;
+    GEN t, s  = mulii(gel(chi,1), gcoeff(Mat, 1, i));
+    for (j = 2; j < l; j++)
+    { /* rarely exercised: D[1]/D[j] could be precomputed */
+      t = mulii(gel(chi,j), diviiexact(gel(D,1), gel(D,j)));
+      s = addii(s, mulii(t, gcoeff(Mat, j, i)));
+    }
+    t = diviiexact(mulii(s, gel(cyc,i)), gel(D,1));
+    gel(lchi,i) = gerepileuptoint(av, modii(t, gel(cyc,i)));
+  }
+  return lchi;
+}
+
+/* Let chi be a character, A(chi) corresponding to the primes dividing diff
+   at s = flag. If s = 0, returns [r, A] where r is the order of vanishing
+   at s = 0 corresponding to diff. No GC */
+static GEN
+ComputeAChi(GEN dtcr, long *r, long flag, long prec)
+{
+  long l, i;
+  GEN A, diff, chi, bnrc;
+
+  bnrc = ch_bnr(dtcr);
+  diff = ch_diff(dtcr); l = lg(diff);
+  chi  = ch_CHI0(dtcr);
+
+  A = gen_1;
+  *r = 0;
+  for (i = 1; i < l; i++)
+  {
+    GEN pr = gel(diff,i), B;
+    GEN z = ComputeImagebyChar(chi, isprincipalray(bnrc, pr));
+
+    if (flag)
+      B = gsubsg(1, gdiv(z, pr_norm(pr)));
+    else if (gequal1(z))
+    {
+      B = glog(pr_norm(pr), prec);
+      (*r)++;
+    }
+    else
+      B = gsubsg(1, z);
+    A = gmul(A, B);
+  }
+  return A;
+}
+/* simplified version of ComputeAchi: return 1 if L(0,chi) = 0 */
+static int
+L_vanishes_at_0(GEN dtcr)
+{
+  long l, i;
+  GEN diff, chi, bnrc;
+
+  bnrc = ch_bnr(dtcr);
+  diff = ch_diff(dtcr); l = lg(diff);
+  chi  = ch_CHI0(dtcr);
+  for (i = 1; i < l; i++)
+  {
+    GEN pr = gel(diff,i);
+    GEN z = ComputeImagebyChar(chi, isprincipalray(bnrc, pr));
+    if (gequal1(z)) return 1;
+  }
+  return 0;
+}
+
+static GEN
+_data4(GEN arch, long r1, long r2)
+{
+  GEN z = cgetg(5, t_VECSMALL);
+  long i, b, q = 0;
+
+  for (i=1; i<=r1; i++) if (signe(gel(arch,i))) q++;
+  z[1] = q; b = r1 - q;
+  z[2] = b;
+  z[3] = r2;
+  z[4] = maxss(b+r2+1, r2+q);
+  return z;
+}
+
+/* Given a list [chi, F = cond(chi)] of characters over Cl(bnr), compute a
+   vector dataCR containing for each character:
+   2: the constant C(F) [t_REAL]
+   3: bnr(F)
+   4: [q, r1 - q, r2, rc] where
+        q = number of real places in F
+        rc = max{r1 + r2 - q + 1, r2 + q}
+   6: diff(chi) primes dividing m but not F
+   7: finite part of F
+
+   1: chi
+   5: [(c_i), z, d] in bnr(m)
+   8: [(c_i), z, d] in bnr(F)
+   9: if NULL then does not compute (for AllStark) */
+static GEN
+InitChar(GEN bnr, GEN listCR, long prec)
+{
+  GEN bnf = checkbnf(bnr), nf = bnf_get_nf(bnf);
+  GEN modul, dk, C, dataCR, chi, cond, initc;
+  long N, r1, r2, prec2, i, j, l;
+  pari_sp av = avma;
+
+  modul = bnr_get_mod(bnr);
+  dk    = nf_get_disc(nf);
+  N     = nf_get_degree(nf);
+  nf_get_sign(nf, &r1,&r2);
+  prec2 = precdbl(prec) + EXTRA_PREC;
+  C     = gmul2n(sqrtr_abs(divir(dk, powru(mppi(prec2),N))), -r2);
+  initc = init_get_chic( bnr_get_cyc(bnr) );
+
+  dataCR = cgetg_copy(listCR, &l);
+  for (i = 1; i < l; i++)
+  {
+    GEN olddtcr, dtcr = cgetg(10, t_VEC);
+    gel(dataCR,i) = dtcr;
+
+    chi  = gmael(listCR, i, 1);
+    cond = gmael(listCR, i, 2);
+
+    /* do we already know the invariants of chi? */
+    olddtcr = NULL;
+    for (j = 1; j < i; j++)
+      if (gequal(cond, gmael(listCR,j,2))) { olddtcr = gel(dataCR,j); break; }
+
+    if (!olddtcr)
+    {
+      ch_C(dtcr) = gmul(C, gsqrt(ZM_det_triangular(gel(cond,1)), prec2));
+      ch_4(dtcr) = _data4(gel(cond,2),r1,r2);
+      ch_cond(dtcr) = gel(cond,1);
+      if (gequal(cond,modul))
+      {
+        ch_bnr(dtcr) = bnr;
+        ch_diff(dtcr) = cgetg(1, t_VEC);
+      }
+      else
+      {
+        ch_bnr(dtcr) = Buchray(bnf, cond, nf_INIT|nf_GEN);
+        ch_diff(dtcr) = get_prdiff(bnr, cond);
+      }
+    }
+    else
+    {
+      ch_C(dtcr) = ch_C(olddtcr);
+      ch_bnr(dtcr) = ch_bnr(olddtcr);
+      ch_4(dtcr) = ch_4(olddtcr);
+      ch_diff(dtcr) = ch_diff(olddtcr);
+      ch_cond(dtcr) = ch_cond(olddtcr);
+    }
+
+    ch_chi(dtcr) = chi; /* the character */
+    ch_CHI(dtcr) = get_Char(chi,initc,NULL,prec); /* associated to bnr(m) */
+    ch_comp(dtcr) = gen_1; /* compute this character (by default) */
+    chi = GetPrimChar(chi, bnr, ch_bnr(dtcr), prec2);
+    if (!chi) chi = ch_CHI(dtcr);
+    ch_CHI0(dtcr) = chi;
+  }
+
+  return gerepilecopy(av, dataCR);
+}
+
+/* compute the list of characters to consider for AllStark and
+   initialize precision-independent data to compute with them */
+static GEN
+get_listCR(GEN bnr, GEN dtQ)
+{
+  GEN MrD, listCR, vecchi, lchi, Surj, cond, Mr, d, allCR;
+  long hD, h, nc, i, j, tnc;
+
+  Surj = gel(dtQ,3);
+  MrD  = gel(dtQ,2);
+  Mr   = bnr_get_cyc(bnr);
+  hD   = itos(gel(dtQ,1));
+  h    = hD >> 1;
+
+  listCR = cgetg(h + 1, t_VEC); /* non-conjugate characters */
+  nc  = 1;
+  allCR  = cgetg(h + 1, t_VEC); /* all characters, including conjugates */
+  tnc = 1;
+
+  vecchi = EltsOfGroup(hD, MrD);
+
+  for (i = 1; tnc <= h; i++)
+  {
+    /* lift a character of D in Clk(m) */
+    lchi = LiftChar(Mr, Surj, gel(vecchi,i), MrD);
+
+    for (j = 1; j < tnc; j++)
+      if (ZV_equal(lchi, gel(allCR,j))) break;
+    if (j != tnc) continue;
+
+    cond = bnrconductorofchar(bnr, lchi);
+    if (gequal0(gel(cond,2))) continue;
+
+    /* the infinite part of chi is non trivial */
+    gel(listCR,nc++) = mkvec2(lchi, cond);
+    gel(allCR,tnc++) = lchi;
+
+    /* if chi is not real, add its conjugate character to allCR */
+    d = Order(Mr, lchi);
+    if (!equaliu(d, 2))
+      gel(allCR,tnc++) = ConjChar(lchi, Mr);
+  }
+  setlg(listCR, nc); return listCR;
+}
+
+/* recompute dataCR with the new precision */
+static GEN
+CharNewPrec(GEN dataCR, GEN nf, long prec)
+{
+  GEN dk, C, p1;
+  long N, l, j, prec2;
+
+  dk    =  nf_get_disc(nf);
+  N     =  nf_get_degree(nf);
+  prec2 = precdbl(prec) + EXTRA_PREC;
+
+  C = sqrtr(divir(absi(dk), powru(mppi(prec2), N)));
+
+  l = lg(dataCR);
+  for (j = 1; j < l; j++)
+  {
+    GEN dtcr = gel(dataCR,j);
+    ch_C(dtcr) = gmul(C, gsqrt(ZM_det_triangular(ch_cond(dtcr)), prec2));
+
+    gmael(ch_bnr(dtcr), 1, 7) = nf;
+
+    p1 = ch_CHI( dtcr); gel(p1,2) = InitRU(gel(p1,3), prec2);
+    p1 = ch_CHI0(dtcr); gel(p1,2) = InitRU(gel(p1,3), prec2);
+  }
+
+  return dataCR;
+}
+
+/********************************************************************/
+/*             4th part: compute the coefficients an(chi)           */
+/*                                                                  */
+/* matan entries are arrays of ints containing the coefficients of  */
+/* an(chi) as a polmod modulo polcyclo(order(chi))                     */
+/********************************************************************/
+
+static void
+_0toCoeff(int *rep, long deg)
+{
+  long i;
+  for (i=0; i<deg; i++) rep[i] = 0;
+}
+
+/* transform a polmod into Coeff */
+static void
+Polmod2Coeff(int *rep, GEN polmod, long deg)
+{
+  long i;
+  if (typ(polmod) == t_POLMOD)
+  {
+    GEN pol = gel(polmod,2);
+    long d = degpol(pol);
+
+    pol += 2;
+    for (i=0; i<=d; i++) rep[i] = itos(gel(pol,i));
+    for (   ; i<deg; i++) rep[i] = 0;
+  }
+  else
+  {
+    rep[0] = itos(polmod);
+    for (i=1; i<deg; i++) rep[i] = 0;
+  }
+}
+
+/* initialize a deg * n matrix of ints */
+static int**
+InitMatAn(long n, long deg, long flag)
+{
+  long i, j;
+  int *a, **A = (int**)pari_malloc((n+1)*sizeof(int*));
+  A[0] = NULL;
+  for (i = 1; i <= n; i++)
+  {
+    a = (int*)pari_malloc(deg*sizeof(int));
+    A[i] = a; a[0] = (i == 1 || flag);
+    for (j = 1; j < deg; j++) a[j] = 0;
+  }
+  return A;
+}
+
+static void
+FreeMat(int **A, long n)
+{
+  long i;
+  for (i = 0; i <= n; i++)
+    if (A[i]) pari_free((void*)A[i]);
+  pari_free((void*)A);
+}
+
+/* initialize Coeff reduction */
+static int**
+InitReduction(GEN CHI, long deg)
+{
+  long j;
+  pari_sp av = avma;
+  int **A;
+  GEN d, polmod, pol;
+
+  A   = (int**)pari_malloc(deg*sizeof(int*));
+  d   = gel(CHI,3);
+  pol = polcyclo(itos(d), 0);
+  for (j = 0; j < deg; j++)
+  {
+    A[j] = (int*)pari_malloc(deg*sizeof(int));
+    polmod = gmodulo(monomial(gen_1, deg+j, 0), pol);
+    Polmod2Coeff(A[j], polmod, deg);
+  }
+
+  avma = av; return A;
+}
+
+#if 0
+void
+pan(int **an, long n, long deg)
+{
+  long i,j;
+  for (i = 1; i <= n; i++)
+  {
+    err_printf("n = %ld: ",i);
+    for (j = 0; j < deg; j++) err_printf("%d ",an[i][j]);
+    err_printf("\n");
+  }
+}
+#endif
+
+/* returns 0 if c is zero, 1 otherwise. */
+static int
+IsZero(int* c, long deg)
+{
+  long i;
+  for (i = 0; i < deg; i++)
+    if (c[i]) return 0;
+  return 1;
+}
+
+/* set c0 <-- c0 * c1 */
+static void
+MulCoeff(int *c0, int* c1, int** reduc, long deg)
+{
+  long i,j;
+  int c, *T;
+
+  if (IsZero(c0,deg)) return;
+
+  T = (int*)new_chunk(2*deg);
+  for (i = 0; i < 2*deg; i++)
+  {
+    c = 0;
+    for (j = 0; j <= i; j++)
+      if (j < deg && j > i - deg) c += c0[j] * c1[i-j];
+    T[i] = c;
+  }
+  for (i = 0; i < deg; i++)
+  {
+    c = T[i];
+    for (j = 0; j < deg; j++) c += reduc[j][i] * T[deg+j];
+    c0[i] = c;
+  }
+}
+
+/* c0 <- c0 + c1 * c2 */
+static void
+AddMulCoeff(int *c0, int *c1, int* c2, int** reduc, long deg)
+{
+  long i, j;
+  pari_sp av;
+  int c, *t;
+
+  if (IsZero(c2,deg)) return;
+  if (!c1) /* c1 == 1 */
+  {
+    for (i = 0; i < deg; i++) c0[i] += c2[i];
+    return;
+  }
+  av = avma;
+  t = (int*)new_chunk(2*deg); /* = c1 * c2, not reduced */
+  for (i = 0; i < 2*deg; i++)
+  {
+    c = 0;
+    for (j = 0; j <= i; j++)
+      if (j < deg && j > i - deg) c += c1[j] * c2[i-j];
+    t[i] = c;
+  }
+  for (i = 0; i < deg; i++)
+  {
+    c = t[i];
+    for (j = 0; j < deg; j++) c += reduc[j][i] * t[deg+j];
+    c0[i] += c;
+  }
+  avma = av;
+}
+
+/* evaluate the Coeff. No Garbage collector */
+static GEN
+EvalCoeff(GEN z, int* c, long deg)
+{
+  long i,j;
+  GEN e, r;
+
+  if (!c) return gen_0;
+#if 0
+  /* standard Horner */
+  e = stoi(c[deg - 1]);
+  for (i = deg - 2; i >= 0; i--)
+    e = gadd(stoi(c[i]), gmul(z, e));
+#else
+  /* specific attention to sparse polynomials */
+  e = NULL;
+  for (i = deg-1; i >=0; i=j-1)
+  {
+    for (j=i; c[j] == 0; j--)
+      if (j==0)
+      {
+        if (!e) return NULL;
+        if (i!=j) z = gpowgs(z,i-j+1);
+        return gmul(e,z);
+      }
+    if (e)
+    {
+      r = (i==j)? z: gpowgs(z,i-j+1);
+      e = gadd(gmul(e,r), stoi(c[j]));
+    }
+    else
+      e = stoi(c[j]);
+  }
+#endif
+  return e;
+}
+
+/* copy the n * (m+1) array matan */
+static void
+CopyCoeff(int** a, int** a2, long n, long m)
+{
+  long i,j;
+
+  for (i = 1; i <= n; i++)
+  {
+    int *b = a[i], *b2 = a2[i];
+    for (j = 0; j < m; j++) b2[j] = b[j];
+  }
+}
+
+/* return q*p if <= n. Beware overflow */
+static long
+next_pow(long q, long p, long n)
+{
+  const GEN x = muluu((ulong)q, (ulong)p);
+  const ulong qp = (ulong)x[2];
+  return (lgefint(x) > 3 || qp > (ulong)n)? 0: qp;
+}
+
+static void
+an_AddMul(int **an,int **an2, long np, long n, long deg, GEN chi, int **reduc)
+{
+  GEN chi2 = chi;
+  long q, qk, k;
+  int *c, *c2 = (int*)new_chunk(deg);
+
+  CopyCoeff(an, an2, n/np, deg);
+  for (q=np;;)
+  {
+    if (gequal1(chi2)) c = NULL; else { Polmod2Coeff(c2, chi2, deg); c = c2; }
+    for(k = 1, qk = q; qk <= n; k++, qk += q)
+      AddMulCoeff(an[qk], c, an2[k], reduc, deg);
+    if (! (q = next_pow(q,np, n)) ) break;
+
+    chi2 = gmul(chi2, chi);
+  }
+}
+
+/* correct the coefficients an(chi) according with diff(chi) in place */
+static void
+CorrectCoeff(GEN dtcr, int** an, int** reduc, long n, long deg)
+{
+  pari_sp av = avma;
+  long lg, j, np;
+  pari_sp av1;
+  int **an2;
+  GEN bnrc, diff, chi, pr;
+  CHI_t C;
+
+  diff = ch_diff(dtcr); lg = lg(diff) - 1;
+  if (!lg) return;
+
+  if (DEBUGLEVEL>2) err_printf("diff(CHI) = %Ps", diff);
+  bnrc =  ch_bnr(dtcr);
+  init_CHI_alg(&C, ch_CHI0(dtcr));
+
+  an2 = InitMatAn(n, deg, 0);
+  av1 = avma;
+  for (j = 1; j <= lg; j++)
+  {
+    pr = gel(diff,j);
+    np = itos( pr_norm(pr) );
+
+    chi  = EvalChar(&C, isprincipalray(bnrc, pr));
+
+    an_AddMul(an,an2,np,n,deg,chi,reduc);
+    avma = av1;
+  }
+  FreeMat(an2, n); avma = av;
+}
+
+/* compute the coefficients an in the general case */
+static int**
+ComputeCoeff(GEN dtcr, LISTray *R, long n, long deg)
+{
+  pari_sp av = avma, av2;
+  long i, l, np;
+  int **an, **reduc, **an2;
+  GEN L, CHI, chi;
+  CHI_t C;
+
+  CHI = ch_CHI(dtcr); init_CHI_alg(&C, CHI);
+
+  an  = InitMatAn(n, deg, 0);
+  an2 = InitMatAn(n, deg, 0);
+  reduc  = InitReduction(CHI, deg);
+  av2 = avma;
+
+  L = R->L1; l = lg(L);
+  for (i=1; i<l; i++, avma = av2)
+  {
+    np = L[i];
+    chi  = EvalChar(&C, gel(R->L1ray,i));
+    an_AddMul(an,an2,np,n,deg,chi,reduc);
+  }
+  FreeMat(an2, n);
+
+  CorrectCoeff(dtcr, an, reduc, n, deg);
+  FreeMat(reduc, deg-1);
+  avma = av; return an;
+}
+
+/********************************************************************/
+/*              5th part: compute L-functions at s=1                */
+/********************************************************************/
+static void
+deg11(LISTray *R, long p, GEN bnr, GEN pr) {
+  GEN z = isprincipalray(bnr, pr);
+  vecsmalltrunc_append(R->L1, p);
+  vectrunc_append(R->L1ray, z);
+}
+static void
+deg12(LISTray *R, long p, GEN bnr, GEN Lpr) {
+  GEN z = isprincipalray(bnr, gel(Lpr,1));
+  vecsmalltrunc_append(R->L11, p);
+  vectrunc_append(R->L11ray, z);
+}
+static void
+deg0(LISTray *R, long p) { vecsmalltrunc_append(R->L0, p); }
+static void
+deg2(LISTray *R, long p) { vecsmalltrunc_append(R->L2, p); }
+
+/* pi(x) <= ?? */
+static long
+PiBound(ulong x)
+{
+  double lx = log((double)x);
+  return 1 + (long) (x/lx * (1 + 3/(2*lx)));
+}
+
+static void
+InitPrimesQuad(GEN bnr, ulong N0, LISTray *R)
+{
+  pari_sp av = avma;
+  GEN bnf = bnr_get_bnf(bnr), cond = gel(bnr_get_mod(bnr), 1);
+  long p,i,l, condZ = itos(gcoeff(cond,1,1)), contZ = itos(content(cond));
+  GEN prime, Lpr, nf = bnf_get_nf(bnf), dk = nf_get_disc(nf);
+  forprime_t T;
+
+  l = 1 + PiBound(N0);
+  R->L0 = vecsmalltrunc_init(l);
+  R->L2 = vecsmalltrunc_init(l); R->condZ = condZ;
+  R->L1 = vecsmalltrunc_init(l); R->L1ray = vectrunc_init(l);
+  R->L11= vecsmalltrunc_init(l); R->L11ray= vectrunc_init(l);
+  prime = utoipos(2);
+  u_forprime_init(&T, 2, N0);
+  while ( (p = u_forprime_next(&T)) )
+  {
+    prime[2] = p;
+    switch (kroiu(dk, p))
+    {
+    case -1: /* inert */
+      if (condZ % p == 0) deg0(R,p); else deg2(R,p);
+      break;
+    case 1: /* split */
+      Lpr = idealprimedec(nf, prime);
+      if      (condZ % p != 0) deg12(R, p, bnr, Lpr);
+      else if (contZ % p == 0) deg0(R,p);
+      else
+      {
+        GEN pr = idealval(nf, cond, gel(Lpr,1))? gel(Lpr,2): gel(Lpr,1);
+        deg11(R, p, bnr, pr);
+      }
+      break;
+    default: /* ramified */
+      if (condZ % p == 0) deg0(R,p);
+      else {
+        GEN pr = gel(idealprimedec(nf,prime),1);
+        deg11(R, p, bnr, pr);
+      }
+      break;
+    }
+  }
+  /* precompute isprincipalray(x), x in Z */
+  R->rayZ = cgetg(condZ, t_VEC);
+  for (i=1; i<condZ; i++)
+    gel(R->rayZ,i) = (ugcd(i,condZ) == 1)? isprincipalray(bnr, utoipos(i)): gen_0;
+  gerepileall(av, 7, &(R->L0), &(R->L2), &(R->rayZ),
+              &(R->L1), &(R->L1ray), &(R->L11), &(R->L11ray) );
+}
+
+static void
+InitPrimes(GEN bnr, ulong N0, LISTray *R)
+{
+  GEN bnf = bnr_get_bnf(bnr), cond = gel(bnr_get_mod(bnr), 1);
+  long p,j,k,l, condZ = itos(gcoeff(cond,1,1)), N = lg(cond)-1;
+  GEN tmpray, tabpr, prime, nf = bnf_get_nf(bnf);
+  forprime_t T;
+
+  R->condZ = condZ; l = PiBound(N0) * N;
+  tmpray = cgetg(N+1, t_VEC);
+  R->L1 = vecsmalltrunc_init(l);
+  R->L1ray = vectrunc_init(l);
+  u_forprime_init(&T, 2, N0);
+  prime = utoipos(2);
+  while ( (p = u_forprime_next(&T)) )
+  {
+    pari_sp av = avma;
+    prime[2] = p;
+    if (DEBUGLEVEL>1 && (p & 2047) == 1) err_printf("%ld ", p);
+    tabpr = idealprimedec(nf, prime);
+    for (j = 1; j < lg(tabpr); j++)
+    {
+      GEN pr  = gel(tabpr,j);
+      ulong np = upowuu(p, pr_get_f(pr));
+      if (!np || np > N0) break;
+      if (condZ % p == 0 && idealval(nf, cond, pr))
+      {
+        gel(tmpray,j) = NULL; continue;
+      }
+
+      vecsmalltrunc_append(R->L1, np);
+      gel(tmpray,j) = gclone( isprincipalray(bnr, pr) );
+    }
+    avma = av;
+    for (k = 1; k < j; k++)
+    {
+      if (!tmpray[k]) continue;
+      vectrunc_append(R->L1ray, ZC_copy(gel(tmpray,k)));
+      gunclone(gel(tmpray,k));
+    }
+  }
+}
+
+static GEN /* cf polcoeff */
+_sercoeff(GEN x, long n)
+{
+  long i = n - valp(x);
+  return (i < 0)? gen_0: gel(x,i+2);
+}
+
+static void
+affect_coeff(GEN q, long n, GEN y)
+{
+  GEN x = _sercoeff(q,-n);
+  if (x == gen_0) gel(y,n) = NULL; else affgr(x, gel(y,n));
+}
+
+typedef struct {
+  GEN c1, aij, bij, cS, cT, powracpi;
+  long i0, a,b,c, r, rc1, rc2;
+} ST_t;
+
+/* compute the principal part at the integers s = 0, -1, -2, ..., -i0
+   of Gamma((s+1)/2)^a Gamma(s/2)^b Gamma(s)^c / (s - z) with z = 0 and 1 */
+/* NOTE: surely not the best way to do this, but it's fast enough! */
+static void
+ppgamma(ST_t *T, long prec)
+{
+  GEN eul, gam,gamun,gamdm, an,bn,cn_evn,cn_odd, x,x2,X,Y, cf, sqpi;
+  GEN p1, p2, aij, bij;
+  long a = T->a;
+  long b = T->b;
+  long c = T->c, r = T->r, i0 = T->i0;
+  long i,j, s,t;
+  pari_sp av;
+
+  aij = cgetg(i0+1, t_VEC);
+  bij = cgetg(i0+1, t_VEC);
+  for (i = 1; i <= i0; i++)
+  {
+    gel(aij,i) = p1 = cgetg(r+1, t_VEC);
+    gel(bij,i) = p2 = cgetg(r+1, t_VEC);
+    for (j=1; j<=r; j++) { gel(p1,j) = cgetr(prec); gel(p2,j) = cgetr(prec); }
+  }
+  av = avma;
+
+  x   = pol_x(0);
+  x2  = gmul2n(x, -1); /* x/2 */
+  eul = mpeuler(prec);
+  sqpi= sqrtr_abs(mppi(prec)); /* Gamma(1/2) */
+
+  /* expansion of log(Gamma(u)) at u = 1 */
+  gamun = cgetg(r+3, t_SER);
+  gamun[1] = evalsigne(1) | _evalvalp(0) | evalvarn(0);
+  gel(gamun,2) = gen_0;
+  gel(gamun,3) = gneg(eul);
+  for (i = 2; i <= r; i++)
+    gel(gamun,i+2) = divrs(szeta(i,prec), odd(i)? -i: i);
+  gamun = gexp(gamun, prec); /* Gamma(1 + x) */
+  gam = gdiv(gamun,x); /* Gamma(x) */
+
+  /* expansion of log(Gamma(u) / Gamma(1/2)) at u = 1/2 */
+  gamdm = cgetg(r+3, t_SER);
+  gamdm[1] = evalsigne(1) | _evalvalp(0) | evalvarn(0);
+  gel(gamdm,2) = gen_0;
+  gel(gamdm,3) = gneg(gadd(gmul2n(mplog2(prec), 1), eul));
+  for (i = 2; i <= r; i++)
+    gel(gamdm,i+2) = mulri(gel(gamun,i+2), subis(int2n(i), 1));
+  gamdm = gmul(sqpi, gexp(gamdm, prec)); /* Gamma(1/2 + x) */
+
+ /* We simplify to get one of the following two expressions
+  * if (b > a) : sqrt{Pi}^a 2^{a-au} Gamma(u)^{a+c} Gamma(  u/2  )^{|b-a|}
+  * if (b <= a): sqrt{Pi}^b 2^{b-bu} Gamma(u)^{b+c} Gamma((u+1)/2)^{|b-a|} */
+  if (b > a)
+  {
+    t = a; s = b; X = x2; Y = gsub(x2,ghalf);
+    p1 = ser_unscale(gam, ghalf);
+    p2 = gdiv(ser_unscale(gamdm,ghalf), Y); /* Gamma((x-1)/2) */
+  }
+  else
+  {
+    t = b; s = a; X = gadd(x2,ghalf); Y = x2;
+    p1 = ser_unscale(gamdm,ghalf);
+    p2 = ser_unscale(gam,ghalf);
+  }
+  cf = powru(sqpi, t);
+  an = gpowgs(gpow(gen_2, gsubsg(1,x), prec), t); /* 2^{t-tx} */
+  bn = gpowgs(gam, t+c); /* Gamma(x)^{t+c} */
+  cn_evn = gpowgs(p1, s-t); /* Gamma(X)^{s-t} */
+  cn_odd = gpowgs(p2, s-t); /* Gamma(Y)^{s-t} */
+  for (i = 0; i < i0/2; i++)
+  {
+    GEN C1,q1, A1 = gel(aij,2*i+1), B1 = gel(bij,2*i+1);
+    GEN C2,q2, A2 = gel(aij,2*i+2), B2 = gel(bij,2*i+2);
+
+    C1 = gmul(cf, gmul(bn, gmul(an, cn_evn)));
+    p1 = gdiv(C1, gsubgs(x, 2*i));
+    q1 = gdiv(C1, gsubgs(x, 2*i+1));
+
+    /* an(x-u-1) = 2^t an(x-u) */
+    an = gmul2n(an, t);
+    /* bn(x-u-1) = bn(x-u) / (x-u-1)^{t+c} */
+    bn = gdiv(bn, gpowgs(gsubgs(x, 2*i+1), t+c));
+
+    C2 = gmul(cf, gmul(bn, gmul(an, cn_odd)));
+    p2 = gdiv(C2, gsubgs(x, 2*i+1));
+    q2 = gdiv(C2, gsubgs(x, 2*i+2));
+    for (j = 1; j <= r; j++)
+    {
+      affect_coeff(p1, j, A1); affect_coeff(q1, j, B1);
+      affect_coeff(p2, j, A2); affect_coeff(q2, j, B2);
+    }
+
+    an = gmul2n(an, t);
+    bn = gdiv(bn, gpowgs(gsubgs(x, 2*i+2), t+c));
+    /* cn_evn(x-2i-2) = cn_evn(x-2i)  / (X - (i+1))^{s-t} */
+    /* cn_odd(x-2i-3) = cn_odd(x-2i-1)/ (Y - (i+1))^{s-t} */
+    cn_evn = gdiv(cn_evn, gpowgs(gsubgs(X,i+1), s-t));
+    cn_odd = gdiv(cn_odd, gpowgs(gsubgs(Y,i+1), s-t));
+  }
+  T->aij = aij;
+  T->bij = bij; avma = av;
+}
+
+static GEN
+_cond(GEN dtcr) { return mkvec2(ch_cond(dtcr), ch_4(dtcr)); }
+
+/* sort chars according to conductor */
+static GEN
+sortChars(GEN dataCR)
+{
+  const long cl = lg(dataCR) - 1;
+  GEN vCond  = cgetg(cl+1, t_VEC);
+  GEN CC     = cgetg(cl+1, t_VECSMALL);
+  GEN nvCond = cgetg(cl+1, t_VECSMALL);
+  long j,k, ncond;
+  GEN vChar;
+
+  for (j = 1; j <= cl; j++) nvCond[j] = 0;
+
+  ncond = 0;
+  for (j = 1; j <= cl; j++)
+  {
+    GEN cond = _cond(gel(dataCR,j));
+    for (k = 1; k <= ncond; k++)
+      if (gequal(cond, gel(vCond,k))) break;
+    if (k > ncond) gel(vCond,++ncond) = cond;
+    nvCond[k]++; CC[j] = k; /* char j has conductor number k */
+  }
+  vChar = cgetg(ncond+1, t_VEC);
+  for (k = 1; k <= ncond; k++)
+  {
+    gel(vChar,k) = cgetg(nvCond[k]+1, t_VECSMALL);
+    nvCond[k] = 0;
+  }
+  for (j = 1; j <= cl; j++)
+  {
+    k = CC[j]; nvCond[k]++;
+    mael(vChar,k,nvCond[k]) = j;
+  }
+  return vChar;
+}
+
+/* Given W(chi), S(chi) and T(chi), return L(1, chi) if fl & 1, else
+   [r(chi), c(chi)] where L(s, chi) ~ c(chi) s^r(chi) at s = 0.
+   If fl & 2, adjust the value to get L_S(s, chi). */
+static GEN
+GetValue(GEN dtcr, GEN W, GEN S, GEN T, long fl, long prec)
+{
+  pari_sp av = avma;
+  GEN cf, z, p1;
+  long q, b, c, r;
+  int isreal = (itos(gel(ch_CHI0(dtcr), 3)) <= 2);
+
+  p1 = ch_4(dtcr);
+  q = p1[1];
+  b = p1[2];
+  c = p1[3];
+
+  if (fl & 1)
+  { /* S(chi) + W(chi).T(chi)) / (C(chi) sqrt(Pi)^{r1 - q}) */
+    cf = gmul(ch_C(dtcr), powruhalf(mppi(prec), b));
+
+    z = gadd(S, gmul(W, T));
+    if (isreal) z = real_i(z);
+    z = gdiv(z, cf);
+    if (fl & 2) z = gmul(z, ComputeAChi(dtcr, &r, 1, prec));
+  }
+  else
+  { /* (W(chi).S(conj(chi)) + T(chi)) / (sqrt(Pi)^q 2^{r1 - q}) */
+    cf = gmul2n(powruhalf(mppi(prec), q), b);
+
+    z = gadd(gmul(W, gconj(S)), gconj(T));
+    if (isreal) z = real_i(z);
+    z = gdiv(z, cf); r = 0;
+    if (fl & 2) z = gmul(z, ComputeAChi(dtcr, &r, 0, prec));
+    z = mkvec2(utoi(b + c + r), z);
+  }
+  return gerepilecopy(av, z);
+}
+
+/* return the order and the first non-zero term of L(s, chi0)
+   at s = 0. If flag != 0, adjust the value to get L_S(s, chi0). */
+static GEN
+GetValue1(GEN bnr, long flag, long prec)
+{
+  GEN bnf = checkbnf(bnr), nf = bnf_get_nf(bnf);
+  GEN h, R, c, diff;
+  long i, l, r, r1, r2;
+  pari_sp av = avma;
+
+  nf_get_sign(nf, &r1,&r2);
+  h = bnf_get_no(bnf);
+  R = bnf_get_reg(bnf);
+
+  c = gneg_i(gdivgs(mpmul(h, R), bnf_get_tuN(bnf)));
+  r = r1 + r2 - 1;
+
+  if (flag)
+  {
+    diff = divcond(bnr);
+    l = lg(diff) - 1; r += l;
+    for (i = 1; i <= l; i++)
+      c = gmul(c, glog(pr_norm(gel(diff,i)), prec));
+  }
+  return gerepilecopy(av, mkvec2(stoi(r), c));
+}
+
+/********************************************************************/
+/*                6th part: recover the coefficients                */
+/********************************************************************/
+static long
+TestOne(GEN plg, RC_data *d)
+{
+  long j, v = d->v;
+  GEN z = gsub(d->beta, gel(plg,v));
+  if (expo(z) >= d->G) return 0;
+  for (j = 1; j < lg(plg); j++)
+    if (j != v && mpcmp(d->B, mpabs(gel(plg,j))) < 0) return 0;
+  return 1;
+}
+
+static GEN
+chk_reccoeff_init(FP_chk_fun *chk, GEN r, GEN mat)
+{
+  RC_data *d = (RC_data*)chk->data;
+  (void)r; d->U = mat; return d->nB;
+}
+
+static GEN
+chk_reccoeff(void *data, GEN x)
+{
+  RC_data *d = (RC_data*)data;
+  GEN v = gmul(d->U, x), z = gel(v,1);
+
+  if (!gequal1(z)) return NULL;
+  *++v = evaltyp(t_COL) | evallg( lg(d->M) );
+  if (TestOne(gmul(d->M, v), d)) return v;
+  return NULL;
+}
+
+/* Using Cohen's method */
+static GEN
+RecCoeff3(GEN nf, RC_data *d, long prec)
+{
+  GEN A, M, nB, cand, p1, B2, C2, tB, beta2, nf2, Bd;
+  GEN beta = d->beta, B = d->B;
+  long N = d->N, v = d->v, e, BIG;
+  long i, j, k, l, ct = 0, prec2;
+  FP_chk_fun chk = { &chk_reccoeff, &chk_reccoeff_init, NULL, NULL, 0 };
+  chk.data = (void*)d;
+
+  d->G = minss(-10, -prec2nbits(prec) >> 4);
+  BIG = maxss(32, -(d->G << 1));
+  tB  = sqrtnr(real2n(BIG-N,DEFAULTPREC), N-1);
+  Bd  = grndtoi(gmin(B, tB), &e);
+  if (e > 0) return NULL; /* failure */
+  Bd = addis(Bd, 1);
+  prec2 = nbits2prec( expi(Bd) + 192 );
+  prec2 = maxss(precdbl(prec), prec2);
+  B2 = sqri(Bd);
+  C2 = shifti(B2, BIG<<1);
+
+LABrcf: ct++;
+  beta2 = gprec_w(beta, prec2);
+  nf2 = nfnewprec_shallow(nf, prec2);
+  d->M = M = nf_get_M(nf2);
+
+  A = cgetg(N+2, t_MAT);
+  for (i = 1; i <= N+1; i++) gel(A,i) = cgetg(N+2, t_COL);
+
+  gcoeff(A, 1, 1) = gadd(gmul(C2, gsqr(beta2)), B2);
+  for (j = 2; j <= N+1; j++)
+  {
+    p1 = gmul(C2, gmul(gneg_i(beta2), gcoeff(M, v, j-1)));
+    gcoeff(A, 1, j) = gcoeff(A, j, 1) = p1;
+  }
+  for (i = 2; i <= N+1; i++)
+    for (j = i; j <= N+1; j++)
+    {
+      p1 = gen_0;
+      for (k = 1; k <= N; k++)
+      {
+        GEN p2 = gmul(gcoeff(M, k, j-1), gcoeff(M, k, i-1));
+        if (k == v) p2 = gmul(C2, p2);
+        p1 = gadd(p1,p2);
+      }
+      gcoeff(A, i, j) = gcoeff(A, j, i) = p1;
+    }
+
+  nB = mului(N+1, B2);
+  d->nB = nB;
+  cand = fincke_pohst(A, nB, -1, prec2, &chk);
+
+  if (!cand)
+  {
+    if (ct > 3) return NULL;
+    prec2 = precdbl(prec2);
+    if (DEBUGLEVEL>1) pari_warn(warnprec,"RecCoeff", prec2);
+    goto LABrcf;
+  }
+
+  cand = gel(cand,1); l = lg(cand) - 1;
+  if (l == 1)
+    return coltoalg(nf, gel(cand,1));
+
+  if (DEBUGLEVEL>1) err_printf("RecCoeff3: no solution found!\n");
+  return NULL;
+}
+
+/* Using linear dependance relations */
+static GEN
+RecCoeff2(GEN nf,  RC_data *d,  long prec)
+{
+  pari_sp av;
+  GEN vec, M = nf_get_M(nf), beta = d->beta;
+  long i, imin, imax, lM = lg(M);
+
+  d->G = minss(-20, -prec2nbits(prec) >> 4);
+
+  vec  = shallowconcat(mkvec(gneg(beta)), row(M, d->v));
+  imin = (long)prec2nbits_mul(prec, .225);
+  imax = (long)prec2nbits_mul(prec, .315);
+
+  av = avma;
+  for (i = imax; i >= imin; i-=16, avma = av)
+  {
+    long e;
+    GEN v = lindep2(vec, i), z = gel(v,1);
+    if (!signe(z)) continue;
+    *++v = evaltyp(t_COL) | evallg(lM);
+    v = grndtoi(gdiv(v, z), &e);
+    if (e > 0) break;
+    if (TestOne(gmul(M, v), d)) return coltoalg(nf, v);
+  }
+  /* failure */
+  return RecCoeff3(nf,d,prec);
+}
+
+/* Attempts to find a polynomial with coefficients in nf such that
+   its coefficients are close to those of pol at the place v and
+   less than B at all the other places */
+static GEN
+RecCoeff(GEN nf,  GEN pol,  long v, long prec)
+{
+  long j, md, cl = degpol(pol);
+  pari_sp av = avma;
+  RC_data d;
+
+  /* if precision(pol) is too low, abort */
+  for (j = 2; j <= cl+1; j++)
+  {
+    GEN t = gel(pol, j);
+    if (prec2nbits(gprecision(t)) - gexpo(t) < 34) return NULL;
+  }
+
+  md = cl/2;
+  pol = leafcopy(pol);
+
+  d.N = nf_get_degree(nf);
+  d.v = v;
+
+  for (j = 1; j <= cl; j++)
+  { /* start with the coefficients in the middle,
+       since they are the harder to recognize! */
+    long cf = md + (j%2? j/2: -j/2);
+    GEN t, bound = shifti(binomial(utoipos(cl), cf), cl-cf);
+
+    if (DEBUGLEVEL>1) err_printf("RecCoeff (cf = %ld, B = %Ps)\n", cf, bound);
+    d.beta = real_i( gel(pol,cf+2) );
+    d.B    = bound;
+    if (! (t = RecCoeff2(nf, &d, prec)) ) return NULL;
+    gel(pol, cf+2) = t;
+  }
+  gel(pol,cl+2) = gen_1;
+  return gerepilecopy(av, pol);
+}
+
+/* an[q * i] *= chi for all (i,p)=1 */
+static void
+an_mul(int **an, long p, long q, long n, long deg, GEN chi, int **reduc)
+{
+  pari_sp av;
+  long c,i;
+  int *T;
+
+  if (gequal1(chi)) return;
+  av = avma;
+  T = (int*)new_chunk(deg); Polmod2Coeff(T,chi, deg);
+  for (c = 1, i = q; i <= n; i += q, c++)
+    if (c == p) c = 0; else MulCoeff(an[i], T, reduc, deg);
+  avma = av;
+}
+/* an[q * i] = 0 for all (i,p)=1 */
+static void
+an_set0_coprime(int **an, long p, long q, long n, long deg)
+{
+  long c,i;
+  for (c = 1, i = q; i <= n; i += q, c++)
+    if (c == p) c = 0; else _0toCoeff(an[i], deg);
+}
+/* an[q * i] = 0 for all i */
+static void
+an_set0(int **an, long p, long n, long deg)
+{
+  long i;
+  for (i = p; i <= n; i += p) _0toCoeff(an[i], deg);
+}
+
+/* compute the coefficients an for the quadratic case */
+static int**
+computean(GEN dtcr, LISTray *R, long n, long deg)
+{
+  pari_sp av = avma, av2;
+  long i, p, q, condZ, l;
+  int **an, **reduc;
+  GEN L, CHI, chi, chi1;
+  CHI_t C;
+
+  CHI = ch_CHI(dtcr); init_CHI_alg(&C, CHI);
+  condZ= R->condZ;
+
+  an = InitMatAn(n, deg, 1);
+  reduc = InitReduction(CHI, deg);
+  av2 = avma;
+
+  /* all pr | p divide cond */
+  L = R->L0; l = lg(L);
+  for (i=1; i<l; i++) an_set0(an,L[i],n,deg);
+
+  /* 1 prime of degree 2 */
+  L = R->L2; l = lg(L);
+  for (i=1; i<l; i++, avma = av2)
+  {
+    p = L[i];
+    if (condZ == 1) chi = C.val[0]; /* 1 */
+    else            chi = EvalChar(&C, gel(R->rayZ, p%condZ));
+    chi1 = chi;
+    for (q=p;;)
+    {
+      an_set0_coprime(an, p,q,n,deg); /* v_p(q) odd */
+      if (! (q = next_pow(q,p, n)) ) break;
+
+      an_mul(an,p,q,n,deg,chi,reduc);
+      if (! (q = next_pow(q,p, n)) ) break;
+      chi = gmul(chi, chi1);
+    }
+  }
+
+  /* 1 prime of degree 1 */
+  L = R->L1; l = lg(L);
+  for (i=1; i<l; i++, avma = av2)
+  {
+    p = L[i];
+    chi = EvalChar(&C, gel(R->L1ray,i));
+    chi1 = chi;
+    for(q=p;;)
+    {
+      an_mul(an,p,q,n,deg,chi,reduc);
+      if (! (q = next_pow(q,p, n)) ) break;
+      chi = gmul(chi, chi1);
+    }
+  }
+
+  /* 2 primes of degree 1 */
+  L = R->L11; l = lg(L);
+  for (i=1; i<l; i++, avma = av2)
+  {
+    GEN ray1, ray2, chi11, chi12, chi2;
+
+    p = L[i]; ray1 = gel(R->L11ray,i); /* use pr1 pr2 = (p) */
+    if (condZ == 1)
+      ray2 = ZC_neg(ray1);
+    else
+      ray2 = ZC_sub(gel(R->rayZ, p%condZ),  ray1);
+    chi11 = EvalChar(&C, ray1);
+    chi12 = EvalChar(&C, ray2);
+
+    chi1 = gadd(chi11, chi12);
+    chi2 = chi12;
+    for(q=p;;)
+    {
+      an_mul(an,p,q,n,deg,chi1,reduc);
+      if (! (q = next_pow(q,p, n)) ) break;
+      chi2 = gmul(chi2, chi12);
+      chi1 = gadd(chi2, gmul(chi1, chi11));
+    }
+  }
+
+  CorrectCoeff(dtcr, an, reduc, n, deg);
+  FreeMat(reduc, deg-1);
+  avma = av; return an;
+}
+
+/* return the vector of A^i/i for i = 1...n */
+static GEN
+mpvecpowdiv(GEN A, long n)
+{
+  GEN v = powruvec(A, n);
+  pari_sp av = avma;
+  long i;
+
+  for (i=2; i<=n; i++) affrr(divru(gel(v,i), i), gel(v,i));
+  avma = av; return v;
+}
+
+static void GetST0(GEN bnr, GEN *pS, GEN *pT, GEN dataCR, GEN vChar, long prec);
+
+/* compute S and T for the quadratic case. The following cases (cs) are:
+   1) bnr complex;
+   2) bnr real and no infinite place divide cond_chi (TBD);
+   3) bnr real and one infinite place divide cond_chi;
+   4) bnr real and both infinite places divide cond_chi (TBD) */
+static void
+QuadGetST(GEN bnr, GEN *pS, GEN *pT, GEN dataCR, GEN vChar, long prec)
+{
+  pari_sp av = avma, av1, av2;
+  long ncond, n, j, k, n0;
+  GEN N0, C, T = *pT, S = *pS, an, degs, cs;
+  LISTray LIST;
+
+  /* initializations */
+  degs = GetDeg(dataCR);
+  ncond = lg(vChar)-1;
+  C    = cgetg(ncond+1, t_VEC);
+  N0   = cgetg(ncond+1, t_VECSMALL);
+  cs   = cgetg(ncond+1, t_VECSMALL);
+  n0 = 0;
+  for (j = 1; j <= ncond; j++)
+  {
+    /* FIXME: make sure that this value of c is correct for the general case */
+    long r1, r2, q;
+    GEN dtcr = gel(dataCR, mael(vChar,j,1)), p1 = ch_4(dtcr), c = ch_C(dtcr);
+
+    gel(C,j) = c;
+    q = p1[1];
+
+    nf_get_sign(bnr_get_nf(gel(dtcr, 3)), &r1, &r2);
+    if (r1 == 2) /* real quadratic */
+    {
+      cs[j] = 2 + q;
+      /* FIXME:
+         make sure that this value of N0 is correct for the general case */
+      N0[j] = (long)prec2nbits_mul(prec, 0.35 * gtodouble(c));
+      if (cs[j] == 2 || cs[j] == 4) /* NOT IMPLEMENTED YET */
+      {
+        GetST0(bnr, pS, pT, dataCR, vChar, prec);
+        return;
+      }
+    }
+    else /* complex quadratic */
+    {
+      cs[j] = 1;
+      N0[j] = (long)prec2nbits_mul(prec, 0.7 * gtodouble(c));
+    }
+    if (n0 < N0[j]) n0 = N0[j];
+  }
+  if (DEBUGLEVEL>1) err_printf("N0 = %ld\n", n0);
+  InitPrimesQuad(bnr, n0, &LIST);
+
+  av1 = avma;
+  /* loop over conductors */
+  for (j = 1; j <= ncond; j++)
+  {
+    GEN c0 = gel(C,j), c1 = divur(1, c0), c2 = divur(2, c0);
+    GEN ec1 = mpexp(c1), ec2 = mpexp(c2), LChar = gel(vChar,j);
+    GEN vf0, vf1, cf0, cf1;
+    const long nChar = lg(LChar)-1, NN = N0[j];
+
+    if (DEBUGLEVEL>1)
+      err_printf("* conductor no %ld/%ld (N = %ld)\n\tInit: ", j,ncond,NN);
+    if (realprec(ec1) > prec) ec1 = rtor(ec1, prec);
+    if (realprec(ec2) > prec) ec2 = rtor(ec2, prec);
+    switch(cs[j])
+    {
+    case 1:
+      cf0 = gen_1;
+      cf1 = c0;
+      vf0 = mpveceint1(rtor(c1, prec), ec1, NN);
+      vf1 = mpvecpowdiv(invr(ec1), NN); break;
+
+    case 3:
+      cf0 = sqrtr(mppi(prec));
+      cf1 = gmul2n(cf0, 1);
+      cf0 = gmul(cf0, c0);
+      vf0 = mpvecpowdiv(invr(ec2), NN);
+      vf1 = mpveceint1(rtor(c2, prec), ec2, NN); break;
+
+    default:
+      cf0 = cf1 = NULL; /* FIXME: not implemented */
+      vf0 = vf1 = NULL;
+    }
+    for (k = 1; k <= nChar; k++)
+    {
+      const long t = LChar[k], d = degs[t];
+      const GEN dtcr = gel(dataCR, t), z = gel(ch_CHI(dtcr), 2);
+      GEN p1 = gen_0, p2 = gen_0;
+      int **matan;
+      long c = 0;
+
+      if (DEBUGLEVEL>1)
+        err_printf("\tcharacter no: %ld (%ld/%ld)\n", t,k,nChar);
+      if (isintzero( ch_comp(gel(dataCR, t)) ))
+      {
+        if (DEBUGLEVEL>1) err_printf("\t  no need to compute this character\n");
+        continue;
+      }
+      av2 = avma;
+      matan = computean(gel(dataCR,t), &LIST, NN, d);
+      for (n = 1; n <= NN; n++)
+        if ((an = EvalCoeff(z, matan[n], d)))
+        {
+          p1 = gadd(p1, gmul(an, gel(vf0,n)));
+          p2 = gadd(p2, gmul(an, gel(vf1,n)));
+          if (++c == 256) { gerepileall(av2,2, &p1,&p2); c = 0; }
+        }
+      gaffect(gmul(cf0, p1), gel(S,t));
+      gaffect(gmul(cf1,  gconj(p2)), gel(T,t));
+      FreeMat(matan,NN); avma = av2;
+    }
+    if (DEBUGLEVEL>1) err_printf("\n");
+    avma = av1;
+  }
+  avma = av;
+}
+
+/* s += t*u. All 3 of them t_REAL, except we allow s or u = NULL (for 0) */
+static GEN
+_addmulrr(GEN s, GEN t, GEN u)
+{
+  if (u)
+  {
+    GEN v = mulrr(t, u);
+    return s? addrr(s, v): v;
+  }
+  return s;
+}
+/* s += t. Both real, except we allow s or t = NULL (for exact 0) */
+static GEN
+_addrr(GEN s, GEN t)
+{ return t? (s? addrr(s, t): t) : s; }
+
+/* S & T for the general case. This is time-critical: optimize */
+static void
+get_cS_cT(ST_t *T, long n)
+{
+  pari_sp av;
+  GEN csurn, nsurc, lncsurn, A, B, s, t, Z, aij, bij;
+  long i, j, r, i0;
+
+  if (T->cS[n]) return;
+
+  av = avma;
+  aij = T->aij; i0= T->i0;
+  bij = T->bij; r = T->r;
+  Z = cgetg(r+1, t_VEC);
+  gel(Z,1) = NULL; /* unused */
+
+  csurn = divru(T->c1, n);
+  nsurc = invr(csurn);
+  lncsurn = logr_abs(csurn);
+
+  if (r > 1)
+  {
+    gel(Z,2) = lncsurn; /* r >= 2 */
+    for (i = 3; i <= r; i++)
+      gel(Z,i) = divru(mulrr(gel(Z,i-1), lncsurn), i-1);
+    /* Z[i] = ln^(i-1)(c1/n) / (i-1)! */
+  }
+
+  /* i = i0 */
+    A = gel(aij,i0); t = _addrr(NULL, gel(A,1));
+    B = gel(bij,i0); s = _addrr(NULL, gel(B,1));
+    for (j = 2; j <= r; j++)
+    {
+      s = _addmulrr(s, gel(Z,j),gel(B,j));
+      t = _addmulrr(t, gel(Z,j),gel(A,j));
+    }
+  for (i = i0 - 1; i > 1; i--)
+  {
+    A = gel(aij,i); if (t) t = mulrr(t, nsurc);
+    B = gel(bij,i); if (s) s = mulrr(s, nsurc);
+    for (j = odd(i)? T->rc2: T->rc1; j > 1; j--)
+    {
+      s = _addmulrr(s, gel(Z,j),gel(B,j));
+      t = _addmulrr(t, gel(Z,j),gel(A,j));
+    }
+    s = _addrr(s, gel(B,1));
+    t = _addrr(t, gel(A,1));
+  }
+  /* i = 1 */
+    A = gel(aij,1); if (t) t = mulrr(t, nsurc);
+    B = gel(bij,1); if (s) s = mulrr(s, nsurc);
+    s = _addrr(s, gel(B,1));
+    t = _addrr(t, gel(A,1));
+    for (j = 2; j <= r; j++)
+    {
+      s = _addmulrr(s, gel(Z,j),gel(B,j));
+      t = _addmulrr(t, gel(Z,j),gel(A,j));
+    }
+  s = _addrr(s, T->b? mulrr(csurn, gel(T->powracpi,T->b)): csurn);
+  if (!s) s = gen_0;
+  if (!t) t = gen_0;
+  gel(T->cS,n) = gclone(s);
+  gel(T->cT,n) = gclone(t); avma = av;
+}
+
+static void
+clear_cScT(ST_t *T, long N)
+{
+  GEN cS = T->cS, cT = T->cT;
+  long i;
+  for (i=1; i<=N; i++)
+    if (cS[i]) {
+      gunclone(gel(cS,i));
+      gunclone(gel(cT,i)); gel(cS,i) = gel(cT,i) = NULL;
+    }
+}
+
+static void
+init_cScT(ST_t *T, GEN dtcr, long N, long prec)
+{
+  GEN p1 = ch_4(dtcr);
+  T->a = p1[1];
+  T->b = p1[2];
+  T->c = p1[3];
+  T->rc1 = T->a + T->c;
+  T->rc2 = T->b + T->c;
+  T->r   = maxss(T->rc2+1, T->rc1); /* >= 2 */
+  ppgamma(T, prec);
+  clear_cScT(T, N);
+}
+
+static void
+GetST0(GEN bnr, GEN *pS, GEN *pT, GEN dataCR, GEN vChar, long prec)
+{
+  pari_sp av = avma, av1, av2;
+  long ncond, n, j, k, jc, n0, prec2, i0, r1, r2;
+  GEN nf = checknf(bnr), racpi, powracpi;
+  GEN N0, C, T = *pT, S = *pS, an, degs, limx;
+  LISTray LIST;
+  ST_t cScT;
+
+  /* initializations */
+  degs = GetDeg(dataCR);
+  ncond = lg(vChar)-1;
+  nf_get_sign(nf,&r1,&r2);
+
+  C  = cgetg(ncond+1, t_VEC);
+  N0 = cgetg(ncond+1, t_VECSMALL);
+  n0 = 0;
+  limx = zeta_get_limx(r1, r2, prec2nbits(prec));
+  for (j = 1; j <= ncond; j++)
+  {
+    GEN dtcr = gel(dataCR, mael(vChar,j,1)), c = ch_C(dtcr);
+    gel(C,j) = c;
+    N0[j] = zeta_get_N0(c, limx);
+    if (n0 < N0[j]) n0  = N0[j];
+  }
+  i0 = zeta_get_i0(r1, r2, prec2nbits(prec), limx);
+  InitPrimes(bnr, n0, &LIST);
+
+  prec2 = precdbl(prec) + EXTRA_PREC;
+  racpi = sqrtr(mppi(prec2));
+  powracpi = cgetg(r1+2,t_VEC);
+  gel(powracpi,1) = racpi;
+  for (j=2; j<=r1; j++) gel(powracpi,j) = mulrr(gel(powracpi,j-1), racpi);
+  cScT.powracpi = powracpi;
+
+  cScT.cS = cgetg(n0+1, t_VEC);
+  cScT.cT = cgetg(n0+1, t_VEC);
+  for (j=1; j<=n0; j++) gel(cScT.cS,j) = gel(cScT.cT,j) = NULL;
+
+  cScT.i0 = i0;
+
+  av1 = avma;
+  for (jc = 1; jc <= ncond; jc++)
+  {
+    const GEN LChar = gel(vChar,jc);
+    const long nChar = lg(LChar)-1, NN = N0[jc];
+
+    if (DEBUGLEVEL>1)
+      err_printf("* conductor no %ld/%ld (N = %ld)\n\tInit: ", jc,ncond,NN);
+
+    cScT.c1 = gel(C,jc);
+    init_cScT(&cScT, gel(dataCR, LChar[1]), NN, prec2);
+    av2 = avma;
+    for (k = 1; k <= nChar; k++)
+    {
+      const long t = LChar[k];
+      if (DEBUGLEVEL>1)
+        err_printf("\tcharacter no: %ld (%ld/%ld)\n", t,k,nChar);
+
+      if (!isintzero( ch_comp(gel(dataCR, t)) ))
+      {
+        const long d = degs[t];
+        const GEN dtcr = gel(dataCR, t), z = gel(ch_CHI(dtcr), 2);
+        GEN p1 = gen_0, p2 = gen_0;
+        long c = 0;
+        int **matan = ComputeCoeff(gel(dataCR,t), &LIST, NN, d);
+        for (n = 1; n <= NN; n++)
+          if ((an = EvalCoeff(z, matan[n], d)))
+          {
+           get_cS_cT(&cScT, n);
+           p1 = gadd(p1, gmul(an, gel(cScT.cS,n)));
+           p2 = gadd(p2, gmul(an, gel(cScT.cT,n)));
+           if (++c == 256) { gerepileall(av2,2, &p1,&p2); c = 0; }
+          }
+        gaffect(p1,        gel(S,t));
+        gaffect(gconj(p2), gel(T,t));
+        FreeMat(matan, NN); avma = av2;
+      }
+      else if (DEBUGLEVEL>1)
+        err_printf("\t  no need to compute this character\n");
+    }
+    if (DEBUGLEVEL>1) err_printf("\n");
+    avma = av1;
+  }
+  clear_cScT(&cScT, n0);
+  avma = av;
+}
+
+static void
+GetST(GEN bnr, GEN *pS, GEN *pT, GEN dataCR, GEN vChar, long prec)
+{
+  const long cl = lg(dataCR) - 1;
+  GEN S, T, nf  = checknf(bnr);
+  long j;
+
+  /* allocate memory for answer */
+  *pS = S = cgetg(cl+1, t_VEC);
+  *pT = T = cgetg(cl+1, t_VEC);
+  for (j = 1; j <= cl; j++)
+  {
+    gel(S,j) = cgetc(prec);
+    gel(T,j) = cgetc(prec);
+  }
+  if (nf_get_degree(nf) == 2)
+  {
+    QuadGetST(bnr, pS, pT, dataCR, vChar, prec);
+    return;
+  }
+  GetST0(bnr, pS, pT, dataCR, vChar, prec);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*     Class fields of real quadratic fields using Stark units     */
+/*                                                                 */
+/*******************************************************************/
+/* compute the Hilbert class field using genus class field theory when
+   the exponent of the class group is exactly 2 (trivial group not covered) */
+/* Cf Herz, Construction of class fields, LNM 21, Theorem 1 (VII-6) */
+static GEN
+GenusFieldQuadReal(GEN disc)
+{
+  long i, i0 = 0, l;
+  pari_sp av = avma;
+  GEN T = NULL, p0 = NULL, P;
+
+  P = gel(Z_factor(disc), 1);
+  l = lg(P);
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(P,i);
+    if (mod4(p) == 3) { p0 = p; i0 = i; break; }
+  }
+  l--; /* remove last prime */
+  if (i0 == l) l--; /* ... remove p0 and last prime */
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(P,i), d, t;
+    if (i == i0) continue;
+    if (equaliu(p, 2))
+      switch (mod32(disc))
+      {
+        case  8: d = gen_2; break;
+        case 24: d = shifti(p0, 1); break;
+        default: d = p0; break;
+      }
+    else
+      d = (mod4(p) == 1)? p: mulii(p0, p);
+    t = mkpoln(3, gen_1, gen_0, negi(d)); /* x^2 - d */
+    T = T? ZX_compositum_disjoint(T, t): t;
+  }
+  return gerepileupto(av, polredbest(T, 0));
+}
+static GEN
+GenusFieldQuadImag(GEN disc)
+{
+  long i, l;
+  pari_sp av = avma;
+  GEN T = NULL, P;
+
+  P = gel(absi_factor(disc), 1);
+  l = lg(P);
+  l--; /* remove last prime */
+  for (i = 1; i < l; i++)
+  {
+    GEN p = gel(P,i), d, t;
+    if (equaliu(p, 2))
+      switch (mod32(disc))
+      {
+        case 24: d = gen_2; break;  /* disc = 8 mod 32 */
+        case  8: d = gen_m2; break; /* disc =-8 mod 32 */
+        default: d = gen_m1; break;
+      }
+    else
+      d = (mod4(p) == 1)? p: negi(p);
+    t = mkpoln(3, gen_1, gen_0, negi(d)); /* x^2 - d */
+    T = T? ZX_compositum_disjoint(T, t): t;
+  }
+  return gerepileupto(av, polredbest(T, 0));
+}
+
+/* if flag != 0, computes a fast and crude approximation of the result */
+static GEN
+AllStark(GEN data,  GEN nf,  long flag,  long newprec)
+{
+  const long BND = 300;
+  long cl, i, j, cpt = 0, N, h, v, n, r1, r2, den;
+  pari_sp av, av2;
+  int **matan;
+  GEN bnr = gel(data,1), p1, p2, S, T, polrelnum, polrel, Lp, W, veczeta;
+  GEN vChar, degs, C, dataCR, cond1, L1, an;
+  LISTray LIST;
+  pari_timer ti;
+
+  nf_get_sign(nf, &r1,&r2);
+  N     = nf_get_degree(nf);
+  cond1 = gel(bnr_get_mod(bnr), 2);
+  dataCR = gel(data,5);
+  vChar = sortChars(dataCR);
+
+  v = 1;
+  while (gequal1(gel(cond1,v))) v++;
+
+  cl = lg(dataCR)-1;
+  degs = GetDeg(dataCR);
+  h  = itos(ZM_det_triangular(gel(data,2))) >> 1;
+
+LABDOUB:
+  if (DEBUGLEVEL) timer_start(&ti);
+  av = avma;
+
+  /* characters with rank > 1 should not be computed */
+  for (i = 1; i <= cl; i++)
+  {
+    GEN chi = gel(dataCR, i);
+    if (L_vanishes_at_0(chi)) ch_comp(chi) = gen_0;
+  }
+
+  W = ComputeAllArtinNumbers(dataCR, vChar, (flag >= 0), newprec);
+  if (DEBUGLEVEL) timer_printf(&ti,"Compute W");
+  Lp = cgetg(cl + 1, t_VEC);
+  if (!flag)
+  {
+    GetST(bnr, &S, &T, dataCR, vChar, newprec);
+    if (DEBUGLEVEL) timer_printf(&ti, "S&T");
+    for (i = 1; i <= cl; i++)
+    {
+      GEN chi = gel(dataCR, i), v = gen_0;
+      if (!isintzero( ch_comp(chi) ))
+        v = gel(GetValue(chi, gel(W,i), gel(S,i), gel(T,i), 2, newprec), 2);
+      gel(Lp, i) = v;
+    }
+  }
+  else
+  { /* compute a crude approximation of the result */
+    C = cgetg(cl + 1, t_VEC);
+    for (i = 1; i <= cl; i++) gel(C,i) = ch_C(gel(dataCR, i));
+    n = zeta_get_N0(vecmax(C), zeta_get_limx(r1, r2, prec2nbits(newprec)));
+    if (n > BND) n = BND;
+    if (DEBUGLEVEL) err_printf("N0 in QuickPol: %ld \n", n);
+    InitPrimes(bnr, n, &LIST);
+
+    L1 = cgetg(cl+1, t_VEC);
+    /* use L(1) = sum (an / n) */
+    for (i = 1; i <= cl; i++)
+    {
+      GEN dtcr = gel(dataCR,i);
+      matan = ComputeCoeff(dtcr, &LIST, n, degs[i]);
+      av2 = avma;
+      p1 = real_0(newprec); p2 = gel(ch_CHI(dtcr), 2);
+      for (j = 1; j <= n; j++)
+        if ( (an = EvalCoeff(p2, matan[j], degs[i])) )
+          p1 = gadd(p1, gdivgs(an, j));
+      gel(L1,i) = gerepileupto(av2, p1);
+      FreeMat(matan, n);
+    }
+    p1 = gmul2n(powruhalf(mppi(newprec), N-2), 1);
+
+    for (i = 1; i <= cl; i++)
+    {
+      long r;
+      GEN WW, A = ComputeAChi(gel(dataCR,i), &r, 0, newprec);
+      WW = gmul(gel(C,i), gmul(A, gel(W,i)));
+      gel(Lp,i) = gdiv(gmul(WW, gconj(gel(L1,i))), p1);
+    }
+  }
+
+  p1 = ComputeLift(gel(data,4));
+
+  den = flag ? h: 2*h;
+  veczeta = cgetg(h + 1, t_VEC);
+  for (i = 1; i <= h; i++)
+  {
+    GEN z = gen_0, sig = gel(p1,i);
+    for (j = 1; j <= cl; j++)
+    {
+      GEN dtcr = gel(dataCR,j), CHI = ch_CHI(dtcr);
+      GEN t = mulreal(gel(Lp,j), ComputeImagebyChar(CHI, sig));
+      if (itos(gel(CHI,3)) != 2) t = gmul2n(t, 1); /* character not real */
+      z = gadd(z, t);
+    }
+    gel(veczeta,i) = gdivgs(z, den);
+  }
+  for (j = 1; j <= h; j++)
+    gel(veczeta,j) = gmul2n(gcosh(gel(veczeta,j), newprec), 1);
+  polrelnum = roots_to_pol(veczeta, 0);
+  if (DEBUGLEVEL)
+  {
+    if (DEBUGLEVEL>1) {
+      err_printf("polrelnum = %Ps\n", polrelnum);
+      err_printf("zetavalues = %Ps\n", veczeta);
+      if (!flag)
+        err_printf("Checking the square-root of the Stark unit...\n");
+    }
+    timer_printf(&ti, "Compute %s", flag? "quickpol": "polrelnum");
+  }
+
+  if (flag)
+    return gerepilecopy(av, polrelnum);
+
+  /* try to recognize this polynomial */
+  polrel = RecCoeff(nf, polrelnum, v, newprec);
+  if (!polrel)
+  {
+    for (j = 1; j <= h; j++)
+      gel(veczeta,j) = gsubgs(gsqr(gel(veczeta,j)), 2);
+    polrelnum = roots_to_pol(veczeta, 0);
+    if (DEBUGLEVEL)
+    {
+      if (DEBUGLEVEL>1) {
+        err_printf("It's not a square...\n");
+        err_printf("polrelnum = %Ps\n", polrelnum);
+      }
+      timer_printf(&ti, "Compute polrelnum");
+    }
+    polrel = RecCoeff(nf, polrelnum, v, newprec);
+  }
+  if (!polrel) /* FAILED */
+  {
+    long incr_pr;
+    if (++cpt >= 3) pari_err_PREC( "stark (computation impossible)");
+
+    /* compute the precision, we need
+          a) get at least EXTRA_PREC fractional digits if there is none;
+       or b) double the fractional digits.
+    */
+    incr_pr = nbits2extraprec( prec2nbits(gprecision(polrelnum))- gexpo(polrelnum) );
+    if (incr_pr < 0) incr_pr = -incr_pr + EXTRA_PREC;
+    newprec = newprec + maxss(ADD_PREC, cpt*incr_pr);
+    if (DEBUGLEVEL) pari_warn(warnprec, "AllStark", newprec);
+
+    nf = nfnewprec_shallow(nf, newprec);
+    dataCR = CharNewPrec(dataCR, nf, newprec);
+
+    gerepileall(av, 2, &nf, &dataCR);
+    goto LABDOUB;
+  }
+
+  if (DEBUGLEVEL) {
+    if (DEBUGLEVEL>1) err_printf("polrel = %Ps\n", polrel);
+    timer_printf(&ti, "Recpolnum");
+  }
+  return gerepilecopy(av, polrel);
+}
+
+/********************************************************************/
+/*                        Main functions                            */
+/********************************************************************/
+
+static GEN
+get_subgroup(GEN H, GEN cyc, const char *s)
+{
+  if (!H || gequal0(H)) return diagonal_shallow(cyc);
+  if (typ(H) != t_MAT) pari_err_TYPE(stack_strcat(s," [subgroup]"), H);
+  RgM_check_ZM(H, s);
+  return ZM_hnfmodid(H, cyc);
+}
+
+GEN
+bnrstark(GEN bnr, GEN subgrp, long prec)
+{
+  long N, newprec;
+  pari_sp av = avma;
+  GEN bnf, p1, cycbnr, nf, data, dtQ;
+
+  /* check the bnr */
+  checkbnr(bnr);
+  bnf = checkbnf(bnr);
+  nf  = bnf_get_nf(bnf);
+  N   = nf_get_degree(nf);
+  if (N == 1) return galoissubcyclo(bnr, subgrp, 0, 0);
+
+  /* check the bnf */
+  if (!nf_get_varn(nf))
+    pari_err_PRIORITY("bnrstark", nf_get_pol(nf), "=", 0);
+  if (nf_get_r2(nf)) pari_err_DOMAIN("bnrstark", "r2", "!=", gen_0, nf);
+  subgrp = get_subgroup(subgrp,bnr_get_cyc(bnr),"bnrstark");
+
+  /* compute bnr(conductor) */
+  p1     = bnrconductor(bnr, subgrp, 2);
+  bnr    = gel(p1,2); cycbnr = bnr_get_cyc(bnr);
+  subgrp = gel(p1,3);
+  if (gequal1( ZM_det_triangular(subgrp) )) { avma = av; return pol_x(0); }
+
+  /* check the class field */
+  if (!gequal0(gel(bnr_get_mod(bnr), 2)))
+    pari_err_DOMAIN("bnrstark", "r2(class field)", "!=", gen_0, bnr);
+
+  /* find a suitable extension N */
+  dtQ = InitQuotient(subgrp);
+  data  = FindModulus(bnr, dtQ, &newprec);
+  if (!data)
+  {
+    GEN vec, H, cyc = gel(dtQ,2), U = gel(dtQ,3), M = RgM_inv(U);
+    long i, j = 1, l = lg(M);
+
+    /* M = indep. generators of Cl_f/subgp, restrict to cyclic components */
+    vec = cgetg(l, t_VEC);
+    for (i = 1; i < l; i++)
+    {
+      if (is_pm1(gel(cyc,i))) continue;
+      H = ZM_hnfmodid(vecsplice(M,i), cycbnr);
+      gel(vec,j++) = bnrstark(bnr, H, prec);
+    }
+    setlg(vec, j); return gerepilecopy(av, vec);
+  }
+
+  if (newprec > prec)
+  {
+    if (DEBUGLEVEL>1) err_printf("new precision: %ld\n", newprec);
+    nf = nfnewprec_shallow(nf, newprec);
+  }
+  return gerepileupto(av, AllStark(data, nf, 0, newprec));
+}
+
+/* For each character of Cl(bnr)/subgp, compute L(1, chi) (or equivalently
+ * the first non-zero term c(chi) of the expansion at s = 0).
+ * If flag & 1: compute the value at s = 1 (for non-trivial characters),
+ * else compute the term c(chi) and return [r(chi), c(chi)] where r(chi) is
+ *   the order of L(s, chi) at s = 0.
+ * If flag & 2: compute the value of the L-function L_S(s, chi) where S is the
+ *   set of places dividing the modulus of bnr (and the infinite places),
+ * else
+ *   compute the value of the primitive L-function associated to chi,
+ * If flag & 4: return also the character */
+GEN
+bnrL1(GEN bnr, GEN subgp, long flag, long prec)
+{
+  GEN cyc, L1, allCR, listCR;
+  GEN indCR, invCR, Qt;
+  long cl, i, nc;
+  pari_sp av = avma;
+
+  checkbnr(bnr);
+  if (flag < 0 || flag > 8) pari_err_FLAG("bnrL1");
+
+  /* compute bnr(conductor) */
+  if (!(flag & 2)) bnr = gel(bnrconductor(bnr, NULL, 2),2);
+  cyc  = bnr_get_cyc(bnr);
+  subgp = get_subgroup(subgp, cyc, "bnrL1");
+
+  cl = itou( ZM_det_triangular(subgp) );
+  Qt = InitQuotient(subgp);
+
+  /* compute all characters */
+  allCR = EltsOfGroup(cl, gel(Qt,2));
+
+  /* make a list of all non-trivial characters modulo conjugation */
+  listCR = cgetg(cl, t_VEC);
+  indCR = new_chunk(cl);
+  invCR = new_chunk(cl); nc = 0;
+  for (i = 1; i < cl; i++)
+  {
+    /* lift to a character on Cl(bnr) */
+    GEN lchi = LiftChar(cyc, gel(Qt,3), gel(allCR,i), gel(Qt,2));
+    GEN clchi = ConjChar(lchi, cyc);
+    long j, a = i;
+    for (j = 1; j <= nc; j++)
+      if (ZV_equal(gmael(listCR, j, 1), clchi)) { a = -j; break; }
+
+    if (a > 0)
+    {
+      nc++;
+      gel(listCR,nc) = mkvec2(lchi, bnrconductorofchar(bnr, lchi));
+      indCR[i]  = nc;
+      invCR[nc] = i;
+    }
+    else
+      indCR[i] = -invCR[-a];
+
+    gel(allCR,i) = lchi;
+  }
+  settyp(allCR[cl], t_VEC); /* set correct type for trivial character */
+
+  setlg(listCR, nc + 1);
+  L1 = cgetg((flag&1)? cl: cl+1, t_VEC);
+  if (nc)
+  {
+    GEN dataCR = InitChar(bnr, listCR, prec);
+    GEN W, S, T, vChar = sortChars(dataCR);
+    GetST(bnr, &S, &T, dataCR, vChar, prec);
+    W = ComputeAllArtinNumbers(dataCR, vChar, 1, prec);
+    for (i = 1; i < cl; i++)
+    {
+      long a = indCR[i];
+      if (a > 0)
+        gel(L1,i) = GetValue(gel(dataCR,a), gel(W,a), gel(S,a), gel(T,a),
+                             flag, prec);
+      else
+        gel(L1,i) = gconj(gel(L1,-a));
+    }
+  }
+  if (!(flag & 1))
+    gel(L1,cl) = GetValue1(bnr, flag & 2, prec);
+  else
+    cl--;
+
+  if (flag & 4) {
+    for (i = 1; i <= cl; i++) gel(L1,i) = mkvec2(gel(allCR,i), gel(L1,i));
+  }
+  return gerepilecopy(av, L1);
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*       Hilbert and Ray Class field using Stark                   */
+/*                                                                 */
+/*******************************************************************/
+/* P in A[x,y], deg_y P < 2, return P0 and P1 in A[x] such that P = P0 + P1 y */
+static void
+split_pol_quad(GEN P, GEN *gP0, GEN *gP1)
+{
+  long i, l = lg(P);
+  GEN P0 = cgetg(l, t_POL), P1 = cgetg(l, t_POL);
+  P0[1] = P1[1] = P[1];
+  for (i = 2; i < l; i++)
+  {
+    GEN c = gel(P,i), c0 = c, c1 = gen_0;
+    if (typ(c) == t_POL) /* write c = c1 y + c0 */
+      switch(degpol(c))
+      {
+        case -1: c0 = gen_0; break;
+        default: c1 = gel(c,3); /* fall through */
+        case  0: c0 = gel(c,2); break;
+      }
+    gel(P0,i) = c0; gel(P1,i) = c1;
+  }
+  *gP0 = normalizepol_lg(P0, l);
+  *gP1 = normalizepol_lg(P1, l);
+}
+
+/* k = nf quadratic field, P relative equation of H_k (Hilbert class field)
+ * return T in Z[X], such that H_k / Q is the compositum of Q[X]/(T) and k */
+static GEN
+makescind(GEN nf, GEN P)
+{
+  GEN Pp, p, pol, G, L, a, roo, P0,P1, Ny,Try, nfpol = nf_get_pol(nf);
+  long i, is_P;
+
+  split_pol_quad(lift_intern(P), &P0, &P1);
+  /* P = P0 + y P1, Norm_{k/Q}(P) = P0^2 + Tr y P0P1 + Ny P1^2, irreducible/Q */
+  Ny = gel(nfpol, 2);
+  Try = negi(gel(nfpol, 3));
+  pol = RgX_add(RgX_sqr(P0), RgX_Rg_mul(RgX_sqr(P1), Ny));
+  if (signe(Try)) pol = RgX_add(pol, RgX_Rg_mul(RgX_mul(P0,P1), Try));
+  /* pol = rnfequation(nf, P); */
+  G = galoisinit(pol, NULL);
+  L = gal_get_group(G);
+  p = gal_get_p(G);
+  a = FpX_quad_root(nfpol, p, 0);
+  /* P mod a prime \wp above p (which splits) */
+  Pp = FpXY_evalx(P, a, p);
+  roo = gal_get_roots(G);
+  is_P = gequal0( FpX_eval(Pp, remii(gel(roo,1),p), p) );
+  /* each roo[i] mod p is a root of P or (exclusive) tau(P) mod \wp */
+  /* record whether roo[1] is a root of P or tau(P) */
+
+  for (i = 1; i < lg(L); i++)
+  {
+    GEN perm = gel(L,i);
+    long k = perm[1]; if (k == 1) continue;
+    k = gequal0( FpX_eval(Pp, remii(gel(roo,k),p), p) );
+    /* roo[k] is a root of the other polynomial */
+    if (k != is_P)
+    {
+      long o = perm_order(perm);
+      if (o != 2) perm = perm_pow(perm, o >> 1);
+      /* perm has order two and doesn't belong to Gal(H_k/k) */
+      return galoisfixedfield(G, perm, 1, varn(P));
+    }
+  }
+  pari_err_BUG("makescind");
+  return NULL; /*not reached*/
+}
+
+/* pbnf = NULL if no bnf is needed, f = NULL may be passed for a trivial
+ * conductor */
+static void
+quadray_init(GEN *pD, GEN f, GEN *pbnf, long prec)
+{
+  GEN D = *pD, nf, bnf = NULL;
+  if (typ(D) == t_INT)
+  {
+    int isfund;
+    if (pbnf) {
+      long v = f? gvar(f): NO_VARIABLE;
+      if (v == NO_VARIABLE) v = fetch_user_var("y");
+      bnf = Buchall(quadpoly0(D, v), nf_FORCE, prec);
+      nf = bnf_get_nf(bnf);
+      isfund = equalii(D, nf_get_disc(nf));
+    }
+    else
+      isfund = Z_isfundamental(D);
+    if (!isfund) pari_err_DOMAIN("quadray", "isfundamental(D)", "=",gen_0, D);
+  }
+  else
+  {
+    bnf = checkbnf(D);
+    nf = bnf_get_nf(bnf);
+    if (nf_get_degree(nf) != 2)
+      pari_err_DOMAIN("quadray", "degree", "!=", gen_2, nf_get_pol(nf));
+    D = nf_get_disc(nf);
+  }
+  if (pbnf) *pbnf = bnf;
+  *pD = D;
+}
+
+/* compute the polynomial over Q of the Hilbert class field of
+   Q(sqrt(D)) where D is a positive fundamental discriminant */
+static GEN
+quadhilbertreal(GEN D, long prec)
+{
+  pari_sp av = avma;
+  long newprec;
+  GEN bnf;
+  VOLATILE GEN bnr, dtQ, data, nf, cyc, M;
+  pari_timer ti;
+  if (DEBUGLEVEL) timer_start(&ti);
+
+  (void)≺ /* prevent longjmp clobbering it */
+  (void)&bnf;  /* prevent longjmp clobbering it, avoid warning due to
+                * quadray_init call : discards qualifiers from pointer type */
+  quadray_init(&D, NULL, &bnf, prec);
+  cyc = bnf_get_cyc(bnf);
+  if (lg(cyc) == 1) { avma = av; return pol_x(0); }
+  /* if the exponent of the class group is 2, use Genus Theory */
+  if (equaliu(gel(cyc,1), 2)) return gerepileupto(av, GenusFieldQuadReal(D));
+
+  bnr  = Buchray(bnf, gen_1, nf_INIT|nf_GEN);
+  M = diagonal_shallow(bnr_get_cyc(bnr));
+  dtQ = InitQuotient(M);
+  nf  = bnf_get_nf(bnf);
+
+  for(;;) {
+    VOLATILE GEN pol = NULL;
+    pari_CATCH(e_PREC) {
+      prec += EXTRA_PREC;
+      if (DEBUGLEVEL) pari_warn(warnprec, "quadhilbertreal", prec);
+      bnr = bnrnewprec_shallow(bnr, prec);
+      bnf = bnr_get_bnf(bnr);
+      nf  = bnf_get_nf(bnf);
+    } pari_TRY {
+      /* find the modulus defining N */
+      pari_timer T;
+      if (DEBUGLEVEL) timer_start(&T);
+      data = FindModulus(bnr, dtQ, &newprec);
+      if (DEBUGLEVEL) timer_printf(&T,"FindModulus");
+      if (!data)
+      {
+        long i, l = lg(M);
+        GEN vec = cgetg(l, t_VEC);
+        for (i = 1; i < l; i++)
+        {
+          GEN t = gcoeff(M,i,i);
+          gcoeff(M,i,i) = gen_1;
+          gel(vec,i) = bnrstark(bnr, M, prec);
+          gcoeff(M,i,i) = t;
+        }
+        return gerepileupto(av, vec);
+      }
+
+      if (newprec > prec)
+      {
+        if (DEBUGLEVEL>1) err_printf("new precision: %ld\n", newprec);
+        nf = nfnewprec_shallow(nf, newprec);
+      }
+      pol = AllStark(data, nf, 0, newprec);
+    } pari_ENDCATCH;
+    if (pol) {
+      pol = makescind(nf, pol);
+      return gerepileupto(av, polredbest(pol, 0));
+    }
+  }
+}
+
+/*******************************************************************/
+/*                                                                 */
+/*       Hilbert and Ray Class field using CM (Schertz)            */
+/*                                                                 */
+/*******************************************************************/
+/* form^2 = 1 ? */
+static int
+hasexp2(GEN form)
+{
+  GEN a = gel(form,1), b = gel(form,2), c = gel(form,3);
+  return !signe(b) || absi_equal(a,b) || equalii(a,c);
+}
+static int
+uhasexp2(GEN form)
+{
+  long a = form[1], b = form[2], c = form[3];
+  return !b || a == labs(b) || a == c;
+}
+
+GEN
+qfbforms(GEN D)
+{
+  ulong d = itou(D), dover3 = d/3, t, b2, a, b, c, h;
+  GEN L = cgetg((long)(sqrt(d) * log2(d)), t_VEC);
+  b2 = b = (d&1); h = 0;
+  if (!b) /* b = 0 treated separately to avoid special cases */
+  {
+    t = d >> 2; /* (b^2 - D) / 4*/
+    for (a=1; a*a<=t; a++)
+      if (c = t/a, t == c*a) gel(L,++h) = mkvecsmall3(a,0,c);
+    b = 2; b2 = 4;
+  }
+  /* now b > 0, b = D (mod 2) */
+  for ( ; b2 <= dover3; b += 2, b2 = b*b)
+  {
+    t = (b2 + d) >> 2; /* (b^2 - D) / 4*/
+    /* b = a */
+    if (c = t/b, t == c*b) gel(L,++h) = mkvecsmall3(b,b,c);
+    /* b < a < c */
+    for (a = b+1; a*a < t; a++)
+      if (c = t/a, t == c*a)
+      {
+        gel(L,++h) = mkvecsmall3(a, b,c);
+        gel(L,++h) = mkvecsmall3(a,-b,c);
+      }
+    /* a = c */
+    if (a * a == t) gel(L,++h) = mkvecsmall3(a,b,a);
+  }
+  setlg(L,h+1); return L;
+}
+
+/* gcd(n, 24) */
+static long
+GCD24(long n)
+{
+  switch(n % 24)
+  {
+    case 0: return 24;
+    case 1: return 1;
+    case 2: return 2;
+    case 3: return 3;
+    case 4: return 4;
+    case 5: return 1;
+    case 6: return 6;
+    case 7: return 1;
+    case 8: return 8;
+    case 9: return 3;
+    case 10: return 2;
+    case 11: return 1;
+    case 12: return 12;
+    case 13: return 1;
+    case 14: return 2;
+    case 15: return 3;
+    case 16: return 8;
+    case 17: return 1;
+    case 18: return 6;
+    case 19: return 1;
+    case 20: return 4;
+    case 21: return 3;
+    case 22: return 2;
+    case 23: return 1;
+    default: return 0;
+  }
+}
+
+struct gpq_data {
+  long p, q;
+  GEN sqd; /* sqrt(D), t_REAL */
+  GEN u, D;
+  GEN pq, pq2; /* p*q, 2*p*q */
+  GEN qfpq ; /* class of \P * \Q */
+};
+
+/* find P and Q two non principal prime ideals (above p <= q) such that
+ *   cl(P) = cl(Q) if P,Q have order 2 in Cl(K).
+ *   Ensure that e = 24 / gcd(24, (p-1)(q-1)) = 1 */
+/* D t_INT, discriminant */
+static void
+init_pq(GEN D, struct gpq_data *T)
+{
+  const long Np = 6547; /* N.B. primepi(50000) = 5133 */
+  const ulong maxq = 50000;
+  GEN listp = cgetg(Np + 1, t_VECSMALL); /* primes p */
+  GEN listP = cgetg(Np + 1, t_VEC); /* primeform(p) if of order 2, else NULL */
+  GEN gcd24 = cgetg(Np + 1, t_VECSMALL); /* gcd(p-1, 24) */
+  forprime_t S;
+  long l = 1;
+  double best = 0.;
+  ulong q;
+
+  u_forprime_init(&S, 2, ULONG_MAX);
+  T->D = D;
+  T->p = T->q = 0;
+  for(;;)
+  {
+    GEN Q;
+    long i, gcdq, mod;
+    int order2, store;
+    double t;
+
+    q = u_forprime_next(&S);
+    if (best > 0 && q >= maxq)
+    {
+      if (DEBUGLEVEL)
+        pari_warn(warner,"possibly suboptimal (p,q) for D = %Ps", D);
+      break;
+    }
+    if (kroiu(D, q) < 0) continue; /* inert */
+    Q = redimag(primeform_u(D, q));
+    if (is_pm1(gel(Q,1))) continue; /* Q | q is principal */
+
+    store = 1;
+    order2 = hasexp2(Q);
+    gcd24[l] = gcdq = GCD24(q-1);
+    mod = 24 / gcdq; /* mod must divide p-1 otherwise e > 1 */
+    listp[l] = q;
+    gel(listP,l) = order2 ? Q : NULL;
+    t = (q+1)/(double)(q-1);
+    for (i = 1; i < l; i++) /* try all (p, q), p < q in listp */
+    {
+      long p = listp[i], gcdp = gcd24[i];
+      double b;
+      /* P,Q order 2 => cl(Q) = cl(P) */
+      if (order2 && gel(listP,i) && !gequal(gel(listP,i), Q)) continue;
+      if (gcdp % gcdq == 0) store = 0; /* already a better one in the list */
+      if ((p-1) % mod) continue;
+
+      b = (t*(p+1)) / (p-1); /* (p+1)(q+1) / (p-1)(q-1) */
+      if (b > best) {
+        store = 0; /* (p,q) always better than (q,r) for r >= q */
+        best = b; T->q = q; T->p = p;
+        if (DEBUGLEVEL>2) err_printf("p,q = %ld,%ld\n", p, q);
+      }
+      /* won't improve with this q as largest member */
+      if (best > 0) break;
+    }
+    /* if !store or (q,r) won't improve on current best pair, forget that q */
+    if (store && t*t > best)
+      if (++l >= Np) pari_err_BUG("quadhilbert (not enough primes)");
+    if (!best) /* (p,q) with p < q always better than (q,q) */
+    { /* try (q,q) */
+      if (gcdq >= 12 && umodiu(D, q)) /* e = 1 and unramified */
+      {
+        double b = (t*q) / (q-1); /* q(q+1) / (q-1)^2 */
+        if (b > best) {
+          best = b; T->q = T->p = q;
+          if (DEBUGLEVEL>2) err_printf("p,q = %ld,%ld\n", q, q);
+        }
+      }
+    }
+    /* If (p1+1)(q+1) / (p1-1)(q-1) <= best, we can no longer improve
+     * even with best p : stop */
+    if ((listp[1]+1)*t <= (listp[1]-1)*best) break;
+  }
+  if (DEBUGLEVEL>1)
+    err_printf("(p, q) = %ld, %ld; gain = %f\n", T->p, T->q, 12*best);
+}
+
+static GEN
+gpq(GEN form, struct gpq_data *T)
+{
+  pari_sp av = avma;
+  long a = form[1], b = form[2], c = form[3];
+  long p = T->p, q = T->q;
+  GEN form2, w, z;
+  int fl, real = 0;
+
+  form2 = qficomp(T->qfpq, mkvec3s(a, -b, c));
+  /* form2 and form yield complex conjugate roots : only compute for the
+   * lexicographically smallest of the 2 */
+  fl = cmpis(gel(form2,1), a);
+  if (fl <= 0)
+  {
+    if (fl < 0) return NULL;
+    fl = cmpis(gel(form2,2), b);
+    if (fl <= 0)
+    {
+      if (fl < 0) return NULL;
+      /* form == form2 : real root */
+      real = 1;
+    }
+  }
+
+  if (p == 2) { /* (a,b,c) = (1,1,0) mod 2 ? */
+    if (a % q == 0 && (a & b & 1) && !(c & 1))
+    { /* apply S : make sure that (a,b,c) represents odd values */
+      lswap(a,c); b = -b;
+    }
+  }
+  if (a % p == 0 || a % q == 0)
+  { /* apply T^k, look for c' = a k^2 + b k + c coprime to N */
+    while (c % p == 0 || c % q == 0)
+    {
+      c += a + b;
+      b += a << 1;
+    }
+    lswap(a, c); b = -b; /* apply S */
+  }
+  /* now (a,b,c) ~ form and (a,pq) = 1 */
+
+  /* gcd(2a, u) = 2,  w = u mod 2pq, -b mod 2a */
+  w = Z_chinese(T->u, stoi(-b), T->pq2, utoipos(a << 1));
+  z = double_eta_quotient(utoipos(a), w, T->D, T->p, T->q, T->pq, T->sqd);
+  if (real && typ(z) == t_COMPLEX) z = gcopy(gel(z, 1));
+  return gerepileupto(av, z);
+}
+
+/* returns an equation for the Hilbert class field of Q(sqrt(D)), D < 0
+ * fundamental discriminant */
+static GEN
+quadhilbertimag(GEN D)
+{
+  GEN L, P, Pi, Pr, qfp, u;
+  pari_sp av = avma;
+  long h, i, prec;
+  struct gpq_data T;
+  pari_timer ti;
+
+  if (DEBUGLEVEL>1) timer_start(&ti);
+  if (lgefint(D) == 3)
+    switch (D[2]) { /* = |D|; special cases where e > 1 */
+      case 3:
+      case 4:
+      case 7:
+      case 8:
+      case 11:
+      case 19:
+      case 43:
+      case 67:
+      case 163: return pol_x(0);
+    }
+  L = qfbforms(D);
+  h = lg(L)-1;
+  if ((1L << vals(h)) == h) /* power of 2 */
+  { /* check whether > |Cl|/2 elements have order <= 2 ==> 2-elementary */
+    long lim = (h>>1) + 1;
+    for (i=1; i <= lim; i++)
+      if (!uhasexp2(gel(L,i))) break;
+    if (i > lim) return GenusFieldQuadImag(D);
+  }
+  if (DEBUGLEVEL>1) timer_printf(&ti,"class number = %ld",h);
+  init_pq(D, &T);
+  qfp = primeform_u(D, T.p);
+  T.pq =  muluu(T.p, T.q);
+  T.pq2 = shifti(T.pq,1);
+  if (T.p == T.q)
+  {
+    GEN qfbp2 = qficompraw(qfp, qfp);
+    u = gel(qfbp2,2);
+    T.u = modii(u, T.pq2);
+    T.qfpq = redimag(qfbp2);
+  }
+  else
+  {
+    GEN qfq = primeform_u(D, T.q), bp = gel(qfp,2), bq = gel(qfq,2);
+    T.u = Z_chinese(bp, bq, utoipos(T.p << 1), utoipos(T.q << 1));
+    /* T.u = bp (mod 2p), T.u = bq (mod 2q) */
+    T.qfpq = qficomp(qfp, qfq);
+  }
+  /* u modulo 2pq */
+  prec = LOWDEFAULTPREC;
+  Pr = cgetg(h+1,t_VEC);
+  Pi = cgetg(h+1,t_VEC);
+  for(;;)
+  {
+    long ex, exmax = 0, r1 = 0, r2 = 0;
+    pari_sp av0 = avma;
+    T.sqd = sqrtr_abs(itor(D, prec));
+    for (i=1; i<=h; i++)
+    {
+      GEN s = gpq(gel(L,i), &T);
+      if (DEBUGLEVEL>3) err_printf("%ld ", i);
+      if (!s) continue;
+      if (typ(s) != t_COMPLEX) gel(Pr, ++r1) = s; /* real root */
+      else                     gel(Pi, ++r2) = s;
+      ex = gexpo(s); if (ex > 0) exmax += ex;
+    }
+    if (DEBUGLEVEL>1) timer_printf(&ti,"roots");
+    setlg(Pr, r1+1);
+    setlg(Pi, r2+1);
+    P = roots_to_pol_r1(shallowconcat(Pr,Pi), 0, r1);
+    P = grndtoi(P,&exmax);
+    if (DEBUGLEVEL>1) timer_printf(&ti,"product, error bits = %ld",exmax);
+    if (exmax <= -10) break;
+    avma = av0; prec += (DEFAULTPREC-2) + nbits2extraprec(exmax);
+    if (DEBUGLEVEL) pari_warn(warnprec,"quadhilbertimag",prec);
+  }
+  return gerepileupto(av,P);
+}
+
+GEN
+quadhilbert(GEN D, long prec)
+{
+  GEN d = D;
+  quadray_init(&d, NULL, NULL, 0);
+  return (signe(d)>0)? quadhilbertreal(D,prec)
+                     : quadhilbertimag(d);
+}
+
+/* return a vector of all roots of 1 in bnf [not necessarily quadratic] */
+static GEN
+getallrootsof1(GEN bnf)
+{
+  GEN T, u, nf = bnf_get_nf(bnf), tu;
+  long i, n = bnf_get_tuN(bnf);
+
+  if (n == 2) {
+    long N = nf_get_degree(nf);
+    return mkvec2(scalarcol_shallow(gen_m1, N),
+                  scalarcol_shallow(gen_1, N));
+  }
+  tu = poltobasis(nf, bnf_get_tuU(bnf));
+  T = zk_multable(nf, tu);
+  u = cgetg(n+1, t_VEC); gel(u,1) = tu;
+  for (i=2; i <= n; i++) gel(u,i) = ZM_ZC_mul(T, gel(u,i-1));
+  return u;
+}
+/* assume bnr has the right conductor */
+static GEN
+get_lambda(GEN bnr)
+{
+  GEN bnf = bnr_get_bnf(bnr), nf = bnf_get_nf(bnf), pol = nf_get_pol(nf);
+  GEN f = gel(bnr_get_mod(bnr), 1), labas, lamodf, u;
+  long a, b, f2, i, lu, v = varn(pol);
+
+  f2 = 2 * itos(gcoeff(f,1,1));
+  u = getallrootsof1(bnf); lu = lg(u);
+  for (i=1; i<lu; i++)
+    gel(u,i) = ZC_hnfrem(gel(u,i), f); /* roots of 1, mod f */
+  if (DEBUGLEVEL>1)
+    err_printf("quadray: looking for [a,b] != unit mod 2f\n[a,b] = ");
+  for (a=0; a<f2; a++)
+    for (b=0; b<f2; b++)
+    {
+      GEN la = deg1pol_shallow(stoi(a), stoi(b), v); /* ax + b */
+      if (umodiu(gnorm(mkpolmod(la, pol)), f2) != 1) continue;
+      if (DEBUGLEVEL>1) err_printf("[%ld,%ld] ",a,b);
+
+      labas = poltobasis(nf, la);
+      lamodf = ZC_hnfrem(labas, f);
+      for (i=1; i<lu; i++)
+        if (ZV_equal(lamodf, gel(u,i))) break;
+      if (i < lu) continue; /* la = unit mod f */
+      if (DEBUGLEVEL)
+      {
+        if (DEBUGLEVEL>1) err_printf("\n");
+        err_printf("lambda = %Ps\n",la);
+      }
+      return labas;
+    }
+  pari_err_BUG("get_lambda");
+  return NULL;
+}
+
+static GEN
+to_approx(GEN nf, GEN a)
+{
+  GEN M = nf_get_M(nf);
+  return gadd(gel(a,1), gmul(gcoeff(M,1,2),gel(a,2)));
+}
+/* Z-basis for a (over C) */
+static GEN
+get_om(GEN nf, GEN a) {
+  return mkvec2(to_approx(nf,gel(a,2)),
+                to_approx(nf,gel(a,1)));
+}
+
+/* Compute all elts in class group G = [|G|,c,g], c=cyclic factors, g=gens.
+ * Set list[j + 1] = g1^e1...gk^ek where j is the integer
+ *   ek + ck [ e(k-1) + c(k-1) [... + c2 [e1]]...] */
+static GEN
+getallelts(GEN bnr)
+{
+  GEN nf, C, c, g, list, pows, gk;
+  long lc, i, j, no;
+
+  nf = bnr_get_nf(bnr);
+  no = itos( bnr_get_no(bnr) );
+  c = bnr_get_cyc(bnr);
+  g = bnr_get_gen_nocheck(bnr); lc = lg(c)-1;
+  list = cgetg(no+1,t_VEC);
+  gel(list,1) = matid(nf_get_degree(nf)); /* (1) */
+  if (!no) return list;
+
+  pows = cgetg(lc+1,t_VEC);
+  c = leafcopy(c); settyp(c, t_VECSMALL);
+  for (i=1; i<=lc; i++)
+  {
+    long k = itos(gel(c,i));
+    c[i] = k;
+    gk = cgetg(k, t_VEC); gel(gk,1) = gel(g,i);
+    for (j=2; j<k; j++)
+      gel(gk,j) = idealmoddivisor(bnr, idealmul(nf, gel(gk,j-1), gel(gk,1)));
+    gel(pows,i) = gk; /* powers of g[i] */
+  }
+
+  C = cgetg(lc+1, t_VECSMALL); C[1] = c[lc];
+  for (i=2; i<=lc; i++) C[i] = C[i-1] * c[lc-i+1];
+  /* C[i] = c(k-i+1) * ... * ck */
+  /* j < C[i+1] <==> j only involves g(k-i)...gk */
+  i = 1;
+  for (j=1; j < C[1]; j++)
+    gel(list, j+1) = gmael(pows,lc,j);
+  while(j<no)
+  {
+    long k;
+    GEN a;
+    if (j == C[i+1]) i++;
+    a = gmael(pows,lc-i,j/C[i]);
+    k = j%C[i] + 1;
+    if (k > 1) a = idealmoddivisor(bnr, idealmul(nf, a, gel(list,k)));
+    gel(list, ++j) = a;
+  }
+  return list;
+}
+
+/* x quadratic integer (approximate), recognize it. If error return NULL */
+static GEN
+findbezk(GEN nf, GEN x)
+{
+  GEN a,b, M = nf_get_M(nf), u = gcoeff(M,1,2);
+  long ea, eb;
+
+  /* u t_COMPLEX generator of nf.zk, write x ~ a + b u, a,b in Z */
+  b = grndtoi(mpdiv(imag_i(x), gel(u,2)), &eb);
+  if (eb > -20) return NULL;
+  a = grndtoi(mpsub(real_i(x), mpmul(b,gel(u,1))), &ea);
+  if (ea > -20) return NULL;
+  return signe(b)? coltoalg(nf, mkcol2(a,b)): a;
+}
+
+static GEN
+findbezk_pol(GEN nf, GEN x)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx,t_POL);
+  for (i=2; i<lx; i++)
+    if (! (gel(y,i) = findbezk(nf,gel(x,i))) ) return NULL;
+  y[1] = x[1]; return y;
+}
+
+/* allow t_QF[IR], and t_VEC/t_COL with 3 components */
+GEN
+form_to_ideal(GEN x)
+{
+  long tx = typ(x);
+  GEN b;
+  if ((is_vec_t(tx) || lg(x) != 4)
+       && tx != t_QFR && tx != t_QFI) pari_err_TYPE("form_to_ideal",x);
+  b = negi(gel(x,2)); if (mpodd(b)) b = addis(b,1);
+  return mkmat2( mkcol2(gel(x,1), gen_0),
+                 mkcol2(shifti(b,-1), gen_1) );
+}
+
+/* P approximation computed at initial precision prec. Compute needed prec
+ * to know P with 1 word worth of trailing decimals */
+static long
+get_prec(GEN P, long prec)
+{
+  long k = gprecision(P);
+  if (k == 3) return precdbl(prec); /* approximation not trustworthy */
+  k = prec - k; /* lost precision when computing P */
+  if (k < 0) k = 0;
+  k += nbits2prec(gexpo(P) + 128);
+  if (k <= prec) k = precdbl(prec); /* dubious: old prec should have worked */
+  return k;
+}
+
+/* Compute data for ellphist */
+static GEN
+ellphistinit(GEN om, long prec)
+{
+  GEN res,om1b,om2b, om1 = gel(om,1), om2 = gel(om,2);
+
+  if (gsigne(imag_i(gdiv(om1,om2))) < 0) { swap(om1,om2); om = mkvec2(om1,om2); }
+  om1b = gconj(om1);
+  om2b = gconj(om2); res = cgetg(4,t_VEC);
+  gel(res,1) = gdivgs(elleisnum(om,2,0,prec),12);
+  gel(res,2) = gdiv(PiI2(prec), gmul(om2, imag_i(gmul(om1b,om2))));
+  gel(res,3) = om2b; return res;
+}
+
+/* Computes log(phi^*(z,om)), using res computed by ellphistinit */
+static GEN
+ellphist(GEN om, GEN res, GEN z, long prec)
+{
+  GEN u = imag_i(gmul(z, gel(res,3)));
+  GEN zst = gsub(gmul(u, gel(res,2)), gmul(z,gel(res,1)));
+  return gsub(ellsigma(om,z,1,prec),gmul2n(gmul(z,zst),-1));
+}
+
+/* Computes phi^*(la,om)/phi^*(1,om) where (1,om) is an oriented basis of the
+   ideal gf*gc^{-1} */
+static GEN
+computeth2(GEN om, GEN la, long prec)
+{
+  GEN p1,p2,res = ellphistinit(om,prec);
+
+  p1 = gsub(ellphist(om,res,la,prec), ellphist(om,res,gen_1,prec));
+  p2 = imag_i(p1);
+  if (gexpo(real_i(p1))>20 || gexpo(p2)> prec2nbits(minss(prec,realprec(p2)))-10)
+    return NULL;
+  return gexp(p1,prec);
+}
+
+/* Computes P_2(X)=polynomial in Z_K[X] closest to prod_gc(X-th2(gc)) where
+   the product is over the ray class group bnr.*/
+static GEN
+computeP2(GEN bnr, long prec)
+{
+  long clrayno, i, first = 1;
+  pari_sp av=avma, av2;
+  GEN listray, P0, P, lanum, la = get_lambda(bnr);
+  GEN nf = bnr_get_nf(bnr), f = gel(bnr_get_mod(bnr), 1);
+  listray = getallelts(bnr);
+  clrayno = lg(listray)-1; av2 = avma;
+PRECPB:
+  if (!first)
+  {
+    if (DEBUGLEVEL) pari_warn(warnprec,"computeP2",prec);
+    nf = gerepilecopy(av2, nfnewprec_shallow(checknf(bnr),prec));
+  }
+  first = 0; lanum = to_approx(nf,la);
+  P = cgetg(clrayno+1,t_VEC);
+  for (i=1; i<=clrayno; i++)
+  {
+    GEN om = get_om(nf, idealdiv(nf,f,gel(listray,i)));
+    GEN s = computeth2(om,lanum,prec);
+    if (!s) { prec = precdbl(prec); goto PRECPB; }
+    gel(P,i) = s;
+  }
+  P0 = roots_to_pol(P, 0);
+  P = findbezk_pol(nf, P0);
+  if (!P) { prec = get_prec(P0, prec); goto PRECPB; }
+  return gerepilecopy(av, P);
+}
+
+#define nexta(a) (a>0 ? -a : 1-a)
+static GEN
+do_compo(GEN x, GEN y)
+{
+  long a, i, l = lg(y);
+  GEN z;
+  y = leafcopy(y); /* y := t^deg(y) y(#/t) */
+  for (i = 2; i < l; i++)
+    if (signe(gel(y,i))) gel(y,i) = monomial(gel(y,i), l-i-1, MAXVARN);
+  for  (a = 0;; a = nexta(a))
+  {
+    if (a) x = gsubst(x, 0, gaddsg(a, pol_x(0)));
+    z = gsubst(resultant(x,y), MAXVARN, pol_x(0));
+    if (issquarefree(z)) return z;
+  }
+}
+#undef nexta
+
+static GEN
+galoisapplypol(GEN nf, GEN s, GEN x)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx,t_POL);
+
+  for (i=2; i<lx; i++) gel(y,i) = galoisapply(nf,s,gel(x,i));
+  y[1] = x[1]; return y;
+}
+/* x quadratic, write it as ua + v, u,v rational */
+static GEN
+findquad(GEN a, GEN x, GEN p)
+{
+  long tu, tv;
+  pari_sp av = avma;
+  GEN u,v;
+  if (typ(x) == t_POLMOD) x = gel(x,2);
+  if (typ(a) == t_POLMOD) a = gel(a,2);
+  u = poldivrem(x, a, &v);
+  u = simplify_shallow(u); tu = typ(u);
+  v = simplify_shallow(v); tv = typ(v);
+  if (!is_scalar_t(tu)) pari_err_TYPE("findquad", u);
+  if (!is_scalar_t(tv)) pari_err_TYPE("findquad", v);
+  x = deg1pol(v, u, varn(a));
+  if (typ(x) == t_POL) x = gmodulo(x,p);
+  return gerepileupto(av, x);
+}
+static GEN
+findquad_pol(GEN p, GEN a, GEN x)
+{
+  long i, lx = lg(x);
+  GEN y = cgetg(lx,t_POL);
+  for (i=2; i<lx; i++) gel(y,i) = findquad(a, gel(x,i), p);
+  y[1] = x[1]; return y;
+}
+static GEN
+compocyclo(GEN nf, long m, long d)
+{
+  GEN sb,a,b,s,p1,p2,p3,res,polL,polLK,nfL, D = nf_get_disc(nf);
+  long ell,vx;
+
+  p1 = quadhilbertimag(D);
+  p2 = polcyclo(m,0);
+  if (d==1) return do_compo(p1,p2);
+
+  ell = m&1 ? m : (m>>2);
+  if (equalui(ell,D)) /* ell = |D| */
+  {
+    p2 = gcoeff(nffactor(nf,p2),1,1);
+    return do_compo(p1,p2);
+  }
+  if (ell%4 == 3) ell = -ell;
+  /* nf = K = Q(a), L = K(b) quadratic extension = Q(t) */
+  polLK = quadpoly(stoi(ell)); /* relative polynomial */
+  res = rnfequation2(nf, polLK);
+  vx = nf_get_varn(nf);
+  polL = gsubst(gel(res,1),0,pol_x(vx)); /* = charpoly(t) */
+  a = gsubst(lift(gel(res,2)), 0,pol_x(vx));
+  b = gsub(pol_x(vx), gmul(gel(res,3), a));
+  nfL = nfinit(polL, DEFAULTPREC);
+  p1 = gcoeff(nffactor(nfL,p1),1,1);
+  p2 = gcoeff(nffactor(nfL,p2),1,1);
+  p3 = do_compo(p1,p2); /* relative equation over L */
+  /* compute non trivial s in Gal(L / K) */
+  sb= gneg(gadd(b, truecoeff(polLK,1))); /* s(b) = Tr(b) - b */
+  s = gadd(pol_x(vx), gsub(sb, b)); /* s(t) = t + s(b) - b */
+  p3 = gmul(p3, galoisapplypol(nfL, s, p3));
+  return findquad_pol(nf_get_pol(nf), a, p3);
+}
+
+/* I integral ideal in HNF. (x) = I, x small in Z ? */
+static long
+isZ(GEN I)
+{
+  GEN x = gcoeff(I,1,1);
+  if (signe(gcoeff(I,1,2)) || !equalii(x, gcoeff(I,2,2))) return 0;
+  return is_bigint(x)? -1: itos(x);
+}
+
+/* Treat special cases directly. return NULL if not special case */
+static GEN
+treatspecialsigma(GEN bnr)
+{
+  GEN bnf = bnr_get_bnf(bnr), nf = bnf_get_nf(bnf);
+  GEN f = gel(bnr_get_mod(bnr), 1),  D = nf_get_disc(nf);
+  GEN p1, p2;
+  long Ds, fl, tryf, i = isZ(f);
+
+  if (i == 1) return quadhilbertimag(D); /* f = 1 */
+
+  if (equaliu(D,3)) /* Q(j) */
+  {
+    if (i == 4 || i == 5 || i == 7) return polcyclo(i,0);
+    if (!equaliu(gcoeff(f,1,1),9) || !equaliu(Q_content(f),3)) return NULL;
+    /* f = P_3^3 */
+    p1 = mkpolmod(bnf_get_tuU(bnf), nf_get_pol(nf));
+    return gadd(monomial(gen_1,3,0), p1); /* x^3+j */
+  }
+  if (equaliu(D,4)) /* Q(i) */
+  {
+    if (i == 3 || i == 5) return polcyclo(i,0);
+    if (i != 4) return NULL;
+    p1 = mkpolmod(bnf_get_tuU(bnf), nf_get_pol(nf));
+    return gadd(monomial(gen_1,2,0), p1); /* x^2+i */
+  }
+  Ds = smodis(D,48);
+  if (i)
+  {
+    if (i==2 && Ds%16== 8) return compocyclo(nf, 4,1);
+    if (i==3 && Ds% 3== 1) return compocyclo(nf, 3,1);
+    if (i==4 && Ds% 8== 1) return compocyclo(nf, 4,1);
+    if (i==6 && Ds   ==40) return compocyclo(nf,12,1);
+    return NULL;
+  }
+
+  p1 = gcoeff(f,1,1); /* integer > 0 */
+  if (is_bigint(p1)) return NULL;
+  tryf = p1[2];
+  p2 = gcoeff(f,2,2); /* integer > 0 */
+  if (is_pm1(p2)) fl = 0;
+  else {
+    if (Ds % 16 != 8 || !equaliu(Q_content(f),2)) return NULL;
+    fl = 1; tryf >>= 1;
+  }
+  if (tryf <= 3 || umodiu(D, tryf) || !uisprime(tryf)) return NULL;
+  if (fl) tryf <<= 2;
+  return compocyclo(nf,tryf,2);
+}
+
+GEN
+quadray(GEN D, GEN f, long prec)
+{
+  GEN bnr, y, bnf;
+  pari_sp av = avma;
+
+  if (isint1(f)) return quadhilbert(D, prec);
+  quadray_init(&D, f, &bnf, prec);
+  bnr = Buchray(bnf, f, nf_INIT|nf_GEN);
+  if (is_pm1(bnr_get_no(bnr))) { avma = av; return pol_x(0); }
+  if (signe(D) > 0)
+    y = bnrstark(bnr,NULL,prec);
+  else
+  {
+    bnr = gel(bnrconductor(bnr,NULL,2), 2);
+    y = treatspecialsigma(bnr);
+    if (!y) y = computeP2(bnr, prec);
+  }
+  return gerepileupto(av, y);
+}
diff --git a/src/modules/subfield.c b/src/modules/subfield.c
new file mode 100644
index 0000000..3889aab
--- /dev/null
+++ b/src/modules/subfield.c
@@ -0,0 +1,932 @@
+/* Copyright (C) 2000-2004  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/*******************************************************************/
+/*                                                                 */
+/*               SUBFIELDS OF A NUMBER FIELD                       */
+/*   J. Klueners and M. Pohst, J. Symb. Comp. (1996), vol. 11      */
+/*                                                                 */
+/*******************************************************************/
+#include "pari.h"
+#include "paripriv.h"
+
+typedef struct _poldata {
+  GEN pol;
+  GEN dis; /* |disc(pol)| */
+  GEN roo; /* roots(pol) */
+  GEN den; /* multiple of index(pol) */
+} poldata;
+typedef struct _primedata {
+  GEN p;  /* prime */
+  GEN pol; /* pol mod p, squarefree */
+  GEN ff; /* factorization of pol mod p */
+  GEN Z; /* cycle structure of the above [ Frobenius orbits ] */
+  long lcm; /* lcm of the above */
+  GEN T;  /* ffinit(p, lcm) */
+
+  GEN fk;      /* factorization of pol over F_(p^lcm) */
+  GEN firstroot; /* *[i] = index of first root of fk[i] */
+  GEN interp;    /* *[i] = interpolation polynomial for fk[i]
+                  * [= 1 on the first root firstroot[i], 0 on the others] */
+  GEN bezoutC; /* Bezout coefficients associated to the ff[i] */
+  GEN Trk;     /* used to compute traces (cf poltrace) */
+} primedata;
+typedef struct _blockdata {
+  poldata *PD; /* data depending from pol */
+  primedata *S;/* data depending from pol, p */
+  GEN DATA; /* data depending from pol, p, degree, # translations [updated] */
+  long N; /* deg(PD.pol) */
+  long d; /* subfield degree */
+  long size;/* block degree = N/d */
+} blockdata;
+
+static GEN print_block_system(blockdata *B, GEN Y, GEN BS);
+static GEN test_block(blockdata *B, GEN L, GEN D);
+
+/* COMBINATORIAL PART: generate potential block systems */
+
+#define BIL 32 /* for 64bit machines also */
+/* Computation of potential block systems of given size d associated to a
+ * rational prime p: give a row vector of row vectors containing the
+ * potential block systems of imprimitivity; a potential block system is a
+ * vector of row vectors (enumeration of the roots). */
+static GEN
+calc_block(blockdata *B, GEN Z, GEN Y, GEN SB)
+{
+  long r = lg(Z), lK, i, j, t, tp, T, u, nn, lnon, lY;
+  GEN K, n, non, pn, pnon, e, Yp, Zp, Zpp;
+  pari_sp av0 = avma;
+
+  if (DEBUGLEVEL>3)
+  {
+    err_printf("lg(Z) = %ld, lg(Y) = %ld\n", r,lg(Y));
+    if (DEBUGLEVEL > 5)
+    {
+      err_printf("Z = %Ps\n",Z);
+      err_printf("Y = %Ps\n",Y);
+    }
+  }
+  lnon = minss(BIL, r);
+  e    = new_chunk(BIL);
+  n    = new_chunk(r);
+  non  = new_chunk(lnon);
+  pnon = new_chunk(lnon);
+  pn   = new_chunk(lnon);
+
+  Zp   = cgetg(lnon,t_VEC);
+  Zpp  = cgetg(lnon,t_VEC); nn = 0;
+  for (i=1; i<r; i++) { n[i] = lg(gel(Z,i))-1; nn += n[i]; }
+  lY = lg(Y); Yp = cgetg(lY+1,t_VEC);
+  for (j=1; j<lY; j++) gel(Yp,j) = gel(Y,j);
+
+  {
+    pari_sp av = avma;
+    long k = nn / B->size;
+    for (j = 1; j < r; j++)
+      if (n[j] % k) break;
+    if (j == r)
+    {
+      gel(Yp,lY) = Z;
+      SB = print_block_system(B, Yp, SB);
+      avma = av;
+    }
+  }
+  gel(Yp,lY) = Zp;
+
+  K = divisorsu(n[1]); lK = lg(K);
+  for (i=1; i<lK; i++)
+  {
+    long ngcd = n[1], k = K[i], dk = B->size*k, lpn = 0;
+    for (j=2; j<r; j++)
+      if (n[j]%k == 0)
+      {
+        if (++lpn >= BIL) pari_err_OVERFLOW("calc_block");
+        pn[lpn] = n[j]; pnon[lpn] = j;
+        ngcd = ugcd(ngcd, n[j]);
+      }
+    if (dk % ngcd) continue;
+    T = 1L<<lpn;
+    if (lpn == r-2)
+    {
+      T--; /* done already above --> print_block_system */
+      if (!T) continue;
+    }
+
+    if (dk == n[1])
+    { /* empty subset, t = 0. Split out for clarity */
+      Zp[1] = Z[1]; setlg(Zp, 2);
+      for (u=1,j=2; j<r; j++) Zpp[u++] = Z[j];
+      setlg(Zpp, u);
+      SB = calc_block(B, Zpp, Yp, SB);
+    }
+
+    for (t = 1; t < T; t++)
+    { /* loop through all non-empty subsets of [1..lpn] */
+      for (nn=n[1],tp=t, u=1; u<=lpn; u++,tp>>=1)
+      {
+        if (tp&1) { nn += pn[u]; e[u] = 1; } else e[u] = 0;
+      }
+      if (dk != nn) continue;
+
+      for (j=1; j<r; j++) non[j]=0;
+      Zp[1] = Z[1];
+      for (u=2,j=1; j<=lpn; j++)
+        if (e[j]) { Zp[u] = Z[pnon[j]]; non[pnon[j]] = 1; u++; }
+      setlg(Zp, u);
+      for (u=1,j=2; j<r; j++)
+        if (!non[j]) Zpp[u++] = Z[j];
+      setlg(Zpp, u);
+      SB = calc_block(B, Zpp, Yp, SB);
+    }
+  }
+  avma = av0; return SB;
+}
+
+/* product of permutations. Put the result in perm1. */
+static void
+perm_mul_i(GEN perm1, GEN perm2)
+{
+  long i, N = lg(perm1);
+  pari_sp av = avma;
+  GEN perm = new_chunk(N);
+  for (i=1; i<N; i++) perm[i] = perm1[perm2[i]];
+  for (i=1; i<N; i++) perm1[i]= perm[i];
+  avma = av;
+}
+
+/* cy is a cycle; compute cy^l as a permutation */
+static GEN
+cycle_power_to_perm(GEN perm,GEN cy,long l)
+{
+  long lp,i,j,b, N = lg(perm), lcy = lg(cy)-1;
+
+  lp = l % lcy;
+  for (i=1; i<N; i++) perm[i] = i;
+  if (lp)
+  {
+    pari_sp av = avma;
+    GEN p1 = new_chunk(N);
+    b = cy[1];
+    for (i=1; i<lcy; i++) b = (perm[b] = cy[i+1]);
+    perm[b] = cy[1];
+    for (i=1; i<N; i++) p1[i] = perm[i];
+
+    for (j=2; j<=lp; j++) perm_mul_i(perm,p1);
+    avma = av;
+  }
+  return perm;
+}
+
+/* image du block system D par la permutation perm */
+static GEN
+im_block_by_perm(GEN D,GEN perm)
+{
+  long i, lb = lg(D);
+  GEN Dn = cgetg(lb,t_VEC);
+  for (i=1; i<lb; i++) gel(Dn,i) = vecpermute(perm, gel(D,i));
+  return Dn;
+}
+
+static void
+append(GEN D, GEN a)
+{
+  long i,l = lg(D), m = lg(a);
+  GEN x = D + (l-1);
+  for (i=1; i<m; i++) gel(x,i) = gel(a,i);
+  setlg(D, l+m-1);
+}
+
+static GEN
+print_block_system(blockdata *B, GEN Y, GEN SB)
+{
+  long i, j, l, ll, lp, u, v, ns, r = lg(Y), N = B->N;
+  long *k, *n, **e, *t;
+  GEN D, De, Z, cyperm, perm, VOID = cgetg(1, t_VECSMALL);
+
+  if (DEBUGLEVEL>5) err_printf("Y = %Ps\n",Y);
+  n = new_chunk(N+1);
+  D = vectrunc_init(N+1);
+  t = new_chunk(r+1);
+  k = new_chunk(r+1);
+  Z = cgetg(r+1, t_VEC);
+  for (ns=0,i=1; i<r; i++)
+  {
+    GEN Yi = gel(Y,i);
+    long ki = 0, si = lg(Yi)-1;
+
+    for (j=1; j<=si; j++) { n[j] = lg(gel(Yi,j))-1; ki += n[j]; }
+    ki /= B->size;
+    De = cgetg(ki+1,t_VEC);
+    for (j=1; j<=ki; j++) gel(De,j) = VOID;
+    for (j=1; j<=si; j++)
+    {
+      GEN cy = gel(Yi,j);
+      for (l=1,lp=0; l<=n[j]; l++)
+      {
+        lp++; if (lp > ki) lp = 1;
+        gel(De,lp) = vecsmall_append(gel(De,lp), cy[l]);
+      }
+    }
+    append(D, De);
+    if (si>1 && ki>1)
+    {
+      GEN p1 = cgetg(si,t_VEC);
+      for (j=2; j<=si; j++) p1[j-1] = Yi[j];
+      ns++;
+      t[ns] = si-1;
+      k[ns] = ki-1;
+      gel(Z,ns) = p1;
+    }
+  }
+  if (DEBUGLEVEL>2) err_printf("\nns = %ld\n",ns);
+  if (!ns) return test_block(B, SB, D);
+
+  setlg(Z, ns+1);
+  e = (long**)new_chunk(ns+1);
+  for (i=1; i<=ns; i++)
+  {
+    e[i] = new_chunk(t[i]+1);
+    for (j=1; j<=t[i]; j++) e[i][j] = 0;
+  }
+  cyperm= cgetg(N+1,t_VECSMALL);
+  perm  = cgetg(N+1,t_VECSMALL); i = ns;
+  do
+  {
+    pari_sp av = avma;
+    for (u=1; u<=N; u++) perm[u] = u;
+    for (u=1; u<=ns; u++)
+      for (v=1; v<=t[u]; v++)
+        perm_mul_i(perm, cycle_power_to_perm(cyperm, gmael(Z,u,v), e[u][v]));
+    SB = test_block(B, SB, im_block_by_perm(D,perm));
+    avma = av;
+
+    /* i = 1..ns, j = 1..t[i], e[i][j] loop through 0..k[i].
+     * TODO: flatten to 1-dimensional loop */
+    if (++e[ns][t[ns]] > k[ns])
+    {
+      j = t[ns]-1;
+      while (j>=1 && e[ns][j] == k[ns]) j--;
+      if (j >= 1) { e[ns][j]++; for (l=j+1; l<=t[ns]; l++) e[ns][l] = 0; }
+      else
+      {
+        i = ns-1;
+        while (i>=1)
+        {
+          j = t[i];
+          while (j>=1 && e[i][j] == k[i]) j--;
+          if (j<1) i--;
+          else
+          {
+            e[i][j]++;
+            for (l=j+1; l<=t[i]; l++) e[i][l] = 0;
+            for (ll=i+1; ll<=ns; ll++)
+              for (l=1; l<=t[ll]; l++) e[ll][l] = 0;
+            break;
+          }
+        }
+      }
+    }
+  }
+  while (i > 0);
+  return SB;
+}
+
+/* ALGEBRAIC PART: test potential block systems */
+
+static GEN
+polsimplify(GEN x)
+{
+  long i,lx = lg(x);
+  for (i=2; i<lx; i++)
+    if (typ(gel(x,i)) == t_POL) gel(x,i) = constant_term(gel(x,i));
+  return x;
+}
+
+/* return 0 if |g[i]| > M[i] for some i; 1 otherwise */
+static long
+ok_coeffs(GEN g,GEN M)
+{
+  long i, lg = lg(g)-1; /* g is monic, and cst term is ok */
+  for (i=3; i<lg; i++)
+    if (absi_cmp(gel(g,i), gel(M,i)) > 0) return 0;
+  return 1;
+}
+
+/* assume x in Fq, return Tr_{Fq/Fp}(x) as a t_INT */
+static GEN
+trace(GEN x, GEN Trq, GEN p)
+{
+  long i, l;
+  GEN s;
+  if (typ(x) == t_INT) return Fp_mul(x, gel(Trq,1), p);
+  l = lg(x)-1; if (l == 1) return gen_0;
+  x++; s = mulii(gel(x,1), gel(Trq,1));
+  for (i=2; i<l; i++)
+    s = addii(s, mulii(gel(x,i), gel(Trq,i)));
+  return modii(s, p);
+}
+
+/* assume x in Fq[X], return Tr_{Fq[X]/Fp[X]}(x), varn(X) = 0 */
+static GEN
+poltrace(GEN x, GEN Trq, GEN p)
+{
+  long i,l;
+  GEN y;
+  if (typ(x) == t_INT || varn(x) != 0) return trace(x, Trq, p);
+  y = cgetg_copy(x, &l); y[1] = x[1];
+  for (i=2; i<l; i++) gel(y,i) = trace(gel(x,i),Trq,p);
+  return normalizepol_lg(y, l);
+}
+
+/* Find h in Fp[X] such that h(a[i]) = listdelta[i] for all modular factors
+ * ff[i], where a[i] is a fixed root of ff[i] in Fq = Z[Y]/(p,T) [namely the
+ * first one in FpX_factorff_irred output]. Let f = ff[i], A the given root,
+ * then h mod f is Tr_Fq/Fp ( h(A) f(X)/(X-A)f'(A) ), most of the expression
+ * being precomputed. The complete h is recovered via chinese remaindering */
+static GEN
+chinese_retrieve_pol(GEN DATA, primedata *S, GEN listdelta)
+{
+  GEN interp, bezoutC, h, p = S->p, pol = FpX_red(gel(DATA,1), p);
+  long i, l;
+  interp = gel(DATA,9);
+  bezoutC= gel(DATA,6);
+
+  h = NULL; l = lg(interp);
+  for (i=1; i<l; i++)
+  { /* h(firstroot[i]) = listdelta[i] */
+    GEN t = FqX_Fq_mul(gel(interp,i), gel(listdelta,i), S->T, p);
+    t = poltrace(t, gel(S->Trk,i), p);
+    t = FpX_mul(t, gel(bezoutC,i), p);
+    h = h? FpX_add(h,t,p): t;
+  }
+  return FpX_rem(h, pol, p);
+}
+
+/* g in Z[X] potentially defines a subfield of Q[X]/f. It is a subfield iff A
+ * (cf subfield) was a block system; then there
+ * exists h in Q[X] such that f | g o h. listdelta determines h s.t f | g o h
+ * in Fp[X] (cf chinese_retrieve_pol). Try to lift it; den is a
+ * multiplicative bound for denominator of lift. */
+static GEN
+embedding(GEN g, GEN DATA, primedata *S, GEN den, GEN listdelta)
+{
+  GEN TR, w0_Q, w0, w1_Q, w1, wpow, h0, gp, T, q2, q, maxp, a, p = S->p;
+  long rt;
+  pari_sp av;
+
+  T   = gel(DATA,1); rt = brent_kung_optpow(degpol(T), 4, 3);
+  maxp= gel(DATA,7);
+  gp = RgX_deriv(g); av = avma;
+  w0 = chinese_retrieve_pol(DATA, S, listdelta);
+  w0_Q = centermod(gmul(w0,den), p);
+  h0 = FpXQ_inv(FpX_FpXQ_eval(gp,w0, T,p), T,p); /* = 1/g'(w0) mod (T,p) */
+  wpow = NULL; q = sqri(p);
+  for(;;)
+  {/* Given g,w0,h0 in Z[x], s.t. h0.g'(w0) = 1 and g(w0) = 0 mod (T,p), find
+    * [w1,h1] satisfying the same conditions mod p^2, [w1,h1] = [w0,h0] (mod p)
+    * (cf. Dixon: J. Austral. Math. Soc., Series A, vol.49, 1990, p.445) */
+    if (DEBUGLEVEL>1)
+      err_printf("lifting embedding mod p^k = %Ps^%ld\n",S->p, Z_pval(q,S->p));
+
+    /* w1 := w0 - h0 g(w0) mod (T,q) */
+    if (wpow) a = FpX_FpXQV_eval(g,wpow, T,q);
+    else      a = FpX_FpXQ_eval(g,w0, T,q); /* first time */
+    /* now, a = 0 (p) */
+    a = FpXQ_mul(ZX_neg(h0), ZX_Z_divexact(a, p), T,p);
+    w1 = ZX_add(w0, ZX_Z_mul(a, p));
+
+    w1_Q = centermod(ZX_Z_mul(w1, remii(den,q)), q);
+    if (ZX_equal(w1_Q, w0_Q))
+    {
+      GEN G = is_pm1(den)? g: RgX_rescale(g,den);
+      if (gequal0(RgX_RgXQ_eval(G, w1_Q, T))) break;
+    }
+    else if (cmpii(q,maxp) > 0)
+    {
+      GEN G = is_pm1(den)? g: RgX_rescale(g,den);
+      if (gequal0(RgX_RgXQ_eval(G, w1_Q, T))) break;
+      if (DEBUGLEVEL) err_printf("coeff too big for embedding\n");
+      return NULL;
+    }
+    gerepileall(av, 5, &w1,&h0,&w1_Q,&q,&p);
+    q2 = sqri(q);
+    wpow = FpXQ_powers(w1, rt, T, q2);
+    /* h0 := h0 * (2 - h0 g'(w1)) mod (T,q)
+     *     = h0 + h0 * (1 - h0 g'(w1)) */
+    a = FpXQ_mul(ZX_neg(h0), FpX_FpXQV_eval(gp, FpXV_red(wpow,q),T,q), T,q);
+    a = ZX_Z_add_shallow(a, gen_1); /* 1 - h0 g'(w1) = 0 (p) */
+    a = FpXQ_mul(h0, ZX_Z_divexact(a, p), T,p);
+    h0 = ZX_add(h0, ZX_Z_mul(a, p));
+    w0 = w1; w0_Q = w1_Q; p = q; q = q2;
+  }
+  TR = gel(DATA,5);
+  if (!gequal0(TR)) w1_Q = RgX_translate(w1_Q, TR);
+  return gdiv(w1_Q,den);
+}
+
+/* return U list of polynomials s.t U[i] = 1 mod fk[i] and 0 mod fk[j] for all
+ * other j */
+static GEN
+get_bezout(GEN pol, GEN fk, GEN p)
+{
+  long i, l = lg(fk);
+  GEN A, B, d, u, v, U = cgetg(l, t_VEC);
+  for (i=1; i<l; i++)
+  {
+    A = gel(fk,i);
+    B = FpX_div(pol, A, p);
+    d = FpX_extgcd(A,B,p, &u, &v);
+    if (degpol(d) > 0) pari_err_COPRIME("get_bezout",A,B);
+    d = constant_term(d);
+    if (!gequal1(d)) v = FpX_Fp_mul(v, Fp_inv(d, p), p);
+    gel(U,i) = FpX_mul(B,v, p);
+  }
+  return U;
+}
+
+static GEN
+init_traces(GEN ff, GEN T, GEN p)
+{
+  long N = degpol(T),i,j,k, r = lg(ff);
+  GEN Frob = FpXQ_matrix_pow(FpXQ_pow(pol_x(varn(T)),p, T,p), N,N, T,p);
+  GEN y,p1,p2,Trk,pow,pow1;
+
+  k = degpol(gel(ff,r-1)); /* largest degree in modular factorization */
+  pow = cgetg(k+1, t_VEC);
+  gel(pow,1) = gen_0; /* dummy */
+  gel(pow,2) = Frob;
+  pow1= cgetg(k+1, t_VEC); /* 1st line */
+  for (i=3; i<=k; i++)
+    gel(pow,i) = FpM_mul(gel(pow,i-1), Frob, p);
+  gel(pow1,1) = gen_0; /* dummy */
+  for (i=2; i<=k; i++)
+  {
+    p1 = cgetg(N+1, t_VEC);
+    gel(pow1,i) = p1; p2 = gel(pow,i);
+    for (j=1; j<=N; j++) gel(p1,j) = gcoeff(p2,1,j);
+  }
+
+  /* Trk[i] = line 1 of x -> x + x^p + ... + x^{p^(i-1)} */
+  Trk = pow; /* re-use (destroy) pow */
+  gel(Trk,1) = vec_ei(N,1);
+  for (i=2; i<=k; i++)
+    gel(Trk,i) = gadd(gel(Trk,i-1), gel(pow1,i));
+  y = cgetg(r, t_VEC);
+  for (i=1; i<r; i++) y[i] = Trk[degpol(gel(ff,i))];
+  return y;
+}
+
+static void
+init_primedata(primedata *S)
+{
+  long i, j, l, lff = lg(S->ff), v = fetch_var(), N = degpol(S->pol);
+  GEN T, p = S->p;
+
+  if (S->lcm == degpol(gel(S->ff,lff-1)))
+  {
+    T = leafcopy(gel(S->ff,lff-1));
+    setvarn(T, v);
+  }
+  else
+    T = init_Fq(p, S->lcm, v);
+  name_var(v,"y");
+  S->T = T;
+  S->firstroot = cgetg(lff, t_VECSMALL);
+  S->interp = cgetg(lff, t_VEC);
+  S->fk = cgetg(N+1, t_VEC);
+  for (l=1,j=1; j<lff; j++)
+  { /* compute roots and fix ordering (Frobenius cycles) */
+    GEN F = gel(S->ff, j), deg1 = FpX_factorff_irred(F, T,p);
+    GEN H = gel(deg1,1), a = Fq_neg(constant_term(H), T,p);
+    GEN Q = FqX_div(F, H, T,p);
+    GEN q = Fq_inv(FqX_eval(Q, a, T,p), T,p);
+    gel(S->interp,j) = FqX_Fq_mul(Q, q, T,p); /* = 1 at a, 0 at other roots */
+    S->firstroot[j] = l;
+    for (i=1; i<lg(deg1); i++,l++) gel(S->fk, l) = gel(deg1, i);
+  }
+  S->Trk     = init_traces(S->ff, T,p);
+  S->bezoutC = get_bezout(S->pol, S->ff, p);
+}
+
+static void
+choose_prime(primedata *S, GEN pol, GEN dpol)
+{
+  long i, j, k, r, lcm, oldlcm, N = degpol(pol);
+  ulong p, pp;
+  GEN Z, ff, oldff, n, oldn;
+  pari_sp av;
+  forprime_t T;
+
+  u_forprime_init(&T, (N*N) >> 2, ULONG_MAX);
+  oldlcm = 0;
+  oldff = oldn = NULL; pp = 0; /* gcc -Wall */
+  av = avma;
+  for(k = 1; k < 11 || !oldlcm; k++,avma = av)
+  {
+    if (k > 5 * N) pari_err_OVERFLOW("choose_prime [too many block systems]");
+    do p = u_forprime_next(&T); while (!umodiu(dpol, p));
+    ff = gel(Flx_factor(ZX_to_Flx(pol,p), p), 1);
+    r = lg(ff)-1;
+    if (r == N || r >= BIL) continue;
+
+    n = cgetg(r+1, t_VECSMALL); lcm = n[1] = degpol(gel(ff,1));
+    for (j=2; j<=r; j++) { n[j] = degpol(gel(ff,j)); lcm = clcm(lcm, n[j]); }
+    if (lcm <= oldlcm) continue; /* false when oldlcm = 0 */
+
+    if (DEBUGLEVEL) err_printf("p = %ld,\tlcm = %ld,\torbits: %Ps\n",p,lcm,n);
+    pp = p;
+    oldn = n;
+    oldff = ff;
+    oldlcm = lcm; if (r == 1) break;
+    av = avma;
+  }
+  if (DEBUGLEVEL) err_printf("Chosen prime: p = %ld\n", pp);
+  FlxV_to_ZXV_inplace(oldff);
+  S->ff = oldff;
+  S->lcm= oldlcm;
+  S->p  = utoipos(pp);
+  S->pol = FpX_red(pol, S->p); init_primedata(S);
+
+  n = oldn; r = lg(n); Z = cgetg(r,t_VEC);
+  for (k=0,i=1; i<r; i++)
+  {
+    GEN t = cgetg(n[i]+1, t_VECSMALL); gel(Z,i) = t;
+    for (j=1; j<=n[i]; j++) t[j] = ++k;
+  }
+  S->Z = Z;
+}
+
+/* maxroot t_REAL */
+static GEN
+bound_for_coeff(long m, GEN rr, GEN *maxroot)
+{
+  long i,r1, lrr=lg(rr);
+  GEN p1,b1,b2,B,M, C = matpascal(m-1);
+
+  for (r1=1; r1 < lrr; r1++)
+    if (typ(gel(rr,r1)) != t_REAL) break;
+  r1--;
+
+  rr = gabs(rr,0); *maxroot = vecmax(rr);
+  for (i=1; i<lrr; i++)
+    if (gcmp(gel(rr,i), gen_1) < 0) gel(rr,i) = gen_1;
+  for (b1=gen_1,i=1; i<=r1; i++) b1 = gmul(b1, gel(rr,i));
+  for (b2=gen_1    ; i<lrr; i++) b2 = gmul(b2, gel(rr,i));
+  B = gmul(b1, gsqr(b2)); /* Mahler measure */
+  M = cgetg(m+2, t_VEC); gel(M,1) = gel(M,2) = gen_0; /* unused */
+  for (i=1; i<m; i++)
+  {
+    p1 = gadd(gmul(gcoeff(C, m, i+1), B),/* binom(m-1, i)   */
+              gcoeff(C, m, i));          /* binom(m-1, i-1) */
+    gel(M,i+2) = ceil_safe(p1);
+  }
+  return M;
+}
+
+/* d = requested degree for subfield. Return DATA, valid for given pol, S and d
+ * If DATA != NULL, translate pol [ --> pol(X+1) ] and update DATA
+ * 1: polynomial pol
+ * 2: p^e (for Hensel lifts) such that p^e > max(M),
+ * 3: Hensel lift to precision p^e of DATA[4]
+ * 4: roots of pol in F_(p^S->lcm),
+ * 5: number of polynomial changes (translations)
+ * 6: Bezout coefficients associated to the S->ff[i]
+ * 7: Hadamard bound for coefficients of h(x) such that g o h = 0 mod pol.
+ * 8: bound M for polynomials defining subfields x PD->den
+ * 9: *[i] = interpolation polynomial for S->ff[i] [= 1 on the first root
+      S->firstroot[i], 0 on the others] */
+static void
+compute_data(blockdata *B)
+{
+  GEN ffL, roo, pe, p1, p2, fk, fhk, MM, maxroot, pol;
+  primedata *S = B->S;
+  GEN p = S->p, T = S->T, ff = S->ff, DATA = B->DATA;
+  long i, j, l, e, N, lff = lg(ff);
+
+  if (DEBUGLEVEL>1) err_printf("Entering compute_data()\n\n");
+  pol = B->PD->pol; N = degpol(pol);
+  roo = B->PD->roo;
+  if (DATA)
+  {
+    GEN Xm1 = gsub(pol_x(varn(pol)), gen_1);
+    GEN TR = addis(gel(DATA,5), 1);
+    GEN mTR = negi(TR), interp, bezoutC;
+
+    if (DEBUGLEVEL>1) err_printf("... update (translate) an existing DATA\n\n");
+
+    gel(DATA,5) = TR;
+    pol = RgX_translate(gel(DATA,1), gen_m1);
+    p1 = cgetg_copy(roo, &l);
+    for (i=1; i<l; i++) gel(p1,i) = gadd(TR, gel(roo,i));
+    roo = p1;
+
+    fk = gel(DATA,4); l = lg(fk);
+    for (i=1; i<l; i++) gel(fk,i) = gsub(Xm1, gel(fk,i));
+
+    bezoutC = gel(DATA,6); l = lg(bezoutC);
+    interp  = gel(DATA,9);
+    for (i=1; i<l; i++)
+    {
+      if (degpol(gel(interp,i)) > 0) /* do not turn pol_1(0) into gen_1 */
+      {
+        p1 = RgX_translate(gel(interp,i), gen_m1);
+        gel(interp,i) = FpXX_red(p1, p);
+      }
+      if (degpol(gel(bezoutC,i)) > 0)
+      {
+        p1 = RgX_translate(gel(bezoutC,i), gen_m1);
+        gel(bezoutC,i) = FpXX_red(p1, p);
+      }
+    }
+    ff = cgetg(lff, t_VEC); /* copy, do not overwrite! */
+    for (i=1; i<lff; i++)
+      gel(ff,i) = FpX_red(RgX_translate(gel(S->ff,i), mTR), p);
+  }
+  else
+  {
+    DATA = cgetg(10,t_VEC);
+    fk = S->fk;
+    gel(DATA,5) = gen_0;
+    gel(DATA,6) = leafcopy(S->bezoutC);
+    gel(DATA,9) = leafcopy(S->interp);
+  }
+  gel(DATA,1) = pol;
+  MM = gmul2n(bound_for_coeff(B->d, roo, &maxroot), 1);
+  gel(DATA,8) = MM;
+  e = logint(shifti(vecmax(MM),20), p, &pe); /* overlift 2^20 [for d-1 test] */
+  gel(DATA,2) = pe;
+  gel(DATA,4) = roots_from_deg1(fk);
+
+  /* compute fhk = ZpX_liftfact(pol,fk,T,p,e,pe) in 2 steps
+   * 1) lift in Zp to precision p^e */
+  ffL = ZpX_liftfact(pol, ff, NULL, p, e, pe);
+  fhk = NULL;
+  for (l=i=1; i<lff; i++)
+  { /* 2) lift factorization of ff[i] in Qp[X] / T */
+    GEN F, L = gel(ffL,i);
+    long di = degpol(L);
+    F = cgetg(di+1, t_VEC);
+    for (j=1; j<=di; j++) F[j] = fk[l++];
+    L = ZpX_liftfact(L, F, T, p, e, pe);
+    fhk = fhk? shallowconcat(fhk, L): L;
+  }
+  gel(DATA,3) = roots_from_deg1(fhk);
+
+  p1 = mulur(N, powruhalf(stor(N-1,DEFAULTPREC), N-1));
+  p2 = powru(maxroot, B->size + N*(N-1)/2);
+  p1 = divrr(mulrr(p1,p2), gsqrt(B->PD->dis,DEFAULTPREC));
+  gel(DATA,7) = mulii(shifti(ceil_safe(p1), 1), B->PD->den);
+
+  if (DEBUGLEVEL>1) {
+    err_printf("f = %Ps\n",DATA[1]);
+    err_printf("p = %Ps, lift to p^%ld\n", p, e);
+    err_printf("2 * Hadamard bound * ind = %Ps\n",DATA[7]);
+    err_printf("2 * M = %Ps\n",DATA[8]);
+  }
+  if (B->DATA) {
+    DATA = gclone(DATA);
+    if (isclone(B->DATA)) gunclone(B->DATA);
+  }
+  B->DATA = DATA;
+}
+
+/* g = polynomial, h = embedding. Return [[g,h]] */
+static GEN
+_subfield(GEN g, GEN h) { return mkvec(mkvec2(g,h)); }
+
+/* Return a subfield, gen_0 [ change p ] or NULL [ not a subfield ] */
+static GEN
+subfield(GEN A, blockdata *B)
+{
+  long N, i, j, d, lf, m = lg(A)-1;
+  GEN M, pe, pol, fhk, g, e, d_1_term, delta, listdelta, whichdelta;
+  GEN T = B->S->T, p = B->S->p, firstroot = B->S->firstroot;
+
+  pol= gel(B->DATA,1); N = degpol(pol); d = N/m; /* m | N */
+  pe = gel(B->DATA,2);
+  fhk= gel(B->DATA,3);
+  M  = gel(B->DATA,8);
+
+  delta = cgetg(m+1,t_VEC);
+  whichdelta = cgetg(N+1, t_VECSMALL);
+  d_1_term = gen_0;
+  for (i=1; i<=m; i++)
+  {
+    GEN Ai = gel(A,i), p1 = gel(fhk,Ai[1]);
+    for (j=2; j<=d; j++)
+      p1 = Fq_mul(p1, gel(fhk,Ai[j]), T, pe);
+    gel(delta,i) = p1;
+    if (DEBUGLEVEL>5) err_printf("delta[%ld] = %Ps\n",i,p1);
+    /* g = prod (X - delta[i])
+     * if g o h = 0 (pol), we'll have h(Ai[j]) = delta[i] for all j */
+    /* fk[k] belongs to block number whichdelta[k] */
+    for (j=1; j<=d; j++) whichdelta[Ai[j]] = i;
+    if (typ(p1) == t_POL) p1 = constant_term(p1);
+    d_1_term = addii(d_1_term, p1);
+  }
+  d_1_term = centermod(d_1_term, pe); /* Tr(g) */
+  if (absi_cmp(d_1_term, gel(M,3)) > 0) {
+    if (DEBUGLEVEL>1) err_printf("d-1 test failed\n");
+    return NULL;
+  }
+  g = FqV_roots_to_pol(delta, T, pe, 0);
+  g = centermod(polsimplify(g), pe); /* assume g in Z[X] */
+  if (!ok_coeffs(g,M)) {
+    if (DEBUGLEVEL>2) err_printf("pol. found = %Ps\n",g);
+    if (DEBUGLEVEL>1) err_printf("coeff too big for pol g(x)\n");
+    return NULL;
+  }
+  if (!FpX_is_squarefree(g, p)) {
+    if (DEBUGLEVEL>2) err_printf("pol. found = %Ps\n",g);
+    if (DEBUGLEVEL>1) err_printf("changing f(x): p divides disc(g)\n");
+    compute_data(B);
+    return subfield(A, B);
+  }
+
+  lf = lg(firstroot); listdelta = cgetg(lf, t_VEC);
+  for (i=1; i<lf; i++) listdelta[i] = delta[whichdelta[firstroot[i]]];
+  if (DEBUGLEVEL) err_printf("candidate = %Ps\n", g);
+  e = embedding(g, B->DATA, B->S, B->PD->den, listdelta);
+  if (!e) return NULL;
+  if (DEBUGLEVEL) err_printf("... OK!\n");
+  return _subfield(g, e);
+}
+
+/* L list of current subfields, test whether potential block D is a block,
+ * if so, append corresponding subfield */
+static GEN
+test_block(blockdata *B, GEN L, GEN D)
+{
+  pari_sp av = avma;
+  GEN sub = subfield(D, B);
+  if (sub) {
+    GEN old = L;
+    L = gclone( L? shallowconcat(L, sub): sub );
+    if (old) gunclone(old);
+  }
+  avma = av; return L;
+}
+
+/* subfields of degree d */
+static GEN
+subfields_of_given_degree(blockdata *B)
+{
+  pari_sp av = avma;
+  GEN L;
+
+  if (DEBUGLEVEL) err_printf("\n* Look for subfields of degree %ld\n\n", B->d);
+  B->DATA = NULL; compute_data(B);
+  L = calc_block(B, B->S->Z, cgetg(1,t_VEC), NULL);
+  if (DEBUGLEVEL>9)
+    err_printf("\nSubfields of degree %ld: %Ps\n", B->d, L? L: cgetg(1,t_VEC));
+  if (isclone(B->DATA)) gunclone(B->DATA);
+  avma = av; return L;
+}
+
+static GEN
+fix_var(GEN x, long v)
+{
+  long i, l = lg(x);
+  if (!v) return x;
+  for (i=1; i<l; i++) { GEN t = gel(x,i); setvarn(t[1],v); setvarn(t[2],v); }
+  return x;
+}
+
+static void
+subfields_poldata(GEN T, poldata *PD)
+{
+  GEN  nf,L,dis;
+
+  T = leafcopy(get_nfpol(T, &nf));
+  PD->pol = T; setvarn(T, 0);
+  if (nf)
+  {
+    PD->den = Q_denom(nf_get_zk(nf));
+    PD->roo = nf_get_roots(nf);
+    PD->dis = mulii(absi(nf_get_disc(nf)), sqri(nf_get_index(nf)));
+  }
+  else
+  {
+    PD->den = initgaloisborne(T,NULL,nbits2prec(bit_accuracy(ZX_max_lg(T))), &L,NULL,&dis);
+    PD->roo = L;
+    PD->dis = absi(dis);
+  }
+}
+
+static GEN
+subfieldsall(GEN nf)
+{
+  pari_sp av = avma;
+  long N, ld, i, v0;
+  GEN G, pol, dg, LSB, NLSB;
+  poldata PD;
+  primedata S;
+  blockdata B;
+
+  /* much easier if nf is Galois (WSS) */
+  G = galoisinit(nf, NULL);
+  if (G != gen_0)
+  {
+    GEN L, S, p;
+    long l;
+
+    pol = get_nfpol(nf, &nf);
+    L = lift_intern( galoissubfields(G, 0, varn(pol)) );
+    l = lg(L);
+    S = cgetg(l, t_VECSMALL);
+    for (i=1; i<l; i++) S[i] = lg(gmael(L,i,1));
+    p = vecsmall_indexsort(S);
+    return gerepilecopy(av,  vecpermute(L, p));
+  }
+
+  subfields_poldata(nf, &PD);
+  pol = PD.pol;
+
+  v0 = varn(pol); N = degpol(pol);
+  dg = divisorsu(N); ld = lg(dg)-1;
+  if (DEBUGLEVEL) err_printf("\n***** Entering subfields\n\npol = %Ps\n",pol);
+
+  LSB = _subfield(pol_x(0), gen_0);
+  if (ld > 2)
+  {
+    B.PD = &PD;
+    B.S  = &S;
+    B.N  = N;
+    choose_prime(&S, PD.pol, PD.dis);
+    for (i=ld-1; i>1; i--)
+    {
+      B.size  = dg[i];
+      B.d = N / B.size;
+      NLSB = subfields_of_given_degree(&B);
+      if (NLSB) { LSB = concat(LSB, NLSB); gunclone(NLSB); }
+    }
+    (void)delete_var(); /* from choose_prime */
+  }
+  LSB = shallowconcat(LSB, _subfield(pol, pol_x(0)));
+  if (DEBUGLEVEL) err_printf("\n***** Leaving subfields\n\n");
+  return fix_var(gerepilecopy(av, LSB), v0);
+}
+
+GEN
+nfsubfields(GEN nf, long d)
+{
+  pari_sp av = avma;
+  long N, v0;
+  GEN LSB, pol, G;
+  poldata PD;
+  primedata S;
+  blockdata B;
+
+  if (!d) return subfieldsall(nf);
+
+  pol = get_nfpol(nf, &nf); /* in order to treat trivial cases */
+  RgX_check_ZX(pol,"nfsubfields");
+  v0 = varn(pol); N = degpol(pol);
+  if (d == N) return gerepilecopy(av, _subfield(pol, pol_x(v0)));
+  if (d == 1) return gerepilecopy(av, _subfield(pol_x(v0), pol));
+  if (d < 1 || d > N || N % d) return cgetg(1,t_VEC);
+
+  /* much easier if nf is Galois (WSS) */
+  G = galoisinit(nf? nf: pol, NULL);
+  if (G != gen_0)
+  { /* Bingo */
+    GEN L = galoissubgroups(G), F;
+    long k,i, l = lg(L), o = N/d;
+    F = cgetg(l, t_VEC);
+    k = 1;
+    for (i=1; i<l; i++)
+    {
+      GEN H = gel(L,i);
+      if (group_order(H) == o)
+        gel(F,k++) = lift_intern(galoisfixedfield(G, gel(H,1), 0, v0));
+    }
+    setlg(F, k);
+    return gerepilecopy(av, F);
+  }
+
+  subfields_poldata(nf? nf: pol, &PD);
+
+  B.PD = &PD;
+  B.S  = &S;
+  B.N  = N;
+  B.d  = d;
+  B.size = N/d;
+
+  choose_prime(&S, PD.pol, PD.dis);
+  LSB = subfields_of_given_degree(&B);
+  (void)delete_var(); /* from choose_prime */
+  avma = av;
+  if (!LSB) return cgetg(1, t_VEC);
+  G = gcopy(LSB); gunclone(LSB);
+  return fix_var(G, v0);
+}
diff --git a/src/modules/thue.c b/src/modules/thue.c
new file mode 100644
index 0000000..e324305
--- /dev/null
+++ b/src/modules/thue.c
@@ -0,0 +1,1384 @@
+/* Copyright (C) 2000  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#include "pari.h"
+#include "paripriv.h"
+
+/********************************************************************/
+/**                                                                **/
+/**             THUE EQUATION SOLVER (G. Hanrot)                   **/
+/**                                                                **/
+/********************************************************************/
+/* In all the forthcoming remarks, "paper" designs the paper "Thue Equations of
+ * High Degree", by Yu. Bilu and G. Hanrot, J. Number Theory (1996). The numbering
+ * of the constants corresponds to Hanrot's thesis rather than to the paper */
+
+/* Check whether tnf is a valid structure */
+static int
+checktnf(GEN tnf)
+{
+  long l = lg(tnf);
+  if (typ(tnf)!=t_VEC || (l!=8 && l!=3)) return 0;
+  if (typ(gel(tnf,1)) != t_VEC) return 0;
+  if (l != 8) return 1; /* S=0 */
+
+  (void)checkbnf(gel(tnf,2));
+  return (typ(gel(tnf,3)) == t_COL
+       && typ(gel(tnf,4)) == t_COL
+       && typ(gel(tnf,5)) == t_MAT
+       && typ(gel(tnf,6)) == t_MAT
+       && typ(gel(tnf,7)) == t_VEC);
+}
+
+static GEN
+distoZ(GEN z)
+{
+  GEN t = gfrac(z);
+  return gmin(t, gsubsg(1,t));
+}
+
+/* Compensates rounding errors for computation/display of the constants.
+ * Round up if dir > 0, down otherwise */
+static GEN
+myround(GEN x, long dir)
+{
+  GEN eps = powis(stoi(dir > 0? 10: -10), -10);
+  return gmul(x, gadd(gen_1, eps));
+}
+
+/* v a t_VEC/t_VEC */
+static GEN
+vecmax_shallow(GEN v) { return gel(v, vecindexmax(v)); }
+
+static GEN
+tnf_get_roots(GEN poly, long prec, long S, long T)
+{
+  GEN R0 = QX_complex_roots(poly, prec), R = cgetg(lg(R0), t_COL);
+  long k;
+
+  for (k=1; k<=S; k++) gel(R,k) = gel(R0,k);
+  /* swap roots to get the usual order */
+  for (k=1; k<=T; k++)
+  {
+    gel(R,k+S)  = gel(R0,2*k+S-1);
+    gel(R,k+S+T)= gel(R0,2*k+S);
+  }
+  return R;
+}
+
+/* Computation of the logarithmic height of x (given by embeddings) */
+static GEN
+LogHeight(GEN x, long prec)
+{
+  long i, n = lg(x)-1;
+  GEN LH = gen_1;
+  for (i=1; i<=n; i++) LH = gmul(LH, gmax(gen_1, gabs(gel(x,i), prec)));
+  return gdivgs(glog(LH,prec), n);
+}
+
+/* |x|^(1/n), x t_INT */
+static GEN
+absisqrtn(GEN x, long n, long prec)
+{ GEN r = itor(x,prec); setabssign(r); return sqrtnr(r, n); }
+
+static GEN
+get_emb(GEN x, GEN r)
+{
+  long l = lg(r), i;
+  GEN y;
+
+  if (typ(x) == t_INT) return const_col(l-1, x);
+  y = cgetg(l, t_COL);
+  for (i=1; i<l; i++)
+  {
+    GEN e = poleval(x, gel(r,i));
+    if (gequal0(e) || (typ(e) != t_INT && precision(e) == 3)) return NULL;
+    gel(y,i) = e;
+  }
+  return y;
+}
+
+/* Computation of the conjugates (sigma_i(v_j)), and log. heights, of elts of v */
+static GEN
+Conj_LH(GEN v, GEN *H, GEN r, long prec)
+{
+  long j, l = lg(v);
+  GEN e, M = cgetg(l,t_MAT);
+
+  (*H) = cgetg(l,t_COL);
+  for (j = 1; j < l; j++)
+  {
+    if (! (e = get_emb(gel(v,j), r)) ) return NULL; /* FAIL */
+    gel(M,j) = e;
+    gel(*H,j) = LogHeight(e, prec);
+  }
+  return M;
+}
+
+static GEN abslog(GEN x, long prec) { return gabs(glog(x,prec), prec); }
+static GEN logabs(GEN x, long prec) { return glog(gabs(x,prec), prec); }
+
+/* Computation of M, its inverse A and precision check (see paper) */
+static GEN
+T_A_Matrices(GEN MatFU, long r, GEN *eps5, long prec)
+{
+  GEN A, p1, m1, IntM, nia, eps3, eps2;
+  long e = prec2nbits(prec);
+
+  m1 = rowslice(vecslice(MatFU, 1,r), 1,r); /* minor order r */
+  m1 = logabs(m1, 3);
+
+  A = RgM_inv(m1); if (!A) pari_err_PREC("thue");
+  IntM = RgM_Rg_add(RgM_mul(A,m1), gen_m1);
+
+  eps2 = gadd(vecmax(gabs(IntM, 3)), real2n(-e, LOWDEFAULTPREC)); /* t_REAL */
+  nia = vecmax(gabs(A, 3));
+  if (typ(nia) != t_REAL) nia = gtofp(nia, LOWDEFAULTPREC);
+
+  /* Check for the precision in matrix inversion. See paper, Lemma 2.4.2. */
+  p1 = addrr(mulsr(r, gmul2n(nia, e)), eps2); /* t_REAL */
+  if (expo(p1) < -2*r) pari_err_PREC("thue");
+
+  p1 = addrr(mulsr(r, gmul2n(nia,-e)), eps2);
+  eps3 = mulrr(mulsr(2*r*r,nia), p1);
+  if (!signe(eps3))
+    eps3 = real2n(expo(eps3), LOWDEFAULTPREC);
+  else
+    eps3 = myround(eps3, 1);
+
+  if (DEBUGLEVEL>1) err_printf("epsilon_3 -> %Ps\n",eps3);
+  *eps5 = mulur(r, eps3); return A;
+}
+
+/* Performs basic computations concerning the equation.
+ * Returns a "tnf" structure containing
+ *  1) the polynomial
+ *  2) the bnf (used to solve the norm equation)
+ *  3) roots, with presumably enough precision
+ *  4) The logarithmic heights of units
+ *  5) The matrix of conjugates of units
+ *  6) its inverse
+ *  7) a few technical constants */
+static GEN
+inithue(GEN P, GEN bnf, long flag, long prec)
+{
+  GEN MatFU, x0, tnf, tmp, gpmin, dP, csts, ALH, eps5, ro, c1, c2, Ind = gen_1;
+  long k,j, n = degpol(P);
+  long s,t, prec_roots;
+
+  if (!bnf)
+  {
+    bnf = Buchall(P, nf_FORCE, DEFAULTPREC);
+    if (flag) (void)bnfcertify(bnf);
+    else
+      Ind = floorr(mulru(bnf_get_reg(bnf), 5));
+  }
+
+  nf_get_sign(bnf_get_nf(bnf), &s, &t);
+  prec_roots = prec;
+  for(;;)
+  {
+    ro = tnf_get_roots(P, prec_roots, s, t);
+    MatFU = Conj_LH(bnf_get_fu(bnf), &ALH, ro, prec);
+    if (MatFU) break;
+    prec_roots = precdbl(prec_roots);
+    if (DEBUGLEVEL>1) pari_warn(warnprec, "inithue", prec_roots);
+  }
+
+  dP = ZX_deriv(P);
+  c1 = NULL; /* min |P'(r_i)|, i <= s */
+  for (k=1; k<=s; k++)
+  {
+    tmp = gabs(poleval(dP,gel(ro,k)),prec);
+    if (!c1 || gcmp(tmp,c1) < 0) c1 = tmp;
+  }
+  c1 = gdiv(int2n(n-1), c1);
+  c1 = gprec_w(myround(c1, 1), DEFAULTPREC);
+
+  c2 = NULL; /* max |r_i - r_j|, i!=j */
+  for (k=1; k<=n; k++)
+    for (j=k+1; j<=n; j++)
+    {
+      tmp = gabs(gsub(gel(ro,j),gel(ro,k)), prec);
+      if (!c2 || gcmp(c2,tmp) > 0) c2 = tmp;
+    }
+  c2 = gprec_w(myround(c2, -1), DEFAULTPREC);
+
+  if (t==0)
+    x0 = real_1(DEFAULTPREC);
+  else
+  {
+    gpmin = NULL; /* min |P'(r_i)|, i > s */
+    for (k=1; k<=t; k++)
+    {
+      tmp = gabs(poleval(dP,gel(ro,s+k)), prec);
+      if (!gpmin || gcmp(tmp,gpmin) < 0) gpmin = tmp;
+    }
+    gpmin = gprec_w(gpmin, DEFAULTPREC);
+
+    /* Compute x0. See paper, Prop. 2.2.1 */
+    x0 = gmul(gpmin, vecmax_shallow(gabs(imag_i(ro), prec)));
+    x0 = sqrtnr(gdiv(int2n(n-1), x0), n);
+  }
+  if (DEBUGLEVEL>1)
+    err_printf("c1 = %Ps\nc2 = %Ps\nIndice <= %Ps\n", c1, c2, Ind);
+
+  ALH = gmul2n(ALH, 1);
+  tnf = cgetg(8,t_VEC); csts = cgetg(8,t_VEC);
+  gel(tnf,1) = P;
+  gel(tnf,2) = bnf;
+  gel(tnf,3) = ro;
+  gel(tnf,4) = ALH;
+  gel(tnf,5) = MatFU;
+  gel(tnf,6) = T_A_Matrices(MatFU, s+t-1, &eps5, prec);
+  gel(tnf,7) = csts;
+  gel(csts,1) = c1; gel(csts,2) = c2;   gel(csts,3) = LogHeight(ro, prec);
+  gel(csts,4) = x0; gel(csts,5) = eps5; gel(csts,6) = utoipos(prec);
+  gel(csts,7) = Ind;
+  return tnf;
+}
+
+typedef struct {
+  GEN c10, c11, c13, c15, bak, NE, ALH, Ind, hal, MatFU, ro, Hmu;
+  GEN delta, lambda, inverrdelta;
+  long r, iroot, deg;
+} baker_s;
+
+/* Compute Baker's bound c9 and B_0, the bound for the b_i's. See Thm 2.3.1 */
+static GEN
+Baker(baker_s *BS)
+{
+  const long prec = DEFAULTPREC;
+  GEN tmp, B0, hb0, c9 = gen_1, ro = BS->ro, ro0 = gel(ro,BS->iroot);
+  long k, i1, i2, r = BS->r;
+
+  switch (BS->iroot) {
+    case 1: i1=2; i2=3; break;
+    case 2: i1=1; i2=3; break;
+   default: i1=1; i2=2; break;
+  }
+
+  /* Compute h_1....h_r */
+  for (k=1; k<=r; k++)
+  {
+    tmp = gdiv(gcoeff(BS->MatFU,i1,k), gcoeff(BS->MatFU,i2,k));
+    tmp = gmax(gen_1, abslog(tmp,prec));
+    c9 = gmul(c9, gmax(gel(BS->ALH,k), gdiv(tmp, BS->bak)));
+  }
+
+  /* Compute a bound for the h_0 */
+  hb0 = gadd(gmul2n(BS->hal,2), gmul2n(gadd(BS->Hmu,mplog2(prec)), 1));
+  tmp = gdiv(gmul(gsub(ro0, gel(ro,i2)), gel(BS->NE,i1)),
+             gmul(gsub(ro0, gel(ro,i1)), gel(BS->NE,i2)));
+  tmp = gmax(gen_1, abslog(tmp, prec));
+  hb0 = gmax(hb0, gdiv(tmp, BS->bak));
+  c9 = gmul(c9,hb0);
+  /* Multiply c9 by the "constant" factor */
+  c9 = gmul(c9, gmul(mulri(mulur(18,mppi(prec)), int2n(5*(4+r))),
+                     gmul(gmul(mpfact(r+3), powiu(muliu(BS->bak,r+2), r+3)),
+                          glog(muliu(BS->bak,2*(r+2)),prec))));
+  c9 = gprec_w(myround(c9, 1), DEFAULTPREC);
+  /* Compute B0 according to Lemma 2.3.3 */
+  B0 = mulir(shifti(BS->Ind,1),
+             divrr(addrr(mulrr(c9,mplog(divrr(mulir(BS->Ind, c9),BS->c10))),
+                         mplog(mulir(BS->Ind, BS->c11))),
+                   BS->c10));
+  B0 = gmax(B0, dbltor(2.71828183));
+  B0 = gmax(B0, mulrr(divir(BS->Ind, BS->c10),
+                      mplog(divrr(mulir(BS->Ind, BS->c11),
+                                  Pi2n(1, prec)))));
+
+  if (DEBUGLEVEL>1) {
+    err_printf("  B0  = %Ps\n",B0);
+    err_printf("  Baker = %Ps\n",c9);
+  }
+  return B0;
+}
+
+/* || x d ||, x t_REAL, d t_INT */
+static GEN
+errnum(GEN x, GEN d)
+{
+  GEN dx = mulir(d, x), D = subri(dx, roundr(dx));
+  setabssign(D); return D;
+}
+
+/* Try to reduce the bound through continued fractions; see paper. */
+static int
+CF_1stPass(GEN *B0, GEN kappa, baker_s *BS)
+{
+  GEN a, b, q, ql, qd, l0, denbound = mulri(*B0, kappa);
+
+  if (cmprr(mulrr(dbltor(0.1),sqrr(denbound)), BS->inverrdelta) > 0)
+    return -1;
+
+  q = denom( bestappr(BS->delta, denbound) );
+  qd = errnum(BS->delta, q);
+  ql = errnum(BS->lambda,q);
+
+  l0 = subrr(ql, addrr(mulrr(qd, *B0), divri(dbltor(0.1),kappa)));
+  if (signe(l0) <= 0) return 0;
+
+  if (BS->r > 1) {
+    a = BS->c15; b = BS->c13;
+  }
+  else {
+    a = BS->c11; b = BS->c10;
+    l0 = mulrr(l0, Pi2n(1, DEFAULTPREC));
+  }
+  *B0 = divrr(mplog(divrr(mulir(q,a), l0)), b);
+  if (DEBUGLEVEL>1) err_printf("    B0 -> %Ps\n",*B0);
+  return 1;
+}
+
+static void
+get_B0Bx(baker_s *BS, GEN l0, GEN *B0, GEN *Bx)
+{
+  GEN t = divrr(mulir(BS->Ind, BS->c15), l0);
+  *B0 = divrr(mulir(BS->Ind, mplog(t)), BS->c13);
+  *Bx = sqrtnr(shiftr(t,1), BS->deg);
+}
+
+static int
+LLL_1stPass(GEN *pB0, GEN kappa, baker_s *BS, GEN *pBx)
+{
+  GEN B0 = *pB0, Bx = *pBx, lllmat, C, l0, l1, triv;
+  long e;
+
+  C = grndtoi(mulir(mulii(BS->Ind, kappa),
+                    gpow(B0, dbltor(2.2), DEFAULTPREC)), &e);
+
+  if (DEBUGLEVEL > 1) err_printf("C (bitsize) : %d\n", expi(C));
+  lllmat = matid(3);
+  if (cmpri(B0, BS->Ind) > 0)
+  {
+    gcoeff(lllmat, 1, 1) = grndtoi(divri(B0, BS->Ind), &e);
+    triv = shiftr(sqrr(B0), 1);
+  }
+  else
+    triv = addir(sqri(BS->Ind), sqrr(B0));
+
+  gcoeff(lllmat, 3, 1) = roundr(negr(mulir(C, BS->lambda)));
+  gcoeff(lllmat, 3, 2) = roundr(negr(mulir(C, BS->delta)));
+  gcoeff(lllmat, 3, 3) = C;
+  lllmat = ZM_lll(lllmat, 0.99, LLL_IM|LLL_INPLACE);
+
+  l0 = gnorml2(gel(lllmat,1));
+  l0 = subrr(divir(l0, dbltor(1.8262)), triv); /* delta = 0.99 */
+  if (signe(l0) <= 0) return 0;
+
+  l1 = shiftr(addri(shiftr(B0,1), BS->Ind), -1);
+  l0 = divri(subrr(sqrtr(l0), l1), C);
+
+  if (signe(l0) <= 0) return 0;
+
+  get_B0Bx(BS, l0, &B0, &Bx);
+  if (DEBUGLEVEL>=2)
+  {
+    err_printf("LLL_First_Pass successful\n");
+    err_printf("B0 -> %Ps\n", B0);
+    err_printf("x <= %Ps\n", Bx);
+  }
+  *pB0 = B0; *pBx = Bx; return 1;
+}
+
+
+/* Check whether a solution has already been found */
+static int
+new_sol(GEN z, GEN S)
+{
+  long i, l = lg(S);
+  for (i=1; i<l; i++)
+    if (ZV_equal(z,gel(S,i))) return 0;
+  return 1;
+}
+
+/* add solution (x,y) if not already known */
+static void
+add_sol(GEN *pS, GEN x, GEN y)
+{
+  GEN u = mkvec2(x,y);
+  if (new_sol(u, *pS)) *pS = shallowconcat(*pS, mkvec(u));
+}
+/* z = P(p,q), d = deg P, |z| = |rhs|. Check signs and (possibly)
+ * add solutions (p,q), (-p,-q) */
+static void
+add_pm(GEN *pS, GEN p, GEN q, GEN z, long d, GEN rhs)
+{
+  if (signe(z) == signe(rhs))
+  {
+    add_sol(pS, p, q);
+    if (!odd(d)) add_sol(pS, negi(p), negi(q));
+  }
+  else
+    if (odd(d))  add_sol(pS, negi(p), negi(q));
+}
+
+/* Check whether a potential solution is a true solution. Return 0 if
+ * truncation error (increase precision) */
+static int
+CheckSol(GEN *pS, GEN z1, GEN z2, GEN P, GEN rhs, GEN ro)
+{
+  GEN x, y, ro1 = gel(ro,1), ro2 = gel(ro,2);
+  long e;
+
+  y = grndtoi(real_i(gdiv(gsub(z2,z1), gsub(ro1,ro2))), &e);
+  if (e > 0) return 0;
+  if (!signe(y)) return 1; /* y = 0 taken care of in SmallSols */
+  x = gadd(z1, gmul(ro1, y));
+  x = grndtoi(real_i(x), &e);
+  if (e > 0) return 0;
+  if (e <= -13)
+  { /* y != 0 and rhs != 0; check whether P(x,y) = rhs or P(-x,-y) = rhs */
+    GEN z = poleval(RgX_rescale(P,y),x);
+    if (absi_equal(z, rhs)) add_pm(pS, x,y, z, degpol(P), rhs);
+  }
+  return 1;
+}
+
+/* find q1,q2,q3 st q1 + b q2 + c q3 ~ 0 */
+static GEN
+GuessQi(GEN b, GEN c, GEN *eps)
+{
+  const long shift = 33;
+  GEN Q, Lat, C = int2n(shift);
+
+  Lat = matid(3);
+  gcoeff(Lat,3,1) = ground(gmul2n(b, shift));
+  gcoeff(Lat,3,2) = ground(gmul2n(c, shift));
+  gcoeff(Lat,3,3) = C;
+
+  Q = gel(lllint(Lat),1);
+  if (gequal0(gel(Q,2))) return NULL; /* FAIL */
+
+  *eps = gadd(gadd(gel(Q,3), gmul(gel(Q,1),b)), gmul(gel(Q,2),c));
+  *eps = mpabs(*eps); return Q;
+}
+
+/* x a t_REAL */
+static GEN
+myfloor(GEN x) { return expo(x) > 30 ? ceil_safe(x): floorr(x); }
+
+/* Check for not-so-small solutions. Return a t_REAL or NULL */
+static GEN
+MiddleSols(GEN *pS, GEN bound, GEN roo, GEN poly, GEN rhs, long s, GEN c1)
+{
+  long j, k, nmax, d;
+  GEN bndcf;
+
+  if (expo(bound) < 0) return bound;
+  d = degpol(poly);
+  bndcf = sqrtnr(shiftr(c1,1), d - 2);
+  if (cmprr(bound, bndcf) < 0) return bound;
+  /* divide by log((1+sqrt(5))/2)
+   * 1 + ==> ceil
+   * 2 + ==> continued fraction is normalized if last entry is 1
+   * 3 + ==> start at a0, not a1 */
+  nmax = 3 + (long)(gtodouble(logr_abs(bound)) / 0.4812118250596);
+  bound = myfloor(bound);
+
+  for (k = 1; k <= s; k++)
+  {
+    GEN t = contfrac0(real_i(gel(roo,k)), NULL, nmax);
+    GEN pm1, qm1, p0, q0;
+
+    pm1 = gen_0; p0 = gen_1;
+    qm1 = gen_1; q0 = gen_0;
+
+    for (j = 1; j < lg(t); j++)
+    {
+      GEN p, q, z, Q, R;
+      p = addii(mulii(p0, gel(t,j)), pm1); pm1 = p0; p0 = p;
+      q = addii(mulii(q0, gel(t,j)), qm1); qm1 = q0; q0 = q;
+      if (cmpii(q, bound) > 0) break;
+      if (DEBUGLEVEL >= 2) err_printf("Checking (+/- %Ps, +/- %Ps)\n",p, q);
+
+      z = poleval(ZX_rescale(poly,q), p); /* = P(p/q) q^dep(P) */
+      Q = dvmdii(rhs, z, &R);
+      if (R != gen_0) continue;
+      setabssign(Q);
+      if (Z_ispowerall(Q, d, &Q))
+      {
+        if (!is_pm1(Q)) { p = mulii(p, Q); q = mulii(q, Q); }
+        add_pm(pS, p, q, z, d, rhs);
+      }
+    }
+    if (j == lg(t)) pari_err_BUG("Short continued fraction in thue");
+  }
+  return bndcf;
+}
+
+static void
+check_y_root(GEN *pS, GEN P, GEN Y)
+{
+  GEN r = nfrootsQ(P);
+  long j;
+  for (j = 1; j < lg(r); j++)
+    if (typ(gel(r,j)) == t_INT) add_sol(pS, gel(r,j), Y);
+}
+
+static void
+check_y(GEN *pS, GEN P, GEN poly, GEN Y, GEN rhs)
+{
+  long j, l = lg(poly);
+  GEN Yn = Y;
+  gel(P, l-1) = gel(poly, l-1);
+  for (j = l-2; j >= 2; j--)
+  {
+    gel(P,j) = mulii(Yn, gel(poly,j));
+    if (j > 2) Yn = mulii(Yn, Y);
+  }
+  gel(P,2) = subii(gel(P,2), rhs); /* P = poly(Y/y)*y^deg(poly) - rhs */
+  check_y_root(pS, P, Y);
+}
+
+/* Check for solutions under a small bound (see paper) */
+static GEN
+SmallSols(GEN S, GEN x3, GEN poly, GEN rhs)
+{
+  pari_sp av = avma, lim = stack_lim(av, 1);
+  GEN X, P, rhs2;
+  long j, l = lg(poly), n = degpol(poly);
+  ulong y, By;
+
+  x3 = myfloor(x3);
+
+  if (DEBUGLEVEL>1) err_printf("* Checking for small solutions <= %Ps\n", x3);
+  if (lgefint(x3) > 3)
+    pari_err_OVERFLOW(stack_sprintf("thue (SmallSols): y <= %Ps", x3));
+  By = itou(x3);
+  /* y = 0 first: solve X^n = rhs */
+  if (odd(n))
+  {
+    if (Z_ispowerall(absi(rhs), n, &X))
+      add_sol(&S, signe(rhs) > 0? X: negi(X), gen_0);
+  }
+  else if (signe(rhs) > 0 && Z_ispowerall(rhs, n, &X))
+  {
+    add_sol(&S, X, gen_0);
+    add_sol(&S, negi(X), gen_0);
+  }
+  rhs2 = shifti(rhs,1);
+  /* y != 0 */
+  P = cgetg(l, t_POL); P[1] = poly[1];
+  for (y = 1; y <= By; y++)
+  {
+    pari_sp av2 = avma;
+    long lS = lg(S);
+    GEN Y = utoipos(y);
+    /* try y */
+    check_y(&S, P, poly, Y, rhs);
+    /* try -y */
+    for (j = l-2; j >= 2; j -= 2) togglesign( gel(P,j) );
+    if (j == 0) gel(P,2) = subii(gel(P,2), rhs2);
+    check_y_root(&S, P, utoineg(y));
+    if (lS == lg(S)) { avma = av2; continue; } /* no solution found */
+
+    if (low_stack(lim,stack_lim(av,1)))
+    {
+      if(DEBUGMEM>1) pari_warn(warnmem,"SmallSols");
+      gerepileall(av, 2, &S, &rhs2);
+      P = cgetg(l, t_POL); P[1] = poly[1];
+    }
+  }
+  return S;
+}
+
+/* Computes [x]! */
+static double
+fact(double x)
+{
+  double ft = 1.0;
+  x = floor(x); while (x>1) { ft *= x; x--; }
+  return ft ;
+}
+
+static GEN
+RgX_homogenize(GEN P, long v)
+{
+  GEN Q = leafcopy(P);
+  long i, l = lg(P), d = degpol(P);
+  for (i = 2; i < l; i++) gel(Q,i) = monomial(gel(Q,i), d--, v);
+  return Q;
+}
+
+/* Compute all relevant constants needed to solve the equation P(x,y)=a given
+ * the solutions of N_{K/Q}(x)=a (see inithue). */
+GEN
+thueinit(GEN pol, long flag, long prec)
+{
+  GEN POL, C, L, fa, tnf, bnf = NULL;
+  pari_sp av = avma;
+  long k, s, lfa, dpol;
+
+  if (checktnf(pol)) { bnf = checkbnf(gel(pol,2)); pol = gel(pol,1); }
+  if (typ(pol)!=t_POL) pari_err_TYPE("thueinit",pol);
+  dpol = degpol(pol);
+  if (dpol <= 0) pari_err_CONSTPOL("thueinit");
+  RgX_check_ZX(pol, "thueinit");
+  if (varn(pol)) { pol = leafcopy(pol); setvarn(pol, 0); }
+  /* POL monic: POL(x) = C pol(x/L), L integer */
+  POL = ZX_primitive_to_monic(Q_primpart(pol), &L);
+  C = gdiv(powiu(L, dpol), gel(pol, dpol+2));
+  pol = POL;
+
+  fa = ZX_factor(pol);
+  lfa = lgcols(fa);
+  if (lfa > 2 || itos(gcoeff(fa,1,2)) > 1)
+  { /* reducible polynomial */
+    GEN P, Q, R, g, f = gcoeff(fa,1,1), E = gcoeff(fa,1,2);
+    long e = itos(E);
+    long vy = fetch_var();
+    long va = fetch_var();
+    long vb = fetch_var();
+    if (e != 1)
+    {
+      if (lfa == 2) {
+        tnf = mkvec2(mkvec3(pol,C,L), mkvec2(thueinit(f, flag, prec), E));
+        delete_var(); delete_var(); delete_var();
+        return gerepilecopy(av, tnf);
+      }
+      P = gpowgs(f,e);
+    }
+    else
+      P = f;
+    g = RgX_div(pol, P);
+    P = RgX_Rg_sub(RgX_homogenize(f, vy), pol_x(va));
+    Q = RgX_Rg_sub(RgX_homogenize(g, vy), pol_x(vb));
+    R = polresultant0(P, Q, -1, 0);
+    tnf = mkvec2(mkvec3(pol,C,L), mkvec2(mkvecsmall4(degpol(f), e, va,vb),  R));
+    delete_var(); delete_var(); delete_var();
+    return gerepilecopy(av, tnf);
+  }
+
+  if (dpol <= 2) pari_err_DOMAIN("thue", "degree","<=",gen_2,pol);
+  s = sturm(pol);
+  if (s)
+  {
+    long PREC, n = degpol(pol);
+    double d, dr, dn = (double)n;
+
+    dr = (double)((s+n-2)>>1); /* s+t-1 */
+    d = dn*(dn-1)*(dn-2);
+    /* Guess precision by approximating Baker's bound. The guess is most of
+     * the time not sharp, ie 10 to 30 decimal digits above what is _really_
+     * necessary. Note that the limiting step is the reduction. See paper. */
+    PREC = nbits2prec((long)((5.83 + (dr+4)*5 + log(fact(dr+3)) + (dr+3)*log(dr+2) +
+                     (dr+3)*log(d) + log(log(2*d*(dr+2))) + (dr+1))
+                     /10.)*32+32);
+
+    if (flag == 0) PREC = (long)(2.2 * PREC); /* Lazy, to be improved */
+    if (PREC < prec) PREC = prec;
+    if (DEBUGLEVEL >=2) err_printf("prec = %d\n", PREC);
+
+    for (;;)
+    {
+      if (( tnf = inithue(pol, bnf, flag, PREC) )) break;
+      PREC = precdbl(PREC);
+      if (DEBUGLEVEL>1) pari_warn(warnprec,"thueinit",PREC);
+      bnf = NULL; avma = av;
+    }
+  }
+  else
+  {
+    GEN ro = roots(pol, DEFAULTPREC), c0 = imag_i(gel(ro,1));
+    for (k=2; k<lg(ro); k++) c0 = mulrr(c0, imag_i(gel(ro,k)));
+    c0 = invr( absr(c0) ); tnf = mkvec2(pol, c0);
+  }
+  gel(tnf,1) = mkvec3(gel(tnf,1), C, L);
+  return gerepilecopy(av,tnf);
+}
+
+static void
+init_get_B(long i1, long i2, GEN Delta, GEN Lambda, GEN eps5, baker_s *BS,
+           long prec)
+{
+  GEN delta, lambda, inverrdelta;
+  if (BS->r > 1)
+  {
+    delta = divrr(gel(Delta,i2),gel(Delta,i1));
+    lambda = gdiv(gsub(gmul(gel(Delta,i2),gel(Lambda,i1)),
+                       gmul(gel(Delta,i1),gel(Lambda,i2))),
+                  gel(Delta,i1));
+    inverrdelta = divrr(subrr(mpabs(gel(Delta,i1)),eps5),
+                        mulrr(addsr(1,delta),eps5));
+  }
+  else
+  { /* r == 1, single fundamental unit (i1 = s = t = 1) */
+    GEN p1, Pi2 = Pi2n(1, prec);
+    GEN fu = gel(BS->MatFU,1), ro = BS->ro;
+
+    p1 = gdiv(gel(fu,2), gel(fu,3));
+    delta = divrr(garg(p1,prec), Pi2);
+
+    p1 = gmul(gdiv(gsub(gel(ro,1), gel(ro,2)),
+                   gsub(gel(ro,1), gel(ro,3))),
+              gdiv(gel(BS->NE,3), gel(BS->NE,2)));
+    lambda = divrr(garg(p1,prec), Pi2);
+
+    inverrdelta = shiftr(gabs(gel(fu,2),prec), prec2nbits(prec)-1);
+  }
+  if (DEBUGLEVEL>1) err_printf("  inverrdelta = %Ps\n",inverrdelta);
+  BS->delta = delta;
+  BS->lambda = lambda;
+  BS->inverrdelta = inverrdelta;
+}
+
+static GEN
+get_B0(long i1, GEN Delta, GEN Lambda, GEN eps5, long prec, baker_s *BS)
+{
+  GEN B0 = Baker(BS);
+  long step = 0, i2 = (i1 == 1)? 2: 1;
+  for(;;) /* i2 from 1 to r unless r = 1 [then i2 = 2] */
+  {
+    init_get_B(i1,i2, Delta,Lambda,eps5, BS, prec);
+    if (DEBUGLEVEL>1) err_printf("  Entering CF...\n");
+    /* Reduce B0 as long as we make progress: newB0 < oldB0 - 0.1 */
+    for (;;)
+    {
+      GEN oldB0 = B0, kappa = utoipos(10);
+      long cf;
+
+      for (cf = 0; cf < 10; cf++, kappa = muliu(kappa,10))
+      {
+        int res = CF_1stPass(&B0, kappa, BS);
+        if (res < 0) return NULL; /* prec problem */
+        if (res) break;
+        if (DEBUGLEVEL>1) err_printf("CF failed. Increasing kappa\n");
+      }
+      if (!step && cf == 10)
+      { /* Semirational or totally rational case */
+        GEN Q, ep, q, l0, denbound;
+
+        if (! (Q = GuessQi(BS->delta, BS->lambda, &ep)) ) break;
+
+        denbound = gadd(B0, absi(gel(Q,1)));
+        q = denom( bestappr(BS->delta, denbound) );
+        l0 = subrr(errnum(BS->delta, q), ep);
+        if (signe(l0) <= 0) break;
+
+        B0 = divrr(mplog(divrr(mulir(gel(Q,2), BS->c15), l0)),  BS->c13);
+        if (DEBUGLEVEL>1) err_printf("Semirat. reduction: B0 -> %Ps\n",B0);
+      }
+      /* if no progress, stop */
+      if (gcmp(oldB0, gadd(B0,dbltor(0.1))) <= 0) return gmin(oldB0, B0);
+      else step++;
+    }
+    i2++; if (i2 == i1) i2++;
+    if (i2 > BS->r) break;
+  }
+  pari_err_BUG("thue (totally rational case)");
+  return NULL; /* not reached */
+}
+
+static GEN
+get_Bx_LLL(long i1, GEN Delta, GEN Lambda, GEN eps5, long prec, baker_s *BS)
+{
+  GEN B0 = Baker(BS), Bx = NULL;
+  long step = 0, i2 = (i1 == 1)? 2: 1;
+  for(;;) /* i2 from 1 to r unless r = 1 [then i2 = 2] */
+  {
+    init_get_B(i1,i2, Delta,Lambda,eps5, BS, prec);
+    if (DEBUGLEVEL>1) err_printf("  Entering LLL...\n");
+    /* Reduce B0 as long as we make progress: newB0 < oldB0 - 0.1 */
+    for (;;)
+    {
+      GEN oldBx = Bx, kappa = utoipos(10);
+      const long cfMAX = 10;
+      long cf;
+
+      for (cf = 0; cf < cfMAX; cf++, kappa = muliu(kappa,10))
+      {
+        int res = LLL_1stPass(&B0, kappa, BS, &Bx);
+        if (res) break;
+        if (DEBUGLEVEL>1) err_printf("LLL failed. Increasing kappa\n");
+      }
+
+      /* FIXME: TO BE COMPLETED */
+      if (!step && cf == cfMAX)
+      { /* Semirational or totally rational case */
+        GEN Q, ep, q, l0, denbound;
+
+        if (! (Q = GuessQi(BS->delta, BS->lambda, &ep)) ) break;
+
+        /* Beware Q[2]] = gen_0 */
+        denbound = gadd(mulri(B0, absi(gel(Q,1))),
+                        mulii(BS->Ind, absi(gel(Q,2))));
+        q = denom( bestappr(BS->delta, denbound) );
+        l0 = divri(subrr(errnum(BS->delta, q), ep), absi(gel(Q,2)));
+        if (signe(l0) <= 0) break;
+
+        get_B0Bx(BS, l0, &B0, &Bx);
+        if (DEBUGLEVEL>1)
+          err_printf("Semirat. reduction: B0 -> %Ps x <= %Ps\n",B0, Bx);
+      }
+      /* if no progress, stop */
+      if (oldBx && gcmp(oldBx, Bx) <= 0) return oldBx; else step++;
+    }
+    i2++; if (i2 == i1) i2++;
+    if (i2 > BS->r) break;
+  }
+  pari_err_BUG("thue (totally rational case)");
+  return NULL; /* not reached */
+}
+
+static GEN
+LargeSols(GEN P, GEN tnf, GEN rhs, GEN ne, GEN *pS)
+{
+  GEN Vect, ro, bnf, MatFU, A, csts, dP, vecdP, Bx;
+  GEN c1,c2,c3,c4,c11,c14,c15, x0, x1, x2, x3, b, zp1, tmp, eps5;
+  long iroot, ine, n, i, r, upb, bi1, Prec, prec, s,t;
+  baker_s BS;
+  pari_sp av = avma;
+
+  bnf  = gel(tnf,2);
+  csts = gel(tnf,7);
+  if (!ne)
+  {
+    ne = bnfisintnorm(bnf, rhs);
+    if (DEBUGLEVEL)
+      if (!is_pm1(gel(csts, 7)) && !is_pm1(bnf_get_no(bnf)) && !is_pm1(rhs))
+        pari_warn(warner, "The result returned by 'thue' is conditional on the GRH");
+  }
+  else if (typ(ne) != t_VEC) pari_err_TYPE("thue",ne);
+  if (lg(ne)==1) return NULL;
+
+  nf_get_sign(bnf_get_nf(bnf), &s, &t);
+  BS.r = r = s+t-1; n = degpol(P);
+  ro     = gel(tnf,3);
+  BS.ALH = gel(tnf,4);
+  MatFU  = gel(tnf,5);
+  A      = gel(tnf,6);
+  c1     = gel(csts,1); c1 = gmul(absi(rhs), c1);
+  c2     = gel(csts,2);
+  BS.hal = gel(csts,3);
+  x0     = gel(csts,4);
+  eps5   = gel(csts,5);
+  Prec = gtolong(gel(csts,6));
+  BS.Ind = gel(csts,7);
+  BS.MatFU = MatFU;
+  BS.bak = mulss(n, (n-1)*(n-2)); /* safe */
+  BS.deg = n;
+
+  if (t) x0 = gmul(x0, absisqrtn(rhs, n, Prec));
+  tmp = divrr(c1,c2);
+  c3 = mulrr(dbltor(1.39), tmp);
+  c4 = mulur(n-1, c3);
+  x1 = gmax(x0, sqrtnr(shiftr(tmp,1),n));
+
+  Vect = gmul(gabs(A,DEFAULTPREC), const_col(r, gen_1));
+  c14 = mulrr(c4, vecmax_shallow(Vect));
+  x2 = gmax(x1, sqrtnr(mulur(10,c14), n));
+  if (DEBUGLEVEL>1) {
+    err_printf("x1 -> %Ps\n",x1);
+    err_printf("x2 -> %Ps\n",x2);
+    err_printf("c14 = %Ps\n",c14);
+  }
+
+  dP = ZX_deriv(P); vecdP = cgetg(s+1, t_VEC);
+  for (i=1; i<=s; i++) gel(vecdP,i) = poleval(dP, gel(ro,i));
+
+  zp1 = dbltor(0.01);
+  x3 = gmax(x2, sqrtnr(shiftr(divrr(c14,zp1),1),n));
+
+  b = cgetg(r+1,t_COL);
+  for (iroot=1; iroot<=s; iroot++)
+  {
+    GEN Delta, MatNE, Hmu, c5, c7;
+
+    Vect = const_col(r, gen_1);
+    if (iroot <= r) gel(Vect,iroot) = stoi(1-n);
+    Delta = RgM_RgC_mul(A,Vect);
+
+    c5 = vecmax_shallow(gabs(Delta,Prec));
+    c5  = myround(gprec_w(c5,DEFAULTPREC), 1);
+    c7  = mulur(r,c5);
+    BS.c10 = divur(n,c7);
+    BS.c13 = divur(n,c5);
+    if (DEBUGLEVEL>1) {
+      err_printf("* real root no %ld/%ld\n", iroot,s);
+      err_printf("  c10 = %Ps\n",BS.c10);
+      err_printf("  c13 = %Ps\n",BS.c13);
+    }
+
+    prec = Prec;
+    for (;;)
+    {
+      if (( MatNE = Conj_LH(ne, &Hmu, ro, prec) )) break;
+      prec = precdbl(prec);
+      if (DEBUGLEVEL>1) pari_warn(warnprec,"thue",prec);
+      ro = tnf_get_roots(P, prec, s, t);
+    }
+    BS.ro    = ro;
+    BS.iroot = iroot;
+
+    for (ine=1; ine<lg(ne); ine++)
+    {
+      pari_sp av2 = avma;
+      GEN Lambda, B0, c6, c8;
+      GEN NE = gel(MatNE,ine), Vect2 = cgetg(r+1,t_COL);
+      long k, i1;
+
+      if (DEBUGLEVEL>1) err_printf("  - norm sol. no %ld/%ld\n",ine,lg(ne)-1);
+      for (k=1; k<=r; k++)
+      {
+        if (k == iroot)
+          tmp = gdiv(rhs, gmul(gel(vecdP,k), gel(NE,k)));
+        else
+          tmp = gdiv(gsub(gel(ro,iroot),gel(ro,k)), gel(NE,k));
+        gel(Vect2,k) = glog(gabs(tmp,prec), prec);
+      }
+      Lambda = RgM_RgC_mul(A,Vect2);
+
+      c6 = addrr(dbltor(0.1), vecmax_shallow(gabs(Lambda,DEFAULTPREC)));
+      c6 = myround(c6, 1);
+      c8 = addrr(dbltor(1.23), mulur(r,c6));
+      c11= mulrr(shiftr(c3,1) , mpexp(divrr(mulur(n,c8),c7)));
+      c15= mulrr(shiftr(c14,1), mpexp(divrr(mulur(n,c6),c5)));
+
+      if (DEBUGLEVEL>1) {
+        err_printf("  c6  = %Ps\n",c6);
+        err_printf("  c8  = %Ps\n",c8);
+        err_printf("  c11 = %Ps\n",c11);
+        err_printf("  c15 = %Ps\n",c15);
+      }
+      BS.c11 = c11;
+      BS.c15 = c15;
+      BS.NE = NE;
+      BS.Hmu = gel(Hmu,ine);
+
+      i1 = vecindexmax(gabs(Delta,prec));
+      if (is_pm1(BS.Ind))
+      {
+        if (! (B0 = get_B0(i1, Delta, Lambda, eps5, prec, &BS)) ) goto PRECPB;
+      }
+      else
+      {
+        if (! (Bx = get_Bx_LLL(i1, Delta, Lambda, eps5, prec, &BS)) ) goto PRECPB;
+        x3 = gerepileupto(av2, gmax(Bx, x3));
+        continue;
+      }
+     /* For each possible value of b_i1, compute the b_i's
+      * and 2 conjugates of z = x - alpha y. Then check. */
+      upb = gtolong(gceil(B0));
+      for (bi1=-upb; bi1<=upb; bi1++)
+      {
+        GEN z1, z2;
+        for (i=1; i<=r; i++)
+        {
+          gel(b,i) = gdiv(gsub(gmul(gel(Delta,i), stoi(bi1)),
+                               gsub(gmul(gel(Delta,i),gel(Lambda,i1)),
+                                    gmul(gel(Delta,i1),gel(Lambda,i)))),
+                          gel(Delta,i1));
+          if (gcmp(distoZ(gel(b,i)), zp1) > 0) break;
+        }
+        if (i <= r) continue;
+
+        z1 = z2 = gen_1;
+        for(i=1; i<=r; i++)
+        {
+          GEN c = ground(gel(b,i));
+          z1 = gmul(z1, powgi(gcoeff(MatFU,1,i), c));
+          z2 = gmul(z2, powgi(gcoeff(MatFU,2,i), c));
+        }
+        z1 = gmul(z1, gel(NE,1));
+        z2 = gmul(z2, gel(NE,2));
+        if (!CheckSol(pS, z1,z2,P,rhs,ro)) goto PRECPB;
+      }
+    }
+  }
+  return gmax(x0, MiddleSols(pS, x3, ro, P, rhs, s, c1));
+
+PRECPB:
+  ne = gerepilecopy(av, ne);
+  prec += 5 * (DEFAULTPREC-2);
+  if (DEBUGLEVEL>1) pari_warn(warnprec,"thue",prec);
+  tnf = inithue(P, bnf, 0, prec);
+  return LargeSols(P, tnf, rhs, ne, pS);
+}
+
+/* restrict to solutions (x,y) with L | x, replacing each by (x/L, y) */
+static GEN
+filter_sol_x(GEN S, GEN L)
+{
+  long i, k, l;
+  if (is_pm1(L)) return S;
+  l = lg(S); k = 1;
+  for (i = 1; i < l; i++)
+  {
+    GEN s = gel(S,i), r;
+    gel(s,1) = dvmdii(gel(s,1), L, &r);
+    if (r == gen_0) gel(S, k++) = s;
+  }
+  setlg(S, k); return S;
+}
+
+static GEN
+sol_0(void)
+{ GEN S = cgetg(2, t_VEC); gel(S,1) = mkvec2(gen_0,gen_0); return S; }
+
+/* Given a tnf structure as returned by thueinit, a RHS and
+ * optionally the solutions to the norm equation, returns the solutions to
+ * the Thue equation F(x,y)=a */
+GEN
+thue(GEN tnf, GEN rhs, GEN ne)
+{
+  pari_sp av = avma;
+  GEN POL, C, L, x3, S;
+
+  if (typ(tnf) == t_POL) tnf = thueinit(tnf, 0, DEFAULTPREC);
+  if (!checktnf(tnf)) pari_err_TYPE("thue [please apply thueinit()]", tnf);
+  if (typ(rhs) != t_INT) pari_err_TYPE("thue",rhs);
+
+  /* solve P(x,y) = rhs <=> POL(L x, y) = C rhs, with POL monic in Z[X] */
+  POL = gel(tnf,1);
+  C = gel(POL,2); rhs = gmul(C, rhs);
+  if (typ(rhs) != t_INT) { avma = av; return cgetg(1, t_VEC); }
+  L = gel(POL,3);
+  POL = gel(POL,1);
+
+  S = cgetg(1,t_VEC);
+  if (lg(tnf) == 8)
+  {
+    if (!signe(rhs)) { avma = av; return sol_0(); }
+    x3 = LargeSols(POL, tnf, rhs, ne, &S);
+    if (!x3) { avma = (pari_sp)S; return S; }
+    S = SmallSols(S, x3, POL, rhs);
+  }
+  else if (typ(gel(tnf,2)) == t_REAL)
+  { /* Case s=0. All solutions are "small". */
+    GEN c0 = gel(tnf,2); /* t_REAL */
+    if (!signe(rhs)) { avma = av; return sol_0(); }
+    x3 = sqrtnr(mulir(absi(rhs),c0), degpol(POL));
+    x3 = addrr(x3, dbltor(0.1)); /* guard from round-off errors */
+    S = SmallSols(S, x3, POL, rhs);
+  }
+  else if (typ(gmael(tnf,2,1)) == t_VEC) /* reducible case, pure power*/
+  {
+    long e;
+    tnf = gel(tnf,2);
+    e = itos( gel(tnf,2) );
+    if (!signe(rhs)) { avma = av; return sol_0(); }
+
+    if (!Z_ispowerall(rhs, e, &rhs)) { avma = av; return cgetg(1, t_VEC); }
+    tnf = gel(tnf,1);
+    S = thue(tnf, rhs, NULL);
+    if (odd(e)) S = shallowconcat(S, thue(tnf, negi(rhs), NULL));
+  }
+  else if (typ(gel(tnf,2)) == t_VEC) /* other reducible cases */
+  { /* solve f^e * g = rhs, f irreducible factor of smallest degree */
+    GEN P, D, v = gmael(tnf, 2, 1), R = gmael(tnf, 2, 2);
+    long i, l, degf = v[1], e = v[2], va = v[3], vb = v[4];
+    if (!signe(rhs)) {
+      if (degf == 1) pari_err_DOMAIN("thue","#sols","=",strtoGENstr("oo"),rhs);
+      avma = av; return cgetg(1, t_VEC);
+    }
+    P = cgetg(lg(POL), t_POL); P[1] = POL[1];
+    D = divisors(rhs); l = lg(D);
+    for (i = 1; i < l; i++)
+    {
+      GEN Rab, ry, df = gel(D,i), dg = diviiexact(rhs, df);
+      long k;
+
+      if (e > 1 && !Z_ispowerall(df, e, &df)) continue;
+      /* Rab: univariate polynomial in Z[Y], whose roots are the possible y. */
+      /* Here and below, Rab != 0 */
+      Rab = gsubst(gsubst(R, va, df), vb, dg);
+      ry = nfrootsQ(Rab);
+      for (k = 1; k < lg(ry); k++)
+        if (typ(gel(ry,k)) == t_INT) check_y(&S, P, POL, gel(ry,k), rhs);
+      if (odd(e)) {
+        Rab = gsubst(gsubst(R, va, negi(df)), vb, negi(dg));
+        ry = nfrootsQ(Rab);
+        for (k = 1; k < lg(ry); k++)
+          if (typ(gel(ry,k)) == t_INT) check_y(&S, P, POL, gel(ry,k), rhs);
+      }
+    }
+  }
+  return gerepilecopy(av, filter_sol_x(S, L));
+}
+
+/********************************************************************/
+/**                                                                **/
+/**                      BNFISINTNORM (K. Belabas)                 **/
+/**                                                                **/
+/********************************************************************/
+struct sol_abs
+{
+  GEN rel; /* Primes PR[i] above a, expressed on generators of Cl(K) */
+  GEN partrel; /* list of vectors, partrel[i] = rel[1..i] * u[1..i] */
+  GEN cyc;     /* orders of generators of Cl(K) given in bnf */
+
+  long *f;     /* f[i] = f(PR[i]/p), inertia degree */
+  long *n;     /* a = prod p^{ n_p }. n[i]=n_p if PR[i] divides p */
+  long *next;  /* index of first P above next p, 0 if p is last */
+  long *S;     /* S[i] = n[i] - sum_{ 1<=k<=i } f[k]*u[k] */
+  long *u;     /* We want principal ideals I = prod PR[i]^u[i] */
+  GEN  normsol;/* lists of copies of the u[] which are solutions */
+
+  long nPR;    /* length(T->rel) = #PR */
+  long sindex, smax; /* current index in T->normsol; max. index */
+};
+
+/* u[1..i] has been filled. Norm(u) is correct.
+ * Check relations in class group then save it. */
+static void
+test_sol(struct sol_abs *T, long i)
+{
+  long k, l;
+  GEN s;
+
+  if (T->partrel && !ZV_dvd(gel(T->partrel, i),  T->cyc)) return;
+  if (T->sindex == T->smax)
+  { /* no more room in solution list: enlarge */
+    long new_smax = T->smax << 1;
+    GEN  new_normsol = new_chunk(new_smax+1);
+
+    for (k=1; k<=T->smax; k++) gel(new_normsol,k) = gel(T->normsol,k);
+    T->normsol = new_normsol; T->smax = new_smax;
+  }
+  gel(T->normsol, ++T->sindex) = s = cgetg_copy(T->u, &l);
+  for (k=1; k <= i; k++) s[k] = T->u[k];
+  for (   ; k < l;  k++) s[k] = 0;
+  if (DEBUGLEVEL>2)
+  {
+    err_printf("sol = %Ps\n",s);
+    if (T->partrel) err_printf("T->partrel = %Ps\n",T->partrel);
+    err_flush();
+  }
+}
+/* partrel[i] <-- partrel[i-1] + u[i] * rel[i] */
+static void
+fix_partrel(struct sol_abs *T, long i)
+{
+  pari_sp av = avma;
+  GEN part1 = gel(T->partrel,i);
+  GEN part0 = gel(T->partrel,i-1);
+  GEN rel = gel(T->rel, i);
+  ulong u = T->u[i];
+  long k, l = lg(part1);
+  for (k=1; k < l; k++)
+    affii(addii(gel(part0,k), muliu(gel(rel,k), u)), gel(part1,k));
+  avma = av;
+}
+
+/* Recursive loop. Suppose u[1..i] has been filled
+ * Find possible solutions u such that, Norm(prod PR[i]^u[i]) = a, taking
+ * into account:
+ *  1) the relations in the class group if need be.
+ *  2) the factorization of a. */
+static void
+isintnorm_loop(struct sol_abs *T, long i)
+{
+  if (T->S[i] == 0) /* sum u[i].f[i] = n[i], do another prime */
+  {
+    long k, next = T->next[i];
+    if (next == 0) { test_sol(T, i); return; } /* no primes left */
+
+    /* some primes left */
+    if (T->partrel) gaffect(gel(T->partrel,i), gel(T->partrel, next-1));
+    for (k=i+1; k < next; k++) T->u[k] = 0;
+    i = next-1;
+  }
+  else if (i == T->next[i]-2 || i == T->nPR-1)
+  { /* only one Prime left above prime; change prime, fix u[i+1] */
+    long q;
+    if (T->S[i] % T->f[i+1]) return;
+    q = T->S[i] / T->f[i+1];
+    i++; T->u[i] = q;
+    if (T->partrel) fix_partrel(T,i);
+    if (T->next[i] == 0) { test_sol(T,i); return; }
+  }
+
+  i++; T->u[i] = 0;
+  if (T->partrel) gaffect(gel(T->partrel,i-1), gel(T->partrel,i));
+  if (i == T->next[i-1])
+  { /* change prime */
+    if (T->next[i] == i+1 || i == T->nPR) /* only one Prime above p */
+    {
+      T->S[i] = 0;
+      T->u[i] = T->n[i] / T->f[i]; /* we already know this is exact */
+      if (T->partrel) fix_partrel(T, i);
+    }
+    else T->S[i] = T->n[i];
+  }
+  else T->S[i] = T->S[i-1]; /* same prime, different Prime */
+  for(;;)
+  {
+    isintnorm_loop(T, i);
+    T->S[i] -= T->f[i]; if (T->S[i] < 0) break;
+    T->u[i]++;
+    if (T->partrel) {
+      pari_sp av = avma;
+      gaffect(ZC_add(gel(T->partrel,i), gel(T->rel,i)), gel(T->partrel,i));
+      avma = av;
+    }
+  }
+}
+
+static int
+get_sol_abs(struct sol_abs *T, GEN bnf, GEN a, GEN *ptPR)
+{
+  GEN nf = bnf_get_nf(bnf);
+  GEN fact = absi_factor(a), P = gel(fact,1), E = gel(fact,2), PR;
+  long N = nf_get_degree(nf), nP = lg(P)-1, Ngen, max, nPR, i, j;
+
+  max = nP*N; /* upper bound for T->nPR */
+  T->f = new_chunk(max+1);
+  T->n = new_chunk(max+1);
+  T->next = new_chunk(max+1);
+  *ptPR = PR = cgetg(max+1, t_VEC); /* length to be fixed later */
+
+  nPR = 0;
+  for (i = 1; i <= nP; i++)
+  {
+    GEN L = idealprimedec(nf, gel(P,i));
+    long lL = lg(L), gcd, k, v;
+    ulong vn = itou(gel(E,i));
+
+    /* check that gcd_{P | p} f_P  divides  n_p */
+    gcd = pr_get_f(gel(L,1));
+    for (j=2; gcd > 1 && j < lL; j++) gcd = ugcd(gcd, pr_get_f(gel(L,j)));
+    if (gcd > 1 && vn % gcd)
+    {
+      if (DEBUGLEVEL>2)
+      { err_printf("gcd f_P  does not divide n_p\n"); err_flush(); }
+      return 0;
+    }
+    v = (i==nP)? 0: nPR + lL;
+    for (k = 1; k < lL; k++)
+    {
+      GEN pr = gel(L,k);
+      gel(PR, ++nPR) = pr;
+      T->f[nPR] = pr_get_f(pr) / gcd;
+      T->n[nPR] = vn / gcd;
+      T->next[nPR] = v;
+    }
+  }
+  T->nPR = nPR;
+  setlg(PR, nPR + 1);
+
+  T->u = cgetg(nPR+1, t_VECSMALL);
+  T->S = new_chunk(nPR+1);
+  T->cyc = bnf_get_cyc(bnf);
+  Ngen = lg(T->cyc)-1;
+  if (Ngen == 0)
+    T->rel = T->partrel = NULL; /* trivial Cl(K), no relations to check */
+  else
+  {
+    int triv = 1;
+    T->partrel = new_chunk(nPR+1);
+    T->rel = new_chunk(nPR+1);
+    for (i=1; i <= nPR; i++)
+    {
+      GEN c = isprincipal(bnf, gel(PR,i));
+      gel(T->rel,i) = c;
+      if (triv && !ZV_equal0(c)) triv = 0; /* non trivial relations in Cl(K)*/
+    }
+    /* triv = 1: all ideals dividing a are principal */
+    if (triv) T->rel = T->partrel = NULL;
+  }
+  if (T->partrel)
+  {
+    long B = ZV_max_lg(T->cyc) + 3;
+    for (i = 0; i <= nPR; i++)
+    { /* T->partrel[0] also needs to be initialized */
+      GEN c = cgetg(Ngen+1, t_COL); gel(T->partrel,i) = c;
+      for (j=1; j<=Ngen; j++)
+      {
+        GEN z = cgeti(B); gel(c,j) = z;
+        z[1] = evalsigne(0)|evallgefint(B);
+      }
+    }
+  }
+  T->smax = 511;
+  T->normsol = new_chunk(T->smax+1);
+  T->S[0] = T->n[1];
+  T->next[0] = 1;
+  T->sindex = 0;
+  isintnorm_loop(T, 0); return 1;
+}
+
+/* Look for unit of norm -1. Return 1 if it exists and set *unit, 0 otherwise */
+static long
+get_unit_1(GEN bnf, GEN *unit)
+{
+  GEN v, nf = bnf_get_nf(bnf);
+  long i, n = nf_get_degree(nf);
+
+  if (DEBUGLEVEL > 2) err_printf("looking for a fundamental unit of norm -1\n");
+  if (odd(n)) { *unit = gen_m1; return 1; }
+  v = nfsign_units(bnf, NULL, 0);
+  for (i = 1; i < lg(v); i++)
+    if ( Flv_sum( gel(v,i), 2) ) { *unit = gel(bnf_get_fu(bnf), i); return 1; }
+  return 0;
+}
+
+GEN
+bnfisintnormabs(GEN bnf, GEN a)
+{
+  struct sol_abs T;
+  GEN nf, res, PR;
+  long i;
+
+  if (typ(a) != t_INT) pari_err_TYPE("bnfisintnormabs",a);
+  bnf = checkbnf(bnf); nf = bnf_get_nf(bnf);
+  if (!signe(a)) return mkvec(gen_0);
+  if (is_pm1(a)) return mkvec(gen_1);
+
+  if (!get_sol_abs(&T, bnf, a, &PR)) return cgetg(1, t_VEC);
+  /* |a| > 1 => T.nPR > 0 */
+  res = cgetg(T.sindex+1, t_VEC);
+  for (i=1; i<=T.sindex; i++)
+  {
+    GEN x = vecsmall_to_col( gel(T.normsol,i) );
+    x = isprincipalfact(bnf, NULL, PR, x, nf_FORCE | nf_GEN_IF_PRINCIPAL);
+    gel(res,i) = coltoliftalg(nf, x); /* x solution, up to sign */
+  }
+  return res;
+}
+
+GEN
+bnfisintnorm(GEN bnf, GEN a)
+{
+  pari_sp av = avma;
+  GEN nf = checknf(bnf), T = nf_get_pol(nf), unit = NULL;
+  GEN z = bnfisintnormabs(bnf, a);
+  long sNx, i, j, N = degpol(T), l = lg(z), sa = signe(a);
+  long norm_1 = 0; /* gcc -Wall */
+
+  /* update z in place to get correct signs: multiply by unit of norm -1 if
+   * it exists, otherwise delete solution with wrong sign */
+  for (i = j = 1; i < l; i++)
+  {
+    GEN x = gel(z,i);
+    int xpol = (typ(x) == t_POL);
+
+    if (xpol) sNx = signe(ZX_resultant(T, Q_primpart(x)));
+    else      sNx = gsigne(x) < 0 && odd(N) ? -1 : 1;
+    if (sNx != sa)
+    {
+      if (! unit) norm_1 = get_unit_1(bnf, &unit);
+      if (!norm_1)
+      {
+        if (DEBUGLEVEL > 2) err_printf("%Ps eliminated because of sign\n",x);
+        continue;
+      }
+      if (xpol) x = (unit == gen_m1)? RgX_neg(x): RgXQ_mul(unit,x,T);
+      else      x = (unit == gen_m1)? gneg(x): RgX_Rg_mul(unit,x);
+    }
+    gel(z,j++) = x;
+  }
+  setlg(z, j);
+  return gerepilecopy(av, z);
+}
diff --git a/src/mt/mpi.c b/src/mt/mpi.c
new file mode 100644
index 0000000..d40d407
--- /dev/null
+++ b/src/mt/mpi.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+#include "mt.h"
+#include <mpi.h>
+
+static THREAD int pari_MPI_size, pari_MPI_rank;
+static THREAD long nbreq = 0;
+
+enum PMPI_cmd { PMPI_close, PMPI_worker, PMPI_work, PMPI_parisize,
+                PMPI_precreal, PMPI_eval };
+
+struct mt_mstate
+{
+  long n;
+  int source;
+  long nbint;
+  long *workid;
+};
+
+static struct mt_mstate pari_mt_data;
+static struct mt_mstate *pari_mt;
+
+static void
+send_long(long a, long dest)
+{
+  MPI_Send(&a, 1, MPI_LONG, dest, 0, MPI_COMM_WORLD);
+}
+
+static void
+send_request(enum PMPI_cmd ecmd, long dest)
+{
+  send_long((long)ecmd, dest);
+}
+
+static void
+send_GEN(GEN elt, int dest)
+{
+  pari_sp av = avma;
+  int size;
+  GEN reloc = copybin_unlink(elt);
+  GENbin *buf = copy_bin(mkvec2(elt,reloc));
+  size = sizeof(GENbin) + buf->len*sizeof(ulong);
+  MPI_Send(buf, size, MPI_CHAR, dest, 0, MPI_COMM_WORLD);
+  pari_free(buf); avma = av;
+}
+
+static void
+send_request_GEN(enum PMPI_cmd ecmd, GEN elt, int dest)
+{
+  send_request(ecmd, dest);
+  send_GEN(elt, dest);
+}
+
+static void
+send_request_long(enum PMPI_cmd ecmd, long elt, int dest)
+{
+  send_request(ecmd, dest);
+  send_long(elt, dest);
+}
+
+static long
+recvfrom_long(int src)
+{
+  long a;
+  MPI_Recv(&a, 1, MPI_LONG, src, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+  return a;
+}
+
+static enum PMPI_cmd
+recvfrom_request(int src)
+{
+  return (enum PMPI_cmd) recvfrom_long(src);
+}
+
+static GENbin *
+recvstatus_buf(int source, MPI_Status *status)
+{
+  int size;
+  GENbin *buf;
+
+  MPI_Get_count(status, MPI_CHAR, &size);
+  buf = (GENbin *)pari_malloc(size);
+  MPI_Recv(buf, size, MPI_CHAR, source, 0/* tag */,
+          MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+  return buf;
+}
+
+static GEN
+recvstatus_GEN(int source, MPI_Status *status)
+{
+  GENbin *buf = recvstatus_buf(source, status);
+  GEN res = bin_copy(buf);
+  bincopy_relink(gel(res,1),gel(res,2));
+  return gel(res,1);
+}
+
+static void
+recvstatus_void(int source, MPI_Status *status)
+{
+  GENbin *buf = recvstatus_buf(source, status);
+  free(buf);
+}
+
+static GEN
+recvfrom_GEN(int src)
+{
+  MPI_Status status;
+  MPI_Probe(src, 0, MPI_COMM_WORLD, &status);
+  return recvstatus_GEN(src, &status);
+}
+
+static GEN
+recvany_GEN(int *source)
+{
+  MPI_Status status;
+  MPI_Probe(MPI_ANY_SOURCE, 0 /* tag */, MPI_COMM_WORLD, &status);
+  *source = status.MPI_SOURCE;
+  return recvstatus_GEN(*source, &status);
+}
+
+static void
+recvany_void(int *source)
+{
+  MPI_Status status;
+  MPI_Probe(MPI_ANY_SOURCE, 0 /* tag */, MPI_COMM_WORLD, &status);
+  *source = status.MPI_SOURCE;
+  recvstatus_void(*source, &status);
+}
+
+static jmp_buf child_env;
+
+static void
+pari_MPI_child(void)
+{
+  pari_sp av = avma;
+  long size;
+  GEN worker = NULL, work, done;
+  struct gp_context rec;
+  if (!diffptr) initprimetable(500000);
+  gp_context_save(&rec);
+  if (setjmp(child_env))
+  {
+    send_GEN(pari_err_last(), 0);
+    gp_context_restore(&rec);
+  }
+  while (1)
+    switch (recvfrom_request(0))
+    {
+    case PMPI_worker:
+      avma = top;
+      worker = recvfrom_GEN(0);
+      av = avma;
+      break;
+    case PMPI_work:
+      work = recvfrom_GEN(0);
+      done = closure_callgenvec(worker, work);
+      send_GEN(done, 0);
+      avma = av;
+      break;
+    case PMPI_parisize:
+      size = recvfrom_long(0);
+      pari_init_stack(size,top-bot);
+      gp_context_save(&rec);
+      break;
+    case PMPI_precreal:
+      precreal = recvfrom_long(0);
+      break;
+    case PMPI_eval:
+      (void) closure_evalgen(recvfrom_GEN(0));
+      avma = av;
+      break;
+    case PMPI_close:
+      MPI_Barrier(MPI_COMM_WORLD);
+      MPI_Finalize();
+      exit(0);
+      break;
+    }
+}
+
+void
+mt_err_recover(long er)
+{
+  if (pari_MPI_rank) longjmp(child_env,er);
+}
+void mt_sigint_block(void) { }
+void mt_sigint_unblock(void) { }
+void mt_sigint(void) {}
+
+int
+mt_is_parallel(void)
+{
+  return !!pari_mt;
+}
+
+int
+mt_is_thread(void)
+{
+  return pari_MPI_rank;
+}
+
+void
+mt_broadcast(GEN code)
+{
+  long i;
+  if (!pari_MPI_rank && !pari_mt)
+    for (i=1;i<pari_MPI_size;i++)
+      send_request_GEN(PMPI_eval, code, i);
+}
+
+void
+pari_mt_init(void)
+{
+  int res = MPI_Init(0, NULL);
+  if (res == MPI_SUCCESS)
+  {
+    MPI_Comm_size(MPI_COMM_WORLD, &pari_MPI_size);
+    MPI_Comm_rank(MPI_COMM_WORLD, &pari_MPI_rank);
+    if (pari_MPI_rank) pari_MPI_child();
+    pari_mt_nbthreads = pari_MPI_size-1;
+  }
+  else
+  {
+    pari_MPI_size = 0;
+    pari_MPI_rank = 0;
+    pari_mt_nbthreads = 1;
+  }
+  pari_mt = NULL;
+}
+
+void
+pari_mt_close(void)
+{
+  long i;
+  if (!pari_MPI_rank)
+    for (i = 1; i < pari_MPI_size; i++)
+      send_request(PMPI_close, i);
+  MPI_Barrier(MPI_COMM_WORLD);
+  MPI_Finalize();
+}
+
+static GEN
+mtmpi_queue_get(struct mt_state *junk, long *workid, long *pending)
+{
+  struct mt_mstate *mt = pari_mt;
+  GEN done;
+  if (mt->nbint<=mt->n) { mt->source=mt->nbint; *pending = nbreq; return NULL; }
+  done = recvany_GEN(&mt->source);
+  nbreq--; *pending = nbreq;
+  if (workid) *workid = mt->workid[mt->source];
+  if (typ(done) == t_ERROR)
+    pari_err(0,done);
+  return done;
+}
+
+static void
+mtmpi_queue_submit(struct mt_state *junk, long workid, GEN work)
+{
+  struct mt_mstate *mt = pari_mt;
+  if (!work) { mt->nbint=mt->n+1; return; }
+  if (mt->nbint<=mt->n) mt->nbint++;
+  nbreq++;
+  mt->workid[mt->source] = workid;
+  send_request_GEN(PMPI_work, work, mt->source);
+}
+
+void
+mt_queue_reset(void)
+{
+  struct mt_mstate *mt = pari_mt;
+  if (DEBUGLEVEL>0 && nbreq)
+    pari_warn(warner,"%ld discarded threads (MPI)",nbreq);
+  for(  ;nbreq>0;  nbreq--) recvany_void(&mt->source);
+  pari_free(mt->workid);
+  pari_mt = NULL;
+}
+
+void
+mt_queue_start(struct pari_mt *pt, GEN worker)
+{
+  if (pari_mt || pari_MPI_size <= 2 || pari_mt_nbthreads <= 1)
+    return mtsingle_queue_start(pt, worker);
+  else
+  {
+    struct mt_mstate *mt = &pari_mt_data;
+    long i, n = minss(pari_mt_nbthreads, pari_MPI_size-1);
+    long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: top-bot;
+    pari_mt = mt;
+    mt->workid = (long*) pari_malloc(sizeof(long)*(n+1));
+    for (i=1; i <= n; i++)
+    {
+      send_request_long(PMPI_parisize, mtparisize, i);
+      send_request_long(PMPI_precreal, precreal, i);
+      send_request_GEN(PMPI_worker, worker, i);
+    }
+    mt->n = n;
+    mt->nbint = 1;
+    mt->source = 1;
+    pt->get=&mtmpi_queue_get;
+    pt->submit=&mtmpi_queue_submit;
+    pt->end=&mt_queue_reset;
+  }
+}
diff --git a/src/mt/mpi.h b/src/mt/mpi.h
new file mode 100644
index 0000000..32489dd
--- /dev/null
+++ b/src/mt/mpi.h
@@ -0,0 +1,16 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define MT_IS_THREAD   mt_is_thread()
+#define MT_SIGINT_BLOCK(block)
+#define MT_SIGINT_UNBLOCK(block)
diff --git a/src/mt/mt.c b/src/mt/mt.c
new file mode 100644
index 0000000..d4d9e6e
--- /dev/null
+++ b/src/mt/mt.c
@@ -0,0 +1,78 @@
+/* $Id$
+
+Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+#include "pari.h"
+#include "paripriv.h"
+#include "mt.h"
+
+static GEN
+mtsingle_queue_get(struct mt_state *mt, long *workid, long *pending)
+{
+  GEN done = mt->pending;
+  if (workid) *workid = mt->workid;
+  mt->pending = NULL; *pending = 0;
+  return done;
+}
+
+static void
+mtsingle_queue_submit(struct mt_state *mt, long workid, GEN work)
+{
+  mt->pending = work? closure_callgenvec(mt->worker, work): NULL;
+  mt->workid = workid;
+}
+
+static void
+mtsingle_queue_end(void) {  }
+
+void
+mtsingle_queue_start(struct pari_mt *pt, GEN worker)
+{
+  pt->get = mtsingle_queue_get;
+  pt->submit = mtsingle_queue_submit;
+  pt->end = mtsingle_queue_end;
+  pt->mt.worker = worker;
+  pt->mt.pending = NULL;
+}
+
+void
+mt_queue_end(struct pari_mt *pt) { pt->end(); }
+
+void
+mt_queue_submit(struct pari_mt *pt, long workid, GEN work)
+{ pt->submit(&pt->mt, workid, work); }
+
+GEN
+mt_queue_get(struct pari_mt *pt, long *workid, long *pending)
+{ return pt->get(&pt->mt, workid, pending); }
+
+
+void
+mtstate_save(long *pending)
+{
+  *pending = mt_is_parallel();
+}
+
+void
+mtstate_restore(long *pending)
+{
+  if (!*pending && mt_is_parallel())
+    mt_queue_reset();
+}
+
+void
+mtstate_reset(void)
+{
+  if (mt_is_parallel())
+    mt_queue_reset();
+}
diff --git a/src/mt/mt.h b/src/mt/mt.h
new file mode 100644
index 0000000..2ec6f10
--- /dev/null
+++ b/src/mt/mt.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+BEGINEXTERN
+
+void mtsingle_queue_start(struct pari_mt *pt, GEN worker);
+void mt_queue_reset(void);
+int  mt_is_parallel(void);
+
+ENDEXTERN
diff --git a/src/mt/pthread.c b/src/mt/pthread.c
new file mode 100644
index 0000000..455ab8d
--- /dev/null
+++ b/src/mt/pthread.c
@@ -0,0 +1,305 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+#include "mt.h"
+#include <pthread.h>
+
+struct mt_queue
+{
+  long no;
+  GEN input, output;
+  GEN worker;
+  long workid;
+  pthread_cond_t cond, cond1, cond2;
+  pthread_mutex_t mut, mut1, mut2;
+  pthread_cond_t *pcond;
+  pthread_mutex_t *pmut;
+};
+
+struct mt_pstate
+{
+  pthread_t *th;
+  struct pari_thread *pth;
+  struct mt_queue *mq;
+  long n, nbint, last;
+  long pending;
+  pthread_cond_t pcond;
+  pthread_mutex_t pmut;
+};
+
+static THREAD long mt_thread_no = -1;
+static struct mt_pstate *pari_mt;
+
+#define LOCK(x) pthread_mutex_lock(x); do
+#define UNLOCK(x) while(0); pthread_mutex_unlock(x)
+
+void
+mt_sigint_block(void)
+{
+  if (mt_thread_no>=0)
+    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
+}
+
+void
+mt_sigint_unblock(void)
+{
+  if (mt_thread_no>=0)
+    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
+}
+
+void
+mt_err_recover(long er)
+{
+  (void) er;
+  if (mt_thread_no>=0)
+  {
+    struct mt_pstate *mt = pari_mt;
+    struct mt_queue *mq = mt->mq+mt_thread_no;
+    LOCK(mq->pmut)
+    {
+      mq->output = pari_err_last();
+      pthread_cond_signal(mq->pcond);
+    } UNLOCK(mq->pmut);
+    pthread_exit((void*)1);
+  }
+}
+
+void
+mt_sigint(void)
+{
+  if (pari_mt) pthread_cond_broadcast(&pari_mt->pcond);
+}
+
+int
+mt_is_parallel(void)
+{
+  return !!pari_mt;
+}
+
+int
+mt_is_thread(void)
+{
+  return mt_thread_no>=0;
+}
+
+void mt_broadcast(GEN code) {(void) code;}
+
+void pari_mt_init(void)
+{
+  pari_mt = NULL;
+#ifdef _SC_NPROCESSORS_CONF
+  pari_mt_nbthreads = sysconf(_SC_NPROCESSORS_CONF);
+#else
+  pari_mt_nbthreads = 1;
+#endif
+}
+
+void pari_mt_close(void) { }
+
+static void*
+mt_queue_run(void *arg)
+{
+  GEN args = pari_thread_start((struct pari_thread*) arg);
+  pari_sp av = avma;
+  struct mt_queue *mq = (struct mt_queue *) args;
+  mt_thread_no = mq->no;
+  for(;;)
+  {
+    GEN work, done;
+    LOCK(&mq->mut)
+    {
+      while(!mq->input)
+        pthread_cond_wait(&mq->cond, &mq->mut);
+    } UNLOCK(&mq->mut);
+    work = gcopy(mq->input);
+    LOCK(&mq->mut1)
+    {
+      mq->input = NULL;
+      pthread_cond_signal(&mq->cond1);
+    } UNLOCK(&mq->mut1);
+    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
+    done = closure_callgenvec(mq->worker,work);
+    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
+    LOCK(mq->pmut)
+    {
+      mq->output = done;
+      pthread_cond_signal(mq->pcond);
+    } UNLOCK(mq->pmut);
+    LOCK(&mq->mut2)
+    {
+      while(mq->output)
+        pthread_cond_wait(&mq->cond2, &mq->mut2);
+    } UNLOCK(&mq->mut2);
+    avma = av;
+  }
+  return NULL;
+}
+
+static long
+mt_queue_check(struct mt_pstate *mt)
+{
+  long i;
+  for(i=0; i<mt->n; i++)
+  {
+    struct mt_queue *mq = mt->mq+i;
+    if (mq->output) return i;
+  }
+  return -1;
+}
+
+static GEN
+mtpthread_queue_get(struct mt_state *junk, long *workid, long *pending)
+{
+  struct mt_pstate *mt = pari_mt;
+  struct mt_queue *mq;
+  GEN done = NULL;
+  long last;
+  (void) junk;
+  if (mt->nbint<mt->n) { mt->last = mt->nbint; *pending = mt->pending; return NULL; }
+  BLOCK_SIGINT_START
+  LOCK(&mt->pmut)
+  {
+    while ((last = mt_queue_check(mt)) < 0)
+    {
+      pthread_cond_wait(&mt->pcond, &mt->pmut);
+      if (PARI_SIGINT_pending)
+      {
+        int sig = PARI_SIGINT_pending;
+        PARI_SIGINT_pending = 0;
+        pthread_mutex_unlock(&mt->pmut);
+        PARI_SIGINT_block = 0;
+        raise(sig);
+        PARI_SIGINT_block = 1;
+        pthread_mutex_lock(&mt->pmut);
+      }
+    }
+  } UNLOCK(&mt->pmut);
+  BLOCK_SIGINT_END
+  mq = mt->mq+last;
+  if (mq->output==err_e_STACK) pari_err(e_STACK);
+  done = gcopy(mq->output);
+  if (workid) *workid = mq->workid;
+  if (typ(done)==t_ERROR) pari_err(0,done);
+  BLOCK_SIGINT_START
+  LOCK(&mq->mut2)
+  {
+    mq->output = NULL;
+    pthread_cond_signal(&mq->cond2);
+  } UNLOCK(&mq->mut2);
+  mt->last = last;
+  mt->pending--;
+  BLOCK_SIGINT_END
+  *pending = mt->pending;
+  return done;
+}
+
+static void
+mtpthread_queue_submit(struct mt_state *junk, long workid, GEN work)
+{
+  struct mt_pstate *mt = pari_mt;
+  struct mt_queue *mq = mt->mq+mt->last;
+  (void) junk;
+  if (!work) { mt->nbint=mt->n; return; }
+  BLOCK_SIGINT_START
+  if (mt->nbint<mt->n) mt->nbint++;
+  LOCK(&mq->mut)
+  {
+    mq->input = work;
+    mq->workid = workid;
+    pthread_cond_signal(&mq->cond);
+  } UNLOCK(&mq->mut);
+  mt->pending++;
+  LOCK(&mq->mut1)
+  {
+    while (mq->input)
+      pthread_cond_wait(&mq->cond1, &mq->mut1);
+  } UNLOCK(&mq->mut1);
+  BLOCK_SIGINT_END
+}
+
+void
+mt_queue_reset(void)
+{
+  struct mt_pstate *mt = pari_mt;
+  long i;
+  for (i=0; i<mt->n; i++)
+    pthread_cancel(mt->th[i]);
+  for (i=0; i<mt->n; i++)
+    pthread_join(mt->th[i],NULL);
+  if (DEBUGLEVEL) pari_warn(warner,"stop threads");
+  pari_mt = NULL;
+  for (i=0;i<mt->n;i++)
+  {
+    struct mt_queue *mq = mt->mq+i;
+    pthread_cond_destroy(&mq->cond);
+    pthread_cond_destroy(&mq->cond1);
+    pthread_cond_destroy(&mq->cond2);
+    pthread_mutex_destroy(&mq->mut);
+    pthread_mutex_destroy(&mq->mut1);
+    pthread_mutex_destroy(&mq->mut2);
+    pari_thread_free(&mt->pth[i]);
+  }
+  pari_free(mt->mq);
+  pari_free(mt->pth);
+  pari_free(mt->th);
+  pari_free(mt);
+}
+
+void
+mt_queue_start(struct pari_mt *pt, GEN worker)
+{
+  if (pari_mt)
+    return mtsingle_queue_start(pt, worker);
+  else
+  {
+    long NBT = pari_mt_nbthreads;
+    struct mt_pstate *mt =
+           (struct mt_pstate*) pari_malloc(sizeof(struct mt_pstate));
+    long mtparisize = GP_DATA->threadsize? GP_DATA->threadsize: top-bot;
+    long i;
+    mt->mq  = (struct mt_queue *) pari_malloc(sizeof(*mt->mq)*NBT);
+    mt->th  = (pthread_t *) pari_malloc(sizeof(*mt->th)*NBT);
+    mt->pth = (struct pari_thread *) pari_malloc(sizeof(*mt->pth)*NBT);
+    mt->pending = 0;
+    mt->n = NBT;
+    mt->nbint = 0;
+    mt->last = 0;
+    pari_mt = mt;
+    pthread_cond_init(&mt->pcond,NULL);
+    pthread_mutex_init(&mt->pmut,NULL);
+    for (i=0;i<NBT;i++)
+    {
+      struct mt_queue *mq = mt->mq+i;
+      mq->no     = i;
+      mq->worker = worker;
+      mq->input  = NULL;
+      mq->output = NULL;
+      mq->pcond  = &mt->pcond;
+      mq->pmut   = &mt->pmut;
+      pthread_cond_init(&mq->cond,NULL);
+      pthread_cond_init(&mq->cond1,NULL);
+      pthread_cond_init(&mq->cond2,NULL);
+      pthread_mutex_init(&mq->mut,NULL);
+      pthread_mutex_init(&mq->mut1,NULL);
+      pthread_mutex_init(&mq->mut2,NULL);
+      pari_thread_alloc(&mt->pth[i],mtparisize,(GEN)mq);
+    }
+    if (DEBUGLEVEL) pari_warn(warner,"start threads");
+    for (i=0;i<NBT;i++)
+      pthread_create(&mt->th[i],NULL, &mt_queue_run, (void*)&mt->pth[i]);
+    pt->get=&mtpthread_queue_get;
+    pt->submit=&mtpthread_queue_submit;
+    pt->end=&mt_queue_reset;
+  }
+}
diff --git a/src/mt/pthread.h b/src/mt/pthread.h
new file mode 100644
index 0000000..fa23934
--- /dev/null
+++ b/src/mt/pthread.h
@@ -0,0 +1,16 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define MT_IS_THREAD              mt_is_thread()
+#define MT_SIGINT_BLOCK(block)    do {if (!block) mt_sigint_block();} while(0)
+#define MT_SIGINT_UNBLOCK(block)  do {if (!block) mt_sigint_unblock();} while(0)
diff --git a/src/mt/single.c b/src/mt/single.c
new file mode 100644
index 0000000..e3da91b
--- /dev/null
+++ b/src/mt/single.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "pari.h"
+#include "paripriv.h"
+#include "mt.h"
+
+void mt_sigint_block(void) { }
+void mt_sigint_unblock(void) { }
+void mt_err_recover(long er) { (void)er; }
+void pari_mt_close(void) { }
+void mt_queue_reset(void) { }
+void mt_broadcast(GEN code) {(void) code;}
+
+void
+mt_sigint(void) {}
+
+int
+mt_is_parallel(void)
+{
+  return 0;
+}
+
+int
+mt_is_thread(void)
+{
+  return 0;
+}
+
+void
+pari_mt_init(void)
+{
+  pari_mt_nbthreads = 1;
+}
+
+void
+mt_queue_start(struct pari_mt *pt, GEN worker)
+{
+  return mtsingle_queue_start(pt, worker);
+}
diff --git a/src/mt/single.h b/src/mt/single.h
new file mode 100644
index 0000000..39c3da8
--- /dev/null
+++ b/src/mt/single.h
@@ -0,0 +1,16 @@
+/* Copyright (C) 2013  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#define MT_IS_THREAD 0
+#define MT_SIGINT_BLOCK(block)
+#define MT_SIGINT_UNBLOCK(block)
diff --git a/src/systems/cygwin/cygwin.c b/src/systems/cygwin/cygwin.c
new file mode 100644
index 0000000..3e6096e
--- /dev/null
+++ b/src/systems/cygwin/cygwin.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2009  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Written by Vasili Burdo */
+
+#include <windows.h>
+#include <stdio.h>
+
+char* win32_datadir(void)
+{
+  char datadir[1024];
+  char* slash;
+  GetModuleFileNameA(0, datadir, sizeof(datadir) );
+  slash = strrchr(datadir, '\\');
+  if( slash ) *(slash+1) = 0;
+  strcat(datadir, "data");
+  return strdup(datadir);
+}
diff --git a/src/systems/darwin/darwin.c b/src/systems/darwin/darwin.c
new file mode 100644
index 0000000..c2ec470
--- /dev/null
+++ b/src/systems/darwin/darwin.c
@@ -0,0 +1,209 @@
+/* Copyright (c) 2002 Peter O'Gorman <ogorman at users.sourceforge.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* Modified for PARI/GP by the PARI group */
+#include "pari.h"
+#ifndef HAS_DLOPEN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <mach-o/dyld.h>
+#include "dlfcn.h"
+
+#define ERR_STR_LEN 256
+
+static void *dlsymIntern(void *handle, const char *symbol);
+static const char *error(int setget, const char *str, ...);
+
+/* Set and get the error string for use by dlerror */
+static const char *error(int setget, const char *str, ...)
+{
+  static char errstr[ERR_STR_LEN];
+  static int err_filled = 0;
+  const char *retval;
+  va_list arg;
+  if (setget == 0)
+  {
+    va_start(arg, str);
+    strncpy(errstr, "dlsimple: ", ERR_STR_LEN);
+    vsnprintf(errstr + 10, ERR_STR_LEN - 10, str, arg);
+    va_end(arg);
+    err_filled = 1;
+    retval = NULL;
+  }
+  else
+  {
+    retval = err_filled? errstr: NULL;
+    err_filled = 0;
+  }
+  return retval;
+}
+
+/* dlopen */
+void *dlopen(const char *path, int mode)
+{
+  void *module = 0;
+  NSObjectFileImage ofi = 0;
+  NSObjectFileImageReturnCode ofirc;
+  static int (*make_private_module_public) (NSModule module) = 0;
+  unsigned int flags =  NSLINKMODULE_OPTION_RETURN_ON_ERROR | NSLINKMODULE_OPTION_PRIVATE;
+
+  /* If we got no path, the app wants the global namespace, use -1 as the marker
+     in this case */
+  if (!path)
+    return (void *)-1;
+
+  /* Create the object file image, works for things linked with the -bundle arg to ld */
+  ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
+  switch (ofirc)
+  {
+    case NSObjectFileImageSuccess:
+      /* It was okay, so use NSLinkModule to link in the image */
+      if (!(mode & RTLD_LAZY)) flags += NSLINKMODULE_OPTION_BINDNOW;
+      module = NSLinkModule(ofi, path,flags);
+      /* Don't forget to destroy the object file image, unless you like leaks */
+      NSDestroyObjectFileImage(ofi);
+      /* If the mode was global, then change the module, this avoids
+         multiply defined symbol errors to first load private then make
+         global. Silly, isn't it. */
+      if ((mode & RTLD_GLOBAL))
+      {
+        if (!make_private_module_public)
+        {
+          _dyld_func_lookup("__dyld_NSMakePrivateModulePublic",
+        (void**)&make_private_module_public);
+        }
+        make_private_module_public((NSModule)module);
+      }
+      break;
+    case NSObjectFileImageInappropriateFile:
+      /* It may have been a dynamic library rather than a bundle, try to load it */
+      module = (void *)NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR);
+      break;
+    case NSObjectFileImageFailure:
+      error(0,"Object file setup failure :  \"%s\"", path);
+      return 0;
+    case NSObjectFileImageArch:
+      error(0,"No object for this architecture :  \"%s\"", path);
+      return 0;
+    case NSObjectFileImageFormat:
+      error(0,"Bad object file format :  \"%s\"", path);
+      return 0;
+    case NSObjectFileImageAccess:
+      error(0,"Can't read object file :  \"%s\"", path);
+      return 0;
+  }
+  if (!module)
+    error(0, "Can not open \"%s\"", path);
+  return module;
+}
+
+static int
+is_mach_header(void *handle)
+{ /* Check for both possible magic numbers depending on x86/ppc byte order */
+  return ((((struct mach_header *)handle)->magic == MH_MAGIC) ||
+          (((struct mach_header *)handle)->magic == MH_CIGAM));
+}
+
+/* used by dlsym to find the symbol */
+void *dlsymIntern(void *handle, const char *symbol)
+{
+  NSSymbol nssym = NULL;
+  if (handle == (void *)-1)
+  { /* Global context */
+    if (NSIsSymbolNameDefined(symbol))
+      nssym = NSLookupAndBindSymbol(symbol);
+  }
+  else
+  {
+    if (is_mach_header(handle))
+    { /* library */
+      if (NSIsSymbolNameDefinedInImage((struct mach_header *)handle, symbol))
+        nssym = NSLookupSymbolInImage((struct mach_header *)handle, symbol,
+                        NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
+                        | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+    }
+    else /* bundle */
+      nssym = NSLookupSymbolInModule((NSModule)handle, symbol);
+  }
+  if (!nssym)
+  {
+    error(0, "Symbol \"%s\" Not found", symbol);
+    return NULL;
+  }
+  return NSAddressOfSymbol(nssym);
+}
+
+const char *dlerror(void)
+{
+  return error(1, (char *)NULL);
+}
+
+int dlclose(void *handle)
+{
+  if (is_mach_header(handle))
+  {
+    error(0, "Can't remove dynamic libraries on darwin");
+    return 0;
+  }
+  if (!NSUnLinkModule((NSModule)handle, 0))
+  {
+    error(0, "unable to unlink module %s", NSNameOfModule((NSModule)handle));
+    return 1;
+  }
+  return 0;
+}
+
+/* dlsym, prepend the underscore and call dlsymIntern */
+void *dlsym(void *handle, const char *symbol)
+{
+  static char undersym[257];  /* Saves calls to malloc(3) */
+  int sym_len = strlen(symbol);
+  void *value = NULL;
+  char *malloc_sym = NULL;
+
+  if (sym_len < 256)
+  {
+    snprintf(undersym, 256, "_%s", symbol);
+    value = dlsymIntern(handle, undersym);
+  }
+  else
+  {
+    malloc_sym = (char*)malloc(sym_len + 2);
+    if (malloc_sym)
+    {
+      sprintf(malloc_sym, "_%s", symbol);
+      value = dlsymIntern(handle, malloc_sym);
+      pari_free(malloc_sym);
+    }
+    else
+      error(0, "Unable to allocate memory");
+  }
+  return value;
+}
+
+#endif
diff --git a/src/systems/darwin/dlfcn.h b/src/systems/darwin/dlfcn.h
new file mode 100644
index 0000000..4b33ab5
--- /dev/null
+++ b/src/systems/darwin/dlfcn.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2002 Jorge Acereda  <jacereda at users.sourceforge.net> &
+                      Peter O'Gorman <ogorman at users.sourceforge.net>
+
+Portions may be copyright others, see the AUTHORS file included with this
+distribution.
+
+Maintained by Peter O'Gorman <ogorman at users.sourceforge.net>
+
+Bug Reports and other queries should go to <ogorman at users.sourceforge.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#ifndef _DLFCN_H_
+#define _DLFCN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Structure filled in by dladdr().
+ */
+
+typedef struct dl_info {
+        const char      *dli_fname;     /* Pathname of shared object */
+        void            *dli_fbase;     /* Base address of shared object */
+        const char      *dli_sname;     /* Name of nearest symbol */
+        void            *dli_saddr;     /* Address of nearest symbol */
+} Dl_info;
+
+
+extern void * dlopen(const char *path, int mode);
+extern void * dlsym(void * handle, const char *symbol);
+extern const char * dlerror(void);
+extern int dlclose(void * handle);
+extern int dladdr(void *, Dl_info *);
+
+#define RTLD_LAZY        0x1
+#define RTLD_NOW        0x2
+#define RTLD_LOCAL        0x4
+#define RTLD_GLOBAL        0x8
+#define RTLD_NOLOAD        0x10
+#define RTLD_NODELETE        0x80
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DLFCN_H_ */
diff --git a/src/systems/mingw/mingw.c b/src/systems/mingw/mingw.c
new file mode 100644
index 0000000..edd7156
--- /dev/null
+++ b/src/systems/mingw/mingw.c
@@ -0,0 +1,158 @@
+/* Copyright (C) 2009  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* Originally written by Vasili Burdo */
+
+#include <windows.h>
+#include <stdio.h>
+#include "mingw.h"
+
+static const char * pariwin32_basedir = NULL;
+
+const char*
+win32_basedir(void)
+{
+  if (pariwin32_basedir) return pariwin32_basedir;
+  else
+  {
+    char basedir[1024];
+    char* slash;
+    GetModuleFileNameA(0, basedir, sizeof(basedir) );
+    slash = strrchr(basedir, '\\');
+    if (slash) slash[1] = 0;
+    pariwin32_basedir = strdup(basedir);
+    return pariwin32_basedir;
+  }
+}
+
+char*
+win32_datadir(void)
+{
+  char datadir[1029];
+  const char * basedir = win32_basedir();
+  sprintf(datadir, "%sdata",basedir);
+  return strdup(datadir);
+}
+
+static WORD
+win32_console_color(unsigned long c)
+{
+  int shift, intense = 0;
+  if( c >= 30 && c <= 37 ) { shift = 0; c -= 30; } else
+  if( c >= 40 && c <= 47 ) { shift = 4; c -= 40; } else
+  if( c >= 90 && c <= 97 ) { shift = 0; intense = 8; c -= 90; } else
+  if(c >= 100 && c <= 107) { shift = 4; intense = 8; c -= 100; } else
+  return 0;
+
+  WORD w = 0;
+  switch(c) {
+  case 0: w = 0; break; /* black      */
+  case 1: w = 4; break; /* red        */
+  case 2: w = 2; break; /* green      */
+  case 3: w = 6; break; /* yellow RG  */
+  case 4: w = 1; break; /* blue       */
+  case 5: w = 5; break; /* magenta RB */
+  case 6: w = 3; break; /* cyan GB    */
+  case 7: w = 7; break; /* white RGB  */
+  }
+  return (w|intense) << shift;
+}
+
+void
+win32_ansi_fputs(const char* s, void* f)
+{
+  WORD color;
+  unsigned long c[3];
+  long nbarg;
+  if( !(f == stdout || f == stderr) ) {
+    fputs(s,f);
+    return;
+  }
+
+  while(1) {
+    char *p;
+    p = strstr(s, "\x1b[");
+    if( p > s )
+      fwrite(s,p-s,1,f);
+
+    if( p )
+      p += 2;
+    else {
+      fputs(s,f);
+      return;
+    }
+    nbarg = 0;
+    c[nbarg++] = strtoul(p,&p,10);
+    if( *p == ';' ) c[nbarg++] = strtoul(p+1,&p,10);
+    if( *p == ';' ) c[nbarg++] = strtoul(p+1,&p,10);
+    if( *p++ == 'm' ) {
+      switch(nbarg)
+      {
+      case 1:
+        color = 7;
+        break;
+      case 2:
+        color = win32_console_color(c[1]);
+        if (c[0]&4) color |= 0x8000;
+        break;
+      case 3:
+        color = win32_console_color(c[1]) | win32_console_color(c[2]);
+        if (c[0]&4) color |= 0x8000;
+      }
+      fflush(f);
+      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color);
+    }
+    s = p;
+  }
+}
+
+int
+win32_terminal_width(void)
+{
+  CONSOLE_SCREEN_BUFFER_INFO sbi;
+  if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &sbi))
+    return 0;
+  return sbi.srWindow.Right - sbi.srWindow.Left + 1;
+}
+
+int
+win32_terminal_height(void)
+{
+  CONSOLE_SCREEN_BUFFER_INFO sbi;
+  if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &sbi))
+    return 0;
+  return sbi.srWindow.Bottom - sbi.srWindow.Top + 1;
+}
+
+void
+win32_set_pdf_viewer(void)
+{
+  char *s = getenv("GP_PDF_VIEWER");
+  if (!s)
+  {
+    HKEY handle;
+    const char *key = "AcroExch.Document\\shell\\open\\command";
+    const long SZ = 512;
+    char str[SZ], *buf;
+    int status;
+    DWORD L = SZ;
+
+    (void)RegOpenKeyEx(HKEY_CLASSES_ROOT, key, 0, KEY_READ, &handle);
+    status = RegQueryValueEx(handle, NULL, 0, NULL, (LPBYTE)str, &L);
+    RegCloseKey(handle);
+    if (status) return;
+    buf = malloc(strlen(str)+16); /*must not be freed*/
+    sprintf(buf,"GP_PDF_VIEWER=%s",str);
+    putenv(buf);
+  }
+}
diff --git a/src/systems/mingw/mingw.h b/src/systems/mingw/mingw.h
new file mode 100644
index 0000000..e108465
--- /dev/null
+++ b/src/systems/mingw/mingw.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2009  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+const char* win32_basedir(void);
+char* win32_datadir(void);
+void win32_ansi_fputs(const char* s, void* f);
+int win32_terminal_width(void);
+int win32_terminal_height(void);
+void win32_set_pdf_viewer(void);
diff --git a/src/systems/os2/README b/src/systems/os2/README
new file mode 100644
index 0000000..9326114
--- /dev/null
+++ b/src/systems/os2/README
@@ -0,0 +1,51 @@
+On OS/2 the build goes the same way as on Unix, e.g.,
+
+  sh Configure
+  make gp
+  make bench
+
+With the current implementation of install(), one can load the functions from
+the PARI library only if the GP executable is build for dynamic linking.
+
+By default, the build will go to an AOUT-type executables (to simplify
+the logic of Configure, and enable restricted binary compatibility with
+DOS/Windows).  Unfortunately, AOUT-type DLLs are very restricted; thus
+the build of dynamically linked target fails.
+
+To build with OMF-type target
+
+  cd Oos2-ix86
+  make _O=.obj _A=.lib CC_FLAVOR="-Zomf -Zcrtdll -Zstack 8192" RLLIBS=-lreadline_import DLLD_IGNORE= AR=emxomfar bench
+
+This build constructs a working DLL.  Both -Zomf and -Zcrtdll are crucial to
+have a functioning DLL (see EMX documentation for details).  -Zstack 8192
+forces the same C stack size as for AOUT build (and the same - 8M - as on many
+Unices, so you get fewer surprises when things work on Unix, but core on OS/2
+due only to shorter C stack).  (The standard Configure adds -Zsysv-signals
+option; in fact, this option is not needed - as checked with v2.3.5.)
+
+Use of the the readline-DLL (via the readline_import.lib library) is not only
+a convenience, but also statically linked readline library are often broken;
+sigh...
+
+To use the gnuplot-engine DLL gnpltdrw.DLL, one can give Configure the option
+--graphic=gnuplot-dynamic,gnpltdrw (requires linking with -Zcrtdll for graphics
+to work).  Add -DOLD_SET_FEEDBACK_RECTANGLE gcc option if gnpltdrw.DLL supports
+mousing, but is an old build, so it won't report this capability.
+
+Thus the build process may look like this:
+
+  sh Configure --graphic=gnuplot-dynamic,gnpltdrw
+  make gp
+  cd Oos2-ix86
+  make _O=.obj _A=.lib CC_FLAVOR="-Zomf -Zcrtdll -Zstack 8192 -DUSE_SET_FEEDBACK_RECTANGLE" RLLIBS=-lreadline_import DLLD_IGNORE= AR=emxomfar bench
+  cd ..
+
+The statically build PARI library is in a file named similar to libpari-2_2.a,
+the library for linking with the PARI DLL is named as pari-2_2.a (or
+pari-2_2.lib).
+
+As a debugging tool, the constructed DLL reports its build options via the
+standard OS/2 way:
+
+  bldlevel FULL_NAME_OF_THE_DLL
diff --git a/src/systems/os2/dlfcn.h b/src/systems/os2/dlfcn.h
new file mode 100644
index 0000000..c8625dd
--- /dev/null
+++ b/src/systems/os2/dlfcn.h
@@ -0,0 +1,5 @@
+#define RTLD_LAZY 1
+#define RTLD_GLOBAL 1
+void *dlopen(char *path, int mode);
+void *dlsym(void *handle, char *symbol);
+char *dlerror(void);
diff --git a/src/systems/os2/os2.c b/src/systems/os2/os2.c
new file mode 100644
index 0000000..eddefd7
--- /dev/null
+++ b/src/systems/os2/os2.c
@@ -0,0 +1,217 @@
+#include "dlfcn.h"
+
+#define INCL_BASE
+#include <os2.h>
+#include <float.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+static ULONG retcode;
+static char fail[300];
+
+static ULONG dllHandle;
+static char dllname[80];
+static int handle_found;
+static int handle_loaded;
+
+#ifdef DLOPEN_INITTERM
+unsigned long _DLL_InitTerm(unsigned long modHandle, unsigned long flag)
+{
+    switch (flag) {
+    case 0:     /* INIT */
+        /* Save handle */
+        dllHandle = modHandle;
+        handle_found = 1;
+        return TRUE;
+
+    case 1:     /* TERM */
+        handle_found = 0;
+        dllHandle = (unsigned long)NULLHANDLE;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+#endif
+
+HMODULE
+find_myself(void)
+{
+
+  static APIRET APIENTRY (*pDosQueryModFromEIP) (HMODULE * hmod, ULONG * obj, ULONG BufLen, PCHAR Buf,
+                    ULONG * Offset, ULONG Address);
+  HMODULE doscalls_h, mod;
+  static int failed;
+  ULONG obj, offset, rc;
+  char buf[260];
+
+  if (failed)
+        return 0;
+  failed = 1;
+  doscalls_h = (HMODULE)dlopen("DOSCALLS",0);
+  if (!doscalls_h)
+        return 0;
+/*  {&doscalls_handle, NULL, 360}, */        /* DosQueryModFromEIP */
+  rc = DosQueryProcAddr(doscalls_h, 360, 0, (PFN*)&pDosQueryModFromEIP);
+  if (rc)
+        return 0;
+  rc = pDosQueryModFromEIP(&mod, &obj, sizeof(buf), buf, &offset, (ULONG)dlopen);
+  if (rc)
+        return 0;
+  failed = 0;
+  handle_found = 1;
+  dllHandle = mod;
+  return mod;
+}
+
+void *
+dlopen(char *path, int mode)
+{
+        HMODULE handle;
+        char tmp[260], *beg, *dot;
+        ULONG rc;
+        unsigned fpflag = _control87(0,0);
+
+        fail[0] = 0;
+        if (!path) {                        /* Our own handle. */
+            if (handle_found || find_myself()) {
+                if (handle_loaded)
+                    return (void*)dllHandle;
+                rc = DosQueryModuleName(dllHandle, sizeof(dllname), dllname);
+                if (rc) {
+                    strcpy(fail, "can't find my DLL name by the handle");
+                    retcode = rc;
+                    return 0;
+                }
+                rc = DosLoadModule((PSZ)fail, sizeof fail, (PSZ)dllname, &handle);
+                if (rc) {
+                    strcpy(fail, "can't load my own DLL");
+                    retcode = rc;
+                    return 0;
+                }
+                handle_loaded = 1;
+                goto ret;
+            }
+            retcode = ERROR_MOD_NOT_FOUND;
+            strcpy(fail, "can't load from myself: compiled without -DDLOPEN_INITTERM");
+            return 0;
+        }
+        if ((rc = DosLoadModule((PSZ)fail, sizeof fail, (PSZ)path, &handle)) == 0)
+                goto ret;
+
+        retcode = rc;
+
+        /* Not found. Check for non-FAT name and try truncated name. */
+        /* Don't know if this helps though... */
+        for (beg = dot = path + strlen(path);
+             beg > path && !strchr(":/\\", *(beg-1));
+             beg--)
+                if (*beg == '.')
+                        dot = beg;
+        if (dot - beg > 8) {
+                int n = beg+8-path;
+                memmove(tmp, path, n);
+                memmove(tmp+n, dot, strlen(dot)+1);
+                rc = DosLoadModule((PSZ)fail, sizeof fail, (PSZ)tmp, &handle);
+                if (rc == 0)
+                        goto ret;
+                retcode = rc;
+        }
+        handle = 0;
+
+      ret:
+        _control87(fpflag, MCW_EM); /* Some modules reset FP flags on load */
+        return (void *)handle;
+}
+
+#define ERROR_WRONG_PROCTYPE 0xffffffff
+
+void *
+dlsym(void *handle, char *symbol)
+{
+        ULONG rc, type;
+        PFN addr;
+
+        fail[0] = 0;
+        rc = DosQueryProcAddr((HMODULE)handle, 0, (PSZ)symbol, &addr);
+        if (rc == 0) {
+                rc = DosQueryProcType((HMODULE)handle, 0, (PSZ)symbol, &type);
+                if (rc == 0 && type == PT_32BIT)
+                        return (void *)addr;
+                rc = ERROR_WRONG_PROCTYPE;
+        }
+        retcode = rc;
+        return NULL;
+}
+
+char *
+dlerror(void)
+{
+        static char buf[700];
+        ULONG len;
+
+        if (retcode == 0)
+                return NULL;
+        if (retcode == ERROR_WRONG_PROCTYPE) {
+                strcpy(buf, "Wrong procedure type");
+                len = strlen(buf);
+        }
+        if ((retcode != ERROR_WRONG_PROCTYPE)
+            && DosGetMessage(NULL, 0, buf, sizeof buf - 1, retcode,
+                             (PSZ)"OSO001.MSG", &len)) {
+                if (fail[0])
+                  sprintf(buf,
+"OS/2 system error code %d, possible problematic module: '%s'",
+                          (int)retcode, fail);
+                else
+                  sprintf(buf, "OS/2 system error code %d", (int)retcode);
+        } else {
+                buf[len] = '\0';
+                if (len && buf[len - 1] == '\n')
+                        buf[--len] = 0;
+                if (len && buf[len - 1] == '\r')
+                        buf[--len] = 0;
+                if (len && buf[len - 1] == '.')
+                        buf[--len] = 0;
+                if (fail[0] && len < 300)
+                  sprintf(buf + len, ", possible problematic module: '%s'",
+                          fail);
+        }
+        retcode = 0;
+        return buf;
+}
+
+int
+dlclose(void *handle)
+{
+        ULONG rc;
+
+        if ((rc = DosFreeModule((HMODULE)handle)) == 0) return 0;
+
+        retcode = rc;
+        return 2;
+}
+
+void*
+get_stack(double fraction, long min)
+{
+  int rc;
+  TIB *tib;
+  PIB *pib;
+  char *s, *e;
+  unsigned long d;
+
+  if (!(_emx_env & 0x200)) return 0;        /* not OS/2. */
+  rc = DosGetInfoBlocks(&tib, &pib);
+  if (rc) return 0;                        /* ignore error */
+  s = (char*)tib->tib_pstack;
+  e = (char*)tib->tib_pstacklimit;
+  d = fraction * (e-s);
+  if (min >= 3*(e-s)/4)
+    min = 3*(e-s)/4;
+  if (d < min)
+    d = min;
+  return (void*)(s + d);
+}
diff --git a/src/systems/os2/pari.def.base b/src/systems/os2/pari.def.base
new file mode 100644
index 0000000..091830a
--- /dev/null
+++ b/src/systems/os2/pari.def.base
@@ -0,0 +1,7 @@
+LIBRARY '<DLL_BASE>' INITINSTANCE TERMINSTANCE
+; Can't put http://.../ in VENDOR, since it is split on :
+; One should be be glad that .../ is good enough
+DESCRIPTION '@#<VENDOR>:<VERSION>#@<DESCR>'
+CODE LOADONCALL
+DATA LOADONCALL NONSHARED MULTIPLE
+EXPORTS
diff --git a/src/systems/win32/README.MSVC b/src/systems/win32/README.MSVC
new file mode 100644
index 0000000..804dbe5
--- /dev/null
+++ b/src/systems/win32/README.MSVC
@@ -0,0 +1,66 @@
+Posted on pari-dev by Bill Daly : [edited to fit current filenames]
+=================================
+
+Here is a description of the steps necessary to compile PARI with the MSVC
+compiler under WIN32.
+
+1. Unzip the archive, preserving folder names so that the directory
+structure is correct. I use WinZip to do this, and I put the files on my D:
+drive, so that the files for pari-2.1.0 for example will be stored under
+D:\pari-2.1.0. Note that the archive as downloaded, pari.tgz, is an archive
+of an archive. Winzip doesn't know how to unzip pari.tgz, so I use Aladdin
+Expander to do this, which will create for example the file pari-2.1.0.tar in
+the same directory as pari.tgz. Unfortunately, Aladdin Expander doesn't know
+how to unzip this file, so I use Winzip to do it. Isn't Windows wonderful?
+
+2. MSVC doesn't like names of the form "pari-2.1.0", so rename the base
+directory to something like "pari210". Now the files are stored under
+D:\pari210.
+
+3. Start MSVC (I use version 6, but earlier versions will probably work the
+same way).
+
+4. Select "File\New..." and pick the "Project" tab to create a new project.
+Select "Win32 Console Application" as the project type, and specify the
+project name as "pari210". Make sure that the "Location" of the files is
+D:\pari210. Click "OK", then select "An empty project" (should be the
+default anyway) and click "Finish".
+
+5. Select "Project\Add to project\Files..." to add files to the project.
+Select all the *.c and *.h files in src\basemath, src\headers,
+src\kernel\ix86, src\language, src\modules and Odos, EXCEPT pariCE.[ch];
+all of the files in src\gp except gp_rl.c; the files plotnull.c, plotport.c
+and rect.h in src\graph.c; and the file src\kernel\none\mp.c.
+
+6. Select "Project\Settings" and pick the "C/C++" tab. Select
+"Preprocessor" from the "Category" menu, and enter
+"D:\pari210\src\headers,D:\pari210\Odos" into the "Additional include
+directories" box. (I also select the "Code Generation" category and change
+the "Struct member alignment" to "1 byte", but this may not be necessary.)
+It is usually convenient for debugging to select the "Debug" tab and
+specify a "Working directory", e.g. "D:\gp" if you have .gp files there
+that you can use for testing.
+
+You should now be able to build and test the program. When you want to
+build a release version, you will have to repeat step 6 above, since MSVC
+doesn't apply the project settings globally.
+
+A couple of points:
+-------------------
+You may get some inconsequential warning messages. You can suppress these
+by adding a line of the form:
+
+   #pragma warning(disable: ...)
+
+to paricfg.h, where ... is a space-delimited list of warning numbers, e.g.
+"4018 4244" corresponding to the warnings C4018 and C4244.
+
+When you compile a release version, global optimization is enabled by
+default. MSVC is fairly buggy with global optimization, so you may find
+that some modules won't compile with it, e.g. sumiter.c. You can either
+disable global optimization (in "Project\Settings" under the tab "C/C++" in
+the category "Optimization"), or you can surround the offending code with:
+
+   #pragma optimization("g", off)
+   ...
+   #pragma optimization("g", on)
diff --git a/src/systems/win32/pariinl.h b/src/systems/win32/pariinl.h
new file mode 100644
index 0000000..e9c69f7
--- /dev/null
+++ b/src/systems/win32/pariinl.h
@@ -0,0 +1,11 @@
+/*   This file contains declarations/definitions of functions that
+ *   _can_ be inlined.
+ */
+#ifndef WINCE
+#  include "../src/kernel/ix86/level0.h"
+#else
+#  include "../src/kernel/none/level0.h"
+#endif
+
+#include "../src/kernel/none/level1.h"
+
diff --git a/src/systems/winCE/pariCE.c b/src/systems/winCE/pariCE.c
new file mode 100644
index 0000000..8c00db3
--- /dev/null
+++ b/src/systems/winCE/pariCE.c
@@ -0,0 +1,151 @@
+#include <stdlib.h>
+#include <windows.h>
+
+
+int isspace(int c) {
+  if ((c >= 0x09) && (c <= 0x0D)) return 1;
+  else if (c == 0x20) return 1;
+  return 0;
+}
+
+int isdigit(int c) {
+  if ((c >= '0') && (c <= '9')) return 1;
+  else return 0;
+}
+
+int isxdigit(int c) {
+  if ((c >= '0') && (c <= '9')) return 1;
+  else if ((c >= 'a') && (c <= 'f')) return 1;
+  else if ((c >= 'A') && (c <= 'F')) return 1;
+  else return 0;
+}
+
+int isalpha(int c) {
+  if ((c >= 'a') && (c <= 'z')) return 1;
+  else if ((c >= 'A') && (c <= 'Z')) return 1;
+  else return 0;
+}
+
+int isalnum(int c) {
+  if ((c >= '0') && (c <= '9')) return 1;
+  else if ((c >= 'a') && (c <= 'z')) return 1;
+  else if ((c >= 'A') && (c <= 'Z')) return 1;
+  else return 0;
+}
+
+static int checkDigit(char ch, int base) {
+  int n;
+
+  if (ch >= '0' && ch <= '9') n = ch - '0';
+  else if (ch >= 'a' && ch <= 'z') n = ch + 10 - 'a';
+  else if (ch >= 'A' && ch <= 'Z') n = ch + 10 - 'A';
+  else return -1;
+
+  return n >= base ? -1 : n;
+}
+
+long strtol(const char *s, char **endptr, int base) {
+  const char *sc;
+  char sign;
+  int actualbase;
+  double result;
+
+  /* if the base is illegal, then return 0; */
+  if (! (base >= 2 && base <= 36) ){
+    if (endptr) *endptr = (char *)s;
+    return 0;
+  }
+
+  /* skip leading spaces */
+  for (sc = s; *sc == ' '; ++sc);
+
+  /* parse the sign */
+  if (*sc == '-' || *sc == '+') {
+    sign = *sc;
+    sc++;
+  }
+  else {
+    sign = '+';
+  }
+
+  /* the default base = 10 */
+  actualbase = base == 0 ? 10 : base;
+
+  /* if base is undefined, and number starts '0x', then we have base 16 */
+  if (base == 0 && sc[0] == '0' && (sc[1] == 'x' || sc[1] == 'X')) { actualbase = 16; sc += 2; }
+
+  /* else if base is undefined, and number starts '0', then we have base 8 */
+  else if (base == 0 && sc[0] == '0') actualbase = 8;
+
+  /* else if base == 16, then skip any leading '0x' */
+  else if (base == 16 && sc[0] == '0' && (sc[1] == 'x' || sc[1] == 'X')) sc += 2;
+
+  /* skip leading zeroes */
+  for (; *sc == '0'; ++sc);
+
+  /* the result so far is 0. We are going to work with doubles because these give 52 bits of accuracy */
+  result = 0.0;
+
+  /* sc now points to the first unprocessed digit. Keep processing until first non digit or overflow */
+  for (;;) {
+    int d = checkDigit(*sc, actualbase);
+
+    /* if the digit was illegal, then we have terminated */
+    if (d < 0) {
+      if (endptr) *endptr = (char *)sc;
+      return sign == '-' ? -(long)result : (long)result;
+    }
+
+    /* roll in the new digit */
+    result = result * actualbase + d;
+
+    /* check for overflow */
+    if (sign == '+' && result > (double)LONG_MAX) {
+      if (endptr) *endptr = (char *)(sc+1);
+      return LONG_MAX;
+    }
+    if (sign == '-' && result > -(double)LONG_MIN) {
+      if (endptr) *endptr = (char *)(sc+1);
+      return LONG_MIN;
+    }
+
+    /* go on to the next character */
+    sc++;
+  }
+}
+
+/*
+ *  Implement rename and unlink in win32 procedures. This is complicated
+ *  by the fact that these procedures take wide chars.
+ */
+
+#define MAX_FILENAME_LEN           128
+
+int rename(const char *oldname, const char *newname) {
+  int succ;
+  short soldname[MAX_FILENAME_LEN];
+  short snewname[MAX_FILENAME_LEN];
+
+  MultiByteToWideChar(CP_ACP, 0, oldname, strlen(oldname)+1, soldname, MAX_FILENAME_LEN);
+  MultiByteToWideChar(CP_ACP, 0, newname, strlen(newname)+1, snewname, MAX_FILENAME_LEN);
+  succ = MoveFile(soldname, snewname);
+  return !succ;
+}
+
+int unlink(char *name) {
+  int succ;
+  short wname[MAX_FILENAME_LEN];
+
+  MultiByteToWideChar(CP_ACP, 0, name, strlen(name)+1, wname, MAX_FILENAME_LEN);
+
+  succ = DeleteFile(wname);
+  return !succ;
+}
+
+void *calloc(size_t nelem, size_t size) {
+  const size_t n = nelem*size;
+  char *p = malloc(n);
+
+  if (p) memset(p, '\0', n);
+  return p;
+}
diff --git a/src/systems/winCE/pariCE.h b/src/systems/winCE/pariCE.h
new file mode 100644
index 0000000..b7e2311
--- /dev/null
+++ b/src/systems/winCE/pariCE.h
@@ -0,0 +1,14 @@
+/* only needed for Windows CE */
+#ifndef PARICE_H
+#define PARICE_H
+
+extern int isspace(int);
+extern int isdigit(int);
+extern int isalpha(int);
+extern int isalnum(int);
+extern long strtol(const char *, char **, int );
+extern int rename(const char *, const *);
+extern int unlink(char *);
+extern void *calloc(size_t, size_t);
+
+#endif
diff --git a/src/test/32/addprimes b/src/test/32/addprimes
new file mode 100644
index 0000000..fcc0fa1
--- /dev/null
+++ b/src/test/32/addprimes
@@ -0,0 +1,26 @@
+[[0, 1, 210, 4, 4, 48, 16, 576, 65000, 10922688], [0, -1, 210000630, 5, 5, 4
+8000096, 32, 576002304, 65000390000650000, 10922786304486912881835264], [0, 
+1, 210007560020790, 6, 6, 48001632003072, 64, 576021888078336, 6500468009724
+0468000708500000, 10923867696016192486063520726766954883717632], [0, 0, 2100
+06930, 6, 7, 48001776007968009216, 96, 576023616144576254592, 65005070125906
+093564391733467296447350000, 10923966011120401354532760288014407980573122245
+052972159937408], [0, -1, -210000630, 5, 5, 48000096, 32, 576002304, 6500039
+0000650000, 10922786304486912881835264]]
+Testing 210, addprimes = [1000003]
+Testing 210000630, addprimes = [1000003]
+Testing 210007560020790, addprimes = [1000003]
+Testing 210008190043470062370, addprimes = [1000003]
+Testing -210000630, addprimes = [1000003]
+Testing 210, addprimes = [1000003, 1000033]
+Testing 210000630, addprimes = [1000003, 1000033]
+Testing 210007560020790, addprimes = [1000003, 1000033]
+Testing 210008190043470062370, addprimes = [1000003, 1000033]
+Testing -210000630, addprimes = [1000003, 1000033]
+Testing 210, addprimes = [1000033]
+Testing 210000630, addprimes = [1000033]
+Testing 210007560020790, addprimes = [1000033]
+Testing 210008190043470062370, addprimes = [1000033]
+Testing -210000630, addprimes = [1000033]
+[]
+[1009]
+Total time spent: 12
diff --git a/src/test/32/agm b/src/test/32/agm
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/32/analyz b/src/test/32/analyz
new file mode 100644
index 0000000..37d2329
--- /dev/null
+++ b/src/test/32/analyz
@@ -0,0 +1,8 @@
+   echo = 1 (on)
+? sum(x=0,50000,x);
+? sum(x=1,1000,log(x));
+? sum(x=1,25,sum(y=1,100,x/y),0.0);
+? sum(x=1,100,sum(y=1,100,x/y,0.0));
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 24
diff --git a/src/test/32/apply b/src/test/32/apply
new file mode 100644
index 0000000..e822410
--- /dev/null
+++ b/src/test/32/apply
@@ -0,0 +1,15 @@
+[1, 4, 9, 16]
+
+[1  4]
+
+[9 16]
+
+16*x^2 + 9*x + 4
+4 + 9*x + 16*x^2 + O(x^3)
+List([1, 4, 9, 16])
+List([1, 2])
+[0, 1, 2]
+[0, 1, 2]
+[2, 3]
+[2, 3]
+Total time spent: 0
diff --git a/src/test/32/arith b/src/test/32/arith
new file mode 100644
index 0000000..86a8892
--- /dev/null
+++ b/src/test/32/arith
@@ -0,0 +1,3 @@
+0
+4162330905307
+Total time spent: 4
diff --git a/src/test/32/aurifeuille b/src/test/32/aurifeuille
new file mode 100644
index 0000000..ea9e28a
--- /dev/null
+++ b/src/test/32/aurifeuille
@@ -0,0 +1,13 @@
+2818034765526617919871
+13851033738067865242961762796990508103341
+2818034765526617919871
+48975219025052205901
+288943522443730350379346314566889
+73194743542229
+97
+13
+818201
+13
+1741
+31
+Total time spent: 16
diff --git a/src/test/32/bern b/src/test/32/bern
new file mode 100644
index 0000000..693b708
--- /dev/null
+++ b/src/test/32/bern
@@ -0,0 +1,45 @@
+-1/2
+1/6
+0
+-1/30
+0
+1/42
+0
+-1/30
+0
+5/66
+0
+-691/2730
+0
+7/6
+0
+-3617/510
+0
+43867/798
+0
+-174611/330
+1
+x - 1/2
+x^2 - x + 1/6
+x^3 - 3/2*x^2 + 1/2*x
+x^4 - 2*x^3 + x^2 - 1/30
+x^5 - 5/2*x^4 + 5/3*x^3 - 1/6*x
+  ***   at top-level: bernfrac(-1)
+  ***                 ^------------
+  *** bernfrac: domain error in bernfrac: index < 0
+  ***   at top-level: bernreal(-1)
+  ***                 ^------------
+  *** bernreal: domain error in bernreal: index < 0
+  ***   at top-level: bernpol(-1)
+  ***                 ^-----------
+  *** bernpol: domain error in bernpol: index < 0
+[1, 1/6, -1/30, 1/42, -1/30, 5/66, -691/2730, 7/6, -3617/510, 43867/798, -17
+4611/330, 854513/138, -236364091/2730, 8553103/6, -23749461029/870, 86158412
+76005/14322, -7709321041217/510, 2577687858367/6, -26315271553053477373/1919
+190, 2929993913841559/6, -261082718496449122051/13530, 152009764391807080269
+1/1806, -27833269579301024235023/690, 596451111593912163277961/282, -5609403
+368997817686249127547/46410, 495057205241079648212477525/66, -80116571813548
+9957347924991853/1590, 29149963634884862421418123812691/798, -24793929293132
+26753685415739663229/870, 84483613348880041862046775994036021/354, -12152331
+40483755572040304994079820246041491/56786730]
+Total time spent: 0
diff --git a/src/test/32/bessel b/src/test/32/bessel
new file mode 100644
index 0000000..9851bff
--- /dev/null
+++ b/src/test/32/bessel
@@ -0,0 +1,339 @@
+1.8572024140248075150290504794684852507 E-10
+1 - 1/46*x^2 + 1/4600*x^4 - 1/745200*x^6 + O(x^7)
+besselh1
+[1, 1]: error("incorrect type in isint (t_POL).")
+[1, 2]: error("incorrect type in isint (t_POL).")
+[1, 3]: error("incorrect type in isint (t_POL).")
+[1, 4]: error("incorrect type in isint (t_POL).")
+[2, 1]: 1 + (-1/4 - 1/2*I)*x^2 + (1/64 + 3/64*I)*x^4 + (-1/2304 - 11/6912*I)
+*x^6 + O(x^7)
+[2, 2]: 0.76519768655796655144971752610266322091 + 0.08825696421567695798292
+6766023515162828*I
+[2, 3]: 0.93846980724081290422840467359971262557 - 0.44451873350670655714839
+847506833191038*I
+[2, 4]: 0.22744989480229475542017649479538153049 - 0.05105545867308961813450
+6215898455430057*I
+[3, 1]: 4*I*x^-2 + (1 + I) + (-1/8 - 5/16*I)*x^2 + O(x^3)
+[3, 2]: 0.44005058574493351595968220371891491313 - 0.78121282130028871654715
+000004796482055*I
+[3, 3]: 0.24226845767487388638395457614153164080 - 1.47147239267024306918858
+46353232974532*I
+[3, 4]: -0.015640669069980772062382408481746291203 - 0.292666506764257448350
+36876789097604882*I
+[4, 1]: (1 - I) + (-1/6 + 1/2*I)*x^2 + (1/120 - 1/24*I)*x^4 + (-1/5040 + 1/7
+20*I)*x^6 + O(x^7)
+[4, 2]: 0.67139670714180309041636401204046708055 - 0.43109886801837607952052
+096729853340009*I
+[4, 3]: 0.54097378993452809133091313466411641349 - 0.99024588024340488002335
+195542348755756*I
+[4, 4]: 0.14085110084956896263172093887132583804 - 0.20269003235062497321863
+218183621826188*I
+[5, 1]: error("domain error in besseln: 2n mod Z != 0")
+[5, 2]: 0.26938214945091836540555327716757818205 - 1.03976259800634355895849
+86807324939546*I
+[5, 3]: 0.10940542017270712675781308031594165118 - 2.26083223277617932197371
+42212172870084*I
+[5, 4]: -0.19118520534054547001908105095911444129 - 0.3150944893490879685794
+3764282485894340*I
+[6, 1]: 380507258880*I*x^-20 + 10569646080*I*x^-18 + 165150720*I*x^-16 + O(x
+^-15)
+[6, 2]: 2.6306151236874532069978536877905029441 E-10 - 121618014.27868918928
+813042666797114529*I
+[6, 3]: 2.6131773608228030862436154291215029458 E-13 - 121963623349.56963053
+464019824934463602*I
+[6, 4]: -3689851.3824160519844673516400513272729 - 205195.348659968658569759
+91788233215816*I
+[7, 1]: error("domain error in besseln: 2n mod Z != 0")
+[7, 2]: 0.58245577631758767297591196874548910891 - 1.44030896971971272914426
+26374028590791*I
+[7, 3]: 0.38021680508212671683351192801028812479 - 0.98141687216350076398947
+866786133576037*I
+[7, 4]: 0.20154312205360321787812231235059586177 - 0.76783553572168519458542
+835586375224028*I
+besselh2
+[1, 1]: error("incorrect type in isint (t_POL).")
+[1, 2]: error("incorrect type in isint (t_POL).")
+[1, 3]: error("incorrect type in isint (t_POL).")
+[1, 4]: error("incorrect type in isint (t_POL).")
+[2, 1]: 1 + (-1/4 + 1/2*I)*x^2 + (1/64 - 3/64*I)*x^4 + (-1/2304 + 11/6912*I)
+*x^6 + O(x^7)
+[2, 2]: 0.76519768655796655144971752610266322091 - 0.08825696421567695798292
+6766023515162828*I
+[2, 3]: 0.93846980724081290422840467359971262557 + 0.44451873350670655714839
+847506833191038*I
+[2, 4]: 1.6477670588097637977792999000562559034 - 0.942004436545154646198413
+22655202096895*I
+[3, 1]: -4*I*x^-2 + (1 - I) + (-1/8 + 5/16*I)*x^2 + O(x^3)
+[3, 2]: 0.44005058574493351595968220371891491313 + 0.78121282130028871654715
+000004796482055*I
+[3, 3]: 0.24226845767487388638395457614153164080 + 1.47147239267024306918858
+46353232974532*I
+[3, 4]: 1.2439613389157879924008217252327509830 + 1.022722564418433025377072
+5711483489108*I
+[4, 1]: (1 + I) + (-1/6 - 1/2*I)*x^2 + (1/120 + 1/24*I)*x^4 + (-1/5040 - 1/7
+20*I)*x^6 + O(x^7)
+[4, 2]: 0.67139670714180309041636401204046708055 + 0.43109886801837607952052
+096729853340009*I
+[4, 3]: 0.54097378993452809133091313466411641349 + 0.99024588024340488002335
+195542348755756*I
+[4, 4]: 1.7949514649306922611370433739385788319 + 0.323099244779188375242885
+44204680075863*I
+[5, 1]: error("domain error in besseln: 2n mod Z != 0")
+[5, 2]: 0.26938214945091836540555327716757818205 + 1.03976259800634355895849
+86807324939546*I
+[5, 3]: 0.10940542017270712675781308031594165118 + 2.26083223277617932197371
+42212172870084*I
+[5, 4]: 0.79927619103677503777576265264156481050 + 1.08771932295608823645995
+14526225738913*I
+[6, 1]: -380507258880*I*x^-20 - 10569646080*I*x^-18 - 165150720*I*x^-16 + O(
+x^-15)
+[6, 2]: 2.6306151236874532069978536877905029441 E-10 + 121618014.27868918928
+813042666797114529*I
+[6, 3]: 2.6131773608228030862436154291215029458 E-13 + 121963623349.56963053
+464019824934463602*I
+[6, 4]: 3689851.3824160527671366489980962693903 + 205195.3486599858655861754
+3080013707678*I
+[7, 1]: error("domain error in besseln: 2n mod Z != 0")
+[7, 2]: -0.053793008222511491987048146883432543616 + 0.334151434861435901321
+42313893855556329*I
+[7, 3]: -0.56713133425013005304402421178983091608 + 0.3472809452441059847447
+3666211692428233*I
+[7, 4]: 0.56669526894058228371774833075826997077 + 0.56952668509437225367197
+037537154780157*I
+besseli
+[1, 1]: error("incorrect type in isint (t_POL).")
+[1, 2]: error("incorrect type in isint (t_POL).")
+[1, 3]: error("incorrect type in isint (t_POL).")
+[1, 4]: error("incorrect type in isint (t_POL).")
+[2, 1]: 1 + 1/4*x^2 + 1/64*x^4 + 1/2304*x^6 + O(x^7)
+[2, 2]: 1.2660658777520083355982446252147175376
+[2, 3]: 1.0634833707413235192631844154453565293
+[2, 4]: 0.93760847680602927659973819742581871695 + 0.49652994760912213216645
+972122523819950*I
+[3, 1]: 1 + 1/8*x^2 + 1/192*x^4 + 1/9216*x^6 + O(x^7)
+[3, 2]: 0.56515910399248502720769602760986330733
+[3, 3]: 0.25789430539089631636247965952320963419
+[3, 4]: 0.36502802882708778851335190162868643100 + 0.61416033492290361016921
+965837550234589*I
+[4, 1]: 1 + 1/6*x^2 + 1/120*x^4 + 1/5040*x^6 + O(x^7)
+[4, 2]: 0.93767488824548764671726288439139336783
+[4, 3]: 0.58799308679041632548887344016323449809
+[4, 4]: 0.72698064596355457190337225790772836581 + 0.64183847533798587174998
+976767935220305*I
+[5, 1]: 1 + 0.10355339059327376220042218105242451964*x^2 + 0.003791260736238
+8304125791589473295974330*x^4 + 7.1572974488511092712416929822558597732 E-5*
+x^6 + O(x^7)
+[5, 2]: 0.33140333780825958195735832301263837654
+[5, 3]: 0.11521946070729671962775067900799783493
+[5, 4]: 0.12322584912763267310364477086934931491 + 0.47591631113525426300712
+693607959667758*I
+[6, 1]: 1 + 1/44*x^2 + 1/4224*x^4 + 1/658944*x^6 + O(x^7)
+[6, 2]: 2.7529480398368736252357102010027635344 E-10
+[6, 3]: 2.6430419258812795384721773266572060848 E-13
+[6, 4]: -3.9133464867902247105872448856946075458 E-10 + 8.603508207756458902
+4593151718189946275 E-9*I
+[7, 1]: 1 + (1/10 - 1/20*I)*x^2 + (1/320 - 1/320*I)*x^4 + (1/21760 - 1/13056
+*I)*x^6 + O(x^7)
+[7, 2]: 0.25369454079993178519197028239401935067 - 0.70438419774772610421577
+993808361942912*I
+[7, 3]: -0.10655105565636707643885659348968940182 - 0.3307637608360080173839
+7259495461028707*I
+[7, 4]: 0.47875240945839830415507917927631039129 + 0.07128046026390996971374
+9689821284593892*I
+besselj
+[1, 1]: error("incorrect type in isint (t_POL).")
+[1, 2]: error("incorrect type in isint (t_POL).")
+[1, 3]: error("incorrect type in isint (t_POL).")
+[1, 4]: error("incorrect type in isint (t_POL).")
+[2, 1]: 1 - 1/4*x^2 + 1/64*x^4 - 1/2304*x^6 + O(x^7)
+[2, 2]: 0.76519768655796655144971752610266322091
+[2, 3]: 0.93846980724081290422840467359971262557
+[2, 4]: 0.93760847680602927659973819742581871695 - 0.49652994760912213216645
+972122523819950*I
+[3, 1]: 1 - 1/8*x^2 + 1/192*x^4 - 1/9216*x^6 + O(x^7)
+[3, 2]: 0.44005058574493351595968220371891491313
+[3, 3]: 0.24226845767487388638395457614153164080
+[3, 4]: 0.61416033492290361016921965837550234589 + 0.36502802882708778851335
+190162868643100*I
+[4, 1]: 1 - 1/6*x^2 + 1/120*x^4 - 1/5040*x^6 + O(x^7)
+[4, 2]: 0.67139670714180309041636401204046708055
+[4, 3]: 0.54097378993452809133091313466411641349
+[4, 4]: 0.96790128289013061188438215640495233496 + 0.06020460621428170101212
+6630105291248378*I
+[5, 1]: 1 - 0.10355339059327376220042218105242451964*x^2 + 0.003791260736238
+8304125791589473295974330*x^4 - 7.1572974488511092712416929822558597732 E-5*
+x^6 + O(x^7)
+[5, 2]: 0.26938214945091836540555327716757818205
+[5, 3]: 0.10940542017270712675781308031594165118
+[5, 4]: 0.30404549284811478387834080084122518461 + 0.38631241680350013394025
+690489885747395*I
+[6, 1]: 1 - 1/44*x^2 + 1/4224*x^4 - 1/658944*x^6 + O(x^7)
+[6, 2]: 2.6306151236874532069978536877905029441 E-10
+[6, 3]: 2.6131773608228030862436154291215029458 E-13
+[6, 4]: 3.9133464867902247105872448856946075458 E-10 + 8.6035082077564589024
+593151718189946275 E-9*I
+[7, 1]: 1 + (-1/10 + 1/20*I)*x^2 + (1/320 - 1/320*I)*x^4 + (-1/21760 + 1/130
+56*I)*x^6 + O(x^7)
+[7, 2]: 0.26433138404753809049443191093102828265 - 0.55307876742913841391141
+974923215175792*I
+[7, 3]: -0.093457264584001668105256141889771395645 - 0.317067963459697389622
+37100287220573902*I
+[7, 4]: 0.38411919549709275079793532155443291627 - 0.09915442531365647045672
+8990246102219357*I
+besseljh
+[1, 1]: error("incorrect type in jbesselh (t_POL).")
+[1, 2]: error("incorrect type in jbesselh (t_POL).")
+[1, 3]: error("incorrect type in jbesselh (t_POL).")
+[1, 4]: error("incorrect type in jbesselh (t_POL).")
+[2, 1]: 1 - 1/6*x^2 + 1/120*x^4 - 1/5040*x^6 + O(x^7)
+[2, 2]: 0.67139670714180309041636401204046708055
+[2, 3]: 0.54097378993452809133091313466411641349
+[2, 4]: 0.96790128289013061188438215640495233496 + 0.06020460621428170101212
+6630105291248379*I
+[3, 1]: 1 - 1/10*x^2 + 1/280*x^4 - 1/15120*x^6 + O(x^7)
+[3, 2]: 0.24029783912342701089584304474193368046
+[3, 3]: 0.091701699625651302638474313904745269419
+[3, 4]: 0.25115830598729948221749558131361228142 + 0.37320184370263719381653
+345438379595363*I
+[4, 1]: error("incorrect type in jbesselh (t_FRAC).")
+[4, 2]: error("incorrect type in jbesselh (t_FRAC).")
+[4, 3]: error("incorrect type in jbesselh (t_FRAC).")
+[4, 4]: error("incorrect type in jbesselh (t_FRAC).")
+[5, 1]: error("incorrect type in jbesselh (t_REAL).")
+[5, 2]: error("incorrect type in jbesselh (t_REAL).")
+[5, 3]: error("incorrect type in jbesselh (t_REAL).")
+[5, 4]: error("incorrect type in jbesselh (t_REAL).")
+[6, 1]: 1 - 1/46*x^2 + 1/4600*x^4 - 1/745200*x^6 + O(x^7)
+[6, 2]: 5.6781874776346222994224383562334313034 E-11
+[6, 3]: 3.9855051571881421614590449651836245138 E-14
+[6, 4]: -7.5567733209249243890627329164720828368 E-10 + 2.075203626533608360
+9444796696375043842 E-9*I
+[7, 1]: error("incorrect type in jbesselh (t_COMPLEX).")
+[7, 2]: error("incorrect type in jbesselh (t_COMPLEX).")
+[7, 3]: error("incorrect type in jbesselh (t_COMPLEX).")
+[7, 4]: error("incorrect type in jbesselh (t_COMPLEX).")
+besselk
+[1, 1]: error("incorrect type in isint (t_POL).")
+[1, 2]: error("incorrect type in isint (t_POL).")
+[1, 3]: error("incorrect type in isint (t_POL).")
+[1, 4]: error("incorrect type in isint (t_POL).")
+[2, 1]: 1/2*x^2 + 3/64*x^4 + 11/6912*x^6 + O(x^7)
+[2, 2]: 0.42102443824070833333562737921260903614
+[2, 3]: 0.92441907122766586178192416753021698954
+[2, 4]: 0.080197726946517818726968736564279166834 - 0.3572774592853302506059
+4569325002398166*I
+[3, 1]: -4*x^-2 + 1 + 5/16*x^2 + O(x^3)
+[3, 2]: 0.60190723019723457473754000153561733926
+[3, 3]: 1.6564411200033008936964454031740915115
+[3, 4]: 0.024568305523740348612477346185201235995 - 0.4597194738011893647760
+4300851857199811*I
+[4, 1]: 1/2*x^-1 - 1 + 1/4*x - 1/6*x^2 + 1/48*x^3 - 1/120*x^4 + O(x^5)
+[4, 2]: 0.46106850444789455843957587387569458969
+[4, 3]: 1.0750476034999202387227558602482085118
+[4, 4]: 0.068685783419996419480057426969766668173 - 0.3815782598126830739602
+7449697029326916*I
+[5, 1]: error("domain error in besselk: 2n mod Z != 0")
+[5, 2]: 0.84808712130706055249297377794312403937
+[5, 3]: 2.8356740750874926233948621504947623564
+[5, 4]: -0.060833677914566850837201614245364256444 - 0.575727321703454299901
+71626792018756559*I
+[6, 1]: 380507258880*x^-20 - 10569646080*x^-18 + 165150720*x^-16 + O(x^-15)
+[6, 2]: 180713289.90102945469159786130234001591
+[6, 3]: 188937569319.90025964462417816833870273
+[6, 4]: -322320.09995047688108832869622332914303 - 5796004.99791820584997486
+34606037942199*I
+[7, 1]: error("domain error in besselk: 2n mod Z != 0")
+[7, 2]: 0.32545977186584141085464640324923711950 + 0.28942803702599212763456
+715924152302743*I
+[7, 3]: 0.38335176781651847215073123333424046030 + 0.96679218008775594813220
+288497987441030*I
+[7, 4]: 0.31594457269950863123935043154373419884 - 0.40665811731437652111601
+397817696670098*I
+besseln
+[1, 1]: error("incorrect type in isint (t_POL).")
+[1, 2]: error("incorrect type in isint (t_POL).")
+[1, 3]: error("incorrect type in isint (t_POL).")
+[1, 4]: error("incorrect type in isint (t_POL).")
+[2, 1]: -1/2*x^2 + 3/64*x^4 - 11/6912*x^6 + O(x^7)
+[2, 2]: 0.088256964215676957982926766023515162828
+[2, 3]: -0.44451873350670655714839847506833191038
+[2, 4]: 0.44547448893603251403195350532678276945 + 0.71015858200373452117956
+170263043718646*I
+[3, 1]: 4*x^-2 + 1 - 5/16*x^2 + O(x^3)
+[3, 2]: -0.78121282130028871654715000004796482055
+[3, 3]: -1.4714723926702430691885846353232974532
+[3, 4]: -0.65769453559134523686372066951966247983 + 0.6298010039928843822316
+0206685724863709*I
+[4, 1]: -1 + 1/2*x^2 - 1/24*x^4 + 1/720*x^6 + O(x^7)
+[4, 2]: -0.43109886801837607952052096729853340009
+[4, 3]: -0.99024588024340488002335195542348755756
+[4, 4]: -0.26289463856490667423075881194150951025 + 0.8270501820405616492526
+6121753362649691*I
+[5, 1]: error("domain error in besseln: 2n mod Z != 0")
+[5, 2]: -1.0397625980063435589584986807324939546
+[5, 3]: -2.2608322327761793219737142212172870084
+[5, 4]: -0.70140690615258810251969454772371641734 + 0.4952306981886602538974
+2185180033962590*I
+[6, 1]: 380507258880*x^-20 + 10569646080*x^-18 + 165150720*x^-16 + O(x^-15)
+[6, 2]: -121618014.27868918928813042666797114529
+[6, 3]: -121963623349.56963053464019824934463602
+[6, 4]: -205195.34865997726207796767434123461747 + 3689851.38241605237580200
+03190737983316*I
+[7, 1]: error("domain error in besseln: 2n mod Z != 0")
+[7, 2]: -0.88723020229057431523284288817070732122 - 0.3181243922700495824814
+8005781446082626*I
+[7, 3]: -0.66434890870380337436710766498913002135 - 0.4736740696661283849387
+6806990005952043*I
+[7, 4]: -0.66868111040802872412869936561765002092 + 0.1825760734434895329198
+1300920383705450*I
+[0.38318604387456485808270441031554362199 - 1.130318207984970054415392055219
+7266147*I, -0.38318604387456485808270441031554362199 + 0.E-38*I]~
+[-0.38318604387456485808270441031554362199 + 0.E-38*I, 0.3831860438745648580
+8270441031554362199 + 1.1303182079849700544153920552197266147*I]~
+[0.E-39 - 0.44005058574493351595968220371891491313*I, 0.E-39 + 0.44005058574
+493351595968220371891491313*I]~
+[0.E-38 - 0.56515910399248502720769602760986330733*I, 0.E-38 + 0.56515910399
+248502720769602760986330733*I]~
+[-0.20755374871029735167013412472066868268 - 0.20755374871029735167013412472
+066868268*I, -0.20755374871029735167013412472066868268 + 0.20755374871029735
+167013412472066868268*I]~
+[-0.69122984369208426288314166384697051872 + 1.22712623014357148924328078170
+72674867*I, -0.69122984369208426288314166384697051872 - 1.227126230143571489
+2432807817072674867*I]~
+[-0.56515910399248502720769602760986330733 - 0.38318604387456485808270441031
+554362199*I, -0.56515910399248502720769602760986330733 + 0.38318604387456485
+808270441031554362199*I]~
+[0.44005058574493351595968220371891491313 - 0.781212821300288716547150000047
+96482055*I]
+[0.44005058574493351595968220371891491313 + 0.781212821300288716547150000047
+96482055*I]
+[0.56515910399248502720769602760986330733]
+[0.44005058574493351595968220371891491313]
+[0.24029783912342701089584304474193368046]
+[0.60190723019723457473754000153561733926]
+[-0.78121282130028871654715000004796482055]
+[0.44005058574493351595968220371891491313 - 0.781212821300288716547150000047
+96482055*I]~
+[0.44005058574493351595968220371891491313 + 0.781212821300288716547150000047
+96482055*I]~
+[0.56515910399248502720769602760986330733]~
+[0.44005058574493351595968220371891491313]~
+[0.24029783912342701089584304474193368046]~
+[0.60190723019723457473754000153561733926]~
+[-0.78121282130028871654715000004796482055]~
+Mat(0.44005058574493351595968220371891491313 - 0.781212821300288716547150000
+04796482055*I)
+Mat(0.44005058574493351595968220371891491313 + 0.781212821300288716547150000
+04796482055*I)
+Mat(0.56515910399248502720769602760986330733)
+Mat(0.44005058574493351595968220371891491313)
+Mat(0.24029783912342701089584304474193368046)
+Mat(0.60190723019723457473754000153561733926)
+Mat(-0.78121282130028871654715000004796482055)
+0.E-96
+5.3192304053524357058659474657917582463 E-127
+2.0125228237125015700004500237283661172 E-436
+5.7412378153505365740198971395908944617 E-10
+2.0105123109834969409015328783614871604 E-436 + 0.E-475*I
+11.628856980944362293418444710423341176
+Total time spent: 32
diff --git a/src/test/32/bestappr b/src/test/32/bestappr
new file mode 100644
index 0000000..29033b0
--- /dev/null
+++ b/src/test/32/bestappr
@@ -0,0 +1,17 @@
+-1/7
+-1/6
+-1/7
+[]
+-1/7
+-1/7
+[]
+(x^3 + 1)/(x^10 + 2)
+1/2*x^3 + 1/2
+1/(x + 1)
+1/(x + 1)
+[]
+1/(x^6 + 1)
+(x - 2)/(-x^2 + x)
+x^2/(-x + 1)
+1/(-x^5 + x^4)
+Total time spent: 4
diff --git a/src/test/32/bit b/src/test/32/bit
new file mode 100644
index 0000000..d719963
--- /dev/null
+++ b/src/test/32/bit
@@ -0,0 +1,185 @@
+4
+3
+2
+3
+100
+  ***   at top-level: hammingweight(I)
+  ***                 ^----------------
+  *** hammingweight: incorrect type in hammingweight (t_COMPLEX).
+[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
+#bitand
+[0, 0, 0]
+[0, 3, 0]
+[0, -3, 0]
+[0, 36893488147419103231, 0]
+[0, 340282366920938463481821351505477763073, 0]
+[0, -340282366920938463481821351505477763073, 0]
+[0, I, error("forbidden bitwise and t_INT , t_COMPLEX.")]
+[3, 3, 3]
+[3, -3, 1]
+[3, 36893488147419103231, 3]
+[3, 340282366920938463481821351505477763073, 1]
+[3, -340282366920938463481821351505477763073, 3]
+[3, I, error("forbidden bitwise and t_INT , t_COMPLEX.")]
+[-3, -3, -3]
+[-3, 36893488147419103231, 36893488147419103229]
+[-3, 340282366920938463481821351505477763073, 340282366920938463481821351505
+477763073]
+[-3, -340282366920938463481821351505477763073, -3402823669209384634818213515
+05477763075]
+[-3, I, error("forbidden bitwise and t_INT , t_COMPLEX.")]
+[36893488147419103231, 36893488147419103231, 36893488147419103231]
+[36893488147419103231, 340282366920938463481821351505477763073, 184467440737
+09551617]
+[36893488147419103231, -340282366920938463481821351505477763073, 18446744073
+709551615]
+[36893488147419103231, I, error("forbidden bitwise and t_INT , t_COMPLEX.")]
+[340282366920938463481821351505477763073, 3402823669209384634818213515054777
+63073, 340282366920938463481821351505477763073]
+[340282366920938463481821351505477763073, -340282366920938463481821351505477
+763073, 1]
+[340282366920938463481821351505477763073, I, error("forbidden bitwise and t_
+INT , t_COMPLEX.")]
+[-340282366920938463481821351505477763073, -34028236692093846348182135150547
+7763073, -340282366920938463481821351505477763073]
+[-340282366920938463481821351505477763073, I, error("forbidden bitwise and t
+_INT , t_COMPLEX.")]
+[I, I, error("forbidden bitwise and t_COMPLEX , t_COMPLEX.")]
+#bitnegimply
+[0, 0, 0]
+[0, 3, 0]
+[0, -3, 0]
+[0, 36893488147419103231, 0]
+[0, 340282366920938463481821351505477763073, 0]
+[0, -340282366920938463481821351505477763073, 0]
+[0, I, error("forbidden bitwise negated imply t_INT , t_COMPLEX.")]
+[3, 3, 0]
+[3, -3, 2]
+[3, 36893488147419103231, 0]
+[3, 340282366920938463481821351505477763073, 2]
+[3, -340282366920938463481821351505477763073, 0]
+[3, I, error("forbidden bitwise negated imply t_INT , t_COMPLEX.")]
+[-3, -3, 0]
+[-3, 36893488147419103231, -36893488147419103232]
+[-3, 340282366920938463481821351505477763073, -34028236692093846348182135150
+5477763076]
+[-3, -340282366920938463481821351505477763073, 34028236692093846348182135150
+5477763072]
+[-3, I, error("forbidden bitwise negated imply t_INT , t_COMPLEX.")]
+[36893488147419103231, 36893488147419103231, 0]
+[36893488147419103231, 340282366920938463481821351505477763073, 184467440737
+09551614]
+[36893488147419103231, -340282366920938463481821351505477763073, 18446744073
+709551616]
+[36893488147419103231, I, error("forbidden bitwise negated imply t_INT , t_C
+OMPLEX.")]
+[340282366920938463481821351505477763073, 3402823669209384634818213515054777
+63073, 0]
+[340282366920938463481821351505477763073, -340282366920938463481821351505477
+763073, 340282366920938463481821351505477763072]
+[340282366920938463481821351505477763073, I, error("forbidden bitwise negate
+d imply t_INT , t_COMPLEX.")]
+[-340282366920938463481821351505477763073, -34028236692093846348182135150547
+7763073, 0]
+[-340282366920938463481821351505477763073, I, error("forbidden bitwise negat
+ed imply t_INT , t_COMPLEX.")]
+[I, I, error("forbidden bitwise negated imply t_COMPLEX , t_COMPLEX.")]
+#bitor
+[0, 0, 0]
+[0, 3, 3]
+[0, -3, -3]
+[0, 36893488147419103231, 36893488147419103231]
+[0, 340282366920938463481821351505477763073, 3402823669209384634818213515054
+77763073]
+[0, -340282366920938463481821351505477763073, -34028236692093846348182135150
+5477763073]
+[0, I, error("forbidden bitwise or t_INT , t_COMPLEX.")]
+[3, 3, 3]
+[3, -3, -1]
+[3, 36893488147419103231, 36893488147419103231]
+[3, 340282366920938463481821351505477763073, 3402823669209384634818213515054
+77763075]
+[3, -340282366920938463481821351505477763073, -34028236692093846348182135150
+5477763073]
+[3, I, error("forbidden bitwise or t_INT , t_COMPLEX.")]
+[-3, -3, -3]
+[-3, 36893488147419103231, -1]
+[-3, 340282366920938463481821351505477763073, -3]
+[-3, -340282366920938463481821351505477763073, -1]
+[-3, I, error("forbidden bitwise or t_INT , t_COMPLEX.")]
+[36893488147419103231, 36893488147419103231, 36893488147419103231]
+[36893488147419103231, 340282366920938463481821351505477763073, 340282366920
+938463500268095579187314687]
+[36893488147419103231, -340282366920938463481821351505477763073, -3402823669
+20938463463374607431768211457]
+[36893488147419103231, I, error("forbidden bitwise or t_INT , t_COMPLEX.")]
+[340282366920938463481821351505477763073, 3402823669209384634818213515054777
+63073, 340282366920938463481821351505477763073]
+[340282366920938463481821351505477763073, -340282366920938463481821351505477
+763073, -1]
+[340282366920938463481821351505477763073, I, error("forbidden bitwise or t_I
+NT , t_COMPLEX.")]
+[-340282366920938463481821351505477763073, -34028236692093846348182135150547
+7763073, -340282366920938463481821351505477763073]
+[-340282366920938463481821351505477763073, I, error("forbidden bitwise or t_
+INT , t_COMPLEX.")]
+[I, I, error("forbidden bitwise or t_COMPLEX , t_COMPLEX.")]
+#bitxor
+[0, 0, 0]
+[0, 3, 3]
+[0, -3, -3]
+[0, 36893488147419103231, 36893488147419103231]
+[0, 340282366920938463481821351505477763073, 3402823669209384634818213515054
+77763073]
+[0, -340282366920938463481821351505477763073, -34028236692093846348182135150
+5477763073]
+[0, I, error("forbidden bitwise xor t_INT , t_COMPLEX.")]
+[3, 3, 0]
+[3, -3, -2]
+[3, 36893488147419103231, 36893488147419103228]
+[3, 340282366920938463481821351505477763073, 3402823669209384634818213515054
+77763074]
+[3, -340282366920938463481821351505477763073, -34028236692093846348182135150
+5477763076]
+[3, I, error("forbidden bitwise xor t_INT , t_COMPLEX.")]
+[-3, -3, 0]
+[-3, 36893488147419103231, -36893488147419103230]
+[-3, 340282366920938463481821351505477763073, -34028236692093846348182135150
+5477763076]
+[-3, -340282366920938463481821351505477763073, 34028236692093846348182135150
+5477763074]
+[-3, I, error("forbidden bitwise xor t_INT , t_COMPLEX.")]
+[36893488147419103231, 36893488147419103231, 0]
+[36893488147419103231, 340282366920938463481821351505477763073, 340282366920
+938463481821351505477763070]
+[36893488147419103231, -340282366920938463481821351505477763073, -3402823669
+20938463481821351505477763072]
+[36893488147419103231, I, error("forbidden bitwise xor t_INT , t_COMPLEX.")]
+[340282366920938463481821351505477763073, 3402823669209384634818213515054777
+63073, 0]
+[340282366920938463481821351505477763073, -340282366920938463481821351505477
+763073, -2]
+[340282366920938463481821351505477763073, I, error("forbidden bitwise xor t_
+INT , t_COMPLEX.")]
+[-340282366920938463481821351505477763073, -34028236692093846348182135150547
+7763073, 0]
+[-340282366920938463481821351505477763073, I, error("forbidden bitwise xor t
+_INT , t_COMPLEX.")]
+[I, I, error("forbidden bitwise xor t_COMPLEX , t_COMPLEX.")]
+#bitneg
+[0, 36893488147419103231, -1]
+[3, 36893488147419103228, -4]
+[-3, 2, 2]
+[36893488147419103231, 0, -36893488147419103232]
+[340282366920938463481821351505477763073, 18446744073709551614, -34028236692
+0938463481821351505477763074]
+[-340282366920938463481821351505477763073, 18446744073709551616, 34028236692
+0938463481821351505477763072]
+error("incorrect type in bitwise negation (t_COMPLEX).")
+1
+1
+  ***   at top-level: bitneg(1,-2)
+  ***                 ^------------
+  *** bitneg: domain error in bitwise negation: exponent < -1
+340282366920938463463374607431768211454
+Total time spent: 0
diff --git a/src/test/32/bnfinit b/src/test/32/bnfinit
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/32/bnfisintnorm b/src/test/32/bnfisintnorm
new file mode 100644
index 0000000..267c794
--- /dev/null
+++ b/src/test/32/bnfisintnorm
@@ -0,0 +1,771 @@
+   echo = 1 (on)
+? setrand(1);bnf=bnfinit(x^2+105);
+? for(i=1,1000,do(i))
+1:1
+4:1
+9:1
+16:1
+25:1
+36:1
+49:1
+64:1
+81:1
+100:1
+105:1
+106:2
+109:2
+114:2
+121:3
+130:2
+141:2
+144:1
+154:2
+169:3
+186:2
+196:1
+205:2
+225:1
+226:2
+249:2
+256:1
+274:2
+289:1
+301:2
+324:1
+330:2
+361:3
+394:2
+400:1
+420:1
+421:2
+424:2
+429:4
+436:2
+441:1
+445:2
+456:2
+466:2
+469:2
+484:3
+501:2
+505:2
+520:2
+529:1
+541:2
+546:2
+564:2
+576:1
+589:4
+616:2
+625:1
+634:2
+645:2
+676:3
+681:2
+709:2
+729:1
+730:2
+744:2
+781:4
+784:1
+820:2
+834:2
+841:1
+861:2
+889:2
+900:1
+904:2
+945:1
+946:4
+949:4
+954:2
+961:3
+970:2
+981:2
+994:2
+996:2
+? setrand(1);bnf=bnfinit(x^2-65);
+? for(i=1,1000,do(i-500))
+-491:2
+-490:6
+-485:2
+-484:3
+-481:2
+-469:4
+-464:10
+-454:4
+-446:4
+-441:3
+-439:2
+-419:2
+-416:6
+-415:2
+-406:8
+-404:6
+-400:5
+-394:4
+-389:2
+-386:4
+-376:8
+-365:2
+-364:6
+-361:1
+-360:4
+-350:4
+-335:2
+-334:4
+-329:4
+-326:4
+-324:3
+-316:6
+-315:2
+-311:2
+-296:8
+-290:4
+-289:1
+-274:4
+-269:2
+-261:2
+-260:3
+-259:4
+-256:9
+-251:2
+-250:2
+-244:6
+-235:2
+-234:2
+-225:1
+-224:12
+-211:2
+-199:2
+-196:9
+-194:4
+-191:2
+-185:2
+-181:2
+-179:2
+-169:1
+-166:4
+-160:6
+-146:4
+-144:5
+-140:6
+-139:2
+-134:4
+-131:2
+-126:4
+-121:1
+-116:6
+-104:4
+-101:2
+-100:3
+-94:4
+-91:2
+-90:2
+-81:1
+-79:2
+-74:4
+-65:1
+-64:7
+-61:2
+-56:8
+-49:3
+-40:4
+-36:3
+-35:2
+-29:2
+-26:2
+-25:1
+-16:5
+-14:4
+-10:2
+-9:1
+-4:3
+-1:1
+0:1
+1:1
+4:3
+9:1
+10:2
+14:4
+16:5
+25:1
+26:2
+29:2
+35:2
+36:3
+40:4
+49:3
+56:8
+61:2
+64:7
+65:1
+74:4
+79:2
+81:1
+90:2
+91:2
+94:4
+100:3
+101:2
+104:4
+116:6
+121:1
+126:4
+131:2
+134:4
+139:2
+140:6
+144:5
+146:4
+160:6
+166:4
+169:1
+179:2
+181:2
+185:2
+191:2
+194:4
+196:9
+199:2
+211:2
+224:12
+225:1
+234:2
+235:2
+244:6
+250:2
+251:2
+256:9
+259:4
+260:3
+261:2
+269:2
+274:4
+289:1
+290:4
+296:8
+311:2
+315:2
+316:6
+324:3
+326:4
+329:4
+334:4
+335:2
+350:4
+360:4
+361:1
+364:6
+365:2
+376:8
+386:4
+389:2
+394:4
+400:5
+404:6
+406:8
+415:2
+416:6
+419:2
+439:2
+441:3
+446:4
+454:4
+464:10
+469:4
+481:2
+484:3
+485:2
+490:6
+491:2
+? setrand(1);bnf=bnfinit(x^5-37);
+? for(i=1,1000,do(i-500))
+-499:1
+-497:3
+-496:2
+-494:1
+-491:3
+-490:1
+-487:1
+-486:1
+-483:1
+-481:1
+-478:1
+-477:1
+-476:1
+-475:1
+-474:1
+-468:1
+-466:1
+-465:4
+-463:1
+-461:1
+-457:1
+-455:1
+-454:1
+-452:1
+-450:1
+-448:2
+-447:1
+-446:1
+-443:1
+-442:1
+-441:1
+-439:1
+-435:1
+-434:1
+-426:2
+-425:1
+-424:1
+-419:1
+-417:1
+-416:2
+-415:1
+-412:1
+-405:1
+-403:1
+-402:1
+-400:2
+-398:1
+-394:1
+-392:1
+-386:1
+-382:3
+-381:1
+-380:1
+-373:1
+-372:4
+-371:1
+-370:1
+-365:1
+-364:1
+-361:1
+-360:1
+-359:1
+-358:1
+-355:3
+-354:1
+-350:1
+-349:1
+-348:1
+-347:1
+-345:1
+-343:1
+-342:1
+-340:1
+-338:1
+-337:1
+-333:1
+-332:1
+-327:1
+-326:1
+-325:1
+-324:1
+-323:1
+-321:1
+-320:2
+-317:1
+-315:1
+-314:1
+-313:1
+-310:1
+-306:1
+-304:2
+-296:1
+-293:1
+-292:1
+-291:1
+-289:1
+-288:2
+-284:3
+-282:1
+-281:3
+-280:1
+-279:1
+-276:1
+-274:1
+-272:2
+-269:1
+-267:1
+-266:1
+-265:1
+-260:1
+-259:1
+-258:1
+-256:3
+-252:1
+-250:1
+-248:1
+-247:1
+-245:1
+-243:1
+-239:1
+-238:1
+-237:1
+-234:1
+-233:1
+-227:1
+-226:1
+-225:1
+-224:2
+-223:1
+-221:1
+-217:1
+-213:2
+-212:1
+-208:2
+-206:1
+-201:1
+-200:1
+-199:1
+-197:1
+-196:1
+-193:1
+-191:3
+-190:1
+-186:4
+-185:1
+-182:1
+-180:1
+-179:1
+-177:1
+-175:1
+-174:1
+-171:1
+-170:1
+-169:1
+-166:1
+-163:1
+-162:1
+-160:2
+-157:1
+-155:1
+-153:1
+-152:1
+-148:1
+-146:1
+-144:2
+-142:3
+-141:1
+-140:1
+-138:1
+-137:1
+-136:1
+-133:1
+-130:1
+-129:1
+-128:2
+-126:1
+-125:1
+-124:1
+-119:1
+-117:1
+-113:1
+-112:2
+-106:1
+-104:1
+-103:1
+-100:1
+-98:1
+-95:1
+-93:4
+-91:1
+-90:1
+-87:1
+-85:1
+-83:1
+-81:1
+-80:2
+-76:1
+-74:1
+-73:1
+-72:1
+-71:3
+-70:1
+-69:1
+-68:1
+-65:1
+-64:2
+-63:1
+-62:1
+-56:1
+-53:1
+-52:1
+-50:1
+-49:1
+-45:1
+-40:1
+-38:1
+-37:1
+-36:1
+-35:1
+-34:1
+-32:2
+-31:1
+-28:1
+-26:1
+-25:1
+-20:1
+-19:1
+-18:1
+-17:1
+-16:2
+-14:1
+-13:1
+-10:1
+-9:1
+-8:1
+-7:1
+-5:1
+-4:1
+-2:1
+-1:1
+0:1
+1:1
+2:1
+4:1
+5:1
+7:1
+8:1
+9:1
+10:1
+13:1
+14:1
+16:2
+17:1
+18:1
+19:1
+20:1
+25:1
+26:1
+28:1
+31:1
+32:2
+34:1
+35:1
+36:1
+37:1
+38:1
+40:1
+45:1
+49:1
+50:1
+52:1
+53:1
+56:1
+62:1
+63:1
+64:2
+65:1
+68:1
+69:1
+70:1
+71:3
+72:1
+73:1
+74:1
+76:1
+80:2
+81:1
+83:1
+85:1
+87:1
+90:1
+91:1
+93:4
+95:1
+98:1
+100:1
+103:1
+104:1
+106:1
+112:2
+113:1
+117:1
+119:1
+124:1
+125:1
+126:1
+128:2
+129:1
+130:1
+133:1
+136:1
+137:1
+138:1
+140:1
+141:1
+142:3
+144:2
+146:1
+148:1
+152:1
+153:1
+155:1
+157:1
+160:2
+162:1
+163:1
+166:1
+169:1
+170:1
+171:1
+174:1
+175:1
+177:1
+179:1
+180:1
+182:1
+185:1
+186:4
+190:1
+191:3
+193:1
+196:1
+197:1
+199:1
+200:1
+201:1
+206:1
+208:2
+212:1
+213:2
+217:1
+221:1
+223:1
+224:2
+225:1
+226:1
+227:1
+233:1
+234:1
+237:1
+238:1
+239:1
+243:1
+245:1
+247:1
+248:1
+250:1
+252:1
+256:3
+258:1
+259:1
+260:1
+265:1
+266:1
+267:1
+269:1
+272:2
+274:1
+276:1
+279:1
+280:1
+281:3
+282:1
+284:3
+288:2
+289:1
+291:1
+292:1
+293:1
+296:1
+304:2
+306:1
+310:1
+313:1
+314:1
+315:1
+317:1
+320:2
+321:1
+323:1
+324:1
+325:1
+326:1
+327:1
+332:1
+333:1
+337:1
+338:1
+340:1
+342:1
+343:1
+345:1
+347:1
+348:1
+349:1
+350:1
+354:1
+355:3
+358:1
+359:1
+360:1
+361:1
+364:1
+365:1
+370:1
+371:1
+372:4
+373:1
+380:1
+381:1
+382:3
+386:1
+392:1
+394:1
+398:1
+400:2
+402:1
+403:1
+405:1
+412:1
+415:1
+416:2
+417:1
+419:1
+424:1
+425:1
+426:2
+434:1
+435:1
+439:1
+441:1
+442:1
+443:1
+446:1
+447:1
+448:2
+450:1
+452:1
+454:1
+455:1
+457:1
+461:1
+463:1
+465:4
+466:1
+468:1
+474:1
+475:1
+476:1
+477:1
+478:1
+481:1
+483:1
+486:1
+487:1
+490:1
+491:3
+494:1
+496:2
+497:3
+499:1
+500:1
+? bnfisintnorm(bnfinit(x^3+5),5)
+[-x]
+? bnfisintnorm(bnfinit('y^2+93),54647)
+[]
+? print("Total time spent: ",gettime);
+Total time spent: 512
diff --git a/src/test/32/bnr b/src/test/32/bnr
new file mode 100644
index 0000000..ffc2f9a
--- /dev/null
+++ b/src/test/32/bnr
@@ -0,0 +1,44 @@
+[]
+[[4, 0, 0; 0, 2, 0; 0, 0, 2], [4, 0, 2; 0, 2, 1; 0, 0, 1], [4, 2, 0; 0, 1, 0
+; 0, 0, 2], [4, 0, 2; 0, 2, 0; 0, 0, 1], [2, 0, 0; 0, 2, 0; 0, 0, 2], [4, 0,
+ 0; 0, 2, 1; 0, 0, 1], [4, 0, 0; 0, 1, 0; 0, 0, 2], [4, 0, 0; 0, 2, 0; 0, 0,
+ 1], [2, 0, 1; 0, 2, 1; 0, 0, 1], [2, 0, 1; 0, 2, 0; 0, 0, 1], [2, 0, 0; 0, 
+2, 1; 0, 0, 1], [4, 0, 2; 0, 1, 0; 0, 0, 1], [4, 2, 2; 0, 1, 0; 0, 0, 1], [2
+, 0, 0; 0, 1, 0; 0, 0, 2], [4, 2, 0; 0, 1, 0; 0, 0, 1], [2, 0, 0; 0, 2, 0; 0
+, 0, 1], [4, 0, 0; 0, 1, 0; 0, 0, 1], [2, 0, 1; 0, 1, 0; 0, 0, 1]]
+[[2, 0, 1; 0, 1, 0; 0, 0, 1]]
+[[1, 0, 0; 0, 2, 1; 0, 0, 1], [2, 0, 1; 0, 1, 0; 0, 0, 1], [2, 1, 1; 0, 1, 0
+; 0, 0, 1], [1, 0, 0; 0, 1, 0; 0, 0, 2], [2, 1, 0; 0, 1, 0; 0, 0, 1], [1, 0,
+ 0; 0, 2, 0; 0, 0, 1], [2, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0; 0, 1, 0; 0, 0,
+ 1]]
+  ***   at top-level: bnrL1(bnrinit(bnfini
+  ***                 ^--------------------
+  *** bnrL1: incorrect type in bnrL1 [subgroup] (t_INT).
+[32, 0, 27656345068767491604576153420888539136]
+0, 12, [24, 12, 40621487921685401825918161408203125]
+0, 12, [24, 12, 40621487921685401825918161408203125]
+1, 12, [24, 12, 40621487921685401825918161408203125]
+1, 6, [12, 12, 18026977100265125]
+12
+2
+12
+2
+[24, 12, 40621487921685401825918161408203125]
+[4, 4, 262205]
+[24, 12, 40621487921685401825918161408203125]
+[4, 4, 262205]
+[[5, 3; 0, 1], [1, 0]]
+[[5, 3; 0, 1], [0, 0]]
+[[5, 3; 0, 1], [1, 0]]
+[[5, 3; 0, 1], [0, 0]]
+[2, 2, [5, 3; 0, 1]]
+0
+0
+[4, 4, 262205]
+[2, 2, [5, 3; 0, 1]]
+6
+0.99197791640852265169404803750682485962 - 0.1264112865127400184730959576238
+0269676*I
+[4, [2, 2], [[3, 1; 0, 1], [114, 1; 0, 1]]]
+[[1, 0, 0; 0, 1, 0; 0, 0, 1], [0]]
+Total time spent: 36
diff --git a/src/test/32/bnrL1 b/src/test/32/bnrL1
new file mode 100644
index 0000000..e1691b4
--- /dev/null
+++ b/src/test/32/bnrL1
@@ -0,0 +1,33 @@
+[[1, 0.38224508584003564132935849918485739404 + 0.E-38*I], [1, 0.38224508584
+003564132935849918485739404 + 0.E-38*I], [0, -3/2]]
+[[2, 2.9500952396964494033818838179661887250 - 0.788700989456330674320020455
+38518403783*I], [2, 1.9859006356377427761789576705596841597], [2, 2.95009523
+96964494033818838179661887250 + 0.78870098945633067432002045538518403783*I],
+ [1, -0.65847894846240835431252317365398422201]]
+[[1, 1.5356246494250488796129024765168058300 + 1.753485276139541406647384954
+4183759911*I], [2, 2.9500952396964494033818838179661887250 - 0.7887009894563
+3067432002045538518403783*I], [1, 1.1816366573593674455773236880707753792 + 
+2.7350893332487963668865434012735333609*I], [2, 1.98590063563774277617895767
+05596841597], [1, 1.1816366573593674455773236880707753792 - 2.73508933324879
+63668865434012735333609*I], [2, 2.9500952396964494033818838179661887250 + 0.
+78870098945633067432002045538518403783*I], [1, 1.535624649425048879612902476
+5168058300 - 1.7534852761395414066473849544183759911*I], [1, -0.658478948462
+40835431252317365398422201]]
+[[1, 1.5356246494250488796129024765168058300 + 1.753485276139541406647384954
+4183759911*I], [2, 2.9500952396964494033818838179661887250 - 0.7887009894563
+3067432002045538518403783*I], [1, 1.1816366573593674455773236880707753793 + 
+2.7350893332487963668865434012735333609*I], [2, 1.98590063563774277617895767
+05596841597], [1, 1.1816366573593674455773236880707753793 - 2.73508933324879
+63668865434012735333609*I], [2, 2.9500952396964494033818838179661887250 + 0.
+78870098945633067432002045538518403783*I], [1, 1.535624649425048879612902476
+5168058300 - 1.7534852761395414066473849544183759911*I], [0, 4.0000000000000
+000000000000000000000000], [1, 1.5356246494250488796129024765168058300 + 1.7
+534852761395414066473849544183759911*I], [0, 1.60000000000000000000000000000
+00000000 + 0.79999999999999999999999999999999999999*I], [1, 1.18163665735936
+74455773236880707753793 + 2.7350893332487963668865434012735333609*I], [0, 0.
+16666666666666666666666666666666666670], [1, 1.18163665735936744557732368807
+07753793 - 2.7350893332487963668865434012735333609*I], [0, 1.600000000000000
+0000000000000000000000 - 0.79999999999999999999999999999999999999*I], [1, 1.
+5356246494250488796129024765168058300 - 1.7534852761395414066473849544183759
+911*I], [1, -0.65847894846240835431252317365398422201]]
+Total time spent: 264
diff --git a/src/test/32/characteristic b/src/test/32/characteristic
new file mode 100644
index 0000000..53ed8c9
--- /dev/null
+++ b/src/test/32/characteristic
@@ -0,0 +1,13 @@
+0
+0
+6
+0
+3
+0
+3
+0
+3
+2
+0
+0
+Total time spent: 0
diff --git a/src/test/32/charpoly b/src/test/32/charpoly
new file mode 100644
index 0000000..b298187
--- /dev/null
+++ b/src/test/32/charpoly
@@ -0,0 +1,73 @@
+(-y + 1)*x + (y^2 - 2*y - 1)
+(-y + 1)*x + (y^2 - 2*y - 1)
+(-y + 1)*x + (y^2 - 2*y - 1)
+(-y + 1)*x + (y^2 - 2*y - 1)
+x^4 - 16*x^2
+x^4 - 16*x^2
+x^2 - 4*x
+
+[1 14/39]
+
+[0     1]
+
+
+[1 0.35897435897435897435897435897435897438]
+
+[0                                        1]
+
+[[5/3, 21/10], [1, 14/39; 0, 1]]
+-x^2 + (-y - w)*x + (-y + (w^2 - w))
+x - 1
+x - 1.0000000000000000000000000000000000000
+x + Mod(2, 3)
+x - 1/2
+x + (2 + O(3))
+x^2 + 1
+x^2 - x - 1
+Mod(1, 3)*x^2 + Mod(1, 3)*x + Mod(1, 3)
+Mod(1, 18446744073709551629)*x^2 + Mod(18446744073709551627, 184467440737095
+51629)*x + Mod(1, 18446744073709551629)
+x^4 - 4*x^3 + 6*x^2 - 4*x + 1
+x^4 - 4*x^3 + 6*x^2 - 4*x + 1
+x^4 - 73786976294838206516*x^3 + 2041694201525630783657939720089299321846*x^
+2 - 25108406941546723108427206932497066002105857518694949724756*x + 11579208
+9237316195749980275248795307917777354730270819790751905975615430356881
+Mod(1, 3)*x^4 + Mod(1, 3)*x^3 + Mod(1, 3)*x^2 + Mod(1, 3)*x
+Mod(1, 18446744073709551629)*x^4 + Mod(18446744073709551600, 184467440737095
+51629)*x^3 + Mod(46, 18446744073709551629)*x^2 + Mod(16, 1844674407370955162
+9)*x
+
+[1 0]
+
+[0 1]
+
+[;]
+
+[1]
+
+
+[0 0 0]
+
+[0 0 0]
+
+[0 0 0]
+
+
+[Mod(0, 2) Mod(0, 2) Mod(0, 2)]
+
+[Mod(0, 2) Mod(0, 2) Mod(0, 2)]
+
+[Mod(0, 2) Mod(0, 2) Mod(0, 2)]
+
+  ***   at top-level: charpoly(x*matid(3))
+  ***                 ^--------------------
+  *** charpoly: incorrect priority in charpoly: variable x = x
+x^4 - 4*x^3 + 6*x^2 - 4*x + 2
+x^2
+[]
+[[], [;]]
+
+[1]
+
+[[1], Mat(1)]
+Total time spent: 4
diff --git a/src/test/32/chinese b/src/test/32/chinese
new file mode 100644
index 0000000..5dd403a
--- /dev/null
+++ b/src/test/32/chinese
@@ -0,0 +1,5 @@
+Mod(x, x^2 + 1)
+Mod(x, x^4 - 1)
+Mod(2, 3)*x^2 + Mod(1, 6)*x + Mod(1, 6)
+[Mod(1, 4), Mod(1, 6)]
+Total time spent: 0
diff --git a/src/test/32/cmp b/src/test/32/cmp
new file mode 100644
index 0000000..af1e48b
--- /dev/null
+++ b/src/test/32/cmp
@@ -0,0 +1,18 @@
+(f,g)->[cmp(f,f),cmp(f,g),cmp(g,f)]
+[0, -1, 1]
+[0, -1, 1]
+[0, -1, 1]
+[0, -1, 1]
+[0, -1, 1]
+[0, -1, 1]
+[0, -1, 1]
+[0, -1, 1]
+0
+0
+0
+1
+  ***   at top-level: Mod(1,3)>0
+  ***                         ^--
+  *** _>_: forbidden comparison t_INTMOD , t_INT.
+-1
+Total time spent: 0
diff --git a/src/test/32/combinat b/src/test/32/combinat
new file mode 100644
index 0000000..0b87933
--- /dev/null
+++ b/src/test/32/combinat
@@ -0,0 +1,42 @@
+[3628800, -10628640, 12753576, -8409500, 3416930, -902055, 157773, -18150, 1
+320, -55]
+[-39916800, 120543840, -150917976, 105258076, -45995730, 13339535, -2637558,
+ 357423, -32670, 1925, -66]
+[1, 1023, 28501, 145750, 246730, 179487, 63987, 11880, 1155, 55]
+[1, 2047, 86526, 611501, 1379400, 1323652, 627396, 159027, 22275, 1705, 66]
+[[1, 2, 3, 4, 5], [1, 2, 3, 5, 4], [1, 2, 4, 3, 5], [1, 2, 4, 5, 3], [1, 2, 
+5, 3, 4], [1, 2, 5, 4, 3], [1, 3, 2, 4, 5], [1, 3, 2, 5, 4], [1, 3, 4, 2, 5]
+, [1, 3, 4, 5, 2], [1, 3, 5, 2, 4], [1, 3, 5, 4, 2], [1, 4, 2, 3, 5], [1, 4,
+ 2, 5, 3], [1, 4, 3, 2, 5], [1, 4, 3, 5, 2], [1, 4, 5, 2, 3], [1, 4, 5, 3, 2
+], [1, 5, 2, 3, 4], [1, 5, 2, 4, 3], [1, 5, 3, 2, 4], [1, 5, 3, 4, 2], [1, 5
+, 4, 2, 3], [1, 5, 4, 3, 2], [2, 1, 3, 4, 5], [2, 1, 3, 5, 4], [2, 1, 4, 3, 
+5], [2, 1, 4, 5, 3], [2, 1, 5, 3, 4], [2, 1, 5, 4, 3], [2, 3, 1, 4, 5], [2, 
+3, 1, 5, 4], [2, 3, 4, 1, 5], [2, 3, 4, 5, 1], [2, 3, 5, 1, 4], [2, 3, 5, 4,
+ 1], [2, 4, 1, 3, 5], [2, 4, 1, 5, 3], [2, 4, 3, 1, 5], [2, 4, 3, 5, 1], [2,
+ 4, 5, 1, 3], [2, 4, 5, 3, 1], [2, 5, 1, 3, 4], [2, 5, 1, 4, 3], [2, 5, 3, 1
+, 4], [2, 5, 3, 4, 1], [2, 5, 4, 1, 3], [2, 5, 4, 3, 1], [3, 1, 2, 4, 5], [3
+, 1, 2, 5, 4], [3, 1, 4, 2, 5], [3, 1, 4, 5, 2], [3, 1, 5, 2, 4], [3, 1, 5, 
+4, 2], [3, 2, 1, 4, 5], [3, 2, 1, 5, 4], [3, 2, 4, 1, 5], [3, 2, 4, 5, 1], [
+3, 2, 5, 1, 4], [3, 2, 5, 4, 1], [3, 4, 1, 2, 5], [3, 4, 1, 5, 2], [3, 4, 2,
+ 1, 5], [3, 4, 2, 5, 1], [3, 4, 5, 1, 2], [3, 4, 5, 2, 1], [3, 5, 1, 2, 4], 
+[3, 5, 1, 4, 2], [3, 5, 2, 1, 4], [3, 5, 2, 4, 1], [3, 5, 4, 1, 2], [3, 5, 4
+, 2, 1], [4, 1, 2, 3, 5], [4, 1, 2, 5, 3], [4, 1, 3, 2, 5], [4, 1, 3, 5, 2],
+ [4, 1, 5, 2, 3], [4, 1, 5, 3, 2], [4, 2, 1, 3, 5], [4, 2, 1, 5, 3], [4, 2, 
+3, 1, 5], [4, 2, 3, 5, 1], [4, 2, 5, 1, 3], [4, 2, 5, 3, 1], [4, 3, 1, 2, 5]
+, [4, 3, 1, 5, 2], [4, 3, 2, 1, 5], [4, 3, 2, 5, 1], [4, 3, 5, 1, 2], [4, 3,
+ 5, 2, 1], [4, 5, 1, 2, 3], [4, 5, 1, 3, 2], [4, 5, 2, 1, 3], [4, 5, 2, 3, 1
+], [4, 5, 3, 1, 2], [4, 5, 3, 2, 1], [5, 1, 2, 3, 4], [5, 1, 2, 4, 3], [5, 1
+, 3, 2, 4], [5, 1, 3, 4, 2], [5, 1, 4, 2, 3], [5, 1, 4, 3, 2], [5, 2, 1, 3, 
+4], [5, 2, 1, 4, 3], [5, 2, 3, 1, 4], [5, 2, 3, 4, 1], [5, 2, 4, 1, 3], [5, 
+2, 4, 3, 1], [5, 3, 1, 2, 4], [5, 3, 1, 4, 2], [5, 3, 2, 1, 4], [5, 3, 2, 4,
+ 1], [5, 3, 4, 1, 2], [5, 3, 4, 2, 1], [5, 4, 1, 2, 3], [5, 4, 1, 3, 2], [5,
+ 4, 2, 1, 3], [5, 4, 2, 3, 1], [5, 4, 3, 1, 2], [5, 4, 3, 2, 1]]
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 2
+1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 4
+0, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 5
+9, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 7
+8, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 9
+7, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 
+113, 114, 115, 116, 117, 118, 119]
+0
+Total time spent: 4
diff --git a/src/test/32/compat b/src/test/32/compat
new file mode 100644
index 0000000..348144a
--- /dev/null
+++ b/src/test/32/compat
@@ -0,0 +1,2594 @@
+   echo = 1 (on)
+? default(compatible,3)
+  *** default: Warning: user functions re-initialized.
+? +3
+3
+? -5
+-5
+? 5+3
+8
+? 5-3
+2
+? 5/3
+5/3
+? 5\3
+1
+? 5\/3
+2
+? 5%3
+2
+? 5^3
+125
+? \precision=154
+   realprecision = 154 significant digits
+? pi
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+6208998628034825342117067982148086513282306647093844609550582231725359408128
+481
+? \precision=38
+   realprecision = 38 significant digits
+? o(x^12)
+O(x^12)
+? padicno=(5/3)*127+o(127^5)
+44*127 + 42*127^2 + 42*127^3 + 42*127^4 + O(127^5)
+? initrect(0,500,500)
+? abs(-0.01)
+0.010000000000000000000000000000000000000
+? acos(0.5)
+1.0471975511965977461542144610931676281
+? acosh(3)
+1.7627471740390860504652186499595846181
+? acurve=initell([0,0,1,-1,0])
+[0, 0, 1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vecs
+mall([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? apoint=[2,2]
+[2, 2]
+? isoncurve(acurve,apoint)
+1
+? addell(acurve,apoint,apoint)
+[21/25, -56/125]
+? addprimes([nextprime(10^9),nextprime(10^10)])
+[1000000007, 10000000019]
+? adj([1,2;3,4])
+
+[ 4 -2]
+
+[-3  1]
+
+? agm(1,2)
+1.4567910310469068691864323832650819750
+? agm(1+o(7^5),8+o(7^5))
+1 + 4*7 + 6*7^2 + 5*7^3 + 2*7^4 + O(7^5)
+? algdep(2*cos(2*pi/13),6)
+x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1
+? algdep2(2*cos(2*pi/13),6,15)
+x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1
+? akell(acurve,1000000007)
+43800
+? nfpol=x^5-5*x^3+5*x+25
+x^5 - 5*x^3 + 5*x + 25
+? nf=initalg(nfpol)
+[x^5 - 5*x^3 + 5*x + 25, [1, 2], 595125, 45, [[1, -1.08911514572050482502495
+27946671612684, -2.4285174907194186068992069565359418365, 0.7194669112891317
+8943997506477288225737, -2.5558200350691694950646071159426779972; 1, -0.1383
+8372073406036365047976417441696637 - 0.4918163765776864349975328551474152510
+7*I, 1.9647119211288133163138753392090569931 + 0.809714924188978951282940822
+19556466857*I, -0.072312766896812300380582649294307897074 + 2.19808037538462
+76641195195160383234878*I, -0.98796319352507039803950539735452837193 + 1.570
+1452385894131769052374806001981109*I; 1, 1.682941293594312776162956161507997
+6006 + 2.0500351226010726172974286983598602164*I, -0.75045317576910401286427
+186094108607489 + 1.3101462685358123283560773619310445916*I, -0.787420688747
+75359433940488309213323154 + 2.1336633893126618034168454610457936018*I, 1.26
+58732110596551455718089553258673705 - 2.716479010374315056657802803578983483
+5*I], [1, -1.0891151457205048250249527946671612684, -2.428517490719418606899
+2069565359418365, 0.71946691128913178943997506477288225737, -2.5558200350691
+694950646071159426779972; 1, -0.63020009731174679864801261932183221743, 2.77
+44268453177922675968161614046216617, 2.1257676084878153637389368667440155907
+, 0.58218204506434277886573208324566973897; 1, 0.353432655843626071347053090
+97299828470, 1.1549969969398343650309345170134923246, -2.2703931422814399645
+001021653326313849, -2.5581084321144835749447428779547264828; 1, 3.732976416
+1953853934603848598678578170, 0.55969309276670831549180550098995851667, 1.34
+62427005649082090774405779536603703, -1.450605799314659911085993848253116112
+9; 1, -0.36709382900675984113447253685186261580, -2.060599444304916341220349
+2228721306665, -2.9210840780604153977562503441379268334, 3.98235222143397020
+22296117589048508540], [1, -1, -2, 1, -3; 1, -1, 3, 2, 1; 1, 0, 1, -2, -3; 1
+, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, -10, 20; 0, -5
+, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0, 200, 110, 17
+7; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [63, 3,
+ 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, -14, -9; -9, 1
+6, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65, 118, -116; 53
+, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -34]], [3, 5, 2
+3]], [-2.4285174907194186068992069565359418365, 1.96471192112881331631387533
+92090569931 + 0.80971492418897895128294082219556466857*I, -0.750453175769104
+01286427186094108607489 + 1.3101462685358123283560773619310445916*I], [1, 1/
+15*x^4 - 2/3*x^2 + 1/3*x + 4/3, x, 2/15*x^4 - 1/3*x^2 + 2/3*x - 1/3, -1/15*x
+^4 + 1/3*x^3 + 1/3*x^2 - 4/3*x - 2/3], [1, 0, 3, 1, 10; 0, 0, -2, 1, -5; 0, 
+1, 0, 3, -5; 0, 0, 1, 1, 10; 0, 0, 0, 3, 0], [1, 0, 0, 0, 0, 0, -1, -1, -2, 
+4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 1, 0, 0, 0, 1, 1,
+ -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3, -3; 0, 0, 1, 0, 
+0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, -2, -1, -1; 0, 0,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0, 2, 0, -1; 0, 0,
+ 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0, 1, 1, 0, 0, -1]]
+? ba=algtobasis(nf,mod(x^3+5,nfpol))
+[6, 1, 3, 1, 3]~
+? anell(acurve,100)
+[1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4, 3, 1
+0, 2, 0, -1, 4, -9, -2, 6, -12, -4, 8, 15, 0, 2, 12, -1, 0, 6, 0, -9, -6, 2,
+ -10, -12, -4, -9, 12, -6, 2, 0, -4, 1, 18, 10, 0, 0, -12, 8, 12, -8, 8, -6,
+ -8, 4, -30, 8, 0, -6, -4, 9, 0, -1, 2, 3, 0, 5, -12, 4, 8, 9, 18, -15, 6, 0
+, -4, -18, 0, 4, 24, 2, 4, 12, 18, 0, -24, 4, 12, -30, -2]
+? apell(acurve,10007)
+66
+? apell2(acurve,10007)
+66
+? apol=x^3+5*x+1
+x^3 + 5*x + 1
+? apprpadic(apol,1+o(7^8))
+[1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8)]~
+? apprpadic(x^3+5*x+1,mod(x*(1+o(7^8)),x^2+x-1))
+[mod((1 + 3*7 + 3*7^2 + 4*7^3 + 4*7^4 + 4*7^5 + 2*7^6 + 3*7^7 + O(7^8))*x + 
+(2*7 + 6*7^2 + 6*7^3 + 3*7^4 + 3*7^5 + 4*7^6 + 5*7^7 + O(7^8)), x^2 + x - 1)
+]~
+? 4*arg(3+3*i)
+3.1415926535897932384626433832795028842
+? 3*asin(sqrt(3)/2)
+3.1415926535897932384626433832795028842
+? asinh(0.5)
+0.48121182505960344749775891342436842314
+? assmat(x^5-12*x^3+0.0005)
+
+[0 0 0 0 -0.00050000000000000000000000000000000000000]
+
+[1 0 0 0                                            0]
+
+[0 1 0 0                                            0]
+
+[0 0 1 0                                           12]
+
+[0 0 0 1                                            0]
+
+? 3*atan(sqrt(3))
+3.1415926535897932384626433832795028842
+? atanh(0.5)
+0.54930614433405484569762261846126285232
+? basis(x^3+4*x+5)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? basis2(x^3+4*x+5)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? basistoalg(nf,ba)
+mod(x^3 + 5, x^5 - 5*x^3 + 5*x + 25)
+? bernreal(12)
+-0.25311355311355311355311355311355311355
+? bernvec(6)
+[1, 1/6, -1/30, 1/42, -1/30, 5/66, -691/2730]
+? bestappr(pi,10000)
+355/113
+? bezout(123456789,987654321)
+[-8, 1, 9]
+? bigomega(12345678987654321)
+8
+? mcurve=initell([0,0,0,-17,0])
+[0, 0, 0, -17, 0, 0, -34, 0, -289, 816, 0, 314432, 1728, Vecsmall([1]), [Vec
+small([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? mpoints=[[-1,4],[-4,2]]~
+[[-1, 4], [-4, 2]]~
+? mhbi=bilhell(mcurve,mpoints,[9,24])
+[-0.72448571035980184146215805860545027441, 1.307328627832055544492943428892
+1943056]~
+? bin(1.1,5)
+-0.0045457500000000000000000000000000000000
+? binary(65537)
+[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
+? bittest(10^100,100)
+1
+? boundcf(pi,5)
+[3, 7, 15, 1, 292]
+? boundfact(40!+1,100000)
+
+[                                         41 1]
+
+[                                         59 1]
+
+[                                        277 1]
+
+[1217669507565553887239873369513188900554127 1]
+
+? move(0,0,0);box(0,500,500)
+? setrand(1);buchimag(1-10^7,1,1)
+[2416, [1208, 2], [qfi(277, 55, 9028), qfi(1700, 1249, 1700)], 1]
+? setrand(1);bnf=buchinitfu(x^2-x-57,0.2,0.2)
+[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.14159
+26535897932384626433832795028842*I; 2.7124653051843439746808795106061300699]
+, [1.7903417566977293763292119206302198761, 1.289761953065273502503008607239
+5031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.5005798036324
+5587382620331339071677436; -1.7903417566977293763292119206302198761, -1.2897
+619530652735025030086072395031018, 0.E-38, -0.500579803632455873826203313390
+71677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1, 
+[0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1, 
+57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0], 
+229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.0663729752107779
+635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.0
+663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229, 
+114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.06637297
+52107779635959310246705326059, 8.0663729752107779635959310246705326058], [1,
+ x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2.
+7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 0
+]], [[1.7903417566977293763292119206302198761, -1.79034175669772937632921192
+06302198761]]], [0, 0]]
+? buchcertify(bnf)
+1
+? buchfu(bnf)
+[x + 7]
+? setrand(1);buchinitforcefu(x^2-x-100000)
+[mat(5), mat([3, 2, 1, 2, 0, 3, 4, 1, 3, 2, 0, 0, 2, 3, 3, 2, 2, 3]), [-129.
+82045011403975460991182396195022419 + 2.525227547 E-64*I; 129.82045011403975
+460991182396195022419 - 1.691398005 E-65*I], [-41.81126458912994339333950225
+8694361489 + 9.49556775 E-66*I, 9.2399004147902289816376260438840931575 + 3.
+1415926535897932384626433832795028842*I, -11.8746098810754067250973159974311
+61032 + 1.186945968 E-65*I, 129.82045011403975460991182396195022419 + 3.1415
+926535897932384626433832795028842*I, 78.769285110119582234934695659371769545
+ + 3.1415926535897932384626433832795028842*I, 843.83292574125840496442685575
+267645724 + 3.1415926535897932384626433832795028842*I, 99.883795405985217941
+669637700687023734 + 1.780418952 E-66*I, -594.223846471886443404737098934077
+97533 + 2.373891936 E-65*I, 552.41258188275650001139759667538361384 + 6.2831
+853071795864769252867665590057684*I, -943.8166642091125376019420340412912927
+2 + 3.798227098 E-65*I, 512.54404927786333037886705989674625866 + 6.28318530
+71795864769252867665590057684*I, 47.668319071568233997332918482707687879 + 3
+.1415926535897932384626433832795028842*I, -1086.2319199838862708766275101783
+094814 + 3.798227098 E-65*I, 232.80982374359817890011490485449930607 + 6.283
+1853071795864769252867665590057684*I, 504.3016123515104053660165366585076775
+9 + 6.2831853071795864769252867665590057684*I, -541.386315978772896320497890
+27161711131 + 3.1415926535897932384626433832795028842*I, -1447.7317003209533
+662215189714163306131 + 3.1415926535897932384626433832795028842*I, 912.61944
+444475616477613125021268707696 + 6.2831853071795864769252867665590057684*I, 
+-954.43070903388610816947075247138143845 + 1.899113549 E-65*I; 41.8112645891
+29943393339502258694361489 + 9.49556775 E-66*I, -9.2399004147902289816376260
+438840931575 + 6.2831853071795864769252867665590057684*I, 11.874609881075406
+725097315997431161032 + 3.1415926535897932384626433832795028842*I, -129.8204
+5011403975460991182396195022419 + 3.1415926535897932384626433832795028842*I,
+ -78.769285110119582234934695659371769545 + 6.283185307179586476925286766559
+0057684*I, -843.83292574125840496442685575267645724 + 6.28318530717958647692
+52867665590057684*I, -99.883795405985217941669637700687023734 + 3.1415926535
+897932384626433832795028842*I, 594.22384647188644340473709893407797533 + 1.3
+88726783 E-64*I, -552.41258188275650001139759667538361384 + 6.28318530717958
+64769252867665590057684*I, 943.81666420911253760194203404129129272 + 3.14159
+26535897932384626433832795028842*I, -512.54404927786333037886705989674625866
+ + 3.1415926535897932384626433832795028842*I, -47.66831907156823399733291848
+2707687879 + 3.1415926535897932384626433832795028842*I, 1086.231919983886270
+8766275101783094814 + 2.587542211 E-64*I, -232.80982374359817890011490485449
+930607 + 3.1415926535897932384626433832795028842*I, -504.3016123515104053660
+1653665850767759 + 3.1415926535897932384626433832795028842*I, 541.3863159787
+7289632049789027161711131 + 1.317510025 E-64*I, 1447.73170032095336622151897
+14163306131 + 3.418404388 E-64*I, -912.61944444475616477613125021268707696 +
+ 6.2831853071795864769252867665590057684*I, 954.4307090338861081694707524713
+8143845 + 2.219588961 E-64*I], [[2, [1, 1]~, 1, 1, [0, 100000; 1, 1]], [5, [
+4, 1]~, 1, 1, [0, 100000; 1, 1]], [13, [-6, 1]~, 1, 1, [5, 100000; 1, 6]], [
+2, [2, 1]~, 1, 1, [1, 100000; 1, 2]], [5, [5, 1]~, 1, 1, [-1, 100000; 1, 0]]
+, [7, [3, 1]~, 2, 1, [3, 100000; 1, 4]], [13, [5, 1]~, 1, 1, [-6, 100000; 1,
+ -5]], [29, [-14, 1]~, 1, 1, [13, 100000; 1, 14]], [29, [13, 1]~, 1, 1, [-14
+, 100000; 1, -13]], [17, [14, 1]~, 1, 1, [2, 100000; 1, 3]], [17, [19, 1]~, 
+1, 1, [-3, 100000; 1, -2]], [23, [-7, 1]~, 1, 1, [6, 100000; 1, 7]], [23, [6
+, 1]~, 1, 1, [-7, 100000; 1, -6]], [31, [23, 1]~, 1, 1, [7, 100000; 1, 8]], 
+[31, [38, 1]~, 1, 1, [-8, 100000; 1, -7]], [41, [-7, 1]~, 1, 1, [6, 100000; 
+1, 7]], [41, [6, 1]~, 1, 1, [-7, 100000; 1, -6]], [43, [-16, 1]~, 1, 1, [15,
+ 100000; 1, 16]], [43, [15, 1]~, 1, 1, [-16, 100000; 1, -15]]], 0, [x^2 - x 
+- 100000, [2, 0], 400001, 1, [[1, -315.72816130129840161392089489603747004; 
+1, 316.72816130129840161392089489603747004], [1, -315.7281613012984016139208
+9489603747004; 1, 316.72816130129840161392089489603747004], [1, -316; 1, 317
+], [2, 1; 1, 200001], [400001, 200000; 0, 1], [200001, -1; -1, 2], [400001, 
+[200000, 100000; 1, 200001]], [7, 57143]], [-315.728161301298401613920894896
+03747004, 316.72816130129840161392089489603747004], [1, x], [1, 0; 0, 1], [1
+, 0, 0, 100000; 0, 1, 1, 1]], [[5, [5], [[2, 1; 0, 1]]], 129.820450114039754
+60991182396195022419, 1, [2, -1], [37955488401901378100630325489636915406833
+6082609238336*x + 119836165644250789990462835950022871665178127611316131167]
+], [mat(1), [[0, 0]], [[-41.811264589129943393339502258694361489 + 9.4955677
+5 E-66*I, 41.811264589129943393339502258694361489 + 9.49556775 E-66*I]]], [0
+, 0]]
+? setrand(1);bnf=buchinitfu(x^2-x-57,0.2,0.2)
+[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.14159
+26535897932384626433832795028842*I; 2.7124653051843439746808795106061300699]
+, [1.7903417566977293763292119206302198761, 1.289761953065273502503008607239
+5031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.5005798036324
+5587382620331339071677436; -1.7903417566977293763292119206302198761, -1.2897
+619530652735025030086072395031018, 0.E-38, -0.500579803632455873826203313390
+71677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1, 
+[0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1, 
+57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0], 
+229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.0663729752107779
+635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.0
+663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229, 
+114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.06637297
+52107779635959310246705326059, 8.0663729752107779635959310246705326058], [1,
+ x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2.
+7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 0
+]], [[1.7903417566977293763292119206302198761, -1.79034175669772937632921192
+06302198761]]], [0, 0]]
+? setrand(1);buchreal(10^9-3,0,0.5,0.5)
+[4, [4], [qfr(199, 31533, -7123, 0.E-57)], 2800.6252519070160764863706217370
+745514]
+? setrand(1);buchgen(x^4-7,0.2,0.2)
+
+[                                                     x^4 - 7]
+
+[                                                      [2, 1]]
+
+[                                                 [-87808, 1]]
+
+[                                            [1, x, x^2, x^3]]
+
+[[2, [2], [[3, 2, 2, 2; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]]]]
+
+[                     14.229975145405511722395637833443108790]
+
+[                                                           1]
+
+[                                                     [2, -1]]
+
+[                                  [x^2 - x - 1, x^2 + x - 1]]
+
+? setrand(1);buchgenfu(x^2-x-100000)
+
+[                       x^2 - x - 100000]
+
+[                                 [2, 0]]
+
+[                            [400001, 1]]
+
+[                                 [1, x]]
+
+[              [5, [5], [[13, 7; 0, 1]]]]
+
+[129.82045011403975460991182396195022417]
+
+[                                      1]
+
+[                                [2, -1]]
+
+[                                    [;]]
+
+? setrand(1);buchgenforcefu(x^2-x-100000)
+
+[x^2 - x - 100000]
+
+[[2, 0]]
+
+[[400001, 1]]
+
+[[1, x]]
+
+[[5, [5], [[2, 1; 0, 1]]]]
+
+[129.82045011403975460991182396195022419]
+
+[1]
+
+[[2, -1]]
+
+[[379554884019013781006303254896369154068336082609238336*x + 119836165644250
+789990462835950022871665178127611316131167]]
+
+? setrand(1);buchgenfu(x^4+24*x^2+585*x+1791,0.1,0.1)
+
+[x^4 + 24*x^2 + 585*x + 1791]
+
+[[0, 2]]
+
+[[18981, 3087]]
+
+[[1, -10/1029*x^3 + 13/343*x^2 - 165/343*x - 1135/343, 1/147*x^3 + 1/147*x^2
+ - 8/49*x + 138/49, -26/1029*x^3 + 170/1029*x^2 - 429/343*x - 2951/343]]
+
+[[4, [4], [[7, 2, 0, 6; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]]]]
+
+[3.7941269688216589341408274220859400303]
+
+[1]
+
+[[6, 10/1029*x^3 - 13/343*x^2 + 165/343*x + 1478/343]]
+
+[[1/147*x^3 + 1/147*x^2 - 8/49*x - 9/49]]
+
+? buchnarrow(bnf)
+[3, [3], [[3, 2; 0, 1]]]
+? buchray(bnf,[[5,3;0,1],[1,0]])
+[12, [12], [[3, 2; 0, 1]]]
+? bnr=buchrayinitgen(bnf,[[5,3;0,1],[1,0]])
+[[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.1415
+926535897932384626433832795028842*I; 2.7124653051843439746808795106061300699
+], [1.7903417566977293763292119206302198761, 1.28976195306527350250300860723
+95031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.500579803632
+45587382620331339071677436; -1.7903417566977293763292119206302198761, -1.289
+7619530652735025030086072395031018, 0.E-38, -0.50057980363245587382620331339
+071677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1,
+ [0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1,
+ 57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0],
+ 229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.066372975210777
+9635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.
+0663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229,
+ 114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.0663729
+752107779635959310246705326059, 8.0663729752107779635959310246705326058], [1
+, x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2
+.7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 
+0]], [[1.7903417566977293763292119206302198761, -1.7903417566977293763292119
+206302198761]]], [0, [mat([[5, 1]~, 1])]]], [[[5, 3; 0, 1], [1, 0]], [8, [4,
+ 2], [2, [-4, 0]~]], mat([[5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], 1]), [[[[4], [
+2], [2], [Vecsmall([0])], 1]], [[2], [-4], [Vecsmall([1])]]], [1, 0; 0, 1]],
+ [1], mat([1, -3, -6]), [12, [12], [[3, 2; 0, 1]]], [[0, 1; 0, 0], [-1, -1; 
+1, -1], 1]]
+? bnr2=buchrayinitgen(bnf,[[25,13;0,1],[1,1]])
+[[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.1415
+926535897932384626433832795028842*I; 2.7124653051843439746808795106061300699
+], [1.7903417566977293763292119206302198761, 1.28976195306527350250300860723
+95031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.500579803632
+45587382620331339071677436; -1.7903417566977293763292119206302198761, -1.289
+7619530652735025030086072395031018, 0.E-38, -0.50057980363245587382620331339
+071677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1,
+ [0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1,
+ 57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0],
+ 229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.066372975210777
+9635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.
+0663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229,
+ 114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.0663729
+752107779635959310246705326059, 8.0663729752107779635959310246705326058], [1
+, x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2
+.7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 
+0]], [[1.7903417566977293763292119206302198761, -1.7903417566977293763292119
+206302198761]]], [0, [mat([[5, 1]~, 1])]]], [[[25, 13; 0, 1], [1, 1]], [80, 
+[20, 2, 2], [2, [-24, 0]~, [2, 2]~]], mat([[5, [-2, 1]~, 1, 1, [1, 57; 1, 2]
+], 2]), [[[[4], [2], [2], [Vecsmall([0, 0])], 1], [[5], [6], [6], [Vecsmall(
+[0, 0])], mat([1/5, -13/5])]], [[2, 2], [-24, [2, 2]~], [Vecsmall([0, 1]), V
+ecsmall([1, 1])]]], [1, -12, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]], [1], mat([1, -3
+, -6, -6]), [12, [12], [[3, 2; 0, 1]]], [[0, 2, 0; -1, 10, 0], [-2, 0; 0, -1
+0], 2]]
+? bytesize(%)
+4768
+? ceil(-2.5)
+-2
+? centerlift(mod(456,555))
+-99
+? cf(pi)
+[3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2, 1, 84, 2, 1
+, 1, 15, 3, 13, 1, 4, 2, 6, 6]
+? cf2([1,3,5,7,9],(exp(1)-1)/(exp(1)+1))
+[0, 6, 10, 42, 30]
+? changevar(x+y,[z,t])
+  ***   at top-level: changevar(x+y,[z,t])
+  ***                 ^--------------------
+  *** changevar: this function no longer exists.
+? char([1,2;3,4],z)
+z^2 - 5*z - 2
+? char(mod(x^2+x+1,x^3+5*x+1),z)
+z^3 + 7*z^2 + 16*z - 19
+? char1([1,2;3,4],z)
+z^2 - 5*z - 2
+? char2(mod(1,8191)*[1,2;3,4],z)
+z^2 + mod(8186, 8191)*z + mod(8189, 8191)
+? acurve=chell(acurve,[-1,1,2,3])
+[-4, -1, -7, -12, -12, 12, 4, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]),
+ [Vecsmall([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? chinese(mod(7,15),mod(13,21))
+mod(97, 105)
+? apoint=chptell(apoint,[-1,1,2,3])
+[1, 3]
+? isoncurve(acurve,apoint)
+1
+? classno(-12391)
+63
+? classno(1345)
+6
+? classno2(-12391)
+63
+? classno2(1345)
+6
+? coeff(sin(x),7)
+-1/5040
+? compimag(qfi(2,1,3),qfi(2,1,3))
+qfi(2, -1, 3)
+? compo(1+o(7^4),3)
+1
+? compositum(x^4-4*x+2,x^3-x-1)
+[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x
+^2 - 128*x - 5]
+? compositum2(x^4-4*x+2,x^3-x-1)
+[[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*
+x^2 - 128*x - 5, mod(-279140305176/29063006931199*x^11 + 129916611552/290630
+06931199*x^10 + 1272919322296/29063006931199*x^9 - 2813750209005/29063006931
+199*x^8 - 2859411937992/29063006931199*x^7 - 414533880536/29063006931199*x^6
+ - 35713977492936/29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 4
+9785595543672/29063006931199*x^3 + 9423768373204/29063006931199*x^2 - 427797
+76146743/29063006931199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8
+*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), m
+od(-279140305176/29063006931199*x^11 + 129916611552/29063006931199*x^10 + 12
+72919322296/29063006931199*x^9 - 2813750209005/29063006931199*x^8 - 28594119
+37992/29063006931199*x^7 - 414533880536/29063006931199*x^6 - 35713977492936/
+29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 49785595543672/2906
+3006931199*x^3 + 9423768373204/29063006931199*x^2 - 13716769215544/290630069
+31199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12
+*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), -1]]
+? comprealraw(qfr(5,3,-1,0.),qfr(7,1,-1,0.))
+qfr(35, 43, 13, 0.E-38)
+? concat([1,2],[3,4])
+[1, 2, 3, 4]
+? conductor(bnf,[[25,13;0,1],[1,1]])
+[[[5, 3; 0, 1], [1, 0]], [12, [12], [[3, 2; 0, 1]]], mat(12)]
+? conductorofchar(bnr,[2])
+[[5, 3; 0, 1], [0, 0]]
+? conj(1+i)
+1 - I
+? conjvec(mod(x^2+x+1,x^3-x-1))
+[4.0795956234914387860104177508366260326, 0.46020218825428060699479112458168
+698369 + 0.18258225455744299269398828369501930574*I, 0.460202188254280606994
+79112458168698369 - 0.18258225455744299269398828369501930574*I]~
+? content([123,456,789,234])
+3
+? convol(sin(x),x*cos(x))
+x + 1/12*x^3 + 1/2880*x^5 + 1/3628800*x^7 + 1/14631321600*x^9 + 1/1448500838
+40000*x^11 + 1/2982752926433280000*x^13 + 1/114000816848279961600000*x^15 + 
+O(x^17)
+? core(54713282649239)
+5471
+? core2(54713282649239)
+[5471, 100003]
+? coredisc(54713282649239)
+21884
+? coredisc2(54713282649239)
+[21884, 100003/2]
+? cos(1)
+0.54030230586813971740093660744297660373
+? cosh(1)
+1.5430806348152437784779056207570616826
+? move(0,200,150)
+? cursor(0)
+? cvtoi(1.7)
+1
+? cyclo(105)
+x^48 + x^47 + x^46 - x^43 - x^42 - 2*x^41 - x^40 - x^39 + x^36 + x^35 + x^34
+ + x^33 + x^32 + x^31 - x^28 - x^26 - x^24 - x^22 - x^20 + x^17 + x^16 + x^1
+5 + x^14 + x^13 + x^12 - x^9 - x^8 - 2*x^7 - x^6 - x^5 + x^2 + x + 1
+? degree(x^3/(x-1))
+2
+? denom(12345/54321)
+18107
+? deplin(mod(1,7)*[2,-1;1,3])
+[-3, 1]~
+? deriv((x+y)^5,y)
+5*x^4 + 20*y*x^3 + 30*y^2*x^2 + 20*y^3*x + 5*y^4
+? ((x+y)^5)'
+5*x^4 + 20*y*x^3 + 30*y^2*x^2 + 20*y^3*x + 5*y^4
+? det([1,2,3;1,5,6;9,8,7])
+-30
+? det2([1,2,3;1,5,6;9,8,7])
+-30
+? detint([1,2,3;4,5,6])
+3
+? diagonal([2,4,6])
+
+[2 0 0]
+
+[0 4 0]
+
+[0 0 6]
+
+? dilog(0.5)
+0.58224052646501250590265632015968010874
+? dz=vector(30,k,1);dd=vector(30,k,k==1);dm=dirdiv(dd,dz)
+[1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -
+1, 0, 0, 1, 0, 0, -1, -1]
+? deu=direuler(p=2,100,1/(1-apell(acurve,p)*x+if(acurve[12]%p,p,0)*x^2))
+[1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4, 3, 1
+0, 2, 0, -1, 4, -9, -2, 6, -12, -4, 8, 15, 0, 2, 12, -1, 0, 6, 0, -9, -6, 2,
+ -10, -12, -4, -9, 12, -6, 2, 0, -4, 1, 18, 10, 0, 0, -12, 8, 12, -8, 8, -6,
+ -8, 4, -30, 8, 0, -6, -4, 9, 0, -1, 2, 3, 0, 5, -12, 4, 8, 9, 18, -15, 6, 0
+, -4, -18, 0, 4, 24, 2, 4, 12, 18, 0, -24, 4, 12, -30, -2]
+? anell(acurve,100)==deu
+1
+? dirmul(abs(dm),dz)
+[1, 2, 2, 2, 2, 4, 2, 2, 2, 4, 2, 4, 2, 4, 4, 2, 2, 4, 2, 4, 4, 4, 2, 4, 2, 
+4, 2, 4, 2, 8]
+? dirzetak(initalg(x^3-10*x+8),30)
+[1, 2, 0, 3, 1, 0, 0, 4, 0, 2, 1, 0, 0, 0, 0, 5, 1, 0, 0, 3, 0, 2, 0, 0, 2, 
+0, 1, 0, 1, 0]
+? disc(x^3+4*x+12)
+-4144
+? discf(x^3+4*x+12)
+-1036
+? discrayabs(bnr,mat(6))
+[12, 12, 18026977100265125]
+? discrayabs(bnr)
+[24, 12, 40621487921685401825918161408203125]
+? discrayabscond(bnr2)
+0
+? lu=ideallistunitgen(bnf,55);discrayabslist(bnf,lu)
+[[[6, 6, mat([229, 3])]], [], [[], []], [[]], [[12, 12, [5, 3; 229, 6]], [12
+, 12, [5, 3; 229, 6]]], [], [], [], [[], [], []], [], [[], []], [[], []], []
+, [], [[24, 24, [3, 6; 5, 9; 229, 12]], [], [], [24, 24, [3, 6; 5, 9; 229, 1
+2]]], [[]], [[], []], [], [[18, 18, [19, 6; 229, 9]], [18, 18, [19, 6; 229, 
+9]]], [[], []], [], [], [], [], [[], [24, 24, [5, 12; 229, 12]], []], [], [[
+], [], [], []], [], [], [], [], [], [[], [12, 12, [3, 3; 11, 3; 229, 6]], [1
+2, 12, [3, 3; 11, 3; 229, 6]], []], [], [], [[18, 18, [2, 12; 3, 12; 229, 9]
+], [], [18, 18, [2, 12; 3, 12; 229, 9]]], [[12, 12, [37, 3; 229, 6]], [12, 1
+2, [37, 3; 229, 6]]], [], [], [], [], [], [[], []], [[], []], [[], [], [], [
+], [], []], [], [], [[12, 12, [2, 12; 3, 3; 229, 6]], [12, 12, [2, 12; 3, 3;
+ 229, 6]]], [[18, 18, [7, 12; 229, 9]]], [], [[], [], [], []], [], [[], []],
+ [], [[], [24, 24, [5, 9; 11, 6; 229, 12]], [24, 24, [5, 9; 11, 6; 229, 12]]
+, []]]
+? discrayabslistlong(bnf,20)
+[[[[matrix(0,2,j,k,0), 6, 6, mat([229, 3])]], [], [[mat([12, 1]), 0, 0, 0], 
+[mat([13, 1]), 0, 0, 0]], [[mat([10, 1]), 0, 0, 0]], [[mat([20, 1]), 12, 12,
+ [5, 3; 229, 6]], [mat([21, 1]), 12, 12, [5, 3; 229, 6]]], [], [], [], [[mat
+([12, 2]), 0, 0, 0], [[12, 1; 13, 1], 0, 0, 0], [mat([13, 2]), 0, 0, 0]], []
+, [[mat([44, 1]), 0, 0, 0], [mat([45, 1]), 0, 0, 0]], [[[10, 1; 12, 1], 0, 0
+, 0], [[10, 1; 13, 1], 0, 0, 0]], [], [], [[[12, 1; 20, 1], 24, 24, [3, 6; 5
+, 9; 229, 12]], [[13, 1; 20, 1], 0, 0, 0], [[12, 1; 21, 1], 0, 0, 0], [[13, 
+1; 21, 1], 24, 24, [3, 6; 5, 9; 229, 12]]], [[mat([10, 2]), 0, 0, 0]], [[mat
+([68, 1]), 0, 0, 0], [mat([69, 1]), 0, 0, 0]], [], [[mat([76, 1]), 18, 18, [
+19, 6; 229, 9]], [mat([77, 1]), 18, 18, [19, 6; 229, 9]]], [[[10, 1; 20, 1],
+ 0, 0, 0], [[10, 1; 21, 1], 0, 0, 0]]]]
+? discrayrel(bnr,mat(6))
+[6, 2, [125, 13; 0, 1]]
+? discrayrel(bnr)
+[12, 1, [1953125, 1160888; 0, 1]]
+? discrayrelcond(bnr2)
+0
+? divisors(8!)
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 28, 30, 32, 
+35, 36, 40, 42, 45, 48, 56, 60, 63, 64, 70, 72, 80, 84, 90, 96, 105, 112, 12
+0, 126, 128, 140, 144, 160, 168, 180, 192, 210, 224, 240, 252, 280, 288, 315
+, 320, 336, 360, 384, 420, 448, 480, 504, 560, 576, 630, 640, 672, 720, 840,
+ 896, 960, 1008, 1120, 1152, 1260, 1344, 1440, 1680, 1920, 2016, 2240, 2520,
+ 2688, 2880, 3360, 4032, 4480, 5040, 5760, 6720, 8064, 10080, 13440, 20160, 
+40320]
+? divres(345,123)
+[2, 99]~
+? divres(x^7-1,x^5+1)
+[x^2, -x^2 - 1]~
+? divsum(8!,x,x)
+159120
+? postdraw([0,0,0])
+? eigen([1,2,3;4,5,6;7,8,9])
+
+[1 -1.2833494518006402717978106547571267252 0.283349451800640271797810654757
+12672521]
+
+[-2 -0.14167472590032013589890532737856336261 0.6416747259003201358989053273
+7856336260]
+
+[1 1 1]
+
+? eint1(2)
+0.048900510708061119567239835228049522315
+? erfc(2)
+0.0046777349810472658379307436327470713891
+? eta(q)
+1 - q - q^2 + q^5 + q^7 - q^12 - q^15 + O(q^17)
+? euler
+0.57721566490153286060651209008240243104
+? z=y;y=x;eval(z)
+x
+? exp(1)
+2.7182818284590452353602874713526624978
+? extract([1,2,3,4,5,6,7,8,9,10],1000)
+[4, 6, 7, 8, 9, 10]
+? 10!
+3628800
+? fact(10)
+3628800.0000000000000000000000000000000
+? factcantor(x^11+1,7)
+
+[mod(1, 7)*x + mod(1, 7) 1]
+
+[mod(1, 7)*x^10 + mod(6, 7)*x^9 + mod(1, 7)*x^8 + mod(6, 7)*x^7 + mod(1, 7)*
+x^6 + mod(6, 7)*x^5 + mod(1, 7)*x^4 + mod(6, 7)*x^3 + mod(1, 7)*x^2 + mod(6,
+ 7)*x + mod(1, 7) 1]
+
+? centerlift(lift(factfq(x^3+x^2+x-1,3,t^3+t^2+t-1)))
+
+[            x - t 1]
+
+[x + (t^2 + t - 1) 1]
+
+[   x + (-t^2 - 1) 1]
+
+? factmod(x^11+1,7)
+
+[mod(1, 7)*x + mod(1, 7) 1]
+
+[mod(1, 7)*x^10 + mod(6, 7)*x^9 + mod(1, 7)*x^8 + mod(6, 7)*x^7 + mod(1, 7)*
+x^6 + mod(6, 7)*x^5 + mod(1, 7)*x^4 + mod(6, 7)*x^3 + mod(1, 7)*x^2 + mod(6,
+ 7)*x + mod(1, 7) 1]
+
+? factor(17!+1)
+
+[    661 1]
+
+[ 537913 1]
+
+[1000357 1]
+
+? p=x^5+3021*x^4-786303*x^3-6826636057*x^2-546603588746*x+3853890514072057
+x^5 + 3021*x^4 - 786303*x^3 - 6826636057*x^2 - 546603588746*x + 385389051407
+2057
+? fa=[11699,6;2392997,2;4987333019653,2]
+
+[        11699 6]
+
+[      2392997 2]
+
+[4987333019653 2]
+
+? factoredbasis(p,fa)
+[1, x, x^2, 1/11699*x^3 + 1847/11699*x^2 - 132/11699*x - 2641/11699, 1/13962
+3738889203638909659*x^4 - 1552451622081122020/139623738889203638909659*x^3 +
+ 418509858130821123141/139623738889203638909659*x^2 - 6810913798507599407313
+4/139623738889203638909659*x - 13185339461968406/58346808996920447]
+? factoreddiscf(p,fa)
+136866601
+? factoredpolred(p,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? factoredpolred2(p,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? factornf(x^3+x^2-2*x-1,t^3+t^2-2*t-1)
+
+[         x + mod(-t, t^3 + t^2 - 2*t - 1) 1]
+
+[   x + mod(-t^2 + 2, t^3 + t^2 - 2*t - 1) 1]
+
+[x + mod(t^2 + t - 1, t^3 + t^2 - 2*t - 1) 1]
+
+? factorpadic(apol,7,8)
+
+[(1 + O(7^8))*x + (6 + 2*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + 6*7^6 + O(7^8)) 1]
+
+[(1 + O(7^8))*x^2 + (1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8
+))*x + (6 + 5*7 + 3*7^2 + 6*7^3 + 7^4 + 3*7^5 + 2*7^6 + 5*7^7 + O(7^8)) 1]
+
+? factorpadic2(apol,7,8)
+
+[(1 + O(7^8))*x + (6 + 2*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + 6*7^6 + O(7^8)) 1]
+
+[(1 + O(7^8))*x^2 + (1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8
+))*x + (6 + 5*7 + 3*7^2 + 6*7^3 + 7^4 + 3*7^5 + 2*7^6 + 5*7^7 + O(7^8)) 1]
+
+? factpol(x^15-1,3,1)
+
+[                              x - 1 1]
+
+[                        x^2 + x + 1 1]
+
+[            x^4 + x^3 + x^2 + x + 1 1]
+
+[x^8 - x^7 + x^5 - x^4 + x^3 - x + 1 1]
+
+? factpol(x^15-1,0,1)
+
+[                              x - 1 1]
+
+[                        x^2 + x + 1 1]
+
+[            x^4 + x^3 + x^2 + x + 1 1]
+
+[x^8 - x^7 + x^5 - x^4 + x^3 - x + 1 1]
+
+? fibo(100)
+354224848179261915075
+? floor(-1/2)
+-1
+? floor(-2.5)
+-3
+? for(x=1,5,print(x!))
+1
+2
+6
+24
+120
+? fordiv(10,x,print(x))
+1
+2
+5
+10
+? forprime(p=1,30,print(p))
+2
+3
+5
+7
+11
+13
+17
+19
+23
+29
+? forstep(x=0,pi,pi/12,print(sin(x)))
+0.E-38
+0.25881904510252076234889883762404832835
+0.50000000000000000000000000000000000000
+0.70710678118654752440084436210484903928
+0.86602540378443864676372317075293618347
+0.96592582628906828674974319972889736763
+1.0000000000000000000000000000000000000
+0.96592582628906828674974319972889736764
+0.86602540378443864676372317075293618348
+0.70710678118654752440084436210484903931
+0.50000000000000000000000000000000000003
+0.25881904510252076234889883762404832839
+4.70197740 E-38
+? forvec(x=[[1,3],[-2,2]],print1([x[1],x[2]]," "));print(" ");
+[1, -2] [1, -1] [1, 0] [1, 1] [1, 2] [2, -2] [2, -1] [2, 0] [2, 1] [2, 2] [3
+, -2] [3, -1] [3, 0] [3, 1] [3, 2]  
+? frac(-2.7)
+0.30000000000000000000000000000000000000
+? galois(x^6-3*x^2-1)
+[12, 1, 1, "A_4(6) = [2^2]3"]
+? nf3=initalg(x^6+108);galoisconj(nf3)
+[-x, x, -1/12*x^4 - 1/2*x, -1/12*x^4 + 1/2*x, 1/12*x^4 - 1/2*x, 1/12*x^4 + 1
+/2*x]~
+? aut=%[2];galoisapply(nf3,aut,mod(x^5,x^6+108))
+mod(x^5, x^6 + 108)
+? gamh(10)
+1133278.3889487855673345741655888924756
+? gamma(10.5)
+1133278.3889487855673345741655888924756
+? gauss(hilbert(10),[1,2,3,4,5,6,7,8,9,0]~)
+[9236800, -831303990, 18288515520, -170691240720, 832112321040, -23298940665
+00, 3883123564320, -3803844432960, 2020775945760, -449057772020]~
+? gaussmodulo([2,3;5,4],[7,11]~,[1,4]~)
+[-5, -1]~
+? gaussmodulo2([2,3;5,4],[7,11]~,[1,4]~)
+[[-5, -1]~, [4, 9; -5, 8]]
+? gcd(12345678,87654321)
+9
+? getrand()
+Vecsmall([1220248512, -582244995, 485580680, -1643185972, -2103930341, -9694
+07356, 336208700, 1439513079, -1910826353, -2042699820, 222745475, 183991374
+4, -2047550919, -1071833333, -2039471221, 1515565777, 534035968, 1434812685,
+ 491096522, 1540420857, 68034275, 307497241, 177037322, -1910209700, 1688743
+598, -1877071123, -1198166641, 184781149, -180667067, 2125896856, 2115414405
+, -1331546909, 182563306, -1668813554, -146811882, -511262153, -14099254, 20
+08709351, 1769611100, 1617398068, 1847419819, -1678454862, -2004927148, 2020
+407188, -253419536, 501072002, 1183173933, -1588428527, 1264275720, 20001149
+40, -152481498, 1985229903, 1344784530, -2033617121, 494748780, -1133870016,
+ 2127752844, 1447882915, -1884321492, 982974715, -2039242948, 2142404071, -3
+80331009, -1264605663, -770161853, 430911112, 290533278, -927756173, -186399
+3863, -1149321316, 1135347562, 1205814267, -1293541270, 806702053, -63173041
+9, 2120242162, 1259144634, -259813841, -1748638985, -481185123, 459357013, 1
+39809444, -1094803819, -956855999, -365358884, -39929854, 1096255899, 201771
+3822, 1464920131, -953056017, -564919260, -823296656, -777957747, -116664011
+9, -409307993, -160471003, 1950881272, 2140986234, 1617664168, -179627494, 1
+618528081, 949905077, 1996141541, -1056917386, -1728441144, -389361315, 1869
+686534, -2072896478, -751871013, -1178639831, 1962418281, -690140718, -19182
+97446, -68103184, -1955591658, 1354080479, -14042013, -129902947, 201832307,
+ -620608254, -1243546160, -48623379, -479563203, -732434165, -294635956, 193
+5166130, 42381710, 876975196, 64, -967805022])
+? globalred(acurve)
+[37, [1, -1, 2, 2], 1, mat([37, 1]), [[1, 5, 0, 1]]]
+? hclassno(2000003)
+357
+? hell(acurve,apoint)
+0.81778253183950144377417759611107234575
+? hell2(acurve,apoint)
+0.81778253183950144377417759611107234597
+? hermite(amat=1/hilbert(7))
+
+[420   0    0    0   210  168   175]
+
+[  0 840    0    0     0    0   504]
+
+[  0   0 2520    0     0    0  1260]
+
+[  0   0    0 2520     0    0   840]
+
+[  0   0    0    0 13860    0  6930]
+
+[  0   0    0    0     0 5544     0]
+
+[  0   0    0    0     0    0 12012]
+
+? hermite2(amat)
+[[420, 0, 0, 0, 210, 168, 175; 0, 840, 0, 0, 0, 0, 504; 0, 0, 2520, 0, 0, 0,
+ 1260; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 13860, 0, 6930; 0, 0, 0, 0, 0, 
+5544, 0; 0, 0, 0, 0, 0, 0, 12012], [420, 420, 840, 630, 2982, 1092, 4159; 21
+0, 280, 630, 504, 2415, 876, 3395; 140, 210, 504, 420, 2050, 749, 2901; 105,
+ 168, 420, 360, 1785, 658, 2542; 84, 140, 360, 315, 1582, 588, 2266; 70, 120
+, 315, 280, 1421, 532, 2046; 60, 105, 280, 252, 1290, 486, 1866]]
+? hermitemod(amat,detint(amat))
+
+[420   0    0    0   210  168   175]
+
+[  0 840    0    0     0    0   504]
+
+[  0   0 2520    0     0    0  1260]
+
+[  0   0    0 2520     0    0   840]
+
+[  0   0    0    0 13860    0  6930]
+
+[  0   0    0    0     0 5544     0]
+
+[  0   0    0    0     0    0 12012]
+
+? hermiteperm(amat)
+[[360360, 0, 0, 0, 0, 144144, 300300; 0, 27720, 0, 0, 0, 0, 22176; 0, 0, 277
+20, 0, 0, 0, 6930; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 2520, 0, 1260; 0, 0
+, 0, 0, 0, 168, 0; 0, 0, 0, 0, 0, 0, 7], [51480, 4620, 5544, 630, 840, 20676
+, 48619; 45045, 3960, 4620, 504, 630, 18074, 42347; 40040, 3465, 3960, 420, 
+504, 16058, 37523; 36036, 3080, 3465, 360, 420, 14448, 33692; 32760, 2772, 3
+080, 315, 360, 13132, 30574; 30030, 2520, 2772, 280, 315, 12036, 27986; 2772
+0, 2310, 2520, 252, 280, 11109, 25803], Vecsmall([7, 6, 5, 4, 3, 2, 1])]
+? hess(hilbert(7))
+
+[1 90281/58800 -1919947/4344340 4858466341/1095033030 -77651417539/819678732
+6 3386888964/106615355 1/2]
+
+[1/3 43/48 38789/5585580 268214641/109503303 -581330123627/126464718744 4365
+450643/274153770 1/4]
+
+[0 217/2880 442223/7447440 53953931/292008808 -32242849453/168619624992 1475
+457901/1827691800 1/80]
+
+[0 0 1604444/264539275 24208141/149362505292 847880210129/47916076768560 -45
+44407141/103873817300 -29/40920]
+
+[0 0 0 9773092581/35395807550620 -24363634138919/107305824577186620 72118203
+606917/60481351061158500 55899/3088554700]
+
+[0 0 0 0 67201501179065/8543442888354179988 -9970556426629/74082861999267660
+0 -3229/13661312210]
+
+[0 0 0 0 0 -258198800769/9279048099409000 -13183/38381527800]
+
+? hilb(2/3,3/4,5)
+1
+? hilbert(5)
+
+[  1 1/2 1/3 1/4 1/5]
+
+[1/2 1/3 1/4 1/5 1/6]
+
+[1/3 1/4 1/5 1/6 1/7]
+
+[1/4 1/5 1/6 1/7 1/8]
+
+[1/5 1/6 1/7 1/8 1/9]
+
+? hilbp(mod(5,7),mod(6,7))
+1
+? hvector(10,x,1/x)
+[1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10]
+? hyperu(1,1,1)
+0.59634736232319407434107849936927937607
+? i^2
+-1
+? initalgred(nfpol)
+[x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*x + 2, [1, 2], 595125, 4, [[1, -1.089115145
+7205048250249527946671612684, -2.4285174907194186068992069565359418365, 0.71
+946691128913178943997506477288225735, -2.55582003506916949506460711594267799
+71; 1, -0.13838372073406036365047976417441696637 + 0.49181637657768643499753
+285514741525107*I, 1.9647119211288133163138753392090569931 - 0.8097149241889
+7895128294082219556466857*I, -0.072312766896812300380582649294307897123 - 2.
+1980803753846276641195195160383234878*I, -0.98796319352507039803950539735452
+837195 - 1.5701452385894131769052374806001981109*I; 1, 1.6829412935943127761
+629561615079976006 + 2.0500351226010726172974286983598602164*I, -0.750453175
+76910401286427186094108607490 + 1.3101462685358123283560773619310445915*I, -
+0.78742068874775359433940488309213323160 + 2.1336633893126618034168454610457
+936016*I, 1.2658732110596551455718089553258673704 - 2.7164790103743150566578
+028035789834836*I], [1, -1.0891151457205048250249527946671612684, -2.4285174
+907194186068992069565359418365, 0.71946691128913178943997506477288225735, -2
+.5558200350691694950646071159426779971; 1, 0.3534326558436260713470530909729
+9828470, 1.1549969969398343650309345170134923246, -2.27039314228143996450010
+21653326313849, -2.5581084321144835749447428779547264828; 1, -0.630200097311
+74679864801261932183221744, 2.7744268453177922675968161614046216617, 2.12576
+76084878153637389368667440155906, 0.58218204506434277886573208324566973893; 
+1, 3.7329764161953853934603848598678578170, 0.559693092766708315491805500989
+95851657, 1.3462427005649082090774405779536603700, -1.4506057993146599110859
+938482531161132; 1, -0.36709382900675984113447253685186261580, -2.0605994443
+049163412203492228721306664, -2.9210840780604153977562503441379268332, 3.982
+3522214339702022296117589048508541], [1, -1, -2, 1, -3; 1, 0, 1, -2, -3; 1, 
+-1, 3, 2, 1; 1, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, 
+-10, 20; 0, -5, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0
+, 200, 110, 177; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 
+0, 1], [63, 3, 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, 
+-14, -9; -9, 16, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65,
+ 118, -116; 53, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -
+34]], [3, 5, 23]], [-1.0891151457205048250249527946671612684, -0.13838372073
+406036365047976417441696637 + 0.49181637657768643499753285514741525107*I, 1.
+6829412935943127761629561615079976006 + 2.0500351226010726172974286983598602
+164*I], [1, x, -1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, -1/2*x^4 + x^3 - x^2 
+- 9/2*x - 1, -1/2*x^4 + x^3 - 2*x^2 - 7/2*x - 2], [1, 0, -1, -7, -14; 0, 1, 
+1, -2, -15; 0, 0, 0, 2, 4; 0, 0, 1, 1, -2; 0, 0, -1, -3, -4], [1, 0, 0, 0, 0
+, 0, -1, -1, -2, 4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 
+1, 0, 0, 0, 1, 1, -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3,
+ -3; 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, 
+-2, -1, -1; 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0
+, 2, 0, -1; 0, 0, 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0,
+ 1, 1, 0, 0, -1]]
+? initalgred2(nfpol)
+[[x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*x + 2, [1, 2], 595125, 4, [[1, -1.08911514
+57205048250249527946671612684, -2.4285174907194186068992069565359418365, 0.7
+1946691128913178943997506477288225735, -2.5558200350691694950646071159426779
+971; 1, -0.13838372073406036365047976417441696637 + 0.4918163765776864349975
+3285514741525107*I, 1.9647119211288133163138753392090569931 - 0.809714924188
+97895128294082219556466857*I, -0.072312766896812300380582649294307897123 - 2
+.1980803753846276641195195160383234878*I, -0.9879631935250703980395053973545
+2837195 - 1.5701452385894131769052374806001981109*I; 1, 1.682941293594312776
+1629561615079976006 + 2.0500351226010726172974286983598602164*I, -0.75045317
+576910401286427186094108607490 + 1.3101462685358123283560773619310445915*I, 
+-0.78742068874775359433940488309213323160 + 2.133663389312661803416845461045
+7936016*I, 1.2658732110596551455718089553258673704 - 2.716479010374315056657
+8028035789834836*I], [1, -1.0891151457205048250249527946671612684, -2.428517
+4907194186068992069565359418365, 0.71946691128913178943997506477288225735, -
+2.5558200350691694950646071159426779971; 1, 0.353432655843626071347053090972
+99828470, 1.1549969969398343650309345170134923246, -2.2703931422814399645001
+021653326313849, -2.5581084321144835749447428779547264828; 1, -0.63020009731
+174679864801261932183221744, 2.7744268453177922675968161614046216617, 2.1257
+676084878153637389368667440155906, 0.58218204506434277886573208324566973893;
+ 1, 3.7329764161953853934603848598678578170, 0.55969309276670831549180550098
+995851657, 1.3462427005649082090774405779536603700, -1.450605799314659911085
+9938482531161132; 1, -0.36709382900675984113447253685186261580, -2.060599444
+3049163412203492228721306664, -2.9210840780604153977562503441379268332, 3.98
+23522214339702022296117589048508541], [1, -1, -2, 1, -3; 1, 0, 1, -2, -3; 1,
+ -1, 3, 2, 1; 1, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5,
+ -10, 20; 0, -5, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 
+0, 200, 110, 177; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0,
+ 0, 1], [63, 3, 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10,
+ -14, -9; -9, 16, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65
+, 118, -116; 53, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, 
+-34]], [3, 5, 23]], [-1.0891151457205048250249527946671612684, -0.1383837207
+3406036365047976417441696637 + 0.49181637657768643499753285514741525107*I, 1
+.6829412935943127761629561615079976006 + 2.050035122601072617297428698359860
+2164*I], [1, x, -1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, -1/2*x^4 + x^3 - x^2
+ - 9/2*x - 1, -1/2*x^4 + x^3 - 2*x^2 - 7/2*x - 2], [1, 0, -1, -7, -14; 0, 1,
+ 1, -2, -15; 0, 0, 0, 2, 4; 0, 0, 1, 1, -2; 0, 0, -1, -3, -4], [1, 0, 0, 0, 
+0, 0, -1, -1, -2, 4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0,
+ 1, 0, 0, 0, 1, 1, -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3
+, -3; 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1,
+ -2, -1, -1; 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 
+0, 2, 0, -1; 0, 0, 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0
+, 1, 1, 0, 0, -1]], mod(-1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, x^5 - 2*x^4 
++ 3*x^3 + 8*x^2 + 3*x + 2)]
+? vp=primedec(nf,3)[1]
+[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0,
+ 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]]
+? idx=idealmul(nf,idmat(5),vp)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idy=ideallllred(nf,idx,[1,5,6])
+
+[5 0 0 0 2]
+
+[0 5 0 0 2]
+
+[0 0 5 0 1]
+
+[0 0 0 5 2]
+
+[0 0 0 0 1]
+
+? idealadd(nf,idx,idy)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealaddone(nf,idx,idy)
+[[0, -1, -3, -1, 2]~, [1, 1, 3, 1, -2]~]
+? idealaddmultone(nf,[idy,idx])
+[[-5, 0, 0, 0, 0]~, [6, 0, 0, 0, 0]~]
+? idealappr(nf,idy)
+[-1, 4, 2, -1, -3]~
+? idealapprfact(nf,idealfactor(nf,idy))
+[-1, 4, 2, -1, -3]~
+? idealcoprime(nf,idx,idx)
+[-1/3, 1/3, 1/3, 1/3, 0]~
+? idz=idealintersect(nf,idx,idy)
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? idealfactor(nf,idz)
+
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]] 1]
+
+[[5, [-1, 0, 0, 0, 2]~, 4, 1, [2, -3, 0, -12, 6; 2, 2, -5, -2, 6; 1, 1, 0, -
+1, -7; 2, 2, 5, 3, 1; 1, -4, 0, -1, 3]] 3]
+
+[[5, [2, 0, 0, 0, -2]~, 1, 1, [2, 1, 10, -4, 2; 0, 0, -5, 0, 0; 3, -1, 0, -1
+, -7; 0, 0, 5, 5, 5; 1, -2, 0, 3, 1]] 1]
+
+? ideallist(bnf,20)
+[[[1, 0; 0, 1]], [], [[3, 2; 0, 1], [3, 0; 0, 1]], [[2, 0; 0, 2]], [[5, 3; 0
+, 1], [5, 1; 0, 1]], [], [], [], [[9, 5; 0, 1], [3, 0; 0, 3], [9, 3; 0, 1]],
+ [], [[11, 9; 0, 1], [11, 1; 0, 1]], [[6, 4; 0, 2], [6, 0; 0, 2]], [], [], [
+[15, 8; 0, 1], [15, 3; 0, 1], [15, 11; 0, 1], [15, 6; 0, 1]], [[4, 0; 0, 4]]
+, [[17, 14; 0, 1], [17, 2; 0, 1]], [], [[19, 18; 0, 1], [19, 0; 0, 1]], [[10
+, 6; 0, 2], [10, 2; 0, 2]]]
+? idx2=idealmul(nf,idx,idx)
+
+[9 5 7 0 4]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idt=idealmulred(nf,idx,idx)
+
+[2 0 0 0 0]
+
+[0 2 0 0 0]
+
+[0 0 2 0 0]
+
+[0 0 0 2 1]
+
+[0 0 0 0 1]
+
+? idealdiv(nf,idy,idt)
+
+[5   0 5/2   0   1]
+
+[0 5/2   0   0   1]
+
+[0   0 5/2   0 1/2]
+
+[0   0   0 5/2   1]
+
+[0   0   0   0 1/2]
+
+? idealdivexact(nf,idx2,idx)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealhermite(nf,vp)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealhermite2(nf,vp[2],3)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealnorm(nf,idt)
+16
+? idp=idealpow(nf,idx,7)
+
+[2187 1436 1807 630 1822]
+
+[   0    1    0   0    0]
+
+[   0    0    1   0    0]
+
+[   0    0    0   1    0]
+
+[   0    0    0   0    1]
+
+? idealpowred(nf,idx,7)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealtwoelt(nf,idy)
+[5, [2, 2, 1, 2, 1]~]
+? idealtwoelt2(nf,idy,10)
+[-1, 4, 2, 4, 2]~
+? idealval(nf,idp,vp)
+7
+? idmat(5)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? if(3<2,print("bof"),print("ok"));
+ok
+? imag(2+3*i)
+3
+? image([1,3,5;2,4,6;3,5,7])
+
+[1 3]
+
+[2 4]
+
+[3 5]
+
+? image(pi*[1,3,5;2,4,6;3,5,7])
+
+[3.1415926535897932384626433832795028842 9.424777960769379715387930149838508
+6526]
+
+[6.2831853071795864769252867665590057684 12.56637061435917295385057353311801
+1537]
+
+[9.4247779607693797153879301498385086526 15.70796326794896619231321691639751
+4421]
+
+? incgam(2,1)
+0.73575888234288464319104754032292173491
+? incgam3(2,1)
+0.26424111765711535680895245967707826509
+? incgam4(4,1,6)
+5.8860710587430771455283803225833738791
+? indexrank([1,1,1;1,1,1;1,1,2])
+[Vecsmall([1, 3]), Vecsmall([1, 3])]
+? indsort([8,7,6,5])
+Vecsmall([4, 3, 2, 1])
+? initell([0,0,0,-1,0])
+[0, 0, 0, -1, 0, 0, -2, 0, -1, 48, 0, 64, 1728, Vecsmall([1]), [Vecsmall([12
+8, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? initrect(1,700,700)
+? nfz=initzeta(x^2-2);
+? integ(sin(x),x)
+1/2*x^2 - 1/24*x^4 + 1/720*x^6 - 1/40320*x^8 + 1/3628800*x^10 - 1/479001600*
+x^12 + 1/87178291200*x^14 - 1/20922789888000*x^16 + O(x^18)
+? integ((-x^2-2*a*x+8*a)/(x^4-14*x^3+(2*a+49)*x^2-14*a*x+a^2),x)
+(x + a)/(x^2 - 7*x + a)
+? intersect([1,2;3,4;5,6],[2,3;7,8;8,9])
+
+[-1]
+
+[-1]
+
+[-1]
+
+? \precision=19
+   realprecision = 19 significant digits
+? intgen(x=0,pi,sin(x))
+2.000000000000000018
+? sqr(2*intgen(x=0,4,exp(-x^2)))
+3.141592556720305686
+? 4*intinf(x=1,10^20,1/(1+x^2))
+3.141592653589793209
+? intnum(x=-0.5,0.5,1/sqrt(1-x^2))
+1.047197551196597746
+? 2*intopen(x=0,100,sin(x)/x)
+3.124450933778112629
+? \precision=38
+   realprecision = 38 significant digits
+? inverseimage([1,1;2,3;5,7],[2,2,6]~)
+[4, -2]~
+? isdiagonal([1,0,0;0,5,0;0,0,0])
+1
+? isfund(12345)
+1
+? isideal(bnf[7],[5,1;0,1])
+1
+? isincl(x^2+1,x^4+1)
+[-x^2, x^2]
+? isinclfast(initalg(x^2+1),initalg(x^4+1))
+[-x^2, x^2]
+? isirreducible(x^5+3*x^3+5*x^2+15)
+0
+? isisom(x^3+x^2-2*x-1,x^3+x^2-2*x-1)
+[x, -x^2 - x + 1, x^2 - 2]
+? isisomfast(initalg(x^3-2),initalg(x^3-6*x^2-6*x-30))
+[-1/25*x^2 + 13/25*x - 2/5]
+? isprime(12345678901234567)
+0
+? isprincipal(bnf,[5,1;0,1])
+[1]~
+? isprincipalgen(bnf,[5,1;0,1])
+[[1]~, [-2, -1/3]~]
+? isprincipalraygen(bnr,primedec(bnf,7)[1])
+[[9]~, [32879/6561, 13958/19683]~]
+? ispsp(73!+1)
+1
+? isqrt(10!^2+1)
+3628800
+? isset([-3,5,7,7])
+0
+? issqfree(123456789876543219)
+0
+? issquare(12345678987654321)
+1
+? isunit(bnf,mod(3405*x-27466,x^2-x-57))
+[-4, mod(1, 2)]~
+? jacobi(hilbert(6))
+[[1.0827994845655497685388772372251778091 E-7, 1.257075712262519492298239799
+6498755378 E-5, 0.00061574835418265769764919938428527140434, 0.0163215213198
+75822124345079564191505890, 0.24236087057520955213572841585070114077, 1.6188
+998589243390969705881471257800713]~, [-0.00124819408408217511693981630463878
+36342, 0.011144320930724710530678340374220998345, -0.06222658815019768177515
+2126611810492941, 0.24032536934252330399154228873240534569, -0.6145448282925
+8676899320019644273870646, 0.74871921887909485900280109200517845109; 0.03560
+6642944287635266122848131812051370, -0.1797327572407600375877689780374064077
+9, 0.49083920971092436297498316169060045043, -0.6976513752773701229620833504
+6678265583, 0.21108248167867048675227675845247769095, 0.44071750324351206127
+160083580231701802; -0.24067907958842295837736719558855680218, 0.60421220675
+295973004426567844103061740, -0.53547692162107486593474491750949545605, -0.2
+3138937333290388042251363554209048307, 0.36589360730302614149086554211117169
+623, 0.32069686982225190106359024326699463107; 0.625460386549227244577534410
+39459331707, -0.44357471627623954554460416705180104473, -0.41703769221897886
+840494514780771076351, 0.13286315850933553530333839628101576048, 0.394706776
+09501756783094636145991581709, 0.25431138634047419251788312792590944672; -0.
+68980719929383668419801738006926828754, -0.441536641012289662221436497529772
+04448, 0.047034018933115649705614518466541245344, 0.362714921464871475252994
+57604461742112, 0.38819043387388642863111448825992418974, 0.2115308400789652
+4664213667673977991960; 0.27160545336631286930015536176213646338, 0.45911481
+681642960284551392793050867151, 0.54068156310385293880022293448123781988, 0.
+50276286675751538489260566368647786274, 0.3706959077673628086177550108480739
+4603, 0.18144297664876947372217005457727093716]]
+? jbesselh(1,1)
+0.24029783912342701089584304474193368046
+? jell(i)
+1728.0000000000000000000000000000000000
+? kbessel(1+i,1)
+0.32545977186584141085464640324923711950 + 0.2894280370259921276345671592415
+2302743*I
+? kbessel2(1+i,1)
+0.32545977186584141085464640324923711950 + 0.2894280370259921276345671592415
+2302743*I
+? x
+x
+? y
+x
+? ker(matrix(4,4,x,y,x/y))
+
+[-1/2 -1/3 -1/4]
+
+[   1    0    0]
+
+[   0    1    0]
+
+[   0    0    1]
+
+? ker(matrix(4,4,x,y,sin(x+y)))
+
+[1.0000000000000000000000000000000000000 1.080604611736279434801873214885953
+2075]
+
+[-1.0806046117362794348018732148859532075 -0.1677063269057152260048635409984
+7562047]
+
+[1 0]
+
+[0 1]
+
+? keri(matrix(4,4,x,y,x+y))
+
+[ 1  2]
+
+[-2 -3]
+
+[ 1  0]
+
+[ 0  1]
+
+? kerint(matrix(4,4,x,y,x*y))
+
+[-1 -1 -1]
+
+[-1  0  1]
+
+[ 1 -1  1]
+
+[ 0  1 -1]
+
+? kerint1(matrix(4,4,x,y,x*y))
+
+[-1 -1 -1]
+
+[-1  0  1]
+
+[ 1 -1  1]
+
+[ 0  1 -1]
+
+? f(u)=u+1;
+? print(f(5));kill(f);
+6
+? f=12
+12
+? killrect(1)
+? kro(5,7)
+-1
+? kro(3,18)
+0
+? laplace(x*exp(x*y)/(exp(x)-1))
+1 - 1/2*x + 13/6*x^2 - 3*x^3 + 419/30*x^4 - 30*x^5 + 6259/42*x^6 - 420*x^7 +
+ 22133/10*x^8 - 7560*x^9 + 2775767/66*x^10 - 166320*x^11 + 2655339269/2730*x
+^12 - 4324320*x^13 + 264873251/10*x^14 - 129729600*x^15 + O(x^16)
+? lcm(15,-21)
+105
+? length(divisors(1000))
+16
+? legendre(10)
+46189/256*x^10 - 109395/256*x^8 + 45045/128*x^6 - 15015/128*x^4 + 3465/256*x
+^2 - 63/256
+? lex([1,3],[1,3,5])
+-1
+? lexsort([[1,5],[2,4],[1,5,1],[1,4,2]])
+[[1, 4, 2], [1, 5], [1, 5, 1], [2, 4]]
+? lift(chinese(mod(7,15),mod(4,21)))
+67
+? lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)])
+[3, 3, -9, 2, -6]~
+? lindep2([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)],14)
+[-3, -3, 9, -2, 6]~
+? move(0,0,900);line(0,900,0)
+? lines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+? m=1/hilbert(7)
+
+[    49    -1176      8820    -29400      48510     -38808     12012]
+
+[ -1176    37632   -317520   1128960   -1940400    1596672   -504504]
+
+[  8820  -317520   2857680 -10584000   18711000  -15717240   5045040]
+
+[-29400  1128960 -10584000  40320000  -72765000   62092800 -20180160]
+
+[ 48510 -1940400  18711000 -72765000  133402500 -115259760  37837800]
+
+[-38808  1596672 -15717240  62092800 -115259760  100590336 -33297264]
+
+[ 12012  -504504   5045040 -20180160   37837800  -33297264  11099088]
+
+? mp=concat(m,idmat(7))
+
+[49 -1176 8820 -29400 48510 -38808 12012 1 0 0 0 0 0 0]
+
+[-1176 37632 -317520 1128960 -1940400 1596672 -504504 0 1 0 0 0 0 0]
+
+[8820 -317520 2857680 -10584000 18711000 -15717240 5045040 0 0 1 0 0 0 0]
+
+[-29400 1128960 -10584000 40320000 -72765000 62092800 -20180160 0 0 0 1 0 0 
+0]
+
+[48510 -1940400 18711000 -72765000 133402500 -115259760 37837800 0 0 0 0 1 0
+ 0]
+
+[-38808 1596672 -15717240 62092800 -115259760 100590336 -33297264 0 0 0 0 0 
+1 0]
+
+[12012 -504504 5045040 -20180160 37837800 -33297264 11099088 0 0 0 0 0 0 1]
+
+? lll(m)
+
+[-420 -420 840 630 -1092 757 2982]
+
+[-210 -280 630 504  -876 700 2415]
+
+[-140 -210 504 420  -749 641 2050]
+
+[-105 -168 420 360  -658 589 1785]
+
+[ -84 -140 360 315  -588 544 1582]
+
+[ -70 -120 315 280  -532 505 1421]
+
+[ -60 -105 280 252  -486 471 1290]
+
+? lllgram(m)
+
+[1 1 27 -27 69   0 141]
+
+[0 1  5 -23 35 -24  50]
+
+[0 1  4 -22 19 -24  24]
+
+[0 1  4 -21 11 -19  14]
+
+[0 1  4 -20  7 -14   9]
+
+[0 1  4 -19  5 -10   6]
+
+[0 1  4 -18  4  -7   4]
+
+? lllgramint(m)
+
+[1 1 27 -27 69   0 141]
+
+[0 1  5 -23 35 -24  50]
+
+[0 1  4 -22 19 -24  24]
+
+[0 1  4 -21 11 -19  14]
+
+[0 1  4 -20  7 -14   9]
+
+[0 1  4 -19  5 -10   6]
+
+[0 1  4 -18  4  -7   4]
+
+? lllgramkerim(mp~*mp)
+[[-420, -420, 840, 630, 2982, -1092, 757; -210, -280, 630, 504, 2415, -876, 
+700; -140, -210, 504, 420, 2050, -749, 641; -105, -168, 420, 360, 1785, -658
+, 589; -84, -140, 360, 315, 1582, -588, 544; -70, -120, 315, 280, 1421, -532
+, 505; -60, -105, 280, 252, 1290, -486, 471; 420, 0, 0, 0, -210, 168, 35; 0,
+ 840, 0, 0, 0, 0, 336; 0, 0, -2520, 0, 0, 0, -1260; 0, 0, 0, -2520, 0, 0, -8
+40; 0, 0, 0, 0, -13860, 0, 6930; 0, 0, 0, 0, 0, 5544, 0; 0, 0, 0, 0, 0, 0, -
+12012], [0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0
+, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0,
+ 0; 1, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 
+1, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 1]]
+? lllint(m)
+
+[-420 -420 840 630 -1092 757 2982]
+
+[-210 -280 630 504  -876 700 2415]
+
+[-140 -210 504 420  -749 641 2050]
+
+[-105 -168 420 360  -658 589 1785]
+
+[ -84 -140 360 315  -588 544 1582]
+
+[ -70 -120 315 280  -532 505 1421]
+
+[ -60 -105 280 252  -486 471 1290]
+
+? lllintpartial(m)
+
+[-420 -420 -630 840 1092 2982 -83]
+
+[-210 -280 -504 630  876 2415  70]
+
+[-140 -210 -420 504  749 2050 137]
+
+[-105 -168 -360 420  658 1785 169]
+
+[ -84 -140 -315 360  588 1582 184]
+
+[ -70 -120 -280 315  532 1421 190]
+
+[ -60 -105 -252 280  486 1290 191]
+
+? lllkerim(mp)
+[[-420, -420, 840, 630, 2982, -1092, 757; -210, -280, 630, 504, 2415, -876, 
+700; -140, -210, 504, 420, 2050, -749, 641; -105, -168, 420, 360, 1785, -658
+, 589; -84, -140, 360, 315, 1582, -588, 544; -70, -120, 315, 280, 1421, -532
+, 505; -60, -105, 280, 252, 1290, -486, 471; 420, 0, 0, 0, -210, 168, 35; 0,
+ 840, 0, 0, 0, 0, 336; 0, 0, -2520, 0, 0, 0, -1260; 0, 0, 0, -2520, 0, 0, -8
+40; 0, 0, 0, 0, -13860, 0, 6930; 0, 0, 0, 0, 0, 5544, 0; 0, 0, 0, 0, 0, 0, -
+12012], [0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0
+, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0,
+ 0; 1, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 
+1, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 1]]
+? \precision=96
+   realprecision = 96 significant digits
+? ln(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996419
+? lngamma(10^50*i)
+-157079632679489661923132169163975144209858469968811.93673753887608474948977
+0941153418951907406848 + 114129254649702284200899572734218210380055074431438
+64.0947684761073895534327259165813042649761556*I
+? \precision=2000
+   realprecision = 2003 significant digits (2000 digits displayed)
+? log(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996418687542001481020570685733685520235758130557032670751635
+0759619307275708283714351903070386238916734711233501153644979552391204751726
+8157493206515552473413952588295045300709532636664265410423915781495204374043
+0385500801944170641671518644712839968171784546957026271631064546150257207402
+4816377733896385506952606683411372738737229289564935470257626520988596932019
+6505855476470330679365443254763274495125040606943814710468994650622016772042
+4524529612687946546193165174681392672504103802546259656869144192871608293803
+1727143677826548775664850856740776484514644399404614226031930967354025744460
+7030809608504748663852313818167675143866747664789088143714198549423151997354
+8803751658612753529166100071053558249879414729509293113897155998205654392871
+7000721808576102523688921324497138932037843935308877482597017155910708823683
+6275898425891853530243634214367061189236789192372314672321720534016492568727
+4778234453534764811494186423867767744060695626573796008670762571991847340226
+5146283790488306203306114463007371948900274364396500258093651944304119115060
+8094879306786515887090060520346842973619384128965255653968602219412292420757
+4321757489097706752687115817051137009158942665478595964890653058460258668382
+9400228330053820740056770530467870018416240441883323279838634900156312188956
+0650553151272199398332030751408426091479001265168243443893572472788205486271
+5527418772430024897945401961872339808608316648114909306675193393128904316413
+7068139777649817697486890388778999129650361927071088926410523092478391737350
+1229842420499568935992206602204654941510613918788574424557751020683703086661
+9480896412186807790208181588580001688115973056186676199187395200766719214592
+2367206025395954365416553112951759899400560003665135675690512459268257439464
+8316833262490180382424082423145230614096380570070255138770268178516306902551
+3703234053802145019015374029509942262995779647427138157363801729873940704242
+17997226696297993931270694
+? logagm(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996418687542001481020570685733685520235758130557032670751635
+0759619307275708283714351903070386238916734711233501153644979552391204751726
+8157493206515552473413952588295045300709532636664265410423915781495204374043
+0385500801944170641671518644712839968171784546957026271631064546150257207402
+4816377733896385506952606683411372738737229289564935470257626520988596932019
+6505855476470330679365443254763274495125040606943814710468994650622016772042
+4524529612687946546193165174681392672504103802546259656869144192871608293803
+1727143677826548775664850856740776484514644399404614226031930967354025744460
+7030809608504748663852313818167675143866747664789088143714198549423151997354
+8803751658612753529166100071053558249879414729509293113897155998205654392871
+7000721808576102523688921324497138932037843935308877482597017155910708823683
+6275898425891853530243634214367061189236789192372314672321720534016492568727
+4778234453534764811494186423867767744060695626573796008670762571991847340226
+5146283790488306203306114463007371948900274364396500258093651944304119115060
+8094879306786515887090060520346842973619384128965255653968602219412292420757
+4321757489097706752687115817051137009158942665478595964890653058460258668382
+9400228330053820740056770530467870018416240441883323279838634900156312188956
+0650553151272199398332030751408426091479001265168243443893572472788205486271
+5527418772430024897945401961872339808608316648114909306675193393128904316413
+7068139777649817697486890388778999129650361927071088926410523092478391737350
+1229842420499568935992206602204654941510613918788574424557751020683703086661
+9480896412186807790208181588580001688115973056186676199187395200766719214592
+2367206025395954365416553112951759899400560003665135675690512459268257439464
+8316833262490180382424082423145230614096380570070255138770268178516306902551
+3703234053802145019015374029509942262995779647427138157363801729873940704242
+17997226696297993931270694
+? \precision=19
+   realprecision = 19 significant digits
+? bcurve=initell([0,0,0,-3,0])
+[0, 0, 0, -3, 0, 0, -6, 0, -9, 144, 0, 1728, 1728, Vecsmall([1]), [Vecsmall(
+[64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? localred(bcurve,2)
+[6, 2, [1, 1, 1, 0], 1]
+? ccurve=initell([0,0,-1,-1,0])
+[0, 0, -1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vec
+small([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? l=lseriesell(ccurve,2,-37,1)
+0.3815754082607112109
+? lseriesell(ccurve,2,-37,1.2)-l
+-8.13151629 E-20
+? sbnf=smallbuchinit(x^3-x^2-14*x-1)
+[x^3 - x^2 - 14*x - 1, 3, 10889, [1, x, x^2 - x - 9], [-3.233732695981516673
+, -0.07182350902743636345, 4.305556205008953036], 0, mat(2), mat([1, 1, 0, 1
+, 0, 1]), [9, 15, 16, 17, 39, 33, 10], [2, -1], [[0, 1, 0]~, [5, 3, 1]~], [[
+[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0
+]~, [-3, 0, 0]~], 0]]
+? makebigbnf(sbnf)
+[mat(2), mat([1, 1, 0, 1, 0, 1]), [1.173637103435061715 + 3.1415926535897932
+38*I, -4.562279014988837902 + 3.141592653589793238*I; -2.633543432738976050 
++ 3.141592653589793238*I, 1.420330600779487358 + 3.141592653589793238*I; 1.4
+59906329303914335, 3.141948414209350544], [1.246346989334819161, 0.540400637
+6129469728 + 3.141592653589793238*I, -0.6926391142471042845 + 3.141592653589
+793238*I, -1.990056445584799714 + 3.141592653589793238*I, -0.830562594660718
+8640, 0.004375616572659815402, 0; 0.6716827432867392936, -0.8333219883742404
+172 + 3.141592653589793238*I, -0.2461086674077943078, 0.5379005671092853267,
+ -1.552661549868775854 + 3.141592653589793238*I, -0.8738318043071131265 + 3.
+141592653589793238*I, 0; -1.918029732621558455 + 3.141592653589793238*I, 0.2
+929213507612934444, 0.9387477816548985923, 1.452155878475514387, 2.383224144
+529494718 + 3.141592653589793238*I, 0.8694561877344533111 + 3.14159265358979
+3238*I, 0], [[3, [-1, 1, 0]~, 1, 1, [1, 10, 45; 1, 7, 6; 1, 1, -3]], [5, [-1
+, 1, 0]~, 1, 1, [0, 10, 45; 1, 6, 6; 1, 1, -4]], [5, [2, 1, 0]~, 1, 1, [1, -
+17, 42; -2, 4, -9; 1, -2, -3]], [5, [3, 1, 0]~, 1, 1, [2, 19, 46; 2, 9, 11; 
+1, 2, -2]], [13, [19, 1, 0]~, 1, 1, [-2, -53, 38; -6, -3, -29; 1, -6, -6]], 
+[11, [1, 1, 0]~, 1, 1, [-3, -8, 43; -1, 1, -4; 1, -1, -7]], [3, [10, 1, 1]~,
+ 1, 2, [-1, 9, 1; 1, 0, 5; 0, 1, -1]]]~, 0, [x^3 - x^2 - 14*x - 1, [3, 0], 1
+0889, 1, [[1, -3.233732695981516673, 4.690759845041404812; 1, -0.07182350902
+743636345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144
+592], [1, -3.233732695981516673, 4.690759845041404812; 1, -0.071823509027436
+36345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144592]
+, [1, -3, 5; 1, 0, -9; 1, 4, 5], [3, 1, 1; 1, 29, 8; 1, 8, 129], [10889, 569
+8, 8994; 0, 1, 0; 0, 0, 1], [3677, -121, -21; -121, 386, -23; -21, -23, 86],
+ [10889, [1899, 46720, 5235; 5191, 7095, 25956; 1, 5191, 1895]], []], [-3.23
+3732695981516673, -0.07182350902743636345, 4.305556205008953036], [1, x, x^2
+ - x - 9], [1, 0, 9; 0, 1, 1; 0, 0, 1], [1, 0, 0, 0, 9, 1, 0, 1, 44; 0, 1, 0
+, 1, 1, 5, 0, 5, 1; 0, 0, 1, 0, 1, 0, 1, 0, -4]], [[2, [2], [[3, 2, 0; 0, 1,
+ 0; 0, 0, 1]]], 10.34800724602767998, 1, [2, -1], [x, x^2 + 2*x - 4]], [mat(
+1), [[0, 0, 0]], [[1.246346989334819161, 0.6716827432867392936, -1.918029732
+621558455 + 3.141592653589793238*I]]], [[[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]
+~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0]~, [-3, 0, 0]~], 0]]
+? concat(mat(vector(4,x,x)~),vector(4,x,10+x)~)
+
+[1 11]
+
+[2 12]
+
+[3 13]
+
+[4 14]
+
+? matextract(matrix(15,15,x,y,x+y),vector(5,x,3*x),vector(3,y,3*y))
+
+[ 6  9 12]
+
+[ 9 12 15]
+
+[12 15 18]
+
+[15 18 21]
+
+[18 21 24]
+
+? ma=mathell(mcurve,mpoints)
+
+[ 1.172183098700697011 0.4476973883408951692]
+
+[0.4476973883408951692  1.755026016172950714]
+
+? gauss(ma,mhbi)
+[-0.9999999999999999999, 0.9999999999999999999]~
+? (1.*hilbert(7))^(-1)
+
+[49.00000000002166512 -1176.000000000823818 8820.000000007686140 -29400.0000
+0002919865 48510.00000005260063 -38808.00000004482172 12012.00000001455373]
+
+[-1176.000000000829171 37632.00000003143297 -317520.0000002926227 1128960.00
+0001109758 -1940400.000001996436 1596672.000001699551 -504504.0000005513250]
+
+[8820.000000007767680 -317520.0000002938229 2857680.000002730998 -10584000.0
+0001034460 18711000.00001859176 -15717240.00001581475 5045040.000005126835]
+
+[-29400.00000002959150 1128960.000001117474 -10584000.00001037403 40320000.0
+0003925864 -72765000.00007050504 62092800.00005993762 -20180160.00001942077]
+
+[48510.00000005341642 -1940400.000002014519 18711000.00001868372 -72765000.0
+0007065280 133402500.0001268113 -115259760.0001077525 37837800.00003489948]
+
+[-38808.00000004559575 1596672.000001717726 -15717240.00001591859 62092800.0
+0006016001 -115259760.0001079261 100590336.0000916694 -33297264.00002968058]
+
+[12012.00000001482304 -504504.0000005579285 5045040.000005167082 -20180160.0
+0001951774 37837800.00003500041 -33297264.00002971854 11099088.00000961957]
+
+? matsize([1,2;3,4;5,6])
+[3, 2]
+? matrix(5,5,x,y,gcd(x,y))
+
+[1 1 1 1 1]
+
+[1 2 1 2 1]
+
+[1 1 3 1 1]
+
+[1 2 1 4 1]
+
+[1 1 1 1 5]
+
+? matrixqz([1,3;3,5;5,7],0)
+
+[1 1]
+
+[3 2]
+
+[5 3]
+
+? matrixqz2([1/3,1/4,1/6;1/2,1/4,-1/4;1/3,1,0])
+
+[19 12 2]
+
+[ 0  1 0]
+
+[ 0  0 1]
+
+? matrixqz3([1,3;3,5;5,7])
+
+[2 -1]
+
+[1  0]
+
+[0  1]
+
+? max(2,3)
+3
+? min(2,3)
+2
+? minim([2,1;1,2],4,6)
+[6, 2, [0, -1, 1; 1, 1, 0]]
+? mod(-12,7)
+mod(2, 7)
+? modp(-12,7)
+mod(2, 7)
+? mod(10873,49649)^-1
+  ***   at top-level: mod(10873,49649)^-1
+  ***                                 ^---
+  *** _^_: impossible inverse in Fp_inv: mod(131, 49649).
+? modreverse(mod(x^2+1,x^3-x-1))
+mod(x^2 - 3*x + 2, x^3 - 5*x^2 + 8*x - 5)
+? move(0,243,583);cursor(0)
+? mu(3*5*7*11*13)
+-1
+? newtonpoly(x^4+3*x^3+27*x^2+9*x+81,3)
+[2, 2/3, 2/3, 2/3]
+? nextprime(100000000000000000000000)
+100000000000000000000117
+? setrand(1);n=10^8;a=matrix(3,5,j,k,vvector(5,l,random()\n))
+
+[[3, 13, 9, 6, 18]~ [18, 1, 20, 4, 7]~ [11, 6, 19, 10, 18]~ [18, 4, 7, 6, 18
+]~ [6, 7, 10, 9, 15]~]
+
+[[6, 17, 13, 19, 5]~ [7, 13, 4, 10, 13]~ [11, 14, 18, 0, 20]~ [9, 8, 2, 4, 1
+4]~ [16, 2, 1, 8, 16]~]
+
+[[6, 17, 13, 14, 18]~ [5, 6, 20, 21, 1]~ [0, 16, 8, 7, 5]~ [17, 16, 21, 0, 1
+3]~ [3, 7, 8, 16, 18]~]
+
+? aid=[idx,idy,idz,idmat(5),idx]
+[[3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1]
+, [5, 0, 0, 0, 2; 0, 5, 0, 0, 2; 0, 0, 5, 0, 1; 0, 0, 0, 5, 2; 0, 0, 0, 0, 1
+], [15, 10, 5, 0, 12; 0, 5, 0, 0, 2; 0, 0, 5, 0, 1; 0, 0, 0, 5, 2; 0, 0, 0, 
+0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0,
+ 0, 1], [3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0
+, 0, 1]]
+? bb=algtobasis(nf,mod(x^3+x,nfpol))
+[1, 1, 4, 1, 3]~
+? da=nfdetint(nf,[a,aid])
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? nfdiv(nf,ba,bb)
+[584/373, 66/373, -32/373, -105/373, 120/373]~
+? nfdiveuc(nf,ba,bb)
+[2, 0, 0, 0, 0]~
+? nfdivres(nf,ba,bb)
+[[2, 0, 0, 0, 0]~, [4, -1, -5, -1, -3]~]
+? nfhermite(nf,[a,aid])
+[[1, -1, -1; 0, 1, 0; 0, 0, 1], [[3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 
+0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? nfhermitemod(nf,[a,aid],da)
+[[1, -1, -1; 0, 1, 0; 0, 0, 1], [[3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 
+0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? nfmod(nf,ba,bb)
+[4, -1, -5, -1, -3]~
+? nfmul(nf,ba,bb)
+[50, -15, -35, 60, 15]~
+? nfpow(nf,bb,5)
+[-291920, 136855, 230560, -178520, 74190]~
+? nfreduce(nf,ba,idx)
+[1, 0, 0, 0, 0]~
+? nfsmith(nf,[a[,1..3],[1,1,1],[idealinv(nf,idx),idealinv(nf,idy),1]])
+[[5113790367401366394295125, 4367721766083689974291960, 47291190972301090559
+2775, 4905437241737335488566685, 4697851874666647634403882; 0, 5, 0, 0, 2; 0
+, 0, 5, 0, 1; 0, 0, 0, 5, 2; 0, 0, 0, 0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 
+0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0, 0;
+ 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1]]
+? nfval(nf,ba,vp)
+0
+? norm(1+i)
+2
+? norm(mod(x+5,x^3+x+1))
+129
+? norml2(vector(10,x,x))
+385
+? nucomp(qfi(2,1,9),qfi(4,3,5),3)
+qfi(2, -1, 9)
+? form=qfi(2,1,9);nucomp(form,form,3)
+qfi(4, -3, 5)
+? numdiv(2^99*3^49)
+5000
+? numer((x+1)/(x-1))
+x + 1
+? nupow(form,111)
+qfi(2, -1, 9)
+? 1/(1+x)+o(x^20)
+1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 -
+ x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)
+? omega(100!)
+25
+? ordell(acurve,1)
+[8, 3]
+? order(mod(33,2^16+1))
+2048
+? tcurve=initell([1,0,1,-19,26]);
+? orderell(tcurve,[1,2])
+6
+? ordred(x^3-12*x+45*x-1)
+[x - 1, x^3 + 33*x - 1]
+? padicprec(padicno,127)
+5
+? pascal(8)
+
+[1 0  0  0  0  0  0 0 0]
+
+[1 1  0  0  0  0  0 0 0]
+
+[1 2  1  0  0  0  0 0 0]
+
+[1 3  3  1  0  0  0 0 0]
+
+[1 4  6  4  1  0  0 0 0]
+
+[1 5 10 10  5  1  0 0 0]
+
+[1 6 15 20 15  6  1 0 0]
+
+[1 7 21 35 35 21  7 1 0]
+
+[1 8 28 56 70 56 28 8 1]
+
+? perf([2,0,1;0,2,1;1,1,2])
+6
+? permutation(7,1035)
+[2, 4, 6, 1, 5, 7, 3]
+? permutation2num([4,7,1,6,3,5,2])
+2781
+? pf(-44,3)
+qfi(3, 2, 4)
+? phi(257^2)
+65792
+? pi
+3.141592653589793239
+? b=10;a=1<<b;plot(x=-5,5,round(sin(x)<<b)/a)
+
+        1 x""x_''''''''''''''''''''''''''''''''''_x""x'''''''''''''''''''|
+          |    x                                _     "_                 |
+          |     x                              _        _                |
+          |      x                            _                          |
+          |       _                                      "               |
+          |                                  "            x              |
+          |        x                        _                            |
+          |                                                "             |
+          |         "                      x                _            |
+          |          _                                                   |
+          |                               "                  x           |
+          ````````````x``````````````````_````````````````````````````````
+          |                                                   "          |
+          |            "                x                      _         |
+          |             _                                                |
+          |                            "                        x        |
+          |              x            _                                  |
+          |               _                                      "       |
+          |                          "                            x      |
+          |                "        "                              x     |
+          |                 "_     "                                x    |
+       -1 |...................x__x".................................."x__x
+          -5                                                             5
+? pnqn([2,6,10,14,18,22,26])
+
+[19318376 741721]
+
+[ 8927353 342762]
+
+? pnqn([1,1,1,1,1,1,1,1;1,1,1,1,1,1,1,1])
+
+[34 21]
+
+[21 13]
+
+? point(0,225,334)
+? points(0,vector(10,k,10*k),vector(10,k,5*k*k))
+? pointell(acurve,zell(acurve,apoint))
+[1.000000000000000000, 3.000000000000000000]
+? polint([0,2,3],[0,4,9],5)
+25
+? polred(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+[x - 1, x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1]
+? polred2(x^4-28*x^3-458*x^2+9156*x-25321)
+
+[                                                1           x - 1]
+
+[                   1/115*x^2 - 14/115*x - 212/115   x^2 - 2*x - 9]
+
+[                  -1/115*x^2 + 14/115*x + 442/115   x^2 - 2*x - 9]
+
+[                   1/115*x^2 - 14/115*x - 327/115        x^2 - 10]
+
+[1/4485*x^3 - 7/1495*x^2 - 1034/4485*x + 7924/4485 x^4 - 8*x^2 + 6]
+
+? polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1
+? polredabs2(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+[x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1, mod(2*x^4 - x^3 + 3*x^2 - 3*x - 1, x^5 -
+ x^4 + 2*x^3 - 4*x^2 + x - 1)]
+? polsym(x^17-1,17)
+[17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17]~
+? polvar(name^4-other)
+name
+? poly(sin(x),x)
+-1/1307674368000*x^15 + 1/6227020800*x^13 - 1/39916800*x^11 + 1/362880*x^9 -
+ 1/5040*x^7 + 1/120*x^5 - 1/6*x^3 + x
+? polylog(5,0.5)
+0.5084005792422687074
+? polylog(-4,t)
+(t^4 + 11*t^3 + 11*t^2 + t)/(-t^5 + 5*t^4 - 10*t^3 + 10*t^2 - 5*t + 1)
+? polylogd(5,0.5)
+1.033792745541689064
+? polylogdold(5,0.5)
+1.034459423449010486
+? polylogp(5,0.5)
+0.9495693489964922601
+? poly([1,2,3,4,5],x)
+x^4 + 2*x^3 + 3*x^2 + 4*x + 5
+? polyrev([1,2,3,4,5],x)
+5*x^4 + 4*x^3 + 3*x^2 + 2*x + 1
+? polzag(6,3)
+4608*x^6 - 13824*x^5 + 46144/3*x^4 - 23168/3*x^3 + 5032/3*x^2 - 120*x + 1
+? postdraw([0,20,20])
+? postploth(x=-5,5,sin(x))
+[-5.000000000000000000, 5.000000000000000000, -0.9999964107564721649, 0.9999
+964107564721649]
+? postploth2(t=0,2*pi,[sin(5*t),sin(7*t)])
+[-0.9999994509568810308, 0.9999994509568810308, -0.9999994509568810308, 0.99
+99994509568810308]
+? postplothraw(vector(100,k,k),vector(100,k,k*k/100))
+[1.000000000000000000, 100.0000000000000000, 0.01000000000000000021, 100.000
+0000000000000]
+? powell(acurve,apoint,10)
+[-28919032218753260057646013785951999/292736325329248127651484680640160000, 
+478051489392386968218136375373985436596569736643531551/158385319626308443937
+475969221994173751192384064000000]
+? cmcurve=initell([0,-3/4,0,-2,-1])
+[0, -3/4, 0, -2, -1, -3, -4, -4, -1, 105, 1323, -343, -3375, Vecsmall([1]), 
+[Vecsmall([64, -1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? powell(cmcurve,[x,y],quadgen(-7))
+[((-2 + 3*w)*x^2 + (6 - w))/((-2 - 5*w)*x + (-4 - 2*w)), ((34 - 11*w)*x^3 + 
+(40 - 28*w)*x^2 + (22 + 23*w)*x)/((-90 - w)*x^2 + (-136 + 44*w)*x + (-40 + 2
+8*w))]
+? powrealraw(qfr(5,3,-1,0.),3)
+qfr(125, 23, 1, 0.E-18)
+? pprint((x-12*y)/(y+13*x));
+-11/14
+? pprint([1,2;3,4])
+[1, 2; 3, 4]
+? pprint1(x+y);pprint(x+y);
+2*x2*x
+? \precision=96
+   realprecision = 96 significant digits
+? pi
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+620899862803482534212
+? prec(pi,20)
+3.1415926535897932384626433833
+? precision(cmcurve)
+28
+? \precision=38
+   realprecision = 38 significant digits
+? prime(100)
+541
+? primedec(nf,2)
+[[2, [3, 0, 1, 0, 0]~, 1, 1, [0, 2, 0, -4, -2; 0, 0, 0, 2, 0; 0, 0, -2, -2, 
+-2; 1, 0, 3, 0, -1; 1, 0, 1, 0, -1]], [2, [12, -4, -2, 11, 3]~, 1, 4, [1, -1
+, 3, -1, 1; 0, 0, -2, -1, 1; 1, 0, 1, 0, -2; 0, 0, 1, 2, 2; 0, -1, 0, 1, 1]]
+]
+? primedec(nf,3)
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]], [3, [1, 1, 1, 0, 0]~, 2, 2, [0, -6,
+ 3, -9, 9; 2, -1, -7, -5, 7; 2, 1, 0, 1, -7; 1, 2, 3, 2, 4; 0, -5, -1, 0, 2]
+]]
+? primedec(nf,11)
+[[11, [11, 0, 0, 0, 0]~, 1, 5, 1]]
+? primes(100)
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 
+157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 2
+39, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 33
+1, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421
+, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541]
+? forprime(p=2,100,print(p," ",lift(primroot(p))))
+2 1
+3 2
+5 2
+7 3
+11 2
+13 2
+17 3
+19 2
+23 5
+29 2
+31 3
+37 2
+41 6
+43 3
+47 5
+53 2
+59 2
+61 2
+67 2
+71 7
+73 5
+79 3
+83 2
+89 3
+97 5
+? principalideal(nf,mod(x^3+5,nfpol))
+[6, 1, 3, 1, 3]~
+? print((x-12*y)/(y+13*x));
+-11/14
+? print([1,2;3,4])
+[1, 2; 3, 4]
+? print1(x+y);print1(" equals ");print(x+y);
+2*x equals 2*x
+? prod(1,k=1,10,1+1/k!)
+3335784368058308553334783/905932868585678438400000
+? prod(1.,k=1,10,1+1/k!)
+3.6821540356142043935732308433185262946
+? pi^2/6*prodeuler(p=2,10000,1-p^-2)
+1.0000098157493066238697591433298145222
+? prodinf(n=0,(1+2^-n)/(1+2^(-n+1)))
+0.33333333333333333333333333333333333329
+? prodinf1(n=0,-2^-n/(1+2^(-n+1)))
+0.33333333333333333333333333333333333329
+? psi(1)
+-0.57721566490153286060651209008240243104
+? quaddisc(-252)
+-7
+? quadgen(-11)
+w
+? quadpoly(-11)
+x^2 - x + 3
+? rank(matrix(5,5,x,y,x+y))
+2
+? rayclassno(bnf,[[5,3;0,1],[1,0]])
+12
+? rayclassnolist(bnf,lu)
+[[3], [], [3, 3], [3], [6, 6], [], [], [], [3, 3, 3], [], [3, 3], [3, 3], []
+, [], [12, 6, 6, 12], [3], [3, 3], [], [9, 9], [6, 6], [], [], [], [], [6, 1
+2, 6], [], [3, 3, 3, 3], [], [], [], [], [], [3, 6, 6, 3], [], [], [9, 3, 9]
+, [6, 6], [], [], [], [], [], [3, 3], [3, 3], [12, 12, 6, 6, 12, 12], [], []
+, [6, 6], [9], [], [3, 3, 3, 3], [], [3, 3], [], [6, 12, 12, 6]]
+? move(0,50,50);rbox(0,50,50)
+? print1("give a value for s? ");s=read();print(1/s)
+give a value for s? 37.
+0.027027027027027027027027027027027027027
+? real(5-7*i)
+5
+? recip(3*x^7-5*x^3+6*x-9)
+-9*x^7 + 6*x^6 - 5*x^4 + 3
+? redimag(qfi(3,10,12))
+qfi(3, -2, 4)
+? redreal(qfr(3,10,-20,1.5))
+qfr(3, 16, -7, 1.5000000000000000000000000000000000000)
+? redrealnod(qfr(3,10,-20,1.5),18)
+qfr(3, 16, -7, 1.5000000000000000000000000000000000000)
+? reduceddisc(x^3+4*x+12)
+[1036, 4, 1]
+? regula(17)
+2.0947125472611012942448228460655286535
+? kill(y);print(x+y);
+x + y
+? resultant(x^3-1,x^3+1)
+8
+? resultant2(x^3-1.,x^3+1.)
+8.0000000000000000000000000000000000000
+? bezoutres(x^2+1,x^2-1)
+[2, -2, 4]
+? reverse(tan(x))
+x - 1/3*x^3 + 1/5*x^5 - 1/7*x^7 + 1/9*x^9 - 1/11*x^11 + 1/13*x^13 - 1/15*x^1
+5 + O(x^17)
+? rhoreal(qfr(3,10,-20,1.5))
+qfr(-20, -10, 3, 2.1074451073987839947135880252731470616)
+? rhorealnod(qfr(3,10,-20,1.5),18)
+qfr(-20, -10, 3, 1.5000000000000000000000000000000000000)
+? rline(0,200,150)
+? cursor(0)
+? rmove(0,5,5);cursor(0)
+? rndtoi(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+x^17 - 1
+? qpol=y^3-y-1;setrand(1);bnf2=buchinit(qpol);nf2=bnf2[7];
+? un=mod(1,qpol);w=mod(y,qpol);p=un*(x^5-5*x+w)
+mod(1, y^3 - y - 1)*x^5 + mod(-5, y^3 - y - 1)*x + mod(y, y^3 - y - 1)
+? aa=rnfpseudobasis(nf2,p)
+[[1, 0, 0, -2, [3, 1, 0]~; 0, 1, 0, 2, [0, -1, 0]~; 0, 0, 1, 1, [-5, -2, 0]~
+; 0, 0, 0, 1, -2; 0, 0, 0, 0, 1], [1, 1, 1, [1, 0, 2/5; 0, 1, 3/5; 0, 0, 1/5
+], [1, 0, 22/25; 0, 1, 8/25; 0, 0, 1/25]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfbasis(bnf2,aa)
+
+[1 0 0  [-26/25, 11/25, -8/25]~         [0, 4, -7]~]
+
+[0 1 0   [53/25, -8/25, -1/25]~ [6/5, -41/5, 53/5]~]
+
+[0 0 1 [-14/25, -21/25, 13/25]~  [-16/5, 1/5, 7/5]~]
+
+[0 0 0     [7/25, -2/25, 6/25]~  [2/5, -2/5, 11/5]~]
+
+[0 0 0     [9/25, 1/25, -3/25]~   [2/5, -7/5, 6/5]~]
+
+? rnfdiscf(nf2,p)
+[[416134375, 202396875, 60056800; 0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfequation(nf2,p)
+x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1
+? rnfequation2(nf2,p)
+[x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1, mod(-x^5 + 5*x, x^15 - 1
+5*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1), 0]
+? rnfhermitebasis(bnf2,aa)
+
+[1 0 0 [-6/5, -4/5, 2/5]~   [3/25, -8/25, 24/25]~]
+
+[0 1 0  [6/5, 4/5, -2/5]~   [-9/25, -1/25, 3/25]~]
+
+[0 0 1  [3/5, 2/5, -1/5]~ [-8/25, 13/25, -39/25]~]
+
+[0 0 0  [3/5, 2/5, -1/5]~   [4/25, 6/25, -18/25]~]
+
+[0 0 0                  0   [-2/25, -3/25, 9/25]~]
+
+? rnfisfree(bnf2,aa)
+1
+? rnfsteinitz(nf2,aa)
+[[1, 0, 0, [-26/25, 11/25, -8/25]~, [29/125, -2/25, 8/125]~; 0, 1, 0, [53/25
+, -8/25, -1/25]~, [-53/125, 7/125, 1/125]~; 0, 0, 1, [-14/25, -21/25, 13/25]
+~, [9/125, 19/125, -13/125]~; 0, 0, 0, [7/25, -2/25, 6/25]~, [-9/125, 2/125,
+ -6/125]~; 0, 0, 0, [9/25, 1/25, -3/25]~, [-8/125, -1/125, 3/125]~], [1, 1, 
+1, 1, [125, 0, 22; 0, 125, 108; 0, 0, 1]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rootmod(x^16-1,41)
+[mod(1, 41), mod(3, 41), mod(9, 41), mod(14, 41), mod(27, 41), mod(32, 41), 
+mod(38, 41), mod(40, 41)]~
+? rootpadic(x^4+1,41,6)
+[3 + 22*41 + 27*41^2 + 15*41^3 + 27*41^4 + 33*41^5 + O(41^6), 14 + 20*41 + 2
+5*41^2 + 24*41^3 + 4*41^4 + 18*41^5 + O(41^6), 27 + 20*41 + 15*41^2 + 16*41^
+3 + 36*41^4 + 22*41^5 + O(41^6), 38 + 18*41 + 13*41^2 + 25*41^3 + 13*41^4 + 
+7*41^5 + O(41^6)]~
+? roots(x^5-5*x^2-5*x-5)
+[2.0509134529831982130058170163696514536 + 0.E-38*I, -0.67063790319207539268
+663382582902335603 - 0.84813118358634026680538906224199030918*I, -0.67063790
+319207539268663382582902335603 + 0.84813118358634026680538906224199030918*I,
+ -0.35481882329952371381627468235580237078 - 1.39980287391035466982975228340
+62081965*I, -0.35481882329952371381627468235580237078 + 1.399802873910354669
+8297522834062081965*I]~
+? rootsold(x^4-1000000000000000000000)
+  ***   at top-level: rootsold(x^4-1000000
+  ***                 ^--------------------
+  *** rootsold: this function no longer exists.
+? round(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+x^17 - 1
+? rounderror(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+-35
+? rpoint(0,20,20)
+? initrect(3,600,600);scale(3,-7,7,-2,2);cursor(3)
+? q*series(anell(acurve,100),q)
+q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 - 5*q^11 - 
+6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 - 4*q^20 + 3*q^21 + 10*
+q^22 + 2*q^23 - q^25 + 4*q^26 - 9*q^27 - 2*q^28 + 6*q^29 - 12*q^30 - 4*q^31 
++ 8*q^32 + 15*q^33 + 2*q^35 + 12*q^36 - q^37 + 6*q^39 - 9*q^41 - 6*q^42 + 2*
+q^43 - 10*q^44 - 12*q^45 - 4*q^46 - 9*q^47 + 12*q^48 - 6*q^49 + 2*q^50 - 4*q
+^52 + q^53 + 18*q^54 + 10*q^55 - 12*q^58 + 8*q^59 + 12*q^60 - 8*q^61 + 8*q^6
+2 - 6*q^63 - 8*q^64 + 4*q^65 - 30*q^66 + 8*q^67 - 6*q^69 - 4*q^70 + 9*q^71 -
+ q^73 + 2*q^74 + 3*q^75 + 5*q^77 - 12*q^78 + 4*q^79 + 8*q^80 + 9*q^81 + 18*q
+^82 - 15*q^83 + 6*q^84 - 4*q^86 - 18*q^87 + 4*q^89 + 24*q^90 + 2*q^91 + 4*q^
+92 + 12*q^93 + 18*q^94 - 24*q^96 + 4*q^97 + 12*q^98 - 30*q^99 - 2*q^100 + O(
+q^101)
+? aset=set([5,-2,7,3,5,1])
+[-2, 1, 3, 5, 7]
+? bset=set([7,5,-5,7,2])
+[-5, 2, 5, 7]
+? setintersect(aset,bset)
+[5, 7]
+? setminus(aset,bset)
+[-2, 1, 3]
+? setprecision(28)
+38
+? setrand(10)
+? setsearch(aset,3)
+3
+? setsearch(bset,3)
+0
+? setserieslength(12)
+16
+? setunion(aset,bset)
+[-5, -2, 1, 2, 3, 5, 7]
+? shift(1,50)
+1125899906842624
+? shift([3,4,-11,-12],-2)
+[0, 1, -2, -3]
+? shiftmul([3,4,-11,-12],-2)
+[3/4, 1, -11/4, -3]
+? sigma(100)
+217
+? sigmak(2,100)
+13671
+? sigmak(-3,100)
+1149823/1000000
+? sign(-1)
+-1
+? sign(0)
+0
+? sign(0.)
+0
+? signat(hilbert(5)-0.11*idmat(5))
+[2, 3]
+? signunit(bnf)
+
+[-1]
+
+[ 1]
+
+? simplefactmod(x^11+1,7)
+
+[ 1 1]
+
+[10 1]
+
+? simplify(((x+i+1)^2-x^2-2*x*(i+1))^2)
+-4
+? sin(pi/6)
+0.5000000000000000000000000000
+? sinh(1)
+1.175201193643801456882381851
+? size([1.3*10^5,2*i*pi*exp(4*pi)])
+7
+? smallbasis(x^3+4*x+12)
+[1, x, 1/2*x^2]
+? smalldiscf(x^3+4*x+12)
+-1036
+? smallfact(100!+1)
+
+[101 1]
+
+[14303 1]
+
+[149239 1]
+
+[432885273849892962613071800918658949059679308685024481795740765527568493010
+727023757461397498800981521440877813288657839195622497225621499427628453 1]
+
+? smallinitell([0,0,0,-17,0])
+[0, 0, 0, -17, 0, 0, -34, 0, -289, 816, 0, 314432, 1728, Vecsmall([1]), [Vec
+small([96, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? smallpolred(x^4+576)
+[x - 1, x^2 - 3*x + 3, x^2 - 2*x + 2, x^2 - x + 1, x^2 + 1, x^4 - x^2 + 1]
+? smallpolred2(x^4+576)
+
+[                           1         x - 1]
+
+[    -1/192*x^3 - 1/8*x + 3/2 x^2 - 3*x + 3]
+
+[                1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[               -1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[    -1/192*x^3 - 1/8*x + 1/2   x^2 - x + 1]
+
+[     1/192*x^3 + 1/8*x + 1/2   x^2 - x + 1]
+
+[                    1/24*x^2       x^2 + 1]
+
+[1/192*x^3 + 1/48*x^2 - 1/8*x x^4 - x^2 + 1]
+
+? smith(matrix(5,5,j,k,random()))
+[1672445828511544211967627586516667006907439878, 2, 1, 1, 1]
+? smith(1/hilbert(6))
+[27720, 2520, 2520, 840, 210, 6]
+? smithpol(x*idmat(5)-matrix(5,5,j,k,1))
+[x^2 - 5*x, x, x, x, 1]
+? solve(x=1,4,sin(x))
+3.141592653589793238462643383
+? sort(vector(17,x,5*x%17))
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+? sqr(1+o(2))
+1 + O(2^3)
+? sqred(hilbert(5))
+
+[1  1/2   1/3    1/4     1/5]
+
+[0 1/12     1   9/10     4/5]
+
+[0    0 1/180    3/2    12/7]
+
+[0    0     0 1/2800       2]
+
+[0    0     0      0 1/44100]
+
+? sqrt(13+o(127^12))
+34 + 125*127 + 83*127^2 + 107*127^3 + 53*127^4 + 42*127^5 + 22*127^6 + 98*12
+7^7 + 127^8 + 23*127^9 + 122*127^10 + 79*127^11 + O(127^12)
+? srgcd(x^10-1,x^15-1)
+x^5 - 1
+? move(0,100,100);string(0,pi)
+? move(0,200,200);string(0,"(0,0)")
+? postdraw([0,10,10])
+? apol=0.3+legendre(10)
+46189/256*x^10 - 109395/256*x^8 + 45045/128*x^6 - 15015/128*x^4 + 3465/256*x
+^2 + 0.05390625000000000000000000000
+? sturm(apol)
+4
+? sturmpart(apol,0.91,1)
+1
+? subcyclo(31,5)
+x^5 + x^4 - 12*x^3 - 21*x^2 + x + 5
+? subell(initell([0,0,0,-17,0]),[-1,4],[-4,2])
+[9, -24]
+? subst(sin(x),x,y)
+y - 1/6*y^3 + 1/120*y^5 - 1/5040*y^7 + 1/362880*y^9 - 1/39916800*y^11 + O(y^
+13)
+? subst(sin(x),x,x+x^2)
+x + x^2 - 1/6*x^3 - 1/2*x^4 - 59/120*x^5 - 1/8*x^6 + 419/5040*x^7 + 59/720*x
+^8 + 13609/362880*x^9 + 19/13440*x^10 - 273241/39916800*x^11 - 14281/3628800
+*x^12 + O(x^13)
+? sum(0,k=1,10,2^-k)
+1023/1024
+? sum(0.,k=1,10,2^-k)
+0.9990234375000000000000000000
+? sylvestermatrix(a2*x^2+a1*x+a0,b1*x+b0)
+
+[a2 b1  0]
+
+[a1 b0 b1]
+
+[a0  0 b0]
+
+? \precision=38
+   realprecision = 38 significant digits
+? 4*sumalt(n=0,(-1)^n/(2*n+1))
+3.1415926535897932384626433832795028842
+? 4*sumalt2(n=0,(-1)^n/(2*n+1))
+3.1415926535897932384626433832795028842
+? suminf(n=1,2.^-n)
+0.99999999999999999999999999999999999999
+? 6/pi^2*sumpos(n=1,n^-2)
+1.0000000000000000000000000000000000000
+? supplement([1,3;2,4;3,6])
+
+[1 3 0]
+
+[2 4 0]
+
+[3 6 1]
+
+? sqr(tan(pi/3))
+3.0000000000000000000000000000000000000
+? tanh(1)
+0.76159415595576488811945828260479359041
+? taniyama(bcurve)
+[x^-2 - x^2 + 3*x^6 - 2*x^10 + O(x^11), -x^-3 + 3*x - 3*x^5 + 8*x^9 + O(x^10
+)]
+? taylor(y/(x-y),y)
+(O(y^12)*x^11 + y*x^10 + y^2*x^9 + y^3*x^8 + y^4*x^7 + y^5*x^6 + y^6*x^5 + y
+^7*x^4 + y^8*x^3 + y^9*x^2 + y^10*x + y^11)/x^11
+? tchebi(10)
+512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1
+? teich(7+o(127^12))
+7 + 57*127 + 58*127^2 + 83*127^3 + 52*127^4 + 109*127^5 + 74*127^6 + 16*127^
+7 + 60*127^8 + 47*127^9 + 65*127^10 + 5*127^11 + O(127^12)
+? texprint((x+y)^3/(x-y)^2)
+\frac{x^3
+ + 3 y x^2
+ + 3 y^2 x
+ + y^3}{x^2
+ - 2 y x
+ + y^2}
+? theta(0.5,3)
+0.080806418251894691299871683210466298523
+? thetanullk(0.5,7)
+-804.63037320243369422783730584965684023
+? torsell(tcurve)
+[12, [6, 2], [[1, 2], [3, -2]]]
+? trace(1+i)
+2
+? trace(mod(x+5,x^3+x+1))
+15
+? trans(vector(2,x,x))
+[1, 2]~
+? %*%~
+
+[1 2]
+
+[2 4]
+
+? trunc(-2.7)
+-2
+? trunc(sin(x^2))
+1/120*x^10 - 1/6*x^6 + x^2
+? tschirnhaus(x^5-x-1)
+x^5 + 10*x^4 + 38*x^3 + 47*x^2 + 27*x + 281
+? type(mod(x,x^2+1))
+9
+? unit(17)
+3 + 2*w
+? n=33;until(n==1,print1(n," ");if(n%2,n=3*n+1,n=n/2));print(1)
+33 100 50 25 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
+? valuation(6^10000-1,5)
+5
+? vec(sin(x))
+[1, 0, -1/6, 0, 1/120, 0, -1/5040, 0, 1/362880, 0, -1/39916800, 0]
+? vecmax([-3,7,-2,11])
+11
+? vecmin([-3,7,-2,11])
+-3
+? vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],2)
+[[2, 5, 8], [3, 6, -6], [1, 8, 5], [4, 8, 6]]
+? vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],[2,1])
+[[2, 5, 8], [3, 6, -6], [1, 8, 5], [4, 8, 6]]
+? weipell(acurve)
+x^-2 + 1/5*x^2 - 1/28*x^4 + 1/75*x^6 - 3/1540*x^8 + O(x^10)
+? wf(i)
+1.1892071150027210667174999705604759153
+? wf2(i)
+1.0905077326652576592070106557607079790
+? m=5;while(m<20,print1(m," ");m=m+1);print()
+5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
+? zell(acurve,apoint)
+0.72491221490962306778878739838332384646
+? zeta(3)
+1.2020569031595942853997381615114499908
+? zeta(0.5+14.1347251*i)
+5.2043097453468479398562848599360610966 E-9 - 3.2690639869786982176409251733
+763732423 E-8*I
+? zetak(nfz,-3)
+0.091666666666666666666666666666666666667
+? zetak(nfz,1.5+3*i)
+0.88324345992059326405525724366416928892 - 0.2067536250233895222724230899142
+7938848*I
+? zidealstar(nf2,54)
+[132678, [1638, 9, 9], [[11, -3, 11]~, [1, -24, 0]~, [-23, -18, -12]~]]
+? bid=zidealstarinit(nf2,54)
+[[[54, 0, 0; 0, 54, 0; 0, 0, 54], [0]], [132678, [1638, 9, 9]], [[2, [2, 0, 
+0]~, 1, 3, 1], 1; [3, [3, 0, 0]~, 1, 3, 1], 3], [[[[7], [[1, 1, 0]~], [[1, -
+27, 0]~], [Vecsmall([])], 1]], [[[26], [[4, 2, 1]~], [[-23, 2, -26]~], [Vecs
+mall([])], 1], [[3, 3, 3], [4, [1, 3, 0]~, [1, 0, 3]~], [-77, [1, -24, 0]~, 
+[1, 0, -24]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/3, 0, 0; 0, 1/
+3, 0; 0, 0, 1/3]], [[3, 3, 3], [10, [1, 9, 0]~, [1, 0, 9]~], [-233, [1, -18,
+ 0]~, [1, 0, -18]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/9, 0, 0;
+ 0, 1/9, 0; 0, 0, 1/9]]], [[], [], []]], [468, -77, 0, 728, -1456, 0, 546, -
+1092; 0, 0, 1, 0, -1, -6, 0, -3; 0, 1, 0, -1, 1, 0, -3, 3]]
+? zideallog(nf2,w,bid)
+[752, 1, 1]~
+? znstar(3120)
+[768, [12, 4, 4, 2, 2], [mod(2641, 3120), mod(2341, 3120), mod(2497, 3120), 
+mod(391, 3120), mod(2081, 3120)]]
+? sin'(4)
+-0.65364362086361191463916818309775038142
+? m=3;if(m==1,2,m==3,4,5)
+4
+? a=[1,2,3];
+? deplin(a)
+[-2, 1, 0]~
+? deplin(mod(a,2))
+[0, 1, 0]~
+? deplin(mod(a,7))
+[-2, 1, 0]~
+? deplin(mod(a,2^64+13))
+[-2, 1, 0]~
+? p=2^64+13;t=char(mod(4*p*y+1,y^4+1),x);discf2(t)
+256
+? getstack()
+104
+? getheap()
+[721, 108902]
+? print("Total time spent: ",gettime);
+Total time spent: 228
diff --git a/src/test/32/concat b/src/test/32/concat
new file mode 100644
index 0000000..fb55b71
--- /dev/null
+++ b/src/test/32/concat
@@ -0,0 +1,104 @@
+
+[1 2 5]
+
+[3 4 6]
+
+
+[1 2]
+
+[3 4]
+
+[7 8]
+
+
+[1 2 5]
+
+[3 4 6]
+
+[7 8 9]
+
+
+[1 2 4]
+
+[0 3 5]
+
+[0 0 6]
+
+
+[1 0 0]
+
+[2 3 0]
+
+[4 5 6]
+
+
+[5 0 7 8]
+
+[6 0 0 0]
+
+[1 2 9 0]
+
+[3 4 0 9]
+
+[;]
+"x2"
+
+[1]
+
+[;]
+  ***   at top-level: concat([;],[1])
+  ***                 ^---------------
+  *** concat: inconsistent concatenation t_MAT (0x0) , t_VEC (1 elts).
+
+[1]
+
+[;]
+  ***   at top-level: concat([1],[;])
+  ***                 ^---------------
+  *** concat: inconsistent concatenation t_VEC (1 elts) , t_MAT (0x0).
+[1, 2]
+[1, 2]
+
+[1 2]
+
+  ***   at top-level: concat(1,A)
+  ***                 ^-----------
+  *** concat: inconsistent concatenation t_INT , t_MAT (2x2).
+
+[2 1]
+
+  ***   at top-level: concat(A,1)
+  ***                 ^-----------
+  *** concat: inconsistent concatenation t_MAT (2x2) , t_INT.
+[1, 2]
+[1, 2, 1]
+  ***   at top-level: concat([1,2],[1,2]~)
+  ***                 ^--------------------
+  *** concat: inconsistent concatenation t_VEC (2 elts) , t_COL (2 elts).
+
+[2 3]
+
+[1 0]
+
+[0 1]
+
+[1, 2]
+[3, 1, 2]
+[2, 3]~
+[2, 3, 1]~
+  ***   at top-level: concat([2,3]~,[1,2])
+  ***                 ^--------------------
+  *** concat: inconsistent concatenation t_COL (2 elts) , t_VEC (2 elts).
+[;]
+
+[1 2]
+
+[3 4]
+
+[1 2]
+
+[1, 2]
+
+[1 2]
+
+Total time spent: 0
diff --git a/src/test/32/content b/src/test/32/content
new file mode 100644
index 0000000..cea6df2
--- /dev/null
+++ b/src/test/32/content
@@ -0,0 +1,46 @@
+1
+1
+1
+3
+1
+6
+1
+1
+1
+1
+x
+error("incorrect type in denom (t_QFI).")
+error("incorrect type in denom (t_QFR).")
+4
+2
+2
+1.0000000000000000000000000000000000000
+Mod(1, 3)
+2
+x
+3 + 2*I
+O(2^-3)
+w
+Mod(1/2*x, x^2)
+2*x
+2
+error("incorrect type in numer (t_QFI).")
+error("incorrect type in numer (t_QFR).")
+[8, 3]
+[2, 4; 3, 8]
+2
+1
+Mod(1, 3)
+2/3
+x
+1/2 + 1/3*I
+1/8
+w
+1/2
+2
+2
+1
+1
+1/4
+1/2
+Total time spent: 0
diff --git a/src/test/32/contfrac b/src/test/32/contfrac
new file mode 100644
index 0000000..ad42782
--- /dev/null
+++ b/src/test/32/contfrac
@@ -0,0 +1,69 @@
+  ***   at top-level: contfrac(1,[],-1)
+  ***                 ^-----------------
+  *** contfrac: domain error in contfrac: nmax < 0
+  ***   at top-level: contfracpnqn(Vecsmal
+  ***                 ^--------------------
+  *** contfracpnqn: incorrect type in pnqn (t_VECSMALL).
+
+[1 0]
+
+[0 1]
+
+[;]
+[;]
+
+[1 2]
+
+[0 1]
+
+
+[2]
+
+[1]
+
+
+[2]
+
+[1]
+
+
+[10 3]
+
+[ 7 2]
+
+
+[1]
+
+[1]
+
+
+[1 3]
+
+[1 2]
+
+
+[1 3 10]
+
+[1 2  7]
+
+
+[144 22]
+
+[ 33  5]
+
+
+[4]
+
+[1]
+
+
+[4 22]
+
+[1  5]
+
+
+[4 22 144]
+
+[1  5  33]
+
+Total time spent: 0
diff --git a/src/test/32/cxtrigo b/src/test/32/cxtrigo
new file mode 100644
index 0000000..49a96b7
--- /dev/null
+++ b/src/test/32/cxtrigo
@@ -0,0 +1,18 @@
+atan: 0.0 + I*-1.0 is a pole ?
+atan: 0.0 + I*1.0 is a pole ?
+atanh: -1.0 + I*0.0 is a pole ?
+atanh: 1.0 + I*0.0 is a pole ?
+0.E-1155
+0.E-1155
+0.E-1155
+0.E-1155
+0.E-1155
+0.E-1155
+1.1702607277107686407657936407619050318
+-1.3130352854993313036361612469308478329*I
+0.27390375020968549781950404333381912152 - 1.0057146343488479442511142161269
+763393*I
+1.5574077246549022305069748074583601731*I
+0.27390375020968549781950404333381912152 - 1.0057146343488479442511142161269
+763393*I
+Total time spent: 244
diff --git a/src/test/32/cyclo b/src/test/32/cyclo
new file mode 100644
index 0000000..990bcab
--- /dev/null
+++ b/src/test/32/cyclo
@@ -0,0 +1,58 @@
+  ***   Warning: new stack size = 8000000 (7.629 Mbytes).
+  ***   at top-level: poliscyclo(1)
+  ***                 ^-------------
+  *** poliscyclo: incorrect type in poliscyclo (t_INT).
+0
+0
+100000
+12345
+[x - 1, x + 1]
+[x - 1, x^2 + x + 1]
+[x - 1, x^2 + 1]
+[x - 1, x^4 + x^3 + x^2 + x + 1]
+[x - 1, x^2 - x + 1]
+[x - 1, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1]
+[x - 1, x^4 + 1]
+[x - 1, x^6 + x^3 + 1]
+[x - 1, x^4 - x^3 + x^2 - x + 1]
+[x + 1, x^2 + x + 1]
+[x + 1, x^2 + 1]
+[x + 1, x^4 + x^3 + x^2 + x + 1]
+[x + 1, x^2 - x + 1]
+[x + 1, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1]
+[x + 1, x^4 + 1]
+[x + 1, x^6 + x^3 + 1]
+[x + 1, x^4 - x^3 + x^2 - x + 1]
+[x^2 + 1, x^2 + x + 1]
+[x^6 + 2*x^5 + 3*x^4 + 3*x^3 + 3*x^2 + 2*x + 1]
+[x^4 + x^2 + 1]
+[x^8 + 2*x^7 + 3*x^6 + 3*x^5 + 3*x^4 + 3*x^3 + 3*x^2 + 2*x + 1]
+[x^4 + 1, x^2 + x + 1]
+[x^8 + x^7 + x^6 + x^5 + x^4 + x^3 + x^2 + x + 1]
+[x^2 + x + 1, x^4 - x^3 + x^2 - x + 1]
+[x^2 + 1, x^4 + x^3 + x^2 + x + 1]
+[x^2 + 1, x^2 - x + 1]
+[x^2 + 1, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1]
+[x^2 + 1, x^4 + 1]
+[x^2 + 1, x^6 + x^3 + 1]
+[x^2 + 1, x^4 - x^3 + x^2 - x + 1]
+[x^4 + x^3 + x^2 + x + 1, x^2 - x + 1]
+[x^10 + 2*x^9 + 3*x^8 + 4*x^7 + 5*x^6 + 5*x^5 + 5*x^4 + 4*x^3 + 3*x^2 + 2*x 
++ 1]
+[x^4 + 1, x^4 + x^3 + x^2 + x + 1]
+[x^10 + x^9 + x^8 + 2*x^7 + 2*x^6 + x^5 + 2*x^4 + 2*x^3 + x^2 + x + 1]
+[x^8 + x^6 + x^4 + x^2 + 1]
+[x^6 + x^5 + x^4 + x^3 + x^2 + x + 1, x^2 - x + 1]
+[x^4 + 1, x^2 - x + 1]
+[x^6 + x^3 + 1, x^2 - x + 1]
+[x^6 - 2*x^5 + 3*x^4 - 3*x^3 + 3*x^2 - 2*x + 1]
+[x^4 + 1, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1]
+[x^12 + x^11 + x^10 + 2*x^9 + 2*x^8 + 2*x^7 + 3*x^6 + 2*x^5 + 2*x^4 + 2*x^3 
++ x^2 + x + 1]
+[x^6 + x^5 + x^4 + x^3 + x^2 + x + 1, x^4 - x^3 + x^2 - x + 1]
+[x^4 + 1, x^6 + x^3 + 1]
+[x^4 + 1, x^4 - x^3 + x^2 - x + 1]
+[x^6 + x^3 + 1, x^4 - x^3 + x^2 - x + 1]
+1
+1
+Total time spent: 532
diff --git a/src/test/32/debugger b/src/test/32/debugger
new file mode 100644
index 0000000..e04570d
--- /dev/null
+++ b/src/test/32/debugger
@@ -0,0 +1,94 @@
+   echo = 1 (on)
+? default(breakloop,1)
+? my(bound=100,step=20,halt=41);check(B)=my(bi=[B^2]);for(i=1,bound,my(p=i+step,N=p^2);if(i==halt,error("check:",N)))
+(B)->my(bound=100,step=20,halt=41);my(bi=[B^2]);for(i=1,bound,my(p=i+step,N=
+p^2);if(i==halt,error("check:",N)))
+? check(1000);
+  ***   at top-level: check(1000)
+  ***                 ^-----------
+  ***   in function check: ...i+step,N=p^2);if(i==halt,error("check:",N)))
+  ***                                                  ^-------------------
+  ***   user error: check:3721
+  ***   Break loop: type 'break' to go back to GP prompt
+break> [bound,step,halt,i,p,N,bi,B]
+[100, 20, 41, 41, 61, 3721, [1000000], 1000]
+break> break
+
+? [bound,step,halt,i,p,N,bi,B]
+[bound, step, halt, i, p, N, bi, B]
+? my(p=120);for(i=1,100,1/0)
+  ***   at top-level: my(p=120);for(i=1,100,1/0)
+  ***                                        ^---
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop: type 'break' to go back to GP prompt
+break> [p,i]
+[120, 1]
+break> dbg_err()
+error("impossible inverse in gdiv: 0.")
+break> break
+
+? fun(N)=check(N^2+1);
+? fun(20);
+  ***   at top-level: fun(20)
+  ***                 ^-------
+  ***   in function fun: check(N^2+1)
+  ***                    ^------------
+  ***   in function check: ...i+step,N=p^2);if(i==halt,error("check:",N)))
+  ***                                                  ^-------------------
+  ***   user error: check:3721
+  ***   Break loop: type 'break' to go back to GP prompt
+break> N
+3721
+break> dbg_up(4)
+  ***   at top-level: fun(20)
+  ***                 ^-------
+  ***   in function fun: check(N^2+1)
+  ***                    ^------------
+break> N
+20
+break> break
+
+? f(N,x)=my(z=x^2+1);breakpoint();gcd(N,z^2+1-z);
+? f(221,3)
+  ***   at top-level: f(221,3)
+  ***                 ^--------
+  ***   in function f: my(z=x^2+1);breakpoint();gcd(N,z
+  ***                              ^--------------------
+
+  ***   Break loop: <Return> to continue; 'break' to go back to GP prompt
+break> z
+10
+13
+? z
+z
+? iferrname("e_VAR",vector(10000,i,1/(i-100)),E,Vec(E))
+  ***   at top-level: ...("e_VAR",vector(10000,i,1/(i-100)),E,Vec(E))
+  ***                                             ^-------------------
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop: type 'break' to go back to GP prompt
+break> i
+100
+break> break
+
+? f()=1/0
+()->1/0
+? f();
+  ***   at top-level: f()
+  ***                 ^---
+  ***   in function f: 1/0
+  ***                   ^--
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop: type 'break' to go back to GP prompt
+break> allocatemem(10^7)
+  ***   Warning: new stack size = 10000000 (9.537 Mbytes).
+? for(i=1,10,if(i==2,1/0));
+  ***   at top-level: for(i=1,10,if(i==2,1/0))
+  ***                                     ^----
+  *** _/_: impossible inverse in gdiv: 0.
+  ***   Break loop: type 'break' to go back to GP prompt
+break> i
+2
+break> break
+
+? print("Total time spent: ",gettime);
+Total time spent: 0
diff --git a/src/test/32/deriv b/src/test/32/deriv
new file mode 100644
index 0000000..41e625c
--- /dev/null
+++ b/src/test/32/deriv
@@ -0,0 +1,53 @@
+-0.65364362086361191463916818309775038142
+-0.65364362086361191463916818309775038142
+1 + O(t^2)
+0
+Mod(x, x^2 + 1)
+Mod(x, x^2 + 1)
+error("incorrect priority in intformal: variable x = x")
+Mod(1/2*y^2*x + y, x^2 + 1)
+Mod(0, 3)
+Mod(0, 3)
+Mod(0, 3)
+Mod(1, 3)*x
+Mod(1, 3)*y
+0
+0
+0
+t*x
+t*y
+y + O(x^2)
+y + O(x^2)
+x + O(x^3)
+x + 1/2*y*x^2 + O(x^4)
+y + 1/2*y^2*x + O(x^3)
+y/(x^2 + 2*y*x + y^2)
+y/(x^2 + 2*y*x + y^2)
+-x/(x^2 + 2*y*x + y^2)
+error("domain error in intformal: residue(series, pole) != 0")
+error("domain error in intformal: residue(series, pole) != 0")
+(-x^4 - 6*x^2 - 12*x + 3)/(3*x^6 + 9*x^4 + 9*x^2 + 3)
+(-x^4 - 6*x^2 - 12*x + 3)/(3*x^6 + 9*x^4 + 9*x^2 + 3)
+0
+error("domain error in intformal: residue(series, pole) != 0")
+(y*x^3 + 3*y*x + 3*y)/(3*x^4 + 6*x^2 + 3)
+[y]
+[y]
+[x]
+[1/2*y*x^2]
+[1/2*y^2*x]
+Mat(y)
+Mat(y)
+Mat(x)
+Mat(1/2*y*x^2)
+Mat(1/2*y^2*x)
+-Y*A + Y
+0
+0
+x
+x
+O(x)
+O(x)
+x
+x
+Total time spent: 0
diff --git a/src/test/32/det b/src/test/32/det
new file mode 100644
index 0000000..40b4cb9
--- /dev/null
+++ b/src/test/32/det
@@ -0,0 +1,9 @@
+2645450238786014260762195151127593140777050579745670159192807728014078750989
+2064506631325
+2645450238786014260762195151127593140777050579745670159192807728014078750989
+2064506631325
+2645450238786014260762195151127593140777050579745670159192807728014078750989
+2064506631325
+2645450238786014260762195151127593140777050579745670159192807728014078750989
+2064506631325
+Total time spent: 8
diff --git a/src/test/32/diffop b/src/test/32/diffop
new file mode 100644
index 0000000..85c200a
--- /dev/null
+++ b/src/test/32/diffop
@@ -0,0 +1,11 @@
+(X^11 + 55*X^9 + 990*X^7 + 6930*X^5 + 17325*X^3 + 10395*X)*E
+Mod(((512*C^8 - 130560*C^6 + 1693440*C^4 - 4838400*C^2 + 3628800)/C^11)*S, S
+^2 + (C^2 - 1))
+240*q + 4320*q^2 + 20160*q^3 + 70080*q^4 + 151200*q^5 + 362880*q^6 + 577920*
+q^7 + 1123200*q^8 + 1635120*q^9 + 2721600*q^10 + 3516480*q^11 + 5886720*q^12
+ + 6857760*q^13 + 10402560*q^14 + 12700800*q^15 + O(q^16)
+Mod(1, x^2 - y)
+0
+((Mod(2, 3)*llx + Mod(2, 3))*lx^4 + Mod(1, 3)*lx^2 + Mod(2, 3))/(Mod(1, 3)*l
+lx^2*lx^6*x^6)
+Total time spent: 0
diff --git a/src/test/32/digits b/src/test/32/digits
new file mode 100644
index 0000000..79f78fc
--- /dev/null
+++ b/src/test/32/digits
@@ -0,0 +1,32 @@
+[]
+[]
+[7, 8, 8, 8, 6, 0, 9, 0, 5, 2, 2, 1, 0, 1, 1, 8, 0, 5, 4, 1, 1, 7, 2, 8, 5, 
+6, 5, 2, 8, 2, 7, 8, 6, 2, 2, 9, 6, 7, 3, 2, 0, 6, 4, 3, 5, 1, 0, 9, 0, 2, 3
+, 0, 0, 4, 7, 7, 0, 2, 7, 8, 9, 3, 0, 6, 6, 4, 0, 6, 2, 5]
+0
+12
+135
+1938780
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+, 0]
+[[0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 
+0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0
+, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1,
+ 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 
+0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0
+, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0]]
+[[1], [0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 
+1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
+, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
+ 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 
+1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
+, 0, 1]]
+[[], [1]]
+  ***   at top-level: binary(I)
+  ***                 ^---------
+  *** binary: incorrect type in binary (t_COMPLEX).
+Total time spent: 112
diff --git a/src/test/32/dirmul b/src/test/32/dirmul
new file mode 100644
index 0000000..78720ec
--- /dev/null
+++ b/src/test/32/dirmul
@@ -0,0 +1,7 @@
+[0, 0, 0, 1]
+[0, 1]
+[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[1, 4, 6, 12]
+[1, 0, 0, 0]
+[1/2, 0, 0, 0]
+Total time spent: 8
diff --git a/src/test/32/disc b/src/test/32/disc
new file mode 100644
index 0000000..f80649c
--- /dev/null
+++ b/src/test/32/disc
@@ -0,0 +1,2 @@
+Mod(1, 2)
+Total time spent: 4
diff --git a/src/test/32/div b/src/test/32/div
new file mode 100644
index 0000000..30fcb20
--- /dev/null
+++ b/src/test/32/div
@@ -0,0 +1,910 @@
+* [1, 1]
+1
+1
+0
+1
+[1, 0]~
+* [1, 2]
+3/4
+0
+3
+1
+[0, 3]~
+* [1, 3]
+0.96774193548387096774193548387096774194
+0
+3
+0
+[0, 3]~
+* [1, 4]
+6
+6
+0
+6
+[6, 0]~
+* [1, 5]
+3/(x^2 + 1)
+0
+3
+0
+[0, 3]~
+* [1, 6]
+Mod(-3*x, x^2 + 1)
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT % t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+* [1, 7]
+0
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT % t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+* [1, 8]
+-3 + 3*w
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT % t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+* [1, 9]
+2*3 + 3^2 + 3^3 + O(3^4)
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT % t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+* [1, 10]
+Mod(0, 3)
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT % t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+* [1, 11]
+3/18446744073709551617
+0
+3
+0
+[0, 3]~
+* [2, 1]
+4/3
+1
+1
+1
+[1, 1]~
+* [2, 2]
+1
+1
+0
+1
+[1, 0]~
+* [2, 3]
+1.2903225806451612903225806451612903226
+1
+0.90000000000000000000000000000000000000
+1
+[1, 0.90000000000000000000000000000000000000]~
+* [2, 4]
+8
+8
+0
+8
+[8, 0]~
+* [2, 5]
+4/(x^2 + 1)
+0
+4
+0
+[0, 4]~
+* [2, 6]
+Mod(-4*x, x^2 + 1)
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT % t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+* [2, 7]
+2*t^4 + 2*t^3 + t^2
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT % t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+* [2, 8]
+-4 + 4*w
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT % t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+* [2, 9]
+2 + O(3^3)
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT % t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+* [2, 10]
+Mod(2, 3)
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT % t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+* [2, 11]
+4/18446744073709551617
+0
+4
+0
+[0, 4]~
+* [3, 1]
+1.0333333333333333333333333333333333333
+1
+0.099999999999999999999999999999999999995
+1
+[1, 0.099999999999999999999999999999999999995]~
+* [3, 2]
+0.77500000000000000000000000000000000000
+0
+3.1000000000000000000000000000000000000
+0
+[0, 3.1000000000000000000000000000000000000]~
+* [3, 3]
+1.0000000000000000000000000000000000000
+1
+0.E-37
+1
+[1, 0.E-37]~
+* [3, 4]
+6.2000000000000000000000000000000000000
+6
+0.099999999999999999999999999999999999995
+6
+[6, 0.099999999999999999999999999999999999995]~
+* [3, 5]
+3.1000000000000000000000000000000000000/(x^2 + 1)
+0
+3.1000000000000000000000000000000000000
+0
+[0, 3.1000000000000000000000000000000000000]~
+* [3, 6]
+Mod(-3.1000000000000000000000000000000000000*x, x^2 + 1)
+error("forbidden division t_REAL \\ t_POLMOD.")
+error("forbidden division t_REAL % t_POLMOD.")
+error("forbidden division t_REAL \\ t_POLMOD.")
+error("forbidden division t_REAL \\ t_POLMOD.")
+* [3, 7]
+error("forbidden division t_REAL / t_FFELT.")
+error("forbidden division t_REAL \\ t_FFELT.")
+error("forbidden division t_REAL % t_FFELT.")
+error("forbidden division t_REAL \\ t_FFELT.")
+error("forbidden division t_REAL \\ t_FFELT.")
+* [3, 8]
+1.9159053651246740294342191865334781649
+error("forbidden division t_REAL \\ t_QUAD.")
+error("forbidden division t_REAL % t_QUAD.")
+error("forbidden division t_REAL \\ t_QUAD.")
+error("forbidden division t_REAL \\ t_QUAD.")
+* [3, 9]
+error("forbidden division t_REAL / t_PADIC.")
+error("forbidden division t_REAL \\ t_PADIC.")
+error("forbidden division t_REAL % t_PADIC.")
+error("forbidden division t_REAL \\ t_PADIC.")
+error("forbidden division t_REAL \\ t_PADIC.")
+* [3, 10]
+error("forbidden division t_REAL / t_INTMOD.")
+error("forbidden division t_REAL \\ t_INTMOD.")
+error("forbidden division t_REAL % t_INTMOD.")
+error("forbidden division t_REAL \\ t_INTMOD.")
+error("forbidden division t_REAL \\ t_INTMOD.")
+* [3, 11]
+1.6805133673525318726204510291596823708 E-19
+0
+3.1000000000000000000000000000000000000
+0
+[0, 3.1000000000000000000000000000000000000]~
+* [4, 1]
+1/6
+0
+2
+0
+[0, 1/2]~
+* [4, 2]
+1/8
+0
+error("impossible inverse in Fl_inv: Mod(2, 4).")
+0
+[0, 1/2]~
+* [4, 3]
+0.16129032258064516129032258064516129032
+0
+1/2
+0
+[0, 1/2]~
+* [4, 4]
+1
+1
+0
+1
+[1, 0]~
+* [4, 5]
+1/(2*x^2 + 2)
+0
+1/2
+0
+[0, 1/2]~
+* [4, 6]
+Mod(-1/2*x, x^2 + 1)
+error("forbidden division t_FRAC \\ t_POLMOD.")
+error("forbidden division t_FRAC % t_POLMOD.")
+error("forbidden division t_FRAC \\ t_POLMOD.")
+error("forbidden division t_FRAC \\ t_POLMOD.")
+* [4, 7]
+t^4 + t^3 + 2*t^2
+error("forbidden division t_FRAC \\ t_FFELT.")
+error("forbidden division t_FRAC % t_FFELT.")
+error("forbidden division t_FRAC \\ t_FFELT.")
+error("forbidden division t_FRAC \\ t_FFELT.")
+* [4, 8]
+-1/2 + 1/2*w
+error("forbidden division t_FRAC \\ t_QUAD.")
+error("forbidden division t_FRAC % t_QUAD.")
+error("forbidden division t_FRAC \\ t_QUAD.")
+error("forbidden division t_FRAC \\ t_QUAD.")
+* [4, 9]
+1 + 2*3 + O(3^3)
+error("forbidden division t_FRAC \\ t_PADIC.")
+error("forbidden division t_FRAC % t_PADIC.")
+error("forbidden division t_FRAC \\ t_PADIC.")
+error("forbidden division t_FRAC \\ t_PADIC.")
+* [4, 10]
+Mod(1, 3)
+error("forbidden division t_FRAC \\ t_INTMOD.")
+error("forbidden division t_FRAC % t_INTMOD.")
+error("forbidden division t_FRAC \\ t_INTMOD.")
+error("forbidden division t_FRAC \\ t_INTMOD.")
+* [4, 11]
+1/36893488147419103234
+0
+9223372036854775809
+0
+[0, 1/2]~
+* [5, 1]
+1/3*x^2 + 1/3
+1/3*x^2 + 1/3
+0
+1/3*x^2 + 1/3
+[1/3*x^2 + 1/3, 0]~
+* [5, 2]
+1/4*x^2 + 1/4
+1/4*x^2 + 1/4
+0
+1/4*x^2 + 1/4
+[1/4*x^2 + 1/4, 0]~
+* [5, 3]
+0.32258064516129032258064516129032258065*x^2 + 0.322580645161290322580645161
+29032258065
+0.32258064516129032258064516129032258065*x^2 + 0.322580645161290322580645161
+29032258065
+0
+0.32258064516129032258064516129032258065*x^2 + 0.322580645161290322580645161
+29032258065
+[0.32258064516129032258064516129032258065*x^2 + 0.32258064516129032258064516
+129032258065, 0]~
+* [5, 4]
+2*x^2 + 2
+2*x^2 + 2
+0
+2*x^2 + 2
+[2*x^2 + 2, 0]~
+* [5, 5]
+1
+1
+0
+1
+[1, 0]~
+* [5, 6]
+0
+error("forbidden euclidean division t_POL , t_POLMOD.")
+error("forbidden euclidean division t_POL , t_POLMOD.")
+error("forbidden euclidean division t_POL , t_POLMOD.")
+error("forbidden euclidean division t_POL , t_POLMOD.")
+* [5, 7]
+(2*t^4 + 2*t^3 + t^2)*x^2 + (2*t^4 + 2*t^3 + t^2)
+(2*t^4 + 2*t^3 + t^2)*x^2 + (2*t^4 + 2*t^3 + t^2)
+0
+(2*t^4 + 2*t^3 + t^2)*x^2 + (2*t^4 + 2*t^3 + t^2)
+[(2*t^4 + 2*t^3 + t^2)*x^2 + (2*t^4 + 2*t^3 + t^2), 0]~
+* [5, 8]
+(-1 + w)*x^2 + (-1 + w)
+(-1 + w)*x^2 + (-1 + w)
+0
+(-1 + w)*x^2 + (-1 + w)
+[(-1 + w)*x^2 + (-1 + w), 0]~
+* [5, 9]
+(2 + 3 + 3^2 + O(3^3))*x^2 + (2 + 3 + 3^2 + O(3^3))
+(2 + 3 + 3^2 + O(3^3))*x^2 + (2 + 3 + 3^2 + O(3^3))
+0
+(2 + 3 + 3^2 + O(3^3))*x^2 + (2 + 3 + 3^2 + O(3^3))
+[(2 + 3 + 3^2 + O(3^3))*x^2 + (2 + 3 + 3^2 + O(3^3)), 0]~
+* [5, 10]
+Mod(2, 3)*x^2 + Mod(2, 3)
+Mod(2, 3)*x^2 + Mod(2, 3)
+Mod(0, 3)
+Mod(2, 3)*x^2 + Mod(2, 3)
+[Mod(2, 3)*x^2 + Mod(2, 3), Mod(0, 3)]~
+* [5, 11]
+1/18446744073709551617*x^2 + 1/18446744073709551617
+1/18446744073709551617*x^2 + 1/18446744073709551617
+0
+1/18446744073709551617*x^2 + 1/18446744073709551617
+[1/18446744073709551617*x^2 + 1/18446744073709551617, 0]~
+* [6, 1]
+Mod(1/3*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_INT.")
+0
+error("forbidden division t_POLMOD \\ t_INT.")
+error("forbidden division t_POLMOD \\ t_INT.")
+* [6, 2]
+Mod(1/4*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_INT.")
+0
+error("forbidden division t_POLMOD \\ t_INT.")
+error("forbidden division t_POLMOD \\ t_INT.")
+* [6, 3]
+Mod(0.32258064516129032258064516129032258065*x, x^2 + 1)
+error("incorrect type in gfloor (t_POLMOD).")
+error("forbidden division t_POLMOD % t_REAL.")
+error("incorrect type in gfloor (t_POLMOD).")
+error("incorrect type in gfloor (t_POLMOD).")
+* [6, 4]
+Mod(2*x, x^2 + 1)
+error("incorrect type in gfloor (t_POLMOD).")
+error("forbidden division t_POLMOD % t_FRAC.")
+error("incorrect type in gfloor (t_POLMOD).")
+error("incorrect type in gfloor (t_POLMOD).")
+* [6, 5]
+error("impossible inverse in RgXQ_inv: 0.")
+error("forbidden euclidean division t_POLMOD , t_POL.")
+Mod(x, x^2 + 1)
+error("forbidden euclidean division t_POLMOD , t_POL.")
+error("forbidden euclidean division t_POLMOD , t_POL.")
+* [6, 6]
+Mod(1, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_POLMOD.")
+error("forbidden division t_POLMOD % t_POLMOD.")
+error("forbidden division t_POLMOD \\ t_POLMOD.")
+error("forbidden division t_POLMOD \\ t_POLMOD.")
+* [6, 7]
+Mod((2*t^4 + 2*t^3 + t^2)*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_FFELT.")
+error("forbidden division t_POLMOD % t_FFELT.")
+error("forbidden division t_POLMOD \\ t_FFELT.")
+error("forbidden division t_POLMOD \\ t_FFELT.")
+* [6, 8]
+Mod((-1 + w)*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_QUAD.")
+error("forbidden division t_POLMOD % t_QUAD.")
+error("forbidden division t_POLMOD \\ t_QUAD.")
+error("forbidden division t_POLMOD \\ t_QUAD.")
+* [6, 9]
+Mod((2 + 3 + 3^2 + O(3^3))*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_PADIC.")
+error("forbidden division t_POLMOD % t_PADIC.")
+error("forbidden division t_POLMOD \\ t_PADIC.")
+error("forbidden division t_POLMOD \\ t_PADIC.")
+* [6, 10]
+Mod(Mod(2, 3)*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_INTMOD.")
+error("forbidden division t_POLMOD % t_INTMOD.")
+error("forbidden division t_POLMOD \\ t_INTMOD.")
+error("forbidden division t_POLMOD \\ t_INTMOD.")
+* [6, 11]
+Mod(1/18446744073709551617*x, x^2 + 1)
+error("forbidden division t_POLMOD \\ t_INT.")
+error("forbidden division t_POLMOD % t_INT.")
+error("forbidden division t_POLMOD \\ t_INT.")
+error("forbidden division t_POLMOD \\ t_INT.")
+* [7, 1]
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+error("forbidden division t_FFELT \\ t_INT.")
+error("forbidden division t_FFELT % t_INT.")
+error("forbidden division t_FFELT \\ t_INT.")
+error("forbidden division t_FFELT \\ t_INT.")
+* [7, 2]
+t
+error("forbidden division t_FFELT \\ t_INT.")
+error("forbidden division t_FFELT % t_INT.")
+error("forbidden division t_FFELT \\ t_INT.")
+error("forbidden division t_FFELT \\ t_INT.")
+* [7, 3]
+error("forbidden division t_FFELT / t_REAL.")
+error("forbidden division t_FFELT / t_REAL.")
+error("forbidden division t_FFELT % t_REAL.")
+error("forbidden division t_FFELT / t_REAL.")
+error("forbidden division t_FFELT / t_REAL.")
+* [7, 4]
+2*t
+error("incorrect type in gfloor (t_FFELT).")
+error("forbidden division t_FFELT % t_FRAC.")
+error("incorrect type in gfloor (t_FFELT).")
+error("incorrect type in gfloor (t_FFELT).")
+* [7, 5]
+t/(x^2 + 1)
+0
+t
+0
+[0, t]~
+* [7, 6]
+Mod(2*t*x, x^2 + 1)
+error("forbidden division t_FFELT \\ t_POLMOD.")
+error("forbidden division t_FFELT % t_POLMOD.")
+error("forbidden division t_FFELT \\ t_POLMOD.")
+error("forbidden division t_FFELT \\ t_POLMOD.")
+* [7, 7]
+1
+error("forbidden division t_FFELT \\ t_FFELT.")
+error("forbidden division t_FFELT % t_FFELT.")
+error("forbidden division t_FFELT \\ t_FFELT.")
+error("forbidden division t_FFELT \\ t_FFELT.")
+* [7, 8]
+error("forbidden division t_FFELT / t_QUAD.")
+error("forbidden division t_FFELT \\ t_QUAD.")
+error("forbidden division t_FFELT % t_QUAD.")
+error("forbidden division t_FFELT \\ t_QUAD.")
+error("forbidden division t_FFELT \\ t_QUAD.")
+* [7, 9]
+error("forbidden division t_FFELT / t_PADIC.")
+error("forbidden division t_FFELT \\ t_PADIC.")
+error("forbidden division t_FFELT % t_PADIC.")
+error("forbidden division t_FFELT \\ t_PADIC.")
+error("forbidden division t_FFELT \\ t_PADIC.")
+* [7, 10]
+2*t
+error("forbidden division t_FFELT \\ t_INTMOD.")
+error("forbidden division t_FFELT % t_INTMOD.")
+error("forbidden division t_FFELT \\ t_INTMOD.")
+error("forbidden division t_FFELT \\ t_INTMOD.")
+* [7, 11]
+2*t
+error("forbidden division t_FFELT \\ t_INT.")
+error("forbidden division t_FFELT % t_INT.")
+error("forbidden division t_FFELT \\ t_INT.")
+error("forbidden division t_FFELT \\ t_INT.")
+* [8, 1]
+1/3*w
+error("forbidden division t_QUAD \\ t_INT.")
+w
+error("forbidden division t_QUAD \\ t_INT.")
+error("forbidden division t_QUAD \\ t_INT.")
+* [8, 2]
+1/4*w
+error("forbidden division t_QUAD \\ t_INT.")
+w
+error("forbidden division t_QUAD \\ t_INT.")
+error("forbidden division t_QUAD \\ t_INT.")
+* [8, 3]
+0.52194644798383704780793123689214132830
+0
+error("forbidden division t_QUAD % t_REAL.")
+0
+[0, w]~
+* [8, 4]
+2*w
+error("incorrect type in gfloor (t_QUAD).")
+error("forbidden division t_QUAD % t_FRAC.")
+error("incorrect type in gfloor (t_QUAD).")
+error("incorrect type in gfloor (t_QUAD).")
+* [8, 5]
+w/(x^2 + 1)
+0
+w
+0
+[0, w]~
+* [8, 6]
+Mod(-w*x, x^2 + 1)
+error("forbidden division t_QUAD \\ t_POLMOD.")
+error("forbidden division t_QUAD % t_POLMOD.")
+error("forbidden division t_QUAD \\ t_POLMOD.")
+error("forbidden division t_QUAD \\ t_POLMOD.")
+* [8, 7]
+error("forbidden division t_QUAD / t_FFELT.")
+error("forbidden division t_QUAD \\ t_FFELT.")
+error("forbidden division t_QUAD % t_FFELT.")
+error("forbidden division t_QUAD \\ t_FFELT.")
+error("forbidden division t_QUAD \\ t_FFELT.")
+* [8, 8]
+1
+error("forbidden division t_QUAD \\ t_QUAD.")
+error("forbidden division t_QUAD % t_QUAD.")
+error("forbidden division t_QUAD \\ t_QUAD.")
+error("forbidden division t_QUAD \\ t_QUAD.")
+* [8, 9]
+error("not an n-th power residue in Qp_sqrt: 5.")
+error("forbidden division t_QUAD \\ t_PADIC.")
+error("forbidden division t_QUAD % t_PADIC.")
+error("forbidden division t_QUAD \\ t_PADIC.")
+error("forbidden division t_QUAD \\ t_PADIC.")
+* [8, 10]
+Mod(0, 3) + Mod(2, 3)*w
+error("forbidden division t_QUAD \\ t_INTMOD.")
+error("forbidden division t_QUAD % t_INTMOD.")
+error("forbidden division t_QUAD \\ t_INTMOD.")
+error("forbidden division t_QUAD \\ t_INTMOD.")
+* [8, 11]
+1/18446744073709551617*w
+error("forbidden division t_QUAD \\ t_INT.")
+w
+error("forbidden division t_QUAD \\ t_INT.")
+error("forbidden division t_QUAD \\ t_INT.")
+* [9, 1]
+2*3^-1 + O(3^2)
+error("forbidden division t_PADIC \\ t_INT.")
+2
+error("forbidden division t_PADIC \\ t_INT.")
+error("forbidden division t_PADIC \\ t_INT.")
+* [9, 2]
+2 + 3 + 3^2 + O(3^3)
+error("forbidden division t_PADIC \\ t_INT.")
+error("inconsistent  t_PADIC , t_INTMOD.")
+error("forbidden division t_PADIC \\ t_INT.")
+error("forbidden division t_PADIC \\ t_INT.")
+* [9, 3]
+error("forbidden division t_PADIC / t_REAL.")
+error("forbidden division t_PADIC / t_REAL.")
+error("forbidden division t_PADIC % t_REAL.")
+error("forbidden division t_PADIC / t_REAL.")
+error("forbidden division t_PADIC / t_REAL.")
+* [9, 4]
+1 + 3 + O(3^3)
+error("incorrect type in gfloor (t_PADIC).")
+error("forbidden division t_PADIC % t_FRAC.")
+error("incorrect type in gfloor (t_PADIC).")
+error("incorrect type in gfloor (t_PADIC).")
+* [9, 5]
+(2 + O(3^3))/(x^2 + 1)
+0
+2 + O(3^3)
+0
+[0, 2 + O(3^3)]~
+* [9, 6]
+Mod((1 + 2*3 + 2*3^2 + O(3^3))*x, x^2 + 1)
+error("forbidden division t_PADIC \\ t_POLMOD.")
+error("forbidden division t_PADIC % t_POLMOD.")
+error("forbidden division t_PADIC \\ t_POLMOD.")
+error("forbidden division t_PADIC \\ t_POLMOD.")
+* [9, 7]
+error("forbidden division t_PADIC / t_FFELT.")
+error("forbidden division t_PADIC \\ t_FFELT.")
+error("forbidden division t_PADIC % t_FFELT.")
+error("forbidden division t_PADIC \\ t_FFELT.")
+error("forbidden division t_PADIC \\ t_FFELT.")
+* [9, 8]
+1 + 2*3 + 2*3^2 + O(3^3) + (2 + O(3^3))*w
+error("forbidden division t_PADIC \\ t_QUAD.")
+error("forbidden division t_PADIC % t_QUAD.")
+error("forbidden division t_PADIC \\ t_QUAD.")
+error("forbidden division t_PADIC \\ t_QUAD.")
+* [9, 9]
+1 + O(3^3)
+error("forbidden division t_PADIC \\ t_PADIC.")
+error("forbidden division t_PADIC % t_PADIC.")
+error("forbidden division t_PADIC \\ t_PADIC.")
+error("forbidden division t_PADIC \\ t_PADIC.")
+* [9, 10]
+Mod(1, 3)
+error("forbidden division t_PADIC \\ t_INTMOD.")
+error("forbidden division t_PADIC % t_INTMOD.")
+error("forbidden division t_PADIC \\ t_INTMOD.")
+error("forbidden division t_PADIC \\ t_INTMOD.")
+* [9, 11]
+1 + 2*3 + 2*3^2 + O(3^3)
+error("forbidden division t_PADIC \\ t_INT.")
+error("inconsistent  t_PADIC , t_INTMOD.")
+error("forbidden division t_PADIC \\ t_INT.")
+error("forbidden division t_PADIC \\ t_INT.")
+* [10, 1]
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+error("forbidden division t_INTMOD \\ t_INT.")
+Mod(2, 3)
+error("forbidden division t_INTMOD \\ t_INT.")
+error("forbidden division t_INTMOD \\ t_INT.")
+* [10, 2]
+Mod(2, 3)
+error("forbidden division t_INTMOD \\ t_INT.")
+Mod(0, 1)
+error("forbidden division t_INTMOD \\ t_INT.")
+error("forbidden division t_INTMOD \\ t_INT.")
+* [10, 3]
+error("forbidden division t_INTMOD / t_REAL.")
+error("forbidden division t_INTMOD / t_REAL.")
+error("forbidden division t_INTMOD % t_REAL.")
+error("forbidden division t_INTMOD / t_REAL.")
+error("forbidden division t_INTMOD / t_REAL.")
+* [10, 4]
+Mod(1, 3)
+error("incorrect type in gfloor (t_INTMOD).")
+error("forbidden division t_INTMOD % t_FRAC.")
+error("incorrect type in gfloor (t_INTMOD).")
+error("incorrect type in gfloor (t_INTMOD).")
+* [10, 5]
+Mod(2, 3)/(x^2 + 1)
+0
+Mod(2, 3)
+0
+[0, Mod(2, 3)]~
+* [10, 6]
+Mod(Mod(1, 3)*x, x^2 + 1)
+error("forbidden division t_INTMOD \\ t_POLMOD.")
+error("forbidden division t_INTMOD % t_POLMOD.")
+error("forbidden division t_INTMOD \\ t_POLMOD.")
+error("forbidden division t_INTMOD \\ t_POLMOD.")
+* [10, 7]
+t^4 + t^3 + 2*t^2
+error("forbidden division t_INTMOD \\ t_FFELT.")
+error("forbidden division t_INTMOD % t_FFELT.")
+error("forbidden division t_INTMOD \\ t_FFELT.")
+error("forbidden division t_INTMOD \\ t_FFELT.")
+* [10, 8]
+Mod(1, 3) + Mod(2, 3)*w
+error("forbidden division t_INTMOD \\ t_QUAD.")
+error("forbidden division t_INTMOD % t_QUAD.")
+error("forbidden division t_INTMOD \\ t_QUAD.")
+error("forbidden division t_INTMOD \\ t_QUAD.")
+* [10, 9]
+Mod(1, 3)
+error("forbidden division t_INTMOD \\ t_PADIC.")
+error("forbidden division t_INTMOD % t_PADIC.")
+error("forbidden division t_INTMOD \\ t_PADIC.")
+error("forbidden division t_INTMOD \\ t_PADIC.")
+* [10, 10]
+Mod(1, 3)
+error("forbidden division t_INTMOD \\ t_INTMOD.")
+error("forbidden division t_INTMOD % t_INTMOD.")
+error("forbidden division t_INTMOD \\ t_INTMOD.")
+error("forbidden division t_INTMOD \\ t_INTMOD.")
+* [10, 11]
+Mod(1, 3)
+error("forbidden division t_INTMOD \\ t_INT.")
+Mod(0, 1)
+error("forbidden division t_INTMOD \\ t_INT.")
+error("forbidden division t_INTMOD \\ t_INT.")
+* [11, 1]
+18446744073709551617/3
+6148914691236517205
+2
+6148914691236517206
+[6148914691236517205, 2]~
+* [11, 2]
+18446744073709551617/4
+4611686018427387904
+1
+4611686018427387904
+[4611686018427387904, 1]~
+* [11, 3]
+5950562604422436005.4838709677419354839
+5950562604422436005
+1.5000000000000000001
+5950562604422436005
+[5950562604422436005, 1.5000000000000000001]~
+* [11, 4]
+36893488147419103234
+36893488147419103234
+0
+36893488147419103234
+[36893488147419103234, 0]~
+* [11, 5]
+18446744073709551617/(x^2 + 1)
+0
+18446744073709551617
+0
+[0, 18446744073709551617]~
+* [11, 6]
+Mod(-18446744073709551617*x, x^2 + 1)
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT % t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+* [11, 7]
+t^4 + t^3 + 2*t^2
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT % t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+* [11, 8]
+-18446744073709551617 + 18446744073709551617*w
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT % t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+* [11, 9]
+1 + 3 + 3^2 + O(3^3)
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT % t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+* [11, 10]
+Mod(1, 3)
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT % t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+* [11, 11]
+1
+1
+0
+1
+[1, 0]~
+* [1, 1]
+1/3*x + O(x^2)
+error("forbidden division t_SER \\ t_INT.")
+error("forbidden division t_SER % t_INT.")
+error("forbidden division t_SER \\ t_INT.")
+* [1, 2]
+1/4*x + O(x^2)
+error("forbidden division t_SER \\ t_INT.")
+error("forbidden division t_SER % t_INT.")
+error("forbidden division t_SER \\ t_INT.")
+* [1, 3]
+0.32258064516129032258064516129032258065*x + O(x^2)
+error("incorrect type in gfloor (t_SER).")
+error("forbidden division t_SER % t_REAL.")
+error("incorrect type in gfloor (t_SER).")
+* [1, 4]
+2*x + O(x^2)
+error("incorrect type in gfloor (t_SER).")
+error("forbidden division t_SER % t_FRAC.")
+error("incorrect type in gfloor (t_SER).")
+* [1, 5]
+x + O(x^2)
+error("forbidden euclidean division t_SER , t_POL.")
+error("forbidden division t_SER % t_POL.")
+error("forbidden euclidean division t_SER , t_POL.")
+* [1, 6]
+error("forbidden division t_SER % t_POL.")
+error("forbidden division t_SER \\ t_POLMOD.")
+error("forbidden division t_SER % t_POLMOD.")
+error("forbidden division t_SER \\ t_POLMOD.")
+* [1, 7]
+(2*t^4 + 2*t^3 + t^2)*x + O(x^2)
+error("forbidden division t_SER \\ t_FFELT.")
+error("forbidden division t_SER % t_FFELT.")
+error("forbidden division t_SER \\ t_FFELT.")
+* [1, 8]
+(-1 + w)*x + O(x^2)
+error("forbidden division t_SER \\ t_QUAD.")
+error("forbidden division t_SER % t_QUAD.")
+error("forbidden division t_SER \\ t_QUAD.")
+* [1, 9]
+(2 + 3 + 3^2 + O(3^3))*x + O(x^2)
+error("forbidden division t_SER \\ t_PADIC.")
+error("forbidden division t_SER % t_PADIC.")
+error("forbidden division t_SER \\ t_PADIC.")
+* [1, 10]
+Mod(2, 3)*x + O(x^2)
+error("forbidden division t_SER \\ t_INTMOD.")
+error("forbidden division t_SER % t_INTMOD.")
+error("forbidden division t_SER \\ t_INTMOD.")
+* [1, 11]
+1/18446744073709551617*x + O(x^2)
+error("forbidden division t_SER \\ t_INT.")
+error("forbidden division t_SER % t_INT.")
+error("forbidden division t_SER \\ t_INT.")
+* [2, 1]
+[2/3, 1]
+[0, 1]
+[2, 0]
+[1, 1]
+* [2, 2]
+[1/2, 3/4]
+[0, 0]
+[2, 3]
+[1, 1]
+* [2, 3]
+[0.64516129032258064516129032258064516129, 0.9677419354838709677419354838709
+6774194]
+[0, 0]
+[2, 3]
+[0, 0]
+* [2, 4]
+[4, 6]
+[4, 6]
+[0, 0]
+[4, 6]
+* [2, 5]
+[2/(x^2 + 1), 3/(x^2 + 1)]
+[0, 0]
+[2, 3]
+[0, 0]
+* [2, 6]
+[Mod(-2*x, x^2 + 1), Mod(-3*x, x^2 + 1)]
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT % t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+* [2, 7]
+[t^4 + t^3 + 2*t^2, 0]
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT % t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+* [2, 8]
+[-2 + 2*w, -3 + 3*w]
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT % t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+* [2, 9]
+[1 + O(3^3), 2*3 + 3^2 + 3^3 + O(3^4)]
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT % t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+* [2, 10]
+[Mod(1, 3), Mod(0, 3)]
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT % t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+* [2, 11]
+[2/18446744073709551617, 3/18446744073709551617]
+[0, 0]
+[2, 3]
+[0, 0]
+* [3, 1]
+Mat(2/3)
+Mat(0)
+Mat(2)
+Mat(1)
+* [3, 2]
+Mat(1/2)
+Mat(0)
+Mat(2)
+Mat(1)
+* [3, 3]
+Mat(0.64516129032258064516129032258064516129)
+Mat(0)
+Mat(2)
+Mat(0)
+* [3, 4]
+Mat(4)
+Mat(4)
+Mat(0)
+Mat(4)
+* [3, 5]
+Mat(2/(x^2 + 1))
+Mat(0)
+Mat(2)
+Mat(0)
+* [3, 6]
+Mat(Mod(-2*x, x^2 + 1))
+error("forbidden division t_INT \\ t_POLMOD.")
+error("forbidden division t_INT % t_POLMOD.")
+error("forbidden division t_INT \\ t_POLMOD.")
+* [3, 7]
+Mat(t^4 + t^3 + 2*t^2)
+error("forbidden division t_INT \\ t_FFELT.")
+error("forbidden division t_INT % t_FFELT.")
+error("forbidden division t_INT \\ t_FFELT.")
+* [3, 8]
+Mat(-2 + 2*w)
+error("forbidden division t_INT \\ t_QUAD.")
+error("forbidden division t_INT % t_QUAD.")
+error("forbidden division t_INT \\ t_QUAD.")
+* [3, 9]
+Mat(1 + O(3^3))
+error("forbidden division t_INT \\ t_PADIC.")
+error("forbidden division t_INT % t_PADIC.")
+error("forbidden division t_INT \\ t_PADIC.")
+* [3, 10]
+Mat(Mod(1, 3))
+error("forbidden division t_INT \\ t_INTMOD.")
+error("forbidden division t_INT % t_INTMOD.")
+error("forbidden division t_INT \\ t_INTMOD.")
+* [3, 11]
+Mat(2/18446744073709551617)
+Mat(0)
+Mat(2)
+Mat(0)
+[0, 1]
+Mat(0)
+[1, 1]
+Mat(1)
+[-x + y, x^2 + x]~
+[[1, 1]~, [2, 1]~]
+[0, 1]~
+[1, 0]~
+  ***   at top-level: divrem(1,"a")
+  ***                 ^-------------
+  *** divrem: forbidden division t_INT \ t_STR.
+2
+x
+Total time spent: 0
diff --git a/src/test/32/ell b/src/test/32/ell
new file mode 100644
index 0000000..f0e6ab0
--- /dev/null
+++ b/src/test/32/ell
@@ -0,0 +1,398 @@
+-1
+0
+152
+1031:[504, 2]
+2053:[1008, 2]
+4099:[4196]
+8209:[8291]
+16411:[8280, 2]
+32771:[32545]
+65537:[65115]
+131101:[130579]
+262147:[261873]
+524309:[525362]
+1048583:[1048721]
+2097169:[2099343]
+4194319:[4190448]
+8388617:[4196176, 2]
+16777259:[16776451]
+33554467:[33556544]
+67108879:[33553348, 2]
+134217757:[134207016]
+268435459:[268450764]
+536870923:[536886729]
+1073741827:[1073696739]
+2147483659:[2147445985]
+4294967311:[4294892145]
+8589934609:[8589800815]
+17179869209:[17179907771]
+34359738421:[34359891299]
+68719476767:[68719109932]
+137438953481:[137439150447]
+274877906951:[274876963417]
+549755813911:[549755723143]
+1099511627791:[1099510624080]
+2199023255579:[1099512197774, 2]
+4398046511119:[4398049864270]
+8796093022237:[8796090641581]
+17592186044423:[17592179180564]
+35184372088891:[35184377696395]
+70368744177679:[70368735914810]
+140737488355333:[140737466844674]
+281474976710677:[281474967245574]
+562949953421381:[562949910045019]
+1125899906842679:[562949923357406, 2]
+2251799813685269:[2251799812875502]
+4503599627370517:[4503599672855988]
+9007199254740997:[9007199395723803]
+18014398509482143:[18014398460825440]
+36028797018963971:[18014398463069820, 2]
+72057594037928017:[36028797145369816, 2]
+144115188075855881:[144115187446866113]
+288230376151711813:[288230375567209858]
+576460752303423619:[576460752721346915]
+1152921504606847009:[1152921506693313952]
+2305843009213693967:[2305843010596733829]
+4611686018427388039:[4611686021547019756]
+9223372036854775837:[9223372041689460430]
+15
+1
+1
+163663
+121661
+1
+1023
+494
+[4, [2, 2], [[-2147484185, 0], [0, 0]]]
+2
+2
+0
+0
+0
+1728
+j
+0
+Mod(0, 5)
+Mod(3, 5)
+Mod(1, 2)*j
+0
+Mod(1, 3)*j
+a
+a
+8*x^9 + 54*x^8 + 393*x^7 + 2373*x^6 + 6993*x^5 + 15267*x^4 + 19998*x^3 + 473
+4*x^2 - 25880*x - 30932
+16*x^33 + 20048*x^30 - 524864*x^27 - 20273280*x^24 - 35051520*x^21 - 1832755
+20*x^18 - 818626560*x^15 - 1017937920*x^12 - 390856704*x^9 + 74973184*x^6 + 
+102760448*x^3 + 4194304
+[3.1096482423243803285501491221965830079, 1.55482412116219016427507456109829
+15039 + 1.0643747452102737569438859937299427442*I]
+[6.2192964846487606571002982443931660158, 3.10964824232438032855014912219658
+30079 + 2.1287494904205475138877719874598854884*I]
+[5.5614800275334595421263952543627169988, 2.78074001376672977106319762718135
+84994 - 2.1374995527123861323185270948750077575*I]
+[6.2192964846487606571002982443931660158, 3.10964824232438032855014912219658
+30079 + 2.1287494904205475138877719874598854884*I]
+[-1.1547274830668428355945002349018042438, -0.828886258466578582202749882549
+09787812 + 0.52313677422798965199542236165917364573*I, -0.828886258466578582
+20274988254909787812 - 0.52313677422798965199542236165917364573*I]
+[10351, [1/2, -1, -2, 5/4], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]
+]
+[10351, [1, -1, 0, -1], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]]
+  ***   at top-level: E.omega
+  ***                   ^-----
+  *** _.omega: incorrect type in omega [not defined over C] (t_VEC).
+[9, [9], [[Mod(3, 7), Mod(5, 7)]]]
+[0, 0, 0, 413748, 716503104, 0, 827496, 2866012416, -171187407504, -19859904
+, -619058681856, -226311754192704000000, 97158364170048/2807086984375, Vecsm
+all([1]), [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~]]]
+[1/30, -13/150, -1/10, -79/500]
+1
+[36, [36], [[a^2 + 1, a^3 + a^2 + a + 1]]]
+1
+[3, [3], [[0, 2]]]
+1
+[4, [4], [[Mod(3, 5), Mod(3, 5)]]]
+[1 + 2*3 + 3^2 + 2*3^3 + 3^4 + 3^5 + 2*3^6 + 3^7 + O(3^8), 1 + 3 + 3^3 + 3^4
+ + 2*3^5 + 2*3^6 + 2*3^7 + O(3^8), 3 + 2*3^3 + O(3^6), [1 + 2*3 + 3^2 + 2*3^
+3 + 3^4 + 3^6 + 2*3^7 + O(3^8), 1 + 3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + 
+3^7 + O(3^8)]]
+[3^-1 + 2 + 2*3^2 + 2*3^5 + 2*3^6 + O(3^8)]~
+[3^2 + 2*3^3 + 3^4 + 2*3^5 + 3^6 + 3^7 + 2*3^8 + 3^9 + O(3^10), 3 + 3^2 + 3^
+4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^9), 3 + 2*3^3 + O(3^6), [3^-2 + 2*3^-1
+ + 1 + 2*3 + 3^2 + 3^4 + 2*3^5 + O(3^6), 3^-2 + 3^-1 + 2 + 3 + 2*3^2 + 2*3^3
+ + 3^4 + 3^5 + O(3^6)]]
+error("incorrect type in obj_check (t_VEC).")
+[2 + 2^6 + 2^10 + O(2^11), Mod(u, u^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^7 + 2
+^8 + 2^9 + O(2^11))), 2^3 + 2^4 + O(2^8), [2^-3 + 2^2 + 2^4 + 2^7 + 2^10 + O
+(2^11), 2^-3 + 2^2 + 2^5 + 2^6 + 2^10 + O(2^13)]]
+x^-2 + 31/15*x^2 + 2501/756*x^4 + 961/675*x^6 + 77531/41580*x^8 + O(x^9)
+[-1, -2*w]
+[I, 1]
+[[I, 1], [-3.1415926535897932384626433832795028842*I, 3.14159265358979323846
+26433832795028842]]
+[1, 1]
+x^-2 - 1/5*x^2 - 1/7*x^4 + 1/75*x^6 + 3/385*x^8 + 277/238875*x^10 - 2/5775*x
+^12 + O(x^14)
+x^-2 - 1/5*x^2 - 1/7*x^4 + 1/75*x^6 + O(x^7)
+8.9760336058655702799613054290253052728
+-8.9795585687185301843619815765809019104
+0.0070737179180847219897019688523688143761 - 4.54459013280902760664280136539
+71181201*I
+[1, 2]
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622 E-37*x^4 + 
+29.790411247556326629130082765180921498*x^6 + 1.695813583 E-36*x^8 + 43.3273
+39141107674122263886023453990048*x^10 + 3.562578744 E-36*x^12 + O(x^14)
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622 E-37*x^4 + 
+29.790411247556326629130082765180921498*x^6 + O(x^7)
+10.092015307351769584764433105625607145
+-10.092015307351769584764433105625607145
+3.2557987470773994635555990212606293690 E-38 - 2.704147351607435273075740713
+3303875089*I
+[1, 3]
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622 E-37*x^4 + 
+29.790411247556326629130082765180921498*x^6 + 1.695813583 E-36*x^8 + 43.3273
+39141107674122263886023453990048*x^10 + 3.562578744 E-36*x^12 + O(x^14)
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622 E-37*x^4 + 
+29.790411247556326629130082765180921498*x^6 + O(x^7)
+10.092015307351769584764433105625607145
+-10.092015307351769584764433105625607145
+3.2557987470773994635555990212606293690 E-38 - 2.704147351607435273075740713
+3303875089*I
+[2, 1]
+x^-1 + 1/15*x^3 + 1/35*x^5 - 1/525*x^7 - 1/1155*x^9 - 277/2627625*x^11 + 2/7
+5075*x^13 + O(x^15)
+x^-1 + 1/15*x^3 + 1/35*x^5 - 1/525*x^7 + O(x^8)
+3.0025857981852417376980007365038576528
+-3.0023507303355942712341893343171384978*I
+1.4945837634650773441141478432745008118 - 1.49552579635851441107083905597206
+14467*I
+[2, 2]
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.315469124 E-37*x^5 - 
+4.2557730353651895184471546807401316426*x^7 - 1.884237315 E-37*x^9 - 3.93884
+90128279703747512623657685445499*x^11 - 2.740445188 E-37*x^13 + O(x^15)
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.315469124 E-37*x^5 - 
+4.2557730353651895184471546807401316426*x^7 + O(x^8)
+2.8609969154308155967482927187353233603
+-2.8813199850735158607638401394492232764*I
+1.7185329556464940714815988649194112975 - 1.71853295564649407148159886491941
+12976*I
+[2, 3]
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.315469124 E-37*x^5 - 
+4.2557730353651895184471546807401316426*x^7 - 1.884237315 E-37*x^9 - 3.93884
+90128279703747512623657685445499*x^11 - 2.740445188 E-37*x^13 + O(x^15)
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.315469124 E-37*x^5 - 
+4.2557730353651895184471546807401316426*x^7 + O(x^8)
+2.8609969154308155967482927187353233603
+-2.8813199850735158607638401394492232764*I
+1.7185329556464940714815988649194112975 - 1.71853295564649407148159886491941
+12976*I
+[3, 1]
+x + 1/60*x^5 + 1/210*x^7 - 1/10080*x^9 - 1/138600*x^11 - 167/259459200*x^13 
+- 19/1513512000*x^15 + O(x^17)
+x + 1/60*x^5 + 1/210*x^7 - 1/10080*x^9 + O(x^10)
+0.33340409272605175654322174351877926789
+0.33339973807064633526799756411632693200*I
+0.33307632454406929865753194192439552171 + 0.3330414840427217068846417452694
+8964209*I
+[3, 2]
+x - 0.78780300053847438455442248556217213914*x^5 - 2.192448541 E-38*x^7 - 0.
+22165484559193695408578930628854852305*x^9 - 1.570197765 E-39*x^11 + 0.00936
+19303173614540570518182056750707483*x^13 + 1.291380487 E-40*x^15 + O(x^17)
+x - 0.78780300053847438455442248556217213914*x^5 - 2.192448541 E-38*x^7 - 0.
+22165484559193695408578930628854852305*x^9 + O(x^10)
+0.33008009031657824359527653587336069208
+0.33008009031657824359527653587336069208*I
+0.34612072856429482856153662202577445445 + 0.3461207285642948285615366220257
+7445446*I
+[3, 3]
+x - 0.78780300053847438455442248556217213914*x^5 - 2.192448541 E-38*x^7 - 0.
+22165484559193695408578930628854852305*x^9 - 1.570197765 E-39*x^11 + 0.00936
+19303173614540570518182056750707483*x^13 + 1.291380487 E-40*x^15 + O(x^17)
+x - 0.78780300053847438455442248556217213914*x^5 - 2.192448541 E-38*x^7 - 0.
+22165484559193695408578930628854852305*x^9 + O(x^10)
+0.33008009031657824359527653587336069208
+0.33008009031657824359527653587336069208*I
+0.34612072856429482856153662202577445445 + 0.3461207285642948285615366220257
+7445446*I
+[4, 1]
+0
+0
+-1.0984000330177788282680372407424344829
+-1.0984130942966868400436474225688716324 + 1.5707963267948966192313216916397
+514421*I
+-0.75286232322707031868584884787482252469 + 0.785345859584418994173505759767
+90041015*I
+[4, 2]
+0
+0
+-1.1084199560389642415209208807828823872
+-1.1084199560389642415209208807828823872 + 1.5707963267948966192313216916397
+514421*I
+-0.71439404801872849905771074604935970853 + 0.785398163397448309615660845819
+87572105*I
+[4, 3]
+0
+0
+-1.1084199560389642415209208807828823872
+-1.1084199560389642415209208807828823872 + 1.5707963267948966192313216916397
+514421*I
+-0.71439404801872849905771074604935970853 + 0.785398163397448309615660845819
+87572105*I
+[2.5135797437238231405782694715779164652, 1.25678987186191157028913473578895
+82326 + 0.78959476569186174055147277865716603189*I]
+[3.1415926535897932384626433832795028842, 9.42477796076937971538793014983850
+86526*I]
+(x)->elleisnum(x,2)
+-2.9936282668967606065680548947245432597 - 7.1637767384648910133063235008836
+078048*I
+-37.699111843077518861551720599354034610
+-37.699111843077518861551720599354034610
+(x)->elleisnum(x,4,1)
+-3.9999999999999999999999999999999999999 - 5.48564030 E-38*I
+189.07272012923385229306139653492131339
+189.07272012923385229306139653492131339
+(x)->elleisnum(x,6,1)
+-4.0000000000000000000000000000000000000 - 1.253860641 E-37*I
+1.841656774 E-35
+1.841656774 E-35
+(x)->elleisnum(x,10)
+-41471.999999999999999999999999999999998 - 7.70371978 E-34*I
+-2.818118420 E-30
+-2.818118420 E-30
+-1
+[0]
+347813742467679407541/38941611811810745401
+[1, [], []]
+[2, [2], [[15, -8]]]
+[3, [3], [[5, 9]]]
+[4, [4], [[5, -2]]]
+[5, [5], [[5, 5]]]
+[6, [6], [[9, 23]]]
+[7, [7], [[-1, 2]]]
+[8, [8], [[2, 6]]]
+[9, [9], [[-3, 7]]]
+[10, [10], [[0, 9]]]
+[12, [12], [[-9, 49]]]
+[4, [2, 2], [[-29/4, 25/8], [-7, 3]]]
+[8, [4, 2], [[-2, 3], [-1, 0]]]
+[12, [6, 2], [[1, 2], [3, -2]]]
+[16, [8, 2], [[4, 58], [-36, 18]]]
+[16, [8, 2], [[117433600, 6734213027200], [352179456, -176089728]]]
+[4, [2, 2], [[-1377493124511464657, 0], [-691668349248679055, 0]]]
+[0.49999999999999999999999999999999999978 - 2.057115114 E-38*I, 1.9216402159
+513147090074725264936203858 + 0.26019438802828824617801390769760176484*I]
+3 + 11^2 + 2*11^3 + 3*11^4 + O(11^5)
+Mod((2 + 3 + O(3^4))*u + (2*3 + 3^2 + O(3^4)), u^2 + (1 + 3 + 2*3^4 + 3^8 + 
+2*3^9 + O(3^10)))
+Mod((1 + 3 + 3^3 + 3^4 + 2*3^6 + 2*3^8 + 2*3^9 + O(3^10))*u + (1 + 3 + 3^2 +
+ 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 3^9 + O(3^10)), u^2 + (3 + 3^3 + 2*3^4 + 3^5 
++ 2*3^6 + 3^7 + 2*3^8 + 3^9 + 2*3^10 + 3^11 + 2*3^12 + O(3^13)))
+Mod((2^3 + 2^7 + O(2^8))*u + (1 + 2 + 2^2 + 2^3 + 2^4 + O(2^6)), u^2 + (1 + 
+2^2 + 2^4 + 2^5 + 2^7 + 2^8 + 2^9 + O(2^13)))
+[Mod(0, 11), Mod(0, 11), Mod(0, 11), Mod(1, 11), Mod(1, 11), Mod(0, 11), Mod
+(2, 11), Mod(4, 11), Mod(10, 11), Mod(7, 11), Mod(5, 11), Mod(10, 11), Mod(9
+, 11), Vecsmall([3]), [11, [9, 5, [6, 0, 0, 0]]], [0, 0, 0, 0]]
+1
+[0.86602540378443864676372317075293618347 - 1/2*I, -0.8660254037844386467637
+2317075293618348 - 1/2*I]
+[-2, 3]
+[0, 1]
+[1, 0, 0, 0]
+0.035247504442186170440172838583518049039
+[0, 0, 0, 1, 1, 0, 2, 4, -1, -48, -864, -496, 6912/31, Vecsmall([1]), [Vecsm
+all([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~]]]
+[0, 0, 0, 1/16, 1/64, 0, 1/8, 1/16, -1/256, -3, -27/2, -31/256, 6912/31, Vec
+small([1]), [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~, [1/2, 0, 
+0, 0], [0, 0, 0, 1, 1, 0, 2, 4, -1, -48, -864, -496, 6912/31, Vecsmall([1]),
+ [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~]]]]]]
+
+0
+20 0 0 -16 0 -4 14 8 0 0 0 26 0 2 0 -28 
+1728
+0 0 -22 0 -14 0 -22 0 0 26 0 18 0 -14 2 0 
+-3375
+16 0 -10 0 -22 24 0 -20 0 0 4 0 8 -18 -26 0 
+8000
+0 -18 6 22 0 0 0 2 0 0 18 0 0 22 0 0 
+54000
+20 0 0 16 0 4 -14 -8 0 0 0 26 0 2 0 -28 
+-32768
+0 0 3 0 0 0 23 16 0 0 21 25 -15 0 0 -20 
+287496
+0 0 22 0 -14 0 22 0 0 -26 0 -18 0 14 2 0 
+-884736
+0 7 23 -9 11 0 18 -24 0 0 0 0 17 0 -22 -25 
+-12288000
+20 0 0 -23 0 19 14 25 0 0 0 7 0 23 0 -11 
+16581375
+16 0 10 0 22 24 0 -20 0 0 -4 0 8 -18 -26 0 
+-884736000
+11 0 0 -13 0 0 0 0 -25 -2 0 -6 0 -27 -10 0 
+-147197952000
+-21 -16 0 0 -23 1 5 7 20 -25 0 11 0 13 0 -27 
+-262537412640768000
+0 19 0 0 0 -21 0 0 4 -23 8 0 0 0 -25 -12 
+4294985035
+[0, 1, [5, 0, 0, 0], 1]
+1
+1
+[6.2500000000000000000000000000000000000, -140.62500000000000000000000000000
+000000]
+[37247908142/10128208321, 7601802384416381/1019292757217119]
+[0, 0, 0, x^2, x, 0, 2*x^2, 4*x, -x^4, -48*x^2, -864*x, -64*x^6 - 432*x^2, -
+6912*x^4/(-4*x^4 - 27), Vecsmall([0]), [Vecsmall([128, 0])], [0, 0, 0, 0]]
+  ***   at top-level: ellminimalmodel(E)
+  ***                 ^------------------
+  *** ellminimalmodel: incorrect type in checkell over Q (t_VEC).
+  ***   at top-level: ellweilpairing(E,[0]
+  ***                 ^--------------------
+  *** ellweilpairing: incorrect type in checkell over Fq (t_VEC).
+  ***   at top-level: ellinit([1])
+  ***                 ^------------
+  *** ellinit: incorrect type in ellxxx [not an elliptic curve (ell5)] (t_VEC).
+  ***   at top-level: ellinit([1,1],quadge
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_QUAD).
+  ***   at top-level: ellinit([Mod(1,2),1]
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+  ***   at top-level: ellinit([O(2),1],ffg
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+  ***   at top-level: ellinit([O(2),1],1.)
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+[0, 0, 0, 1, 2, 0, 2, 8, -1, -48, -1728, -1792, 432/7, Vecsmall([0]), [Vecsm
+all([128, -1])], [0, 0, 0, 0]]
+[0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 1, 3, 0, Vecsmall([4]), [0, [Vecsmall([0]), V
+ecsmall([0, 1]), [Vecsmall([0, 1]), Vecsmall([0]), Vecsmall([0]), Vecsmall([
+0])]]], [0, 0, 0, 0]]
+  ***   at top-level: ellinit([ffgen(5),1]
+  ***                 ^--------------------
+  *** ellinit: inconsistent moduli in ellinit: 3 != 5
+[0, 0, 0, 1.0000000000000000000000000000000000000, 1, 0, 2.00000000000000000
+00000000000000000000, 4, -1.0000000000000000000000000000000000000, -48.00000
+0000000000000000000000000000000, -864, -496.00000000000000000000000000000000
+000, 222.96774193548387096774193548387096774, Vecsmall([0]), [Vecsmall([128,
+ -1])], [0, 0, 0, 0]]
+  ***   at top-level: ellinit([1.,Mod(1,3)
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+1
+-1
+1
+x^-2 + Mod(-1/5*x, x^2 + 5)*x^2 + Mod(-1/15, x^2 + 5)*x^6 + Mod(2/975*x, x^2
+ + 5)*x^10 + O(x^14)
+x^-1 + Mod(1/15*x, x^2 + 5)*x^3 + Mod(1/105, x^2 + 5)*x^7 + Mod(-2/10725*x, 
+x^2 + 5)*x^11 + O(x^15)
+x + Mod(1/60*x, x^2 + 5)*x^5 + Mod(1/2016, x^2 + 5)*x^9 + Mod(23/51891840*x,
+ x^2 + 5)*x^13 + O(x^17)
+Mod(1, 1009)*x^-2 + Mod(807, 1009)*x^2 + Mod(148, 1009)*x^6 + Mod(368, 1009)
+*x^10 + O(x^14)
+Mod(1, 1009)*x^-1 + Mod(740, 1009)*x^3 + Mod(123, 1009)*x^7 + Mod(150, 1009)
+*x^11 + O(x^15)
+Mod(1, 1009)*x + Mod(185, 1009)*x^5 + Mod(101, 1009)*x^9 + Mod(990, 1009)*x^
+13 + O(x^17)
+-52760
+-52832
+Total time spent: 1052
diff --git a/src/test/32/ellanal b/src/test/32/ellanal
new file mode 100644
index 0000000..218e706
--- /dev/null
+++ b/src/test/32/ellanal
@@ -0,0 +1,14 @@
+[0, 0.25384186085591068433775892335090946105]
+[1, 0.30599977383405230182048368332167647444]
+[2, 1.5186330005768535404603852157894440392]
+[3, 10.391099400715804138751850510360917049]
+[5, 9997.0334671722554999496820788093288504]
+[-339/16, 691/64]
+[-3, 12]
+[69648970982596494254458225/166136231668185267540804, 5389624350896046150780
+04307258785218335/67716816556077455999228495435742408]
+[553/17424, 25469/2299968]
+35
+  ***   Warning: new stack size = 30000000 (28.610 Mbytes).
+[1317254400, 19916886528000]
+Total time spent: 13904
diff --git a/src/test/32/ellff b/src/test/32/ellff
new file mode 100644
index 0000000..f093026
--- /dev/null
+++ b/src/test/32/ellff
@@ -0,0 +1,6 @@
+93
+88
+[0]
+[0]
+Mod(1, 2)*a1^2*x^2 + Mod(1, 2)*a3^2
+Total time spent: 8029
diff --git a/src/test/32/ellglobalred b/src/test/32/ellglobalred
new file mode 100644
index 0000000..579c767
--- /dev/null
+++ b/src/test/32/ellglobalred
@@ -0,0 +1,20 @@
+[["496a1", [0, 0, 0, 1, 1], [[0, 1]]], [1, 0, 0, 0]]
+["11a1", [0, -1, 1, -10, -20], []]
+[["11a1", [0, -1, 1, -10, -20], []], ["11a2", [0, -1, 1, -7820, -263580], []
+], ["11a3", [0, -1, 1, 0, 0], []]]
+[]
+[["11a1", [0, -1, 1, -10, -20], []], ["11a2", [0, -1, 1, -7820, -263580], []
+], ["11a3", [0, -1, 1, 0, 0], []]]
+["11a1", [0, -1, 1, -10, -20], []]
+[["11a1", [0, -1, 1, -10, -20], []], ["11a2", [0, -1, 1, -7820, -263580], []
+], ["11a3", [0, -1, 1, 0, 0], []]]
+[["11a1", [0, -1, 1, -10, -20], []], ["11a2", [0, -1, 1, -7820, -263580], []
+], ["11a3", [0, -1, 1, 0, 0], []]]
+[["11a1", [0, -1, 1, -10, -20], []], ["11a2", [0, -1, 1, -7820, -263580], []
+], ["11a3", [0, -1, 1, 0, 0], []]]
+-122023936/161051
+error("incorrect type in ellsearch (t_VEC).")
+error("domain error in ellsearch: conductor <= 0")
+error("incorrect type in ellsearch (t_STR).")
+error("incorrect type in ellsearch (t_COMPLEX).")
+Total time spent: 1240
diff --git a/src/test/32/elliptic b/src/test/32/elliptic
new file mode 100644
index 0000000..95eed6d
--- /dev/null
+++ b/src/test/32/elliptic
@@ -0,0 +1,129 @@
+   echo = 1 (on)
+? ellinit([-1,0])
+[0, 0, 0, -1, 0, 0, -2, 0, -1, 48, 0, 64, 1728, Vecsmall([1]), [Vecsmall([12
+8, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? ellinit([-17,0],1)
+[0, 0, 0, -17, 0, 0, -34, 0, -289, 816, 0, 314432, 1728, Vecsmall([1]), [Vec
+small([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? ellsub(%,[-1,4],[-4,2])
+[9, -24]
+? ellj(I)
+1728.0000000000000000000000000000000000
+? acurve=ellinit([0,0,1,-1,0])
+[0, 0, 1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vecs
+mall([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? apoint=[2,2]
+[2, 2]
+? elladd(acurve,apoint,apoint)
+[21/25, -56/125]
+? ellak(acurve,1000000007)
+43800
+? ellan(acurve,100)
+[1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4, 3, 1
+0, 2, 0, -1, 4, -9, -2, 6, -12, -4, 8, 15, 0, 2, 12, -1, 0, 6, 0, -9, -6, 2,
+ -10, -12, -4, -9, 12, -6, 2, 0, -4, 1, 18, 10, 0, 0, -12, 8, 12, -8, 8, -6,
+ -8, 4, -30, 8, 0, -6, -4, 9, 0, -1, 2, 3, 0, 5, -12, 4, 8, 9, 18, -15, 6, 0
+, -4, -18, 0, 4, 24, 2, 4, 12, 18, 0, -24, 4, 12, -30, -2]
+? ellap(acurve,10007)
+66
+? deu=direuler(p=2,100,1/(1-ellap(acurve,p)*x+if(acurve[12]%p,p,0)*x^2))
+[1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4, 3, 1
+0, 2, 0, -1, 4, -9, -2, 6, -12, -4, 8, 15, 0, 2, 12, -1, 0, 6, 0, -9, -6, 2,
+ -10, -12, -4, -9, 12, -6, 2, 0, -4, 1, 18, 10, 0, 0, -12, 8, 12, -8, 8, -6,
+ -8, 4, -30, 8, 0, -6, -4, 9, 0, -1, 2, 3, 0, 5, -12, 4, 8, 9, 18, -15, 6, 0
+, -4, -18, 0, 4, 24, 2, 4, 12, 18, 0, -24, 4, 12, -30, -2]
+? ellan(acurve,100)==deu
+1
+? ellisoncurve(acurve,apoint)
+1
+? acurve=ellchangecurve(acurve,[-1,1,2,3])
+[-4, -1, -7, -12, -12, 12, 4, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]),
+ [Vecsmall([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? apoint=ellchangepoint(apoint,[-1,1,2,3])
+[1, 3]
+? ellisoncurve(acurve,apoint)
+1
+? ellglobalred(acurve)
+[37, [1, -1, 2, 2], 1, Mat([37, 1]), [[1, 5, 0, 1]]]
+? ellheight(acurve,apoint)
+0.81778253183950144377417759611107234575
+? ellheight(acurve,apoint,1)
+0.81778253183950144377417759611107234575
+? ellordinate(acurve,1)
+[8, 3]
+? ellpointtoz(acurve,apoint)
+0.72491221490962306778878739838332384646
+? ellztopoint(acurve,%)
+[1.0000000000000000000000000000000000000, 3.00000000000000000000000000000000
+00000]
+? ellmul(acurve,apoint,10)
+[-28919032218753260057646013785951999/292736325329248127651484680640160000, 
+478051489392386968218136375373985436596569736643531551/158385319626308443937
+475969221994173751192384064000000]
+? ellwp(acurve,x+O(x^33))
+x^-2 + 1/5*x^2 - 1/28*x^4 + 1/75*x^6 - 3/1540*x^8 + 1943/3822000*x^10 - 1/11
+550*x^12 + 193/10510500*x^14 - 1269/392392000*x^16 + 21859/34684650000*x^18 
+- 1087/9669660000*x^20 + 22179331/1060517858400000*x^22 - 463/124093970000*x
+^24 + 47495017/70175140035000000*x^26 - 34997918161/291117454720092000000*x^
+28 + O(x^30)
+? q*Ser(ellan(acurve,100),q)
+q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 - 5*q^11 - 
+6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 - 4*q^20 + 3*q^21 + 10*
+q^22 + 2*q^23 - q^25 + 4*q^26 - 9*q^27 - 2*q^28 + 6*q^29 - 12*q^30 - 4*q^31 
++ 8*q^32 + 15*q^33 + 2*q^35 + 12*q^36 - q^37 + 6*q^39 - 9*q^41 - 6*q^42 + 2*
+q^43 - 10*q^44 - 12*q^45 - 4*q^46 - 9*q^47 + 12*q^48 - 6*q^49 + 2*q^50 - 4*q
+^52 + q^53 + 18*q^54 + 10*q^55 - 12*q^58 + 8*q^59 + 12*q^60 - 8*q^61 + 8*q^6
+2 - 6*q^63 - 8*q^64 + 4*q^65 - 30*q^66 + 8*q^67 - 6*q^69 - 4*q^70 + 9*q^71 -
+ q^73 + 2*q^74 + 3*q^75 + 5*q^77 - 12*q^78 + 4*q^79 + 8*q^80 + 9*q^81 + 18*q
+^82 - 15*q^83 + 6*q^84 - 4*q^86 - 18*q^87 + 4*q^89 + 24*q^90 + 2*q^91 + 4*q^
+92 + 12*q^93 + 18*q^94 - 24*q^96 + 4*q^97 + 12*q^98 - 30*q^99 - 2*q^100 + O(
+q^101)
+? bcurve=ellinit([-3,0])
+[0, 0, 0, -3, 0, 0, -6, 0, -9, 144, 0, 1728, 1728, Vecsmall([1]), [Vecsmall(
+[128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? elllocalred(bcurve,2)
+[6, 2, [1, 1, 1, 0], 1]
+? elltaniyama(bcurve)
+[x^-2 - x^2 + 3*x^6 - 2*x^10 + 7*x^14 + O(x^15), -x^-3 + 3*x - 3*x^5 + 8*x^9
+ - 9*x^13 + O(x^14)]
+? ccurve=ellinit([0,0,-1,-1,0])
+[0, 0, -1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vec
+small([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? l=elllseries(ccurve,2)
+0.38157540826071121129371040958008663665
+? abs(elllseries(ccurve,2,1.2)-l)<1.4e-38
+1
+? tcurve=ellinit([1,0,1,-19,26]);
+? ellorder(tcurve,[1,2])
+6
+? elltors(tcurve)
+[12, [6, 2], [[1, 2], [3, -2]]]
+? mcurve=ellinit([-17,0])
+[0, 0, 0, -17, 0, 0, -34, 0, -289, 816, 0, 314432, 1728, Vecsmall([1]), [Vec
+small([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? mpoints=[[-1,4],[-4,2]]~
+[[-1, 4], [-4, 2]]~
+? mhbi=ellbil(mcurve,mpoints,[9,24])
+[-0.72448571035980184146215805860545027441, 1.307328627832055544492943428892
+1943056]~
+? ma=ellheightmatrix(mcurve,mpoints)
+
+[1.1721830987006970106016415566698834135 0.447697388340895169139483498064433
+13906]
+
+[0.44769738834089516913948349806443313906 1.75502601617295071363242692695662
+74446]
+
+? matsolve(ma,mhbi)
+[-1.0000000000000000000000000000000000000, 1.0000000000000000000000000000000
+000000]~
+? cmcurve=ellinit([0,-3/4,0,-2,-1])
+[0, -3/4, 0, -2, -1, -3, -4, -4, -1, 105, 1323, -343, -3375, Vecsmall([1]), 
+[Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? ellmul(cmcurve,[x,y],quadgen(-7))
+[((-2 + 3*w)*x^2 + (6 - w))/((-2 - 5*w)*x + (-4 - 2*w)), ((34 - 11*w)*y*x^2 
++ (40 - 28*w)*y*x + (22 + 23*w)*y)/((-90 - w)*x^2 + (-136 + 44*w)*x + (-40 +
+ 28*w))]
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 32
diff --git a/src/test/32/ellsea b/src/test/32/ellsea
new file mode 100644
index 0000000..d947038
--- /dev/null
+++ b/src/test/32/ellsea
@@ -0,0 +1,13 @@
+[x^6 + 30*x^5 + 315*x^4 + 1300*x^3 + 1575*x^2 + (-y + 750)*x + 125, 0]
+1: -18627161351017007203
+2: 18827282990304904850
+3: -311256626765211726406998
+4: -1156815323986765479761266
+5: 8021839135157401454666601928
+6: 69384671472347162238655401774
+7: -28652256072001057705168347198
+8: 1271547588042840381566950172346
+9: 1854715558584444
+10: 20420247695
+11: -4742075250
+Total time spent: 11668
diff --git a/src/test/32/ellweilpairing b/src/test/32/ellweilpairing
new file mode 100644
index 0000000..6f55675
--- /dev/null
+++ b/src/test/32/ellweilpairing
@@ -0,0 +1,296 @@
+[56, 46, [39, 3]]
+[63, 1, [18, 6]]
+[118, 118, [57, 3]]
+[144, 12, [12, 12]]
+[104, 1, [63, 3]]
+[108, 84, [57, 3]]
+[92, 1, [63, 3]]
+[98, 1, [16, 8]]
+[37, 136, [40, 4]]
+[148, 1, [72, 2]]
+[156, 28, [12, 12]]
+[4, 1, [5, 5]]
+[64, 108, [18, 6]]
+[107, 126, [18, 6]]
+[12, 12, [12, 12]]
+[178, 36, [14, 14]]
+[95, 98, [16, 8]]
+[148, 0, [72, 2]]
+[107, 37, [18, 6]]
+[104, 53, [63, 3]]
+[11, 7, [9, 3]]
+[92, 58, [63, 3]]
+[136, 0, [40, 4]]
+[156, 0, [30, 6]]
+[161, 36, [14, 14]]
+[144, 0, [30, 6]]
+[1, 0, [128]]
+[1, 0, [192]]
+[1, 1, [114]]
+[1, 1, [18, 6]]
+[156, 1, [12, 12]]
+[1, 1, [180]]
+[1, 1, [192]]
+[1, 3, [6]]
+t^5 + t^3 + t^2
+t^5 + t^4 + 1
+[0, 0, 0, 2, 0]:[2, 2]
+[0, 0, 1, 2, 2]:[2, 2]
+[0, 0, 2, 2, 2]:[2, 2]
+[1, 2, 0, 2, 0]:[2, 2]
+[1, 2, 1, 0, 2]:[2, 2]
+[1, 2, 2, 1, 2]:[2, 2]
+[2, 2, 0, 2, 0]:[2, 2]
+[2, 2, 1, 1, 2]:[2, 2]
+[2, 2, 2, 0, 2]:[2, 2]
+[0, 0, 0, 1, 0]:[2, 2]
+[0, 0, 0, 4, 0]:[4, 2]
+[0, 0, 1, 1, 1]:[2, 2]
+[0, 0, 1, 4, 1]:[4, 2]
+[0, 0, 2, 1, 4]:[2, 2]
+[0, 0, 2, 4, 4]:[4, 2]
+[0, 0, 3, 1, 4]:[2, 2]
+[0, 0, 3, 4, 4]:[4, 2]
+[0, 0, 4, 1, 1]:[2, 2]
+[0, 0, 4, 4, 1]:[4, 2]
+[0, 1, 0, 1, 1]:[4, 2]
+[0, 1, 0, 3, 0]:[2, 2]
+[0, 1, 1, 1, 2]:[4, 2]
+[0, 1, 1, 3, 1]:[2, 2]
+[0, 1, 2, 1, 0]:[4, 2]
+[0, 1, 2, 3, 4]:[2, 2]
+[0, 1, 3, 1, 0]:[4, 2]
+[0, 1, 3, 3, 4]:[2, 2]
+[0, 1, 4, 1, 2]:[4, 2]
+[0, 1, 4, 3, 1]:[2, 2]
+[0, 2, 0, 2, 0]:[4, 2]
+[0, 2, 0, 4, 3]:[2, 2]
+[0, 2, 1, 2, 1]:[4, 2]
+[0, 2, 1, 4, 4]:[2, 2]
+[0, 2, 2, 2, 4]:[4, 2]
+[0, 2, 2, 4, 2]:[2, 2]
+[0, 2, 3, 2, 4]:[4, 2]
+[0, 2, 3, 4, 2]:[2, 2]
+[0, 2, 4, 2, 1]:[4, 2]
+[0, 2, 4, 4, 4]:[2, 2]
+[0, 3, 0, 2, 0]:[4, 2]
+[0, 3, 0, 4, 2]:[2, 2]
+[0, 3, 1, 2, 1]:[4, 2]
+[0, 3, 1, 4, 3]:[2, 2]
+[0, 3, 2, 2, 4]:[4, 2]
+[0, 3, 2, 4, 1]:[2, 2]
+[0, 3, 3, 2, 4]:[4, 2]
+[0, 3, 3, 4, 1]:[2, 2]
+[0, 3, 4, 2, 1]:[4, 2]
+[0, 3, 4, 4, 3]:[2, 2]
+[0, 4, 0, 1, 4]:[4, 2]
+[0, 4, 0, 3, 0]:[2, 2]
+[0, 4, 1, 1, 0]:[4, 2]
+[0, 4, 1, 3, 1]:[2, 2]
+[0, 4, 2, 1, 3]:[4, 2]
+[0, 4, 2, 3, 4]:[2, 2]
+[0, 4, 3, 1, 3]:[4, 2]
+[0, 4, 3, 3, 4]:[2, 2]
+[0, 4, 4, 1, 0]:[4, 2]
+[0, 4, 4, 3, 1]:[2, 2]
+[1, 0, 0, 1, 4]:[4, 2]
+[1, 0, 0, 3, 0]:[2, 2]
+[1, 0, 1, 0, 1]:[2, 2]
+[1, 0, 1, 3, 0]:[4, 2]
+[1, 0, 2, 0, 3]:[4, 2]
+[1, 0, 2, 2, 4]:[2, 2]
+[1, 0, 3, 2, 3]:[4, 2]
+[1, 0, 3, 4, 4]:[2, 2]
+[1, 0, 4, 1, 1]:[2, 2]
+[1, 0, 4, 4, 0]:[4, 2]
+[1, 1, 0, 1, 0]:[2, 2]
+[1, 1, 0, 4, 0]:[4, 2]
+[1, 1, 1, 1, 1]:[4, 2]
+[1, 1, 1, 3, 1]:[2, 2]
+[1, 1, 2, 0, 4]:[2, 2]
+[1, 1, 2, 3, 4]:[4, 2]
+[1, 1, 3, 0, 4]:[4, 2]
+[1, 1, 3, 2, 4]:[2, 2]
+[1, 1, 4, 2, 1]:[4, 2]
+[1, 1, 4, 4, 1]:[2, 2]
+[1, 2, 0, 1, 1]:[4, 2]
+[1, 2, 0, 3, 0]:[2, 2]
+[1, 2, 1, 0, 1]:[2, 2]
+[1, 2, 1, 3, 2]:[4, 2]
+[1, 2, 2, 0, 0]:[4, 2]
+[1, 2, 2, 2, 4]:[2, 2]
+[1, 2, 3, 2, 0]:[4, 2]
+[1, 2, 3, 4, 4]:[2, 2]
+[1, 2, 4, 1, 1]:[2, 2]
+[1, 2, 4, 4, 2]:[4, 2]
+[1, 3, 0, 2, 0]:[4, 2]
+[1, 3, 0, 4, 3]:[2, 2]
+[1, 3, 1, 1, 4]:[2, 2]
+[1, 3, 1, 4, 1]:[4, 2]
+[1, 3, 2, 1, 4]:[4, 2]
+[1, 3, 2, 3, 2]:[2, 2]
+[1, 3, 3, 0, 2]:[2, 2]
+[1, 3, 3, 3, 4]:[4, 2]
+[1, 3, 4, 0, 1]:[4, 2]
+[1, 3, 4, 2, 4]:[2, 2]
+[1, 4, 0, 2, 0]:[4, 2]
+[1, 4, 0, 4, 2]:[2, 2]
+[1, 4, 1, 1, 3]:[2, 2]
+[1, 4, 1, 4, 1]:[4, 2]
+[1, 4, 2, 1, 4]:[4, 2]
+[1, 4, 2, 3, 1]:[2, 2]
+[1, 4, 3, 0, 1]:[2, 2]
+[1, 4, 3, 3, 4]:[4, 2]
+[1, 4, 4, 0, 1]:[4, 2]
+[1, 4, 4, 2, 3]:[2, 2]
+[2, 0, 0, 1, 1]:[4, 2]
+[2, 0, 0, 3, 0]:[2, 2]
+[2, 0, 1, 0, 2]:[4, 2]
+[2, 0, 1, 2, 1]:[2, 2]
+[2, 0, 2, 1, 4]:[2, 2]
+[2, 0, 2, 4, 0]:[4, 2]
+[2, 0, 3, 0, 4]:[2, 2]
+[2, 0, 3, 3, 0]:[4, 2]
+[2, 0, 4, 2, 2]:[4, 2]
+[2, 0, 4, 4, 1]:[2, 2]
+[2, 1, 0, 2, 0]:[4, 2]
+[2, 1, 0, 4, 3]:[2, 2]
+[2, 1, 1, 1, 1]:[4, 2]
+[2, 1, 1, 3, 4]:[2, 2]
+[2, 1, 2, 0, 4]:[4, 2]
+[2, 1, 2, 2, 2]:[2, 2]
+[2, 1, 3, 1, 2]:[2, 2]
+[2, 1, 3, 4, 4]:[4, 2]
+[2, 1, 4, 0, 4]:[2, 2]
+[2, 1, 4, 3, 1]:[4, 2]
+[2, 2, 0, 2, 0]:[4, 2]
+[2, 2, 0, 4, 2]:[2, 2]
+[2, 2, 1, 1, 1]:[4, 2]
+[2, 2, 1, 3, 3]:[2, 2]
+[2, 2, 2, 0, 4]:[4, 2]
+[2, 2, 2, 2, 1]:[2, 2]
+[2, 2, 3, 1, 1]:[2, 2]
+[2, 2, 3, 4, 4]:[4, 2]
+[2, 2, 4, 0, 3]:[2, 2]
+[2, 2, 4, 3, 1]:[4, 2]
+[2, 3, 0, 1, 4]:[4, 2]
+[2, 3, 0, 3, 0]:[2, 2]
+[2, 3, 1, 0, 0]:[4, 2]
+[2, 3, 1, 2, 1]:[2, 2]
+[2, 3, 2, 1, 4]:[2, 2]
+[2, 3, 2, 4, 3]:[4, 2]
+[2, 3, 3, 0, 4]:[2, 2]
+[2, 3, 3, 3, 3]:[4, 2]
+[2, 3, 4, 2, 0]:[4, 2]
+[2, 3, 4, 4, 1]:[2, 2]
+[2, 4, 0, 1, 0]:[2, 2]
+[2, 4, 0, 4, 0]:[4, 2]
+[2, 4, 1, 0, 1]:[2, 2]
+[2, 4, 1, 3, 1]:[4, 2]
+[2, 4, 2, 2, 4]:[4, 2]
+[2, 4, 2, 4, 4]:[2, 2]
+[2, 4, 3, 1, 4]:[4, 2]
+[2, 4, 3, 3, 4]:[2, 2]
+[2, 4, 4, 0, 1]:[4, 2]
+[2, 4, 4, 2, 1]:[2, 2]
+[3, 0, 0, 1, 1]:[4, 2]
+[3, 0, 0, 3, 0]:[2, 2]
+[3, 0, 1, 2, 2]:[4, 2]
+[3, 0, 1, 4, 1]:[2, 2]
+[3, 0, 2, 0, 4]:[2, 2]
+[3, 0, 2, 3, 0]:[4, 2]
+[3, 0, 3, 1, 4]:[2, 2]
+[3, 0, 3, 4, 0]:[4, 2]
+[3, 0, 4, 0, 2]:[4, 2]
+[3, 0, 4, 2, 1]:[2, 2]
+[3, 1, 0, 2, 0]:[4, 2]
+[3, 1, 0, 4, 3]:[2, 2]
+[3, 1, 1, 0, 4]:[2, 2]
+[3, 1, 1, 3, 1]:[4, 2]
+[3, 1, 2, 1, 2]:[2, 2]
+[3, 1, 2, 4, 4]:[4, 2]
+[3, 1, 3, 0, 4]:[4, 2]
+[3, 1, 3, 2, 2]:[2, 2]
+[3, 1, 4, 1, 1]:[4, 2]
+[3, 1, 4, 3, 4]:[2, 2]
+[3, 2, 0, 2, 0]:[4, 2]
+[3, 2, 0, 4, 2]:[2, 2]
+[3, 2, 1, 0, 3]:[2, 2]
+[3, 2, 1, 3, 1]:[4, 2]
+[3, 2, 2, 1, 1]:[2, 2]
+[3, 2, 2, 4, 4]:[4, 2]
+[3, 2, 3, 0, 4]:[4, 2]
+[3, 2, 3, 2, 1]:[2, 2]
+[3, 2, 4, 1, 1]:[4, 2]
+[3, 2, 4, 3, 3]:[2, 2]
+[3, 3, 0, 1, 4]:[4, 2]
+[3, 3, 0, 3, 0]:[2, 2]
+[3, 3, 1, 2, 0]:[4, 2]
+[3, 3, 1, 4, 1]:[2, 2]
+[3, 3, 2, 0, 4]:[2, 2]
+[3, 3, 2, 3, 3]:[4, 2]
+[3, 3, 3, 1, 4]:[2, 2]
+[3, 3, 3, 4, 3]:[4, 2]
+[3, 3, 4, 0, 0]:[4, 2]
+[3, 3, 4, 2, 1]:[2, 2]
+[3, 4, 0, 1, 0]:[2, 2]
+[3, 4, 0, 4, 0]:[4, 2]
+[3, 4, 1, 0, 1]:[4, 2]
+[3, 4, 1, 2, 1]:[2, 2]
+[3, 4, 2, 1, 4]:[4, 2]
+[3, 4, 2, 3, 4]:[2, 2]
+[3, 4, 3, 2, 4]:[4, 2]
+[3, 4, 3, 4, 4]:[2, 2]
+[3, 4, 4, 0, 1]:[2, 2]
+[3, 4, 4, 3, 1]:[4, 2]
+[4, 0, 0, 1, 4]:[4, 2]
+[4, 0, 0, 3, 0]:[2, 2]
+[4, 0, 1, 1, 1]:[2, 2]
+[4, 0, 1, 4, 0]:[4, 2]
+[4, 0, 2, 2, 3]:[4, 2]
+[4, 0, 2, 4, 4]:[2, 2]
+[4, 0, 3, 0, 3]:[4, 2]
+[4, 0, 3, 2, 4]:[2, 2]
+[4, 0, 4, 0, 1]:[2, 2]
+[4, 0, 4, 3, 0]:[4, 2]
+[4, 1, 0, 1, 0]:[2, 2]
+[4, 1, 0, 4, 0]:[4, 2]
+[4, 1, 1, 2, 1]:[4, 2]
+[4, 1, 1, 4, 1]:[2, 2]
+[4, 1, 2, 0, 4]:[4, 2]
+[4, 1, 2, 2, 4]:[2, 2]
+[4, 1, 3, 0, 4]:[2, 2]
+[4, 1, 3, 3, 4]:[4, 2]
+[4, 1, 4, 1, 1]:[4, 2]
+[4, 1, 4, 3, 1]:[2, 2]
+[4, 2, 0, 1, 1]:[4, 2]
+[4, 2, 0, 3, 0]:[2, 2]
+[4, 2, 1, 1, 1]:[2, 2]
+[4, 2, 1, 4, 2]:[4, 2]
+[4, 2, 2, 2, 0]:[4, 2]
+[4, 2, 2, 4, 4]:[2, 2]
+[4, 2, 3, 0, 0]:[4, 2]
+[4, 2, 3, 2, 4]:[2, 2]
+[4, 2, 4, 0, 1]:[2, 2]
+[4, 2, 4, 3, 2]:[4, 2]
+[4, 3, 0, 2, 0]:[4, 2]
+[4, 3, 0, 4, 3]:[2, 2]
+[4, 3, 1, 0, 1]:[4, 2]
+[4, 3, 1, 2, 4]:[2, 2]
+[4, 3, 2, 0, 2]:[2, 2]
+[4, 3, 2, 3, 4]:[4, 2]
+[4, 3, 3, 1, 4]:[4, 2]
+[4, 3, 3, 3, 2]:[2, 2]
+[4, 3, 4, 1, 4]:[2, 2]
+[4, 3, 4, 4, 1]:[4, 2]
+[4, 4, 0, 2, 0]:[4, 2]
+[4, 4, 0, 4, 2]:[2, 2]
+[4, 4, 1, 0, 1]:[4, 2]
+[4, 4, 1, 2, 3]:[2, 2]
+[4, 4, 2, 0, 1]:[2, 2]
+[4, 4, 2, 3, 4]:[4, 2]
+[4, 4, 3, 1, 4]:[4, 2]
+[4, 4, 3, 3, 1]:[2, 2]
+[4, 4, 4, 1, 3]:[2, 2]
+[4, 4, 4, 4, 1]:[4, 2]
+Total time spent: 40
diff --git a/src/test/32/env b/src/test/32/env
new file mode 100644
index 0000000..93fa06b
--- /dev/null
+++ b/src/test/32/env
@@ -0,0 +1,6 @@
+"/root"
+1
+"XXX, YYY, XXX"
+"XXX"
+0
+Total time spent: 0
diff --git a/src/test/32/equal b/src/test/32/equal
new file mode 100644
index 0000000..c4ed67d
--- /dev/null
+++ b/src/test/32/equal
@@ -0,0 +1,17 @@
+[1, 0, 1, 0]
+[0, 0]
+[1, 0]
+[1, 0]
+[0, 1]
+[0, 0, 1]
+[0, 0, 1]
+[0, 0, 1]
+[0, 0, 0, 0, 1]
+1
+0
+[0, 0, 0, 1]
+0
+[0, 1]
+[0, 0, 0, 1]
+[1, 1]
+Total time spent: 0
diff --git a/src/test/32/err b/src/test/32/err
new file mode 100644
index 0000000..62f7d17
--- /dev/null
+++ b/src/test/32/err
@@ -0,0 +1,590 @@
+  ***   at top-level: g(10)
+  ***                 ^-----
+  ***   in function g: for(i=-N,N,f(i))
+  ***                             ^-----
+  ***   in function f: 1/x
+  ***                   ^--
+  *** _/_: impossible inverse in gdiv: 0.
+
+  ***   at top-level: f(Mat(0),Col(1))
+  ***                 ^----------------
+  ***   in function f: matsolve
+  ***                  ^--------
+  *** matsolve: impossible inverse in gauss: Mat(0).
+
+  ***   at top-level: (matsolve)(Mat(0),Col
+  ***                  ^--------------------
+  *** matsolve: impossible inverse in gauss: Mat(0).
+
+  ***   at top-level: g(I)
+  ***                 ^----
+  ***   in function g: [x.foo]
+  ***                     ^----
+  ***   in member function foo: 1/(1+a^2)
+  ***                            ^--------
+  *** _/_: impossible inverse in gdiv: 0.
+
+  ***   at top-level: (x->1/x)(0)
+  ***                  ^----------
+  ***   in anonymous function: 1/x
+  ***                           ^--
+  *** _/_: impossible inverse in gdiv: 0.
+
+274177
+274177
+  ***   at top-level: ...=2;A=x^2+1;B=[x+t,x+t];r=polhensellift(A,B,[p
+  ***                                             ^--------------------
+  *** polhensellift: elements not coprime in BuildTree:
+    x + t
+    x + t
+  ***   at top-level: M[,1]=1
+  ***                 ^-------
+  ***   incorrect type in matrix col assignment (t_INT).
+  ***   at top-level: M[,3]=1
+  ***                  ^------
+  ***   non-existent component: index > 2
+  ***   at top-level: M[,1]=[1,2]
+  ***                 ^-----------
+  ***   incorrect type in matrix col assignment (t_VEC).
+  ***   at top-level: M[,1]=[1,2,3]~
+  ***                 ^--------------
+  ***   inconsistent dimensions in matrix col assignment.
+  ***   at top-level: M[1,]=1
+  ***                 ^-------
+  ***   incorrect type in matrix row assignment (t_INT).
+  ***   at top-level: M[3,]=1
+  ***                  ^------
+  ***   non-existent component: index > 2
+  ***   at top-level: M[1,]=[1,2]~
+  ***                 ^------------
+  ***   incorrect type in matrix row assignment (t_COL).
+  ***   at top-level: M[1,]=[1,2,3]
+  ***                 ^-------------
+  ***   inconsistent dimensions in matrix row assignment.
+  ***   at top-level: [;][1,]
+  ***                    ^----
+  ***   non-existent component: index > 0
+  ***   at top-level: [;][,1]
+  ***                    ^----
+  ***   non-existent component: index > 0
+  ***   at top-level: 1[1]
+  ***                  ^---
+  ***   incorrect type in _[_] OCcompo1 [not a vector] (t_INT).
+  ***   at top-level: issquare(1,&v[1])
+  ***                              ^----
+  ***   incorrect type in &_[_] OCcompo1ptr [not a vector] (t_POL).
+  ***   at top-level: 1[1,1]
+  ***                  ^-----
+  ***   incorrect type in _[_,_] OCcompo2 [not a matrix] (t_INT).
+  ***   at top-level: 1[,1]
+  ***                  ^----
+  ***   incorrect type in _[,_] OCcompoC [not a matrix] (t_INT).
+  ***   at top-level: issquare(1,&v[,1])
+  ***                              ^-----
+  ***   incorrect type in &_[,_] OCcompoCptr [not a matrix] (t_POL).
+  ***   at top-level: 1[1,]
+  ***                  ^----
+  ***   incorrect type in _[_,] OCcompoL [not a matrix] (t_INT).
+  ***   at top-level: issquare(1,&v[1,])
+  ***                              ^-----
+  ***   incorrect type in &_[_,] OCcompoLptr [not a matrix] (t_POL).
+  ***   at top-level: v[2]=1
+  ***                  ^-----
+  ***   non-existent component: index > 1
+  ***   at top-level: v[1]=Pi
+  ***                 ^-------
+  ***   incorrect type in t_VECSMALL assignment (t_REAL).
+  ***   at top-level: v[1]=2^64
+  ***                 ^---------
+  ***   incorrect type in t_VECSMALL assignment (t_INT).
+  ***   at top-level: v[Pi]=1
+  ***                   ^-----
+  ***   incorrect type in gtos [integer expected] (t_REAL).
+  ***   at top-level: M=[1.,0;0,1];qflll(M,1)
+  ***                              ^----------
+  *** qflll: incorrect type in qflll [integer matrix] (t_MAT).
+  ***   at top-level: addprimes(1.)
+  ***                 ^-------------
+  *** addprimes: incorrect type in addprimes [integer vector] (t_VEC).
+  ***   at top-level: nfalgtobasis(nfinit(
+  ***                 ^--------------------
+  *** nfalgtobasis: inconsistent moduli in algtobasis: t^3 - 2 != t^2 + 1
+  ***   at top-level: vector(-1,i,0)
+  ***                 ^--------------
+  *** vector: domain error in vector: dimension < 0
+  ***   at top-level: vectorv(-1,i,0)
+  ***                 ^---------------
+  *** vectorv: domain error in vector: dimension < 0
+  ***   at top-level: vectorsmall(-1,i,0)
+  ***                 ^-------------------
+  *** vectorsmall: domain error in vectorsmall: dimension < 0
+  ***   at top-level: matrix(-1,1,i,j,0)
+  ***                 ^------------------
+  *** matrix: domain error in matrix: nbrows < 0
+  ***   at top-level: matrix(1,-1,i,j,0)
+  ***                 ^------------------
+  *** matrix: domain error in matrix: nbcols < 0
+  ***   at top-level: next(-1)
+  ***                 ^--------
+  *** next: domain error in next: n < 1
+  ***   at top-level: break(-1)
+  ***                 ^---------
+  *** break: domain error in break: n < 1
+  ***   at top-level: v[-1]
+  ***                  ^----
+  ***   non-existent component: index < 1
+  ***   at top-level: v[#v+1]
+  ***                  ^------
+  ***   non-existent component: index > 1
+  ***   variable name expected: subst(x,1,0)
+  ***                                   ^----
+  ***   at top-level: exp(1/x)
+  ***                 ^--------
+  *** exp: domain error in exp: valuation < 0
+  ***   at top-level: cos(1/x)
+  ***                 ^--------
+  *** cos: domain error in cos: valuation < 0
+  ***   at top-level: sin(1/x)
+  ***                 ^--------
+  *** sin: domain error in sin: valuation < 0
+  ***   at top-level: tan(1/x)
+  ***                 ^--------
+  *** tan: domain error in tan: valuation < 0
+  ***   at top-level: cotan(1/x)
+  ***                 ^----------
+  *** cotan: domain error in cotan: valuation < 0
+  ***   at top-level: atan(1/x)
+  ***                 ^---------
+  *** atan: domain error in atan: valuation < 0
+  ***   at top-level: asin(1/x)
+  ***                 ^---------
+  *** asin: domain error in asin: valuation < 0
+  ***   at top-level: acos(1/x)
+  ***                 ^---------
+  *** acos: domain error in acos: valuation < 0
+  ***   at top-level: asinh(1/x)
+  ***                 ^----------
+  *** asinh: domain error in asinh: valuation < 0
+  ***   at top-level: acosh(1/x)
+  ***                 ^----------
+  *** acosh: domain error in acosh: valuation < 0
+  ***   at top-level: atanh(1/x)
+  ***                 ^----------
+  *** atanh: domain error in atanh: valuation < 0
+  ***   at top-level: lngamma(x)
+  ***                 ^----------
+  *** lngamma: domain error in lngamma: valuation != 0
+  ***   at top-level: besselj(2,1/x)
+  ***                 ^--------------
+  *** besselj: domain error in besselj: valuation < 0
+  ***   at top-level: besseljh(2,1/x)
+  ***                 ^---------------
+  *** besseljh: domain error in besseljh: valuation < 0
+  ***   at top-level: besselk(2,1/x)
+  ***                 ^--------------
+  *** besselk: domain error in _kbessel1: valuation < 0
+  ***   at top-level: besselk(1/3,O(x))
+  ***                 ^-----------------
+  *** besselk: domain error in besselk: 2n mod Z != 0
+  ***   at top-level: besseln(2,1/x)
+  ***                 ^--------------
+  *** besseln: domain error in _kbessel1: valuation < 0
+  ***   at top-level: besseln(1/3,O(x))
+  ***                 ^-----------------
+  *** besseln: domain error in besseln: 2n mod Z != 0
+  ***   at top-level: polylog(2,1/x)
+  ***                 ^--------------
+  *** polylog: domain error in polylog: valuation < 0
+  ***   at top-level: sqrt(x)
+  ***                 ^-------
+  *** sqrt: domain error in sqrtn: valuation != Mod(0, 2)
+  ***   at top-level: sqrt(2+O(2^2))
+  ***                 ^--------------
+  *** sqrt: not an n-th power residue in Qp_sqrt: 2 + O(2^2).
+  ***   at top-level: sqrtn(x,3)
+  ***                 ^----------
+  *** sqrtn: domain error in sqrtn: valuation != Mod(0, 3)
+  ***   at top-level: sqrtn(2+O(2^2),3)
+  ***                 ^-----------------
+  *** sqrtn: not an n-th power residue in gsqrtn: 2 + O(2^2).
+  ***   at top-level: log(x)
+  ***                 ^------
+  *** log: domain error in log: series valuation != 0
+  ***   at top-level: log(0)
+  ***                 ^------
+  *** log: domain error in log: argument = 0
+  ***   at top-level: abs(x+O(x^2))
+  ***                 ^-------------
+  *** abs: domain error in abs: series valuation != 0
+  ***   at top-level: vecmax([])
+  ***                 ^----------
+  *** vecmax: domain error in vecindexmax: empty argument = []
+  ***   at top-level: vecmax([],&i)
+  ***                 ^-------------
+  *** vecmax: domain error in vecindexmax: empty argument = []
+  ***   at top-level: vecmin([])
+  ***                 ^----------
+  *** vecmin: domain error in vecindexmin: empty argument = []
+  ***   unexpected character '&': vecmmin([],&i)
+  ***                                        ^---
+  ***   at top-level: vecmax(matrix(0,2))
+  ***                 ^-------------------
+  *** vecmax: domain error in vecmax: empty argument = matrix(0,2)
+  ***   at top-level: listput(L,x,-1)
+  ***                 ^---------------
+  *** listput: non-existent component in listput: index < 0
+  ***   at top-level: listinsert(L,x,-1)
+  ***                 ^------------------
+  *** listinsert: non-existent component in listinsert: index <= 0
+  ***   at top-level: listinsert(L,x,10)
+  ***                 ^------------------
+  *** listinsert: non-existent component in listinsert: index > 1
+  ***   at top-level: ellj(Mod(1,2))
+  ***                 ^--------------
+  *** ellj: incorrect type in modular function (t_INTMOD).
+  ***   at top-level: ellj(Qfb(1,1,1))
+  ***                 ^----------------
+  *** ellj: incorrect type in ellj (t_QFI).
+  ***   at top-level: eta(1+O(2))
+  ***                 ^-----------
+  *** eta: domain error in eta: v_p(q) <= 0
+  ***   at top-level: eta(1/x)
+  ***                 ^--------
+  *** eta: domain error in eta: v_p(q) <= 0
+  ***   at top-level: idealhnf(K,Qfb(1,1,1
+  ***                 ^--------------------
+  *** idealhnf: domain error in idealhnf [Qfb]: disc(q) != -4
+  ***   at top-level: idealfactor(K,[;])
+  ***                 ^------------------
+  *** idealfactor: domain error in idealfactor: ideal = 0
+  ***   at top-level: idealdiv(K,2,0,1)
+  ***                 ^-----------------
+  *** idealdiv: impossible inverse in idealdivexact: 0.
+  ***   at top-level: valuation(Pi,2)
+  ***                 ^---------------
+  *** valuation: inconsistent valuation t_REAL , t_INT.
+  ***   at top-level: x^Pi
+  ***                  ^---
+  *** _^_: domain error in gpow [irrational exponent]: valuation != 0
+  ***   at top-level: x^x
+  ***                  ^--
+  *** _^_: domain error in gpow [irrational exponent]: valuation != 0
+  ***   at top-level: 0^0.
+  ***                  ^---
+  *** _^_: domain error in gpow(0,n): n <= 0
+  ***   at top-level: agm([],[])
+  ***                 ^----------
+  *** agm: forbidden agm t_VEC (0 elts) , t_VEC (0 elts).
+  ***   at top-level: sin(1/2+O(2^1))
+  ***                 ^---------------
+  *** sin: domain error in gsin(t_PADIC): argument out of range
+  ***   at top-level: cos(1/9+O(3^1))
+  ***                 ^---------------
+  *** cos: domain error in gcos(t_PADIC): argument out of range
+  ***   at top-level: exp(1/9+O(3^1))
+  ***                 ^---------------
+  *** exp: domain error in gexp(t_PADIC): argument out of range
+  ***   at top-level: ...],Vecsmall([2,2,2,2,3])];galoissubgroups(G)
+  ***                                             ^------------------
+  *** galoissubgroups: sorry, group_quotient for a non-WSS group is not yet implemented.
+  ***   at top-level: bnrstark(bnrinit(bnf
+  ***                 ^--------------------
+  *** bnrstark: domain error in bnrstark: r2 != 0
+  ***   at top-level: bnrstark(bnrinit(bnf
+  ***                 ^--------------------
+  *** bnrstark: domain error in bnrstark: r2(class field) != 0
+  ***   at top-level: quadray(-16,1)
+  ***                 ^--------------
+  *** quadray: domain error in quadray: isfundamental(D) = 0
+  ***   at top-level: quadray(bnfinit(x^3-
+  ***                 ^--------------------
+  *** quadray: domain error in quadray: degree != 2
+  ***   at top-level: galoissubcyclo(-1)
+  ***                 ^------------------
+  *** galoissubcyclo: domain error in galoissubcyclo: degree <= 0
+  ***   at top-level: galoissubcyclo(6,Mod
+  ***                 ^--------------------
+  *** galoissubcyclo: inconsistent moduli in galoissubcyclo: 6 != 3
+  ***   at top-level: galoissubcyclo(6,[;]
+  ***                 ^--------------------
+  *** galoissubcyclo: incorrect type in galoissubcyclo [H not in HNF] (t_MAT).
+  ***   at top-level: galoissubcyclo(6,Mat
+  ***                 ^--------------------
+  *** galoissubcyclo: incorrect type in galoissubcyclo [N not a bnrinit or znstar] (t_MAT).
+  ***   at top-level: galoissubcyclo(znsta
+  ***                 ^--------------------
+  *** galoissubcyclo: inconsistent dimensions in galoissubcyclo.
+  ***   at top-level: galoissubcyclo(bnrin
+  ***                 ^--------------------
+  *** galoissubcyclo: domain error in bnr_to_znstar: bnr != Q
+  ***   at top-level: polsubcyclo(-1,2)
+  ***                 ^-----------------
+  *** polsubcyclo: domain error in polsubcyclo: n <= 0
+  ***   at top-level: polsubcyclo(2,-1)
+  ***                 ^-----------------
+  *** polsubcyclo: domain error in polsubcyclo: d <= 0
+  ***   at top-level: random(-1)
+  ***                 ^----------
+  *** random: domain error in random: N <= 0
+  ***   at top-level: znprimroot(0)
+  ***                 ^-------------
+  *** znprimroot: domain error in znprimroot: argument = 0
+  ***   at top-level: sqrtint(-1)
+  ***                 ^-----------
+  *** sqrtint: domain error in sqrtint: argument < 0
+  ***   at top-level: sqrtnint(-1,2)
+  ***                 ^--------------
+  *** sqrtnint: domain error in sqrtnint: x < 0
+  ***   at top-level: sqrtnint(2,-2)
+  ***                 ^--------------
+  *** sqrtnint: domain error in sqrtnint: n <= 0
+  ***   at top-level: znprimroot(8)
+  ***                 ^-------------
+  *** znprimroot: domain error in znprimroot: argument = 8
+  ***   at top-level: polroots(x^2+Mod(1,2
+  ***                 ^--------------------
+  *** polroots: incorrect type in roots (t_INTMOD).
+  ***   at top-level: prime(-2)
+  ***                 ^---------
+  *** prime: domain error in prime: n <= 0
+  ***   at top-level: addprimes(-1)
+  ***                 ^-------------
+  *** addprimes: domain error in addprimes: p < 2
+  ***   at top-level: padicappr(x^2+1+O(3^
+  ***                 ^--------------------
+  *** padicappr: inconsistent moduli in Zp_to_Z: 5 != 3
+  ***   at top-level: factorpadic(x^2+1,2,
+  ***                 ^--------------------
+  *** factorpadic: domain error in factorpadic: precision <= 0
+  ***   at top-level: polrootspadic(x^2+1,
+  ***                 ^--------------------
+  *** polrootspadic: domain error in rootpadic: precision <= 0
+  ***   at top-level: ellinit([1+O(3),1+O(
+  ***                 ^--------------------
+  *** ellinit: inconsistent moduli in ellinit: 3 != 5
+  ***   at top-level: ellwp([1,I],I)
+  ***                 ^--------------
+  *** ellwp: domain error in ellwp: argument = 0
+  ***   at top-level: ellsigma([1,I],x,1)
+  ***                 ^-------------------
+  *** ellsigma: incorrect type in log(ellsigma) (t_SER).
+  ***   at top-level: ellsigma([1,I],1,1)
+  ***                 ^-------------------
+  *** ellsigma: domain error in log(ellsigma): argument = 0
+  ***   at top-level: ellap(E)
+  ***                 ^--------
+  *** ellap: incorrect type in ellap [can't determine p] (t_VEC).
+  ***   at top-level: ellap(E,1)
+  ***                 ^----------
+  *** ellap: domain error in ellap: p < 2
+  ***   at top-level: ellap(E,'x)
+  ***                 ^-----------
+  *** ellap: incorrect type in ellap (t_POL).
+  ***   at top-level: elltaniyama(E,-1)
+  ***                 ^-----------------
+  *** elltaniyama: domain error in elltaniyama: precision < 0
+  ***   at top-level: ellheight(E,[2,2])
+  ***                 ^------------------
+  *** ellheight: domain error in ellheight: point not on E
+  ***   at top-level: Qfb(0,0,0)
+  ***                 ^----------
+  *** Qfb: domain error in Qfb: issquare(disc) = 1
+  ***   at top-level: quadpoly(2)
+  ***                 ^-----------
+  *** quadpoly: domain error in quadpoly: disc % 4 > 1
+  ***   at top-level: qfbprimeform(2,5)
+  ***                 ^-----------------
+  *** qfbprimeform: domain error in primeform: disc % 4 > 1
+  ***   at top-level: qfbcomp(Qfb(1,1,1),Q
+  ***                 ^--------------------
+  ***   not a function in function call
+  ***   at top-level: galoisinit(x^2)
+  ***                 ^---------------
+  *** galoisinit: domain error in galoisinit: issquarefree(pol) = 0
+  ***   at top-level: galoisinit(2*x)
+  ***                 ^---------------
+  *** galoisinit: sorry, galoisinit(non-monic) is not yet implemented.
+  ***   at top-level: ellL1(1,-1)
+  ***                 ^-----------
+  *** ellL1: domain error in ellL1: derivative order < 0
+  ***   at top-level: ellheegner(ellinit([
+  ***                 ^--------------------
+  *** ellheegner: domain error in ellheegner: (analytic rank)%2 = 0
+  ***   at top-level: ellheegner(ellinit([
+  ***                 ^--------------------
+  *** ellheegner: domain error in ellheegner: analytic rank > 1
+  ***   at top-level: substpol(x+O(x^2),x^
+  ***                 ^--------------------
+  *** substpol: domain error in gdeflate: valuation(x) % 3 != 0
+  ***   at top-level: intformal(1/(x^2+1))
+  ***                 ^--------------------
+  *** intformal: domain error in intformal: residue(series, pole) != 0
+  ***   at top-level: component(x,-1)
+  ***                 ^---------------
+  *** component: non-existent component: index < 1
+  ***   at top-level: component(O(x),2)
+  ***                 ^-----------------
+  *** component: non-existent component: index > 1
+  ***   at top-level: component(Vecsmall([
+  ***                 ^--------------------
+  *** component: non-existent component: index > 0
+  ***   at top-level: polcoeff(O(x),2)
+  ***                 ^----------------
+  *** polcoeff: domain error in polcoeff: t_SER = O(x)
+  ***   at top-level: polcoeff(x+O(x^2),2)
+  ***                 ^--------------------
+  *** polcoeff: domain error in polcoeff: degree > 1
+  ***   at top-level: polcoeff([],2)
+  ***                 ^--------------
+  *** polcoeff: non-existent component in polcoeff: index > 0
+  ***   at top-level: polcoeff([],-1)
+  ***                 ^---------------
+  *** polcoeff: non-existent component in polcoeff: index < 1
+  ***   at top-level: polcoeff("",2)
+  ***                 ^--------------
+  *** polcoeff: incorrect type in polcoeff (t_STR).
+  ***   at top-level: matcompanion(0*x)
+  ***                 ^-----------------
+  *** matcompanion: domain error in matcompanion: polynomial = 0
+  ***   at top-level: matrixqz(Mat([1,2]))
+  ***                 ^--------------------
+  *** matrixqz: domain error in QM_minors_coprime: n > m
+  ***   at top-level: matrixqz(Mat(0))
+  ***                 ^----------------
+  *** matrixqz: domain error in QM_minors_coprime: rank(A) < 1
+  ***   at top-level: vecextract([1],[-1])
+  ***                 ^--------------------
+  *** vecextract: non-existent component in vecextract: index <= 0
+  ***   at top-level: vecextract([1],[2])
+  ***                 ^-------------------
+  *** vecextract: non-existent component in vecextract: index >= 2
+  ***   at top-level: idealfrobenius(K,gal
+  ***                 ^--------------------
+  *** idealfrobenius: domain error in idealfrobenius: pr.e > 1
+  ***   at top-level: nfisincl(x^2,x^2)
+  ***                 ^-----------------
+  *** nfisincl: not an irreducible polynomial in nfisincl: x^2.
+  ***   at top-level: polcompositum(x^2,x)
+  ***                 ^--------------------
+  *** polcompositum: domain error in polcompositum: issquarefree(arg) = 0
+  ***   at top-level: rnfdedekind(K,x^2+x-
+  ***                 ^--------------------
+  *** rnfdedekind: incorrect type in rnfdedekind [non integral pol] (t_POL).
+  ***   at top-level: hilbert(Mod(1,2),1)
+  ***                 ^-------------------
+  *** hilbert: precision too low in hilbert.
+  ***   at top-level: hilbert(Mod(1,3),Mod
+  ***                 ^--------------------
+  *** hilbert: inconsistent moduli in hilbert: 5 != 3
+  ***   at top-level: hilbert(Mod(1,3),2,0
+  ***                 ^--------------------
+  *** hilbert: inconsistent moduli in hilbert: 3 != oo
+  ***   at top-level: znorder(0)
+  ***                 ^----------
+  *** znorder: incorrect type in znorder [t_INTMOD expected] (t_INT).
+  ***   at top-level: znorder(Mod(2,4))
+  ***                 ^-----------------
+  *** znorder: elements not coprime in znorder:
+    2
+    4
+  ***   at top-level: contfrac(1e100)
+  ***                 ^---------------
+  *** contfrac: precision too low in gboundcf.
+  ***   at top-level: contfrac(1.,[1],10)
+  ***                 ^-------------------
+  *** contfrac: inconsistent dimensions in contfrac [too few denominators].
+  ***   at top-level: contfrac(1,,-1)
+  ***                 ^---------------
+  *** contfrac: domain error in gboundcf: nmax < 0
+  ***   at top-level: contfracpnqn(matrix(
+  ***                 ^--------------------
+  *** contfracpnqn: inconsistent dimensions in pnqn [ nbrows != 1,2 ].
+  ***   at top-level: divisors(1/2)
+  ***                 ^-------------
+  *** divisors: incorrect type in divisors [denominator] (t_FRAC).
+  ***   at top-level: idealstar(K,0)
+  ***                 ^--------------
+  *** idealstar: domain error in Idealstar: ideal = 0
+  ***   at top-level: idealstar(K,1/2)
+  ***                 ^----------------
+  *** idealstar: domain error in Idealstar: denominator(ideal) != 1
+  ***   at top-level: idealaddtoone(K,[[;]
+  ***                 ^--------------------
+  *** idealaddtoone: domain error in idealaddmultoone: sum(ideals) != 1
+  ***   at top-level: idealdiv(K,1,2,1)
+  ***                 ^-----------------
+  *** idealdiv: domain error in idealdivexact: denominator(x/y) != 1
+  ***   at top-level: idealred(K,matid(2),
+  ***                 ^--------------------
+  *** idealred: inconsistent dimensions in idealred.
+  ***   at top-level: idealtwoelt(K,matid(
+  ***                 ^--------------------
+  *** idealtwoelt: domain error in idealtwoelt2: element mod ideal != 0
+  ***   at top-level: rnf=rnfinit(K,x^2-y);rnfeltdown(rnf,x)
+  ***                                      ^-----------------
+  *** rnfeltdown: domain error in rnfeltdown: element not in the base field
+  ***   at top-level: matid(-1)
+  ***                 ^---------
+  *** matid: domain error in matid: size < 0
+  ***   at top-level: polinterpolate([1,1]
+  ***                 ^--------------------
+  *** polinterpolate: domain error in polinterpolate: X[1] = X[2]
+  ***   at top-level: modreverse(Mod(-x^3+
+  ***                 ^--------------------
+  *** modreverse: domain error in modreverse: deg(minpoly(z)) < 4
+  ***   at top-level: rnfnormgroup(bnrinit
+  ***                 ^--------------------
+  *** rnfnormgroup: domain error in rnfnormgroup: rnfisabelian(bnr,pol) = 0
+  ***   at top-level: concat([1,2],[3,4]~)
+  ***                 ^--------------------
+  *** concat: inconsistent concatenation t_VEC (2 elts) , t_COL (2 elts).
+  ***   at top-level: concat([])
+  ***                 ^----------
+  *** concat: domain error in concat: vector = []
+  ***   at top-level: concat(List())
+  ***                 ^--------------
+  *** concat: domain error in concat: vector = List([])
+  ***   at top-level: mathnfmod([1;2],2)
+  ***                 ^------------------
+  *** mathnfmod: domain error in ZM_hnfmod: nb lines > nb columns
+  ***   at top-level: polsturm(x^2)
+  ***                 ^-------------
+  *** polsturm: domain error in polsturm: issquarefree(pol) = 0
+  ***   at top-level: removeprimes(2)
+  ***                 ^---------------
+  *** removeprimes: domain error in removeprime: prime not in primetable
+  ***   at top-level: forstep(a=1,2,0,)
+  ***                 ^-----------------
+  ***   domain error in forstep: step = 0
+  ***   at top-level: e.omega
+  ***                   ^-----
+  *** _.omega: incorrect type in omega [not defined over C] (t_VEC).
+  ***   at top-level: e.eta
+  ***                   ^---
+  *** _.eta: incorrect type in eta [not defined over C] (t_VEC).
+  ***   at top-level: e.area
+  ***                   ^----
+  *** _.area: incorrect type in area [not defined over C] (t_VEC).
+  ***   at top-level: e.tate
+  ***                   ^----
+  *** _.tate: incorrect type in tate [not defined over Qp] (t_VEC).
+  ***   at top-level: ellorder(e,[0,0]*Mod
+  ***                 ^--------------------
+  *** ellorder: sorry, ellorder for curve with singular reduction is not yet implemented.
+  ***   at top-level: thue(x*(x^3-2),0)
+  ***                 ^-----------------
+  *** thue: domain error in thue: #sols = oo
+  ***   at top-level: direuler(p=2,10,2/(1-p*X))
+  ***                                 ^----------
+  ***   domain error in direuler: constant term != 1
+  ***   at top-level: solve(x=0,1,x^2+1)
+  ***                             ^------
+  ***   domain error in solve: f(a)f(b) > 0
+  ***   user warning: 1
+(x)->trap(e_INV,INFINITY,1/x)
+1/2
+INFINITY
+  ***   at top-level: trap(e_INV,INFINITY,log(0))
+  ***                                     ^-------
+  *** log: domain error in log: argument = 0
+Total time spent: 20
diff --git a/src/test/32/exact0 b/src/test/32/exact0
new file mode 100644
index 0000000..c2619ab
--- /dev/null
+++ b/src/test/32/exact0
@@ -0,0 +1,24 @@
+  ***   at top-level: Mod(0,2)*x*1.
+  ***                           ^---
+  *** _*_: forbidden multiplication t_REAL * t_INTMOD.
+Mod(0, 2)
+Mod(0, 2)
+Mod(0, 2)*x^15 + O(x^16)
+Mod(2, 4) + Mod(0, 4)*I
+Mod(0, 4) + Mod(0, 4)*I
+Mod(1, 2) + Mod(0, 2)*I
+Mod(0, 2)
+Mod(0, 2)
+Mod(0, 2)
+1
+Mod(0, 5)
+Mod(0, 5)
+0 0 O(x^4) 0 0 O(x^4) 
+Mod(0, 2) Mod(0, 2) Mod(0, 2)*x^3 + O(x^4) Mod(0, 2) Mod(0, 2) Mod(0, 2)*x^3
+ + O(x^4) 
+x^3 1 1 + O(x^4) 1 x^3 x^3 + O(x^4) 
+x^3 + 0.E-38*x 0.E-38*x^2 + 1 1 + 0.E-38*x^2 + O(x^4) 0.E-38*x^2 + 1 x^3 + 0
+.E-38*x 0.E-38*x + x^3 + O(x^4) 
+x^3 + 0.E-38 0.E-38*x^3 + 1 1 + 0.E-38*x^3 + O(x^4) 0.E-38*x^3 + 1 x^3 + 0.E
+-38 0.E-38 + x^3 + O(x^4) 
+Total time spent: 8
diff --git a/src/test/32/extract b/src/test/32/extract
new file mode 100644
index 0000000..9ba9ea6
--- /dev/null
+++ b/src/test/32/extract
@@ -0,0 +1,308 @@
+[36][37][38][39][40][41][42][43][44][45][46][47][48][49][50][51][52][53][54]
+[55][56][57][58][59][60][61][62][63][64][65][66][67][68][69][70][71][72][73]
+[74][75][76][77][78][79][80][81][82][83][84][85][86][87][88][89][90][91][92]
+[93][94][95][96][97][98][99][100]
+[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
+[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
+[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
+[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
+[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
+[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
+[2, 3, 4]
+[99, 98, 97]
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 
+22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 
+41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 
+60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 
+80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 
+99, 100]
+[1, 100]
+[100, 1]
+[3, 5]
+
+[0 1 0]
+
+[0 0 1]
+
+
+[0]
+
+[1]
+
+[0, 2]
+[0, 2]
+[0, 0]
+[0, 0]
+[0, 0]
+[0, 0]
+[1]
+[1, 0]
+[1/x]
+[5, 4, 3, 2, 1]
+[1, 2]
+[1, 2]
+[1, 2]
+[1, 2, 3]
+[1, 2, 3, 4, 5]
+["a", "b"]
+[[1, 3]~, [2, 4]~, [3, 5]~]
+["e_INV", "gdiv", 0]
+[1, 0, 0, 0]
+[1, 0, 0, 0]
+[1/x, 0, 0, 0]
+[5, 4, 3, 2]
+[1, 2, 0, 0]
+[1, 2, 0, 0]
+[1, 2, 0, 0]
+[1, 2, 3, 0]
+[1, 2, 3, 4]
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[0, 0, 0, 1]
+[0, 0, 1, 0]
+[0, 0, 0, 1/x]
+[5, 4, 3, 2]
+[0, 0, 1, 2]
+[0, 0, 1, 2]
+[0, 0, 1, 2]
+[0, 1, 2, 3]
+[1, 2, 3, 4]
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[1]
+[0, 1]
+[1/x]
+[1, 2, 3, 4, 5]
+[2, 1]
+[2, 1]
+[2, 1]
+[3, 2, 1]
+[5, 4, 3, 2, 1]
+["b", "a"]
+[[3, 5]~, [2, 4]~, [1, 3]~]
+[0, "gdiv", "e_INV"]
+[0, 0, 0, 1]
+[0, 0, 0, 1]
+[0, 0, 0, 1/x]
+[2, 3, 4, 5]
+[0, 0, 2, 1]
+[0, 0, 2, 1]
+[0, 0, 2, 1]
+[0, 3, 2, 1]
+[4, 3, 2, 1]
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[1, 0, 0, 0]
+[0, 1, 0, 0]
+[1/x, 0, 0, 0]
+[2, 3, 4, 5]
+[2, 1, 0, 0]
+[2, 1, 0, 0]
+[2, 1, 0, 0]
+[3, 2, 1, 0]
+[4, 3, 2, 1]
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[1]~
+[1, 0]~
+[1/x]~
+[5, 4, 3, 2, 1]~
+[1, 2]~
+[1, 2]~
+[1, 2]~
+[1, 2, 3]~
+[1, 2, 3, 4, 5]~
+["a", "b"]~
+[[1, 2, 3], [3, 4, 5]]~
+["e_INV", "gdiv", 0]~
+[1, 0, 0, 0]~
+[1, 0, 0, 0]~
+[1/x, 0, 0, 0]~
+[5, 4, 3, 2]~
+[1, 2, 0, 0]~
+[1, 2, 0, 0]~
+[1, 2, 0, 0]~
+[1, 2, 3, 0]~
+[1, 2, 3, 4]~
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[0, 0, 0, 1]~
+[0, 0, 1, 0]~
+[0, 0, 0, 1/x]~
+[5, 4, 3, 2]~
+[0, 0, 1, 2]~
+[0, 0, 1, 2]~
+[0, 0, 1, 2]~
+[0, 1, 2, 3]~
+[1, 2, 3, 4]~
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[1]~
+[0, 1]~
+[1/x]~
+[1, 2, 3, 4, 5]~
+[2, 1]~
+[2, 1]~
+[2, 1]~
+[3, 2, 1]~
+[5, 4, 3, 2, 1]~
+["b", "a"]~
+[[3, 4, 5], [1, 2, 3]]~
+[0, "gdiv", "e_INV"]~
+[0, 0, 0, 1]~
+[0, 0, 0, 1]~
+[0, 0, 0, 1/x]~
+[2, 3, 4, 5]~
+[0, 0, 2, 1]~
+[0, 0, 2, 1]~
+[0, 0, 2, 1]~
+[0, 3, 2, 1]~
+[4, 3, 2, 1]~
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+[1, 0, 0, 0]~
+[0, 1, 0, 0]~
+[1/x, 0, 0, 0]~
+[2, 3, 4, 5]~
+[2, 1, 0, 0]~
+[2, 1, 0, 0]~
+[2, 1, 0, 0]~
+[3, 2, 1, 0]~
+[4, 3, 2, 1]~
+error("incorrect type in gtovec (t_STR).")
+error("incorrect type in gtovec (t_MAT).")
+error("incorrect type in gtovec (t_ERROR).")
+Vecsmall([1])
+Vecsmall([1, 0])
+error("incorrect type in vectosmall (t_RFRAC).")
+Vecsmall([5, 4, 3, 2, 1])
+Vecsmall([1, 2])
+Vecsmall([1, 2])
+Vecsmall([1, 2])
+Vecsmall([1, 2, 3])
+Vecsmall([1, 2, 3, 4, 5])
+Vecsmall([97, 98])
+error("incorrect type in vectosmall (t_MAT).")
+error("incorrect type in vectosmall (t_ERROR).")
+Vecsmall([1, 0, 0, 0])
+Vecsmall([1, 0, 0, 0])
+error("incorrect type in gtovecsmall (t_RFRAC).")
+Vecsmall([5, 4, 3, 2])
+Vecsmall([1, 2, 0, 0])
+Vecsmall([1, 2, 0, 0])
+Vecsmall([1, 2, 0, 0])
+Vecsmall([1, 2, 3, 0])
+Vecsmall([1, 2, 3, 4])
+error("incorrect type in gtovecsmall (t_STR).")
+error("incorrect type in gtovecsmall (t_MAT).")
+error("incorrect type in gtovecsmall (t_ERROR).")
+Vecsmall([0, 0, 0, 1])
+Vecsmall([0, 0, 1, 0])
+error("incorrect type in gtovecsmall (t_RFRAC).")
+Vecsmall([5, 4, 3, 2])
+Vecsmall([0, 0, 1, 2])
+Vecsmall([0, 0, 1, 2])
+Vecsmall([0, 0, 1, 2])
+Vecsmall([0, 1, 2, 3])
+Vecsmall([1, 2, 3, 4])
+error("incorrect type in gtovecsmall (t_STR).")
+error("incorrect type in gtovecsmall (t_MAT).")
+error("incorrect type in gtovecsmall (t_ERROR).")
+error("incorrect type in component [leaf] (t_INT).")
+1
+x
+2
+2
+2
+2
+2
+2
+error("incorrect type in component [leaf] (t_STR).")
+[2, 4]~
+0
+error("incorrect type in component [leaf] (t_INT).")
+0
+error("non-existent component: index > 2")
+0
+error("non-existent component: index > 3")
+error("non-existent component: index > 2")
+error("non-existent component: index > 2")
+error("non-existent component: index > 3")
+error("non-existent component: index > 5")
+error("incorrect type in component [leaf] (t_STR).")
+error("non-existent component: index > 3")
+error("non-existent component: index > 3")
+0
+0
+1
+0
+0
+error("non-existent component in polcoeff: index < 1")
+error("incorrect type in polcoeff (t_VECSMALL).")
+error("incorrect type in polcoeff (t_LIST).")
+error("incorrect type in polcoeff (t_LIST).")
+error("incorrect type in polcoeff (t_STR).")
+error("non-existent component in polcoeff: index < 1")
+error("incorrect type in polcoeff (t_ERROR).")
+0
+0
+0
+3
+2
+2
+error("incorrect type in polcoeff (t_VECSMALL).")
+error("incorrect type in polcoeff (t_LIST).")
+error("incorrect type in polcoeff (t_LIST).")
+error("incorrect type in polcoeff (t_STR).")
+[2, 4]~
+error("incorrect type in polcoeff (t_ERROR).")
+0
+0
+0
+0
+error("domain error in polcoeff: degree > 2")
+error("non-existent component in polcoeff: index > 2")
+error("incorrect type in polcoeff (t_VECSMALL).")
+error("incorrect type in polcoeff (t_LIST).")
+error("incorrect type in polcoeff (t_LIST).")
+error("incorrect type in polcoeff (t_STR).")
+error("non-existent component in polcoeff: index > 3")
+error("incorrect type in polcoeff (t_ERROR).")
+0
+0
+x + 1
+0
+y + O(y^2)
+Vecsmall([4, 5, 3])
+Total time spent: 0
diff --git a/src/test/32/factor b/src/test/32/factor
new file mode 100644
index 0000000..c61eebc
--- /dev/null
+++ b/src/test/32/factor
@@ -0,0 +1,149 @@
+
+[x^2 + I 1]
+
+
+[x + 1.0000000000000000000000000000000000000 1]
+
+[x - 1.0000000000000000000000000000000000000 1]
+
+
+[x + (0.45508986056222734130435775782246856962 - 1.0986841134678099660398011
+952406783785*I) 1]
+
+[x + (-0.45508986056222734130435775782246856962 + 1.098684113467809966039801
+1952406783785*I) 1]
+
+
+[Mod(1, 5)*x + Mod(2, 5) 1]
+
+[Mod(1, 5)*x + Mod(3, 5) 1]
+
+
+[Mod(1, 3)*x^2 + (Mod(1, 3) + Mod(1, 3)*I) 1]
+
+  ***   at top-level: factor(x^2+Mod(1,5)*
+  ***                 ^--------------------
+  *** factor: sorry, factor for general polynomials is not yet implemented.
+
+[(1 + O(5))*x^2 + O(5)*x + (2 + O(5)) 1]
+
+  ***   at top-level: factor(x^2+(1+O(3))+
+  ***                 ^--------------------
+  *** factor: sorry, factor for general polynomial is not yet implemented.
+
+[(1 + O(5))*x + (2 + O(5)) 1]
+
+[(1 + O(5))*x + (3 + O(5)) 1]
+
+
+[ x + (1 - w) 1]
+
+[x + (-1 + w) 1]
+
+  ***   at top-level: factor(x^2+quadgen(-
+  ***                 ^--------------------
+  *** factor: sorry, factor for general polynomial is not yet implemented.
+
+[Mod(1, 5)*x + (Mod(4, 5) + Mod(1, 5)*w) 1]
+
+[Mod(1, 5)*x + (Mod(1, 5) + Mod(4, 5)*w) 1]
+
+  ***   at top-level: factor(x^2+quadgen(-
+  ***                 ^--------------------
+  *** factor: sorry, factor for general polynomial is not yet implemented.
+  ***   at top-level: factor(x^2+quadgen(-3)*(1+O(3)))
+  ***                                       ^----------
+  *** _*_: not an n-th power residue in Qp_sqrt: -3.
+
+[x^2 + Mod(y, y^2 + 1) 1]
+
+
+[Mod(Mod(1, 3), Mod(1, 3)*y^2 + Mod(1, 3))*x + Mod(Mod(1, 3)*y + Mod(1, 3), 
+Mod(1, 3)*y^2 + Mod(1, 3)) 1]
+
+[Mod(Mod(1, 3), Mod(1, 3)*y^2 + Mod(1, 3))*x + Mod(Mod(2, 3)*y + Mod(2, 3), 
+Mod(1, 3)*y^2 + Mod(1, 3)) 1]
+
+  ***   at top-level: factor(x^2+Mod(y*Mod
+  ***                 ^--------------------
+  *** factor: sorry, factor for general polynomial is not yet implemented.
+  ***   at top-level: factor(x^2+Mod(y*(1+
+  ***                 ^--------------------
+  *** factor: sorry, factor for general polynomial is not yet implemented.
+
+[       I  1]
+
+[       3 -1]
+
+[   1 + I -2]
+
+[15 + 2*I  1]
+
+
+[  1 + I 1]
+
+[  2 + I 4]
+
+[1 + 2*I 4]
+
+
+[     -I 1]
+
+[  1 + I 1]
+
+[2 + 3*I 1]
+
+
+[     -I 1]
+
+[      3 1]
+
+[  1 + I 1]
+
+[2 + 3*I 1]
+
+
+[   -1 1]
+
+[1 + I 5]
+
+
+[     -1  1]
+
+[1 + 2*I -2]
+
+
+[31271192761826143388782348951  1]
+
+[31274945109847936339856761591 -1]
+
+
+[      500009  1]
+
+[      500029 -1]
+
+[      500041  1]
+
+[      500057 -1]
+
+[      500069  1]
+
+[      500083 -1]
+
+[250110012091  1]
+
+[250115013209 -1]
+
+
+[             x - 2.0000000000000000000000000000000000000 1]
+
+[x^2 + 0.E-38*x + 1.0000000000000000000000000000000000000 1]
+
+
+[x + 1  2]
+
+[x - 2 -1]
+
+[x + 2 -1]
+
+Total time spent: 8
diff --git a/src/test/32/factorint b/src/test/32/factorint
new file mode 100644
index 0000000..43c2038
--- /dev/null
+++ b/src/test/32/factorint
@@ -0,0 +1,29 @@
+
+[    -1 1]
+
+[     3 5]
+
+[    73 1]
+
+[   181 1]
+
+[   223 1]
+
+[   293 2]
+
+[  4157 2]
+
+[112573 1]
+
+[281191 1]
+
+
+[    2 1]
+
+[  397 1]
+
+[27031 1]
+
+[32203 1]
+
+Total time spent: 8
diff --git a/src/test/32/factormod b/src/test/32/factormod
new file mode 100644
index 0000000..2413925
--- /dev/null
+++ b/src/test/32/factormod
@@ -0,0 +1,251 @@
+[x + 7, 1; x + 8, 1; x + 20, 1; x + 21, 1; x + 24, 1; x + 50, 1; x + 60, 1; 
+x + 63, 1; x + 68, 1; x + 72, 1; x + 125, 1; x + 139, 1; x + 146, 1; x + 150
+, 1; x + 170, 1; x + 180, 1; x + 189, 1; x + 193, 1; x + 204, 1; x + 212, 1;
+ x + 216, 1; x + 239, 1; x + 293, 1; x + 338, 1; x + 343, 1; x + 346, 1; x +
+ 364, 1; x + 365, 1; x + 375, 1; x + 377, 1; x + 392, 1; x + 406, 1; x + 412
+, 1; x + 416, 1; x + 417, 1; x + 425, 1; x + 438, 1; x + 448, 1; x + 450, 1;
+ x + 457, 1; x + 463, 1; x + 464, 1; x + 466, 1; x + 502, 1; x + 506, 1; x +
+ 510, 1; x + 512, 1; x + 530, 1; x + 540, 1; x + 567, 1; x + 578, 1; x + 579
+, 1; x + 612, 1; x + 614, 1; x + 622, 1; x + 636, 1; x + 648, 1; x + 682, 1;
+ x + 716, 1; x + 717, 1; x + 751, 1; x + 767, 1; x + 769, 1; x + 778, 1; x +
+ 791, 1; x + 814, 1; x + 826, 1; x + 844, 1; x + 845, 1; x + 865, 1; x + 874
+, 1; x + 879, 1; x + 881, 1; x + 904, 1; x + 910, 1; x + 923, 1; x + 944, 1;
+ x + 980, 1; x + 982, 1; x + 994, 1; x + 1014, 1; x + 1015, 1; x + 1029, 1; 
+x + 1030, 1; x + 1038, 1; x + 1040, 1; x + 1057, 1; x + 1092, 1; x + 1095, 1
+; x + 1120, 1; x + 1125, 1; x + 1131, 1; x + 1136, 1; x + 1160, 1; x + 1165,
+ 1; x + 1176, 1; x + 1178, 1; x + 1208, 1; x + 1218, 1; x + 1223, 1; x + 122
+6, 1; x + 1236, 1; x + 1241, 1; x + 1248, 1; x + 1251, 1; x + 1255, 1; x + 1
+265, 1; x + 1275, 1; x + 1279, 1; x + 1280, 1; x + 1282, 1; x + 1314, 1; x +
+ 1325, 1; x + 1327, 1; x + 1344, 1; x + 1350, 1; x + 1371, 1; x + 1373, 1; x
+ + 1389, 1; x + 1392, 1; x + 1398, 1; x + 1403, 1; x + 1406, 1; x + 1409, 1;
+ x + 1417, 1; x + 1445, 1; x + 1468, 1; x + 1481, 1; x + 1506, 1; x + 1518, 
+1; x + 1522, 1; x + 1526, 1; x + 1530, 1; x + 1535, 1; x + 1536, 1; x + 1555
+, 1; x + 1573, 1; x + 1590, 1; x + 1620, 1; x + 1658, 1; x + 1681, 1; x + 16
+87, 1; x + 1694, 1; x + 1701, 1; x + 1705, 1; x + 1706, 1; x + 1721, 1; x + 
+1734, 1; x + 1737, 1; x + 1744, 1; x + 1756, 1; x + 1763, 1; x + 1783, 1; x 
++ 1790, 1; x + 1802, 1; x + 1804, 1; x + 1826, 1; x + 1836, 1; x + 1842, 1; 
+x + 1849, 1; x + 1866, 1; x + 1868, 1; x + 1882, 1; x + 1891, 1; x + 1892, 1
+; x + 1901, 1; x + 1908, 1; x + 1927, 1; x + 1928, 1; x + 1936, 1; x + 1942,
+ 1; x + 1944, 1; x + 1945, 1; x + 2021, 1; x + 2035, 1; x + 2046, 1; x + 205
+4, 1; x + 2065, 1; x + 2068, 1; x + 2110, 1; x + 2148, 1; x + 2151, 1; x + 2
+164, 1; x + 2171, 1; x + 2185, 1; x + 2209, 1; x + 2212, 1; x + 2213, 1; x +
+ 2231, 1; x + 2253, 1; x + 2257, 1; x + 2260, 1; x + 2275, 1; x + 2276, 1; x
+ + 2291, 1; x + 2293, 1; x + 2301, 1; x + 2307, 1; x + 2334, 1; x + 2338, 1;
+ x + 2360, 1; x + 2373, 1; x + 2429, 1; x + 2434, 1; x + 2442, 1; x + 2450, 
+1; x + 2455, 1; x + 2461, 1; x + 2478, 1; x + 2483, 1; x + 2485, 1; x + 2528
+, 1; x + 2532, 1; x + 2535, 1; x + 2575, 1; x + 2593, 1; x + 2595, 1; x + 26
+00, 1; x + 2602, 1; x + 2622, 1; x + 2626, 1; x + 2633, 1; x + 2637, 1; x + 
+2643, 1; x + 2672, 1; x + 2674, 1; x + 2689, 1; x + 2712, 1; x + 2717, 1; x 
++ 2730, 1; x + 2769, 1; x + 2776, 1; x + 2800, 1; x + 2827, 1; x + 2828, 1; 
+x + 2832, 1; x + 2840, 1; x + 2873, 1; x + 2894, 1; x + 2900, 1; x + 2926, 1
+; x + 2929, 1; x + 2940, 1; x + 2941, 1; x + 2945, 1; x + 2946, 1; x + 2947,
+ 1; x + 2957, 1; x + 2982, 1; x + 3007, 1; x + 3017, 1; x + 3019, 1; x + 302
+0, 1; x + 3028, 1; x + 3042, 1; x + 3045, 1; x + 3056, 1; x + 3065, 1; x + 3
+087, 1; x + 3090, 1; x + 3094, 1; x + 3114, 1; x + 3116, 1; x + 3120, 1; x +
+ 3121, 1; x + 3154, 1; x + 3163, 1; x + 3171, 1; x + 3200, 1; x + 3205, 1; x
+ + 3221, 1; x + 3229, 1; x + 3232, 1; x + 3238, 1; x + 3268, 1; x + 3276, 1;
+ x + 3278, 1; x + 3285, 1; x + 3317, 1; x + 3332, 1; x + 3338, 1; x + 3343, 
+1; x + 3344, 1; x + 3359, 1; x + 3360, 1; x + 3368, 1; x + 3375, 1; x + 3393
+, 1; x + 3407, 1; x + 3408, 1; x + 3418, 1; x + 3448, 1; x + 3449, 1; x + 34
+51, 1; x + 3454, 1; x + 3466, 1; x + 3469, 1; x + 3480, 1; x + 3495, 1; x + 
+3502, 1; x + 3511, 1; x + 3515, 1; x + 3528, 1; x + 3534, 1; x + 3536, 1; x 
++ 3572, 1; x + 3578, 1; x + 3589, 1; x + 3607, 1; x + 3624, 1; x + 3654, 1; 
+x + 3669, 1; x + 3670, 1; x + 3678, 1; x + 3708, 1; x + 3723, 1; x + 3744, 1
+; x + 3753, 1; x + 3758, 1; x + 3765, 1; x + 3769, 1; x + 3793, 1; x + 3795,
+ 1; x + 3805, 1; x + 3808, 1; x + 3815, 1; x + 3821, 1; x + 3825, 1; x + 383
+7, 1; x + 3840, 1; x + 3846, 1; x + 3869, 1; x + 3889, 1; x + 3929, 1; x + 3
+941, 1; x + 3942, 1; x + 3944, 1; x + 3947, 1; x + 3959, 1; x + 3961, 1; x +
+ 3975, 1; x + 3981, 1; x + 3982, 1; x + 4032, 1; x + 4050, 1; x + 4094, 1; x
+ + 4099, 1; x + 4103, 1; x + 4113, 1; x + 4119, 1; x + 4138, 1; x + 4145, 1;
+ x + 4153, 1; x + 4167, 1; x + 4176, 1; x + 4194, 1; x + 4209, 1; x + 4218, 
+1; x + 4222, 1; x + 4227, 1; x + 4235, 1; x + 4238, 1; x + 4251, 1; x + 4265
+, 1; x + 4267, 1; x + 4273, 1; x + 4289, 1; x + 4301, 1; x + 4335, 1; x + 43
+52, 1; x + 4360, 1; x + 4378, 1; x + 4390, 1; x + 4404, 1; x + 4411, 1; x + 
+4423, 1; x + 4443, 1; x + 4475, 1; x + 4483, 1; x + 4489, 1; x + 4499, 1; x 
++ 4504, 1; x + 4505, 1; x + 4510, 1; x + 4518, 1; x + 4523, 1; x + 4538, 1; 
+x + 4554, 1; x + 4564, 1; x + 4565, 1; x + 4566, 1; x + 4578, 1; x + 4590, 1
+; x + 4605, 1; x + 4608, 1; x + 4649, 1; x + 4661, 1; x + 4665, 1; x + 4670,
+ 1; x + 4684, 1; x + 4693, 1; x + 4697, 1; x + 4705, 1; x + 4719, 1; x + 472
+7, 1; x + 4730, 1; x + 4739, 1; x + 4770, 1; x + 4781, 1; x + 4820, 1; x + 4
+834, 1; x + 4840, 1; x + 4855, 1; x + 4860, 1; x + 4883, 1; x + 4906, 1; x +
+ 4913, 1; x + 4924, 1; x + 4939, 1; x + 4963, 1; x + 4974, 1; x + 4987, 1; x
+ + 5002, 1; x + 5039, 1; x + 5043, 1; x + 5054, 1; x + 5061, 1; x + 5063, 1;
+ x + 5078, 1; x + 5082, 1; x + 5102, 1; x + 5103, 1; x + 5115, 1; x + 5118, 
+1; x + 5135, 1; x + 5163, 1; x + 5170, 1; x + 5189, 1; x + 5202, 1; x + 5207
+, 1; x + 5209, 1; x + 5211, 1; x + 5216, 1; x + 5219, 1; x + 5232, 1; x + 52
+46, 1; x + 5268, 1; x + 5275, 1; x + 5287, 1; x + 5289, 1; x + 5349, 1; x + 
+5368, 1; x + 5370, 1; x + 5386, 1; x + 5406, 1; x + 5410, 1; x + 5411, 1; x 
++ 5412, 1; x + 5416, 1; x + 5461, 1; x + 5464, 1; x + 5478, 1; x + 5508, 1; 
+x + 5509, 1; x + 5518, 1; x + 5526, 1; x + 5530, 1; x + 5547, 1; x + 5588, 1
+; x + 5596, 1; x + 5598, 1; x + 5604, 1; x + 5609, 1; x + 5618, 1; x + 5646,
+ 1; x + 5650, 1; x + 5653, 1; x + 5662, 1; x + 5672, 1; x + 5673, 1; x + 567
+6, 1; x + 5690, 1; x + 5703, 1; x + 5724, 1; x + 5734, 1; x + 5776, 1; x + 5
+781, 1; x + 5784, 1; x + 5789, 1; x + 5797, 1; x + 5808, 1; x + 5826, 1; x +
+ 5832, 1; x + 5835, 1; x + 5836, 1; x + 5845, 1; x + 5900, 1; x + 5959, 1; x
+ + 5966, 1; x + 5969, 1; x + 5987, 1; x + 5993, 1; x + 6026, 1; x + 6063, 1;
+ x + 6085, 1; x + 6086, 1; x + 6092, 1; x + 6094, 1; x + 6105, 1; x + 6125, 
+1; x + 6127, 1; x + 6138, 1; x + 6151, 1; x + 6162, 1; x + 6164, 1; x + 6184
+, 1; x + 6195, 1; x + 6197, 1; x + 6203, 1; x + 6204, 1; x + 6226, 1; x + 62
+63, 1; x + 6296, 1; x + 6302, 1; x + 6320, 1; x + 6323, 1; x + 6330, 1; x + 
+6389, 1; x + 6444, 1; x + 6453, 1; x + 6454, 1; x + 6457, 1; x + 6463, 1; x 
++ 6481, 1; x + 6492, 1; x + 6500, 1; x + 6505, 1; x + 6508, 1; x + 6513, 1; 
+x + 6555, 1; x + 6565, 1; x + 6586, 1; x + 6599, 1; x + 6613, 1; x + 6616, 1
+; x + 6617, 1; x + 6627, 1; x + 6636, 1; x + 6639, 1; x + 6643, 1; x + 6671,
+ 1; x + 6680, 1; x + 6685, 1; x + 6691, 1; x + 6693, 1; x + 6701, 1; x + 674
+2, 1; x + 6759, 1; x + 6763, 1; x + 6771, 1; x + 6780, 1; x + 6781, 1; x + 6
+811, 1; x + 6825, 1; x + 6828, 1; x + 6873, 1; x + 6877, 1; x + 6878, 1; x +
+ 6879, 1; x + 6883, 1; x + 6903, 1; x + 6919, 1; x + 6921, 1; x + 6940, 1; x
+ + 7000, 1; x + 7002, 1; x + 7014, 1; x + 7021, 1; x + 7043, 1; x + 7057, 1;
+ x + 7070, 1; x + 7073, 1; x + 7078, 1; x + 7080, 1; x + 7082, 1; x + 7087, 
+1; x + 7100, 1; x + 7119, 1; x + 7126, 1; x + 7154, 1; x + 7171, 1; x + 7174
+, 1; x + 7186, 1; x + 7187, 1; x + 7207, 1; x + 7211, 1; x + 7226, 1; x + 72
+28, 1; x + 7235, 1; x + 7246, 1; x + 7250, 1; x + 7287, 1; x + 7302, 1; x + 
+7315, 1; x + 7326, 1; x + 7350, 1; x + 7365, 1; x + 7376, 1; x + 7383, 1; x 
++ 7406, 1; x + 7429, 1; x + 7434, 1; x + 7449, 1; x + 7455, 1; x + 7469, 1; 
+x + 7508, 1; x + 7519, 1; x + 7550, 1; x + 7559, 1; x + 7562, 1; x + 7570, 1
+; x + 7584, 1; x + 7592, 1; x + 7596, 1; x + 7605, 1; x + 7619, 1; x + 7624,
+ 1; x + 7628, 1; x + 7640, 1; x + 7681, 1; x + 7684, 1; x + 7699, 1; x + 771
+1, 1; x + 7723, 1; x + 7724, 1; x + 7725, 1; x + 7735, 1; x + 7751, 1; x + 7
+766, 1; x + 7771, 1; x + 7779, 1; x + 7784, 1; x + 7785, 1; x + 7790, 1; x +
+ 7800, 1; x + 7806, 1; x + 7814, 1; x + 7846, 1; x + 7866, 1; x + 7878, 1; x
+ + 7885, 1; x + 7899, 1; x + 7911, 1; x + 7929, 1; x + 7937, 1; x + 7954, 1;
+ x + 7988, 1; x + 8000, 1; x + 8016, 1; x + 8022, 1; x + 8024, 1; x + 8038, 
+1; x + 8051, 1; x + 8054, 1; x + 8062, 1; x + 8067, 1; x + 8071, 1; x + 8080
+, 1; x + 8095, 1; x + 8113, 1; x + 8122, 1; x + 8136, 1; x + 8144, 1; x + 81
+51, 1; x + 8170, 1; x + 8176, 1; x + 8186, 1; x + 8190, 1; x + 8195, 1; x + 
+8239, 1; x + 8257, 1; x + 8307, 1; x + 8308, 1; x + 8314, 1; x + 8328, 1; x 
++ 8330, 1; x + 8342, 1; x + 8345, 1; x + 8347, 1; x + 8348, 1; x + 8360, 1; 
+x + 8400, 1; x + 8420, 1; x + 8443, 1; x + 8449, 1; x + 8452, 1; x + 8464, 1
+; x + 8468, 1; x + 8474, 1; x + 8481, 1; x + 8484, 1; x + 8494, 1; x + 8496,
+ 1; x + 8520, 1; x + 8524, 1; x + 8531, 1; x + 8536, 1; x + 8545, 1; x + 856
+6, 1; x + 8581, 1; x + 8611, 1; x + 8619, 1; x + 8620, 1; x + 8635, 1; x + 8
+665, 1; x + 8682, 1; x + 8700, 1; x + 8711, 1; x + 8717, 1; x + 8753, 1; x +
+ 8755, 1; x + 8761, 1; x + 8774, 1; x + 8778, 1; x + 8787, 1; x + 8794, 1; x
+ + 8809, 1; x + 8820, 1; x + 8823, 1; x + 8835, 1; x + 8838, 1; x + 8840, 1;
+ x + 8841, 1; x + 8871, 1; x + 8881, 1; x + 8882, 1; x + 8896, 1; x + 8914, 
+1; x + 8921, 1; x + 8929, 1; x + 8930, 1; x + 8945, 1; x + 8946, 1; x + 8951
+, 1; x + 8957, 1; x + 8972, 1; x + 9004, 1; x + 9011, 1; x + 9013, 1; x + 90
+21, 1; x + 9051, 1; x + 9057, 1; x + 9060, 1; x + 9068, 1; x + 9084, 1; x + 
+9089, 1; x + 9118, 1; x + 9126, 1; x + 9135, 1; x + 9168, 1; x + 9169, 1; x 
++ 9173, 1; x + 9175, 1; x + 9195, 1; x + 9199, 1; x + 9202, 1; x + 9224, 1; 
+x + 9233, 1; x + 9244, 1; x + 9247, 1; x + 9261, 1; x + 9269, 1; x + 9270, 1
+; x + 9272, 1; x + 9282, 1; x + 9307, 1; x + 9332, 1; x + 9342, 1; x + 9343,
+ 1; x + 9344, 1; x + 9348, 1; x + 9349, 1; x + 9360, 1; x + 9363, 1; x + 938
+9, 1; x + 9395, 1; x + 9416, 1; x + 9449, 1; x + 9457, 1; x + 9461, 1; x + 9
+462, 1; x + 9489, 1; x + 9513, 1; x + 9520, 1; x + 9559, 1; x + 9572, 1; x +
+ 9577, 1; x + 9600, 1; x + 9615, 1; x + 9617, 1; x + 9646, 1; x + 9652, 1; x
+ + 9656, 1; x + 9663, 1; x + 9667, 1; x + 9687, 1; x + 9689, 1; x + 9694, 1;
+ x + 9696, 1; x + 9714, 1; x + 9754, 1; x + 9757, 1; x + 9761, 1; x + 9804, 
+1; x + 9806, 1; x + 9811, 1; x + 9828, 1; x + 9834, 1; x + 9839, 1; x + 9847
+, 1; x + 9855, 1; x + 9860, 1; x + 9916, 1; x + 9929, 1; x + 9951, 1; x + 99
+55, 1; x + 9982, 1; x + 9988, 1; x + 9996, 1; x + 9998, 1; x + 10013, 1; x +
+ 10014, 1; x + 10029, 1; x + 10032, 1; x + 10036, 1; x + 10058, 1; x + 10076
+, 1; x + 10077, 1; x + 10080, 1; x + 10104, 1; x + 10118, 1; x + 10125, 1; x
+ + 10138, 1; x + 10141, 1; x + 10179, 1; x + 10221, 1; x + 10224, 1; x + 102
+35, 1; x + 10243, 1; x + 10254, 1; x + 10268, 1; x + 10344, 1; x + 10345, 1;
+ x + 10347, 1; x + 10353, 1; x + 10361, 1; x + 10362, 1; x + 10381, 1; x + 1
+0388, 1; x + 10397, 1; x + 10398, 1; x + 10407, 1; x + 10421, 1; x + 10423, 
+1; x + 10440, 1; x + 10447, 1; x + 10453, 1; x + 10463, 1; x + 10485, 1; x +
+ 10487, 1; x + 10499, 1; x + 10506, 1; x + 10526, 1; x + 10533, 1; x + 10545
+, 1; x + 10552, 1; x + 10555, 1; x + 10568, 1; x + 10583, 1; x + 10584, 1; x
+ + 10588, 1; x + 10595, 1; x + 10602, 1; x + 10608, 1; x + 10631, 1; x + 106
+69, 1; x + 10699, 1; x + 10716, 1; x + 10734, 1; x + 10753, 1; x + 10754, 1;
+ x + 10759, 1; x + 10763, 1; x + 10767, 1; x + 10771, 1; x + 10783, 1; x + 1
+0808, 1; x + 10821, 1; x + 10844, 1; x + 10872, 1; x + 10880, 1; x + 10883, 
+1; x + 10886, 1; x + 10891, 1; x + 10897, 1; x + 10900, 1; x + 10916, 1; x +
+ 10918, 1; x + 10939, 1; x + 10945, 1; x + 10962, 1; x + 10964, 1; x + 10975
+, 1; x + 11007, 1; x + 11009, 1; x + 11010, 1; x + 11014, 1; x + 11024, 1; x
+ + 11034, 1; x + 11038, 1; x + 11041, 1; x + 11048, 1; x + 11053, 1; x + 110
+63, 1; x + 11066, 1; x + 11071, 1; x + 11081, 1; x + 11111, 1; x + 11113, 1;
+ x + 11124, 1; x + 11129, 1; x + 11153, 1; x + 11158, 1; x + 11164, 1; x + 1
+1169, 1; x + 11194, 1; x + 11197, 1; x + 11232, 1; x + 11249, 1; x + 11251, 
+1; x + 11259, 1; x + 11260, 1; x + 11274, 1; x + 11275, 1; x + 11295, 1; x +
+ 11307, 1; x + 11309, 1; x + 11345, 1; x + 11366, 1; x + 11379, 1; x + 11385
+, 1; x + 11408, 1; x + 11410, 1; x + 11415, 1; x + 11424, 1; x + 11444, 1; x
+ + 11445, 1; x + 11463, 1; x + 11475, 1; x + 11498, 1; x + 11511, 1; x + 115
+20, 1; x + 11522, 1; x + 11538, 1; x + 11572, 1; x + 11573, 1; x + 11607, 1;
+ x + 11641, 1; x + 11653, 1; x + 11667, 1; x + 11675, 1; x + 11677, 1; x + 1
+1710, 1; x + 11711, 1; x + 11722, 1; x + 11749, 1; x + 11759, 1; x + 11777, 
+1; x + 11779, 1; x + 11783, 1; x + 11787, 1; x + 11823, 1; x + 11825, 1; x +
+ 11826, 1; x + 11832, 1; x + 11839, 1; x + 11841, 1; x + 11851, 1; x + 11864
+, 1; x + 11872, 1; x + 11873, 1; x + 11877, 1; x + 11883, 1; x + 11897, 1; x
+ + 11912, 1; x + 11914, 1; x + 11924, 1; x + 11925, 1; x + 11943, 1; x + 119
+46, 1; x + 11951, 1; x + 11996, 1; x + 12050, 1; x + 12073, 1; x + 12077, 1;
+ x + 12085, 1; x + 12096, 1; x + 12100, 1; x + 12109, 1; x + 12119, 1; x + 1
+2139, 1; x + 12143, 1; x + 12150, 1; x + 12164, 1; x + 12217, 1; x + 12221, 
+1; x + 12226, 1; x + 12229, 1; x + 12239, 1; x + 12265, 1; x + 12268, 1; x +
+ 12269, 1; x + 12281, 1; x + 12282, 1]
+
+[ y + 697093550 1]
+
+[y + 1551133506 1]
+
+[y + 2227781066 1]
+
+
+[x 1]
+
+[x^3 + x^2 + 1 1]
+
+[x^27 + x^25 + x^24 + x^23 + x^22 + x^17 + x^16 + x^15 + x^13 + x^12 + x^9 +
+ x^8 + x^4 + x + 1 1]
+
+[Mod(1, 41), Mod(3, 41), Mod(9, 41), Mod(14, 41), Mod(27, 41), Mod(32, 41), 
+Mod(38, 41), Mod(40, 41)]~
+[Mod(1, 5), Mod(2, 5)]~
+  ***   at top-level: polrootsmod(Pol(0),2
+  ***                 ^--------------------
+  *** polrootsmod: zero polynomial in rootmod.
+[]~
+
+[            Mod(1, 2)*x 1]
+
+[Mod(1, 2)*x + Mod(1, 2) 2]
+
+
+[Mod(0, 2) 1]
+
+[;]
+
+[1 1]
+
+[1 2]
+
+[2 1]
+
+[Mod(1, 7), Mod(2, 7), Mod(3, 7), Mod(5, 7), Mod(6, 7)]~
+  ***   at top-level: polrootsmod(Pol(0),p
+  ***                 ^--------------------
+  *** polrootsmod: zero polynomial in rootmod.
+[]~
+[Mod(0, 18446744073709551629)]~
+[Mod(9223372036854775814, 18446744073709551629)]~
+
+[Mod(1, 18446744073709551629)*x + Mod(2370518075556110396, 18446744073709551
+629) 1]
+
+[Mod(1, 18446744073709551629)*x + Mod(16076225998153441233, 1844674407370955
+1629) 1]
+
+
+[1 1]
+
+[1 1]
+
+
+[2 1]
+
+
+[Mod(1, 18446744073709551629)*x + Mod(2370518075556110396, 18446744073709551
+629) 1]
+
+[Mod(1, 18446744073709551629)*x + Mod(16076225998153441233, 1844674407370955
+1629) 1]
+
+
+[Mod(0, 18446744073709551629) 1]
+
+[;]
+1
+0
+Total time spent: 56
diff --git a/src/test/32/ff b/src/test/32/ff
new file mode 100644
index 0000000..9735799
--- /dev/null
+++ b/src/test/32/ff
@@ -0,0 +1,280 @@
+? test(2,20)
+[a^2 + a + 1, a^19 + a^18 + a^17 + a^15 + a^14 + a^13 + a^12 + a^8 + a^7 + a
+^6 + a^4 + a^2 + 1, 0, a + 1, 0, 0, 0, 0, a^19 + a^16 + a^15 + a^11 + a^8 + 
+a^5 + a^4 + a^3 + a^2 + a, a, a^2, a^12 + a^9 + a^6 + a^4 + a^3 + a^2 + a, a
+^18 + a^17 + a^16 + a^14 + a^13 + a^12 + a^11 + a^10 + a^9 + a^8 + a^7 + a^4
+ + a^3 + a^2 + 1, a^18 + a^17 + a^16 + a^14 + a^13 + a^12 + a^11 + a^10 + a^
+9 + a^8 + a^7 + a^4 + a^3 + a^2 + 1, a^16 + a^12 + a^9 + a^6 + a^4 + a^3 + a
+^2 + a + 1, 0, Mod(1, 2), Mod(0, 2), Mod(1, 2)*x^20 + Mod(1, 2)*x^17 + Mod(1
+, 2)*x^16 + Mod(1, 2)*x^12 + Mod(1, 2)*x^9 + Mod(1, 2)*x^6 + Mod(1, 2)*x^5 +
+ Mod(1, 2)*x^4 + Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(1, 2), Mod(1, 2)*x^20 +
+ Mod(1, 2)*x^17 + Mod(1, 2)*x^16 + Mod(1, 2)*x^12 + Mod(1, 2)*x^9 + Mod(1, 2
+)*x^6 + Mod(1, 2)*x^5 + Mod(1, 2)*x^4 + Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(
+1, 2), [a, a^2, a^4, a^8, a^16, a^16 + a^12 + a^9 + a^6 + a^4 + a^3 + a^2, a
+^16 + a^12 + a^9 + a^6 + a^4 + a^3 + a + 1, a^16 + a^12 + a^9 + a^6 + a^3 + 
+a^2 + a, a^16 + a^12 + a^9 + a^8 + a^6 + a^4 + a^3 + a^2 + a + 1, a^12 + a^9
+ + a^6 + a^4 + a^3 + a^2 + a, a + 1, a^2 + 1, a^4 + 1, a^8 + 1, a^16 + 1, a^
+16 + a^12 + a^9 + a^6 + a^4 + a^3 + a^2 + 1, a^16 + a^12 + a^9 + a^6 + a^4 +
+ a^3 + a, a^16 + a^12 + a^9 + a^6 + a^3 + a^2 + a + 1, a^16 + a^12 + a^9 + a
+^8 + a^6 + a^4 + a^3 + a^2 + a]~, [x^3 + (a^17 + a^16 + a^10 + a^7 + a^3), 1
+; x^3 + (a^17 + a^16 + a^10 + a^7 + a^3 + a), 1], [], a/x, 1, a^19 + a^18 + 
+a^17 + a^13 + a^10 + a^6 + a^4, 1048575, a, [x + (a^18 + a^16 + a^13 + a^9 +
+ a^8 + a^7 + a^6 + a^4 + a^2 + a), 1; x + (a^18 + a^16 + a^13 + a^9 + a^8 + 
+a^7 + a^6 + a^4 + a^2 + a + 1), 1], [a^18 + a^16 + a^13 + a^9 + a^8 + a^7 + 
+a^6 + a^4 + a^2 + a, a^18 + a^16 + a^13 + a^9 + a^8 + a^7 + a^6 + a^4 + a^2 
++ a + 1]~]
+? test(7,7)
+[a^2 + 3*a + 1, a^6 + 2*a^4 + 5*a^3 + 2*a^2 + 5*a + 1, 3*a + 3, a + 3, 5*a^6
+ + 3*a^4 + 4*a^3 + 3*a^2 + 4*a, 2*a + 2, 2*a + 2, 4*a + 4, 6*a^6 + 6*a^5 + 5
+*a^4 + 2, 6*a, a^2, 3*a^6 + 5*a^5 + 3*a^3 + a^2 + 2*a + 6, 4*a^2 + 4*a + 4, 
+4*a^2 + 4*a + 4, 4, 3*a^6 + 3*a^5 + 5*a^4 + 2*a^3 + 3*a^2 + a + 4, Mod(1, 7)
+, Mod(6, 7), Mod(1, 7)*x^7 + Mod(1, 7)*x^6 + Mod(2, 7)*x^5 + Mod(5, 7)*x + M
+od(1, 7), Mod(1, 7)*x^7 + Mod(1, 7)*x^6 + Mod(2, 7)*x^5 + Mod(5, 7)*x + Mod(
+1, 7), [a, 6*a^6 + 5*a^5 + 2*a + 6, 5*a^6 + 6*a^5 + 2*a^3 + 4*a^2 + 3*a + 5,
+ 3*a^6 + 5*a^2 + 5, 6*a^6 + 2*a^5 + 5*a^4 + 5*a^3 + 5*a^2 + a + 5, 2*a^6 + 5
+*a^2 + 6, 6*a^6 + a^5 + 2*a^4 + 2*a^2]~, [x^2 + (a^6 + 6*a^5 + 4*a^4 + 5*a^3
+ + 4)*x + 4, 1; x^2 + (2*a^6 + 5*a^5 + a^4 + 3*a^3 + 1)*x + 2, 1; x^2 + (4*a
+^6 + 3*a^5 + 2*a^4 + 6*a^3 + 2)*x + 1, 1], [0, 0, 0, a, 1, 0, 2*a, 4, 6*a^2,
+ a, 4, 6*a^3 + 2, a^6 + 4*a^5 + 4*a^4 + 6*a^2 + a + 4, Vecsmall([4]), [a, [V
+ecsmall([131072, 0, 1]), Vecsmall([131072, 1]), [Vecsmall([131072, 6]), Vecs
+mall([131072]), Vecsmall([131072]), Vecsmall([131072])]]], [0, 0, 0, 0]], a/
+x, (x + a)/(x + 6*a), 6*a^6 + 4*a^5 + 4*a^4 + 4*a^3 + 5*a^2 + 6*a + 3, 27451
+4, a, [x + (2*a^6 + 6*a^5 + 5*a^2 + 2*a + 1), 1; x + (5*a^6 + a^5 + 2*a^2 + 
+5*a), 1], [2*a^6 + 6*a^5 + 5*a^2 + 2*a, 5*a^6 + a^5 + 2*a^2 + 5*a + 6]~]
+? test(precprime(2^32),3)
+[a^2 + 3*a + 1, 3435973833*a^2 + 3435973833, 2863311528*a + 2863311528, a + 
+3435973833, 3579139409*a^2 + 2863311528, 3579139410*a + 3579139410, 1024*a +
+ 1024, 859832319*a + 859832319, 4294967290*a^2 + 4294967290*a + 4, 429496729
+0*a, a^2, 3885163399*a^2 + 2553150559*a + 523234686, a^2 + a + 1, a^2 + a + 
+1, 1, 4264202413*a^2 + 356078407*a + 3929909005, Mod(25, 4294967291), Mod(42
+94967290, 4294967291), Mod(1, 4294967291)*x^3 + Mod(1, 4294967291)*x^2 + Mod
+(4294967287, 4294967291)*x + Mod(1, 4294967291), Mod(1, 4294967291)*x^3 + Mo
+d(1, 4294967291)*x^2 + Mod(4294967287, 4294967291)*x + Mod(1, 4294967291), [
+a, a^2 + a + 4294967288, 4294967290*a^2 + 4294967289*a + 2]~, [x + (34444702
+3*a^2 + 1616586690*a + 252460086), 1; x + (3340051543*a^2 + 1627577691*a + 2
+021233148), 1; x^2 + (954915748*a^2 + 2667389600*a + 2273734143)*x + (816322
+992*a^2 + 830924795*a + 1995175223), 1; x^2 + (3950520268*a^2 + 2678380601*a
+ + 4042507205)*x + (1642837480*a^2 + 2548350348*a + 1670376662), 1], [0, 0, 
+0, a, 1, 0, 2*a, 4, 4294967290*a^2, 4294967243*a, 4294966427, 64*a^2 + 42949
+67035*a + 4294966923, 3618892287*a^2 + 1482857269*a + 1021597254, Vecsmall([
+4]), [a, [Vecsmall([131072, 0, 1296]), Vecsmall([131072, 46656]), [Vecsmall(
+[131072, 6]), Vecsmall([131072]), Vecsmall([131072]), Vecsmall([131072])]]],
+ [0, 0, 0, 0]], a/x, (x + a)/(x + 4294967290*a), 904302895*a^2 + 515242178*a
+ + 1663190800, 36893488070109691946, a, [x + (1365670490*a^2 + 3373566631*a 
++ 4083593885), 1; x + (2929296801*a^2 + 921400660*a + 211373407), 1], [13656
+70490*a^2 + 3373566631*a + 4083593884, 2929296801*a^2 + 921400660*a + 211373
+406]~]
+? test(nextprime(2^32),3)
+[a^2 + 3*a + 1, a^2 + 4294967310, 1431655771*a + 1431655771, a + 3435973849,
+ 3579139425*a^2 + 1431655772, 715827886*a + 715827886, 1024*a + 1024, 114504
+4996*a + 1145044996, a^2 + a + 4294967309, 4294967310*a, a^2, 264190711*a^2 
++ 2629464558*a + 2494776416, 2086193154*a^2 + 2086193154*a + 2086193154, 208
+6193154*a^2 + 2086193154*a + 2086193154, 2208774156, 996804783*a^2 + 2908221
+018*a + 1206110100, Mod(13, 4294967311), Mod(4294967310, 4294967311), Mod(1,
+ 4294967311)*x^3 + Mod(1, 4294967311)*x^2 + Mod(4294967309, 4294967311)*x + 
+Mod(4294967310, 4294967311), Mod(1, 4294967311)*x^3 + Mod(1, 4294967311)*x^2
+ + Mod(4294967309, 4294967311)*x + Mod(4294967310, 4294967311), [a, a^2 + 42
+94967309, 4294967310*a^2 + 4294967310*a + 1]~, [x^2 + (2086193155*a^2 + 1225
+81001)*x + 2086193154, 1; x^2 + (2208774157*a^2 + 4172386308)*x + 2208774156
+, 1; x^2 + (4294967310*a^2 + 2)*x + 1, 1], [0, 0, 0, a, 1, 0, 2*a, 4, 429496
+7310*a^2, 4294967263*a, 4294966447, 64*a^2 + 4294967183*a + 4294966815, 1484
+088443*a^2 + 1141114953*a + 4283364322, Vecsmall([4]), [a, [1296*a, 46656, [
+6, 0, 0, 0]]], [0, 0, 0, 0]], a/x, (x + a)/(x + 4294967310*a), 2425092325*a^
+2 + 865514605*a + 1871360732, 6148914735617846011, a, [x + (268392743*a^2 + 
+2459390605*a + 1304316255), 1; x + (4026574568*a^2 + 1835576706*a + 29906510
+57), 1], [268392743*a^2 + 2459390605*a + 1304316254, 4026574568*a^2 + 183557
+6706*a + 2990651056]~]
+? test2(p)=ffgen(x*Mod(1,p));g=ffprimroot(ffgen((x+1)*Mod(1,p)),&o);print([g,o]);fflog(g^17,g,o);
+? test2(2)
+[1, [1, matrix(0,2)]]
+0
+? test2(3)
+[2, [2, Mat([2, 1])]]
+1
+? test2(46744073709551653)
+[2, [46744073709551652, [2, 2; 3, 1; 7, 1; 37, 1; 1036513, 1; 14510113, 1]]]
+[]
+? test2(precprime(1<<32))
+[2, [4294967290, [2, 1; 5, 1; 19, 1; 22605091, 1]]]
+17
+? for(i=1,10,print(ffnbirred(11,i)));
+11
+55
+440
+3630
+32208
+295020
+2783880
+26793030
+261994040
+2593726344
+? for(i=1,10,print(ffnbirred(11,i,1)));
+11
+66
+506
+4136
+36344
+331364
+3115244
+29908274
+291902314
+2885628658
+? do(f,p,T)=centerlift(lift(polrootsff(f,p,T)));
+? do(x^3+x^2+x-1,3,t^3+t^2+t-1)
+[t, t^2 + 1, -t^2 - t + 1]~
+? t=ffgen(3^3,'t);do((x^3+x^2+x-1)*t^0,t.p,t.mod)
+[t, t^2 + 1, -t^2 - t + 1]~
+? polrootsff(x^4+1,2,y^2+y+1)
+[Mod(Mod(1, 2), Mod(1, 2)*y^2 + Mod(1, 2)*y + Mod(1, 2))]~
+? t=ffgen(7^4);fflog(t^6,t^2)
+3
+? t=ffgen(2^64)^((2^64-1)\5);1/t
+x^58 + x^57 + x^56 + x^52 + x^51 + x^49 + x^46 + x^45 + x^42 + x^39 + x^36 +
+ x^35 + x^32 + x^30 + x^29 + x^25 + x^23 + x^22 + x^21 + x^20 + x^19 + x^12 
++ x^8 + x^7 + x^6 + x^2
+? t=ffgen(('t^2+'t+1)*Mod(1,2));
+? factorff(x^12+t*x^10+x^6+(t+1)*x^2+1)
+
+[x + 1 6]
+
+[x + t 6]
+
+? polrootsff(x^2-x-ffgen((v^2+1)*Mod(1,3)))
+[]~
+? polrootsff(2*x+1,2,y)
+[]~
+? sqrt(Mod(-1,4296540161))
+Mod(1086811600, 4296540161)
+? sqrt(Mod(-1,18446744073944432641))
+Mod(6687681666819568403, 18446744073944432641)
+? centerlift(factorcantor(prod(i=-10,10,(x^2-i)),2^64+13)[,1])
+[x, x + 1, x + 2, x + 3, x + 248527397336721375, x + 2370518075556110396, x 
++ 2888582621843189425, x + 4741036151112220792, x + 5193293969518580612, x +
+ 6494187761904104278, x + 7111554226668331188, x + 7312212166335540022, x + 
+7562574061564804959, x - 7562574061564804959, x - 7312212166335540022, x - 7
+111554226668331188, x - 6494187761904104278, x - 5193293969518580612, x - 47
+41036151112220792, x - 2888582621843189425, x - 2370518075556110396, x - 248
+527397336721375, x - 3, x - 2, x - 1, x^2 + 2, x^2 + 3, x^2 + 8, x^2 + 10, x
+^2 - 10, x^2 - 8, x^2 - 3, x^2 - 2]~
+? #polrootsff(x^107+2*x^3+1,3,ffinit(3,107,'a))
+107
+? t=ffprimroot(ffgen(2^61));fflog(t^1234567891012345678,t)
+1234567891012345678
+? t=ffprimroot(ffgen(3^23));fflog(t^12345678910,t)
+12345678910
+? t=ffprimroot(ffgen(5^23));fflog(t^1234567891012345,t)
+1234567891012345
+? t=ffprimroot(ffgen(5^17));fflog(t^123456789101,t)
+123456789101
+? ffgen(x^2+x+Mod(1,3))
+  ***   at top-level: ffgen(x^2+x+Mod(1,3)
+  ***                 ^--------------------
+  *** ffgen: not an irreducible polynomial in ffgen: x^2 + x + 1.
+? conjvec(Mod(x,x^2+Mod(1,3)))
+[Mod(Mod(1, 3)*x, Mod(1, 3)*x^2 + Mod(1, 3)), Mod(Mod(2, 3)*x, Mod(1, 3)*x^2
+ + Mod(1, 3))]~
+? t=ffgen(5^4,'t);
+? factor((x^24-1)*t^0)
+
+[                x + 1 1]
+
+[                x + 2 1]
+
+[                x + 3 1]
+
+[                x + 4 1]
+
+[      x + (t^3 + 4*t) 1]
+
+[  x + (t^3 + 4*t + 1) 1]
+
+[  x + (t^3 + 4*t + 2) 1]
+
+[  x + (t^3 + 4*t + 3) 1]
+
+[  x + (t^3 + 4*t + 4) 1]
+
+[    x + (2*t^3 + 3*t) 1]
+
+[x + (2*t^3 + 3*t + 1) 1]
+
+[x + (2*t^3 + 3*t + 2) 1]
+
+[x + (2*t^3 + 3*t + 3) 1]
+
+[x + (2*t^3 + 3*t + 4) 1]
+
+[    x + (3*t^3 + 2*t) 1]
+
+[x + (3*t^3 + 2*t + 1) 1]
+
+[x + (3*t^3 + 2*t + 2) 1]
+
+[x + (3*t^3 + 2*t + 3) 1]
+
+[x + (3*t^3 + 2*t + 4) 1]
+
+[      x + (4*t^3 + t) 1]
+
+[  x + (4*t^3 + t + 1) 1]
+
+[  x + (4*t^3 + t + 2) 1]
+
+[  x + (4*t^3 + t + 3) 1]
+
+[  x + (4*t^3 + t + 4) 1]
+
+? factorff(Pol(0),t.p,t.mod)
+
+[0 1]
+
+? factorff(Pol(1),t.p,t.mod)
+[;]
+? factorff(x^4-t,t.p,t.mod)
+
+[Mod(Mod(1, 5), Mod(1, 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t 
++ Mod(3, 5))*x^4 + Mod(Mod(0, 5), Mod(1, 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*
+t^2 + Mod(1, 5)*t + Mod(3, 5))*x^3 + Mod(Mod(0, 5), Mod(1, 5)*t^4 + Mod(1, 5
+)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t + Mod(3, 5))*x^2 + Mod(Mod(0, 5), Mod(1,
+ 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t + Mod(3, 5))*x + Mod(M
+od(4, 5)*t, Mod(1, 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t + Mo
+d(3, 5)) 1]
+
+? test(q)=my(t=ffgen(q,'t),m=[t,t^2,1+t^3;1+t,1+t^2,1+t^3]);print(matker(m));print(matimage(m));print(matrank(m));my(M=[t,2*t^0,3*t^0;t,t^2,1+t^3;1+t,1+t^2,1+t^3]);print(matdet(M));print(M^(-1)*M);my(v=[t^0,t^1,t^2]~);print(M*v);
+? test(2^5)
+[t^4 + t^3; t^4 + t^3; 1]
+[t, t^2; t + 1, t^2 + 1]
+2
+t^4 + t^2
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+[t^2 + t, t^4 + t^3 + 1, t^4 + t^3 + t]~
+? test(7^5)
+[3*t^4 + 5*t^3 + 6*t^2 + 2*t; 4*t^4 + 2*t^3 + t^2 + 5*t; 1]
+[t, t^2; t + 1, t^2 + 1]
+2
+6*t^4 + 2*t^3 + 4*t^2 + 2*t + 2
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+[3*t^2 + 3*t, 6*t^4 + 5*t^3 + 4*t^2 + 5*t + 6, 6*t^4 + 5*t^3 + 4*t^2 + 6*t]~
+? test((2^64+13)^5)
+[3*t^4 + 5*t^3 + 18446744073709551621*t^2 + 18446744073709551617*t; 18446744
+073709551626*t^4 + 18446744073709551624*t^3 + 8*t^2 + 12*t; 1]
+[t, t^2; t + 1, t^2 + 1]
+2
+18446744073709551628*t^4 + 2*t^3 + 18446744073709551626*t^2 + 2*t + 2
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+[3*t^2 + 3*t, 18446744073709551628*t^4 + 5*t^3 + 4*t^2 + 1844674407370955162
+7*t + 18446744073709551628, 18446744073709551628*t^4 + 5*t^3 + 4*t^2 + 18446
+744073709551628*t]~
+? p=2^64+13;g=ffprimroot(ffgen(p^2),&o);a=2*g^0;
+? v=[I,-1,Mat(1),matid(2)/2];
+? for(i=1,#v,print(iferr(fflog(a,g,v[i]),E,E)));
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_COMPLEX).")
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_INT).")
+error("incorrect type in factorback [not a factorization] (t_MAT).")
+error("incorrect type in factorback [not an exponent vector] (t_COL).")
+? g^fflog(a,g,o)==a
+1
+? print("Total time spent: ",gettime);
+Total time spent: 2356
diff --git a/src/test/32/ffisom b/src/test/32/ffisom
new file mode 100644
index 0000000..837b2dd
--- /dev/null
+++ b/src/test/32/ffisom
@@ -0,0 +1,47 @@
+-------------e=0--------------
+[0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0]
+-------------e=1--------------
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+-------------e=2--------------
+[0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+-------------e=3--------------
+[0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+-------------e>=4--------------
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+----------large p---------------
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0]
+----------huge p---------------
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+[0, 0, 0]
+Total time spent: 300
diff --git a/src/test/32/for b/src/test/32/for
new file mode 100644
index 0000000..6cb8b27
--- /dev/null
+++ b/src/test/32/for
@@ -0,0 +1,17 @@
+18446744073709551614
+18446744073709551615
+18446744073709551616
+18446744073709551617
+18446744073709551618
+-18446744073709551618
+-18446744073709551617
+-18446744073709551616
+-18446744073709551615
+-18446744073709551614
+4294967279
+4294967291
+4294967311
+18446744073709551557
+18446744073709551629
+18446744073709551653
+Total time spent: 0
diff --git a/src/test/32/galois b/src/test/32/galois
new file mode 100644
index 0000000..5c900c8
--- /dev/null
+++ b/src/test/32/galois
@@ -0,0 +1,220 @@
+[2, -1, 1, "S2"]1
+[3, 1, 1, "A3"]1
+[6, -1, 2, "S3"]1
+[4, -1, 1, "C(4) = 4"]1
+[4, 1, 2, "E(4) = 2[x]2"]1
+[8, -1, 3, "D(4)"]1
+[12, 1, 4, "A4"]1
+[24, -1, 5, "S4"]1
+[5, 1, 1, "C(5) = 5"]1
+[10, 1, 2, "D(5) = 5:2"]1
+[20, -1, 3, "F(5) = 5:4"]1
+[60, 1, 4, "A5"]1
+[120, -1, 5, "S5"]1
+[6, -1, 1, "C(6) = 6 = 3[x]2"]1
+[6, -1, 2, "D_6(6) = [3]2"]1
+[12, -1, 3, "D(6) = S(3)[x]2"]1
+[12, 1, 4, "A_4(6) = [2^2]3"]1
+[18, -1, 5, "F_18(6) = [3^2]2 = 3 wr 2"]1
+[24, -1, 6, "2A_4(6) = [2^3]3 = 2 wr 3"]1
+[24, 1, 7, "S_4(6d) = [2^2]S(3)"]1
+[24, -1, 8, "S_4(6c) = 1/2[2^3]S(3)"]1
+[36, -1, 9, "F_18(6):2 = [1/2.S(3)^2]2"]1
+[36, 1, 10, "F_36(6) = 1/2[S(3)^2]2"]1
+[48, -1, 11, "2S_4(6) = [2^3]S(3) = 2 wr S(3)"]1
+[60, 1, 12, "L(6) = PSL(2,5) = A_5(6)"]1
+[72, -1, 13, "F_36(6):2 = [S(3)^2]2 = S(3) wr 2"]1
+[120, -1, 14, "L(6):2 = PGL(2,5) = S_5(6)"]1
+[360, 1, 15, "A6"]1
+[720, -1, 16, "S6"]1
+[7, 1, 1, "C(7) = 7"]1
+[14, -1, 2, "D(7) = 7:2"]1
+[21, 1, 3, "F_21(7) = 7:3"]1
+[42, -1, 4, "F_42(7) = 7:6"]1
+[168, 1, 5, "L(7) = L(3,2)"]1
+[2520, 1, 6, "A7"]1
+[5040, -1, 7, "S7"]1
+[8, -1, 1, "C(8)=8"]1
+[8, 1, 2, "4[x]2"]1
+[8, 1, 3, "E(8)=2[x]2[x]2"]1
+[8, 1, 4, "D_8(8)=[4]2"]1
+[8, 1, 5, "Q_8(8)"]1
+[16, -1, 6, "D(8)"]1
+[16, -1, 7, "1/2[2^3]4"]1
+[16, -1, 8, "2D_8(8)=[D(4)]2"]1
+[16, 1, 9, "E(8):2=D(4)[x]2"]1
+[16, 1, 10, "[2^2]4"]1
+[16, 1, 11, "1/2[2^3]E(4)=Q_8:2"]1
+[24, 1, 12, "2A_4(8)=[2]A(4)=SL(2,3)"]1
+[24, 1, 13, "E(8):3=A(4)[x]2"]1
+[24, 1, 14, "S(4)[1/2]2=1/2(S_4[x]2)"]1
+[32, -1, 15, "[1/4.cD(4)^2]2"]1
+[32, -1, 16, "1/2[2^4]4"]1
+[32, -1, 17, "[4^2]2"]1
+[32, 1, 18, "E(8):E_4=[2^2]D(4)"]1
+[32, 1, 19, "E(8):4=[1/4.eD(4)^2]2"]1
+[32, 1, 20, "[2^3]4"]1
+[32, -1, 21, "1/2[2^4]E(4)=[1/4.dD(4)^2]2"]1
+[32, 1, 22, "E(8):D_4=[2^3]2^2"]1
+[48, -1, 23, "2S_4(8)=GL(2,3)"]1
+[48, 1, 24, "E(8):D_6=S(4)[x]2"]1
+[56, 1, 25, "E(8):7=F_56(8)"]1
+[64, -1, 26, "1/2[2^4]eD(4)"]1
+[64, -1, 27, "[2^4]4"]1
+[64, -1, 28, "1/2[2^4]dD(4)"]1
+[64, 1, 29, "E(8):D_8=[2^3]D(4)"]1
+[64, -1, 30, "1/2[2^4]cD(4)"]1
+[64, -1, 31, "[2^4]E(4)"]1
+[96, 1, 32, "[2^3]A(4)"]1
+[96, 1, 33, "E(8):A_4=[1/3.A(4)^2]2=E(4):6"]1
+[96, 1, 34, "1/2[E(4)^2:S_3]2=E(4)^2:D_6"]1
+[128, -1, 35, "[2^4]D(4)"]1
+[168, 1, 36, "E(8):F_21"]1
+[168, 1, 37, "L(8)=PSL(2,7)"]1
+[192, -1, 38, "[2^4]A(4)"]1
+[192, 1, 39, "[2^3]S(4)"]1
+[192, -1, 40, "1/2[2^4]S(4)"]1
+[192, 1, 41, "E(8):S_4=[E(4)^2:S_3]2=E(4)^2:D_12"]1
+[288, 1, 42, "[A(4)^2]2"]1
+[336, -1, 43, "L(8):2=PGL(2,7)"]1
+[384, -1, 44, "[2^4]S(4)"]1
+[576, 1, 45, "[1/2.S(4)^2]2"]1
+[576, -1, 46, "1/2[S(4)^2]2"]1
+[1152, -1, 47, "[S(4)^2]2"]1
+[1344, 1, 48, "E(8):L_7=AL(8)"]1
+[20160, 1, 49, "A8"]1
+[40320, -1, 50, "S8"]1
+[9, 1, 1, "C(9)=9"]1
+[9, 1, 2, "E(9)=3[x]3"]1
+[18, 1, 3, "D(9)=9:2"]1
+[18, -1, 4, "S(3)[x]3"]1
+[18, 1, 5, "S(3)[1/2]S(3)=3^2:2"]1
+[27, 1, 6, "1/3[3^3]3"]1
+[27, 1, 7, "E(9):3=[3^2]3"]1
+[36, -1, 8, "S(3)[x]S(3)=E(9):D_4"]1
+[36, 1, 9, "E(9):4"]1
+[54, 1, 10, "[3^2]S(3)_6"]1
+[54, 1, 11, "E(9):6=1/2[3^2:2]S(3)"]1
+[54, -1, 12, "[3^2]S(3)"]1
+[54, -1, 13, "E(9):D_6=[3^2:2]3=[1/2.S(3)^2]3"]1
+[72, 1, 14, "M(9)=E(9):Q_8"]1
+[72, -1, 15, "E(9):8"]1
+[72, -1, 16, "E(9):D_8"]1
+[81, 1, 17, "[3^3]3=3wr3"]1
+[108, -1, 18, "E(9):D_12=[3^2:2]S(3)=[1/2.S(3)^2]S(3)"]1
+[144, -1, 19, "E(9):2D_8"]1
+[162, -1, 20, "[3^3]S(3)=3wrS(3)"]1
+[162, 1, 21, "1/2.[3^3:2]S(3)"]1
+[162, -1, 22, "[3^3:2]3"]1
+[216, 1, 23, "E(9):2A_4"]1
+[324, -1, 24, "[3^3:2]S(3)"]1
+[324, 1, 25, "[1/2.S(3)^3]3"]1
+[432, -1, 26, "E(9):2S_4"]1
+[504, 1, 27, "L(9)=PSL(2,8)"]1
+[648, -1, 28, "[S(3)^3]3=S(3)wr3"]1
+[648, -1, 29, "[1/2.S(3)^3]S(3)"]1
+[648, 1, 30, "1/2[S(3)^3]S(3)"]1
+[1296, -1, 31, "[S(3)^3]S(3)=S(3)wrS(3)"]1
+[1512, 1, 32, "L(9):3=P|L(2,8)"]1
+[181440, 1, 33, "A9"]1
+[362880, -1, 34, "S9"]1
+[10, -1, 1, "C(10)=5[x]2"]1
+[10, -1, 2, "D(10)=5:2"]1
+[20, -1, 3, "D_10(10)=[D(5)]2"]1
+[20, -1, 4, "1/2[F(5)]2"]1
+[40, -1, 5, "F(5)[x]2"]1
+[50, -1, 6, "[5^2]2"]1
+[60, 1, 7, "A_5(10)"]1
+[80, 1, 8, "[2^4]5"]1
+[100, -1, 9, "[1/2.D(5)^2]2"]1
+[100, -1, 10, "1/2[D(5)^2]2"]1
+[120, -1, 11, "A(5)[x]2"]1
+[120, -1, 12, "1/2[S(5)]2=S_5(10a)"]1
+[120, -1, 13, "S_5(10d)"]1
+[160, -1, 14, "[2^5]5"]1
+[160, 1, 15, "[2^4]D(5)"]1
+[160, -1, 16, "1/2[2^5]D(5)"]1
+[200, -1, 17, "[5^2:4]2"]1
+[200, 1, 18, "[5^2:4]2_2"]1
+[200, -1, 19, "[5^2:4_2]2"]1
+[200, -1, 20, "[5^2:4_2]2_2"]1
+[200, -1, 21, "[D(5)^2]2"]1
+[240, -1, 22, "S(5)[x]2"]1
+[320, -1, 23, "[2^5]D(5)"]1
+[320, 1, 24, "[2^4]F(5)"]1
+[320, -1, 25, "1/2[2^5]F(5)"]1
+[360, 1, 26, "L(10)=PSL(2,9)"]1
+[400, -1, 27, "[1/2.F(5)^2]2"]1
+[400, 1, 28, "1/2[F(5)^2]2"]1
+[640, -1, 29, "[2^5]F(5)"]1
+[720, -1, 30, "L(10):2=PGL(2,9)"]1
+[720, 1, 31, "M(10)=L(10)'2"]1
+[720, -1, 32, "S_6(10)=L(10):2"]1
+[800, -1, 33, "[F(5)^2]2"]1
+[960, 1, 34, "[2^4]A(5)"]1
+[1440, -1, 35, "L(10).2^2=P|L(2,9)"]1
+[1920, -1, 36, "[2^5]A(5)"]1
+[1920, 1, 37, "[2^4]S(5)"]1
+[1920, -1, 38, "1/2[2^5]S(5)"]1
+[3840, -1, 39, "[2^5]S(5)"]1
+[7200, -1, 40, "[A(5)^2]2"]1
+[14400, -1, 41, "[1/2.S(5)^2]2=[A(5):2]2"]1
+[14400, 1, 42, "1/2[S(5)^2]2"]1
+[28800, -1, 43, "[S(5)^2]2"]1
+[1814400, 1, 44, "A10"]1
+[3628800, -1, 45, "S10"]1
+[11, 1, 1, "C(11)=11"]1
+[22, -1, 2, "D(11)=11:2"]1
+[55, 1, 3, "F_55(11)=11:5"]1
+[110, -1, 4, "F_110(11)=11:10"]1
+[660, 1, 5, "L(11)=PSL(2,11)(11)"]1
+[7920, 1, 6, "M(11)"]1
+[19958400, 1, 7, "A11"]1
+[39916800, -1, 8, "S11"]1
+[2, -1, 1, "S2"]1
+[3, 1, 1, "A3"]1
+[6, -1, 1, "S3"]1
+[4, -1, 1, "C(4) = 4"]1
+[4, 1, 1, "E(4) = 2[x]2"]1
+[8, -1, 1, "D(4)"]1
+[12, 1, 1, "A4"]1
+[24, -1, 1, "S4"]1
+[5, 1, 1, "C(5) = 5"]1
+[10, 1, 1, "D(5) = 5:2"]1
+[20, -1, 1, "F(5) = 5:4"]1
+[60, 1, 1, "A5"]1
+[120, -1, 1, "S5"]1
+[6, -1, 1, "C(6) = 6 = 3[x]2"]1
+[6, -1, 2, "D_6(6) = [3]2"]1
+[12, -1, 1, "D(6) = S(3)[x]2"]1
+[12, 1, 1, "A_4(6) = [2^2]3"]1
+[18, -1, 1, "F_18(6) = [3^2]2 = 3 wr 2"]1
+[24, -1, 2, "2A_4(6) = [2^3]3 = 2 wr 3"]1
+[24, 1, 1, "S_4(6d) = [2^2]S(3)"]1
+[24, -1, 1, "S_4(6c) = 1/2[2^3]S(3)"]1
+[36, -1, 1, "F_18(6):2 = [1/2.S(3)^2]2"]1
+[36, 1, 1, "F_36(6) = 1/2[S(3)^2]2"]1
+[48, -1, 1, "2S_4(6) = [2^3]S(3) = 2 wr S(3)"]1
+[60, 1, 1, "L(6) = PSL(2,5) = A_5(6)"]1
+[72, -1, 1, "F_36(6):2 = [S(3)^2]2 = S(3) wr 2"]1
+[120, -1, 1, "L(6):2 = PGL(2,5) = S_5(6)"]1
+[360, 1, 1, "A6"]1
+[720, -1, 1, "S6"]1
+[7, 1, 1, "C(7) = 7"]1
+[14, -1, 1, "D(7) = 7:2"]1
+[21, 1, 1, "F_21(7) = 7:3"]1
+[42, -1, 1, "F_42(7) = 7:6"]1
+[168, 1, 1, "L(7) = L(3,2)"]1
+[2520, 1, 1, "A7"]1
+[5040, -1, 1, "S7"]1
+[16, -1, 7, "1/2[2^3]4"]
+[10, -1, 1, "C(10)=5[x]2"]
+[32, -1, 21, "1/2[2^4]E(4)=[1/4.dD(4)^2]2"]
+[32, -1, 15, "[1/4.cD(4)^2]2"]
+[8, 1, 2, "4[x]2"]
+[16, -1, 7, "1/2[2^3]4"]
+[16, -1, 7, "1/2[2^3]4"]
+[16, -1, 8, "2D_8(8)=[D(4)]2"]
+[8, 1, 2, "4[x]2"]
+[39916800, -1, 8, "S11"]
+Total time spent: 7552
diff --git a/src/test/32/galoisinit b/src/test/32/galoisinit
new file mode 100644
index 0000000..69bd633
--- /dev/null
+++ b/src/test/32/galoisinit
@@ -0,0 +1,102 @@
+[2, 1]
+[16, 10]
+[20, 3]
+[24, 12]
+[4, 2]
+[4, 2]
+[4, 2]
+[4, 2]
+[12, 3]
+[24, 12]
+[54, 11]
+[54, 14]
+[64, 66]
+[8, 5]
+[4, 2]
+[14, 1]
+  ***   at top-level: do(x^48+688253440*x^
+  ***                 ^--------------------
+  ***   in function do: galoisidentify(galoi
+  ***                   ^--------------------
+  *** galoisidentify: incorrect type in checkgal (t_INT).
+[x]~
+[-x, x]~
+[-x, x, -x^3, x^3]~
+
+[11  0  0 10 7 1]
+
+[ 0 11  0  6 1 1]
+
+[ 0  0 11  7 1 5]
+
+[ 0  0  0  1 0 0]
+
+[ 0  0  0  0 1 0]
+
+[ 0  0  0  0 0 1]
+
+[11, [9, 10, 5, 10, 6, 10]~, 1, 3, [10, 0, 0, -1, -4, 1; 4, 10, 0, -1, -5, -
+3; 5, 4, 10, -1, -5, -4; 1, 5, 4, 9, -5, -4; 0, 1, 5, 3, 5, -4; 0, 0, 1, 4, 
+-1, 6]]
+Mod(x^4 + x^2, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)
+1/2
+[1, 1]
+[1, 1/2]
+[1, Mod(x^2, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)]
+[1, Mod(x^2, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)]
+[1, [0, 0, 0, 0, 0, 0]~]
+[1, [;]]
+[1, [1, 2; Mod(x^2, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1), 3]]
+"Group((1, 5)(2, 10)(3, 11)(4, 6)(7, 9)(8, 12), (1, 8)(2, 3)(4, 7)(5, 12)(6,
+ 9)(10, 11), (1, 2, 7)(3, 9, 12)(4, 5, 11)(6, 8, 10))"
+"PermutationGroup<12|[5, 10, 11, 6, 1, 4, 9, 12, 7, 2, 3, 8], [8, 3, 2, 7, 1
+2, 9, 4, 1, 6, 11, 10, 5], [2, 7, 9, 5, 11, 8, 1, 10, 12, 6, 4, 3]>"
+[[[Vecsmall([7, 4, 11, 2, 10, 8, 1, 6, 12, 5, 3, 9]), Vecsmall([3, 7, 2, 1, 
+6, 12, 11, 9, 5, 8, 4, 10]), Vecsmall([5, 8, 9, 6, 7, 2, 10, 4, 11, 1, 12, 3
+])], Vecsmall([2, 3, 2])], [[Vecsmall([7, 4, 11, 2, 10, 8, 1, 6, 12, 5, 3, 9
+]), Vecsmall([3, 7, 2, 1, 6, 12, 11, 9, 5, 8, 4, 10])], Vecsmall([2, 3])], [
+[Vecsmall([7, 4, 11, 2, 10, 8, 1, 6, 12, 5, 3, 9]), Vecsmall([5, 8, 9, 6, 7,
+ 2, 10, 4, 11, 1, 12, 3])], Vecsmall([2, 2])], [[Vecsmall([7, 4, 11, 2, 10, 
+8, 1, 6, 12, 5, 3, 9]), Vecsmall([12, 5, 6, 10, 4, 11, 9, 3, 1, 2, 8, 7])], 
+Vecsmall([2, 2])], [[Vecsmall([7, 4, 11, 2, 10, 8, 1, 6, 12, 5, 3, 9]), Vecs
+mall([6, 9, 5, 12, 11, 7, 8, 1, 4, 3, 10, 2])], Vecsmall([2, 2])], [[Vecsmal
+l([7, 4, 11, 2, 10, 8, 1, 6, 12, 5, 3, 9])], Vecsmall([2])], [[Vecsmall([11,
+ 1, 4, 7, 8, 9, 3, 12, 10, 6, 2, 5])], Vecsmall([3])], [[], Vecsmall([])]]
+[1, 1, 0, 0, 0, 1, 1, 1]
+[0, [2, 1; 0, 3], [2, 1; 0, 2], [2, 1; 0, 2], [2, 1; 0, 2], Mat(2), Mat(3), 
+[;]]
+[0, 1, 1, 1, 1, 1, 1, 1]
+[0, [6], [4], [4], [4], [2], [3], []]
+[x^12 - 24*x^10 - 10*x^9 + 216*x^8 + 180*x^7 - 844*x^6 - 1080*x^5 + 1056*x^4
+ + 2200*x^3 + 720*x^2 - 240*x - 80, x^2 - 30*x + 180, x^4 - 30*x^3 + 180*x^2
+ + 1080*x - 6480, x^6 - 18*x^4 + 20*x^3 + 36*x^2 - 60*x + 20, x^4 - 30*x^3 +
+ 180*x^2 + 1080*x - 6480, x^2 - 30*x + 180, x^3 - 12*x + 14, x^3 - 12*x - 4,
+ x^3 - 30*x + 50, x^3 - 12*x + 14, x^3 - 12*x - 4, x^3 - 30*x + 50]
+[x^2 - 30*x + 180, Mod(3/4*x^9 - 27/2*x^7 - 9/2*x^6 + 81*x^5 + 54*x^4 - 162*
+x^3 - 162*x^2 + 30, x^12 - 24*x^10 - 10*x^9 + 216*x^8 + 180*x^7 - 844*x^6 - 
+1080*x^5 + 1056*x^4 + 2200*x^3 + 720*x^2 - 240*x - 80)]
+[x^2 - 30*x + 180, Mod(3/4*x^9 - 27/2*x^7 - 9/2*x^6 + 81*x^5 + 54*x^4 - 162*
+x^3 - 162*x^2 + 30, x^12 - 24*x^10 - 10*x^9 + 216*x^8 + 180*x^7 - 844*x^6 - 
+1080*x^5 + 1056*x^4 + 2200*x^3 + 720*x^2 - 240*x - 80), [x^6 - 12*x^4 - 1/3*
+y*x^3 + 36*x^2 + 2*y*x + (4/3*y - 20), x^6 - 12*x^4 + (1/3*y - 10)*x^3 + 36*
+x^2 + (-2*y + 60)*x + (-4/3*y + 20)]]
+x^2 + x - 4
+[x^8 - x^6 + x^4 - x^2 + 1, [41, 2, 1681], [487, 471, 894, 207, 1194, 1210, 
+787, 1474], [183, 1676, 1421, 923, 183, 1676, 1421, 923; 1136, 1035, 545, 64
+6, 545, 646, 1136, 1035; 1238, 928, 443, 753, 1238, 928, 443, 753; 1007, 121
+9, 1108, 28, 674, 462, 573, 1653; 941, 1493, 1426, 1183, 941, 1493, 1426, 11
+83; 927, 253, 1472, 899, 754, 1428, 209, 782; 1493, 1426, 1183, 941, 1493, 1
+426, 1183, 941; 1653, 674, 462, 573, 28, 1007, 1219, 1108], 1, [Vecsmall([1,
+ 2, 3, 4, 5, 6, 7, 8]), Vecsmall([2, 3, 4, 1, 6, 7, 8, 5]), Vecsmall([3, 4, 
+1, 2, 7, 8, 5, 6]), Vecsmall([4, 1, 2, 3, 8, 5, 6, 7]), Vecsmall([5, 6, 7, 8
+, 1, 2, 3, 4]), Vecsmall([6, 7, 8, 5, 2, 3, 4, 1]), Vecsmall([7, 8, 5, 6, 3,
+ 4, 1, 2]), Vecsmall([8, 5, 6, 7, 4, 1, 2, 3])], [Vecsmall([2, 3, 4, 1, 6, 7
+, 8, 5]), Vecsmall([5, 6, 7, 8, 1, 2, 3, 4])], Vecsmall([4, 2])]
+[[x, 0], [x^2 + 1, Mod(-x^5, x^8 - x^6 + x^4 - x^2 + 1)], [x^2 - 2*x - 4, Mo
+d(-2*x^6 + 2*x^4 + 2, x^8 - x^6 + x^4 - x^2 + 1)], [x^2 + 5, Mod(2*x^7 - x^5
+ + 2*x^3, x^8 - x^6 + x^4 - x^2 + 1)], [x^4 + 3*x^2 + 1, Mod(x^7 - x^5 + x^3
+, x^8 - x^6 + x^4 - x^2 + 1)], [x^4 - 2*x^3 + 4*x^2 - 8*x + 16, Mod(2*x^2, x
+^8 - x^6 + x^4 - x^2 + 1)], [x^4 - 5*x^2 + 5, Mod(-x^7 + x^5 - x^3 + 2*x, x^
+8 - x^6 + x^4 - x^2 + 1)], [x^8 - x^6 + x^4 - x^2 + 1, Mod(x, x^8 - x^6 + x^
+4 - x^2 + 1)]]
+Total time spent: 2392
diff --git a/src/test/32/galpol b/src/test/32/galpol
new file mode 100644
index 0000000..5da6865
--- /dev/null
+++ b/src/test/32/galpol
@@ -0,0 +1,22 @@
+5
+[x^8 - x^7 - 7*x^6 + 6*x^5 + 15*x^4 - 10*x^3 - 10*x^2 + 4*x + 1, 1]
+[x^8 - 7*x^6 + 14*x^4 - 8*x^2 + 1, 1]
+[x^8 - 4*x^7 - 8*x^6 + 24*x^5 + 30*x^4 - 16*x^3 - 20*x^2 + 2, 3]
+[x^8 - 12*x^6 + 36*x^4 - 36*x^2 + 9, 3]
+[x^8 - 12*x^6 + 23*x^4 - 12*x^2 + 1, 7]
+[x^8 + 8*x^6 + 20*x^4 + 16*x^2 + 2, 1]
+[x^8 - x^7 + x^5 - x^4 + x^3 - x + 1, 1]
+[x^8 + 3*x^4 + 1, 1]
+[x^8 + 12*x^6 + 36*x^4 + 36*x^2 + 9, 3]
+[x^8 - x^4 + 1, 1]
+  ***   at top-level: galoisgetpol(8,6)
+  ***                 ^-----------------
+  *** galoisgetpol: domain error in galoisgetpol: group index > 5
+  ***   at top-level: galoisgetpol(3,1,3)
+  ***                 ^-------------------
+  *** galoisgetpol: invalid flag in galoisgetpol.
+  ***   at top-level: galoisgetpol(3,1,2)
+  ***                 ^-------------------
+  *** galoisgetpol: domain error in galoisgetpol: s > 1
+[96, 161]
+Total time spent: 3624
diff --git a/src/test/32/gamma b/src/test/32/gamma
new file mode 100644
index 0000000..94ebee7
--- /dev/null
+++ b/src/test/32/gamma
@@ -0,0 +1,86 @@
+(0.65296549642016672783864624794608469715 + 0.343065839816545357588735986978
+31148676*I) + (0.19044897540645184469078131473790885364 + 0.5805524673194769
+2349794265298068695525*I)*x + (0.090862784286733058570355592072096462602 + 0
+.21088392899265350361451872767550590408*I)*x^2 + (0.034253752000523576920016
+074597694397590 + 0.15168994440796279268955277197772465641*I)*x^3 + (-0.0093
+139210540785894159197859484392067433 + 0.03306465179643991397682862055127742
+8299*I)*x^4 + (0.0066762623841895560506752315759505427095 + 0.01516404667566
+6586697065188398143396917*I)*x^5 + O(x^6)
+1 - 0.57721566490153286060651209008240243104*x + 0.9890559953279725553953956
+5150063470794*x^2 - 0.90747907608088628901656016735627511493*x^3 + 0.9817280
+8683440018733638029402185085036*x^4 - 0.981995068903145202104701413791374675
+51*x^5 + O(x^6)
+0.50000000000000000000000000000000000000*x^-1 + 0.46139216754923356969674395
+495879878448 + 0.93661624898783663224281375818851553069*x + 0.72048875166669
+501900756857612523634633*x^2 + 1.1032890464233243060581361321045221793*x^3 +
+ O(x^4)
+(-0.30434960902188368417660077077485938103 + 0.48375784292991511172812918802
+297918039*I) + (0.59465032062247697727187848272191072247 + 0.576674047468581
+17413405079475000049045*I)*x + (0.23150004831138189314916325909209289721 - 0
+.14711677137965943279150680857845149795*I)*x^2 + (-0.02190784473534413804389
+4242836567535805 + 0.044442141724177702067115896271895013565*I)*x^3 + (-0.00
+095310203972899907556180572534221362410 - 0.01322027411091502743411636095975
+8682565*I)*x^4 + (0.0023292347420337443292606199295988510753 + 0.00349489957
+19835964464884744063791902941*I)*x^5 + O(x^6)
+-0.57721566490153286060651209008240243104*x + 0.8224670334241132182362075833
+2301259461*x^2 - 0.40068563438653142846657938717048333025*x^3 + 0.2705808084
+2778454787900092413529197569*x^4 - 0.20738555102867398526627309729140683361*
+x^5 + O(x^6)
+  *** lngamma: Warning: normalizing a series with 0 leading term.
+  ***   at top-level: lngamma(-2+x)
+  ***                 ^-------------
+  *** lngamma: domain error in intformal: residue(series, pole) != 0
+(0.59465032062247697727187848272191072247 + 0.576674047468581174134050794750
+00049045*I) + (0.46300009662276378629832651818418579441 - 0.2942335427593188
+6558301361715690299591*I)*x + (-0.065723534206032414131682728509702607416 + 
+0.13332642517253310620134768881568504070*I)*x^2 + (-0.0038124081589159963022
+472229013688544964 - 0.052881096443660109736465443839034730260*I)*x^3 + (0.0
+11646173710168721646303099647994255377 + 0.017474497859917982232442372031895
+951470*I)*x^4 + (-0.0077757069690405743408170730434310459472 - 0.00403456423
+41327827815714280332882428196*I)*x^5 + O(x^6)
+-0.57721566490153286060651209008240243104 + 1.644934066848226436472415166646
+0251892*x - 1.2020569031595942853997381615114499908*x^2 + 1.0823232337111381
+915160036965411679028*x^3 - 1.0369277551433699263313654864570341681*x^4 + 1.
+0173430619844491397145179297909205279*x^5 + O(x^6)
+  *** psi: Warning: normalizing a series with 0 leading term.
+-1.0000000000000000000000000000000000000*x^-1 + 0.92278433509846713939348790
+991759756896 + 2.8949340668482264364724151666460251892*x - 0.077056903159594
+285399738161511449990765*x^2 + 2.1448232337111381915160036965411679028*x^3 +
+ O(x^4)
+-1.0000000000000000000000000000000000000*x^-1 - 0.57721566490153286060651209
+008240243104 + 1.6449340668482264364724151666460251892*x - 1.202056903159594
+2853997381615114499908*x^2 + 1.0823232337111381915160036965411679028*x^3 - 1
+.0369277551433699263313654864570341681*x^4 + O(x^5)
+x^-1 - 0.57721566490153286060651209008240243104 + 0.989055995327972555395395
+65150063470794*x - 0.90747907608088628901656016735627511493*x^2 + 0.98172808
+683440018733638029402185085036*x^3 - 0.9819950689031452021047014137913746755
+1*x^4 + O(x^5)
+4.0238726007709377354370243392300398572 E2564
+277.25887222397812376689284858327062723
+0.70315664064524318722569033366791109947
+170141183460469231740910675752738881536
+1001.0000000000000000000000000000000000
+8.4592930575197658134779513864578051837 E92
+8.4592930575197658134779513864578051837 E92 + 417.27460269708707626917373711
+782229398*I
+799877009219260410589.21059353880333769 + 88.7228391116729996053515014380223
+25492*I
+-864.73828787067971564321683481711497423 - 631.46012337154844093099132003918
+007972*I
+3.2007257594901922498857741835634344245 E867
+3.0616681090421088936612867355651954590 E867 - 9.193770672812704213454512890
+0883765323 E866*I
+8459293057519765813477951386457805183660969095271154721136672171659780563637
+16339197618169691.8420416798032146865778 + 417.27460269708707626917373711782
+2293981451080884873662980649365715022960425756218794729722851598595227020005
+2467288*I
+8459293057519765813477951386457805183660969095271154721136672171659780563637
+16339197618169691.8420416798032146865778 + 41727.460269708707626917373711782
+2293981451080884873662980649365715022960425756218794729722851598595227020005
+2467288*I
+8459293057519765813477951386457805183660969095271154721136672171659780563637
+16339197618169691.8420416798032146865778407117373385742831330551204346169684
+432 + 417274.602697087076269173737117822293981451080884873662980649365715022
+9604257562187947297228515985952270200052467287642139293693997776338197086953
+142287383*I
+Total time spent: 12
diff --git a/src/test/32/gcdext b/src/test/32/gcdext
new file mode 100644
index 0000000..d64db0d
--- /dev/null
+++ b/src/test/32/gcdext
@@ -0,0 +1,113 @@
+[0, 0, 0]
+[0, 0, 0]
+[0, 0, 0]
+[0, 1, 2]
+[0, 3 + 2*5 + 2*5^2 + O(5^3), 1]
+[0, 1, x]
+[0, 0, 0]
+[0, 0, 0]
+[0, 0, 0]
+[0, 1/2, 1]
+[0, 3 + 2*5 + 2*5^2 + O(5^3), 1]
+[0, 1, x]
+[0, 0, 0]
+[0, 0, 0]
+[0, 0, 0]
+[0, 1/2, 1]
+[0, 3 + 2*5 + 2*5^2 + O(5^3), 1]
+[0, 1, x]
+[1, 0, 2]
+[1/2, 0, 1]
+[1/2, 0, 1]
+[0, 1, 2]
+[1/2, 0, 1]
+[1/2, 0, 1]
+[3 + 2*5 + 2*5^2 + O(5^3), 0, 1]
+[3 + 2*5 + 2*5^2 + O(5^3), 0, 1]
+[3 + 2*5 + 2*5^2 + O(5^3), 0, 1]
+[3 + 2*5 + 2*5^2 + O(5^3), 0, 1]
+[3 + 2*5 + 2*5^2 + O(5^3), 0, 1]
+[3 + 2*5 + 2*5^2 + O(5^3), 0, 1]
+[1, 0, x]
+[1, 0, x]
+[1, 0, x]
+[0, 1/2, 1]
+[0, 3 + 2*5 + 2*5^2 + O(5^3), 1]
+[0, 1, x]
+[0, -3, 3*x + 5]
+[0, 1, (10*T^3 + 3*T + 5)*z^6 + (11*T^3 + 15*T^2 + 10*T + 13)*z^4 + (14*T^3 
++ 13*T^2 + 15)*z^2 + (9 + 13*T + 14*T^2 + 11*T^3 + O(T^20))]
+Mod(1, 7)*x
+1 + I
+1
+1
+2
+w
+w
+Mod(2, 4)
+  ***   at top-level: gcd(1/2,Mod(2,4))
+  ***                 ^-----------------
+  *** gcd: inconsistent gcd t_INTMOD , t_FRAC.
+Mod(1, 4)
+2
+Mod(1, 5)
+Mod(1, 5)
+1/2
+1/2
+1
+1
+1
+Mod(1, x^3)
+Mod(x, x^3)
+1
+  ***   at top-level: gcd(t,1/x)
+  ***                 ^----------
+  *** gcd: inconsistent gcd t_POLMOD , t_RFRAC.
+1
+y
+x
+1/x^2
+1
+1
+1
+0
+1
+1
+error("forbidden gcd t_REAL , t_FFELT.")
+error("forbidden gcd t_REAL , t_FFELT.")
+error("forbidden gcd t_REAL , t_FFELT.")
+1
+1
+error("inconsistent gcd t_INTMOD , t_FFELT.")
+error("inconsistent gcd t_FRAC , t_FFELT.")
+error("inconsistent gcd t_FRAC , t_FFELT.")
+1
+1
+1
+error("inconsistent gcd t_FRAC , t_FFELT.")
+0
+1
+error("inconsistent gcd t_FRAC , t_FFELT.")
+error("forbidden gcd t_FFELT , t_COMPLEX.")
+error("forbidden gcd t_FFELT , t_COMPLEX.")
+error("forbidden gcd t_FFELT , t_COMPLEX.")
+error("forbidden gcd t_FFELT , t_QUAD.")
+error("forbidden gcd t_FFELT , t_QUAD.")
+error("forbidden gcd t_FFELT , t_QUAD.")
+1
+1
+error("inconsistent gcd t_FFELT , t_PADIC.")
+0
+1
+error("inconsistent gcd t_FFELT , t_PADIC.")
+error("inconsistent gcd t_FFELT , t_PADIC.")
+error("inconsistent gcd t_FFELT , t_PADIC.")
+error("inconsistent gcd t_FFELT , t_PADIC.")
+1
+1 + I
+Mod(1, 3)
+1
+1
+(-k + 0.E-38)/k
+1/18446744073709551616
+Total time spent: 4
diff --git a/src/test/32/genus2red b/src/test/32/genus2red
new file mode 100644
index 0000000..fd0880a
--- /dev/null
+++ b/src/test/32/genus2red
@@ -0,0 +1,718 @@
+Type: [I{0-0-0}], p. 155
+[1, Mat([7, 0]), x^5 + x^3 - 2*x^2 + 3*x + 1, [7, [1, []], ["(tame) [I{0-0-0
+}] page 155", []]]]
+Type: [I*{0-0-0}], p. 155
+[2401, Mat([7, 4]), 7*x^5 + 7*x^3 - 14*x^2 + 21*x + 7, [7, [1, []], ["(tame)
+ [I*{0-0-0}] page 155", [2, 2, 2, 2]]]]
+Type: [II], p. 155
+[49, Mat([7, 2]), x^6 + 28*x^4 - 49*x^2 + 343, [7, [1, []], ["(tame) [II] pa
+ge 155", []]]]
+[9, Mat([3, 2]), x^6 + 12*x^4 - 9*x^2 + 27, [3, [1, []], ["[II] page 155", [
+]]]]
+Type: [III], p. 155
+[2401, Mat([7, 4]), x^6 + 28*x^3 + 49, [7, [1, []], ["[III] page 155", [3, 3
+]]]]
+Type: [IV], p. 155
+[2401, Mat([7, 4]), 7*x^6 + 245*x^3 + 343, [7, [4, []], ["[III*{3}] page 184
+", []]]]
+Type: [V], p. 156
+[2401, Mat([7, 4]), x^6 + 7, [7, [1, []], ["[V] page 156", [3]]]]
+Type: [V*], p. 156
+[2401, Mat([7, 4]), 7*x^6 + 49, [7, [1, []], ["[V*] page 156", [3]]]]
+Type: [VI], p. 156
+[2401, Mat([7, 4]), x^5 + 21*x^3 + 49*x, [7, [1, []], ["(tame) [VI] page 156
+", [2, 2]]]]
+Type: [VII], p. 156
+[2401, Mat([7, 4]), x^5 + 7*x, [7, [1, []], ["[VII] page 156", [2]]]]
+Type: [VII*], p. 156
+[2401, Mat([7, 4]), 7*x^5 + 49*x, [7, [1, []], ["[VII*] page 156", [2]]]]
+Type: [VIII-1], p. 156
+[2401, Mat([7, 4]), x^5 + 7, [7, [1, []], ["[VIII-1] page 156", []]]]
+Type: [VIII-2], p. 157
+[2401, Mat([7, 4]), x^5 + 343, [7, [1, []], ["[VIII-2] page 157", []]]]
+Type: [VIII-3], p. 157
+[2401, Mat([7, 4]), 7*x^5 + 343, [7, [1, []], ["[VIII-3] page 157", []]]]
+Type: [VIII-4], p. 157
+[2401, Mat([7, 4]), 7*x^6 + 49*x, [7, [1, []], ["[VIII-4] page 157", []]]]
+Type: [IX-1], p. 157
+[2401, Mat([7, 4]), x^5 + 49, [7, [1, []], ["[IX-1] page 157", [5]]]]
+Type: [IX-2], p. 157
+[2401, Mat([7, 4]), x^6 + 7*x, [7, [1, []], ["[IX-2] page 157", [5]]]]
+Type: [IX-3], p. 157
+[2401, Mat([7, 4]), 7*x^5 + 49, [7, [1, []], ["[IX-3] page 157", [5]]]]
+Type: [IX-4], p. 158
+[2401, Mat([7, 4]), x^6 + 49*x, [7, [1, []], ["[IX-4] page 158", [5]]]]
+Type: [I{0}-I{0}-10], p. 158
+[1, Mat([7, 0]), 508021860739623365322188197652216501772434524836001*x^6 + 5
+080218607396233653221881976522165257101144141196013*x^4 + 508021860739623365
+322188197652216501772434524836002*x^3 + 2393767988928360030*x^2 + 2393767988
+92836013*x + 1, [7, [5, [Mod(2, 7), Mod(5, 7)]], ["(tame) [I{0}-I{0}-10] pag
+e 158", []]]]
+[1, Mat([3, 0]), 42391158275216203514294433201*x^6 + 42391158275216203510807
+648800*x^4 + 42391158275216203514294433202*x^3 - 3486784401*x^2 - 3486784400
+*x + 1, [3, [5, [Mod(0, 3), Mod(1, 3)]], ["[I{0}-I{0}-10] page 158", []]]]
+Type: [I{0}*-I{0}*-10], p. 158
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 24893071176241544900787221684958
+6097837332861811440640*x^4 + 17425149823369080494630608325843912106803538735
+9467068*x^3 - 6551443127975383932596363883779423529*x^2 + 410531210101213754
+7000*x + 66885, [7, [5, [Mod(2, 7), Mod(2, 7)]], ["(tame) [I*{0}-I*{0}-10] p
+age 158", [2, 2, 2, 2]]]]
+[81, Mat([3, 4]), 42391158275216203514294433201*x^6 - 36472996377170786403*x
+^5 + 381520424476945831607729192403*x^4 + 1144561273102580527491412618835*x^
+3 - 984770902371897590535*x^2 - 564859072647*x + 945, [3, [5, [Mod(0, 3), Mo
+d(0, 3)]], ["[I*{0}-I*{0}-10] page 158", [2, 2, 2, 2]]]]
+Type: [I{0}-I{0}*-10], p. 159
+[49, Mat([7, 2]), 508021860739623365322188197652216501772434524836001*x^6 + 
+248930711762415449007872216849586086107869716062476493*x^4 + 174251498233690
+814305510551794710260107945042018748344*x^3 + 117294631457489641470*x^2 + 82
+106242020242749519*x + 343, [7, [5, [Mod(2, 7), Mod(5, 7)]], ["(tame) [I{0}-
+I*{0}-10] page 159", [2, 2]]]]
+[9, Mat([3, 2]), 42391158275216203514294433201*x^6 + 38152042447694583162516
+3114408*x^4 + 1144561273430837494885949696428*x^3 - 31381059609*x^2 - 941431
+78818*x + 27, [3, [5, [Mod(0, 3), Mod(1, 3)]], ["[I*{0}-I{0}-10] page 159", 
+[2, 2]]]]
+Type: [2I{0}-11], p. 159
+[49, Mat([7, 2]), x^6 - 21*x^4 + 574743694141699243350*x^2 + 265173084183644
+7612787128678837, [7, [5, [Mod(2, 7), Mod(2, 7)]], ["(tame) [2I{0}-11] page 
+159", []]]]
+[9, Mat([3, 2]), x^6 - 9*x^4 - 282429536454*x^2 + 150095482585608537, [3, [5
+, [Mod(0, 3), Mod(0, 3)]], ["[2I{0}-11] page 159", []]]]
+Type: [2I{0}*-10], p. 159
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 82106242020242749176*x^2 + 54116956037952
+111668959660849*x - 574743694141699243546, [7, [5, [Mod(2, 7), Mod(2, 7)]], 
+["(tame) [2I*{0}-10] page 159", [2, 2]]]]
+Type: [2I{0}*-10], p. 159
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 82106242020242749176*x^2 + 54116956037952
+111668959660849*x - 574743694141699243546, [7, [5, [Mod(2, 7), Mod(2, 7)]], 
+["(tame) [2I*{0}-10] page 159", [2, 2]]]]
+Type: [I{0}-II-10], p. 159
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 23937679889283600
+3*x^4 + x^3 + 44567640326363195900190045974568007*x^2 + 1675637592249852021*
+x + 7, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I{0}-II-10] page 159", []]
+]]
+Type: [I{0}-II*-10], p. 160
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 23937679889283600
+3*x^4 + x^3 + 107006904423598033356356300384937784807*x^2 + 4023205858991894
+702421*x + 16807, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I{0}-II*-10] pa
+ge 160", []]]]
+[243, Mat([3, 5]), 12157665459056928801*x^5 + 10460353203*x^4 + x^3 + 295431
+2706550833698643*x^2 + 2541865828329*x + 243, [3, [5, [Mod(0, 3), Mod(0, 3)]
+], ["[II*-I{0}-10] pages 159-177", []]]]
+Type: [I{0}-IV-10], p. 160
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 23937679889283600
+3*x^4 + x^3 + 311973482284542371301330321821976049*x^2 + 1172946314574896414
+7*x + 49, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I{0}-IV-10] page 160", 
+[3]]]]
+[243, Mat([3, 5]), 12157665459056928801*x^5 + 10460353203*x^4 + x^3 + 109418
+989131512359209*x^2 + 94143178827*x + 9, [3, [5, [Mod(0, 3), Mod(0, 3)]], ["
+[IV-I{0}-10] pages 159-177", [3]]]]
+Type: [I{0}-IV*-10], p. 160
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 23937679889283600
+3*x^4 + x^3 + 15286700631942576193765185769276826401*x^2 + 57474369414169924
+3203*x + 2401, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I{0}-IV*-10] page 
+160", [3]]]]
+[243, Mat([3, 5]), 12157665459056928801*x^5 + 10460353203*x^4 + x^3 + 984770
+902183611232881*x^2 + 847288609443*x + 81, [3, [5, [Mod(0, 3), Mod(0, 3)]], 
+["[IV*-I{0}-10] pages 159-177", [3]]]]
+Type: [I{0}*-II-10], p. 160
+[2401, Mat([7, 4]), 311973482284542371301330321821976049*x^5 + 1675637592249
+852021*x^4 + x^3 + 2183814375991796599109312252753832343*x^2 + 1172946314574
+8964147*x + 7, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I*{0}-II-10] page 
+160", [2, 2]]]]
+[2187, Mat([3, 7]), 109418989131512359209*x^5 + 31381059609*x^4 + x^3 + 3282
+56967394537077627*x^2 + 94143178827*x + 3, [3, [5, [Mod(0, 3), Mod(0, 3)]], 
+["[II-I*{0}-10] pages 159-177", [2, 2]]]]
+Type: [I{0}*-II*-10], p. 160-161
+[2401, Mat([7, 4]), 311973482284542371301330321821976049*x^5 + 1675637592249
+852021*x^4 + x^3 + 5243338316756303634461458718861951455543*x^2 + 2816244101
+2943262916947*x + 16807, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I*{0}-II
+*-10] page 160", [2, 2]]]]
+[2187, Mat([3, 7]), 109418989131512359209*x^5 + 31381059609*x^4 + x^3 + 2658
+8814358957503287787*x^2 + 7625597484987*x + 243, [3, [5, [Mod(0, 3), Mod(0, 
+3)]], ["[II*-I*{0}-10] pages 159-177", [2, 2]]]]
+Type: [I{0}*-IV-10], p. 161
+[2401, Mat([7, 4]), 311973482284542371301330321821976049*x^5 + 1675637592249
+852021*x^4 + x^3 + 15286700631942576193765185769276826401*x^2 + 821062420202
+42749029*x + 49, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I*{0}-IV-10] pag
+e 161", [6, 2]]]]
+Type: [I{0}*-IV*-10], p. 161
+[2401, Mat([7, 4]), 311973482284542371301330321821976049*x^5 + 1675637592249
+852021*x^4 + x^3 + 749048330965186233494494102694564493649*x^2 + 40232058589
+91894702421*x + 2401, [7, [5, [Mod(0, 7), Mod(2, 7)]], ["(tame) [I*{0}-IV*-1
+0] page 161", [6, 2]]]]
+Type: [I{0}-III-10], p. 161
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 23937679889283600
+3*x^4 + 44567640326363195900190045974568008*x^3 + 1675637592249852021*x^2 + 
+7*x, [7, [5, [Mod(2, 7), Mod(6, 7)]], ["(tame) [I{0}-III-10] page 161", [2]]
+]]
+Type: [I{0}-III*-10], p. 162
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 23937679889283600
+3*x^4 + 2183814375991796599109312252753832344*x^3 + 82106242020242749029*x^2
+ + 343*x, [7, [5, [Mod(2, 7), Mod(6, 7)]], ["(tame) [I{0}-III*-10] page 162"
+, [2]]]]
+Type: [I{0}*-III-10], p. 162
+[2401, Mat([7, 4]), 311973482284542371301330321821976049*x^5 + 1675637592249
+852021*x^4 + 2183814375991796599109312252753832344*x^3 + 1172946314574896414
+7*x^2 + 7*x, [7, [5, [Mod(2, 7), Mod(6, 7)]], ["(tame) [I*{0}-III-10] page 1
+62", [2, 2, 2]]]]
+Type: [I{0}*-III*-10], p. 162
+[2401, Mat([7, 4]), 311973482284542371301330321821976049*x^5 + 1675637592249
+852021*x^4 + 107006904423598033356356300384937784808*x^3 + 57474369414169924
+3203*x^2 + 343*x, [7, [5, [Mod(2, 7), Mod(6, 7)]], ["(tame) [I*{0}-III*-10] 
+page 162", [2, 2, 2]]]]
+Type: [2II-10], p. 162
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 147*x^2 + 7730993719707444524137094407*x 
+- 343, [7, [5, [Mod(0, 7), Mod(0, 7)]], ["(tame) [2II-10] page 162", []]]]
+[59049, Mat([3, 10]), x^6 - 9*x^4 + 27*x^2 + 5559060566555523*x - 27, [3, [5
+, [Mod(0, 3), Mod(0, 3)]], ["[2II-10] page 162", []]]]
+Type: [2II*-10], p. 163
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 147*x^2 + 378818692265664781682717625943*
+x - 343, [7, [5, [Mod(0, 7), Mod(0, 7)]], ["(tame) [2II*-10] page 163", []]]
+]
+[59049, Mat([3, 10]), x^6 - 9*x^4 + 27*x^2 + 50031545098999707*x - 27, [3, [
+5, [Mod(0, 3), Mod(0, 3)]], ["[2II*-10] page 163", []]]]
+Type: [II-II-10], p. 163
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 3556153
+025177363557255317383565515512407041673852013*x^3 - 133702920979089587700570
+137923704021*x^2 + 1675637592249852021*x + 42, [7, [5, [Mod(0, 7), Mod(0, 7)
+]], ["(tame) [II-II-10] page 163", []]]]
+Type: [II-II*-10], p. 163
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 3556153
+025177363557255317383565515512407041673868813*x^3 - 133702920979089587700570
+137923704021*x^2 + 1675637592249852021*x + 117642, [7, [5, [Mod(0, 7), Mod(0
+, 7)]], ["(tame) [II-II*-10] page 163", []]]]
+Type: [II*-II*-10], p. 163
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 8538323
+413450849900970017037940802745289307058918685613*x^3 - 321020713270794100069
+068901154813354421*x^2 + 4023205858991894702421*x + 282458442, [7, [5, [Mod(
+0, 7), Mod(0, 7)]], ["(tame) [II*-II*-10] page 163", []]]]
+Type: [II*-II*-(-1)], p. 163
+[2401, Mat([7, 4]), 7*x^6 - 21*x^5 + 21*x^4 + 679*x^3 - 1029*x^2 + 1029*x + 
+16464, [7, [5, [Mod(0, 7), Mod(0, 7)]], ["(tame) [II*-II*--1] page 163", []]
+]]
+Type: [II-IV-10], p. 164
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 3556153
+025177363557255317383565515512407041673852055*x^3 - 133702920979089587700570
+137923704021*x^2 + 1675637592249852021*x + 336, [7, [5, [Mod(0, 7), Mod(0, 7
+)]], ["(tame) [II-IV-10] page 164", [3]]]]
+Type: [II-IV*-10], p. 164
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 3556153
+025177363557255317383565515512407041673854407*x^3 - 133702920979089587700570
+137923704021*x^2 + 1675637592249852021*x + 16800, [7, [5, [Mod(0, 7), Mod(0,
+ 7)]], ["(tame) [II-IV*-10] page 164", [3]]]]
+Type: [II*-IV-10], p. 164
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 8538323
+413450849900970017037940802745289307058918668855*x^3 - 321020713270794100069
+068901154813354421*x^2 + 4023205858991894702421*x + 806736, [7, [5, [Mod(0, 
+7), Mod(0, 7)]], ["(tame) [II*-IV-10] page 164", [3]]]]
+Type: [II*-IV-(-1)], p. 164
+[2401, Mat([7, 4]), x^6 + 56*x^3 + 343, [7, [5, [Mod(0, 7), Mod(0, 7)]], ["[
+II*-IV-(-1)] page 164", [3]]]]
+Type: [II*-IV*-10], p. 164-165
+[2401, Mat([7, 4]), 174251498233690814305510551794710260107945042018748343*x
+^6 - 935920446853627113903990965465928147*x^5 + 1675637592249852021*x^4 + 41
+8377847259091645147530834859099334519176045887014771591*x^3 - 22471449928955
+58700483482308083693480947*x^2 + 4023205858991894702421*x + 115248, [7, [5, 
+[Mod(0, 7), Mod(0, 7)]], ["(tame) [II*-IV*-10] page 164", [3]]]]
+Type: [2IV-10], p. 165
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 147*x^2 + 54116956037952111668959660506, 
+[7, [5, [Mod(0, 7), Mod(0, 7)]], ["(tame) [2IV-10] page 165", [3]]]]
+[59049, Mat([3, 10]), x^6 - 9*x^4 + 27*x^2 + 16677181699666542, [3, [5, [Mod
+(0, 3), Mod(0, 3)]], ["[2IV-10] page 165", [3]]]]
+Type: [2IV*-10], p. 165
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 147*x^2 + 378818692265664781682717625600,
+ [7, [5, [Mod(0, 7), Mod(0, 7)]], ["(tame) [2IV*-10] page 165", [3]]]]
+[59049, Mat([3, 10]), x^6 - 9*x^4 + 27*x^2 + 50031545098999680, [3, [5, [Mod
+(0, 3), Mod(0, 3)]], ["[2IV*-10] page 165", [3]]]]
+Type: [IV-IV-10], p. 165
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 2489307
+1176241544900787221684958608586849291716964097*x^3 - 93592044685362711390399
+0965465928147*x^2 + 11729463145748964147*x + 2352, [7, [5, [Mod(0, 7), Mod(0
+, 7)]], ["(tame) [IV-IV-10] page 165", [3, 3]]]]
+Type: [IV-IV*-10], p. 165
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 239376798892836003*x^4 + 2489307
+1176241544900787221684958608586849291716966449*x^3 - 93592044685362711390399
+0965465928147*x^2 + 11729463145748964147*x + 117600, [7, [5, [Mod(0, 7), Mod
+(0, 7)]], ["(tame) [IV-IV*-10] page 165", [3, 3]]]]
+Type: [IV*-IV*-10], p. 166
+[2401, Mat([7, 4]), 174251498233690814305510551794710260107945042018748343*x
+^6 - 935920446853627113903990965465928147*x^5 + 1675637592249852021*x^4 + 41
+8377847259091645147530834859099334519176045887014771549*x^3 - 22471449928955
+58700483482308083693480947*x^2 + 4023205858991894702421*x + 14406, [7, [5, [
+Mod(0, 7), Mod(0, 7)]], ["(tame) [IV*-IV*-10] page 166", [3, 3]]]]
+Type: [II-III-10], p. 166
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 35561530251773635572553173835655
+15751783840566688010*x^4 - 133702920979089587700570137923704015*x^3 + 167563
+7592249852021*x^2 + 42*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["(tame) [II-III-
+10] page 166", [2]]]]
+Type: [II-III*-10], p. 166
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 17425149823369081430551055179471
+0260347321840911584346*x^4 - 6551443127975389797327936758261497023*x^3 + 821
+06242020242749029*x^2 + 2058*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["(tame) [I
+I-III*-10] page 166", [2]]]]
+Type: [II*-III-10], p. 166
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 35561530251773635572553173835655
+15751783840566688010*x^4 - 133702920979089587700570137923687215*x^3 + 167563
+7592249852021*x^2 + 117642*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["(tame) [II*
+-III-10] page 166", [2]]]]
+Type: [II*-III-(-1)], p. 167
+[2401, Mat([7, 4]), x^5 + 7*x^3 + 49*x^2 + 343, [7, [5, [Mod(0, 7), Mod(6, 7
+)]], ["[II*-III-(-1)] page 167", [2]]]]
+Type: [II*-III*-10], p. 167
+[2401, Mat([7, 4]), 174251498233690814305510551794710260107945042018748343*x
+^6 - 935920446853627113903990965465928147*x^5 + 5976826389415594930679011926
+5585619218700787004680533670*x^4 - 321020713270794100069068901154813354373*x
+^3 + 574743694141699243203*x^2 + 16464*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], [
+"(tame) [II*-III*-10] page 167", [2]]]]
+Type: [IV-III-10], p. 167
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 35561530251773635572553173835655
+15751783840566688010*x^4 - 133702920979089587700570137923703973*x^3 + 167563
+7592249852021*x^2 + 336*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["(tame) [IV-III
+-10] page 167", [6]]]]
+[2187, Mat([3, 7]), 42391158275216203514294433201*x^6 - 36472996377170786403
+*x^5 + 127173474825648610553343652806*x^4 - 109418989131512359201*x^3 + 3138
+1059609*x^2 + 24*x, [3, [5, [Mod(0, 3), Mod(0, 3)]], ["[III-IV-10] pages 160
+-174", [6]]]]
+Type: [IV-III*-10], p. 167
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 17425149823369081430551055179471
+0260347321840911584346*x^4 - 6551443127975389797327936758261496981*x^3 + 821
+06242020242749029*x^2 + 16464*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["(tame) [
+IV-III*-10] page 167", [6]]]]
+[2187, Mat([3, 7]), 42391158275216203514294433201*x^6 - 36472996377170786403
+*x^5 + 1144561273430837494896410049630*x^4 - 984770902183611232873*x^3 + 282
+429536481*x^2 + 216*x, [3, [5, [Mod(0, 3), Mod(0, 3)]], ["[III*-IV-10] pages
+ 160-174", [6]]]]
+Type: [IV-III*-(-1)], p. 167
+[2401, Mat([7, 4]), x^6 + 7*x^4 + 7*x^3 + 49*x, [7, [5, [Mod(0, 7), Mod(6, 7
+)]], ["[IV-III*-(-1)] page 167", [6]]]]
+Type: [IV*-III-10], p. 168
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 35561530251773635572553173835655
+15751783840566688010*x^4 - 133702920979089587700570137923701621*x^3 + 167563
+7592249852021*x^2 + 16800*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["(tame) [IV*-
+III-10] page 168", [6]]]]
+Type: [IV*-III*-10], p. 168
+[2401, Mat([7, 4]), 174251498233690814305510551794710260107945042018748343*x
+^6 - 935920446853627113903990965465928147*x^5 + 5976826389415594930679011926
+5585619218700787004680533670*x^4 - 321020713270794100069068901154813354415*x
+^3 + 574743694141699243203*x^2 + 2058*x, [7, [5, [Mod(0, 7), Mod(6, 7)]], ["
+(tame) [IV*-III*-10] page 168", [6]]]]
+Type: [2III-10], p. 168
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 3909821048582988049*x^3 + 147*x^2 - 27368
+747340080916343*x - 343, [7, [5, [Mod(6, 7), Mod(6, 7)]], ["(tame) [2III-10]
+ page 168", [2]]]]
+[81, Mat([3, 4]), x^6 - 9*x^4 + 31381059609*x^3 + 27*x^2 - 94143178827*x - 2
+7, [3, [5, [Mod(0, 3), Mod(0, 3)]], ["[2III-10] page 168", [2]]]]
+Type: [2III*-10], p. 168
+[2401, Mat([7, 4]), x^6 - 21*x^4 + 27368747340080916343*x^3 + 147*x^2 - 1915
+81231380566414401*x - 343, [7, [5, [Mod(6, 7), Mod(6, 7)]], ["(tame) [2III*-
+10] page 168", [2]]]]
+[81, Mat([3, 4]), x^6 - 9*x^4 + 94143178827*x^3 + 27*x^2 - 282429536481*x - 
+27, [3, [5, [Mod(0, 3), Mod(0, 3)]], ["[2III*-10] page 168", [2]]]]
+Type: [III-III-10], p. 169
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 35561530251773635572553173835655
+16310329704649972017*x^4 - 133702920979089587700570137923704029*x^3 + 558545
+8640832840070*x^2 - 56*x, [7, [5, [Mod(6, 7), Mod(6, 7)]], ["(tame) [III-III
+-10] page 169", [2, 2]]]]
+Type: [III-III*-10], p. 169
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
+- 19100417282727083957224305417672003*x^5 + 35561530251773635572553173835655
+43120531180647604353*x^4 - 133702920979089587700570137923704365*x^3 + 193256
+868972816266422*x^2 - 2408*x, [7, [5, [Mod(6, 7), Mod(6, 7)]], ["(tame) [III
+-III*-10] page 169", [2, 2]]]]
+Type: [III*-III*-{10}], p. 169
+[2401, Mat([7, 4]), 174251498233690814305510551794710260107945042018748343*x
+^6 - 935920446853627113903990965465928147*x^5 + 5976826389415594930679011926
+5585619222610608053263521719*x^4 - 321020713270794100069068901154813354429*x
+^3 + 1915812313805664144010*x^2 - 2744*x, [7, [5, [Mod(6, 7), Mod(6, 7)]], [
+"(tame) [III*-III*-10] page 169", [2, 2]]]]
+Type: [I{9-0-0}], p. 170
+[7, Mat([7, 1]), x^5 + 40353610*x^3 + x^2 + 121060821*x + 40353607, [7, [2, 
+[Mod(2, 7)]], ["(tame) [I{9-0-0}] page 170", [9]]]]
+Type: [I{9}-I{0}-10], p. 170
+[7, Mat([7, 1]), 6366805760909027985741435139224001*x^5 - 159584532595224002
+*x^4 + 19100417282727083957224305458025611*x^3 + 636680576090902750698783735
+3551995*x^2 - 159584532474163178*x + 40353608, [7, [6, [Mod(2, 7)]], ["(tame
+) [I{9}-I{0}-10] page 170", [9]]]]
+[3, Mat([3, 1]), 12157665459056928801*x^5 - 6973568802*x^4 - 121576654590569
+09117*x^3 + 12157665466030497603*x^2 - 6973588486*x + 19684, [3, [6, [Mod(0,
+ 3)]], ["(tame) [I{9}-I{0}-10] page 170", [9]]]]
+Type: [I{0}-I*{9}-10], p. 170
+[49, Mat([7, 2]), x^6 + 4*x^5 + 19100417282727083957224307394998728*x^4 + 50
+8021860739623479924691894014720245118274940175011*x^3 + 35561530629451293191
+48051483501814963799504492542834*x^2 + 1004525211269079266605816908070627976
+335864597950580018666977*x + 70316764788835530156201894723072969620825817394
+84589084102397, [7, [6, [Mod(2, 7)]], ["(tame) [I*{9}-I{0}-10] page 170", [4
+]]]]
+[9, Mat([3, 2]), x^6 - 12157665459056751660*x^4 + 42391158250900872596180575
+607*x^3 + 127171321168158531362286712374*x^2 + 75094665106723368777956004270
+53229*x + 22528399551400256301066821173173441, [3, [6, [Mod(0, 3)]], ["[I*{9
+}-I{0}-10] page 170", [4]]]]
+Type: [I{9}-I{0}*-10], p. 171
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 935920446853627113903990965506281755*x^3 + 218381437599179657565038
+5961255904049*x^2 - 54737494674229852310*x + 13841287544, [7, [6, [Mod(2, 7)
+]], ["(tame) [I*{0}-I{9}-10] page 170", [18, 2]]]]
+Type: [I*{9-0-0}], p. 171
+[2401, Mat([7, 4]), 7*x^5 + 282475270*x^3 + 7*x^2 + 847425747*x + 282475249,
+ [7, [2, [Mod(2, 7)]], ["(tame) [I*{9-0-0}] page 171", [4, 2, 2]]]]
+Type: [I*{9}-I{0}*-10], p. 171
+[2401, Mat([7, 4]), x^6 + 4*x^5 + 935920446853627113903990967443254872*x^4 +
+ 174251498233690819921033232916472943531890842723624217*x^3 + 12197604894864
+56222471317833459850493873847680657395212*x^2 + 3445521474652941218234561596
+37489836481536621034944927824583035*x + 241186503225705876208378720213726101
+0629601295382324365507824195, [7, [6, [Mod(2, 7)]], ["(tame) [I*{0}-I*{9}-10
+] page 171", [4, 2, 2]]]]
+Type: [II{9-0}], p. 171
+[343, Mat([7, 3]), x^6 - 2*x^5 + 5764823*x^4 - 42*x^3 + 121060891*x^2 - 98*x
+ + 282475298, [7, [2, [Mod(0, 7)]], ["(tame) [II{9-0}] page 171", [36]]]]
+Type: [II*{9-0}], p. 172
+[49, Mat([7, 2]), 7*x^6 - 14*x^5 + 40353761*x^4 - 294*x^3 + 847426237*x^2 - 
+686*x + 1977327086, [7, [2, [Mod(0, 7)]], ["(tame) [II*{9-0}] page 172", []]
+]]
+Type: [II-I{9}-10], p. 172
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 40353608*x^3 + 44567640326363195900190045974568007*x^2 - 1117091728
+166568014*x + 282475256, [7, [6, [Mod(0, 7)]], ["(tame) [II-I{9}-10] page 17
+2", [9]]]]
+Type: [II*-I{9}-10], p. 172
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 40353608*x^3 + 107006904423598033356356300384937784807*x^2 - 268213
+7239327929801614*x + 678223089656, [7, [6, [Mod(0, 7)]], ["(tame) [II*-I{9}-
+10] page 172", [9]]]]
+Type: [IV-I{9}-10], p. 173
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 40353608*x^3 + 311973482284542371301330321821976049*x^2 - 781964209
+7165976098*x + 1977326792, [7, [6, [Mod(0, 7)]], ["(tame) [IV-I{9}-10] page 
+173", [9, 3]]]]
+Type: [IV*-I{9}-10], p. 173
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 40353608*x^3 + 15286700631942576193765185769276826401*x^2 - 3831624
+62761132828802*x + 96889012808, [7, [6, [Mod(0, 7)]], ["(tame) [IV*-I{9}-10]
+ page 173", [9, 3]]]]
+Type: [II-I*{9}-10], p. 173
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
++ 25467223043636111942965740556896004*x^5 + 157775381157130877341310732*x^4 
++ 3556153025177363557255317383565515512407053537812471*x^3 + 178270561305452
+783600760183898272028*x^2 + 1104427668099916141389175124*x + 83047723248, [7
+, [6, [Mod(0, 7)]], ["(tame) [II-I*{9}-10] page 173", [4]]]]
+Type: [II*-I*{9}-10], p. 174
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
++ 25467223043636111942965740556896004*x^5 + 157775381157130877341310732*x^4 
++ 8538323413450849900970017037940802745289307070782629271*x^3 + 428027617694
+392133425425201539751139228*x^2 + 2651730831107898655475409472724*x + 199397
+583518448, [7, [6, [Mod(0, 7)]], ["(tame) [II*-I*{9}-10] page 174", [4]]]]
+Type: [II*-I*{9}-(-1)], p. 174
+[2401, Mat([7, 4]), 7*x^5 - 14*x^4 + 282475256*x^3 + 343*x^2 - 686*x + 13841
+287544, [7, [6, [Mod(0, 7)]], ["(tame) [II*-I*{9}--1] page 174", [4]]]]
+Type: [IV-I*{9}-10], p. 174
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
++ 25467223043636111942965740556896004*x^5 + 157775381157130877341310732*x^4 
++ 24893071176241544900787221684958608586849303580924513*x^3 + 12478939291381
+69485205321287287904196*x^2 + 7730993676699412989724225868*x + 581334062736,
+ [7, [6, [Mod(0, 7)]], ["(tame) [IV-I*{9}-10] page 174", [12]]]]
+Type: [IV*-I*{9}-10], p. 174
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
++ 25467223043636111942965740556896004*x^5 + 157775381157130877341310732*x^4 
++ 1219760487635835700138573862562971820755615305995198865*x^3 + 611468025277
+70304775060743077107305604*x^2 + 378818690158271236496487067532*x + 28485369
+074064, [7, [6, [Mod(0, 7)]], ["(tame) [IV*-I*{9}-10] page 174", [12]]]]
+Type: [IV*-I*{9}-(-1)], p. 175
+[2401, Mat([7, 4]), 7*x^5 - 14*x^4 + 282475256*x^3 + 49*x^2 - 98*x + 1977326
+792, [7, [6, [Mod(0, 7)]], ["(tame) [IV*-I*{9}--1] page 174", [12]]]]
+[2187, Mat([3, 7]), 3*x^5 - 6*x^4 + 59052*x^3 + 9*x^2 - 18*x + 177156, [3, [
+6, [Mod(0, 3)]], ["[IV*-I*{9}--1] pages 159-177", [12]]]]
+Type: [IV-II{9}], p. 175
+[343, Mat([7, 3]), x^6 - 2*x^5 + 40353608*x^4 + 7*x^3 - 14*x^2 + 282475256*x
+, [7, [2, [Mod(0, 7)]], ["(tame) [IV-II{9}] page 175", [29]]]]
+[729, Mat([3, 6]), x^6 - 2*x^5 + 19684*x^4 + 3*x^3 - 6*x^2 + 59052*x, [3, [6
+, [Mod(0, 3)]], ["[IV-II{9}] page 175", [29]]]]
+Type: [IV*-II{9}], p. 175
+[343, Mat([7, 3]), x^6 - 2*x^5 + 5764802*x^4 + 49*x^3 - 98*x^2 + 282475298*x
+, [7, [2, [Mod(0, 7)]], ["(tame) [IV*-II{9}] page 175", [28]]]]
+[729, Mat([3, 6]), x^6 - 2*x^5 + 6562*x^4 + 9*x^3 - 18*x^2 + 59058*x, [3, [6
+, [Mod(0, 3)]], ["[IV*-II{9}] page 175", [28]]]]
+Type: [IV*-II{0}], p. 175
+[343, Mat([7, 3]), 7*x^5 + 49*x^3 + 49*x^2 + 343, [7, [2, [Mod(0, 7)]], ["[I
+V*-II{0}] page 175", []]]]
+[729, Mat([3, 6]), 3*x^5 + 9*x^3 + 9*x^2 + 27, [3, [6, [Mod(0, 3)]], ["[IV*-
+II{0}] page 175", []]]]
+Type: [II-II*{9}], p. 176
+[2401, Mat([7, 4]), x^5 + 282475249*x^3 + 7*x^2 + 1977326743, [7, [2, [Mod(0
+, 7)]], ["[II-II*{9}] page 176", [2, 2]]]]
+[2187, Mat([3, 7]), x^5 + 59049*x^3 + 3*x^2 + 177147, [3, [6, [Mod(0, 3)]], 
+["[II-II*{9}] page 176", [2, 2]]]]
+Type: [II*-II*{9}], p. 176
+[2401, Mat([7, 4]), 7*x^6 - 14*x^5 + 282475256*x^4 + 49*x^3 - 98*x^2 + 19773
+26792*x, [7, [2, [Mod(0, 7)]], ["(tame) [II*-II*{9}] page 176", [2, 2]]]]
+[2187, Mat([3, 7]), 3*x^6 - 6*x^5 + 59052*x^4 + 9*x^3 - 18*x^2 + 177156*x, [
+3, [6, [Mod(0, 3)]], ["[II*-II*{9}] page 176", [2, 2]]]]
+Type: [III-I{9}-10], p. 176
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 44567640326363195900190046014921615*x^3 - 1117091728166568014*x^2 +
+ 282475256*x, [7, [6, [Mod(6, 7)]], ["(tame) [III-I{9}-10] page 176", [18]]]
+]
+[27, Mat([3, 3]), 12157665459056928801*x^5 - 6973568802*x^4 + 36472996377170
+806087*x^3 - 20920706406*x^2 + 59052*x, [3, [6, [Mod(0, 3)]], ["[III-I{9}-10
+] pages 159-177", [18]]]]
+Type: [III*-I{9}-10], p. 176
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 - 1595845325952240
+02*x^4 + 2183814375991796599109312252794185951*x^3 - 54737494680161832686*x^
+2 + 13841287544*x, [7, [6, [Mod(6, 7)]], ["(tame) [III*-I{9}-10] page 176", 
+[18]]]]
+[27, Mat([3, 3]), 12157665459056928801*x^5 - 6973568802*x^4 + 32825696739453
+7097311*x^3 - 188286357654*x^2 + 531468*x, [3, [6, [Mod(0, 3)]], ["[III*-I{9
+}-10] pages 159-177", [18]]]]
+Type: [III-I*{9}-10], p. 177
+[2401, Mat([7, 4]), x^6 + 4*x^5 + 44567640326363195900190047951894732*x^4 + 
+267405841958179175401140283756715034*x^3 + 881247867777497128998513653865824
+60699453771*x^2 + 528748722538339171106362420127476948226402066*x - 61687350
+9628062366290756156815389740634465608, [7, [6, [Mod(6, 7)]], ["(tame) [III-I
+*{9}-10] page 177", [4, 2]]]]
+[81, Mat([3, 4]), x^6 + 36472996377170963544*x^4 + 72945992754341572814*x^3 
++ 6460972470237541785510147*x^2 + 12922163778453346599281658*x - 19383245667
+680019897328164, [3, [6, [Mod(0, 3)]], ["[I*{9}-III-10] pages 161-177", [4, 
+2]]]]
+Type: [III*-I*{9}-10], p. 177
+[2401, Mat([7, 4]), x^6 + 4*x^5 + 2183814375991796599109312254731159068*x^4 
++ 13102886255950779594655873524432301050*x^3 + 43181145521097359320927169039
+42542282683541067*x^2 + 25908687404378619384211758586246368564860027954*x - 
+30226801971775055948247051683954096626707029144, [7, [6, [Mod(6, 7)]], ["(ta
+me) [III*-I*{9}-10] page 177", [4, 2]]]]
+[81, Mat([3, 4]), x^6 + 328256967394537254768*x^4 + 656513934789074155262*x^
+3 + 58148752232137876078094403*x^2 + 116299474006080119382197514*x - 1744492
+11009120179071701948, [3, [6, [Mod(0, 3)]], ["[I*{9}-III*-10] pages 162-177"
+, [4, 2]]]]
+Type: [III*-I*{9}-(-1)], p. 177
+[2401, Mat([7, 4]), 7*x^5 - 14*x^4 + 282475305*x^3 - 98*x^2 + 1977326792*x, 
+[7, [6, [Mod(6, 7)]], ["(tame) [III*-I*{9}--1] page 177", [4, 2]]]]
+Type: [III-II{9}], p. 177
+[343, Mat([7, 3]), x^6 - 2*x^5 + 40353608*x^4 + 7*x^2 - 14*x + 282475256, [7
+, [2, [Mod(6, 7)]], ["(tame) [III-II{9}] page 177", [19]]]]
+Type: [III*-II{9}], p. 178
+[343, Mat([7, 3]), x^6 - 2*x^5 + 5764802*x^4 + 343*x^2 - 686*x + 1977327086,
+ [7, [2, [Mod(6, 7)]], ["(tame) [III*-II{9}] page 178", [19]]]]
+Type: [III*-II{0}], p. 178
+[343, Mat([7, 3]), 7*x^6 + 49*x^4 + 49*x^2 + 343, [7, [2, [Mod(6, 7)]], ["(t
+ame) [III*-II{0}] page 178", []]]]
+Type: [III-II*{9}], p. 178
+[2401, Mat([7, 4]), x^6 + 282475249*x^4 + 7*x^2 + 1977326743, [7, [2, [Mod(6
+, 7)]], ["(tame) [III-II*{9}] page 178", [8]]]]
+Type: [III*-II*{9}], p. 178
+[2401, Mat([7, 4]), 282475249*x^6 + 7*x^4 + 1977326743*x^2 + 49, [7, [2, [Mo
+d(6, 7)]], ["(tame) [III*-II*{9}] page 178", [8]]]]
+Type: [I{9-8-0}], p. 179
+[49, Mat([7, 2]), x^5 + x^4 + 46118407*x^3 + 34588805*x^2 + 232630473633600*
+x - 232630554340814, [7, [3, []], ["(tame) [I{8-9-0}] page 179", [72]]]]
+Type: [I{9}-I{8}-10}], p. 179
+[49, Mat([7, 2]), 6366805760909027985741435139224001*x^5 + 63668057609090278
+26156902543999999*x^4 + 256923577521058878088611317639703031862407*x^3 + 256
+923577521058871648799965597876173411195*x^2 - 6439811511393728899334400*x + 
+232630554340814, [7, [7, []], ["(tame) [I{8}-I{9}-10] page 179", [72]]]]
+Type: [I*{9-8-0}], p. 180
+[2401, Mat([7, 4]), 7*x^5 + 7*x^4 + 322828849*x^3 + 242121635*x^2 + 16284133
+15435200*x - 1628413880385698, [7, [3, []], ["(tame) [I*{8-9-0}] page 180", 
+[4, 2, 2]]]]
+[81, Mat([3, 4]), 3*x^5 + 3*x^4 + 78729*x^3 + 39363*x^2 + 387361440*x - 3874
+79538, [3, [3, []], ["(tame) [I*{8-9-0}] page 180", [4, 2, 2]]]]
+Type: [I*{9}-I*{8}-10], p. 180
+[2401, Mat([7, 4]), 508021860739623365322188197652216501772434524836001*x^6 
++ 3556153025177363582722540427201627455372782230748011*x^5 + 100452521126907
+9039999221712767258830172664699912235178878009*x^4 + 70316764788835533303515
+71935604422778407017230014542438128339*x^3 + 3524991484034604193284089402685
+37457526303138*x^2 + 311973470135837957969478408827349338*x + 23458926374545
+651500, [7, [7, []], ["(tame) [I*{8}-I*{9}-10] page 180", [4, 2, 2]]]]
+Type: [I{9}-I*{8}-10], p. 180
+[343, Mat([7, 3]), 6366805760909027985741435139224001*x^5 + 4456764032636319
+5740605513379344005*x^4 + 1798465042647412146620279223477921223036843*x^3 + 
+12589255298531884981263281802603029551507501*x^2 - 3155507640582927177622370
+94*x + 79792268274938744, [7, [7, []], ["(tame) [I*{8}-I{9}-10] page 180", [
+18, 2]]]]
+[27, Mat([3, 3]), 12157665459056928801*x^5 + 36472996370197217601*x^4 + 7178
+97987691831668083527*x^3 + 2153693962663775502180501*x^2 - 1235345630247378*
+x + 3486961548, [3, [7, []], ["[I*{8}-I{9}-10] page 180", [18, 2]]]]
+Type: [2I{8}-10], p. 181
+[343, Mat([7, 3]), x^6 + 1977326722*x^4 + 9387480337620071731394*x^2 + 18562
+115855305211939015772541728, [7, [7, []], ["(tame) [2I{8}-10] page 181", [8]
+]]]
+[27, Mat([3, 3]), x^6 + 177138*x^4 + 2541864765474*x^2 + 450276280295106672,
+ [3, [7, []], ["[2I{8}-10] page 181", [8]]]]
+Type: [2I{9}-10}], p. 181
+[343, Mat([7, 3]), x^6 + 1977326722*x^4 + 9387480337647754305649*x^3 - 27682
+574255*x^2 + 18562115855305211938918883531664*x + 96889010064, [7, [7, []], 
+["(tame) [2I{9}-10] page 181", [9]]]]
+Type: [2I{8}-0], p. 181
+[343, Mat([7, 3]), x^6 - 7*x^4 + 117600*x^2 + 823886, [7, [3, []], ["(tame) 
+[2I{8}-0] page 181", [8]]]]
+Type: [2I{9}-0], p. 181
+[343, Mat([7, 3]), x^6 - 7*x^4 + 117649*x^3 - 49*x^2 + 823543*x + 343, [7, [
+3, []], ["(tame) [2I{9}-0] page 181", [9]]]]
+Type: [2I*{8}-10}], p. 181
+[2401, Mat([7, 4]), x^6 + 1977326743*x^5 - 21*x^4 - 27682574402*x^3 + 657123
+62363534280139690*x^2 + 129934811447123020117269034708856*x - 45998653654473
+9960977144, [7, [7, []], ["(tame) [2I*{8}-10] page 181", [2, 2]]]]
+[81, Mat([3, 4]), x^6 + 177147*x^5 - 9*x^4 - 1062882*x^3 + 7625597485014*x^2
+ + 1350851717674586412*x - 22876792454988, [3, [7, []], ["[2I*{8}-10] pages 
+159, 181", [2, 2]]]]
+Type: [2I*{9}-10}], p. 181
+[2401, Mat([7, 4]), x^6 + 1977326743*x^5 - 21*x^4 + 65712362363506597565141*
+x^3 + 129934811447123020117172145698596*x^2 - 459986536544643071966394*x - 3
+43, [7, [7, []], ["(tame) [2I*{9}-10] page 181", [4]]]]
+Type: [II{9-8}], p. 182
+[343, Mat([7, 3]), x^6 - 2*x^5 + 46118416*x^4 - 80707228*x^3 + 2326308771696
+77*x^2 - 564950498*x + 1628413880385698, [7, [3, []], ["[II{9-8}] page 182",
+ [36]]]]
+[27, Mat([3, 3]), x^6 - 2*x^5 + 26248*x^4 - 39372*x^3 + 129238581*x^2 - 1180
+98*x + 387479538, [3, [3, []], ["[II{9-8}] page 182", [36]]]]
+Type: [III{8}], p. 182
+[2401, Mat([7, 4]), 2402*x^5 - 14*x^3 + 49*x, [7, [3, []], ["(tame) [III{8}]
+ page 182", [2, 2]]]]
+Type: [III{9}], p. 182
+[2401, Mat([7, 4]), x^5 + 16807*x^4 - 14*x^3 + 49*x, [7, [3, []], ["(tame) [
+III{9}] page 182", [4]]]]
+Type: [I{9-8-5}], pp. 182-183
+[49, Mat([7, 2]), x^6 - 6*x^5 + 46135228*x^4 - 265214472*x^3 + 2334061737433
+69*x^2 - 931878986337810*x + 3910752249023424154, [7, [4, []], ["(tame) [I{5
+-8-9}] page 182", [157]]]]
+Type: [I*{9-8-5}], pp. 183
+[2401, Mat([7, 4]), 7*x^6 - 42*x^5 + 322946596*x^4 - 1856501304*x^3 + 163384
+3216203583*x^2 - 6523152904364670*x + 27375265743163969078, [7, [4, []], ["(
+tame) [I*{5-8-9}] page 183", [4, 4]]]]
+Type: [II{9-8}], p. 183
+[343, Mat([7, 3]), x^6 - 2*x^5 + 5764788*x^4 + 28*x^3 - 80589530*x^2 - 23539
+6*x + 678505665796, [7, [4, []], ["(tame) [II{9-8}] page 183", [44]]]]
+[27, Mat([3, 3]), x^6 - 2*x^5 + 6556*x^4 + 12*x^3 - 38634*x^2 - 1476*x + 484
+2756, [3, [4, []], ["(tame) [II{9-8}] page 183", [44]]]]
+Type: [II{9-9}], p. 183
+[343, Mat([7, 3]), x^6 - 2*x^5 + 5764788*x^4 + 117677*x^3 - 80942477*x^2 + 6
+78223190400*x + 282475298, [7, [4, []], ["(tame) [II{9-9}] page 183", [45]]]
+]
+[27, Mat([3, 3]), x^6 - 2*x^5 + 6556*x^4 + 741*x^3 - 40821*x^2 + 4783680*x +
+ 59058, [3, [4, []], ["(tame) [II{9-9}] page 183", [45]]]]
+Type: [II*{9-8}], p. 184
+[343, Mat([7, 3]), 7*x^6 - 14*x^5 + 40353516*x^4 + 196*x^3 - 564126710*x^2 -
+ 1647772*x + 4749539660572, [7, [4, []], ["(tame) [II*{9-8}] page 184", [8]]
+]]
+Type: [II*{9-9}], p. 184
+[343, Mat([7, 3]), 7*x^6 - 14*x^5 + 40353516*x^4 + 823739*x^3 - 566597339*x^
+2 + 4747562332800*x + 1977327086, [7, [4, []], ["(tame) [II*{9-9}] page 184"
+, [9]]]]
+Type: [III{12}], p. 184
+[2401, Mat([7, 4]), x^6 - 14*x^3 + 117698, [7, [4, []], ["[III{12}] page 184
+", [3, 3]]]]
+[59049, Mat([3, 10]), x^6 - 6*x^3 + 738, [3, [4, []], ["[III{12}] page 184",
+ [3, 3]]]]
+Type: [III{13}], p. 184
+[2401, Mat([7, 4]), x^6 - 14*x^3 + 117649*x + 49, [7, [4, []], ["[III{13}] p
+age 184", [9]]]]
+[59049, Mat([3, 10]), x^6 - 6*x^3 + 729*x + 9, [3, [4, []], ["[III{13}] page
+ 184", [9]]]]
+Type: [III{14}], p. 184
+[2401, Mat([7, 4]), x^6 - 14*x^3 + 117649*x^2 + 49, [7, [4, []], ["[III{14}]
+ page 184", [9]]]]
+[59049, Mat([3, 10]), x^6 - 6*x^3 + 729*x^2 + 9, [3, [4, []], ["[III{14}] pa
+ge 184", [9]]]]
+Type: [III*{6}], p. 184
+[2401, Mat([7, 4]), 7*x^6 - 98*x^3 + 17150, [7, [4, []], ["[III*{6}] page 18
+4", []]]]
+[59049, Mat([3, 10]), 3*x^6 - 18*x^3 + 270, [3, [5, [Mod(0, 3), Mod(0, 3)]],
+ ["[III*{6}] page 184", []]]]
+Type: [III*{7}], p. 184
+[2401, Mat([7, 4]), 7*x^6 - 98*x^3 + 16807*x + 343, [7, [4, []], ["[III*{7}]
+ page 184", []]]]
+[59049, Mat([3, 10]), 3*x^6 - 18*x^3 + 243*x + 27, [3, [5, [Mod(0, 3), Mod(0
+, 3)]], ["[III*{7}] page 184", []]]]
+Type: [III{8}], p. 184
+[2401, Mat([7, 4]), x^6 - 14*x^3 + 2401*x^2 + 49, [7, [4, []], ["[III{8}] pa
+ge 184", [9]]]]
+[59049, Mat([3, 10]), x^6 - 6*x^3 + 81*x^2 + 9, [3, [5, [Mod(0, 3), Mod(0, 3
+)]], ["[III{8}] page 184", [9]]]]
+   echo = 1 (on)
+? genus2red(0,-x^6-6*x^2-7,3)
+[81, Mat([3, 4]), -x^6 - 6*x^2 - 7, [3, [7, []], ["(tame) [I*{1}-I*{1}-0] pa
+ge 180", [4, 4]]]]
+? genus2red(0,-9*x^6+6*x^5-8*x^4-5*x^3+5*x^2-10*x+3,3)
+[9, Mat([3, 2]), -9*x^6 + 6*x^5 - 8*x^4 - 5*x^3 + 5*x^2 - 10*x + 3, [3, [3, 
+[]], ["(tame) [I{2-8-0}] page 179", [8, 2]]]]
+? genus2red(0,3*x^6+3*x^4+3*x^3+x^2-5*x-5,3)
+[27, Mat([3, 3]), -5*x^6 - 5*x^5 + x^4 + 3*x^3 + 3*x^2 + 3, [3, [2, [Mod(0, 
+3)]], ["(tame) [III-II{4}] page 177", [9]]]]
+? genus2red(0,-3*x^6+6*x^5-1*x^4+6*x^3-6*x^2-1*x-6,3)
+[3, Mat([3, 1]), -2187*x^6 - 2430*x^5 - 1089*x^4 - 242*x^3 - 26*x^2 - x, [3,
+ [6, [Mod(2, 3)]], ["(tame) [I{1}-I{0}-1] page 170", []]]]
+? genus2red(0,(x^3+2*x+1)*(x^3+3^2*x^2+3^8),3)
+[3, Mat([3, 1]), 729*x^6 + 729*x^5 + 18*x^4 + 6580*x^3 + x^2 + 162*x + 9, [3
+, [6, [Mod(1, 3)]], ["(tame) [I{2}-I{0}-1] page 170", [2]]]]
+? P=x^6+4*x^5-24*x^4-16*x^3-52*x^2-48*x;
+? genus2red(0,P,3)
+[9, Mat([3, 2]), 729*x^6 + 2268*x^5 + 2664*x^4 + 1520*x^3 + 428*x^2 + 48*x, 
+[3, [7, []], ["(tame) [I{2}-I{2}-1] page 179", [2, 2]]]]
+? P=x^6+4*x^5+24*x^4+32*x^3+56*x^2+48*x+24;
+? genus2red(0,P,3)
+[9, Mat([3, 2]), 729*x^6 + 2268*x^5 + 3096*x^4 + 2336*x^3 + 1016*x^2 + 240*x
+ + 24, [3, [7, []], ["(tame) [I{1}-I{1}-1] page 179", []]]]
+? P=24*x^5+56*x^4+76*x^3+33*x^2-4*x-20;
+? genus2red(0,P,3)
+[9, Mat([3, 2]), 1944*x^5 + 5904*x^4 + 7196*x^3 + 4397*x^2 + 1346*x + 165, [
+3, [7, []], ["(tame) [I{2}-I{2}-1] page 179", [2, 2]]]]
+? P=-3*x^6+6*x^5-25*x^4+36*x^3-69*x^2+38*x-39;
+? genus2red(0,P,3)
+[9, Mat([3, 2]), -2187*x^6 - 6804*x^5 - 9000*x^4 - 6464*x^3 - 2656*x^2 - 592
+*x - 56, [3, [7, []], ["(tame) [I{1}-I{1}-1] page 179", []]]]
+? P=-5*x^5+5*x^4+10*x^3-7;
+? genus2red(1,P,3)
+[9, Mat([3, 2]), -1620*x^5 - 2520*x^4 - 1520*x^3 - 440*x^2 - 60*x - 3, [3, [
+7, []], ["(tame) [I{1}-I{2}-1] page 179", [2]]]]
+? P=-5*x^6-3*x^5-10*x^4-10*x^3-7;
+? genus2red(1,P,3)
+[3, Mat([3, 1]), -14580*x^6 - 59292*x^5 - 100800*x^4 - 91720*x^3 - 47120*x^2
+ - 12960*x - 1491, [3, [6, [Mod(1, 3)]], ["(tame) [I{1}-I{0}-1] page 170", [
+]]]]
+? P=3*x^5+5*x^4+5*x-4;
+? genus2red(1,P,3)
+[3, Mat([3, 1]), 972*x^5 + 1260*x^4 + 640*x^3 + 160*x^2 + 20*x + 1, [3, [6, 
+[Mod(1, 3)]], ["(tame) [I{2}-I{0}-1] page 170", [2]]]]
+? Q=x^2+x;P=-9*x^6+6*x^5-8*x^4-5*x^3+5*x^2-10*x+3;
+? genus2red(Q,P,3)
+[3, Mat([3, 1]), -26244*x^6 - 138024*x^5 - 302679*x^4 - 354290*x^3 - 233475*
+x^2 - 82136*x - 12052, [3, [6, [Mod(0, 3)]], ["(tame) [I{12}-I{0}-1] page 17
+0", [12]]]]
+? Q=x^3+1;P=-7*x^6+5*x^3+5*x^2-6*x+1;
+? genus2red(Q,P,3)
+[3, Mat([3, 1]), -27*x^6 + 22*x^3 + 20*x^2 - 24*x + 5, [3, [6, [Mod(2, 3)]],
+ ["(tame) [I{1}-I{0}-1] page 170", []]]]
+? genus2red(0,27*x^5+97*x^4+118*x^3+60*x^2+13*x+1,3)
+[729, Mat([3, 6]), 27*x^5 + 367*x^4 + 1974*x^3 + 5256*x^2 + 6933*x + 3627, [
+3, [6, [Mod(0, 3)]], ["[IV-II{6}] page 175", [20]]]]
+? genus2red(x,-x^6-3*x^4-10*x^2-1,3)
+[729, Mat([3, 6]), -4*x^6 - 12*x^4 - 39*x^2 - 4, [3, [5, [Mod(0, 3), Mod(0, 
+3)]], ["[IV*-IV*-0] pages 160-175", [3, 3]]]]
+? genus2red(x^3+x^2+x+1,-60*x^6-203*x^5-291*x^4-244*x^3-129*x^2-41*x-7)
+[729, [2, -1; 3, 6], -239*x^6 - 810*x^5 - 1161*x^4 - 972*x^3 - 513*x^2 - 162
+*x - 27, [[2, [5, [Mod(0, 2), Mod(0, 2)]], []], [3, [5, [Mod(0, 3), Mod(0, 3
+)]], ["[2IV*-0] page 165", [3]]]]]
+? genus2red(0,6*x^6+5*x^4+x^2+1,7)
+[2401, Mat([7, 4]), 6*x^6 + 180*x^5 + 2255*x^4 + 15100*x^3 + 57001*x^2 + 115
+010*x + 96901, [7, [5, [Mod(0, 7), Mod(0, 7)]], ["(tame) [II-II-0] page 163"
+, []]]]
+? genus2red(x^3-1,1)
+[18225, [3, 6; 5, 2], x^6 - 2*x^3 + 5, [[3, [5, [Mod(0, 3), Mod(0, 3)]], ["[
+II-II-0] pages 159-174", []]], [5, [5, [Mod(0, 5), Mod(0, 5)]], ["[I{0}-II-0
+] page 159", []]]]]
+? print("Total time spent: ",gettime);
+Total time spent: 72
diff --git a/src/test/32/graph b/src/test/32/graph
new file mode 100644
index 0000000..d86c32e
--- /dev/null
+++ b/src/test/32/graph
@@ -0,0 +1,65 @@
+   echo = 1 (on)
+? plotinit(0,500,500)
+? plotmove(0,0,0);plotbox(0,500,500)
+? plotmove(0,200,150)
+? plotcursor(0)
+[200, 150]
+? psdraw([0,0,0])
+? plotinit(1,700,700)
+? plotkill(1)
+? plotmove(0,0,900);plotlines(0,900,0)
+? plotlines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+? plotmove(0,243,583);plotcursor(0)
+[243, 583]
+? plot(x=-5,5,sin(x),-1,1)
+
+        1 x""x_''''''''''''''''''''''''''''''''''_x""x'''''''''''''''''''|
+          |    x                                _     "_                 |
+          |     x                              _        _                |
+          |      x                            _                          |
+          |       _                                      "               |
+          |                                  "            x              |
+          |        x                        _                            |
+          |                                                "             |
+          |         "                      x                _            |
+          |          _                                                   |
+          |                               "                  x           |
+          ````````````x``````````````````_````````````````````````````````
+          |                                                   "          |
+          |            "                x                      _         |
+          |             _                                                |
+          |                            "                        x        |
+          |              x            _                                  |
+          |               _                                      "       |
+          |                          "                            x      |
+          |                "        "                              x     |
+          |                 "_     "                                x    |
+       -1 |...................x__x".................................."x__x
+          -5                                                             5
+? plotpoints(0,225,334)
+? plotpoints(0,vector(10,k,10*k),vector(10,k,5*k*k))
+? psdraw([0,20,20])
+? psploth(x=-5,5,sin(x))
+[-5.000000000000000000, 5.000000000000000000, -0.9999964107564721649, 0.9999
+964107564721649]
+? psploth(t=0,2*Pi,[sin(5*t),sin(7*t)],1,100)
+[-0.9998741276738750683, 0.9998741276738750683, -0.9998741276738750683, 0.99
+98741276738750683]
+? psplothraw(vector(100,k,k),vector(100,k,k*k/100))
+[1.0000000000000000000, 100.00000000000000000, 0.010000000000000000208, 100.
+00000000000000000]
+? plotmove(0,50,50);plotrbox(0,50,50)
+? plotrline(0,200,150)
+? plotcursor(0)
+[250, 200]
+? plotrmove(0,5,5);plotcursor(0)
+[255, 205]
+? plotrpoint(0,20,20)
+? plotinit(3,600,600);plotscale(3,-7,7,-2,2);plotcursor(3)
+[-7, 2]
+? plotmove(0,100,100);plotstring(0,Pi)
+? plotmove(0,200,200);plotstring(0,"(0,0)")
+? psdraw([0,10,10])
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 16
diff --git a/src/test/32/help b/src/test/32/help
new file mode 100644
index 0000000..eb17c23
--- /dev/null
+++ b/src/test/32/help
@@ -0,0 +1,149 @@
+x: user defined variable
+
+sin(x): sine of x.
+
+f =
+  ()->1
+
+echo: default
+
+echo: default
+
+log: default
+
+new is aliased to:
+
+sin(x): sine of x.
+
+test1
+
+test2
+
+test3
+
+test4
+
+new is aliased to:
+
+test2
+
+does_not_exist: unknown identifier
+
+Help topics: for a list of relevant subtopics, type ?n for n in
+  0: user-defined functions (aliases, installed and user functions)
+  1: Standard monadic or dyadic OPERATORS
+  2: CONVERSIONS and similar elementary functions
+  3: TRANSCENDENTAL functions
+  4: NUMBER THEORETICAL functions
+  5: Functions related to ELLIPTIC CURVES
+  6: Functions related to general NUMBER FIELDS
+  7: POLYNOMIALS and power series
+  8: Vectors, matrices, LINEAR ALGEBRA and sets
+  9: SUMS, products, integrals and similar functions
+ 10: GRAPHIC functions
+ 11: PROGRAMMING under GP
+ 12: The PARI community
+
+Also:
+  ? functionname (short on-line help)
+  ?\             (keyboard shortcuts)
+  ?.             (member functions)
+Extended help (if available):
+  ??             (opens the full user's manual in a dvi previewer)
+  ??  tutorial / refcard / libpari (tutorial/reference card/libpari manual)
+  ??  keyword    (long help text about "keyword" from the user's manual)
+  ??? keyword    (a propos: list of related functions).
+Member functions, followed by relevant objects
+
+a1-a6, b2-b8, c4-c6 : coeff. of the curve.         ell
+area : area                                        ell
+bid  : big ideal                     bid,                     bnr
+bnf  : big number field                                   bnf,bnr
+clgp : class group                   bid,                 bnf,bnr
+cyc  : cyclic decomposition (SNF)    bid,     clgp,ell,   bnf,bnr
+diff, codiff: different and codifferent                nf,bnf,bnr
+disc : discriminant                                ell,nf,bnf,bnr,rnf
+e, f : inertia/residue  degree           prid
+fu   : fundamental units                                  bnf,bnr
+gen  : generators                    bid,prid,clgp,ell,   bnf,bnr,    gal
+group: group                                       ell,          ,rnf,gal
+index: index                                           nf,bnf,bnr
+j    : j-invariant                                 ell
+mod  : modulus                       bid,                     bnr,    gal
+nf   : number field                                    nf,bnf,bnr,rnf
+no   : number of elements            bid,     clgp,ell,   bnf,bnr
+omega, eta: [w1,w2] and [eta1, eta2]               ell
+orders: relative orders of generators                                 gal
+p    : rational prime                    prid,     ell,           rnf,gal
+pol  : defining polynomial                             nf,bnf,bnr,    gal
+polabs: defining polynomial over Q                                rnf
+reg  : regulator                                          bnf,bnr
+roots: roots                                       ell,nf,bnf,bnr,    gal
+sign,r1,r2 : signature                                 nf,bnf,bnr
+t2   : t2 matrix                                       nf,bnf,bnr
+tate : Tate's [u^2, u, q, [a,b]]                   ell
+tu   : torsion unit and its order                         bnf,bnr
+zk   : integral basis                                  nf,bnf,bnr,rnf
+zkst : structure of (Z_K/m)*         bid,                     bnr
+
+#       : enable/disable timer
+##      : print time for last result
+\\      : comment up to end of line
+\a {n}  : print result in raw format (readable by PARI)
+\B {n}  : print result in beautified format
+\c      : list all commands (same effect as ?*)
+\d      : print all defaults
+\e {n}  : enable/disable echo (set echo=n)
+\g {n}  : set debugging level
+\gf{n}  : set file debugging level
+\gm{n}  : set memory debugging level
+\h {m-n}: hashtable information
+\l {f}  : enable/disable logfile (set logfile=f)
+\m {n}  : print result in prettymatrix format
+\o {n}  : set output method (0=raw, 1=prettymatrix, 2=prettyprint, 3=2-dim)
+\p {n}  : change real precision
+\ps{n}  : change series precision
+\q      : quit completely this GP session
+\r {f}  : read in a file
+\s      : print stack information
+\t      : print the list of PARI types
+\u      : print the list of user-defined functions
+\um     : print the list of user-defined member functions
+\v      : print current version of GP
+\w {nf} : write to a file
+\x {n}  : print complete inner structure of result
+\y {n}  : disable/enable automatic simplification (set simplify=n)
+
+{f}=optional filename. {n}=optional integer
+
+
+cmp      divrem   lex      max      min      shift    shiftmul sign
+vecmax   vecmin   
+
+List of the PARI types:
+  t_INT    : long integers     [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]
+  t_REAL   : long real numbers [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]
+  t_INTMOD : integermods       [ code ] [ mod  ] [ integer ]
+  t_FRAC   : irred. rationals  [ code ] [ num. ] [ den. ]
+  t_FFELT  : finite field elt. [ code ] [ cod2 ] [ elt ] [ mod ] [ p ]
+  t_COMPLEX: complex numbers   [ code ] [ real ] [ imag ]
+  t_PADIC  : p-adic numbers    [ cod1 ] [ cod2 ] [ p ] [ p^r ] [ int ]
+  t_QUAD   : quadratic numbers [ cod1 ] [ mod  ] [ real ] [ imag ]
+  t_POLMOD : poly mod          [ code ] [ mod  ] [ polynomial ]
+  -------------------------------------------------------------
+  t_POL    : polynomials       [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]
+  t_SER    : power series      [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]
+  t_RFRAC  : irred. rat. func. [ code ] [ num. ] [ den. ]
+  t_QFR    : real qfb          [ code ] [ a ] [ b ] [ c ] [ del ]
+  t_QFI    : imaginary qfb     [ code ] [ a ] [ b ] [ c ]
+  t_VEC    : row vector        [ code ] [  x_1  ] ... [  x_k  ]
+  t_COL    : column vector     [ code ] [  x_1  ] ... [  x_k  ]
+  t_MAT    : matrix            [ code ] [ col_1 ] ... [ col_k ]
+  t_LIST   : list              [ code ] [ n ] [ nmax ][ vec ]
+  t_STR    : string            [ code ] [ man_1 ] ... [ man_k ]
+  t_VECSMALL: vec. small ints  [ code ] [ x_1 ] ... [ x_k ]
+  t_CLOSURE: functions [ code ] [ arity ] [ code ] [ operand ] [ data ] [ te
+xt ]
+  t_ERROR  : error context     [ code ] [ errnum ] [ dat_1 ] ... [ dat_k ]
+
+Total time spent: 0
diff --git a/src/test/32/history b/src/test/32/history
new file mode 100644
index 0000000..2e56d9c
--- /dev/null
+++ b/src/test/32/history
@@ -0,0 +1,18 @@
+2
+3
+4
+5
+  ***   History result %-1 not available [%1-%5]
+
+0
+3
+3
+1
+1
+1
+  ***   History result %11 not available [%13-%13]
+
+  ***   History result %12 has been deleted (histsize changed)
+
+1
+Total time spent: 4
diff --git a/src/test/32/ideal b/src/test/32/ideal
new file mode 100644
index 0000000..3193053
--- /dev/null
+++ b/src/test/32/ideal
@@ -0,0 +1,147 @@
+[;]
+[[1]~, [0]~]
+  ***   at top-level: idealaddtoone(Q,2,[;
+  ***                 ^--------------------
+  *** idealaddtoone: elements not coprime in idealaddtoone:
+    Mat(2)
+    [;]
+  ***   at top-level: idealaddtoone(Q,[;],
+  ***                 ^--------------------
+  *** idealaddtoone: elements not coprime in idealaddtoone:
+    [;]
+    [;]
+[[1]~, [0]~]
+[[1]~, [0]~]
+  ***   at top-level: idealaddtoone(Q,[1,[
+  ***                 ^--------------------
+  *** idealaddtoone: incorrect type in idealaddmultoone [integer matrix] (t_MAT).
+  ***   at top-level: ideallog(Q,2,idealst
+  ***                 ^--------------------
+  *** ideallog: elements not coprime in zlog_pk:
+    2
+    [2, [2]~, 1, 1, 1]
+  ***   at top-level: idealstar(Qi,matdiag
+  ***                 ^--------------------
+  *** idealstar: elements not coprime in idealaddtoone:
+    [6, 0; 0, 3]
+    [2, 0; 0, 2]
+[512, [16, 8, 4], [[-1, -2]~, 29, [0, -1]~]]
+
+[1/2   0]
+
+[  0 1/2]
+
+[1]~
+
+[2 1]
+
+[0 1]
+
+
+[2 0 0]
+
+[0 1 0]
+
+[0 0 1]
+
+
+[1/12    0    0]
+
+[   0 1/12    0]
+
+[   0    0 1/12]
+
+
+[2 0 0]
+
+[0 2 0]
+
+[0 0 2]
+
+
+[1/6   0   0]
+
+[  0 1/6   0]
+
+[  0   0 1/6]
+
+
+[1/3   0   0]
+
+[  0 1/6   0]
+
+[  0   0 1/6]
+
+
+[1 0   0]
+
+[0 1   0]
+
+[0 0 1/2]
+
+0
+0
+1
+1
+
+[1725 35 1704]
+
+[   0  5    4]
+
+[   0  0    1]
+
+5
+
+[1 0   0]
+
+[0 1   0]
+
+[0 0 1/2]
+
+
+[1   0   0]
+
+[0 1/2   0]
+
+[0   0 1/2]
+
+7
+8
+9
+10
+11
+
+[1 0 0]
+
+[0 1 0]
+
+[0 0 1]
+
+[[1, 0, 0; 0, 1, 0; 0, 0, 1], Mat([2, 1])]
+
+[[67452192952521724999, [-16711321285323715217, 1]~, 1, 1, [1671132128532371
+5218, -1; 1, 16711321285323715217]] 1]
+
+[[762234946175168528650011228121, [-63146078120386376378131819641, 1]~, 1, 1
+, [63146078120386376378131819642, -1; 1, 63146078120386376378131819641]] -1]
+
+
+[67452192952521724999 19368419142280277485321497629511555041951889852345/762
+234946175168528650011228121]
+
+[0 1/762234946175168528650011228121]
+
+[[67452192952521724999, 50740871667198009782; 0, 1], [7622349461751685286500
+11228121, 699088868054782152271879408480; 0, 1]]
+[[67452192952521724999, 50740871667198009782; 0, 1], 1]
+[1, 1]
+[1, 2]
+[[1, 0; 0, 1], [2, 0; 0, 2]]
+[18, 9]~
+[-85124952/2401, 33204681/2401]~
+[249/2401, 1644/2401]~
+[-7, -14]~
+[14, 0]~
+  *** nfinit: Warning: non-monic polynomial. Result of the form [nf,c].
+[[2, [0, 1]~, 2, 1, [0, -2; 1, 0]]]
+Total time spent: 8
diff --git a/src/test/32/idealappr b/src/test/32/idealappr
new file mode 100644
index 0000000..7304d10
--- /dev/null
+++ b/src/test/32/idealappr
@@ -0,0 +1,9 @@
+[3, 0]~
+[23/2, [6, 1/2]~]
+[-23/2, 0]~
+[0, 0]
+  ***   at top-level: idealtwoelt(K,[;],1)
+  ***                 ^--------------------
+  *** idealtwoelt: domain error in idealtwoelt2: element mod ideal != 0
+[0, 0]~
+Total time spent: 0
diff --git a/src/test/32/idealramgroups b/src/test/32/idealramgroups
new file mode 100644
index 0000000..07c4a14
--- /dev/null
+++ b/src/test/32/idealramgroups
@@ -0,0 +1,21 @@
+2:[[2, 1], [2, 1], [2, 1], [2, 1]]
+3:[[15, 1]]
+5:[[2, 1], [2, 1]]
+7:[[2, 1]]
+11:[[2, 1]]
+13:[[15, 1]]
+17:[[2, 1]]
+19:[[10, 1], [5, 1]]
+23:[[2, 1]]
+29:[[2, 1]]
+31:[[15, 1]]
+37:[[15, 1], [3, 1]]
+[Vecsmall([5, 6, 4, 3, 1, 2]), [[[Vecsmall([5, 6, 4, 3, 1, 2])], Vecsmall([2
+])]]]
+[[[Vecsmall([2, 1, 4, 3, 6, 5, 8, 7]), Vecsmall([3, 4, 1, 2, 7, 8, 5, 6]), V
+ecsmall([5, 6, 7, 8, 1, 2, 3, 4])], Vecsmall([2, 2, 2])], [[Vecsmall([2, 1, 
+4, 3, 6, 5, 8, 7]), Vecsmall([3, 4, 1, 2, 7, 8, 5, 6])], Vecsmall([2, 2])], 
+[[Vecsmall([2, 1, 4, 3, 6, 5, 8, 7]), Vecsmall([3, 4, 1, 2, 7, 8, 5, 6])], V
+ecsmall([2, 2])], [[Vecsmall([2, 1, 4, 3, 6, 5, 8, 7])], Vecsmall([2])], [[V
+ecsmall([2, 1, 4, 3, 6, 5, 8, 7])], Vecsmall([2])]]
+Total time spent: 1188
diff --git a/src/test/32/incgam b/src/test/32/incgam
new file mode 100644
index 0000000..952c47f
--- /dev/null
+++ b/src/test/32/incgam
@@ -0,0 +1,73 @@
+      1/2,          -100: 5.0 e-18
+   10 - I,    19 + 236*I: 3.4 e-18
+   10 - I,          -100: 3.8 e-18
+1 + 128*I,       -1/10*I: 1.9 e-17
+1 + 128*I, 1/10 - 1/10*I: 6.5 e-18
+      1/2,          -100: 1.0 e-37
+   10 - I,    19 + 236*I: 6.3 e-37
+   10 - I,          -100: 5.8 e-37
+1 + 128*I,       -1/10*I: 8.6 e-37
+1 + 128*I, 1/10 - 1/10*I: 2.1 e-37
+      1/2,          -100: 4.7 e-43
+   10 - I,          -100: 7.9 e-56
+1: -37
+2: -37
+3: -38
+4: -39
+5: -37
+6: -37
+7: oo
+8: -37
+9: oo
+10: -34
+11: oo
+12: -41
+13: -40
+1: -75
+2: -75
+3: -77
+4: -77
+5: -76
+6: -75
+7: -76
+8: -75
+9: -76
+10: -72
+11: -77
+12: -79
+13: -79
+6.4517096605632180286130396475962100207 E-43429453
+-0.0096304981549875294045330406967324266004 + 0.0104448408245333075664155335
+90425336552*I
+3.6835977616820321802351926205081189877 E-46
+0.033148544714002591996135923592143390256
+[0.048900510708061119567239835228049522318, 0.003779352409848906478874860132
+4664148561, 0.00036008245216265865929539411577179720024, 3.76656228439249017
+72557995950752726710 E-5, 4.1569689296853242774028598102781906834 E-6]
+   realprecision = 1001 significant digits (1000 digits displayed)
+3.68359776168203218023519262050811898765522013690956761970324308577568037914
+9250955037826947160993268362230577110061496836716989214907855071445311470563
+2214214800120311153618035942174272929063643378947115395627352197174490634249
+2683012033541722036066655328102708417224861577032395241919552543821704857579
+1038133881414147745573634350151991309184589035264539926631750948004685156693
+1899374361029305819637078712617022128735026710549374269865118275697787239064
+9991027682952231969799549304090829468335748935154281470949071433111492071075
+1767849583590888814450823588721920568263412763123076003634199144923928930230
+2664745888505585350305095849977148442159142952183358896948374051498045266578
+8430696482922647521423833733233609936621457658644695029880537347596251466117
+5788754599498196161120801923825870392159899899969875656841020930930028966473
+7128930613953196310035735416832096667412906792274453172606232569623655768685
+0600705332240514673617947823961471228466621963387238011480537605649806838965
+7919746393664 E-46
+   realprecision = 481 significant digits
+4.75192490656016273728795810646514224344948226385534946774854953981091171184
+5548377731754871666027106541752645628899410605265552482172045603954059223044
+9909582386495215175154235730928812285673186372640110666892550763379643115805
+1645655068324986893656866348640914630478382436361571896386505720797343025328
+1801089025819604231751731641093222001525360979162684147070957434471241677012
+3171321323534000876101297696945758112884328591356813522962187412618386959055
+56983544264467232044835043 E-68
+  ***   at top-level: eint1(0)
+  ***                 ^--------
+  *** eint1: domain error in eint1: x = 0
+Total time spent: 188
diff --git a/src/test/32/interpol b/src/test/32/interpol
new file mode 100644
index 0000000..82d8181
--- /dev/null
+++ b/src/test/32/interpol
@@ -0,0 +1,11 @@
+x + 1
+Mod(1, 7)*x + Mod(1, 7)
+Mod(1, 7)
+-1/6*x^2 + 3/2*x + 2/3
+Mod(3, 7)
+2.5416666666666666666666666666666666667
+0.041666666666666666666666666666666666667
+0
+0
+2
+Total time spent: 0
diff --git a/src/test/32/intnum b/src/test/32/intnum
new file mode 100644
index 0000000..235b013
--- /dev/null
+++ b/src/test/32/intnum
@@ -0,0 +1,117 @@
+? allocatemem(20*10^6);
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+? check(a,b)=my(t=abs((a-b)/b));if(t,ceil(log(t)/log(10)),"-oo");
+? oo=[1];
+? \p96
+   realprecision = 96 significant digits
+? check(intcirc(s=1,0.5,zeta(s)),1)
+-93
+? check(intlaplaceinv(x=2,1,1/x),1)
+-53
+? m=intnumstep();
+? check(intlaplaceinv(x=2,1,1/x,m+1),1)
+-94
+? check(intlaplaceinv(x=5,1,1/x),1)
+-83
+? check(intlaplaceinv(x=100,1,1/x),1)
+-52
+? A=intmellininv(s=2,4,gamma(s)^3);
+? tab=intfuncinit(t=[-oo,4.5],[oo,4.5],gamma(2+I*t)^3,1);
+? check(intmellininvshort(2,4,tab),A)
+-94
+? f(x)=1/(exp(x)-1)-exp(-x)/x;
+? F=truncate(f(t+O(t^7)));
+? g(x)=if(x>1e-18,f(x),subst(F,t,x));
+? check(intnum(x=0,[oo,1],f(x)),Euler)
+"-oo"
+? check(intnum(x=0,[oo,1],g(x)),Euler)
+"-oo"
+? check(intnum(x=0,1,1/sqrt(x)),2)
+-59
+? check(intnum(x=[0,-1/2],1,1/sqrt(x)),2)
+"-oo"
+? check(intnum(x=0,[oo,1],sin(x)/x),Pi/2)
+-2
+? check(intnum(x=0,[oo,-I],sin(x)/x),Pi/2)
+"-oo"
+? check(intnum(x=0,[oo,-2*I],sin(2*x)/x),Pi/2)
+"-oo"
+? A=intnum(x=0,1,(1-cos(x))/x^2)+intnum(x=1,oo,1/x^2)-intnum(x=1,[oo,I],cos(x)/x^2);
+? check(A,Pi/2)
+-96
+? check(intnum(x=0,[oo,1],sin(x)^3*exp(-x)),3/10)
+-96
+? check(intnum(x=0,[oo,-I],sin(x)^3*exp(-x)),3/10)
+-88
+? tab=intnuminit(0,[oo,-I],m+1);
+? check(intnum(x=0,oo,sin(x)^3*exp(-x),tab),3/10)
+-96
+? check(intnum(x=0,[oo,-I],x^2*sin(x)),-2)
+"-oo"
+? tab=intnuminit(-1,1);
+? check(intnum(x=-1,1,intnum(y=-sqrt(1-x^2),sqrt(1-x^2),x^2+y^2,tab),tab),Pi/2)
+-93
+? \p308
+   realprecision = 308 significant digits
+? a=sumpos(n=1,1/(n^3+n+1));
+? tab=sumnuminit(2);
+? b=sumnum(n=1,2,1/(n^3+n+1),tab);
+? check(a,b)
+-305
+? check(sumnum(n=1,2,1/(n^3+n+1),tab,1),a)
+-305
+? c=sumnum(n=1,2,1/(n^2+1),tab,1);
+? d=sumpos(n=1,1/(n^2+1));
+? check(c,d)
+-305
+? check(sumnum(n=1,2,n^(-4/3),,1),zeta(4/3))
+-110
+? tab=sumnuminit([2,-3/2]);
+? check(sumnum(n=1,[2,-3/2],1/(n*sqrt(n)),tab,1),zeta(3/2))
+-305
+? check(suminf(n=1,2^(-n)),1)
+-307
+? check(sumpos(n=1,2^(-n)),1)
+-307
+? check(sumnum(n=1,[2,log(2)],2^(-n),intnumstep()+1,1),1)
+-304
+? tab=sumnuminit(2,,-1);
+? a=sumnumalt(n=1,2,1/(n^3+n+1),tab,1);
+? b=sumalt(n=1,(-1)^n/(n^3+n+1));
+? check(a,b)
+-307
+? \p96
+   realprecision = 96 significant digits
+? T=intnuminitgen(t,0,[1],exp(2*sinh(t)));
+? check(intnum(x=0,[1],1/(1+x^2),T),Pi/2)
+"-oo"
+? T=intnuminitgen(t,0,[[1],1],exp(t-exp(-t)));
+? check(intnum(x=0,[[1],1],exp(-x),T),1)
+"-oo"
+? intfourierexp(t=0,[[1],1],1/2,exp(-t^2))
+0.07515645001618094506724269142337819200573257288846457257682421833136020654
+01315089580450881170413 - 0.408148557374490719018171389392074027664154880556
+033855962874575972355801042051564345987971241498*I
+? intfouriercos(t=0,[[1],1],1/2,exp(-t^2))
+0.07515645001618094506724269142337819200573257288846457257682421833136020654
+01315089580450881170413
+? intfouriersin(t=0,[[1],1],1/2,exp(-t^2))
+0.40814855737449071901817138939207402766415488055603385596287457597235580104
+2051564345987971241498
+? \p38
+   realprecision = 38 significant digits
+? intnumromb(x=0,1,sin(x))
+0.45969769413186028259906339255702339518
+? intnumromb(x=0,1,sin(x),1)
+0.45969769413186028259906339255702338970
+? intnumromb(x=1,100,exp(-x^2),2)
+0.13940279264033098824961630553871957887
+? intnumromb(x=0,1,sin(x)/x,3)
+0.94608307036718301494135331382317964743
+? f(x)=-log(cos(x));
+? F=truncate(f(t+O(t^16)));
+? g(x)=if(x>1e-2,f(x),subst(F,t,x));
+? sumpos(n=1,g(1/n))
+0.94536905472633293526609521540827019810
+? print("Total time spent: ",gettime);
+Total time spent: 20736
diff --git a/src/test/32/io b/src/test/32/io
new file mode 100644
index 0000000..e25df43
--- /dev/null
+++ b/src/test/32/io
@@ -0,0 +1,8 @@
+["123", "456", "a7b", "\\frac{1}{2}"]
+[1, 3]
+1
+setting x
+setting F
+setting del
+()->system(Str("rm -f ",F))
+Total time spent: 4
diff --git a/src/test/32/ispower b/src/test/32/ispower
new file mode 100644
index 0000000..267ae14
--- /dev/null
+++ b/src/test/32/ispower
@@ -0,0 +1,1194 @@
+[101, 6]
+[5, 2160]
+[3, 21218]
+[5, 21218]
+[5, 84872]
+[21, 21218]
+[35, 21218]
+[105, 21218]
+[100003, 103]
+[121, 541]
+[2, 2]
+[3, 2]
+[2, 3]
+[4, 2]
+[2, 5]
+[3, 3]
+[5, 2]
+[2, 6]
+[2, 7]
+[6, 2]
+[4, 3]
+[2, 10]
+[2, 11]
+[3, 5]
+[7, 2]
+[2, 12]
+[2, 13]
+[2, 14]
+[3, 6]
+[2, 15]
+[5, 3]
+[8, 2]
+[2, 17]
+[2, 18]
+[3, 7]
+[2, 19]
+[2, 20]
+[2, 21]
+[2, 22]
+[9, 2]
+[2, 23]
+[2, 24]
+[4, 5]
+[2, 26]
+[6, 3]
+[2, 28]
+[2, 29]
+[2, 30]
+[2, 31]
+[3, 10]
+[10, 2]
+[2, 33]
+[2, 34]
+[2, 35]
+[4, 6]
+[3, 11]
+[2, 37]
+[2, 38]
+[2, 39]
+[2, 40]
+[2, 41]
+[3, 12]
+[2, 42]
+[2, 43]
+[2, 44]
+[2, 45]
+[11, 2]
+[2, 46]
+[7, 3]
+[3, 13]
+[2, 47]
+[2, 48]
+[4, 7]
+[2, 50]
+[2, 51]
+[2, 52]
+[3, 14]
+[2, 53]
+[2, 54]
+[2, 55]
+[5, 5]
+[2, 56]
+[2, 57]
+[2, 58]
+[3, 15]
+[2, 59]
+[2, 60]
+[2, 61]
+[2, 62]
+[2, 63]
+[12, 2]
+[2, 65]
+[2, 66]
+[2, 67]
+[2, 68]
+[2, 69]
+[2, 70]
+[3, 17]
+[2, 71]
+[2, 72]
+[2, 73]
+[2, 74]
+[2, 75]
+[2, 76]
+[3, 18]
+[2, 77]
+[2, 78]
+[2, 79]
+[2, 80]
+[8, 3]
+[2, 82]
+[3, 19]
+[2, 83]
+[2, 84]
+[2, 85]
+[2, 86]
+[2, 87]
+[2, 88]
+[5, 6]
+[2, 89]
+[3, 20]
+[2, 90]
+[13, 2]
+[2, 91]
+[2, 92]
+[2, 93]
+[2, 94]
+[2, 95]
+[2, 96]
+[3, 21]
+[2, 97]
+[2, 98]
+[2, 99]
+[4, 10]
+[2, 101]
+[2, 102]
+[2, 103]
+[3, 22]
+[2, 104]
+[2, 105]
+[2, 106]
+[2, 107]
+[2, 108]
+[2, 109]
+[2, 110]
+[3, 23]
+[2, 111]
+[2, 112]
+[2, 113]
+[2, 114]
+[2, 115]
+[2, 116]
+[2, 117]
+[3, 24]
+[2, 118]
+[2, 119]
+[2, 120]
+[4, 11]
+[2, 122]
+[2, 123]
+[2, 124]
+[6, 5]
+[2, 126]
+[2, 127]
+[14, 2]
+[2, 129]
+[5, 7]
+[2, 130]
+[2, 131]
+[2, 132]
+[3, 26]
+[2, 133]
+[2, 134]
+[2, 135]
+[2, 136]
+[2, 137]
+[2, 138]
+[2, 139]
+[2, 140]
+[9, 3]
+[2, 141]
+[2, 142]
+[2, 143]
+[4, 12]
+[2, 145]
+[2, 146]
+[2, 147]
+[2, 148]
+[3, 28]
+[2, 149]
+[2, 150]
+[2, 151]
+[2, 152]
+[2, 153]
+[2, 154]
+[2, 155]
+[2, 156]
+[3, 29]
+[2, 157]
+[2, 158]
+[2, 159]
+[2, 160]
+[2, 161]
+[2, 162]
+[2, 163]
+[2, 164]
+[3, 30]
+[2, 165]
+[2, 166]
+[2, 167]
+[2, 168]
+[4, 13]
+[2, 170]
+[2, 171]
+[2, 172]
+[3, 31]
+[2, 173]
+[2, 174]
+[2, 175]
+[2, 176]
+[2, 177]
+[2, 178]
+[2, 179]
+[2, 180]
+[2, 181]
+[15, 2]
+[2, 182]
+[2, 183]
+[2, 184]
+[2, 185]
+[2, 186]
+[2, 187]
+[2, 188]
+[2, 189]
+[3, 33]
+[2, 190]
+[2, 191]
+[2, 192]
+[2, 193]
+[2, 194]
+[2, 195]
+[4, 14]
+[2, 197]
+[2, 198]
+[3, 34]
+[2, 199]
+[2, 200]
+[2, 201]
+[2, 202]
+[2, 203]
+[2, 204]
+[2, 205]
+[2, 206]
+[2, 207]
+[3, 35]
+[2, 208]
+[2, 209]
+[2, 210]
+[2, 211]
+[2, 212]
+[2, 213]
+[2, 214]
+[2, 215]
+[6, 6]
+[2, 217]
+[2, 218]
+[2, 219]
+[2, 220]
+[2, 221]
+[2, 222]
+[2, 223]
+[2, 224]
+[4, 15]
+[3, 37]
+[2, 226]
+[2, 227]
+[2, 228]
+[2, 229]
+[2, 230]
+[2, 231]
+[2, 232]
+[2, 233]
+[2, 234]
+[3, 38]
+[2, 235]
+[2, 236]
+[2, 237]
+[2, 238]
+[2, 239]
+[2, 240]
+[2, 241]
+[2, 242]
+[10, 3]
+[3, 39]
+[2, 244]
+[2, 245]
+[2, 246]
+[2, 247]
+[2, 248]
+[2, 249]
+[2, 250]
+[2, 251]
+[2, 252]
+[3, 40]
+[2, 253]
+[2, 254]
+[2, 255]
+[16, 2]
+[2, 257]
+[2, 258]
+[2, 259]
+[2, 260]
+[2, 261]
+[2, 262]
+[3, 41]
+[2, 263]
+[2, 264]
+[2, 265]
+[2, 266]
+[2, 267]
+[2, 268]
+[2, 269]
+[2, 270]
+[2, 271]
+[2, 272]
+[3, 42]
+[2, 273]
+[2, 274]
+[2, 275]
+[2, 276]
+[2, 277]
+[2, 278]
+[2, 279]
+[7, 5]
+[2, 280]
+[2, 281]
+[3, 43]
+[2, 282]
+[2, 283]
+[2, 284]
+[2, 285]
+[2, 286]
+[2, 287]
+[2, 288]
+[4, 17]
+[2, 290]
+[2, 291]
+[3, 44]
+[2, 292]
+[2, 293]
+[2, 294]
+[2, 295]
+[2, 296]
+[2, 297]
+[2, 298]
+[2, 299]
+[2, 300]
+[2, 301]
+[3, 45]
+[2, 302]
+[2, 303]
+[2, 304]
+[2, 305]
+[2, 306]
+[2, 307]
+[2, 308]
+[2, 309]
+[2, 310]
+[2, 311]
+[3, 46]
+[2, 312]
+[2, 313]
+[2, 314]
+[2, 315]
+[2, 316]
+[5, 10]
+[2, 317]
+[2, 318]
+[2, 319]
+[2, 320]
+[2, 321]
+[2, 322]
+[3, 47]
+[2, 323]
+[4, 18]
+[2, 325]
+[2, 326]
+[2, 327]
+[2, 328]
+[2, 329]
+[2, 330]
+[2, 331]
+[2, 332]
+[3, 48]
+[2, 333]
+[2, 334]
+[2, 335]
+[2, 336]
+[2, 337]
+[2, 338]
+[2, 339]
+[2, 340]
+[2, 341]
+[2, 342]
+[6, 7]
+[2, 344]
+[2, 345]
+[2, 346]
+[2, 347]
+[2, 348]
+[2, 349]
+[2, 350]
+[2, 351]
+[2, 352]
+[2, 353]
+[3, 50]
+[2, 354]
+[2, 355]
+[2, 356]
+[2, 357]
+[2, 358]
+[2, 359]
+[2, 360]
+[4, 19]
+[2, 362]
+[17, 2]
+[2, 363]
+[2, 364]
+[3, 51]
+[2, 365]
+[2, 366]
+[2, 367]
+[2, 368]
+[2, 369]
+[2, 370]
+[2, 371]
+[2, 372]
+[2, 373]
+[2, 374]
+[3, 52]
+[2, 375]
+[2, 376]
+[2, 377]
+[2, 378]
+[2, 379]
+[2, 380]
+[2, 381]
+[2, 382]
+[2, 383]
+[2, 384]
+[2, 385]
+[3, 53]
+[2, 386]
+[2, 387]
+[2, 388]
+[2, 389]
+[2, 390]
+[2, 391]
+[2, 392]
+[2, 393]
+[2, 394]
+[2, 395]
+[2, 396]
+[3, 54]
+[2, 397]
+[2, 398]
+[2, 399]
+[4, 20]
+[2, 401]
+[5, 11]
+[2, 402]
+[2, 403]
+[2, 404]
+[2, 405]
+[2, 406]
+[2, 407]
+[3, 55]
+[2, 408]
+[2, 409]
+[2, 410]
+[2, 411]
+[2, 412]
+[2, 413]
+[2, 414]
+[2, 415]
+[2, 416]
+[2, 417]
+[2, 418]
+[2, 419]
+[3, 56]
+[2, 420]
+[11, 3]
+[2, 421]
+[2, 422]
+[2, 423]
+[2, 424]
+[2, 425]
+[2, 426]
+[2, 427]
+[2, 428]
+[2, 429]
+[2, 430]
+[3, 57]
+[2, 431]
+[2, 432]
+[2, 433]
+[2, 434]
+[2, 435]
+[2, 436]
+[2, 437]
+[2, 438]
+[2, 439]
+[2, 440]
+[4, 21]
+[3, 58]
+[2, 442]
+[2, 443]
+[2, 444]
+[2, 445]
+[2, 446]
+[2, 447]
+[2, 448]
+[2, 449]
+[2, 450]
+[2, 451]
+[2, 452]
+[2, 453]
+[3, 59]
+[2, 454]
+[2, 455]
+[2, 456]
+[2, 457]
+[2, 458]
+[2, 459]
+[2, 460]
+[2, 461]
+[2, 462]
+[2, 463]
+[2, 464]
+[3, 60]
+[2, 465]
+[2, 466]
+[2, 467]
+[2, 468]
+[2, 469]
+[2, 470]
+[2, 471]
+[2, 472]
+[2, 473]
+[2, 474]
+[2, 475]
+[2, 476]
+[3, 61]
+[2, 477]
+[2, 478]
+[2, 479]
+[2, 480]
+[2, 481]
+[2, 482]
+[2, 483]
+[4, 22]
+[2, 485]
+[2, 486]
+[2, 487]
+[2, 488]
+[3, 62]
+[2, 489]
+[2, 490]
+[2, 491]
+[2, 492]
+[2, 493]
+[2, 494]
+[2, 495]
+[2, 496]
+[2, 497]
+[2, 498]
+[5, 12]
+[2, 499]
+[2, 500]
+[3, 63]
+[2, 501]
+[2, 502]
+[2, 503]
+[2, 504]
+[2, 505]
+[2, 506]
+[2, 507]
+[2, 508]
+[2, 509]
+[2, 510]
+[2, 511]
+[18, 2]
+[2, 513]
+[2, 514]
+[2, 515]
+[2, 516]
+[2, 517]
+[2, 518]
+[2, 519]
+[2, 520]
+[2, 521]
+[2, 522]
+[2, 523]
+[2, 524]
+[3, 65]
+[2, 525]
+[2, 526]
+[2, 527]
+[2, 528]
+[4, 23]
+[7, 6]
+[2, 530]
+[2, 531]
+[2, 532]
+[2, 533]
+[2, 534]
+[2, 535]
+[2, 536]
+[3, 66]
+[2, 537]
+[2, 538]
+[2, 539]
+[2, 540]
+[2, 541]
+[2, 542]
+[2, 543]
+[2, 544]
+[2, 545]
+[2, 546]
+[2, 547]
+[2, 548]
+[3, 67]
+[2, 549]
+[2, 550]
+[2, 551]
+[2, 552]
+[2, 553]
+[2, 554]
+[2, 555]
+[2, 556]
+[2, 557]
+[2, 558]
+[2, 559]
+[2, 560]
+[3, 68]
+[2, 561]
+[2, 562]
+[2, 563]
+[2, 564]
+[2, 565]
+[2, 566]
+[2, 567]
+[2, 568]
+[2, 569]
+[2, 570]
+[2, 571]
+[2, 572]
+[2, 573]
+[3, 69]
+[2, 574]
+[2, 575]
+[4, 24]
+[2, 577]
+[2, 578]
+[2, 579]
+[2, 580]
+[2, 581]
+[2, 582]
+[2, 583]
+[2, 584]
+[2, 585]
+[3, 70]
+[2, 586]
+[2, 587]
+[2, 588]
+[2, 589]
+[2, 590]
+[2, 591]
+[2, 592]
+[2, 593]
+[2, 594]
+[2, 595]
+[2, 596]
+[2, 597]
+[2, 598]
+[3, 71]
+[2, 599]
+[2, 600]
+[2, 601]
+[2, 602]
+[2, 603]
+[2, 604]
+[2, 605]
+[2, 606]
+[2, 607]
+[2, 608]
+[2, 609]
+[5, 13]
+[2, 610]
+[3, 72]
+[2, 611]
+[2, 612]
+[2, 613]
+[2, 614]
+[2, 615]
+[2, 616]
+[2, 617]
+[2, 618]
+[2, 619]
+[2, 620]
+[2, 621]
+[2, 622]
+[2, 623]
+[3, 73]
+[2, 624]
+[8, 5]
+[2, 626]
+[2, 627]
+[2, 628]
+[2, 629]
+[2, 630]
+[2, 631]
+[2, 632]
+[2, 633]
+[2, 634]
+[2, 635]
+[2, 636]
+[3, 74]
+[2, 637]
+[2, 638]
+[2, 639]
+[2, 640]
+[2, 641]
+[2, 642]
+[2, 643]
+[2, 644]
+[2, 645]
+[2, 646]
+[2, 647]
+[2, 648]
+[2, 649]
+[3, 75]
+[2, 650]
+[2, 651]
+[2, 652]
+[2, 653]
+[2, 654]
+[2, 655]
+[2, 656]
+[2, 657]
+[2, 658]
+[2, 659]
+[2, 660]
+[2, 661]
+[2, 662]
+[3, 76]
+[2, 663]
+[2, 664]
+[2, 665]
+[2, 666]
+[2, 667]
+[2, 668]
+[2, 669]
+[2, 670]
+[2, 671]
+[2, 672]
+[2, 673]
+[2, 674]
+[2, 675]
+[3, 77]
+[4, 26]
+[2, 677]
+[2, 678]
+[2, 679]
+[2, 680]
+[2, 681]
+[2, 682]
+[2, 683]
+[2, 684]
+[2, 685]
+[2, 686]
+[2, 687]
+[2, 688]
+[3, 78]
+[2, 689]
+[2, 690]
+[2, 691]
+[2, 692]
+[2, 693]
+[2, 694]
+[2, 695]
+[2, 696]
+[2, 697]
+[2, 698]
+[2, 699]
+[2, 700]
+[2, 701]
+[2, 702]
+[3, 79]
+[2, 703]
+[2, 704]
+[2, 705]
+[2, 706]
+[2, 707]
+[2, 708]
+[2, 709]
+[2, 710]
+[2, 711]
+[2, 712]
+[2, 713]
+[2, 714]
+[2, 715]
+[3, 80]
+[2, 716]
+[2, 717]
+[2, 718]
+[2, 719]
+[2, 720]
+[2, 721]
+[2, 722]
+[2, 723]
+[2, 724]
+[19, 2]
+[2, 725]
+[2, 726]
+[2, 727]
+[2, 728]
+[12, 3]
+[2, 730]
+[2, 731]
+[2, 732]
+[2, 733]
+[5, 14]
+[2, 734]
+[2, 735]
+[2, 736]
+[2, 737]
+[2, 738]
+[2, 739]
+[2, 740]
+[2, 741]
+[2, 742]
+[3, 82]
+[2, 743]
+[2, 744]
+[2, 745]
+[2, 746]
+[2, 747]
+[2, 748]
+[2, 749]
+[2, 750]
+[2, 751]
+[2, 752]
+[2, 753]
+[2, 754]
+[2, 755]
+[2, 756]
+[3, 83]
+[2, 757]
+[2, 758]
+[2, 759]
+[2, 760]
+[2, 761]
+[2, 762]
+[2, 763]
+[2, 764]
+[2, 765]
+[2, 766]
+[2, 767]
+[2, 768]
+[2, 769]
+[3, 84]
+[2, 770]
+[2, 771]
+[2, 772]
+[2, 773]
+[2, 774]
+[2, 775]
+[2, 776]
+[2, 777]
+[2, 778]
+[2, 779]
+[2, 780]
+[2, 781]
+[2, 782]
+[2, 783]
+[3, 85]
+[4, 28]
+[2, 785]
+[2, 786]
+[2, 787]
+[2, 788]
+[2, 789]
+[2, 790]
+[2, 791]
+[2, 792]
+[2, 793]
+[2, 794]
+[2, 795]
+[2, 796]
+[2, 797]
+[3, 86]
+[2, 798]
+[2, 799]
+[2, 800]
+[2, 801]
+[2, 802]
+[2, 803]
+[2, 804]
+[2, 805]
+[2, 806]
+[2, 807]
+[2, 808]
+[2, 809]
+[2, 810]
+[2, 811]
+[3, 87]
+[2, 812]
+[2, 813]
+[2, 814]
+[2, 815]
+[2, 816]
+[2, 817]
+[2, 818]
+[2, 819]
+[2, 820]
+[2, 821]
+[2, 822]
+[2, 823]
+[2, 824]
+[2, 825]
+[3, 88]
+[2, 826]
+[2, 827]
+[2, 828]
+[2, 829]
+[2, 830]
+[2, 831]
+[2, 832]
+[2, 833]
+[2, 834]
+[2, 835]
+[2, 836]
+[2, 837]
+[2, 838]
+[2, 839]
+[3, 89]
+[2, 840]
+[4, 29]
+[2, 842]
+[2, 843]
+[2, 844]
+[2, 845]
+[2, 846]
+[2, 847]
+[2, 848]
+[2, 849]
+[2, 850]
+[2, 851]
+[2, 852]
+[2, 853]
+[3, 90]
+[2, 854]
+[2, 855]
+[2, 856]
+[2, 857]
+[2, 858]
+[2, 859]
+[2, 860]
+[2, 861]
+[2, 862]
+[2, 863]
+[2, 864]
+[2, 865]
+[2, 866]
+[2, 867]
+[2, 868]
+[3, 91]
+[2, 869]
+[2, 870]
+[2, 871]
+[5, 15]
+[2, 872]
+[2, 873]
+[2, 874]
+[2, 875]
+[2, 876]
+[2, 877]
+[2, 878]
+[2, 879]
+[2, 880]
+[2, 881]
+[2, 882]
+[3, 92]
+[2, 883]
+[2, 884]
+[2, 885]
+[2, 886]
+[2, 887]
+[2, 888]
+[2, 889]
+[2, 890]
+[2, 891]
+[2, 892]
+[2, 893]
+[2, 894]
+[2, 895]
+[2, 896]
+[3, 93]
+[2, 897]
+[2, 898]
+[2, 899]
+[4, 30]
+[2, 901]
+[2, 902]
+[2, 903]
+[2, 904]
+[2, 905]
+[2, 906]
+[2, 907]
+[7, 7]
+[2, 908]
+[2, 909]
+[2, 910]
+[2, 911]
+[3, 94]
+[2, 912]
+[2, 913]
+[2, 914]
+[2, 915]
+[2, 916]
+[2, 917]
+[2, 918]
+[2, 919]
+[2, 920]
+[2, 921]
+[2, 922]
+[2, 923]
+[2, 924]
+[2, 925]
+[3, 95]
+[2, 926]
+[2, 927]
+[2, 928]
+[2, 929]
+[2, 930]
+[2, 931]
+[2, 932]
+[2, 933]
+[2, 934]
+[2, 935]
+[2, 936]
+[2, 937]
+[2, 938]
+[2, 939]
+[2, 940]
+[3, 96]
+[2, 941]
+[2, 942]
+[2, 943]
+[2, 944]
+[2, 945]
+[2, 946]
+[2, 947]
+[2, 948]
+[2, 949]
+[2, 950]
+[2, 951]
+[2, 952]
+[2, 953]
+[2, 954]
+[2, 955]
+[3, 97]
+[2, 956]
+[2, 957]
+[2, 958]
+[2, 959]
+[2, 960]
+[4, 31]
+[2, 962]
+[2, 963]
+[2, 964]
+[2, 965]
+[2, 966]
+[2, 967]
+[2, 968]
+[2, 969]
+[2, 970]
+[3, 98]
+[2, 971]
+[2, 972]
+[2, 973]
+[2, 974]
+[2, 975]
+[2, 976]
+[2, 977]
+[2, 978]
+[2, 979]
+[2, 980]
+[2, 981]
+[2, 982]
+[2, 983]
+[2, 984]
+[2, 985]
+[3, 99]
+[2, 986]
+[2, 987]
+[2, 988]
+[2, 989]
+[2, 990]
+[2, 991]
+[2, 992]
+[2, 993]
+[2, 994]
+[2, 995]
+[2, 996]
+[2, 997]
+[2, 998]
+[2, 999]
+[6, 10]
+[3, -4]
+[3, -2]
+[3, -21218]
+[3, -1/4]
+3
+1
+[1431, 5737585]
+[1278, 6780590]
+[983, 36262840]
+[508, 2044406843184]
+[274, 3873816255479006870044]
+[228, 34028236692093846346337460743176821665314]
+121
+1
+[0, 0, 1, 0]
+[0, 0, 0, 0]
+[0, 0, Mod(735321672858813933, 1000039000207000297), 0]
+[0, 0, 0, 0]
+[Mod(766696600158900228, 1000039000207000297), Mod(9366110197497040718447998
+65730, 1000113004462069006305361406593), Mod(6, 1000039000207000297), Mod(30
+66786400635600912, 8000312001656002376)]
+[Mod(761273567620851421, 1000039000207000297), Mod(6350198471236493446056154
+0863, 1000113004462069006305361406593), Mod(542283254184885017, 100003900020
+7000297), Mod(4933525601020401464, 8000312001656002376)]
+Mod(583, 875)
+1
+Mod(0, 2)
+0
+0
+0
+1
+0
+1
+1
+y
+0
+1
+Mod(1, 2)*x + Mod(1, 2)
+t*x^2 + (t + 1)*x + 1
+Mod(1, 5)*x^2 + Mod(3, 5)*x + Mod(2, 5)
+2/x
+1
+1.2247448713915890490986420373529456960
+1
+0
+1
+1
+t^2 + t + 1
+1
+0
+1
+1
+0
+-2
+1
+-2/3
+1
+0
+0
+0
+2
+x + 2
+0
+11
+1
+1
+1/(2*x)
+1 + 1/3*x + O(x^2)
+-2/3
+121
+121
+187
+1
+Total time spent: 5192
diff --git a/src/test/32/isprime b/src/test/32/isprime
new file mode 100644
index 0000000..f81e289
--- /dev/null
+++ b/src/test/32/isprime
@@ -0,0 +1,23 @@
+1
+1
+1
+
+[      2 5 1]
+
+[      3 2 1]
+
+[1000003 2 1]
+
+
+[      2 3 1]
+
+[     29 2 1]
+
+[1000003 2 1]
+
+[1, 1, 0]
+[1, Mat([2, 2, 1]), 0]
+[1, 1, 0]
+[0, 1, 0, 1]
+1
+Total time spent: 17128
diff --git a/src/test/32/iterator b/src/test/32/iterator
new file mode 100644
index 0000000..70892cc
--- /dev/null
+++ b/src/test/32/iterator
@@ -0,0 +1,22 @@
+4
+6
+8
+9
+10
+6
+8
+9
+10
+6
+8
+9
+10
+12
+6
+8
+9
+6
+  ***   at top-level: forcomposite(a=6,12,print(a);a=1)
+  ***                                     ^-------------
+  ***   index read-only: was changed to 1.
+Total time spent: 0
diff --git a/src/test/32/kernel b/src/test/32/kernel
new file mode 100644
index 0000000..9a23dd7
--- /dev/null
+++ b/src/test/32/kernel
@@ -0,0 +1,23 @@
+INT: 187654321
+conv:40000003  0b2f60b1  
++:40000003  0a72ff63  
+-:40000003  0bebc1ff  
+*:c0000004  00083b0b  5e0e86ee  
+/:c0000003  0000000f  
+rem:40000003  0025ad1f  
+pow:
+40000004  007d1b13  db833a61  
+40000006  00003d23  74418fff  02bee98c  c29618c1  
+40000009  0e99ea50  3d586253  467c3cea  a7f1a742  85bba548  639e3a9d  8890c181  
+40000010  00d5322a  b115c7b2  9cf0c571  1679c253  8cea7ca0  1cbe7596  78ae7d09  c969dc26  cd42e2ad  51204d31  635bced8  66d36dbb  0351ebad  5363c301  
+4000001e  0000b18c  84df6289  66df9491  766a39a4  0685a8a3  cfc68c74  4d9dcb91  02634462  b398839e  1a6892ef  5efe0281  bcae4afb  eb64fcab  f930fa7a  00f01335  2a5dc7c3  4295b5c3  b031c96e  a5c19079  8e647dac  09e2091e  67abbc58  c118d1b7  76f5e31c  e97a9180  140e859c  64a53cad  0d508601  
+invmod:4000001e  000089fe  3c4ba859  b9472dac  d0585e64  b5311881  edf8024b  aaf6ce9e  98d94a2d  b253c748  bc185be1  6de1e5f8  831bbecd  a3d629dc  0fc9440e  b0ab325d  53dd21c5  9fb7fc20  cc22d98c  e8fe6eca  928c7f4f  a3861061  a7ab0bdc  71deda26  17a28387  77487563  9f0f88ac  424ca4d5  3dcae2a5  
+
+REAL: 187654321.000000
+conv1:6000001b  b2f60b10  00000000  
+conv2:6000001b  b2f60b10  00000000  
++:6000001b  a72ff630  00000000  
+-:6000001b  bebc1ff0  00000000  
+*:e0000033  83b0b5e0  e86ee000  
+/:e0000003  f3333482  0be1df0a  
+gcc bug?:60000000  c0000000  00000000  
diff --git a/src/test/32/krasner b/src/test/32/krasner
new file mode 100644
index 0000000..db79096
--- /dev/null
+++ b/src/test/32/krasner
@@ -0,0 +1,119 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+[[0, 1, 3, 0], [2, 3, 1, 2]]
+[[0, 1, 5, 0], [4, 5, 1, 4]]
+[[0, 1, 7, 0], [6, 7, 1, 6]]
+[[0, 1, 9, 0], [6, 3, 3, 6], [8, 9, 1, 8]]
+[[0, 1, 11, 0], [10, 11, 1, 10]]
+[[0, 1, 13, 0], [12, 13, 1, 12]]
+[[0, 1, 15, 0], [10, 3, 5, 10], [12, 5, 3, 12], [14, 15, 1, 14]]
+[[0, 1, 17, 0], [16, 17, 1, 16]]
+[[0, 1, 19, 0], [18, 19, 1, 18]]
+[[0, 1, 21, 0], [14, 3, 7, 14], [18, 7, 3, 18], [18, 7, 3, 18], [18, 7, 3, 1
+8], [20, 21, 1, 20]]
+[[0, 1, 23, 0], [22, 23, 1, 22]]
+[[0, 1, 25, 0], [20, 5, 5, 20], [24, 25, 1, 24]]
+[[0, 1, 27, 0], [18, 3, 9, 18], [24, 9, 3, 24], [26, 27, 1, 26]]
+[[0, 1, 29, 0], [28, 29, 1, 28]]
+[[0, 1, 31, 0], [30, 31, 1, 30]]
+[[0, 1, 33, 0], [22, 3, 11, 22], [30, 11, 3, 30], [32, 33, 1, 32]]
+[[0, 1, 35, 0], [28, 5, 7, 28], [30, 7, 5, 30], [34, 35, 1, 34]]
+[[0, 1, 37, 0], [36, 37, 1, 36]]
+[[0, 1, 39, 0], [26, 3, 13, 26], [36, 13, 3, 36], [38, 39, 1, 38]]
+[[0, 1, 41, 0], [40, 41, 1, 40]]
+[[0, 1, 43, 0], [42, 43, 1, 42]]
+[[0, 1, 45, 0], [30, 3, 15, 30], [36, 5, 9, 36], [40, 9, 5, 40], [42, 15, 3,
+ 42], [44, 45, 1, 44]]
+[[0, 1, 47, 0], [46, 47, 1, 46]]
+[[0, 1, 49, 0], [42, 7, 7, 42], [48, 49, 1, 48]]
+[[0, 1, 2, 0], [1, 2, 1, 1], [1, 2, 1, 1]]
+[[0, 1, 4, 0], [2, 2, 2, 2], [2, 2, 2, 2], [3, 4, 1, 3], [3, 4, 1, 3]]
+[[0, 1, 5, 0], [4, 5, 1, 4]]
+[[0, 1, 7, 0], [6, 7, 1, 6]]
+[[0, 1, 8, 0], [4, 2, 4, 4], [4, 2, 4, 4], [6, 4, 2, 6], [6, 4, 2, 6], [6, 4
+, 2, 6], [7, 8, 1, 7], [7, 8, 1, 7]]
+[[0, 1, 10, 0], [5, 2, 5, 5], [5, 2, 5, 5], [8, 5, 2, 8], [9, 10, 1, 9], [9,
+ 10, 1, 9]]
+[[0, 1, 11, 0], [10, 11, 1, 10]]
+[[0, 1, 13, 0], [12, 13, 1, 12]]
+[[0, 1, 14, 0], [7, 2, 7, 7], [7, 2, 7, 7], [12, 7, 2, 12], [13, 14, 1, 13],
+ [13, 14, 1, 13]]
+[[0, 1, 16, 0], [8, 2, 8, 8], [8, 2, 8, 8], [12, 4, 4, 12], [12, 4, 4, 12], 
+[12, 4, 4, 12], [14, 8, 2, 14], [14, 8, 2, 14], [14, 8, 2, 14], [14, 8, 2, 1
+4], [14, 8, 2, 14], [15, 16, 1, 15], [15, 16, 1, 15]]
+[[0, 1, 17, 0], [16, 17, 1, 16]]
+[[0, 1, 19, 0], [18, 19, 1, 18]]
+[[0, 1, 20, 0], [10, 2, 10, 10], [10, 2, 10, 10], [15, 4, 5, 15], [15, 4, 5,
+ 15], [16, 5, 4, 16], [16, 5, 4, 16], [18, 10, 2, 18], [18, 10, 2, 18], [19,
+ 20, 1, 19], [19, 20, 1, 19]]
+[[0, 1, 22, 0], [11, 2, 11, 11], [11, 2, 11, 11], [20, 11, 2, 20], [21, 22, 
+1, 21], [21, 22, 1, 21]]
+[[0, 1, 23, 0], [22, 23, 1, 22]]
+[[0, 1, 25, 0], [20, 5, 5, 20], [24, 25, 1, 24]]
+[[0, 1, 26, 0], [13, 2, 13, 13], [13, 2, 13, 13], [24, 13, 2, 24], [25, 26, 
+1, 25], [25, 26, 1, 25]]
+[[0, 1, 28, 0], [14, 2, 14, 14], [14, 2, 14, 14], [21, 4, 7, 21], [21, 4, 7,
+ 21], [24, 7, 4, 24], [26, 14, 2, 26], [26, 14, 2, 26], [27, 28, 1, 27], [27
+, 28, 1, 27]]
+[[0, 1, 29, 0], [28, 29, 1, 28]]
+[[0, 1, 31, 0], [30, 31, 1, 30]]
+[[0, 1, 32, 0], [16, 2, 16, 16], [16, 2, 16, 16], [24, 4, 8, 24], [24, 4, 8,
+ 24], [24, 4, 8, 24], [28, 8, 4, 28], [28, 8, 4, 28], [28, 8, 4, 28], [28, 8
+, 4, 28], [28, 8, 4, 28], [30, 16, 2, 30], [30, 16, 2, 30], [30, 16, 2, 30],
+ [30, 16, 2, 30], [30, 16, 2, 30], [31, 32, 1, 31], [31, 32, 1, 31]]
+[[0, 1, 34, 0], [17, 2, 17, 17], [17, 2, 17, 17], [32, 17, 2, 32], [33, 34, 
+1, 33], [33, 34, 1, 33]]
+[[0, 1, 35, 0], [28, 5, 7, 28], [30, 7, 5, 30], [34, 35, 1, 34]]
+[[0, 1, 37, 0], [36, 37, 1, 36]]
+[[0, 1, 38, 0], [19, 2, 19, 19], [19, 2, 19, 19], [36, 19, 2, 36], [37, 38, 
+1, 37], [37, 38, 1, 37]]
+[[0, 1, 40, 0], [20, 2, 20, 20], [20, 2, 20, 20], [30, 4, 10, 30], [30, 4, 1
+0, 30], [30, 4, 10, 30], [32, 5, 8, 32], [32, 5, 8, 32], [35, 8, 5, 35], [35
+, 8, 5, 35], [36, 10, 4, 36], [36, 10, 4, 36], [36, 10, 4, 36], [36, 10, 4, 
+36], [38, 20, 2, 38], [38, 20, 2, 38], [38, 20, 2, 38], [39, 40, 1, 39], [39
+, 40, 1, 39]]
+[[0, 1, 41, 0], [40, 41, 1, 40]]
+[[0, 1, 43, 0], [42, 43, 1, 42]]
+[[0, 1, 44, 0], [22, 2, 22, 22], [22, 2, 22, 22], [33, 4, 11, 33], [33, 4, 1
+1, 33], [40, 11, 4, 40], [42, 22, 2, 42], [42, 22, 2, 42], [43, 44, 1, 43], 
+[43, 44, 1, 43]]
+[[0, 1, 46, 0], [23, 2, 23, 23], [23, 2, 23, 23], [44, 23, 2, 44], [45, 46, 
+1, 45], [45, 46, 1, 45]]
+[[0, 1, 47, 0], [46, 47, 1, 46]]
+[[0, 1, 49, 0], [42, 7, 7, 42], [48, 49, 1, 48]]
+[[0, 1, 50, 0], [25, 2, 25, 25], [25, 2, 25, 25], [40, 5, 10, 40], [45, 10, 
+5, 45], [45, 10, 5, 45], [48, 25, 2, 48], [49, 50, 1, 49], [49, 50, 1, 49]]
+[[0, 1, 105, 0], [70, 3, 35, 70], [84, 5, 21, 84], [90, 7, 15, 90], [90, 7, 
+15, 90], [90, 7, 15, 90], [98, 15, 7, 98], [100, 21, 5, 100], [102, 35, 3, 1
+02], [102, 35, 3, 102], [102, 35, 3, 102], [104, 105, 1, 104]]
+[[0, 1, 21, 0], [14, 3, 7, 14], [18, 7, 3, 18], [20, 21, 1, 20]]
+[[0, 1, 55, 0], [44, 5, 11, 44], [50, 11, 5, 50], [50, 11, 5, 50], [50, 11, 
+5, 50], [50, 11, 5, 50], [50, 11, 5, 50], [50, 11, 5, 50], [50, 11, 5, 50], 
+[50, 11, 5, 50], [50, 11, 5, 50], [50, 11, 5, 50], [50, 11, 5, 50], [54, 55,
+ 1, 54], [54, 55, 1, 54], [54, 55, 1, 54], [54, 55, 1, 54], [54, 55, 1, 54],
+ [54, 55, 1, 54], [54, 55, 1, 54], [54, 55, 1, 54], [54, 55, 1, 54], [54, 55
+, 1, 54], [54, 55, 1, 54]]
+[[0, 1, 12, 0], [6, 2, 6, 6], [6, 2, 6, 6], [8, 3, 4, 8], [8, 3, 4, 8], [9, 
+4, 3, 9], [9, 4, 3, 9], [10, 6, 2, 10], [10, 6, 2, 10], [10, 6, 2, 10], [10,
+ 6, 2, 10], [11, 12, 1, 11], [11, 12, 1, 11]]
+[[12, 2, 4, 12], [12, 2, 4, 12], [12, 2, 4, 12], [12, 2, 4, 12], [12, 2, 4, 
+12], [12, 2, 4, 12], [12, 2, 4, 12], [12, 2, 4, 12], [12, 2, 4, 12], [12, 2,
+ 4, 12], [12, 2, 4, 12], [12, 2, 4, 12], [12, 4, 2, 12], [12, 4, 2, 12], [12
+, 4, 2, 12], [12, 4, 2, 12], [12, 4, 2, 12], [12, 4, 2, 12], [12, 4, 2, 12],
+ [12, 4, 2, 12], [12, 4, 2, 12], [12, 4, 2, 12], [12, 4, 2, 12], [12, 4, 2, 
+12], [12, 8, 1, 12], [12, 8, 1, 12], [12, 8, 1, 12], [12, 8, 1, 12], [12, 8,
+ 1, 12], [12, 8, 1, 12]]
+15
+[[0, 1, 2, 0], [1, 2, 1, 1], [1, 2, 1, 1]]
+[[0, 1, 4, 0], [4, 2, 2, 4], [4, 2, 2, 4], [4, 2, 2, 4], [4, 2, 2, 4], [4, 4
+, 1, 4], [6, 2, 2, 6], [6, 2, 2, 6], [6, 2, 2, 6], [6, 2, 2, 6], [6, 2, 2, 6
+], [6, 2, 2, 6], [6, 4, 1, 6], [6, 4, 1, 6], [6, 4, 1, 6], [8, 4, 1, 8], [8,
+ 4, 1, 8], [8, 4, 1, 8], [8, 4, 1, 8], [8, 4, 1, 8], [8, 4, 1, 8], [8, 4, 1,
+ 8], [8, 4, 1, 8], [9, 4, 1, 9], [9, 4, 1, 9], [9, 4, 1, 9], [9, 4, 1, 9], [
+9, 4, 1, 9], [9, 4, 1, 9], [9, 4, 1, 9], [9, 4, 1, 9], [10, 4, 1, 10], [10, 
+4, 1, 10], [10, 4, 1, 10], [10, 4, 1, 10], [10, 4, 1, 10], [10, 4, 1, 10], [
+10, 4, 1, 10], [10, 4, 1, 10], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11
+], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1
+, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 
+4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11], [
+11, 4, 1, 11], [11, 4, 1, 11], [11, 4, 1, 11]]
+Total time spent: 2896
diff --git a/src/test/32/lambert b/src/test/32/lambert
new file mode 100644
index 0000000..d01050f
--- /dev/null
+++ b/src/test/32/lambert
@@ -0,0 +1,15 @@
+  ***   at top-level: do(-1)
+  ***                 ^------
+  ***   in function do: my(x=lambertw(y));exp(x)*
+  ***                        ^--------------------
+  *** lambertw: domain error in Lw: y < 0
+  ***   at top-level: do(I)
+  ***                 ^-----
+  ***   in function do: my(x=lambertw(y));exp(x)*
+  ***                        ^--------------------
+  *** lambertw: sorry, lambert(t_COMPLEX) is not yet implemented.
+1.0000000000000000000000000000000000000
+0.99999999999999999999999999999999999999999999999999999999999999999999999999
+9999999999999999999999999999999999999999999999999999999999999999999999999999
+9999999999999999999999999999999999999999999999999999999999999
+Total time spent: 0
diff --git a/src/test/32/lex b/src/test/32/lex
new file mode 100644
index 0000000..5a2307c
--- /dev/null
+++ b/src/test/32/lex
@@ -0,0 +1,29 @@
+[1, 1, 0]
+[1, 2, -1]
+[1, 3, -1]
+[1, 4, -1]
+[1, 5, -1]
+[1, 6, -1]
+[1, 7, -1]
+[2, 2, 0]
+[2, 3, 1]
+[2, 4, 1]
+[2, 5, 1]
+[2, 6, 1]
+[2, 7, 1]
+[3, 3, 0]
+[3, 4, -1]
+[3, 5, 1]
+[3, 6, -1]
+[3, 7, -1]
+[4, 4, 0]
+[4, 5, 1]
+[4, 6, 1]
+[4, 7, -1]
+[5, 5, 0]
+[5, 6, -1]
+[5, 7, -1]
+[6, 6, 0]
+[6, 7, -1]
+[7, 7, 0]
+Total time spent: 0
diff --git a/src/test/32/lift b/src/test/32/lift
new file mode 100644
index 0000000..e69dce8
--- /dev/null
+++ b/src/test/32/lift
@@ -0,0 +1,65 @@
+lift
+1
+2
+x
+3
+8/3
+x
+x + 2
+(a)->lift(a,'x)
+1
+Mod(2, 3)
+x
+3 + O(3^3)
+2*3^-1 + 2 + O(3)
+x
+Mod(1, 3)*x + Mod(2, 3)
+(a)->lift(a,'y)
+1
+Mod(2, 3)
+x
+3 + O(3^3)
+2*3^-1 + 2 + O(3)
+Mod(x, x^2)
+Mod(1, 3)*x + Mod(2, 3)
+(a)->lift(a,'z)
+1
+Mod(2, 3)
+x
+3 + O(3^3)
+2*3^-1 + 2 + O(3)
+Mod(x, x^2)
+Mod(1, 3)*x + Mod(2, 3)
+centerlift
+1
+-1
+x
+3
+-1/3
+x
+x - 1
+liftall
+1
+2
+x
+3
+8/3
+x
+x + 2
+liftint
+1
+2
+x
+3
+8/3
+Mod(x, x^2)
+x + 2
+liftpol
+1
+Mod(2, 3)
+x
+3 + O(3^3)
+2*3^-1 + 2 + O(3)
+x
+Mod(1, 3)*x + Mod(2, 3)
+Total time spent: 4
diff --git a/src/test/32/lindep b/src/test/32/lindep
new file mode 100644
index 0000000..bb7d035
--- /dev/null
+++ b/src/test/32/lindep
@@ -0,0 +1,19 @@
+[-1, -1, 1]~
+[1, -2]~
+[1, -2, 1]~
+[y, y, -1, -y^2]~
+x^2 + (-y^3 - y^2 - 5*y - 1)
+0
+(-y + 1)*x - 1
+-x + (5*y + 1)
+[]~
+[1]~
+[]~
+[]~
+1
+  ***   at top-level: algdep(1,-1)
+  ***                 ^------------
+  *** algdep: domain error in algdep: degree < 0
+x^2 - 2
+[0, 0, 0]~
+Total time spent: 0
diff --git a/src/test/32/linear b/src/test/32/linear
new file mode 100644
index 0000000..b757898
--- /dev/null
+++ b/src/test/32/linear
@@ -0,0 +1,679 @@
+   echo = 1 (on)
+? algdep(2*cos(2*Pi/13),6)
+x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1
+? algdep(2*cos(2*Pi/13),6,15)
+x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1
+? charpoly([1,2;3,4],z)
+z^2 - 5*z - 2
+? charpoly(Mod(x^2+x+1,x^3+5*x+1),z)
+z^3 + 7*z^2 + 16*z - 19
+? charpoly([1,2;3,4],z,1)
+z^2 - 5*z - 2
+? charpoly(Mod(1,8191)*[1,2;3,4],z,2)
+z^2 + Mod(8186, 8191)*z + Mod(8189, 8191)
+? lindep(Mod(1,7)*[2,-1;1,3])
+[-3, 1]~
+? lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)])
+[3, 3, -9, 2, -6]~
+? lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)],14)
+[-3, -3, 9, -2, 6]~
+? matadjoint([1,2;3,4])
+
+[ 4 -2]
+
+[-3  1]
+
+? matcompanion(x^5-12*x^3+0.0005)
+
+[0 0 0 0 -0.00050000000000000000000000000000000000000]
+
+[1 0 0 0                                            0]
+
+[0 1 0 0                                            0]
+
+[0 0 1 0                                           12]
+
+[0 0 0 1                                            0]
+
+? matdet([1,2,3;1,5,6;9,8,7])
+-30
+? matdet([1,2,3;1,5,6;9,8,7],1)
+-30
+? matdetint([1,2,3;4,5,6])
+3
+? matdiagonal([2,4,6])
+
+[2 0 0]
+
+[0 4 0]
+
+[0 0 6]
+
+? mateigen([1,2,3;4,5,6;7,8,9])
+
+[1 -1.2833494518006402717978106547571267252 0.283349451800640271797810654757
+12672521]
+
+[-2 -0.14167472590032013589890532737856336261 0.6416747259003201358989053273
+7856336260]
+
+[1 1 1]
+
+? mathess(mathilbert(7))
+
+[1 90281/58800 -1919947/4344340 4858466341/1095033030 -77651417539/819678732
+6 3386888964/106615355 1/2]
+
+[1/3 43/48 38789/5585580 268214641/109503303 -581330123627/126464718744 4365
+450643/274153770 1/4]
+
+[0 217/2880 442223/7447440 53953931/292008808 -32242849453/168619624992 1475
+457901/1827691800 1/80]
+
+[0 0 1604444/264539275 24208141/149362505292 847880210129/47916076768560 -45
+44407141/103873817300 -29/40920]
+
+[0 0 0 9773092581/35395807550620 -24363634138919/107305824577186620 72118203
+606917/60481351061158500 55899/3088554700]
+
+[0 0 0 0 67201501179065/8543442888354179988 -9970556426629/74082861999267660
+0 -3229/13661312210]
+
+[0 0 0 0 0 -258198800769/9279048099409000 -13183/38381527800]
+
+? mathilbert(5)
+
+[  1 1/2 1/3 1/4 1/5]
+
+[1/2 1/3 1/4 1/5 1/6]
+
+[1/3 1/4 1/5 1/6 1/7]
+
+[1/4 1/5 1/6 1/7 1/8]
+
+[1/5 1/6 1/7 1/8 1/9]
+
+? amat=1/mathilbert(7)
+
+[    49    -1176      8820    -29400      48510     -38808     12012]
+
+[ -1176    37632   -317520   1128960   -1940400    1596672   -504504]
+
+[  8820  -317520   2857680 -10584000   18711000  -15717240   5045040]
+
+[-29400  1128960 -10584000  40320000  -72765000   62092800 -20180160]
+
+[ 48510 -1940400  18711000 -72765000  133402500 -115259760  37837800]
+
+[-38808  1596672 -15717240  62092800 -115259760  100590336 -33297264]
+
+[ 12012  -504504   5045040 -20180160   37837800  -33297264  11099088]
+
+? mathnf(amat)
+
+[420   0    0    0   210  168   175]
+
+[  0 840    0    0     0    0   504]
+
+[  0   0 2520    0     0    0  1260]
+
+[  0   0    0 2520     0    0   840]
+
+[  0   0    0    0 13860    0  6930]
+
+[  0   0    0    0     0 5544     0]
+
+[  0   0    0    0     0    0 12012]
+
+? mathnf(amat,1)
+[[420, 0, 0, 0, 210, 168, 175; 0, 840, 0, 0, 0, 0, 504; 0, 0, 2520, 0, 0, 0,
+ 1260; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 13860, 0, 6930; 0, 0, 0, 0, 0, 
+5544, 0; 0, 0, 0, 0, 0, 0, 12012], [420, 420, 840, 630, 2982, 1092, 4159; 21
+0, 280, 630, 504, 2415, 876, 3395; 140, 210, 504, 420, 2050, 749, 2901; 105,
+ 168, 420, 360, 1785, 658, 2542; 84, 140, 360, 315, 1582, 588, 2266; 70, 120
+, 315, 280, 1421, 532, 2046; 60, 105, 280, 252, 1290, 486, 1866]]
+? mathnf(amat,4)
+[[420, 0, 0, 0, 210, 168, 175; 0, 840, 0, 0, 0, 0, 504; 0, 0, 2520, 0, 0, 0,
+ 1260; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 13860, 0, 6930; 0, 0, 0, 0, 0, 
+5544, 0; 0, 0, 0, 0, 0, 0, 12012], [420, 420, 840, 630, 2982, 1092, 4159; 21
+0, 280, 630, 504, 2415, 876, 3395; 140, 210, 504, 420, 2050, 749, 2901; 105,
+ 168, 420, 360, 1785, 658, 2542; 84, 140, 360, 315, 1582, 588, 2266; 70, 120
+, 315, 280, 1421, 532, 2046; 60, 105, 280, 252, 1290, 486, 1866]]
+? mathnf(amat,5)
+[[360360, 0, 0, 0, 0, 144144, 300300; 0, 27720, 0, 0, 0, 0, 22176; 0, 0, 277
+20, 0, 0, 0, 6930; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 2520, 0, 1260; 0, 0
+, 0, 0, 0, 168, 0; 0, 0, 0, 0, 0, 0, 7], [51480, 4620, 5544, 630, 840, 20676
+, 48619; 45045, 3960, 4620, 504, 630, 18074, 42347; 40040, 3465, 3960, 420, 
+504, 16058, 37523; 36036, 3080, 3465, 360, 420, 14448, 33692; 32760, 2772, 3
+080, 315, 360, 13132, 30574; 30030, 2520, 2772, 280, 315, 12036, 27986; 2772
+0, 2310, 2520, 252, 280, 11109, 25803], Vecsmall([7, 6, 5, 4, 3, 2, 1])]
+? mathnfmod(amat,matdetint(amat))
+
+[420   0    0    0   210  168   175]
+
+[  0 840    0    0     0    0   504]
+
+[  0   0 2520    0     0    0  1260]
+
+[  0   0    0 2520     0    0   840]
+
+[  0   0    0    0 13860    0  6930]
+
+[  0   0    0    0     0 5544     0]
+
+[  0   0    0    0     0    0 12012]
+
+? mathnfmodid(amat,123456789*10^100)
+
+[60   0   0   0  30 24  35]
+
+[ 0 120   0   0   0  0  24]
+
+[ 0   0 360   0   0  0 180]
+
+[ 0   0   0 360   0  0 240]
+
+[ 0   0   0   0 180  0  90]
+
+[ 0   0   0   0   0 72   0]
+
+[ 0   0   0   0   0  0  12]
+
+? matid(5)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? matimage([1,3,5;2,4,6;3,5,7])
+
+[1 3]
+
+[2 4]
+
+[3 5]
+
+? matimage([1,3,5;2,4,6;3,5,7],1)
+
+[3 5]
+
+[4 6]
+
+[5 7]
+
+? matimage(Pi*[1,3,5;2,4,6;3,5,7])
+
+[3.1415926535897932384626433832795028842 9.424777960769379715387930149838508
+6526]
+
+[6.2831853071795864769252867665590057684 12.56637061435917295385057353311801
+1537]
+
+[9.4247779607693797153879301498385086526 15.70796326794896619231321691639751
+4421]
+
+? matimagecompl([1,3,5;2,4,6;3,5,7])
+Vecsmall([3])
+? matimagecompl(Pi*[1,3,5;2,4,6;3,5,7])
+Vecsmall([3])
+? matindexrank([1,1,1;1,1,1;1,1,2])
+[Vecsmall([1, 3]), Vecsmall([1, 3])]
+? matintersect([1,2;3,4;5,6],[2,3;7,8;8,9])
+
+[-1]
+
+[-1]
+
+[-1]
+
+? matinverseimage([1,1;2,3;5,7],[2,2,6]~)
+[4, -2]~
+? matisdiagonal([1,0,0;0,5,0;0,0,0])
+1
+? matker(matrix(4,4,x,y,x/y))
+
+[-1/2 -1/3 -1/4]
+
+[   1    0    0]
+
+[   0    1    0]
+
+[   0    0    1]
+
+? matker(matrix(4,4,x,y,sin(x+y)))
+
+[1.0000000000000000000000000000000000000 1.080604611736279434801873214885953
+2075]
+
+[-1.0806046117362794348018732148859532075 -0.1677063269057152260048635409984
+7562047]
+
+[1 0]
+
+[0 1]
+
+? matker(matrix(4,4,x,y,x+y),1)
+
+[ 1  2]
+
+[-2 -3]
+
+[ 1  0]
+
+[ 0  1]
+
+? matkerint(matrix(4,4,x,y,x*y))
+
+[-1 -1 -1]
+
+[-1  0  1]
+
+[ 1 -1  1]
+
+[ 0  1 -1]
+
+? matkerint(matrix(4,4,x,y,x*y),1)
+
+[-1 -1 -1]
+
+[-1  0  1]
+
+[ 1 -1  1]
+
+[ 0  1 -1]
+
+? matkerint(matrix(4,6,x,y,2520/(x+y)))
+
+[   3    1]
+
+[ -30  -15]
+
+[  70   70]
+
+[   0 -140]
+
+[-126  126]
+
+[  84  -42]
+
+? matmuldiagonal(amat,[1,2,3,4,5,6,7])
+
+[    49    -2352     26460    -117600     242550    -232848      84084]
+
+[ -1176    75264   -952560    4515840   -9702000    9580032   -3531528]
+
+[  8820  -635040   8573040  -42336000   93555000  -94303440   35315280]
+
+[-29400  2257920 -31752000  161280000 -363825000  372556800 -141261120]
+
+[ 48510 -3880800  56133000 -291060000  667012500 -691558560  264864600]
+
+[-38808  3193344 -47151720  248371200 -576298800  603542016 -233080848]
+
+[ 12012 -1009008  15135120  -80720640  189189000 -199783584   77693616]
+
+? matmultodiagonal(amat^-1,%)
+
+[1 0 0 0 0 0 0]
+
+[0 2 0 0 0 0 0]
+
+[0 0 3 0 0 0 0]
+
+[0 0 0 4 0 0 0]
+
+[0 0 0 0 5 0 0]
+
+[0 0 0 0 0 6 0]
+
+[0 0 0 0 0 0 7]
+
+? matpascal(8)
+
+[1 0  0  0  0  0  0 0 0]
+
+[1 1  0  0  0  0  0 0 0]
+
+[1 2  1  0  0  0  0 0 0]
+
+[1 3  3  1  0  0  0 0 0]
+
+[1 4  6  4  1  0  0 0 0]
+
+[1 5 10 10  5  1  0 0 0]
+
+[1 6 15 20 15  6  1 0 0]
+
+[1 7 21 35 35 21  7 1 0]
+
+[1 8 28 56 70 56 28 8 1]
+
+? matrank(matrix(5,5,x,y,x+y))
+2
+? matrix(5,5,x,y,gcd(x,y))
+
+[1 1 1 1 1]
+
+[1 2 1 2 1]
+
+[1 1 3 1 1]
+
+[1 2 1 4 1]
+
+[1 1 1 1 5]
+
+? matrixqz([1,3;3,5;5,7],0)
+
+[1 1]
+
+[3 2]
+
+[5 3]
+
+? matrixqz([1/3,1/4,1/6;1/2,1/4,-1/4;1/3,1,0],-1)
+
+[19 12 2]
+
+[ 0  1 0]
+
+[ 0  0 1]
+
+? matrixqz([1,3;3,5;5,7],-2)
+
+[2 -1]
+
+[1  0]
+
+[0  1]
+
+? matsize([1,2;3,4;5,6])
+[3, 2]
+? matsnf(1/mathilbert(6))
+[27720, 2520, 2520, 840, 210, 6]
+? matsnf(x*matid(5)-matrix(5,5,j,k,1),2)
+[x^2 - 5*x, x, x, x, 1]
+? matsolve(mathilbert(10),[1,2,3,4,5,6,7,8,9,0]~)
+[9236800, -831303990, 18288515520, -170691240720, 832112321040, -23298940665
+00, 3883123564320, -3803844432960, 2020775945760, -449057772020]~
+? matsolvemod([2,3;5,4],[7,11]~,[1,4]~)
+[-5, -1]~
+? matsolvemod([2,3;5,4],[7,11]~,[1,4]~,1)
+[[-5, -1]~, [4, 9; -5, 8]]
+? matsupplement([1,3;2,4;3,6])
+
+[1 3 0]
+
+[2 4 0]
+
+[3 6 1]
+
+? mattranspose(vector(2,x,x))
+[1, 2]~
+? %*%~
+
+[1 2]
+
+[2 4]
+
+? norml2(vector(10,x,x))
+385
+? qfgaussred(mathilbert(5))
+
+[1  1/2   1/3    1/4     1/5]
+
+[0 1/12     1   9/10     4/5]
+
+[0    0 1/180    3/2    12/7]
+
+[0    0     0 1/2800       2]
+
+[0    0     0      0 1/44100]
+
+? qfjacobi(mathilbert(6))
+[[1.0827994845655497685388772372251778091 E-7, 1.257075712262519492298239799
+6498755378 E-5, 0.00061574835418265769764919938428527140434, 0.0163215213198
+75822124345079564191505890, 0.24236087057520955213572841585070114077, 1.6188
+998589243390969705881471257800713]~, [-0.00124819408408217511693981630463878
+36342, 0.011144320930724710530678340374220998345, -0.06222658815019768177515
+2126611810492941, 0.24032536934252330399154228873240534569, -0.6145448282925
+8676899320019644273870646, 0.74871921887909485900280109200517845109; 0.03560
+6642944287635266122848131812051370, -0.1797327572407600375877689780374064077
+9, 0.49083920971092436297498316169060045043, -0.6976513752773701229620833504
+6678265583, 0.21108248167867048675227675845247769095, 0.44071750324351206127
+160083580231701802; -0.24067907958842295837736719558855680218, 0.60421220675
+295973004426567844103061740, -0.53547692162107486593474491750949545605, -0.2
+3138937333290388042251363554209048307, 0.36589360730302614149086554211117169
+623, 0.32069686982225190106359024326699463107; 0.625460386549227244577534410
+39459331707, -0.44357471627623954554460416705180104473, -0.41703769221897886
+840494514780771076351, 0.13286315850933553530333839628101576048, 0.394706776
+09501756783094636145991581709, 0.25431138634047419251788312792590944672; -0.
+68980719929383668419801738006926828754, -0.441536641012289662221436497529772
+04448, 0.047034018933115649705614518466541245344, 0.362714921464871475252994
+57604461742112, 0.38819043387388642863111448825992418974, 0.2115308400789652
+4664213667673977991960; 0.27160545336631286930015536176213646338, 0.45911481
+681642960284551392793050867151, 0.54068156310385293880022293448123781988, 0.
+50276286675751538489260566368647786274, 0.3706959077673628086177550108480739
+4603, 0.18144297664876947372217005457727093716]]
+? m=1/mathilbert(7)
+
+[    49    -1176      8820    -29400      48510     -38808     12012]
+
+[ -1176    37632   -317520   1128960   -1940400    1596672   -504504]
+
+[  8820  -317520   2857680 -10584000   18711000  -15717240   5045040]
+
+[-29400  1128960 -10584000  40320000  -72765000   62092800 -20180160]
+
+[ 48510 -1940400  18711000 -72765000  133402500 -115259760  37837800]
+
+[-38808  1596672 -15717240  62092800 -115259760  100590336 -33297264]
+
+[ 12012  -504504   5045040 -20180160   37837800  -33297264  11099088]
+
+? mp=concat(m,matid(7))
+
+[49 -1176 8820 -29400 48510 -38808 12012 1 0 0 0 0 0 0]
+
+[-1176 37632 -317520 1128960 -1940400 1596672 -504504 0 1 0 0 0 0 0]
+
+[8820 -317520 2857680 -10584000 18711000 -15717240 5045040 0 0 1 0 0 0 0]
+
+[-29400 1128960 -10584000 40320000 -72765000 62092800 -20180160 0 0 0 1 0 0 
+0]
+
+[48510 -1940400 18711000 -72765000 133402500 -115259760 37837800 0 0 0 0 1 0
+ 0]
+
+[-38808 1596672 -15717240 62092800 -115259760 100590336 -33297264 0 0 0 0 0 
+1 0]
+
+[12012 -504504 5045040 -20180160 37837800 -33297264 11099088 0 0 0 0 0 0 1]
+
+? qflll(m)
+
+[-420 -420 840 630 -1092 757 2982]
+
+[-210 -280 630 504  -876 700 2415]
+
+[-140 -210 504 420  -749 641 2050]
+
+[-105 -168 420 360  -658 589 1785]
+
+[ -84 -140 360 315  -588 544 1582]
+
+[ -70 -120 315 280  -532 505 1421]
+
+[ -60 -105 280 252  -486 471 1290]
+
+? qflllgram(m)
+
+[1 1 27 -27 69   0 141]
+
+[0 1  5 -23 35 -24  50]
+
+[0 1  4 -22 19 -24  24]
+
+[0 1  4 -21 11 -19  14]
+
+[0 1  4 -20  7 -14   9]
+
+[0 1  4 -19  5 -10   6]
+
+[0 1  4 -18  4  -7   4]
+
+? qflllgram(m,1)
+
+[1 1 27 -27 69   0 141]
+
+[0 1  5 -23 35 -24  50]
+
+[0 1  4 -22 19 -24  24]
+
+[0 1  4 -21 11 -19  14]
+
+[0 1  4 -20  7 -14   9]
+
+[0 1  4 -19  5 -10   6]
+
+[0 1  4 -18  4  -7   4]
+
+? qflllgram(mp~*mp,4)
+[[-420, -420, 840, 630, 2982, -1092, 757; -210, -280, 630, 504, 2415, -876, 
+700; -140, -210, 504, 420, 2050, -749, 641; -105, -168, 420, 360, 1785, -658
+, 589; -84, -140, 360, 315, 1582, -588, 544; -70, -120, 315, 280, 1421, -532
+, 505; -60, -105, 280, 252, 1290, -486, 471; 420, 0, 0, 0, -210, 168, 35; 0,
+ 840, 0, 0, 0, 0, 336; 0, 0, -2520, 0, 0, 0, -1260; 0, 0, 0, -2520, 0, 0, -8
+40; 0, 0, 0, 0, -13860, 0, 6930; 0, 0, 0, 0, 0, 5544, 0; 0, 0, 0, 0, 0, 0, -
+12012], [0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0
+, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0,
+ 0; 1, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 
+1, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 1]]
+? qflll(m,1)
+
+[-420 -420 840 630 -1092 757 2982]
+
+[-210 -280 630 504  -876 700 2415]
+
+[-140 -210 504 420  -749 641 2050]
+
+[-105 -168 420 360  -658 589 1785]
+
+[ -84 -140 360 315  -588 544 1582]
+
+[ -70 -120 315 280  -532 505 1421]
+
+[ -60 -105 280 252  -486 471 1290]
+
+? qflll(m,2)
+
+[-420 -420 -630 840 1092 2982 -83]
+
+[-210 -280 -504 630  876 2415  70]
+
+[-140 -210 -420 504  749 2050 137]
+
+[-105 -168 -360 420  658 1785 169]
+
+[ -84 -140 -315 360  588 1582 184]
+
+[ -70 -120 -280 315  532 1421 190]
+
+[ -60 -105 -252 280  486 1290 191]
+
+? qflll(mp,4)
+[[-420, -420, 840, 630, 2982, -1092, 757; -210, -280, 630, 504, 2415, -876, 
+700; -140, -210, 504, 420, 2050, -749, 641; -105, -168, 420, 360, 1785, -658
+, 589; -84, -140, 360, 315, 1582, -588, 544; -70, -120, 315, 280, 1421, -532
+, 505; -60, -105, 280, 252, 1290, -486, 471; 420, 0, 0, 0, -210, 168, 35; 0,
+ 840, 0, 0, 0, 0, 336; 0, 0, -2520, 0, 0, 0, -1260; 0, 0, 0, -2520, 0, 0, -8
+40; 0, 0, 0, 0, -13860, 0, 6930; 0, 0, 0, 0, 0, 5544, 0; 0, 0, 0, 0, 0, 0, -
+12012], [0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0
+, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0,
+ 0; 1, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 
+1, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 1]]
+? qfminim([2,1;1,2],4,6)
+[6, 2, [0, -1, 1; 1, 1, 0]]
+? qfperfection([2,0,1;0,2,1;1,1,2])
+6
+? qfsign(mathilbert(5)-0.11*matid(5))
+[2, 3]
+? trace(1+I)
+2
+? trace(Mod(x+5,x^3+x+1))
+15
+? Vec(sin(x))
+[1, 0, -1/6, 0, 1/120, 0, -1/5040, 0, 1/362880, 0, -1/39916800, 0, 1/6227020
+800, 0, -1/1307674368000, 0]
+? vecmax([-3,7,-2,11])
+11
+? vecmin([-3,7,-2,11])
+-3
+? concat([1,2],[3,4])
+[1, 2, 3, 4]
+? concat(Mat(vector(4,x,x)~),vector(4,x,10+x)~)
+
+[1 11]
+
+[2 12]
+
+[3 13]
+
+[4 14]
+
+? vecextract([1,2,3,4,5,6,7,8,9,10],1000)
+[4, 6, 7, 8, 9, 10]
+? vecextract(matrix(15,15,x,y,x+y),vector(5,x,3*x),vector(3,y,3*y))
+
+[ 6  9 12]
+
+[ 9 12 15]
+
+[12 15 18]
+
+[15 18 21]
+
+[18 21 24]
+
+? round((1.*mathilbert(7))^(-1)<<77)/2^77
+
+[49 -1176 8820 -29400 48510 -38808 12012]
+
+[-1176 37632 -317520 1128960 -1940400 1596672 -504504]
+
+[8820 -317520 2857680 -10584000 18711000 -15717240 5045040]
+
+[-29400 1128960 -10584000 6092986130857731040519127040001/151115727451828646
+838272 -10995935908032311487186862080001/151115727451828646838272 9383198641
+520905802399455641601/151115727451828646838272 -20180160]
+
+[48510 -1940400 18711000 -10995935908032311487186862080001/15111572745182864
+6838272 10079607915696285529921290240001/75557863725914323419136 -8708781239
+161590697851994767361/75557863725914323419136 37837800]
+
+[-38808 1596672 -15717240 9383198641520905802399455641601/151115727451828646
+838272 -8708781239161590697851994767361/75557863725914323419136 152007817992
+63867399887118139393/151115727451828646838272 -33297264]
+
+[12012 -504504 5045040 -20180160 37837800 -33297264 11099088]
+
+? vecsort([8,7,6,5],,1)
+Vecsmall([4, 3, 2, 1])
+? vecsort([[1,5],[2,4],[1,5,1],[1,4,2]])
+[[1, 4, 2], [1, 5], [1, 5, 1], [2, 4]]
+? vecsort(vector(17,x,5*x%17))
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+? vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],2)
+[[2, 5, 8], [3, 6, -6], [1, 8, 5], [4, 8, 6]]
+? vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],[2,1])
+[[2, 5, 8], [3, 6, -6], [1, 8, 5], [4, 8, 6]]
+? vector(10,x,1/x)
+[1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10]
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 12
diff --git a/src/test/32/list b/src/test/32/list
new file mode 100644
index 0000000..368fa62
--- /dev/null
+++ b/src/test/32/list
@@ -0,0 +1,26 @@
+List([1, 2, 3, 3, 5])
+List([1, 1, 2, 3, 3])
+List([1, 2, 3, 3, 1, 2, 3, 3])
+List([1, 1, 2, 2, 3, 3, 3, 3])
+List([1, 2, 3])
+List([1, 2])
+List([2])
+List([[1, 2, 3], 2])
+3
+List([[3, 2, 3], 2])
+List([Vecsmall([1, 2, 3]), 2])
+3
+
+[List([0])]
+
+List([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
+List([10, 9, 8, 7, 6, 5])
+List([])
+List([1, y + 1])
+List([y, x])
+List([y, y*x])
+108
+1
+Mod(1, 3)
+Mod(22, 30)
+Total time spent: 28
diff --git a/src/test/32/lll b/src/test/32/lll
new file mode 100644
index 0000000..5b6395f
--- /dev/null
+++ b/src/test/32/lll
@@ -0,0 +1,78 @@
+
+[1 0]
+
+[0 1]
+
+
+[  0  0 -8   5]
+
+[  8 -5 26 -16]
+
+[ 13 -8 -5   3]
+
+[-21 13  8  -5]
+
+[;]
+[;]
+[;]
+[[;], [;]]
+[[;], [;]]
+[;]
+Mat(1)
+Mat(1)
+Mat(1)
+[[;], Mat(1)]
+[[;], Mat(1)]
+Mat(1)
+[;]
+[;]
+Mat(1)
+[Mat(1), [;]]
+[Mat(1), [;]]
+[;]
+[1; 0]
+[1; 0]
+error("impossible inverse in dvmdii: 0.")
+[[-2; 1], [1; 0]]
+[[-2; 1], [1; 0]]
+[1; 0]
+error("incorrect type in rescale_to_int (t_POL).")
+error("incorrect type in qflll [integer matrix] (t_MAT).")
+error("incorrect type in qflll [integer matrix] (t_MAT).")
+error("incorrect type in qflll [integer matrix] (t_MAT).")
+[[x + 1; -x], [-1; 1]]
+[-1; 1]
+error("incorrect type in rescale_to_int (t_POL).")
+error("incorrect type in qflll [integer matrix] (t_MAT).")
+error("incorrect type in qflll [integer matrix] (t_MAT).")
+error("incorrect type in qflll [integer matrix] (t_MAT).")
+[[;], [-x^2 + 852*x + 833561, x^5 + 1052503*x^4 - 898292021*x^3 - 8780356471
+55*x^2 + 1; 1, -x^3 - 1053355*x^2]]
+[-x^2 + 852*x + 833561, x^5 + 1052503*x^4 - 898292021*x^3 - 878035647155*x^2
+ + 1; 1, -x^3 - 1053355*x^2]
+[;]
+[;]
+[[;], [;]]
+[[;], [;]]
+Mat(1)
+Mat(1)
+[[;], Mat(1)]
+[[;], Mat(1)]
+[;]
+[;]
+[Mat(1), [;]]
+[Mat(1), [;]]
+[1; 0]
+[1; 0]
+[[-2; 1], [1; 0]]
+[[-2; 1], [1; 0]]
+error("incorrect type in rescale_to_int (t_POL).")
+error("incorrect type in qflllgram [integer matrix] (t_MAT).")
+error("incorrect type in qflllgram [integer matrix] (t_MAT).")
+[[x + 1; -x], [-1; 1]]
+error("incorrect type in rescale_to_int (t_POL).")
+error("incorrect type in qflllgram [integer matrix] (t_MAT).")
+error("incorrect type in qflllgram [integer matrix] (t_MAT).")
+[[;], [-x^2 + 852*x + 833561, x^5 + 1052503*x^4 - 898292021*x^3 - 8780356471
+55*x^2 + 1; 1, -x^3 - 1053355*x^2]]
+Total time spent: 8
diff --git a/src/test/32/log b/src/test/32/log
new file mode 100644
index 0000000..ecdd7b8
--- /dev/null
+++ b/src/test/32/log
@@ -0,0 +1,4 @@
+9.9999999999999999999999999999950000000 E-31
+-5.7721566490153286060651209008157996401 E-31
+error("not a prime number in p-adic log: 33.")
+Total time spent: 0
diff --git a/src/test/32/logint b/src/test/32/logint
new file mode 100644
index 0000000..7c1c36f
--- /dev/null
+++ b/src/test/32/logint
@@ -0,0 +1,9 @@
+99
+100
+1267650600228229401496703205376
+232
+6901746346790563787434755862277025452451108972170386555162524223799296
+146
+116
+100
+Total time spent: 0
diff --git a/src/test/32/mat b/src/test/32/mat
new file mode 100644
index 0000000..e7c1b62
--- /dev/null
+++ b/src/test/32/mat
@@ -0,0 +1,361 @@
+error("inconsistent addition t_MAT (1x1) + t_MAT (0x1).")
+error("impossible inverse in gdiv: [Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(0, 
+2)].")
+[0, 2]
+1
+1
+  ***   at top-level: mathouseholder(1,1)
+  ***                 ^-------------------
+  *** mathouseholder: incorrect type in mathouseholder (t_INT).
+  ***   at top-level: mathouseholder(q,1)
+  ***                 ^-------------------
+  *** mathouseholder: incorrect type in mathouseholder (t_INT).
+[;]
+
+[1]
+
+
+[1 2 3]
+
+[2 3 4]
+
+
+[1 1]
+
+[1 5]
+
+
+[1 0]
+
+[0 1]
+
+error("incorrect type in diagonal (t_MAT).")
+
+[1    0     0    0 0]
+
+[1    1     0    0 0]
+
+[1  3/2     1    0 0]
+
+[1  7/4   7/4    1 0]
+
+[1 15/8 35/16 15/8 1]
+
+
+[ 4  6]
+
+[10 12]
+
+
+[0 -2]
+
+[2  4]
+
+[0  0]
+
+
+[Mod(0, 2) Mod(0, 2)]
+
+[Mod(0, 2) Mod(0, 2)]
+
+[Mod(0, 2) Mod(0, 2)]
+
+
+[Mod(0, 7) Mod(5, 7)]
+
+[Mod(2, 7) Mod(4, 7)]
+
+[Mod(0, 7) Mod(0, 7)]
+
+
+[Mod(0, 18446744073709551629) Mod(18446744073709551627, 18446744073709551629
+)]
+
+[Mod(2, 18446744073709551629)                    Mod(4, 18446744073709551629
+)]
+
+[Mod(0, 18446744073709551629)                    Mod(0, 18446744073709551629
+)]
+
+
+[Mod(0, 3037000507) Mod(3037000505, 3037000507)]
+
+[Mod(2, 3037000507)          Mod(4, 3037000507)]
+
+[Mod(0, 3037000507)          Mod(0, 3037000507)]
+
+[Mod(0, 2), Mod(0, 2), Mod(0, 2)]~
+[Mod(0, 7), Mod(2, 7), Mod(0, 7)]~
+[Mod(0, 18446744073709551629), Mod(2, 18446744073709551629), Mod(0, 18446744
+073709551629)]~
+[Mod(0, 3037000507), Mod(2, 3037000507), Mod(0, 3037000507)]~
+matdet:
+Mod(1, 2)
+Mod(1, 7)
+Mod(29, 3037000507)
+Mod(29, 18446744073709551629)
+29 + O(101^3)
+matrank:
+3
+3
+3
+3
+3
+matadjoint:
+[Mod(1, 2), Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(1, 2), Mod(1, 2); Mod(0, 2)
+, Mod(1, 2), Mod(0, 2)]
+[Mod(6, 7), Mod(0, 7), Mod(1, 7); Mod(6, 7), Mod(3, 7), Mod(1, 7); Mod(1, 7)
+, Mod(2, 7), Mod(1, 7)]
+[Mod(69, 3037000507), Mod(14, 3037000507), Mod(3037000473, 3037000507); Mod(
+3037000499, 3037000507), Mod(3, 3037000507), Mod(1, 3037000507); Mod(3037000
+501, 3037000507), Mod(3037000502, 3037000507), Mod(8, 3037000507)]
+[Mod(69, 18446744073709551629), Mod(14, 18446744073709551629), Mod(184467440
+73709551595, 18446744073709551629); Mod(18446744073709551621, 18446744073709
+551629), Mod(3, 18446744073709551629), Mod(1, 18446744073709551629); Mod(184
+46744073709551623, 18446744073709551629), Mod(18446744073709551624, 18446744
+073709551629), Mod(8, 18446744073709551629)]
+[69 + O(101^3), 14 + O(101^3), 67 + 100*101 + 100*101^2 + O(101^3); 93 + 100
+*101 + 100*101^2 + O(101^3), 3 + O(101^3), 1 + O(101^3); 95 + 100*101 + 100*
+101^2 + O(101^3), 96 + 100*101 + 100*101^2 + O(101^3), 8 + O(101^3)]
+matimage:
+[Mod(1, 2), Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(0, 2), Mod(1, 2); Mod(0, 2)
+, Mod(1, 2), Mod(1, 2)]
+[Mod(1, 7), Mod(2, 7), Mod(4, 7); Mod(2, 7), Mod(5, 7), Mod(0, 7); Mod(2, 7)
+, Mod(2, 7), Mod(4, 7)]
+[Mod(1, 3037000507), Mod(2, 3037000507), Mod(4, 3037000507); Mod(2, 30370005
+07), Mod(12, 3037000507), Mod(7, 3037000507); Mod(2, 3037000507), Mod(9, 303
+7000507), Mod(11, 3037000507)]
+[Mod(1, 18446744073709551629), Mod(2, 18446744073709551629), Mod(4, 18446744
+073709551629); Mod(2, 18446744073709551629), Mod(12, 18446744073709551629), 
+Mod(7, 18446744073709551629); Mod(2, 18446744073709551629), Mod(9, 184467440
+73709551629), Mod(11, 18446744073709551629)]
+[1 + O(101^3), 2 + O(101^3), 4 + O(101^3); 2 + O(101^3), 12 + O(101^3), 7 + 
+O(101^3); 2 + O(101^3), 9 + O(101^3), 11 + O(101^3)]
+matimagecompl:
+Vecsmall([])
+Vecsmall([])
+Vecsmall([])
+Vecsmall([])
+Vecsmall([])
+matindexrank:
+[Vecsmall([1, 2, 3]), Vecsmall([1, 2, 3])]
+[Vecsmall([1, 2, 3]), Vecsmall([1, 2, 3])]
+[Vecsmall([1, 2, 3]), Vecsmall([1, 2, 3])]
+[Vecsmall([1, 2, 3]), Vecsmall([1, 2, 3])]
+[Vecsmall([1, 2, 3]), Vecsmall([1, 2, 3])]
+matker:
+[;]
+[;]
+[;]
+[;]
+[;]
+lindep:
+[]~
+[]~
+[]~
+[]~
+[]~
+(x)->matsolve(x,vectorv(#x,i,i)):
+[Mod(1, 2), Mod(1, 2), Mod(0, 2)]~
+[Mod(2, 7), Mod(1, 7), Mod(1, 7)]~
+[Mod(2827552196, 3037000507), Mod(1256689865, 3037000507), Mod(942517399, 30
+37000507)]~
+[Mod(16538460204015460081, 18446744073709551629), Mod(11449703218164549287, 
+18446744073709551629), Mod(17810649450478187780, 18446744073709551629)]~
+[66 + 69*101 + 62*101^2 + O(101^3), 7 + 87*101 + 27*101^2 + O(101^3), 56 + 9
+0*101 + 20*101^2 + O(101^3)]~
+(x)->matsolve(x,matrix(#x,#x,i,j,i+j)):
+[Mod(0, 2), Mod(1, 2), Mod(0, 2); Mod(1, 2), Mod(1, 2), Mod(1, 2); Mod(1, 2)
+, Mod(0, 2), Mod(1, 2)]
+[Mod(2, 7), Mod(2, 7), Mod(2, 7); Mod(4, 7), Mod(0, 7), Mod(3, 7); Mod(5, 7)
+, Mod(2, 7), Mod(6, 7)]
+[Mod(628344934, 3037000507), Mod(1466138179, 3037000507), Mod(2303931424, 30
+37000507); Mod(2303931419, 3037000507), Mod(314172466, 3037000507), Mod(1361
+414020, 3037000507); Mod(209448311, 3037000507), Mod(2513379730, 3037000507)
+, Mod(1780310642, 3037000507)]
+[Mod(5724851609082274645, 18446744073709551629), Mod(13357987087858640838, 1
+8446744073709551629), Mod(2544378492925455402, 18446744073709551629); Mod(25
+44378492925455397, 18446744073709551629), Mod(12085797841395913136, 18446744
+073709551629), Mod(3180473116156819246, 18446744073709551629); Mod(190828386
+9694091548, 18446744073709551629), Mod(4452662362619546945, 1844674407370955
+1629), Mod(6997040855545002342, 18446744073709551629)]
+[5 + 94*101 + 13*101^2 + O(101^3), 45 + 17*101 + 66*101^2 + O(101^3), 85 + 4
+1*101 + 17*101^2 + O(101^3); 80 + 41*101 + 17*101^2 + O(101^3), 52 + 97*101 
++ 6*101^2 + O(101^3), 24 + 52*101 + 97*101^2 + O(101^3); 35 + 31*101 + 38*10
+1^2 + O(101^3), 14 + 73*101 + 55*101^2 + O(101^3), 94 + 13*101 + 73*101^2 + 
+O(101^3)]
+(x)->x^(-1):
+[Mod(1, 2), Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(1, 2), Mod(1, 2); Mod(0, 2)
+, Mod(1, 2), Mod(0, 2)]
+[Mod(6, 7), Mod(0, 7), Mod(1, 7); Mod(6, 7), Mod(3, 7), Mod(1, 7); Mod(1, 7)
+, Mod(2, 7), Mod(1, 7)]
+[Mod(1675586489, 3037000507), Mod(2408655575, 3037000507), Mod(2827552195, 3
+037000507); Mod(2094483108, 3037000507), Mod(733069088, 3037000507), Mod(125
+6689865, 3037000507); Mod(1570862331, 3037000507), Mod(2827552196, 303700050
+7), Mod(942517399, 3037000507)]
+[Mod(15266270957552732385, 18446744073709551629), Mod(12721892464627276986, 
+18446744073709551629), Mod(16538460204015460080, 18446744073709551629); Mod(
+636094623231363849, 18446744073709551629), Mod(15902365580784096232, 1844674
+4073709551629), Mod(11449703218164549287, 18446744073709551629); Mod(5088756
+985850910794, 18446744073709551629), Mod(16538460204015460081, 1844674407370
+9551629), Mod(17810649450478187780, 18446744073709551629)]
+[79 + 48*101 + 3*101^2 + O(101^3), 98 + 6*101 + 87*101^2 + O(101^3), 65 + 69
+*101 + 62*101^2 + O(101^3); 45 + 10*101 + 80*101^2 + O(101^3), 21 + 59*101 +
+ 83*101^2 + O(101^3), 7 + 87*101 + 27*101^2 + O(101^3); 59 + 83*101 + 34*101
+^2 + O(101^3), 66 + 69*101 + 62*101^2 + O(101^3), 56 + 90*101 + 20*101^2 + O
+(101^3)]
+(x)->x^2:
+[Mod(1, 2), Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(1, 2), Mod(1, 2); Mod(0, 2)
+, Mod(1, 2), Mod(0, 2)]
+[Mod(6, 7), Mod(6, 7), Mod(6, 7); Mod(5, 7), Mod(1, 7), Mod(1, 7); Mod(0, 7)
+, Mod(1, 7), Mod(3, 7)]
+[Mod(13, 3037000507), Mod(62, 3037000507), Mod(62, 3037000507); Mod(40, 3037
+000507), Mod(211, 3037000507), Mod(169, 3037000507); Mod(42, 3037000507), Mo
+d(211, 3037000507), Mod(192, 3037000507)]
+[Mod(13, 18446744073709551629), Mod(62, 18446744073709551629), Mod(62, 18446
+744073709551629); Mod(40, 18446744073709551629), Mod(211, 184467440737095516
+29), Mod(169, 18446744073709551629); Mod(42, 18446744073709551629), Mod(211,
+ 18446744073709551629), Mod(192, 18446744073709551629)]
+[13 + O(101^3), 62 + O(101^3), 62 + O(101^3); 40 + O(101^3), 9 + 2*101 + O(1
+01^3), 68 + 101 + O(101^3); 42 + O(101^3), 9 + 2*101 + O(101^3), 91 + 101 + 
+O(101^3)]
+(x)->A*x:
+[Mod(1, 2), Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(1, 2), Mod(1, 2); Mod(0, 2)
+, Mod(1, 2), Mod(0, 2)]
+[Mod(6, 7), Mod(6, 7), Mod(6, 7); Mod(5, 7), Mod(1, 7), Mod(1, 7); Mod(0, 7)
+, Mod(1, 7), Mod(3, 7)]
+[Mod(13, 3037000507), Mod(62, 3037000507), Mod(62, 3037000507); Mod(40, 3037
+000507), Mod(211, 3037000507), Mod(169, 3037000507); Mod(42, 3037000507), Mo
+d(211, 3037000507), Mod(192, 3037000507)]
+[Mod(13, 18446744073709551629), Mod(62, 18446744073709551629), Mod(62, 18446
+744073709551629); Mod(40, 18446744073709551629), Mod(211, 184467440737095516
+29), Mod(169, 18446744073709551629); Mod(42, 18446744073709551629), Mod(211,
+ 18446744073709551629), Mod(192, 18446744073709551629)]
+[13 + O(101^3), 62 + O(101^3), 62 + O(101^3); 40 + O(101^3), 9 + 2*101 + O(1
+01^3), 68 + 101 + O(101^3); 42 + O(101^3), 9 + 2*101 + O(101^3), 91 + 101 + 
+O(101^3)]
+[;]
+matdet:
+1
+1
+1
+1
+1
+matrank:
+0
+0
+0
+0
+0
+matadjoint:
+[;]
+[;]
+[;]
+[;]
+[;]
+matimage:
+[;]
+[;]
+[;]
+[;]
+[;]
+matimagecompl:
+Vecsmall([])
+Vecsmall([])
+Vecsmall([])
+Vecsmall([])
+Vecsmall([])
+matindexrank:
+[Vecsmall([]), Vecsmall([])]
+[Vecsmall([]), Vecsmall([])]
+[Vecsmall([]), Vecsmall([])]
+[Vecsmall([]), Vecsmall([])]
+[Vecsmall([]), Vecsmall([])]
+matker:
+[;]
+[;]
+[;]
+[;]
+[;]
+lindep:
+[]~
+[]~
+[]~
+[]~
+[]~
+(x)->matsolve(x,vectorv(#x,i,i)):
+[]~
+[]~
+[]~
+[]~
+[]~
+(x)->matsolve(x,matrix(#x,#x,i,j,i+j)):
+[;]
+[;]
+[;]
+[;]
+[;]
+(x)->x^(-1):
+[;]
+[;]
+[;]
+[;]
+[;]
+(x)->x^2:
+[;]
+[;]
+[;]
+[;]
+[;]
+(x)->A*x:
+[;]
+[;]
+[;]
+[;]
+[;]
+Mod(3037000506, 3037000507)
+Mod(18446744073709551628, 18446744073709551629)
+[Mod(3, 18446744073709551629), Mod(1, 18446744073709551629), Mod(18446744073
+709551628, 18446744073709551629)]~
+
+[-1.0000000000000000000000000000000000000*I]
+
+[                                         1]
+
+3
+0
+error("inconsistent dimensions in gtrace.")
+
+[1 0]
+
+[0 1]
+
+[;]
+lindep:
+[1, 0, 1]~
+[1, -2, 1]~
+[1, -2, 1]~
+[1, -2, 1]~
+[3 + O(101^3), 95 + 100*101 + 100*101^2 + O(101^3), 3 + O(101^3)]~
+matsupplement:
+[Mod(1, 2), Mod(0, 2), Mod(0, 2); Mod(0, 2), Mod(1, 2), Mod(0, 2); Mod(1, 2)
+, Mod(0, 2), Mod(1, 2)]
+[Mod(1, 7), Mod(2, 7), Mod(0, 7); Mod(4, 7), Mod(5, 7), Mod(0, 7); Mod(0, 7)
+, Mod(1, 7), Mod(1, 7)]
+[Mod(1, 3037000507), Mod(2, 3037000507), Mod(0, 3037000507); Mod(4, 30370005
+07), Mod(5, 3037000507), Mod(0, 3037000507); Mod(7, 3037000507), Mod(8, 3037
+000507), Mod(1, 3037000507)]
+[Mod(1, 18446744073709551629), Mod(2, 18446744073709551629), Mod(0, 18446744
+073709551629); Mod(4, 18446744073709551629), Mod(5, 18446744073709551629), M
+od(0, 18446744073709551629); Mod(7, 18446744073709551629), Mod(8, 1844674407
+3709551629), Mod(1, 18446744073709551629)]
+[1 + O(101^3), 2 + O(101^3), 0; 4 + O(101^3), 5 + O(101^3), 0; 7 + O(101^3),
+ 8 + O(101^3), 1]
+  ***   Warning: new stack size = 1000000 (0.954 Mbytes).
+68
+2
+68
+0
+2
+17
+Total time spent: 56
diff --git a/src/test/32/mathnf b/src/test/32/mathnf
new file mode 100644
index 0000000..fd73fa0
--- /dev/null
+++ b/src/test/32/mathnf
@@ -0,0 +1,59 @@
+   echo = 1 (on)
+? mathnf([0,2])
+
+[2]
+
+? mathnf([0,2],1)
+[Mat(2), [1, 0; 0, 1]]
+? mathnf([0,x])
+
+[x]
+
+? mathnf([0,x],1)
+[Mat(x), [1, 0; 0, 1]]
+? mathnf([x,x^2+1;x^3+x+1,x+2]*Mod(1,5))
+
+[Mod(1, 5)*x^5 + Mod(2, 5)*x^3 + Mod(4, 5)*x + Mod(1, 5) Mod(4, 5)*x^4 + Mod
+(2, 5)*x^3 + Mod(4, 5)*x^2 + Mod(3, 5)*x]
+
+[0 1]
+
+? v=[116085838,181081878,314252913,10346840];
+? [H,U]=mathnf(v,1);[v*U,norml2(U)]
+[[0, 0, 0, 1], 2833319]
+? [H,U]=mathnf(v,5);[v*U,norml2(U)]
+[[0, 0, 0, 1], 765585180708864230567243002686057927228240493]
+? mathnf([])
+[;]
+? mathnf([],1)
+[[;], [;]]
+? mathnf([;])
+[;]
+? mathnf([;],1)
+[[;], [;]]
+? mathnfmodid(matrix(0,2),[])
+[;]
+? mathnfmodid([0,7;-1,0;-1,-1],[6,2,2])
+
+[2 1 1]
+
+[0 1 0]
+
+[0 0 1]
+
+? matsolvemod([;],[]~,[]~,1)
+0
+? matsolvemod([;],[],[]~,1)
+  ***   at top-level: matsolvemod([;],[],[
+  ***                 ^--------------------
+  *** matsolvemod: incorrect type in gaussmodulo (t_VEC).
+? matsolvemod([;],[]~,[],1)
+  ***   at top-level: matsolvemod([;],[]~,
+  ***                 ^--------------------
+  *** matsolvemod: incorrect type in gaussmodulo (t_VEC).
+? matsolvemod([;],1,1,1)
+0
+? matsolvemod([1,2;3,4],1,2,1)
+[[0, 0]~, [-1, 0; 0, 1]]
+? print("Total time spent: ",gettime);
+Total time spent: 0
diff --git a/src/test/32/matsnf b/src/test/32/matsnf
new file mode 100644
index 0000000..487131a
--- /dev/null
+++ b/src/test/32/matsnf
@@ -0,0 +1,331 @@
+x^2 + 10*x + 27
+x^2 + 10/3*x + 3
+x^2 + 10/a*x + 27/a^2
+x^2 + 10*a*x + 27*a^2
+[2, 1]
+[X^2 + 10*X + 27, X^2 + 10*X + 27, 1, 1]
+x^2 - 2*a*x + (a^2 - d^2)
+[Mat(1), Mat(1)]
+[[0, -1; 1, 2], [1, -1; 0, 1]]
+[[0, 0, 1; 1, 0, -3; 0, 1, 3], [1, -2, 1; 0, 3, -2; 0, -1, 1]]
+[[0, 0, 0, -1; 1, 0, 0, 4; 0, 1, 0, -6; 0, 0, 1, 4], [1, -3, 3, -1; 0, 6, -8
+, 3; 0, -4, 7, -3; 0, 1, -2, 1]]
+[[0, 0, 0, 0, 1; 1, 0, 0, 0, -5; 0, 1, 0, 0, 10; 0, 0, 1, 0, -10; 0, 0, 0, 1
+, 5], [1, -4, 6, -4, 1; 0, 10, -20, 15, -4; 0, -10, 25, -21, 6; 0, 5, -14, 1
+3, -4; 0, -1, 3, -3, 1]]
+[[0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 6; 0, 1, 0, 0, 0, -15; 0, 0, 1, 0, 0, 20
+; 0, 0, 0, 1, 0, -15; 0, 0, 0, 0, 1, 6], [1, -5, 10, -10, 5, -1; 0, 15, -40,
+ 45, -24, 5; 0, -20, 65, -81, 46, -10; 0, 15, -54, 73, -44, 10; 0, -6, 23, -
+33, 21, -5; 0, 1, -4, 6, -4, 1]]
+[[0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, -7; 0, 1, 0, 0, 0, 0, 21; 0, 0, 1, 
+0, 0, 0, -35; 0, 0, 0, 1, 0, 0, 35; 0, 0, 0, 0, 1, 0, -21; 0, 0, 0, 0, 0, 1,
+ 7], [1, -6, 15, -20, 15, -6, 1; 0, 21, -70, 105, -84, 35, -6; 0, -35, 140, 
+-231, 196, -85, 15; 0, 35, -154, 273, -244, 110, -20; 0, -21, 98, -183, 171,
+ -80, 15; 0, 7, -34, 66, -64, 31, -6; 0, -1, 5, -10, 10, -5, 1]]
+[[0, 0, 0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 0, 0, 8; 0, 1, 0, 0, 0, 0, 0, -28;
+ 0, 0, 1, 0, 0, 0, 0, 56; 0, 0, 0, 1, 0, 0, 0, -70; 0, 0, 0, 0, 1, 0, 0, 56;
+ 0, 0, 0, 0, 0, 1, 0, -28; 0, 0, 0, 0, 0, 0, 1, 8], [1, -7, 21, -35, 35, -21
+, 7, -1; 0, 28, -112, 210, -224, 140, -48, 7; 0, -56, 266, -546, 616, -400, 
+141, -21; 0, 70, -364, 798, -944, 635, -230, 35; 0, -56, 308, -708, 871, -60
+5, 225, -35; 0, 28, -160, 381, -484, 346, -132, 21; 0, -8, 47, -115, 150, -1
+10, 43, -7; 0, 1, -6, 15, -20, 15, -6, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, 0, 0, -9; 0, 1, 0, 0, 0, 0, 0
+, 0, 36; 0, 0, 1, 0, 0, 0, 0, 0, -84; 0, 0, 0, 1, 0, 0, 0, 0, 126; 0, 0, 0, 
+0, 1, 0, 0, 0, -126; 0, 0, 0, 0, 0, 1, 0, 0, 84; 0, 0, 0, 0, 0, 0, 1, 0, -36
+; 0, 0, 0, 0, 0, 0, 0, 1, 9], [1, -8, 28, -56, 70, -56, 28, -8, 1; 0, 36, -1
+68, 378, -504, 420, -216, 63, -8; 0, -84, 462, -1134, 1596, -1380, 729, -217
+, 28; 0, 126, -756, 1974, -2904, 2595, -1406, 427, -56; 0, -126, 798, -2178,
+ 3321, -3055, 1695, -525, 70; 0, 84, -552, 1557, -2444, 2306, -1308, 413, -5
+6; 0, -36, 243, -703, 1130, -1090, 631, -203, 28; 0, 9, -62, 183, -300, 295,
+ -174, 57, -8; 0, -1, 7, -21, 35, -35, 21, -7, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 10; 0, 1, 0, 0, 
+0, 0, 0, 0, 0, -45; 0, 0, 1, 0, 0, 0, 0, 0, 0, 120; 0, 0, 0, 1, 0, 0, 0, 0, 
+0, -210; 0, 0, 0, 0, 1, 0, 0, 0, 0, 252; 0, 0, 0, 0, 0, 1, 0, 0, 0, -210; 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 120; 0, 0, 0, 0, 0, 0, 0, 1, 0, -45; 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 10], [1, -9, 36, -84, 126, -126, 84, -36, 9, -1; 0, 45, -240, 6
+30, -1008, 1050, -720, 315, -80, 9; 0, -120, 750, -2142, 3612, -3900, 2745, 
+-1225, 316, -36; 0, 210, -1428, 4326, -7608, 8475, -6110, 2779, -728, 84; 0,
+ -252, 1806, -5706, 10377, -11875, 8751, -4053, 1078, -126; 0, 210, -1560, 5
+085, -9500, 11126, -8364, 3941, -1064, 126; 0, -120, 915, -3055, 5834, -6970
+, 5335, -2555, 700, -84; 0, 45, -350, 1191, -2316, 2815, -2190, 1065, -296, 
+36; 0, -10, 79, -273, 539, -665, 525, -259, 73, -9; 0, 1, -8, 28, -56, 70, -
+56, 28, -8, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11; 0, 1, 
+0, 0, 0, 0, 0, 0, 0, 0, 55; 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -165; 0, 0, 0, 1, 
+0, 0, 0, 0, 0, 0, 330; 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -462; 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 462; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -330; 0, 0, 0, 0, 0, 0, 0, 1
+, 0, 0, 165; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -55; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+, 11], [1, -10, 45, -120, 210, -252, 210, -120, 45, -10, 1; 0, 55, -330, 990
+, -1848, 2310, -1980, 1155, -440, 99, -10; 0, -165, 1155, -3762, 7392, -9570
+, 8415, -5005, 1936, -441, 45; 0, 330, -2508, 8646, -17688, 23595, -21230, 1
+2859, -5048, 1164, -120; 0, -462, 3696, -13266, 28017, -38335, 35211, -21693
+, 8638, -2016, 210; 0, 462, -3828, 14157, -30668, 42878, -40116, 25109, -101
+36, 2394, -252; 0, -330, 2805, -10615, 23474, -33430, 31795, -20195, 8260, -
+1974, 210; 0, 165, -1430, 5511, -12396, 17935, -17310, 11145, -4616, 1116, -
+120; 0, -55, 484, -1893, 4319, -6335, 6195, -4039, 1693, -414, 45; 0, 11, -9
+8, 388, -896, 1330, -1316, 868, -368, 91, -10; 0, -1, 9, -36, 84, -126, 126,
+ -84, 36, -9, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12; 
+0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -66; 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 220; 
+0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -495; 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 792;
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -924; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 792
+; 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -495; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 22
+0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -66; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12
+], [1, -11, 55, -165, 330, -462, 462, -330, 165, -55, 11, -1; 0, 66, -440, 1
+485, -3168, 4620, -4752, 3465, -1760, 594, -120, 11; 0, -220, 1705, -6237, 1
+3992, -21120, 22275, -16555, 8536, -2916, 595, -55; 0, 495, -4158, 16071, -3
+7488, 58245, -62810, 47509, -24848, 8589, -1770, 165; 0, -792, 6996, -28116,
+ 67617, -107635, 118371, -90993, 48238, -16866, 3510, -330; 0, 924, -8448, 3
+4947, -86108, 139898, -156540, 122129, -65576, 23184, -4872, 462; 0, -792, 7
+425, -31405, 78914, -130450, 148219, -117215, 63700, -22764, 4830, -462; 0, 
+495, -4730, 20361, -51996, 87235, -100470, 80445, -44216, 15966, -3420, 330;
+ 0, -220, 2134, -9318, 24119, -40985, 47775, -38689, 21493, -7839, 1695, -16
+5; 0, 66, -648, 2863, -7496, 12880, -15176, 12418, -6968, 2566, -560, 55; 0,
+ -12, 119, -531, 1404, -2436, 2898, -2394, 1356, -504, 111, -11; 0, 1, -10, 
+45, -120, 210, -252, 210, -120, 45, -10, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ -13; 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78; 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, -286; 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 715; 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, -1287; 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1716; 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0, 0, -1716; 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1287; 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -715; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 286
+; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -78; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+1, 13], [1, -12, 66, -220, 495, -792, 924, -792, 495, -220, 66, -12, 1; 0, 7
+8, -572, 2145, -5148, 8580, -10296, 9009, -5720, 2574, -780, 143, -12; 0, -2
+86, 2431, -9867, 24882, -42900, 52767, -47047, 30316, -13806, 4225, -781, 66
+; 0, 715, -6578, 28171, -73788, 130845, -164450, 149149, -97448, 44889, -138
+70, 2585, -220; 0, -1287, 12441, -55341, 149292, -270985, 347061, -319683, 2
+11588, -98541, 30735, -5775, 495; 0, 1716, -17160, 78507, -216788, 401258, -
+522444, 488033, -326936, 153864, -48432, 9174, -792; 0, -1716, 17589, -82225
+, 231374, -435370, 575107, -544103, 368620, -175224, 55650, -10626, 924; 0, 
+1287, -13442, 63921, -182676, 348595, -466374, 446349, -305576, 146646, -469
+80, 9042, -792; 0, -715, 7579, -36543, 105794, -204335, 276465, -267379, 184
+843, -89514, 28920, -5610, 495; 0, 286, -3068, 14963, -43796, 85480, -116816
+, 114058, -79568, 38866, -12660, 2475, -220; 0, -78, 845, -4161, 12294, -242
+16, 33390, -32886, 23136, -11394, 3741, -737, 66; 0, 13, -142, 705, -2100, 4
+170, -5796, 5754, -4080, 2025, -670, 133, -12; 0, -1, 11, -55, 165, -330, 46
+2, -462, 330, -165, 55, -11, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+, 0, 0, 14; 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -91; 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 364; 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1001; 0, 0, 
+0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2002; 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
+, -3003; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3432; 0, 0, 0, 0, 0, 0, 0, 1
+, 0, 0, 0, 0, 0, -3003; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2002; 0, 0, 0
+, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1001; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
+, 364; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -91; 0, 0, 0, 0, 0, 0, 0, 0, 0
+, 0, 0, 0, 1, 14], [1, -13, 78, -286, 715, -1287, 1716, -1716, 1287, -715, 2
+86, -78, 13, -1; 0, 91, -728, 3003, -8008, 15015, -20592, 21021, -16016, 900
+9, -3640, 1001, -168, 13; 0, -364, 3367, -15015, 42042, -81510, 114543, -119
+119, 92092, -52416, 21385, -5929, 1002, -78; 0, 1001, -10010, 47047, -136708
+, 272415, -390962, 413413, -323960, 186459, -76790, 21461, -3652, 286; 0, -2
+002, 21021, -102531, 306592, -624910, 913341, -980343, 777868, -452466, 1880
+35, -52965, 9075, -715; 0, 3003, -32604, 163449, -499928, 1038323, -1541748,
+ 1677221, -1346240, 790929, -331572, 94116, -16236, 1287; 0, -3432, 38181, -
+195481, 608894, -1284790, 1934179, -2129687, 1727692, -1024644, 433170, -123
+882, 21516, -1716; 0, 3003, -34034, 177177, -560196, 1198015, -1825446, 2031
+933, -1664648, 996066, -424500, 122298, -21384, 1716; 0, -2002, 23023, -1214
+85, 388934, -841400, 1295769, -1456567, 1204147, -726579, 312060, -90552, 15
+939, -1287; 0, 1001, -11648, 62153, -201096, 439405, -683096, 774718, -64584
+8, 392791, -169960, 49665, -8800, 715; 0, -364, 4277, -23037, 75214, -165786
+, 259902, -297150, 249648, -152964, 66661, -19613, 3498, -286; 0, 91, -1078,
+ 5853, -19260, 42780, -67572, 77826, -65856, 40635, -17830, 5281, -948, 78; 
+0, -14, 167, -913, 3025, -6765, 10758, -12474, 10626, -6600, 2915, -869, 157
+, -13; 0, 1, -12, 66, -220, 495, -792, 924, -792, 495, -220, 66, -12, 1]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, -15; 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105; 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -455; 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 1365; 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3003; 0, 0, 0, 0, 0, 1, 
+0, 0, 0, 0, 0, 0, 0, 0, 5005; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -643
+5; 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 6435; 0, 0, 0, 0, 0, 0, 0, 0, 1
+, 0, 0, 0, 0, 0, -5005; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3003; 0, 0
+, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1365; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+, 1, 0, 0, 455; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -105; 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15], [1, -14, 91, -364, 1001, -2002, 3003, -34
+32, 3003, -2002, 1001, -364, 91, -14, 1; 0, 105, -910, 4095, -12012, 25025, 
+-38610, 45045, -40040, 27027, -13650, 5005, -1260, 195, -14; 0, -455, 4550, 
+-22113, 68068, -146575, 231660, -275275, 248248, -169533, 86450, -31955, 810
+0, -1261, 91; 0, 1365, -14742, 75439, -240812, 532675, -859430, 1038037, -94
+8584, 654927, -337050, 125565, -32044, 5018, -364; 0, -3003, 34034, -180609,
+ 592878, -1340625, 2201628, -2698059, 2495584, -1740753, 903750, -339251, 87
+153, -13728, 1001; 0, 5005, -58630, 319605, -1072500, 2469753, -4118322, 511
+2653, -4781672, 3367503, -1763002, 666688, -172392, 27313, -2002; 0, -6435, 
+77220, -429715, 1467752, -3431935, 5799040, -7282835, 6880840, -4889505, 258
+0315, -982740, 255750, -40755, 3003; 0, 6435, -78650, 444873, -1541748, 3651
+895, -6242430, 7921245, -7553960, 5413050, -2878380, 1103850, -289080, 46332
+, -3432; 0, -5005, 62062, -355719, 1247792, -2988545, 5160630, -6609715, 635
+7295, -4591440, 2459205, -949410, 250173, -40326, 3003; 0, 3003, -37674, 218
+309, -773668, 1870835, -3259670, 4210150, -4081280, 2969365, -1601390, 62223
+7, -164956, 26741, -2002; 0, -1365, 17290, -101115, 361500, -881501, 1548189
+, -2014866, 1967364, -1441251, 782376, -305899, 81576, -13299, 1001; 0, 455,
+ -5810, 34245, -123364, 303040, -536040, 702450, -690480, 509103, -278090, 1
+09385, -29340, 4810, -364; 0, -105, 1350, -8011, 29051, -71830, 127875, -168
+630, 166782, -123717, 67980, -26895, 7255, -1196, 91; 0, 15, -194, 1158, -42
+24, 10505, -18810, 24948, -24816, 18513, -10230, 4070, -1104, 183, -14; 0, -
+1, 13, -78, 286, -715, 1287, -1716, 1716, -1287, 715, -286, 78, -13, 1]]
+[[0, -1; 1, 2], [1, -1; 0, 1]]
+[[0, 0, 1; 1, 0, -3; 0, 1, 3], [1, -3/2, 1/2; 0, 2, -1; 0, -1/2, 1/2]]
+[[0, 0, 0, -1; 1, 0, 0, 4; 0, 1, 0, -6; 0, 0, 1, 4], [1, -11/6, 1, -1/6; 0, 
+3, -5/2, 1/2; 0, -3/2, 2, -1/2; 0, 1/3, -1/2, 1/6]]
+[[0, 0, 0, 0, 1; 1, 0, 0, 0, -5; 0, 1, 0, 0, 10; 0, 0, 1, 0, -10; 0, 0, 0, 1
+, 5], [1, -25/12, 35/24, -5/12, 1/24; 0, 4, -13/3, 3/2, -1/6; 0, -3, 19/4, -
+2, 1/4; 0, 4/3, -7/3, 7/6, -1/6; 0, -1/4, 11/24, -1/4, 1/24]]
+[[0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 6; 0, 1, 0, 0, 0, -15; 0, 0, 1, 0, 0, 20
+; 0, 0, 0, 1, 0, -15; 0, 0, 0, 0, 1, 6], [1, -137/60, 15/8, -17/24, 1/8, -1/
+120; 0, 5, -77/12, 71/24, -7/12, 1/24; 0, -5, 107/12, -59/12, 13/12, -1/12; 
+0, 10/3, -13/2, 49/12, -1, 1/12; 0, -5/4, 61/24, -41/24, 11/24, -1/24; 0, 1/
+5, -5/12, 7/24, -1/12, 1/120]]
+[[0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, -7; 0, 1, 0, 0, 0, 0, 21; 0, 0, 1, 
+0, 0, 0, -35; 0, 0, 0, 1, 0, 0, 35; 0, 0, 0, 0, 1, 0, -21; 0, 0, 0, 0, 0, 1,
+ 7], [1, -49/20, 203/90, -49/48, 35/144, -7/240, 1/720; 0, 6, -87/10, 29/6, 
+-31/24, 1/6, -1/120; 0, -15/2, 117/8, -461/48, 137/48, -19/48, 1/48; 0, 20/3
+, -127/9, 31/3, -121/36, 1/2, -1/36; 0, -15/4, 33/4, -307/48, 107/48, -17/48
+, 1/48; 0, 6/5, -27/10, 13/6, -19/24, 2/15, -1/120; 0, -1/6, 137/360, -5/16,
+ 17/144, -1/48, 1/720]]
+[[0, 0, 0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 0, 0, 8; 0, 1, 0, 0, 0, 0, 0, -28;
+ 0, 0, 1, 0, 0, 0, 0, 56; 0, 0, 0, 1, 0, 0, 0, -70; 0, 0, 0, 0, 1, 0, 0, 56;
+ 0, 0, 0, 0, 0, 1, 0, -28; 0, 0, 0, 0, 0, 0, 1, 8], [1, -363/140, 469/180, -
+967/720, 7/18, -23/360, 1/180, -1/5040; 0, 7, -223/20, 319/45, -37/16, 59/14
+4, -3/80, 1/720; 0, -21/2, 879/40, -3929/240, 71/12, -9/8, 13/120, -1/240; 0
+, 35/3, -949/36, 389/18, -1219/144, 247/144, -25/144, 1/144; 0, -35/4, 41/2,
+ -2545/144, 22/3, -113/72, 1/6, -1/144; 0, 21/5, -201/20, 134/15, -185/48, 6
+9/80, -23/240, 1/240; 0, -7/6, 1019/360, -1849/720, 41/36, -19/72, 11/360, -
+1/720; 0, 1/7, -7/20, 29/90, -7/48, 5/144, -1/240, 1/5040]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, 0, 0, -9; 0, 1, 0, 0, 0, 0, 0
+, 0, 36; 0, 0, 1, 0, 0, 0, 0, 0, -84; 0, 0, 0, 1, 0, 0, 0, 0, 126; 0, 0, 0, 
+0, 1, 0, 0, 0, -126; 0, 0, 0, 0, 0, 1, 0, 0, 84; 0, 0, 0, 0, 0, 0, 1, 0, -36
+; 0, 0, 0, 0, 0, 0, 0, 1, 9], [1, -761/280, 29531/10080, -267/160, 1069/1920
+, -9/80, 13/960, -1/1120, 1/40320; 0, 8, -481/35, 349/36, -329/90, 115/144, 
+-73/720, 1/144, -1/5040; 0, -14, 621/20, -18353/720, 15289/1440, -179/72, 23
+9/720, -17/720, 1/1440; 0, 56/3, -2003/45, 797/20, -268/15, 71/16, -149/240,
+ 11/240, -1/720; 0, -35/2, 691/16, -1457/36, 10993/576, -179/36, 209/288, -1
+/18, 1/576; 0, 56/5, -141/5, 4891/180, -1193/90, 2581/720, -391/720, 31/720,
+ -1/720; 0, -14/3, 2143/180, -187/16, 2803/480, -13/8, 61/240, -1/48, 1/1440
+; 0, 8/7, -103/35, 527/180, -67/45, 61/144, -49/720, 29/5040, -1/5040; 0, -1
+/8, 363/1120, -469/1440, 967/5760, -7/144, 23/2880, -1/1440, 1/40320]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, -1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 10; 0, 1, 0, 0, 
+0, 0, 0, 0, 0, -45; 0, 0, 1, 0, 0, 0, 0, 0, 0, 120; 0, 0, 0, 1, 0, 0, 0, 0, 
+0, -210; 0, 0, 0, 0, 1, 0, 0, 0, 0, 252; 0, 0, 0, 0, 0, 1, 0, 0, 0, -210; 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 120; 0, 0, 0, 0, 0, 0, 0, 1, 0, -45; 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 10], [1, -7129/2520, 6515/2016, -4523/2268, 95/128, -3013/17280
+, 5/192, -29/12096, 1/8064, -1/362880; 0, 9, -4609/280, 14139/1120, -7667/14
+40, 7807/5760, -77/360, 59/2880, -11/10080, 1/40320; 0, -18, 5869/140, -2083
+7/560, 24901/1440, -6787/1440, 563/720, -7/90, 43/10080, -1/10080; 0, 28, -6
+289/90, 72569/1080, -4013/120, 13873/1440, -401/240, 31/180, -7/720, 1/4320;
+ 0, -63/2, 6499/80, -6519/80, 122249/2880, -36769/2880, 3313/1440, -353/1440
+, 41/2880, -1/2880; 0, 126/5, -265/4, 1091/16, -5273/144, 32773/2880, -305/1
+44, 67/288, -1/72, 1/2880; 0, -14, 6709/180, -84307/2160, 10279/480, -9823/1
+440, 313/240, -53/360, 13/1440, -1/4320; 0, 36/7, -967/70, 4101/280, -2939/3
+60, 3817/1440, -373/720, 151/2520, -19/5040, 1/10080; 0, -9/8, 3407/1120, -1
+823/560, 10579/5760, -3487/5760, 347/2880, -41/2880, 37/40320, -1/40320; 0, 
+1/9, -761/2520, 29531/90720, -89/480, 1069/17280, -1/80, 13/8640, -1/10080, 
+1/362880]]
+[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1; 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -11; 0, 1, 
+0, 0, 0, 0, 0, 0, 0, 0, 55; 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -165; 0, 0, 0, 1, 
+0, 0, 0, 0, 0, 0, 330; 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -462; 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 462; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -330; 0, 0, 0, 0, 0, 0, 0, 1
+, 0, 0, 165; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -55; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+, 11], [1, -7381/2520, 177133/50400, -84095/36288, 341693/362880, -8591/3456
+0, 7513/172800, -121/24192, 11/30240, -11/725760, 1/3628800; 0, 10, -4861/25
+2, 79913/5040, -663941/90720, 6041/2880, -6709/17280, 67/1440, -211/60480, 1
+/6720, -1/362880; 0, -45/2, 6121/112, -115923/2240, 264767/10080, -92771/115
+20, 18047/11520, -1123/5760, 607/40320, -53/80640, 1/80640; 0, 40, -6541/63,
+ 400579/3780, -433739/7560, 13349/720, -5419/1440, 349/720, -97/2520, 13/756
+0, -1/30240; 0, -105/2, 6751/48, -71689/480, 728587/8640, -163313/5760, 3434
+3/5760, -2281/2880, 31/480, -17/5760, 1/17280; 0, 252/5, -6877/50, 1197/8, -
+62549/720, 43319/1440, -93773/14400, 8/9, -107/1440, 1/288, -1/14400; 0, -35
+, 6961/72, -461789/4320, 273431/4320, -129067/5760, 28603/5760, -1999/2880, 
+19/320, -49/17280, 1/17280; 0, 120/7, -1003/21, 22439/420, -242639/7560, 832
+1/720, -1253/480, 1877/5040, -41/1260, 1/630, -1/30240; 0, -45/8, 3533/224, 
+-39867/2240, 435893/40320, -45449/11520, 10427/11520, -757/5760, 59/5040, -4
+7/80640, 1/80640; 0, 10/9, -263/84, 161353/45360, -197741/90720, 6947/8640, 
+-3229/17280, 119/4320, -151/60480, 23/181440, -1/362880; 0, -1/10, 7129/2520
+0, -1303/4032, 4523/22680, -19/256, 3013/172800, -1/384, 29/120960, -1/80640
+, 1/3628800]]
+[Mat(1), Mat(1)]
+[[0, -1/12; 1, 4/3], [1, -2; 0, 2]]
+[[0, 0, 1/2160; 1, 0, -127/720; 0, 1, 23/15], [1, -154/27, 50/9; 0, 42, -60;
+ 0, -80/3, 40]]
+[[0, 0, 0, -1/6048000; 1, 0, 0, 41/23625; 0, 1, 0, -3341/12600; 0, 0, 1, 176
+/105], [1, -1421/125, 27513/1000, -4487/250; 0, 279797/250, -522126/125, 416
+871/125; 0, -34704/5, 131814/5, -106344/5; 0, 20664/5, -78624/5, 63504/5]]
+[[0, 0, 0, 0, 1/266716800000; 1, 0, 0, 0, -61501/53343360000; 0, 1, 0, 0, 85
+2401/222264000; 0, 0, 1, 0, -735781/2116800; 0, 0, 0, 1, 563/315], [1, -4158
+38/21875, 63349974/765625, -19249072/153125, 9456222/153125; 0, 5784458/175,
+ -282462084/1225, 538108488/1225, -60617472/245; 0, -2580562224/875, 6365089
+50552/30625, -244100163456/6125, 138115393056/6125; 0, 15152256, -106838784,
+ 204933888, -115983360; 0, -42384384/5, 1494360576/25, -573308928/5, 3244769
+28/5]]
+[[0, 0, 0, 0, 0, -1/186313420339200000; 1, 0, 0, 0, 0, 3529/70573265280000; 
+0, 1, 0, 0, 0, -10828423/2688505344000; 0, 0, 1, 0, 0, 18344719/2750517000; 
+0, 0, 0, 1, 0, -14806217/34927200; 0, 0, 0, 0, 1, 6508/3465], [1, -113705672
+62/397065375, 51367426213/264710250, -200591528282/397065375, 26582711207/47
+647845, -698502310/3176523; 0, 26892406032994/26471025, -100223794140728/882
+3675, 1000761213157984/26471025, -154200190180144/3176523, 22461031505440/10
+58841; 0, -11956428602368/7203, 44866837991366/2401, -450093250332248/7203, 
+1739649716044700/21609, -254051753060600/7203; 0, 1265964151819744/12005, -1
+4255235800309184/12005, 47676508772317984/12005, -36859404825081920/7203, 53
+83325127369200/2401; 0, -22893136773120/49, 257789598088320/49, -86218471948
+0320/49, 1110955189536000/49, -486768934080000/49; 0, 12188747704320/49, -13
+7252461547520/49, 459045508331520/49, -591496874496000/49, 259166666880000/4
+9]]
+[[0, 0, 0, 0, 0, 0, 1/2067909047925770649600000; 1, 0, 0, 0, 0, 0, -8237351/
+59083115655022018560000; 0, 1, 0, 0, 0, 0, 509709971/1758426061161369600; 0,
+ 0, 1, 0, 0, 0, -126867217979/12687056718336000; 0, 0, 0, 1, 0, 0, 280412388
+5/276900047424; 0, 0, 0, 0, 1, 0, -197708437/399567168; 0, 0, 0, 0, 0, 1, 88
+069/45045], [1, -2105183226479/52298274672, 10204001676775/26149137336, -379
+5959134735/2490394032, 110012661902975/39223706004, -28250835545377/11621838
+816, 5977827829649/7471182096; 0, 694154228498195/21781872, -567779093797712
+5/10890936, 19203290942517085/7260624, -13361594883135815/2333772, 268719450
+14192725/4840416, -6176666319494645/3111696; 0, -48323938697783864125/444713
+22, 1588963999041773969375/88942644, -96306745655113424950/1058841, 26337479
+589176600409625/133413966, -473905474639530537050/2470629, 21821995843038607
+4825/3176523; 0, 377327226220357400/343, -6204414565597298000/343, 315912214
+44969069000/343, -29387968974963236000/147, 66632096787393901500/343, -34092
+94728736153400/49; 0, -903376448781904680000/16807, 14854370951794001100000/
+16807, -10804977525060138816000/2401, 164173623814473593240000/16807, -15952
+9759706768415120000/16807, 8162510430881545440000/2401; 0, 14866788313713600
+00/7, -24445722399278400000/7, 124471715219042400000/7, -3859706030495040000
+0, 262537102163437200000/7, -13432993354329120000; 0, -37259471250144000000/
+343, 612664107014044800000/343, -445648264430054400000/49, 67712971287424320
+00000/343, -6579763803550080000000/343, 336660704216179200000/49]]
+[[0, 0, 0, 0, 0, 0, 0, -1/365356847125734485878112256000000; 1, 0, 0, 0, 0, 
+0, 0, 442037/17839689801061254193267200000; 0, 1, 0, 0, 0, 0, 0, -1258152740
+869/906142974022158943150080000; 0, 0, 1, 0, 0, 0, 0, 725120398661/668862621
+366980040000; 0, 0, 0, 1, 0, 0, 0, -3571285252517/176488178798131200; 0, 0, 
+0, 0, 1, 0, 0, 85934726089/6093243231075; 0, 0, 0, 0, 0, 1, 0, -6070382321/1
+0821610800; 0, 0, 0, 0, 0, 0, 1, 91072/45045], [1, -42500337230198189/789037
+335136512, 1485561263561785385/2104099560364032, -2464043972606130965/642919
+310111232, 718262854403191163375/69435285492013056, -56581509496159888799/38
+57515860667392, 9878887530104754629/944697761796096, -149549670813037235/506
+08808667648; 0, 1597795988934645006308365/1578074670273024, -299836399161675
+7631524325/131506222522752, 52351244773959107312090215/321459655055616, -566
+606430091427159033910455/1084926335812704, 402649158744516505072233775/48218
+9482583424, -2401721270189620785803345/3690225632016, 3116622818176389995970
+25/1581525270864; 0, -810133477962910968828875/1035416844, 16278035944517515
+917941125/920370528, -961904309558071374658162075/7593056856, 45797400660388
+5374127196125/1124897312, -9902444332568340060689566775/15186113712, 2103043
+69107054866754822725/413227584, -3415235346455446300557875/22137192; 0, 4319
+362769093923379127587825/295833384, -8137191631206765936749175625/24652782, 
+47493879724607616186085748625/20087452, -3090987614443956021375650065250/406
+770903, 1100195852610318620167150387625/90393534, -2628709950385259668939964
+2900/2767149, 3415205806974130682543927000/1185921; 0, -54848998853756143562
+10000/539, 123995581243617591167763750/539, -9770214869603157247861659000/59
+29, 31400712562043027405220098750/5929, -50295067308334909983004777500/5929,
+ 801137981304080453838309375/121, -242861544697294090465625000/121; 0, 21815
+8654361365089166145000/539, -4931851901883773788541100000/539, 3886046471450
+08366217993250000/5929, -1248945312265946216389327600000/5929, 2000457585111
+261976697403900000/5929, -31864807885065137436311520000/121, 965968044382624
+9833287200000/121; 0, -1458806909788672329600000, 32978841764920871648400000
+, -236233063370670130814400000, 759234820879813379166000000, -12160797209842
+05624924000000, 949161438034641498303000000, -287734238270502040180800000; 0
+, 721538527935067128000000, -16311620707236185760000000, 1168429189411405078
+56000000, -375524117964065416320000000, 601483562848427369760000000, -469463
+468662526817024000000, 142315846641335937024000000]]
+[0, x, x]
+[0, x^2 + x, 1]
+[0, 0]
+[[1, 0, 0; 0, -2, 1], [;], [;]]
+[]
+[matrix(0,1), [1, 0; 0, -2; 0, 1], matrix(0,2)]
+[Mod(1, 3)*x^4 + Mod(2, 3)*x + Mod(2, 3), 1, 1]
+Total time spent: 292
diff --git a/src/test/32/member b/src/test/32/member
new file mode 100644
index 0000000..6e839ae
--- /dev/null
+++ b/src/test/32/member
@@ -0,0 +1,319 @@
+.pol: x
+.a1: 0
+.a2: 0
+.a3: 0
+.a4: 0
+.a6: 0
+NF
+.codiff: [1, 553/1105; 0, 1/1105]
+.diff: [1105, 553; 0, 1]
+.disc: 1105
+.index: 2
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.pol: y^2 - 1105
+.r1: 2
+.r2: 0
+.roots: [-33.241540277189322804630581542105218897, 33.2415402771893228046305
+81542105218897]
+.sign: [2, 0]
+.t2: [2, -1.0000000000000000000000000000000000000; -1.0000000000000000000000
+000000000000000, 553.00000000000000000000000000000000000]
+.zk: [1, 1/2*y - 1/2]
+NF chvar
+  *** nfinit: Warning: non-monic polynomial. Result of the form [nf,c].
+.codiff: [1/2, 0; 0, 1/4]
+.diff: [4, 0; 0, 2]
+.disc: -8
+.index: 1
+.nf: [y^2 + 2, [0, 1], -8, 1, [Mat([1, 0.E-38 + 1.41421356237309504880168872
+42096980786*I]), [1, 1.4142135623730950488016887242096980786; 1, -1.41421356
+23730950488016887242096980786], [1, 1; 1, -1], [2, 0; 0, -4], [4, 0; 0, 2], 
+[2, 0; 0, -1], [2, [0, -2; 1, 0]], [2]], [0.E-38 + 1.41421356237309504880168
+87242096980786*I], [1, y], [1, 0; 0, 1], [1, 0, 0, -2; 0, 1, 1, 0]]
+.pol: y^2 + 2
+.r1: 0
+.r2: 1
+.roots: [0.E-38 + 1.4142135623730950488016887242096980786*I]
+.sign: [0, 1]
+.t2: [2, 0.E-38; 0.E-38, 4.0000000000000000000000000000000000000]
+.zk: [1, y]
+BNF
+.bnf: [[2, 0; 0, 2], [1, 1, 0; 1, 0, 1], [10.9503854058256053302677508250179
+37393 + 3.1415926535897932384626433832795028842*I; -10.950385405825605330267
+750825017937393 + 6.2831853071795864769252867665590057684*I], [-2.8070134016
+636593080928506577483570863 + 6.2831853071795864769252867665590057684*I, -6.
+4656286076812397829259659980344686073 + 6.2831853071795864769252867665590057
+684*I, 6.3140644011531557847583424971265245465 + 4.70197740 E-38*I, 0, 0; 2.
+8070134016636593080928506577483570863 + 3.1415926535897932384626433832795028
+842*I, 6.4656286076812397829259659980344686073 + 3.1415926535897932384626433
+832795028842*I, -6.3140644011531557847583424971265245465 + 7.05296610 E-38*I
+, 0, 0], [[2, [-1, 1]~, 1, 1, [0, 276; 1, -1]], [3, [0, 2]~, 1, 1, [-1, -276
+; -1, 0]], [5, [1, 2]~, 2, 1, [1, 552; 2, -1]], [2, [2, 1]~, 1, 1, [1, 276; 
+1, 0]], [3, [2, 2]~, 1, 1, [0, -276; -1, 1]]], 0, [y^2 - 1105, [2, 0], 1105,
+ 2, [[1, -17.120770138594661402315290771052609448; 1, 16.1207701385946614023
+15290771052609448], [1, -17.120770138594661402315290771052609448; 1, 16.1207
+70138594661402315290771052609448], [1, -17; 1, 16], [2, -1; -1, 553], [1105,
+ 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276; 1, 552]], [5, 13, 17]], [-33.
+241540277189322804630581542105218897, 33.24154027718932280463058154210521889
+7], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0, 276; 0, 1, 1, -1]], [[4, [2, 2
+], [[2, 0; 0, 1], [3, 1; 0, 1]]], 10.950385405825605330267750825017937393, 1
+, [2, -1], [857*y - 28488]], [[-1, 0; 0, -1], [[0, 0], [0, 0]], [[2.80701340
+16636593080928506577483570863 - 6.2831853071795864769252867665590057684*I, -
+2.8070134016636593080928506577483570863 - 3.14159265358979323846264338327950
+28842*I], [6.4656286076812397829259659980344686073 - 6.283185307179586476925
+2867665590057684*I, -6.4656286076812397829259659980344686073 - 3.14159265358
+97932384626433832795028842*I]]], [0, 0]]
+.clgp: [4, [2, 2], [[2, 0; 0, 1], [3, 1; 0, 1]]]
+.codiff: [1, 553/1105; 0, 1/1105]
+.cyc: [2, 2]
+.diff: [1105, 553; 0, 1]
+.disc: 1105
+.fu: [Mod(857*y - 28488, y^2 - 1105)]
+.gen: [[2, 0; 0, 1], [3, 1; 0, 1]]
+.index: 2
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.no: 4
+.pol: y^2 - 1105
+.r1: 2
+.r2: 0
+.reg: 10.950385405825605330267750825017937393
+.roots: [-33.241540277189322804630581542105218897, 33.2415402771893228046305
+81542105218897]
+.sign: [2, 0]
+.t2: [2, -1.0000000000000000000000000000000000000; -1.0000000000000000000000
+000000000000000, 553.00000000000000000000000000000000000]
+.tu: [2, Mod(-1, y^2 - 1105)]
+.zk: [1, 1/2*y - 1/2]
+BNR
+.bid: [[[4, 0; 0, 4], [0, 0]], [4, [2, 2], [[1, -2]~, [-1, -2]~]], [[2, [-1,
+ 1]~, 1, 1, [0, 276; 1, -1]], 2; [2, [2, 1]~, 1, 1, [1, 276; 1, 0]], 2], [[[
+[1], [1], [[1, 0]~], [Vecsmall([])], 1], [[2], [-1], [[1, -2]~], [Vecsmall([
+])], Mat([1/2, -1/2])]], [[[1], [1], [[1, 0]~], [Vecsmall([])], 1], [[2], [-
+1], [[-1, -2]~], [Vecsmall([])], Mat([1/2, 0])]], [[], [], []]], [0, 1, 0, 0
+; 0, 0, 0, 1]]
+.bnf: [[2, 0; 0, 2], [1, 1, 0; 1, 0, 1], [10.9503854058256053302677508250179
+37393 + 3.1415926535897932384626433832795028842*I; -10.950385405825605330267
+750825017937393 + 6.2831853071795864769252867665590057684*I], [-2.8070134016
+636593080928506577483570863 + 6.2831853071795864769252867665590057684*I, -6.
+4656286076812397829259659980344686073 + 6.2831853071795864769252867665590057
+684*I, 6.3140644011531557847583424971265245465 + 4.70197740 E-38*I, 0, 0; 2.
+8070134016636593080928506577483570863 + 3.1415926535897932384626433832795028
+842*I, 6.4656286076812397829259659980344686073 + 3.1415926535897932384626433
+832795028842*I, -6.3140644011531557847583424971265245465 + 7.05296610 E-38*I
+, 0, 0], [[2, [-1, 1]~, 1, 1, [0, 276; 1, -1]], [3, [0, 2]~, 1, 1, [-1, -276
+; -1, 0]], [5, [1, 2]~, 2, 1, [1, 552; 2, -1]], [2, [2, 1]~, 1, 1, [1, 276; 
+1, 0]], [3, [2, 2]~, 1, 1, [0, -276; -1, 1]]], 0, [y^2 - 1105, [2, 0], 1105,
+ 2, [[1, -17.120770138594661402315290771052609448; 1, 16.1207701385946614023
+15290771052609448], [1, -17.120770138594661402315290771052609448; 1, 16.1207
+70138594661402315290771052609448], [1, -17; 1, 16], [2, -1; -1, 553], [1105,
+ 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276; 1, 552]], [5, 13, 17]], [-33.
+241540277189322804630581542105218897, 33.24154027718932280463058154210521889
+7], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0, 276; 0, 1, 1, -1]], [[4, [2, 2
+], [[2, 0; 0, 1], [3, 1; 0, 1]]], 10.950385405825605330267750825017937393, 1
+, [2, -1], [857*y - 28488]], [[-1, 0; 0, -1], [[0, 0], [0, 0]], [[2.80701340
+16636593080928506577483570863 - 6.2831853071795864769252867665590057684*I, -
+2.8070134016636593080928506577483570863 - 3.14159265358979323846264338327950
+28842*I], [6.4656286076812397829259659980344686073 - 6.283185307179586476925
+2867665590057684*I, -6.4656286076812397829259659980344686073 - 3.14159265358
+97932384626433832795028842*I]]], [0, [Mat([[16, -1]~, 1]), Mat([[-137, -8]~,
+ 1])]]]
+.clgp: [4, [2, 2]]
+.codiff: [1, 553/1105; 0, 1/1105]
+.cyc: [2, 2]
+.diff: [1105, 553; 0, 1]
+.disc: 1105
+.index: 2
+.mod: [[4, 0; 0, 4], [0, 0]]
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.no: 4
+.pol: y^2 - 1105
+.r1: 2
+.r2: 0
+.roots: [-33.241540277189322804630581542105218897, 33.2415402771893228046305
+81542105218897]
+.sign: [2, 0]
+.t2: [2, -1.0000000000000000000000000000000000000; -1.0000000000000000000000
+000000000000000, 553.00000000000000000000000000000000000]
+.zk: [1, 1/2*y - 1/2]
+.zkst: [4, [2, 2], [[1, -2]~, [-1, -2]~]]
+RNF
+.disc: [[4420, 553; 0, 1], [1, 2]~]
+.index: [2, 0; 0, 1]
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.pol: x^2 - y
+.polabs: x^4 - 1105
+.zk: [[1, x - 1], [1, [1, 1/2; 0, 1/2]]]
+QUADCLASSUNIT
+.clgp: [4, [2, 2], [Qfb(2, 31, -18, 0.E-48), Qfb(3, 29, -22, 0.E-48)]]
+.cyc: [2, 2]
+.gen: [Qfb(2, 31, -18, 0.E-48), Qfb(3, 29, -22, 0.E-48)]
+.no: 4
+.reg: 10.950385405825605330267750825017937393
+GAL
+.gen: [Vecsmall([2, 1])]
+.group: [Vecsmall([1, 2]), Vecsmall([2, 1])]
+.mod: 96889010407
+.orders: Vecsmall([2])
+.p: 7
+.pol: x^2 - 2
+.roots: [19757775943, 77131234464]~
+ELL
+.a1: 1
+.a2: 2
+.a3: 3
+.a4: 4
+.a6: 5
+.b2: 9
+.b4: 11
+.b6: 29
+.b8: 35
+.c4: -183
+.c6: -3429
+.area: 2.9719152678179096707716479509361896060
+.disc: -10351
+.eta: [3.1096482423243803285501491221965830079, 1.55482412116219016427507456
+10982915039 + 1.0643747452102737569438859937299427442*I]
+.gen: [[1, 2]]
+.j: 6128487/10351
+.omega: [2.7807400137667297710631976271813584994, 1.390370006883364885531598
+8135906792497 - 1.0687497763561930661592635474375038788*I]
+.roots: [-1.6189099322673713423780009396072169751, -0.3155450338663143288109
+9953019639151248 + 2.0925470969119586079816894466366945829*I, -0.31554503386
+631432881099953019639151248 - 2.0925470969119586079816894466366945829*I]~
+ELLFp
+.a1: Mod(1, 13)
+.a2: Mod(2, 13)
+.a3: Mod(3, 13)
+.a4: Mod(4, 13)
+.a6: Mod(5, 13)
+.b2: Mod(9, 13)
+.b4: Mod(11, 13)
+.b6: Mod(3, 13)
+.b8: Mod(9, 13)
+.c4: Mod(12, 13)
+.c6: Mod(3, 13)
+.cyc: [13]
+.disc: Mod(10, 13)
+.gen: [[Mod(12, 13), Mod(3, 13)]]
+.group: [13, [13], [[Mod(12, 13), Mod(3, 13)]]]
+.j: Mod(9, 13)
+.no: 13
+.p: 13
+ELLFq
+.a1: 1
+.a2: 2
+.a3: 3
+.a4: 4
+.a6: 5
+.b2: 9
+.b4: 11
+.b6: 3
+.b8: 9
+.c4: 12
+.c6: 3
+.cyc: [195]
+.disc: 10
+.gen: [[9*x + 8, 4*x + 2]]
+.group: [195, [195], [[9*x + 8, 4*x + 2]]]
+.j: 9
+.no: 195
+.p: 13
+ELLQp
+.a1: 1
+.a2: 2
+.a3: 3
+.a4: 4
+.a6: 5
+.b2: 9
+.b4: 11
+.b6: 29
+.b8: 35
+.c4: -183
+.c6: -3429
+.disc: -10351
+.j: 6128487/10351
+.p: 11
+.roots: [9 + O(11^2)]~
+.tate: [6 + 8*11 + 5*11^2 + O(11^4), Mod(u, u^2 + (5 + 2*11 + 5*11^2 + 10*11
+^3 + O(11^4))), 3*11 + 7*11^2 + O(11^4), [6 + 3*11 + O(11^4), 6 + 11 + 9*11^
+2 + 11^3 + O(11^4)]]
+FFELT
+.f: 3
+.mod: x^3 + x^2 + 1
+.p: 2
+.pol: x
+.f: 3
+.mod: x^3 + x^2 + x + 2
+.p: 3
+.pol: x
+.f: 2
+.mod: x^2 + x + 1
+.p: 18446744073709551629
+.pol: x
+INTMOD
+.mod: 3
+POLMOD
+.mod: x^2 + 1
+.pol: x
+QFB
+QUAD
+.disc: -4
+.fu: []
+.mod: x^2 + 1
+.pol: x^2 + 1
+.tu: [4, w]
+.zk: [1, x]
+PRID
+.e: 1
+.f: 1
+.gen: [2, [-1, 1]~]
+.p: 2
+MODPR
+.e: 1
+.f: 1
+.gen: [2, [-1, 1]~]
+.p: 2
+BID
+.bid: [[[4, 1; 0, 1], [0, 0]], [2, [2], [3]], Mat([[2, [-1, 1]~, 1, 1, [0, 2
+76; 1, -1]], 2]), [[[[1], [1], [1], [Vecsmall([])], 1], [[2], [-1], [-1], [V
+ecsmall([])], Mat([1/2, -1/2])]], [[], [], []]], Mat([0, 1])]
+.clgp: [2, [2], [3]]
+.cyc: [2]
+.gen: [3]
+.mod: [[4, 1; 0, 1], [0, 0]]
+.no: 2
+.zkst: [2, [2], [3]]
+BID (nogen)
+.bid: [[[4, 1; 0, 1], [0, 0]], [2, [2]], Mat([[2, [-1, 1]~, 1, 1, [0, 276; 1
+, -1]], 2]), [[[[1], [1], [1], [Vecsmall([])], 1], [[2], [-1], [-1], [Vecsma
+ll([])], Mat([1/2, -1/2])]], [[], [], []]], Mat([0, 1])]
+.clgp: [2, [2]]
+.cyc: [2]
+.mod: [[4, 1; 0, 1], [0, 0]]
+.no: 2
+.zkst: [2, [2]]
+Total time spent: 48
diff --git a/src/test/32/minim b/src/test/32/minim
new file mode 100644
index 0000000..492c2b9
--- /dev/null
+++ b/src/test/32/minim
@@ -0,0 +1,5 @@
+1
+1
+78
+77
+Total time spent: 0
diff --git a/src/test/32/minmax b/src/test/32/minmax
new file mode 100644
index 0000000..f8d2246
--- /dev/null
+++ b/src/test/32/minmax
@@ -0,0 +1,5 @@
+1: [1, 1, 0, 0]
+2: [11, -3, 1, 4]
+3: [11, -3, 1, 4]
+4: [11, -3, [1, 1], [2, 2]]
+Total time spent: 0
diff --git a/src/test/32/modfun b/src/test/32/modfun
new file mode 100644
index 0000000..704b6bf
--- /dev/null
+++ b/src/test/32/modfun
@@ -0,0 +1,9 @@
+1 + 2 + 2^3 + 2^4 + 2^7 + 2^12 + 2^13 + 2^14 + 2^16 + 2^17 + 2^18 + 2^19 + O
+(2^20)
+1 - x - 2*x^2 - 3*x^3 - 4*x^4 + O(x^5)
+0.99812906992595851327996232224527387813
+2^-1 + 2^5 + 2^7 + 2^8 + 2^10 + 2^12 + 2^16 + O(2^18)
+x^-1 + 743 + 196884*x + 21690644*x^2 + O(x^3)
+0.E-38 - 0.50432357748832834893222560519660217759*I
+1.0905077326652576592070106557607079790
+Total time spent: 0
diff --git a/src/test/32/modpr b/src/test/32/modpr
new file mode 100644
index 0000000..45f9f98
--- /dev/null
+++ b/src/test/32/modpr
@@ -0,0 +1,363 @@
+  ***   at top-level: nfeltmulmodpr(nfinit
+  ***                 ^--------------------
+  *** nfeltmulmodpr: incorrect type in checkprid (t_INT).
+(P)->my(f);print(K.pol,": ",P);f=[nfeltdivmodpr,nfeltmulmodpr];for(i=1,#v,fo
+r(j=i,#v,print("*",[i,j],":");for(k=1,#f,print(iferr(f[k](K,v[i],v[j],P),E,E
+)))));f=[(K,x,P)->nfeltpowmodpr(K,x,-3,P),nfeltreducemodpr];for(i=1,#v,print
+("*",i,":");for(k=1,#f,print(iferr(f[k](K,v[i],P),E,E))))
+y^2 + 1: [[1, 0]~, [1, 1], [2, [1, 1]~, 2, 1, [1, -1; 1, 1]]]~
+*[1, 1]:
+error("impossible inverse in dvmdii: 0.")
+[0, 0]~
+*[1, 2]:
+[0, 0]~
+[0, 0]~
+*[1, 3]:
+[0, 0]~
+[0, 0]~
+*[1, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[1, 5]:
+[0, 0]~
+[0, 0]~
+*[1, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[2, 2]:
+[1, 0]~
+[1, 0]~
+*[2, 3]:
+[1, 0]~
+[1, 0]~
+*[2, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[2, 5]:
+error("impossible inverse in Rg_to_ff: Mod(0, 2).")
+[0, 0]~
+*[2, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[3, 3]:
+[1, 0]~
+[1, 0]~
+*[3, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[3, 5]:
+error("impossible inverse in Rg_to_ff: Mod(0, 2).")
+[0, 0]~
+*[3, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[4, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 5]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("inconsistent variables in poltobasis, z != y.")
+*[5, 5]:
+[1, 0]~
+[0, 0]~
+*[5, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[6, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*1:
+error("impossible inverse in Fl_inv: Mod(0, 2).")
+[0, 0]~
+*2:
+[1, 0]~
+[1, 0]~
+*3:
+[1, 0]~
+[1, 0]~
+*4:
+error("inconsistent variables in poltobasis, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*5:
+error("impossible inverse in Fl_inv: Mod(0, 2).")
+[0, 0]~
+*6:
+error("incorrect type in Rg_to_ff (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+y^2 + 1: [[1, 0]~, [1, 0; 0, 1], [3, [3, 0]~, 1, 2, 1], y^2 + 1]~
+*[1, 1]:
+error("impossible inverse in dvmdii: 0.")
+[0, 0]~
+*[1, 2]:
+[0, 0]~
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[1, 3]:
+[0, 0]~
+[0, 0]~
+*[1, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[1, 5]:
+[0, 0]~
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*[1, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[2, 2]:
+[1, 0]~
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 3]:
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 5]:
+[0, 2]~
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[3, 3]:
+[1, 0]~
+[2, 0]~
+*[3, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[3, 5]:
+[0, 0]~
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*[3, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[4, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 5]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("inconsistent variables in poltobasis, z != y.")
+*[5, 5]:
+[1, 0]~
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*[5, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*[6, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*1:
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+[0, 0]~
+*2:
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*3:
+[0, 1]~
+[0, 1]~
+*4:
+error("inconsistent variables in poltobasis, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*5:
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*6:
+error("incorrect type in Rg_to_ff (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+y^3 - 9: [[1, 0, 0]~, [1, 0, 0], [3, [0, 1, 0]~, 3, 1, [0, 3, 0; 0, 0, 3; 1,
+ 0, 0]]]~
+*[1, 1]:
+error("impossible inverse in dvmdii: 0.")
+[0, 0, 0]~
+*[1, 2]:
+[0, 0, 0]~
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[1, 3]:
+[0, 0, 0]~
+[0, 0, 0]~
+*[1, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[1, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[1, 6]:
+[0, 0, 0]~
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*[2, 2]:
+[1, 0, 0]~
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 3]:
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[2, 6]:
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*[3, 3]:
+[1, 0, 0]~
+[0, 0, 0]~
+*[3, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[3, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[3, 6]:
+[0, 0, 0]~
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*[4, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 6]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[5, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[5, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[6, 6]:
+[1, 0, 0]~
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+*1:
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+[0, 0, 0]~
+*2:
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+*3:
+error("impossible inverse in Fl_inv: Mod(0, 3).")
+[0, 0, 0]~
+*4:
+error("inconsistent variables in poltobasis, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*5:
+error("incorrect type in Rg_to_ff (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*6:
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+error("impossible inverse in Rg_to_ff: Mod(0, 3).")
+y^3 - 9: [[2, 1, 1]~, [1, 1, 0; 0, 1, 1], [2, [3, 3, 1]~, 1, 2, [1, 3, 0; 0,
+ 1, 3; 1, 0, 1]], y^2 + y + 1]~
+*[1, 1]:
+error("impossible inverse in dvmdii: 0.")
+[0, 0, 0]~
+*[1, 2]:
+[0, 0, 0]~
+[0, 0, 0]~
+*[1, 3]:
+[0, 0, 0]~
+[0, 0, 0]~
+*[1, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[1, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[1, 6]:
+[0, 0, 0]~
+[0, 0, 0]~
+*[2, 2]:
+[1, 0, 0]~
+[1, 0, 0]~
+*[2, 3]:
+[1, 0, 1]~
+[0, 0, 1]~
+*[2, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[2, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[2, 6]:
+error("impossible inverse in Rg_to_ff: Mod(0, 2).")
+[0, 0, 0]~
+*[3, 3]:
+[1, 0, 0]~
+[1, 0, 1]~
+*[3, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[3, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[3, 6]:
+error("impossible inverse in Rg_to_ff: Mod(0, 2).")
+[0, 0, 0]~
+*[4, 4]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("inconsistent variables in poltobasis, z != y.")
+*[4, 6]:
+error("inconsistent variables in nf_to_scalar_or_alg, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*[5, 5]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[5, 6]:
+error("incorrect type in nf_to_scalar_or_alg (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*[6, 6]:
+[1, 0, 0]~
+[0, 0, 0]~
+*1:
+error("impossible inverse in Fl_inv: Mod(0, 2).")
+[0, 0, 0]~
+*2:
+[1, 0, 0]~
+[1, 0, 0]~
+*3:
+[1, 0, 0]~
+[0, 0, 1]~
+*4:
+error("inconsistent variables in poltobasis, z != y.")
+error("inconsistent variables in poltobasis, z != y.")
+*5:
+error("incorrect type in Rg_to_ff (t_COL).")
+error("incorrect type in Rg_to_ff (t_COL).")
+*6:
+error("impossible inverse in Flxq_inv: 0.")
+[0, 0, 0]~
+[1, 0]~
+
+[x + 1 3]
+
+[1, y]~
+[;]
+[0, 1]~
+
+[  y y^2]
+
+[y^2 y^3]
+
+
+[1]
+
+[1]
+
+  ***   at top-level: nfsolvemodpr(K,m,v,P
+  ***                 ^--------------------
+  *** nfsolvemodpr: impossible inverse in nfsolvemodpr: [1, 1; 1, 1].
+  ***   at top-level: nfsolvemodpr(K,m,m,P
+  ***                 ^--------------------
+  *** nfsolvemodpr: impossible inverse in nfsolvemodpr: [1, 1; 1, 1].
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+0, 0, 0, 0, 0, 0, 0]~
+Total time spent: 244
diff --git a/src/test/32/modular b/src/test/32/modular
new file mode 100644
index 0000000..7c06e27
--- /dev/null
+++ b/src/test/32/modular
@@ -0,0 +1,68 @@
+1: Mod(1, 2)*x^8 + Mod(1, 2)*x^6 + Mod(1, 2)*x^4 + Mod(1, 2)*x^2 + Mod(1, 2)
+2: Mod(1, 3)*x^8 + Mod(1, 3)*x^6 + Mod(1, 3)*x^4 + Mod(1, 3)*x^2 + Mod(1, 3)
+3: Mod(1, 4294967295)*x^8 + Mod(1, 4294967295)*x^6 + Mod(1, 4294967295)*x^4 
++ Mod(1, 4294967295)*x^2 + Mod(1, 4294967295)
+4: Mod(1, 18446744073709551615)*x^8 + Mod(1, 18446744073709551615)*x^6 + Mod
+(1, 18446744073709551615)*x^4 + Mod(1, 18446744073709551615)*x^2 + Mod(1, 18
+446744073709551615)
+5: Mod(1, 100000000000000000000)*x^8 + Mod(1, 100000000000000000000)*x^6 + M
+od(1, 100000000000000000000)*x^4 + Mod(1, 100000000000000000000)*x^2 + Mod(1
+, 100000000000000000000)
+1: Mod(1, 2)*x^8 + Mod(1, 2)*x^6 + Mod(1, 2)*x^4 + Mod(1, 2)*x^2 + Mod(1, 2)
+2: Mod(1, 3)*x^8 + Mod(1, 3)*x^7 + Mod(2, 3)*x^5 + Mod(2, 3)*x^4 + Mod(2, 3)
+*x^3 + Mod(1, 3)*x + Mod(1, 3)
+3: Mod(1, 4294967295)*x^8 + Mod(4294967293, 4294967295)*x^7 + Mod(3, 4294967
+295)*x^6 + Mod(4294967291, 4294967295)*x^5 + Mod(5, 4294967295)*x^4 + Mod(42
+94967291, 4294967295)*x^3 + Mod(3, 4294967295)*x^2 + Mod(4294967293, 4294967
+295)*x + Mod(1, 4294967295)
+4: Mod(1, 18446744073709551615)*x^8 + Mod(18446744073709551613, 184467440737
+09551615)*x^7 + Mod(3, 18446744073709551615)*x^6 + Mod(18446744073709551611,
+ 18446744073709551615)*x^5 + Mod(5, 18446744073709551615)*x^4 + Mod(18446744
+073709551611, 18446744073709551615)*x^3 + Mod(3, 18446744073709551615)*x^2 +
+ Mod(18446744073709551613, 18446744073709551615)*x + Mod(1, 1844674407370955
+1615)
+5: Mod(1, 100000000000000000000)*x^8 + Mod(99999999999999999998, 10000000000
+0000000000)*x^7 + Mod(3, 100000000000000000000)*x^6 + Mod(999999999999999999
+96, 100000000000000000000)*x^5 + Mod(5, 100000000000000000000)*x^4 + Mod(999
+99999999999999996, 100000000000000000000)*x^3 + Mod(3, 100000000000000000000
+)*x^2 + Mod(99999999999999999998, 100000000000000000000)*x + Mod(1, 10000000
+0000000000000)
+1: [Mod(1, 2), Mod(0, 2); Mod(1, 2), Mod(0, 2)]
+2: [Mod(0, 3), Mod(0, 3); Mod(2, 3), Mod(2, 3)]
+3: [Mod(4294967286, 4294967295), Mod(6, 4294967295); Mod(4294967276, 4294967
+295), Mod(14, 4294967295)]
+4: [Mod(18446744073709551606, 18446744073709551615), Mod(6, 1844674407370955
+1615); Mod(18446744073709551596, 18446744073709551615), Mod(14, 184467440737
+09551615)]
+5: [Mod(99999999999999999991, 100000000000000000000), Mod(6, 100000000000000
+000000); Mod(99999999999999999981, 100000000000000000000), Mod(14, 100000000
+000000000000)]
+1: [Mod(1, 2), Mod(0, 2); Mod(1, 2), Mod(0, 2)]
+2: [Mod(1, 3), Mod(1, 3); Mod(0, 3), Mod(1, 3)]
+3: [Mod(7, 4294967295), Mod(10, 4294967295); Mod(15, 4294967295), Mod(22, 42
+94967295)]
+4: [Mod(7, 18446744073709551615), Mod(10, 18446744073709551615); Mod(15, 184
+46744073709551615), Mod(22, 18446744073709551615)]
+5: [Mod(7, 100000000000000000000), Mod(10, 100000000000000000000); Mod(15, 1
+00000000000000000000), Mod(22, 100000000000000000000)]
+Mod(Mod(1, y), x)
+Mod(Mod(1, y), x)
+error("forbidden division t_INT % t_STR.")
+error("impossible inverse in %: 0.")
+error("impossible inverse in %: 0.")
+error("inconsistent division t_SER % t_POL.")
+Mod(x, x^2)
+Mod(x, x^2)
+Mod(x, x^2)
+Mod(1, 3)
+Mod(-x, x^2 + 1)
+[Mod(1, 2), Mod(0, 2)]
+Mod(1, 2)*x
+Mod(1, y)*x
+Mod(0, 2)
+Mod(0, 2)
+  *** _^_: Warning: Mod(a,b)^n with n >> b : wasteful.
+Mod(2, 7)
+  *** _^_: Warning: Mod(a,b)^n with n >> b : wasteful.
+Mod(4, 7)
+Total time spent: 4
diff --git a/src/test/32/multiif b/src/test/32/multiif
new file mode 100644
index 0000000..cba8d47
--- /dev/null
+++ b/src/test/32/multiif
@@ -0,0 +1,21 @@
+1
+1
+1
+1
+1
+1
+default
+2
+2
+2
+2
+default
+default
+3
+3
+default
+default
+default
+1
+2
+Total time spent: 4
diff --git a/src/test/32/multivar-mul b/src/test/32/multivar-mul
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/32/nf b/src/test/32/nf
new file mode 100644
index 0000000..c9036cc
--- /dev/null
+++ b/src/test/32/nf
@@ -0,0 +1,362 @@
+   realprecision = 38 significant digits
+[85997496, [42998748, 2], [[408188227, 99620635; 0, 1], [2, 1; 0, 1]]]
+12.340047278667903334059769086970462220
+4.1894250945222025884896456921310573068
+20915648110955829231381594293324156411897455346679838307589120000
+571459344155975480004612560667633185714077696
+[54898, [54898], [[1195, 78; 0, 1]]]
+[26, [26], [[19, 14, 5, 14; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]]]
+[1, [], []]
+  *** zetakinit: Warning: non-monic polynomial. Result of the form [nf,c].
+  *** zetakinit: Warning: non-monic polynomial. Change of variables discarded.
+20/3
+-5
+13
+0
+5/2
+-1
+1024/243
+-1
+31
+-11
+1/32
+15853839
+1736217747
+Mod(4/3, y^5 - 4*y^3 + 2*y + 11)
+Mod(-1, y^5 - 4*y^3 + 2*y + 11)
+Mod(y^2 + y + 1, y^5 - 4*y^3 + 2*y + 11)
+Mod(y, y^5 - 4*y^3 + 2*y + 11)
+Mod(1/2, y^5 - 4*y^3 + 2*y + 11)
+Mod(5*y^4 + 4*y^3 - 12*y^2 - y - 5, y^5 - 4*y^3 + 2*y + 11)
+[4/3, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[3, 1, 1, 0, 0]~
+[0, 1, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1, 2, 3, 4, 5]~
+(f)->for(i=1,#v,for(j=1,#v,print(f(nf,v[i],v[j]))))
+[8/3, 0, 0, 0, 0]~
+[1/3, 0, 0, 0, 0]~
+[13/3, 1, 1, 0, 0]~
+[4/3, 1, 0, 0, 0]~
+[11/6, 0, 0, 0, 0]~
+[7/3, 2, 3, 4, 5]~
+[1/3, 0, 0, 0, 0]~
+[-2, 0, 0, 0, 0]~
+[2, 1, 1, 0, 0]~
+[-1, 1, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[0, 2, 3, 4, 5]~
+[13/3, 1, 1, 0, 0]~
+[2, 1, 1, 0, 0]~
+[6, 2, 2, 0, 0]~
+[3, 2, 1, 0, 0]~
+[7/2, 1, 1, 0, 0]~
+[4, 3, 4, 4, 5]~
+[4/3, 1, 0, 0, 0]~
+[-1, 1, 0, 0, 0]~
+[3, 2, 1, 0, 0]~
+[0, 2, 0, 0, 0]~
+[1/2, 1, 0, 0, 0]~
+[1, 3, 3, 4, 5]~
+[11/6, 0, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[7/2, 1, 1, 0, 0]~
+[1/2, 1, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[3/2, 2, 3, 4, 5]~
+[7/3, 2, 3, 4, 5]~
+[0, 2, 3, 4, 5]~
+[4, 3, 4, 4, 5]~
+[1, 3, 3, 4, 5]~
+[3/2, 2, 3, 4, 5]~
+[2, 4, 6, 8, 10]~
+[1, 0, 0, 0, 0]~
+[-4/3, 0, 0, 0, 0]~
+[-64/93, 16/31, 16/93, -8/31, 4/93]~
+[0, 4/33, 4/33, 0, -4/33]~
+[8/3, 0, 0, 0, 0]~
+[1587988/47561517, -165136/5284613, 41300/5284613, -212540/47561517, -78712/
+47561517]~
+[-3/4, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[16/31, -12/31, -4/31, 6/31, -1/31]~
+[0, -1/11, -1/11, 0, 1/11]~
+[-2, 0, 0, 0, 0]~
+[-396997/15853839, 123852/5284613, -30975/5284613, 53135/15853839, 19678/158
+53839]~
+[9/4, 3/4, 3/4, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[1, 0, 0, 0, 0]~
+[1, 12/11, 1/11, 0, -1/11]~
+[6, 2, 2, 0, 0]~
+[1249690/15853839, -222317/5284613, 39600/5284613, -477392/15853839, 434/158
+53839]~
+[0, 3/4, 0, 0, 0]~
+[0, -1, 0, 0, 0]~
+[3/31, -10/31, 7/31, 5/31, -6/31]~
+[1, 0, 0, 0, 0]~
+[0, 2, 0, 0, 0]~
+[-672280/15853839, 150044/5284613, -148123/5284613, 73247/15853839, -53135/1
+5853839]~
+[3/8, 0, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[-8/31, 6/31, 2/31, -3/31, 1/62]~
+[0, 1/22, 1/22, 0, -1/22]~
+[1, 0, 0, 0, 0]~
+[396997/31707678, -61926/5284613, 30975/10569226, -53135/31707678, -9839/158
+53839]~
+[3/4, 3/2, 9/4, 3, 15/4]~
+[-1, -2, -3, -4, -5]~
+[-314/31, -59/31, 311/31, 14/31, -85/31]~
+[7, -27/11, 39/11, 5, 5/11]~
+[2, 4, 6, 8, 10]~
+[1, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[-1, 1, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[3, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-2, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[2, 1, 1, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[1, 0, 0, 0, 0]~
+[1, 1, 0, 0, 0]~
+[6, 2, 2, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 1, 0, 0, 0]~
+[0, -1, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 2, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 2, 2, 3, 4]~
+[-1, -2, -3, -4, -5]~
+[-10, -2, 10, 0, -3]~
+[7, -2, 4, 5, 0]~
+[2, 4, 6, 8, 10]~
+[1, 0, 0, 0, 0]~
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[-1, 0, 0, 0, 0]~, [1/3, 0, 0, 0, 0]~]
+[[-1, 1, 0, 0, 0]~, [7/3, -2, 0, -1, 0]~]
+[[0, 0, 0, 0, 0]~, [4/3, 0, 0, 0, 0]~]
+[[3, 0, 0, 0, 0]~, [-1/6, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [4/3, 0, 0, 0, 0]~]
+[[-1, 0, 0, 0, 0]~, [1/3, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [-4, -1, -1, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [-1, 0, 0, 0, 0]~]
+[[-2, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [-1, 0, 0, 0, 0]~]
+[[2, 1, 1, 0, 0]~, [1/3, -1/3, -1/3, 0, 0]~]
+[[-3, -1, -1, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[1, 1, 0, 0, 0]~, [1, 0, 0, 0, 0]~]
+[[6, 2, 2, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [3, 1, 1, 0, 0]~]
+[[0, 1, 0, 0, 0]~, [0, -1/3, 0, 0, 0]~]
+[[0, -1, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [0, 1, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 2, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [0, 1, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[1, 2, 2, 3, 4]~, [-1/3, -2/3, 1/3, 0, -1/3]~]
+[[-1, -2, -3, -4, -5]~, [0, 0, 0, 0, 0]~]
+[[-10, -2, 10, 0, -3]~, [-6, -2, 1, 2, 1]~]
+[[7, -2, 4, 5, 0]~, [-5, 0, 0, 0, 0]~]
+[[2, 4, 6, 8, 10]~, [0, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[0, 0, 0, 0, 0]~
+[1/3, 0, 0, 0, 0]~
+[7/3, -2, 0, -1, 0]~
+[4/3, 0, 0, 0, 0]~
+[-1/6, 0, 0, 0, 0]~
+[4/3, 0, 0, 0, 0]~
+[1/3, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-4, -1, -1, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[1/3, -1/3, -1/3, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[3, 1, 1, 0, 0]~
+[0, -1/3, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 1, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 1, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[-1/3, -2/3, 1/3, 0, -1/3]~
+[0, 0, 0, 0, 0]~
+[-6, -2, 1, 2, 1]~
+[-5, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[16/9, 0, 0, 0, 0]~
+[-4/3, 0, 0, 0, 0]~
+[4, 4/3, 4/3, 0, 0]~
+[0, 4/3, 0, 0, 0]~
+[2/3, 0, 0, 0, 0]~
+[4/3, 8/3, 4, 16/3, 20/3]~
+[-4/3, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[0, -1, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[-1, -2, -3, -4, -5]~
+[4, 4/3, 4/3, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[13, 5, 6, 2, 1]~
+[2, 3, 1, 1, 0]~
+[3/2, 1/2, 1/2, 0, 0]~
+[-58, -42, 23, 27, 17]~
+[0, 4/3, 0, 0, 0]~
+[0, -1, 0, 0, 0]~
+[2, 3, 1, 1, 0]~
+[2, 0, 1, 0, 0]~
+[0, 1/2, 0, 0, 0]~
+[-33, -3, 11, 8, 4]~
+[2/3, 0, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[3/2, 1/2, 1/2, 0, 0]~
+[0, 1/2, 0, 0, 0]~
+[1/4, 0, 0, 0, 0]~
+[1/2, 1, 3/2, 2, 5/2]~
+[4/3, 8/3, 4, 16/3, 20/3]~
+[-1, -2, -3, -4, -5]~
+[-58, -42, 23, 27, 17]~
+[-33, -3, 11, 8, 4]~
+[1/2, 1, 3/2, 2, 5/2]~
+[-1071, -384, -251, -155, 20]~
+[1]
+[1, 1/2*x - 1/2]
+[2, Mod(0, 2)]~
+[x^2 + x + 1, [0, 1], -3, 1, [Mat([1, -0.50000000000000000000000000000000000
+000 + 0.86602540378443864676372317075293618347*I]), [1, 0.366025403784438646
+76372317075293618347; 1, -1.3660254037844386467637231707529361835], [1, 0; 1
+, -1], [2, -1; -1, -1], [3, 2; 0, 1], [1, -1; -1, -2], [3, [2, -1; 1, 1]], [
+]], [-0.50000000000000000000000000000000000000 + 0.8660254037844386467637231
+7075293618347*I], [1, x], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, -1]]
+2
+[0, Mod(0, 2), 1, 0, 0, 0, 0]~
+[0, Mod(1, 2), 1, 0, 1, 0, 0]~
+[]~
+[]~
+388
+
+[2 0]
+
+[0 1]
+
+  *** bnfisprincipal: Warning: precision too low for generators, not given.
+[[]~, [-16275043782306513717209797591668600538906793729160424387141562023303
+069241961, -3992515767463859376807521115314587378342597458337773390379448027
+181914746015, 40263088752008514039400780199135662260965092541683607359784818
+2049497266399, 3875196415920480829978279850511752676499384722019721458357455
+29259111353576, 524613164482816169908873750526849574668376660999341089640759
+880949016596504]~]
+[[[[[1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0,
+ 1], Vecsmall([1])], [2, [2]], matrix(0,2), [[[2], [-1], [Vecsmall([1])]]], 
+Mat(1)]], [[[[2, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0,
+ 0, 0, 0, 1], Vecsmall([1])], [2, [2]], Mat([[2, [0, 0, 1, -1, 0]~, 3, 1, [0
+, -80, 60, -10, -60; 0, 0, -100, 0, -20; 0, 0, 0, -10, -100; 0, 2, -2, 0, 2;
+ 1, 0, 0, -10, 0]], 1]), [[[[1], [1], [1], [Vecsmall([])], 1]], [[2], [-1], 
+[Vecsmall([1])]]], Mat(1)]], [], [[[[2, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 
+0, 0; 0, 0, 0, 2, 0; 0, 0, 0, 0, 1], Vecsmall([1])], [4, [2, 2]], Mat([[2, [
+0, 0, 1, -1, 0]~, 3, 1, [0, -80, 60, -10, -60; 0, 0, -100, 0, -20; 0, 0, 0, 
+-10, -100; 0, 2, -2, 0, 2; 1, 0, 0, -10, 0]], 2]), [[[[1], [1], [1], [Vecsma
+ll([])], 1], [[2], [[1, 0, 0, -1, 0]~], [[1, 0, 0, -1, 0]~], [Vecsmall([])],
+ Mat([0, 0, 0, 1, 0])]], [[2], [-1], [Vecsmall([1])]]], [1, 0; 0, 1]], [[[2,
+ 0, 1, 0, 0; 0, 2, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], Ve
+csmall([1])], [6, [6]], Mat([[2, [1, 2, 1, 0, 0]~, 1, 2, [0, -80, -20, 0, 0;
+ 0, -1, -100, -10, -120; 1, 0, -1, -20, -100; 0, 2, 0, 0, 0; 1, 1, -1, -10, 
+0]], 1]), [[[[3], [[0, 1, 0, 0, 0]~], [[0, 1, 0, 0, 0]~], [Vecsmall([])], 1]
+], [[2], [-1], [Vecsmall([1])]]], Mat([-2, -3])]]]
+[[[[[[1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0
+, 1], Vecsmall([1])], [2, [2], [[-1, 0, 0, 0, 0]~]], matrix(0,2), [[[2], [-1
+], [Vecsmall([1])]]], Mat(1)], Mat([1, 1, 1])]], [[[[[2, 0, 0, 0, 0; 0, 1, 0
+, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], Vecsmall([1])], [2, [2
+], [[-1, 0, 0, 0, 0]~]], Mat([[2, [0, 0, 1, -1, 0]~, 3, 1, [0, -80, 60, -10,
+ -60; 0, 0, -100, 0, -20; 0, 0, 0, -10, -100; 0, 2, -2, 0, 2; 1, 0, 0, -10, 
+0]], 1]), [[[[1], [1], [1], [Vecsmall([])], 1]], [[2], [-1], [Vecsmall([1])]
+]], Mat(1)], Mat([1, 1, 1])]], [], [[[[[2, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 
+1, 0, 0; 0, 0, 0, 2, 0; 0, 0, 0, 0, 1], Vecsmall([1])], [4, [2, 2], [[1, 0, 
+0, -1, 0]~, [-1, 0, 0, 0, 0]~]], Mat([[2, [0, 0, 1, -1, 0]~, 3, 1, [0, -80, 
+60, -10, -60; 0, 0, -100, 0, -20; 0, 0, 0, -10, -100; 0, 2, -2, 0, 2; 1, 0, 
+0, -10, 0]], 2]), [[[[1], [1], [1], [Vecsmall([])], 1], [[2], [[1, 0, 0, -1,
+ 0]~], [[1, 0, 0, -1, 0]~], [Vecsmall([])], Mat([0, 0, 0, 1, 0])]], [[2], [-
+1], [Vecsmall([1])]]], [1, 0; 0, 1]], [0, 0, 0; 1, 1, 1]], [[[[2, 0, 1, 0, 0
+; 0, 2, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], Vecsmall([1])
+], [6, [6], [[-1, -1, 0, 0, 0]~]], Mat([[2, [1, 2, 1, 0, 0]~, 1, 2, [0, -80,
+ -20, 0, 0; 0, -1, -100, -10, -120; 1, 0, -1, -20, -100; 0, 2, 0, 0, 0; 1, 1
+, -1, -10, 0]], 1]), [[[[3], [[1, 1, 0, 0, 0]~], [[1, 1, 0, 0, 0]~], [Vecsma
+ll([])], 1]], [[2], [-1], [Vecsmall([1])]]], Mat([-2, -3])], Mat([-3, 1, -3]
+)]]]
+[[[5, 1, [2, 2; 5, 2; 39821, 1; 161141, 1]]], [[]], [], [[10, 2, [2, 6; 5, 4
+; 39821, 2; 161141, 2]], []]]
+[[[5, -1, [-1, 1; 2, 2; 5, 2; 39821, 1; 161141, 1]]], [[]], [], [[10, -2, [2
+, 6; 5, 4; 39821, 2; 161141, 2]], []]]
+[[[[matrix(0,2), 5, -1, [-1, 1; 2, 2; 5, 2; 39821, 1; 161141, 1]]], [[Mat([5
+0, 1]), 0, 0, 0]], [], [[Mat([50, 2]), 10, -2, [2, 6; 5, 4; 39821, 2; 161141
+, 2]], [Mat([56, 1]), 0, 0, 0]]]]
+  ***   at top-level: nfinit([y^3+2,[1,x]]
+  ***                 ^--------------------
+  *** nfinit: incorrect type in nfbasic_init (t_VEC).
+  ***   at top-level: nfinit([y^3+2,[1,x,x
+  ***                 ^--------------------
+  *** nfinit: incorrect type in nfbasic_init (t_VEC).
+  ***   at top-level: nfinit([y^3+2,[1,y^5
+  ***                 ^--------------------
+  *** nfinit: incorrect type in nfbasic_init (t_VEC).
+2
+[6416795761]
+  ***   at top-level: nfnewprec(x)
+  ***                 ^------------
+  *** nfnewprec: incorrect type in nfnewprec (t_POL).
+  ***   at top-level: nfnewprec(quadgen(5)
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_QUAD).
+  ***   at top-level: nfnewprec(vector(5))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(6))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(8))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(9))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(16)
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+Total time spent: 1952
diff --git a/src/test/32/nffactor b/src/test/32/nffactor
new file mode 100644
index 0000000..accbc30
--- /dev/null
+++ b/src/test/32/nffactor
@@ -0,0 +1,734 @@
+  ***   Warning: new stack size = 16000000 (15.259 Mbytes).
+[x^72 - 291*x^70 + 168*x^69 + 40380*x^68 - 48588*x^67 - 3528919*x^66 + 66721
+20*x^65 + 215657160*x^64 - 575538144*x^63 - 9642387423*x^62 + 34735086786*x^
+61 + 318475831783*x^60 - 1543992152304*x^59 - 7526047084203*x^58 + 517099213
+23996*x^57 + 110268119466273*x^56 - 1306863903654948*x^55 - 197687339387338*
+x^54 + 24340617020480994*x^53 - 37674206381844006*x^52 - 309388136734870296*
+x^51 + 1097175021601270233*x^50 + 1965430743178095924*x^49 - 170577413076819
+44498*x^48 + 12695705864721864408*x^47 + 149941210123858078557*x^46 - 449449
+010694960248724*x^45 - 360137445013361079753*x^44 + 4743771886303072354536*x
+^43 - 7957480107528139931362*x^42 - 20006312987061736459890*x^41 + 103127662
+005251951018025*x^40 - 57922725775374790826892*x^39 - 5753749773364770608784
+06*x^38 + 1141042155363070337681952*x^37 + 2107623272811930164219492*x^36 - 
+8555883275792119671168984*x^35 - 6622038332271478648217502*x^34 + 6778849907
+3804904961005264*x^33 - 62194621346216574281355513*x^32 - 298503076979390816
+950994616*x^31 + 935868776923024509133161567*x^30 - 602191893688026944562387
+144*x^29 - 2378403718028116295265005703*x^28 + 7144715267789671188060423636*
+x^27 - 8264313767410946129053876314*x^26 - 2988303993119955116599622124*x^25
+ + 36320303706133493815706370331*x^24 - 93405543373036036850518472592*x^23 +
+ 137892731303549623166716872621*x^22 - 73374564495372153466268524626*x^21 - 
+180690507689854149951443988039*x^20 + 506199649638427572646328975856*x^19 - 
+511453665473658325356669209047*x^18 - 183193264910244539106552118338*x^17 + 
+1423840911419731272911578335897*x^16 - 2367314022969857609246689985844*x^15 
++ 2236677523353346112926136338695*x^14 - 1548489683091587051973217338168*x^1
+3 + 2105049137205776145162583648404*x^12 - 4754348311294629767834593856064*x
+^11 + 7567806891220394207855512254933*x^10 - 7605431066578089163623568649610
+*x^9 + 3882625664788999249342771140339*x^8 + 1356469287400668040516453202076
+*x^7 - 3841355512345259545848813224621*x^6 + 2330504083587501732658867007532
+*x^5 - 169172628407290891217225606775*x^4 - 398965703199322569698936377044*x
+^3 + 198978398979453484569202793808*x^2 - 60597282938946837445378411698*x + 
+12280639561039083425818958713]~
+[x^48 + 12*x^46 + 948*x^44 + 7200*x^42 + 152361*x^40 + 815832*x^38 + 9475380
+*x^36 + 44654004*x^34 + 299137536*x^32 + 1335241260*x^30 + 5029216452*x^28 +
+ 15282825984*x^26 + 37737671337*x^24 + 79579803672*x^22 + 143658877428*x^20 
++ 222699104460*x^18 + 303698198961*x^16 + 348787956312*x^14 + 312863646960*x
+^12 + 212893847424*x^10 + 111407984496*x^8 + 43762394880*x^6 + 11836253952*x
+^4 + 1904684544*x^2 + 136048896]~
+[x^48 - 104*x^46 + 4664*x^44 - 122476*x^42 + 2137838*x^40 - 26567700*x^38 + 
+245144964*x^36 - 1725955872*x^34 + 9441692003*x^32 - 40611588644*x^30 + 1383
+56971048*x^28 - 374714866240*x^26 + 807289826646*x^24 - 1380693858220*x^22 +
+ 1866021172640*x^20 - 1978766780068*x^18 + 1630151673857*x^16 - 102950530102
+4*x^14 + 489498952012*x^12 - 170832297056*x^10 + 42133382284*x^8 - 690450713
+6*x^6 + 669868016*x^4 - 28899680*x^2 + 16]~
+[x^64 - 6384*x^62 + 18261761*x^60 - 31231019568*x^58 + 35925400902280*x^56 -
+ 29635423138225800*x^54 + 18244443900381139917*x^52 - 8609789775431197305288
+*x^50 + 3173715440318358526295493*x^48 - 926253189958924421506713024*x^46 + 
+216130107574547887816493973792*x^44 - 40600173780591579547211667354912*x^42 
++ 6168621134132051706341715912515370*x^40 - 76013943427734813586799139295141
+5744*x^38 + 76052955647065900426700689494084391434*x^36 - 617531184355441677
+2593815034596603661344*x^34 + 406137362468726923132369140307868626461744*x^3
+2 - 21559959594011538596615817038079394912524336*x^30 + 91912201022866770299
+8458013093798368177514170*x^28 - 3125139901367629320825272825691018301064633
+1888*x^26 + 840017792466500823059432352741453680599704357258*x^24 - 17651171
+087877975905738341268934550365403253370816*x^22 + 28594069425611136664663397
+3109729677687028435758880*x^20 - 3510138236669349338216717168753743720690200
+440965920*x^18 + 31970659328447652254136627725804141367170891626180389*x^16 
+- 210577501276259853380917767547980363666145460503950096*x^14 + 972633665701
+140285445722173171004563157651576027463941*x^12 - 30387893683293821798844628
+51269146550248145196800679728*x^10 + 615892792889658839667059092889638564907
+5890120481042824*x^8 - 76951350792700777906576936340585240393085356418600329
+04*x^6 + 5510293734552561962521574495431679567021222445632508873*x^4 - 19987
+78331104544904932086470347413669495129560426038280*x^2 + 2738927449953408337
+77347939263771534786080723599733441]~
+[x^8 + (-1531349/22619785297920*y^14 + 559416343/10977248747520*y^12 - 25247
+684591/1995863408640*y^10 + 103982297321/80715064320*y^8 - 76714376249/12229
+55520*y^6 + 111774992009/74186640*y^4 - 26292202739/1686060*y^2 + 1564021312
+/54549)*x^6 + (19000645309/37322645741568*y^14 - 96552535399/249482926080*y^
+12 + 536960987254321/5488624373760*y^10 - 4194944378809/407651840*y^8 + 5247
+721708074653/10089383040*y^6 - 10860233882239/843030*y^4 + 630194805031381/4
+636665*y^2 - 796961577488/2755)*x^4 + (-29172555558095/18661322870784*y^14 +
+ 5062445860141/4248161280*y^12 - 92318704732060083/304923576320*y^10 + 64819
+3176741591037/20178766080*y^8 - 8268979844194150111/5044691520*y^6 + 1896413
+90835405983/4636665*y^4 - 223283186783900366/515185*y^2 + 8946625664367872/9
+405)*x^2 + (41018074761203773/23326653588480*y^14 - 409009053454624763/30492
+3576320*y^12 + 117044119382956746521/343039023360*y^10 - 2927890424340695147
+291/80715064320*y^8 + 4678762405288733527739/2522345760*y^6 - 38206315875334
+762391/824296*y^4 + 2278645136881067296952/4636665*y^2 - 2967400235256172868
+48/272745), x^8 + (-16146091/248817638277120*y^14 + 48843457/997931704320*y^
+12 - 267727721489/21954497495040*y^10 + 9174250019/7337733120*y^8 - 82557283
+8631/13452510720*y^6 + 10012035071/6744240*y^4 - 286258292621/18546660*y^2 +
+ 140187040/4959)*x^6 + (1723176289/3392967794688*y^14 - 1059662276341/274431
+2186880*y^12 + 48715274000629/498965852160*y^10 - 69104247053959/6726255360*
+y^8 + 476508837255737/917216640*y^6 - 238731150890387/18546660*y^4 + 5725141
+8840649/421515*y^2 - 26269467866896/90915)*x^4 + (-29113232197835/1866132287
+0784*y^14 + 1631934517436717/1372156093440*y^12 - 92145545361015407/30492357
+6320*y^10 + 323550303468175019/10089383040*y^8 - 8257197132303274099/5044691
+520*y^6 + 378862459377191059/9273330*y^4 - 223115572203411654/515185*y^2 + 2
+59332507639972352/272745)*x^2 + (40511288842122127/23326653588480*y^14 - 454
+626079701495571/343039023360*y^12 + 231451885839430725263/686078046720*y^10 
+- 60389017554715183063/1681563840*y^8 + 2319938341838169992923/1261172880*y^
+6 - 42704610621363386060/927333*y^4 + 2268063476824046324248/4636665*y^2 - 9
+8528590048656718144/90915), x^8 + (181633/186613228707840*y^14 - 6489/138601
+62560*y^12 - 45386471/5488624373760*y^10 + 178250659/7337733120*y^8 - 259150
+32257/10089383040*y^6 + 126060163/1498720*y^4 - 4457231129/4636665*y^2 + 945
+4484/4959)*x^6 + (-11712673/1169988894720*y^14 + 2040142733/274431218688*y^1
+2 - 93021416071/52522721280*y^10 + 1688263641205/10762008576*y^8 - 890698149
+9371/1834433280*y^6 + 14859994827859/296746560*y^4 - 27539242721/421515*y^2 
+- 10412804654/90915)*x^4 + (-65739851389/2915831698560*y^14 + 8080067384671/
+548862437376*y^12 - 615919067582923/228692682240*y^10 + 7139625968047/558581
+76*y^8 - 12035658240536459/5044691520*y^6 + 2909281718459207/148373280*y^4 -
+ 114027481266257/1545555*y^2 + 26994839222348/272745)*x^2 + (351875661187594
+13/746452914831360*y^14 - 175794408000830383/5488624373760*y^12 + 2827285081
+9680893321/4390899499008*y^10 - 44274526240947916361/107620085760*y^8 + 4488
+83263777097920391/40357532160*y^6 - 2698220779200958903/20465280*y^4 + 28100
+59487221296958/4636665*y^2 - 81508587175798061/90915), x^8 + (3074279/248817
+638277120*y^14 - 127157047/10977248747520*y^12 + 86499746437/21954497495040*
+y^10 - 2843672497/4747944960*y^8 + 109487803351/2690502144*y^6 - 89990421617
+/74186640*y^4 + 52396810109/3709332*y^2 - 13670576288/272745)*x^6 + (-361812
+87281/186613228707840*y^14 + 446988241097/2744312186880*y^12 - 2791944232079
+/57774993408*y^10 + 85916604196211/13452510720*y^8 - 3999057670537517/100893
+83040*y^6 + 3070998321176/272745*y^4 - 593787426492949/4636665*y^2 + 3730615
+4735728/90915)*x^4 + (64206977604683/93306614353920*y^14 - 781339956430123/1
+372156093440*y^12 + 151234315443075961/914770728960*y^10 - 42955239518225908
+7/20178766080*y^8 + 1307386034599317571/1008938304*y^6 - 168786176615470804/
+4636665*y^4 + 127255299187877218/309111*y^2 - 351207138657683456/272745)*x^2
+ + (-18530706211095341/23326653588480*y^14 + 1798847927551103773/27443121868
+80*y^12 - 16252626552763832987/85759755840*y^10 + 653145277562478020663/2690
+5021440*y^8 - 742591759365166575365/504469152*y^6 + 1530656313511014482581/3
+7093320*y^4 - 432314981823305760184/927333*y^2 + 131680859590220167616/90915
+), x^8 + (3692011/248817638277120*y^14 - 8644699/645720514560*y^12 + 9566281
+9793/21954497495040*y^10 - 51309940321/80715064320*y^8 + 113348313275/269050
+2144*y^6 - 91885783933/74186640*y^4 + 53072545513/3709332*y^2 - 13761686272/
+272745)*x^6 + (-1260198851/6434938920960*y^14 + 450923192773/2744312186880*y
+^12 - 53418556138459/1097724874752*y^10 + 43167277559347/6726255360*y^8 - 40
+11960654104563/10089383040*y^6 + 209270128922867/18546660*y^4 - 594571618949
+411/4636665*y^2 + 37329116221552/90915)*x^4 + (3385038072653/4910874439680*y
+^14 - 782493705352717/1372156093440*y^12 + 151409707529271949/914770728960*y
+^10 - 214941552982993939/10089383040*y^8 + 1308011461994334679/1008938304*y^
+6 - 337682297591934347/9273330*y^4 + 127297072825004458/309111*y^2 - 3513766
+27887928064/272745)*x^2 + (-18884591927033039/23326653588480*y^14 + 76223129
+967189533/114346341120*y^12 - 131815203944675271599/686078046720*y^10 + 1236
+67566697208858791/5044691520*y^8 - 373763922533488473487/252234576*y^6 + 116
+278861122276976/2805*y^4 - 433850608352102566712/927333*y^2 + 39636262539108
+2293312/272745), x^8 + (1192483/67859355893760*y^14 - 46386469/3659082915840
+*y^12 + 5757671539/1995863408640*y^10 - 9816397597/40357532160*y^8 + 3150430
+0903/3668866560*y^6 - 6499115329/49457760*y^4 + 1346529151/1686060*y^2 - 127
+928104/54549)*x^6 + (-377107043/23326653588480*y^14 + 1193294195/99793170432
+*y^12 - 3898646479517/1372156093440*y^10 + 83324546651/326121472*y^8 - 90270
+807160297/10089383040*y^6 + 203979621263/1586880*y^4 - 6453521496733/9273330
+*y^2 + 3740713928/2755)*x^4 + (-55838547409/2455437219840*y^14 + 81535888034
+65/548862437376*y^12 - 1244323126435859/457385364480*y^10 + 2090484253417429
+/16143012864*y^8 - 12134063030016491/5044691520*y^6 + 165453933807769/872784
+0*y^4 - 93498888636323/1545555*y^2 + 14213849400992/272745)*x^2 + (118472423
+79734429/248817638277120*y^14 - 177668994581955997/5488624373760*y^12 + 2861
+1858755746067827/4390899499008*y^10 - 134874618655070911657/322860257280*y^8
+ + 152404618518667558583/13452510720*y^6 - 4203829397104185047/31236480*y^4 
++ 2871462472553380217/4636665*y^2 - 250004160368295232/272745), x^8 + (64582
+73/186613228707840*y^14 - 3719191/152461788160*y^12 + 29024100473/5488624373
+760*y^10 - 32135840999/80715064320*y^8 + 23297956603/2017876608*y^6 - 211350
+0871/16485920*y^4 + 408266029/927333*y^2 - 316594868/272745)*x^6 + (-1110768
+17459/373226457415680*y^14 + 18106275053/85759755840*y^12 - 509560146214411/
+10977248747520*y^10 + 195109797344857/53810042880*y^8 - 2298235154532701/201
+78766080*y^6 + 87655160117623/59349312*y^4 - 32564739888281/4636665*y^2 + 96
+8008850258/90915)*x^4 + (83518124920963/93306614353920*y^14 - 91728776500189
+/144437483520*y^12 + 42591591062192407/304923576320*y^10 - 88154696029372417
+3/80715064320*y^8 + 86773202255301785/252234576*y^6 - 665429827885409833/148
+373280*y^4 + 2218979482348247/103037*y^2 - 8878201815711796/272745)*x^2 + (-
+246642305436057809/248817638277120*y^14 + 3860028638255806541/5488624373760*
+y^12 - 3395672666468176433467/21954497495040*y^10 + 3904062052800146734661/3
+22860257280*y^8 - 1024736920815948655471/2690502144*y^6 + 294713641271965414
+9381/593493120*y^4 - 22118315020431326588/927333*y^2 + 9833627869746746873/2
+72745), x^8 + (38996983/746452914831360*y^14 - 45610777/1219694305280*y^12 +
+ 184040442823/21954497495040*y^10 - 6929849477/10089383040*y^8 + 19138097395
+1/8071506432*y^6 - 6002605199/16485920*y^4 + 8595505583/3709332*y^2 - 144096
+9832/272745)*x^6 + (-56627432063/186613228707840*y^14 + 1182421824943/548862
+4373760*y^12 - 260406121516747/5488624373760*y^10 + 200262331426543/53810042
+880*y^8 - 297480952643123/2522345760*y^6 + 92330503999361/59349312*y^4 - 710
+13869059163/9273330*y^2 + 1098811062152/90915)*x^4 + (83613652919587/9330661
+4353920*y^14 - 1744863264395929/2744312186880*y^12 + 42642092214501583/30492
+3576320*y^10 - 882655350615967327/80715064320*y^8 + 173789516969368021/50446
+9152*y^6 - 666541614309814567/148373280*y^4 + 2223816735480437/103037*y^2 - 
+306949426473056/9405)*x^2 + (-742452471836109913/746452914831360*y^14 + 3873
+208908206849579/5488624373760*y^12 - 3407275945042823422193/21954497495040*y
+^10 + 1305807204358253484253/107620085760*y^8 - 3084740075192713038863/80715
+06432*y^6 + 173955024027274456667/34911360*y^4 - 22194121937138006191/927333
+*y^2 + 3289110492164463424/90915)]~
+[x^2 + (-872560111/1750783970525184*y^18 - 103213549/291797328420864*y^16 - 
+4176139757/7204872306688*y^14 - 4416978655/10807308460032*y^12 - 13066402491
+575/72949332105216*y^10 - 215588492387/1736888859648*y^8 - 892238763206647/1
+09423998157824*y^6 - 89512294102405/18237333026304*y^4 - 18290521472141/2532
+96292032*y^2 - 284080544581/14072016224), x^2 + (872560111/1750783970525184*
+y^18 - 103213549/291797328420864*y^16 + 4176139757/7204872306688*y^14 - 4416
+978655/10807308460032*y^12 + 13066402491575/72949332105216*y^10 - 2155884923
+87/1736888859648*y^8 + 892238763206647/109423998157824*y^6 - 89512294102405/
+18237333026304*y^4 + 18290521472141/253296292032*y^2 - 284080544581/14072016
+224), x^2 + (-10955729699/63028222938906624*y^19 + 1635275/72949332105216*y^
+17 - 471200483153/2334378627366912*y^15 + 68074817/2701827115008*y^13 - 1631
+44665311371/2626175955787776*y^11 + 10788711995/1519777752192*y^9 - 10805585
+507756651/3939263933681664*y^7 + 476476835441/4559333256576*y^5 - 6980744239
+9435/3039555504384*y^3 - 4848316573/3518004056*y)*x + (-240314803/1313087977
+893888*y^18 + 2177899/20842666315776*y^16 - 10352388961/48632888070144*y^14 
++ 650809375/5403654230016*y^12 - 3599460371687/54711999078912*y^10 + 2204400
+27581/6079111008768*y^8 - 35061053609941/11723999802624*y^6 + 11692976745493
+/9118666513152*y^4 - 1447968833323/63324073008*y^2 - 24126350651/7036008112)
+, x^2 + (-4768637809/63028222938906624*y^19 - 116295749/1750783970525184*y^1
+7 - 205045552843/2334378627366912*y^15 - 4961577191/64843850760192*y^13 - 10
+134809023919/375167993683968*y^11 - 1681738838629/72949332105216*y^9 - 46714
+70958726025/3939263933681664*y^7 - 93324108785933/109423998157824*y^5 - 2896
+7498100409/3039555504384*y^3 - 245294011997/84432097344*y)*x + (-496188853/1
+750783970525184*y^18 + 8017631/48632888070144*y^16 - 338265737/1029267472384
+*y^14 + 338938437/1801218076672*y^12 - 7349975872277/72949332105216*y^10 + 1
+12959067435/2026370336256*y^8 - 471966038240125/109423998157824*y^6 + 558940
+2408335/3039555504384*y^4 - 8241706316231/253296292032*y^2 + 1651761915/1005
+144016), x^2 + (-550587305/9004031848415232*y^19 - 8520109/250111995789312*y
+^17 - 165341932517/2334378627366912*y^15 - 2570761369/64843850760192*y^13 - 
+56851147336519/2626175955787776*y^11 - 895619999627/72949332105216*y^9 - 356
+0831957116295/3939263933681664*y^7 - 62314525927891/109423998157824*y^5 - 18
+560457963575/3039555504384*y^3 - 371119778467/84432097344*y)*x + (496188853/
+1750783970525184*y^18 + 8017631/48632888070144*y^16 + 338265737/102926747238
+4*y^14 + 338938437/1801218076672*y^12 + 7349975872277/72949332105216*y^10 + 
+112959067435/2026370336256*y^8 + 471966038240125/109423998157824*y^6 + 55894
+02408335/3039555504384*y^4 + 8241706316231/253296292032*y^2 + 1651761915/100
+5144016), x^2 + (-21512837/1313087977893888*y^19 - 102134281/175078397052518
+4*y^17 - 932348591/48632888070144*y^15 - 4407863827/64843850760192*y^13 - 16
+4654686001/27355999539456*y^11 - 1540975821641/72949332105216*y^9 - 25030796
+719091/82067998618368*y^7 - 110013175606897/109423998157824*y^5 - 7018393859
+/2261574036*y^3 - 296247471259/28144032448*y)*x + (240314803/131308797789388
+8*y^18 + 2177899/20842666315776*y^16 + 10352388961/48632888070144*y^14 + 650
+809375/5403654230016*y^12 + 3599460371687/54711999078912*y^10 + 220440027581
+/6079111008768*y^8 + 35061053609941/11723999802624*y^6 + 11692976745493/9118
+666513152*y^4 + 1447968833323/63324073008*y^2 - 24126350651/7036008112), x^2
+ + (21512837/1313087977893888*y^19 + 102134281/1750783970525184*y^17 + 93234
+8591/48632888070144*y^15 + 4407863827/64843850760192*y^13 + 164654686001/273
+55999539456*y^11 + 1540975821641/72949332105216*y^9 + 25030796719091/8206799
+8618368*y^7 + 110013175606897/109423998157824*y^5 + 7018393859/2261574036*y^
+3 + 296247471259/28144032448*y)*x + (240314803/1313087977893888*y^18 + 21778
+99/20842666315776*y^16 + 10352388961/48632888070144*y^14 + 650809375/5403654
+230016*y^12 + 3599460371687/54711999078912*y^10 + 220440027581/6079111008768
+*y^8 + 35061053609941/11723999802624*y^6 + 11692976745493/9118666513152*y^4 
++ 1447968833323/63324073008*y^2 - 24126350651/7036008112), x^2 + (550587305/
+9004031848415232*y^19 + 8520109/250111995789312*y^17 + 165341932517/23343786
+27366912*y^15 + 2570761369/64843850760192*y^13 + 56851147336519/262617595578
+7776*y^11 + 895619999627/72949332105216*y^9 + 3560831957116295/3939263933681
+664*y^7 + 62314525927891/109423998157824*y^5 + 18560457963575/3039555504384*
+y^3 + 371119778467/84432097344*y)*x + (496188853/1750783970525184*y^18 + 801
+7631/48632888070144*y^16 + 338265737/1029267472384*y^14 + 338938437/18012180
+76672*y^12 + 7349975872277/72949332105216*y^10 + 112959067435/2026370336256*
+y^8 + 471966038240125/109423998157824*y^6 + 5589402408335/3039555504384*y^4 
++ 8241706316231/253296292032*y^2 + 1651761915/1005144016), x^2 + (4768637809
+/63028222938906624*y^19 + 116295749/1750783970525184*y^17 + 205045552843/233
+4378627366912*y^15 + 4961577191/64843850760192*y^13 + 10134809023919/3751679
+93683968*y^11 + 1681738838629/72949332105216*y^9 + 4671470958726025/39392639
+33681664*y^7 + 93324108785933/109423998157824*y^5 + 28967498100409/303955550
+4384*y^3 + 245294011997/84432097344*y)*x + (-496188853/1750783970525184*y^18
+ + 8017631/48632888070144*y^16 - 338265737/1029267472384*y^14 + 338938437/18
+01218076672*y^12 - 7349975872277/72949332105216*y^10 + 112959067435/20263703
+36256*y^8 - 471966038240125/109423998157824*y^6 + 5589402408335/303955550438
+4*y^4 - 8241706316231/253296292032*y^2 + 1651761915/1005144016), x^2 + (1095
+5729699/63028222938906624*y^19 - 1635275/72949332105216*y^17 + 471200483153/
+2334378627366912*y^15 - 68074817/2701827115008*y^13 + 163144665311371/262617
+5955787776*y^11 - 10788711995/1519777752192*y^9 + 10805585507756651/39392639
+33681664*y^7 - 476476835441/4559333256576*y^5 + 69807442399435/3039555504384
+*y^3 + 4848316573/3518004056*y)*x + (-240314803/1313087977893888*y^18 + 2177
+899/20842666315776*y^16 - 10352388961/48632888070144*y^14 + 650809375/540365
+4230016*y^12 - 3599460371687/54711999078912*y^10 + 220440027581/607911100876
+8*y^8 - 35061053609941/11723999802624*y^6 + 11692976745493/9118666513152*y^4
+ - 1447968833323/63324073008*y^2 - 24126350651/7036008112)]~
+[x^24 + 69]~
+[x^7 - 2*y*x^6 + y^2*x^5 - 28*x^3 + 4*y^2*x + 16/7*y^3, x^7 + 2*y*x^6 + y^2*
+x^5 - 28*x^3 + 4*y^2*x - 16/7*y^3, x^7 - 2/7*y^3*x^6 - y^2*x^5 - 28*x^3 - 4*
+y^2*x + 16*y, x^7 + 2/7*y^3*x^6 - y^2*x^5 - 28*x^3 - 4*y^2*x - 16*y]~
+[x^2 + (-12035/386*y^15 + 8337/386*y^14 + 566267/772*y^13 - 392327/772*y^12 
+- 4449119/772*y^11 + 3083327/772*y^10 + 7320431/386*y^9 - 2537889/193*y^8 - 
+9975849/386*y^7 + 3461954/193*y^6 + 10712085/772*y^5 - 7445851/772*y^4 - 190
+5321/772*y^3 + 1322419/772*y^2 + 25961/386*y - 17735/386), x^2 + (-7909/772*
+y^15 - 1879/772*y^14 + 93535/386*y^13 + 44921/772*y^12 - 371330/193*y^11 - 9
+0974/193*y^10 + 4991389/772*y^9 + 317305/193*y^8 - 7127131/772*y^7 - 1947859
+/772*y^6 + 2110371/386*y^5 + 1295397/772*y^4 - 224091/193*y^3 - 160649/386*y
+^2 + 19739/772*y + 4029/386), x^2 + (7909/772*y^15 - 1879/772*y^14 - 93535/3
+86*y^13 + 44921/772*y^12 + 371330/193*y^11 - 90974/193*y^10 - 4991389/772*y^
+9 + 317305/193*y^8 + 7127131/772*y^7 - 1947859/772*y^6 - 2110371/386*y^5 + 1
+295397/772*y^4 + 224091/193*y^3 - 160649/386*y^2 - 19739/772*y + 4029/386), 
+x^2 + (12035/386*y^15 + 8337/386*y^14 - 566267/772*y^13 - 392327/772*y^12 + 
+4449119/772*y^11 + 3083327/772*y^10 - 7320431/386*y^9 - 2537889/193*y^8 + 99
+75849/386*y^7 + 3461954/193*y^6 - 10712085/772*y^5 - 7445851/772*y^4 + 19053
+21/772*y^3 + 1322419/772*y^2 - 25961/386*y - 17735/386), x^4 + (541/386*y^14
+ - 12373/386*y^12 + 46089/193*y^10 - 137477/193*y^8 + 311469/386*y^6 - 14604
+9/386*y^4 + 12308/193*y^2 - 686/193)*x^2 + (-765/772*y^14 + 8659/386*y^12 - 
+126401/772*y^10 + 90322/193*y^8 - 369401/772*y^6 + 38430/193*y^4 - 23865/772
+*y^2 + 553/386), x^4 + (2052/193*y^14 - 96741/386*y^12 + 763031/386*y^10 - 1
+265475/193*y^8 + 1759047/193*y^6 - 2000657/386*y^4 + 430823/386*y^2 - 13920/
+193)*x^2 + (-28293/772*y^14 + 166359/193*y^12 - 5225705/772*y^10 + 4295689/1
+93*y^8 - 23400329/772*y^6 + 6320077/386*y^4 - 2369321/772*y^2 + 45727/386), 
+x^4 + (-5155/193*y^15 - 15405/772*y^14 + 241769/386*y^13 + 180325/386*y^12 -
+ 944044/193*y^11 - 2807115/772*y^10 + 3072303/193*y^9 + 2267740/193*y^8 - 41
+02400/193*y^7 - 11896515/772*y^6 + 4334901/386*y^5 + 2988835/386*y^4 - 43236
+3/193*y^3 - 1014845/772*y^2 + 23130/193*y + 3602/193)*x^2 + (14406/193*y^15 
++ 38997/772*y^14 - 338423/193*y^13 - 455769/386*y^12 + 2652454/193*y^11 + 70
+72893/772*y^10 - 8698762/193*y^9 - 11354279/386*y^8 + 23683093/386*y^7 + 293
+19183/772*y^6 - 13169735/386*y^5 - 7082173/386*y^4 + 3014249/386*y^3 + 21677
+71/772*y^2 - 244333/386*y + 2027/386), x^4 + (-4705/193*y^15 - 4035/772*y^14
+ + 220691/386*y^13 + 47935/386*y^12 - 1722817/386*y^11 - 767525/772*y^10 + 5
+588151/386*y^9 + 654320/193*y^8 - 3658160/193*y^7 - 3833005/772*y^6 + 341354
+7/386*y^5 + 1159745/386*y^4 - 194425/386*y^3 - 441715/772*y^2 - 129025/386*y
+ - 9518/193)*x^2 + (69045/772*y^15 + 17477/772*y^14 - 1634153/772*y^13 - 208
+093/386*y^12 + 12990539/772*y^11 + 3347413/772*y^10 - 43749763/772*y^9 - 576
+5109/386*y^8 + 62724993/772*y^7 + 17320883/772*y^6 - 37352513/772*y^5 - 5621
+249/386*y^4 + 7969659/772*y^3 + 2742727/772*y^2 - 182407/772*y - 34007/386),
+ x^4 + (4705/193*y^15 - 4035/772*y^14 - 220691/386*y^13 + 47935/386*y^12 + 1
+722817/386*y^11 - 767525/772*y^10 - 5588151/386*y^9 + 654320/193*y^8 + 36581
+60/193*y^7 - 3833005/772*y^6 - 3413547/386*y^5 + 1159745/386*y^4 + 194425/38
+6*y^3 - 441715/772*y^2 + 129025/386*y - 9518/193)*x^2 + (-69045/772*y^15 + 1
+7477/772*y^14 + 1634153/772*y^13 - 208093/386*y^12 - 12990539/772*y^11 + 334
+7413/772*y^10 + 43749763/772*y^9 - 5765109/386*y^8 - 62724993/772*y^7 + 1732
+0883/772*y^6 + 37352513/772*y^5 - 5621249/386*y^4 - 7969659/772*y^3 + 274272
+7/772*y^2 + 182407/772*y - 34007/386), x^4 + (5155/193*y^15 - 15405/772*y^14
+ - 241769/386*y^13 + 180325/386*y^12 + 944044/193*y^11 - 2807115/772*y^10 - 
+3072303/193*y^9 + 2267740/193*y^8 + 4102400/193*y^7 - 11896515/772*y^6 - 433
+4901/386*y^5 + 2988835/386*y^4 + 432363/193*y^3 - 1014845/772*y^2 - 23130/19
+3*y + 3602/193)*x^2 + (-14406/193*y^15 + 38997/772*y^14 + 338423/193*y^13 - 
+455769/386*y^12 - 2652454/193*y^11 + 7072893/772*y^10 + 8698762/193*y^9 - 11
+354279/386*y^8 - 23683093/386*y^7 + 29319183/772*y^6 + 13169735/386*y^5 - 70
+82173/386*y^4 - 3014249/386*y^3 + 2167771/772*y^2 + 244333/386*y + 2027/386)
+]~
+[x^4 - 4*y*x^3 + (-1/2*y^14 + 1/2*y^10 - 7/2*y^6 + 15/2*y^2)*x^2 + (y^15 - y
+^11 + 7*y^7 - 7*y^3)*x + (1/2*y^12 - y^8 + 5/2*y^4 - 2), x^4 + 4*y*x^3 + (-1
+/2*y^14 + 1/2*y^10 - 7/2*y^6 + 15/2*y^2)*x^2 + (-y^15 + y^11 - 7*y^7 + 7*y^3
+)*x + (1/2*y^12 - y^8 + 5/2*y^4 - 2), x^4 + (-2*y^11 - 10*y^3)*x^3 + (9/2*y^
+14 - 1/2*y^10 + 55/2*y^6 - 7/2*y^2)*x^2 + (3*y^13 + y^9 + 17*y^5 + 3*y)*x + 
+(1/2*y^12 + y^8 + 5/2*y^4 + 4), x^4 + (2*y^11 + 10*y^3)*x^3 + (9/2*y^14 - 1/
+2*y^10 + 55/2*y^6 - 7/2*y^2)*x^2 + (-3*y^13 - y^9 - 17*y^5 - 3*y)*x + (1/2*y
+^12 + y^8 + 5/2*y^4 + 4), x^4 + (-2*y^13 - 10*y^5)*x^3 + (1/2*y^14 - 1/2*y^1
+0 + 7/2*y^6 - 15/2*y^2)*x^2 + (3*y^15 + y^11 + 17*y^7 + 3*y^3)*x + (1/2*y^12
+ - y^8 + 5/2*y^4 - 2), x^4 + (-y^13 - y^9 - 7*y^5 - 3*y)*x^3 + (7/2*y^14 - 1
+/2*y^10 + 37/2*y^6 - 3/2*y^2)*x^2 + (-y^15 + y^11 - 7*y^7 + 7*y^3)*x + (-1/2
+*y^12 - y^8 - 5/2*y^4 - 2), x^4 + (-y^13 + y^9 - 7*y^5 + 3*y)*x^3 + (-7/2*y^
+14 + 1/2*y^10 - 37/2*y^6 + 3/2*y^2)*x^2 + (-3*y^15 - y^11 - 17*y^7 - 3*y^3)*
+x + (-1/2*y^12 - y^8 - 5/2*y^4 - 2), x^4 + (y^13 - y^9 + 7*y^5 - 3*y)*x^3 + 
+(-7/2*y^14 + 1/2*y^10 - 37/2*y^6 + 3/2*y^2)*x^2 + (3*y^15 + y^11 + 17*y^7 + 
+3*y^3)*x + (-1/2*y^12 - y^8 - 5/2*y^4 - 2), x^4 + (y^13 + y^9 + 7*y^5 + 3*y)
+*x^3 + (7/2*y^14 - 1/2*y^10 + 37/2*y^6 - 3/2*y^2)*x^2 + (y^15 - y^11 + 7*y^7
+ - 7*y^3)*x + (-1/2*y^12 - y^8 - 5/2*y^4 - 2), x^4 + (2*y^13 + 10*y^5)*x^3 +
+ (1/2*y^14 - 1/2*y^10 + 7/2*y^6 - 15/2*y^2)*x^2 + (-3*y^15 - y^11 - 17*y^7 -
+ 3*y^3)*x + (1/2*y^12 - y^8 + 5/2*y^4 - 2), x^4 + (-4*y^15 - 24*y^7)*x^3 + (
+-9/2*y^14 + 1/2*y^10 - 55/2*y^6 + 7/2*y^2)*x^2 + (-y^13 + y^9 - 7*y^5 + 7*y)
+*x + (1/2*y^12 + y^8 + 5/2*y^4 + 4), x^4 + (-3*y^15 - y^11 - 17*y^7 - 7*y^3)
+*x^3 + (3/2*y^14 - 5/2*y^10 + 17/2*y^6 - 23/2*y^2)*x^2 + (3*y^13 + y^9 + 17*
+y^5 + 3*y)*x + (-1/2*y^12 + y^8 - 5/2*y^4 + 4), x^4 + (-3*y^15 + y^11 - 17*y
+^7 + 7*y^3)*x^3 + (-3/2*y^14 + 5/2*y^10 - 17/2*y^6 + 23/2*y^2)*x^2 + (-y^13 
++ y^9 - 7*y^5 + 7*y)*x + (-1/2*y^12 + y^8 - 5/2*y^4 + 4), x^4 + (3*y^15 - y^
+11 + 17*y^7 - 7*y^3)*x^3 + (-3/2*y^14 + 5/2*y^10 - 17/2*y^6 + 23/2*y^2)*x^2 
++ (y^13 - y^9 + 7*y^5 - 7*y)*x + (-1/2*y^12 + y^8 - 5/2*y^4 + 4), x^4 + (3*y
+^15 + y^11 + 17*y^7 + 7*y^3)*x^3 + (3/2*y^14 - 5/2*y^10 + 17/2*y^6 - 23/2*y^
+2)*x^2 + (-3*y^13 - y^9 - 17*y^5 - 3*y)*x + (-1/2*y^12 + y^8 - 5/2*y^4 + 4),
+ x^4 + (4*y^15 + 24*y^7)*x^3 + (-9/2*y^14 + 1/2*y^10 - 55/2*y^6 + 7/2*y^2)*x
+^2 + (y^13 - y^9 + 7*y^5 - 7*y)*x + (1/2*y^12 + y^8 + 5/2*y^4 + 4)]~
+[x^64 + 192*x^62 + 17568*x^60 + 1019520*x^58 + 42131676*x^56 + 1319651424*x^
+54 + 32559096528*x^52 + 649228312512*x^50 + 10651553826426*x^48 + 1456394385
+52224*x^46 + 1674922821206832*x^44 + 16307859539653056*x^42 + 13502367773216
+7696*x^40 + 953248899971965824*x^38 + 5745239175305568960*x^36 + 29556064271
+185194240*x^34 + 129595725382952883843*x^32 + 483002100692576612640*x^30 + 1
+523870714370199019760*x^28 + 4047489983524093705152*x^26 + 89858128286488620
+19536*x^24 + 16525310345394167002752*x^22 + 24893927149975603242048*x^20 + 3
+0294355815129821928192*x^18 + 29274561574319887883226*x^16 + 219878017711043
+40121824*x^14 + 12494344840480632094992*x^12 + 5187763623118143696192*x^10 +
+ 1502211081063677383836*x^8 + 283567347515314680480*x^6 + 311461554388845258
+72*x^4 + 1543354925530003776*x^2 + 8057044481403681]~
+[x + (-7/16*y^29 - 5/32*y^25 - 97/8*y^21 - 139/32*y^17 - 435/16*y^13 - 323/3
+2*y^9 - 21/4*y^5 - 77/32*y), x + (-11/32*y^29 - 1/8*y^25 - 309/32*y^21 - 55/
+16*y^17 - 797/32*y^13 - 7*y^9 - 355/32*y^5 + 9/16*y), x + (11/32*y^29 + 1/8*
+y^25 + 309/32*y^21 + 55/16*y^17 + 797/32*y^13 + 7*y^9 + 355/32*y^5 - 9/16*y)
+, x + (7/16*y^29 + 5/32*y^25 + 97/8*y^21 + 139/32*y^17 + 435/16*y^13 + 323/3
+2*y^9 + 21/4*y^5 + 77/32*y), x + (-21/32*y^31 - 587/32*y^23 + 1/16*y^19 - 14
+43/32*y^15 + 13/8*y^11 - 541/32*y^7 + 21/16*y^3), x + (-5/16*y^31 - 5/32*y^2
+7 - 35/4*y^23 - 139/32*y^19 - 349/16*y^15 - 323/32*y^11 - 57/8*y^7 - 109/32*
+y^3), x + (5/16*y^31 + 5/32*y^27 + 35/4*y^23 + 139/32*y^19 + 349/16*y^15 + 3
+23/32*y^11 + 57/8*y^7 + 109/32*y^3), x + (21/32*y^31 + 587/32*y^23 - 1/16*y^
+19 + 1443/32*y^15 - 13/8*y^11 + 541/32*y^7 - 21/16*y^3), x^4 + (-13/4*y^24 -
+ 91*y^16 - 909/4*y^8 - 169/2), x^4 + (-1/4*y^24 - 7*y^16 - 57/4*y^8 - 1/2)]~
+[8711099/70204123*y^14 - 3396450/70204123*y^13 - 230089978/70204123*y^12 + 7
+1459644/70204123*y^11 + 2039293754/70204123*y^10 - 522502724/70204123*y^9 - 
+7578045032/70204123*y^8 + 136410216/6382193*y^7 + 11598831422/70204123*y^6 -
+ 1582740050/70204123*y^5 - 6466526698/70204123*y^4 + 712163508/70204123*y^3 
++ 865017354/70204123*y^2 - 11706800/70204123*y + 7921687/70204123]
+[y, -123209112482/559553426209*y^11 - 236161397417/559553426209*y^10 + 52225
+05497467/559553426209*y^9 + 7627164004768/559553426209*y^8 - 68684785347690/
+559553426209*y^7 - 98327585435469/559553426209*y^6 + 334508906676131/5595534
+26209*y^5 + 508054669424553/559553426209*y^4 - 499853398148011/559553426209*
+y^3 - 780815391953932/559553426209*y^2 + 222263541657120/559553426209*y + 69
+71304961116/11905392047, -176690268281/1119106852418*y^11 - 641210922141/223
+8213704836*y^10 + 3748626002639/559553426209*y^9 + 20339566453621/2238213704
+836*y^8 - 49228932581896/559553426209*y^7 - 262748733927015/2238213704836*y^
+6 + 958463738831775/2238213704836*y^5 + 1369627950576313/2238213704836*y^4 -
+ 1439017147702531/2238213704836*y^3 - 2121737405539667/2238213704836*y^2 + 6
+35967216650047/2238213704836*y + 19194169373855/47621568188, -68393259315/11
+19106852418*y^11 - 143769468519/1119106852418*y^10 + 2890095068571/111910685
+2418*y^9 + 4773072512403/1119106852418*y^8 - 38008182942981/1119106852418*y^
+7 - 30714902181882/559553426209*y^6 + 92097658832451/559553426209*y^5 + 1570
+24332093039/559553426209*y^4 - 134021586074273/559553426209*y^3 - 2404025913
+19962/559553426209*y^2 + 58771325675463/559553426209*y + 4293056883813/23810
+784094, -26071422312/559553426209*y^11 - 45431912634/559553426209*y^10 + 110
+4266993455/559553426209*y^9 + 1416464504385/559553426209*y^8 - 1440773326506
+3/559553426209*y^7 - 18255285107310/559553426209*y^6 + 69227537719650/559553
+426209*y^5 + 94877281869432/559553426209*y^4 - 100252280561328/559553426209*
+y^3 - 143113043669604/559553426209*y^2 + 41073387952215/559553426209*y + 128
+0223975324/11905392047, -16163557893/1119106852418*y^11 - 64908872441/223821
+3704836*y^10 + 685522417391/1119106852418*y^9 + 2130089587017/2238213704836*
+y^8 - 4531221497592/559553426209*y^7 - 27490869176393/2238213704836*y^6 + 89
+319951302323/2238213704836*y^5 + 141812611520979/2238213704836*y^4 - 1390748
+45426265/2238213704836*y^3 - 220351698213059/2238213704836*y^2 + 72725691407
+453/2238213704836*y + 2005246545505/47621568188, 9609209575/2238213704836*y^
+11 + 25387385845/559553426209*y^10 - 383645617715/2238213704836*y^9 - 208031
+3216715/1119106852418*y^8 + 6033944605985/2238213704836*y^7 + 53081257123225
+/2238213704836*y^6 - 33975296862719/2238213704836*y^5 - 255089221619815/2238
+213704836*y^4 + 47525895430055/2238213704836*y^3 + 398412597852115/223821370
+4836*y^2 - 30904572571305/2238213704836*y - 890343326655/11905392047, 522817
+60559/2238213704836*y^11 + 14008471016/559553426209*y^10 - 2227004686929/223
+8213704836*y^9 - 336992949231/559553426209*y^8 + 28726688292139/223821370483
+6*y^7 + 17917872421753/2238213704836*y^6 - 137376604832131/2238213704836*y^5
+ - 105226511459813/2238213704836*y^4 + 207364021022899/2238213704836*y^3 + 1
+70780696189363/2238213704836*y^2 - 84647799904795/2238213704836*y - 44402858
+6648/11905392047, 106577461171/2238213704836*y^11 + 236835106673/22382137048
+36*y^10 - 4499294217001/2238213704836*y^9 - 8003166405117/2238213704836*y^8 
++ 59306288158573/2238213704836*y^7 + 25836461923340/559553426209*y^6 - 72093
+028502701/559553426209*y^5 - 264172433170095/1119106852418*y^4 + 21076054618
+3675/1119106852418*y^3 + 203797855675948/559553426209*y^2 - 94758333876515/1
+119106852418*y - 7259569285101/47621568188, 175225361355/2238213704836*y^11 
++ 367043962553/2238213704836*y^10 - 7404910374729/2238213704836*y^9 - 121761
+49738023/2238213704836*y^8 + 97370244941211/2238213704836*y^7 + 784051152149
+35/1119106852418*y^6 - 117954333048705/559553426209*y^5 - 401317015113399/11
+19106852418*y^4 + 171430955359538/559553426209*y^3 + 307321505664217/5595534
+26209*y^2 - 148696095658675/1119106852418*y - 10923889360787/47621568188, 18
+4746017659/1119106852418*y^11 + 182236626649/559553426209*y^10 - 78287971960
+99/1119106852418*y^9 - 5941166792275/559553426209*y^8 + 103098612489213/1119
+106852418*y^7 + 153130332934513/1119106852418*y^6 - 502641225832273/11191068
+52418*y^5 - 789225051651289/1119106852418*y^4 + 749848149414089/111910685241
+8*y^3 + 1216440499421323/1119106852418*y^2 - 334431034048089/1119106852418*y
+ - 5475399545957/11905392047, 421608384/2321798449*y^11 + 1306271719/4643596
+898*y^10 - 17925705039/2321798449*y^9 - 38874965891/4643596898*y^8 + 2342247
+56928/2321798449*y^7 + 502105436649/4643596898*y^6 - 2270324281083/464359689
+8*y^5 - 2658757184017/4643596898*y^4 + 3428983243105/4643596898*y^3 + 405157
+9820087/4643596898*y^2 - 1510458181035/4643596898*y - 36134279083/98799934]
+
+[x + (-y + 1) 1]
+
+[x^2 + (y + 2)*x + (y^2 + y + 1) 1]
+
+[x^2 + (y + 2)*x + (1/25*y^8 - 3/5*y^5 - 87/25*y^2 + y + 1) 1]
+
+[x^2 + (-2/15*y^7 + 7/3*y^4 + 79/15*y + 2)*x + (1/25*y^8 - 2/15*y^7 - 3/5*y^
+5 + 7/3*y^4 - 87/25*y^2 + 79/15*y + 1) 1]
+
+[x^2 + (2/15*y^7 - 7/3*y^4 - 94/15*y + 2)*x + (1/25*y^8 + 2/15*y^7 - 3/5*y^5
+ - 7/3*y^4 - 87/25*y^2 - 94/15*y + 1) 1]
+
+
+[        x - y 3]
+
+[      x^2 + y 4]
+
+[x^3 - y*x + y 5]
+
+
+[x - y 1]
+
+[x + y 1]
+
+
+[x - y 1]
+
+[x + y 1]
+
+
+[  x + 1 3]
+
+[2*x + 1 2]
+
+9
+[x - y, x + y, x + (-373/2372*y^15 - 69/2372*y^13 + 2205/593*y^11 - 56641/47
+44*y^9 + 30823/1186*y^7 - 16782/593*y^5 + 11109/593*y^3 - 15285/2372*y), x +
+ (373/2372*y^15 + 69/2372*y^13 - 2205/593*y^11 + 56641/4744*y^9 - 30823/1186
+*y^7 + 16782/593*y^5 - 11109/593*y^3 + 15285/2372*y), x^4 + (-231/1186*y^14 
+- 313/1186*y^12 + 10535/2372*y^10 - 5568/593*y^8 + 10658/593*y^6 - 4133/593*
+y^4 + 1731/1186*y^2 - 648/593)*x^2 + (81/1186*y^14 + 181/2372*y^12 - 937/593
+*y^10 + 4313/1186*y^8 - 4207/593*y^6 + 5463/1186*y^4 - 1270/593*y^2 + 1883/5
+93), x^4 + (-93/1186*y^14 - 49/1186*y^12 + 4457/2372*y^10 - 3058/593*y^8 + 6
+170/593*y^6 - 6793/593*y^4 + 8429/1186*y^2 - 2140/593)*x^2 + (81/1186*y^14 +
+ 181/2372*y^12 - 937/593*y^10 + 4313/1186*y^8 - 4207/593*y^6 + 5463/1186*y^4
+ - 1270/593*y^2 + 1883/593), x^4 + (159/2372*y^14 + 281/1186*y^12 - 3437/237
+2*y^10 - 217/593*y^8 + 4085/1186*y^6 - 8339/593*y^4 + 12947/1186*y^2 + 146/5
+93)*x^2 + (-81/1186*y^14 - 181/2372*y^12 + 937/593*y^10 - 4313/1186*y^8 + 42
+07/593*y^6 - 5463/1186*y^4 + 1270/593*y^2 + 489/593)]~
+36
+9
+x^6 - x^5 - 76*x^4 + 60*x^3 + 1140*x^2 + 1155*x - 695
+a^36 - 140*a^34 + 8402*a^32 - 288950*a^30 + 6406703*a^28 - 97539585*a^26 + 1
+059042259*a^24 - 8396309325*a^22 + 49297808195*a^20 - 215811263825*a^18 + 70
+4643601819*a^16 - 1705347924285*a^14 + 3017070472643*a^12 - 3809285900925*a^
+10 + 3299709349267*a^8 - 1836758642090*a^6 + 585303753211*a^4 - 85409835875*
+a^2 + 3969153125
+[28870969412445129304/150333898582544875079637735*a^34 - 2391040639738112524
+6214047/902003391495269250477826410000*a^32 + 1408280580925577743188914959/9
+02003391495269250477826410000*a^30 - 47270903738475878949122441507/902003391
+495269250477826410000*a^28 + 1016201980774469757442024408441/902003391495269
+250477826410000*a^26 - 1653232481585973809555097282799/100222599055029916719
+758490000*a^24 + 30766570387863948626786349252629/18040067829905385009556528
+2000*a^22 - 25494617146287004973995153489163/20044519811005983343951698000*a
+^20 + 54269387427573548559506499935047/7843507752132776091111534000*a^18 - 4
+967271194565117464292207548275283/180400678299053850095565282000*a^16 + 1439
+0821273796469411671423134660909/180400678299053850095565282000*a^14 - 166375
+51485416921371399599192940627/100222599055029916719758490000*a^12 + 21844296
+4149682251745527062058729581/902003391495269250477826410000*a^10 - 715047828
+08114491609686387087461101/300667797165089750159275470000*a^8 + 132463576654
+385501140215626560461749/902003391495269250477826410000*a^6 - 45521136780059
+303695022254815264059/902003391495269250477826410000*a^4 + 48834547745749769
+48426917348117/644288136782335178912733150*a^2 - 31947174916220852422727719/
+91469478158982811558152, 3251350527072966578858963/4510016957476346252389132
+050000*a^34 - 224934549971331898950636911/2255008478738173126194566025000*a^
+32 + 8860674919294055555049660713/1503338985825448750796377350000*a^30 - 995
+57773024274624590944467659/501112995275149583598792450000*a^28 + 19366458770
+607880120052179046813/4510016957476346252389132050000*a^26 - 285516842858115
+285212427862265807/4510016957476346252389132050000*a^24 + 595519111738991674
+011764093345989/902003391495269250477826410000*a^22 - 4489547975447934229698
+580858261951/902003391495269250477826410000*a^20 + 1076139228842461035371382
+151402807/39217538760663880455557670000*a^18 - 11124367147850754939979966580
+304871/100222599055029916719758490000*a^16 + 1479061897966350800416026745438
+951477/4510016957476346252389132050000*a^14 - 315025857271480248157232912303
+3742463/4510016957476346252389132050000*a^12 + 47206506294320482873673946762
+98685961/4510016957476346252389132050000*a^10 - 4777649748033279268548939533
+863439719/4510016957476346252389132050000*a^8 + 3048859768911341351494073063
+483963897/4510016957476346252389132050000*a^6 - 5426099614227091504278277319
+81252029/2255008478738173126194566025000*a^4 + 64587456786717997350050100662
+819/1718101698086227143767288400*a^2 - 165798995832125073281674123/914694781
+58982811558152, -704379081794904012990253/9020033914952692504778264100000*a^
+35 - 185571858472598531261171/501112995275149583598792450000*a^34 + 48715152
+518881440798956651/4510016957476346252389132050000*a^33 + 462092978647254762
+515957747/9020033914952692504778264100000*a^32 - 143866499279758588432618534
+1/2255008478738173126194566025000*a^31 - 13648984531486738963024628227/45100
+16957476346252389132050000*a^30 + 96938310360867353475048806483/451001695747
+6346252389132050000*a^29 + 919883530735586552464580524411/902003391495269250
+4778264100000*a^28 - 232651976540261816658545907611/501112995275149583598792
+450000*a^27 - 19875071662837802701492211372933/90200339149526925047782641000
+00*a^26 + 30845327236112782336432658557121/4510016957476346252389132050000*a
+^25 + 97629122086975105817272227332809/3006677971650897501592754700000*a^24 
+- 21424985574841499437846887948389/300667797165089750159275470000*a^23 - 610
+575128275733935942680874074229/1804006782990538500955652820000*a^22 + 484017
+286454879828969736789559843/902003391495269250477826410000*a^21 + 1533419100
+031007894056656543524977/601335594330179500318550940000*a^20 - 1158671856773
+61369373967739807791/39217538760663880455557670000*a^19 - 110190466434352089
+3720157677667127/78435077521327760911115340000*a^18 + 1537709883158934201041
+232269889911/128857627356467035782546630000*a^17 + 1024351631773365912595517
+71572103159/1804006782990538500955652820000*a^16 - 5292233983819996489701399
+1241504027/1503338985825448750796377350000*a^15 - 15119400756767591614096930
+48855920037/9020033914952692504778264100000*a^14 + 4823557309278799269424083
+0453221347/644288136782335178912733150000*a^13 + 107239762638007600223629533
+3324062521/3006677971650897501592754700000*a^12 - 16846944747925094111730588
+2802587131/1503338985825448750796377350000*a^11 - 48160226697757002253310017
+91553165721/9020033914952692504778264100000*a^10 + 1491287229390570953499395
+745247369/13148737485353779161484350000*a^9 + 162299919830912395515658180697
+4277013/3006677971650897501592754700000*a^8 - 327237997164237860693483056003
+293031/4510016957476346252389132050000*a^7 - 3103794645076791741342476648584
+879777/9020033914952692504778264100000*a^6 + 2348322029477720044136044070025
+03923/9020033914952692504778264100000*a^5 + 11035578261555912186076185579856
+11613/9020033914952692504778264100000*a^4 - 37526865321657483383315681982802
+9/90200339149526925047782641000*a^3 - 49201004178902125753179356208497/25771
+52547129340715650932600*a^2 + 455861078300944766857933289/213428782370959893
+6356880*a + 336438999245457567382406693/365877912635931246232608, -457997268
+286495201285601/9020033914952692504778264100000*a^35 - 155437231387909379055
+577/1804006782990538500955652820000*a^34 + 63219462123808708551299959/902003
+3914952692504778264100000*a^33 + 10732815328231466161638631/9020033914952692
+50477826410000*a^32 - 1241268191638711501136747401/3006677971650897501592754
+700000*a^31 - 162691836407833782279946/231996757071828510925366875*a^30 + 41
+671559320168530327971893369/3006677971650897501592754700000*a^29 + 472202100
+3894765857767375379/200445198110059833439516980000*a^28 - 268827453299542151
+9377197948701/9020033914952692504778264100000*a^27 - 91447940232848524115401
+7943217/1804006782990538500955652820000*a^26 + 39380597302365530060214378192
+779/9020033914952692504778264100000*a^25 + 13404987653711757838118111598667/
+1804006782990538500955652820000*a^24 - 81492696927754851227554074520033/1804
+006782990538500955652820000*a^23 - 27755367080515496240602993106981/36080135
+6598107700191130564000*a^22 + 608451862766595678082876478420347/180400678299
+0538500955652820000*a^21 + 207309689387565154271678626939871/360801356598107
+700191130564000*a^20 - 144141144819342089103549365967439/7843507752132776091
+1115340000*a^19 - 49116300327361576889751394682183/1568701550426555218222306
+8000*a^18 + 163228993853205803064485212811743/22271688678895537048835220000*
+a^17 + 1501366475076386034781931026134473/120267118866035900063710188000*a^1
+6 - 192040496313746347169675964373314529/9020033914952692504778264100000*a^1
+5 - 65378470826900674859623854989910833/1804006782990538500955652820000*a^14
+ + 400908998300724067746255602794046311/9020033914952692504778264100000*a^13
+ + 136351102083667187315285017348776623/1804006782990538500955652820000*a^12
+ - 586838332855949729229685411031808997/9020033914952692504778264100000*a^11
+ - 199368556080951864152805639007833629/1804006782990538500955652820000*a^10
+ + 578030985689012831826453965281320943/9020033914952692504778264100000*a^9 
++ 196244779045524955444897983850505039/1804006782990538500955652820000*a^8 -
+ 357480334490463612280117630223936969/9020033914952692504778264100000*a^7 - 
+121476601421295423170534909540278573/1804006782990538500955652820000*a^6 + 3
+0624370220614590128641706307930719/2255008478738173126194566025000*a^5 + 104
+63389029506180036157409002660637/451001695747634625238913205000*a^4 - 861866
+1493592985666542036187341/4295254245215567859418221000*a^3 - 266859198823298
+552776653451403/76360075470498984167435040*a^2 + 591232823580313844716106279
+/6402863471128796809070640*a + 59236281207552249649513295/365877912635931246
+232608, 457997268286495201285601/9020033914952692504778264100000*a^35 - 1554
+37231387909379055577/1804006782990538500955652820000*a^34 - 6321946212380870
+8551299959/9020033914952692504778264100000*a^33 + 10732815328231466161638631
+/902003391495269250477826410000*a^32 + 1241268191638711501136747401/30066779
+71650897501592754700000*a^31 - 162691836407833782279946/23199675707182851092
+5366875*a^30 - 41671559320168530327971893369/3006677971650897501592754700000
+*a^29 + 4722021003894765857767375379/200445198110059833439516980000*a^28 + 2
+688274532995421519377197948701/9020033914952692504778264100000*a^27 - 914479
+402328485241154017943217/1804006782990538500955652820000*a^26 - 393805973023
+65530060214378192779/9020033914952692504778264100000*a^25 + 1340498765371175
+7838118111598667/1804006782990538500955652820000*a^24 + 81492696927754851227
+554074520033/1804006782990538500955652820000*a^23 - 277553670805154962406029
+93106981/360801356598107700191130564000*a^22 - 60845186276659567808287647842
+0347/1804006782990538500955652820000*a^21 + 20730968938756515427167862693987
+1/360801356598107700191130564000*a^20 + 144141144819342089103549365967439/78
+435077521327760911115340000*a^19 - 49116300327361576889751394682183/15687015
+504265552182223068000*a^18 - 163228993853205803064485212811743/2227168867889
+5537048835220000*a^17 + 1501366475076386034781931026134473/12026711886603590
+0063710188000*a^16 + 192040496313746347169675964373314529/902003391495269250
+4778264100000*a^15 - 65378470826900674859623854989910833/1804006782990538500
+955652820000*a^14 - 400908998300724067746255602794046311/9020033914952692504
+778264100000*a^13 + 136351102083667187315285017348776623/1804006782990538500
+955652820000*a^12 + 586838332855949729229685411031808997/9020033914952692504
+778264100000*a^11 - 199368556080951864152805639007833629/1804006782990538500
+955652820000*a^10 - 578030985689012831826453965281320943/9020033914952692504
+778264100000*a^9 + 196244779045524955444897983850505039/18040067829905385009
+55652820000*a^8 + 357480334490463612280117630223936969/902003391495269250477
+8264100000*a^7 - 121476601421295423170534909540278573/1804006782990538500955
+652820000*a^6 - 30624370220614590128641706307930719/225500847873817312619456
+6025000*a^5 + 10463389029506180036157409002660637/45100169574763462523891320
+5000*a^4 + 8618661493592985666542036187341/4295254245215567859418221000*a^3 
+- 266859198823298552776653451403/76360075470498984167435040*a^2 - 5912328235
+80313844716106279/6402863471128796809070640*a + 59236281207552249649513295/3
+65877912635931246232608, 704379081794904012990253/90200339149526925047782641
+00000*a^35 - 185571858472598531261171/501112995275149583598792450000*a^34 - 
+48715152518881440798956651/4510016957476346252389132050000*a^33 + 4620929786
+47254762515957747/9020033914952692504778264100000*a^32 + 1438664992797585884
+326185341/2255008478738173126194566025000*a^31 - 136489845314867389630246282
+27/4510016957476346252389132050000*a^30 - 96938310360867353475048806483/4510
+016957476346252389132050000*a^29 + 919883530735586552464580524411/9020033914
+952692504778264100000*a^28 + 232651976540261816658545907611/5011129952751495
+83598792450000*a^27 - 19875071662837802701492211372933/902003391495269250477
+8264100000*a^26 - 30845327236112782336432658557121/4510016957476346252389132
+050000*a^25 + 97629122086975105817272227332809/30066779716508975015927547000
+00*a^24 + 21424985574841499437846887948389/300667797165089750159275470000*a^
+23 - 610575128275733935942680874074229/1804006782990538500955652820000*a^22 
+- 484017286454879828969736789559843/902003391495269250477826410000*a^21 + 15
+33419100031007894056656543524977/601335594330179500318550940000*a^20 + 11586
+7185677361369373967739807791/39217538760663880455557670000*a^19 - 1101904664
+343520893720157677667127/78435077521327760911115340000*a^18 - 15377098831589
+34201041232269889911/128857627356467035782546630000*a^17 + 10243516317733659
+1259551771572103159/1804006782990538500955652820000*a^16 + 52922339838199964
+897013991241504027/1503338985825448750796377350000*a^15 - 151194007567675916
+1409693048855920037/9020033914952692504778264100000*a^14 - 48235573092787992
+694240830453221347/644288136782335178912733150000*a^13 + 1072397626380076002
+236295333324062521/3006677971650897501592754700000*a^12 + 168469447479250941
+117305882802587131/1503338985825448750796377350000*a^11 - 481602266977570022
+5331001791553165721/9020033914952692504778264100000*a^10 - 14912872293905709
+53499395745247369/13148737485353779161484350000*a^9 + 1622999198309123955156
+581806974277013/3006677971650897501592754700000*a^8 + 3272379971642378606934
+83056003293031/4510016957476346252389132050000*a^7 - 31037946450767917413424
+76648584879777/9020033914952692504778264100000*a^6 - 23483220294777200441360
+4407002503923/9020033914952692504778264100000*a^5 + 110355782615559121860761
+8557985611613/9020033914952692504778264100000*a^4 + 375268653216574833833156
+819828029/90200339149526925047782641000*a^3 - 492010041789021257531793562084
+97/2577152547129340715650932600*a^2 - 455861078300944766857933289/2134287823
+709598936356880*a + 336438999245457567382406693/365877912635931246232608]
+[x^12 - x^11 + 7/13*x^10 - 3/13*x^9 + 15/169*x^8 - 5/169*x^7 + 19/2197*x^6 -
+ 5/2197*x^5 + 15/28561*x^4 - 3/28561*x^3 + 7/371293*x^2 - 1/371293*x + 1/482
+6809, x^12 + x^11 + 7/13*x^10 + 3/13*x^9 + 15/169*x^8 + 5/169*x^7 + 19/2197*
+x^6 + 5/2197*x^5 + 15/28561*x^4 + 3/28561*x^3 + 7/371293*x^2 + 1/371293*x + 
+1/4826809]~
+[x - 3, x + (-1/2*y + 1/2), x + (1/2*y + 1/2)]~
+
+[x + (-y^7 - 1) 1]
+
+[ x + (y^7 + 1) 1]
+
+[x + (-2*y^3 + y), x + (-4/3*y^3 + 2/3*y), x + (-2/3*y^3 + 1/3*y)]~
+[x + (-1/3*y - 1/3), x + (2/3*y + 2/3)]~
+  *** nffactor: Warning: non-monic polynomial. Result of the form [nf,c].
+
+[x + (-2/3*y + 1/3) 1]
+
+[ x + (2/3*y - 1/3) 1]
+
+  *** nfroots: Warning: non-monic polynomial. Result of the form [nf,c].
+[-2/3*y + 1/3, 2/3*y - 1/3]
+
+[x + (-26388279066648/85070591730466729623209871765718171955*t^5 + 131941395
+333267/85070591730466729623209871765718171955*t^4 + 967140655690823730462717
+32/85070591730466729623209871765718171955*t^3 - 290142196708032170441048136/
+85070591730466729623209871765718171955*t^2 - 7443676776386824622360021639744
+6118676/85070591730466729623209871765718171955*t + 7443676776373163760598366
+6543681076444/85070591730466729623209871765718171955) 1]
+
+[x^2 + (26388279066648/85070591730466729623209871765718171955*t^5 - 13194139
+5333267/85070591730466729623209871765718171955*t^4 - 96714065569082373046271
+732/85070591730466729623209871765718171955*t^3 + 290142196708032170441048136
+/85070591730466729623209871765718171955*t^2 + 744367677638682462236002163974
+46118676/85070591730466729623209871765718171955*t - 744367677637316376059836
+66543681076444/85070591730466729623209871765718171955)*x + (54/8507059173046
+6729623209871765718171955*t^5 + 19342813113869251167387394/85070591730466729
+623209871765718171955*t^4 - 77371252455674916762549976/850705917304667296232
+09871765718171955*t^3 - 42535295865117307932921126639575760427/8507059173046
+6729623209871765718171955*t^2 + 85070591730773796781391952391721450758/85070
+591730466729623209871765718171955*t + 23384026197336981987123709535795926763
+206176932728/85070591730466729623209871765718171955) 1]
+
+x^6 + 10/7*x^5 - 867/49*x^4 - 76/245*x^3 + 3148/35*x^2 - 25944/245*x + 48771
+/1225
+
+[  x - 1 1]
+
+[  x + 1 1]
+
+[x^2 - 5 1]
+
+[x^2 - 2 1]
+
+
+[                       x + 1 1]
+
+[                     x^2 + 1 1]
+
+[     x^4 - x^3 + x^2 - x + 1 1]
+
+[                     x^4 + 1 1]
+
+[     x^4 + x^3 + x^2 + x + 1 1]
+
+[   x^8 - x^6 + x^4 - x^2 + 1 1]
+
+[                     x^8 + 1 1]
+
+[ x^16 - x^12 + x^8 - x^4 + 1 1]
+
+[x^32 - x^24 + x^16 - x^8 + 1 1]
+
+10
+10
+[0]
+[x + (-314226370217524044*y + 1473852319020386314), x + (314226370217524044*
+y - 1473852319020386314)]~
+Total time spent: 8061
diff --git a/src/test/32/nfhilbert b/src/test/32/nfhilbert
new file mode 100644
index 0000000..84ac7e8
--- /dev/null
+++ b/src/test/32/nfhilbert
@@ -0,0 +1,107 @@
+[-1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1]
+[-1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1]
+-1
+  ***   at top-level: nfhilbert(nf,Mod(0,3
+  ***                 ^--------------------
+  *** nfhilbert: domain error in nfhilbert: a = 0
+  ***   at top-level: nfhilbert(nf,3,0.,pr
+  ***                 ^--------------------
+  *** nfhilbert: domain error in nfhilbert: b = 0
+1
+1
+-1
+[1, 2]: 1
+[1, 3]: 1
+[1, 4]: error("precision too low in hilbert.")
+[1, 5]: error("precision too low in hilbert.")
+[1, 6]: 1
+[1, 7]: 1
+[1, 8]: 0
+[1, 9]: error("precision too low in hilbert.")
+[1, 10]: 1
+[1, 11]: 1
+[2, 3]: 1
+[2, 4]: error("precision too low in hilbert.")
+[2, 5]: error("precision too low in hilbert.")
+[2, 6]: 1
+[2, 7]: 1
+[2, 8]: 0
+[2, 9]: error("precision too low in hilbert.")
+[2, 10]: 1
+[2, 11]: 1
+[3, 4]: error("forbidden hilbert t_REAL , t_INTMOD.")
+[3, 5]: error("forbidden hilbert t_REAL , t_INTMOD.")
+[3, 6]: error("forbidden hilbert t_REAL , t_INTMOD.")
+[3, 7]: error("forbidden hilbert t_REAL , t_INTMOD.")
+[3, 8]: error("forbidden hilbert t_REAL , t_INTMOD.")
+[3, 9]: error("forbidden hilbert t_REAL , t_PADIC.")
+[3, 10]: error("forbidden hilbert t_REAL , t_PADIC.")
+[3, 11]: error("forbidden hilbert t_REAL , t_PADIC.")
+[4, 5]: error("precision too low in hilbert.")
+[4, 6]: error("precision too low in hilbert.")
+[4, 7]: error("precision too low in hilbert.")
+[4, 8]: error("precision too low in hilbert.")
+[4, 9]: error("precision too low in hilbert.")
+[4, 10]: error("precision too low in hilbert.")
+[4, 11]: error("precision too low in hilbert.")
+[5, 6]: error("precision too low in hilbert.")
+[5, 7]: error("precision too low in hilbert.")
+[5, 8]: error("precision too low in hilbert.")
+[5, 9]: error("precision too low in hilbert.")
+[5, 10]: error("precision too low in hilbert.")
+[5, 11]: error("precision too low in hilbert.")
+[6, 7]: error("inconsistent moduli in hilbert: 3 != 8")
+[6, 8]: error("inconsistent moduli in hilbert: 5 != 8")
+[6, 9]: error("inconsistent moduli in hilbert: 8 != 2")
+[6, 10]: error("inconsistent moduli in hilbert: 8 != 2")
+[6, 11]: error("inconsistent moduli in hilbert: 8 != 5")
+[7, 8]: error("inconsistent moduli in hilbert: 5 != 3")
+[7, 9]: error("inconsistent moduli in hilbert: 3 != 2")
+[7, 10]: error("inconsistent moduli in hilbert: 3 != 2")
+[7, 11]: error("inconsistent moduli in hilbert: 3 != 5")
+[8, 9]: error("inconsistent moduli in hilbert: 5 != 2")
+[8, 10]: error("inconsistent moduli in hilbert: 5 != 2")
+[8, 11]: 0
+[9, 10]: error("precision too low in hilbert.")
+[9, 11]: error("precision too low in hilbert.")
+[10, 11]: error("inconsistent moduli in hilbert: 2 != 5")
+p = 0:
+1: 1
+2: 1
+3: 1
+4: error("inconsistent moduli in hilbert: 2 != oo")
+5: error("inconsistent moduli in hilbert: 4 != oo")
+6: error("inconsistent moduli in hilbert: 8 != oo")
+7: error("inconsistent moduli in hilbert: 3 != oo")
+8: error("inconsistent moduli in hilbert: 5 != oo")
+9: error("inconsistent moduli in hilbert: 0 != 2")
+10: error("inconsistent moduli in hilbert: 0 != 2")
+11: error("inconsistent moduli in hilbert: 0 != 5")
+p = 2:
+1: 1
+2: -1
+3: error("inconsistent moduli in hilbert: 2 != oo")
+4: error("precision too low in hilbert.")
+5: error("precision too low in hilbert.")
+6: 1
+7: error("precision too low in hilbert.")
+8: error("precision too low in hilbert.")
+9: error("precision too low in hilbert.")
+10: 1
+11: error("inconsistent moduli in hilbert: 2 != 5")
+p = 5:
+1: 1
+2: 1
+3: error("inconsistent moduli in hilbert: 5 != oo")
+4: error("inconsistent moduli in hilbert: 2 != 5")
+5: error("inconsistent moduli in hilbert: 4 != 5")
+6: error("inconsistent moduli in hilbert: 8 != 5")
+7: error("inconsistent moduli in hilbert: 3 != 5")
+8: error("precision too low in hilbert.")
+9: error("inconsistent moduli in hilbert: 5 != 2")
+10: error("inconsistent moduli in hilbert: 5 != 2")
+11: 1
+-1
+-1
+1
+Total time spent: 1084
diff --git a/src/test/32/nfields b/src/test/32/nfields
new file mode 100644
index 0000000..42861e7
--- /dev/null
+++ b/src/test/32/nfields
@@ -0,0 +1,751 @@
+   echo = 1 (on)
+? p2=Pol([1,3021,-786303,-6826636057,-546603588746,3853890514072057]);
+? fa=[11699,6;2392997,2;4987333019653,2];
+? setrand(1);N=10^8;a=matrix(3,5,j,k,vectorv(5,l,random\N));
+? nfpol=x^5-5*x^3+5*x+25;nf=nfinit(nfpol)
+[x^5 - 5*x^3 + 5*x + 25, [1, 2], 595125, 45, [[1, -1.08911514572050482502495
+27946671612684, -2.4285174907194186068992069565359418365, 0.7194669112891317
+8943997506477288225737, -2.5558200350691694950646071159426779972; 1, -0.1383
+8372073406036365047976417441696637 - 0.4918163765776864349975328551474152510
+7*I, 1.9647119211288133163138753392090569931 + 0.809714924188978951282940822
+19556466857*I, -0.072312766896812300380582649294307897074 + 2.19808037538462
+76641195195160383234878*I, -0.98796319352507039803950539735452837193 + 1.570
+1452385894131769052374806001981109*I; 1, 1.682941293594312776162956161507997
+6006 + 2.0500351226010726172974286983598602164*I, -0.75045317576910401286427
+186094108607489 + 1.3101462685358123283560773619310445916*I, -0.787420688747
+75359433940488309213323154 + 2.1336633893126618034168454610457936018*I, 1.26
+58732110596551455718089553258673705 - 2.716479010374315056657802803578983483
+5*I], [1, -1.0891151457205048250249527946671612684, -2.428517490719418606899
+2069565359418365, 0.71946691128913178943997506477288225737, -2.5558200350691
+694950646071159426779972; 1, -0.63020009731174679864801261932183221743, 2.77
+44268453177922675968161614046216617, 2.1257676084878153637389368667440155907
+, 0.58218204506434277886573208324566973897; 1, 0.353432655843626071347053090
+97299828470, 1.1549969969398343650309345170134923246, -2.2703931422814399645
+001021653326313849, -2.5581084321144835749447428779547264828; 1, 3.732976416
+1953853934603848598678578170, 0.55969309276670831549180550098995851667, 1.34
+62427005649082090774405779536603703, -1.450605799314659911085993848253116112
+9; 1, -0.36709382900675984113447253685186261580, -2.060599444304916341220349
+2228721306665, -2.9210840780604153977562503441379268334, 3.98235222143397020
+22296117589048508540], [1, -1, -2, 1, -3; 1, -1, 3, 2, 1; 1, 0, 1, -2, -3; 1
+, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, -10, 20; 0, -5
+, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0, 200, 110, 17
+7; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [63, 3,
+ 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, -14, -9; -9, 1
+6, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65, 118, -116; 53
+, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -34]], [3, 5, 2
+3]], [-2.4285174907194186068992069565359418365, 1.96471192112881331631387533
+92090569931 + 0.80971492418897895128294082219556466857*I, -0.750453175769104
+01286427186094108607489 + 1.3101462685358123283560773619310445916*I], [1, 1/
+15*x^4 - 2/3*x^2 + 1/3*x + 4/3, x, 2/15*x^4 - 1/3*x^2 + 2/3*x - 1/3, -1/15*x
+^4 + 1/3*x^3 + 1/3*x^2 - 4/3*x - 2/3], [1, 0, 3, 1, 10; 0, 0, -2, 1, -5; 0, 
+1, 0, 3, -5; 0, 0, 1, 1, 10; 0, 0, 0, 3, 0], [1, 0, 0, 0, 0, 0, -1, -1, -2, 
+4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 1, 0, 0, 0, 1, 1,
+ -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3, -3; 0, 0, 1, 0, 
+0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, -2, -1, -1; 0, 0,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0, 2, 0, -1; 0, 0,
+ 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0, 1, 1, 0, 0, -1]]
+? nfinit(nfpol,2)
+[x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*x + 2, [1, 2], 595125, 4, [[1, -1.089115145
+7205048250249527946671612684, -2.4285174907194186068992069565359418365, 0.71
+946691128913178943997506477288225735, -2.55582003506916949506460711594267799
+71; 1, -0.13838372073406036365047976417441696637 + 0.49181637657768643499753
+285514741525107*I, 1.9647119211288133163138753392090569931 - 0.8097149241889
+7895128294082219556466857*I, -0.072312766896812300380582649294307897123 - 2.
+1980803753846276641195195160383234878*I, -0.98796319352507039803950539735452
+837195 - 1.5701452385894131769052374806001981109*I; 1, 1.6829412935943127761
+629561615079976006 + 2.0500351226010726172974286983598602164*I, -0.750453175
+76910401286427186094108607490 + 1.3101462685358123283560773619310445915*I, -
+0.78742068874775359433940488309213323160 + 2.1336633893126618034168454610457
+936016*I, 1.2658732110596551455718089553258673704 - 2.7164790103743150566578
+028035789834836*I], [1, -1.0891151457205048250249527946671612684, -2.4285174
+907194186068992069565359418365, 0.71946691128913178943997506477288225735, -2
+.5558200350691694950646071159426779971; 1, 0.3534326558436260713470530909729
+9828470, 1.1549969969398343650309345170134923246, -2.27039314228143996450010
+21653326313849, -2.5581084321144835749447428779547264828; 1, -0.630200097311
+74679864801261932183221744, 2.7744268453177922675968161614046216617, 2.12576
+76084878153637389368667440155906, 0.58218204506434277886573208324566973893; 
+1, 3.7329764161953853934603848598678578170, 0.559693092766708315491805500989
+95851657, 1.3462427005649082090774405779536603700, -1.4506057993146599110859
+938482531161132; 1, -0.36709382900675984113447253685186261580, -2.0605994443
+049163412203492228721306664, -2.9210840780604153977562503441379268332, 3.982
+3522214339702022296117589048508541], [1, -1, -2, 1, -3; 1, 0, 1, -2, -3; 1, 
+-1, 3, 2, 1; 1, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, 
+-10, 20; 0, -5, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0
+, 200, 110, 177; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 
+0, 1], [63, 3, 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, 
+-14, -9; -9, 16, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65,
+ 118, -116; 53, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -
+34]], [3, 5, 23]], [-1.0891151457205048250249527946671612684, -0.13838372073
+406036365047976417441696637 + 0.49181637657768643499753285514741525107*I, 1.
+6829412935943127761629561615079976006 + 2.0500351226010726172974286983598602
+164*I], [1, x, -1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, -1/2*x^4 + x^3 - x^2 
+- 9/2*x - 1, -1/2*x^4 + x^3 - 2*x^2 - 7/2*x - 2], [1, 0, -1, -7, -14; 0, 1, 
+1, -2, -15; 0, 0, 0, 2, 4; 0, 0, 1, 1, -2; 0, 0, -1, -3, -4], [1, 0, 0, 0, 0
+, 0, -1, -1, -2, 4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 
+1, 0, 0, 0, 1, 1, -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3,
+ -3; 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, 
+-2, -1, -1; 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0
+, 2, 0, -1; 0, 0, 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0,
+ 1, 1, 0, 0, -1]]
+? nfinit(nfpol,3)[2]
+Mod(-1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*
+x + 2)
+? nf3=nfinit(x^6+108);
+? setrand(1);bnf2=bnfinit(y^3-y-1);nf2=bnf2[7];
+? setrand(1);bnf=bnfinit(x^2-x-57,,[0.2,0.2])
+[Mat(3), Mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.14159
+26535897932384626433832795028842*I; 2.7124653051843439746808795106061300699]
+, [1.7903417566977293763292119206302198761, 1.289761953065273502503008607239
+5031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.5005798036324
+5587382620331339071677436; -1.7903417566977293763292119206302198761, -1.2897
+619530652735025030086072395031018, 0.E-38, -0.500579803632455873826203313390
+71677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1, 
+[0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1, 
+57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0], 
+229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.0663729752107779
+635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.0
+663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229, 
+114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.06637297
+52107779635959310246705326059, 8.0663729752107779635959310246705326058], [1,
+ x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2.
+7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [Mat(1), [[0, 0
+]], [[1.7903417566977293763292119206302198761, -1.79034175669772937632921192
+06302198761]]], [0, 0]]
+? dobnf(x^2-x-100000,1)
+[[5], [Mod(379554884019013781006303254896369154068336082609238336*x + 119836
+165644250789990462835950022871665178127611316131167, x^2 - x - 100000)]]
+? \p19
+   realprecision = 19 significant digits
+? setrand(1);sbnf=bnfcompress(bnfinit(x^3-x^2-14*x-1))
+[x^3 - x^2 - 14*x - 1, 3, 10889, [1, x, x^2 - x - 9], [-3.233732695981516673
+, -0.07182350902743636345, 4.305556205008953036], 0, Mat(2), Mat([1, 1, 0, 1
+, 0, 1]), [9, 15, 16, 17, 39, 33, 10], [2, -1], [[0, 1, 0]~, [5, 3, 1]~], [[
+[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0
+]~, [-3, 0, 0]~], 0]]
+? bnfinit(sbnf)
+[Mat(2), Mat([1, 1, 0, 1, 0, 1]), [1.173637103435061715 + 3.1415926535897932
+38*I, -4.562279014988837902 + 3.141592653589793238*I; -2.633543432738976050 
++ 3.141592653589793238*I, 1.420330600779487358 + 3.141592653589793238*I; 1.4
+59906329303914335, 3.141948414209350544], [1.246346989334819161, 0.540400637
+6129469728 + 3.141592653589793238*I, -0.6926391142471042845 + 3.141592653589
+793238*I, -1.990056445584799714 + 3.141592653589793238*I, -0.830562594660718
+8640, 0.004375616572659815402, 0; 0.6716827432867392936, -0.8333219883742404
+172 + 3.141592653589793238*I, -0.2461086674077943078, 0.5379005671092853267,
+ -1.552661549868775854 + 3.141592653589793238*I, -0.8738318043071131265 + 3.
+141592653589793238*I, 0; -1.918029732621558455 + 3.141592653589793238*I, 0.2
+929213507612934444, 0.9387477816548985923, 1.452155878475514387, 2.383224144
+529494718 + 3.141592653589793238*I, 0.8694561877344533111 + 3.14159265358979
+3238*I, 0], [[3, [-1, 1, 0]~, 1, 1, [1, 10, 45; 1, 7, 6; 1, 1, -3]], [5, [-1
+, 1, 0]~, 1, 1, [0, 10, 45; 1, 6, 6; 1, 1, -4]], [5, [2, 1, 0]~, 1, 1, [1, -
+17, 42; -2, 4, -9; 1, -2, -3]], [5, [3, 1, 0]~, 1, 1, [2, 19, 46; 2, 9, 11; 
+1, 2, -2]], [13, [19, 1, 0]~, 1, 1, [-2, -53, 38; -6, -3, -29; 1, -6, -6]], 
+[11, [1, 1, 0]~, 1, 1, [-3, -8, 43; -1, 1, -4; 1, -1, -7]], [3, [10, 1, 1]~,
+ 1, 2, [-1, 9, 1; 1, 0, 5; 0, 1, -1]]]~, 0, [x^3 - x^2 - 14*x - 1, [3, 0], 1
+0889, 1, [[1, -3.233732695981516673, 4.690759845041404812; 1, -0.07182350902
+743636345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144
+592], [1, -3.233732695981516673, 4.690759845041404812; 1, -0.071823509027436
+36345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144592]
+, [1, -3, 5; 1, 0, -9; 1, 4, 5], [3, 1, 1; 1, 29, 8; 1, 8, 129], [10889, 569
+8, 8994; 0, 1, 0; 0, 0, 1], [3677, -121, -21; -121, 386, -23; -21, -23, 86],
+ [10889, [1899, 46720, 5235; 5191, 7095, 25956; 1, 5191, 1895]], []], [-3.23
+3732695981516673, -0.07182350902743636345, 4.305556205008953036], [1, x, x^2
+ - x - 9], [1, 0, 9; 0, 1, 1; 0, 0, 1], [1, 0, 0, 0, 9, 1, 0, 1, 44; 0, 1, 0
+, 1, 1, 5, 0, 5, 1; 0, 0, 1, 0, 1, 0, 1, 0, -4]], [[2, [2], [[3, 2, 0; 0, 1,
+ 0; 0, 0, 1]]], 10.34800724602767998, 1, [2, -1], [x, x^2 + 2*x - 4]], [Mat(
+1), [[0, 0, 0]], [[1.246346989334819161, 0.6716827432867392936, -1.918029732
+621558455 + 3.141592653589793238*I]]], [[[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]
+~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0]~, [-3, 0, 0]~], 0]]
+? \p38
+   realprecision = 38 significant digits
+? bnr=bnrinit(bnf,[[5,3;0,1],[1,0]],1);bnr.cyc
+[12]
+? bnr2=bnrinit(bnf,[[25,13;0,1],[1,1]],1);bnr2.bid
+[[[25, 13; 0, 1], [1, 1]], [80, [20, 2, 2], [2, [-24, 0]~, [2, 2]~]], Mat([[
+5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], 2]), [[[[4], [2], [2], [Vecsmall([0, 0])]
+, 1], [[5], [6], [6], [Vecsmall([0, 0])], Mat([1/5, -13/5])]], [[2, 2], [-24
+, [2, 2]~], [Vecsmall([0, 1]), Vecsmall([1, 1])]]], [1, -12, 0, 0; 0, 0, 1, 
+0; 0, 0, 0, 1]]
+? rnfinit(nf2,x^5-x-2)
+[x^5 - x - 2, [[83718587879473471, -18162091535584830*x^14 + 659399873895590
+0*x^13 + 89125883511340690*x^12 - 123429972713895380*x^11 - 8618468612826159
+0*x^10 + 508290939376248430*x^9 - 88425050961683595*x^8 - 806556841120532680
+*x^7 - 2575481228604156570*x^6 + 2756771576006241774*x^5 - 28977279276236285
+95*x^4 + 4379071886234238350*x^3 - 4957913590225421420*x^2 - 981408476020699
+484*x + 24006278056864075, 39516536165538345*x^14 - 6500512476832995*x^13 - 
+196215472046117185*x^12 + 229902227480108910*x^11 + 237380704030959181*x^10 
+- 1064931988160773805*x^9 - 20657086671714300*x^8 + 1772885205999206010*x^7 
++ 5952033217241102348*x^6 - 4838840187320655696*x^5 + 5180390720553188700*x^
+4 - 8374015687535120430*x^3 + 8907744727915040221*x^2 + 4155976664123434381*
+x + 318920215718580450], 1/83718587879473471], [[49744, 0, 0; 0, 49744, 0; 0
+, 0, 49744], 3109], 1, [], [], [[1, x, x^2, x^3, x^4], [1, 1, 1, 1, 1]], [1,
+ 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], []
+, [y^3 - y - 1, [1, 1], -23, 1, [[1, 0.7548776662466927600495088963585286919
+0, 1.3247179572447460259609088544780973407; 1, -0.87743883312334638002475444
+817926434595 - 0.74486176661974423659317042860439236724*I, -0.66235897862237
+301298045442723904867037 + 0.56227951206230124389918214490937306150*I], [1, 
+0.75487766624669276004950889635852869190, 1.32471795724474602596090885447809
+73407; 1, -1.6223005997430906166179248767836567132, -0.100079466560071769081
+27228232967560887; 1, -0.13257706650360214343158401957487197871, -1.22463849
+06846742568796365721484217319], [1, 1, 1; 1, -2, 0; 1, 0, -1], [3, -1, 0; -1
+, 1, 3; 0, 3, 2], [23, 16, 13; 0, 1, 0; 0, 0, 1], [7, -2, 3; -2, -6, 9; 3, 9
+, -2], [23, [10, 1, 8; 7, 3, 1; 1, 7, 10]], [23]], [1.3247179572447460259609
+088544780973407, -0.66235897862237301298045442723904867037 + 0.5622795120623
+0124389918214490937306150*I], [1, y^2 - 1, y], [1, 0, 1; 0, 0, 1; 0, 1, 0], 
+[1, 0, 0, 0, 0, 1, 0, 1, 1; 0, 1, 0, 1, -1, 0, 0, 0, 1; 0, 0, 1, 0, 1, 0, 1,
+ 0, 0]], [x^15 - 5*x^13 + 5*x^12 + 7*x^11 - 26*x^10 - 5*x^9 + 45*x^8 + 158*x
+^7 - 98*x^6 + 110*x^5 - 190*x^4 + 189*x^3 + 144*x^2 + 25*x + 1, 395165361655
+38345/83718587879473471*x^14 - 6500512476832995/83718587879473471*x^13 - 196
+215472046117185/83718587879473471*x^12 + 229902227480108910/8371858787947347
+1*x^11 + 237380704030959181/83718587879473471*x^10 - 1064931988160773805/837
+18587879473471*x^9 - 20657086671714300/83718587879473471*x^8 + 1772885205999
+206010/83718587879473471*x^7 + 5952033217241102348/83718587879473471*x^6 - 4
+838840187320655696/83718587879473471*x^5 + 5180390720553188700/8371858787947
+3471*x^4 - 8374015687535120430/83718587879473471*x^3 + 8907744727915040221/8
+3718587879473471*x^2 + 4155976664123434381/83718587879473471*x + 31892021571
+8580450/83718587879473471, -1, y^3 - y - 1, x^5 - x - 2], [0, 0]]
+? bnfcertify(bnf)
+1
+? dobnf(x^4+24*x^2+585*x+1791,,[0.1,0.1])
+[[4], [Mod(1/147*x^3 + 1/147*x^2 - 8/49*x - 9/49, x^4 + 24*x^2 + 585*x + 179
+1)]]
+? bnrconductor(bnf,[[25,13;0,1],[1,1]])
+[[5, 3; 0, 1], [1, 0]]
+? bnrconductorofchar(bnr,[2])
+[[5, 3; 0, 1], [0, 0]]
+? bnfisprincipal(bnf,[5,1;0,1],0)
+[1]~
+? bnfisprincipal(bnf,[5,1;0,1])
+[[1]~, [-2, -1/3]~]
+? bnfisunit(bnf,Mod(3405*x-27466,x^2-x-57))
+[-4, Mod(1, 2)]~
+? bnfnarrow(bnf)
+[3, [3], [[3, 2; 0, 1]]]
+? bnfsignunit(bnf)
+
+[-1]
+
+[ 1]
+
+? bnrclassno(bnf,[[5,3;0,1],[1,0]])
+12
+? lu=ideallist(bnf,55,3);
+? bnrclassnolist(bnf,lu)
+[[3], [], [3, 3], [3], [6, 6], [], [], [], [3, 3, 3], [], [3, 3], [3, 3], []
+, [], [12, 6, 6, 12], [3], [3, 3], [], [9, 9], [6, 6], [], [], [], [], [6, 1
+2, 6], [], [3, 3, 3, 3], [], [], [], [], [], [3, 6, 6, 3], [], [], [9, 3, 9]
+, [6, 6], [], [], [], [], [], [3, 3], [3, 3], [12, 12, 6, 6, 12, 12], [], []
+, [6, 6], [9], [], [3, 3, 3, 3], [], [3, 3], [], [6, 12, 12, 6]]
+? bnrdisc(bnr,Mat(6))
+[12, 12, 18026977100265125]
+? bnrdisc(bnr)
+[24, 12, 40621487921685401825918161408203125]
+? bnrdisc(bnr2,,,2)
+0
+? bnrdisc(bnr,Mat(6),,1)
+[6, 2, [125, 13; 0, 1]]
+? bnrdisc(bnr,,,1)
+[12, 1, [1953125, 1160888; 0, 1]]
+? bnrdisc(bnr2,,,3)
+0
+? bnrdisclist(bnf,lu)
+[[[6, 6, Mat([229, 3])]], [], [[], []], [[]], [[12, 12, [5, 3; 229, 6]], [12
+, 12, [5, 3; 229, 6]]], [], [], [], [[], [], []], [], [[], []], [[], []], []
+, [], [[24, 24, [3, 6; 5, 9; 229, 12]], [], [], [24, 24, [3, 6; 5, 9; 229, 1
+2]]], [[]], [[], []], [], [[18, 18, [19, 6; 229, 9]], [18, 18, [19, 6; 229, 
+9]]], [[], []], [], [], [], [], [[], [24, 24, [5, 12; 229, 12]], []], [], [[
+], [], [], []], [], [], [], [], [], [[], [12, 12, [3, 3; 11, 3; 229, 6]], [1
+2, 12, [3, 3; 11, 3; 229, 6]], []], [], [], [[18, 18, [2, 12; 3, 12; 229, 9]
+], [], [18, 18, [2, 12; 3, 12; 229, 9]]], [[12, 12, [37, 3; 229, 6]], [12, 1
+2, [37, 3; 229, 6]]], [], [], [], [], [], [[], []], [[], []], [[], [], [], [
+], [], []], [], [], [[12, 12, [2, 12; 3, 3; 229, 6]], [12, 12, [2, 12; 3, 3;
+ 229, 6]]], [[18, 18, [7, 12; 229, 9]]], [], [[], [], [], []], [], [[], []],
+ [], [[], [24, 24, [5, 9; 11, 6; 229, 12]], [24, 24, [5, 9; 11, 6; 229, 12]]
+, []]]
+? bnrdisclist(bnf,20)
+[[[[matrix(0,2), [[6, 6, Mat([229, 3])], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]],
+ [], [[Mat([12, 1]), [[0, 0, 0], [0, 0, 0], [0, 0, 0], [12, 0, [3, 3; 229, 6
+]]]], [Mat([13, 1]), [[0, 0, 0], [12, 6, [-1, 1; 3, 3; 229, 6]], [0, 0, 0], 
+[0, 0, 0]]]], [[Mat([10, 1]), [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]]
+, [[Mat([20, 1]), [[12, 12, [5, 3; 229, 6]], [0, 0, 0], [0, 0, 0], [24, 0, [
+5, 9; 229, 12]]]], [Mat([21, 1]), [[12, 12, [5, 3; 229, 6]], [24, 12, [5, 9;
+ 229, 12]], [0, 0, 0], [0, 0, 0]]]], [], [], [], [[Mat([12, 2]), [[0, 0, 0],
+ [0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[12, 1; 13, 1], [[0, 0, 0], [0, 0, 0], 
+[12, 6, [-1, 1; 3, 6; 229, 6]], [24, 0, [3, 12; 229, 12]]]], [Mat([13, 2]), 
+[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]], [], [[Mat([44, 1]), [[0, 0, 
+0], [12, 6, [-1, 1; 11, 3; 229, 6]], [0, 0, 0], [0, 0, 0]]], [Mat([45, 1]), 
+[[0, 0, 0], [0, 0, 0], [0, 0, 0], [12, 0, [11, 3; 229, 6]]]]], [[[10, 1; 12,
+ 1], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[10, 1; 13, 1], [[0, 0,
+ 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]], [], [], [[[12, 1; 20, 1], [[24, 24,
+ [3, 6; 5, 9; 229, 12]], [0, 0, 0], [0, 0, 0], [48, 0, [3, 12; 5, 18; 229, 2
+4]]]], [[13, 1; 20, 1], [[0, 0, 0], [24, 12, [3, 6; 5, 6; 229, 12]], [24, 12
+, [3, 6; 5, 9; 229, 12]], [48, 0, [3, 12; 5, 18; 229, 24]]]], [[12, 1; 21, 1
+], [[0, 0, 0], [0, 0, 0], [24, 12, [3, 6; 5, 9; 229, 12]], [48, 0, [3, 12; 5
+, 18; 229, 24]]]], [[13, 1; 21, 1], [[24, 24, [3, 6; 5, 9; 229, 12]], [48, 2
+4, [3, 12; 5, 18; 229, 24]], [0, 0, 0], [0, 0, 0]]]], [[Mat([10, 2]), [[0, 0
+, 0], [12, 6, [-1, 1; 2, 12; 229, 6]], [12, 6, [-1, 1; 2, 12; 229, 6]], [24,
+ 0, [2, 36; 229, 12]]]]], [[Mat([68, 1]), [[0, 0, 0], [0, 0, 0], [12, 6, [-1
+, 1; 17, 3; 229, 6]], [0, 0, 0]]], [Mat([69, 1]), [[0, 0, 0], [0, 0, 0], [12
+, 6, [-1, 1; 17, 3; 229, 6]], [0, 0, 0]]]], [], [[Mat([76, 1]), [[18, 18, [1
+9, 6; 229, 9]], [0, 0, 0], [0, 0, 0], [36, 0, [19, 15; 229, 18]]]], [Mat([77
+, 1]), [[18, 18, [19, 6; 229, 9]], [36, 18, [-1, 1; 19, 15; 229, 18]], [0, 0
+, 0], [0, 0, 0]]]], [[[10, 1; 20, 1], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 
+0, 0]]], [[10, 1; 21, 1], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]]]]
+? bnrisprincipal(bnr,idealprimedec(bnf,7)[1])
+[[9]~, [32879/6561, 13958/19683]~]
+? dirzetak(nfinit(x^3-10*x+8),30)
+[1, 2, 0, 3, 1, 0, 0, 4, 0, 2, 1, 0, 0, 0, 0, 5, 1, 0, 0, 3, 0, 2, 0, 0, 2, 
+0, 1, 0, 1, 0]
+? factornf(x^3+x^2-2*x-1,t^3+t^2-2*t-1)
+
+[         x + Mod(-t, t^3 + t^2 - 2*t - 1) 1]
+
+[   x + Mod(-t^2 + 2, t^3 + t^2 - 2*t - 1) 1]
+
+[x + Mod(t^2 + t - 1, t^3 + t^2 - 2*t - 1) 1]
+
+? vp=idealprimedec(nf,3)[1]
+[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0,
+ 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]]
+? idx=idealhnf(nf,vp)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idy=idealred(nf,idx,[1,5,6])
+
+[5 0 0 0 2]
+
+[0 5 0 0 2]
+
+[0 0 5 0 1]
+
+[0 0 0 5 2]
+
+[0 0 0 0 1]
+
+? idx2=idealmul(nf,idx,idx)
+
+[9 5 7 0 4]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idt=idealmul(nf,idx,idx,1)
+
+[2 0 0 0 0]
+
+[0 2 0 0 0]
+
+[0 0 2 0 0]
+
+[0 0 0 2 1]
+
+[0 0 0 0 1]
+
+? idz=idealintersect(nf,idx,idy)
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? aid=[idx,idy,idz,1,idx];
+? idealadd(nf,idx,idy)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealaddtoone(nf,idx,idy)
+[[0, -1, -3, -1, 2]~, [1, 1, 3, 1, -2]~]
+? idealaddtoone(nf,[idy,idx])
+[[-5, 0, 0, 0, 0]~, [6, 0, 0, 0, 0]~]
+? idealappr(nf,idy)
+[-1, 4, 2, -1, -3]~
+? idealappr(nf,idealfactor(nf,idy),1)
+[-1, 4, 2, -1, -3]~
+? idealcoprime(nf,idx,idx)
+[-1/3, 1/3, 1/3, 1/3, 0]~
+? idealdiv(nf,idy,idt)
+
+[5   0 5/2   0   1]
+
+[0 5/2   0   0   1]
+
+[0   0 5/2   0 1/2]
+
+[0   0   0 5/2   1]
+
+[0   0   0   0 1/2]
+
+? idealdiv(nf,idx2,idx,1)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealfactor(nf,idz)
+
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]] 1]
+
+[[5, [-1, 0, 0, 0, 2]~, 4, 1, [2, -3, 0, -12, 6; 2, 2, -5, -2, 6; 1, 1, 0, -
+1, -7; 2, 2, 5, 3, 1; 1, -4, 0, -1, 3]] 3]
+
+[[5, [2, 0, 0, 0, -2]~, 1, 1, [2, 1, 10, -4, 2; 0, 0, -5, 0, 0; 3, -1, 0, -1
+, -7; 0, 0, 5, 5, 5; 1, -2, 0, 3, 1]] 1]
+
+? idealhnf(nf,vp[2],3)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? ideallist(bnf,20)
+[[[1, 0; 0, 1]], [], [[3, 2; 0, 1], [3, 0; 0, 1]], [[2, 0; 0, 2]], [[5, 3; 0
+, 1], [5, 1; 0, 1]], [], [], [], [[9, 5; 0, 1], [3, 0; 0, 3], [9, 3; 0, 1]],
+ [], [[11, 9; 0, 1], [11, 1; 0, 1]], [[6, 4; 0, 2], [6, 0; 0, 2]], [], [], [
+[15, 8; 0, 1], [15, 3; 0, 1], [15, 11; 0, 1], [15, 6; 0, 1]], [[4, 0; 0, 4]]
+, [[17, 14; 0, 1], [17, 2; 0, 1]], [], [[19, 18; 0, 1], [19, 0; 0, 1]], [[10
+, 6; 0, 2], [10, 2; 0, 2]]]
+? bid=idealstar(nf2,54)
+[[[54, 0, 0; 0, 54, 0; 0, 0, 54], [0]], [132678, [1638, 9, 9]], [[2, [2, 0, 
+0]~, 1, 3, 1], 1; [3, [3, 0, 0]~, 1, 3, 1], 3], [[[[7], [[2, 1, 0]~], [[-26,
+ -27, 0]~], [Vecsmall([])], 1]], [[[26], [[2, 2, 0]~], [[-25, 2, 0]~], [Vecs
+mall([])], 1], [[3, 3, 3], [4, [1, 3, 0]~, [1, 0, 3]~], [-77, [1, -24, 0]~, 
+[1, 0, -24]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/3, 0, 0; 0, 1/
+3, 0; 0, 0, 1/3]], [[3, 3, 3], [10, [1, 9, 0]~, [1, 0, 9]~], [-233, [1, -18,
+ 0]~, [1, 0, -18]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/9, 0, 0;
+ 0, 1/9, 0; 0, 0, 1/9]]], [[], [], []]], [468, 469, 0, 0, -910, 0, 0, -1092;
+ 0, 0, 1, 0, -3, -6, 0, 0; 0, 0, 0, 1, -3, 0, -6, 0]]
+? ideallog(nf2,y,bid)
+[176, 2, 0]~
+? idealmin(nf,idx,[1,2,3])
+[1, 0, 1, 0, 0]~
+? idealnorm(nf,idt)
+16
+? idp=idealpow(nf,idx,7)
+
+[2187 1436 1807 630 1822]
+
+[   0    1    0   0    0]
+
+[   0    0    1   0    0]
+
+[   0    0    0   1    0]
+
+[   0    0    0   0    1]
+
+? idealpow(nf,idx,7,1)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealprimedec(nf,2)
+[[2, [3, 0, 1, 0, 0]~, 1, 1, [0, 2, 0, -4, -2; 0, 0, 0, 2, 0; 0, 0, -2, -2, 
+-2; 1, 0, 3, 0, -1; 1, 0, 1, 0, -1]], [2, [12, -4, -2, 11, 3]~, 1, 4, [1, -1
+, 3, -1, 1; 0, 0, -2, -1, 1; 1, 0, 1, 0, -2; 0, 0, 1, 2, 2; 0, -1, 0, 1, 1]]
+]
+? idealprimedec(nf,3)
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]], [3, [1, 1, 1, 0, 0]~, 2, 2, [0, -6,
+ 3, -9, 9; 2, -1, -7, -5, 7; 2, 1, 0, 1, -7; 1, 2, 3, 2, 4; 0, -5, -1, 0, 2]
+]]
+? idealprimedec(nf,11)
+[[11, [11, 0, 0, 0, 0]~, 1, 5, 1]]
+? idealtwoelt(nf,idy)
+[5, [2, 2, 1, 2, 1]~]
+? idealtwoelt(nf,idy,10)
+[-1, 4, 2, 4, 2]~
+? idealstar(nf2,54)
+[[[54, 0, 0; 0, 54, 0; 0, 0, 54], [0]], [132678, [1638, 9, 9]], [[2, [2, 0, 
+0]~, 1, 3, 1], 1; [3, [3, 0, 0]~, 1, 3, 1], 3], [[[[7], [[2, 1, 1]~], [[-26,
+ -27, -27]~], [Vecsmall([])], 1]], [[[26], [[4, 2, 2]~], [[-23, 2, 2]~], [Ve
+csmall([])], 1], [[3, 3, 3], [4, [1, 3, 0]~, [1, 0, 3]~], [-77, [1, -24, 0]~
+, [1, 0, -24]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/3, 0, 0; 0, 
+1/3, 0; 0, 0, 1/3]], [[3, 3, 3], [10, [1, 9, 0]~, [1, 0, 9]~], [-233, [1, -1
+8, 0]~, [1, 0, -18]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/9, 0, 
+0; 0, 1/9, 0; 0, 0, 1/9]]], [[], [], []]], [468, 469, 0, 0, -364, 0, 0, -109
+2; 0, 0, 1, 0, -6, -6, 0, 0; 0, 0, 0, 1, -3, 0, -6, 0]]
+? idealval(nf,idp,vp)
+7
+? ba=nfalgtobasis(nf,x^3+5)
+[6, 1, 3, 1, 3]~
+? bb=nfalgtobasis(nf,x^3+x)
+[1, 1, 4, 1, 3]~
+? bc=matalgtobasis(nf,[x^2+x;x^2+1])
+
+[[3, -2, 1, 1, 0]~]
+
+[[4, -2, 0, 1, 0]~]
+
+? matbasistoalg(nf,bc)
+
+[Mod(x^2 + x, x^5 - 5*x^3 + 5*x + 25)]
+
+[Mod(x^2 + 1, x^5 - 5*x^3 + 5*x + 25)]
+
+? nfbasis(x^3+4*x+5)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? nfbasis(x^3+4*x+5,2)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? nfbasis(x^3+4*x+12,1)
+[1, x, 1/2*x^2]
+? nfbasistoalg(nf,ba)
+Mod(x^3 + 5, x^5 - 5*x^3 + 5*x + 25)
+? nfbasis(p2,0,fa)
+[1, x, x^2, 1/11699*x^3 + 1847/11699*x^2 - 132/11699*x - 2641/11699, 1/13962
+3738889203638909659*x^4 - 1552451622081122020/139623738889203638909659*x^3 +
+ 418509858130821123141/139623738889203638909659*x^2 - 6810913798507599407313
+4/139623738889203638909659*x - 13185339461968406/58346808996920447]
+? nfdisc(x^3+4*x+12)
+-1036
+? nfdisc(x^3+4*x+12,1)
+-1036
+? nfdisc(p2,0,fa)
+136866601
+? nfeltdiv(nf,ba,bb)
+[584/373, 66/373, -32/373, -105/373, 120/373]~
+? nfeltdiveuc(nf,ba,bb)
+[2, 0, 0, 0, 0]~
+? nfeltdivrem(nf,ba,bb)
+[[2, 0, 0, 0, 0]~, [4, -1, -5, -1, -3]~]
+? nfeltmod(nf,ba,bb)
+[4, -1, -5, -1, -3]~
+? nfeltmul(nf,ba,bb)
+[50, -15, -35, 60, 15]~
+? nfeltpow(nf,bb,5)
+[-291920, 136855, 230560, -178520, 74190]~
+? nfeltreduce(nf,ba,idx)
+[1, 0, 0, 0, 0]~
+? nfeltval(nf,ba,vp)
+0
+? nffactor(nf2,x^3+x)
+
+[      x 1]
+
+[x^2 + 1 1]
+
+? aut=nfgaloisconj(nf3)
+[-x, x, -1/12*x^4 - 1/2*x, -1/12*x^4 + 1/2*x, 1/12*x^4 - 1/2*x, 1/12*x^4 + 1
+/2*x]~
+? nfgaloisapply(nf3,aut[5],Mod(x^5,x^6+108))
+Mod(-1/2*x^5 + 9*x^2, x^6 + 108)
+? nfhilbert(nf,3,5)
+-1
+? nfhilbert(nf,3,5,vp)
+-1
+? nfhnf(nf,[a,aid])
+[[1, -1, -1; 0, 1, 0; 0, 0, 1], [[3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 
+0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? da=nfdetint(nf,[a,aid])
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? nfhnfmod(nf,[a,aid],da)
+[[1, -1, -1; 0, 1, 0; 0, 0, 1], [[3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 
+0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? nfisideal(bnf[7],[5,1;0,1])
+1
+? nfisincl(x^2+1,x^4+1)
+[-x^2, x^2]
+? nfisincl(x^2+1,nfinit(x^4+1))
+[-x^2, x^2]
+? nfisisom(x^3+x^2-2*x-1,x^3+x^2-2*x-1)
+[x, -x^2 - x + 1, x^2 - 2]
+? nfisisom(x^3-2,nfinit(x^3-6*x^2-6*x-30))
+[-1/25*x^2 + 13/25*x - 2/5]
+? nfroots(nf2,x+2)
+[Mod(-2, y^3 - y - 1)]
+? nfrootsof1(nf)
+[2, -1]
+? nfsnf(nf,[a[,1..3],[1,1,1],[idealinv(nf,idx),idealinv(nf,idy),1]])
+[[5113790367401366394295125, 4367721766083689974291960, 47291190972301090559
+2775, 4905437241737335488566685, 4697851874666647634403882; 0, 5, 0, 0, 2; 0
+, 0, 5, 0, 1; 0, 0, 0, 5, 2; 0, 0, 0, 0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 
+0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0, 0;
+ 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1]]
+? nfsubfields(nf)
+[[x, 0], [x^5 - 5*x^3 + 5*x + 25, x]]
+? polcompositum(x^4-4*x+2,x^3-x-1)
+[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x
+^2 - 128*x - 5]
+? polcompositum(x^4-4*x+2,x^3-x-1,1)
+[[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*
+x^2 - 128*x - 5, Mod(-279140305176/29063006931199*x^11 + 129916611552/290630
+06931199*x^10 + 1272919322296/29063006931199*x^9 - 2813750209005/29063006931
+199*x^8 - 2859411937992/29063006931199*x^7 - 414533880536/29063006931199*x^6
+ - 35713977492936/29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 4
+9785595543672/29063006931199*x^3 + 9423768373204/29063006931199*x^2 - 427797
+76146743/29063006931199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8
+*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), M
+od(-279140305176/29063006931199*x^11 + 129916611552/29063006931199*x^10 + 12
+72919322296/29063006931199*x^9 - 2813750209005/29063006931199*x^8 - 28594119
+37992/29063006931199*x^7 - 414533880536/29063006931199*x^6 - 35713977492936/
+29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 49785595543672/2906
+3006931199*x^3 + 9423768373204/29063006931199*x^2 - 13716769215544/290630069
+31199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12
+*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), -1]]
+? polgalois(x^6-3*x^2-1)
+[12, 1, 1, "A_4(6) = [2^2]3"]
+? polred(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+[x - 1, x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1]
+? polred(x^4-28*x^3-458*x^2+9156*x-25321,3)
+
+[                                                1           x - 1]
+
+[                   1/115*x^2 - 14/115*x - 212/115   x^2 - 2*x - 9]
+
+[                  -1/115*x^2 + 14/115*x + 442/115   x^2 - 2*x - 9]
+
+[                   1/115*x^2 - 14/115*x - 327/115        x^2 - 10]
+
+[1/4485*x^3 - 7/1495*x^2 - 1034/4485*x + 7924/4485 x^4 - 8*x^2 + 6]
+
+? polred(x^4+576,1)
+[x - 1, x^2 - 3*x + 3, x^2 - 2*x + 2, x^2 - x + 1, x^2 + 1, x^4 - x^2 + 1]
+? polred(x^4+576,3)
+
+[                           1         x - 1]
+
+[    -1/192*x^3 - 1/8*x + 3/2 x^2 - 3*x + 3]
+
+[                1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[               -1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[    -1/192*x^3 - 1/8*x + 1/2   x^2 - x + 1]
+
+[     1/192*x^3 + 1/8*x + 1/2   x^2 - x + 1]
+
+[                    1/24*x^2       x^2 + 1]
+
+[1/192*x^3 + 1/48*x^2 - 1/8*x x^4 - x^2 + 1]
+
+? polred(p2,0,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? polred(p2,1,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1
+? polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568,1)
+[x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1, Mod(2*x^4 - x^3 + 3*x^2 - 3*x - 1, x^5 -
+ x^4 + 2*x^3 - 4*x^2 + x - 1)]
+? polredord(x^3-12*x+45*x-1)
+[x - 1, x^3 + 33*x - 1]
+? polsubcyclo(31,5)
+x^5 + x^4 - 12*x^3 - 21*x^2 + x + 5
+? setrand(1);poltschirnhaus(x^5-x-1)
+x^5 - 15*x^4 + 88*x^3 - 273*x^2 + 532*x - 304
+? p=x^5-5*x+y;aa=rnfpseudobasis(nf2,p)
+[[1, 0, 0, -2, [3, 1, 0]~; 0, 1, 0, 2, [0, -1, 0]~; 0, 0, 1, 1, [-5, -2, 0]~
+; 0, 0, 0, 1, -2; 0, 0, 0, 0, 1], [1, 1, 1, [1, 0, 2/5; 0, 1, 3/5; 0, 0, 1/5
+], [1, 0, 22/25; 0, 1, 8/25; 0, 0, 1/25]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfbasis(bnf2,aa)
+
+[1 0 0  [-26/25, 11/25, -8/25]~         [0, 4, -7]~]
+
+[0 1 0   [53/25, -8/25, -1/25]~ [6/5, -41/5, 53/5]~]
+
+[0 0 1 [-14/25, -21/25, 13/25]~  [-16/5, 1/5, 7/5]~]
+
+[0 0 0     [7/25, -2/25, 6/25]~  [2/5, -2/5, 11/5]~]
+
+[0 0 0     [9/25, 1/25, -3/25]~   [2/5, -7/5, 6/5]~]
+
+? rnfdisc(nf2,p)
+[[416134375, 202396875, 60056800; 0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfequation(nf2,p)
+x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1
+? rnfequation(nf2,p,1)
+[x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1, Mod(-x^5 + 5*x, x^15 - 1
+5*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1), 0]
+? rnfhnfbasis(bnf2,aa)
+
+[1 0 0 [-6/5, -4/5, 2/5]~   [3/25, -8/25, 24/25]~]
+
+[0 1 0  [6/5, 4/5, -2/5]~   [-9/25, -1/25, 3/25]~]
+
+[0 0 1  [3/5, 2/5, -1/5]~ [-8/25, 13/25, -39/25]~]
+
+[0 0 0  [3/5, 2/5, -1/5]~   [4/25, 6/25, -18/25]~]
+
+[0 0 0                  0   [-2/25, -3/25, 9/25]~]
+
+? rnfisfree(bnf2,aa)
+1
+? rnfsteinitz(nf2,aa)
+[[1, 0, 0, [-26/25, 11/25, -8/25]~, [29/125, -2/25, 8/125]~; 0, 1, 0, [53/25
+, -8/25, -1/25]~, [-53/125, 7/125, 1/125]~; 0, 0, 1, [-14/25, -21/25, 13/25]
+~, [9/125, 19/125, -13/125]~; 0, 0, 0, [7/25, -2/25, 6/25]~, [-9/125, 2/125,
+ -6/125]~; 0, 0, 0, [9/25, 1/25, -3/25]~, [-8/125, -1/125, 3/125]~], [1, 1, 
+1, 1, [125, 0, 22; 0, 125, 108; 0, 0, 1]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? nfz=zetakinit(x^2-2);
+? zetak(nfz,-3)
+0.091666666666666666666666666666666666667
+? zetak(nfz,1.5+3*I)
+0.88324345992059326405525724366416928892 - 0.2067536250233895222724230899142
+7938848*I
+? setrand(1);quadclassunit(1-10^7,,[1,1])
+[2416, [1208, 2], [Qfb(277, 55, 9028), Qfb(1700, 1249, 1700)], 1]
+? setrand(1);quadclassunit(10^9-3,,[0.5,0.5])
+[4, [4], [Qfb(199, 31533, -7123, 0.E-57)], 2800.6252519070160764863706217370
+745514]
+? sizebyte(%)
+156
+? getheap
+[217, 100743]
+? print("Total time spent: ",gettime);
+Total time spent: 116
diff --git a/src/test/32/nfrootsof1 b/src/test/32/nfrootsof1
new file mode 100644
index 0000000..7d71d6d
--- /dev/null
+++ b/src/test/32/nfrootsof1
@@ -0,0 +1,8 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+46
+46
+18
+2
+2
+2
+Total time spent: 58691
diff --git a/src/test/32/norm b/src/test/32/norm
new file mode 100644
index 0000000..41d1bfe
--- /dev/null
+++ b/src/test/32/norm
@@ -0,0 +1,38 @@
+1/4
+  ***   at top-level: norml2(quadgen(5))
+  ***                 ^------------------
+  *** norml2: incorrect type in gnorml2 (t_QUAD).
+1
+1
+1/2
+1
+(x)->normlp(x,1)
+6
+10
+21
+2.4142135623730950488016887242096980786
+1.6180339887498948482045868343656381177
+5
+(x)->normlp(x,2)
+3.7416573867739413855837487323165493018
+5.4772255750516611345696978280080213395
+9.5393920141694564915262158602322654026
+1.7320508075688772935274463415058723670
+1.6180339887498948482045868343656381177
+5
+(x)->normlp(x,2.5)
+3.4585606563304871862271371438840799750
+4.9402040006184485884345102892270748966
+8.2976320964215261445777796306034959974
+1.6273657035458510939647914767411763647
+1.6180339887498948482045868343656381177
+5
+normlp
+3
+4
+6
+1.4142135623730950488016887242096980786
+1.6180339887498948482045868343656381177
+5.0000000000000000000000000000000000000
+422481
+Total time spent: 4
diff --git a/src/test/32/number b/src/test/32/number
new file mode 100644
index 0000000..a3477a4
--- /dev/null
+++ b/src/test/32/number
@@ -0,0 +1,272 @@
+   echo = 1 (on)
+? addprimes([nextprime(10^9),nextprime(10^10)])
+[1000000007, 10000000019]
+? bestappr(Pi,10000)
+355/113
+? gcdext(123456789,987654321)
+[-8, 1, 9]
+? bigomega(12345678987654321)
+8
+? binomial(1.1,5)
+-0.0045457500000000000000000000000000000000
+? chinese(Mod(7,15),Mod(13,21))
+Mod(97, 105)
+? content([123,456,789,234])
+3
+? contfrac(Pi)
+[3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2, 1, 84, 2, 1
+, 1, 15, 3, 13, 1, 4, 2, 6, 6]
+? contfrac(Pi,5)
+[3, 7, 15, 1, 292]
+? contfrac((exp(1)-1)/(exp(1)+1),[1,3,5,7,9])
+[0, 6, 10, 42, 30]
+? contfracpnqn([2,6,10,14,18,22,26])
+
+[19318376 741721]
+
+[ 8927353 342762]
+
+? contfracpnqn([1,1,1,1,1,1,1,1;1,1,1,1,1,1,1,1])
+
+[34 21]
+
+[21 13]
+
+? core(54713282649239)
+5471
+? core(54713282649239,1)
+[5471, 100003]
+? coredisc(54713282649239)
+21884
+? coredisc(54713282649239,1)
+[21884, 100003/2]
+? divisors(8!)
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 28, 30, 32, 
+35, 36, 40, 42, 45, 48, 56, 60, 63, 64, 70, 72, 80, 84, 90, 96, 105, 112, 12
+0, 126, 128, 140, 144, 160, 168, 180, 192, 210, 224, 240, 252, 280, 288, 315
+, 320, 336, 360, 384, 420, 448, 480, 504, 560, 576, 630, 640, 672, 720, 840,
+ 896, 960, 1008, 1120, 1152, 1260, 1344, 1440, 1680, 1920, 2016, 2240, 2520,
+ 2688, 2880, 3360, 4032, 4480, 5040, 5760, 6720, 8064, 10080, 13440, 20160, 
+40320]
+? eulerphi(257^2)
+65792
+? factor(17!+1)
+
+[    661 1]
+
+[ 537913 1]
+
+[1000357 1]
+
+? factor(100!+1,0)
+
+[101 1]
+
+[14303 1]
+
+[149239 1]
+
+[432885273849892962613071800918658949059679308685024481795740765527568493010
+727023757461397498800981521440877813288657839195622497225621499427628453 1]
+
+? factor(40!+1,100000)
+
+[                                         41 1]
+
+[                                         59 1]
+
+[                                        277 1]
+
+[1217669507565553887239873369513188900554127 1]
+
+? factorback(factor(12354545545))
+12354545545
+? factor(230873846780665851254064061325864374115500032^6)
+
+[     2 120]
+
+[     3   6]
+
+[     7   6]
+
+[    23   6]
+
+[    29   6]
+
+[500501  36]
+
+? factorcantor(x^11+1,7)
+
+[Mod(1, 7)*x + Mod(1, 7) 1]
+
+[Mod(1, 7)*x^10 + Mod(6, 7)*x^9 + Mod(1, 7)*x^8 + Mod(6, 7)*x^7 + Mod(1, 7)*
+x^6 + Mod(6, 7)*x^5 + Mod(1, 7)*x^4 + Mod(6, 7)*x^3 + Mod(1, 7)*x^2 + Mod(6,
+ 7)*x + Mod(1, 7) 1]
+
+? centerlift(lift(factorff(x^3+x^2+x-1,3,t^3+t^2+t-1)))
+
+[            x - t 1]
+
+[x + (t^2 + t - 1) 1]
+
+[   x + (-t^2 - 1) 1]
+
+? 10!
+3628800
+? factorial(10)
+3628800.0000000000000000000000000000000
+? factormod(x^11+1,7)
+
+[Mod(1, 7)*x + Mod(1, 7) 1]
+
+[Mod(1, 7)*x^10 + Mod(6, 7)*x^9 + Mod(1, 7)*x^8 + Mod(6, 7)*x^7 + Mod(1, 7)*
+x^6 + Mod(6, 7)*x^5 + Mod(1, 7)*x^4 + Mod(6, 7)*x^3 + Mod(1, 7)*x^2 + Mod(6,
+ 7)*x + Mod(1, 7) 1]
+
+? factormod(x^11+1,7,1)
+
+[ 1 1]
+
+[10 1]
+
+? setrand(1);ffinit(2,11)
+Mod(1, 2)*x^11 + Mod(1, 2)*x^10 + Mod(1, 2)*x^8 + Mod(1, 2)*x^4 + Mod(1, 2)*
+x^3 + Mod(1, 2)*x^2 + Mod(1, 2)
+? setrand(1);ffinit(7,4)
+Mod(1, 7)*x^4 + Mod(1, 7)*x^3 + Mod(1, 7)*x^2 + Mod(1, 7)*x + Mod(1, 7)
+? fibonacci(100)
+354224848179261915075
+? gcd(12345678,87654321)
+9
+? gcd(x^10-1,x^15-1)
+x^5 - 1
+? hilbert(2/3,3/4,5)
+1
+? hilbert(Mod(5,7),Mod(6,7))
+1
+? isfundamental(12345)
+1
+? isprime(12345678901234567)
+0
+? ispseudoprime(73!+1)
+1
+? issquare(12345678987654321)
+1
+? issquarefree(123456789876543219)
+0
+? kronecker(5,7)
+-1
+? kronecker(3,18)
+0
+? lcm(15,-21)
+105
+? lift(chinese(Mod(7,15),Mod(4,21)))
+67
+? modreverse(Mod(x^2+1,x^3-x-1))
+Mod(x^2 - 3*x + 2, x^3 - 5*x^2 + 8*x - 5)
+? moebius(3*5*7*11*13)
+-1
+? nextprime(100000000000000000000000)
+100000000000000000000117
+? numdiv(2^99*3^49)
+5000
+? omega(100!)
+25
+? precprime(100000000000000000000000)
+99999999999999999999977
+? prime(100)
+541
+? primes(100)
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 
+157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 2
+39, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 33
+1, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421
+, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541]
+? qfbclassno(-12391)
+63
+? qfbclassno(1345)
+6
+? qfbclassno(-12391,1)
+63
+? qfbclassno(1345,1)
+6
+? Qfb(2,1,3)*Qfb(2,1,3)
+Qfb(2, -1, 3)
+? qfbcompraw(Qfb(5,3,-1,0.),Qfb(7,1,-1,0.))
+Qfb(35, 43, 13, 0.E-38)
+? qfbhclassno(2000003)
+357
+? qfbnucomp(Qfb(2,1,9),Qfb(4,3,5),3)
+Qfb(2, -1, 9)
+? form=Qfb(2,1,9);qfbnucomp(form,form,3)
+Qfb(4, -3, 5)
+? qfbnupow(form,111)
+Qfb(2, -1, 9)
+? qfbpowraw(Qfb(5,3,-1,0.),3)
+Qfb(125, 23, 1, 0.E-38)
+? qfbprimeform(-44,3)
+Qfb(3, 2, 4)
+? qfbred(Qfb(3,10,12),,-1)
+Qfb(3, -2, 4)
+? qfbred(Qfb(3,10,-20,1.5))
+Qfb(3, 16, -7, 1.5000000000000000000000000000000000000)
+? qfbred(Qfb(3,10,-20,1.5),2,,18)
+Qfb(3, 16, -7, 1.5000000000000000000000000000000000000)
+? qfbred(Qfb(3,10,-20,1.5),1)
+Qfb(-20, -10, 3, 2.1074451073987839947135880252731470616)
+? qfbred(Qfb(3,10,-20,1.5),3,,18)
+Qfb(-20, -10, 3, 1.5000000000000000000000000000000000000)
+? quaddisc(-252)
+-7
+? quadgen(-11)
+w
+? quadpoly(-11)
+x^2 - x + 3
+? quadregulator(17)
+2.0947125472611012942448228460655286535
+? quadunit(17)
+3 + 2*w
+? sigma(100)
+217
+? sigma(100,2)
+13671
+? sigma(100,-3)
+1149823/1000000
+? sqrtint(10!^2+1)
+3628800
+? znorder(Mod(33,2^16+1))
+2048
+? forprime(p=2,100,print(p," ",lift(znprimroot(p))))
+2 1
+3 2
+5 2
+7 3
+11 2
+13 2
+17 3
+19 2
+23 5
+29 2
+31 3
+37 2
+41 6
+43 3
+47 5
+53 2
+59 2
+61 2
+67 2
+71 7
+73 5
+79 3
+83 2
+89 3
+97 5
+? znstar(3120)
+[768, [12, 4, 4, 2, 2], [Mod(2641, 3120), Mod(2341, 3120), Mod(2497, 3120), 
+Mod(391, 3120), Mod(2081, 3120)]]
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 24
diff --git a/src/test/32/objets b/src/test/32/objets
new file mode 100644
index 0000000..c2e564c
--- /dev/null
+++ b/src/test/32/objets
@@ -0,0 +1,125 @@
+   echo = 1 (on)
+? +3
+3
+? -5
+-5
+? 5+3
+8
+? 5-3
+2
+? 5/3
+5/3
+? 5\3
+1
+? 5\/3
+2
+? 5%3
+2
+? 5^3
+125
+? binary(65537)
+[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
+? bittest(10^100,100)
+1
+? ceil(-2.5)
+-2
+? centerlift(Mod(456,555))
+-99
+? component(1+O(7^4),3)
+1
+? conj(1+I)
+1 - I
+? conjvec(Mod(x^2+x+1,x^3-x-1))
+[4.0795956234914387860104177508366260326, 0.46020218825428060699479112458168
+698369 + 0.18258225455744299269398828369501930574*I, 0.460202188254280606994
+79112458168698369 - 0.18258225455744299269398828369501930574*I]~
+? truncate(1.7,&e)
+1
+? e
+-1
+? denominator(12345/54321)
+18107
+? divrem(345,123)
+[2, 99]~
+? divrem(x^7-1,x^5+1)
+[x^2, -x^2 - 1]~
+? floor(-1/2)
+-1
+? floor(-2.5)
+-3
+? frac(-2.7)
+0.30000000000000000000000000000000000000
+? I^2
+-1
+? imag(2+3*I)
+3
+? lex([1,3],[1,3,5])
+-1
+? max(2,3)
+3
+? min(2,3)
+2
+? Mod(-12,7)
+Mod(2, 7)
+? norm(1+I)
+2
+? norm(Mod(x+5,x^3+x+1))
+129
+? numerator((x+1)/(x-1))
+x + 1
+? 1/(1+x)+O(x^20)
+1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 -
+ x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)
+? numtoperm(7,1035)
+[2, 4, 6, 1, 5, 7, 3]
+? permtonum([4,7,1,6,3,5,2])
+2781
+? 37.
+37.000000000000000000000000000000000000
+? real(5-7*I)
+5
+? shift(1,50)
+1125899906842624
+? shift([3,4,-11,-12],-2)
+[0, 1, -2, -3]
+? shiftmul([3,4,-11,-12],-2)
+[3/4, 1, -11/4, -3]
+? sign(-1)
+-1
+? sign(0)
+0
+? sign(0.)
+0
+? simplify(((x+I+1)^2-x^2-2*x*(I+1))^2)
+-4
+? sizedigit([1.3*10^5,2*I*Pi*exp(4*Pi)])
+7
+? truncate(-2.7)
+-2
+? truncate(sin(x^2))
+-1/5040*x^14 + 1/120*x^10 - 1/6*x^6 + x^2
+? type(Mod(x,x^2+1))
+"t_POLMOD"
+? valuation(6^10000-1,5)
+5
+? \p57
+   realprecision = 57 significant digits
+? Pi
+3.14159265358979323846264338327950288419716939937510582098
+? \p38
+   realprecision = 38 significant digits
+? O(x^12)
+O(x^12)
+? padicno=(5/3)*127+O(127^5)
+44*127 + 42*127^2 + 42*127^3 + 42*127^4 + O(127^5)
+? padicprec(padicno,127)
+5
+? length(divisors(1000))
+16
+? Mod(10873,49649)^-1
+  ***   at top-level: Mod(10873,49649)^-1
+  ***                                 ^---
+  *** _^_: impossible inverse in Fp_inv: Mod(131, 49649).
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 4
diff --git a/src/test/32/op b/src/test/32/op
new file mode 100644
index 0000000..f2779cb
--- /dev/null
+++ b/src/test/32/op
@@ -0,0 +1,7 @@
+2
+3
+1
+10
+2
+[2, 3, 1, 10, 2]
+Total time spent: 0
diff --git a/src/test/32/orthopol b/src/test/32/orthopol
new file mode 100644
index 0000000..b09cddf
--- /dev/null
+++ b/src/test/32/orthopol
@@ -0,0 +1,16 @@
+U
+T
+L
+H
+1
+1
+1
+1
+0
+-1
+209
+1
+1
+1
+1
+Total time spent: 16
diff --git a/src/test/32/padic b/src/test/32/padic
new file mode 100644
index 0000000..7627574
--- /dev/null
+++ b/src/test/32/padic
@@ -0,0 +1,80 @@
+[O(2)]~
+  ***   at top-level: padicappr(x^2+1+O(3)
+  ***                 ^--------------------
+  *** padicappr: inconsistent moduli in Zp_to_Z: 5 != 3
+  ***   at top-level: padicappr(x^2+1+O(3)
+  ***                 ^--------------------
+  *** padicappr: inconsistent moduli in Zp_to_Z: 5 != 3
+
+[(1 + O(7^8))*y + O(7^8) 1]
+
+[(1 + O(7^8))*y + (3 + 2*7 + 6*7^2 + 2*7^3 + 7^4 + 7^5 + 5*7^6 + 2*7^7 + O(7
+^8)) 1]
+
+[(1 + O(7^8))*y + (4 + 4*7 + 4*7^3 + 5*7^4 + 5*7^5 + 7^6 + 4*7^7 + O(7^8)) 1
+]
+
+
+[(1 + O(3^5))*y^2 + O(3^5)*y + O(3^5) 1]
+
+[2, 2, 2]
+[3, 2, 2]
+[6, 2, 2]
+[7, 2, 2]
+[10, 2, 2]
+[2, 2, 3]
+[3, 2, 3]
+[4, 2, 3]
+[5, 2, 3]
+[6, 2, 3]
+[7, 2, 3]
+[10, 2, 3]
+[2, 3, 3]
+[3, 3, 3]
+[4, 3, 3]
+[5, 3, 3]
+[6, 3, 3]
+[7, 3, 3]
+[8, 3, 3]
+[9, 3, 3]
+[10, 3, 3]
+[2, 11, 3]
+[3, 11, 3]
+[4, 11, 3]
+[5, 11, 3]
+[6, 11, 3]
+[7, 11, 3]
+[8, 11, 3]
+[9, 11, 3]
+[10, 11, 3]
+[2, 18446744073709551629, 3]
+[3, 18446744073709551629, 3]
+[4, 18446744073709551629, 3]
+[5, 18446744073709551629, 3]
+[6, 18446744073709551629, 3]
+[7, 18446744073709551629, 3]
+[8, 18446744073709551629, 3]
+[9, 18446744073709551629, 3]
+[10, 18446744073709551629, 3]
+[2^3 + O(2^6), 1 + 2^2 + O(2^6), 1 + 2 + 2^4 + 2^5 + O(2^6)]~
+[Mod((1 + 2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + O(2^10))*y + (
+1 + 2^2 + 2^6 + 2^7 + O(2^10)), y^2 + y + 1), Mod((1 + 2 + 2^2 + 2^3 + 2^4 +
+ 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + O(2^10))*y + (1 + 2 + 2^4 + 2^5 + 2^8 + O(2^1
+0)), y^2 + y + 1)]~
+1/2
+3 + 3^2 + O(3^5)
+2 + 2^2 + O(2^5)
+1 + 2*3^2 + 3^3 + 3^4 + O(3^5)
+1 + O(x)
+3 + 2*3^3 + 3^4 + O(3^5)
+O(x)
+3 + O(3^5)
+O(x)
+1 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + O(3^5)
+18446744073709551627 + 18446744073709551628*18446744073709551629 + O(1844674
+4073709551629^2)
+3074457345618258605 + 15372286728091293024*18446744073709551629 + O(18446744
+073709551629^2)
+2*3^2 + 2*3^3 + O(3^5)
+2 + 5 + 2*5^2 + O(5^3)
+Total time spent: 0
diff --git a/src/test/32/parallel b/src/test/32/parallel
new file mode 100644
index 0000000..3abcd15
--- /dev/null
+++ b/src/test/32/parallel
@@ -0,0 +1,23 @@
+[[1238926361552897, 1; 93461639715357977769163558199606896584051237541638188
+580280321, 1], [13821503, 1; 61654440233248340616559, 1; 1473226532114531733
+1353282383, 1]]
+[[1238926361552897, 1; 93461639715357977769163558199606896584051237541638188
+580280321, 1], [13821503, 1; 61654440233248340616559, 1; 1473226532114531733
+1353282383, 1]]
+[[1238926361552897, 1; 93461639715357977769163558199606896584051237541638188
+580280321, 1], [13821503, 1; 61654440233248340616559, 1; 1473226532114531733
+1353282383, 1]]
+188
+Vecsmall([188, 722, 752])
+[414951556888099295851240786369116115101244623224243689999565732969065281141
+2908146399707048947103794288197886611300789182395151075411775307886874834113
+963687061181803401509523685563, 41495155688809929585124078636911611510124462
+3224243689999565732969065281141290814639970704894710379428819788661130078918
+2395151075411775307886874834113963687061181803401509523686097, 4149515568880
+9929585124078636911611510124462322424368999956573296906528114129081463997070
+4894710379428819788661130078918239515107541177530788687483411396368706118180
+3401509523686127]
+7432339208719
+7432339208719
+[75, 85070591730234615858594180193637120807]
+Total time spent: 23517
diff --git a/src/test/32/partition b/src/test/32/partition
new file mode 100644
index 0000000..de868e1
--- /dev/null
+++ b/src/test/32/partition
@@ -0,0 +1,194 @@
+1
+8646071025430235692572306890072717030146724518193248646261116875039232350406
+4122598473575016156709489555875867462346177817327218448788050480708550298957
+4471483457937988438791783450144642834886864800778387328738323763398284354678
+2467335026575150468969
+1315427338504929671669092893786176891908548434593143202526362264344662102688
+7135460507830403502328150530572701695452361942348769947362008712123081217734
+3187588697310977671154801113558915889945842493651964099879750098080254447377
+4788789260448084624118833910204856780861639068659823549617824714773817253043
+9615105518523078574457183315543226124650052897717744601504125318067437912564
+426796827005538082281337283642237399241533
+  ***   at top-level: numbpart(10^15+2)
+  ***                 ^-----------------
+  *** numbpart: overflow in numbpart [n < 10^15].
+[Vecsmall([])]
+[Vecsmall([1])]
+[Vecsmall([9]), Vecsmall([1, 8]), Vecsmall([2, 7]), Vecsmall([3, 6]), Vecsma
+ll([4, 5]), Vecsmall([1, 1, 7]), Vecsmall([1, 2, 6]), Vecsmall([1, 3, 5]), V
+ecsmall([1, 4, 4]), Vecsmall([2, 2, 5]), Vecsmall([2, 3, 4]), Vecsmall([3, 3
+, 3]), Vecsmall([1, 1, 1, 6]), Vecsmall([1, 1, 2, 5]), Vecsmall([1, 1, 3, 4]
+), Vecsmall([1, 2, 2, 4]), Vecsmall([1, 2, 3, 3]), Vecsmall([2, 2, 2, 3]), V
+ecsmall([1, 1, 1, 1, 5]), Vecsmall([1, 1, 1, 2, 4]), Vecsmall([1, 1, 1, 3, 3
+]), Vecsmall([1, 1, 2, 2, 3]), Vecsmall([1, 2, 2, 2, 2]), Vecsmall([1, 1, 1,
+ 1, 1, 4]), Vecsmall([1, 1, 1, 1, 2, 3]), Vecsmall([1, 1, 1, 2, 2, 2]), Vecs
+mall([1, 1, 1, 1, 1, 1, 3]), Vecsmall([1, 1, 1, 1, 1, 2, 2]), Vecsmall([1, 1
+, 1, 1, 1, 1, 1, 2]), Vecsmall([1, 1, 1, 1, 1, 1, 1, 1, 1])]
+[Vecsmall([3, 3, 3]), Vecsmall([1, 2, 3, 3]), Vecsmall([2, 2, 2, 3]), Vecsma
+ll([1, 1, 1, 3, 3]), Vecsmall([1, 1, 2, 2, 3]), Vecsmall([1, 2, 2, 2, 2]), V
+ecsmall([1, 1, 1, 1, 2, 3]), Vecsmall([1, 1, 1, 2, 2, 2]), Vecsmall([1, 1, 1
+, 1, 1, 1, 3]), Vecsmall([1, 1, 1, 1, 1, 2, 2]), Vecsmall([1, 1, 1, 1, 1, 1,
+ 1, 2]), Vecsmall([1, 1, 1, 1, 1, 1, 1, 1, 1])]
+[]
+[]
+[1, 4]
+[2, 3]
+[1, 1, 3]
+[1, 2, 2]
+[0, 0, 1, 4]
+[0, 0, 2, 3]
+[0, 1, 1, 3]
+[0, 1, 2, 2]
+[1, 1, 1, 2]
+451276
+[9]
+[1, 8]
+[2, 7]
+[3, 6]
+[4, 5]
+[1, 1, 7]
+[1, 2, 6]
+[1, 3, 5]
+[1, 4, 4]
+[2, 2, 5]
+[2, 3, 4]
+[3, 3, 3]
+[1, 1, 1, 6]
+[1, 1, 2, 5]
+[1, 1, 3, 4]
+[1, 2, 2, 4]
+[1, 2, 3, 3]
+[2, 2, 2, 3]
+[1, 1, 1, 1, 5]
+[1, 1, 1, 2, 4]
+[1, 1, 1, 3, 3]
+[1, 1, 2, 2, 3]
+[1, 2, 2, 2, 2]
+[1, 1, 1, 1, 1, 4]
+[1, 1, 1, 1, 2, 3]
+[1, 1, 1, 2, 2, 2]
+[1, 1, 1, 1, 1, 1, 3]
+[1, 1, 1, 1, 1, 2, 2]
+[1, 1, 1, 1, 1, 1, 1, 2]
+[1, 1, 1, 1, 1, 1, 1, 1, 1]
+[11]
+[1, 10]
+[2, 9]
+[3, 8]
+[4, 7]
+[5, 6]
+[1, 1, 9]
+[1, 2, 8]
+[1, 3, 7]
+[1, 4, 6]
+[1, 5, 5]
+[2, 2, 7]
+[2, 3, 6]
+[2, 4, 5]
+[3, 3, 5]
+[3, 4, 4]
+[1, 1, 1, 8]
+[1, 1, 2, 7]
+[1, 1, 3, 6]
+[1, 1, 4, 5]
+[1, 2, 2, 6]
+[1, 2, 3, 5]
+[1, 2, 4, 4]
+[1, 3, 3, 4]
+[2, 2, 2, 5]
+[2, 2, 3, 4]
+[2, 3, 3, 3]
+[1, 1, 1, 1, 7]
+[1, 1, 1, 2, 6]
+[1, 1, 1, 3, 5]
+[1, 1, 1, 4, 4]
+[1, 1, 2, 2, 5]
+[1, 1, 2, 3, 4]
+[1, 1, 3, 3, 3]
+[1, 2, 2, 2, 4]
+[1, 2, 2, 3, 3]
+[2, 2, 2, 2, 3]
+[1, 11]
+[2, 10]
+[3, 9]
+[4, 8]
+[5, 7]
+[6, 6]
+[1, 1, 10]
+[1, 2, 9]
+[1, 3, 8]
+[1, 4, 7]
+[1, 5, 6]
+[2, 2, 8]
+[2, 3, 7]
+[2, 4, 6]
+[2, 5, 5]
+[3, 3, 6]
+[3, 4, 5]
+[4, 4, 4]
+[1, 1, 1, 9]
+[1, 1, 2, 8]
+[1, 1, 3, 7]
+[1, 1, 4, 6]
+[1, 1, 5, 5]
+[1, 2, 2, 7]
+[1, 2, 3, 6]
+[1, 2, 4, 5]
+[1, 3, 3, 5]
+[1, 3, 4, 4]
+[2, 2, 2, 6]
+[2, 2, 3, 5]
+[2, 2, 4, 4]
+[2, 3, 3, 4]
+[3, 3, 3, 3]
+[1, 1, 1, 1, 8]
+[1, 1, 1, 2, 7]
+[1, 1, 1, 3, 6]
+[1, 1, 1, 4, 5]
+[1, 1, 2, 2, 6]
+[1, 1, 2, 3, 5]
+[1, 1, 2, 4, 4]
+[1, 1, 3, 3, 4]
+[1, 2, 2, 2, 5]
+[1, 2, 2, 3, 4]
+[1, 2, 3, 3, 3]
+[2, 2, 2, 2, 4]
+[2, 2, 2, 3, 3]
+[1, 1, 1, 1, 1, 7]
+[1, 1, 1, 1, 2, 6]
+[1, 1, 1, 1, 3, 5]
+[1, 1, 1, 1, 4, 4]
+[1, 1, 1, 2, 2, 5]
+[1, 1, 1, 2, 3, 4]
+[1, 1, 1, 3, 3, 3]
+[1, 1, 2, 2, 2, 4]
+[1, 1, 2, 2, 3, 3]
+[1, 2, 2, 2, 2, 3]
+[2, 2, 2, 2, 2, 2]
+[5, 6, 6, 6]
+[3, 3, 5, 6, 6]
+[3, 4, 4, 6, 6]
+[3, 4, 5, 5, 6]
+[3, 5, 5, 5, 5]
+[4, 4, 4, 5, 6]
+[4, 4, 5, 5, 5]
+[3, 3, 3, 3, 5, 6]
+[3, 3, 3, 4, 4, 6]
+[3, 3, 3, 4, 5, 5]
+[3, 3, 4, 4, 4, 5]
+[3, 4, 4, 4, 4, 4]
+[3, 3, 3, 3, 3, 3, 5]
+[3, 3, 3, 3, 3, 4, 4]
+[0, 0, 0, 2, 3]
+[0, 0, 1, 1, 3]
+[0, 0, 1, 2, 2]
+[0, 1, 1, 1, 2]
+[1, 1, 1, 1, 1]
+[0, 0, 3, 3, 3, 3, 3]
+[0, 1, 2, 3, 3, 3, 3]
+[0, 2, 2, 2, 3, 3, 3]
+[1, 1, 1, 3, 3, 3, 3]
+[1, 1, 2, 2, 3, 3, 3]
+[1, 2, 2, 2, 2, 3, 3]
+[2, 2, 2, 2, 2, 2, 3]
+Total time spent: 10984
diff --git a/src/test/32/ploth b/src/test/32/ploth
new file mode 100644
index 0000000..1a02c3f
--- /dev/null
+++ b/src/test/32/ploth
@@ -0,0 +1,83 @@
+   echo = 1 (on)
+? \p19
+   realprecision = 19 significant digits
+? t=plothsizes();
+? plotinit(0,t[1]-11,t[2]-11)
+? plotscale(0,0,1000,0,1000);
+? plotbox(0,500,500)
+? plotdraw([0,0,0])
+? psdraw([0,0,0])
+? plotcolor(0,2);
+? plotmove(0,0,900);plotlines(0,900,0)
+? plotlines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+? plotmove(0,243,583);plotcursor(0)
+[243, 583]
+? plot(x=-5,5,sin(x))
+
+0.9995545 x""x_''''''''''''''''''''''''''''''''''_x""x'''''''''''''''''''|
+          |    x                                _     "_                 |
+          |     x                              _        _                |
+          |      x                            _                          |
+          |       _                                      "               |
+          |                                  "            x              |
+          |        x                        _                            |
+          |                                                "             |
+          |         "                      x                _            |
+          |          _                                                   |
+          |                               "                  x           |
+          ````````````x``````````````````_````````````````````````````````
+          |                                                   "          |
+          |            "                x                      _         |
+          |             _                                                |
+          |                            "                        x        |
+          |              x            _                                  |
+          |               _                                      "       |
+          |                          "                            x      |
+          |                "        "                              x     |
+          |                 "_     "                                x    |
+-0.999555 |...................x__x".................................."x__x
+          -5                                                             5
+? ploth(x=-5,5,sin(x))
+[-5.000000000000000000, 5.000000000000000000, -0.9999964107564721649, 0.9999
+964107564721649]
+? ploth(t=0,2*Pi,[sin(5*t),sin(7*t)])
+[0.E-307, 6.283185307179586232, -0.9999987638285974256, 0.999998763828597425
+6]
+? ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],1,100)
+[-0.9998741276738750683, 0.9998741276738750683, -0.9998741276738750683, 0.99
+98741276738750683]
+? ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],2,100)
+  ***   at top-level: ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],
+  ***                                ^--------------------
+  ***   incorrect type in ploth [multi-curves cannot be plot recursively] (t_VEC).
+? ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],3,100)
+[-1.000000000000000000, 1.000000000000000000, -1.000000000000000000, 1.00000
+0000000000000]
+? plothraw(vector(501,k,k-1),vector(501,k,(k-1)*(k-1)/500));
+? plothraw(vector(501,k,k-1),vector(501,k,(k-1)*(k-1)/500),1);
+? plotpoints(0,225,334)
+? plotpoints(0,vector(10,k,10*k),vector(10,k,5*k*k))
+? psploth(x=-5,5,sin(x));
+? psplothraw(vector(501,k,k-1),vector(501,k,(k-1)*(k-1)/500),1);
+? plotmove(0,50,50);plotrbox(0,50,50)
+? plotrline(0,150,100)
+? plotcolor(0,4);
+? plotcursor(0)
+[200, 150]
+? plotrmove(0,5,5);plotcursor(0)
+[205, 155]
+? plotrpoint(0,20,20)
+? plotmove(0,100,100);plotstring(0,Pi)
+? plotmove(0,200,200);plotstring(0,"(0,0)")
+? plotdraw([0,10,10])
+? psdraw([0,10,10])
+? ploth(x=0,2*Pi,if(x<1,[cos(x),sin(x)],1),1)
+  ***   at top-level: ploth(x=0,2*Pi,if(x<1,[cos(x),sin(x)],
+  ***                                   ^--------------------
+  ***   inconsistent dimensions in rectploth.
+? ploth(x=0,1,x,,1)
+  ***   at top-level: ploth(x=0,1,x,,1)
+  ***                 ^-----------------
+  *** ploth: domain error in ploth: #points < 2
+? print("Total time spent: ",gettime);
+Total time spent: 36
diff --git a/src/test/32/pol b/src/test/32/pol
new file mode 100644
index 0000000..a88dd4f
--- /dev/null
+++ b/src/test/32/pol
@@ -0,0 +1,36 @@
+Mod(0, 3) Mod(0, 3) Mod(0, 3)
+Mod(0, 3) + O(y^16) Mod(0, 3) + O(x^16) Mod(0, 3) + O(x^5)
+y x x
+y + O(y^17) y + O(x^16) y + O(x^5)
+0 0 0
+y^-1 + O(y^15) 1/y + O(x^16) 1/y + O(x^5)
+y x x
+y^-1 + y + O(y^15) ((y^2 + 1)/y) + O(x^16) ((y^2 + 1)/y) + O(x^5)
+y^2 + 2*y + 3 x^2 + 2*x + 3 3*x^2 + 2*x + 1
+1 + 2*y + 3*y^2 + O(y^3) 1 + 2*x + 3*x^2 + O(x^3) 1 + 2*x + 3*x^2 + O(x^3)
+y^2 + 2*y x^2 + 2*x 2*x + 1
+1 + 2*y + O(y^3) 1 + 2*x + O(x^3) 1 + 2*x + O(x^3)
+y^2 + 2*y + 4 x^2 + 2*x + 4 4*x^2 + 2*x + 1
+1 + 2*y + 4*y^2 + O(y^3) 1 + 2*x + 4*x^2 + O(x^3) 1 + 2*x + 4*x^2 + O(x^3)
+y^2 + 2*y - 4 x^2 + 2*x - 4 -4*x^2 + 2*x + 1
+1 + 2*y - 4*y^2 + O(y^3) 1 + 2*x - 4*x^2 + O(x^3) 1 + 2*x - 4*x^2 + O(x^3)
+2*y^2 + y 2*x^2 + x 2*x^2 + x
+y + 2*y^2 + O(y^4) (y + 2*y^2 + O(y^4)) + O(x^16) (y + 2*y^2 + O(y^4)) + O(x
+^5)
+2 2 3
+y + 2 y + 2 (y + 2) + x + O(x^2)
+1 1 x + 2
+x^2 + x + 1
+-x^3 - 2*x^2 - x + 1
+[4, 0, 0, 0, -2]~
+x^2 + 1
+2
+
+[0 -1/2]
+
+[1    0]
+
+  ***   at top-level: Pol("")
+  ***                 ^-------
+  *** Pol: incorrect type in gtopoly (t_STR).
+Total time spent: 0
diff --git a/src/test/32/polmod b/src/test/32/polmod
new file mode 100644
index 0000000..1994600
--- /dev/null
+++ b/src/test/32/polmod
@@ -0,0 +1,61 @@
+Mod(x + 1/3, 2*x^2 + x + 1)
+Mod(15/16*x + 21/32, 2*x^2 + x + 1)
+Mod(1/2*x - 1/4, 2*x^2 + x + 1)
+Mod(-2*x, 2*x^2 + x + 1)
+Mod(-1/4*x - 3/8, 2*x^2 + x + 1)
+Mod(1, 2*x^2 + x + 1)
+Mod(4/3*x^2 - 1/6, 2*x^3 + x + 1)
+Mod(-39/103*x^2 + 93/103*x + 183/206, 2*x^3 + x + 1)
+Mod(3/2*x^2 - 1/2*x - 3/4, 2*x^3 + x + 1)
+Mod(2*x^2 - 2*x + 2, 2*x^3 + x + 1)
+Mod(-5/4*x^2 - 9/4*x - 7/8, 2*x^3 + x + 1)
+Mod(1, 2*x^3 + x + 1)
+Mod(Mod(6, 17)*x^2 + Mod(7, 17)*x + Mod(4, 17), Mod(2, 17)*x^3 + Mod(1, 17)*
+x + Mod(1, 17))
+Mod(Mod(3, 17)*x^2 + Mod(16, 17)*x + Mod(6, 17), Mod(2, 17)*x^3 + Mod(1, 17)
+*x + Mod(1, 17))
+Mod(Mod(15, 17)*x^2 + Mod(13, 17)*x + Mod(8, 17), Mod(2, 17)*x^3 + Mod(1, 17
+)*x + Mod(1, 17))
+Mod(Mod(8, 17)*x^2 + Mod(13, 17)*x + Mod(1, 17), Mod(2, 17)*x^3 + Mod(1, 17)
+*x + Mod(1, 17))
+Mod(Mod(16, 17)*x^2 + Mod(10, 17), Mod(2, 17)*x^3 + Mod(1, 17)*x + Mod(1, 17
+))
+Mod(Mod(1, 17), Mod(2, 17)*x^3 + Mod(1, 17)*x + Mod(1, 17))
+Mod(Mod(9223372036854775829, 18446744073709551629)*x^2 + Mod(7, 184467440737
+09551629)*x + Mod(9223372036854775827, 18446744073709551629), Mod(2, 1844674
+4073709551629)*x^3 + Mod(1, 18446744073709551629)*x + Mod(1, 184467440737095
+51629))
+Mod(Mod(9746969576064872258, 18446744073709551629)*x^2 + Mod(103913973166311
+44804, 18446744073709551629)*x + Mod(12445510739686138545, 18446744073709551
+629), Mod(2, 18446744073709551629)*x^3 + Mod(1, 18446744073709551629)*x + Mo
+d(1, 18446744073709551629))
+Mod(Mod(9223372036854775821, 18446744073709551629)*x^2 + Mod(922337203685477
+5819, 18446744073709551629)*x + Mod(8, 18446744073709551629), Mod(2, 1844674
+4073709551629)*x^3 + Mod(1, 18446744073709551629)*x + Mod(1, 184467440737095
+51629))
+Mod(Mod(11980875016945378893, 18446744073709551629)*x^2 + Mod(95086309658296
+65788, 18446744073709551629)*x + Mod(6656041676080766052, 184467440737095516
+29), Mod(2, 18446744073709551629)*x^3 + Mod(1, 18446744073709551629)*x + Mod
+(1, 18446744073709551629))
+Mod(Mod(4611686018427387936, 18446744073709551629)*x^2 + Mod(461168601842738
+7920, 18446744073709551629)*x + Mod(9223372036854775833, 1844674407370955162
+9), Mod(2, 18446744073709551629)*x^3 + Mod(1, 18446744073709551629)*x + Mod(
+1, 18446744073709551629))
+Mod(Mod(1, 18446744073709551629), Mod(2, 18446744073709551629)*x^3 + Mod(1, 
+18446744073709551629)*x + Mod(1, 18446744073709551629))
+Mod(1/4, t)*x^2 + Mod(1, t)*x + Mod(1, t)
+0
+Mod(y, x)
+Mod(1/y, x)
+Mod(O(y), x)
+Mod(1, y)*x
+Mod(1, y)/(Mod(1, y)*x)
+Mod(1, y)
+  ***   at top-level: Mod(1+O(y),y+1)
+  ***                 ^---------------
+  *** Mod: forbidden division t_SER % t_POL.
+0
+Mod(-1, x - 1)
+36893488147419103232
+a
+Total time spent: 0
diff --git a/src/test/32/polred b/src/test/32/polred
new file mode 100644
index 0000000..83877e0
--- /dev/null
+++ b/src/test/32/polred
@@ -0,0 +1,70 @@
+x^7 + Mod(7*y, y^2 - y - 1)*x^6 + Mod(21*y + 7, y^2 - y - 1)*x^5 + 35*x^4 + 
+Mod(-35*y - 14, y^2 - y - 1)*x^3 + Mod(-7*y - 77, y^2 - y - 1)*x^2 + Mod(14*
+y + 7, y^2 - y - 1)*x + Mod(-y + 44, y^2 - y - 1)
+x^8 + Mod(-5265231366756*y - 11544453645457, y^2 - y - 7)*x^7 + Mod(28411458
+5416607426786*y + 622944640581258439174, y^2 - y - 7)*x^6 + Mod(-88698486784
+831757442657946*y - 194478741347464554095950854, y^2 - y - 7)*x^5 + Mod(1457
+861838374320941446687517087*y + 3196482213651741289611519839129, y^2 - y - 7
+)*x^4 + Mod(-3466536016262523445329224834043387*y - 760066587058933027706659
+6588522840, y^2 - y - 7)*x^3 + Mod(500059251848756466420835983321373618*y + 
+1096421116344588264250099626740668170, y^2 - y - 7)*x^2 + Mod(-1916208944621
+5341153510282273347908836*y - 42014460135353505823787366140454468112, y^2 - 
+y - 7)*x + Mod(100691810991091652032034511974871062155*y + 22077509296238717
+8747302102119022589688, y^2 - y - 7)
+x^8 + Mod(-8*y + 1, y^2 - y - 7)*x^7 + Mod(21*y + 189, y^2 - y - 7)*x^6 + Mo
+d(-385*y - 251, y^2 - y - 7)*x^5 + Mod(695*y + 2955, y^2 - y - 7)*x^4 + Mod(
+-2451*y - 3350, y^2 - y - 7)*x^3 + Mod(2402*y + 6871, y^2 - y - 7)*x^2 + Mod
+(-2050*y - 3861, y^2 - y - 7)*x + Mod(565*y + 1331, y^2 - y - 7)
+x^8 + Mod(-8*y + 1, y^2 - y - 7)*x^7 + Mod(21*y + 189, y^2 - y - 7)*x^6 + Mo
+d(-385*y - 251, y^2 - y - 7)*x^5 + Mod(695*y + 2955, y^2 - y - 7)*x^4 + Mod(
+-2451*y - 3350, y^2 - y - 7)*x^3 + Mod(2402*y + 6871, y^2 - y - 7)*x^2 + Mod
+(-2050*y - 3861, y^2 - y - 7)*x + Mod(565*y + 1331, y^2 - y - 7)
+x^8 + Mod(-8*y + 1, y^2 - y - 7)*x^7 + Mod(21*y + 189, y^2 - y - 7)*x^6 + Mo
+d(-385*y - 251, y^2 - y - 7)*x^5 + Mod(695*y + 2955, y^2 - y - 7)*x^4 + Mod(
+-2451*y - 3350, y^2 - y - 7)*x^3 + Mod(2402*y + 6871, y^2 - y - 7)*x^2 + Mod
+(-2050*y - 3861, y^2 - y - 7)*x + Mod(565*y + 1331, y^2 - y - 7)
+x^8 + Mod(-8*y + 1, y^2 - y - 7)*x^7 + Mod(21*y + 189, y^2 - y - 7)*x^6 + Mo
+d(-385*y - 251, y^2 - y - 7)*x^5 + Mod(695*y + 2955, y^2 - y - 7)*x^4 + Mod(
+-2451*y - 3350, y^2 - y - 7)*x^3 + Mod(2402*y + 6871, y^2 - y - 7)*x^2 + Mod
+(-2050*y - 3861, y^2 - y - 7)*x + Mod(565*y + 1331, y^2 - y - 7)
+x^3 + Mod(y^2 - 2, y^3 - y - 1)*x^2 + Mod(-y + 1, y^3 - y - 1)*x + Mod(y - 1
+, y^3 - y - 1)
+x^9 - 4*x^8 + 8*x^7 - 9*x^6 + 7*x^5 - 3*x^4 - x^3 + 4*x^2 - 3*x + 1
+0
+0
+0
+  *** nfinit: Warning: non-monic polynomial. Result of the form [nf,c].
+x^2 - 3646554366
+304
+x^4 + 1000000000000000000000*x^2 + 1
+x^4 + 146077*x^2 + 2629386
+x^9 - 4*x^7 - 3*x^6 + 9*x^5 + 8*x^4 - 6*x^3 - 9*x^2 - 4*x - 1
+x^5 - 13*x^3 - 3*x^2 + 5*x + 1
+x^6 + 21471450*x^2 + 71643071500
+x^6 - 12*x^4 - 24*x^3 + 21651666*x^2 - 257657256*x + 71814482884
+x^4 + 146077*x^2 + 10517544
+x
+[x, Mod(-1/2, x)]
+  ***   at top-level: polred([x,[1]])
+  ***                 ^---------------
+  *** polred: domain error in gvaluation: p = 1
+[x - 1]
+[x - 1]
+
+[  1   x - 1]
+
+[2*x x^2 + 1]
+
+x + 1
+[x + 1, Mod(-1/2, x + 1)]
+[x^2 + 1, Mod(1/2*x, x^2 + 1)]
+[2*x + 1]
+[x - 1, x^2 + 1]
+[x^8 - 4*x^7 + 24*x^6 - 58*x^5 + 126*x^4 - 160*x^3 + 160*x^2 - 89*x + 26, Mo
+d(-68/135*x^7 + 208/135*x^6 - 1378/135*x^5 + 56/3*x^4 - 194/5*x^3 + 4976/135
+*x^2 - 4492/135*x + 1856/135, x^8 - 4*x^7 + 24*x^6 - 58*x^5 + 126*x^4 - 160*
+x^3 + 160*x^2 - 89*x + 26)]
+x^16 - 4*x^15 - 334*x^14 + 264*x^13 + 32231*x^12 + 57392*x^11 - 1031422*x^10
+ - 3628868*x^9 + 7185297*x^8 + 42417784*x^7 + 11283472*x^6 - 137773504*x^5 -
+ 127243504*x^4 + 69059728*x^3 + 56307944*x^2 - 6264432*x + 6436
+Total time spent: 2596
diff --git a/src/test/32/polygonal b/src/test/32/polygonal
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/32/polylog b/src/test/32/polylog
new file mode 100644
index 0000000..ab865ea
--- /dev/null
+++ b/src/test/32/polylog
@@ -0,0 +1,19 @@
+1.0496589501864398696458324932101000704
+2.0886953792151632708518141489041442185 - 4.27563394103876217704892645569519
+63565*I
+4.3226178452644705784020044544722613393 - 2.90951877177225946407469488966471
+03179*I
+[-0.20561675835602830455905189583075314865 - 0.91596559417721901505460351493
+238411077*I, -0.20561675835602830455905189583075314865 + 0.91596559417721901
+505460351493238411077*I]~
+[0.58224052646501250590265632015968010874, 0.7275863077163333895135362968404
+8110789]
+x + 1/4*x^2 + 1/9*x^3 + 1/16*x^4 + O(x^5)
+0.58224052646501250590265632015968010874 + 1.3862943611198906188344642429163
+531362*x + 0.61370563888010938116553575708364686385*x^2 + 0.5150591481598541
+5844595232388847084820*x^3 + 0.56074461109355209566440484750062706103*x^4 + 
+O(x^5)
+-2.3699397969983658319855374253503230488
+0.91596559417721901505460351493238411077
+0.34657359027997265470861606072908828404
+Total time spent: 4
diff --git a/src/test/32/polyser b/src/test/32/polyser
new file mode 100644
index 0000000..93fef60
--- /dev/null
+++ b/src/test/32/polyser
@@ -0,0 +1,160 @@
+   echo = 1 (on)
+? apol=y^3+5*y+1
+y^3 + 5*y + 1
+? deriv((x+y)^5,y)
+5*x^4 + 20*y*x^3 + 30*y^2*x^2 + 20*y^3*x + 5*y^4
+? ((x+y)^5)'
+5*x^4 + 20*y*x^3 + 30*y^2*x^2 + 20*y^3*x + 5*y^4
+? dz=vector(30,k,1);dd=vector(30,k,k==1);dm=dirdiv(dd,dz)
+[1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -
+1, 0, 0, 1, 0, 0, -1, -1]
+? direuler(s=1,40,1+s*X+s^2*X)
+[1, 6, 12, 0, 30, 72, 56, 0, 0, 180, 132, 0, 182, 336, 360, 0, 306, 0, 380, 
+0, 672, 792, 552, 0, 0, 1092, 0, 0, 870, 2160, 992, 0, 1584, 1836, 1680, 0, 
+1406, 2280, 2184, 0]
+? dirmul(abs(dm),dz)
+[1, 2, 2, 2, 2, 4, 2, 2, 2, 4, 2, 4, 2, 4, 4, 2, 2, 4, 2, 4, 4, 4, 2, 4, 2, 
+4, 2, 4, 2, 8]
+? zz=yy;yy=xx;eval(zz)
+xx
+? factorpadic(apol,7,8)
+
+[(1 + O(7^8))*y + (6 + 2*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + 6*7^6 + O(7^8)) 1]
+
+[(1 + O(7^8))*y^2 + (1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8
+))*y + (6 + 5*7 + 3*7^2 + 6*7^3 + 7^4 + 3*7^5 + 2*7^6 + 5*7^7 + O(7^8)) 1]
+
+? factorpadic(apol,7,8,1)
+
+[(1 + O(7^8))*y + (6 + 2*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + 6*7^6 + O(7^8)) 1]
+
+[(1 + O(7^8))*y^2 + (1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8
+))*y + (6 + 5*7 + 3*7^2 + 6*7^3 + 7^4 + 3*7^5 + 2*7^6 + 5*7^7 + O(7^8)) 1]
+
+? intformal(sin(x))
+1/2*x^2 - 1/24*x^4 + 1/720*x^6 - 1/40320*x^8 + 1/3628800*x^10 - 1/479001600*
+x^12 + 1/87178291200*x^14 - 1/20922789888000*x^16 + O(x^18)
+? intformal((-x^2-2*a*x+8*a)/(x^4-14*x^3+(2*a+49)*x^2-14*a*x+a^2))
+(x + a)/(x^2 - 7*x + a)
+? newtonpoly(x^4+3*x^3+27*x^2+9*x+81,3)
+[2, 2/3, 2/3, 2/3]
+? padicappr(apol,1+O(7^8))
+[1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8)]~
+? padicappr(x^3+5*x+1,Mod(x*(1+O(7^8)),x^2+x-1))
+[Mod((1 + 3*7 + 3*7^2 + 4*7^3 + 4*7^4 + 4*7^5 + 2*7^6 + 3*7^7 + O(7^8))*x + 
+(2*7 + 6*7^2 + 6*7^3 + 3*7^4 + 3*7^5 + 4*7^6 + 5*7^7 + O(7^8)), x^2 + x - 1)
+]~
+? Pol(sin(x))
+-1/1307674368000*x^15 + 1/6227020800*x^13 - 1/39916800*x^11 + 1/362880*x^9 -
+ 1/5040*x^7 + 1/120*x^5 - 1/6*x^3 + x
+? Pol([1,2,3,4,5])
+x^4 + 2*x^3 + 3*x^2 + 4*x + 5
+? Polrev([1,2,3,4,5])
+5*x^4 + 4*x^3 + 3*x^2 + 2*x + 1
+? polcoeff(sin(x),7)
+-1/5040
+? polcyclo(105)
+x^48 + x^47 + x^46 - x^43 - x^42 - 2*x^41 - x^40 - x^39 + x^36 + x^35 + x^34
+ + x^33 + x^32 + x^31 - x^28 - x^26 - x^24 - x^22 - x^20 + x^17 + x^16 + x^1
+5 + x^14 + x^13 + x^12 - x^9 - x^8 - 2*x^7 - x^6 - x^5 + x^2 + x + 1
+? pcy=polcyclo(405)
+x^216 - x^189 + x^135 - x^108 + x^81 - x^27 + 1
+? pcy*pcy
+x^432 - 2*x^405 + x^378 + 2*x^351 - 4*x^324 + 4*x^297 - x^270 - 4*x^243 + 7*
+x^216 - 4*x^189 - x^162 + 4*x^135 - 4*x^108 + 2*x^81 + x^54 - 2*x^27 + 1
+? poldegree(x^3/(x-1))
+2
+? poldisc(x^3+4*x+12)
+-4144
+? poldiscreduced(x^3+4*x+12)
+[1036, 4, 1]
+? polinterpolate([0,2,3],[0,4,9],5)
+25
+? polisirreducible(x^5+3*x^3+5*x^2+15)
+0
+? pollegendre(10)
+46189/256*x^10 - 109395/256*x^8 + 45045/128*x^6 - 15015/128*x^4 + 3465/256*x
+^2 - 63/256
+? zpol=0.3+pollegendre(10)
+46189/256*x^10 - 109395/256*x^8 + 45045/128*x^6 - 15015/128*x^4 + 3465/256*x
+^2 + 0.053906250000000000000000000000000000001
+? polrecip(3*x^7-5*x^3+6*x-9)
+-9*x^7 + 6*x^6 - 5*x^4 + 3
+? polresultant(x^3-1,x^3+1)
+8
+? polresultant(x^3-1.,x^3+1.,,1)
+8.0000000000000000000000000000000000000
+? polroots(x^5-5*x^2-5*x-5)
+[2.0509134529831982130058170163696514536 + 0.E-38*I, -0.67063790319207539268
+663382582902335603 - 0.84813118358634026680538906224199030918*I, -0.67063790
+319207539268663382582902335603 + 0.84813118358634026680538906224199030918*I,
+ -0.35481882329952371381627468235580237078 - 1.39980287391035466982975228340
+62081965*I, -0.35481882329952371381627468235580237078 + 1.399802873910354669
+8297522834062081965*I]~
+? polroots(x^4-1000000000000000000000)
+[-177827.94100389228012254211951926848447 + 0.E-38*I, 177827.941003892280122
+54211951926848447 + 0.E-38*I, 0.E-38 - 177827.941003892280122542119519268484
+47*I, 0.E-38 + 177827.94100389228012254211951926848447*I]~
+? polrootsmod(x^16-1,41)
+[Mod(1, 41), Mod(3, 41), Mod(9, 41), Mod(14, 41), Mod(27, 41), Mod(32, 41), 
+Mod(38, 41), Mod(40, 41)]~
+? polrootspadic(x^4+1,41,6)
+[3 + 22*41 + 27*41^2 + 15*41^3 + 27*41^4 + 33*41^5 + O(41^6), 14 + 20*41 + 2
+5*41^2 + 24*41^3 + 4*41^4 + 18*41^5 + O(41^6), 27 + 20*41 + 15*41^2 + 16*41^
+3 + 36*41^4 + 22*41^5 + O(41^6), 38 + 18*41 + 13*41^2 + 25*41^3 + 13*41^4 + 
+7*41^5 + O(41^6)]~
+? polsturm(zpol)
+4
+? polsturm(zpol,0.91,1)
+1
+? polsylvestermatrix(a2*x^2+a1*x+a0,b1*x+b0)
+
+[a2 b1  0]
+
+[a1 b0 b1]
+
+[a0  0 b0]
+
+? polsym(x^17-1,17)
+[17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17]~
+? poltchebi(10)
+512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1
+? polzagier(6,3)
+4608*x^6 - 13824*x^5 + 46144/3*x^4 - 23168/3*x^3 + 5032/3*x^2 - 120*x + 1
+? serconvol(sin(x),x*cos(x))
+x + 1/12*x^3 + 1/2880*x^5 + 1/3628800*x^7 + 1/14631321600*x^9 + 1/1448500838
+40000*x^11 + 1/2982752926433280000*x^13 + 1/114000816848279961600000*x^15 + 
+O(x^17)
+? serlaplace(x*exp(x*y)/(exp(x)-1))
+1 + (y - 1/2)*x + (y^2 - y + 1/6)*x^2 + (y^3 - 3/2*y^2 + 1/2*y)*x^3 + (y^4 -
+ 2*y^3 + y^2 - 1/30)*x^4 + (y^5 - 5/2*y^4 + 5/3*y^3 - 1/6*y)*x^5 + (y^6 - 3*
+y^5 + 5/2*y^4 - 1/2*y^2 + 1/42)*x^6 + (y^7 - 7/2*y^6 + 7/2*y^5 - 7/6*y^3 + 1
+/6*y)*x^7 + (y^8 - 4*y^7 + 14/3*y^6 - 7/3*y^4 + 2/3*y^2 - 1/30)*x^8 + (y^9 -
+ 9/2*y^8 + 6*y^7 - 21/5*y^5 + 2*y^3 - 3/10*y)*x^9 + (y^10 - 5*y^9 + 15/2*y^8
+ - 7*y^6 + 5*y^4 - 3/2*y^2 + 5/66)*x^10 + (y^11 - 11/2*y^10 + 55/6*y^9 - 11*
+y^7 + 11*y^5 - 11/2*y^3 + 5/6*y)*x^11 + (y^12 - 6*y^11 + 11*y^10 - 33/2*y^8 
++ 22*y^6 - 33/2*y^4 + 5*y^2 - 691/2730)*x^12 + (y^13 - 13/2*y^12 + 13*y^11 -
+ 143/6*y^9 + 286/7*y^7 - 429/10*y^5 + 65/3*y^3 - 691/210*y)*x^13 + (y^14 - 7
+*y^13 + 91/6*y^12 - 1001/30*y^10 + 143/2*y^8 - 1001/10*y^6 + 455/6*y^4 - 691
+/30*y^2 + 7/6)*x^14 + (y^15 - 15/2*y^14 + 35/2*y^13 - 91/2*y^11 + 715/6*y^9 
+- 429/2*y^7 + 455/2*y^5 - 691/6*y^3 + 35/2*y)*x^15 + O(x^16)
+? serreverse(tan(x))
+x - 1/3*x^3 + 1/5*x^5 - 1/7*x^7 + 1/9*x^9 - 1/11*x^11 + 1/13*x^13 - 1/15*x^1
+5 + O(x^17)
+? subst(sin(x),x,y)
+y - 1/6*y^3 + 1/120*y^5 - 1/5040*y^7 + 1/362880*y^9 - 1/39916800*y^11 + 1/62
+27020800*y^13 - 1/1307674368000*y^15 + O(y^17)
+? subst(sin(x),x,x+x^2)
+x + x^2 - 1/6*x^3 - 1/2*x^4 - 59/120*x^5 - 1/8*x^6 + 419/5040*x^7 + 59/720*x
+^8 + 13609/362880*x^9 + 19/13440*x^10 - 273241/39916800*x^11 - 14281/3628800
+*x^12 - 6495059/6227020800*x^13 + 69301/479001600*x^14 + 26537089/1188794880
+00*x^15 + 1528727/17435658240*x^16 + O(x^17)
+? taylor(y/(x-y),y)
+(O(y^16)*x^15 + y*x^14 + y^2*x^13 + y^3*x^12 + y^4*x^11 + y^5*x^10 + y^6*x^9
+ + y^7*x^8 + y^8*x^7 + y^9*x^6 + y^10*x^5 + y^11*x^4 + y^12*x^3 + y^13*x^2 +
+ y^14*x + y^15)/x^15
+? variable(name^4-other)
+name
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 4
diff --git a/src/test/32/pow b/src/test/32/pow
new file mode 100644
index 0000000..58e84bd
--- /dev/null
+++ b/src/test/32/pow
@@ -0,0 +1,55 @@
+Mod(1, 3)
+Mod(1, 3)
+1
+[;]
+
+[1]
+
+Qfb(1, 2, -1, 0.E-38)
+Qfb(-1, 2, 1, 0.E-38)
+Vecsmall([1, 2, 3])
+O(2)
+  ***   at top-level: O(2)^-2
+  ***                     ^---
+  *** _^_: impossible inverse in powps: O(2).
+4
+9/4
+1
+1
+-1
+  ***   at top-level: 2^n
+  ***                  ^--
+  *** _^_: overflow in lg().
+  ***   at top-level: (1/2)^n
+  ***                      ^--
+  *** _^_: overflow in lg().
+Qfb(2, 0, -1, 0.E-38)
+Mod(x^2, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)
+O(x^0)
+x + O(x^4)
+[8.0000000000000000000000000000000000000, 27.0000000000000000000000000000000
+00000]
+[8.0000000000000000000000000000000000000, 27.0000000000000000000000000000000
+00000]~
+
+[8.0000000000000000000000000000000000000]
+
+O(x^0)
+0.E-19
+  ***   at top-level: Mod(2,3)^(1/2)
+  ***                         ^------
+  *** _^_: not an n-th power residue in gpow: Mod(2, 3).
+Mod(2, 3)
+  ***   at top-level: Mod(1,4)^(1/2)
+  ***                         ^------
+  *** _^_: not a prime number in gpow: 4.
+3 + 7 + 2*7^2 + 6*7^3 + 7^4 + O(7^5)
+  ***   at top-level: (3+O(7^5))^(1/2)
+  ***                           ^------
+  *** _^_: not an n-th power residue in gpow: 3 + O(7^5).
+1 + O(2)
+1 + O(2)
+  ***   at top-level: sqrt(Mod(2,4))
+  ***                 ^--------------
+  *** sqrt: not a prime number in Fl_sqrt [modulus]: 4.
+Total time spent: 0
diff --git a/src/test/32/prec b/src/test/32/prec
new file mode 100644
index 0000000..d56316a
--- /dev/null
+++ b/src/test/32/prec
@@ -0,0 +1,22 @@
+I
+1 + O(3^3) + I
+O(2^3)
+1 + O(2^3)
+x + (1 + O(3^3))
+(1 + O(3^3))/((1 + O(5^3))*x)
+Mod(1 + O(3^3), (1 + O(3^3))*x)
+[1 + O(3^3), 2]
+77
+38
+1
+1
+2
+  ***   at top-level: padicprec(O(2^2),3)
+  ***                 ^-------------------
+  *** padicprec: inconsistent moduli in padicprec: 2 != 3
+2
+3
+3
+3
+4
+Total time spent: 8
diff --git a/src/test/32/prime b/src/test/32/prime
new file mode 100644
index 0000000..2225454
--- /dev/null
+++ b/src/test/32/prime
@@ -0,0 +1,9 @@
+8161 17863 38873 84017 180503 386093 821641 1742537 3681131 7754077 16290047
+ 172 309 564 1028 1900 3512 6542 12251 23000 43390 82025 155611 295947 56416
+3 1077871 2063689 3957809 
+8161 17863 38873 84017 180503 386093 821641 1742537 3681131 7754077 16290047
+ 172 309 564 1028 1900 3512 6542 12251 23000 43390 82025 155611 295947 56416
+3 1077871 2063689 3957809 
+203280221
+0
+Total time spent: 1204
diff --git a/src/test/32/primes b/src/test/32/primes
new file mode 100644
index 0000000..aae7da2
--- /dev/null
+++ b/src/test/32/primes
@@ -0,0 +1,12 @@
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 
+157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229]
+[11, 13, 17, 19]
+[4294967197, 4294967231, 4294967279, 4294967291, 4294967311, 4294967357, 429
+4967371, 4294967377, 4294967387, 4294967389]
+[18446744073709551521, 18446744073709551533, 18446744073709551557, 184467440
+73709551629, 18446744073709551653, 18446744073709551667, 1844674407370955169
+7, 18446744073709551709]
+61938
+2
+Total time spent: 476
diff --git a/src/test/32/print b/src/test/32/print
new file mode 100644
index 0000000..9fddc1e
--- /dev/null
+++ b/src/test/32/print
@@ -0,0 +1,4 @@
+1:2:3:4
+
+1:2:3:4
+Total time spent: 0
diff --git a/src/test/32/printf b/src/test/32/printf
new file mode 100644
index 0000000..185d080
--- /dev/null
+++ b/src/test/32/printf
@@ -0,0 +1,187 @@
+[1.0000000000000000000000000000000000000, 2.00000000000000000000000000000000
+00000]
+2.5000000000000000000000000000000000000
+"string"
+1000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000001
+%0.12ld for 1 : 1
+%ld for medium : 10000000000000000000100000000000000000001
+%ld for big : 12532566399657183181075548323827342061649850750809861714634950
+0752097059631738116432448839054351520763198615919551594076685828989467263022
+7617908382708545798300151112466612039846243589298325716157180147040963056680
+9750761327366302322689525054138592715842608868449408241676861770818959228693
+6039922311125683719215046689156738352590137241554510185855964549927575493247
+3911325485343784979788060849510858742020118363623157274201095547829887915300
+8828971184455050023048563841318994713214224394733419925930073562249293741945
+3650061490302105127920314430401636855677549136337481321811349678427076091437
+3450453993373486112611680559293554029928231924911903600270361122831809358727
+7521451746401317827465710073632156460683825273960115641462844554366314469605
+0650160812621814327062666195172701780200286645023823083185928061371310300829
+284071141207731280600001
+%ld for very big : 955872885674440970277216219337772744771565088008376702328
+0974773688280268898473796935187284691567817630339154703241830944055836706177
+8809896056537787803996812085304655368999238720837283566913503724303376966199
+2663921364613620578078079956465825388253571736411644948206142873619873902998
+9324013720616227984952217113628421798278427274142950039049698146988876476074
+8939673178886152066176070650100465366222106170273181288184814238180677916113
+9684997815338572295836554681424837278459728932615631869144483961065493749103
+9440849607597072892720960637713500992934377088954488639521019698524851190029
+1396723345810969718342617610081591495344841181777365972361910710227844455871
+0596976906677394315185249714607867087670127235754713322083182486995225340569
+5472246371195637391184890765488286905297801802857817902007091052166469500735
+8517047069777274928903320771601018941992164314045858885325103919749607054488
+9716525591702409353851295924370666649874439353143103187877095721819476871104
+6039621541420996552337447405835520269309421928095461368238705472240356556168
+3129303880551193588007845178971366661918797743038379406063588297944215992646
+3691785340362496774349413524560068683544691371721027653642635405810344391477
+8552857183108973243687481777037313832906066461962417663222739497894271864945
+7391622038713205017081287426068599398078599136506248490477428409273845322293
+9485952504181333605883065410971257816284281979689677087852619367458015841421
+6117184233630606194460758031603508715854504925822065735415717117642951641517
+0445161262958756852411254262131565398676464291882351960512281733196470348829
+7690518593597258112492554340486265712974508286368276821774451328996893631934
+4348417912106139777376465001699264700607259391929003413720980579337938903285
+2410694501172153015867959754936696154432352039610821684585660293442216332255
+5070580750071570579340084075728526850951081388750387834675278083942728330312
+9711486511315300779788820180840619721223715240178672367496637042272860292245
+0067577330084637312473407306058275594314586875447869409332649806214474569067
+0799154712883103337029383119878973056652922226725046494001644377943743452783
+5338666583328256991155261024818087422713163540831882172167350724012550758316
+3850231515419634983097154734421315315036556738503952579851113060316168285064
+4412325371035901826210793778328430197276032260968667239887228910011826161802
+3097733175960455531908934464156879721834606048691762387934893989942083292678
+6114165436117688507634248134379080283752266998295759671652312537861057724541
+2889100481703172021091464154836384754183307922685257765508608568562975782300
+6971972040325406985944302869772198927623772954412851810428791278029990247190
+8061321027142210255474075271028994680673537259392939342492022814681786038589
+5053727937460469006290862306302596599241161335993054628084062016336294286371
+0165693652750247998589876833218400433596639923643807793537485122586379078832
+9952053631976486273900477504002455220369355782732974197522274191043597836052
+1374994258036101231726093072258297440735657687656653417040524326319232166510
+8439492869411835325894574661493440023985420450317689828201344808075337883055
+2236604912047688409349395104864407345150328046921038566652090310866567690216
+4572259483873093955981046211889222995200260181463334023494439942826046007589
+3236653915339309050400710399118863480174135729126354272327526198931847020456
+6728795325066963336117780662108205792489168581971029055585610731736547385276
+6380276425506456602527540117971724098601448357463139833373628747458351815403
+5433419999067815905003867353801202355724147944135805427332705248973655994186
+5041130611413678219355893476567393224303909082190781300190833131836924007165
+5654227122516796281838018086974384712482708966565668013921060459766010941761
+0550107899541363062705900288745023359281714958359096826397307447904564333619
+7694074509803666398360812784827236941956828332814974205954617687782092087303
+0997251851490998934287793587314012691376625709193571373268588370102514439184
+2763124051970683992048284118502185564135882596329582052566937100416646790947
+1498445969988310316737367710893103311537634234857638996253794816827403174591
+3409235603550443259621347217893233554496027667610159114124614916154055763095
+1198325464161885467428416604670527897561905627890990303514190544175156737750
+5869860752456948939764228528356856339078838226722969646304926903904720271260
+7550621174274970352102351832642427284726879688473896768703167609747279566439
+5578498800392719274981059063029204606278954141664029984815148568896796636434
+2663259247454948386361302515198028514970927235946532045204096515486837699846
+6925997198878817815883151755772929011530954905662647277529146304631954871875
+7981842193591589605589369634076907345944834305812025808883916881139977347406
+6104158312454962651296646292425018112638301705729188098557570438397461077275
+1107669799661841774127735327529629987980135495274197751725763357367533317639
+2595386396318128625002625818616041303229327960689610743787729001226196109507
+5078523249740107441649074205707503339857610479405501354002538172669003503829
+4356191810667246935208576620655055471197756731578925829743450128512240334555
+0857364422106089400098097980710060066914254172300542199332763840179799715395
+2104179819732919053768526215633403666084738829519651771281993035539758359068
+8781450433887737655547586394649717798423766234759515768872575211201196790591
+1773213950669559781858266965876148843986997239366002767913524466843361409330
+3498345026183151219733115820351724151015788117079632416971637874342694800810
+9209865880902821016061611090832735445696838927030768843853697669522942154966
+7227488553970025901787267380707303012994912546555721617521680092662101142028
+4912848777979814836521707093537471826821110236198096852733094988389478392581
+8255430850346483797946697754305745787159147586676699909367391555177886905439
+4354984318116361381459652470477414950669405827757863125230111000328882575478
+0655518167212766883031211722120463766886341101457050139738519752913754699466
+0984654027027880833056766431641756531642291956384797536599138648642741701263
+5420756335784238406846840625370460253690959084071991825552481499175959058377
+5949053421792234142884606332226448167839120570370707535455711251191807387373
+1274143941499679321050883552677572347377971057431359097681006230800441905433
+4051844084001679240936517390705136670076739932448640186313345352369221610604
+4837426872832723526396728877036669789154090875467323512305194341580241450118
+1685347224120973539459000033751051887902883571810921667754470616753277989433
+4539341149484795033723281521701291803170133581206157279751790372163062541903
+1411202633562600785638810288281580443225667903452442889326189645233595744838
+8704292410919276051488657556172485434602528126371477338338731272695247871214
+8531344208588042196247695295308496482430546116548334405206970596956241690495
+9592823688304857706697333346755997888475028602837035242143266422559083501581
+6744458763490333085563144012365643054194157661033194309985104088529575754028
+2534806827228478629743216744688778020961699159280663882846234689310077081736
+6024364166838206543521287616625394911441500124016866024811204707786292528961
+1603281589332853085867183258825475535891673425352359526353307011017102021845
+6229639266689296722655890932615138528947513810895234272775643400016991462766
+4682562543427367805737384621907606506084956620139938655220574272932080283397
+5080497672363160478194130557651083752225518739812293257597020321852115872321
+0423508984903210022531681471604161527283425018665371038789874350824128553133
+1838974789640955121321093762374555239210706518217412878830898523103817418183
+7246685831115344213967160913112271567741324804620469079393138672209754183205
+9576782459769012214555724662882616655925264453429317826369579546710887759595
+4276881268091161226599431968684120556631276490437760589000224109022110256760
+5022075066636881862874245112440744177946359096747914603998880496920718236536
+5846712087804226663296973642180785685584316325681616712576674198500848437376
+7270481096010523945334771956720006714196133587212624400370608940242114233954
+9257662158399796716927732769103582352343826936693477382089998797977999286844
+0635047656175995670212747442387427529440977381337458352545813688850563539619
+3029768685508418714596477252230715588593206795794876020845605080470098683651
+9496988775827449061579572659785772724941721637757846265570487428940047668565
+5266879736209501456715267417789381173273996244782286259433553292793669397502
+3309431808302492895087313521101191124546218312241301713223117215210051730192
+4058849195801312549213512806000001
+31 in base 16 == 1f
+%X for medium : 1D6329F1C35CA4BFB125BCBF2D63100001
+%#X for medium : 0X1D6329F1C35CA4BFB125BCBF2D63100001
+%x for 0: 0
+%#X for 0: 0X0
+%10s for "string" :     string
+%.4s for "string" : stri
+%*.*s for "string" :         st
+%s for 1 : 1
+%s for aa : [1.0000000000000000000000000000000000000, 2.00000000000000000000
+00000000000000000]
+%s for 4/3 : 4/3
+%s inter %.2s %4.2f for aa, bb, aa : [1.000000000000000000000000000000000000
+0, 2.0000000000000000000000000000000000000] inter 2. [1.00,2.00]
+"%s inter %.2s %4.2f for aa, bb, aa : [1.00000000000000000000000000000000000
+00, 2.0000000000000000000000000000000000000] inter 2. [1.00,2.00]\n"
+%10.5s for 3.5 :      3.500
+%10f for Pi     : 3.1415926536|
+%20.10f for 2.7 :         2.7000000000|
+%+20.10f for 2.7:        +2.7000000000|
+%-20.10f for 2.7: 2.7000000000        |
+%-*.*f for 2.7: 2.7000000000        |
+%20.10e for 2.7 :      2.7000000000 e0|
+%+20.10E for 2.7:     +2.7000000000 E0|
+%-20.10e for 2.7: 2.7000000000 e0     |
+%-20.10E for ii : 1.0000000000 E100   |
+%e for 1+0*I    : 1.0000000000000000000000000000000000000 E0
+%8.3g for 3.14159:     3.14
+%8.3G for ii     : 1.00 E100
+%8.3g for ii     : 1.00 e100
+%-20.10g for 4/3 : 1.333333333         |
+%20.13e for 4/3  :   1.3333333333333 e0|
+%e for 4/3       : 1.3333333333333333333333333333333333333 e0|
+  ***   at top-level: printf("%missing arg
+  ***                 ^--------------------
+  *** printf: invalid conversion or specification m in format `%missing argument to format
+'.
+  ***   at top-level: printf("%d missing a
+  ***                 ^--------------------
+  *** printf: missing arg 1 for printf format '%d missing argument to format
+'.
+%-20.10g for aa : [1.000000000         ,2.000000000         ]
+1.0000000000000000000000000000000000000 E38
+0.99999999999999999999999999999999999999770976478687585326332384035105970783
+05024613172747796087832576
+0
+0.000050
+b
+b
+a
+a
+[1.00]
+
+[2.00]
+Total time spent: 4
diff --git a/src/test/32/program b/src/test/32/program
new file mode 100644
index 0000000..2ffd900
--- /dev/null
+++ b/src/test/32/program
@@ -0,0 +1,107 @@
+1
+   echo = 1 (on)
+? alias(ln,log)
+? ln(2)
+0.69314718055994530941723212145817656807
+? for(x=1,5,print(x!))
+1
+2
+6
+24
+120
+? fordiv(10,x,print(x))
+1
+2
+5
+10
+? forprime(p=1,30,print(p))
+2
+3
+5
+7
+11
+13
+17
+19
+23
+29
+? forstep(x=0,2.9,Pi/12,print(sin(x)))
+0.E-38
+0.25881904510252076234889883762404832835
+0.50000000000000000000000000000000000000
+0.70710678118654752440084436210484903928
+0.86602540378443864676372317075293618347
+0.96592582628906828674974319972889736763
+1.0000000000000000000000000000000000000
+0.96592582628906828674974319972889736764
+0.86602540378443864676372317075293618348
+0.70710678118654752440084436210484903931
+0.50000000000000000000000000000000000003
+0.25881904510252076234889883762404832839
+? forvec(x=[[1,3],[-2,2]],print1([x[1],x[2]]," "));print(" ");
+[1, -2] [1, -1] [1, 0] [1, 1] [1, 2] [2, -2] [2, -1] [2, 0] [2, 1] [2, 2] [3
+, -2] [3, -1] [3, 0] [3, 1] [3, 2]  
+? if(3<2,print("bof"),print("ok"));
+ok
+? kill(y);print(x+y);
+x + y
+? f(u)=u+1;
+? print(f(5));kill(f);
+6
+? f=12
+12
+? g(u)=if(u,,return(17));u+2
+(u)->if(u,,return(17));u+2
+? g(2)
+4
+? g(0)
+17
+? n=33;until(n==1,print1(n," ");if(n%2,n=3*n+1,n=n/2));print(1)
+33 100 50 25 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
+? m=5;while(m<20,print1(m," ");m=m+1);print()
+5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
+? default(seriesprecision,12)
+? print((x-12*y)/(y+13*x));
+(x - 12*y)/(13*x + y)
+? print([1,2;3,4])
+[1, 2; 3, 4]
+? print1(x+y);print(x+y);
+x + yx + y
+? print((x-12*y)/(y+13*x));
+(x - 12*y)/(13*x + y)
+? print([1,2;3,4])
+[1, 2; 3, 4]
+? print1(x+y);print1(" equals ");print(x+y);
+x + y equals x + y
+? print1("give a value for s? ");s=input();print(1/(s^2+1))
+give a value for s? printtex((x+y)^3/(x-y)^2)
+\frac{x^3
+ + 3 y x^2
+ + 3 y^2 x
+ + y^3}{x^2
+ - 2 y x
+ + y^2}
+1
+? for(i=1,100,for(j=1,25,if(i+j==32,break(2)));print(i))
+1
+2
+3
+4
+5
+6
+? u=v=p=q=1;for(k=1,400,w=u+v;u=v;v=w;p*=w;q=lcm(q,w);if(k%50==0,print(k" "log(p)/log(q))));
+50 1.5612291269030992792061717254933840846
+100 1.6013353755908753487111410311704818784
+150 1.6069155486736591275233947740278385687
+200 1.6186599989915284815081751756012014738
+250 1.6262847062047467650860809884971000430
+300 1.6278227768451030011920245320536334380
+350 1.6321059051729866681896522731053701088
+400 1.6324242855329314481714056192369470068
+? install(addii,GG)
+? addii(1,2)
+3
+? kill(addii)
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 20
diff --git a/src/test/32/qf b/src/test/32/qf
new file mode 100644
index 0000000..29371c2
--- /dev/null
+++ b/src/test/32/qf
@@ -0,0 +1,69 @@
+
+[7/2    1   8/7     11/7]
+
+[ -1 -7/2  -4/7     -5/7]
+
+[  0    0 -24/7   -29/24]
+
+[  0    0     0 -311/168]
+
+[12, 1]
+266
+Vecsmall([0, 0, 0, 0, 0, 0, 0, 133, 0, 165, 0, 638, 0, 396, 0, 4268])
+Vecsmall([0, 0, 0, 133, 165, 638, 396, 4268])
+[8, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]~]
+[8, [-2, -2, 1, 1, -1, -1, 2, 0, 1, 1, 1, 1]~]
+[]
+[]
+[0, 0, [;]]
+  ***   at top-level: qfminim(L~*L,10^16)[
+  ***                 ^--------------------
+  *** qfminim: precision too low in qfminim.
+10000000000000000.000
+[0, 1]~
+[1, 0]~
+[-1, 1]~
+[-1, 1]~
+14
+23
+2
+-13
+
+[66  78  90]
+
+[78  93 108]
+
+[90 108 126]
+
+
+[35  67  99]
+
+[67 110 153]
+
+[99 153 207]
+
+  ***   at top-level: qfnorm(1)
+  ***                 ^---------
+  *** qfnorm: incorrect type in qfnorm (t_INT).
+  ***   at top-level: qfnorm(1,1)
+  ***                 ^-----------
+  *** qfnorm: incorrect type in qfnorm (t_INT).
+  ***   at top-level: qfnorm(x,Mat(1))
+  ***                 ^----------------
+  *** qfnorm: inconsistent dimensions in qfeval.
+  ***   at top-level: qfnorm(x,Mat(x~))
+  ***                 ^-----------------
+  *** qfnorm: inconsistent dimensions in qfeval.
+  ***   at top-level: qfbil(1,1)
+  ***                 ^----------
+  *** qfbil: incorrect type in qfbil (t_INT).
+  ***   at top-level: qfbil([1],[1,2])
+  ***                 ^----------------
+  *** qfbil: inconsistent dimensions in qfbil.
+  ***   at top-level: qfbil([1,2],[1,2],q)
+  ***                 ^--------------------
+  *** qfbil: inconsistent dimensions in qfevalb.
+  ***   at top-level: qfbil([1,2],[1,2],Ma
+  ***                 ^--------------------
+  *** qfbil: inconsistent operation 'RgV_RgM_mul' t_VEC (2 elts) , t_MAT (1x2).
+Total time spent: 16
diff --git a/src/test/32/qfb b/src/test/32/qfb
new file mode 100644
index 0000000..f16b1c9
--- /dev/null
+++ b/src/test/32/qfb
@@ -0,0 +1,16 @@
+[-1, 6, 6]
+Qfb(-2, -34, 17, -1.2031810657666797073140254201751247272)
+Qfb(-2, 34, 17, 3.6095431973000391219420762605253741816)
+Qfb(1, 34, -34, 0.E-38)
+Qfb(-2, 34, 17, 1.2031810657666797073140254201751247272)
+Qfb(17, 34, -2, -2.9945542852277529726974917240067615601)
+Qfb(17, 34, -2, -22194773194531041164.334277319893643413)
+Qfb(1, -1, 6)
+Qfb(8, 13, 6)
+Qfb(1, 1, 6)
+Qfb(1009, 60, 99108027750247771)
+Qfb(1, 0, 100000000000000000039)
+Qfb(1, 1, 6)
+Qfb(18446744073709551629, 4741036151112220792, 304625896260305173)
+Qfb(18446744073709551629, 7562574061564804959, 775103267656920011, 0.E-38)
+Total time spent: 0
diff --git a/src/test/32/qfbsolve b/src/test/32/qfbsolve
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/32/qfisom b/src/test/32/qfisom
new file mode 100644
index 0000000..21968b1
--- /dev/null
+++ b/src/test/32/qfisom
@@ -0,0 +1,20 @@
+[2, [Mat(-1)]]
+78382080
+78382080
+78382080
+78382080
+339738624
+339738624
+339738624
+339738624
+OK
+OK
+OK
+OK
+"Group([[-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]], [[0, 0,
+ 0, 1], [0, 1, 1, 0], [0, -1, 0, 0], [1, -1, -1, 0]], [[-1, 0, 0, 0], [0, 0,
+ -1, 1], [-1, 0, 1, 0], [-1, 1, 1, 0]])"
+"MatrixGroup<4, Integers() |[[-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0
+, 0, 0, -1]], [[0, 0, 0, 1], [0, 1, 1, 0], [0, -1, 0, 0], [1, -1, -1, 0]], [
+[-1, 0, 0, 0], [0, 0, -1, 1], [-1, 0, 1, 0], [-1, 1, 1, 0]]>"
+Total time spent: 876
diff --git a/src/test/32/quad b/src/test/32/quad
new file mode 100644
index 0000000..ec7bdb1
--- /dev/null
+++ b/src/test/32/quad
@@ -0,0 +1,12 @@
+1
+2.6180339887498948482045868343656381177
+1.6180339887498948482045868343656381177 + I
+1.6180339887498948482045868343656381177
+0.E-38 + 1.6180339887498948482045868343656381177*I
+-1
+  ***   at top-level: norml2(w)
+  ***                 ^---------
+  *** norml2: incorrect type in gnorml2 (t_QUAD).
+1
+2
+Total time spent: 4
diff --git a/src/test/32/quadclassunit b/src/test/32/quadclassunit
new file mode 100644
index 0000000..654c725
--- /dev/null
+++ b/src/test/32/quadclassunit
@@ -0,0 +1,628 @@
+? test(D)=for(d=D,D+10^3,if(!isfundamental(d),next);print(d," ",quadclassunit(d).cyc));
+? setrand(1);test(10^15);test(-10^15)
+1000000000000001 [4, 4, 2, 2, 2]
+1000000000000005 [2, 2, 2, 2]
+1000000000000009 [2, 2]
+1000000000000012 [32, 2]
+1000000000000013 []
+1000000000000021 [2]
+1000000000000024 [4]
+1000000000000028 [2]
+1000000000000029 [6, 2]
+1000000000000033 []
+1000000000000037 []
+1000000000000040 [8, 2, 2, 2]
+1000000000000041 [2, 2, 2, 2]
+1000000000000045 [4, 2]
+1000000000000049 [2]
+1000000000000056 [2, 2, 2]
+1000000000000057 [2]
+1000000000000060 [6, 2, 2, 2]
+1000000000000061 [2]
+1000000000000065 [4, 2, 2]
+1000000000000069 []
+1000000000000076 [2]
+1000000000000077 [6, 2]
+1000000000000081 [2, 2]
+1000000000000085 [2, 2]
+1000000000000088 [13]
+1000000000000093 []
+1000000000000097 [4, 4]
+1000000000000101 [12, 2, 2]
+1000000000000104 [2]
+1000000000000105 [2, 2, 2]
+1000000000000108 [2]
+1000000000000109 [4]
+1000000000000113 [2, 2]
+1000000000000117 []
+1000000000000120 [12, 2]
+1000000000000121 [2]
+1000000000000124 [8, 2, 2]
+1000000000000129 [6]
+1000000000000133 [2, 2, 2]
+1000000000000136 [4]
+1000000000000137 [4, 2]
+1000000000000140 [2, 2, 2, 2]
+1000000000000145 [4]
+1000000000000149 [2, 2]
+1000000000000153 []
+1000000000000156 [2, 2, 2, 2]
+1000000000000157 [22, 2, 2]
+1000000000000165 [2, 2, 2]
+1000000000000168 [334]
+1000000000000169 []
+1000000000000172 [3]
+1000000000000173 [2, 2]
+1000000000000177 [2]
+1000000000000181 [2]
+1000000000000184 [2, 2, 2, 2]
+1000000000000185 [8, 2, 2, 2]
+1000000000000189 [22]
+1000000000000193 [2]
+1000000000000201 [2]
+1000000000000204 [4, 2, 2]
+1000000000000205 [2]
+1000000000000209 [2, 2, 2]
+1000000000000216 [2, 2, 2]
+1000000000000217 [7]
+1000000000000220 [8, 2, 2]
+1000000000000221 [2, 2, 2, 2]
+1000000000000229 [2]
+1000000000000232 [6, 2]
+1000000000000236 [8, 4]
+1000000000000237 [4]
+1000000000000241 []
+1000000000000245 [2, 2, 2]
+1000000000000248 [2, 2, 2]
+1000000000000249 []
+1000000000000252 [2]
+1000000000000253 [76]
+1000000000000257 [2, 2]
+1000000000000261 [2]
+1000000000000264 [2, 2]
+1000000000000265 [2, 2, 2]
+1000000000000268 [2, 2]
+1000000000000273 []
+1000000000000277 [4, 2]
+1000000000000280 [2, 2]
+1000000000000281 [2, 2, 2]
+1000000000000284 [4, 2]
+1000000000000285 [2]
+1000000000000289 []
+1000000000000293 []
+1000000000000297 []
+1000000000000301 [2]
+1000000000000309 [14, 2, 2, 2]
+1000000000000312 [2, 2]
+1000000000000313 [6, 6]
+1000000000000316 [2, 2]
+1000000000000317 [2, 2]
+1000000000000321 [3]
+1000000000000328 [4, 2, 2]
+1000000000000329 [2]
+1000000000000333 [4]
+1000000000000344 [2, 2, 2]
+1000000000000345 [8, 2, 2]
+1000000000000349 []
+1000000000000353 [8, 2, 2]
+1000000000000357 []
+1000000000000360 [2, 2, 2]
+1000000000000361 [4]
+1000000000000364 [4, 2]
+1000000000000365 [6, 2, 2]
+1000000000000369 [4, 2]
+1000000000000373 [2, 2]
+1000000000000376 [26, 2]
+1000000000000380 [2, 2]
+1000000000000381 [2]
+1000000000000385 [2]
+1000000000000389 [2, 2]
+1000000000000392 [2]
+1000000000000393 [3]
+1000000000000396 [2, 2]
+1000000000000397 [4]
+1000000000000401 [3]
+1000000000000405 [2, 2, 2]
+1000000000000408 [2, 2]
+1000000000000409 [6, 2]
+1000000000000412 [2, 2]
+1000000000000417 [6]
+1000000000000421 [2]
+1000000000000424 [2, 2]
+1000000000000428 [12, 2, 2]
+1000000000000429 []
+1000000000000433 []
+1000000000000437 []
+1000000000000444 []
+1000000000000445 [2, 2, 2, 2]
+1000000000000453 [2, 2]
+1000000000000456 [2, 2]
+1000000000000457 [4]
+1000000000000460 [6, 2, 2, 2]
+1000000000000461 [4]
+1000000000000465 [4, 2]
+1000000000000469 [158]
+1000000000000472 []
+1000000000000473 []
+1000000000000477 [2, 2]
+1000000000000481 [2]
+1000000000000488 [12, 2, 2]
+1000000000000489 [2, 2, 2]
+1000000000000492 [24]
+1000000000000493 []
+1000000000000497 []
+1000000000000501 [12]
+1000000000000504 [24]
+1000000000000505 [2, 2, 2]
+1000000000000508 [2, 2]
+1000000000000509 [2]
+1000000000000513 []
+1000000000000517 [2]
+1000000000000520 [10, 2]
+1000000000000524 [6, 2, 2, 2, 2]
+1000000000000529 [2, 2]
+1000000000000537 [2, 2, 2]
+1000000000000540 [6, 2, 2]
+1000000000000541 [27]
+1000000000000545 [2, 2, 2]
+1000000000000549 [2]
+1000000000000552 [2]
+1000000000000553 [14]
+1000000000000556 [2, 2]
+1000000000000561 [6, 2]
+1000000000000565 [2, 2]
+1000000000000568 [2]
+1000000000000569 []
+1000000000000572 [36, 2, 2, 2, 2]
+1000000000000577 [4, 2]
+1000000000000581 [6, 3]
+1000000000000585 [2, 2]
+1000000000000588 [2]
+1000000000000589 [36, 2]
+1000000000000597 [4]
+1000000000000601 [54, 2]
+1000000000000604 [4, 2]
+1000000000000605 [4, 2]
+1000000000000609 [16]
+1000000000000613 []
+1000000000000616 [2, 2, 2]
+1000000000000617 [4, 2, 2, 2]
+1000000000000621 [12, 2]
+1000000000000632 [2]
+1000000000000633 [2]
+1000000000000636 [2]
+1000000000000637 [2]
+1000000000000641 [2]
+1000000000000645 [2, 2]
+1000000000000648 [10, 2, 2]
+1000000000000649 [14]
+1000000000000652 [2, 2]
+1000000000000653 [8]
+1000000000000657 [3]
+1000000000000661 [2]
+1000000000000664 [4, 2]
+1000000000000668 [4, 2, 2]
+1000000000000669 [36]
+1000000000000673 [2]
+1000000000000677 [2, 2, 2]
+1000000000000681 [9]
+1000000000000684 [12, 2]
+1000000000000685 [4, 2, 2]
+1000000000000689 [4]
+1000000000000693 []
+1000000000000696 [2, 2, 2]
+1000000000000697 []
+1000000000000705 [12, 2, 2]
+1000000000000709 [36]
+1000000000000712 [4, 2, 2]
+1000000000000713 [4, 2]
+1000000000000716 [2, 2, 2]
+1000000000000717 [8, 4]
+1000000000000721 []
+1000000000000732 [2, 2]
+1000000000000733 [2]
+1000000000000741 []
+1000000000000744 [2, 2]
+1000000000000745 [6]
+1000000000000748 [2]
+1000000000000749 [8, 2]
+1000000000000753 [2, 2]
+1000000000000757 []
+1000000000000760 [2, 2, 2]
+1000000000000761 [14]
+1000000000000765 [2]
+1000000000000769 []
+1000000000000776 [4, 2, 2]
+1000000000000777 []
+1000000000000780 [6, 2, 2, 2]
+1000000000000781 [2, 2, 2]
+1000000000000785 [6, 2]
+1000000000000789 [6]
+1000000000000792 [10, 2]
+1000000000000793 [2, 2]
+1000000000000796 []
+1000000000000797 [2]
+1000000000000801 [4]
+1000000000000805 [4, 2]
+1000000000000808 [2, 2]
+1000000000000812 [2, 2, 2, 2]
+1000000000000813 [8, 2]
+1000000000000817 [2, 2]
+1000000000000821 [2, 2]
+1000000000000824 [4, 2]
+1000000000000828 [2, 2]
+1000000000000829 []
+1000000000000833 [2, 2, 2]
+1000000000000837 [12, 2]
+1000000000000840 [2, 2]
+1000000000000841 [2]
+1000000000000844 [2, 2, 2]
+1000000000000849 [2]
+1000000000000853 [6]
+1000000000000856 [2]
+1000000000000857 [3]
+1000000000000860 [4, 2, 2, 2]
+1000000000000861 [6]
+1000000000000865 [32, 2, 2]
+1000000000000869 [16, 2, 2, 2]
+1000000000000873 []
+1000000000000877 [2]
+1000000000000885 [2, 2, 2]
+1000000000000888 [13]
+1000000000000889 [54, 2]
+1000000000000892 [30, 2, 2]
+1000000000000893 [2]
+1000000000000897 [2, 2, 2]
+1000000000000904 [2, 2, 2]
+1000000000000905 [2, 2, 2]
+1000000000000909 [2, 2, 2]
+1000000000000913 [8]
+1000000000000920 [2, 2]
+1000000000000921 []
+1000000000000924 [2, 2, 2, 2]
+1000000000000929 [4]
+1000000000000933 []
+1000000000000936 [2, 2]
+1000000000000937 [2, 2]
+1000000000000941 [6]
+1000000000000945 [4]
+1000000000000949 []
+1000000000000952 [4]
+1000000000000956 [2, 2]
+1000000000000957 [2, 2]
+1000000000000961 [2, 2, 2, 2]
+1000000000000965 [2, 2]
+1000000000000968 [2, 2, 2]
+1000000000000969 [2]
+1000000000000972 [2]
+1000000000000973 [6]
+1000000000000977 [64, 2]
+1000000000000981 [2, 2]
+1000000000000984 [2, 2, 2]
+1000000000000985 [2, 2]
+1000000000000988 [108]
+1000000000000993 []
+1000000000000997 [12]
+-999999999999995 [3872378, 2]
+-999999999999992 [2471436, 2, 2, 2]
+-999999999999991 [9144306, 2]
+-999999999999988 [1124902, 2, 2]
+-999999999999987 [913748, 2, 2, 2]
+-999999999999983 [13126428, 2]
+-999999999999979 [2148058, 2, 2]
+-999999999999976 [2984712, 2, 2]
+-999999999999971 [7798614, 2]
+-999999999999967 [11253252]
+-999999999999960 [481528, 2, 2, 2, 2]
+-999999999999959 [25233340, 2]
+-999999999999956 [9820206, 2]
+-999999999999955 [2185542, 2]
+-999999999999951 [11807322, 2]
+-999999999999947 [7261653]
+-999999999999944 [7665042, 2]
+-999999999999943 [17982820]
+-999999999999940 [1458740, 2, 2]
+-999999999999939 [1705720, 2, 2]
+-999999999999935 [3815348, 2, 2, 2]
+-999999999999931 [377454, 2, 2, 2, 2]
+-999999999999928 [3090498, 2]
+-999999999999924 [275778, 2, 2, 2, 2, 2, 2]
+-999999999999923 [2614376, 2, 2]
+-999999999999919 [20175156]
+-999999999999915 [878588, 2, 2, 2]
+-999999999999912 [3618208, 2]
+-999999999999911 [31532754]
+-999999999999908 [12931036]
+-999999999999907 [3859738]
+-999999999999903 [448308, 2, 2, 2, 2, 2]
+-999999999999899 [21701582]
+-999999999999895 [4135150, 2, 2]
+-999999999999892 [1620700, 2, 2]
+-999999999999887 [10190276, 2, 2]
+-999999999999883 [2498825]
+-999999999999880 [1139478, 2, 2, 2]
+-999999999999879 [9394500, 2]
+-999999999999876 [1305236, 2, 2, 2]
+-999999999999871 [13406168, 2]
+-999999999999867 [4308820, 2]
+-999999999999863 [7643020, 2]
+-999999999999860 [4852422, 2, 2]
+-999999999999859 [3024070, 2]
+-999999999999851 [1199052, 4, 2]
+-999999999999848 [6290596, 2]
+-999999999999844 [5296516, 2]
+-999999999999843 [3731152, 2]
+-999999999999839 [42706704]
+-999999999999835 [1378294, 2, 2]
+-999999999999832 [974764, 2, 2, 2]
+-999999999999831 [21097546, 2]
+-999999999999827 [7464103]
+-999999999999823 [4886300, 2]
+-999999999999816 [3866004, 2, 2]
+-999999999999815 [8445968, 2, 2]
+-999999999999812 [1381066, 2, 2, 2]
+-999999999999811 [3696512, 2]
+-999999999999807 [3253596, 2, 2]
+-999999999999803 [7540922, 2]
+-999999999999799 [17550154]
+-999999999999796 [2206538, 2, 2]
+-999999999999795 [1527752, 2, 2]
+-999999999999791 [31405878]
+-999999999999787 [5207448]
+-999999999999780 [760782, 2, 2, 2, 2]
+-999999999999779 [11011782]
+-999999999999771 [3947234, 2]
+-999999999999768 [1192776, 2, 2, 2]
+-999999999999767 [1232812, 2, 2, 2, 2]
+-999999999999764 [7211408, 2]
+-999999999999763 [1285040, 2, 2]
+-999999999999759 [3402540, 2, 2, 2]
+-999999999999755 [3392056, 4]
+-999999999999752 [3435556, 2, 2]
+-999999999999751 [2159360, 2, 2, 2]
+-999999999999748 [3957274, 2]
+-999999999999743 [11495572, 2]
+-999999999999739 [4758772]
+-999999999999736 [4989568, 2]
+-999999999999735 [2123290, 2, 2, 2]
+-999999999999732 [2033166, 2, 2]
+-999999999999731 [15293774]
+-999999999999727 [9307746]
+-999999999999723 [499770, 2, 2, 2]
+-999999999999719 [11514202, 2, 2]
+-999999999999716 [1035944, 8, 2]
+-999999999999707 [1111128, 2, 2, 2]
+-999999999999704 [3465724, 2, 2]
+-999999999999703 [10063644, 2]
+-999999999999699 [2335196, 2, 2]
+-999999999999695 [7306262, 2, 2]
+-999999999999691 [1922812, 2, 2]
+-999999999999688 [480138, 6, 2]
+-999999999999687 [3846148, 2, 2]
+-999999999999683 [3139616, 2]
+-999999999999679 [5354308, 2, 2]
+-999999999999672 [2178978, 2, 2]
+-999999999999671 [21071960, 2]
+-999999999999668 [6288672, 2]
+-999999999999667 [2443850, 2]
+-999999999999663 [2452980, 2, 2, 2]
+-999999999999659 [11343307]
+-999999999999656 [20023842]
+-999999999999655 [3183568, 4]
+-999999999999652 [2245500, 2]
+-999999999999647 [6567154, 2, 2]
+-999999999999643 [3389457]
+-999999999999640 [181472, 4, 2, 2, 2, 2]
+-999999999999636 [3131420, 2, 2]
+-999999999999635 [4518528, 2]
+-999999999999631 [889308, 2, 2, 2, 2]
+-999999999999627 [368676, 2, 2, 2, 2]
+-999999999999624 [3148536, 2, 2]
+-999999999999623 [7549890, 2, 2]
+-999999999999620 [6700476, 2]
+-999999999999619 [6401220]
+-999999999999615 [16746558, 2]
+-999999999999611 [6632280, 2]
+-999999999999608 [3452468, 4]
+-999999999999607 [4112552, 2, 2]
+-999999999999604 [1056580, 2, 2, 2]
+-999999999999599 [39104546]
+-999999999999595 [173704, 4, 2, 2, 2]
+-999999999999592 [1255062, 2, 2]
+-999999999999591 [16457136, 2]
+-999999999999588 [1333928, 2, 2, 2]
+-999999999999587 [7930856]
+-999999999999583 [8857554]
+-999999999999579 [2607088, 2, 2]
+-999999999999572 [1824280, 2, 2, 2]
+-999999999999571 [7737015]
+-999999999999563 [5842726, 2]
+-999999999999560 [787644, 2, 2, 2, 2]
+-999999999999556 [3220034, 2, 2]
+-999999999999555 [200052, 4, 2, 2, 2]
+-999999999999551 [34658682]
+-999999999999547 [1902240, 2]
+-999999999999544 [2464376, 4]
+-999999999999543 [11437680]
+-999999999999539 [1482104, 2, 2, 2]
+-999999999999535 [4463256, 2, 2]
+-999999999999528 [1134018, 2, 2, 2]
+-999999999999527 [22589484]
+-999999999999524 [2781866, 2, 2, 2]
+-999999999999523 [1317604, 4]
+-999999999999519 [851520, 4, 2, 2, 2]
+-999999999999515 [6167982]
+-999999999999512 [676026, 2, 2, 2, 2]
+-999999999999508 [4069512, 2]
+-999999999999507 [287934, 2, 2, 2, 2]
+-999999999999503 [18452280, 2]
+-999999999999499 [4420082]
+-999999999999496 [8867516]
+-999999999999492 [1799496, 2, 2]
+-999999999999491 [12931961]
+-999999999999487 [13431888]
+-999999999999483 [2954944, 2]
+-999999999999480 [658258, 2, 2, 2, 2]
+-999999999999479 [42634930]
+-999999999999476 [530106, 2, 2, 2, 2, 2]
+-999999999999471 [21070792]
+-999999999999467 [2013918, 2, 2]
+-999999999999464 [9339422, 2]
+-999999999999463 [12087026]
+-999999999999460 [1021798, 2, 2, 2]
+-999999999999451 [3551814, 2]
+-999999999999448 [871296, 2, 2, 2]
+-999999999999447 [2331996, 4, 2]
+-999999999999444 [760910, 2, 2, 2, 2]
+-999999999999443 [4886894, 2]
+-999999999999439 [7887366, 3]
+-999999999999435 [3573078, 2]
+-999999999999431 [36933388]
+-999999999999428 [5934232, 2]
+-999999999999427 [946428, 2, 2]
+-999999999999419 [7505708, 2]
+-999999999999416 [2201616, 6, 2]
+-999999999999415 [7505874, 2]
+-999999999999412 [3733110, 2]
+-999999999999411 [8423382]
+-999999999999407 [3056636, 2, 2, 2]
+-999999999999403 [438314, 2, 2, 2]
+-999999999999399 [5204266, 2, 2]
+-999999999999395 [440000, 4, 4, 2]
+-999999999999391 [14843488, 2]
+-999999999999384 [2002552, 2, 2, 2]
+-999999999999383 [19605798]
+-999999999999380 [2144856, 2, 2, 2]
+-999999999999379 [5267274]
+-999999999999371 [6940440, 2]
+-999999999999368 [1560324, 2, 2, 2]
+-999999999999367 [15851448]
+-999999999999364 [984184, 2, 2, 2]
+-999999999999363 [3479512, 2]
+-999999999999359 [10558080, 4]
+-999999999999355 [5188556]
+-999999999999348 [1472778, 2, 2, 2]
+-999999999999347 [2888262, 3]
+-999999999999343 [1765078, 2, 2, 2]
+-999999999999339 [2666192, 2, 2]
+-999999999999336 [2780706, 2, 2]
+-999999999999335 [8120196, 2, 2]
+-999999999999332 [12013212]
+-999999999999331 [3435240]
+-999999999999327 [2825256, 4, 2]
+-999999999999323 [8120472]
+-999999999999320 [7587430, 2]
+-999999999999319 [3528900, 2, 2]
+-999999999999316 [2926406, 2, 2]
+-999999999999311 [37355466]
+-999999999999307 [1562448, 4]
+-999999999999304 [6246412, 2]
+-999999999999303 [2311596, 2, 2, 2]
+-999999999999299 [5152654, 2, 2]
+-999999999999295 [3017552, 2, 2]
+-999999999999291 [682078, 2, 2, 2]
+-999999999999287 [6563352, 2, 2]
+-999999999999284 [3932520, 2, 2]
+-999999999999283 [6236089]
+-999999999999272 [4838350, 2, 2]
+-999999999999271 [19947218]
+-999999999999268 [948156, 2, 2]
+-999999999999267 [813228, 2, 2]
+-999999999999263 [27501864]
+-999999999999256 [1905422, 2, 2]
+-999999999999255 [3073434, 2, 2, 2]
+-999999999999247 [3166660, 2, 2]
+-999999999999240 [515266, 2, 2, 2, 2]
+-999999999999239 [2062240, 8, 2]
+-999999999999236 [1284828, 4, 2, 2]
+-999999999999235 [329016, 6, 2]
+-999999999999227 [11870444]
+-999999999999224 [2451664, 4, 2]
+-999999999999223 [4068880, 4]
+-999999999999219 [1892074, 2, 2]
+-999999999999215 [11095728, 2, 2]
+-999999999999211 [3135780, 2]
+-999999999999208 [982388, 2, 2, 2]
+-999999999999204 [4434704, 2, 2]
+-999999999999203 [1940982, 3]
+-999999999999199 [23272893]
+-999999999999195 [1021548, 2, 2, 2]
+-999999999999192 [4345186, 2]
+-999999999999191 [31434168]
+-999999999999188 [3242488, 2, 2]
+-999999999999187 [2881940, 2]
+-999999999999183 [17566220]
+-999999999999179 [2219256, 2, 2]
+-999999999999176 [7857696, 2]
+-999999999999172 [1647452, 2, 2]
+-999999999999167 [5533268, 2, 2]
+-999999999999163 [3285527]
+-999999999999160 [2366136, 2, 2]
+-999999999999159 [16819386, 2]
+-999999999999156 [757280, 2, 2, 2, 2]
+-999999999999155 [2575746, 2, 2]
+-999999999999151 [9783152, 2]
+-999999999999147 [210532, 4, 2, 2, 2]
+-999999999999143 [3668170, 2, 2, 2]
+-999999999999140 [2439050, 2, 2, 2]
+-999999999999139 [4723660, 2]
+-999999999999131 [2175188, 2, 2, 2]
+-999999999999128 [4662970, 2]
+-999999999999127 [11240510]
+-999999999999124 [313380, 6, 2, 2]
+-999999999999123 [580976, 2, 2, 2]
+-999999999999119 [27047100]
+-999999999999115 [2647620, 2]
+-999999999999107 [2325772, 2, 2]
+-999999999999103 [14333463]
+-999999999999096 [3888906, 2, 2]
+-999999999999095 [40990872]
+-999999999999092 [1314992, 4, 2]
+-999999999999091 [1503646, 2, 2]
+-999999999999087 [9765916, 2]
+-999999999999083 [11734392]
+-999999999999080 [909790, 2, 2, 2, 2]
+-999999999999079 [18131164]
+-999999999999076 [5363012, 2]
+-999999999999071 [30434221]
+-999999999999067 [2822928]
+-999999999999064 [4215872, 2]
+-999999999999060 [929084, 2, 2, 2]
+-999999999999059 [2815234, 2, 2]
+-999999999999055 [9636108, 2]
+-999999999999051 [4857524, 2]
+-999999999999048 [1242132, 2, 2, 2]
+-999999999999047 [24219706]
+-999999999999044 [2202860, 2, 2, 2]
+-999999999999043 [3834587]
+-999999999999039 [2550480, 8]
+-999999999999035 [816218, 2, 2, 2, 2]
+-999999999999032 [9616060]
+-999999999999031 [19377240]
+-999999999999028 [937052, 4, 2]
+-999999999999023 [24634500]
+-999999999999019 [8268794]
+-999999999999016 [2220068, 2, 2]
+-999999999999015 [1964780, 2, 2, 2]
+-999999999999012 [2168582, 2, 2]
+-999999999999011 [10976478]
+-999999999999007 [2903838, 2, 2]
+-999999999999003 [4036316, 2]
+? setrand(11);quadclassunit(-8419588).cyc
+[176, 2]
+? setrand(2);quadclassunit(-1459008).cyc
+[16, 4, 2, 2]
+? setrand(7);quadclassunit(-3799812).cyc
+[54, 2, 2, 2]
+? setrand(1);quadclassunit(-13163208).cyc
+[156, 2, 2]
+? setrand(38);quadclassunit(-29920).cyc
+[4, 2, 2, 2]
+? quadclassunit(-13163208,,[0.1]).cyc
+[156, 2, 2]
+? setrand(1);quadclassunit((2^70+25)).cyc
+[17]
+? setrand(1);quadclassunit(8*3*5*7).cyc
+[2, 2]
+? print("Total time spent: ",gettime);
+Total time spent: 4964
diff --git a/src/test/32/quadray b/src/test/32/quadray
new file mode 100644
index 0000000..5865708
--- /dev/null
+++ b/src/test/32/quadray
@@ -0,0 +1,1126 @@
+-15: x^2 - x + 1
+-35: x^2 - x - 1
+-51: x^2 - x + 1
+-91: x^2 - x + 2
+-115: x^2 - x - 1
+-123: x^2 - x + 1
+-187: x^2 - x + 3
+-195: x^4 - x^3 + 2*x^2 + x + 1
+-235: x^2 - x - 1
+-267: x^2 - x + 1
+-403: x^2 - x - 3
+-427: x^2 - x + 2
+-435: x^4 - x^3 + 2*x^2 + x + 1
+-483: x^4 - x^3 - x^2 - 2*x + 4
+-555: x^4 - x^3 + 2*x^2 + x + 1
+-595: x^4 - x^3 + 5*x^2 + 2*x + 4
+-627: x^4 - x^3 - 2*x^2 - 3*x + 9
+-715: x^4 - x^3 + 8*x^2 + 3*x + 9
+-795: x^4 - x^3 + 2*x^2 + x + 1
+-1155: x^8 + 15*x^6 + 32*x^4 + 15*x^2 + 1
+-1435: x^4 - x^3 + 5*x^2 + 2*x + 4
+-1995: x^8 + 15*x^6 + 32*x^4 + 15*x^2 + 1
+-3003: x^8 - 9*x^6 + 80*x^4 - 9*x^2 + 1
+-3315: x^8 + 9*x^6 + 77*x^4 + 36*x^2 + 16
+-20: x^2 + 1
+-24: x^2 - 2
+-40: x^2 + 2
+-52: x^2 + 1
+-84: x^4 - x^2 + 1
+-88: x^2 - 2
+-120: x^4 + 2*x^2 + 4
+-132: x^4 - x^2 + 1
+-148: x^2 + 1
+-168: x^4 - 2*x^3 + x^2 + 6*x + 3
+-228: x^4 - x^2 + 1
+-232: x^2 + 2
+-280: x^4 - 2*x^3 - 5*x^2 + 6*x - 1
+-312: x^4 + 2*x^2 + 4
+-340: x^4 + 3*x^2 + 1
+-372: x^4 - x^2 + 1
+-408: x^4 + 2*x^2 + 4
+-420: x^8 - 3*x^6 + 8*x^4 - 3*x^2 + 1
+-520: x^4 + 6*x^2 + 4
+-532: x^4 - 3*x^2 + 4
+-660: x^8 - 3*x^6 + 8*x^4 - 3*x^2 + 1
+-708: x^4 - x^2 + 1
+-760: x^4 - 2*x^3 - 5*x^2 + 6*x - 1
+-840: x^8 - 4*x^7 - 2*x^6 + 20*x^5 - 3*x^4 - 32*x^3 + 64*x^2 - 44*x + 19
+-1012: x^4 - 5*x^2 + 9
+-1092: x^8 + 3*x^6 + 5*x^4 + 12*x^2 + 16
+-1320: x^8 - 4*x^7 - 2*x^6 + 20*x^5 - 3*x^4 - 32*x^3 + 64*x^2 - 44*x + 19
+-1380: x^8 - 3*x^6 + 8*x^4 - 3*x^2 + 1
+-1428: x^8 + 3*x^6 + 5*x^4 + 12*x^2 + 16
+-1540: x^8 - 4*x^7 + 20*x^6 - 46*x^5 + 93*x^4 - 114*x^3 + 92*x^2 - 42*x + 9
+-1848: x^8 - 2*x^7 + 3*x^6 - 14*x^5 + 65*x^4 - 64*x^3 + 46*x^2 + 24*x + 4
+-5460: x^16 + 9*x^14 + 44*x^12 + 261*x^10 + 1029*x^8 + 1044*x^6 + 704*x^4 + 
+576*x^2 + 256
+x^20 + 27*x^19 + 601*x^18 - 4014*x^17 + 9878*x^16 - 12222*x^15 + 8299*x^14 +
+ 513*x^13 - 12221*x^12 + 18036*x^11 - 17652*x^10 + 18036*x^9 - 12221*x^8 + 5
+13*x^7 + 8299*x^6 - 12222*x^5 + 9878*x^4 - 4014*x^3 + 601*x^2 + 27*x + 1
+x^88 - 40020669926358941160*x^87 + 24818793423306608856949584839454391158484
+*x^86 - 451973748435231895940407803697160552367624*x^85 + 121004331374769746
+054948634032289469332999202*x^84 + 10059034519705811655247252004833057160111
+397504*x^83 + 351284965440856373689297063590147136894509846260*x^82 + 707678
+7287183022340559185242964779073439110772112*x^81 + 7424864222152380159098443
+6783829317747047323123243*x^80 + 3016669049298212175376092685252207677089225
+7842640*x^79 - 8503049963424563714394067134889147531482230019127096*x^78 - 6
+465786499366041070602916523486769538280412443510592*x^77 + 42150640446078238
+28889410160434091263108115351306399268*x^76 + 119525498598846149476451596656
+291689313170011135603889064*x^75 + 20718604339256925426029633374659465824208
+08944831769026648*x^74 + 270442350108592451558861828173327053458397647311211
+61439752*x^73 + 285289444253762620585561596576563633155859094294552566693317
+*x^72 + 2511360320483460798105377398519192324398612242438706127652544*x^71 +
+ 18767162620094934148004508464072766541604227783326918296860524*x^70 + 12042
+3236654140529086575067911655764408706511359484732574173280*x^69 + 6698817371
+51097381424117880193907743151768744395737304965311638*x^68 + 326030488372458
+1257592451461949148632593587481718123636435123040*x^67 + 1401218174432359703
+4445628272252822116949974099153581386829916444*x^66 + 5367567444185487660948
+9208131197694348596313231643993076280314304*x^65 + 1849586073448214933624859
+71605260396892443743494472396759635803315*x^64 + 578456213592452525292885126
+678569328965911834184953238551507495512*x^63 + 16556817033476544452278210145
+76515450466671643615335188802513429760*x^62 + 436878840618787239769625306249
+7812500894047454768846457782824580152*x^61 + 1068901472860957747029160191079
+1767817886254017900185293235705392064*x^60 + 2434351573155431230076300160060
+7496079487124511808873652848483412160*x^59 + 5169291351655811457272426459136
+7158576380942435990299052219843612304*x^58 + 1022998330750281135258911239455
+74251052215437006684583051817380710576*x^57 + 188216797464058241448403558395
+102741401235865324690992824034126437157*x^56 + 32060490325705765556225025226
+1154391117300969466803557524988405580272*x^55 + 5026257120330186308702396982
+13303008551061011852403188260952091908532*x^54 + 719344126023656757735145641
+575616692987264009491520118662670217955328*x^53 + 92863816021928333070040902
+0325566534550048821651703177989079236808666*x^52 + 1060255920795694954480037
+612092276836813066528658822564102146651872808*x^51 + 10301589215685511855337
+00012926850740762293883499745887488519869745108*x^50 + 771562428494427189300
+153767022260995609490878443644218508155682351496*x^49 + 27346597807333111348
+9250648988966144580871198921998690911757680224167*x^48 - 3923005119785941970
+23702549831167038133623220261146198494006317061248*x^47 - 107531566077276356
+7397744183394797701786847214693166357365831146616968*x^46 - 1589451619928021
+606930471659130946568147974811578111348229080651241920*x^45 - 17807285584553
+57793403089789263493950243261264268279117584476244784076*x^44 - 158945161992
+8021606930471659130946568147974811578111348229080651241920*x^43 - 1075315660
+772763567397744183394797701786847214693166357365831146616968*x^42 - 39230051
+1978594197023702549831167038133623220261146198494006317061248*x^41 + 2734659
+78073331113489250648988966144580871198921998690911757680224167*x^40 + 771562
+428494427189300153767022260995609490878443644218508155682351496*x^39 + 10301
+58921568551185533700012926850740762293883499745887488519869745108*x^38 + 106
+0255920795694954480037612092276836813066528658822564102146651872808*x^37 + 9
+28638160219283330700409020325566534550048821651703177989079236808666*x^36 + 
+719344126023656757735145641575616692987264009491520118662670217955328*x^35 +
+ 502625712033018630870239698213303008551061011852403188260952091908532*x^34 
++ 320604903257057655562250252261154391117300969466803557524988405580272*x^33
+ + 188216797464058241448403558395102741401235865324690992824034126437157*x^3
+2 + 102299833075028113525891123945574251052215437006684583051817380710576*x^
+31 + 51692913516558114572724264591367158576380942435990299052219843612304*x^
+30 + 24343515731554312300763001600607496079487124511808873652848483412160*x^
+29 + 10689014728609577470291601910791767817886254017900185293235705392064*x^
+28 + 4368788406187872397696253062497812500894047454768846457782824580152*x^2
+7 + 1655681703347654445227821014576515450466671643615335188802513429760*x^26
+ + 578456213592452525292885126678569328965911834184953238551507495512*x^25 +
+ 184958607344821493362485971605260396892443743494472396759635803315*x^24 + 5
+3675674441854876609489208131197694348596313231643993076280314304*x^23 + 1401
+2181744323597034445628272252822116949974099153581386829916444*x^22 + 3260304
+883724581257592451461949148632593587481718123636435123040*x^21 + 66988173715
+1097381424117880193907743151768744395737304965311638*x^20 + 1204232366541405
+29086575067911655764408706511359484732574173280*x^19 + 187671626200949341480
+04508464072766541604227783326918296860524*x^18 + 251136032048346079810537739
+8519192324398612242438706127652544*x^17 + 2852894442537626205855615965765636
+33155859094294552566693317*x^16 + 270442350108592451558861828173327053458397
+64731121161439752*x^15 + 207186043392569254260296333746594658242080894483176
+9026648*x^14 + 119525498598846149476451596656291689313170011135603889064*x^1
+3 + 4215064044607823828889410160434091263108115351306399268*x^12 - 646578649
+9366041070602916523486769538280412443510592*x^11 - 8503049963424563714394067
+134889147531482230019127096*x^10 + 30166690492982121753760926852522076770892
+257842640*x^9 + 74248642221523801590984436783829317747047323123243*x^8 + 707
+6787287183022340559185242964779073439110772112*x^7 + 35128496544085637368929
+7063590147136894509846260*x^6 + 10059034519705811655247252004833057160111397
+504*x^5 + 121004331374769746054948634032289469332999202*x^4 - 45197374843523
+1895940407803697160552367624*x^3 + 24818793423306608856949584839454391158484
+*x^2 - 40020669926358941160*x + 1
+x^180 - 51943580593235209115083942712389400740668999369044471856895869572767
+632154790719027854950985687*x^179 + 2547067915519807589841747083394507454979
+0011527047653789753822310509943417124290253283094780925633779566003695241876
+25607464220675698078104131248853904115685519116107718359916443145223952387*x
+^178 + 856553204115408701571995615320558014258368114267533599476048702248757
+8902931990647387774030794984717551258507939754932426394582122894184128382067
+3569718022270291080598668993546776793532380154605306*x^177 + 762080502049470
+4596056107304576599883142953678664313128034639146864833136453854583485947432
+8177873097803164055573630934557797549232895603414584717394309407907786380469
+1683834066404398393999145427753183790*x^176 + 308956387636466960153212938167
+7324702311668880384172270669030644914696644648416192123216702361395347555520
+1495693461977412751821857739072346944409047470651675235147675440424833117756
+620892810879743135932320774*x^175 + 4884082662966665508762665969667649569784
+5383873744492103182446854649967702283785050616521253612447115822047297077954
+9614257317623478309277167745811150568304415956966429217813229788441116863100
+4611840186992685967179*x^174 + 124005751296377747143710892598982088036932674
+2142684503704154223658839486788375142268292156393058732202380086677633411654
+8906703591687073852643697697716348962297583421466683339923965234468615997649
+73368284228647193399*x^173 + 77807342164244792507055296791047457009683139076
+6947550930281827860194252415153283906741350015821421760488076097868784697023
+0684361377176470035198102530788844464458277074023468487612291349831916437723
+0037354352780222249*x^172 - 266780829858115272388789250462200876445272313022
+9458822467054384295379499065005972497338635889603051515741573734931874924695
+7762408306245748926170958653384087543385833522528441856486249210097660034861
+41241557823792412532*x^171 + 14828387316468627026106844845255173547861518903
+8589804985538974108300682790820861191991101023068971076505863346789787263249
+0126030705881134291752595991307720023292738164624564498269365143236280388326
+494814603159167594322356*x^170 + 2072714630254694525113015470563866763470442
+2896220054319081495621750749375349838158314700070013284414958575563065629489
+9298590460673967042987581924280852650108640761845803254699501956119005286958
+261737898381117495750422003980*x^169 + 4082791913658668250669500487553396688
+5085691856416081922391794020222319071345465620337482939902695391053957179581
+6611178564058054521757672875527012214760684410920857152888054853976552319787
+6482624865506579220641586389147210551*x^168 + 259189082653047933938409996483
+4388754704904320561628999269016762833054641395201768798413743705844841716523
+7122469823713043234758088183799986341785098434142194216335145773402440120312
+8996614247360365624068492442391061043753826375*x^167 + 187953325502846394560
+1845576629993961992277419183089685715578164443785846738744772926323542142109
+3239442057010359289122214569790614009609800219235849343052099855997036406115
+0923659276590113217049579865275822653265241753456459305677*x^166 - 991521344
+5331010597888861249891641758957210552260378821158082129353375249195112911754
+5101896722210096101308894624313777264446536224444928929934283573873319884763
+96421078624456037269503565600020449431475165029149896378441501395456086*x^16
+5 + 870693463207410936114722926699616258456056310070174066271951439226764821
+3754498248686363880077592953938815295745097173153881494887680426204688519814
+5897926708879854493196612672343274340176910775178567635481360192954408109761
+462192666*x^164 + 3291269524452623301119531480681600174871375032940729132937
+0111633092058003369070348946123426594086592059620504463833497832222752798945
+6786539655327366403796168376108709100410788811433213881703418728731545343808
+0442501304279059153710266*x^163 - 484246978651008470853848049302633937841710
+0550099032573348884254048698254002568503682165709558099310997622779159632947
+3025430835921039731844769840401840637061784295187888579980063280775750740026
+326213445261209232925320378956182578938287*x^162 + 2366596557978274147475255
+9694980204169227021539516608112189319106501133980328077568567038119905399100
+7262473707379165967733688444634721899783534252243520926838729652205328556398
+93844358125373580233579396300331967493836532017339326648417*x^161 - 17861170
+0215272735065228998593221635895564621730723507909826896490382687997403628306
+4372396936244334959016553113715105764010891971421665085465778102274753181053
+1359367179288752406620535700142801097476915172283884503644920276078920893715
+961*x^160 + 5442886569416213072883949195207003561672975168183342200753916623
+1352236561897735153650345477157913505173224695785900066453147636135746522196
+8827923591138659172463973059604218652821751160134239388400400287612441644522
+959559654240096950196384*x^159 - 1878459194456192332344340381238953837099207
+3242518317157665963817987346890603378811086853859511458448823118145390556032
+4154260960085054869261002833248663395080605671172589053299559751809388877985
+0319337022684143396368259434426190698508218580*x^158 - 184127686942437509991
+1682977301117955663562464265148547217904344728959181240974449884065092029207
+1219636898969097801966235466592457246648086083637230209961453625153292993135
+3654541155851737335657948991190630601809654864812836013412428233332508*x^157
+ + 6272745337714289642780597731451440214649776184728899306566105704002623998
+3846347725688034820317498349655377903497224390190783460776334008364647848917
+2185092927745795075828017034702156144511828098427591206009505705409852728295
+6922223630676347544*x^156 - 130604497267883349741727434418972048161536102040
+0079797720405754446746742329603550983122106752795162131799337624121456619065
+4040984420476843424034832196407821304821044910660899700295392042290695059542
+0192036616889048813209519926849958791965413764*x^155 + 193926478941415365968
+2053039191650223015193274556301436638340272814361000782491228757908126714135
+5744831221609082890601733276886531625139761625422579999591146356457708488690
+66496229883200724281039999541182828857613242844117034223500903997780849964*x
+^154 - 208974081851580435134258388853435474877812078509775426825496895616316
+4247844796032344219123951883470813854035626246176679417061266672681500232259
+0819881630082047200072248185982748889171705144305698252911851141436968395735
+658717414968653352018931248*x^153 + 1672646641882017449123000870294181985430
+0761766363953593948968045530794890931499833945021531203099422476121576329491
+5115325960697513628674724029981734640823975379662772003784140453861998461512
+834247290509716832865395591217427305274661390000337103156*x^152 - 1133671449
+3365295420476716878286616778916616227898741966819659317216135015136322466563
+9827520548877905001507920274192410200096811676503284991805981968482979122155
+1667739765953276248859306767909175625527281202034750975189910458947747942582
+531447345892*x^151 + 9686019535728726397130721344019529061590234306774474243
+9775684839133142925738217830167297947427381690011105467748624943273307935251
+9499655312785116384255798310740712404573339361423884780107379495371631287103
+7645627569348897599991117672316256550430096*x^150 - 119041422102704367724958
+8670984575013578769595187713509039012506017888268506357273229685836102923546
+0042880742312037127040383670254597841508519424535302963725051506007472454825
+8974287875801351389617602402488639578930420752407411968308213028222350043652
+*x^149 + 1390123122192263005517637294725114792837919558648428420458046731138
+9247265920089325134952307005121333495864776226593824308305802270042865765522
+1706646232757561716861576911604612712092794424651617086755060497490406116694
+4942837594012909743669233042838778*x^148 - 123556176727604048392495454055977
+6399401450331126081896858806346942631913417189093648332266104142047071085393
+9823740200206373338590719973268754644225041558662153246796876936041537325415
+712055287848699094590808089508684909536725801531454334504662219155298*x^147 
++ 78338857321405672298333791473025282082268588399811517655878927064487660842
+9607987213338588537735184368455093544029691810855962878302724567812912958403
+0825298526758499964573195767901492416707505928731723979119497738428625056534
+2370732550549861221973628014*x^146 - 337429582052616696434247676773381390391
+7055420548509812685321889562165936397719575630122778103303140528950818170117
+1026255806634894861047352747650017243235019037703269391120793396636598558834
+3500355068020592838333693543614719695933939552850201760137124456*x^145 + 126
+7022512449095517765635963857489440320956185849532448394191453064873093377283
+3265337003928417799194535821386624283453961529673399508105934582815314275613
+7678638219426069444953840237686338226443443810754745407998159540925231095791
+4375045407870857103723132*x^144 - 153719609646143637199240438779331521362833
+0310998022856189520090149655383700607165028197046414621578662674582267335714
+8666559983310416796564643262431404031085708670498514131879172395315308884725
+086995597751964859239544450069420287019254955379338574827976392*x^143 + 2752
+6304730305492309519611803297873567100995379403820014027217572772469997269963
+1748009334990330332456740707557146239264293783419594063347012801512753255648
+8881765170905842263824483323437412768565907107652500759762684840263032684703
+37380600828996170962184046*x^142 - 35523095157828313337139462591351702816577
+1657314332343178845563474958542277571903631744992145135630234735095041102708
+2345107273636547410151070834277389339249300538652341339871040322822838485719
+858336476359261521139254885461283464480406069617193177086233150446*x^141 + 3
+4465803554989246489275769454764022910035142135182748059069252044933640478411
+7307420416885830806110765171489332421312130211574457885515807957701177125587
+6661225789242989640797022695297712572706533434600560766081391785453226598427
+1205078552129981020849945722662*x^140 - 268373722516115839581073071008494919
+0456447093682830001175926782807962713700083271168738651540876330140533486735
+6212670696857859739463338404796759488859624213038265846572508064515479105995
+6333436847011768914542767112707721040710586854450242752202162514502517912*x^
+139 + 1742444297891369493991077844263622734267621349666334184372478600323885
+9274274057411635646353155281298451648136704209002938845039923935043096379256
+4945721786177563429720654144039386133241554831725367065310144005542392522004
+1645737733894132094639323829473024157820*x^138 - 963039078892037951990470202
+7468142690696595830419680618831266273162745223389393610063433083213953579433
+1498149151107810908751113241849208570338521838179678717281819508263987198750
+1951924824700995996767534695158748257780864272656666861530432230128739030598
+8300028*x^137 + 457447376242160839222865523212081512251204172769554541536513
+6010675335568757571514112103791600745366139173664147588418919365588570555754
+7201003803879915712080042312095406320055413204732204964260573379515119902554
+321889204610298573371864198642424001195728006133182*x^136 - 1868860578136529
+9491100566653843653174297026478659238845320831747624530880395790502643308730
+5462268585078679776019946603866991516563272428283420299615035631054324035580
+7669631492489263529234747091359176119230324423792605912297491073916656362078
+32174634098946284598*x^135 + 65093076668803329116252899967129264121954195081
+3165825173825622033622982444205859242607493663511833086292972086895883603422
+4352287091011138657074487961579204189764700353920301472872505685227133477493
+45517047708745645767806883427498415677547239236298519819304443846*x^134 - 18
+8737857214948682414768736751123125995536055732425264657481481197221465136381
+8263080004881765201151009039934832304971738613022285488660591980899059758561
+7366670880266091842937662126151457793355558304191543211766144762254313419741
+75714575265820939899419742919508060*x^133 + 42953889394645520155740965583894
+8254975167613584775116826651538942451816286881091436166450578237021021126058
+7376408754507104013237607018922017023401049094197048725524392809860540776061
+0681787413121689140926769612819886834513469809393226136437731088172895759675
+25206*x^132 - 63261727328918865906350940397161109868277289808692349026207015
+7309911965921420948111081525007085507792610014228611379115265836635218591862
+4482685303352489099685390524441351132050179570452776530090912230189731431879
+125807895290459527651483474680605068851138729175634*x^131 - 1119917054839792
+2751688900486058857057243360132605635779686384574213339264438353376214516815
+2207498910643644483743753772520064969246600402986900436948848152659373181329
+5043050789946887406470007794027558566892567839570362080341591028664006259784
+979214096682612622692*x^130 + 4271314220049466852560738338393246245128105321
+7108326600567680720579375803199815405014779522998261575404021302604267102318
+9973496984002749534274370752056057799997646445393423249459072172570153388076
+90270224780252688493775073451490812831750493851260007482523315136586*x^129 -
+ 148023590100710673141860886464816208028643509093283015533625006596766946972
+6741578398020050334876578221252235677233082465182051389340632694789790351225
+9115953643872578590861494318898368229141353955324644187699428307147375218229
+1248178529493117079561875143739124185634*x^128 + 246175641614565897679653848
+5278045743286736401969417596627545851445013857575343959525966011452777041860
+1869322995580927692188758796260764328940765798775338277071600497396412898284
+6342663264119335560750008714258096212502569271025201095741906534219351860811
+124404494196*x^127 + 1990565893680671595442880060323376805411690898242032023
+4239133079106312028397798694740007864603898415855926572783576585034622967997
+5575446479539827712484639591071048813727255731760017261708300771575388890158
+591817638333201062459001177511936169630253047386566741135222*x^126 - 3016573
+2341675486865253308430441797191448447887193219597832133129727214089635223501
+3420645212244383261917032590855201496548201420356430642227248391186457171733
+8876314939133108407277975244227776960656325124355051502725255975073566061339
+620994099300340563608380613367050*x^125 + 1257948144362960333134666662231940
+3520935251787268873160231319726921665830921964289640164226960267401382195941
+0507546787902030963701849030258171787071185516798073095493413189997247441301
+2363645824641255038770075189157898962912949013449974648054148641764400320533
+8871882*x^124 - 366475271260105058017315536083033948306360270631496526170966
+8533370069635382315040133597595175856841519762702237383805014287542574172530
+6210807276260801506088986006283626986882464595447299254658758485568943925875
+853699462405048328247959368186852953798221740253247284792*x^123 + 8471667404
+0018254168541029547267006611224730675524513635334023354380604251758054675593
+5413776225446989659313910683079873583112382327573250917778108062870743163021
+6965584066720515779759514188951805094972301673794254962048375194153324360075
+8727975735045108792516939770864*x^122 - 161146935717428461799784793801173853
+4799737191324944707968337975196134044400893575466678041623869973266066574271
+1435647219232676062975775017270732119064549881937724549671623652481177104960
+6102487507016989267838733791065664530366349655384775511706599600386112512946
+557632*x^121 + 2522694849864480951479825412582961761101029175375006299005032
+3620295897587695065324670940000207944207812042399077482469097012869133985212
+6354595110186421998141347799854111539239085705015451817020704883026517132793
+698058647769011690212936405831828960609809154392310067086*x^120 - 3123954350
+9500414655822246820581466016766786100370771446195101114970617420873338985187
+2315673998753642061765284674623730429562551231647203516276142369010876697525
+2061798837483708658347309888670578337964579148520979957165473678360131302986
+17785547855899873691057926811306*x^119 + 26093073450128435322247521043157618
+8712392067239030279298185926981851613832003883595362112194063431825921469777
+7131659026396971729721032938463093179955600695600080670675651357367571081793
+3768796854158042814794986008423382503555686761603718155957955520619915926285
+1046258*x^118 - 546620888417310306456913297002373587712583541328636451073449
+6886209350963457906027266059482196156973048570086277940148682564568467798438
+0285254978737341206009503937974698380503149962544290779617099637261387918629
+06553536781663415296571417609896418531671323802662549340*x^117 - 50182021729
+3272384861232464186597002893681068429473599603378492391207630929901153313074
+7556376877685127771266174954090809732508146930392545806488414232849981602588
+1335383921214771830072947665323419883309028215410288472590477875403748309525
+8171403839074803113722901930619*x^116 + 118406103336876158577917454186709740
+5449294198484094378541103762383848536861007244183348157579457933081098099434
+7987985778741196393539613217121527222032672240767536916435194526743758819619
+0063225181878442213678933572194923238652257406338724947439730929871504295850
+1899245*x^115 - 176734272677759520180709847378647130652166835479092271077232
+7067981192822476939413912953115815829947318808933249885514336602487836197536
+9611127131928890515684844943744748937382837125994011164383657897820620204611
+77419977223148716322298719262705865767989172293892727336803*x^114 + 17753024
+8530519942716171951800965388948822797211110233322549229100349316200278964800
+3343785390491378071036048329384421918922891522117638503012421231548652255160
+9599399433846703918342084346728100760000876417162144138571778762943474072695
+63098844605653811371200441716482612*x^113 - 68055808039422415389652936779491
+5026270001374161671086886435342385993661681436002138177107204224273906832078
+4550021770688659201646483026969760576245696112286845508450285259619203332272
+0607576466516522822599227539775561873960850804338603845784871088630793662781
+4494090172*x^112 - 171345855168993153971261857965957509597335839923271917993
+0591415159154748543112880240425751510004701705465610982555391151034624958936
+9879115829869566643976034053215829935761929815383663545384160654490729816046
+26649741228358475390951398611771434975819632467920927623648602*x^111 + 47552
+8035498943149856772358089912005247194452214038396267610512853006205877224336
+1926512880782786766025384282981294577511242497413537295024045772317451006411
+5818078293866049727834002439604775548744427611299658913382859972919684056285
+23953865800092933414124830547621130787*x^110 - 67242845398127603346764378735
+8630417913935967603492325560313387832908161625682986070993808856191981689362
+7337618874976711707752413017492294772666895660870194024839210050819796389750
+1107885069331081963929674153546422780993129544541729439872603263786386221427
+59365186532673*x^109 + 55277967193070494108505087891929956655236889687657826
+8175977451438771610569912425740087266827286735858716699401268464277514456005
+1393232906738794805715412356740750890873873435323228244674952180988795091974
+844777831874994317609349719963129292674653332785827444869904448205*x^108 - 4
+1613159149582603037007821378021268206873842361757130321500129691291418451571
+4472352328413744363607247454062853765212695133675510221970937823367502714872
+5264869179155436190999803707918965482858909915679294544758294286168029187155
+42674704594105656661109974298786084926216*x^107 - 64968550520798695907403393
+0416095844971770756779773877908365468264830766792721054670459397639118998550
+4854765543268865886038920660387928909044011453689430024047009170672072811027
+5782636138143658008315348604258233043465414839888086317182504579245353574551
+48087384017967480*x^106 + 10988704303885892826315283975311210242722783884449
+6602821133827001873720461030281740372523131562861261636562127194114761297380
+7567084209038324237434507186912029498523360122835533024309739840677831689412
+3866277910335992520712968840312446267340837533424627065123680989778324*x^105
+ - 1037769081322395498728280928797930421673181636784651943232652987200370379
+9887100753154281880152957110034253066824316513009332837888828809575782813589
+8395431699875068691882278140209738486991188655032931569583339555950811469751
+14819216369493334147767991300084429910831005417*x^104 + 69810186616581919293
+4087787107108500858284600224749854536503416055380938630777674032345768445107
+9032740625501338477163577149719360331436837611189692791831261366092124431433
+6123841173727343108316374825964917374812406255193704152752318584125714566685
+03155908009579048097943*x^103 - 62618742936856051264116083169826991776205278
+5386210430790988069604384137878194361389699073225403863192706786752381073990
+4629809810678656127043145873535279223707645117591581849318483810783264428888
+878968101286898381392510682070079334990623735577742545874102235967038827767*
+x^102 + 88547713577136747022744956480711506392268841458610902634308115200722
+8001393811863713127092848888453031249274119846056297769374759830206187529701
+1903856417069237945552069729259203293736154678785007420066778201855082685563
+641521979888163226986534911148352808279115809917358*x^101 - 4630158507380629
+2388093315791533391137484806668109034703107450628858742143410102799736933423
+6907074978067722826731138207487756769203481925052544349731537665931451375605
+6491082785764360244007240454153410730447743408991269401214809728107755411693
+172395012634341107448006498*x^100 - 1987106430336286502255096633621975778269
+2749388116010785359867801694785712027822174277716791024889732874123796999017
+1165326722657523135611227287116367063127157679861619224054599116072941563451
+0018552431779605085569288059085092991937490866241369045567079522410549051673
+8430*x^99 + 6116111486479868423461963851524196927490094772654137109281820240
+9655601335862868609893744275312622546677599324483201519748792880440223783865
+4698142956751915407410209991596924924552268184253687896341372281276522326101
+46924043615114529192462392320461821295874662972625510705*x^98 - 858543407202
+8582480876325305001979394403680268817452625497746550339718414937826890236809
+6758090513332370728027354524452052318746523877507900393006790670253886201007
+7151654142173689494119764321155798594114038476768325934248961469594904974057
+82950778627016658691250674749383*x^97 + 490657445429729269017920968754688301
+6620517611911217595219014813015157155188541123765870476789021829481317185682
+2346556428383215072814614042291152340886085128841976463982340477579191182175
+5499560251631802981685749888173758813318700520169103214089237413133223676806
+81136483*x^96 + 588480356039113348615320015128938548650997950279831325677040
+1070851758771375890193257175605171144175873327958813862484089151772708967824
+5090710382325088735952719657758536202632425695397040762304230638936597396706
+141037241892685559313526162149365318736429347633086308123732*x^95 - 17860801
+0792691455581088588394658008616948993017279794644452692311940825238452683644
+2319855428360563553746309477403369295008059059368147822762461583481089204503
+0764049052795948146830428753094428042821800841448746792034840653376273350188
+2132907610936220993092559868115464848*x^94 + 2080698898949934438755286742414
+6744709766506973468237223938818775848985645787102709849630670421667929387077
+1839075966340916755041077369098858794090822988106295163721588178394959186872
+9508222800790599280424915420728365948473247641413622178547384599468880111768
+92625701864148*x^93 - 855864218009308575944636024731111359775112255204531345
+0227262959123310950664276060293965525931786616911167403341188615339530286675
+6775269831884121745448799689118137607558616438099756213430487533482486835346
+075682903923880766231253716972279618549611306311532332784130069552*x^92 - 13
+6808840532983525733371616856915983783270410509211401019532102689258560876086
+9527424128221769318567475482882108641281228508725452571176264919548871309015
+8567261995935441375507973903437038954795573527871968541716126334097932463611
+5855437833626026464694567238228672113384692*x^91 + 3083618181674886298656898
+1166117178805224503007958449196073139881830099761716540415100166992970870487
+3373126707079788137630463247890473378063608477167239399012803238136948080412
+0312330266139820196748423629961267609185986608819581540758534872341667324689
+62725982670350394704*x^90 - 289813403115309877208771459417444034084104076525
+9424539591515173961928303088393009380304940043691096832523560373895822112785
+5808066054703063840814273369617911574459101343616657558236169574623914019563
+0152780337575962071156042744164046198739767637262303611768624103749962308*x^
+89 + 72613516630962625965473064217525199676649443527570426575481770740043888
+6779646591936295747831851104939101651758342323554432645301127945581635380942
+7667424563537772502070799105048465507245059623491244896462413185145901940314
+5696053808385861551596378474452839548918080875392*x^88 + 2007137579167957439
+0719911721927558340943820520648172319420534796396724837614574044411853430783
+6722420021463109835306685189533078441109841798470591234934768692554906817588
+1207883501035322616933512472492706367178783380343756988188031103657790713917
+44233368892636267232901060*x^87 - 341583527690850438763926482860330447741875
+4305316360798807536901393938873869395520682617524619723932218997197449552997
+7902796418727196784273654618816384850023400797434252298928612530838816162641
+9574193744111685493591811397803624718762999077541037470623557200013418602943
+344*x^86 + 26018106816532769824515089189721783374074645569591071162155828403
+0232272450337410678138897855399057733160129008028400975157648521806369052132
+6598794386401757044711814737218799723053160173284842744313272283592926991658
+43454191912138368855662104946473338105764358284022377572*x^85 - 285687497537
+0233084322274278589877961032877493803575239781590446839383349838850110790540
+3648812210524131836405185194224569727115488819662642038492004860753207681555
+8064901322408777764393214575140844637199884061193474482510361153824419358509
+00211274244171508240172003554579*x^84 - 185573110599032244389576804101570527
+0843481327670529736455724859117579642729259313496498283495085435901054470028
+2535196961772216663035884293412871166129440632779930980074617184610893507366
+8787347778013694399060652311121162557391893749324368007933315027120013043270
+445299679*x^83 + 25050117171467230890654410834087578617559538489554781281579
+3157881185513898265592143907721703598168076058572348175071967137401886379765
+0888321554412070999395502650926168357968511833244292802087783872357923454661
+96695362647033860816591741894381954763538781291764410558313991*x^82 - 156624
+0805166486574430551541640026858587070051407347684313586931160679067646271535
+7829110559493831623647655992465253608722147473274284815377360979913488645232
+4593004096825642014547164807256580851492214151707379016954878586353260655456
+252800749153240368998676635038282783458*x^81 - 11012223211385811697215073324
+1921762613953353874156837170764058477228457541142548719349032469796569578235
+3672680465156231420291792630380279912309212851197963260878119761617542365940
+8318430631980819407521411635240395800739279192347581702754335107271855384807
+48046492479002*x^80 + 111235651556599161905998900669481587907856029607842384
+7058721145611490097096177499329308286485980098377201553695581299300616429252
+2896328548114223502627261314995818846022537138353738293647104367494387573394
+8393580977308270455034395646105894483336815886915572733813700662098*x^79 - 1
+2379020348161516025309044646747619580648761934003227421750700116671436042176
+9234916272072233533611217288217765756191391217561297135624913793687875324800
+7048123566476086580476470670309837456621193106958657582668447487991539286833
+57031134988178799550492266664939630915472321*x^78 + 644986875872857655262996
+2870357260072805613051355925273289691398310869281737114462536081840128975833
+0126180055099077158183624456453836279081213829170321954905360807167962624432
+0834840220201004905145194463702511094693925819892194681047038159942946067231
+75356042055357536463*x^77 + 632358398592670498095591997355631111077729590643
+2790651615164768723582162467454648690012209970035792209695666782959269273807
+2446931808206460612526556054521554052793022216016750971388084812894581490191
+87488153428597505906544272968607176655503312813518126744417345713254937*x^76
+ - 4407859464775144416487947396167367671725803977033500362272840558411894622
+4142347820758760948628866949429655387790156661766965605123758771588193309114
+1713374027265271324050920162934924792826148476476160753572716996003978356956
+83804000368513801138734053137773265487620770740*x^75 + 416372459606780296337
+1450264046151575655550849081038974416037216542290125884466463363808042083850
+9725418382508403123824265992064946980309513636360132567064198384175231861989
+9012223863450049443691606628550828159458118620199310401445249248178059313891
+70550354597821348040080*x^74 - 184161864081127040151143217561783123125395859
+3984003709177376821791781406937773897678496561663900933927169138471529228790
+5120635576312602740572720848588646957621039854850944227105781066695720878550
+615756344477554232044466772542846688611602947158650463473157163999798684880*
+x^73 - 294578669269207164351858864041819495442047310529305439637416055593898
+7162766090787137613214756965908109965354836546249973970248312691190106013609
+0096047571535265598414402965513205463264379299412109566296535132357163168956
+32417972107859793797811490919775354115913639814509*x^72 + 116883470301786694
+7940105919928790584076295711500240939987022716541425069544597244574520104343
+0843831048650119597248348399831622778255382707463446772861932339155184508820
+8691492120804327123688321050527154392082194558601074437356409645501206599517
+48548591269958013706855735*x^71 - 962337617562843751668517056379887654984035
+3607775335999245012386540197534720757701144759975007037123456361469330484819
+1315697282222826434358396253666199318705803758613569124925030815057449171128
+4560115497697369485081240160416963318875432658639303154335108578568393415001
+1*x^70 + 3726752627853214173014791913150187098796942009506244634835348186874
+6218248319652489347706949219918369593434279794434225414007742000717921598981
+1967637764507611718683775885631603729498673872931482040372125187697058620137
+8729901384977948736920393075869570682078658147246402*x^69 + 6664265109721472
+5027167403028506905386766457143598965411785702251914095207487701546804662501
+9973661173639310250173508615816977744154467727637322113813424769329746934178
+2443037607433520297691194068563933234561377974297855282622664908800977935892
+66781529161410379918516384*x^68 - 207155588658254864820236483929333843353850
+2314782709935428769719703150188593756759875477634107095955930003335004909963
+7806096997987389425073233030518177133541278388985086967790086225554199073757
+9183726492903476789901947898423219398782945711055270770359481994540571424130
+4*x^67 + 1533378023324702195795907021652744267401738630185227412509325644190
+9317029060634416164456507736338956677716876820335111092883359676091733924360
+7562229802747370137957394804745995747839571913841959663322372004062970960303
+2739266325572053308062722007360886556152476819257995*x^66 - 5448017961352466
+8954308761386777114515861425568699050923629567132295720176133619693154821070
+6679446412666455188970580511515741859257380970964727783399666659463150181605
+5832418778533170218818696428046565610812780388008951253152709903494726503303
+47825776795872045310257331*x^65 - 782824212320460720852470872605130544762708
+5270844370030560427574258798403117425649643063954946491985881365052474110155
+1061361746263419716606571746236356433610334478429319747526941053865615635278
+723792660268903548758991779230812111021711115289261014249648789576891768349*
+x^64 + 241834633150739573689757316859489557852802058328995612290604847372968
+8962517428202844726500527178649942306770989823832330134459212595922531552449
+0113231117621334324551438466322046602200466264537273693298034252254721421676
+4952766269941267448422272698762518675998018630028*x^63 - 1666339352730570520
+3639407453322817405171376787484109204948738088424073980877086807482951938601
+8865103399869046069476520137316208493195695957902129110473449041403464176591
+9862359175005557769808809061623387720644388156956972127266850675533756707525
+90808120744985654078946*x^62 + 570449220804489887130193887685456156518623172
+5938262332193484731157547462002602737462832660959996983966122744124822282431
+6513410409807491982186685917971756931992668327222699312497837591559016609364
+497532211515388699725043424503916154248093353508075820460159560673674726*x^6
+1 + 379845061301619031112865499569621029871812941926085546779444789597593099
+2185392685054292994780700584857914762885227739218300244363323904098764703684
+3361325604313463057739669057343485520202139735014463468397975802705055082320
+02067235766810391945048746973605012517899250*x^60 - 179994905002158006799826
+1581248012266767598208187591516407746996243019260988129520947344375845837479
+4375473622136858584586366278763492407789131148115802682216654123834671962143
+5349323536462397163996997039950567353507544138641982512459740031444726096910
+94329488186488560*x^59 + 119982265165997044452636805135365752758447406488957
+0849779495733478576958767846719905517427473004995721810584114866176087023280
+6873265056291159390228424704274613300367658551594974395489222878733856100659
+323368722107547660243619913236809974787583082322603729616639857984*x^58 - 41
+4498343303395026162273181308147077705273369693379285169454980456825999696414
+1108191789225342382926105642661506635099783996258510012027273821755928691169
+8167108483458843302305307979514403727827680023751217095370719902334212472094
+52432417836264144119102122277397817768*x^57 + 151296257711150774319086699562
+0621904293538949888063494638380250947255821384926730801679647950967103397199
+8232664074317796747475932212744662087954481765782241246318995396862540981468
+8990486802544852254612284845621149784604770996714678404380352987098024383778
+212981782*x^56 + 77172024931648750131579375330854241317819193485030367789630
+0194979153932088946756095579709069441269038355362032598061536345375371499825
+7603589364289068081989344439616890875432673156016666549955986167393531518011
+67940120441305635456765616769010583292909919688853748166*x^55 - 533362214813
+2692571383637474154386631336646003027379100089536309803633081806433335487865
+3788348099478071550518684140405592181062300213393211342562869431261195244746
+4055597603055279266341016276936298547770626014341819185195774706572228813839
+188337921892972222814214310*x^54 + 19809672028428641640899800622402763302466
+1074260655961612294389483118237887223011979572958763434751014838641676318160
+5147310285574152096915663041869066749730025370705253261451722595574825909044
+34743256388662636089294615411424475808328353443007431056186153291193660236*x
+^53 - 3298236730725312112107997188999732021783635308764336412988146411506139
+7104102160872908196077077455976298299221998872649597890835291998156291479044
+1156752915907868097785637632834657415730880385603530195196698224915641474164
+13441042710366926553820042101416886262527990*x^52 - 101449365782699643760207
+3313704763252338242187697491537141888115893684195480412346735267432884547401
+4326674844047109008343871306484926351769633249049418818884494773111342516465
+1364847880469035878140408781793697844423605606532175548895998431434178111893
+10391299541374*x^51 + 997010027853800903810835659831173696396465410573075578
+3905356009776229418935908969084901777161285772610734056245264176199983282362
+9243150822898802087782083582569387718010137408195023053062329504663705594403
+79207927321560114457596629472924327212630999737789491694628*x^50 - 400136364
+1205954524277253466159627211505461211798229164611000835355077955105452940106
+5043117114065854192422518937652264386811681003511656062957851230278085993229
+0192913573475644905186402717704749806127538925050731511982133457968662243173
+0574757902251581738927743514*x^49 + 9180213565693093847510479570029388499565
+5962139940688297118935762169143151779122206282849955988804045093690808202777
+1911618936765072016334219087961215540705432215522135777876375064000492729289
+146413824719753030999530625757538495495631483556564886408485696916274754*x^4
+8 - 501301210215460642688466550524464459820491039666763299722186435065388308
+0998476722426260605286629208716991084876566352805637002187528368901275570021
+1644840502467709341605696416994819511376212841122712589602930316113642419950
+349056502832731819476610421210838732740*x^47 - 54944298141158889585149685718
+7336779616988953202758365462768772698687661348560047998087075022182507327394
+7519184487938281971887003668567460606612213622645012761958365390879314022191
+3390146402903001771434654731924912308476740943900732699364805260630537589779
+248726*x^46 + 26686242659774581832060629132908634079744101584587342617793147
+5307484074819549903437909197788777407246096377950996274109900334593209284669
+9285933458439326397108293597155087876694426014357663784876151672816297841341
+0567270026180297395204928647366053260719567023642*x^45 - 7052486107172135206
+1948720744051909520509772591994862049253916379923010961566356969081325001527
+6292596993146766741009335610467932996827331500592308883886627814990767499859
+2394117000761761120597925240350755933951486120638079133147944601048042610283
+120648681977182*x^44 + 12094887988138431717848391295686703675228636456392942
+8682928139939229340261144090919567211768947492261796729623549396284079133495
+0340419054279131563933305486551030416968393033147914495046708454093512499973
+890279938140031139050906270219603864516933692949758319412*x^43 - 13249348861
+2052453833911384927269989662957662727807923412036942065407705141769113172260
+4562201840504522929158651370739513572711063413085138434059101868053588204820
+3501768317607444088231087593813985100365809682458092656030031376932623184206
+7770517384807361785676*x^42 + 1335101451287203762576138569495941477971504274
+6021839256417498499034598980956043459656243655749784623012085862698394648682
+7521666667680743107938078657228707393173302010891133955653957891206671708545
+21488797288438881522832497235465398261203063264934026405622216*x^41 - 499475
+9281308667412284807719782367452360187792248198012964464639579785518460647882
+3014472001994317756006942461078978073213429085061547197216262635883812900592
+1762751571689421659752686372945957018076639346750248956405331139984740983345
+9224825604519148808410998*x^40 + 2123055922106609642987166390747900882690095
+8265815080171818541132693855937048009649851884749195950160943998565872859377
+2466719457136622623105718221481729397353928547971338648745934933818733937555
+9722029289477935787063071140468460122494931142480075599166922066*x^39 - 5735
+6058097105987281579532842451121455107789722350655401468909089438850594255611
+7398406496524366947519084716044767896958134185399473668974877433779111152344
+5537408221196487932870899207582741017935334684933397499648658504512170298450
+81211351675478818348250014*x^38 + 101323645478522769412770862955451494838700
+8662851023057079588279613730397359405727823936580260643332082414004528447161
+9032650746240307864113277781931187876839372237162972094621638339957461376513
+5960868446031017264818726765755631590746592742117108410287162304*x^37 - 1158
+2691707749694320983466811601420819483438159197492729367460974781919497846083
+2310429882055944418973561703899132956324878554694827218593251972032733158957
+9073374625050919799091874768764116278381338067763168272226788014070435497357
+3342854568112725548119716*x^36 + 8305639013155526915236550603371532656184411
+8485479725403465416008780582431421160189594698593377625798613038172701584227
+4206678281269713323953843252700015484543752538426082787722637328521354532797
+854400197712614233087564302182567125978130820395479367756272*x^35 - 72553146
+6299857240224175450027827480387792541339702494044488085609115659954701904593
+4063182032757400157986647557124710638076669642745888083114891044280939887931
+1080380921785649390927188469314188121124356596001115383235298455028416330896
+607528890361908254*x^34 + 19860798394812096491425064118869331882474934679022
+5013755600349219086403728983462588925205132120800514991648137327905331386108
+2705026046100093497647310070721575578185071569257102022328094661667732591461
+4871110127674403701118049341045392289086401193314942*x^33 - 4504073630957286
+8996977652497679946908816176921415580423759147328165686549644634042466661073
+3511461352309328969240076472657648713663010059789108519959246932375899814162
+7806075361906437084097330297847347043691634727928126429902493759079959944869
+550465866*x^32 + 69070920659658480551690757737221770374761684950145185121047
+2484618737300993281417864275556465857656908421568917719365815679023157411215
+6057258090256560697376971566576951036121500346489928053597706896717897892051
+06698965797835172312757476105925970954316*x^31 - 950505499088700807538705994
+4852731534709761992249213459182019380991510107647264892021396558868144681611
+0662751219780692605034863184825513108322135455467480199290708985357993844600
+052787576017748490330946803622885911462082828385374632295839177766961104*x^3
+0 + 146871001034451764467214379577713350818960453105454365246878113141243396
+2615951663537883759634270986980824512323040156750516514483187596408739659469
+9598984366473798855873087478723052705650039925818705324797901096311128210246
+360020474709693712139627212*x^29 - 21967157682673292571077015450821069674087
+1544590587052875727692154504672915643984006806321437086319058849365052767208
+2180148377519601579902888730828514202452285184145954435822512134773040530752
+315307032862652438081839581459737476241460047819927741316*x^28 + 25868119256
+5279242165718547565481040416951897690007278140339301372610232089805802058986
+0750808024571604981879223368087235334407689838584104951724530561739372122119
+8951117323168039502216475583876368959953874029365022369810172602125325497095
+6445408416*x^27 - 2428059268010321918244811964086129262156487932219116205050
+4070649327791508268466838408282079070232219613570494028527410574628542390142
+2207371899314254390315983442044698383945383757774385604939274532392556556337
+58840387565176697754574091764000708652*x^26 + 228044964584692160131546765047
+2366230486843363280069375103638842382972106217168205477299195570432297721283
+4433913421328769323459813505283451769698919188569838637736110033080505201432
+29246149018773191614239310593408598408683107352357568527200654044*x^25 - 240
+6358660663137122033270184955011680904324977408022614274765749948013776439046
+4906763369553397919412224096925386397722165397929814334230617641557808733911
+0574280678118160005286224756835902809740658092872319513670690482675603106255
+535345368222072*x^24 + 17662623687632442866313848276740533318473562530568602
+6085380981148726634500393960831149553262597329458902932994016704916196167706
+1802977969263820447192793617562366039417910781021352992596284460784056551089
+2584667589514772071439777359014056460388*x^23 - 1017233748361584303945573233
+3032677102808866261456706104325822118295706056417248083910297875201722536975
+2472365095568346621934137039403008509886158851525905826470428214804919613199
+4864749556380390451533567703437432891192535285335888731490497100*x^22 + 6962
+4456632649901142038343508876142609309013148113719161965752973745996685396900
+6242981394546059022035086880100336674301708003625923297490632324135942177919
+6475754925837957964115788320422751182797666627301620661963625816748809270833
+0371586416*x^21 - 5935499297476735425457730389595586123363077244239039067258
+8757027689826646450579634779345685940054648366379750425264531723684895242726
+1296842788479967443307602969542957303586075752713784702233094467073009888400
+4709831827016377193151480549079*x^20 - 9924231276887632055264945002835855931
+6153619298064184888082607712523327637455849379369518935749807342918090562216
+3515240369483362753072379694694969114717331800146736581921176222284191504989
+22256734920743781356158658426818610320388361445943*x^19 - 223034910041976985
+5872625998444601823522405237422212480311276936526222907234872314934718146257
+7999956125097315186127418244514895084164378747477418159356551630505131305135
+965903638935951781824035858082445936492127698502477328282539866049529*x^18 -
+ 236643075076678358536974434180237346813030211220576398492261042505158468477
+4245106684856714329733460194522211899691204030539339797067532556065266564996
+8995125799175143049170891303055998502994776437035748429866958228412329271509
+6914727786*x^17 + 2542147127057127485965235153724151047113766830500853009593
+4057432271073053439213859505464662101818485064985498169276132525055092922810
+7659183430955698520972071635491614118402866501132808889569521209673251362824
+76709756655755991836417802*x^16 + 183111149787713493280210397619912322103096
+5247871179773154030195420556994309371058325110985700072545919333159157805938
+6224014037352998052003315867311050614119923950889255536852768279112155091364
+24174340648729576596630358137790527045198*x^15 - 449821041069912943789836264
+5632599818971731559621159648780555694409049475593695526598586098851324811318
+2519341945707916157927951713037252573987087918917682037480599453640794209186
+225479459166338434922942612532823032751347335799704869*x^14 - 22982911151946
+0414712287398340054262339910173840391365052883789755652220150460120847132485
+5387525455121237089427182900348837425717452397567606502432587339063493335218
+99975599619910693111772391894198334123224235975702158966025975889*x^13 + 425
+1167449102860417605682557582269062328643891986101063790164447568501066388281
+7083671619051397999677516644732430950231634364346445730340715448369087696718
+20523719962939869299988336933823914482320626610429276999595557594057384713*x
+^12 - 2634769415254884935578685134629261396873246071233388299830258283684407
+2876185708249044979665212650744995875688959242932243838420517823860588878429
+2448997291893491640954677331782979368685632076417137547307030222199889038379
+76940*x^11 - 178196131047104477494733878949091986680056821951080835036094383
+8004052330411696017910506157765585633940095861000688753523141138483928490467
+4511256960034750139170510478764583267207761340591670181326537548133703111264
+9106926204*x^10 + 1825000405670995013845885918902187142186986666482440933839
+6462521386957836588377301330171147216240982523247505481312286617417272521511
+8638399348219471080155689525455946137646839420547370091703312293991788729921
+1491191185252*x^9 - 65201626996640512322761674559318066561117950070675294766
+3169824762769899024352909587195016688965354038529484926750838814982572946010
+1979637110489360349176312329919783316622660007379629280095704481299477431710
+45016069129*x^8 - 1437891149083999662420144342263678250208894256538560561654
+8933951961391248431401653150798776891480895285823868174684751112400557598552
+7985918610201118598268431458892734242196304467884366814923258790382419891488
+25558929*x^7 - 1428280930363758251384969101052572662110879466805595758704451
+0527911033933582902662199662263051097627055254401259144549052189981714948608
+3243967370749726951525986876271444939969618263711803265644040382245363794511
+71*x^6 - 1170826372197459055177820600881979710066693446685651498813175990981
+0745762172031150515086932467894419392120220600061432329279936873889655682224
+7191725072125442393957901078704198243471395495180025340969870794174*x^5 - 11
+8223168675776843350634299306552668061864957826207103017499838634433318705119
+3832689526795798631026606743575718322278065328528551578753575871762638217620
+241416850427234233281589721441720700844787815627946*x^4 - 953048457007008296
+1795451881320794184817199918350969024576327248906812630041977334151670173523
+3419849726478663631547211301749427543946011643243124123502481796513891248511
+161398944690550172434214114*x^3 - 254706791524256177472181318251041912950779
+4390935955929617768730635253624759862293118182940797954335902313821207678848
+027543595348285081414397619295455292216447729433614628265514361363667915*x^2
+ + 7114590568532928191072122754665584122789751912529693393245247725975835948
+7615527828928204048113*x - 1
+x^240 + (-15*y - 36)*x^239 + (499*y + 430)*x^238 + (-6344*y - 983)*x^237 + (
+35389*y - 20411)*x^236 + (-40904*y + 178707)*x^235 + (-424808*y - 438197)*x^
+234 + (1415485*y - 635208)*x^233 + (1438139*y + 2974605)*x^232 + (-5864817*y
+ + 6566480)*x^231 + (-25462020*y - 12961894)*x^230 + (19238389*y - 79956654)
+*x^229 + (247313433*y - 30224897)*x^228 + (294927621*y + 801098945)*x^227 + 
+(-2353679384*y + 1069498319)*x^226 + (-3266832488*y - 5793469739)*x^225 + (1
+2761071756*y - 10152009124)*x^224 + (28891772141*y + 27706877778)*x^223 + (-
+56780498004*y + 70327470888)*x^222 + (-156989267296*y - 100643285180)*x^221 
++ (163182970660*y - 341901790078)*x^220 + (692925090103*y + 282970748175)*x^
+219 + (-520851483678*y + 1249500770581)*x^218 + (-2100987167651*y - 97780636
+3155)*x^217 + (2144907137575*y - 3501192827999)*x^216 + (5741131351957*y + 5
+368774573391)*x^215 + (-12949633488740*y + 9667592872104)*x^214 + (-18978818
+705192*y - 28988343801763)*x^213 + (61143701295650*y - 42854297857883)*x^212
+ + (99522339968066*y + 118416217970085)*x^211 + (-203990291010201*y + 226088
+796680855)*x^210 + (-487486417978576*y - 306059642167156)*x^209 + (371437987
+605861*y - 969565907346414)*x^208 + (1761877237395113*y + 250996844693464)*x
+^207 + (347587448455796*y + 2909203449118066)*x^206 + (-4262623171412271*y +
+ 1900082482875272)*x^205 + (-5112561845313059*y - 5258998337190094)*x^204 + 
+(4703077156935772*y - 10817923562329066)*x^203 + (19596724151621886*y + 4579
+71706716061)*x^202 + (10779811934500297*y + 31310302362591686)*x^201 + (-444
+71188376651271*y + 33091705261026148)*x^200 + (-70367728073948453*y - 553681
+43890230595)*x^199 + (58047935399898944*y - 125056052810735004)*x^198 + (196
+220179915190037*y + 46078797156864550)*x^197 + (-15612945533449050*y + 27845
+2806220656846)*x^196 + (-364966710997667886*y + 30164046307811567)*x^195 + (
+-76920393658091100*y - 455669527002677921)*x^194 + (569724473209147360*y - 1
+04273935104641941)*x^193 + (107554480539021407*y + 761016430421970356)*x^192
+ + (-1120756441763956820*y + 138805435659787599)*x^191 + (-36744368496876235
+9*y - 1741313178847717412)*x^190 + (2626931562224608194*y - 1142827140366519
+672)*x^189 + (3001549496003468616*y + 3542726921147289593)*x^188 + (-3816304
+655625513057*y + 6563685064488706687)*x^187 + (-12280102244383760786*y - 216
+9956083918876046)*x^186 + (-3280308295039992672*y - 20013773166146345265)*x^
+185 + (28534969871679714183*y - 14758677431499660732)*x^184 + (3420538527727
+7312731*y + 35129207175233160758)*x^183 + (-35562575528916824663*y + 6232129
+2808100809456)*x^182 + (-97526968113689340266*y - 24577168539715006795)*x^18
+1 + (-2924482708853224503*y - 135200656059538396115)*x^180 + (16755988046047
+3073606*y - 50231770182088582728)*x^179 + (117217186558944665294*y + 1844313
+16627662099968)*x^178 + (-175018700423712057769*y + 199325075624692888658)*x
+^177 + (-287672838056258651793*y - 130006789216819808906)*x^176 + (433136887
+58928043819*y - 370488386653834436323)*x^175 + (435685264104825475131*y - 87
+230951613648934063)*x^174 + (260789110179489506909*y + 473346146286012258862
+)*x^173 + (-477453708065915165092*y + 475828532275903297714)*x^172 + (-73312
+5023480253537968*y - 445824632107486741239)*x^171 + (377925441616168009977*y
+ - 1037951764481934863694)*x^170 + (1401192440853964064557*y + 2712041812199
+21501426)*x^169 + (-116817470274758714683*y + 1837817520966021102844)*x^168 
++ (-2362860273074857968126*y + 103875680474127044027)*x^167 + (-421763202246
+558569481*y - 2985437596461812106201)*x^166 + (3701454711020661872373*y - 87
+8419497758685431399)*x^165 + (1521006793930665203185*y + 4486769840934475059
+679)*x^164 + (-5292413186031800428648*y + 2393646591871321675898)*x^163 + (-
+3527560907713404811933*y - 6046342849737077241029)*x^162 + (6659475347960239
+461641*y - 4934471883467342725819)*x^161 + (6606218484397706362957*y + 70346
+33694928572084799)*x^160 + (-7071251229918632950519*y + 85221454139378375409
+88)*x^159 + (-10654172673396879304988*y - 6660938661086516468010)*x^158 + (5
+677741559220425296264*y - 12963319200223537566424)*x^157 + (1537868439480061
+5926642*y + 3975042669473795569897)*x^156 + (-1409491575820818236069*y + 177
+68498236629148702410)*x^155 + (-19928654122136236656860*y + 2108177954625221
+499359)*x^154 + (-6549200784773657451762*y - 21617243705895522615660)*x^153 
++ (22645379524677683996954*y - 11738628703873244299229)*x^152 + (17408053637
+884918322314*y + 22982504260819896781381)*x^151 + (-22809630522578701355929*
+y + 23340226072231874490562)*x^150 + (-29550558164952915082604*y - 224392369
+77615542871417)*x^149 + (22094525765620020809639*y - 36386527353264595233550
+)*x^148 + (44442209345887020103552*y + 21630493829993114594971)*x^147 + (-20
+362621509381561401708*y + 54254830555411448139989)*x^146 + (-659133869795653
+69752671*y - 17161940478911028975987)*x^145 + (10842803083053018739937*y - 7
+8798826823995559998884)*x^144 + (91671114659094334230114*y + 699010605156960
+463896)*x^143 + (13121977610624125288643*y + 103143317388027280663819)*x^142
+ + (-112336281183122778011108*y + 29666952173920616332098)*x^141 + (-4771339
+6391497944676658*y - 119352040791078382516354)*x^140 + (12522214489937288097
+8343*y - 66535424263344578690303)*x^139 + (86443391588395703449231*y + 13128
+8792905554584140170)*x^138 + (-138308631379146285689943*y + 1087117764446364
+01209940)*x^137 + (-134836856528001466894586*y - 145826834678543169365085)*x
+^136 + (152261003156819774060466*y - 165525545074570372890946)*x^135 + (2000
+19883617103704090631*y + 155735524429003454571137)*x^134 + (-155214006426713
+875482591*y + 236255888142978579163439)*x^133 + (-271843213937284128310312*y
+ - 151199785544397594300869)*x^132 + (145501652264843306972923*y - 305326875
+506321100258855)*x^131 + (336932852565943631529246*y + 140097781638111061499
+104)*x^130 + (-135773050794564515416176*y + 368242715265481945864410)*x^129 
++ (-400908655930869378612741*y - 131401561676926670933942)*x^128 + (12442649
+3872813948801626*y - 435145712814328666799539)*x^127 + (46899879143136708390
+7400*y + 112351662733835870233203)*x^126 + (-94381628626117531492779*y + 498
+937937395927788353831)*x^125 + (-521540559152841029573809*y - 72188367562634
+255595303)*x^124 + (49231480359271694594387*y - 535280543629519838165745)*x^
+123 + (541321200409785903521904*y + 28961946371400127296768)*x^122 + (-12936
+229773088822436824*y + 542775176498979507958864)*x^121 - 5428178716207228568
+54615*y*x^120 + (-12936229773088822436824*y - 542775176498979507958864)*x^11
+9 + (541321200409785903521904*y - 28961946371400127296768)*x^118 + (49231480
+359271694594387*y + 535280543629519838165745)*x^117 + (-52154055915284102957
+3809*y + 72188367562634255595303)*x^116 + (-94381628626117531492779*y - 4989
+37937395927788353831)*x^115 + (468998791431367083907400*y - 1123516627338358
+70233203)*x^114 + (124426493872813948801626*y + 435145712814328666799539)*x^
+113 + (-400908655930869378612741*y + 131401561676926670933942)*x^112 + (-135
+773050794564515416176*y - 368242715265481945864410)*x^111 + (336932852565943
+631529246*y - 140097781638111061499104)*x^110 + (145501652264843306972923*y 
++ 305326875506321100258855)*x^109 + (-271843213937284128310312*y + 151199785
+544397594300869)*x^108 + (-155214006426713875482591*y - 23625588814297857916
+3439)*x^107 + (200019883617103704090631*y - 155735524429003454571137)*x^106 
++ (152261003156819774060466*y + 165525545074570372890946)*x^105 + (-13483685
+6528001466894586*y + 145826834678543169365085)*x^104 + (-1383086313791462856
+89943*y - 108711776444636401209940)*x^103 + (86443391588395703449231*y - 131
+288792905554584140170)*x^102 + (125222144899372880978343*y + 665354242633445
+78690303)*x^101 + (-47713396391497944676658*y + 119352040791078382516354)*x^
+100 + (-112336281183122778011108*y - 29666952173920616332098)*x^99 + (131219
+77610624125288643*y - 103143317388027280663819)*x^98 + (91671114659094334230
+114*y - 699010605156960463896)*x^97 + (10842803083053018739937*y + 787988268
+23995559998884)*x^96 + (-65913386979565369752671*y + 17161940478911028975987
+)*x^95 + (-20362621509381561401708*y - 54254830555411448139989)*x^94 + (4444
+2209345887020103552*y - 21630493829993114594971)*x^93 + (2209452576562002080
+9639*y + 36386527353264595233550)*x^92 + (-29550558164952915082604*y + 22439
+236977615542871417)*x^91 + (-22809630522578701355929*y - 2334022607223187449
+0562)*x^90 + (17408053637884918322314*y - 22982504260819896781381)*x^89 + (2
+2645379524677683996954*y + 11738628703873244299229)*x^88 + (-654920078477365
+7451762*y + 21617243705895522615660)*x^87 + (-19928654122136236656860*y - 21
+08177954625221499359)*x^86 + (-1409491575820818236069*y - 177684982366291487
+02410)*x^85 + (15378684394800615926642*y - 3975042669473795569897)*x^84 + (5
+677741559220425296264*y + 12963319200223537566424)*x^83 + (-1065417267339687
+9304988*y + 6660938661086516468010)*x^82 + (-7071251229918632950519*y - 8522
+145413937837540988)*x^81 + (6606218484397706362957*y - 703463369492857208479
+9)*x^80 + (6659475347960239461641*y + 4934471883467342725819)*x^79 + (-35275
+60907713404811933*y + 6046342849737077241029)*x^78 + (-529241318603180042864
+8*y - 2393646591871321675898)*x^77 + (1521006793930665203185*y - 44867698409
+34475059679)*x^76 + (3701454711020661872373*y + 878419497758685431399)*x^75 
++ (-421763202246558569481*y + 2985437596461812106201)*x^74 + (-2362860273074
+857968126*y - 103875680474127044027)*x^73 + (-116817470274758714683*y - 1837
+817520966021102844)*x^72 + (1401192440853964064557*y - 271204181219921501426
+)*x^71 + (377925441616168009977*y + 1037951764481934863694)*x^70 + (-7331250
+23480253537968*y + 445824632107486741239)*x^69 + (-477453708065915165092*y -
+ 475828532275903297714)*x^68 + (260789110179489506909*y - 473346146286012258
+862)*x^67 + (435685264104825475131*y + 87230951613648934063)*x^66 + (4331368
+8758928043819*y + 370488386653834436323)*x^65 + (-287672838056258651793*y + 
+130006789216819808906)*x^64 + (-175018700423712057769*y - 199325075624692888
+658)*x^63 + (117217186558944665294*y - 184431316627662099968)*x^62 + (167559
+880460473073606*y + 50231770182088582728)*x^61 + (-2924482708853224503*y + 1
+35200656059538396115)*x^60 + (-97526968113689340266*y + 24577168539715006795
+)*x^59 + (-35562575528916824663*y - 62321292808100809456)*x^58 + (3420538527
+7277312731*y - 35129207175233160758)*x^57 + (28534969871679714183*y + 147586
+77431499660732)*x^56 + (-3280308295039992672*y + 20013773166146345265)*x^55 
++ (-12280102244383760786*y + 2169956083918876046)*x^54 + (-38163046556255130
+57*y - 6563685064488706687)*x^53 + (3001549496003468616*y - 3542726921147289
+593)*x^52 + (2626931562224608194*y + 1142827140366519672)*x^51 + (-367443684
+968762359*y + 1741313178847717412)*x^50 + (-1120756441763956820*y - 13880543
+5659787599)*x^49 + (107554480539021407*y - 761016430421970356)*x^48 + (56972
+4473209147360*y + 104273935104641941)*x^47 + (-76920393658091100*y + 4556695
+27002677921)*x^46 + (-364966710997667886*y - 30164046307811567)*x^45 + (-156
+12945533449050*y - 278452806220656846)*x^44 + (196220179915190037*y - 460787
+97156864550)*x^43 + (58047935399898944*y + 125056052810735004)*x^42 + (-7036
+7728073948453*y + 55368143890230595)*x^41 + (-44471188376651271*y - 33091705
+261026148)*x^40 + (10779811934500297*y - 31310302362591686)*x^39 + (19596724
+151621886*y - 457971706716061)*x^38 + (4703077156935772*y + 1081792356232906
+6)*x^37 + (-5112561845313059*y + 5258998337190094)*x^36 + (-4262623171412271
+*y - 1900082482875272)*x^35 + (347587448455796*y - 2909203449118066)*x^34 + 
+(1761877237395113*y - 250996844693464)*x^33 + (371437987605861*y + 969565907
+346414)*x^32 + (-487486417978576*y + 306059642167156)*x^31 + (-2039902910102
+01*y - 226088796680855)*x^30 + (99522339968066*y - 118416217970085)*x^29 + (
+61143701295650*y + 42854297857883)*x^28 + (-18978818705192*y + 2898834380176
+3)*x^27 + (-12949633488740*y - 9667592872104)*x^26 + (5741131351957*y - 5368
+774573391)*x^25 + (2144907137575*y + 3501192827999)*x^24 + (-2100987167651*y
+ + 977806363155)*x^23 + (-520851483678*y - 1249500770581)*x^22 + (6929250901
+03*y - 282970748175)*x^21 + (163182970660*y + 341901790078)*x^20 + (-1569892
+67296*y + 100643285180)*x^19 + (-56780498004*y - 70327470888)*x^18 + (288917
+72141*y - 27706877778)*x^17 + (12761071756*y + 10152009124)*x^16 + (-3266832
+488*y + 5793469739)*x^15 + (-2353679384*y - 1069498319)*x^14 + (294927621*y 
+- 801098945)*x^13 + (247313433*y + 30224897)*x^12 + (19238389*y + 79956654)*
+x^11 + (-25462020*y + 12961894)*x^10 + (-5864817*y - 6566480)*x^9 + (1438139
+*y - 2974605)*x^8 + (1415485*y + 635208)*x^7 + (-424808*y + 438197)*x^6 + (-
+40904*y - 178707)*x^5 + (35389*y + 20411)*x^4 + (-6344*y + 983)*x^3 + (499*y
+ - 430)*x^2 + (-15*y + 36)*x - 1
+x^3 + (-y - 1)*x^2 + (y - 2)*x + 1
+x^6 + (-y - 1)*x^5 + (y - 2)*x^4 + 5*x^3 + (-y - 1)*x^2 + (y - 2)*x + 1
+x^15 + (-13*y - 28)*x^14 + (-35*y + 71)*x^13 + (-96*y + 193)*x^12 + (-5*y - 
+196)*x^11 + (-239*y - 365)*x^10 + (120*y + 1489)*x^9 + (-261*y - 2084)*x^8 +
+ (-261*y + 2345)*x^7 + (120*y - 1609)*x^6 + (-239*y + 604)*x^5 + (-5*y + 201
+)*x^4 + (-96*y - 97)*x^3 + (-35*y - 36)*x^2 + (-13*y + 41)*x - 1
+x^64 - 2869316*y*x^63 - 14321320456*x^62 + 157634598028*y*x^61 - 20284120752
+9344*x^60 - 187341502817748*y*x^59 + 42513677421330536*x^58 + 89659277585427
+48*y*x^57 - 581758105735037800*x^56 - 22856296353162116*y*x^55 - 17452945591
+56518504*x^54 - 264489521649865972*y*x^53 + 3806496834205999360*x^52 - 74814
+8105793479444*y*x^51 + 34464709754552397448*x^50 - 807085220707689700*y*x^49
+ + 112558678351798157916*x^48 + 885683152130937644*y*x^47 + 3063853398816684
+17048*x^46 + 13203419547826420924*y*x^45 + 419863456051485069824*x^44 + 3434
+3465258453402972*y*x^43 + 465608689069031605064*x^42 + 80309592889200941068*
+y*x^41 - 360175722030581287640*x^40 + 110486596140091600940*y*x^39 - 1490512
+424563931678536*x^38 + 143957811491089032572*y*x^37 - 3739364774219046032256
+*x^36 + 100323919847689168220*y*x^35 - 4985975135633301110936*x^34 + 5481611
+3881156185292*y*x^33 - 6135008383051601995834*x^32 - 54816113881156185292*y*
+x^31 - 4985975135633301110936*x^30 - 100323919847689168220*y*x^29 - 37393647
+74219046032256*x^28 - 143957811491089032572*y*x^27 - 1490512424563931678536*
+x^26 - 110486596140091600940*y*x^25 - 360175722030581287640*x^24 - 803095928
+89200941068*y*x^23 + 465608689069031605064*x^22 - 34343465258453402972*y*x^2
+1 + 419863456051485069824*x^20 - 13203419547826420924*y*x^19 + 3063853398816
+68417048*x^18 - 885683152130937644*y*x^17 + 112558678351798157916*x^16 + 807
+085220707689700*y*x^15 + 34464709754552397448*x^14 + 748148105793479444*y*x^
+13 + 3806496834205999360*x^12 + 264489521649865972*y*x^11 - 1745294559156518
+504*x^10 + 22856296353162116*y*x^9 - 581758105735037800*x^8 - 89659277585427
+48*y*x^7 + 42513677421330536*x^6 + 187341502817748*y*x^5 - 202841207529344*x
+^4 - 157634598028*y*x^3 - 14321320456*x^2 + 2869316*y*x + 1
+x^21 + (-13*y - 229)*x^20 + (234*y - 100)*x^19 + (-480*y + 8370)*x^18 + (-17
+35*y - 16719)*x^17 + (4913*y - 7834)*x^16 + (-2688*y + 51064)*x^15 + (-3960*
+y - 34104)*x^14 + (1782*y - 32956)*x^13 + (8448*y + 5262)*x^12 + (-6380*y + 
+97204)*x^11 + (-6380*y - 90824)*x^10 + (8448*y - 13710)*x^9 + (1782*y + 3117
+4)*x^8 + (-3960*y + 38064)*x^7 + (-2688*y - 48376)*x^6 + (4913*y + 2921)*x^5
+ + (-1735*y + 18454)*x^4 + (-480*y - 7890)*x^3 + (234*y - 134)*x^2 + (-13*y 
++ 242)*x - 1
+x^6 - 2*y*x^5 + (-y - 7)*x^4 - 4*x^3 + (y - 8)*x^2 + (2*y - 2)*x + 1
+x^240 + (-15*y - 36)*x^239 + (499*y + 430)*x^238 + (-6344*y - 983)*x^237 + (
+35389*y - 20411)*x^236 + (-40904*y + 178707)*x^235 + (-424808*y - 438197)*x^
+234 + (1415485*y - 635208)*x^233 + (1438139*y + 2974605)*x^232 + (-5864817*y
+ + 6566480)*x^231 + (-25462020*y - 12961894)*x^230 + (19238389*y - 79956654)
+*x^229 + (247313433*y - 30224897)*x^228 + (294927621*y + 801098945)*x^227 + 
+(-2353679384*y + 1069498319)*x^226 + (-3266832488*y - 5793469739)*x^225 + (1
+2761071756*y - 10152009124)*x^224 + (28891772141*y + 27706877778)*x^223 + (-
+56780498004*y + 70327470888)*x^222 + (-156989267296*y - 100643285180)*x^221 
++ (163182970660*y - 341901790078)*x^220 + (692925090103*y + 282970748175)*x^
+219 + (-520851483678*y + 1249500770581)*x^218 + (-2100987167651*y - 97780636
+3155)*x^217 + (2144907137575*y - 3501192827999)*x^216 + (5741131351957*y + 5
+368774573391)*x^215 + (-12949633488740*y + 9667592872104)*x^214 + (-18978818
+705192*y - 28988343801763)*x^213 + (61143701295650*y - 42854297857883)*x^212
+ + (99522339968066*y + 118416217970085)*x^211 + (-203990291010201*y + 226088
+796680855)*x^210 + (-487486417978576*y - 306059642167156)*x^209 + (371437987
+605861*y - 969565907346414)*x^208 + (1761877237395113*y + 250996844693464)*x
+^207 + (347587448455796*y + 2909203449118066)*x^206 + (-4262623171412271*y +
+ 1900082482875272)*x^205 + (-5112561845313059*y - 5258998337190094)*x^204 + 
+(4703077156935772*y - 10817923562329066)*x^203 + (19596724151621886*y + 4579
+71706716061)*x^202 + (10779811934500297*y + 31310302362591686)*x^201 + (-444
+71188376651271*y + 33091705261026148)*x^200 + (-70367728073948453*y - 553681
+43890230595)*x^199 + (58047935399898944*y - 125056052810735004)*x^198 + (196
+220179915190037*y + 46078797156864550)*x^197 + (-15612945533449050*y + 27845
+2806220656846)*x^196 + (-364966710997667886*y + 30164046307811567)*x^195 + (
+-76920393658091100*y - 455669527002677921)*x^194 + (569724473209147360*y - 1
+04273935104641941)*x^193 + (107554480539021407*y + 761016430421970356)*x^192
+ + (-1120756441763956820*y + 138805435659787599)*x^191 + (-36744368496876235
+9*y - 1741313178847717412)*x^190 + (2626931562224608194*y - 1142827140366519
+672)*x^189 + (3001549496003468616*y + 3542726921147289593)*x^188 + (-3816304
+655625513057*y + 6563685064488706687)*x^187 + (-12280102244383760786*y - 216
+9956083918876046)*x^186 + (-3280308295039992672*y - 20013773166146345265)*x^
+185 + (28534969871679714183*y - 14758677431499660732)*x^184 + (3420538527727
+7312731*y + 35129207175233160758)*x^183 + (-35562575528916824663*y + 6232129
+2808100809456)*x^182 + (-97526968113689340266*y - 24577168539715006795)*x^18
+1 + (-2924482708853224503*y - 135200656059538396115)*x^180 + (16755988046047
+3073606*y - 50231770182088582728)*x^179 + (117217186558944665294*y + 1844313
+16627662099968)*x^178 + (-175018700423712057769*y + 199325075624692888658)*x
+^177 + (-287672838056258651793*y - 130006789216819808906)*x^176 + (433136887
+58928043819*y - 370488386653834436323)*x^175 + (435685264104825475131*y - 87
+230951613648934063)*x^174 + (260789110179489506909*y + 473346146286012258862
+)*x^173 + (-477453708065915165092*y + 475828532275903297714)*x^172 + (-73312
+5023480253537968*y - 445824632107486741239)*x^171 + (377925441616168009977*y
+ - 1037951764481934863694)*x^170 + (1401192440853964064557*y + 2712041812199
+21501426)*x^169 + (-116817470274758714683*y + 1837817520966021102844)*x^168 
++ (-2362860273074857968126*y + 103875680474127044027)*x^167 + (-421763202246
+558569481*y - 2985437596461812106201)*x^166 + (3701454711020661872373*y - 87
+8419497758685431399)*x^165 + (1521006793930665203185*y + 4486769840934475059
+679)*x^164 + (-5292413186031800428648*y + 2393646591871321675898)*x^163 + (-
+3527560907713404811933*y - 6046342849737077241029)*x^162 + (6659475347960239
+461641*y - 4934471883467342725819)*x^161 + (6606218484397706362957*y + 70346
+33694928572084799)*x^160 + (-7071251229918632950519*y + 85221454139378375409
+88)*x^159 + (-10654172673396879304988*y - 6660938661086516468010)*x^158 + (5
+677741559220425296264*y - 12963319200223537566424)*x^157 + (1537868439480061
+5926642*y + 3975042669473795569897)*x^156 + (-1409491575820818236069*y + 177
+68498236629148702410)*x^155 + (-19928654122136236656860*y + 2108177954625221
+499359)*x^154 + (-6549200784773657451762*y - 21617243705895522615660)*x^153 
++ (22645379524677683996954*y - 11738628703873244299229)*x^152 + (17408053637
+884918322314*y + 22982504260819896781381)*x^151 + (-22809630522578701355929*
+y + 23340226072231874490562)*x^150 + (-29550558164952915082604*y - 224392369
+77615542871417)*x^149 + (22094525765620020809639*y - 36386527353264595233550
+)*x^148 + (44442209345887020103552*y + 21630493829993114594971)*x^147 + (-20
+362621509381561401708*y + 54254830555411448139989)*x^146 + (-659133869795653
+69752671*y - 17161940478911028975987)*x^145 + (10842803083053018739937*y - 7
+8798826823995559998884)*x^144 + (91671114659094334230114*y + 699010605156960
+463896)*x^143 + (13121977610624125288643*y + 103143317388027280663819)*x^142
+ + (-112336281183122778011108*y + 29666952173920616332098)*x^141 + (-4771339
+6391497944676658*y - 119352040791078382516354)*x^140 + (12522214489937288097
+8343*y - 66535424263344578690303)*x^139 + (86443391588395703449231*y + 13128
+8792905554584140170)*x^138 + (-138308631379146285689943*y + 1087117764446364
+01209940)*x^137 + (-134836856528001466894586*y - 145826834678543169365085)*x
+^136 + (152261003156819774060466*y - 165525545074570372890946)*x^135 + (2000
+19883617103704090631*y + 155735524429003454571137)*x^134 + (-155214006426713
+875482591*y + 236255888142978579163439)*x^133 + (-271843213937284128310312*y
+ - 151199785544397594300869)*x^132 + (145501652264843306972923*y - 305326875
+506321100258855)*x^131 + (336932852565943631529246*y + 140097781638111061499
+104)*x^130 + (-135773050794564515416176*y + 368242715265481945864410)*x^129 
++ (-400908655930869378612741*y - 131401561676926670933942)*x^128 + (12442649
+3872813948801626*y - 435145712814328666799539)*x^127 + (46899879143136708390
+7400*y + 112351662733835870233203)*x^126 + (-94381628626117531492779*y + 498
+937937395927788353831)*x^125 + (-521540559152841029573809*y - 72188367562634
+255595303)*x^124 + (49231480359271694594387*y - 535280543629519838165745)*x^
+123 + (541321200409785903521904*y + 28961946371400127296768)*x^122 + (-12936
+229773088822436824*y + 542775176498979507958864)*x^121 - 5428178716207228568
+54615*y*x^120 + (-12936229773088822436824*y - 542775176498979507958864)*x^11
+9 + (541321200409785903521904*y - 28961946371400127296768)*x^118 + (49231480
+359271694594387*y + 535280543629519838165745)*x^117 + (-52154055915284102957
+3809*y + 72188367562634255595303)*x^116 + (-94381628626117531492779*y - 4989
+37937395927788353831)*x^115 + (468998791431367083907400*y - 1123516627338358
+70233203)*x^114 + (124426493872813948801626*y + 435145712814328666799539)*x^
+113 + (-400908655930869378612741*y + 131401561676926670933942)*x^112 + (-135
+773050794564515416176*y - 368242715265481945864410)*x^111 + (336932852565943
+631529246*y - 140097781638111061499104)*x^110 + (145501652264843306972923*y 
++ 305326875506321100258855)*x^109 + (-271843213937284128310312*y + 151199785
+544397594300869)*x^108 + (-155214006426713875482591*y - 23625588814297857916
+3439)*x^107 + (200019883617103704090631*y - 155735524429003454571137)*x^106 
++ (152261003156819774060466*y + 165525545074570372890946)*x^105 + (-13483685
+6528001466894586*y + 145826834678543169365085)*x^104 + (-1383086313791462856
+89943*y - 108711776444636401209940)*x^103 + (86443391588395703449231*y - 131
+288792905554584140170)*x^102 + (125222144899372880978343*y + 665354242633445
+78690303)*x^101 + (-47713396391497944676658*y + 119352040791078382516354)*x^
+100 + (-112336281183122778011108*y - 29666952173920616332098)*x^99 + (131219
+77610624125288643*y - 103143317388027280663819)*x^98 + (91671114659094334230
+114*y - 699010605156960463896)*x^97 + (10842803083053018739937*y + 787988268
+23995559998884)*x^96 + (-65913386979565369752671*y + 17161940478911028975987
+)*x^95 + (-20362621509381561401708*y - 54254830555411448139989)*x^94 + (4444
+2209345887020103552*y - 21630493829993114594971)*x^93 + (2209452576562002080
+9639*y + 36386527353264595233550)*x^92 + (-29550558164952915082604*y + 22439
+236977615542871417)*x^91 + (-22809630522578701355929*y - 2334022607223187449
+0562)*x^90 + (17408053637884918322314*y - 22982504260819896781381)*x^89 + (2
+2645379524677683996954*y + 11738628703873244299229)*x^88 + (-654920078477365
+7451762*y + 21617243705895522615660)*x^87 + (-19928654122136236656860*y - 21
+08177954625221499359)*x^86 + (-1409491575820818236069*y - 177684982366291487
+02410)*x^85 + (15378684394800615926642*y - 3975042669473795569897)*x^84 + (5
+677741559220425296264*y + 12963319200223537566424)*x^83 + (-1065417267339687
+9304988*y + 6660938661086516468010)*x^82 + (-7071251229918632950519*y - 8522
+145413937837540988)*x^81 + (6606218484397706362957*y - 703463369492857208479
+9)*x^80 + (6659475347960239461641*y + 4934471883467342725819)*x^79 + (-35275
+60907713404811933*y + 6046342849737077241029)*x^78 + (-529241318603180042864
+8*y - 2393646591871321675898)*x^77 + (1521006793930665203185*y - 44867698409
+34475059679)*x^76 + (3701454711020661872373*y + 878419497758685431399)*x^75 
++ (-421763202246558569481*y + 2985437596461812106201)*x^74 + (-2362860273074
+857968126*y - 103875680474127044027)*x^73 + (-116817470274758714683*y - 1837
+817520966021102844)*x^72 + (1401192440853964064557*y - 271204181219921501426
+)*x^71 + (377925441616168009977*y + 1037951764481934863694)*x^70 + (-7331250
+23480253537968*y + 445824632107486741239)*x^69 + (-477453708065915165092*y -
+ 475828532275903297714)*x^68 + (260789110179489506909*y - 473346146286012258
+862)*x^67 + (435685264104825475131*y + 87230951613648934063)*x^66 + (4331368
+8758928043819*y + 370488386653834436323)*x^65 + (-287672838056258651793*y + 
+130006789216819808906)*x^64 + (-175018700423712057769*y - 199325075624692888
+658)*x^63 + (117217186558944665294*y - 184431316627662099968)*x^62 + (167559
+880460473073606*y + 50231770182088582728)*x^61 + (-2924482708853224503*y + 1
+35200656059538396115)*x^60 + (-97526968113689340266*y + 24577168539715006795
+)*x^59 + (-35562575528916824663*y - 62321292808100809456)*x^58 + (3420538527
+7277312731*y - 35129207175233160758)*x^57 + (28534969871679714183*y + 147586
+77431499660732)*x^56 + (-3280308295039992672*y + 20013773166146345265)*x^55 
++ (-12280102244383760786*y + 2169956083918876046)*x^54 + (-38163046556255130
+57*y - 6563685064488706687)*x^53 + (3001549496003468616*y - 3542726921147289
+593)*x^52 + (2626931562224608194*y + 1142827140366519672)*x^51 + (-367443684
+968762359*y + 1741313178847717412)*x^50 + (-1120756441763956820*y - 13880543
+5659787599)*x^49 + (107554480539021407*y - 761016430421970356)*x^48 + (56972
+4473209147360*y + 104273935104641941)*x^47 + (-76920393658091100*y + 4556695
+27002677921)*x^46 + (-364966710997667886*y - 30164046307811567)*x^45 + (-156
+12945533449050*y - 278452806220656846)*x^44 + (196220179915190037*y - 460787
+97156864550)*x^43 + (58047935399898944*y + 125056052810735004)*x^42 + (-7036
+7728073948453*y + 55368143890230595)*x^41 + (-44471188376651271*y - 33091705
+261026148)*x^40 + (10779811934500297*y - 31310302362591686)*x^39 + (19596724
+151621886*y - 457971706716061)*x^38 + (4703077156935772*y + 1081792356232906
+6)*x^37 + (-5112561845313059*y + 5258998337190094)*x^36 + (-4262623171412271
+*y - 1900082482875272)*x^35 + (347587448455796*y - 2909203449118066)*x^34 + 
+(1761877237395113*y - 250996844693464)*x^33 + (371437987605861*y + 969565907
+346414)*x^32 + (-487486417978576*y + 306059642167156)*x^31 + (-2039902910102
+01*y - 226088796680855)*x^30 + (99522339968066*y - 118416217970085)*x^29 + (
+61143701295650*y + 42854297857883)*x^28 + (-18978818705192*y + 2898834380176
+3)*x^27 + (-12949633488740*y - 9667592872104)*x^26 + (5741131351957*y - 5368
+774573391)*x^25 + (2144907137575*y + 3501192827999)*x^24 + (-2100987167651*y
+ + 977806363155)*x^23 + (-520851483678*y - 1249500770581)*x^22 + (6929250901
+03*y - 282970748175)*x^21 + (163182970660*y + 341901790078)*x^20 + (-1569892
+67296*y + 100643285180)*x^19 + (-56780498004*y - 70327470888)*x^18 + (288917
+72141*y - 27706877778)*x^17 + (12761071756*y + 10152009124)*x^16 + (-3266832
+488*y + 5793469739)*x^15 + (-2353679384*y - 1069498319)*x^14 + (294927621*y 
+- 801098945)*x^13 + (247313433*y + 30224897)*x^12 + (19238389*y + 79956654)*
+x^11 + (-25462020*y + 12961894)*x^10 + (-5864817*y - 6566480)*x^9 + (1438139
+*y - 2974605)*x^8 + (1415485*y + 635208)*x^7 + (-424808*y + 438197)*x^6 + (-
+40904*y - 178707)*x^5 + (35389*y + 20411)*x^4 + (-6344*y + 983)*x^3 + (499*y
+ - 430)*x^2 + (-15*y + 36)*x - 1
+x^6 + (-2*y - 2)*x^5 + (2*y - 4)*x^4 + 14*x^3 + (-2*y - 2)*x^2 + (2*y - 4)*x
+ + 1
+Total time spent: 436
diff --git a/src/test/32/random b/src/test/32/random
new file mode 100644
index 0000000..165c528
--- /dev/null
+++ b/src/test/32/random
@@ -0,0 +1,30 @@
+Mod(0, 3)
+0
+[[a^2 + 1, a + 1], 1]
+1387580271*a^2 + 3706911745*a + 1312695376
+[[573349991*a^2 + 2420031944*a + 1343519483, 2386085604*a^2 + 2392264859*a +
+ 1794760354], 1]
+15580190479849132290*a^2 + 18389358899415525065*a + 4921253721701162537
+[[13347186698121344203*a^2 + 15544125443573997803*a + 6210385477505767251, 3
+879726255188522250*a^2 + 4837042904171823814*a + 4650867119660314244], 1]
+0.2189535881123797030145835478
+Mod(4, 7)*x^4 + Mod(4, 7)*x^3 + Mod(2, 7)*x^2 + Mod(6, 7)*x
+  ***   at top-level: randomprime(2)
+  ***                 ^--------------
+  *** randomprime: domain error in randomprime: N < 2
+  ***   at top-level: randomprime([0,1])
+  ***                 ^------------------
+  *** randomprime: domain error in randomprime: floor(b) - max(ceil(a),2) < 0
+  ***   at top-level: randomprime([2.5,2.4
+  ***                 ^--------------------
+  *** randomprime: domain error in randomprime: b-a < 0
+  ***   at top-level: randomprime([2.4,2.5
+  ***                 ^--------------------
+  *** randomprime: domain error in randomprime: floor(b) - max(ceil(a),2) < 0
+2124902699
+5
+1267650600228229401496704197543
+  ***   at top-level: random("")
+  ***                 ^----------
+  *** random: incorrect type in genrand (t_STR).
+Total time spent: 0
diff --git a/src/test/32/ranges b/src/test/32/ranges
new file mode 100644
index 0000000..85597ec
--- /dev/null
+++ b/src/test/32/ranges
@@ -0,0 +1,144 @@
+[1267650600228229401496703205376, 1267650600228229401496703205377, 126765060
+0228229401496703205378, 1267650600228229401496703205379]
+[5, 10, 26, 50, 122, 170, 290, 362, 530, 842, 962, 1370]
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 
+157, 163, 167, 173]
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
+[2, 5, 10, 17, 26, 37, 50, 65, 82, 101, 5, 13, 29, 53, 85, 10, 13, 25, 34, 5
+8, 73, 109, 17, 25, 41, 65, 97, 26, 29, 34, 41, 61, 74, 89, 106, 37, 61, 85,
+ 50, 53, 58, 65, 74, 85, 113, 130, 149, 65, 73, 89, 113, 145, 82, 85, 97, 10
+6, 130, 145, 181, 101, 109, 149, 181]
+[13, 29, 53, 13, 34, 58, 29, 34, 74, 53, 58, 74]
+[[1, 1, 1], [2, 1, 1], [2, 2, 1], [2, 2, 2], [3, 1, 1], [3, 2, 1], [3, 2, 2]
+, [3, 3, 1], [3, 3, 2], [3, 3, 3], [4, 1, 1], [4, 2, 1], [4, 2, 2], [4, 3, 1
+], [4, 3, 2], [4, 3, 3], [4, 4, 1], [4, 4, 2], [4, 4, 3], [4, 4, 4], [5, 1, 
+1], [5, 2, 1], [5, 2, 2], [5, 3, 1], [5, 3, 2], [5, 3, 3], [5, 4, 1], [5, 4,
+ 2], [5, 4, 3], [5, 4, 4], [5, 5, 1], [5, 5, 2], [5, 5, 3], [5, 5, 4], [5, 5
+, 5]]
+[7, 11, 13, 17]
+[2, 3]
+[1, 2, 3, 4]
+[3, 4, 5]
+[1, 2, 4, 5]
+Vecsmall([2, 3])
+Vecsmall([1, 2, 3, 4])
+Vecsmall([3, 4, 5])
+Vecsmall([1, 2, 4, 5])
+
+[7 12]
+
+[8 13]
+
+[1, 2]~
+[1, 6, 11, 16]
+
+[1 6 11 16 21]
+
+[2 7 12 17 22]
+
+[3 8 13 18 23]
+
+
+[1  6 11]
+
+[2  7 12]
+
+[3  8 13]
+
+[4  9 14]
+
+[5 10 15]
+
+
+[1  6 11 16]
+
+[2  7 12 17]
+
+[3  8 13 18]
+
+[4  9 14 19]
+
+[5 10 15 20]
+
+
+[1 6 11 16]
+
+[2 7 12 17]
+
+[3 8 13 18]
+
+[4 9 14 19]
+
+
+[13 18 23]
+
+[14 19 24]
+
+[15 20 25]
+
+
+[ 6 11]
+
+[ 7 12]
+
+[ 9 14]
+
+[10 15]
+
+[1, 3, 4, 5]~
+[1, 6, 11, 21]
+
+[1  6 11 16 21]
+
+[2  7 12 17 22]
+
+[4  9 14 19 24]
+
+[5 10 15 20 25]
+
+
+[1  6 16 21]
+
+[2  7 17 22]
+
+[3  8 18 23]
+
+[4  9 19 24]
+
+[5 10 20 25]
+
+
+[1 6 11 16]
+
+[2 7 12 17]
+
+[3 8 13 18]
+
+[4 9 14 19]
+
+
+[1 6 11 21]
+
+[2 7 12 22]
+
+[3 8 13 23]
+
+[4 9 14 24]
+
+
+[13 18 23]
+
+[14 19 24]
+
+[15 20 25]
+
+[1, [2, 2]]
+[3, [1, 4]]
+[0, 3, 4, [3, 4], 0, I]
+[0, 3, 4, [3, 4], 0, I]
+[]
+[2, 3, 3, 4, 4, 4, 5, 5, 5, 5]
+[2, 3, 3, 4, 4, 4, 5, 5, 5, 5]
+[0, 1, 2, 3, 4, 5]
+Total time spent: 0
diff --git a/src/test/32/real b/src/test/32/real
new file mode 100644
index 0000000..cc962db
--- /dev/null
+++ b/src/test/32/real
@@ -0,0 +1,7 @@
+  ***   at top-level: 1.<<2^60
+  ***                     ^----
+  ***   overflow in t_INT-->long assignment.
+  ***   at top-level: 1.>>2^60
+  ***                     ^----
+  ***   overflow in t_INT-->long assignment.
+Total time spent: 0
diff --git a/src/test/32/resultant b/src/test/32/resultant
new file mode 100644
index 0000000..0b08883
--- /dev/null
+++ b/src/test/32/resultant
@@ -0,0 +1,413 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+1127614608
+911045
+-23334121
+15152875237197769342976
+1
+4789697380021054650345938381924768779764502171151476021251465336909773783338
+1483478046719619493978767437813786102680708004930493639232124484712989030743
+77376
+5591778626126198515789740127923726924138993122908566507963011388676468961202
+4477425313882678703984217553283789132475364222241284814412314235455434422980
+7710892411505927222662178875831559611191917768447356156048451344207870859235
+3421574869121809933510172523951949560305425573780083942051414584453903225103
+9150588607156375894233737091156899560058104991022009737098022791771063870332
+4543754376917999871790501326547853054436444495861161564399190140572883960196
+4316196975817429852098537493490087741730840438498474174124950599821563462379
+4776250318165223172258299094200163699119837367865451816040219464272808536682
+6366448478399563694093511562444580982947874089384832363620244514399470126325
+1221138634876570856880659380036429623915483515843179086549287327024998004564
+7976338198194174093893290033883921204928495263532870000949040615012556044932
+1818817854986256664488406380789052297112100244473983193254140610618060591371
+899564340082899114785688720906351258905787053899776
+7081072488122549108437740087055070486542322805098742006332199056679193928966
+4483785295568554162322036400017122263928267064345800987077874888846659771496
+6170549511731996859294513678297798592859196096017665960363670523570420377721
+2919429331810768214726379757777338665118630273090090442002357392565485086331
+5412450577546522243130333620009107379345899855572005517250317530197793851722
+3355902478870693674755098498729934799138680215415329581514280083638477701043
+9833756129716245879910591555562933614970015160148305939648207485535526079234
+0506699237254741161806485754160707356885468112747818240990343439929195771261
+5718119731260544392168170080797529909765466194544434766073431138630521097535
+7468986434676998993972786577010278592120332643280493735615754118765736720057
+2393565271488265393041765173848995399894723709392064991834838703664591278597
+3082437298288444379811881610029611376601722270737965832251382983968232233649
+8871844617255503719150928022965927019986189521620111495073311653079160054784
+6857440559795860180426346941115122603046544514726275045580655957003854337394
+2366607422738440023830359041009316801704138433518791880556684554000725519633
+6314221737402120724621182545795068118781015520862409165116892715165782201744
+7373528760095126876619409888737755809138526698545586130093257520346765161706
+3668856674264264360780430404097555123190894892970707035925992771214318693453
+9933063048724159581078642517219830228441155730464252333929338601304473477379
+9924164123779424453250220315240908440937220701876898151966131532288459419998
+7193319301791449277695747801099373571458888429427291702061589949699722004575
+4707271425056836723915364693803883154598841808528064617757070166253079521282
+9330993749058818913084870487521920089024720415354689556927595116643657782190
+7772163668281509363539079928183803467678757273373526894367221203626621799795
+0834794280858064768554880652430560684135485447762704940367795197161950331922
+1933358870993764673870859837952485667866798702804457244640778314935644379077
+1781507677136287186820379853171767323733496951804295617087357625661051104152
+1796342712986360278714707030680719176226964227465901389213818171046492873465
+0022790780563242373313610025898026386690016904386357791379547299812905227390
+1289797056874223293654886944596148419408207352315947914652557188420178927934
+2848619416825409009261520189608713137382207964171283558993905022859982486695
+9083539842491853405079547629485109169490879476146913423588991526498785270327
+3968511118988030085179508566878601698165562240004498473335561372165388144302
+5299351926733300122236311522899635357333042443410986368545768414279154922277
+9119056516432750446537012271038703954019716627215359282310427886580436806649
+1128763133722567880219150198570570334214903477999954881887377115447377529450
+4743238205391224700586995860603351466477628176669368096677069674459366626343
+1922851858053123966451232560199488110901933493373773342297093283394419388248
+4588916794714441254931339361853072424034331084764085081987592855543881808243
+1779116507645535321440983529543254866843835344498356598259597110076846725320
+6851381772957660420342635656117691678289746109819594274521914714099658185017
+8103384766865875768775851148845968289169127685348378811992123602296633104373
+2805377772394628665071343459476385073839007177030802143173978958766607853438
+1299576637513893400073478172504247752287917016974831919723343256352815430862
+2756735538729480299222282240438655236155081637699288961373884981739501718182
+2681375497299984831021291189018514228948558064909058423273859290080459595571
+1759917019292934458035585962564362223881131293139396977742617328161512636776
+1481197065944479079708702464472102740839784622024302517281715139380530650311
+1915971888668147117514765329906797514964395684832725088547469451338928840681
+2720007091412810257156421902095403437335707368255329889993567282476060201844
+5771396723720966942646908853851819209514039160674238448209530171041506333081
+6987469227749752421380683026987025045039827341868747252476484201784372446710
+3510248767574924856801643658855141372196574234941500177109628589376464368340
+2999855256027646684276215612404965044584115542842898614670574653437772506535
+4923273222262319450335041587329691932221764773887410781545905870741527518830
+0856696173015051150711714497101540120133632210837848926748871140267926449098
+1423419401755524300390263874468295460307895598999553133968897373983301216878
+2734049667608533709411627027619810284143582046196764517591998490057580029106
+3609272592147333011404236522621245214993427142890298259090018689952508140536
+9654638037034014821665971679425142385118234439462497731669309557480650496918
+1834649980677855020436832515946267480139639823701397028549118270169170519415
+6721794171223686532838514733056067604528123664171806646382399462634890916813
+6669485840561432308522405899924251087786067664162652793044413599537424129707
+5477186898973633516352568883257904332028806586709401569864515982688222129153
+1567052855728262995530434353630134178070106956944279797548165372439216825076
+8326715576786303582598430095125742864236163720296213905787039885884463657834
+4643437508267891744779261877500947228066699509369586576615123985367219743858
+6658022062041430002005181878216355419666743135504759692658299056669357259645
+1140384349942471935622171122262819380938871819723106690671480887238746092900
+8259392457845570085120138974098112147614179833839988811199774487498092129908
+2525330985586942998714786716070693999685611141806930181168204974764634546790
+2491116660085873519752267226746027234992210672340901561111569016509087980484
+7241366934438873990966498559499978863196895478498708930733440029703211754971
+3174338633455354574979245770026845320172157118052425714203442756762690452556
+2195168177038659757872091439804829007952032160043794866268382905222485457033
+4877085046605283955920544774598658688901188561694172099986871239644906361688
+0166806589820637842653846370311841209424194808154992103008317976618840646505
+3524843776927351496372745683004763216358973783229858596853568141313750965802
+6732704253827791291256677915251801050186660146187981397142021248163932831949
+1242424936332974106235910726775448936949225963230406509254485317395735192765
+9629100658168052496684342007876499412743472614751505477517198117059603151774
+0513333347014771131213527388744277817255738469560576073881746517093241538950
+8379623995721601203434646863118546318466543153575568871197413866654425349795
+9709665081383739157130696886846685180184094150972054886673926466898476160056
+9251865200870473249492728358926161261942133179621059876513623829755485965508
+1765991185475814355042047437792982556193134305138749561348141122718465434028
+5955347577189501961066057321746605591478524411065032570202848093134945388063
+1469805925510862902366653958969425851152732736769061502994730346813692391400
+2188444260504305088747166891225921799545810287134453936174360046753896543846
+4725311018589188158298923975056694464632127746952940324604386896534946901154
+0189748233921118167859285574717815593009931414748460169210424421915763646667
+2549643791464812661184224613907766599594347549012695657501957708976598982379
+6246315465707511680970636654737842632945349283341207033490434420327941078448
+1247500527894528017587459786622669024443518026949815548731423263169447248228
+5110467004911800020475713052104208484239152280018530719982927713299542152131
+6714018358725068038634436774574546773146283899186292132944165251525364117406
+2333682077664235142911764750699483904374984050720750763989372219539833457960
+7517904946980952462925255681650144231984545917747245637003964519102283728734
+7801223416232398530453943213234148790697908440512084299095779197332999945341
+4964437542186365784691171021464796820401539232838986619037169900580166453697
+8875543972648384797560654661388477062010340611513997845689183412371725641192
+7259435812034642758959741241517961837062742047554891249269300463738657711558
+2873355887128229794245851803952135238397923971740033612324297035200484275438
+9156423664686366531825242597376576860994929676476159165395192353567061116835
+0879052026395527993330789894200638408494686144201609307220354927475939107543
+4100442246166647741053132902356715119673330523458821912870829882436969397956
+9406895726081967239095445456258787542857469923106451553636440562462133562360
+2668811067192149078795792377466748973463935910225355341714595027831656267131
+7448279182843337911131360717637361214496493274000813932176510723999645466023
+2577690480408703033440600514220628547543775356283381461661090490710822064909
+4442349542870358299272174730711563333091694493018076280359800542459534086476
+1753773084515412810070994263488287449786031849963340685709770929949412357769
+8595467102933787819050340231630643521983014085233294591129365647969335691870
+1645312144680039661515859963988461311015875682686126364930986339192596055528
+5415821644430946059242051021689727062158285718368764970142254220221251016570
+1562643814866019675250331401163912499653408827720323147052395354630909040950
+2449569343309008357721192162211813923928897216896982491145981783817245900369
+5174601436757895126770570726969854492290667489978001725658920553817006873736
+9361312619106558444263836067946213531312792247596380264510333469355965494293
+8057756424419773526095629957221330300887874765988164844737994868164641605222
+2825624132991984850324866986619296607978229622915429194579009932988226000946
+2799716898319535999601514182213198784020927602509705335937317436616464565386
+2872053875185499951244257832174070631688990682874482631471691339296616648363
+3357285120715643816652078133798755984077111980231423933018801082978392585421
+0557410505624607436448090236794246925372257552335205668157924552625077577745
+1444994285443412758408944565156472565466791294345215703318051430639461056798
+9104112885134043465540265984546300434414876490392033240018221222923160975692
+3574118451325235191746732118645012230660740739534589705361993281591810042848
+1386376501091030428196191317149726249705003607276293632768455631898002411316
+8723568666921731614366149245523567264036839088882693159534231662113318927190
+2700597763458658138616263432657882123932584966891493766304566230992548511870
+1578565808767928420814686276337773212621085152030353396508799832670048785694
+9458195423731556131061003012038999565838203945938486770903260446305772890830
+7066407095099849010110311279170801482672160151298019450205523688931455046837
+2696701818054864809290668621005085842719448633255369262839048473607324076854
+3876512651160078271451580705114404951011889183775018819798848974826562003664
+6661732798500898437911439686901081863261839430043923237046283466602427871304
+8506953093641642163367712127257564040734310940434747428181583577390637815785
+0568728380279883244330109231305923546694823757987289677640095975011241184318
+9915958392805871916668742980025522360460089258695511886998639786040952838129
+9175637805687931816598611425185730113293288911861332695586714572145658115029
+9844338849992973326251312720024106411331530108183020506081531934467942310766
+5480466436597036298814762723036190616555337378880978653796168575503594499080
+4938901966476145425173757988475188344145289435972780305559861056903328353435
+7097324796734880384585281130228000670331841246489779777755289555189750148212
+2460528569662635491485226214600657466593819032606384448441494814219184451373
+2970238465927788712086864971958121372221766814952929308218481647008620534821
+4870259320816045694040663489812592473817436697294095899807395498397055191786
+4246700427676626086795906762335651942030658186154911718028023640965476401541
+6331290138619972853394139193068857312592167829698870990849405149987021278969
+3611178142790638697562338466404270499149409798871543973677872220822218818707
+9274126492516822409042659368169703465936982972778620219021231830098682652554
+0129952966507080659132966853812023584303362666270317227465529934100811015105
+8868990725432564987605223395940547374046157871476155638971225706634706527197
+2100070728017924350626608177098973079265381463675975950527323756525988663565
+2792980895669484248299410038263537660623017805311298685765900580697924240570
+4587261148427817001651793337528843665500553095898064074090246369336456936473
+4373803950522485651344423864279448523115897237705822685322793258751461529039
+8924994348239102187147135789338076432776608620822393601589765157207326701598
+4974900600705558869601187220120358321975167707693820494316713534023111843805
+9283173364786844421849556149175619273166387467854407493491717116722424505053
+1728410586881113103309453413145528185963208374232941772791717539803691080384
+2397504701077839217768067902076125594575708755459937027409745203440094884479
+5140234505133742864665356178364606835448830939783260650214862842555014663195
+6415594335179574317671856504206447373591062916029471992300116963386628460524
+2847904416596141934741828146379809581664158905733011925952574996015569831756
+9703063616018931599351925057216994635704120606881418401365121636867122399809
+2774750147634980569429662641208390283422787696005105225767492395514821395148
+2324024851334114743756051875991639774324459647227599873088565986456465788550
+2092343371481239343113881272770926693174789149230168249996032172137615301356
+4980996965817634459665187513080922525605201604845857286564525064751439629278
+3000725290349331142295275597309690835948790615844571089136616960068195339705
+6194453861378417806817597789190149182835100252960383932417019745974444767099
+0624786637634290275002442959915995647243356991025785049598063354253091991467
+2884054539571691939828506354835147188797863438910609128541007455256683557131
+0562342964055240007307201315671721394884726343064451522335113780730215996618
+7001991699723165212904103307749537867071104955163912366676956060671585948075
+8856561990076412149919627753695230757225285324756951063606903236791929080189
+3973661505448117852920548942305793298953857044793936910063454602785182803429
+6064134170981414962838068243888581694538254933156788556169768331700594145992
+0829625229407628504478197743358306906062714062770191995932116159202455454698
+1833772637944551971708296532225251958444281762416016999602533345461193450313
+0648189438004061438499414165320145166394394137001741494601734265729049916536
+8786615510481765352709341172007999297661820452214994434444847483883226297140
+1401674898781247484082998956186471711709567755084249521203388115453739850984
+8201380092084758372275724538800301564602934678377853362331910728064061151092
+9552063475242103758900477732246484302256511528132759160221241392285002767024
+3669353741641622336731841455767829054402291230797389101828234070416279585998
+4177436328311159747118290973201619007228947454290436250321638835917076230562
+5908006734101378403247160809171990515159263712996771840506632764382172811486
+1045805889911666828084633587319003655886973250316525236916645981137835447238
+4293626020194196895754257039489866614124700604916502009470350933899248574263
+2433831333725836823737934116481556444659294843962623404784927612972830908818
+7995066167536899612315082598662649426372702024801212726876754386409528327484
+0407885195827599897275621697816182411963589270076348733404107791216013908821
+3312645242645005779272501822320994675104938946391158040143587955328884418481
+8552189233778681399753847414887764033120998535989960058575892967832407772128
+1486370558522744063535928001892364461867715745222791284527793212818188214212
+5151430384435843528957786512789031489302505302683680671161908878734972360980
+0169606010403993591690593687152871349591312158380146505938090653290650464244
+2393715616059833231533447758770144994797243194913401125749757861887967071474
+0668243840880239975410140487167665497226423304105183321456942712666755366048
+4513930715943788103872965224019765145227252865954271143477545308093565624554
+2038273819668351030692714185064237433409989989843369326821584267652829971183
+1047748516522526723332327680235436145444057502299686617058124219054013357604
+2058644381474119584378752212603826077589535634202050954327560432207418222041
+2946673374895032454261423042963903275402019846909997207392220695906575618068
+1181429287813762522813907938908102094558672580674959851503384043646005266215
+3556850616430494449519453861414586052518051253449165323698938124942367235337
+0262664752264544375885466166799033815620881800700646346140590254007360813756
+6106251312723308725668659882494606260328217635132797924501855990617961457672
+1019271798366689077713053020708507055485816592439719097528763798162719234761
+5768842729973762210377565701985356010572319029207706749810973378297162497315
+2429257893364899813089138087002591377752115806621791689735535324942239281389
+7707658659115297091177669024303780469123021110257015997626532926986838094760
+0251473914561276418783777747329910981979578981148252984143759266894331122042
+9041445345958715283155660279310587345830247860448837525261315766198993992499
+5959135476557955828889140898705619068986297758402572687925341338893322191018
+4951574210527100911563052359377857775455187162182640677583563839405015418626
+6271003135442406476904292013486173064280165102275860877358055961680377018117
+9407258058194062595343490331433128277476287025562338780716296607863734421964
+6398504753473310937624280680487514461999171822917905958714467444259409540096
+5125686461106746954329208107230838711949699315085161624573548764472629924000
+9388826927578163869644055897801290539733180405120167542888739695296935787527
+2277231024000866999422003496826219681773340717008049883536929150209759419719
+2476724495228873880421184843573662693488497521949703654293617023731876188021
+8161101036337107718919946052334638989394796774173354409309562498787726965123
+8501379728687520420137037318041140155292864410431179358106654346624235823545
+3064016217648136175289259908242738578378187682097303323420320267640427880197
+0546103037587443506906154893888189366292745693373465818296838130553615764337
+2012379056702209339738530032711295715914291111593610024223834654681234489950
+2995984070241882647259917195419249566653665169904729139657919194431471824505
+6330324626778144078219820414708337050772319628837159448612020902643054481443
+7785951186072214743077777545529851432900878096517420251324683967383472320646
+7072171826870056133206946006956594590822413225672457103287494913610929672857
+2802979513803760415959689167004444098745469704235693893440234078348358992130
+2902412804083486203809602146349295979585966691897800454007145734217841442504
+2509656059394416722763927807238311390512732351899114617488579954637344893472
+6956418040020563753576439361112293250812538205606862069824031389592220753938
+3931980444000396952790300025110413488020461314044867096400482555611861766557
+8779872633208790282853041828436599000821439615909297936909400476874231409386
+1107957033048878824727553773211614689966672780402203443704886999018746632918
+4578570059506705179295259119648586493455744160376834283354774861549013613756
+8914104974148435789675196156799768181676487349484914162264963803701991647308
+7077370046772797817386919223496396655554669074903108105861101273892479122450
+4487000094244471654652318170723070150479698387577348165943027364806795695771
+1605973865900671088466048319535078791374564546245300366331708247635343933398
+1970452195592950156793361600483926819755275149212838754795843185318110832468
+1382112309145641859606129754154071672948878109857709847136184953440514957541
+3641496697051930106770483801168708463106269498367706651154782577811514002639
+5106563888263605829321212608718331459495599039042081814718848457860199057649
+5346982843071231821350857965681984563335821957754569468382305016184024887604
+8160723684261393716356258451730935748281676054966556038316792751070826540054
+2473315712042588604514882868227176108332833877184166366265303737221912013662
+4512167901067209682554392111071155499467651327936730515479173229263731514370
+3330182513953368428655392818025057917570653783124434362274261551843983160477
+7351162406242450929672186628419950329891867814208035883609977838886238833276
+5330774679847630559105909002838233097431528046724187446177909715044255520881
+8889404837062703651637318360772404220635983462926485432718138685237631857455
+0598465166755132919094585655974966874524784130599703711634113352005177406926
+9603316645497284293848590370977640427328887687375912505822778917514360380115
+5682283488759427844250167972992511302878057532531484502529732162662116955639
+7406173957119233848745104829248262313597011029557702426889396863083570940205
+7509537898825756241704038780955282562441114537127841752209332467489719207566
+1744264901601182958290811929305241680377823312435028072780502748828361181784
+5922938685656775729737638617283841787967835602522574138712851435180707114732
+7070864077169181369700790505644456673957432875813509347521540262444684384812
+1628162757064794714790594967511664494982986431532091869678632267880119090181
+3358285913239640118632956526316790292934199787619720599635756782126973040030
+8849995749515739684821341699562520787829013184888043096654760631188226253631
+8585742707987188915896804232114765200530074024419150347592072019161120869323
+4748482955558033228065455570160118248780022998383870238441158071327622059726
+4610703982594367716830470741988537826072641367377235148800000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000
+6424194875218376701463113579276658235425909119319864401234806441094021127054
+1375266956365232048220109345396592726319742460454879981062694340660155949810
+0158596374952141691378161159043297105952129759212696862483103011107449914024
+0094518530978010978915317583738701606420973822157506086835007914978002982268
+5532998094510470979840507359084614024762021412168744501835585051640684574287
+1078953105601656693485703971643934048397703383418980257911777900546820380805
+4170495763626051350369423653552681147090272505727727116438873040672296148742
+3911117072924868470426691405643972081983053358547587471903955838256977365810
+7214608397690540588379230455866567931949997177575434025237758134753480761133
+28381591924887486184484485922972282971065248302182867907169925597
+5666658170985421428568776023708028963706995520080659357168692551584779660342
+8845370048005616413297301438788684948250159498753561703887501771998471467047
+0572365820592328518742718645259795706159687456606230246406028480921554259928
+5340467680010491452771885463865912263236353978700007220144125780098160197203
+2031201691364593717086444953556041504587784874247426094277154056141748358043
+3092986242590451866923730053842550530645976252654457481607935574398512975433
+5958612625992295737052292037154444584538830158625476522119502606425844311713
+7568524432625716058524905583126467948164530845151327970412952427667083285696
+1654438323710321253566705831201432914714319143016623815060881384835591899090
+2471009192031544760372901915755330058532299670144074706793928555485848393159
+1828050109715302094839971124460319775966476623121401249450851220197356866122
+8030237845317849716956219074680650465917640347731788333583546848508416734340
+3898157780073751187903396667377222823979111370825580014201215182931295599914
+4605197631998749857550713081687598984457233392295649568376711423487711804910
+2828628167987521424766213383686969797280488819530652810103906518166447659272
+3960381547488477643959422660036664159679049695106798176262658899212922595345
+2454875526563586504128373884988804533889790263675488058966242204666913885135
+4761573671014826411947138132267980759790003000518532492673507771928777973799
+4462263269300516966805110216380798756683709747323283623552894566069041854716
+5029737260856017571432569467896308128832457972079937266842795609362963233465
+6327577377986418531570275825779937665992342542699460636407443029919577857433
+8281851600006090764198717337191559381124541561638319703069586167725629299517
+5076255282960787602293050101970033429356854625906232924704291310702886456251
+8632469985653278132588227168080017020040737658591877433454971545485013311503
+6561434485127084618089632304423817006708918171546906346335387536183560949225
+6036074667890092069337996522372744178148163354405930752765169071704137022745
+3128090263426824477803494585851173850223374121732489365069889497237403158227
+9840839631127334319988930212402475223960918680308467061859410851679902975904
+4661251441881460358726937406012647950645653613408222529039998067223110238643
+6584292316840781276633771054531894956472312394322235029051864341558369191090
+2082097544642694038762391681516198232954168712855564402707363812139413162515
+0480689998678584944350427513554851672228063533753307926177250358938882470874
+0552103661088094930068257811242447871657269388113518991542989510629246336165
+8479986550036211371919196414464310026701922662394921566955076574453643730934
+1341714157231194704091874372904256798372091118444460231449307037375034411885
+3676576076869932519269429557331764241054721425368240513547145593868968757079
+8413509507002710738565824220172607150332145236704442306962285332528127656564
+2590547122402610145691024371453366639652111607302939245318506315359461958010
+0338927812940593249471506386124415697212645252323776162585064558232775462345
+0951607701158724151244612836110221471968264848258590874666079962640426472074
+3487324130608218105825409790022539148520998476124281291687889066415082363438
+6377246876252328877677745106379993913801568088315491443230018175530767089172
+2280158309314854025846942336224310461844675465480506733085433084145387991886
+9163462389952637245366258421425459491140903882880003377713821451634188188884
+1270198600409570953154950542534700105045291091093642996156632611959521886490
+8877592687889204374862069654416936978853484961795533371115377921813664130820
+8894785497117037657605284341185537822982652138954441751771331074319374402086
+3465730168436263997343397576043651046145427257983216065136429702161412662944
+2533223411458043954241454056372388815682513236663702933669435298342603484167
+4852785108888112467902247752587923476008872710917234044426046543641356109789
+5876614276812058803783525798121627107430819643445666863755934345575064341943
+2421032153639761995700534388812500609556342126624454207517606357454052961253
+1916416611193320507596890878455922703579754346646730035078177578839631475550
+5154158684730661893835639483595803909842534400000000
+-146466598037214168132166946505504582163231930504557323838755829912078704914
+7232527937922846836370373882122793776918426462873008206004301364448376259630
+0479960292079047337203318689620054594788601914812264756266003542446303110405
+9560079971360943834135331209843652685215567853268091899077562013277846536466
+1786518361349621668764106591611342239895640346016543607765851182771101386113
+1718339502303309148563294578063244261544184459699371894760765768843605074654
+7819950060859169704099256831302939154372463310228820624840543133303743930037
+6832934573832667131007316074052766767211417435693835817229175698303748426224
+8164061481748353716219313553054565951525041510277959943506642274215459240400
+3413401913246940636306835336167263971847965958219668263615363229308670003786
+1280039502702514466128151473480194115101423711109540716126364366215022998077
+3438647003907052554543859418494105956511786638237430891776264628858558346877
+0999659442974447178448949336232579513990920556348752372589170183967760081282
+1068289815831941416139365959202771157717021073756071068631224445237941841902
+1010658333344411773566886865763298166211226814014383373536918777417377201936
+0194260907892936117931560105492485208945051722728459321506684015902216398736
+1437975998395864293392034350055095596731285567989250747678871045484053969091
+7738158228784687166822861537430240167501324801433542590655581872680422750854
+3569243672323920014911105847183250923458086240362287243614402774812893620769
+8958454584510734797308369548615843411537874069109470839942512266512001813365
+1686265685135225872723370661848625082264317185263751868490644013658070593769
+5212774412568374551078701167623193603318820391281854674000166562224088265619
+7825432639193103479385714210231933101171528480116302844082573381791300062745
+3330296747551928284460293572813648085075110582219522739451115791229369187172
+3294412418856265068263073465221599276551549319404148769602303854312999142835
+4606621084918266578016717978376941310421414846182410260276675218940359642915
+0789105727294502216051212140336002972753983918342578254176884786051351324666
+191659726146306072405158680
+5826600558186537404838856131370471039379659134
+58076907875090050502110574307657256807287717388760005394175044663
+2615551406650875838181122330253797985104602070241678014791853540448034257062
+39419
+1177939634036131600533146804222733342399463976109814236899724042387935525345
+792616589264996745575
+227719861472038658791603995367201962254031696071197510053300364489
+1025559083270339613328225485791293069513108821161587766318899893534558082149
+559351
+4618707505262750497822661744140123902029723820123390596302244701544184757399
+151666179549811431957
+1973
+[2, -2, 4]
+y^4 + 2*y^3 + 3*y^2 + 2*y + 1
+1
+1
+y^4
+[-2*x^2 - y*x, x^2, x^4]
+[-18*y, 6*y^2 + 4, 4]
+Mod(0, 3)
+Mod(1, 3)
+Mod(0, 2)
+[-x + 1, 1, 1]
+Total time spent: 6844
diff --git a/src/test/32/rfrac b/src/test/32/rfrac
new file mode 100644
index 0000000..066de55
--- /dev/null
+++ b/src/test/32/rfrac
@@ -0,0 +1,8 @@
+y^2/x
+(32*b^4 + 128*b^3 + 80*b^2 + 16*b + 1)/(32*b^3 + 40*b^2 + 12*b + 1)
+(x^408 - x^306 - x^102 + 1)/(x^510 + 1)
+(-x^994 + x^497)/(x^1491 + 1)
+[1, 0]
+0
+0.E-38/x
+Total time spent: 3004
diff --git a/src/test/32/rnf b/src/test/32/rnf
new file mode 100644
index 0000000..e6f59b2
--- /dev/null
+++ b/src/test/32/rnf
@@ -0,0 +1,754 @@
+[[1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1], [6, 6, 6, 6]]
+[]
+[[1, 0, 0, 0, 0, [55466/1129, 839/1129]~, [111031/1129, 1480/1129]~, [179046
+2/21451, 22844/21451]~, [3584116/21451, 39304/21451]~; 0, 1, 0, 0, 0, [-6071
+0/1129, -512/1129]~, [-121528/1129, -808/1129]~, [4210657/21451, -12522/2145
+1]~, [8428762/21451, -39940/21451]~; 0, 0, 1, 0, 0, [184604/1129, 1104/1129]
+~, [369536/1129, 1552/1129]~, [-2435574/21451, 1771/21451]~, [-4875461/21451
+, 12168/21451]~; 0, 0, 0, 1, 0, [96298/1129, 463/1129]~, [192767/1129, 584/1
+129]~, [1767005/21451, 5405/21451]~, [3537145/21451, 4540/21451]~; 0, 0, 0, 
+0, 1, [-216304/1129, -928/1129]~, [-432992/1129, -1088/1129]~, [-4319640/214
+51, -5473/21451]~, [-8646937/21451, 4368/21451]~; 0, 0, 0, 0, 0, [-564/1129,
+ -1/1129]~, -1, [9/21451, -18/21451]~, [18/21451, -36/21451]~; 0, 0, 0, 0, 0
+, [1/1129, -2/1129]~, [2/1129, -4/1129]~, [7/21451, -14/21451]~, [14/21451, 
+-28/21451]~; 0, 0, 0, 0, 0, 0, 0, [-10723/21451, -5/21451]~, [-21465/21451, 
+28/21451]~; 0, 0, 0, 0, 0, 0, 0, [1/21451, -2/21451]~, [2/21451, -4/21451]~]
+, [1, 1, 1, 1, 1, 1, 1, 1, [1, 0; 0, 1]], [1, 0; 0, 1], 1]
+[[[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, 1]], [1, 0, 0; 0, 1, 0; 0, 0, 1]]
+[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 1,
+ 0, 0, 0, 0, 0, -1, 0, 1; 0, 0, 0, 1, 0, 0, 0, 0, -1, -1, 1; 0, 0, 0, 0, 1, 
+0, 0, 0, 1, -1, 1; 0, 0, 0, 0, 0, 1, 0, 0, -1, 1, 0; 0, 0, 0, 0, 0, 0, 1, 0,
+ 1, -1, 0; 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, -1; 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
+; 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 
+1, 1, 1, 1, 1, 1, 1/3, 1/3, 1/3], [6487513772307278389925284499624661, 0; 0,
+ 6487513772307278389925284499624661], 6487513772307278389925284499624661]
+[[Mat(861), [1]], [480, [120, 2, 2], [334, 286, [-860]~]], [1, 0, 0; 0, 2, 0
+; 0, 0, 2]]
+[0, [[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, [1, 1/2; 0, 1/2]]], 2]
+[1, [[1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 1, 1]], 8]
+0
+1
+0
+0
+  ***   at top-level: rnfdedekind(nf,P,pr2
+  ***                 ^--------------------
+  *** rnfdedekind: sorry, Dedekind in the difficult case is not yet implemented.
+1
+  ***   at top-level: rnfdedekind(nf,P)
+  ***                 ^-----------------
+  *** rnfdedekind: sorry, Dedekind in the difficult case is not yet implemented.
+  ***   at top-level: rnfdedekind(nf,P,[pr
+  ***                 ^--------------------
+  *** rnfdedekind: sorry, Dedekind in the difficult case is not yet implemented.
+[[8, 2; 0, 1], [2, 0; 0, 1], [1, 0; 0, 1]]
+1
+1
+1
+1
+[x, 1]
+[(-78/43*y^2 - 14/43*y + 173/43)*x^2 + (60/43*y^2 + 24/43*y - 143/43)*x + (-
+23/43*y^2 - 35/43*y + 110/43), 1]
+[(-2*y^2 - 24/25*y + 139/25)*x^2 + (8/5*y^2 + 21/25*y - 4)*x + (-24/25*y^2 -
+ 27/25*y + 61/25), 1]
+[x, -1]
+  *** rnfisnorm: Warning: useless flag in rnfisnorm: the extension is Galois.
+[1, 2]
+Mod(x^5 + x^2 + 2, x^6 + x^5 + x^4 + x^3 + x^2 + x + 1)
+Mod(z^2, z^3 + z^2 - 2*z - 1)
+[[1, -1/2; 0, 1], [1, 1]]
+[Mod(1/6*y + 2/3, y^2 - 40), -3]
+[Mod(1/2*y + 4, y^2 - 40), 1]
+[Mod(-1, y^3 - 21), -2]
+[Mod(-y + 3, y^3 - 21), 1]
+1:
+[2, 0]~
+Mod(2, x^2 + Mod(-y, y^3 - 21))
+2
+2
+2
+2
+4
+4
+2:
+[1/2, 0]~
+Mod(1/2, x^2 + Mod(-y, y^3 - 21))
+1/2
+1/2
+1/2
+1/2
+1
+1/4
+3:
+[Mod(y^2 + y, y^3 - 21), Mod(1, y^3 - 21)]~
+Mod(x + Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+error("incorrect type in rnfeltabstorel (t_POL).")
+Mod(x^2 + x, x^6 - 21)
+error("inconsistent variables in nf_to_scalar_or_basis, x != y.")
+error("incorrect type in rnfeltabstorel (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+4:
+[1, 0]~
+Mod(1, x^2 + Mod(-y, y^3 - 21))
+Mod(1, x^2 + Mod(-y, y^3 - 21))
+Mod(1, x^6 - 21)
+1
+1
+2
+1
+5:
+[1/2, 0]~
+Mod(1/2, x^2 + Mod(-y, y^3 - 21))
+Mod(1/2, x^2 + Mod(-y, y^3 - 21))
+Mod(1/2, x^6 - 21)
+1/2
+1/2
+1
+1/4
+6:
+[Mod(y, y^3 - 21), 0]~
+Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(x^2, x^6 - 21)
+Mod(x^2, x^6 - 21)
+Mod(y, y^3 - 21)
+Mod(2*y, y^3 - 21)
+Mod(y^2, y^3 - 21)
+7:
+error("inconsistent moduli in rnfalgtobasis: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+1
+Mod(1, x^6 - 21)
+Mod(1, x^6 - 21)
+1
+2
+1
+8:
+error("inconsistent moduli in rnfalgtobasis: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+1/2
+Mod(1/2, x^6 - 21)
+Mod(1/2, x^6 - 21)
+1/2
+1
+1/4
+9:
+error("inconsistent moduli in rnfalgtobasis: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+Mod(x, x^2 + Mod(-y, y^3 - 21))
+Mod(x, x^6 - 21)
+Mod(x, x^6 - 21)
+error("domain error in rnfeltdown: element not in the base field")
+0
+Mod(-y, y^3 - 21)
+10:
+[Mod(y^2 + 1/2*y, y^3 - 21), Mod(1, y^3 - 21)]~
+Mod(x + Mod(1/2*y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(x + Mod(1/2*y, y^3 - 21), x^2 - y)
+Mod(1/2*x^2 + x, x^6 - 21)
+error("inconsistent moduli in nf_to_scalar_or_basis: x^2 - y != y^3 - 21")
+error("domain error in rnfeltdown: element not in the base field")
+Mod(y, y^3 - 21)
+Mod(1/4*y^2 - y, y^3 - 21)
+11:
+[Mod(y, y^3 - 21), 0]~
+Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(x^2, x^6 - 21)
+Mod(x^2, x^6 - 21)
+Mod(y, y^3 - 21)
+Mod(2*y, y^3 - 21)
+Mod(y^2, y^3 - 21)
+12:
+error("incorrect priority in rnfalgtobasis: variable z >= y")
+error("inconsistent variables in rnfbasistoalg, z != x.")
+error("inconsistent variables in rnfeltabstorel, z != x.")
+error("inconsistent variables in eltreltoabs, z != y.")
+error("inconsistent variables in nf_to_scalar_or_basis, z != y.")
+error("inconsistent variables in rnfeltabstorel, z != x.")
+error("inconsistent variables in rnfeltabstorel, z != x.")
+error("inconsistent variables in rnfeltabstorel, z != x.")
+13:
+error("inconsistent moduli in rnfalgtobasis: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfeltabstorel: y^2 + 1 != x^6 - 21")
+error("inconsistent moduli in rnfeltreltoabs: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in nf_to_scalar_or_basis: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfeltdown: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfeltabstorel: y^2 + 1 != x^6 - 21")
+error("inconsistent moduli in rnfeltabstorel: y^2 + 1 != x^6 - 21")
+14:
+error("inconsistent dimensions in rnfalgtobasis.")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+15:
+[1, 2]~
+Mod(2*x + Mod(-2*y^2 + 1, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+16:
+[1, Mod(y, y^3 - 21)]~
+Mod(Mod(y, y^3 - 21)*x + Mod(-20, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+17:
+error("incorrect type in rnfalgtobasis (t_COMPLEX).")
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+18:
+error("incorrect type in rnfalgtobasis (t_POL).")
+error("incorrect type in rnfbasistoalg (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+error("incorrect type in rnfeltup (t_COL).")
+error("incorrect type in rnfeltup (t_COL).")
+error("incorrect type in rnfeltdown (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+19:
+[y, 0]~
+Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+Mod(x^2, x^6 - 21)
+error("inconsistent variables in nf_to_scalar_or_basis, x != y.")
+Mod(y, y^3 - 21)
+Mod(2*y, y^3 - 21)
+Mod(y^2, y^3 - 21)
+1:
+x^2 - 4*x + 4
+2:
+x^2 - x + 1/4
+3:
+x^2 + Mod(-2*y, y^3 - 21)*x + Mod(y^2 - y, y^3 - 21)
+4:
+x^2 - 2*x + 1
+5:
+x^2 - x + 1/4
+6:
+x^2 + Mod(-2*y, y^3 - 21)*x + Mod(y^2, y^3 - 21)
+7:
+error("inconsistent moduli in rnfcharpoly: x^6 - 21 != y^3 - 21")
+8:
+error("inconsistent moduli in rnfcharpoly: x^6 - 21 != y^3 - 21")
+9:
+error("inconsistent moduli in rnfcharpoly: x^6 - 21 != y^3 - 21")
+10:
+x^2 + Mod(-y, y^3 - 21)*x + Mod(1/4*y^2 - y, y^3 - 21)
+11:
+x^2 + Mod(-2*y, y^3 - 21)*x + Mod(y^2, y^3 - 21)
+12:
+error("incorrect priority in rnfcharpoly: variable z >= y")
+13:
+error("inconsistent moduli in rnfcharpoly: y^2 + 1 != y^3 - 21")
+14:
+error("incorrect type in rnfcharpoly (t_COL).")
+15:
+error("incorrect type in rnfcharpoly (t_COL).")
+16:
+error("incorrect type in rnfcharpoly (t_COL).")
+17:
+error("incorrect type in rnfcharpoly (t_COL).")
+18:
+error("incorrect type in rnfcharpoly (t_POL).")
+19:
+x^2 + Mod(-2*y, y^3 - 21)*x + Mod(y^2, y^3 - 21)
+1:
+[2, 0]~
+Mod(2, x^2 + 1)
+2
+2
+2
+2
+4
+4
+2:
+[1/2, 0]~
+Mod(1/2, x^2 + 1)
+1/2
+1/2
+1/2
+1/2
+1
+1/4
+3:
+[-1, 1]~
+Mod(x - 1, x^2 + 1)
+error("incorrect type in rnfeltabstorel (t_POL).")
+Mod(x - 2, x^2 - 2*x + 2)
+error("inconsistent variables in nf_to_scalar_or_basis, x != y.")
+error("incorrect type in rnfeltabstorel (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+error("incorrect type in rnfeltabstorel (t_POL).")
+4:
+[1/2, 0]~
+Mod(1/2, x^2 + 1)
+Mod(1/2, x^2 + 1)
+Mod(1/2, x^2 - 2*x + 2)
+1/2
+1/2
+1
+1/4
+5:
+[Mod(y, y + 1), 0]~
+Mod(Mod(-1, y + 1), x^2 + 1)
+Mod(-1, x^2 + 1)
+-1
+-1
+-1
+-2
+1
+6:
+[1, 1/2]~
+Mod(1/2*x + 1, x^2 + 1)
+Mod(1/2*x + 1, x^2 + 1)
+Mod(1/2*x + 1/2, x^2 - 2*x + 2)
+error("inconsistent moduli in nf_to_scalar_or_basis: x^2 + 1 != y + 1")
+error("domain error in rnfeltdown: element not in the base field")
+2
+5/4
+7:
+[0, 1]~
+Mod(x, x^2 + 1)
+Mod(x, x^2 + 1)
+Mod(x - 1, x^2 - 2*x + 2)
+error("inconsistent moduli in nf_to_scalar_or_basis: x^2 + 1 != y + 1")
+error("domain error in rnfeltdown: element not in the base field")
+0
+1
+8:
+error("inconsistent moduli in rnfalgtobasis: x^2 - 2*x + 2 != y + 1")
+error("inconsistent moduli in rnfbasistoalg: x^2 - 2*x + 2 != y + 1")
+Mod(x + 1, x^2 + 1)
+Mod(x, x^2 - 2*x + 2)
+Mod(x, x^2 - 2*x + 2)
+error("domain error in rnfeltdown: element not in the base field")
+2
+2
+9:
+error("inconsistent moduli in rnfalgtobasis: x^2 - y != y + 1")
+error("inconsistent moduli in rnfbasistoalg: x^2 - y != y + 1")
+error("inconsistent moduli in rnfeltabstorel: x^2 - y != x^2 - 2*x + 2")
+error("inconsistent moduli in rnfeltreltoabs: x^2 - y != y + 1")
+error("inconsistent moduli in nf_to_scalar_or_basis: x^2 - y != y + 1")
+error("inconsistent moduli in rnfeltdown: x^2 - y != y + 1")
+error("inconsistent moduli in rnfeltabstorel: x^2 - y != x^2 - 2*x + 2")
+error("inconsistent moduli in rnfeltabstorel: x^2 - y != x^2 - 2*x + 2")
+10:
+[0, 1]~
+Mod(x, x^2 + 1)
+Mod(x + 1, x^2 + 1)
+Mod(x - 1, x^2 - 2*x + 2)
+error("inconsistent variables in nf_to_scalar_or_basis, x != y.")
+error("domain error in rnfeltdown: element not in the base field")
+2
+2
+11:
+error("inconsistent dimensions in rnfalgtobasis.")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+1
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+12:
+[1, 2]~
+Mod(2*x + 1, x^2 + 1)
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+13:
+error("inconsistent dimensions in rnfalgtobasis.")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltreltoabs (t_COL).")
+Mod(y, x^2 - 2*x + 2)
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+error("incorrect type in rnfeltabstorel (t_COL).")
+1:
+x^2 - 4*x + 4
+2:
+x^2 - x + 1/4
+3:
+x^2 + 2*x + 2
+4:
+x^2 - x + 1/4
+5:
+x^2 + 2*x + 1
+6:
+x^2 - 2*x + 5/4
+7:
+x^2 + 1
+8:
+error("inconsistent moduli in rnfcharpoly: x^2 - 2*x + 2 != y + 1")
+9:
+error("inconsistent moduli in rnfcharpoly: x^2 - y != y + 1")
+10:
+x^2 + 1
+11:
+error("incorrect type in rnfcharpoly (t_COL).")
+12:
+error("incorrect type in rnfcharpoly (t_COL).")
+13:
+error("incorrect type in rnfcharpoly (t_COL).")
+Mod(1, x^2 - 2)
+0
+[[1, -66328; 0, 1], [[280970, 12259, 35869; 0, 1, 0; 0, 0, 1], [1, 1/2, 1/2;
+ 0, 1/2, 0; 0, 0, 1/2]]]
+[280970, x^2 + 12259, x^4 + 35869, -x^4 + x - 66328, -1/2*x^4 + 1/2*x^3 - 33
+164*x^2 + 1/2*x - 66349/2, 1/2*x^5 - 66329/2*x^4 - 21/2*x^2 + 1/2*x - 33164]
+1
+[18416, x^2 + 6979, x^4 + 3879, -9208*x^4 + 9208*x, -6979/2*x^4 + 1/2*x^3 + 
+6979/2*x - 21/2, 1/2*x^5 - 3879/2*x^4 - 21/2*x^2 + 3879/2*x]
+1
+
+[280970 12259 35869]
+
+[     0     1     0]
+
+[     0     0     1]
+
+[280970, x^2 + 12259, x^4 + 35869, -140485*x^4 + 140485*x, -12259/2*x^4 + 1/
+2*x^3 + 12259/2*x - 21/2, 1/2*x^5 - 35869/2*x^4 - 21/2*x^2 + 35869/2*x]
+1:
+[[1, 0; 0, 1], [2, 1]]
+[2, 2*x^2, 2*x^4, -x^4 + x, x^3 - 21, x^5 - 21*x^2]
+error("incorrect type in rnfidealabstorel (t_INT).")
+2
+[2, 2*x^2, 2*x^4, -x^4 + x, x^3 - 21, x^5 - 21*x^2]
+[4, 0, 0; 0, 4, 0; 0, 0, 4]
+64
+[2, 0]
+2:
+[[1, 0; 0, 1], [1/2, 1/4]]
+[1/2, 1/2*x^2, 1/2*x^4, -1/4*x^4 + 1/4*x, 1/4*x^3 - 21/4, 1/4*x^5 - 21/4*x^2
+]
+error("incorrect type in rnfidealabstorel (t_FRAC).")
+1/2
+[1/2, 1/2*x^2, 1/2*x^4, -1/4*x^4 + 1/4*x, 1/4*x^3 - 21/4, 1/4*x^5 - 21/4*x^2
+]
+[1/4, 0, 0; 0, 1/4, 0; 0, 0, 1/4]
+1/64
+[1/2, 0]
+3:
+[[1, 42; 0, 1], [[210, 189, 189; 0, 1, 0; 0, 0, 1], [1, 1/2, 1/2; 0, 1/2, 0;
+ 0, 0, 1/2]]]
+[210, x^2 + 189, x^4 + 189, -x^4 + x + 42, -1/2*x^4 + 1/2*x^3 + 21*x^2 + 1/2
+*x + 21/2, 1/2*x^5 + 41/2*x^4 - 21/2*x^2 + 1/2*x + 21]
+error("incorrect type in rnfidealabstorel (t_POL).")
+[210, 189, 189; 0, 1, 0; 0, 0, 1]
+error("inconsistent variables in nf_to_scalar_or_basis, x != y.")
+[420, 399, 399; 0, 1, 0; 0, 0, 1]
+420
+[210, Mod(x + Mod(-105*y - 84, y^3 - 21), x^2 + Mod(-y, y^3 - 21))]
+4:
+[[1, 0; 0, 1], [1, 1/2]]
+[1, x^2, x^4, -1/2*x^4 + 1/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+1
+[1, x^2, x^4, -1/2*x^4 + 1/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+1
+[1, 0]
+5:
+[[1, 0; 0, 1], [1/2, 1/4]]
+[1/2, 1/2*x^2, 1/2*x^4, -1/4*x^4 + 1/4*x, 1/4*x^3 - 21/4, 1/4*x^5 - 21/4*x^2
+]
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+1/2
+[1/2, 1/2*x^2, 1/2*x^4, -1/4*x^4 + 1/4*x, 1/4*x^3 - 21/4, 1/4*x^5 - 21/4*x^2
+]
+[1/4, 0, 0; 0, 1/4, 0; 0, 0, 1/4]
+1/64
+[1/2, 0]
+6:
+[[1, 0; 0, 1], [[21, 0, 0; 0, 1, 0; 0, 0, 1], [21/2, 0, 0; 0, 1/2, 0; 0, 0, 
+1/2]]]
+[21, x^2, x^4, -21/2*x^4 + 21/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+[21, 0, 0; 0, 1, 0; 0, 0, 1]
+[21, x^2, x^4, -21/2*x^4 + 21/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+[21, 0, 0; 0, 21, 0; 0, 0, 1]
+441
+[21, Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))]
+7:
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in nf_to_scalar_or_basis: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+8:
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in nf_to_scalar_or_basis: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+9:
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in nf_to_scalar_or_basis: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: x^6 - 21 != y^3 - 21")
+10:
+[[1, -756; 0, 1], [[903/2, 84, 336; 0, 1/2, 0; 0, 0, 1/2], 1/4]]
+[903/2, 1/2*x^2 + 84, 1/2*x^4 + 336, -1/4*x^4 + 1/4*x - 189, 1/4*x^3 - 189*x
+^2 - 21/4, 1/4*x^5 - 189*x^4 - 21/4*x^2]
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+[903/2, 84, 336; 0, 1/2, 0; 0, 0, 1/2]
+error("inconsistent moduli in nf_to_scalar_or_basis: x^2 - y != y^3 - 21")
+[903/4, 42, 168; 0, 1/4, 0; 0, 0, 1/4]
+903/64
+[903/2, Mod(1/2*x - 42, x^2 + Mod(-y, y^3 - 21))]
+11:
+[[1, 0; 0, 1], [[21, 0, 0; 0, 1, 0; 0, 0, 1], [21/2, 0, 0; 0, 1/2, 0; 0, 0, 
+1/2]]]
+[21, x^2, x^4, -21/2*x^4 + 21/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+error("incorrect type in rnfidealabstorel (t_POL).")
+[21, 0, 0; 0, 1, 0; 0, 0, 1]
+[21, x^2, x^4, -21/2*x^4 + 21/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+[21, 0, 0; 0, 21, 0; 0, 0, 1]
+441
+[21, Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))]
+12:
+error("inconsistent variables in rnfbasistoalg, z != x.")
+error("inconsistent variables in rnfbasistoalg, z != x.")
+error("incorrect type in rnfidealabstorel (t_POL).")
+error("inconsistent variables in rnfbasistoalg, z != x.")
+error("inconsistent variables in nf_to_scalar_or_basis, z != y.")
+error("inconsistent variables in rnfbasistoalg, z != x.")
+error("inconsistent variables in rnfbasistoalg, z != x.")
+error("inconsistent variables in rnfbasistoalg, z != x.")
+13:
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+error("incorrect type in rnfidealabstorel (t_POLMOD).")
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in nf_to_scalar_or_basis: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+error("inconsistent moduli in rnfbasistoalg: y^2 + 1 != y^3 - 21")
+14:
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("incorrect type in rnfidealabstorel (t_COL).")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (2 elts) , t_COL (1 elts).
+")
+15:
+[[1, -5371968; 0, 1], [[10743937, 8099895, 3524829; 0, 1, 0; 0, 0, 1], 1/2]]
+[10743937, x^2 + 8099895, x^4 + 3524829, -1/2*x^4 + 1/2*x - 2685984, 1/2*x^3
+ - 2685984*x^2 - 21/2, 1/2*x^5 - 2685984*x^4 - 21/2*x^2]
+error("incorrect type in rnfidealabstorel (t_COL).")
+[10743937, 8099895, 3524829; 0, 1, 0; 0, 0, 1]
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+[10743937, 8099895, 3524829; 0, 1, 0; 0, 0, 1]
+10743937
+[10743937, Mod(x - 1847139, x^2 + Mod(-y, y^3 - 21))]
+16:
+[[1, [0, 0, -18]~; 0, 1], [379, 1/2]]
+[379, 379*x^2, 379*x^4, -19/2*x^4 + 1/2*x, 1/2*x^3 - 399/2, 1/2*x^5 - 399/2*
+x^2]
+error("incorrect type in rnfidealabstorel (t_COL).")
+379
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+[379, 0, 0; 0, 379, 0; 0, 0, 379]
+54439939
+[379, Mod(Mod(1/2*y, y^3 - 21)*x + 359/2, x^2 + Mod(-y, y^3 - 21))]
+17:
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+error("incorrect type in rnfidealabstorel (t_COL).")
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+error("incorrect type in nf_to_scalar_or_basis (t_COL).")
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+error("incorrect type in nf_to_scalar_or_alg (t_COMPLEX).")
+18:
+error("incorrect type in rnfbasistoalg (t_POL).")
+error("incorrect type in rnfbasistoalg (t_POL).")
+error("incorrect type in rnfidealabstorel (t_POL).")
+error("incorrect type in rnfbasistoalg (t_POL).")
+error("incorrect type in idealhnf [integer vector] (t_COL).")
+error("incorrect type in rnfbasistoalg (t_POL).")
+error("incorrect type in rnfbasistoalg (t_POL).")
+error("incorrect type in rnfbasistoalg (t_POL).")
+19:
+[[1, 0; 0, 1], [[21, 0, 0; 0, 1, 0; 0, 0, 1], [21/2, 0, 0; 0, 1/2, 0; 0, 0, 
+1/2]]]
+[21, x^2, x^4, -21/2*x^4 + 21/2*x, 1/2*x^3 - 21/2, 1/2*x^5 - 21/2*x^2]
+error("incorrect type in rnfidealabstorel (t_POL).")
+[21, 0, 0; 0, 1, 0; 0, 0, 1]
+error("inconsistent variables in nf_to_scalar_or_basis, x != y.")
+[21, 0, 0; 0, 21, 0; 0, 0, 1]
+441
+[21, Mod(Mod(y, y^3 - 21), x^2 + Mod(-y, y^3 - 21))]
+20:
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealabstorel (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+[]
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+21:
+[[;], []]
+[]
+[[;], []]
+[;]
+error("incorrect type in idealtyp (t_VEC).")
+[;]
+0
+[0, 0]
+22:
+[[;], []]
+[]
+error("incorrect type in rnfidealabstorel (t_INT).")
+[;]
+[]
+[;]
+0
+[0, 0]
+23:
+[[;], []]
+[]
+error("inconsistent dimensions in rnfidealabstorel.")
+[;]
+[]
+[;]
+0
+[0, 0]
+24:
+error("inconsistent dimensions in rnfidealabstorel.")
+error("inconsistent dimensions in rnfidealabstorel.")
+error("inconsistent dimensions in rnfidealabstorel.")
+error("inconsistent dimensions in rnfidealabstorel.")
+[2, x^2 + 1, x^4 + 1, -x^4 + x, -1/2*x^4 + 1/2*x^3 + 1/2*x - 21/2, 1/2*x^5 -
+ 1/2*x^4 - 21/2*x^2 + 1/2*x]
+error("inconsistent dimensions in rnfidealabstorel.")
+error("inconsistent dimensions in rnfidealabstorel.")
+error("inconsistent dimensions in rnfidealabstorel.")
+25:
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealabstorel (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+[18416, x^2 + 6979, x^4 + 3879, -9208*x^4 + 9208*x, -6979/2*x^4 + 1/2*x^3 + 
+6979/2*x - 21/2, 1/2*x^5 - 3879/2*x^4 - 21/2*x^2 + 3879/2*x]
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+26:
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealabstorel (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (3 elts) , t_COL (6 elts).
+")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+error("incorrect type in rnfidealhnf (t_MAT).")
+27:
+[[1, -66328; 0, 1], [[280970, 12259, 35869; 0, 1, 0; 0, 0, 1], [1, 1/2, 1/2;
+ 0, 1/2, 0; 0, 0, 1/2]]]
+[280970, x^2 + 12259, x^4 + 35869, -x^4 + x - 66328, -1/2*x^4 + 1/2*x^3 - 33
+164*x^2 + 1/2*x - 66349/2, 1/2*x^5 - 66329/2*x^4 - 21/2*x^2 + 1/2*x - 33164]
+[[1, -66328; 0, 1], [[280970, 12259, 35869; 0, 1, 0; 0, 0, 1], [1, 1/2, 1/2;
+ 0, 1/2, 0; 0, 0, 1/2]]]
+[280970, 12259, 35869; 0, 1, 0; 0, 0, 1]
+error("incorrect type in idealtyp (t_VEC).")
+[561940, 12259, 316839; 0, 1, 0; 0, 0, 1]
+561940
+[280970, Mod(x + Mod(-140485*y + 110026, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+]
+28:
+[[1, -66328; 0, 1], [[280970, 12259, 35869; 0, 1, 0; 0, 0, 1], [1, 1/2, 1/2;
+ 0, 1/2, 0; 0, 0, 1/2]]]
+[280970, x^2 + 12259, x^4 + 35869, -x^4 + x - 66328, -1/2*x^4 + 1/2*x^3 - 33
+164*x^2 + 1/2*x - 66349/2, 1/2*x^5 - 66329/2*x^4 - 21/2*x^2 + 1/2*x - 33164]
+error("inconsistent dimensions in rnfidealabstorel.")
+[280970, 12259, 35869; 0, 1, 0; 0, 0, 1]
+error("inconsistent operation 'RgV_RgC_mul' t_VEC (3 elts) , t_COL (2 elts).
+")
+[561940, 12259, 316839; 0, 1, 0; 0, 0, 1]
+561940
+[280970, Mod(x + Mod(-140485*y + 110026, y^3 - 21), x^2 + Mod(-y, y^3 - 21))
+]
+[[;], []]
+[[;], []]
+[[1, 0; 0, 1], [[21, 0, 0; 0, 21, 0; 0, 0, 1], [21/2, 0, 0; 0, 1/2, 0; 0, 0,
+ 1/2]]]
+[[1, 0; 0, 1], [[21, 0, 0; 0, 21, 0; 0, 0, 1], [21/2, 0, 0; 0, 1/2, 0; 0, 0,
+ 1/2]]]
+[[1, -347298; 0, 1], [[5900370, 2821959, 5374299; 0, 1, 0; 0, 0, 1], [1, 1/2
+, 1/2; 0, 1/2, 0; 0, 0, 1/2]]]
+[[1, -347298; 0, 1], [[5900370, 2821959, 5374299; 0, 1, 0; 0, 0, 1], [1, 1/2
+, 1/2; 0, 1/2, 0; 0, 0, 1/2]]]
+  ***   at top-level: rnfdet(L,[[;],[]])
+  ***                 ^------------------
+  *** rnfdet: incorrect type in checknf [please apply nfinit()] (t_VEC).
+
+[1 0 0]
+
+[0 1 0]
+
+[0 0 1]
+
+
+[280970 12259/2 316839/2]
+
+[     0     1/2        0]
+
+[     0       0      1/2]
+
+
+[1 -1 [-1/2, -1/2]~]
+
+[0  1   [1/2, 1/2]~]
+
+1
+  ***   at top-level: rnfidealtwoelt(L,[[1
+  ***                 ^--------------------
+  *** rnfidealtwoelt: incorrect type in idealtyp [non-square t_MAT] (t_MAT).
+Total time spent: 356
diff --git a/src/test/32/rnfkummer b/src/test/32/rnfkummer
new file mode 100644
index 0000000..06ce962
--- /dev/null
+++ b/src/test/32/rnfkummer
@@ -0,0 +1,174 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+1
+x^3 + (774806212396682040403800911285727580902360710356054423617*y^3 + 97150
+6686017039131003298525517527246921922994336799568156*y^2 + 11821565693684429
+03321920531164159182095127947357116914844*y - 108151434729309365356471406894
+6874997341102881639770950564)*x + (-1101081691006188641718571980971003086516
+33373991261854661897473896921309979584250845786*y^3 - 1380613897447424014891
+54687510692802021389570171865180798324014776069968837855846155006*y^2 - 1679
+9696925608361694809806777136478453179953197918860855068694876240580160263314
+5257038*y + 1536946435524002857971775575314758204502648805624233135296867674
+44129838100365080190135)
+2
+x^3 + (19710886507130683637499481853346220*y^3 - 276380637484283870500207144
+441368652*y^2 + 321806883259334223558938029085301164*y - 2893764851921508720
+91555008989630209)*x + (6234404200069914092635781978921534907087800821965005
+*y^3 - 87417103564909472118499724747251035776212296637996160*y^2 + 101785081
+248253577808797714025930280347744436431138133*y - 91527591822456102976671051
+455070193926146359966708289)
+3
+x^5 + (-9882428445923387818821706968670335401786927402638328900/229919*y^3 +
+ 252296777028239459931789167380697746461097731168075549950/229919*y^2 + 1764
+4021855250586329539712931017927391863593427164909150900/229919*y - 461692355
+191971518777222710539906813016286270054704580073485/229919)*x^3 + (520648303
+4334319898424292755071689636089386050580211646308033020493437324042023205648
+285918215543231297402900/229919*y^3 - 13510172122026074216086588070702609589
+3274800593243490875856383565886434411548352697079275501817626818578211700/22
+9919*y^2 - 91473485080232913616389746227405125029881179747504594125555560364
+17693246367183353048197363570304603875632855400/229919*y + 24083884821528790
+5870876643297480712506722062627721245971248179841780526788214041708631271723
+314318325272663341055/229919)*x + (78137203667601283191063741412847022062641
+2553304434052564901727553710334163982444928868366386245031074040191710705188
+597170075308725360/229919*y^3 - 20291832477897260584400088627142955032228712
+6427904642210421816300635730915867663743323382945939621991147280148116769004
+38451037873749030/229919*y^2 - 137170262514990797441175521438442862270311264
+3393460621755229264686711842372711647632765203362009673941201960909433758980
+882672771455560640/229919*y + 3612647630201497335124541722355310659164687014
+5370282645038587200821787637136651857253746125088350772872898101762266295117
+812934962971329040/229919)
+4
+x^5 + 110*x^3 + 385*x^2 + (-6875*y + 13310)*x + (-20625*y - 32197)
+5
+x^5 + (-59095446785579003366541009942375474915703002626492806189050492652833
+6777227549063361763754954860856971852727711644600186470988132579883875090572
+9803079525882763972042802336339760904754466580963063546728664273633835569948
+0*y - 1255629109524792530051324640019771107268873725738371437452373149898780
+8664516003920392766770766925545374769735534821188976309229469036602133358973
+7353191296177407075389420915446219197172794981974368679535268516967068453014
+30)*x^3 + (11644277774384555651776761259407289108977097641980228242435012611
+5006754047681181999487760953138546648235722021492042022118650990530538252856
+6414026821914198835648283336487147011301797121610470649794178541651897633010
+2619047628371992250175584734031123181309438679458163454790544342192453573731
+030635180622494122125396065225820240750*y + 24741151693056889254242449430265
+2468571687763455720080168193011803383803554198165726270992078221951492505067
+2523425192260651173461424746524891653990998558483368796194367290039509450211
+7223320966363646799929781716117510559483376953872459496427621808761687440880
+10277314407796992274304485160518076623273185053816747431170532802842069845)*
+x^2 + (320866938295880246233167260001000107563618762621372357179787452301597
+9350243325972757280812393253232955595814724357682764593258584468527774061354
+2242378888385866623543414807804481384620950782988555853437885938726285585062
+2324974444011130041770207214081732874802346406968000065586950875225394719188
+4623518822273224739400356095176344365863179853117653552348695220926538716085
+52583923034075591677276229656463785917547906153809856186338690340273770*y + 
+6817612691384532081704360643291649127584439227042117746627599451584982487957
+6572901637848129335693848481338746129646962173434343962797786879758126909137
+7381569437746730995649270075916763692439787747952720962070291143876149949483
+9614210516670824112990967457708754073489807333741020159222761044037864938737
+9246261520429224755935851613098789923520029380140527313267008357869036620577
+084037264149183512006735786452064658360584468340747599718504718410)*x + (479
+1488386897535189299378836180236085423634678222324224479062728075805982822437
+0614332128649486567242879024330122834178040516179680619153613540616585806037
+7183008624149304402927672945839064441661929200188706502079385514833093457024
+5806768755451682906974568908692395923842934118138666558754879374484134729144
+9868938565283781573780271601168423137627916189740121447280291767296950672317
+2653182572204989153504589598280507795775397905066604615953915755995460867052
+8060987075566530764495079483852000046629727066859504551785397092661954246711
+193183042460393012*y + 10180703630802729839362382111928571606603034484770558
+5306817525550772935039363481339905081141074846357682616458160033371105154149
+6119838532365651143544327472969299425232951428413020731520165630201894232969
+6028437849118197154430955186252049741741678410724900967414872905752188199744
+8965131243275080049232949513675900438219816309709252600957185206156348098304
+0110963425335258732186906609451092433156633139140137874710419209044386833785
+3831519981280988311756942110207862081145784700793692327274548446746868893274
+79033237457477153740386126551265094681046611167)
+6
+x^5 + 1497313810*x^3 - 153069990594216277067332440*x^2 - 9993317827227414657
+08504771613701347765415*x + 421302153697978557238622899127897637801303537233
+83633556328
+7
+x^3 + (6*y^3 - 12*y^2 + 12*y - 12)*x + (12*y^5 - 32*y^4 + 54*y^3 - 32*y^2 + 
+28*y + 20)
+8
+x^5 + (677308732982301944730030845266716201702837696162187325328/5*y^3 + 481
+271319660811460139352501916644558975058261230836596652/5*y^2 - 3487807973304
+8511269299413306183851642254427026365686673888/5*y - 24783137893632185532697
+291886880720222932490781434136563942/5)*x^3 + (31218254245856135648287296707
+04559492032765858876683224121119983801219547643890505070*y^3 + 2218259473949
+490736667831079912338956828105689590705243343986337391928943383709808005*y^2
+ - 1607587048361124753491934721194326369713341397761485813643339582894344828
+06877560648970*y - 114229487928172801039731551289054638597376141715926648946
+031483976252167029243247022480)*x^2 + (-166692999053535356002572927874917139
+2118748717302099608304683989705975477021372381764147057643564706588636619348
+44/5*y^3 - 11844618891597190508741280303272659573039434424283175367673479155
+0346393669679117451359918013807557809143042588696/5*y^2 + 858387225015985285
+7479162156889677342550132950704205078118190632176525884150800135006242479797
+595396229858998666474/5*y + 609939805478110246004601234032243326487160080269
+1098946100231203424708988718659688581036467351459717869466864403491/5)*x + (
+-376194285951669846001777157888755029972864354842521262364492544230511805129
+9870709569967640457662697840860989973276516003636843449764721490044/5*y^3 - 
+2673104432455463952284352656647109245231103663592899118247897745294025980581
+570350460813923435311458682114748384710825808171997155877533243621/5*y^2 + 1
+9372161459591757660716692723884954611407620247350322071070721355639658608496
+4322202670927424345172110316287310450443586757567876756951302594424/5*y + 13
+7651773558650909762864388498297831958827163349141150095186154867116600261791
+580542516653158613689676015829782898702530873665348851222173825666/5)
+9
+x^3 + (-2138833799099964*y - 77766538091057475)*x + (28996816968231072456572
+6*y + 10543044869724218021876124)
+10
+[x^3 + (-2138833799099964*y - 77766538091057475)*x + (1851372044880720815279
+5*y + 673146247780080575068281), x^3 + (-2138833799099964*y - 77766538091057
+475)*x + (289968169682310724565726*y + 10543044869724218021876124), x^3 + (-
+2138833799099964*y - 77766538091057475)*x + (-271454449233503516412931*y - 9
+869898621944137446807843), x^3 - 3*x - 1]
+11
+x^5 - 10*x^3 + 20*x + 10
+12
+x^5 + (14433507922528796322450466885842559554649240/81*y^3 - 206924153900250
+190478940045819169994648120380/81*y^2 + 387221873734866129008668004845146809
+3411480/27*y + 9952907878012919545305461812329973812202320)*x^3 + (151213742
+1515820547988557267937916762553254164737251785897692300/27*y^3 + 18337121232
+6689306422734140437601099700562333545199409203074500/27*y^2 - 11743163565184
+2472537762690188912056014010181849092684109181910170/9*y + 27790231583332542
+847266778828385473838935385907433291290641467310)*x^2 + (-226693826937254370
+3014068118233450711618653187091810917531287989910268281950806313112830/81*y^
+3 + 239315207598088678889180580299531524598499432218877226931937677230073476
+51161135616414460/81*y^2 + 4557474129212450957128695360991332089101354211319
+1870218073887288396098516272850951573480/27*y - 4785071374094015662869316915
+362187463934517274549880463316992141982057530013804207289130)*x + (119270482
+8439928261324666320105890752409692593927875083779153072015384025578913692346
+3989511892271143598294/3*y^3 - 152961475704026365347150203105055935481447020
+419907822345352843160917118838486063790065390094212607259586902/3*y^2 - 9397
+8480302010002477264842168576217064771866107133401243553275407905590291763780
+391464292290112011298779372*y + 40510745442141622784634137179518469588738855
+3485545336064626090438935556037611475681610251702629961491235364)
+13
+x^5 + (332998327585787806210715462400*y - 3088099449668699468241894745310)*x
+^3 + (494543408306584524066234839600325678760597930*y - 45862068980974258959
+41238867752143070004508050)*x^2 + (26417254338273940545860942707750465903049
+7550267984532302200*y - 2449835384316313335292893982961063446219951623920034
+753322415)*x + (476291033688128034677862637907914049204992323838153758931557
+45202346081966*y - 441694133924899145700575613631246753719401915993262186949
+236663342183626850)
+14
+x^2 + (-246740429132141146463522518444668570440348378975804*y^5 + 4703956945
+787111585543964420667028301009723801572487*y^4 + 266190357187628389772307989
+7863774384599266481260032*y^3 - 39177999595004651251370478833094268643601230
+78018159*y^2 + 10590083476362079496920522735326808810513545604444961*y - 584
+8192501453958323706561054990087672915722280908328)
+15
+x^2 + (-1/2*y^5 - 9/2*y^4 - y^3 - 3/2*y^2 - 3/2*y + 3)
+16
+x^2 + (7064114974621884209263957684*y^5 - 78597289737331354515763478580*y^4 
++ 13905399960467092094726011945*y^3 - 27789415584376787513088098359*y^2 + 26
+854231387417039826858692319*y - 60884996981015832433185005956)
+17
+x^2 + (-8*y - 101)
+18
+x^2 - 5
+19
+x^3 + (-3*y^3 - 141/2*y^2 + 921*y - 6963/2)*x + (49621/3*y^3 - 547595/6*y^2 
+- 619772/3*y + 15840415/6)
+20
+[x^3 - 3*x - 1]
+Total time spent: 15853
diff --git a/src/test/32/round b/src/test/32/round
new file mode 100644
index 0000000..dd3ad79
--- /dev/null
+++ b/src/test/32/round
@@ -0,0 +1,86 @@
+1
+1
+2
+-1
+error("incorrect type in gceil (t_INTMOD).")
+error("incorrect type in gceil (t_QUAD).")
+error("incorrect type in gceil (t_POLMOD).")
+x
+error("incorrect type in gceil (t_SER).")
+0
+[2, 2]
+[1, 0; 0, 1]
+error("incorrect type in gceil (t_STR).")
+1
+1
+-2
+error("incorrect type in gfloor (t_INTMOD).")
+error("incorrect type in gfloor (t_QUAD).")
+error("incorrect type in gfloor (t_POLMOD).")
+x
+error("incorrect type in gfloor (t_SER).")
+0
+[1, 2]
+[0, 0; 0, 0]
+error("incorrect type in gfloor (t_STR).")
+0
+0.60000000000000000000000000000000000000
+2/3
+error("incorrect type in gfloor (t_INTMOD).")
+error("incorrect type in gfloor (t_QUAD).")
+error("incorrect type in gfloor (t_POLMOD).")
+0
+error("incorrect type in gfloor (t_SER).")
+1/x
+[1/3, 0]
+[1/3, 0; 0, 1/3]
+error("incorrect type in gfloor (t_STR).")
+1
+2
+-1
+Mod(1, 3)
+w
+Mod(x + 1, x^2 + 1/2)
+x
+1 + O(x^2)
+1/x
+[1, 2]
+[0, 0; 0, 0]
+error("incorrect type in ground (t_STR).")
+[1, -oo]
+[2, -2]
+[-1, -oo]
+[Mod(1, 3), -oo]
+[w, -oo]
+[Mod(x + 1, x^2 + 1/2), -oo]
+[x, -oo]
+[1 + O(x^2), -oo]
+[1/x, -oo]
+[[1, 2], -oo]
+[[0, 0; 0, 0], -oo]
+error("incorrect type in grndtoi (t_STR).")
+1
+1
+-1
+error("incorrect type in gtrunc (t_INTMOD).")
+error("incorrect type in gtrunc (t_QUAD).")
+error("incorrect type in gtrunc (t_POLMOD).")
+x
+1/3*x + 4/3
+0
+[1, 2]
+[0, 0; 0, 0]
+error("incorrect type in gtrunc (t_STR).")
+[1, -oo]
+[1, -1]
+[-1, -oo]
+error("incorrect type in gtrunc (t_INTMOD).")
+error("incorrect type in gtrunc (t_QUAD).")
+error("incorrect type in gtrunc (t_POLMOD).")
+[x, -oo]
+[1/3*x + 4/3, -oo]
+[0, -oo]
+[[1, 2], -oo]
+[[0, 0; 0, 0], -oo]
+error("incorrect type in gtrunc (t_STR).")
+Total time spent: 4
diff --git a/src/test/32/round4 b/src/test/32/round4
new file mode 100644
index 0000000..f9a8c11
--- /dev/null
+++ b/src/test/32/round4
@@ -0,0 +1,462 @@
+  ***   Warning: new stack size = 10000000 (9.537 Mbytes).
+1	disc = 691051969765063543658256000000000000
+2	disc = 31088519960728128454656000000000000
+3	disc = 1802370734536
+4	disc = 654752768
+5	disc = -58773124044
+6	disc = 11378694989371408384
+7	disc = -48269476692516748870958029675063944413184
+8	disc = 1461501637330902918203684832716283019655932542976
+9	disc = 1346286087882789617664
+10	disc = 193428131138340667952988160000000000000000
+11	disc = 11378694989371408384
+12	disc = 144
+13	disc = 442050625
+14	disc = 12008989
+15	disc = 16974593
+16	disc = 31554496
+17	disc = 33076161
+18	disc = 11574317056
+19	disc = 10368641602001
+20	disc = 39213900625
+21	disc = 103161709
+22	disc = 105823817
+23	disc = 65037750625
+24	disc = 73116160000
+25	disc = 183250432
+26	disc = 21292697885552828353
+27	disc = 213813760000
+28	disc = 225360027841
+29	disc = 114655968874330129
+30	disc = 393832837
+31	disc = 440711081
+32	disc = 364488705441
+33	disc = 370150560000
+34	disc = 234001122366390625
+35	disc = 395451064801
+36	disc = 364007458703857
+37	disc = 497871360000
+38	disc = 588865925376
+39	disc = 709732288
+40	disc = 647395642881
+41	disc = 659020863601
+42	disc = 446012924483368791310336
+43	disc = 670801950625
+44	disc = 728933458176
+45	disc = 689869781056000000
+46	disc = 913308254830140625
+47	disc = 979146657
+48	disc = 1064726745878753869969
+49	disc = 1048772096
+50	disc = 1082432160000
+51	disc = 1192518600625
+52	disc = 1559914552888693
+53	disc = 1442919878656
+54	disc = 1334633301
+55	disc = 1490902050625
+56	disc = 2980200459393400813138329769
+57	disc = 1718786550625
+58	disc = 1810639360000
+59	disc = 1835743170816
+60	disc = 2222606887281
+61	disc = 1856331989
+62	disc = 1986121593
+63	disc = 2752095195136
+64	disc = 2786442301696
+65	disc = 2217342464
+66	disc = 2972069112961
+67	disc = 3035957760000
+68	disc = 5920163568466890625
+69	disc = 3471607400625
+70	disc = 2588282117
+71	disc = 7027753833487138816
+72	disc = 5245121853990193
+73	disc = 5958832035878149
+74	disc = 2961169856
+75	disc = 4797852160000
+76	disc = 3301293169
+77	disc = 3436115229
+78	disc = 5922408960000
+79	disc = 42098158229810084367630336
+80	disc = 26961206542479413771201
+81	disc = 7233948160000
+82	disc = 11899888859544201
+83	disc = 7909194404241
+84	disc = 24827168863016095744
+85	disc = 9082363580416
+86	disc = 9272177250625
+87	disc = 9421854806016
+88	disc = 16696389126347776
+89	disc = 52518884338097360421921
+90	disc = 30232096024977015625
+91	disc = 5564051648
+92	disc = 10971993760000
+93	disc = 12625407900625
+94	disc = 24566124836069257
+95	disc = 6869835701
+96	disc = 7177888089
+97	disc = 13932481925376
+98	disc = 52817485810944986209
+99	disc = 7495014493
+100	disc = 31681277442558976
+101	disc = 71415925156555415437890625
+102	disc = 9166361760000
+103	disc = 103063708057575099687890625
+104	disc = 17471883970840462300304775614373553
+105	disc = 95468057368868913808408576
+106	disc = 17515230173
+107	disc = 62523502209
+108	disc = 62523502209
+109	disc = 74724856128
+110	disc = 80174499328
+111	disc = 183637853504
+112	disc = 326371204125
+113	disc = 376367048000
+114	disc = 567869252041
+115	disc = 567869252041
+116	disc = 688231506789
+117	disc = 899470488077
+118	disc = 941192000000
+119	disc = 998800479936
+120	disc = 1095593933629
+121	disc = 1488181848713
+122	disc = 2565164201769
+123	disc = 2565164201769
+124	disc = 2694903759296
+125	disc = 2704209893568
+126	disc = 3151307486781
+127	disc = 3338010958625
+128	disc = 3471072448221
+129	disc = 4007556327104
+130	disc = 4624076296000
+131	disc = 4831094417625
+132	disc = 5534900853769
+133	disc = 5534900853769
+134	disc = 6056434988352
+135	disc = 6080389219008
+136	disc = 6636193049088
+137	disc = 7100029448000
+138	disc = 7534146860864
+139	disc = 7720128615744
+140	disc = 8606302763157
+141	disc = 9288810792261
+142	disc = 9750967476544
+143	disc = 10229618526912
+144	disc = 10367362235125
+145	disc = 15883285246353
+146	disc = 18243373078125
+147	disc = 18525482136000
+148	disc = 18753251922432
+149	disc = 20339710127073
+150	disc = 21673299838841
+151	disc = 21682629379125
+152	disc = 22454408824128
+153	disc = 22454408824128
+154	disc = 22454408824128
+155	disc = 22886952012288
+156	disc = 24907321081664
+157	disc = 24991851015053
+158	disc = 25002110044521
+159	disc = 25002110044521
+160	disc = 25032903977277
+161	disc = 27100023260653
+162	disc = 29698839199232
+163	disc = 31033149890625
+164	disc = 31033149890625
+165	disc = 31033149890625
+166	disc = 32012033131008
+167	disc = 32795655776729
+168	disc = 32795655776729
+169	disc = 32795655776729
+170	disc = 32795655776729
+171	disc = 33252674173893
+172	disc = 33823616904000
+173	disc = 35861911358833
+174	disc = 37156053182409
+175	disc = 37166075494848
+176	disc = 40509102979584
+177	disc = 45990453983552
+178	disc = 50722851083913
+179	disc = 51035911843357
+180	disc = 51895117000000
+181	disc = 51895117000000
+182	disc = 52464835537856
+183	disc = 57566111236928
+184	disc = 57566111236928
+185	disc = 64867893832000
+186	disc = 68443024217625
+187	disc = 70608870146368
+188	disc = 70608870146368
+189	disc = 70608870146368
+190	disc = 73792042960384
+191	disc = 78496212952513
+192	disc = 78496212952513
+193	disc = 78496212952513
+194	disc = 78496212952513
+195	disc = 80018416634688
+196	disc = 81272764978496
+197	disc = 83780436119093
+198	disc = 88812049936896
+199	disc = 91343875006656
+200	disc = 93113668703125
+201	disc = 95343780573504
+202	disc = 95343780573504
+203	disc = 95550545616813
+204	disc = 100856310518592
+205	disc = 100856310518592
+206	disc = 101481612657984
+207	disc = 102588675089797
+208	disc = 102792615579584
+209	disc = 104413920565969
+210	disc = 104413920565969
+211	disc = 106712258605568
+212	disc = 109627139656000
+213	disc = 109627139656000
+214	disc = 109627139656000
+215	disc = 117102420633024
+216	disc = 121935391665472
+217	disc = 124460777226752
+218	disc = 130323843000000
+219	disc = 131197182717797
+220	disc = 131623667572032
+221	disc = 142114065431625
+222	disc = 143318415538368
+223	disc = 148203857088000
+224	disc = 154888759857664
+225	disc = 155625375008069
+226	disc = 160032253665088
+227	disc = 160315392124224
+228	disc = 160315392124224
+229	disc = 160315392124224
+230	disc = 163963718650837
+231	disc = 164206490176000
+232	disc = 168855627483648
+233	disc = 172808693000000
+234	disc = 172808693000000
+235	disc = 182284263000000
+236	disc = 190983561521472
+237	disc = 190983561521472
+238	disc = 190983561521472
+239	disc = 205849293516125
+240	disc = 207390588005888
+241	disc = 219747465563625
+242	disc = 227081481823729
+243	disc = 227081481823729
+244	disc = 238374130976064
+245	disc = 238374130976064
+246	disc = 238374130976064
+247	disc = 239853467163968
+248	disc = 240421391475741
+249	disc = 244152343937501
+250	disc = 244152343937501
+251	disc = 244152343937501
+252	disc = 245832015985984
+253	disc = 245832015985984
+254	disc = 245832015985984
+255	disc = 250380544197952
+256	disc = 250380544197952
+257	disc = 250380544197952
+258	disc = 264856663448000
+259	disc = 266306899520753
+260	disc = 266953144495869
+261	disc = 268036698442176
+262	disc = 280342627195392
+263	disc = 284124652664021
+264	disc = 286295419093312
+265	disc = 286295419093312
+266	disc = 286295419093312
+267	disc = 292979858533713
+268	disc = 293019561288000
+269	disc = 301855146292441
+270	disc = 301855146292441
+271	disc = 312604448196032
+272	disc = 325603874022336
+273	disc = 343308792619261
+274	disc = 343956387524625
+275	disc = 358470842647653
+276	disc = 363887297335457
+277	disc = 370533009640625
+278	disc = 378388023667289
+279	disc = 378388023667289
+280	disc = 378388023667289
+281	disc = 378435108978368
+282	disc = 382167204596009
+283	disc = 385272136297125
+284	disc = 391853565596233
+285	disc = 407478805289353
+286	disc = 412162994888000
+287	disc = 419718684302848
+288	disc = 420610640203125
+289	disc = 427178948689297
+290	disc = 430522800097229
+291	disc = 437262761657701
+292	disc = 458528043068928
+293	disc = 461406087545417
+294	disc = 471655843734321
+295	disc = 471655843734321
+296	disc = 471710378827584
+297	disc = 471710378827584
+298	disc = 471710378827584
+299	disc = 474114086770176
+300	disc = 479184750300277
+301	disc = 492814358126272
+302	disc = 507558505272832
+303	disc = 512537788181952
+304	disc = 526672365558093
+305	disc = 531381953186973
+306	disc = 534063139779637
+307	disc = 545919434583489
+308	disc = 561188988355904
+309	disc = 561188988355904
+310	disc = 561188988355904
+311	disc = 561842388364096
+312	disc = 586376253000000
+313	disc = 586376253000000
+314	disc = 611354775822848
+315	disc = 619777755576000
+316	disc = 626277261303693
+317	disc = 632689381320192
+318	disc = 642489904555569
+319	disc = 646877982134125
+320	disc = 646877982134125
+321	disc = 646877982134125
+322	disc = 650182119827968
+323	disc = 654605112335552
+324	disc = 654605112335552
+325	disc = 654605112335552
+326	disc = 676928662294464
+327	disc = 678155219611793
+328	disc = 683425351376704
+329	disc = 687273151702249
+330	disc = 704612615071625
+331	disc = 706705122232017
+332	disc = 706705122232017
+333	disc = 706705122232017
+334	disc = 722893530076992
+335	disc = 726766684501312
+336	disc = 740652641361333
+337	disc = 743283484296704
+338	disc = 743702041351801
+339	disc = 743702041351801
+340	disc = 761874028623037
+341	disc = 761949106541056
+342	disc = 765458231098176
+343	disc = 773039298114433
+344	disc = 778002617184317
+345	disc = 810000647049313
+346	disc = 814597322969457
+347	disc = 840297038077125
+348	disc = 847557413131456
+349	disc = 847557413131456
+350	disc = 847557413131456
+351	disc = 847557413131456
+352	disc = 860329562380029
+353	disc = 873174569112000
+354	disc = 884514834431488
+355	disc = 884514834431488
+356	disc = 888834215593792
+357	disc = 898101431252641
+358	disc = 901568676645125
+359	disc = 920173609084625
+360	disc = 927543993493824
+361	disc = 936704478987584
+362	disc = 936704478987584
+363	disc = 936704478987584
+364	disc = 945000308955968
+365	disc = 945000308955968
+366	disc = 945000308955968
+367	disc = 946850453833536
+368	disc = 948500270840277
+369	disc = 965396977375537
+370	disc = 995805877256000
+371	disc = 995805877256000
+372	disc = 995805877256000
+373	disc = 4239150758955121
+374	disc = 71583777861999601
+375	disc = 154348440599505681
+376	disc = 206144025464586496
+377	disc = 498311414318121121
+378	disc = 988566915013816576
+379	disc = 1594861774888100161
+380	disc = 1920991678454430481
+381	disc = 3203072711854650625
+382	disc = 3282543960141600625
+383	disc = 3563847743378991201
+384	disc = 11629529577315932881
+385	disc = 14937144551057330176
+386	disc = 15104107144156878961
+387	disc = 18107955017924915521
+388	disc = 32757037519097735281
+389	disc = 34660765693554192481
+390	disc = 39891322034514837441
+391	disc = 47965803087841295281
+392	disc = 54568636296342159616
+393	disc = 73911569995951513281
+394	disc = 3830987530897003258893
+395	disc = 17723470954150385603125
+396	disc = 33413112917251096211093
+397	disc = 34637703462495298817024
+398	disc = 88388569691530342400000
+399	disc = 219491114456594107591949
+400	disc = 480467194090863436127232
+401	disc = 1037662435085339279821824
+402	disc = 1173200147537629968564224
+403	disc = 1288413852163540951269376
+404	disc = 1309219498202517731691949
+405	disc = 2589525908257257679485601
+406	disc = 3190278955076438566369093
+407	disc = 3227335013943818471790557
+408	disc = 3713732460841424506170368
+409	disc = 4862809470873883357637632
+410	disc = 6002643624777941817982976
+411	disc = 7126069515444754920275968
+412	disc = 12952518753342414842160584761
+413	disc = 19889530276837831068388593664
+414	disc = 33146763213552556976071497289
+415	disc = 134058271322059099333696000000
+416	disc = 143177496454836860404568609521
+417	disc = 782366492329362629572165894144
+418	disc = 885679687572037927988717116729
+419	disc = 1932285652221352905364571537408
+420	disc = 792696226576452305767009651523584
+421	disc = 1120727194291010174129782797578125
+422	disc = 1677627312816188946952964414636032
+423	disc = 40650250977089753091644821936498677
+424	disc = 82968596939468322350524272929144832
+425	disc = 86620507852136986313803229728551889
+426	disc = -48269476692516748870958029675063944413184
+427	disc = 3057647616
+428	disc = -61837572703801526878847827968
+429	disc = 10953815427820782987807562683135882947045385765546796605368678856
+6125676724691632661454253689888403350678050281881600000000000000000000000000
+0000000000000000000000
+430	disc = -4047658052954461065912911660658154357072747299150021320490491581
+6357719261043885533725876268976267090078699982339498617717978285115098117423
+5226786109719181996179722769519700738142090773570860994444369576764647340001
+6271055386451054047833315739281064060031306134989387012001240366983880112401
+0950391140893121715388041767973840048335480267318286333231520541406374861862
+1393537964424677884127250669128055090586243928496505152582326687370689187624
+8193649132417798542886781065276330907871042811558631554710375998113711429289
+8496513938923014831159547568236743150682002399344666964624978609632942422782
+6498518955202829946466452792918443649912549887356822674330431196145149803023
+4895588458953982456193472068052465591438058958231038395076434692769303458913
+1290964204147229157301290925386293126167130333738976824399921085466861118333
+7378074137212916065543375824994038692415387212889537750773611240570763116746
+580968621149210575140668368291928317296640
+3940200619639447921227904010014361380507973927046544666794829340424572177149
+7210611414266254884915640806627990306816
+63456228123711897600000000
+32065375478269160177874763776
+44676618219970392643498326834331791020485046219833344
+3353338721974729817301054329173240175627277795006862757734308781291047845002
+3018568598687979818620835459868306327959658030836737051459691258806591089437
+47971479203517856656769812503986176
+2
+2
+[1, 1/4747561509943*x + 1/4747561509943], -2100
+[1, 1/23737807549715*x + 1/23737807549715], -84
+[1, 1/23737807549715*x + 1/23737807549715], -84
+[1, 1/23737807549715*x + 1/23737807549715], -84
+[1, x], -47332614610453741984512822900
+[1, 1/23737807549715*x + 1/23737807549715], -84
+Total time spent: 5576
diff --git a/src/test/32/select b/src/test/32/select
new file mode 100644
index 0000000..14c76a6
--- /dev/null
+++ b/src/test/32/select
@@ -0,0 +1,15 @@
+[2, 5, 17, 37, 101, 197, 257, 401, 577, 677, 1297, 1601]
+Vecsmall([2, 3, 5])
+List([2, 3, 5])
+List([2, 3, 5])
+
+[1 2]
+
+[2 0]
+
+
+[0]
+
+[0]
+
+Total time spent: 8
diff --git a/src/test/32/ser b/src/test/32/ser
new file mode 100644
index 0000000..187e157
--- /dev/null
+++ b/src/test/32/ser
@@ -0,0 +1,38 @@
+x + x^2 + O(x^5)
+x + x^2 - 1/3*x^3 - x^4 + O(x^5)
+O(x^5)
+x + x^2 + 1/6*x^3 + 1/2*x^4 + O(x^5)
+O(x^5)
+1.5707963267948966192313216916397514421 - x - x^2 - 1/6*x^3 - 1/2*x^4 + O(x^
+5)
+1.5707963267948966192313216916397514421 + O(x^5)
+1 + 1/2*x^2 + x^3 + 13/24*x^4 + O(x^5)
+1 + O(x^5)
+x + x^2 + 1/6*x^3 + 1/2*x^4 + O(x^5)
+O(x^5)
+x + x^2 - 1/3*x^3 - x^4 + O(x^5)
+O(x^5)
+1.5707963267948966192313216916397514421*I - 1.000000000000000000000000000000
+0000000*I*x - 1.0000000000000000000000000000000000000*I*x^2 - 0.166666666666
+66666666666666666666666667*I*x^3 - 0.50000000000000000000000000000000000000*
+I*x^4 + O(x^5)
+1.5707963267948966192313216916397514421*I + O(x^5)
+x + x^2 - 1/6*x^3 - 1/2*x^4 + O(x^5)
+O(x^5)
+x + x^2 + 1/3*x^3 + x^4 + O(x^5)
+O(x^5)
+O(x^-2)
+O(x^-2)
+2 + O(x^2)
+1 + O(x^2)
+1
+O(x^0)
+2*x + O(x^2)
+2*x - 8/3*x^3 + 32/5*x^5 - 128/7*x^7 + 512/9*x^9 - 2048/11*x^11 + 8192/13*x^
+13 - 32768/15*x^15 + O(x^17)
+y + x + O(x^2)
+(y + O(y^17)) + (1 + O(y^16))*x + O(x^2)
+  ***   at top-level: Ser("")
+  ***                 ^-------
+  *** Ser: incorrect type in gtoser (t_STR).
+Total time spent: 0
diff --git a/src/test/32/set b/src/test/32/set
new file mode 100644
index 0000000..631061c
--- /dev/null
+++ b/src/test/32/set
@@ -0,0 +1,25 @@
+[1, 2, 3]
+[]
+[1, 2, 3]
+List([1, 2, 3])
+[1]
+[-2, 1, 3, 5, 7, x, "1"]
+[-5, 2, 5, 7, "1"]
+[5, 7, "1"]
+0
+1
+[-2, 1, 3, x]
+3
+7
+0
+3
+  ***   at top-level: setsearch(1,3)
+  ***                 ^--------------
+  *** setsearch: incorrect type in setsearch (t_INT).
+[-5, -2, 1, 2, 3, 5, 7, x, "1"]
+[3, 4, 5, 6, 7]
+[2, 3, 4, 5, 6]
+  ***   at top-level: setbinop(x->x,X)
+  ***                 ^----------------
+  *** setbinop: incorrect type in setbinop [function needs exactly 2 arguments] (t_CLOSURE).
+Total time spent: 4
diff --git a/src/test/32/size b/src/test/32/size
new file mode 100644
index 0000000..a8b8368
--- /dev/null
+++ b/src/test/32/size
@@ -0,0 +1,13 @@
+1
+1
+1
+0
+3
+3
+[1, 3]
+[3, 1]
+[2, 3]
+  ***   at top-level: matsize(1)
+  ***                 ^----------
+  *** matsize: incorrect type in matsize (t_INT).
+Total time spent: 4
diff --git a/src/test/32/sort b/src/test/32/sort
new file mode 100644
index 0000000..cec2d30
--- /dev/null
+++ b/src/test/32/sort
@@ -0,0 +1,30 @@
+[[1, 3/2], [1, 2], [5/2, 1.0000000000000000000000000000000000000], [3, 4], [
+4, 1]]
+2
+0
+[[1, 2], [1, 3/2], [5/2, 1.0000000000000000000000000000000000000], [3, 4], [
+4, 1]]
+1
+0
+[[4, 1], [5/2, 1.0000000000000000000000000000000000000], [1, 3/2], [1, 2], [
+3, 4]]
+4
+4
+error("incorrect type in lexicographic vecsort, index too large (t_INT).")
+[[5/2, 1.0000000000000000000000000000000000000], [4, 1], [1, 3/2], [1, 2], [
+3, 4]]
+4
+0
+error("incorrect type in lexicographic vecsort, index too large (t_INT).")
+[[1, 2], [1, 3/2], [5/2, 1.0000000000000000000000000000000000000], [3, 4], [
+4, 1]]
+1
+0
+Vecsmall([1, 2, 3, 4])
+List([1, 2, 3, 4])
+[1, 2, 3, 4]
+Vecsmall([4, 6, 2, 1])
+[4, 3, 2, 1]
+Vecsmall([1, 2, 6, 4])
+List([])
+Total time spent: 8
diff --git a/src/test/32/sqrtn b/src/test/32/sqrtn
new file mode 100644
index 0000000..48b9d59
--- /dev/null
+++ b/src/test/32/sqrtn
@@ -0,0 +1,55 @@
+0.E-12
+0.E-12 + 0.E-12*I
+0.79370052598409973737585281963615413020
+1.1739849967053285099666839718862667420
+2 + O(17^100)
+  ***   at top-level: sqrtn(Mod(0,3),-2)
+  ***                 ^------------------
+  *** sqrtn: impossible inverse in Fp_sqrtn: Mod(0, 3).
+  ***   at top-level: sqrtn(O(3),-2)
+  ***                 ^--------------
+  *** sqrtn: impossible inverse in ginv: O(3).
+  ***   at top-level: sqrtn(0*ffgen((2^64+
+  ***                 ^--------------------
+  *** sqrtn: impossible inverse in FpXQ_sqrtn: 0.
+  ***   at top-level: sqrtn(0*ffgen(3^2),-
+  ***                 ^--------------------
+  *** sqrtn: impossible inverse in Flxq_sqrtn: Vecsmall([0]).
+  ***   at top-level: sqrtn(0*ffgen(2^2),-
+  ***                 ^--------------------
+  *** sqrtn: impossible inverse in F2xq_sqrtn: Vecsmall([0]).
+9
+1.0717734625362931642130063250233420229
+0.80901699437494742410229341718281905886 + 0.5877852522924731291687059546390
+7276860*I
+1 + 3^2 + 2*3^4 + 2*3^5 + 3^6 + O(3^8)
+2 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + O(3^8)
+1 + O(3^2)
+1
+1
+1
+  ***   at top-level: sqrtn(Mod(2,4),3)
+  ***                 ^-----------------
+  *** sqrtn: impossible inverse in Fl_inv: Mod(2, 4).
+  ***   at top-level: sqrtn(Mod(2,4),3,&z)
+  ***                 ^--------------------
+  *** sqrtn: impossible inverse in Fl_inv: Mod(2, 4).
+1.0000000000000000000000000000000000000
+0
+error("not an n-th power residue in Qp_sqrt: 1 + 2 + O(2^2).")
+error("not an n-th power residue in Qp_sqrt: 1 + 2 + O(2^3).")
+error("not an n-th power residue in gsqrt: Mod(2, 11).")
+error("not a prime number in Fl_sqrt [modulus]: 33.")
+0.E-5 + 0.E-5*I
+[1 + O(3), 1]
+[1 + O(2), -1]
+[2 + O(2^3), 1 + 2 + O(2^2)]
+[2.0000000000000000000000000000000000000, 3.00000000000000000000000000000000
+00000]
+error("not an n-th power residue in gsqrtn: Mod(2, 7).")
+[0, 0]
+Mod(2, 21)
+error("not a prime number in sqrtn [modulus]: 35.")
+[Mod(99, 101), Mod(100, 101)]
+0.E-38
+Total time spent: 0
diff --git a/src/test/32/stark b/src/test/32/stark
new file mode 100644
index 0000000..8cd8a40
--- /dev/null
+++ b/src/test/32/stark
@@ -0,0 +1,117 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+321: x^3 - x^2 - 4*x + 1
+520: x^4 - 2*x^3 - 5*x^2 + 6*x - 1
+577: x^7 - x^6 - 10*x^5 + 13*x^4 + 10*x^3 - 7*x^2 - 2*x + 1
+840: x^4 - 2*x^3 - 5*x^2 + 6*x - 1
+904: x^8 - 3*x^7 - 17*x^6 + 44*x^5 + 112*x^4 - 206*x^3 - 357*x^2 + 315*x + 4
+63
+1009: x^7 - x^6 - 9*x^5 + 2*x^4 + 21*x^3 + x^2 - 13*x - 1
+1129: x^9 - x^8 - 16*x^7 - 6*x^6 + 64*x^5 + 65*x^4 - 48*x^3 - 78*x^2 - 19*x 
++ 1
+1229: x^3 - 14*x - 19
+1297: x^11 - x^10 - 28*x^9 - 50*x^8 + 63*x^7 + 216*x^6 + 77*x^5 - 173*x^4 - 
+122*x^3 + 18*x^2 + 17*x - 1
+1509: x^3 - x^2 - 7*x + 4
+1901: x^3 - x^2 - 9*x - 4
+1937: x^6 - 10*x^4 + 25*x^2 - 13
+2305: x^16 - 4*x^15 - 27*x^14 + 99*x^13 + 237*x^12 - 752*x^11 - 769*x^10 + 2
+006*x^9 + 1063*x^8 - 2006*x^7 - 769*x^6 + 752*x^5 + 237*x^4 - 99*x^3 - 27*x^
+2 + 4*x + 1
+2584: x^8 - 3*x^7 - 15*x^6 + 54*x^5 + 16*x^4 - 220*x^3 + 271*x^2 - 119*x + 1
+7
+2920: x^12 - 4*x^11 - 44*x^10 + 264*x^9 - 66*x^8 - 1880*x^7 + 2558*x^6 + 304
+0*x^5 - 6688*x^4 + 2192*x^3 + 446*x^2 - 80*x + 1
+3281: x^6 - x^5 - 17*x^4 - 11*x^3 + 17*x^2 - x - 1
+32569: x^3 - x^2 - 20*x + 13
+[y^2 - 145, 1]: x^4 + Mod(y - 14, y^2 - 145)*x^3 + Mod(-8*y + 95, y^2 - 145)
+*x^2 + Mod(21*y - 250, y^2 - 145)*x + Mod(-18*y + 217, y^2 - 145)
+[y^2 - 229, 1]: x^3 + Mod(-y - 14, y^2 - 229)*x^2 + Mod(6*y + 87, y^2 - 229)
+*x + Mod(-10*y - 156, y^2 - 229)
+[y^2 - 401, 1]: x^5 + Mod(-11/2*y - 219/2, y^2 - 401)*x^4 + Mod(155/2*y + 30
+91/2, y^2 - 401)*x^3 + Mod(-403*y - 8075, y^2 - 401)*x^2 + Mod(1789/2*y + 35
+845/2, y^2 - 401)*x + Mod(-1421/2*y - 28437/2, y^2 - 401)
+[y^2 - 577, 1]: x^7 + Mod(11/2*y - 271/2, y^2 - 577)*x^6 + Mod(-553/2*y + 13
+285/2, y^2 - 577)*x^5 + Mod(5730*y - 137633, y^2 - 577)*x^4 + Mod(-118647/2*
+y + 2849989/2, y^2 - 577)*x^3 + Mod(319416*y - 7672635, y^2 - 577)*x^2 + Mod
+(-1674997/2*y + 40234809/2, y^2 - 577)*x + Mod(810062*y - 19458357, y^2 - 57
+7)
+[y^2 - 761, 1]: x^3 + Mod(731/2*y - 20167/2, y^2 - 761)*x^2 + Mod(-244695*y 
++ 6750211, y^2 - 761)*x + Mod(21008185/2*y - 579536589/2, y^2 - 761)
+[y^3 - y^2 - 17*y - 16, 1]: x^4 + Mod(-2*y^2 - 10*y - 11, y^3 - y^2 - 17*y -
+ 16)*x^3 + Mod(21*y^2 + 86*y + 68, y^3 - y^2 - 17*y - 16)*x^2 + Mod(-66*y^2 
+- 262*y - 204, y^3 - y^2 - 17*y - 16)*x + Mod(65*y^2 + 262*y + 209, y^3 - y^
+2 - 17*y - 16)
+[y^3 - 14*y - 7, 1]: x^3 + Mod(-5*y^2 - 21*y - 10, y^3 - 14*y - 7)*x^2 + Mod
+(25*y^2 + 98*y + 42, y^3 - 14*y - 7)*x + Mod(-30*y^2 - 119*y - 52, y^3 - 14*
+y - 7)
+[y^3 - y^2 - 16*y + 22, 1]: x^3 + Mod(-10*y^2 - 27*y + 60, y^3 - y^2 - 16*y 
++ 22)*x^2 + Mod(171*y^2 + 464*y - 1016, y^3 - y^2 - 16*y + 22)*x + Mod(-715*
+y^2 - 1940*y + 4234, y^3 - y^2 - 16*y + 22)
+[y^3 - 36*y - 45, 1]: x^3 + Mod(-12*y^2 - 78*y - 79, y^3 - 36*y - 45)*x^2 + 
+Mod(1589/3*y^2 + 3469*y + 3642, y^3 - 36*y - 45)*x + Mod(-3038/3*y^2 - 6631*
+y - 6962, y^3 - 36*y - 45)
+[y^3 - 21*y - 35, 1]: x^3 + Mod(-4*y^2 + 12*y + 48, y^3 - 21*y - 35)*x^2 + M
+od(20*y^2 - 64*y - 219, y^3 - 21*y - 35)*x + Mod(-34*y^2 + 106*y + 381, y^3 
+- 21*y - 35)
+[y^3 - 12*y - 1, 1]: x^3 + Mod(-3*y^2 - 13*y - 4, y^3 - 12*y - 1)*x^2 + Mod(
+16*y^2 + 54*y + 7, y^3 - 12*y - 1)*x + Mod(-18*y^2 - 65*y - 6, y^3 - 12*y - 
+1)
+[y^3 - y^2 - 17*y - 16, 1]: x^4 + Mod(-2*y^2 - 10*y - 11, y^3 - y^2 - 17*y -
+ 16)*x^3 + Mod(21*y^2 + 86*y + 68, y^3 - y^2 - 17*y - 16)*x^2 + Mod(-66*y^2 
+- 262*y - 204, y^3 - y^2 - 17*y - 16)*x + Mod(65*y^2 + 262*y + 209, y^3 - y^
+2 - 17*y - 16)
+[y^3 - y^2 - 30*y - 27, 1]: x^3 + Mod(-250/3*y^2 - 1343/3*y - 350, y^3 - y^2
+ - 30*y - 27)*x^2 + Mod(3004/3*y^2 + 16139/3*y + 4244, y^3 - y^2 - 30*y - 27
+)*x + Mod(-2729*y^2 - 14662*y - 11564, y^3 - y^2 - 30*y - 27)
+[y^3 - 14*y - 7, 1]: x^3 + Mod(-5*y^2 - 21*y - 10, y^3 - 14*y - 7)*x^2 + Mod
+(25*y^2 + 98*y + 42, y^3 - 14*y - 7)*x + Mod(-30*y^2 - 119*y - 52, y^3 - 14*
+y - 7)
+[y^3 - y^2 - 16*y + 22, 1]: x^3 + Mod(-10*y^2 - 27*y + 60, y^3 - y^2 - 16*y 
++ 22)*x^2 + Mod(171*y^2 + 464*y - 1016, y^3 - y^2 - 16*y + 22)*x + Mod(-715*
+y^2 - 1940*y + 4234, y^3 - y^2 - 16*y + 22)
+[y^3 - y^2 - 30*y + 71, 1]: x^3 + Mod(-35*y^2 - 108*y + 604, y^3 - y^2 - 30*
+y + 71)*x^2 + Mod(194*y^2 + 599*y - 3364, y^3 - y^2 - 30*y + 71)*x + Mod(-25
+9*y^2 - 800*y + 4495, y^3 - y^2 - 30*y + 71)
+[y^3 - y^2 - 16*y - 6, 1]: x^3 + Mod(-11*y^2 + 47*y + 22, y^3 - y^2 - 16*y -
+ 6)*x^2 + Mod(293*y^2 - 1260*y - 532, y^3 - y^2 - 16*y - 6)*x + Mod(-1325*y^
+2 + 5696*y + 2410, y^3 - y^2 - 16*y - 6)
+[y^3 - 36*y - 45, 1]: x^3 + Mod(-12*y^2 - 78*y - 79, y^3 - 36*y - 45)*x^2 + 
+Mod(1589/3*y^2 + 3469*y + 3642, y^3 - 36*y - 45)*x + Mod(-3038/3*y^2 - 6631*
+y - 6962, y^3 - 36*y - 45)
+[y^3 - 12*y - 1, [5, y + 1]]: x^6 + Mod(-2*y^2 - 7*y - 2, y^3 - 12*y - 1)*x^
+5 + Mod(27*y^2 + 94*y + 1, y^3 - 12*y - 1)*x^4 + Mod(-149*y^2 - 521*y - 32, 
+y^3 - 12*y - 1)*x^3 + Mod(410*y^2 + 1439*y + 127, y^3 - 12*y - 1)*x^2 + Mod(
+-557*y^2 - 1957*y - 178, y^3 - 12*y - 1)*x + Mod(298*y^2 + 1046*y + 89, y^3 
+- 12*y - 1)
+[y^3 - y^2 - 37*y + 64, 1]: x^3 + Mod(-20*y^2 - 91*y + 230, y^3 - y^2 - 37*y
+ + 64)*x^2 + Mod(17039/3*y^2 + 25994*y - 195548/3, y^3 - y^2 - 37*y + 64)*x 
++ Mod(-504718*y^2 - 2309993*y + 5792200, y^3 - y^2 - 37*y + 64)
+[y^3 - y^2 - 9*y + 8, 1]: x^3 + Mod(-y^2 - 3*y, y^3 - y^2 - 9*y + 8)*x^2 + M
+od(5*y^2 + 12*y - 11, y^3 - y^2 - 9*y + 8)*x + Mod(-6*y^2 - 13*y + 15, y^3 -
+ y^2 - 9*y + 8)
+[y^3 - 21*y - 35, 1]: x^3 + Mod(-4*y^2 + 12*y + 48, y^3 - 21*y - 35)*x^2 + M
+od(20*y^2 - 64*y - 219, y^3 - 21*y - 35)*x + Mod(-34*y^2 + 106*y + 381, y^3 
+- 21*y - 35)
+[y^3 - y^2 - 16*y + 8, 1]: x^3 + Mod(-3/2*y^2 - 11/2*y, y^3 - y^2 - 16*y + 8
+)*x^2 + Mod(10*y^2 + 34*y - 18, y^3 - y^2 - 16*y + 8)*x + Mod(-16*y^2 - 53*y
+ + 32, y^3 - y^2 - 16*y + 8)
+[y^3 - y^2 - 4*y - 1, 7]: x^9 + Mod(3803*y^2 - 4845*y - 13885, y^3 - y^2 - 4
+*y - 1)*x^8 + Mod(-11843718*y^2 + 15087600*y + 43242511, y^3 - y^2 - 4*y - 1
+)*x^7 + Mod(2447928994*y^2 - 3118393621*y - 8937617416, y^3 - y^2 - 4*y - 1)
+*x^6 + Mod(-122752473137*y^2 + 156373216132*y + 448180745635, y^3 - y^2 - 4*
+y - 1)*x^5 + Mod(2291810549483*y^2 - 2919515812766*y - 8367614391752, y^3 - 
+y^2 - 4*y - 1)*x^4 + Mod(-20378966548367*y^2 + 25960573005885*y + 7440551044
+5644, y^3 - y^2 - 4*y - 1)*x^3 + Mod(92972141705228*y^2 - 118436333193067*y 
+- 339449973794721, y^3 - y^2 - 4*y - 1)*x^2 + Mod(-210119040368961*y^2 + 267
+668660944174*y + 767164243383345, y^3 - y^2 - 4*y - 1)*x + Mod(1864214244966
+95*y^2 - 237480491909325*y - 680642034264572, y^3 - y^2 - 4*y - 1)
+[y^3 - y^2 - 7*y + 6, [29, y - 13]]: x^7 + Mod(-5*y^2 - 11*y + 7, y^3 - y^2 
+- 7*y + 6)*x^6 + Mod(143*y^2 + 254*y - 306, y^3 - y^2 - 7*y + 6)*x^5 + Mod(-
+1762*y^2 - 3082*y + 3846, y^3 - y^2 - 7*y + 6)*x^4 + Mod(11513*y^2 + 20157*y
+ - 25127, y^3 - y^2 - 7*y + 6)*x^3 + Mod(-39912*y^2 - 69904*y + 87044, y^3 -
+ y^2 - 7*y + 6)*x^2 + Mod(67549*y^2 + 118318*y - 147287, y^3 - y^2 - 7*y + 6
+)*x + Mod(-43616*y^2 - 76398*y + 95101, y^3 - y^2 - 7*y + 6)
+x^4 + Mod(-32520*y - 102840, y^2 - 10)*x^3 + Mod(11599824*y + 36681864, y^2 
+- 10)*x^2 + Mod(-656457120*y - 2075899680, y^2 - 10)*x + Mod(6706767168*y + 
+21208659984, y^2 - 10)
+Total time spent: 25097
diff --git a/src/test/32/str b/src/test/32/str
new file mode 100644
index 0000000..487f2ca
--- /dev/null
+++ b/src/test/32/str
@@ -0,0 +1,8 @@
+Vecsmall([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33])
+"hello world!"
+"\\pmatrix{\n \\frac{1}{2}\\cr\n }"
+"\\frac{x^3\n + 3 y x^2\n + 3 y^2 x\n + y^3}{x^2\n - 2 y x\n + y^2}"
+"x"
+"1/2"
+"ab1"
+Total time spent: 4
diff --git a/src/test/32/subcyclo b/src/test/32/subcyclo
new file mode 100644
index 0000000..8462c33
--- /dev/null
+++ b/src/test/32/subcyclo
@@ -0,0 +1,3 @@
+x - 1
+[x^2 - 2, x^2 - 131, x^2 + 262, x^2 + 1, x^2 - 262, x^2 + 2, x^2 + x + 33]
+Total time spent: 16
diff --git a/src/test/32/subfields b/src/test/32/subfields
new file mode 100644
index 0000000..55f9e41
--- /dev/null
+++ b/src/test/32/subfields
@@ -0,0 +1,1701 @@
+1: [[x, 0], [x^2 + 972, 3*x^3], [x^3 + 54, 1/12*x^4 + 3/2*x], [x^3 + 864, 2*
+x^2], [x^3 - 54, -1/12*x^4 + 3/2*x], [x^6 + 108, x]]
+2: [[x, 0], [x^2 - 30, -8/7*x^7 + 97/7*x^5 - 197/7*x^3 + 139/7*x], [x^2 - 2,
+ -6/7*x^7 + 71/7*x^5 - 125/7*x^3 + 43/7*x], [x^2 - 24*x + 84, -2*x^6 + 24*x^
+4 - 44*x^2 + 24], [x^2 - 6, 2*x^7 - 23*x^5 + 35*x^3 - 11*x], [x^2 - 24*x + 6
+4, 32/7*x^6 - 360/7*x^4 + 480/7*x^2 - 24/7], [x^2 - 24*x + 36, -18/7*x^6 + 1
+92/7*x^4 - 144/7*x^2 + 24/7], [x^2 - 10, -x^5 + 11*x^3 - 11*x], [x^4 - 16*x^
+2 + 49, -x^7 + 12*x^5 - 23*x^3 + 13*x], [x^4 - 18*x^2 + 36, 3/7*x^7 - 32/7*x
+^5 + 24/7*x^3 + 31/7*x], [x^4 - 20*x^2 + 25, -4/7*x^7 + 45/7*x^5 - 60/7*x^3 
++ 31/7*x], [x^4 - 4*x^2 + 1, 4/7*x^7 - 45/7*x^5 + 60/7*x^3 - 17/7*x], [x^4 -
+ 6*x^2 + 4, -3/7*x^7 + 32/7*x^5 - 24/7*x^3 - 17/7*x], [x^4 - 8*x^2 + 1, x^7 
+- 12*x^5 + 23*x^3 - 11*x], [x^4 - 24*x^3 + 92*x^2 - 96*x + 16, 2*x^2], [x^8 
+- 12*x^6 + 23*x^4 - 12*x^2 + 1, x]]
+3: [[x, 0], [x^2 - 8, -x^6 + 9*x^2], [x^2 - 4*x - 8, x^6 - 11*x^2 + 2], [x^2
+ + 8*x - 8, -x^4 + 1], [x^4 - 8*x^3 + 20*x^2 - 16*x - 8, -x^7 + 10*x^3 + x +
+ 2], [x^4 + 4*x^2 - 8, x^7 - 10*x^3 + x], [x^4 - 4*x^3 - 4*x^2 + 16*x - 8, -
+x^2 + 1], [x^8 - 10*x^4 + 1, x]]
+4: [[x, 0], [x^2 - 6*x + 7, -x^4 - 2*x^2], [x^4 - 4*x^3 + 10*x^2 - 12*x + 7,
+ -x^2], [x^8 + 4*x^6 + 10*x^4 + 12*x^2 + 7, x]]
+5: [[x, 0], [x^3 - 54*x^2 + 729*x - 4116, 1/64*x^8 - 9/32*x^7 + 117/64*x^6 -
+ 87/16*x^5 + 99/16*x^4 + 9/2*x^3 + 737/16*x^2 + 15*x + 24], [x^3 - 81*x^2 + 
+1764*x - 4116, -1/70*x^8 + 121/490*x^7 - 207/140*x^6 + 883/245*x^5 - 851/980
+*x^4 - 3042/245*x^3 - 9804/245*x^2 - 7936/245*x + 5493/245], [x^3 - 81*x^2 -
+ 1176*x - 4116, 87/2240*x^8 - 5361/7840*x^7 + 9567/2240*x^6 - 46203/3920*x^5
+ + 20697/1960*x^4 + 30841/1960*x^3 + 483999/3920*x^2 + 35967/490*x + 10937/2
+45], [x^3 - 81*x^2 + 3528*x - 4116, 3/448*x^8 - 197/1568*x^7 + 387/448*x^6 -
+ 2111/784*x^5 + 263/98*x^4 + 2619/392*x^3 + 2095/784*x^2 + 97/98*x - 3/49], 
+[x^9 - 18*x^8 + 117*x^7 - 348*x^6 + 396*x^5 + 288*x^4 + 3012*x^3 + 576*x^2 +
+ 576*x - 512, x]]
+6: [[x, 0], [x^5 - 45*x^4 + 524*x^3 - 1559*x^2 + 91*x - 1, 3038549959301070/
+193673549326818517*x^9 + 114701029571267416/193673549326818517*x^8 - 3295488
+74995714210/193673549326818517*x^7 + 376376940440002096/17606686302438047*x^
+6 - 14032630283051868958/193673549326818517*x^5 + 31750438068174608336/19367
+3549326818517*x^4 - 33640776846315210164/193673549326818517*x^3 + 1289404927
+8294790719/193673549326818517*x^2 - 592790870239579798/193673549326818517*x 
++ 867925062747780/17606686302438047], [x^10 + 38*x^9 - 99*x^8 + 1334*x^7 - 4
+272*x^6 + 9244*x^5 - 8297*x^4 + 1222*x^3 + 1023*x^2 - 74*x + 1, x]]
+7: [[x, 0], [x^5 + 120*x^4 + 3990*x^3 + 42160*x^2 + 43385*x + 517448, -17134
+556086196609/5068781981585866941336*x^9 + 206152709694981995/506878198158586
+6941336*x^8 + 1609240120979582377/5068781981585866941336*x^7 - 6920363323788
+920677/1689593993861955647112*x^6 + 81936943990555286701/5068781981585866941
+336*x^5 + 152900407806684015487/1689593993861955647112*x^4 - 119401334776012
+7291825/5068781981585866941336*x^3 - 3869545478257111110553/5068781981585866
+941336*x^2 + 5817710613679700856179/1267195495396466735334*x - 2019572872099
+705211315/633597747698233367667], [x^10 - 20*x^9 + 80*x^8 + 200*x^7 - 3770*x
+^6 + 872*x^5 + 29080*x^4 + 36280*x^3 - 456615*x^2 + 541260*x - 517448, x]]
+8: [[x, 0], [x^2 + 6*x + 16, 1258375/11052559*x^9 - 688130/11052559*x^8 - 11
+958010/11052559*x^7 + 31393060/11052559*x^6 + 276559109/11052559*x^5 + 61866
+7950/11052559*x^4 + 716993740/11052559*x^3 + 454173360/11052559*x^2 + 196757
+250/11052559*x + 9706600/11052559], [x^10 - 10*x^8 + 20*x^7 + 235*x^6 + 606*
+x^5 + 800*x^4 + 600*x^3 + 270*x^2 + 70*x + 16, x]]
+9: [[x, 0], [x^3 - 4*x^2 - 12*x - 8, -5/74*x^11 + 7/37*x^10 - 17/74*x^9 - 6/
+37*x^8 + 29/37*x^7 - 22/37*x^6 + 12/37*x^5 + 43/37*x^4 - 46/37*x^3 + 20/37*x
+^2 + 18/37*x - 60/37], [x^4 + 6*x^3 + 12*x^2 + 8*x + 8, -4/37*x^11 + 15/74*x
+^10 - 5/74*x^9 - 17/37*x^8 + 41/74*x^7 + 24/37*x^6 - 3/37*x^5 + 54/37*x^4 + 
+30/37*x^3 - 42/37*x^2 + 14/37*x - 22/37], [x^12 + 6*x^9 + 4*x^8 + 8*x^6 - 4*
+x^5 - 12*x^4 + 8*x^3 - 8*x + 8, x]]
+10: [[x, 0], [x^2 - 23*x + 1, -95/63*x^11 - 760/63*x^10 + 170/21*x^9 + 745/7
+*x^8 + 3305/21*x^7 + 205*x^6 + 635/3*x^5 + 870/7*x^4 + 590/7*x^3 + 575/63*x^
+2 - 650/63*x + 454/21], [x^3 + 15*x^2 + 12*x - 1, -16/7*x^11 - 142/7*x^10 - 
+89/21*x^9 + 3530/21*x^8 + 8051/21*x^7 + 1667/3*x^6 + 1916/3*x^5 + 10919/21*x
+^4 + 7034/21*x^3 + 2672/21*x^2 - 436/21*x - 386/21], [x^4 + 18*x^3 - 36*x^2 
++ 270*x + 1107, 310/63*x^11 + 2644/63*x^10 - 290/63*x^9 - 7426/21*x^8 - 2114
+/3*x^7 - 1010*x^6 - 3398/3*x^5 - 6084/7*x^4 - 11806/21*x^3 - 11218/63*x^2 + 
+2762/63*x + 47/9], [x^6 - 3*x^5 - 33*x^4 + 106*x^3 - 24*x^2 - 75*x + 1, -x^1
+1 - 9*x^10 - 3*x^9 + 73*x^8 + 177*x^7 + 267*x^6 + 315*x^5 + 267*x^4 + 177*x^
+3 + 73*x^2 - 2*x - 7], [x^12 + 9*x^11 + 3*x^10 - 73*x^9 - 177*x^8 - 267*x^7 
+- 315*x^6 - 267*x^5 - 177*x^4 - 73*x^3 + 3*x^2 + 9*x + 1, x]]
+11: [[x - 34734, 34734], [x^3 - 404450238*x^2 + 51929707167307884*x - 210694
+5843643976551078888, -9342188044422318487729248211775/2214669002270171995835
+0965876436984233324505664916550383771692*x^11 + 6033218347374060922967697235
+7289403/5536672505675429989587741469109246058331126416229137595942923*x^10 -
+ 755466390810188186936214482171670640445/22146690022701719958350965876436984
+233324505664916550383771692*x^9 - 392022912004136123430642855831161297287133
+3/3691115003783619993058494312739497372220750944152758397295282*x^8 + 647561
+90353632899593058088471324843886641684521/7382230007567239986116988625478994
+744441501888305516794590564*x^7 - 183930570642208634667376560703511349411910
+07246920/1845557501891809996529247156369748686110375472076379198647641*x^6 -
+ 214051222556933671040122576873060155244550337314553091/36911150037836199930
+58494312739497372220750944152758397295282*x^5 + 3742979005268450240872002688
+31763757380697898774819087471/3691115003783619993058494312739497372220750944
+152758397295282*x^4 + 732738748406097211695426095371309517410358207767354112
+603431/7382230007567239986116988625478994744441501888305516794590564*x^3 - 1
+029490244100838046829406711792226497002817428265903324830579547/553667250567
+5429989587741469109246058331126416229137595942923*x^2 - 45096911671329708869
+307328748124201519401653093836882262901185897/553667250567542998958774146910
+9246058331126416229137595942923*x + 1035452998465952296944708318989954617972
+835353405495945537529058226202/553667250567542998958774146910924605833112641
+6229137595942923], [x^4 - 34734*x^3 + 300462696*x^2 + 41450199048*x - 379221
+762052224, 14110573821076695554388896014506463760825/18532084403873711703780
+61701288375914871619333547574940815535866672304656*x^11 - 481485463029074233
+335782172117101303661014341/185320844038737117037806170128837591487161933354
+7574940815535866672304656*x^10 + 5367285004556060607251380985659601467584643
+783775/185320844038737117037806170128837591487161933354757494081553586667230
+4656*x^9 - 724957691551120605058587600103565855371815194249245/7721701834947
+3798765752570887015663119650805564482289200647327778012694*x^8 - 14926423613
+827844109215358159995271639520843231424596277/617736146795790390126020567096
+125304957206444515858313605178622224101552*x^7 + 763836288194432943202980786
+97519097254809895356403449363049/6177361467957903901260205670961253049572064
+44515858313605178622224101552*x^6 + 2751911640084126226673791312812683285355
+833117563952352468743/386085091747368993828762854435078315598254027822411446
+00323663889006347*x^5 - 2797395403590994034737713919789472364603094039317098
+91862395749053/6177361467957903901260205670961253049572064445158583136051786
+22224101552*x^4 - 7504160249443571670530091644599868598849760894682531424393
+6087235753/61773614679579039012602056709612530495720644451585831360517862222
+4101552*x^3 + 19286065393511873013020896030775996158242421244256300897032679
+4213732861/46330211009684279259451542532209397871790483338689373520388396666
+8076164*x^2 - 16191548463390010827165201305543186319057665739249267235602841
+186782411209/463302110096842792594515425322093978717904833386893735203883966
+668076164*x - 13056495504690505502174926686295024785326634173334466106080828
+4353649867306/11582552752421069814862885633052349467947620834672343380097099
+1667019041], [x^4 - 34734*x^3 + 300462696*x^2 + 719593875864*x - 12048455417
+833440, -912978933965428702677328421946337977275/427009469302505073852676592
+5050936313158231730603136168953156155639990654*x^11 + 8407246036776236034735
+3536967653431092912031/17080378772100202954107063700203745252632926922412544
+675812624622559962616*x^10 - 11398417297495892544563044210311176625029955575
+/17080378772100202954107063700203745252632926922412544675812624622559962616*
+x^9 - 893347789433470245763794191918075393707137337220545/142336489767501691
+2842255308350312104386077243534378722984385385213330218*x^8 + 24686014963885
+51544725562985100449173297162272410181126/7116824488375084564211276541751560
+52193038621767189361492192692606665109*x^7 + 2619172679537435761406478400285
+9409319675692967171797755667/56934595907000676513690212334012484175443089741
+37514891937541540853320872*x^6 - 5444505759822050986091257169800106052333185
+45168647774612861/1358820904701686790302869029451371937361410256357402122180
+7975037836088*x^5 - 84089850641580864583404121851546268588343862717124795869
+654550967/569345959070006765136902123340124841754430897413751489193754154085
+3320872*x^4 + 82269200238740742666178668103656946863466970188385987011592278
+2319399/56934595907000676513690212334012484175443089741375148919375415408533
+20872*x^3 + 3503428352603249699300472760879525632722446036723176866748006043
+47982411/8540189386050101477053531850101872626316463461206272337906312311279
+981308*x^2 - 253000270288016296385150329441426641791370700418697463186888852
+550572865528/213504734651252536926338296252546815657911586530156808447657807
+7819995327*x + 1912911454523555330264483772745854808577616715831981650539735
+0720058392018650/21350473465125253692633829625254681565791158653015680844765
+78077819995327], [x^4 - 34734*x^3 + 300462696*x^2 - 667378778688*x + 4288123
+52307072, 6273160180385208072676253797115270236775/5940858424916371597850579
+454643268649594120257767824314298295930250745714*x^11 - 91833464035386409580
+0706379445833929537385123/23763433699665486391402317818573074598376481031071
+297257193183721002982856*x^10 + 11620993006244117573583895395392195759302448
+890375/237634336996654863914023178185730745983764810310712972571931837210029
+82856*x^9 - 2209724345743573539834795881875030751631879652688650/99014307081
+9395266308429909107211441599020042961304052383049321708457619*x^8 - 90490574
+1980509693458118408199924159677196678384605051/99014307081939526630842990910
+7211441599020042961304052383049321708457619*x^7 + 22877305893848271839343308
+7107542783081076855760562416305945/79211445665551621304674392728576915327921
+60343690432419064394573667660952*x^6 - 2571247739128689359677893264165877823
+48593980664060018882482409/7921144566555162130467439272857691532792160343690
+432419064394573667660952*x^5 - 902915509977437374550740055247922997304160711
+198583657340540293029/792114456655516213046743927285769153279216034369043241
+9064394573667660952*x^4 + 12647371976768847217149269686446726878798796344873
+47056588849959499529/7921144566555162130467439272857691532792160343690432419
+064394573667660952*x^3 + 184108522696301150772261875173517105531726706993092
+1240026759849694558005/11881716849832743195701158909286537299188240515535648
+628596591860501491428*x^2 - 105226430872673292657652274117946531461625581409
+6099354896910163038066989639/59408584249163715978505794546432686495941202577
+67824314298295930250745714*x + 351897368084118382955192810963102536960435507
+9690659359039411864812112186299/29704292124581857989252897273216343247970601
+28883912157149147965125372857], [x^4 - 34734*x^3 + 300462696*x^2 - 944224434
+00*x - 618844230971136, -6244226996057952777880113480351379337675/7384147894
+07247856391164124670071766158949771633107927816709692427223344*x^11 + 216750
+230651841816989537133589460109029889307/738414789407247856391164124670071766
+158949771633107927816709692427223344*x^10 - 24992189654493621869938874441777
+63611038902109425/7384147894072478563911641246700717661589497716331079278167
+09692427223344*x^9 + 188417720221548167848861074692067065579959656279560/153
+83641445984330341482585930626495128311453575689748496181451925567153*x^8 + 5
+318638181429905968700041964805475458478670091210794943/246138263135749285463
+721374890023922052983257211035975938903230809074448*x^7 - 386763277860758627
+19262052122652322648153233487743293439359/2461382631357492854637213748900239
+22052983257211035975938903230809074448*x^6 + 7698539809990361287561871527957
+3605511131289962139790144515/61534565783937321365930343722505980513245814302
+758993984725807702268612*x^5 + 143154969427923778251503253915653423528426333
+300467148693599297771/246138263135749285463721374890023922052983257211035975
+938903230809074448*x^4 - 449658405954593992823654430001517440485681802597131
+26203807118706065/2461382631357492854637213748900239220529832572110359759389
+03230809074448*x^3 - 1130232482228186252206081607706505141404728370672174259
+61760024891067261/1846036973518119640977910311675179415397374429082769819541
+77423106805836*x^2 + 6176287549251876371093517320579733410772043422707238431
+4053857007019156725/18460369735181196409779103116751794153973744290827698195
+4177423106805836*x + 6525285229664225652346515846620963724288153967638848197
+61741487186945039935/4615092433795299102444775779187948538493436072706924548
+8544355776701459], [x^6 - 34734*x^5 + 401383590*x^4 - 1534279047280*x^3 - 62
+3086166484273*x^2 + 6024207776080451370*x - 3395544494886430834931, 65048283
+4595988426289875584925611839809050403841510975/93879859694709658900023423073
+908800229122926824806536083213101085431514021400845370272*x^11 - 28280356132
+03818848595391814308127055615711578492704854205/1173498246183870736250292788
+4238600028640365853100817010401637635678939252675105671284*x^10 + 2617776421
+07582168054428278943612510410442805195866384609962775/9387985969470965890002
+3423073908800229122926824806536083213101085431514021400845370272*x^9 - 15927
+1141747715886165104807305964751229171393962459611440912934045/15646643282451
+609816670570512318133371520487804134422680535516847571919003566807561712*x^8
+ - 549955490462771080691539799763589822375411867004836057005174839265373/312
+9328656490321963334114102463626674304097560826884536107103369514383800713361
+5123424*x^7 + 25864145666173083796718616503459506022050622985470132525739709
+5009034637/19558304103064512270838213140397666714400609755168028350669396059
+46489875445850945214*x^6 - 6592918174156851318520777070014249645801977031244
+2956213117809125335509633/78233216412258049083352852561590666857602439020672
+11340267758423785959501783403780856*x^5 - 7800898227382363725189039065112986
+079820471591287196576278549997100182092831745/156466432824516098166705705123
+18133371520487804134422680535516847571919003566807561712*x^4 + 6122142268116
+939512060932346170056883900854266430077469732839637712189084833407767/312932
+8656490321963334114102463626674304097560826884536107103369514383800713361512
+3424*x^3 + 32022508461623431276902286210776146200887958099783536289531117346
+37868539334724284127/5867491230919353681251463942119300014320182926550408505
+200818817839469626337552835642*x^2 - 800638255584388658971089596935817625981
+9365543316588290509462947927580787314394474893213/23469964923677414725005855
+768477200057280731706201634020803275271357878505350211342568*x - 58990558701
+8760848758798275065010731791241332701940631740790132610723674961763135480882
+7/29337456154596768406257319710596500071600914632752042526004094089197348131
+68776417821], [x^6 - 404450238*x^5 + 54100188079371078*x^4 - 271097787243506
+0813524384*x^3 + 42285567950111504527423881770355*x^2 - 20529948264855793448
+2118273363967086790*x + 308274255189281477531677588408355830798086937, -4120
+869126500964373994569574506880767182320713762311294475/938798596947096589000
+23423073908800229122926824806536083213101085431514021400845370272*x^11 + 146
+940653700070562235241304852946310481730440354337413521273481/938798596947096
+58900023423073908800229122926824806536083213101085431514021400845370272*x^10
+ - 222703401706905275932607625400403116755352990786094605748551642925/117349
+8246183870736250292788423860002864036585310081701040163763567893925267510567
+1284*x^9 + 12377075137704495001629425332319301954065542889699939222464837777
+94171/1564664328245160981667057051231813337152048780413442268053551684757191
+9003566807561712*x^8 + 20066085476142621805212784221307018563054220745603711
+26880840186645292325/3129328656490321963334114102463626674304097560826884536
+1071033695143838007133615123424*x^7 - 30041249679723991746278208886194545785
+402295380444679832412631893124807114363/312932865649032196333411410246362667
+43040975608268845361071033695143838007133615123424*x^6 + 2087488959839192259
+0201106347226773398572860027042902334147818870582912283491027/31293286564903
+219633341141024636266743040975608268845361071033695143838007133615123424*x^5
+ + 1113444893903474697082861783171804271560046490863740226525751251144715711
+09171287261/3129328656490321963334114102463626674304097560826884536107103369
+5143838007133615123424*x^4 - 54037078291258541773319971842404517193840416945
+659451213116511317761079476637043173135/156466432824516098166705705123181333
+71520487804134422680535516847571919003566807561712*x^3 - 2033717931912934290
+80383788937903691340509781131977766772274019850437153377772215101960661/4693
+9929847354829450011711536954400114561463412403268041606550542715757010700422
+685136*x^2 + 119226908672022683411723814050468113357593231142438856317381065
+29862168277632359183454929030/2933745615459676840625731971059650007160091463
+275204252600409408919734813168776417821*x + 21641809528558234322621587024281
+82538403098495413532025934764817277543505392932163117127075065/1173498246183
+8707362502927884238600028640365853100817010401637635678939252675105671284], 
+[x^6 - 34734*x^5 + 401383590*x^4 - 1534279047280*x^3 - 169315682156985*x^2 +
+ 770453108539110906*x + 33220916743396911661, -36288992724566042787812747887
+4582939883814569944534675/93879859694709658900023423073908800229122926824806
+536083213101085431514021400845370272*x^11 + 12238241238442727065545358658257
+447978393534633897082817625/938798596947096589000234230739088002291229268248
+06536083213101085431514021400845370272*x^10 - 664894587801976961863497775860
+01819171185673071207046291100875/4693992984735482945001171153695440011456146
+3412403268041606550542715757010700422685136*x^9 + 64656603467569138793589545
+104078793625221358249253851685597622935/156466432824516098166705705123181333
+71520487804134422680535516847571919003566807561712*x^8 + 4623655015680620106
+51546371322987178103039625985593620591030386511105/3129328656490321963334114
+1024636266743040975608268845361071033695143838007133615123424*x^7 - 19015914
+06040779221754637364862552563516461322719907608688999134986276895/3129328656
+4903219633341141024636266743040975608268845361071033695143838007133615123424
+*x^6 - 187253813263959519095029930199330041649555034596928840729682216077533
+1374615/31293286564903219633341141024636266743040975608268845361071033695143
+838007133615123424*x^5 + 737173668594558261406590356021435552666740739108865
+6090600761557159558228783105/31293286564903219633341141024636266743040975608
+268845361071033695143838007133615123424*x^4 + 241072810751756404394252967643
+452651428825132481432156536773360547515144225208245/195583041030645122708382
+1314039766671440060975516802835066939605946489875445850945214*x^3 - 10065153
+5951548846430086524306167014187845178960442135338090501538082420220528382937
+25/4693992984735482945001171153695440011456146341240326804160655054271575701
+0700422685136*x^2 - 11030678695913727302525949875303187188235190350092781841
+5833782064611237363146362782355/11734982461838707362502927884238600028640365
+853100817010401637635678939252675105671284*x - 53243921404282058396885384791
+2984676758353132269792636297543816246239642774824102925179/11734982461838707
+362502927884238600028640365853100817010401637635678939252675105671284], [x^1
+2 - 34734*x^11 + 401000259*x^10 - 1456627492885*x^9 - 2537142937228035*x^8 +
+ 18762072755679375516*x^7 - 812368636358864062944*x^6 - 70132863629758257512
+231931*x^5 + 25834472514893102332821062085*x^4 + 766232806103524502472479395
+84745*x^3 - 45080885015422662132515763499758450*x^2 - 2070499552240812214288
+316981071818900*x - 550505759097778545485364826246753544, x]]
+12: [[x, 0], [x^3 + 3*x^2 + 4*x + 1, 14932268423886825/134040977335195199*x^
+14 + 3314773739694150/134040977335195199*x^13 - 458667248689990/134040977335
+195199*x^12 + 299660784416742155/134040977335195199*x^11 + 19314189677750535
+00/134040977335195199*x^10 + 7916949557683234500/134040977335195199*x^9 + 26
+268194382756835965/134040977335195199*x^8 + 56555578533556092735/13404097733
+5195199*x^7 + 80995665441611911535/134040977335195199*x^6 + 7977029541497422
+1849/134040977335195199*x^5 + 57127754838291495800/134040977335195199*x^4 + 
+29731892661369048315/134040977335195199*x^3 + 10792405938344532550/134040977
+335195199*x^2 + 2538888140467665125/134040977335195199*x + 15389316999785695
+0/134040977335195199], [x^15 + 20*x^12 + 125*x^11 + 503*x^10 + 1650*x^9 + 34
+30*x^8 + 4690*x^7 + 4335*x^6 + 2904*x^5 + 1400*x^4 + 485*x^3 + 100*x^2 + 15*
+x + 1, x]]
+13: [[x, 0], [x^2 - 2, -x^16], [x^4 - 2, -x^8], [x^8 - 2, -x^4], [x^16 - 2, 
+-x^2], [x^32 - 2, x]]
+14: [[x, 0], [x^3 - 2256*x^2 + 6096*x + 64, -1941708934505961434012429883125
+0860157099952726892251/770574202417890161481916508657135480583189210820336*x
+^26 + 465261347703887731979726777504133076469790749716419/700522002198081964
+98356046241557770962108110074576*x^25 + 145546216272635060505776677527078053
+207287212099723363/48160887651118135092619781791070967536449325676271*x^24 +
+ 609511312566045052425738403987685164063245222472298727/77057420241789016148
+1916508657135480583189210820336*x^23 - 1103182803837756393768615747557123337
+78850601164726902531/770574202417890161481916508657135480583189210820336*x^2
+2 - 35764561073596826122555693587416304111300918329291915265/385287101208945
+080740958254328567740291594605410168*x^21 + 26986765568191452078996731186958
+97987172753535814855959363/7705742024178901614819165086571354805831892108203
+36*x^20 + 2535300787966757653899707912201660658080677991096347496733/7705742
+02417890161481916508657135480583189210820336*x^19 - 184500421281399807210404
+13663350432313722498669439849137367/3852871012089450807409582543285677402915
+94605410168*x^18 - 382738570038762014634870665591100320360686007281960816237
+5/70052200219808196498356046241557770962108110074576*x^17 + 1421780866681070
+75407847922087024499291038110902822453518015/3852871012089450807409582543285
+67740291594605410168*x^16 + 493295798745689513357840097535831176396514500407
+9263946901/10853157780533664246224176178269513811030833955216*x^15 - 7580219
+3796193664872566402589365181600247079699279904970949/48160887651118135092619
+781791070967536449325676271*x^14 - 14189256922643983469056003182354684709402
+94544244297103895783/770574202417890161481916508657135480583189210820336*x^1
+3 + 2949268306628070636920950136227355126808364474772530622119857/7705742024
+17890161481916508657135480583189210820336*x^12 + 166589581259622181167910161
+70344564504455183828422917544032/4378262513738012281147252890097360685131756
+879661*x^11 - 990545784945921859397562859484696191953405596548533237819111/1
+92643550604472540370479127164283870145797302705084*x^10 - 157793499488740516
+3427397869387988325500213538839953957172237/38528710120894508074095825432856
+7740291594605410168*x^9 + 25167273706310086938584534037448648134472333543354
+60822902795/770574202417890161481916508657135480583189210820336*x^8 + 168807
+157196505177595021303258144303674164728470637182661851/700522002198081964983
+56046241557770962108110074576*x^7 - 5183318657770617066878045436057944671512
+46150122922616485461/770574202417890161481916508657135480583189210820336*x^6
+ - 1776270119139661464090238799174385930766842582827745181371/27132894451334
+16061556044044567378452757708488804*x^5 - 3795395413907166661496173345979920
+804875647669966430644559/48160887651118135092619781791070967536449325676271*
+x^4 + 935037583110078417918059907483691589942806970032644103842/481608876511
+18135092619781791070967536449325676271*x^3 + 2070294748166925189510266998004
+27111617432833336068658906/4816088765111813509261978179107096753644932567627
+1*x^2 + 7357964953188324444369967201461211123383988457557368072/481608876511
+18135092619781791070967536449325676271*x - 296542410803660849197540276703255
+503397498629122175636/48160887651118135092619781791070967536449325676271], [
+x^3 - 2796*x^2 - 960*x + 64, -7398467947462967071449081703305414037830363783
+21220562733077/1645953101585370740052867362633907861270288820806508923601430
+2*x^26 - 2210993295137920400153699721177302123375420702843160712207137/90527
+420587195390702907704944864932369865885144357990798078661*x^25 + 77445201700
+75936726930825115050660035652030327315379025370168609/1448438729395126251246
+523279117838917917854162309727852769258576*x^24 + 82848290601238996733541936
+29984574355737519289228087543949666801/1448438729395126251246523279117838917
+917854162309727852769258576*x^23 - 35901241029118455354254627498534656545123
+1093312484340564713122561/14484387293951262512465232791178389179178541623097
+27852769258576*x^22 - 526355214064307610466287041002238084273709423917420752
+758848566155/144843872939512625124652327911783891791785416230972785276925857
+6*x^21 + 4214074164145018774103330454775088768075402073444754515666988968027
+/724219364697563125623261639558919458958927081154863926384629288*x^20 + 6853
+33941994204545917666687114508041352829458300155523409117259047/6583812406341
+4829602114694505356314450811552832260356944057208*x^19 - 1069113459131628901
+45797530748166151719319481766399539290295124370257/1448438729395126251246523
+279117838917917854162309727852769258576*x^18 - 22069952916279389685637098419
+7801378507797376158324629810580186498873/14484387293951262512465232791178389
+17917854162309727852769258576*x^17 + 715081621748863035896257730347336007514
+096632125143483060169924296217/144843872939512625124652327911783891791785416
+2309727852769258576*x^16 + 2324224568652505655977226792952233162797157561370
+9400185272082977721/20400545484438397904880609565039984759406396652249688067
+172656*x^15 - 23131236016680905235421808448499402418785791423724641025641151
+24139141/1448438729395126251246523279117838917917854162309727852769258576*x^
+14 - 5856573846677695136177029586583045589991747682773104214719002354121491/
+1448438729395126251246523279117838917917854162309727852769258576*x^13 + 3651
+180832394425697740451101578157246205689545997506067287443696038813/144843872
+9395126251246523279117838917917854162309727852769258576*x^12 + 1004042652056
+1184085992260635787732864328665489520074163406248596214953/14484387293951262
+51246523279117838917917854162309727852769258576*x^11 - 114188197615994305615
+0352420584376105638987469781569823827491087714737/72421936469756312562326163
+9558919458958927081154863926384629288*x^10 - 1941840292156985856042966823843
+091035965664640434668187482417279631473/362109682348781562811630819779459729
+479463540577431963192314644*x^9 - 405860245714839461599072202945428738509236
+22637376408267704565530984/9052742058719539070290770494486493236986588514435
+7990798078661*x^8 + 10820459067092489285053671440133728060542917460755957359
+61248749508317/7242193646975631256232616395589194589589270811548639263846292
+88*x^7 + 8861380037025468041187538410135187781855132948807436648032930087691
+51/1448438729395126251246523279117838917917854162309727852769258576*x^6 + 12
+69141287869800364601057824070219376621096840567842007072173038733/2040054548
+4438397904880609565039984759406396652249688067172656*x^5 - 97999252912324104
+67649476579009759547499077607129326386012443315269/7242193646975631256232616
+39558919458958927081154863926384629288*x^4 - 1658731209293582607061161341066
+424240216534357816117719110794086563/362109682348781562811630819779459729479
+463540577431963192314644*x^3 - 416959419511533591557215671143180277136599872
+17016395377055931636/9052742058719539070290770494486493236986588514435799079
+8078661*x^2 - 56581149967573957735857504427621740454460778602059876917654405
+1/90527420587195390702907704944864932369865885144357990798078661*x + 4694516
+3889986656942312064244366240832320064800593433556330540/90527420587195390702
+907704944864932369865885144357990798078661], [x^3 + 2084*x^2 - 960*x + 64, -
+561227570831357327261748722300671144714199316788821374765/374668834678013552
+26040608060903790085031225603283152269884*x^26 - 467030323146825154773051563
+1866750664171046908135280010511/14986753387120542090416243224361516034012490
+2413132609079536*x^25 + 2655982187166666534628949444775506592906681662216895
+80586573/149867533871205420904162432243615160340124902413132609079536*x^24 +
+ 348189693874250542182197336832501857397062852480476420943493/74933766935602
+710452081216121807580170062451206566304539768*x^23 - 11991930611906407551536
+445364733429567610668847504070595479995/149867533871205420904162432243615160
+340124902413132609079536*x^22 - 37235352814481353508805084287382560420754145
+636609578106529157/149867533871205420904162432243615160340124902413132609079
+536*x^21 + 66280866039627531860595064079889449185756276577079917533847481/37
+466883467801355226040608060903790085031225603283152269884*x^20 + 97025825868
+1296916077879398527505905523423187223670658505540191/14986753387120542090416
+2432243615160340124902413132609079536*x^19 - 2943834022061190442942620941971
+984643359379628448515131816679339/149867533871205420904162432243615160340124
+902413132609079536*x^18 - 33453163819081176501607746814474780772199787876567
+28390306807373/37466883467801355226040608060903790085031225603283152269884*x
+^17 + 13674894571589217805893019967706794800106223373397441220281477961/1498
+67533871205420904162432243615160340124902413132609079536*x^16 + 602078892442
+5419464207909813876066699842494812053733761289242738/93667208669503388065101
+52015225947521257806400820788067471*x^15 + 294353128177567374225243548890428
+4513815971474029257246467871633/14986753387120542090416243224361516034012490
+2413132609079536*x^14 - 1679924756905466276300271331479047571634361641324411
+11855454223073/74933766935602710452081216121807580170062451206566304539768*x
+^13 - 169267741415077227249195598342359460523011079667191272211912713953/149
+867533871205420904162432243615160340124902413132609079536*x^12 + 58046585984
+6163164837994249948641890283751158991278197371683104575/14986753387120542090
+4162432243615160340124902413132609079536*x^11 + 2147410176527707658180995922
+86832494196454603044510532036771347703/7493376693560271045208121612180758017
+0062451206566304539768*x^10 - 2872787266706575737776271249101323331042903273
+1768248245371736066/93667208669503388065101520152259475212578064008207880674
+71*x^9 - 209256865896977388082011634232583311906433877889134968495769903035/
+74933766935602710452081216121807580170062451206566304539768*x^8 + 9685258147
+1927815905027359872825120899862107832279504762470974403/14986753387120542090
+4162432243615160340124902413132609079536*x^7 + 14832711113305345708607215075
+6136089755982514457246759954599435889/14986753387120542090416243224361516034
+0124902413132609079536*x^6 + 33168501411549202886974743354924252564559928858
+971918327422562761/149867533871205420904162432243615160340124902413132609079
+536*x^5 - 931928278647997781785460776674338081385763852142006458288593669/74
+933766935602710452081216121807580170062451206566304539768*x^4 - 884320275796
+39909365435795132422861390768710065327799424993805/9366720866950338806510152
+015225947521257806400820788067471*x^3 - 936806802023053551712453814839333960
+9995787303491086013553107/93667208669503388065101520152259475212578064008207
+88067471*x^2 - 208351784599897404047289786553424700159852897511454373845310/
+9366720866950338806510152015225947521257806400820788067471*x + 1193113483585
+5584097274884576748218224199275925940999633098/93667208669503388065101520152
+25947521257806400820788067471], [x^3 - 51*x^2 + 48*x + 64, -1556489649355473
+0802541434445630680122230637785713/69371377009148175174079939980762865583621
+192509024*x^26 + 393501446137692897081827553127467296566229415669/8671422126
+143521896759992497595358197952649063628*x^25 + 37344182946088564647085401854
+93019023991368874788845/138742754018296350348159879961525731167242385018048*
+x^24 + 602926222827948364981856199181901144185734257180965/69371377009148175
+174079939980762865583621192509024*x^23 - 16077924803290367253342138093970327
+920188322691620725/12612977638026940940741807269229611924294762274368*x^22 -
+ 3922406994747505217428135835740166290414595225993829/4335711063071760948379
+996248797679098976324531814*x^21 + 21610264543463032674757067537962822239076
+07275350339129/69371377009148175174079939980762865583621192509024*x^20 + 216
+5332906631891276979111265349104518658030209781415135/69371377009148175174079
+939980762865583621192509024*x^19 - 58971883561690962814857386897540236894683
+583190758222363/138742754018296350348159879961525731167242385018048*x^18 - 8
+08645284560840678162955816053077064211321736186290851/1576622204753367617592
+725908653701490536845284296*x^17 + 45261307567929335788409099690641349911541
+0773495517136907/138742754018296350348159879961525731167242385018048*x^16 + 
+4155848462327012334970630882373126743426565772330513255/97706164801617148132
+5069577193843177234101302944*x^15 - 1916906407594845094913715796739119007763
+588347723982336701/138742754018296350348159879961525731167242385018048*x^14 
+- 299986634866134590855571167455249119719959097988270586497/1734284425228704
+3793519984995190716395905298127256*x^13 + 4619793814928435228565196907730980
+082353574691727216335189/138742754018296350348159879961525731167242385018048
+*x^12 + 78349128423247148069316494006284281559905812973178274922/21678555315
+35880474189998124398839549488162265907*x^11 - 153594553777708340787226033909
+9202940420314394485957829651/34685688504574087587039969990381432791810596254
+512*x^10 - 1374248730618644771669596326725122660339183152162363418743/346856
+88504574087587039969990381432791810596254512*x^9 + 1919162854225643871387126
+081073397590088114675963996148559/693713770091481751740799399807628655836211
+92509024*x^8 + 1636581600518698341122592802132989754567255132253863579141/69
+371377009148175174079939980762865583621192509024*x^7 - 727051399465472071466
+584875687608034262747495330744101465/138742754018296350348159879961525731167
+242385018048*x^6 - 3129326368290877748444147730987503938931452439481078643/4
+88530824008085740662534788596921588617050651472*x^5 - 7853272245711394369054
+234379510390729335994591529342187/867142212614352189675999249759535819795264
+9063628*x^4 + 418784664422249354368655241667072778856572355632199668/2167855
+531535880474189998124398839549488162265907*x^3 + 397946744104206949165008228
+423407476436082933606062359/867142212614352189675999249759535819795264906362
+8*x^2 + 251390094927160964464702475829596929962821546628958/1970777755941709
+52199090738581712686317105660537*x - 120287049627746942543990932319832935260
+559012851341/2167855531535880474189998124398839549488162265907], [x^9 - 48*x
+^8 + 174*x^7 + 14126*x^6 - 161823*x^5 + 385746*x^4 - 6675*x^3 - 36000*x^2 - 
+2448*x + 64, 178178188655743046634447029929429775436905591698460680780932100
+496865/205964516518161148365604356303023392123365913321634271403375289329924
+48*x^26 - 585564521969460077357680976548963715611696576170565261427516488163
+5/2574556456477014354570054453787792401542073916520428392542191116624056*x^2
+5 - 10687142837936058552014304459469637843322787822700061498629424693789001/
+10298225825908057418280217815151169606168295666081713570168764466496224*x^24
+ - 1402835507304473951065987980845947097369430580835560692715331310893721/51
+49112912954028709140108907575584803084147833040856785084382233248112*x^23 + 
+1012900128203402102473485745427750041226576306008764551073032446477869925/20
+596451651816114836560435630302339212336591332163427140337528932992448*x^22 +
+ 328919581448673536238320354914122819802889778663415605005969834124974355/10
+298225825908057418280217815151169606168295666081713570168764466496224*x^21 -
+ 24790579215115629643252807365825638835640678700733064482849780918179925523/
+20596451651816114836560435630302339212336591332163427140337528932992448*x^20
+ - 5832173386907353146877015446206257884156389895865445571205228489686060047
+/5149112912954028709140108907575584803084147833040856785084382233248112*x^19
+ + 1696111600359040840909273306891850706643173999758616474941310792478712626
+35/10298225825908057418280217815151169606168295666081713570168764466496224*x
+^18 + 9697080497106399861191897725886740668361334064940381998717473365130732
+1643/5149112912954028709140108907575584803084147833040856785084382233248112*
+x^17 - 327120630399511077813977748140132153294726625367687415565755220838576
+526321/257455645647701435457005445378779240154207391652042839254219111662405
+6*x^16 - 1138999673083302844914685818871872035600808950711579060183082860884
+5047711/72522717083859559283663505740501194409635884972406433592737777933072
+*x^15 + 13972754134719749413326585357917408124657913013131895961405820974299
+46258661/2574556456477014354570054453787792401542073916520428392542191116624
+056*x^14 + 65851885655711698415709986878635451394614593495829489112190966898
+67146999399/1029822582590805741828021781515116960616829566608171357016876446
+6496224*x^13 - 2720653637087195067556980889397993529153592086209825470898788
+5001814753969751/20596451651816114836560435630302339212336591332163427140337
+528932992448*x^12 - 34293755135701575804420198501418430750514382699868104822
+77918479570182936201/2574556456477014354570054453787792401542073916520428392
+542191116624056*x^11 + 22835826439820082051418422065018962048491089627612609
+78426528451857752896183/1287278228238507177285027226893896200771036958260214
+196271095558312028*x^10 + 14947887342855499886534738267886765943220050885236
+006331762672201405988749873/102982258259080574182802178151511696061682956660
+81713570168764466496224*x^9 - 2312626178466862669742148746078905849305846413
+1200007005664027600126057045919/20596451651816114836560435630302339212336591
+332163427140337528932992448*x^8 - 445576096474071332751690815863648261275689
+0661907234201680058799794426791471/51491129129540287091401089075755848030841
+47833040856785084382233248112*x^7 + 4624705944018551774607971268900030697728
+951454822215623885390104059701776587/205964516518161148365604356303023392123
+36591332163427140337528932992448*x^6 + 8599900915430274139150759565472944105
+816722893187288625260353285457226639/362613585419297796418317528702505972048
+17942486203216796368888966536*x^5 + 8034752231162982963190541106207649014904
+4362450082543846760021463982037425/25745564564770143545700544537877924015420
+73916520428392542191116624056*x^4 - 8922522963571710266599529622404778968906
+989375240265231503487119143065023/128727822823850717728502722689389620077103
+6958260214196271095558312028*x^3 - 21144874691331414081453526729989154088688
+08374418990056015683781028655257/1287278228238507177285027226893896200771036
+958260214196271095558312028*x^2 - 192483049971516500402391596044759086577617
+18632176246503489249504631097/3218195570596267943212568067234740501927592395
+65053549067773889578007*x + 772370447994581394351475349432365497130635003149
+339294507791334368751/321819557059626794321256806723474050192759239565053549
+067773889578007], [x^9 + 99*x^8 + 1440*x^7 - 31531*x^6 + 21918*x^5 + 657516*
+x^4 - 412728*x^3 - 101232*x^2 - 4704*x + 64, -116775785290993381144377305824
+508342289373090070606232293294852385637399/129654663148182442896147942292753
+22534165884243596877384842474463318746016*x^26 + 552166192249122841691117037
+564558605113925565497001189328618239765847369/285240258926001374371525473044
+057095751649453359131302466534438193012412352*x^25 + 30797646004445989526038
+9354175175129155748185908023135837408937764111256765/28524025892600137437152
+5473044057095751649453359131302466534438193012412352*x^24 + 9548937104748672
+2067604217301278561093619276908698815411389806740649145115/28524025892600137
+4371525473044057095751649453359131302466534438193012412352*x^23 - 1457190262
+1536833762306452679243678193359782225230874835173123442581019901013/28524025
+8926001374371525473044057095751649453359131302466534438193012412352*x^22 - 5
+073118360294215627279555750716078075739573108615793775479122727454113175915/
+142620129463000687185762736522028547875824726679565651233267219096506206176*
+x^21 + 222288182831921152527413812511774822190998494079156377266028775168506
+19754407/1782751618287508589822034206525356848447809083494570640415840238706
+3275772*x^20 + 3192268104418169501707726526426130696821166172727519861651402
+5007215961315745/25930932629636488579229588458550645068331768487193754769684
+948926637492032*x^19 - 48440665919063213836160970027368071803912510753947878
+69169605809477979881385121/2852402589260013743715254730440570957516494533591
+31302466534438193012412352*x^18 - 576157032248853180780745828304852594090428
+3898322254036932624541470676168884155/28524025892600137437152547304405709575
+1649453359131302466534438193012412352*x^17 + 3708017260965446785241454380744
+9789527054845951753559142553988351117586485322367/28524025892600137437152547
+3044057095751649453359131302466534438193012412352*x^16 + 6690532737232158463
+06739724709248108090991883324363234427195339752480326510401/4017468435577484
+146077823564000804165516189483931426795303301946380456512*x^15 - 15647336223
+3854132825610078371890840094526749287076481504357502173381907576765179/28524
+0258926001374371525473044057095751649453359131302466534438193012412352*x^14 
+- 19058607474696748298021760840723277900858668670358109594140791035299241669
+8366147/28524025892600137437152547304405709575164945335913130246653443819301
+2412352*x^13 + 3760501130250599632842150837484828093032120941847886194504246
+34750660671639395111/2852402589260013743715254730440570957516494533591313024
+66534438193012412352*x^12 + 194618130808416391775814659595407495648049868134
+830027990122903410205676919898881/142620129463000687185762736522028547875824
+726679565651233267219096506206176*x^11 - 12487466875264921887021926610814574
+6988791012998543786000090321197456260187496439/71310064731500343592881368261
+014273937912363339782825616633609548253103088*x^10 - 10302912988461472796984
+0085996371342275695248315537711218283616740029260681078825/71310064731500343
+592881368261014273937912363339782825616633609548253103088*x^9 + 391226077488
+60089947316569637897236944176473734384191179581404758131894035826045/3565503
+2365750171796440684130507136968956181669891412808316804774126551544*x^8 + 23
+6791436304921675278668511641876113141565152478740335854961221227114230880915
+635/285240258926001374371525473044057095751649453359131302466534438193012412
+352*x^7 - 630310047255703733187591942383343386185406018610210158245704121817
+78622927671947/2852402589260013743715254730440570957516494533591313024665344
+38193012412352*x^6 - 4414056259818143328633827998833799596044572702988049946
+80425955472862036771007/2008734217788742073038911782000402082758094741965713
+397651650973190228256*x^5 - 944276862193647191236572777447563272665598810128
+681991559924394348134257090533/356550323657501717964406841305071369689561816
+69891412808316804774126551544*x^4 + 1152931981696616284615891384450236658814
+02268276620622776231400099174946122829/1782751618287508589822034206525356848
+4478090834945706404158402387063275772*x^3 + 63911647394393256923010357440864
+74495628949058051998012436427850094434819152/4456879045718771474555085516313
+392121119522708736426601039600596765818943*x^2 + 463132299442654665686412246
+504660574754698118158865041528435016000535480469/891375809143754294911017103
+2626784242239045417472853202079201193531637886*x - 9208473384290981762320762
+179065375898275268983750393346913361377172631986/445687904571877147455508551
+6313392121119522708736426601039600596765818943], [x^9 - 36*x^8 - 1581*x^7 - 
+2929*x^6 + 6342*x^5 + 13140*x^4 - 5496*x^3 - 14256*x^2 - 1632*x + 64, -91264
+312653239983701677339387771012871773982715072549763565121697/313893413349304
+9088141512594511220295539662206569996472570775568064*x^26 + 2745582646439785
+84477295461966060388344340650830038564070601043341/3452827546842353996955663
+8539623423250936284272269961198278531248704*x^25 + 1203965815809614488879224
+74044847036629177847017731084385042343270615/3452827546842353996955663853962
+3423250936284272269961198278531248704*x^24 + 3031925519530116108814534260803
+9392794060378706535173405700888801025/34528275468423539969556638539623423250
+936284272269961198278531248704*x^23 - 28518954338528671683342728763611931534
+10321035165476507650507925792737/1726413773421176998477831926981171162546814
+2136134980599139265624352*x^22 - 1820763297285515713967265577848039056258664
+187729519639125160621742915/172641377342117699847783192698117116254681421361
+34980599139265624352*x^21 + 139553735144038802830612649236255728367390088187
+505648928779037635668371/345282754684235399695566385396234232509362842722699
+61198278531248704*x^20 + 129686606522264455422841162767307824993100156224193
+240593913110845200739/345282754684235399695566385396234232509362842722699611
+98278531248704*x^19 - 173532953655053765496231468194247392183807099820540136
+027251907055479893/313893413349304908814151259451122029553966220656999647257
+0775568064*x^18 - 2157477331235938546809646641030311758841162089998282663463
+720014029046761/345282754684235399695566385396234232509362842722699611982785
+31248704*x^17 + 147200639187815395903446627577382567028901847339250410912658
+02436213941351/3452827546842353996955663853962342325093628427226996119827853
+1248704*x^16 + 1795722965295458382045568195813715108170495250683439616774057
+6446505837301/34528275468423539969556638539623423250936284272269961198278531
+248704*x^15 - 62861305847846745699518468082664906156499345022513346882708829
+186805129423/345282754684235399695566385396234232509362842722699611982785312
+48704*x^14 - 726990034788507410471959465283929848133294060418304485664994122
+69535053991/3452827546842353996955663853962342325093628427226996119827853124
+8704*x^13 + 7655821445485614815740348581605904625416871871803841414070808687
+6767165807/17264137734211769984778319269811711625468142136134980599139265624
+352*x^12 + 18740280729290702376768166717427932090712009519308402067176639829
+485666773/431603443355294249619457981745292790636703553403374514978481640608
+8*x^11 - 5153830538944909309488705674760851910658376375732381992123114243343
+1877201/8632068867105884992389159634905855812734071068067490299569632812176*
+x^10 - 804182804496969247263648152816681379666915719606620345867191595892304
+41945/17264137734211769984778319269811711625468142136134980599139265624352*x
+^9 + 13150072800085550850048805482319300205089752530910115876141449706340982
+3483/34528275468423539969556638539623423250936284272269961198278531248704*x^
+8 + 944269737518033467982150142657319754481410403815125791153115299566510871
+95/34528275468423539969556638539623423250936284272269961198278531248704*x^7 
+- 13813955213489775578706620836640970336210852546815268784537605184487441961
+/17264137734211769984778319269811711625468142136134980599139265624352*x^6 - 
+802271934030959556263962373968189966415033784342954215470502046665383015/107
+9008608388235624048644954363231976591758883508436287446204101522*x^5 - 72451
+7100962773427156670692738379583851925682226419513627174684155584211/86320688
+67105884992389159634905855812734071068067490299569632812176*x^4 + 2418167612
+5968096635225561790528764696326074242511085050335949381946643/10790086083882
+35624048644954363231976591758883508436287446204101522*x^3 + 1004157410376915
+7648641064714562586079600589195151968367072242288370801/21580172167764712480
+97289908726463953183517767016872574892408203044*x^2 + 1711141209765523604984
+48866349636866704202518064544969032341737623527/1079008608388235624048644954
+363231976591758883508436287446204101522*x - 35031118434103246442769646163584
+06962357832888273571276711411976738/5395043041941178120243224771816159882958
+79441754218143723102050761], [x^9 - 96*x^8 + 2913*x^7 - 28705*x^6 + 12234*x^
+5 + 175548*x^4 - 110904*x^3 + 24528*x^2 - 2208*x + 64, 150464597533827861737
+42015461484622984451943452088362123746955056657/3117448019913018583701561488
+164299719684030835200018606598334807241824*x^26 - 65898798875673847752724420
+76936563048347586086638330235490276563089/6234896039826037167403122976328599
+439368061670400037213196669614483648*x^25 - 49596040460286201565423234730781
+11384044599111339788219400805892643411/8572982054760801105179294092451824229
+131084796800051168145420719915016*x^24 - 12141603218369585496713463730946078
+783160573119585354378652218197968741/685838564380864088414343527396145938330
+48678374400409345163365759320128*x^23 + 469246396711786900856367874322991480
+727862807000605790905003330530467621/171459641095216022103585881849036484582
+62169593600102336290841439830032*x^22 + 812026684590436831198902813308877329
+84388462768190024292819119726682403/4286491027380400552589647046225912114565
+542398400025584072710359957508*x^21 - 57252485621224171053610790079053074884
+01540735497592584645496979991612211/8572982054760801105179294092451824229131
+084796800051168145420719915016*x^20 - 45025655528787062067308044620887856565
+260162652822461630327103538277392663/685838564380864088414343527396145938330
+48678374400409345163365759320128*x^19 + 311812245399480577380344713687445211
+651446733012893871452158592110284838411/342919282190432044207171763698072969
+16524339187200204672581682879660064*x^18 + 738824930056479941811559746967838
+338282351986280105053378936482919932569623/685838564380864088414343527396145
+93833048678374400409345163365759320128*x^17 - 119286613136994729940998894984
+4922438769660844714680847900869826335559755609/17145964109521602210358588184
+903648458262169593600102336290841439830032*x^16 - 85718291300597573998646682
+684253312757490588288040002137889973360241979299/965969808987132518893441587
+881895687789418005273245202044554447314368*x^15 + 10060227424829357358617480
+425423345222601099453443979677539887162221414563995/342919282190432044207171
+76369807296916524339187200204672581682879660064*x^14 + 221440083349784089012
+7701050423892962534315935571623448333454890318596338405/62348960398260371674
+03122976328599439368061670400037213196669614483648*x^13 - 150971208159657906
+9851149965333271398896932073879377746980651656779266372275/21432455136902002
+76294823523112956057282771199200012792036355179978754*x^12 - 123841349445716
+65906902737053577847916500272875472139962375420035554510095353/1714596410952
+1602210358588184903648458262169593600102336290841439830032*x^11 + 1601727606
+4226742567044256659232798000673939983517328094316742223158236359997/17145964
+109521602210358588184903648458262169593600102336290841439830032*x^10 + 13027
+667371741636275282946942427179589667888073059834172920586922129479988811/171
+45964109521602210358588184903648458262169593600102336290841439830032*x^9 - 1
+249795050842533952607898011139041343931767458160962146201655054789209199919/
+2143245513690200276294823523112956057282771199200012792036355179978754*x^8 -
+ 270605827030395561198470270920088587032771851486899902221598365663845253040
+9/6234896039826037167403122976328599439368061670400037213196669614483648*x^7
+ + 4986554526140487336384634300870192208642389731097457866557480606164182349
+89/4286491027380400552589647046225912114565542398400025584072710359957508*x^
+6 + 691102780126791419969141024144208861749974585808631098263191971324745142
+9/60373113061695782430840099242618480486838625329577825127784652957148*x^5 +
+ 244477829204093556057635888982776362030706875207811209894915898021554766473
+/17145964109521602210358588184903648458262169593600102336290841439830032*x^4
+ - 7089009901061870418772873999715539202969615640615901178397832665649979865
+/2143245513690200276294823523112956057282771199200012792036355179978754*x^3 
+- 300413556215192122321647509858218440648415571439716751070265850713055549/3
+89681002489127322962695186020537464960503854400002325824791850905228*x^2 - 6
+2607373637686066421772012245577706993957689823422569362072179275009919/21432
+45513690200276294823523112956057282771199200012792036355179978754*x + 139033
+8607723961974464380565605508734659040794131017136124182385578780/10716227568
+45100138147411761556478028641385599600006396018177589989377], [x^27 - 120*x^
+25 - 63*x^24 + 5673*x^23 + 5181*x^22 - 138003*x^21 - 167184*x^20 + 1865730*x
+^19 + 2668613*x^18 - 14070078*x^17 - 21889917*x^16 + 57688596*x^15 + 8948208
+9*x^14 - 132575217*x^13 - 190829625*x^12 + 164200812*x^11 + 215956974*x^10 -
+ 86796519*x^9 - 129504396*x^8 + 1575183*x^7 + 32931993*x^6 + 9928740*x^5 + 4
+9968*x^4 - 372144*x^3 - 50736*x^2 - 1344*x + 64, x]]
+15: [[x, 0], [x^2 - 9*x + 26, 1/4*x^8 - 1/4*x^6 + 5/4*x^4 - 7/4*x^2 + 13/2],
+ [x^4 + 9*x^2 + 26, -1/8*x^11 + 1/4*x^9 - 3/4*x^7 + x^5 - 13/8*x^3 + 1/4*x],
+ [x^6 + 3*x^5 + 8*x^4 + 14*x^3 + 21*x^2 + 23*x + 26, -x^2], [x^6 - 6*x^5 + 1
+8*x^4 - 36*x^3 + 53*x^2 - 52*x + 26, -1/16*x^10 - 1/8*x^9 + 1/8*x^8 + 3/8*x^
+7 - 3/8*x^6 - 5/8*x^5 + 1/2*x^4 + 5/8*x^3 - 13/16*x^2 - 3/4*x + 13/8], [x^6 
+- 6*x^5 + 18*x^4 - 36*x^3 + 53*x^2 - 52*x + 26, -1/16*x^10 + 1/8*x^9 + 1/8*x
+^8 - 3/8*x^7 - 3/8*x^6 + 5/8*x^5 + 1/2*x^4 - 5/8*x^3 - 13/16*x^2 + 3/4*x + 1
+3/8], [x^12 - 3*x^10 + 8*x^8 - 14*x^6 + 21*x^4 - 23*x^2 + 26, x]]
+16: [[x + 8, -8], [x^2 - 128*x + 432, -63853319923935875733112922371/1966774
+45849124016855404926596330*x^23 - 347020193457008939020832368879/13111829723
+2749344570269951064220*x^22 + 1982575716076299992923868753999/19667744584912
+4016855404926596330*x^21 + 19493268324409893257675589592367/1966774458491240
+16855404926596330*x^20 - 14932100440413369856941006855715/786709783396496067
+42161970638532*x^19 - 67744878698721403584811614689019/437060990775831148567
+56650354740*x^18 + 174898430108209143532696301394817/65559148616374672285134
+975532110*x^17 + 1091659012660063950399753242337676/983387229245620084277024
+63298165*x^16 - 31871852329624596113344375061168/134710579348715080037948579
+8605*x^15 - 244046691963636257474551069780003/855119329778800073284369246071
+0*x^14 + 16914117747207052855867936091832763/1966774458491240168554049265963
+30*x^13 + 352042453850719240333222491668827/39335489169824803371080985319266
+*x^12 - 13741518852590675208071626279654969/98338722924562008427702463298165
+*x^11 + 1241568198829120261307917362777269/13563961782698208058993443213540*
+x^10 + 9654446830505254415463413843706103/196677445849124016855404926596330*
+x^9 - 11415227965484593081409990734676318/98338722924562008427702463298165*x
+^8 + 32598724049685323796271849547813467/393354891698248033710809853192660*x
+^7 - 528773939007352577000940657428188/19667744584912401685540492659633*x^6 
+- 30013863579657220176453366895779967/196677445849124016855404926596330*x^5 
++ 8395004490545366622815116016736529/196677445849124016855404926596330*x^4 -
+ 4005311266907432823376705250987299/43706099077583114856756650354740*x^3 - 1
+0434735356465814055105764148854887/393354891698248033710809853192660*x^2 + 3
+443184498214169462961749332429279/65559148616374672285134975532110*x + 15116
+602831050450769752445128065041/196677445849124016855404926596330], [x^3 + 8*
+x^2 - 224*x + 832, -25167798587599067474576627480079175785311/12391180178858
+71369570044909938690767230139380*x^23 - 629381005050427357633379262860193288
+59863/371735405365761410871013472981607230169041814*x^22 + 73063468282620141
+6701968149575006679565931/1239118017885871369570044909938690767230139380*x^2
+1 + 10326356023409929244005814719241662887497083/165215735718116182609339321
+3251587689640185840*x^20 - 9527595290655738777730304003808686117484103/92933
+8513414403527177533682454018075422604535*x^19 - 7182269062008195914677380953
+9929284189557075/743470810731522821742026945963214460338083628*x^18 + 203704
+8120273075143767701314314087192010861299/14869416214630456434840538919264289
+206761672560*x^17 + 2047597832114659016884010941347229611101357321/297388324
+2926091286968107783852857841352334512*x^16 - 5970672241502210208992454142441
+947745762049/5092265826928238505082376342213797673548518*x^15 - 612073735395
+038122826306433768432119908872467/323248178578922965974794324331832374060036
+360*x^14 + 1121147049647191018123239530328482820809271433/309779504471467842
+392511227484672691807534845*x^13 + 19202612990354992415990096137657665767199
+83457/826078678590580913046696606625793844820092920*x^12 - 31836941441515018
+097310599095971704502960344507/743470810731522821742026945963214460338083628
+0*x^11 - 429923037654339845298230104518334151252052071/256369245079835455773
+112739987315331151063320*x^10 - 66066567140940412290718921432354966580277235
+1/2478236035771742739140089819877381534460278760*x^9 + 845107242349572808223
+8958177949283169510746053/1652157357181161826093393213251587689640185840*x^8
+ + 571456667518488282535990065275685445420915711/743470810731522821742026945
+963214460338083628*x^7 - 40987718259023987123110871072594114727149233283/743
+4708107315228217420269459632144603380836280*x^6 - 12442975498817761454150693
+158095724275856679829/1652157357181161826093393213251587689640185840*x^5 - 5
+787931825323004141890817142718039502657962799/123911801788587136957004490993
+8690767230139380*x^4 + 6336476749528282806129322474762881455933674451/743470
+8107315228217420269459632144603380836280*x^3 - 36998997560438657374619789272
+480718742278003/495647207154348547828017963975476306892055752*x^2 - 19368530
+540770431766052137116415061665505291693/148694162146304564348405389192642892
+06761672560*x + 22661897566194450311787210499625704062202984207/495647207154
+3485478280179639754763068920557520], [x^3 + 8*x^2 - 288*x + 832, 68364404746
+903530024663065757008379579955/581171881309459312442391330267291824333509443
+63*x^23 + 407018094398282215126229404324184695928621/11623437626189186248847
+8266053458364866701888726*x^22 - 1846342647374143836526239251659707362713159
+1/232468752523783724976956532106916729733403777452*x^21 - 104319145852502097
+726417979402988924882380925/929875010095134899907826128427666918933615109808
+*x^20 + 544645884665062890915770180576266315439973353/2324687525237837249769
+56532106916729733403777452*x^19 - 571087973351048776307830735034211879963335
+57/232468752523783724976956532106916729733403777452*x^18 - 32634449384411587
+311921727417502938376980295137/929875010095134899907826128427666918933615109
+808*x^17 + 5109984413175970220050697757257255410691829557/103319445566126099
+989758458714185213214846123312*x^16 + 63834497382931344472264386071524333891
+257697/265375288269159503398352205601503116134022577*x^15 - 3180032649381151
+67955428829968481196292272256965/4649375050475674499539130642138334594668075
+54904*x^14 - 32758026171964994776099258362315044592758611151/116234376261891
+862488478266053458364866701888726*x^13 + 13950580052378885880698431611713106
+31447183684601/464937505047567449953913064213833459466807554904*x^12 - 36713
+8669921305627112153919132682656188637214155/15497916834918914998463768807127
+7819822269184968*x^11 - 9344350691334055924155794611473267518951813359/17813
+69751140105172237214805416986434738726264*x^10 + 445418222695092774567764884
+0473205808434425052699/464937505047567449953913064213833459466807554904*x^9 
++ 532735991282814263758070069339436465634811057629/9298750100951348999078261
+28427666918933615109808*x^8 - 3097041132005892702978963412137429379401585681
+943/232468752523783724976956532106916729733403777452*x^7 + 47107732208383237
+91495995968582774999138838194793/4649375050475674499539130642138334594668075
+54904*x^6 + 4680094261142940356595699040461088668345290745295/92987501009513
+4899907826128427666918933615109808*x^5 - 31772068877699035216127743260740332
+40872898935249/232468752523783724976956532106916729733403777452*x^4 + 267602
+3300754930565238673841878460022598929454265/46493750504756744995391306421383
+3459466807554904*x^3 + 1973867445762173054028454079380253917823814402527/464
+937505047567449953913064213833459466807554904*x^2 - 452745708031015599250534
+7554656042387453269590969/929875010095134899907826128427666918933615109808*x
+ + 4513082178431793136859575419012685513290234695549/92987501009513489990782
+6128427666918933615109808], [x^3 + 8*x^2 - 176*x + 256, -1291795034206608863
+62191055636986486401/5348195879528384571737930341960898650842570*x^23 - 1017
+388578405808067580350543227199930467/534819587952838457173793034196089865084
+2570*x^22 + 17293020253475804663513339324112592886509/2139278351811353828695
+1721367843594603370280*x^21 + 153591004975668357616122953689943413579981/213
+92783518113538286951721367843594603370280*x^20 - 195257710452618747974251461
+30654489573679/1188487973228529904830651187102421922409460*x^19 - 2394000234
+493207441559886062250365661371359/213927835181135382869517213678435946033702
+80*x^18 + 2548502849189477343363734355824249444564159/1069639175905676914347
+5860683921797301685140*x^17 + 16559618779681825783245163882758786645690143/2
+1392783518113538286951721367843594603370280*x^16 - 2253812945412770680253754
+6686894078901186431/10696391759056769143475860683921797301685140*x^15 - 2386
+49100524803675730685640840718761757387/1550201704211125962822588504916202507
+49060*x^14 + 83764510804010183344194485774562389670847129/106963917590567691
+43475860683921797301685140*x^13 - 724007625467909157262967431788072319623749
+8/2674097939764192285868965170980449325421285*x^12 - 65813656109345475127909
+062133316902447218653/5348195879528384571737930341960898650842570*x^11 + 429
+75845367520331985190157408978391239733028/2674097939764192285868965170980449
+325421285*x^10 + 7169267563324905872474977052289419196241397/213927835181135
+38286951721367843594603370280*x^9 - 4085299109515478220857392480376490666731
+84261/21392783518113538286951721367843594603370280*x^8 + 1605463393210303557
+3446511836165145052704493/891365979921397428622988390326816441807095*x^7 - 1
+1976704743917628507434028345515781505450429/23769759464570598096613023742048
+43844818920*x^6 - 337905522070797040728739459350043422683071433/213927835181
+13538286951721367843594603370280*x^5 + 1905156900839662827301096172627475123
+96974077/10696391759056769143475860683921797301685140*x^4 - 1679791704686921
+31031722771982103131653648097/10696391759056769143475860683921797301685140*x
+^3 - 126098373977878421269087002029319424484684681/2139278351811353828695172
+1367843594603370280*x^2 + 114130769395071974525095359635352451213259051/1069
+6391759056769143475860683921797301685140*x - 1852565384501048222774521568355
+2153251778309/4278556703622707657390344273568718920674056], [x^4 + 8*x^3 + 2
+4*x^2 + 24*x + 16, 797586616242660780670544616783278840027/93203209582363941
+194709877980669191536824720*x^23 + 416930024946098786179648458855064651064/5
+825200598897746324669367373791824471051545*x^22 - 76483139002173486712024594
+82579537616379/31067736527454647064903292660223063845608240*x^21 - 490116417
+570507717782256988318382238102971/186406419164727882389419755961338383073649
+440*x^20 + 401479600928580479150821819436198970287551/9320320958236394119470
+9877980669191536824720*x^19 + 418723798220495877200291178100828144075573/103
+55912175818215688301097553407687948536080*x^18 - 110146073037221150383706821
+65405081545163563/186406419164727882389419755961338383073649440*x^17 - 52942
+801431544551995159688249513710795991263/186406419164727882389419755961338383
+073649440*x^16 + 12242700330402727625801859343924334575476719/23300802395590
+985298677469495167297884206180*x^15 + 66165807124282508781305344105235930331
+108373/93203209582363941194709877980669191536824720*x^14 - 22950727122632699
+79924417185269170856043297/1294489021977276961037637194175960993567010*x^13 
+- 19146732166941600924433106357183343366881023/93203209582363941194709877980
+669191536824720*x^12 + 35890897250549504054728654280046515364193021/15533868
+263727323532451646330111531922804120*x^11 - 18615941477917969938728982456122
+3869178185397/93203209582363941194709877980669191536824720*x^10 + 4163070985
+32453007601505238856238804612211/647244510988638480518818597087980496783505*
+x^9 + 287757327733184487152262147810851296810166311/186406419164727882389419
+755961338383073649440*x^8 - 68358421103456607009255404623620124209598979/233
+00802395590985298677469495167297884206180*x^7 + 1018360144986811239148930243
+63517723739380599/46601604791181970597354938990334595768412360*x^6 + 6648397
+6998090365966441619595197217331832127/20711824351636431376602195106815375897
+072160*x^5 + 48087864063875215970746866353480398693077223/932032095823639411
+94709877980669191536824720*x^4 + 2336146371036484790434237269814921810505094
+7/5825200598897746324669367373791824471051545*x^3 + 319288179836847038859497
+79257443509042046649/46601604791181970597354938990334595768412360*x^2 + 3603
+65600143693048930795243805489756245941713/1864064191647278823894197559613383
+83073649440*x - 13773231328822549892612253248252528613609411/414236487032728
+6275320439021363075179414432], [x^4 + 8*x^3 + 24*x^2 + 24*x + 16, -488736511
+558300125692146988636796617179/95578380347448095121483086750097588285896736*
+x^23 - 2051932177967284799581018723934835044459/5309910019302671951193504819
+4498660158831520*x^22 + 14517814543943288309153164167882045028759/7964865028
+9540079267902572291747990238247280*x^21 + 1392663827938882169506189996340090
+27125571/95578380347448095121483086750097588285896736*x^20 - 923937492475388
+986204658550803232214661557/238945950868620237803707716875243970714741840*x^
+19 - 10631029772526434455295915370434401838939803/47789190173724047560741543
+3750487941429483680*x^18 + 13232150180027486337799985973119575983730111/2389
+45950868620237803707716875243970714741840*x^17 + 171179212934529535946568728
+17556318499321911/119472975434310118901853858437621985357370920*x^16 - 10970
+3774841359229762900118681090656000872273/23894595086862023780370771687524397
+0714741840*x^15 - 38171088413014548603260111248314077702032/2164365496998371
+71923648294271054321299585*x^14 + 193992073164158769423639985280476876083008
+43/13274775048256679877983762048624665039707880*x^13 - 485909976509256122795
+4831797349179479998895/5973648771715505945092692921881099267868546*x^12 - 74
+7694190985765990865555663083248713479035647/47789190173724047560741543375048
+7941429483680*x^11 + 436434976101863543377189366772544232044840017/159297300
+579080158535805144583495980476494560*x^10 - 69114826050221450249069274996582
+793300440569/39824325144770039633951286145873995119123640*x^9 - 590684763980
+745767673635614634720526563044811/477891901737240475607415433750487941429483
+680*x^8 + 379779415104260449606302623291513345166050495/95578380347448095121
+483086750097588285896736*x^7 - 8819608414588227103160201248693726722042285/2
+654955009651335975596752409724933007941576*x^6 - 140521448998820557119363816
+656940918131422723/79648650289540079267902572291747990238247280*x^5 + 126216
+8829341241906720696926886552986776800959/47789190173724047560741543375048794
+1429483680*x^4 - 57844968154351950070234769626930248895643429/15929730057908
+015853580514458349598047649456*x^3 + 881301280989954919647791802483929254073
+024561/477891901737240475607415433750487941429483680*x^2 + 29243370469732450
+440677221435538723375090369/19912162572385019816975643072936997559561820*x -
+ 47011330840295019461394642343977534957826491/265495500965133597559675240972
+49330079415760], [x^4 + 8*x^3 + 24*x^2 + 248*x + 1744, 219672788337945683139
+27675097089313745104160955047/1014922463372614382177445798597477834284022303
+635334456*x^23 + 451687949693457121990886779621285611194341334145487/2537306
+158431535955443614496493694585710055759088336140*x^22 - 13382516981582247552
+47406174486075310765498060572731/2029844926745228764354891597194955668568044
+607270668912*x^21 - 4223856776965630429359975395444009155302560885943337/634
+326539607883988860903624123423646427513939772084035*x^20 + 51461276114821276
+38912060431639907463590080095940249/4228843597385893259072690827489490976183
+42626514722690*x^19 + 105936163610012076872125349335497405382040687559737968
+9/10149224633726143821774457985974778342840223036353344560*x^18 - 3452344986
+70983358763303874528795477249298778155336817/2029844926745228764354891597194
+955668568044607270668912*x^17 - 19182792473542713025398308867987412778610410
+19894694197/2537306158431535955443614496493694585710055759088336140*x^16 + 1
+05629504409321431307553638640481644623085206126643607/6951523721730235494366
+0671136813550293426185180502360*x^15 + 3784770371195205864845116670473718252
+6338800845507061/18386276510373448952489960119519525983406201152814030*x^14 
+- 5667555402115990253347794530780260472114097324564393497/101492246337261438
+2177445798597477834284022303635334456*x^13 - 6641638794910391960936620673185
+168682231598789447099287/507461231686307191088722899298738917142011151817667
+2280*x^12 + 24049764175786070793476825475231470529143359000282678811/2537306
+158431535955443614496493694585710055759088336140*x^11 - 16008808436687663096
+8087023738250605484861574103897979/34997326323193599385429165468878546009793
+872539149464*x^10 - 48963475354535881489960109688076686948554461694434405349
+/10149224633726143821774457985974778342840223036353344560*x^9 + 170107818254
+2242003068275881477787526790107232819436613/25373061584315359554436144964936
+9458571005575908833614*x^8 - 11539471305543338750986400623650399774098712516
+10932353/422884359738589325907269082748949097618342626514722690*x^7 + 709764
+703822713854140140055431911423015308135885895003/338307487790871460725815266
+1991592780946741012117781520*x^6 + 23208255318049227426850418921476949716547
+254062896852519/2537306158431535955443614496493694585710055759088336140*x^5 
++ 267452972565485887755305340342359338986825994184278195/1014922463372614382
+177445798597477834284022303635334456*x^4 + 230801947204155727991302777461721
+57190069965693854287359/5074612316863071910887228992987389171420111518176672
+280*x^3 + 8746785179662579050668569130655251913092563603040368361/1014922463
+3726143821774457985974778342840223036353344560*x^2 + 38066887924252694224202
+134807737382611963682052887562207/101492246337261438217744579859747783428402
+23036353344560*x - 16613935596769377706629903068976014792030072749138987127/
+2537306158431535955443614496493694585710055759088336140], [x^4 + 8*x^3 + 24*
+x^2 + 24*x + 16, 69447805351768765373607565352615156828747/18417623689593630
+845389455585205871067752496480*x^23 + 48640037439929590831569926526232920584
+1653/18417623689593630845389455585205871067752496480*x^22 - 4305686882583414
+087262398642542662585554199/27626435534390446268084183377808806601628744720*
+x^21 - 241133464788758001152646064492803285353837/23022029611992038556736819
+4815073388346906206*x^20 + 5549107294305918846419681800867339542504913/15348
+01974132802570449121298767155922312708040*x^19 + 917486582561884963358107715
+313211136054340247/55252871068780892536168366755617613203257489440*x^18 - 29
+27354856021208654007261816804903495079660893/5525287106878089253616836675561
+7613203257489440*x^17 - 6148011192251326623690910612187753151553441693/55252
+871068780892536168366755617613203257489440*x^16 + 16659158095393614901761014
+7373263173924359927/378444322388910222850468265449435706871626640*x^15 + 421
+6098805893565312642385142134546955723496939/27626435534390446268084183377808
+806601628744720*x^14 - 2675983732556069521507138217026200808136829743/172665
+2220899402891755261461113050412601796545*x^13 + 1788310169033395499344701721
+175832375461702153/3069603948265605140898242597534311844625416080*x^12 + 444
+52081743594001532680179309546559943546054917/1841762368959363084538945558520
+5871067752496480*x^11 - 4319567159583304716923734589250498326591184061/19052
+71416164858363316150577779917696664051360*x^10 - 142504104702325246299495674
+47312513258558075343/27626435534390446268084183377808806601628744720*x^9 + 3
+69708578125450862509474360053533543924733249/1918502467666003213061401623458
+94490289088505*x^8 - 11967368496526327137675654041396737888341317791/6139207
+896531210281796485195068623689250832160*x^7 + 333348487477700398549377560262
+74482346958723413/27626435534390446268084183377808806601628744720*x^6 + 3868
+30470483318083897419896567302981752609111/2412789129641087010313029115965834
+63769683360*x^5 - 18842604735018897070130480283341577785106480877/6139207896
+531210281796485195068623689250832160*x^4 + 209390199785183771570549524271665
+4690321405155/1841762368959363084538945558520587106775249648*x^3 - 123877769
+054334108036625667326870432576253219071/552528710687808925361683667556176132
+03257489440*x^2 - 7925487180434948895501097569391545353202549677/36835247379
+18726169077891117041174213550499296*x - 267455378231931860912020044636251354
+79025638959/11050574213756178507233673351123522640651497888], [x^6 + 8*x^5 -
+ 224*x^4 + 3584*x^2 + 2048*x - 4096, -21967278833794568313927675097089313745
+104160955047/1522383695058921573266168697896216751426033455453001684*x^23 - 
+451687949693457121990886779621285611194341334145487/380595923764730393316542
+1744740541878565083638632504210*x^22 + 1338251698158224755247406174486075310
+765498060572731/3044767390117843146532337395792433502852066910906003368*x^21
+ + 8447713553931260858719950790888018310605121771886674/19029796188236519665
+82710872370270939282541819316252105*x^20 - 514612761148212763891206043163990
+7463590080095940249/634326539607883988860903624123423646427513939772084035*x
+^19 - 1059361636100120768721253493354974053820406875597379689/15223836950589
+215732661686978962167514260334554530016840*x^18 + 34523449867098335876330387
+4528795477249298778155336817/30447673901178431465323373957924335028520669109
+06003368*x^17 + 1918279247354271302539830886798741277861041019894694197/3805
+959237647303933165421744740541878565083638632504210*x^16 - 10562950440932143
+1307553638640481644623085206126643607/10427285582595353241549100670522032544
+0139277770753540*x^15 - 1261590123731735288281705556824572750877960028183568
+7/9193138255186724476244980059759762991703100576407015*x^14 + 56675554021159
+90253347794530780260472114097324564393497/1522383695058921573266168697896216
+751426033455453001684*x^13 + 66416387949103919609366206731851686822315987894
+47099287/7611918475294607866330843489481083757130167277265008420*x^12 - 2404
+9764175786070793476825475231470529143359000282678811/38059592376473039331654
+21744740541878565083638632504210*x^11 + 160088084366876630968087023738250605
+484861574103897979/52495989484790399078143748203317819014690808808724196*x^1
+0 + 48963475354535881489960109688076686948554461694434405349/152238369505892
+15732661686978962167514260334554530016840*x^9 - 1701078182542242003068275881
+477787526790107232819436613/380595923764730393316542174474054187856508363863
+250421*x^8 + 1153947130554333875098640062365039977409871251610932353/6343265
+39607883988860903624123423646427513939772084035*x^7 - 7097647038227138541401
+40055431911423015308135885895003/5074612316863071910887228992987389171420111
+518176672280*x^6 - 23208255318049227426850418921476949716547254062896852519/
+3805959237647303933165421744740541878565083638632504210*x^5 - 26745297256548
+5887755305340342359338986825994184278195/15223836950589215732661686978962167
+51426033455453001684*x^4 - 2308019472041557279913027774617215719006996569385
+4287359/7611918475294607866330843489481083757130167277265008420*x^3 - 874678
+5179662579050668569130655251913092563603040368361/15223836950589215732661686
+978962167514260334554530016840*x^2 + 228284598781041687064446131081112874450
+77656165232505153/15223836950589215732661686978962167514260334554530016840*x
+ + 11539323279906305795742674075988625620609961230962314847/3805959237647303
+933165421744740541878565083638632504210], [x^6 + 8*x^5 - 96*x^4 - 448*x^3 + 
+4352*x^2 - 9216*x + 4096, -3500954735160931119014945191286514244723143201268
+91/30447673901178431465323373957924335028520669109060033680*x^23 - 368753192
+376745612767576249150695148256008996498511/380595923764730393316542174474054
+1878565083638632504210*x^22 + 1222625548165299105222761470379044939248540356
+671807/3805959237647303933165421744740541878565083638632504210*x^21 + 269066
+12703922440457002658814457944899258851853285507/7611918475294607866330843489
+481083757130167277265008420*x^20 - 32744711717911473968833608836796502659132
+601780070963/6089534780235686293064674791584867005704133821812006736*x^19 - 
+821215527972063467089717577787130145191745484957891347/152238369505892157326
+61686978962167514260334554530016840*x^18 + 719519555995694923374695500139118
+398858293255363049029/101492246337261438217744579859747783428402230363533445
+60*x^17 + 11456591503899749478776843861204390272634390714911041293/304476739
+01178431465323373957924335028520669109060033680*x^16 - 126204248534422370494
+879068149453132967995005612845677/208545711651907064830982013410440650880278
+555541507080*x^15 - 53531955812109655939472657032353807104275538376490069/55
+158829531120346857469880358558577950218603458442090*x^14 + 26202308497279151
+618253227373408437905565991764550480251/152238369505892157326616869789621675
+14260334554530016840*x^13 + 354501989648103752047502920687867766560793495564
+3395919/3805959237647303933165421744740541878565083638632504210*x^12 - 13113
+890767927935230296960282562651862223700759508281357/101492246337261438217744
+57985974778342840223036353344560*x^11 - 192082355663057123910705211616724893
+129593397695446143/524959894847903990781437482033178190146908088087241960*x^
+10 - 9075733795262301087631564506281286680882246660124904363/380595923764730
+3933165421744740541878565083638632504210*x^9 + 49456672348902714074007125072
+616599461038794256834978203/152238369505892157326616869789621675142603345545
+30016840*x^8 + 36666141424661985681937782663463554650862812240098411231/1522
+3836950589215732661686978962167514260334554530016840*x^7 - 82408754732311433
+70973635583124680417011947954932254389/1691537438954357303629076330995796390
+473370506058890760*x^6 - 119558618647824072907167067569787864040350402317013
+169449/30447673901178431465323373957924335028520669109060033680*x^5 - 841455
+23660896882078649693962881935941270834937407797919/3044767390117843146532337
+3957924335028520669109060033680*x^4 - 62972355756385214926235004866384135061
+546328061654626117/30447673901178431465323373957924335028520669109060033680*
+x^3 - 1104223987741025090975428994366940678681301630289615753/25373061584315
+35955443614496493694585710055759088336140*x^2 - 4062551484247078790667379350
+0102030712257870147945309931/30447673901178431465323373957924335028520669109
+060033680*x + 63791074728602678092728158519442947086183797037123189561/30447
+673901178431465323373957924335028520669109060033680], [x^6 + 8*x^5 - 128*x^4
+ - 336*x^3 + 7216*x^2 - 13120*x - 27904, 68395468625287412392477690238745594
+48112516050997/529524763498755329831710851442162348322098593201044064*x^23 +
+ 264057752641949424493842106909461112590467967337481/26476238174937766491585
+54257210811741610492966005220320*x^22 - 974650745949411817591373952935998638
+29212493378033/220635318124481387429879521434234311800874413833768360*x^21 -
+ 4941506074493296333553606068488925730493565848754739/1323811908746888324579
+277128605405870805246483002610160*x^20 + 24078898601220202511610606643783446
+43832988608729965/264762381749377664915855425721081174161049296600522032*x^1
+9 + 16743486854633442145271927295191821763033605591256547/294180424165975183
+239839361912312415734499218445024480*x^18 - 34317973686366666262503682270060
+8765518234232496961533/26476238174937766491585542572108117416104929660052203
+20*x^17 - 981102178436040493756473234328095480400180483728045283/26476238174
+93776649158554257210811741610492966005220320*x^16 + 196920256616637674628715
+31157832943814657336924119077/1813440970886148389834626203569049138089378743
+8391920*x^15 + 689090669649716376532960462282604780676889170615347111/132381
+1908746888324579277128605405870805246483002610160*x^14 - 1273683806791508562
+85638496849037298953730047095610861/3677255302074689790497992023903905196681
+2402305628060*x^13 + 2485351815425058020615521703184464791386487277406174907
+/1323811908746888324579277128605405870805246483002610160*x^12 + 312382368194
+6147368634637399354195683346858273744081353/88254127249792554971951808573693
+7247203497655335073440*x^11 - 6287938069412429299642811500856899956945062877
+53952631/91297373017026781005467388179683163503810102276042080*x^10 + 108496
+5570278322118820496988195911861039316141818223213/22063531812448138742987952
+1434234311800874413833768360*x^9 + 49738705590952448136202078366786630967472
+35255881648837/1323811908746888324579277128605405870805246483002610160*x^8 -
+ 30564883782772692313551672403250018729763244239306584093/264762381749377664
+9158554257210811741610492966005220320*x^7 + 55983370269919396444243378427028
+95394755251639721124809/6619059543734441622896385643027029354026232415013050
+80*x^6 + 6468781183225860084941162895575323110987873786198460187/88254127249
+7925549719518085736937247203497655335073440*x^5 - 24852977506194092170356774
+803279205001209040292120132861/264762381749377664915855425721081174161049296
+6005220320*x^4 + 1327972991473283508138105984790596622315886735312127307/165
+476488593361040572409641075675733850655810375326270*x^3 + 299632919694526912
+082388120687014152052200719486935981/529524763498755329831710851442162348322
+098593201044064*x^2 - 702367680592129411469173646967083897138855141470308193
+1/2647623817493776649158554257210811741610492966005220320*x + 12028355404631
+0749679541443128494550924664806994372701/17650825449958510994390361714738744
+9440699531067014688], [x^6 + 8*x^5 - 72*x^4 - 112*x^3 + 1472*x^2 - 4800*x + 
+12352, -248324288537794230037344308658506264756917300289/2876492574509062963
+1859588056612503569693593867794080*x^23 - 1788748620824073658480983802303622
+157316650910699/28764925745090629631859588056612503569693593867794080*x^22 +
+ 4893922720628537561273452838882757793231219431109/1438246287254531481592979
+4028306251784846796933897040*x^21 + 3477085372182623557557010422509100552156
+7114446533/14382462872545314815929794028306251784846796933897040*x^20 - 2329
+267418517221917544908110719619940474852418971/299634643178027391998537375589
+713578850974936122855*x^19 - 36211036674871834249014731942467873276992413301
+1069/9588308581696876543953196018870834523231197955931360*x^18 + 66399259316
+0132006401578900951086063888768596769503/57529851490181259263719176113225007
+13938718773558816*x^17 + 708474306805082797315250059590332816571738207608904
+7/28764925745090629631859588056612503569693593867794080*x^16 - 1419886558510
+5681836944633021110852794734453458011539/14382462872545314815929794028306251
+784846796933897040*x^15 - 14769419898249128084480146170042786528408477556530
+7/625324472719361513736078001230706599341165084082480*x^14 + 323960664350904
+9016930803198865984070727960224129333/89890392953408217599561212676914073655
+2924808368565*x^13 - 34123747442987175586246571318801027001553844802359467/1
+4382462872545314815929794028306251784846796933897040*x^12 - 1555307524104889
+57676817582740630777142530887261952459/2876492574509062963185958805661250356
+9693593867794080*x^11 + 5611065044029938661851758835069339903558371291080869
+/639220572113125102930213067924722301548746530395424*x^10 - 1329990693517910
+3545685384332880895809075669624695873/14382462872545314815929794028306251784
+846796933897040*x^9 - 132902926244065063289565453777965966565360552060333181
+/14382462872545314815929794028306251784846796933897040*x^8 + 321060842687100
+19760073062384633292977314710996745003/3196102860565625514651065339623611507
+743732651977120*x^7 - 52582446308425500485788625618509529958229776403424113/
+14382462872545314815929794028306251784846796933897040*x^6 - 1855903197408266
+50666702624481311839541729106810034121/2876492574509062963185958805661250356
+9693593867794080*x^5 + 34698179995887962509230000075311213988219795782233967
+3/28764925745090629631859588056612503569693593867794080*x^4 - 10747995470738
+3134704504984330387320654093888816006317/14382462872545314815929794028306251
+784846796933897040*x^3 - 801447974127260299398566407668416155686883036376243
+/1917661716339375308790639203774166904646239591186272*x^2 + 2513150246512834
+81146307861918871333436359512535194513/2876492574509062963185958805661250356
+9693593867794080*x - 93791254118396714369508841824281931691346215907343683/2
+8764925745090629631859588056612503569693593867794080], [x^6 - 128*x^5 + 5872
+*x^4 - 110336*x^3 + 795392*x^2 - 2438144*x + 2768896, 5649470056065977450530
+82001281171957988403/14869416214630456434840538919264289206761672560*x^23 + 
+4576532719144485338350431459895444546264559/14869416214630456434840538919264
+289206761672560*x^22 - 3519537609094748522281539121681598929028575/297388324
+2926091286968107783852857841352334512*x^21 - 8534861125502448321599130492222
+7654302765421/7434708107315228217420269459632144603380836280*x^20 + 54858101
+671222010860445203162434994118109361/247823603577174273914008981987738153446
+0278760*x^19 + 1323404357548547002222338506563276589994897211/74347081073152
+28217420269459632144603380836280*x^18 - 563534094015914174167325245459659833
+497439479/1858677026828807054355067364908036150845209070*x^17 - 186767130335
+43179605037598406725254681267964793/1486941621463045643484053891926428920676
+1672560*x^16 + 65240068584557528892049749665489366182509043/2546132913464119
+2525411881711068988367742590*x^15 + 6944007180578281149330884958150231801623
+2949/21549878571928197731652954955455491604002424*x^14 - 1219174587667121314
+5097649705621399806172628349/1486941621463045643484053891926428920676167256*
+x^13 - 11531509639587328119129820371087061958449864373/371735405365761410871
+0134729816072301690418140*x^12 + 3363722933903668618222259839078028556777794
+7539/2973883242926091286968107783852857841352334512*x^11 + 57048978793036875
+266208398722224476402917621/512738490159670911546225479974630662302126640*x^
+10 - 9931723438897636702730021299455125408949339377/297388324292609128696810
+7783852857841352334512*x^9 - 8324380555078678334003224973705739159475574969/
+1486941621463045643484053891926428920676167256*x^8 + 23080865038452497904061
+85266061420043751198217/4956472071543485478280179639754763068920557520*x^7 +
+ 13674858885676639879037946066055961216170017553/165215735718116182609339321
+3251587689640185840*x^6 + 41735059483006355414229520171038669516050144171/29
+73883242926091286968107783852857841352334512*x^5 + 7186190271448691755023431
+380567195860042576223/2973883242926091286968107783852857841352334512*x^4 - 1
+4405581188606609112904525272472545779340276021/74347081073152282174202694596
+32144603380836280*x^3 - 2614630846638350869102700103520575851369849617/18586
+77026828807054355067364908036150845209070*x^2 + 1884514305024652071465391873
+9958781841298690819/7434708107315228217420269459632144603380836280*x + 58868
+217835201339274718911108764026097012470569/148694162146304564348405389192642
+89206761672560], [x^6 - 128*x^5 + 5796*x^4 - 112752*x^3 + 974512*x^2 - 23552
+64*x - 5548544, 1568657609589000676489632398214484238355739331/4277425046437
+6205395760001907672678270946295051168*x^23 + 1310392797443712975720057683918
+5412785996084631/42774250464376205395760001907672678270946295051168*x^22 - 7
+825752530749745195712621956219300646309315241/712904174406270089929333365127
+8779711824382508528*x^21 - 494925130009383521126186614863393761465958337453/
+42774250464376205395760001907672678270946295051168*x^20 + 425517647316621894
+914327362265995304687924955347/213871252321881026978800009538363391354731475
+25584*x^19 + 2637895609954650813151566780872822762028959587285/1425808348812
+5401798586667302557559423648765017056*x^18 - 3012474760399789309765163253180
+255286915598006995/10693562616094051348940000476918169567736573762792*x^17 -
+ 30006077141420698676975375468669190716885674983923/213871252321881026978800
+00953836339135473147525584*x^16 + 777088102733057353928882078089759179960608
+504941/292974318249152091751780834984059440211960925008*x^15 + 2032340189594
+403927077993499458301866711231975891/464937505047567449953913064213833459466
+807554904*x^14 - 39621258846105079185530791539914545643343648203425/35645208
+72031350449646666825639389855912191254264*x^13 - 433727716793143290401944109
+47762103910151042829479/10693562616094051348940000476918169567736573762792*x
+^12 + 111830217632072514908210550989211871176552309481599/475269449604180059
+9528889100852519807882921672352*x^11 - 1471024591196603780353096370192893579
+7358917135551/1474974153944007082612413858885264767963665346592*x^10 - 78428
+948244601401396523150212212384571885038777261/356452087203135044964666682563
+9389855912191254264*x^9 + 10939471959341527147172170510572393445222068105662
+57/42774250464376205395760001907672678270946295051168*x^8 + 1196107874490196
+95434901722719788534878705734430029/4277425046437620539576000190767267827094
+6295051168*x^7 - 85459961331114624499693402127290245760415437599821/53467813
+08047025674470000238459084783868286881396*x^6 + 1694407481139199129039479112
+0329596090351233276463/891130218007837612411666706409847463978047813566*x^5 
++ 226932506488249147866358992212134279355199507497449/4277425046437620539576
+0001907672678270946295051168*x^4 + 84260644432024255173228501431933921888577
+448684655/21387125232188102697880000953836339135473147525584*x^3 + 260049245
+774695484902410565325631597167723463363247/427742504643762053957600019076726
+78270946295051168*x^2 - 165043255946056640400288944775348142222709154110443/
+21387125232188102697880000953836339135473147525584*x + 173844651349840294286
+61539192228026476067275039793/3564520872031350449646666825639389855912191254
+264], [x^6 - 128*x^5 + 5692*x^4 - 115568*x^3 + 1201904*x^2 - 6659968*x + 183
+62368, 10977377138240491962525269171644277846711683/181154090831385442213907
+176542899559101339531040*x^23 + 30389802666938135888599842289638890696282063
+/60384696943795147404635725514299853033779843680*x^22 - 20321499860927178090
+338758847250601516530803/11322130676961590138369198533931222443833720690*x^2
+1 - 1692856094886090358692988590503313309011438707/9057704541569272110695358
+8271449779550669765520*x^20 + 146290478798968128247582698562472768048091403/
+4528852270784636055347679413572488977533488276*x^19 + 5856146193472700543165
+702003568135214964860663/20128232314598382468211908504766617677926614560*x^1
+8 - 27129060630264870753132675422104089414367844657/603846969437951474046357
+25514299853033779843680*x^17 - 377547666471355013205262359495866832790774367
+189/181154090831385442213907176542899559101339531040*x^16 + 5012845164765129
+943475067825204268184119510991/124078144405058522064319983933492848699547624
+0*x^15 + 21464029225265033477063259623192271085420396829/3938132409377944395
+954503837889120850029120240*x^14 - 66110715131219166560504239575476426535996
+3222319/45288522707846360553476794135724889775334882760*x^13 - 1576467094845
+86952011928320421438821016639345613/9057704541569272110695358827144977955066
+9765520*x^12 + 4061550160739337620996102631219726749347532706413/18115409083
+1385442213907176542899559101339531040*x^11 - 1126332939004088227857652608248
+98315977612857287/6246692787289153179789902639410329624184121760*x^10 - 1521
+8280324567988648111123170812861216900515754/56610653384807950691845992669656
+11221916860345*x^9 + 2081840960113587441649887745098530287210809711431/90577
+045415692721106953588271449779550669765520*x^8 - 428449047753103620447116130
+2184875955387571541621/181154090831385442213907176542899559101339531040*x^7 
++ 469413210760649906034859986011059047980598506869/4528852270784636055347679
+4135724889775334882760*x^6 + 55864720594330294619778660374760514737674836230
+07/181154090831385442213907176542899559101339531040*x^5 - 297019991547106006
+9699481257358315937125437709863/18115409083138544221390717654289955910133953
+1040*x^4 + 842529722841499853191326096902857051186604210531/3019234847189757
+3702317862757149926516889921840*x^3 + 25569321234426637982752574535714316934
+74517360113/181154090831385442213907176542899559101339531040*x^2 - 209669369
+920421622205353858461916799081258776973/201282323145983824682119085047666176
+77926614560*x + 2456366117824133783672097608608233023116792401857/1811540908
+31385442213907176542899559101339531040], [x^8 - 128*x^7 + 6372*x^6 - 149962*
+x^5 + 1601908*x^4 - 4447116*x^3 + 4733661*x^2 - 2052988*x + 471147, -4938012
+67890851608442652181089455243582594539/6050876637033176880948824891675684806
+826043255360*x^23 - 1298238726344360747051216746018180635108945183/201695887
+9011058960316274963891894935608681085120*x^22 + 2579616545696938697613415271
+48364603575472233/94544947453643388764825388932432575106656925865*x^21 + 492
+30559645855923056526779514779792483270605159/2016958879011058960316274963891
+894935608681085120*x^20 - 41406178774920134738833334058684869147462081381/75
+6359579629147110118603111459460600853255406920*x^19 - 7724381279500877086330
+35584053889389281598477369/2016958879011058960316274963891894935608681085120
+*x^18 + 2358835879674900051908319304947407423987612057321/302543831851658844
+0474412445837842403413021627680*x^17 + 1637069190446776120896121182768056394
+247271995105/605087663703317688094882489167568480682604325536*x^16 - 5605399
+8810774207794362193302686741493827943811/82888721055248998369161984817475134
+34008278432*x^15 - 34462417002712261963651034841051949470083046471/548086651
+9051790653033355880141018846762720340*x^14 + 3676725024907929399836864045889
+0836235093772131181/1512719159258294220237206222918921201706510813840*x^13 -
+ 629562075627110243900646403074672859663508507181/50423971975276474007906874
+0972973733902170271280*x^12 - 2348082477043420454267263243751494053257037524
+01263/6050876637033176880948824891675684806826043255360*x^11 + 2047679654093
+121992125127730924628727508381812883/695503061727951365626301711686860322623
+68313280*x^10 + 35807564174625103131109673128733316150782935929529/302543831
+8516588440474412445837842403413021627680*x^9 - 20214740785304719650309013891
+4713160359244487448853/6050876637033176880948824891675684806826043255360*x^8
+ + 152376990429523067481580072754410086697791175220813/605087663703317688094
+8824891675684806826043255360*x^7 - 24673250685713016912556818665356369103433
+845251369/3025438318516588440474412445837842403413021627680*x^6 - 5775343593
+4377162586561553429153825824818245333063/15127191592582942202372062229189212
+01706510813840*x^5 + 42754065234985779395416847570915717710996944251749/2016
+958879011058960316274963891894935608681085120*x^4 - 260160730027295582317770
+18867506925369338347342907/1512719159258294220237206222918921201706510813840
+*x^3 + 24823784754233139999500092096084727381834317981813/605087663703317688
+0948824891675684806826043255360*x^2 + 37424516202962553865962690734053704131
+883836050337/1512719159258294220237206222918921201706510813840*x + 125434138
+59531923934973881780920138972969617647929/5042397197527647400790687409729737
+33902170271280], [x^8 - 128*x^7 + 6388*x^6 - 152670*x^5 + 1655686*x^4 - 5638
+780*x^3 + 17111589*x^2 - 32653422*x + 22826961, -966605961188333904906317903
+5652813746394173/101169715597773808686089847324978297200621695056*x^23 - 180
+63686216291631172846228154619046616837551/2248215902172751304135329940555073
+2711249265568*x^22 + 61693510809720597393420424738361583463991519/2248215902
+1727513041353299405550732711249265568*x^21 + 1203367812960522378437532450096
+5962065932978375/404678862391095234744359389299913188802486780224*x^20 - 967
+6605749301200266256219940917283934262310269/20233943119554761737217969464995
+6594401243390112*x^19 - 46888528112293669665288948620537693877008430863/1011
+69715597773808686089847324978297200621695056*x^18 + 267312789841489274465803
+784558886621328658163937/404678862391095234744359389299913188802486780224*x^
+17 + 1359564533340134004092064702898265422684733547817/404678862391095234744
+359389299913188802486780224*x^16 - 83590022585006593865035333730192776993859
+49617/1385886515037997379261504757876415030145502672*x^15 - 2750391611645660
+9434643445256128704331160281439/29324555245731538749591260094196607884238172
+48*x^14 + 375051952370671286475329668213446472221751560887/16861619266295634
+781014974554163049533436949176*x^13 + 13195581129082420428768306559304700532
+12733591521/202339431195547617372179694649956594401243390112*x^12 - 75022815
+74945212347151304734565225931871054191163/2023394311955476173721796946499565
+94401243390112*x^11 + 7746284714609786366972622305105065430551843127/3876234
+31409095052437125851819840219159470096*x^10 + 175946480642415213047956695459
+877666282099456441/11241079510863756520676649702775366355624632784*x^9 - 123
+27342703958373998887525590912952250328347837347/4046788623910952347443593892
+99913188802486780224*x^8 + 3821374820076942747478969609504803774352274090287
+/202339431195547617372179694649956594401243390112*x^7 - 14878164241730448838
+9478390912949887670722475325/33723238532591269562029949108326099066873898352
+*x^6 - 6222375070248348975219220413600628712546578012523/1348929541303650782
+48119796433304396267495593408*x^5 + 3873246544084234995269750537879131829331
+507703/25292428899443452171522461831244574300155423764*x^4 - 693424534714447
+53187442022222072168598183328869/2107702408286954347626871819270381191679618
+647*x^3 - 4237862675350265727076348403540204959319197316185/2023394311955476
+17372179694649956594401243390112*x^2 + 2586062114347979593199803148645105082
+88250090483/134892954130365078248119796433304396267495593408*x + 21675465644
+39918128228998511047191574468859719247/1348929541303650782481197964333043962
+67495593408], [x^8 - 128*x^7 + 6252*x^6 - 141414*x^5 + 1382948*x^4 - 3759740
+*x^3 + 3800153*x^2 - 7172584*x + 131759955, -1434844500108651823671046245869
+357722482048984911441/152238369505892157326616869789621675142603345545300168
+40*x^23 - 2360248553857644561153562817136791989825417906952161/3044767390117
+843146532337395792433502852066910906003368*x^22 + 87173632580954524367306477
+982456636146356714783720619/304476739011784314653233739579243350285206691090
+60033680*x^21 + 352303062731989479239858978593368311587664597723593601/12179
+069560471372586129349583169734011408267643624013472*x^20 - 40168626462020809
+6739159797334958761758634303266933601/76119184752946078663308434894810837571
+30167277265008420*x^19 - 275122015140672215400034184034233494706575007613886
+2755/6089534780235686293064674791584867005704133821812006736*x^18 + 89774874
+47810351332820197929340628153198362023725455255/1217906956047137258612934958
+3169734011408267643624013472*x^17 + 6587693705310815697012843197649612107949
+0307340464905151/20298449267452287643548915971949556685680446072706689120*x^
+16 - 456653571533095731745200375715087858605012033188666557/6951523721730235
+4943660671136813550293426185180502360*x^15 - 1132692441628489144192494927147
+2134397548325112949555901/13238119087468883245792771286054058708052464830026
+10160*x^14 + 362432174014317480048478971081546342548806144095956168819/15223
+836950589215732661686978962167514260334554530016840*x^13 + 12024185697750483
+3960932827002163638604768758609545585477/30447673901178431465323373957924335
+028520669109060033680*x^12 - 13030176649505436631272788181758072398359598383
+1950270699/3383074877908714607258152661991592780946741012117781520*x^11 + 54
+2807100222157433854855297232207847718098908270755205/23331550882129066256952
+776979252364006529248359432976*x^10 + 10387878742116168717484553665985401170
+8147962354618472239/7611918475294607866330843489481083757130167277265008420*
+x^9 - 1819856552502463405316947768101240474679904916809582226137/60895347802
+356862930646747915848670057041338218120067360*x^8 + 810491866103236539189548
+37706236850331188041083782957961/3805959237647303933165421744740541878565083
+638632504210*x^7 - 32282509767607037064771269938993949421074543959221288349/
+3805959237647303933165421744740541878565083638632504210*x^6 - 25544033303698
+20101316215131962008120825983490870537705723/6089534780235686293064674791584
+8670057041338218120067360*x^5 + 22901642742154067726943738201768804798288313
+4305484684749/30447673901178431465323373957924335028520669109060033680*x^4 -
+ 179898736115995158794701606412394379275299516278483601917/60895347802356862
+93064674791584867005704133821812006736*x^3 - 1051213427478377979478022079669
+70243591354528534092045953/1522383695058921573266168697896216751426033455453
+0016840*x^2 + 625869742638167665148168633053205193372974205328860697139/6089
+5347802356862930646747915848670057041338218120067360*x + 1283657458421180203
+581590597667101411582298024120277652453/608953478023568629306467479158486700
+57041338218120067360], [x^8 - 128*x^7 + 5996*x^6 - 125058*x^5 + 1105242*x^4 
+- 2195548*x^3 - 4844447*x^2 + 18920794*x + 28602153, -1355993271508975902670
+112217525308808448831/34816877659779740502243080421348085032189650880*x^23 -
+ 238007005988586203227673394695931155753199/77370839243954978893873512047440
+1889604214464*x^22 + 22666074111129984968367457453509266857588241/1740843882
+9889870251121540210674042516094825440*x^21 + 4068898820114725409336462252389
+7117519643353/3481687765977974050224308042134808503218965088*x^20 - 45490205
+9145483766490823300364278918000444221/17408438829889870251121540210674042516
+094825440*x^19 - 1280985844312606564102406755871045658987741551/696337553195
+5948100448616084269617006437930176*x^18 + 2598361076913682739962749218813875
+832561504377/6963375531955948100448616084269617006437930176*x^17 + 151902257
+41390744173144088753581024754155545179/1160562588659324683408102680711602834
+4063216960*x^16 - 18900912952568602594825077189605970512897976743/5802812943
+296623417040513403558014172031608480*x^15 - 23585711861370635389761750146132
+22534552292339/756888644777820445700936530898871413743253280*x^14 + 10366282
+1112077760277790972916514232989363620101/87042194149449351255607701053370212
+58047412720*x^13 - 8080295846375527232887657273986796427537554367/1740843882
+9889870251121540210674042516094825440*x^12 - 6780516421331314972211686552096
+96539275384915153/34816877659779740502243080421348085032189650880*x^11 + 349
+1339271605870680074644026838395611311794381/24011639765365338277409020980240
+0586428894144*x^10 + 114047357549613363278263916996653840364989677259/174084
+38829889870251121540210674042516094825440*x^9 - 3014857976230057453614064597
+49821573518824578199/17408438829889870251121540210674042516094825440*x^8 + 4
+50539268925236609769367081964550664930155288379/3481687765977974050224308042
+1348085032189650880*x^7 - 68332281719297567374473816383660612866633967263/17
+408438829889870251121540210674042516094825440*x^6 - 675158388668363191013877
+911825935432767942973847/34816877659779740502243080421348085032189650880*x^5
+ + 381362819390927554165294200203702917063933423627/348168776597797405022430
+80421348085032189650880*x^4 - 1111569200596416923083307203056275347661212691
+/145070323582415585426012835088950354300790212*x^3 + 31042672538291695701501
+960271430850425771758889/11605625886593246834081026807116028344063216960*x^2
+ + 150661304165789605189254514770671138042455188637/116056258865932468340810
+26807116028344063216960*x + 384635219444636964842750487491135732871308506247
+/34816877659779740502243080421348085032189650880], [x^12 + 8*x^11 - 32*x^10 
+- 228*x^9 + 1024*x^8 + 3336*x^7 - 13530*x^6 - 16088*x^5 + 122256*x^4 - 10698
+8*x^3 + 199232*x^2 - 69080*x + 78121, 65066666656093209286963509220431628228
+125609846371/30447673901178431465323373957924335028520669109060033680*x^23 +
+ 28777027886387296107612774093715751039967295566281/169153743895435730362907
+6330995796390473370506058890760*x^22 - 2753699887715968424402732053871259784
+36259728360997/3805959237647303933165421744740541878565083638632504210*x^21 
+- 80022006269961814753010340427347685423806044997340627/12179069560471372586
+1293495831697340114082676436240134720*x^20 + 2273476822227624723439495830767
+039255674203960133177/152238369505892157326616869789621675142603345545300168
+4*x^19 + 13500641478661078022453215249761643930343870473494211/1268653079215
+767977721807248246847292855027879544168070*x^18 - 91116180070534091147512832
+1506610978694307473001131181/40596898534904575287097831943899113371360892145
+413378240*x^17 - 9728433400735177582778402652117110626985769366869131837/121
+790695604713725861293495831697340114082676436240134720*x^16 + 11124909834319
+966382406610794456633338230128467448343/521364279129767662077455033526101627
+20069638885376770*x^15 + 563364077300010961430863629176782915709833296596117
+827/2647623817493776649158554257210811741610492966005220320*x^14 - 145633345
+15922394909146331379895744140552053495796963891/1522383695058921573266168697
+8962167514260334554530016840*x^13 + 8822016970556534270059884803005486339208
+288670044090491/60895347802356862930646747915848670057041338218120067360*x^1
+2 + 127206528408595099249429648017412581216779996600352749267/60895347802356
+862930646747915848670057041338218120067360*x^11 - 40821026680563052231204166
+76831874684914089777315963893/2099839579391615963125749928132712760587632352
+348967840*x^10 - 101985428721339153294698554491524192926257856110159030157/6
+0895347802356862930646747915848670057041338218120067360*x^9 + 42791766032533
+0146232441040852756514798359805317404516211/12179069560471372586129349583169
+7340114082676436240134720*x^8 - 21843954609832577130402129425960435382306446
+172595380307/30447673901178431465323373957924335028520669109060033680*x^7 - 
+79670778033972604290997393827918115436558365746889551131/6089534780235686293
+0646747915848670057041338218120067360*x^6 + 14201999281039237169430199865835
+9369497010150308053107821/12179069560471372586129349583169734011408267643624
+0134720*x^5 - 32880812508889018789944360729178675709347346407241109941/30447
+673901178431465323373957924335028520669109060033680*x^4 + 116995332462780805
+31093763988581003099401090648148804321/6766149755817429214516305323983185561
+893482024235563040*x^3 + 163571436871579920806852364397323814127866296390460
+27087/60895347802356862930646747915848670057041338218120067360*x^2 + 1251190
+7933455800045847813518474740191270721168689800161/13532299511634858429032610
+647966371123786964048471126080*x - 45416834122173675984634751540632770357151
+083852279226589/121790695604713725861293495831697340114082676436240134720], 
+[x^12 - 128*x^11 + 7072*x^10 - 222776*x^9 + 4451440*x^8 - 59277312*x^7 + 538
+574272*x^6 - 3352440064*x^5 + 14157839104*x^4 - 39002448384*x^3 + 6493475532
+8*x^2 - 60655540224*x + 67769520128, 424335399013795484329585864541253623465
+77727596679/2537306158431535955443614496493694585710055759088336140*x^23 + 2
+331149955329407472011088732357531429051310087608687/152238369505892157326616
+86978962167514260334554530016840*x^22 - 117744683119365199164534463210453275
+78601169057156303/30447673901178431465323373957924335028520669109060033680*x
+^21 - 19203277897513304435875027071404693549163294955615771/3383074877908714
+607258152661991592780946741012117781520*x^20 + 17776413035305843790166972269
+14277892436745504290040/3805959237647303933165421744740541878565083638632504
+21*x^19 + 2783688502078301876560706834153669130185103696349940957/3044767390
+1178431465323373957924335028520669109060033680*x^18 - 9368835950076297763911
+3187803799778204842493008934437/16915374389543573036290763309957963904733705
+06058890760*x^17 - 22494833465693100038623435737441544529042861169363366239/
+30447673901178431465323373957924335028520669109060033680*x^16 + 126985191749
+458775559712085056412947213693017549428081/208545711651907064830982013410440
+650880278555541507080*x^15 + 19591133792006442315214801942678713499657467010
+04151259/661905954373444162289638564302702935402623241501305080*x^14 - 41426
+206800008018177054092468893471960443825648821883303/152238369505892157326616
+86978962167514260334554530016840*x^13 - 269776341839468553115193902728158799
+5060970730880391713/422884359738589325907269082748949097618342626514722690*x
+^12 + 46732021768203656922658288059424441444564612668808031887/7611918475294
+607866330843489481083757130167277265008420*x^11 + 41457121502089039190792804
+3693337502039665952339040431/58328877205322665642381942448130910016323120898
+582440*x^10 - 224031270077185123205112847922590353376838562318236338363/3044
+7673901178431465323373957924335028520669109060033680*x^9 - 36706572813832147
+301604562077501280352523525747845964451/101492246337261438217744579859747783
+42840223036353344560*x^8 + 6348692994518079825970323151915463372525713814650
+9584497/15223836950589215732661686978962167514260334554530016840*x^7 + 18749
+04595986563688607035761097189266972839481139388301/3044767390117843146532337
+3957924335028520669109060033680*x^6 + 24322607008365923955576621650706418947
+7224297241159265407/30447673901178431465323373957924335028520669109060033680
+*x^5 + 2018932968640554810896242722069864549217866087131569398/2114421798692
+94662953634541374474548809171313257361345*x^4 - 3229658818789990933359262960
+7895158083800617281062974671/76119184752946078663308434894810837571301672772
+65008420*x^3 + 44189240178661966971221203380498006729702098568972563783/3044
+7673901178431465323373957924335028520669109060033680*x^2 - 32774215919947604
+742286419081703350830159707258712991371/152238369505892157326616869789621675
+14260334554530016840*x + 918914399905141421157902593003827981001865103376222
+20197/30447673901178431465323373957924335028520669109060033680], [x^12 - 128
+*x^11 + 7000*x^10 - 215116*x^9 + 4123778*x^8 - 51886068*x^7 + 441305792*x^6 
+- 2587581388*x^5 + 10632143781*x^4 - 30176341404*x^3 + 57593424368*x^2 - 661
+54473176*x + 40390771664, 18463333363928720155975373691072684438923522507053
+7/5295247634987553298317108514421623483220985932010440640*x^23 + 14764526048
+80626530553846524977325866833188168836537/5295247634987553298317108514421623
+483220985932010440640*x^22 - 74563168555786751271456226849761196022885418315
+9599/661905954373444162289638564302702935402623241501305080*x^21 - 277034534
+2288087595503347842807168999915911160687795/26476238174937766491585542572108
+1174161049296600522032*x^20 + 1952462511265135777118054851030325738619954157
+2761357/882541272497925549719518085736937247203497655335073440*x^19 + 862775
+355008463613598861314059265231988773175429977471/529524763498755329831710851
+4421623483220985932010440640*x^18 - 1671698503824514168765610134764272312875
+344639274132939/5295247634987553298317108514421623483220985932010440640*x^17
+ - 6046753989964051670150427547837550905068469974439337417/52952476349875532
+98317108514421623483220985932010440640*x^16 + 100801483178391565008385581717
+147016529133985704616193/362688194177229677966925240713809827617875748767838
+40*x^15 + 459042749345955009876011084734556610501145869459401983/17650825449
+9585109943903617147387449440699531067014688*x^14 - 6696071714046912378411037
+767491697499530865033768128673/661905954373444162289638564302702935402623241
+501305080*x^13 + 2843887739020458188940081476050531170015006076104519289/264
+7623817493776649158554257210811741610492966005220320*x^12 + 1749773848262678
+2066355810225587881685578115610236771051/10590495269975106596634217028843246
+96644197186402088128*x^11 - 262831859305312267071570609341247658975337459598
+1226203/182594746034053562010934776359366327007620204552084160*x^10 - 157937
+3035852710694285764315809691750557713541998251247/26476238174937766491585542
+5721081174161049296600522032*x^9 + 56771438349914697469159750141686154803567
+58682133096713/330952977186722081144819282151351467701311620750652540*x^8 - 
+15119194179787853101127959095680921091042759833521116131/1765082544995851099
+439036171473874494406995310670146880*x^7 + 102810823183983243963428197906125
+932479203158517964291/441270636248962774859759042868468623601748827667536720
+*x^6 + 13125284180768256728812564593605976684173487579326904331/105904952699
+7510659663421702884324696644197186402088128*x^5 - 53893267212112384604258881
+33373553192823646013149797697/5295247634987553298317108514421623483220985932
+010440640*x^4 + 2042730533678441931444059941826970835004691000616458689/1323
+81190874688832457927712860540587080524648300261016*x^3 - 1372649475078011559
+9211968200080332353637900234726227791/52952476349875532983171085144216234832
+20985932010440640*x^2 - 5955933052912600565408319112157485403575846366641862
+129/5295247634987553298317108514421623483220985932010440640*x + 276576666305
+09651485384460280486609412867864388770353397/5295247634987553298317108514421
+623483220985932010440640], [x^12 + 8*x^11 - 48*x^10 - 324*x^9 + 1600*x^8 + 5
+656*x^7 - 25178*x^6 - 24856*x^5 + 264128*x^4 - 276364*x^3 + 14400*x^2 - 1096
+08*x + 560233, 22862683949356898693145720439385966734937527286017/3044767390
+117843146532337395792433502852066910906003368*x^23 + 30556774423730200703910
+0439676636870860564868697503/50746123168630719108872289929873891714201115181
+76672280*x^22 - 364791094039097263336592619234953294707856932092757/15223836
+95058921573266168697896216751426033455453001684*x^21 - 273742642624935610504
+146130767021453802539972115720943/121790695604713725861293495831697340114082
+676436240134720*x^20 + 14134085765647291289865842805006724827498636217759473
+1/30447673901178431465323373957924335028520669109060033680*x^19 + 1762486125
+10521942483402656912564193084191824833668099/5074612316863071910887228992987
+389171420111518176672280*x^18 - 53155113329191278415304907106997345808113240
+9688264449/8119379706980915057419566388779822674272178429082675648*x^17 - 29
+186583579369888686985010537793345832291701792552327297/121790695604713725861
+293495831697340114082676436240134720*x^16 + 11817062190700924586592156144389
+5390547606705964931577/20854571165190706483098201341044065088027855554150708
+0*x^15 + 1363966020060875846841749525186906348568750503317401503/26476238174
+93776649158554257210811741610492966005220320*x^14 - 294104004144804546814634
+2871333931639873341678117947157/15223836950589215732661686978962167514260334
+55453001684*x^13 + 19112993959296991832395430252531127320925998625478787687/
+60895347802356862930646747915848670057041338218120067360*x^12 + 156333529316
+220483031618920533800052228339474556808477213/608953478023568629306467479158
+48670057041338218120067360*x^11 - 119109811646202738658274183044654802333199
+7885655797093/419967915878323192625149985626542552117526470469793568*x^10 + 
+47920057190141968221470677444242599015309498856573549967/6089534780235686293
+0646747915848670057041338218120067360*x^9 + 57923265720776925815812973461067
+014182029316155657865059/243581391209427451722586991663394680228165352872480
+26944*x^8 - 129103734947918182696414547934825944381114830346566447877/304476
+73901178431465323373957924335028520669109060033680*x^7 + 1585073692884849190
+30160135488531036952019108757315221173/6089534780235686293064674791584867005
+7041338218120067360*x^6 + 52457631936009898928081743383414740674142398819731
+6146629/121790695604713725861293495831697340114082676436240134720*x^5 - 2027
+1881673669276000010912604356149766757839753125231593/60895347802356862930646
+74791584867005704133821812006736*x^4 + 5998134809924570435371766096065767126
+4785160000836785237/20298449267452287643548915971949556685680446072706689120
+*x^3 + 82125560315233201823890923941614440369511665073048407767/608953478023
+56862930646747915848670057041338218120067360*x^2 + 4468048988614755837724669
+20404288528897644279371535857/1353229951163485842903261064796637112378696404
+8471126080*x - 36853554286489699504420429525610772575693627713065347137/1217
+90695604713725861293495831697340114082676436240134720], [x^12 - 128*x^11 + 7
+028*x^10 - 217804*x^9 + 4201632*x^8 - 52044492*x^7 + 410394530*x^6 - 1951174
+544*x^5 + 4906888668*x^4 - 3571058548*x^3 - 7310592704*x^2 + 8305027036*x + 
+8727249437, 354204903997215358797307281171178249849973081621/639220572113125
+1029302130679247223015487465303954240*x^23 + 2487869341017886594476380601135
+8224944545334971617/57529851490181259263719176113225007139387187735588160*x^
+22 - 1495198202130256279220517957152874314680896954621/799025715141406378662
+766334905902876935933162994280*x^21 - 25966415989986949963577511108089769993
+615534412979/1598051430282812757325532669811805753871866325988560*x^20 + 109
+9116491805220663815752150310053611201337564429957/28764925745090629631859588
+056612503569693593867794080*x^19 + 14420763777731206410046878257265435461644
+627644870991/57529851490181259263719176113225007139387187735588160*x^18 - 31
+553116547541365721111777958296319698059776971879007/575298514901812592637191
+76113225007139387187735588160*x^17 - 972498436345994409047717032282946890576
+96145029094833/57529851490181259263719176113225007139387187735588160*x^16 + 
+135677260380623987418934151831085143839101525665107381/287649257450906296318
+59588056612503569693593867794080*x^15 + 377894481706623930704387691847302005
+0700623474542391/1250648945438723027472156002461413198682330168164960*x^14 -
+ 19562719073527465023636694631182686094154715049100273/119853857271210956799
+4149502358854315403899744491420*x^13 + 1908591369254382006959493273752222755
+4397778508717641/3196102860565625514651065339623611507743732651977120*x^12 +
+ 1300107356368521428018560813290773003351388611398957147/5752985149018125926
+3719176113225007139387187735588160*x^11 - 1699951639250159298693534919196173
+587070390344681333031/57529851490181259263719176113225007139387187735588160*
+x^10 + 21885214988151134785275765148847535005644172169314179/479415429084843
+8271976598009435417261615598977965680*x^9 + 31665344378449207077751274203403
+356981738049781701411/1198538572712109567994149502358854315403899744491420*x
+^8 - 384048224170228136943335430902244076368661075238273365/1150597029803625
+1852743835222645001427877437547117632*x^7 + 24537667159561162056113877288532
+3269695201997090034903/14382462872545314815929794028306251784846796933897040
+*x^6 + 518211448087437512082635179578312011744395434386465281/19176617163393
+753087906392037741669046462395911862720*x^5 - 495935376162218891763871987374
+901719755454688995137219/191766171633937530879063920377416690464623959118627
+20*x^4 + 430442457721369897191159498019894905104349245911845359/143824628725
+45314815929794028306251784846796933897040*x^3 + 3877973790598824428826471770
+8230091254542725845133977/63922057211312510293021306792472230154874653039542
+40*x^2 - 866270056674413518007230896808600800427673779982469421/575298514901
+81259263719176113225007139387187735588160*x + 184897148273759181037362315660
+360107875627946870841823/191766171633937530879063920377416690464623959118627
+20], [x^12 + 8*x^11 - 20*x^10 - 192*x^9 + 502*x^8 + 2856*x^7 - 3060*x^6 - 13
+352*x^5 + 52353*x^4 + 42544*x^3 - 8992*x^2 - 25208*x + 136624, 9948798988126
+75791402622578945789183307264767199/8457687194771786518145381654978981952366
+85253029445380*x^23 + 59789121796993226628948017663501487213874578749141/507
+4612316863071910887228992987389171420111518176672280*x^22 - 3587372968213104
+38734338853899660739396012657034911/2029844926745228764354891597194955668568
+0446072706689120*x^21 - 2873644538544616442300064820458652204594434885416599
+/6766149755817429214516305323983185561893482024235563040*x^20 - 225589532411
+7630810209853167935028674275921290261/44319758225878357300325144043557984029
+869969591062640*x^19 + 13835692239945575042839142170853097859813764868679991
+7/20298449267452287643548915971949556685680446072706689120*x^18 + 1457156005
+6883905575916866446065437075872717486135657/50746123168630719108872289929873
+89171420111518176672280*x^17 - 395760275355413610621807116292185233854861851
+002844533/6766149755817429214516305323983185561893482024235563040*x^16 - 939
+112140068214318715010369110990436866535698835453/463434914782015699624404474
+24542366862284123453668240*x^15 + 132950761649943569069311808062897979602968
+310160498177/441270636248962774859759042868468623601748827667536720*x^14 + 9
+78046276355281793667534074922071332286493561828989489/1014922463372614382177
+4457985974778342840223036353344560*x^13 - 1129747394988597964467917318244127
+095892064667203424565/101492246337261438217744579859747783428402230363533445
+6*x^12 + 421426032051438936722778095963751075383403070519243271/507461231686
+3071910887228992987389171420111518176672280*x^11 + 4362458932800119355826262
+59743091386419019331873167499/1749866316159679969271458273443927300489693626
+95747320*x^10 - 10313894948045606599628050224105385214968336425524192873/676
+6149755817429214516305323983185561893482024235563040*x^9 - 51545870853179449
+095853282767237096546480108369838175731/202984492674522876435489159719495566
+85680446072706689120*x^8 + 9117132738149561693605469592970411714722159288430
+688623/2537306158431535955443614496493694585710055759088336140*x^7 - 4829913
+928007192670126765410781714580554864652496841001/405968985349045752870978319
+4389911337136089214541337824*x^6 - 60887880298505501517060765765033323911833
+35610880266333/6766149755817429214516305323983185561893482024235563040*x^5 +
+ 46084005155239229035442834618698271542646311695210480277/101492246337261438
+21774457985974778342840223036353344560*x^4 - 8153259732874800058123066239009
+394363808083426262437247/338307487790871460725815266199159278094674101211778
+1520*x^3 - 24080782821134485584190150996460355347673534634324443257/20298449
+267452287643548915971949556685680446072706689120*x^2 + 397620832390668218552
+0923103263375479437623604945781277/10149224633726143821774457985974778342840
+22303635334456*x - 52744010985633614911677082098196802012646172402331852887/
+20298449267452287643548915971949556685680446072706689120], [x^12 + 8*x^11 - 
+96*x^10 - 500*x^9 + 3496*x^8 + 1896*x^7 - 23646*x^6 + 10088*x^5 - 7632*x^4 +
+ 108804*x^3 - 72120*x^2 + 29080*x + 8669, -160076268942759843491807179189838
+226976188513273211/20298449267452287643548915971949556685680446072706689120*
+x^23 - 996499635730976890503667465144832055871723653093551/15223836950589215
+732661686978962167514260334554530016840*x^22 + 17733655257084927901033078811
+53296896121059813393801/7611918475294607866330843489481083757130167277265008
+420*x^21 + 98424969300447112803010536981003748205958953274541561/40596898534
+904575287097831943899113371360892145413378240*x^20 - 50932526295732471756349
+575482932816704526233461136379/121790695604713725861293495831697340114082676
+43624013472*x^19 - 1145230923459929339628594743781409599519998376321752411/3
+0447673901178431465323373957924335028520669109060033680*x^18 + 2350200912696
+730758224519321784847776410893983727229239/405968985349045752870978319438991
+13371360892145413378240*x^17 + 326416164085346765403320903745258911722545507
+96691214423/121790695604713725861293495831697340114082676436240134720*x^16 -
+ 215203527208982101554131954505106199673836033352432421/41709142330381412966
+1964026820881301760557111083014160*x^15 - 1848131016790642703978207397953274
+286212446217631879483/264762381749377664915855425721081174161049296600522032
+0*x^14 + 55328977529123941436545890133199926186670098756144408033/3044767390
+1178431465323373957924335028520669109060033680*x^13 + 6512714067097255297913
+449617341311661885063658367692287/202984492674522876435489159719495566856804
+46072706689120*x^12 - 832741003561894524701602644325502684017255494394387966
+69/30447673901178431465323373957924335028520669109060033680*x^11 + 410881995
+192243441699889583733158322072766997991674623/233315508821290662569527769792
+523640065292483594329760*x^10 + 29379558359240744593646038441273899479199882
+829159795253/60895347802356862930646747915848670057041338218120067360*x^9 - 
+25565663436635476659601393395810012994911625365562733711/1353229951163485842
+9032610647966371123786964048471126080*x^8 + 29255048017247281406169956044711
+995016584629206346895769/152238369505892157326616869789621675142603345545300
+16840*x^7 - 68664980484187976386528046668326132069656697441891027871/6089534
+7802356862930646747915848670057041338218120067360*x^6 - 38113723010604051750
+8636133797935097577710954942079446719/12179069560471372586129349583169734011
+4082676436240134720*x^5 - 20426554047909827220845525005027316136195713469917
+30893/6766149755817429214516305323983185561893482024235563040*x^4 - 84134077
+486443969853039440381806581478078071947496932503/304476739011784314653233739
+57924335028520669109060033680*x^3 - 2960783154005029317239038437213566955696
+2249202521416123/60895347802356862930646747915848670057041338218120067360*x^
+2 + 49723190123383675496609082996917957082213122058381448129/121790695604713
+725861293495831697340114082676436240134720*x + 17299898357937903217009106857
+9518664529518677926525605711/12179069560471372586129349583169734011408267643
+6240134720], [x^12 + 8*x^11 - 96*x^10 - 440*x^9 + 3820*x^8 + 2476*x^7 - 2886
+6*x^6 - 7944*x^5 + 28592*x^4 - 78536*x^3 + 10884*x^2 + 159444*x - 91071, -28
+43543750743793449163008440663651841434705457245/2706459902326971685806522129
+593274224757392809694225216*x^23 - 12602975509304114055800420933216793110727
+93599978009/121790695604713725861293495831697340114082676436240134720*x^22 +
+ 570731733731003725351372109440675289941476598770863/30447673901178431465323
+373957924335028520669109060033680*x^21 + 51592625775826643511866946240589855
+77759549230333661/13532299511634858429032610647966371123786964048471126080*x
+^20 - 5774381398913496913794879696624862509179034351243487/60895347802356862
+930646747915848670057041338218120067360*x^19 - 25468830711446803184345827193
+2277843024215812872643049/40596898534904575287097831943899113371360892145413
+378240*x^18 + 20033262878589630479972285983900066074399699473462869/30447673
+901178431465323373957924335028520669109060033680*x^17 + 55176945627841311088
+2177179020595815257295888900607149/10149224633726143821774457985974778342840
+223036353344560*x^16 - 2196210823307814646404558793935983828145341622776393/
+92686982956403139924880894849084733724568246907336480*x^15 - 281198062671316
+44596199544287679231995494222195918933/1103176590622406937149397607171171559
+00437206916884180*x^14 + 304514561389622743033625986558859551531129703238802
+3343/15223836950589215732661686978962167514260334554530016840*x^13 + 6341682
+965913223773626928153451927146827201459143872529/101492246337261438217744579
+85974778342840223036353344560*x^12 - 971232245781567976274478605121606023057
+45728225275341069/121790695604713725861293495831697340114082676436240134720*
+x^11 - 2551276395028313523351048147505389667653665761782939583/4199679158783
+231926251499856265425521175264704697935680*x^10 + 10180519150826648417575790
+6926793237808116128714341253427/60895347802356862930646747915848670057041338
+218120067360*x^9 - 20272760961834455884178435606038856153257919669244492931/
+40596898534904575287097831943899113371360892145413378240*x^8 - 1865773872120
+99192426030273535446653260095296117785642631/1217906956047137258612934958316
+97340114082676436240134720*x^7 + 3300537798438143487111980175860071706890748
+8889952173347/20298449267452287643548915971949556685680446072706689120*x^6 -
+ 39115208858757321709938597019725056041630348474811196863/608953478023568629
+30646747915848670057041338218120067360*x^5 - 5539361638969286663932918946276
+6239897550377218752807981/40596898534904575287097831943899113371360892145413
+378240*x^4 + 64402986133347052436258518319496764711767679294921068777/608953
+47802356862930646747915848670057041338218120067360*x^3 - 1297933348655958087
+58307214004222253253020247405099177719/1217906956047137258612934958316973401
+14082676436240134720*x^2 + 3900779029174220341536242528866339356307493590998
+2781157/60895347802356862930646747915848670057041338218120067360*x + 3917569
+0216233454071931113702470696322351493063061964491/60895347802356862930646747
+915848670057041338218120067360], [x^12 + 8*x^11 - 96*x^10 - 404*x^9 + 3412*x
+^8 + 1260*x^7 - 21558*x^6 - 22600*x^5 + 115056*x^4 + 129348*x^3 - 158196*x^2
+ - 308636*x - 142411, -668965224263535698951035552798251404886640051188469/1
+21790695604713725861293495831697340114082676436240134720*x^23 - 522171975341
+2401374098995133400803800172339867929167/12179069560471372586129349583169734
+0114082676436240134720*x^22 + 5718323145017272666709458110806890233229264753
+381243/30447673901178431465323373957924335028520669109060033680*x^21 + 12434
+087272000961086772811753580691066313436906507469/761191847529460786633084348
+9481083757130167277265008420*x^20 - 3893187297078473294000250738769036174547
+2914592223087/10149224633726143821774457985974778342840223036353344560*x^19 
+- 1043301491205948231908424718639106834470204686958032907/405968985349045752
+87097831943899113371360892145413378240*x^18 + 667864415723478355393870787186
+1675496441670377137933487/12179069560471372586129349583169734011408267643624
+0134720*x^17 + 22122086031461047810356371854786679936211211173131714093/1217
+90695604713725861293495831697340114082676436240134720*x^16 - 394863083446836
+915534524170968216903183701507703296477/834182846607628259323928053641762603
+521114222166028320*x^15 - 11103731891455954559643155427971936684242173308347
+43981/2647623817493776649158554257210811741610492966005220320*x^14 + 5193183
+9285403408769737480751228092224989253670367415221/30447673901178431465323373
+957924335028520669109060033680*x^13 - 44551296374879728480089523872541484087
+65609414389517739/60895347802356862930646747915848670057041338218120067360*x
+^12 - 339372827622239657883169496965045381019939562026015194207/121790695604
+713725861293495831697340114082676436240134720*x^11 + 26541490769726873500666
+65846456196103044261908748211563/1399893052927743975417166618755141840391754
+901565978560*x^10 + 16167287887659074297609123346059902626725458808559143179
+/15223836950589215732661686978962167514260334554530016840*x^9 - 158397352873
+93423962056277168280332697428495450276352123/7611918475294607866330843489481
+083757130167277265008420*x^8 + 116063234760368696797073011434558912526742361
+85087299217/8119379706980915057419566388779822674272178429082675648*x^7 - 19
+434164957414447238256519636329478106624733429298116103/304476739011784314653
+23373957924335028520669109060033680*x^6 - 2832965223540201167307000776498771
+81268540478120997440163/1217906956047137258612934958316973401140826764362401
+34720*x^5 + 181552408650077417895085086169959141618857336367367324417/121790
+695604713725861293495831697340114082676436240134720*x^4 - 807763889237836951
+23221859525260859276171260950761502643/6089534780235686293064674791584867005
+7041338218120067360*x^3 + 39678238836131920899246476567750525687401412328606
+354359/40596898534904575287097831943899113371360892145413378240*x^2 + 298470
+299527392719046809962954040235580423608315993300221/121790695604713725861293
+495831697340114082676436240134720*x + 11790798094515584514981227444717596268
+5297095338144540411/12179069560471372586129349583169734011408267643624013472
+0], [x^24 + 8*x^23 - 32*x^22 - 298*x^21 + 624*x^20 + 4592*x^19 - 8845*x^18 -
+ 31488*x^17 + 76813*x^16 + 65924*x^15 - 265616*x^14 + 48348*x^13 + 385639*x^
+12 - 394984*x^11 - 20946*x^10 + 369102*x^9 - 362877*x^8 + 183396*x^7 + 43450
+1*x^6 - 194418*x^5 + 450637*x^4 + 125800*x^3 - 16401*x^2 - 45880*x + 115151,
+ x]]
+Total time spent: 6096
diff --git a/src/test/32/subgroup b/src/test/32/subgroup
new file mode 100644
index 0000000..ec02864
--- /dev/null
+++ b/src/test/32/subgroup
@@ -0,0 +1,11 @@
+[53835600, 29]
+[5, 0; 0, 1]
+[10, 0; 0, 1]
+[[12, 0; 0, 1]]
+[]
+[]
+  ***   at top-level: forsubgroup(h=[2,3],2,print(h))
+  ***                                       ^---------
+  ***   incorrect type in forsubgroup [not a group] (t_VEC).
+[]
+Total time spent: 4
diff --git a/src/test/32/subst b/src/test/32/subst
new file mode 100644
index 0000000..94fb2eb
--- /dev/null
+++ b/src/test/32/subst
@@ -0,0 +1,24 @@
+Y/x
+x + 1
+0
+O(x^2)
+  ***   at top-level: subst(x+O(x^2),x,Mod
+  ***                 ^--------------------
+  *** subst: forbidden substitution t_SER , t_INTMOD.
+Mod(0, 3)
+  ***   at top-level: subst(1/x+O(x^2),x,M
+  ***                 ^--------------------
+  *** subst: impossible inverse in gsubst: Mod(0, 3).
+Mod(2, 3)
+-1/-y
+Mod(y*x, y^2*x^2 + 1)
+y*x/(y^2*x^2 + 1)
+List([])
+List([y*x^2])
+x^2 + y^2*x^3 + O(x^4)
+[;]
+  ***   at top-level: subst(1,x,Mat([1,2])
+  ***                 ^--------------------
+  *** subst: forbidden substitution t_INT , t_MAT (1x2).
+4*y^2 + O(y^3)
+Total time spent: 0
diff --git a/src/test/32/sumdedekind b/src/test/32/sumdedekind
new file mode 100644
index 0000000..635eba8
--- /dev/null
+++ b/src/test/32/sumdedekind
@@ -0,0 +1,5 @@
+-1/18
+0
+1145846923/57826382
+56713727820156410558782357164918483627/73786976294838206464
+Total time spent: 4
diff --git a/src/test/32/sumdiv b/src/test/32/sumdiv
new file mode 100644
index 0000000..fda6d59
--- /dev/null
+++ b/src/test/32/sumdiv
@@ -0,0 +1,92 @@
+3628800
+20993420690550
+3628800
+20993420690550
+3628800
+20993420690550
+3628800
+20993420690550
+0
+0
+7
+[7, 720]
+4
+15
+829440
+270
+15334088
+20993420690550
+57335533287534038504
+1
+error("domain error in moebius: argument = 0")
+0
+[0, 1]
+error("domain error in omega: argument = 0")
+error("domain error in bigomega: argument = 0")
+2
+error("domain error in numdiv: argument = 0")
+error("domain error in sumdiv: argument = 0")
+error("domain error in sumdivk: argument = 0")
+error("domain error in sumdivk: argument = 0")
+1
+error("incorrect type in moebius (t_MAT).")
+0
+[0, 1]
+error("incorrect type in omega (t_MAT).")
+error("incorrect type in bigomega (t_MAT).")
+2
+error("incorrect type in numdiv (t_MAT).")
+error("incorrect type in sumdiv (t_MAT).")
+error("incorrect type in sumdivk (t_MAT).")
+error("incorrect type in sumdivk (t_MAT).")
+0
+-1
+-2
+[-2, 1]
+1
+1
+1
+2
+3
+5
+9
+0
+-1
+-2
+[-2, 1]
+1
+1
+1
+2
+3
+5
+9
+1
+1
+1
+[1, 1]
+0
+0
+1
+1
+1
+1
+1
+[1]
+1
+0
+[0, 1]
+  ***   at top-level: divisors(fa)
+  ***                 ^------------
+  *** divisors: domain error in divisors: argument = 0
+[30, 2]
+[1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120]
+x^10 + 16*x^9 + 115*x^8 + 491*x^7 + 1387*x^6 + 2729*x^5 + 3822*x^4 + 3805*x^
+3 + 2616*x^2 + 1146*x + 252
+x^10 + 16*x^9 + 115*x^8 + 491*x^7 + 1387*x^6 + 2729*x^5 + 3822*x^4 + 3805*x^
+3 + 2616*x^2 + 1146*x + 252
+x^10 + 16*x^9 + 115*x^8 + 491*x^7 + 1387*x^6 + 2729*x^5 + 3822*x^4 + 3805*x^
+3 + 2616*x^2 + 1146*x + 252
+x^10 + 16*x^9 + 115*x^8 + 491*x^7 + 1387*x^6 + 2729*x^5 + 3822*x^4 + 3805*x^
+3 + 2616*x^2 + 1146*x + 252
+Total time spent: 0
diff --git a/src/test/32/sumformal b/src/test/32/sumformal
new file mode 100644
index 0000000..d6280c9
--- /dev/null
+++ b/src/test/32/sumformal
@@ -0,0 +1,10 @@
+  ***   at top-level: sumformal(1/n)
+  ***                 ^--------------
+  *** sumformal: incorrect type in sumformal [not a t_POL] (t_RFRAC).
+0
+x
+1/2*n^2 + 1/2*n
+1/3*n^3 + 1/2*n^2 + 1/6*n
+1/2*y*x^2 + (1/2*y + 1)*x
+(1/2*y^2 + 1/2*y)*x + y
+Total time spent: 4
diff --git a/src/test/32/sumiter b/src/test/32/sumiter
new file mode 100644
index 0000000..61c7b12
--- /dev/null
+++ b/src/test/32/sumiter
@@ -0,0 +1,43 @@
+   realprecision = 19 significant digits
+   echo = 1 (on)
+? intnum(x=0,Pi,sin(x))
+2.000000000000000000
+? intnum(x=0,4,exp(-x^2))
+0.8862269117895689458
+? intnum(x=1,[1],1/(1+x^2))-Pi/4
+0.E-19
+? intnum(x=-0.5,0.5,1/sqrt(1-x^2))-Pi/3
+0.E-18
+? intnum(x=0,[[1],-I],sin(x)/x)-Pi/2
+0.E-18
+? \p38
+   realprecision = 38 significant digits
+? prod(k=1,10,1+1/k!)
+3335784368058308553334783/905932868585678438400000
+? prod(k=1,10,1+1./k!)
+3.6821540356142043935732308433185262946
+? Pi^2/6*prodeuler(p=2,10000,1-p^-2)
+1.0000098157493066238697591433298145222
+? prodinf(n=0,(1+2^-n)/(1+2^(-n+1)))
+0.33333333333333333333333333333333333329
+? prodinf(n=0,-2^-n/(1+2^(-n+1)),1)
+0.33333333333333333333333333333333333329
+? solve(x=1,4,sin(x))
+3.1415926535897932384626433832795028842
+? sum(k=1,10,2^-k)
+1023/1024
+? sum(k=1,10,2.^-k)
+0.99902343750000000000000000000000000000
+? 4*sumalt(n=0,(-1)^n/(2*n+1))
+3.1415926535897932384626433832795028842
+? 4*sumalt(n=0,(-1)^n/(2*n+1),1)
+3.1415926535897932384626433832795028842
+? sumdiv(8!,x,x)
+159120
+? suminf(n=1,2.^-n)
+0.99999999999999999999999999999999999999
+? 6/Pi^2*sumpos(n=1,n^-2)
+1.0000000000000000000000000000000000000
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 8
diff --git a/src/test/32/thue b/src/test/32/thue
new file mode 100644
index 0000000..df2737c
--- /dev/null
+++ b/src/test/32/thue
@@ -0,0 +1,26 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+[]
+[[0, -1]]
+[[-4, 3], [4, 3], [-4, -3], [4, -3]]
+[[1, 1]]
+[[1, 0], [-1, 0], [0, 1], [1, 1], [-1, -1], [0, -1]]
+[[1868, 514], [-4, -2]]
+[]
+[[5, 1]]
+[[1, 1]]
+[[1, 1]]
+  ***   at top-level: thueinit(x^0)
+  ***                 ^-------------
+  *** thueinit: constant polynomial in thueinit.
+[[2, 2], [-2, -2], [4, 1], [-4, -1]]
+[[0, 3], [3, -276], [3, 0]]
+[[0, 3], [3, 0], [19, 2], [27, 3]]
+[[-1, 1], [1, 1], [-1, -1], [1, -1]]
+[]
+[[0, 0]]
+[[0, 0]]
+[[-1, -1], [1, -1], [-1, 1], [1, 1]]
+[]
+[[0, 1], [1, -537825], [1, 1], [537824, 1], [1, 0]]
+[[44, -131]]
+Total time spent: 2188
diff --git a/src/test/32/time b/src/test/32/time
new file mode 100644
index 0000000..db791a0
--- /dev/null
+++ b/src/test/32/time
@@ -0,0 +1,3 @@
+1
+1
+Total time spent: 0
diff --git a/src/test/32/trans b/src/test/32/trans
new file mode 100644
index 0000000..1d7b6d0
--- /dev/null
+++ b/src/test/32/trans
@@ -0,0 +1,438 @@
+   realprecision = 2003 significant digits (2000 digits displayed)
+   echo = 1 (on)
+? abs(-0.01)
+0.01000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000
+? agm(1,2)
+1.45679103104690686918643238326508197497386394322130559079417238326792645458
+0250900257473712818448444328189401816036799935576243074340124511691213249952
+2793768970211976726893728266666782707432902072384564600963133367494416649516
+4008269322390862633767383824102548872626451365906604088758851004667281309474
+3978935512911720175447186956416035641113070606125170400972745374521370401420
+1441576823232389645029091322392292018630204591966775362115295609984320494009
+6186133886391108403038148862815907317011423554730230353362620898683561308007
+5985703121250813571733533606272496417145565136129415437696905495272776402217
+1898328404019382434954163396634111712470749200493994758236553202742331569542
+1876892595105619103413471250457295583940482770732998417330233202020190654108
+3764475690954512308594220997449412380273230046465841574004772512701790771147
+6178286660643441589473410355454995401702603050129297014707762364655074858504
+2893120294754259839628734570376126531045680923276419320475962493117272367678
+4849010063883831645335627155765372880260543270126668904548807658246837332956
+7456063204392060008273159252979241205175727929568980698371820180811180125021
+3108997246951100317036787543001787446227930192106015685776149083936743191510
+5478717272782446538831715921363968336746689231345994523668360452657260101103
+3970534995271323625630073974543738138730451563908543487241207008447748794693
+7515044344604858428093017239592603673212918887571985640286492629881099516041
+7385214470404976503137921156910217010840121652176385776278443131535045190731
+0748437504378670908384466987679450508904899924299954903140622820681590930451
+6140452345824869722715061998188837843566441517471116059500690242314345907596
+6810454416997061373268370421830924936517791683419258027937814913005585514983
+9054216129918366396073532425917284089191304056017436113358867622552811309835
+6883812066118653768412057434259281956810028485877428124011968982035483804304
+1113162808407169939503577633814675423251711145297625856010709698328986771681
+3002707534621244314382491
+? agm(1+O(7^5),8+O(7^5))
+1 + 4*7 + 6*7^2 + 5*7^3 + 2*7^4 + O(7^5)
+? 4*arg(3+3*I)
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+6208998628034825342117067982148086513282306647093844609550582231725359408128
+4811174502841027019385211055596446229489549303819644288109756659334461284756
+4823378678316527120190914564856692346034861045432664821339360726024914127372
+4587006606315588174881520920962829254091715364367892590360011330530548820466
+5213841469519415116094330572703657595919530921861173819326117931051185480744
+6237996274956735188575272489122793818301194912983367336244065664308602139494
+6395224737190702179860943702770539217176293176752384674818467669405132000568
+1271452635608277857713427577896091736371787214684409012249534301465495853710
+5079227968925892354201995611212902196086403441815981362977477130996051870721
+1349999998372978049951059731732816096318595024459455346908302642522308253344
+6850352619311881710100031378387528865875332083814206171776691473035982534904
+2875546873115956286388235378759375195778185778053217122680661300192787661119
+5909216420198938095257201065485863278865936153381827968230301952035301852968
+9957736225994138912497217752834791315155748572424541506959508295331168617278
+5588907509838175463746493931925506040092770167113900984882401285836160356370
+7660104710181942955596198946767837449448255379774726847104047534646208046684
+2590694912933136770289891521047521620569660240580381501935112533824300355876
+4024749647326391419927260426992279678235478163600934172164121992458631503028
+6182974555706749838505494588586926995690927210797509302955321165344987202755
+9602364806654991198818347977535663698074265425278625518184175746728909777727
+9380008164706001614524919217321721477235014144197356854816136115735255213347
+5741849468438523323907394143334547762416862518983569485562099219222184272550
+2542568876717904946016534668049886272327917860857843838279679766814541009538
+8378636095068006422512520511739298489608412848862694560424196528502221066118
+6306744278622039194945047123713786960956364371917287467764657573962413890865
+8326459958133904780275901
+? bernreal(12)
+-0.2531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+3553113553113553113553113553113553113553113553113553113553113553113553113553
+1135531135531135531135531135531135531135531135531135531135531135531135531135
+5311355311355311355311355311355311355311355311355311355311355311355311355311
+355311355311355311355311355
+? bernvec(6)
+[1, 1/6, -1/30, 1/42, -1/30, 5/66, -691/2730]
+? eta(q)
+1 - q - q^2 + q^5 + q^7 - q^12 - q^15 + O(q^17)
+? gammah(10)
+1133278.38894878556733457416558889247556029830827515977660872341452948339005
+6004153717630538727607290658350271700893237334889580173178076577597995379664
+6009714415152490764416630481375706606053932396039541459764525989187023837695
+1671610855238044170151137400635358652611835795089229729903867565432085491785
+4385740637379886563030379410949122020517030255827739818376409926875136586189
+2723863412249690833216320407918186480305202146014474770321625907339955121137
+5592642390902407584016964257200480120814533383602757695668466603948271024098
+9327940404023866529740516995285324916879158647845355052036653927090566136730
+0094575478250332011940143726954935586482054200041299507288301750480889450074
+6343904971296912338686722783533463981407672637863409944118391772608796763236
+9447079178552767334696553209914181695759970997941993901164691598147347830004
+4823839605663115658079374350293361148126253885222073444191541294051101114944
+2148757269775793389728426903218921936202601614618932645339512192242743521391
+3623655029508006651504215607326378350230912034475135438952688674605137188671
+8291478726407002040566684129567384943465438236552781293212272474626739330722
+3823357944724162685811265841905467657996783321819427448381523647154314724898
+8856361879313902224622692050075011483135711717132961476630033785190129658511
+7517708668749218485078393526224163290497667641778463362558549256811856160652
+4106684792418747471383982225174086085681964985490608637796815226536639176681
+1441751691654768874563756211537865821827254193841183086848150171014212517613
+4162649414056791266931385305249721381461657257845049119527820872404022311592
+3493153739717855496390762049815239940623016182617392553134094087438136687759
+5419535805662758475769269988659439227267578534611414012815013931015921875970
+6336658641047462598114625941565529553227923237890531007539153745378752638260
+5084066808355122734552729235496172099847732335381840125710668124155748264901
+6432532465927671474115401431858884909633728259417038958526362232126251606829
+1066841997114282966060548
+? Pi
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+6208998628034825342117067982148086513282306647093844609550582231725359408128
+4811174502841027019385211055596446229489549303819644288109756659334461284756
+4823378678316527120190914564856692346034861045432664821339360726024914127372
+4587006606315588174881520920962829254091715364367892590360011330530548820466
+5213841469519415116094330572703657595919530921861173819326117931051185480744
+6237996274956735188575272489122793818301194912983367336244065664308602139494
+6395224737190702179860943702770539217176293176752384674818467669405132000568
+1271452635608277857713427577896091736371787214684409012249534301465495853710
+5079227968925892354201995611212902196086403441815981362977477130996051870721
+1349999998372978049951059731732816096318595024459455346908302642522308253344
+6850352619311881710100031378387528865875332083814206171776691473035982534904
+2875546873115956286388235378759375195778185778053217122680661300192787661119
+5909216420198938095257201065485863278865936153381827968230301952035301852968
+9957736225994138912497217752834791315155748572424541506959508295331168617278
+5588907509838175463746493931925506040092770167113900984882401285836160356370
+7660104710181942955596198946767837449448255379774726847104047534646208046684
+2590694912933136770289891521047521620569660240580381501935112533824300355876
+4024749647326391419927260426992279678235478163600934172164121992458631503028
+6182974555706749838505494588586926995690927210797509302955321165344987202755
+9602364806654991198818347977535663698074265425278625518184175746728909777727
+9380008164706001614524919217321721477235014144197356854816136115735255213347
+5741849468438523323907394143334547762416862518983569485562099219222184272550
+2542568876717904946016534668049886272327917860857843838279679766814541009538
+8378636095068006422512520511739298489608412848862694560424196528502221066118
+6306744278622039194945047123713786960956364371917287467764657573962413890865
+8326459958133904780275901
+? precision(Pi,38)
+3.14159265358979323846264338327950288420
+? sqr(1+O(2))
+1 + O(2^3)
+? sqrt(13+O(127^12))
+34 + 125*127 + 83*127^2 + 107*127^3 + 53*127^4 + 42*127^5 + 22*127^6 + 98*12
+7^7 + 127^8 + 23*127^9 + 122*127^10 + 79*127^11 + O(127^12)
+? teichmuller(7+O(127^12))
+7 + 57*127 + 58*127^2 + 83*127^3 + 52*127^4 + 109*127^5 + 74*127^6 + 16*127^
+7 + 60*127^8 + 47*127^9 + 65*127^10 + 5*127^11 + O(127^12)
+? \p500
+   realprecision = 500 significant digits
+? Catalan
+0.91596559417721901505460351493238411077414937428167213426649811962176301977
+6254769479356512926115106248574422619196199579035898803325859059431594737481
+1584069953320287733194605190387274781640878659090247064841521630002287276409
+4238825995774150881639747025248201156070764488380787337048990086477511322599
+7134340748540755323076856533576809583526021938232395080072068035576104823573
+3942319149829836189977069036404180862179411019175327431499782339761055122477
+9530324875371878665828082360570225594194818097
+? Euler
+0.57721566490153286060651209008240243104215933593992359880576723488486772677
+7664670936947063291746749514631447249807082480960504014486542836224173997644
+9235362535003337429373377376739427925952582470949160087352039481656708532331
+5177661152862119950150798479374508570574002992135478614669402960432542151905
+8775535267331399254012967420513754139549111685102807984234877587205038431093
+9973613725530608893312676001724795378367592713515772261027349291394079843010
+3417771778088154957066107501016191663340152279
+? acos(0.5)
+1.04719755119659774615421446109316762806572313312503527365831486410260546876
+2069666209344941780705689327382695504427435549031281536516860743908453136042
+8270391500947009006461737018532148743163183101273214762703252219778153761585
+4941126226105509040063638188285564115344953681810888273779786908674971375790
+8195668868771862724960506973654276418030571788122630863453337110176849606822
+1737947156506471705364776857567885865306510307287057939775372643683728493581
+541266542498557839619175749637426460610039831
+? acosh(3)
+1.76274717403908605046521864995958461805632065652327082150659121730675436844
+4052175667413783820512085713479632384212984377524145023953183875054510925531
+5808184431573607257943924806147148192510979557431265247356130135260657908083
+2711638011905460870335948934683023103172356012785221262668194525145789831496
+9445764001529311893860982812579887622449034763169345542526389217689105106337
+1787365189299048490338319777210134365908031791918295896639410019154526845141
+480345838118685682417318463628901744528191443
+? 3*asin(sqrt(3)/2)
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+6208998628034825342117067982148086513282306647093844609550582231725359408128
+4811174502841027019385211055596446229489549303819644288109756659334461284756
+4823378678316527120190914564856692346034861045432664821339360726024914127372
+4587006606315588174881520920962829254091715364367892590360011330530548820466
+5213841469519415116094330572703657595919530921861173819326117931051185480744
+623799627495673518857527248912279381830119491
+? asinh(0.5)
+0.48121182505960344749775891342436842313518433438566051966101816884016386760
+8221774412009429122723474997231839958293656411272568323726737622753059241864
+4097541824170072118371502238239374691872752432791930187970790035617267969445
+4575230534543418876528553256490207399693496618755630102123996367930820635997
+7988509980156825797852649328666651116241713808272592788479026096533113247227
+5149314064985088932176366002566661953210679681757661847307351598603984845754
+5412056323413570047800639487224315261789680045
+? 3*atan(sqrt(3))
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+6208998628034825342117067982148086513282306647093844609550582231725359408128
+4811174502841027019385211055596446229489549303819644288109756659334461284756
+4823378678316527120190914564856692346034861045432664821339360726024914127372
+4587006606315588174881520920962829254091715364367892590360011330530548820466
+5213841469519415116094330572703657595919530921861173819326117931051185480744
+623799627495673518857527248912279381830119491
+? atanh(0.5)
+0.54930614433405484569762261846126285232374527891137472586734716681874714660
+9304483436807877406866044393985014532978932871184002112965259910526400935383
+6387053015813845916906835896868494221804799518712851583979557605727959588753
+3567352747008338779011110158512647344878034505326075282143406901815868664928
+8891183495827396065909074510015051911815061124326374099112995548726245448229
+0267335044229825428742220595094285438237474335398065429147058010830605920007
+0491275719597438444683992471511278657676648427
+? besseljh(1,1)
+0.24029783912342701089584304474193368045758480608072900860700721913956804181
+9821642483230581867706826873304134469286897059613333800107373387969440858132
+2409671228346463513063730101700769785661236389472736777787130860593313537501
+4950471611773181090861874975058165031596147120593670107339079838226694509538
+1174862561382806604491442967609698710345402983618630021989455840750069855186
+9089492304665506543890102558566214670131694260158621630986009048855189842820
+0103186464147214505293464124112486584095535336
+? cos(1)
+0.54030230586813971740093660744297660373231042061792222767009725538110039477
+4471764517951856087183089343571731160030089097860633760021663456406512265417
+3185847179711644744794942331179245513932543359435177567028925963757361543275
+4964175449177511513122273010063135707823223677140151746899593667873067422762
+0245077637440675874981617842720216455851115632968890571081242729331698685247
+1456894904342375433094423024093596239583182454728173664078071243433621748100
+3220271297578822917644683598726994264913443918
+? cosh(1)
+1.54308063481524377847790562075706168260152911236586370473740221471076906304
+9223698964264726435543035587046858604423527565032194694709586290763493942377
+3472069151633480026408029059364105029494057980033657762593319443209506958499
+1368981037430548471273929845616039038581747145363600451873630682751434880120
+2720574972705524471670706447103271142282939448411677273102139632958667273012
+2826261409857215459162042522453939258584439199475134380734969475319971032521
+055637731102374474158960765443652715148207669
+? exp(1)
+2.71828182845904523536028747135266249775724709369995957496696762772407663035
+3547594571382178525166427427466391932003059921817413596629043572900334295260
+5956307381323286279434907632338298807531952510190115738341879307021540891499
+3488416750924476146066808226480016847741185374234544243710753907774499206955
+1702761838606261331384583000752044933826560297606737113200709328709127443747
+0472306969772093101416928368190255151086574637721112523897844250569536967707
+854499699679468644549059879316368892300987931
+? exp(1.123)
+3.07406257154898987680161138009760625104248179708261339399712186197767466996
+4935625311477807765382361174054209564400933143178772679923822312458571526893
+0949675915002937652898704613739372482459452568993085662295138072557500421797
+5971600253639265100975969190654549368799844236165029593059925114588814911583
+9185488320031389051117206437605098919216790228388886978184284707042848120462
+1182818728513135542290354814654148922271957843494116542832234810156127014491
+955053641170027738831683277094167546025000529
+? incgam(4,1,6)
+5.88607105874307714552838032258337387913297809650828535212538882715938393191
+8396853714356389534714299946037204429503923331951612684642064138026457431905
+5805294751098780374098407782238580023298615198035196589516153270359568407982
+7992725182985932743696823436032979670756942663882506560584119323653928852565
+9814209708876601791309278295271957611829097587465878928057118995331313636440
+2883453599077405070514506827481973857316860179666499801153515201126481557348
+108412200404484860301786425134984607926838502
+? incgamc(2,1)
+0.26424111765711535680895245967707826510837773793646433098432639660507700851
+0200393285705451308160712506745349446312009583506048414419741982746692821011
+8024338156112652453237699027220177497087673100245600426310480841205053949002
+1500909352126758407037897070495877541155382167014686679926985084543258893429
+2523223786390424776086340213091005298521362801566765133992860125583585795444
+9639568300115324366185686646564753267835392477541687524855810599859189805331
+4864484749494393924622766968581269240091451867
+? log(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996418687542001481020570685733685520235758130557032670751635
+0759619307275708283714351903070386238916734711233501153644979552391204751726
+8157493206515552473413952588295045300709532636664265410423915781495204374043
+0385500801944170641671518644712839968171784546957026271631064546150257207402
+4816377733896385506952606683411372738737229289564935470257626520988596932019
+6505855476470330679365443254763274495125040607
+? sin(Pi/6)
+0.50000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000
+? sinh(1)
+1.17520119364380145688238185059560081515571798133409587022956541301330756730
+4323895607117452089623391840419533327579532356785218901919457282136840352883
+2484238229689806253026878572974193778037894530156457975748559863812033933000
+2119435713493927674792878380863977809159438228870943791837123225023064326834
+8982186865900736859713876553648773791543620849195059840098569695750460170734
+7646045559914877642254885845736315892502135438245978143162874775249565935186
+798861968577094170390099113872716177152780263
+? sqr(tan(Pi/3))
+3.00000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000
+? tanh(1)
+0.76159415595576488811945828260479359041276859725793655159681050012195324457
+6638483458947521673676714421902759701554077532368309114762485413297006669611
+3211253965101376080877764393409926042066795531174758011305900662577831975245
+1237997591796119707757354591410814335043351567518059703276048802963895774140
+4110555282743457474128870116732022433666141820426521385314984008017809424940
+5971665020197077111278076211510055741702778683601321201082307883017522102475
+0850545493659202265152413525903793814306804484
+? thetanullk(0.5,7)
+-804.63037320243369422783730584965684022502842525603918290428537089203649185
+3005202838354617419978916066838351498344792388634514250685494567531066970308
+1395985000299687911464724641787835671746030420666636980738176244141521534964
+5910468287548147547821547802569972386188420035275376210374637455233928908304
+8519707951113024675783203592515011343853492633432924541927657918744234297707
+8009339159045897789510058204677594956471190358977738843586880213576194151544
+6040652826323066997075899093444932117587282486
+? \p210
+   realprecision = 211 significant digits (210 digits displayed)
+? dilog(0.5)
+0.58224052646501250590265632015968010874419847480612642543434704787317104407
+1683200816840318587915857185644360650489146599186798136823369642378773825725
+010992996274322284433100379999291599248198351965163954430361
+? eint1(2)
+0.04890051070806111956723983522804952231449218496302311632732287371169292871
+4152191279268961007451641767339733440496339126093474911387068904573480132428
+0606565260878276314803271231475388617592828799527149833070515
+? lngamma(10^50*I)
+-157079632679489661923132169163975144209858469968811.93673753887608474948977
+0941153418951907406847934940095420371647821881900698782085734298414871973667
+351244826946727013485797329023211606491949054831345082284018 + 1141292546497
+0228420089957273421821038005507443143864.09476847610738955343272591658130426
+4976155641647932550343141949832879612722439831043441291767982893579577059574
+3877177782974245137531522747279687821610884364*I
+? polylog(5,0.5)
+0.50840057924226870745910884925858994131954112566482164872449779635262539422
+8780242619384210049344955062253148566177885373776251290109126927256295587733
+653575441097747430180753135597085935261518462072899907112039
+? polylog(-4,t)
+(t^4 + 11*t^3 + 11*t^2 + t)/(-t^5 + 5*t^4 - 10*t^3 + 10*t^2 - 5*t + 1)
+? polylog(5,0.5,1)
+1.03379274554168906408344764673478841754654188263517803810922886849674521856
+8302490767987790059233900087664928281011147504065464055196977752510643903051
+08453214093020806938180803753912648028281347292317330014656
+? polylog(5,0.5,2)
+1.03445942344901048625461825783418822628308099519811715037388226488478462874
+5613316541842884367897989911634714028478465772399056966065341954518002332809
+93803867195735501893802985262734041524337126856608372430479
+? polylog(5,0.5,3)
+0.94956934899649226018699647701016092398772870595673235481511016276008056001
+9780143078976018486726179185715990894178927384257428042889858760164776911430
+334108913396327982261675208743365007260765477862866539420350
+? psi(1)
+-0.5772156649015328606065120900824024310421593359399235988057672348848677267
+7766467093694706329174674951463144724980708248096050401448654283622417399764
+4923536253500333742937337737673942792595258247094916008735204
+? round(prod(k=1,17,x-exp(2*I*Pi*k/17)),&e)
+x^17 - 1
+? e
+-693
+? theta(0.5,3)
+0.08080641825189469129987168321046629852436630463736585818145355698789812007
+7007090242373481570553349455066987093523256662570622075796055596272586626054
+1756288186798491280103427257359418016911094472073083250230198
+? weber(I)
+1.18920711500272106671749997056047591529297209246381741301900222471946666822
+6917159870781344538137673716037394774769213186063726361789847756785360862538
+01777507015151140355709227316234286888992417544607190871050
+? weber(I,1)
+1.09050773266525765920701065576070797899270271854006712178566764768330053084
+8841840338211140494203119891451619262918090010347769026116087255320275930582
+70136445935603377184958072509793552467405409688916300069889
+? weber(I,2)
+1.09050773266525765920701065576070797899270271854006712178566764768330053084
+8841840338211140494203119891451619262918090010347769026116087255320275930582
+70136445935603377184958072509793552467405409688916300069889
+? zeta(3)
+1.20205690315959428539973816151144999076498629234049888179227155534183820578
+6313090186455873609335258146199157795260719418491995998673283213776396837207
+90016145394178294936006671919157552224249424396156390966410
+? \p38
+   realprecision = 38 significant digits
+? besselk(1+I,1)
+0.32545977186584141085464640324923711950 + 0.2894280370259921276345671592415
+2302743*I
+? erfc(2)
+0.0046777349810472658379307436327470713891
+? gamma(10.5)
+1133278.3889487855673345741655888924756
+? hyperu(1,1,1)
+0.59634736232319407434107849936927937607
+? incgam(2,1)
+0.73575888234288464319104754032292173491
+? zeta(0.5+14.1347251*I)
+5.2043097453468479398562848599360610966 E-9 - 3.2690639869786982176409251733
+763732423 E-8*I
+? if(getheap()!=HEAP,getheap())
+? print("Total time spent: ",gettime);
+Total time spent: 44
diff --git a/src/test/32/trans2 b/src/test/32/trans2
new file mode 100644
index 0000000..fdeab87
--- /dev/null
+++ b/src/test/32/trans2
@@ -0,0 +1,90 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+5.0431656433600286513118821892854247103
+[0.54030230586813971740093660744297660373 - 0.841470984807896506652502321630
+29899962*I, 0.54030230586813971740093660744297660373 + 0.8414709848078965066
+5250232163029899962*I]~
+error("incorrect type in exp (t_STR).")
+O(2^0)
+error("impossible inverse in powp: O(2^0).")
+0
+1
+2 + O(x^2)
+2.8284271247461900976033774484193961571 + O(x^2)
+Mod(1, y) + Mod(1/2, y)*x + O(x^2)
+2 + 1/4*x + O(x^2)
+error("domain error in gpow [irrational exponent]: valuation != 0")
+O(x^0)
+1 + 0.50000000000000000000000000000000000000*x + O(x^2)
+error("overflow in sqrtn [valuation].")
+error("incorrect type in gpow (t_STR).")
+error("incorrect type in gpow(0,n) (t_INTMOD).")
+x^196608
+error("overflow in pow_monome [degree].")
+error("overflow in gpow.")
+1.0000000000000000000000000000000000000
+0.E-38
+1 + 1/2*x - 1/16*x^2 + 1/32*x^3 - 21/1024*x^4 + 31/2048*x^5 - 195/16384*x^6 
++ 319/32768*x^7 - 34325/4194304*x^8 + 58899/8388608*x^9 + O(x^10)
+[1.0000000000000000000000000000000000000, 1.45679103104690686918643238326508
+19750]
+3 + 2*3^2 + 3^3 + O(3^4)
+error("incorrect type in exp (t_INTMOD).")
+1 + O(3^5)
+1 + 3 + 3^2 + 2*3^3 + 2*3^4 + O(3^5)
+0.54030230586813971740093660744297660373 + 0.8414709848078965066525023216302
+9899962*I
+error("incorrect type in log (t_INTMOD).")
+error("domain error in Qp_log: argument = 0")
+O(3^4)
+0.E-38 + 1.5707963267948966192313216916397514421*I
+error("incorrect type in cos (t_INTMOD).")
+1 + O(3^5)
+1 + 3^2 + 2*3^4 + O(3^6)
+1.5430806348152437784779056207570616826
+error("incorrect type in sin (t_INTMOD).")
+O(3^5)
+3 + 3^2 + 3^3 + 2*3^4 + O(3^5)
+1.1752011936438014568823818505956008152*I
+error("incorrect type in tan (t_INTMOD).")
+O(3^5)
+3 + 3^2 + 3^4 + O(3^5)
+0.76159415595576488811945828260479359041*I
+error("incorrect type in cotan (t_INTMOD).")
+error("impossible inverse in divpp: O(3^5).")
+3^-1 + 2 + 3^2 + O(3^3)
+-1.3130352854993313036361612469308478329*I
+0.27175258531951171652884372249858892071 + 1.0839233273386945434757520612119
+717214*I
+-0.45765755436028576375027741043204727643
+1.5707963267948966192313216916397514421 + O(x)
+-1.5707963267948966192313216916397514421 + O(x)
+1.5707963267948966192313216916397514421*I + O(x)
+-1.5707963267948966192313216916397514421*I + O(x)
+O(x)
+1.4142135623730950488016887242096980786*x + O(x^2)
+1.3169578969248167086250463473079684440 + 0.57735026918962576450914878050195
+745565*x - 0.19245008972987525483638292683398581855*x^2 + O(x^3)
+O(x)
+0.84147098480789650665250232163029899962*I
+1.6 e-38
+4.9 e-39
+3.1 e-39
+0.e-38
+0.e-38
+0.e-38
+0.e-38
+0.e-38
+3.4 e-39
+3.5 e-39
+0.e-38
+0.e-38
+0.e-38
+4.2 e-39
+0.e-38
+0.e-38
+-0.20484755831421800270211268209700671730 - 1.024400881608445881724860454410
+8866770*I
+2.2048475583142180027021126820970067173 - 1.02440088160844588172486045441088
+66770*I
+0.E-2003
+Total time spent: 12
diff --git a/src/test/32/valuation b/src/test/32/valuation
new file mode 100644
index 0000000..6cd177b
--- /dev/null
+++ b/src/test/32/valuation
@@ -0,0 +1,27 @@
+oo oo oo oo 
+0 0 0 0 
+0 -1 0 0 
+ERROR ERROR 0 0 
+0 ERROR 0 0 
+ERROR 0 0 0 
+ERROR ERROR 0 0 
+1 0 0 0 
+ERROR 1 0 0 
+0 0 ERROR 0 
+1 0 0 0 
+0 1 1 ERROR 
+  ***   at top-level: valuation(0,1)
+  ***                 ^--------------
+  *** valuation: domain error in gvaluation: p = 1
+  ***   at top-level: valuation(0,-1)
+  ***                 ^---------------
+  *** valuation: domain error in gvaluation: p = -1
+  ***   at top-level: valuation(0,0)
+  ***                 ^--------------
+  *** valuation: domain error in gvaluation: p = 0
+2
+1.0000000000000000000000000000000000000 - 1.00000000000000000000000000000000
+00000*x + O(x^2)
+1
+O(x)
+Total time spent: 8
diff --git a/src/test/32/variable b/src/test/32/variable
new file mode 100644
index 0000000..e35a088
--- /dev/null
+++ b/src/test/32/variable
@@ -0,0 +1,12 @@
+0
+3
+x
+x
+y
+y
+y
+z
+0
+y
+[x, y, z, v]
+Total time spent: 4
diff --git a/src/test/32/whatnow b/src/test/32/whatnow
new file mode 100644
index 0000000..26928e0
--- /dev/null
+++ b/src/test/32/whatnow
@@ -0,0 +1,22 @@
+This function was suppressed
+
+New syntax: char(x,y) ===> charpoly(x,y)
+
+charpoly(A,{v='x},{flag=5}): det(v*Id-A)=characteristic polynomial of the 
+matrix or polmod A. flag is optional and ignored unless A is a matrix; it ma
+y 
+be set to 0 (Le Verrier), 1 (Lagrange interpolation), 2 (Hessenberg form), 3
+ 
+(Berkowitz), 4 (modular) if A is integral, or 5 (default, choose best method
+). 
+Algorithms 0 (Le Verrier) and 1 (Lagrange) assume that n! is invertible, whe
+re 
+n is the dimension of the matrix.
+
+This function did not change
+
+As far as I can recall, this function never existed
+
+New syntax: compimag(x,y) ===> x*y
+
+Total time spent: 0
diff --git a/src/test/32/zetak b/src/test/32/zetak
new file mode 100644
index 0000000..3ef1e32
--- /dev/null
+++ b/src/test/32/zetak
@@ -0,0 +1,52 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+0.38333333333333333333333333333333333333
+1.1900844397698990786847627856353764894
+1.5621990258332796845160673535794981916
+0
+0.52829297004121867318219948972250171261 - 0.6001012794792921690364554072200
+6732485*I
+0
+1.0626469254716411511118505129747317450
+1.2851909554841494029175117986995746040
+-1/6
+0.27507533904957993693083735106782913088 - 0.3452281589507528653748718471820
+1732549*I
+19.233333333333333333333333333333333333
+1.1534151088973390171523689045164417703
+1.4238865750458932917520934583801572803
+0
+0.75988495679933103986934747406595883227 - 0.2667106768393931037951306887756
+2967178*I
+0
+1.1979816549838251284996760656144424126
+1.6026632619004405990075072278650989584
+0
+0.56325049211603379810700612339514262331 - 0.7431218796586144471407060306283
+8684011*I
+0
+1.1897132625253515180121146266710359469
+1.5629865407014105763649713127143672948
+0
+0.080540708067958142852744201044408963832 - 0.799351155467781709752299811229
+26087752*I
+2*5^-1 + 2*5 + 3*5^2 + 3*5^4 + 3*5^5 + 2*5^6 + 5^7 + 4*5^8 + O(5^9)
+0.57721614942066140874800424251188396262 - 99.999271841202858157138397118797
+159155*I
+1.0000000000000000000000000000000000000
+1.0000000000000000000000000000000000000
+1.0000000000000000000000000000007888609
+1.0000000004656629065033784072989233251
+1.0000000000000000000000000000007731864 - 1.56474806799752292404311992386390
+49803 E-31*I
+1.0000000000000000006672083904260744090 - 5.54210563151691387135805391417775
+67374 E-19*I
+-1.8236338315400224657144248914124703368 E1769 + 6.8223788001755144705322033
+655798283436 E1768*I
+2^-1 + 1 + 2^2 + 2^3 + 2^5 + 2^6 + 2^7 + O(2^9)
+1.0000000000000000000000000000000000000
+0.E-38
+error("overflow in zeta [large negative argument].")
+1.6449340668482264364724151666460251892 - 9.37548254315843753702574094567864
+97790 E-102*I
+100.57794333849687249028215428579024415
+Total time spent: 1344
diff --git a/src/test/32/zn b/src/test/32/zn
new file mode 100644
index 0000000..bac685d
--- /dev/null
+++ b/src/test/32/zn
@@ -0,0 +1,45 @@
+Mod(1, 2)
+Mod(3, 4)
+  ***   at top-level: znprimroot(8)
+  ***                 ^-------------
+  *** znprimroot: domain error in znprimroot: argument = 8
+Mod(5, 9223372036854775837)
+Mod(5, 85070591730234616400799229995519050569)
+Mod(5, 170141183460469232801598459991038101138)
+Mod(5, 170141183460469232801598459991038101138)
+Mod(5, 170141183460469232801598459991038101138)
+[2, [2], [-1]]
+[1, [], []]
+[4, [4], [Mod(7, 10)]]
+[6, [6], [Mod(3, 14)]]
+[216, [36, 6], [Mod(2, 247), Mod(160, 247)]]
+[216, [36, 6], [Mod(2, 247), Mod(160, 247)]]
+[216, [36, 6], [Mod(2, 247), Mod(160, 247)]]
+[2, [2], [-1]]
+  *** _^_: Warning: Mod(a,b)^n with n >> b : wasteful.
+Mod(84, 148)
+[599, 599, 599, 599]
+[1000000003700000003419, 1000000003700000003419, 1000000003700000003419, 100
+0000003700000003419]
+[]
+[]
+[]
+[]
+1
+48
+[]
+[]
+[]
+194
+Mod(1, 2)
+945843084768538962295343
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_COMPLEX).")
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_INT).")
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_MAT).")
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_MAT).")
+[]
+Total time spent: 1392
diff --git a/src/test/32/zncoppersmith b/src/test/32/zncoppersmith
new file mode 100644
index 0000000..2f56c69
--- /dev/null
+++ b/src/test/32/zncoppersmith
@@ -0,0 +1,9 @@
+[100000000000]
+1
+[1339991002000615200]
+1
+[-997, -955, -913, -871, -829, -787, -745, -703, -661, -619, -577, -535, -49
+3, -451, -409, -367, -325, -283, -241, -199, -157, -115, -73, -31, 11, 53, 9
+5, 137, 179, 221, 263, 305, 347, 389, 431, 473, 515, 557, 599, 641, 683, 725
+, 767, 809, 851, 893, 935, 977]
+Total time spent: 284
diff --git a/src/test/64/bnr b/src/test/64/bnr
new file mode 100644
index 0000000..7ce77ea
--- /dev/null
+++ b/src/test/64/bnr
@@ -0,0 +1,44 @@
+[]
+[[4, 0, 0; 0, 2, 0; 0, 0, 2], [4, 0, 2; 0, 2, 1; 0, 0, 1], [4, 2, 0; 0, 1, 0
+; 0, 0, 2], [4, 0, 2; 0, 2, 0; 0, 0, 1], [2, 0, 0; 0, 2, 0; 0, 0, 2], [4, 0,
+ 0; 0, 2, 1; 0, 0, 1], [4, 0, 0; 0, 1, 0; 0, 0, 2], [4, 0, 0; 0, 2, 0; 0, 0,
+ 1], [2, 0, 1; 0, 2, 1; 0, 0, 1], [1, 0, 0; 0, 2, 0; 0, 0, 2], [2, 0, 0; 0, 
+2, 1; 0, 0, 1], [4, 0, 2; 0, 1, 0; 0, 0, 1], [4, 2, 2; 0, 1, 0; 0, 0, 1], [2
+, 0, 0; 0, 1, 0; 0, 0, 2], [4, 2, 0; 0, 1, 0; 0, 0, 1], [2, 0, 0; 0, 2, 0; 0
+, 0, 1], [4, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0; 0, 2, 1; 0, 0, 1]]
+[[1, 0, 0; 0, 2, 1; 0, 0, 1]]
+[[1, 0, 0; 0, 2, 1; 0, 0, 1], [2, 0, 1; 0, 1, 0; 0, 0, 1], [2, 1, 1; 0, 1, 0
+; 0, 0, 1], [1, 0, 0; 0, 1, 0; 0, 0, 2], [2, 1, 0; 0, 1, 0; 0, 0, 1], [1, 0,
+ 0; 0, 2, 0; 0, 0, 1], [2, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0; 0, 1, 0; 0, 0,
+ 1]]
+  ***   at top-level: bnrL1(bnrinit(bnfini
+  ***                 ^--------------------
+  *** bnrL1: incorrect type in bnrL1 [subgroup] (t_INT).
+[32, 0, 27656345068767491604576153420888539136]
+0, 12, [24, 12, 40621487921685401825918161408203125]
+0, 12, [24, 12, 40621487921685401825918161408203125]
+1, 12, [24, 12, 40621487921685401825918161408203125]
+1, 6, [12, 12, 18026977100265125]
+12
+2
+12
+2
+[24, 12, 40621487921685401825918161408203125]
+[4, 4, 262205]
+[24, 12, 40621487921685401825918161408203125]
+[4, 4, 262205]
+[[5, 3; 0, 1], [1, 0]]
+[[5, 3; 0, 1], [0, 0]]
+[[5, 3; 0, 1], [1, 0]]
+[[5, 3; 0, 1], [0, 0]]
+[2, 2, [5, 3; 0, 1]]
+0
+0
+[4, 4, 262205]
+[2, 2, [5, 3; 0, 1]]
+6
+0.99197791640852265169404803750682485966 + 0.1264112865127400184730959576238
+0269659*I
+[4, [2, 2], [[3, 1; 0, 1], [114, 1; 0, 1]]]
+[[1, 0, 0; 0, 1, 0; 0, 0, 1], [0]]
+Total time spent: 28
diff --git a/src/test/64/bnrL1 b/src/test/64/bnrL1
new file mode 100644
index 0000000..0544e8a
--- /dev/null
+++ b/src/test/64/bnrL1
@@ -0,0 +1,33 @@
+[[1, 0.38224508584003564132935849918485739404 + 0.E-38*I], [1, 0.38224508584
+003564132935849918485739404 + 0.E-38*I], [0, -3/2]]
+[[2, 2.9500952396964494033818838179661887250 - 0.788700989456330674320020455
+38518403783*I], [2, 1.9859006356377427761789576705596841597], [2, 2.95009523
+96964494033818838179661887250 + 0.78870098945633067432002045538518403783*I],
+ [1, -0.65847894846240835431252317365398422201]]
+[[1, 1.5356246494250488796129024765168058300 - 1.753485276139541406647384954
+4183759911*I], [2, 2.9500952396964494033818838179661887250 + 0.7887009894563
+3067432002045538518403783*I], [1, 1.1816366573593674455773236880707753793 - 
+2.7350893332487963668865434012735333609*I], [2, 1.98590063563774277617895767
+05596841597], [1, 1.1816366573593674455773236880707753793 + 2.73508933324879
+63668865434012735333609*I], [2, 2.9500952396964494033818838179661887250 - 0.
+78870098945633067432002045538518403783*I], [1, 1.535624649425048879612902476
+5168058300 + 1.7534852761395414066473849544183759911*I], [1, -0.658478948462
+40835431252317365398422201]]
+[[1, 1.1816366573593674455773236880707753792 + 2.735089333248796366886543401
+2735333609*I], [2, 2.9500952396964494033818838179661887250 + 0.7887009894563
+3067432002045538518403783*I], [1, 1.5356246494250488796129024765168058300 + 
+1.7534852761395414066473849544183759911*I], [2, 1.98590063563774277617895767
+05596841597], [1, 1.5356246494250488796129024765168058300 - 1.75348527613954
+14066473849544183759911*I], [2, 2.9500952396964494033818838179661887250 - 0.
+78870098945633067432002045538518403783*I], [1, 1.181636657359367445577323688
+0707753792 - 2.7350893332487963668865434012735333609*I], [0, 4.0000000000000
+000000000000000000000000], [1, 0.49056950319423383962705716863100284403 - 1.
+7496558360390569095857738490658820097*I], [0, 1.6000000000000000000000000000
+000000000 - 0.80000000000000000000000000000000000000*I], [1, -0.286637225639
+49733694072756623658748705 - 1.1610559298277086090237596773995345405*I], [0,
+ 0.16666666666666666666666666666666666670], [1, -0.2866372256394973369407275
+6623658748705 + 1.1610559298277086090237596773995345405*I], [0, 1.6000000000
+000000000000000000000000000 + 0.80000000000000000000000000000000000000*I], [
+1, 0.49056950319423383962705716863100284403 + 1.7496558360390569095857738490
+658820097*I], [1, -0.65847894846240835431252317365398422201]]
+Total time spent: 108
diff --git a/src/test/64/compat b/src/test/64/compat
new file mode 100644
index 0000000..7392970
--- /dev/null
+++ b/src/test/64/compat
@@ -0,0 +1,2593 @@
+   echo = 1 (on)
+? default(compatible,3)
+  *** default: Warning: user functions re-initialized.
+? +3
+3
+? -5
+-5
+? 5+3
+8
+? 5-3
+2
+? 5/3
+5/3
+? 5\3
+1
+? 5\/3
+2
+? 5%3
+2
+? 5^3
+125
+? \precision=154
+   realprecision = 154 significant digits
+? pi
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+6208998628034825342117067982148086513282306647093844609550582231725359408128
+481
+? \precision=38
+   realprecision = 38 significant digits
+? o(x^12)
+O(x^12)
+? padicno=(5/3)*127+o(127^5)
+44*127 + 42*127^2 + 42*127^3 + 42*127^4 + O(127^5)
+? initrect(0,500,500)
+? abs(-0.01)
+0.010000000000000000000000000000000000000
+? acos(0.5)
+1.0471975511965977461542144610931676281
+? acosh(3)
+1.7627471740390860504652186499595846181
+? acurve=initell([0,0,1,-1,0])
+[0, 0, 1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vecs
+mall([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? apoint=[2,2]
+[2, 2]
+? isoncurve(acurve,apoint)
+1
+? addell(acurve,apoint,apoint)
+[21/25, -56/125]
+? addprimes([nextprime(10^9),nextprime(10^10)])
+[1000000007, 10000000019]
+? adj([1,2;3,4])
+
+[ 4 -2]
+
+[-3  1]
+
+? agm(1,2)
+1.4567910310469068691864323832650819750
+? agm(1+o(7^5),8+o(7^5))
+1 + 4*7 + 6*7^2 + 5*7^3 + 2*7^4 + O(7^5)
+? algdep(2*cos(2*pi/13),6)
+x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1
+? algdep2(2*cos(2*pi/13),6,15)
+x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1
+? akell(acurve,1000000007)
+43800
+? nfpol=x^5-5*x^3+5*x+25
+x^5 - 5*x^3 + 5*x + 25
+? nf=initalg(nfpol)
+[x^5 - 5*x^3 + 5*x + 25, [1, 2], 595125, 45, [[1, -1.08911514572050482502495
+27946671612684, -2.4285174907194186068992069565359418365, 0.7194669112891317
+8943997506477288225737, -2.5558200350691694950646071159426779972; 1, -0.1383
+8372073406036365047976417441696637 - 0.4918163765776864349975328551474152510
+7*I, 1.9647119211288133163138753392090569931 + 0.809714924188978951282940822
+19556466857*I, -0.072312766896812300380582649294307897074 + 2.19808037538462
+76641195195160383234878*I, -0.98796319352507039803950539735452837193 + 1.570
+1452385894131769052374806001981109*I; 1, 1.682941293594312776162956161507997
+6006 + 2.0500351226010726172974286983598602164*I, -0.75045317576910401286427
+186094108607489 + 1.3101462685358123283560773619310445916*I, -0.787420688747
+75359433940488309213323154 + 2.1336633893126618034168454610457936018*I, 1.26
+58732110596551455718089553258673705 - 2.716479010374315056657802803578983483
+5*I], [1, -1.0891151457205048250249527946671612684, -2.428517490719418606899
+2069565359418365, 0.71946691128913178943997506477288225737, -2.5558200350691
+694950646071159426779972; 1, -0.63020009731174679864801261932183221743, 2.77
+44268453177922675968161614046216617, 2.1257676084878153637389368667440155907
+, 0.58218204506434277886573208324566973897; 1, 0.353432655843626071347053090
+97299828470, 1.1549969969398343650309345170134923246, -2.2703931422814399645
+001021653326313849, -2.5581084321144835749447428779547264828; 1, 3.732976416
+1953853934603848598678578170, 0.55969309276670831549180550098995851667, 1.34
+62427005649082090774405779536603703, -1.450605799314659911085993848253116112
+9; 1, -0.36709382900675984113447253685186261580, -2.060599444304916341220349
+2228721306665, -2.9210840780604153977562503441379268334, 3.98235222143397020
+22296117589048508540], [1, -1, -2, 1, -3; 1, -1, 3, 2, 1; 1, 0, 1, -2, -3; 1
+, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, -10, 20; 0, -5
+, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0, 200, 110, 17
+7; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [63, 3,
+ 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, -14, -9; -9, 1
+6, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65, 118, -116; 53
+, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -34]], [3, 5, 2
+3]], [-2.4285174907194186068992069565359418365, 1.96471192112881331631387533
+92090569931 + 0.80971492418897895128294082219556466857*I, -0.750453175769104
+01286427186094108607489 + 1.3101462685358123283560773619310445916*I], [1, 1/
+15*x^4 - 2/3*x^2 + 1/3*x + 4/3, x, 2/15*x^4 - 1/3*x^2 + 2/3*x - 1/3, -1/15*x
+^4 + 1/3*x^3 + 1/3*x^2 - 4/3*x - 2/3], [1, 0, 3, 1, 10; 0, 0, -2, 1, -5; 0, 
+1, 0, 3, -5; 0, 0, 1, 1, 10; 0, 0, 0, 3, 0], [1, 0, 0, 0, 0, 0, -1, -1, -2, 
+4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 1, 0, 0, 0, 1, 1,
+ -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3, -3; 0, 0, 1, 0, 
+0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, -2, -1, -1; 0, 0,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0, 2, 0, -1; 0, 0,
+ 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0, 1, 1, 0, 0, -1]]
+? ba=algtobasis(nf,mod(x^3+5,nfpol))
+[6, 1, 3, 1, 3]~
+? anell(acurve,100)
+[1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4, 3, 1
+0, 2, 0, -1, 4, -9, -2, 6, -12, -4, 8, 15, 0, 2, 12, -1, 0, 6, 0, -9, -6, 2,
+ -10, -12, -4, -9, 12, -6, 2, 0, -4, 1, 18, 10, 0, 0, -12, 8, 12, -8, 8, -6,
+ -8, 4, -30, 8, 0, -6, -4, 9, 0, -1, 2, 3, 0, 5, -12, 4, 8, 9, 18, -15, 6, 0
+, -4, -18, 0, 4, 24, 2, 4, 12, 18, 0, -24, 4, 12, -30, -2]
+? apell(acurve,10007)
+66
+? apell2(acurve,10007)
+66
+? apol=x^3+5*x+1
+x^3 + 5*x + 1
+? apprpadic(apol,1+o(7^8))
+[1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8)]~
+? apprpadic(x^3+5*x+1,mod(x*(1+o(7^8)),x^2+x-1))
+[mod((1 + 3*7 + 3*7^2 + 4*7^3 + 4*7^4 + 4*7^5 + 2*7^6 + 3*7^7 + O(7^8))*x + 
+(2*7 + 6*7^2 + 6*7^3 + 3*7^4 + 3*7^5 + 4*7^6 + 5*7^7 + O(7^8)), x^2 + x - 1)
+]~
+? 4*arg(3+3*i)
+3.1415926535897932384626433832795028842
+? 3*asin(sqrt(3)/2)
+3.1415926535897932384626433832795028842
+? asinh(0.5)
+0.48121182505960344749775891342436842314
+? assmat(x^5-12*x^3+0.0005)
+
+[0 0 0 0 -0.00050000000000000000000000000000000000000]
+
+[1 0 0 0                                            0]
+
+[0 1 0 0                                            0]
+
+[0 0 1 0                                           12]
+
+[0 0 0 1                                            0]
+
+? 3*atan(sqrt(3))
+3.1415926535897932384626433832795028842
+? atanh(0.5)
+0.54930614433405484569762261846126285232
+? basis(x^3+4*x+5)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? basis2(x^3+4*x+5)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? basistoalg(nf,ba)
+mod(x^3 + 5, x^5 - 5*x^3 + 5*x + 25)
+? bernreal(12)
+-0.25311355311355311355311355311355311355
+? bernvec(6)
+[1, 1/6, -1/30, 1/42, -1/30, 5/66, -691/2730]
+? bestappr(pi,10000)
+355/113
+? bezout(123456789,987654321)
+[-8, 1, 9]
+? bigomega(12345678987654321)
+8
+? mcurve=initell([0,0,0,-17,0])
+[0, 0, 0, -17, 0, 0, -34, 0, -289, 816, 0, 314432, 1728, Vecsmall([1]), [Vec
+small([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? mpoints=[[-1,4],[-4,2]]~
+[[-1, 4], [-4, 2]]~
+? mhbi=bilhell(mcurve,mpoints,[9,24])
+[-0.72448571035980184146215805860545027441, 1.307328627832055544492943428892
+1943056]~
+? bin(1.1,5)
+-0.0045457500000000000000000000000000000000
+? binary(65537)
+[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
+? bittest(10^100,100)
+1
+? boundcf(pi,5)
+[3, 7, 15, 1, 292]
+? boundfact(40!+1,100000)
+
+[                                         41 1]
+
+[                                         59 1]
+
+[                                        277 1]
+
+[1217669507565553887239873369513188900554127 1]
+
+? move(0,0,0);box(0,500,500)
+? setrand(1);buchimag(1-10^7,1,1)
+[2416, [1208, 2], [qfi(277, 55, 9028), qfi(1700, 1249, 1700)], 1]
+? setrand(1);bnf=buchinitfu(x^2-x-57,0.2,0.2)
+[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.14159
+26535897932384626433832795028842*I; 2.7124653051843439746808795106061300699]
+, [1.7903417566977293763292119206302198761, 1.289761953065273502503008607239
+5031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.5005798036324
+5587382620331339071677436; -1.7903417566977293763292119206302198761, -1.2897
+619530652735025030086072395031018, 0.E-38, -0.500579803632455873826203313390
+71677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1, 
+[0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1, 
+57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0], 
+229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.0663729752107779
+635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.0
+663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229, 
+114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.06637297
+52107779635959310246705326059, 8.0663729752107779635959310246705326058], [1,
+ x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2.
+7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 0
+]], [[1.7903417566977293763292119206302198761, -1.79034175669772937632921192
+06302198761]]], [0, 0]]
+? buchcertify(bnf)
+1
+? buchfu(bnf)
+[x + 7]
+? setrand(1);buchinitforcefu(x^2-x-100000)
+[mat(5), mat([3, 2, 1, 2, 0, 3, 4, 1, 3, 2, 0, 0, 2, 3, 3, 2, 2, 3]), [-129.
+82045011403975460991182396195022419 + 6.771228821958152128 E-113*I; 129.8204
+5011403975460991182396195022419 + 5.796659156428942826 E-113*I], [-41.811264
+589129943393339502258694361489, 9.2399004147902289816376260438840931575 + 3.
+1415926535897932384626433832795028842*I, -11.8746098810754067250973159974311
+61032, 129.82045011403975460991182396195022419 + 3.1415926535897932384626433
+832795028842*I, 78.769285110119582234934695659371769545 + 3.1415926535897932
+384626433832795028842*I, 843.83292574125840496442685575267645724 + 3.1415926
+535897932384626433832795028842*I, 99.883795405985217941669637700687023734 + 
+6.2831853071795864769252867665590057684*I, -594.2238464718864434047370989340
+7797533 + 2.761280718999426355 E-113*I, 552.41258188275650001139759667538361
+384 + 6.2831853071795864769252867665590057684*I, -943.8166642091125376019420
+3404129129272 + 5.197704882822449609 E-113*I, 512.54404927786333037886705989
+674625866 + 6.2831853071795864769252867665590057684*I, 47.668319071568233997
+332918482707687879 + 3.1415926535897932384626433832795028842*I, -1086.231919
+9838862708766275101783094814 + 5.847417993175255810 E-113*I, 232.80982374359
+817890011490485449930607 + 6.2831853071795864769252867665590057684*I, 504.30
+161235151040536601653665850767759 + 6.2831853071795864769252867665590057684*
+I, -541.38631597877289632049789027161711131 + 3.1415926535897932384626433832
+795028842*I, -1447.7317003209533662215189714163306131 + 3.141592653589793238
+4626433832795028842*I, 912.61944444475616477613125021268707696 + 6.283185307
+1795864769252867665590057684*I, -954.43070903388610816947075247138143845 + 4
+.223135217293240308 E-113*I; 41.811264589129943393339502258694361489 + 6.497
+131103528062012 E-114*I, -9.2399004147902289816376260438840931575 + 6.283185
+3071795864769252867665590057684*I, 11.874609881075406725097315997431161032 +
+ 3.1415926535897932384626433832795028842*I, -129.820450114039754609911823961
+95022419 + 3.1415926535897932384626433832795028842*I, -78.769285110119582234
+934695659371769545 + 6.2831853071795864769252867665590057684*I, -843.8329257
+4125840496442685575267645724 + 6.2831853071795864769252867665590057684*I, -9
+9.883795405985217941669637700687023734 + 3.141592653589793238462643383279502
+8842*I, 594.22384647188644340473709893407797533 + 4.263742286690290695 E-113
+*I, -552.41258188275650001139759667538361384 + 6.283185307179586476925286766
+5590057684*I, 943.81666420911253760194203404129129272 + 3.141592653589793238
+4626433832795028842*I, -512.54404927786333037886705989674625866 + 3.14159265
+35897932384626433832795028842*I, -47.668319071568233997332918482707687879 + 
+3.1415926535897932384626433832795028842*I, 1086.2319199838862708766275101783
+094814 + 7.877771463027775189 E-113*I, -232.80982374359817890011490485449930
+607 + 3.1415926535897932384626433832795028842*I, -504.3016123515104053660165
+3665850767759 + 3.1415926535897932384626433832795028842*I, 541.3863159787728
+9632049789027161711131 + 4.588598841866693796 E-113*I, 1447.7317003209533662
+215189714163306131 + 1.0882694598409503869 E-112*I, -912.6194444447561647761
+3125021268707696 + 6.2831853071795864769252867665590057684*I, 954.4307090338
+8610816947075247138143845 + 6.172274548351658911 E-113*I], [[2, [1, 1]~, 1, 
+1, [0, 100000; 1, 1]], [5, [4, 1]~, 1, 1, [0, 100000; 1, 1]], [13, [-6, 1]~,
+ 1, 1, [5, 100000; 1, 6]], [2, [2, 1]~, 1, 1, [1, 100000; 1, 2]], [5, [5, 1]
+~, 1, 1, [-1, 100000; 1, 0]], [7, [3, 1]~, 2, 1, [3, 100000; 1, 4]], [13, [5
+, 1]~, 1, 1, [-6, 100000; 1, -5]], [29, [-14, 1]~, 1, 1, [13, 100000; 1, 14]
+], [29, [13, 1]~, 1, 1, [-14, 100000; 1, -13]], [17, [14, 1]~, 1, 1, [2, 100
+000; 1, 3]], [17, [19, 1]~, 1, 1, [-3, 100000; 1, -2]], [23, [-7, 1]~, 1, 1,
+ [6, 100000; 1, 7]], [23, [6, 1]~, 1, 1, [-7, 100000; 1, -6]], [31, [23, 1]~
+, 1, 1, [7, 100000; 1, 8]], [31, [38, 1]~, 1, 1, [-8, 100000; 1, -7]], [41, 
+[-7, 1]~, 1, 1, [6, 100000; 1, 7]], [41, [6, 1]~, 1, 1, [-7, 100000; 1, -6]]
+, [43, [-16, 1]~, 1, 1, [15, 100000; 1, 16]], [43, [15, 1]~, 1, 1, [-16, 100
+000; 1, -15]]], 0, [x^2 - x - 100000, [2, 0], 400001, 1, [[1, -315.728161301
+29840161392089489603747004; 1, 316.72816130129840161392089489603747004], [1,
+ -315.72816130129840161392089489603747004; 1, 316.72816130129840161392089489
+603747004], [1, -316; 1, 317], [2, 1; 1, 200001], [400001, 200000; 0, 1], [2
+00001, -1; -1, 2], [400001, [200000, 100000; 1, 200001]], [7, 57143]], [-315
+.72816130129840161392089489603747004, 316.7281613012984016139208948960374700
+4], [1, x], [1, 0; 0, 1], [1, 0, 0, 100000; 0, 1, 1, 1]], [[5, [5], [[2, 1; 
+0, 1]]], 129.82045011403975460991182396195022419, 1, [2, -1], [3795548840190
+13781006303254896369154068336082609238336*x + 119836165644250789990462835950
+022871665178127611316131167]], [mat(1), [[0, 0]], [[-41.81126458912994339333
+9502258694361489, 41.811264589129943393339502258694361489 + 6.49713110352806
+2012 E-114*I]]], [0, 0]]
+? setrand(1);bnf=buchinitfu(x^2-x-57,0.2,0.2)
+[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.14159
+26535897932384626433832795028842*I; 2.7124653051843439746808795106061300699]
+, [1.7903417566977293763292119206302198761, 1.289761953065273502503008607239
+5031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.5005798036324
+5587382620331339071677436; -1.7903417566977293763292119206302198761, -1.2897
+619530652735025030086072395031018, 0.E-38, -0.500579803632455873826203313390
+71677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1, 
+[0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1, 
+57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0], 
+229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.0663729752107779
+635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.0
+663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229, 
+114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.06637297
+52107779635959310246705326059, 8.0663729752107779635959310246705326058], [1,
+ x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2.
+7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 0
+]], [[1.7903417566977293763292119206302198761, -1.79034175669772937632921192
+06302198761]]], [0, 0]]
+? setrand(1);buchreal(10^9-3,0,0.5,0.5)
+[4, [4], [qfr(211, 31405, -16263, 0.E-57)], 2800.625251907016076486370621737
+0745514]
+? setrand(1);buchgen(x^4-7,0.2,0.2)
+
+[                                                     x^4 - 7]
+
+[                                                      [2, 1]]
+
+[                                                 [-87808, 1]]
+
+[                                            [1, x, x^2, x^3]]
+
+[[2, [2], [[3, 2, 2, 2; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]]]]
+
+[                     14.229975145405511722395637833443108790]
+
+[                                                           1]
+
+[                                                     [2, -1]]
+
+[                                  [x^2 - x - 1, x^2 + x - 1]]
+
+? setrand(1);buchgenfu(x^2-x-100000)
+
+[                       x^2 - x - 100000]
+
+[                                 [2, 0]]
+
+[                            [400001, 1]]
+
+[                                 [1, x]]
+
+[              [5, [5], [[13, 7; 0, 1]]]]
+
+[129.82045011403975460991182396195022417]
+
+[                                      1]
+
+[                                [2, -1]]
+
+[                                    [;]]
+
+? setrand(1);buchgenforcefu(x^2-x-100000)
+
+[x^2 - x - 100000]
+
+[[2, 0]]
+
+[[400001, 1]]
+
+[[1, x]]
+
+[[5, [5], [[2, 1; 0, 1]]]]
+
+[129.82045011403975460991182396195022419]
+
+[1]
+
+[[2, -1]]
+
+[[379554884019013781006303254896369154068336082609238336*x + 119836165644250
+789990462835950022871665178127611316131167]]
+
+? setrand(1);buchgenfu(x^4+24*x^2+585*x+1791,0.1,0.1)
+
+[x^4 + 24*x^2 + 585*x + 1791]
+
+[[0, 2]]
+
+[[18981, 3087]]
+
+[[1, -10/1029*x^3 + 13/343*x^2 - 165/343*x - 1135/343, 17/1029*x^3 - 32/1029
+*x^2 + 109/343*x + 2444/343, -26/1029*x^3 + 170/1029*x^2 - 429/343*x - 3294/
+343]]
+
+[[4, [4], [[7, 2, 0, 1; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]]]]
+
+[3.7941269688216589341408274220859400303]
+
+[1]
+
+[[6, 10/1029*x^3 - 13/343*x^2 + 165/343*x + 1478/343]]
+
+[[1/343*x^3 - 46/1029*x^2 - 122/343*x - 174/343]]
+
+? buchnarrow(bnf)
+[3, [3], [[3, 2; 0, 1]]]
+? buchray(bnf,[[5,3;0,1],[1,0]])
+[12, [12], [[3, 2; 0, 1]]]
+? bnr=buchrayinitgen(bnf,[[5,3;0,1],[1,0]])
+[[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.1415
+926535897932384626433832795028842*I; 2.7124653051843439746808795106061300699
+], [1.7903417566977293763292119206302198761, 1.28976195306527350250300860723
+95031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.500579803632
+45587382620331339071677436; -1.7903417566977293763292119206302198761, -1.289
+7619530652735025030086072395031018, 0.E-38, -0.50057980363245587382620331339
+071677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1,
+ [0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1,
+ 57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0],
+ 229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.066372975210777
+9635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.
+0663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229,
+ 114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.0663729
+752107779635959310246705326059, 8.0663729752107779635959310246705326058], [1
+, x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2
+.7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 
+0]], [[1.7903417566977293763292119206302198761, -1.7903417566977293763292119
+206302198761]]], [0, [mat([[5, 1]~, 1])]]], [[[5, 3; 0, 1], [1, 0]], [8, [4,
+ 2], [2, [-4, 0]~]], mat([[5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], 1]), [[[[4], [
+2], [2], [Vecsmall([0])], 1]], [[2], [-4], [Vecsmall([1])]]], [1, 0; 0, 1]],
+ [1], mat([1, -3, -6]), [12, [12], [[3, 2; 0, 1]]], [[0, 1; 0, 0], [-1, -1; 
+1, -1], 1]]
+? bnr2=buchrayinitgen(bnf,[[25,13;0,1],[1,1]])
+[[mat(3), mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.1415
+926535897932384626433832795028842*I; 2.7124653051843439746808795106061300699
+], [1.7903417566977293763292119206302198761, 1.28976195306527350250300860723
+95031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.500579803632
+45587382620331339071677436; -1.7903417566977293763292119206302198761, -1.289
+7619530652735025030086072395031018, 0.E-38, -0.50057980363245587382620331339
+071677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1,
+ [0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1,
+ 57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0],
+ 229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.066372975210777
+9635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.
+0663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229,
+ 114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.0663729
+752107779635959310246705326059, 8.0663729752107779635959310246705326058], [1
+, x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2
+.7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [mat(1), [[0, 
+0]], [[1.7903417566977293763292119206302198761, -1.7903417566977293763292119
+206302198761]]], [0, [mat([[5, 1]~, 1])]]], [[[25, 13; 0, 1], [1, 1]], [80, 
+[20, 2, 2], [2, [-24, 0]~, [2, 2]~]], mat([[5, [-2, 1]~, 1, 1, [1, 57; 1, 2]
+], 2]), [[[[4], [2], [2], [Vecsmall([0, 0])], 1], [[5], [6], [6], [Vecsmall(
+[0, 0])], mat([1/5, -13/5])]], [[2, 2], [-24, [2, 2]~], [Vecsmall([0, 1]), V
+ecsmall([1, 1])]]], [1, -12, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]], [1], mat([1, -3
+, -6, -6]), [12, [12], [[3, 2; 0, 1]]], [[0, 2, 0; -1, 10, 0], [-2, 0; 0, -1
+0], 2]]
+? bytesize(%)
+9216
+? ceil(-2.5)
+-2
+? centerlift(mod(456,555))
+-99
+? cf(pi)
+[3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2, 1, 84, 2, 1
+, 1, 15, 3, 13, 1, 4, 2, 6, 6]
+? cf2([1,3,5,7,9],(exp(1)-1)/(exp(1)+1))
+[0, 6, 10, 42, 30]
+? changevar(x+y,[z,t])
+  ***   at top-level: changevar(x+y,[z,t])
+  ***                 ^--------------------
+  *** changevar: this function no longer exists.
+? char([1,2;3,4],z)
+z^2 - 5*z - 2
+? char(mod(x^2+x+1,x^3+5*x+1),z)
+z^3 + 7*z^2 + 16*z - 19
+? char1([1,2;3,4],z)
+z^2 - 5*z - 2
+? char2(mod(1,8191)*[1,2;3,4],z)
+z^2 + mod(8186, 8191)*z + mod(8189, 8191)
+? acurve=chell(acurve,[-1,1,2,3])
+[-4, -1, -7, -12, -12, 12, 4, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]),
+ [Vecsmall([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? chinese(mod(7,15),mod(13,21))
+mod(97, 105)
+? apoint=chptell(apoint,[-1,1,2,3])
+[1, 3]
+? isoncurve(acurve,apoint)
+1
+? classno(-12391)
+63
+? classno(1345)
+6
+? classno2(-12391)
+63
+? classno2(1345)
+6
+? coeff(sin(x),7)
+-1/5040
+? compimag(qfi(2,1,3),qfi(2,1,3))
+qfi(2, -1, 3)
+? compo(1+o(7^4),3)
+1
+? compositum(x^4-4*x+2,x^3-x-1)
+[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x
+^2 - 128*x - 5]
+? compositum2(x^4-4*x+2,x^3-x-1)
+[[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*
+x^2 - 128*x - 5, mod(-279140305176/29063006931199*x^11 + 129916611552/290630
+06931199*x^10 + 1272919322296/29063006931199*x^9 - 2813750209005/29063006931
+199*x^8 - 2859411937992/29063006931199*x^7 - 414533880536/29063006931199*x^6
+ - 35713977492936/29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 4
+9785595543672/29063006931199*x^3 + 9423768373204/29063006931199*x^2 - 427797
+76146743/29063006931199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8
+*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), m
+od(-279140305176/29063006931199*x^11 + 129916611552/29063006931199*x^10 + 12
+72919322296/29063006931199*x^9 - 2813750209005/29063006931199*x^8 - 28594119
+37992/29063006931199*x^7 - 414533880536/29063006931199*x^6 - 35713977492936/
+29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 49785595543672/2906
+3006931199*x^3 + 9423768373204/29063006931199*x^2 - 13716769215544/290630069
+31199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12
+*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), -1]]
+? comprealraw(qfr(5,3,-1,0.),qfr(7,1,-1,0.))
+qfr(35, 43, 13, 0.E-38)
+? concat([1,2],[3,4])
+[1, 2, 3, 4]
+? conductor(bnf,[[25,13;0,1],[1,1]])
+[[[5, 3; 0, 1], [1, 0]], [12, [12], [[3, 2; 0, 1]]], mat(12)]
+? conductorofchar(bnr,[2])
+[[5, 3; 0, 1], [0, 0]]
+? conj(1+i)
+1 - I
+? conjvec(mod(x^2+x+1,x^3-x-1))
+[4.0795956234914387860104177508366260326, 0.46020218825428060699479112458168
+698369 + 0.18258225455744299269398828369501930574*I, 0.460202188254280606994
+79112458168698369 - 0.18258225455744299269398828369501930574*I]~
+? content([123,456,789,234])
+3
+? convol(sin(x),x*cos(x))
+x + 1/12*x^3 + 1/2880*x^5 + 1/3628800*x^7 + 1/14631321600*x^9 + 1/1448500838
+40000*x^11 + 1/2982752926433280000*x^13 + 1/114000816848279961600000*x^15 + 
+O(x^17)
+? core(54713282649239)
+5471
+? core2(54713282649239)
+[5471, 100003]
+? coredisc(54713282649239)
+21884
+? coredisc2(54713282649239)
+[21884, 100003/2]
+? cos(1)
+0.54030230586813971740093660744297660373
+? cosh(1)
+1.5430806348152437784779056207570616826
+? move(0,200,150)
+? cursor(0)
+? cvtoi(1.7)
+1
+? cyclo(105)
+x^48 + x^47 + x^46 - x^43 - x^42 - 2*x^41 - x^40 - x^39 + x^36 + x^35 + x^34
+ + x^33 + x^32 + x^31 - x^28 - x^26 - x^24 - x^22 - x^20 + x^17 + x^16 + x^1
+5 + x^14 + x^13 + x^12 - x^9 - x^8 - 2*x^7 - x^6 - x^5 + x^2 + x + 1
+? degree(x^3/(x-1))
+2
+? denom(12345/54321)
+18107
+? deplin(mod(1,7)*[2,-1;1,3])
+[-3, 1]~
+? deriv((x+y)^5,y)
+5*x^4 + 20*y*x^3 + 30*y^2*x^2 + 20*y^3*x + 5*y^4
+? ((x+y)^5)'
+5*x^4 + 20*y*x^3 + 30*y^2*x^2 + 20*y^3*x + 5*y^4
+? det([1,2,3;1,5,6;9,8,7])
+-30
+? det2([1,2,3;1,5,6;9,8,7])
+-30
+? detint([1,2,3;4,5,6])
+3
+? diagonal([2,4,6])
+
+[2 0 0]
+
+[0 4 0]
+
+[0 0 6]
+
+? dilog(0.5)
+0.58224052646501250590265632015968010874
+? dz=vector(30,k,1);dd=vector(30,k,k==1);dm=dirdiv(dd,dz)
+[1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -
+1, 0, 0, 1, 0, 0, -1, -1]
+? deu=direuler(p=2,100,1/(1-apell(acurve,p)*x+if(acurve[12]%p,p,0)*x^2))
+[1, -2, -3, 2, -2, 6, -1, 0, 6, 4, -5, -6, -2, 2, 6, -4, 0, -12, 0, -4, 3, 1
+0, 2, 0, -1, 4, -9, -2, 6, -12, -4, 8, 15, 0, 2, 12, -1, 0, 6, 0, -9, -6, 2,
+ -10, -12, -4, -9, 12, -6, 2, 0, -4, 1, 18, 10, 0, 0, -12, 8, 12, -8, 8, -6,
+ -8, 4, -30, 8, 0, -6, -4, 9, 0, -1, 2, 3, 0, 5, -12, 4, 8, 9, 18, -15, 6, 0
+, -4, -18, 0, 4, 24, 2, 4, 12, 18, 0, -24, 4, 12, -30, -2]
+? anell(acurve,100)==deu
+1
+? dirmul(abs(dm),dz)
+[1, 2, 2, 2, 2, 4, 2, 2, 2, 4, 2, 4, 2, 4, 4, 2, 2, 4, 2, 4, 4, 4, 2, 4, 2, 
+4, 2, 4, 2, 8]
+? dirzetak(initalg(x^3-10*x+8),30)
+[1, 2, 0, 3, 1, 0, 0, 4, 0, 2, 1, 0, 0, 0, 0, 5, 1, 0, 0, 3, 0, 2, 0, 0, 2, 
+0, 1, 0, 1, 0]
+? disc(x^3+4*x+12)
+-4144
+? discf(x^3+4*x+12)
+-1036
+? discrayabs(bnr,mat(6))
+[12, 12, 18026977100265125]
+? discrayabs(bnr)
+[24, 12, 40621487921685401825918161408203125]
+? discrayabscond(bnr2)
+0
+? lu=ideallistunitgen(bnf,55);discrayabslist(bnf,lu)
+[[[6, 6, mat([229, 3])]], [], [[], []], [[]], [[12, 12, [5, 3; 229, 6]], [12
+, 12, [5, 3; 229, 6]]], [], [], [], [[], [], []], [], [[], []], [[], []], []
+, [], [[24, 24, [3, 6; 5, 9; 229, 12]], [], [], [24, 24, [3, 6; 5, 9; 229, 1
+2]]], [[]], [[], []], [], [[18, 18, [19, 6; 229, 9]], [18, 18, [19, 6; 229, 
+9]]], [[], []], [], [], [], [], [[], [24, 24, [5, 12; 229, 12]], []], [], [[
+], [], [], []], [], [], [], [], [], [[], [12, 12, [3, 3; 11, 3; 229, 6]], [1
+2, 12, [3, 3; 11, 3; 229, 6]], []], [], [], [[18, 18, [2, 12; 3, 12; 229, 9]
+], [], [18, 18, [2, 12; 3, 12; 229, 9]]], [[12, 12, [37, 3; 229, 6]], [12, 1
+2, [37, 3; 229, 6]]], [], [], [], [], [], [[], []], [[], []], [[], [], [], [
+], [], []], [], [], [[12, 12, [2, 12; 3, 3; 229, 6]], [12, 12, [2, 12; 3, 3;
+ 229, 6]]], [[18, 18, [7, 12; 229, 9]]], [], [[], [], [], []], [], [[], []],
+ [], [[], [24, 24, [5, 9; 11, 6; 229, 12]], [24, 24, [5, 9; 11, 6; 229, 12]]
+, []]]
+? discrayabslistlong(bnf,20)
+[[[[matrix(0,2,j,k,0), 6, 6, mat([229, 3])]], [], [[mat([12, 1]), 0, 0, 0], 
+[mat([13, 1]), 0, 0, 0]], [[mat([10, 1]), 0, 0, 0]], [[mat([20, 1]), 12, 12,
+ [5, 3; 229, 6]], [mat([21, 1]), 12, 12, [5, 3; 229, 6]]], [], [], [], [[mat
+([12, 2]), 0, 0, 0], [[12, 1; 13, 1], 0, 0, 0], [mat([13, 2]), 0, 0, 0]], []
+, [[mat([44, 1]), 0, 0, 0], [mat([45, 1]), 0, 0, 0]], [[[10, 1; 12, 1], 0, 0
+, 0], [[10, 1; 13, 1], 0, 0, 0]], [], [], [[[12, 1; 20, 1], 24, 24, [3, 6; 5
+, 9; 229, 12]], [[13, 1; 20, 1], 0, 0, 0], [[12, 1; 21, 1], 0, 0, 0], [[13, 
+1; 21, 1], 24, 24, [3, 6; 5, 9; 229, 12]]], [[mat([10, 2]), 0, 0, 0]], [[mat
+([68, 1]), 0, 0, 0], [mat([69, 1]), 0, 0, 0]], [], [[mat([76, 1]), 18, 18, [
+19, 6; 229, 9]], [mat([77, 1]), 18, 18, [19, 6; 229, 9]]], [[[10, 1; 20, 1],
+ 0, 0, 0], [[10, 1; 21, 1], 0, 0, 0]]]]
+? discrayrel(bnr,mat(6))
+[6, 2, [125, 13; 0, 1]]
+? discrayrel(bnr)
+[12, 1, [1953125, 1160888; 0, 1]]
+? discrayrelcond(bnr2)
+0
+? divisors(8!)
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 28, 30, 32, 
+35, 36, 40, 42, 45, 48, 56, 60, 63, 64, 70, 72, 80, 84, 90, 96, 105, 112, 12
+0, 126, 128, 140, 144, 160, 168, 180, 192, 210, 224, 240, 252, 280, 288, 315
+, 320, 336, 360, 384, 420, 448, 480, 504, 560, 576, 630, 640, 672, 720, 840,
+ 896, 960, 1008, 1120, 1152, 1260, 1344, 1440, 1680, 1920, 2016, 2240, 2520,
+ 2688, 2880, 3360, 4032, 4480, 5040, 5760, 6720, 8064, 10080, 13440, 20160, 
+40320]
+? divres(345,123)
+[2, 99]~
+? divres(x^7-1,x^5+1)
+[x^2, -x^2 - 1]~
+? divsum(8!,x,x)
+159120
+? postdraw([0,0,0])
+? eigen([1,2,3;4,5,6;7,8,9])
+
+[1 -1.2833494518006402717978106547571267252 0.283349451800640271797810654757
+12672521]
+
+[-2 -0.14167472590032013589890532737856336261 0.6416747259003201358989053273
+7856336260]
+
+[1 1 1]
+
+? eint1(2)
+0.048900510708061119567239835228049522315
+? erfc(2)
+0.0046777349810472658379307436327470713891
+? eta(q)
+1 - q - q^2 + q^5 + q^7 - q^12 - q^15 + O(q^17)
+? euler
+0.57721566490153286060651209008240243104
+? z=y;y=x;eval(z)
+x
+? exp(1)
+2.7182818284590452353602874713526624978
+? extract([1,2,3,4,5,6,7,8,9,10],1000)
+[4, 6, 7, 8, 9, 10]
+? 10!
+3628800
+? fact(10)
+3628800.0000000000000000000000000000000
+? factcantor(x^11+1,7)
+
+[mod(1, 7)*x + mod(1, 7) 1]
+
+[mod(1, 7)*x^10 + mod(6, 7)*x^9 + mod(1, 7)*x^8 + mod(6, 7)*x^7 + mod(1, 7)*
+x^6 + mod(6, 7)*x^5 + mod(1, 7)*x^4 + mod(6, 7)*x^3 + mod(1, 7)*x^2 + mod(6,
+ 7)*x + mod(1, 7) 1]
+
+? centerlift(lift(factfq(x^3+x^2+x-1,3,t^3+t^2+t-1)))
+
+[            x - t 1]
+
+[x + (t^2 + t - 1) 1]
+
+[   x + (-t^2 - 1) 1]
+
+? factmod(x^11+1,7)
+
+[mod(1, 7)*x + mod(1, 7) 1]
+
+[mod(1, 7)*x^10 + mod(6, 7)*x^9 + mod(1, 7)*x^8 + mod(6, 7)*x^7 + mod(1, 7)*
+x^6 + mod(6, 7)*x^5 + mod(1, 7)*x^4 + mod(6, 7)*x^3 + mod(1, 7)*x^2 + mod(6,
+ 7)*x + mod(1, 7) 1]
+
+? factor(17!+1)
+
+[    661 1]
+
+[ 537913 1]
+
+[1000357 1]
+
+? p=x^5+3021*x^4-786303*x^3-6826636057*x^2-546603588746*x+3853890514072057
+x^5 + 3021*x^4 - 786303*x^3 - 6826636057*x^2 - 546603588746*x + 385389051407
+2057
+? fa=[11699,6;2392997,2;4987333019653,2]
+
+[        11699 6]
+
+[      2392997 2]
+
+[4987333019653 2]
+
+? factoredbasis(p,fa)
+[1, x, x^2, 1/11699*x^3 + 1847/11699*x^2 - 132/11699*x - 2641/11699, 1/13962
+3738889203638909659*x^4 - 1552451622081122020/139623738889203638909659*x^3 +
+ 418509858130821123141/139623738889203638909659*x^2 - 6810913798507599407313
+4/139623738889203638909659*x - 13185339461968406/58346808996920447]
+? factoreddiscf(p,fa)
+136866601
+? factoredpolred(p,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? factoredpolred2(p,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? factornf(x^3+x^2-2*x-1,t^3+t^2-2*t-1)
+
+[         x + mod(-t, t^3 + t^2 - 2*t - 1) 1]
+
+[   x + mod(-t^2 + 2, t^3 + t^2 - 2*t - 1) 1]
+
+[x + mod(t^2 + t - 1, t^3 + t^2 - 2*t - 1) 1]
+
+? factorpadic(apol,7,8)
+
+[(1 + O(7^8))*x + (6 + 2*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + 6*7^6 + O(7^8)) 1]
+
+[(1 + O(7^8))*x^2 + (1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8
+))*x + (6 + 5*7 + 3*7^2 + 6*7^3 + 7^4 + 3*7^5 + 2*7^6 + 5*7^7 + O(7^8)) 1]
+
+? factorpadic2(apol,7,8)
+
+[(1 + O(7^8))*x + (6 + 2*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + 6*7^6 + O(7^8)) 1]
+
+[(1 + O(7^8))*x^2 + (1 + 6*7 + 4*7^2 + 4*7^3 + 3*7^4 + 4*7^5 + 6*7^7 + O(7^8
+))*x + (6 + 5*7 + 3*7^2 + 6*7^3 + 7^4 + 3*7^5 + 2*7^6 + 5*7^7 + O(7^8)) 1]
+
+? factpol(x^15-1,3,1)
+
+[                              x - 1 1]
+
+[                        x^2 + x + 1 1]
+
+[            x^4 + x^3 + x^2 + x + 1 1]
+
+[x^8 - x^7 + x^5 - x^4 + x^3 - x + 1 1]
+
+? factpol(x^15-1,0,1)
+
+[                              x - 1 1]
+
+[                        x^2 + x + 1 1]
+
+[            x^4 + x^3 + x^2 + x + 1 1]
+
+[x^8 - x^7 + x^5 - x^4 + x^3 - x + 1 1]
+
+? fibo(100)
+354224848179261915075
+? floor(-1/2)
+-1
+? floor(-2.5)
+-3
+? for(x=1,5,print(x!))
+1
+2
+6
+24
+120
+? fordiv(10,x,print(x))
+1
+2
+5
+10
+? forprime(p=1,30,print(p))
+2
+3
+5
+7
+11
+13
+17
+19
+23
+29
+? forstep(x=0,pi,pi/12,print(sin(x)))
+0.E-38
+0.25881904510252076234889883762404832835
+0.50000000000000000000000000000000000000
+0.70710678118654752440084436210484903928
+0.86602540378443864676372317075293618347
+0.96592582628906828674974319972889736763
+1.0000000000000000000000000000000000000
+0.96592582628906828674974319972889736764
+0.86602540378443864676372317075293618348
+0.70710678118654752440084436210484903931
+0.50000000000000000000000000000000000003
+0.25881904510252076234889883762404832839
+4.701977403289150032 E-38
+? forvec(x=[[1,3],[-2,2]],print1([x[1],x[2]]," "));print(" ");
+[1, -2] [1, -1] [1, 0] [1, 1] [1, 2] [2, -2] [2, -1] [2, 0] [2, 1] [2, 2] [3
+, -2] [3, -1] [3, 0] [3, 1] [3, 2]  
+? frac(-2.7)
+0.30000000000000000000000000000000000000
+? galois(x^6-3*x^2-1)
+[12, 1, 1, "A_4(6) = [2^2]3"]
+? nf3=initalg(x^6+108);galoisconj(nf3)
+[-x, x, -1/12*x^4 - 1/2*x, -1/12*x^4 + 1/2*x, 1/12*x^4 - 1/2*x, 1/12*x^4 + 1
+/2*x]~
+? aut=%[2];galoisapply(nf3,aut,mod(x^5,x^6+108))
+mod(x^5, x^6 + 108)
+? gamh(10)
+1133278.3889487855673345741655888924756
+? gamma(10.5)
+1133278.3889487855673345741655888924756
+? gauss(hilbert(10),[1,2,3,4,5,6,7,8,9,0]~)
+[9236800, -831303990, 18288515520, -170691240720, 832112321040, -23298940665
+00, 3883123564320, -3803844432960, 2020775945760, -449057772020]~
+? gaussmodulo([2,3;5,4],[7,11]~,[1,4]~)
+[-5, -1]~
+? gaussmodulo2([2,3;5,4],[7,11]~,[1,4]~)
+[[-5, -1]~, [4, 9; -5, 8]]
+? gcd(12345678,87654321)
+9
+? getrand()
+Vecsmall([-696235626332558091, -7363039021536514678, -3123062006620239999, -
+2510915082749224356, -5278885121447018503, 8033304491650294704, 333461878925
+5801153, -4832266575436120673, 5861272496338969128, -5636745352271241140, -8
+607028155296715188, -2043539049048791177, -2842859901633655235, 690428093846
+572717, 6906280973824914900, 6398713593090797853, -1497282003360819453, 3975
+034254136078416, 1627271192805803924, 5189434097992515925, 92808575969012367
+5, -7043904930238153560, -8983157976956081156, -5952879933245555180, 2152499
+775656320938, 4269213403952002558, -8158273861213025959, 9081732703698285391
+, -968017538072501892, -1829026030745713069, -7068326569863156221, -33123451
+58404984065, 5646154901651443941, 8401460694091262829, 5443723805913372967, 
+-6498478506213724335, -7440916561918862159, 6706000563295944300, 60772564257
+86072811, -6121231345839840470, -6231072835177963555, 581918341205122502, 39
+42269887095438680, 696327281251301999, -6893563203940457921, 200727759119899
+8199, 7105508585849937036, 5457053460371917318, -5526524089828180513, 703267
+9944660440486, 7855791284947948178, -8184369163059197809, 334420549169131260
+6, 2606551983777159860, 4711304445555935711, -1411378265676602388, 538367195
+0969683929, 1186259937007930248, -3095483643856126747, 7159506599077310177, 
+-8545857881376569117, 8750587220496604824, -6606875118057441902, -8141165263
+118337473, 58, 8891599926458724970])
+? globalred(acurve)
+[37, [1, -1, 2, 2], 1, mat([37, 1]), [[1, 5, 0, 1]]]
+? hclassno(2000003)
+357
+? hell(acurve,apoint)
+0.81778253183950144377417759611107234575
+? hell2(acurve,apoint)
+0.81778253183950144377417759611107234597
+? hermite(amat=1/hilbert(7))
+
+[420   0    0    0   210  168   175]
+
+[  0 840    0    0     0    0   504]
+
+[  0   0 2520    0     0    0  1260]
+
+[  0   0    0 2520     0    0   840]
+
+[  0   0    0    0 13860    0  6930]
+
+[  0   0    0    0     0 5544     0]
+
+[  0   0    0    0     0    0 12012]
+
+? hermite2(amat)
+[[420, 0, 0, 0, 210, 168, 175; 0, 840, 0, 0, 0, 0, 504; 0, 0, 2520, 0, 0, 0,
+ 1260; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 13860, 0, 6930; 0, 0, 0, 0, 0, 
+5544, 0; 0, 0, 0, 0, 0, 0, 12012], [420, 420, 840, 630, 2982, 1092, 4159; 21
+0, 280, 630, 504, 2415, 876, 3395; 140, 210, 504, 420, 2050, 749, 2901; 105,
+ 168, 420, 360, 1785, 658, 2542; 84, 140, 360, 315, 1582, 588, 2266; 70, 120
+, 315, 280, 1421, 532, 2046; 60, 105, 280, 252, 1290, 486, 1866]]
+? hermitemod(amat,detint(amat))
+
+[420   0    0    0   210  168   175]
+
+[  0 840    0    0     0    0   504]
+
+[  0   0 2520    0     0    0  1260]
+
+[  0   0    0 2520     0    0   840]
+
+[  0   0    0    0 13860    0  6930]
+
+[  0   0    0    0     0 5544     0]
+
+[  0   0    0    0     0    0 12012]
+
+? hermiteperm(amat)
+[[360360, 0, 0, 0, 0, 144144, 300300; 0, 27720, 0, 0, 0, 0, 22176; 0, 0, 277
+20, 0, 0, 0, 6930; 0, 0, 0, 2520, 0, 0, 840; 0, 0, 0, 0, 2520, 0, 1260; 0, 0
+, 0, 0, 0, 168, 0; 0, 0, 0, 0, 0, 0, 7], [51480, 4620, 5544, 630, 840, 20676
+, 48619; 45045, 3960, 4620, 504, 630, 18074, 42347; 40040, 3465, 3960, 420, 
+504, 16058, 37523; 36036, 3080, 3465, 360, 420, 14448, 33692; 32760, 2772, 3
+080, 315, 360, 13132, 30574; 30030, 2520, 2772, 280, 315, 12036, 27986; 2772
+0, 2310, 2520, 252, 280, 11109, 25803], Vecsmall([7, 6, 5, 4, 3, 2, 1])]
+? hess(hilbert(7))
+
+[1 90281/58800 -1919947/4344340 4858466341/1095033030 -77651417539/819678732
+6 3386888964/106615355 1/2]
+
+[1/3 43/48 38789/5585580 268214641/109503303 -581330123627/126464718744 4365
+450643/274153770 1/4]
+
+[0 217/2880 442223/7447440 53953931/292008808 -32242849453/168619624992 1475
+457901/1827691800 1/80]
+
+[0 0 1604444/264539275 24208141/149362505292 847880210129/47916076768560 -45
+44407141/103873817300 -29/40920]
+
+[0 0 0 9773092581/35395807550620 -24363634138919/107305824577186620 72118203
+606917/60481351061158500 55899/3088554700]
+
+[0 0 0 0 67201501179065/8543442888354179988 -9970556426629/74082861999267660
+0 -3229/13661312210]
+
+[0 0 0 0 0 -258198800769/9279048099409000 -13183/38381527800]
+
+? hilb(2/3,3/4,5)
+1
+? hilbert(5)
+
+[  1 1/2 1/3 1/4 1/5]
+
+[1/2 1/3 1/4 1/5 1/6]
+
+[1/3 1/4 1/5 1/6 1/7]
+
+[1/4 1/5 1/6 1/7 1/8]
+
+[1/5 1/6 1/7 1/8 1/9]
+
+? hilbp(mod(5,7),mod(6,7))
+1
+? hvector(10,x,1/x)
+[1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10]
+? hyperu(1,1,1)
+0.59634736232319407434107849936927937607
+? i^2
+-1
+? initalgred(nfpol)
+[x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*x + 2, [1, 2], 595125, 4, [[1, -1.089115145
+7205048250249527946671612684, -2.4285174907194186068992069565359418365, 0.71
+946691128913178943997506477288225735, -2.55582003506916949506460711594267799
+71; 1, -0.13838372073406036365047976417441696637 + 0.49181637657768643499753
+285514741525107*I, 1.9647119211288133163138753392090569931 - 0.8097149241889
+7895128294082219556466857*I, -0.072312766896812300380582649294307897123 - 2.
+1980803753846276641195195160383234878*I, -0.98796319352507039803950539735452
+837195 - 1.5701452385894131769052374806001981109*I; 1, 1.6829412935943127761
+629561615079976006 + 2.0500351226010726172974286983598602164*I, -0.750453175
+76910401286427186094108607490 + 1.3101462685358123283560773619310445915*I, -
+0.78742068874775359433940488309213323160 + 2.1336633893126618034168454610457
+936016*I, 1.2658732110596551455718089553258673704 - 2.7164790103743150566578
+028035789834836*I], [1, -1.0891151457205048250249527946671612684, -2.4285174
+907194186068992069565359418365, 0.71946691128913178943997506477288225735, -2
+.5558200350691694950646071159426779971; 1, 0.3534326558436260713470530909729
+9828470, 1.1549969969398343650309345170134923246, -2.27039314228143996450010
+21653326313849, -2.5581084321144835749447428779547264828; 1, -0.630200097311
+74679864801261932183221744, 2.7744268453177922675968161614046216617, 2.12576
+76084878153637389368667440155906, 0.58218204506434277886573208324566973893; 
+1, 3.7329764161953853934603848598678578170, 0.559693092766708315491805500989
+95851657, 1.3462427005649082090774405779536603700, -1.4506057993146599110859
+938482531161132; 1, -0.36709382900675984113447253685186261580, -2.0605994443
+049163412203492228721306664, -2.9210840780604153977562503441379268332, 3.982
+3522214339702022296117589048508541], [1, -1, -2, 1, -3; 1, 0, 1, -2, -3; 1, 
+-1, 3, 2, 1; 1, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, 
+-10, 20; 0, -5, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0
+, 200, 110, 177; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 
+0, 1], [63, 3, 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, 
+-14, -9; -9, 16, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65,
+ 118, -116; 53, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -
+34]], [3, 5, 23]], [-1.0891151457205048250249527946671612684, -0.13838372073
+406036365047976417441696637 + 0.49181637657768643499753285514741525107*I, 1.
+6829412935943127761629561615079976006 + 2.0500351226010726172974286983598602
+164*I], [1, x, -1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, -1/2*x^4 + x^3 - x^2 
+- 9/2*x - 1, -1/2*x^4 + x^3 - 2*x^2 - 7/2*x - 2], [1, 0, -1, -7, -14; 0, 1, 
+1, -2, -15; 0, 0, 0, 2, 4; 0, 0, 1, 1, -2; 0, 0, -1, -3, -4], [1, 0, 0, 0, 0
+, 0, -1, -1, -2, 4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 
+1, 0, 0, 0, 1, 1, -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3,
+ -3; 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, 
+-2, -1, -1; 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0
+, 2, 0, -1; 0, 0, 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0,
+ 1, 1, 0, 0, -1]]
+? initalgred2(nfpol)
+[[x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*x + 2, [1, 2], 595125, 4, [[1, -1.08911514
+57205048250249527946671612684, -2.4285174907194186068992069565359418365, 0.7
+1946691128913178943997506477288225735, -2.5558200350691694950646071159426779
+971; 1, -0.13838372073406036365047976417441696637 + 0.4918163765776864349975
+3285514741525107*I, 1.9647119211288133163138753392090569931 - 0.809714924188
+97895128294082219556466857*I, -0.072312766896812300380582649294307897123 - 2
+.1980803753846276641195195160383234878*I, -0.9879631935250703980395053973545
+2837195 - 1.5701452385894131769052374806001981109*I; 1, 1.682941293594312776
+1629561615079976006 + 2.0500351226010726172974286983598602164*I, -0.75045317
+576910401286427186094108607490 + 1.3101462685358123283560773619310445915*I, 
+-0.78742068874775359433940488309213323160 + 2.133663389312661803416845461045
+7936016*I, 1.2658732110596551455718089553258673704 - 2.716479010374315056657
+8028035789834836*I], [1, -1.0891151457205048250249527946671612684, -2.428517
+4907194186068992069565359418365, 0.71946691128913178943997506477288225735, -
+2.5558200350691694950646071159426779971; 1, 0.353432655843626071347053090972
+99828470, 1.1549969969398343650309345170134923246, -2.2703931422814399645001
+021653326313849, -2.5581084321144835749447428779547264828; 1, -0.63020009731
+174679864801261932183221744, 2.7744268453177922675968161614046216617, 2.1257
+676084878153637389368667440155906, 0.58218204506434277886573208324566973893;
+ 1, 3.7329764161953853934603848598678578170, 0.55969309276670831549180550098
+995851657, 1.3462427005649082090774405779536603700, -1.450605799314659911085
+9938482531161132; 1, -0.36709382900675984113447253685186261580, -2.060599444
+3049163412203492228721306664, -2.9210840780604153977562503441379268332, 3.98
+23522214339702022296117589048508541], [1, -1, -2, 1, -3; 1, 0, 1, -2, -3; 1,
+ -1, 3, 2, 1; 1, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5,
+ -10, 20; 0, -5, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 
+0, 200, 110, 177; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0,
+ 0, 1], [63, 3, 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10,
+ -14, -9; -9, 16, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65
+, 118, -116; 53, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, 
+-34]], [3, 5, 23]], [-1.0891151457205048250249527946671612684, -0.1383837207
+3406036365047976417441696637 + 0.49181637657768643499753285514741525107*I, 1
+.6829412935943127761629561615079976006 + 2.050035122601072617297428698359860
+2164*I], [1, x, -1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, -1/2*x^4 + x^3 - x^2
+ - 9/2*x - 1, -1/2*x^4 + x^3 - 2*x^2 - 7/2*x - 2], [1, 0, -1, -7, -14; 0, 1,
+ 1, -2, -15; 0, 0, 0, 2, 4; 0, 0, 1, 1, -2; 0, 0, -1, -3, -4], [1, 0, 0, 0, 
+0, 0, -1, -1, -2, 4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0,
+ 1, 0, 0, 0, 1, 1, -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3
+, -3; 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1,
+ -2, -1, -1; 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 
+0, 2, 0, -1; 0, 0, 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0
+, 1, 1, 0, 0, -1]], mod(-1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, x^5 - 2*x^4 
++ 3*x^3 + 8*x^2 + 3*x + 2)]
+? vp=primedec(nf,3)[1]
+[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0,
+ 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]]
+? idx=idealmul(nf,idmat(5),vp)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idy=ideallllred(nf,idx,[1,5,6])
+
+[5 0 0 0 2]
+
+[0 5 0 0 2]
+
+[0 0 5 0 1]
+
+[0 0 0 5 2]
+
+[0 0 0 0 1]
+
+? idealadd(nf,idx,idy)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealaddone(nf,idx,idy)
+[[0, -1, -3, -1, 2]~, [1, 1, 3, 1, -2]~]
+? idealaddmultone(nf,[idy,idx])
+[[-5, 0, 0, 0, 0]~, [6, 0, 0, 0, 0]~]
+? idealappr(nf,idy)
+[-1, 4, 2, -1, -3]~
+? idealapprfact(nf,idealfactor(nf,idy))
+[-1, 4, 2, -1, -3]~
+? idealcoprime(nf,idx,idx)
+[-1/3, 1/3, 1/3, 1/3, 0]~
+? idz=idealintersect(nf,idx,idy)
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? idealfactor(nf,idz)
+
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]] 1]
+
+[[5, [-1, 0, 0, 0, 2]~, 4, 1, [2, -3, 0, -12, 6; 2, 2, -5, -2, 6; 1, 1, 0, -
+1, -7; 2, 2, 5, 3, 1; 1, -4, 0, -1, 3]] 3]
+
+[[5, [2, 0, 0, 0, -2]~, 1, 1, [2, 1, 10, -4, 2; 0, 0, -5, 0, 0; 3, -1, 0, -1
+, -7; 0, 0, 5, 5, 5; 1, -2, 0, 3, 1]] 1]
+
+? ideallist(bnf,20)
+[[[1, 0; 0, 1]], [], [[3, 2; 0, 1], [3, 0; 0, 1]], [[2, 0; 0, 2]], [[5, 3; 0
+, 1], [5, 1; 0, 1]], [], [], [], [[9, 5; 0, 1], [3, 0; 0, 3], [9, 3; 0, 1]],
+ [], [[11, 9; 0, 1], [11, 1; 0, 1]], [[6, 4; 0, 2], [6, 0; 0, 2]], [], [], [
+[15, 8; 0, 1], [15, 3; 0, 1], [15, 11; 0, 1], [15, 6; 0, 1]], [[4, 0; 0, 4]]
+, [[17, 14; 0, 1], [17, 2; 0, 1]], [], [[19, 18; 0, 1], [19, 0; 0, 1]], [[10
+, 6; 0, 2], [10, 2; 0, 2]]]
+? idx2=idealmul(nf,idx,idx)
+
+[9 5 7 0 4]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idt=idealmulred(nf,idx,idx)
+
+[2 0 0 0 0]
+
+[0 2 0 0 0]
+
+[0 0 2 0 0]
+
+[0 0 0 2 1]
+
+[0 0 0 0 1]
+
+? idealdiv(nf,idy,idt)
+
+[5   0 5/2   0   1]
+
+[0 5/2   0   0   1]
+
+[0   0 5/2   0 1/2]
+
+[0   0   0 5/2   1]
+
+[0   0   0   0 1/2]
+
+? idealdivexact(nf,idx2,idx)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealhermite(nf,vp)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealhermite2(nf,vp[2],3)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealnorm(nf,idt)
+16
+? idp=idealpow(nf,idx,7)
+
+[2187 1436 1807 630 1822]
+
+[   0    1    0   0    0]
+
+[   0    0    1   0    0]
+
+[   0    0    0   1    0]
+
+[   0    0    0   0    1]
+
+? idealpowred(nf,idx,7)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealtwoelt(nf,idy)
+[5, [2, 2, 1, 2, 1]~]
+? idealtwoelt2(nf,idy,10)
+[-1, 4, 2, 4, 2]~
+? idealval(nf,idp,vp)
+7
+? idmat(5)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? if(3<2,print("bof"),print("ok"));
+ok
+? imag(2+3*i)
+3
+? image([1,3,5;2,4,6;3,5,7])
+
+[1 3]
+
+[2 4]
+
+[3 5]
+
+? image(pi*[1,3,5;2,4,6;3,5,7])
+
+[3.1415926535897932384626433832795028842 9.424777960769379715387930149838508
+6526]
+
+[6.2831853071795864769252867665590057684 12.56637061435917295385057353311801
+1537]
+
+[9.4247779607693797153879301498385086526 15.70796326794896619231321691639751
+4421]
+
+? incgam(2,1)
+0.73575888234288464319104754032292173491
+? incgam3(2,1)
+0.26424111765711535680895245967707826509
+? incgam4(4,1,6)
+5.8860710587430771455283803225833738791
+? indexrank([1,1,1;1,1,1;1,1,2])
+[Vecsmall([1, 3]), Vecsmall([1, 3])]
+? indsort([8,7,6,5])
+Vecsmall([4, 3, 2, 1])
+? initell([0,0,0,-1,0])
+[0, 0, 0, -1, 0, 0, -2, 0, -1, 48, 0, 64, 1728, Vecsmall([1]), [Vecsmall([12
+8, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? initrect(1,700,700)
+? nfz=initzeta(x^2-2);
+? integ(sin(x),x)
+1/2*x^2 - 1/24*x^4 + 1/720*x^6 - 1/40320*x^8 + 1/3628800*x^10 - 1/479001600*
+x^12 + 1/87178291200*x^14 - 1/20922789888000*x^16 + O(x^18)
+? integ((-x^2-2*a*x+8*a)/(x^4-14*x^3+(2*a+49)*x^2-14*a*x+a^2),x)
+(x + a)/(x^2 - 7*x + a)
+? intersect([1,2;3,4;5,6],[2,3;7,8;8,9])
+
+[-1]
+
+[-1]
+
+[-1]
+
+? \precision=19
+   realprecision = 19 significant digits
+? intgen(x=0,pi,sin(x))
+2.000000000000000018
+? sqr(2*intgen(x=0,4,exp(-x^2)))
+3.141592556720305686
+? 4*intinf(x=1,10^20,1/(1+x^2))
+3.141592653589793209
+? intnum(x=-0.5,0.5,1/sqrt(1-x^2))
+1.047197551196597746
+? 2*intopen(x=0,100,sin(x)/x)
+3.124450933778112629
+? \precision=38
+   realprecision = 38 significant digits
+? inverseimage([1,1;2,3;5,7],[2,2,6]~)
+[4, -2]~
+? isdiagonal([1,0,0;0,5,0;0,0,0])
+1
+? isfund(12345)
+1
+? isideal(bnf[7],[5,1;0,1])
+1
+? isincl(x^2+1,x^4+1)
+[-x^2, x^2]
+? isinclfast(initalg(x^2+1),initalg(x^4+1))
+[-x^2, x^2]
+? isirreducible(x^5+3*x^3+5*x^2+15)
+0
+? isisom(x^3+x^2-2*x-1,x^3+x^2-2*x-1)
+[x, -x^2 - x + 1, x^2 - 2]
+? isisomfast(initalg(x^3-2),initalg(x^3-6*x^2-6*x-30))
+[-1/25*x^2 + 13/25*x - 2/5]
+? isprime(12345678901234567)
+0
+? isprincipal(bnf,[5,1;0,1])
+[1]~
+? isprincipalgen(bnf,[5,1;0,1])
+[[1]~, [-2, -1/3]~]
+? isprincipalraygen(bnr,primedec(bnf,7)[1])
+[[9]~, [32879/6561, 13958/19683]~]
+? ispsp(73!+1)
+1
+? isqrt(10!^2+1)
+3628800
+? isset([-3,5,7,7])
+0
+? issqfree(123456789876543219)
+0
+? issquare(12345678987654321)
+1
+? isunit(bnf,mod(3405*x-27466,x^2-x-57))
+[-4, mod(1, 2)]~
+? jacobi(hilbert(6))
+[[1.0827994845655497685388772372251778091 E-7, 1.257075712262519492298239799
+6498755378 E-5, 0.00061574835418265769764919938428527140434, 0.0163215213198
+75822124345079564191505890, 0.24236087057520955213572841585070114077, 1.6188
+998589243390969705881471257800713]~, [-0.00124819408408217511693981630463878
+36342, 0.011144320930724710530678340374220998345, -0.06222658815019768177515
+2126611810492941, 0.24032536934252330399154228873240534569, -0.6145448282925
+8676899320019644273870646, 0.74871921887909485900280109200517845109; 0.03560
+6642944287635266122848131812051370, -0.1797327572407600375877689780374064077
+9, 0.49083920971092436297498316169060045043, -0.6976513752773701229620833504
+6678265583, 0.21108248167867048675227675845247769095, 0.44071750324351206127
+160083580231701802; -0.24067907958842295837736719558855680218, 0.60421220675
+295973004426567844103061740, -0.53547692162107486593474491750949545605, -0.2
+3138937333290388042251363554209048307, 0.36589360730302614149086554211117169
+623, 0.32069686982225190106359024326699463107; 0.625460386549227244577534410
+39459331707, -0.44357471627623954554460416705180104473, -0.41703769221897886
+840494514780771076351, 0.13286315850933553530333839628101576048, 0.394706776
+09501756783094636145991581709, 0.25431138634047419251788312792590944672; -0.
+68980719929383668419801738006926828754, -0.441536641012289662221436497529772
+04448, 0.047034018933115649705614518466541245344, 0.362714921464871475252994
+57604461742112, 0.38819043387388642863111448825992418974, 0.2115308400789652
+4664213667673977991960; 0.27160545336631286930015536176213646338, 0.45911481
+681642960284551392793050867151, 0.54068156310385293880022293448123781988, 0.
+50276286675751538489260566368647786274, 0.3706959077673628086177550108480739
+4603, 0.18144297664876947372217005457727093716]]
+? jbesselh(1,1)
+0.24029783912342701089584304474193368046
+? jell(i)
+1728.0000000000000000000000000000000000
+? kbessel(1+i,1)
+0.32545977186584141085464640324923711950 + 0.2894280370259921276345671592415
+2302743*I
+? kbessel2(1+i,1)
+0.32545977186584141085464640324923711950 + 0.2894280370259921276345671592415
+2302743*I
+? x
+x
+? y
+x
+? ker(matrix(4,4,x,y,x/y))
+
+[-1/2 -1/3 -1/4]
+
+[   1    0    0]
+
+[   0    1    0]
+
+[   0    0    1]
+
+? ker(matrix(4,4,x,y,sin(x+y)))
+
+[1.0000000000000000000000000000000000000 1.080604611736279434801873214885953
+2075]
+
+[-1.0806046117362794348018732148859532075 -0.1677063269057152260048635409984
+7562047]
+
+[1 0]
+
+[0 1]
+
+? keri(matrix(4,4,x,y,x+y))
+
+[ 1  2]
+
+[-2 -3]
+
+[ 1  0]
+
+[ 0  1]
+
+? kerint(matrix(4,4,x,y,x*y))
+
+[-1 -1 -1]
+
+[-1  0  1]
+
+[ 1 -1  1]
+
+[ 0  1 -1]
+
+? kerint1(matrix(4,4,x,y,x*y))
+
+[-1 -1 -1]
+
+[-1  0  1]
+
+[ 1 -1  1]
+
+[ 0  1 -1]
+
+? f(u)=u+1;
+? print(f(5));kill(f);
+6
+? f=12
+12
+? killrect(1)
+? kro(5,7)
+-1
+? kro(3,18)
+0
+? laplace(x*exp(x*y)/(exp(x)-1))
+1 - 1/2*x + 13/6*x^2 - 3*x^3 + 419/30*x^4 - 30*x^5 + 6259/42*x^6 - 420*x^7 +
+ 22133/10*x^8 - 7560*x^9 + 2775767/66*x^10 - 166320*x^11 + 2655339269/2730*x
+^12 - 4324320*x^13 + 264873251/10*x^14 - 129729600*x^15 + O(x^16)
+? lcm(15,-21)
+105
+? length(divisors(1000))
+16
+? legendre(10)
+46189/256*x^10 - 109395/256*x^8 + 45045/128*x^6 - 15015/128*x^4 + 3465/256*x
+^2 - 63/256
+? lex([1,3],[1,3,5])
+-1
+? lexsort([[1,5],[2,4],[1,5,1],[1,4,2]])
+[[1, 4, 2], [1, 5], [1, 5, 1], [2, 4]]
+? lift(chinese(mod(7,15),mod(4,21)))
+67
+? lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)])
+[3, 3, -9, 2, -6]~
+? lindep2([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)],14)
+[-3, -3, 9, -2, 6]~
+? move(0,0,900);line(0,900,0)
+? lines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+? m=1/hilbert(7)
+
+[    49    -1176      8820    -29400      48510     -38808     12012]
+
+[ -1176    37632   -317520   1128960   -1940400    1596672   -504504]
+
+[  8820  -317520   2857680 -10584000   18711000  -15717240   5045040]
+
+[-29400  1128960 -10584000  40320000  -72765000   62092800 -20180160]
+
+[ 48510 -1940400  18711000 -72765000  133402500 -115259760  37837800]
+
+[-38808  1596672 -15717240  62092800 -115259760  100590336 -33297264]
+
+[ 12012  -504504   5045040 -20180160   37837800  -33297264  11099088]
+
+? mp=concat(m,idmat(7))
+
+[49 -1176 8820 -29400 48510 -38808 12012 1 0 0 0 0 0 0]
+
+[-1176 37632 -317520 1128960 -1940400 1596672 -504504 0 1 0 0 0 0 0]
+
+[8820 -317520 2857680 -10584000 18711000 -15717240 5045040 0 0 1 0 0 0 0]
+
+[-29400 1128960 -10584000 40320000 -72765000 62092800 -20180160 0 0 0 1 0 0 
+0]
+
+[48510 -1940400 18711000 -72765000 133402500 -115259760 37837800 0 0 0 0 1 0
+ 0]
+
+[-38808 1596672 -15717240 62092800 -115259760 100590336 -33297264 0 0 0 0 0 
+1 0]
+
+[12012 -504504 5045040 -20180160 37837800 -33297264 11099088 0 0 0 0 0 0 1]
+
+? lll(m)
+
+[-420 -420 840 630 -1092 757 2982]
+
+[-210 -280 630 504  -876 700 2415]
+
+[-140 -210 504 420  -749 641 2050]
+
+[-105 -168 420 360  -658 589 1785]
+
+[ -84 -140 360 315  -588 544 1582]
+
+[ -70 -120 315 280  -532 505 1421]
+
+[ -60 -105 280 252  -486 471 1290]
+
+? lllgram(m)
+
+[1 1 27 -27 69   0 141]
+
+[0 1  5 -23 35 -24  50]
+
+[0 1  4 -22 19 -24  24]
+
+[0 1  4 -21 11 -19  14]
+
+[0 1  4 -20  7 -14   9]
+
+[0 1  4 -19  5 -10   6]
+
+[0 1  4 -18  4  -7   4]
+
+? lllgramint(m)
+
+[1 1 27 -27 69   0 141]
+
+[0 1  5 -23 35 -24  50]
+
+[0 1  4 -22 19 -24  24]
+
+[0 1  4 -21 11 -19  14]
+
+[0 1  4 -20  7 -14   9]
+
+[0 1  4 -19  5 -10   6]
+
+[0 1  4 -18  4  -7   4]
+
+? lllgramkerim(mp~*mp)
+[[-420, -420, 840, 630, 2982, -1092, 757; -210, -280, 630, 504, 2415, -876, 
+700; -140, -210, 504, 420, 2050, -749, 641; -105, -168, 420, 360, 1785, -658
+, 589; -84, -140, 360, 315, 1582, -588, 544; -70, -120, 315, 280, 1421, -532
+, 505; -60, -105, 280, 252, 1290, -486, 471; 420, 0, 0, 0, -210, 168, 35; 0,
+ 840, 0, 0, 0, 0, 336; 0, 0, -2520, 0, 0, 0, -1260; 0, 0, 0, -2520, 0, 0, -8
+40; 0, 0, 0, 0, -13860, 0, 6930; 0, 0, 0, 0, 0, 5544, 0; 0, 0, 0, 0, 0, 0, -
+12012], [0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0
+, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0,
+ 0; 1, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 
+1, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 1]]
+? lllint(m)
+
+[-420 -420 840 630 -1092 757 2982]
+
+[-210 -280 630 504  -876 700 2415]
+
+[-140 -210 504 420  -749 641 2050]
+
+[-105 -168 420 360  -658 589 1785]
+
+[ -84 -140 360 315  -588 544 1582]
+
+[ -70 -120 315 280  -532 505 1421]
+
+[ -60 -105 280 252  -486 471 1290]
+
+? lllintpartial(m)
+
+[-420 -420 -630 840 1092 2982 -83]
+
+[-210 -280 -504 630  876 2415  70]
+
+[-140 -210 -420 504  749 2050 137]
+
+[-105 -168 -360 420  658 1785 169]
+
+[ -84 -140 -315 360  588 1582 184]
+
+[ -70 -120 -280 315  532 1421 190]
+
+[ -60 -105 -252 280  486 1290 191]
+
+? lllkerim(mp)
+[[-420, -420, 840, 630, 2982, -1092, 757; -210, -280, 630, 504, 2415, -876, 
+700; -140, -210, 504, 420, 2050, -749, 641; -105, -168, 420, 360, 1785, -658
+, 589; -84, -140, 360, 315, 1582, -588, 544; -70, -120, 315, 280, 1421, -532
+, 505; -60, -105, 280, 252, 1290, -486, 471; 420, 0, 0, 0, -210, 168, 35; 0,
+ 840, 0, 0, 0, 0, 336; 0, 0, -2520, 0, 0, 0, -1260; 0, 0, 0, -2520, 0, 0, -8
+40; 0, 0, 0, 0, -13860, 0, 6930; 0, 0, 0, 0, 0, 5544, 0; 0, 0, 0, 0, 0, 0, -
+12012], [0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0
+, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0,
+ 0; 1, 0, 0, 0, 0, 0, 0; 0, 1, 0, 0, 0, 0, 0; 0, 0, 1, 0, 0, 0, 0; 0, 0, 0, 
+1, 0, 0, 0; 0, 0, 0, 0, 1, 0, 0; 0, 0, 0, 0, 0, 1, 0; 0, 0, 0, 0, 0, 0, 1]]
+? \precision=96
+   realprecision = 96 significant digits
+? ln(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996419
+? lngamma(10^50*i)
+-157079632679489661923132169163975144209858469968811.93673753887608474948977
+0941153418951907406848 + 114129254649702284200899572734218210380055074431438
+64.0947684761073895534327259165813042649761556*I
+? \precision=2000
+   realprecision = 2003 significant digits (2000 digits displayed)
+? log(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996418687542001481020570685733685520235758130557032670751635
+0759619307275708283714351903070386238916734711233501153644979552391204751726
+8157493206515552473413952588295045300709532636664265410423915781495204374043
+0385500801944170641671518644712839968171784546957026271631064546150257207402
+4816377733896385506952606683411372738737229289564935470257626520988596932019
+6505855476470330679365443254763274495125040606943814710468994650622016772042
+4524529612687946546193165174681392672504103802546259656869144192871608293803
+1727143677826548775664850856740776484514644399404614226031930967354025744460
+7030809608504748663852313818167675143866747664789088143714198549423151997354
+8803751658612753529166100071053558249879414729509293113897155998205654392871
+7000721808576102523688921324497138932037843935308877482597017155910708823683
+6275898425891853530243634214367061189236789192372314672321720534016492568727
+4778234453534764811494186423867767744060695626573796008670762571991847340226
+5146283790488306203306114463007371948900274364396500258093651944304119115060
+8094879306786515887090060520346842973619384128965255653968602219412292420757
+4321757489097706752687115817051137009158942665478595964890653058460258668382
+9400228330053820740056770530467870018416240441883323279838634900156312188956
+0650553151272199398332030751408426091479001265168243443893572472788205486271
+5527418772430024897945401961872339808608316648114909306675193393128904316413
+7068139777649817697486890388778999129650361927071088926410523092478391737350
+1229842420499568935992206602204654941510613918788574424557751020683703086661
+9480896412186807790208181588580001688115973056186676199187395200766719214592
+2367206025395954365416553112951759899400560003665135675690512459268257439464
+8316833262490180382424082423145230614096380570070255138770268178516306902551
+3703234053802145019015374029509942262995779647427138157363801729873940704242
+17997226696297993931270694
+? logagm(2)
+0.69314718055994530941723212145817656807550013436025525412068000949339362196
+9694715605863326996418687542001481020570685733685520235758130557032670751635
+0759619307275708283714351903070386238916734711233501153644979552391204751726
+8157493206515552473413952588295045300709532636664265410423915781495204374043
+0385500801944170641671518644712839968171784546957026271631064546150257207402
+4816377733896385506952606683411372738737229289564935470257626520988596932019
+6505855476470330679365443254763274495125040606943814710468994650622016772042
+4524529612687946546193165174681392672504103802546259656869144192871608293803
+1727143677826548775664850856740776484514644399404614226031930967354025744460
+7030809608504748663852313818167675143866747664789088143714198549423151997354
+8803751658612753529166100071053558249879414729509293113897155998205654392871
+7000721808576102523688921324497138932037843935308877482597017155910708823683
+6275898425891853530243634214367061189236789192372314672321720534016492568727
+4778234453534764811494186423867767744060695626573796008670762571991847340226
+5146283790488306203306114463007371948900274364396500258093651944304119115060
+8094879306786515887090060520346842973619384128965255653968602219412292420757
+4321757489097706752687115817051137009158942665478595964890653058460258668382
+9400228330053820740056770530467870018416240441883323279838634900156312188956
+0650553151272199398332030751408426091479001265168243443893572472788205486271
+5527418772430024897945401961872339808608316648114909306675193393128904316413
+7068139777649817697486890388778999129650361927071088926410523092478391737350
+1229842420499568935992206602204654941510613918788574424557751020683703086661
+9480896412186807790208181588580001688115973056186676199187395200766719214592
+2367206025395954365416553112951759899400560003665135675690512459268257439464
+8316833262490180382424082423145230614096380570070255138770268178516306902551
+3703234053802145019015374029509942262995779647427138157363801729873940704242
+17997226696297993931270694
+? \precision=19
+   realprecision = 19 significant digits
+? bcurve=initell([0,0,0,-3,0])
+[0, 0, 0, -3, 0, 0, -6, 0, -9, 144, 0, 1728, 1728, Vecsmall([1]), [Vecsmall(
+[64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? localred(bcurve,2)
+[6, 2, [1, 1, 1, 0], 1]
+? ccurve=initell([0,0,-1,-1,0])
+[0, 0, -1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vec
+small([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? l=lseriesell(ccurve,2,-37,1)
+0.3815754082607112109
+? lseriesell(ccurve,2,-37,1.2)-l
+-8.131516293641283255 E-20
+? sbnf=smallbuchinit(x^3-x^2-14*x-1)
+[x^3 - x^2 - 14*x - 1, 3, 10889, [1, x, x^2 - x - 9], [-3.233732695981516673
+, -0.07182350902743636345, 4.305556205008953036], 0, mat(2), mat([1, 1, 0, 1
+, 0, 1]), [9, 15, 16, 17, 39, 33, 10], [2, -1], [[0, 1, 0]~, [5, 3, 1]~], [[
+[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0
+]~, [-3, 0, 0]~], 0]]
+? makebigbnf(sbnf)
+[mat(2), mat([1, 1, 0, 1, 0, 1]), [1.173637103435061715 + 3.1415926535897932
+38*I, -4.562279014988837902 + 3.141592653589793238*I; -2.633543432738976050 
++ 3.141592653589793238*I, 1.420330600779487358 + 3.141592653589793238*I; 1.4
+59906329303914335, 3.141948414209350544], [1.246346989334819161, 0.540400637
+6129469728 + 3.141592653589793238*I, -0.6926391142471042845 + 3.141592653589
+793238*I, -1.990056445584799714 + 3.141592653589793238*I, -0.830562594660718
+8640, 0.004375616572659815402, 0; 0.6716827432867392936, -0.8333219883742404
+172 + 3.141592653589793238*I, -0.2461086674077943078, 0.5379005671092853267,
+ -1.552661549868775854 + 3.141592653589793238*I, -0.8738318043071131265 + 3.
+141592653589793238*I, 0; -1.918029732621558455 + 3.141592653589793238*I, 0.2
+929213507612934444, 0.9387477816548985923, 1.452155878475514387, 2.383224144
+529494718 + 3.141592653589793238*I, 0.8694561877344533111 + 3.14159265358979
+3238*I, 0], [[3, [-1, 1, 0]~, 1, 1, [1, 10, 45; 1, 7, 6; 1, 1, -3]], [5, [-1
+, 1, 0]~, 1, 1, [0, 10, 45; 1, 6, 6; 1, 1, -4]], [5, [2, 1, 0]~, 1, 1, [1, -
+17, 42; -2, 4, -9; 1, -2, -3]], [5, [3, 1, 0]~, 1, 1, [2, 19, 46; 2, 9, 11; 
+1, 2, -2]], [13, [19, 1, 0]~, 1, 1, [-2, -53, 38; -6, -3, -29; 1, -6, -6]], 
+[11, [1, 1, 0]~, 1, 1, [-3, -8, 43; -1, 1, -4; 1, -1, -7]], [3, [10, 1, 1]~,
+ 1, 2, [-1, 9, 1; 1, 0, 5; 0, 1, -1]]]~, 0, [x^3 - x^2 - 14*x - 1, [3, 0], 1
+0889, 1, [[1, -3.233732695981516673, 4.690759845041404812; 1, -0.07182350902
+743636345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144
+592], [1, -3.233732695981516673, 4.690759845041404812; 1, -0.071823509027436
+36345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144592]
+, [1, -3, 5; 1, 0, -9; 1, 4, 5], [3, 1, 1; 1, 29, 8; 1, 8, 129], [10889, 569
+8, 8994; 0, 1, 0; 0, 0, 1], [3677, -121, -21; -121, 386, -23; -21, -23, 86],
+ [10889, [1899, 46720, 5235; 5191, 7095, 25956; 1, 5191, 1895]], []], [-3.23
+3732695981516673, -0.07182350902743636345, 4.305556205008953036], [1, x, x^2
+ - x - 9], [1, 0, 9; 0, 1, 1; 0, 0, 1], [1, 0, 0, 0, 9, 1, 0, 1, 44; 0, 1, 0
+, 1, 1, 5, 0, 5, 1; 0, 0, 1, 0, 1, 0, 1, 0, -4]], [[2, [2], [[3, 2, 0; 0, 1,
+ 0; 0, 0, 1]]], 10.34800724602767998, 1, [2, -1], [x, x^2 + 2*x - 4]], [mat(
+1), [[0, 0, 0]], [[1.246346989334819161, 0.6716827432867392936, -1.918029732
+621558455 + 3.141592653589793238*I]]], [[[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]
+~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0]~, [-3, 0, 0]~], 0]]
+? concat(mat(vector(4,x,x)~),vector(4,x,10+x)~)
+
+[1 11]
+
+[2 12]
+
+[3 13]
+
+[4 14]
+
+? matextract(matrix(15,15,x,y,x+y),vector(5,x,3*x),vector(3,y,3*y))
+
+[ 6  9 12]
+
+[ 9 12 15]
+
+[12 15 18]
+
+[15 18 21]
+
+[18 21 24]
+
+? ma=mathell(mcurve,mpoints)
+
+[ 1.172183098700697011 0.4476973883408951692]
+
+[0.4476973883408951692  1.755026016172950714]
+
+? gauss(ma,mhbi)
+[-0.9999999999999999999, 0.9999999999999999999]~
+? (1.*hilbert(7))^(-1)
+
+[49.00000000002167189 -1176.000000000824159 8820.000000007689380 -29400.0000
+0002921115 48510.00000005262155 -38808.00000004484400 12012.00000001456215]
+
+[-1176.000000000829440 37632.00000003144616 -317520.0000002927650 1128960.00
+0001110352 -1940400.000001997563 1596672.000001700589 -504504.0000005516749]
+
+[8820.000000007770381 -317520.0000002939542 2857680.000002732420 -10584000.0
+0001035058 18711000.00001860328 -15717240.00001582519 5045040.000005130356]
+
+[-29400.00000002960234 1128960.000001118000 -10584000.00001037971 40320000.0
+0003928254 -72765000.00007055130 62092800.00005997937 -20180160.00001943491]
+
+[48510.00000005343676 -1940400.000002015506 18711000.00001869438 -72765000.0
+0007069760 133402500.0001268981 -115259760.0001078308 37837800.00003492601]
+
+[-38808.00000004561365 1596672.000001718594 -15717240.00001592797 62092800.0
+0006019943 -115259760.0001080025 100590336.0000917382 -33297264.00002970394]
+
+[12012.00000001482901 -504504.0000005582180 5045040.000005170209 -20180160.0
+0001953088 37837800.00003502588 -33297264.00002974149 11099088.00000962736]
+
+? matsize([1,2;3,4;5,6])
+[3, 2]
+? matrix(5,5,x,y,gcd(x,y))
+
+[1 1 1 1 1]
+
+[1 2 1 2 1]
+
+[1 1 3 1 1]
+
+[1 2 1 4 1]
+
+[1 1 1 1 5]
+
+? matrixqz([1,3;3,5;5,7],0)
+
+[1 1]
+
+[3 2]
+
+[5 3]
+
+? matrixqz2([1/3,1/4,1/6;1/2,1/4,-1/4;1/3,1,0])
+
+[19 12 2]
+
+[ 0  1 0]
+
+[ 0  0 1]
+
+? matrixqz3([1,3;3,5;5,7])
+
+[2 -1]
+
+[1  0]
+
+[0  1]
+
+? max(2,3)
+3
+? min(2,3)
+2
+? minim([2,1;1,2],4,6)
+[6, 2, [0, -1, 1; 1, 1, 0]]
+? mod(-12,7)
+mod(2, 7)
+? modp(-12,7)
+mod(2, 7)
+? mod(10873,49649)^-1
+  ***   at top-level: mod(10873,49649)^-1
+  ***                                 ^---
+  *** _^_: impossible inverse in Fp_inv: mod(131, 49649).
+? modreverse(mod(x^2+1,x^3-x-1))
+mod(x^2 - 3*x + 2, x^3 - 5*x^2 + 8*x - 5)
+? move(0,243,583);cursor(0)
+? mu(3*5*7*11*13)
+-1
+? newtonpoly(x^4+3*x^3+27*x^2+9*x+81,3)
+[2, 2/3, 2/3, 2/3]
+? nextprime(100000000000000000000000)
+100000000000000000000117
+? setrand(1);n=10^8;a=matrix(3,5,j,k,vvector(5,l,random()\n))
+
+[[15, 8, 17, 7, 12]~ [1, 4, 10, 6, 4]~ [1, 12, 9, 11, 11]~ [5, 0, 12, 21, 7]
+~ [18, 12, 18, 3, 8]~]
+
+[[16, 15, 20, 18, 14]~ [0, 3, 21, 15, 8]~ [10, 0, 18, 0, 7]~ [11, 18, 15, 7,
+ 20]~ [13, 2, 0, 4, 4]~]
+
+[[11, 11, 12, 7, 2]~ [9, 20, 11, 7, 16]~ [0, 12, 3, 12, 2]~ [0, 20, 20, 17, 
+5]~ [5, 11, 14, 7, 0]~]
+
+? aid=[idx,idy,idz,idmat(5),idx]
+[[3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1]
+, [5, 0, 0, 0, 2; 0, 5, 0, 0, 2; 0, 0, 5, 0, 1; 0, 0, 0, 5, 2; 0, 0, 0, 0, 1
+], [15, 10, 5, 0, 12; 0, 5, 0, 0, 2; 0, 0, 5, 0, 1; 0, 0, 0, 5, 2; 0, 0, 0, 
+0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0,
+ 0, 1], [3, 2, 1, 0, 1; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0
+, 0, 1]]
+? bb=algtobasis(nf,mod(x^3+x,nfpol))
+[1, 1, 4, 1, 3]~
+? da=nfdetint(nf,[a,aid])
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? nfdiv(nf,ba,bb)
+[584/373, 66/373, -32/373, -105/373, 120/373]~
+? nfdiveuc(nf,ba,bb)
+[2, 0, 0, 0, 0]~
+? nfdivres(nf,ba,bb)
+[[2, 0, 0, 0, 0]~, [4, -1, -5, -1, -3]~]
+? nfhermite(nf,[a,aid])
+[[1, 1, 4; 0, 1, 0; 0, 0, 1], [[15, 2, 10, 12, 4; 0, 1, 0, 0, 0; 0, 0, 1, 0,
+ 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? nfhermitemod(nf,[a,aid],da)
+[[1, 1, 4; 0, 1, 0; 0, 0, 1], [[15, 2, 10, 12, 4; 0, 1, 0, 0, 0; 0, 0, 1, 0,
+ 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? nfmod(nf,ba,bb)
+[4, -1, -5, -1, -3]~
+? nfmul(nf,ba,bb)
+[50, -15, -35, 60, 15]~
+? nfpow(nf,bb,5)
+[-291920, 136855, 230560, -178520, 74190]~
+? nfreduce(nf,ba,idx)
+[1, 0, 0, 0, 0]~
+? nfsmith(nf,[a[,1..3],[1,1,1],[idealinv(nf,idx),idealinv(nf,idy),1]])
+[[15706993357777254170417850, 1636878763571210697462070, 1307908830618593502
+9427775, 1815705333955314515809980, 7581330311082212790621785; 0, 5, 0, 0, 0
+; 0, 0, 5, 0, 0; 0, 0, 0, 5, 0; 0, 0, 0, 0, 5], [1, 0, 0, 0, 0; 0, 1, 0, 0, 
+0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0,
+ 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1]]
+? nfval(nf,ba,vp)
+0
+? norm(1+i)
+2
+? norm(mod(x+5,x^3+x+1))
+129
+? norml2(vector(10,x,x))
+385
+? nucomp(qfi(2,1,9),qfi(4,3,5),3)
+qfi(2, -1, 9)
+? form=qfi(2,1,9);nucomp(form,form,3)
+qfi(4, -3, 5)
+? numdiv(2^99*3^49)
+5000
+? numer((x+1)/(x-1))
+x + 1
+? nupow(form,111)
+qfi(2, -1, 9)
+? 1/(1+x)+o(x^20)
+1 - x + x^2 - x^3 + x^4 - x^5 + x^6 - x^7 + x^8 - x^9 + x^10 - x^11 + x^12 -
+ x^13 + x^14 - x^15 + x^16 - x^17 + x^18 - x^19 + O(x^20)
+? omega(100!)
+25
+? ordell(acurve,1)
+[8, 3]
+? order(mod(33,2^16+1))
+2048
+? tcurve=initell([1,0,1,-19,26]);
+? orderell(tcurve,[1,2])
+6
+? ordred(x^3-12*x+45*x-1)
+[x - 1, x^3 + 33*x - 1]
+? padicprec(padicno,127)
+5
+? pascal(8)
+
+[1 0  0  0  0  0  0 0 0]
+
+[1 1  0  0  0  0  0 0 0]
+
+[1 2  1  0  0  0  0 0 0]
+
+[1 3  3  1  0  0  0 0 0]
+
+[1 4  6  4  1  0  0 0 0]
+
+[1 5 10 10  5  1  0 0 0]
+
+[1 6 15 20 15  6  1 0 0]
+
+[1 7 21 35 35 21  7 1 0]
+
+[1 8 28 56 70 56 28 8 1]
+
+? perf([2,0,1;0,2,1;1,1,2])
+6
+? permutation(7,1035)
+[2, 4, 6, 1, 5, 7, 3]
+? permutation2num([4,7,1,6,3,5,2])
+2781
+? pf(-44,3)
+qfi(3, 2, 4)
+? phi(257^2)
+65792
+? pi
+3.141592653589793239
+? b=10;a=1<<b;plot(x=-5,5,round(sin(x)<<b)/a)
+
+        1 x""x_''''''''''''''''''''''''''''''''''_x""x'''''''''''''''''''|
+          |    x                                _     "_                 |
+          |     x                              _        _                |
+          |      x                            _                          |
+          |       _                                      "               |
+          |                                  "            x              |
+          |        x                        _                            |
+          |                                                "             |
+          |         "                      x                _            |
+          |          _                                                   |
+          |                               "                  x           |
+          ````````````x``````````````````_````````````````````````````````
+          |                                                   "          |
+          |            "                x                      _         |
+          |             _                                                |
+          |                            "                        x        |
+          |              x            _                                  |
+          |               _                                      "       |
+          |                          "                            x      |
+          |                "        "                              x     |
+          |                 "_     "                                x    |
+       -1 |...................x__x".................................."x__x
+          -5                                                             5
+? pnqn([2,6,10,14,18,22,26])
+
+[19318376 741721]
+
+[ 8927353 342762]
+
+? pnqn([1,1,1,1,1,1,1,1;1,1,1,1,1,1,1,1])
+
+[34 21]
+
+[21 13]
+
+? point(0,225,334)
+? points(0,vector(10,k,10*k),vector(10,k,5*k*k))
+? pointell(acurve,zell(acurve,apoint))
+[1.000000000000000000, 3.000000000000000000]
+? polint([0,2,3],[0,4,9],5)
+25
+? polred(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+[x - 1, x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1]
+? polred2(x^4-28*x^3-458*x^2+9156*x-25321)
+
+[                                                1           x - 1]
+
+[                   1/115*x^2 - 14/115*x - 212/115   x^2 - 2*x - 9]
+
+[                  -1/115*x^2 + 14/115*x + 442/115   x^2 - 2*x - 9]
+
+[                   1/115*x^2 - 14/115*x - 327/115        x^2 - 10]
+
+[1/4485*x^3 - 7/1495*x^2 - 1034/4485*x + 7924/4485 x^4 - 8*x^2 + 6]
+
+? polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1
+? polredabs2(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+[x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1, mod(2*x^4 - x^3 + 3*x^2 - 3*x - 1, x^5 -
+ x^4 + 2*x^3 - 4*x^2 + x - 1)]
+? polsym(x^17-1,17)
+[17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17]~
+? polvar(name^4-other)
+name
+? poly(sin(x),x)
+-1/1307674368000*x^15 + 1/6227020800*x^13 - 1/39916800*x^11 + 1/362880*x^9 -
+ 1/5040*x^7 + 1/120*x^5 - 1/6*x^3 + x
+? polylog(5,0.5)
+0.5084005792422687074
+? polylog(-4,t)
+(t^4 + 11*t^3 + 11*t^2 + t)/(-t^5 + 5*t^4 - 10*t^3 + 10*t^2 - 5*t + 1)
+? polylogd(5,0.5)
+1.033792745541689064
+? polylogdold(5,0.5)
+1.034459423449010486
+? polylogp(5,0.5)
+0.9495693489964922601
+? poly([1,2,3,4,5],x)
+x^4 + 2*x^3 + 3*x^2 + 4*x + 5
+? polyrev([1,2,3,4,5],x)
+5*x^4 + 4*x^3 + 3*x^2 + 2*x + 1
+? polzag(6,3)
+4608*x^6 - 13824*x^5 + 46144/3*x^4 - 23168/3*x^3 + 5032/3*x^2 - 120*x + 1
+? postdraw([0,20,20])
+? postploth(x=-5,5,sin(x))
+[-5.000000000000000000, 5.000000000000000000, -0.9999964107564721649, 0.9999
+964107564721649]
+? postploth2(t=0,2*pi,[sin(5*t),sin(7*t)])
+[-0.9999994509568810308, 0.9999994509568810308, -0.9999994509568810308, 0.99
+99994509568810308]
+? postplothraw(vector(100,k,k),vector(100,k,k*k/100))
+[1.000000000000000000, 100.0000000000000000, 0.01000000000000000021, 100.000
+0000000000000]
+? powell(acurve,apoint,10)
+[-28919032218753260057646013785951999/292736325329248127651484680640160000, 
+478051489392386968218136375373985436596569736643531551/158385319626308443937
+475969221994173751192384064000000]
+? cmcurve=initell([0,-3/4,0,-2,-1])
+[0, -3/4, 0, -2, -1, -3, -4, -4, -1, 105, 1323, -343, -3375, Vecsmall([1]), 
+[Vecsmall([64, -1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? powell(cmcurve,[x,y],quadgen(-7))
+[((-2 + 3*w)*x^2 + (6 - w))/((-2 - 5*w)*x + (-4 - 2*w)), ((34 - 11*w)*x^3 + 
+(40 - 28*w)*x^2 + (22 + 23*w)*x)/((-90 - w)*x^2 + (-136 + 44*w)*x + (-40 + 2
+8*w))]
+? powrealraw(qfr(5,3,-1,0.),3)
+qfr(125, 23, 1, 0.E-18)
+? pprint((x-12*y)/(y+13*x));
+-11/14
+? pprint([1,2;3,4])
+[1, 2; 3, 4]
+? pprint1(x+y);pprint(x+y);
+2*x2*x
+? \precision=96
+   realprecision = 96 significant digits
+? pi
+3.14159265358979323846264338327950288419716939937510582097494459230781640628
+620899862803482534212
+? prec(pi,20)
+3.14159265358979323846264338327950288420
+? precision(cmcurve)
+38
+? \precision=38
+   realprecision = 38 significant digits
+? prime(100)
+541
+? primedec(nf,2)
+[[2, [3, 0, 1, 0, 0]~, 1, 1, [0, 2, 0, -4, -2; 0, 0, 0, 2, 0; 0, 0, -2, -2, 
+-2; 1, 0, 3, 0, -1; 1, 0, 1, 0, -1]], [2, [12, -4, -2, 11, 3]~, 1, 4, [1, -1
+, 3, -1, 1; 0, 0, -2, -1, 1; 1, 0, 1, 0, -2; 0, 0, 1, 2, 2; 0, -1, 0, 1, 1]]
+]
+? primedec(nf,3)
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]], [3, [1, 1, 1, 0, 0]~, 2, 2, [0, -6,
+ 3, -9, 9; 2, -1, -7, -5, 7; 2, 1, 0, 1, -7; 1, 2, 3, 2, 4; 0, -5, -1, 0, 2]
+]]
+? primedec(nf,11)
+[[11, [11, 0, 0, 0, 0]~, 1, 5, 1]]
+? primes(100)
+[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 
+157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 2
+39, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 33
+1, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421
+, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541]
+? forprime(p=2,100,print(p," ",lift(primroot(p))))
+2 1
+3 2
+5 2
+7 3
+11 2
+13 2
+17 3
+19 2
+23 5
+29 2
+31 3
+37 2
+41 6
+43 3
+47 5
+53 2
+59 2
+61 2
+67 2
+71 7
+73 5
+79 3
+83 2
+89 3
+97 5
+? principalideal(nf,mod(x^3+5,nfpol))
+[6, 1, 3, 1, 3]~
+? print((x-12*y)/(y+13*x));
+-11/14
+? print([1,2;3,4])
+[1, 2; 3, 4]
+? print1(x+y);print1(" equals ");print(x+y);
+2*x equals 2*x
+? prod(1,k=1,10,1+1/k!)
+3335784368058308553334783/905932868585678438400000
+? prod(1.,k=1,10,1+1/k!)
+3.6821540356142043935732308433185262946
+? pi^2/6*prodeuler(p=2,10000,1-p^-2)
+1.0000098157493066238697591433298145222
+? prodinf(n=0,(1+2^-n)/(1+2^(-n+1)))
+0.33333333333333333333333333333333333329
+? prodinf1(n=0,-2^-n/(1+2^(-n+1)))
+0.33333333333333333333333333333333333329
+? psi(1)
+-0.57721566490153286060651209008240243104
+? quaddisc(-252)
+-7
+? quadgen(-11)
+w
+? quadpoly(-11)
+x^2 - x + 3
+? rank(matrix(5,5,x,y,x+y))
+2
+? rayclassno(bnf,[[5,3;0,1],[1,0]])
+12
+? rayclassnolist(bnf,lu)
+[[3], [], [3, 3], [3], [6, 6], [], [], [], [3, 3, 3], [], [3, 3], [3, 3], []
+, [], [12, 6, 6, 12], [3], [3, 3], [], [9, 9], [6, 6], [], [], [], [], [6, 1
+2, 6], [], [3, 3, 3, 3], [], [], [], [], [], [3, 6, 6, 3], [], [], [9, 3, 9]
+, [6, 6], [], [], [], [], [], [3, 3], [3, 3], [12, 12, 6, 6, 12, 12], [], []
+, [6, 6], [9], [], [3, 3, 3, 3], [], [3, 3], [], [6, 12, 12, 6]]
+? move(0,50,50);rbox(0,50,50)
+? print1("give a value for s? ");s=read();print(1/s)
+give a value for s? 37.
+0.027027027027027027027027027027027027027
+? real(5-7*i)
+5
+? recip(3*x^7-5*x^3+6*x-9)
+-9*x^7 + 6*x^6 - 5*x^4 + 3
+? redimag(qfi(3,10,12))
+qfi(3, -2, 4)
+? redreal(qfr(3,10,-20,1.5))
+qfr(3, 16, -7, 1.5000000000000000000000000000000000000)
+? redrealnod(qfr(3,10,-20,1.5),18)
+qfr(3, 16, -7, 1.5000000000000000000000000000000000000)
+? reduceddisc(x^3+4*x+12)
+[1036, 4, 1]
+? regula(17)
+2.0947125472611012942448228460655286535
+? kill(y);print(x+y);
+x + y
+? resultant(x^3-1,x^3+1)
+8
+? resultant2(x^3-1.,x^3+1.)
+8.0000000000000000000000000000000000000
+? bezoutres(x^2+1,x^2-1)
+[2, -2, 4]
+? reverse(tan(x))
+x - 1/3*x^3 + 1/5*x^5 - 1/7*x^7 + 1/9*x^9 - 1/11*x^11 + 1/13*x^13 - 1/15*x^1
+5 + O(x^17)
+? rhoreal(qfr(3,10,-20,1.5))
+qfr(-20, -10, 3, 2.1074451073987839947135880252731470616)
+? rhorealnod(qfr(3,10,-20,1.5),18)
+qfr(-20, -10, 3, 1.5000000000000000000000000000000000000)
+? rline(0,200,150)
+? cursor(0)
+? rmove(0,5,5);cursor(0)
+? rndtoi(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+x^17 - 1
+? qpol=y^3-y-1;setrand(1);bnf2=buchinit(qpol);nf2=bnf2[7];
+? un=mod(1,qpol);w=mod(y,qpol);p=un*(x^5-5*x+w)
+mod(1, y^3 - y - 1)*x^5 + mod(-5, y^3 - y - 1)*x + mod(y, y^3 - y - 1)
+? aa=rnfpseudobasis(nf2,p)
+[[1, 0, 0, -2, [3, 1, 0]~; 0, 1, 0, 2, [0, -1, 0]~; 0, 0, 1, 1, [-5, -2, 0]~
+; 0, 0, 0, 1, -2; 0, 0, 0, 0, 1], [1, 1, 1, [1, 0, 2/5; 0, 1, 3/5; 0, 0, 1/5
+], [1, 0, 22/25; 0, 1, 8/25; 0, 0, 1/25]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfbasis(bnf2,aa)
+
+[1 0 0  [-26/25, 11/25, -8/25]~         [0, 4, -7]~]
+
+[0 1 0   [53/25, -8/25, -1/25]~ [6/5, -41/5, 53/5]~]
+
+[0 0 1 [-14/25, -21/25, 13/25]~  [-16/5, 1/5, 7/5]~]
+
+[0 0 0     [7/25, -2/25, 6/25]~  [2/5, -2/5, 11/5]~]
+
+[0 0 0     [9/25, 1/25, -3/25]~   [2/5, -7/5, 6/5]~]
+
+? rnfdiscf(nf2,p)
+[[416134375, 202396875, 60056800; 0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfequation(nf2,p)
+x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1
+? rnfequation2(nf2,p)
+[x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1, mod(-x^5 + 5*x, x^15 - 1
+5*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1), 0]
+? rnfhermitebasis(bnf2,aa)
+
+[1 0 0 [-6/5, -4/5, 2/5]~   [3/25, -8/25, 24/25]~]
+
+[0 1 0  [6/5, 4/5, -2/5]~   [-9/25, -1/25, 3/25]~]
+
+[0 0 1  [3/5, 2/5, -1/5]~ [-8/25, 13/25, -39/25]~]
+
+[0 0 0  [3/5, 2/5, -1/5]~   [4/25, 6/25, -18/25]~]
+
+[0 0 0                  0   [-2/25, -3/25, 9/25]~]
+
+? rnfisfree(bnf2,aa)
+1
+? rnfsteinitz(nf2,aa)
+[[1, 0, 0, [-26/25, 11/25, -8/25]~, [29/125, -2/25, 8/125]~; 0, 1, 0, [53/25
+, -8/25, -1/25]~, [-53/125, 7/125, 1/125]~; 0, 0, 1, [-14/25, -21/25, 13/25]
+~, [9/125, 19/125, -13/125]~; 0, 0, 0, [7/25, -2/25, 6/25]~, [-9/125, 2/125,
+ -6/125]~; 0, 0, 0, [9/25, 1/25, -3/25]~, [-8/125, -1/125, 3/125]~], [1, 1, 
+1, 1, [125, 0, 22; 0, 125, 108; 0, 0, 1]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rootmod(x^16-1,41)
+[mod(1, 41), mod(3, 41), mod(9, 41), mod(14, 41), mod(27, 41), mod(32, 41), 
+mod(38, 41), mod(40, 41)]~
+? rootpadic(x^4+1,41,6)
+[3 + 22*41 + 27*41^2 + 15*41^3 + 27*41^4 + 33*41^5 + O(41^6), 14 + 20*41 + 2
+5*41^2 + 24*41^3 + 4*41^4 + 18*41^5 + O(41^6), 27 + 20*41 + 15*41^2 + 16*41^
+3 + 36*41^4 + 22*41^5 + O(41^6), 38 + 18*41 + 13*41^2 + 25*41^3 + 13*41^4 + 
+7*41^5 + O(41^6)]~
+? roots(x^5-5*x^2-5*x-5)
+[2.0509134529831982130058170163696514536 + 0.E-38*I, -0.67063790319207539268
+663382582902335603 - 0.84813118358634026680538906224199030918*I, -0.67063790
+319207539268663382582902335603 + 0.84813118358634026680538906224199030918*I,
+ -0.35481882329952371381627468235580237078 - 1.39980287391035466982975228340
+62081965*I, -0.35481882329952371381627468235580237078 + 1.399802873910354669
+8297522834062081965*I]~
+? rootsold(x^4-1000000000000000000000)
+  ***   at top-level: rootsold(x^4-1000000
+  ***                 ^--------------------
+  *** rootsold: this function no longer exists.
+? round(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+x^17 - 1
+? rounderror(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+-35
+? rpoint(0,20,20)
+? initrect(3,600,600);scale(3,-7,7,-2,2);cursor(3)
+? q*series(anell(acurve,100),q)
+q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + 6*q^6 - q^7 + 6*q^9 + 4*q^10 - 5*q^11 - 
+6*q^12 - 2*q^13 + 2*q^14 + 6*q^15 - 4*q^16 - 12*q^18 - 4*q^20 + 3*q^21 + 10*
+q^22 + 2*q^23 - q^25 + 4*q^26 - 9*q^27 - 2*q^28 + 6*q^29 - 12*q^30 - 4*q^31 
++ 8*q^32 + 15*q^33 + 2*q^35 + 12*q^36 - q^37 + 6*q^39 - 9*q^41 - 6*q^42 + 2*
+q^43 - 10*q^44 - 12*q^45 - 4*q^46 - 9*q^47 + 12*q^48 - 6*q^49 + 2*q^50 - 4*q
+^52 + q^53 + 18*q^54 + 10*q^55 - 12*q^58 + 8*q^59 + 12*q^60 - 8*q^61 + 8*q^6
+2 - 6*q^63 - 8*q^64 + 4*q^65 - 30*q^66 + 8*q^67 - 6*q^69 - 4*q^70 + 9*q^71 -
+ q^73 + 2*q^74 + 3*q^75 + 5*q^77 - 12*q^78 + 4*q^79 + 8*q^80 + 9*q^81 + 18*q
+^82 - 15*q^83 + 6*q^84 - 4*q^86 - 18*q^87 + 4*q^89 + 24*q^90 + 2*q^91 + 4*q^
+92 + 12*q^93 + 18*q^94 - 24*q^96 + 4*q^97 + 12*q^98 - 30*q^99 - 2*q^100 + O(
+q^101)
+? aset=set([5,-2,7,3,5,1])
+[-2, 1, 3, 5, 7]
+? bset=set([7,5,-5,7,2])
+[-5, 2, 5, 7]
+? setintersect(aset,bset)
+[5, 7]
+? setminus(aset,bset)
+[-2, 1, 3]
+? setprecision(28)
+38
+? setrand(10)
+? setsearch(aset,3)
+3
+? setsearch(bset,3)
+0
+? setserieslength(12)
+16
+? setunion(aset,bset)
+[-5, -2, 1, 2, 3, 5, 7]
+? shift(1,50)
+1125899906842624
+? shift([3,4,-11,-12],-2)
+[0, 1, -2, -3]
+? shiftmul([3,4,-11,-12],-2)
+[3/4, 1, -11/4, -3]
+? sigma(100)
+217
+? sigmak(2,100)
+13671
+? sigmak(-3,100)
+1149823/1000000
+? sign(-1)
+-1
+? sign(0)
+0
+? sign(0.)
+0
+? signat(hilbert(5)-0.11*idmat(5))
+[2, 3]
+? signunit(bnf)
+
+[-1]
+
+[ 1]
+
+? simplefactmod(x^11+1,7)
+
+[ 1 1]
+
+[10 1]
+
+? simplify(((x+i+1)^2-x^2-2*x*(i+1))^2)
+-4
+? sin(pi/6)
+0.5000000000000000000000000000
+? sinh(1)
+1.175201193643801456882381851
+? size([1.3*10^5,2*i*pi*exp(4*pi)])
+7
+? smallbasis(x^3+4*x+12)
+[1, x, 1/2*x^2]
+? smalldiscf(x^3+4*x+12)
+-1036
+? smallfact(100!+1)
+
+[101 1]
+
+[14303 1]
+
+[149239 1]
+
+[432885273849892962613071800918658949059679308685024481795740765527568493010
+727023757461397498800981521440877813288657839195622497225621499427628453 1]
+
+? smallinitell([0,0,0,-17,0])
+[0, 0, 0, -17, 0, 0, -34, 0, -289, 816, 0, 314432, 1728, Vecsmall([1]), [Vec
+small([128, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
+? smallpolred(x^4+576)
+[x - 1, x^2 - 3*x + 3, x^2 - 2*x + 2, x^2 - x + 1, x^2 + 1, x^4 - x^2 + 1]
+? smallpolred2(x^4+576)
+
+[                           1         x - 1]
+
+[    -1/192*x^3 - 1/8*x + 3/2 x^2 - 3*x + 3]
+
+[                1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[               -1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[    -1/192*x^3 - 1/8*x + 1/2   x^2 - x + 1]
+
+[     1/192*x^3 + 1/8*x + 1/2   x^2 - x + 1]
+
+[                    1/24*x^2       x^2 + 1]
+
+[1/192*x^3 + 1/48*x^2 - 1/8*x x^4 - x^2 + 1]
+
+? smith(matrix(5,5,j,k,random()))
+[5310167935312697687837910465800456021217135496, 2, 1, 1, 1]
+? smith(1/hilbert(6))
+[27720, 2520, 2520, 840, 210, 6]
+? smithpol(x*idmat(5)-matrix(5,5,j,k,1))
+[x^2 - 5*x, x, x, x, 1]
+? solve(x=1,4,sin(x))
+3.141592653589793238462643383
+? sort(vector(17,x,5*x%17))
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+? sqr(1+o(2))
+1 + O(2^3)
+? sqred(hilbert(5))
+
+[1  1/2   1/3    1/4     1/5]
+
+[0 1/12     1   9/10     4/5]
+
+[0    0 1/180    3/2    12/7]
+
+[0    0     0 1/2800       2]
+
+[0    0     0      0 1/44100]
+
+? sqrt(13+o(127^12))
+34 + 125*127 + 83*127^2 + 107*127^3 + 53*127^4 + 42*127^5 + 22*127^6 + 98*12
+7^7 + 127^8 + 23*127^9 + 122*127^10 + 79*127^11 + O(127^12)
+? srgcd(x^10-1,x^15-1)
+x^5 - 1
+? move(0,100,100);string(0,pi)
+? move(0,200,200);string(0,"(0,0)")
+? postdraw([0,10,10])
+? apol=0.3+legendre(10)
+46189/256*x^10 - 109395/256*x^8 + 45045/128*x^6 - 15015/128*x^4 + 3465/256*x
+^2 + 0.05390625000000000000000000000
+? sturm(apol)
+4
+? sturmpart(apol,0.91,1)
+1
+? subcyclo(31,5)
+x^5 + x^4 - 12*x^3 - 21*x^2 + x + 5
+? subell(initell([0,0,0,-17,0]),[-1,4],[-4,2])
+[9, -24]
+? subst(sin(x),x,y)
+y - 1/6*y^3 + 1/120*y^5 - 1/5040*y^7 + 1/362880*y^9 - 1/39916800*y^11 + O(y^
+13)
+? subst(sin(x),x,x+x^2)
+x + x^2 - 1/6*x^3 - 1/2*x^4 - 59/120*x^5 - 1/8*x^6 + 419/5040*x^7 + 59/720*x
+^8 + 13609/362880*x^9 + 19/13440*x^10 - 273241/39916800*x^11 - 14281/3628800
+*x^12 + O(x^13)
+? sum(0,k=1,10,2^-k)
+1023/1024
+? sum(0.,k=1,10,2^-k)
+0.9990234375000000000000000000
+? sylvestermatrix(a2*x^2+a1*x+a0,b1*x+b0)
+
+[a2 b1  0]
+
+[a1 b0 b1]
+
+[a0  0 b0]
+
+? \precision=38
+? 4*sumalt(n=0,(-1)^n/(2*n+1))
+3.1415926535897932384626433832795028842
+? 4*sumalt2(n=0,(-1)^n/(2*n+1))
+3.1415926535897932384626433832795028842
+? suminf(n=1,2.^-n)
+0.99999999999999999999999999999999999999
+? 6/pi^2*sumpos(n=1,n^-2)
+1.0000000000000000000000000000000000000
+? supplement([1,3;2,4;3,6])
+
+[1 3 0]
+
+[2 4 0]
+
+[3 6 1]
+
+? sqr(tan(pi/3))
+3.0000000000000000000000000000000000000
+? tanh(1)
+0.76159415595576488811945828260479359041
+? taniyama(bcurve)
+[x^-2 - x^2 + 3*x^6 - 2*x^10 + O(x^11), -x^-3 + 3*x - 3*x^5 + 8*x^9 + O(x^10
+)]
+? taylor(y/(x-y),y)
+(O(y^12)*x^11 + y*x^10 + y^2*x^9 + y^3*x^8 + y^4*x^7 + y^5*x^6 + y^6*x^5 + y
+^7*x^4 + y^8*x^3 + y^9*x^2 + y^10*x + y^11)/x^11
+? tchebi(10)
+512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1
+? teich(7+o(127^12))
+7 + 57*127 + 58*127^2 + 83*127^3 + 52*127^4 + 109*127^5 + 74*127^6 + 16*127^
+7 + 60*127^8 + 47*127^9 + 65*127^10 + 5*127^11 + O(127^12)
+? texprint((x+y)^3/(x-y)^2)
+\frac{x^3
+ + 3 y x^2
+ + 3 y^2 x
+ + y^3}{x^2
+ - 2 y x
+ + y^2}
+? theta(0.5,3)
+0.080806418251894691299871683210466298523
+? thetanullk(0.5,7)
+-804.63037320243369422783730584965684023
+? torsell(tcurve)
+[12, [6, 2], [[1, 2], [3, -2]]]
+? trace(1+i)
+2
+? trace(mod(x+5,x^3+x+1))
+15
+? trans(vector(2,x,x))
+[1, 2]~
+? %*%~
+
+[1 2]
+
+[2 4]
+
+? trunc(-2.7)
+-2
+? trunc(sin(x^2))
+1/120*x^10 - 1/6*x^6 + x^2
+? tschirnhaus(x^5-x-1)
+x^5 - 5*x^4 - 8*x^3 + 71*x^2 + 302*x - 604
+? type(mod(x,x^2+1))
+9
+? unit(17)
+3 + 2*w
+? n=33;until(n==1,print1(n," ");if(n%2,n=3*n+1,n=n/2));print(1)
+33 100 50 25 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
+? valuation(6^10000-1,5)
+5
+? vec(sin(x))
+[1, 0, -1/6, 0, 1/120, 0, -1/5040, 0, 1/362880, 0, -1/39916800, 0]
+? vecmax([-3,7,-2,11])
+11
+? vecmin([-3,7,-2,11])
+-3
+? vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],2)
+[[2, 5, 8], [3, 6, -6], [1, 8, 5], [4, 8, 6]]
+? vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],[2,1])
+[[2, 5, 8], [3, 6, -6], [1, 8, 5], [4, 8, 6]]
+? weipell(acurve)
+x^-2 + 1/5*x^2 - 1/28*x^4 + 1/75*x^6 - 3/1540*x^8 + O(x^10)
+? wf(i)
+1.1892071150027210667174999705604759153
+? wf2(i)
+1.0905077326652576592070106557607079790
+? m=5;while(m<20,print1(m," ");m=m+1);print()
+5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
+? zell(acurve,apoint)
+0.72491221490962306778878739838332384646
+? zeta(3)
+1.2020569031595942853997381615114499908
+? zeta(0.5+14.1347251*i)
+5.2043097453468479398562848599360610966 E-9 - 3.2690639869786982176409251733
+763732423 E-8*I
+? zetak(nfz,-3)
+0.091666666666666666666666666666666666667
+? zetak(nfz,1.5+3*i)
+0.88324345992059326405525724366416928892 - 0.2067536250233895222724230899142
+7938848*I
+? zidealstar(nf2,54)
+[132678, [1638, 9, 9], [[3, -26, 1]~, 31, [1, 0, -24]~]]
+? bid=zidealstarinit(nf2,54)
+[[[54, 0, 0; 0, 54, 0; 0, 0, 54], [0]], [132678, [1638, 9, 9]], [[2, [2, 0, 
+0]~, 1, 3, 1], 1; [3, [3, 0, 0]~, 1, 3, 1], 3], [[[[7], [[1, 1, 1]~], [[1, -
+27, -27]~], [Vecsmall([])], 1]], [[[26], [[3, 2, 0]~], [[3, 2, 0]~], [Vecsma
+ll([])], 1], [[3, 3, 3], [4, [1, 3, 0]~, [1, 0, 3]~], [-77, [1, -24, 0]~, [1
+, 0, -24]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/3, 0, 0; 0, 1/3,
+ 0; 0, 0, 1/3]], [[3, 3, 3], [10, [1, 9, 0]~, [1, 0, 9]~], [-233, [1, -18, 0
+]~, [1, 0, -18]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/9, 0, 0; 0
+, 1/9, 0; 0, 0, 1/9]]], [[], [], []]], [468, 469, 0, 0, -728, 0, 0, -546; 0,
+ 0, 1, 0, -6, -6, 0, 0; 0, 0, 0, 1, -3, 0, -6, 0]]
+? zideallog(nf2,w,bid)
+[922, 8, 0]~
+? znstar(3120)
+[768, [12, 4, 4, 2, 2], [mod(2641, 3120), mod(2341, 3120), mod(2497, 3120), 
+mod(391, 3120), mod(2081, 3120)]]
+? sin'(4)
+-0.65364362086361191463916818309775038142
+? m=3;if(m==1,2,m==3,4,5)
+4
+? a=[1,2,3];
+? deplin(a)
+[-2, 1, 0]~
+? deplin(mod(a,2))
+[0, 1, 0]~
+? deplin(mod(a,7))
+[-2, 1, 0]~
+? deplin(mod(a,2^64+13))
+[-2, 1, 0]~
+? p=2^64+13;t=char(mod(4*p*y+1,y^4+1),x);discf2(t)
+256
+? getstack()
+200
+? getheap()
+[721, 98442]
+? print("Total time spent: ",gettime);
+Total time spent: 132
diff --git a/src/test/64/ell b/src/test/64/ell
new file mode 100644
index 0000000..b2ddd63
--- /dev/null
+++ b/src/test/64/ell
@@ -0,0 +1,405 @@
+-1
+0
+152
+1031:[504, 2]
+2053:[1008, 2]
+4099:[4196]
+8209:[8291]
+16411:[8280, 2]
+32771:[32545]
+65537:[65115]
+131101:[130579]
+262147:[261873]
+524309:[525362]
+1048583:[1048721]
+2097169:[2099343]
+4194319:[4190448]
+8388617:[4196176, 2]
+16777259:[16776451]
+33554467:[33556544]
+67108879:[33553348, 2]
+134217757:[134207016]
+268435459:[268450764]
+536870923:[536886729]
+1073741827:[1073696739]
+2147483659:[2147445985]
+4294967311:[4294892145]
+8589934609:[8589800815]
+17179869209:[17179907771]
+34359738421:[34359891299]
+68719476767:[68719109932]
+137438953481:[137439150447]
+274877906951:[274876963417]
+549755813911:[549755723143]
+1099511627791:[1099510624080]
+2199023255579:[1099512197774, 2]
+4398046511119:[4398049864270]
+8796093022237:[8796090641581]
+17592186044423:[17592179180564]
+35184372088891:[35184377696395]
+70368744177679:[70368735914810]
+140737488355333:[140737466844674]
+281474976710677:[281474967245574]
+562949953421381:[562949910045019]
+1125899906842679:[562949923357406, 2]
+2251799813685269:[2251799812875502]
+4503599627370517:[4503599672855988]
+9007199254740997:[9007199395723803]
+18014398509482143:[18014398460825440]
+36028797018963971:[18014398463069820, 2]
+72057594037928017:[36028797145369816, 2]
+144115188075855881:[144115187446866113]
+288230376151711813:[288230375567209858]
+576460752303423619:[576460752721346915]
+1152921504606847009:[1152921506693313952]
+2305843009213693967:[2305843010596733829]
+4611686018427388039:[4611686021547019756]
+9223372036854775837:[9223372041689460430]
+15
+1
+1
+163663
+121661
+1
+1023
+494
+[4, [2, 2], [[-2147484185, 0], [0, 0]]]
+2
+2
+0
+0
+0
+1728
+j
+0
+Mod(0, 5)
+Mod(3, 5)
+Mod(1, 2)*j
+0
+Mod(1, 3)*j
+a
+a
+8*x^9 + 54*x^8 + 393*x^7 + 2373*x^6 + 6993*x^5 + 15267*x^4 + 19998*x^3 + 473
+4*x^2 - 25880*x - 30932
+16*x^33 + 20048*x^30 - 524864*x^27 - 20273280*x^24 - 35051520*x^21 - 1832755
+20*x^18 - 818626560*x^15 - 1017937920*x^12 - 390856704*x^9 + 74973184*x^6 + 
+102760448*x^3 + 4194304
+[3.1096482423243803285501491221965830079, 1.55482412116219016427507456109829
+15039 + 1.0643747452102737569438859937299427442*I]
+[6.2192964846487606571002982443931660158, 3.10964824232438032855014912219658
+30079 + 2.1287494904205475138877719874598854884*I]
+[5.5614800275334595421263952543627169988, 2.78074001376672977106319762718135
+84994 - 2.1374995527123861323185270948750077575*I]
+[6.2192964846487606571002982443931660158, 3.10964824232438032855014912219658
+30079 + 2.1287494904205475138877719874598854884*I]
+[-1.1547274830668428355945002349018042438, -0.828886258466578582202749882549
+09787812 + 0.52313677422798965199542236165917364573*I, -0.828886258466578582
+20274988254909787812 - 0.52313677422798965199542236165917364573*I]
+[10351, [1/2, -1, -2, 5/4], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]
+]
+[10351, [1, -1, 0, -1], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]]
+  ***   at top-level: E.omega
+  ***                   ^-----
+  *** _.omega: incorrect type in omega [not defined over C] (t_VEC).
+[9, [9], [[Mod(3, 7), Mod(5, 7)]]]
+[0, 0, 0, 413748, 716503104, 0, 827496, 2866012416, -171187407504, -19859904
+, -619058681856, -226311754192704000000, 97158364170048/2807086984375, Vecsm
+all([1]), [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~]]]
+[1/30, -13/150, -1/10, -79/500]
+1
+[36, [36], [[a^4 + a^3 + a^2, a^2]]]
+1
+[3, [3], [[0, 2]]]
+1
+[4, [4], [[Mod(3, 5), Mod(3, 5)]]]
+[1 + 2*3 + 3^2 + 2*3^3 + 3^4 + 3^5 + 2*3^6 + 3^7 + O(3^8), 1 + 3 + 3^3 + 3^4
+ + 2*3^5 + 2*3^6 + 2*3^7 + O(3^8), 3 + 2*3^3 + O(3^6), [1 + 2*3 + 3^2 + 2*3^
+3 + 3^4 + 3^6 + 2*3^7 + O(3^8), 1 + 3 + 2*3^2 + 3^3 + 2*3^4 + 2*3^5 + 3^6 + 
+3^7 + O(3^8)]]
+[3^-1 + 2 + 2*3^2 + 2*3^5 + 2*3^6 + O(3^8)]~
+[3^2 + 2*3^3 + 3^4 + 2*3^5 + 3^6 + 3^7 + 2*3^8 + 3^9 + O(3^10), 3 + 3^2 + 3^
+4 + 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + O(3^9), 3 + 2*3^3 + O(3^6), [3^-2 + 2*3^-1
+ + 1 + 2*3 + 3^2 + 3^4 + 2*3^5 + O(3^6), 3^-2 + 3^-1 + 2 + 3 + 2*3^2 + 2*3^3
+ + 3^4 + 3^5 + O(3^6)]]
+error("incorrect type in obj_check (t_VEC).")
+[2 + 2^6 + 2^10 + O(2^11), Mod(u, u^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^7 + 2
+^8 + 2^9 + O(2^11))), 2^3 + 2^4 + O(2^8), [2^-3 + 2^2 + 2^4 + 2^7 + 2^10 + O
+(2^11), 2^-3 + 2^2 + 2^5 + 2^6 + 2^10 + O(2^13)]]
+x^-2 + 31/15*x^2 + 2501/756*x^4 + 961/675*x^6 + 77531/41580*x^8 + O(x^9)
+[-1, -2*w]
+[I, 1]
+[[I, 1], [-3.1415926535897932384626433832795028842*I, 3.14159265358979323846
+26433832795028842]]
+[1, 1]
+x^-2 - 1/5*x^2 - 1/7*x^4 + 1/75*x^6 + 3/385*x^8 + 277/238875*x^10 - 2/5775*x
+^12 + O(x^14)
+x^-2 - 1/5*x^2 - 1/7*x^4 + 1/75*x^6 + O(x^7)
+8.9760336058655702799613054290253052728
+-8.9795585687185301843619815765809019104
+0.0070737179180847219897019688523688143761 - 4.54459013280902760664280136539
+71181201*I
+[1, 2]
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622160325336 E-
+37*x^4 + 29.790411247556326629130082765180921498*x^6 + 1.6958135836526736817
+ E-36*x^8 + 43.327339141107674122263886023453990048*x^10 + 3.562578745481278
+561 E-36*x^12 + O(x^14)
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622160325336 E-
+37*x^4 + 29.790411247556326629130082765180921498*x^6 + O(x^7)
+10.092015307351769584764433105625607145
+-10.092015307351769584764433105625607145
+3.2557987470773994635555990212606293690 E-38 - 2.704147351607435273075740713
+3303875089*I
+[1, 3]
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622160325336 E-
+37*x^4 + 29.790411247556326629130082765180921498*x^6 + 1.6958135836526736817
+ E-36*x^8 + 43.327339141107674122263886023453990048*x^10 + 3.562578745481278
+561 E-36*x^12 + O(x^14)
+x^-2 + 9.4536360064616926146530698267460656697*x^2 + 6.577345622160325336 E-
+37*x^4 + 29.790411247556326629130082765180921498*x^6 + O(x^7)
+10.092015307351769584764433105625607145
+-10.092015307351769584764433105625607145
+3.2557987470773994635555990212606293690 E-38 - 2.704147351607435273075740713
+3303875089*I
+[2, 1]
+x^-1 + 1/15*x^3 + 1/35*x^5 - 1/525*x^7 - 1/1155*x^9 - 277/2627625*x^11 + 2/7
+5075*x^13 + O(x^15)
+x^-1 + 1/15*x^3 + 1/35*x^5 - 1/525*x^7 + O(x^8)
+3.0025857981852417376980007365038576528
+-3.0023507303355942712341893343171384978*I
+1.4945837634650773441141478432745008118 - 1.49552579635851441107083905597206
+14467*I
+[2, 2]
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.3154691244320650671 E
+-37*x^5 - 4.2557730353651895184471546807401316426*x^7 - 1.884237315169637424
+ E-37*x^9 - 3.9388490128279703747512623657685445499*x^11 - 2.740445188831752
+739 E-37*x^13 + O(x^15)
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.3154691244320650671 E
+-37*x^5 - 4.2557730353651895184471546807401316426*x^7 + O(x^8)
+2.8609969154308155967482927187353233603
+-2.8813199850735158607638401394492232764*I
+1.7185329556464940714815988649194112975 - 1.71853295564649407148159886491941
+12976*I
+[2, 3]
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.3154691244320650671 E
+-37*x^5 - 4.2557730353651895184471546807401316426*x^7 - 1.884237315169637424
+ E-37*x^9 - 3.9388490128279703747512623657685445499*x^11 - 2.740445188831752
+739 E-37*x^13 + O(x^15)
+x^-1 - 3.1512120021538975382176899422486885566*x^3 - 1.3154691244320650671 E
+-37*x^5 - 4.2557730353651895184471546807401316426*x^7 + O(x^8)
+2.8609969154308155967482927187353233603
+-2.8813199850735158607638401394492232764*I
+1.7185329556464940714815988649194112975 - 1.71853295564649407148159886491941
+12976*I
+[3, 1]
+x + 1/60*x^5 + 1/210*x^7 - 1/10080*x^9 - 1/138600*x^11 - 167/259459200*x^13 
+- 19/1513512000*x^15 + O(x^17)
+x + 1/60*x^5 + 1/210*x^7 - 1/10080*x^9 + O(x^10)
+0.33340409272605175654322174351877926789
+0.33339973807064633526799756411632693200*I
+0.33307632454406929865753194192439552171 + 0.3330414840427217068846417452694
+8964209*I
+[3, 2]
+x - 0.78780300053847438455442248556217213914*x^5 - 2.1924485407201084452 E-3
+8*x^7 - 0.22165484559193695408578930628854852305*x^9 - 1.5701977626413645215
+ E-39*x^11 + 0.0093619303173614540570518182056750707483*x^13 + 1.29138042132
+44866734 E-40*x^15 + O(x^17)
+x - 0.78780300053847438455442248556217213914*x^5 - 2.1924485407201084452 E-3
+8*x^7 - 0.22165484559193695408578930628854852305*x^9 + O(x^10)
+0.33008009031657824359527653587336069208
+0.33008009031657824359527653587336069208*I
+0.34612072856429482856153662202577445445 + 0.3461207285642948285615366220257
+7445446*I
+[3, 3]
+x - 0.78780300053847438455442248556217213914*x^5 - 2.1924485407201084452 E-3
+8*x^7 - 0.22165484559193695408578930628854852305*x^9 - 1.5701977626413645215
+ E-39*x^11 + 0.0093619303173614540570518182056750707483*x^13 + 1.29138042132
+44866734 E-40*x^15 + O(x^17)
+x - 0.78780300053847438455442248556217213914*x^5 - 2.1924485407201084452 E-3
+8*x^7 - 0.22165484559193695408578930628854852305*x^9 + O(x^10)
+0.33008009031657824359527653587336069208
+0.33008009031657824359527653587336069208*I
+0.34612072856429482856153662202577445445 + 0.3461207285642948285615366220257
+7445446*I
+[4, 1]
+0
+0
+-1.0984000330177788282680372407424344829
+-1.0984130942966868400436474225688716324 + 1.5707963267948966192313216916397
+514421*I
+-0.75286232322707031868584884787482252469 + 0.785345859584418994173505759767
+90041015*I
+[4, 2]
+0
+0
+-1.1084199560389642415209208807828823872
+-1.1084199560389642415209208807828823872 + 1.5707963267948966192313216916397
+514421*I
+-0.71439404801872849905771074604935970853 + 0.785398163397448309615660845819
+87572105*I
+[4, 3]
+0
+0
+-1.1084199560389642415209208807828823872
+-1.1084199560389642415209208807828823872 + 1.5707963267948966192313216916397
+514421*I
+-0.71439404801872849905771074604935970853 + 0.785398163397448309615660845819
+87572105*I
+[2.5135797437238231405782694715779164652, 1.25678987186191157028913473578895
+82326 + 0.78959476569186174055147277865716603189*I]
+[3.1415926535897932384626433832795028842, 9.42477796076937971538793014983850
+86526*I]
+(x)->elleisnum(x,2)
+-2.9936282668967606065680548947245432597 - 7.1637767384648910133063235008836
+078048*I
+-37.699111843077518861551720599354034610
+-37.699111843077518861551720599354034610
+(x)->elleisnum(x,4,1)
+-3.9999999999999999999999999999999999999 - 5.485640303837341704 E-38*I
+189.07272012923385229306139653492131339
+189.07272012923385229306139653492131339
+(x)->elleisnum(x,6,1)
+-4.0000000000000000000000000000000000000 - 1.2538606408771066752 E-37*I
+1.8416567742048910940 E-35
+1.8416567742048910940 E-35
+(x)->elleisnum(x,10)
+-41471.999999999999999999999999999999998 - 7.703719777548943412 E-34*I
+-2.8181184198060280543 E-30
+-2.8181184198060280543 E-30
+-1
+[0]
+347813742467679407541/38941611811810745401
+[1, [], []]
+[2, [2], [[15, -8]]]
+[3, [3], [[5, 9]]]
+[4, [4], [[5, -2]]]
+[5, [5], [[5, 5]]]
+[6, [6], [[9, 23]]]
+[7, [7], [[-1, 2]]]
+[8, [8], [[2, 6]]]
+[9, [9], [[-3, 7]]]
+[10, [10], [[0, 9]]]
+[12, [12], [[-9, 49]]]
+[4, [2, 2], [[-29/4, 25/8], [-7, 3]]]
+[8, [4, 2], [[-2, 3], [-1, 0]]]
+[12, [6, 2], [[1, 2], [3, -2]]]
+[16, [8, 2], [[4, 58], [-36, 18]]]
+[16, [8, 2], [[117433600, 6734213027200], [352179456, -176089728]]]
+[4, [2, 2], [[-1377493124511464657, 0], [-691668349248679055, 0]]]
+[0.49999999999999999999999999999999999978 - 2.0571151139390031389 E-38*I, 1.
+9216402159513147090074725264936203858 + 0.2601943880282882461780139076976017
+6484*I]
+3 + 11^2 + 2*11^3 + 3*11^4 + O(11^5)
+Mod((2 + 3 + O(3^4))*u + (2*3 + 3^2 + O(3^4)), u^2 + (1 + 3 + 2*3^4 + 3^8 + 
+2*3^9 + O(3^10)))
+Mod((1 + 3 + 3^3 + 3^4 + 2*3^6 + 2*3^8 + 2*3^9 + O(3^10))*u + (1 + 3 + 3^2 +
+ 3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 3^9 + O(3^10)), u^2 + (3 + 3^3 + 2*3^4 + 3^5 
++ 2*3^6 + 3^7 + 2*3^8 + 3^9 + 2*3^10 + 3^11 + 2*3^12 + O(3^13)))
+Mod((2^3 + 2^7 + O(2^8))*u + (1 + 2 + 2^2 + 2^3 + 2^4 + O(2^6)), u^2 + (1 + 
+2^2 + 2^4 + 2^5 + 2^7 + 2^8 + 2^9 + O(2^13)))
+[Mod(0, 11), Mod(0, 11), Mod(0, 11), Mod(1, 11), Mod(1, 11), Mod(0, 11), Mod
+(2, 11), Mod(4, 11), Mod(10, 11), Mod(7, 11), Mod(5, 11), Mod(10, 11), Mod(9
+, 11), Vecsmall([3]), [11, [9, 5, [6, 0, 0, 0]]], [0, 0, 0, 0]]
+1
+[0.86602540378443864676372317075293618347 - 1/2*I, -0.8660254037844386467637
+2317075293618348 - 1/2*I]
+[-2, 3]
+[0, 1]
+[1, 0, 0, 0]
+0.035247504442186170440172838583518049039
+[0, 0, 0, 1, 1, 0, 2, 4, -1, -48, -864, -496, 6912/31, Vecsmall([1]), [Vecsm
+all([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~]]]
+[0, 0, 0, 1/16, 1/64, 0, 1/8, 1/16, -1/256, -3, -27/2, -31/256, 6912/31, Vec
+small([1]), [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~, [1/2, 0, 
+0, 0], [0, 0, 0, 1, 1, 0, 2, 4, -1, -48, -864, -496, 6912/31, Vecsmall([1]),
+ [Vecsmall([128, -1])], [0, 0, 0, 0, 0, 0, 0, [[2, 3]~]]]]]]
+
+0
+20 0 0 -16 0 -4 14 8 0 0 0 26 0 2 0 -28 
+1728
+0 0 -22 0 -14 0 -22 0 0 26 0 18 0 -14 2 0 
+-3375
+16 0 -10 0 -22 24 0 -20 0 0 4 0 8 -18 -26 0 
+8000
+0 -18 6 22 0 0 0 2 0 0 18 0 0 22 0 0 
+54000
+20 0 0 16 0 4 -14 -8 0 0 0 26 0 2 0 -28 
+-32768
+0 0 3 0 0 0 23 16 0 0 21 25 -15 0 0 -20 
+287496
+0 0 22 0 -14 0 22 0 0 -26 0 -18 0 14 2 0 
+-884736
+0 7 23 -9 11 0 18 -24 0 0 0 0 17 0 -22 -25 
+-12288000
+20 0 0 -23 0 19 14 25 0 0 0 7 0 23 0 -11 
+16581375
+16 0 10 0 22 24 0 -20 0 0 -4 0 8 -18 -26 0 
+-884736000
+11 0 0 -13 0 0 0 0 -25 -2 0 -6 0 -27 -10 0 
+-147197952000
+-21 -16 0 0 -23 1 5 7 20 -25 0 11 0 13 0 -27 
+-262537412640768000
+0 19 0 0 0 -21 0 0 4 -23 8 0 0 0 -25 -12 
+4294985035
+[0, 1, [5, 0, 0, 0], 1]
+1
+0
+[6.2500000000000000000000000000000000000, -140.62500000000000000000000000000
+000000]
+[37247908142/10128208321, 7601802384416381/1019292757217119]
+[0, 0, 0, x^2, x, 0, 2*x^2, 4*x, -x^4, -48*x^2, -864*x, -64*x^6 - 432*x^2, -
+6912*x^4/(-4*x^4 - 27), Vecsmall([0]), [Vecsmall([128, 0])], [0, 0, 0, 0]]
+  ***   at top-level: ellminimalmodel(E)
+  ***                 ^------------------
+  *** ellminimalmodel: incorrect type in checkell over Q (t_VEC).
+  ***   at top-level: ellweilpairing(E,[0]
+  ***                 ^--------------------
+  *** ellweilpairing: incorrect type in checkell over Fq (t_VEC).
+  ***   at top-level: ellinit([1])
+  ***                 ^------------
+  *** ellinit: incorrect type in ellxxx [not an elliptic curve (ell5)] (t_VEC).
+  ***   at top-level: ellinit([1,1],quadge
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_QUAD).
+  ***   at top-level: ellinit([Mod(1,2),1]
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+  ***   at top-level: ellinit([O(2),1],ffg
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+  ***   at top-level: ellinit([O(2),1],1.)
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+[0, 0, 0, 1, 2, 0, 2, 8, -1, -48, -1728, -1792, 432/7, Vecsmall([0]), [Vecsm
+all([128, -1])], [0, 0, 0, 0]]
+[0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 1, 3, 0, Vecsmall([4]), [0, [Vecsmall([0]), V
+ecsmall([0, 1]), [Vecsmall([0, 1]), Vecsmall([0]), Vecsmall([0]), Vecsmall([
+0])]]], [0, 0, 0, 0]]
+  ***   at top-level: ellinit([ffgen(5),1]
+  ***                 ^--------------------
+  *** ellinit: inconsistent moduli in ellinit: 3 != 5
+[0, 0, 0, 1.0000000000000000000000000000000000000, 1, 0, 2.00000000000000000
+00000000000000000000, 4, -1.0000000000000000000000000000000000000, -48.00000
+0000000000000000000000000000000, -864, -496.00000000000000000000000000000000
+000, 222.96774193548387096774193548387096774, Vecsmall([0]), [Vecsmall([128,
+ -1])], [0, 0, 0, 0]]
+  ***   at top-level: ellinit([1.,Mod(1,3)
+  ***                 ^--------------------
+  *** ellinit: incorrect type in elliptic curve base_ring (t_VEC).
+1
+-1
+1
+x^-2 + Mod(-1/5*x, x^2 + 5)*x^2 + Mod(-1/15, x^2 + 5)*x^6 + Mod(2/975*x, x^2
+ + 5)*x^10 + O(x^14)
+x^-1 + Mod(1/15*x, x^2 + 5)*x^3 + Mod(1/105, x^2 + 5)*x^7 + Mod(-2/10725*x, 
+x^2 + 5)*x^11 + O(x^15)
+x + Mod(1/60*x, x^2 + 5)*x^5 + Mod(1/2016, x^2 + 5)*x^9 + Mod(23/51891840*x,
+ x^2 + 5)*x^13 + O(x^17)
+Mod(1, 1009)*x^-2 + Mod(807, 1009)*x^2 + Mod(148, 1009)*x^6 + Mod(368, 1009)
+*x^10 + O(x^14)
+Mod(1, 1009)*x^-1 + Mod(740, 1009)*x^3 + Mod(123, 1009)*x^7 + Mod(150, 1009)
+*x^11 + O(x^15)
+Mod(1, 1009)*x + Mod(185, 1009)*x^5 + Mod(101, 1009)*x^9 + Mod(990, 1009)*x^
+13 + O(x^17)
+-52760
+-52832
+Total time spent: 568
diff --git a/src/test/64/ellanal b/src/test/64/ellanal
new file mode 100644
index 0000000..ec93e18
--- /dev/null
+++ b/src/test/64/ellanal
@@ -0,0 +1,14 @@
+[0, 0.25384186085591068433775892335090946105]
+[1, 0.30599977383405230182048368332167647444]
+[2, 1.5186330005768535404603852157894440392]
+[3, 10.391099400715804138751850510360917049]
+[5, 9997.0334671722554999496820788093288503]
+[-339/16, 691/64]
+[-3, 12]
+[69648970982596494254458225/166136231668185267540804, 5389624350896046150780
+04307258785218335/67716816556077455999228495435742408]
+[553/17424, 25469/2299968]
+35
+  ***   Warning: new stack size = 30000000 (28.610 Mbytes).
+[1317254400, 19916886528000]
+Total time spent: 10772
diff --git a/src/test/64/ff b/src/test/64/ff
new file mode 100644
index 0000000..2b163ca
--- /dev/null
+++ b/src/test/64/ff
@@ -0,0 +1,283 @@
+? test(2,20)
+[a^2 + a + 1, a^19 + a^18 + a^17 + a^15 + a^14 + a^13 + a^12 + a^8 + a^7 + a
+^6 + a^4 + a^2 + 1, 0, a + 1, 0, 0, 0, 0, a^19 + a^16 + a^15 + a^11 + a^8 + 
+a^5 + a^4 + a^3 + a^2 + a, a, a^2, a^12 + a^9 + a^6 + a^4 + a^3 + a^2 + a, a
+^18 + a^17 + a^16 + a^14 + a^13 + a^12 + a^11 + a^10 + a^9 + a^8 + a^7 + a^4
+ + a^3 + a^2 + 1, a^18 + a^17 + a^16 + a^14 + a^13 + a^12 + a^11 + a^10 + a^
+9 + a^8 + a^7 + a^4 + a^3 + a^2 + 1, a^16 + a^12 + a^9 + a^6 + a^4 + a^3 + a
+^2 + a + 1, 0, Mod(1, 2), Mod(0, 2), Mod(1, 2)*x^20 + Mod(1, 2)*x^17 + Mod(1
+, 2)*x^16 + Mod(1, 2)*x^12 + Mod(1, 2)*x^9 + Mod(1, 2)*x^6 + Mod(1, 2)*x^5 +
+ Mod(1, 2)*x^4 + Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(1, 2), Mod(1, 2)*x^20 +
+ Mod(1, 2)*x^17 + Mod(1, 2)*x^16 + Mod(1, 2)*x^12 + Mod(1, 2)*x^9 + Mod(1, 2
+)*x^6 + Mod(1, 2)*x^5 + Mod(1, 2)*x^4 + Mod(1, 2)*x^3 + Mod(1, 2)*x^2 + Mod(
+1, 2), [a, a^2, a^4, a^8, a^16, a^16 + a^12 + a^9 + a^6 + a^4 + a^3 + a^2, a
+^16 + a^12 + a^9 + a^6 + a^4 + a^3 + a + 1, a^16 + a^12 + a^9 + a^6 + a^3 + 
+a^2 + a, a^16 + a^12 + a^9 + a^8 + a^6 + a^4 + a^3 + a^2 + a + 1, a^12 + a^9
+ + a^6 + a^4 + a^3 + a^2 + a, a + 1, a^2 + 1, a^4 + 1, a^8 + 1, a^16 + 1, a^
+16 + a^12 + a^9 + a^6 + a^4 + a^3 + a^2 + 1, a^16 + a^12 + a^9 + a^6 + a^4 +
+ a^3 + a, a^16 + a^12 + a^9 + a^6 + a^3 + a^2 + a + 1, a^16 + a^12 + a^9 + a
+^8 + a^6 + a^4 + a^3 + a^2 + a]~, [x^3 + (a^17 + a^16 + a^10 + a^7 + a^3), 1
+; x^3 + (a^17 + a^16 + a^10 + a^7 + a^3 + a), 1], [], a/x, 1, a^18 + a^16 + 
+a^14 + a^13 + a^12 + a^11 + a^9 + a^8 + a^7 + a^3 + a^2 + a + 1, 1048575, a,
+ [x + (a^18 + a^16 + a^13 + a^9 + a^8 + a^7 + a^6 + a^4 + a^2 + a), 1; x + (
+a^18 + a^16 + a^13 + a^9 + a^8 + a^7 + a^6 + a^4 + a^2 + a + 1), 1], [a^18 +
+ a^16 + a^13 + a^9 + a^8 + a^7 + a^6 + a^4 + a^2 + a, a^18 + a^16 + a^13 + a
+^9 + a^8 + a^7 + a^6 + a^4 + a^2 + a + 1]~]
+? test(7,7)
+[a^2 + 3*a + 1, a^6 + 2*a^4 + 5*a^3 + 2*a^2 + 5*a + 1, 3*a + 3, a + 3, 5*a^6
+ + 3*a^4 + 4*a^3 + 3*a^2 + 4*a, 2*a + 2, 2*a + 2, 4*a + 4, 6*a^6 + 6*a^5 + 5
+*a^4 + 2, 6*a, a^2, 3*a^6 + 5*a^5 + 3*a^3 + a^2 + 2*a + 6, 4*a^2 + 4*a + 4, 
+4*a^2 + 4*a + 4, 4, 3*a^6 + 3*a^5 + 5*a^4 + 2*a^3 + 3*a^2 + a + 4, Mod(1, 7)
+, Mod(6, 7), Mod(1, 7)*x^7 + Mod(1, 7)*x^6 + Mod(2, 7)*x^5 + Mod(5, 7)*x + M
+od(1, 7), Mod(1, 7)*x^7 + Mod(1, 7)*x^6 + Mod(2, 7)*x^5 + Mod(5, 7)*x + Mod(
+1, 7), [a, 6*a^6 + 5*a^5 + 2*a + 6, 5*a^6 + 6*a^5 + 2*a^3 + 4*a^2 + 3*a + 5,
+ 3*a^6 + 5*a^2 + 5, 6*a^6 + 2*a^5 + 5*a^4 + 5*a^3 + 5*a^2 + a + 5, 2*a^6 + 5
+*a^2 + 6, 6*a^6 + a^5 + 2*a^4 + 2*a^2]~, [x^2 + (a^6 + 6*a^5 + 4*a^4 + 5*a^3
+ + 4)*x + 4, 1; x^2 + (2*a^6 + 5*a^5 + a^4 + 3*a^3 + 1)*x + 2, 1; x^2 + (4*a
+^6 + 3*a^5 + 2*a^4 + 6*a^3 + 2)*x + 1, 1], [0, 0, 0, a, 1, 0, 2*a, 4, 6*a^2,
+ a, 4, 6*a^3 + 2, a^6 + 4*a^5 + 4*a^4 + 6*a^2 + a + 4, Vecsmall([4]), [a, [V
+ecsmall([140737488355328, 0, 1]), Vecsmall([140737488355328, 1]), [Vecsmall(
+[140737488355328, 6]), Vecsmall([140737488355328]), Vecsmall([14073748835532
+8]), Vecsmall([140737488355328])]]], [0, 0, 0, 0]], a/x, (x + a)/(x + 6*a), 
+4*a^6 + 4*a^5 + 2*a^4 + 3*a^3 + a^2 + a + 6, 274514, a, [x + (2*a^6 + 6*a^5 
++ 5*a^2 + 2*a + 1), 1; x + (5*a^6 + a^5 + 2*a^2 + 5*a), 1], [2*a^6 + 6*a^5 +
+ 5*a^2 + 2*a, 5*a^6 + a^5 + 2*a^2 + 5*a + 6]~]
+? test(precprime(2^32),3)
+[a^2 + 3*a + 1, 3435973833*a^2 + 3435973833, 2863311528*a + 2863311528, a + 
+3435973833, 3579139409*a^2 + 2863311528, 3579139410*a + 3579139410, 1024*a +
+ 1024, 859832319*a + 859832319, 4294967290*a^2 + 4294967290*a + 4, 429496729
+0*a, a^2, 3885163399*a^2 + 2553150559*a + 523234686, a^2 + a + 1, a^2 + a + 
+1, 1, 4264202413*a^2 + 356078407*a + 3929909005, Mod(25, 4294967291), Mod(42
+94967290, 4294967291), Mod(1, 4294967291)*x^3 + Mod(1, 4294967291)*x^2 + Mod
+(4294967287, 4294967291)*x + Mod(1, 4294967291), Mod(1, 4294967291)*x^3 + Mo
+d(1, 4294967291)*x^2 + Mod(4294967287, 4294967291)*x + Mod(1, 4294967291), [
+a, a^2 + a + 4294967288, 4294967290*a^2 + 4294967289*a + 2]~, [x + (34444702
+3*a^2 + 1616586690*a + 252460086), 1; x + (3340051543*a^2 + 1627577691*a + 2
+021233148), 1; x^2 + (954915748*a^2 + 2667389600*a + 2273734143)*x + (816322
+992*a^2 + 830924795*a + 1995175223), 1; x^2 + (3950520268*a^2 + 2678380601*a
+ + 4042507205)*x + (1642837480*a^2 + 2548350348*a + 1670376662), 1], [0, 0, 
+0, a, 1, 0, 2*a, 4, 4294967290*a^2, 4294967243*a, 4294966427, 64*a^2 + 42949
+67035*a + 4294966923, 3618892287*a^2 + 1482857269*a + 1021597254, Vecsmall([
+4]), [a, [Vecsmall([140737488355328, 0, 1296]), Vecsmall([140737488355328, 4
+6656]), [Vecsmall([140737488355328, 6]), Vecsmall([140737488355328]), Vecsma
+ll([140737488355328]), Vecsmall([140737488355328])]]], [0, 0, 0, 0]], a/x, (
+x + a)/(x + 4294967290*a), 4020082597*a^2 + 70712658*a + 4018965070, 3689348
+8070109691946, a, [x + (1365670490*a^2 + 3373566631*a + 4083593885), 1; x + 
+(2929296801*a^2 + 921400660*a + 211373407), 1], [1365670490*a^2 + 3373566631
+*a + 4083593884, 2929296801*a^2 + 921400660*a + 211373406]~]
+? test(nextprime(2^32),3)
+[a^2 + 3*a + 1, a^2 + 4294967310, 1431655771*a + 1431655771, a + 3435973849,
+ 3579139425*a^2 + 1431655772, 715827886*a + 715827886, 1024*a + 1024, 114504
+4996*a + 1145044996, a^2 + a + 4294967309, 4294967310*a, a^2, 264190711*a^2 
++ 2629464558*a + 2494776416, 2086193154*a^2 + 2086193154*a + 2086193154, 220
+8774156*a^2 + 2208774156*a + 2208774156, 2086193154, 996804783*a^2 + 2908221
+018*a + 1206110100, Mod(13, 4294967311), Mod(4294967310, 4294967311), Mod(1,
+ 4294967311)*x^3 + Mod(1, 4294967311)*x^2 + Mod(4294967309, 4294967311)*x + 
+Mod(4294967310, 4294967311), Mod(1, 4294967311)*x^3 + Mod(1, 4294967311)*x^2
+ + Mod(4294967309, 4294967311)*x + Mod(4294967310, 4294967311), [a, a^2 + 42
+94967309, 4294967310*a^2 + 4294967310*a + 1]~, [x^2 + (2086193155*a^2 + 1225
+81001)*x + 2086193154, 1; x^2 + (2208774157*a^2 + 4172386308)*x + 2208774156
+, 1; x^2 + (4294967310*a^2 + 2)*x + 1, 1], [0, 0, 0, a, 1, 0, 2*a, 4, 429496
+7310*a^2, 4294967263*a, 4294966447, 64*a^2 + 4294967183*a + 4294966815, 1484
+088443*a^2 + 1141114953*a + 4283364322, Vecsmall([4]), [a, [Vecsmall([140737
+488355328, 0, 1296]), Vecsmall([140737488355328, 46656]), [Vecsmall([1407374
+88355328, 6]), Vecsmall([140737488355328]), Vecsmall([140737488355328]), Vec
+small([140737488355328])]]], [0, 0, 0, 0]], a/x, (x + a)/(x + 4294967310*a),
+ 4204026293*a^2 + 2068287144*a + 232863018, 6148914735617846011, a, [x + (26
+8392743*a^2 + 2459390605*a + 1304316255), 1; x + (4026574568*a^2 + 183557670
+6*a + 2990651057), 1], [268392743*a^2 + 2459390605*a + 1304316254, 402657456
+8*a^2 + 1835576706*a + 2990651056]~]
+? test2(p)=ffgen(x*Mod(1,p));g=ffprimroot(ffgen((x+1)*Mod(1,p)),&o);print([g,o]);fflog(g^17,g,o);
+? test2(2)
+[1, [1, matrix(0,2)]]
+0
+? test2(3)
+[2, [2, Mat([2, 1])]]
+1
+? test2(46744073709551653)
+[2, [46744073709551652, [2, 2; 3, 1; 7, 1; 37, 1; 1036513, 1; 14510113, 1]]]
+[]
+? test2(precprime(1<<32))
+[2, [4294967290, [2, 1; 5, 1; 19, 1; 22605091, 1]]]
+17
+? for(i=1,10,print(ffnbirred(11,i)));
+11
+55
+440
+3630
+32208
+295020
+2783880
+26793030
+261994040
+2593726344
+? for(i=1,10,print(ffnbirred(11,i,1)));
+11
+66
+506
+4136
+36344
+331364
+3115244
+29908274
+291902314
+2885628658
+? do(f,p,T)=centerlift(lift(polrootsff(f,p,T)));
+? do(x^3+x^2+x-1,3,t^3+t^2+t-1)
+[t, t^2 + 1, -t^2 - t + 1]~
+? t=ffgen(3^3,'t);do((x^3+x^2+x-1)*t^0,t.p,t.mod)
+[t, t^2 + 1, -t^2 - t + 1]~
+? polrootsff(x^4+1,2,y^2+y+1)
+[Mod(Mod(1, 2), Mod(1, 2)*y^2 + Mod(1, 2)*y + Mod(1, 2))]~
+? t=ffgen(7^4);fflog(t^6,t^2)
+3
+? t=ffgen(2^64)^((2^64-1)\5);1/t
+x^58 + x^57 + x^56 + x^52 + x^51 + x^49 + x^46 + x^45 + x^42 + x^39 + x^36 +
+ x^35 + x^32 + x^30 + x^29 + x^25 + x^23 + x^22 + x^21 + x^20 + x^19 + x^12 
++ x^8 + x^7 + x^6 + x^2
+? t=ffgen(('t^2+'t+1)*Mod(1,2));
+? factorff(x^12+t*x^10+x^6+(t+1)*x^2+1)
+
+[x + 1 6]
+
+[x + t 6]
+
+? polrootsff(x^2-x-ffgen((v^2+1)*Mod(1,3)))
+[]~
+? polrootsff(2*x+1,2,y)
+[]~
+? sqrt(Mod(-1,4296540161))
+Mod(1086811600, 4296540161)
+? sqrt(Mod(-1,18446744073944432641))
+Mod(6687681666819568403, 18446744073944432641)
+? centerlift(factorcantor(prod(i=-10,10,(x^2-i)),2^64+13)[,1])
+[x, x + 1, x + 2, x + 3, x + 248527397336721375, x + 2370518075556110396, x 
++ 2888582621843189425, x + 4741036151112220792, x + 5193293969518580612, x +
+ 6494187761904104278, x + 7111554226668331188, x + 7312212166335540022, x + 
+7562574061564804959, x - 7562574061564804959, x - 7312212166335540022, x - 7
+111554226668331188, x - 6494187761904104278, x - 5193293969518580612, x - 47
+41036151112220792, x - 2888582621843189425, x - 2370518075556110396, x - 248
+527397336721375, x - 3, x - 2, x - 1, x^2 + 2, x^2 + 3, x^2 + 8, x^2 + 10, x
+^2 - 10, x^2 - 8, x^2 - 3, x^2 - 2]~
+? #polrootsff(x^107+2*x^3+1,3,ffinit(3,107,'a))
+107
+? t=ffprimroot(ffgen(2^61));fflog(t^1234567891012345678,t)
+1234567891012345678
+? t=ffprimroot(ffgen(3^23));fflog(t^12345678910,t)
+12345678910
+? t=ffprimroot(ffgen(5^23));fflog(t^1234567891012345,t)
+1234567891012345
+? t=ffprimroot(ffgen(5^17));fflog(t^123456789101,t)
+123456789101
+? ffgen(x^2+x+Mod(1,3))
+  ***   at top-level: ffgen(x^2+x+Mod(1,3)
+  ***                 ^--------------------
+  *** ffgen: not an irreducible polynomial in ffgen: x^2 + x + 1.
+? conjvec(Mod(x,x^2+Mod(1,3)))
+[Mod(Mod(1, 3)*x, Mod(1, 3)*x^2 + Mod(1, 3)), Mod(Mod(2, 3)*x, Mod(1, 3)*x^2
+ + Mod(1, 3))]~
+? t=ffgen(5^4,'t);
+? factor((x^24-1)*t^0)
+
+[                x + 1 1]
+
+[                x + 2 1]
+
+[                x + 3 1]
+
+[                x + 4 1]
+
+[      x + (t^3 + 4*t) 1]
+
+[  x + (t^3 + 4*t + 1) 1]
+
+[  x + (t^3 + 4*t + 2) 1]
+
+[  x + (t^3 + 4*t + 3) 1]
+
+[  x + (t^3 + 4*t + 4) 1]
+
+[    x + (2*t^3 + 3*t) 1]
+
+[x + (2*t^3 + 3*t + 1) 1]
+
+[x + (2*t^3 + 3*t + 2) 1]
+
+[x + (2*t^3 + 3*t + 3) 1]
+
+[x + (2*t^3 + 3*t + 4) 1]
+
+[    x + (3*t^3 + 2*t) 1]
+
+[x + (3*t^3 + 2*t + 1) 1]
+
+[x + (3*t^3 + 2*t + 2) 1]
+
+[x + (3*t^3 + 2*t + 3) 1]
+
+[x + (3*t^3 + 2*t + 4) 1]
+
+[      x + (4*t^3 + t) 1]
+
+[  x + (4*t^3 + t + 1) 1]
+
+[  x + (4*t^3 + t + 2) 1]
+
+[  x + (4*t^3 + t + 3) 1]
+
+[  x + (4*t^3 + t + 4) 1]
+
+? factorff(Pol(0),t.p,t.mod)
+
+[0 1]
+
+? factorff(Pol(1),t.p,t.mod)
+[;]
+? factorff(x^4-t,t.p,t.mod)
+
+[Mod(Mod(1, 5), Mod(1, 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t 
++ Mod(3, 5))*x^4 + Mod(Mod(0, 5), Mod(1, 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*
+t^2 + Mod(1, 5)*t + Mod(3, 5))*x^3 + Mod(Mod(0, 5), Mod(1, 5)*t^4 + Mod(1, 5
+)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t + Mod(3, 5))*x^2 + Mod(Mod(0, 5), Mod(1,
+ 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t + Mod(3, 5))*x + Mod(M
+od(4, 5)*t, Mod(1, 5)*t^4 + Mod(1, 5)*t^3 + Mod(2, 5)*t^2 + Mod(1, 5)*t + Mo
+d(3, 5)) 1]
+
+? test(q)=my(t=ffgen(q,'t),m=[t,t^2,1+t^3;1+t,1+t^2,1+t^3]);print(matker(m));print(matimage(m));print(matrank(m));my(M=[t,2*t^0,3*t^0;t,t^2,1+t^3;1+t,1+t^2,1+t^3]);print(matdet(M));print(M^(-1)*M);my(v=[t^0,t^1,t^2]~);print(M*v);
+? test(2^5)
+[t^4 + t^3; t^4 + t^3; 1]
+[t, t^2; t + 1, t^2 + 1]
+2
+t^4 + t^2
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+[t^2 + t, t^4 + t^3 + 1, t^4 + t^3 + t]~
+? test(7^5)
+[3*t^4 + 5*t^3 + 6*t^2 + 2*t; 4*t^4 + 2*t^3 + t^2 + 5*t; 1]
+[t, t^2; t + 1, t^2 + 1]
+2
+6*t^4 + 2*t^3 + 4*t^2 + 2*t + 2
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+[3*t^2 + 3*t, 6*t^4 + 5*t^3 + 4*t^2 + 5*t + 6, 6*t^4 + 5*t^3 + 4*t^2 + 6*t]~
+? test((2^64+13)^5)
+[3*t^4 + 5*t^3 + 18446744073709551621*t^2 + 18446744073709551617*t; 18446744
+073709551626*t^4 + 18446744073709551624*t^3 + 8*t^2 + 12*t; 1]
+[t, t^2; t + 1, t^2 + 1]
+2
+18446744073709551628*t^4 + 2*t^3 + 18446744073709551626*t^2 + 2*t + 2
+[1, 0, 0; 0, 1, 0; 0, 0, 1]
+[3*t^2 + 3*t, 18446744073709551628*t^4 + 5*t^3 + 4*t^2 + 1844674407370955162
+7*t + 18446744073709551628, 18446744073709551628*t^4 + 5*t^3 + 4*t^2 + 18446
+744073709551628*t]~
+? p=2^64+13;g=ffprimroot(ffgen(p^2),&o);a=2*g^0;
+? v=[I,-1,Mat(1),matid(2)/2];
+? for(i=1,#v,print(iferr(fflog(a,g,v[i]),E,E)));
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_COMPLEX).")
+error("incorrect type in generic discrete logarithm (order factorization) (t
+_INT).")
+error("incorrect type in factorback [not a factorization] (t_MAT).")
+error("incorrect type in factorback [not an exponent vector] (t_COL).")
+? g^fflog(a,g,o)==a
+1
+? print("Total time spent: ",gettime);
+Total time spent: 1536
diff --git a/src/test/64/incgam b/src/test/64/incgam
new file mode 100644
index 0000000..8e40dcf
--- /dev/null
+++ b/src/test/64/incgam
@@ -0,0 +1,73 @@
+      1/2,          -100: 5.0 e-18
+   10 - I,    19 + 236*I: 3.4 e-18
+   10 - I,          -100: 3.8 e-18
+1 + 128*I,       -1/10*I: 1.9 e-17
+1 + 128*I, 1/10 - 1/10*I: 6.5 e-18
+      1/2,          -100: 1.0 e-37
+   10 - I,    19 + 236*I: 6.3 e-37
+   10 - I,          -100: 5.8 e-37
+1 + 128*I,       -1/10*I: 8.6 e-37
+1 + 128*I, 1/10 - 1/10*I: 2.1 e-37
+      1/2,          -100: 3.8 e-43
+   10 - I,          -100: 4.1 e-56
+1: -37
+2: -37
+3: -38
+4: -39
+5: -37
+6: -37
+7: oo
+8: -37
+9: oo
+10: -34
+11: oo
+12: -41
+13: -40
+1: -75
+2: -75
+3: -77
+4: -77
+5: -76
+6: -75
+7: -76
+8: -75
+9: -76
+10: -72
+11: -77
+12: -79
+13: -79
+6.4517096605632180286130396475962100207 E-43429453
+-0.0096304981549875294045330406967324266004 + 0.0104448408245333075664155335
+90425336552*I
+3.6835977616820321802351926205081189877 E-46
+0.033148544714002591996135923592143390256
+[0.048900510708061119567239835228049522318, 0.003779352409848906478874860132
+4664148561, 0.00036008245216265865929539411577179720024, 3.76656228439249017
+72557995950752726710 E-5, 4.1569689296853242774028598102781906834 E-6]
+   realprecision = 1001 significant digits (1000 digits displayed)
+3.68359776168203218023519262050811898765522013690956761970324308577568037914
+9250955037826947160993268362230577110061496836716989214907855071445311470563
+2214214800120311153618035942174272929063643378947115395627352197174490634249
+2683012033541722036066655328102708417224861577032395241919552543821704857579
+1038133881414147745573634350151991309184589035264539926631750948004685156693
+1899374361029305819637078712617022128735026710549374269865118275697787239064
+9991027682952231969799549304090829468335748935154281470949071433111492071075
+1767849583590888814450823588721920568263412763123076003634199144923928930230
+2664745888505585350305095849977148442159142952183358896948374051498045266578
+8430696482922647521423833733233609936621457658644695029880537347596251466117
+5788754599498196161120801923825870392159899899969875656841020930930028966473
+7128930613953196310035735416832096667412906792274453172606232569623655768685
+0600705332240514673617947823961471228466621963387238011480537605649806838965
+7919746393664 E-46
+   realprecision = 481 significant digits
+4.75192490656016273728795810646514224344948226385534946774854953981091171184
+5548377731754871666027106541752645628899410605265552482172045603954059223044
+9909582386495215175154235730928812285673186372640110666892550763379643115805
+1645655068324986893656866348640914630478382436361571896386505720797343025328
+1801089025819604231751731641093222001525360979162684147070957434471241677012
+3171321323534000876101297696945758112884328591356813522962187412618386959055
+56983544264467232044835371 E-68
+  ***   at top-level: eint1(0)
+  ***                 ^--------
+  *** eint1: domain error in eint1: x = 0
+Total time spent: 84
diff --git a/src/test/64/isprime b/src/test/64/isprime
new file mode 100644
index 0000000..5cb0414
--- /dev/null
+++ b/src/test/64/isprime
@@ -0,0 +1,25 @@
+1
+1
+1
+
+[      2 5 1]
+
+[      3 2 1]
+
+[1000003 2 1]
+
+[1000033 2 1]
+
+
+[      2 3 1]
+
+[     29 2 1]
+
+[1000003 2 1]
+
+[1, 1, 0]
+[1, Mat([2, 2, 1]), 0]
+[1, 1, 0]
+[0, 1, 0, 1]
+1
+Total time spent: 3036
diff --git a/src/test/64/kernel b/src/test/64/kernel
new file mode 100644
index 0000000..f28bcc8
--- /dev/null
+++ b/src/test/64/kernel
@@ -0,0 +1,23 @@
+INT: 187654321
+conv:4000000000000003  000000000b2f60b1  
++:4000000000000003  000000000a72ff63  
+-:4000000000000003  000000000bebc1ff  
+*:c000000000000003  00083b0b5e0e86ee  
+/:c000000000000003  000000000000000f  
+rem:4000000000000003  000000000025ad1f  
+pow:
+4000000000000003  007d1b13db833a61  
+4000000000000004  00003d2374418fff  02bee98cc29618c1  
+4000000000000006  000000000e99ea50  3d586253467c3cea  a7f1a74285bba548  639e3a9d8890c181  
+4000000000000009  00d5322ab115c7b2  9cf0c5711679c253  8cea7ca01cbe7596  78ae7d09c969dc26  cd42e2ad51204d31  635bced866d36dbb  0351ebad5363c301  
+4000000000000010  0000b18c84df6289  66df9491766a39a4  0685a8a3cfc68c74  4d9dcb9102634462  b398839e1a6892ef  5efe0281bcae4afb  eb64fcabf930fa7a  00f013352a5dc7c3  4295b5c3b031c96e  a5c190798e647dac  09e2091e67abbc58  c118d1b776f5e31c  e97a9180140e859c  64a53cad0d508601  
+invmod:4000000000000010  000089fe3c4ba859  b9472dacd0585e64  b5311881edf8024b  aaf6ce9e98d94a2d  b253c748bc185be1  6de1e5f8831bbecd  a3d629dc0fc9440e  b0ab325d53dd21c5  9fb7fc20cc22d98c  e8fe6eca928c7f4f  a3861061a7ab0bdc  71deda2617a28387  774875639f0f88ac  424ca4d53dcae2a5  
+
+REAL: 187654321.000000
+conv1:600000000000001b  b2f60b1000000000  
+conv2:600000000000001b  b2f60b1000000000  
++:600000000000001b  a72ff63000000000  
+-:600000000000001b  bebc1ff000000000  
+*:e000000000000033  83b0b5e0e86ee000  
+/:e000000000000003  f33334820be1df0a  
+gcc bug?:6000000000000000  c000000000000000  
diff --git a/src/test/64/member b/src/test/64/member
new file mode 100644
index 0000000..50827a1
--- /dev/null
+++ b/src/test/64/member
@@ -0,0 +1,319 @@
+.pol: x
+.a1: 0
+.a2: 0
+.a3: 0
+.a4: 0
+.a6: 0
+NF
+.codiff: [1, 553/1105; 0, 1/1105]
+.diff: [1105, 553; 0, 1]
+.disc: 1105
+.index: 2
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.pol: y^2 - 1105
+.r1: 2
+.r2: 0
+.roots: [-33.241540277189322804630581542105218897, 33.2415402771893228046305
+81542105218897]
+.sign: [2, 0]
+.t2: [2, -1.0000000000000000000000000000000000000; -1.0000000000000000000000
+000000000000000, 553.00000000000000000000000000000000000]
+.zk: [1, 1/2*y - 1/2]
+NF chvar
+  *** nfinit: Warning: non-monic polynomial. Result of the form [nf,c].
+.codiff: [1/2, 0; 0, 1/4]
+.diff: [4, 0; 0, 2]
+.disc: -8
+.index: 1
+.nf: [y^2 + 2, [0, 1], -8, 1, [Mat([1, 0.E-38 + 1.41421356237309504880168872
+42096980786*I]), [1, 1.4142135623730950488016887242096980786; 1, -1.41421356
+23730950488016887242096980786], [1, 1; 1, -1], [2, 0; 0, -4], [4, 0; 0, 2], 
+[2, 0; 0, -1], [2, [0, -2; 1, 0]], [2]], [0.E-38 + 1.41421356237309504880168
+87242096980786*I], [1, y], [1, 0; 0, 1], [1, 0, 0, -2; 0, 1, 1, 0]]
+.pol: y^2 + 2
+.r1: 0
+.r2: 1
+.roots: [0.E-38 + 1.4142135623730950488016887242096980786*I]
+.sign: [0, 1]
+.t2: [2, 0.E-38; 0.E-38, 4.0000000000000000000000000000000000000]
+.zk: [1, y]
+BNF
+.bnf: [[2, 0; 0, 2], [1, 1, 0; 1, 0, 1], [10.9503854058256053302677508250179
+37393 + 3.1415926535897932384626433832795028842*I; -10.950385405825605330267
+750825017937393 + 6.2831853071795864769252867665590057684*I], [-2.8070134016
+636593080928506577483570863 + 6.2831853071795864769252867665590057684*I, -6.
+4656286076812397829259659980344686073 + 6.2831853071795864769252867665590057
+684*I, 6.3140644011531557847583424971265245465 + 4.701977403289150032 E-38*I
+, 0, 0; 2.8070134016636593080928506577483570863 + 3.141592653589793238462643
+3832795028842*I, 6.4656286076812397829259659980344686073 + 3.141592653589793
+2384626433832795028842*I, -6.3140644011531557847583424971265245465 + 7.05296
+6104933725048 E-38*I, 0, 0], [[2, [-1, 1]~, 1, 1, [0, 276; 1, -1]], [3, [0, 
+2]~, 1, 1, [-1, -276; -1, 0]], [5, [1, 2]~, 2, 1, [1, 552; 2, -1]], [2, [2, 
+1]~, 1, 1, [1, 276; 1, 0]], [3, [2, 2]~, 1, 1, [0, -276; -1, 1]]], 0, [y^2 -
+ 1105, [2, 0], 1105, 2, [[1, -17.120770138594661402315290771052609448; 1, 16
+.120770138594661402315290771052609448], [1, -17.1207701385946614023152907710
+52609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1, 16], [2, 
+-1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276; 1, 552]],
+ [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.241540277189322
+804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0, 276; 0, 1,
+ 1, -1]], [[4, [2, 2], [[2, 0; 0, 1], [3, 1; 0, 1]]], 10.9503854058256053302
+67750825017937393, 1, [2, -1], [857*y - 28488]], [[-1, 0; 0, -1], [[0, 0], [
+0, 0]], [[2.8070134016636593080928506577483570863 - 6.2831853071795864769252
+867665590057684*I, -2.8070134016636593080928506577483570863 - 3.141592653589
+7932384626433832795028842*I], [6.4656286076812397829259659980344686073 - 6.2
+831853071795864769252867665590057684*I, -6.465628607681239782925965998034468
+6073 - 3.1415926535897932384626433832795028842*I]]], [0, 0]]
+.clgp: [4, [2, 2], [[2, 0; 0, 1], [3, 1; 0, 1]]]
+.codiff: [1, 553/1105; 0, 1/1105]
+.cyc: [2, 2]
+.diff: [1105, 553; 0, 1]
+.disc: 1105
+.fu: [Mod(857*y - 28488, y^2 - 1105)]
+.gen: [[2, 0; 0, 1], [3, 1; 0, 1]]
+.index: 2
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.no: 4
+.pol: y^2 - 1105
+.r1: 2
+.r2: 0
+.reg: 10.950385405825605330267750825017937393
+.roots: [-33.241540277189322804630581542105218897, 33.2415402771893228046305
+81542105218897]
+.sign: [2, 0]
+.t2: [2, -1.0000000000000000000000000000000000000; -1.0000000000000000000000
+000000000000000, 553.00000000000000000000000000000000000]
+.tu: [2, Mod(-1, y^2 - 1105)]
+.zk: [1, 1/2*y - 1/2]
+BNR
+.bid: [[[4, 0; 0, 4], [0, 0]], [4, [2, 2], [[1, -2]~, [-1, -2]~]], [[2, [-1,
+ 1]~, 1, 1, [0, 276; 1, -1]], 2; [2, [2, 1]~, 1, 1, [1, 276; 1, 0]], 2], [[[
+[1], [1], [[1, 0]~], [Vecsmall([])], 1], [[2], [-1], [[1, -2]~], [Vecsmall([
+])], Mat([1/2, -1/2])]], [[[1], [1], [[1, 0]~], [Vecsmall([])], 1], [[2], [-
+1], [[-1, -2]~], [Vecsmall([])], Mat([1/2, 0])]], [[], [], []]], [0, 1, 0, 0
+; 0, 0, 0, 1]]
+.bnf: [[2, 0; 0, 2], [1, 1, 0; 1, 0, 1], [10.9503854058256053302677508250179
+37393 + 3.1415926535897932384626433832795028842*I; -10.950385405825605330267
+750825017937393 + 6.2831853071795864769252867665590057684*I], [-2.8070134016
+636593080928506577483570863 + 6.2831853071795864769252867665590057684*I, -6.
+4656286076812397829259659980344686073 + 6.2831853071795864769252867665590057
+684*I, 6.3140644011531557847583424971265245465 + 4.701977403289150032 E-38*I
+, 0, 0; 2.8070134016636593080928506577483570863 + 3.141592653589793238462643
+3832795028842*I, 6.4656286076812397829259659980344686073 + 3.141592653589793
+2384626433832795028842*I, -6.3140644011531557847583424971265245465 + 7.05296
+6104933725048 E-38*I, 0, 0], [[2, [-1, 1]~, 1, 1, [0, 276; 1, -1]], [3, [0, 
+2]~, 1, 1, [-1, -276; -1, 0]], [5, [1, 2]~, 2, 1, [1, 552; 2, -1]], [2, [2, 
+1]~, 1, 1, [1, 276; 1, 0]], [3, [2, 2]~, 1, 1, [0, -276; -1, 1]]], 0, [y^2 -
+ 1105, [2, 0], 1105, 2, [[1, -17.120770138594661402315290771052609448; 1, 16
+.120770138594661402315290771052609448], [1, -17.1207701385946614023152907710
+52609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1, 16], [2, 
+-1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276; 1, 552]],
+ [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.241540277189322
+804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0, 276; 0, 1,
+ 1, -1]], [[4, [2, 2], [[2, 0; 0, 1], [3, 1; 0, 1]]], 10.9503854058256053302
+67750825017937393, 1, [2, -1], [857*y - 28488]], [[-1, 0; 0, -1], [[0, 0], [
+0, 0]], [[2.8070134016636593080928506577483570863 - 6.2831853071795864769252
+867665590057684*I, -2.8070134016636593080928506577483570863 - 3.141592653589
+7932384626433832795028842*I], [6.4656286076812397829259659980344686073 - 6.2
+831853071795864769252867665590057684*I, -6.465628607681239782925965998034468
+6073 - 3.1415926535897932384626433832795028842*I]]], [0, [Mat([[16, -1]~, 1]
+), Mat([[-137, -8]~, 1])]]]
+.clgp: [4, [2, 2]]
+.codiff: [1, 553/1105; 0, 1/1105]
+.cyc: [2, 2]
+.diff: [1105, 553; 0, 1]
+.disc: 1105
+.index: 2
+.mod: [[4, 0; 0, 4], [0, 0]]
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.no: 4
+.pol: y^2 - 1105
+.r1: 2
+.r2: 0
+.roots: [-33.241540277189322804630581542105218897, 33.2415402771893228046305
+81542105218897]
+.sign: [2, 0]
+.t2: [2, -1.0000000000000000000000000000000000000; -1.0000000000000000000000
+000000000000000, 553.00000000000000000000000000000000000]
+.zk: [1, 1/2*y - 1/2]
+.zkst: [4, [2, 2], [[1, -2]~, [-1, -2]~]]
+RNF
+.disc: [[4420, 553; 0, 1], [1, 2]~]
+.index: [2, 0; 0, 1]
+.nf: [y^2 - 1105, [2, 0], 1105, 2, [[1, -17.12077013859466140231529077105260
+9448; 1, 16.120770138594661402315290771052609448], [1, -17.12077013859466140
+2315290771052609448; 1, 16.120770138594661402315290771052609448], [1, -17; 1
+, 16], [2, -1; -1, 553], [1105, 553; 0, 1], [553, 1; 1, 2], [1105, [553, 276
+; 1, 552]], [5, 13, 17]], [-33.241540277189322804630581542105218897, 33.2415
+40277189322804630581542105218897], [1, 1/2*y - 1/2], [1, 1; 0, 2], [1, 0, 0,
+ 276; 0, 1, 1, -1]]
+.pol: x^2 - y
+.polabs: x^4 - 1105
+.zk: [[1, x - 1], [1, [1, 1/2; 0, 1/2]]]
+QUADCLASSUNIT
+.clgp: [4, [2, 2], [Qfb(2, 31, -18, 0.E-57), Qfb(3, 29, -22, 0.E-57)]]
+.cyc: [2, 2]
+.gen: [Qfb(2, 31, -18, 0.E-57), Qfb(3, 29, -22, 0.E-57)]
+.no: 4
+.reg: 10.950385405825605330267750825017937393
+GAL
+.gen: [Vecsmall([2, 1])]
+.group: [Vecsmall([1, 2]), Vecsmall([2, 1])]
+.mod: 1341068619663964900807
+.orders: Vecsmall([2])
+.p: 7
+.pol: x^2 - 2
+.roots: [1323350126780315668282, 17718492883649232525]~
+ELL
+.a1: 1
+.a2: 2
+.a3: 3
+.a4: 4
+.a6: 5
+.b2: 9
+.b4: 11
+.b6: 29
+.b8: 35
+.c4: -183
+.c6: -3429
+.area: 2.9719152678179096707716479509361896060
+.disc: -10351
+.eta: [3.1096482423243803285501491221965830079, 1.55482412116219016427507456
+10982915039 + 1.0643747452102737569438859937299427442*I]
+.gen: [[1, 2]]
+.j: 6128487/10351
+.omega: [2.7807400137667297710631976271813584994, 1.390370006883364885531598
+8135906792497 - 1.0687497763561930661592635474375038788*I]
+.roots: [-1.6189099322673713423780009396072169751, -0.3155450338663143288109
+9953019639151248 + 2.0925470969119586079816894466366945829*I, -0.31554503386
+631432881099953019639151248 - 2.0925470969119586079816894466366945829*I]~
+ELLFp
+.a1: Mod(1, 13)
+.a2: Mod(2, 13)
+.a3: Mod(3, 13)
+.a4: Mod(4, 13)
+.a6: Mod(5, 13)
+.b2: Mod(9, 13)
+.b4: Mod(11, 13)
+.b6: Mod(3, 13)
+.b8: Mod(9, 13)
+.c4: Mod(12, 13)
+.c6: Mod(3, 13)
+.cyc: [13]
+.disc: Mod(10, 13)
+.gen: [[Mod(6, 13), Mod(12, 13)]]
+.group: [13, [13], [[Mod(6, 13), Mod(12, 13)]]]
+.j: Mod(9, 13)
+.no: 13
+.p: 13
+ELLFq
+.a1: 1
+.a2: 2
+.a3: 3
+.a4: 4
+.a6: 5
+.b2: 9
+.b4: 11
+.b6: 3
+.b8: 9
+.c4: 12
+.c6: 3
+.cyc: [195]
+.disc: 10
+.gen: [[6*x + 1, x + 6]]
+.group: [195, [195], [[6*x + 1, x + 6]]]
+.j: 9
+.no: 195
+.p: 13
+ELLQp
+.a1: 1
+.a2: 2
+.a3: 3
+.a4: 4
+.a6: 5
+.b2: 9
+.b4: 11
+.b6: 29
+.b8: 35
+.c4: -183
+.c6: -3429
+.disc: -10351
+.j: 6128487/10351
+.p: 11
+.roots: [9 + O(11^2)]~
+.tate: [6 + 8*11 + 5*11^2 + O(11^4), Mod(u, u^2 + (5 + 2*11 + 5*11^2 + 10*11
+^3 + O(11^4))), 3*11 + 7*11^2 + O(11^4), [6 + 3*11 + O(11^4), 6 + 11 + 9*11^
+2 + 11^3 + O(11^4)]]
+FFELT
+.f: 3
+.mod: x^3 + x^2 + 1
+.p: 2
+.pol: x
+.f: 3
+.mod: x^3 + x^2 + x + 2
+.p: 3
+.pol: x
+.f: 2
+.mod: x^2 + x + 1
+.p: 18446744073709551629
+.pol: x
+INTMOD
+.mod: 3
+POLMOD
+.mod: x^2 + 1
+.pol: x
+QFB
+QUAD
+.disc: -4
+.fu: []
+.mod: x^2 + 1
+.pol: x^2 + 1
+.tu: [4, w]
+.zk: [1, x]
+PRID
+.e: 1
+.f: 1
+.gen: [2, [-1, 1]~]
+.p: 2
+MODPR
+.e: 1
+.f: 1
+.gen: [2, [-1, 1]~]
+.p: 2
+BID
+.bid: [[[4, 1; 0, 1], [0, 0]], [2, [2], [3]], Mat([[2, [-1, 1]~, 1, 1, [0, 2
+76; 1, -1]], 2]), [[[[1], [1], [1], [Vecsmall([])], 1], [[2], [-1], [-1], [V
+ecsmall([])], Mat([1/2, -1/2])]], [[], [], []]], Mat([0, 1])]
+.clgp: [2, [2], [3]]
+.cyc: [2]
+.gen: [3]
+.mod: [[4, 1; 0, 1], [0, 0]]
+.no: 2
+.zkst: [2, [2], [3]]
+BID (nogen)
+.bid: [[[4, 1; 0, 1], [0, 0]], [2, [2]], Mat([[2, [-1, 1]~, 1, 1, [0, 276; 1
+, -1]], 2]), [[[[1], [1], [1], [Vecsmall([])], 1], [[2], [-1], [-1], [Vecsma
+ll([])], Mat([1/2, -1/2])]], [[], [], []]], Mat([0, 1])]
+.clgp: [2, [2]]
+.cyc: [2]
+.mod: [[4, 1; 0, 1], [0, 0]]
+.no: 2
+.zkst: [2, [2]]
+Total time spent: 48
diff --git a/src/test/64/nf b/src/test/64/nf
new file mode 100644
index 0000000..c3e87e0
--- /dev/null
+++ b/src/test/64/nf
@@ -0,0 +1,361 @@
+[85997496, [42998748, 2], [[408188227, 99620635; 0, 1], [2, 1; 0, 1]]]
+12.340047278667903334059769086970462250
+4.1894250945222025884896456921310573068
+20915648110955829231381594293324156411897455346679838307589120000
+571459344155975480004612560667633185714077696
+[54898, [54898], [[7, 0; 0, 1]]]
+[26, [26], [[19, 14, 5, 13; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]]]
+[1, [], []]
+  *** zetakinit: Warning: non-monic polynomial. Result of the form [nf,c].
+  *** zetakinit: Warning: non-monic polynomial. Change of variables discarded.
+20/3
+-5
+13
+0
+5/2
+-1
+1024/243
+-1
+31
+-11
+1/32
+15853839
+1736217747
+Mod(4/3, y^5 - 4*y^3 + 2*y + 11)
+Mod(-1, y^5 - 4*y^3 + 2*y + 11)
+Mod(y^2 + y + 1, y^5 - 4*y^3 + 2*y + 11)
+Mod(y, y^5 - 4*y^3 + 2*y + 11)
+Mod(1/2, y^5 - 4*y^3 + 2*y + 11)
+Mod(5*y^4 + 4*y^3 - 12*y^2 - y - 5, y^5 - 4*y^3 + 2*y + 11)
+[4/3, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[3, 1, 1, 0, 0]~
+[0, 1, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1, 2, 3, 4, 5]~
+(f)->for(i=1,#v,for(j=1,#v,print(f(nf,v[i],v[j]))))
+[8/3, 0, 0, 0, 0]~
+[1/3, 0, 0, 0, 0]~
+[13/3, 1, 1, 0, 0]~
+[4/3, 1, 0, 0, 0]~
+[11/6, 0, 0, 0, 0]~
+[7/3, 2, 3, 4, 5]~
+[1/3, 0, 0, 0, 0]~
+[-2, 0, 0, 0, 0]~
+[2, 1, 1, 0, 0]~
+[-1, 1, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[0, 2, 3, 4, 5]~
+[13/3, 1, 1, 0, 0]~
+[2, 1, 1, 0, 0]~
+[6, 2, 2, 0, 0]~
+[3, 2, 1, 0, 0]~
+[7/2, 1, 1, 0, 0]~
+[4, 3, 4, 4, 5]~
+[4/3, 1, 0, 0, 0]~
+[-1, 1, 0, 0, 0]~
+[3, 2, 1, 0, 0]~
+[0, 2, 0, 0, 0]~
+[1/2, 1, 0, 0, 0]~
+[1, 3, 3, 4, 5]~
+[11/6, 0, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[7/2, 1, 1, 0, 0]~
+[1/2, 1, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[3/2, 2, 3, 4, 5]~
+[7/3, 2, 3, 4, 5]~
+[0, 2, 3, 4, 5]~
+[4, 3, 4, 4, 5]~
+[1, 3, 3, 4, 5]~
+[3/2, 2, 3, 4, 5]~
+[2, 4, 6, 8, 10]~
+[1, 0, 0, 0, 0]~
+[-4/3, 0, 0, 0, 0]~
+[-64/93, 16/31, 16/93, -8/31, 4/93]~
+[0, 4/33, 4/33, 0, -4/33]~
+[8/3, 0, 0, 0, 0]~
+[1587988/47561517, -165136/5284613, 41300/5284613, -212540/47561517, -78712/
+47561517]~
+[-3/4, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[16/31, -12/31, -4/31, 6/31, -1/31]~
+[0, -1/11, -1/11, 0, 1/11]~
+[-2, 0, 0, 0, 0]~
+[-396997/15853839, 123852/5284613, -30975/5284613, 53135/15853839, 19678/158
+53839]~
+[9/4, 3/4, 3/4, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[1, 0, 0, 0, 0]~
+[1, 12/11, 1/11, 0, -1/11]~
+[6, 2, 2, 0, 0]~
+[1249690/15853839, -222317/5284613, 39600/5284613, -477392/15853839, 434/158
+53839]~
+[0, 3/4, 0, 0, 0]~
+[0, -1, 0, 0, 0]~
+[3/31, -10/31, 7/31, 5/31, -6/31]~
+[1, 0, 0, 0, 0]~
+[0, 2, 0, 0, 0]~
+[-672280/15853839, 150044/5284613, -148123/5284613, 73247/15853839, -53135/1
+5853839]~
+[3/8, 0, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[-8/31, 6/31, 2/31, -3/31, 1/62]~
+[0, 1/22, 1/22, 0, -1/22]~
+[1, 0, 0, 0, 0]~
+[396997/31707678, -61926/5284613, 30975/10569226, -53135/31707678, -9839/158
+53839]~
+[3/4, 3/2, 9/4, 3, 15/4]~
+[-1, -2, -3, -4, -5]~
+[-314/31, -59/31, 311/31, 14/31, -85/31]~
+[7, -27/11, 39/11, 5, 5/11]~
+[2, 4, 6, 8, 10]~
+[1, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[-1, 1, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[3, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-2, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[2, 1, 1, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[1, 0, 0, 0, 0]~
+[1, 1, 0, 0, 0]~
+[6, 2, 2, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 1, 0, 0, 0]~
+[0, -1, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 2, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 2, 2, 3, 4]~
+[-1, -2, -3, -4, -5]~
+[-10, -2, 10, 0, -3]~
+[7, -2, 4, 5, 0]~
+[2, 4, 6, 8, 10]~
+[1, 0, 0, 0, 0]~
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[-1, 0, 0, 0, 0]~, [1/3, 0, 0, 0, 0]~]
+[[-1, 1, 0, 0, 0]~, [7/3, -2, 0, -1, 0]~]
+[[0, 0, 0, 0, 0]~, [4/3, 0, 0, 0, 0]~]
+[[3, 0, 0, 0, 0]~, [-1/6, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [4/3, 0, 0, 0, 0]~]
+[[-1, 0, 0, 0, 0]~, [1/3, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [-4, -1, -1, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [-1, 0, 0, 0, 0]~]
+[[-2, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [-1, 0, 0, 0, 0]~]
+[[2, 1, 1, 0, 0]~, [1/3, -1/3, -1/3, 0, 0]~]
+[[-3, -1, -1, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[1, 1, 0, 0, 0]~, [1, 0, 0, 0, 0]~]
+[[6, 2, 2, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [3, 1, 1, 0, 0]~]
+[[0, 1, 0, 0, 0]~, [0, -1/3, 0, 0, 0]~]
+[[0, -1, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [0, 1, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 2, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [0, 1, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[[0, 0, 0, 0, 0]~, [1/2, 0, 0, 0, 0]~]
+[[1, 2, 2, 3, 4]~, [-1/3, -2/3, 1/3, 0, -1/3]~]
+[[-1, -2, -3, -4, -5]~, [0, 0, 0, 0, 0]~]
+[[-10, -2, 10, 0, -3]~, [-6, -2, 1, 2, 1]~]
+[[7, -2, 4, 5, 0]~, [-5, 0, 0, 0, 0]~]
+[[2, 4, 6, 8, 10]~, [0, 0, 0, 0, 0]~]
+[[1, 0, 0, 0, 0]~, [0, 0, 0, 0, 0]~]
+[0, 0, 0, 0, 0]~
+[1/3, 0, 0, 0, 0]~
+[7/3, -2, 0, -1, 0]~
+[4/3, 0, 0, 0, 0]~
+[-1/6, 0, 0, 0, 0]~
+[4/3, 0, 0, 0, 0]~
+[1/3, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-4, -1, -1, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[-1, 0, 0, 0, 0]~
+[1/3, -1/3, -1/3, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[3, 1, 1, 0, 0]~
+[0, -1/3, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 1, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 1, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[1/2, 0, 0, 0, 0]~
+[-1/3, -2/3, 1/3, 0, -1/3]~
+[0, 0, 0, 0, 0]~
+[-6, -2, 1, 2, 1]~
+[-5, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[0, 0, 0, 0, 0]~
+[16/9, 0, 0, 0, 0]~
+[-4/3, 0, 0, 0, 0]~
+[4, 4/3, 4/3, 0, 0]~
+[0, 4/3, 0, 0, 0]~
+[2/3, 0, 0, 0, 0]~
+[4/3, 8/3, 4, 16/3, 20/3]~
+[-4/3, 0, 0, 0, 0]~
+[1, 0, 0, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[0, -1, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[-1, -2, -3, -4, -5]~
+[4, 4/3, 4/3, 0, 0]~
+[-3, -1, -1, 0, 0]~
+[13, 5, 6, 2, 1]~
+[2, 3, 1, 1, 0]~
+[3/2, 1/2, 1/2, 0, 0]~
+[-58, -42, 23, 27, 17]~
+[0, 4/3, 0, 0, 0]~
+[0, -1, 0, 0, 0]~
+[2, 3, 1, 1, 0]~
+[2, 0, 1, 0, 0]~
+[0, 1/2, 0, 0, 0]~
+[-33, -3, 11, 8, 4]~
+[2/3, 0, 0, 0, 0]~
+[-1/2, 0, 0, 0, 0]~
+[3/2, 1/2, 1/2, 0, 0]~
+[0, 1/2, 0, 0, 0]~
+[1/4, 0, 0, 0, 0]~
+[1/2, 1, 3/2, 2, 5/2]~
+[4/3, 8/3, 4, 16/3, 20/3]~
+[-1, -2, -3, -4, -5]~
+[-58, -42, 23, 27, 17]~
+[-33, -3, 11, 8, 4]~
+[1/2, 1, 3/2, 2, 5/2]~
+[-1071, -384, -251, -155, 20]~
+[1]
+[1, 1/2*x - 1/2]
+[2, Mod(0, 2)]~
+[x^2 + x + 1, [0, 1], -3, 1, [Mat([1, -0.50000000000000000000000000000000000
+000 + 0.86602540378443864676372317075293618347*I]), [1, 0.366025403784438646
+76372317075293618347; 1, -1.3660254037844386467637231707529361835], [1, 0; 1
+, -1], [2, -1; -1, -1], [3, 2; 0, 1], [1, -1; -1, -2], [3, [2, -1; 1, 1]], [
+]], [-0.50000000000000000000000000000000000000 + 0.8660254037844386467637231
+7075293618347*I], [1, x], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, -1]]
+2
+[0, Mod(0, 2), 1, 0, 0, 0, 0]~
+[0, Mod(1, 2), 1, 0, 1, 0, 0]~
+[]~
+[]~
+388
+
+[2 0]
+
+[0 1]
+
+  *** bnfisprincipal: Warning: precision too low for generators, not given.
+[[]~, [-16275043782306513717209797591668600538906793729160424387141562023303
+069241961, -3992515767463859376807521115314587378342597458337773390379448027
+181914746015, 40263088752008514039400780199135662260965092541683607359784818
+2049497266399, 3875196415920480829978279850511752676499384722019721458357455
+29259111353576, 524613164482816169908873750526849574668376660999341089640759
+880949016596504]~]
+[[[[[1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0,
+ 1], Vecsmall([1])], [2, [2]], matrix(0,2), [[[2], [-1], [Vecsmall([1])]]], 
+Mat(1)]], [[[[2, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0,
+ 0, 0, 0, 1], Vecsmall([1])], [2, [2]], Mat([[2, [0, 0, 1, -1, 0]~, 3, 1, [0
+, -80, 60, -10, -60; 0, 0, -100, 0, -20; 0, 0, 0, -10, -100; 0, 2, -2, 0, 2;
+ 1, 0, 0, -10, 0]], 1]), [[[[1], [1], [1], [Vecsmall([])], 1]], [[2], [-1], 
+[Vecsmall([1])]]], Mat(1)]], [], [[[[2, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 
+0, 0; 0, 0, 0, 2, 0; 0, 0, 0, 0, 1], Vecsmall([1])], [4, [2, 2]], Mat([[2, [
+0, 0, 1, -1, 0]~, 3, 1, [0, -80, 60, -10, -60; 0, 0, -100, 0, -20; 0, 0, 0, 
+-10, -100; 0, 2, -2, 0, 2; 1, 0, 0, -10, 0]], 2]), [[[[1], [1], [1], [Vecsma
+ll([])], 1], [[2], [[1, 0, 0, -1, 0]~], [[1, 0, 0, -1, 0]~], [Vecsmall([])],
+ Mat([0, 0, 0, 1, 0])]], [[2], [-1], [Vecsmall([1])]]], [1, 0; 0, 1]], [[[2,
+ 0, 1, 0, 0; 0, 2, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], Ve
+csmall([1])], [6, [6]], Mat([[2, [1, 2, 1, 0, 0]~, 1, 2, [0, -80, -20, 0, 0;
+ 0, -1, -100, -10, -120; 1, 0, -1, -20, -100; 0, 2, 0, 0, 0; 1, 1, -1, -10, 
+0]], 1]), [[[[3], [[1, 1, 0, 0, 0]~], [[1, 1, 0, 0, 0]~], [Vecsmall([])], 1]
+], [[2], [-1], [Vecsmall([1])]]], Mat([-2, -3])]]]
+[[[[[[1, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0
+, 1], Vecsmall([1])], [2, [2], [[-1, 0, 0, 0, 0]~]], matrix(0,2), [[[2], [-1
+], [Vecsmall([1])]]], Mat(1)], Mat([1, 1, 1])]], [[[[[2, 0, 0, 0, 0; 0, 1, 0
+, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], Vecsmall([1])], [2, [2
+], [[-1, 0, 0, 0, 0]~]], Mat([[2, [0, 0, 1, -1, 0]~, 3, 1, [0, -80, 60, -10,
+ -60; 0, 0, -100, 0, -20; 0, 0, 0, -10, -100; 0, 2, -2, 0, 2; 1, 0, 0, -10, 
+0]], 1]), [[[[1], [1], [1], [Vecsmall([])], 1]], [[2], [-1], [Vecsmall([1])]
+]], Mat(1)], Mat([1, 1, 1])]], [], [[[[[2, 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 
+1, 0, 0; 0, 0, 0, 2, 0; 0, 0, 0, 0, 1], Vecsmall([1])], [4, [2, 2], [[1, 0, 
+0, -1, 0]~, [-1, 0, 0, 0, 0]~]], Mat([[2, [0, 0, 1, -1, 0]~, 3, 1, [0, -80, 
+60, -10, -60; 0, 0, -100, 0, -20; 0, 0, 0, -10, -100; 0, 2, -2, 0, 2; 1, 0, 
+0, -10, 0]], 2]), [[[[1], [1], [1], [Vecsmall([])], 1], [[2], [[1, 0, 0, -1,
+ 0]~], [[1, 0, 0, -1, 0]~], [Vecsmall([])], Mat([0, 0, 0, 1, 0])]], [[2], [-
+1], [Vecsmall([1])]]], [1, 0; 0, 1]], [0, 0, 0; 1, 1, 1]], [[[[2, 0, 1, 0, 0
+; 0, 2, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], Vecsmall([1])
+], [6, [6], [[0, -1, 0, 0, 0]~]], Mat([[2, [1, 2, 1, 0, 0]~, 1, 2, [0, -80, 
+-20, 0, 0; 0, -1, -100, -10, -120; 1, 0, -1, -20, -100; 0, 2, 0, 0, 0; 1, 1,
+ -1, -10, 0]], 1]), [[[[3], [[0, 1, 0, 0, 0]~], [[0, 1, 0, 0, 0]~], [Vecsmal
+l([])], 1]], [[2], [-1], [Vecsmall([1])]]], Mat([-2, -3])], Mat([-3, -1, -3]
+)]]]
+[[[5, 1, [2, 2; 5, 2; 39821, 1; 161141, 1]]], [[]], [], [[10, 2, [2, 6; 5, 4
+; 39821, 2; 161141, 2]], []]]
+[[[5, -1, [-1, 1; 2, 2; 5, 2; 39821, 1; 161141, 1]]], [[]], [], [[10, -2, [2
+, 6; 5, 4; 39821, 2; 161141, 2]], []]]
+[[[[matrix(0,2), 5, -1, [-1, 1; 2, 2; 5, 2; 39821, 1; 161141, 1]]], [[Mat([5
+0, 1]), 0, 0, 0]], [], [[Mat([50, 2]), 10, -2, [2, 6; 5, 4; 39821, 2; 161141
+, 2]], [Mat([56, 1]), 0, 0, 0]]]]
+  ***   at top-level: nfinit([y^3+2,[1,x]]
+  ***                 ^--------------------
+  *** nfinit: incorrect type in nfbasic_init (t_VEC).
+  ***   at top-level: nfinit([y^3+2,[1,x,x
+  ***                 ^--------------------
+  *** nfinit: incorrect type in nfbasic_init (t_VEC).
+  ***   at top-level: nfinit([y^3+2,[1,y^5
+  ***                 ^--------------------
+  *** nfinit: incorrect type in nfbasic_init (t_VEC).
+2
+[6416795761]
+  ***   at top-level: nfnewprec(x)
+  ***                 ^------------
+  *** nfnewprec: incorrect type in nfnewprec (t_POL).
+  ***   at top-level: nfnewprec(quadgen(5)
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_QUAD).
+  ***   at top-level: nfnewprec(vector(5))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(6))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(8))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(9))
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+  ***   at top-level: nfnewprec(vector(16)
+  ***                 ^--------------------
+  *** nfnewprec: incorrect type in nfnewprec (t_VEC).
+Total time spent: 1408
diff --git a/src/test/64/nfields b/src/test/64/nfields
new file mode 100644
index 0000000..3bba1dd
--- /dev/null
+++ b/src/test/64/nfields
@@ -0,0 +1,751 @@
+   echo = 1 (on)
+? p2=Pol([1,3021,-786303,-6826636057,-546603588746,3853890514072057]);
+? fa=[11699,6;2392997,2;4987333019653,2];
+? setrand(1);N=10^8;a=matrix(3,5,j,k,vectorv(5,l,random\N));
+? nfpol=x^5-5*x^3+5*x+25;nf=nfinit(nfpol)
+[x^5 - 5*x^3 + 5*x + 25, [1, 2], 595125, 45, [[1, -1.08911514572050482502495
+27946671612684, -2.4285174907194186068992069565359418365, 0.7194669112891317
+8943997506477288225737, -2.5558200350691694950646071159426779972; 1, -0.1383
+8372073406036365047976417441696637 - 0.4918163765776864349975328551474152510
+7*I, 1.9647119211288133163138753392090569931 + 0.809714924188978951282940822
+19556466857*I, -0.072312766896812300380582649294307897074 + 2.19808037538462
+76641195195160383234878*I, -0.98796319352507039803950539735452837193 + 1.570
+1452385894131769052374806001981109*I; 1, 1.682941293594312776162956161507997
+6006 + 2.0500351226010726172974286983598602164*I, -0.75045317576910401286427
+186094108607489 + 1.3101462685358123283560773619310445916*I, -0.787420688747
+75359433940488309213323154 + 2.1336633893126618034168454610457936018*I, 1.26
+58732110596551455718089553258673705 - 2.716479010374315056657802803578983483
+5*I], [1, -1.0891151457205048250249527946671612684, -2.428517490719418606899
+2069565359418365, 0.71946691128913178943997506477288225737, -2.5558200350691
+694950646071159426779972; 1, -0.63020009731174679864801261932183221743, 2.77
+44268453177922675968161614046216617, 2.1257676084878153637389368667440155907
+, 0.58218204506434277886573208324566973897; 1, 0.353432655843626071347053090
+97299828470, 1.1549969969398343650309345170134923246, -2.2703931422814399645
+001021653326313849, -2.5581084321144835749447428779547264828; 1, 3.732976416
+1953853934603848598678578170, 0.55969309276670831549180550098995851667, 1.34
+62427005649082090774405779536603703, -1.450605799314659911085993848253116112
+9; 1, -0.36709382900675984113447253685186261580, -2.060599444304916341220349
+2228721306665, -2.9210840780604153977562503441379268334, 3.98235222143397020
+22296117589048508540], [1, -1, -2, 1, -3; 1, -1, 3, 2, 1; 1, 0, 1, -2, -3; 1
+, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, -10, 20; 0, -5
+, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0, 200, 110, 17
+7; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [63, 3,
+ 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, -14, -9; -9, 1
+6, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65, 118, -116; 53
+, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -34]], [3, 5, 2
+3]], [-2.4285174907194186068992069565359418365, 1.96471192112881331631387533
+92090569931 + 0.80971492418897895128294082219556466857*I, -0.750453175769104
+01286427186094108607489 + 1.3101462685358123283560773619310445916*I], [1, 1/
+15*x^4 - 2/3*x^2 + 1/3*x + 4/3, x, 2/15*x^4 - 1/3*x^2 + 2/3*x - 1/3, -1/15*x
+^4 + 1/3*x^3 + 1/3*x^2 - 4/3*x - 2/3], [1, 0, 3, 1, 10; 0, 0, -2, 1, -5; 0, 
+1, 0, 3, -5; 0, 0, 1, 1, 10; 0, 0, 0, 3, 0], [1, 0, 0, 0, 0, 0, -1, -1, -2, 
+4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 1, 0, 0, 0, 1, 1,
+ -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3, -3; 0, 0, 1, 0, 
+0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, -2, -1, -1; 0, 0,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0, 2, 0, -1; 0, 0,
+ 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0, 1, 1, 0, 0, -1]]
+? nfinit(nfpol,2)
+[x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*x + 2, [1, 2], 595125, 4, [[1, -1.089115145
+7205048250249527946671612684, -2.4285174907194186068992069565359418365, 0.71
+946691128913178943997506477288225735, -2.55582003506916949506460711594267799
+71; 1, -0.13838372073406036365047976417441696637 + 0.49181637657768643499753
+285514741525107*I, 1.9647119211288133163138753392090569931 - 0.8097149241889
+7895128294082219556466857*I, -0.072312766896812300380582649294307897123 - 2.
+1980803753846276641195195160383234878*I, -0.98796319352507039803950539735452
+837195 - 1.5701452385894131769052374806001981109*I; 1, 1.6829412935943127761
+629561615079976006 + 2.0500351226010726172974286983598602164*I, -0.750453175
+76910401286427186094108607490 + 1.3101462685358123283560773619310445915*I, -
+0.78742068874775359433940488309213323160 + 2.1336633893126618034168454610457
+936016*I, 1.2658732110596551455718089553258673704 - 2.7164790103743150566578
+028035789834836*I], [1, -1.0891151457205048250249527946671612684, -2.4285174
+907194186068992069565359418365, 0.71946691128913178943997506477288225735, -2
+.5558200350691694950646071159426779971; 1, 0.3534326558436260713470530909729
+9828470, 1.1549969969398343650309345170134923246, -2.27039314228143996450010
+21653326313849, -2.5581084321144835749447428779547264828; 1, -0.630200097311
+74679864801261932183221744, 2.7744268453177922675968161614046216617, 2.12576
+76084878153637389368667440155906, 0.58218204506434277886573208324566973893; 
+1, 3.7329764161953853934603848598678578170, 0.559693092766708315491805500989
+95851657, 1.3462427005649082090774405779536603700, -1.4506057993146599110859
+938482531161132; 1, -0.36709382900675984113447253685186261580, -2.0605994443
+049163412203492228721306664, -2.9210840780604153977562503441379268332, 3.982
+3522214339702022296117589048508541], [1, -1, -2, 1, -3; 1, 0, 1, -2, -3; 1, 
+-1, 3, 2, 1; 1, 4, 1, 1, -1; 1, 0, -2, -3, 4], [5, 2, 0, -1, -2; 2, -2, -5, 
+-10, 20; 0, -5, 10, -10, 5; -1, -10, -10, -17, 1; -2, 20, 5, 1, -8], [345, 0
+, 200, 110, 177; 0, 345, 95, 1, 145; 0, 0, 5, 4, 3; 0, 0, 0, 1, 0; 0, 0, 0, 
+0, 1], [63, 3, 0, -6, -9; 3, 8, -5, -1, 16; 0, -5, 22, -10, 0; -6, -1, -10, 
+-14, -9; -9, 16, 0, -9, -2], [345, [138, 117, 330, 288, -636; -172, -88, 65,
+ 118, -116; 53, 1, 138, -173, 65; 1, -172, 54, 191, 106; 0, 118, 173, 225, -
+34]], [3, 5, 23]], [-1.0891151457205048250249527946671612684, -0.13838372073
+406036365047976417441696637 + 0.49181637657768643499753285514741525107*I, 1.
+6829412935943127761629561615079976006 + 2.0500351226010726172974286983598602
+164*I], [1, x, -1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, -1/2*x^4 + x^3 - x^2 
+- 9/2*x - 1, -1/2*x^4 + x^3 - 2*x^2 - 7/2*x - 2], [1, 0, -1, -7, -14; 0, 1, 
+1, -2, -15; 0, 0, 0, 2, 4; 0, 0, 1, 1, -2; 0, 0, -1, -3, -4], [1, 0, 0, 0, 0
+, 0, -1, -1, -2, 4, 0, -1, 3, -1, 1, 0, -2, -1, -3, -1, 0, 4, 1, -1, -1; 0, 
+1, 0, 0, 0, 1, 1, -1, -1, 1, 0, -1, -2, -1, 1, 0, -1, -1, -1, 3, 0, 1, 1, 3,
+ -3; 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 1, 0, -1, -1, 0, -1, 
+-2, -1, -1; 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 1, 0, 0, 0, 0
+, 2, 0, -1; 0, 0, 0, 0, 1, 0, -1, -1, -1, 1, 0, -1, 0, 1, 0, 0, -1, 1, 0, 0,
+ 1, 1, 0, 0, -1]]
+? nfinit(nfpol,3)[2]
+Mod(-1/2*x^4 + 3/2*x^3 - 5/2*x^2 - 2*x + 1, x^5 - 2*x^4 + 3*x^3 + 8*x^2 + 3*
+x + 2)
+? nf3=nfinit(x^6+108);
+? setrand(1);bnf2=bnfinit(y^3-y-1);nf2=bnf2[7];
+? setrand(1);bnf=bnfinit(x^2-x-57,,[0.2,0.2])
+[Mat(3), Mat([1, 1, 2]), [-2.7124653051843439746808795106061300699 + 3.14159
+26535897932384626433832795028842*I; 2.7124653051843439746808795106061300699]
+, [1.7903417566977293763292119206302198761, 1.289761953065273502503008607239
+5031018 + 3.1415926535897932384626433832795028842*I, 0.E-37, 0.5005798036324
+5587382620331339071677436; -1.7903417566977293763292119206302198761, -1.2897
+619530652735025030086072395031018, 0.E-38, -0.500579803632455873826203313390
+71677436 + 3.1415926535897932384626433832795028842*I], [[3, [-1, 1]~, 1, 1, 
+[0, 57; 1, 1]], [5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], [3, [0, 1]~, 1, 1, [-1, 
+57; 1, 0]], [5, [1, 1]~, 1, 1, [-2, 57; 1, -1]]], 0, [x^2 - x - 57, [2, 0], 
+229, 1, [[1, -7.0663729752107779635959310246705326059; 1, 8.0663729752107779
+635959310246705326058], [1, -7.0663729752107779635959310246705326059; 1, 8.0
+663729752107779635959310246705326058], [1, -7; 1, 8], [2, 1; 1, 115], [229, 
+114; 0, 1], [115, -1; -1, 2], [229, [114, 57; 1, 115]], [229]], [-7.06637297
+52107779635959310246705326059, 8.0663729752107779635959310246705326058], [1,
+ x], [1, 0; 0, 1], [1, 0, 0, 57; 0, 1, 1, 1]], [[3, [3], [[3, 2; 0, 1]]], 2.
+7124653051843439746808795106061300699, 1, [2, -1], [x + 7]], [Mat(1), [[0, 0
+]], [[1.7903417566977293763292119206302198761, -1.79034175669772937632921192
+06302198761]]], [0, 0]]
+? dobnf(x^2-x-100000,1)
+[[5], [Mod(379554884019013781006303254896369154068336082609238336*x + 119836
+165644250789990462835950022871665178127611316131167, x^2 - x - 100000)]]
+? \p19
+   realprecision = 19 significant digits
+? setrand(1);sbnf=bnfcompress(bnfinit(x^3-x^2-14*x-1))
+[x^3 - x^2 - 14*x - 1, 3, 10889, [1, x, x^2 - x - 9], [-3.233732695981516673
+, -0.07182350902743636345, 4.305556205008953036], 0, Mat(2), Mat([1, 1, 0, 1
+, 0, 1]), [9, 15, 16, 17, 39, 33, 10], [2, -1], [[0, 1, 0]~, [5, 3, 1]~], [[
+[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0
+]~, [-3, 0, 0]~], 0]]
+? bnfinit(sbnf)
+[Mat(2), Mat([1, 1, 0, 1, 0, 1]), [1.173637103435061715 + 3.1415926535897932
+38*I, -4.562279014988837902 + 3.141592653589793238*I; -2.633543432738976050 
++ 3.141592653589793238*I, 1.420330600779487358 + 3.141592653589793238*I; 1.4
+59906329303914335, 3.141948414209350544], [1.246346989334819161, 0.540400637
+6129469728 + 3.141592653589793238*I, -0.6926391142471042845 + 3.141592653589
+793238*I, -1.990056445584799714 + 3.141592653589793238*I, -0.830562594660718
+8640, 0.004375616572659815402, 0; 0.6716827432867392936, -0.8333219883742404
+172 + 3.141592653589793238*I, -0.2461086674077943078, 0.5379005671092853267,
+ -1.552661549868775854 + 3.141592653589793238*I, -0.8738318043071131265 + 3.
+141592653589793238*I, 0; -1.918029732621558455 + 3.141592653589793238*I, 0.2
+929213507612934444, 0.9387477816548985923, 1.452155878475514387, 2.383224144
+529494718 + 3.141592653589793238*I, 0.8694561877344533111 + 3.14159265358979
+3238*I, 0], [[3, [-1, 1, 0]~, 1, 1, [1, 10, 45; 1, 7, 6; 1, 1, -3]], [5, [-1
+, 1, 0]~, 1, 1, [0, 10, 45; 1, 6, 6; 1, 1, -4]], [5, [2, 1, 0]~, 1, 1, [1, -
+17, 42; -2, 4, -9; 1, -2, -3]], [5, [3, 1, 0]~, 1, 1, [2, 19, 46; 2, 9, 11; 
+1, 2, -2]], [13, [19, 1, 0]~, 1, 1, [-2, -53, 38; -6, -3, -29; 1, -6, -6]], 
+[11, [1, 1, 0]~, 1, 1, [-3, -8, 43; -1, 1, -4; 1, -1, -7]], [3, [10, 1, 1]~,
+ 1, 2, [-1, 9, 1; 1, 0, 5; 0, 1, -1]]]~, 0, [x^3 - x^2 - 14*x - 1, [3, 0], 1
+0889, 1, [[1, -3.233732695981516673, 4.690759845041404812; 1, -0.07182350902
+743636345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144
+592], [1, -3.233732695981516673, 4.690759845041404812; 1, -0.071823509027436
+36345, -8.923017874523549404; 1, 4.305556205008953036, 5.232258029482144592]
+, [1, -3, 5; 1, 0, -9; 1, 4, 5], [3, 1, 1; 1, 29, 8; 1, 8, 129], [10889, 569
+8, 8994; 0, 1, 0; 0, 0, 1], [3677, -121, -21; -121, 386, -23; -21, -23, 86],
+ [10889, [1899, 46720, 5235; 5191, 7095, 25956; 1, 5191, 1895]], []], [-3.23
+3732695981516673, -0.07182350902743636345, 4.305556205008953036], [1, x, x^2
+ - x - 9], [1, 0, 9; 0, 1, 1; 0, 0, 1], [1, 0, 0, 0, 9, 1, 0, 1, 44; 0, 1, 0
+, 1, 1, 5, 0, 5, 1; 0, 0, 1, 0, 1, 0, 1, 0, -4]], [[2, [2], [[3, 2, 0; 0, 1,
+ 0; 0, 0, 1]]], 10.34800724602767998, 1, [2, -1], [x, x^2 + 2*x - 4]], [Mat(
+1), [[0, 0, 0]], [[1.246346989334819161, 0.6716827432867392936, -1.918029732
+621558455 + 3.141592653589793238*I]]], [[[4, -1, 0]~, [-1, 1, 0]~, [2, 1, 0]
+~, [3, 1, 0]~, [-10, -5, -1]~, [-1, -1, 0]~, [-3, 0, 0]~], 0]]
+? \p38
+   realprecision = 38 significant digits
+? bnr=bnrinit(bnf,[[5,3;0,1],[1,0]],1);bnr.cyc
+[12]
+? bnr2=bnrinit(bnf,[[25,13;0,1],[1,1]],1);bnr2.bid
+[[[25, 13; 0, 1], [1, 1]], [80, [20, 2, 2], [2, [-24, 0]~, [2, 2]~]], Mat([[
+5, [-2, 1]~, 1, 1, [1, 57; 1, 2]], 2]), [[[[4], [2], [2], [Vecsmall([0, 0])]
+, 1], [[5], [6], [6], [Vecsmall([0, 0])], Mat([1/5, -13/5])]], [[2, 2], [-24
+, [2, 2]~], [Vecsmall([0, 1]), Vecsmall([1, 1])]]], [1, -12, 0, 0; 0, 0, 1, 
+0; 0, 0, 0, 1]]
+? rnfinit(nf2,x^5-x-2)
+[x^5 - x - 2, [[83718587879473471, -18162091535584830*x^14 + 659399873895590
+0*x^13 + 89125883511340690*x^12 - 123429972713895380*x^11 - 8618468612826159
+0*x^10 + 508290939376248430*x^9 - 88425050961683595*x^8 - 806556841120532680
+*x^7 - 2575481228604156570*x^6 + 2756771576006241774*x^5 - 28977279276236285
+95*x^4 + 4379071886234238350*x^3 - 4957913590225421420*x^2 - 981408476020699
+484*x + 24006278056864075, 39516536165538345*x^14 - 6500512476832995*x^13 - 
+196215472046117185*x^12 + 229902227480108910*x^11 + 237380704030959181*x^10 
+- 1064931988160773805*x^9 - 20657086671714300*x^8 + 1772885205999206010*x^7 
++ 5952033217241102348*x^6 - 4838840187320655696*x^5 + 5180390720553188700*x^
+4 - 8374015687535120430*x^3 + 8907744727915040221*x^2 + 4155976664123434381*
+x + 318920215718580450], 1/83718587879473471], [[49744, 0, 0; 0, 49744, 0; 0
+, 0, 49744], 3109], 1, [], [], [[1, x, x^2, x^3, x^4], [1, 1, 1, 1, 1]], [1,
+ 0, 0, 0, 0; 0, 1, 0, 0, 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], []
+, [y^3 - y - 1, [1, 1], -23, 1, [[1, 0.7548776662466927600495088963585286919
+0, 1.3247179572447460259609088544780973407; 1, -0.87743883312334638002475444
+817926434595 - 0.74486176661974423659317042860439236724*I, -0.66235897862237
+301298045442723904867037 + 0.56227951206230124389918214490937306150*I], [1, 
+0.75487766624669276004950889635852869190, 1.32471795724474602596090885447809
+73407; 1, -1.6223005997430906166179248767836567132, -0.100079466560071769081
+27228232967560887; 1, -0.13257706650360214343158401957487197871, -1.22463849
+06846742568796365721484217319], [1, 1, 1; 1, -2, 0; 1, 0, -1], [3, -1, 0; -1
+, 1, 3; 0, 3, 2], [23, 16, 13; 0, 1, 0; 0, 0, 1], [7, -2, 3; -2, -6, 9; 3, 9
+, -2], [23, [10, 1, 8; 7, 3, 1; 1, 7, 10]], [23]], [1.3247179572447460259609
+088544780973407, -0.66235897862237301298045442723904867037 + 0.5622795120623
+0124389918214490937306150*I], [1, y^2 - 1, y], [1, 0, 1; 0, 0, 1; 0, 1, 0], 
+[1, 0, 0, 0, 0, 1, 0, 1, 1; 0, 1, 0, 1, -1, 0, 0, 0, 1; 0, 0, 1, 0, 1, 0, 1,
+ 0, 0]], [x^15 - 5*x^13 + 5*x^12 + 7*x^11 - 26*x^10 - 5*x^9 + 45*x^8 + 158*x
+^7 - 98*x^6 + 110*x^5 - 190*x^4 + 189*x^3 + 144*x^2 + 25*x + 1, 395165361655
+38345/83718587879473471*x^14 - 6500512476832995/83718587879473471*x^13 - 196
+215472046117185/83718587879473471*x^12 + 229902227480108910/8371858787947347
+1*x^11 + 237380704030959181/83718587879473471*x^10 - 1064931988160773805/837
+18587879473471*x^9 - 20657086671714300/83718587879473471*x^8 + 1772885205999
+206010/83718587879473471*x^7 + 5952033217241102348/83718587879473471*x^6 - 4
+838840187320655696/83718587879473471*x^5 + 5180390720553188700/8371858787947
+3471*x^4 - 8374015687535120430/83718587879473471*x^3 + 8907744727915040221/8
+3718587879473471*x^2 + 4155976664123434381/83718587879473471*x + 31892021571
+8580450/83718587879473471, -1, y^3 - y - 1, x^5 - x - 2], [0, 0]]
+? bnfcertify(bnf)
+1
+? dobnf(x^4+24*x^2+585*x+1791,,[0.1,0.1])
+[[4], [Mod(1/343*x^3 - 46/1029*x^2 - 122/343*x - 174/343, x^4 + 24*x^2 + 585
+*x + 1791)]]
+? bnrconductor(bnf,[[25,13;0,1],[1,1]])
+[[5, 3; 0, 1], [1, 0]]
+? bnrconductorofchar(bnr,[2])
+[[5, 3; 0, 1], [0, 0]]
+? bnfisprincipal(bnf,[5,1;0,1],0)
+[1]~
+? bnfisprincipal(bnf,[5,1;0,1])
+[[1]~, [-2, -1/3]~]
+? bnfisunit(bnf,Mod(3405*x-27466,x^2-x-57))
+[-4, Mod(1, 2)]~
+? bnfnarrow(bnf)
+[3, [3], [[3, 2; 0, 1]]]
+? bnfsignunit(bnf)
+
+[-1]
+
+[ 1]
+
+? bnrclassno(bnf,[[5,3;0,1],[1,0]])
+12
+? lu=ideallist(bnf,55,3);
+? bnrclassnolist(bnf,lu)
+[[3], [], [3, 3], [3], [6, 6], [], [], [], [3, 3, 3], [], [3, 3], [3, 3], []
+, [], [12, 6, 6, 12], [3], [3, 3], [], [9, 9], [6, 6], [], [], [], [], [6, 1
+2, 6], [], [3, 3, 3, 3], [], [], [], [], [], [3, 6, 6, 3], [], [], [9, 3, 9]
+, [6, 6], [], [], [], [], [], [3, 3], [3, 3], [12, 12, 6, 6, 12, 12], [], []
+, [6, 6], [9], [], [3, 3, 3, 3], [], [3, 3], [], [6, 12, 12, 6]]
+? bnrdisc(bnr,Mat(6))
+[12, 12, 18026977100265125]
+? bnrdisc(bnr)
+[24, 12, 40621487921685401825918161408203125]
+? bnrdisc(bnr2,,,2)
+0
+? bnrdisc(bnr,Mat(6),,1)
+[6, 2, [125, 13; 0, 1]]
+? bnrdisc(bnr,,,1)
+[12, 1, [1953125, 1160888; 0, 1]]
+? bnrdisc(bnr2,,,3)
+0
+? bnrdisclist(bnf,lu)
+[[[6, 6, Mat([229, 3])]], [], [[], []], [[]], [[12, 12, [5, 3; 229, 6]], [12
+, 12, [5, 3; 229, 6]]], [], [], [], [[], [], []], [], [[], []], [[], []], []
+, [], [[24, 24, [3, 6; 5, 9; 229, 12]], [], [], [24, 24, [3, 6; 5, 9; 229, 1
+2]]], [[]], [[], []], [], [[18, 18, [19, 6; 229, 9]], [18, 18, [19, 6; 229, 
+9]]], [[], []], [], [], [], [], [[], [24, 24, [5, 12; 229, 12]], []], [], [[
+], [], [], []], [], [], [], [], [], [[], [12, 12, [3, 3; 11, 3; 229, 6]], [1
+2, 12, [3, 3; 11, 3; 229, 6]], []], [], [], [[18, 18, [2, 12; 3, 12; 229, 9]
+], [], [18, 18, [2, 12; 3, 12; 229, 9]]], [[12, 12, [37, 3; 229, 6]], [12, 1
+2, [37, 3; 229, 6]]], [], [], [], [], [], [[], []], [[], []], [[], [], [], [
+], [], []], [], [], [[12, 12, [2, 12; 3, 3; 229, 6]], [12, 12, [2, 12; 3, 3;
+ 229, 6]]], [[18, 18, [7, 12; 229, 9]]], [], [[], [], [], []], [], [[], []],
+ [], [[], [24, 24, [5, 9; 11, 6; 229, 12]], [24, 24, [5, 9; 11, 6; 229, 12]]
+, []]]
+? bnrdisclist(bnf,20)
+[[[[matrix(0,2), [[6, 6, Mat([229, 3])], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]],
+ [], [[Mat([12, 1]), [[0, 0, 0], [0, 0, 0], [0, 0, 0], [12, 0, [3, 3; 229, 6
+]]]], [Mat([13, 1]), [[0, 0, 0], [12, 6, [-1, 1; 3, 3; 229, 6]], [0, 0, 0], 
+[0, 0, 0]]]], [[Mat([10, 1]), [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]]
+, [[Mat([20, 1]), [[12, 12, [5, 3; 229, 6]], [0, 0, 0], [0, 0, 0], [24, 0, [
+5, 9; 229, 12]]]], [Mat([21, 1]), [[12, 12, [5, 3; 229, 6]], [24, 12, [5, 9;
+ 229, 12]], [0, 0, 0], [0, 0, 0]]]], [], [], [], [[Mat([12, 2]), [[0, 0, 0],
+ [0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[12, 1; 13, 1], [[0, 0, 0], [0, 0, 0], 
+[12, 6, [-1, 1; 3, 6; 229, 6]], [24, 0, [3, 12; 229, 12]]]], [Mat([13, 2]), 
+[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]], [], [[Mat([44, 1]), [[0, 0, 
+0], [12, 6, [-1, 1; 11, 3; 229, 6]], [0, 0, 0], [0, 0, 0]]], [Mat([45, 1]), 
+[[0, 0, 0], [0, 0, 0], [0, 0, 0], [12, 0, [11, 3; 229, 6]]]]], [[[10, 1; 12,
+ 1], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]], [[10, 1; 13, 1], [[0, 0,
+ 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]], [], [], [[[12, 1; 20, 1], [[24, 24,
+ [3, 6; 5, 9; 229, 12]], [0, 0, 0], [0, 0, 0], [48, 0, [3, 12; 5, 18; 229, 2
+4]]]], [[13, 1; 20, 1], [[0, 0, 0], [24, 12, [3, 6; 5, 6; 229, 12]], [24, 12
+, [3, 6; 5, 9; 229, 12]], [48, 0, [3, 12; 5, 18; 229, 24]]]], [[12, 1; 21, 1
+], [[0, 0, 0], [0, 0, 0], [24, 12, [3, 6; 5, 9; 229, 12]], [48, 0, [3, 12; 5
+, 18; 229, 24]]]], [[13, 1; 21, 1], [[24, 24, [3, 6; 5, 9; 229, 12]], [48, 2
+4, [3, 12; 5, 18; 229, 24]], [0, 0, 0], [0, 0, 0]]]], [[Mat([10, 2]), [[0, 0
+, 0], [12, 6, [-1, 1; 2, 12; 229, 6]], [12, 6, [-1, 1; 2, 12; 229, 6]], [24,
+ 0, [2, 36; 229, 12]]]]], [[Mat([68, 1]), [[0, 0, 0], [0, 0, 0], [12, 6, [-1
+, 1; 17, 3; 229, 6]], [0, 0, 0]]], [Mat([69, 1]), [[0, 0, 0], [0, 0, 0], [12
+, 6, [-1, 1; 17, 3; 229, 6]], [0, 0, 0]]]], [], [[Mat([76, 1]), [[18, 18, [1
+9, 6; 229, 9]], [0, 0, 0], [0, 0, 0], [36, 0, [19, 15; 229, 18]]]], [Mat([77
+, 1]), [[18, 18, [19, 6; 229, 9]], [36, 18, [-1, 1; 19, 15; 229, 18]], [0, 0
+, 0], [0, 0, 0]]]], [[[10, 1; 20, 1], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 
+0, 0]]], [[10, 1; 21, 1], [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]]]]]
+? bnrisprincipal(bnr,idealprimedec(bnf,7)[1])
+[[9]~, [32879/6561, 13958/19683]~]
+? dirzetak(nfinit(x^3-10*x+8),30)
+[1, 2, 0, 3, 1, 0, 0, 4, 0, 2, 1, 0, 0, 0, 0, 5, 1, 0, 0, 3, 0, 2, 0, 0, 2, 
+0, 1, 0, 1, 0]
+? factornf(x^3+x^2-2*x-1,t^3+t^2-2*t-1)
+
+[         x + Mod(-t, t^3 + t^2 - 2*t - 1) 1]
+
+[   x + Mod(-t^2 + 2, t^3 + t^2 - 2*t - 1) 1]
+
+[x + Mod(t^2 + t - 1, t^3 + t^2 - 2*t - 1) 1]
+
+? vp=idealprimedec(nf,3)[1]
+[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0,
+ 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]]
+? idx=idealhnf(nf,vp)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idy=idealred(nf,idx,[1,5,6])
+
+[5 0 0 0 2]
+
+[0 5 0 0 2]
+
+[0 0 5 0 1]
+
+[0 0 0 5 2]
+
+[0 0 0 0 1]
+
+? idx2=idealmul(nf,idx,idx)
+
+[9 5 7 0 4]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idt=idealmul(nf,idx,idx,1)
+
+[2 0 0 0 0]
+
+[0 2 0 0 0]
+
+[0 0 2 0 0]
+
+[0 0 0 2 1]
+
+[0 0 0 0 1]
+
+? idz=idealintersect(nf,idx,idy)
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? aid=[idx,idy,idz,1,idx];
+? idealadd(nf,idx,idy)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealaddtoone(nf,idx,idy)
+[[0, -1, -3, -1, 2]~, [1, 1, 3, 1, -2]~]
+? idealaddtoone(nf,[idy,idx])
+[[-5, 0, 0, 0, 0]~, [6, 0, 0, 0, 0]~]
+? idealappr(nf,idy)
+[-1, 4, 2, -1, -3]~
+? idealappr(nf,idealfactor(nf,idy),1)
+[-1, 4, 2, -1, -3]~
+? idealcoprime(nf,idx,idx)
+[-1/3, 1/3, 1/3, 1/3, 0]~
+? idealdiv(nf,idy,idt)
+
+[5   0 5/2   0   1]
+
+[0 5/2   0   0   1]
+
+[0   0 5/2   0 1/2]
+
+[0   0   0 5/2   1]
+
+[0   0   0   0 1/2]
+
+? idealdiv(nf,idx2,idx,1)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealfactor(nf,idz)
+
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]] 1]
+
+[[5, [-1, 0, 0, 0, 2]~, 4, 1, [2, -3, 0, -12, 6; 2, 2, -5, -2, 6; 1, 1, 0, -
+1, -7; 2, 2, 5, 3, 1; 1, -4, 0, -1, 3]] 3]
+
+[[5, [2, 0, 0, 0, -2]~, 1, 1, [2, 1, 10, -4, 2; 0, 0, -5, 0, 0; 3, -1, 0, -1
+, -7; 0, 0, 5, 5, 5; 1, -2, 0, 3, 1]] 1]
+
+? idealhnf(nf,vp[2],3)
+
+[3 2 1 0 1]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? ideallist(bnf,20)
+[[[1, 0; 0, 1]], [], [[3, 2; 0, 1], [3, 0; 0, 1]], [[2, 0; 0, 2]], [[5, 3; 0
+, 1], [5, 1; 0, 1]], [], [], [], [[9, 5; 0, 1], [3, 0; 0, 3], [9, 3; 0, 1]],
+ [], [[11, 9; 0, 1], [11, 1; 0, 1]], [[6, 4; 0, 2], [6, 0; 0, 2]], [], [], [
+[15, 8; 0, 1], [15, 3; 0, 1], [15, 11; 0, 1], [15, 6; 0, 1]], [[4, 0; 0, 4]]
+, [[17, 14; 0, 1], [17, 2; 0, 1]], [], [[19, 18; 0, 1], [19, 0; 0, 1]], [[10
+, 6; 0, 2], [10, 2; 0, 2]]]
+? bid=idealstar(nf2,54)
+[[[54, 0, 0; 0, 54, 0; 0, 0, 54], [0]], [132678, [1638, 9, 9]], [[2, [2, 0, 
+0]~, 1, 3, 1], 1; [3, [3, 0, 0]~, 1, 3, 1], 3], [[[[7], [[2, 1, 1]~], [[-26,
+ -27, -27]~], [Vecsmall([])], 1]], [[[26], [[4, 2, 1]~], [[-23, 2, -26]~], [
+Vecsmall([])], 1], [[3, 3, 3], [4, [1, 3, 0]~, [1, 0, 3]~], [-77, [1, -24, 0
+]~, [1, 0, -24]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/3, 0, 0; 0
+, 1/3, 0; 0, 0, 1/3]], [[3, 3, 3], [10, [1, 9, 0]~, [1, 0, 9]~], [-233, [1, 
+-18, 0]~, [1, 0, -18]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/9, 0
+, 0; 0, 1/9, 0; 0, 0, 1/9]]], [[], [], []]], [468, -77, 0, 728, -1456, 0, 54
+6, -1092; 0, 0, 1, 0, -1, -6, 0, -3; 0, 1, 0, -1, 1, 0, -3, 3]]
+? ideallog(nf2,y,bid)
+[284, 1, 1]~
+? idealmin(nf,idx,[1,2,3])
+[1, 0, 1, 0, 0]~
+? idealnorm(nf,idt)
+16
+? idp=idealpow(nf,idx,7)
+
+[2187 1436 1807 630 1822]
+
+[   0    1    0   0    0]
+
+[   0    0    1   0    0]
+
+[   0    0    0   1    0]
+
+[   0    0    0   0    1]
+
+? idealpow(nf,idx,7,1)
+
+[1 0 0 0 0]
+
+[0 1 0 0 0]
+
+[0 0 1 0 0]
+
+[0 0 0 1 0]
+
+[0 0 0 0 1]
+
+? idealprimedec(nf,2)
+[[2, [3, 0, 1, 0, 0]~, 1, 1, [0, 2, 0, -4, -2; 0, 0, 0, 2, 0; 0, 0, -2, -2, 
+-2; 1, 0, 3, 0, -1; 1, 0, 1, 0, -1]], [2, [12, -4, -2, 11, 3]~, 1, 4, [1, -1
+, 3, -1, 1; 0, 0, -2, -1, 1; 1, 0, 1, 0, -2; 0, 0, 1, 2, 2; 0, -1, 0, 1, 1]]
+]
+? idealprimedec(nf,3)
+[[3, [1, 0, 1, 0, 0]~, 1, 1, [1, 4, -1, 6, -4; -1, 2, 4, 3, -5; -1, -1, 1, 0
+, 4; -1, -1, -2, 0, -2; 0, 3, 0, 0, 0]], [3, [1, 1, 1, 0, 0]~, 2, 2, [0, -6,
+ 3, -9, 9; 2, -1, -7, -5, 7; 2, 1, 0, 1, -7; 1, 2, 3, 2, 4; 0, -5, -1, 0, 2]
+]]
+? idealprimedec(nf,11)
+[[11, [11, 0, 0, 0, 0]~, 1, 5, 1]]
+? idealtwoelt(nf,idy)
+[5, [2, 2, 1, 2, 1]~]
+? idealtwoelt(nf,idy,10)
+[-1, 4, 2, 4, 2]~
+? idealstar(nf2,54)
+[[[54, 0, 0; 0, 54, 0; 0, 0, 54], [0]], [132678, [1638, 9, 9]], [[2, [2, 0, 
+0]~, 1, 3, 1], 1; [3, [3, 0, 0]~, 1, 3, 1], 3], [[[[7], [[0, 0, 1]~], [[-26,
+ 0, -27]~], [Vecsmall([])], 1]], [[[26], [[0, 0, 2]~], [[-27, 0, 2]~], [Vecs
+mall([])], 1], [[3, 3, 3], [4, [1, 3, 0]~, [1, 0, 3]~], [-77, [1, -24, 0]~, 
+[1, 0, -24]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/3, 0, 0; 0, 1/
+3, 0; 0, 0, 1/3]], [[3, 3, 3], [10, [1, 9, 0]~, [1, 0, 9]~], [-233, [1, -18,
+ 0]~, [1, 0, -18]~], [Vecsmall([]), Vecsmall([]), Vecsmall([])], [1/9, 0, 0;
+ 0, 1/9, 0; 0, 0, 1/9]]], [[], [], []]], [468, 469, 0, 0, -182, 0, 0, -546; 
+0, 0, 1, 0, -7, -6, 0, -3; 0, 0, 0, 1, -3, 0, -6, 0]]
+? idealval(nf,idp,vp)
+7
+? ba=nfalgtobasis(nf,x^3+5)
+[6, 1, 3, 1, 3]~
+? bb=nfalgtobasis(nf,x^3+x)
+[1, 1, 4, 1, 3]~
+? bc=matalgtobasis(nf,[x^2+x;x^2+1])
+
+[[3, -2, 1, 1, 0]~]
+
+[[4, -2, 0, 1, 0]~]
+
+? matbasistoalg(nf,bc)
+
+[Mod(x^2 + x, x^5 - 5*x^3 + 5*x + 25)]
+
+[Mod(x^2 + 1, x^5 - 5*x^3 + 5*x + 25)]
+
+? nfbasis(x^3+4*x+5)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? nfbasis(x^3+4*x+5,2)
+[1, x, 1/7*x^2 - 1/7*x - 2/7]
+? nfbasis(x^3+4*x+12,1)
+[1, x, 1/2*x^2]
+? nfbasistoalg(nf,ba)
+Mod(x^3 + 5, x^5 - 5*x^3 + 5*x + 25)
+? nfbasis(p2,0,fa)
+[1, x, x^2, 1/11699*x^3 + 1847/11699*x^2 - 132/11699*x - 2641/11699, 1/13962
+3738889203638909659*x^4 - 1552451622081122020/139623738889203638909659*x^3 +
+ 418509858130821123141/139623738889203638909659*x^2 - 6810913798507599407313
+4/139623738889203638909659*x - 13185339461968406/58346808996920447]
+? nfdisc(x^3+4*x+12)
+-1036
+? nfdisc(x^3+4*x+12,1)
+-1036
+? nfdisc(p2,0,fa)
+136866601
+? nfeltdiv(nf,ba,bb)
+[584/373, 66/373, -32/373, -105/373, 120/373]~
+? nfeltdiveuc(nf,ba,bb)
+[2, 0, 0, 0, 0]~
+? nfeltdivrem(nf,ba,bb)
+[[2, 0, 0, 0, 0]~, [4, -1, -5, -1, -3]~]
+? nfeltmod(nf,ba,bb)
+[4, -1, -5, -1, -3]~
+? nfeltmul(nf,ba,bb)
+[50, -15, -35, 60, 15]~
+? nfeltpow(nf,bb,5)
+[-291920, 136855, 230560, -178520, 74190]~
+? nfeltreduce(nf,ba,idx)
+[1, 0, 0, 0, 0]~
+? nfeltval(nf,ba,vp)
+0
+? nffactor(nf2,x^3+x)
+
+[      x 1]
+
+[x^2 + 1 1]
+
+? aut=nfgaloisconj(nf3)
+[-x, x, -1/12*x^4 - 1/2*x, -1/12*x^4 + 1/2*x, 1/12*x^4 - 1/2*x, 1/12*x^4 + 1
+/2*x]~
+? nfgaloisapply(nf3,aut[5],Mod(x^5,x^6+108))
+Mod(-1/2*x^5 + 9*x^2, x^6 + 108)
+? nfhilbert(nf,3,5)
+-1
+? nfhilbert(nf,3,5,vp)
+-1
+? nfhnf(nf,[a,aid])
+[[1, 1, 4; 0, 1, 0; 0, 0, 1], [[15, 2, 10, 12, 4; 0, 1, 0, 0, 0; 0, 0, 1, 0,
+ 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? da=nfdetint(nf,[a,aid])
+
+[15 10 5 0 12]
+
+[ 0  5 0 0  2]
+
+[ 0  0 5 0  1]
+
+[ 0  0 0 5  2]
+
+[ 0  0 0 0  1]
+
+? nfhnfmod(nf,[a,aid],da)
+[[1, 1, 4; 0, 1, 0; 0, 0, 1], [[15, 2, 10, 12, 4; 0, 1, 0, 0, 0; 0, 0, 1, 0,
+ 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], 1, 1]]
+? nfisideal(bnf[7],[5,1;0,1])
+1
+? nfisincl(x^2+1,x^4+1)
+[-x^2, x^2]
+? nfisincl(x^2+1,nfinit(x^4+1))
+[-x^2, x^2]
+? nfisisom(x^3+x^2-2*x-1,x^3+x^2-2*x-1)
+[x, -x^2 - x + 1, x^2 - 2]
+? nfisisom(x^3-2,nfinit(x^3-6*x^2-6*x-30))
+[-1/25*x^2 + 13/25*x - 2/5]
+? nfroots(nf2,x+2)
+[Mod(-2, y^3 - y - 1)]
+? nfrootsof1(nf)
+[2, -1]
+? nfsnf(nf,[a[,1..3],[1,1,1],[idealinv(nf,idx),idealinv(nf,idy),1]])
+[[15706993357777254170417850, 1636878763571210697462070, 1307908830618593502
+9427775, 1815705333955314515809980, 7581330311082212790621785; 0, 5, 0, 0, 0
+; 0, 0, 5, 0, 0; 0, 0, 0, 5, 0; 0, 0, 0, 0, 5], [1, 0, 0, 0, 0; 0, 1, 0, 0, 
+0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1], [1, 0, 0, 0, 0; 0, 1, 0, 0,
+ 0; 0, 0, 1, 0, 0; 0, 0, 0, 1, 0; 0, 0, 0, 0, 1]]
+? nfsubfields(nf)
+[[x, 0], [x^5 - 5*x^3 + 5*x + 25, x]]
+? polcompositum(x^4-4*x+2,x^3-x-1)
+[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x
+^2 - 128*x - 5]
+? polcompositum(x^4-4*x+2,x^3-x-1,1)
+[[x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*
+x^2 - 128*x - 5, Mod(-279140305176/29063006931199*x^11 + 129916611552/290630
+06931199*x^10 + 1272919322296/29063006931199*x^9 - 2813750209005/29063006931
+199*x^8 - 2859411937992/29063006931199*x^7 - 414533880536/29063006931199*x^6
+ - 35713977492936/29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 4
+9785595543672/29063006931199*x^3 + 9423768373204/29063006931199*x^2 - 427797
+76146743/29063006931199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8
+*x^9 + 12*x^8 + 12*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), M
+od(-279140305176/29063006931199*x^11 + 129916611552/29063006931199*x^10 + 12
+72919322296/29063006931199*x^9 - 2813750209005/29063006931199*x^8 - 28594119
+37992/29063006931199*x^7 - 414533880536/29063006931199*x^6 - 35713977492936/
+29063006931199*x^5 - 17432607267590/29063006931199*x^4 + 49785595543672/2906
+3006931199*x^3 + 9423768373204/29063006931199*x^2 - 13716769215544/290630069
+31199*x + 37962587857138/29063006931199, x^12 - 4*x^10 + 8*x^9 + 12*x^8 + 12
+*x^7 + 138*x^6 + 132*x^5 - 43*x^4 + 58*x^2 - 128*x - 5), -1]]
+? polgalois(x^6-3*x^2-1)
+[12, 1, 1, "A_4(6) = [2^2]3"]
+? polred(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+[x - 1, x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1]
+? polred(x^4-28*x^3-458*x^2+9156*x-25321,3)
+
+[                                                1           x - 1]
+
+[                   1/115*x^2 - 14/115*x - 212/115   x^2 - 2*x - 9]
+
+[                  -1/115*x^2 + 14/115*x + 442/115   x^2 - 2*x - 9]
+
+[                   1/115*x^2 - 14/115*x - 327/115        x^2 - 10]
+
+[1/4485*x^3 - 7/1495*x^2 - 1034/4485*x + 7924/4485 x^4 - 8*x^2 + 6]
+
+? polred(x^4+576,1)
+[x - 1, x^2 - 3*x + 3, x^2 - 2*x + 2, x^2 - x + 1, x^2 + 1, x^4 - x^2 + 1]
+? polred(x^4+576,3)
+
+[                           1         x - 1]
+
+[    -1/192*x^3 - 1/8*x + 3/2 x^2 - 3*x + 3]
+
+[                1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[               -1/24*x^2 + 1 x^2 - 2*x + 2]
+
+[    -1/192*x^3 - 1/8*x + 1/2   x^2 - x + 1]
+
+[     1/192*x^3 + 1/8*x + 1/2   x^2 - x + 1]
+
+[                    1/24*x^2       x^2 + 1]
+
+[1/192*x^3 + 1/48*x^2 - 1/8*x x^4 - x^2 + 1]
+
+? polred(p2,0,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? polred(p2,1,fa)
+[x - 1, x^5 - 80*x^3 - 223*x^2 + 800*x + 2671]
+? polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1
+? polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568,1)
+[x^5 - x^4 + 2*x^3 - 4*x^2 + x - 1, Mod(2*x^4 - x^3 + 3*x^2 - 3*x - 1, x^5 -
+ x^4 + 2*x^3 - 4*x^2 + x - 1)]
+? polredord(x^3-12*x+45*x-1)
+[x - 1, x^3 + 33*x - 1]
+? polsubcyclo(31,5)
+x^5 + x^4 - 12*x^3 - 21*x^2 + x + 5
+? setrand(1);poltschirnhaus(x^5-x-1)
+x^5 + 10*x^4 + 32*x^3 - 100*x^2 - 879*x - 1457
+? p=x^5-5*x+y;aa=rnfpseudobasis(nf2,p)
+[[1, 0, 0, -2, [3, 1, 0]~; 0, 1, 0, 2, [0, -1, 0]~; 0, 0, 1, 1, [-5, -2, 0]~
+; 0, 0, 0, 1, -2; 0, 0, 0, 0, 1], [1, 1, 1, [1, 0, 2/5; 0, 1, 3/5; 0, 0, 1/5
+], [1, 0, 22/25; 0, 1, 8/25; 0, 0, 1/25]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfbasis(bnf2,aa)
+
+[1 0 0  [-26/25, 11/25, -8/25]~         [0, 4, -7]~]
+
+[0 1 0   [53/25, -8/25, -1/25]~ [6/5, -41/5, 53/5]~]
+
+[0 0 1 [-14/25, -21/25, 13/25]~  [-16/5, 1/5, 7/5]~]
+
+[0 0 0     [7/25, -2/25, 6/25]~  [2/5, -2/5, 11/5]~]
+
+[0 0 0     [9/25, 1/25, -3/25]~   [2/5, -7/5, 6/5]~]
+
+? rnfdisc(nf2,p)
+[[416134375, 202396875, 60056800; 0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? rnfequation(nf2,p)
+x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1
+? rnfequation(nf2,p,1)
+[x^15 - 15*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1, Mod(-x^5 + 5*x, x^15 - 1
+5*x^11 + 75*x^7 - x^5 - 125*x^3 + 5*x + 1), 0]
+? rnfhnfbasis(bnf2,aa)
+
+[1 0 0 [-6/5, -4/5, 2/5]~   [3/25, -8/25, 24/25]~]
+
+[0 1 0  [6/5, 4/5, -2/5]~   [-9/25, -1/25, 3/25]~]
+
+[0 0 1  [3/5, 2/5, -1/5]~ [-8/25, 13/25, -39/25]~]
+
+[0 0 0  [3/5, 2/5, -1/5]~   [4/25, 6/25, -18/25]~]
+
+[0 0 0                  0   [-2/25, -3/25, 9/25]~]
+
+? rnfisfree(bnf2,aa)
+1
+? rnfsteinitz(nf2,aa)
+[[1, 0, 0, [-26/25, 11/25, -8/25]~, [29/125, -2/25, 8/125]~; 0, 1, 0, [53/25
+, -8/25, -1/25]~, [-53/125, 7/125, 1/125]~; 0, 0, 1, [-14/25, -21/25, 13/25]
+~, [9/125, 19/125, -13/125]~; 0, 0, 0, [7/25, -2/25, 6/25]~, [-9/125, 2/125,
+ -6/125]~; 0, 0, 0, [9/25, 1/25, -3/25]~, [-8/125, -1/125, 3/125]~], [1, 1, 
+1, 1, [125, 0, 22; 0, 125, 108; 0, 0, 1]], [416134375, 202396875, 60056800; 
+0, 3125, 2700; 0, 0, 25], [-1275, 5, 5]~]
+? nfz=zetakinit(x^2-2);
+? zetak(nfz,-3)
+0.091666666666666666666666666666666666667
+? zetak(nfz,1.5+3*I)
+0.88324345992059326405525724366416928892 - 0.2067536250233895222724230899142
+7938848*I
+? setrand(1);quadclassunit(1-10^7,,[1,1])
+[2416, [1208, 2], [Qfb(277, 55, 9028), Qfb(1700, 1249, 1700)], 1]
+? setrand(1);quadclassunit(10^9-3,,[0.5,0.5])
+[4, [4], [Qfb(211, 31405, -16263, 0.E-57)], 2800.625251907016076486370621737
+0745514]
+? sizebyte(%)
+288
+? getheap
+[217, 91238]
+? print("Total time spent: ",gettime);
+Total time spent: 64
diff --git a/src/test/64/random b/src/test/64/random
new file mode 100644
index 0000000..71d38ab
--- /dev/null
+++ b/src/test/64/random
@@ -0,0 +1,30 @@
+Mod(2, 3)
+a^2
+[[a, a + 1], 1]
+3223690774*a^2 + 2495927739*a + 1543932468
+[[222673296*a^2 + 832141822*a + 1993872865, 3473855231*a^2 + 2165401445*a + 
+1363496513], 1]
+4063307077606482163*a^2 + 2169871353760194456*a + 10598342506117936052
+[[2557945391375694509*a^2 + 5182580949942002905*a + 4115304207846177355, 808
+4599253441901006*a^2 + 17319169765369271807*a + 5394713598728073384], 1]
+0.56071855301363810851584520787407920871
+Mod(5, 7)*x^3 + Mod(2, 7)*x^2 + Mod(1, 7)*x + Mod(6, 7)
+  ***   at top-level: randomprime(2)
+  ***                 ^--------------
+  *** randomprime: domain error in randomprime: N < 2
+  ***   at top-level: randomprime([0,1])
+  ***                 ^------------------
+  *** randomprime: domain error in randomprime: floor(b) - max(ceil(a),2) < 0
+  ***   at top-level: randomprime([2.5,2.4
+  ***                 ^--------------------
+  *** randomprime: domain error in randomprime: b-a < 0
+  ***   at top-level: randomprime([2.4,2.5
+  ***                 ^--------------------
+  *** randomprime: domain error in randomprime: floor(b) - max(ceil(a),2) < 0
+1051006573
+5
+1267650600228229401496703924683
+  ***   at top-level: random("")
+  ***                 ^----------
+  *** random: incorrect type in genrand (t_STR).
+Total time spent: 4
diff --git a/src/test/64/real b/src/test/64/real
new file mode 100644
index 0000000..a67f7f7
--- /dev/null
+++ b/src/test/64/real
@@ -0,0 +1,3 @@
+5.85492786017126177 E347063955532709820
+1.707962973895205473 E-347063955532709821
+Total time spent: 0
diff --git a/src/test/64/rnfkummer b/src/test/64/rnfkummer
new file mode 100644
index 0000000..9dc8270
--- /dev/null
+++ b/src/test/64/rnfkummer
@@ -0,0 +1,175 @@
+  ***   Warning: new stack size = 20000000 (19.073 Mbytes).
+1
+x^3 + (774806212396682040403800911285727580902360710356054423617*y^3 + 97150
+6686017039131003298525517527246921922994336799568156*y^2 + 11821565693684429
+03321920531164159182095127947357116914844*y - 108151434729309365356471406894
+6874997341102881639770950564)*x + (-1101081691006188641718571980971003086516
+33373991261854661897473896921309979584250845786*y^3 - 1380613897447424014891
+54687510692802021389570171865180798324014776069968837855846155006*y^2 - 1679
+9696925608361694809806777136478453179953197918860855068694876240580160263314
+5257038*y + 1536946435524002857971775575314758204502648805624233135296867674
+44129838100365080190135)
+2
+x^3 + (19710886507130683637499481853346220*y^3 - 276380637484283870500207144
+441368652*y^2 + 321806883259334223558938029085301164*y - 2893764851921508720
+91555008989630209)*x + (6234404200069914092635781978921534907087800821965005
+*y^3 - 87417103564909472118499724747251035776212296637996160*y^2 + 101785081
+248253577808797714025930280347744436431138133*y - 91527591822456102976671051
+455070193926146359966708289)
+3
+x^5 + (6496145841479065310793035362475434921070895525/229919*y^3 - 329356645
+026252920894440103714065134919947272250/229919*y^2 - 48247357455860338551230
+0816299651400343793076825/229919*y + 123543801866424262505457797279174925381
+529732076495/229919)*x^3 + (-71021557287220849179024643469344999388010723208
+10325676573265995217846319711524646891132825/229919*y^3 + 312915518730297348
+797926075100976561711702080179753673084705465541436855444615670623948955500/
+229919*y^2 + 373389088629333541906488751920872126291403867985783777391039415
+1262039050473082240798959765475/229919*y - 186975830481723304596917319076060
+227260827340522313433115743539192208330842442737088985645016230/229919)*x + 
+(285331357696034096491305981627897525039281997301328341344454998449876878331
+78806777454970473740731009132513474900/229919*y^3 - 126322042014283687392680
+0705112647035932847475526596784692041313402766945033286681679632475546355981
+254757708245150/229919*y^2 - 14588214877690614738986561739773261402627551978
+016213443426143272871312909382946730136611425023860571069442516995750/229919
+*y + 74449855196093407639269271676900667861540430004636286318089786062493654
+8665702724402264597499043949735472272479146816/229919)
+4
+x^5 + 110*x^3 + 385*x^2 + (-6875*y + 13310)*x + (-20625*y - 32197)
+5
+x^5 + (590954467855790033665410099423754749157030026264928061890504926528336
+7772275490633617637549548608569718527277116446001864709881325798838750905729
+8030795258827639720428023363397609047544665809630635467286642736338355699480
+*y - 12615386542033504303879787410140086547604440260010207180712781991640642
+3422387588267289431462624116310719550083059856489949563282822945905208680310
+3334992487656834725937011490801952876482416400706750342081349443304520100091
+0)*x^3 + (-11644277774384555651776761259407289108977097641980228242435012611
+5006754047681181999487760953138546648235722021492042022118650990530538252856
+6414026821914198835648283336487147011301797121610470649794178541651897633010
+2619047628371992250175584734031123181309438679458163454790544342192453573731
+030635180622494122125396065225820240750*y + 24857594470800734810760217042859
+3197482585473219918102992436513064533871094674977546265869687753336958987424
+4725574396462863038560477800350177318131266777625357152677200654910979563229
+6935482013428626217783946905880811585673853237592381998183469149072919253974
+48956772571251782818646677614091807653908365676310869556566598028662310595)*
+x^2 + (-32086693829588024623316726000100010756361876262137235717978745230159
+7935024332597275728081239325323295559581472435768276459325858446852777406135
+4224237888838586662354341480780448138462095078298855585343788593872628558506
+2232497444401113004177020721408173287480234640696800006558695087522539471918
+8462351882227322473940035609517634436586317985311765355234869522092653871608
+552583923034075591677276229656463785917547906153809856186338690340273770*y +
+ 684969938521412010632767736929174913834080110330425498234557819681514228146
+0090549891357621057501917177689832760208273044989366982124463965716426233156
+1527040802440908533713005052405522578751808660353806475066416377243465617273
+3705861162967500113371237563099623430813050413374757885431028358350978378497
+3112848884290316479154536924753537624150534114549576218278910101174064517316
+1007071339740860788236392250237982206266738278196933938408844992180)*x + (-4
+7914883868975351892993788361802360854236346782223242244790627280758059828224
+3706143321286494865672428790243301228341780405161796806191536135406165858060
+3771830086241493044029276729458390644416619292001887065020793855148330934570
+2458067687554516829069745689086923959238429341181386665587548793744841347291
+4498689385652837815737802716011684231376279161897401214472802917672969506723
+1726531825722049891535045895982805077957753979050666046159539157559954608670
+5280609870755665307644950794838520000466297270668595045517853970926619542467
+11193183042460393012*y + 102286185146717051912553759002903739674572708315527
+8177292654318235805156376457250460484024275697120301114067014612617128855593
+1140879004477270052052029080767411295114744444724422974609785562746185135249
+7148990880570567486377403008887101174292961952397946466565017968649906175409
+2628317871187630174976464224281745898238726541254470553169688694375326272602
+0141231081533644354618819738411776242588786830306751837706932242870021408128
+3604919815766725639113030288154884719519014500084386431221130684472131661639
+4338537789242874246402340373262458277723507004179)
+6
+x^5 + 1497313810*x^3 - 153069990594216277067332440*x^2 - 9993317827227414657
+08504771613701347765415*x + 421302153697978557238622899127897637801303537233
+83633556328
+7
+x^3 + (6*y^4 - 18*y^3 + 6*y^2 - 18*y - 12)*x + (-4*y^5 + 30*y^4 + 32*y^2 + 2
+4*y + 4)
+8
+x^5 + (677308732982301944730030845266716201702837696162187325328/5*y^3 + 481
+271319660811460139352501916644558975058261230836596652/5*y^2 - 3487807973304
+8511269299413306183851642254427026365686673888/5*y - 24783137893632185532697
+291886880720222932490781434136563942/5)*x^3 + (31218254245856135648287296707
+04559492032765858876683224121119983801219547643890505070*y^3 + 2218259473949
+490736667831079912338956828105689590705243343986337391928943383709808005*y^2
+ - 1607587048361124753491934721194326369713341397761485813643339582894344828
+06877560648970*y - 114229487928172801039731551289054638597376141715926648946
+031483976252167029243247022480)*x^2 + (-166692999053535356002572927874917139
+2118748717302099608304683989705975477021372381764147057643564706588636619348
+44/5*y^3 - 11844618891597190508741280303272659573039434424283175367673479155
+0346393669679117451359918013807557809143042588696/5*y^2 + 858387225015985285
+7479162156889677342550132950704205078118190632176525884150800135006242479797
+595396229858998666474/5*y + 609939805478110246004601234032243326487160080269
+1098946100231203424708988718659688581036467351459717869466864403491/5)*x + (
+-376194285951669846001777157888755029972864354842521262364492544230511805129
+9870709569967640457662697840860989973276516003636843449764721490044/5*y^3 - 
+2673104432455463952284352656647109245231103663592899118247897745294025980581
+570350460813923435311458682114748384710825808171997155877533243621/5*y^2 + 1
+9372161459591757660716692723884954611407620247350322071070721355639658608496
+4322202670927424345172110316287310450443586757567876756951302594424/5*y + 13
+7651773558650909762864388498297831958827163349141150095186154867116600261791
+580542516653158613689676015829782898702530873665348851222173825666/5)
+9
+x^3 + (-2138833799099964*y - 77766538091057475)*x + (-2899681696823107245657
+26*y - 10543044869724218021876124)
+10
+[x^3 + (-2138833799099964*y - 77766538091057475)*x + (-271454449233503516412
+931*y - 9869898621944137446807843), x^3 + (-2138833799099964*y - 77766538091
+057475)*x + (18513720448807208152795*y + 673146247780080575068281), x^3 + (-
+2138833799099964*y - 77766538091057475)*x + (-289968169682310724565726*y - 1
+0543044869724218021876124), x^3 - 3*x - 1]
+11
+x^5 - 10*x^3 + 20*x + 10
+12
+x^5 + (14433507922528796322450466885842559554649240/81*y^3 - 206924153900250
+190478940045819169994648120380/81*y^2 + 387221873734866129008668004845146809
+3411480/27*y + 9952907878012919545305461812329973812202320)*x^3 + (151213742
+1515820547988557267937916762553254164737251785897692300/27*y^3 + 18337121232
+6689306422734140437601099700562333545199409203074500/27*y^2 - 11743163565184
+2472537762690188912056014010181849092684109181910170/9*y + 27790231583332542
+847266778828385473838935385907433291290641467310)*x^2 + (-226693826937254370
+3014068118233450711618653187091810917531287989910268281950806313112830/81*y^
+3 + 239315207598088678889180580299531524598499432218877226931937677230073476
+51161135616414460/81*y^2 + 4557474129212450957128695360991332089101354211319
+1870218073887288396098516272850951573480/27*y - 4785071374094015662869316915
+362187463934517274549880463316992141982057530013804207289130)*x + (119270482
+8439928261324666320105890752409692593927875083779153072015384025578913692346
+3989511892271143598294/3*y^3 - 152961475704026365347150203105055935481447020
+419907822345352843160917118838486063790065390094212607259586902/3*y^2 - 9397
+8480302010002477264842168576217064771866107133401243553275407905590291763780
+391464292290112011298779372*y + 40510745442141622784634137179518469588738855
+3485545336064626090438935556037611475681610251702629961491235364)
+13
+x^5 + (332998327585787806210715462400*y - 3088099449668699468241894745310)*x
+^3 + (494543408306584524066234839600325678760597930*y - 45862068980974258959
+41238867752143070004508050)*x^2 + (26417254338273940545860942707750465903049
+7550267984532302200*y - 2449835384316313335292893982961063446219951623920034
+753322415)*x + (476291033688128034677862637907914049204992323838153758931557
+45202346081966*y - 441694133924899145700575613631246753719401915993262186949
+236663342183626850)
+14
+x^2 + (-246740429132141146463522518444668570440348378975804*y^5 + 4703956945
+787111585543964420667028301009723801572487*y^4 + 266190357187628389772307989
+7863774384599266481260032*y^3 - 39177999595004651251370478833094268643601230
+78018159*y^2 + 10590083476362079496920522735326808810513545604444961*y - 584
+8192501453958323706561054990087672915722280908328)
+15
+x^2 + (-920087175408128195117442723568586073341235912833280265/2*y^5 - 37050
+25155369837955703313470274107124929636667858610239*y^4 + 3476715311084623172
+771710772579521132641467422385394005*y^3 + 684153818451910860756827784361755
+8566461173006276695185/2*y^2 - 486305569529989362223536556373888481868397838
+5576079475*y + 292682713400104576552871705948634083358899219106803462)
+16
+x^2 + (7064114974621884209263957684*y^5 - 78597289737331354515763478580*y^4 
++ 13905399960467092094726011945*y^3 - 27789415584376787513088098359*y^2 + 26
+854231387417039826858692319*y - 60884996981015832433185005956)
+17
+x^2 + (-8*y - 101)
+18
+x^2 - 5
+19
+x^3 + (-3*y^3 - 141/2*y^2 + 921*y - 6963/2)*x + (49621/3*y^3 - 547595/6*y^2 
+- 619772/3*y + 15840415/6)
+20
+[x^3 - 3*x - 1]
+Total time spent: 12057
diff --git a/src/test/dotest b/src/test/dotest
new file mode 100755
index 0000000..89c54c4
--- /dev/null
+++ b/src/test/dotest
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+trap exit 2
+bitlen=$1; shift
+testlist=$@
+
+if (tail -n 1 $0 >/dev/null 2>&1); then
+  tail='tail -n'
+else
+  tail='tail -'
+fi
+if (head -n 1 $0 >/dev/null 2>&1); then
+  head='head -n'
+else
+  head='head -'
+fi
+if (printf %-22s foo >/dev/null 2>&1); then
+  printf=OK
+else
+  printf=
+fi
+
+wrln () { echo "$1"; echo "$1" >&3; }
+wr () { echo $n "$1$c"; echo $n "$1$c" >&3; }
+wrtab () {
+  if test -n "$printf"; then
+    printf %$1s "$2"
+    printf %$1s "$2" >&3
+  else
+    echo $n "$2	$c"
+    echo $n "$2	$c" >&3
+  fi;
+}
+
+confdir=../config
+testdir=../src/test
+execdir=.
+if test -f /bin/rm ; then RM=/bin/rm; else RM=rm ; fi
+if sh -c 'test -x /bin/sh' 2>&-; then x=-x; else x=-r; fi
+(echo "hi there\c" ; echo " ") > echotmp
+if grep c echotmp >/dev/null 2>&1 ; then n=-n; c=; else n=; c='\c'; fi
+$RM -f echotmp
+
+. $confdir/version
+exec 3>> Bench-$VersionMajor.$VersionMinor.$patch
+
+dotestSUF=${dotestSUF:-"sta dyn"}
+for arg in $dotestSUF; do
+  if test $x $execdir/gp-$arg; then
+    SUF="$SUF $arg"
+    datadir=`echo 'print(default(datadir))' | $RUNTEST $execdir/gp-$arg -q -f`
+  fi
+done
+file_test=gp.out
+for suf in $SUF; do eval time$suf=0 files$suf=; done
+for testdata in $testlist; do
+  O=$IFS;IFS=_;set $testdata;IFS=$O;testname=$1;testmul=$2
+  pkgs=`${head}1 $testdir/in/$testname | grep package: | cut -f2 -d:`
+  skip=""
+  for pkg in $pkgs; do
+    if test ! $x "$datadir/$pkg"; then
+      wrln "! Skipping $testname: optional package $pkg not installed."
+      skip="true";
+    fi
+  done
+  if test "$skip" = "true"; then
+    untested="$untested $testname"
+  else
+    file_in=$testdir/in/$testname
+    file_out=$testdir/$bitlen/$testname
+    if test ! -r $file_out; then file_out=$testdir/32/$testname; fi
+    if test ! -r $file_out; then touch $file_out; fi
+    if test ! -r $file_in; then
+      wrln "Error! Missing file, can't test $testname"
+      exit 1
+    fi
+
+    testmul=${testmul:-1000}
+    wrtab -27 "* Testing $testname "
+    for suf in $SUF; do
+      file_diff=$testname-$suf.dif
+      gp=$execdir/gp-$suf
+      (echo 'gettime();0;'; cat $file_in; echo 'print("Total time spent: ",gettime);') | $RUNTEST $gp -q --test  > $file_test 2>&1
+      diff -c $file_out $file_test > $file_diff
+      pat=`grep "^[-+!] " $file_diff | grep -v "Total time"`
+      time=`${tail}1 $file_test | sed -n 's,.*Total time spent: \(.*\),\1,p'`
+      if test -n "$time"; then
+        eval t='$'time$suf
+        if test -n "$exprbug"; then
+          t=`expr $time \'\*\' $testmul / 1000 + $t`
+        else
+          t=`expr $time '*' $testmul / 1000 + $t`
+        fi
+        eval time$suf=$t
+      fi
+      pre=
+      if test -z "$pat"; then
+        wr "gp-$suf..TIME="
+        wrtab 8 "$time   ";
+      else
+        eval BUG$suf=BUG
+        eval files$suf=\"'$'files$suf $file_diff\"
+        wrtab -21 "gp-$suf..BUG [${time:-0}]"
+      fi
+    done
+    wrln
+  fi
+done
+$RM -f $file_test
+BUG=
+for suf in $SUF; do
+  B=`eval echo '$'BUG$suf`; BUG="$BUG$B"
+  t=`eval echo '$'time$suf`
+  if test -n "$B"; then B=' [BUG]'; fi
+  wrln "+++$B Total bench for gp-$suf is $t"
+done
+if test -n "$untested"; then
+  wrln "The following tests were skipped:$untested"
+fi
+if test -z "$BUG"; then exit 0; fi
+
+pwd=`pwd`
+wrln
+wrln "PROBLEMS WERE NOTED. The following files list them in diff format: "
+wrln "Directory: $pwd"
+bugp=; buge=;
+for suf in $SUF; do
+  B=`eval echo '$'BUG$suf`; BUG="$BUG$B"
+  if test -n "$B"; then
+    flist=`eval echo '$'files$suf`
+    for f in $flist; do
+      wrln "	$f"
+      case "$f" in
+        *program*)  bugp="$suf$bugp";;
+      esac
+    done
+  fi
+done
+
+case "x$bugp" in
+  x)  file_diff=;;
+  xsta|xdyn)
+      end=" (gp-$bugp only)"
+      file_diff=program-$bugp.dif;;
+  xexe)
+      end=" (gp-$bugp)"
+      file_diff=program.dif;;
+  x*) end=""
+      file_diff=program-sta.dif;;
+esac
+if test -n "$file_diff"; then
+  len=`wc -l $file_diff | sed -e 's/ *\([0-9]*\).*/\1/'`
+  pat=`${head}4 $file_diff|${tail}1|grep "\(99\|100\),107"`
+  case "$pat" in
+    *99,107*) elen=29;;
+    *100,107*) elen=24;;
+    *) elen=0;;
+  esac
+  if test $len -eq $elen  -a  -n "$pat"; then
+    cat << EOT
+NOTE: the problem noted in 'program' is that install() does not work in your
+configuration$end, so you will not be able to use this feature.
+Otherwise you can safely ignore the above warning.
+EOT
+  fi
+fi
+exit 1
diff --git a/src/test/dummy.c b/src/test/dummy.c
new file mode 100644
index 0000000..3f9848b
--- /dev/null
+++ b/src/test/dummy.c
@@ -0,0 +1,23 @@
+void nchar2nlong(){}
+void newblock(){}
+void remsBIL(){}
+void bit_accuracy(){}
+void is_bigint(){}
+void divsBIL(){}
+void copy_bin(){}
+void pari_free(){}
+void pari_malloc(){}
+void shiftaddress(){}
+void shiftaddress_canon(){}
+void powuu(){}
+void gerepilemanysp(){}
+void ceilr(){}
+void roundr(){}
+void pari_err(){}
+void pari_err_BUG(){}
+void pari_err_INV(){}
+void pari_err_OVERFLOW(){}
+void pari_err_PREC(){}
+void pari_warn(){}
+void quadratic_prec_mask(){}
+void prec2nbits(){}
diff --git a/src/test/in/addprimes b/src/test/in/addprimes
new file mode 100644
index 0000000..cda4e53
--- /dev/null
+++ b/src/test/in/addprimes
@@ -0,0 +1,29 @@
+p = nextprime(10^6);
+q = nextprime(p+1);
+a = 2*3*5*7;
+
+sigma2(x) = sigma(x, 2);
+sigma3(x) = sigma(x, 3);
+fun = [ispowerful, moebius, core, omega, bigomega, eulerphi, numdiv, sigma, sigma2, sigma3];
+
+vec(n) = vector(#fun, i, fun[i](n));
+args = [a, a*p, a*p*q, a*p^2*q, -a*p];
+vals = vector(#args, i, vec(args[i]))
+TEST() =
+{
+  for (i=1,#args,
+    my(n = args[i]);
+    print("Testing ", n, ", addprimes = ", addprimes());
+    if (vec(n) != vals[i], error(n))
+  );
+}
+
+addprimes(p);
+TEST();
+addprimes([p,q]);
+TEST();
+removeprimes(p);
+TEST();
+removeprimes(addprimes())
+addprimes([p,q,1009]);
+removeprimes([p,q])
diff --git a/src/test/in/agm b/src/test/in/agm
new file mode 100644
index 0000000..35c2aa6
--- /dev/null
+++ b/src/test/in/agm
@@ -0,0 +1,18 @@
+default(realprecision,38);
+gagm(a,b)=
+{
+  my(prec=10^(1-precision(a*1.)));
+  while(norm(a-b)>prec^2,
+    aa = (a+b)/2;
+    bb = sqrt(a*b);
+    if (norm(aa-bb)>norm(aa+bb), bb=-bb);
+    a=aa;b=bb);
+  a;
+}
+
+testagm(x)= {
+  my(e = norm(agm(x,1)-gagm(x,1)));
+  if(e > 1.E-75, printf("error %s: %s\n",x,e));
+}
+
+for(i=-6,6,for(j=-6,6,testagm(1/6+i/3+j*I/3)));
diff --git a/src/test/in/analyz b/src/test/in/analyz
new file mode 100644
index 0000000..a6b7257
--- /dev/null
+++ b/src/test/in/analyz
@@ -0,0 +1,8 @@
+HEAP=[9, if(precision(1.)==38,79,87)];
+default(realprecision,38);
+\e
+sum(x=0,50000,x);
+sum(x=1,1000,log(x));
+sum(x=1,25,sum(y=1,100,x/y),0.0);
+sum(x=1,100,sum(y=1,100,x/y,0.0));
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/apply b/src/test/in/apply
new file mode 100644
index 0000000..3dc8bb7
--- /dev/null
+++ b/src/test/in/apply
@@ -0,0 +1,12 @@
+apply(x->x^2, [1,2,3,4])
+apply(x->x^2, [1,2;3,4])
+apply(x->x^2, 4*x^2 + 3*x+ 2)
+apply(x->x^2, 4*x^2 + 3*x+ 2 + O(x^3))
+apply(x->x^2, List([1,2,3,4]))
+L = List([Mod(1,3), Mod(2,4)]); apply(lift, L)
+f(x)=L=0;x-1;
+L=List(); apply(f, L);
+L=[1,2,3]; apply(f, L)
+L=[1,2,3]; [f(x) | x<-L]
+L=[1,2,3]; [x | x<-L, f(x)]
+L=[1,2,3]; select(f, L)
diff --git a/src/test/in/arith b/src/test/in/arith
new file mode 100644
index 0000000..7af4539
--- /dev/null
+++ b/src/test/in/arith
@@ -0,0 +1,4 @@
+\\#1304
+issquarefree(0)
+\\#1412
+core(4*10^15+27)
diff --git a/src/test/in/aurifeuille b/src/test/in/aurifeuille
new file mode 100644
index 0000000..ed5834e
--- /dev/null
+++ b/src/test/in/aurifeuille
@@ -0,0 +1,21 @@
+alias(factor_Aurifeuille,"_factor_Aurifeuille")
+do(d, a) = polcyclo(d, a) / factor_Aurifeuille(a, d);
+
+do(35, -7*3^2)
+do(35, 5*3^2*7^2)
+
+do(70, 7*3^2)
+do(70, -5*3^2)
+
+do(44, 2*11*9^2)
+do(44, 2*11)
+do(12,6)
+do(4,8)
+
+do(100,2)
+
+alias(factor_Aurifeuille_prime,"_factor_Aurifeuille_prime")
+do(d, a) = polcyclo(d, a) / factor_Aurifeuille_prime(a, d);
+do(12, 2)
+do(15, 5)
+do(30, 3)
diff --git a/src/test/in/bern b/src/test/in/bern
new file mode 100644
index 0000000..facf86b
--- /dev/null
+++ b/src/test/in/bern
@@ -0,0 +1,8 @@
+bernfrac(0);
+bernfrac(1);
+for(k = 1, 20, print(bernfrac(k)));
+for(k = 0, 5, print(bernpol(k)));
+bernfrac(-1)
+bernreal(-1)
+bernpol(-1)
+bernvec(30)
diff --git a/src/test/in/bessel b/src/test/in/bessel
new file mode 100644
index 0000000..88d57cd
--- /dev/null
+++ b/src/test/in/bessel
@@ -0,0 +1,27 @@
+default(realprecision,38);
+default(seriesprecision,6);
+besseljh(1,2^64)
+besseljh(10,x)
+NU = [x,0,1,1/2,sqrt(2),10,1+I];
+ARG = [x,1,1/2,1+I];
+F=[besselh1,besselh2,besseli,besselj,besseljh,besselk,besseln];
+test(f)=
+{
+print(f);
+for (i=1,#NU,
+  for (j=1,#ARG,
+    print([i,j],": ", iferr(f(NU[i],ARG[j]), E,E));
+  )
+);
+}
+for(i=1,#F,test(F[i]));
+for(i=1,#F,print(F[i](1,Mod(x,x^2+1))));
+for(i=1,#F,print(F[i](1,[1])));
+for(i=1,#F,print(F[i](1,[1]~)));
+for(i=1,#F,print(F[i](1,Mat(1))));
+besseljh(2,0.)
+besseljh(2,1e-50)
+besselk(1,1000)
+besselk(1e-5,20)
+besselk(I,1000)
+besselk(1e-20,1e-5)
diff --git a/src/test/in/bestappr b/src/test/in/bestappr
new file mode 100644
index 0000000..7593a48
--- /dev/null
+++ b/src/test/in/bestappr
@@ -0,0 +1,18 @@
+bestappr(-1./7, 7)
+bestappr(-1./7, 6)
+bestappr(Mod(-1/7,100))
+bestappr(Mod(-1/7,100), 6)
+bestappr(Mod(-1/7,100), 7)
+bestappr(-1/7 + O(5^10))
+bestappr(-1/7 + O(5^10), 6)
+
+bestapprPade((x^3+1)/(x^10+2))
+bestapprPade((x^3+1)/(x^10+2), 4)
+bestapprPade(1/(1+x+O(x^10)))
+bestapprPade(1/(1+x+O(x^10)), 5)
+bestapprPade(1/(1+x^6+O(x^13)), 5)
+bestapprPade(1/(1+x^6+O(x^13)))
+bestapprPade(Mod(x^3+x^2+x+1, x^5-2))
+bestapprPade(x^2+x^3+x^4+O(x^5))
+bestapprPade(x^-4+x^-3+x^-2+O(x^-1))
+
diff --git a/src/test/in/bit b/src/test/in/bit
new file mode 100644
index 0000000..3841e2b
--- /dev/null
+++ b/src/test/in/bit
@@ -0,0 +1,32 @@
+hammingweight(15)
+hammingweight(x^100 + 2*x + 1)
+hammingweight([Mod(1,2), 2, Mod(0,3)])
+hammingweight(Vecsmall([0,1,2,3]))
+hammingweight(matid(100))
+hammingweight(I)
+N = 2^128+2^64+1;
+[bittest(N, i) | i<-[60..70]]
+{
+  args = [0, 3, -3, 2^65-1, N, -N, I];
+  funs = [bitand, bitnegimply, bitor, bitxor];
+  for (a=1,#funs,
+    my (f = funs[a]);
+    print("#", f);
+    for (i=1,#args,
+      for (j=i,#args,
+        my(u=args[i], v=args[j]);
+        print([u,v,iferr(f(u,v),E,E)])
+      )
+    )
+  );
+  print("#bitneg");
+  for (i=1, #args,
+    my (u=args[i]);
+    print(iferr([u, bitneg(u,65),bitneg(u)],E,E))
+  )
+}
+
+bittest(-1,10)
+bitneg(-2,64)
+bitneg(1,-2)
+bitneg(1,128)
diff --git a/src/test/in/bnfisintnorm b/src/test/in/bnfisintnorm
new file mode 100644
index 0000000..472c070
--- /dev/null
+++ b/src/test/in/bnfisintnorm
@@ -0,0 +1,16 @@
+do(i)=
+{ my(t = bnfisintnorm(bnf,i));
+  for (k=1,#t, if (nfeltnorm(bnf,t[k])!=i, error([i,k])));
+  if (#t, print(i,":",#t));
+}
+default(realprecision,38);
+\e
+setrand(1); bnf=bnfinit(x^2+105);
+for(i=1,1000, do(i))
+setrand(1); bnf=bnfinit(x^2-65);
+for(i=1,1000, do(i-500))
+setrand(1); bnf=bnfinit(x^5-37);
+for(i=1,1000, do(i-500))
+/* regression tests: */
+bnfisintnorm(bnfinit(x^3+5), 5)
+bnfisintnorm(bnfinit('y^2+93),54647)
diff --git a/src/test/in/bnr b/src/test/in/bnr
new file mode 100644
index 0000000..b717469
--- /dev/null
+++ b/src/test/in/bnr
@@ -0,0 +1,50 @@
+default(realprecision,38);
+K=bnfinit(x^3-x-1);
+subgrouplist(bnrinit(K,10))
+K=bnrinit(K,20);
+default(realprecision,77);
+nfnewprec(K);
+default(realprecision,38);
+subgrouplist(K)
+subgrouplist(K, 2)
+subgrouplist(K, 2, 1)
+bnrL1(bnrinit(bnfinit(x^2+1),10,1),1);
+setrand(5);bnrL1(bnrinit(bnfinit(y^2+6),1,1),0);
+bnrL1(bnrinit(bnfinit(x),[5,[1]],1));
+\\ #1399
+bnf=bnfinit(y^2+1); bnrdisc(bnf,12)
+
+setrand(1);bnf=bnfinit(x^2-x-57);
+test(m)=
+{
+  print(bnrisconductor(bnf,m), ", ", bnrclassno(bnf,m), ", ", bnrdisc(bnf,m));
+}
+test([[25,13;0,1],[1,1]])
+test([[25,13;0,1],[1,0]])
+test([[5,3;0,1],[1,0]])
+test([5,3;0,1])
+
+m=[idealfactor(bnf,[5,3;0,1]),[1,0]];
+H=Mat(2);
+bnr=bnrinit(bnf, idealstar(bnf,m,2));
+f=[bnrclassno,bnrdisc,bnrconductor];
+{
+for (i=1,#f,
+  print(f[i](bnr));
+  print(f[i](bnr,H));
+  print(f[i](bnf,m));
+  print(f[i](bnf,m,H));
+)
+}
+bnrdisc(bnf,m,H,1)
+bnrdisc(bnf,m,H,2)
+bnrdisc(bnf,m,H,3)
+bnrdisc(bnf,[5,3;0,1],H,2)
+bnrdisc(bnf,[5,3;0,1],H,3)
+bnrclassno(bnf, idealprimedec(bnf,5)[1])
+
+bnr=bnrinit(bnf,[7,[1,1]],1);
+bnrrootnumber(bnr, [2,1])
+
+bnfnarrow(bnfinit(x^2-460))
+bnrconductor(bnfinit(K),4,Mat(3))
diff --git a/src/test/in/bnrL1 b/src/test/in/bnrL1
new file mode 100644
index 0000000..06208e1
--- /dev/null
+++ b/src/test/in/bnrL1
@@ -0,0 +1,6 @@
+default(realprecision,38);
+K=bnfinit(x^2+31);bnrL1(bnrinit(K,1,1))
+K=bnfinit(x^2-3);
+bnrL1(bnrinit(K,5,1))
+bnrL1(bnrinit(K,[5,[1,0]],1))
+bnrL1(bnrinit(K,[5,[1,1]],1))
diff --git a/src/test/in/characteristic b/src/test/in/characteristic
new file mode 100644
index 0000000..fd1e009
--- /dev/null
+++ b/src/test/in/characteristic
@@ -0,0 +1,6 @@
+v=[1,1.,Mod(1,6),1/2,I+Mod(1,3), O(2), quadgen(5)*Mod(1,3), Mod(1,x)];
+for (i=1,#v, print(characteristic(v[i])))
+characteristic(v)
+characteristic(matid(2)*Mod(1,2))
+characteristic([])
+characteristic(List())
diff --git a/src/test/in/charpoly b/src/test/in/charpoly
new file mode 100644
index 0000000..846b020
--- /dev/null
+++ b/src/test/in/charpoly
@@ -0,0 +1,37 @@
+charpoly([x,x+1;1,2],y,0)
+charpoly([x,x+1;1,2],y,1)
+charpoly([x,x+1;1,2],y,2)
+charpoly([x,x+1;1,2],y,3)
+charpoly([0,0,2,2;0,0,2,2;2,2,0,0;2,2,0,0])
+charpoly([0,0,2,2;0,0,2,2;2,2,0,0;2,2,0,0],,4)
+minpoly(matrix(4,4,i,j,i/j))
+
+default(realprecision,38);
+A=[5/3,7/45;0,21/10];
+mateigen(A)
+mateigen(A*1.)
+mateigen(A,1)
+M=[x,x+y;x+1,1];charpoly(M,w)
+v=[1,1.,Mod(1,3),1/2,1+O(3),I,quadgen(5),matid(2)*Mod(1,3),matid(2)*Mod(1,2^64+13)];
+for(i=1,#v,print(charpoly(v[i])))
+charpoly(matid(4),,0)
+charpoly(matid(4),,3)
+charpoly(matid(4)*(2^64+13))
+m=[1,2,3,4;5,6,7,8;9,10,11,12;1,5,7,11];
+charpoly(m*Mod(1,3))
+charpoly(m*Mod(1,2^64+13))
+matadjoint(matid(2),1)
+matadjoint([;])
+matadjoint(Mat(1))
+matadjoint([x,0,0;0,0,0;0,0,0])
+matadjoint([Mod(1,2)*x,0,0;0,0,0;0,0,0])
+charpoly(x*matid(3))
+minpoly(Mod(x+1,x^4+1))
+minpoly(Mod(x,x^2))
+
+a=[1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0;0,1,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0;0,0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0;-1,-1,-1,4,0,0,0,0,-1,0,0,0,0,0,0,0,0;0,0,0,0,1,0,0,-1,0,0,0,0,0,0,0,0,0;0,0,0,0,0,1,0,-1,0,0,0,0,0,0,0,0,0;0,0,0,0,0,0,1,-1,0,0,0,0,0,0,0,0,0;0,0,0,0,-1,-1,-1,4,-1,0,0,0,0,0,0,0,0;0,0,0,-1,0,0,0,-1,4,-1,-1,0,0,0,0,0,0;0,0,0,0,0,0,0,0,-1,1,0,0,0,0,0,0,0;0,0,0,0,0,0,0,0,-1,0,4,-1,-1,-1,0,0,0;0,0,0,0,0,0,0,0,0,0,-1,1,0,0,0,0,0;0,0,0,0,0,0,0,0,0,0,-1,0,1,0,0,0,0;0,0,0,0,0,0,0,0,0,0,-1 [...]
+mateigen(a);
+mateigen([;])
+mateigen([;],1)
+mateigen(Mat(1))
+mateigen(Mat(1),1)
diff --git a/src/test/in/chinese b/src/test/in/chinese
new file mode 100644
index 0000000..bc7a0cf
--- /dev/null
+++ b/src/test/in/chinese
@@ -0,0 +1,4 @@
+chinese(Mod(x,x^2+1),Mod(x,x^2+1))
+chinese(Mod(x,x^2+1),Mod(x,x^2-1))
+chinese(Mod(1,2)*x+Mod(1,2), Mod(2,3)*x^2+Mod(1,3)*x+Mod(1,3))
+chinese([Mod(1,2),Mod(1,3)], [Mod(1,4),Mod(1,2)])
diff --git a/src/test/in/cmp b/src/test/in/cmp
new file mode 100644
index 0000000..e1fcdc0
--- /dev/null
+++ b/src/test/in/cmp
@@ -0,0 +1,17 @@
+test(f,g)=[cmp(f,f), cmp(f,g), cmp(g,f)]
+
+test(()->1, ()->2)
+test(1.,2.)
+test(1,2)
+test(Vecsmall([1,2]),Vecsmall([1,3]))
+test(List([1,2]),List([1,3]))
+test(x,x+1)
+test(x,y)
+test(O(x),x+O(x^2))
+
+1 > "a"
+1. > "a"
+1/2 > "a"
+"a" > 0
+Mod(1,3) > 0
+lex(Vecsmall([1,2]),Vecsmall([1,3]))
diff --git a/src/test/in/combinat b/src/test/in/combinat
new file mode 100644
index 0000000..01e5013
--- /dev/null
+++ b/src/test/in/combinat
@@ -0,0 +1,8 @@
+vector(10, k, stirling(11,k))
+vector(11, k, stirling(12,k))
+vector(10, k, stirling(11,k, 2))
+vector(11, k, stirling(12,k, 2))
+v = vector(5!,i,numtoperm(5, i-1))
+if(vecsort(v,lex)!=v,error("numtoperm"));
+vector(#v, i, permtonum(v[i]))
+stirling(100,0)
diff --git a/src/test/in/compat b/src/test/in/compat
new file mode 100644
index 0000000..954e7e2
--- /dev/null
+++ b/src/test/in/compat
@@ -0,0 +1,621 @@
+\e
+default(compatible,3)
++3
+-5
+5+3
+5-3
+5/3
+5\3
+5\/3
+5%3
+5^3
+\precision=154
+pi
+\precision=38
+o(x^12)
+padicno=(5/3)*127+O(127^5)
+initrect(0,500,500)
+\\ A
+abs(-0.01)
+acos(0.5)
+acosh(3)
+acurve=initell([0,0,1,-1,0])
+apoint=[2,2]
+isoncurve(acurve,apoint)
+addell(acurve,apoint,apoint)
+addprimes([nextprime(10^9),nextprime(10^10)])
+adj([1,2;3,4])
+agm(1,2)
+agm(1+o(7^5),8+o(7^5))
+algdep(2*cos(2*pi/13),6)
+algdep2(2*cos(2*pi/13),6,15)
+akell(acurve,1000000007)
+nfpol=x^5-5*x^3+5*x+25
+nf=initalg(nfpol)
+ba=algtobasis(nf,mod(x^3+5,nfpol))
+anell(acurve,100)
+apell(acurve,10007)
+apell2(acurve,10007)
+apol=x^3+5*x+1
+apprpadic(apol,1+O(7^8))
+apprpadic(x^3+5*x+1,mod(x*(1+O(7^8)),x^2+x-1))
+4*arg(3+3*i)
+3*asin(sqrt(3)/2)
+asinh(0.5)
+assmat(x^5-12*x^3+0.0005)
+3*atan(sqrt(3))
+atanh(0.5)
+\\ B
+basis(x^3+4*x+5)
+basis2(x^3+4*x+5)
+basistoalg(nf,ba)
+bernreal(12)
+bernvec(6)
+bestappr(pi,10000)
+bezout(123456789,987654321)
+bigomega(12345678987654321)
+mcurve=initell([0,0,0,-17,0])
+mpoints=[[-1,4],[-4,2]]~
+mhbi=bilhell(mcurve,mpoints,[9,24])
+bin(1.1,5)
+binary(65537)
+bittest(10^100,100)
+boundcf(pi,5)
+boundfact(40!+1,100000)
+move(0,0,0);box(0,500,500)
+setrand(1);buchimag(1-10^7,1,1)
+setrand(1);bnf=buchinitfu(x^2-x-57,0.2,0.2)
+buchcertify(bnf)
+buchfu(bnf)
+setrand(1);buchinitforcefu(x^2-x-100000)
+setrand(1);bnf=buchinitfu(x^2-x-57,0.2,0.2)
+setrand(1);buchreal(10^9-3,0,0.5,0.5)
+setrand(1);buchgen(x^4-7,0.2,0.2)
+setrand(1);buchgenfu(x^2-x-100000)
+setrand(1);buchgenforcefu(x^2-x-100000)
+setrand(1);buchgenfu(x^4+24*x^2+585*x+1791,0.1,0.1)
+buchnarrow(bnf)
+buchray(bnf,[[5,3;0,1],[1,0]])
+bnr=buchrayinitgen(bnf,[[5,3;0,1],[1,0]])
+bnr2=buchrayinitgen(bnf,[[25,13;0,1],[1,1]])
+bytesize(%)
+\\ C
+ceil(-2.5)
+centerlift(mod(456,555))
+cf(pi)
+cf2([1,3,5,7,9],(exp(1)-1)/(exp(1)+1))
+changevar(x+y,[z,t])
+char([1,2;3,4],z)
+char(mod(x^2+x+1,x^3+5*x+1),z)
+char1([1,2;3,4],z)
+char2(mod(1,8191)*[1,2;3,4],z)
+acurve=chell(acurve,[-1,1,2,3])
+chinese(mod(7,15),mod(13,21))
+apoint=chptell(apoint,[-1,1,2,3])
+isoncurve(acurve,apoint)
+classno(-12391)
+classno(1345)
+classno2(-12391)
+classno2(1345)
+coeff(sin(x),7)
+compimag(qfi(2,1,3),qfi(2,1,3))
+compo(1+o(7^4),3)
+compositum(x^4-4*x+2,x^3-x-1)
+compositum2(x^4-4*x+2,x^3-x-1)
+comprealraw(qfr(5,3,-1,0.),qfr(7,1,-1,0.))
+concat([1,2],[3,4])
+conductor(bnf,[[25,13;0,1],[1,1]])
+conductorofchar(bnr,[2])
+conj(1+i)
+conjvec(mod(x^2+x+1,x^3-x-1))
+content([123,456,789,234])
+convol(sin(x),x*cos(x))
+core(54713282649239)
+core2(54713282649239)
+coredisc(54713282649239)
+coredisc2(54713282649239)
+cos(1)
+cosh(1)
+move(0,200,150)
+cursor(0)
+cvtoi(1.7)
+cyclo(105)
+\\ D
+degree(x^3/(x-1))
+denom(12345/54321)
+deplin(mod(1,7)*[2,-1;1,3])
+deriv((x+y)^5,y)
+((x+y)^5)'
+det([1,2,3;1,5,6;9,8,7])
+det2([1,2,3;1,5,6;9,8,7])
+detint([1,2,3;4,5,6])
+diagonal([2,4,6])
+dilog(0.5)
+dz=vector(30,k,1);dd=vector(30,k,k==1);dm=dirdiv(dd,dz)
+deu=direuler(p=2,100,1/(1-apell(acurve,p)*x+if(acurve[12]%p,p,0)*x^2))
+anell(acurve,100)==deu
+dirmul(abs(dm),dz)
+dirzetak(initalg(x^3-10*x+8),30)
+disc(x^3+4*x+12)
+discf(x^3+4*x+12)
+discrayabs(bnr,mat(6))
+discrayabs(bnr)
+discrayabscond(bnr2)
+lu=ideallistunitgen(bnf,55);discrayabslist(bnf,lu)
+discrayabslistlong(bnf,20)
+discrayrel(bnr,mat(6))
+discrayrel(bnr)
+discrayrelcond(bnr2)
+divisors(8!)
+divres(345,123)
+divres(x^7-1,x^5+1)
+divsum(8!,x,x)
+postdraw([0,0,0])
+\\ E
+eigen([1,2,3;4,5,6;7,8,9])
+eint1(2)
+erfc(2)
+eta(q)
+euler
+z=y;y=x;eval(z)
+exp(1)
+extract([1,2,3,4,5,6,7,8,9,10],1000)
+\\ F
+10!
+fact(10)
+factcantor(x^11+1,7)
+centerlift(lift(factfq(x^3+x^2+x-1,3,t^3+t^2+t-1)))
+factmod(x^11+1,7)
+factor(17!+1)
+p=x^5+3021*x^4-786303*x^3-6826636057*x^2-546603588746*x+3853890514072057
+fa=[11699,6;2392997,2;4987333019653,2]
+factoredbasis(p,fa)
+factoreddiscf(p,fa)
+factoredpolred(p,fa)
+factoredpolred2(p,fa)
+factornf(x^3+x^2-2*x-1,t^3+t^2-2*t-1)
+factorpadic(apol,7,8)
+factorpadic2(apol,7,8)
+factpol(x^15-1,3,1)
+factpol(x^15-1,0,1)
+fibo(100)
+floor(-1/2)
+floor(-2.5)
+for(x=1,5,print(x!))
+fordiv(10,x,print(x))
+forprime(p=1,30,print(p))
+forstep(x=0,pi,pi/12,print(sin(x)))
+forvec(x=[[1,3],[-2,2]],print1([x[1],x[2]]," "));print(" ");
+frac(-2.7)
+\\ G
+galois(x^6-3*x^2-1)
+nf3=initalg(x^6+108);galoisconj(nf3)
+aut=%[2];galoisapply(nf3,aut,mod(x^5,x^6+108))
+gamh(10)
+gamma(10.5)
+gauss(hilbert(10),[1,2,3,4,5,6,7,8,9,0]~)
+gaussmodulo([2,3;5,4],[7,11]~,[1,4]~)
+gaussmodulo2([2,3;5,4],[7,11]~,[1,4]~)
+gcd(12345678,87654321)
+getrand()
+globalred(acurve)
+\\ H
+hclassno(2000003)
+hell(acurve,apoint)
+hell2(acurve,apoint)
+hermite(amat=1/hilbert(7))
+hermite2(amat)
+hermitemod(amat,detint(amat))
+hermiteperm(amat)
+hess(hilbert(7))
+hilb(2/3,3/4,5)
+hilbert(5)
+hilbp(mod(5,7),mod(6,7))
+hvector(10,x,1/x)
+hyperu(1,1,1)
+\\ I
+i^2
+initalgred(nfpol)
+initalgred2(nfpol)
+vp=primedec(nf,3)[1]
+idx=idealmul(nf,idmat(5),vp)
+idy=ideallllred(nf,idx,[1,5,6])
+idealadd(nf,idx,idy)
+idealaddone(nf,idx,idy)
+idealaddmultone(nf,[idy,idx])
+idealappr(nf,idy)
+idealapprfact(nf,idealfactor(nf,idy))
+idealcoprime(nf,idx,idx)
+idz=idealintersect(nf,idx,idy)
+idealfactor(nf,idz)
+ideallist(bnf,20)
+idx2=idealmul(nf,idx,idx)
+idt=idealmulred(nf,idx,idx)
+idealdiv(nf,idy,idt)
+idealdivexact(nf,idx2,idx)
+idealhermite(nf,vp)
+idealhermite2(nf,vp[2],3)
+idealnorm(nf,idt)
+idp=idealpow(nf,idx,7)
+idealpowred(nf,idx,7)
+idealtwoelt(nf,idy)
+idealtwoelt2(nf,idy,10)
+idealval(nf,idp,vp)
+idmat(5)
+if(3<2,print("bof"),print("ok"));
+imag(2+3*i)
+image([1,3,5;2,4,6;3,5,7])
+image(pi*[1,3,5;2,4,6;3,5,7])
+incgam(2,1)
+incgam3(2,1)
+incgam4(4,1,6)
+indexrank([1,1,1;1,1,1;1,1,2])
+indsort([8,7,6,5])
+initell([0,0,0,-1,0])
+initrect(1,700,700)
+nfz=initzeta(x^2-2);
+integ(sin(x),x)
+integ((-x^2-2*a*x+8*a)/(x^4-14*x^3+(2*a+49)*x^2-14*a*x+a^2),x)
+intersect([1,2;3,4;5,6],[2,3;7,8;8,9])
+\precision=19
+intgen(x=0,pi,sin(x))
+sqr(2*intgen(x=0,4,exp(-x^2)))
+4*intinf(x=1,10^20,1/(1+x^2))
+intnum(x=-0.5,0.5,1/sqrt(1-x^2))
+2*intopen(x=0,100,sin(x)/x)
+\precision=38
+inverseimage([1,1;2,3;5,7],[2,2,6]~)
+isdiagonal([1,0,0;0,5,0;0,0,0])
+isfund(12345)
+isideal(bnf[7],[5,1;0,1])
+isincl(x^2+1,x^4+1)
+isinclfast(initalg(x^2+1),initalg(x^4+1))
+isirreducible(x^5+3*x^3+5*x^2+15)
+isisom(x^3+x^2-2*x-1,x^3+x^2-2*x-1)
+isisomfast(initalg(x^3-2),initalg(x^3-6*x^2-6*x-30))
+isprime(12345678901234567)
+isprincipal(bnf,[5,1;0,1])
+isprincipalgen(bnf,[5,1;0,1])
+isprincipalraygen(bnr,primedec(bnf,7)[1])
+ispsp(73!+1)
+isqrt(10!^2+1)
+isset([-3,5,7,7])
+issqfree(123456789876543219)
+issquare(12345678987654321)
+isunit(bnf,mod(3405*x-27466,x^2-x-57))
+\\ J
+jacobi(hilbert(6))
+jbesselh(1,1)
+jell(i)
+\\ K
+kbessel(1+i,1)
+kbessel2(1+i,1)
+x
+y
+ker(matrix(4,4,x,y,x/y))
+ker(matrix(4,4,x,y,sin(x+y)))
+keri(matrix(4,4,x,y,x+y))
+kerint(matrix(4,4,x,y,x*y))
+kerint1(matrix(4,4,x,y,x*y))
+f(u)=u+1;
+print(f(5));kill(f);
+f=12
+killrect(1)
+kro(5,7)
+kro(3,18)
+\\ L
+laplace(x*exp(x*y)/(exp(x)-1))
+lcm(15,-21)
+length(divisors(1000))
+legendre(10)
+lex([1,3],[1,3,5])
+lexsort([[1,5],[2,4],[1,5,1],[1,4,2]])
+lift(chinese(mod(7,15),mod(4,21)))
+lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)])
+lindep2([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)],14)
+move(0,0,900);line(0,900,0)
+lines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+m=1/hilbert(7)
+mp=concat(m,idmat(7))
+lll(m)
+lllgram(m)
+lllgramint(m)
+lllgramkerim(mp~*mp)
+lllint(m)
+lllintpartial(m)
+lllkerim(mp)
+\precision=96
+ln(2)
+lngamma(10^50*i)
+\precision=2000
+log(2)
+logagm(2)
+\precision=19
+bcurve=initell([0,0,0,-3,0])
+localred(bcurve,2)
+ccurve=initell([0,0,-1,-1,0])
+l=lseriesell(ccurve,2,-37,1)
+lseriesell(ccurve,2,-37,1.2)-l
+\\ M
+sbnf=smallbuchinit(x^3-x^2-14*x-1)
+makebigbnf(sbnf)
+concat(mat(vector(4,x,x)~),vector(4,x,10+x)~)
+matextract(matrix(15,15,x,y,x+y),vector(5,x,3*x),vector(3,y,3*y))
+ma=mathell(mcurve,mpoints)
+gauss(ma,mhbi)
+(1.*hilbert(7))^(-1)
+matsize([1,2;3,4;5,6])
+matrix(5,5,x,y,gcd(x,y))
+matrixqz([1,3;3,5;5,7],0)
+matrixqz2([1/3,1/4,1/6;1/2,1/4,-1/4;1/3,1,0])
+matrixqz3([1,3;3,5;5,7])
+max(2,3)
+min(2,3)
+minim([2,1;1,2],4,6)
+mod(-12,7)
+modp(-12,7)
+mod(10873,49649)^-1
+modreverse(mod(x^2+1,x^3-x-1))
+move(0,243,583);cursor(0)
+mu(3*5*7*11*13)
+\\ N
+newtonpoly(x^4+3*x^3+27*x^2+9*x+81,3)
+nextprime(100000000000000000000000)
+setrand(1);N=10^8;a=matrix(3,5,j,k,vvector(5,l,random()\N))
+aid=[idx,idy,idz,idmat(5),idx]
+bb=algtobasis(nf,mod(x^3+x,nfpol))
+da=nfdetint(nf,[a,aid])
+nfdiv(nf,ba,bb)
+nfdiveuc(nf,ba,bb)
+nfdivres(nf,ba,bb)
+nfhermite(nf,[a,aid])
+nfhermitemod(nf,[a,aid],da)
+nfmod(nf,ba,bb)
+nfmul(nf,ba,bb)
+nfpow(nf,bb,5)
+nfreduce(nf,ba,idx)
+nfsmith(nf,[a[,1..3],[1,1,1],[idealinv(nf,idx), idealinv(nf,idy),1]])
+nfval(nf,ba,vp)
+norm(1+i)
+norm(mod(x+5,x^3+x+1))
+norml2(vector(10,x,x))
+nucomp(qfi(2,1,9),qfi(4,3,5),3)
+form=qfi(2,1,9);nucomp(form,form,3)
+numdiv(2^99*3^49)
+numer((x+1)/(x-1))
+nupow(form,111)
+\\ O
+1/(1+x)+o(x^20)
+omega(100!)
+ordell(acurve,1)
+order(mod(33,2^16+1))
+tcurve=initell([1,0,1,-19,26]);
+orderell(tcurve,[1,2])
+ordred(x^3-12*x+45*x-1)
+\\ P
+padicprec(padicno,127)
+pascal(8)
+perf([2,0,1;0,2,1;1,1,2])
+permutation(7,1035)
+permutation2num([4,7,1,6,3,5,2])
+pf(-44,3)
+phi(257^2)
+pi
+b=10;a=1<<b;plot(x=-5,5,round(sin(x)<<b)/a)
+pnqn([2,6,10,14,18,22,26])
+pnqn([1,1,1,1,1,1,1,1;1,1,1,1,1,1,1,1])
+point(0,225,334)
+points(0,vector(10,k,10*k),vector(10,k,5*k*k))
+pointell(acurve,zell(acurve,apoint))
+polint([0,2,3],[0,4,9],5)
+polred(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+polred2(x^4-28*x^3-458*x^2+9156*x-25321)
+polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+polredabs2(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+polsym(x^17-1,17)
+polvar(name^4-other)
+poly(sin(x),x)
+polylog(5,0.5)
+polylog(-4,t)
+polylogd(5,0.5)
+polylogdold(5,0.5)
+polylogp(5,0.5)
+poly([1,2,3,4,5],x)
+polyrev([1,2,3,4,5],x)
+polzag(6,3)
+postdraw([0,20,20])
+postploth(x=-5,5,sin(x))
+postploth2(t=0,2*pi,[sin(5*t),sin(7*t)])
+postplothraw(vector(100,k,k),vector(100,k,k*k/100))
+powell(acurve,apoint,10)
+cmcurve=initell([0,-3/4,0,-2,-1])
+powell(cmcurve,[x,y],quadgen(-7))
+powrealraw(qfr(5,3,-1,0.),3)
+pprint((x-12*y)/(y+13*x));
+pprint([1,2;3,4])
+pprint1(x+y);pprint(x+y);
+\precision=96
+pi
+prec(pi,20)
+precision(cmcurve)
+\precision=38
+prime(100)
+primedec(nf,2)
+primedec(nf,3)
+primedec(nf,11)
+primes(100)
+forprime(p=2,100,print(p," ",lift(primroot(p))))
+principalideal(nf,mod(x^3+5,nfpol))
+print((x-12*y)/(y+13*x));
+print([1,2;3,4])
+print1(x+y);print1(" equals ");print(x+y);
+prod(1,k=1,10,1+1/k!)
+prod(1.,k=1,10,1+1/k!)
+pi^2/6*prodeuler(p=2,10000,1-p^-2)
+prodinf(n=0,(1+2^-n)/(1+2^(-n+1)))
+prodinf1(n=0,-2^-n/(1+2^(-n+1)))
+psi(1)
+\\ Q
+quaddisc(-252)
+quadgen(-11)
+quadpoly(-11)
+\\ R
+rank(matrix(5,5,x,y,x+y))
+rayclassno(bnf,[[5,3;0,1],[1,0]])
+rayclassnolist(bnf,lu)
+move(0,50,50);rbox(0,50,50)
+print1("give a value for s? ");s=read();print(1/s)
+37.
+real(5-7*i)
+recip(3*x^7-5*x^3+6*x-9)
+redimag(qfi(3,10,12))
+redreal(qfr(3,10,-20,1.5))
+redrealnod(qfr(3,10,-20,1.5),18)
+reduceddisc(x^3+4*x+12)
+regula(17)
+kill(y);print(x+y);
+resultant(x^3-1,x^3+1)
+resultant2(x^3-1.,x^3+1.)
+bezoutres(x^2+1, x^2-1)
+reverse(tan(x))
+rhoreal(qfr(3,10,-20,1.5))
+rhorealnod(qfr(3,10,-20,1.5),18)
+rline(0,200,150)
+cursor(0)
+rmove(0,5,5);cursor(0)
+rndtoi(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+qpol=y^3-y-1;setrand(1);bnf2=buchinit(qpol);nf2=bnf2[7];
+un=mod(1,qpol);w=mod(y,qpol);p=un*(x^5-5*x+w)
+aa=rnfpseudobasis(nf2,p)
+rnfbasis(bnf2,aa)
+rnfdiscf(nf2,p)
+rnfequation(nf2,p)
+rnfequation2(nf2,p)
+rnfhermitebasis(bnf2,aa)
+rnfisfree(bnf2,aa)
+rnfsteinitz(nf2,aa)
+rootmod(x^16-1,41)
+rootpadic(x^4+1,41,6)
+roots(x^5-5*x^2-5*x-5)
+rootsold(x^4-1000000000000000000000)
+round(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+rounderror(prod(1,k=1,17,x-exp(2*i*pi*k/17)))
+rpoint(0,20,20)
+\\ S
+initrect(3,600,600);scale(3,-7,7,-2,2);cursor(3)
+q*series(anell(acurve,100),q)
+aset=set([5,-2,7,3,5,1])
+bset=set([7,5,-5,7,2])
+setintersect(aset,bset)
+setminus(aset,bset)
+setprecision(28)
+setrand(10)
+setsearch(aset,3)
+setsearch(bset,3)
+setserieslength(12)
+setunion(aset,bset)
+shift(1,50)
+shift([3,4,-11,-12],-2)
+shiftmul([3,4,-11,-12],-2)
+sigma(100)
+sigmak(2,100)
+sigmak(-3,100)
+sign(-1)
+sign(0)
+sign(0.)
+signat(hilbert(5)-0.11*idmat(5))
+signunit(bnf)
+simplefactmod(x^11+1,7)
+simplify(((x+i+1)^2-x^2-2*x*(i+1))^2)
+sin(pi/6)
+sinh(1)
+size([1.3*10^5,2*i*pi*exp(4*pi)])
+smallbasis(x^3+4*x+12)
+smalldiscf(x^3+4*x+12)
+smallfact(100!+1)
+smallinitell([0,0,0,-17,0])
+smallpolred(x^4+576)
+smallpolred2(x^4+576)
+smith(matrix(5,5,j,k,random()))
+smith(1/hilbert(6))
+smithpol(x*idmat(5)-matrix(5,5,j,k,1))
+solve(x=1,4,sin(x))
+sort(vector(17,x,5*x%17))
+sqr(1+o(2))
+sqred(hilbert(5))
+sqrt(13+o(127^12))
+srgcd(x^10-1,x^15-1)
+move(0,100,100);string(0,pi)
+move(0,200,200);string(0,"(0,0)")
+postdraw([0,10,10])
+apol=0.3+legendre(10)
+sturm(apol)
+sturmpart(apol,0.91,1)
+subcyclo(31,5)
+subell(initell([0,0,0,-17,0]),[-1,4],[-4,2])
+subst(sin(x),x,y)
+subst(sin(x),x,x+x^2)
+sum(0,k=1,10,2^-k)
+sum(0.,k=1,10,2^-k)
+sylvestermatrix(a2*x^2+a1*x+a0,b1*x+b0)
+\precision=38
+4*sumalt(n=0,(-1)^n/(2*n+1))
+4*sumalt2(n=0,(-1)^n/(2*n+1))
+suminf(n=1,2.^-n)
+6/pi^2*sumpos(n=1,n^-2)
+supplement([1,3;2,4;3,6])
+\\ T
+sqr(tan(pi/3))
+tanh(1)
+taniyama(bcurve)
+taylor(y/(x-y),y)
+tchebi(10)
+teich(7+o(127^12))
+texprint((x+y)^3/(x-y)^2)
+theta(0.5,3)
+thetanullk(0.5,7)
+torsell(tcurve)
+trace(1+i)
+trace(mod(x+5,x^3+x+1))
+trans(vector(2,x,x))
+%*%~
+trunc(-2.7)
+trunc(sin(x^2))
+tschirnhaus(x^5-x-1)
+type(mod(x,x^2+1))
+\\ U
+unit(17)
+n=33;until(n==1,print1(n," ");if(n%2,n=3*n+1,n=n/2));print(1)
+\\ V
+valuation(6^10000-1,5)
+vec(sin(x))
+vecmax([-3,7,-2,11])
+vecmin([-3,7,-2,11])
+vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],2)
+vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],[2,1])
+\\ W
+weipell(acurve)
+wf(i)
+wf2(i)
+m=5;while(m<20,print1(m," ");m=m+1);print()
+\\ Z
+zell(acurve,apoint)
+zeta(3)
+zeta(0.5+14.1347251*i)
+zetak(nfz,-3)
+zetak(nfz,1.5+3*i)
+zidealstar(nf2,54)
+bid=zidealstarinit(nf2,54)
+zideallog(nf2,w,bid)
+znstar(3120)
+sin'(4)
+m = 3; if(m == 1,2, m == 3,4,5)
+A=[1,2,3];
+deplin(A)
+deplin(Mod(A,2))
+deplin(Mod(A,7))
+deplin(Mod(A,2^64+13))
+
+p=2^64+13; T=char(Mod(4*p*y+1,y^4+1), x); discf2(T)
+getstack()
+getheap()
diff --git a/src/test/in/concat b/src/test/in/concat
new file mode 100644
index 0000000..e70c4b6
--- /dev/null
+++ b/src/test/in/concat
@@ -0,0 +1,34 @@
+A=[1,2;3,4]; B=[5,6]~; C=[7,8]; D=9;
+matconcat([A, B])
+matconcat([A, C]~)
+matconcat([A, B; C, D])
+matconcat([1, [2,3]~, [4,5,6]~])
+matconcat([1, [2,3], [4,5,6]]~)
+matconcat([B, C; A, D])
+matconcat([])
+concat("x",2)
+concat([;],1)
+concat([;],[])
+concat([;],[1])
+concat(1,[;])
+concat([],[;])
+concat([1],[;])
+concat(1,2)
+concat(1,[2])
+concat(1,Mat(2))
+concat(1,A)
+concat(Mat(2),1)
+concat(A,1)
+concat([1,2],[]~)
+concat([1,2],[1]~)
+concat([1,2],[1,2]~)
+concat([2,3], matid(2))
+concat([]~,[1,2])
+concat([3]~,[1,2])
+concat([2,3]~,[])
+concat([2,3]~,[1])
+concat([2,3]~,[1,2])
+concat([;],[])
+concat(A,[1,2])
+concat(List([[1],[2]]))
+concat([1]~,Mat(2))
diff --git a/src/test/in/content b/src/test/in/content
new file mode 100644
index 0000000..3a96377
--- /dev/null
+++ b/src/test/in/content
@@ -0,0 +1,7 @@
+default(realprecision,38);
+o=[2,1.0,Mod(1,3),2/3,ffgen(2^3),1/2+I/3,O(2^-3),quadgen(5),Mod(x/2,x^2),2*x,2/x, Qfb(1,2,4),Qfb(-1,2,4),[2,3/4], [1,2;3/2,4]];
+
+test(f)= [ print(iferr(f(p),E,E)) | p<-o ];
+test(denominator);
+test(numerator);
+test(content);
diff --git a/src/test/in/contfrac b/src/test/in/contfrac
new file mode 100644
index 0000000..9457154
--- /dev/null
+++ b/src/test/in/contfrac
@@ -0,0 +1,18 @@
+contfrac(1,[],-1)
+contfracpnqn(Vecsmall([]))
+contfracpnqn([])
+contfracpnqn([],0)
+contfracpnqn([],1)
+contfracpnqn([2])
+contfracpnqn([2],0)
+contfracpnqn([2],1)
+v=[1,2,3];
+contfracpnqn(v)
+contfracpnqn(v,0)
+contfracpnqn(v,1)
+contfracpnqn(v,2)
+v=[1,2,3;4,5,6];
+contfracpnqn(v)
+contfracpnqn(v,0)
+contfracpnqn(v,1)
+contfracpnqn(v,2)
diff --git a/src/test/in/cxtrigo b/src/test/in/cxtrigo
new file mode 100644
index 0000000..7aa7682
--- /dev/null
+++ b/src/test/in/cxtrigo
@@ -0,0 +1,43 @@
+ASIN(z) = -I*log(I*z + sqrt(1 - z^2));
+ACOS(z) = -I*log(z + I*sqrt(1 - z^2));
+ATAN(z) = (log(1+I*z) - log(1-I*z))/(2*I);
+
+ASINH(z) = log(z + sqrt(1 + z^2));
+ACOSH(z) = 2*log(sqrt((z-1)/2) + sqrt((z+1)/2));
+ATANH(z) = (log(1+z) - log(1-z))/2;
+
+fun = [asin,acos,atan,asinh,acosh,atanh];
+FUN = [ASIN,ACOS,ATAN,ASINH,ACOSH,ATANH];
+
+test(f, F, N = 4) = {
+  my (h = 1./ N);
+  forstep (re = -N, N, h,
+    forstep (im = -N, N, h,
+      iferr(
+        my(mr, cr, dt);
+        mr = f(re+im*I);
+        cr = F(re+im*I); dt = mr - cr;
+        if (abs(dt) > 1e-10,
+          printf("%s(%.1f + I*%.1f):\t%.2g + I*%.2g\n",
+                 f, re, im, real(dt), imag(dt));
+        ),
+        ERR, printf ("%s: %.1f + I*%.1f is a pole ?\n", f, re, im))
+    )
+  );
+}
+for(k = 1, #fun, test(fun[k], FUN[k]));
+default(realprecision,1155);
+a=sqrt(2)/2;
+sin(asin(a)) - a
+cos(acos(a)) - a
+tan(atan(a)) - a
+sinh(asinh(a)) - a
+cosh(acosh(a)) - a
+tanh(atanh(a)) - a
+default(realprecision,38);
+a *= 1.;
+cotan(a)
+cotan(I)
+cotan(a+I)
+tanh(I)
+cotan(a+I)
diff --git a/src/test/in/cyclo b/src/test/in/cyclo
new file mode 100644
index 0000000..5fcbaab
--- /dev/null
+++ b/src/test/in/cyclo
@@ -0,0 +1,39 @@
+allocatemem(8000000);
+poliscyclo(1)
+poliscyclo(x^0)
+poliscyclo(x)
+for (i=1,100, if (poliscyclo(polcyclo(i)) != i, error(i)))
+for (i=1,100, if (!poliscycloprod(x^i-1), error(i)))
+poliscyclo(polcyclo(10^5))
+poliscyclo(polcyclo(12345))
+{
+  for (i=1,10,
+    f = polcyclo(i);
+    for(j=i+1,10,
+      g = f*polcyclo(j);
+      if (poliscyclo(g), error("is ", [i,j]));
+      if (!poliscycloprod(g), error("prod ", [i,j]));
+      print (polcyclofactors(g))
+    )
+  );
+}
+poliscycloprod((x-1)^2)
+poliscycloprod((x+1)^2*(x-1))
+test(n,t)= if (polcyclo(n,t) != subst(polcyclo(n),x,t), error([n,t]));
+test(5,1);test(5,-1);
+test(10,1);test(10,-1);
+test(40,1);test(40,-1);
+test(2,-1);
+test(10,-1);
+test(11,-1);
+test(5,Mod(-1,3))
+
+\\ roots of 1
+test(20, I)
+test(10, Mod(3,11))
+test(10, 2 + O(11))
+test(30, -1.0)
+z15 = Mod(t,polcyclo(15,t));
+test(15, z15)
+test(30, z15)
+test(105, z15)
diff --git a/src/test/in/debugger b/src/test/in/debugger
new file mode 100644
index 0000000..403086a
--- /dev/null
+++ b/src/test/in/debugger
@@ -0,0 +1,38 @@
+\e
+default(breakloop,1)
+my(bound=100,step=20,halt=41); check(B)=
+{
+  my(bi=[B^2]);
+  for(i=1,bound,
+    my(p=i+step,N=p^2);
+    if(i==halt,error("check:",N)))
+}
+check(1000);
+[bound, step, halt, i, p, N, bi, B]
+break
+[bound, step, halt, i, p, N, bi, B]
+my(p=120);for(i=1,100,1/0)
+[p,i]
+dbg_err()
+break
+fun(N)=check(N^2+1);
+fun(20);
+N
+dbg_up(4)
+N
+break
+f(N,x)=my(z=x^2+1);breakpoint();gcd(N,z^2+1-z);
+f(221,3)
+z
+
+z
+
+iferrname("e_VAR",vector(10000,i,1/(i-100)),E,Vec(E))
+i
+break
+f()=1/0
+f();
+allocatemem(10^7)
+for(i=1,10,if(i==2,1/0));
+i
+break
diff --git a/src/test/in/deriv b/src/test/in/deriv
new file mode 100644
index 0000000..b86acd3
--- /dev/null
+++ b/src/test/in/deriv
@@ -0,0 +1,24 @@
+default(realprecision,38);
+sin'(4)
+derivnum(x=4,sin(x))
+derivnum(x=t^2+O(t^4),sin(x))
+v=[Mod(x*y+1,x^2+1), Mod(1,3), ffgen(2^3,'t), 1+x*y+O(x^3), x/(x+y), (x^3/3+x+1) / (x^2+1)^2, [x*y], Mat(x*y)];
+{
+for (i=1,#v,
+  my(u=v[i]);
+  print(deriv(u,'x));
+  print(u');
+  print(deriv(u,'y));
+  iferr(print(intformal(u,'x)),E,print(E));
+  iferr(print(intformal(u,'y)),E,print(E));
+)
+}
+intformal(1-A,Y)
+a=intformal(Pol(0,x), y)
+b=intformal(Pol(0,y), x)
+variable(a)
+variable(b)
+a=intformal(O(x), y)
+b=intformal(O(y), x)
+variable(a)
+variable(b)
diff --git a/src/test/in/det b/src/test/in/det
new file mode 100644
index 0000000..742a9a9
--- /dev/null
+++ b/src/test/in/det
@@ -0,0 +1,5 @@
+M=[8, 520037431316, 0, 520037431316, 0, 520035574851, 0, 965887922181; 520037431316, 38628881218179226412880, 4958989308, 38629 323366757228004016, 73478513407, 38628858533840109643628, 4243910044575227, 71751970539224190286968; 0, 4958989308, 0, 4957132843, 0, 5710115047, -5940688, 0; 520037431316, 38629323366757228004016, 4957132843, 38629765515335229595152, 74231495611, 38629300680212375365180, 4243835813079616, 71752791774805257538227; 0, 73478513407, 0, 74231495611, -5940688, 0, 185 [...]
+matdet(M)
+matdet(M,1)
+centerlift(chinese(apply(p->matdet(M*Mod(1,p)),primes(200))))
+centerlift(chinese(apply(p->matdet(M*Mod(1,p)),vector(5,i,nextprime(2^(128+i))))))
diff --git a/src/test/in/diffop b/src/test/in/diffop
new file mode 100644
index 0000000..cf21ae8
--- /dev/null
+++ b/src/test/in/diffop
@@ -0,0 +1,7 @@
+diffop(E*X,[X,E],[1,X*E],10)
+diffop(Mod(S/C,S^2+C^2-1),[C],[-S],10)
+E4(n)=1+240*sum(i=1,n,sigma(i,3)*q^i)+O(q^(n+1));
+diffop(E4(15),[q],[q])
+diffop(Mod(y,x^2-y),[y],[1])
+diffop(Mod(10^100,x^2+y),[y],[1])
+diffop(lllx,[x,lx,llx,lllx],[1,1/x,1/(x*lx),1/(x*lx*llx)],6)*Mod(1,3)
diff --git a/src/test/in/digits b/src/test/in/digits
new file mode 100644
index 0000000..95a7b0f
--- /dev/null
+++ b/src/test/in/digits
@@ -0,0 +1,18 @@
+binary(0)
+digits(0)
+digits(5^100)
+check(a,b)=my(v=digits(a,b));if(subst(Pol(v),'x,b)!=a || vecmax(v)>=b,error([a,b]));
+check(5^100,2)
+check(5^100,3)
+check(3^100,2^64-1)
+check(3^100,2^128+1)
+sumdigits(0)
+sumdigits(129)
+sumdigits(123456789123456789123456789)
+sumdigits(100000!)
+default(realprecision,38);
+binary(0.)
+binary(0.005)
+binary(1.1)
+binary([0,1])
+binary(I)
diff --git a/src/test/in/dirmul b/src/test/in/dirmul
new file mode 100644
index 0000000..701623b
--- /dev/null
+++ b/src/test/in/dirmul
@@ -0,0 +1,6 @@
+dirmul([0,1],[0,1])
+dirdiv([0,1,1,1,1],[1])
+dirmul(vector(10,n,moebius(n)),vector(10,n,1))
+dirmul([1,2,3,4],[1,2,3,4])
+dirdiv([1,2,3,4],[1,2,3,4])
+dirdiv([1,2,3,4],2*[1,2,3,4])
diff --git a/src/test/in/disc b/src/test/in/disc
new file mode 100644
index 0000000..58d13b5
--- /dev/null
+++ b/src/test/in/disc
@@ -0,0 +1 @@
+poldisc(Mod(1, 2)*x)
diff --git a/src/test/in/div b/src/test/in/div
new file mode 100644
index 0000000..baefa9b
--- /dev/null
+++ b/src/test/in/div
@@ -0,0 +1,37 @@
+default(realprecision,38);
+v=[3,4,3.1,1/2,x^2+1, Mod(x,x^2+1),ffgen(3^5,'t),quadgen(5),2+O(3^3),Mod(2,3), 2^64 + 1];
+{
+for (i=1,#v,
+  for(j=1,#v,
+    print("* ",[i,j]);
+    print(iferr(v[i]/v[j],E,E));
+    print(iferr(v[i]\v[j],E,E));
+    print(iferr(v[i]%v[j],E,E));
+    print(iferr(v[i]\/v[j],E,E));
+    print(iferr(divrem(v[i],v[j]),E,E));
+  )
+)
+}
+w=[x + O(x^2),[2,3],Mat(2)];
+{
+for (i=1,#w,
+  for(j=1,#v,
+    print("* ",[i,j]);
+    print(iferr(w[i]/v[j],E,E));
+    print(iferr(w[i]\v[j],E,E));
+    print(iferr(w[i]%v[j],E,E));
+    print(iferr(w[i]\/v[j],E,E));
+  )
+)
+}
+for (i=2,#w, print(w[i]%2))
+for (i=2,#w, print(w[i]\2))
+divrem(x+y^2,y+x,y)
+divrem([3,5],2)
+divrem(1,x)
+divrem(1,Pol(1))
+divrem(1,"a")
+
+(5/3) \/ 1
+
+floor((x^2+1)/x)
diff --git a/src/test/in/ell b/src/test/in/ell
new file mode 100644
index 0000000..31e13b1
--- /dev/null
+++ b/src/test/in/ell
@@ -0,0 +1,271 @@
+default(realprecision,38);
+ellap(ellinit([1,0,1,4,-6]), 2)
+ellap(ellinit([0,17]), 2)
+ellap(ellinit([0, 0, 1, -1, 0]),2486246173)
+
+testgroup(v,p)=
+{ my(E,F,G,g);
+  E=ellinit(v); if(!E || E.disc%p==0, next);
+  E[13] = 0; /*allow singular curves*/
+  G=ellgroup(E,p,1); if (G.no == 1, return);
+  g=G.gen; if (!ellisoncurve(E,g), error(E, G));
+  F=vector(#g,i, ellorder(E,g[i],G.no));
+  if ((p <= 3 && F!=G.cyc) || (p > 3 && F[1] != G.cyc[1]),
+    print("error:",v,":",F,":",G));
+}
+{
+  for(x=10,63,
+    my(p,E,G,g);
+    p=nextprime(2^x);E=ellinit([0,0,1,2,3]);
+    G = ellgroup(E,p,1); if (!ellisoncurve(E,G.gen), error(E));
+    if(ellorder(E,G.gen[1])!=G.cyc[1], error(E));
+    print(p,":",ellgroup(E,p)));
+  for(p=2,3,
+    forvec(v=vector(5,i,[0,p-1]), testgroup(v,p)));
+  forvec(v=vector(2,i,[0,4]), testgroup(v,5));
+}
+setrand(1)
+a=ffgen(2^8,'a); E=ellinit([a,1,0,0,1]);
+P=[a^3,ellordinate(E,a^3)[1]]; Q=ellmul(E,P,113);
+e=elllog(E,P,Q,242)
+ellmul(E,Q,e) == P
+ellpow(E,Q,e) == P
+
+p=655637;
+E=ellinit([123,47], p);
+X=1;until(Y!=[],X++;Y=ellordinate(E,X));
+P=[X,Y[1]]; Q=ellmul(E,P,113);
+o=ellorder(E,P, p+1-ellap(E,p))
+e=elllog(E,P,Q,o)
+ellmul(E,Q,e) == P
+
+p=1073741827;
+E=ellinit([1,3], p);
+G=[Mod(1050932506,p),Mod(12325986,p)];
+P=ellmul(E,G,1023);
+elllog(E,P,G)
+
+ellorder(ellinit([0,2],1),[-1,-1]*Mod(1,997))
+
+E = ellinit([-2147484185^2,0]);
+elltors(E)
+ellorder(E, [0,0])
+ellorder(E, [2147484185, 0])
+ellorder(E, [2147484185/3, 1/11])
+E = ellinit([1,1]); P = [72, 611];
+ellorder (E, ellmul(E, P, 20))
+
+ellinit([a1,a2,a3,a4,a6]*Mod(1,5));
+ellinit([a1,a2,a3,a4,a6]);
+ellinit(ellfromj(0)).j
+ellinit(ellfromj(1728)).j
+ellinit(ellfromj(j)).j
+ellinit(ellfromj(Mod(0,2))).j
+ellinit(ellfromj(Mod(0,5))).j
+ellinit(ellfromj(Mod(3,5))).j
+ellinit(ellfromj(j*Mod(1,2))).j
+ellinit(ellfromj(Mod(0,3))).j
+ellinit(ellfromj(j*Mod(1,3))).j
+a=ffgen(2^5,'a);ellinit(ellfromj(a)).j
+a=ffgen(3^5,'a);ellinit(ellfromj(a)).j
+elldivpol(ellinit([1,2,3,5,7]),4)
+elldivpol(ellinit([0,0,0,0,1]),8)
+
+e = ellinit([1.,2,3,4,5]);
+e.eta
+E = ellchangecurve(e, [2,3,4,5]);
+E.eta
+e = ellinit([1,2,3,4,5]); e.eta; e.roots;
+E = ellchangecurve(e, [2,3,4,5]);
+E.omega
+E.eta
+E.roots
+ellglobalred(E)
+ellglobalred(e)
+
+E = ellchangecurve(e, [1,0,0,0]);
+
+E = ellchangecurve(e, [2,3,4,5]*Mod(1,7));
+E.omega
+E.group
+
+ellminimalmodel(ellinit([1/5,1/4,1/3,1/2,1],1),&v)
+v
+
+j=ffgen(2^5,'a);e = ellinit(ellfromj(j)); e.group; elllog(e,e.gen[1],e.gen[1])
+E = ellchangecurve(e,[3,7,1,0]);
+E.group
+
+e = ellinit(ellfromj(Mod(1,3))); e.group; elllog(e,e.gen[1],e.gen[1])
+E = ellchangecurve(e,[2,7,1,0]);
+E.group
+
+e = ellinit(ellfromj(Mod(1,5))); e.group; elllog(e,e.gen[1],e.gen[1])
+E = ellchangecurve(e,[2,7,1,0]);
+E.group
+
+e = ellinit(ellfromj(1/3),  O(3^5)); e.tate
+e.roots
+E = ellchangecurve(e,[3,1,1,2]);
+E.tate
+iferr(ellztopoint(e,3),E,E)
+
+e = ellinit(ellfromj(11/8),  O(2^5));
+e.tate
+
+\\#1185
+ellwp(ellinit([0,-1,1,-10,-20]),x+O(x^12))
+
+\\#1186
+ellmul(ellinit([3,0]), [1,2], -quadgen(-4))
+
+logsigma(e,z='x)=if(type(z) != "t_POL" && type(z) != "t_SER", ellsigma(e,z,1));
+v = [ellwp,ellzeta,ellsigma,logsigma];
+e = ellinit([1,1]);
+w = ellperiods([1,I])
+w2 = ellperiods([1,I], 1)
+u = [e,w,w2];
+{
+for (i = 1, #v,
+  my(f = v[i]);
+  for (j = 1, #u,
+    my (a = u[j]);
+    print([i,j]);
+    print(f(a));
+    print(f(a, x+O(x^10)));
+    print(f(a, 1/3));
+    print(f(a, I/3));
+    print(f(a, (1+I)/3));
+  )
+)
+}
+elleta(e)
+elleta([1,I])
+v = [x->elleisnum(x,2),x->elleisnum(x,4,1),x->elleisnum(x,6,1),x->elleisnum(x,10)];
+{
+for (i = 1, #v,
+  my(f = v[i]);
+  print(f);
+  print(f(e));
+  print(f(w));
+  print(f(w2));
+)
+}
+
+\\ #1257
+ellrootno(ellinit([0,-1,1,217,-282]))
+
+\\ #1296
+e=ellinit([0,-1,0,-33,62]);
+ellztopoint(e,-2.5261979245524788020279452840822073870+0.E-36*I)
+
+\\ #1308
+ellinit([108/91,11664/8281,-6561/8281,708588/753571,-14348907/68574961]).disc
+
+do(e)=elltors(ellinit(e));
+do([0, -1, 1, -7820, -263580])
+do([1, 0, 1, -171, -874])
+do([0, 1, 1, -9, -15])
+do([1, 1, 1, -80, 242])
+do([0, -1, 1, -10, -20])
+do([1, 0, 1, 4, -6])
+do([1, -1, 1, -3, 3])
+do([1, 1, 1, 35, -28])
+do([1, -1, 1, -14, 29])
+do([1, 0, 0, -45, 81])
+do([1, -1, 1, -122, 1721])
+
+do([1, 1, 1, -135, -660])
+do([1, 1, 1, -10, -10])
+do([1, 0, 1, -19, 26])
+do([1, 0, 0, -1070, 7812])
+do([1,0,0,-372368141774940800,87459461608665181808640000])
+do([0,706607569223786457,0,-1866575649655837263252847197205171425,-1298198297451307472292414787720779720378300792679274425])
+
+e=ellinit([1,3.+I]); x=1/2; y=ellordinate(e,x)[1];
+ellztopoint(e,ellpointtoz(e,[x,y]))
+e=ellinit([0,-1,1,0,0], O(11^5));
+ellpointtoz(e,[0,0])
+e=ellinit([1,1,1,-10,-10], O(3^5));
+ellpointtoz(e,[3,-2])
+e=ellinit(ellfromj(2/9), O(3^10)); x=2; y=ellordinate(e,x)[1];
+ellpointtoz(e,[x,y])
+e=ellinit(ellfromj(1/4), O(2^10)); x=1/2; y=ellordinate(e,x)[1];
+ellpointtoz(e,[x,y])
+
+ellinit([1,1], Mod(1,11))
+ellrootno(ellinit([31^4,31^6]), 31)
+e=ellinit([1,0,0,1,1]);
+ellordinate(e, I)
+E=ellchangecurve(e,[1/(2^4*3^4*5^2*7),2,3,4]);
+forprime(p=2,11, if (ellap(e,p) != ellap(E,p),error(p)));
+for(k=2,50, if (ellak(e,k) != ellak(E,k),error(k)));
+if (ellan(e,100) != ellan(E,100),error("ellan"));
+P=ellchangepoint([0,1],[1,2,3,4])
+ellchangepointinv(P,[1,2,3,4])
+
+\\#1416
+E=ellinit([155818018413/16,-78179511999813417/32]);
+ellminimalmodel(E,&v);
+E2=ellchangecurve(E,v); ellminimalmodel(E2,&w); w
+
+\\#1432
+E=ellinit([-3,-60,480,0,0]); ellheight(E,[0,0])
+
+e=ellminimalmodel(ellinit([1,1]));
+e=ellchangecurve(e,1)
+e=ellchangecurve(e,[2,0,0,0])
+ellinit(ellinit([0,1]*Mod(1,5),ffgen(5^2)));
+
+J=[0,1728,-3375,8000,54000,-32768,287496,-884736,-12288000,16581375,-884736000,-147197952000,-262537412640768000];
+{
+for (i=1,#J,
+  my(e = ellinit(ellfromj(J[i])));
+  my(v = ellan(e,200));
+  print("\n", e.j);
+  forprime(p = 127, 200, print1(v[p]," "))
+);
+}
+
+p=2^32+15;
+ellcard(ellinit([1,2,3,4,5], p))
+E=ellinit([625,15625]);
+elllocalred(E,5)
+ellisoncurve(E,[0.,125.])
+ellisoncurve(E,[0.,125+1e-50])
+elladd(E,[0.,125.],[0.,125+1e-38])
+iferr(ellmul([0,1,0,2,-15],[2,1],5),E,E)
+x='x;
+E=ellinit([x^2,x])
+ellminimalmodel(E)
+ellweilpairing(E,[0],[0],1)
+ellinit([1])
+ellinit([1,1],quadgen(5))
+ellinit([Mod(1,2),1],O(2))
+ellinit([O(2),1],ffgen(2^3))
+ellinit([O(2),1],1.)
+ellinit([1,2],1.)
+ellinit([ffgen(5),1],5)
+ellinit([ffgen(5),1],3)
+ellinit([1.,1],precision(1.,60))
+ellinit([1.,Mod(1,3)])
+
+\\#1527
+E = ellinit([0,0,0,-82,0]);
+ellrootno(E,2)
+ellrootno(E)
+ellrootno(E,2)
+
+E=ellinit([0,0,0,Mod(x,x^2+5),0]);
+ellwp(E)
+ellzeta(E)
+ellsigma(E)
+
+E=ellinit([0,0,0,Mod(1,1009),0]);
+ellwp(E)
+ellzeta(E)
+ellsigma(E)
+
+\\#1558
+ellap(ellinit([-1137195,489565862]),2038074751)
+ellap(ellinit([582304190,64196421]),2147438927)
diff --git a/src/test/in/ellanal b/src/test/in/ellanal
new file mode 100644
index 0000000..4d0cae6
--- /dev/null
+++ b/src/test/in/ellanal
@@ -0,0 +1,18 @@
+default(realprecision,38);
+rk(x)=x=ellinit(x);ellanalyticrank(x);
+rk([0, -1, 1, -10, -20])
+rk([0, 0, 1, -1, 0])
+rk([0, 1, 1, -2, 0])
+rk([0, 0, 1, -7, 6])
+rk([-5187, 176830])
+
+he(x)=x=ellinit(x);ellheegner(x);
+he([1, 1, 0, -1297, -18530])
+he([0, -1, 1, -33, 93])
+he([-157^2,0])
+he([0,0,-9/484,0,-27/234256])
+getheap()[1]
+
+allocatemem(30000000);
+E=ellinit([0,-1437004800,0,458885065605120000,0]);
+ellglobalred(E); ellheegner(E)
diff --git a/src/test/in/ellff b/src/test/in/ellff
new file mode 100644
index 0000000..4594564
--- /dev/null
+++ b/src/test/in/ellff
@@ -0,0 +1,133 @@
+\\package:seadata
+test(p,n=0,v='a,w=1)=
+{
+  my(a=if(n,ffgen(p^n,v),p));
+  my(E=ellinit([w,1,1-w,0,a+3],a));
+  my(G=ellgenerators(E));
+  if (#G==0,return);
+  [d1]=ellgroup(E);
+  my(P=random(E));
+  G=G[1];
+  if(ellorder(E,G)!=d1,error([p,n,0]));
+  if(ellmul(E,G,d1)!=[0],error([p,n,1]));
+  if(ellmul(E,P,d1)!=[0],error([p,n,2]));
+  if(d1%ellorder(E,P)!=0,error([p,n,3]));
+  if (d1<10^7,
+    P=ellmul(E,G,1023);
+    if(elllog(E,P,G)!=1023%d1,error([p,n,4]));
+  );
+}
+test(2);
+test(2,78);
+test(2,100);
+test(2,1,,0);
+test(2,2,,0);
+test(2,4,,0);
+test(2,6,,0);
+test(2,101,,0);
+test(3,,,0);
+test(3,50,,0);
+test(3,51,,1);
+test(5);
+test(5,3);
+test(5,50);
+test(7,3);
+test(7,51);
+test(11,2);
+test(13,41);
+test(17,2);
+test(1009,3);
+test(1013,7);
+test(1009,11,'x);
+test(17);
+test(41);
+test(1073741827);
+test(nextprime(2^65),2);
+
+a=ffgen(101^3,'a);
+E=ellinit([1,3],a); E.j
+E.disc
+P=random(E);Q=random(E);
+R=elladd(E,P,Q);
+elladd(E,ellsub(E,R,P),ellneg(E,Q))
+N=ellcard(E);ellmul(E,P,N)
+
+check(a)=
+{
+  my(E,P,N);
+  E=ellinit(ellfromj(a),a);
+  N=ellcard(E);
+  if ((N==1)!=(#random(E)==1),error(a));
+  for(i=1,4,
+    P=random(E);
+    if(ellmul(E,P,N)!=[0],error(a)));
+  ellgenerators(E);
+}
+{
+  for(a=1,8,
+    g = ffprimroot(ffgen(2^a,'t));
+    for(i=0,2^a-2, check(g^i)));
+  for(a=1,6,
+    g = ffprimroot(ffgen(3^a,'t));
+    for(i=0,3^a-2, check(g^i)));
+  for(a=1,4,
+    g = ffprimroot(ffgen(5^a,'t));
+    for(i=0,5^a-2, check(g^i)));
+}
+
+checkt(p,n,f,B=100)=
+{
+  my(a=ffgen(p^n,'a));
+  for(i=1,B,
+    my(E,N,b);
+    until(b,b=random(a));
+    E=ellinit(if(f==0,[0,b],f==1,[b,0],[b^2,b^3]));
+    if(#E==0,next);
+    N=ellcard(ellinit(E,a));
+    if(#ellmul(E,random(E),N)>1,error([p,n,f],b)));
+}
+checkt(3,5,0);
+checkt(3,6,0);
+checkt(3,5,1);
+checkt(3,6,1);
+checkt(3,5,2);
+checkt(3,6,2);
+checkt(7,5,0);
+checkt(7,6,0);
+checkt(11,5,0);
+checkt(11,6,0);
+checkt(7,5,1);
+checkt(7,6,1);
+checkt(13,5,1);
+checkt(13,6,1);
+checkt(11,6,2);
+checkt(13,5,2);
+checkt(18446744073709551667,2,0,10);
+checkt(18446744073709551667,3,0,10);
+checkt(18446744073709551667,2,1,10);
+checkt(18446744073709551667,3,1,10);
+checkt(18446744073709551667,2,2,10);
+checkt(18446744073709551667,3,2,10);
+checkt(18446744073709551629,2,0,10);
+checkt(18446744073709551629,3,0,10);
+checkt(18446744073709551629,2,1,10);
+checkt(18446744073709551629,3,1,10);
+checkt(18446744073709551629,2,2,10);
+checkt(18446744073709551629,3,2,10);
+
+E=ellinit([a1,a2,a3,a4,a6]*Mod(1,2));
+elldivpol(E,2)
+
+check(q)=
+{ my(g,E,x = 1,y);
+  g = ffprimroot(ffgen(q,'t));
+  E = ellinit(ellfromj(g));
+  for(i=1,10,
+    x *= g;
+    y = ellordinate(E,x);
+    for(i=1,#y, if (!ellisoncurve(E,[x,y[i]]), error([x,y])))
+  );
+}
+check(2^4)
+check(3^4)
+check((2^64+13)^4)
diff --git a/src/test/in/ellglobalred b/src/test/in/ellglobalred
new file mode 100644
index 0000000..9f639a4
--- /dev/null
+++ b/src/test/in/ellglobalred
@@ -0,0 +1,22 @@
+\\package:elldata
+{
+  forell(E,1,9999,
+    N = ellconvertname(E[1])[1];
+    M = ellglobalred( ellinit(E[2], 1) )[1];
+    if (N != M, print(E," bad for N = ",N))
+  );
+}
+ellidentify(ellinit([1,1]))
+ellsearch("11a1")
+ellsearch("11a")
+ellsearch("11b")
+ellsearch("11")
+ellsearch([11,0,1])
+ellsearch([11,0])
+ellsearch([11])
+ellsearch(11)
+ellinit("11a1").j
+iferr(ellsearch([0,0,0,0]),E,E)
+iferr(ellsearch([0,0,0]),E,E)
+iferr(ellsearch("curve"),E,E)
+iferr(ellsearch(I),E,E)
diff --git a/src/test/in/elliptic b/src/test/in/elliptic
new file mode 100644
index 0000000..2888daf
--- /dev/null
+++ b/src/test/in/elliptic
@@ -0,0 +1,51 @@
+HEAP=[77, if(precision(1.)==38,5279,5458)];
+default(realprecision,154); Pi; default(realprecision,38);
+\e
+ellinit([-1,0])
+ellinit([-17,0],1)
+ellsub(%,[-1,4],[-4,2])
+ellj(I)
+\\
+acurve=ellinit([0,0,1,-1,0])
+apoint=[2,2]
+elladd(acurve,apoint,apoint)
+ellak(acurve,1000000007)
+ellan(acurve,100)
+ellap(acurve,10007)
+deu=direuler(p=2,100,1/(1-ellap(acurve,p)*x+if(acurve[12]%p,p,0)*x^2))
+ellan(acurve,100)==deu
+ellisoncurve(acurve,apoint)
+acurve=ellchangecurve(acurve,[-1,1,2,3])
+apoint=ellchangepoint(apoint,[-1,1,2,3])
+ellisoncurve(acurve,apoint)
+ellglobalred(acurve)
+ellheight(acurve,apoint)
+ellheight(acurve,apoint,1)
+ellordinate(acurve,1)
+ellpointtoz(acurve,apoint)
+ellztopoint(acurve,%)
+ellmul(acurve,apoint,10)
+ellwp(acurve, x+O(x^33))
+q*Ser(ellan(acurve,100),q)
+\\
+bcurve=ellinit([-3,0])
+elllocalred(bcurve,2)
+elltaniyama(bcurve)
+\\
+ccurve=ellinit([0,0,-1,-1,0])
+l=elllseries(ccurve,2)
+abs(elllseries(ccurve,2,1.2)-l) < 1.4e-38
+\\
+tcurve=ellinit([1,0,1,-19,26]);
+ellorder(tcurve,[1,2])
+elltors(tcurve)
+\\
+mcurve=ellinit([-17,0])
+mpoints=[[-1,4],[-4,2]]~
+mhbi=ellbil(mcurve,mpoints,[9,24])
+ma=ellheightmatrix(mcurve,mpoints)
+matsolve(ma,mhbi)
+\\
+cmcurve=ellinit([0,-3/4,0,-2,-1])
+ellmul(cmcurve,[x,y],quadgen(-7))
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/ellsea b/src/test/in/ellsea
new file mode 100644
index 0000000..d5f696e
--- /dev/null
+++ b/src/test/in/ellsea
@@ -0,0 +1,21 @@
+\\package:seadata
+ellmodulareqn(5)
+do(i,v)=
+{
+  E = ellinit([0,0,0,v[2],v[3]]*Mod(1,v[1]));
+  print(i, ": ", ellap(E,v[1]));
+}
+{
+v=[[202600005603433095160409308644759862837,25496852782325453225973142890909600552,129550610797481291887769966647995045232],
+[173327739907566197112155895875385467119,52716988591102938437323369716512206005,43087597392844950895070462564402654315],
+[523583591335747530615071369664554118036421993253,25679429559575246581833628827363203226862930934,78220741356817481602535950765825830003112603824],
+[1319450668936329467137913739322239157303860926441,807652438980115949692649657326438677571309575087,1017125626316888896817395440127041355136940446205],
+[439581010348913995032270658729785287035964480323270935583,323922016281172901245123590380881598241426088528431020005,54496426275749371996644207660602248980615186517525561222],
+[2979720374579183569554262247145622188470249961843364603751,428654869348535206084607029945439317783967748844874233571,1182279475380088064870625220629639405548336474256523329003],
+[6243380271698146227966925307851825694742847655729810693741,4068721281680536125235363885580194460678653324971583338307,2519148351962491328666249705249360758373031631978108875818],
+[1606938044258990275541962092341162602522202993782792835304761,1,252199199707645577897249048746397012330572101453777389069968],
+[1267650600228229401496703205953,1,417990942431022911086532367249],
+[590295810358705651741,1,3],
+[18446744073709551629,1,42]];
+}
+for(i=1,#v, do(i,v[i]));
diff --git a/src/test/in/ellweilpairing b/src/test/in/ellweilpairing
new file mode 100644
index 0000000..67e3b6b
--- /dev/null
+++ b/src/test/in/ellweilpairing
@@ -0,0 +1,61 @@
+weil(E,P,Q,m,p)=lift(ellweilpairing(E,P,Q,m));
+tate(E,P,Q,m,p)=if(p%m!=1,return(0));lift(elltatepairing(E,P,Q,m)^((p-1)/m));
+check(v,P,Q,m,p)=
+{
+  my (E=ellinit(v*Mod(1,p)));
+  P*=Mod(1,p); Q*=Mod(1,p);
+  print([weil(E,P,Q,m,p),tate(E,P,Q,m,p),ellgroup(E)]);
+}
+
+check([0,0,1,0,0],[0,0],[57,46],3,103)
+check([0,0,1,0,0],[64,63],[0,0],3,109)
+check([0,0,1,0,0],[0,0],[150,32],3,151)
+check([0,0,1,0,0],[0,156],[13,144],3,157)
+check([0,0,1,0,0],[0,0],[59,58],3,163)
+check([0,0,1,0,0],[192,84],[0,192],3,193)
+check([0,0,1,0,0],[198,92],[0,0],3,199)
+check([0,0,0,1,0],[1,51],[72,9],4,113)
+check([0,0,0,1,0],[88,56],[1,31],4,137)
+check([1,0,0,0,3],[13,6],[147,89],4,149)
+check([0,0,1,0,0],[5,58],[36,128],4,157)
+check([0,0,1,0,3],[30,1],[26,1],5,31)
+check([0,0,1,0,0],[58,107],[22,76],6,109)
+check([0,0,1,0,0],[90,4],[32,1],6,127)
+check([0,0,1,0,0],[138,60],[62,155],6,157)
+check([0,0,0,1,0],[47,53],[160,147],7,197)
+check([0,0,0,1,0],[50,80],[16,65],8,113)
+check([1,0,0,0,3],[25,82],[23,49],8,149)
+check([0,0,1,0,0],[10,67],[88,35],9,127)
+check([0,0,1,0,0],[102,32],[87,55],9,163)
+check([0,0,1,0,0],[12,11],[17,5],9,19)
+check([0,0,1,0,0],[190,47],[194,169],9,199)
+check([0,0,0,1,0],[28,34],[22,11],10,137)
+check([0,0,0,1,0],[94,84],[10,142],10,157)
+check([0,0,0,1,0],[159,29],[115,100],14,197)
+check([0,0,0,1,0],[154,21],[58,126],15,157)
+check([0,0,0,1,0],[121,63],[121,64],16,127);
+check([0,0,0,1,0],[18,104],[177,153],16,191);
+check([0,0,1,0,0],[16,56],[16,56],2,113);
+check([0,0,1,0,0],[66,63],[66,63],2,127);
+check([0,0,1,0,0],[126,78],[89,78],2,157);
+check([0,0,1,0,0],[150,89],[150,89],2,179);
+check([0,0,1,0,0],[22,95],[22,95],2,191);
+check([0,0,0,1,3],[4,1],[4,6],6,7);
+
+t=ffgen(Mod(1,2)*(t^6+t^5+t^3+t^2+1));
+E=ellinit([0,0,1,0,0],t);
+P=[t^2+t+1,t+1];
+Q=[t^5+t^3+t^2,t^5+t^4+t^3+t^2+t+1];
+ellweilpairing(E,P,Q,3)
+elltatepairing(E,P,Q,3)
+
+test(p) =
+{
+  forvec(v=vector(5,i,[0,p-1]),
+    E=ellinit(v*Mod(1,p));
+    if (!#E, next);
+    G=ellgroup(E);if(#G>1,print(v,":",G)),
+  );
+}
+test(3);
+test(5);
diff --git a/src/test/in/env b/src/test/in/env
new file mode 100644
index 0000000..7efb0bd
--- /dev/null
+++ b/src/test/in/env
@@ -0,0 +1,5 @@
+Strexpand("~root")
+#Strexpand("~") > 1
+Strexpand("$AAA, $BBB, $AAA")
+getenv("AAA")
+getenv("__DOES_NOT_EXIST__")
diff --git a/src/test/in/equal b/src/test/in/equal
new file mode 100644
index 0000000..aa1411e
--- /dev/null
+++ b/src/test/in/equal
@@ -0,0 +1,24 @@
+[O(3)===O(3), 1+O(3)===2+O(3), 1+O(3)==O(3^-1), 1+O(3)==2+O(3)]
+[O(x)===O(y), 2+O(x)===1+O(x)]
+[1.===1., 1.===-1.]
+f(x)=1;
+g(x)=1;
+h()=1;
+[f===g, f===h]
+[3/2===-3/2, 3/2===3/2]
+m=Mod(1,2);
+[Mod(1,3)===m, m===Mod(0,2), m===m]
+m=Mod(1,x);
+[m===Mod(Pol(1),x), m===Mod(1,y), m===m]
+t=ffgen(8);
+[t===t+1, t===ffgen(4), t===t]
+q=Qfb(1,2,3);
+[q===Qfb(1,2,2), q===Qfb(1,3,3), q===Qfb(2,2,3), Qfb(1,0,-2)===Qfb(1,0,-2,1.), q===q]
+Qfb(1,0,-2)==Qfb(1,0,-2,1.)
+1/x===1/y
+[[1,2,3]===[1,2], [1,2,3]===[1,2,4], [1,2,3]===[1,2,3.], [1,2]===[1,2]]
+quadgen(5)==quadgen(13)
+["ab"==="ac", "ab"==="ab"]
+L=List([1,2]);
+[L===List([]), L===List([2,1]), L===List([1]), L===L]
+[[]==0, []~==0]
diff --git a/src/test/in/err b/src/test/in/err
new file mode 100644
index 0000000..907dd0e
--- /dev/null
+++ b/src/test/in/err
@@ -0,0 +1,252 @@
+f(x) = 1/x;
+g(N) = for(i = -N, N, f(i));
+g(10)
+print();
+
+f=matsolve;
+f(Mat(0), Col(1))
+print();
+
+(matsolve)(Mat(0), Col(1))
+print();
+
+a.foo=1/(1+a^2);
+g(x)=[x.foo];
+g(I)
+print();
+
+(x->1/x)(0)
+print();
+
+ecm(N,t,B)=
+{
+  for(a=1,t,
+     iferr(
+       my(E=ellinit([0,0,0,a,1]*Mod(1,N)));
+       ellmul(E,[0,1]*Mod(1,N),B),
+       err,if(errname(err)=="e_INV",return(gcd(lift(component(err,2)),N))
+            ,error(err))));
+}
+ecm(2^64+1,10,200!)
+ecm2(N,t,B)=
+{
+  for(a=1,t,
+     iferr(
+       my(E=ellinit([0,0,0,a,1]*Mod(1,N)));
+       ellmul(E,[0,1]*Mod(1,N),B),
+       err,return(gcd(lift(component(err,2)),N))
+          ,errname(err)=="e_INV"));
+}
+ecm2(2^64+1,10,200!)
+
+rev(A) = my(B=Ser(A)); serreverse(B);
+iferr(rev([0,0,0,2,2,4,8,4,16,12]),E,0);
+\\ e_COPRIME
+T=t^2-2;p=2;A=x^2+1;B=[x+t,x+t];r=polhensellift(A,B,[p,T],6)
+\\ e_DIM / e_TYPE
+M = matid(2);
+M[,1] = 1
+M[,3] = 1
+M[,1] = [1,2]
+M[,1] = [1,2,3]~
+M[1,] = 1
+M[3,] = 1
+M[1,] = [1,2]~
+M[1,] = [1,2,3]
+
+[;][1,]
+[;][,1]
+1[1]
+issquare(1,&v[1])
+1[1,1]
+1[,1]
+issquare(1,&v[,1])
+1[1,]
+issquare(1,&v[1,])
+
+v=Vecsmall([1]);
+v[2] = 1
+v[1] = Pi
+v[1] = 2^64
+v[Pi] = 1
+
+\\ RgM_check_ZM
+M = [1.,0;0,1]; qflll(M,1)
+\\ RgV_check_ZV
+addprimes(1.)
+
+\\e_MODULUS
+nfalgtobasis(nfinit(t^3-2),Mod(t,t^2+1))
+\\e_DIM
+vector(-1,i,0)
+vectorv(-1,i,0)
+vectorsmall(-1,i,0)
+matrix(-1,1,i,j,0)
+matrix(1,-1,i,j,0)
+
+next(-1)
+break(-1)
+v[-1]
+v[#v+1]
+subst(x,1,0)
+exp(1/x)
+cos(1/x)
+sin(1/x)
+tan(1/x)
+cotan(1/x)
+atan(1/x)
+asin(1/x)
+acos(1/x)
+asinh(1/x)
+acosh(1/x)
+atanh(1/x)
+lngamma(x)
+besselj(2,1/x)
+besseljh(2,1/x)
+besselk(2,1/x)
+besselk(1/3,O(x))
+besseln(2,1/x)
+besseln(1/3,O(x))
+polylog(2,1/x)
+sqrt(x)
+sqrt(2+O(2^2))
+sqrtn(x,3)
+sqrtn(2+O(2^2),3)
+log(x)
+log(0)
+abs(x+O(x^2))
+vecmax([])
+vecmax([], &i)
+vecmin([])
+vecmmin([], &i)
+vecmax(matrix(0,2))
+L=List();
+listput(L, x, -1)
+listinsert(L,x,-1)
+listinsert(L,x,10)
+listpop(L)
+ellj(Mod(1,2))
+ellj(Qfb(1,1,1))
+eta(1+O(2))
+eta(1/x)
+K = nfinit(y^2+1);
+idealhnf(K, Qfb(1,1,1))
+idealfactor(K, [;])
+idealval(K, [;], idealprimedec(K,2)[1]);
+idealdiv(K,2,0, 1);
+valuation(Pi,2)
+x^Pi
+x^x
+0^0.
+agm([],[])
+sin(1/2+O(2^1))
+cos(1/9+O(3^1))
+exp(1/9+O(3^1))
+G=[[Vecsmall([6,11,14,16,17,1,21,24,26,27,2,29,30,3,31,4,5,34,36,37,7,39,40,8,41,9,10,42,12,13,15,44,45,18,46,19,20,47,22,23,25,28,48,32,33,35,38,43]),Vecsmall([5,10,13,15,1,17,20,23,25,2,27,28,3,30,4,31,6,33,35,7,37,38,8,40,9,41,11,12,42,14,16,43,18,45,19,46,21,22,47,24,26,29,32,48,34,36,39,44]),Vecsmall([15,25,28,1,4,31,35,38,2,9,41,3,12,42,5,6,16,43,7,19,46,8,22,47,10,11,26,13,14,29,17,18,32,48,20,21,36,23,24,39,27,30,33,34,44,37,40,45]),Vecsmall([30,40,1,42,14,13,45,2,47,24,23,4,5,6, [...]
+bnrstark(bnrinit(bnfinit(y^2+1),2,1))
+bnrstark(bnrinit(bnfinit(y^2-2),[4,[1,1]],1))
+quadray(-16,1)
+quadray(bnfinit(x^3-2),1)
+galoissubcyclo(-1)
+galoissubcyclo(6,Mod(1,3))
+galoissubcyclo(6,[;])
+galoissubcyclo(6,Mat(1))
+galoissubcyclo(znstar(5),matid(2))
+galoissubcyclo(bnrinit(bnfinit(y^2+1),1,1), 2)
+polsubcyclo(-1,2);
+polsubcyclo(2,-1);
+random(-1)
+znprimroot(0)
+sqrtint(-1)
+sqrtnint(-1,2)
+sqrtnint(2,-2)
+znprimroot(8)
+polroots(x^2+Mod(1,2))
+prime(-2)
+addprimes(-1)
+padicappr(x^2+1+O(3^5), 1+O(5))
+factorpadic(x^2+1,2,-1)
+polrootspadic(x^2+1,2,-1)
+ellinit([1+O(3),1+O(5)])
+ellwp([1,I],I)
+ellsigma([1,I],x,1)
+ellsigma([1,I],1,1)
+E=ellinit([1,1]);
+ellap(E)
+ellap(E,1)
+ellap(E,'x)
+elltaniyama(E,-1)
+ellheight(E,[2,2])
+Qfb(0,0,0)
+quadpoly(2)
+qfbprimeform(2,5)
+qfbcomp(Qfb(1,1,1),Qfb(1,0,2))
+\\qfbcompraw(Qfb(21,1,2),Qfb(112,0,2))
+galoisinit(x^2)
+galoisinit(2*x)
+ellL1(1,-1)
+ellheegner(ellinit([0,-1,1,-10,-20]))
+ellheegner(ellinit([0,0,1,-7,6]))
+substpol(x+O(x^2),x^3,x)
+intformal(1/(x^2+1))
+component(x,-1)
+component(O(x),2)
+component(Vecsmall([]),1)
+polcoeff(O(x),2)
+polcoeff(x+O(x^2),2)
+polcoeff([],2)
+polcoeff([],-1)
+polcoeff("",2)
+matcompanion(0*x)
+matrixqz(Mat([1,2]))
+matrixqz(Mat(0))
+vecextract([1],[-1]);
+vecextract([1],[2]);
+idealfrobenius(K,galoisinit(K),idealprimedec(K,2)[1])
+nfisincl(x^2,x^2)
+polcompositum(x^2,x)
+rnfdedekind(K, x^2+x-1/3)
+hilbert(Mod(1,2),1)
+hilbert(Mod(1,3),Mod(1,5))
+hilbert(Mod(1,3),2,0)
+znorder(0)
+znorder(Mod(2,4))
+contfrac(1e100)
+contfrac(1.,[1],10)
+contfrac(1,,-1)
+contfracpnqn(matrix(3,1));
+divisors(1/2)
+idealstar(K,0)
+idealstar(K,1/2)
+idealaddtoone(K,[[;]])
+idealdiv(K,1,2,1)
+idealred(K,matid(2),[])
+idealtwoelt(K,matid(2),1/2)
+rnf=rnfinit(K,x^2-y); rnfeltdown(rnf, x)
+matid(-1)
+polinterpolate([1,1],[2,3],Pi)
+modreverse(Mod(-x^3+9*x,x^4-10*x^2+1))
+rnfnormgroup(bnrinit(bnfinit(K),9), x^3-2)
+concat([1,2],[3,4]~)
+concat([])
+concat(List())
+mathnfmod([1;2],2)
+polsturm(x^2)
+removeprimes(2)
+forstep(a=1,2,0,)
+e=ellinit([1,1+O(31^2)]);
+e.omega
+e.eta
+e.area
+e=ellinit([0,1]);
+e.tate
+ellorder(e, [0,0]*Mod(1,2))
+thue(x*(x^3-2),0);
+direuler(p=2, 10, 2/(1-p*X))
+solve(x=0,1,x^2+1)
+warning(1)
+\\ backward compatibility. Eventually remove
+inv(x) = trap (e_INV, INFINITY, 1/x)
+inv(2)
+inv(0)
+trap (e_INV, INFINITY, log(0))
diff --git a/src/test/in/exact0 b/src/test/in/exact0
new file mode 100644
index 0000000..e5121b6
--- /dev/null
+++ b/src/test/in/exact0
@@ -0,0 +1,28 @@
+default(realprecision,38);
+Mod(0,2)*x*1.
+Pol(Mod(0,2)) + 2
+Mod(1,2)+Pol(Mod(1,2))
+Ser(Mod(1,2)) + 1
+(2+0*I)+I*Mod(0,4)
+a=b=Mod(2, 4) + Mod(2, 4)*I; a*b
+(Mod(0,2)+I)^2
+Mod(0,2)/x
+a=Mod(1, 2)*x^10 + Mod(1, 2); 2*a
+a+a
+valuation(Mod(0,101),101)
+gcd(Mod(0,5),10)
+gcd(Mod(0,5),Mod(0,10))
+{
+v = [[0,0,0,0], [0,0,0,Mod(0,2)], [1,0,0,Mod(0,2)],
+     [1,0,0.,Mod(0,2)], [1,0,Mod(0,2),0.]];
+for (i = 1, #v,
+  w = v[i];
+  print1(Pol(w), " ");
+  print1(Polrev(w), " ");
+  print1(Ser(w), " ");
+  w = vecextract(w, "-1..1");
+  print1(Pol(w), " ");
+  print1(Polrev(w), " ");
+  print1(Ser(w), " "); print();
+)
+}
diff --git a/src/test/in/extract b/src/test/in/extract
new file mode 100644
index 0000000..43983e0
--- /dev/null
+++ b/src/test/in/extract
@@ -0,0 +1,39 @@
+A=vector(100,i,i);
+for(i=35,99, print1(vecextract(A,1<<i)))
+for(i=20,25, print(vecextract(A,1<<100-2-1<<i-1<<(2*i)-1<<(3*i))))
+vecextract(A,"2..4")
+vecextract(A,"-2..-4")
+vecextract(A,"^65")
+vecextract(A,"^2..99")
+vecextract(A,"^99..2")
+vecextract(A,Vecsmall([3,5]))
+
+A=matid(3);
+vecextract(A,"2..","..")
+vecextract(A,"-2..","-1..")
+matsize(vecextract(A, 0, 3))
+matsize(vecextract(A, [], 3))
+matsize(vecextract(A, "2..3", []))
+matsize(vecextract(A, [1,2], []))
+matsize(vecextract(A, Vecsmall([1,2]), []))
+matsize(vecextract(matid(6), 3, 0))
+
+fun = [Vec,Vecrev,Col,Colrev,Vecsmall];
+obj = [1, 'x, 1/x, Pol([5,4,3,2,1]), x+2*x^2+O(x^3),[1,2], Vecsmall([1,2]), List([1,2,3]), List([1,2,3,4,5]), "ab", [1,2,3;3,4,5], iferr(1/0,E,E)];
+test(f)= [ print(iferr(f(p),E,E)) | p<-obj ];
+[if(1,test(f); test(x->f(x,4)); test(x->f(x,-4))) | f <- fun];
+test(x->component(x,2));
+test(x->component(x,10));
+
+test(x->polcoeff(x,-1));
+test(x->polcoeff(x,2));
+test(x->polcoeff(x,10));
+polcoeff(O(x),0)
+s=x*(y+O(x))+y;
+polcoeff(s,0,y)
+polcoeff(s,1,y)
+s=y+O(y^2);
+polcoeff(s,0,x)
+polcoeff(s,1,x)
+
+vecextract(Vecsmall([3,4,5]),[2,3,1])
diff --git a/src/test/in/factor b/src/test/in/factor
new file mode 100644
index 0000000..ecddc15
--- /dev/null
+++ b/src/test/in/factor
@@ -0,0 +1,30 @@
+default(realprecision,38);
+factor(x^2+I)
+factor(x^2-1.)
+factor(x^2+I+1.)
+factor(x^2+Mod(1,5))
+factor(x^2+Mod(1,3)+I)
+factor(x^2+Mod(1,5)*I)
+factor(x^2+(1+O(5))*I)
+factor(x^2+(1+O(3))+I)
+factor(x^2+(1+O(5)))
+factor(x^2+quadgen(-3))
+factor(x^2+quadgen(-3)*Mod(1,3))
+factor(x^2+quadgen(-3)*Mod(1,5))
+factor(x^2+quadgen(-3)*(1+O(5)))
+factor(x^2+quadgen(-3)*(1+O(3)))
+factor(x^2+Mod(y,y^2+1))
+factor(x^2+Mod(y*Mod(1,3),y^2+1))
+factor(x^2+Mod(y*Mod(1,5),y^2+1))
+factor(x^2+Mod(y*(1+O(3)),y^2+1))
+factor(5/2 + I/3)
+factor(5^4 + 5^4*I)
+factor(5 + I)
+factor(15+3*I)
+factor(4+4*I)
+factor((3+4*I)/25)
+q=31271192761826143388782348951/31274945109847936339856761591;
+factor(q,2)
+factor(q,500100)
+factor((x-2.)*(x^2+1))
+factor((x+1)^2/(x^2-4))
diff --git a/src/test/in/factorint b/src/test/in/factorint
new file mode 100644
index 0000000..0c73f43
--- /dev/null
+++ b/src/test/in/factorint
@@ -0,0 +1,2 @@
+factorint(-33623546348886051018593728804851,1)
+factorint(691160558642,1)
diff --git a/src/test/in/factormod b/src/test/in/factormod
new file mode 100644
index 0000000..605f176
--- /dev/null
+++ b/src/test/in/factormod
@@ -0,0 +1,29 @@
+print(lift(factorcantor(x^1024+1,12289)))
+setrand(4); lift(factormod(y^3-3*y-1,2238004061))
+\\#1451
+lift(factormod(x^31+x^30+x^29+x^28+x^26+x^24+x^23+x^21+x^16+x^15+x^14+x^11+x^10+x^9+x^8+x^7+x^3+x^2+x,2))
+polrootsmod(x^16-1,41,1)
+polrootsmod(x^5+x^2-4*x+2,5,1)
+polrootsmod(Pol(0),2)
+polrootsmod(Pol(1),2)
+factorcantor(x^3+x,2)
+factormod(Pol(0),2)
+factormod(Pol(1),2)
+factormod((x^2+x+1)*(x^3+x),2, 1)
+polrootsmod(x^5+4*x^4+2*x^3+x^2+4*x+2,7)
+
+p=2^64+13;
+polrootsmod(Pol(0),p)
+polrootsmod(Pol(1),p)
+polrootsmod(x,p)
+polrootsmod(2*x+1,p)
+factormod(x^2+1,p)
+factormod(x^2+1,p,1)
+factormod(x^2+3,p,1)
+
+factorcantor(x^2+1,p)
+factorcantor(Pol(0),p)
+factorcantor(Pol(1),p)
+
+polisirreducible((x^2+x+1)*Mod(1,2))
+polisirreducible((x^2+1)*Mod(1,p))
diff --git a/src/test/in/ff b/src/test/in/ff
new file mode 100644
index 0000000..6404029
--- /dev/null
+++ b/src/test/in/ff
@@ -0,0 +1,108 @@
+{
+test(p,f) = setrand(1); a = ffgen(p^f, 'a); [
+a^2+3*a+1,
+a/(1+a),
+2*(a+1)/3,
+1/5+a,
+if (6*a, 5/6/(a+1)),
+if (6*a, 5/6*(a+1)),
+shiftmul(a+1,10),
+if (2*a, shiftmul(a+1,-10)),
+a^-1,
+-a,
+sqr(a),
+sqrt(a^(2^10)),
+sqrtn((a^2+a+1)^3,3),
+sqrtn((a^2+a+1)^3,3,&z),
+z,
+if (ispower(a,3), a^(2/3)),
+norm(a^2+1),
+trace(a),
+charpoly(a),
+minpoly(a),
+conjvec(a),
+factor(x^6-a*x^3+1),
+ellinit([a,1]),
+a/x,
+(x+a)/(x-a),
+b=ffprimroot(a),
+fforder(a),
+b^fflog(a,b),
+factorff(x^2+x+a),
+polrootsff(x^2+x+a)
+];
+}
+default(echo,1);
+
+test(2, 20)
+test(7, 7)
+test(precprime(2^32), 3)
+test(nextprime(2^32), 3)
+
+test2(p)=
+{
+  ffgen(x*Mod(1,p));
+  g = ffprimroot(ffgen((x+1)*Mod(1,p)), &o);
+  print([g, o]);
+  fflog(g^17, g, o);
+}
+test2(2)
+test2(3)
+test2(46744073709551653)
+test2(precprime(1<<32))
+
+for(i=1,10,print(ffnbirred(11,i)));
+for(i=1,10,print(ffnbirred(11,i,1)));
+
+do(f,p,T)=centerlift(lift(polrootsff(f,p,T)));
+do(x^3+x^2+x-1,3,t^3+t^2+t-1)
+t = ffgen(3^3,'t); do((x^3+x^2+x-1)*t^0, t.p, t.mod)
+polrootsff(x^4+1,2,y^2+y+1)
+
+t = ffgen(7^4); fflog(t^6,t^2)
+
+t = ffgen(2^64)^((2^64-1)\5);1/t
+
+t = ffgen(('t^2+'t+1)*Mod(1,2));
+factorff(x^12 + t*x^10 + x^6 + (t+1)*x^2 + 1)
+
+\\ #1241
+polrootsff(x^2 - x - ffgen((v^2+1) * Mod(1,3)))
+\\ #1350
+polrootsff(2*x+1,2,y)
+sqrt(Mod(-1,4296540161))
+sqrt(Mod(-1,18446744073944432641))
+centerlift(factorcantor(prod(i=-10,10,(x^2-i)),2^64+13)[,1])
+#polrootsff(x^107+2*x^3+1,3,ffinit(3,107,'a))
+t = ffprimroot(ffgen(2^61)); fflog(t^1234567891012345678,t)
+t = ffprimroot(ffgen(3^23)); fflog(t^12345678910,t)
+t = ffprimroot(ffgen(5^23)); fflog(t^1234567891012345,t)
+t = ffprimroot(ffgen(5^17)); fflog(t^123456789101,t)
+ffgen(x^2+x+Mod(1,3))
+conjvec(Mod(x, x^2+Mod(1,3)))
+t = ffgen(5^4,'t);
+factor((x^24-1)*t^0)
+factorff(Pol(0),t.p,t.mod)
+factorff(Pol(1),t.p,t.mod)
+factorff(x^4-t,t.p,t.mod)
+
+test(q)=
+{
+  my(t = ffgen(q,'t), m=[t,t^2,1+t^3; 1+t,1+t^2,1+t^3]);
+  print(matker(m));
+  print(matimage(m));
+  print(matrank(m));
+  my(M = [t,2*t^0,3*t^0; t,t^2,1+t^3; 1+t,1+t^2,1+t^3]);
+  print(matdet(M));
+  print(M^(-1)*M);
+  my(v = [t^0, t^1, t^2]~);
+  print(M*v);
+}
+test(2^5)
+test(7^5)
+test((2^64+13)^5)
+
+p=2^64+13; g=ffprimroot(ffgen(p^2), &o); a=2*g^0;
+v=[I,-1,Mat(1),matid(2)/2];
+for(i=1,#v, print(iferr(fflog(a,g,v[i]),E,E)));
+g^fflog(a,g,o) == a
diff --git a/src/test/in/ffisom b/src/test/in/ffisom
new file mode 100644
index 0000000..4684cb9
--- /dev/null
+++ b/src/test/in/ffisom
@@ -0,0 +1,53 @@
+fpisom2(l,P,Q)=
+{
+        my(L);
+        x=variable(P);
+        L=factorff(lift(P),l,lift(subst(Q,x,MAXVARN)))[,1];
+        L=-subst(L,x,0);
+        subst(lift(L),MAXVARN,x)*Mod(1,Q);
+}
+fptest(l,P,Q)=
+{
+        my(C);
+        C=fpisom2(l,P,Q);
+        print(vector(length(C),i,if(subst(P,x,C[i])==0,0,error("fptest("a","l","P","Q")"))));
+        C;
+}
+print("-------------e=0--------------");
+fptest(13,x^4+2*x^2+2*x+1,x^4+2*x^3+2*x^2+1);
+fptest(131,x^10+126*x^5+78,x^10+128*x^5+70);
+fptest(11,x^3+2*x^2+6*x+7,x^3+5*x^2+3*x+6);
+fptest(1009,x^17+x+25,x^17+42*x^16+61*x^15+952*x^14+113*x^13+398*x^12+694*x^11+238*x^10+465*x^9+308*x^8+545*x^7+145*x^6+79*x^5+896*x^4+515*x^3+63*x^2+808*x+1008);
+fptest(1009,x^16+x^15+964*x^14+911*x^13+650*x^12+165*x^11+451*x^10+957*x^9+342*x^8+616*x^7+212*x^6+595*x^5+130*x^4+63*x^3+340*x^2+537*x+694,x^16+11);
+fptest(1009,x^20+595*x^19+863*x^18+194*x^17+127*x^16+364*x^15+31*x^14+869*x^13+422*x^12+663*x^11+669*x^10+28*x^9+9*x^8+937*x^7+35*x^6+292*x^5+302*x^4+441*x^3+863*x^2+118*x+1,x^20+919*x^19+582*x^18+634*x^17+881*x^16+563*x^15+966*x^14+892*x^13+894*x^12+40*x^11+322*x^10+961*x^9+431*x^8+172*x^7+641*x^6+599*x^5+1001*x^4+718*x^3+582*x^2+851*x+1);
+fptest(23,x^20+x+5,x^20+4*x^19+15*x^18+17*x^17+6*x^16+3*x^15+8*x^14+16*x^13+11*x^12+20*x^11+x^10+20*x^9+11*x^8+16*x^7+8*x^6+3*x^5+6*x^4+17*x^3+15*x^2+4*x+1);
+fptest(10007,x^30+9557*x^29+7812*x^28+7090*x^27+7645*x^26+4110*x^25+3307*x^24+5763*x^23+7900*x^22+3872*x^21+8123*x^20+4076*x^19+3265*x^18+3777*x^17+3398*x^16+5674*x^15+4018*x^14+6820*x^13+6479*x^12+984*x^11+5652*x^10+1129*x^9+7573*x^8+1822*x^7+837*x^6+4169*x^5+4787*x^4+1616*x^3+5185*x^2+2649*x+1933,x^30+x+2);
+fptest(67108879,x^30+67107859*x^29+502860*x^28+41752426*x^27+47923483*x^26+56252217*x^25+29702433*x^24+34566275*x^23+43724662*x^22+43031233*x^21+6098024*x^20+7989587*x^19+27885185*x^18+50348895*x^17+46982824*x^16+27081672*x^15+64032686*x^14+24948096*x^13+22483934*x^12+62577008*x^11+33925741*x^10+21192636*x^9+60947997*x^8+24913164*x^7+28577178*x^6+19817925*x^5+12532882*x^4+28467302*x^3+18972253*x^2+4366256*x+32457808,x^30+50150808*x^29+63186895*x^28+49093281*x^27+9998922*x^26+33903391*x^2 [...]
+print("-------------e=1--------------");
+fptest(11,x^11+x^9+9*x^7+3*x^6+8*x^5+7*x^4+5*x^3+x^2+10*x+3,x^11+10*x+1);
+fptest(7,x^14+x+4,x^14+5*x^8+5*x^7+x^2+2*x+5);
+fptest(5,x^30+4*x^26+4*x^25+3*x^20+3*x^16+3*x^12+3*x^8+4*x^6+3*x^4+x^2+x+4,x^30+x^3+x+3);
+fptest(7,x^35+2*x^2+x+6,x^35+2*x^28+6*x^24+3*x^23+4*x^22+4*x^21+3*x^17+5*x^16+2*x^15+x^14+5*x^13+x^12+4*x^11+4*x^10+5*x^9+4*x^8+3*x^7+2*x^6+6*x^5+3*x^4+x^3+2*x^2+2*x+3);
+print("-------------e=2--------------");
+fptest(2,x^4+x+1,x^4+x^3+1);
+fptest(5,x^25+2*x^3+3*x+2,x^25+2*x^19+3*x^17+4*x^16+2*x^15+x^14+x^13+3*x^12+2*x^11+4*x^10+2*x^8+4*x^7+4*x^5+2*x^4+3*x^3+2*x+2);
+fptest(3,x^18+x^3+2*x+1,x^18+x^17+2*x^16+2*x^15+x^14+x^13+x^12+x^11+x^10+2*x^9+x^7+2*x^5+x^4+2*x^3+1);
+fptest(2,x^20+x^3+1,x^20+x^14+x^13+x^10+x^7+x^5+x^4+x^3+x^2+x+1);
+print("-------------e=3--------------");
+fptest(2,x^8+x^4+x^3+x+1,x^8+x^6+x^5+x^3+1);
+fptest(3,x^27+x^5+x^3+x^2+2*x+2,x^27+x^25+2*x^24+x^23+x^22+x^20+x^17+2*x^15+2*x^13+x^12+2*x^11+2*x^10+x^9+2*x^8+x^7+2*x^6+2*x^5+2*x^4+2);
+fptest(2,x^40+x^5+x^4+x^3+1,x^40+x^38+x^37+x^36+x^35+x^34+x^32+x^31+x^30+x^29+x^28+x^25+x^23+x^19+x^16+x^15+x^13+x^11+x^10+x^9+x^7+x^5+x^3+x^2+1);
+print("-------------e>=4--------------");
+fptest(2,x^16+x^15+x^14+x^12+x^7+x^6+x^5+x^2+1,x^16+x^5+x^3+x+1);
+fptest(2,x^32+x^7+x^3+x^2+1,x^32+x^29+x^28+x^27+x^25+x^24+x^22+x^18+x^17+x^15+x^14+x^13+x^11+x^5+x^4+x^3+1);
+fptest(2,x^64+x^4+x^3+x+1,x^64+x^59+x^57+x^56+x^53+x^52+x^51+x^48+x^47+x^46+x^45+x^42+x^40+x^39+x^35+x^33+x^32+x^30+x^29+x^26+x^25+x^24+x^22+x^21+x^20+x^18+x^17+x^14+x^13+x^11+x^10+x^7+x^5+x^2+1);
+fptest(2,x^48+x^5+x^3+x^2+1,x^48+x^46+x^45+x^43+x^42+x^40+x^39+x^38+x^37+x^35+x^34+x^33+x^31+x^29+x^25+x^19+x^18+x^16+x^12+x^9+x^4+x^3+1);
+print("----------large p---------------");
+fptest(300007,x^29+111826*x^28+192245*x^27+118259*x^26+116591*x^25+90193*x^24+179240*x^23+218526*x^22+105853*x^21+39775*x^20+120877*x^19+141649*x^18+95990*x^17+253141*x^16+113157*x^15+174998*x^14+231363*x^13+45405*x^12+279688*x^11+260746*x^10+295341*x^9+186647*x^8+1286*x^7+5846*x^6+226308*x^5+155249*x^4+161003*x^3+892*x^2+124319*x+45791 ,x^29+144375*x^28+258947*x^27+2448*x^26+213576*x^25+275912*x^24+295000*x^23+16021*x^22+62890*x^21+223177*x^20+133874*x^19+291070*x^18+268346*x^17+231097* [...]
+print("----------huge p---------------");
+p=nextprime(3^64);P=ffinit(p,19);Q=poltschirnhaus(P);fptest(p,P,Q);
+p=nextprime(3^66);P=ffinit(p,19);Q=poltschirnhaus(P);fptest(p,P,Q);
+p=1208925819614629174706189;
+P=x^3+154950186819311566804335*x^2+793256884583803289109038*x+629557146926040851399629;
+Q=factormod(polcyclo(13),p)[3,1];
+fptest(p,P,Q);
diff --git a/src/test/in/for b/src/test/in/for
new file mode 100644
index 0000000..29b6724
--- /dev/null
+++ b/src/test/in/for
@@ -0,0 +1,6 @@
+N = 1<<64;
+for(a=N-2, N+2, print(a))
+for(a=-N-2, -N+2, print(a))
+
+forprime(p=2^32-50,2^32+30,print(p))
+forprime(p=2^64-70,2^64+50,print(p))
diff --git a/src/test/in/galois b/src/test/in/galois
new file mode 100644
index 0000000..ef9b086
--- /dev/null
+++ b/src/test/in/galois
@@ -0,0 +1,226 @@
+\\package:galdata
+test(a)=
+{ my(x, y, z);
+  for (i=1, 10000,
+    z = eval(Str("T", a, "_", i));
+    if (poldegree(z) == 1, break); \\ undefined
+    x = polgalois(z);
+    y = polgalois(poltschirnhaus( poltschirnhaus(z) ));
+    print(x, x==y)
+  );
+}
+
+{
+T1_1 = x;
+
+T2_1 = x^2+x+1;
+
+T3_1 = x^3+x^2-2*x-1;
+T3_2 = x^3+2;
+
+T4_1 = x^4+x^3+x^2+x+1;
+T4_2 = x^4+1;
+T4_3 = x^4-2;
+T4_4 = x^4+8*x+12;
+T4_5 = x^4+x+1;
+
+T5_1 = x^5+x^4-4*x^3-3*x^2+3*x+1;
+T5_2 = x^5-5*x+12;
+T5_3 = x^5+2;
+T5_4 = x^5+20*x+16;
+T5_5 = x^5-x+1;
+
+T6_1 = x^6+x^5+x^4+x^3+x^2+x+1;
+T6_2 = x^6+108;
+T6_3 = x^6+2;
+T6_4 = x^6-3*x^2-1;
+T6_5 = x^6+3*x^3+3;
+T6_6 = x^6-3*x^2+1;
+T6_7 = x^6-4*x^2-1;
+T6_8 = x^6-3*x^5+6*x^4-7*x^3+2*x^2+x-4;
+T6_9 = x^6+2*x^3-2;
+T6_10= x^6+6*x^4+2*x^3+9*x^2+6*x-4;
+T6_11= x^6+2*x^2+2;
+T6_12= x^6-2*x^5-5*x^2-2*x-1;
+T6_13= x^6+2*x^4+2*x^3+x^2+2*x+2;
+T6_14= x^6-x^5-10*x^4+30*x^3-31*x^2+7*x+9;
+T6_15= x^6+24*x-20;
+T6_16= x^6+x+1;
+
+T7_1 = x^7+x^6-12*x^5-7*x^4+28*x^3+14*x^2-9*x+1;
+T7_2 = x^7+7*x^3+7*x^2+7*x-1;
+T7_3 = x^7-14*x^5+56*x^3-56*x+22;
+T7_4 = x^7+2;
+T7_5 = x^7-7*x^3+14*x^2-7*x+1;
+T7_6 = x^7+7*x^4+14*x+3;
+T7_7 = x^7+x+1;
+
+T8_50 = x^8-x-1;
+T8_49 = x^8-2*x^6-2*x^5-x^4-x^3+4*x^2+4*x-2;
+T8_48 = x^8-2*x^6-2*x^5+2*x^4+4*x^2+2;
+T8_47 = x^8+x^2+2*x+1;
+T8_46 = x^8-4*x^5-9*x^4-16*x^2-12*x+9;
+T8_45 = x^8-2*x^6-2*x^5+4*x^3+2*x^2+2;
+T8_44 = x^8+x^2-1;
+T8_43 = x^8-x^7+7*x^2-x+1;
+T8_42 = x^8-2*x^6+2*x^5+3*x^4-2*x^3+x^2+4*x+2;
+T8_41 = x^8-4*x^5+3*x^4+16*x^2+12*x+9;
+T8_40 = x^8+8*x^6+18*x^4-1;
+T8_39 = x^8+x^2+1;
+T8_38 = x^8-4*x^6+28;
+T8_37 = x^8-4*x^7+28*x^5-21*x^4-70*x^3+189*x^2-173*x+69;
+T8_36 = x^8-4*x^7+112*x^4+224*x^3-112*x^2-736*x+536;
+T8_35 = x^8+4*x^2-1;
+T8_34 = x^8+4*x^7+5*x^6+x^5+x^4-2*x^3-x^2+3*x+2;
+T8_33 = x^8-8*x^6+18*x^4-16*x^3-40*x^2+8*x+23;
+T8_32 = x^8-8*x^6+18*x^4+4;
+T8_31 = x^8+2*x^6+x^4+14;
+T8_30 = x^8+4*x^6+4*x^4-2;
+T8_29 = x^8+x^4+2*x^2+1;
+T8_28 = x^8+4*x^6+2;
+T8_27 = x^8+5*x^6+3*x^4-6*x^2-4;
+T8_26 = x^8+2*x^4-2;
+T8_25 = x^8-x^7+29*x^2+29;
+T8_24 = x^8-4*x^2+4;
+T8_23 = x^8-8*x^6+12*x^4-12;
+T8_22 = x^8+x^4+4;
+T8_21 = x^8+2*x^4+4*x^2+2;
+T8_20 = x^8+x^6-6*x^4-x^2+1;
+T8_19 = x^8+4*x^4+4*x^2+1;
+T8_18 = x^8+2*x^6+2*x^2+1;
+T8_17 = x^8+2*x^4+2;
+T8_16 = x^8+4*x^4+2;
+T8_15 = x^8+3;
+T8_14 = x^8+2*x^7+4*x^6-2*x^5+2*x^4-2*x^3+4*x^2+2*x+1;
+T8_13 = x^8+4*x^6+8*x^4+4;
+T8_12 = x^8-22*x^6+135*x^4-150*x^2+1;
+T8_11 = x^8+9;
+T8_10 = x^8+2*x^6+4*x^4+3*x^2+1;
+T8_9  = x^8+4*x^4+1;
+T8_8  = x^8-2;
+T8_7  = x^8+x^7-28*x^6-7*x^5+70*x^4+7*x^3-28*x^2-x+1;
+T8_6  = x^8+2;
+T8_5  = x^8-12*x^6+36*x^4-36*x^2+9;
+T8_4  = x^8+3*x^4+1;
+T8_3  = x^8-x^4+1;
+T8_2  = x^8+1;
+T8_1  = x^8+x^7-7*x^6-6*x^5+15*x^4+10*x^3-10*x^2-4*x+1;
+
+T9_34 = x^9-x-1;
+T9_33 = x^9+27*x-24;
+T9_32 = x^9+x^7+2*x^5+4*x^3-x^2+x+1;
+T9_31 = x^9-2*x^7-2*x^6-x^5-x^4+4*x^3+5*x^2+4*x+1;
+T9_30 = x^9+2*x^5+4*x^4+4*x^3+4*x^2+x+1;
+T9_29 = x^9-6*x^6-18*x^5+36*x^4-36*x^3+108*x^2-144*x+48;
+T9_28 = x^9-2*x^7-2*x^6-x^5-2*x^4+3*x^2+3*x+1;
+T9_27 = x^9-36*x^6-54*x^5+432*x^3+324*x^2-243*x-1152;
+T9_26 = x^9-x^7+5*x^6+x^5-2*x^4+4*x^3+3*x^2-x-1;
+T9_25 = x^9-9*x^6-9*x^4+24*x^3+9*x^2-9*x+1;
+T9_24 = x^9-2*x^6-2*x^3-2;
+T9_23 = x^9+9*x^7-60*x^6+72*x^5+354*x^3-495*x^2+2124*x-845;
+T9_22 = x^9-12*x^6-27*x^5-18*x^4+9*x^3+36*x-8;
+T9_21 = x^9+3*x^6+3*x^3-2;
+T9_20 = x^9-2*x^7-2*x^6-2*x^5+x^4+4*x^3+3*x^2+3*x+1;
+T9_19 = x^9-3*x^8-24*x^5-24*x^4-48*x+16;
+T9_18 = x^9-2*x^6-2*x^3-1;
+T9_17 = x^9-17*x^7-6*x^6+87*x^5+47*x^4-143*x^3-69*x^2+72*x+27;
+T9_16 = x^9-2*x^7+3*x^6+x^5-x^4-2*x^3+x+1;
+T9_15 = x^9-9*x^7-21*x^6+72*x^5+99*x^4-99*x^3-585*x^2+549*x+166;
+T9_14 = x^9-30*x^6+45*x^5+126*x^4-240*x^3-90*x^2+405*x+80;
+T9_13 = x^9-2*x^6-x^3+1;
+T9_12 = x^9+x^8+x^7+4*x^6-2*x^5-x^4+3*x^3+x^2-1;
+T9_11 = x^9-x^6+5*x^3+1;
+T9_10 = x^9-2;
+T9_9  = x^9-3*x^8+3*x^7-15*x^6+33*x^5-3*x^4+24*x^3+6*x^2-4;
+T9_8  = x^9-6*x^6+8*x^3-8;
+T9_7  = x^9-232*x^7-9*x^6+7485*x^5+8631*x^4-3097*x^3-738*x^2+325*x-27;
+T9_6  = x^9+x^8-32*x^7-84*x^6-14*x^5+112*x^4+84*x^3+4*x^2-8*x-1;
+T9_5  = x^9+3*x^6+3*x^3-1;
+T9_4  = x^9+4*x^6+3*x^3-1;
+T9_3  = x^9+9*x^7-6*x^6+27*x^5-36*x^4+27*x^3-54*x^2-32;
+T9_2  = x^9-15*x^7+4*x^6+54*x^5-12*x^4-38*x^3+9*x^2+6*x-1;
+T9_1  = x^9-9*x^7+27*x^5-30*x^3+9*x-1;
+
+T10_45 = x^10-x-1;
+T10_44 = x^10-2*x^8-2*x^7-2*x^3+2*x^2+x-1;
+T10_43 = x^10-2*x^8-2*x^7-2*x^6-2*x^5-x^4-2*x^3+3*x^2-2*x+1;
+T10_42 = x^10-32*x^5-200*x^2+256;
+T10_41 = x^10+2*x^9+4*x^8-x^6+x^4-2*x-1;
+T10_40 = x^10+x^9-x^8-x^7-2*x^6+2*x^3+3*x^2+x+1;
+T10_39 = x^10-2*x^8-2*x^7-2*x^6-2*x^5+2*x^4-2*x^3+2*x^2-1;
+T10_38 = x^10-2*x^8-x^6-2*x^4+2*x^2-2;
+T10_37 = x^10-2*x^8-2*x^7-x^6-x^5-x^4-2*x^3-2*x^2+1;
+T10_36 = x^10-2*x^8-x^6+3*x^4-x^2+2;
+T10_35 = x^10+300*x^6-18*x^5+10000*x^2-200*x+81;
+T10_34 = x^10-x^8-2*x^6-x^4+x^2-1;
+T10_33 = x^10-2*x^9+12*x^8-20*x^7+66*x^6-20*x^5+228*x^4+84*x^3+276*x^2+120*x+100;
+T10_32 = x^10-9*x^8+27*x^6+2*x^5-27*x^4-9*x^3+8*x+1;
+T10_31 = x^10-1800*x^8-24000*x^7+1422000*x^6+30960000*x^5-462480000*x^4-14500800000*x^3+12996000000*x^2+2414368000000*x-12197187420489;
+T10_30 = x^10+90*x^6-648*x^5+1080*x^4-2160*x^3+3645*x^2+5400*x+12960;
+T10_29 = x^10+2*x^8-2*x^6-x^2+2;
+T10_28 = x^10-10*x^7+10*x^6+36*x^5+50*x^4-10*x^3-1;
+T10_27 = x^10+3*x^6-2*x^5+x^2+2*x+1;
+T10_26 = x^10-15*x^8-75*x^6-6*x^5-165*x^4-30*x^3-180*x^2-50*x-90;
+T10_25 = x^10-2*x^8-2*x^6-x^2-2;
+T10_24 = x^10+x^8-x^4+3*x^2-1;
+T10_23 = x^10-2*x^8-x^7+3*x^6+2*x^5-2*x^4-2*x^3+2*x^2+3*x+1;
+T10_22 = x^10-2*x^8-2*x^7-x^6+x^4-2*x^3+2*x^2-1;
+T10_21 = x^10+x^6-2*x^5-x^4+3*x^2-2*x+1;
+T10_20 = x^10-3*x^9+x^8+36*x^7-39*x^6-105*x^5+99*x^4+180*x^3-45*x^2-135*x-45;
+T10_19 = x^10-10*x^8+35*x^6-2*x^5-50*x^4+10*x^3+25*x^2-10*x+2;
+T10_18 = x^10+60*x^6-240*x^5+850*x^2-5440*x-1088;
+T10_17 = x^10-2*x^5-2;
+T10_16 = x^10+7*x^8+17*x^6-31*x^4-40*x^2+127;
+T10_15 = x^10-x^8-2*x^6+x^4+3*x^2-1;
+T10_14 = x^10+x^8-4*x^6-3*x^4+3*x^2+1;
+T10_13 = x^10-2*x^8-x^7-2*x^6+x^5+3*x^4-2*x^3-x^2+x+1;
+T10_12 = x^10+2*x^9+3*x^8-x^6-2*x^5-x^4+3*x^2+2*x+1;
+T10_11 = x^10+10*x^6+25*x^2-8;
+T10_10 = x^10-2*x^5-4;
+T10_9  = x^10-50*x^8-100*x^7+865*x^6+4036*x^5+4100*x^4+16400*x^2+13120*x+2624;
+T10_8  = x^10-4*x^8+2*x^6+5*x^4-2*x^2-1;
+T10_7  = x^10-2*x^5-15*x^4-10*x^3-15*x^2-5;
+T10_6  = x^10+5*x^8-33*x^7-67*x^6+132*x^5-375*x^4+1551*x^3+5505*x^2-8987*x+4291;
+T10_5  = x^10-2;
+T10_4  = x^10-x^5-1;
+T10_3  = x^10-x^8-x^6+3*x^4+2*x^2+1;
+T10_2  = x^10-35*x^6+130*x^4+160;
+T10_1  = x^10-x^9+x^8-x^7+x^6-x^5+x^4-x^3+x^2-x+1;
+
+T11_8 = x^11-x-1;
+T11_7 = x^11+x^10+2*x^9+2*x^8+x^6-x^5+2*x^4+2*x^3+x^2-1;
+T11_6 = x^11-x^10-121*x^9+65*x^8+5345*x^7-481*x^6-96739*x^5-23689*x^4+413690*x^3-493810*x^2+26910*x-856170;
+T11_5 = x^11-898*x^9-3080*x^8+293480*x^7+4185984*x^6-145552352*x^5+1474999680*x^4-16923164544*x^3+177410331648*x^2-709199732736*x+63589515264;
+T11_4 = x^11-2;
+T11_3 = x^11-33*x^9+396*x^7-2079*x^5+4455*x^3-2673*x-243;
+T11_2 = x^11-x^10+5*x^8+8*x^5+6*x^4-x^3+x^2+3*x+1;
+T11_1 = x^11+x^10-10*x^9-9*x^8+36*x^7+28*x^6-56*x^5-35*x^4+35*x^3+15*x^2-6*x-1;
+}
+
+default(realprecision, 38);
+default(new_galois_format, 1)
+for (i = 1, 11, test(i))
+
+default(new_galois_format, 0)
+for (i = 1, 7, test(i))
+
+\\ miscellaneous tests
+setrand(3);polgalois(x^8-24447832222819253258096747170722821932737551721814653244372785289945042560370884249414339208601850*x^4+24447832222819253258096747170722821932737551721825709426404304862673539625219608619381278993545125*x^2+24447832222819253258096747170722821932737551721825709426404304862673539625219608619381278993545125)
+
+polgalois(polzagier(11,0)/polzagier(1,0))
+
+polgalois(x^8-1864259299553450972214799899167226732549697977945716*x^6+331143259018657601105207922631212331088735421305543663274125986698777318014979969*x^4-2225286541902342283500014249183311190477390*x^2+5)
+
+polgalois(x^8+162644002617632464507038884216211529274267271168000002)
+
+polgalois(x^8+2^2^12)
+
+setrand(5);polgalois(x^8-3911867303938246274330482940384509030446487325649036998411199166662010711465575565062431210085563041214249877058238647352476889609806751307308111079477582030570450*x^4-3911867303938246274330482940384509030446487325649036998411199166662010711465575569485025077906938222890835591100732814736005780908137290944485033156820540880540405*x^2+39118673039382462743304829403845090304464873256490369984111991666620107114655755694850250779069382228908355911007328147360057809081 [...]
+
+setrand(15);polgalois(x^8-1642492255488433999638100059165477791152530*x^4-1642492255488433999640965798385546876573045*x^2+1642492255488433999640965798385546876573045)
+
+setrand(4);polgalois(x^8-264*x^6+25410*x^4-1054152*x^2+15856203)
+
+setrand(15);polgalois(x^8-3512859249280433994187541000*x^6+1542522513156886787688759313741174472421584953335229070*x^4-60930543678769127629182898645000*x^2+300849025)
+
+setrand(1); polgalois(x^11+627*x^4-584)
diff --git a/src/test/in/galoisinit b/src/test/in/galoisinit
new file mode 100644
index 0000000..5dac49c
--- /dev/null
+++ b/src/test/in/galoisinit
@@ -0,0 +1,70 @@
+do(p)=galoisidentify(galoisinit(p));
+
+do(algdep(I,3))
+do(galoissubcyclo(bnrinit(bnfinit(y),[1232,[1]],1),[4,0,0,0;0,2,0,1;0,0,2,1;0,0,0,1]))
+do(x^20-40*x^18+605*x^16-4600*x^14+19500*x^12-48250*x^10+70425*x^8-59500*x^6+27625*x^4-6250*x^2+500)
+do(x^24-12*x^23+6*x^22+440*x^21-1659*x^20-2352*x^19+24274*x^18-34812*x^17-66078*x^16+249212*x^15-192066*x^14-234528*x^13+515149*x^12-234528*x^11-192066*x^10+249212*x^9-66078*x^8-34812*x^7+24274*x^6-2352*x^5-1659*x^4+440*x^3+6*x^2-12*x+1)
+do(x^4+272*x^3+40256*x^2+1740800*x+25397248)
+do(x^4+5264*x^3+8034856*x^2+4205424384*x+504485485632)
+do(x^4+884*x^3-1972*x^2-884*x+1)
+do(x^4-42*x^2+144)
+do(x^12-30*x^8-370*x^6+1665*x^4+23166*x^2+81)
+do(x^24+3*x^22+22*x^20+31*x^18+138*x^16+85*x^14+297*x^12+149*x^10+249*x^8+238*x^6+98*x^4+16*x^2+1)
+do(x^54+4288*x^45+4739337*x^36+88723254*x^27+799530047*x^18-256778413*x^9+40353607)
+do(x^54-3762*x^52+6515505*x^50-6911246832*x^48+5039176931676*x^46-2686817670557400*x^44+1087963170065343636*x^42-342933852097598081616*x^40+85549691240003522127726*x^38-17077123231759966515087980*x^36+2746482620444718167893994910*x^34-357137314570021313085512898384*x^32+37572202145621696209178550611604*x^30-3191423993701636005506286262264824*x^28+217847326568033953619436917061987732*x^26-11861321463900503282422713802261870896*x^24+509777643921195165950639871535287639897*x^22-170557678799 [...]
+do(x^64-3645070*x^56+3769245010705*x^48+120173739648338450*x^40+2124098086173949323364*x^32+16674620185061962554229010*x^24+87774696936415565369888312017*x^16+56323712629998864272734706*x^8+78032457926322172553281)
+do(x^8-4*x^7-126*x^6+392*x^5+4853*x^4-10364*x^3-58244*x^2+63492*x+197761)
+do(y^4+1)
+
+p=x^14-271*x^13+14191*x^12-320438*x^11+3790080*x^10-25112800*x^9+92495160*x^8-167147800*x^7+50530009*x^6+301971239*x^5-450938136*x^4+211398894*x^3-16216756*x^2-8116135*x+1041461; do(p)
+
+do(x^48+688253440*x^36+64889579202*x^24+688253440*x^12+1);
+
+\\\\\\\\\\\
+
+nfgaloisconj(x^3-x-1)
+nfgaloisconj(x^24+2814)
+nfgaloisconj(x^4+1)
+
+
+nf=nfinit(polcyclo(7)); s = nfgaloisconj(nf)[2]; pr = idealprimedec(nf,11)[1];
+nfgaloisapply(nf,s,idealhnf(nf,pr))
+nfgaloisapply(nf,s,pr)
+nfgaloisapply(nf,s,x^2+x)
+nfgaloisapply(nf,s,1/2)
+v=[1,1/2,Mod(x,nf.pol),x,vectorv(6),[;],[1,2;x,3]];
+for (i=1,#v, print(nfgaloisapply(nf,s,[1,v[i]])))
+
+galoistest(P)=
+{
+  my(G,F,L);
+  G=galoisinit(P);
+  F=galoissubfields(G,2);
+  for (i=1,#F, my(L=F[i]);
+    if (subst(L[1],x,L[2])!=0,
+       error("galoissubfields1"));
+    if (factorback(L[3]*Mod(1,subst(L[1],x,y)))!=P,
+       error("galoissubfields2")));
+}
+galoistest(x^4 + 431452248691495692750746750*x^3+447244936830156353775324765*x^2+5580140636706480467906000*x - 238676773812533681600);
+
+G=galoisinit(x);
+galoisexport(G);
+galoisexport(G,1);
+G=galoisinit(x^12-30*x^8-370*x^6+1665*x^4+23166*x^2+81);
+galoisexport(G)
+galoisexport(G, 1)
+G=galoisinit(x^12-24*x^10-10*x^9+216*x^8+180*x^7-844*x^6-1080*x^5+1056*x^4+2200*x^3+720*x^2-240*x-80);
+L=galoissubgroups(G)
+apply(H->galoisisnormal(G,H),L)
+apply(H->galoisisabelian(H),L)
+apply(H->galoisisabelian(H,1),L)
+apply(H->galoisisabelian(H,2),L)
+
+vector(#G.group, i, galoisfixedfield(G,G.group[i],1))
+galoisfixedfield(G,G.group[2])
+galoisfixedfield(G,G.group[2],2,y)
+galoissubcyclo(17,2)
+
+G=galoisinit(polcyclo(20))
+galoissubfields(G)
diff --git a/src/test/in/galpol b/src/test/in/galpol
new file mode 100644
index 0000000..4e1ad17
--- /dev/null
+++ b/src/test/in/galpol
@@ -0,0 +1,31 @@
+\\ package: galpol
+
+galoisgetpol(8)
+for(i=1,5,print(galoisgetpol(8,i)))
+for(i=1,5,print(galoisgetpol(8,i,2)))
+galoisgetpol(8,6)
+galoisgetpol(3,1,3)
+galoisgetpol(3,1,2)
+test(n,k)=
+  if(galoisidentify(galoisinit(galoisgetpol(n,k)[1])) != [n,k], error([n,k]));
+test(8,3)
+test(18,5)
+test(27,3)
+test(45,2)
+test(30,4)
+test(32,4)
+test(32,13)
+test(32,30)
+test(32,32)
+test(42,2)
+test(48,12)
+test(64,3)
+test(64,14)
+test(64,16)
+test(64,48)
+test(64,51)
+test(64,70)
+test(64,68)
+test(64,80)
+test(64,44)
+galoisidentify(galoisinit(polcyclo(390)))
diff --git a/src/test/in/gamma b/src/test/in/gamma
new file mode 100644
index 0000000..b2aa09f
--- /dev/null
+++ b/src/test/in/gamma
@@ -0,0 +1,32 @@
+default(realprecision,38);
+default(seriesprecision,6);
+gamma(2+I+x)
+gamma(1+x)
+gamma(-2+x)
+lngamma(2+I+x)
+lngamma(1+x)
+lngamma(-2+x)
+psi(2+I+x)
+psi(1+x)
+psi(-2+x)
+
+psi(x)
+gamma(x)
+gamma(1000)
+psi(2^400)
+psi(-1.5)
+
+binomial(2^64+1,2)
+binomial(1001.,1000)
+
+lngamma(2^301)
+lngamma(2^301 + 2*I)
+lngamma(2^64 + 2*I)
+lngamma(-200.5)
+gammah(400)
+gammah(400+I)
+default(realprecision,115);
+lngamma(2^301 + 2*I)
+lngamma(2^301 + 200*I)
+default(realprecision,154);
+lngamma(2^301 + 2000*I)
diff --git a/src/test/in/gcdext b/src/test/in/gcdext
new file mode 100644
index 0000000..6c6d0a5
--- /dev/null
+++ b/src/test/in/gcdext
@@ -0,0 +1,62 @@
+{
+  a=[0,0*x,O(5^3),2,2+O(5^3),x];
+  for(i=1,#a,
+    for(j=1,#a,
+      print(gcdext(a[i],a[j]))));
+}
+gcdext(-1/3*x - 5/9,-x - 5/3)
+z;T;
+FF=Mod((O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z^7+((2+2^3+O(2^4))*T^3+O(2^4)*T^2+(1+2+O(2^4))*T+(1+2^2+O(2^4)))*z^6+(O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z^5+((1+2+2^3+O(2^4))*T^3+(1+2+2^2+2^3+O(2^4))*T^2+(2+2^3+O(2^4))*T+(1+2^2+2^3+O(2^4)))*z^4+(O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z^3+((2+2^2+2^3+O(2^4))*T^3+(1+2^2+2^3+O(2^4))*T^2+O(2^4)*T+(1+2+2^2+2^3+O(2^4)))*z^2+(O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z+((1+2^3+O(2^4))+(1+2^2+2^3+O(2^4))*T+(2+2^2+2^3+O(2^4))*T^2+(1+2+2^3+O(2^4) [...]
+GG=Mod((O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z^7+((2+2^3+O(2^4))*T^3+O(2^4)*T^2+(1+2+O(2^4))*T+(1+2^2+O(2^4)))*z^6+(O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z^5+((1+2+2^3+O(2^4))*T^3+(1+2+2^2+2^3+O(2^4))*T^2+(2+2^3+O(2^4))*T+(1+2^2+2^3+O(2^4)))*z^4+(O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z^3+((2+2^2+2^3+O(2^4))*T^3+(1+2^2+2^3+O(2^4))*T^2+O(2^4)*T+(1+2+2^2+2^3+O(2^4)))*z^2+(O(2^4)*T^3+O(2^4)*T^2+O(2^4)*T+O(2^4))*z+((1+2^3+O(2^4))+(1+2^2+2^3+O(2^4))*T+(2+2^2+2^3+O(2^4))*T^2+(1+2+2^3+O(2^4) [...]
+AA=truncate(lift(lift(FF)));
+BB=truncate(lift(lift(GG)));
+gcdext(AA,BB)
+gcd(x*Mod(1,7),x*Mod(1,7))
+
+gcd(2*I, 1+I)
+gcd(I*1., I+1.)
+gcd(1+O(2),1+O(3))
+gcd(2+O(2^2),4+O(2^3))
+w=quadgen(5);
+gcd(w,2*w)
+gcd(2*w,w)
+gcd(2,Mod(2,4))
+gcd(1/2,Mod(2,4))
+gcd(1/3,Mod(2,4))
+gcd(Mod(2,4), 2+O(2^3))
+gcd(Mod(2,5), I)
+gcd(Mod(2,5), w)
+gcd(1/2, 1/(I+1))
+gcd(1/2, 2+O(2^3))
+gcd(I, 2+O(2^3))
+gcd(I, w)
+gcd(w, 2+O(2^3))
+t = Mod(x^2,x^3);
+gcd(t,y)
+gcd(t,x)
+gcd(t,1/y)
+gcd(t,1/x)
+gcd(t,1/(x+1))
+gcd(Pol(0), y)
+gcd(x+O(x^5), x^2+O(x^3))
+gcd(x+O(x^5), 1/x^2)
+v=[1,2,1.,Mod(1,2),1/2,1/3,2/3,I,quadgen(5), 1+O(2),2+O(2^2),1/2+O(2)];
+{
+t1=ffgen(2^3);
+t0=0*t1;
+t2=ffgen(3^3);
+for (i=1,#v,
+  print(iferr(gcd(t0,v[i]),E,E));
+  print(iferr(gcd(t1,v[i]),E,E));
+  print(iferr(gcd(t2,v[i]),E,E));
+)
+}
+gcd(0,1.+I)
+gcd(0,1+I)
+gcd(0,Mod(1,3)+I)
+gcd(1/2, 1.+I)
+
+default(realprecision,38);
+gcd(Pol(0.),x)
+(k+1.)/k - (2*k+1.)/k
+gcd(1/(2^64*y),Mod(x^2,2^64*x^3))
diff --git a/src/test/in/genus2red b/src/test/in/genus2red
new file mode 100644
index 0000000..ce78002
--- /dev/null
+++ b/src/test/in/genus2red
@@ -0,0 +1,699 @@
+\\ Input from Namikawa-Ueno's list of curves of genus 2
+\\ with expected reduction types.
+
+\\ t is to be substituted with a prime number bigger or equal to 7
+\\ and can be taken to be 5 most of the time...
+\\ a, b, c are generic integers (to be changed if unexpected output)
+do(P, s) = print("Type: ", s); genus2red(0,subst(P,'t,7),7);
+
+P0=x^5+a*x^3+b*x^2+c*x+1;
+do(substvec(P0, [a,b,c], [1,-2,3]), "[I{0-0-0}], p. 155")
+
+P0=x^5+a*t^2*x^3+b*t^3*x^2+c*t^4*x+t^5;
+do(substvec(P0, [a,b,c], [1,-2,3]), "[I*{0-0-0}], p. 155")
+
+P0=x^6+a*t*x^4+b*t^2*x^2+t^3;
+do(substvec(P0, [a,b], [4,-1]), "[II], p. 155")
+genus2red(0,substvec(P0,[a,b,t],[4,-1,3]),3)
+
+P0=x^6+a*t*x^3+t^2;
+do(subst(P0, 'a, 4), "[III], p. 155")
+
+P0=t*(x^6+a*t*x^3+t^2);
+do(subst(P0, 'a, 5), "[IV], p. 155")
+
+P0=x^6+t;
+do(P0, "[V], p. 156")
+
+P0=x^6+t^5;
+do(P0, "[V*], p. 156")
+
+P0=x*(x^4+a*t*x^2+t^2);
+do(subst(P0, 'a, 3), "[VI], p. 156")
+
+P0=x*(x^4+t);
+do(P0, "[VII], p. 156")
+
+P0=x*(x^4+t^5);
+do(P0, "[VII*], p. 156")
+
+P0=x^5+t;
+do(P0, "[VIII-1], p. 156")
+
+P0=x^5+t^3;
+do(P0, "[VIII-2], p. 157")
+
+P0=x^5+t^7;
+do(P0, "[VIII-3], p. 157")
+
+P0=x^5+t^9;
+do(P0, "[VIII-4], p. 157")
+
+P0=x^5+t^2;
+do(P0, "[IX-1], p. 157")
+
+P0=x^5+t^4;
+do(P0, "[IX-2], p. 157")
+
+P0=x^5+t^6;
+do(P0, "[IX-3], p. 157")
+
+P0=x^5+t^8;
+do(P0, "[IX-4], p. 158")
+
+\\ Elliptic type,
+\\ m > 0 FIXME: changed !
+m = 10;
+P0=(x^3+a*x+1)*(x^3+b*t^(4*m)*x+t^(6*m)) ;
+do(substvec(P0, [a,b], [3,10]), Strprintf("[I{0}-I{0}-%ld], p. 158", m))
+\\ misprint in N-U: m+1 must be m.
+genus2red(0,substvec(P0,['a,'b,'t],[-1,1,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=((x-1)^3+a*t^2*(x-1)+t^3)*(x^3+b*t^(4*m+2)*x+t^(6*m+3));
+do(substvec(P0, [a,b], [3,10]), Strprintf("[I{0}*-I{0}*-%ld], p. 158", m))
+genus2red(0,substvec(P0,['a,'b,'t],[-1,1,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^3+a*x+1)*(x^3+b*t^(4*m+2)*x+t^(6*m+3));
+do(substvec(P0, [a,b], [3,10]), Strprintf("[I{0}-I{0}*-%ld], p. 159", m))
+genus2red(0,substvec(P0,['a,'b,'t],[-1,1,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+a*t^(2*m+4)*(x^2-t)+t^(3*m+6);
+do(subst(P0, 'a, 3), Strprintf("[2I{0}-%ld], p. 159",m+1))
+genus2red(0,substvec(P0,['a,'t],[-1,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+a*t^(2*m+3)*(x^2-t)+t^(3*m+4)*x;
+do(subst(P0, 'a, 3), Strprintf("[2I{0}*-%ld], p. 159",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+a*t^(2*m+3)*(x^2-t)+t^(3*m+4)*x;
+do(subst(P0, 'a, 3), Strprintf("[2I{0}*-%ld], p. 159",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+1))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}-II-%ld], p. 159",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+5))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}-II*-%ld], p. 160",m))
+genus2red(0,substvec(P0,['a,'t],[3,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+2))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}-IV-%ld], p. 160",m))
+genus2red(0,substvec(P0,['a,'t],[3,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+4))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}-IV*-%ld], p. 160",m))
+genus2red(0,substvec(P0,['a,'t],[3,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=t*(x^3+t^(6*m+4))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}*-II-%ld], p. 160",m))
+genus2red(0,substvec(P0,['a,'t],[3,3]),3)
+
+\\ m >= -1
+m = 10;
+P0=t*(x^3+t^(6*m+8))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}*-II*-%ld], p. 160-161",m))
+genus2red(0,substvec(P0,['a,'t],[3,3]),3)
+
+\\ m >= 0
+m = 10;
+P0=t*(x^3+t^(6*m+5))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}*-IV-%ld], p. 161",m))
+
+\\ m >= -1
+m = 10;
+P0=t*(x^3+t^(6*m+7))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}*-IV*-%ld], p. 161",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}-III-%ld], p. 161",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+3))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}-III*-%ld], p. 162",m))
+
+\\ m >= 0
+m = 10;
+P0=t*x*(x^2+t^(4*m+3))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}*-III-%ld], p. 162",m))
+
+\\ m >= -1
+m = 10;
+P0=t*x*(x^2+t^(4*m+5))*(x^2+a*x+1);
+do(subst(P0, 'a, 3), Strprintf("[I{0}*-III*-%ld], p. 162",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+t^(3*m+3)*x;
+do(P0, Strprintf("[2II-%ld], p. 162",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+t^(3*m+5)*x;
+do(P0, Strprintf("[2II*-%ld], p. 163",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+1))*((x-1)^3+t);
+do(P0, Strprintf("[II-II-%ld], p. 163",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+1))*((x-1)^3+t^5);
+do(P0, Strprintf("[II-II*-%ld], p. 163",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+5))*((x-1)^3+t^5);
+do(P0, Strprintf("[II*-II*-%ld], p. 163",m))
+
+\\ m = -1
+P0=t*(x^3+t^2)*((x-1)^3+t^2);
+do(P0, "[II*-II*-(-1)], p. 163")
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+1))*((x-1)^3+t^2);
+do(P0, Strprintf("[II-IV-%ld], p. 164",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+1))*((x-1)^3+t^4);
+do(P0, Strprintf("[II-IV*-%ld], p. 164",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+5))*((x-1)^3+t^2);
+do(P0, Strprintf("[II*-IV-%ld], p. 164",m))
+
+\\ m = -1
+P0=(x^3+t^2)*(x^3+t);
+do(P0, "[II*-IV-(-1)], p. 164")
+
+\\ m bigger or equal -1
+m = 10;
+P0=t*(x^3+t^(6*m+7))*((x-1)^3+t^2);
+do(P0, Strprintf("[II*-IV*-%ld], p. 164-165",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+t^(3*m+4);
+do(P0, Strprintf("[2IV-%ld], p. 165",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)^3+t^(3*m+5);
+do(P0, Strprintf("[2IV*-%ld], p. 165",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+2))*((x-1)^3+t^2);
+do(P0, Strprintf("[IV-IV-%ld], p. 165",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^3+t^(6*m+2))*((x-1)^3+t^4);
+do(P0, Strprintf("[IV-IV*-%ld], p. 165",m))
+
+\\ m >= -1
+m = 10;
+P0=t*(x^3+t^(6*m+7))*((x-1)^3+t);
+do(P0, Strprintf("[IV*-IV*-%ld], p. 166",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*((x-1)^3+t);
+do(P0, Strprintf("[II-III-%ld], p. 166",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+3))*((x-1)^3+t);
+do(P0, Strprintf("[II-III*-%ld], p. 166",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*((x-1)^3+t^5);
+do(P0, Strprintf("[II*-III-%ld], p. 166",m))
+
+\\ m = -1
+P0=(x^2+t)*(x^3+t^2);
+do(P0, "[II*-III-(-1)], p. 167")
+
+\\ m at least -1
+m = 10;
+P0=t*x*(x^2+t^(4*m+5))*((x-1)^3+t^2);
+do(P0, Strprintf("[II*-III*-%ld], p. 167",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*((x-1)^3+t^2);
+do(P0, Strprintf("[IV-III-%ld], p. 167",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+3))*((x-1)^3+t^2);
+do(P0, Strprintf("[IV-III*-%ld], p. 167",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m = -1
+P0=x*(x^2+t)*(x^3+t);
+do(P0, "[IV-III*-(-1)], p. 167")
+\\ the top horizontal line has mult. 3.
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*((x-1)^3+t^4);
+do(P0, Strprintf("[IV*-III-%ld], p. 168",m))
+
+\\ m at least -1
+m = 10;
+P0=t*x*(x^2+t^(4*m+5))*((x-1)^3+t);
+do(P0, Strprintf("[IV*-III*-%ld], p. 168",m))
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)*((x^2-t)^2+t^(2*m+2)*x);
+do(P0, Strprintf("[2III-%ld], p. 168",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=(x^2-t)*((x^2-t)^2+t^(2*m+3)*x);
+do(P0, Strprintf("[2III*-%ld], p. 168",m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*(x-1)*((x-1)^2+t);
+do(P0, Strprintf("[III-III-%ld], p. 169",m))
+
+\\ m >= 0
+m = 10;
+P0=x*(x^2+t^(4*m+1))*(x-1)*((x-1)^2+t^3);
+do(P0, Strprintf("[III-III*-%ld], p. 169",m))
+
+\\ m >= -1
+m = 10;
+P0=t*x*(x^2+t^(4*m+5))*(x-1)*((x-1)^2+t);
+do(P0, Strprintf("[III*-III*-{%ld}], p. 169",m))
+
+\\ Parabolic type
+\\ n > 0
+n = 9;
+P0=(x^3+a*x+1)*(x^2+t^n);
+do(subst(P0, 'a, 3), Strprintf("[I{%ld-0-0}], p. 170",n))
+
+\\ n, m > 0
+n = 9; m = 10;
+P0=(x^3+a*t^(4*m)*x+t^(6*m))*((x-1)^2+t^n);
+do(subst(P0, 'a, 3), Strprintf("[I{%ld}-I{0}-%ld], p. 170",n, m))
+genus2red(0,substvec(P0,['a,'t],[-1,3]),3)
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x+t)*(x^2+t^(n+2))*((x-1)^3+a*t^(4*m)*(x-1)+t^(6*m));
+do(subst(P0, 'a, 3), Strprintf("[I{0}-I*{%ld}-%ld], p. 170",n, m))
+genus2red(0,substvec(P0,['a,'t],[-1,3]),3)
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x^3+a*t^(4*m+2)*x+t^(6*m+3))*((x-1)^2+t^n);
+do(subst(P0, 'a, 3), Strprintf("[I{%ld}-I{0}*-%ld], p. 171",n, m))
+
+\\ n > 0
+n = 9;
+P0=t*(x^3+a*x+1)*(x^2+t^n);
+do(subst(P0, 'a, 3), Strprintf("[I*{%ld-0-0}], p. 171",n))
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x+t)*(x^2+t^(n+2))*((x-1)^3+a*t^(4*m+2)*(x-1)+t^(6*m+3));
+do(subst(P0, 'a, 3), Strprintf("[I*{%ld}-I{0}*-%ld], p. 171",n, m))
+
+\\ n > 0
+n = 9;
+P0=(x^4+a*t*x^2+t^2)*((x-1)^2+t^(n-1));
+do(subst(P0, 'a, 3), Strprintf("[II{%ld-0}], p. 171",n))
+
+\\ n > 0
+n = 9;
+P0=t*(x^4+a*t*x^2+t^2)*((x-1)^2+t^(n-1));
+do(subst(P0, 'a, 3), Strprintf("[II*{%ld-0}], p. 172",n))
+
+\\ n > 0, m >= 0
+n = 9; m = 10;
+P0=(x^3+t^(6*m+1))*((x-1)^2+t^n);
+do(P0, Strprintf("[II-I{%ld}-%ld], p. 172",n, m))
+
+\\ n > 0, m >= 0
+n = 9; m = 10;
+P0=(x^3+t^(6*m+5))*((x-1)^2+t^n);
+do(P0, Strprintf("[II*-I{%ld}-%ld], p. 172",n, m))
+
+\\ n > 0, m >= 0
+n = 9; m = 10;
+P0=(x^3+t^(6*m+2))*((x-1)^2+t^n);
+do(P0, Strprintf("[IV-I{%ld}-%ld], p. 173",n, m))
+
+\\ n > 0, m >= 0
+n = 9; m = 10;
+P0=(x^3+t^(6*m+4))*((x-1)^2+t^n);
+do(P0, Strprintf("[IV*-I{%ld}-%ld], p. 173",n, m))
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x-1+t)*((x-1)^2+t^(n+2))*(x^3+t^(6*m+1));
+do(P0, Strprintf("[II-I*{%ld}-%ld], p. 173",n, m))
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x-1+t)*((x-1)^2+t^(n+2))*(x^3+t^(6*m+5));
+do(P0, Strprintf("[II*-I*{%ld}-%ld], p. 174",n, m))
+
+\\ n >= 0, m = -1
+n = 9;
+P0=t*((x-1)^2+t^n)*(x^3+t^2);
+do(P0, Strprintf("[II*-I*{%ld}-(-1)], p. 174",n))
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x-1+t)*((x-1)^2+t^(n+2))*(x^3+t^(6*m+2));
+do(P0, Strprintf("[IV-I*{%ld}-%ld], p. 174",n, m))
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x-1+t)*((x-1)^2+t^(n+2))*(x^3+t^(6*m+4));
+do(P0, Strprintf("[IV*-I*{%ld}-%ld], p. 174",n, m))
+
+\\ n >= 0, m = -1
+n = 9;
+P0=t*((x-1)^2+t^n)*(x^3+t);
+do(P0, Strprintf("[IV*-I*{%ld}-(-1)], p. 175",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n >= 0
+n = 9;
+P0=x*(x^3+t)*((x-1)^2+t^n);
+do(P0, Strprintf("[IV-II{%ld}], p. 175",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0
+n = 9;
+P0=x*(x^3+t^2)*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[IV*-II{%ld}], p. 175",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n = 0
+P0=(x^2+t^3)*(x^3+t^4);
+do(P0, "[IV*-II{0}], p. 175")
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n >= 0
+n = 9;
+P0=(x^3+t)*(x^2+t^(n+1));
+do(P0, Strprintf("[II-II*{%ld}], p. 176",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n >= 0
+n = 9;
+P0=t*x*(x^3+t)*((x-1)^2+t^n);
+do(P0, Strprintf("[II*-II*{%ld}], p. 176",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0, m >= 0
+n = 9; m = 10;
+P0=x*(x^2+t^(4*m+1))*((x-1)^2+t^n);
+do(P0, Strprintf("[III-I{%ld}-%ld], p. 176",n,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0, m >= 0
+n = 9; m = 10;
+P0=x*(x^2+t^(4*m+3))*((x-1)^2+t^n);
+do(P0, Strprintf("[III*-I{%ld}-%ld], p. 176",n,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x+t)*(x^2+t^(n+2))*(x-1)*((x-1)^2+t^(4*m+1));
+do(P0, Strprintf("[III-I*{%ld}-%ld], p. 177",n,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n, m >= 0
+n = 9; m = 10;
+P0=(x+t)*(x^2+t^(n+2))*(x-1)*((x-1)^2+t^(4*m+3));
+do(P0, Strprintf("[III*-I*{%ld}-%ld], p. 177",n,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n >= 0, m = -1
+n = 9;
+P0=t*x*(x^2+t)*((x-1)^2+t^n);
+do(P0, Strprintf("[III*-I*{%ld}-(-1)], p. 177",n))
+\\ attach one more projective line of mult. 1 to the component 2B
+
+\\ n >= 0
+n = 9;
+P0=(x^4+t)*((x-1)^2+t^n);
+do(P0, Strprintf("[III-II{%ld}], p. 177",n))
+
+\\ n > 0
+n = 9;
+P0=(x^4+t^3)*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[III*-II{%ld}], p. 178",n))
+
+\\ n = 0
+P0=t*(x^2+t)*(x^4+t);
+do(P0, "[III*-II{0}], p. 178")
+
+\\ n >= 0
+n = 9;
+P0=(x^4+t)*(x^2+t^(n+1));
+do(P0, Strprintf("[III-II*{%ld}], p. 178",n))
+
+\\ n >= 0
+n = 9;
+P0=(x^4+t^3)*(x^2+t^(n+2));
+do(P0, Strprintf("[III*-II*{%ld}], p. 178",n))
+
+\\ Parabolic type continued
+
+\\ n, p > 0
+n = 9; p = 8;
+P0=(x^2+t^n)*((x+1)^2+t^p)*(x-1);
+do(P0, Strprintf("[I{%ld-%ld-0}], p. 179",n,p))
+
+\\ n, p, m > 0
+n = 9; p = 8; m = 10;
+P0=(x+t^(2*m))*(x^2+t^(4*m+n))*((x-1)^2+t^p);
+do(P0, Strprintf("[I{%ld}-I{%ld}-%ld}], p. 179",n,p,m))
+
+\\ n, p > 0
+n = 9; p = 8;
+P0=t*(x^2+t^n)*((x+1)^2+t^p)*(x-1);
+do(P0, Strprintf("[I*{%ld-%ld-0}], p. 180",n,p))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n, p > 0, m >= 0
+n = 9; p = 8; m = 10;
+P0=(x+t^(2*m+1))*(x^2+t^(4*m+n+2))*(x-1+t)*((x-1)^2+t^(p+2));
+do(P0, Strprintf("[I*{%ld}-I*{%ld}-%ld], p. 180",n,p,m))
+
+\\ n, p > 0, m >= 0
+n = 9; p = 8; m = 10;
+P0=(x+t^(2*m+1))*(x^2+t^(4*m+p+2))*((x-1)^2+t^n);
+do(P0, Strprintf("[I{%ld}-I*{%ld}-%ld], p. 180",n,p,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0 even, m > 0
+k = 4; m = 10; n=2*k;
+P0=((x^2-t)+t^(m+1))*((x^2-t)^2+t^(2*m+k+2));
+do(P0, Strprintf("[2I{%ld}-%ld], p. 181",n,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0 odd, m > 0
+k = 4; m = 10; n=2*k+1;
+P0=((x^2-t)+t^(m+1))*((x^2-t)^2+t^(2*m+k+2)*x);
+do(P0, Strprintf("[2I{%ld}-%ld}], p. 181",n,m))
+
+\\ n > 0 even, m=0
+k = 4; n=2*k;
+P0=((x^2-t)+a*t)*((x^2-t)^2+t^(k+2));
+do(subst(P0, 'a, 2), Strprintf("[2I{%ld}-0], p. 181",n))
+
+\\ n > 0 odd, m=0
+k = 4; n=2*k+1;
+P0=((x^2-t)+a*t)*((x^2-t)^2+t^(k+2)*x);
+do(subst(P0, 'a, 2), Strprintf("[2I{%ld}-0], p. 181",n))
+
+\\ n > 0 even, m >= 0
+k = 4; m = 10; n=2*k;
+P0=((x^2-t)+t^(m+1)*x)*((x^2-t)^2+t^(2*m+k+3));
+do(P0, Strprintf("[2I*{%ld}-%ld}], p. 181",n,m))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0 odd, m
+k = 4; m = 10; n=2*k+1;
+P0=((x^2-t)+t^(m+1)*x)*((x^2-t)^2+t^(2*m+k+3)*x);
+do(P0, Strprintf("[2I*{%ld}-%ld}], p. 181",n,m))
+
+\\ n, p > 0
+n = 9; p = 8;
+P0=(x^2+t)*(x^2+t^(p+1))*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[II{%ld-%ld}], p. 182",n,p))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0 even
+k = 4; n=2*k;
+P0=x*((x^2-t^3)^2+t^(k+6));
+do(P0, Strprintf("[III{%ld}], p. 182",n))
+
+\\ n > 0 odd
+k = 4; n=2*k+1;
+P0=x*((x^2-t^3)^2+t^(k+5)*x);
+do(P0, Strprintf("[III{%ld}], p. 182",n))
+
+\\ n, p, q > 0
+n = 9; p = 8; q = 5;
+P0=(x^2+t^n)*((x-1)^2+t^p)*((x-2)^2+t^q);
+do(P0, Strprintf("[I{%ld-%ld-%ld}], pp. 182-183",n,p,q))
+
+\\ n, p, q > 0
+n = 9; p = 8; q = 5;
+P0=t*(x^2+t^n)*((x-1)^2+t^p)*((x-2)^2+t^q);
+do(P0, Strprintf("[I*{%ld-%ld-%ld}], pp. 183",n,p,q))
+
+\\ n, p > 0, p even
+n = 9; k = 4; p=2*k;
+P0=((x^2-t)^2+t^(k+2))*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[II{%ld-%ld}], p. 183",n,p))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n, p > 0, p odd
+n = 9; k = 4; p=2*k+1;
+P0=((x^2-t)^2+t^(k+2)*x)*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[II{%ld-%ld}], p. 183",n,p))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n, p > 0
+n = 9; k = 4; l = 0; p=2*k+l;
+P0=t*((x^2-t)^2+t^(k+2)*x^l)*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[II*{%ld-%ld}], p. 184",n,p))
+
+\\ n, p > 0
+n = 9; k = 4; l = 1; p=2*k+l;
+P0=t*((x^2-t)^2+t^(k+2)*x^l)*((x-1)^2+t^(n-1));
+do(P0, Strprintf("[II*{%ld-%ld}], p. 184",n,p))
+
+\\ n > 0
+k = 4; l = 0; n=3*k+l;
+P0=(x^3-t)^2+t^(k+2)*x^l;
+do(P0, Strprintf("[III{%ld}], p. 184",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0
+k = 4; l = 1; n=3*k+l;
+P0=(x^3-t)^2+t^(k+2)*x^l;
+do(P0, Strprintf("[III{%ld}], p. 184",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0
+k = 4; l = 2; n=3*k+l;
+P0=(x^3-t)^2+t^(k+2)*x^l;
+do(P0, Strprintf("[III{%ld}], p. 184",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0
+k = 2; l = 0; n=3*k+l;
+P0=t*((x^3-t)^2+t^(k+2)*x^l);
+do(P0, Strprintf("[III*{%ld}], p. 184",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0
+k = 2; l = 1; n=3*k+l;
+P0=t*((x^3-t)^2+t^(k+2)*x^l);
+do(P0, Strprintf("[III*{%ld}], p. 184",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ n > 0
+k = 2; l = 2; n=3*k+l;
+P0=(x^3-t)^2+t^(k+2)*x^l;
+do(P0, Strprintf("[III{%ld}], p. 184",n))
+genus2red(0,subst(P0,'t,3),3)
+
+\\ Extras : p = 3
+\e
+\\ Colin Stahlke, bug28
+genus2red(0,-x^6-6*x^2-7,3)
+genus2red(0,-9*x^6+6*x^5-8*x^4-5*x^3+5*x^2-10*x+3,3)
+\\ M. Stoll, bug28
+genus2red(0,3*x^6+3*x^4+3*x^3+x^2-5*x-5,3)
+\\ Colin Stahlke, bug28
+genus2red(0,-3*x^6+6*x^5-1*x^4+6*x^3-6*x^2-1*x-6,3)
+\\ J. Mueller, bug28
+genus2red(0,(x^3+2*x+1)*(x^3+3^2*x^2+3^8),3)
+
+\\ A. Brumer, bug28
+P=x^6+4*x^5-24*x^4-16*x^3-52*x^2-48*x;
+genus2red(0,P,3)
+
+P=x^6+4*x^5+24*x^4+32*x^3+56*x^2+48*x+24;
+genus2red(0,P,3)
+
+P=24*x^5+56*x^4+76*x^3+33*x^2-4*x-20;
+genus2red(0,P,3)
+
+P=-3*x^6+6*x^5-25*x^4+36*x^3-69*x^2+38*x-39;
+genus2red(0,P,3)
+
+\\ M. Stoll, bug28
+P=-5*x^5+5*x^4+10*x^3-7;
+genus2red(1,P,3)
+
+P=-5*x^6-3*x^5-10*x^4-10*x^3-7;
+genus2red(1,P,3)
+
+P=3*x^5+5*x^4+5*x-4;
+genus2red(1,P,3)
+
+Q=x^2+x; P=-9*x^6+6*x^5-8*x^4-5*x^3+5*x^2-10*x+3;
+genus2red(Q,P,3)
+
+\\ M. Stoll, bug27
+Q=x^3+1; P=-7*x^6+5*x^3+5*x^2-6*x+1;
+genus2red(Q,P,3)
+
+\\ #1596
+genus2red(0,27*x^5 + 97*x^4 + 118*x^3 + 60*x^2 + 13*x + 1,3)
+
+\\ #1597
+genus2red(x,-x^6 - 3*x^4 - 10*x^2 - 1,3)
+\\ #1597
+genus2red(x^3+x^2+x+1,-60*x^6-203*x^5-291*x^4-244*x^3-129*x^2-41*x-7)
+\\ #1597
+genus2red(0,6*x^6+5*x^4+x^2+1,7)
+\\ #1597
+genus2red(x^3-1,1)
diff --git a/src/test/in/graph b/src/test/in/graph
new file mode 100644
index 0000000..c629aeb
--- /dev/null
+++ b/src/test/in/graph
@@ -0,0 +1,30 @@
+HEAP=[31, if(precision(1.)==38,282,296)];
+default(realprecision,38);
+\e
+plotinit(0,500,500)
+plotmove(0,0,0);plotbox(0,500,500)
+plotmove(0,200,150)
+plotcursor(0)
+psdraw([0,0,0])
+plotinit(1,700,700)
+plotkill(1)
+plotmove(0,0,900);plotlines(0,900,0)
+plotlines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+plotmove(0,243,583);plotcursor(0)
+plot(x=-5,5,sin(x),-1,1)
+plotpoints(0,225,334)
+plotpoints(0,vector(10,k,10*k),vector(10,k,5*k*k))
+psdraw([0,20,20])
+psploth(x=-5,5,sin(x))
+psploth(t=0,2*Pi,[sin(5*t),sin(7*t)],1,100)
+psplothraw(vector(100,k,k),vector(100,k,k*k/100))
+plotmove(0,50,50);plotrbox(0,50,50)
+plotrline(0,200,150)
+plotcursor(0)
+plotrmove(0,5,5);plotcursor(0)
+plotrpoint(0,20,20)
+plotinit(3,600,600);plotscale(3,-7,7,-2,2);plotcursor(3)
+plotmove(0,100,100);plotstring(0,Pi)
+plotmove(0,200,200);plotstring(0,"(0,0)")
+psdraw([0,10,10])
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/help b/src/test/in/help
new file mode 100644
index 0000000..00d6f59
--- /dev/null
+++ b/src/test/in/help
@@ -0,0 +1,25 @@
+f()=1;
+?x
+?sin
+?f
+?echo
+?default(echo)
+?default(log)
+alias(new,sin)
+?new
+addhelp(x,"test1")
+addhelp(sin,"test2")
+addhelp(f,"test3")
+addhelp(echo,"test4")
+addhelp(new,"test5")
+?x
+?sin
+?f
+?echo
+?new
+?does_not_exist
+?
+?.
+?\
+?1
+\t
diff --git a/src/test/in/history b/src/test/in/history
new file mode 100644
index 0000000..12dbac1
--- /dev/null
+++ b/src/test/in/history
@@ -0,0 +1,17 @@
+2
+3
+4
+5
+%``````
+%````
+%3
+%
+%#3 < 5
+%#``` < 5
+%#  < 5
+default(histsize,1)
+1;
+%``
+default(histsize,10)
+%``
+%13
diff --git a/src/test/in/ideal b/src/test/in/ideal
new file mode 100644
index 0000000..3556795
--- /dev/null
+++ b/src/test/in/ideal
@@ -0,0 +1,72 @@
+Qi = nfinit(x^2+1);
+idealintersect(Qi,0,1)
+Q = nfinit(y);
+idealaddtoone(Q,1,[;])
+idealaddtoone(Q,2,[;])
+idealaddtoone(Q,[;],[;])
+idealaddtoone(Q,[;],1)
+idealaddtoone(Q,[1,[;]])
+idealaddtoone(Q,[1,[;],Mat(1/2)])
+ideallog(Q,2,idealstar(Q,4,1))
+idealstar(Qi,matdiagonal([6,12]))
+P = idealprimedec(Qi,2)[1];
+idealprincipalunits(Qi,P,10)
+idealintersect(Qi, 1/2,1/2)
+ideallog(Q,1/2,idealstar(Q,3,1))
+
+K=nfinit(x^2-236*x+13384);\\ Q(sqrt(60)), in disguise
+idealhnf(K, qfbprimeform(60,2))
+
+K=nfinit(x^3-2);
+u=nfalgtobasis(K,x);v=nfalgtobasis(K,x^2);
+idealhnf(K,Mat([u,v]))
+idealhnf(K,1/4,1/6)
+idealhnf(K,4,[6,0,0]~)
+idealhnf(K,2,1/6)
+idealhnf(K,1/3,u/2)
+idealhnf(K,u,v/2)
+P = idealprimedec(K,5);
+idealval(K,P[1],P[2])
+idealval(K,P[2],P[1])
+idealval(K,P[2],P[2])
+P[1][2] += 5;
+idealval(K,P[1],P[1])
+idealmul(K,P[1],P[1].gen[2])
+idealnorm(K,P[1])
+idealinv(K,Mod(x,K.pol))
+idealpow(K,idealhnf(K,x),-2)
+v=[1,1/2,x,Mod(x,K.pol),P[1],[;],Mat(1),vector(5),[1,2,3;4,5,6;7,8,9],[1,2,3;0,4,5;0,0,6],I];
+for(i=1,#v, if(!nfisideal(K,v[i]),print(i)))
+idealred(K,2)
+idealred(K,[2,[;]])
+
+nf=nfinit(x^2+x+1);
+p = 67452192952521724999; pi = idealprimedec(nf,p)[1];
+q = 762234946175168528650011228121; qi = idealprimedec(nf,q)[1];
+f=idealfactor(nf, idealdiv(nf,pi,qi))
+id=idealfactorback(nf,f)
+idealnumden(nf,id)
+idealnumden(nf,pi)
+idealnumden(nf,1)
+idealnumden(nf,1/2)
+idealnumden(nf,x/2)
+nffactorback(nf, [3, x+1, [1,2]~], [1, 2, 3])
+
+test(nf,F,v)=
+{
+  my(Y = idealchinese(nf,F,v), P = F[,1], E = F[,2]);
+  for (i=1,#P, if( nfeltval(nf, nfeltadd(nf,v[i],-Y),P[i]) < E[i], error(i)));
+  Y;
+}
+v = [x,0,[0,1/7^4]~,Mod(x,nf.pol)];
+F = idealfactor(nf, 2*3*7);
+F[,2] *= 3;
+test(nf,F,v)
+F[,2] = [0,1,-2,3]~;
+test(nf,F,v)
+F[,2] = [1,1,1,1]~;
+test(nf,F,[1,0,0,0])
+test(nf,F,[0,1/11,0,0])
+
+K=nfinit(2*x^2+1);
+idealprimedec(K,2)
diff --git a/src/test/in/idealappr b/src/test/in/idealappr
new file mode 100644
index 0000000..74aced2
--- /dev/null
+++ b/src/test/in/idealappr
@@ -0,0 +1,8 @@
+idealaddtoone(nfinit(x),[1,[;]]);
+K=nfinit(x^2+23); A=idealhnf(K,x/2);
+idealtwoelt(K, 3, 6)
+idealtwoelt(K, A)
+idealtwoelt(K, A, x)
+idealtwoelt(K, [;])
+idealtwoelt(K, [;], 1)
+idealtwoelt(K, [;], 0)
diff --git a/src/test/in/idealramgroups b/src/test/in/idealramgroups
new file mode 100644
index 0000000..5916693
--- /dev/null
+++ b/src/test/in/idealramgroups
@@ -0,0 +1,16 @@
+{
+  K=nfinit(x^30 - 240*x^28 + 24364*x^26 - 1366520*x^24 + 46492470*x^22 - 994986280*x^20 + 13527103660*x^18 - 116596891080*x^16 + 634140564945*x^14 - 2144111162280*x^12 + 4349007947424*x^10 - 4933119511040*x^8 + 2746986107136*x^6 - 564152514560*x^4 + 40138752000*x^2 - 409600000);
+  G=galoisinit(K);
+  forprime(p=2,37,
+    pr = idealprimedec(K,p)[1];
+    print(pr.p,":", apply(galoisidentify,idealramgroups(K,G,pr))))
+}
+K=nfinit(x^6 + 12*x^5 + 60*x^4 + 376*x^3 + 80268*x^2 + 4569240*x + 66227068);
+G=galoisinit(K);
+D=idealprimedec(K,29)[1];
+[idealfrobenius(K,G,D),idealramgroups(K,G,D)]
+
+K=nfinit(polcyclo(24));
+G=galoisinit(K);
+P=idealprimedec(K,2)[1];
+idealramgroups(K,G,P)
diff --git a/src/test/in/incgam b/src/test/in/incgam
new file mode 100644
index 0000000..f3cf3d7
--- /dev/null
+++ b/src/test/in/incgam
@@ -0,0 +1,52 @@
+Vs=[0,1/2,10-I,1+128*I];
+Vx=[19+236*I,1/10,-1/10,I/10,-I/10,1/10-I/10,100,-100,100+1001*I];
+
+test(fun, p) =
+{
+  my (P = 1/10.^(p-1));
+  for (i=1,#Vs,
+    for (j=1,#Vx,
+      my (v,w, s = Vs[i], x = Vx[j]);
+      default(realprecision, p);      v = fun(s, x);
+      default(realprecision, p + 38); w = fun(s, x);
+\\      e = abs((v-w)/w);
+      e = if (abs(w) < 1, abs(v-w), abs((v-w)/w));
+      if (e > P, printf("%9s, %13s: %.1e\n", s,x,e));
+    )
+  );
+}
+
+test(incgam, 19)
+test(incgam, 38)
+test(incgam, 100)
+
+mylog(x)=if(x==0, "oo", round(log(abs(x))/log(10)));
+
+Vs=[1/10+1/5*I,-1/10,-1/10,-1/10+2/5*I,-1/10+2/5*I,2/5*I,2/5*I,-2/5*I,-2/5*I,-1,-1,-20,-200001/10000];
+Vx=[13/10,1/10000,13/10,13/10,1/10000,11/10,1/10000,11/10,1/10000,1/10000,11/10,11/10,11/10];
+
+default(realprecision, 100);
+VR = vector(#Vs,j,incgam(Vs[j]*1.,Vx[j]*1.));
+test(fun, prec)=
+{
+  default(realprecision,prec);
+  for(j=1,#Vs,
+    res = fun(Vs[j]*1.,Vx[j]*1.) - VR[j];
+    print(j, ": ", mylog(res))
+  );
+}
+test(incgam, 38)
+test(incgam, 76)
+
+default(realprecision, 38);
+incgam(1/2,10^8)
+eint1(-0.3-95*I)
+eint1(100)
+incgam(-10+1e-100,1)
+eint1(2,5)
+\p1000
+eint1(100)
+\p481
+eint1(150)
+
+eint1(0)
diff --git a/src/test/in/interpol b/src/test/in/interpol
new file mode 100644
index 0000000..5f7efff
--- /dev/null
+++ b/src/test/in/interpol
@@ -0,0 +1,11 @@
+default(realprecision, 38);
+polinterpolate([2,3,4])
+polinterpolate([2,3,4] * Mod(1,7))
+polinterpolate([2,3,4] * Mod(1,7),, 0)
+polinterpolate([1,2,4], [2,3,4])
+polinterpolate([1,2,4] * Mod(1,7), [2,3,4], 0)
+polinterpolate([1,2,4], [2,3,4], 1.5)
+polinterpolate([1,2,4], [2,3,4], 1.5, &e); e
+polinterpolate([1,2],[0,0])
+polinterpolate([],[])
+polinterpolate([1],[2])
diff --git a/src/test/in/intnum b/src/test/in/intnum
new file mode 100644
index 0000000..bdaeda7
--- /dev/null
+++ b/src/test/in/intnum
@@ -0,0 +1,75 @@
+default(echo,1);
+allocatemem(20 * 10^6);
+check(a,b) = my(t = abs((a-b)/b)); if (t, ceil(log(t)/log(10)), "-oo");
+oo = [1];
+\p96
+check(intcirc(s=1, 0.5, zeta(s)), 1)
+check(intlaplaceinv(x=2, 1, 1/x), 1)
+m = intnumstep();
+check(intlaplaceinv(x=2, 1, 1/x, m+1), 1)
+check(intlaplaceinv(x=5, 1, 1/x), 1)
+check(intlaplaceinv(x=100, 1, 1/x), 1)
+A = intmellininv(s=2,4, gamma(s)^3);
+tab = intfuncinit(t=[-oo, 4.5],[oo, 4.5], gamma(2+I*t)^3, 1);
+check(intmellininvshort(2,4,tab), A)
+f(x) = 1/(exp(x)-1) - exp(-x)/x;
+F = truncate( f(t + O(t^7)) );
+g(x) = if (x > 1e-18, f(x), subst(F,t,x));
+check(intnum(x = 0, [oo,1],  f(x)), Euler)
+check(intnum(x = 0, [oo,1],  g(x)), Euler)
+check(intnum(x = 0, 1,  1/sqrt(x)), 2)
+check(intnum(x = [0,-1/2], 1,  1/sqrt(x)), 2)
+check(intnum(x = 0, [oo,1], sin(x)/x), Pi/2)
+check(intnum(x = 0, [oo,-I], sin(x)/x), Pi/2)
+check(intnum(x = 0, [oo,-2*I], sin(2*x)/x), Pi/2)
+A=intnum(x=0,1,(1-cos(x))/x^2)+intnum(x=1,oo,1/x^2)-intnum(x=1,[oo,I],cos(x)/x^2);
+check(A, Pi/2)
+check(intnum(x = 0, [oo, 1], sin(x)^3*exp(-x)), 3/10)
+check(intnum(x = 0, [oo,-I], sin(x)^3*exp(-x)), 3/10)
+tab = intnuminit(0,[oo,-I], m+1);
+check(intnum(x = 0, oo, sin(x)^3*exp(-x), tab), 3/10)
+check(intnum(x = 0, [oo, -I], x^2*sin(x)), -2)
+tab = intnuminit(-1,1);
+check(intnum(x=-1,1, intnum(y=-sqrt(1-x^2),sqrt(1-x^2),x^2+y^2,tab),tab), Pi/2)
+\\
+\p 308
+a = sumpos(n=1, 1/(n^3+n+1));
+tab = sumnuminit(2);
+b = sumnum(n=1,2, 1/(n^3+n+1), tab);
+check(a, b)
+check(sumnum(n=1,2, 1/(n^3+n+1), tab, 1), a)
+c = sumnum(n=1,2,1/(n^2+1),tab,1);
+d = sumpos(n=1,1/(n^2+1));
+check(c, d)
+check(sumnum(n=1,2,n^(-4/3),,1), zeta(4/3))
+tab = sumnuminit([2,-3/2]);
+check(sumnum(n=1,[2,-3/2],1/(n*sqrt(n)),tab,1), zeta(3/2))
+\\
+check(suminf(n=1, 2^(-n)), 1)
+check(sumpos(n=1, 2^(-n)), 1)
+check(sumnum(n=1,[2,log(2)],2^(-n), intnumstep()+1, 1), 1)
+\\
+tab = sumnuminit(2,,-1);
+a = sumnumalt(n=1,2,1/(n^3+n+1),tab,1);
+b = sumalt(n=1,(-1)^n/(n^3+n+1));
+check(a, b)
+
+\p96
+T=intnuminitgen(t,0,[1],exp(2*sinh(t)));
+check(intnum(x=0,[1],1/(1+x^2),T), Pi/2)
+T=intnuminitgen(t,0,[[1],1],exp(t-exp(-t)));
+check(intnum(x=0,[[1],1],exp(-x),T), 1)
+intfourierexp(t=0,[[1],1], 1/2, exp(-t^2))
+intfouriercos(t=0,[[1],1], 1/2, exp(-t^2))
+intfouriersin(t=0,[[1],1], 1/2, exp(-t^2))
+
+\p38
+intnumromb(x=0,1,sin(x))
+intnumromb(x=0,1,sin(x), 1)
+intnumromb(x=1,100,exp(-x^2), 2)
+intnumromb(x=0,1,sin(x)/x, 3)
+
+f(x) = -log(cos(x));
+F = truncate( f(t + O(t^16)) );
+g(x) = if (x > 1e-2, f(x), subst(F,t,x));
+sumpos(n=1,g(1/n))
diff --git a/src/test/in/io b/src/test/in/io
new file mode 100644
index 0000000..8303869
--- /dev/null
+++ b/src/test/in/io
@@ -0,0 +1,23 @@
+F="io-testfile";
+del()=system(Str("rm -f ", F));
+del()
+write(F, 123)
+write(F, 456)
+write1(F, "a",7)
+write1(F, "b")
+write(F)
+writetex(F, 1/2)
+readstr(F)
+del()
+\\
+write(F, 1)
+write(F, "1+2")
+readvec(F)
+del()
+\\
+writebin(F,1)
+read(F)
+del()
+writebin(F)
+read(F)
+del()
diff --git a/src/test/in/ispower b/src/test/in/ispower
new file mode 100644
index 0000000..21495b9
--- /dev/null
+++ b/src/test/in/ispower
@@ -0,0 +1,117 @@
+do(n)= my(k,z); if (k=ispower(n,,&z), print([k,z]));
+{v = [
+3^101*2^20,
+3^101*2^101,
+3^101*2^101*5,
+2^20*3^15*5^5,
+2^3*103^6,
+2^5*103^10,
+2^7*103^10,
+2^15*103^10,
+2^21*103^42,
+2^35*103^70,
+2^105*103^210,
+103^100003,
+541^121
+]; }
+
+for (i=1, #v, do(v[i]))
+for (i=1, 10^6, do(i))
+\\#1259
+do(-16)
+do(-64)
+do(-8)
+do(-8 * 103^6)
+do(-1/64)
+
+for (i=1, #v, if (!ispowerful(v[i]), print(i)))
+ispowerful(5^3*(10^1000+1)^2)
+
+v = [1, 2^10, 2^15, 2^32, 2^64, 2^128];
+
+/*
+correct = vector(#v);
+{
+for (k = 1, #v,
+  my(u = v[k], s,t);
+  for(i=u, u+10^4,
+    if ((a = ispower(i,,&p)) && isprime(p), s += a; t += p);
+    if (isprime(i), s ++; t += i);
+  );
+  correct[k] = [s,t];
+)
+}
+*/
+{
+for (k = 1, #v,
+  my(u = v[k], s,t);
+  s = sum(i=u, u+10^4, isprimepower(i));
+  t = 0;for (i = u, u+10^4, if (isprimepower(i,&p), t += p));
+  print([s,t]);
+\\  if ([s,t] != correct[k], error(k))
+)
+}
+
+isprimepower(541^121)
+ispowerful(4)
+
+p = 10^6+3; q = 10^6+33; r = 10^6+37;
+v = [Mod(p, p^2*q), Mod(6*p, q*(p*r)^2), Mod(6, p^2*q), Mod(4*p, 8*p^2*q)];
+[ispower(a,2) | a<-v]
+[ispower(a,3) | a<-v]
+[if(ispower(a,2,&b), b) | a<-v]
+[if(ispower(a,3,&b), b) | a<-v]
+[if(issquare(a^2,&b), b) | a<-v]
+[if(ispower(a^3,3,&b), b) | a<-v]
+ispower(Mod(2, 7*5^3), 7, &z); z
+issquare(Mod(1,2))
+issquare(Mod(0,2),&s);s
+issquare(Mod(2,3))
+issquare(Mod(13,121))
+
+default(realprecision,38);
+if(issquare(Pol(0),&z),z)
+issquare(Pol(4))
+issquare(x^2+x)
+issquare(y^2*x^0)
+issquare(x^0/4)
+if(issquare(y^2*x^0,&z),z)
+issquare(2*x^4+x^3+Mod(1,2))
+issquare(x^2+Mod(1,2))
+if(issquare(x^2+Mod(1,2),&z),z)
+t=ffgen(2^3,'t);
+if(issquare((t*x^2+(1+t)*x+1)^2,&z),z)
+if(issquare((x^4+x^3+3*x^2+2*x+4)*Mod(1,5),&z),z)
+if(issquare(4/x^2,&z),z)
+issquare(1.5)
+if(issquare(1.5,&z),z)
+issquare(4/9)
+issquare(4/3)
+issquare(I)
+issquare(ffgen(2^3,'t))
+if(issquare(ffgen(2^3,'t),&z),z)
+issquare(O(x))
+issquare(x+O(x^2))
+issquare(4*x^2+O(x^3))
+issquare(4/x^2)
+ispower(-8,4)
+if(ispower(-8,3,&z),z)
+ispower(-8/27,3)
+if(ispower(-8/27,3,&z),z)
+ispower(Pol(0),3)
+ispower(x^2,3)
+ispower(x^3+x,3)
+ispower(x^3+4,3)
+if(ispower(Pol(8),3,&z),z)
+if(ispower((2+x)^3,3,&z),z)
+ispower((2+x)^3-1,3)
+ispower(1009^11)
+ispower(-1.,3)
+ispower(I,3)
+if(ispower(1/(2*x)^3,3,&z), z)
+if(ispower(1+x+O(x^2),3,&z), z)
+if(ispower((-2/3)^5,5,&z),z)
+ispower(3^(11^2))
+ispower((2/3)^(11^2))
+ispower(30011^(17*11))
+issquare(Mod(3,22))
diff --git a/src/test/in/isprime b/src/test/in/isprime
new file mode 100644
index 0000000..78998af
--- /dev/null
+++ b/src/test/in/isprime
@@ -0,0 +1,16 @@
+isprime(5368962301599408606279497323618896374219)
+isprime(4309513411435775833571)
+isprime(26959946667150639794667015087019630673557916260026308143510066298881)
+
+p=10^6+3; q=10^6+33;
+isprime(1+24*p*q, 1)
+isprime(1+232*p^2*q^3, 1)
+
+isprime([2,3,4])
+isprime([2,3,4],1)
+isprime([2,3,4],2)
+ispseudoprime([1,3,4,5],2)
+\\isprime(2^3515+159, 2) 10 min
+\\isprime(2^2000+841, 2) 1 min
+\\isprime(2^1600+895, 2) 27s
+isprime(2^1000+297, 2)
diff --git a/src/test/in/iterator b/src/test/in/iterator
new file mode 100644
index 0000000..1ae8e8b
--- /dev/null
+++ b/src/test/in/iterator
@@ -0,0 +1,5 @@
+forcomposite(a=2,10,print(a))
+forcomposite(a=5,11,print(a))
+forcomposite(a=6,12,print(a))
+forcomposite(a=6,,print(a); if (a>8, break))
+forcomposite(a=6,12,print(a); a=1)
diff --git a/src/test/in/krasner b/src/test/in/krasner
new file mode 100644
index 0000000..ab09b69
--- /dev/null
+++ b/src/test/in/krasner
@@ -0,0 +1,15 @@
+allocatemem(20*10^6);
+filter_output(p, v) = vecsort(vector(#v, j, my([D,e,f,d]=v[j]); [valuation(poldisc(D), p), e, f, d]), cmp);
+
+do(p,N,flag)=filter_output(p, padicfields(p,N,flag));
+
+p = 2; for (d = 2, 50, if (d%p, print(do(p,d,1))))
+p = 3; for (d = 2, 50, if (d%p, print(do(p,d,1))))
+do(2, 105, 1)
+do(5, 21, 1)
+do(23, 55, 1)
+do(23459, 12, 1)
+do(2, [8,12], 1)
+padicfields(2^64+13, 8, 2)
+do(2^64+13, 2, 1)
+do(2, 4, 1)
diff --git a/src/test/in/lambert b/src/test/in/lambert
new file mode 100644
index 0000000..3597451
--- /dev/null
+++ b/src/test/in/lambert
@@ -0,0 +1,7 @@
+do(y)=my(x = lambertw(y)); exp(x)*x / y;
+do(-1)
+do(I)
+default(realprecision,38);
+do(2)
+default(realprecision,211);
+do(1e14)
diff --git a/src/test/in/lex b/src/test/in/lex
new file mode 100644
index 0000000..e255dd2
--- /dev/null
+++ b/src/test/in/lex
@@ -0,0 +1,15 @@
+v = [0, 2, [1,2], [1,2;3,4], [1,0;1,2], [1,2,3]~, [1,2,3;4,5,6]];
+
+isvec(x) = type(x) == "t_VEC" || type(x) == "t_COL";
+{
+  for (i = 1, #v,
+    for (j = i, #v,
+      s = lex(v[i],v[j]);
+      print([i,j,s]);
+      if (s != -lex(v[j], v[i]), error(2));
+      if (isvec(v[i]) && lex(Vecsmall(v[i]), v[j]) != s, error(3));
+      if (isvec(v[j]) && lex(v[i], Vecsmall(v[j])) != s, error(4));
+
+    )
+  );
+}
diff --git a/src/test/in/lift b/src/test/in/lift
new file mode 100644
index 0000000..4db29b2
--- /dev/null
+++ b/src/test/in/lift
@@ -0,0 +1,12 @@
+v=[Mod(y,y^2+1), Mod(1,z), Mod(2,3), 1+O(3)];
+T=Pol(v,'x);
+Z=[1, Mod(5,3), "x", 3+O(3^3), -1/3+O(3), Mod(x,x^2), x*Mod(1,3) + Mod(2,3)];
+F=[lift, a->lift(a,'x), a->lift(a,'y), a->lift(a,'z), centerlift,liftall,liftint,liftpol];
+{
+  for(i=1,#F,
+    my (f=F[i]);
+    print(f);
+    for (j=1,#Z, print(f(Z[j])))
+  )
+}
+
diff --git a/src/test/in/lindep b/src/test/in/lindep
new file mode 100644
index 0000000..7515d46
--- /dev/null
+++ b/src/test/in/lindep
@@ -0,0 +1,17 @@
+lindep([sqrt(2), sqrt(3), sqrt(2)+sqrt(3)])
+lindep([1, 2 + 3 + 3^2 + 3^3 + 3^4 + O(3^5)])
+lindep([1,2,3;4,5,6;7,8,9])
+lindep([x*y, x^2 + y, x^2*y + x*y^2, 1])
+z = sqrt(1+5*y+y^2+y^3);
+seralgdep(z, 2,3)
+seralgdep(z, 2,2)
+seralgdep(1/(1-y+O(y^5)), 1,1)
+seralgdep(1+5*y+O(y^3), 1,10)
+lindep([])
+lindep([0])
+lindep([1])
+lindep([1,I])
+algdep(1,0)
+algdep(1,-1)
+z=sqrt(2+O(7^4)); algdep(z,2)
+lindep(Mod([E*x, E*x + E, E^2*x^2 + E*x + 2*E], E^3))
diff --git a/src/test/in/linear b/src/test/in/linear
new file mode 100644
index 0000000..e5dd68b
--- /dev/null
+++ b/src/test/in/linear
@@ -0,0 +1,93 @@
+HEAP=[98, if(precision(1.)==38,10174,10354)];
+default(realprecision,38);
+\e
+algdep(2*cos(2*Pi/13),6)
+algdep(2*cos(2*Pi/13),6,15)
+charpoly([1,2;3,4],z)
+charpoly(Mod(x^2+x+1,x^3+5*x+1),z)
+charpoly([1,2;3,4],z,1)
+charpoly(Mod(1,8191)*[1,2;3,4],z,2)
+lindep(Mod(1,7)*[2,-1;1,3])
+lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)])
+lindep([(1-3*sqrt(2))/(3-2*sqrt(3)),1,sqrt(2),sqrt(3),sqrt(6)],14)
+matadjoint([1,2;3,4])
+matcompanion(x^5-12*x^3+0.0005)
+matdet([1,2,3;1,5,6;9,8,7])
+matdet([1,2,3;1,5,6;9,8,7],1)
+matdetint([1,2,3;4,5,6])
+matdiagonal([2,4,6])
+mateigen([1,2,3;4,5,6;7,8,9])
+mathess(mathilbert(7))
+mathilbert(5)
+amat=1/mathilbert(7)
+mathnf(amat)
+mathnf(amat,1)
+mathnf(amat,4)
+mathnf(amat,5)
+mathnfmod(amat,matdetint(amat))
+mathnfmodid(amat,123456789*10^100)
+matid(5)
+matimage([1,3,5;2,4,6;3,5,7])
+matimage([1,3,5;2,4,6;3,5,7],1)
+matimage(Pi*[1,3,5;2,4,6;3,5,7])
+matimagecompl([1,3,5;2,4,6;3,5,7])
+matimagecompl(Pi*[1,3,5;2,4,6;3,5,7])
+matindexrank([1,1,1;1,1,1;1,1,2])
+matintersect([1,2;3,4;5,6],[2,3;7,8;8,9])
+matinverseimage([1,1;2,3;5,7],[2,2,6]~)
+matisdiagonal([1,0,0;0,5,0;0,0,0])
+matker(matrix(4,4,x,y,x/y))
+matker(matrix(4,4,x,y,sin(x+y)))
+matker(matrix(4,4,x,y,x+y),1)
+matkerint(matrix(4,4,x,y,x*y))
+matkerint(matrix(4,4,x,y,x*y),1)
+matkerint(matrix(4,6,x,y,2520/(x+y)))
+matmuldiagonal(amat,[1,2,3,4,5,6,7])
+matmultodiagonal(amat^-1,%)
+matpascal(8)
+matrank(matrix(5,5,x,y,x+y))
+matrix(5,5,x,y,gcd(x,y))
+matrixqz([1,3;3,5;5,7],0)
+matrixqz([1/3,1/4,1/6;1/2,1/4,-1/4;1/3,1,0],-1)
+matrixqz([1,3;3,5;5,7],-2)
+matsize([1,2;3,4;5,6])
+matsnf(1/mathilbert(6))
+matsnf(x*matid(5)-matrix(5,5,j,k,1),2)
+matsolve(mathilbert(10),[1,2,3,4,5,6,7,8,9,0]~)
+matsolvemod([2,3;5,4],[7,11]~,[1,4]~)
+matsolvemod([2,3;5,4],[7,11]~,[1,4]~,1)
+matsupplement([1,3;2,4;3,6])
+mattranspose(vector(2,x,x))
+%*%~
+norml2(vector(10,x,x))
+qfgaussred(mathilbert(5))
+qfjacobi(mathilbert(6))
+m=1/mathilbert(7)
+mp=concat(m,matid(7))
+qflll(m)
+qflllgram(m)
+qflllgram(m,1)
+qflllgram(mp~*mp,4)
+qflll(m,1)
+qflll(m,2)
+qflll(mp,4)
+qfminim([2,1;1,2],4,6)
+qfperfection([2,0,1;0,2,1;1,1,2])
+qfsign(mathilbert(5)-0.11*matid(5))
+trace(1+I)
+trace(Mod(x+5,x^3+x+1))
+Vec(sin(x))
+vecmax([-3,7,-2,11])
+vecmin([-3,7,-2,11])
+concat([1,2],[3,4])
+concat(Mat(vector(4,x,x)~),vector(4,x,10+x)~)
+vecextract([1,2,3,4,5,6,7,8,9,10],1000)
+vecextract(matrix(15,15,x,y,x+y),vector(5,x,3*x),vector(3,y,3*y))
+round((1.*mathilbert(7))^(-1) << 77) / 2^77
+vecsort([8,7,6,5],,1)
+vecsort([[1,5],[2,4],[1,5,1],[1,4,2]])
+vecsort(vector(17,x,5*x%17))
+vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],2)
+vecsort([[1,8,5],[2,5,8],[3,6,-6],[4,8,6]],[2,1])
+vector(10,x,1/x)
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/list b/src/test/in/list
new file mode 100644
index 0000000..834cafe
--- /dev/null
+++ b/src/test/in/list
@@ -0,0 +1,37 @@
+L = List();
+for (i=1,10^5,listput(L,i))
+L = List([1,2,3]);
+for (i=1,5000,listinsert(L,i,3))
+L = List([1,2,3,3]);
+concat(L,5)
+concat(1,L)
+L = concat(L,L)
+listsort(L); L
+listsort(L,1); L
+listpop(L); L
+listpop(L,1); L
+\\
+L = List([[1,2,3], 2])
+L[1][1] = 3
+L
+L = List([Vecsmall([1,2,3]), 2])
+L[1][1] = 3
+
+L = List(); listput(L,1); listpop(L); listpop(L);
+
+matdiagonal(List([0]))
+g(L)=for(i=1,5,listput(L,5-i));L;
+l=List([10,9,8,7,6,5]); g(l)
+l
+listkill(l)
+listcreate()
+
+subst(List([x,x^2+y]),x,1)
+substvec(List([x,y]), [x,y], [y,x])
+substpol(List([x^2,x^3]), x^2, y)
+
+getheap()[1]
+
+chinese(List())
+chinese(List([Mod(1,3)]))
+chinese(List([Mod(0,2),Mod(1,3),Mod(2,5)]))
diff --git a/src/test/in/lll b/src/test/in/lll
new file mode 100644
index 0000000..73b8609
--- /dev/null
+++ b/src/test/in/lll
@@ -0,0 +1,18 @@
+qflllgram(matid(2)*1.)
+m=[219902325555200,60779507942430,113687426768697,93478400051083;219902325555200 ,60779507942430,61044718855924,60943417301157;214748364800,155393376570,17984250 9148,115833849065;214748364800,155393376570,134851934330,188630128295]; qflll(m, 1)
+M=[x, x^3 - 852*x^2 - 833561*x; x^3 + 1053355*x^2, x^5 + 1052503*x^4 - 898292021*x^3 - 878035647155*x^2 + 1];
+
+test()=
+{
+for(i=1,#v,
+  for (j=1,#f,
+    print(iferr(f[j](v[i]),E,E));
+  )
+);
+}
+v=[[;],Mat(1),Mat(0),Mat([1,2]), [x, x+1; x^2, x^2+x], M];
+f=[qflll,x->qflll(x,1),x->qflll(x,2),x->qflll(x,4),x->qflll(x,5),x->qflll(x,8)];
+test();
+v=vector(#v,i,v[i]~*v[i]);
+f=[qflllgram,x->qflllgram(x,1),x->qflllgram(x,4),x->qflllgram(x,5)];
+test();
diff --git a/src/test/in/log b/src/test/in/log
new file mode 100644
index 0000000..5339f1f
--- /dev/null
+++ b/src/test/in/log
@@ -0,0 +1,4 @@
+default(realprecision,38);
+log(1+10^-30)
+lngamma(1+10^-30)
+iferr(log(2+O(33)),E,E)
diff --git a/src/test/in/logint b/src/test/in/logint
new file mode 100644
index 0000000..9adae55
--- /dev/null
+++ b/src/test/in/logint
@@ -0,0 +1,8 @@
+logint(2^99,2)
+logint(2^100,2,&z)
+z
+logint(5^100,2,&z)
+z
+logint(5^100,3)
+logint(5^100,4)
+logint(5^100,5)
diff --git a/src/test/in/mat b/src/test/in/mat
new file mode 100644
index 0000000..2033e72
--- /dev/null
+++ b/src/test/in/mat
@@ -0,0 +1,94 @@
+iferr(Mat([1]) + matrix(0,1),E,E)
+iferr(1/matrix(2,2,i,j,Mod(0, 2)),E,E)
+test(n)=
+{
+  until(matrank(M)==n,M=matrix(n,n,i,j,random(Mod(1,2))));
+  if(M^-1*M!=matid(n),error("F2m"));
+}
+test(200)
+test(2)
+
+matsize(matrix(0, 0) * matrix(0, 2))
+
+default(realprecision,38);
+h=mathilbert(40);
+[Q,R] = matqr(h); vecmax(abs(h-Q*R)) < 1e-37
+[q,R] = matqr(h,1); vecmax(abs(mathouseholder(q,h)-R)) < 1e-37
+mathouseholder(1,1)
+mathouseholder(q,1)
+Mat(List())
+Mat(List([1]))
+Mat([[1,2,3],[2,3,4]]~)
+Mat(Qfb(1,2,5))
+matdiagonal(matid(2))
+iferr(matdiagonal([1,2;3,4]),E,E)
+matpascal(4,1/2)
+A=[1,2,3;4,5,6];B=[4,6;10,12]
+matinverseimage(A,B)
+matinverseimage(A*Mod(1,2),B)
+matinverseimage(A*Mod(1,7),B)
+matinverseimage(A*Mod(1,2^64+13),B)
+matinverseimage(A*Mod(1,3037000507),B)
+B=[4,10]~;
+matinverseimage(A*Mod(1,2),B)
+matinverseimage(A*Mod(1,7),B)
+matinverseimage(A*Mod(1,2^64+13),B)
+matinverseimage(A*Mod(1,3037000507),B)
+
+test(f)=
+{
+  print(f,":");
+  print(f(A*Mod(1,2)));
+  print(f(A*Mod(1,7)));
+  print(f(A*Mod(1,3037000507)));
+  print(f(A*Mod(1,2^64+13)));
+  print(f(A*(1+O(101^3))));
+}
+testall()=
+{
+  test(matdet);
+  test(matrank);
+  test(matadjoint);
+  test(matimage);
+  test(matimagecompl);
+  test(matindexrank);
+  test(matker);
+  test(lindep);
+  test(x->matsolve(x,vectorv(#x,i,i)));
+  test(x->matsolve(x,matrix(#x,#x,i,j,i+j)));
+  test(x->x^(-1));
+  test(x->x^2);
+  test(x->A*x);
+}
+A = [1,2,4;2,12,7;2,9,11];
+testall();
+A = [;]
+testall();
+
+A=[0,1,0;1,0,1;2,0,3];
+matdet(A*Mod(1,3037000507))
+matdet(A*Mod(1,2^64+13))
+matsolve(A*Mod(1,2^64+13),[1,2,3]~)
+
+matker([1.,I;I,-1.])
+
+trace(matid(3))
+trace([;])
+iferr(trace(Mat([1,2])),E,E)
+matrixqz([1/3,1/4;1/2,1/3])
+matrixqz(matrix(2,2),-1)
+
+A=[1,2,3;4,5,6;7,8,9];
+test(lindep)
+test(matsupplement)
+
+default(parisize,10^6); \\ need to exercise gerepile in matker+matimage
+p=2^64+13;
+A=matrix(70,70,i,j, i+j);
+Ap=Mod(A,p);
+#matker(Ap)
+#matimage(Ap)
+#matker(A)
+vecsum([])
+vecsum([2])
+vecsum(primes(4))
diff --git a/src/test/in/mathnf b/src/test/in/mathnf
new file mode 100644
index 0000000..197998e
--- /dev/null
+++ b/src/test/in/mathnf
@@ -0,0 +1,21 @@
+\e
+mathnf([0,2])
+mathnf([0,2], 1)
+mathnf([0,x])
+mathnf([0,x], 1)
+mathnf([x,x^2+1; x^3+x+1, x+2]*Mod(1,5))
+v=[116085838, 181081878, 314252913,10346840];
+[H,U]=mathnf(v, 1); [v*U, norml2(U)]
+[H,U]=mathnf(v, 5); [v*U, norml2(U)]
+mathnf([])
+mathnf([],1)
+mathnf([;])
+mathnf([;],1)
+mathnfmodid(matrix(0,2), [])
+mathnfmodid([0,7;-1,0;-1,-1], [6,2,2])
+
+matsolvemod([;],[]~,[]~,1)
+matsolvemod([;],[],[]~,1)
+matsolvemod([;],[]~,[],1)
+matsolvemod([;],1,1,1)
+matsolvemod([1,2;3,4],1,2,1)
diff --git a/src/test/in/matsnf b/src/test/in/matsnf
new file mode 100644
index 0000000..0c29d00
--- /dev/null
+++ b/src/test/in/matsnf
@@ -0,0 +1,24 @@
+minpoly([-5,0,-1,1;0,-5,-1,-1;1,1,-5,0;-1,1,0,-5])
+minpoly([-5,0,-1,1;0,-5,-1,-1;1,1,-5,0;-1,1,0,-5]/3)
+minpoly([-5,0,-1,1;0,-5,-1,-1;1,1,-5,0;-1,1,0,-5]/a)
+minpoly([-5,0,-1,1;0,-5,-1,-1;1,1,-5,0;-1,1,0,-5]*a)
+matsnf([1,2;3,4],2)
+matsnf([-X-5,-1,-1,0;0,X^2+10*X+26,-1,-X-5;1,-X-5,-X-5,1;-1,0,0,1],2)
+minpoly([a+4/51*d,29/51*d,55/17*d,-9/17*d;-11/51*d,a+35/51*d,-11/17*d,12/17*d;23/51*d,1/51*d,a+6/17*d,-5/17*d;11/17*d,16/17*d,33/17*d,a-19/17*d])
+{
+  for(i=1,15,
+      print(matfrobenius(matrix(i,i,k,j,k>=j),2)));
+  for(i=1,10,
+     print(matfrobenius(matpascal(i),2)));
+  for(i=1,8,
+     print(matfrobenius(mathilbert(i),2)));
+}
+matsnf(matdiagonal([x,0,x]), 2)
+matsnf(matdiagonal([1+x,0,x]), 2)
+\\Bug #1208
+matsnf([0;1;2], 4)
+matsnf([0;1;2], 5)
+matsnf(Mat([0,1,2]), 4)
+matsnf(Mat([0,1,2]), 5)
+
+m=[2*x^6+x^5+2*x^4+2*x^3+x^2+2*x+2,2*x^5+x^3+2*x^2+2,x^3+x^2+x;2*x^6+x^3+x+2,2*x^5+2*x^4+x^3+2*x,x^3+2*x^2+2*x;2*x^6+x^4+x^3+2*x^2+2,2*x^5+2*x^4+2*x^3+x^2+2,x^3+2*x^2+x+2];matsnf(m*Mod(1,3), 2)
diff --git a/src/test/in/member b/src/test/in/member
new file mode 100644
index 0000000..8783781
--- /dev/null
+++ b/src/test/in/member
@@ -0,0 +1,90 @@
+\\package:elldata
+default(realprecision,38);
+{
+members=[
+a1,
+a2,
+a3,
+a4,
+a6,
+b2,
+b4,
+b6,
+b8,
+c4,
+c6,
+area,
+bid,
+bnf,
+clgp,
+codiff,
+cyc,
+diff,
+disc,
+e,
+eta,
+f,
+fu,
+gen,
+group,
+index,
+j,
+mod,
+nf,
+no,
+omega,
+orders,
+p,
+pol,
+polabs,
+r1,
+r2,
+reg,
+roots,
+sign,
+t2,
+tate,
+tu,
+zk,
+zkst
+];}
+\\ tufu, futu omitted
+
+test(s)=
+{
+  for (i=1, #members,
+    my (m = members[i]);
+    iferr( print(".", m, ": ",  eval(Str("s.", m))),
+           E, n = errname(E);
+              if (n != "e_IMPL" && n != "e_TYPE" && n != "e_MISC", error(E))));
+}
+
+test(x)
+test(vector(5))
+test(vector(20))
+test([]~)
+print("NF"); test( NF = nfinit(y^2-1105) )
+print("NF chvar"); test( nfinit(2*y^2+1) )
+print("BNF");test( BNF = bnfinit(NF) )
+print("BNR");test( bnrinit(BNF, 4) )
+print("RNF");test( rnfinit(NF, x^2-y) )
+print("QUADCLASSUNIT"); test( quadclassunit(1105) )
+print("GAL"); test( galoisinit(x^2-2) )
+print("ELL");test( ellinit([1,2,3,4,5]) )
+print("ELLFp");test( ellinit([1,2,3,4,5], 13) )
+print("ELLFq");test( ellinit([1,2,3,4,5], ffgen(13^2)) )
+print("ELLQp");test( ellinit([1,2,3,4,5], O(11^2)) )
+print("FFELT");
+test( ffgen(2^3) )
+test( ffgen(3^3) )
+test( ffgen((2^64+13)^2) )
+print("INTMOD");test( Mod(1,3) )
+print("POLMOD");test( Mod(x,x^2+1) )
+print("QFB");test( Qfb(1,2,3) )
+print("QUAD"); test( quadgen(-4) )
+P=idealprimedec(NF,2)[1];
+print("PRID"); test(P)
+print("MODPR"); test(nfmodprinit(NF,P))
+A=idealpow(NF,P,2);
+print("BID"); test(idealstar(NF,A,2))
+print("BID (nogen)"); test(idealstar(NF,idealpow(NF,A,1)))
diff --git a/src/test/in/minim b/src/test/in/minim
new file mode 100644
index 0000000..3151e81
--- /dev/null
+++ b/src/test/in/minim
@@ -0,0 +1,14 @@
+{d=[4,2,-2,-2,2,1,-2,2,2; 2,4,-2,0,0,0,-1,0,2; -2,-2,4,2,-2,1,1,-1,-2;
+-2,0,2,4,-3,1,0,-2,0; 2,0,-2,-3,4,-1,0,2,0; 1,0,1,1,-1,4,0,-1,1;
+-2,-1,1,0,0,0,4,-2,-2; 2,0,-1,-2,2,-1,-2,4,0; 2,2,-2,0,0,1,-2,0,4];}
+qfperfection(d)
+{d=[4,2,-2,2,2,-2,-1,0;2,4,-2,0,2,-2,1,-1; -2,-2,4,-2,0,2,1,-1;
+2,0,-2,4,1,0,-2,0; 2,2,0,1,4,-1,1,-2;-2,-2,2,0,-1,4,-1,-1;
+-1,1,1,-2,1,-1,4,0;0,-1,-1,0,-2,-1,0,4];}
+qfperfection(d)
+
+d=[2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,2,1,0,1,1,1,1,1,1,1,1;-1,1,2,1,1,1,1,1, 1,1,1,1;-1,0,1,2,1,1,1,1,1,1,1,1;-1,1,1,1,2,1,1,1,1,1,1,1;-1,1,1,1,1,2,1,1,1,1,1 ,1;-1,1,1,1,1,1,2,1,1,1,1,1;-1,1,1,1,1,1,1,2,1,1,1,1;-1,1,1,1,1,1,1,1,2,1,1,1;-1 ,1,1,1,1,1,1,1,1,2,1,1;-1,1,1,1,1,1,1,1,1,1,2,1;-1,1,1,1,1,1,1,1,1,1,1,2];
+qfperfection(d)
+
+d=[84,-42,42,-34,49,42,44,-5,42,-42,42,34;-42,84,-42,-8,-44,-42,-47,-20,-22,0,-17,8;42,-42,84,-34,49,42,47,-22,22,-42,22,20;-34,-8,-34,84,3,-34,-31,0,8,34,-5,-42;49,-44,49,3,98,49,49,-27,49,-27,49,-8;42,-42,42,-34,49,84,42,-8,22,-20,24,-8;44,-47,47,-31,49,42,94,20,2,-22,2,25;-5,-20,-22,0,-27,-8,20,84,-42,42,-5,-8;42,-22,22,8,49,22,2,-42,84,-42,42,9;-42,0,-42,34,-27,-20,-22,42,-42,84,-5,-28;42,-17,22,-5,49,24,2,-5,42,-5,84,1;34,8,20,-42,-8,-8,25,-8,9,-28,1,84];
+qfperfection(d)
diff --git a/src/test/in/minmax b/src/test/in/minmax
new file mode 100644
index 0000000..484026e
--- /dev/null
+++ b/src/test/in/minmax
@@ -0,0 +1,12 @@
+v = [-3,7,-2,11];
+obj = [1, v, Vecsmall(v), [-3,7;-2,11]];
+
+{
+for (i = 1, #obj,
+  my (o = obj[i], u,v);
+  vecmin(o, &u);
+  vecmax(o, &v);
+  print(i, ": ", [vecmax(o), vecmin(o), u, v]);
+)
+}
+
diff --git a/src/test/in/modfun b/src/test/in/modfun
new file mode 100644
index 0000000..9eab702
--- /dev/null
+++ b/src/test/in/modfun
@@ -0,0 +1,8 @@
+default(realprecision,38)
+eta(2+O(2^20))
+eta(x+x^2+x^3+x^4+O(x^5))
+eta(I)
+ellj(2+O(2^20))
+ellj(x+x^2+x^3+x^4+O(x^5))
+theta(1/2,I)
+weber(1.0*I,1)
diff --git a/src/test/in/modpr b/src/test/in/modpr
new file mode 100644
index 0000000..28d0be4
--- /dev/null
+++ b/src/test/in/modpr
@@ -0,0 +1,41 @@
+nfeltmulmodpr(nfinit(x),x,x,1);
+v=[0,1/3,y,z,[1,1/3]~, [1,1,1/3]~];
+test(P) =
+{ my(f);
+  print(K.pol, ": ", P);
+  f=[nfeltdivmodpr,nfeltmulmodpr];
+  for (i=1,#v,
+    for (j=i,#v,
+      print("*",[i,j],":");
+      for (k=1, #f,
+        print(iferr(f[k](K,v[i],v[j],P),E,E))
+      )
+    )
+  );
+  f=[(K,x,P)->nfeltpowmodpr(K,x,-3,P),nfeltreducemodpr];
+  for(i=1,#v,
+    print("*",i,":");
+    for(k=1, #f,  print(iferr(f[k](K,v[i],P),E,E)))
+  )
+}
+K=nfinit(y^2+1);
+P = nfmodprinit(K,idealprimedec(K,2)[1]); test(P);
+P = nfmodprinit(K,idealprimedec(K,3)[1]); test(P);
+K=nfinit(y^3-9);
+P = nfmodprinit(K,idealprimedec(K,3)[1]); test(P);
+P = nfmodprinit(K,idealprimedec(K,2)[2]); test(P);
+K=nfinit(y^2-1105);
+P = nfmodprinit(K,idealprimedec(K,2)[1]);
+nfeltreducemodpr(K,(-y+1)/2,P)
+nffactormod(K, x^3+y*x^2+y*x+1, P)
+m=[1,y;[1/2,1/2]~,1]; v = [1,y]~
+nfkermodpr(K, m, P)
+nfsolvemodpr(K, m, v, P)
+m=[y,y^2;y^2,y^3]
+nfkermodpr(K, m, P)
+nfsolvemodpr(K, m, v, P)
+nfsolvemodpr(K, m, m, P)
+
+K=nfinit(charpoly(Mod(2*x+1,polcyclo(51))));
+P=idealprimedec(K,2)[1];
+nfeltreducemodpr(K,P.gen[2],nfmodprinit(K,P))
diff --git a/src/test/in/modular b/src/test/in/modular
new file mode 100644
index 0000000..caabcbc
--- /dev/null
+++ b/src/test/in/modular
@@ -0,0 +1,29 @@
+v = [2,3,2^32-1,2^64-1,10^20];
+f(a,b,p)=(a*Mod(1,p)) * (b*Mod(1,p));
+g(a,p)=sqr(a*Mod(1,p));
+test(a,b)=
+{
+  for (i=1,#v,print(i, ": ", f(a,b,v[i])));
+  for (i=1,#v,print(i, ": ", g(a,v[i])));
+}
+test(polcyclo(10),polcyclo(5));
+test([1,2;3,4], [-1,2;-4,2]);
+Mod(Mod(1,y),x)
+Mod(Mod(1,x),y)
+iferr(Mod(1,"a"),E,E)
+iferr(Mod(0,0),E,E)
+iferr(Mod(0,Pol(0)),E,E)
+iferr(Mod(x+O(x^2), x^3), E,E)
+Mod(x+O(x^2), x^2)
+Mod(x+O(x^3), x^2)
+Mod(Mod(x,x^3), x^2)
+Mod(Mod(1,12), 9)
+Mod(1/x,x^2+1)
+Mod([5,6],2)
+Mod(3*x,2)
+Mod(x,y)
+Mod(Pol(0),2)
+Pol(0)*Mod(1,2)
+k=100000000000000000000;
+Mod(3,7)^-k
+Mod(3,7)^k
diff --git a/src/test/in/multiif b/src/test/in/multiif
new file mode 100644
index 0000000..48a2dab
--- /dev/null
+++ b/src/test/in/multiif
@@ -0,0 +1,22 @@
+f(x)=
+{
+  if(x==1,print(1));
+  if(x==1,print(1)
+         ,print("default"));
+  if(x==1,print(1)
+    ,x==2,print(2));
+  if(x==1,print(1)
+    ,x==2,print(2)
+         ,print("default"));
+  if(x==1,print(1)
+    ,x==2,print(2)
+    ,x==3,print(3));
+  if(x==1,print(1)
+    ,x==2,print(2)
+    ,x==3,print(3)
+    ,print("default"));
+}
+for(i=1,4,f(i));
+g(x)=if(x,return(1),return(2),return(3));
+g(1)
+g(0)
diff --git a/src/test/in/multivar-mul b/src/test/in/multivar-mul
new file mode 100644
index 0000000..ec7c8a1
--- /dev/null
+++ b/src/test/in/multivar-mul
@@ -0,0 +1,26 @@
+/* Multivariable Laurent polynomial bug finding */
+/* 05 Jul 2007 Michael Somos */
+
+x;y;z;
+
+pol(N, v)=
+{
+  my(t = vector(N+1));
+  t[N+1] = 1;
+  for (i=1,#v,
+    my(n = v[i]);
+    if (n < 1, t[1] = 1
+             , t[(n-1) \ 2 + 1] = if (n%2,1/y,1/z)));
+  Polrev(t);
+}
+
+/* Attempt to multiply two polynomials in x */
+f(N,M,v,w)= iferr(pol(N,v)*pol(M,w); 0, E, 1);
+
+{doit(N,M)=
+  forvec(v=[[0,2*N],[0,2*N],[0,2*N]],
+    forvec(w=[[0,2*M],[0,2*M]],
+      if(f(N,M,v,w), print([N,M],v,w)),2),2);
+}
+
+doit(9,9);
diff --git a/src/test/in/nf b/src/test/in/nf
new file mode 100644
index 0000000..4e9a1b7
--- /dev/null
+++ b/src/test/in/nf
@@ -0,0 +1,82 @@
+\p38
+setrand(1429412696);bnfinit(x^2+29051222508*x-12).clgp
+setrand(1); bnfinit(x^8 + 12*x^6 + 30*x^4 + 24*x^2 + 4).reg
+setrand(1); bnfinit(x^4 - 3*x^2 + 49).reg
+
+nfinit(factor(polzagier(9,5))[2,1],2).disc
+nfinit(Pol([1,0,42,112,728,3248,14224,3392,289478,-804944,2966908,-11015200,17342836,-108601584,381107816,-1679988352,6252186465,-14812800240,28868620970,-27997506768,-33428758132,98285772160,-51592356424,-39975211584,55983352320,-24670808064,5337884160,-733917184,87744512])).disc
+
+setrand(3);bnfinit(x^2-(1130481^2+4)).clgp
+setrand(2);bnfinit(x^4 - x^3 + 63*x^2 - 22*x + 1004).clgp
+setrand(1);bnfinit(x^8 - 8*x^6 + 38*x^4 - 143*x^2 + 121).clgp
+bnfcertify(bnfinit(x^2-40!));
+bnfcertify(bnfinit(x^8-2));
+zetakinit(2*x-1);
+
+nf=nfinit(y^5-4*y^3+2*y+11);
+v = [4/3, -1, y^2+y+1, Mod(y,nf.pol),Mod(1/2,nf.pol),[1,2,3,4,5]~];
+for (i=1, #v, print( nfelttrace(nf,v[i]) ))
+for (i=1, #v, print( nfeltnorm(nf,v[i]) ))
+nfeltnorm(nf,[3,3;y,2])
+for (i=1, #v, print( nfbasistoalg(nf,v[i]) ))
+for (i=1, #v, print( nfalgtobasis(nf,v[i]) ))
+funs = [nfeltadd, nfeltdiv, nfeltdiveuc, nfeltdivrem, nfeltmod, nfeltmul];
+
+try(f) = for (i=1, #v, for (j=1,#v, print( f(nf, v[i],v[j])) ))
+for (i = 1, #funs, try(funs[i]))
+
+nfisincl(nfinit(x-1),y)
+p=x^2+3;nfbasis(p, 0, factor(poldisc(p)))
+
+\\#1253
+k=bnfinit(z^2+19*z+6,1); bnfisunit(k,k.fu[1]^2)
+
+\\#1158
+nf=nfinit(x^2+x+1);
+nfinit(nf)
+
+\\#1180
+setrand(1);quadclassunit(572).no
+
+bnf=bnfinit(x^3-2); S=bnfsunit(bnf,idealfactor(bnf,2*3*5*7)[,1]);
+bnfissunit(bnf,S,x)
+bnfissunit(bnf,S,x+2)
+bnfissunit(bnf,S,x+100)
+bnfisunit(bnf, x+100)
+
+\\#1247
+setrand(1);bnfinit(x^2+1635847).no
+
+\\#1381
+K = bnfinit(x^2+23); L = bnrdisclist(K, 10); s = L[1][2];
+bnfdecodemodule(K, s[1][1])
+
+default(realprecision,19);
+K=bnfinit(x^5-x^4+x^3+100*x+20,1);
+A=idealpow(K,idealprimedec(K,5)[2],150);
+bnfisprincipal(K,A);
+default(realprecision,173);
+K=nfnewprec(K);
+bnfisprincipal(K,A)
+
+l=ideallist(K,4,0);ideallistarch(K,l,[1])
+l=ideallist(K,4,3);L=ideallistarch(K,l,[1])
+bnrdisclist(K,l)
+bnrdisclist(K,L)
+bnrdisclist(K,4,[1])
+nfinit([y^3+2,[1,x]])
+nfinit([y^3+2,[1,x,x^2]])
+nfinit([y^3+2,[1,y^5,y]])
+K=nfinit([x^5-x^4+x^3+100*x+20,[1,x,x^2-x,1/2*x^4-1/2*x^3+1/2*x^2+40,x^3-x^2+x]]);
+K.index
+
+K=nfinit([x^5+9*x^4+851*x^3+18890*x^2+252376*x+529348, 39820]);
+nfcertify(K)
+
+nfnewprec(x)
+nfnewprec(quadgen(5))
+nfnewprec(vector(5))
+nfnewprec(vector(6))
+nfnewprec(vector(8))
+nfnewprec(vector(9))
+nfnewprec(vector(16))
diff --git a/src/test/in/nffactor b/src/test/in/nffactor
new file mode 100644
index 0000000..a23b68f
--- /dev/null
+++ b/src/test/in/nffactor
@@ -0,0 +1,108 @@
+default(parisize,"16M");
+do(T, P) = lift(nffactor(nfinit(T),P)[,1]);
+
+do(polcyclo(13,y),x^72-291*x^70+168*x^69+40380*x^68-48588*x^67-3528919*x^66+6672120*x^65+215657160*x^64-575538144*x^63-9642387423*x^62+34735086786*x^61+318475831783*x^60-1543992152304*x^59-7526047084203*x^58+51709921323996*x^57+110268119466273*x^56-1306863903654948*x^55-197687339387338*x^54+24340617020480994*x^53-37674206381844006*x^52-309388136734870296*x^51+1097175021601270233*x^50+1965430743178095924*x^49-17057741307681944498*x^48+12695705864721864408*x^47+149941210123858078557*x^46-4 [...]
+
+do(polcyclo(5,y),x^48+12*x^46+948*x^44+7200*x^42+152361*x^40+815832*x^38+9475380*x^36+44654004*x^34+299137536*x^32+1335241260*x^30+5029216452*x^28+15282825984*x^26+37737671337*x^24+79579803672*x^22+143658877428*x^20+222699104460*x^18+303698198961*x^16+348787956312*x^14+312863646960*x^12+212893847424*x^10+111407984496*x^8+43762394880*x^6+11836253952*x^4+1904684544*x^2+136048896)
+
+do(polsubcyclo(17,8,y),x^48-104*x^46+4664*x^44-122476*x^42+2137838*x^40-26567700*x^38+245144964*x^36-1725955872*x^34+9441692003*x^32-40611588644*x^30+138356971048*x^28-374714866240*x^26+807289826646*x^24-1380693858220*x^22+1866021172640*x^20-1978766780068*x^18+1630151673857*x^16-1029505301024*x^14+489498952012*x^12-170832297056*x^10+42133382284*x^8-6904507136*x^6+669868016*x^4-28899680*x^2+16)
+
+do(polsubcyclo(19,6,y),x^64-6384*x^62+18261761*x^60-31231019568*x^58+35925400902280*x^56-29635423138225800*x^54+18244443900381139917*x^52-8609789775431197305288*x^50+3173715440318358526295493*x^48-926253189958924421506713024*x^46+216130107574547887816493973792*x^44-40600173780591579547211667354912*x^42+6168621134132051706341715912515370*x^40-760139434277348135867991392951415744*x^38+76052955647065900426700689494084391434*x^36-6175311843554416772593815034596603661344*x^34+4061373624687269 [...]
+
+do(y^16-748*y^14+183362*y^12-18209312*y^10+854163776*y^8-19811731456*y^6+217053667328*y^4-963359277056*y^2+1401249857536,x^64-50520*x^62+1184742668*x^60-17134912892184*x^58+171302841202784230*x^56-1257730595773642457272*x^54+7035168717087126283039868*x^52-30701149599207543908253060456*x^50+106209876041363261331175274376961*x^48-294437498300554938723628131780900528*x^46+658747632370852789155278036118795891376*x^44-1194508299658559259423544698671941624911936*x^42+17588861063453589975414296 [...]
+
+do(y^20+1161*y^16+357096*y^12+15694096*y^8+129931776*y^4+26873856,x^20+1219*x^16+25560*x^14+1352565*x^12+15766260*x^10+468310924*x^8-1266123120*x^6+27117441616*x^4+101145350400*x^2+914700960000)
+
+do(y^3+y^2+2,x^24+69)
+
+do(y^4+7^2,x^28-14*x^24+20321*x^20+166992*x^16+1171296*x^12+1342208*x^8-5005056*x^4+3211264)
+
+setrand(1);do(y^16-24*y^14+196*y^12-696*y^10+1118*y^8-840*y^6+292*y^4-40*y^2+1,x^32-208*x^30+12736*x^28-330032*x^26+4326444*x^24-32221712*x^22+147532904*x^20-437660272*x^18+869634612*x^16-1179150112*x^14+1097619208*x^12-696893680*x^10+295476664*x^8-80251024*x^6+12946192*x^4-1075648*x^2+33614)
+
+setrand(29);do(y^16+6*y^8+1,x^64+112*x^60+4672*x^56-61472*x^52+1722640*x^48-19382720*x^44+137146112*x^40+89961600*x^36+762162272*x^32-7723483904*x^28+17460562944*x^24+100233636352*x^20+74611011840*x^16-74722169856*x^12+20300812288*x^8+1358714880*x^4+21381376)
+
+setrand(6);do(y^16+2,x^64+192*x^62+17568*x^60+1019520*x^58+42131676*x^56+1319651424*x^54+32559096528*x^52+649228312512*x^50+10651553826426*x^48+145639438552224*x^46+1674922821206832*x^44+16307859539653056*x^42+135023677732167696*x^40+953248899971965824*x^38+5745239175305568960*x^36+29556064271185194240*x^34+129595725382952883843*x^32+483002100692576612640*x^30+1523870714370199019760*x^28+4047489983524093705152*x^26+8985812828648862019536*x^24+16525310345394167002752*x^22+2489392714997560 [...]
+
+do(y^32+28*y^24+70*y^16+28*y^8+1, x^16-72*x^12-280*x^8+288*x^4+16)
+
+S=x^5-5*x^3+4*x-1;
+T=y^15-25*y^13+202*y^11-16*y^10-665*y^9+115*y^8+916*y^7-160*y^6-537*y^5+80*y^4+119*y^3-16*y^2-8*y+1;
+lift(nfroots(T,S))
+
+S=polsubcyclo(97,12);
+lift(nfroots(subst(S,x,y),S))
+
+S=x^9+9*x^8+36*x^7+69*x^6+36*x^5-99*x^4-303*x^3-450*x^2-342*x-226;
+T=y^9-15*y^6-87*y^3-125;
+lift(nffactor(T,S))
+
+lift(nffactor(nfinit(y^2+1),(x-y)^3*(x^2+y)^4*(x^3-y*x+y)^5))
+
+lift(factor(Mod(y,y^2+1)*x^2 + y))
+lift(nffactor(nfinit(y^2+1),Mod(y,y^2+1)*x^2 + y))
+
+factor((2*x+1)^2*(x+1)^3)
+
+{P=x^9 - 9*x^7 + 27*x^5 - 30*x^3 + 9*x - 2/7;
+K=
+a^27-63*a^25+1701*a^23-25914*a^21+246960*a^19-700*a^18-1543941*a^17+21168*a^16+6465060*a^15-254016*a^14-18197865*a^13+1560552*a^12+34034175*a^11-5321988*a^10-40965225*a^9+10224144*a^8+29604330*a^7-10606932*a^6-10927980*a^5+5186160*a^4+1256409*a^3-777924*a^2+21952; #nfroots(K,P)}
+
+\\ no factor #930
+do(y^16-24*y^12+80*y^10-172*y^8+192*y^6-112*y^4+32*y^2+4, x^16-24*x^12+80*x^10-172*x^8+192*x^6-112*x^4+32*x^2+4)
+
+\\ Bug #959
+P=x^36+12*x^35-900*x^34-585*x^33+232905*x^32-1232184*x^31-15039873*x^30+110066100*x^29+430062960*x^28-3889034165*x^27-6885371553*x^26+72277665264*x^25+75990228525*x^24-788853282390*x^23-669954405945*x^22+5321235823803*x^21+4528671343041*x^20-22706777648475*x^19-20887787332600*x^18+62214414690960*x^17+62136364754205*x^16-111213087488775*x^15-118854042392850*x^14+131983619890275*x^13+148593402314775*x^12-105099439356375*x^11-122927027339625*x^10+56029221057500*x^9+67224101142000*x^8-195789 [...]
+#nfgaloisconj(nfinit([P,nfbasis(P,, factor(poldisc(P), 175069))]),1)
+
+\\ Bug #1006
+#do(y^24-12*y^23+72*y^22-286*y^21+849*y^20-2022*y^19+4034*y^18-6894*y^17+10182*y^16-13048*y^15+14532*y^14-13974*y^13+11365*y^12-7578*y^11+4038*y^10-1766*y^9+762*y^8-408*y^7+236*y^6-126*y^5+69*y^4-38*y^3+18*y^2-6*y+1,8*x^9+42*x^6+6*x^3-1)
+
+\\ Bug #980
+P=x^6 - x^5 - 76*x^4 + 60*x^3 + 1140*x^2 + 1155*x - 695
+Q=a^36 - 140*a^34 + 8402*a^32 - 288950*a^30 + 6406703*a^28 - 97539585*a^26 + 1059042259*a^24 - 8396309325*a^22 + 49297808195*a^20 - 215811263825*a^18 + 704643601819*a^16 - 1705347924285*a^14 + 3017070472643*a^12 - 3809285900925*a^10 + 3299709349267*a^8 - 1836758642090*a^6 + 585303753211*a^4 - 85409835875*a^2 + 3969153125
+lift(nfroots(Q,P))
+
+\\ Bug #1023
+do(a^2+13,polcyclo(13,13*x^2))
+
+\\ Bug #1070
+do(y^2-5,x^5-x^4-7*x^3+x^2+7*x-3)
+
+\\ Bug #1075
+lift(nffactor(polcyclo(21,y), x^2-y^7))
+
+\\ Bug #1132
+do(y^4-y^2+1, 3*x^3+(-12*y^3+6*y)*x^2-11*y^2*x+(2*y^3-4*y))
+
+\\ Bug #1141
+do(y^2-y+1, (x-(1+y)/3)^2*(x+2*(1+y)/3))
+
+\\ Bug #1142
+lift(nffactor(3*y^2+1, 3*x^2+1))
+lift(nfroots(3*y^2+1, 3*x^2+1))
+
+\\#1207
+lift(nffactor(t^6-6*t^5-3298534883316*t^4+13194139533310*t^3+3626777458830693384585198*t^2-7253554917667983838937052*t-1329227995784915872903826851489644559,x^3-3))
+
+\\#1276
+pol=1225*x^6+1750*x^5-21675*x^4-380*x^3+110180*x^2-129720*x+48771;lift(factorback(factornf(pol,subst(pol,x,y))))
+
+\\#1438
+factor((x^2-1)*(x^2-2)*(x^2-5))
+
+\\#1446
+factor((x^80-1)/(x-1))
+
+\\also exercises Flx_extgcd above half-gcd threshold
+test(T,p)=
+{
+  v=polhensellift(T,lift(factormod(T,p)[,1]),p,10);
+  valuation(T-factorback(v),p);
+}
+\\also exercises FqX_extgcd above half-gcd threshold
+test(polcyclo(503),3)
+test(polcyclo(211),18446744073709551667)
+
+nfroots(a^2+1,x^2)
+
+do(y^2-22,x^2+926246528884912528275985458927067632*y-4344481316563541186659879867597013188)
diff --git a/src/test/in/nfhilbert b/src/test/in/nfhilbert
new file mode 100644
index 0000000..d79b195
--- /dev/null
+++ b/src/test/in/nfhilbert
@@ -0,0 +1,58 @@
+nf=nfinit(y^3-1009);
+P = primes(20);
+
+pr = idealprimedec(nf,1009)[1]; a = y^11*(y+1)*101; b = y^5*(y+3)*19;
+vector(#P, i, nfhilbert(nf, a, P[i]*b, pr))
+
+pr = idealprimedec(nf,19)[1]; a = 19^11*(y+1)*101; b = 19^5*(y+3)*19;
+vector(#P, i, nfhilbert(nf, a, (1+P[i]*y)*b, pr))
+
+/* old regression cases: */
+nf=nfinit(y^2+1); pr=idealprimedec(nf,2)[1];
+nfhilbert(nf, [1,1]~, 3, pr)
+
+nfhilbert(nf, Mod(0,3), 3, pr)
+nfhilbert(nf, 3, 0., pr)
+
+nfhilbert(nf,[1,-2]~,[1,-2]~, pr)
+
+nf=nfinit(y^2+3);
+nfhilbert(nf,[3,0]~,[3,0]~,idealprimedec(nf,2)[1])
+
+
+\\ #1147
+K = nfinit(x^5-23);
+p = idealprimedec(K, 2)[1];
+nfhilbert(K,x,-x^2-5*x,p)
+
+K = nfinit(x^8 + 2*x^7 + 3*x^6 + 3*x^4 + 3*x^2 + 2*x + 3);
+p = idealprimedec(K, 2)[1];
+{
+for (j = 1,10,
+  setrand(j);
+  a = vectorv(8,i,random(7));
+  b = vectorv(8,i,random(7));
+  c = vectorv(8,i,random(7));
+  d = nfeltmul(K,b,c);
+  if (nfhilbert(K, a,b,p) * nfhilbert(K, a,c,p) != nfhilbert(K, a,d,p),
+    error([a,b,c]))
+)
+}
+
+L = [2, 3, 1.0, Mod(1,2), Mod(1,4), Mod(1,8), Mod(1,3), Mod(0,5), 1 + O(2^2), 1 + O(2^3), 1 + O(5)];
+for (i=1,#L, for(j=i+1,#L, print([i,j], ": ", iferr(hilbert(L[i],L[j]), E, E))))
+print("p = 0:");
+for (i=1,#L, print(i, ": ", iferr(hilbert(L[i],L[i], 0), E, E)))
+print("p = 2:");
+for (i=1,#L, print(i, ": ", iferr(hilbert(L[i],L[i], 2), E, E)))
+print("p = 5:");
+for (i=1,#L, print(i, ": ", iferr(hilbert(L[i],L[i], 5), E, E)))
+\\#1251
+hilbert(-1,-1,0)
+\\#1261
+K=nfinit(y^2+5); P=idealprimedec(K,2)[1];
+nfhilbert(K,2*y,2,P)
+
+\\#1569
+K=nfinit(x^3-4*x+2);
+nfhilbert(K,2,-2, idealprimedec(K,2)[1])
diff --git a/src/test/in/nfields b/src/test/in/nfields
new file mode 100644
index 0000000..89ff6f1
--- /dev/null
+++ b/src/test/in/nfields
@@ -0,0 +1,147 @@
+default(realprecision,154); Pi; default(realprecision,38);
+dobnf(T,flag=0,tech=[])= setrand(1); my(K = bnfinit(T,flag,tech)); [K.cyc,K.fu];
+\e
+\\ Initialisations diverses
+p2=Pol([1,3021,-786303,-6826636057,-546603588746,3853890514072057]);
+fa=[11699,6;2392997,2;4987333019653,2];
+setrand(1);N=10^8;a=matrix(3,5,j,k,vectorv(5,l,random\N));
+nfpol=x^5-5*x^3+5*x+25; nf=nfinit(nfpol)
+nfinit(nfpol,2)
+nfinit(nfpol,3)[2]
+nf3=nfinit(x^6+108);
+setrand(1);bnf2=bnfinit(y^3-y-1);nf2=bnf2[7];
+setrand(1);bnf=bnfinit(x^2-x-57,,[0.2,0.2])
+dobnf(x^2-x-100000,1)
+\p19
+setrand(1);sbnf=bnfcompress(bnfinit(x^3-x^2-14*x-1))
+bnfinit(sbnf)
+\p38
+bnr=bnrinit(bnf,[[5,3;0,1],[1,0]],1); bnr.cyc
+bnr2=bnrinit(bnf,[[25,13;0,1],[1,1]],1); bnr2.bid
+rnfinit(nf2,x^5-x-2)
+\\
+bnfcertify(bnf)
+dobnf(x^4+24*x^2+585*x+1791,,[0.1,0.1])
+bnrconductor(bnf,[[25,13;0,1],[1,1]])
+bnrconductorofchar(bnr,[2])
+bnfisprincipal(bnf,[5,1;0,1],0)
+bnfisprincipal(bnf,[5,1;0,1])
+bnfisunit(bnf,Mod(3405*x-27466,x^2-x-57))
+bnfnarrow(bnf)
+bnfsignunit(bnf)
+bnrclassno(bnf,[[5,3;0,1],[1,0]])
+lu=ideallist(bnf,55,3);
+bnrclassnolist(bnf,lu)
+bnrdisc(bnr,Mat(6))
+bnrdisc(bnr)
+bnrdisc(bnr2,,,2)
+bnrdisc(bnr,Mat(6),,1)
+bnrdisc(bnr,,,1)
+bnrdisc(bnr2,,,3)
+bnrdisclist(bnf,lu)
+bnrdisclist(bnf,20)
+bnrisprincipal(bnr,idealprimedec(bnf,7)[1])
+dirzetak(nfinit(x^3-10*x+8), 30)
+factornf(x^3+x^2-2*x-1,t^3+t^2-2*t-1)
+\\
+vp=idealprimedec(nf,3)[1]
+idx=idealhnf(nf,vp)
+idy=idealred(nf,idx,[1,5,6])
+idx2=idealmul(nf,idx,idx)
+idt=idealmul(nf,idx,idx,1)
+idz=idealintersect(nf,idx,idy)
+aid=[idx,idy,idz,1,idx];
+idealadd(nf,idx,idy)
+idealaddtoone(nf,idx,idy)
+idealaddtoone(nf,[idy,idx])
+idealappr(nf,idy)
+idealappr(nf,idealfactor(nf,idy),1)
+idealcoprime(nf,idx,idx)
+idealdiv(nf,idy,idt)
+idealdiv(nf,idx2,idx,1)
+idealfactor(nf,idz)
+idealhnf(nf,vp[2],3)
+ideallist(bnf,20)
+bid=idealstar(nf2,54)
+ideallog(nf2,y,bid)
+idealmin(nf,idx,[1,2,3])
+idealnorm(nf,idt)
+idp=idealpow(nf,idx,7)
+idealpow(nf,idx,7,1)
+idealprimedec(nf,2)
+idealprimedec(nf,3)
+idealprimedec(nf,11)
+idealtwoelt(nf,idy)
+idealtwoelt(nf,idy,10)
+idealstar(nf2,54)
+idealval(nf,idp,vp)
+\\
+ba=nfalgtobasis(nf,x^3+5)
+bb=nfalgtobasis(nf,x^3+x)
+bc=matalgtobasis(nf,[x^2+x;x^2+1])
+matbasistoalg(nf,bc)
+nfbasis(x^3+4*x+5)
+nfbasis(x^3+4*x+5,2)
+nfbasis(x^3+4*x+12,1)
+nfbasistoalg(nf,ba)
+nfbasis(p2,0,fa)
+nfdisc(x^3+4*x+12)
+nfdisc(x^3+4*x+12,1)
+nfdisc(p2,0,fa)
+nfeltdiv(nf,ba,bb)
+nfeltdiveuc(nf,ba,bb)
+nfeltdivrem(nf,ba,bb)
+nfeltmod(nf,ba,bb)
+nfeltmul(nf,ba,bb)
+nfeltpow(nf,bb,5)
+nfeltreduce(nf,ba,idx)
+nfeltval(nf,ba,vp)
+nffactor(nf2,x^3+x)
+aut=nfgaloisconj(nf3)
+nfgaloisapply(nf3,aut[5],Mod(x^5,x^6+108))
+nfhilbert(nf,3,5)
+nfhilbert(nf,3,5,vp)
+nfhnf(nf,[a,aid])
+da=nfdetint(nf,[a,aid])
+nfhnfmod(nf,[a,aid],da)
+nfisideal(bnf[7],[5,1;0,1])
+nfisincl(x^2+1,x^4+1)
+nfisincl(x^2+1,nfinit(x^4+1))
+nfisisom(x^3+x^2-2*x-1,x^3+x^2-2*x-1)
+nfisisom(x^3-2,nfinit(x^3-6*x^2-6*x-30))
+nfroots(nf2,x+2)
+nfrootsof1(nf)
+nfsnf(nf,[a[,1..3], [1,1,1], [idealinv(nf,idx),idealinv(nf,idy),1]])
+nfsubfields(nf)
+polcompositum(x^4-4*x+2,x^3-x-1)
+polcompositum(x^4-4*x+2,x^3-x-1,1)
+polgalois(x^6-3*x^2-1)
+polred(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+polred(x^4-28*x^3-458*x^2+9156*x-25321,3)
+polred(x^4+576,1)
+polred(x^4+576,3)
+polred(p2,0,fa)
+polred(p2,1,fa)
+polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568)
+polredabs(x^5-2*x^4-4*x^3-96*x^2-352*x-568,1)
+polredord(x^3-12*x+45*x-1)
+polsubcyclo(31,5)
+setrand(1);poltschirnhaus(x^5-x-1)
+\\
+p=x^5-5*x+y; aa=rnfpseudobasis(nf2,p)
+rnfbasis(bnf2,aa)
+rnfdisc(nf2,p)
+rnfequation(nf2,p)
+rnfequation(nf2,p,1)
+rnfhnfbasis(bnf2,aa)
+rnfisfree(bnf2,aa)
+rnfsteinitz(nf2,aa)
+\\
+nfz=zetakinit(x^2-2);
+zetak(nfz,-3)
+zetak(nfz,1.5+3*I)
+\\
+setrand(1);quadclassunit(1-10^7,,[1,1])
+setrand(1);quadclassunit(10^9-3,,[0.5,0.5])
+sizebyte(%)
+getheap
diff --git a/src/test/in/nfrootsof1 b/src/test/in/nfrootsof1
new file mode 100644
index 0000000..46efb4b
--- /dev/null
+++ b/src/test/in/nfrootsof1
@@ -0,0 +1,9 @@
+allocatemem(20*10^6);
+do(P)=nfrootsof1(nfinit(P))[1];
+
+do(polcyclo(23))
+do(polresultant(y^3*x^3-y^2*x^2+1, polcyclo(23)))
+do(x^54+9*x^51+18*x^48-81*x^45+387*x^42-729*x^39+1953*x^36-7560*x^33+14229*x^30-12393*x^27-270*x^24+6156*x^21+26136*x^18-77679*x^15+88452*x^12-49572*x^9+10287*x^6+972*x^3+27)
+do(x^2+396735)
+do(x^2+4372152)
+do(x^2+x+99184)
diff --git a/src/test/in/norm b/src/test/in/norm
new file mode 100644
index 0000000..df7237c
--- /dev/null
+++ b/src/test/in/norm
@@ -0,0 +1,23 @@
+norml2(-1/2)
+norml2(quadgen(5))
+norml2(quadgen(-3))
+normlp(-1, 1)
+normlp(-1/2, 1)
+normlp(I, 1)
+
+default(realprecision,38);
+F = [x->normlp(x,1), x->normlp(x,2), x->normlp(x,2.5), normlp];
+{
+  for(i=1, #F,
+    my(f = F[i]);
+    print(f);
+    print(f([1,-2,3]));
+    print(f([1,-2;-3,4]));
+    print(f([[1,2],[3,4],5,6]));
+    print(f((1+I) + I*x^2));
+    print(f(-quadgen(5)));
+    print(f(3+4*I));
+  )
+}
+normlp([95800,217519,414560], 4)
+
diff --git a/src/test/in/number b/src/test/in/number
new file mode 100644
index 0000000..b297cac
--- /dev/null
+++ b/src/test/in/number
@@ -0,0 +1,86 @@
+HEAP=[92, if(precision(1.)==38,2736,2776)];
+default(realprecision,154); Pi; default(realprecision,38);
+\e
+addprimes([nextprime(10^9),nextprime(10^10)])
+bestappr(Pi,10000)
+gcdext(123456789,987654321)
+bigomega(12345678987654321)
+binomial(1.1,5)
+chinese(Mod(7,15),Mod(13,21))
+content([123,456,789,234])
+contfrac(Pi)
+contfrac(Pi,5)
+contfrac((exp(1)-1)/(exp(1)+1),[1,3,5,7,9])
+contfracpnqn([2,6,10,14,18,22,26])
+contfracpnqn([1,1,1,1,1,1,1,1;1,1,1,1,1,1,1,1])
+core(54713282649239)
+core(54713282649239,1)
+coredisc(54713282649239)
+coredisc(54713282649239,1)
+divisors(8!)
+eulerphi(257^2)
+factor(17!+1)
+factor(100!+1,0)
+factor(40!+1,100000)
+factorback(factor(12354545545))
+factor(230873846780665851254064061325864374115500032^6)
+factorcantor(x^11+1,7)
+centerlift(lift(factorff(x^3+x^2+x-1,3,t^3+t^2+t-1)))
+10!
+factorial(10)
+factormod(x^11+1,7)
+factormod(x^11+1,7,1)
+setrand(1);ffinit(2,11)
+setrand(1);ffinit(7,4)
+fibonacci(100)
+gcd(12345678,87654321)
+gcd(x^10-1,x^15-1)
+hilbert(2/3,3/4,5)
+hilbert(Mod(5,7),Mod(6,7))
+isfundamental(12345)
+isprime(12345678901234567)
+ispseudoprime(73!+1)
+issquare(12345678987654321)
+issquarefree(123456789876543219)
+kronecker(5,7)
+kronecker(3,18)
+lcm(15,-21)
+lift(chinese(Mod(7,15),Mod(4,21)))
+modreverse(Mod(x^2+1,x^3-x-1))
+moebius(3*5*7*11*13)
+nextprime(100000000000000000000000)
+numdiv(2^99*3^49)
+omega(100!)
+precprime(100000000000000000000000)
+prime(100)
+primes(100)
+qfbclassno(-12391)
+qfbclassno(1345)
+qfbclassno(-12391,1)
+qfbclassno(1345,1)
+Qfb(2,1,3)*Qfb(2,1,3)
+qfbcompraw(Qfb(5,3,-1,0.),Qfb(7,1,-1,0.))
+qfbhclassno(2000003)
+qfbnucomp(Qfb(2,1,9),Qfb(4,3,5),3)
+form=Qfb(2,1,9);qfbnucomp(form,form,3)
+qfbnupow(form,111)
+qfbpowraw(Qfb(5,3,-1,0.),3)
+qfbprimeform(-44,3)
+qfbred(Qfb(3,10,12),,-1)
+qfbred(Qfb(3,10,-20,1.5))
+qfbred(Qfb(3,10,-20,1.5),2,,18)
+qfbred(Qfb(3,10,-20,1.5),1)
+qfbred(Qfb(3,10,-20,1.5),3,,18)
+quaddisc(-252)
+quadgen(-11)
+quadpoly(-11)
+quadregulator(17)
+quadunit(17)
+sigma(100)
+sigma(100,2)
+sigma(100,-3)
+sqrtint(10!^2+1)
+znorder(Mod(33,2^16+1))
+forprime(p=2,100,print(p," ",lift(znprimroot(p))))
+znstar(3120)
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/objets b/src/test/in/objets
new file mode 100644
index 0000000..abbae8a
--- /dev/null
+++ b/src/test/in/objets
@@ -0,0 +1,62 @@
+HEAP=[63, if(precision(1.)==38,822,846)];
+default(realprecision,38);
+\e
++3
+-5
+5+3
+5-3
+5/3
+5\3
+5\/3
+5%3
+5^3
+binary(65537)
+bittest(10^100,100)
+ceil(-2.5)
+centerlift(Mod(456,555))
+component(1+O(7^4),3)
+conj(1+I)
+conjvec(Mod(x^2+x+1,x^3-x-1))
+truncate(1.7,&e)
+e
+denominator(12345/54321)
+divrem(345,123)
+divrem(x^7-1,x^5+1)
+floor(-1/2)
+floor(-2.5)
+frac(-2.7)
+I^2
+imag(2+3*I)
+lex([1,3],[1,3,5])
+max(2,3)
+min(2,3)
+Mod(-12,7)
+norm(1+I)
+norm(Mod(x+5,x^3+x+1))
+numerator((x+1)/(x-1))
+1/(1+x)+O(x^20)
+numtoperm(7,1035)
+permtonum([4,7,1,6,3,5,2])
+37.
+real(5-7*I)
+shift(1,50)
+shift([3,4,-11,-12],-2)
+shiftmul([3,4,-11,-12],-2)
+sign(-1)
+sign(0)
+sign(0.)
+simplify(((x+I+1)^2-x^2-2*x*(I+1))^2)
+sizedigit([1.3*10^5,2*I*Pi*exp(4*Pi)])
+truncate(-2.7)
+truncate(sin(x^2))
+type(Mod(x,x^2+1))
+valuation(6^10000-1,5)
+\p 57
+Pi
+\p 38
+O(x^12)
+padicno=(5/3)*127+O(127^5)
+padicprec(padicno,127)
+length(divisors(1000))
+Mod(10873,49649)^-1
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/op b/src/test/in/op
new file mode 100644
index 0000000..89f8ddc
--- /dev/null
+++ b/src/test/in/op
@@ -0,0 +1,6 @@
+a = 5; a \= 2
+b = 5; b \/= 2
+c = 5; c >>= 2
+d = 5; d <<= 1
+e = 5; e %= 3
+[a,b,c,d,e]
diff --git a/src/test/in/orthopol b/src/test/in/orthopol
new file mode 100644
index 0000000..2a0d1ae
--- /dev/null
+++ b/src/test/in/orthopol
@@ -0,0 +1,26 @@
+U(n)=polchebyshev(n,2);
+T(n)=polchebyshev(n,1);
+L(n)=pollegendre(n);
+H(n)=polhermite(n);
+print("U");for (n=-50,50, if (U(n+1)+U(n-1)-2*x*U(n), print(n)))
+print("T");for (n=-50,50, if (T(n+1)+T(n-1)-2*x*T(n), print(n)))
+print("L");for (n=-50,50, if ((n+1)*L(n+1)-(2*n+1)*x*L(n)+n*L(n-1), print(n)))
+print("H");for (n=0,100, Hn=H(n); if (H(n+1)-2*x*Hn+Hn', print(n)))
+
+T=polchebyshev(5,1,x);
+subst(T,x,2) == polchebyshev(5,1,2)
+U=polchebyshev(5,2,x);
+subst(U,x,2) == polchebyshev(5,2,2)
+H=polhermite(5);
+subst(H,x,2) == polhermite(5,2)
+L=pollegendre(5);
+subst(L,x,2) == pollegendre(5,2)
+polchebyshev(-1,2,2)
+polchebyshev(-2,2,2)
+polchebyshev(4,2,2)
+
+z=Mod(2,2^64+13); N=1000;
+T=polchebyshev(N,1);subst(T,x,z) == polchebyshev(N,1,z)
+T=polchebyshev(N,2);subst(T,x,z) == polchebyshev(N,2,z)
+T=polhermite(N);subst(T,x,z) == polhermite(N,z)
+T=pollegendre(N);subst(T,x,z) == pollegendre(N,z)
diff --git a/src/test/in/padic b/src/test/in/padic
new file mode 100644
index 0000000..cf2bbc4
--- /dev/null
+++ b/src/test/in/padic
@@ -0,0 +1,40 @@
+padicappr(x,O(2))
+padicappr(x^2+1+O(3), Mod(-1+O(5^10),y^2-2))
+padicappr(x^2+1+O(3), -1+O(5^10))
+factorpadic(y^3+5*y,7,8,1)
+factorpadic(y^2+3^5,3,5)
+
+test(p, e = 1)=
+{ my (N = 7*p^2);
+  for (i=0,10,if (!ispower(i+O(p^e), N), print([i,p,e])));
+  for (i=1,10,if (!ispower((i+O(p^e))^N, N), error([i,p,e])));
+}
+test(2)
+test(2,2)
+test(2,3)
+test(3)
+test(3,3)
+test(11,3)
+test(nextprime(2^64),3)
+polrootspadic(x^3-x+8, 2, 6)
+f = subst(x^3-x+8, x, x + Mod(y,y^2+y+1));
+padicappr(f, Mod(1+O(2^10)-y, y^2+y+1))
+\\#1424
+lift(1/2 + O(2))
+
+s=3+3^2+O(3^5)
+t=2+2^2+O(2^5)
+f=[cosh,sinh,tanh];
+{
+for (i=1,#f,
+  print(f[i](s));
+  print(f[i](O(x)));
+)
+}
+gamma(s)
+p=2^64+13;
+gamma(3 + O(p^2))
+gamma(-3 + O(p^2))
+lngamma(17+O(3^5))
+
+I + O(5^3)
diff --git a/src/test/in/parallel b/src/test/in/parallel
new file mode 100644
index 0000000..25feb21
--- /dev/null
+++ b/src/test/in/parallel
@@ -0,0 +1,34 @@
+V=[2^256 + 1, 2^193 - 1];
+parapply(factor,V)
+my(V=[2^256 + 1, 2^193 - 1]); parvector(#V,i,factor(V[i]))
+fun(V)=pareval(vector(#V,i,()->factor(V[i])));
+fun(V)
+parfirst(fun,V)=parfor(i=1,#V,fun(V[i]),j,if(j,return([i,j])));
+parfirst(isprime, [2^600..2^600+1000])[1]
+parselect(isprime, [2^600..2^600+1000],1)
+parselect(isprime, [2^600..2^600+1000])
+
+/* Disable tests that depends on ellsea
+findp(E,n)=
+{
+  my(check(p) = my(c=ellcard(E,p)); if(isprime(c),c,0));
+  parforprime(p=2^n,,check(p),card,if(card,return([p,card])));
+}
+my(E=ellinit([1,3])); findp(E,80)
+*/
+
+inline(ell,ell2);
+ell(a,B,N)=my(E=ellinit([0,0,0,a,1]*Mod(1,N))); ellpow(E,[0,1]*Mod(1,N),B);
+ecm(N,t,B)=
+  iferr(parvector(t,a,ell(a,B,N)),err,gcd(lift(component(err,2)),N),errname(err)=="e_INV");
+ecm(2^101-1,500,600!)
+
+ell2(a,B,N)=iferr(ell(a,B,N),err,return(gcd(lift(component(err,2)),N)),errname(err)=="e_INV");0;
+ecm2(N,t,B)=my(z);parfirst(a->ell2(a,B,N),[1..t])[2];
+ecm2(2^101-1,500,600!)
+uninline();
+inline(chkell);
+chkell(n)=a->my(E=ellinit([1,0,0,0,ffgen(2^n)^a]),N=ellcard(E)/4);if(isprime(N),N);
+ellp(n)=parfirst(chkell(n),[1..10000]);
+ellp(128)
+uninline();
diff --git a/src/test/in/partition b/src/test/in/partition
new file mode 100644
index 0000000..5c72bd9
--- /dev/null
+++ b/src/test/in/partition
@@ -0,0 +1,39 @@
+test(N) = /* pentagonal numbers recurrence */
+{ my(s,t,p);
+  p = vector(N); p[1] = 1;
+  for (n=1, N-1,
+    s = 0; t = n+1;
+    for (k=1, n,
+      t -= 2*k-1; /* n+1 - k(3k-1)/2 */
+      if (t<=0,break);
+      s -= (-1)^k*p[t];
+      t -= k;     /* n+1 - k(3k+1)/2 */
+      if (t<=0,break);
+      s -= (-1)^k*p[t]
+    );
+    p[n+1] = s;
+    if (s != numbpart(n), error([n, s]))
+  );
+}
+test(10^4);
+
+numbpart(0)
+numbpart(52602)
+numbpart(147007)
+numbpart(10^15+2)
+partitions(0)
+partitions(1)
+partitions(9)
+partitions(9,3)
+partitions(-1)
+partitions(5,[3,4],[1,2])
+forpart(v=-1,)
+forpart(v=5,print(Vec(v)),4,3)
+forpart(v=5,print(Vec(v)),[0,5],[2,4])
+my(i=0); forpart(x=55,i++); i
+forpart(x=9,print(Vec(x)));
+forpart(x=11,print(Vec(x)),,5);
+forpart(x=12,print(Vec(x)),,[2,6]);
+forpart(x=23,print(Vec(x)),[3,6]);
+forpart(x=5, print(Vec(x)),[0,3]);
+forpart(x=15, print(Vec(x)),[0,3],7);
diff --git a/src/test/in/ploth b/src/test/in/ploth
new file mode 100644
index 0000000..2b07d56
--- /dev/null
+++ b/src/test/in/ploth
@@ -0,0 +1,36 @@
+\e
+\p19
+t=plothsizes();
+plotinit(0,t[1]-11,t[2]-11)
+plotscale(0,0,1000,0,1000);
+plotbox(0,500,500)
+plotdraw([0,0,0])
+psdraw([0,0,0])
+plotcolor(0,2);
+plotmove(0,0,900); plotlines(0,900,0)
+plotlines(0,vector(5,k,50*k),vector(5,k,10*k*k))
+plotmove(0,243,583); plotcursor(0)
+plot(x=-5,5,sin(x))
+ploth(x=-5,5,sin(x))
+ploth(t=0,2*Pi,[sin(5*t),sin(7*t)])
+ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],1,100)
+ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],2,100)
+ploth(t=0,2*Pi,[sin(5*t),sin(7*t)],3,100)
+plothraw(vector(501,k,k-1),vector(501,k,(k-1)*(k-1)/500));
+plothraw(vector(501,k,k-1),vector(501,k,(k-1)*(k-1)/500),1);
+plotpoints(0,225,334)
+plotpoints(0,vector(10,k,10*k),vector(10,k,5*k*k))
+psploth(x=-5,5,sin(x));
+psplothraw(vector(501,k,k-1),vector(501,k,(k-1)*(k-1)/500),1);
+plotmove(0,50,50);plotrbox(0,50,50)
+plotrline(0,150,100)
+plotcolor(0,4);
+plotcursor(0)
+plotrmove(0,5,5); plotcursor(0)
+plotrpoint(0,20,20)
+plotmove(0,100,100); plotstring(0,Pi)
+plotmove(0,200,200); plotstring(0,"(0,0)")
+plotdraw([0,10,10])
+psdraw([0,10,10])
+ploth(x=0,2*Pi,if (x <1, [cos(x),sin(x)], 1),1)
+ploth(x=0,1,x,,1)
diff --git a/src/test/in/pol b/src/test/in/pol
new file mode 100644
index 0000000..e02dfd3
--- /dev/null
+++ b/src/test/in/pol
@@ -0,0 +1,22 @@
+o = [Mod(0,3),y,1/y, (y^2+1)/y, [1,2,3], Vecsmall([1,2,0]), Qfb(1,2,4), Qfb(1,2,-4), y+2*y^2+O(y^4)];
+{
+  for (i=1,#o,
+    my (v = o[i]);
+    printsep(" ", Pol(v,y), Pol(v,x), Polrev(v));
+    printsep(" ", Ser(v,y), Ser(v,x), Ser(v,,5));
+  )
+}
+o = [2*x+3*y, 2+x+y+O(x^2), 2+x+y+O(y^2)];
+{
+  for (i=1,#o,
+    my (v = o[i]);
+    printsep(" ",pollead(v), pollead(v,x), pollead(v,y))
+  )
+}
+polgraeffe(x^2+x+1)
+polgraeffe(x^3+x+1)
+polsym(2*x^4+1,4)
+norm(I*x+1)
+trace(I*x+1)
+matcompanion(2*x^2+1)
+Pol("")
diff --git a/src/test/in/polmod b/src/test/in/polmod
new file mode 100644
index 0000000..80df593
--- /dev/null
+++ b/src/test/in/polmod
@@ -0,0 +1,35 @@
+test() = {
+  print(a*b);
+  print(a/b);
+  print(sqr(a));
+  print(a^-1);
+  print(a^3);
+  print(a^0);
+}
+
+T=2*x^2 + x + 1; a = Mod(x+1/2, T); b = Mod(x/3+1, T);
+test();
+
+T=2*x^3 + x + 1; a = Mod(x^2+x+1/2, T); b = Mod(x^2+x/3+1, T);
+test();
+
+modp(p) = {
+  t = T*Mod(1,p);
+  a = Mod((x^2+x+3) * Mod(1,p), t); b = Mod((3*x^2+2*x+5) * Mod(1,p), t);
+}
+modp(17); test();
+modp(18446744073709551629); test();
+(Mod(1/2, 't)*'x + Mod(1, 't)) ^ 2
+Mod(Mod(x,x^2+1),x)
+Mod(y,x)
+Mod(1/y,x)
+Mod(O(y),x)
+Mod(x,y)
+Mod(1/x,y)
+Mod(1+O(y),y)
+Mod(1+O(y),y+1)
+
+modreverse(Mod(0,Pol(1)))
+modreverse(Mod(1,x+1))
+norm(Mod(2^65,3*x+1))
+norm(Mod('a,3*x+1))
diff --git a/src/test/in/polred b/src/test/in/polred
new file mode 100644
index 0000000..4902bed
--- /dev/null
+++ b/src/test/in/polred
@@ -0,0 +1,55 @@
+rnfpolredbest(nfinit(quadpoly(5,y)),x^7-14*x^5+56*x^3-56*x+22)
+\\ rnfpolred(nfinit(quadpoly(1996,y)),quadray(1996,1))
+\\ rnfpolred(nfinit(quadpoly(904,y)),quadray(904,1))
+K=nfinit(quadpoly(29,y)); T=quadray(29,17)
+rnfpolredabs(K,T)
+rnfpolredbest(K,T)
+rnfpolredabs(K,[T,100],16)
+rnfpolredbest(K,[T,100])
+K=nfinit(y^3-y-1); T=x^3-x-1;
+rnfpolredabs(K,T)
+rnfpolredabs(K,T,2)
+[P,a]=rnfpolredbest(K,T,1);
+subst(K.pol,y,a)
+[P,a,b]=rnfpolredbest(K,T,3);
+subst(K.pol,y,a)
+substvec(T,[x,y],[a,b])
+rnfpolred(nfinit(quadpoly(1129,y)),quadray(1129,1));
+
+nfinit(Pol([256,-2560,5120,6400,-60320,6976,116320,72560,-456615,270630,-129362]));
+polredabs(quadpoly(14586217464))
+{
+p=x^32 - 1680*x^30 + 1026480*x^28 - 289096080*x^26 + 39933334980*x^24 -
+2715474610800*x^22 + 88886222283600*x^20 - 1619990314513200*x^18 +
+17928141864081750*x^16 - 125620995771054000*x^14 + 565267786831818000*x^12 -
+1629524362237758000*x^10 + 2978275448322310500*x^8 - 3393290168363970000*x^6 +
+2319247705779270000*x^4 - 866101453967610000*x^2 + 135345425000900625;
+#polredabs(p,4)
+}
+polredabs(x^12+139968*x^10+24814646784*x^8+1854249948463104*x^6+177954917169813848064*x^4-1827912356210202139164672*x^2+68504919608701082757419237376);
+polredabs(x^4+10^21*x^2+1)
+polredabs(x^4+146077*x^2+2629386)
+\\#1146
+polredabs(x^9-4*x^7-3*x^6+9*x^5+8*x^4-6*x^3-9*x^2-4*x-1)
+\\#1228
+polredabs(x^5-13*x^3+3*x^2+5*x-1)
+\\#1229
+polredabs(x^6+21471450*x^2+71643071500)
+polredbest(x^6+21471450*x^2+71643071500)
+polredbest(4*x^4+146077*x^2+2629386)
+
+polredabs(2*x+1)
+polredabs(2*x+1,1)
+polred([x,[1]])
+polred(2*x+1)
+polred(2*x+1,1)
+polred(4*x^2+1,2)
+polredbest(2*x+1)
+polredbest(2*x+1,1)
+polredbest(4*x^2+1,1)
+polredord(2*x+1)
+polredord(4*x^2+1)
+\\ #1519, test T->unscale != 1
+polredbest(x^8+24*x^6+80*x^5+1040*x^4-4288*x^3-12736*x^2-61952*x+311296,1)
+\\ #1511
+polredabs(x^16-4*x^15-334*x^14+264*x^13+32231*x^12+57392*x^11-1031422*x^10-3628868*x^9+7185297*x^8+42417784*x^7+11283472*x^6-137773504*x^5-127243504*x^4+69059728*x^3+56307944*x^2-6264432*x+6436)
diff --git a/src/test/in/polygonal b/src/test/in/polygonal
new file mode 100644
index 0000000..29f92ce
--- /dev/null
+++ b/src/test/in/polygonal
@@ -0,0 +1,5 @@
+P(n,s)=((s-2)*n^2-(s-4)*n)>>1;
+test(s)=for(n=0,10, if (!ispolygonal(P(n,s),s,&N) || N != n, error([n,s])));
+
+for (s=4,10, test(s))
+for (s=2^64, 2^64+10, test(s))
diff --git a/src/test/in/polylog b/src/test/in/polylog
new file mode 100644
index 0000000..34414d6
--- /dev/null
+++ b/src/test/in/polylog
@@ -0,0 +1,11 @@
+default(realprecision,38);
+polylog(3,0.9)
+polylog(2,3.9)
+polylog(3,3.9)
+polylog(2,Mod(x,x^2+1))
+polylog(2,[0.5,0.6])
+polylog(2,x+O(x^5))
+polylog(2,1/2+x+O(x^5))
+dilog(-4)
+polylog(2,1+I,1)
+polylog(1,2,3)
diff --git a/src/test/in/polyser b/src/test/in/polyser
new file mode 100644
index 0000000..1c4ca26
--- /dev/null
+++ b/src/test/in/polyser
@@ -0,0 +1,52 @@
+HEAP=[62, if(precision(1.)==38,7190,7252)];
+default(realprecision,154); Pi; default(realprecision,38);
+\e
+apol=y^3+5*y+1
+deriv((x+y)^5,y)
+((x+y)^5)'
+dz=vector(30,k,1);dd=vector(30,k,k==1);dm=dirdiv(dd,dz)
+direuler(s=1,40,1+s*X+s^2*X)
+dirmul(abs(dm),dz)
+zz=yy;yy=xx;eval(zz)
+factorpadic(apol,7,8)
+factorpadic(apol,7,8,1)
+intformal(sin(x))
+intformal((-x^2-2*a*x+8*a)/(x^4-14*x^3+(2*a+49)*x^2-14*a*x+a^2))
+newtonpoly(x^4+3*x^3+27*x^2+9*x+81,3)
+padicappr(apol,1+O(7^8))
+padicappr(x^3+5*x+1,Mod(x*(1+O(7^8)),x^2+x-1))
+Pol(sin(x))
+Pol([1,2,3,4,5])
+Polrev([1,2,3,4,5])
+polcoeff(sin(x),7)
+polcyclo(105)
+pcy=polcyclo(405)
+pcy * pcy
+poldegree(x^3/(x-1))
+poldisc(x^3+4*x+12)
+poldiscreduced(x^3+4*x+12)
+polinterpolate([0,2,3],[0,4,9],5)
+polisirreducible(x^5+3*x^3+5*x^2+15)
+pollegendre(10)
+zpol=0.3+pollegendre(10)
+polrecip(3*x^7-5*x^3+6*x-9)
+polresultant(x^3-1,x^3+1)
+polresultant(x^3-1.,x^3+1.,,1)
+polroots(x^5-5*x^2-5*x-5)
+polroots(x^4-1000000000000000000000)
+polrootsmod(x^16-1,41)
+polrootspadic(x^4+1,41,6)
+polsturm(zpol)
+polsturm(zpol,0.91,1)
+polsylvestermatrix(a2*x^2+a1*x+a0,b1*x+b0)
+polsym(x^17-1,17)
+poltchebi(10)
+polzagier(6,3)
+serconvol(sin(x),x*cos(x))
+serlaplace(x*exp(x*y)/(exp(x)-1))
+serreverse(tan(x))
+subst(sin(x),x,y)
+subst(sin(x),x,x+x^2)
+taylor(y/(x-y),y)
+variable(name^4-other)
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/pow b/src/test/in/pow
new file mode 100644
index 0000000..bef4286
--- /dev/null
+++ b/src/test/in/pow
@@ -0,0 +1,36 @@
+default(realprecision,38);
+(Mod(1,3)+I)^0
+(Mod(1,9)+I*Mod(1,3))^0
+(1/x)^0
+[;]^0
+Mat(2)^0
+Qfb(2,0,-1)^0
+Qfb(2,0,-1)^1
+Vecsmall([3,2,1])^0
+O(2)^1
+O(2)^-2
+(1/2)^-2
+(-2/3)^-2
+n=2^64;
+1^n
+(-1)^n
+(-1)^(n+1)
+2^n
+(1/2)^n
+Qfb(2,0,-1)^n
+Mod(x,polcyclo(7))^n
+O(x)^(1/2)
+(x^3+O(x^6))^(1/3)
+[2,3]^3.
+[2,3]~^3.
+Mat(2)^3.
+O(x^0)^(1/3)
+0.^(1/2)
+Mod(2,3)^(1/2)
+Mod(2,3)^(1/3)
+Mod(1,4)^(1/2)
+(2+O(7^5))^(1/2)
+(3+O(7^5))^(1/2)
+sqrt(1+O(2))
+sqrt(1+O(2^3))
+sqrt(Mod(2,4))
diff --git a/src/test/in/prec b/src/test/in/prec
new file mode 100644
index 0000000..627862e
--- /dev/null
+++ b/src/test/in/prec
@@ -0,0 +1,24 @@
+precision(I,3)
+precision(1+O(3)+I,3)
+precision(O(2),3)
+precision(1+O(2),3)
+precision(x+1+O(3),3)
+precision((1+O(3))/((1+O(5))*x),3)
+precision(Mod(1+O(3),(1+O(3))*x),3)
+precision([1+O(3),2],3)
+default(realprecision,38);
+t=(precision(1.,77)*x+1);
+precision(t)
+precision(1./t)
+precision(Qfb(1,0,-2));
+
+padicprec(1,2)  == padicprec(0,2)
+padicprec(1/2,2)== padicprec(0,2)
+padicprec(Mod(1,9),3)
+padicprec(O(2^2),3)
+padicprec(O(2^2),2)
+t=1+O(2^3);
+padicprec(t,2)
+padicprec((x+2)*t, 2)
+padicprec((1+2*x+O(x^2))*t, 2)
+padicprec([2,4]*t, 2)
diff --git a/src/test/in/prime b/src/test/in/prime
new file mode 100644
index 0000000..c4505c4
--- /dev/null
+++ b/src/test/in/prime
@@ -0,0 +1,11 @@
+test(N)=
+{
+  default(primelimit, N);
+  for (b=10, 20, print1(prime(2^b), " "));
+  for (b=10, 26, print1(primepi(2^b), " "));
+}
+
+test(10^6);
+test(10^8);
+primepi(2^32)
+precprime(1)
diff --git a/src/test/in/primes b/src/test/in/primes
new file mode 100644
index 0000000..e39aca6
--- /dev/null
+++ b/src/test/in/primes
@@ -0,0 +1,6 @@
+primes(50)
+primes([10,20])
+primes([2^32-100,2^32+100])
+primes([2^64-100,2^64+100])
+#primes([10^7, 10^7+10^6])
+#primes([2^1023+5000, 2^1023+7000])
diff --git a/src/test/in/print b/src/test/in/print
new file mode 100644
index 0000000..ea056fa
--- /dev/null
+++ b/src/test/in/print
@@ -0,0 +1,4 @@
+printsep(":", 1,2,3,4)
+printsep(":")
+printsep1(":", 1,2,3,4)
+printsep1(":")
diff --git a/src/test/in/printf b/src/test/in/printf
new file mode 100644
index 0000000..7e0491e
--- /dev/null
+++ b/src/test/in/printf
@@ -0,0 +1,75 @@
+default(realprecision,38);
+aa=[1.0,2.0]
+bb=2.5
+str="string"
+ii=10^100+1
+
+/* d conversions */
+printf("%%0.12ld for 1 : %0.12ld\n", 1)
+printf("%%ld for medium : %ld\n", 10^40+10^20+1)
+printf("%%ld for big : %ld\n", 7^1000)
+printf("%%ld for very big : %ld\n", 7^10000)
+
+/* x conversions */
+printf("31 in base 16 == %x\n", 31)
+printf("%%X for medium : %X\n", 10^40+10^20+1)
+printf("%%#X for medium : %#X\n", 10^40+10^20+1)
+printf("%%x for 0: %x\n", 0)
+printf("%%#X for 0: %#X\n", 0)
+
+/* s conversion */
+printf("%%10s for \"string\" : %10s\n", str)
+printf("%%.4s for \"string\" : %.4s\n", str)
+printf("%%*.*s for \"string\" : %*.*s\n", 10,2, str)
+
+printf("%%s for 1 : %s\n", 1)
+printf("%%s for aa : %s\n", aa) /* same as %Z */
+printf("%%s for 4/3 : %s\n", 4/3)
+printf("%%s inter %%.2s %%4.2f for aa, bb, aa : %4s inter %.2s %4.2f\n", aa, bb, aa)
+
+Strprintf("%%s inter %%.2s %%4.2f for aa, bb, aa : %4s inter %.2s %4.2f\n", aa, bb, aa)
+
+printf("%%10.5s for 3.5 : %10.5s", 3.5)
+
+
+/* f conversion */
+printf("%%10f for Pi     : %.10f|\n", Pi)
+printf("%%20.10f for 2.7 : %20.10f|\n", 2.7)
+printf("%%+20.10f for 2.7: %+20.10f|\n", 2.7)
+printf("%%-20.10f for 2.7: %-20.10f|\n", 2.7)
+printf("%%-*.*f for 2.7: %-*.*f|\n", 20, 10, 2.7)
+
+/* e conversion */
+printf("%%20.10e for 2.7 : %20.10e|\n", 2.7)
+printf("%%+20.10E for 2.7: %+20.10E|\n", 2.7)
+printf("%%-20.10e for 2.7: %-20.10e|\n", 2.7)
+printf("%%-20.10E for ii : %-20.10E|\n", ii)
+printf("%%e for 1+0*I    : %E\n", 1+0*I)
+
+/* g conversion */
+printf("%%8.3g for 3.14159: %8.3g\n", 3.14159)
+printf("%%8.3G for ii     : %8.3G\n", ii)
+printf("%%8.3g for ii     : %8.3g\n", ii)
+printf("%%-20.10g for 4/3 : %-20.10g|\n", 4/3)
+printf("%%20.13e for 4/3  : %20.13e|\n", 4/3)
+printf("%%e for 4/3       : %e|\n", 4/3)
+
+/* error tests */
+printf("%missing argument to format\n") /* ERROR : %m is not a valid format */
+
+printf("%d missing argument to format\n") /* ERROR : missing argument(s) to format */
+
+printf("%%-20.10g for aa : %-20.10g\n", aa) /* ERROR : aa is not a t_REAL */
+
+\\regression tests
+10^38 + 1.
+printf("%.100f",1.)
+printf("%1.0f",0)
+printf("%.6f",5e-5)
+
+\\ conversions using gtolong()
+printf("%c",97.5)
+printf("%c",97+1/2)
+printf("%c",97 + 0.*I)
+printf("%c",97 +0*quadgen(5))
+printf("%1.2f",Mat([1.,2.]))
diff --git a/src/test/in/program b/src/test/in/program
new file mode 100644
index 0000000..7464e2e
--- /dev/null
+++ b/src/test/in/program
@@ -0,0 +1,47 @@
+HEAP=[50, if(precision(1.)==38,1927,3359)];
+STACK=if(precision(1.)==38, 232, 128);
+STACK == getstack
+default(realprecision,38);
+\e
+alias(ln,log)
+ln(2)
+for(x=1,5,print(x!))
+fordiv(10,x,print(x))
+forprime(p=1,30,print(p))
+forstep(x=0,2.9,Pi/12,print(sin(x)))
+forvec(x=[[1,3],[-2,2]],print1([x[1],x[2]]," "));print(" ");
+if(3<2,print("bof"),print("ok"));
+kill(y);print(x+y);
+f(u)=u+1;
+print(f(5));kill(f);
+f=12
+g(u)=if(u,,return(17));u+2
+g(2)
+g(0)
+n=33;until(n==1,print1(n," ");if(n%2,n=3*n+1,n=n/2));print(1)
+m=5;while(m<20,print1(m," ");m=m+1);print()
+\\
+default(seriesprecision,12)
+print((x-12*y)/(y+13*x));
+print([1,2;3,4])
+print1(x+y);print(x+y);
+print((x-12*y)/(y+13*x));
+print([1,2;3,4])
+print1(x+y);print1(" equals ");print(x+y);
+print1("give a value for s? ");s=input();print(1/(s^2+1))
+printtex((x+y)^3/(x-y)^2)
+for(i=1,100,for(j=1,25,if(i+j==32,break(2)));print(i))
+{
+  u=v=p=q=1;
+  for (k=1,400,
+    w=u+v; u=v; v=w;
+    p *= w; q=lcm(q,w);
+    if (k%50==0,
+      print(k" "log(p)/log(q))
+    )
+  );
+}
+install(addii,GG)
+addii(1,2)
+kill(addii)
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/qf b/src/test/in/qf
new file mode 100644
index 0000000..cabbe63
--- /dev/null
+++ b/src/test/in/qf
@@ -0,0 +1,36 @@
+qfgaussred([0,7,2,3; 7,0,6,8; 2,6,0,9; 3,8,9,0])
+Q=[2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,2,1,1,1,1,1,1,1,1,1,1,1;-1,1,2,1,1,1,1,1,1,1,1,1,1;-1,1,1,2,1,1,1,1,1,1,1,1,1;-1,1,1,1,2,1,1,1,1,1,1,1,1;-1,1,1,1,1,2,1,1,1,1,1,1,1;-1,1,1,1,1,1,2,1,1,1,1,1,1;-1,1,1,1,1,1,1,2,1,1,1,1,1;-1,1,1,1,1,1,1,1,2,1,1,1,1;-1,1,1,1,1,1,1,1,1,2,1,1,1;-1,1,1,1,1,1,1,1,1,1,2,1,1;-1,1,1,1,1,1,1,1,1,1,1,2,1;-1,1,1,1,1,1,1,1,1,1,1,1,2];
+Q[3,7] += 1.25;
+Q[7,3] += 1.25; qfsign(Q)
+
+Q=[8,4,4,4,4,4,4,4,4,4,4,4;4,8,4,4,4,4,4,4,4,4,4,4;4,4,8,0,0,0,3,0,0,0,0,0;4,4,0 ,8,4,4,1,4,4,4,4,4;4,4,0,4,8,4,4,4,4,4,4,4;4,4,0,4,4,8,4,4,4,4,4,4;4,4,3,1,4,4,8 ,4,1,1,1,1;4,4,0,4,4,4,4,8,4,4,4,4;4,4,0,4,4,4,1,4,8,4,4,4;4,4,0,4,4,4,1,4,4,8,4 ,4;4,4,0,4,4,4,1,4,4,4,8,4;4,4,0,4,4,4,1,4,4,4,4,8];
+qfminim(Q,,0,2)[1]
+qfrep(Q,16)
+qfrep(Q,8,1)
+qfminim(Q,,,1)
+qfminim(Q,8,,1)
+qfminim(Q,4,,1)
+qfminim([;],4,,1)
+qfminim([;],4,,2)
+
+L=[360815,2283021;-316840,2566404];
+qfminim(L~*L,10^16)[2]
+qfminim(L~*L,10^16,,2)[2]
+forqfvec(v, [3,2;2,3], 3, print(v))
+forqfvec(v, [3,2;2,3],, print(v))
+forqfvec(v,[;],3,)
+x=[1,2,3]~; y=[-1,0,1]~; qfnorm(x)
+q=[1,2,3;2,2,-1;3,-1,0]; qfnorm(x, q)
+qfbil(x,y)
+qfbil(x,y,q)
+M=[1,2,3;4,5,6;7,8,9]; qfnorm(M)
+qfnorm(M,q)
+
+qfnorm(1)
+qfnorm(1,1)
+qfnorm(x,Mat(1))
+qfnorm(x,Mat(x~))
+qfbil(1,1)
+qfbil([1],[1,2])
+qfbil([1,2],[1,2],q)
+qfbil([1,2],[1,2],Mat([1,2]))
diff --git a/src/test/in/qfb b/src/test/in/qfb
new file mode 100644
index 0000000..1e1ac6d
--- /dev/null
+++ b/src/test/in/qfb
@@ -0,0 +1,20 @@
+vec(x) = [component(x,1), component(x,2), component(x,3)];
+vec( qfbred(Qfb(6,6,-1),1) )
+default(realprecision,38)
+q=Qfb(7, 30, -14)^2;
+qfbpowraw(q,-1)
+q*q^2
+q^0
+q^1
+q^-1
+q^-(2^64+1)
+q=Qfb(2, 1, 3); q2=q*q;
+q3=qfbcompraw(q,q2)
+qfbpowraw(q,3)
+qfbred(q3,1)
+q=Qfb(1009, 60, 99108027750247771)
+qfbnupow(q, 8839368315)
+qfbred(Qfb(22000957029, 25035917443, 7122385192))
+p=2^64+13;
+qfbprimeform(-4,p)
+qfbprimeform(5,p)
diff --git a/src/test/in/qfbsolve b/src/test/in/qfbsolve
new file mode 100644
index 0000000..0072622
--- /dev/null
+++ b/src/test/in/qfbsolve
@@ -0,0 +1,22 @@
+qfbeval(V,v)=
+{
+  my(x,y);
+  V=Vec(V);x=v[1];y=v[2];
+  V[1]*x^2+V[2]*x*y+V[3]*y^2;
+}
+
+bqfb(N=10000)=
+{
+  my(p,d,r,V,q,q2,Q,Q2);
+  for(i=1,N,
+    until(!issquare(d) && (d%4==0 || d%4==1),d=random(4000)-2000);
+    until(p==1 || kronecker(d,p)>=0,
+      p=random(1000)-100;
+      if(p<3,p=1,p=nextprime(p)));
+    q=qfbprimeform(d,p); V=Vec(q); q2=Qfb(V[3],V[2],V[1]);
+    until(Q!=0||Q2!=0,r=nextprime(random(30000));
+      Q=qfbsolve(q,r);Q2=qfbsolve(q2,r));
+    if(Q2==0||qfbeval(q2,Q2)!=r,print("qfbsolve(",q2,",",r,")!=",Q2);break);
+    if(Q==0||qfbeval(q,Q)!=r,print("qfbsolve(",q,",",r,")!=",Q);break));
+}
+bqfb();
diff --git a/src/test/in/qfisom b/src/test/in/qfisom
new file mode 100644
index 0000000..8820169
--- /dev/null
+++ b/src/test/in/qfisom
@@ -0,0 +1,32 @@
+in(M,fl)=if(fl,qfisominit(M,fl),qfisominit(M));
+checkauto(M,f,fl)=
+{
+  my(G=if(f,qfauto(in(M,fl)),if(fl,qfauto(M,fl),qfauto(M))));
+  for(j=1,#G[2],my(Q=G[2][j]);
+      if(M!=Q~*M*Q ,error(v)));
+  print(G[1]);
+}
+checkisom(M,N,f,fl)=
+{
+  my(Q=if(f,qfisom(in(M,fl),N),if(fl,qfisom(M,N,fl),qfisom(M,N))));
+  if(Q==0 || M!=Q~*N*Q ,error(N),print("OK"));
+}
+qfauto(Mat(1))
+M=[4,-2,-1,1,1,-2,-2,-2,-1,-1,-1,-1;-2,4,-1,-2,-2,2,2,0,-1,-1,-1,-1;-1,-1,4,-1,-1,1,1,2,0,0,2,2;1,-2,-1,4,1,-1,-1,0,2,2,-1,-1;1,-2,-1,1,4,-1,-1,-1,1,1,0,0;-2,2,1,-1,-1,4,1,0,-1,-1,-1,-1;-2,2,1,-1,-1,1,4,2,1,1,0,0;-2,0,2,0,-1,0,2,4,2,2,2,2;-1,-1,0,2,1,-1,1,2,4,2,1,0;-1,-1,0,2,1,-1,1,2,2,4,0,1;-1,-1,2,-1,0,-1,0,2,1,0,4,2;-1,-1,2,-1,0,-1,0,2,0,1,2,4];
+checkauto(M,0)
+checkauto(M,0,[1,1])
+checkauto(M,1)
+checkauto(M,1,[1,1])
+N1=[2,0,-1,-1,-1,1,0,0,0,0,-1,0,-1,1,0,0;0,2,1,1,1,-1,0,0,-1,1,-1,0,0,0,1,1;-1,1,4,2,2,-2,0,1,1,0,-1,1,-1,1,-1,-1;-1,1,2,4,2,-2,0,1,-1,0,1,1,1,1,1,1;-1,1,2,2,4,-2,0,1,-1,0,-1,1,-1,1,1,1;1,-1,-2,-2,-2,4,-1,0,1,0,1,0,1,-1,-1,-1;0,0,0,0,0,-1,2,-1,-1,0,-1,0,-1,0,1,0;0,0,1,1,1,0,-1,2,1,0,0,1,0,1,-1,0;0,-1,1,-1,-1,1,-1,1,4,0,0,0,0,0,-3,-2;0,1,0,0,0,0,0,0,0,2,-1,0,0,-1,0,0;-1,-1,-1,1,-1,1,-1,0,0,-1,4,0,3,-1,0,0;0,0,1,1,1,0,0,1,0,0,0,2,0,1,0,0;-1,0,-1,1,-1,1,-1,0,0,0,3,0,4,-1,0,1;1,0,1,1,1,-1,0, [...]
+  N2=[6,-4,4,-2,2,-1,1,1,-1,2,1,-2,3,-1,1,0;-4,8,-6,2,-1,0,0,-1,2,-4,0,3,-3,0,-2,-1;4,-6,8,-4,1,1,2,2,-2,5,3,-4,2,1,2,2;-2,2,-4,4,-1,0,-2,-2,1,-3,-3,2,-1,-1,-1,-1;2,-1,1,-1,2,0,1,0,0,1,1,-1,1,0,0,0;-1,0,1,0,0,2,1,0,0,1,1,-1,-1,1,0,1;1,0,2,-2,1,1,4,2,0,2,3,-2,0,1,0,1;1,-1,2,-2,0,0,2,4,-1,1,1,-2,1,0,1,0;-1,2,-2,1,0,0,0,-1,2,-1,0,1,-1,0,0,0;2,-4,5,-3,1,1,2,1,-1,6,4,-3,0,2,1,2;1,0,3,-3,1,1,3,1,0,4,6,-2,-2,2,0,2;-2,3,-4,2,-1,-1,-2,-2,1,-3,-2,4,-1,-1,-1,-1;3,-3,2,-1,1,-1,0,1,-1,0,-2,-1,4,-1,1, [...]
+checkauto(N1,0)
+checkauto(N1,0,[1,1]);
+checkauto(N1,1)
+checkauto(N1,1,[1,1]);
+checkisom(N1,N2,0)
+checkisom(N1,N2,0,[1,1])
+checkisom(N1,N2,1)
+checkisom(N1,N2,1,[1,1])
+G= [1152,[[-1,0,0,0;0,-1,0,0;0,0,-1,0;0,0,0,-1],[0,0,0,1;0,1,1,0;0,-1,0,0;1,-1,-1,0],[-1,0,0,0;0,0,-1,1;-1,0,1,0;-1,1,1,0]]];
+qfautoexport(G)
+qfautoexport(G,1)
diff --git a/src/test/in/quad b/src/test/in/quad
new file mode 100644
index 0000000..ee70640
--- /dev/null
+++ b/src/test/in/quad
@@ -0,0 +1,11 @@
+default(realprecision,38);
+w=quadgen(5);
+w^0
+w+1.
+w+(I+0.)
+1.*w
+(I+0.)*w
+norm(w)
+norml2(w)
+trace(w)
+trace(1+0*w)
diff --git a/src/test/in/quadclassunit b/src/test/in/quadclassunit
new file mode 100644
index 0000000..e9a47fc
--- /dev/null
+++ b/src/test/in/quadclassunit
@@ -0,0 +1,23 @@
+default(realprecision,38);
+default(echo,1);
+test(D) =
+{
+  for (d = D, D+10^3,
+    if (!isfundamental(d),next);
+    print(d, " ", quadclassunit(d).cyc)
+  );
+}
+setrand(1); test(10^15); test(-10^15)
+
+\\ #1195
+setrand(11);quadclassunit(-8419588).cyc
+setrand(2);quadclassunit(-1459008).cyc
+setrand(7);quadclassunit(-3799812).cyc
+setrand(1); quadclassunit(-13163208).cyc
+
+\\ #1195 with non-fundamental discriminants [oo loop]
+setrand(38);quadclassunit(-29920).cyc
+
+quadclassunit(-13163208,,[0.1]).cyc
+setrand(1); quadclassunit((2^70+25)).cyc
+setrand(1); quadclassunit(8*3*5*7).cyc
diff --git a/src/test/in/quadray b/src/test/in/quadray
new file mode 100644
index 0000000..5040344
--- /dev/null
+++ b/src/test/in/quadray
@@ -0,0 +1,19 @@
+default(realprecision,38);
+\\ all quad imaginary whose class group has exponent 2
+v=[-15,-35,-51,-91,-115,-123,-187,-195,-235,-267,-403,-427,-435,-483,-555,-595,-627,-715,-795,-1155,-1435,-1995,-3003,-3315,-20,-24,-40,-52,-84,-88,-120,-132,-148,-168,-228,-232,-280,-312,-340,-372,-408,-420,-520,-532,-660,-708,-760,-840,-1012,-1092,-1320,-1380,-1428,-1540,-1848,-5460];
+for(i=1, #v, print(v[i]": "quadhilbert(v[i])))
+
+quadhilbert(-4036)
+quadhilbert(-300003)
+quadhilbert(-3628843)
+
+Q(D,f) = lift(quadray(D,f));
+Q(-4,31)
+Q(-11,2)
+Q(-15,3)
+Q(-179,2)
+Q(-2276,2)
+Q(-251,2)
+Q(-35,2)
+Q(-4,31)
+Q(-51,3)
diff --git a/src/test/in/random b/src/test/in/random
new file mode 100644
index 0000000..2d871e1
--- /dev/null
+++ b/src/test/in/random
@@ -0,0 +1,29 @@
+ff(p,f) = ffgen(p^f, 'a);
+doell(a)= {
+  my(e, P);
+  e = ellinit([1,1,3,4,5]*a);
+  P = random(e); [P, ellisoncurve(e,P)];
+}
+doff(p,f)=my(a = ff(p,f)); print(random(a)); doell(a);
+
+setrand(1);
+random(Mod(1,3))
+doff(2,3)
+doff(precprime(2^32), 3)
+doff(nextprime(2^64), 3)
+random(1.)
+random(x^5*Mod(1,7))
+randomprime(2)
+randomprime([0,1])
+randomprime([2.5,2.4])
+randomprime([2.4,2.5])
+randomprime()
+randomprime(10)
+randomprime([2^100, 2^100 + 2^20])
+for (i=1,10, a = random([15,20]); if (a < 15 || a > 20, print(a)))
+s=getrand();
+v=vector(10,i,random());
+setrand(s);
+w=vector(10,i,random());
+if(v != w,error("setrand"));
+random("")
diff --git a/src/test/in/ranges b/src/test/in/ranges
new file mode 100644
index 0000000..7f2233e
--- /dev/null
+++ b/src/test/in/ranges
@@ -0,0 +1,60 @@
+[2^100..2^100+3]
+[x^2+1|x<-[1..40],isprime(x)]
+[x|x<-primes(40)]
+[x|x<-[1..40],isprime(x)]
+[a^2+b^2|a<-[1..10];b<-[1..10],gcd(a,b)==1]
+[a^2+b^2|a<-[1..10],isprime(a);b<-[1..10],a!=b && isprime(b)]
+[[a,b,c]|a<-[1..5];b<-[1..a];c<-[1..b]]
+primes(100)[4..7]
+V=[1..5];
+V[2..3]
+V[1..-2]
+V[-3..-1]
+V[^3]
+V=Vecsmall(V);
+V[2..3]
+V[1..-2]
+V[-3..-1]
+V[^3]
+M=matrix(5,5,i,j,i+5*j-5);
+M[2..3,2..3]
+M[1..2,1]
+M[1,1..4]
+M[1..3,]
+M[,1..3]
+M[1..5,1..-2]
+M[1..-2,1..-2]
+M[-3..-1,-3..-1]
+M[^3,2..3]
+M[^2,1]
+M[1,^4]
+M[^3,]
+M[,^3]
+M[^5,1..-2]
+M[1..-2,^-2]
+M[-3..-1,-3..-1]
+
+f(v,c=0)=my(a,b=[1,2]);[a,b[c]]=v;[a,b];
+f([1,2],1)
+f([3,4],2)
+
+g(v)=
+{
+  my(a,[b,c]=v,d=v,e=0,f=I);
+  [a,b,c,d,e,f];
+}
+h(v)=
+{
+  local(a,[b,c]=v,d=v,e=0,f=I);
+  [a,b,c,d,e,f];
+}
+g([3,4])
+h([3,4])
+
+\\ #1552
+[x|x<-[];y<-[]]
+[x|x<-[1..5];y<-[1..x-1];z<-[1]]
+[x|x<-[1..5];y<-[1..x], y<x; z<-[1]]
+
+\\ was [0,0,0,0,0] due to incorrect incloop
+[0..5]
diff --git a/src/test/in/real b/src/test/in/real
new file mode 100644
index 0000000..10dcf92
--- /dev/null
+++ b/src/test/in/real
@@ -0,0 +1,4 @@
+\\ #1322
+default(realprecision,19)
+1. << 2^60
+1. >> 2^60
diff --git a/src/test/in/resultant b/src/test/in/resultant
new file mode 100644
index 0000000..5680268
--- /dev/null
+++ b/src/test/in/resultant
@@ -0,0 +1,77 @@
+allocatemem(20*10^6);
+do(P,Q)=my(v=variable()); substvec(polresultant(P,Q), v, vector(#v,i,i));
+
+P = Pol([k,c,d,e,f,g]);
+Q = P';
+do(P,Q)
+
+P = Pol([1,b,c,d,e]);
+Q = Pol([1,g,h,k,j]);
+do(P,Q)
+
+P = x^7 + x^3 + b*x^2 + c*x + d;
+Q = x^7 + x^3 + f*x^2 + g*x + h;
+do(P,Q)
+
+P = x^20 + a*x^5 + b;
+Q = x^20 + c*x^5 + d;
+do(P,Q)
+
+P = (x+a)^8;
+Q = (x+z)^8;
+do(P,Q)
+
+P = x^50 + 5*a*x^40 + 4*a*x^30 + a*x^20 + 2*a*x^10 + 3*a;
+Q = x^45 + 2*b*x^35 + b*x^25 + 4*b*x^15 + 5*b*x^5 + 6*b;
+do(P,Q)
+
+P = polcyclo(31); P = subst(P, x, a*x);
+Q = polcyclo(29); Q = subst(Q, x, a*x);
+do(P,Q)
+
+P = 1 + Polrev( vector(101, j, b^(101-j)) );
+Q = 1 + Polrev( vector(101, j, b^(j-1)) );
+do(P,Q)
+
+P = 1 + Polrev( vector(301,i,1) );
+Q = 1 + Polrev( vector(201,i,i-1) );
+do(P,Q)
+
+P = 1 + Polrev( vector(301,j, (j-1)^5) );
+Q = 1 + Polrev( vector(301,j, (j-1)^4) );
+do(P,Q)
+
+n = 110;
+polrandom(d, fudge) = x^d + Polrev(vector(d+1, i, round(2^32*sin(i+fudge))));
+P = polrandom(n,0);
+Q = polrandom(n,1/2);
+do(P,Q)
+
+polresultant(-27673*x^2+322883101*x-1876122109136,x^4+4503599627370517)
+P=-27673*x^2+322883101*x-1876122109136;
+for(n=2,3,for(l=1,3,print(polresultant(P*x^l+1,n*x^4+4503599627370517))))
+
+polresultant(z^1746-1, polcyclo(1973, z))
+
+for(i=1,15,if(rnfequation(polcyclo(17,'y),x+y^i,1)[2]^17!=1,print("error",i)))
+
+\\ #1233
+polresultantext(x^2+1, x^2-1)
+\\ #1234
+polresultant(x^4+x^2+1, x^4-y, x, 0)
+
+p1=x2*(x3-x4);p2=x2*(x3-2*x4);polresultant(p1,p2,x1)
+polresultant(x,x,y)
+
+A = x*y; B = (x+y)^2;
+polresultant(A, B)
+polresultantext(A, B, y)
+
+\\ #1509
+polresultantext(y^3+y,3*y^2+1,y)
+poldisc(x^3 + Mod(2,3)*x^2)
+poldisc(x^3 + Mod(2,3)*x^2+1)
+
+norm(Mod(Pol(Mod(0,2)), 3*x))
+
+polresultantext(x+1, x^2)
diff --git a/src/test/in/rfrac b/src/test/in/rfrac
new file mode 100644
index 0000000..2d81ce0
--- /dev/null
+++ b/src/test/in/rfrac
@@ -0,0 +1,46 @@
+prd(b)=prod(n=1,#b,a[n]^b[n]);
+
+init(N) =
+{ k = 1;
+  v = vector(3^N);
+  w = vector(3^N);
+  forvec(u = vector(N,i,[-1,1]), v[k] = prd(u); w[k] = u; k++);
+}
+{doit(N)=
+  for (i=1,#v, A = v[i];
+    for (j=1,#v, B = v[j]; if ( A+B-A != B, error(A,"+",B))
+    )
+  );
+}
+{doit2(i)=
+  for (i=1,#v, A = v[i];
+    for (j=1,#v, B = v[j]; C = prd(w[i]-w[j]); if(A/B!=C, error(A,"/",B)))); }
+
+a=[a1,a2,a3,a4,a5,a6]; N = 4;
+init(N); doit(N); doit2(N);
+
+{doit3(i)=
+  for (i=1,#v, A = v[i];
+    for (j=1,#v, B = v[j]; D = A*C;
+      for (k=1,#v, C = v[k];  D - C))); }
+a=[x,1+x,y,1+y];
+init(N); doit3(N);
+
+\\ regression tests
+(y/x)/(x^0/y)
+
+x1=(8*b^2 + 8*b + 1)/(4*b + 1);
+x2=32*b^4/(32*b^3 + 40*b^2 + 12*b + 1);
+x1-x2 \\ simplified ?
+
+n=102;(1-x^n)*(1-x^(3*n))/(1+x^(5*n))
+n=497;x^n*(1-x^n)/(1+(x^3)^n)
+
+M = [ 1, 1, 1; 'a, 'b, 'c; 'a^2, 'b^2, 'c^2 ];
+[A, B, C] = M^-1 * [ 'u, 'v, 'w ]~;
+X = A^2 + B^2 + C^2; Y = A*B + A*C + B*C; Z = 'u^2 - 2*Y;
+[X == Z,X - Z]
+
+trace(I/x)
+default(realprecision,38);
+1./x-1/x
diff --git a/src/test/in/rnf b/src/test/in/rnf
new file mode 100644
index 0000000..9e72dab
--- /dev/null
+++ b/src/test/in/rnf
@@ -0,0 +1,130 @@
+nf=nfinit(y^2+1);
+rnfidealmul(rnfinit(nf,x^4-x-1),2,3)
+rnfidealup(rnfinit(nf,x),[;])
+nf=nfinit(quadpoly(1129,y));ord=rnfpseudobasis(nf,quadray(1129,1));rnfsteinitz(nf,ord)
+rnflllgram(nf,x^3+2,rnfpseudobasis(nf,x^3+2))
+rnfpseudobasis(nfinit(quadpoly(17,y)),x^11-11*x^10+31*x^9-26*x^8+36*x^7+7*x^6+15*x^5-27*x^4+26*x^3+20*x^2-33*x+42)
+rnfconductor(bnfinit(y),x^4+x^3-71*x^2+72*x+5184)
+K=bnfinit(quadpoly(1596,y),1); rnfbasis(K,rnfsteinitz(K,rnfpseudobasis(K,quadray(K,1))));
+
+nf = nfinit(y^2-3); P = x^3 - 2*y;
+pr3 = idealprimedec(nf,3)[1];
+pr2 = idealprimedec(nf,2)[1];
+
+rnfdedekind(nf, P, pr2)
+rnfdedekind(nf, P, pr3)
+rnfdedekind(nf, P, pr2, 1)
+rnfdedekind(nf, P, pr3, 1)
+rnfdedekind(nf, P)
+rnfdedekind(nf, P, [pr2,pr3])
+
+P = (y+1)*x^4 + x^2 + x + 2;
+rnfdedekind(nf, P, pr2, 1)
+rnfdedekind(nf, P, pr3, 1)
+rnfdedekind(nf, P)
+rnfdedekind(nf, P, [pr2,pr3])
+
+K = nfinit(x^2-x+2); M = [1, 0, x; 0, x, 0; 0,0,2+x]; N = [1, 1, 1];
+nfsnf(K, [M, N, N])
+rnfisabelian(y,x)
+rnfisabelian(y^2+23,x^3+x^2-1)
+T = polcyclo(7, x+Mod(y, nf.pol));
+rnfisabelian(nf.pol, T)
+rnfisabelian(nf, T)
+
+pol = y^3+y^2-2*y-1;
+bnf = bnfinit(pol);
+T=rnfisnorminit(bnf, x^3-y);
+do(T,u,flag=0)=lift(lift(rnfisnorm(T,u,flag)));
+do(T,y)
+do(T,2,100)
+do(T,2,-2*3*5*7)
+
+T=rnfisnorminit(y^2+23, x^2-y);
+do(T,y)
+do(T,2,100)
+
+\\#1157
+rnfisnorminit(y,x^2-Mod(2+y,y));
+
+\\#1255
+K = nfinit(z^3+z^2-2*z-1); rnf = rnfinit(K, x^2+Mod(-z,z^3+z^2-2*z-1)*x+1);
+a = rnfeltup(rnf,z^2)
+rnfeltdown(rnf, a)
+
+nf=nfinit(y); A = [[1,1/2;0,1],[1,1]];
+nfhnfmod(nf, A, nfdetint(nf,A))
+
+K=bnfinit(y^2-40);
+bnfisnorm(K,2, 0)
+bnfisnorm(K,6, 0)
+
+K=bnfinit(y^3-21);
+bnfisnorm(K,2)
+bnfisnorm(K,6)
+L=rnfinit(K,x^2-y);
+v = [2,1/2,x+y,Mod(1,K.pol),Mod(1/2,K.pol),Mod(y,K.pol),Mod(1,L.polabs),Mod(1/2,L.polabs),Mod(x,L.polabs),Mod(x+y/2,L.pol),y,z,Mod(y+1/2,y^2+1),[1]~,[1,2]~,[1,y]~,[1,I]~, y+I,x^2];
+f=[rnfalgtobasis,rnfbasistoalg,rnfeltabstorel,rnfeltreltoabs,rnfeltup,rnfeltdown,rnfelttrace,rnfeltnorm];
+
+test(L,v) =
+{
+  for (i=1,#v,
+    print(i,":");
+    for (j=1,#f, print(iferr(f[j](L,v[i]), E,E)))
+  );
+  my (K = L.nf);
+  for (i=1,#v,
+    print(i,":");
+    print(iferr(rnfcharpoly(K,x^2-y,v[i]),E,E))
+  );
+}
+test(L,v);
+KQ = nfinit(y+1);
+LQ = rnfinit(KQ, x^2-y);
+vQ = [2,1/2,x+y, Mod(1/2,KQ.pol), y, Mod(Mod(x/2+1,KQ.pol),LQ.pol), Mod(x,LQ.pol), Mod(x,LQ.polabs), Mod(x+y/2,x^2-y), x, [1]~,[1,2]~,[y]~];
+test(LQ, vQ);
+
+nf = nfinit(y);
+rnf = rnfinit(nf,x^2-2);
+rel = Mod(Mod(1,y)+0*y,x^2-2);
+a = rnfeltreltoabs(rnf,rel)
+variable(lift(a))
+
+Labs = nfinit(L.polabs);
+idL = idealhnf(Labs, x^3+x^2+10);
+idK = idealhnf(K, y^2+10*y+5);
+id = rnfidealabstorel(L,Labs.zk*idL)
+rnfidealnormabs(L,id) == idealnorm(Labs, idL);
+m = rnfidealreltoabs(L, id)
+mathnf(matalgtobasis(Labs,m)) == idL
+
+m = rnfidealup(L, idK)
+rnfidealdown(L, m) == idK
+m = rnfidealdown(L, Labs.zk*idL)
+rnfidealup(L, m)
+\\
+V=concat(v, [[;], [], 0, [[;],[]], idealprimedec(K,2)[1], idK, idL, Labs.zk*idL, id]);
+f=[rnfidealhnf,rnfidealreltoabs,rnfidealabstorel,rnfidealdown,rnfidealup,rnfidealnormrel,rnfidealnormabs,rnfidealtwoelt];
+{
+for (i=1,#V,
+  print(i,":");
+  for (j=1, #f,
+    print(iferr(f[j](L,V[i]),E,E))
+  )
+)
+}
+rnfidealmul(L, 0,1)
+rnfidealmul(L, 1,0)
+rnfidealmul(L, x,y)
+rnfidealmul(L, y,x)
+rnfidealmul(L, id,x)
+rnfidealmul(L, x,id)
+rnfdet(L,[[;],[]])
+rnfdet(K,[[;],[]])
+rnfdet(K,id)
+rnfbasis(bnfinit(y^2-1105),x^2-y)
+\\#1508
+K=nfinit(y); L=rnfinit(K,x^3-2); rnfeltdown(L,Mod(Mod(1,K.pol),L.polabs))
+\\#1530
+L=rnfinit(nfinit(y^2-3),x^2+23);
+rnfidealtwoelt(L, [[1;0], [1/104]])
diff --git a/src/test/in/rnfkummer b/src/test/in/rnfkummer
new file mode 100644
index 0000000..f9b6e3e
--- /dev/null
+++ b/src/test/in/rnfkummer
@@ -0,0 +1,45 @@
+default(realprecision,38);
+allocatemem(20*10^6)
+count = 0;
+do(nf,f, H = 0, flag = 0)=
+  setrand(1); print(count++); lift(rnfkummer(bnrinit(bnfinit(nf,1),f,1), H, flag));
+
+do(y^4+12*y^3+15*y^2+15*y-15,1)
+do(y^4-13*y^3+2*y^2+2*y-15,1)
+do(y^4-y^3-2404*y^2+2404*y+1154401, 5, matdiagonal([5,1]))
+do(quadpoly(-8,y), 11, [5,2;0,1])
+do(quadpoly(181433,y),1)
+do(quadpoly(-1752,y), 19, matdiagonal([5,1,1]))
+do(nf=nfinit(y^6-2*y^5+3*y^4+y^2+3*y+1), idealprimedec(nf,2)[1])
+do(y^4-52*y^2+26, 3, Mat(5))
+do(quadpoly(5288,y), 9, [1,0;0,3])
+do(quadpoly(5288,y), 9, [1,0;0,3], 3)
+do(y^4+y^3-9*y^2-9*y+11, 10, Mat(5))
+do(y^4-y^3-159*y^2-441*y+1701, 10, Mat(5))
+do(quadpoly(344,y),11,matdiagonal([5,1]))
+/* \\ used to be very slow (>20h). Now ~ 7mn
+ do(quadpoly(17,y),311,Mat(13))
+ do(nf=nfinit(y^4-y^3+2*y+1), idealprimedec(nf,463)[2]);
+ do(y^2+5393946914743),1,,3);
+*/
+
+
+/* 0 eq. OK at \p200 */
+do(y^6-19*y^5-11*y^4-6*y^3-15*y^2-11*y+15, 4, [2,1,1; 0,1,0;0,0,1])
+
+/* more than 1 eq. OK at \p200 */
+do(y^6+8*y^5-7*y^4+y^3+4*y^2-9*y+10, 8, [2,1,1,1,1;0,1,0,0,0;0,0,1,0,0;0,0,0,1,0;0,0,0,0,1])
+
+/* 1 eq. Wrong result at \p28. OK at \p200 */
+do(y^6-9*y^5-20*y^4-4*y^2-y-17, 17, [2,0,1;0,1,0;0,0,1])
+
+setrand(1); for(i=1,10,rnfkummer(bnrinit(bnfinit(quadpoly(2540,y)),9,1),[3,1;0,1]))
+
+do(quadpoly(689,y), 2, Mat(2))
+
+do(y^8-76*y^6+1425*y^4-5776*y^2+5776, 5, matdiagonal([2,1]))
+
+do(y^4 - 34*y^2 + 1189, 5, matdiagonal([3,1,1]))
+
+do(y, [36,[1]], matdiagonal([3,1]), 3)
+
diff --git a/src/test/in/round b/src/test/in/round
new file mode 100644
index 0000000..d588294
--- /dev/null
+++ b/src/test/in/round
@@ -0,0 +1,13 @@
+default(realprecision,38);
+round((1e-40 + x) / (1e-39 + x))
+
+v=[1,1.6,-4/3,Mod(1,3),quadgen(5),Mod(4/3+x,x^2+1/2),x,4/3+x/3+O(x^2),1/x,[4/3,2],matid(2)/3,""];
+test(f)= for(i=1,#v, print(iferr(f(v[i]),E,E)));
+test(ceil)
+test(floor)
+test(frac)
+test(round)
+round(0,&E);
+test(x->[round(x,&e), if(e==E,-'oo,e)])
+test(truncate)
+test(x->[truncate(x,&e), if(e==E,-'oo,e)])
diff --git a/src/test/in/round4 b/src/test/in/round4
new file mode 100644
index 0000000..1ec20e5
--- /dev/null
+++ b/src/test/in/round4
@@ -0,0 +1,468 @@
+allocatemem(10 * 10^6)
+{ v = [
+[1,0,-363,0,53550,0,-4091823,0,170172414,0,-3663509067,0,33703350345,0,-63300912912,0,32451860736],
+[1,-6,18,-30,42,-174,738,-2514,6885,-14348,21720,-29856,48284,-47064,-27768,139824,-135588,56256,78464,-60528,18864,47296,73728,-109056,80656],
+[1,2,3,-3,122,-1],
+[1,1,-2,-26,39,-1],
+[1,0,3888,-12],
+[1,0,-12,-84,-196,2856,6328,-42336,-64820,352464,298928,-1776096,-262416,5458656,-1875872,-6688416,7866576],
+[1,0,57,0,1197,0,13681,0,136854,0,1048044,0,4603892,0,11460015,0,16001100,0,11131014,0,2739339,0,-368793,0,-7569],
+[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16],
+[1,0,-432,0,68688,0,-4717440,0,112637304,0,409406400,0,2774305728,0,4041156096,0,11224978704],
+[1,0,160,0,11216,0,455360,0,11928052,0,212540000,0,2645190320,0,23223642560,0,143402547926,0,613283590880,0,1764753386480,0,3275906117440,0,3788371498452,0,2940754348320,0,1769278869776,0,73445288000,0,87782430961],
+[1,0,0,0,-42,0,560,0,-1645,0,2352,0,10290,0,10192,0,3969],
+[1,0,0,0,576],
+[1,-43,624,-3816,11173,-15800,9408,-1152,-256],
+[1,-28,141,0,-887,336,1436],
+[1,-171,3222,1079,-25200,-9024,34304],
+[1,-124,1109,-2208,-1504,2048,256],
+[1,-347283,25076856,-419348507,1408022154,-1607716596,540519736],
+[1,-130544,2518160,-15612096,27910496,42272512,-146951936,78195712,1806592],
+[1,-219,2951,-12762,6701,80815,-139748,-128864,354688,42752,-263168],
+[1,-20819,4970290,-185332015,974972214,1853069313,-4527488547,-3894160116,4476129404],
+[1,-155,2836,-14379,14428,13117,-9421],
+[1,-262286,3014265745,-8638841144522,-7230469691722,19997484886500,5356591151857],
+[1,-599,17808,-98580,-253979,537680,1226232,464832,18896],
+[1,-29999376,561121360,-119997504,-4488970656,479990016,8977941760,1919960064,256],
+[1,-25283692,99985153436,-3431807998368,5805487078640,46121794880,-628204481984],
+[1,-271,14191,-320438,3790080,-25112800,92495160,-167147800,50530009,301971239,-450938136,211398894,-16216756,-8116135,1041461],
+[1,-16555440,793241872,-6812040640,18249197408,-12596399360,-9598940928,7536655360,2589634816],
+[1,-3335,1068872,-29372236,170603861,-25778864,-465050888,178972480,-13193008],
+[1,-511,60996,-2085405,31489262,-239889832,934179225,-1604993848,-72959248,4517839616,-6762846464,4114843648,-916779008],
+[1,-295,3558,-5031,-24914,17697,28397],
+[1,-20167,13515214,-589415581,409276760,689575296,-490712576],
+[1,-1423,112198,-2946382,25666342,-2017279,-128707766,38702420,141457873],
+[1,-13921853102206569014202396016,13823607745009668573058211237056109158822,-82639509138390410981775119083025418818311274199568,7191389922579887711216370122117810510341770052031768545,-6403782491712680689180662417515214438745217916122612992,-16491332368693741394107994410424645158974225360655824008,12504550730163409756349966122713154452136883600552704256,-700835788721071887889404301240818143183667934525633904],
+[1,-2265,351933,-10876260,113882619,-320899050,-624685627,2393614800,1152858000,-3523888640,-151501056,1315123200,-232738816],
+[1,-711,98421,-3240421,25070664,-58439271,31703851,28789266,-19416303],
+[1,-506,49087,-1669640,17231279,47336349,-144695555,-359331616,176538278,650890177,224030107],
+[1,-189958896599609331973768440700,687813619771577743504399928302877897734798,-860743693595930793317020644139268141132387039380,2604128524727083170656936316847150343534889684322129,280304933578645057171094798274221018642041201687880,-8574385501054918331765273413936458875072943056877128,-479436876642637253489428928646435311733187408834720,6755064609347154305550891307820436374837227182458896],
+[1,-4690816,910531264,-11915116288,18021615232,54853406720,-82373120000,-64180142080,54969143296],
+[1,-6649860,1576752264,-22267257600,-85164579264,-77640519936,-18089594368],
+[1,-33050180802,1659048077186129481,-272574365342594573834910,11095950373591662695013161508,58710086095790534948543753694,105952057692427053452669684769,70434206621571881660299570170,9218882946199589918384363001],
+[1,-329544091788,469019476748118,-61449507761159844,363555883847040681,-209190672257869480,-1016729503227038920,1359941005734916512,-457890480465748336],
+[1,-1150160,1331537926,-601897862304,133574881579623,-15016280356014512,791282768405768068,-14453225350510767424,3154149235832315999,242466621761228454864,224495153622717846854,-670291322259768317344,-916062740958336644647,48963442301119929008,327298375561074497040,32908217054793357184,-23393721242771438528],
+[1,-2851,158608,-1924400,1243901,34900240,22503032,-108662912,-119975344],
+[1,-11111186112955398247507018336,243445863707559988779970226428228863828166,-414424563629922001757819169028157495238172960,1123139273032476660541621993600473674277033409,498129315610741879775567628417300033144884288,-2159960274391288199445420339203867726147763080,161278633821087460912298650651190604277783808,333627755901815676223362327425185259040194704],
+[1,-14504420704,1182354951470244,-23480009042438862560,27882235470885485836070,-9087586203362364155630944,599651930510874648548788156,-3678137244259701713858840096,7016885534906558207253820945,1533248468172570268668346560,-20993688867208520156247602216,24208677107524343574382863104,-8499807937568973364674358384],
+[1,-2913,1474630,-174340074,5392477240,-26251520300,-34286734387,209569240200,193605730560,-244321588224,-184759603200,14498496512,10333454336],
+[1,-2181,137643,-694573,1252707,-898221,209611],
+[1,-973,167753,-3919581,16265742,49310580,-279670537,-250608765,1769132170,725790142,-5312558309,-1392312472,7380239894,1386140637,-3489374699],
+[1,-1212,22194,-55328,-30951,115500,-2908],
+[1,-4773835280837163132310630467298960,9870109995525906834183518595974886029184804344444758,-1782775404778879896945364961485617187018000556874876449394160,89883389132977359805126346146881786926610135522042323723472323569,72714593243136238897319878334430413252438964969406750065955662080,-321968360190732413575930663497507427814725029860845628527928503688,-312452766852442968760965441219058671845140150026306340120175130880,41087519521030021203246250696563962180882228355460884053944990096],
+[1,-49896695,527277779708,-120568048624615,452535974730544,1625411298707505,-1448067181855113,-2752052546805840,2221923470234796],
+[1,-1046,20228,-68392,-265619,904512,1991640,-2358154,-6146747,-2463528,407228],
+[1,-549625252,1238278325058,-15329043153964,28925414899409,130312025889136,-32291232291184,-108323448091520,-24340205012416],
+[1,-2949,16059,10208,-83400,-25584,78256],
+[1,-3640278,2282599369,-18694808796,-4328145452,125291204160,-18186492672,-205081362432,13073301504],
+[1,-3152,285561,-9564226,152595945,-1188640275,3575524815,4816808889,-47197026465,33049819979,215848146317,-258582345090,-504831954170,649899122769,650345190624,-691225563843,-408430121580,242381088477,57377645757],
+[1,-15395,20249992,-239327620,273434573,856975880,-760838848,-750006400,143449856],
+[1,-2003677328,3012846911824,-25136484060736,32567282104928,90286574332160,-166925807110912,-30270493268992,97725572006144],
+[1,-61640944,166625534272,-3562612049536,15797138088064,236548072448,-60906347638784,18108824379392,56198906318848],
+[1,-21383884,35000497050,-11829222158080,237367247198299,-217929167425584,-553512315641022,292541289325812,110433041508969],
+[1,-23869184,4853367907,-14052765056,-26838500008,56069168640,30654626608],
+[1,-225138,52033917,-248800311,97200810,921552876,-1066931352],
+[1,-88696708256,4437008017160880,-23039907832217560704,26485741273275758734176,42379932318953038254592,-58866917909043924655360,-68089115862472243066880,20476771544555918463232],
+[1,-421636160003888,1902246275640252720,-166497940584948453440,3590656805496104164192,-18533569942452269505792,40219147880321276111616,-39862736608896158137344,14916521497035956080896],
+[1,-3320,224698,-424748,-1157911,905628,951268],
+[1,-64961,187497640,-8356326784,-15832630267,65698325576,143412228032,-26731714048,-127433961472],
+[1,-232394051587274973485503996066296,4038758106150313180814013396186630354012,-18284722485505087322089714887666913243539528,93595182799438815963839033321760695064344070,209067053454541747659819712614587487150583608,-588680842304077969068594235539671561808141348,-538836714977811176095248603569636252559381624,867715345720344138137761716498628626987199041],
+[1,-53655,319168490,-186349285325,21120443415975,-686099144383750,4735314251803625,2204357656605000,-26162347980297500,12824037220300000,33965267757150000,-36652423750000000,9889099639000000],
+[1,-125472786714116,674561716001370442,-230527140127968669608,8169775848285148195,1888371575828384127208,1007346199547631496042,-3767048153290292678684,-3343242731361766639199],
+[1,-34088504,1506866218,-1104149192,-12689882023,4007782176,25448506156],
+[1,-243443804696372,2923285683273533645014,-103724188690208930700257484,219554589005762032602647336897,-27447284989915966720818474455448,820411316616154701751838916778832,2735010684783093171601973097236320,1536067156806457414066577673199968,-1646791109769306193071759068458624,-1348134788310032629244426609918208,-193806232242166413772905190324736,-7677311000369732927493903060736],
+[1,-5331,3275258,-663977892,41259312682,9981725432,-375222796900,-231422557815,575556116832,210741891801,-134691222781],
+[1,-5138,96044,-353472,-420203,3742600,-2116288,-11951190,13094613,11859720,-16211204],
+[1,-532733417840,4744177349160832,-8413021486831026176,22311336993776500736,-7638962491965308928,-7352121709731774464],
+[1,-7637967984048,3386340581184784,-42324961034160576,122919529892620128,-58964720593317120,-192640220764305152,245607942083017728,-76666118683840256],
+[1,-2006,1008774,-17353883,-7844872,51013821,14640013],
+[1,-12474419222154,2431008594310441359,-6912314557744523417164,33605190900312985781583,-52569003547113031969674,25970935931836648827073],
+[1,-161443012754157099829832920,10138436074617165816404765780234617807802044,-20060069047647004218617188450114148708733068200,660050897576330492829543886032989254940435324134,-974098875961622456132597564420652229994235789160,-2264260286588310696319122780977992700907740060420,4240004472781140599100651710696566992990808375400,-1414274241286414395747708743194177178965032607775],
+[1,-1553722352,901513660416,-152989098177664,6553256584676096,-109901782972756992,685652288206471168,-43714124749955072,-9422339586035687424,8817974156500074496,44807770136073207808,-45324909008823779328,-87566115761309089792,86042657488023584768,51374273094909689856,-59775177056420102144,12687790985866903552],
+[1,-165086,69953110,-9018434345,392237516455,-5982463478261,18025737017474,55747520697647,-168806685673984,-244753603799872,393573790098944,502650387525632,-215239123238912,-336114163122176,-57002141155328],
+[1,-93823428592,34799861952304,-443389708735808,779896340053856,1393944632569600,-1484337524757760,-2345449331993600,-694036275654400],
+[1,-61061,2315481,-35028903,275610780,-1232308077,3218060943,-4849095216,3959990439,-1499118593,179027083],
+[1,-5695,92812,-488095,780736,805481,-2779721,525584,1541164],
+[1,-8235528536,78777096091648,-14962965975823424,752649877179796160,-12880180969335749632,38155186289451728896,469400924480987815936,702074460321333391360,-715079303427341254656,-2125387453139301433344,-1140826580450709602304,-3849685126809911296],
+[1,-5406403952479432,6865958511876428164654,-71567316451693131796818392,1752342108645328326901864169,-5653754613528818355093017840,2811073291210554121492866968,7423802461456459480849518400,-6565509149627058490734170224],
+[1,-151343,1368301504,-21253251016,26539419173,96661676120,-105696181312,-91329016192,40152391424],
+[1,-807505248612,37556725718396046,-265013994507552057420,318993321672688235401233,978883284203583405618744,4557622950652832660664,-2130952270988696435076960,-1494408400545790896087024],
+[1,-9448903436,104730633623688,-53760865135709952,4832151934763017856,-84739601628184897024,-42812862492100717568,184460624879942123520,71427552535240871936,-98041653705232138240,-30794067428621123584],
+[1,-79582678925,8620909385125051,-236507758358268974098,174874878867315968757470,-18058520227439698846912507,645254756102974107764676912,-7870697647323874283979696891,11521347153009947384786791458,34576202721355388442595149476,-70344554343965963219721213592,-10740194212304372050166074384,62770663206984074836346600032,-16345615646987859185051367488,569708459683633657748324224],
+[1,-456523770,8737152129501,-982742700549550,27196974696122506,-267044865205988930,534419840754543701,3738689503783264990,-4543932463986286454,-15278585690067252590,8496454288061882845,19353624370456339750,-1008847637476820975],
+[1,-34856,3760018,-13969928,-5409343,51565824,-37781504],
+[1,-301640751830458520553597141376,328952494035105106001631823186510670131969344412,-133358741515025683238080442729603137995456051732608,1955525927172552794216114449002457559485718278748870,9397121011646773996373023708967458825619733367758208,13257860889783058889903253607299903955098315590042012,6433754692163674744311497881625324357727770698216576,492667067892517490964245788797821843444034967148801],
+[1,-512577532102040,43768111570354384,-683727333405049120,2702203304894313824,-3908326472950549120,1831103370543136000,-49811703475904000,-49563059750240000],
+[1,-4442,2971547,-533338420,2818485691,606965289,-20799684871,16060220236,40016291866,-40620996767,-6838744349],
+[1,-1661445000,648950992194,-9056465203448,-52361475864447,-82213976895840,-37808675256052],
+[1,-3549,1055643,2390843,-2763021,-10087077,-6219557],
+[1,-523628898243604618995885416807147298909363801451789372900,367600369666309006452842123621050246321452143630580273014679831593578783458649152654286,-3343275983768744237278154637602147245967274497790433362036635892377724426950499872902658229340,1832082009549845619949867091376004679654159119363411909937509445666660442613413884422767828119857,7972083317253398926405060410933145316118484573008126052905764693835293798003418767254772929076840,11794511604384984730506391635816484705277974441262 [...]
+[1,-223216,303477256,-17417922005,277522832382,-594271046752,-5464816647875,5815492998512,37144434715472,-2497746038784,-79766894614784,-25810480361472,32744553869312],
+[1,-7391,100628,-303943,-80708,1274729,-1168709],
+[1,-11102485161464,638119123854694146704,-1090684976797347138361401856,5790691092346201260104535212544,-2905557524355781158067995198312448,-1740704435184319464552349984251904,8456003228780781810129632899235840,611694445981518923832996667392000,-1827580422204898657895630230781952,296569960368897491493657596919808],
+[1,-382595410,10131435161569707,-339358625351926857460,1944397043581582983327025,-377576456004292682578424730,18893558349780213971828050325,-114503495274862313003240421015,-941511767079645429966258595415,170925446090929041685072722405,7547994422752820490440513334577,4952842395416051576334911693070,-20605333735289498909471577909366,-17080193694570124063317347368550,22769567946872826808692277299420,13826681275125601697987763384700,-10810372895085097433637727654775],
+[1,-317966635855342617399169385408829217452748440,13678545113125269147071264087266524532070024308404763646867605208904755730954,-15684278338411526690829309518564793208753920587856759367478501240440935756809346120,612415923065287098473685281157727791918451722108963506485602685360057577081310228969,537814306804262126760386283977264831761888559875268935565912225023248124597985989680,-1990656139061697921305239415958192303820860550542494738202435034180069987344650996640,-205546917784505934920 [...]
+[1,-416659279474111237,330377843894082003274229160,-62905768010062743987970346108852670,16821570317735641478410538460455385145365,-1198558791812587254354860392504583675770158336,18870041073230571748664972200517215466193250634297,-3110266503818994429391891213184926395344435891196215,46333969996580915198177746519714350753415778619811755,-71051745585560409536489422446312023987208684949679585,-171670385086621478643416705514305417043725434872977218,35399819262974537265183181693870651721210884 [...]
+[1,-2942,425807,-21480253,510509165,-6192552408,36292590688,-53610411500,-425058743459,1751257692002,873280257113,-13398050061528,6817766400536,51340604838743,-41334969659574,-115572213676468,88181505312905,157994239939799,-76512548098590,-116800296271737,13943948986543,27978407467337,1658082397889],
+[1,-235975492672,591805571783776,-382200656154472448,73171728402630615744,-2825821907089361173504,22281146497688453411328,32388399560710497509376,-589295596207800671283712,-475154989792559233744896,5936410136560770774966272,9110311309239759390113792,-15457901130275180849086464,-36773863935231836576546816,-392960681613067998593024,38539327581294094379384832,21102849638169519181594624],
+[1,-22,135,-317,90,941,-1851,1519,-580,83],
+[1,-480,13575,-57397,-15045,155166,-22856,-113661,22119,20089],
+[1,-5349,69225,30959,-693771,-198237,1904389,513114,-1472148,-541304],
+[1,-45,339,-792,-186,2907,-2911,-765,2280,-829],
+[1,-1999,44014,-243753,270704,621536,-1193984,115712,409600,32768],
+[1,-2154,83832,-98620,-727088,628528,1715120,-1208288,-1009216,688192],
+[1,-84,180,796,-1713,-2337,5216,1461,-5232,1819],
+[1,-28714,389720,-442556,-3374000,506160,5449392,774688,-540736,3136],
+[1,-131061,64254945,-433211377,-295697859,2231628243,196751629,-2475058518,468570444,325387336],
+[1,-43228917,3217754381,-36758007254,-59567284560,289702061749,332943277127,-581210582241,-584264535430,52410165263],
+[1,-31323,7353216,-107319715,116275089,383163324,-599105591,-198697686,666629724,-267848648],
+[1,-170,447,951,-2186,-2265,2785,2117,-798,-433],
+[1,-97,920,-2507,191,7795,-7979,-2434,5592,-1609],
+[1,-25601,4735751,-157811795,73527676,713816792,-217322176,-919714240,50378496,268434944],
+[1,-404,4704,3918,-92546,-153360,222561,511962,102988,-113801],
+[1,-431,5515,-29560,84442,-134947,110307,-22471,-27036,14251],
+[1,-129254964843,35178418165170,-223350030592755,251360581683972,960227812394016,-2217902130474495,-80657197605174,3263837746063692,-2051179019383688],
+[1,-10430037,3613650129,-255462258585,649253166885,405120779307,-2141143390203,901824939450,1197088044012,-731164579192],
+[1,-50244,30294557,-672636955,-5671628428,-15674959992,-18431496640,-7309777088,1356664064,746926592],
+[1,-339,4344,10623,-36633,-85575,53843,193416,92484,2087],
+[1,-31428,5168421,-69645814,-94901634,121411929,57346600,-76184430,18720885,-1242287],
+[1,-1322,9117,-14806,-25436,87546,-32311,-75542,33116,26104],
+[1,-189471444,37709676681,-165918005985,34691048736,558925368933,-498161898711,-154612190805,146945330826,-21509240369],
+[1,-2147,464267,-6245532,12094236,28528805,-87534615,6524347,127671918,-83816513],
+[1,-2783342,8025984,23658900,-59547440,-78387408,138067248,117240288,-97770944,-60103616],
+[1,-1255489,64926593,-338839895,426458329,692772093,-1921085372,734227214,1055601045,-717464063],
+[1,-36319094,630812278698,-1655815071045331,-6028129014962075,246012746225435,22671225935869189,25087340269832470,2776221409366657,-4230997679557999],
+[1,-873711251373,753411928367685,-13806936287537310,-111661195235268408,-270166294338135339,-191717097502403145,179751315005491911,323631491466547362,124064847445413967],
+[1,-14849018,494063251800,-710653227516,-3713349421616,4822039295664,4687257735600,-9757306885344,4980576448960,-802641509312],
+[1,-33175596,143698989,797637285,-324064356,-3785215656,-2493975360,2377896000,1441632000,-683840000],
+[1,-1929,14472,20253,-156633,-129093,449669,289806,-258072,24407],
+[1,-614748,2204740,20504164,7285904,-146034016,-334984464,-302768000,-123461056,-18857792],
+[1,-3029,22091,-40644,-47982,203135,-80355,-209549,146292,22027],
+[1,-3981,1369677,-17835518,-21602640,14896077,19095023,-1882569,-3453006,-58337],
+[1,-56691,2221227,-15141714,16322598,55966149,-107613132,13226364,54559296,-19694537],
+[1,-14521960,647012999,-4984218590,-36502752545,-43863546837,79899788485,212682283274,154229646540,36019185544],
+[1,-158662082,2623228504,-11293514652,2847667984,61874154160,-78720960080,-54521566560,105714608064,-17136358336],
+[1,-196587,3209851035,-169981929492,1004435496252,-2429211599883,2941338597321,-1853910108045,570724116606,-66727824497],
+[1,-911,5074,-2190,-29204,37810,39031,-64347,-13824,25019],
+[1,-7845,71310,-122285,-330972,945684,133568,-1565232,369216,616384],
+[1,-145515609,153019764729,-3709746066522,1393445404674,19486365429555,-8829957473691,-23095161469062,11557736900892,833570519624],
+[1,-1279836,7972421733,-33976650387,-87081745548,139649755464,254306919488,-38030593728,-154168849152,-46733991424],
+[1,-1983,670992,-4138497,3171843,9255633,-5546089,-7199220,876864,930959],
+[1,-22338,412581,-1979142,682380,7847946,-7588855,-3039222,2196060,626552],
+[1,-22113768108,3796190022029887441,-60498021996410510930523,-71963443045623180847600,487699550239607072040156,523305125593515644446032,-1131228403974427035526864,-946796045857905092061056,584092504774559261558848],
+[1,-16954592,118348117057,-461615145371,-96594517766,2084887420818,-1349388002526,-2335232607204,2081484755957,104568589897],
+[1,-10489,2707211,-104488860,245611206,380234947,-1177729795,106009611,667160224,62376107],
+[1,-45837,369335901,-31624086134,-169410056472,-164382128003,465586425823,980522077951,392287249570,-132604621897],
+[1,-463038,45146493216,-1001353353260,-1082922317616,5505441329328,3829125921584,-6282318595872,-885395763648,497609171008],
+[1,-841,13536,9553,-157013,-163985,275717,183350,-197524,13031],
+[1,-15461,715359,-5328764,15402786,-16694517,-5169563,23976863,-13678964,443227],
+[1,-862775,10856170258,-3134053418062,-1375422646243,19989421509103,3876108599660,-31488084661671,5161577814899,4115642633441],
+[1,-50213409,41604993225,-1178945921599,1767674719401,4428816426693,-5499446500460,-5981060846562,3930075321357,2711088130897],
+[1,-14509669203,854596766777094810,-7066981747963431235083291,46268306243972593019173548,-119361738755744810792850960,153452578934040181394048337,-101367063741886754383215702,31015690796531163812304780,-2923893082958254149947912],
+[1,-734,33756,-127241,-72904,584650,-73590,-605936,-25506,23491],
+[1,-73853860518,13777317132366702,-115071949708121348242,160844716195874633181,682814078330976048036,-833639776266212564111,-943177567400978615382,831764080222499743356,66392226304335963256],
+[1,-694758725855,106254756427419398,-3784025583829693170297,-13131948767566391974232,-12527679077150368308464,-1542996140822612863744,1578487928494511479040,-80007592280225568768,597456985245478912],
+[1,-17515,10964993,-247423288,310805648,1085556229,-1447110681,-631704583,509927518,170424799],
+[1,-8336,239429,-1115012,1550830,675992,-3307903,2496650,-569892,13592],
+[1,-5629442086276204273505,386546651233719446359200330993497888059691,-72838371990626194759378155526759022817640816485447,-68944817078273525878833279122529093499192548461768,532698119339953491504511496245901043962109891591692,399760666506683453127871057655514820189115908767536,-1251030146293132816263700188101197788442427262158608,-574105270447410879019496275688738988931548783376384,933502079102512278754584352311502694379008136095296],
+[1,-89444164815,3805216350344442726,-19525076579766367621887201,10677494665576920234465120,172915220543339268243570816,-75371913447675512226165760,-481724974617695286501519360,133078578075456026880442368,404416685885850617500663808],
+[1,-5017340872,54242041495381,-5530836376965691,-11636668895078592,11075773964676508,38232904505954064,23198971246434608,2517662503984512,53606420009024],
+[1,-39793,797843,-4481430,9023194,-3106271,-9102293,6350523,2351580,-1467437],
+[1,-34677,757365,-2384830,-8575564,7310693,25880563,2552523,-19252762,-8564513],
+[1,-958954166,5186058440675,-5517238669966482,15054662708038354,6305516444274500,-46821935177109565,37215625652899170,-4390922315873148,-2421624954194536],
+[1,-6299100,13352120109,-51758626611,-417163766490,-393905254122,823253994102,1291197091500,235580470281,-131269473631],
+[1,-68277338,5340670808,-96736086908,-116282155376,520304803568,350815905584,-710962872544,-144532807488,203482129472],
+[1,-1001,222000,-9506853,78349585,-284295243,538163385,-539633442,256936366,-38830541],
+[1,-38919,34177467,-59964732,-1118422710,-2838483267,-2244178117,692405229,1753197504,630872507],
+[1,-17126633628,18516619842741,-5017128507606003,8455548998534724,25861081463217288,-49455246822756288,-20953146111899328,72565428488438016,-31456789981646336],
+[1,-1263537,9873936,-28868559,36398475,-12468609,-10015971,4721094,1594332,-10529],
+[1,-19146870,1769475136,5281209652,-10032427760,-40835782480,-6739762640,71055695712,63912971840,15410458688],
+[1,-7447032,4924412571,-359721985189,117617755923,2143792138494,-1486073631188,-2975007994749,3230985686523,-741473702423],
+[1,-517106,58273606,-1373815122,-4952974364,4913608780,27153942409,6950089002,-34145062392,-20469346393],
+[1,-3757,1530645,-28029222,2359216,144391005,61784671,-203472129,-207072198,-54303129],
+[1,-173780,45443268,-1790226588,-8863383152,-4818339936,30375498864,42501629952,-3312569792,-19727103296],
+[1,-2400825,190198232783,-22943866813291,24838048578516,112066877369032,-119639920213824,-123200993753280,100605510714624,35506136494592],
+[1,-25753,6751411,-44524192,68742386,111823375,-385704903,257283371,63338444,-67180373],
+[1,-284655,209650037,-38162364134,-163511305238,-14981588021,711289445675,760350260231,-313829615616,-502101723341],
+[1,-68325922,137056373910552,-11145282232549212,21173555959782288,42902777425768240,-99554991522541136,10187009777496736,51567358997901760,-19350004011936704],
+[1,-11733462,65284119,-55303584,-232858944,336527964,205314537,-268417494,-148242276,-18541736],
+[1,-258893,79530727,-4243301484,-11876635766,16008454795,57961696909,6993684303,-48619490844,-16019695837],
+[1,-18001,4498247,-188001200,-52454414,551685395,-309521095,-64332897,57315780,-7370757],
+[1,-1435968130,714533588440,-8231837770332,40163539982096,-106986363839824,167944712628400,-155368804314976,78443060916160,-16677501283264],
+[1,-405158247,18670761978019422,27304726717430303,-407622516493630984,-1469519465184311920,-1133627454427613440,1794923858940870912,3248244532977154048,1358504654641917952],
+[1,-404792,2745549,-2331396,-17521082,28819704,34082865,-69811830,-19635428,46786072],
+[1,-68548140398,1451627133858028890,-5095232432365659132155182,3088999911253813409361703,38873934921503404756228124,-21888154468509856869401849,-79286358464811742369152944,38737113817267168448327653,19271373881512324513129886],
+[1,-233746,2998825,-13950594,28124252,-19043266,-12805079,21860330,-5429540,-1331272],
+[1,-10355,21543,160412,-124146,-768627,10747,1106549,390000,-98893],
+[1,-238089,2428653591,-52281136592,55494790434,272982489999,-244755630607,-464342035173,229499404284,232532331139],
+[1,-9804802050650,16692730784125432,-2541495071565357628,21109219806111169040,-72218607566697081616,126705850231213211952,-117594235131211600096,52634612644066947776,-8014881018162450368],
+[1,-1546315729274,288782232725785318,-11969666848599934914420,-27130970349234930466201,47347882539110061930082,124498595190336220679942,-1304964757812833452501,-64114240217284796166421,11258025941338821246113],
+[1,-84967786647367,333964189732653895352180494,-9366745551868952432186167003262289,-22234121711987296225427381730172744,19539169743580452197619049789466096,51305696359169176802147610083269888,-17680157436544518186836737589196544,-26000314945482248211133277039052800,4518541019882267511313091077443584],
+[1,-7748673829668,83640796005524661,-937392562807456731,-4611558327627410100,288178943379431256,19088931030202667712,14002224746809121856,-11962021354522502400,-8709955859254358528],
+[1,-843304,33763525,-106394721,-151759084,340324475,-121861811,-28549267,13500892,493667],
+[1,-576711,9817828593,-1467968536170,1986116686590,2315529499551,-1792787441257,-759829031733,199468065048,18428192267],
+[1,-67782267991926,3118994381718067776,-4510225427657444408844,-9065205219388262002800,14548147545483838825008,26311576762212059385392,-13479025960226694184608,-13821453446321155975104,1249714021685870356544],
+[1,-116597,11204304,-75508472,143658084,90374012,-590670119,595390427,-100047640,-72256661],
+[1,-3749259036,554393241540,-10928530910172,-39909926964720,20361183490848,153596163158000,62626956568704,-84859519298496,-23307365582656],
+[1,-2485415052,131251617492,-500838205596,-311199622896,1007849076384,593369475824,-435068972160,-386665742016,-74265379648],
+[1,-235161,314874197,-3066156650,1109853192,24058441897,-22580421725,-49502851657,52464975134,9173335783],
+[1,-1315854,125620602,-575073054,-177194175,3317366889,-1778517616,-5030239005,3601178400,766593875],
+[1,-264278631900,6391720172245021,-1626519277074267211,2468549508074110996,7490117873595926920,-9983338376585959872,-7051946118841625792,5426902073117889792,1165973386759172608],
+[1,-79480668163070691548277,579797667303611907678285,871006426643549798058665,-5664575884052113261186899,-2721765628562603760591507,18297656442680378045473468,795444294656219988472938,-19650372769147029403078227,4906440383881879319836057],
+[1,-17361556040,19571584838731,-1912255610523653,-6599847690074333,-1764459194463858,10908227354203500,5823874326739107,-3161768996970613,-458530154815991],
+[1,-942677402975,306729542653406,-2223101555541649,-3021993537312,7932499451339520,-2016256763813888,-5852785072668672,743954651807744,853725799251968],
+[1,-388145,847961035,-14279672808,5155032058,75179631715,-45679619663,-98354672793,40080406380,43812242819],
+[1,-449715623374,5733757254592,-14174764915308,-36178039104112,88431316773488,56172051631024,-118680037031968,-12225526217408,30136055145536],
+[1,-6233425,50543493135,-277319344376,37556421978,1194329803539,-616149265735,-956720534777,-149559849324,-308254333],
+[1,-6720205764,466584698229,-850591371171,-5544923326524,-2245384789368,3411810485312,216995495232,-119747460864,6720270848],
+[1,-68026878750,236661304531200,-24579892325699628,77376432563312400,2783150175184752,-213772569953649232,171920098000293600,41445518831184192,-55371652962821056],
+[1,-618153,190453312,-13900878563,20306957207,79305438779,-96593023923,-148646368490,107484573608,96104956471],
+[1,-3837199411252,15987954244320349,-4697757619575778443,2802514151170278956,38842254787450812632,-17674389726474822464,-102961225717149802432,26969461377075245824,86730122212734152192],
+[1,-312562763163,346886487839985702,-93018430645583163427,-387503893699644070202,-227171488895637052053,1059383162575032981336,1901280189644540086305,1023913564393379937375,106567992233337642217],
+[1,-361474931790,611242743954816096,1973129339163640404,-4210002821826072816,-21443528944285721232,-15903091278377251408,17306737477063294944,15843200491976309568,-4434448077967904704],
+[1,-3778306,8394069,18391906,-34113836,-39223526,37637081,36203162,-3782500,-3555784],
+[1,-191995,18675888,-415738733,-487665149,3237735637,2525439827,-7701107584,-3455492344,5556122951],
+[1,-3528390465674391,295399734866669842800558,-2387118057773104769342178715473,-7115146127465248916444129922720,11963803174219131542545857515424,51351848128578616328477557919744,7113243674866320054741354132480,-92257589727819000341535933628416,-68487179831190246952800572440576],
+[1,-45037547,172457645883975,-1790412841228672560,2627143167768991192,9869078575652884449,-12596759728507943955,-11343571983898538301,8920514581505543602,836831771418477463],
+[1,-14544930060140,137330449552913,-154735085579409,-876479584619080,1181245400641645,1927455097458009,-1718524722383285,-1592842129577062,-167254737213209],
+[1,-1873500886,25283363424,-29198798220,-138943513968,158616778288,124060717360,-125965364384,-21725034944,16468307008],
+[1,-3806541,20782949919,-25366811811144,71965988436246,12103674791283,-142770198449983,57267830343243,20593396170576,-4139930393533],
+[1,-60381,329921913,-102719400354,-307998071508,131405772501,985414316087,568240988175,-134586047862,-1251538057],
+[1,-19205016,82891756860,-75612791103068,-22071609728016,593620957189344,99885969101168,-1527816117229632,-114091235429184,1291999661947072],
+[1,-5057480,64075800525,-114778121458632,342619789089451,253607005501671,-1403010587394510,423528819767088,1315290786628078,-789115629856541],
+[1,-17285705759,3645479098061046,-977455946035418697,574017488559447304,3349789413247368432,-3569470539005100288,-1037838976341249792,2354542035384567808,-697220011518488576],
+[1,-6736624153863,183177091628494320483286,-6180182788352798530510347577,2716240087955115185667279592,22390195136916081365570958960,-6868581589286990272316103936,-23220089103784231280127389440,2774463146325228316623759360,6830897897415504340570279936],
+[1,-284114632574,23328357826240,-297773293976816,-1110327740162464,569323040643840,4056968008207104,1629816222242048,-1887449511731968,-365569572674048],
+[1,-61113734390,16008403307648,-87192050722668,-8774403711472,522877371403184,-217607455565136,-1064677625078688,341515181013568,772225254630464],
+[1,-575283,495123480,-94146773163,-254138442099,329023946235,1060550521751,-194008114416,-860201958138,231411895019],
+[1,-8715488748,5970471668822748,-131516946743691692,-362146003783658160,550507485957558816,1689675762706695152,-133240171325456256,-1367117267311120704,41605663243747264],
+[1,-754302766,204635138688,307739257524,-2886558712752,-9696999595088,-10366769647184,-2966994334496,924587406400,210205089856],
+[1,-298841222,38173752800,-1269934926572,7899038846608,-16724081597392,-866792844880,54216158908256,-76186873466304,33725611694144],
+[1,-2022907274,153018343932907460,320981614532593515,-1651825547656661424,-5520681723930583807,-2507330145920455952,7612874974670639605,9523512831628249435,3051738873709998473],
+[1,-345693,380929328,-8222062311,40755162175,-80293086305,60679695873,375595674,-15404928472,1789630471],
+[1,-1171530,41629156263,-9975372520038,4403806572024,63987965950752,-13186797282636,-112717060634367,-8838630882576,39713414160403],
+[1,-15249215987703345643683789,65999669051011969127587880853,-57965945899301298379214742190,-480754726560884120624717949336,46654331036366608056055398309,922676089806829907322011185351,525899145779582992611381401751,803706295997501863197084450,-6533057249290496498299013153],
+[1,-3,-85,330,1854,-8885,-7333,67837,-61572,-15731],
+[1,-18999317,14188004237,-654799572422,-142273400912,3076892255165,436752369839,-2186399517169,-425979209310,-20092498369],
+[1,-55267655,19031864097901,-1323773104630459478,7816233223528156410,-13003751961452721333,-4361156381824269353,29106395680462736587,-19234804081092308268,-410206599790220149],
+[1,-412277031461850,21678032306781299848248,-2461595077336056893741948,10570781888949923022591312,-10667200780571632434652752,-10137987860380589840189776,21329690948753641253058336,-8768906897999430259248192,500089379074200034412608],
+[1,-6974625030,7921875723488,-1938747773729292,1214230999079824,4740127316419120,-2454499565586384,-1488131163123616,296470031713856,27233217614912],
+[1,-792296275135581,7157731383572903793,-14192905336051175984266,-56193023159687986802166,3245304106744257077607,240118027162073934066565,219593747170291395284346,-127601727583918000465764,-153555585114394714772984],
+[1,-67379282,4783160426852,-37576960879135497,120962277105887128,40492542396123880,-423592117788419911,293306304698596826,64540471360047660,-29466075743752904],
+[1,-885599,1922958558,-14798379535,23102811288,29078205681,-91476357427,46833416626,23835146496,-18433878713],
+[1,-37874815979,112053086952040606,-1544680561043401411,-6722143771571041474,-749983562147267093,20680704519842468816,14958350042389121649,-13882577924450532001,-12578727178554392743],
+[1,-31069515170,993008647032,-6402959118300,-2905149336944,48134362508976,-26066596030032,-84622776300384,86427873915328,-16089254473664],
+[1,-104969409,2035692788899,-1999021285887904,4277012701797850,6490533958588179,-18781551224385823,6497216849011975,7256037158527100,-3779402883441125],
+[1,-4555508548662,2674415374271232,-81556857978611852,-155911831259546544,195164499853718640,353033807605799600,-121630137797124768,-160738427156398272,53457532904521792],
+[1,-292626982,11992564939200,-330201914364588,80624084150032,1942305209216560,-245628921486416,-3461988496574112,-175512680818624,1550860071386176],
+[1,-173228820876,1791731502684,-1014936656748,-18417922724208,19590235822176,60388707916784,-68861940059520,-61536451698240,71275249001408],
+[1,-11246620216342,99851364475552,328388289229524,-393490018444528,-2024960633935312,-1892179994121296,-20385701464480,686502512699968,229322168012864],
+[1,-1237071828105,226330377674255,-1028826653892667,-1243355993777356,7113712215592584,1502735894044352,-14186021241027776,-227811750892288,8154199934755328],
+[1,-2880325,13051731,61229670,-55416886,-332621863,-257926005,24693879,68374404,11869747],
+[1,-1216510579279377834,226162806214537461603432492,-91363300579643873512882902453,-190363013774198779059661199340,320772368294364761476370276040,621146801883650894342167055082,-209967955680511453135279973748,-206835781623241247022061648872,46846125166425639252867787039],
+[1,-16051762116,19087414262829,-126691360247643,-707884372586988,204692259817224,3682390171810368,2263521694089024,-4668341182458624,-4304362700713472],
+[1,-7418819767071,2646922977548674967694,-83245372936799845908873,498860138779433263629168,-1152057925399851778938048,848492435332317598462976,783476452275007945113600,-1568089294433419059658752,672589256230703474671616],
+[1,-7250282,838327455,-23386012735,-34079059949,85710931608,65998729223,-89276257757,3941198888,4945836787],
+[1,-658926378322,18624546237015928,-2062234561069619292,-10800530029643230576,-14347395422084489296,13569364360070177968,50870831766432498080,45071763330662993344,13380496521121534016],
+[1,-87653,78349445,-12327824838,-54682458328,-36903462411,115909907863,154880615935,24370087762,-3917426209],
+[1,-438085,117367249,-765249018,-902080036,6285923837,1004191351,-14765116657,2699080210,7161771479],
+[1,-89381858,-142394209,799646068,1526254980,-1546314144,-4251799647,-1262470950,1268754300,579015000],
+[1,-1678781529,6313135911903,-888987461357292,461137299708870,4475062911662451,-2137281952110171,-4379994405324045,79551555963984,364326732434611],
+[1,-5621468032962190117,19721495932848051225969410979081,-494435239021384832364087405797378646465,-3503664529435812829750998396559356043915,-9124243100343144417050357428842011111837,-9526368450086740816186714808892443240283,-486694537840218958297274619641538468550,5828567877426929283173978233126673295980,2905667579994707534468750797365702380296],
+[1,-814194057585,414056472969393,-19420820935146007,-135767377725635751,-267506949366593571,44028334994232964,753808838498392206,873256330986976101,315197368076799697],
+[1,-48907,2168113,-15522118,40915117,-21872408,-102803829,224647094,-179223282,51380371],
+[1,-1019523,5276891832,-512500692083,-2386438662987,-2722713064845,1113611574359,2316252243144,-228547173210,-175243581101],
+[1,-335795,8955359,-4221018,-218609410,-604886039,-661683308,-298461804,-23856012,12327687],
+[1,-8493477,119548048545,-1039122579942,1104300080064,4829469460413,-7772017130269,-3487646723661,6666637397586,1090822908167],
+[1,-24148314,10369842460185,-66358173654029330,-184211899158470751,179153716075265505,863191377824078990,425853472285376526,-508078822351279560,-363722086791200321],
+[1,-91217336,11790289341,-293572970004,1239757219462,-1181507394248,-2289699741743,4943392317978,-2519232117732,117157404888],
+[1,-251463513,10440593464,-14265126137,-210419757524,-221483555932,501196160416,1091475057552,697417217984,146211954368],
+[1,-25940023070,16615613882984966,-829888106638892401149,3107103253174297644999,-337341923594596490527,-10542300455562618582368,12615050591111237515880,-3682983838644978274608,316368182375400330512],
+[1,-344710145293,506731498334954649,-1305002432564881675,-27770714167595284139,-100974973531681593551,-159136863919579149436,-110396521799745595710,-17097447266803771775,9483928020957047641],
+[1,-19910899,1108243349,-1950406148,-38850153660,-82634407987,34083615647,288974196125,324929315398,117212127463],
+[1,-9615,1189381,-10892942,-70419271,-89261804,65855479,155687606,9425986,-52554269],
+[1,-1147627924054,4374439941008582,-1169722829354124386,-612087790189978155,8807145419930617852,3294095723624945425,-21200736991273111062,-4444970550839273348,16300547401929510392],
+[1,-7771959873865200,24298758098496889106127,-13467349983140240629450854,-31435123124322071864978217,31200803159740556829335643,120465025183103142530732661,82581065775122714689153866,16184315122000165922082828,-210904929440283813831032],
+[1,-76777,759763,-2575014,2781966,2455221,-7052269,3554543,904532,-762341],
+[1,-129121,120356183,-1587001214,7055600862,-14501253063,14635813347,-6580536485,811733712,33502771],
+[1,-53749,13871545,-212064370,-371996812,1399479345,1662006187,-2712066489,-1696167522,1758914271],
+[1,-709961863,5807333837354195,-30105496773035385164,118414882271428824296,-124409004468477171111,-6841469921742780647,45908280183240492363,185601242150477146,-3309039887809200593],
+[1,-449844927,501967196801922,-72847833684086316098,188158281820054449237,-42630456470973039012,-106990492719289674022,32807845530580852935,13035007653546210654,-3390566962302687329],
+[1,-23035292,118489036601663,-48932375051930605557,8568147074136076147,390559380607279426130,-92321152949709386920,-955466384049168573505,215934908717498580239,651139902668687666017],
+[1,-1682208809185979027263740087665,38460669002109216521306017047488771434208899615232339,-789215380710611948301225849906279583250175442757096555490324572457729702,1298256148668053925249650971384686586826702470064914229044090274999256215,6030329823121832098863396859553835498131032809403984593479645183753512324,-9490084470419754437453910267860740292415249457134746840897452481872148103,-12394600943847745519754407531435779729178452247379772195631334665755880166,17341080285216312802767349120 [...]
+[1,-1144265495309835730,49923475084524347869216,-15995488733125357472577758,-48796962740367288699108596,55713495918778048738211031,264166134873195160170026981,71070428772729452084221562,-287282783333568985533701572,-168386733883938686911812856],
+[1,-57220933917748482791055,421432683704137008041973785434926,-19706728990556038367918172669771186953673,-12723424145005161802854383220896287885368,127580657871872145892123207192434478107408,65741687506553976917267947208625253170432,-225142929942783048993990402047872349206272,-69761989665958342964660248694611335247872,78030709323948095525370311539795889876992],
+[1,-439201344361100057172,298682339369083510723957169,-1993819825297374478439136896,-413303435630088983138297958,14748097505558080719171177748,-9462024177248915246648070223,-26659710763054612986043303206,25359544965613546263800593388,-1888978298649312011654077384],
+[1,-5802127263,287719052234104404,-28057294707841304943420,35994973074431882507118,93560769205410381354270,-68930980457931982724892,-75581789160975641071308,-2168783935945534218663,1123856354218638808457],
+[1,0,-1125,-8187,418527,6063246,-29628708,-1114750107,-8016271533,-19159072973],
+[1,-291018221130686,6177811912283862896672,-24098188560084773665644,-55048694056791696049840,168071111174651808652720,115168131681977741268528,-310410872603033686377248,-2220471709185131354560,40713723811892150741056],
+[1,-45742676674,13285226508600,-884300379122844,-7924935686070000,-26863342798122320,-44887054617934160,-37686285269143904,-13497695087511104,-823934867294144],
+[1,-6812707,29260293621,-3863681916534,-33899272787250,-111278748580121,-166973063735213,-96219537975701,14879138337040,25557866840803],
+[1,-452685,258015456,-32640097359,-100508736369,-549156225,290122084313,369635601162,172652033112,26034334007],
+[1,-828120321,19196894484243,-2089902600280579,-8925328592739468,-8315112977750355,6139800082126669,8199504364294929,-793429782322065,-1642176313344791],
+[1,-29399280246084,41736821359896213,-8689441224824011203,25748899993528535340,13652429148325027992,-79459998887124758848,4683397865515364928,63322739270171112192,-2397673249658495488],
+[1,-25665087553029947767,5002010563050666885419506918,-4640397039631951425031718106690993,13276249105588143916264279701500984,1544827222563604148108556918471440,-40633243602231836771322648275415296,48647018551609709297127556195926272,-20092948136529590566080329583429632,1899626379682900076985335069339648],
+[1,-4637070372,267390918364691973,-3359070220125120611,-8105967729252032076,14433914809537703496,29396438188961407040,-16037510797635992256,-27512591734201443072,319497287955931648],
+[1,-740389088691,5004042945081330,-151636472754243634,-523513097234815119,412846622368142655,2424331629274400332,849682242734634177,-1814618342964912189,-649092289960547063],
+[1,-22667808,21478629458,-159734672882,310522791150,312843358201,-1596160761077,1087057090346,1013551660772,-1068737159384],
+[1,-118268599457732021762,13399423901526523925888893396,-37425136346633436638727167810220927,91910191137993644964843610826806732,158700085555037722899767217847608640,-466718969360496984942243557276658311,-57994110985325353303034094257966982,489803170996960297784106903074766828,-101196279482189530047794770391958856],
+[1,-8381463,4529381481,-7123725636,-40365253980,58609388961,112828469567,-139876332639,-95346809970,79811896607],
+[1,-190623987134302,10493332733817654112,-200976379697735153132,-362113359089685425264,1357637176890689247600,2000562870652299770800,-2241151875769637504288,-3021369928455051919552,-180984330143355512768],
+[1,-545911781686038,1730369425087127616,-272632842836862857356,888993602871827854480,191956349995970603696,-2594867433851352350928,969701493484050638176,1269690910288337998400,40768664618894583872],
+[1,-85874508818,1654204738629592,-716784590322130460,5627245910316387344,-17839758794358300240,28737683622515717296,-24297806085710613088,9830990459438087104,-1346093235942890432],
+[1,-27076783192750,24271758572883968,-243037537579443116,-2310073161266346160,-5326182729140894160,-675777809414818256,13147788338535368160,17552314353524427328,7070510147611960384],
+[1,-21155105313,1070254921430139,-8712148588351133312,-8310650783259700974,24696514940072822943,6983175715238778641,-12380078215276546173,-800178352897745916,349756210435254643],
+[1,-12385688757,1916080199393019,-58741618673592542228,26929663025166493266,239986041238361005407,-12316995824222759299,-248523201105492395517,-58399583014201665996,11162188846705939387],
+[1,-15580412921,10623466568,154966313561,-51279535173,-535328756625,-20329193443,668167599734,246541089532,-85980663233],
+[1,-1891790001388,574801216007853,-13941273621114531,67799559326838492,-108653948402398568,-13754837500374848,207321333594705472,-191671749190377728,52120943273655808],
+[1,-762692554055132,188338536604999496013685,-46929736652845070757445419374,-57831423295578968941024692602,204770636072818689433603599849,160466853240255751165397666568,-263630344100570306422895267718,-45159126601739526691377711235,65450970623725405682676537889],
+[1,-3106123,810597809,-1876412892,-9309480837,20165946328,35098753439,-71648543976,-43488201116,84488560031],
+[1,-3918735787722242231421117,80084232039976969048861461,-429331637992124079459325854,831075230218880302651032120,-408725236905283779167498811,-420414034582254809217609993,361175537167240879449700119,35761097653507891045200210,-48640722933604597956528257],
+[1,-112011082963371,5899066359243887720046,-14995174545778923704947,-34251476632829243370858,109639206542080354146123,10175095996245722651752,-196595181254681388459807,130116571613861427796599,-11528618720994558355439],
+[1,-3246166543941725424,151410873803245034545931959,-110953078534039896467952064542,609107951424251899808848415239,-1121115269275985262145454134649,637639391393057012614116747237,232557885323381458359435104522,-260101647651275389179436402868,16856809039625836040762310344],
+[1,-120054372,126025066500,-1421354175318,1353917591943,6080973645663,-8845904223982,-1484583717465,5457878297754,-1388894309713],
+[1,-46957890382377078,570204983223035855740524,-25672873250292003289796170440,33292485242970903574364959872,150518964306624020618101483008,-181040742420192126948708179968,-213632903931162431063281238016,195891936156400167131409285120,69566168302856134094250573824],
+[1,-32613422196,13293195120138093,-1451198812942745283,-11756808143248997588,-32085693547566336488,-24881695260413271872,37363082983377837632,77251669176633041664,37728923163711151616],
+[1,-1177876571708,6366485452947343117,-46575653986899163507,118087889720953902204,-102687859188394641768,-32784338554941935424,82031747722754312768,-13088162495314385152,-9550737546785669632],
+[1,-106193595,4410985912,-41256964077,-99838531933,157099639053,265867078099,-92388561848,5247677968,166889743],
+[1,-23408585484,71423272387788501,-5957416320204490299,6046568575728367188,17271011510560257480,-12692446337212757440,-7771006242052503744,5134862897421219072,49153182042931712],
+[1,-694029721,183524338905,-6882896399490,8710775797688,20599308065701,-29781479801389,-10134047490425,24318968190922,-7450233496889],
+[1,-902673908688158,1392214421567394848,-15624512933141859116,45976911448034913616,-1847088925044495568,-164925717518358482896,157602672401859580640,105528690493768488000,-141810055562534205376],
+[1,-145135715,1186849347,-1232951040,-9840395474,18746113329,23583433803,-57550208091,-12643422272,45915585619],
+[1,-1137129,2442823,10556386,-17706290,-32192263,32586259,30777579,-11138776,-8089213],
+[1,-466028963406063,64934307251398103646,-6795302529775113006487,-20111248346049663289440,10166165643444478722408,51213693101581443134377,-4938111472168085906118,-31715758212696299786004,3018843864850336077112],
+[1,-9987419931184017,766560303506581526663724687,-73777573791928694859141506558885353,240702694079437759281909426824308596,105453916148765716102467506033386794,-909588225036127831694292097476051563,434609095030391040744938868028123509,574516931945941600135504148887557936,-282567548391085032510183482287898684],
+[1,-619200324,247155304503,-14227997035842,-2932198663218,80360375375082,12041228088737,-110627350073286,-22984587133092,12136738671032],
+[1,-194430045483,10930146184233981,-37004080723791054054,169726782696692480550,-121850854246462427577,-419088703965457667649,644180698259593014915,-130415057098479078768,-77994015988756051997],
+[1,-37014675,150133071037,-89620040871142,-4246798262090,640816272972087,147122927011511,-1170139960911845,-531617720804872,159192200026963],
+[1,-1085703,3628104285,-197606400462,-400590842046,571459462659,1475071012244,732972054948,15568335054,-9004627981],
+[1,-4805757,162271526,-1207375811,3240587248,-3189146408,-248324480,2064628672,-711019520,-103058944],
+[1,-8082773729841,7260177718541589,28691529131451941,-20557898848641255,-213364185531482127,-184371916552970420,329892988876405854,601797085412257449,259979183434469281],
+[1,-327891526494659045215108397,12415949753135545692786849440153069,-2528824608659201824230894171119626984446,-19867650886035452536415225545819645157008,-59490878386915725860364903888898567091339,-80096469746659429846156883572389294365529,-36334678633058706082811834269602763167737,16048305290574653957540109247696824131978,14598909565142137515897772375275790462111],
+[1,-1046380114073260670,20461446800168814236788782402,-1395232429054800226055784119164,-11814830543579397346428451421,5187235222648332575642588530166,-236870611278038562893157010142,-4651363385403742416925139405309,150561742385028119335244624551,1038951692548448313014037731009],
+[1,-14044369,12712692256,-286283415627,368099638175,1245716824619,-1974026678075,-427395032890,1130080662208,-32110434817],
+[1,-5189841843,23667382972401,-12751055463010026,-70854790453915134,-97420334705757141,102420442025729991,333086507171382423,198625307893879704,-5385976036858301],
+[1,-100447264,2367460909,-11647735164,-22659089650,114239885504,72479652193,-359640790390,-77397327524,373442503832],
+[1,-2137478889,106206931677635,-137342518370245600,-548950403860134704,-806850507473777605,-509923887572030632,-99145665684287766,22120067430795354,6701494189754083],
+[1,-76845191,309627891,1178754408,-2684467622,-5638221435,7036965267,8677867233,-5024074076,-329883557],
+[1,-73349517645,9184526794530,-275174935414557,624089227488372,736671369199572,-2530554989347968,497043204973776,1702306987221312,-627298115144768],
+[1,-314188397798636593619823,335970190538190385985388108,-2414209575658577982978368202,-5403727205452820242763021868,12933423122404575469588280664,19593247481667281142338815857,-15129693407601610182001856262,-13604065318256732137470719220,7457050262510409218629063672],
+[1,-19613331564964,221825287915904845,-928652730777886419,-692577743679720820,4056206153338650200,-326088834466007360,-3929148676265343424,2185149010484329216,-265604387866161664],
+[1,-3981185570,76011080711988933,-23874049544574829458,-136138771723302192998,-240844682398345291548,-76918201009868870223,155033768170795859610,109760640295897249900,18782705380108675000],
+[1,-172918841552596,3326082333651752197,-77606226984224548443,-323486630754573011620,43392800154469323992,1311641451694761581248,833149624842987031616,-1354833569202833797376,-1213199124937137459712],
+[1,-219939,25656614,-148980261,300996296,-192818528,-86227968,120957952,-15949824,-1933312],
+[1,-325039,3248407325,-61496266078,208103854874,26916787075,-960970439460,946207281740,582283091062,-831729129029],
+[1,-27077552910,996329801790,-7650375746856,21540998233233,-18800807089122,-20061209044887,49042182145770,-30149768524068,5007823431256],
+[1,-28751530871839,10574092487997798958822,-1064233181826773349067411689,3086017265431827206232691784,2585812762794672936236166800,-12627806321822040150419161856,2591019949720475543671503104,12609645435265892314981599232,-6595161125977310121290272768],
+[1,-1167525,31775760,-249446135,758488911,-920336241,222836969,228398058,-23167992,-466673],
+[1,-25887144925806,3228307717735968,-83460145644098736,-31254690807457824,701552749826173440,32196751623863552,-1808979773357928192,147213095097255168,1319250460887229952],
+[1,-186067,8007080296,-1865934302217,3966801254303,9764014243985,-22965783804461,-7322978196952,29716362405820,-11181475114489],
+[1,-9105950887403324,17666285281338173839583953,-570225555961995219052493620,333720143634492832572686024,1257498900967760044752503413,-71646383269843578661102537,-323577514416177359919299151,25676111897036233933151855,5136184155827329813126817],
+[1,-407428497251464,1169129660369347454339,-12408323314263781106933,-22924663153724183062269,56734592730273345953278,88457730891209789862388,-46732029534409282400013,-57420744657176302056509,17302177255596979365593],
+[1,-213213855519,5423797965783991833,-1071312540597931185690,-498371991342578083170,5827169960992863316359,3468725904869426685603,-6569781497332253668557,-5369231464981086373632,-798079038447414756797],
+[1,-39145644316170,43098838479383974936,-1393381401798061080472636,-3902984552673438037505456,3794052851460402331786032,17307395339038214282812080,7735210768810043394014240,-7927441531908453543449664,-4759108912905967023018944],
+[1,-71587718645955734,417709663167480853549472,-121646909545951504366970700,178902787378484431960104912,289349589287143068373753968,-622054227198478243591683920,331988161251811787761431392,-68106746864077162624351936,4673098904747061778490432],
+[1,-4705121338830,188778652339574912,-86569198544652853100,134884524756528912976,310097710221151687856,-423514086093021322192,-287314421675886646304,299569972394547221568,-18948072597586879424],
+[1,-871311988917,41911926136054575,-17588024008736068228,-120949716225573676494,-238622182867805315625,121301920871698218781,1024479863088054971979,1286847499723222294116,524750985381154767083],
+[1,-660365,155219491,-1477944460,-1948890582,9157180447,10047668573,-12801064069,-17022201300,-4708346837],
+[1,-162440766,9725435232,-139472754732,-141447952944,651832308528,463200875056,-409455676704,-139580609472,31344580672],
+[1,-73991769,3779656539275,-1588324550621888,-2850700671540006,6639439780940335,12458178772978345,-3001336183876405,-7186195488461548,1228560936643291],
+[1,-602280,185127438,-8032330614,4655899242,36336791775,-13888605445,-34794992070,566261220,5905586312],
+[1,-793305427,10722262867,-37799372520,13250340782,134903221145,-150273057893,-117445140091,161375702896,16536355067],
+[1,-7241081574,25886115248064,-786523151946252,1487821004382864,4799922480477360,-12254503026731984,-3204019391007648,23648365707528768,-14001538998804416],
+[1,-68709109711103355268342,794212042694172998959675623203456,-199639739058313536433860442701516,-12256667642945599347878098666683632,-5135370218322307255753488643369808,55826302577396829528549641891338800,42093919672649181457791027580818272,-78094346463401717012162246496433600,-73603759196961759355613981208833984],
+[1,-3333295274727,34572781123653309,-11642536353513403198,-23346644708711384862,50795498459103908163,109640146196378460331,-23335310666994177381,-69139738612718500884,14647493405052447371],
+[1,-113,1176,-3819,-386,23467,-28696,-29832,52809,20412,-29249,-13190,-223],
+[1,-1572,41840,-301023,589524,1129931,-5022917,2678727,7731400,-10207822,3007737,369710,-116575],
+[1,-46755835,1206514419,-5981923871,-20191272521,74416768475,265724484731,116877948397,-443728589789,-721745657080,-459066275673,-134096733890,-14549748671],
+[1,-264906556,164181027958,-18724183114540,70479317370001,72968634377264,-492727397394752,41734123101312,1145261155531392,-311954756027392,-1066408976920576,177404063801344,324503752118272],
+[1,-5714993505410139272296507009782216,49344904230148839293333660593889114227590536,-75912374820652001820427996265214005713100104096,7989457304633369412761744707841570955171670040304,-15974027141958161121922623941579887028811517171968,-74507683121314337453815413692153070376569859762432,171572955584394974398431302183548180568510573755392,196203312057724991575518132602141074697099154452224,-592343421549858666594462955237479564143319009110016,-21950313161471530090788036532799002703196428335 [...]
+[1,-229977242,5072291958437,-111384225021100,762216494900532,-1477672431507456,-1588524670358112,8220409292590464,-6523629466987136,-5815313742183936,11727602200559872,-6334684476656640,1139431063577600],
+[1,-6882808231898278414037342656,3455143367204290127157342521604388436332,-36661537168170277066262237535877306066556,49588942370382232589832601858597528091646,356029612847288265291799825046259689332740,-524595811862855775985469194571914871611070,-1025519268248587041651819209950715500501700,1453885449064670671053483316089032783488877,747401846174933080367524379803979196331968,-1239203088628559715562735153436589082944258,371531325751546776299376086652334941513708,-2596776267543978399863664 [...]
+[1,-776227,35260611,-410113332,1237367010,2841962582,-15400560964,-1522071034,58779614473,-20028404187,-89273582271,28252161234,49612618017],
+[1,-136450,5782301,-48188974,-56158306,391808464,181381185,-993288620,-120590148,904251232,-106435088,-198059200,24040000],
+[1,-185290035842832019128,489482814788592695857251131528,-123165591405668541618484310233779552,1382699398509947087245384403121233136,1451187676274817203561770378629695744,-13349894710179599610890326621200296192,-7161656140212798022704305275356945408,43064097291752964399345748822207213312,16507693434261046711880963152245303296,-50477638084627481625862451247360866304,-13250359536841308975630870347363442688,14228047455441382330877775957566296064],
+[1,-75175041843787104,61342026170367580199808,-459463266498901473284181276,1317681283458970522968876746,914122129345622228984628000,-4650476380148947381939741522,1679357874014236559395086036,1961391076946617638906024301,-625118212064863116606332772,-340301790330722833417811686,37393240021622932269078624,16301182707955190633537041],
+[1,-103236086,1999763737347,-18474454131370,23989959263891,154675342323716,-361052854031686,-347070912266276,1292028449255827,25369597086578,-1592955973471413,278906963487694,623764364353873],
+[1,-32005221058803772,4637628625018039965419958,-3717203173067266200276095100,515928600836885426751576737537,2320049127272374987480220302912,-336813174821028838294852909072,-15025829957190389847037460506240,-18686879576562303590015975665568,15747418480602911176531575123968,45665292986239411427949932910848,29894966333608668824087612589056,5838410116627764613546230919424],
+[1,-88609157169,692928400278227226000,-18639810502615504141898100307,205777952269910958141151211118,-482100482299731057868364980215,-544825876501103943774907374028,3041454447812245838666224632240,-2257391223497009459404058192895,-2899875613277346059907829946934,4886609292155880670960692388815,-2131600168767190456076052602130,207947193981122938848112605041],
+[1,-3183024216747548838268,347142389627273461829262758,-441720998655169990664213630864,4524051142705909842996317944557,21516534920861500399431369303444,12595830718870799466701805370122,-52288927013806481740769778432036,-93644009415943389791426854732746,-59025104012256737349525392054516,-14575361103996761413374764908204,-565750002074801746316160856768,165811130561518571726921110129],
+[1,-81149837561968,58618864154304079578,-10830571585373935637269404,559883747296995376809712124049,-704021501828066365749085402734,-2399148964611094599666442234758,3316182464824620576759571538086,1774891982677587277011080687322,-3555103338212968630407231466105,821712824228692877635550872675,183505222060318667427166134250,6885453722596067903626375625],
+[1,-1563597153071082805844103218753996,67018408332460754570119044177545638014970296054,-37099623251162852103090511887535812015226342243484,2192103164616289787313759550176665641270606908066305,3243664850340545232184241866506929046611370635737776,-10514814844966903945501503146066088655871990118947664,-14593405378288550597224538554184727096639754156996160,8248342728040074458071264923080698568212480136102752,10243276134747222494276314221993162945870371455561984,-18451635385796344786729937235 [...]
+[1,-31223691959794830602,42765558803732842831684673,-2162188858003428992663539272,22770781950430057176647472882,102451102783461555146628667630,-112668144173957992900965272712,-704253264672437025442827529992,-120308852756586442278247401279,1487503938885226109640060456728,941700250082578881601264792984,-685422721638535434474056198048,-502933781105705055904786490864],
+[1,-115413570,287574254057,-58930369951719,352419522365346,1169402955316028,-2959989895389245,-6672131819310801,7192141635813067,12811780547077466,-4190604601917150,-4269036523999336,678648868147889],
+[1,-20591096196457859900,1840165796772256356932562727958,-131023868085871742456573444328908,701541835202696113652263210173297,-977060642871327547379950033689264,-894856974044186745852632084570176,2756921144494190878619328965507456,-668446399254552912794190974754176,-1496860430561691770146268333923328,603516994619292536597205769392128,-11814503100330293795716197736448,-1872632051224425073935746363392],
+[1,0,-1820,0,136936,0,-219035,0,32426,0,-1245,0,1],
+[1,-1838,110466,-1457530,-594624,21403672,7660335,-108811868,-64537506,201749902,118477401,-163628568,-66610521,54780838,6013838,-2634629],
+[1,-31231,3740300,-91245378,298050327,934268992,-4401788427,-2431740698,23399074654,-4515560125,-58297569578,31288267422,67359582176,-48602354979,-27725192380,22560578171],
+[1,-40906,4286270,-77984287,300321740,106381441,-2061594435,1180829506,5534987315,-3707589161,-8142120477,3490266018,6790116438,-312434143,-2360372242,-631502281],
+[1,-18809,1007298,-19194788,162121198,-636963643,1090404363,-104031362,-2310502773,3004164956,-1048406868,-331692206,203203153,-8625326,-3759294,39563],
+[1,-280247649,12370143747059,-850996907727117,17385934340695578,-104953177346379097,159438058163777720,405739918822250928,-1435006226138656256,743609052920692224,1986922299122192384,-2872064678639902720,712942514844729344,923102855384727552,-660573116061712384,124295145963126784],
+[1,-147688084208955,162514697333979754370332,-143071328373556693619330813,814498028407902816901453997,1196611416136583566907442618,-9230269879970666415572781126,-5425788355021335902884454579,39559829432680865697441913757,21760961992967478375384616236,-74241951529995456358177390411,-53798271555346409210041704986,46955989795933394809385282456,49835178466379490784238600176,6845569936271203166407322128,-2455600842687476406035136544],
+[1,-55984,12125520,-753020810,14698113542,-10692173035,-119906892189,96604699315,352327599777,-246807485043,-429478006526,248789197002,163568577073,-96696233259,13402072876,-354876733],
+[1,-1558704,11732227927,-17345399656327,7366503686195955,-219574743844310286,1050340456599871611,-735317912369847266,-4907175335868488768,9266149963815066786,3225135837007914497,-18338172819145680515,7818339608856537238,8690381388382833077,-5833601680346328990,-53861918546721361],
+[1,-47014,1305366,-11961382,38360176,27298817,-406153819,532893405,1091715987,-2802659689,-217964548,4576634412,-1968144183,-2321534605,992713950,510072919],
+[1,-1468556417308,8300573606061360977,-2705938416133717124406,88913013089260833668088,-724328419046932245976481,1018249670823443732932416,4943137677182200075532416,-13176958801527732381953024,-3702816671601945471262720,34434714043481702461800448,-19941833091022080943325184,-15466571196576506123911168,11141321908580043460706304,2504851002933833130573824,-1206197920399232258277376],
+[1,-145550294,227269379365,-25771911970426,342428330853015,-1911328449126942,5363840743877974,-6826775727050422,-822314470569433,12964880540399583,-11801460015689989,-2874370303777420,9159227460645875,-2583732509408625,-1835556642885000,850347584171875],
+[1,-89808081674911266815048,79128424242713365122209007041188013157,-3052605634288681896804299927151469576341541685931943,24591740445889625411404075364678274009623245146424112178428077240,-17295013047448966608202709921007706026591999795959417294752856173802683699,32065934259538323730519329339477925731742492637748351705129217070213658440,180607570389961128875518521066867058106924267586859262925565140714113736839,-310783026814865911699927351862274732318782062129735230464762961875076241643,- [...]
+[1,-388076989,1079758962665,-281006949020358,2301148841669910,-3410848405792517,-17769471739473636,56921951799902635,9594228172805605,-213590792333203357,191641376766491029,201891846345306138,-385039239395541241,133476271288119771,48496821572762834,-23740268125285297],
+[1,-47962447,344298998595169,-1598332423506290719,104781036499703828895,-749710308556203862066,-4349324659762972691351,-632975120607203069430,18754307769062652874915,19566916842088227057339,-11596690452466850281895,-21211124126666080071840,-2795072395379003358055,5387658101225234848338,2195619029190942811826,204373679742809086883],
+[1,-16518941628,5965603522057334479,-7902534202340150038231033,1563180197295523061482407892335,-46882526739774604073556748947752515,199012743910932707366382724114583796,193796820300599395174342295123702072,-1867627039195345259033198899684724864,1248034401840430437165453602838706816,5205132319166130315425869956472064512,-7581715285764862346487806635495281664,-2219003884964290099007728441527504896,9678066934299569255447806826446049280,-5773320352397120248544510362810892288,1009574104375918 [...]
+[1,-214049722596,3515714950664743487,-1352763252743625525233897,134556838299926508927771395895,-694229409087478927613911873976723,-1102057343661796638891548804391356,4122358655749500542829101852368056,6567735440113497250488420670505856,-7532406362362521310529100542616960,-12841282877968992332152198801927680,3381821301827390538283885749808128,8356624089583657435604470882791424,1493225778137995040855190707687424,-199261734637216839574037309931520,-1830221442717099448219317600256],
+[1,-743770101390697,179419980281548028514163,-47209784662238068446421397693,1248977050905179806682070021106,1138108288107148036661401765271,-16195729635522542904392224321224,-10335199330314339547964315816528,79093290025996932849843874650624,45994798119262657358160806641152,-176564325311670661882126639722496,-104241905119071943248240054968320,168410248253547783422319967862784,106300676916113531032996015702016,-43266322876251150948515193552896,-27685068885118507173495257956352],
+[1,-1139079482,9939596145423448,-1902335933481257389,117425735899567780910,-2203383527933426907249,-8743264574237991049024,12590871955008258625152,64511776855381108698112,-7620215622544895725568,-160648619168498645729280,-43192155000817402511360,142877658633594204061696,44786017920496170958848,-34610078159668865138688,-5023825047731467976704],
+[1,-28249891883667623002,209333791113233162830035670621,-206080823889129581613504494878551149,745711151920412521002496959709010921922,-349900362184470467941911122602681421536801,25866755459018097671760805134724063035040394,-218360750504778413337426314748621266761874617,726694460005575886021746251339899033147125897,-948101347386199003904663020605411105820179610,-351473235980877001164810248335701001104968716,2263115345811528628293565339506776266436277322,-1628043327174614390095620802286856 [...]
+[1,-38916737668,1528799614490356,-2959843739298189104,150572195870418370016,-1887499327892869844416,10111591039381440766592,-24333285436127064356608,9015500577097486903296,83798762809040412580864,-175225989236014069346304,72431666294194247462912,184152647584566164135936,-277161654443569499160576,123482629056011438227456,28344410041864025210880,-47905841572526876590080,17103424167689825288192,-2096255434680407687168],
+[1,-1275747,575090691,-21949658774,205310705256,49953112230,-4602380202278,-1053036804867,33434535620298,15540401250227,-109588439593713,-70375383550812,174662374195533,138502456852110,-125360405379972,-123681682521918,23360045823231,40926121982052,7767192570581],
+[1,-191284643876,6341580163006836,-1934761696042349496,164849740308816714448,-4754806011233115697888,31297720637036736916912,40563063715135443568640,-447349624075426149585792,-106666893295835639752960,2465625625557089016418048,238299557470283831347200,-6723424858005364092543232,-1437413580211248751588352,9098140489377774130455552,4194676060685113931065344,-4594013743663310745149440,-3833217510275099671912448,-745788684857196104839168],
+[1,-11750704,1200072194,-27059115460,275329643437,-1507066331248,4350637026294,-3402695953616,-19674226511210,71635260352328,-81896826447748,-72414392799636,335995637293393,-374570043319420,6375222215324,412780308002080,-455545471094544,219580211621952,-41955549898176],
+[1,-205266599,3059043580551,-7377470986131672,255750467781184134,-1632953660152413431,1068348929913192629,15919951883702020817,-32949097618499452897,-41812738164568952022,160392472026537870106,-19845888168060926647,-288196883084542066323,200932996304757155217,155961581801981478736,-191793894011410484713,20117729114239674902,27921588898947726276,-6338062216813197688],
+[1,-45609411820,264233335559214594,-66441505779889023806,1651301768353913905227,-5702509244843790310454,-17718742162923383793609,94554646423607253705100,41437587470866244656495,-576600445583458394851654,185162070050586644716796,1716238691984098960776328,-1138541205600600045107878,-2575616331400632645628288,2165795832813798728006237,1728370435725705408915414,-1598090764684425326864451,-311652308942791826171932,277546280986572881143364],
+[1,-1504162,19478258228,-1774538541554,33455987402844,43770320753508,-1315720656188287,-2806299428446827,13190626459075508,39866462208944136,-42146184067179328,-226648796078997568,-47571258714568960,562561066722199040,515663033658126336,-448457920894472192,-815336848346726400,-205333486181974016,175547802745831424,85189809140924416,6179324718743552,-953640242118656],
+[1,-2914968254,729565958618632,-37698933666991280689,557592894223395782959240,-1616283511372600082985105238,97042988438726838461737336436,-232284398484404334687457106105,-6732183788640945316473023556024,-21348108851313726862647539943344,9922779856397149629502473433344,151080206871502990665840133664512,191217808340180216960353992423424,-216777783257003298753858212900864,-657568359287067519413657902383104,-264659148877840769934330259767296,519913318612345880722233221447680,5259213192634249 [...]
+[1,-480959,127971287,-6210024988,117387642442,-974058483871,2716073861918,7176144743881,-48663748603594,18221811396627,253842407160806,-280982367878873,-541503028206526,840715026571134,401195003303691,-937048907916075,29239682420856,364610731756647,-90373942881826,-27466833161452,8779464511312,-163262489641],
+[1,-39941932898,6210764439806586,-5769357270954813832,1526235405765947359933,-105496607496711652450502,450637888725327737131320,9650202211913359888961239,31737300783657595070950160,-10913973852576470640831520,-245106653555832534199738368,-427683566166591584370447360,43660237154569010897502208,877952103594834582382673920,792609270976277877255831552,-195542552331371677253369856,-644898095720832121314476032,-295879994185715924103856128,-3536321127891585200029696,19459832831021097932554240,1 [...]
+[1,-86337736,7598242460043,-167569983434765540,17305983969682532799,-292019419313169397092,2075131431123040068658,-6665424416191304282566,2295068132975694200659,52514343624238390352037,-155524020328280599942642,72098535485922177259119,502162464846325947972732,-1081612566036474720415173,329934201842389030200420,1737975836203010588034872,-2616168718135781832784509,780663560042069680589185,1552368103386097794485265,-1884069429357675284396666,861632370133018734294910,-149417527248961307018797],
+[1,-75050219,2577820557210,-8708771477008617,10240537455834038070,-4171566442262579317992,166242467088694549400458,-1033863257761812234666121,-8510117703992561276196134,-1712218241553391418046499,59073478059006844802989434,62496120000886690394671741,-133496170027675884289942785,-206919534061107404384206114,87453839145789156887240569,220760986134809816904048267,7507090977497795083539865,-78187519420065641576046009,-14126847417965732398061212,2838691415177922884136661,323334550937520096385 [...]
+[1,-17381757580476,43726977976046475,-38587155046755611490,14404264950505670063217,-1992519320820093070368720,21131148543713011874724917,-40503643542455943304425426,-221092172903838528916304883,686267541176044244116291476,934545360515028736247232153,-3842393422466848354481305014,-2297337276469143012081280444,10852822516123803157100624352,4469779496649171224523959382,-16461707981027715595966487388,-7275510508518637850373793095,11989066274939596734697677612,6820828079009357932513725593,-23 [...]
+[1,0,57,0,1197,0,13681,0,136854,0,1048044,0,4603892,0,11460015,0,16001100,0,11131014,0,2739339,0,-368793,0,-7569],
+[1,0,0,0,-18,0,0,0,9],
+[-23708160,-225566208,-1131314688,-3968372736,-7276863744,-1112932224,22694392512,49279399488,50845741200,22483386864],
+[1,0,-2688,0,3413088,0,-2724744960,0,1535600481660,0,-650301043097664,0,215057090579702112,0,-56979500534544010752,0,12312194148189466704810,0,-2197264040603188502904000,0,326768343229764821604655296,0,-40745795222319658376664019968,0,4276794417515300890531785084048,0,-378647131200860936657636244230400,0,28286761991498448324281307379182240,0,-1780798310985522419497792899399649536,0,94205009985586665787286958769846028883,0,-4168270688350510971203431806915951526848,0,1532562820530160729566 [...]
+[1,0,-17229669740904638767885350393332951562413714341828385758128479090081583244187998208,0,-71235119005677090243907227200286049434393307742277600967494975973115889925067636736,0,-5259378713381781018295191965451438296538249792128869065118144857985370794902945792,0,-67185232557396629198034682141700718381157435991581841393585439368871735885394083840]
+]; }
+
+B=8*10^4;for (j = 1, #v, print (j, "\tdisc = ", nfdisc([Pol(v[j]),B])));
+\\ regression cases:
+nfdisc([x^64+2^16, 2])
+{ setrand(4);
+nfdisc([x^16+55217847968*x^14+11568829819230010799488*x^12+19573246544559873463135725971456*x^10+115798873240393273382017613115315418501120*x^8+9751010259434557755318401766641355472978476793856*x^6+2904119935452194825826787270394602800779367044985691045888*x^4+6769347776028701650273803177165355131571803342503490887843053568*x^2+63248430005730803250826766342470963758035727918152866840245543135870976,100])
+}
+{ \\ #682
+  setrand(3);nfdisc(x^16-60984*x^12+11303671896*x^8-20089501740000*x^4 +108519183506250000)
+}
+{
+nfdisc([x^24-550424160*x^22+23447555340004224*x^20-254383477518162636864000
+*x^18+862812576554550932720517161472*x^16-1110768959838047114430666510009753
+600*x^14+541867253550600812790187039808706018508800*x^12-8880986872306708993
+8371545135871969866802135040*x^10+502809538666033468057546874414277270924367
+6318040064*x^8-104322413758462359374825625780272604972404212502794076160*x^6
++694786520666433058606640970483690360770938934881316184784896*x^4-1330062293
+022709810755455132342051017217797022292939901816012800*x^2+16121742882523614
+4296369045049933195993063619592571720690380570624,100])
+}
+{
+nfdisc(x^72-75690*x^66+2229471657*x^60-36061389458264*x^54+392606586400579
+074*x^48+539504008591676523876*x^42-426714543074858418197350*x^36-4006505099
+33584626955407768*x^30+394042536990878794791484268061*x^24-91837221214280331
+370349672199754*x^18+6951526052977952270050730689115037*x^12+785336155384304
+5069776294861433728*x^6+4160868820129268027225702088135675904)
+}
+
+f=x^3 - 17298759218009623610183566031041339386720075700346652258609213718601496711017550 61362121841699226629974891813971909562156420018402691827454215309735885355556958 2565670693186076986600637143595083773458690435867304359843677566038921950799648* x^2 + 18711064803260370378157988406301782093292956776803873824168436875360414402394604 63586013733720488993651052248629894449551488639706757608435718517657626971261696 1979263694661040207430295034473827583701817472886223534079585752374021477 [...]
+d=poldisc(f); e=valuation(d,2); valuation(nfdisc(f, [2, e; d/2^e, 1]),2)
+valuation(nfdisc(f, [2]), 2)
+
+T = (x+1)^2 + 3*5^2*7^31;
+P = [[7], [5,7], factor(poldisc(T)), 7, 2, 1];
+for (i=1,#P, print(nfbasis(T,P[i]), ", ", nfdisc(T,P[i])));
diff --git a/src/test/in/select b/src/test/in/select
new file mode 100644
index 0000000..51fef21
--- /dev/null
+++ b/src/test/in/select
@@ -0,0 +1,6 @@
+select(isprime, vector(50,i,i^2+1))
+select(isprime, List([1,2,3,4,5]), 1)
+select(isprime, List([1,2,3,4,5]))
+select(x->(x<100), %)
+select(x->x, [0,1,2;0,2,0])
+select(x->!x, [0,1,2;0,2,0])
diff --git a/src/test/in/ser b/src/test/in/ser
new file mode 100644
index 0000000..3f16965
--- /dev/null
+++ b/src/test/in/ser
@@ -0,0 +1,22 @@
+default(realprecision,38);
+s=x+x^2+O(x^5)
+f=[atan,asin,acos,cosh,sinh,tanh,acosh,asinh,atanh];
+{
+for (i=1,#f,
+  print(f[i](s));
+  print(f[i](O(x^5)));
+)
+}
+O(x^-2)
+O(1/x^2)
+trace(I*x+1+O(x^2))
+norm(I*x+1+O(x^2))
+a=Ser(vector(200,i,i));
+a^2 == a*(a+1) - a \\ test RgX_mullow, RgX_sqrlow
+3+O(1)
+serreverse(x/2+O(x^2))
+serreverse(tan(x)/2)
+
+Ser(x+y+O(x^2),x)
+Ser(x+y+O(x^2),y)
+Ser("")
diff --git a/src/test/in/set b/src/test/in/set
new file mode 100644
index 0000000..3477603
--- /dev/null
+++ b/src/test/in/set
@@ -0,0 +1,23 @@
+Set(Vecsmall([1,2,1,3]))
+Set(List([]))
+L=List([1,3,1,2,3]);
+Set(L)
+listsort(L,1); L
+Set(1)
+a=Set([5,-2,7,3,5,1,x,"1"])
+b=Set([7,5,-5,7,2,"1"])
+setintersect(a,b)
+setisset([-3,5,7,7])
+setisset(a)
+setminus(a,b)
+setsearch(a,3)
+setsearch(a,"1")
+setsearch(b,3)
+setsearch(L,3)
+setsearch(1,3)
+setunion(a,b)
+
+X = [1,2,3]; Y = [2,3,4];
+setbinop((x,y)->x+y, X,Y)
+setbinop((x,y)->x+y, X)
+setbinop(x->x, X)
diff --git a/src/test/in/size b/src/test/in/size
new file mode 100644
index 0000000..b098e64
--- /dev/null
+++ b/src/test/in/size
@@ -0,0 +1,11 @@
+default(realprecision,38)
+#1
+#1. == if (#(2^32)==1, 2, 4)
+length([x])
+length(List())
+length(List([1,2,3]))
+length("abc")
+matsize([1,2,3])
+matsize([1,2,3]~)
+matsize(matrix(2,3))
+matsize(1)
diff --git a/src/test/in/sort b/src/test/in/sort
new file mode 100644
index 0000000..9288c3c
--- /dev/null
+++ b/src/test/in/sort
@@ -0,0 +1,24 @@
+default(realprecision,38);
+v = [[1,2], [4,1], [3,4], [1,3/2], [5/2,1.]]; x = [1,2]; y = [5,2];
+w = vecsort(v)
+vecsearch(w, x)
+vecsearch(w, y)
+
+K = [ 1, 2, 3, [2,1], [3,1], (x,y)->sign(x[1] - y[1]) ];
+{
+  for (i = 1, #K,
+    iferr (w = vecsort(v, K[i]);
+           print(w);
+           print(vecsearch(w, x, K[i]));
+           print(vecsearch(w, y, K[i])),
+           E, print(E))
+  )
+}
+vecsort(Vecsmall([4,3,2,1]))
+vecsort(List([4,3,2,1]))
+v=[4,3,4,1,4,2,3];
+vecsort(v,, 8)
+vecsort(v,, 8+1)
+vecsort(v,, 8+4)
+vecsort(v,, 8+4+1)
+vecsort(List(),,4)
diff --git a/src/test/in/sqrtn b/src/test/in/sqrtn
new file mode 100644
index 0000000..ac9d316
--- /dev/null
+++ b/src/test/in/sqrtn
@@ -0,0 +1,53 @@
+default(realprecision,38);
+sqrtn(0.,3)
+sqrtn(0.*I,3)
+sqrtn(1/2,3)
+sqrtn(quadgen(5),3)
+sqrtn(8+O(17^100), 3)
+
+test(b, M)=
+{ my (B = b);
+  for (i=2, M,
+    B *= b; \\ b^i
+    a = sqrtnint(B + 1, i);
+    if (a != b, error([b, B, i]));
+  );
+}
+test(2, 100);
+test(2^32+1, 10);
+test(2^64+1, 10);
+
+sqrtn(Mod(0,3),-2)
+sqrtn(O(3),-2)
+sqrtn(0*ffgen((2^64+13)^2),-2)
+sqrtn(0*ffgen(3^2),-2)
+sqrtn(0*ffgen(2^2),-2)
+
+sqrtnint(10^1000,1001)
+sqrtn(2., 10, &z)
+z
+sqrtn(10+O(3^8), 10, &z)
+z
+sqrtn(1+O(3^3), -3)
+sqrtn(1,1)
+sqrtn(1,-1,&z)
+z
+sqrtn(Mod(2,4),3)
+sqrtn(Mod(2,4),3,&z)
+sqrtn(1, 3)
+sqrtint(0)
+iferr(sqrt(3+O(2^2)),E,E)
+iferr(sqrt(3+O(2^3)),E,E)
+iferr(sqrt(Mod(2,11)),E,E)
+iferr(sqrt(Mod(2,33)),E,E)
+sqrt(0.e-10+1e-10*I)
+[sqrtn(27+O(3^4),3,&z), z]
+[sqrtn(16+O(2^5),4,&z), z]
+[sqrtn(2^6+O(2^9),6,&z), z]
+sqrtn([8,27],3)
+iferr(sqrtn(Mod(2,7),3),E,E)
+[sqrtn(Mod(2,7),3,&z),z]
+iferr(sqrtn(Mod(2,21),3),E,E)
+iferr(sqrtn(Mod(6,35),6),E,E)
+[sqrtn(Mod(64,101),6,&z),z]
+sqrtn(0,3)
diff --git a/src/test/in/stark b/src/test/in/stark
new file mode 100644
index 0000000..1e76549
--- /dev/null
+++ b/src/test/in/stark
@@ -0,0 +1,46 @@
+allocatemem(20*10^6)
+v=[321,520,577,840,904,1009,1129,1229,1297,1509,1901,1937,2305,2584,2920,3281,2^15-199];
+for(i=1, #v, print(v[i]": "quadhilbert(v[i])))
+
+{
+v = [
+[y^2-145,1],
+[y^2-229,1],
+[y^2-401,1],
+[y^2-577,1],
+[y^2-761,1],
+[y^3-y^2-17*y-16,1],
+[y^3-14*y-7,1],
+[y^3-y^2-16*y+22,1],
+[y^3-36*y-45,1],
+[y^3-21*y-35,1],
+/* tougher: */
+[y^3-12*y-1,1],
+[y^3-y^2-17*y-16,1],
+[y^3-y^2-30*y-27,1],
+[y^3-14*y-7,1],
+[y^3-y^2-16*y+22,1],
+[y^3-y^2-30*y+71,1],
+[y^3-y^2-16*y-6,1],
+[y^3-36*y-45,1],
+[y^3-12*y-1,[5,y+1]],
+[y^3-y^2-37*y+64,1],
+[y^3-y^2-9*y+8,1],
+[y^3-21*y-35,1],
+[y^3-y^2-16*y+8,1],
+[y^3-y^2-4*y-1,7],
+[y^3-y^2-7*y+6,[29,y-13]]
+];
+}
+
+do(w) =
+{ my(mod, bnf);
+  mod = w[2]; bnf = bnfinit(w[1]);
+  if (type(mod) == "t_VEC", mod = idealhnf(bnf, mod[1], mod[2]));
+  bnrstark(bnrinit(bnf, mod, 1), 0);
+}
+for(i = 1, #v, print(v[i]": "do(v[i])));
+
+\\ quadray(31897,1)
+
+bnrstark(bnrinit(bnfinit(quadpoly(40,y)),120,1),matdiagonal([1,1,2,2]))
diff --git a/src/test/in/str b/src/test/in/str
new file mode 100644
index 0000000..be89e7b
--- /dev/null
+++ b/src/test/in/str
@@ -0,0 +1,7 @@
+v=Vecsmall("hello world!")
+Strchr(v)
+Strtex(Mat(1/2))
+Strtex((x+y)^3/(x-y)^2)
+Str(x)
+Str(1/2)
+Str("a","b",1)
diff --git a/src/test/in/subcyclo b/src/test/in/subcyclo
new file mode 100644
index 0000000..b59d134
--- /dev/null
+++ b/src/test/in/subcyclo
@@ -0,0 +1,2 @@
+polsubcyclo(8,1)
+polsubcyclo(1048,2)
diff --git a/src/test/in/subfields b/src/test/in/subfields
new file mode 100644
index 0000000..f819fb5
--- /dev/null
+++ b/src/test/in/subfields
@@ -0,0 +1,34 @@
+/*
+test(n)=
+{
+  p = x^n-x-1; c = p;
+  for (i=1,n-3, c = polcompositum(p, c); c = c[#c]);
+  c;
+}
+nfsubfields(test(5));
+*/
+
+/* From Hulpke and Klueners's papers */
+{v=[
+x^6 + 108,
+x^8 - 12*x^6 + 23*x^4 - 12*x^2 + 1,
+x^8 - 10*x^4 + 1,
+x^8 + 4*x^6 + 10*x^4 + 12*x^2 + 7,
+x^9 - 18*x^8 + 117*x^7 - 348*x^6 + 396*x^5 + 288*x^4 + 3012*x^3 + 576*x^2 + 576*x - 512,
+x^10 + 38*x^9 - 99*x^8 + 1334*x^7 - 4272*x^6 + 9244*x^5 - 8297*x^4 + 1222*x^3 + 1023*x^2 - 74*x + 1,
+x^10 - 20*x^9 + 80*x^8 + 200*x^7 - 3770*x^6 + 872*x^5 + 29080*x^4 + 36280*x^3 - 456615*x^2 + 541260*x - 517448,
+x^10 - 10*x^8 + 20*x^7 + 235*x^6 + 606*x^5 + 800*x^4 + 600*x^3 + 270*x^2 + 70*x + 16,
+x^12 + 6*x^9 + 4*x^8 + 8*x^6 - 4*x^5 - 12*x^4 + 8*x^3 - 8*x + 8,
+x^12 + 9*x^11 + 3*x^10 - 73*x^9 - 177*x^8 - 267*x^7 - 315*x^6 - 267*x^5 - 177*x^4 - 73*x^3 + 3*x^2 + 9*x + 1,
+x^12 - 34734*x^11 + 401000259*x^10 - 1456627492885*x^9 - 2537142937228035*x^8 + 187620727556 79375516*x^7 - 812368636358864062944*x^6 - 70132863629758257512231931*x^5 + 25834472514 893102332821062085*x^4 + 76623280610352450247247939584745*x^3 - 45080885015422662132 515763499758450*x^2 - 2070499552240812214288316981071818900*x - 5505057590977785454 85364826246753544,
+x^15 + 20*x^12 + 125*x^11 + 503*x^10 + 1650*x^9 + 3430*x^8 + 4690*x^7 + 4335*x^6 + 2904*x^5 + 1400*x^4 + 485*x^3 + 100*x^2 + 15*x + 1,
+x^32-2,
+x^27 - 120*x^25 - 63*x^24 + 5673*x^23 + 5181*x^22 - 138003*x^21 - 167184*x^20 + 1865730*x^19 + 2668613*x^18 - 14070078*x^17 - 21889917*x^16 + 57688596*x^15 + 89482089*x^14 - 132575217*x^13 - 190829625*x^12 + 164200812*x^11 + 215956974*x^10 - 86796519*x^9 - 129504396*x^8 + 1575183*x^7 + 32931993*x^6 + 9928740*x^5 + 49968*x^4 - 372144*x^3 - 50736*x^2 - 1344*x + 64,
+x^12 - 3*x^10 + 8*x^8 - 14*x^6 + 21*x^4 - 23*x^2 + 26,
+x^24 +8*x^23 -32*x^22 -298*x^21+624*x^20+4592*x^19-8845*x^18-31488*x^17+76813*x^16+ 65924*x^15 - 265616*x^14 + 48348*x^13 + 385639*x^12 - 394984*x^11 - 20946*x^10 + 369102*x^9 - 362877*x^8+183396*x^7+434501*x^6-194418*x^5+450637*x^4+125800*x^3-16401*x^2-45880*x+ 115151
+/*
+x^60 + 36*x^59 + 579*x^58 + 5379*x^57 + 30720*x^56 + 100695*x^55 + 98167*x^54 - 611235*x^53 - 2499942*x^52 - 1083381*x^51 + 15524106*x^50 + 36302361*x^49 - 22772747*x^48 - 205016994*x^47 - 194408478*x^46 + 417482280*x^45 + 954044226*x^44 + 281620485*x^43 - 366211766*x^42 - 1033459767*x^41 - 8746987110*x^40 - 15534020046*x^39 + 23906439759*x^38 + 104232578583*x^37 + 31342660390*x^36 - 364771340802*x^35 - 547716092637*x^34 + 583582152900*x^33 + 2306558029146*x^32 + 998482693677*x^31 - 3932 [...]
+*/
+];}
+
+for (i=1,#v, print(i,": ",nfsubfields(v[i])))
diff --git a/src/test/in/subgroup b/src/test/in/subgroup
new file mode 100644
index 0000000..276447c
--- /dev/null
+++ b/src/test/in/subgroup
@@ -0,0 +1,10 @@
+G=[53835600, 29]
+do(h,G)=print(mathnf(concat(h,matdiagonal(G))));
+forsubgroup(h=G,[5], do(h,G))
+forsubgroup(h=G,[10], do(h,G))
+subgrouplist(G,[12])
+forsubgroup(h=[],[1],print(h))
+forsubgroup(h=[],[2],print(h))
+forsubgroup(h=[],2,print(h))
+forsubgroup(h=[2,3],2,print(h))
+forsubgroup(h=matid(2),2,print(h))
diff --git a/src/test/in/subst b/src/test/in/subst
new file mode 100644
index 0000000..c7a1349
--- /dev/null
+++ b/src/test/in/subst
@@ -0,0 +1,24 @@
+x; y; p; q;
+subst(Y/X,X,x)
+substvec(x+y,[x,y],[1,x])
+
+\\ #1321
+v = [p + w*q, w*p + q] * Mod(1, w + 1);
+substvec(x+y, [x, y], v)
+\\ #1447
+subst(O(x^2),x,0*x)
+subst(x+O(x^2),x,Mod(1,3))
+subst(x+O(x^2),x,Mod(0,3))
+subst(1/x+O(x^2),x,Mod(0,3))
+subst(2+x+O(x^2),x,Mod(0,3))
+
+substpol(x,1/x,y)
+substpol(Mod(x*y^2, y^3*x^2+1), y^2,y)
+substpol(x*y^2/(y^3*x^2+1), y^2,y)
+substpol(List(), y^2,y)
+substpol(List(x^2*y^2), y^2,y)
+substpol(x^2+y^3*x^3+O(x^4),y^2, y)
+
+subst(1,x,[;])
+subst(1,x,Mat([1,2]))
+subst(x^2+x^3+O(x^4),x, 2*y+O(y^2))
diff --git a/src/test/in/sumdedekind b/src/test/in/sumdedekind
new file mode 100644
index 0000000..f7a34c7
--- /dev/null
+++ b/src/test/in/sumdedekind
@@ -0,0 +1,4 @@
+sumdedekind(-2,-3)
+sumdedekind(2, 4)
+sumdedekind(123186,28913191)
+sumdedekind(2^64+1, 2^65)
diff --git a/src/test/in/sumdiv b/src/test/in/sumdiv
new file mode 100644
index 0000000..566ec43
--- /dev/null
+++ b/src/test/in/sumdiv
@@ -0,0 +1,41 @@
+n = 10!; fa = factor(n);
+sumdiv(n, d, eulerphi(d))
+sumdiv(n, d, d^2)
+sumdiv(fa, d, eulerphi(d))
+sumdiv(fa, d, d^2)
+sumdivmult(n, d, eulerphi(d))
+sumdivmult(n, d, d^2)
+sumdivmult(fa, d, eulerphi(d))
+sumdivmult(fa, d, d^2)
+
+sigma2(x) = sigma(x,2);
+sigma3(x) = sigma(x,3);
+core2(x) = core(x,1);
+fun = [ispowerful, moebius, core, core2, omega, bigomega, eulerphi, numdiv, sigma, sigma2, sigma3];
+for(i=1,#fun, print(fun[i](fa)))
+
+test(fa)= for(i=1,#fun, print(iferr(fun[i](fa),E,E)));
+test(0)
+test(factor(0))
+test(-2)
+test(factor(-2))
+
+fa = factor(1);
+for(i=1,#fun, print(fun[i](fa)))
+divisors(fa)
+
+fa = factor(0);
+ispowerful(fa)
+core(fa)
+core2(fa)
+divisors(fa)
+
+fa = [5!, factor(5!)];
+core2(fa)
+divisors(fa)
+
+n = x^2*(x+1)^3*(x+2)^5; fa = factor(n);
+sumdiv(n, d, d)
+sumdiv(fa, d, d)
+sumdivmult(n, d, d)
+sumdivmult(fa, d, d)
diff --git a/src/test/in/sumformal b/src/test/in/sumformal
new file mode 100644
index 0000000..12573f2
--- /dev/null
+++ b/src/test/in/sumformal
@@ -0,0 +1,7 @@
+sumformal(1/n)
+sumformal(0)
+sumformal(1)
+sumformal(n)
+sumformal(n^2)
+sumformal(x*y + 1)
+sumformal(x*y + 1,y)
diff --git a/src/test/in/sumiter b/src/test/in/sumiter
new file mode 100644
index 0000000..362f38e
--- /dev/null
+++ b/src/test/in/sumiter
@@ -0,0 +1,23 @@
+HEAP=[23, if(precision(1.)==38,200,230)];
+\p 19
+\e
+intnum(x=0,Pi,sin(x))
+intnum(x=0,4,exp(-x^2))
+intnum(x=1,[1],1/(1+x^2)) - Pi/4
+intnum(x=-0.5,0.5,1/sqrt(1-x^2)) - Pi/3
+intnum(x=0,[[1],-I],sin(x)/x) - Pi/2
+\p 38
+prod(k=1,10,1+1/k!)
+prod(k=1,10,1+1./k!)
+Pi^2/6*prodeuler(p=2,10000,1-p^-2)
+prodinf(n=0,(1+2^-n)/(1+2^(-n+1)))
+prodinf(n=0,-2^-n/(1+2^(-n+1)),1)
+solve(x=1,4,sin(x))
+sum(k=1,10,2^-k)
+sum(k=1,10,2.^-k)
+4*sumalt(n=0,(-1)^n/(2*n+1))
+4*sumalt(n=0,(-1)^n/(2*n+1),1)
+sumdiv(8!,x,x)
+suminf(n=1,2.^-n)
+6/Pi^2*sumpos(n=1,n^-2)
+if (getheap()!=HEAP, getheap())
diff --git a/src/test/in/thue b/src/test/in/thue
new file mode 100644
index 0000000..daff338
--- /dev/null
+++ b/src/test/in/thue
@@ -0,0 +1,28 @@
+allocatemem(20*10^6);
+thue(x^4 - 13*x^3 - 172*x^2 - 13*x + 1, 9)
+thue(x^3 - 2, 2)
+thue(x^6 - 2, 2638)
+thue(x^3 + x^2 - 43690*x - 3529208, -3572896)
+thue(x^4-x^3+x^2-x+1,1)
+thue(x^3-48,320)
+thue(x^7-401,88)
+
+thue(thueinit(x^3-100,1), 25)
+
+thue((x+1)^2*(x^2+2), 12)
+thue((4*x+1)^2*(x^2+2), 75)
+
+thueinit(x^0)
+
+thue(23*x^4 + 40*x^3 - 600*x^2 + 160*x + 368, -144)
+
+thue(x^3+92*x+1,3^3)
+thue(x^3-18*x^2+81*x+1,3^3)
+thue((x^4+1)^2,4)
+thue((x^2+1)^2*(x^2-2),0)
+thue((x^3-2)^2,0)
+thue(x^3-2,0)
+thue((x^2+1)^2*(x^2-2),-4)
+thue(x^3-12*x-13,87)
+thue(x^3-537825*x^2+537824*x+1, 1)
+thue(thueinit(x^3-x-1),1578191)
diff --git a/src/test/in/time b/src/test/in/time
new file mode 100644
index 0000000..25f1d43
--- /dev/null
+++ b/src/test/in/time
@@ -0,0 +1,9 @@
+gettime();
+T=getabstime();
+for(i=1,2*10^6,)
+T2=getabstime();
+t2=gettime();
+abs(t2 - (T2 - T)) < 5
+for(i=1,2*10^6,)
+t3=gettime();
+abs(t2+t3 - (getabstime() - T)) < 5
diff --git a/src/test/in/trans b/src/test/in/trans
new file mode 100644
index 0000000..02cd8fd
--- /dev/null
+++ b/src/test/in/trans
@@ -0,0 +1,68 @@
+HEAP=[183, if(precision(1.)==38, 5763, 8929)];
+\\ A tres grande precision
+\p 2000
+\e
+abs(-0.01)
+agm(1,2)
+agm(1+O(7^5),8+O(7^5))
+4*arg(3+3*I)
+bernreal(12)
+bernvec(6)
+eta(q)
+gammah(10)
+Pi
+precision(Pi,38)
+sqr(1+O(2))
+sqrt(13+O(127^12))
+teichmuller(7+O(127^12))
+\\ A grande precision
+\p 500
+Catalan
+Euler
+acos(0.5)
+acosh(3)
+3*asin(sqrt(3)/2)
+asinh(0.5)
+3*atan(sqrt(3))
+atanh(0.5)
+besseljh(1,1)
+cos(1)
+cosh(1)
+exp(1)
+exp(1.123)
+incgam(4,1,6)
+incgamc(2,1)
+log(2)
+sin(Pi/6)
+sinh(1)
+sqr(tan(Pi/3))
+tanh(1)
+thetanullk(0.5,7)
+\\ A moyenne precision
+\p 210
+dilog(0.5)
+eint1(2)
+lngamma(10^50*I)
+polylog(5,0.5)
+polylog(-4,t)
+polylog(5,0.5,1)
+polylog(5,0.5,2)
+polylog(5,0.5,3)
+psi(1)
+round(prod(k=1,17,x-exp(2*I*Pi*k/17)), &e)
+e
+theta(0.5,3)
+weber(I)
+weber(I,1)
+weber(I,2)
+zeta(3)
+\\ A faible precision
+\p 38
+besselk(1+I,1)
+erfc(2)
+gamma(10.5)
+hyperu(1,1,1)
+incgam(2,1)
+zeta(0.5+14.1347251*I)
+\\
+if (getheap() != HEAP, getheap())
diff --git a/src/test/in/trans2 b/src/test/in/trans2
new file mode 100644
index 0000000..e08429f
--- /dev/null
+++ b/src/test/in/trans2
@@ -0,0 +1,53 @@
+OVERFLOW_EXPONENT=if(precision(1.)==38, 2^50, 2^20);
+default(realprecision,38);
+default(parisize,20*10^6);
+exp(quadgen(5))
+exp(Mod(x,x^2+1))
+iferr(exp(""),E,E)
+N=2^64;
+O(2^0)^N
+iferr(O(2^0)^-N,E,E)
+0^N
+Pol(1)^N
+(8+O(x^2))^(1/3)
+(8+O(x^2))^(1/2)
+sqrt(Mod(1,y)*(1+x+O(x^2)))
+sqrt(4+x+O(x^2))
+iferr(O(x)^0.5,E,E)
+O(1)^0.5
+(1+x+O(x^2))^0.5
+iferr(O(x)^((2^65)/3),E,E)
+iferr(""^0,E,E)
+iferr(0^Mod(1,3),E,E)
+N=2^16; y=x^N;
+y^3
+iferr(y^OVERFLOW_EXPONENT,E,E)
+iferr(0.^(2.^64),E,E)
+agm(1,1)
+agm(-1,1)
+agm(1+x+O(x^10),1)
+agm(1,[1,2])
+log(4+O(3^4))
+
+obj=[Mod(1,3), O(3^5), 3 + O(3^5),I];
+test(f,p)=print(iferr(f(p),E,E));
+[test(f,p) | f<-[exp,log,cos,sin,tan,cotan];p<-obj];
+tan(1+I)
+cotan(2)
+asin(1+O(x^3))
+asin(-1+O(x^3))
+asinh(I+O(x^3))
+asinh(-I+O(x^3))
+acosh(1+O(x^3))
+acosh(1+x^2+O(x^3))
+acosh(2+x+O(x^3))
+acos(1+O(x^3))
+sinh(I)
+w=Pi/4;
+test(z)= my(a=expm1(z),b=exp(z)-1); printf("%.1e\n", abs(a-b)/abs(a));
+for (i=0,7, z=1e-20+i*w; test(z))
+for (i=0,7, z=1e-20+i*w+I; test(z))
+erfc(1/2+I)
+erfc(-1/2+I)
+default(realprecision,2003);
+log(exp(1.5)) - 1.5
diff --git a/src/test/in/valuation b/src/test/in/valuation
new file mode 100644
index 0000000..010c0f9
--- /dev/null
+++ b/src/test/in/valuation
@@ -0,0 +1,26 @@
+default(realprecision,38);
+a = [0, 1, 1/3, 1.0, Mod(1,2), Mod(1,3), ffgen(Mod(1,3)*(x^2+1)), 2*I, 3 + O(3^2), Mod(x,x^2+1), 2*x + 2, 3*(x +O(x^2)) ];
+b = [2, 3, x, x^2+1 ];
+oo = valuation(0, 2);
+{
+for (i = 1, #a,
+  for(j = 1, #b,
+    v = iferr(valuation(a[i],b[j]), E, "ERROR");
+    if (v == oo, v = "oo");
+    print1(v, " ")
+  );
+  print()
+)
+}
+valuation(0,1)
+valuation(0,-1)
+valuation(0,0)
+
+\\ #1319
+s=Mod(1,3)*(1+x); valuation((s+O(x^2)) - s,x)
+\\ #1336
+1./(x+1)+O(x^2)
+\\ #1345
+valuation(0*x,3) == oo
+
+1+O(x)-1
diff --git a/src/test/in/variable b/src/test/in/variable
new file mode 100644
index 0000000..f053d13
--- /dev/null
+++ b/src/test/in/variable
@@ -0,0 +1,7 @@
+v=[1,O(3),x,O(x),Mod(y,y^2+1),1/y,[y,z],Mat(z),List(),List([y,z])];
+{
+for(i=1,#v,
+  print(iferr(variable(v[i]), E,E))
+)
+}
+variable()
diff --git a/src/test/in/whatnow b/src/test/in/whatnow
new file mode 100644
index 0000000..baadc5e
--- /dev/null
+++ b/src/test/in/whatnow
@@ -0,0 +1,5 @@
+whatnow(changevar)
+whatnow(char)
+whatnow(allocatemem)
+whatnow(proveGRH)
+whatnow(compimag)
diff --git a/src/test/in/zetak b/src/test/in/zetak
new file mode 100644
index 0000000..a70e7f3
--- /dev/null
+++ b/src/test/in/zetak
@@ -0,0 +1,28 @@
+default(realprecision,38);
+do(p) = {
+  my(v = [-3,3,2,0,1/2+I]);
+  my(znf = zetakinit(p));
+  for(i=1,#v, print(zetak(znf,v[i])));
+}
+
+allocatemem(20*10^6);
+do(y^2-3);
+do(y^2+3);
+do(y^3-4*y+2);
+do(y^3-2);
+do(y^5-3*y^2+y+2);
+zeta(3+O(5^10))
+zeta(1 + I/100)
+zeta(1000.5)
+zeta(1000)
+zeta(100)
+zeta(31)
+zeta(100+100*I)
+zeta(60+I)
+zeta(-1000+I)
+zeta(2+O(2^10))
+zeta(2^64)
+zeta(-2^64)
+iferr(zeta(-1-2^64),E,E)
+zeta(2+1e-101*I)
+zeta(1.01)
diff --git a/src/test/in/zn b/src/test/in/zn
new file mode 100644
index 0000000..001b1e9
--- /dev/null
+++ b/src/test/in/zn
@@ -0,0 +1,50 @@
+znprimroot(2)
+znprimroot(4)
+znprimroot(8)
+p=9223372036854775837;
+znprimroot(p)
+znprimroot(p^2)
+znprimroot(2*p^2)
+znprimroot([2,1;p,2])
+znprimroot([2*p^2,[2,1;p,2]])
+znstar(0)
+znstar(1)
+znstar(10)
+znstar(14)
+znstar(247)
+znstar(-247)
+znstar(factor(-247))
+znstar(factor(0))
+Mod(10,148)^(2^64)
+do(n)= {
+  g = Mod(3,n); phi = eulerphi(n); G = g^(phi-1);
+  o = znorder(g, phi); fo = factor(o);
+  [znlog(G,g), znlog(G,g,o), znlog(G,g,fo), znlog(G,g,[o,fo])];
+}
+do(2^5*5^3*7)
+do(2^3*5^2*nextprime(10^10)^2)
+
+znlog(7,Mod(3,8),znorder(Mod(3,8)))
+znlog(7,Mod(3,8))
+znlog(0,Mod(3,4))
+znlog(6,Mod(2,7),znorder(Mod(2,7)))
+znlog(3,Mod(3,8),znorder(Mod(3,8)))
+znlog(5,Mod(2,401))
+
+znlog(7,3+O(2^3))
+znlog(7,3+O(2^3), znorder(Mod(3,8)))
+znlog(7,3+O(7^3))
+znlog(2,3+O(7^3))
+
+{
+for (i = 1,10^4,
+  d = eulerphi(i);
+  if (!istotient(d, &n) || eulerphi(n) != d, error(i)))
+}
+Mod(1,2)-Mod(2,4)
+
+p=2^80+13; znlog(Mod(3,p),Mod(2,p))
+
+g=Mod(3,11);
+v=[I,-1,Mat(1),matid(2)/2,Mat([5,1])];
+a=Mod(-1,11); for(i=1,#v, print(iferr(znlog(a,g,v[i]),E,E)));
diff --git a/src/test/in/zncoppersmith b/src/test/in/zncoppersmith
new file mode 100644
index 0000000..7814baf
--- /dev/null
+++ b/src/test/in/zncoppersmith
@@ -0,0 +1,15 @@
+p = 10^30+57;
+q = 10^31+33; N = p*q;
+p0 = p % 10^20;
+z = zncoppersmith(10^19*x + p0, N, 10^12, 10^29)
+gcd(z[1]*10^19 + p0, N) == p
+
+setrand(1);
+P = 4625048078322670354774415943228839104529734663852281547523640;
+e = 3;
+X = floor(N^0.3);
+x0 = 1339991002000615200;
+C = lift( (Mod(x0,N) + P)^e );
+z = zncoppersmith((P+x)^3 - C, N, X)
+z[1] == x0
+zncoppersmith(Pol([192378,19237198,912831923,12938719]), 2*3*5*7, 1000.5, 29.5)
diff --git a/src/test/kerntest.c b/src/test/kerntest.c
new file mode 100644
index 0000000..bcd7889
--- /dev/null
+++ b/src/test/kerntest.c
@@ -0,0 +1,82 @@
+#include "pari.h"
+
+GEN   gen_0, gen_1, gen_m1, gen_2, gen_m2;
+THREAD pari_sp top, bot, avma;
+THREAD size_t memused = 0;
+ulong  DEBUGLEVEL,DEBUGMEM = 0;
+const double LOG10_2 = 0.;
+const long lontyp[] = {0};
+THREAD VOLATILE int PARI_SIGINT_block, PARI_SIGINT_pending;
+
+void mt_sigint_block(void) { }
+void mt_sigint_unblock(void) { }
+
+void specinit()
+{
+  long size = 100000L;
+  bot = (pari_sp)malloc(size);
+  top = avma = bot + size;
+  gen_0 = cgeti(2); affui(0, gen_0);
+  gen_1 = utoipos(1);
+  gen_m1= utoineg(1);
+  gen_2 = utoipos(2);
+  gen_m2= utoineg(2);
+}
+
+void sorstring(ulong x)
+{
+#ifdef LONG_IS_64BIT
+  printf("%016lx  ", x);
+#else
+  printf("%08lx  ", x);
+#endif
+}
+
+void _voiri(GEN x)
+{
+  long i, lx = lgefint(x);
+  GEN y = int_MSW(x);
+  /* sorstring(x[0]); depends on the kernel and contains no useful info */
+  sorstring(x[1]);
+  for (i=2; i < lx; i++, y = int_precW(y)) sorstring(*y);
+  printf("\n");
+}
+void _voirr(GEN x)
+{
+  long i, lx = lg(x);
+  for (i=1; i < lx; i++) sorstring(x[i]);
+  printf("\n");
+}
+
+int main()
+{
+  GEN x,y,r,z, xr,yr;
+  specinit();
+  x = utoipos(187654321UL);
+  y = utoineg(12345678UL);
+  printf("INT: %ld\n", itos(x));
+  printf("conv:"); _voiri(x);
+  printf("+:"); _voiri(addii(x,y));
+  printf("-:"); _voiri(subii(x,y));
+  printf("*:"); _voiri(mulii(x,y));
+  printf("/:"); _voiri(dvmdii(x,y, &z));
+  printf("rem:"); _voiri(z);
+  printf("pow:\n");
+  z = mulii(x,x); _voiri(z);
+  z = mulii(z,z); _voiri(z);
+  z = mulii(z,z); _voiri(z);
+  z = mulii(z,z); _voiri(z);
+  z = mulii(z,z); _voiri(z);
+  printf("invmod:"); invmod(y,z,&r); _voiri(r);
+  xr = itor(x, DEFAULTPREC);
+  yr = itor(y, DEFAULTPREC);
+  printf("\nREAL: %f\n", rtodbl(xr));
+  printf("conv1:"); _voirr(xr);
+  printf("conv2:"); _voirr(dbltor(rtodbl(xr)));
+  printf("+:"); _voirr(addrr(xr,yr));
+  printf("-:"); _voirr(subrr(xr,yr));
+  printf("*:"); _voirr(mulrr(xr,yr));
+  printf("/:"); _voirr(divrr(xr,yr));
+  printf("gcc bug?:"); _voirr(divru(dbltor(3.),2));
+  return 0;
+}
diff --git a/src/test/tune.c b/src/test/tune.c
new file mode 100644
index 0000000..e4c15c9
--- /dev/null
+++ b/src/test/tune.c
@@ -0,0 +1,637 @@
+/* Copyright (C) 2001  The PARI group.
+
+This file is part of the PARI/GP package.
+
+PARI/GP 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. It is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY WHATSOEVER.
+
+Check the License for details. You should have received a copy of it, along
+with the package; see the file 'COPYING'. If not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+/* This file is a quick hack adapted from gmp-4.0 tuning utilities
+ * (T. Granlund et al.)
+ *
+ * (GMU MP Library is Copyright Free Software Foundation, Inc.) */
+#define PARI_TUNE
+#include <pari.h>
+#include <paripriv.h>
+
+#define numberof(x) (sizeof(x) / sizeof((x)[0]))
+
+int option_trace = 0;
+double Step_Factor = .01; /* small steps by default */
+ulong DFLT_mod1, DFLT_hmod, DFLT_mod2;
+GEN LARGE_mod;
+
+#ifdef LONG_IS_64BIT
+#  define DFLT_mod DFLT_mod1
+#  define Fmod_MUL_MULII_LIMIT Flx_MUL_MULII_LIMIT
+#  define Fmod_SQR_SQRI_LIMIT  Flx_SQR_SQRI_LIMIT
+#  else
+#  define DFLT_mod DFLT_mod2
+#  define Fmod_MUL_MULII_LIMIT Flx_MUL_MULII2_LIMIT
+#  define Fmod_SQR_SQRI_LIMIT  Flx_SQR_SQRI2_LIMIT
+#endif
+
+typedef struct {
+  ulong reps, type;
+  long *var, *var_disable, size, enabled;
+  GEN x, y;
+  ulong l;
+  GEN p;
+} speed_param;
+
+typedef double (*speed_function_t)(speed_param *s);
+
+typedef struct {
+  int               kernel;
+  const char        *name;
+  long              *var;
+  int               type; /* t_INT or t_REAL */
+  long              min_size;
+  long              max_size;
+  speed_function_t  fun;
+  double            step_factor; /* how much to step sizes (rounded down) */
+  double            stop_factor;
+  long              *var_disable;
+} tune_param;
+
+/* ========================================================== */
+/* To use GMP cycle counting functions, look for GMP in Oxxx/Makefile */
+#ifdef GMP_TIMER
+/* needed to link with gmp-4.0/tune/{time,freq}.o */
+int speed_option_verbose = 0;
+extern double speed_unittime;
+extern int    speed_precision;
+void speed_starttime(void);
+double speed_endtime(void);
+#else
+static pari_timer __T;
+static double speed_unittime = 1e-4;
+static int    speed_precision= 1000;
+static void speed_starttime() { timer_start(&__T); }
+static double speed_endtime() { return (double)timer_delay(&__T)/1000.; }
+#endif
+
+/* ========================================================== */
+/* int, n words, odd */
+static GEN
+rand_INT(long n)
+{
+  pari_sp av = avma;
+  GEN x, N = int2n(n*BITS_IN_LONG);
+  do x = randomi(N); while (lgefint(x) != n+2);
+  if (!mpodd(x)) x = addis(x,1); /*For Barrett REDC */
+  return gerepileuptoint(av, x);
+}
+/* real, n words */
+static GEN
+rand_REAL(long n) { return gmul2n(itor(rand_INT(n), n+2),-BITS_IN_LONG*n); }
+
+static GEN
+rand_FpX(long n)
+{
+  GEN x;
+  do x = random_FpX(n+1, 0, LARGE_mod); while (degpol(x) < n);
+  return x;
+}
+/* Flx, degree n */
+static GEN
+rand_Flx(long n, ulong l)
+{
+  GEN x;
+  do x = random_Flx(n+1, 0, l); while (degpol(x) < n);
+  return x;
+}
+
+/* normalized Fpx, degree n */
+static GEN
+rand_NFpX(long n)
+{
+  pari_sp av = avma;
+  GEN x = gadd(monomial(gen_1,n,0), random_FpX(n, 0, LARGE_mod));
+  return gerepileupto(av, x);
+}
+
+/* normalized Flx, degree n */
+static GEN
+rand_NFlx(long n, ulong l)
+{
+  pari_sp av = avma;
+  GEN x = Flx_add(Flx_shift(pol1_Flx(0),n), random_Flx(n, 0, l), l);
+  return gerepileuptoleaf(av, x);
+}
+
+#define t_Fhx   99
+#define t_Flx  100
+#define t_Fl1x 101
+#define t_Fl2x 102
+#define t_NFlx 103
+#define t_FpX  104
+#define t_NFpX 105
+
+static GEN
+rand_g(long n, long type)
+{
+  switch (type) {
+    case t_INT:  return rand_INT(n);
+    case t_REAL: return rand_REAL(n);
+    case t_Fhx:  return rand_Flx(n,DFLT_hmod);
+    case t_Flx:  return rand_Flx(n,DFLT_mod);
+    case t_Fl1x: return rand_Flx(n,DFLT_mod1);
+    case t_Fl2x: return rand_Flx(n,DFLT_mod2);
+    case t_NFlx: return rand_NFlx(n,DFLT_mod);
+    case t_FpX:  return rand_FpX(n);
+    case t_NFpX: return rand_NFpX(n);
+  }
+  return NULL;
+}
+
+static void
+dftmod(speed_param *s)
+{
+  switch (s->type) {
+    case t_Fhx:  s->l=DFLT_hmod; return;
+    case t_Flx:  s->l=DFLT_mod;  return;
+    case t_Fl1x: s->l=DFLT_mod1; return;
+    case t_Fl2x: s->l=DFLT_mod2; return;
+    case t_NFlx: s->l=DFLT_mod;  return;
+    case t_FpX:  s->p=LARGE_mod; return;
+    case t_NFpX: s->p=LARGE_mod; return;
+  }
+}
+
+/* ========================================================== */
+#define TIME_FUN(call) {\
+  {                                      \
+    pari_sp av = avma;                   \
+    int i;                               \
+    speed_starttime();                   \
+    i = (s)->reps;                       \
+    do { call; avma = av; } while (--i); \
+  }                                      \
+  return speed_endtime();                \
+}
+
+#define  m_enable(s,var) (*(s->var)=lg(s->x)-2)/* enable  asymptotically fastest */
+#define m_disable(s,var) (*(s->var)=lg(s->x)+1)/* disable asymptotically fastest */
+
+static void enable(speed_param *s)
+{
+  m_enable(s,var); s->enabled = 1;
+  if (s->var_disable) m_disable(s,var_disable);
+}
+
+static void disable(speed_param *s)
+{
+  m_disable(s,var); s->enabled = 0;
+  if (s->var_disable) m_disable(s,var_disable);
+}
+
+static double speed_mulrr(speed_param *s)
+{ TIME_FUN(mulrr(s->x, s->y)); }
+
+static double speed_mulii(speed_param *s)
+{ TIME_FUN(mulii(s->x, s->y)); }
+
+static double speed_exp(speed_param *s)
+{ TIME_FUN(mpexp(s->x)); }
+
+static double speed_inv(speed_param *s)
+{ TIME_FUN(invr(s->x)); }
+
+static double speed_log(speed_param *s)
+{ TIME_FUN(mplog(s->x)); }
+
+static double speed_logcx(speed_param *s)
+{ GEN z; setexpo(s->x,0); z = mkcomplex(gen_1, s->x);
+  glog(z,s->size);
+  TIME_FUN(glog(z,s->size)); }
+
+static double speed_atan(speed_param *s)
+{ setexpo(s->x, 0);
+  gatan(s->x, 0);
+  TIME_FUN(gatan(s->x, 0)); }
+
+static double speed_sqri (speed_param *s)
+{ TIME_FUN(sqri(s->x)); }
+
+static double speed_Fp_pow(speed_param *s)
+{ TIME_FUN( Fp_pow(s->x, subis(s->y,1), s->y)); }
+
+static double speed_divrr(speed_param *s)
+{ TIME_FUN(divrr(s->x, s->y)); }
+
+static double speed_invmod(speed_param *s)
+{ GEN T; TIME_FUN(invmod(s->x, s->y, &T)); }
+
+static double speed_Flx_sqr(speed_param *s)
+{ TIME_FUN(Flx_sqr(s->x, s->l)); }
+
+static double speed_Flx_inv(speed_param *s)
+{ TIME_FUN(Flx_invBarrett(s->x, s->l)); }
+
+static double speed_Flx_mul(speed_param *s)
+{ TIME_FUN(Flx_mul(s->x, s->y, s->l)); }
+
+static double speed_Flx_divrem(speed_param *s)
+{
+  GEN r, x = rand_NFlx((degpol(s->x)-1)*2, s->l);
+  TIME_FUN(Flx_divrem(x, s->x, s->l, &r));
+}
+
+static double speed_Flx_rem(speed_param *s) {
+  GEN x = rand_NFlx((degpol(s->x)-1)*2, s->l);
+  TIME_FUN(Flx_rem(x, s->x, s->l));
+}
+
+static double speed_Flxq_red(speed_param *s) {
+  GEN x = rand_NFlx((degpol(s->x)-1)*2, s->l);
+  GEN q = Flx_get_red(s->x, s->l);
+  TIME_FUN(Flx_rem(x, q, s->l));
+}
+
+static double speed_Flx_halfgcd(speed_param *s)
+{ TIME_FUN(Flx_halfgcd(s->x, s->y, s->l)); }
+
+static double speed_Flx_gcd(speed_param *s)
+{ TIME_FUN(Flx_gcd(s->x, s->y, s->l)); }
+
+static double speed_Flx_extgcd(speed_param *s)
+{ GEN u,v; TIME_FUN(Flx_extgcd(s->x, s->y, s->l, &u, &v)); }
+
+static double speed_FpX_inv(speed_param *s)
+{ TIME_FUN(FpX_invBarrett(s->x, s->p)); }
+
+static double speed_FpX_divrem(speed_param *s)
+{
+  GEN r, x = rand_NFpX((degpol(s->x)-1)*2);
+  TIME_FUN(FpX_divrem(x, s->x, s->p, &r));
+}
+
+static double speed_FpX_rem(speed_param *s)
+{
+  GEN x = rand_NFpX((degpol(s->x)-1)*2);
+  TIME_FUN(FpX_rem(x, s->x, s->p));
+}
+
+static double speed_FpXQ_red(speed_param *s) {
+  GEN x = rand_NFpX((degpol(s->x)-1)*2);
+  GEN q = FpX_get_red(s->x, s->p);
+  TIME_FUN(FpX_rem(x, q, s->p));
+}
+
+static double speed_FpX_halfgcd(speed_param *s)
+{ TIME_FUN(FpX_halfgcd(s->x, s->y, s->p)); }
+static double speed_FpX_gcd(speed_param *s)
+{ TIME_FUN(FpX_gcd(s->x, s->y, s->p)); }
+static double speed_FpX_extgcd(speed_param *s)
+{ GEN u,v; TIME_FUN(FpX_extgcd(s->x, s->y, s->p, &u, &v)); }
+
+/* small coeffs: earlier thresholds for more complicated rings */
+static double speed_RgX_sqr(speed_param *s)
+{ TIME_FUN(RgX_sqr(s->x)); }
+static double speed_RgX_mul(speed_param *s)
+{ TIME_FUN(RgX_mul(s->x, s->y)); }
+
+enum { PARI = 1, GMP = 2 };
+#ifdef PARI_KERNEL_GMP
+#  define AVOID PARI
+#else
+#  define AVOID GMP
+#endif
+
+/* Thresholds are set in this order. If f() depends on g(), g() should
+ * occur first */
+#define var(a) # a, &a
+static tune_param param[] = {
+{PARI,var(MULII_KARATSUBA_LIMIT),  t_INT, 4,0, speed_mulii,0,0,&MULII_FFT_LIMIT},
+{PARI,var(SQRI_KARATSUBA_LIMIT),   t_INT, 4,0, speed_sqri,0,0,&SQRI_FFT_LIMIT},
+{PARI,var(MULII_FFT_LIMIT),        t_INT, 500,0, speed_mulii,0.02},
+{PARI,var(SQRI_FFT_LIMIT),         t_INT, 500,0, speed_sqri,0.02},
+{0,   var(MULRR_MULII_LIMIT),      t_REAL,4,0, speed_mulrr},
+{0,   var(Fp_POW_REDC_LIMIT),      t_INT, 3,100, speed_Fp_pow,0,0,&Fp_POW_BARRETT_LIMIT},
+{0,   var(Fp_POW_BARRETT_LIMIT),   t_INT, 3,0, speed_Fp_pow},
+{0,   var(INVNEWTON_LIMIT),        t_REAL,66,0, speed_inv,0.03},
+{GMP, var(DIVRR_GMP_LIMIT),        t_REAL,4,0, speed_divrr},
+{0,   var(EXPNEWTON_LIMIT),        t_REAL,66,0, speed_exp},
+{0,   var(LOGAGM_LIMIT),           t_REAL,4,0, speed_log},
+{0,   var(LOGAGMCX_LIMIT),         t_REAL,3,0, speed_logcx,0.05},
+{0,   var(AGM_ATAN_LIMIT),         t_REAL,20,0, speed_atan,0.05},
+{GMP, var(INVMOD_GMP_LIMIT),       t_INT, 3,0, speed_invmod},
+{0,   var(Flx_MUL_KARATSUBA_LIMIT),t_Flx,5,0, speed_Flx_mul,0,0,&Fmod_MUL_MULII_LIMIT},
+{0,   var(Flx_SQR_KARATSUBA_LIMIT),t_Flx,5,0, speed_Flx_sqr,0,0,&Fmod_SQR_SQRI_LIMIT},
+{0,   var(Flx_MUL_HALFMULII_LIMIT),t_Fhx,3,0, speed_Flx_mul},
+{0,   var(Flx_SQR_HALFSQRI_LIMIT), t_Fhx,3,0, speed_Flx_sqr},
+{0,   var(Flx_MUL_MULII_LIMIT),    t_Fl1x,5,0, speed_Flx_mul},
+{0,   var(Flx_SQR_SQRI_LIMIT),     t_Fl1x,5,0, speed_Flx_sqr},
+{0,   var(Flx_MUL_MULII2_LIMIT),   t_Fl2x,5,20000, speed_Flx_mul,0.05},
+{0,   var(Flx_SQR_SQRI2_LIMIT),    t_Fl2x,5,20000, speed_Flx_sqr,0.05},
+{0,   var(Flx_INVBARRETT_LIMIT),  t_NFlx,10,0, speed_Flx_inv,0.05},
+{0,  var(Flx_DIVREM_BARRETT_LIMIT),t_NFlx,10,0, speed_Flx_divrem,0.05},
+{0,  var(Flx_REM_BARRETT_LIMIT),  t_NFlx,10,0, speed_Flx_rem,0.05},
+{0,  var(Flx_BARRETT_LIMIT),      t_NFlx,10,0, speed_Flxq_red},
+{0,  var(Flx_HALFGCD_LIMIT),       t_Flx,10,0, speed_Flx_halfgcd},
+{0,  var(Flx_GCD_LIMIT),           t_Flx,10,0, speed_Flx_gcd,0.1},
+{0,  var(Flx_EXTGCD_LIMIT),        t_Flx,10,0, speed_Flx_extgcd},
+{0,  var(FpX_INVBARRETT_LIMIT),   t_NFpX,10,0, speed_FpX_inv,0.05},
+{0,  var(FpX_DIVREM_BARRETT_LIMIT),t_NFpX,10,0, speed_FpX_divrem,0.05},
+{0,  var(FpX_REM_BARRETT_LIMIT),  t_NFpX,10,0, speed_FpX_rem,0.05},
+{0,  var(FpX_BARRETT_LIMIT),      t_NFpX,10,0, speed_FpXQ_red},
+{0,  var(FpX_HALFGCD_LIMIT),       t_FpX,10,0, speed_FpX_halfgcd},
+{0,  var(FpX_GCD_LIMIT),           t_FpX,10,0, speed_FpX_gcd,0.1},
+{0,  var(FpX_EXTGCD_LIMIT),        t_FpX,10,0, speed_FpX_extgcd},
+{0,  var(RgX_MUL_LIMIT),           t_FpX, 4,0, speed_RgX_mul},
+{0,  var(RgX_SQR_LIMIT),           t_FpX, 4,0, speed_RgX_sqr},
+};
+
+/* ========================================================== */
+int ndat = 0, allocdat = 0;
+struct dat_t {
+  long size;
+  double d;
+} *dat = NULL;
+
+int
+double_cmp_ptr(double *x, double *y) { return (int)(*x - *y); }
+
+double
+time_fun(speed_function_t fun, speed_param *s, long enabled)
+{
+  const double TOLERANCE = 1.005; /* 0.5% */
+  pari_sp av = avma;
+  double t[30];
+  ulong i, j, e;
+
+  s->reps = 1;
+  if (enabled) enable(s); else disable(s);
+  for (i = 0; i < numberof(t); i++)
+  {
+    for (;;)
+    {
+      double reps_d;
+      t[i] = fun(s);
+      if (!t[i]) { s->reps *= 10; continue; }
+      if (t[i] >= speed_unittime * speed_precision) break;
+
+      /* go to a value of reps to make t[i] >= precision */
+      reps_d = ceil (1.1 * s->reps
+                     * speed_unittime * speed_precision
+                     / maxdd(t[i], speed_unittime));
+      if (reps_d > 2e9 || reps_d < 1.0)
+        pari_err(e_MISC, "Fatal error: new reps bad: %.2f", reps_d);
+
+      s->reps = (ulong)reps_d;
+    }
+    t[i] /= s->reps;
+
+    /* require 3 values within TOLERANCE when >= 2 secs, 4 when below */
+    e = (t[0] >= 2.0)? 3: 4;
+
+   /* Look for e many t[]'s within TOLERANCE of each other to consider a
+      valid measurement.  Return smallest among them.  */
+    if (i >= e)
+    {
+      qsort (t, i+1, sizeof(t[0]), (QSCOMP)double_cmp_ptr);
+      for (j = e-1; j < i; j++)
+        if (t[j] <= t[j-e+1] * TOLERANCE) { avma = av; return t[j-e+1]; }
+    }
+  }
+  pari_err(e_MISC,"couldn't measure time");
+  return -1.0; /* not reached */
+}
+
+void
+add_dat(long size, double d)
+{
+  if (ndat == allocdat)
+  {
+    allocdat += maxss(allocdat, 100);
+    dat = (struct dat_t*) pari_realloc((void*)dat, allocdat * sizeof(dat[0]));
+  }
+  dat[ndat].size = size;
+  dat[ndat].d    = d; ndat++;
+}
+
+void
+diag(const char *format, ...)
+{
+  va_list ap;
+  va_start(ap, format);
+  vfprintf(stderr, format, ap);
+}
+void
+print_define(const char *name, long value)
+{ printf("#define __%-25s  %5ld\n", name, value); }
+
+long
+analyze_dat(int final)
+{
+  double  x, min_x;
+  int     j, min_j;
+
+  /* If the threshold is set at dat[0].size, any positive values are bad. */
+  x = 0.0;
+  for (j = 0; j < ndat; j++)
+    if (dat[j].d > 0.0) x += dat[j].d;
+
+  if (final && option_trace >= 3)
+  {
+    diag("\n");
+    diag("x is the sum of the badness from setting thresh at given size\n");
+    diag("  (minimum x is sought)\n");
+    diag("size=%ld  first x=%.4f\n", dat[j].size, x);
+  }
+
+  min_x = x;
+  min_j = 0;
+
+  /* When stepping to the next dat[j].size, positive values are no longer
+     bad (so subtracted), negative values become bad (so add the absolute
+     value, meaning subtract). */
+  for (j = 0; j < ndat; j++)
+  {
+    if (final && option_trace >= 3)
+      diag ("size=%ld  x=%.4f\n", dat[j].size, x);
+
+    if (x < min_x) { min_x = x; min_j = j; }
+    x -= dat[j].d;
+  }
+  return min_j;
+}
+
+void
+Test(tune_param *param)
+{
+  int since_positive, since_change, thresh, new_thresh;
+  speed_param s;
+  long save_var_disable = -1;
+  pari_timer T;
+
+  if (param->kernel == AVOID) { print_define(param->name, -1); return; }
+
+#define DEFAULT(x,n)  if (! (param->x))  param->x = (n);
+  DEFAULT(step_factor, Step_Factor);
+  DEFAULT(stop_factor, 1.2);
+  DEFAULT(max_size, 10000);
+  if (param->var_disable) save_var_disable = *(param->var_disable);
+
+  s.type = param->type;
+  s.size = param->min_size;
+  s.var  = param->var;
+  s.var_disable  = param->var_disable;
+  dftmod(&s);
+  ndat = since_positive = since_change = thresh = 0;
+  if (option_trace >= 1)
+  {
+    timer_start(&T);
+    diag("\nSetting %s... (default %ld)\n", param->name, *(param->var));
+  }
+  if (option_trace >= 2)
+  {
+    diag("              algorithm-A  algorithm-B   ratio  possible\n");
+    diag("               (seconds)    (seconds)    diff    thresh\n");
+  }
+
+  for(;;)
+  {
+    pari_sp av=avma;
+    double t1, t2, d;
+    s.x = rand_g(s.size, s.type);
+    s.y = rand_g(s.size, s.type);
+    t1 = time_fun(param->fun, &s, 0);
+    t2 = time_fun(param->fun, &s, 1);
+    avma = av;
+    if (t2 >= t1) d = (t2 - t1) / t2;
+    else          d = (t2 - t1) / t1;
+
+    add_dat(s.size, d);
+    new_thresh = analyze_dat(0);
+
+    if (option_trace >= 2)
+      diag ("size =%4ld     %.8f   %.8f  % .4f %c  %ld\n",
+            s.size, t1,t2, d, d < 0? '#': ' ', dat[new_thresh].size);
+
+#define SINCE_POSITIVE 20
+#define SINCE_CHANGE 50
+    /* Stop if method B has been consistently faster for a while */
+    if (d >= 0)
+      since_positive = 0;
+    else
+      if (++since_positive > SINCE_POSITIVE)
+      {
+        if (option_trace >= 1)
+          diag("Stop: since_positive (%d)\n", SINCE_POSITIVE);
+        break;
+      }
+    /* Stop if method A has become slower by a certain factor */
+    if (t1 >= t2 * param->stop_factor)
+    {
+      if (option_trace >= 1)
+        diag("Stop: t1 >= t2 * factor (%.1f)\n", param->stop_factor);
+      break;
+    }
+    /* Stop if threshold implied hasn't changed for a while */
+    if (thresh != new_thresh)
+      since_change = 0, thresh = new_thresh;
+    else
+      if (++since_change > SINCE_CHANGE)
+      {
+        if (option_trace >= 1)
+          diag("Stop: since_change (%d)\n", SINCE_CHANGE);
+        break;
+      }
+    s.size += maxss((long)floor(s.size * param->step_factor), 1);
+    if (s.size >= param->max_size)
+    {
+      if (option_trace >= 1)
+        diag("Stop: max_size (%ld). Disable Algorithm B?\n",param->max_size);
+      break;
+    }
+  }
+  thresh = dat[analyze_dat(1)].size;
+  if (option_trace >= 1)
+    diag("Total time: %gs\n", (double)timer_delay(&T)/1000.);
+  print_define(param->name, thresh);
+  *(param->var) = thresh; /* set to optimal value for next tests */
+  if (param->var_disable) *(param->var_disable) = save_var_disable;
+}
+
+void error(char **argv) {
+  long i;
+  diag("This is the PARI/GP tuning utility. Usage: tune [OPTION] var1 var2...\n");
+  diag("Options:\n");
+  diag("  -t:     verbose output\n");
+  diag("  -tt:    very verbose output\n");
+  diag("  -ttt:   output everything\n");
+  diag("  -s xxx: set step factor between successive sizes to xxx (default 0.01)\n");
+  diag("  -p xxx: set Flx modulus to xxx (default 27449)\n");
+  diag("  -u xxx: set speed_unittime to xxx (default 1e-4s)\n");
+  diag("Tunable variables (omitting variable indices tunes everybody):\n");
+  for (i = 0; i < (long)numberof(param); i++)
+    diag("  %2ld: %-25s (default %4ld)\n", i, param[i].name, *(param[i].var));
+  exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+  int i, r, n = 0;
+  GEN v;
+  pari_init(8000000, 2);
+  DFLT_mod = 27449;
+  LARGE_mod=subis(powuu(3,128),62);
+#ifdef LONG_IS_64BIT
+  DFLT_hmod = 257;
+  DFLT_mod2 = 281474976710677UL;
+#else
+  DFLT_hmod = 17;
+  DFLT_mod1 = 1031UL;
+#endif
+  v = new_chunk(argc);
+  for (i = 1; i < argc; i++)
+  {
+    char *s = argv[i];
+    if (*s == '-') {
+      switch(*++s) {
+        case 't': option_trace++;
+          while (*++s == 't') option_trace++;
+          break;
+
+        case 'p':
+          if (!*++s)
+          {
+            if (++i == argc) error(argv);
+            s = argv[i];
+          }
+          DFLT_mod = itou(gp_read_str(s)); break;
+        case 's':
+          if (!*++s)
+          {
+            if (++i == argc) error(argv);
+            s = argv[i];
+          }
+          Step_Factor = atof(s); break;
+        case 'u': s++;
+          if (!*++s)
+          {
+            if (++i == argc) error(argv);
+            s = argv[i];
+          }
+          speed_unittime = atof(s); break;
+        default: error(argv);
+      }
+    } else {
+      if (!isdigit((int)*s)) error(argv);
+      r = atol(s); if (r >= (long)numberof(param) || r < 0) error(argv);
+      v[n++] = r;
+    }
+  }
+  if (n) { for (i = 0; i < n; i++) Test(&param[ v[i] ]); return 0; }
+  n = numberof(param);
+  for (i = 0; i < n; i++) Test(&param[i]);
+  return 0;
+}
diff --git a/src/whatnow b/src/whatnow
new file mode 100644
index 0000000..f6f3b28
--- /dev/null
+++ b/src/whatnow
@@ -0,0 +1,653 @@
+!_
+#_
+%
++_
+-_
+O
+O(_^_)
+_!
+_!=_
+_%=_
+_%_
+_&&_
+_'
+_*=_
+_*_
+_++
+_+=_
+_+_
+_--
+_-=_
+_-_
+_.a1
+_.a2
+_.a3
+_.a4
+_.a6
+_.area
+_.b2
+_.b4
+_.b6
+_.b8
+_.bid
+_.bnf
+_.c4
+_.c6
+_.clgp
+_.codiff
+_.cyc
+_.diff
+_.disc
+_.e
+_.eta
+_.f
+_.fu
+_.futu
+_.gen
+_.group
+_.index
+_.j
+_.mod
+_.nf
+_.no
+_.omega
+_.orders
+_.p
+_.pol
+_.r1
+_.r2
+_.reg
+_.roots
+_.sign
+_.t2
+_.tate
+_.tu
+_.tufu
+_.zk
+_.zkst
+_/=_
+_/_
+_<<=_
+_<<_
+_<=_
+_<_
+_==_
+_>=_
+_>>=_
+_>>_
+_>_
+_[_.._,_.._]
+_[_.._]
+_\\/=_
+_\\/_
+_\\=_
+_\\_
+_^_
+_^s
+__
+_derivfun
+_eval_mnemonic
+_multi_if
+_void_if
+_||_
+_~
+Str
+abs
+acos
+acosh
+addell(e,z1,z2)=elladd(e,z1,z2);
+addprimes
+adj(x)=matadjoint(x);
+agm
+akell(e,n)=ellak(e,n);
+algdep
+algdep2(x,n,dec)=algdep(x,n,dec);
+algtobasis(nf,x)=nfalgtobasis(nf,x);
+allocatemem
+anell(e,n)=ellan(e,n);
+apell(e,n)=ellap(e,n);
+apell2(e,n)=ellap(e,n);
+apprpadic(x,a)=padicappr(x,a);
+arg
+asin
+asinh
+assmat(x)=matcompanion(x);
+atan
+atanh
+basis(x)=nfbasis(x);
+basis2(x)=nfbasis(x,2);
+basistoalg(nf,x)=nfbasistoalg(nf,x);
+bernreal
+bernvec
+bestappr
+bezout
+bezoutres
+bigomega
+bilhell(e,z1,z2)=ellbil(e,z1,z2);
+bin(x,y)=binomial(x,y);
+binary
+bittest
+boundcf(x,lmax)=contfrac(x,,lmax);
+boundfact(x,lim)=factor(x,lim);
+box(x,a)=plotbox(x,a);
+buchcertify(bnf)=bnfcertify(bnf);
+buchfu(bnf)=;
+buchgen(P)=;
+buchgenforcefu(P)=;
+buchgenfu(P)=;
+buchimag(D,c1,c2,g)=quadclassunit(D,,[c1,c2,g]);
+buchinit(P)=bnfinit(P,2);
+buchinitforcefu(P)=bnfinit(P,1);
+buchinitfu(P)=bnfinit(P);
+buchnarrow(bnf)=bnfnarrow(bnf);
+buchray(bnf,ideal)=bnrinit(bnf,ideal);
+buchrayinit(bnf,ideal)=bnrinit(bnf,ideal);
+buchrayinitgen(bnf,ideal)=bnrinit(bnf,ideal,1);
+buchreal(D)=quadclassunit(D);
+bytesize(x)=sizebyte(x);
+ceil
+centerlift
+cf(x)=contfrac(x);
+cf2(b,x)=contfrac(x,b);
+changevar=;
+char(x,y)=charpoly(x,y);
+char1(x,y)=charpoly(x,y,1);
+char2(x,y)=charpoly(x,y,2);
+chell(x,y)=ellchangecurve(x,y);
+chinese
+chptell(x,y)=ellchangepoint(x,y);
+classno(x)=qfbclassno(x);
+classno2(x)=qfbclassno(x,1);
+coeff(x,s)=polcoeff(x,s);
+color(w,c)=plotcolor(w,c);
+compimag(x,y)=x*y;
+compo(x,s)=component(x,s);
+compositum(pol1,pol2)=polcompositum(pol1,pol2);
+compositum2(pol1,pol2)=polcompositum(pol1,pol2,1);
+comprealraw(x,y)=qfbcompraw(x,y);
+concat
+conductor(a1)=bnrconductor(a1);
+conductorofchar(bnr,chi)=bnrconductorofchar(bnr,chi);
+conj
+conjvec
+content
+convol(x,y)=serconvol(x,y);
+core
+core2(x)=core(x,1);
+coredisc
+coredisc2(x)=coredisc(x,1);
+cos
+cosh
+cursor(w)=plotcursor(w);
+cvtoi(x)=truncate(x,&e);
+cyclo(n)=polcyclo(n);
+decodefactor(fa)=factorback(fa);
+decodemodule(nf,fa)=bnfdecodemodule(nf,fa);
+default
+degree(x)=poldegree(x);
+denom(x)=denominator(x);
+deplin(x)=lindep(x,-1);
+deriv
+det(x)=matdet(x);
+det2(x)=matdet(x,1);
+detint(x)=matdetint(x);
+diagonal(x)=matdiagonal(x);
+dilog
+dirdiv
+direuler
+dirmul
+dirzetak
+disc(x)=poldisc(x);
+discf(x)=nfdisc(x);
+discf2(x)=nfdisc(x,2);
+discrayabs(bnr,subgroup)=bnrdisc(bnr,subgroup);
+discrayabscond(bnr)=bnrdisc(bnr,,,2);
+discrayabslist(bnf,list)=bnrdisclist(bnf,list);
+discrayabslistarch(bnf,arch,bound)=bnrdisclist(bnf,bound,arch);
+discrayabslistarchall(bnf,bound)=bnrdisclist(bnf,bound,,1);
+discrayabslistlong(bnf,bound)=bnrdisclist(bnf,bound);
+discrayrel(bnr,subgroup)=bnrdisc(bnr,subgroup,,1);
+discrayrelcond(bnr,subgroup)=bnrdisc(bnr,subgroup,,3);
+divisors
+divres(x,y)=divrem(x,y);
+divsum(n,X,expr)=sumdiv(n,X,expr);
+draw(list)=plotdraw(list);
+eigen(x)=mateigen(x);
+eint1
+erfc
+eta
+euler=Euler;
+eval
+exp
+extract(x,y)=vecextract(x,y);
+fact(x)=factorial(x);
+factcantor(x,p)=factorcantor(x,p);
+factfq(x,p,a)=factorff(x,p,a);
+factmod(x,p)=factormod(x,p);
+factor
+factoredbasis(x,p)=nfbasis(x,,p);
+factoreddiscf(x,p)=nfdisc(x,,p);
+factoredpolred(x,p)=polred(x,,p);
+factoredpolred2(x,p)=polred(x,2,p);
+factornf
+factorpadic
+factorpadic2(x,p,r)=factorpadic(x,p,r,1);
+factpol(x,l,hint)=factor(x);
+factpol2(x,l,hint)=factor(x);
+fibo(x)=fibonacci(x);
+floor
+for
+fordiv
+forprime
+forstep
+forvec
+fpn(p,n)=ffinit(p,n);
+frac
+galois(x)=polgalois(x);
+galoisapply(nf,aut,x)=nfgaloisapply(nf,aut,x);
+galoisconj(nf)=nfgaloisconj(nf);
+galoisconj1(nf)=nfgaloisconj(nf,2);
+galoisconjforce=nfgaloisconj(nf,1);
+gamh(x)=gammah(x);
+gamma
+gauss(a,b)=matsolve(a,b);
+gaussmodulo(M,D,Y)=matsolvemod(M,D,Y);
+gaussmodulo2(M,D,Y)=matsolvemod(M,D,Y,1);
+gcd
+getheap
+getrand
+getstack
+gettime
+globalred(x,y)=ellglobalred(x,y);
+goto=;
+hclassno(x)=qfbhclassno(x);
+hell(e,x)=ellheight(e,x);
+hell2(e,x)=ellheight(e,x,1);
+hermite(x)=mathnf(x);
+hermite2(x)=mathnf(x,1);
+hermitehavas(x)=;
+hermitemod(x,d)=mathnfmod(x,d);
+hermitemodid(x,d)=mathnfmodid(x,d);
+hermiteperm(x)=mathnf(x,3);
+hess(x)=mathess(x);
+hilb(x,y)=hilbert(x,y);
+hilbert(n)=mathilbert(n);
+hilbp(x,y,p)=hilbert(x,y,p);
+hvector(n,X,expr)=vector(n,X,expr);
+hyperu
+i=I;
+idealadd
+idealaddmultone(nf,list)=idealaddtoone(nf,list);
+idealaddone(nf,x,y)=idealaddtoone(nf,x,y);
+idealappr
+idealapprfact(nf,x)=idealappr(nf,x,1);
+idealchinese
+idealcoprime
+idealdiv
+idealdivexact(nf,x,y)=idealdiv(nf,x,y,1);
+idealfactor
+idealhermite(nf,x)=idealhnf(nf,x);
+idealhermite2(nf,x)=idealhnf(nf,x);
+idealintersect
+idealinv
+idealinv2(nf,x)=idealinv(nf,x,1);
+ideallist
+ideallistarch
+ideallistarchgen(nf,list,arch)=ideallistarch(nf,list,arch);
+ideallistunit(nf,list)=ideallist(nf,list,2);
+ideallistunitarch=ideallistarch(nf,list,arch);
+ideallistunitarchgen=ideallistarch(nf,list,arch);
+ideallistunitgen=ideallist(nf,list,3);
+ideallistzstar(nf,bound)=ideallist(nf,bound);
+ideallistzstargen(nf,bound)=ideallist(nf,bound,1);
+ideallllred(nf,x,vdir)=idealred(nf,x,vdir);
+idealmul
+idealmulred(nf,x,y)=idealmul(nf,x,y,1);
+idealnorm
+idealpow
+idealpowred(nf,x,y)=idealpow(nf,x,y,1);
+idealtwoelt
+idealtwoelt2(nf,x,a)=idealtwoelt(nf,x,a);
+idealval
+idmat(n)=matid(n);
+if
+imag
+image(x)=matimage(x);
+image2(x)=matimage(x,1);
+imagecompl(x)=matimagecompl(x);
+incgam
+incgam1(s,x)=;
+incgam2(s,x)=;
+incgam3(s,x)=;
+incgam4(s,x,y)=incgam(s,x,y);
+indexrank(x)=matindexrank(x);
+indsort(x)=vecsort(x,,1);
+initalg(pol)=nfinit(pol);
+initalgred(x)=nfinit(x,2);
+initalgred2(x)=nfinit(x,3);
+initell(x)=ellinit(x);
+initrect(w,x,y)=plotinit(w,x,y);
+initzeta(x)=zetakinit(x);
+integ(x,y)=intformal(x,y);
+intersect(x,y)=matintersect(x,y);
+intgen(x=a,b,s)=intnum(x=a,b,s,1);
+intinf(x=a,b,s)=intnum(x=a,b,s,2);
+intnum
+intopen(x=a,b,s)=intnum(x=a,b,s,3);
+inverseimage(x,y)=matinverseimage(x,y);
+isdiagonal(x)=matisdiagonal(x);
+isfund(x)=isfundamental(x);
+isideal(nf,x)=nfisideal(nf,x);
+isincl(x,y)=nfisincl(x,y);
+isinclfast(nf1,nf2)=nfisincl(nf1,nf2,1);
+isirreducible(x)=polisirreducible(x);
+isisom(x,y)=nfisisom(x,y);
+isisomfast(x,y)=nfisisom(x,y);
+isoncurve(e,x)=ellisoncurve(e,x);
+isprime
+isprincipal(bnf,x)=bnfisprincipal(bnf,x,0);
+isprincipalforce(bnf,x)=bnfisprincipal(bnf,x,2);
+isprincipalgen(bnf,x)=bnfisprincipal(bnf,x);
+isprincipalgenforce(bnf,x)=bnfisprincipal(bnf,x,3);
+isprincipalray(bnf,x)=bnrisprincipal(bnf,x);
+isprincipalraygen
+ispsp(x)=ispseudoprime(x);
+isqrt(x)=sqrtint(x);
+isset(x)=setisset(x);
+issqfree(x)=issquarefree(x);
+issquare
+isunit(bnf,x)=bnfisunit(bnf,x);
+jacobi(x)=qfjacobi(x);
+jbesselh(n,x)=besseljh(n,x);
+jell(x)=ellj(x);
+karamul(x,y,k)=;
+kbessel(nu,x)=besselk(nu,x);
+kbessel2(nu,x)=besselk(nu,x);
+ker(x)=matker(x);
+keri(x)=matker(x,1);
+kerint(x)=matkerint(x);
+kerint1(x)=matkerint(x,1);
+kerint2(x)=;
+kill
+killrect(w)=plotkill(w);
+kro(x,y)=kronecker(x,y);
+label=;
+lambdak(nfz,s)=zetak(nfz,s,1);
+laplace(x)=serlaplace(x);
+lcm
+legendre(n)=pollegendre(n);
+length
+lex
+lexsort(x)=vecsort(x,,2);
+lift
+lindep
+lindep2(x)=lindep(x,1);
+line(w,x2,y2)=plotlines(w,x2,y2);
+lines(w,x2,y2)=plotlines(w,x2,y2);
+lll(x)=qflll(x);
+lll1(x)=;
+lllgen(x)=qflll(x,8);
+lllgram(x)=qflllgram(x);
+lllgram1(x)=;
+lllgramgen(x)=qflllgram(x,8);
+lllgramint(x)=qflllgram(x,1);
+lllgramkerim(x)=qflllgram(x,4);
+lllgramkerimgen(x)=qflllgram(x,5);
+lllint(x)=qflll(x,1);
+lllintpartial(x)=qflll(x,2);
+lllkerim(x)=qflll(x,4);
+lllkerimgen(x)=qflll(x,5);
+lllrat(x)=;
+ln(x)=log(x);
+lngamma
+localred(e)=elllocalred(e);
+log
+logagm(x)=log(x,1);
+lseriesell(e,s,N,A)=elllseries(e,s,A);
+makebigbnf(sbnf)=bnfinit(sbnf);
+mat(x)=Mat(x);
+matextract(x,y,z)=vecextract(x,y,z);
+mathell(e,x)=ellheightmatrix(e,x);
+matrix
+matrixqz
+matrixqz2(x,p)=matrixqz(x,-1);
+matrixqz3(x,p)=matrixqz(x,-2);
+matsize
+max
+min
+minideal(nf,ix,vdir)=idealmin(nf,ix,vdir);
+minim(x,bound,maxnum)=qfminim(x,bound,maxnum);
+minim2(x,bound)=qfminim(x,bound,,1);
+mod(x,y)=Mod(x,y);
+modp(x,y,p)=Mod(x,y);
+modreverse
+modulargcd(x,y)=gcd(x,y,1);
+move(w,x,y)=plotmove(w,x,y);
+mu(n)=moebius(n);
+newtonpoly
+nextprime
+nfdetint
+nfdiv(nf,a,b)=nfeltdiv(nf,a,b);
+nfdiveuc(nf,a,b)=nfeltdiveuc(nf,a,b);
+nfdivres(nf,a,b)=nfeltdivrem(nf,a,b);
+nfhermite(nf,x)=nfhnf(nf,x);
+nfhermitemod(nf,x,detx)=nfhnfmod(nf,x,detx);
+nfmod(nf,a,b)=nfeltmod(nf,a,b);
+nfmul(nf,a,b)=nfeltmul(nf,a,b);
+nfpow(nf,a,k)=nfeltpow(nf,a,k);
+nfreduce(nf,a,id)=nfeltreduce(nf,a,id);
+nfsmith(nf,x)=nfsnf(nf,x);
+nfval(nf,a,pr)=nfeltval(nf,a,pr);
+norm
+norml2
+nucomp(x,y,l)=qfbnucomp(x,y,l);
+numdiv
+numer(x)=numerator(x);
+nupow(x,n)=qfbnupow(x,n);
+o(x)=O(x);
+omega
+ordell(e,x)=ellordinate(e,x);
+order(x)=znorder(x);
+orderell(e,x)=ellorder(e,x);
+ordred(x)=polredord(x);
+padicprec
+pascal(n)=matpascal(n);
+perf(a)=qfperfection(a);
+permutation(n,k)=numtoperm(n,k);
+permutation2num(vect)=permtonum(vect);
+pf(x,p)=qfbprimeform(x,p);
+phi(x)=eulerphi(x);
+pi=Pi;
+plot
+ploth
+ploth2(X=a,b,expr)=ploth(X=a,b,expr,1);
+plothmult(X=a,b,expr)=ploth(X=a,b,expr);
+plothraw
+pnqn(x)=contfracpnqn(x);
+point(w,x,y)=plotpoints(w,x,y);
+pointell(e,z)=ellztopoint(e,z);
+points(w,x,y)=plotpoints(w,x,y);
+polint(xa,ya,x)=polinterpolate(xa,ya,p);
+polred
+polred2(x)=polred(x,2);
+polredabs
+polredabs2(x)=polredabs(x,1);
+polredabsall(x)=polredabs(x,4);
+polredabsfast(x)=polredabs(x,8);
+polredabsnored(x)=polredabs(x,2);
+polsym
+polvar(x)=variable(x);
+poly(x,v)=Pol(x,v);
+polylog
+polylogd(m,x)=polylog(m,x,1);
+polylogdold(m,x)=polylog(m,x,2);
+polylogp(m,x)=polylog(m,x,3);
+polyrev(x,v)=Polrev(x,v);
+polzag(n,m)=polzagier(n,m);
+postdraw(list)=psdraw(list);
+postploth(X=a,b,expr)=psploth(X=a,b,expr);
+postploth2(X=a,b,expr)=psploth(X=a,b,expr,1);
+postplothraw(listx,listy)=psplothraw(listx,listy);
+powell(e,x,n)=ellmul(e,x,n);
+powrealraw(x,n)=qfbpowraw(x,n);
+pprint(x)=;
+pprint1(x)=;
+prec(x,n)=precision(x,n);
+precision
+prime
+primedec(nf,p)=idealprimedec(nf,p);
+primes
+primroot(n)=znprimroot(n);
+principalideal(nf,x)=;
+principalidele(nf,x)=;
+print
+print1
+prod(x,X=a,b,expr)=prod(X=a,b,expr,x);
+prodeuler
+prodinf
+prodinf1(X=a,expr)=prodinf(X=a,expr,1);
+psi
+qfi(a,b,c)=Qfb(a,b,c);
+qfr(a,b,c,d)=Qfb(a,b,c,d);
+quaddisc
+quadgen
+quadpoly
+random
+rank(x)=matrank(x);
+rayclassno(bnf,x)=bnrclassno(bnf,x);
+rayclassnolist(bnf,liste)=bnrclassnolist(bnf,liste);
+rbox(w,dx,dy)=plotrbox(w,dx,dy);
+read(x)=input(x);
+real
+recip(x)=polrecip(x);
+redimag(x)=qfbred(x);
+redreal(x)=qfbred(x);
+redrealnod(x,d)=qfbred(x,2,,d);
+reduceddisc(f)=poldiscreduced(f);
+regula(x)=quadregulator(x);
+reorder=;
+resultant(x,y)=polresultant(x,y);
+resultant2(x,y)=polresultant(x,y,1);
+reverse(x)=serreverse(x);
+rhoreal(x)=qfbred(x,1);
+rhorealnod(x,d)=qfbred(x,3,,d);
+rline(w,dx,dy)=plotrline(w,dx,dy);
+rlines(w,dx,dy)=plotrlines(w,dx,dy,1);
+rmove(w,dx,dy)=plotrmove(w,dx,dy);
+rndtoi(x)=round(x,&e);
+rnfbasis
+rnfdiscf(nf,pol)=rnfdisc(nf,pol);
+rnfequation
+rnfequation2(nf,pol)=rnfequation(nf,pol,1);
+rnfhermitebasis(bnf,order)=rnfhnfbasis(bnf,order);
+rnfisfree
+rnflllgram
+rnfpolred
+rnfpseudobasis
+rnfsteinitz
+rootmod(x,p)=polrootsmod(x,p);
+rootmod2(x,p)=polrootsmod(x,p,1);
+rootpadic(x,p,r)=polrootspadic(x,p,r);
+roots(x)=polroots(x);
+rootsof1(nf)=nfrootsof1(nf);
+rootsold(x)=;
+round
+rounderror(x)=round(x,&e);
+rpoint(w,dx,dy)=plotrpoint(w,dx,dy);
+rpoints(w,dx,dy)=plotrpoints(w,dx,dy);
+scale(w,x1,x2,y1,y2)=plotscale(w,x1,x2,y1,y2);
+series(x,v)=Ser(x,v);
+set(x)=Set(x);
+setintersect
+setminus
+setprecision(n)=default(realprecision,n);
+setrand
+setsearch
+setserieslength(n)=default(seriesprecision,n);
+settype(x,t)=type(x,t);
+setunion
+shift
+shiftmul
+sigma
+sigmak(k,x)=sigma(x,k);
+sign
+signat(x)=qfsign(x);
+signunit(bnf)=bnfsignunit(bnf);
+simplefactmod(x,p)=factormod(x,p,1);
+simplify
+sin
+sinh
+size(x)=sizedigit(x);
+smallbasis(x)=nfbasis(x,1);
+smallbuchinit(x)=bnfcompress(x);
+smalldiscf(x)=nfdisc(x,1);
+smallfact(x)=factor(x,0);
+smallinitell(x)=ellinit(x,1);
+smallpolred(x)=polred(x,1);
+smallpolred2(x)=polred(x,3);
+smith(x)=matsnf(x);
+smith2(x)=matsnf(x,1);
+smithclean(x)=matsnf(x,4);
+smithpol(x)=matsnf(x,2);
+solve
+sort(x)=vecsort(x);
+sqr
+sqred(x)=qfgaussred(x);
+sqrt
+srgcd(x,y)=gcd(x,y,2);
+string(w,x)=plotstring(w,x);
+sturm(x)=polsturm(x);
+sturmpart(x,a,b)=polsturm(x,a,b);
+subcyclo(p,d)=polsubcyclo(p,d);
+subell(e,a,b)=ellsub(e,a,b);
+subst
+sum(x,X=a,b,expr)=sum(X=a,b,expr,x);
+sumalt
+sumalt2(X=a,expr)=sumalt(X=a,expr,1);
+suminf
+sumpos
+sumpos2(X=a,expr)=sumpos(X=a,expr,1);
+supplement(x)=matsupplement(x);
+sylvestermatrix(x,y)=polsylvestermatrix(x,y);
+system
+tan
+tanh
+taniyama(e)=elltaniyama(e);
+taylor
+tchebi(n)=polchebyshev(n);
+teich(x)=teichmuller(x);
+texprint(x)=printtex(x);
+theta
+thetanullk
+threetotwo2=;
+threetotwo=;
+torsell(e)=elltors(e);
+trace
+trans(x)=mattranspose(x);
+trunc(x)=truncate(x);
+tschirnhaus(x)=poltschirnhaus(x);
+twototwo(nf,a,b)=;
+type
+unit(x)=quadunit(x);
+until
+valuation
+vec(x)=Vec(x);
+vecindexsort(x)=vecsort(x,,1);
+veclexsort(x)=vecsort(x,,2);
+vecmax
+vecmin
+vecsort
+vector
+vvector(n,X,expr)=vectorv(n,X,expr);
+weipell(e)=ellwp(e);
+wf(x)=weber(x);
+wf2(x)=weber(x,2);
+while
+zell(e,P)=ellpointtoz(e,P);
+zeta
+zetak
+zideallog(nf,x,bid)=ideallog(nf,x,bid);
+zidealstar(nf,I)=idealstar(nf,I);
+zidealstarinit(nf,id)=idealstar(nf,id,1);
+zidealstarinitgen(nf,id)=idealstar(nf,id,2);
+znstar

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pari-sage.git



More information about the debian-science-commits mailing list